Mario Danic Thomas Schmitt Joe Neeman Philippe Rouquier Gabriel Craciunescu George Danchev Jean-Francois Wauthy Lorenzo Taylor 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 Derek Foreman and Ben Jansens Copyright (C) 2002-2006 Derek Foreman and Ben Jansens Mario Danic , Thomas Schmitt Copyright (C) 2006-2017 Mario Danic, Thomas Schmitt This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later as published by the Free Software Foundation. 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 git clone git@dev.lovelyhq.com:libburnia/libburn.git (to become libburn-1.5.8 or higher) =============================================================================== - no novelties yet - libburn-1.5.6.tar.gz Wed Jun 07 2023 =============================================================================== * Bug fix: Overburning with cdrskin option -force ended by a libburn error * New API call burn_write_opts_set_bdr_obs_exempt() * New cdrskin option --bdr_obs_exempt * Officially enabled overburning with burn_write_opts_set_force(,1) libburn-1.5.4.tar.gz Sat Jan 30 2021 =============================================================================== * Bug fix: Early SCSI commands from sg-linux.c were not logged * New API call burn_drive_set_speed_exact() * New API call burn_nominal_slowdown() libburn-1.5.2.pl01.tar.gz Mon Nov 25 2019 =============================================================================== * Bug fix: cdrskin multi-track burning was slow and stalled after track 1. Regression introduced in version 1.5.0 by commit 84fad99, 2018.02.05 O_DIRECT is now disabled for track sources. libburn-1.5.2.tar.gz Sat Oct 26 2019 =============================================================================== * Bug fix: No lock was obtained for setting up a fifo object * Bug fix: Stream recording was applied regardless whether the drive offers it. This caused Xfburn failures with some MATSHITA laptop drives. * Bug fix: TDK Corporation was not recognized as manufacturer of DVD-R "TTH02" * Made libburn ready for building out-of-source. Thanks Ross Burton. * New API calls burn_drive_get_feature_codes(), burn_drive_get_feature() * New cdrskin option --list_features libburn-1.5.0.tar.gz Sat Sep 15 2018 =============================================================================== * Bug fix: cdrskin threw errno 22 on data file input if libburn is configured with --enable-track-src-odirect * Bug fix: SIGSEGV could happen if a track ended by reaching its fixed size while the track source still was willing to deliver bytes. Thanks to user swordragon. * Bug fix: Device file comparison parameters were recorded wrong with Linux sg libburn-1.4.8.tar.gz Tue Sep 12 2017 =============================================================================== * Bug fix: Option -dummy did not affect writing by direct_write_amount= * New API call burn_drive_reset_simulate() * New API call burn_drive_get_bd_r_pow() * Refusing to write to BD-R if formatted to Pseudo Overwrite libburn-1.4.6.tar.gz Fri Sep 16 2016 =============================================================================== * Bug fix: SAO CD could be perceived 2 blocks to short. Regression in 1.4.4 by rev 5672. * Now operating optical drives on OpenBSD. Thanks to SASANO Takayoshi. * New API call burn_drive_set_immed() * New cdrskin option use_immed_bit= libburn-1.4.4.tar.gz Fri Jul 01 2016 =============================================================================== * Bug fix: Option drive_scsi_dev_family=sg did not convert /dev/sr* to /dev/sg* * Bug fix: burn_make_input_sheet_v07t() falsly recognized double byte encoding. Affected cdrskin option: cdtext_to_v07t= * Bug fix: Double free at end of run if burn_write_opts_set_leadin_text() is used. Affected cdrskin option: textfile= * Bug fix: DVD book type of DVD+RW DL and DVD+R DL was reported wrong. Thanks to Etienne Bergeron. libburn-1.4.2.pl01.tar.gz Fri Jan 29 2016 =============================================================================== * Bug fix: cdrskin "failed to attach fifo" when burning from stdin. Regression of 1.4.2, rev 5522. libburn-1.4.2.tar.gz Sat Nov 28 2015 =============================================================================== * Bug fix: burn_disc_get_media_id() returned BD identifiers 2 chars too long * Bug fix: burn_disc_get_multi_caps() returned 2048 bytes too many in caps.start_range_high * Bug fix: Media summary session count of blank and closed media was short by 1 * Bug fix: Endless loop if transport error occurs while waiting for drive ready * New API calls burn_drive_get_serial_no() and burn_drive_get_media_sno() * Result of a Coverity audit: 40+ code changes, but no easy-to-trigger bugs libburn-1.4.0.tar.gz Sun May 17 2015 =============================================================================== * Bug fix: Double free with cdrskin -vvv. Introduced with rev 5065, version 1.3.1 * Bug fix: Wrong read access to memory. Reported by valgrind of lian jianfei. libburn-1.3.8.tar.gz Sat Jun 28 2014 =============================================================================== * Bug fix: Wrong stack usage caused SIGBUS on sparc when compiled by gcc -O2 * Bug fix: Minimum drive buffer fill was measured by cdrskin before the buffer could get full * Bug fix: A failed MMC BLANK command did not cause error indication by libburn * Bug fix: A final fsync(2) was performed with stdio drives, even if not desired libburn-1.3.6.pl01.tar.gz Tue Mar 18 2013 =============================================================================== * Bug fix: CD TAO with multiple tracks could cause a buffer overrun * Bug fix: Compilation warning for unsupported systems mutated into an error libburn-1.3.6.tar.gz Tue Mar 04 2013 =============================================================================== * New system adapter for NetBSD libburn-1.3.4.tar.gz Thu Dec 12 2013 =============================================================================== * Bug fix: Drive error reports were ignored during blanking and formatting * Bug fix: Drive LG BH16NS40 stalls on inspection of unformatted DVD+RW * New API call burn_disc_pretend_full_uncond() libburn-1.3.2.tar.gz Wed Aug 07 2013 =============================================================================== * Bug fix: cdrskin -msinfo on DVD and BD reported old session start = next writable address. Regression introduced by version 1.2.8 (rev 4956). * Bug fix: The signal handler aborted on SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU * New API call burn_make_input_sheet_v07t() * API call burn_session_input_sheet_v07t(): read multiple blocks from same file * New API calls burn_drive_extract_audio(), burn_drive_extract_audio_track() * New cdrskin option textfile_to_v07t= * New cdrskin options cdtext_to_textfile= and cdtext_to_v07t= * New cdrskin options extract_audio_to= , extract_tracks= , extract_basename= , --extract_dap * New cdrskin option --pacifier_with_newline * Improved granularity of SCSI log time measurement, now with timestamp * Optional "make doc" now demands doxygen 1.8.4 libburn-1.3.0.pl01.tar.gz Fri May 31 2013 =============================================================================== * Bug fix: cdrskin -msinfo on DVD and BD reported old session start = next writable address. Regression introduced by version 1.2.8. libburn-1.3.0.tar.gz Fri May 17 2013 =============================================================================== * Bug fix: Full formatting of BD-RE used certification regardless of drive capabilities * Bug fix: DVD+R with damaged TOC were reported by -minfo with wrong end address libburn-1.2.8.tar.gz Mon Mar 18 2013 =============================================================================== * Bug fix: All CD tracks were reported with the sizes of the tracks in the first session. Regression introduced with version 1.2.0 (rev 4552). * Bug fix: On some drives the request for minimum speed yielded maximum speed * New cdrskin option --list_speeds * -toc and -minfo now report about tracks in the incomplete session * New API call burn_disc_get_incomplete_sessions() * New burn_toc_entry component .track_status_bits libburn-1.2.6.tar.gz Tue Jan 08 2013 =============================================================================== * Bug fix: Speed setting had no effect on BD media * New cdrskin option --no_load * New API call burn_read_audio() * New API call burn_list_sev_texts() libburn-1.2.4.tar.gz Fri Jul 20 2012 =============================================================================== * Bug fix: CD SAO sessions with data tracks started by an audio pause * Bug fix: CD tracks were perceived 2 sectors too short. Nice with TAO, bad with SAO. * Bug fix: cdrskin SIGSEGV if track source was added when no drive was available * New API call burn_write_opts_set_obs_pad(), ./configure --enable-dvd-obs-pad * New cdrskin option --obs_pad libburn-1.2.2.tar.gz Mon Apr 02 2012 =============================================================================== * Small internal refinements libburn-1.2.0.tar.gz Sat Jan 28 2012 =============================================================================== * Bug fix: cdrskin produced a memory fault if interupted before writing began * Bug fix: Solaris adapter mishandled write commands which failed on first try * Bug fix: Interrupting libburn while drive tray is loading led to endless loop * Bug fix: Progress report with blanking and formatting could be bogus * New API calls burn_disc_get_leadin_text(), burn_write_opts_set_leadin_text() * New API calls for composing CD-TEXT, see doc/cdtext.txt * New API call burn_session_by_cue_file() for reading CDRWIN .cue files * New API call burn_track_set_isrc_string() * New API calls burn_track_set_index(), burn_track_clear_indice() * New API calls burn_session_set_start_tno(), burn_session_get_start_tno() * New API calls burn_track_set_pregap_size(), burn_track_set_postgap_size() * Implemented cdrskin option textfile= * Implemented cdrskin option combination -vv -toc for cdtext.dat production * Implemented cdrskin options mcn= and isrc= * Implemented cdrskin options -scms -copy -nocopy -preemp -nopreemp * Implemented cdrskin option index= * Partly implemented cdrskin options cuefile= and -text * New cdrskin option input_sheet_v07t= for CD-TEXT definition * New cdrskin options --cdtext_dummy and --cdtext_verbose * New cdrskin options --four_channel --two_channel * New cdrskin option cd_start_tno= * New cdrskin options sao_pregap=, sao_postgap= libburn-1.1.8.tar.gz Mon Nov 21 2011 =============================================================================== * Bug fix: Misinterpreted mode page 2A if block descriptors are present * Enabled recognition of QEMU DVD-ROM 0.12 * Avoiding to intermediately close and open drive device file * New API call burn_drive_re_assess() libburn-1.1.6.tar.gz Tue Sep 27 2011 =============================================================================== * Bug fix: stdio sizes > 4 TB - 32 kB caused integer rollover * Worked around a collision with Linux udev which lets links vanish libburn-1.1.4.tar.gz Sun Aug 07 2011 =============================================================================== * Bug fix: Some drives return -150 as NWA of blank CD, rather than 0. libburn forwarded this misleading information to the application. * Bug fix: Some drives returned wrong CD sizes after having burned DVD-R * Bug fix: Empty ROM drive was mistaken to hold an unsuitable disc * Bug fix: Avoiding to load speed descriptor list twice * New API call burn_lookup_device_link() * New API call burn_disc_get_phys_format_info() * New cdrskin option --device_links Release 1.1.2 was skipped to get back in sync with libisoburn. libburn-1.1.0.pl01.tar.gz Mon Jun 20 2011 =============================================================================== * Bug fix: libburn-1.1.0 compiled only on Linux, FreeBSD, and Solaris libburn-1.1.0.tar.gz Sat Jun 18 2011 =============================================================================== * Bug fix: burn_disc_format() on DVD-RW issued wrong block size with type 00h * New API call burn_disc_next_track_is_damaged() * New API call burn_disc_close_damaged() * Dropped suffix .plXY from tarball name Release 1.0.8 was skipped to get back in sync with libisofs and libisoburn. libburn-1.0.6.pl00.tar.gz Sat Apr 9 2011 =============================================================================== * Burning DVD-R DAO with 2 kB size granularity rather than 32 kB * New API call burn_allow_drive_role_4() libburn-1.0.4.pl00.tar.gz Thu Mar 3 2011 =============================================================================== * Bug fix: Read-only file descriptors were classified as write-only pseudo drives libburn-1.0.2.pl00.tar.gz Wed Feb 23 2011 =============================================================================== * Removed compilation obstacles on Solaris 9. * Improved recognition of non-seekable stdio pseudo-drives. libburn-1.0.0.pl00.tar.gz Sun Jan 16 2011 =============================================================================== * Allowed umask to create stdio-drive files with rw-permissions for all * cdrskin now refuses to burn if the foreseeable size exceeds media capacity libburn-0.9.0.pl00.tar.gz Wed Dec 08 2010 =============================================================================== * Regression fix: SCSI reply data logging was disabled in release 0.8.6 libburn-0.8.8.pl00.tar.gz Wed Oct 20 2010 =============================================================================== * New API call burn_offst_source_new() * New API call burn_disc_get_bd_spare_info() libburn-0.8.6.pl00.tar.gz Fri Sep 17 2010 =============================================================================== * Lifted test reservation on DVD-R DL media. * Hiding all non-API symbols from the linker by use of --version-script * Now with history of release notes in ./ChangeLog file. libburn-0.8.4.pl00.tar.gz Wed Jun 30 2010 =============================================================================== * General POSIX system adapters ignore SIGWINCH and SIGURG if defined * Allowed 64 kB max output buffer size on all OSes libburn-0.8.2.pl00.tar.gz Fri Jun 11 2010 =============================================================================== * New system adapter for Solaris uscsi (tested on snv134, kernel 5.11) * Bug fix: CD TOC was not read if the first track did not start at LBA 0 * Bug fix: CD-ROM media got attributed random lead-in and lead-out adresses * Bug fix: SIGSEGV of experimental libcdio system adapter if drive list is empty libburn-0.8.0.pl00.tar.gz Fri Apr 09 2010 =============================================================================== * libburn now works with ahci driver on FreeBSD 8-STABLE. libburn-0.7.8.pl00.tar.gz Wed Mar 10 2010 =============================================================================== * Bug fix: On FreeBSD, piped input was falsely attributed a small fixed size. * Built-in abort handling is more suitable for FreeBSD now. cdrskin novelties: * Bug fix: Option fs=0 led to SIGSEGV. Regression introduced by version 0.7.4 in december 2009. * Abort handling is more suitable for FreeBSD now. libburn-0.7.6.pl00.tar.gz Sat Jan 23 2010 =============================================================================== * Bug fix: System adapter for generic X/Open was missing in libburn release tarball * Bug fix: with non-Linux adapters there were 0 readable bytes on block devices * Made FreeBSD system adapter safe from mutal burn spoiling and drive deadlock * Enabled FreeBSD system adapter for Debian kfreebsd * Experimental SCSI transport adapter via GNU libcdio 0.83git cdrskin novelties: * none libburn-0.7.4.pl01.tar.gz Sat Dec 26 2009 =============================================================================== * Bug fix: Added missing system adapter for generic X/Open to libburn release tarball Libburn 0.7.4.pl00 Mon Dec 07 2009 =============================================================================== * Bug fix: SIGSEGV from NULL pointer with media product id inquiry on LG GH22LS30 * Bug fix: DVD DAO track size was rounded up much too generously * Workaround for Pioneer DVR-216D which got stuck on DVD-R burns. (already fixed in 0.7.2.pl01) * Workaround for Pioneer DVR-216D refusal to eject. (already fixed in 0.7.2.pl01) * Configure options --enable-dvd-obs-64k, --enable-track-src-odirect * New API calls burn_write_opts_set_dvd_obs(), burn_write_opts_set_stdio_fsync() * New API call burn_set_scsi_logging() * New API calls burn_fifo_get_statistics(), burn_fifo_next_interval(), burn_fifo_fill() * Re-implemented ECMA-130 P-parity, Q-parity and scrambling for BURN_WRITE_RAW cdrskin novelties: * cdrskin option -V for logging of SCSI commands * New cdrskin options dvd_obs= and stdio_fsync= * New compile_cdrskin.sh option -dvd_obs_64k libburn-0.7.2.pl01.tar.gz Fri Nov 13 2009 =============================================================================== * Workaround for Pioneer DVR-216D which got stuck on DVD-R burns. * Workaround for Pioneer DVR-216D refusal to eject. Libburn 0.7.2.pl00 Mon Oct 12 2009 =============================================================================== * Bug fix: CD TAO sessions with multiple tracks did not work in -dummy mode * New API calls burn_get_media_product_id() , burn_guess_manufacturer() , burn_guess_cd_manufacturer() * New API call burn_disc_get_cd_info() * New API call burn_track_set_cdxa_conv() cdrskin novelties: * Better interpretation of options -mode2, -xa, -xa1, -xa2 * New option --xa1-ignore * New -atip report lines "Product Id:" and "Producer:" libburn-0.7.0.pl00.tar.gz Thu Aug 27 2009 =============================================================================== * New API calls burn_drive_get_all_profiles(), burn_obtain_profile_name() allow to inquire and process the list of supported media types. cdrskin lists all supported profiles with option -atip -v * New API call burn_drive_snooze() allows to calm down a drive when no i/o is expected for a while. * Bug fix: Some SCSI commands stalled on U3 memory sticks which appear as a hub with a memory stick and a CD-ROM drive containing a small CD. These commands make not much sense with a CD-ROM and are now avoided for this media situation. libburn-0.6.8.pl00.tar.gz Tue Jul 14 2009 =============================================================================== * Bug fix: Old MMC-1 drives were rejected because of mode page 2Ah length. * cdrskin -scanbus now works with high SCSI bus numbers. libburn-0.6.6.pl00.tar.gz Fri May 8 2009 =============================================================================== * Bug fix: Improper abort handling with broken pipe during outputto a stdio: pseudo-drive. * Bug fix: Device scan stalled on FreeBSD with non-burner USB device libburn-0.6.4.pl00.tar.gz Fri Mar 13 2009 =============================================================================== * New operating system adapter "dummy" for stdio on general X/Open systems * New API function burn_drive_set_stream_recording() allows to write the crucial start blocks of a filesystem with slow BD-RE Defect Management and to write the bulk of data with full nominal speed. libburn-0.6.2.pl00.tar.gz Fri Feb 20 2009 =============================================================================== * Improvements with build system for FreeBSD libburn-0.6.0.pl01.tar.gz Wed Jan 07 2009 =============================================================================== * Bug fix: BD-R were not correctly finalized libburn-0.6.0.pl00.tar.gz Sun Jan 04 2009 =============================================================================== * Formatting and writing of BD-R media * New API function burn_get_read_capacity() libburn-0.5.8.pl00.tar.gz Mon Dec 08 2008 =============================================================================== * Bug fix: A session without leadout entry on CD caused a SIGSEGV by NULL * Improvements about BD-RE formatting libburn-0.5.6.pl00.tar.gz Wed Nov 12 2008 =============================================================================== * Bug fix: libburn fifo thread was not aborted when burn run was aborted which could lead to use of freed memory. libburn-0.5.4.pl00.tar.gz Mon Oct 6 2008 =============================================================================== * Bug fix: On Linux 2.4 /dev/sr0 was accepted as enumerable address but then failed to work. libburn-0.5.2.pl00.tar.gz Wed Aug 20 2008 =============================================================================== * Larger set of possibly acceptable drive device file names libburn-0.5.0.pl00.tar.gz Thu Jul 17 2008 =============================================================================== * Bug fix: cdrskin option drive_scsi_dev_family=scd lead to buffer overflow * Ability to use /dev/scd as fallback if /dev/sr does not exist * New API call burn_fifo_peek_data() libburn-0.4.8.pl00.tar.gz Sat May 17 2008 =============================================================================== * Bug fix: Random access addressing for DVD-RAM and BD-RE did not work. * cdrskin: Affected were options write_start_address= and -- grow_overwriteable_iso on DVD-RAM or BD-RE. * xorriso: Affected were sessions on DVD-RAM or BD-RE after the first one. libburn-0.4.6.pl00.tar.gz Sun May 11 2008 =============================================================================== * Support for BD-RE media is now official * New burn_write_opts_set_stream_recording() can speed up DVD-RAM and BD-RE * New cdrskin option --list_formats * New cdrskin blank types for expert formatting of DVD-RAM and BD-RE * New cdrskin blank type blank=as_needed for automatic handling of media libburn-0.4.4.tar.gz Thu April 10 2008 =============================================================================== * Support for DVD+R/DL media is now official libburn-0.4.2.tar.gz Sun Feb 3 2008 =============================================================================== * Long term commitment to ABI libburn.so.4. * ABI compatibility is guaranteed for any older feature set released since libburn-0.3.2 about one year ago. * libburn provides means for compile time and runtime checking of its version. * Compile time check in cdrskin for proper version of libburn include file. Required is at least 0.4.2. * Runtime check in cdrskin prevents dynamic linking with outdated version of libburn.so.4. Required is at least the version seen in the include file at compile time. libburn-0.4.0.tar.gz Mon Oct 29 2007 =============================================================================== * New option direct_write_amount= * New option --grow_overwriteable_iso * New option --allow_emulated_drives dev=stdio: * More cdrecord options supported: -format, -inq, -load, -lock, -immed, -waiti * New option fallback_program= * A lot of libburn API additions. libburn-0.3.8.tar.gz Tue Jul 31 2007 =============================================================================== * Now able to cope with the peculiarities of Linux 2.4 USB * Refusal to perform -dummy runs on media which cannot simulate burning * New option modesty_on_drive= may help with hda -> hdb burns * New option minbuf= , cdrecord compatible frontend of modesty_on_drive= * New option --adjust_speed_to_drive * Precautions against using the burner drive as track source * Note: ABI has not been broken. libburn-0.3.6.tar.gz Thu Apr 26 2007 =============================================================================== * On Linux kernel 2.6, /dev/sr* gets used rather than /dev/sg*. * DVD+R now get finalized (if not -multi is given) libburn-0.3.4.tar.gz Mon Mar 12 2007 =============================================================================== * Multi-session recording on DVD+R, including -toc, -msinfo * Options --tell_media_space , assert_write_lba= * Bug fix of rare multi track fifo stall libburn-0.3.2.tar.gz Feb 11 2007 =============================================================================== * Burnfree enabled by default * Multi-session recording on sequential DVD-R[W], including -toc, -msinfo * DVD-R[W] Disk-at-once recording libburn-0.3.0.1.tar.gz Tue Jan 30 2007 =============================================================================== * Improved recognition of unsuitable media types * Replaced ban of chmod u+s by loud warning * detailed man page for cdrskin * Burning of DVD+RW and DVD-RAM media as single-track TAO-like initial session * Formatting and then burning to DVD-RW like to DVD+RW * New option -msifile=path from cdrkit/wodim * 0.3.0.1 release notes * * Bug fix enabling tracks >= 1.3 GB from disk file libburn-0.2.6.3.tar.gz Fri Dec 29 2006 =============================================================================== * 0.2.6 release notes (Wed Nov 22 2006) * After a lot of time with dedication to this project, we proudly present you libburn 0.2.6. It is the first version of cdrskin and libburn after they have been split from genisofs and libisofs. Main new features are write mode TAO and support for multi session. * 0.2.6.1 release notes (Fri Nov 24 2006) * Point release to fix misleading version numbers in messages and documentation * 0.2.6.2 release notes (Sat Dec 16 2006) * cdrskin man page backported from development version 0.2.7. * 0.2.6.3 release notes (Fri Dec 29 2006) * Point release to fix build system problems people have experienced with the past release. libburn-0.2.3.snapshot02.tar.gz Thu Nov 02 2006 =============================================================================== * Stabilized snapshot including release 0.2.4.pl01 of cdrskin * cdrskin 0.2.4 release notes * Compatibility with cdrecord has been improved in respect to drive addresses, audio extraction from .wav, -scanbus, -toc, drive buffer fill indicator. * Note: The previous snapshot01 with the same source base is handicapped by a broken ./configure setup. It works well on Intel compatible CPUs but is supposed to be unusable on big-endian architectures. libburn-0.2.2.tar.gz Wed Sep 20 2006 =============================================================================== Initial release of libburnia's libburn combined with cdrskin. Installation Instructions ************************* Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== Briefly, the shell commands `./configure; make; make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf bug. Until the bug is fixed you can use this workaround: CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. # ts A90315 : LIBBURNIA_PKGCONFDIR is defined OS specific in acinclude.m4 # was: pkgconfigdir=$(libdir)/pkgconfig pkgconfigdir=$(LIBBURNIA_PKGCONFDIR) libincludedir=$(includedir)/libburn lib_LTLIBRARIES = libburn/libburn.la ACLOCAL_AMFLAGS = -I ./ ## ========================================================================= ## # Build libraries libburn_libburn_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) $(LIBLDFLAGS) # This causes undesired .o names # configure.ac appends -D options to variable CFLAG ### libburn_libburn_la_CFLAGS = $(LIBBURN_DVD_OBS_64K) libburn_libburn_la_LIBADD = $(LIBBURN_ARCH_LIBS) $(THREAD_LIBS) libburn_libburn_la_SOURCES = \ libburn/async.c \ libburn/async.h \ libburn/back_hacks.h \ libburn/cdtext.c \ libburn/cleanup.c \ libburn/cleanup.h \ libburn/crc.c \ libburn/crc.h \ libburn/debug.c \ libburn/debug.h \ libburn/drive.c \ libburn/drive.h \ libburn/ecma130ab.c \ libburn/ecma130ab.h \ libburn/error.h \ libburn/file.c \ libburn/file.h \ libburn/init.c \ libburn/init.h \ libburn/libburn.h \ libburn/libdax_audioxtr.h \ libburn/libdax_audioxtr.c \ libburn/libdax_msgs.h \ libburn/libdax_msgs.c \ libburn/mmc.c \ libburn/mmc.h \ libburn/null.c \ libburn/null.h \ libburn/options.c \ libburn/options.h \ libburn/os.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 ## libburn/sg-@ARCH@.c \ libinclude_HEADERS = \ libburn/libburn.h install-exec-hook: $(LIBBURNIA_LDCONFIG_CMD) "$(DESTDIR)$(libdir)" || echo 'NOTE: Explicit dynamic library configuration failed. If needed, configure manually for:' "$(DESTDIR)$(libdir)" ## ========================================================================= ## ## Build test applications noinst_PROGRAMS = \ test/libburner \ test/offst_source \ test/telltoc \ test/dewav \ test/fake_au \ test/poll bin_PROGRAMS = \ cdrskin/cdrskin LIBBURN_EXTRALIBS = $(LIBBURN_ARCH_LIBS) $(THREAD_LIBS) test_libburner_CPPFLAGS = -Ilibburn test_libburner_LDADD = $(libburn_libburn_la_OBJECTS) $(LIBBURN_EXTRALIBS) test_libburner_SOURCES = test/libburner.c test_offst_source_CPPFLAGS = -Ilibburn test_offst_source_LDADD = $(libburn_libburn_la_OBJECTS) $(LIBBURN_EXTRALIBS) test_offst_source_SOURCES = test/offst_source.c test_telltoc_CPPFLAGS = -Ilibburn test_telltoc_LDADD = $(libburn_libburn_la_OBJECTS) $(LIBBURN_EXTRALIBS) test_telltoc_SOURCES = test/telltoc.c test_dewav_CPPFLAGS = -Ilibburn test_dewav_LDADD = $(libburn_libburn_la_OBJECTS) $(LIBBURN_EXTRALIBS) test_dewav_SOURCES = test/dewav.c test_fake_au_CPPFLAGS = test_fake_au_LDADD = test_fake_au_SOURCES = test/fake_au.c test_poll_CPPFLAGS = -Ilibburn test_poll_LDADD = $(libburn_libburn_la_OBJECTS) $(LIBBURN_EXTRALIBS) test_poll_SOURCES = test/poll.c ## cdrskin construction site - ts A60816 - C30607 cdrskin_cdrskin_CPPFLAGS = -Ilibburn cdrskin_cdrskin_CFLAGS = -DCdrskin_libburn_1_5_7 # cdrskin_cdrskin_LDADD = $(libburn_libburn_la_OBJECTS) $(LIBBURN_EXTRALIBS) # ts A80123, change proposed by Simon Huggins to cause dynamic libburn linking cdrskin_cdrskin_LDADD = libburn/libburn.la $(LIBBURN_EXTRALIBS) cdrskin_cdrskin_SOURCES = cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cdrfifo.h cdrskin/cdrskin_timestamp.h ## ## Open questions: how to compute $timestamp and express -DX="$timestamp" ## # "make clean" shall remove a few stubborn .libs directories # which George Danchev reported Dec 03 2011. # Learned from: http://www.gnu.org/software/automake/manual/automake.html#Clean clean-local: -rm -rf cdrskin/.libs test/.libs ## ========================================================================= ## ## Build documentation (You need Doxygen for this to work) webhost = http://libburn-api.pykix.org webpath = / docdir = $(DESTDIR)$(prefix)/share/doc/$(PACKAGE)-$(VERSION) doc: doc/html doc/html: doc/doxygen.conf if [ -f ./doc/doc.lock ]; then \ $(RM) -r doc/html; \ doxygen doc/doxygen.conf; \ fi doc-upload: doc/html scp -r $ and Thomas Schmitt Copyright (C) 2006-2023 Mario Danic, Thomas Schmitt Still containing parts of Libburn. By Derek Foreman and Ben Jansens Copyright (C) 2002-2006 Derek Foreman and Ben Jansens http://files.libburnia-project.org/releases/libburn-1.5.6.tar.gz ------------------------------------------------------------------------------ Build and Installation From tarball Obtain libburn-1.5.6.tar.gz, take it to a directory of your choice and do: tar xzf libburn-1.5.6.tar.gz cd libburn-1.5.6 ./configure --prefix=/usr make To make libburn accessible for running and application development, and to install the cdrecord compatibility binary cdrskin, do (as Superuser): make install This procedure installs libburn.so.4 and cdrskin depending on it. For a standalone cdrskin binary, see cdrskin/README. A behavioral conflict is known between any burn software and demons like hald which probe CD drives. This can spoil burn runs for CD-R or CD-RW. You may have to keep your hald away from the drive. See for example http://www.freebsd.org/gnome/docs/halfaq.html From git Our build system is based on autotools. For preparing the build of a git snapshot you will need autotools of at least version 1.7. Do in a directory of your choice: git clone https://dev.lovelyhq.com/libburnia/libburn.git libburn-git cd libburn-git ./bootstrap ./configure --prefix=/usr make make install Warning: The master branch might contain experimental features which might not persist until next release. Special ./configure options make install on GNU/Linux will try to run program ldconfig with the library installation directory as only argument. Failure to do so will not abort installation. One may disable ldconfig by ./configure option: --disable-ldconfig-at-install In some situations Linux may deliver a better write performance to drives if the track input is read with O_DIRECT (see man 2 open). The API call burn_os_open_track_src() and the input readers of cdrskin and libburn fifo can be told to use this peculiar read mode by: --enable-track-src-odirect But often libburn call burn_write_opts_set_dvd_obs(opts, 64*1024) will yield even better performance in such a situation. 64k can be made default at configure time by: --enable-dvd-obs-64k This may be combined with above --enable-track-src-odirect . If it is desired that DVD DAO writing and stdio: writing get padded up to a full write chunk of 32k or 64k, then use ./configure option: --enable-dvd-obs-pad Alternatively the transport of SCSI commands can be done via libcdio-0.83. You may install it and re-run libburn's ./configure with option --enable-libcdio By use of a version script, the libburn.so library exposes no other function names but those of the API definition in libburn/libburn.h. If -Wl,--version-script=... makes problems with the local compiler, then disable this encapsulation feature by --disable-versioned-libs Make sure to re-compile all source files after running ./configure make clean ; make make install Linux only: libburn tries to avoid a collision with udev's drive examination by waiting 0.1 seconds before opening the device file for a longer time, after udev might have been alarmed by drive scanning activities. The waiting time can be set at ./configure time with microsecond granularity. E.g. 2 seconds: CFLAGS="$CFLAGS -DLibburn_udev_wait_useC=2000000" ./configure ...options... Waiting can be disabled by zero waiting time: CFLAGS="$CFLAGS -DLibburn_udev_wait_useC=0" Alternatively, libburn can try to be nice by opening the device file, closing it immediately, waiting, and only then opening it for real: CFLAGS="$CFLAGS -DLibburn_udev_extra_open_cyclE -DLibburn_udev_wait_useC=500000" ------------------------------------------------------------------------------ The other parts of the the libburnia project are hosted as neighbors of libburn: git clone https://dev.lovelyhq.com/libburnia/libisofs.git git clone https://dev.lovelyhq.com/libburnia/libisoburn.git See README files there. ------------------------------------------------------------------------------ Overview of libburnia-project.org libburnia-project.org is an open-source software project for reading, mastering and writing optical discs. For now this means CD media, all DVD media, all BD media. The project comprises of several more or less interdependent parts which together strive to be a usable foundation for application development. These are libraries, language bindings, and middleware binaries which emulate classical (and valuable) Linux tools. Currently it is supported on GNU/Linux with kernels >= 2.4, on FreeBSD with ATAPI/CAM enabled in the kernel (see man atapicam), on OpenSolaris (tested with kernel 5.11), on NetBSD (tested with 6.1.3). On other X/Open compliant systems there will only be pseudo drives, but no direct MMC operation on real CD/DVD/BD drives. For full ports to other systems we would need : login on a development machine or a live OS on CD or DVD, advise from a system person about the equivalent of Linux sg or FreeBSD CAM, volunteers for testing of realistic use cases. We have a well tested code base for burning data and audio CDs, DVDs and BDs. The burn API is quite comprehensively documented and can be used to build a presentable application. We have a functional application which emulates the core use cases of cdrecord in order to prove that usability, and in order to allow you to explore libburn's scope by help of existing cdrecord frontends. ISO 9660 filesystems with Rock Ridge and Joliet extensions can be created and manipulated quite freely. This capability together with our burn capability makes possible a single binary application which covers all steps of image composition, updating and writing. Quite unique in the Linux world. The project components (list subject to growth, hopefully): - libburn is the library by which preformatted data get onto optical media. It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or /dev/srM or /dev/hdX (e.g. on kernel 2.6). libburn is the foundation of our cdrecord emulation. Its code is independent of cdrecord. Its DVD capabilities are learned from studying the code of dvd+rw-tools and MMC-5 specs. No code but only the pure SCSI knowledge has been taken from dvd+rw-tools, though. - libisofs is the library to pack up hard disk files and directories into a ISO 9660 disk image. This may then be brought to CD via libburn. An own ISO 9660 extension stores ACLs, xattr, and MD5 of file content. - libisoburn is an add-on to libburn and libisofs which coordinates both and also can grow ISO-9660 filesystem images on multi-session media as well as on overwriteable media via the same API. All media peculiarities are handled automatically. It also contains the methods of command oriented application xorriso and offers them via a C language API. - cdrskin is a limited cdrecord compatibility wrapper for libburn. cdrecord is a powerful GPL'ed burn program included in Joerg Schilling's cdrtools. cdrskin strives to be a second source for the services traditionally provided by cdrecord. Additionally it provides libburn's DVD capabilities, where only -sao is compatible with cdrecord. cdrskin does not contain any bytes copied from cdrecord's sources. Many bytes have been copied from the message output of cdrecord runs, though. See cdrskin/README for more. - xorriso is an application of all three libraries which creates, loads, manipulates and writes ISO 9660 filesystem images with Rock Ridge extensions. Manipulation is not only adding or overwriting of files but also deleting, renaming, attribute changing, incremental backups, activating boot images, and extracting of files from ISO images to disk. There is also a sparse emulation of cdrecord and a more laborate one of mkisofs. All features of xorriso are also available via a C language API of libisoburn. A static compilation of xorriso and the libraries is dedicated to the GNU Operating System. See xorriso/README_gnu_xorriso . - "test" is a collection of application gestures and examples given by the authors of the library features. The burn API example of libburn is named test/libburner.c . The API for media information inquiry is demonstrated in test/telltoc.c . Explore these examples if you look for inspiration. We strive to be a responsive upstream. Our libraries are committed to maintain older feature sets in newer versions. This applies to source code headers (API) as well as to linkable objects (ABI). The only exception from this rule is about non-release versions x.y.*[13579] which are allowed to introduce new features, change those new features in any way and even may revoke such new features before the next release of x.y.*[02468]. As soon as it is released, a feature is promised to persist. SONAMES: libburn.so.4 (since 0.3.4, March 2007), libisofs.so.6 (since 0.6.2, February 2008), libisoburn.so.1 (since 0.1.0, February 2008). Applications must use 64 bit off_t. E.g. by defining #define _LARGEFILE_SOURCE #define _FILE_OFFSET_BITS 64 or take special precautions to interface with the libraries by 64 bit integers where the .h files prescribe off_t. To reduce libburn's off_t size to 32 bit will keep it from processing tracks of more than 2 GB size. ------------------------------------------------------------------------------ Project history as far as known to me: - Founded in 2002 as it seems. See mailing list archives http://lists.freedesktop.org/archives/libburn/ The site of this founder team is reachable and offers download of a (somewhat outdated) tarball and from CVS : http://icculus.org/burn/ Copyright holders and most probably founders: Derek Foreman and Ben Jansens. - I came to using libburn in 2005. Founded the cdrskin project and submitted necessary patches which were accepted or implemented better. Except one remaining patch which prevented cdrskin from using vanilla libburn from CVS. The cdrskin project site is reachable and offers download of the heavily patched (elsewise outdated) tarball under the name cdrskin-0.1.2 : http://scdbackup.sourceforge.net/cdrskin_eng.html It has meanwhile moved to use vanilla libburn.pykix.org , though. Version 0.1.4 constitutes the first release of this kind. - In July 2006 our team mate Mario Danic announced a revival of libburn which by about nearly everybody else was perceived as unfriendly fork. Derek Foreman four days later posted a message which expressed his discontent. The situation first caused me to publicly regret it and then - after i got the opportunity to move in with cdrskin - gave me true reason to personally apologize to Derek Foreman, Ben Jansens and the contributors at icculus.org/burn. Posted to both projects: http://lists.freedesktop.org/archives/libburn/2006-August/000446.html http://mailman-mail1.webfaction.com/pipermail/libburn-hackers/2006-August/000024.html - Mid August 2006 project cdrskin established a branch office in libburn.pykix.org so that all maintainers of our tools have one single place to get the current (at least slightely) usable coordinated versions of everything. Project cdrskin will live forth independendly for a while but it is committed to stay in sync with libburn.pykix.org (or some successor, if ever). cdrskin is also committed to support icculus.org/burn if the pending fork is made reality by content changes in that project. It will cease to maintain a patched version of icculus.org/burn though. Precondition for a new release of cdrskin on base of icculus.org/burn would be the pending "whitelist patch" therefore. I would rather prefer if both projects find consense and merge, or at least cooperate. I have not given up hope totally, yet. I, personally, will honor any approach. - 2nd September 2006 the decision is made to strive for a consolidation of copyright and a commitment to GPL in a reasonable and open minded way. This is to avoid long term problems with code of unknown origin and with finding consense among the not so clearly defined group of copyright claimers and -holders. libisofs is already claimed sole copyright Mario Danic. cdrskin and libburner are already claimed sole copyright Thomas Schmitt. Rewrites of other components will follow and concluded by claiming full copyright within the group of libburn.pykix.org-copyright holders. - 16th September 2006 feature freeze for release of libburn-0.2.2 . - 20th September 2006 release of libburn-0.2.2 . - 26th October 2006 feature freeze for cdrskin-0.2.4 based on libburn-0.2.3 . This version of cdrskin is much more cdrecord compatible in repect to drive addressing and audio features. - 30th October 2006 release of cdrskin-0.2.4 . - 13th November 2006 splitting releases of libburn+cdrskin from libisofs. - 24th November 2006 release of libburn-0.2.6 and cdrskin-0.2.6 . cdrskin has become suitable for unaware frontends as long as they perform only the core of cdrecord use cases (including open-ended input streams, audio, and multi-session). - 28th November 2006 the umbrella project which encloses both, libisofs and libburn, is now called libburnia. For the origin of this name, see http://en.wikipedia.org/wiki/Liburnians . - 16th January 2007 release of libburn-0.3.0 and cdrskin-0.3.0 . Now the scope is widened to a first class of DVD media: overwriteable single layer types DVD-RAM, DVD+RW, DVD-RW. This is not a cdrecord emulation but rather inspired by dvd+rw-tools' "poor man" writing facility for this class of media. Taking a bow towards Andy Polyakov. - 11th February 2007 version 0.3.2 covers sequential DVD-RW and DVD-R with multi-session and with DAO. - 12th March 2007 version 0.3.4 supports DVD+R and thus covers all single layer DVD media. Code for double layer DVD+/-R is implemented but awaits a tester yet. - 23th April 2007 version 0.3.6 follows the unanimous opinion of Linux kernel people that one should not use /dev/sg on kernel 2.6. - 31st July 2007 version 0.3.8 marks the first anniversary of libburn revival. We look back on improved stability, a substantially extended list of media and write modes, and better protection against typical user mishaps. - 24th October 2007 version 0.4.0 is the foundation of new library libisoburn and an upcoming integrated application for manipulating and writing ISO 9660 + Rock Ridge images. cdrskin-0.4.0 got capabilities like growisofs by these enhancements: growing of overwriteable media and disk files. Taking again a bow towards Andy Polyakov. - 26th Januar 2008 version 0.4.2 rectifies the version numbering so that we reliably release libburn.so.4 as should have been done since libburn-0.3.2. cdrskin now is by default linked dynamically and does a runtime check to ensure not to be started with a libburn which is older than itself. - 3rd Feb 2008 libisofs-0.2.x (.so.5) has been deprecated. - 14th Feb 2008 libisofs-0.6.2 permanently replaces the old libisofs-0.2.x. It is the first release of new libisofs.so.6 which will guarantee future API/ABI compatibility for its whole feature set. - 15th Feb 2008 libisoburn-0.1.0 (.so.1) coordinates libisofs and libburn for the purpose of ISO image reading and writing. It emulates multi-session on overwriteable media. Application xorriso makes use of all three libraries. - 8th Apr 2008 libburn-0.4.4 has proven to be capable of burning to DVD+R/DL and read performance on disk file pseudo-drives has been improved. - 27th Apr 2008 libisofs-0.6.4 can now read data file content from images and can map pieces of disk files onto image files. Image directory iteration has been enhanced. Input data streams and extended information have been exposed in the API to enable future development. - 29th Apr 2008 libisoburn-0.1.4 was made more efficient with reading of image tree nodes. It now depends on libisofs-0.6.4 and libburn-0.4.4. xorriso makes use of new libisofs features by performing incremental updates of directory trees and by cutting oversized data files into pieces. A primitive single session emulation of cdrecord and mkisofs is provided. - 10th May 2008 libburn-0.4.6 supports formatting and writing of BD-RE, full nominal speed for DVD-RAM and BD-RE. cdrskin has a unified blank type with automatic media state recognition. - 17th May 2008 an old bug with DVD-RAM and now with BD-RE is fixed by libburn-0.4.8. So libisoburn can now perform emulation of multisession on those media. - 19th May 2008 libisoburn-0.1.6 brings better table-of-content emulation on overwriteble media and disk files. - 1st Jun 2008 libisofs-0.6.6 fixes some problems around device files. - 3rd Jun 2008 libisoburn-0.1.8 fixes a bug with overwriteable media. - 23rd Jun 2008 libisoburn-0.2.0 introduces extraction of files from ISO images. - 16th Jul 2008 libburn-0.5.0 handles systems with no /dev/sr* but only /dev/scd*. - 19th Jul 2008 libisoburn/xorriso-0.2.2 can do multi-session in mkisofs and cdrecord style. xorriso now can serve underneath growisofs. - 20th Aug 2008 libburn-0.5.2 revokes the necessity that a drive must be enumerable in order to be addressable. Enumeration is enhanced by examining /proc/sys/dev/cdrom/info. - 24th Aug 2008 libisoburn/xorriso-0.2.4 introduces a media readability check with data retrieval option. - 18th Sep 2008 libisofs-0.6.8 supports ISO 9660 Level 3 which can represent very large data files in the image. - 20th Sep 2008 libisoburn/xorriso-0.2.6 takes into respect the new Level 3 capabilities of libisofs. - 6th Oct 2008 libburn-0.5.4 adjusts the changes of 0.5.2 to the needs of Linux kernel 2.4 and introduces human readable SCSI error messages. - 6th Oct 2008 libisofs-0.6.10 fixes two bugs which prevented adding and manipulation of ISOLINUX boot images. - 15th Oct 2008 libisoburn/xorriso-0.2.8 can activate and maintain an ISOLINUX boot image by an EL Torito boot record. - 12th Nov 2008 libburn-0.5.6 fixes usage of freed memory by the fifo thread of an aborted burn run. - 26th Nov 2008 libisofs-0.6.12 can produce a ISOLINUX isohybrid MBR on the fly and can produce ISO images which resemble old mkisofs images. - 2nd Dec 2008 libisoburn-0.3.0. xorriso now is ready for exotic character sets, for legacy FreeBSD systems which expect an outdated Rock Ridge signature, and for producing ISO images with MBR which boot from hard disk or USB stick. Three minor bugs were fixed. - 7th Dec 2008 libburn-0.5.8 prevents a SIGSEGV with weird CD table-of-content and improves BD-RE formatting. - 9th Dec 2008 Our project received a donation from Thomas Weber. - 2nd Jan 2009 libburn-0.6.0 learned to format BD-R and write to either formatted or unformatted BD-R. - 6th Jan 2009 libisoburn-0.3.2. xorriso can produce and execute commands for mounting older sessions from all kinds of media. Pseudo-drives outside the /dev/ tree can be addressed without prefix "stdio:". - 20th Feb 2009 libburn-0.6.2 source release now compiles out of the box on FreeBSD. - 28 Feb 2009 libisofs-0.6.14 can record ACLs and Extended Attributes xattr in its ISO images. - 01 Mar 2009 libisoburn-0.3.4. xorriso makes use of the ACL and xattr capabilities provided by libisofs for xorriso backup features. - 11 Mar 2009 libisofs-0.6.16 of libisofs fixes two bugs which on Solaris prevented to navigate the ISO images by ".." and to recognize the Rock Ridge extensions of ISO images. The ban to build libisofs on operating systems other than Linux and FreeBSD has been lifted. - 13 Mar 2009 libburn-0.6.4 got a dummy adapter for SCSI/MMC command transport. It will show no drives and thus libburn will only be able to perform operations on "stdio:" pseudo drives. Nevertheless this was precondition to lift the ban to build libburn on operating systems other than Linux and FreeBSD. - 16 Mar 2009 libisoburn-0.3.6: xorriso uses RRIP version 1.10 as default in order to be mountable where mkisofs images are mountable. - 17 Apr 2009 libisofs-0.6.18 introduces content filtering of data files. Built-in filters implement compression to formats gzip and zisofs. External filter processes can perform arbitrary data conversions like encryption. - 19 Apr 2009 libisoburn-0.3.8 makes use of the new libisofs capability to perform content filtering of data files. - 08 May 2009 libburn-0.6.6 fixes a bug with aborting on broken output pipe and a bug with device scan on FreeBSD. - 31 May 2009 libisofs-0.6.20 can record hard link relations in ISO images and offers support with restoring them to disk. Current Linux kernels will mount images with such hard links but will attribute a unique inode number to each file. - 28 Jun 2009 libisoburn-0.4.0: xorriso can record and restore hard link relations of files. Performance of data reading has been improved. Option -find now supports logical operators with its tests. - 14 Jul 2009 libburn-0.6.8 fixes bugs and shortcommings with old MMC-1 drives and with large SCSI bus numbers as handed out by Linux for USB drives. - 20 Jul 2009 libisoburn-0.4.0.pl01 fixes a regression in xorriso which caused data loss in older sessions if xorriso was used underneath growisofs. Affected are releases since libisoburn-0.3.2 in january 2009. - 25 Aug 2009 libisofs-0.6.22 can record MD5 checksums for the whole session and for each single data file. Checksum tags can be used to verify superblock and directory tree before importing them. - 27 Aug 2009 libburn-0.7.0 learned to calm down a drive and to inquire its supported profiles. It works around some pitfalls with U3 enhanced memory sticks which emulate a CD-ROM. - 27 Aug 2009 libisoburn-0.4.0.pl00 can record MD5 checksums by which one may verify the session or single data files in the image. When comparing image files with files in the local filesystem, the MD5 sums avoid the need for reading file content from the image. - 22 Sep 2009 libisoburn-0.4.0.pl01 fixes a bug in xorriso option -cut_out. - 08 Oct 2009 libisofs-0.6.24 fixes a bug which could cause the loss of blanks in file names when a new session got added to an ISO image. With names shorter than 251 characters this happened only to trailing blanks. - 08 Oct 2009 libisoburn-0.4.0.pl02 fixes bugs with xorriso option -for_backup, with xorrisofs -help, and with xorrecord -help. - 12 Oct 2009 libburn-0.7.2 fixes a bug with CD TAO multi-track dummy sessions. It can retrieve media product info and can process track input which was prepared for CD-ROM XA Mode 2 Form 1. cdrskin now performs option -minfo. - 28 Oct 2009 libisoburn-0.4.4 fixes a bug with cdrecord emulation and introduces new information options about media type and ISO image id strings. On Linux it helps with mounting two sessions of the same media simultaneously. - 12 Nov 2009 libburn-0.7.2.pl01 works around problems with Pioneer DVR-216D. DVD-R runs made the drive stuck. Ejecting the tray did not work properly. - 06 Dec 2009 libburn-0.7.4 works around problems with newer DVD burners, provides throughput enhancements with hampered busses on Linux, and new API calls to log SCSI commands and to control the libburn fifo. - 09 Dec 2009 libisoburn-0.4.6 now offers performance tuning of output to DVD drives or disk files. - 26 Dec 2009 libburn-0.7.4.pl01 fixes the release tarball which was lacking the files of the generic system adapter for X/Open. - 29 Dec 2009 Our project received a donation for purchasing a fine small computer which shall serve as OS farm for development and support. - 20 Jan 2010 Version 0.6.26 of libisofs fixes minor bugs and shall enhance portability. - 22 Jan 2010 libburn-0.7.6 has an improved system adapter for FreeBSD, fixes bugs about the generic X/Open system adapter, and can use libcdio >= 0.83 as SCSI transport facility. - 10 Feb 2010 libisofs-0.6.28 fixes a regression about bootable images which was introduced by version 0.6.22 in August 2009. - 23 Feb 2010 libisoburn-0.5.0 marks the transition of the xorriso standalone version to an official GNU project. The name changed to "GNU xorriso" and its license is now GPLv3+. The licenses of libburnia libraries and applications are not affected by this change. - 10 Mar 2010 libburn-0.7.8 fixes bugs and improves the built-in abort handler on FreeBSD. - 30 Mar 2010 Release 0.5.2 of libisoburn provides xorriso documentation in GNU Texinfo format with embedded extra data to derive a full man page. - 09 Apr 2010 libburn-0.8.0 now works with ahci driver on FreeBSD 8-STABLE. - 03 May 2010 Version 0.6.32 of libisofs is able to create ISO images with multiple boot images. All boot catalog parameters described in El-Torito specs can be set and inquired. This was needed to use GRUB boot images for EFI. - 04 May 2010 Release 0.5.6.pl00 of libisoburn makes use of the new libisofs capabilities about boot images. - 11 Jun 2010 libburn-0.8.2 now works on Solaris. - 14 Jun 2010 By release 0.5.8.pl00 of libisoburn, xorriso becomes a public C language API of libisoburn. The emulations of mkisofs and cdrecord have been enhanced. - Tue Jun 29 2010 Version 0.6.34 of libisofs provides new features about hiding file names from directory trees. - Wed Jun 30 2010 libburn-0.8.4 removes some restrictions on operating systems other than Linux and FreeBSD. - Fri Jul 02 2010 Release 0.6.0.pl00 of libisoburn adds more options to the mkisofs emulation of xorriso. It also fixes minor bugs and shortcommings. - Wed Sep 15 2010 Version 0.6.36 of libisofs can produce ISO images which bear a partiton 1 with non-zero start address. They can be mounted from USB stick via the main device file (e.g. /dev/sdb) as well as via the partition device file (e.g. /dev/sdb1). - Fri Sep 17 2010 libburn-0.8.6 lifts the test reservation on DVD-R DL media. - Sat Sep 18 2010 Release 0.6.2.pl00 of libisoburn introduces a partition with non-zero offset for ISO 9660 images on USB sticks, improves mkisofs emulation, and fixes a regression which existed since version 0.4.2. - Wed Oct 20 2010 libburn-0.8.8 can report the used amount of BD spare blocks. - Sat Oct 23 2010 Version 0.6.38 of libisofs can use libjte to produce jigdo files along with the ISO image. Further filesystem images may be appended as MBR partitions 1 to 4. The capability was added to produce boot blocks for computers with MIPS CPU. - Tue Oct 26 2010 Release 0.6.4.pl00 of libisoburn and xorriso makes use of the new libisofs capabilities. - Wed Dec 08 2010 libburn-0.9.0 fixes a regression with SCSI command logging. - Fri Dec 10 2010 Version 0.6.40 of libisofs makes the prediction of the emerging image size less expensive and is able to make images bootable for SUN SPARC systems. - Sun Dec 12 2010 Release 0.6.6.pl00 of libisoburn and xorriso can read ISO images which were copied to a different start address than they were prepared for. - Mon Jan 17 2011 we go for release 1.0.0. This does not indicate a technological overhaul but shall emphasize the maturity of the software. libisofs-1.0.0 fixes a bug about the length of ECMA-119 directory names and is ready to store untranslated ECMA-119 names (violating the specs). libburn-1.0.0.pl00 is now willing to create stdio-drive files with rw-permissions for all, if umask really asks for it. cdrskin now refuses to burn if the foreseeable size exceeds media capacity libisoburn-1.0.0.pl00 can now create an ISO 9660:1999 directory tree, improved the emulation fidelity of command -as mkisofs, lowered the default abort threshold for xorriso batch mode, and increased that threshold for xorriso dialog mode. - Wed Feb 23 2011 release 1.0.2: libisofs fixes several bugs and introduces the capability to copy files inside the ISO filesystem. libburn removed a compilation obstacle on Solaris 9 and improved recognition of stdio pseudo-drives. libisoburn and xorriso fix bugs and make use of the new libisofs capability. xorriso improves its mkisofs emulation. - Thu Mar 10 2011 release 1.0.4: Several bugs were fixed in the libraries and in the mkisofs emulation of xorriso. This emulation xorrisofs has now an own man page and info document. - Sat Apr 09 2011 release 1.0.6: libburn refined its representation of emulated drives. The size alignment of DVD DAO is now 2 kB rather than 32 kB. libisofs produces Joliet names of up to 103 characters. xorriso fixes two bugs and makes use of the library improvements. - Thu Apr 14 2011 release libisoburn-1.0.8: A bug in the mkisofs emulation of xorriso could cause options to be ignored. The problem was freshly introduced with libisoburn-1.0.6. - Fri May 13 2011 release libisofs-1.0.8: Fixes a few rarely occurring bugs that have been found during the last month. - Sat Jun 18 2011 release 1.1.0: The consumption of stack memory was reduced. Statical program analysis found some rarely occurring memory leaks. Several small bugs were fixed. The suffix .plXY was dropped from tarball names of libburn and libisoburn. - Mon Jun 20 2011 patch release libburn-1.1.0.pl01: libburn-1.1.0 compiled only on Linux, FreeBSD, and Solaris, but not on other X/Open compliant systems. - Fri Jul 08 2011 release libisofs-1.1.2 and libisoburn-1.1.2: A severe regression was fixed in libisoburn and xorriso, which was introduced with version 1.0.6. It caused ISO 9660 images to be unreadable if they were written to a write-only random-access file. E.g. by: xorrisofs ... >image.iso - Mon Aug 08 2011 release 1.1.4: Several bugs were fixed in libburn. The most severe of them prevented xorriso on some drives from burning mountable ISO 9660 images to CD media. New means to list drives by their udev symbolic links help to deal with the non-persistent drive addresses on modern GNU/Linux. - Tue Sep 27 2011 release 1.1.6: libisoburn now comes with a test suite. See releng/README. Bugs were fixed in several rarely used features. Processing of ACL and extattr was enabled on FreeBSD. Workarounds try to cope with vanishing udev links on GNU/Linux. - Mon Nov 21 2011 release libburn-1.1.8 and libisoburn-1.1.8: libburn avoids to close and open drive device files while operating on them. xorriso emulation mode xorrecord now has an own manual. libburn and xorriso were prepared to operate on qemu virtio-blk-pci devices. - Sat Jan 28 2012 release 1.2.0: libburn has learned to read and write CD-TEXT with CD SAO audio sessions. It can now read CDRWIN .cue files which define pure audio or pure data sessions. libisofs and libisoburn improved timestamp handling. Several minor bugs were fixed. - Mon Apr 02 2012 release 1.2.2: The handling of intentional deviations from ECMA-119 specifications has been improved in libisofs. libisoburn and xorriso now make use of these improvements. Some rarely occurring bugs have been fixed. - Fri Jul 20 2012 release 1.2.4: libburn and libisofs got some rarely occurring bugs fixed. libisofs learned to produce HFS+ metadata and Apple Partition Map. The capabilities of isohybrid options --efi and --mac have been implemented (GPT and APM). - Tue Jan 08 2013 release 1.2.6: Small improvements were made in libburn. Minor bugs were fixed in the libraries. xorriso improved its capabilities to serve the needs of frontend programs. A proof of concept for a GUI frontend has been implemented: xorriso-tcltk - Mon Mar 18 2013 release 1.2.8: Some rarely occurring bugs were fixed in libisofs and libburn. libburn's handling of incomplete sessions has been improved. xorriso's mkisofs emulation learned to set El Torito section id strings. - Fri May 17 2013 release 1.3.0: Several bugs were fixed in the libraries and in xorriso. The recently introduced new boot preparation capabilities have been tested. New boot preparation options for GRUB2 were added. - Fri May 31 2013 patch release libburn-1.3.0.pl01: cdrskin -msinfo on DVD and BD reported as old session start the same number as the next writable address. Regression introduced by version 1.2.8. - Fri Aug 07 2013 release 1.3.2: cdrskin has acquired the capability to copy audio tracks to .wav files. It can extract CD-TEXT in a form that is readable for humans and for cdrskin itself. Several small bugs were fixed in xorriso. Its capabilities to serve frontend programs in dialog mode have been improved. - Thu Dec 12 2013 release 1.3.4: A long standing hidden bug was fixed, which affected inspection of unformatted DVD+RW. xorriso now by default puts EL Torito boot images to low block addresses. It can report and set read speeds. Several rarely occurring bugs were fixed. - Tue Mar 04 2014 release 1.3.6: libburn learned to operate optical drives and media on NetBSD. libisofs got a bug fix about HFS+ and enhancements about character set conversion. Minor bugs were fixed in libisoburn. xorriso can now find files with names which cannot be represented unchanged in ECMA-119, Joliet, or HFS+. - Sat Jun 28 2014 release 1.3.8: libburn got several bugs fixed. libisofs offers new API calls for inspection of boot sectors in ISO 9660 filesystems. xorriso improved its support for NetBSD, offers new features with command -find, and a command to extract ISO 9660 file content onto standard output or into filter processes. - Sun May 17 2015 release 1.4.0: This release is mainly about bug fixes and a new feature of xorriso to propose commands or as_mkisofs options which can reproduce the boot equipment of the loaded ISO filesystem. - Sat Nov 28 2015 release 1.4.2: libburn got some bugs fixed and learned to inquire the drive serial number. libisofs now sorts data file content by ECMA-119 file names for better reproducability of ISO content. Rock Ridge filenames may be restricted to lengths between 64 and 255 bytes. If needed, a qualified truncation happens. xorriso now can replay boot settings when modifying ISO filesystems. In order to avoid clogging of concurrent Linux SG_IO, xorriso got command -modesty_on_drive to enable an old workaround from IDE master/slave days. The source code underwent a scan by Coverity. About 150 code changes resulted, but no easy-to-trigger bugs were found. - Fri Jan 29 2016 patch release libburn-1.4.2.pl01: cdrskin did not work with "-" (stdin) as input. Regression introduced by version 1.4.2. - Fri Jul 01 2016 release 1.4.4: The capability to use Linux /dev/sg was revived in order to circumvent the sr_mutex lock which hampers concurrent use of optical drives via SG_IO. libisofs now can use appended partitions by El Torito, avoiding the need for duplicate EFI System Partition images. Several bugs have been fixed. - Fri Sep 16 2016 release 1.4.6: libburn now operates optical drives on OpenBSD. libisofs makes pseudo-random id generation reproducible by relating it to the volume date. xorriso interprets environment variable SOURCE_DATE_EPOCH of reproducible-builds.org to prepare a default setting which leads to identical ISO images on identical input and identical constant options. Several moderately embarrassing bugs have been fixed. - Tue Sep 12 2017 release 1.4.8: libburn now refuses to write to SRM+POW formatted BD-R, because it would spoil them. libisofs got several bugs fixed and offers new API calls to support new xorriso features. libisoburn and xorriso offer more detail control with particular boot sector types. New bugs and a regression from version 1.4.4 were fixed. - Sat Sep 15 2018 release 1.5.0 libisofs now can record all xattr namespaces, user defined padding was moved after appended partitions. libisoburn and xorriso make use of the new xattr capability of libisofs. All three libraries got some rarely triggered bugs fixed. - Sat Oct 26 2019 release 1.5.2 The libraries are now ready for building out-of-source. libisofs got minor API extensions. libburn got a bug fixed which spoiled burn runs on some MATSHITA laptop drives. It is now able to list all features which the drive announces. xorriso can now set GPT type GUIDs with appended partitions and the ISO partition. All three libraries got some rarely triggered bugs fixed. - Mon Nov 25 2019 patch release 1.5.2.pl01 cdrskin did not properly burn multiple tracks. Track 1 was slow and burning stalled before track 2. Regression introduced by 1.5.0. - Sat Jan 30 2021 , 07 Feb 2021 release 1.5.4 libburn offers new opportunities to influence drive speed. libisofs got a bug fixed which under medium rare circumstances spoiled zisofs production. libisofs switched to usage of libjte-2.0.0. Further it can now write and read zisofs2 format, which enables compression of files >= 4 GiB. When reading Joliet file trees, the names get stripped of their version numbers by default. After loading metadata, libisofs can now tell which directory tree was loaded and whether Rock Ridge is used. libisoburn and xorriso make use of these new features. xorriso can put out the data stream of -check_media to standard output. It can restore files with many zero bytes as sparse files and it is able to extract recognized boot images into data files on hard disk. A new script xorriso-dd-target helps to put an ISO image onto an USB stick without endangering valuable hard disk content. Several rarely triggered bugs were fixed. - Wed Jun 07 2023 release 1.5.6 libburn can overburn CD media, i.e. write more data than the drive announced as medium capacity. libisofs can assess many of the features which were in effect when a given ISO 9660 filesystem was created. A safety limit was introduced not to exceed the limit of Linux which is imposed on the number of SUSP CE entries which is good for about 60 KiB of AAIP data. Some rarely occuring bugs were fixed in libisofs. libisoburn and xorriso expose the new library features to the user and got some minor improvements. Many minor bugs were fixed. ------------------------------------------------------------------------------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------------ Clarification in my name and in the name of Mario Danic, upcoming copyright holders on toplevel of libburnia. To be fully in effect after the remaining other copyrighted code has been replaced by ours and by copyright-free contributions of our friends: ------------------------------------------------------------------------------ We, the copyright holders, agree on the interpretation that dynamical linking of our libraries constitutes "use of" and not "derivation from" our work in the sense of GPL, provided those libraries are compiled from our unaltered code or from altered code published under GPL. So we will not raise legal protest if you link our libraries dynamically with applications which are not under GPL, or if you distribute our libraries and application tools in binary form, as long as you fulfill the usual condition of GPL to offer a copy of their source code -altered or unaltered- under GPL. We ask you politely to use our work in open source spirit and with the due reference to the entire open source community. If there should really arise the case where above clarification does not suffice to fulfill a clear and neat request in open source spirit that would otherwise be declined for mere formal reasons, only in that case we will duely consider to issue a special license covering only that special case. It is the open source idea of responsible freedom which will be decisive and you will have to prove that you exhausted all own means to qualify for GPL. For now we are firmly committed to maintain one single license: GPL. signed: Mario Danic, Thomas Schmitt Agreement joined later by: Vreixo Formoso AC_DEFUN([LIBBURNIA_SET_FLAGS], [ case $target_os in freebsd* | netbsd*) LDFLAGS="$LDFLAGS -L/usr/local/lib" CPPFLAGS="$CPPFLAGS -I/usr/local/include" ;; esac ]) AC_DEFUN([TARGET_SHIZZLE], [ ARCH="" LIBBURNIA_PKGCONFDIR="$libdir"/pkgconfig AC_MSG_CHECKING([target operating system]) libburn_check_libcam= LIBBURNIA_LDCONFIG_CMD="echo 'No ldconfig run performed. If needed, configure manually for:'" case $target in *-*-linux*) ARCH=linux LIBBURN_ARCH_LIBS= LIBBURNIA_LDCONFIG_CMD=ldconfig ;; *-*-freebsd*) ARCH=freebsd LIBBURN_ARCH_LIBS=-lcam LIBBURNIA_PKGCONFDIR=$(echo "$libdir" | sed 's/\/lib$/\/libdata/')/pkgconfig ;; *-kfreebsd*-gnu*) ARCH=freebsd LIBBURN_ARCH_LIBS=-lcam libburn_check_libcam=yes ;; *-solaris*) ARCH=solaris LIBBURN_ARCH_LIBS=-lvolmgt ;; *) ARCH= LIBBURN_ARCH_LIBS= # AC_ERROR([You are attempting to compile for an unsupported platform]) ;; esac AC_MSG_RESULT([$ARCH]) if test x"$libburn_check_libcam" = xyes then LIBBURNIA_CHECK_LIBCAM fi ]) dnl LIBBURN_ASSERT_VERS_LIBS is by Thomas Schmitt, libburnia project dnl It tests whether -Wl,--version-script=... works with the compiler AC_DEFUN([LIBBURN_ASSERT_VERS_LIBS], [ libburnia_save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -Wl,--version-script=$srcdir/libburn/libburn.ver" AC_TRY_LINK([#include ], [printf("Hello\n");], [vers_libs_test="yes"], [vers_libs_test="no"]) if test x$vers_libs_test = xyes then LIBLDFLAGS="-Wl,--version-script=$srcdir/libburn/libburn.ver" fi LDFLAGS="$libburnia_save_LDFLAGS" AC_SUBST(LIBLDFLAGS) ]) dnl LIBBURNIA_SET_PKGCONFIG determines the install directory for the *.pc file. dnl Important: Must be performed _after_ TARGET_SHIZZLE dnl AC_DEFUN([LIBBURNIA_SET_PKGCONFIG], [ ### for testing --enable-libdir-pkgconfig on Linux ### LIBBURNIA_PKGCONFDIR="$libdir"data/pkgconfig if test "x$LIBBURNIA_PKGCONFDIR" = "x$libdir"/pkgconfig then dummy=dummy else AC_ARG_ENABLE(libdir-pkgconfig, [ --enable-libdir-pkgconfig Install to $libdir/pkgconfig on any OS, default=no], , enable_libdir_pkgconfig="no") AC_MSG_CHECKING([for --enable-libdir-pkgconfig]) if test "x$enable_libdir_pkgconfig" = xyes then LIBBURNIA_PKGCONFDIR="$libdir"/pkgconfig fi AC_MSG_RESULT([$enable_libdir_pkgconfig]) fi libburnia_pkgconfig_override="no" AC_ARG_ENABLE(pkgconfig-path, [ --enable-pkgconfig-path=DIR Absolute path of directory for libisofs-*.pc], libburnia_pkgconfig_override="yes" , enable_pkgconfig_path="none") AC_MSG_CHECKING([for overridden pkgconfig directory path]) if test "x$enable_pkgconfig_path" = xno then libburnia_pkgconfig_override="no" fi if test "x$enable_pkgconfig_path" = x -o "x$enable_pkgconfig_path" = xyes then libburnia_pkgconfig_override="invalid argument" fi if test "x$libburnia_pkgconfig_override" = xyes then LIBBURNIA_PKGCONFDIR="$enable_pkgconfig_path" AC_MSG_RESULT([$LIBBURNIA_PKGCONFDIR]) else AC_MSG_RESULT([$libburnia_pkgconfig_override]) fi AC_SUBST(LIBBURNIA_PKGCONFDIR) dnl For debugging only ### AC_MSG_RESULT([LIBBURNIA_PKGCONFDIR = $LIBBURNIA_PKGCONFDIR]) ]) dnl LIBBURNIA_CHECK_ARCH_LIBS is by Thomas Schmitt, libburnia project dnl It tests whether the OS dependent libraries are available. dnl With libisoburn they are needed only for the case that indirect linking dnl does not work. So it is worth a try to omit them. dnl $1 = "mandatory" or "optional" define the action if test linking fails. dnl "silent" is like "optional" but without message. AC_DEFUN([LIBBURNIA_CHECK_ARCH_LIBS], [ libburnia_save_LIBS="$LIBS" if test "x$LIBBURN_ARCH_LIBS" = x then dummy=dummy else LIBS="$LIBS $LIBBURN_ARCH_LIBS" AC_TRY_LINK([#include ], [printf("Hello\n");], [archlibs_test="yes"], [archlibs_test="no"]) LIBS="$libburnia_save_LIBS" if test x$archlibs_test = xno then if test x"$1" = xmandatory then echo >&2 echo "FATAL: Test linking with mandatory library options failed: $LIBBURN_ARCH_LIBS" >&2 echo >&2 (exit 1); exit 1; else if test x"$1" = xoptional then echo "disabled linking with $LIBBURN_ARCH_LIBS (because not found)" fi LIBBURN_ARCH_LIBS="" fi else if test x"$1" = xsilent then dummy=dummy else echo "enabled linking with $LIBBURN_ARCH_LIBS" fi fi fi ]) dnl LIBBURNIA_CHECK_LINUX_SCSI is by Thomas Schmitt, libburnia project dnl AC_DEFUN([LIBBURNIA_CHECK_LINUX_SCSI], [ dnl Check whether it is a Linux without scsi/scsi.h libburn_scsi_disabled= if test x"$ARCH" = xlinux then AH_TEMPLATE([Libburn_use_sg_dummY], [Define to compile without OS specific SCSI features]) AC_MSG_CHECKING([for missing scsi/scsi.h on Linux]) AC_TRY_COMPILE([ #ifdef __linux #include #endif ], [;], [AC_MSG_RESULT([no])], [AC_DEFINE([Libburn_use_sg_dummY], [yes]) libburn_scsi_disabled=yes AC_MSG_RESULT([yes])] ) fi if test x"$libburn_scsi_disabled" = xyes then echo "disabled operation of optical drives via SCSI" fi ]) dnl LIBBURNIA_CHECK_LIBCAM is by Thomas Schmitt, libburnia project dnl AC_DEFUN([LIBBURNIA_CHECK_LIBCAM], [ dnl Check whether libcam is requested for FreeBSD kernel but missing libburn_scsi_disabled= if test x"$LIBBURN_ARCH_LIBS" = x"-lcam" then AH_TEMPLATE([Libburn_use_sg_dummY], [Define to compile without OS specific SCSI features]) AC_MSG_CHECKING([for missing libcam for SCSI on FreeBSD kernel]) dnl If libcam is not available, LIBBURN_ARCH_LIBS will be made empty LIBBURNIA_CHECK_ARCH_LIBS(silent) if test x"$LIBBURN_ARCH_LIBS" = x then AC_DEFINE([Libburn_use_sg_dummY], [yes]) libburn_scsi_disabled=yes AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi fi if test x"$LIBBURN_ARCH_LIBS" = x"-lcam" then AC_MSG_CHECKING([for missing libcam headers]) AC_TRY_COMPILE([ #include #include #include #include ], [;], [AC_MSG_RESULT([no])], [AC_DEFINE([Libburn_use_sg_dummY], [yes]) libburn_scsi_disabled=yes AC_MSG_RESULT([yes])] ) fi if test x"$libburn_scsi_disabled" = xyes then echo "disabled operation of optical drives via SCSI" fi ]) #!/bin/sh -x aclocal -I . libtoolize --copy --force autoconf # ts A61101 : libburn is not prepared for config.h # autoheader automake --foreign --add-missing --copy --include-deps ------------------------------------------------------------------------------ libburnia-project.org scdbackup.sourceforge.net/cdrskin_eng.html ------------------------------------------------------------------------------ cdrskin. By Thomas Schmitt Integrated sub project of libburnia-project.org but also published via: http://scdbackup.sourceforge.net/cdrskin_eng.html http://scdbackup.sourceforge.net/cdrskin-1.5.7.tar.gz Copyright (C) 2006-2023 Thomas Schmitt, provided under GPL version 2 or later. ------------------------------------------------------------------------------ cdrskin is a limited cdrecord compatibility wrapper which allows to use most of the libburn features from the command line. Currently it is fully supported on GNU/Linux with kernels >= 2.4, on FreeBSD, on OpenSolaris, and on NetBSD. IDE drives under Linux 2.4 need kernel module ide-scsi. ATA and SATA drives under FreeBSD need kernel module atapicam. On other X/Open compliant systems there will only be emulated drives, but no direct MMC operation on real CD/DVD/BD drives. By using this software you agree to the disclaimer at the end of this text "This software is provided as is. There is no warranty implied and ..." Compilation, First Glimpse, Installation Obtain cdrskin-1.5.7.tar.gz, take it to a directory of your choice and do: tar xzf cdrskin-1.5.7.tar.gz cd cdrskin-1.5.7 Within that directory execute: ./configure --prefix=/usr make This will already produce a cdrskin binary. But it will be necessary to install libburn in order to use this binary. In order to surely get a standalone binary, execute cdrskin/compile_cdrskin.sh Version identification and help texts available afterwards: cdrskin/cdrskin -version cdrskin/cdrskin --help cdrskin/cdrskin -help man cdrskin/cdrskin.1 Install (eventually as superuser) cdrskin to a directory where it can be found: The command for global installation of both, libburn and cdrskin is make install If the library libburn.so.4 is not found with a test run of cdrskin, then try whether command ldconfig makes it accessible. With the statically linked binary this should not matter. With that static binary you may as well do the few necessary actions manually. If cdrskin was already installed by a previous version, or by "make install" in the course of this installation, then find out where: which cdrskin Copy your standalone binary to exactly the address which you get as reply. E.g.: cp cdrskin/cdrskin /usr/bin/cdrskin Check the version timestamps of the globally installed binary cdrskin -version It is not necessary for the standalone cdrskin binary to have libburn installed, since it incorporates the necessary libburn parts at compile time. It will not collide with an installed version of libburn either. But libpthread must be installed on the system and glibc has to match. (See below for a way to create a totally static linked binary.) To install the man page, you may do: echo $MANPATH and choose one of the listed directories to copy the man-page under its ./man1 directory. Like: cp cdrskin/cdrskin.1 /usr/share/man/man1/cdrskin.1 Note: The content of the cdrskin tarball is essentially the complete libburn of the same version number. You may thus perform above steps in a local SVN copy of libburn or in a unpacked libburn tarball as well. Usage The user of cdrskin needs rw-permission for the CD burner device. A list of rw-accessible drives can be obtained by cdrskin --devices CD devices which offer no rw-permission are invisible to normal users. The superuser should be able to see any usable drive and then set the permissions as needed. If this hangs then there is a drive with unexpected problems (locked, busy, broken, whatever). You might have to guess the address of your (non-broken) burner by other means, then. On Linux 2.4 this would be some /dev/sgN and on 2.6. some /dev/srM or /dev/hdX. The output of cdrskin --devices might look like 0 dev='/dev/sr0' rwrwr- : '_NEC' 'DVD_RW ND-4570A' 1 dev='/dev/sr1' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B' On Linux, full and insecure enabling of both for everybody would look like chmod a+rw /dev/sr0 /dev/hda This is equivalent to the traditional setup chmod a+x,u+s cdrecord. On FreeBSD, device rw-permissions are to be set in /etc/devfs.rules. On Solaris, pfexec privileges may be restricted to "basic,sys_devices". On NetBSD, rw-permission may be granted by chmod a+rw /dev/rcd?d. See below "System Dependend Drive Permission Examples". I strongly discourage to run cdrskin with setuid root or via sudo ! It is not checked for the necessary degree of hacker safety. Better consider to grant the necessary permissions to group "floppy" and to add users to it. A behavioral conflict is known between any burn software and demons like hald which probe CD drives. This can spoil burn runs for CD-R or CD-RW. You may have to keep your hald away from the drive. See for example http://www.freebsd.org/gnome/docs/halfaq.html Helpful with Linux kernel 2.4 is a special SCSI feature: It is possible to address a scsi(-emulated) drive via associated device files which are not listed by option --devices but point to the same SCSI addresses as listed device files. This addressing via e.g. /dev/sr0 or /dev/scd1 is compatible with generic read programs like dd and with write program growisofs. For finding /dev/sg1 from /dev/sr0, the program needs rw-access to both files. Usage examples For options and recordable media classes see man 1 cdrskin Get an overview of cdrecord style addresses of available devices cdrskin -scanbus cdrskin dev=ATA -scanbus cdrskin --devices Adresses reported with dev=ATA need prefix "ATA:". Address examples: dev=0,1,0 dev=ATA:1,0,0 dev=/dev/sg1 dev=/dev/hdc dev=/dev/sr0 See also "Drive Addressing" below. Obtain some info about the drive cdrskin dev=0,1,0 -checkdrive Obtain some info about the drive and the inserted media cdrskin dev=0,1,0 -atip -v -minfo Prepare CD-RW or DVD-RW for re-use, DVD-RAM or BD-RE for first use cdrskin -v dev=/dev/sg1 blank=as_needed -eject Format DVD-RW to avoid need for blanking before re-use cdrskin -v dev=0,1,0 blank=format_overwrite De-format DVD-RW to make it capable of multi-session again cdrskin -v dev=/dev/sr0 blank=deformat_sequential Burn image file my_image.iso to media cdrskin -v dev=0,1,0 speed=12 fs=8m driveropts=burnfree padsize=300k \ -eject my_image.iso Write multi-session to the same CD , DVD-R[W] or DVD+R[/DL] cdrskin dev=/dev/hdc padsize=300k -multi 1.iso cdrskin dev=/dev/hdc padsize=300k -multi 2.iso cdrskin dev=/dev/hdc padsize=300k -multi 3.iso cdrskin dev=/dev/hdc padsize=300k 4.iso Get multi-session info for option -C of program mkisofs: c_values=$(cdrskin dev=/dev/hdc -msinfo 2>/dev/null) mkisofs ... -C "$c_values" ... Burn a compressed afio archive to media on-the-fly find . | afio -oZ - | cdrskin -v dev=0,1,0 fs=32m speed=8 \ driveropts=burnfree padsize=300k - Burn 6 audio tracks from files with different formats to CD (not to any DVD). Anything except .wav or .au files has to be converted into raw format first. See below "Audio CD" for specifications. ogg123 -d raw -f track01.cd /path/to/track1.ogg oggdec -R -o track02.cd /path/to/track2.ogg lame --decode -t /path/to/track3.mp3 track03.cd madplay -o raw:track04.cd /path/to/track4.mp3 mppdec --raw-le /path/to/track5.mpc track05.cd cdrskin -v dev=0,1,0 blank=fast -eject speed=48 -sao \ -audio -swab track0[1-5].cd /path/to/track6.wav Extract audio tracks and CD-TEXT from CD into directory /home/me/my_cd: mkdir /home/me/my_cd cdrskin -v dev=/dev/sr0 extract_audio_to=/home/me/my_cd \ cdtext_to_v07t=/home/me/my_cd/cdtext.v07t Restrictions Several advanced CD related options of cdrecord are still unsupported. See output of command cdrskin --list_ignored_options If you have use cases for them, please report your wishes and expectations. On the other hand, the capability of multi-session and of writing streams of unpredicted length surpass the current DVD capabilities of cdrecord. Inspiration and Standard cdrskin combines the command line interface standard set by cdrecord with libburn, which is a control software for optical drives according to standard MMC-5. For particular CD legacy commands, standards MMC-3 and MMC-1 apply. For the original meaning of cdrecord options see : man cdrecord (http://cdrecord.berlios.de/old/private/man/cdrecord-2.0.html) Do not bother Joerg Schilling with any cdrskin problems. (Be cursed if you install cdrskin as "cdrecord" without clearly forwarding this "don't bother Joerg" demand.) cdrskin does not contain any bytes copied from cdrecord's sources. Many bytes have been copied from the message output of cdrecord runs, though. I am thankful to Joerg Schilling for every single one of them. I have the hope that Joerg feels more flattered than annoyed by cdrskin. Many thanks to Andy Polyakov for his dvd+rw-tools http://fy.chalmers.se/~appro/linux/DVD+RW/tools which provide me with examples and pointers into MMC specs for DVD writing. Startup Files If not --no_rc is the first argument then cdrskin attempts on startup to read arguments from the following three files: /etc/default/cdrskin /etc/opt/cdrskin/rc /etc/cdrskin/cdrskin.conf $HOME/.cdrskinrc The files are read in the sequence given above. Each readable line is treated as one single argument. No extra blanks. A first character '#' marks a comment, empty lines are ignored. Example content of a startup file: # This is the default device dev=0,1,0 # Some more options fifo_start_at=0 fs=16m Audio CD Lorenzo Taylor enabled option -audio in cdrskin (thanks !) and reports neat results with audio data files which are : headerless PCM (i.e. uncompressed) 44100 Hz sampling rate 16 bits per sample stereo (2 channels) little-endian byte order with option -swab, or big-endian without -swab Files with name extension .wav get examined wether they are in Microsoft WAVE format with above parameters and eventually get extracted by cdrskin itself. In the same way files with name extension .au get examined wether they are in SUN's audio format. For both formats, track format -audio and eventual endianness option -swab are enabled automatically. Any other formats are to be converted to format .wav with above parameters or to be extracted as raw CD track data by commands like those given above under "Usage examples". Those raw files need option -audio and in most cases option -swab to mark them as little-endian/Intel/LSB-first 16-bit data. Incorrect endianness setting results in random noise on CD. I myself am not into audio. So libburn-hackers@pykix.org might be the best address for suggestions, requests and bug reports. DVD+RW , DVD-RAM , BD-RE These random access media get treated as blank media regardless wether they hold data or not. Options -audio and -multi are not allowed. Only one track is allowed. -toc does not return information about the media content. Speed is counted in DVD units (i.e. 1x = 1,385,000 bytes/second) or BD units (1x = 4,495,625 bytes/second). Currently there is no difference between -sao and -tao. If ever, then -tao will be the mode which preserves the current behavior. BD-RE media need formatting before first use. cdrskin option "blank=as_needed" recognizes unformatted BD-RE and applies a lengthy formatting run. During write operations DVD-RAM and BD-RE automatically apply Defect Management. This usually slows them down to half nominal speed. If drive and media produce flawless results anyway, then one can try to reach full nominal speed by option "stream_recording=on". In this case bad blocks are not detected during write and not even previously known bad blocks are avoided. So you have to make your own readability tests and go back to half speed as soon as the first read errors show up. Instead of "on" one may also set a start address for stream recording. Like "stream_recording=100m". This will write slowly to the first 100 MB of the media and accelerate when writing to higher addresses. Option --grow_overwriteable_iso allows -multi (although unneeded), enables -msinfo and -toc, and makes blank=fast an invalidator for ISO filesystems on overwriteable media. Initial session (equivalent to growisofs -Z): mkisofs ... | cdrskin --grow_overwriteable_iso blank=fast ... Add-on session (equivalent to growisofs -M): cparms=$(cdrskin dev=/dev/sr0 --grow_overwriteable_iso -msinfo) mkisofs -C "$cparms" -M /dev/sr0 ... | \ cdrskin dev=/dev/sr0 --grow_overwriteable_iso ... - DVD-RW , DVD-R , DVD-R DL DVD-RW are usable if formatted to state "Restricted Overwrite" or if in state "Sequential Recording". DVD-R are always in sequential state. DVD-R DL are always sequential and incapable of multi-session. "Sequential" is the state of unused media and of media previously blanked or written by cdrecord. dvd+rw-format -blank can also achieve this state. The according cdrskin option is blank=deformat_sequential . If "Incremental Streaming" is available, then sequential media are capable of multi-session like CD-R[W]. (But not capable of -audio recording.) This means they need option -multi to stay appendable, need to be blanked to be writeable from start, return useable info with -toc and -msinfo, eventually perform appending automatically. Without Incremental Streaming offered by the drive, only write mode DAO is available with sequential DVD-R[W]. It only works with blank media, allows only one single track, no -multi, and demands a fixely predicted track size. (growisofs uses it with DVD-R[W] if option -dvd-compat is given.) Overwriteable DVD-RW behave much like DVD+RW. "Restricted" refers only to the granularity of random access and block size which have always to be aligned to full 32 kB. Sequential DVD-RW are converted into overwriteable DVD-RW by cdrskin dev=... -v blank=format_overwrite (Command dvd+rw-format -force can achieve Restricted Overwrite, too.) Formatting or first use of freshly formatted DVD-RW can produce unusual noises from the drive and last several minutes. Depending on mutual compatibility of drive and media, formatting can yield unusable media. It seems that those die too on blanking by cdrecord, dvd+rw-format or cdrskin. Perils of DVD-RW. There are three DVD-RW formatting variants with cdrskin currently: blank=format_overwrite uses "DVD-RW Quick" formatting (MMC-type 15h) and writes a first session of 128 MiB. This leads to media which are expandable and random addressable by cdrskin. blank=format_overwrite_quickest uses "DVD-RW Quick" formatting (type 15h) too, but leaves the media in "intermediate" state. In the first session of writing one may only write sequentially to such a DVD. After that, it gets random addressable by cdrskin. DVD-ROM drives might show ill behavior with them. blank=format_overwrite_full uses preferably "Full Format" (type 00h). This formatting lasts as long as writing a full DVD. It includes writing of lead-out which is said to be good for DVD ROM compatibility. De-formatting options are available to make overwriteable DVD-RW sequential: blank=deformat_sequential performs thorough blanking of all states of DVD-RW. blank=all and blank=fast perform the same thorough blanking, but refuse to do this with overwriteable DVD-RW, thus preserving their formatting. The specs allow minimal blanking but the resulting media on my drives offer no Incremental Streaming afterwards. So blank=fast will do full blanking. blank=deformat_sequential_quickest is faster but might yield DAO-only media. DVD+R , DVD+R DL , BD-R From the view of cdrskin they behave much like DVD-R. Each track gets wrapped into an own session, though. DVD+R DL appear as extra large DVD+R. cdrskin does not allow to set the address of the layer break where a reading drive might show some delay while switching between both media layers. BD-R are sold unformatted blank. If used without initial formatting then the drive is supposed to format them to maximum payload size with no Defect Management (see also above with BD-RE). If Defect Management is desired then BD-R need to be formatted before the first attempt to write a session to them. blank=format_if_needed will detect the situation and eventually apply default sized Defect Management formatting. blank=format_defectmgt_* will apply non-default parameters to formatting. Emulated Drives cdrskin can use filesystem objects as emulated drives. Regular files or block devices appear similar to DVD-RAM. Other file types resemble blank DVD-R. Necessary precondition is option --allow_emulated_drives which is not accepted if cdrskin took another user identity because of the setuid bit of its access permissions. Addresses of emulated drives begin with prefix "stdio:". E.g. dev=stdio:/tmp/my_pseudo_drive For safety reasons the superuser is only allowed to use /dev/null as emulated drive. See man page section FILES for a way to lift that ban. ------------------------------------------------------------------------------ Special compilation variations All following options of ./configure and cdrskin/compile_cdrskin.sh are combinable. After runs of ./configure do as next: make clean ; make In some situations Linux may deliver a better write performance to drives if the track input is read with O_DIRECT (see man 2 open). The API call burn_os_open_track_src() and the input readers of cdrskin and libburn fifo can be told to use this peculiar read mode by: --enable-track-src-odirect But often cdrskin option dvd_obs=64k will yield even better performance in such a situation. 64k can be made default at compile time by cdrskin/compile_cdrskin.sh -dvd_obs_64k It can also be enabled at configure time by ./configure ... --enable-dvd-obs-64k ... Alternatively the transport of SCSI commands can be done via libcdio-0.83. You may install it and re-run libburn's ./configure with option --enable-libcdio Add option -use_libcdio to your run of cdrskin/compile_cdrskin.sh . You may get a (super fat) statically linked binary by : cdrskin/compile_cdrskin.sh -static if your system supports static linking, at all. This will not help with kernels which do not properly support the necessary low-level interfaces chosen by your compile-time libraries. A size reduced but fully functional binary may be produced by cdrskin/compile_cdrskin.sh -do_strip An extra lean binary with reduced capabilities is created by cdrskin/compile_cdrskin.sh -do_diet -do_strip It will not read startup files, will abort on option dev_translation= , will not have a fifo buffer, and will not be able to put out help texts or debugging messages. Linux only: libburn tries to avoid a collision with udev's drive examination by waiting 0.1 seconds before opening the device file for a longer time, after udev might have been alarmed by drive scanning activities. The waiting time can be set at ./configure time with microsecond granularity. E.g. 2 seconds: CFLAGS="$CFLAGS -DLibburn_udev_wait_useC=2000000" ./configure ...options... Waiting can be disabled by zero waiting time: CFLAGS="$CFLAGS -DLibburn_udev_wait_useC=0" Alternatively, libburn can try to be nice by opening the device file, closing it immediately, waiting, and only then opening it for real: CFLAGS="$CFLAGS -DLibburn_udev_extra_open_cyclE -DLibburn_udev_wait_useC=500000" ------------------------------------------------------------------------------ System Dependend Drive Permission Examples Accessing the optical drives requires privileges which usually are granted only to the superuser. Linux, FreeBSD, Solaris, NetBSD, offer quite different approaches for avoiding the need for unrestricted privileges. First check whether some friendly system setting already allows you to access the drives as normal user: cdrskin --devices Those drives of which you see address and type strings are already usable. If there remain drives invisible which the superuser can see by the same command, then the following examples might help: --------------- On all systems: --------------- Add the authorized users of CD drives to group "floppy" in /etc/group. If missing: create this group. Changes to /etc/group often only affect new login sessions. So log out and in before making the first tests. --------- On Linux: --------- Allow rw-access to the drives chgrp floppy /dev/sr0 /dev/sr1 chmod g+rw /dev/sr0 /dev/sr1 It might be necessary to perform chgrp and chmod after each reboot or to edit distro dependent device configuration files for permanent settings. ----------- On FreeBSD: ----------- Edit /etc/devfs.rules and make sure to have these lines [localrules=10] add path 'acd*' mode 0664 group floppy add path 'cd*' mode 0664 group floppy add path 'pass*' mode 0664 group floppy add path 'xpt*' mode 0664 group floppy [localrules=5] add path 'pass*' mode 0664 group floppy add path 'cd*' mode 0664 group floppy add path 'xpt*' mode 0664 group floppy add path 'acd*' mode 0664 group floppy Edit /etc/rc.conf and add the following line if missing devfs_system_ruleset="localrules" This gets into effect by reboot or by command /etc/rc.d/devfs start ----------- On Solaris: ----------- Run cdrskin by pfexec cdrskin ...arguments... The following settings will make pfexec keep original UID and EUID and prevent most superuser powers. Be aware that you still can manipulate all device files if you have the file permissions for that. Full root privileges for cdrskin can then be acquired only by command su. Edit /etc/security/exec_attr and add this line to the other "Media Backup" lines: Media Backup:solaris:cmd:::/usr/local/bin/cdrskin:privs=basic,sys_devices Edit /etc/user_attr and add profile "Media Backup" to the user's line: thomas::::profiles=Media Backup,Primary Administrator;roles=root See also man privileges, man exec_attr, man user_attr. Then allow the group r-access to the drives pfexec chgrp floppy /dev/rdsk/c3t0d0s2 /dev/rdsk/c4t0d0s2 pfexec chmod g+r /dev/rdsk/c3t0d0s2 /dev/rdsk/c4t0d0s2 The last two commands have to be executed after each boot. I do not know the relevant device configuration files yet. ---------- On NetBSD: ---------- Allow rw-access to the drives chgrp floppy /dev/rcd[01]d chmod g+rw /dev/rcd[01]d ------------------------------------------------------------------------------ Project aspects and legal stuff ------------------------------------------------------------------------------ Important Disclaimer : This software is provided as is. There is no warranty implied and no protection against possible damages. You use this on your own risk. Don't blame me or other authors of libburn if anything goes wrong. Actually, in case of severe trouble, nearly always the drive and the media are the cause. Any mistake of the burn program is supposed to be caught by the drive's firmware and to lead to mere misburns. The worst mishaps which hit the author imposed the need to reboot the system because of drives gnawing endlessly on ill media. Permanent hardware damage did not occur in 13 years of development. But one never knows ... ------------------------------------------------------------------------------ Interested users are invited to participate in the development of cdrskin. Contact: scdbackup@gmx.net or libburn-hackers@pykix.org . We will keep copyright narrow but will of course acknowledge valuable contributions in a due way. ------------------------------------------------------------------------------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later as published by the Free Software Foundation. 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 ------------------------------------------------------------------------------ Based on and sub project of: libburnia-project.org By Mario Danic and Thomas Schmitt Copyright (C) 2006-2023 Mario Danic, Thomas Schmitt libburnia-project.org is inspired by and in other components still containing parts of Libburn. By Derek Foreman and Ben Jansens Copyright (C) 2002-2006 Derek Foreman and Ben Jansens See toplevel README for an overview of the current copyright situation in libburnia-project.org. #!/bin/sh set -x # This script documents how this cdrskin version was derived from # a vanilla libburn version. It is not intended nor needed for any # use of cdrskin but included here only to show the technical # relationship between both projects - which are close friends # and issue roughly the same software. # # Package maintainers are advised to cover rather libburn than # cdrskin unless they put only emphasis on the cdrecord emulation # provided by cdrskin. libburn contains cdrskin - cdrskin is an # oscillating, friendly and coordinated fork of libburn. # # Script results are a source tarball and two binaries # one dynamic and one static in respect to system libs. # Both binaries are static in respect to libburn. # # The script is to be run in the directory above the toplevel # directory of libburn (or cdrskin) development. # # The top level directory in the SVN snapshot is named intermediate="./libburn_pykix" # libburn source used: http://libburnia.pykix.org # Downloaded by: # $ svn co http://libburnia-svn.pykix.org/libburn/tags/... $intermediate # packed up in a tarball just to save it from inadverted changes by # $ tar czf libburn_svn.tgz $intermediate original="./libburn_svn_release.tgz" # Historic moments: # original="./libburn_svn_A60815.tgz" # original="./libburn_cdrskin_A60819.tgz" # My changes are in $changes , mainly in $changes/cdrskin changes="./libburn-release" skin_release="1.5.6" patch_level="" # patch_level=".pl00" skin_rev="$skin_release""$patch_level" # The result directory and the name of the result tarballs target="./cdrskin-${skin_release}" cdrskin_tarball="./cdrskin-${skin_rev}.tar.gz" cdrskin_tarball_svn="./cdrskin-${skin_rev}.svn.tar.gz" # (This once earned me an embarrassingly blooping source tarball) # compile_dir="$changes" compile_dir="$target" compile_cmd="./cdrskin/compile_cdrskin.sh" compile_static_opts="-static" compile_result="cdrskin/cdrskin" man_to_html_cmd="./cdrskin/convert_man_to_html.sh" man_page_html="cdrskin/man_1_cdrskin.html" # bintarget_dynamic="cdrskin_${skin_rev}-x86-suse9_0" # bintarget_dynamic="cdrskin_${skin_rev}-amd64-suse10_2" bintarget_dynamic="cdrskin_${skin_rev}-amd64-debian8_0" bintarget_static="$bintarget_dynamic"-static if test -d "$changes" then dummy=dummy else echo "$0 : FATAL : no directory $changes" >&2 exit 1 fi for i in "$target" "$intermediate" do if test -e "$i" then echo "$0 : FATAL : already existing $i" >&2 exit 2 fi done if test -f "$original" then dummy=dummy else echo "$0 : FATAL : no file $original" >&2 exit 3 fi # Unpack SVN snapshot. tar xzf "$original" # Rename the directory to the cdrskin name mv "$intermediate" "$target" # Copy the changes from the development tree # cdrskin_dir="$changes"/cdrskin libburn_dir="$changes"/libburn cdrskin_target="$target"/cdrskin libburn_target="$target"/libburn # Create version timestamp # timestamp="$(date -u '+%Y.%m.%d.%H%M%S')" # echo "$timestamp" # echo '#define Cdrskin_timestamP "'"$timestamp"'"' >"$cdrskin_dir"/cdrskin_timestamp.h # Add the cdrskin files if test -e "$cdrskin_target" then rm -rf "$cdrskin_target" fi cp -a "$cdrskin_dir" "$cdrskin_target" # Remove copied vim.swp and binaries rm "$cdrskin_target"/.*.swp rm "$cdrskin_target"/.*.swo rm "$cdrskin_target"/*.o rm "$cdrskin_target"/cdrfifo rm "$cdrskin_target"/cdrskin rm "$cdrskin_target"/cleanup for i in std new make old do if test -e "$cdrskin_target"/cdrskin_"$i" then rm "$cdrskin_target"/cdrskin_"$i" fi done # Remove eventual SVN stuff from cdrskin directory for i in .deps .dirstamp .libs do if test -e "$cdrskin_target"/"$i" then rm -rf "$cdrskin_target"/"$i" fi done # Remove GIFs of cdrskin_eng.html rm "$cdrskin_target"/doener_*.gif "$cdrskin_target"/doener_*.png # Remove automatically generated HTML man page rm "$cdrskin_target"/man_1_cdrskin.html # Remove all add_ts_changes_to_libburn besides this one for i in "$cdrskin_target"/add_ts_changes_to_libburn* do if test $(basename "$0") = $(basename "$i") then dummy=dummy else rm $i fi done # Remove libcevap rm -rf "$target"/libcevap # Remove unwanted SVN stuff (TODO: avoid downloading it) for i in "$target"/.svn "$target"/*/.svn do if test "$i" = "$target"'/*/.svn' then dummy=dummy else if test -e "$i" then rm -rf "$i" fi fi done # Make SVN state tarball for the libburn team tar czf "$cdrskin_tarball_svn" "$target" # Get over dependecy on autotools. Rely only on cc, make et. al. # This is not the same as "make dist" but i can do it without # having to evaluate the quality of said "make dist" # ( cd "$target" ; ./bootstrap ) # Remove unwanted stuff after bootstrap for i in "$target"/autom4te.cache do if echo "$i" | grep '\*' >/dev/null then dummy=dummy else if test -e "$i" then rm -rf "$i" fi fi done # Repair non-portable shell code output of ./bootstrap ( cd "$compile_dir" || exit 1 sed -e 's/^for ac_header in$/test -z 1 \&\& for ac_header in dummy/' \ < ./configure > ./configure-repaired if test "$?" = 0 then echo "$0: Empty 'for ac_header in' found in configure." >&2 fi mv ./configure-repaired ./configure chmod a+rx,go-w,u+w ./configure ) # Pack it up to the new libburn+cdrskin-tarball tar czf "$cdrskin_tarball" "$target" # Produce a static and a dynamic binary, and a HTML man page ( cd "$compile_dir" || exit 1 ./configure make "$compile_cmd" -O2 -do_strip cp "$compile_result" "../$bintarget_dynamic" if test -n "$compile_static_opts" then "$compile_cmd" $compile_static_opts -O2 -do_strip cp "$compile_result" "../$bintarget_static" fi ( cd cdrskin ; cc -g -Wall -o unite_html_b_line unite_html_b_line.c ) "$man_to_html_cmd" mv "$man_page_html" .. ) # Remove the build area # Disable this for debugging the merge process rm -rf "$target" # Show the result ./"$bintarget_dynamic" -version ./"$bintarget_static" -version ls -l "$cdrskin_tarball" ls -l "$bintarget_dynamic" ls -l "$bintarget_static" ls -l $(basename "$man_page_html") #!/bin/sh set -x # This script documents how this cdrskin version was derived from # a vanilla libburn version. It is not intended nor needed for any # use of cdrskin but included here only to show the technical # relationship between both projects - which are close friends # and issue roughly the same software. # # Package maintainers are advised to cover rather libburn than # cdrskin unless they put only emphasis on the cdrecord emulation # provided by cdrskin. libburn contains cdrskin - cdrskin is an # oscillating, friendly and coordinated fork of libburn. # # Script results are a source tarball and two binaries # one dynamic and one static in respect to system libs. # Both binaries are static in respect to libburn. # # The script is to be run in the directory above the toplevel # directory of libburn (or cdrskin) development. # # The top level directory in the SVN snapshot is named intermediate="./libburn_pykix" # libburn source used: http://libburnia-project.org # Downloaded by: # $ svn co http://libburnia-project.org/libburn/tags/... $intermediate # packed up in a tarball just to save it from inadverted changes by # $ tar czf libburn_svn.tgz $intermediate original="./libburn_svn.tgz" # Historic moments: # original="./libburn_svn_A60815.tgz" # original="./libburn_cdrskin_A60819.tgz" # My changes are in $changes , mainly in $changes/cdrskin changes="./libburn-develop" skin_release="1.5.7" patch_level="" skin_rev="$skin_release""$patch_level" # The result directory and the name of the result tarballs target="./cdrskin-${skin_release}" cdrskin_tarball="./cdrskin-${skin_rev}.tar.gz" cdrskin_tarball_svn="./cdrskin-${skin_rev}.svn.tar.gz" # (This once earned me an embarrassingly blooping source tarball) # compile_dir="$changes" compile_dir="$target" compile_cmd="./cdrskin/compile_cdrskin.sh" compile_static_opts="-static" compile_result="cdrskin/cdrskin" man_to_html_cmd="./cdrskin/convert_man_to_html.sh" man_page_html="cdrskin/man_1_cdrskin.html" # bintarget_dynamic="cdrskin_${skin_rev}-amd64-suse10_2" bintarget_dynamic="cdrskin_${skin_rev}-amd64-debian8_0" bintarget_static="$bintarget_dynamic"-static if test -d "$changes" then dummy=dummy else echo "$0 : FATAL : no directory $changes" >&2 exit 1 fi for i in "$target" "$intermediate" do if test -e "$i" then echo "$0 : FATAL : already existing $i" >&2 exit 2 fi done if test -f "$original" then dummy=dummy else echo "$0 : FATAL : no file $original" >&2 exit 3 fi # Unpack SVN snapshot. tar xzf "$original" # Rename the directory to the cdrskin name mv "$intermediate" "$target" # Copy the changes from the development tree # cdrskin_dir="$changes"/cdrskin libburn_dir="$changes"/libburn cdrskin_target="$target"/cdrskin libburn_target="$target"/libburn # Create version timestamp timestamp="$(date -u '+%Y.%m.%d.%H%M%S')" echo "$timestamp" echo '#define Cdrskin_timestamP "'"$timestamp"'"' >"$cdrskin_dir"/cdrskin_timestamp.h # Add the cdrskin files if test -e "$cdrskin_target" then rm -rf "$cdrskin_target" fi cp -a "$cdrskin_dir" "$cdrskin_target" # Remove copied vim.swp and binaries rm "$cdrskin_target"/.*.swp rm "$cdrskin_target"/.*.swo rm "$cdrskin_target"/*.o rm "$cdrskin_target"/cdrfifo rm "$cdrskin_target"/cdrskin rm "$cdrskin_target"/cleanup for i in std new make old do if test -e "$cdrskin_target"/cdrskin_"$i" then rm "$cdrskin_target"/cdrskin_"$i" fi done # Remove eventual SVN stuff from cdrskin directory for i in .deps .dirstamp .libs do if test -e "$cdrskin_target"/"$i" then rm -rf "$cdrskin_target"/"$i" fi done # Remove GIFs of cdrskin_eng.html rm "$cdrskin_target"/doener_*.gif "$cdrskin_target"/doener_*.png # Remove automatically generated HTML man page rm "$cdrskin_target"/man_1_cdrskin.html # Remove libcevap rm -rf "$target"/libcevap # Remove all add_ts_changes_to_libburn besides this one for i in "$cdrskin_target"/add_ts_changes_to_libburn* do if test $(basename "$0") = $(basename "$i") then dummy=dummy else rm $i fi done # Remove unwanted SVN stuff (TODO: avoid downloading it) for i in "$target"/.svn "$target"/*/.svn do if test "$i" = "$target"'/*/.svn' then dummy=dummy else if test -e "$i" then rm -rf "$i" fi fi done # Make SVN state tarball for the libburn team tar czf "$cdrskin_tarball_svn" "$target" # Get over dependecy on autotools. Rely only on cc, make et. al. # This is not the same as "make dist" but i can do it without # having to evaluate the quality of said "make dist" # ( cd "$target" ; ./bootstrap ) # Remove unwanted stuff after bootstrap for i in "$target"/autom4te.cache do if echo "$i" | grep '\*' >/dev/null then dummy=dummy else if test -e "$i" then rm -rf "$i" fi fi done # Repair non-portable shell code output of ./bootstrap ( cd "$compile_dir" || exit 1 sed -e 's/^for ac_header in$/test -z 1 \&\& for ac_header in dummy/' \ < ./configure > ./configure-repaired if test "$?" = 0 then echo "$0: Empty 'for ac_header in' found in configure." >&2 fi mv ./configure-repaired ./configure chmod a+rx,go-w,u+w ./configure ) # Pack it up to the new libburn+cdrskin-tarball tar czf "$cdrskin_tarball" "$target" # Produce a static and a dynamic binary, and a HTML man page ( cd "$compile_dir" || exit 1 ./configure make "$compile_cmd" -libburn_svn -O2 -do_strip cp "$compile_result" "../$bintarget_dynamic" if test -n "$compile_static_opts" then "$compile_cmd" $compile_static_opts -libburn_svn -O2 -do_strip cp "$compile_result" "../$bintarget_static" fi # "$compile_cmd" -libburn_svn -O2 -do_diet -do_strip # cp "$compile_result" "../$bintarget_dynamic"_diet ( cd cdrskin ; cc -g -Wall -o unite_html_b_line unite_html_b_line.c ) "$man_to_html_cmd" mv "$man_page_html" .. ) # Remove the build area # Disable this for debugging the merge process rm -rf "$target" # Show the result ./"$bintarget_dynamic" -version ./"$bintarget_static" -version ls -l "$cdrskin_tarball" ls -l "$bintarget_dynamic" ls -l "$bintarget_static" ls -l $(basename "$man_page_html") #!/bin/sh # # Spying on the call to cdrecord. # # Move $(which cdrecord) to $(dirname $(which cdrecord))/real_cdrecord . # Install this sript instead. (Do not forget to revoke this after the test.) # # The report target is set in variable rt. # The default is this file : rt=/tmp/cdrecord_spy_log # To use a bystanding xterm as target i find out the pty address by # executing in that terminal # sleep 12345 # and then running in another terminal # ps -ef | grep 'sleep 12345' # which answers something like # thomas 21303 30518 0 14:02 pts/23 00:00:00 sleep 12345 # thomas 21421 30523 0 14:02 pts/24 00:00:00 grep sleep 12345 # from which i learn that pts/23 is sleeping 12345. Now sleep can be aborted. # # rt=/dev/pts/23 echo '------------------------------------- cdrecord_spy 0.1.0 -------' >>"$rt" date >>"$rt" echo '----------------------------------------------------------------' >>"$rt" echo "$0" >>"$rt" for i in "$@" do echo "$i" >>"$rt" done echo '------------------------------------- cdrecord_spy 0.1.0 - end -' >>"$rt" real_cdrecord "$@" /* cdrfifo.c , Copyright 2006 - 2016 Thomas Schmitt A fd-to-fd or fd-to-memory fifo to be used within cdrskin or independently. By chaining of fifo objects, several fifos can be run simultaneously in fd-to-fd mode. Modes are controlled by parameter flag of Cdrfifo_try_to_work(). Provided under GPL license within cdrskin and under BSD license elsewise. */ /* Compile as standalone tool : cc -g -o cdrfifo -DCdrfifo_standalonE cdrfifo.c */ #include #include #include #include #include #include #include #include #include #include #ifndef Cdrfifo_standalonE /* for burn_os_alloc_buffer() */ #include "../libburn/libburn.h" /* ts B91124: DISABLED, because this spoils multi-track burning by slowing down first track and stalling before the second track begins. Obviously a problem with chained input and waiting for full O_DRIECT suitable read chunks. DO NOT ENABLE before the wait code in this source file is fixed. That long, ./configure option --enable-track-src-odirect must not get into effect in libburn. NO -DLibburn_read_o_direcT either. For extra safety, O_DIRECT has been banned in libburn/sg-linux.c too. # def ine Libburn_has_open_trac_srC 1 */ #endif #include "cdrfifo.h" /* Macro for creation of arrays of objects (or single objects) */ #define TSOB_FELD(typ,anz) (typ *) calloc(anz, sizeof(typ)); #define Cdrfifo_buffer_chunK 2048 /** Number of follow-up fd pairs */ #define Cdrfifo_ffd_maX 100 /* 1= enable , 0= disable status messages to stderr 2= report each */ static int Cdrfifo_debuG= 0; struct CdrfifO { int chunk_size; int source_fd; double in_counter; double fd_in_counter; double fd_in_limit; char *buffer; int buffer_size; int buffer_is_full; int write_idx; int read_idx; int dest_fd; double out_counter; struct timeval start_time; double speed_limit; /* statistics */ double interval_counter; struct timeval interval_start_time; double interval_start_counter; int total_min_fill; int interval_min_fill; double put_counter; double get_counter; double empty_counter; double full_counter; /* eventual ISO-9660 image size obtained from first 64k of input */ double iso_fs_size; char *iso_fs_descr; /* eventually block 16 to 31 of input */ /* (sequential) fd chaining */ /* fds: 0=source, 1=dest */ int follow_up_fds[Cdrfifo_ffd_maX][2]; /* index of first byte in buffer which does not belong to predecessor fd */ int follow_up_eop[Cdrfifo_ffd_maX]; /* if follow_up_eop[i]==buffer_size : read_idx was 0 when this was set */ int follow_up_was_full_buffer[Cdrfifo_ffd_maX]; /* index of first byte in buffer which belongs to [this] fd pair */ int follow_up_sod[Cdrfifo_ffd_maX]; /* values for fd_in_limit */ double follow_up_in_limits[Cdrfifo_ffd_maX]; /* number of defined follow-ups */ int follow_up_fd_counter; /* index of currently active (i.e. reading) follow-up */ int follow_up_fd_idx; /* short read encountered, take subsequent errno 22 with O_DIRECT as EOF */ int o_direct_was_short; /* (simultaneous) peer chaining */ struct CdrfifO *next; struct CdrfifO *prev; /* rank in peer chain */ int chain_idx; }; /** Create a fifo object. @param ff Returns the address of the new object. @param source_fd Filedescriptor opened to a readable data stream. @param dest_fd Filedescriptor opened to a writable data stream. To work with libburn, it needs to be attached to a struct burn_source object. @param chunk_size Size of buffer block for a single transaction (0=default) @param buffer_size Size of fifo buffer @param flag bit0= Debugging verbosity @return 1 on success, <=0 on failure */ int Cdrfifo_new(struct CdrfifO **ff, int source_fd, int dest_fd, int chunk_size, int buffer_size, int flag) { struct CdrfifO *o; struct timezone tz; int i; (*ff)= o= TSOB_FELD(struct CdrfifO,1); if(o==NULL) return(-1); if(chunk_size<=0) chunk_size= Cdrfifo_buffer_chunK; o->chunk_size= chunk_size; if(buffer_size%chunk_size) buffer_size+= chunk_size-(buffer_size%chunk_size); o->source_fd= source_fd; o->in_counter= 0.0; o->fd_in_counter= 0; o->fd_in_limit= -1.0; o->buffer= NULL; o->buffer_is_full= 0; o->buffer_size= buffer_size; o->write_idx= 0; o->read_idx= 0; o->dest_fd= dest_fd; o->out_counter= 0.0; memset(&(o->start_time),0,sizeof(o->start_time)); gettimeofday(&(o->start_time),&tz); o->speed_limit= 0.0; o->interval_counter= 0.0; memset(&(o->interval_start_time),0,sizeof(o->interval_start_time)); gettimeofday(&(o->interval_start_time),&tz); o->interval_start_counter= 0.0; o->total_min_fill= buffer_size; o->interval_min_fill= buffer_size; o->put_counter= 0.0; o->get_counter= 0.0; o->empty_counter= 0.0; o->full_counter= 0.0; o->iso_fs_size= -1.0; o->iso_fs_descr= NULL; for(i= 0; ifollow_up_fds[i][0]= o->follow_up_fds[i][1]= -1; o->follow_up_eop[i]= o->follow_up_sod[i]= -1; o->follow_up_was_full_buffer[i]= 0; o->follow_up_in_limits[i]= -1.0; } o->follow_up_fd_counter= 0; o->follow_up_fd_idx= -1; o->o_direct_was_short= 0; o->next= o->prev= NULL; o->chain_idx= 0; #ifdef Libburn_has_open_trac_srC o->buffer= burn_os_alloc_buffer((size_t) buffer_size, 0); #else o->buffer= TSOB_FELD(char,buffer_size); #endif /* ! Libburn_has_open_trac_srC */ if(o->buffer==NULL) goto failed; return(1); failed:; Cdrfifo_destroy(ff,0); return(-1); } /** Close any output fds */ int Cdrfifo_close(struct CdrfifO *o, int flag) { int i; if(o->dest_fd!=-1) close(o->dest_fd); o->dest_fd= -1; for(i=0; ifollow_up_fd_counter; i++) { if(o->follow_up_fds[i][1]!=-1) close(o->follow_up_fds[i][1]); o->follow_up_fds[i][1]= -1; } return(1); } /** Release from memory a fifo object previously created by Cdrfifo_new(). @param ff The victim (gets returned as NULL, call can stand *ff==NULL)) @param flag Bitfield for control purposes: bit0= do not close destination fd */ int Cdrfifo_destroy(struct CdrfifO **ff, int flag) /* flag bit0= do not close destination fd */ { struct CdrfifO *o; o= *ff; if(o==NULL) return(0); if(o->next!=NULL) o->next->prev= o->prev; if(o->prev!=NULL) o->prev->next= o->next; if(!(flag&1)) Cdrfifo_close(o,0); /* eventual closing of source fds is the job of the calling application */ if(o->iso_fs_descr!=NULL) free((char *) o->iso_fs_descr); if(o->buffer!=NULL) #ifdef Libburn_has_open_trac_srC burn_os_free_buffer(o->buffer, o->buffer_size, 0); #else free((char *) o->buffer); #endif /* Libburn_has_open_trac_srC */ free((char *) o); (*ff)= NULL; return(1); } int Cdrfifo_get_sizes(struct CdrfifO *o, int *chunk_size, int *buffer_size, int flag) { *chunk_size= o->chunk_size; *buffer_size= o->buffer_size; return(1); } /** Set a speed limit for buffer output. @param o The fifo object @param bytes_per_second >0 catch up slowdowns over the whole run time <0 catch up slowdowns only over one interval =0 disable speed limit */ int Cdrfifo_set_speed_limit(struct CdrfifO *o, double bytes_per_second, int flag) { o->speed_limit= bytes_per_second; return(1); } /** Set a fixed size for input in order to cut off any unwanted tail @param o The fifo object @param idx index for fds attached via Cdrfifo_attach_follow_up_fds(), first attached is 0, <0 directs limit to active fd limit (i.e. first track is -1, second track is 0, third is 1, ...) */ int Cdrfifo_set_fd_in_limit(struct CdrfifO *o, double fd_in_limit, int idx, int flag) { if(idx<0) { o->fd_in_limit= fd_in_limit; return(1); } if(idx >= o->follow_up_fd_counter) return(0); o->follow_up_in_limits[idx]= fd_in_limit; return(1); } int Cdrfifo_set_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag) { o->source_fd= source_fd; o->dest_fd= dest_fd; return(1); } int Cdrfifo_get_fds(struct CdrfifO *o, int *source_fd, int *dest_fd, int flag) { *source_fd= o->source_fd; *dest_fd= o->dest_fd; return(1); } /** Attach a further pair of input and output fd which will use the same fifo buffer when its predecessors are exhausted. Reading will start as soon as reading of the predecessor encounters EOF. Writing will start as soon as all pending predecessor data are written. @return index number of new item + 1, <=0 indicates error */ int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag) { if(o->follow_up_fd_counter>=Cdrfifo_ffd_maX) return(0); o->follow_up_fds[o->follow_up_fd_counter][0]= source_fd; o->follow_up_fds[o->follow_up_fd_counter][1]= dest_fd; o->follow_up_fd_counter++; return(o->follow_up_fd_counter); } /** Attach a further fifo which shall be processed simultaneously with this one by Cdrfifo_try_to_work() in fd-to-fd mode. */ int Cdrfifo_attach_peer(struct CdrfifO *o, struct CdrfifO *next, int flag) { int idx; struct CdrfifO *s; for(s= o;s->prev!=NULL;s= s->prev); /* determine start of o-chain */ for(;o->next!=NULL;o= o->next); /* determine end of o-chain */ for(;next->prev!=NULL;next= next->prev); /* determine start of next-chain */ next->prev= o; o->next= next; for(idx= 0;s!=NULL;s= s->next) s->chain_idx= idx++; return(1); } static int Cdrfifo_tell_buffer_space(struct CdrfifO *o, int flag) { if(o->buffer_is_full) return(0); if(o->write_idx>=o->read_idx) return((o->buffer_size - o->write_idx) + o->read_idx); return(o->read_idx - o->write_idx); } /** Obtain buffer state. @param o The buffer object @param fill Returns the number of pending payload bytes in the buffer @param space Returns the number of unused buffer bytes @param flag Unused yet @return -1=error , 0=inactive , 1=reading and writing , 2=reading ended (but still writing) */ int Cdrfifo_get_buffer_state(struct CdrfifO *o,int *fill,int *space,int flag) /* return : -1=error 0=inactive 1=reading and writing 2=reading ended, still writing */ { *space= Cdrfifo_tell_buffer_space(o,0); *fill= o->buffer_size-(*space); if(o->dest_fd==-1) return(0); if(o->source_fd<0) return(2); return(1); } int Cdrfifo_get_counters(struct CdrfifO *o, double *in_counter, double *out_counter, int flag) { *in_counter= o->in_counter; *out_counter= o->out_counter; return(1); } /** reads min_fill and begins measurement interval for next min_fill */ int Cdrfifo_next_interval(struct CdrfifO *o, int *min_fill, int flag) { struct timezone tz; o->interval_counter++; gettimeofday(&(o->interval_start_time),&tz); o->interval_start_counter= o->out_counter; *min_fill= o->interval_min_fill; o->interval_min_fill= o->buffer_size - Cdrfifo_tell_buffer_space(o,0); return(1); } int Cdrfifo_get_min_fill(struct CdrfifO *o, int *total_min_fill, int *interval_min_fill, int flag) { *total_min_fill= o->total_min_fill; *interval_min_fill= o->interval_min_fill; return(1); } int Cdrfifo_get_iso_fs_size(struct CdrfifO *o, double *size_in_bytes, int flag) { *size_in_bytes= o->iso_fs_size; return(o->iso_fs_size>=2048); } int Cdrfifo_adopt_iso_fs_descr(struct CdrfifO *o, char **pt, int flag) { *pt= o->iso_fs_descr; o->iso_fs_descr= NULL; return(*pt!=NULL); } /** Get counters which are mentioned by cdrecord at the end of burning. It still has to be examined wether they mean what i believe they do. */ int Cdrfifo_get_cdr_counters(struct CdrfifO *o, double *put_counter, double *get_counter, double *empty_counter, double *full_counter, int flag) { *put_counter= o->put_counter; *get_counter= o->get_counter; *empty_counter= o->empty_counter; *full_counter= o->full_counter; return(1); } /** Adjust a given buffer fill value so it will not cross an eop boundary. @param o The fifo to exploit. @param buffer_fill The byte count to adjust. @param eop_idx If eop boundary exactly hit: index of follow-up fd pair @param flag Unused yet. @return 0= nothing changed , 1= buffer_fill adjusted */ int Cdrfifo_eop_adjust(struct CdrfifO *o,int *buffer_fill, int *eop_idx, int flag) { int i,eop_is_near= 0,valid_fill; *eop_idx= -1; valid_fill= *buffer_fill; for(i=0; i<=o->follow_up_fd_idx; i++) { if(o->follow_up_eop[i]>=0 && o->follow_up_eop[i]>=o->read_idx) { eop_is_near= 1; if(o->follow_up_eop[i]buffer_size || o->read_idx>0) { valid_fill= o->follow_up_eop[i]-o->read_idx; o->follow_up_was_full_buffer[i]= 0; } else { /* If an input fd change hit exactly the buffer end then follow_up_eop points to buffer_size and not to 0. So it is time to switch output pipes unless this is immediately after follow_up_eop was set and read_idx was 0 (... if this is possible at all while write_idx is 0). follow_up_was_full_buffer was set in this case and gets invalid as soon as a non-0 read_idx is detected (see above). */ if(o->follow_up_was_full_buffer[i]) valid_fill= o->buffer_size; else valid_fill= 0; /* the current pipe is completely served */ } if(valid_fill==0) *eop_idx= i; else if(valid_fillchunk_size) eop_is_near= 2; /* for debugging. to carry a break point */ break; } } if(*buffer_fill>valid_fill) *buffer_fill= valid_fill; return(!!eop_is_near); } /* Perform pre-select activities of Cdrfifo_try_to_work() */ static int Cdrfifo_setup_try(struct CdrfifO *o, struct timeval start_tv, double start_out_counter, int *still_to_wait, int *speed_limiter, int *ready_to_write, fd_set *rds, fd_set *wts, int *max_fd, int flag) /* flag: bit0= enable debug pacifier (same with Cdrfifo_debuG) bit1= do not write, just fill buffer bit2= fd-to-memory mode (else fd-to-fd mode): rather than writing a chunk return it and its size in reply_* bit3= with bit2: do not check destination fd for readiness */ { int buffer_space,buffer_fill,eop_reached= -1,eop_is_near= 0,was_closed; int fd_buffer_fill, eop_reached_counter= 0; struct timeval current_tv; struct timezone tz; double diff_time,diff_counter,limit,min_wait_time; setup_try:; buffer_space= Cdrfifo_tell_buffer_space(o,0); fd_buffer_fill= buffer_fill= o->buffer_size - buffer_space; #ifdef NIX fprintf(stderr,"cdrfifo_debug: o->write_idx=%d o->read_idx=%d o->source_fd=%d\n",o->write_idx,o->read_idx,o->source_fd); if(buffer_fill>10) sleep(1); #endif if(o->follow_up_fd_idx>=0) eop_is_near= Cdrfifo_eop_adjust(o,&fd_buffer_fill,&eop_reached,0); if(fd_buffer_fill<=0 && (o->source_fd==-1 || eop_reached>=0) ) { was_closed= 0; if(o->dest_fd!=-1 && !(flag&4)) close(o->dest_fd); if(o->dest_fd<0) was_closed= 1; else o->dest_fd= -1; if(eop_reached>=0) { /* switch to next output fd */ o->dest_fd= o->follow_up_fds[eop_reached][1]; if(Cdrfifo_debuG) fprintf(stderr,"\ncdrfifo %d: new fifo destination fd : %d\n", o->chain_idx,o->dest_fd); o->read_idx= o->follow_up_sod[eop_reached]; o->follow_up_eop[eop_reached]= -1; eop_is_near= 0; eop_reached= -1; eop_reached_counter= 0; goto setup_try; } else { /* work is really done */ if((!was_closed) && ((flag&1)||Cdrfifo_debuG)) fprintf(stderr, "\ncdrfifo %d: w=%d r=%d | b=%d s=%d | i=%.f o=%.f (done)\n", o->chain_idx,o->write_idx,o->read_idx,buffer_fill,buffer_space, o->in_counter,o->out_counter); return(2); } } else if(eop_reached>=0) eop_reached_counter++; if(o->interval_counter>0) { if(o->total_min_fill>buffer_fill && o->source_fd>=0) o->total_min_fill= buffer_fill; if(o->interval_min_fill>buffer_fill) o->interval_min_fill= buffer_fill; } *speed_limiter= 0; if(o->speed_limit!=0) { gettimeofday(¤t_tv,&tz); if(o->speed_limit>0) { diff_time= ((double) current_tv.tv_sec)-((double) o->start_time.tv_sec)+ (((double) current_tv.tv_usec)-((double) o->start_time.tv_usec))*1e-6; diff_counter= o->out_counter; limit= o->speed_limit; } else if(flag&4) { if(o->interval_start_time.tv_sec==0) o->interval_start_time= start_tv; diff_time= ((double) current_tv.tv_sec) - ((double) o->interval_start_time.tv_sec) + (((double) current_tv.tv_usec) -((double) o->interval_start_time.tv_usec))*1e-6; diff_counter= o->out_counter - o->interval_start_counter; limit= -o->speed_limit; } else { diff_time= ((double) current_tv.tv_sec) - ((double) start_tv.tv_sec) + (((double) current_tv.tv_usec) -((double)start_tv.tv_usec))*1e-6; diff_counter= o->out_counter - start_out_counter; limit= -o->speed_limit; } if(diff_time>0.0) if(diff_counter/diff_time>limit) { min_wait_time= (diff_counter/limit - diff_time)*1.0e6; if(min_wait_time<*still_to_wait) *still_to_wait= min_wait_time; if(*still_to_wait>0) *speed_limiter= 1; } } if(o->source_fd>=0) { if(buffer_space>0) { FD_SET((o->source_fd),rds); if(*max_fdsource_fd) *max_fd= o->source_fd; } else if(o->interval_counter>0) o->full_counter++; } *ready_to_write= 0; if(o->dest_fd>=0 && (!(flag&2)) && !*speed_limiter) { if(fd_buffer_fill>=o->chunk_size || o->source_fd<0 || eop_is_near) { if((flag&(4|8))==(4|8)) { *still_to_wait= 0; *ready_to_write= 1; } else { FD_SET((o->dest_fd),wts); if(*max_fddest_fd) *max_fd= o->dest_fd; } } else if(o->interval_counter>0) o->empty_counter++; } return(1); } /* Perform post-select activities of Cdrfifo_try_to_work() */ static int Cdrfifo_transact(struct CdrfifO *o, fd_set *rds, fd_set *wts, char *reply_buffer, int *reply_count, int flag) /* flag: bit0= enable debug pacifier (same with Cdrfifo_debuG) bit1= do not write, just fill buffer bit2= fd-to-memory mode (else fd-to-fd mode): rather than writing a chunk return it and its size in reply_* bit3= with bit2: do not check destination fd for readiness return: <0 = error , 0 = idle , 1 = did some work */ { double buffer_space; int can_read,can_write= 0,ret,did_work= 0,idx,sod, eop_idx; buffer_space= Cdrfifo_tell_buffer_space(o,0); if(o->dest_fd>=0) if(FD_ISSET((o->dest_fd),wts)) { can_write= o->buffer_size - buffer_space; if(can_write>o->chunk_size) can_write= o->chunk_size; if(o->read_idx+can_write > o->buffer_size) can_write= o->buffer_size - o->read_idx; if(o->follow_up_fd_idx>=0) { Cdrfifo_eop_adjust(o,&can_write,&eop_idx,0); if(can_write<=0) goto after_write; } if(flag&4) { memcpy(reply_buffer,o->buffer+o->read_idx,can_write); *reply_count= ret= can_write; } else { ret= write(o->dest_fd,o->buffer+o->read_idx,can_write); } if(ret==-1) { /* >>> handle broken pipe */; fprintf(stderr,"\ncdrfifo %d: on write: errno=%d , \"%s\"\n", o->chain_idx,errno, errno==0?"-no error code available-":strerror(errno)); if(!(flag&4)) close(o->dest_fd); o->dest_fd= -1; {ret= -1; goto ex;} } did_work= 1; o->get_counter++; o->out_counter+= can_write; o->read_idx+= can_write; if(o->read_idx>=o->buffer_size) o->read_idx= 0; o->buffer_is_full= 0; } after_write:; if(o->source_fd>=0) if(FD_ISSET((o->source_fd),rds)) { can_read= o->buffer_size - o->write_idx; #ifdef Libburn_has_open_trac_srC /* ts A91115 This chunksize must be aligned to filesystem blocksize. */ #define Cdrfifo_o_direct_chunK 32768 if(o->write_idx < o->read_idx && o->write_idx + can_read > o->read_idx) can_read= o->read_idx - o->write_idx; if(o->fd_in_limit>=0.0) if(can_read > o->fd_in_limit - o->fd_in_counter) can_read= o->fd_in_limit - o->fd_in_counter; /* Make sure to read with properly aligned size */ if(can_read > Cdrfifo_o_direct_chunK) can_read= Cdrfifo_o_direct_chunK; else if(can_read < Cdrfifo_o_direct_chunK) can_read= -1; ret= 0; if(can_read>0) { ret= read(o->source_fd,o->buffer+o->write_idx,can_read); if(ret > 0) { if(ret < can_read) { /* Probably EOF. Prepare for errno = 22 in the next read. */ o->o_direct_was_short= 1; } else { o->o_direct_was_short= 0; } } } if(can_read < 0) { /* waiting for a full Cdrfifo_o_direct_chunK to fit */ if(can_write <= 0 && o->dest_fd >= 0) { fd_set rds,wts,exs; struct timeval wt; FD_ZERO(&rds); FD_ZERO(&wts); FD_ZERO(&exs); FD_SET((o->dest_fd),&wts); wt.tv_sec= 0; wt.tv_usec= 10000; select(o->dest_fd + 1,&rds, &wts, &exs, &wt); } } else #else /* Libburn_has_open_trac_srC */ if(can_read>o->chunk_size) can_read= o->chunk_size; if(o->write_idxread_idx && o->write_idx+can_read > o->read_idx) can_read= o->read_idx - o->write_idx; if(o->fd_in_limit>=0.0) if(can_read > o->fd_in_limit - o->fd_in_counter) can_read= o->fd_in_limit - o->fd_in_counter; ret= 0; if(can_read>0) ret= read(o->source_fd,o->buffer+o->write_idx,can_read); #endif /* ! Libburn_has_open_trac_srC */ if(ret==-1) { if(o->o_direct_was_short && errno == 22) goto have_eof; /* >>> handle input error */; fprintf(stderr,"\ncdrfifo %d: on read: errno=%d , \"%s\"\n", o->chain_idx,errno, errno==0?"-no error code available-":strerror(errno)); o->source_fd= -1; } else if(ret==0) { /* eof */ have_eof:; /* activate eventual follow-up source fd */ if(Cdrfifo_debuG || (flag&1)) fprintf(stderr,"\ncdrfifo %d: on read(%d,buffer,%d): eof\n", o->chain_idx,o->source_fd,can_read); if(o->follow_up_fd_idx+1 < o->follow_up_fd_counter) { idx= ++(o->follow_up_fd_idx); o->source_fd= o->follow_up_fds[idx][0]; /* End-Of-Previous */ if(o->write_idx==0) { o->follow_up_eop[idx]= o->buffer_size; /* A70304 : can this happen ? */ o->follow_up_was_full_buffer[idx]= (o->read_idx==0); if(Cdrfifo_debuG || (flag&1)) fprintf(stderr,"\ncdrfifo %d: write_idx 0 on eop: read_idx= %d\n", o->chain_idx,o->read_idx); } else o->follow_up_eop[idx]= o->write_idx; /* Start-Of-Data . Try to start at next full chunk */ sod= o->write_idx; if(o->write_idx%o->chunk_size) sod+= o->chunk_size - (o->write_idx%o->chunk_size); /* but do not catch up to the read pointer */ if((o->write_idx<=o->read_idx && o->read_idx<=sod) || sod==o->read_idx) sod= o->write_idx; if(sod>=o->buffer_size) sod= 0; o->follow_up_sod[idx]= sod; o->write_idx= sod; o->fd_in_counter= 0; o->fd_in_limit= o->follow_up_in_limits[idx]; if(Cdrfifo_debuG || (flag&1)) fprintf(stderr,"\ncdrfifo %d: new fifo source fd : %d\n", o->chain_idx,o->source_fd); } else { o->source_fd= -1; } } else { did_work= 1; o->put_counter++; o->in_counter+= ret; o->fd_in_counter+= ret; o->write_idx+= ret; if(o->write_idx>=o->buffer_size) o->write_idx= 0; if(o->write_idx==o->read_idx) o->buffer_is_full= 1; } } ret= !!did_work; ex:; return(ret); } /** Check for pending data at the fifo's source file descriptor and wether the fifo is ready to take them. Simultaneously check the buffer for existing data and the destination fd for readiness to accept some. If so, a small chunk of data is transferred to and/or from the fifo. This is done for the given fifo object and all members of its next-chain. The check and transactions are repeated until a given timespan has elapsed. libburn applications call this function in the burn loop instead of sleep(). It may also be used instead of read(). Then it returns as soon as an output transaction would be performed. See flag:bit2. @param o The fifo object @param wait_usec The time in microseconds after which the function shall return. @param reply_buffer with bit2: Returns write-ready buffer chunk and must be able to take at least chunk_size bytes @param reply_count with bit2: Returns number of writeable bytes in reply @param flag Bitfield for control purposes: bit0= Enable debug pacifier (same with Cdrfifo_debuG) bit1= Do not write, just fill buffer bit2= fd-to-memory mode (else fd-to-fd mode): Rather than writing a chunk return it and its size. No simultaneous processing of chained fifos. bit3= With bit2: do not check destination fd for readiness @return <0 = error , 0 = idle , 1 = did some work , 2 = all work is done */ int Cdrfifo_try_to_work(struct CdrfifO *o, int wait_usec, char *reply_buffer, int *reply_count, int flag) { struct timeval wt,start_tv,current_tv; struct timezone tz; fd_set rds,wts,exs; int ready,ret,max_fd= -1,buffer_space,dummy,still_active= 0; int did_work= 0,elapsed,still_to_wait,speed_limiter= 0,ready_to_write= 0; double start_out_counter; struct CdrfifO *ff; start_out_counter= o->out_counter; gettimeofday(&start_tv,&tz); still_to_wait= wait_usec; if(flag&4) *reply_count= 0; try_again:; /* is there still a destination open ? */ for(ff= o; ff!=NULL; ff= ff->next) if(ff->dest_fd!=-1) break; if(ff==NULL) return(2); FD_ZERO(&rds); FD_ZERO(&wts); FD_ZERO(&exs); for(ff= o; ff!=NULL; ff= ff->next) { ret= Cdrfifo_setup_try(ff,start_tv,start_out_counter, &still_to_wait,&speed_limiter,&ready_to_write, &rds,&wts,&max_fd,flag&15); if(ret<=0) return(ret); else if(ret==2) { /* This fifo is done */; } else still_active= 1; if(flag&2) break; } if(!still_active) return(2); if(still_to_wait>0 || max_fd>=0) { wt.tv_sec= still_to_wait/1000000; wt.tv_usec= still_to_wait%1000000; ready= select(max_fd+1,&rds,&wts,&exs,&wt); } else ready= 0; if(ready<=0) { if(!ready_to_write) goto check_wether_done; FD_ZERO(&rds); } if(ready_to_write) FD_SET((o->dest_fd),&wts); for(ff= o; ff!=NULL; ff= ff->next) { ret= Cdrfifo_transact(ff,&rds,&wts,reply_buffer,reply_count,flag&15); if(ret<0) goto ex; if(ret>0) did_work= 1; if(flag&2) break; } check_wether_done:; if((flag&4) && *reply_count>0) {ret= 1; goto ex;} gettimeofday(¤t_tv,&tz); elapsed= (current_tv.tv_sec-start_tv.tv_sec)*1000000 + (((int) current_tv.tv_usec) - ((int) start_tv.tv_usec)); still_to_wait= wait_usec-elapsed; if(still_to_wait>0) goto try_again; ret= !!did_work; ex:; if(flag&4) { gettimeofday(¤t_tv,&tz); elapsed= (current_tv.tv_sec - o->interval_start_time.tv_sec)*1000000 + (((int) current_tv.tv_usec) - ((int) o->interval_start_time.tv_usec)); } else elapsed= wait_usec; if(elapsed>=wait_usec) { if((flag&1)||Cdrfifo_debuG>=2) { fprintf(stderr,"\n"); for(ff= o; ff!=NULL; ff= ff->next) { buffer_space= Cdrfifo_tell_buffer_space(ff,0); fprintf(stderr, "cdrfifo %d: w=%d r=%d | b=%d s=%d | i=%.f o=%.f\n", ff->chain_idx,ff->write_idx,ff->read_idx, ff->buffer_size-buffer_space,buffer_space, ff->in_counter,ff->out_counter); } } if(flag&4) Cdrfifo_next_interval(o,&dummy,0); } return(ret); } /** Fill the fifo as far as possible without writing to destination fd */ int Cdrfifo_fill(struct CdrfifO *o, int size, int flag) { int ret,fill= 0,space,state; while(1) { state= Cdrfifo_get_buffer_state(o,&fill,&space,0); if(state==-1) { /* >>> handle error */; return(0); } else if(state!=1) break; if(space<=0) break; if(size>=0 && fill>=size) break; ret= Cdrfifo_try_to_work(o,100000,NULL,NULL,2); if(ret<0) { /* >>> handle error */; return(0); } if(ret==2) break; } #ifndef Cdrfifo_standalonE if(fill>=32*2048) { int Scan_for_iso_size(unsigned char data[2048], double *size_in_bytes, int flag); int bs= 16*2048; double size; /* memorize blocks 16 to 31 */ if(o->iso_fs_descr!=NULL) free((char *) o->iso_fs_descr); o->iso_fs_descr= TSOB_FELD(char,bs); if(o->iso_fs_descr==NULL) return(-1); memcpy(o->iso_fs_descr,o->buffer+bs,bs); /* try to obtain ISO-9660 file system size from block 16 */ ret= Scan_for_iso_size((unsigned char *) (o->buffer+bs), &size, 0); if(ret>0) o->iso_fs_size= size; } #endif o->total_min_fill= fill; o->interval_min_fill= fill; return(1); } int Cdrfifo_close_all(struct CdrfifO *o, int flag) { struct CdrfifO *ff; if(o==NULL) return(0); for(ff= o; ff->prev!=NULL; ff= ff->prev); for(; ff!=NULL; ff= ff->next) Cdrfifo_close(ff,0); return(1); } #ifdef Cdrfifo_standalonE /* ---------------------------------------------------------------------- */ /** Application example. See also cdrskin.c */ double Scanf_io_size(char *text, int flag) /* bit0= default value -1 rather than 0 */ { int c; double ret= 0.0; if(flag&1) ret= -1.0; if(text[0]==0) return(ret); sscanf(text,"%lf",&ret); c= text[strlen(text)-1]; if(c=='k' || c=='K') ret*= 1024.0; if(c=='m' || c=='M') ret*= 1024.0*1024.0; if(c=='g' || c=='G') ret*= 1024.0*1024.0*1024.0; if(c=='t' || c=='T') ret*= 1024.0*1024.0*1024.0*1024.0; if(c=='p' || c=='P') ret*= 1024.0*1024.0*1024.0*1024.0*1024.0; if(c=='e' || c=='E') ret*= 1024.0*1024.0*1024.0*1024.0*1024.0*1024.0; if(c=='s' || c=='S') ret*= 2048.0; return(ret); } /* This is a hardcoded test mock-up for two simultaneous fifos of which the one runs with block size 2048 and feeds the other which runs with 2352. Both fifos have the same number of follow_up pipes (tracks) which shall be connected 1-to-1. */ int Test_mixed_bs(char **paths, int path_count, int fs_size, double speed_limit, double interval, int flag) /* bit0= debugging verbousity */ { int fd_in[100],fd_out[100],ret,pipe_fds[100][2]; int i,iv,stall_counter= 0,cycle_counter= 0.0; char target_path[80]; double in_counter, out_counter, prev_in= -1.0, prev_out= -1.0; struct CdrfifO *ff_in= NULL, *ff_out= NULL; if(path_count<1) return(2); Cdrfifo_new(&ff_in,fd_in[0],fd_out[0],2048,fs_size,0); for(i= 0; ichain_idx,ret); if(ret<0) return(-7); break; } cycle_counter++; Cdrfifo_get_counters(ff_in, &in_counter, &out_counter, 0); if(prev_in == in_counter && prev_out == out_counter) stall_counter++; prev_in= in_counter; prev_out= out_counter; } return(1); } /* This is a hardcoded test mock-up for two simultaneous fifos of which the first one simulates the cdrskin fifo feeding libburn and the second one simulates libburn and the burner at given speed. Both have two fd pairs (i.e. tracks). The tracks are read from /u/test/cdrskin/in_[12] and written to /u/test/cdrskin/out_[12]. */ int Test_multi(int fs_size, double speed_limit, double interval, int flag) /* bit0= debugging verbousity */ { int fd_in[4],fd_out[4],ret,pipe_fds[4][2]; int i,iv; struct CdrfifO *ff1= NULL,*ff2= NULL; /* open four pairs of fds */ fd_in[0]= open("/u/test/cdrskin/in_1",O_RDONLY); fd_in[1]= open("/u/test/cdrskin/in_2",O_RDONLY); fd_out[2]= open("/u/test/cdrskin/out_1", O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); fd_out[3]= open("/u/test/cdrskin/out_2", O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); if(pipe(pipe_fds[0])==-1) return(-3); if(pipe(pipe_fds[1])==-1) return(-3); fd_out[0]= pipe_fds[0][1]; fd_out[1]= pipe_fds[1][1]; fd_in[2]= pipe_fds[0][0]; fd_in[3]= pipe_fds[1][0]; for(i=0;i<4;i++) { if(fd_in[i]==-1) return(-1); if(fd_out[i]==-1) return(-2); } /* Create two fifos with two sequential fd pairs each and chain them for simultaneous usage. */ Cdrfifo_new(&ff1,fd_in[0],fd_out[0],2048,fs_size,0); Cdrfifo_new(&ff2,fd_in[2],fd_out[2],2048,2*1024*1024,0); /*burner cache 2 MB*/ if(ff1==NULL || ff2==NULL) return(-3); Cdrfifo_set_speed_limit(ff2,speed_limit,0); ret= Cdrfifo_attach_follow_up_fds(ff1,fd_in[1],fd_out[1],0); if(ret<=0) return(-4); ret= Cdrfifo_attach_follow_up_fds(ff2,fd_in[3],fd_out[3],0); if(ret<=0) return(-4); Cdrfifo_attach_peer(ff1,ff2,0); /* Let the fifos work */ iv= interval*1e6; while(1) { ret= Cdrfifo_try_to_work(ff1,iv,NULL,NULL,flag&1); if(ret<0 || ret==2) { /* <0 = error , 2 = work is done */ fprintf(stderr,"\ncdrfifo %d: fifo ended work with ret=%d\n", ff1->chain_idx,ret); if(ret<0) return(-7); break; } } return(1); } int main(int argc, char **argv) { int i,ret,exit_value= 0,verbous= 1,fill_buffer= 0,min_fill,fifo_percent,fd; double fs_value= 4.0*1024.0*1024.0,bs_value= 2048,in_counter,out_counter; double interval= 1.0,speed_limit= 0.0; char output_file[4096]; struct CdrfifO *ff= NULL; strcpy(output_file,"-"); fd= 1; for(i= 1; i1000.0) interval= 1; } else if(strncmp(argv[i],"of=",3)==0) { if(strcmp(argv[i]+3,"-")==0 || argv[i][3]==0) continue; fd= open(argv[i]+3,O_WRONLY|O_CREAT); if(fd<0) { fprintf(stderr,"cdrfifo: FATAL : cannot open output file '%s'\n", argv[i]+3); fprintf(stderr,"cdrfifo: errno=%d , \"%s\"\n", errno,errno==0?"-no error code available-":strerror(errno)); {exit_value= 4; goto ex;} } } else if(strncmp(argv[i],"sl=",3)==0) { speed_limit= Scanf_io_size(argv[i]+3,0); } else if(strncmp(argv[i],"vb=",3)==0) { sscanf(argv[i]+3,"%d",&verbous); } else if(strcmp(argv[i],"-mixed_bs_test")==0) { ret= Test_mixed_bs(argv+i+1,argc-i-1, (int) fs_value,speed_limit,interval,(verbous>=2)); fprintf(stderr,"Test_mixed_bs(): ret= %d\n",ret); exit(ret<0); } else if(strcmp(argv[i],"-multi_test")==0) { if(speed_limit==0.0) speed_limit= 10*150*1024; ret= Test_multi((int) fs_value,speed_limit,interval,(verbous>=2)); fprintf(stderr,"Test_multi(): ret= %d\n",ret); exit(ret<0); } else { fprintf(stderr,"cdrfifo 0.3 : stdin-to-stdout fifo buffer.\n"); fprintf(stderr,"usage : %s [bs=block_size] [fl=fillfirst]\n",argv[0]); fprintf(stderr," [fs=fifo_size] [iv=interval] [of=output_file]\n"); fprintf(stderr," [sl=bytes_per_second_limit] [vb=verbosity]\n"); fprintf(stderr,"fl=1 reads full buffer before writing starts.\n"); fprintf(stderr,"sl>0 allows catch up for whole run time.\n"); fprintf(stderr,"sl<0 allows catch up for single interval.\n"); fprintf(stderr,"vb=0 is silent, vb=2 is debug.\n"); fprintf(stderr,"example: cdrfifo bs=8k fl=1 fs=32m iv=0.1 sl=-5400k\n"); if(strcmp(argv[i],"-help")!=0 && strcmp(argv[i],"--help")!=0) { fprintf(stderr,"\ncdrfifo: FATAL : option not recognized: '%s'\n", argv[i]); exit_value= 1; } goto ex; } } if(verbous>=1) { fprintf(stderr, "cdrfifo: bs=%.lf fl=%d fs=%.lf iv=%lf of='%s' sl=%.lf vb=%d\n", bs_value,fill_buffer,fs_value,interval,output_file,speed_limit, verbous); } ret= Cdrfifo_new(&ff,0,fd,(int) bs_value,(int) fs_value,0); if(ret<=0) { fprintf(stderr, "cdrfifo: FATAL : creation of fifo object with %.lf bytes failed\n", fs_value); {exit_value= 3; goto ex;} } if(speed_limit!=0.0) Cdrfifo_set_speed_limit(ff,speed_limit,0); if(fill_buffer) { ret= Cdrfifo_fill(ff,0,0); if(ret<=0) { fprintf(stderr, "cdrfifo: FATAL : initial filling of fifo buffer failed\n"); {exit_value= 4; goto ex;} } } while(1) { ret= Cdrfifo_try_to_work(ff,(int) (interval*1000000.0), NULL,NULL,(verbous>=2)); if(ret<0) { fprintf(stderr,"\ncdrfifo: FATAL : fifo aborted. errno=%d , \"%s\"\n", errno,errno==0?"-no error code available-":strerror(errno)); {exit_value= 4; goto ex;} } else if(ret==2) { if(verbous>=1) { double put_counter,get_counter,empty_counter,full_counter; int total_min_fill; Cdrfifo_get_counters(ff,&in_counter,&out_counter,0); fprintf(stderr,"\ncdrfifo: done : %.lf bytes in , %.lf bytes out\n", in_counter,out_counter); Cdrfifo_get_min_fill(ff,&total_min_fill,&min_fill,0); fifo_percent= 100.0*((double) total_min_fill)/fs_value; if(fifo_percent==0 && total_min_fill>0) fifo_percent= 1; Cdrfifo_get_cdr_counters(ff,&put_counter,&get_counter, &empty_counter,&full_counter,0); fprintf(stderr,"cdrfifo: fifo had %.lf puts and %.lf gets.\n", put_counter,get_counter); fprintf(stderr, "cdrfifo: fifo was %.lf times empty and %.lf times full, min fill was %d%%.\n", empty_counter,full_counter,fifo_percent); } break; } Cdrfifo_next_interval(ff,&min_fill,0); } ex:; Cdrfifo_destroy(&ff,0); exit(exit_value); } #endif /* Cdrfifo_standalonE */ /* cdrfifo.c , Copyright 2006 - 2016 Thomas Schmitt A fd-to-fd or fd-to-memory fifo to be used within cdrskin or independently. By chaining of fifo objects, several fifos can be run simultaneously in fd-to-fd mode. Modes are controlled by parameter flag of Cdrfifo_try_to_work(). Provided under GPL license within cdrskin and under BSD license elsewise. */ #ifndef Cdrfifo_headerfile_includeD #define Cdrfifo_headerfile_includeD /** The fifo buffer which will smoothen the data stream from data provider to data consumer. Although this is not a mandatory lifesaver for modern burners any more, a fifo can speed up burning of data which is delivered with varying bandwidths (e.g. compressed archives created on the fly or mkisofs running at its speed limit.). This structure is opaque to applications and may only be used via the Cdrfifo*() methods described in cdrfifo.h . */ struct CdrfifO; /** Create a fifo object. @param ff Returns the address of the new object. @param source_fd Filedescriptor opened to a readable data stream. @param dest_fd Filedescriptor opened to a writable data stream. To work with libburn, it needs to be attached to a struct burn_source object. @param chunk_size Size of buffer block for a single transaction (0=default) @param buffer_size Size of fifo buffer @param flag bit0= Debugging verbosity @return 1 on success, <=0 on failure */ int Cdrfifo_new(struct CdrfifO **ff, int source_fd, int dest_fd, int chunk_size, int buffer_size, int flag); /** Release from memory a fifo object previously created by Cdrfifo_new(). @param ff The victim (gets returned as NULL, call can stand *ff==NULL) @param flag Bitfield for control purposes: bit0= do not close destination fd */ int Cdrfifo_destroy(struct CdrfifO **ff, int flag); /** Close any output fds */ int Cdrfifo_close(struct CdrfifO *o, int flag); /** Close any output fds of o and its chain peers */ int Cdrfifo_close_all(struct CdrfifO *o, int flag); int Cdrfifo_get_sizes(struct CdrfifO *o, int *chunk_size, int *buffer_size, int flag); /** Set a speed limit for buffer output. @param o The fifo object @param bytes_per_second >0 catch up slowdowns over the whole run time <0 catch up slowdowns only over one interval =0 disable speed limit */ int Cdrfifo_set_speed_limit(struct CdrfifO *o, double bytes_per_second, int flag); /** Set a fixed size for input in order to cut off any unwanted tail @param o The fifo object @param idx index for fds attached via Cdrfifo_attach_follow_up_fds(), first attached is 0, <0 directs limit to active fd limit (i.e. first track is -1, second track is 0, third is 1, ...) */ int Cdrfifo_set_fd_in_limit(struct CdrfifO *o, double fd_in_limit, int idx, int flag); int Cdrfifo_set_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag); int Cdrfifo_get_fds(struct CdrfifO *o, int *source_fd, int *dest_fd, int flag); /** Attach a further pair of input and output fd which will use the same fifo buffer when its predecessors are exhausted. Reading will start as soon as reading of the predecessor encounters EOF. Writing will start as soon as all pending predecessor data are written. @return index number of new item + 1, <=0 indicates error */ int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag); /** Attach a further fifo which shall be processed simultaneously with this one by Cdrfifo_try_to_work() in fd-to-fd mode. */ int Cdrfifo_attach_peer(struct CdrfifO *o, struct CdrfifO *next, int flag); /** Obtain buffer state. @param o The buffer object @param fill Returns the number of pending payload bytes in the buffer @param space Returns the number of unused buffer bytes @param flag unused yet @return -1=error , 0=inactive , 1=reading and writing , 2=reading ended (but still writing) */ int Cdrfifo_get_buffer_state(struct CdrfifO *o,int *fill,int *space,int flag); int Cdrfifo_get_counters(struct CdrfifO *o, double *in_counter, double *out_counter, int flag); /** reads min_fill and begins measurement interval for next min_fill */ int Cdrfifo_next_interval(struct CdrfifO *o, int *min_fill, int flag); int Cdrfifo_get_min_fill(struct CdrfifO *o, int *total_min_fill, int *interval_min_fill, int flag); int Cdrfifo_get_cdr_counters(struct CdrfifO *o, double *put_counter, double *get_counter, double *empty_counter, double *full_counter, int flag); /** Inquire the eventually detected size of an eventual ISO-9660 file system @return 0=no ISO size detected, 1=size_in_bytes is valid */ int Cdrfifo_get_iso_fs_size(struct CdrfifO *o, double *size_in_bytes,int flag); /** Take over the eventually memorized blocks 16 to 31 of input (2 kB each). The fifo forgets the blocks by this call. I.e. a second one will return 0. After this call it is the responsibility of the caller to dispose the retrieved memory via call free(). @param pt Will be filled either with NULL or a pointer to 32 kB of data @return 0=nothing is buffered, 1=pt points to valid freeable data */ int Cdrfifo_adopt_iso_fs_descr(struct CdrfifO *o, char **pt, int flag); /** Check for pending data at the fifo's source file descriptor and wether the fifo is ready to take them. Simultaneously check the buffer for existing data and the destination fd for readiness to accept some. If so, a small chunk of data is transferred to and/or from the fifo. This is done for the given fifo object and all members of its next-chain. The check and transactions are repeated until a given timespan has elapsed. libburn applications call this function in the burn loop instead of sleep(). It may also be used instead of read(). Then it returns as soon as an output transaction would be performed. See flag:bit2. @param o The fifo object @param wait_usec The time in microseconds after which the function shall return. @param reply_buffer with bit2: Returns write-ready buffer chunk and must be able to take at least chunk_size bytes @param reply_count with bit2: Returns number of writeable bytes in reply_pt @param flag Bitfield for control purposes: bit0= Enable debug pacifier (same with Cdrfifo_debuG) bit1= Do not write, just fill buffer bit2= fd-to-memory mode (else fd-to-fd mode): Rather than writing a chunk return it and its size. No simultaneous processing of chained fifos. bit3= With bit2: do not check destination fd for readiness @return <0 = error , 0 = idle , 1 = did some work , 2 = all work is done */ int Cdrfifo_try_to_work(struct CdrfifO *o, int wait_usec, char *reply_buffer, int *reply_count, int flag); /** Fill the fifo as far as possible without writing to destination fd. @param size if >=0 : end filling after the given number of bytes @return 1 on success, <=0 on failure */ int Cdrfifo_fill(struct CdrfifO *o, int size, int flag); #endif /* Cdrfifo_headerfile_includeD */ .\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH CDRSKIN 1 "Version 1.5.7, Jun 07, 2023" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME cdrskin \- burns preformatted data to CD, DVD, and BD via libburn. .SH SYNOPSIS .B cdrskin .RI [ options | track_source_addresses ] .br .SH DESCRIPTION .PP .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invode bold face and italics, .\" respectively. .PP \fBcdrskin\fP is a program that provides some of cdrecord's options in a compatible way for CD media. With DVD and BD it has its own ways. You do not need to be superuser for its daily usage. .SS .B Overview of features: .br Blanking of CD-RW and DVD-RW. .br Formatting of DVD-RW, DVD+RW, DVD-RAM, BD. .br Burning of data tracks or audio tracks with CD-TEXT to CD, .br either in versatile Track at Once mode (TAO) .br or in Session at Once mode for seamless tracks. .br Multi session on CD (follow-up sessions in TAO only) .br or on DVD-R[W] (in Incremental mode) or DVD+R[/DL] or BD-R. .br Single session Disk-at-once on DVD-RW, DVD-R, DVD-R DL. .br Single session or emulated ISO-9660 multi-session .br on overwriteable DVD+RW, DVD-RW, DVD-RAM, BD-RE .br or on data file or block device. .br Extraction of audio tracks and CD-TEXT to hard disk files. .br Bus scan, burnfree, speed options, retrieving media info, padding, fifo. .br See section EXAMPLES at the end of this text. .SS .B General information paragraphs: .br Track recording model .br Write mode selection .br Recordable CD Media .br Sequentially Recordable DVD or BD Media .br Overwriteable DVD or BD Media .br Drive preparation and addressing .br Emulated drives .SS .B Track recording model: .br The input-output entities which get processed are called tracks. A \fBtrack\fP stores a stream of bytes. .br More than one track can be burned by a single run of cdrskin. In the terms of the MMC standard all tracks written by the same run constitute a \fBsession\fP. .br Normally, each track is initiated by one track source address argument, which may either be "-" for standard input or the address of a readable file. Alternatively, option cuefile= may be used to read a session description from a text file and to read the session content from a single data file. .br If no write mode is given explicitly then one will be chosen which matches the peculiarities of track sources and the state of the output media. .PP Some media types can be kept appendable so that further tracks can be written to them in subsequent runs of cdrskin (see option -multi). Info about the addresses of burned tracks is kept in a table of content (TOC) on media and can be retrieved via cdrskin option -toc. This information is also used by the operating systems' CD-ROM read drivers. .PP In general there are two types of tracks: data and audio. They differ in sector size, throughput and readability via the systems' CD-ROM drivers and by music CD players. With DVD and BD there is only type data. .br If not explicitly option -audio is given, then any track is burned as type data, unless the track source is a file with suffix ".wav" or ".au" and has a header part which identifies it as MS-WAVE or SUN Audio with suitable parameters. Such files are burned as audio tracks by default. .PP While audio tracks just contain a given time span of acoustic vibrations, data tracks may have an arbitrary meaning. Nevertheless, ISO-9660 filesystems are established as a format which can represent a tree of directories and files on all major operating systems. Such filesystem images can be produced by programs mkisofs or genisoimage or xorriso. They can also be extended by follow-up tracks if prepared properly. See the man pages of said programs. cdrskin is able to fulfill the needs about their option -C. .br Another type of data track content are archive formats which originally have been developed for magnetic tapes. Only formats which mark a detectable end-of-archive in their data are suitable, though. Well tested are the archivers afio and star. Not suitable seems GNU tar. .SS .B Write mode selection: .br In general there are two approaches for writing media: .br A permissive mode selected by option .B -tao which needs no predicted track size and can use multi-session capabilities if offered by drive and medium. .br A more restrictive mode .B -sao (alias -dao) which usually demands a predictable track size and is not necessarily capable of multi-session. It can be used to write CD-TEXT and it is the only one that works with option cuefile=. .br If none of the options -dao, -tao or -sao is given then the program will try to choose a write mode which matches the defined recording job, the capabilities of the drive and the state of the present media. .br So the mentioning of write modes in the following paragraphs and in the examples is not so much a demand that the user shall choose one explicitly, but rather an illustration of what to expect with particular media types. .SS .B Recordable CD Media: .br CD-R can be initially written only once and eventually extended until they get closed (or are spoiled because they are overly full). After that they are read-only. Closing is done automatically unless option .B -multi is given which keeps the media appendable. .br Write mode -tao is able to use track sources of unpredictable length (like stdin) and to write further sessions to appendable media. -sao produces audio sessions with seamless tracks but needs predicted track sizes and cannot append sessions to media. .br CD-RW media can be blanked to make them re-usable for another round of overwriting. Usually .B blank=fast is the appropriate option. Blanking damages the previous content but does not make it completely unreadable. It is no effective privacy precaution. Multiple cycles of blanking and overwriting with random numbers might be needed. .SS .B Sequentially Recordable DVD or BD Media: .br Currently DVD-RW, DVD-R[DL], DVD+R[DL], and BD-R can be used for the Sequential recording model. It resembles the model of CD media. Only DVD-RW can be blanked and re-used from scratch. .br DVD-RW are sequential media if they are in state "Sequential Recording". The media must be either blank or appendable. Newly purchased DVD-RW and DVD-R media are in this state. Used DVD-RW get into blank sequential state by option .B blank=deformat_sequential . .br With DVD-R[W] two write modes may be available: .br Mode DAO has many restrictions. It does not work with appendable media, cannot do -multi and writes only a single track. The size of the track needs to be known in advance. So either its source has to be a disk file of recognizable size or the size has to be announced explicitly by options .B tsize= or .B tao_to_sao_tsize= . .br DAO is the only mode for DVD-R media which do not offer feature 21h Incremental Streaming (e.g. DVD-R DL). DAO may also be selected explicitly by option .B -sao . Program growisofs uses DAO on sequential DVD-R[W] media for maximum DVD-ROM/-Video compatibility. .br The other mode, Incremental Streaming, is the default write mode if it is available and if the restrictions of DAO would prevent the job. Incremental Streaming may be selected explicitly by option .B -tao as it resembles much CD TAO by accepting track sources of unpredicted length and being able to keep media appendable by option .B -multi . It does not work with DVD-R DL and minimally blanked DVD-RW. The only restriction towards CD-R[W] is the lack of support for -audio tracks. Multiple tracks per session are permissible. .br The write modes for DVD+R[/DL] and BD-R resemble those with DVD-R except that each track gets wrapped in an own session. There is no -dummy writing with DVD+R[/DL] or BD-R. .br Quite deliberately write mode -sao insists in the tradition of a predicted track size and blank media, whereas -tao writes the tracks open ended and can be applied to appendable media. .br BD-R may be formatted before first use to enable the Defect Management which might catch and repair some bad spots at the expense of slow speed even with flawless media. .br .B Note: Option -multi might make DVD media unreadable in some DVD-ROM drives. Best reader compatibility is achieved without it (i.e. by single session media). .SS .B Overwriteable DVD or BD Media: .br Currently types DVD+RW, DVD-RW, DVD-RAM and BD-RE can be overwritten via cdrskin. .br Option -audio is not allowed. Only one track is allowed. Option -multi cannot mark a recognizable end of overwriteable media. Therefore -multi is banned unless ISO-9660 images shall be expandable by help of option .B --grow_overwriteable_iso . Without this option or without an ISO-9660 filesystem image present on media, -toc does not return information about the media content and media get treated as blank regardless whether they hold data or not. .br Currently there is no difference between -sao and -tao. If ever, then -tao will be the mode which preserves the current behavior. .PP DVD+RW and DVD-RAM media need no special initial formatting. They offer a single continuous data area for blockwise random access. BD-RE need explicit formatting before use. See .B blank=as_needed or blank=format_defectmgt . .br DVD-RW are sold in state "Sequential Recording". To become suitable for the Overwriteable DVD recording model they need to get formatted to state "Restricted Overwrite". Then they behave much like DVD+RW. This formatting can be done by option .B blank=format_overwrite . .br Several programs like dvd+rw-format, cdrecord, wodim, or cdrskin can bring a DVD-RW out of overwriteable state so that it has to be formatted again. If in doubt, just give it a try. .SS .B Drive preparation and addressing: .br The drives, CD, DVD, or BD burners, are accessed via addresses which are specific to libburn and the operating system. Those addresses get listed by a run of \fBcdrskin --devices\fP or \fBcdrskin --device_links\fP. .br On Linux, they are device files which traditionally do not offer w-permissions for normal users. Because libburn needs rw-permission, it might be only the .B superuser who is able to get this list without further precautions. .br It is consensus that \fBchmod a+rw /dev/sr0\fP or \fBchmod a+rw /dev/hdc\fP is less security sensitive than chmod u+s,a+x /usr/bin/cdrskin. The risk for the drive is somewhat higher but the overall system is much less at stake. Consider to restrict rw-access to a single group which bundles the users who are allowed to use the burner drive (like group "floppy"). .br For drive permission examples on Linux, FreeBSD, and Solaris, see cdrskin/README. .br .PP If you only got one CD capable drive then you may leave out cdrskin option \fBdev=\fP. Else you should use this option to address the drive you want. .br cdrskin option dev= not only accepts the listed addresses but also traditional cdrecord SCSI addresses which consist of three numbers: Bus,Target,Lun. On Linux there is also a related address family "ATA" which accesses IDE drives not under control of Linux SCSI drivers: ATA:Bus,Target,Lun. .br See option -scanbus for getting a list of cdrecord style addresses. .br Further are accepted: links to libburn-suitable device files, device files which have the same major and minor device number, and device files which have the same SCSI address parameters (e.g. /dev/sg0). .br .SS .B Emulated drives: .br Option .B --allow_emulated_drives enables addressing of pseudo-drives which get emulated on top of filesystem objects. Regular data files and block devices result in pseudo-drives which behave much like DVD-RAM. If the given address does not exist yet but its directory exists, then it gets created as regular file. Other file types like character devices or pipes result in pseudo-drives which behave much like blank DVD-R. The target file address is given after prefix "stdio:". .br E.g.: dev=stdio:/tmp/my_pseudo_drive .br Addresses of the form "stdio:/dev/fd/" are treated special. The number is read literally and used as open file descriptor. With dev="stdio:/dev/fd/1" the normal standard output of the program is redirected to stderr and the stream data of a burn run will appear on stdout. .br Not good for terminals ! Redirect it. .br Pseudo-drives support -dummy. Their reply with --tell_media_space can be utopic. -dummy burn runs touch the file but do not modify its data content. .br Note: --allow_emulated_drives is restricted to stdio:/dev/null if cdrskin is run by the .B superuser or if it has changed user identity via the .B setuid bit of its access permissions. The ban for the superuser can be lifted by a skillfully created file. See section FILES below. .br .SH OPTIONS .TP .BI \-\-help Show non-cdrecord compatible options. .TP .BI \-help Show cdrecord compatible options. .br Note that some of the help texts are quite wrong - for cdrecord as well as for cdrskin (e.g. -format, blank=, -load). They are, nevertheless, traditional indicators for the availability of the listed options. Some frontend programs make decisions after reading them. .TP .BI \-version Print cdrskin id line, compatibility lure line, libburn version, cdrskin version, version timestamp, build timestamp (if available), and then exit. .PP Alphabetical list of options which are intended to be compatible with original cdrecord by Joerg Schilling: .TP .BI \-atip Retrieve some info about media state. With CD-RW print "Is erasable". With DVD media print "book type:" and a media type text. With BD media print "Mounted Media:" and media type text. .TP .BI \-audio Announces that the subsequent tracks are to be burned as audio. The source is supposed to be uncompressed headerless PCM, 44100 Hz, 16 bit, stereo. For little-endian byte order (which is usual on PCs) use option -swab. Unless marked explicitly by option -data, input files with suffix ".wav" are examined whether they have a header in MS-WAVE format confirming those parameters and eventually raw audio data get extracted and burned as audio track. Same is done for suffix ".au" and SUN Audio. .br Option -audio may be used only with CD media and not with DVD or BD. .TP .BI blank= type Blank a CD-RW, DVD-RW, or format a DVD-RW, DVD+RW, DVD-RAM, BD. This is combinable with burning in the same run of cdrskin. The type given with blank= selects the particular behavior: .RS .TP as_needed Try to make the media ready for writing from scratch. If it needs formatting, then format it. If it is not blank, then try to apply blank=fast. It is a reason to abort if the media cannot assume thoroughly writeable state, e.g. if it is non-blank write-once. .br This leaves unformatted DVD-RW in unformatted blank state. To format DVD-RW use blank=format_overwriteable. Blank unformatted BD-R stay unformatted. .br (Note: blank=as_needed is not an original cdrecord option.) .TP The following blank types are specific to particular media familes. Use them if special features are desired. .TP all Blank an entire CD-RW or an unformatted DVD-RW. (See also --prodvd_cli_compatible, --grow_overwriteable_iso) .TP fast Minimally blank an entire CD-RW or blank an unformatted DVD-RW. (See also --prodvd_cli_compatible, --grow_overwriteable_iso) .TP deformat_sequential Like blank=all but with the additional ability to blank overwriteable DVD-RW. This will destroy their formatting and make them sequentially recordable. Another peculiarity is the ability to blank media which appear already blank. This is similar to option -force but does not try to blank media other than recognizable CD-RW and DVD-RW. .br (Note: blank=deformat_* are not original cdrecord options.) .TP deformat_sequential_quickest Like blank=deformat_sequential but blanking DVD-RW only minimally. This is faster than full blanking but may yield media incapable of Incremental Streaming (-tao). .TP format_if_needed Format a media if it is not formatted yet, and if cdrskin supports formatting for the media type, and if formatting will not happen automatically during write. This currently applies to unformatted DVD-RW, DVD-RAM, BD-RE, and blank unformatted BD-R. Eventually the appropriate default formatting is chosen. If other media or states are encountered then nothing happens. .br The following formatting types are more specialized to particular media families. .TP format_overwrite Format a DVD-RW to "Restricted Overwrite". The user should bring some patience. .br (Note: blank=format_* are not original cdrecord options.) .TP format_overwrite_quickest Like format_overwrite without creating a 128 MiB trailblazer session. Leads to "intermediate" state which only supports sequential write beginning from address 0. The "intermediate" state ends after the first session of writing data. .TP format_overwrite_full For DVD-RW this is like format_overwrite but claims full media size rather than just 128 MiB. Most traditional formatting is attempted. No data get written. Much patience is required. .br This option treats already formatted media even if not option -force is given. .br For DVD+RW this is the only supported explicit formatting type. It provides complete "de-icing" so no reader slips on unwritten data areas. .TP format_defectmgt Format DVD-RAM or BD to reserve the default amount of spare blocks for defect management. .br The following format_defectmgt_* enable the user to submit wishes which nevertheless have to match one of the available formats. These formats are offered by the drive after examining the media. .TP format_defectmgt_cert_off Disable the usual media quality certification in order to save time and format to default size. The certification setting persists even if subsequent blank= options override the size of the format selection. .br Whether formatting without certification works properly depends much on the drive. One should check the "Format status:" from --list_formats afterwards. .TP format_defectmgt_cert_on Re-enable the usual media quality certification and format to default size. The certification setting persists like with format_defectmgt_cert_off. .br Whether there happens certification at all depends much on the media state and the actually selected format descriptor. .TP format_defectmgt_max Format DVD-RAM or BD to reserve a maximum number of spare blocks. .TP format_defectmgt_min Format DVD-RAM or BD to reserve a minimum number of spare blocks. It might be necessary to format format_defectmgt_none first in order to get offered the most minmal spare blocks sizes for format_defectmgt_min. .TP format_defectmgt_none Format DVD-RAM or BD-RE to the largest available payload in the hope to disable defect management at all. This may or may not have a speed increasing effect. Unformatted blank BD-R will be left unformatted. .TP format_defectmgt_payload_ Format DVD-RAM or BD. The text after "format_defectmgt_payload_" gives a number of bytes, eventually with suffixes "s", "k", "m". The largest number of spare blocks will be chosen which enables at least the given payload size. .TP format_by_index_ Format DVD-RW, DVD+RW, DVD-RAM or BD. The number after "format_by_index_" is used as index to the list of available format descriptors. This list can be obtained by option --list_formats. The numbers after text "Format idx" are the ones to be used with format_by_index_. Format descriptor lists are volatile. Do neither eject nor write the media between the run of --list_formats and the run of blank=format_by_index_ or else you may get a different format than desired. .TP help Print this list of blanking types. .RE .TP .BI \-checkdrive Retrieve some info about the addressed drive and then exit. Exits with non-zero value if the drive cannot be found and opened. .TP .BI \-copy Create the subsequent tracks with permission for an unlimited number of copies. .TP .BI cuefile= path Read a session description from a cue sheet file in CDRWIN format. Base the tracks on a single file which is given in the sheet by command FILE. To enable CD-TEXT from the cue sheet file, cdrskin option -text has to be present. .br cdrskin currently supports TRACK datatypes AUDIO and MODE1/2048 which may not be mixed. Data source may be of FILE type BINARY, MOTOROLA, or WAVE. .br Non-CDRWIN commands ARRANGER, COMPOSER, MESSAGE are supported. .br Cue sheet file commands CATALOG and ISRC may be overridden by option mcn= and by input_sheet_v07t= purpose specifiers "UPC / EAN" and "ISRC". This does not affect their appearance in CD-TEXT, but only on Q sub-channel. .br The track numbers may be overridden by option cd_start_tno=. .TP .BI \-dao Alias for option -sao. Write CD in Session at Once mode or DVD-R[W] in Disc-at-once mode. .TP .BI \-data Subsequent tracks are data tracks. This option is default and only needed to mark the end of the range of an eventual option -audio or -xa1. .br Options -mode2, -xa, and -xa2 get mapped to -data, not using the desired CD sector formats and thus not taking advantage of eventual higher payload. .TP .BI \-xa1 Subsequent tracks are data tracks with input suitable for CD-ROM XA mode 2 form 1. This differs from -data input by 8 additional header bytes per block. cdrskin will not write CD-ROM XA but rather strip the header bytes and write as -data tracks. .TP .BI dev= target Set the address of the drive to use. Valid are at least the addresses listed with options --devices or --device_links, X,Y,Z addresses listed with option -scanbus, ATA:X,Y,Z addresses listed with options dev=ATA -scanbus, and volatile libburn drive numbers (numbering starts at "0"). Other device file addresses which lead to the same drive might work too. .br If no dev= is given, volatile address "dev=0" is assumed. That is the first drive found being available. Better avoid this ambiguity on systems with more than one drive. .br The special target "help" lists hints about available addressing formats. Be aware that deprecated option --old_pseudo_scsi_adr may change the meaning of Bus,Target,Lun addresses. .TP .BI driveropts= opt Set "driveropts=noburnfree" to disable the drive's eventual protection mechanism against temporary lack of source data (i.e. buffer underrun). A drive that announces no such capabilities will not get them enabled anyway, even if attempted explicitly via "driveropts=burnfree". .TP .BI \-dummy Try to perform the drive operations without actually affecting the inserted media. There is no warranty that this will work with a particular combination of drive, media, and write mode. Blanking is prevented reliably, though. To avoid inadverted real burning, -dummy refuses burn runs on anything but CD-R[W], DVD-R[W], or emulated stdio-drives. .TP .BI \-eject Eject the disc after work is done. .TP .BI \-force Assume that the user knows better in situations when cdrskin or libburn are refusing because of concerns about drive or media state. .br .B Caution: Use option -force only when in urgent need. .br This option enables the attempt to blank media which are classified as unknown or unsuitable, and the attempt to use write modes of which libburn believes they are not supported by the drive. .br Another application is to enforce blanking or re-formatting of media which appear to be in the desired blank or format state already. .br This option enables a burn run with option -dummy even if libburn believes that drive and media will not simulate the write mode but will write for real. .br It enables a burn run where cdrskin expects to exceed the available media capacity. This is known as "overburn" and might succeed on CD media with write type SAO. .B Too much overburning might be .B harmful to the medium and might .B make the drive unusable (hopefully only until it gets powered off and on). The man page of cdrecord mentions 88 seconds = 6600 blocks as halfways safe amount over the official medium capacity. The assessment of track sizes by libburn will be wrong if the written size reaches or exceeds 90 minutes = 405000 sectors. The overall medium size assessment by the Linux kernel is supposed to yield roughly the written size, but you should test this yourself with every overburnt medium. .br First consider to use a medium with more capacity rather than trying to overburn a CD. .TP .BI \-format Same as blank=format_overwrite_full -force but restricted to DVD+RW. .TP .BI fs= size Set the fifo size to the given value. The value may have appended letters which multiply the preceding number: .br "k" or "K" = 1024 , "m" or "M" = 1024k , "g" or "G" = 1024m , "s" or "S" = 2048 .br Set size to 0 in order to disable the fifo (default is "4m"). .br The fifo buffers an eventual temporary surplus of track source data in order to provide the drive with a steady stream during times of temporary lack of track source supply. The larger the fifo, the longer periods of poor source supply can be compensated. But a large fifo needs substantial time to fill up if not curbed via option fifo_start_at=size. .TP .BI gracetime= seconds Set the grace time before starting to write. (Default is 0) .TP .BI -immed Equivalent to: .br modesty_on_drive=1:min_percent=75:max_percent=95 .br The name of this cdrecord option stems from the "Immed" bit which can make some long running drive commands asynchronous and thus eases the load on some wiring hardware types. Regardless of option -immed, cdrskin uses asynchronous commands where possible and appropriate. To really disable asynchronous command execution, use option use_immed_bit=off . .TP .BI index= list Set a comma separated list of index start address numbers for the next track. This applies to CD SAO sessions only. .br The addresses count sectors from the start of the next track. The first number is for index 1 and must be 0. The following numbers have to be larger than their respective predecessors. Up to 99 numbers are allowed. .br Sector numbers are computed from Min:Sec:Frame addresses by .br Sector = ((Min*60)+Sec)*75+Frame .br E.g.: "0,7512,20408" sets index 2 to 01:40:12 and index 3 to 04:32:08. .TP .BI -inq Print the identification of the drive and then exit. .TP .BI -isosize The next track following this option will try to obtain its source size from the header information out of the first few blocks of the source data. If these blocks indicate an ISO-9660 filesystem then its declared size will be used under the assumption that it is a single session filesystem. .br If not, then the burn run will be aborted. .br The range of -isosize is exactly one track. Further tracks may be preceded by further -isosize options, though. At least 15 blocks of padding will be added to each -isosize track. But be advised to rather use padsize=300k. .br This option can be performed on track sources which are regular files or block devices. For the first track of the session it can be performed on any type of source if there is a fifo of at least 64 kiB. See option fs= . .TP .BI isrc= text Set the ISRC for the next track source to the given text, which must be exactly 13 characters long. It must comply to the format CCOOOYYSSSSS. .br CC is the country code. OOO is the owner code. Both may consist of capital letters A to Z and of decimal digits 0 to 9. YY depicts the year (00 to 99). SSSSS is the serial number (00000 to 99999). .br This option does not affect CD-TEXT but only the Q sub-channel. .TP .BI -load Load the media and exit. Exit value is 0 if any kind of media was found, non zero else. Note: Option -eject will unload the media even if -load is given. .TP .BI -lock Like option -load but leave the drive's eject button disabled if there is any media found and not option -eject is given. .br Use program "eject" or cdrskin -eject to get the tray out of the drive. Runs of programs like cdrecord, growisofs, wodim, cdrskin will not be hampered and normally enable the drive's eject button when they are done. .TP .BI mcn= text Set the CD Media Catalog Number to text, which must be exactly 13 characters long and should consist of decimal digits. .br This option does not affect CD-TEXT but only the Q sub-channel. .TP .BI minbuf= percentage Equivalent to: .br modesty_on_drive=1:min_percent=:max_percent=95 .br Percentage is permissible between 25 and 95. .TP .BI -minfo Print information about the loaded media. This includes media type, writability state, and a quite readable table of content. .TP .BI msifile= path Run option -msinfo and copy the result line into the file given by path. Unlike -msinfo this option does not redirect all normal output away from standard output. But it may be combined with -msinfo to achieve this. .br Note: msifile=path is actually an option of wodim and not of cdrecord. .TP .BI \-msinfo Retrieve multi-session info for preparing a follow-up session by option -C of programs mkisofs, genisoimage, or xorriso -as mkisofs. Print result to standard output. This option redirects to stderr all message output except the one of option --tell_media_space and its own result string, which consists of two numbers. The result string shall be used as argument of option -C with said programs. It gives the start address of the most recent session and the predicted start address of the next session to be appended. The string is empty if the most recent session was not written with option -multi. .br To have a chance for working on overwriteable media, this option has to be accompanied by option --grow_overwriteable_iso. .TP .BI \-multi This option keeps CD, unformatted DVD-R[W], DVD+R, or BD-R appendable after the current session has been written. Without it the disc gets closed and may not be written any more - unless it is a -RW and gets blanked which causes loss of its content. .br The following sessions can only be written in -tao mode. -multi is prohibited with DVD-R[W] DAO write mode and on DVD-R DL media. Option --prodvd_cli_compatible eventually makes -multi tolerable but cannot make it work. .br In order to have all filesystem content accessible, the eventual ISO-9660 filesystem of a follow-up session needs to be prepared in a special way by the filesystem formatter program. mkisofs and genisoimage expect particular info about the situation which can be retrieved by cdrskin option -msinfo. .br To retrieve an archive file which was written as follow-up session, you may use option -toc to learn about the "lba" of the desired track number. This lba is the address of the 2048 byte block where the archive begins. .br With overwriteable DVD or BD media, -multi cannot mark the end of the session. So when adding a new session this end has to be determined from the payload. Currently only ISO-9660 filesystems can be used that way. See option .B \--grow_overwriteable_iso for lifting the ban on -multi. .br Note: -multi might make DVD media unreadable in some DVD-ROM drives. .TP .BI \-nocopy Create subsequent tracks with permission for a single level of copies. I.e. those copies would then be marked by -scms as offering no permission for further copies. .TP .BI \-nopad Do not add trailing zeros to the data stream. Nevertheless, since there seems to be no use for audio tracks with incomplete last sector, this option applies only to data tracks. There it is default. .TP .BI \-nopreemp Indicate for subsequent tracks that they were mastered without pre-emphasis. .TP .BI \-pad Add 30 kiB of trailing zeros to each data track. (This is not sufficient to avoid problems with various CD-ROM read drivers.) .TP .BI padsize= size Add the given amount of trailing zeros to the next data track. This option gets reset to padsize=0 after that next track is written. It may be set again before the next track argument. About size specifiers, see option fs=. .TP .BI \-preemp Indicate for subsequent tracks that they were mastered with pre-emphasis. .TP .BI \-sao Write CD in Session At Once mode or sequential DVD-R[W] in Disc-at-once (DAO) mode. .br With CD this mode is able to put several audio tracks on media without producing audible gaps between them. .br With DVD-R[W] this mode can only write a single track. No -multi is allowed with DVD-R[W] -sao. .br -sao is permissible with overwriteable DVD, or DVD+R[/DL], or BD but actually only imposes restrictions without providing known advantages. .br -sao can only be used for tracks of fixely predicted size. This implies that track arguments which depict stdin or named pipes need to be preceded by option tsize= or by option tao_to_sao_tsize=. .br -sao cannot be used on appendable media. .TP .BI \-scanbus Scan the system for drives. On Linux the drives at /dev/s* and at /dev/hd* are to be scanned by two separate runs. One without dev= for /dev/s* and one with dev=ATA for /dev/hd* devices. (Option --drives lists all available drives in a single run.) .br Drives which are busy or which offer no rw-permission to the user of cdrskin are not listed. Busy drives get reported in form of warning messages. .br The useful fields in a result line are: .br Bus,Target,Lun Number) 'Vendor' 'Mode' 'Revision' .TP .BI \-scms Create subsequent tracks without permission for being copied. This is usually done for tracks which are copies of tracks that were marked with -nocopy (but not yet with -scms). So copies of copies are prohibited. .br This option gets reset by option -copy. Thus the combination -copy -nocopy means -nocopy surely without -scms. .TP .BI speed= number Set speed of drive. With data CD, 1x speed corresponds to a throughput of 153,600 bytes/second. With DVD, 1x = 1,385,000 bytes/second. With BD 1x = 4,495,625 bytes/second. It is not an error to set a speed higher than is suitable for drive and media. One should stay within a realistic speed range, though. Special speed settings are: .br 0 = minimal speed , -1 = maximal speed (default), text "any" = like -1. .TP .BI \-swab Announce that the raw audio data source of subsequent tracks is byte swapped versus the expectations of cdrecord. This option is suitable for audio where the least significant byte of a 16 bit word is first (little-endian, Intel). Most raw audio data on PC systems are available in this byte order. Less guesswork is needed if track sources are in format MS-WAVE in a file with suffix ".wav". .TP .BI \-tao Write CD in Track At Once (TAO) mode, sequential DVD-R[W] in Incremental Streaming mode, or DVD+R[/DL] without traditional -sao restrictions. This mode also applies pro-forma to overwriteable media .br Mode -tao can be used with track sources of unpredictable size, like standard input or named pipes. It is also the only mode that can be used for writing to appendable media which already hold data. With unformatted DVD-R[W] it is the only mode which can keep media appendable by option -multi. .br Mode -tao is not usable for minimally blanked DVD-RW and for DVD-R DL. .TP .BI \-text Enable writing of CD-TEXT attributes read by option cuefile=. Without option -text, cue sheet file command CDTEXTFILE will be ignored and no CD-TEXT attributes will be read from the file. Nevertheless, CATALOG and ISRC will have the same effect as options mcn= and isrc=. .TP .BI textfile= path Read CD-TEXT packs from the file depicted by path and put them into the Lead-in of the emerging session. This session has to be done by Session At Once (SAO) mode and may only contain audio tracks. .br path must lead to a regular file, which consists of an optional header of four bytes and one or more text packs of 18 bytes each. Suitable would be the file 'cdtext.dat' which gets extracted from CD media by options -vv -toc and shown in human readable form by -vvv -toc. .br The header, if present, must tell the file size minus 2, encoded as big-endian 16 bit word. The other two bytes must be 0. .br If there is no 4-byte header, then a trailing 0-byte, as of Sony specification, is tolerated and ignored. .br A text pack consists of a pack type byte, a track number byte, a counter byte, a Block Number and Character Indicator byte, 12 text characters or data bytes, two optional CRC bytes. For details see libburn documentation file doc/cdtext.txt. .br By default, the input file is checked for correct CRC bytes. If all CRC bytes are 0, then the correct values get silently inserted. If there are non-zero CRC bytes, then a mismatch causes the abort of the burn run. This check can be disabled by option -force. .br Note that this option overrides option input_sheet_v07t= . .TP .BI \-toc Print the table of content (TOC) which describes the tracks recorded on disc. The output contains all info from option -atip plus lines which begin with "track:", the track number, the word "lba:" and a number which gives the start address of the track. Addresses are counted in CD sectors which with SAO or TAO data tracks hold 2048 bytes each. .br If verbosity is set to level 2 (-v -v) then the CD-TEXT packs from the lead-in of an audio CD get extracted and written into file 'cdtext.dat', if that file does not yet exist. Prepended is a 4 byte header, followed by one or more packs of 18 bytes each. .br Verbosity level 3 causes the CD-TEXT packs to be printed as hex numbers to standard output. Bytes 4 to 15 of certain pack types are printed as ASCII characters if they have values in the range of 32 to 126. .br See option textfile= for more information about the text pack format. .RS .TP Example. Retrieve an afio archive from track number 2: .br tracknumber=2 .br lba=$(cdrskin dev=/dev/cdrom -toc 2>&1 | \\ .br grep '^track:[ ]*[ 0-9][0-9]' | \\ .br tail +"$tracknumber" | head -1 | \\ .br awk '{ print $4}' ) .br dd if=/dev/cdrom bs=2048 skip="$lba" | \\ .br afio -t - | less .RE .TP .BI tsize= size Announces the exact size of the next track source. This is necessary with any write mode other than -tao if the track source is not a regular disk file, but e.g. "-" (standard input) or a named pipe. About size specifiers, see option fs=. .br If the track source does not deliver the predicted amount of bytes, the remainder of the track is padded with zeros. This is not considered an error. If on the other hand the track source delivers more than the announced bytes then the track on media gets truncated to the predicted size and cdrskin exits with non-zero value. .TP .BI \-v Increment verbosity level by one. Startlevel is 0 with only few messages. Level 1 prints progress report with long running operations and also causes some extra lines to be put out with info retrieval options. Level 2 additionally reports about option settings derived from arguments or startup files. Level 3 is for debugging and useful mainly in conjunction with somebody who had a look into the program sourcecode. .TP .BI \-V Enable logging of SCSI commands to stderr. This is helpful for expert examination of the interaction between libburn and the drive. The commands are specified in SCSI-3 standards SPC, SBC, MMC. .TP .BI \-waiti Wait until input data is available at stdin or EOF occurs at stdin. Only then begin to access any drives. .br One should use this if cdrskin is working at the end of a pipe where the feeder process reads from the drive before it starts writing its output into cdrskin. Example: .br mkisofs ... -C 0,12800 -M /dev/sr0 | \\ .br cdrskin dev=/dev/sr0 ... -waiti - .br This option works even if stdin is not among the track sources. If no process is piping in, then the Enter key of your terminal will act as trigger for cdrskin. Note that this input line will not be consumed by cdrskin if stdin is not among the track sources. It will end up as shell command, usually. .PP Alphabetical list of options which are genuine to cdrskin and intended for normal use: .TP .BI \--adjust_speed_to_drive Curb explicitly given speed= values to the maximum which is announced by the drive for the loaded media. By default, such an adjustment is only made with pseudo-speeds 0 and -1 whereas speed settings > 0 are sent unchanged to the drive which will then choose an appropriate speed on its own. .TP .BI \--allow_emulated_drives Enable drive addresses of the form dev=stdio:. See above, paragraph "Drive preparation and addressing". .TP .BI \--allow_setuid Disable the loud warning about insecure discrepance between login user and effective user which indicates application of chmod u+s to the program binary. One should not do this chmod u+s , but it is an old cdrecord tradition. .TP .BI \--any_track Allow source_addresses to begin with "-" (plus further characters) or to contain a "=" character. By default such arguments are seen as misspelled options. It is nevertheless not possible to use one of the options listed with --list_ignored_options. .TP .BI assert_write_lba= block_number | byte_address Abort if the write address given with this option is not the same as predicted immediately before the write session starts. This option can ensure that a start address which was presumed by a formatter like mkisofs -C is really used by the drive for writing. assert_write_lba=0 effectively demands blank media and excludes appendables. .br Block numbering is peculiar: If the last character of the option string is a letter [a-zA-Z] then the usual unit scaling by "s", "k", "m", etc. applies and the result is divided by 2048. Else the number value of the string is taken as plain block number with block size 2048 byte. (E.g ...=1000 or ...=1000s means block 1000, ...=1m means block 512, ...=4096b means block number 2) .TP .BI cd_start_tno= number Set the number which shall be written as CD track number with the first track of the session. The following tracks will then get written with consecutive CD track numbers. The resulting number of the last track must not exceed 99. The lowest possible start number is 1, which is also the default. .br This setting applies only to CD SAO writing. It overrides the track number settings caused by options cuefile= or input_sheet_v07t=. .TP .BI cdtext_to_textfile= path Extract the CD-TEXT packs from the lead-in of an audio CD and write them to the file with the given path. If CD-TEXT can be retrieved, then this file will be suitable for option textfile=. .br Not all drives can read CD-TEXT and not all audio CDs bear CD-TEXT. It is not considered an error if no CD-TEXT is available. .TP .BI cdtext_to_v07t= path Extract the CD-TEXT packs from the lead-in of an audio CD and write them as human readable Sony Input Sheet Version 0.7T to the file with the given path. If CD-TEXT can be retrieved, then this file will be suitable for option input_sheet_v07t=. .br If the given path is "-", then the result is printed to standard output. .br Not all drives can read CD-TEXT and not all audio CDs bear CD-TEXT. It is not considered an error if no CD-TEXT is available. .TP .BI \--demand_a_drive Exit with a nonzero value if no drive can be found during a bus scan. .TP .BI \--devices List the device file addresses of all accessible CD drives. In order to get listed, a drive has to offer rw-permission for the cdrskin user and it may not be busy. The superuser should be able to see all idle drives listed and busy drives reported as "SORRY" messages. .br Each available drive gets listed by a line containing the following fields: .br Number dev='Devicefile' rw-Permissions : 'Vendor' 'Model' .br Number and Devicefile can both be used with option dev=, but number is volatile (numbering changes if drives become busy). .TP .BI \--device_links Like --devices, but presenting the drives with addresses of symbolic links which point to the actual device files. .br Modern GNU/Linux systems may shuffle drive addresses from boot to boot. The udev daemon is supposed to create links which always point to the same drive, regardless of its system address. Option --device_links shows the addresses of such links if they begin by "/dev/dvd" or "/dev/cd". Precedence is: "dvdrw", "cdrw", "dvd", "cdrom", "cd". .TP .BI direct_write_amount= size Do not write a session with tracks but rather make an appropriate number of direct write operations with no preparations. Flushing the drive buffer will be the only finalization. It is advised to eject the media afterwards because the write operations circumvent the usual system i/o with its caches and buffers. By ejecting, those invalid memory copies get surely discarded. .br Only few media can be written this way: DVD-RAM, BD-RE, RVD+RW and overwriteable DVD-RW. Writing is restricted to the already formatted area of the media. .br Writing starts at byte 0 of the media or at the address given by option .B write_start_address= . Only the first track source is used as input for the write operations. The fifo (fs=) is disabled. .br Parameter .B size controls the amount of data to be written. Size 0 means that the track source shall be used up until EOF. In this case, the last write transaction gets padded up to the necessary size by zeros. Size -1 revokes direct writing and switches back to normal session oriented writing. .br Both, write_start_address and direct_write_amount size must be aligned to a media dependent transaction size. With DVD-RAM, BD-RE, DVD+RW this is 2k, with overwriteable DVD-RW it is 32k. .TP .BI dvd_obs= default|32k|64k Set the number of bytes to be transmitted with each write operation to DVD or BD media. With most write types, tracks get padded up to the next multiple of this write size (see option --obs_pad). A number of 64 KB may improve throughput with systems which show latency problems. The default depends on media type, option stream_recording=, and on compile time options. .TP .BI extract_audio_to= directory_path Extract tracks from an audio CD as separate WAVE audio files into the given directory. This directory has to already exist, but none of the track files may exist. This option will rather fail than overwrite an existing file. .br By default all tracks of the CD are extracted to files with names NN.wav, where NN is the track number from 01 to at most 99. .TP .BI extract_basename= name Set a filename prefix which shall be used by extract_audio_to= instead of the empty default name prefix. .TP .BI --extract_dap Enable Digital Audio Play flaw obscuring mechanisms like audio data mute and interpolate. .TP .BI extract_tracks= number[,number[,...]] Set a list of track numbers to define which tracks shall be extracted by extract_audio_to=. If no extract_tracks= is given, then all audio tracks get extracted. It is permissible to have more than one extract_tracks= option in order to split a long list into shorter pieces. .br The lowest permissible track number is 1, the highest is 99. .TP .BI fallback_program= command Set a command name to be executed if cdrskin encounters a known cdrecord option which it does not yet support. If a non-empty command is given with fallback_program=, and if no essential options are given which are specific to cdrskin, then cdrskin will delegate the job to said command. .br The natural commands to be given are cdrecord or wodim but one may well submit the address of an own program. .br The fallback program will get all arguments of cdrskin which do not match the shell patterns --?* or *_*=* . This eventually suppresses path names of track sources which happen to match those patterns. The options from the startup files are not handed to the fallback program. .br Fallback program execution is disabled if cdrskin is run setuid and not option --allow_setuid is given. In general, the drive's device files and the involved programs should be set up so that each program runs under its advised conditions. (E.g. cdrskin as member of group floppy, cdrecord setuid root.) .br Two alias names for cdrskin are predefined with default fallback programs: .br .B unicord implies fallback_program=cdrecord .br .B codim implies fallback_program=wodim .TP .BI --four_channel Indicate for subsequent tracks that they were mastered with four channels. .TP .BI fifo_start_at= size Do not wait for full fifo but start burning as soon as the given number of bytes is read. This option may be helpful to bring the average throughput near to the maximum throughput of a drive. A large fs= and a small fifo_start_at= combine a quick burn start and a large savings buffer to compensate for temporary lack of source data. At the beginning of burning, the software protection against buffer underrun is as weak as the size of fifo_start_at= . So it is best if the drive offers hardware protection which is enabled automatically if not driveropts=noburnfree is given. .TP .BI \--grow_overwriteable_iso Enable emulation of multi-session writing on overwriteable media which contain an ISO-9660 filesystem. This emulation is learned from growisofs -M but adapted to the usage model of .br .B cdrskin -msinfo .br .B mkisofs -C -M | cdrskin -waiti [-multi] - .br --grow_overwriteable_iso does not hamper the use of true multi-session media. I.e. it is possible to use the same cdrskin options with both kinds of media and to achieve similar results if ISO-9660 filesystem images are to be written. This option implies option -isosize and therefore demands that the track source is a ISO-9660 filesystem image. .br With overwriteable media and no option blank=fast|all present it expands an eventual ISO-9660 filesystem on media. It is assumed that this image's inner size description points to the end of the valuable data. Overwriteable media with a recognizable ISO-9660 size will be regarded as appendable rather than as blank. I.e. options -msinfo and -toc will work. -toc will always show a single session with its size increasing with every added mkisofs image. .br If not overridden by option write_start_address=, the track with the new image will be placed behind the end of the old one. One may use option assert_write_lba= to make sure that media state and mkisofs job do match. .br --grow_overwriteable_iso causes option blank=fast|all to invalidate an eventual ISO-9660 image by altering the first few bytes of block 16 on overwriteable media. Option -multi is tolerated in order not to hamper true multi-session media. .br An equivalent of growisofs -Z for overwriteable media is: .br .B mkisofs | cdrskin --grow_overwriteable_iso blank=fast [-multi] - .br With multi-session DVD, blank=fast will act like dvd+rw-format -blank=full . .br growisofs -dvd-compat is roughly equivalent to cdrskin without option -multi. .TP .BI input_sheet_v07t= path Read CD-TEXT definitions from a Sony Input Sheet version 0.7T. Up to eight or seven such sheets can be read by multiple input_sheet_v07t= options. Each will define one CD-TEXT language block. .br The first line of a sheet file decides whether more than one sheet may be defined by the file. If it is .br Input Sheet Version = 0.7T .br then each further line with that text switches to the next sheet for the next block. If it is not, then all definitions apply to a single block. .br The information in such a sheet is given by text lines of the following form: .br purpose specifier [whitespace] = [whitespace] content text .br [whitespace] is zero or more ASCII 32 (space) or ASCII 9 (tab) characters. The purpose specifier tells the meaning of the content text. Empty content text does not cause a CD-TEXT attribute to be attached. .br The following purpose specifiers apply to the session as a whole: .br Purpose specifier | Content example .br ------------------------------------------------------------- .br Text Code = 8859 .br Language Code = English .br Album Title = Joyful Nights .br Artist Name = United Cat Orchestra .br Songwriter = Various Songwriters .br Composer = Various Composers .br Arranger = Tom Cat .br Album Message = For all our fans .br Catalog Number = 1234567890 .br Genre Code = Classical .br Genre Information = Feline classic music .br Closed Information = This is not to be shown by CD players .br UPC / EAN = 1234567890123 .br Text Data Copy Protection = OFF .br First Track Number = 1 .br Last Track Number = 3 .br The following purpose specifiers apply to particular tracks: .br Purpose specifier | Content example .br ------------------------------------------------------------- .br Track 01 Title = Song of Joy .br Track 01 Artist = Felix and The Purrs .br Track 01 Songwriter = Friedrich Schiller .br Track 01 Composer = Ludwig van Beethoven .br Track 01 Arranger = Tom Cat .br Track 01 Message = Fritz and Louie once were punks .br ISRC 01 = XYCRR1101234 .br Track numbers are decimal despite the leading 0. There should be as many track definitions as there are track source files given. .br See libburn's doc/cdtext.txt for a detailed definition of 0.7T and the possible values for Text Code, Language Code, Genre Code, Text Data Copy Protection. .br The Q sub-channel settings by "UPC / EAN" and "ISRC" may be overridden by options mcn= and isrc=. This will not affect their appearance as CD-TEXT. They may override cuefile= commands CATALOG and ISRC in the same way. .br If options -text cuefile= are given and if the cue sheet file defines CD-TEXT, then only seven input_sheet_v07t= options may be given. They will then be used as CD-TEXT language blocks 1 to 7. .br This option will get into effect only if no option textfile= is given. The write mode must be SAO on CD. All tracks must be -audio tracks. .br The track numbers may be overridden by option cd_start_tno=. .TP .BI \--list_formats List the available format descriptors as reported by the drive for the loaded media. Each descriptor line begins with "Format idx" and the descriptor's list index, followed by a ":", the format type, the number of payload blocks and that same number converted to MiB. .br The meaning of the format types is defined by the MMC standard with command FORMAT UNIT. A user will more be interested in the sizes than in the types. .TP .BI \--list_ignored_options List all ignored cdrecord options. The "-" options cannot be used as addresses of track sources. No track source address may begin with a text equal to an option which ends by "=". The list is ended by an empty line. .TP .BI \--list_speeds Put out a list of speed values as reported by the output drive with the loaded medium. This does not necessarily mean that the medium is writable or that these speeds are actually achievable. Especially the lists reported with empty drive or with ROM media obviously advertise speeds for other media. .br It is not mandatory to use speed values out of the listed range. The drive is supposed to choose a safe speed that is as near to the desired speed as possible. .br At the end of the list, "Write speed L" and "Write speed H" are the best guesses for lower and upper speed limit. "Write speed l" and "Write speed h" may appear only with CD and eventually override the list of other speed offers. .br Only if the drive reports contradicting speed information there will appear "Write speed 0" or "Write speed-1", which tell the outcome of speed selection by options speed=0 or speed=-1, if it deviates from "Write speed L" or "Write speed H", respectively. .TP .BI \--long_toc Like option -toc but marking each session start by a line "first: X last: Y" and each session end by "track:lout ...". .TP .BI \--no_load When aquiring the optical drive, do not try to load its tray. This yields the same behavior for desktop drives with tray loader as is shown by laptop drives which usually lack a motorized tray loader. .TP .BI \--no_rc Only if used as first command line argument this option prevents reading and interpretation of eventual startup files. See section FILES below. .TP .BI \--pacifier_with_newline Adds a newline character to each pacifier line that would elsewise be overwritten by the next pacifier line. Such lines are emitted during a run of writing, formatting, or blanking if option -v is given. .TP .BI \--prodvd_cli_compatible Activates behavior modifications with some DVD situations which bring cdrskin nearer to the behavior of cdrecord-ProDVD: .br Option -multi with unsuitable media is not an error but simply has no effect. .br Options blank=fast and blank=all deformat overwriteable DVD-RW media. .br Option blank=fast does indeed minmal blanking with DVD-RW. This may yield media which can only do DAO but not Incremental Streaming. .TP .BI \--single_track Accept only the last argument of the command line as track source address. .TP .BI stdio_sync= on|off|number Set the number of bytes after which to force output to drives with prefix "stdio:". This forcing keeps the memory from being clogged with lots of pending data for slow devices. Default "on" is the same as "16m". Forced output can be disabled by "off". .TP .BI stream_recording= on|off|number By setting "on" request that compliance to the desired speed setting is preferred over management of write errors. With DVD-RAM and BD this can bring effective write speed near to the nominal write speed of the media. But it will also disable the automatic use of replacement blocks if write errors occur. It might as well be disliked or ignored by the drive. .br If a number is given, then error management stays enabled for all byte addresses below that number. Any number below 16s is the same as "off". .TP .BI tao_to_sao_tsize= size Set an exact fixed size for the next track to be in effect only if the track source cannot deliver a size prediction and no tsize= was specified and an exact track size prediction is demanded by the write mode. .br This was the fallback from bad old times when cdrskin was unable to burn in mode -tao . It came back with minimally blanked DVD-RW, which cannot do Incremental Streaming (-tao), and with explicitly selected write mode -sao for best DVD-ROM compatibility. .br If the track source delivers less bytes than announced then the missing ones will be filled with zeros. .TP .BI --tell_media_space Prepare a recording session, do not perform it but rather inquire the maximum number of 2048 byte data blocks which may be written in the current state of media with the prepared setup. So this option disables recording of data. It does not disable blanking, though, and will measure space afterwards. .br It is not mandatory to give track sources but their nature may influence the available capacity. So for most realistic results one may set up the full burn session and add --tell_media_space. But if one has to expect a cdrskin version prior to 0.3.3 no track source should be given in order not to start an involuntary burn session. In this case set at least -sao or -tao explicitly. .br The result gets printed to standard output. It is 0 or empty if no writing is possible with the given options. This option redirects to stderr all message output except its own result string and eventual output of -msinfo. .TP .BI textfile_to_v07t= path Read a CD-TEXT pack file (e.g. cdtext.dat from a run with -v -v -toc) and print its content in the human readable format that is described with option input_sheet_v07t=. .br The program run ends immediately thereafter. No drive scan will happen and no drive will be acquired. .br To avoid the cdrskin start message in the output, run: cdrskin textfile_to_v07t=cdtext.dat | grep -v '^cdrskin' .TP .BI --two_channel Indicate for subsequent tracks that they were mastered with two channels. .TP .BI write_start_address= byte_offset Set the address on media where to start writing the track. With DVD+RW, DVD-RAM or BD-RE byte_offset must be aligned to 2 kiB blocks, but better is 32 kiB. With DVD-RW 32 kiB alignment is mandatory. .br Other media are not suitable for this option yet. .TP .BI modesty_on_drive= [:parameter=[:parameter=...]] Mode 1 keeps the program from trying to write to the burner drive while its buffer is in danger to be filled by more than parameter "max_percent". If this filling is exceeded then the program will wait until the filling is at most the value of parameter "min_percent". .br Percentages are permissible in the range of 25 to 100. .br This can ease the load on operating system and drive controller and thus help with achieving better input bandwidth if disk and burner are not on independent controllers (like hda and hdb). Unsufficient input bandwidth is indicated by output "(fifo xy%)" of option -v if xy is lower than 90 for some time. modesty_on_drive= might hamper output bandwidth and cause buffer underruns. .br A new use case is to work around the poor simultaneous performance of multiple burn runs on Linux kernel 3.16 and alike. Here it is not about giving the hard disk enough time to fill the fifo, but about keeping ioctl(SG_IO) from blocking for a longer time and thus blocking all other burn runs. .br To have max_percent larger than the burner's best actual buffer fill has the same effect as min_percent==max_percent. Some burners do not use their full buffer with all media types. Watch output "[buf xy%]" of option -v to get an impression of the actual buffer usage. Some burners are not suitable because they report buffer fill with granularity too large in size or time, or because they go to full speed only when their buffer is full. .br If a write attempt is delayed, the program will wait for a number of microseconds which is given by parameter "min_usec" before inquiring the buffer again. If more retries occur, this waiting time between inquiries increases up to the value of parameter "max_usec". .br If the delay lasts longer than the number of seconds given by parameter "timeout_sec", then mode 1 is set 0 and normal burning goes on. .br Mode 0 disables this feature. Mode -1 keeps it unchanged. Default is: .br 0:min_percent=65:max_percent=95:timeout_sec=120: min_usec=10000:max_usec=100000 .br The defaults of cdrskin are good for IDE problems. With concurrent Linux SG_IO problems on modern hardware, higher min_percent and lower usec might yield better buffer fills while still avoiding the problem: .br min_percent=90:max_percent=95:min_usec=5000:max_usec=25000 .PP Alphabetical list of options which are only intended for very special situations and not for normal use: .TP .BI \--abort_handler Establish default signal handling not to leave a drive in busy state but rather to shut it down and to wait until it has ended the final operations. This option is only needed for revoking eventual --ignore_signals or --no_abort_handler. .TP .BI \--allow_untested_media Enable the use of media profiles which have been implemented but not yet tested. Currently this option is without effect because no media types are under test reservation. .br (If you really test experimental media, then please report the outcome on libburn-hackers@pykix.org) .TP .BI \--cdtext_dummy Prepare a burn run, report the effective array of CD-TEXT packs to stdout, and then end the program run without starting to burn the session. A blank CD-R or CD-RW has to be present in the drive, nevertheless. .br The output is formatted in lines which describe 18 bytes as 2-digit hex numbers or as single printable characters. See libburn document doc/cdtext.txt about the format of these records. .TP .BI \--cdtext_verbose Like --cdtext_dummy but without preventing the burn run. Combinable with option -dummy to exercise a CD burn run with no persistent impact on the medium. .TP .BI dev_translation= Set drive address alias. This was necessary before cdrskin-0.2.4 to manually translate cdrecord addresses into cdrskin addresses. .br is a single character which may not occur in the address string . is an address as expected to be given by the user via option dev=. is the address to be used instead whenever is given. More than one translation instruction can be given in one cdrskin run. .br E.g.: dev_translation=+ATA:1,0,0+/dev/sr1 dev_translation=+ATA:1,1,0+/dev/sr2 .TP .BI \--drive_abort_on_busy Linux specific: Abort process if a busy drive is encountered. .TP .BI \--drive_blocking Linux specific: Try to wait for a busy drive to become free. This is not guaranteed to work with all drivers. Some need nonblocking i/o. .TP .BI \--drive_f_setlk Linux specific: Try to get exclusive lock on drive device file via fcntl(2). .TP .BI \--drive_not_exclusive Linux specific: Combine --drive_not_f_setlk and --drive_not_o_excl. .TP .BI \--drive_not_f_setlk Linux specific: Do not try to get exclusive lock on drive device file via fcntl(2). .TP .BI \--drive_not_o_excl Linux specific: Do not ask the operating system to prevent opening busy drives. Whether this leads to senseful behavior depends on operating system and kernel. .TP .BI drive_scsi_dev_family= sr | scd | sg Linux specific: Select a SCSI device file family to be scanned for by options --devices, --device_links and -scanbus. Normally this is /dev/sgN on kernel versions < 2.6 and /dev/srN on kernels >= 2.6 . This option explicitly overrides that default in order to meet other programs at a common device file for each drive. On kernel 2.4 families sr and scd will find no drives. .br Device file family /dev/hdX on kernel >= 2.6 is not affected by this setting. .TP .BI \--drive_scsi_exclusive Linux specific: Try to exclusively reserve device files /dev/srN, /dev/scdM, /dev/sgK of drives. This would be helpful to protect against collisions with program growisofs. Regrettably on Linux kernel 2.4 with ide-scsi emulation this seems not to work. Whether it becomes helpful with new Linux systems has to be evaluated. .TP .BI \--fifo_disable Disable fifo despite any fs=. .TP .BI \--fifo_per_track Use a separate fifo for each track. .TP .BI \--fill_up_media Expand the last track of the session to occupy all remaining free space on the media. .br This option overrides option -multi. It will not fill up media if option -sao is given with CD media. .br .B Caution: With multi-session media this option might increase readatibility on DVD-ROM drives but with some DVD recorders and media types it might also fail to produce readable media at all. "Your mileage may vary". .br You can expect the best possible read compatibility if you do not use -multi at all. .TP .BI grab_drive_and_wait= seconds Open the addressed drive, wait the given number of seconds, release the drive, and do normal work as indicated by the other options used. This option helps to explore the program behavior when faced with busy drives. Just start a second cdrskin with option --devices while grab_drive_and_wait= is still active. .TP .BI \--ignore_signals Try to ignore any signals rather than to abort the program. This is not a very good idea. You might end up waiting a very long time for cdrskin to finish. .TP .BI \--list_features List the SCSI/MMC features which were obtained from the drive when it was last acquired or re-assessed. Although this is better readable than the raw reply to SCSI command GET CONFIGURATION, the MMC specification text is still needed for interpreting it. .br The list consists of line groups of the form .br Code +/- : Name : Version,P/N .br Raw feature data bytes as hex numbers .br Parsed info as Name=Value pairs .br The headline is the only one which has no blank at its start. Code is given as 16 bit hex number. .br "+" marks a currently offered feature. "-" marks those which may be offered under different circumstances. .br Name is the feature name as listed in MMC specs. .br "P" marks persistent features. "N" marks non-persistent features. .br The Raw data can occupy more than one line. No "=" occurs in such lines. If no raw data are present, one line with some blanks is listed instead. .br The Parsed info shows some extracted field values with names which resemble the names used in the MMC description of the particular feature. Parsed info lines contain at least one Name=Value pair. More than one line is possible. If no parsed info is produced, one line with some blanks is listed instead. .br Example: .br 0107 - : Real Time Streaming : 4,N .br 1f 00 00 00 .br RBCB=1 , SCS=1 , MP2A=1 , WSPD=1 , SW=1 .TP .BI \--no_abort_handler On signals exit even if the drive is in busy state. This is not a very good idea. You might end up with a stuck drive that refuses to hand out the media. .TP .BI \--no_blank_appendable Refuse to blank appendable CD-RW or DVD-RW. This is a feature that was once builtin with libburn. No information available for what use case it was needed. .TP .BI \--no_convert_fs_adr Do only literal translations of dev=. This prevents cdrskin from test-opening device files in order to find one that matches the given dev= specifier. .br Partly Linux specific: Such opening is needed for Bus,Target,Lun addresses unless option --old_pseudo_scsi_adr is given. It is also needed to resolve device file addresses which are not listed with cdrskin --devices but nevertheless point to a usable drive. (Like /dev/sg0 using the same SCSI address as /dev/sr0.) .TP .BI \--obs_pad Pad the data of the last write operation of a DVD-R[W] DAO session, or BD-R session, or stdio: pseudo-drive session up to the full size of an output chunk. This padding has to be applied automatically to the other DVD and BD media types, where it causes e.g. ISO images to have trailing unclaimed blocks. Whether it is applied automatically to BD-R depends on option --bdr_obs_exempt. .br Use this option if there is the suspicion that DVD-R[W] DAO or BD-R sessions abort with your kernel and/or DVD drive, if their size is not a multiple of 16 blocks. .br This option may also get enabled at compile time of libburn. .TP .BI \--bdr_obs_exempt Exempt BD-R media from automatic unconditional transaction end padding, provided that this padding is not requested by --obs_pad and that no stream_recording is requested. .br This is a new feature introduced with version 1.5.6. It might become default in later versions. .TP .BI \--old_pseudo_scsi_adr Linux specific: Use and report literal Bus,Target,Lun addresses rather than real SCSI and pseudo ATA addresses. This method is outdated and was never compatible with original cdrecord. .TP .BI sao_postgap= off|number Define whether a post-gap shall be written at the end of the track and how many sectors this gap shall have. A post-gap occupies the range of an additional index of the track. It contains zeros. No bytes from the track source will be read for writing the post-gap. .br This setting affects only CD SAO write runs. .TP .BI sao_pregap= off|number Define whether a pre-gap shall be written before the track and how many sectors this pre-gap shall have. A pre-gap is written in the range of track index 0 and contains zeros. No bytes from the track source will be read for writing the pre-gap. .br This setting affects only CD SAO write runs. .br The first track automatically gets a pre-gap of at least 150 sectors. Its size can only be enlarged by this call. .TP .BI use_immed_bit= on|off|default Control whether several long lasting SCSI commands shall be executed with the Immed bit, which makes the commands end early while the drive operation is still going on. cdrskin then inquires progress indication until the drive reports to be ready again. If this feature is turned off, then blanking and formatting will show no progress indication. .br It may depend on the operating system whether use_immed_bit= is set to "off" by default. .TP .BI --xa1-ignore Silently interpret option -xa1 as -data. This may be necessary if a frontend does not prepare -xa1 block headers but insists in using option -xa1. .SH EXAMPLES .SS .B Get an overview of drives and their addresses: .br cdrskin -scanbus .br cdrskin dev=ATA -scanbus .br cdrskin --device_links .SS .B Get info about a particular drive or loaded media: .br cdrskin dev=0,1,0 -checkdrive .br cdrskin dev=ATA:1,0,0 -v -atip .br cdrskin dev=/dev/hdc -minfo .SS .B Prepare CD-RW or DVD-RW for re-use, DVD-RAM or BD-RE for first use: .br cdrskin -v dev=/dev/sg1 blank=as_needed -eject .SS .B Format DVD-RW to avoid need for blanking before re-use: .br cdrskin -v dev=/dev/sr0 blank=format_overwrite .SS .B De-format DVD-RW to make it capable of multi-session again: .br cdrskin -v dev=/dev/sr0 blank=deformat_sequential .SS .B Write ISO-9660 filesystem image as only one to blank or formatted media: .br cdrskin -v dev=/dev/hdc speed=12 fs=8m \\ .br blank=as_needed -eject padsize=300k my_image.iso .SS .B Write compressed afio archive on-the-fly (not possible with minimally blanked DVD-RW or DVD-R DL): .br find . | afio -oZ - | \\ .br cdrskin -v dev=0,1,0 fs=32m speed=8 \\ .br blank=as_needed padsize=300k - .SS .B Write multi-session to the same CD, DVD-R[W], DVD+R[/DL], or BD-R: .br cdrskin dev=/dev/sr0 -v padsize=300k -multi 1.iso .br cdrskin dev=/dev/sr0 -v padsize=300k -multi 2.iso .br cdrskin dev=/dev/sr0 -v padsize=300k -multi 3.iso .br cdrskin dev=/dev/sr0 -v padsize=300k 4.iso .SS .B Get multi-session info for option -C of program mkisofs: .br c_values=$(cdrskin dev=/dev/hdc -msinfo 2>/dev/null) .br mkisofs ... -C "$c_values" ... .SS .B Inquire free space on media for a -multi run: .br x=$(cdrskin dev=/dev/sr0 -multi \\ .br --tell_media_space 2>/dev/null) .br echo "Available: $x blocks of 2048 data bytes" .SS .B Write audio tracks and CD-TEXT to CD: .br cdrskin -v dev=ATA:1,0,0 speed=48 -sao \\ .br input_sheet_v07t=cdtext.v07t \\ .br track1.wav track2.au -audio -swab track3.raw .SS .B Extract audio tracks and CD-TEXT from CD into directory /home/me/my_cd: .br mkdir /home/me/my_cd .br cdrskin -v dev=/dev/sr0 extract_audio_to=/home/me/my_cd \\ .br cdtext_to_v07t=/home/me/my_cd/cdtext.v07t .SH FILES .SS Startup files: .br If not --no_rc is given as the first argument then cdrskin attempts on startup to read the arguments from the following files: .PP .br .B /etc/default/cdrskin .br .B /etc/opt/cdrskin/rc .br .B /etc/cdrskin/cdrskin.conf .br .B $HOME/.cdrskinrc .br .PP The files are read in the sequence given above, but none of them is required for cdrskin to function properly. Each readable line is treated as one single argument. No extra blanks. A first character '#' marks a comment, empty lines are ignored. .br Example content of a startup file: .br # This is the default device .br dev=0,1,0 .br # Some more options .br fifo_start_at=0 .br fs=16m .br .SS Disabling superuser safety precautions: The superuser is normally banned from using any other emulated drive but /dev/null. This ban can be lifted by the existence of file .PP .B /root/cdrskin_permissions/allow_emulated_drives .PP where the directory must be owned by the superuser and must not offer w-permissions for group or others. .br Warning: Superusers must take care not to spoil their hard disk via its raw block device (like stdio:/dev/hda or stdio:/dev/sd0). .SH SEE ALSO .TP Formatting data track sources for cdrskin: .br .BR mkisofs (8), .BR genisoimage (8), .BR xorriso (1), .BR afio (1), .BR star (1) .br .TP Other CD/DVD/BD burn programs: .br .BR cdrecord (1), .BR wodim (1), .BR xorriso (1) .br .TP For DVD/BD burning (also tutor of libburn's DVD/BD capabilities): .br .BR growisofs (1) .br .SH AUTHOR cdrskin was written by Thomas Schmitt . .PP This manual page was started by George Danchev and is now maintained by Thomas Schmitt. /* cdrskin.c , Copyright 2006-2023 Thomas Schmitt Provided under GPL version 2 or later. A cdrecord compatible command line interface for libburn. This project is neither directed against original cdrecord nor does it exploit any source code of said program. It rather tries to be an alternative method to burn CD, DVD, or BD, which is not based on the same code as cdrecord. See also : http://scdbackup.sourceforge.net/cdrskin_eng.html Interested users of cdrecord are encouraged to contribute further option implementations as they need them. Contributions will get published under GPL but it is essential that the authors allow a future release under LGPL. There is a script test/cdrecord_spy.sh which may be installed between the cdrecord command and real cdrecord in order to learn about the options used by your favorite cdrecord frontend. Edit said script and install it according to the instructions given inside. The implementation of an option would probably consist of - necessary structure members for structs CdrpreskiN and/or CdrskiN - code in Cdrpreskin_setup() and Cdrskin_setup() which converts argv[i] into CdrpreskiN/CdrskiN members (or into direct actions) - removal of option from ignore list "ignored_partial_options" or "ignored_full_options" in Cdrskin_setup() - functions which implement the option's run time functionality - eventually calls of those functions in Cdrskin_run() - changes to be made within Cdrskin_burn() or Cdrskin_blank() or other existing methods See option blank= for an example. ------------------------------------------------------------------------------ For a more comprehensive example of the advised way to write an application of libburn see test/libburner.c . ------------------------------------------------------------------------------ This program is currently copyright Thomas Schmitt only. The copyrights of several components of libburnia-project.org are willfully tangled at toplevel to form an irrevocable commitment to true open source spirit. We have chosen the GPL for legal compatibility and clearly express that it shall not hamper the use of our software by non-GPL applications which show otherwise the due respect to the open source community. See toplevel README and cdrskin/README for that commitment. For a short time, this place showed a promise to release a BSD license on mere request. I have to retract that promise now, and replace it by the promise to make above commitment reality in a way that any BSD conformant usage in due open source spirit will be made possible somehow and in the particular special case. I will not raise public protest if you fork yourself a BSD license from an (outdated) cdrskin.c which still bears that old promise. Note that this extended commitment is valid only for cdrskin.[ch], cdrfifo.[ch] and cleanup.[ch], but not for libburnia-project.org as a whole. cdrskin is originally inspired by libburn-0.2/test/burniso.c : (c) Derek Foreman and Ben Jansens ------------------------------------------------------------------------------ Compilation within cdrskin-* : cd cdrskin cc -g -I.. -DCdrskin_build_timestamP='...' \ -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 \ -o cdrskin cdrskin.c cdrfifo.c cleanup.c \ -L../libburn/.libs -lburn -lpthread or cd .. cc -g -I. -DCdrskin_build_timestamP='...' \ -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 \ -o cdrskin/cdrskin cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cleanup.c \ libburn/async.o libburn/crc.o libburn/debug.o libburn/drive.o \ libburn/file.o libburn/init.o libburn/lec.o \ libburn/mmc.o libburn/options.o libburn/sbc.o libburn/sector.o \ libburn/sg.o libburn/spc.o libburn/source.o libburn/structure.o \ libburn/toc.o libburn/util.o libburn/write.o libburn/read.o \ libburn/libdax_audioxtr.o libburn/libdax_msgs.o \ -lpthread */ /** The official program version */ #ifndef Cdrskin_prog_versioN #define Cdrskin_prog_versioN "1.5.7" #endif /** The official libburn interface revision to use. (May get changed further below) */ #ifndef Cdrskin_libburn_majoR #define Cdrskin_libburn_majoR 1 #endif #ifndef Cdrskin_libburn_minoR #define Cdrskin_libburn_minoR 5 #endif #ifndef Cdrskin_libburn_micrO #define Cdrskin_libburn_micrO 6 #endif /** The source code release timestamp */ #include "cdrskin_timestamp.h" #ifndef Cdrskin_timestamP #define Cdrskin_timestamP "-none-given-" #endif /** The binary build timestamp is to be set externally by the compiler */ #ifndef Cdrskin_build_timestamP #define Cdrskin_build_timestamP "-none-given-" #endif #ifdef Cdrskin_libburn_versioN #undef Cdrskin_libburn_versioN #endif #ifdef Cdrskin_libburn_1_5_6 #define Cdrskin_libburn_versioN "1.5.6" #endif #ifdef Cdrskin_libburn_1_5_7 #define Cdrskin_libburn_versioN "1.5.7" #endif #ifndef Cdrskin_libburn_versioN #define Cdrskin_libburn_1_5_6 #define Cdrskin_libburn_versioN "1.5.6" #endif #ifdef Cdrskin_libburn_1_5_6 #undef Cdrskin_libburn_majoR #undef Cdrskin_libburn_minoR #undef Cdrskin_libburn_micrO #define Cdrskin_libburn_majoR 1 #define Cdrskin_libburn_minoR 5 #define Cdrskin_libburn_micrO 6 #endif #ifdef Cdrskin_libburn_1_5_7 #undef Cdrskin_libburn_majoR #undef Cdrskin_libburn_minoR #undef Cdrskin_libburn_micrO #define Cdrskin_libburn_majoR 1 #define Cdrskin_libburn_minoR 5 #define Cdrskin_libburn_micrO 7 #endif /* History of development macros. As of version 1.1.8 they are now unconditional, thus removing the option to compile a heavily restricted cdrskin for the old libburn at icculus.org. */ /* 0.2.2 */ /* Cdrskin_libburn_does_ejecT */ /* Cdrskin_libburn_has_drive_get_adR */ /* Cdrskin_progress_track_does_worK */ /* Cdrskin_is_erasable_on_load_does_worK */ /* Cdrskin_grab_abort_does_worK */ /* 0.2.4 */ /* Cdrskin_allow_libburn_taO */ /* Cdrskin_libburn_has_is_enumerablE */ /* Cdrskin_libburn_has_convert_fs_adR */ /* Cdrskin_libburn_has_convert_scsi_adR */ /* Cdrskin_libburn_has_burn_msgS */ /* Cdrskin_libburn_has_burn_aborT */ /* Cdrskin_libburn_has_cleanup_handleR */ /* Cdrskin_libburn_has_audioxtR */ /* Cdrskin_libburn_has_get_start_end_lbA */ /* Cdrskin_libburn_has_burn_disc_unsuitablE */ /* Cdrskin_libburn_has_read_atiP */ /* Cdrskin_libburn_has_buffer_progresS */ /* 0.2.6 */ /* Cdrskin_libburn_has_pretend_fulL */ /* Cdrskin_libburn_has_multI */ /* Cdrskin_libburn_has_buffer_min_filL */ /* 0.3.0 */ /* Cdrskin_atip_speed_is_oK */ /* Cdrskin_libburn_has_get_profilE */ /* Cdrskin_libburn_has_set_start_bytE */ /* Cdrskin_libburn_has_wrote_welL */ /* Cdrskin_libburn_has_bd_formattinG */ /* Cdrskin_libburn_has_burn_disc_formaT */ /* 0.3.2 */ /* Cdrskin_libburn_has_get_msc1 */ /* Cdrskin_libburn_has_toc_entry_extensionS */ /* Cdrskin_libburn_has_get_multi_capS */ /* 0.3.4 */ /* Cdrskin_libburn_has_set_filluP */ /* Cdrskin_libburn_has_get_spacE */ /* Cdrskin_libburn_write_mode_ruleS */ /* Cdrskin_libburn_has_allow_untested_profileS */ /* Cdrskin_libburn_has_set_forcE */ /* 0.3.6 */ /* Cdrskin_libburn_preset_device_familY */ /* Cdrskin_libburn_has_track_set_sizE */ /* 0.3.8 */ /* Cdrskin_libburn_has_set_waitinG */ /* Cdrskin_libburn_has_get_best_speeD */ /* 0.4.0 */ /* Cdrskin_libburn_has_random_access_rW */ /* Cdrskin_libburn_has_get_drive_rolE */ /* Cdrskin_libburn_has_drive_equals_adR */ /* 0.4.2 */ /* no novel libburn features but rather organizational changes */ /* 0.4.4 */ /* novel libburn features are transparent to cdrskin */ /* 0.4.6 */ /* Cdrskin_libburn_has_stream_recordinG */ /* 0.4.8 */ /* Bug fix release for write_start_address=... on DVD-RAM and BD-RE */ /* 0.5.0 , 0.5.2 , 0.5.4 , 0.5.6 , 0.5.8 , 0.6.0 , 0.6.2 */ /* novel libburn features are transparent to cdrskin */ /* 0.6.4 */ /* Ended to mark novelties by macros. libburnia libburn and cdrskin are fixely in sync now. icculus libburn did not move for 30 months. */ /* 1.1.8 */ /* The code which got enabled by novelty macros was made unconditional. */ #ifdef Cdrskin_new_api_tesT /* put macros under test caveat here */ #endif /* Cdrskin_new_api_tesT */ /** ts A90901 The raw write modes of libburn depend in part on code borrowed from cdrdao. Since this code is not understood by the current developers and since CDs written with cdrskin -raw96r seem unreadable anyway, -raw96r is given up for now. */ #define Cdrskin_disable_raw96R 1 /** A macro which is able to eat up a function call like printf() */ #ifdef Cdrskin_extra_leaN #define ClN(x) #define Cdrskin_no_cdrfifO 1 #else #define ClN(x) x #ifdef Cdrskin_use_libburn_fifO /* # define Cdrskin_no_cdrfifO 1 */ #endif #endif /** Verbosity level for pacifying progress messages */ #define Cdrskin_verbose_progresS 1 /** Verbosity level for command recognition and execution logging */ #define Cdrskin_verbose_cmD 2 /** Verbosity level for reporting of debugging messages */ #define Cdrskin_verbose_debuG 3 /** Verbosity level for fifo debugging */ #define Cdrskin_verbose_debug_fifO 4 #include #include #include #include #include #include #include #include #include #include #include "../libburn/libburn.h" #define Cleanup_set_handlers burn_set_signal_handling #define Cleanup_app_handler_T burn_abort_handler_t /* # define Cdrskin_use_libburn_cleanuP 1 */ /* May not use libburn cleanup with cdrskin fifo */ #ifndef Cdrskin_use_libburn_fifO #ifdef Cdrskin_use_libburn_cleanuP #undef Cdrskin_use_libburn_cleanuP #endif #endif #ifdef Cdrskin_use_libburn_cleanuP #define Cleanup_handler_funC NULL #define Cleanup_handler_handlE "cdrskin: " #define Cleanup_handler_flaG 48 #else #define Cleanup_handler_funC (Cleanup_app_handler_T) Cdrskin_abort_handler #define Cleanup_handler_handlE skin #define Cleanup_handler_flaG 4 #endif /* ! Cdrskin_use_libburn_cleanuP */ /* 0= no abort going on, -1= Cdrskin_abort_handler was called */ static int Cdrskin_abort_leveL= 0; /** The size of a string buffer for pathnames and similar texts */ #define Cdrskin_strleN 4096 /** The maximum length +1 of a drive address */ #define Cdrskin_adrleN BURN_DRIVE_ADR_LEN /** If tsize= sets a value smaller than media capacity divided by this number then there will be a warning and gracetime set at least to 15 */ #define Cdrskin_minimum_tsize_quotienT 2048.0 /* --------------------------------------------------------------------- */ /* Imported from scdbackup-0.8.5/src/cd_backup_planer.c */ /** Macro for creation of arrays of objects (or single objects) */ #define TSOB_FELD(typ,anz) (typ *) calloc(anz, sizeof(typ)); /** Convert a text so that eventual characters special to the shell are made literal. Note: this does not make a text terminal-safe ! @param in_text The text to be converted @param out_text The buffer for the result. It should have size >= strlen(in_text)*5+2 @param flag Unused yet @return For convenience out_text is returned */ char *Text_shellsafe(char *in_text, char *out_text, int flag) { int l,i,w=0; /* enclose everything by hard quotes */ l= strlen(in_text); out_text[w++]= '\''; for(i=0;i0) if(line[l-1]=='\r') line[--l]= 0; if(l>0) if(line[l-1]=='\n') line[--l]= 0; if(l>0) if(line[l-1]=='\r') line[--l]= 0; return(ret); } #ifndef Cdrskin_extra_leaN /** Destroy a synthetic argument array */ int Sfile_destroy_argv(int *argc, char ***argv, int flag) { int i; if(*argc>0 && *argv!=NULL){ for(i=0;i<*argc;i++){ if((*argv)[i]!=NULL) free((*argv)[i]); } free((char *) *argv); } *argc= 0; *argv= NULL; return(1); } /** Read a synthetic argument array from a list of files. @param progname The content for argv[0] @param filenames The paths of the filex from where to read @param filenamecount The number of paths in filenames @param argc Returns the number of read arguments (+1 for progname) @param argv Returns the array of synthetic arguments @param argidx Returns source file indice of argv[] items @param arglno Returns source file line numbers of argv[] items @param flag Bitfield for control purposes: bit0= read progname as first argument from line bit1= just release argument array argv and return bit2= tolerate failure to open file @return 1=ok , 0=cannot open file , -1=cannot create memory objects */ int Sfile_multi_read_argv(char *progname, char **filenames, int filename_count, int *argc, char ***argv, int **argidx, int **arglno, int flag) { int ret,i,pass,maxl=0,l,argcount=0,line_no; char buf[Cdrskin_strleN]; FILE *fp= NULL; Sfile_destroy_argv(argc,argv,0); if(flag&2) return(1); if((*argidx)!=NULL) free((char *) *argidx); if((*arglno)!=NULL) free((char *) *arglno); *argidx= *arglno= NULL; for(pass=0;pass<2;pass++) { if(!(flag&1)){ argcount= 1; if(pass==0) maxl= strlen(progname)+1; else { (*argv)[0]= (char *) calloc(1, strlen(progname)+1); if((*argv)[0]==NULL) {ret= -1; goto ex;} strcpy((*argv)[0],progname); } } else { argcount= 0; if(pass==0) maxl= 1; } for(i=0; imaxl) maxl= l; } else { if(argcount >= *argc) break; (*argv)[argcount]= (char *) calloc(1, l+1); if((*argv)[argcount]==NULL) {ret= -1; goto ex;} strcpy((*argv)[argcount],buf); (*argidx)[argcount]= i; (*arglno)[argcount]= line_no; } argcount++; } fclose(fp); fp= NULL; } if(pass==0){ *argc= argcount; if(argcount>0) { *argv= (char **) calloc(argcount, sizeof(char *)); *argidx= (int *) calloc(argcount, sizeof(int)); *arglno= (int *) calloc(argcount, sizeof(int)); if(*argv==NULL || *argidx==NULL || *arglno==NULL) {ret= -1; goto ex;} } for(i=0;i<*argc;i++) { (*argv)[i]= NULL; (*argidx)[i]= -1; (*arglno)[i]= -1; } } } ret= 1; ex:; if(fp!=NULL) fclose(fp); return(ret); } /** Combine environment variable HOME with given filename @param filename Address relative to $HOME @param fileadr Resulting combined address @param fa_size Size of array fileadr @param flag Unused yet @return 1=ok , 0=no HOME variable , -1=result address too long */ int Sfile_home_adr_s(char *filename, char *fileadr, int fa_size, int flag) { char *home; strcpy(fileadr,filename); home= getenv("HOME"); if(home==NULL) return(0); if((int) (strlen(home) + strlen(filename) + 1) >= fa_size) return(-1); strcpy(fileadr,home); if(filename[0]!=0){ strcat(fileadr,"/"); strcat(fileadr,filename); } return(1); } #endif /* ! Cdrskin_extra_leaN */ /* -------------------------- other misc functions ----------------------- */ /* Learned from reading growisofs.c , watching mkisofs, and viewing its results via od -c */ /* @return 0=no size found , 1=*size_in_bytes is valid */ int Scan_for_iso_size(unsigned char data[2048], double *size_in_bytes, int flag) { double sectors= 0.0; if(data[0]!=1) return(0); if(strncmp((char *) (data+1),"CD001",5)!=0) return(0); sectors= data[80] | (data[81]<<8) | (data[82]<<16) | (data[83]<<24); *size_in_bytes= sectors*2048.0; return(1); } int Set_descr_iso_size(unsigned char data[2048], double size_in_bytes, int flag) { unsigned int sectors, i; sectors= size_in_bytes/2048.0; if(size_in_bytes>((double) sectors) * 2048.0) sectors++; for(i=0;i<4;i++) data[87-i]= data[80+i]= (sectors >> (8*i)) & 0xff; return(1); } int Wait_for_input(int fd, int microsec, int flag) { struct timeval wt; fd_set rds,wts,exs; int ready; FD_ZERO(&rds); FD_ZERO(&wts); FD_ZERO(&exs); FD_SET(fd,&rds); FD_SET(fd,&exs); wt.tv_sec= microsec/1000000; wt.tv_usec= microsec%1000000; ready= select(fd+1,&rds,&wts,&exs,&wt); if(ready<=0) return(0); if(FD_ISSET(fd,&exs)) return(-1); if(FD_ISSET(fd,&rds)) return(1); return(0); } /* --------------------------------------------------------------------- */ /** Address translation table for users/applications which do not look for the output of -scanbus but guess a Bus,Target,Lun on their own. */ /** The maximum number of entries in the address translation table */ #define Cdradrtrn_leN 256 /** The address prefix which will prevent translation */ #define Cdrskin_no_transl_prefiX "LITERAL_ADR:" struct CdradrtrN { char *from_address[Cdradrtrn_leN]; char *to_address[Cdradrtrn_leN]; int fill_counter; }; #ifndef Cdrskin_extra_leaN /** Create a device address translator object */ int Cdradrtrn_new(struct CdradrtrN **trn, int flag) { struct CdradrtrN *o; int i; (*trn)= o= TSOB_FELD(struct CdradrtrN,1); if(o==NULL) return(-1); for(i= 0;ifrom_address[i]= NULL; o->to_address[i]= NULL; } o->fill_counter= 0; return(1); } /** Release from memory a device address translator object */ int Cdradrtrn_destroy(struct CdradrtrN **o, int flag) { int i; struct CdradrtrN *trn; trn= *o; if(trn==NULL) return(0); for(i= 0;ifill_counter;i++) { if(trn->from_address[i]!=NULL) free(trn->from_address[i]); if(trn->to_address[i]!=NULL) free(trn->to_address[i]); } free((char *) trn); *o= NULL; return(1); } /** Add a translation pair to the table @param trn The translator which shall learn @param from The user side address @param to The cdrskin side address @param flag Bitfield for control purposes: bit0= "from" contains from+to address, to[0] contains delimiter */ int Cdradrtrn_add(struct CdradrtrN *trn, char *from, char *to, int flag) { char buf[2*Cdrskin_adrleN+1],*from_pt,*to_pt; int cnt; cnt= trn->fill_counter; if(cnt>=Cdradrtrn_leN) return(-1); if(flag&1) { if(strlen(from)>=sizeof(buf)) return(0); strcpy(buf,from); to_pt= strchr(buf,to[0]); if(to_pt==NULL) return(0); *(to_pt)= 0; from_pt= buf; to_pt++; } else { from_pt= from; to_pt= to; } if(strlen(from)>=Cdrskin_adrleN || strlen(to)>=Cdrskin_adrleN) return(0); trn->from_address[cnt]= calloc(1, strlen(from_pt)+1); trn->to_address[cnt]= calloc(1, strlen(to_pt)+1); if(trn->from_address[cnt]==NULL || trn->to_address[cnt]==NULL) return(-2); strcpy(trn->from_address[cnt],from_pt); strcpy(trn->to_address[cnt],to_pt); trn->fill_counter++; return(1); } /** Apply eventual device address translation @param trn The translator @param from The address from which to translate @param driveno With backward translation only: The libburn drive number @param to The result of the translation @param flag Bitfield for control purposes: bit0= translate backward @return <=0 error, 1=no translation found, 2=translation found, 3=collision avoided */ int Cdradrtrn_translate(struct CdradrtrN *trn, char *from, int driveno, char to[Cdrskin_adrleN], int flag) { int i,ret= 1; char *adr; to[0]= 0; adr= from; if(flag&1) goto backward; if(strncmp(adr,Cdrskin_no_transl_prefiX, strlen(Cdrskin_no_transl_prefiX))==0) { adr= adr+strlen(Cdrskin_no_transl_prefiX); ret= 2; } else { for(i=0;ifill_counter;i++) if(strcmp(adr,trn->from_address[i])==0) break; if(ifill_counter) { adr= trn->to_address[i]; ret= 2; } } if(strlen(adr)>=Cdrskin_adrleN) return(-1); strcpy(to,adr); return(ret); backward:; if(strlen(from)>=Cdrskin_adrleN) sprintf(to,"%s%d",Cdrskin_no_transl_prefiX,driveno); else strcpy(to,from); for(i=0;ifill_counter;i++) if(strcmp(from,trn->to_address[i])==0 && strlen(trn->from_address[i])fill_counter) { ret= 2; strcpy(to,trn->from_address[i]); } else { for(i=0;ifill_counter;i++) if(strcmp(from,trn->from_address[i])==0) break; if(ifill_counter) if(strlen(from)+strlen(Cdrskin_no_transl_prefiX)boss= boss; o->trackno= trackno; o->source_path[0]= 0; o->original_source_path[0]= 0; o->source_fd= -1; o->is_from_stdin= !!(flag&2); o->fixed_size= 0.0; o->tao_to_sao_tsize= 0.0; o->padding= 0.0; o->set_by_padsize= 0; o->sector_pad_up= 1; o->track_type= BURN_MODE1; o->mode_modifiers= 0; o->sector_size= 2048.0; o->track_type_by_default= 1; o->swap_audio_bytes= 0; o->cdxa_conversion= 0; o->isrc[0]= 0; o->index_string= NULL; o->sao_pregap= -1; o->sao_postgap= -1; o->data_image_size= -1.0; o->iso_fs_descr= NULL; o->use_data_image_size= 0; o->extracting_container= 0; o->fifo_enabled= 0; o->fifo= NULL; o->fifo_outlet_fd= -1; o->fifo_size= 0; o->ff_fifo= NULL; o->ff_idx= -1; o->libburn_track= NULL; o->libburn_track_is_own= 0; #ifdef Cdrskin_use_libburn_fifO o->libburn_fifo= NULL; #endif /* Cdrskin_use_libburn_fifO */ if(flag & 1) return(1); ret= Cdrskin_get_source(boss,o->source_path,&(o->fixed_size), &(o->tao_to_sao_tsize),&(o->use_data_image_size), &(o->padding),&(o->set_by_padsize),&(skin_track_type), &(o->track_type_by_default), &(o->mode_modifiers), &(o->swap_audio_bytes), &(o->cdxa_conversion), 0); if(ret<=0) goto failed; strcpy(o->original_source_path,o->source_path); if(o->fixed_size>0.0) o->extracting_container= 1; Cdrtrack_set_track_type(o,skin_track_type,0); #ifndef Cdrskin_extra_leaN ret= Cdrskin_get_fifo_par(boss, &(o->fifo_enabled),&(o->fifo_size), &fifo_start_at,0); if(ret<=0) goto failed; #endif /* ! Cdrskin_extra_leaN */ return(1); failed:; Cdrtrack_destroy(track,0); return(-1); } /** Release from memory a track object previously created by Cdrtrack_new() */ int Cdrtrack_destroy(struct CdrtracK **o, int flag) { struct CdrtracK *track; track= *o; if(track==NULL) return(0); #ifndef Cdrskin_no_cdrfifO Cdrfifo_destroy(&(track->fifo),0); #endif if(track->libburn_track != NULL && track->libburn_track_is_own) burn_track_free(track->libburn_track); if(track->iso_fs_descr!=NULL) free((char *) track->iso_fs_descr); if(track->index_string != NULL) free(track->index_string); free((char *) track); *o= NULL; return(1); } int Cdrtrack_set_track_type(struct CdrtracK *o, int track_type, int flag) { if(track_type==BURN_AUDIO) { o->track_type= BURN_AUDIO; o->sector_size= 2352.0; } else { o->track_type= BURN_MODE1; o->sector_size= 2048.0; } return(1); } int Cdrtrack_get_track_type(struct CdrtracK *o, int *track_type, int *sector_size, int flag) { *track_type= o->track_type; *sector_size= o->sector_size; return(1); } /** @param flag Bitfield for control purposes: bit0= size returns number of actually processed source bytes rather than the predicted fixed_size (if available). padding returns the difference from number of written bytes. bit1= size returns fixed_size, padding returns tao_to_sao_tsize */ int Cdrtrack_get_size(struct CdrtracK *track, double *size, double *padding, double *sector_size, int *use_data_image_size, int flag) { off_t readcounter= 0,writecounter= 0; *size= track->fixed_size; *padding= track->padding; *use_data_image_size= track->use_data_image_size; if((flag&1) && track->libburn_track!=NULL) { burn_track_get_counters(track->libburn_track,&readcounter,&writecounter); *size= readcounter; *padding= writecounter-readcounter; } else if(flag&2) *padding= track->tao_to_sao_tsize; *sector_size= track->sector_size; return(1); } int Cdrtrack_get_iso_fs_descr(struct CdrtracK *track, char **descr, double *size, int flag) { *descr= track->iso_fs_descr; *size= track->data_image_size; return(*descr != NULL && *size > 0.0); } int Cdrtrack_get_source_path(struct CdrtracK *track, char **source_path, int *source_fd, int *is_from_stdin, int flag) { *source_path= track->original_source_path; *source_fd= track->source_fd; *is_from_stdin= track->is_from_stdin; return(1); } #ifdef Cdrskin_use_libburn_fifO int Cdrtrack_get_libburn_fifo(struct CdrtracK *track, struct burn_source **fifo, int flag) { *fifo= track->libburn_fifo; return(1); } int Cdrtrack_report_fifo(struct CdrtracK *track, int flag) { int size, free_bytes, ret; int total_min_fill, interval_min_fill, put_counter, get_counter; int empty_counter, full_counter; double fifo_percent; char *status_text; if(track->libburn_fifo == NULL) return(0); /* Check for open input or leftover bytes in liburn fifo */ ret = burn_fifo_inquire_status(track->libburn_fifo, &size, &free_bytes, &status_text); if(ret >= 0 && size - free_bytes > 1) { /* not clear why free_bytes is reduced by 1 */ fprintf(stderr, "cdrskin: FATAL : Fifo still contains data after burning has ended.\n"); fprintf(stderr, "cdrskin: FATAL : %d bytes left.\n", size - free_bytes - 1); fprintf(stderr, "cdrskin: FATAL : This indicates an overflow of the last track.\n"); fprintf(stderr, "cdrskin: NOTE : The media might appear ok but is probably truncated.\n"); return(-1); } burn_fifo_get_statistics(track->libburn_fifo, &total_min_fill, &interval_min_fill, &put_counter, &get_counter, &empty_counter, &full_counter); fifo_percent= 100.0*((double) total_min_fill)/(double) size; if(fifo_percent==0 && total_min_fill>0) fifo_percent= 1; fflush(stdout); fprintf(stderr,"Cdrskin: fifo had %d puts and %d gets.\n", put_counter,get_counter); fprintf(stderr, "Cdrskin: fifo was %d times empty and %d times full, min fill was %.f%%.\n", empty_counter, full_counter, fifo_percent); return(1); } #endif /* Cdrskin_use_libburn_fifO */ int Cdrtrack_get_fifo(struct CdrtracK *track, struct CdrfifO **fifo, int flag) { *fifo= track->fifo; return(1); } /** Try whether automatic audio extraction is appropriate and eventually open a file descriptor to the raw data. @return -3 identified as .wav but with cdrecord-inappropriate parameters -2 could not open track source, no use in retrying -1 severe error 0 not appropriate to extract, burn plain file content 1 to be extracted, *fd is a filedescriptor delivering raw data */ int Cdrtrack_extract_audio(struct CdrtracK *track, int *fd, off_t *xtr_size, int flag) { int l, ok= 0; struct libdax_audioxtr *xtr= NULL; char *fmt,*fmt_info; int num_channels,sample_rate,bits_per_sample,msb_first,ret; *fd= -1; if(track->track_type!=BURN_AUDIO && !track->track_type_by_default) return(0); l= strlen(track->source_path); if(l>=4) if(strcmp(track->source_path+l-4,".wav")==0) ok= 1; if(l>=3) if(strcmp(track->source_path+l-3,".au")==0) ok= 1; if(!ok) return(0); if(track->track_type_by_default) { Cdrtrack_set_track_type(track,BURN_AUDIO,0); track->track_type_by_default= 2; fprintf(stderr,"cdrskin: NOTE : Activated -audio for '%s'\n", track->source_path); } ret= libdax_audioxtr_new(&xtr,track->source_path,0); if(ret<=0) return(ret); libdax_audioxtr_get_id(xtr,&fmt,&fmt_info, &num_channels,&sample_rate,&bits_per_sample,&msb_first,0); if((strcmp(fmt,".wav")!=0 && strcmp(fmt,".au")!=0) || num_channels!=2 || sample_rate!=44100 || bits_per_sample!=16) { fprintf(stderr,"cdrskin: ( %s )\n",fmt_info); fprintf(stderr,"cdrskin: FATAL : Inappropriate audio coding in '%s'.\n", track->source_path); {ret= -3; goto ex;} } libdax_audioxtr_get_size(xtr,xtr_size,0); ret= libdax_audioxtr_detach_fd(xtr,fd,0); if(ret<=0) {ret= -1*!!ret; goto ex;} track->swap_audio_bytes= !!msb_first; track->extracting_container= 1; fprintf(stderr,"cdrskin: NOTE : %.f %saudio bytes in '%s'\n", (double) *xtr_size, (msb_first ? "" : "(-swab) "), track->source_path); ret= 1; ex: libdax_audioxtr_destroy(&xtr,0); return(ret); } /* @param flag bit0=set *size_used as the detected data image size */ int Cdrtrack_activate_image_size(struct CdrtracK *track, double *size_used, int flag) { if(flag&1) track->data_image_size= *size_used; else *size_used= track->data_image_size; if(track->use_data_image_size!=1) return(2); if(*size_used<=0) return(0); track->fixed_size= *size_used; track->use_data_image_size= 2; if(track->libburn_track!=NULL) burn_track_set_size(track->libburn_track, (off_t) *size_used); /* man cdrecord prescribes automatic -pad with -isosize. cdrskin obeys only if the current padding is less than that. */ if(track->padding<15*2048) { track->padding= 15*2048; track->set_by_padsize= 0; } track->extracting_container= 1; #ifndef Cdrskin_no_cdrfifO if(track->ff_fifo!=NULL) Cdrfifo_set_fd_in_limit(track->ff_fifo,track->fixed_size,track->ff_idx,0); #endif return(1); } int Cdrtrack_seek_isosize(struct CdrtracK *track, int fd, int flag) { struct stat stbuf; char secbuf[2048]; int ret,got,i; double size; if(fstat(fd,&stbuf)==-1) return(0); if((stbuf.st_mode&S_IFMT)!=S_IFREG && (stbuf.st_mode&S_IFMT)!=S_IFBLK) return(2); if(track->iso_fs_descr!=NULL) free((char *) track->iso_fs_descr); track->iso_fs_descr= TSOB_FELD(char,16*2048); if(track->iso_fs_descr==NULL) return(-1); for(i=0;i<32 && track->data_image_size<=0;i++) { for(got= 0; got<2048;got+= ret) { ret= read(fd, secbuf+got, 2048-got); if(ret<=0) return(0); } if(i<16) continue; memcpy(track->iso_fs_descr+(i-16)*2048,secbuf,2048); if(i>16) continue; ret= Scan_for_iso_size((unsigned char *) secbuf, &size, 0); if(ret<=0) break; track->data_image_size= size; if(track->use_data_image_size) { Cdrtrack_activate_image_size(track,&size,1); track->fixed_size= size; track->use_data_image_size= 2; } } ret= lseek(fd, (off_t) 0, SEEK_SET); if(ret!=0) { fprintf(stderr, "cdrskin: FATAL : Cannot lseek() to 0 after -isosize determination\n"); if(errno!=0) fprintf(stderr, "cdrskin: errno=%d : %s\n", errno, strerror(errno)); return(-1); } return(track->data_image_size>0); } /** Deliver an open file descriptor corresponding to the source path of track. @param flag Bitfield for control purposes: bit0=debugging verbosity bit1=open as source for direct write: no audio extract, no minimum track size bit2=permission to use burn_os_open_track_src() (evtl O_DIRECT) bit3=This is a CD. Enforce minimum track size if not bit1. @return <=0 error, 1 success */ int Cdrtrack_open_source_path(struct CdrtracK *track, int *fd, int flag) { int is_wav= 0, size_from_file= 0, ret, self_opened= 0; off_t xtr_size= 0; struct stat stbuf; char *device_adr,*raw_adr; int no_convert_fs_adr; int Cdrskin_get_device_adr(struct CdrskiN *skin, char **device_adr, char **raw_adr, int *no_convert_fs_adr,int flag); int Cdrskin_get_drive(struct CdrskiN *skin, struct burn_drive **drive, int flag); struct burn_drive *drive; if(track->source_path[0]=='-' && track->source_path[1]==0) *fd= 0; else if(track->source_path[0]=='#' && (track->source_path[1]>='0' && track->source_path[1]<='9')) *fd= atoi(track->source_path+1); else { *fd= -1; self_opened= 1; ret= Cdrskin_get_device_adr(track->boss,&device_adr,&raw_adr, &no_convert_fs_adr,0); if(ret <= 0) { fprintf(stderr, "cdrskin: FATAL : No drive found. Cannot prepare track.\n"); return(0); } /* fprintf(stderr, "cdrskin: DEBUG : device_adr='%s' , raw_adr='%s' , ncfs=%d\n", device_adr, raw_adr, no_convert_fs_adr); */ if(!no_convert_fs_adr) { if(flag&1) ClN(fprintf(stderr, "cdrskin_debug: checking track source for identity with drive\n")); ret= Cdrskin_get_drive(track->boss,&drive,0); if(ret<=0) { fprintf(stderr, "cdrskin: FATAL : Program error. Cannot determine libburn drive.\n"); return(0); } if(burn_drive_equals_adr(drive,track->source_path,2)>0) { fprintf(stderr, "cdrskin: FATAL : track source address leads to burner drive\n"); fprintf(stderr, "cdrskin: : dev='%s' -> '%s' <- track source '%s'\n", raw_adr, device_adr, track->source_path); return(0); } } /* fprintf(stderr,"cdrskin: EXPERIMENTAL : Deliberate abort\n"); return(0); */ if(!(flag&2)) is_wav= Cdrtrack_extract_audio(track,fd,&xtr_size,0); if(is_wav==-1) return(-1); if(is_wav==-3) return(0); if(is_wav==0) { if(track->track_type != BURN_MODE1 || (track->cdxa_conversion & 0x7fffffff)) flag&= ~4; /* Better avoid O_DIRECT with odd sectors */ if(flag & 4) *fd= burn_os_open_track_src(track->source_path, O_RDONLY, 0); else *fd= open(track->source_path, O_RDONLY); } if(*fd==-1) { fprintf(stderr,"cdrskin: failed to open source address '%s'\n", track->source_path); fprintf(stderr,"cdrskin: errno=%d , \"%s\"\n",errno, errno==0?"-no error code available-":strerror(errno)); return(0); } if(track->use_data_image_size==1 && xtr_size<=0) { ret= Cdrtrack_seek_isosize(track,*fd,0); if(ret == -1) { close(*fd); *fd= -1; return(-1); } } else if(track->fixed_size<=0) { /* >>> ??? is it intentional that tsize overrides .wav header ? */ if(xtr_size>0) { track->fixed_size= xtr_size; if(track->use_data_image_size==1) track->use_data_image_size= 2; /* count this as image size found */ size_from_file= 1; } else { if(fstat(*fd,&stbuf)!=-1) { if((stbuf.st_mode&S_IFMT)==S_IFREG) { track->fixed_size= stbuf.st_size; size_from_file= 1; } /* all other types are assumed of open ended size */ } } } } track->source_fd= *fd; if(track->fixed_size < Cdrtrack_minimum_sizE * track->sector_size && (track->fixed_size>0 || size_from_file) && (flag & 8) && !(flag&2)) { if(track->track_type == BURN_AUDIO) { /* >>> cdrecord: We differ in automatic padding with audio: Audio tracks must be at least 705600 bytes and a multiple of 2352. */ fprintf(stderr, "cdrskin: FATAL : Audio tracks must be at least %.f bytes\n", Cdrtrack_minimum_sizE*track->sector_size); return(0); } else { fprintf(stderr, "cdrskin: NOTE : Enforcing minimum track size of %.f bytes\n", Cdrtrack_minimum_sizE*track->sector_size); track->fixed_size= Cdrtrack_minimum_sizE*track->sector_size; } } return((*fd >= 0) * (1 + self_opened)); } #ifndef Cdrskin_no_cdrfifO /** Install a fifo object between data source and libburn. Its parameters are known to track. @param outlet_fd Returns the filedescriptor of the fifo outlet. @param previous_fifo Object address for chaining or follow-up attachment. @param flag Bitfield for control purposes: bit0= Debugging verbosity bit1= Do not create and attach a new fifo but attach new follow-up fd pair to previous_fifo bit2= Do not enforce fixed_size if not container extraction bit3= This is a CD. Enforce minimum track size. (Forward to Cdrtrack_open_source_path) @return <=0 error, 1 success */ int Cdrtrack_attach_fifo(struct CdrtracK *track, int *outlet_fd, struct CdrfifO *previous_fifo, int flag) { struct CdrfifO *ff; int source_fd,pipe_fds[2],ret; *outlet_fd= -1; if(track->fifo_size<=0) return(2); ret= Cdrtrack_open_source_path(track,&source_fd, (flag & (1 | 8)) | (4 * (track->fifo_size >= 256 * 1024))); if(ret<=0) return(ret); if(pipe(pipe_fds)==-1) return(0); Cdrfifo_destroy(&(track->fifo),0); if(flag&2) { ret= Cdrfifo_attach_follow_up_fds(previous_fifo,source_fd,pipe_fds[1],0); if(ret<=0) return(ret); track->ff_fifo= previous_fifo; track->ff_idx= ret-1; } else { /* >>> ??? obtain track sector size and use instead of 2048 ? */ ret= Cdrfifo_new(&ff,source_fd,pipe_fds[1],2048,track->fifo_size, flag & 1); if(ret<=0) return(ret); if(previous_fifo!=NULL) Cdrfifo_attach_peer(previous_fifo,ff,0); track->fifo= track->ff_fifo= ff; track->ff_idx= -1; } track->fifo_outlet_fd= pipe_fds[0]; if((track->extracting_container || !(flag&4)) && track->fixed_size>0) Cdrfifo_set_fd_in_limit(track->ff_fifo,track->fixed_size,track->ff_idx,0); if(flag&1) printf( "cdrskin_debug: track %d fifo replaced source_address '%s' by '#%d'\n", track->trackno+1,track->source_path,track->fifo_outlet_fd); sprintf(track->source_path,"#%d",track->fifo_outlet_fd); track->source_fd= track->fifo_outlet_fd; *outlet_fd= track->fifo_outlet_fd; return(1); } #endif /* ! Cdrskin_no_cdrfifO */ #ifndef Cdrskin_extra_leaN #ifdef Cdrskin_use_libburn_fifO /** Read data into the eventual libburn fifo until either fifo_start_at bytes are read (-1 = no limit), it is full or or the data source is exhausted. @return <=0 error, 1 success */ int Cdrtrack_fill_libburn_fifo(struct CdrtracK *track, int fifo_start_at, int flag) { int ret, bs= 32 * 1024; int buffer_size, buffer_free; double data_image_size; char buf[64 * 1024], *buffer_text; if(fifo_start_at == 0) return(2); if(track->libburn_fifo == NULL) return(2); if(fifo_start_at>0 && fifo_start_atfifo_size) printf( "cdrskin: NOTE : Input buffer will be initially filled up to %d bytes\n", fifo_start_at); printf("Waiting for reader process to fill input buffer ... "); fflush(stdout); ret= burn_fifo_fill(track->libburn_fifo, fifo_start_at, (fifo_start_at == -1)); if(ret < 0) return(0); /** Ticket 55: check fifos for input, throw error on 0-bytes from stdin @return <=0 abort run, 1 go on with burning */ ret= burn_fifo_inquire_status(track->libburn_fifo, &buffer_size, &buffer_free, &buffer_text); if(track->is_from_stdin) { if(ret<0 || buffer_size <= buffer_free) { fprintf(stderr,"\ncdrskin: FATAL : (First track) fifo did not read a single byte from stdin\n"); return(0); } } /* Try to obtain ISO 9660 Volume Descriptors and size from fifo. Not an error if there is no ISO 9660. */ if(track->iso_fs_descr != NULL) free(track->iso_fs_descr); track->iso_fs_descr = NULL; if(buffer_size - buffer_free >= 64 * 1024) { ret= burn_fifo_peek_data(track->libburn_fifo, buf, 64 * 1024, 0); if(ret == 1) { track->iso_fs_descr = calloc(1, bs); if(track->iso_fs_descr == NULL) return(-1); memcpy(track->iso_fs_descr, buf + bs, bs); ret= Scan_for_iso_size((unsigned char *) buf + bs, &data_image_size, 0); if(ret > 0) track->data_image_size= data_image_size; } } return(1); } #endif /* Cdrskin_use_libburn_fifO */ #ifdef Cdrskin_no_cdrfifO int Cdrtrack_fill_fifo(struct CdrtracK *track, int fifo_start_at, int flag) { return(Cdrtrack_fill_libburn_fifo(track, fifo_start_at, 0)); } #else /* Cdrskin_no_cdrfifO */ int Cdrtrack_fill_fifo(struct CdrtracK *track, int fifo_start_at, int flag) { int ret,buffer_fill,buffer_space; double data_image_size; if(fifo_start_at==0) return(2); if(track->fifo==NULL) { #ifdef Cdrskin_use_libburn_fifO ret= Cdrtrack_fill_libburn_fifo(track, fifo_start_at, 0); return(ret); #else return(2); #endif } if(fifo_start_at>0 && fifo_start_atfifo_size) printf( "cdrskin: NOTE : Input buffer will be initially filled up to %d bytes\n", fifo_start_at); printf("Waiting for reader process to fill input buffer ... "); fflush(stdout); ret= Cdrfifo_fill(track->fifo,fifo_start_at,0); if(ret<=0) return(ret); /** Ticket 55: check fifos for input, throw error on 0-bytes from stdin @return <=0 abort run, 1 go on with burning */ if(track->is_from_stdin) { ret= Cdrfifo_get_buffer_state(track->fifo,&buffer_fill,&buffer_space,0); if(ret<0 || buffer_fill<=0) { fprintf(stderr,"\ncdrskin: FATAL : (First track) fifo did not read a single byte from stdin\n"); return(0); } } ret= Cdrfifo_get_iso_fs_size(track->fifo,&data_image_size,0); if(ret>0) track->data_image_size= data_image_size; if(track->iso_fs_descr!=NULL) free((char *) track->iso_fs_descr); Cdrfifo_adopt_iso_fs_descr(track->fifo,&(track->iso_fs_descr),0); return(1); } #endif /* ! Cdrskin_no_cdrfifO */ #endif /* ! Cdrskin_extra_leaN */ int Cdrtrack_set_indice(struct CdrtracK *track, int flag) { int idx= 1, adr, prev_adr= -1, ret; char *cpt, *ept; if(track->sao_pregap >= 0) { ret= burn_track_set_pregap_size(track->libburn_track, track->sao_pregap, 0); if(ret <= 0) return(ret); } if(track->sao_postgap >= 0) { ret= burn_track_set_postgap_size(track->libburn_track, track->sao_postgap, 0); if(ret <= 0) return(ret); } if(track->index_string == NULL) return(2); for(ept= cpt= track->index_string; ept != NULL; cpt= ept + 1) { ept= strchr(cpt, ','); if(ept != NULL) *ept= 0; adr= -1; sscanf(cpt, "%d", &adr); if(adr < 0) { fprintf(stderr, "cdrskin: SORRY : Bad address number with index=\n"); return(0); } if(idx == 1 && adr != 0) { fprintf(stderr, "cdrskin: SORRY : First address number of index= is not 0\n"); return(0); } if(idx > 1 && adr < prev_adr) { fprintf(stderr, "cdrskin: SORRY : Backward address number with index=\n"); return(0); } ret= burn_track_set_index(track->libburn_track, idx, adr, 0); if(ret <= 0) return(ret); prev_adr= adr; if(ept != NULL) *ept= ','; idx++; } return(1); } /** Create a corresponding libburn track object and add it to the libburn session. This may change the trackno index set by Cdrtrack_new(). */ int Cdrtrack_add_to_session(struct CdrtracK *track, int trackno, struct burn_session *session, int flag) /* bit0= debugging verbosity bit1= apply padding hack (<<< should be unused for now) bit2= permission to use O_DIRECT (if enabled at compile time) bit3= This is a CD. (Forward to Cdrtrack_open_source_path) */ { struct burn_track *tr; struct burn_source *src= NULL; double padding,lib_padding; int ret,sector_pad_up; double fixed_size; int source_fd; #ifdef Cdrskin_use_libburn_fifO struct burn_source *fd_src= NULL; #endif track->trackno= trackno; tr= burn_track_create(); if(tr == NULL) {ret= -1; goto ex;} track->libburn_track= tr; track->libburn_track_is_own= 1; /* Note: track->track_type may get set in here */ if(track->source_fd==-1) { ret= Cdrtrack_open_source_path(track, &source_fd, flag & (8 | 4 | 1)); if(ret<=0) goto ex; } padding= 0.0; sector_pad_up= track->sector_pad_up; if(track->padding>0) { if(track->set_by_padsize || track->track_type!=BURN_AUDIO) padding= track->padding; else sector_pad_up= 1; } if(flag&2) lib_padding= 0.0; else lib_padding= padding; if(flag&1) { if(sector_pad_up) { ClN(fprintf(stderr,"cdrskin_debug: track %d telling burn_track_define_data() to pad up last sector\n",trackno+1)); } if(lib_padding>0 || !sector_pad_up) { ClN(fprintf(stderr, "cdrskin_debug: track %d telling burn_track_define_data() to pad %.f bytes\n", trackno+1,lib_padding)); } } burn_track_define_data(tr,0,(int) lib_padding,sector_pad_up, track->track_type | track->mode_modifiers); burn_track_set_default_size(tr, (off_t) track->tao_to_sao_tsize); burn_track_set_byte_swap(tr, (track->track_type==BURN_AUDIO && track->swap_audio_bytes)); if(!(track->cdxa_conversion & (1 << 31))) burn_track_set_cdxa_conv(tr, track->cdxa_conversion & 0x7fffffff); fixed_size= track->fixed_size; if((flag&2) && track->padding>0) { if(flag&1) ClN(fprintf(stderr,"cdrskin_debug: padding hack : %.f + %.f = %.f\n", track->fixed_size,track->padding, track->fixed_size+track->padding)); fixed_size+= track->padding; } src= burn_fd_source_new(track->source_fd,-1,(off_t) fixed_size); #ifdef Cdrskin_use_libburn_fifO if(src != NULL && track->fifo == NULL) { int fifo_enabled, fifo_size, fifo_start_at, chunksize, chunks; int Cdrskin_get_fifo_par(struct CdrskiN *skin, int *fifo_enabled, int *fifo_size, int *fifo_start_at, int flag); Cdrskin_get_fifo_par(track->boss, &fifo_enabled, &fifo_size, &fifo_start_at, 0); if(track->track_type == BURN_AUDIO) chunksize= 2352; else if (track->cdxa_conversion == 1) chunksize= 2056; else chunksize= 2048; chunks= fifo_size / chunksize; if(chunks > 1 && fifo_enabled) { fd_src= src; src= burn_fifo_source_new(fd_src, chunksize, chunks, (chunksize * chunks >= 128 * 1024)); if((flag & 1) || src == NULL) fprintf(stderr, "cdrskin_DEBUG: %s libburn fifo of %d bytes\n", src != NULL ? "installed" : "failed to install", chunksize * chunks); track->libburn_fifo= src; if(src == NULL) { src= fd_src; fd_src= NULL; } } } #endif /* Cdrskin_use_libburn_fifO */ if(src==NULL) { fprintf(stderr, "cdrskin: FATAL : Could not create libburn data source object\n"); {ret= 0; goto ex;} } if(burn_track_set_source(tr,src)!=BURN_SOURCE_OK) { fprintf(stderr,"cdrskin: FATAL : libburn rejects data source object\n"); {ret= 0; goto ex;} } ret= Cdrtrack_set_indice(track, 0); if(ret <= 0) goto ex; burn_session_add_track(session,tr,BURN_POS_END); ret= 1; ex: #ifdef Cdrskin_use_libburn_fifO if(fd_src!=NULL) burn_source_free(fd_src); #endif if(src!=NULL) burn_source_free(src); return(ret); } /** Release libburn track information after a session is done */ int Cdrtrack_cleanup(struct CdrtracK *track, int flag) { if(track->libburn_track==NULL) return(0); if(track->libburn_track_is_own) burn_track_free(track->libburn_track); track->libburn_track= NULL; return(1); } int Cdrtrack_ensure_padding(struct CdrtracK *track, int flag) /* flag: bit0= debugging verbosity */ { if(track->track_type!=BURN_AUDIO) return(2); if(flag&1) fprintf(stderr,"cdrskin_debug: enforcing -pad on last -audio track\n"); track->sector_pad_up= 1; return(1); } int Cdrtrack_get_sectors(struct CdrtracK *track, int flag) { return(burn_track_get_sectors(track->libburn_track)); } #ifndef Cdrskin_no_cdrfifO /** Try to read bytes from the track's fifo outlet and eventually discard them. Not to be called unless the track is completely written. */ int Cdrtrack_has_input_left(struct CdrtracK *track, int flag) { int ready,ret; char buf[2]; if(track->fifo_outlet_fd<=0) return(0); ready= Wait_for_input(track->fifo_outlet_fd, 0, 0); if(ready<=0) return(0); ret= read(track->fifo_outlet_fd,buf,1); if(ret>0) return(1); return(0); } #endif /* ! Cdrskin_no_cdrfifO */ /* --------------------------------------------------------------------- */ /** The list of startup file names */ #define Cdrpreskin_rc_nuM 4 static char Cdrpreskin_sys_rc_nameS[Cdrpreskin_rc_nuM][80]= { "/etc/default/cdrskin", "/etc/opt/cdrskin/rc", "/etc/cdrskin/cdrskin.conf", "placeholder for $HOME/.cdrskinrc" }; /** A structure which bundles several parameters for creation of the CdrskiN object. It finally becomes a managed subordinate of the CdrskiN object. */ struct CdrpreskiN { /* to be transferred into skin */ int verbosity; char queue_severity[81]; char print_severity[81]; /** Whether to wait for available standard input data before touching drives*/ int do_waiti; /** Stores eventually given absolute device address before translation */ char raw_device_adr[Cdrskin_adrleN]; /** Stores an eventually given translated absolute device address between Cdrpreskin_setup() and Cdrskin_create() . */ char device_adr[Cdrskin_adrleN]; /** The eventual address translation table */ struct CdradrtrN *adr_trn; /** Memorizes the abort handling mode from presetup to creation of control object. Defined handling modes are: 0= no abort handling 1= try to cancel, release, exit (leave signal mode as set by caller) 2= try to ignore all signals 3= mode 1 in normal operation, mode 2 during abort handling 4= mode 1 in normal operation, mode 0 during abort handling -1= install abort handling 1 only in Cdrskin_burn() after burning started */ int abort_handler; /** Whether to allow getuid()!=geteuid() */ int allow_setuid; /** Whether to allow user provided addresses like #4 */ int allow_fd_source; /** Whether to support media types which are implemented but yet untested */ int allow_untested_media; /** Whether to allow libburn pseudo-drives "stdio:" . 0=forbidden, 1=seems ok, 2=potentially forbidden (depends on uid, euid, file type) */ int allow_emulated_drives; /** Whether an option is given which needs a full bus scan */ int no_whitelist; /** Whether the translated device address shall not follow softlinks, device clones and SCSI addresses */ int no_convert_fs_adr; /** Whether Bus,Target,Lun addresses shall be converted literally as old Pseudo SCSI-Adresses. New default is to use (possibly system emulated) real SCSI addresses via burn_drive_convert_scsi_adr() and literally emulated and cdrecord-incompatible ATA: addresses. */ int old_pseudo_scsi_adr; /** Whether to omit bus scanning */ int do_not_scan; /** Whether bus scans shall exit!=0 if no drive was found */ int scan_demands_drive; /** Whether to abort when a busy drive is encountered during bus scan */ int abort_on_busy_drive; /** Linux specific : Whether to try to avoid collisions when opening drives */ int drive_exclusive; /** Linux specific : Whether to obtain an exclusive drive lock via fcntl() */ int drive_fcntl_f_setlk; /** Linux specific : Device file address family to use : 0=default , 1=sr , 2=scd , 4=sg */ int drive_scsi_dev_family; /** Whether to try to wait for unwilling drives to become willing to open */ int drive_blocking; /** Explicit write mode option is determined before skin processes any track arguments */ char write_mode_name[80]; #ifndef Cdrskin_extra_leaN /** List of startupfiles */ char rc_filenames[Cdrpreskin_rc_nuM][Cdrskin_strleN]; int rc_filename_count; /** Non-argument options from startupfiles */ int pre_argc; char **pre_argv; int *pre_argidx; int *pre_arglno; #endif /* ! Cdrskin_extra_leaN */ /* The eventual name of a program to be executed if demands_cdrecord_caps is >0 and demands_cdrskin_caps is <=0 */ char fallback_program[Cdrskin_strleN]; int demands_cdrecord_caps; int demands_cdrskin_caps; int result_fd; }; /** Create a preliminary cdrskin program run control object. It will become part of the final control object. @param preskin Returns pointer to resulting @param flag Bitfield for control purposes: unused yet @return <=0 error, 1 success */ int Cdrpreskin_new(struct CdrpreskiN **preskin, int flag) { struct CdrpreskiN *o; int i; (*preskin)= o= TSOB_FELD(struct CdrpreskiN,1); if(o==NULL) return(-1); o->verbosity= 0; strcpy(o->queue_severity,"NEVER"); strcpy(o->print_severity,"SORRY"); o->do_waiti= 0; o->raw_device_adr[0]= 0; o->device_adr[0]= 0; o->adr_trn= NULL; o->abort_handler= 3; o->allow_setuid= 0; o->allow_fd_source= 0; o->allow_untested_media= 0; o->allow_emulated_drives= 0; o->no_whitelist= 0; o->no_convert_fs_adr= 0; o->old_pseudo_scsi_adr= 0; o->do_not_scan= 0; o->scan_demands_drive= 0; o->abort_on_busy_drive= 0; o->drive_exclusive= 1; o->drive_fcntl_f_setlk= 1; o->drive_scsi_dev_family= 0; o->drive_blocking= 0; strcpy(o->write_mode_name,"DEFAULT"); #ifndef Cdrskin_extra_leaN o->rc_filename_count= Cdrpreskin_rc_nuM; for(i=0;irc_filename_count-1;i++) strcpy(o->rc_filenames[i],Cdrpreskin_sys_rc_nameS[i]); o->rc_filenames[o->rc_filename_count-1][0]= 0; o->pre_argc= 0; o->pre_argv= NULL; o->pre_argidx= NULL; o->pre_arglno= NULL; #endif /* ! Cdrskin_extra_leaN */ o->fallback_program[0]= 0; o->demands_cdrecord_caps= 0; o->demands_cdrskin_caps= 0; o->result_fd = -1; return(1); } int Cdrpreskin_destroy(struct CdrpreskiN **preskin, int flag) { struct CdrpreskiN *o; o= *preskin; if(o==NULL) return(0); #ifndef Cdrskin_extra_leaN if((o->pre_arglno)!=NULL) free((char *) o->pre_arglno); if((o->pre_argidx)!=NULL) free((char *) o->pre_argidx); if(o->pre_argc>0 && o->pre_argv!=NULL) Sfile_destroy_argv(&(o->pre_argc),&(o->pre_argv),0); Cdradrtrn_destroy(&(o->adr_trn),0); #endif /* ! Cdrskin_extra_leaN */ free((char *) o); *preskin= NULL; return(1); } int Cdrpreskin_set_severities(struct CdrpreskiN *preskin, char *queue_severity, char *print_severity, int flag) { if(queue_severity!=NULL) strcpy(preskin->queue_severity,queue_severity); if(print_severity!=NULL) strcpy(preskin->print_severity,print_severity); burn_msgs_set_severities(preskin->queue_severity, preskin->print_severity, "cdrskin: "); return(1); } int Cdrpreskin_initialize_lib(struct CdrpreskiN *preskin, int flag) { int ret, major, minor, micro; /* Needed are at least 44 bits in signed type off_t . This is a popular mistake in configuration or compilation. */ if(sizeof(off_t) < 6) { fprintf(stderr, "\ncdrskin: FATAL : Compile time misconfiguration. sizeof(off_t) too small.\n" ); return(0); } /* This is the minimum requirement of cdrskin towards the libburn header at compile time. It gets compared against the version macros in libburn/libburn.h : burn_header_version_major burn_header_version_minor burn_header_version_micro If the header is too old then the following code shall cause failure of cdrskin compilation rather than to allow production of a program with unpredictable bugs or memory corruption. The compiler message supposed to appear in this case is: error: 'LIBBURN_MISCONFIGURATION' undeclared (first use in this function) error: 'INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libburn_dot_h_TOO_OLD__SEE_cdrskin_dot_c' undeclared (first use in this function) error: 'LIBBURN_MISCONFIGURATION_' undeclared (first use in this function) */ /* The indendation is an advise of man gcc to help old compilers ignoring */ #if Cdrskin_libburn_majoR > burn_header_version_major #define Cdrskin_libburn_dot_h_too_olD 1 #endif #if Cdrskin_libburn_majoR == burn_header_version_major && Cdrskin_libburn_minoR > burn_header_version_minor #define Cdrskin_libburn_dot_h_too_olD 1 #endif #if Cdrskin_libburn_minoR == burn_header_version_minor && Cdrskin_libburn_micrO > burn_header_version_micro #define Cdrskin_libburn_dot_h_too_olD 1 #endif #ifdef Cdrskin_libburn_dot_h_too_olD LIBBURN_MISCONFIGURATION = 0; INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libburn_dot_h_TOO_OLD__SEE_cdrskin_dot_c = 0; LIBBURN_MISCONFIGURATION_ = 0; #endif ret= burn_initialize(); if(ret==0) { fprintf(stderr,"cdrskin: FATAL : Initialization of libburn failed\n"); return(0); } /* This is the runtime check towards eventual dynamically linked libburn. cdrskin deliberately does not to allow the library to be older than the header file which was seen at compile time. More liberal would be to use here Cdrskin_libburn_* instead of burn_header_version_* . */ burn_version(&major, &minor, µ); if(majorqueue_severity); strcpy(print_severity,o->print_severity); } if(o->verbosity>=Cdrskin_verbose_debuG) Cdrpreskin_set_severities(o,"DEBUG","NEVER",0); else Cdrpreskin_set_severities(o,"SORRY","NEVER",0); queueing= 1; return(1); } if(queueing) Cdrpreskin_set_severities(o,queue_severity,print_severity,0); queueing= 0; for(first= 1; ; first= 0) { ret= burn_msgs_obtain("ALL",&error_code,msg,&os_errno,msg_severity); if(ret==0) break; if(ret<0) { fprintf(stderr, "cdrskin: NOTE : Please inform libburn-hackers@pykix.org about:\n"); fprintf(stderr, "cdrskin: burn_msgs_obtain() returns %d\n",ret); break; } if(first) fprintf(stderr, "cdrskin: -------------------- Messages from Libburn ---------------------\n"); for(i=0;msg_severity[i]!=0;i++) filler[i]= ' '; filler[i]= 0; fprintf(stderr,"cdrskin: %s : %s\n",msg_severity,msg); if(strcmp(msg_severity,"DEBUG")!=0 && os_errno!=0) fprintf(stderr,"cdrskin: %s ( errno=%d '%s')\n", filler,os_errno,strerror(os_errno)); } if(first==0) fprintf(stderr, "cdrskin: ----------------------------------------------------------------\n"); #endif /* Cdrskin_debug_libdax_msgS */ return(1); } /** Evaluate whether the user would be allowed in any case to use device_adr as pseudo-drive */ int Cdrpreskin__allows_emulated_drives(char *device_adr, char reason[4096], int flag) { struct stat stbuf; reason[0]= 0; if(device_adr[0]) { if(strcmp(device_adr,"/dev/null")==0) return(1); strcat(reason,"File object is not /dev/null. "); } if(getuid()!=geteuid()) { strcat(reason,"UID and EUID differ"); return(0); } if(getuid()!=0) return(1); strcat(reason,"UID is 0. "); /* Directory must be owned by root and write protected against any others*/ if(lstat("/root/cdrskin_permissions",&stbuf)==-1 || !S_ISDIR(stbuf.st_mode)) { strcat(reason, "No directory /root/cdrskin_permissions exists"); return(0); } if(stbuf.st_uid!=0) { strcat(reason, "Directory /root/cdrskin_permissions not owned by UID 0"); return(0); } if(stbuf.st_mode & (S_IWGRP | S_IWOTH)) { strcat(reason, "Directory /root/cdrskin_permissions has w-permission for group or others"); return(0); } if(stat("/root/cdrskin_permissions/allow_emulated_drives",&stbuf)==-1) { strcat(reason, "No file /root/cdrskin_permissions/allow_emulated_drives exists"); return(0); } reason[0]= 0; return(1); } int Cdrpreskin_consider_normal_user(int flag) { fprintf(stderr, "cdrskin: HINT : Consider to allow rw-access to the writer devices and\n"); fprintf(stderr, "cdrskin: HINT : to run cdrskin under your normal user identity.\n"); return(1); } /* Start the fallback program as replacement of the cdrskin run. @param flag bit0=do not report start command */ int Cdrpreskin_fallback(struct CdrpreskiN *preskin, int argc, char **argv, int flag) { char **hargv= NULL; int i, wp= 1; char *ept, *upt; if(preskin->fallback_program[0] == 0) return(1); if(getuid()!=geteuid() && !preskin->allow_setuid) { fprintf(stderr, "cdrskin: SORRY : uid and euid differ. Will not start external fallback program.\n"); Cdrpreskin_consider_normal_user(0); fprintf(stderr, "cdrskin: HINT : Option --allow_setuid disables this safety check.\n"); goto failure; } if(!(flag&1)) { fprintf(stderr,"cdrskin: --------------------------------------------------------------------\n"); fprintf(stderr,"cdrskin: Starting fallback program:\n"); } hargv= TSOB_FELD(char *,argc+1); if(hargv==NULL) goto failure; hargv[0]= strdup(preskin->fallback_program); if(argv[0]==NULL) goto failure; if(!(flag&1)) fprintf(stderr," %s", hargv[0]); for(i= 1; ifallback_program); fprintf(stderr,"cdrskin: errno=%d \"%s\"\n", errno, (errno > 0 ? strerror(errno) : "unidentified error")); exit(15); } /** Convert a cdrecord-style device address into a libburn device address or into a libburn drive number. It depends on the "scsibus" number of the cdrecord-style address which kind of libburn address emerges: bus=0 : drive number , bus=1 : /dev/sgN , bus=2 : /dev/hdX (This call intentionally has no CdrpreskiN argument) @param flag Bitfield for control purposes: bit0= old_pseudo_scsi_adr @return 1 success, 0=no recognizable format, -1=severe error, -2 could not find scsi device, -3 address format error */ int Cdrpreskin__cdrecord_to_dev(char *adr, char device_adr[Cdrskin_adrleN], int *driveno, int flag) { int comma_seen= 0,digit_seen= 0,busno= 0,k,lun_no= -1; *driveno= -1; device_adr[0]= 0; if(strlen(adr)==0) return(0); if(strncmp(adr,"stdio:",6)==0) return(0); /* read the trailing numeric string as device address code */ /* accepts "1" , "0,1,0" , "ATA:0,1,0" , ... */ for(k= strlen(adr)-1;k>=0;k--) { if(adr[k]==',' && !comma_seen) { sscanf(adr+k+1,"%d",&lun_no); comma_seen= 1; digit_seen= 0; continue; } if(adr[k]<'0' || adr[k]>'9') break; digit_seen= 1; } if(!digit_seen) { k= strlen(adr)-1; if(adr[k]==':' || (adr[k]>='A' && adr[k]<='Z')) {/* empty prefix ? */ *driveno= 0; return(1); } return(0); } sscanf(adr+k+1,"%d",driveno); digit_seen= 0; if(k>0) if(adr[k]==',') { for(k--;k>=0;k--) { if(adr[k]<'0' || adr[k]>'9') break; digit_seen= 1; } if(digit_seen) { sscanf(adr+k+1,"%d",&busno); if(flag&1) { /* look for symbolic bus : 1=/dev/sgN 2=/dev/hdX */ if(busno==1) { sprintf(device_adr,"/dev/sg%d",*driveno); } else if(busno==2) { sprintf(device_adr,"/dev/hd%c",'a'+(*driveno)); } else if(busno!=0) { fprintf(stderr, "cdrskin: FATAL : dev=[Prefix:]Bus,Target,Lun expects Bus out of {0,1,2}\n"); return(-3); } } else { if(busno<0) { fprintf(stderr, "cdrskin: FATAL : dev=[Prefix:]Bus,Target,Lun expects Bus number >= 0\n"); return(-3); } if(busno>=1000) { busno-= 1000; goto ata_bus; } else if((strncmp(adr,"ATA",3)==0 && (adr[3]==0 || adr[3]==':')) || (strncmp(adr,"ATAPI",5)==0 && (adr[5]==0 || adr[5]==':'))) { ata_bus:; if(busno>12 || (*driveno)<0 || (*driveno)>1) { fprintf(stderr, "cdrskin: FATAL : dev=ATA:Bus,Target,Lun expects Bus {0..12}, Target {0,1}\n"); return(-3); } sprintf(device_adr,"/dev/hd%c",'a'+(2*busno)+(*driveno)); } else { int ret; ret= burn_drive_convert_scsi_adr(busno,-1,-1,*driveno,lun_no, device_adr); if(ret==0) { fprintf(stderr, "cdrskin: FATAL : Cannot find /dev/* with Bus,Target,Lun = %d,%d,%d\n", busno,*driveno,lun_no); fprintf(stderr, "cdrskin: HINT : This drive may be in use by another program currently\n"); return(-2); } else if(ret<0) return(-1); return(1); } } } } return(1); } /** Set the eventual output fd for the result of Cdrskin_msinfo() */ int Cdrpreskin_set_result_fd(struct CdrpreskiN *o, int result_fd, int flag) { o->result_fd= result_fd; return(1); } #ifndef Cdrskin_extra_leaN /** Load content startup files into preskin cache */ int Cdrpreskin_read_rc(struct CdrpreskiN *o, char *progname, int flag) { int ret,i; char **filenames_v; filenames_v= TSOB_FELD(char *, o->rc_filename_count+1); if(filenames_v==NULL) return(-1); for(i=0;irc_filename_count;i++) filenames_v[i]= o->rc_filenames[i]; Sfile_home_adr_s(".cdrskinrc",o->rc_filenames[o->rc_filename_count-1], Cdrskin_strleN,0); ret= Sfile_multi_read_argv(progname,filenames_v,o->rc_filename_count, &(o->pre_argc),&(o->pre_argv), &(o->pre_argidx),&(o->pre_arglno),4); free((char *) filenames_v); return(ret); } #endif /* ! Cdrskin_extra_leaN */ /** Interpret those arguments which do not need libburn or which influence the startup of libburn and/or the creation of the CdrskiN object. This is run before libburn gets initialized and before Cdrskin_new() is called. Options which need libburn or a CdrskiN object are processed in a different function named Cdrskin_setup(). @param flag Bitfield for control purposes: bit0= do not finalize setup bit1= do not read and interpret rc files @return <=0 error, 1 success , 2 end program run with exit value 0 */ int Cdrpreskin_setup(struct CdrpreskiN *o, int argc, char **argv, int flag) /* return: <=0 error 1 ok 2 end program run (--help) */ { int i,ret; char *value_pt, reason[4096], *argpt; #ifndef Cdrskin_extra_leaN if(argc>1) { if(strcmp(argv[1],"--no_rc")==0 || strcmp(argv[1],"-version")==0 || strcmp(argv[1],"--help")==0 || strcmp(argv[1],"-help")==0 || strncmp(argv[1], "textfile_to_v07t=", 17) == 0 || strncmp(argv[1], "-textfile_to_v07t=", 18) == 0) flag|= 2; } if(!(flag&2)) { ret= Cdrpreskin_read_rc(o,argv[0],0); if(ret<0) return(-1); if(o->pre_argc>1) { ret= Cdrpreskin_setup(o,o->pre_argc,o->pre_argv,flag|1|2); if(ret<=0) return(ret); /* ??? abort on ret==2 ? */ } } #endif if(argc==1) { fprintf(stderr,"cdrskin: SORRY : No options given. Try option --help\n"); return(0); } /* The two predefined fallback personalities are triggered by the progname */ value_pt= strrchr(argv[0],'/'); if(value_pt==NULL) value_pt= argv[0]; else value_pt++; if(strcmp(value_pt,"unicord")==0) strcpy(o->fallback_program,"cdrecord"); else if(strcmp(value_pt,"codim")==0) strcpy(o->fallback_program,"wodim"); for (i= 1;i 3) argpt++; if(strcmp(argv[i],"--abort_handler")==0) { o->abort_handler= 3; } else if(strcmp(argv[i],"--allow_emulated_drives")==0) { if(Cdrpreskin__allows_emulated_drives("",reason,0)<=0) { fprintf(stderr,"cdrskin: WARNING : %s.\n",reason); fprintf(stderr, "cdrskin: WARNING : Only /dev/null will be available with \"stdio:\".\n"); Cdrpreskin_consider_normal_user(0); o->allow_emulated_drives= 2; } else o->allow_emulated_drives= 1; } else if(strcmp(argv[i],"--allow_setuid")==0) { o->allow_setuid= 1; } else if(strcmp(argv[i],"--allow_untested_media")==0) { o->allow_untested_media= 1; } else if(strcmp(argpt, "blank=help") == 0 || strcmp(argpt, "-blank=help") == 0) { #ifndef Cdrskin_extra_leaN fprintf(stderr,"Blanking options:\n"); fprintf(stderr,"\tall\t\tblank the entire disk\n"); fprintf(stderr,"\tdisc\t\tblank the entire disk\n"); fprintf(stderr,"\tdisk\t\tblank the entire disk\n"); fprintf(stderr,"\tfast\t\tminimally blank the entire disk\n"); fprintf(stderr,"\tminimal\t\tminimally blank the entire disk\n"); fprintf(stderr, "\tas_needed\tblank or format media to make it ready for (re-)use\n"); fprintf(stderr, "\tdeformat_sequential\t\tfully blank, even formatted DVD-RW\n"); fprintf(stderr, "\tdeformat_sequential_quickest\tminimally blank, even DVD-RW\n"); fprintf(stderr, "\tformat_if_needed\t\tmake overwriteable if needed and possible\n"); fprintf(stderr, "\tformat_overwrite\t\tformat a DVD-RW to \"Restricted Overwrite\"\n"); fprintf(stderr, "\tformat_overwrite_quickest\tto \"Restricted Overwrite intermediate\"\n"); fprintf(stderr, "\tformat_overwrite_full\t\tfull-size format a DVD-RW or DVD+RW\n"); fprintf(stderr, "\tformat_defectmgt[_max|_min|_none]\tformat DVD-RAM or BD-R[E]\n"); fprintf(stderr, "\tformat_defectmgt[_cert_on|_cert_off]\tcertification slow|quick\n"); fprintf(stderr, "\tformat_defectmgt_payload_\tformat DVD-RAM or BD-R[E]\n"); fprintf(stderr, "\tformat_by_index_\t\tformat by index from --list_formats\n"); #else /* ! Cdrskin_extra_leaN */ goto see_cdrskin_eng_html; #endif /* ! Cdrskin_extra_leaN */ if(argc==2) {ret= 2; goto final_checks;} } else if(strcmp(argv[i],"--bragg_with_audio")==0) { /* OBSOLETE 0.2.3 */; } else if(strcmp(argv[i],"--demand_a_drive")==0) { o->scan_demands_drive= 1; o->demands_cdrskin_caps= 1; } else if(strcmp(argv[i],"--devices") == 0 || strcmp(argv[i],"--device_links") == 0) { o->no_whitelist= 1; o->demands_cdrskin_caps= 1; } else if(strncmp(argv[i],"dev_translation=",16)==0) { o->demands_cdrskin_caps= 1; #ifndef Cdrskin_extra_leaN if(o->adr_trn==NULL) { ret= Cdradrtrn_new(&(o->adr_trn),0); if(ret<=0) goto no_adr_trn_mem; } if(argv[i][16]==0) { fprintf(stderr, "cdrskin: FATAL : dev_translation= : missing separator character\n"); return(0); } ret= Cdradrtrn_add(o->adr_trn,argv[i]+17,argv[i]+16,1); if(ret==-2) { no_adr_trn_mem:; fprintf(stderr, "cdrskin: FATAL : address_translation= : cannot allocate memory\n"); } else if(ret==-1) fprintf(stderr, "cdrskin: FATAL : address_translation= : table full (%d items)\n", Cdradrtrn_leN); else if(ret==0) fprintf(stderr, "cdrskin: FATAL : address_translation= : no address separator '%c' found\n", argv[i][17]); if(ret<=0) return(0); #else /* ! Cdrskin_extra_leaN */ fprintf(stderr, "cdrskin: FATAL : dev_translation= is not available in lean version\n"); return(0); #endif /* Cdrskin_extra_leaN */ } else if(strncmp(argpt, "-dev=", 5) == 0) { value_pt= argpt + 5; goto set_dev; } else if(strncmp(argpt, "dev=", 4) == 0) { value_pt= argpt + 4; set_dev:; if(strcmp(value_pt,"help")==0) { #ifndef Cdrskin_extra_leaN printf("\nSupported SCSI transports for this platform:\n"); fflush(stdout); if(o->old_pseudo_scsi_adr) { fprintf(stderr,"\nTransport name:\t\tlibburn OLD_PSEUDO\n"); fprintf(stderr, "Transport descr.:\tBus0=DriveNum , Bus1=/dev/sgN , Bus2=/dev/hdX\n"); } else { fprintf(stderr,"\nTransport name:\t\tlibburn SCSI\n"); fprintf(stderr, "Transport descr.:\tSCSI Bus,Id,Lun as of operating system\n"); } fprintf(stderr,"Transp. layer ind.:\t\n"); fprintf(stderr,"Target specifier:\tbus,target,lun\n"); fprintf(stderr,"Target example:\t\t1,2,0\n"); fprintf(stderr,"SCSI Bus scanning:\tsupported\n"); fprintf(stderr,"Open via UNIX device:\tsupported\n"); if(!o->old_pseudo_scsi_adr) { fprintf(stderr,"\nTransport name:\t\tlibburn HD\n"); fprintf(stderr, "Transport descr.:\tLinux specific alias for /dev/hdX\n"); fprintf(stderr,"Transp. layer ind.:\tATA:\n"); fprintf(stderr,"Target specifier:\tbus,target,lun\n"); fprintf(stderr,"Target example:\t\tATA:1,0,0\n"); fprintf(stderr,"SCSI Bus scanning:\tsupported\n"); fprintf(stderr,"Open via UNIX device:\tsupported\n"); } if(o->allow_emulated_drives) { fprintf(stderr,"\nTransport name:\t\tlibburn on standard i/o\n"); if(o->allow_emulated_drives==2) fprintf(stderr, "Transport descr.:\troot or setuid may only write into /dev/null\n"); else fprintf(stderr, "Transport descr.:\twrite into file objects\n"); fprintf(stderr,"Transp. layer ind.:\tstdio:\n"); fprintf(stderr,"Target specifier:\tpath\n"); fprintf(stderr,"Target example:\t\tstdio:/tmp/pseudo_drive\n"); fprintf(stderr,"SCSI Bus scanning:\tnot supported\n"); fprintf(stderr,"Open via UNIX device:\tnot supported\n"); } else { if(Cdrpreskin__allows_emulated_drives("",reason,0)>0) printf("\ncdrskin: NOTE : Option --allow_emulated_drives would allow dev=stdio:\n"); } #else /* ! Cdrskin_extra_leaN */ goto see_cdrskin_eng_html; #endif /* Cdrskin_extra_leaN */ {ret= 2; goto final_checks;} } if(strlen(value_pt)>=sizeof(o->raw_device_adr)) goto dev_too_long; strcpy(o->raw_device_adr,value_pt); } else if(strcmp(argv[i],"--drive_abort_on_busy")==0) { o->abort_on_busy_drive= 1; } else if(strcmp(argv[i],"--drive_blocking")==0) { o->drive_blocking= 1; } else if(strcmp(argv[i],"--drive_f_setlk")==0) { o->drive_fcntl_f_setlk= 1; } else if(strcmp(argv[i],"--drive_not_exclusive")==0) { o->drive_exclusive= 0; o->drive_fcntl_f_setlk= 0; } else if(strcmp(argv[i],"--drive_not_f_setlk")==0) { o->drive_fcntl_f_setlk= 0; } else if(strcmp(argv[i],"--drive_not_o_excl")==0) { o->drive_exclusive= 0; } else if(strncmp(argv[i],"drive_scsi_dev_family=",22)==0) { value_pt= argv[i]+22; if(strcmp(value_pt,"sr")==0) o->drive_scsi_dev_family= 1; else if(strcmp(value_pt,"scd")==0) o->drive_scsi_dev_family= 2; else if(strcmp(value_pt,"sg")==0) o->drive_scsi_dev_family= 4; else o->drive_scsi_dev_family= 0; } else if(strcmp(argv[i],"--drive_scsi_exclusive")==0) { o->drive_exclusive= 2; o->demands_cdrskin_caps= 1; } else if(strcmp(argpt,"driveropts=help")==0 || strcmp(argpt,"-driveropts=help")==0) { #ifndef Cdrskin_extra_leaN fprintf(stderr,"Driver options:\n"); fprintf(stderr,"burnfree\tPrepare writer to use BURN-Free technology\n"); fprintf(stderr,"noburnfree\tDisable using BURN-Free technology\n"); #else /* ! Cdrskin_extra_leaN */ goto see_cdrskin_eng_html; #endif /* Cdrskin_extra_leaN */ if(argc==2 || (i==2 && argc==3 && strncmp(argv[1],"dev=",4)==0)) {ret= 2; goto final_checks;} } else if(strcmp(argv[i],"--help")==0) { #ifndef Cdrskin_extra_leaN printf("\n"); printf("Usage: %s [options|source_addresses]\n", argv[0]); printf("Burns preformatted data to CD or DVD via libburn.\n"); printf("For the cdrecord compatible options which control the work of\n"); printf( "blanking and burning see output of option -help rather than --help.\n"); printf("Non-cdrecord options:\n"); printf(" --abort_handler do not leave the drive in busy state\n"); printf( " --adjust_speed_to_drive set only speeds offered by drive and media\n"); printf(" --allow_emulated_drives dev=stdio: on file objects\n"); printf( " --allow_setuid disable setuid warning (setuid is insecure !)\n"); printf( " --allow_untested_media enable implemented untested media types\n"); printf( " --any_track allow source_addresses to match '^-.' or '='\n"); printf( " assert_write_lba= abort if not next write address == lba\n"); printf( " cd_start_tno= set number of first track in CD SAO session\n"); printf( " --cdtext_dummy show CD-TEXT pack array instead of writing CD\n"); printf( " cdtext_to_textfile= extract CD-TEXT from CD to disk file\n"); printf( " cdtext_to_v07t= report CD-TEXT from CD to file or stdout\n"); printf(" --cdtext_verbose show CD-TEXT pack array before writing CD\n"); printf( " direct_write_amount= write random access to media like DVD+RW\n"); printf(" --demand_a_drive exit !=0 on bus scans with empty result\n"); printf(" --devices list accessible devices (tells /dev/...)\n"); printf(" --device_links list accessible devices by (udev) links\n"); printf( " dev_translation= set input address alias\n"); printf(" e.g.: dev_translation=+ATA:1,0,0+/dev/sg1\n"); printf(" --drive_abort_on_busy abort process if busy drive is found\n"); printf(" (might be triggered by a busy hard disk)\n"); printf(" --drive_blocking try to wait for busy drive to become free\n"); printf(" (might be stalled by a busy hard disk)\n"); printf(" --drive_f_setlk obtain exclusive lock via fcntl.\n"); printf(" --drive_not_exclusive combined not_o_excl and not_f_setlk.\n"); printf(" --drive_not_f_setlk do not obtain exclusive lock via fcntl.\n"); printf(" --drive_not_o_excl do not ask kernel to prevent opening\n"); printf(" busy drives. Effect is kernel dependent.\n"); printf( " drive_scsi_dev_family= select Linux device\n"); printf(" file family to be used for (pseudo-)SCSI.\n"); printf( " --drive_scsi_exclusive try to exclusively reserve device files\n"); printf(" /dev/srN, /dev/scdM, /dev/stK of drive.\n"); printf(" dvd_obs=\"default\"|number\n"); printf( " set number of bytes per DVD/BD write: 32k or 64k\n"); printf(" extract_audio_to=\n"); printf( " Copy all tracks of an audio CD as separate\n"); printf( " .WAV files /track.wav to hard disk\n"); printf(" extract_basename=\n"); printf( " Compose track files as /.wav\n"); printf( " --extract_dap Enable flaw obscuring mechanisms for audio reading\n"); printf(" extract_tracks=\n"); printf( " Restrict extract_audio_to= to list of tracks.\n"); printf( " fallback_program= use external program for exotic CD jobs.\n"); printf(" --fifo_disable disable fifo despite any fs=...\n"); printf(" --fifo_per_track use a separate fifo for each track\n"); printf( " fifo_start_at= do not wait for full fifo but start burning\n"); printf( " as soon as the given number of bytes is read\n"); printf(" --fill_up_media cause the last track to have maximum size\n"); printf(" --four_channel indicate that audio tracks have 4 channels\n"); printf( " grab_drive_and_wait= grab drive, wait given number of\n"); printf( " seconds, release drive, and do normal work\n"); printf( " --grow_overwriteable_iso emulate multi-session on media like DVD+RW\n"); printf( " --ignore_signals try to ignore any signals rather than to abort\n"); printf(" input_sheet_v07t= read a Sony CD-TEXT definition file\n"); printf(" --list_formats list format descriptors for loaded media.\n"); printf(" --list_ignored_options list all ignored cdrecord options.\n"); printf(" --list_speeds list speed descriptors for loaded media.\n"); printf(" --list_features list SCSI features reported by the drive\n"); printf(" --long_toc print overview of media content\n"); printf(" modesty_on_drive= no writing into full drive buffer\n"); printf(" --no_abort_handler exit even if the drive is in busy state\n"); printf(" --no_blank_appendable refuse to blank appendable CD-RW\n"); printf(" --no_convert_fs_adr only literal translations of dev=\n"); printf(" --no_load do not try to load the drive tray\n"); printf( " --no_rc as first argument: do not read startup files\n"); printf(" --obs_pad pad DVD DAO to full 16 or 32 blocks\n"); printf( " --bdr_obs_exempt possibly exempt BD-R from padding to full 64k\n"); printf(" --old_pseudo_scsi_adr use and report literal Bus,Target,Lun\n"); printf(" rather than real SCSI and pseudo ATA.\n"); printf( " --pacifier_with_newline do not overwrite pacifier line by next one.\n"); printf(" --prodvd_cli_compatible react on some DVD types more like\n"); printf(" cdrecord-ProDVD with blank= and -multi\n"); printf(" sao_postgap=\"off\"|number\n"); printf(" defines whether the next track will get a\n"); printf( " post-gap and how many sectors it shall have\n"); printf(" sao_pregap=\"off\"|number\n"); printf(" defines whether the next track will get a\n"); printf( " pre-gap and how many sectors it shall have\n"); printf( " --single_track accept only last argument as source_address\n"); printf(" stream_recording=\"on\"|\"off\"|number\n"); printf( " \"on\" requests to prefer speed over write\n"); printf( " error management. A number prevents this with\n"); printf( " byte addresses below that number.\n"); printf(" stdio_sync=\"default\"|\"off\"|number\n"); printf( " set number of bytes after which to force output\n"); printf( " to drives with prefix \"stdio:\".\n"); printf( " tao_to_sao_tsize= use num as fixed track size if in a\n"); printf( " non-TAO mode track data are read from \"-\"\n"); printf( " and no tsize= was specified.\n"); printf( " --tell_media_space print maximum number of writeable data blocks\n"); printf(" textfile_to_v07t=file_path\n"); printf( " print CD-TEXT file in Sony format and exit.\n"); printf(" --two_channel indicate that audio tracks have 2 channels\n"); printf( " use_immed_bit=on|off|default real control over SCSI Immed bit\n"); printf( " write_start_address= write to given byte address (DVD+RW)\n"); printf( " --xa1-ignore with -xa1 do not strip 8 byte headers\n"); printf( " -xamix DO NOT USE (Dummy announcement to make K3B use -xa)\n"); printf( "Preconfigured arguments are read from the following startup files\n"); printf( "if they exist and are readable. The sequence is as listed here:\n"); printf(" /etc/default/cdrskin /etc/opt/cdrskin/rc\n"); printf(" /etc/cdrskin/cdrskin.conf $HOME/.cdrskinrc\n"); printf("Each file line is a single argument. No whitespace.\n"); printf( "By default any argument that does not match grep '^-.' or '=' is\n"); printf( "used as track source. If it is \"-\" then stdin is used.\n"); printf("cdrskin : http://scdbackup.sourceforge.net/cdrskin_eng.html\n"); printf(" mailto:scdbackup@gmx.net (Thomas Schmitt)\n"); printf("libburn : http://libburnia-project.org\n"); printf("cdrecord : ftp://ftp.berlios.de/pub/cdrecord/\n"); printf("My respect to the authors of cdrecord and libburn.\n"); printf("scdbackup: http://scdbackup.sourceforge.net/main_eng.html\n"); printf("\n"); #else /* ! Cdrskin_extra_leaN */ see_cdrskin_eng_html:; printf("This is a capability reduced lean version without help texts.\n"); printf("See http://scdbackup.sourceforge.net/cdrskin_eng.html\n"); #endif /* Cdrskin_extra_leaN */ {ret= 2; goto final_checks;} } else if(strcmp(argv[i],"-help")==0) { #ifndef Cdrskin_extra_leaN fprintf(stderr,"Usage: %s [options|source_addresses]\n",argv[0]); fprintf(stderr,"Note: This is not cdrecord. See cdrskin start message on stdout. See --help.\n"); fprintf(stderr,"Options:\n"); fprintf(stderr,"\t-version\tprint version information and exit\n"); fprintf(stderr, "\tdev=target\tpseudo-SCSI target to use as CD-Recorder\n"); fprintf(stderr, "\tgracetime=#\tset the grace time before starting to write to #.\n"); fprintf(stderr,"\t-v\t\tincrement general verbose level by one\n"); fprintf(stderr, "\t-V\t\tincrement SCSI command transport verbose level by one\n"); fprintf(stderr, "\tdriveropts=opt\topt= one of {burnfree,noburnfree,help}\n"); fprintf(stderr, "\t-checkdrive\tcheck if a driver for the drive is present\n"); fprintf(stderr,"\t-inq\t\tdo an inquiry for the drive and exit\n"); fprintf(stderr,"\t-scanbus\tscan the SCSI bus and exit\n"); fprintf(stderr,"\tspeed=#\t\tset speed of drive\n"); fprintf(stderr,"\tblank=type\tblank a CD-RW disc (see blank=help)\n"); fprintf(stderr,"\t-format\t\tformat a CD-RW/DVD-RW/DVD+RW disc\n"); fprintf(stderr, "\tfs=#\t\tSet fifo size to # (0 to disable, default is 4 MB)\n"); fprintf(stderr, "\t-load\t\tload the disk and exit (works only with tray loader)\n"); fprintf(stderr, "\t-lock\t\tload and lock the disk and exit (works only with tray loader)\n"); fprintf(stderr, "\t-eject\t\teject the disk after doing the work\n"); fprintf(stderr,"\t-dummy\t\tdo everything with laser turned off\n"); fprintf(stderr,"\t-minfo\t\tretrieve and print media information/status\n"); fprintf(stderr,"\t-media-info\tretrieve and print media information/status\n"); fprintf(stderr, "\t-msinfo\t\tretrieve multi-session info for mkisofs >= 1.10\n"); fprintf(stderr,"\tmsifile=path\trun -msinfo and copy output to file\n"); fprintf(stderr,"\t-toc\t\tretrieve and print TOC/PMA data\n"); fprintf(stderr, "\t-atip\t\tretrieve media state, print \"Is *erasable\"\n"); fprintf(stderr, "\tminbuf=percent\tset lower limit for drive buffer modesty\n"); fprintf(stderr, "\t-multi\t\tgenerate a TOC that allows multi session\n"); fprintf(stderr, "\t-waiti\t\twait until input is available before opening SCSI\n"); fprintf(stderr, "\t-immed\t\tTry to use the SCSI IMMED flag with certain long lasting commands\n"); fprintf(stderr, "\t-force\t\tforce to continue on some errors to allow blanking\n"); fprintf(stderr,"\t-tao\t\tWrite disk in TAO mode.\n"); fprintf(stderr,"\t-dao\t\tWrite disk in SAO mode.\n"); fprintf(stderr,"\t-sao\t\tWrite disk in SAO mode.\n"); #ifndef Cdrskin_disable_raw96R fprintf(stderr,"\t-raw96r\t\tWrite disk in RAW/RAW96R mode\n"); #endif fprintf(stderr,"\ttsize=#\t\tannounces exact size of source data\n"); fprintf(stderr,"\tpadsize=#\tAmount of padding\n"); fprintf(stderr, "\tmcn=text\tSet the media catalog number for this CD to 'text'\n"); fprintf(stderr, "\tisrc=text\tSet the ISRC number for the next track to 'text'\n"); fprintf(stderr, "\tindex=list\tSet the index list for the next track to 'list'\n"); fprintf(stderr, "\t-text\t\tWrite CD-Text from information from *.cue files\n"); fprintf(stderr, "\ttextfile=name\tSet the file with CD-Text data to 'name'\n"); fprintf(stderr, "\tcuefile=name\tSet the file with CDRWIN CUE data to 'name'\n"); fprintf(stderr,"\t-audio\t\tSubsequent tracks are CD-DA audio tracks\n"); fprintf(stderr, "\t-data\t\tSubsequent tracks are CD-ROM data mode 1 (default)\n"); fprintf(stderr, "\t-xa1\t\tSubsequent tracks are CD-ROM XA mode 2 form 1 - 2056 bytes\n"); fprintf(stderr, "\t-isosize\tUse iso9660 file system size for next data track\n"); fprintf(stderr,"\t-preemp\t\tAudio tracks are mastered with 50/15 microseconds preemphasis\n"); fprintf(stderr,"\t-nopreemp\tAudio tracks are mastered with no preemphasis (default)\n"); fprintf(stderr,"\t-copy\t\tAudio tracks have unlimited copy permission\n"); fprintf(stderr,"\t-nocopy\t\tAudio tracks may only be copied once for personal use (default)\n"); fprintf(stderr,"\t-scms\t\tAudio tracks will not have any copy permission at all\n"); fprintf(stderr,"\t-pad\t\tpadsize=30k\n"); fprintf(stderr, "\t-nopad\t\tDo not pad (default, but applies only to data tracks)\n"); fprintf(stderr, "\t-swab\t\tAudio data source is byte-swapped (little-endian/Intel)\n"); fprintf(stderr,"\t-help\t\tprint this text to stderr and exit\n"); fprintf(stderr, "Without option -data, .wav and .au files are extracted and burned as -audio.\n"); fprintf(stderr, "By default any argument that does not match grep '^-.' or '=' is used\n"); fprintf(stderr, "as track source address. Address \"-\" means stdin.\n"); fprintf(stderr, "cdrskin will ensure that an announced tsize= is written even if\n"); fprintf(stderr, "the source delivers fewer bytes. But 0 bytes from stdin with fifo\n"); fprintf(stderr, "enabled will lead to abort and no burn attempt at all.\n"); #else /* ! Cdrskin_extra_leaN */ fprintf(stderr,"Note: This is not cdrecord. See cdrskin start message on stdout.\n"); fprintf(stderr, "(writer profile: -atip retrieve, blank=type, -eject after work)\n"); goto see_cdrskin_eng_html; #endif /* Cdrskin_extra_leaN */ {ret= 2; goto final_checks;} } else if(strcmp(argv[i],"--ignore_signals")==0) { o->abort_handler= 2; } else if(strncmp(argv[i],"fallback_program=",17)==0) { if(strlen(argv[i] + 17) >= sizeof(o->fallback_program)) { fprintf(stderr, "cdrskin: FATAL : fallback_program=... too long (max. %d characters)\n", (int) sizeof(o->fallback_program) - 1); {ret= 0; goto ex;} } strcpy(o->fallback_program,argv[i]+17); } else if(strcmp(argv[i],"--no_abort_handler")==0) { o->abort_handler= 0; } else if(strcmp(argv[i],"--no_convert_fs_adr")==0) { o->no_convert_fs_adr= 1; } else if(strcmp(argv[i],"--old_pseudo_scsi_adr")==0) { o->old_pseudo_scsi_adr= 1; o->demands_cdrskin_caps= 1; } else if(strcmp(argv[i],"--no_rc")==0) { if(i!=1) fprintf(stderr, "cdrskin: NOTE : option --no_rc would only work as first argument.\n"); #ifndef Cdrskin_disable_raw96R } else if(strcmp(argpt,"-raw96r")==0) { strcpy(o->write_mode_name,"RAW/RAW96R"); #endif } else if(strcmp(argpt,"-sao")==0 || strcmp(argpt,"-dao")==0) { strcpy(o->write_mode_name,"SAO"); } else if(strcmp(argpt,"-scanbus")==0) { o->no_whitelist= 1; } else if(strcmp(argpt,"-tao")==0) { strcpy(o->write_mode_name,"TAO"); } else if(strncmp(argpt, "-textfile_to_v07t=", 18) == 0) { o->do_not_scan= 1; } else if(strncmp(argpt ,"textfile_to_v07t=", 17) == 0) { o->do_not_scan= 1; } else if(strcmp(argv[i],"-V")==0 || strcmp(argpt, "-Verbose") == 0 || strcmp(argv[i],"-VV")==0 || strcmp(argpt, "-VVV") == 0) { burn_set_scsi_logging(2 | 4); /* log SCSI to stderr */ } else if(strcmp(argv[i],"-v")==0 || strcmp(argpt, "-verbose") == 0) { (o->verbosity)++; ClN(printf("cdrskin: verbosity level : %d\n",o->verbosity)); set_severities:; if(o->verbosity>=Cdrskin_verbose_debuG) Cdrpreskin_set_severities(o,"NEVER","DEBUG",0); else if(o->verbosity >= Cdrskin_verbose_progresS) Cdrpreskin_set_severities(o, "NEVER", "UPDATE", 0); } else if(strcmp(argv[i],"-vv")==0 || strcmp(argv[i],"-vvv")==0 || strcmp(argv[i],"-vvvv")==0) { (o->verbosity)+= strlen(argv[i])-1; goto set_severities; } else if(strcmp(argpt,"-version")==0) { int major, minor, micro; printf( "Cdrecord 2.01a27 Emulation. Copyright (C) 2006-2023, see libburnia-project.org\n"); if(o->fallback_program[0]) { char *hargv[2]; printf("Fallback program : %s\n",o->fallback_program); printf("Fallback version :\n"); hargv[0]= argv[0]; hargv[1]= "-version"; Cdrpreskin_fallback(o,2,hargv,1); /* dirty never come back */ } printf("System adapter : %s\n", burn_scsi_transport_id(0)); printf("libburn interface : %d.%d.%d\n", burn_header_version_major, burn_header_version_minor, burn_header_version_micro); burn_version(&major, &minor, µ); printf("libburn in use : %d.%d.%d\n", major, minor, micro); #ifndef Cdrskin_extra_leaN printf("cdrskin version : %s\n",Cdrskin_prog_versioN); #else printf("cdrskin version : %s.lean (capability reduced lean version)\n", Cdrskin_prog_versioN); #endif printf("Version timestamp : %s\n",Cdrskin_timestamP); printf("Build timestamp : %s\n",Cdrskin_build_timestamP); {ret= 2; goto ex;} } else if(strcmp(argpt,"-waiti")==0) { o->do_waiti= 1; } else if(strcmp(argpt,"-xamix")==0) { fprintf(stderr, "cdrskin: FATAL : Option -xamix not implemented and data not yet convertible to other modes.\n"); return(0); } } ret= 1; final_checks:; if(flag&1) goto ex; if(o->verbosity >= Cdrskin_verbose_debuG) ClN(fprintf(stderr, "cdrskin: DEBUG : Using %s\n", burn_scsi_transport_id(0))); if(o->do_waiti) { fprintf(stderr, "cdrskin: Option -waiti pauses program until input appears at stdin\n"); printf("Waiting for data on stdin...\n"); for(ret= 0; ret==0; ) ret= Wait_for_input(0,1000000,0); if(ret<0 || feof(stdin)) fprintf(stderr, "cdrskin: NOTE : stdin produces exception rather than data\n"); fprintf(stderr,"cdrskin: Option -waiti pausing is done.\n"); } burn_preset_device_open(o->drive_exclusive | (o->drive_scsi_dev_family<<2) | ((!!o->drive_fcntl_f_setlk)<<5), o->drive_blocking, o->abort_on_busy_drive); if(strlen(o->raw_device_adr)>0 && !o->no_whitelist) { int driveno,hret; char *adr,buf[Cdrskin_adrleN]; if(strcmp(o->raw_device_adr,"stdio:-")==0) { fprintf(stderr, "cdrskin: SORRY : Cannot accept drive address \"stdio:-\".\n"); fprintf(stderr, "cdrskin: HINT : Use \"stdio:/dev/fd/1\" if you really want to write to stdout.\n"); {ret= 0; goto ex;} } adr= o->raw_device_adr; #ifndef Cdrskin_extra_leaN if(o->adr_trn!=NULL) { hret= Cdradrtrn_translate(o->adr_trn,adr,-1,buf,0); if(hret<=0) { fprintf(stderr, "cdrskin: FATAL : address translation failed (address too long ?) \n"); {ret= 0; goto ex;} } adr= buf; } #endif /* ! Cdrskin_extra_leaN */ if(adr[0]=='/') { if(strlen(adr)>=sizeof(o->device_adr)) { dev_too_long:; fprintf(stderr, "cdrskin: FATAL : dev=... too long (max. %d characters)\n", (int) sizeof(o->device_adr)-1); {ret= 0; goto ex;} } strcpy(o->device_adr,adr); } else { ret= Cdrpreskin__cdrecord_to_dev(adr,o->device_adr,&driveno, !!o->old_pseudo_scsi_adr); if(ret==-2 || ret==-3) {ret= 0; goto ex;} if(ret<0) goto ex; if(ret==0) { strcpy(o->device_adr,adr); ret= 1; } } if(strlen(o->device_adr)>0 && !o->no_convert_fs_adr) { int lret; char link_adr[Cdrskin_strleN+1]; strcpy(link_adr,o->device_adr); lret = burn_drive_convert_fs_adr(link_adr,o->device_adr); if(lret<0) { fprintf(stderr, "cdrskin: NOTE : Please inform libburn-hackers@pykix.org about:\n"); fprintf(stderr, "cdrskin: burn_drive_convert_fs_adr() returned %d\n",lret); } } } burn_allow_untested_profiles(!!o->allow_untested_media); /* A60927 : note to myself : no "ret= 1;" here. It breaks --help , -version */ ex:; /* Eventually replace current stdout by dup(1) from start of program */ if(strcmp(o->device_adr,"stdio:/dev/fd/1")==0 && o->result_fd >= 0) sprintf(o->device_adr,"stdio:/dev/fd/%d",o->result_fd); #ifndef Cdrskin_extra_leaN if(ret<=0 || !(flag&1)) Cdradrtrn_destroy(&(o->adr_trn),0); #endif return(ret); } /* --------------------------------------------------------------------- */ /** The maximum number of tracks */ #define Cdrskin_track_maX 99 /** Work around the fact that libburn leaves the track input fds open after the track is done. This can hide a few overflow bytes buffered by the fifo-to-libburn pipe which would cause a broken-pipe error if libburn would close that outlet. This macro enables a coarse workaround which tries to read bytes from the track inlets after burning has ended. Probably not a good idea if libburn would close the inlet fds. */ #define Cdrskin_libburn_leaves_inlet_opeN 1 /** List of furter wishes towards libburn: - a possibility to implement cdrskin -reset */ /* 4 TiB - 32 KiB : The libburn API works with int rather than uint32_t */ #define Cdrskin_tracksize_maX 4398046478336.0 /* Some constants obtained by hearsay and experiments */ /** The CD payload speed factor for reporting progress: 1x = 150 kB/s */ static double Cdrskin_cd_speed_factoR= 150.0*1024.0; /** The DVD payload speed factor for reporting progress: 1x */ static double Cdrskin_dvd_speed_factoR= 1385000; /** The BD payload speed factor for reporting progress: 1x */ static double Cdrskin_bd_speed_factoR= 4495625; /** The effective payload speed factor for reporting progress */ static double Cdrskin_speed_factoR= 150.0*1024.0; /** The speed conversion factors consumer x-speed to libburn speed as used with burn_drive_set_speed() burn_drive_get_write_speed() */ static double Cdrskin_libburn_cd_speed_factoR= 176.4; static double Cdrskin_libburn_dvd_speed_factoR= 1385.0; static double Cdrskin_libburn_bd_speed_factoR= 4495.625; /* The effective speed conversion factor for burn_drive_set_speed() */ static double Cdrskin_libburn_speed_factoR= 176.4; /** Add-on for burn_drive_set_speed() to accommodate to the slightly oversized speed ideas of my LG DVDRAM GSA-4082B. LITE-ON LTR-48125S tolerates it. */ static double Cdrskin_libburn_cd_speed_addoN= 40.0; static double Cdrskin_libburn_dvd_speed_addoN= 1.0; /*poor accuracy with 2.4x*/ static double Cdrskin_libburn_bd_speed_addoN= 1.0; static double Cdrskin_libburn_speed_addoN = 40.0; /** The program run control object. Defaults: see Cdrskin_new(). */ struct CdrskiN { /** Settings already interpreted by Cdrpreskin_setup */ struct CdrpreskiN *preskin; /** Job: what to do, plus some parameters. */ int verbosity; int pacifier_with_newline; double x_speed; int adjust_speed_to_drive; int gracetime; int dummy_mode; int force_is_set; int stream_recording_is_set; /* see burn_write_opts_set_stream_recording() */ int dvd_obs; /* DVD write chunk size: 0, 32k or 64k */ int obs_pad; /* Whether to force obs end padding */ int bdr_obs_exempt; /* Whether to possibly exempt BD-R from automatic obs_pad */ int stdio_sync; /* stdio fsync interval: -1, 0, >=32 */ int single_track; int prodvd_cli_compatible; int use_immed; /* 1= yes, 0= libburn default, -1= no */ int do_devices; /* 1= --devices , 2= --device_links */ int do_scanbus; int do_load; /* 1= -load , 2= -lock , -1= --no_load */ int do_checkdrive; int do_msinfo; char msifile[Cdrskin_strleN]; int do_atip; int do_list_speeds; int do_list_formats; int do_list_features; int do_cdtext_to_textfile; char cdtext_to_textfile_path[Cdrskin_strleN]; int do_cdtext_to_vt07; char cdtext_to_vt07_path[Cdrskin_strleN]; int do_extract_audio; char extract_audio_dir[Cdrskin_strleN]; char extract_basename[249]; char extract_audio_tracks[100]; /* if [i] > 0 : extract track i */ int extract_flags; /* bit3 = for flag bit3 of burn_drive_extract_audio() */ #ifdef Libburn_develop_quality_scaN int do_qcheck; /* 0= no , 1=nec_optiarc_rep_err_rate */ #endif /* Libburn_develop_quality_scaN */ int do_blank; int blank_fast; int no_blank_appendable; int blank_format_type; /* bit0-7: job type 0=blank 1=format_overwrite for DVD+RW, DVD-RW bit8-15: bit0-7 of burn_disc_format(flag) bit8 = write zeros after formatting bit9 = insist in size 0 bit10= format to maximum available size bit11= - reserved - bit12= - reserved - bit13= try to disable eventual defect management bit14= - reserved - bit15= format by index 2=deformat_sequential (blank_fast might matter) 3=format (= format_overwrite restricted to DVD+RW) 4=format_defectmgt for DVD-RAM, BD-R[E] bit8-15: bit0-7 of burn_disc_format(flag) bit8 = write zeros after formatting bit9+10: size mode 0 = use parameter size as far as it makes sense 1 = (identical to size mode 0) 2 = without bit7: format to maximum size with bit7 : take size from indexed format descriptor 3 = without bit7: format to default size with bit7 : take size from indexed format descriptor bit11= - reserved - bit12= - reserved - bit13= try to disable defect management bit14= - reserved - bit15= format by index 5=format_by_index gets mapped to 4 with DVD-RAM and BD-RE else to 1, bit15 should be set and bit16-23 should contain a usable index number 6=format_if_needed gets mapped to default variants of specialized formats if the media state requires formatting before writing 7=if_needed gets mapped to 6 for DVD-RAM and BD-RE, to 0 with all other non-blanks bit8-15: bit0-7 of burn_disc_format(flag) depending on job type */ int blank_format_index; /* bit8-15 of burn_disc_format(flag) */ double blank_format_size; /* to be used with burn_disc_format() */ int blank_format_no_certify; int do_direct_write; int do_burn; int tell_media_space; /* actually do not burn but tell the available space */ int burnfree; /** The write mode (like SAO or TAO). See libburn. Controlled by preskin->write_mode_name */ enum burn_write_types write_type; int block_type; int multi; int cdxa_conversion; /* bit0-30: for burn_track_set_cdxa_conv() bit31 : ignore bits 0 to 30 */ int modesty_on_drive; int min_buffer_usec; /* The other parameters for this function */ int max_buffer_usec; int buffer_timeout_sec; int min_buffer_percent; int max_buffer_percent; double write_start_address; double direct_write_amount; int assert_write_lba; int do_eject; char eject_device[Cdrskin_strleN]; /** The current data source and its eventual parameters. source_path may be either "-" for stdin, "#N" for open filedescriptor N or the address of a readable file. */ char source_path[Cdrskin_strleN]; double fixed_size; double smallest_tsize; int has_open_ended_track; double padding; int set_by_padsize; int fill_up_media; /** track_type may be set to BURN_MODE1, BURN_AUDIO, etc. */ int track_type; int track_type_by_default; /* 0= explicit, 1=not set, 2=by file extension */ int swap_audio_bytes; int track_modemods; /** CDRWIN cue sheet file */ char cuefile[Cdrskin_adrleN]; int use_cdtext; /** CD-TEXT */ unsigned char *text_packs; int num_text_packs; int sheet_v07t_blocks; char sheet_v07t_paths[8][Cdrskin_adrleN]; int cdtext_test; /* Media Catalog Number and ISRC */ char mcn[14]; char next_isrc[13]; char *index_string; int sao_pregap; int sao_postgap; /** The list of tracks with their data sources and parameters */ struct CdrtracK *tracklist[Cdrskin_track_maX]; int track_counter; /** a guess about what track might be processing right now */ int supposed_track_idx; /** The number of the first track in a CD SAO session */ int cd_start_tno; int fifo_enabled; /** Optional fifo between input fd and libburn. It uses a pipe(2) to transfer data to libburn. This fifo may be actually the start of a chain of fifos which are to be processed simultaneously. The fifo object knows the real input fd and the fd[1] of the pipe. This is just a reference pointer. The fifos are managed by the tracks which either line up their fifos or share the fifo of the first track. */ struct CdrfifO *fifo; /** fd[0] of the fifo pipe. This is from where libburn reads its data. */ int fifo_outlet_fd; int fifo_size; int fifo_start_at; int fifo_per_track; struct burn_source *cuefile_fifo; /** User defined address translation */ struct CdradrtrN *adr_trn; /** The drives known to libburn after scan */ struct burn_drive_info *drives; unsigned int n_drives; /** The drive selected for operation by CdrskiN */ int driveno; /** The persistent drive address of that drive */ char device_adr[Cdrskin_adrleN]; /** Progress state info: whether libburn is actually processing payload data*/ int is_writing; /** Previously detected drive state */ enum burn_drive_status previous_drive_status; /** abort parameters */ int abort_max_wait; /** Engagement info for eventual abort */ int lib_is_initialized; pid_t control_pid; /* pid of the thread that calls libburn */ int drive_is_grabbed; struct burn_drive *grabbed_drive; /* Whether drive was told to do something cancel-worthy 0= no: directly call burn_abort 1= yes: A worker thread is busy (e.g. writing, formatting) 2= yes: A synchronous operation is busy (e.g. grabbing). */ int drive_is_busy; #ifndef Cdrskin_extra_leaN /** Abort test facility */ double abort_after_bytecount; #endif /* ! Cdrskin_extra_leaN */ /** Some intermediate option info which is stored until setup finalization */ double tao_to_sao_tsize; int stdin_source_used; /* Info about media capabilities */ int media_does_multi; int media_is_overwriteable; /* For option -isosize and --grow_overwriteable_iso */ int use_data_image_size; /* For growisofs stunt : 0=disabled, 1=do stunt, fabricate toc, allow multi, 2=overwriteable_iso_head is valid 3=initial session (mostly to appease -multi on overwriteables) */ int grow_overwriteable_iso; /* New image head buffer for --grow_overwriteable_iso */ char overwriteable_iso_head[32*2048]; /* block 0 to 31 of target */ }; int Cdrskin_destroy(struct CdrskiN **o, int flag); int Cdrskin_grab_drive(struct CdrskiN *skin, int flag); int Cdrskin_release_drive(struct CdrskiN *skin, int flag); int Cdrskin_report_disc_status(struct CdrskiN *skin, enum burn_disc_status s, int flag); /** Create a cdrskin program run control object. @param skin Returns pointer to resulting @param flag Bitfield for control purposes: bit0= library is already initialized @return <=0 error, 1 success */ int Cdrskin_new(struct CdrskiN **skin, struct CdrpreskiN *preskin, int flag) { struct CdrskiN *o; int ret,i; (*skin)= o= TSOB_FELD(struct CdrskiN,1); if(o==NULL) return(-1); o->preskin= preskin; o->verbosity= preskin->verbosity; o->pacifier_with_newline= 0; o->x_speed= -1.0; o->adjust_speed_to_drive= 0; o->gracetime= 0; o->dummy_mode= 0; o->force_is_set= 0; o->stream_recording_is_set= 0; o->dvd_obs= 0; o->obs_pad= 0; o->bdr_obs_exempt= 0; o->stdio_sync= 0; o->single_track= 0; o->prodvd_cli_compatible= 0; o->use_immed= 0; o->do_devices= 0; o->do_scanbus= 0; o->do_load= 0; o->do_checkdrive= 0; o->do_msinfo= 0; o->msifile[0]= 0; o->do_atip= 0; o->do_list_speeds= 0; o->do_list_formats= 0; o->do_list_features= 0; o->do_cdtext_to_textfile= 0; o->cdtext_to_textfile_path[0]= 0; o->do_cdtext_to_vt07= 0; o->cdtext_to_vt07_path[0]= 0; o->do_extract_audio= 0; o->extract_audio_dir[0]= 0; o->extract_basename[0]= 0; for(i= 0; i < 100; i++) o->extract_audio_tracks[i]= 0; o->extract_flags= 0; #ifdef Libburn_develop_quality_scaN o->do_qcheck= 0; #endif /* Libburn_develop_quality_scaN */ o->do_blank= 0; o->blank_fast= 0; o->no_blank_appendable= 0; o->blank_format_type= 0; o->blank_format_index= -1; o->blank_format_size= 0.0; o->blank_format_no_certify= 0; o->do_direct_write= 0; o->do_burn= 0; o->tell_media_space= 0; o->write_type= BURN_WRITE_SAO; o->block_type= BURN_BLOCK_SAO; o->multi= 0; o->cdxa_conversion= 0; o->modesty_on_drive= 0; o->buffer_timeout_sec= 120; o->min_buffer_usec= 10000; o->max_buffer_usec= 100000; o->min_buffer_percent= 65; o->max_buffer_percent= 95; o->write_start_address= -1.0; o->direct_write_amount= -1.0; o->assert_write_lba= -1; o->burnfree= 1; o->do_eject= 0; o->eject_device[0]= 0; o->source_path[0]= 0; o->fixed_size= 0.0; o->smallest_tsize= -1.0; o->has_open_ended_track= 0; o->padding= 0.0; o->set_by_padsize= 0; o->fill_up_media= 0; o->track_type= BURN_MODE1; o->swap_audio_bytes= 1; /* cdrecord default is big-endian (msb_first) */ o->cuefile[0] = 0; o->use_cdtext = 0; o->text_packs= NULL; o->num_text_packs= 0; o->sheet_v07t_blocks= 0; for(i= 0; i < 8; i++) memset(o->sheet_v07t_paths, 0, Cdrskin_adrleN); o->cdtext_test= 0; o->mcn[0]= 0; o->next_isrc[0]= 0; o->index_string= NULL; o->sao_pregap= -1; o->sao_postgap= -1; o->track_modemods= 0; o->track_type_by_default= 1; for(i=0;itracklist[i]= NULL; o->track_counter= 0; o->supposed_track_idx= -1; o->cd_start_tno= 0; o->fifo_enabled= 1; o->fifo= NULL; o->fifo_outlet_fd= -1; o->fifo_size= 4*1024*1024; o->fifo_start_at= -1; o->fifo_per_track= 0; o->cuefile_fifo= NULL; o->adr_trn= NULL; o->drives= NULL; o->n_drives= 0; o->driveno= 0; o->device_adr[0]= 0; o->is_writing= 0; o->previous_drive_status = BURN_DRIVE_IDLE; o->abort_max_wait= 74*60; o->lib_is_initialized= (flag&1); o->control_pid= getpid(); o->drive_is_grabbed= 0; o->drive_is_busy= 0; o->grabbed_drive= NULL; #ifndef Cdrskin_extra_leaN o->abort_after_bytecount= -1.0; #endif /* ! Cdrskin_extra_leaN */ o->tao_to_sao_tsize= 0.0; o->stdin_source_used= 0; o->use_data_image_size= 0; o->media_does_multi= 0; o->media_is_overwriteable= 0; o->grow_overwriteable_iso= 0; memset(o->overwriteable_iso_head,0,sizeof(o->overwriteable_iso_head)); #ifndef Cdrskin_extra_leaN ret= Cdradrtrn_new(&(o->adr_trn),0); if(ret<=0) goto failed; #endif /* ! Cdrskin_extra_leaN */ return(1); failed:; Cdrskin_destroy(skin,0); return(-1); } /** Release from memory a cdrskin object */ int Cdrskin_destroy(struct CdrskiN **o, int flag) { struct CdrskiN *skin; int i; skin= *o; if(skin==NULL) return(0); if(skin->drive_is_grabbed) Cdrskin_release_drive(skin,0); for(i=0;itrack_counter;i++) Cdrtrack_destroy(&(skin->tracklist[i]),0); if(skin->index_string != NULL) free(skin->index_string); #ifndef Cdrskin_extra_leaN Cdradrtrn_destroy(&(skin->adr_trn),0); #endif /* ! Cdrskin_extra_leaN */ if(skin->cuefile_fifo != NULL) burn_source_free(skin->cuefile_fifo); if(skin->text_packs != NULL) free(skin->text_packs); Cdrpreskin_destroy(&(skin->preskin),0); if(skin->drives!=NULL) burn_drive_info_free(skin->drives); free((char *) skin); *o= NULL; return(1); } int Cdrskin_assert_driveno(struct CdrskiN *skin, int flag) { if(skin->driveno < 0 || (unsigned int) skin->driveno >= skin->n_drives) { fprintf(stderr, "cdrskin: FATAL : No drive found. Cannot perform desired operation.\n"); return(0); } return(1); } /** Return the addresses of the drive. device_adr is the libburn persistent address of the drive, raw_adr is the address as given by the user. */ int Cdrskin_get_device_adr(struct CdrskiN *skin, char **device_adr, char **raw_adr, int *no_convert_fs_adr, int flag) { int ret; if(skin->driveno < 0 || (unsigned int) skin->driveno >= skin->n_drives) return(0); ret= burn_drive_get_adr(&skin->drives[skin->driveno],skin->device_adr); if(ret <= 0) return(0); *device_adr= skin->device_adr; *raw_adr= skin->preskin->raw_device_adr; *no_convert_fs_adr= skin->preskin->no_convert_fs_adr; return(1); } int Cdrskin_get_drive(struct CdrskiN *skin, struct burn_drive **drive,int flag) { if(skin->driveno<0 || (unsigned int) skin->driveno >= skin->n_drives) return(0); *drive= skin->drives[skin->driveno].drive; return ((*drive) != NULL); } /** Return information about current track source */ int Cdrskin_get_source(struct CdrskiN *skin, char *source_path, double *fixed_size, double *tao_to_sao_tsize, int *use_data_image_size, double *padding, int *set_by_padsize, int *track_type, int *track_type_by_default, int *mode_mods, int *swap_audio_bytes, int *cdxa_conversion, int flag) { strcpy(source_path,skin->source_path); *fixed_size= skin->fixed_size; *tao_to_sao_tsize = skin->tao_to_sao_tsize; *use_data_image_size= skin->use_data_image_size; *padding= skin->padding; *set_by_padsize= skin->set_by_padsize; *track_type= skin->track_type; *track_type_by_default= skin->track_type_by_default; *mode_mods= skin->track_modemods; *swap_audio_bytes= skin->swap_audio_bytes; *cdxa_conversion= skin->cdxa_conversion; return(1); } #ifndef Cdrskin_extra_leaN /** Return information about current fifo setting */ int Cdrskin_get_fifo_par(struct CdrskiN *skin, int *fifo_enabled, int *fifo_size, int *fifo_start_at, int flag) { *fifo_enabled= skin->fifo_enabled; *fifo_size= skin->fifo_size; *fifo_start_at= skin->fifo_start_at; return(1); } #ifndef Cdrskin_no_cdrfifO /** Create and install fifo objects between track data sources and libburn. The sources and parameters are known to skin. @return <=0 error, 1 success */ int Cdrskin_attach_fifo(struct CdrskiN *skin, int flag) { struct CdrfifO *ff= NULL; int ret,i,hflag; int profile_number; char profile_name[80]; ret= Cdrskin_assert_driveno(skin, 0); if(ret <= 0) return(ret); ret= burn_disc_get_profile(skin->drives[skin->driveno].drive, &profile_number, profile_name); #ifdef Cdrskin_use_libburn_fifO /* Refuse here and thus use libburn fifo only with single track, non-CD */ if(profile_number != 0x09 && profile_number != 0x0a && skin->track_counter == 1) return(1); #endif /* Cdrskin_use_libburn_fifO */ skin->fifo= NULL; for(i=0;itrack_counter;i++) { hflag= (skin->verbosity>=Cdrskin_verbose_debuG) | ((profile_number == 0x09 || profile_number == 0x0a) << 3); if(i==skin->track_counter-1) hflag|= 4; if(skin->verbosity>=Cdrskin_verbose_cmD) { if(skin->fifo_per_track) printf("cdrskin: track %d establishing fifo of %d bytes\n", i+1,skin->fifo_size); else if(i==0) printf("cdrskin: establishing fifo of %d bytes\n",skin->fifo_size); else { if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr,"cdrskin_debug: attaching track %d to fifo\n",i+1)); hflag|= 2; } } ret= Cdrtrack_attach_fifo(skin->tracklist[i],&(skin->fifo_outlet_fd),ff, hflag); if(ret<=0) { fprintf(stderr,"cdrskin: FATAL : failed to attach fifo.\n"); return(0); } if(i==0 || skin->fifo_per_track) Cdrtrack_get_fifo(skin->tracklist[i],&ff,0); if(i==0) skin->fifo= ff; } return(1); } #endif /* ! Cdrskin_no_cdrfifO */ /** Read data into the track fifos until either #1 is full or its data source is exhausted. @return <=0 error, 1 success */ int Cdrskin_fill_fifo(struct CdrskiN *skin, int flag) { int ret; ret= Cdrtrack_fill_fifo(skin->tracklist[0],skin->fifo_start_at,0); if(ret<=0) return(ret); printf("input buffer ready.\n"); fflush(stdout); return(1); } #endif /* ! Cdrskin_extra_leaN */ /** Inform libburn about the consumer x-speed factor of skin */ int Cdrskin_adjust_speed(struct CdrskiN *skin, int flag) { int k_speed, modesty= 0; if(skin->x_speed<0) k_speed= 0; /* libburn.h promises 0 to be max speed. */ else if(skin->x_speed==0) { /* cdrecord specifies 0 as minimum speed. */ k_speed= -1; } else k_speed= skin->x_speed*Cdrskin_libburn_speed_factoR + Cdrskin_libburn_speed_addoN; if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr,"cdrskin_debug: k_speed= %d\n",k_speed)); if(skin->adjust_speed_to_drive && !skin->force_is_set) { struct burn_speed_descriptor *best_descr; burn_drive_get_best_speed(skin->drives[skin->driveno].drive,k_speed, &best_descr,0); if(best_descr!=NULL) { k_speed= best_descr->write_speed; skin->x_speed = ((double) k_speed) / Cdrskin_libburn_speed_factoR; } } burn_drive_set_speed(skin->drives[skin->driveno].drive,k_speed,k_speed); modesty= skin->modesty_on_drive; burn_drive_set_buffer_waiting(skin->drives[skin->driveno].drive, modesty, skin->min_buffer_usec, skin->max_buffer_usec, skin->buffer_timeout_sec, skin->min_buffer_percent, skin->max_buffer_percent); return(1); } int Cdrskin_determine_media_caps(struct CdrskiN *skin, int flag) { int ret; struct burn_multi_caps *caps = NULL; skin->media_is_overwriteable= skin->media_does_multi= 0; ret= burn_disc_get_multi_caps(skin->grabbed_drive,BURN_WRITE_NONE,&caps,0); if(ret<=0) goto ex; skin->media_is_overwriteable= !!caps->start_adr; skin->media_does_multi= !!caps->multi_session; ret= 1; ex:; if(caps != NULL) burn_disc_free_multi_caps(&caps); return(ret); } int Cdrskin__is_aborting(int flag) { if(Cdrskin_abort_leveL) return(-1); return(burn_is_aborting(0)); } int Cdrskin_abort(struct CdrskiN *skin, int flag) { int ret; Cdrskin_abort_leveL= 1; ret= burn_abort(skin->abort_max_wait, burn_abort_pacifier, "cdrskin: "); if(ret<=0) { fprintf(stderr, "\ncdrskin: ABORT : Cannot cancel burn session and release drive.\n"); } else { fprintf(stderr, "cdrskin: ABORT : Drive is released and library is shut down now.\n"); } fprintf(stderr, "cdrskin: ABORT : Program done. Even if you do not see a shell prompt.\n"); fprintf(stderr,"\n"); exit(1); } /** Obtain access to a libburn drive for writing or information retrieval. If libburn is not restricted to a single persistent address then the unused drives are dropped. This might be done by shutting down and restartiing libburn with the wanted drive only. Thus, after this call, libburn is supposed to have open only the reserved drive. All other drives should be free for other use. Warning: Do not store struct burn_drive pointer over this call. Any such pointer might be invalid afterwards. @param flag Bitfield for control purposes: bit0= bus is unscanned, device is known, use burn_drive_scan_and_grab() bit1= do not load drive tray bit2= do not issue error message on failure bit3= demand and evtl. report media, return 0 if none to see bit4= grab drive with unsuitable media even if fallback program bit5= do not re-assess the drive if it is already grabbed but rather perform a release-and-grab cycle @return <=0 error, 1 success */ int Cdrskin_grab_drive(struct CdrskiN *skin, int flag) { int ret,i,profile_number, mem; struct burn_drive *drive; char profile_name[80]; enum burn_disc_status s; if(skin->drive_is_grabbed && (flag & 32)) Cdrskin_release_drive(skin,0); if(!skin->drive_is_grabbed) { if(flag&1) { skin->driveno= 0; drive= NULL; skin->grabbed_drive= drive; } else { ret= Cdrskin_assert_driveno(skin, 0); if(ret <= 0) return(ret); drive= skin->drives[skin->driveno].drive; skin->grabbed_drive= drive; } } if(skin->drive_is_grabbed) { drive= skin->grabbed_drive; mem= skin->drive_is_busy; skin->drive_is_busy= 2; ret= burn_drive_re_assess(skin->grabbed_drive, 0); skin->drive_is_busy= mem; if(Cdrskin__is_aborting(0)) { fprintf(stderr,"cdrskin: ABORT : Drive re-assessment aborted\n"); Cdrskin_abort(skin, 0); /* Never comes back */ } if(ret <= 0) { if(!(flag&4)) fprintf(stderr,"cdrskin: FATAL : unable to re-assess drive '%s'\n", skin->preskin->device_adr); goto ex; } } else if(flag&1) { mem= skin->drive_is_busy; skin->drive_is_busy= 2; ret= burn_drive_scan_and_grab(&(skin->drives),skin->preskin->device_adr, (skin->do_load != -1) && !(flag&2)); skin->drive_is_busy= mem; if(Cdrskin__is_aborting(0)) { aborted:; fprintf(stderr,"cdrskin: ABORT : Drive aquiration aborted\n"); Cdrskin_abort(skin, 0); /* Never comes back */ } if(ret<=0) { if(!(flag&4)) fprintf(stderr,"cdrskin: FATAL : unable to open drive '%s'\n", skin->preskin->device_adr); goto ex; } skin->driveno= 0; drive= skin->drives[skin->driveno].drive; skin->grabbed_drive= drive; } else { if(strlen(skin->preskin->device_adr)<=0) { if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr, "cdrskin_debug: Cdrskin_grab_drive() dropping unwanted drives (%d)\n", skin->n_drives-1)); for(i= 0; i < (int) skin->n_drives; i++) { if(i==skin->driveno) continue; if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr, "cdrskin_debug: Cdrskin_grab_drive() dropped drive number %d\n",i)); ret= burn_drive_info_forget(&(skin->drives[i]), 0); if(ret==1 || ret==2) continue; fprintf(stderr, "cdrskin: NOTE : Please inform libburn-hackers@pykix.org about:\n"); fprintf(stderr, "cdrskin: burn_drive_info_forget() returns %d\n",ret); } } mem= skin->drive_is_busy; skin->drive_is_busy= 2; ret= burn_drive_grab(drive,(skin->do_load != -1) && !(flag&2)); skin->drive_is_busy= mem; if(Cdrskin__is_aborting(0)) goto aborted; if(ret==0) { if(!(flag&4)) fprintf(stderr,"cdrskin: FATAL : unable to open drive %d\n", skin->driveno); goto ex; } } if(skin->use_immed != 0) burn_drive_set_immed(drive, skin->use_immed > 0); skin->drive_is_grabbed= 1; s= burn_disc_get_status(drive); if((flag&8)) { if(skin->verbosity>=Cdrskin_verbose_progresS) Cdrskin_report_disc_status(skin,s,1); if(s==BURN_DISC_EMPTY) { Cdrskin_release_drive(skin,0); fprintf(stderr,"cdrskin: SORRY : No media found in drive\n"); ret= 0; goto ex; } } Cdrskin_speed_factoR= Cdrskin_cd_speed_factoR; Cdrskin_libburn_speed_factoR= Cdrskin_libburn_cd_speed_factoR; Cdrskin_libburn_speed_addoN= Cdrskin_libburn_cd_speed_addoN; ret= burn_disc_get_profile(drive,&profile_number,profile_name); if(ret>0) { if(strstr(profile_name,"DVD")==profile_name || strstr(profile_name,"stdio")==profile_name ) { Cdrskin_speed_factoR= Cdrskin_dvd_speed_factoR; Cdrskin_libburn_speed_factoR= Cdrskin_libburn_dvd_speed_factoR; Cdrskin_libburn_speed_addoN= Cdrskin_libburn_dvd_speed_addoN; } else if(strstr(profile_name,"BD")==profile_name) { Cdrskin_speed_factoR= Cdrskin_bd_speed_factoR; Cdrskin_libburn_speed_factoR= Cdrskin_libburn_bd_speed_factoR; Cdrskin_libburn_speed_addoN= Cdrskin_libburn_bd_speed_addoN; } } if(skin->preskin->fallback_program[0] && s==BURN_DISC_UNSUITABLE && skin->preskin->demands_cdrskin_caps<=0 && !(flag&16)) { skin->preskin->demands_cdrecord_caps= 1; fprintf(stderr, "cdrskin: NOTE : Will delegate job to fallback program '%s'.\n", skin->preskin->fallback_program); Cdrskin_release_drive(skin,0); ret= 0; goto ex; } Cdrskin_determine_media_caps(skin,0); ret= 1; ex:; if(ret<=0) { skin->drive_is_grabbed= 0; skin->grabbed_drive= NULL; } return(ret); } /** Release grabbed libburn drive @param flag Bitfield for control purposes: bit0= eject bit1= leave tray locked (eventually overrides bit0) */ int Cdrskin_release_drive(struct CdrskiN *skin, int flag) { if((!skin->drive_is_grabbed) || skin->grabbed_drive==NULL) { fprintf(stderr,"cdrskin: CAUGHT : release of non-grabbed drive.\n"); return(0); } if(flag&2) burn_drive_leave_locked(skin->grabbed_drive,0); else burn_drive_release(skin->grabbed_drive,(flag&1)); skin->drive_is_grabbed= 0; skin->grabbed_drive= NULL; return(1); } /** Clean up resources in abort situations. To be called by Cleanup subsystem but hardly ever by the application. The program must exit afterwards. */ int Cdrskin_abort_handler(struct CdrskiN *skin, int signum, int flag) { struct burn_progress p; enum burn_drive_status drive_status= BURN_DRIVE_SPAWNING; /* fprintf(stderr, "cdrskin_DEBUG: Cdrskin_abort_handler: signum=%d, flag=%d, drive_is_busy=%d\n", signum, flag, skin->drive_is_busy); */ if(skin->drive_is_busy == 0) Cdrskin_abort(skin, 0); /* Never comes back */ if(getpid()!=skin->control_pid) { if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr, "\ncdrskin_debug: ABORT : [%lu] Thread rejected: pid=%lu, signum=%lu\n", (unsigned long int) skin->control_pid, (unsigned long int) getpid(), (unsigned long int) signum)); #ifdef Not_yeT /* >>> find more abstract and system independent way to determine signals which make no sense with a return */ if(signum==11) { Cleanup_set_handlers(NULL,NULL,1); /* allow abort */ return(0); /* let exit */ } #endif usleep(1000000); return(-2); /* do only process the control thread */ } if(skin->preskin->abort_handler==3) Cleanup_set_handlers(NULL,NULL,2); /* ignore all signals */ else if(skin->preskin->abort_handler==4) Cleanup_set_handlers(NULL,NULL,1); /* allow abort */ fprintf(stderr, "\ncdrskin: ABORT : Handling started. Please do not press CTRL+C now.\n"); if(skin->preskin->abort_handler==3) fprintf(stderr,"cdrskin: ABORT : Trying to ignore any further signals\n"); #ifndef Cdrskin_no_cdrfifO if(skin->fifo!=NULL) Cdrfifo_close_all(skin->fifo,0); #endif /* Only for user info */ if(skin->grabbed_drive!=NULL) drive_status= burn_drive_get_status(skin->grabbed_drive,&p); if(drive_status!=BURN_DRIVE_IDLE) { fprintf(stderr,"cdrskin: ABORT : Abort processing depends on speed and buffer size\n"); fprintf(stderr,"cdrskin: ABORT : Usually it is done with 4x speed after about a MINUTE\n"); fprintf(stderr,"cdrskin: URGE : But wait at least the normal burning time before any kill -9\n"); } Cdrskin_abort_leveL= -1; if (!(flag & 1)) { if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr,"cdrskin: ABORT : Calling burn_abort(-1)\n")); burn_abort(-1, burn_abort_pacifier, "cdrskin: "); } fprintf(stderr, "cdrskin: ABORT : Urged drive worker threads to do emergency halt.\n"); return(-2); } /** Convert a libburn device address into a libburn drive number @return <=0 error, 1 success */ int Cdrskin_driveno_of_location(struct CdrskiN *skin, char *devicename, int *driveno, int flag) { int i,ret; char adr[Cdrskin_adrleN]; for(i= 0; i < (int) skin->n_drives; i++) { ret= burn_drive_get_adr(&(skin->drives[i]), adr); if(ret<=0) continue; if(strcmp(adr,devicename)==0) { *driveno= i; return(1); } } return(0); } /** Convert a cdrskin address into a libburn drive number @return <=0 error, 1 success */ int Cdrskin_dev_to_driveno(struct CdrskiN *skin, char *in_adr, int *driveno, int flag) { int ret; char *adr,translated_adr[Cdrskin_adrleN],synthetic_adr[Cdrskin_adrleN]; adr= in_adr; #ifndef Cdrskin_extra_leaN /* user defined address translation */ ret= Cdradrtrn_translate(skin->adr_trn,adr,-1,translated_adr,0); if(ret<=0) { fprintf(stderr, "cdrskin: FATAL : address translation failed (address too long ?) \n"); return(0); } if(skin->verbosity>=Cdrskin_verbose_cmD && strcmp(adr,translated_adr)!=0) printf("cdrskin: dev_translation=... : dev='%s' to dev='%s'\n", adr,translated_adr); adr= translated_adr; #endif /* ! Cdrskin_extra_leaN */ if(strncmp(adr, "stdio:", 6)==0) { if(skin->n_drives<=0) goto wrong_devno; *driveno= 0; return(1); } else if(adr[0]=='/') { ret= Cdrskin_driveno_of_location(skin,adr,driveno,0); if(ret<=0) { location_not_found:; fprintf(stderr, "cdrskin: FATAL : cannot find '%s' among accessible drive devices.\n", adr); fprintf(stderr, "cdrskin: HINT : use option --devices for a list of drive devices.\n"); return(0); } return(1); } ret= Cdrpreskin__cdrecord_to_dev(adr,synthetic_adr,driveno, !!skin->preskin->old_pseudo_scsi_adr); if(ret<=0) { wrong_devno:; if(skin->n_drives<=0) { fprintf(stderr,"cdrskin: FATAL : No accessible drives.\n"); } else { fprintf(stderr, "cdrskin: FATAL : Address does not lead to an accessible drive: %s\n", in_adr); fprintf(stderr, "cdrskin: HINT : dev= expects /dev/xyz, Bus,Target,0 or a number [0,%d]\n", skin->n_drives-1); } return(0); } if(strlen(synthetic_adr)>0) { if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: converted address '%s' to '%s'\n",adr,synthetic_adr)); ret= Cdrskin_driveno_of_location(skin,synthetic_adr,driveno,0); if(ret<=0) { fprintf(stderr, "cdrskin: failure while using address converted from '%s'\n",adr); adr= synthetic_adr; goto location_not_found; } } if((unsigned int) (*driveno) >= skin->n_drives || (*driveno) < 0) { ClN(fprintf(stderr,"cdrskin: obtained drive number %d from '%s'\n", *driveno,adr)); goto wrong_devno; } return(1); } /** Convert a libburn drive number into a cdrecord-style address which represents a device address if possible and the drive number else. @param flag Bitfield for control purposes: bit0= do not apply user defined address translation @return <0 error, pseudo transport groups: 0 volatile drive number, 1 /dev/sgN, 2 /dev/hdX, 3 stdio, 1000000+busno = non-pseudo SCSI bus 2000000+busno = pseudo-ATA|ATAPI SCSI bus (currently busno==2) */ int Cdrskin_driveno_to_btldev(struct CdrskiN *skin, int driveno, char btldev[Cdrskin_adrleN], int flag) { int k,ret,still_untranslated= 1,hret,k_start; char *loc= NULL,buf[Cdrskin_adrleN],adr[Cdrskin_adrleN]; if(driveno < 0 || driveno > (int) skin->n_drives) goto fallback; ret= burn_drive_get_adr(&(skin->drives[driveno]), adr); if(ret<=0) goto fallback; loc= adr; ret= burn_drive_get_drive_role(skin->drives[driveno].drive); if(ret!=1) { if(strlen(adr) > Cdrskin_adrleN - 7) { fprintf(stderr, "cdrskin : FAILURE : File path too long for \"stdio:\" prefix: %s\n", adr); sprintf(btldev, "stdio:"); /* Address of the null drive */ return(-1); } sprintf(btldev,"stdio:%s",adr); {ret= 2; goto adr_translation;} } if(!skin->preskin->old_pseudo_scsi_adr) { int host_no= -1,channel_no= -1,target_no= -1,lun_no= -1, bus_no= -1; ret= burn_drive_obtain_scsi_adr(loc,&bus_no,&host_no,&channel_no, &target_no,&lun_no); if(ret<=0) { if(strncmp(loc,"/dev/hd",7)==0) if(loc[7]>='a' && loc[7]<='z') if(loc[8]==0) { bus_no= (loc[7]-'a')/2; sprintf(btldev,"%d,%d,0",bus_no,(loc[7]-'a')%2); {ret= 2000000 + bus_no; goto adr_translation;} } goto fallback; } else { sprintf(btldev,"%d,%d,%d",bus_no,target_no,lun_no); ret= 1000000+bus_no; goto adr_translation; } } k_start= 0; if(strncmp(loc,"/dev/sg",7)==0 || strncmp(loc,"/dev/sr",7)==0) k_start= 7; if(strncmp(loc,"/dev/scd",8)==0) k_start= 8; if(k_start>0) { for(k= k_start;loc[k]!=0;k++) if(loc[k]<'0' || loc[k]>'9') break; if(loc[k]==0 && k>k_start) { sprintf(btldev,"1,%s,0",loc+k_start); {ret= 1; goto adr_translation;} } } if(strncmp(loc,"/dev/hd",7)==0) if(loc[7]>='a' && loc[7]<='z') if(loc[8]==0) { sprintf(btldev,"2,%d,0",loc[7]-'a'); {ret= 2; goto adr_translation;} } fallback:; if(skin->preskin->old_pseudo_scsi_adr) { sprintf(btldev,"0,%d,0",driveno); } else { if(loc!=NULL) strcpy(btldev,loc); else sprintf(btldev,"%d",driveno); } ret= 0; adr_translation:; #ifndef Cdrskin_extra_leaN /* user defined address translation */ if(!(flag&1)) { if(ret>0) { /* try whether a translation points to loc */ hret= Cdradrtrn_translate(skin->adr_trn,loc,driveno,buf,1); if(hret==2) { still_untranslated= 0; strcpy(btldev,buf); } } if(still_untranslated) { Cdradrtrn_translate(skin->adr_trn,btldev,driveno,buf,1); strcpy(btldev,buf); } } #endif /* ! Cdrskin_extra_leaN */ return(ret); } /** Read and buffer the start of an existing ISO-9660 image from overwriteable target media. */ int Cdrskin_overwriteable_iso_size(struct CdrskiN *skin, int *size, int flag) { int ret; off_t data_count= 0; double size_in_bytes; char *buf; buf= skin->overwriteable_iso_head; if(!skin->media_is_overwriteable) {ret= 0; goto ex;} /* Read first 64 kB */ ret= burn_read_data(skin->grabbed_drive,(off_t) 0,buf,32*2048,&data_count,0); if(ret<=0) {ret= 0; goto ex;} ret= Scan_for_iso_size((unsigned char *) (buf+16*2048), &size_in_bytes,0); if(ret<=0) { if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr,"cdrskin_debug: No detectable ISO-9660 size on media\n")); {ret= 0; goto ex;} } if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr,"cdrskin_debug: detected ISO-9660 size : %.f (%fs)\n", size_in_bytes, size_in_bytes/2048.0)); if(size_in_bytes/2048.0>2147483647-1-16) { fprintf(stderr, "cdrskin: FATAL : ISO-9660 filesystem in terabyte size detected\n"); {ret= 0; goto ex;} } *size= size_in_bytes/2048.0; if(size_in_bytes-((double) *size)*2048.0>0.0) (*size)++; if((*size)%16) *size+= 16-((*size)%16); if(skin->grow_overwriteable_iso==1) skin->grow_overwriteable_iso= 2; ret= 1; ex:; return(ret); } int Cdrskin_invalidate_iso_head(struct CdrskiN *skin, int flag) { int ret; int size; fprintf(stderr, "cdrskin: blank=... : invalidating ISO-9660 head on overwriteable media\n"); ret= Cdrskin_overwriteable_iso_size(skin,&size,0); if(ret<=0) { fprintf(stderr, "cdrskin: NOTE : Not an ISO-9660 file system. Left unaltered.\n"); return(2); } skin->overwriteable_iso_head[16*2048]= skin->overwriteable_iso_head[16*2048+3]= skin->overwriteable_iso_head[16*2048+4]= 'x'; ret= burn_random_access_write(skin->grabbed_drive,(off_t) 16*2048, skin->overwriteable_iso_head+16*2048, (off_t) 16*2048,1); return(ret); } /** Report media status s to the user @param flag Bitfield for control purposes: bit0= permission to check for overwriteable ISO image bit1= do not report media profile bit2= do not report but only check for pseudo appendable @return 1=ok, 2=ok, is pseudo appendable, <=0 error */ int Cdrskin_report_disc_status(struct CdrskiN *skin, enum burn_disc_status s, int flag) { int ret, iso_size, pseudo_appendable= 0; if(flag&1) { if(skin->media_is_overwriteable && skin->grow_overwriteable_iso>0) { if(skin->grow_overwriteable_iso==2) ret= 1; else ret= Cdrskin_overwriteable_iso_size(skin,&iso_size,0); if(ret>0) { s= BURN_DISC_APPENDABLE; pseudo_appendable= 1; } } } if(flag&4) return(1+pseudo_appendable); printf("cdrskin: status %d ",s); if(s==BURN_DISC_FULL) { printf("burn_disc_full \"There is a disc with data on it in the drive\"\n"); } else if(s==BURN_DISC_BLANK) { printf("burn_disc_blank \"The drive holds a blank disc\"\n"); } else if(s==BURN_DISC_APPENDABLE) { printf( "BURN_DISC_APPENDABLE \"There is an incomplete disc in the drive\"\n"); } else if(s==BURN_DISC_EMPTY) { printf("BURN_DISC_EMPTY \"There is no disc at all in the drive\"\n"); } else if(s==BURN_DISC_UNREADY) { printf("BURN_DISC_UNREADY \"The current status is not yet known\"\n"); } else if(s==BURN_DISC_UNGRABBED) { printf("BURN_DISC_UNGRABBED \"API usage error: drive not grabbed\"\n"); } else if(s==BURN_DISC_UNSUITABLE) { printf("BURN_DISC_UNSUITABLE \"Media is not suitable\"\n"); } else printf("-unknown status code-\n"); if(flag&2) return(1+pseudo_appendable); if((s==BURN_DISC_FULL || s==BURN_DISC_APPENDABLE || s==BURN_DISC_BLANK || s==BURN_DISC_UNSUITABLE) && skin->driveno>=0) { char profile_name[80]; int profile_number; printf("Current: "); ret= burn_disc_get_profile(skin->drives[skin->driveno].drive, &profile_number,profile_name); if(ret>0 && profile_name[0]!=0) printf("%s\n", profile_name); else if(ret>0) printf("UNSUITABLE MEDIA (Profile %4.4Xh)\n",profile_number); else printf("-unidentified-\n"); } else if(s==BURN_DISC_EMPTY) { printf("Current: none\n"); } return(1+pseudo_appendable); } /** Perform operations -scanbus or --devices @param flag Bitfield for control purposes: bit0= perform --devices rather than -scanbus bit1= with bit0: perform --device_links @return <=0 error, 1 success */ int Cdrskin_scanbus(struct CdrskiN *skin, int flag) { int ret,i,busno,first_on_bus,pseudo_transport_group= 0,skipped_devices= 0; int busmax= 16, busidx, max_dev_len, pad, j; char shellsafe[5*Cdrskin_strleN+2],perms[40],btldev[Cdrskin_adrleN]; char adr[Cdrskin_adrleN],*raw_dev,*drives_shown= NULL; char link_adr[BURN_DRIVE_ADR_LEN]; int *drives_busses= NULL; struct stat stbuf; drives_shown= calloc(1, skin->n_drives+1); drives_busses= calloc((skin->n_drives+1), sizeof(int)); if(drives_shown == NULL || drives_busses == NULL) {ret= -1; goto ex;} if(flag&1) { max_dev_len= 0; for(i= 0; i < (int) skin->n_drives; i++) { drives_shown[i]= 0; ret= burn_drive_get_adr(&(skin->drives[i]), adr); if(ret<=0) continue; if(flag & 2) { ret= burn_lookup_device_link(adr, link_adr, "/dev", NULL, 0, 0); if(ret == 1) strcpy(adr, link_adr); } if(strlen(adr)>=Cdrskin_strleN) Text_shellsafe("failure:oversized string", shellsafe, 0); else Text_shellsafe(adr, shellsafe,0); if((int) strlen(shellsafe) > max_dev_len) max_dev_len= strlen(shellsafe); } printf("cdrskin: Overview of accessible drives (%d found) :\n", skin->n_drives); printf("-----------------------------------------------------------------------------\n"); for(i= 0; i < (int) skin->n_drives; i++) { ret= burn_drive_get_adr(&(skin->drives[i]), adr); if(ret<=0) { /* >>> one should massively complain */; continue; } if(stat(adr,&stbuf)==-1) { sprintf(perms,"errno=%d",errno); } else { strcpy(perms,"------"); if(stbuf.st_mode&S_IRUSR) perms[0]= 'r'; if(stbuf.st_mode&S_IWUSR) perms[1]= 'w'; if(stbuf.st_mode&S_IRGRP) perms[2]= 'r'; if(stbuf.st_mode&S_IWGRP) perms[3]= 'w'; if(stbuf.st_mode&S_IROTH) perms[4]= 'r'; if(stbuf.st_mode&S_IWOTH) perms[5]= 'w'; } if(flag & 2) { ret= burn_lookup_device_link(adr, link_adr, "/dev", NULL, 0, 0); if(ret == 1) strcpy(adr, link_adr); } if(strlen(adr)>=Cdrskin_strleN) Text_shellsafe("failure:oversized string",shellsafe,0); else Text_shellsafe(adr,shellsafe,0); printf("%d dev=%s", i, shellsafe); pad= max_dev_len - strlen(shellsafe); if(pad > 0) for(j= 0; j < pad; j++) printf(" "); printf(" %s : '%-8.8s' '%s'\n", perms, skin->drives[i].vendor, skin->drives[i].product); } printf("-----------------------------------------------------------------------------\n"); } else { if(!skin->preskin->old_pseudo_scsi_adr) { pseudo_transport_group= 1000000; raw_dev= skin->preskin->raw_device_adr; if(strncmp(raw_dev,"ATA",3)==0 && (raw_dev[3]==0 || raw_dev[3]==':')) pseudo_transport_group= 2000000; if(strncmp(raw_dev,"ATAPI",5)==0 && (raw_dev[5]==0 || raw_dev[5]==':')) pseudo_transport_group= 2000000; if(pseudo_transport_group==2000000) { fprintf(stderr,"scsidev: 'ATA'\ndevname: 'ATA'\n"); fprintf(stderr,"scsibus: -2 target: -2 lun: -2\n"); } } /* >>> fprintf(stderr,"Linux sg driver version: 3.1.25\n"); */ printf("Using libburn version '%s'.\n", Cdrskin_libburn_versioN); if(pseudo_transport_group!=1000000) if(skin->preskin->old_pseudo_scsi_adr) printf("cdrskin: NOTE : The printed addresses are not cdrecord compatible !\n"); for(i= 0; i < (int) skin->n_drives; i++) { drives_busses[i]= -1; ret= Cdrskin_driveno_to_btldev(skin,i,btldev,1); if(ret >= pseudo_transport_group && ret < pseudo_transport_group + 1000000) { drives_busses[i]= ret - pseudo_transport_group; if(ret > pseudo_transport_group + busmax) busmax= 1 + ret - pseudo_transport_group; } } for(busidx= 0; busidx < (int) skin->n_drives + 1; busidx++) { if(busidx < (int) skin->n_drives) busno= drives_busses[busidx]; else busno= busmax; if(busno < 0) continue; for(i= 0; i < busidx; i++) if(drives_busses[i] == busno) break; if(i < busidx) continue; first_on_bus= 1; for(i= 0; i < (int) skin->n_drives; i++) { ret= Cdrskin_driveno_to_btldev(skin,i,btldev,1); if(busno==busmax && drives_shown[i]==0) { if(ret/1000000 != pseudo_transport_group) { skipped_devices++; if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr,"cdrskin_debug: skipping drive '%s%s'\n", ((ret/1000000)==2?"ATA:":""), btldev)); continue; } } else if(ret != pseudo_transport_group + busno) continue; if(first_on_bus) printf("scsibus%d:\n",busno); first_on_bus= 0; printf("\t%s\t %d) '%-8s' '%-16s' '%-4s' Removable CD-ROM\n", btldev,i,skin->drives[i].vendor,skin->drives[i].product, skin->drives[i].revision); drives_shown[i]= 1; } } } if(skipped_devices>0) { if(skipped_devices>1) printf("cdrskin: NOTE : There were %d drives not shown.\n", skipped_devices); else printf("cdrskin: NOTE : There was 1 drive not shown.\n"); printf("cdrskin: HINT : To surely see all drives try option: --devices\n"); if(pseudo_transport_group!=2000000) printf("cdrskin: HINT : or try options: dev=ATA -scanbus\n"); } ret= 1; ex:; if(drives_shown!=NULL) free((char *) drives_shown); if(drives_busses!=NULL) free((char *) drives_busses); return(ret); } /** Perform -checkdrive . @param flag Bitfield for control purposes: bit0= do not print message about pseudo-checkdrive @return <=0 error, 1 success */ int Cdrskin_checkdrive(struct CdrskiN *skin, char *profile_name, int flag) { struct burn_drive_info *drive_info; int ret; char btldev[Cdrskin_adrleN]; char *sno= NULL; int sno_len = 0; if(!(flag&1)) { if(flag&2) ClN(printf("cdrskin: pseudo-inquiry on drive %d\n",skin->driveno)); else ClN(printf("cdrskin: pseudo-checkdrive on drive %d\n",skin->driveno)); } if(skin->driveno >= (int) skin->n_drives || skin->driveno < 0) { fprintf(stderr,"cdrskin: FATAL : there is no drive #%d\n",skin->driveno); {ret= 0; goto ex;} } drive_info= &(skin->drives[skin->driveno]); ret= Cdrskin_driveno_to_btldev(skin,skin->driveno,btldev,0); if(ret>=0) fprintf(stderr,"scsidev: '%s'\n",btldev); printf("Device type : "); ret= burn_drive_get_drive_role(drive_info->drive); if(ret==0) printf("%s\n","Emulated (null-drive)"); else if(ret==2) printf("%s\n","Emulated (stdio-drive, 2k random read-write)"); else if(ret==3) printf("%s\n","Emulated (stdio-drive, sequential write-only)"); else if(ret==4) printf("%s\n","Emulated (stdio-drive, 2k random read-only)"); else if(ret == 5) printf("%s\n","Emulated (stdio-drive, 2k random write-only)"); else if(ret!=1) printf("%s\n","Emulated (stdio-drive)"); else printf("%s\n","Removable CD-ROM"); printf("Vendor_info : '%s'\n",drive_info->vendor); printf("Identifikation : '%s'\n",drive_info->product); printf("Revision : '%s'\n",drive_info->revision); if(flag&2) {ret= 1; goto ex;} burn_drive_get_serial_no(drive_info->drive, &sno, &sno_len); if(sno_len > 0) printf("Drive id : '%s'\n", sno); if(sno != NULL) free(sno); sno= NULL; printf("Driver flags : %s\n","BURNFREE"); printf("Supported modes:"); if((drive_info->tao_block_types & (BURN_BLOCK_MODE1)) == (BURN_BLOCK_MODE1)) printf(" TAO"); if(drive_info->sao_block_types & BURN_BLOCK_SAO) printf(" SAO"); #ifndef Cdrskin_disable_raw96R if((drive_info->raw_block_types & BURN_BLOCK_RAW96R) && strstr(profile_name,"DVD")!=profile_name && strstr(profile_name,"BD")!=profile_name) printf(" RAW/RAW96R"); #endif /* ! Cdrskin_disable_raw96R */ printf("\n"); ret= 1; ex:; return(ret); } /** Predict address block number where the next write will go to @param flag Bitfield for control purposes: bit0= do not return nwa from eventual write_start_address @return <=0 error, 1 nwa from drive , 2 nwa from write_start_address */ int Cdrskin_obtain_nwa(struct CdrskiN *skin, int *nwa, int flag) { int ret,lba; struct burn_drive *drive; struct burn_write_opts *o= NULL; if(skin->write_start_address>=0 && !(flag&1)) { /* (There is no sense in combining random addressing with audio) */ *nwa= skin->write_start_address/2048; return(2); } /* Set write opts in order to provoke MODE SELECT. LG GSA-4082B needs it. */ drive= skin->drives[skin->driveno].drive; o= burn_write_opts_new(drive); if(o!=NULL) { burn_write_opts_set_perform_opc(o, 0); burn_write_opts_set_write_type(o,skin->write_type,skin->block_type); burn_write_opts_set_underrun_proof(o,skin->burnfree); } ret= burn_disc_track_lba_nwa(drive,o,0,&lba,nwa); if(o!=NULL) burn_write_opts_free(o); return(ret); } /* @param flag bit0-3= source of text packs: 0= CD Lead-in 1= session and tracks 2= textfile= bit4-7= output format 0= -vv -toc 1= Sony CD-TEXT Input Sheet Version 0.7T */ int Cdrskin_print_text_packs(struct CdrskiN *skin, unsigned char *text_packs, int num_packs, int flag) { int i, j, from, fmt, ret, char_code= 0; char *from_text= "", *result= NULL; unsigned char *pack; from= flag & 15; fmt= (flag >> 4) & 15; if(from == 0) from_text= " from CD Lead-in"; else if(from == 1) from_text= " from session and tracks"; else if(from == 2) from_text= " from textfile= or cuefile= CDTEXTFILE"; if (fmt == 1) { ret = burn_make_input_sheet_v07t(text_packs, num_packs, 0, 0, &result, &char_code, 0); if(ret <= 0) goto ex; if(char_code == 0x80) fprintf(stderr, "cdrskin : WARNING : Double byte characters encountered.\n"); for(i = 0; i < ret; i++) printf("%c", result[i]); } else { printf("CD-TEXT data%s:\n", from_text); for(i= 0; i < num_packs; i++) { pack= text_packs + 18 * i; printf("%4d :", i); for(j= 0; j < 18; j++) { if(j >= 4 && j <= 15 && pack[j] >= 32 && pack[j] <= 126 && pack[0] != 0x88 && pack[0] != 0x89 && pack[0] != 0x8f) printf(" %c", pack[j]); else printf(" %2.2x", pack[j]); } printf("\n"); } } ret= 1; ex:; if(result != NULL) free(result); return(ret); } /* @param flag bit4= no not overwrite existing target file */ int Cdrskin_store_text_packs(struct CdrskiN *skin, unsigned char *text_packs, int num_packs, char *path, int flag) { int data_length, ret; struct stat stbuf; FILE *fp; unsigned char fake_head[4]; if(stat(path, &stbuf) != -1 && (flag & 16)) { fprintf(stderr, "cdrskin: SORRY : Will not overwrite file '%s'\n", path); return(0); } fp= fopen(path, "w"); if(fp == NULL) { fprintf(stderr, "cdrskin: SORRY : Cannot open file '%s' for storing extracted CD-TEXT\n", path); fprintf(stderr, "cdrskin: %s (errno=%d)\n", strerror(errno), errno); return(0); } data_length= num_packs * 18 + 2; fake_head[0]= (data_length >> 8) & 0xff; fake_head[1]= data_length & 0xff; fake_head[2]= fake_head[3]= 0; ret= fwrite(fake_head, 4, 1, fp); if(ret != 1) { write_failure:; fprintf(stderr, "cdrskin: SORRY : Cannot write all data to file '%s'\n", path); fprintf(stderr, "cdrskin: %s (errno=%d)\n", strerror(errno), errno); fclose(fp); return(0); } ret= fwrite(text_packs, data_length - 2, 1, fp); if(ret != 1) goto write_failure; fprintf(stderr, "cdrskin: NOTE : Wrote header and %d CD-TEXT bytes to file '%s'\n", data_length - 2, path); fclose(fp); return(1); } /* @param flag bit0-3= output format 1= Sony CD-TEXT Input Sheet Version 0.7T 15= Cdrskin_store_text_packs bit4= do not overwrite existing the target file */ int Cdrskin_cdtext_to_file(struct CdrskiN *skin, char *path, int flag) { int ret, fmt, char_code= 0, to_write; struct burn_drive *drive; unsigned char *text_packs= NULL; int num_packs= 0; char *result= 0; FILE *fp= NULL; struct stat stbuf; ret= Cdrskin_grab_drive(skin, 0); if(ret<=0) goto ex; fmt= flag & 15; drive= skin->drives[skin->driveno].drive; ret= burn_disc_get_leadin_text(drive, &text_packs, &num_packs, 0); if(ret <= 0 || num_packs <= 0) { fprintf(stderr, "cdrskin: No CD-Text or CD-Text unaware drive.\n"); ret= 2; goto ex; } if(fmt == 1) { ret = burn_make_input_sheet_v07t(text_packs, num_packs, 0, 0, &result, &char_code, 0); if(ret <= 0) goto ex; to_write= ret; if(stat(path, &stbuf) != -1 && (flag & 16)) { fprintf(stderr, "cdrskin: SORRY : Will not overwrite file '%s'\n", path); return(0); } if(strcmp(path, "-") == 0) fp= stdout; else fp = fopen(path, "w"); if(fp == NULL) { if(errno > 0) fprintf(stderr, "cdrskin: %s (errno=%d)\n", strerror(errno), errno); fprintf(stderr, "cdrskin: SORRY : Cannot write CD-TEXT list to file '%s'\n", path); {ret= 0; goto ex;} } ret = fwrite(result, to_write, 1, fp); if(ret != 1) { if(errno > 0) fprintf(stderr, "cdrskin: %s (errno=%d)\n", strerror(errno), errno); fprintf(stderr, "cdrskin: SORRY : Cannot write all CD-TEXT to file '%s'\n", path); ret= 0; } if(ret <= 0) goto ex; } else if(fmt == 15) { if(skin->verbosity >= Cdrskin_verbose_debuG) Cdrskin_print_text_packs(skin, text_packs, num_packs, 0); ret= Cdrskin_store_text_packs(skin, text_packs, num_packs, path, flag & 16); if(ret <= 0) goto ex; printf("CD-Text len: %d\n", num_packs * 18 + 4); } else { fprintf(stderr, "cdrskin: FATAL : Program error : Unknown format %d with Cdrskin_cdtext_to_file.\n", fmt); {ret= -1; goto ex;} } ret= 1; ex:; if(result != NULL) free(result); if(text_packs != NULL) free(text_packs); if(fp != NULL && fp != stdout) fclose(fp); return(1); } /** Perform -toc under control of Cdrskin_atip(). @param flag Bitfield for control purposes: bit0= do not list sessions separately (do it cdrecord style) @return <=0 error, 1 success */ int Cdrskin_toc(struct CdrskiN *skin, int flag) { int num_sessions= 0,num_tracks= 0,lba= 0,track_count= 0,total_tracks= 0; int session_no, track_no, pmin, psec, pframe, ret, final_ret= 1; int track_offset = 1, open_sessions= 0, have_real_open_session= 0; struct burn_drive *drive; struct burn_disc *disc= NULL; struct burn_session **sessions; struct burn_track **tracks; struct burn_toc_entry toc_entry; enum burn_disc_status s; char profile_name[80]; int profile_number, is_bdr_pow= 0; drive= skin->drives[skin->driveno].drive; s= burn_disc_get_status(drive); if(s == BURN_DISC_EMPTY || s == BURN_DISC_BLANK) goto summary; disc= burn_drive_get_disc(drive); if(disc==NULL) { if(skin->grow_overwriteable_iso>0) { ret= Cdrskin_overwriteable_iso_size(skin,&lba,0); if(ret>0) { printf( "first: 1 last: 1 (fabricated from ISO-9660 image on overwriteable media)\n"); printf( "track: 1 lba: 0 ( 0) 00:02:00 adr: 1 control: 4 mode: 1\n"); burn_lba_to_msf(lba, &pmin, &psec, &pframe); printf("track:lout lba: %9d (%9d) %2.2d:%2.2d:%2.2d", lba,4*lba,pmin,psec,pframe); printf(" adr: 1 control: 4 mode: -1\n"); goto summary; } } goto cannot_read; } sessions= burn_disc_get_sessions(disc,&num_sessions); open_sessions= burn_disc_get_incomplete_sessions(disc); if(num_sessions > 0) track_offset = burn_session_get_start_tno(sessions[0], 0); if(track_offset <= 0) track_offset= 1; if(flag&1) { for(session_no= 0; session_no < num_sessions + open_sessions; session_no++) { tracks= burn_session_get_tracks(sessions[session_no],&num_tracks); total_tracks+= num_tracks; if(session_no == num_sessions + open_sessions - 1 && open_sessions > 0) { total_tracks--; /* Do not count invisible track */ if(num_tracks > 1) have_real_open_session= 1; } } printf("first: %d last %d\n", track_offset, total_tracks + track_offset - 1); } for(session_no= 0; session_no < num_sessions + open_sessions; session_no++) { tracks= burn_session_get_tracks(sessions[session_no],&num_tracks); if(tracks==NULL) continue; if(session_no == num_sessions + open_sessions - 1 && open_sessions > 0) num_tracks--; if(num_tracks <= 0) continue; if(!(flag&1)) printf("first: %d last: %d\n", track_count + track_offset, track_count + num_tracks + track_offset - 1); for(track_no= 0; track_no>> From where does cdrecord take "mode" ? */ /* This is not the "mode" as printed by cdrecord : printf(" mode: %d\n",burn_track_get_mode(tracks[track_no])); */ /* own guess: cdrecord says "1" on data and "0" on audio : */ printf(" mode: %d\n",((toc_entry.control&7)<4?0:1)); } if((flag&1) && session_no < num_sessions + open_sessions - 1 + have_real_open_session - 1) continue; if(have_real_open_session) { /* Use start of invisible track */ burn_track_get_entry(tracks[num_tracks], &toc_entry); } else { burn_session_get_leadout_entry(sessions[session_no],&toc_entry); } if(toc_entry.extensions_valid&1) { /* DVD extension valid */ lba= toc_entry.start_lba; burn_lba_to_msf(lba, &pmin, &psec, &pframe); } else { pmin= toc_entry.pmin; psec= toc_entry.psec; pframe= toc_entry.pframe; lba= burn_msf_to_lba(pmin,psec,pframe); } printf("track:lout lba: %9d (%9d) %2.2d:%2.2d:%2.2d", lba,4*lba,pmin,psec,pframe); printf(" adr: %d control: %d",toc_entry.adr,toc_entry.control); printf(" mode: -1\n"); } if(skin->verbosity >= Cdrskin_verbose_cmD) { ret= Cdrskin_cdtext_to_file(skin, "cdtext.dat", 15 | 16); if(ret <= 0 && ret < final_ret) final_ret= ret; } summary: ret= burn_disc_get_profile(drive, &profile_number, profile_name); if(ret <= 0) strcpy(profile_name, "media"); if(open_sessions > 0 && !have_real_open_session) open_sessions--; is_bdr_pow= burn_drive_get_bd_r_pow(drive); printf("Media summary: %d sessions, %d tracks, %s %s\n", num_sessions + open_sessions, track_count, is_bdr_pow ? "unusable (POW)" : s==BURN_DISC_BLANK ? "blank" : s==BURN_DISC_APPENDABLE ? "appendable" : s==BURN_DISC_FULL ? "closed" : s==BURN_DISC_EMPTY ? "no " : "unknown ", profile_name); if(have_real_open_session) printf("Warning : Incomplete session encountered !\n"); if(disc!=NULL) burn_disc_free(disc); if(s == BURN_DISC_EMPTY) return(0); return(final_ret); cannot_read:; fprintf(stderr,"cdrecord_emulation: Cannot read TOC header\n"); fprintf(stderr,"cdrecord_emulation: Cannot read TOC/PMA\n"); return(0); } /** Perform -minfo under control of Cdrskin_atip(). @param flag Bitfield for control purposes: @return <=0 error, 1 success */ int Cdrskin_minfo(struct CdrskiN *skin, int flag) { int num_sessions= 0,num_tracks= 0,lba= 0,track_count= 0,total_tracks= 0; int session_no, track_no, pmin, psec, pframe, ret, size= 0, nwa= 0; int last_leadout= 0, ovwrt_full= 0, track_offset= 1, open_sessions= 0; struct burn_drive *drive; struct burn_disc *disc= NULL; struct burn_session **sessions= NULL; struct burn_track **tracks; struct burn_toc_entry toc_entry; enum burn_disc_status s, show_status; char profile_name[80]; int pno, is_bdr_pow= 0; char media_class[80]; int nominal_sessions= 1, ftils= 1, ltils= 1, first_track= 1, read_capacity= 0; int app_code, cd_info_valid, lra, alloc_blocks, free_blocks; int have_real_open_session= 0; off_t avail, buf_count; char disc_type[80], bar_code[9], buf[2 * 2048], *type_text; unsigned int disc_id; drive= skin->drives[skin->driveno].drive; s= burn_disc_get_status(drive); if(s == BURN_DISC_EMPTY) { fprintf(stderr, "cdrecord-Emulation: No disk / Wrong disk!\n"); return(1); } ret= burn_disc_get_profile(drive, &pno, profile_name); if(ret <= 0) { fprintf(stderr, "cdrskin: SORRY : Cannot inquire current media profile\n"); return(1); } if(pno >= 0x08 && pno <= 0x0a) strcpy(media_class, "CD"); else if(pno >= 0x10 && pno <= 0x2f) strcpy(media_class, "DVD"); else if(pno >= 0x40 && pno <= 0x43) strcpy(media_class, "BD"); else sprintf(media_class, "Unknown class (profile 0x%4.4X)", pno); is_bdr_pow= burn_drive_get_bd_r_pow(drive); printf("\n"); printf("Mounted media class: %s\n", media_class); printf("Mounted media type: %s%s\n", profile_name, is_bdr_pow ? ", Pseudo Overwrite formatted" : ""); ret= burn_disc_erasable(drive); printf("Disk Is %serasable\n", (ret || skin->media_is_overwriteable) ? "" : "not "); show_status= s; ret = burn_get_read_capacity(drive, &read_capacity, 0); if(ret <= 0) read_capacity= 0; if(skin->media_is_overwriteable && read_capacity > 0) ovwrt_full= 1; if(ovwrt_full) show_status= BURN_DISC_FULL; printf("disk status: %s\n", is_bdr_pow ? "unusable (POW)" : show_status == BURN_DISC_BLANK ? "empty" : show_status == BURN_DISC_APPENDABLE ? "incomplete/appendable" : show_status == BURN_DISC_FULL ? "complete" : "unusable"); printf("session status: %s\n", show_status == BURN_DISC_BLANK ? "empty" : show_status == BURN_DISC_APPENDABLE ? "empty" : show_status == BURN_DISC_FULL ? "complete" : "unknown"); disc= burn_drive_get_disc(drive); if(disc==NULL || s == BURN_DISC_BLANK) { first_track= 1; num_sessions= 0; nominal_sessions= 1; ftils= ltils= 1; } else { sessions= burn_disc_get_sessions(disc, &num_sessions); open_sessions= burn_disc_get_incomplete_sessions(disc); if(num_sessions > 0) track_offset= burn_session_get_start_tno(sessions[0], 0); if(track_offset <= 0) track_offset= 1; first_track= track_offset; nominal_sessions= num_sessions + open_sessions; if(s == BURN_DISC_APPENDABLE && open_sessions == 0) nominal_sessions++; for(session_no= 0; session_no < num_sessions + open_sessions; session_no++) { ftils= total_tracks + 1; tracks= burn_session_get_tracks(sessions[session_no],&num_tracks); if(tracks==NULL) continue; total_tracks+= num_tracks; ltils= total_tracks; if(session_no==0 && burn_session_get_hidefirst(sessions[session_no]) && total_tracks >= 2) first_track= 2; } if(s == BURN_DISC_APPENDABLE && open_sessions == 0) ftils= ltils= total_tracks + 1; } printf("first track: %d\n", first_track); printf("number of sessions: %d\n", nominal_sessions); printf("first track in last sess: %d\n", ftils + track_offset - 1); printf("last track in last sess: %d\n", ltils + track_offset - 1); burn_disc_get_cd_info(drive, disc_type, &disc_id, bar_code, &app_code, &cd_info_valid); printf("Disk Is %sunrestricted\n", (cd_info_valid & 16) ? "" : "not "); if((cd_info_valid & 8) && !(cd_info_valid & 16)) printf("Disc application code: %d\n", app_code); if(strcmp(media_class, "DVD") == 0 || strcmp(media_class, "BD") == 0) printf("Disk type: DVD, HD-DVD or BD\n"); else if(strcmp(media_class, "CD") == 0 && (cd_info_valid & 1)) printf("Disk type: %s\n", disc_type); else printf("Disk type: unrecognizable\n"); if(cd_info_valid & 2) printf("Disk id: 0x%-X\n", disc_id); ret= burn_disc_get_bd_spare_info(drive, &alloc_blocks, &free_blocks, 0); if(ret == 1) { printf("BD Spare Area consumed: %d\n", alloc_blocks - free_blocks); printf("BD Spare Area available: %d\n", free_blocks); } printf("\n"); printf("Track Sess Type Start Addr End Addr Size\n"); printf("==============================================\n"); for(session_no= 0; session_no < num_sessions + open_sessions; session_no++) { tracks= burn_session_get_tracks(sessions[session_no],&num_tracks); if(tracks==NULL) continue; for(track_no= 0; track_no= 0x08 && pno <= 0x0a && ((toc_entry.control & 7) >= 4) && lra >= 2 && size >= 2) { /* If last two blocks not readable then assume TAO and subtract 2 from lra and size. */; ret= burn_read_data(drive, (off_t) (lra - 1) * (off_t) 2048, buf, 2 * 2048, &buf_count, 2 | 4); if(ret <= 0) { lra-= 2; size-= 2; } } #ifdef Cdrskin_with_last_recorded_addresS /* Interesting, but obviously not what cdrecord prints as "End Addr" */ if(toc_entry.extensions_valid & 2) { /* LRA extension valid */ if(pno == 0x11 || pno == 0x13 || pno == 0x14 || pno == 0x15 || pno == 0x41 || pno == 0x42 || pno == 0x51) lra= toc_entry.last_recorded_address; } #endif /* Cdrskin_with_last_recorded_addresS */ if(session_no < num_sessions) { type_text= ((toc_entry.control&7)<4) ? "Audio" : "Data"; } else { if(track_no < num_tracks - 1) { type_text= "Rsrvd"; have_real_open_session = 1; } else { type_text= "Blank"; } if(toc_entry.extensions_valid & 4) { if(toc_entry.track_status_bits & (1 << 14)) type_text= "Blank"; else if(toc_entry.track_status_bits & (1 << 16)) { type_text= "Apdbl"; have_real_open_session = 1; } else if(toc_entry.track_status_bits & (1 << 15)) { type_text= "Rsrvd"; have_real_open_session = 1; } else type_text= "Invsb"; } } printf("%5d %5d %-6s %-10d %-10d %-10d\n", track_count + track_offset - 1, session_no + 1, type_text, lba, lra, size); if(session_no < num_sessions) last_leadout= lba + size; } } if(last_leadout > 0) if(read_capacity > last_leadout) read_capacity= last_leadout; if(nominal_sessions > num_sessions) { ret= burn_disc_track_lba_nwa(drive, NULL, 0, &lba, &nwa); if(ret > 0) { avail= burn_disc_available_space(drive, NULL); size= avail / 2048; if(read_capacity == 0 && skin->media_is_overwriteable) size= 0; /* unformatted overwriteable media */ if(nominal_sessions > num_sessions + open_sessions) { printf("%5d %5d %-6s %-10d %-10d %-10d\n", track_count + track_offset, nominal_sessions, ovwrt_full ? "Data" : "Blank", nwa, nwa + size - 1, size); } } } printf("\n"); if(num_sessions > 0) { ret= burn_disc_get_msc1(drive, &lba); if(ret > 0) printf("Last session start address: %-10d\n", lba); if(last_leadout > 0) printf("Last session leadout start address: %-10d\n", last_leadout); if(s == BURN_DISC_FULL) { if(read_capacity > 0 && (last_leadout != read_capacity || pno == 0x08 || pno == 0x10 || pno == 0x40 || pno == 0x50)) printf("Read capacity: %-10d\n", read_capacity); } } else if(ovwrt_full) { printf("Last session start address: %-10d\n", nwa); printf("Last session leadout start address: %-10d\n", size); } if(nominal_sessions > num_sessions && !skin->media_is_overwriteable) { printf("Next writable address: %-10d\n", nwa); printf("Remaining writable size: %-10d\n", size); } if(ovwrt_full) { printf("\n"); printf("cdrskin: Media is overwriteable. No blanking needed. No reliable track size.\n"); printf("cdrskin: Above contrary statements follow cdrecord traditions.\n"); } if(have_real_open_session) printf("\nWarning: Incomplete session encountered !\n"); if(disc!=NULL) burn_disc_free(disc); if(s == BURN_DISC_EMPTY) return(0); return(1); } int Cdrskin_print_all_profiles(struct CdrskiN *skin, struct burn_drive *drive, int flag) { int num_profiles, profiles[64], i, ret; char is_current[64], profile_name[80]; burn_drive_get_all_profiles(drive, &num_profiles, profiles, is_current); for(i= 0; i < num_profiles; i++) { ret= burn_obtain_profile_name(profiles[i], profile_name); if(ret <= 0) strcpy(profile_name, "unknown"); printf("Profile: 0x%4.4X (%s)%s\n", (unsigned int) profiles[i], profile_name, is_current[i] ? " (current)" : ""); } return(1); } /** Perform -atip . @param flag Bitfield for control purposes: bit0= perform -toc bit1= perform -toc with session markers bit2= perform -minfo @return <=0 error, 1 success */ int Cdrskin_atip(struct CdrskiN *skin, int flag) { int ret,is_not_really_erasable= 0, current_is_cd= 1; double x_speed_max= -1.0, x_speed_min= -1.0; enum burn_disc_status s; struct burn_drive *drive; int profile_number= 0; char profile_name[80], *manuf= NULL, *media_code1= NULL, *media_code2= NULL; char *book_type= NULL, *product_id= NULL; char *sno= NULL; int sno_len = 0, i, is_bdr_pow= 0; ClN(printf("cdrskin: pseudo-atip on drive %d\n",skin->driveno)); ret= Cdrskin_grab_drive(skin,0); if(ret<=0) return(ret); drive= skin->drives[skin->driveno].drive; s= burn_disc_get_status(drive); Cdrskin_report_disc_status(skin,s,1|2); if(s==BURN_DISC_APPENDABLE && skin->no_blank_appendable) is_not_really_erasable= 1; profile_name[0]= 0; ret= burn_disc_get_profile(drive,&profile_number,profile_name); if(ret<=0) { profile_number= 0; strcpy(profile_name, "-unidentified-"); } if(profile_number != 0x08 && profile_number != 0x09 && profile_number != 0x0a) current_is_cd= 0; ret= Cdrskin_checkdrive(skin,profile_name,1); if(ret<=0) return(ret); if(burn_disc_get_status(drive) != BURN_DISC_UNSUITABLE) { ret= burn_disc_read_atip(drive); if(ret>0) { ret= burn_drive_get_min_write_speed(drive); x_speed_min= ((double) ret)/Cdrskin_libburn_speed_factoR; } } if(burn_disc_get_status(drive) == BURN_DISC_UNSUITABLE) { if(skin->verbosity>=Cdrskin_verbose_progresS) { if(profile_name[0]) printf("Current: %s\n",profile_name); else printf("Current: UNSUITABLE MEDIA (Profile %4.4Xh)\n",profile_number); } {ret= 0; goto ex;} } if(burn_disc_get_status(drive) != BURN_DISC_EMPTY) { ret= burn_drive_get_write_speed(drive); x_speed_max= ((double) ret)/Cdrskin_libburn_speed_factoR; if(x_speed_min<0) x_speed_min= x_speed_max; } printf("cdrskin: burn_drive_get_write_speed = %d (%.1fx)\n",ret,x_speed_max); if(skin->verbosity>=Cdrskin_verbose_progresS) { if(burn_disc_get_status(drive) == BURN_DISC_EMPTY) printf("Current: none\n"); else if(profile_name[0]) printf("Current: %s\n",profile_name); else if(burn_disc_erasable(drive)) printf("Current: CD-RW\n"); else printf("Current: CD-R\n"); Cdrskin_print_all_profiles(skin, drive, 0); } if(burn_disc_get_status(drive) == BURN_DISC_EMPTY) {ret= 0; goto ex;} if(strstr(profile_name,"DVD")==profile_name) { /* These are dummy messages for project scdbackup, so its media recognition gets a hint that the media is suitable and not in need of blanking. scdbackup will learn to interpret cdrskin's DVD messages but the current stable version needs to believe it is talking to its own growisofs_wrapper. So this is an emulation of an emulator. The real book type is available meanwhile. But that one is not intended. */ printf("book type: %s (emulated booktype)\n", profile_name); if(profile_number==0x13) /* DVD-RW */ printf("cdrskin: message for sdvdbackup: \"(growisofs mode Restricted Overwrite)\"\n"); } else if(strstr(profile_name,"BD")==profile_name) { is_bdr_pow= burn_drive_get_bd_r_pow(drive); printf("Mounted Media: %2.2Xh, %s%s\n", profile_number, profile_name, is_bdr_pow ? ", Pseudo Overwrite formatted" : ""); } else { printf("ATIP info from disk:\n"); if(burn_disc_erasable(drive)) { if(is_not_really_erasable) printf(" Is erasable (but not while in this incomplete state)\n"); else printf(" Is erasable\n"); } else { printf(" Is not erasable\n"); } { int start_lba,end_lba,min,sec,fr; int m_lo, s_lo, f_lo; ret= burn_drive_get_start_end_lba(drive,&start_lba,&end_lba,0); if(ret>0) { burn_lba_to_msf(start_lba,&min,&sec,&fr); printf(" ATIP start of lead in: %d (%-2.2d:%-2.2d/%-2.2d)\n", start_lba,min,sec,fr); burn_lba_to_msf(end_lba,&m_lo,&s_lo,&f_lo); printf(" ATIP start of lead out: %d (%-2.2d:%-2.2d/%-2.2d)\n", end_lba, m_lo, s_lo, f_lo); if(current_is_cd) manuf= burn_guess_cd_manufacturer(min, sec, fr, m_lo, s_lo, f_lo, 0); } } printf(" 1T speed low: %.f 1T speed high: %.f\n",x_speed_min,x_speed_max); } ret= burn_disc_get_media_id(drive, &product_id, &media_code1, &media_code2, &book_type, 0); if(ret > 0 && (!current_is_cd) && manuf == NULL && media_code1 != NULL && media_code2 != NULL) { manuf= burn_guess_manufacturer(profile_number, media_code1, media_code2, 0); } if(product_id != NULL) printf("Product Id: %s\n", product_id); if(manuf != NULL) printf("Producer: %s\n", manuf); if(skin->verbosity >= Cdrskin_verbose_progresS) { if (current_is_cd) { if(manuf != NULL) printf("Manufacturer: %s\n", manuf); } else if(product_id != NULL && media_code1 != NULL && media_code2 != NULL){ free(product_id); free(media_code1); free(media_code2); if(book_type != NULL) free(book_type); product_id= media_code1= media_code2= book_type= NULL; ret= burn_disc_get_media_id(drive, &product_id, &media_code1, &media_code2, &book_type, 1); if(ret > 0) { if(profile_number == 0x11 || profile_number == 0x13 || profile_number == 0x14 || profile_number == 0x15) printf("Manufacturer: '%s'\n", media_code1); else if(profile_number >= 0x40 && profile_number <= 0x43) { printf("Manufacturer: '%s'\n", media_code1); if(media_code2[0]) printf("Media type: '%s'\n", media_code2); } else { printf("Manufacturer: '%s'\n", media_code1); if(media_code2[0]) printf("Media type: '%s'\n", media_code2); } } } } burn_drive_get_media_sno(drive, &sno, &sno_len); if(sno_len > 0) { printf("Media id: "); for(i= 0; i < sno_len && i < 1024; i++) printf("%2.2X", (unsigned int) ((unsigned char *) sno)[i]); if(i < sno_len) printf("..."); printf("\n"); } if(sno != NULL) free(sno); sno= NULL; ret= 1; if(flag&1) ret= Cdrskin_toc(skin, !(flag & 2)); /*cdrecord seems to ignore -toc errors if -atip is ok */ if(ret > 0 && (flag & 4)) ret= Cdrskin_minfo(skin, 0); ex:; if(manuf != NULL) free(manuf); if(media_code1 != NULL) free(media_code1); if(media_code2 != NULL) free(media_code2); if(book_type != NULL) free(book_type); if(product_id != NULL) free(product_id); return(ret); } /** Perform --list_formats @param flag Bitfield for control purposes: @return <=0 error, 1 success */ int Cdrskin_list_formats(struct CdrskiN *skin, int flag) { struct burn_drive *drive; int ret, i, status, num_formats, profile_no, type, alloc_blocks, free_blocks; int is_bdr_pow= 0; off_t size; unsigned dummy; char status_text[80], profile_name[90]; ret= Cdrskin_grab_drive(skin,0); if(ret<=0) return(ret); drive= skin->drives[skin->driveno].drive; ret = burn_disc_get_formats(drive, &status, &size, &dummy, &num_formats); if(ret <= 0) { fprintf(stderr, "cdrskin: SORRY: Cannot obtain format list info\n"); ret= 0; goto ex; } ret= burn_disc_get_profile(drive, &profile_no, profile_name); is_bdr_pow= burn_drive_get_bd_r_pow(drive); printf("Media current: "); if(profile_no > 0 && ret > 0) { if(profile_name[0]) printf("%s%s\n", profile_name, is_bdr_pow ? ", Pseudo Overwrite formatted" : ""); else printf("%4.4Xh\n", profile_no); } else printf("is not recognizable\n"); if(status == BURN_FORMAT_IS_UNFORMATTED) sprintf(status_text, "unformatted, up to %.1f MiB", ((double) size) / 1024.0 / 1024.0); else if(status == BURN_FORMAT_IS_FORMATTED) { if(profile_no==0x12 || profile_no==0x13 || profile_no==0x1a || profile_no==0x43) sprintf(status_text, "formatted, with %.1f MiB", ((double) size) / 1024.0 / 1024.0); else sprintf(status_text, "written, with %.1f MiB", ((double) size) / 1024.0 / 1024.0); } else if(status == BURN_FORMAT_IS_UNKNOWN) { if (profile_no > 0) sprintf(status_text, "intermediate or unknown"); else sprintf(status_text, "no media or unknown media"); } else sprintf(status_text, "illegal status according to MMC-5"); printf("Format status: %s\n", status_text); ret= burn_disc_get_bd_spare_info(drive, &alloc_blocks, &free_blocks, 0); if(ret == 1) printf("BD Spare Area: %d blocks consumed, %d blocks available\n", alloc_blocks - free_blocks, free_blocks); for (i = 0; i < num_formats; i++) { ret= burn_disc_get_format_descr(drive, i, &type, &size, &dummy); if (ret <= 0) continue; printf("Format idx %-2d: %2.2Xh , %.fs , %.1f MiB\n", i, type, ((double) size) / 2048.0, ((double) size) / 1024.0/1024.0); } ret= 1; ex:; return(ret); } /** Perform --list_speeds @param flag Bitfield for control purposes: @return <=0 error, 1 success */ int Cdrskin_list_speeds(struct CdrskiN *skin, int flag) { struct burn_drive *drive; int ret, i, profile_no, high= -1, low= 0x7fffffff, is_cd= 0, is_bdr_pow= 0; char profile_name[90], *speed_unit= "D"; double speed_factor= 1385000.0, cd_factor= 75.0 * 2352; struct burn_speed_descriptor *speed_list= NULL, *item, *other; ret= Cdrskin_grab_drive(skin,0); if(ret<=0) return(ret); drive= skin->drives[skin->driveno].drive; ret= burn_drive_get_speedlist(drive, &speed_list); if(ret <= 0) { fprintf(stderr, "cdrskin: SORRY: Cannot obtain speed list info\n"); ret= 0; goto ex; } ret= burn_disc_get_profile(drive, &profile_no, profile_name); is_bdr_pow= burn_drive_get_bd_r_pow(drive); printf("Media current: "); if(profile_no > 0 && ret > 0) { if(profile_name[0]) printf("%s%s\n", profile_name, is_bdr_pow ? ", Pseudo Overwrite formatted" : ""); else printf("%4.4Xh\n", profile_no); } else printf("is not recognizable\n"); if(profile_no >= 0x08 && profile_no <= 0x0a) is_cd= profile_no; speed_factor= Cdrskin_libburn_speed_factoR * 1000.0; if(Cdrskin_libburn_speed_factoR == Cdrskin_libburn_cd_speed_factoR) speed_unit= "C"; else if(Cdrskin_libburn_speed_factoR == Cdrskin_libburn_bd_speed_factoR) speed_unit= "B"; for (item= speed_list; item != NULL; item= item->next) { if(item->source == 1) { /* CD mode page 2Ah : report only if not same speed by GET PERFORMANCE */ for(other= speed_list; other != NULL; other= other->next) if(other->source == 2 && item->write_speed == other->write_speed) break; if(other != NULL) continue; } printf("Write speed : %5dk , %4.1fx%s\n", item->write_speed, ((double) item->write_speed) * 1000.0 / speed_factor, speed_unit); if(item->write_speed > high) high= item->write_speed; if(item->write_speed < low) low= item->write_speed; } /* Maybe there is ATIP info */ if(is_cd) { ret= burn_disc_read_atip(drive); if(ret < 0) goto ex; if(ret > 0) { for(i= 0; i < 2; i++) { if(i == 0) ret= burn_drive_get_min_write_speed(drive); else ret= burn_drive_get_write_speed(drive); if(ret > 0) { if(ret < low || (i == 0 && ret != low)) { printf("Write speed l: %5dk , %4.1fx%s\n", ret, ((double) ret) * 1000.0 / cd_factor, "C"); low= ret; } if(ret > high || (i == 1 && ret != high)) { printf("Write speed h: %5dk , %4.1fx%s\n", ret, ((double) ret) * 1000.0 / cd_factor, "C"); high= ret; } } } } } if(high > -1) { printf("Write speed L: %5dk , %4.1fx%s\n", low, ((double) low) * 1000.0 / speed_factor, speed_unit); printf("Write speed H: %5dk , %4.1fx%s\n", high, ((double) high) * 1000.0 / speed_factor, speed_unit); ret= burn_drive_get_best_speed(drive, -1, &item, 2); if(ret > 0 && item != NULL) if(item->write_speed != low) printf("Write speed 0: %5dk , %4.1fx%s\n", item->write_speed, ((double) item->write_speed) * 1000.0 / speed_factor, speed_unit); ret= burn_drive_get_best_speed(drive, 0, &item, 2); if(ret > 0 && item != NULL) if(item->write_speed != high) printf("Write speed-1: %5dk , %4.1fx%s\n", item->write_speed, ((double) item->write_speed) * 1000.0 / speed_factor, speed_unit); } else { fprintf(stderr, "cdrskin: SORRY : Could not get any write speed information from drive"); } ret= 1; ex:; if(speed_list != NULL) burn_drive_free_speedlist(&speed_list); return(ret); } /* @param flag: bit0= do not print prefix on first line bit1= plan for multiples of 4 hex words */ static int Feature_printf(char *prefix, char *text, int flag) { char *tpt, *npt; int lp, lp0, line_size; lp0= !(flag & 1); tpt= text; lp= strlen(prefix); /* Plan 2 char shorter for possible comma inclusion */ line_size= 77; if(flag & 2) { line_size= (80 - lp) / 3; line_size-= line_size % 4; line_size= line_size * 3 - 1 + lp; } while(lp * lp0 + strlen(tpt) >= 80) { /* Line size for 20 */ for(npt= tpt + line_size - lp * lp0; npt > tpt; npt--) if(*npt == ' ') break; if(npt <= tpt) { printf("%s%s\n", lp0 ? prefix : "", tpt); return(2); } /* Keep comma in printed line */ if(*(npt + 1) == ',' && *(npt + 1) == ' ') npt+= 2; /* Print and advance */ *npt= 0; printf("%s%s\n", lp0 ? prefix : "", tpt); tpt= npt + 1; lp0= 1; } printf("%s%s\n", lp0 ? prefix : "", tpt); return(1); } /** Perform --list_features @param flag Bitfield for control purposes: @return <=0 error, 1 success */ int Cdrskin_list_features(struct CdrskiN *skin, int flag) { struct burn_drive *d; int ret, count, i, j; unsigned int *feature_codes= NULL; unsigned char flags, additional_length, *feature_data = NULL; char *feature_text = NULL, *cpt, *dpt; ret= Cdrskin_grab_drive(skin,0); if(ret <= 0) return(ret); d= skin->drives[skin->driveno].drive; fprintf(stderr, "SCSI Features reported by the drive:\n"); fprintf(stderr, " + = Current, - = Not Current, P = Persistent, N = Not Persistent\n"); fprintf(stderr, "Code C : Name : Version,Persistent\n"); fprintf(stderr, " Additional data if present (hex bytes)\n"); fprintf(stderr, " Parsed info if available (fieldname=content)\n"); fprintf(stderr, "\n"); burn_drive_get_feature_codes(d, &count, &feature_codes); if(count <= 0) { fprintf(stderr, "cdrskin: NOTE: The drive feature list is empty\n"); ret= 1; goto ex; } for(i= 0; i < count; i++) { ret= burn_drive_get_feature(d, feature_codes[i], &flags, &additional_length, &feature_data, &feature_text); if(ret <= 0) { fprintf(stderr, "cdrskin: SORRY: Cannot obtain data of feature %4.4x\n", feature_codes[i]); continue; } cpt= feature_text; for(j= 0; j < 3 && cpt != NULL; j++) cpt= strchr(cpt + 1, ':'); if(cpt != NULL) { *cpt= 0; cpt++; } Feature_printf(" ", feature_text, 1); if(cpt != NULL) { dpt= cpt; if(*dpt == ' ') dpt++; for(j= 0; j < 1 && cpt != NULL; j++) cpt= strchr(cpt + 1, ':'); if(cpt != NULL) { *cpt= 0; cpt++; } Feature_printf(" ", dpt, 2); } if(cpt != NULL) { if(*cpt == ' ') cpt++; Feature_printf(" ", cpt, 0); } else { printf("\n"); } if(feature_data != NULL) free(feature_data); feature_data= NULL; if(feature_text != NULL) free(feature_text); feature_text= NULL; } ret= 1; ex:; if(feature_codes != NULL) free(feature_codes); return(ret); } int Cdrskin_read_textfile(struct CdrskiN *skin, char *path, int flag) { int ret, num_packs = 0; unsigned char *text_packs = NULL; ret= burn_cdtext_from_packfile(path, &text_packs, &num_packs, 0); if(ret <= 0) goto ex; if(skin->text_packs != NULL) free(skin->text_packs); skin->text_packs= text_packs; skin->num_text_packs= num_packs; ret= 1; ex:; if(ret <= 0 && text_packs != NULL) free(text_packs); return(ret); } /* @param flag Bitfield for control purposes: bit3= Enable DAP : "flaw obscuring mechanisms like audio data mute and interpolate" */ int Cdrskin_extract_audio_to_dir(struct CdrskiN *skin, char *dir, char *basename, char print_tracks[100], int flag) { int num_sessions= 0, num_tracks= 0, profile_number, session_no, track_no; int track_count= 0, existing= 0, ret, pass, not_audio= 0, pick_tracks= 0, i; int tracks_extracted= 0, min_tno= 100, max_tno= 0; struct burn_drive *drive; struct burn_disc *disc= NULL; struct burn_session **sessions; struct burn_track **tracks; enum burn_disc_status s; struct burn_toc_entry toc_entry; struct stat stbuf; char profile_name[80], path[4096 + 256]; ret= Cdrskin_grab_drive(skin, 0); if(ret<=0) goto ex; drive= skin->drives[skin->driveno].drive; s= burn_disc_get_status(drive); disc= burn_drive_get_disc(drive); if(s == BURN_DISC_EMPTY || s == BURN_DISC_BLANK || disc == NULL) { not_readable:; fprintf(stderr, "cdrskin: SORRY : No audio CD in drive.\n"); ret= 0; goto ex; } ret= burn_disc_get_profile(drive, &profile_number, profile_name); if(ret <= 0 || profile_number < 0x08 || profile_number > 0x0a) { not_audio_cd:; fprintf(stderr, "cdrskin: SORRY : Medium in drive is not an audio CD.\n"); ret= 0; goto ex; } for(i= 0; i < 99; i++) if(print_tracks[i]) pick_tracks= 1; sessions= burn_disc_get_sessions(disc, &num_sessions); if(num_sessions <= 0) goto not_readable; for(pass= 0; pass < 2; pass++) { if(pass > 0) { if(existing > 0) {ret= 0; goto ex;} if(not_audio == track_count) goto not_audio_cd; } track_count= 0; for(session_no= 0; session_no < num_sessions; session_no++) { tracks= burn_session_get_tracks(sessions[session_no],&num_tracks); if(tracks==NULL) continue; for(track_no= 0; track_no < num_tracks; track_no++) { track_count++; burn_track_get_entry(tracks[track_no], &toc_entry); if(toc_entry.point < min_tno) min_tno= toc_entry.point; if(toc_entry.point > max_tno) max_tno= toc_entry.point; if(pick_tracks) { if(toc_entry.point <= 0 || toc_entry.point > 99) continue; /* should not happen */ if(print_tracks[toc_entry.point] == 0) continue; } if((toc_entry.control & 7) >= 4) { if(pass == 0) fprintf(stderr, "cdrskin: WARNING : Track %2.2d is not an audio track.\n", toc_entry.point); not_audio++; continue; } sprintf(path, "%s/%s%2.2u.wav", dir, basename, toc_entry.point); if(pass == 0) { if(stat(path, &stbuf) != -1) { fprintf(stderr, "cdrskin: SORRY : May not overwrite existing file: '%s'\n", path); existing++; } continue; } if(skin->verbosity >= Cdrskin_verbose_progresS) fprintf(stderr, "cdrskin: Writing audio track file: %s\n", path); ret= burn_drive_extract_audio_track(drive, tracks[track_no], path, (flag & 8) | (skin->verbosity >= Cdrskin_verbose_progresS)); if(ret <= 0) goto ex; tracks_extracted++; } } } if(tracks_extracted == 0 && pick_tracks) { fprintf(stderr, "cdrskin: SORRY : Not a single track matched the list of extract_tracks=\n"); if(min_tno < 100 && max_tno > 0) fprintf(stderr, "cdrskin: HINT : Track range is %2.2d to %2.2d\n", min_tno, max_tno); ret= 0; goto ex; } ret= 1; ex:; return(ret); } #ifndef Cdrskin_extra_leaN /* A70324: proposal by Eduard Bloch */ int Cdrskin_warn_of_mini_tsize(struct CdrskiN *skin, int flag) { off_t media_space= 0; enum burn_disc_status s; struct burn_drive *drive; int ret; ret= burn_drive_get_drive_role(skin->drives[skin->driveno].drive); if(ret != 1) return(1); if(skin->multi || skin->has_open_ended_track || skin->smallest_tsize<0) return(1); drive= skin->drives[skin->driveno].drive; s= burn_disc_get_status(drive); if(s!=BURN_DISC_BLANK) return(1); media_space= burn_disc_available_space(drive, NULL); if(media_space<=0 || skin->smallest_tsize >= media_space / Cdrskin_minimum_tsize_quotienT) return(1); fprintf(stderr,"\n"); fprintf(stderr,"\n"); fprintf(stderr, "cdrskin: WARNING: Very small track size set by option tsize=\n"); fprintf(stderr, "cdrskin: Track size %.1f MB <-> media capacity %.1f MB\n", skin->smallest_tsize/1024.0/1024.0, ((double) media_space)/1024.0/1024.0); fprintf(stderr,"\n"); fprintf(stderr, "cdrskin: Will wait at least 15 seconds until real burning starts\n"); fprintf(stderr,"\n"); if(skin->gracetime<15) skin->gracetime= 15; return(1); } /** Emulate the gracetime= behavior of cdrecord @param flag Bitfield for control purposes: bit0= do not print message about pseudo-checkdrive */ int Cdrskin_wait_before_action(struct CdrskiN *skin, int flag) /* flag: bit0= BLANK rather than write mode bit1= FORMAT rather than write mode */ { int i; Cdrskin_warn_of_mini_tsize(skin,0); if(skin->verbosity>=Cdrskin_verbose_progresS) { char speed_text[80]; if(skin->x_speed<0) strcpy(speed_text,"MAX"); else if(skin->x_speed==0) strcpy(speed_text,"MIN"); else sprintf(speed_text,"%.f",skin->x_speed); printf( "Starting to write CD/DVD at speed %s in %s %s mode for %s session.\n", speed_text,(skin->dummy_mode?"dummy":"real"), (flag&2?"FORMAT":(flag&1?"BLANK":skin->preskin->write_mode_name)), (skin->multi?"multi":"single")); printf("Last chance to quit, starting real write in %3d seconds.", skin->gracetime); fflush(stdout); } for(i= skin->gracetime-1;i>=0;i--) { usleep(1000000); if(Cdrskin__is_aborting(0)) return(0); if(skin->verbosity>=Cdrskin_verbose_progresS) { printf("\b\b\b\b\b\b\b\b\b\b\b\b\b %3d seconds.",i); fflush(stdout); } } if(skin->verbosity>=Cdrskin_verbose_progresS) {printf(" Operation starts.\n");fflush(stdout);} return(1); } #endif /* Cdrskin_extra_leaN */ /** Perform blank=[all|fast] @return <=0 error, 1 success */ int Cdrskin_blank(struct CdrskiN *skin, int flag) { enum burn_disc_status s; struct burn_progress p; struct burn_drive *drive; int ret,loop_counter= 0,hint_force= 0,do_format= 0, profile_number= -1; int wrote_well= 1, format_flag= 0, status, num_formats, using_immed= 1; off_t size; unsigned dummy; double start_time; char *verb= "blank", *presperf="blanking", *fmt_text= "..."; char profile_name[80], progress_text[40]; static char fmtp[][40]= { "...", "format_overwrite", "deformat_sequential", "(-format)", "format_defectmgt", "format_by_index", "format_if_needed", "as_needed"}; static int fmtp_max= 7; start_time= Sfile_microtime(0); /* will be refreshed later */ ret= Cdrskin_grab_drive(skin,0); if(ret<=0) return(ret); drive= skin->drives[skin->driveno].drive; s= burn_disc_get_status(drive); profile_name[0]= 0; if(skin->grabbed_drive) burn_disc_get_profile(skin->grabbed_drive,&profile_number,profile_name); ret= Cdrskin_report_disc_status(skin,s, 1|(4*!(skin->verbosity>=Cdrskin_verbose_progresS))); if(ret==2) s= BURN_DISC_APPENDABLE; do_format= skin->blank_format_type & 0xff; if(s==BURN_DISC_UNSUITABLE) { if(skin->force_is_set) { ClN(fprintf(stderr,"cdrskin: NOTE : -force blank=... : Treating unsuitable media as burn_disc_full\n")); ret= burn_disc_pretend_full(drive); if(ret <= 0) { ClN(fprintf(stderr, "cdrskin: FAILURE : Medium still considered unsuitable\n")); {ret= 0; goto ex;} } s= burn_disc_get_status(drive); } else hint_force= 1; } if(do_format) if(do_format>=0 && do_format<=fmtp_max) fmt_text= fmtp[do_format]; if(do_format==5) { /* format_by_index */ if(profile_number == 0x12 || profile_number == 0x43) do_format= 4; else do_format= 1; } else if(do_format==6 || do_format==7) { /* format_if_needed , as_needed */ /* Find out whether format is needed at all. Eventuelly set up a suitable formatting run */ if(profile_number == 0x14 && do_format==6) { /* sequential DVD-RW */ do_format= 1; skin->blank_format_type= 1|(1<<8); skin->blank_format_size= 128*1024*1024; } else if(profile_number == 0x12 || profile_number == 0x43 || (profile_number == 0x41 && do_format==6)) { /* DVD-RAM , BD-RE , BD-R SRM */ ret= burn_disc_get_formats(drive, &status, &size, &dummy, &num_formats); if((ret>0 && status!=BURN_FORMAT_IS_FORMATTED)) { do_format= 4; skin->blank_format_type= 4|(3<<9); /* default payload size */ skin->blank_format_size= 0; } } else if(do_format==7) { /* try to blank what is not blank yet */ if(s!=BURN_DISC_BLANK) { do_format= 0; skin->blank_fast= 1; } } if(do_format==6 || do_format==7) { if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(fprintf(stderr, "cdrskin: NOTE : blank=%s : no need for action detected\n", fmt_text)); {ret= 2; goto ex;} } } if(do_format == 1 || do_format == 3 || do_format == 4) { verb= "format"; presperf= "formatting"; } if(do_format==2) { /* Forceful blanking to Sequential Recording for DVD-R[W] and CD-RW */ if(!(profile_number == 0x14 || profile_number == 0x13 || profile_number == 0x0a)) { if(skin->grow_overwriteable_iso>0 && skin->media_is_overwriteable) goto pseudo_blank_ov; else goto unsupported_format_type; } } else if(do_format==1 || do_format==3) { /* Formatting to become overwriteable for DVD-RW and DVD+RW */ if(do_format==3 && profile_number != 0x1a) { fprintf(stderr, "cdrskin: SORRY : -format does DVD+RW only\n"); if(profile_number==0x14) fprintf(stderr, "cdrskin: HINT : blank=format_overwrite would format this media\n"); {ret= 0; goto ex;} } if(profile_number == 0x14) { /* DVD-RW sequential */ /* ok */; } else if(profile_number == 0x13) { /* DVD-RW restricted overwrite */ if(!(skin->force_is_set || ((skin->blank_format_type>>8)&4))) { fprintf(stderr, "cdrskin: NOTE : blank=format_... : media is already formatted\n"); fprintf(stderr, "cdrskin: HINT : If you really want to re-format, add option -force\n"); {ret= 2; goto ex;} } } else if(profile_number == 0x1a) { /* DVD+RW */ if(!((skin->blank_format_type>>8)&4)) { fprintf(stderr, "cdrskin: NOTE : blank=format_... : DVD+RW do not need this\n"); fprintf(stderr, "cdrskin: HINT : For de-icing use option blank=format_overwrite_full\n"); {ret= 2; goto ex;} } } else { fprintf(stderr, "cdrskin: SORRY : blank=%s for now does DVD-RW and DVD+RW only\n", fmt_text); {ret= 0; goto ex;} } if(s==BURN_DISC_UNSUITABLE) fprintf(stderr, "cdrskin: NOTE : blank=%s accepted not yet suitable media\n", fmt_text); } else if(do_format==4) { /* Formatting and influencing defect management of DVD-RAM , BD-RE */ if(!(profile_number == 0x12 || profile_number == 0x41 || profile_number == 0x43)) { fprintf(stderr, "cdrskin: SORRY : blank=%s for now does DVD-RAM and BD only\n", fmt_text); {ret= 0; goto ex;} } if(s==BURN_DISC_UNSUITABLE) fprintf(stderr, "cdrskin: NOTE : blank=%s accepted not yet suitable media\n", fmt_text); } else if(do_format==0) { /* Classical blanking of erasable media */ if(skin->grow_overwriteable_iso>0 && skin->media_is_overwriteable) { pseudo_blank_ov:; if(skin->dummy_mode) { fprintf(stderr, "cdrskin: would have begun to pseudo-blank disc if not in -dummy mode\n"); goto blanking_done; } skin->grow_overwriteable_iso= 3; ret= Cdrskin_invalidate_iso_head(skin, 0); if(ret<=0) goto ex; goto blanking_done; } else if(s!=BURN_DISC_FULL && (s!=BURN_DISC_APPENDABLE || skin->no_blank_appendable) && (profile_number!=0x13 || !skin->prodvd_cli_compatible) && (s!=BURN_DISC_BLANK || !skin->force_is_set)) { if(s==BURN_DISC_BLANK) { fprintf(stderr, "cdrskin: NOTE : blank=... : media was already blank (and still is)\n"); {ret= 2; goto ex;} } else if(s==BURN_DISC_APPENDABLE) { fprintf(stderr, "cdrskin: FATAL : blank=... : media is still appendable\n"); } else { fprintf(stderr, "cdrskin: FATAL : blank=... : no blankworthy disc found\n"); if(hint_force) fprintf(stderr, "cdrskin: HINT : If you are certain to have a CD-RW, try option -force\n"); } {ret= 0; goto ex;} } if(!burn_disc_erasable(drive)) { fprintf(stderr,"cdrskin: FATAL : blank=... : media is not erasable\n"); {ret= 0; goto ex;} } if((profile_number == 0x14 || profile_number == 0x13) && !skin->prodvd_cli_compatible) skin->blank_fast= 0; /* only with deformat_sequential_quickest */ } else { unsupported_format_type:; fprintf(stderr, "cdrskin: SORRY : blank=%s is unsupported with media type %s\n", fmt_text, profile_name); {ret= 0; goto ex;} } if(skin->dummy_mode) { fprintf(stderr, "cdrskin: would have begun to %s disc if not in -dummy mode\n", verb); goto blanking_done; } fprintf(stderr,"cdrskin: beginning to %s disc\n",verb); Cdrskin_adjust_speed(skin,0); #ifndef Cdrskin_extra_leaN Cdrskin_wait_before_action(skin, 1+(do_format==1 || do_format==3 || do_format==4)); #endif /* ! Cdrskin_extra_leaN */ if(Cdrskin__is_aborting(0)) {ret= 0; goto ex;} using_immed= burn_drive_get_immed(drive); skin->drive_is_busy= 1; if(do_format==0 || do_format==2) { burn_disc_erase(drive,skin->blank_fast); } else if(do_format==1 || do_format==3 || do_format==4) { format_flag= (skin->blank_format_type>>8)&(1|2|4|32|128); if(skin->force_is_set) format_flag|= 16; if(format_flag&128) format_flag|= (skin->blank_format_index&255)<<8; if(skin->blank_format_no_certify) format_flag|= 64; burn_disc_format(drive,(off_t) skin->blank_format_size,format_flag); } else { fprintf(stderr,"cdrskin: SORRY : Format type %d not implemented yet.\n", do_format); ret= 0; goto ex; } loop_counter= 0; start_time= Sfile_microtime(0); if(!using_immed) sprintf(progress_text, "synchronous"); while(burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) { if(loop_counter>0) if(skin->verbosity>=Cdrskin_verbose_progresS) { double percent= 50.0; if(p.sectors>0) /* i want a display of 1 to 99 percent */ percent= 1.0+((double) p.sector+1.0)/((double) p.sectors)*98.0; if(using_immed) sprintf(progress_text, "done %.1f%%", percent); fprintf(stderr, "%scdrskin: %s ( %s , %lu seconds elapsed ) %s", skin->pacifier_with_newline ? "" : "\r", presperf, progress_text, (unsigned long) (Sfile_microtime(0)-start_time), skin->pacifier_with_newline ? "\n" : ""); } sleep(1); loop_counter++; } blanking_done:; wrote_well = burn_drive_wrote_well(drive); if(wrote_well && skin->verbosity>=Cdrskin_verbose_progresS) { fprintf(stderr, "%scdrskin: %s done \n", skin->pacifier_with_newline ? "" : "\r", presperf); printf("%s time: %.3fs\n", (do_format==1 || do_format==3 || do_format==4 ? "Formatting":"Blanking"), Sfile_microtime(0)-start_time); } fflush(stdout); if(!wrote_well) fprintf(stderr, "%scdrskin: %s failed \n", skin->pacifier_with_newline ? "" : "\r", presperf); ret= !!(wrote_well); ex:; skin->drive_is_busy= 0; if(Cdrskin__is_aborting(0)) Cdrskin_abort(skin, 0); /* Never comes back */ return(ret); } int Cdrskin__libburn_fifo_status(struct burn_source *current_fifo, char fifo_text[80], int flag) { int ret, size, free_space, fill, fifo_percent; char *status_text= ""; ret= burn_fifo_inquire_status(current_fifo, &size, &free_space, &status_text); if(ret <= 0 || ret >= 4) { strcpy(fifo_text, "(fifo 0%) "); } else if(ret == 1) { burn_fifo_next_interval(current_fifo, &fill); fifo_percent= 100.0 * ((double) fill) / (double) size; if(fifo_percent<100 && fill>0) fifo_percent++; sprintf(fifo_text, "(fifo %3d%%) ", fifo_percent); } return(1); } /** Report burn progress. This is done partially in cdrecord style. Actual reporting happens only if write progress hit the next MB or if in non-write-progress states a second has elapsed since the last report. After an actual report a new statistics interval begins. @param drive_status As obtained from burn_drive_get_status() @param p Progress information from burn_drive_get_status() @param start_time Timestamp of burn start in seconds @param last_time Timestamp of report interval start in seconds @param total_count Returns the total number of bytes written so far @param total_count Returns the number of bytes written during interval @param flag Bitfield for control purposes: bit0= report in growisofs style rather than cdrecord style @return <=0 error, 1 seems to be writing payload, 2 doing something else */ int Cdrskin_burn_pacifier(struct CdrskiN *skin, int start_tno, enum burn_drive_status drive_status, struct burn_progress *p, double start_time, double *last_time, double *total_count, double *last_count, int *min_buffer_fill, int flag) /* bit0= growisofs style */ { double bytes_to_write= 0.0; double written_bytes= 0.0,written_total_bytes= 0.0; double fixed_size= 0.0, padding,sector_size,speed_factor; double measured_total_speed,measured_speed; double elapsed_time,elapsed_total_time,current_time; double estim_time,estim_minutes,estim_seconds,percent; int ret,fifo_percent,fill,advance_interval=0,new_mb,old_mb,time_to_tell; int old_track_idx,buffer_fill,formatting= 0,use_data_image_size; char fifo_text[80],mb_text[40], pending[40]; char *debug_mark= ""; /* use this to prepend a marker text for experiments */ #ifndef Cdrskin_no_cdrfifO double buffer_size; int fs, bs, space; #endif #ifdef Cdrskin_use_libburn_fifO struct burn_source *current_fifo= NULL; #ifdef NIX int size, free_space; char *status_text= ""; #endif #endif /* Cdrskin_use_libburn_fifO */ /* for debugging */ static double last_fifo_in= 0.0,last_fifo_out= 0.0,curr_fifo_in,curr_fifo_out; if(Cdrskin__is_aborting(0)) Cdrskin_abort(skin, 0); /* Never comes back */ current_time= Sfile_microtime(0); elapsed_total_time= current_time-start_time; elapsed_time= current_time-*last_time; time_to_tell= (elapsed_time>=1.0)&&(elapsed_total_time>=1.0); written_total_bytes= *last_count; /* to be overwritten by p.sector */ if(drive_status==BURN_DRIVE_FORMATTING) formatting= 1; if(drive_status==BURN_DRIVE_WRITING) { ; } else if(drive_status==BURN_DRIVE_WRITING_LEADIN || drive_status==BURN_DRIVE_WRITING_PREGAP || formatting) { if(time_to_tell || skin->is_writing) { if(skin->verbosity>=Cdrskin_verbose_progresS) { if(skin->is_writing) fprintf(stderr,"\n"); fprintf(stderr, "%scdrskin: %s (burning since %.f seconds)%s", skin->pacifier_with_newline ? "" : "\r", (formatting?"formatting":"working pre-track"), elapsed_total_time, skin->pacifier_with_newline ? "\n" : " "); } skin->is_writing= 0; advance_interval= 1; } {ret= 2; goto ex;} } else if(drive_status==BURN_DRIVE_WRITING_LEADOUT || drive_status==BURN_DRIVE_CLOSING_TRACK || drive_status==BURN_DRIVE_CLOSING_SESSION) { if(drive_status==BURN_DRIVE_CLOSING_SESSION && skin->previous_drive_status!=drive_status) {printf("\nFixating...\n"); fflush(stdout);} if(time_to_tell || skin->is_writing) { if(skin->verbosity>=Cdrskin_verbose_progresS) { if(skin->is_writing && !skin->pacifier_with_newline) fprintf(stderr,"\n"); fprintf(stderr, "%scdrskin: working post-track (burning since %.f seconds)%s", skin->pacifier_with_newline ? "" : "\r", elapsed_total_time, skin->pacifier_with_newline ? "\n" : " "); } skin->is_writing= 0; advance_interval= 1; } {ret= 2; goto ex;} } else goto thank_you_for_patience; old_track_idx= skin->supposed_track_idx; skin->supposed_track_idx= p->track; if(old_track_idx>=0 && old_track_idxsupposed_track_idx && skin->supposed_track_idx < skin->track_counter) { Cdrtrack_get_size(skin->tracklist[old_track_idx],&fixed_size,&padding, §or_size,&use_data_image_size,1); if(skin->verbosity>=Cdrskin_verbose_progresS) printf("\n"); printf("%sTrack %-2.2d: Total bytes read/written: %.f/%.f (%.f sectors).\n", debug_mark, old_track_idx + start_tno, fixed_size,fixed_size+padding, (fixed_size+padding)/sector_size); *last_count= 0; } sector_size= 2048.0; if(skin->supposed_track_idx>=0 && skin->supposed_track_idxtrack_counter) Cdrtrack_get_size(skin->tracklist[skin->supposed_track_idx],&fixed_size, &padding,§or_size,&use_data_image_size,0); bytes_to_write= ((double) p->sectors)*sector_size; written_total_bytes= ((double) p->sector)*sector_size; written_bytes= written_total_bytes-*last_count; if(written_total_bytes<1024*1024) { thank_you_for_patience:; if(time_to_tell || (skin->is_writing && elapsed_total_time>=1.0)) { if(skin->verbosity>=Cdrskin_verbose_progresS) { if(skin->is_writing) { fprintf(stderr,"\n"); } pending[0]= 0; /* if(bytes_to_write > 0 && skin->verbosity >= Cdrskin_verbose_debuG) sprintf(pending, " pnd %.f", bytes_to_write - written_total_bytes); */ fprintf(stderr, "%scdrskin: thank you for being patient for %.f seconds%21.21s%s", skin->pacifier_with_newline ? "" : "\r", elapsed_total_time, pending, skin->pacifier_with_newline ? "\n" : ""); } advance_interval= 1; } skin->is_writing= 0; {ret= 2; goto ex;} } new_mb= written_total_bytes/(1024*1024); old_mb= (*last_count)/(1024*1024); if(new_mb==old_mb && !(written_total_bytes>=skin->fixed_size && skin->fixed_size>0 && time_to_tell)) {ret= 1; goto ex;} #ifndef Cdrskin_extra_leaN percent= 0.0; if(bytes_to_write>0) percent= written_total_bytes/bytes_to_write*100.0; measured_total_speed= 0.0; measured_speed= 0.0; estim_time= -1.0; estim_minutes= -1.0; estim_seconds= -1.0; if(elapsed_total_time>0.0) { measured_total_speed= written_total_bytes/elapsed_total_time; estim_time= (bytes_to_write-written_bytes)/measured_total_speed; if(estim_time>0.0 && estim_time<86400.0) { estim_minutes= ((int) estim_time)/60; estim_seconds= estim_time-estim_minutes*60.0; if(estim_seconds<0.0) estim_seconds= 0.0; } } if(written_bytes==written_total_bytes && elapsed_total_time>0) { measured_speed= measured_total_speed; } else if(elapsed_time>0.0) { measured_speed= written_bytes/elapsed_time; } else if(written_bytes>0.0) { measured_speed= 99.91*Cdrskin_speed_factoR; } if(measured_speed<=0.0 && written_total_bytes>=skin->fixed_size && skin->fixed_size>0) { if(!skin->is_writing) goto thank_you_for_patience; skin->is_writing= 0; measured_speed= measured_total_speed; } else skin->is_writing= 1; if(skin->supposed_track_idx<0) skin->supposed_track_idx= 0; if(*last_count <= 0.0 && !skin->pacifier_with_newline) printf("\r%-78.78s\r", ""); /* wipe output line */ if(skin->verbosity>=Cdrskin_verbose_progresS) { if(flag&1) { printf("%.f/%.f (%2.1f%%) @%1.1f, remaining %.f:%2.2d\n", written_total_bytes,bytes_to_write,percent, measured_speed/Cdrskin_speed_factoR, estim_minutes,(int) estim_seconds); } else { fill= 0; fifo_percent= 50; fifo_text[0]= 0; curr_fifo_in= last_fifo_in; curr_fifo_out= last_fifo_out; if(skin->cuefile_fifo != NULL) Cdrskin__libburn_fifo_status(skin->cuefile_fifo, fifo_text, 0); #ifdef Cdrskin_use_libburn_fifO /* Inquire fifo fill and format fifo pacifier text */ if(skin->fifo == NULL && skin->supposed_track_idx >= 0 && skin->supposed_track_idx < skin->track_counter && skin->fifo_size > 0 && fifo_text[0] == 0) { Cdrtrack_get_libburn_fifo(skin->tracklist[skin->supposed_track_idx], ¤t_fifo, 0); if(current_fifo != NULL) { Cdrskin__libburn_fifo_status(current_fifo, fifo_text, 0); } else if(skin->fifo_size > 0) { strcpy(fifo_text, "(fifo 100%) "); } } #endif /* Cdrskin_use_libburn_fifO */ #ifndef Cdrskin_no_cdrfifO if(skin->fifo!=NULL) { ret= Cdrfifo_get_buffer_state(skin->fifo,&fill,&space,0); buffer_size= fill+space; if(ret==2 || ret==0) { fifo_percent= 100; } else if(ret>0 && buffer_size>0.0) { /* obtain minimum fill of pacifier interval */ Cdrfifo_next_interval(skin->fifo,&fill,0); fifo_percent= 100.0*((double) fill)/buffer_size; if(fifo_percent<100 && fill>0) fifo_percent++; } if(skin->verbosity>=Cdrskin_verbose_debuG) { Cdrfifo_get_counters(skin->fifo,&curr_fifo_in,&curr_fifo_out,0); Cdrfifo_get_sizes(skin->fifo,&bs,&fs,0); } if(skin->fifo_size>0) { sprintf(fifo_text,"(fifo %3d%%) ",fifo_percent); if(skin->verbosity>=Cdrskin_verbose_debug_fifO) { fprintf(stderr, "\ncdrskin_debug: fifo >= %9d / %d : %8.f in, %8.f out\n", fill,(int) buffer_size, curr_fifo_in-last_fifo_in,curr_fifo_out-last_fifo_out); last_fifo_in= curr_fifo_in; last_fifo_out= curr_fifo_out; } } } #endif /* ! Cdrskin_no_cdrfifO */ if(skin->supposed_track_idx >= 0 && skin->supposed_track_idx < skin->track_counter) { /* fixed_size,padding are fetched above via Cdrtrack_get_size() */; } else if(skin->fixed_size!=0) { fixed_size= skin->fixed_size; padding= skin->padding; } if(fixed_size || (skin->fill_up_media && skin->supposed_track_idx==skin->track_counter-1)) { sprintf(mb_text,"%4d of %4d",(int) (written_total_bytes/1024.0/1024.0), (int) ((double) Cdrtrack_get_sectors( skin->tracklist[skin->supposed_track_idx],0)* sector_size/1024.0/1024.0)); } else sprintf(mb_text,"%4d",(int) (written_total_bytes/1024.0/1024.0)); speed_factor= Cdrskin_speed_factoR*sector_size/2048; buffer_fill= 50; if(p->buffer_capacity>0) buffer_fill= (double) (p->buffer_capacity - p->buffer_available)*100.0 / (double) p->buffer_capacity; if(p->buffered_bytes > p->buffer_capacity) if(buffer_fill < *min_buffer_fill) *min_buffer_fill= buffer_fill; printf("%s%sTrack %-2.2d: %s MB written %s[buf %3d%%] %4.1fx.%s", skin->pacifier_with_newline ? "" : "\r", debug_mark, skin->supposed_track_idx + start_tno, mb_text, fifo_text, buffer_fill, measured_speed / speed_factor, skin->pacifier_with_newline ? "\n" : ""); fflush(stdout); } if(skin->is_writing==0) { printf("\n"); goto thank_you_for_patience; } } #else /* ! Cdrskin_extra_leaN */ if(skin->supposed_track_idx<0) skin->supposed_track_idx= 0; if(written_bytes<=0.0 && written_total_bytes>=skin->fixed_size && skin->fixed_size>0) { if(!skin->is_writing) goto thank_you_for_patience; skin->is_writing= 0; } else { if((!skin->is_writing) && !skin->pacifier_with_newline) printf("\n"); skin->is_writing= 1; } printf("%sTrack %-2.2d: %3d MB written %s", skin->pacifier_with_newline ? "" : "\r", skin->supposed_track_idx + start_tno, (int) (written_total_bytes / 1024.0 / 1024.0), skin->pacifier_with_newline ? "\n" : ""); if(skin->is_writing == 0 && !skin->pacifier_with_newline) printf("\n"); fflush(stdout); #endif /* Cdrskin_extra_leaN */ advance_interval= 1; ret= 1; ex:; if(advance_interval) { if(written_total_bytes>0) *last_count= written_total_bytes; else *last_count= 0.0; if(*last_count>*total_count) *total_count= *last_count; *last_time= current_time; } skin->previous_drive_status= drive_status; return(ret); } /** After everything else about burn_write_opts and burn_disc is set up, this call determines the effective write mode and checks whether the drive promises to support it. */ int Cdrskin_activate_write_mode(struct CdrskiN *skin, struct burn_write_opts *opts, struct burn_disc *disc, int flag) { int profile_number= -1, ret, was_still_default= 0; char profile_name[80], reasons[BURN_REASONS_LEN]; enum burn_disc_status s= BURN_DISC_UNGRABBED; enum burn_write_types wt; profile_name[0]= 0; if(skin->grabbed_drive) { burn_disc_get_profile(skin->grabbed_drive,&profile_number,profile_name); s= burn_disc_get_status(skin->grabbed_drive); } if(strcmp(skin->preskin->write_mode_name,"DEFAULT")==0) { was_still_default= 1; wt= burn_write_opts_auto_write_type(opts, disc, reasons, 0); if(wt==BURN_WRITE_NONE) { if(strncmp(reasons,"MEDIA: ",7)==0) ret= -1; else ret= 0; goto report_failure; } skin->write_type= wt; #ifndef Cdrskin_disable_raw96R if(wt==BURN_WRITE_RAW) strcpy(skin->preskin->write_mode_name,"RAW/RAW96R"); else #endif /* ! Cdrskin_disable_raw96R */ if(wt==BURN_WRITE_TAO) strcpy(skin->preskin->write_mode_name,"TAO"); else if(wt==BURN_WRITE_SAO) strcpy(skin->preskin->write_mode_name,"SAO"); else sprintf(skin->preskin->write_mode_name,"LIBBURN/%d", (int) wt); } #ifndef Cdrskin_disable_raw96R if(strcmp(skin->preskin->write_mode_name,"RAW/RAW96R")==0) { skin->write_type= BURN_WRITE_RAW; skin->block_type= BURN_BLOCK_RAW96R; } else #endif /* ! Cdrskin_disable_raw96R */ if(strcmp(skin->preskin->write_mode_name,"TAO")==0) { skin->write_type= BURN_WRITE_TAO; skin->block_type= BURN_BLOCK_MODE1; } else if(strncmp(skin->preskin->write_mode_name,"LIBBURN/",8)==0) { skin->block_type= BURN_BLOCK_MODE1; } else { strcpy(skin->preskin->write_mode_name,"SAO"); skin->write_type= BURN_WRITE_SAO; skin->block_type= BURN_BLOCK_SAO; /* cdrecord tolerates the combination of -sao and -multi. -multi wins. */ burn_write_opts_set_write_type(opts,skin->write_type,skin->block_type); ret = burn_precheck_write(opts,disc,reasons,0); if(ret <= 0) { if(strstr(reasons, "multi session capability lacking") != NULL) { fprintf(stderr,"cdrskin: WARNING : Cannot do SAO/DAO. Reason: %s\n", reasons); fprintf(stderr,"cdrskin: Defaulting to TAO/Incremental.\n"); skin->write_type= BURN_WRITE_TAO; skin->block_type= BURN_BLOCK_MODE1; } } } if(!was_still_default) burn_write_opts_set_write_type(opts,skin->write_type,skin->block_type); ret = burn_precheck_write(opts,disc,reasons,0); if(ret<=0) { report_failure:; if(ret!=-1) fprintf(stderr,"cdrskin: Reason: %s\n",reasons); fprintf(stderr,"cdrskin: Media : %s%s\n", s==BURN_DISC_BLANK?"blank ": s==BURN_DISC_APPENDABLE?"appendable ": s==BURN_DISC_FULL?"** closed ** ":"", profile_name[0]?profile_name: s==BURN_DISC_EMPTY?"no media":"unknown media"); return(0); } if(skin->verbosity>=Cdrskin_verbose_cmD) printf("cdrskin: Write type : %s\n", skin->preskin->write_mode_name); return(1); } #ifndef Cdrskin_extra_leaN int Cdrskin_announce_tracks(struct CdrskiN *skin, int start_tno, int flag) { int i,mb,use_data_image_size; double size,padding,sector_size= 2048.0; double sectors; if(skin->verbosity>=Cdrskin_verbose_progresS) { if(start_tno < 1) start_tno= 1; for(i=0;itrack_counter;i++) { Cdrtrack_get_size(skin->tracklist[i],&size,&padding,§or_size, &use_data_image_size,0); if(size<=0) { printf("Track %-2.2d: %s unknown length", i + start_tno, (sector_size==2048?"data ":"audio")); } else { mb= size/1024.0/1024.0; printf("Track %-2.2d: %s %4d MB ", i + start_tno, (sector_size==2048?"data ":"audio"), mb); } if(padding>0) printf(" padsize: %.f KB\n",padding/1024.0); else printf("\n"); } if(skin->fixed_size<=0) { printf("Total size: 0 MB (00:00.00) = 0 sectors\n"); printf("Lout start: 0 MB (00:02/00) = 0 sectors\n"); } else { /* >>> This is quite a fake. Need to learn about 12:35.25 and "Lout" ??? Is there a way to obtain the toc in advance (print_cue()) ? */ double seconds; int min,sec,frac; mb= skin->fixed_size/1024.0/1024.0; seconds= skin->fixed_size/150.0/1024.0+2.0; min= seconds/60.0; sec= seconds-min*60; frac= (seconds-min*60-sec)*100; if(frac>99) frac= 99; sectors= (int) (skin->fixed_size/sector_size); if(sectors*sector_size != skin->fixed_size) sectors++; printf("Total size: %5d MB (%-2.2d:%-2.2d.%-2.2d) = %d sectors\n", mb,min,sec,frac,(int) sectors); seconds+= 2; min= seconds/60.0; sec= seconds-min*60; frac= (seconds-min*60-sec)*100; if(frac>99) frac= 99; sectors+= 150; printf("Lout start: %5d MB (%-2.2d:%-2.2d/%-2.2d) = %d sectors\n", mb,min,sec,frac,(int) sectors); } } return(1); } #endif /* ! Cdrskin_extra_leaN */ int Cdrskin_direct_write(struct CdrskiN *skin, int flag) { off_t byte_address, data_count, chunksize, i, alignment, fill; int ret, max_chunksize= 64*1024, source_fd= -1, is_from_stdin, eof_sensed= 0; int self_opened= 0; char *buf= NULL, *source_path, amount_text[81]; char *dummy_text= ""; struct burn_multi_caps *caps= NULL; ret= Cdrskin_grab_drive(skin,0); if(ret<=0) goto ex; ret= burn_disc_get_multi_caps(skin->grabbed_drive,BURN_WRITE_NONE,&caps,0); if(ret<=0) goto ex; if(caps->start_adr==0) { fprintf(stderr, "cdrskin: SORRY : Direct writing is not supported by drive and media\n"); {ret= 0; goto ex;} } alignment= caps->start_alignment; if(alignment>0 && (((off_t) skin->direct_write_amount) % alignment)!=0) { fprintf(stderr, "cdrskin: SORRY : direct_write_amount=%.f not aligned to blocks of %dk\n", skin->direct_write_amount,(int) alignment/1024); {ret= 0; goto ex;} } if(skin->track_counter<=0) { fprintf(stderr, "cdrskin: SORRY : No track source given for direct writing\n"); {ret= 0; goto ex;} } Cdrtrack_get_source_path(skin->tracklist[0], &source_path,&source_fd,&is_from_stdin,0); if(source_fd==-1) { ret= Cdrtrack_open_source_path(skin->tracklist[0],&source_fd, 2 | (skin->verbosity >= Cdrskin_verbose_debuG) | (4 * (skin->fifo_size >= 256 * 1024))); if(ret<=0) goto ex; if(ret == 2) self_opened= 1; } buf= calloc(1, max_chunksize); if(buf==NULL) { fprintf(stderr, "cdrskin: FATAL : Cannot allocate %d bytes of read buffer.\n", max_chunksize); {ret= -1; goto ex;} } byte_address= skin->write_start_address; if(byte_address<0) byte_address= 0; data_count= skin->direct_write_amount; if(data_count>0) sprintf(amount_text,"%.fk",(double) (data_count/1024)); else strcpy(amount_text,"0=open_ended"); burn_drive_reset_simulate(skin->grabbed_drive, !!skin->dummy_mode); if(skin->dummy_mode) dummy_text= " simulation of"; fprintf(stderr,"Beginning%s direct write (start=%.fk,amount=%s) ...\n", dummy_text, (double) (byte_address / 1024), amount_text); for(i= 0; i 0 ? alignment : 2048); else chunksize= data_count-i; if(chunksize>max_chunksize) chunksize= max_chunksize; /* read buffer from first track */ for(fill= 0; fill0) fprintf(stderr,"cdrskin: %s (errno=%d)\n", strerror(errno), errno); ret= 0; goto ex; } else if(ret==0) { eof_sensed= 1; if(data_count==0) { memset(buf+fill,0,(size_t) (chunksize-fill)); break; } else { fprintf(stderr, "cdrskin: FATAL : Premature EOF while reading from '%s'\n", source_path); ret= 0; goto ex; } } } ret= burn_random_access_write(skin->grabbed_drive,byte_address, buf,chunksize,0); if(ret<=0) goto ex; if(eof_sensed) break; byte_address+= chunksize; fprintf(stderr, "%s%9.fk written %s", skin->pacifier_with_newline ? "" : "\r", ((double) (i+chunksize)) / 1024.0, skin->pacifier_with_newline ? "\n" : ""); } fprintf(stderr, "%s%9.fk written \n", skin->pacifier_with_newline ? "" : "\r", ((double) i) / 1024.0); /* flush drive buffer */ fprintf(stderr,"syncing cache ...\n"); ret = burn_random_access_write(skin->grabbed_drive,byte_address,buf,0,1); if(ret<=0) goto ex; ret= 1; ex:; if(caps!=NULL) burn_disc_free_multi_caps(&caps); if(buf!=NULL) free(buf); if(self_opened && source_fd >= 0) close(source_fd); if(ret>0) fprintf(stderr,"writing done\n"); else fprintf(stderr,"writing failed\n"); return(ret); } int Cdrskin_grow_overwriteable_iso(struct CdrskiN *skin, int flag) { int ret, i, went_well= 1; char *track_descr, *md; unsigned char *td; /* Because the representation of 255 in char is ambigous */ double track_size, media_size; ret= Cdrtrack_get_iso_fs_descr(skin->tracklist[0],&track_descr,&track_size,0); if(ret<=0) { fprintf(stderr,"cdrskin: SORRY : Saw no ISO-9660 filesystem in track 0\n"); return(ret); } if(skin->grow_overwriteable_iso==3) /* initial session */ return(1); if(skin->grow_overwriteable_iso!=2) { fprintf(stderr, "cdrskin: SORRY : Could not read ISO-9660 descriptors from media\n"); return(0); } ret= Scan_for_iso_size((unsigned char *) skin->overwriteable_iso_head+16*2048, &media_size, 0); if(ret<=0) { fprintf(stderr,"cdrskin: SORRY : No recognizable ISO-9660 on media\n"); return(0); } if(skin->write_start_address>=0.0) media_size= skin->write_start_address; /* Write new sum into media descr 0 */ md= skin->overwriteable_iso_head+16*2048; memcpy(md,track_descr,2048); Set_descr_iso_size((unsigned char *) md,track_size+media_size,0); if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr,"cdrskin_debug: new ISO-9660 size : %.f (%fs)\n", track_size+media_size, (track_size+media_size)/2048)); /* Copy type 255 CD001 descriptors from track to media descriptor buffer and adjust their size entries */ for(i=1; i<16; i++) { td= (unsigned char *) (track_descr + i * 2048); md= skin->overwriteable_iso_head+(16+i)*2048; if(td[0] != 255) break; /* demand media descrN[0] == track descrN[0] */ if(((char *) td)[0] != md[0]) { fprintf(stderr, "cdrskin: SORRY : Type mismatch of ISO volume descriptor #%d (%u <-> %u)\n", i, ((unsigned int) td[0]) & 0xff, ((unsigned int) md[0])&0xff); went_well= 0; } memcpy(md,td,2048); Set_descr_iso_size((unsigned char *) md,track_size+media_size,0); } if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr,"cdrskin_debug: copied %d secondary ISO descriptors\n", i-1)); /* write block 16 to 31 to media */ if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr,"cdrskin_debug: writing to media: blocks 16 to 31\n")); ret= burn_random_access_write(skin->grabbed_drive, (off_t) (16*2048), skin->overwriteable_iso_head+16*2048, (off_t) (16*2048), 1); if(ret<=0) return(ret); return(went_well); } int Cdrskin_cdtext_test(struct CdrskiN *skin, struct burn_write_opts *o, struct burn_session *session, int flag) { int ret, num_packs; unsigned char *text_packs = NULL; if(skin->num_text_packs > 0) { Cdrskin_print_text_packs(skin, skin->text_packs, skin->num_text_packs, 2); ret= 1; goto ex; } ret= burn_cdtext_from_session(session, &text_packs, &num_packs, 0); if(ret < 0) goto ex; if(ret > 0) { Cdrskin_print_text_packs(skin, text_packs, num_packs, 1); ret= 1; goto ex; } ret= 1; ex: if (text_packs != NULL) free(text_packs); return(ret); } int Cdrskin_read_input_sheet_v07t(struct CdrskiN *skin, char *path, int block, struct burn_session *session, int flag) { int ret= 0; ret= burn_session_input_sheet_v07t(session, path, block, 1); return(ret); } int Cdrskin_cdrtracks_from_session(struct CdrskiN *skin, struct burn_session *session, int flag) { int ret, num_tracks, i; struct burn_track **tracks; struct CdrtracK *t; double sectors; for(i= 0; i < skin->track_counter; i++) Cdrtrack_destroy(&(skin->tracklist[i]), 0); skin->track_counter= 0; tracks= burn_session_get_tracks(session, &num_tracks); if(num_tracks <= 0) { fprintf(stderr, "cdrskin: SORRY : No tracks defined for burn run.\n"); ret= 0; goto ex; } for(i= 0; i < num_tracks; i++) { /* Create Cdrtrack without reading parameters from skin */ ret= Cdrtrack_new(&(skin->tracklist[skin->track_counter]), skin, skin->track_counter, 1); if(ret <= 0) { fprintf(stderr, "cdrskin: FATAL : Creation of track control object failed.\n"); goto ex; } /* Set essential properties */ t= skin->tracklist[skin->track_counter]; t->track_type= burn_track_get_mode(tracks[i]); if(t->track_type & BURN_AUDIO) { t->sector_size= 2352; t->track_type= BURN_AUDIO; } else { t->sector_size= 2048; t->track_type= BURN_MODE1; } sectors= burn_track_get_sectors(tracks[i]); t->fixed_size= sectors * t->sector_size; t->libburn_track= tracks[i]; t->libburn_track_is_own= 0; skin->track_counter++; } ret= 1; ex: if(ret <= 0) { for(i= 0; i < skin->track_counter; i++) Cdrtrack_destroy(&(skin->tracklist[i]), 0); skin->track_counter= 0; } return(ret); } int Cdrskin_write_result_string(struct CdrskiN *skin, char *msg, int flag) { int ret; if(skin->preskin->result_fd < 0) { printf("%s",msg); return(1); } ret= write(skin->preskin->result_fd, msg, strlen(msg)); if(ret != (int) strlen(msg)) return(0); return(1); } /** Burn data via libburn according to the parameters set in skin. @return <=0 error, 1 success */ int Cdrskin_burn(struct CdrskiN *skin, int flag) { struct burn_disc *disc = NULL; struct burn_session *session = NULL; struct burn_write_opts *o = NULL; struct burn_source *cuefile_fifo= NULL; enum burn_disc_status s; enum burn_drive_status drive_status; struct burn_progress p; struct burn_drive *drive; int ret,loop_counter= 0,max_track= -1,i,hflag,nwa,num, wrote_well= 2; int fifo_disabled= 0, min_buffer_fill= 101, length, start_tno= 1; int use_data_image_size, needs_early_fifo_fill= 0,iso_size= -1, non_audio= 0; double start_time,last_time; double total_count= 0.0,last_count= 0.0,size,padding,sector_size= 2048.0; char *doing; char *source_path; unsigned char *payload; int source_fd, is_from_stdin; int text_flag= 4; /* Check CRCs and silently repair CRCs if all are 0 */ unsigned char *text_packs= NULL; int num_packs= 0, start_block, block_no, profile_number; #ifndef Cdrskin_no_cdrfifO double put_counter, get_counter, empty_counter, full_counter; int total_min_fill, fifo_percent; #endif off_t free_space; char msg[80], profile_name[80]; if(skin->tell_media_space) doing= "estimating"; else doing= "burning"; printf("cdrskin: beginning to %s disc\n", skin->tell_media_space?"estimate":"burn"); if(skin->fill_up_media && skin->multi) { ClN(fprintf(stderr, "cdrskin: NOTE : Option --fill_up_media disabled option -multi\n")); skin->multi= 0; } ret= Cdrskin_grab_drive(skin,0); if(ret<=0) goto burn_failed; drive= skin->drives[skin->driveno].drive; s= burn_disc_get_status(drive); if(skin->verbosity>=Cdrskin_verbose_progresS) Cdrskin_report_disc_status(skin,s,1); burn_disc_get_profile(drive, &profile_number, profile_name); disc= burn_disc_create(); session= burn_session_create(); ret= burn_disc_add_session(disc,session,BURN_POS_END); if(ret==0) { fprintf(stderr,"cdrskin: FATAL : Cannot add session to disc object.\n"); burn_failed:; ret= 0; goto ex; } skin->fixed_size= 0.0; skin->has_open_ended_track= 0; if(skin->cuefile[0]) { if(skin->track_counter > 0) { fprintf(stderr, "cdrskin: SORRY : Option cuefile= cannot be combined with track sources\n"); goto burn_failed; } if(strcmp(skin->preskin->write_mode_name, "SAO") != 0 && strcmp(skin->preskin->write_mode_name, "DEFAULT") != 0) { fprintf(stderr, "cdrskin: SORRY : Option cuefile= works only with write mode SAO"); goto burn_failed; } strcpy(skin->preskin->write_mode_name, "SAO"); ret= burn_session_by_cue_file(session, skin->cuefile, skin->fifo_size, &cuefile_fifo, &text_packs, &num_packs, !skin->use_cdtext); if(ret <= 0) goto burn_failed; if(num_packs > 0) { if(skin->num_text_packs > 0) { fprintf(stderr, "cdrskin: WARNING : Option textfile= overrides CDTEXTFILE from option cuesheet=\n"); if(text_packs != NULL) free(text_packs); } else { skin->text_packs= text_packs; skin->num_text_packs= num_packs; } } text_packs= NULL; /* Now either freed or attached */ ret= Cdrskin_cdrtracks_from_session(skin, session, 0); if(ret <= 0) goto burn_failed; skin->cuefile_fifo= cuefile_fifo; cuefile_fifo= NULL; } else { for(i=0;itrack_counter;i++) { hflag= (skin->verbosity>=Cdrskin_verbose_debuG); if(i==skin->track_counter-1) Cdrtrack_ensure_padding(skin->tracklist[i],hflag&1); /* if(skin->fifo_size >= 256 * 1024) */ hflag|= 4; if(profile_number == 0x09 || profile_number == 0x0a) hflag|= 8; ret= Cdrtrack_add_to_session(skin->tracklist[i],i,session,hflag); if(ret<=0) { fprintf(stderr,"cdrskin: FATAL : Cannot add track %d to session.\n",i+1); goto burn_failed; } Cdrtrack_get_size(skin->tracklist[i],&size,&padding,§or_size, &use_data_image_size,0); if(use_data_image_size==1) { /* still unfulfilled -isosize demand pending */ needs_early_fifo_fill= 1; } non_audio|= (skin->tracklist[i]->track_type != BURN_AUDIO); } } if(skin->sheet_v07t_blocks > 0) { if(skin->num_text_packs > 0) { fprintf(stderr, "cdrskin: WARNING : Option textfile= or cuefile= overrides option input_sheet_v07t=\n"); } else if(non_audio) { fprintf(stderr, "cdrskin: SORRY : Option input_sheet_v07t= works only if all tracks are -audio\n"); goto burn_failed; } else { /* If cuefile and session has block 0, then start at block 1 */ start_block= 0; if(skin->cuefile[0]) { for(i= 0x80; i < 0x8f; i++) { ret= burn_session_get_cdtext(session, 0, i, "", &payload, &length, 0); if(ret > 0 && length > 0) break; } if(i < 0x8f) start_block= 1; } block_no = start_block; for(i= 0; i < skin->sheet_v07t_blocks && block_no < 8; i++) { ret= Cdrskin_read_input_sheet_v07t(skin, skin->sheet_v07t_paths[i], block_no, session, 0); if(ret <= 0) goto burn_failed; block_no += ret; } if(i < skin->sheet_v07t_blocks) { fprintf(stderr, "cdrskin: WARNING : Too many CD-TEXT blocks. input_sheet_v07t= files ignored: %d\n", skin->sheet_v07t_blocks - i); } } } #ifndef Cdrskin_extra_leaN /* Final decision on track size has to be made after eventual -isosize determination via fifo content. */ if(needs_early_fifo_fill && !skin->tell_media_space) { int start_memorized; start_memorized= skin->fifo_start_at; /* try ISO-9660 size recognition via fifo */ if(32*2048<=skin->fifo_size) skin->fifo_start_at= 32*2048; else skin->fifo_start_at= skin->fifo_size; ret= Cdrskin_fill_fifo(skin,0); if(ret<=0) goto fifo_filling_failed; if((start_memorized>skin->fifo_start_at || start_memorized<=0) && skin->fifo_start_atfifo_size) needs_early_fifo_fill= 2; /* continue filling fifo at normal stage */ skin->fifo_start_at= start_memorized; } #endif /* Cdrskin_extra_leaN */ for(i=0;itrack_counter;i++) { Cdrtrack_get_size(skin->tracklist[i],&size,&padding,§or_size, &use_data_image_size,0); if(use_data_image_size==1 && size<=0 && skin->tell_media_space) size= 1024*1024; /* a dummy size */ ret= Cdrtrack_activate_image_size(skin->tracklist[i],&size, !!skin->tell_media_space); if(ret<=0) { Cdrtrack_get_source_path(skin->tracklist[i], &source_path,&source_fd,&is_from_stdin,0); fprintf(stderr, "cdrskin: FATAL : Cannot determine -isosize of track source\n"); fprintf(stderr, "cdrskin: '%s'\n", source_path); {ret= 0; goto ex;} } Cdrtrack_get_size(skin->tracklist[i],&size,&padding,§or_size, &use_data_image_size,0); if(use_data_image_size==2 && skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr, "cdrskin: DEBUG: track %2.2d : activated -isosize %.fs (= %.fb)\n", i+1, size/2048.0,size)); if(size>0) skin->fixed_size+= size+padding; else skin->has_open_ended_track= 1; if(skin->tracklist[i]->isrc[0] && skin->tracklist[i]->libburn_track != NULL) { ret= burn_track_set_isrc_string(skin->tracklist[i]->libburn_track, skin->tracklist[i]->isrc, 0); if(ret <= 0) goto ex; } } o= burn_write_opts_new(drive); burn_write_opts_set_perform_opc(o, 0); /* growisofs stunt: assessment of media and start for next session */ if((skin->grow_overwriteable_iso==1 || skin->grow_overwriteable_iso==2) && skin->media_is_overwriteable) { /* Obtain ISO size from media, keep 64 kB head in memory */ ret= Cdrskin_overwriteable_iso_size(skin,&iso_size,0); if(ret<0) goto ex; if(ret>0 && skin->write_start_address<0) { skin->write_start_address= ((double) iso_size)*2048.0; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf( "cdrskin: write start address by --grow_overwriteable_iso : %ds\n", iso_size)); } else if(ret==0) skin->grow_overwriteable_iso= 3; /* do not patch ISO header later on */ } burn_write_opts_set_start_byte(o, skin->write_start_address); if(skin->media_is_overwriteable && skin->multi) { if(skin->grow_overwriteable_iso<=0) { fprintf(stderr, "cdrskin: FATAL : -multi cannot leave a recognizable end mark on this media.\n"); fprintf(stderr, "cdrskin: HINT : For ISO-9660 images try --grow_overwriteable_iso -multi\n"); {ret= 0; goto ex;} } skin->multi= 0; } if(skin->multi && !skin->media_does_multi) { if(skin->prodvd_cli_compatible) { skin->multi= 0; if(skin->verbosity>=Cdrskin_verbose_progresS) fprintf(stderr, "cdrskin: NOTE : Ignored option -multi.\n"); } } burn_write_opts_set_multi(o,skin->multi); burn_write_opts_set_fillup(o, skin->fill_up_media); burn_write_opts_set_force(o, !!skin->force_is_set); burn_write_opts_set_stream_recording(o, skin->stream_recording_is_set); #ifdef Cdrskin_dvd_obs_default_64K if(skin->dvd_obs == 0) burn_write_opts_set_dvd_obs(o, 64 * 1024); else #endif burn_write_opts_set_dvd_obs(o, skin->dvd_obs); burn_write_opts_set_obs_pad(o, skin->obs_pad); burn_write_opts_set_bdr_obs_exempt(o, skin->bdr_obs_exempt); burn_write_opts_set_stdio_fsync(o, skin->stdio_sync); if(skin->dummy_mode) { fprintf(stderr, "cdrskin: NOTE : -dummy mode will prevent actual writing\n"); burn_write_opts_set_simulate(o, 1); } burn_write_opts_set_underrun_proof(o,skin->burnfree); if(skin->num_text_packs > 0) { if(non_audio) { fprintf(stderr, "cdrskin: SORRY : Option textfile= works only if all tracks are -audio\n"); goto burn_failed; } if(!!skin->force_is_set) text_flag= 1; /* No CRC verification or repairing */ ret= burn_write_opts_set_leadin_text(o, skin->text_packs, skin->num_text_packs, text_flag); if(ret <= 0) goto burn_failed; } if(skin->mcn[0]) { burn_write_opts_set_mediacatalog(o, (unsigned char *) skin->mcn); burn_write_opts_set_has_mediacatalog(o, 1); } if(skin->cd_start_tno >= 1 && skin->cd_start_tno <= 99) { ret= burn_session_set_start_tno(session, skin->cd_start_tno, 0); if(ret <= 0) goto burn_failed; } ret= Cdrskin_activate_write_mode(skin,o,disc,0); if(ret<=0) goto burn_failed; if(skin->cdtext_test) { ret= Cdrskin_cdtext_test(skin, o, session, (skin->cdtext_test == 1)); if(ret <= 0) goto ex; if(skin->cdtext_test >= 2) { fprintf(stderr, "cdrskin: Option --cdtext_dummy prevents actual burn run\n"); ret= 1; goto ex; } } ret= Cdrskin_obtain_nwa(skin, &nwa,0); if(ret<=0) nwa= -1; if(skin->assert_write_lba>=0 && nwa!=skin->assert_write_lba) { fprintf(stderr, "cdrskin: FATAL : Option assert_write_lba= demands block number %10d\n", skin->assert_write_lba); fprintf(stderr, "cdrskin: FATAL : but predicted actual write start address is %10d\n", nwa); {ret= 0; goto ex;} } #ifndef Cdrskin_extra_leaN Cdrskin_announce_tracks(skin, burn_session_get_start_tno(session, 0), 0); #endif if(skin->tell_media_space || skin->track_counter <= 0) { /* write capacity estimation and return without actual burning */ free_space= burn_disc_available_space(drive,o); sprintf(msg,"%d\n",(int) (free_space/(off_t) 2048)); Cdrskin_write_result_string(skin, msg, 0); if(skin->track_counter>0) fprintf(stderr, "cdrskin: NOTE : %s burn run suppressed by option --tell_media_space\n", skin->preskin->write_mode_name); {ret= 1; goto ex;} } if(skin->fixed_size > 0 && !skin->force_is_set) { free_space= burn_disc_available_space(drive,o); if(skin->fixed_size > free_space && free_space > 0) { fprintf(stderr, "cdrskin: FATAL : predicted session size %lus does not fit on media (%lus)\n", (unsigned long) ((skin->fixed_size + 2047.0) / 2048.0), (unsigned long) ((free_space + 2047) / 2048)); ClN(fprintf(stderr, "cdrskin: HINT : This test may be disabled by option -force\n");) {ret= 0; goto ex;} } } Cdrskin_adjust_speed(skin,0); #ifndef Cdrskin_extra_leaN Cdrskin_wait_before_action(skin,0); if(burn_is_aborting(0)) {ret= 0; goto ex;} if(needs_early_fifo_fill==1) ret= 1; else if(skin->cuefile[0] != 0) ret= 1; else ret= Cdrskin_fill_fifo(skin,0); if(ret<=0) { fifo_filling_failed:; fprintf(stderr,"cdrskin: FATAL : Filling of fifo failed\n"); goto ex; } #endif /* ! Cdrskin_extra_leaN */ start_tno = burn_session_get_start_tno(session, 0); if(skin->verbosity>=Cdrskin_verbose_progresS && nwa>=0) { printf("Starting new track at sector: %d\n",nwa); fflush(stdout); } if(burn_is_aborting(0)) {ret= 0; goto ex;} skin->drive_is_busy= 1; burn_disc_write(o, disc); if(skin->preskin->abort_handler==-1) Cleanup_set_handlers(Cleanup_handler_handlE, Cleanup_handler_funC, Cleanup_handler_flaG); last_time= start_time= Sfile_microtime(0); burn_write_opts_free(o); o= NULL; while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) { /* >>> how do i learn about success or failure ? */ ; } loop_counter= 0; while (1) { drive_status= burn_drive_get_status(drive, &p); if(drive_status==BURN_DRIVE_IDLE) break; /* >>> how do i learn about success or failure ? */ if(loop_counter>0 || Cdrskin__is_aborting(0)) Cdrskin_burn_pacifier(skin, start_tno, drive_status,&p,start_time,&last_time, &total_count,&last_count,&min_buffer_fill,0); if(max_tracksupposed_track_idx) max_track= skin->supposed_track_idx; #ifndef Cdrskin_extra_leaN /* <<< debugging : artificial abort without a previous signal */; if(skin->abort_after_bytecount>=0.0 && total_count>=skin->abort_after_bytecount) { /* whatever signal handling is installed: this thread is the boss now */ fprintf(stderr, "cdrskin: DEVELOPMENT : synthetic abort by abort_after_bytecount=%.f\n", skin->abort_after_bytecount); skin->control_pid= getpid(); ret= Cdrskin_abort_handler(skin,0,0); fprintf(stderr,"cdrskin: done (aborted)\n"); exit(1); } if(Cdrskin__is_aborting(0)) fifo_disabled= 1; if(skin->fifo==NULL || fifo_disabled) { usleep(20000); } else { #ifdef Cdrskin_no_cdrfifO /* Should never happen as skin->fifo should be NULL */ usleep(20000); #else /* Cdrskin_no_cdrfifO */ ret= Cdrfifo_try_to_work(skin->fifo,20000,NULL,NULL,0); if(ret<0) { int abh; abh= skin->preskin->abort_handler; if(abh!=2) fprintf(stderr, "\ncdrskin: FATAL : Fifo encountered error during burn loop.\n"); if(abh==0) { ret= -1; goto ex; } else if(abh==1 || abh==3 || abh==4 || abh==-1) { Cdrskin_abort_handler(skin,0,0); fprintf(stderr,"cdrskin: done (aborted)\n"); exit(10); } else { if(skin->verbosity>=Cdrskin_verbose_debuG) fprintf(stderr, "\ncdrskin_debug: Cdrfifo_try_to_work() returns %d\n",ret); } } if(ret==2) { /* <0 = error , 2 = work is done */ if(skin->verbosity>=Cdrskin_verbose_debuG) fprintf(stderr,"\ncdrskin_debug: fifo ended work with ret=%d\n",ret); fifo_disabled= 1; } #endif /* ! Cdrskin_no_cdrfifO */ } #else /* ! Cdrskin_extra_leaN */ usleep(20000); #endif /* Cdrskin_extra_leaN */ loop_counter++; } skin->drive_is_busy= 0; if(skin->verbosity>=Cdrskin_verbose_progresS) printf("\n"); if(Cdrskin__is_aborting(0)) Cdrskin_abort(skin, 0); /* Never comes back */ wrote_well = burn_drive_wrote_well(drive); if(skin->media_is_overwriteable && skin->grow_overwriteable_iso>0 && wrote_well) { /* growisofs final stunt : update volume descriptors at start of media */ ret= Cdrskin_grow_overwriteable_iso(skin,0); if(ret<=0) wrote_well= 0; } if(max_track<0) { printf("Track %-2.2d: Total bytes read/written: %.f/%.f (%.f sectors).\n", start_tno, total_count, total_count, total_count / sector_size); } else { Cdrtrack_get_size(skin->tracklist[max_track],&size,&padding,§or_size, &use_data_image_size,1); if (start_tno <= 0) start_tno = 1; printf("Track %-2.2d: Total bytes read/written: %.f/%.f (%.f sectors).\n", max_track + start_tno, size,size+padding,(size+padding)/sector_size); } if(skin->verbosity>=Cdrskin_verbose_progresS) printf("Writing time: %.3fs\n",Sfile_microtime(0)-start_time); #ifndef Cdrskin_extra_leaN #ifdef Cdrskin_use_libburn_fifO if(skin->fifo == NULL && skin->verbosity>=Cdrskin_verbose_progresS) { /* >>> this should rather be done for each track (for now this libburn_fifo should only be used with single track) */ Cdrtrack_report_fifo(skin->tracklist[skin->track_counter - 1], 0); } #endif /* Cdrskin_use_libburn_fifO */ #ifndef Cdrskin_no_cdrfifO if(skin->fifo!=NULL && skin->fifo_size>0 && wrote_well) { int dummy,final_fill; Cdrfifo_get_buffer_state(skin->fifo,&final_fill,&dummy,0); if(final_fill>0) { fifo_full_at_end:; fprintf(stderr, "cdrskin: FATAL : Fifo still contains data after burning has ended.\n"); fprintf(stderr, "cdrskin: FATAL : %.d bytes left.\n",final_fill); fprintf(stderr, "cdrskin: FATAL : This indicates an overflow of the last track.\n"); fprintf(stderr, "cdrskin: NOTE : The media might appear ok but is probably truncated.\n"); ret= -1; goto ex; } #ifdef Cdrskin_libburn_leaves_inlet_opeN for(i= 0;itrack_counter;i++) { ret= Cdrtrack_has_input_left(skin->tracklist[i],0); if(ret>0) { fprintf(stderr, "cdrskin: FATAL : Fifo outlet of track #%d is still buffering some bytes.\n", i+1); goto fifo_full_at_end; } } #endif /* Cdrskin_libburn_leaves_inlet_opeN */ } if(skin->verbosity>=Cdrskin_verbose_progresS) { if(skin->fifo!=NULL && skin->fifo_size>0) { int dummy; Cdrfifo_get_min_fill(skin->fifo,&total_min_fill,&dummy,0); fifo_percent= 100.0*((double) total_min_fill)/(double) skin->fifo_size; if(fifo_percent==0 && total_min_fill>0) fifo_percent= 1; Cdrfifo_get_cdr_counters(skin->fifo,&put_counter,&get_counter, &empty_counter,&full_counter,0); fflush(stdout); fprintf(stderr,"Cdrskin: fifo had %.f puts and %.f gets.\n", put_counter,get_counter); fprintf(stderr, "Cdrskin: fifo was %.f times empty and %.f times full, min fill was %d%%.\n", empty_counter,full_counter,fifo_percent); } } #endif /* ! Cdrskin_no_cdrfifO */ if(skin->verbosity>=Cdrskin_verbose_progresS) { drive_status= burn_drive_get_status(drive, &p); if(p.buffer_min_fill<=p.buffer_capacity && p.buffer_capacity>0) { num= 100.0 * ((double) p.buffer_min_fill)/(double) p.buffer_capacity; if(num100) min_buffer_fill= 50; printf("Min drive buffer fill was %d%%\n", min_buffer_fill); } #endif /* ! Cdrskin_extra_leaN */ ret= 1; if(wrote_well) { if(skin->verbosity>=Cdrskin_verbose_progresS) printf("cdrskin: burning done\n"); } else ret= 0; ex:; if(ret<=0) { if(skin->verbosity>=Cdrskin_verbose_progresS) printf("cdrskin: %s failed\n",doing); fprintf(stderr,"cdrskin: FATAL : %s failed.\n",doing); } skin->drive_is_busy= 0; if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(printf("cdrskin_debug: do_eject= %d\n",skin->do_eject)); for(i= 0;itrack_counter;i++) Cdrtrack_cleanup(skin->tracklist[i],0); if(o != NULL) burn_write_opts_free(o); if(cuefile_fifo != NULL) burn_source_free(cuefile_fifo); if(session != NULL) burn_session_free(session); if(disc != NULL) burn_disc_free(disc); return(ret); } #ifdef Libburn_develop_quality_scaN int Cdrskin_qcheck(struct CdrskiN *skin, int flag) { struct burn_drive *drive; int ret, rate_period, profile_number; char profile_name[80]; printf("cdrskin: beginning to perform quality check on disc\n"); ret= Cdrskin_grab_drive(skin,0); if(ret<=0) return(ret); drive= skin->drives[skin->driveno].drive; ret= burn_disc_get_profile(drive, &profile_number, profile_name); if(ret <= 0) profile_number= 0; if(profile_number != 0x08 && profile_number != 0x09 && profile_number != 0x0a) rate_period= 8; else rate_period= 75; if(skin->do_qcheck == 1) { ret= burn_nec_optiarc_rep_err_rate(drive, 0, rate_period, 0); if(ret<=0) return(ret); } return(1); } #endif /* Libburn_develop_quality_scaN */ /** Print lba of first track of last session and Next Writeable Address of the next unwritten session. */ int Cdrskin_msinfo(struct CdrskiN *skin, int flag) { int num_sessions, session_no, ret, num_tracks, open_sessions= 0; int nwa= -123456789, lba= -123456789, aux_lba, is_bdr_pow= 0; char msg[80]; enum burn_disc_status s; struct burn_drive *drive; struct burn_disc *disc= NULL; struct burn_session **sessions= NULL; struct burn_track **tracks; struct burn_toc_entry toc_entry; ret= Cdrskin_grab_drive(skin,0); if(ret<=0) return(ret); drive= skin->drives[skin->driveno].drive; s= burn_disc_get_status(drive); if(s!=BURN_DISC_APPENDABLE) { if(skin->grow_overwriteable_iso==1 || skin->grow_overwriteable_iso==2) { lba= 0; ret= Cdrskin_overwriteable_iso_size(skin,&nwa,0); if(ret>0) { s= BURN_DISC_APPENDABLE; goto put_out; } } Cdrskin_report_disc_status(skin,s,0); fprintf(stderr,"cdrskin: FATAL : -msinfo can only operate on appendable (i.e. -multi) discs\n"); if(skin->grow_overwriteable_iso>0) fprintf(stderr,"cdrskin: or on overwriteables with existing ISO-9660 file system.\n"); {ret= 0; goto ex;} } is_bdr_pow= burn_drive_get_bd_r_pow(drive); if(is_bdr_pow) { fprintf(stderr, "cdrskin: FATAL : -msinfo cannot operate on POW formatted BD-R discs\n"); {ret= 0; goto ex;} } disc= burn_drive_get_disc(drive); if(disc==NULL) { /* No TOC available. Try to inquire directly. */ ret= burn_disc_get_msc1(drive,&lba); if(ret>0) goto obtain_nwa; fprintf(stderr,"cdrskin: FATAL : Cannot obtain info about CD content\n"); {ret= 0; goto ex;} } sessions= burn_disc_get_sessions(disc,&num_sessions); open_sessions= burn_disc_get_incomplete_sessions(disc); for(session_no= 0; session_no < num_sessions + open_sessions; session_no++) { tracks= burn_session_get_tracks(sessions[session_no],&num_tracks); if(tracks==NULL || num_tracks<=0) continue; burn_track_get_entry(tracks[0],&toc_entry); if(toc_entry.extensions_valid&1) { /* DVD extension valid */ if(session_no >= num_sessions) { if(!(toc_entry.extensions_valid & 4)) continue; /* open session with no track status bits from libburn */ if((toc_entry.track_status_bits & (1 << 14)) || !((toc_entry.track_status_bits & (1 << 16)) || ((toc_entry.track_status_bits & (1 << 17)) && toc_entry.last_recorded_address > toc_entry.start_lba))) continue; /* Blank or not appendable and not recorded */ } lba= toc_entry.start_lba; } else { lba= burn_msf_to_lba(toc_entry.pmin,toc_entry.psec,toc_entry.pframe); } } if(lba==-123456789) { fprintf(stderr,"cdrskin: FATAL : Cannot find any track on CD\n"); {ret= 0; goto ex;} } obtain_nwa:; ret= Cdrskin_obtain_nwa(skin,&nwa,flag); if(ret<=0) { if (sessions == NULL) { fprintf(stderr, "cdrskin: SORRY : Cannot obtain next writeable address\n"); {ret= 0; goto ex;} } ClN(fprintf(stderr, "cdrskin: NOTE : Guessing next writeable address from leadout\n")); burn_session_get_leadout_entry(sessions[num_sessions-1],&toc_entry); if(toc_entry.extensions_valid&1) { /* DVD extension valid */ aux_lba= toc_entry.start_lba; } else { aux_lba= burn_msf_to_lba(toc_entry.pmin,toc_entry.psec,toc_entry.pframe); } if(num_sessions>0) nwa= aux_lba+6900; else nwa= aux_lba+11400; } put_out:; sprintf(msg,"%d,%d\n",lba,nwa); Cdrskin_write_result_string(skin, msg, 0); if(strlen(skin->msifile)) { FILE *fp; fp = fopen(skin->msifile, "w"); if(fp==NULL) { if(errno>0) fprintf(stderr,"cdrskin: %s (errno=%d)\n", strerror(errno), errno); fprintf(stderr,"cdrskin: FATAL : Cannot write msinfo to file '%s'\n", skin->msifile); {ret= 0; goto ex;} } fprintf(fp,"%d,%d",lba,nwa); fclose(fp); } ret= 1; ex:; if(disc!=NULL) burn_disc_free(disc); return(ret); } /** Work around the failure of libburn to eject the tray. This employs a system(2) call and is therefore an absolute no-no for any pseudo user identities. @return <=0 error, 1 success */ int Cdrskin_eject(struct CdrskiN *skin, int flag) { int i,ret,max_try= 5; if(!skin->do_eject) return(1); if((int) skin->n_drives <= skin->driveno || skin->driveno < 0) return(2); /* ??? A61012 : retry loop might now be obsolete (a matching bug in burn_disc_write_sync() was removed ) */ /* A61227 : A kindof race condition with -atip -eject on SuSE 9.3. Loop saved me. Waiting seems to help. I suspect the media demon. */ for(i= 0;i0 || i>=max_try-1) break; if(skin->verbosity>=Cdrskin_verbose_progresS) ClN(fprintf(stderr, "cdrskin: NOTE : Attempt #%d of %d failed to grab drive for eject\n", i+1,max_try)); usleep(1000000); } if(ret>0) { ret= Cdrskin_release_drive(skin,1); if(ret<=0) goto sorry_failed_to_eject; } else { sorry_failed_to_eject:; fprintf(stderr,"cdrskin: SORRY : Failed to finally eject tray.\n"); return(0); } return(1); } /** Interpret all arguments of the program after libburn has been initialized and drives have been scanned. This call reports to stderr any valid cdrecord options which are not implemented yet. @param flag Bitfield for control purposes: bit0= do not finalize setup bit1= do not interpret (again) skin->preskin->pre_argv @return <=0 error, 1 success */ int Cdrskin_setup(struct CdrskiN *skin, int argc, char **argv, int flag) { int i,k,l,ret, idx= -1, cd_start_tno; double value,grab_and_wait_value= -1.0, num; char *cpt,*value_pt,adr[Cdrskin_adrleN],*blank_mode= "", *argpt; struct stat stbuf; /* cdrecord 2.01 options which are not scheduled for implementation, yet */ static char ignored_partial_options[][41]= { "timeout=", "debug=", "kdebug=", "kd=", "driver=", "ts=", "pregap=", "defpregap=", "pktsize=", "" }; static char ignored_full_options[][41]= { "-d", "-silent", "-s", "-setdropts", "-prcap", "-reset", "-abort", "-overburn", "-ignsize", "-useinfo", "-fix", "-nofix", "-raw", "-raw96p", "-raw16", "-raw96r", "-clone", "-cdi", "-shorttrack", "-noshorttrack", "-packet", "-noclose", "" }; /* are we pretending to be cdrecord ? */ cpt= strrchr(argv[0],'/'); if(cpt==NULL) cpt= argv[0]; else cpt++; if(strcmp(cpt,"cdrecord")==0 && !(flag&1)) { fprintf(stderr,"\n"); fprintf(stderr, "Note: This is not cdrecord by Joerg Schilling. Do not bother him.\n"); fprintf(stderr, " See cdrskin start message on stdout. See --help. See -version.\n"); fprintf(stderr,"\n"); /* allow automatic -tao to -sao redirection */ skin->tao_to_sao_tsize=650*1024*1024; } #ifndef Cdrskin_extra_leaN if(!(flag&2)) { if(skin->preskin->pre_argc>1) { ret= Cdrskin_setup(skin,skin->preskin->pre_argc,skin->preskin->pre_argv, flag|1|2); if(ret<=0) return(ret); } } #endif for (i= 1;i 3) argpt++; /* is this a known option which is not planned to be implemented ? */ /* such an option will not be accepted as data source */ for(k=0;ignored_partial_options[k][0]!=0;k++) { if(argpt[0]=='-') if(strncmp(argpt+1,ignored_partial_options[k], strlen(ignored_partial_options[k]))==0) goto no_volunteer; if(strncmp(argpt,ignored_partial_options[k], strlen(ignored_partial_options[k]))==0) goto no_volunteer; } for(k=0;ignored_full_options[k][0]!=0;k++) if(strcmp(argpt,ignored_full_options[k])==0) goto no_volunteer; if(0) { no_volunteer:; skin->preskin->demands_cdrecord_caps= 1; if(skin->preskin->fallback_program[0]) fprintf(stderr,"cdrskin: NOTE : Unimplemented option: '%s'\n",argpt); else fprintf(stderr,"cdrskin: NOTE : ignoring unimplemented option : '%s'\n", argpt); fprintf(stderr, "cdrskin: NOTE : option is waiting for a volunteer to implement it.\n"); continue; } #ifndef Cdrskin_extra_leaN if(strncmp(argv[i],"abort_after_bytecount=",22)==0) { skin->abort_after_bytecount= Scanf_io_size(argv[i]+22,0); fprintf(stderr, "cdrskin: NOTE : will perform synthetic abort after %.f bytes\n", skin->abort_after_bytecount); } else if(strcmp(argv[i],"--abort_handler")==0) { #else /* ! Cdrskin_extra_leaN */ if(strcmp(argv[i],"--abort_handler")==0) { #endif /* is handled in Cdrpreskin_setup() */; } else if(strncmp(argv[i],"-abort_max_wait=",16)==0) { value_pt= argv[i]+16; goto set_abort_max_wait; } else if(strncmp(argv[i],"abort_max_wait=",15)==0) { value_pt= argv[i]+15; set_abort_max_wait:; value= Scanf_io_size(value_pt,0); if(value<0 || value>86400) { fprintf(stderr, "cdrskin: NOTE : ignored out-of-range value: abort_max_wait=%s\n", value_pt); } else { skin->abort_max_wait= value; if(skin->verbosity>=Cdrskin_verbose_cmD) printf( "cdrskin: maximum waiting time with abort handling : %d seconds\n", skin->abort_max_wait); } } else if(strcmp(argv[i],"--adjust_speed_to_drive")==0) { skin->adjust_speed_to_drive= 1; } else if(strcmp(argv[i],"--allow_emulated_drives")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argv[i],"--allow_setuid")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argv[i],"--allow_untested_media")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argv[i],"--any_track")==0) { skin->single_track= -1; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf( "cdrskin: --any_track : will accept any unknown option as track source\n")); } else if(strncmp(argv[i],"assert_write_lba=",17)==0) { value_pt= argv[i]+17; value= Scanf_io_size(value_pt,0); l= strlen(value_pt); if(l>1) if(isalpha(value_pt[l-1])) value/= 2048.0; skin->assert_write_lba= value; } else if(strcmp(argpt,"-atip")==0) { if(skin->do_atip<1) skin->do_atip= 1; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: will put out some -atip style lines\n")); } else if(strcmp(argpt,"-audio")==0) { skin->track_type= BURN_AUDIO; skin->track_type_by_default= 0; } else if(strncmp(argpt,"-blank=",7)==0) { cpt= argpt + 7; goto set_blank; } else if(strncmp(argpt,"blank=",6)==0) { cpt= argpt + 6; set_blank:; skin->blank_format_type= 0; blank_mode= cpt; if(strcmp(cpt,"all")==0 || strcmp(cpt,"disc")==0 || strcmp(cpt,"disk")==0) { skin->do_blank= 1; skin->blank_fast= 0; blank_mode= "all"; } else if(strcmp(cpt,"fast")==0 || strcmp(cpt,"minimal")==0) { skin->do_blank= 1; skin->blank_fast= 1; blank_mode= "fast"; } else if(strcmp(cpt,"format_if_needed")==0) { skin->do_blank= 1; skin->blank_format_type= 6; skin->preskin->demands_cdrskin_caps= 1; } else if(strcmp(cpt,"format_overwrite")==0) { skin->do_blank= 1; skin->blank_format_type= 1|(1<<8); skin->blank_format_size= 128*1024*1024; skin->preskin->demands_cdrskin_caps= 1; } else if(strcmp(cpt,"format_overwrite_full")==0) { skin->do_blank= 1; skin->blank_format_type= 1|(1<<10); skin->blank_format_size= 0; skin->preskin->demands_cdrskin_caps= 1; } else if(strcmp(cpt,"format_overwrite_quickest")==0) { skin->do_blank= 1; skin->blank_format_type= 1; skin->blank_format_size= 0; skin->preskin->demands_cdrskin_caps= 1; } else if(strncmp(cpt,"format_defectmgt",16)==0) { skin->do_blank= 1; skin->blank_format_type= 4|(3<<9); /* default payload size */ skin->blank_format_size= 0; skin->preskin->demands_cdrskin_caps= 1; if(cpt[16]=='_') { cpt+= 17; if(strcmp(cpt,"none")==0) skin->blank_format_type= 4|(1<<13); else if(strcmp(cpt,"max")==0) skin->blank_format_type= 4; /* smallest payload size above 0 */ else if(strcmp(cpt,"min")==0) skin->blank_format_type= 4|(2<<9); /*largest payload size with mgt*/ else if(strncmp(cpt,"payload_",8)==0) { skin->blank_format_size= Scanf_io_size(cpt+8,0); skin->blank_format_type= 4; } else if(strcmp(cpt,"cert_off")==0) skin->blank_format_no_certify= 1; else if(strcmp(cpt,"cert_on")==0) skin->blank_format_no_certify= 0; else goto unsupported_blank_option; } skin->preskin->demands_cdrskin_caps= 1; } else if(strncmp(cpt,"format_by_index_",16)==0) { sscanf(cpt+16, "%d", &idx); if(idx<0 || idx>255) { fprintf(stderr,"cdrskin: SORRY : blank=%s provides unusable index\n", cpt); return(0); } skin->do_blank= 1; skin->blank_format_type= 5|(2<<9)|(1<<15); skin->blank_format_index= idx; skin->blank_format_size= 0; skin->preskin->demands_cdrskin_caps= 1; } else if(strcmp(cpt,"deformat_sequential")==0) { skin->do_blank= 1; skin->blank_format_type= 2; skin->blank_fast= 0; skin->preskin->demands_cdrskin_caps= 1; } else if(strcmp(cpt,"deformat_sequential_quickest")==0) { skin->do_blank= 1; skin->blank_format_type= 2; skin->blank_fast= 1; skin->preskin->demands_cdrskin_caps= 1; } else if(strcmp(cpt,"as_needed")==0) { skin->do_blank= 1; skin->blank_format_type= 7; } else if(strcmp(cpt,"help")==0) { /* is handled in Cdrpreskin_setup() */; continue; } else { unsupported_blank_option:; fprintf(stderr,"cdrskin: FATAL : Blank option '%s' not supported yet\n", cpt); return(0); } if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: blank mode : blank=%s\n",blank_mode)); } else if(strcmp(argv[i],"--bragg_with_audio")==0) { /* OBSOLETE 0.2.3 : was handled in Cdrpreskin_setup() */; } else if(strncmp(argv[i], "-cd_start_tno=", 14) == 0) { value_pt= argv[i] + 14; goto set_cd_start_tno; } else if(strncmp(argv[i], "cd_start_tno=", 13) == 0) { value_pt= argv[i] + 13; set_cd_start_tno:; cd_start_tno= -1; sscanf(value_pt, "%d", &cd_start_tno); if(cd_start_tno < 1 || cd_start_tno > 99) { fprintf(stderr, "cdrskin: FATAL : cd_start_tno= gives a number outside [1 ... 99]\n"); return(0); } skin->cd_start_tno= cd_start_tno; } else if(strcmp(argv[i],"--cdtext_dummy")==0) { skin->cdtext_test= 2; } else if(strncmp(argv[i], "-cdtext_to_textfile=", 20) == 0) { value_pt= argv[i] + 20; goto set_cdtext_to_textfile; } else if(strncmp(argv[i], "cdtext_to_textfile=", 19) == 0) { value_pt= argv[i] + 19; set_cdtext_to_textfile:; if(strlen(value_pt) >= sizeof(skin->cdtext_to_textfile_path)) { fprintf(stderr, "cdrskin: FATAL : cdtext_to_textfile=... too long. (max: %d, given: %d)\n", (int) sizeof(skin->cdtext_to_textfile_path)-1,(int) strlen(value_pt)); return(0); } skin->do_cdtext_to_textfile= 1; strcpy(skin->cdtext_to_textfile_path, value_pt); } else if(strncmp(argv[i], "-cdtext_to_v07t=", 16) == 0) { value_pt= argv[i] + 16; goto set_cdtext_to_v07t; } else if(strncmp(argv[i], "cdtext_to_v07t=", 15) == 0) { value_pt= argv[i] + 15; set_cdtext_to_v07t:; if(strlen(value_pt) >= sizeof(skin->cdtext_to_vt07_path)) { fprintf(stderr, "cdrskin: FATAL : cdtext_to_vt07=... too long. (max: %d, given: %d)\n", (int) sizeof(skin->cdtext_to_vt07_path)-1,(int) strlen(value_pt)); return(0); } skin->do_cdtext_to_vt07= 1; strcpy(skin->cdtext_to_vt07_path, value_pt); } else if(strcmp(argv[i],"--cdtext_verbose")==0) { skin->cdtext_test= 1; } else if(strcmp(argpt,"-checkdrive")==0) { skin->do_checkdrive= 1; } else if(strcmp(argpt,"-copy")==0) { skin->track_modemods|= BURN_COPY; skin->track_modemods&= ~BURN_SCMS; } else if(strncmp(argpt, "-cuefile=", 9)==0) { value_pt= argpt + 9; goto set_cuefile; } else if(strncmp(argpt, "cuefile=", 8)==0) { value_pt= argpt + 8; set_cuefile:; if(strlen(value_pt) >= sizeof(skin->cuefile)) { fprintf(stderr, "cdrskin: FATAL : cuefile=... too long. (max: %d, given: %d)\n", (int) sizeof(skin->cuefile) - 1, (int) strlen(value_pt)); return(0); } strcpy(skin->cuefile, value_pt); skin->do_burn= 1; } else if(strcmp(argpt,"-data")==0) { option_data:; /* All Subsequent Tracks Option */ skin->cdxa_conversion= (skin->cdxa_conversion & ~0x7fffffff) | 0; skin->track_type= BURN_MODE1; skin->track_type_by_default= 0; } else if(strcmp(argv[i],"--demand_a_drive")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argv[i],"--devices")==0) { skin->do_devices= 1; } else if(strcmp(argv[i],"--device_links")==0) { skin->do_devices= 2; #ifndef Cdrskin_extra_leaN } else if(strncmp(argv[i],"dev_translation=",16)==0) { if(argv[i][16]==0) { fprintf(stderr, "cdrskin: FATAL : dev_translation= : missing separator character\n"); return(0); } ret= Cdradrtrn_add(skin->adr_trn,argv[i]+17,argv[i]+16,1); if(ret==-2) fprintf(stderr, "cdrskin: FATAL : address_translation= : cannot allocate memory\n"); else if(ret==-1) fprintf(stderr, "cdrskin: FATAL : address_translation= : table full (%d items)\n", Cdradrtrn_leN); else if(ret==0) fprintf(stderr, "cdrskin: FATAL : address_translation= : no address separator '%c' found\n", argv[i][17]); if(ret<=0) return(0); #endif /* ! Cdrskin_extra_leaN */ } else if(strncmp(argpt,"-dev=",5)==0) { /* is handled in Cdrpreskin_setup() */; } else if(strncmp(argpt,"dev=",4)==0) { /* is handled in Cdrpreskin_setup() */; } else if(strncmp(argv[i],"direct_write_amount=",20)==0) { skin->direct_write_amount= Scanf_io_size(argv[i]+20,0); if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: amount for direct writing : %.f\n", skin->direct_write_amount)); if(skin->direct_write_amount>=0.0) { skin->do_direct_write= 1; printf("cdrskin: NOTE : Direct writing will only use first track source and no fifo.\n"); skin->preskin->demands_cdrskin_caps= 1; } else skin->do_direct_write= 0; } else if(strcmp(argv[i],"--drive_abort_on_busy")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argv[i],"--drive_blocking")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argv[i],"--drive_not_exclusive")==0 || strcmp(argv[i],"--drive_not_f_setlk")==0 || strcmp(argv[i],"--drive_not_o_excl")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strncmp(argv[i],"drive_scsi_dev_family=",22)==0) { /* is handled in Cdrpreskin_setup() */; if(skin->verbosity>=Cdrskin_verbose_debuG && skin->preskin->drive_scsi_dev_family!=0) ClN(fprintf(stderr,"cdrskin_debug: drive_scsi_dev_family= %d\n", skin->preskin->drive_scsi_dev_family)); } else if(strcmp(argv[i],"--drive_scsi_exclusive")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strncmp(argpt, "-driveropts=", 12)==0) { value_pt= argpt + 12; goto set_driveropts; } else if(strncmp(argpt, "driveropts=", 11)==0) { value_pt= argpt + 11; set_driveropts:; if(strcmp(value_pt,"burnfree")==0 || strcmp(value_pt,"burnproof")==0) { skin->burnfree= 1; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: burnfree : on\n")); } else if(strcmp(argpt+11,"noburnfree")==0 || strcmp(argpt+11,"noburnproof")==0 ) { skin->burnfree= 0; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: burnfree : off\n")); } else if(strcmp(argpt+11,"help")==0) { /* handled in Cdrpreskin_setup() */; } else goto ignore_unknown; } else if(strcmp(argpt,"-dummy")==0) { skin->dummy_mode= 1; } else if(strncmp(argv[i], "-dvd_obs=", 9)==0) { value_pt= argv[i] + 9; goto dvd_obs; } else if(strncmp(argv[i], "dvd_obs=", 8)==0) { value_pt= argv[i] + 8; dvd_obs:; if(strcmp(value_pt, "default") == 0) num= 0; else num = Scanf_io_size(value_pt,0); if(num != 0 && num != 32768 && num != 65536) { fprintf(stderr, "cdrskin: SORRY : Option dvd_obs= accepts only sizes 0, 32k, 64k\n"); } else skin->dvd_obs= num; } else if(strcmp(argpt,"-eject")==0) { skin->do_eject= 1; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: eject after work : on\n")); } else if(strncmp(argv[i],"eject_device=",13)==0) { if(strlen(argv[i]+13)>=sizeof(skin->eject_device)) { fprintf(stderr, "cdrskin: FATAL : eject_device=... too long. (max: %d, given: %d)\n", (int) sizeof(skin->eject_device)-1,(int) strlen(argv[i]+13)); return(0); } strcpy(skin->eject_device,argv[i]+13); if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: ignoring obsolete eject_device=%s\n", skin->eject_device)); } else if(strncmp(argv[i],"-extract_audio_to=", 18)==0) { value_pt= argpt + 18; goto extract_audio_to; } else if(strncmp(argpt, "extract_audio_to=", 17) == 0) { value_pt= argpt + 17; extract_audio_to:; if(strlen(value_pt) >= sizeof(skin->extract_audio_dir)) { fprintf(stderr, "cdrskin: FAILURE : extract_audio_to=... is much too long.\n"); return(0); } skin->do_extract_audio= 1; strcpy(skin->extract_audio_dir, value_pt); } else if(strncmp(argv[i],"-extract_tracks=", 16)==0) { value_pt= argpt + 16; goto extract_tracks; } else if(strncmp(argpt, "extract_tracks=", 15) == 0) { value_pt= argpt + 15; extract_tracks:; value= 0.0; for(cpt= value_pt; ; cpt++) { if(*cpt >= '0' && *cpt <= '9') { value= value * 10 + *cpt - '0'; } else { if(value >= 1.0 && value <= 99.0) { skin->extract_audio_tracks[(int) value]= 1; if(skin->verbosity >= Cdrskin_verbose_cmD) fprintf(stderr, "cdrskin: Will extract track number %.f\n", value); } else { fprintf(stderr, "cdrskin: WARNING : extract_tracks= with unsuitable number: %.f\n", value); } if(*cpt == 0) break; value= 0; } } } else if(strncmp(argv[i],"-extract_basename=", 18)==0) { value_pt= argpt + 18; goto extract_basename; } else if(strncmp(argpt, "extract_basename=", 17) == 0) { value_pt= argpt + 17; extract_basename:; if(strchr(value_pt, '/') != NULL) { fprintf(stderr, "cdrskin: FAILURE : extract_basename=... may not contain '/'\n"); return(0); } if(strlen(value_pt) > 248) { fprintf(stderr, "cdrskin: FAILURE : Oversized extract_basename=... (Max. 248)\n"); return(0); } strcpy(skin->extract_basename, value_pt); } else if(strcmp(argv[i],"--extract_dap") == 0) { skin->extract_flags|= 8; #ifndef Cdrskin_extra_leaN } else if(strcmp(argv[i],"--fifo_disable")==0) { skin->fifo_enabled= 0; skin->fifo_size= 0; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: option fs=... disabled\n")); } else if(strcmp(argv[i],"--fifo_start_empty")==0) { /* obsoleted */ skin->fifo_start_at= 0; } else if(strncmp(argv[i],"fifo_start_at=",14)==0) { value= Scanf_io_size(argv[i]+14,0); if(value>1024.0*1024.0*1024.0) value= 1024.0*1024.0*1024.0; else if(value<0) value= 0; skin->fifo_start_at= value; } else if(strcmp(argv[i],"--fifo_per_track")==0) { skin->fifo_per_track= 1; #endif /* ! Cdrskin_extra_leaN */ } else if(strcmp(argv[i],"--fill_up_media")==0) { skin->fill_up_media= 1; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf( "cdrskin: will fill up last track to full free media space\n")); } else if(strcmp(argpt,"-force")==0) { skin->force_is_set= 1; } else if(strcmp(argpt,"-format")==0) { skin->do_blank= 1; skin->blank_format_type= 3|(1<<10); skin->blank_format_size= 0; skin->force_is_set= 1; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf( "cdrskin: will format DVD+RW by blank=format_overwrite_full -force\n")); } else if(strcmp(argv[i],"--four_channel")==0) { skin->track_modemods|= BURN_4CH; #ifndef Cdrskin_extra_leaN } else if(strncmp(argpt, "-fs=", 4) == 0) { value_pt= argpt + 4; goto fs_equals; } else if(strncmp(argpt, "fs=", 3) == 0) { value_pt= argpt + 3; fs_equals:; if(skin->fifo_enabled) { value= Scanf_io_size(value_pt,0); if(value<0.0 || value>1024.0*1024.0*1024.0) { fprintf(stderr, "cdrskin: FATAL : fs=N expects a size between 0 and 1g\n"); return(0); } skin->fifo_size= value; if(skin->verbosity>=Cdrskin_verbose_cmD) printf("cdrskin: fifo size : %d\n",skin->fifo_size); } } else if(strncmp(argv[i],"grab_drive_and_wait=",20)==0) { value_pt= argv[i]+20; grab_and_wait_value= Scanf_io_size(value_pt,0); skin->preskin->demands_cdrskin_caps= 1; } else if(strncmp(argpt, "-gracetime=", 11) == 0) { value_pt= argpt + 11; goto gracetime_equals; } else if(strncmp(argpt, "gracetime=", 10) == 0) { value_pt= argpt + 10; gracetime_equals:; sscanf(value_pt,"%d",&(skin->gracetime)); } else if(strncmp(argv[i],"--grow_overwriteable_iso",24)==0) { skin->grow_overwriteable_iso= 1; skin->use_data_image_size= 1; skin->preskin->demands_cdrskin_caps= 1; #else /* ! Cdrskin_extra_leaN */ } else if( strcmp(argv[i],"--fifo_disable")==0 || strcmp(argv[i],"--fifo_start_empty")==0 || strncmp(argv[i],"fifo_start_at=",14)==0 || strcmp(argv[i],"--fifo_per_track")==0 || strncmp(argv[i],"-fs=",4)==0 || strncmp(argv[i],"fs=",3)==0 || strncmp(argv[i],"-gracetime=",11)==0 || strncmp(argv[i],"gracetime=",10)==0) { fprintf(stderr, "cdrskin: NOTE : lean version ignores option: '%s'\n", argv[i]); #endif /* Cdrskin_extra_leaN */ } else if(strcmp(argv[i],"--help")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argv[i],"-help")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argv[i],"--ignore_signals")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argpt,"-immed")==0) { skin->modesty_on_drive= 1; skin->min_buffer_percent= 75; skin->max_buffer_percent= 95; } else if(strncmp(argpt, "-index=", 7) == 0) { value_pt= argpt + 7; goto set_index; } else if(strncmp(argpt, "index=", 6) == 0) { value_pt= argpt + 6; set_index:; if(skin->index_string != NULL) free(skin->index_string); skin->index_string= strdup(value_pt); if(skin->index_string == NULL) { fprintf(stderr, "cdrskin: FATAL : Out of memory\n"); return(-1); } } else if(strncmp(argv[i], "input_sheet_v07t=", 17)==0) { if(skin->sheet_v07t_blocks >= 8) { fprintf(stderr, "cdrskin: SORRY : Too many input_sheet_v07t= options. (Max. 8)\n"); return(0); } if(argv[i][17] == 0) { fprintf(stderr, "cdrskin: SORRY : Missing file path after option input_sheet_v07t=\n"); return(0); } if(strlen(argv[i] + 17) > Cdrskin_adrleN) { fprintf(stderr, "cdrskin: SORRY : File path too long after option input_sheet_v07t=\n"); return(0); } strcpy(skin->sheet_v07t_paths[skin->sheet_v07t_blocks], argv[i] + 17); skin->sheet_v07t_blocks++; skin->preskin->demands_cdrskin_caps= 1; } else if(strcmp(argpt,"-inq")==0) { skin->do_checkdrive= 2; } else if(strcmp(argpt,"-isosize")==0) { skin->use_data_image_size= 1; } else if(strncmp(argpt, "-isrc=", 6) == 0) { value_pt= argpt + 6; goto set_isrc; } else if(strncmp(argpt, "isrc=", 5) == 0) { value_pt= argpt + 5; set_isrc:; if(strlen(value_pt) != 12) { fprintf(stderr, "cdrskin: SORRY : isrc=... is not exactly 12 characters long.\n"); return(0); } memcpy(skin->next_isrc, value_pt, 13); } else if(strcmp(argv[i],"--list_formats")==0) { skin->do_list_formats= 1; skin->preskin->demands_cdrskin_caps= 1; } else if(strcmp(argv[i],"--list_ignored_options")==0) { /* is also handled in Cdrpreskin_setup() */; printf("cdrskin: List of all ignored options:\n"); for(k=0;ignored_partial_options[k][0]!=0;k++) printf("%s\n",ignored_partial_options[k]); for(k=0;ignored_full_options[k][0]!=0;k++) printf("%s\n",ignored_full_options[k]); printf("\n"); } else if(strcmp(argv[i],"--list_speeds")==0) { skin->do_list_speeds= 1; skin->preskin->demands_cdrskin_caps= 1; } else if(strcmp(argv[i],"--list_features")==0) { skin->do_list_features= 1; skin->preskin->demands_cdrskin_caps= 1; } else if(strncmp(argv[i],"fallback_program=",17)==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argpt,"-load")==0) { skin->do_load= 1; } else if(strcmp(argpt,"-lock")==0) { skin->do_load= 2; } else if(strcmp(argv[i],"--long_toc")==0) { skin->do_atip= 3; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: will put out some -atip style lines plus -toc\n")); } else if(strncmp(argpt,"-mcn=", 5) == 0) { value_pt= argpt + 5; goto set_mcn; } else if(strncmp(argpt,"mcn=", 4) == 0) { value_pt= argpt + 4; set_mcn:; if(strlen(value_pt) != 13) { fprintf(stderr, "cdrskin: SORRY : mcn=... is not exactly 13 characters long.\n"); return(0); } memcpy(skin->mcn, value_pt, 14); } else if(strncmp(argpt, "-minbuf=", 8) == 0) { value_pt= argpt + 8; goto minbuf_equals; } else if(strncmp(argpt, "minbuf=", 7) == 0) { value_pt= argpt + 7; minbuf_equals:; skin->modesty_on_drive= 1; sscanf(value_pt,"%lf",&value); if (value<25 || value>95) { fprintf(stderr, "cdrskin: FATAL : minbuf= value must be between 25 and 95\n"); return(0); } skin->min_buffer_percent= value; skin->max_buffer_percent= 95; ClN(printf("cdrskin: minbuf=%d percent desired buffer fill\n", skin->min_buffer_percent)); } else if(strcmp(argpt,"-minfo") == 0 || strcmp(argpt,"-media-info") == 0) { skin->do_atip= 4; } else if(strcmp(argpt, "-mode2") == 0) { fprintf(stderr, "cdrskin: NOTE : defaulting option -mode2 to option -data\n"); goto option_data; } else if(strncmp(argv[i],"modesty_on_drive=",17)==0) { value_pt= argv[i]+17; if(*value_pt == '0' || strncmp(value_pt, "off", 3) == 0) { skin->modesty_on_drive= 0; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf( "cdrskin: modesty_on_drive=0 : buffer waiting by os driver\n")); } else if(*value_pt=='1' || strncmp(value_pt, "on", 2) == 0) { skin->modesty_on_drive= 1; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf( "cdrskin: modesty_on_drive=1 : buffer waiting by libburn\n")); } else if(*value_pt=='-' && argv[i][18]=='1') { skin->modesty_on_drive= -1; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf( "cdrskin: modesty_on_drive=-1 : buffer waiting as libburn defaults\n")); } else { fprintf(stderr, "cdrskin: FATAL : modesty_on_drive= must be -1, 0 or 1\n"); return(0); } while(1) { value_pt= strchr(value_pt,':'); if(value_pt==NULL) break; value_pt++; if(strncmp(value_pt,"min_percent=",12)==0) { sscanf(value_pt+12,"%lf",&value); if (value<25 || value>100) { fprintf(stderr, "cdrskin: FATAL : modest_on_drive= min_percent value must be between 25 and 100\n"); return(0); } skin->min_buffer_percent= value; if(skin->verbosity >= Cdrskin_verbose_cmD) ClN(printf( "cdrskin: modesty_on_drive : %d percent min buffer fill\n", skin->min_buffer_percent)); } else if(strncmp(value_pt,"max_percent=",12)==0) { sscanf(value_pt+12,"%lf",&value); if (value<25 || value>100) { fprintf(stderr, "cdrskin: FATAL : modest_on_drive= max_percent value must be between 25 and 100\n"); return(0); } skin->max_buffer_percent= value; if(skin->verbosity >= Cdrskin_verbose_cmD) ClN(printf( "cdrskin: modesty_on_drive : %d percent max buffer fill\n", skin->max_buffer_percent)); } else if(strncmp(value_pt, "min_usec=", 9) == 0) { sscanf(value_pt + 9, "%lf", &value); if(value < 0) value= 0; skin->min_buffer_usec= value; if(skin->verbosity >= Cdrskin_verbose_cmD) ClN(printf( "cdrskin: modesty_on_drive : %d microseconds minimum sleep time\n", skin->min_buffer_usec)); } else if(strncmp(value_pt,"max_usec=", 9)==0) { sscanf(value_pt + 9, "%lf", &value); if(value < 0) value= 0; skin->max_buffer_usec= value; if(skin->verbosity >= Cdrskin_verbose_cmD) ClN(printf( "cdrskin: modesty_on_drive : %d microseconds maximum sleep time\n", skin->max_buffer_usec)); } else if(strncmp(value_pt,"timeout_sec=", 12)==0) { sscanf(value_pt + 9, "%lf", &value); if(value < 0) value= 0; skin->buffer_timeout_sec= value; if(skin->verbosity >= Cdrskin_verbose_cmD) ClN(printf( "cdrskin: modesty_on_drive : %d seconds fallback timeout\n", skin->max_buffer_usec)); } else { fprintf(stderr, "cdrskin: SORRY : modest_on_drive= unknown option code : %s\n", value_pt); } } skin->preskin->demands_cdrskin_caps= 1; } else if(strcmp(argpt,"-multi")==0) { skin->multi= 1; } else if(strncmp(argpt, "-msifile=", 9) == 0) { value_pt= argpt + 9; goto msifile_equals; } else if(strncmp(argpt, "msifile=", 8) == 0) { value_pt= argpt + 8; msifile_equals:; if(strlen(value_pt)>=sizeof(skin->msifile)) { fprintf(stderr, "cdrskin: FATAL : msifile=... too long. (max: %d, given: %d)\n", (int) sizeof(skin->msifile)-1,(int) strlen(value_pt)); return(0); } strcpy(skin->msifile, value_pt); skin->do_msinfo= 1; } else if(strcmp(argpt,"-msinfo")==0) { skin->do_msinfo= 1; #ifdef Libburn_develop_quality_scaN } else if(strcmp(argv[i],"--nec_optiarc_qcheck")==0) { skin->do_qcheck= 1; #endif /* Libburn_develop_quality_scaN */ } else if(strcmp(argv[i],"--no_abort_handler")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argv[i],"--no_blank_appendable")==0) { skin->no_blank_appendable= 1; } else if(strcmp(argv[i],"--no_convert_fs_adr")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argv[i],"--no_load")==0) { skin->do_load= -1; } else if(strcmp(argv[i],"--no_rc")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argpt,"-nocopy")==0) { skin->track_modemods&= ~BURN_COPY; } else if(strcmp(argpt,"-nopad")==0) { skin->padding= 0.0; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: padding : off\n")); } else if(strcmp(argpt,"-nopreemp")==0) { skin->track_modemods&= ~BURN_PREEMPHASIS; } else if(strcmp(argv[i],"--obs_pad")==0) { skin->obs_pad= 1; } else if(strcmp(argv[i],"--bdr_obs_exempt")==0) { skin->bdr_obs_exempt= 1; } else if(strcmp(argv[i],"--old_pseudo_scsi_adr")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argv[i], "--pacifier_with_newline") == 0) { skin->pacifier_with_newline= 1; } else if(strcmp(argpt,"-pad")==0) { skin->padding= 15*2048; skin->set_by_padsize= 0; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: padding : %.f\n",skin->padding)); } else if(strncmp(argpt, "-padsize=", 9) == 0) { value_pt= argpt + 9; goto set_padsize; } else if(strncmp(argpt, "padsize=", 8) == 0) { value_pt= argpt + 8; set_padsize:; skin->padding= Scanf_io_size(value_pt, 0); skin->set_by_padsize= 1; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: padding : %.f\n",skin->padding)); } else if(strcmp(argpt,"-preemp")==0) { skin->track_modemods|= BURN_PREEMPHASIS; } else if(strcmp(argv[i],"--prodvd_cli_compatible")==0) { skin->prodvd_cli_compatible= 1; } else if(strcmp(argpt,"-sao")==0 || strcmp(argpt,"-dao")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argpt,"-scanbus")==0) { skin->do_scanbus= 1; } else if(strcmp(argpt,"-scms")==0) { skin->track_modemods|= BURN_SCMS; } else if(strcmp(argv[i],"--single_track")==0) { skin->single_track= 1; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf( "cdrskin: --single_track : will only accept last argument as track source\n")); skin->preskin->demands_cdrskin_caps= 1; } else if(strncmp(argv[i], "-sao_postgap=", 13) == 0) { value_pt= argv[i] + 13; goto set_sao_postgap; } else if(strncmp(argv[i], "sao_postgap=", 12) == 0) { value_pt= argv[i] + 12; set_sao_postgap:; skin->sao_postgap= -1; if(strcmp(value_pt, "off") == 0) skin->sao_postgap = -1; else sscanf(value_pt, "%d", &(skin->sao_postgap)); if(skin->sao_postgap < 0) { fprintf(stderr, "cdrskin: SORRY : sao_postgap must be \"off\" or a number >= 0\n"); return(0); } } else if(strncmp(argv[i], "-sao_pregap=", 12) == 0) { value_pt= argv[i] + 12; goto set_sao_pregap; } else if(strncmp(argv[i], "sao_pregap=", 11) == 0) { value_pt= argv[i] + 11; set_sao_pregap:; skin->sao_pregap= -1; if(strcmp(value_pt, "off") == 0) skin->sao_pregap = -1; else sscanf(value_pt, "%d", &(skin->sao_pregap)); if(skin->sao_pregap < 0) { fprintf(stderr, "cdrskin: SORRY : sao_pregap must be \"off\" or a number >= 0\n"); return(0); } } else if(strncmp(argpt, "-speed=", 7) == 0) { value_pt= argpt + 7; goto set_speed; } else if(strncmp(argpt, "speed=", 6) == 0) { value_pt= argpt + 6; set_speed:; if(strcmp(value_pt,"any")==0) skin->x_speed= -1; else sscanf(value_pt,"%lf",&(skin->x_speed)); if(skin->x_speed<1.0 && skin->x_speed!=0.0 && skin->x_speed!=-1) { fprintf(stderr,"cdrskin: FATAL : speed= must be -1, 0 or at least 1\n"); return(0); } if(skin->x_speed<0) skin->preskin->demands_cdrskin_caps= 1; /* >>> cdrecord speed=0 -> minimum speed , libburn -> maximum speed */; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: speed : %f\n",skin->x_speed)); } else if(strncmp(argv[i], "-stdio_sync=", 12)==0) { value_pt= argv[i] + 12; goto stdio_sync; } else if(strncmp(argv[i], "stdio_sync=", 11)==0) { value_pt= argv[i] + 11; stdio_sync:; if(strcmp(value_pt, "default") == 0 || strcmp(value_pt, "on") == 0) num= 0; else if(strcmp(value_pt, "off") == 0) num= -1; else num = Scanf_io_size(value_pt,0); if(num > 0) num/= 2048; if(num != -1 && num != 0 && (num < 32 || num > 512 * 1024)) { fprintf(stderr, "cdrskin: SORRY : Option stdio_sync= accepts only sizes -1, 0, 32k ... 1g\n"); } else skin->stdio_sync= num; } else if(strncmp(argv[i],"-stream_recording=",18)==0) { value_pt= argv[i]+18; goto set_stream_recording; } else if(strncmp(argv[i],"stream_recording=",17)==0) { value_pt= argv[i]+17; set_stream_recording:; if(strcmp(value_pt, "on")==0) skin->stream_recording_is_set= 1; else if(value_pt[0] >= '0' && value_pt[0] <= '9') { num= Scanf_io_size(value_pt, 0); num/= 2048.0; if(num >= 16 && num <= 0x7FFFFFFF) skin->stream_recording_is_set= num; else skin->stream_recording_is_set= 0; } else skin->stream_recording_is_set= 0; } else if(strcmp(argpt,"-swab")==0) { skin->swap_audio_bytes= 0; } else if(strcmp(argpt,"-tao")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strncmp(argv[i],"tao_to_sao_tsize=",17)==0) { skin->tao_to_sao_tsize= Scanf_io_size(argv[i]+17,0); if(skin->tao_to_sao_tsize>Cdrskin_tracksize_maX) goto track_too_large; skin->preskin->demands_cdrskin_caps= 1; #ifndef Cdrskin_extra_leaN if(skin->verbosity>=Cdrskin_verbose_cmD) printf("cdrskin: size default for non-tao write modes: %.f\n", skin->tao_to_sao_tsize); #endif /* ! Cdrskin_extra_leaN */ } else if(strcmp(argv[i],"--tell_media_space")==0) { skin->tell_media_space= 1; skin->preskin->demands_cdrskin_caps= 1; } else if(strcmp(argpt, "-text") == 0) { skin->use_cdtext= 1; } else if(strncmp(argpt, "-textfile=", 10) == 0) { value_pt= argpt + 10; goto set_textfile; } else if(strncmp(argpt ,"textfile=", 9) == 0) { value_pt= argpt + 9; set_textfile:; ret= Cdrskin_read_textfile(skin, value_pt, 0); if(ret <= 0) return(ret); } else if(strncmp(argpt, "-textfile_to_v07t=", 18) == 0) { value_pt= argpt + 18; goto textfile_to_v07t; } else if(strncmp(argpt ,"textfile_to_v07t=", 17) == 0) { value_pt= argpt + 17; textfile_to_v07t:; ret= Cdrskin_read_textfile(skin, value_pt, 0); if(ret <= 0) return(ret); ret= Cdrskin_print_text_packs(skin, skin->text_packs, skin->num_text_packs, (1 << 4) | 2); if(ret <= 0) return(ret); if(i != 1 || argc != 2) fprintf(stderr, "cdrskin: WARNING : Program run ended by option textfile_to_v07t=. Other options may have been ignored.\n"); return(2); } else if(strcmp(argpt,"-toc")==0) { skin->do_atip= 2; if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: will put out some -atip style lines plus -toc\n")); } else if(strncmp(argpt, "-tsize=", 7) == 0) { value_pt= argpt + 7; goto set_tsize; } else if(strncmp(argpt, "tsize=", 6) == 0) { value_pt= argpt + 6; set_tsize:; skin->fixed_size= Scanf_io_size(value_pt,0); if(skin->fixed_size>Cdrskin_tracksize_maX) { track_too_large:; fprintf(stderr,"cdrskin: FATAL : Track size too large\n"); return(0); } if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: fixed track size : %.f\n",skin->fixed_size)); if(skin->smallest_tsize<0 || skin->smallest_tsize>skin->fixed_size) skin->smallest_tsize= skin->fixed_size; } else if(strcmp(argv[i],"--two_channel")==0) { skin->track_modemods&= ~BURN_4CH; } else if(strncmp(argv[i], "use_immed_bit=", 14) == 0) { if(strcmp(argv[i] + 14, "on") == 0) { skin->use_immed= 1; } else if(strcmp(argv[i] + 14, "off") == 0) { skin->use_immed= -1; } else if(strcmp(argv[i] + 14, "default") == 0) { skin->use_immed= 0; } else { fprintf(stderr, "cdrskin: FATAL : use_immed_bits= must be \"on\", \"off\", or \"default\"\n"); return(0); } } else if(strcmp(argv[i],"-V") == 0 || strcmp(argpt, "-Verbose") == 0 || strcmp(argv[i],"-VV") == 0 || strcmp(argpt, "-VVV") == 0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argv[i],"-v")==0 || strcmp(argpt,"-verbose")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argv[i],"-vv")==0 || strcmp(argv[i],"-vvv")==0 || strcmp(argv[i],"-vvvv")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strcmp(argpt,"-version")==0) { /* is handled in Cdrpreskin_setup() and should really not get here */; } else if(strcmp(argpt,"-waiti")==0) { /* is handled in Cdrpreskin_setup() */; } else if(strncmp(argv[i],"write_start_address=",20)==0) { skin->write_start_address= Scanf_io_size(argv[i]+20,0); if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: write start address : %.f\n", skin->write_start_address)); skin->preskin->demands_cdrskin_caps= 1; } else if(strcmp(argpt, "-xa") == 0) { fprintf(stderr,"cdrskin: NOTE : defaulting option -xa to option -data\n"); goto option_data; } else if(strcmp(argpt, "-xa1") == 0) { /* All Subsequent Tracks Option */ skin->cdxa_conversion= (skin->cdxa_conversion & ~0x7fffffff) | 1; skin->track_type= BURN_MODE1; skin->track_type_by_default= 0; } else if(strcmp(argpt, "-xa2") == 0) { fprintf(stderr, "cdrskin: NOTE : defaulting option -xa2 to option -data\n"); goto option_data; } else if(strcmp(argv[i], "--xa1-ignore") == 0) { skin->cdxa_conversion|= (1 << 31); } else if( i==argc-1 || (skin->single_track==0 && strchr(argv[i],'=')==NULL && !(argv[i][0]=='-' && argv[i][1]!=0) ) || (skin->single_track==-1)) { if(strlen(argv[i])>=sizeof(skin->source_path)) { fprintf(stderr, "cdrskin: FATAL : Source address too long. (max: %d, given: %d)\n", (int) sizeof(skin->source_path)-1,(int) strlen(argv[i])); return(0); } strcpy(skin->source_path,argv[i]); if(strcmp(skin->source_path,"-")==0) { if(skin->stdin_source_used) { fprintf(stderr, "cdrskin: FATAL : \"-\" (stdin) can be used as track source only once.\n"); return(0); } skin->stdin_source_used= 1; } else if(argv[i][0]=='#' && (argv[i][1]>='0' && argv[i][1]<='9')) { if(skin->preskin->allow_fd_source==0) { fprintf(stderr, "cdrskin: SORRY : '%s' is a reserved source path with cdrskin\n", argv[i]); fprintf(stderr, "cdrskin: SORRY : which would use an open file descriptor as source.\n"); fprintf(stderr, "cdrskin: SORRY : Its usage is dangerous and disabled for now.\n"); return(0); } } else { if(stat(skin->source_path,&stbuf)!=-1) { if((stbuf.st_mode&S_IFMT)==S_IFREG) ; else if((stbuf.st_mode&S_IFMT)==S_IFDIR) { fprintf(stderr, "cdrskin: FATAL : Source address is a directory: '%s'\n", skin->source_path); return(0); } } } if(skin->track_counter>=Cdrskin_track_maX) { fprintf(stderr,"cdrskin: FATAL : Too many tracks given. (max %d)\n", Cdrskin_track_maX); return(0); } ret= Cdrtrack_new(&(skin->tracklist[skin->track_counter]),skin, skin->track_counter, (strcmp(skin->source_path,"-")==0)<<1); if(ret<=0) { fprintf(stderr, "cdrskin: FATAL : Creation of track control object failed.\n"); return(ret); } if(skin->next_isrc[0]) memcpy(skin->tracklist[skin->track_counter]->isrc, skin->next_isrc, 13); skin->tracklist[skin->track_counter]->index_string= skin->index_string; skin->tracklist[skin->track_counter]->sao_pregap= skin->sao_pregap; skin->tracklist[skin->track_counter]->sao_postgap= skin->sao_postgap; skin->index_string= NULL; skin->sao_postgap= skin->sao_pregap= -1; skin->track_counter++; skin->use_data_image_size= 0; if(skin->verbosity>=Cdrskin_verbose_cmD) { if(strcmp(skin->source_path,"-")==0) printf("cdrskin: track %d data source : '-' (i.e. standard input)\n", skin->track_counter); else printf("cdrskin: track %d data source : '%s'\n", skin->track_counter,skin->source_path); } /* reset track options */ if(skin->set_by_padsize) skin->padding= 0; /* cdrecord-ProDVD-2.01b31 resets to 30k the man page says padsize= is reset to 0 Joerg Schilling will change in 2.01.01 to 0 */ skin->fixed_size= 0; skin->next_isrc[0]= 0; } else { ignore_unknown:; if(skin->preskin->fallback_program[0]) fprintf(stderr,"cdrskin: NOTE : Unknown option : '%s'\n",argv[i]); else fprintf(stderr,"cdrskin: NOTE : ignoring unknown option : '%s'\n", argv[i]); skin->preskin->demands_cdrecord_caps= 1; } } if(flag&1) /* no finalizing yet */ return(1); if(skin->preskin->fallback_program[0] && skin->preskin->demands_cdrecord_caps>0 && skin->preskin->demands_cdrskin_caps<=0) { fprintf(stderr,"cdrskin: NOTE : Unsupported options found.\n"); fprintf(stderr, "cdrskin: NOTE : Will delegate job to fallback program '%s'.\n", skin->preskin->fallback_program); return(0); } #ifndef Cdrskin_extra_leaN if(skin->verbosity>=Cdrskin_verbose_cmD) { if(skin->preskin->abort_handler==1) printf("cdrskin: installed abort handler.\n"); else if(skin->preskin->abort_handler==2) printf("cdrskin: will try to ignore any signals.\n"); else if(skin->preskin->abort_handler==3) printf("cdrskin: installed hard abort handler.\n"); else if(skin->preskin->abort_handler==4) printf("cdrskin: installed soft abort handler.\n"); else if(skin->preskin->abort_handler==-1) printf("cdrskin: will install abort handler in eventual burn loop.\n"); } #endif /* ! Cdrskin_extra_leaN */ if(strlen(skin->preskin->raw_device_adr)>0 || strlen(skin->preskin->device_adr)>0) { if(strlen(skin->preskin->device_adr)>0) cpt= skin->preskin->device_adr; else cpt= skin->preskin->raw_device_adr; if(strcmp(cpt,"ATA")!=0 && strcmp(cpt,"ATAPI")!=0 && strcmp(cpt,"SCSI")!=0){ ret= Cdrskin_dev_to_driveno(skin,cpt,&(skin->driveno),0); if(ret<=0) return(ret); if(skin->verbosity>=Cdrskin_verbose_cmD) { ret= burn_drive_get_adr(&(skin->drives[skin->driveno]), adr); if(ret<=0) adr[0]= 0; printf("cdrskin: active drive number : %d '%s'\n", skin->driveno,adr); } } } if(grab_and_wait_value>0) { Cdrskin_grab_drive(skin,16); for(k= 0; kpacifier_with_newline ? "" : "\r", k, skin->pacifier_with_newline ? "\n" : " "); usleep(1000000); } fprintf(stderr, "%scdrskin: held drive grabbed for %d seconds \n", skin->pacifier_with_newline ? "" : "\r", k); Cdrskin_release_drive(skin,0); } if(skin->track_counter>0) { skin->do_burn= 1; #ifndef Cdrskin_no_cdrfifO if(!skin->do_direct_write) { ret= Cdrskin_attach_fifo(skin,0); if(ret<=0) return(ret); } #endif /* ! Cdrskin_no_cdrfifO */ } return(1); } /** Initialize libburn, create a CdrskiN program run control object, set eventual device whitelist, and obtain the list of available drives. @param o Returns the CdrskiN object created @param lib_initialized Returns whether libburn was initialized here @param exit_value Returns after error the proposal for an exit value @param flag bit0= do not scan for devices @return <=0 error, 1 success */ int Cdrskin_create(struct CdrskiN **o, struct CdrpreskiN **preskin, int *exit_value, int flag) { int ret, stdio_drive= 0, mem; struct CdrskiN *skin; char reason[4096]; *o= NULL; *exit_value= 0; if(strlen((*preskin)->device_adr)>0) { /* disable scan for all others */ ClN(printf( "cdrskin: NOTE : greying out all drives besides given dev='%s'\n", (*preskin)->device_adr)); burn_drive_add_whitelist((*preskin)->device_adr); if(strncmp((*preskin)->device_adr, "stdio:", 6)==0) { ret= Cdrpreskin__allows_emulated_drives((*preskin)->device_adr+6,reason,0); if((*preskin)->allow_emulated_drives && ret>0) { stdio_drive= 1; (*preskin)->demands_cdrskin_caps= 1; } else if((*preskin)->allow_emulated_drives) { fprintf(stderr,"cdrskin: SORRY : dev=stdio:... rejected despite --allow_emulated_drives\n"); fprintf(stderr,"cdrskin: SORRY : Reason: %s.\n", reason); } else { fprintf(stderr,"cdrskin: SORRY : dev=stdio:... works only with option --allow_emulated_drives\n"); if(ret<=0) { fprintf(stderr,"cdrskin: SORRY : but: %s.\n", reason); fprintf(stderr, "cdrskin: SORRY : So this option would not help anyway.\n"); } } if(!stdio_drive) { Cdrpreskin_consider_normal_user(0); {*exit_value= 2; goto ex;} } } } ret= Cdrskin_new(&skin,*preskin,1); if(ret<=0) { fprintf(stderr,"cdrskin: FATAL : Creation of control object failed\n"); {*exit_value= 2; goto ex;} } *preskin= NULL; /* the preskin object now is under management of skin */ *o= skin; if(skin->preskin->abort_handler==1 || skin->preskin->abort_handler==3 || skin->preskin->abort_handler==4) Cleanup_set_handlers(Cleanup_handler_handlE, Cleanup_handler_funC, Cleanup_handler_flaG); else if(skin->preskin->abort_handler==2) Cleanup_set_handlers(Cleanup_handler_handlE, Cleanup_handler_funC, 2 | 8); if(!(flag & 1)) printf("cdrskin: scanning for devices ...\n"); fflush(stdout); if(skin->preskin->verbositypreskin,1); if(stdio_drive) { mem= skin->drive_is_busy; skin->drive_is_busy= 1; ret= burn_drive_scan_and_grab(&(skin->drives),skin->preskin->device_adr,0); skin->drive_is_busy= mem; if(Cdrskin__is_aborting(0)) { fprintf(stderr,"cdrskin: ABORT : Startup aborted\n"); Cdrskin_abort(skin, 0); /* Never comes back */ } if(ret <= 0) { fprintf(stderr,"cdrskin: FATAL : Failed to grab emulated stdio-drive\n"); {*exit_value= 2; goto ex;} } skin->n_drives= 1; skin->driveno= 0; burn_drive_release(skin->drives[0].drive, 0); } else if(flag & 1){ skin->n_drives= 0; skin->driveno= 0; } else { while (!burn_drive_scan(&(skin->drives), &(skin->n_drives))) { usleep(20000); /* >>> ??? set a timeout ? */ } if(skin->n_drives <= 0) skin->driveno= -1; } burn_msgs_set_severities(skin->preskin->queue_severity, skin->preskin->print_severity, "cdrskin: "); /* This prints the eventual queued messages */ Cdrpreskin_queue_msgs(skin->preskin,0); if(!(flag & 1)) printf("cdrskin: ... scanning for devices done\n"); fflush(stdout); ex:; return((*exit_value)==0); } /** Perform the activities which were ordered by setup @param skin Knows what to do @param exit_value Returns the proposal for an exit value @param flag Unused yet @return <=0 error, 1 success */ int Cdrskin_run(struct CdrskiN *skin, int *exit_value, int flag) { int ret; *exit_value= 0; if(skin->preskin->allow_setuid==0 && getuid()!=geteuid()) { fprintf(stderr,"\n"); fprintf(stderr,"cdrskin: WARNING : THIS PROGRAM WAS TREATED WITH chmod u+s WHICH IS INSECURE !\n"); fprintf(stderr, "cdrskin: HINT : Consider to allow rw-access to the writer device and\n"); fprintf(stderr, "cdrskin: HINT : to run cdrskin under your normal user identity.\n"); fprintf(stderr, "cdrskin: HINT : Option --allow_setuid disables this safety check.\n"); fprintf(stderr,"\n"); } if(skin->do_devices) { if(skin->n_drives<=0 && skin->preskin->scan_demands_drive) {*exit_value= 4; goto no_drive;} if(Cdrskin__is_aborting(0)) goto ex; ret= Cdrskin_scanbus(skin, 1 | (2 * (skin->do_devices == 2))); if(ret<=0) { fprintf(stderr,"cdrskin: FATAL : --devices failed.\n"); {*exit_value= 4; goto ex;} } } if(skin->do_scanbus) { if(skin->n_drives<=0 && skin->preskin->scan_demands_drive) {*exit_value= 5; goto no_drive;} if(Cdrskin__is_aborting(0)) goto ex; ret= Cdrskin_scanbus(skin,0); if(ret<=0) fprintf(stderr,"cdrskin: FATAL : -scanbus failed.\n"); {*exit_value= 5*(ret<=0); goto ex;} } if(skin->do_load > 0) { if(Cdrskin__is_aborting(0)) goto ex; ret= Cdrskin_grab_drive(skin,8); if(ret>0) { if(skin->do_load==2 && !skin->do_eject) { printf( "cdrskin: NOTE : Option -lock orders program to exit with locked tray.\n"); printf( "cdrskin: HINT : Run cdrskin with option -eject to unlock the drive tray.\n"); } else if(!skin->do_eject) printf( "cdrskin: NOTE : option -load orders program to exit after loading tray.\n"); Cdrskin_release_drive(skin,(skin->do_load==2)<<1); } {*exit_value= 14*(ret<=0); goto ex;} } if(skin->do_checkdrive) { if(Cdrskin__is_aborting(0)) goto ex; ret= Cdrskin_checkdrive(skin,"",(skin->do_checkdrive==2)<<1); {*exit_value= 6*(ret<=0); goto ex;} } if(skin->do_msinfo) { if(skin->n_drives<=0) {*exit_value= 12; goto no_drive;} if(Cdrskin__is_aborting(0)) goto ex; ret= Cdrskin_msinfo(skin,0); if(ret<=0) {*exit_value= 12; goto ex;} } if(skin->do_atip) { if(skin->n_drives<=0) {*exit_value= 7; goto no_drive;} if(Cdrskin__is_aborting(0)) goto ex; ret= Cdrskin_atip(skin, skin->do_atip == 4 ? 4 : (skin->do_atip>1) | (2 * (skin->do_atip > 2))); if(ret<=0) {*exit_value= 7; goto ex;} } if(skin->do_cdtext_to_textfile) { ret= Cdrskin_cdtext_to_file(skin, skin->cdtext_to_textfile_path, 15); if(ret<=0) {*exit_value= 18; goto ex;} } if(skin->do_cdtext_to_vt07) { ret= Cdrskin_cdtext_to_file(skin, skin->cdtext_to_vt07_path, 1); if(ret<=0) {*exit_value= 19; goto ex;} } if(skin->do_extract_audio) { ret= Cdrskin_extract_audio_to_dir(skin, skin->extract_audio_dir, skin->extract_basename, skin->extract_audio_tracks, skin->extract_flags & 8); if(ret<=0) {*exit_value= 19; goto ex;} } if(skin->do_list_speeds) { if(skin->n_drives<=0) {*exit_value= 17; goto no_drive;} if(Cdrskin__is_aborting(0)) goto ex; ret= Cdrskin_list_speeds(skin, 0); if(ret<=0) {*exit_value= 17; goto ex;} } if(skin->do_list_formats) { if(skin->n_drives<=0) {*exit_value= 16; goto no_drive;} if(Cdrskin__is_aborting(0)) goto ex; ret= Cdrskin_list_formats(skin, 0); if(ret<=0) {*exit_value= 16; goto ex;} } if(skin->do_list_features) { if(skin->n_drives<=0) {*exit_value= 20; goto no_drive;} if(Cdrskin__is_aborting(0)) goto ex; ret= Cdrskin_list_features(skin, 0); if(ret<=0) {*exit_value= 20; goto ex;} } if(skin->do_blank) { if(skin->n_drives<=0) {*exit_value= 8; goto no_drive;} if(Cdrskin__is_aborting(0)) goto ex; ret= Cdrskin_blank(skin,0); if(ret<=0) {*exit_value= 8; goto ex;} } if(skin->do_direct_write) { skin->do_burn= 0; if(Cdrskin__is_aborting(0)) goto ex; ret= Cdrskin_direct_write(skin,0); if(ret<=0) {*exit_value= 13; goto ex;} } if(skin->do_burn || skin->tell_media_space) { if(skin->n_drives<=0) {*exit_value= 10; goto no_drive;} if(Cdrskin__is_aborting(0)) goto ex; ret= Cdrskin_burn(skin,0); if(ret<=0) {*exit_value= 10; goto ex;} } #ifdef Libburn_develop_quality_scaN if(skin->do_qcheck) { ret= Cdrskin_qcheck(skin, 0); if(ret<=0) {*exit_value= 15; goto ex;} } #endif /* Libburn_develop_quality_scaN */ ex:; if(Cdrskin__is_aborting(0)) Cdrskin_abort(skin, 0); /* Never comes back */ return((*exit_value)==0); no_drive:; fprintf(stderr,"cdrskin: FATAL : This run would need an accessible drive\n"); goto ex; } int main(int argc, char **argv) { int ret,exit_value= 0,lib_initialized= 0,i,result_fd= -1, do_not_scan; struct CdrpreskiN *preskin= NULL, *h_preskin= NULL; struct CdrskiN *skin= NULL; char *lean_id= ""; #ifdef Cdrskin_extra_leaN lean_id= ".lean"; #endif /* For -msinfo: Redirect normal stdout to stderr */ for(i=1; ido_not_scan; /* preskin will vanish in Cdrskin_create */ ret= Cdrskin_create(&skin,&preskin,&exit_value, preskin->do_not_scan); if(ret<=0) {exit_value= 2; goto ex;} if(skin->n_drives<=0 && !do_not_scan) { fprintf(stderr,"cdrskin: NOTE : No usable drive detected.\n"); if(getuid()!=0) { fprintf(stderr, "cdrskin: HINT : Run this program as superuser with option --devices\n"); fprintf(stderr, "cdrskin: HINT : Allow rw-access to the dev='...' file of the burner.\n"); fprintf(stderr, "cdrskin: HINT : Busy drives are invisible. (Busy = open O_EXCL)\n"); } } ret= Cdrskin_setup(skin,argc,argv,0); if(ret<=0) {exit_value= 3; goto ex;} if(skin->verbosity>=Cdrskin_verbose_cmD) ClN(printf("cdrskin: called as : %s\n",argv[0])); if(skin->verbosity>=Cdrskin_verbose_debuG) { #ifdef Cdrskin_new_api_tesT ClN(fprintf(stderr,"cdrskin_debug: Compiled with option -experimental\n")); #endif } if(!Cdrskin__is_aborting(0)) Cdrskin_run(skin,&exit_value,0); ex:; if(Cdrskin__is_aborting(0)) Cdrskin_abort(skin, 0); /* Never comes back */ if(preskin!=NULL) h_preskin= preskin; else if(skin!=NULL) h_preskin= skin->preskin; if(h_preskin!=NULL) { if(skin!=NULL) if(skin->verbosity>=Cdrskin_verbose_debuG) ClN(fprintf(stderr, "cdrskin_debug: demands_cdrecord_caps= %d , demands_cdrskin_caps= %d\n", h_preskin->demands_cdrecord_caps, h_preskin->demands_cdrskin_caps)); if(exit_value && h_preskin->demands_cdrecord_caps>0 && h_preskin->demands_cdrskin_caps<=0) { /* prepare fallback */ /* detach preskin from managers which would destroy it */ preskin= NULL; if(skin!=NULL) skin->preskin= NULL; } else h_preskin= NULL; /* prevent fallback */ } if(skin!=NULL) { Cleanup_set_handlers(NULL,NULL,1); if(skin->preskin!=NULL) Cdrskin_eject(skin,0); Cdrskin_destroy(&skin,0); } Cdrpreskin_destroy(&preskin,0); if(lib_initialized) burn_finish(); if(h_preskin!=NULL) Cdrpreskin_fallback(h_preskin,argc,argv,0); /* never come back */ exit(exit_value); } cdrskin homepage english
cdrskin logo: Doener mit Scharf

Homepage of

cdrskin

Limited cdrecord compatibility wrapper for libburn

Purpose:

Burns preformatted data to CD, DVD, and BD media:
CD-R, DVD-R, DVD-R DL, DVD+R, DVD+R DL, BD-R, CD-RW, DVD-RW, DVD-RAM, DVD+RW, BD-RE


Direct hop to download links ->

Hardware requirements:

About any CD, DVD, or BD recorder produced in the recent ten years.
libburn supports recorders which are compliant to standards MMC-1 for CD and MMC-5 for DVD or BD. Linux, FreeBSD, Solaris, and NetBSD can communicate with drives connected via SCSI, PATA (aka IDE, ATA), USB, or SATA.

Software requirements :

Linux with kernel 2.4 or higher (and libc, of course) :
With kernel 2.4 an ATA drive has to be under ide-scsi emulation.
With kernel 2.6 or higher the drive should not be under ide-scsi.
or FreeBSD (with libc, of course) :
ATA and SATA drives need atapicam running.
libcam has to be installed.
or Solaris (with libc, of course) :
Tested on kernel 5.11, hopefully suitable for older ones too.
or NetBSD (with libc, of course) :
Tested on 6.1.2 and 6.1.3
libpthread
is supposed to be a standard system component.

GPL software included:

libburn-1.5.6
(founded by Derek Foreman and Ben Jansens, developed and maintained since August 2006 by Thomas Schmitt from team of libburnia-project.org)
transfers data to CD, DVD, BD

This program system has been tested on Intel/AMD with Linux, FreeBSD, OpenSolaris, and NetBSD based operating systems.
Ports to other usable systems are appreciated. Reports are welcome.


Special features:

  • Source code is independent of cdrecord

Commands:

The most common options of cdrecord for data and audio on CD media are provided in a compatible way.
On all DVD media, cdrskin is able to perform any recording job which is possible with cdrecord. Other than with cdrecord, option -multi is supported with many DVD types and BD-R. Write mode -tao works with anything but quickly blanked DVD-RW and DVD-R DL, which both support no -multi.


Get an overview of drives and their addresses
# cdrskin -scanbus
# cdrskin dev=ATA -scanbus
# cdrskin --devices
Being superuser avoids permission problems with /dev/srN and /dev/hdX .
Ordinary users should then get granted access to the /dev files as listed by option --devices. Linux, FreeBSD, and NetBSD demand rw-permission. On Solaris it is r-permission and privileges "basic,sys_devices".
 
Get info about a particular drive or loaded media:
$ cdrskin dev=0,1,0 -checkdrive
$ cdrskin dev=ATA:1,0,0 -v -atip
$ cdrskin dev=/dev/hdc -minfo
Prepare CD-RW or DVD-RW for re-use, DVD-RAM or BD-RE for first use:
$ cdrskin -v dev=/dev/sg1 blank=as_needed -eject
Format DVD-RW to avoid need for blanking before re-use:
$ cdrskin -v dev=/dev/sr0 blank=format_overwrite
De-format DVD-RW to make it capable of multi-session again:
$ cdrskin -v dev=/dev/sr0 blank=deformat_sequential
Write ISO-9660 filesystem image as only one to blank or formatted media:
$ cdrskin -v dev=/dev/hdc speed=12 fs=8m \
  blank=as_needed -eject padsize=300k my_image.iso
Write compressed afio archive on-the-fly (not DVD-R DL or minimally blanked DVD-RW):
$ find . | afio -oZ - | \
  cdrskin -v dev=0,1,0 fs=32m speed=8 \
  blank=as_needed padsize=300k -
Write several sessions to the same CD, DVD-R[W] or DVD+R[/DL]:
$ cdrskin dev=/dev/hdc -v padsize=300k -multi 1.iso
$ cdrskin dev=/dev/hdc -v padsize=300k -multi 2.iso
$ cdrskin dev=/dev/hdc -v padsize=300k -multi 3.iso
$ cdrskin dev=/dev/hdc -v padsize=300k 4.iso
Get multi-session info for option -C of program mkisofs:
$ c_values=$(cdrskin dev=/dev/sr0 -msinfo 2>/dev/null)
$ mkisofs ... -C "$c_values" ...
Inquire free space on media for a -multi run:
$ x=$(cdrskin dev=/dev/sr0 -multi \
  --tell_media_space 2>/dev/null)
$ echo "Available: $x blocks of 2048 data bytes"
Accelerate BD-RE writing to full nominal speed after the first 250 MB
$ cdrskin ... stream_recording=250m ...
Write audio tracks to CD:
$ cdrskin -v dev=ATA:1,0,0 speed=48 -sao \
  track1.wav track2.au -audio -swab track3.raw
Get overview of the cdrecord compatible options:
$ cdrskin -help
Get overview of the non-cdrecord options:
$ cdrskin --help
Read the detailed manual page:
$ man cdrskin
Read about the standard for which cdrskin is striving:
$  man cdrecord
Do not bother Joerg Schilling with any cdrskin problems. (Be cursed if you install cdrskin as "cdrecord" without clearly forwarding this "don't bother Joerg" demand.)
Learn to know a more versatile way to burn ISO 9660 formatted data
Standalone ISO 9660 multi-session CD/DVD/BD tool xorriso.


Download as source code (see README):
cdrskin-1.5.6.tar.gz (1075 KB).
cdrskin-1.5.6.tar.gz.sig
(detached GPG signature for verification by gpg --verify cdrskin-1.5.6.tar.gz.sig cdrskin-1.5.6.tar.gz
after gpg --keyserver keyserver.ubuntu.com --recv-keys ABC0A854).
The cdrskin tarballs are source code identical with libburn releases of the same version number. They get produced via a different procedure, though.
cdrskin is part of libburn - full libburn is provided with cdrskin releases.
Documentation:
README an introduction
cdrskin --help non-cdrecord options
cdrskin -help cdrecord compatible options
man cdrskin the manual page
 
Contact:
Thomas Schmitt, scdbackup@gmx.net
GNU xorriso mailing list, where cdrskin and libburn are on topic, too: bug-xorriso@gnu.org
License:
GPL, an Open Source approved license
 


Enhancements towards previous stable version cdrskin-1.5.4:

  • New cdrskin option --bdr_obs_exempt
  • Officially enabled overburning on CD media
Bug fixes towards cdrskin-1.5.4:
  • Overburning with cdrskin option -force ended by a libburn error

Development snapshot, version 1.5.7 :

Enhancements towards current stable version 1.5.6:
  • none yet
Bug fixes towards cdrskin-1.5.6:
  • none yet
 
README 1.5.7
cdrskin-1.5.7 --help
cdrskin-1.5.7 -help
man cdrskin (as of 1.5.7)
 
Maintainers of cdrskin unstable packages please use git of libburnia-project.org
Download: git clone https://dev.lovelyhq.com/libburnia/libburn.git
Build: cd libburn ; ./bootstrap ; ./configure --prefix /usr ; make ; cdrskin/compile_cdrskin.sh
Build of SVN versions needs autotools of at least version 1.7 installed. But after the run of ./bootstrap, only vanilla tools like make and gcc are needed.
 
The following download is intended for adventurous end users or admins with full system sovereignty.
Source (./bootstrap is already applied, build tested, for more see upcoming README ):
cdrskin-1.5.7.tar.gz (1075 KB).


Many thanks to Joerg Schilling for cdrecord,
and to Derek Foreman and Ben Jansens for creating libburn.
Historic versions based on Derek's and Ben's icculus.org/burn :
cdrskin-0.1.2.0.2.ts.tar.gz
cdrskin-0.1.3.0.2.ts.tar.gz
Very special thanks to Andy Polyakov whose dvd+rw-tools provide libburn with invaluable examples on how to deal with DVD and BD media.


Example for a setup of device permissions.
Newer Linux distros enable rw-access for the desktop user automatically. So try as normal user whether all your drives are found. CD devices which offer no rw-permission will stay invisible.
$ cdrskin --devices
If not all desired drives show up, become superuser and do again:
# cdrskin --devices
...
0  dev='/dev/sr0'  rwr-r- :  'TEAC' 'CD-ROM CD-532S'
1  dev='/dev/hdc'  rwrw-- :  'LITE-ON' 'LTR-48125S'
Most simple and most insecure is this equivalent of the usual cdrecord permissions u+s,a+x:
# chmod a+rw /dev/sr0 /dev/hdc
More secure is to put the permitted users into a group like "floppy", to assign /dev/sr0 /dev/hdc to this group, and to allow rw-access only to group members.
# vi /etc/group
...
floppy:x:19:thomas,scdbackup
...
# chgrp floppy /dev/sr0 /dev/hdc
# chmod g+rw /dev/sr0 /dev/hdc


Example how to setup K3b to use cdrskin for burning data CD projects.
(
K3b is a GUI frontend which uses cdrecord for CD burning.)


About the relationship of cdrecord and cdrskin

First of all: this relationship is single sided, as cdrskin has to be aware of cdrecord but not vice versa.

I was a long time user of cdrecord and it worked fine for me. Especially i do appreciate its write mode -tao which can pipe arbitrary data on CD and CD-RW via stdin. cdrecord is reliable, versatile and well maintained. So for me - there would be no problem with using it for burning CDs.
But the author of cdrecord and the Linux kernel people foster a very hostile relationship. Ok, that's their business, not mine (or ours if you are with me). One has to be aware, though, that this relationship might lead to a situation where cdrecord is no longer available for certain Linux kernels.
To have my own project prepared for such a time, i began to implement its cdrecord gestures on top of libburn. From now on i invite other interested users of cdrecord to teach cdrskin the gestures necessary for their cdrecord applications. Contact me. Let's see what we can achieve.

libburn and cdrskin are now mature enough to substitute cdrecord in its major use cases of CD and DVD burning. It is possible to foist cdrskin on various software packages if it gets falsely named "cdrecord". I do not encourage this approach, but of course such a replacement opportunity is the goal of a cdrecord compatibility wrapper.

It is very important to me that this project is not perceived as hostile towards Joerg Schilling and his work. I owe him much. For cdrecord, for mkisofs, for star. Chapeau.


cdrskin logo: Doener mit Scharf

Enjoying free Open Source hosting by www.webframe.org
100 % Microsoft free
and by sourceforge.net
SourceForge Logo

Links to my other published software projects :
xorriso, a standalone ISO 9660 multi-session CD/DVD/BD burn tool. No mkisofs needed.
(a second source of above)
scdbackup, multi volume CD backup
(a second source of above)
Some Tools for Image Collectors
pppoem, a DSL throughput monitor (mainly for Linux kernel 2.4)


Legal statement: This website does not serve any commercial purpose.
#define Cdrskin_timestamP "2024.09.15.141244" ------------------------------------------------------------------------------ libburnia-project.org scdbackup.sourceforge.net/cdrskin ------------------------------------------------------------------------------ Deliberate deviations of cdrskin from cdrecord compatibility: + cdrskin does drive operations (on its drive 0) without a dev= option (Note: cdrecord meanwhile has a default for missing dev= option.) + gracetime=0 is allowed and set by default + -pad is always set for audio tracks + -sao is default where possible, -tao is default where needed (Note: cdrecord write mode defaults have changed in cdrtools-2.01.01a20) + driveropts=burnfree is default if the drive supports buffer underrun protection. If desired, use driveropts=noburnfree to disable this feature. + premature end of source is not an error and leads to full announced tracksize + -msinfo pushes all other messages to stderr. It works independently of other options which would prevent it with cdrecord (-atip, -scanbus, ...) + DVD track sources get not concateneated to a single track. In general DVD writing is quite different from cdrecord-ProDVD: DVD-R[W] "Disc-at-once" (-sao) is nearest to cdrecord-ProDVD's methods. DVD-R[W] "Incremental Streaming" (-tao) on unformatted media allows multi-session and track sources of unpredictable size. Writing DVD-RAM, DVD+RW and "Restricted Overwrite" DVD-RW is like single track -tao on blank CD. Formatting is done via cdrskin-specific blank=format_overwrite and not with option -format. + DVD-RW get blanked fast only with option blank=deformat_sequential_quickest . Option blank=fast is the same as blank=all in order to achieve media which are capable of Incremental Streaming. + It has not been evaluated how far -isosize is compatible with the original cdrecord option. man cdrecord forbids stdin as source, cdrskin allows it if a fifo is used. ------------------------------------------------------------------------------ Changelog ------------------------------------------------------------------------------ 19 Aug 2006 [committed] README cdrskin/README cdrskin/cdrskin.c cdrskin/add_ts_changes_to_libburn_0_2_1 \ cdrskin/cdrskin_timestamp.h cdrskin/compile_cdrskin.sh cdrskin-0.1.4 "stable" released on base of August 15 2006 version of libburn-svn.pykix.org/trunk after initial merge of libburn and cdrskin-0.1.3 . ------------------------------------------------------------------------------ 19 Aug 2006 [committed 16] cdrskin/changelog.txt cdrskin-0.1.5 development started. Introduced this changelog. ---------------------------------- Format (still emerging): Day Month Year [eventual commit mark with revision number] affected files , naked, one by line, [eventually = internal snapshot tarball] More ore less concise description. ---------------------------------- End of Format 20 Aug 2006 [committed together with next change, i fear] libburn/sg.c Hopefully fixed a file descriptor resource leak in sg_grab(). All scanned drives (seem to) stay open once, the grabbed one got re-opened and its stored first file descriptor got forgotten. Now we try to detect and re-use the still open fd. 21 Aug 2006 [committed] libburn/libburn.h libburn/sg.c libburn/init.c = libburn_cdrskin_A60819.tgz cdrskin/cdrskin.c [committed later as revision 11] O_EXCL experiments imported from cdrskin-0.1.3 In default configuration and on compliant kernels expect that a busy drive is invisible to further cdrskin instances. The user gets hints in case of empty bus scan result resp. busy drive given with dev=... Wether cdrecord and cdrskin respect each other would have to be evaluated. Options to play with: --demand_a_drive exit !=0 on bus scans with empty result --drive_abort_on_busy abort process if busy drive is found (might be triggered by a busy hard disk) --drive_blocking try to wait for busy drive to become free (might be stalled by a busy hard disk) --drive_not_exclusive do not ask kernel to prevent opening busy drives. Effect is kernel dependend. grab_drive_and_wait= grab drive, wait given number of seconds, release drive, and do normal work 21 Aug 2006 [committed] libburn/write.c Rectified non-ANSI-C comment, complained by gcc. 21 Aug 2006 [committed 13] cdrskin/cdrskin_eng.html The homepage moved in from scdbackup's internal doc collection. 21 Aug 2006 [committed with revision 11, i fear] cdrskin/cdrskin.c Removed flaw: Help text of tao_to_sao_tsize= clarified. 21 Aug 2006 [committed 12] cdrskin/wiki_plain.txt The initial filling of the cdrskin wiki on libburn.pykix.org . I am not sure about the future fate of this text. I plan to keep it up to date with the online wiki for now. The online version will be considered the original. 21 Aug 2006 [committed 15] cdrskin/cdrskin_timestamp.h Timestamping the new version. 21 Aug 2006 [committed 14] cdrskin/add_ts_changes_to_libburn_0_2_1 cdrskin/compile_cdrskin.sh Readjusted relationship glue of libburn and cdrskin. --------------------------------------------------------------------- cycled - 27 Aug 2006 [40] libburn/mmc.c libburn/sg.c Inserted prints to see how sg_issue_command() is called (printing is disabled now) 21 Aug 2006 [17] README Reported obvious need for automake >=1.7 21 Aug 2006 [18] cdrskin/cdrskin_eng.html cdrskin/README Reported obvious need for automake >=1.7 22 Aug 2006 [19] libburn/drive.c libburn/drive.h New internal function burn_drive_is_open() 23 Aug 2006 [20] cdrskin/cdrskin.c Implemented Lorenzo Taylor's audio patch manually by copy+paste as i wanted to fully understand it. Hopefully did not break it that way. 24 Aug 2006 [21] libburn/libburn.h libburn/drive.c Introduced API functions burn_drive_scan_and_grab() burn_drive_get_adr() 24 Aug 2006 [22] cdrskin/compile_cdrskin.sh Experimental option cdrskin/compile_cdrskin.sh -newapi 24 Aug 2006 [23] cdrskin/cdrskin.c First test of possibility to obey the self imposed rules of Revison 21 24 Aug 2006 [25] cdrskin/cdrskin.c Fixed undefined track_type introduced by revision 20. (We broke cdrskin, but i did not break the patch. Success.) 24 Aug 2006 [30] libburn/libburn.h Hopefully fixed an unintended line break in API doxygen 25 Aug 2006 [32] cdrskin/cdrskin.c Installed protection against resource leak in Cdrskin_grab_drive() Just to be sure. 25 Aug 2006 [33] libburn/drive.c burn_drive_free() now closes all open drive file descriptors 25 Aug 2006 [34] libburn/libburn.h Adjusted statement at API documention of burn_initialize() 25 Aug 2006 [35] cdrskin/cdrskin.c Worked forth in order to make cdrskin fully newapi compliant 26 Aug 2006 [37] Makefile.am libburn/back_hacks.h libburn/drive.c libburn/init.c Allowed to blank appendable files and installed first back_hacks.h variable ever 26 Aug 2006 [38] test/burniso.c Rewrote it to new API practice, inflated explanation comments, SAO mode 27 Aug 2006 [39] cdrskin/cdrskin.c Implemented Lorenzos blank-appendable patch plus option --no_blank_appendable 27 Aug 2006 [44] test/blank.c Rewrote test/blank.c to new API practice, inflated explanation comments 27 Aug 2006 [41] cdrskin/cdrskin.c Fixed obscure sigsegv introduced with 35 or 39 by obeying libburn.h text (could be a fandango starting in burn_drive_info_free) 27 Aug 2006 [45] test/burniso.c Disabled inner burn_drive_info_free like in cdrskin, polished a bit 27 Aug 2006 [43] libburn/libburn.h Changed some 'release' to 'close' with specs of burn_drive_scan_and_grab 28 Aug 2006 [46] test/burniso.c Polished a bit more for doxygen 28 Aug 2006 [50] libburn/libburn.h cdrskin/cdrskin.c Wrote into API the imperative not to use drive.location but burn_drive_get_adr 28 Aug 2006 [47] test/burniso.c Integrated functionality of test/devices.c into test/burniso.c Proposed to rename it to test/libburner.c 28 Aug 2006 [51] cdrskin/cdrskin.c Closed a pitfall with reading from '-' and no tsize= or tao_to_sao_tsize= Ticket 55 28 Aug 2006 [52] cdrskin/cdrskin.c Added personal commitment to grant BSD license on request. Insisted in GPL for now. 28 Aug 2006 [53] cdrskin/cdrskin.c Forced each track to have a minimum size of 600 kB Ticket 55 29 Aug 2006 [58] test/burniso.c Integrated functionality of test/blank.c into test/burniso.c 29 Aug 2006 [55] cdrskin/cdrskin.c Made cdrskin ready to make good use of now working libburn-eject 29 Aug 2006 [56] cdrskin/cdrskin.c Avoided unwanted tray loading on eject of never grabbed drive 29 Aug 2006 [57] cdrskin/cdrskin.c Disabled unconditionality of eject introduced by 55 or 56 30 Aug 2006 [59] test/burniso.c Repaired SIGSEGV caused by releasing ungrabbed drive after mere bus scan 30 Aug 2006 [60] test/libburner.c Makefile.am My proposal for new souvereign app as API doc and reference for API decisions 31 Aug 2006 [61] libburn/sg.c cdrskin/cdrskin.c Outcommented "experimental:" messages of O_EXCL development 31 Aug 2006 [62] test/libburner.c Added 300 kB of padding to get rid of warning in doc, plus end sector padding 31 Aug 2006 [63] cdrskin/cdrskin.c Promoted "newapi" functionality and libburn-eject from test to standard 31 Aug 2006 [64] cdrskin/cdrskin.c Made cdrskin abort if fifo filling before burn yields 0 bytes (ticket 55) 1 Sep 2006 [65] cdrskin/README cdrskin/cdrskin.c cdrskin/cdrskin_eng.html Updated cdrskin help tests and docs: -audio, obsolete eject_device= 1 Sep 2006 [66] libburn/write.c cdrskin/cdrskin.c Implemented track number patch by bonfire-app@wanadoo.fr, tickets 58 and 9 1 Sep 2006 [67] cdrskin/cdrskin.c Added clarifying URGE to ABORT texts 1 Sep 2006 [71] test/libburner.c Made "read-ahead" comment sufficiently ambigous: "buffer"|"filesystem" == "" 1 Sep 2006 [72] [73] cdrskin/cdrskin.c cdrskin/compile_cdrskin.sh Rowed back from revision 64. Now #ifdef Cdrskin_fifo_abort_on_emptY 1 Sep 2006 [74] cdrskin/cdrskin.c cdrskin/compile_cdrskin.sh Rowed forth from revision 73. Now hopefully compliant to man cdrecord. 1 Sep 2006 [78] cdrskin/cdrskin.c Made -pad behave cdrecord-ly on audio tracks (not tested acousticly) 2 Sep 2006 [85] test/libburner.c Added upcoming clarification of copyright and license aspiration 3 Sep 2006 [86] README Added upcoming clarification of copyright and license aspiration 3 Sep 2006 [87] [88] cdrskin/README cdrskin/cdrskin.c Added upcoming clarification of copyright and license aspiration 3 Sep 2006 [91] cdrskin/changelog.txt cdrskin/cdrskin_timestamp.h Opened new cdrskin-0.1.5 upload cycle. This marks a should-be-stable phase. ---------------------------------------------------- cycled - 2006.09.03.132934 3 Sep 2006 [89] doc/comments_test_ts Made a try to get doxygen portal page readable by html dl lists 4 Sep 2006 [92] cdrskin/README Made changes as reported by Lorenzo on libburn-hackers today 4 Sep 2006 [93] libburn/transport.h libburn/drive.h libburn/drive.c libburn/init.c libburn/libburn.h Integrated elmom patch proposal #3 from ticket #62 /* ts A60904 : ticket 62, contribution by elmom */ 4 Sep 2006 [94] cdrskin/README Removed reference to frontend "burn" (needs a patch to work for .mp3) 5 Sep 2006 [95] doc/comments_test_ts Made a try to get doxygen portal page readable by pre tags and truncation 5 Sep 2006 [96] test/libburner.c Rearranged definitions and header inclusions. Is safer so. 5 Sep 2006 [97] test/libburner.c Re-inserted lost tab. 6 Sep 2006 [trac] closed ticket 55: burn of empty tracks from stdin is now forbidden 6 Sep 2006 [98] libburn/libburn.h libburn/drive.c Added new parameter "force" to API-experimental burn_drive_info_forget() 6 Sep 2006 [99] doc/comments Made doc test portal the official doc portal 6 Sep 2006 [100] cdrskin/cdrfifo.c Added an initial value on proposal by Bart Vanherck 7 Sep 2006 [101] test/libburner.c Changed a macro name from Burniso_ to Libburner_ 7 Sep 2006 [102] [103] libburn/libburn.h libburn/drive.c cdrskin/cdrskin.c Implemented first use of API-experimental burn_drive_info_forget() in cdrskin signal handler 7 Sep 2006 [104] cdrskin/cdrskin.c Tried to make abort messages clearer 7 Sep 2006 [105] Makefile.am test/testburner.c Prepared test bed for burn_drive_info_forget() as regular API call 7 Sep 2006 [106] test/testburner.c Removed a remnant piece of rather unhealthy test code 7 Sep 2006 [106] test/testburner.c Corrected test reciepe 7 Sep 2006 [107] test/libburner.c Added constraint --stdin_size >= 600k, better bus scan behavior 8 Sep 2006 2006 [109] libburn/drive.c test/testburner.c cdrskin/cdrskin.c Hopefully ensured correct burn_disc_erasable() already after first grab 9 Sep 2006 2006 [112] libburn/drive.c cdrskin/cdrskin.c Hunted down the bug which let newapi-cdrskin fail with drive 1 10 Sep 2006 [113] libburn/drive.c test/libburner.c test/testburner.c cdrskin/cdrskin.c Slowed down highspeed loops waiting for drive status changes 10 Sep 2006 [114] cdrskin/compile_cdrskin.sh Aliased switch name -newapi by -experimental 10 Sep 2006 [115] test/libburner.c test/testburner.c cdrskin/cdrskin.c Re-enabled call to burn_drive_info_free() after repair by revision 93 11 Sep 2006 [116] cdrskin/compile_cdrskin.sh Changed -newapi to -experimental in help text 11 Sep 2006 [117] libburn/drive.c Removed a bug introduced with revison 93 11 Sep 2006 [118] libburn/libburn.h test/libburner.c Officialized burn_drive_info_forget() 11 Sep 2006 [119] [120] Makefile.am test/testburner.c Deleted until next occasion: testburner 12 Sep Sep 2006 [124] cdrskin/cdrskin.c Repaired regression of -eject which loaded tray again 12 Sep 2006 [126] cdrskin/cdrskin.c Replaced -experimental method of closing libburn by burn_drive_info_forget() 12 Sep 2006 [129] cdrskin/cdrskin.c Added automated padding to last audio track (ticket 41) 2006.09.13.093350 [130] cdrskin/make_timestamp.sh Prepared for new revision timestamps to mark cdrskin test versions From now on cdrskin/cdrskin_timestamp.h is to be part of any commit. 15 Sep 2006 [135] Makefile.am Replaced a few 8-blanks by tab 15 Sep 2006 [136] README Moved installation instructions in front of overview paragraph 15 Sep 2006 [137] Makefile.am cdrskin/README Made cdrskin an installable program 2006.09.15.092509 [138] cdrskin/cdrskin.c cdrskin/compile_cdrskin.sh Prepared cdrskin-build for version leap ----------------------------- cycled (last cdrskin-0.1.5 ?) - 2006.09.15.101326 2006.09.15.101326 [139] cdrskin/README cdrskin/changelog.txt New upload of scdbackup.sourceforge.net/cdrskin-0.1.5.tar.gz 2006.09.15.174748 [143] cdrskin/cdrskin.c Revoked change of 1 Sep 2006 revision 78 (full -nopad) (ticket 41) 16 Sep 2006 [144] libburn/libburn.h Made doxygen happy with parameter of burn_drive_get_adr 2006.09.16.194730 [145] 000_CAUTION_RELEASE_CANDIDATE zzz_CAUTION_RELEASE_CANDIDATE README Makefile.am cdrskin/README cdrskin/cdrskin.c cdrskin/compile_cdrskin.sh Perfomed version leap in respect to SVN 2006.09.19.124540 [160] [161] cdrskin/cdrskin.c Allowed driveropts=burnproof as alias for burnfree 2006.09.19.140716 [162] [163] cdrskin/cdrskin.c Added error message in case of failed eject 2006.09.20.134219 [175] [176] [177] cdrskin/cdrskin.c Fixed bug with dev=1,1,0 (ticket 75) 21 Sep 2006 [186] configure.ac Makefile.am Forwarded changes from 0.2.2 to 0.2.3 Sep 2006 [187] [188] [189] [190] cdrskin/cdrskin.c cdrskin/compile_cdrskin.sh cdrskin/cdrskin_eng.html cdrskin/README cdrskin/changelog.txt - cdrskin/add_ts_changes_to_libburn_0_2_1 + cdrskin/add_ts_changes_to_libburn_0_2_3 Makefile.am cdrskin/cdrskin_timestamp.h Performed development version leap to cdrskin-0.2.3 ------------------------------------ cycled - cdrskin-0.2.3 - 2006.09.21.082411 21 Sep 2006 [191] README Clarified build from SVN versus tarball 2006.09.21.173012 [192] cdrskin/cdrskin.c cdrskin/compile_cdrskin.sh Gave up compile options -tarball_0_2 , -cvs_A51208 , -libburn_0_2_1 Now standard and therefore no longer needed as macros: Cdrskin_libburn_p_sectoR Cdrskin_libburn_with_fd_sourcE Cdrskin_libburn_largefilE Cdrskin_libburn_padding_does_worK 2006.09.21.185623 [193] cdrskin/cdrskin.c cdrskin/compile_cdrskin.sh Officialized gestures of pre-0.2.3 -experimental, introduced -oldfashioned 2006.09.21.194006 [194] cdrskin/cdrskin.c Investigating failure to open drive on eject 2006.09.22.133027 [195] libburn/libburn.h libburn/sg.c libburn/sg.h libburn/drive.c cdrskin/cdrskin.c Implemented resolving of softlinks (ticket 33) 2006.09.22.170220 [196] libburn/libburn.h libburn/sg.c libburn/sg.h libburn/drive.c cdrskin/cdrskin.c Implemented new API function burn_drive_convert_fs_adr() 2006.09.22.172307 [197] cdrskin/cdrskin.c Fixed SIGSEGV of -eject on non-existent device address 22 Sep 2006 [198] libburn/sg.c libburn/sg.h libburn/drive.c Implemented finding matching /dev/sgN from /dev/srM or /dev/scdK (ioctl(): SCSI_IOCTL_GET_IDLUN) 2006.09.22.195414 [199] cdrskin/cdrskin.c Removed bug prone implementation of link resolving (now only via libburn) 2006.09.22.202631 [200] cdrskin/cdrskin.c Kept new address conversion from hopping on libburn drive numbers 2006.09.22.203939 [201] cdrskin/cdrskin.c Fixed --no_follow_links and renamed to --no_convert_fs_adr 2006.09.23.080015 [202] libburn/drive.c Restructured SCSI search, removed a potential bug with hdX 23 Sep 2006 [203] libburn/libburn.h Made burn_drive_convert_scsi_adr() a new API function 23 Sep 2006 [204] libburn/drive.c libburn/sg.c Changed outdated comments 23 Sep 2006 [205] libburn/libburn.h libburn/drive.c Introduced new API function burn_drive_obtain_scsi_adr() 2006.09.23.100001 [not in libburn-0.2.2 yet] cdrskin-0.2.2/cdrskin/cdrskin.c Backported bug fix of revision 197. Version timestamp : 2006.09.23.100001 23 Sep 2006 [206] [207] libburn/drive.c libburn/sg.c Enabled unused SCSI part of struct burn_drive. Switched persistent address to burn_drive.devname 2006.09.23.114858 [208] cdrskin/cdrskin.c Implemented --no_pseudo_scsi_adr 2006.09.23.132755 [209] cdrskin/cdrskin.c libburn/drive.c Removed a bug with SCSI address of scanned drives without such address 23 Sep 2006 [210] cdrskin/README Explained the new addressing mode 2006.09.23.182444 [211] cdrskin/README cdrskin/cdrskin.c Bus,Target,Lun for dev=ATA and dev=ATAPI, real SCSI addressing by default 2006.09.23.183608 [212] cdrskin/cdrskin.c Made small improvement with debug message 2006.09.24.171706 [214] Makefile.am libburn/libburn.h libburn/libdax_msgs.h libburn/libdax_msgs.c libburn/init.c cdrskin/compile_cdrskin.sh cdrskin/cdrskin.c Added an error message handling facility (ticket 74) 2006.09.24.180836 [215] libburn/init.c libburn/sg.c libburn/libdax_msgs.h libburn/libdax_msgs.c cdrskin/cdrskin.c Made use of new message handling facility and removed first bugs 24 Sep 2006 [216] libburn/libdax_msgs.h Recorded error_code 0x00020001 24 Sep 2006 [217] [218] Makefile.am libburn/libburn.h libburn/write.c libburn/drive.c cdrskin/compile_cdrskin.sh Obsoleted libburn/message.[ch] 25 Sep 2006 [219] libburn/read.c libburn/write.c Removed inclusion of libburn/message.h 2006.09.25.104629 [220] libburn/init.c libburn/drive.c cdrskin/cdrskin.c Converted "libburn_experimental:" messages of address conversion into "DEBUG" 2006.09.25.141035 [221] libburn/libdax_msgs.h libburn/libdax_msgs.c libburn/drive.c libburn/sg.h libburn/sg.c Implemented sg_close_drive_fd (ticket 74) 2006.09.25.144506 [222] libburn/libdax_msgs.c Achieved minimum strerror thread safety (strerror_r is burned by Unix and GNU) 2006.09.26.114552 [223] libburn/libburn.h libburn/init.c libburn/libdax_msgs.c cdrskin/cdrskin.c Made first use of queued messages and fixed several bugs with that 2006.09.26.142824 [224] libburn/sg.c libburn/libdax_msgs.h cdrskin/cdrskin.c Made changes with usage of queued messages 24 Sep 2006 [225] Makefile.am libburn/libburn.h libburn/write.c libburn/drive.c - libburn/message.c - libburn/message.h Removed libburn/message.[ch] 2006.09.26.205504 [227] libburn/drive.c Enhanced softlink resolution 2006.09.26.210711 [228] libburn/drive.c Fixed bug in enhanced softlink resolution 2006.09.27.063147 [229] cdrskin/cdrskin.c Fixed bug with relative device addresses and Cdrpreskin__cdrecord_to_dev() 2006.09.27.074910 [230] cdrskin/cdrskin.c Fixed broken -version and --help (second time for same mistake) 2006.09.27.080826 [231] cdrskin/README cdrskin/cdrskin.c Allowed comments and empty lines in startup files 2006.09.27.082057 [232] cdrskin/cdrskin.c Prevented reading of startup files with first arg -version, -help or --help 2006.09.27.115919 [233] libburn/drive.c libburn/sg.h libburn/sg.c libburn/libdax_msgs.h cdrskin/cdrskin.c Disabled but did not discarded failed attempt to lock against growisofs 2006.09.27.120656 [234] libburn/drive.h libburn/init.c libburn/transport.h libburn/libburn.h Disabled but did not discarded failed attempt to lock against growisofs 2006.09.27.134332 [235] libburn/sg.c Kept /dev/hdX from all having SCSI address 0,0,0 2006.09.27.142312 [236] libburn/drive.c Curbed endless links to 20 hops 2006.09.27.143843 [237] libburn/sg.c Removed obsolete code and comments 2006.09.28.074434 [238] cdrskin/cdrskin.c Enabled optional growisofs lock attempt via --drive_scsi_exclusive 28 Sep 2006 [239] libburn/libburn.h Made official exclusive==2 with burn_preset_device_open() ------------------------------------ cycled - cdrskin-0.2.3 - 2006.09.28.100934 2006.09.28.115152 [240] cdrskin/cdrskin.c Restored vanished line at end of -help text and added a new one 2006.10.01.104140 [243] libburn/drive.c cdrskin/cdrskin.c Enhanced Cdrpreskin__cdrecord_to_dev so it warns of invisible SCSI drive. 2006.10.02.103418 [244] libburn/libburn.h libburn/drive.h libburn/drive.c libburn/libdax_msgs.h libburn/libdax_msgs.c cdrskin/cdrskin.c Implemented burn_abort() and made use of it 2006.10.03.162719 [245] Makefile.am libburn/libburn.h libburn/init.c libburn/cleanup.h libburn/cleanup.c cdrskin/cleanup.h cdrskin/cleanup.c test/libburner.c cdrskin/compile_cdrskin.sh cdrskin/cdrskin.c Implemented new API function burn_set_signal_handling(), libburner uses it 5 Oct 2006 [246] libburn/drive.c Uploaded forgotten part of revision 245 2006.10.05.142628 [247] libburn/libburn.h libburn/transport.h libburn/sg.c libburn/sg.h libburn/drive.c cdrskin/cdrskin.c Made use of SCSI_IOCTL_GET_BUS_NUMBER in hope of cdrecord compatibility 6 Oct 2006 [248] + libburn/asserts.txt Listed findings on assert() within libburn 7 Oct 2006 [249] libburn/async.c libburn/libdax_msgs.h libburn/asserts.txt Got rid by soft means of assert() in async.c 2006.10.07.121234 [250] libburn/libburn.h libburn/async.c libburn/drive.h libburn/drive.c libburn/init.c libburn/libdax_msgs.h libburn/sg.c libburn/asserts.txt Got rid of assert() in drive.c by soft means 2006.10.07.132916 [251] libburn/init.c libburn/async.c Got rid of assert() in init.c by soft means 2006.10.07.142454 [252] libburn/libburn.h libburn/async.c libburn/options.c libburn/sector.c libburn/spc.c libburn/libdax_msgs.h Got rid of assert() in options.c by soft means 7 Oct 2006 [253] libburn/read.c Got rid of assert() in read.c by soft means 2006.10.07.170913 [254] libburn/sg.c libburn/asserts.txt Got rid of some assert() in sg.c by soft means 2006.10.07.175427 [255] libburn/spc.c libburn/async.c libburn/libdax_msgs.h Got rid of assert() in spc.c by soft means 2006.10.08.095016 [256] libburn/structure.c libburn/sector.c libburn/spc.c libburn/libdax_msgs.h libburn/asserts.txt Got rid of assert() in structure.c by soft means 8 Oct 2006 [257] libburn/toc.c Got rid of assert() in toc.c by soft means 8 Oct 2006 [258] libburn/util.c Got rid of assert() in util.c by soft means 2006.10.09.083438 [259] libburn/write.c libburn/structure.c libburn/libdax_msgs.h libburn/asserts.txt Got rid of assert() in write.c by soft means 2006.10.09.123518 [260] libburn/mmc.c libburn/read.c libburn/sector.c libburn/mmc.h libburn/transport.h libburn/asserts.txt Got rid of assert() in mmc.c by soft means 2006.10.10.112545 [261] libburn/sector.h libburn/sector.c libburn/async.c libburn/write.h libburn/write.c libburn/libdax_msgs.h libburn/asserts.txt Got rid of assert() in sector.c by soft means 2006.10.10.175444 [262] libburn/sg.c libburn/libdax_msgs.h Got rid of assert() in sg.c by soft means 10 Oct 2006 [263] libburn/asserts.txt Got rid of assert() in libburn 2006.10.11.191959 [264] cdrskin/cdrskin.c cdrskin/README Changed pseudo-cdrecord addresses: /dev/hdX = ATA:(X-'a')/2,(X-'a')%2,0 13 Oct 2006 [270] Makefile.am libburn/sg.c libburn/cleanup.c libburn/sg-linux.c libburn/read.c libburn/drive.c libburn/sbc.c libburn/transport.h cdrskin/cleanup.c Made libburn and cdrskin build on my Linux again 2006.10.13.114554 [271] cdrskin/cdrskin.c libburn/write.c libburn/sg-linux.c libburn/libdax_msgs.h Removed bug in burn_disc_write_sync(): BURN_DRIVE_IDLE, then d->sync_cache() 2006.10.14.105224 [272] libburn/sg.h libburn/sg-freebsd.c libburn/sg-linux.c libburn/drive.c Introduced burn_drive_enumerator_t to allow more complete sg-freebsd implementation 2006.10.15.133035 [274] cdrskin/cdrskin.c Changed ambigous include statement of libburn.h 15 Oct 2006 2006 [275] Makefile.am libburn/libdax_msgs.h + libburn/libdax_audioxtr.h + libburn/libdax_audioxtr.c Implemented a first attempt of a .wav decapitator (ticket 38) 15 Oct 2006 2006 [276] + test/dewav.c Implemented a first attempt of a .wav decapitator (ticket 38) 15 Oct 2006 2006 [277] test/dewav.c Fixed permissions of eventually created output file 15 Oct 2006 2006 [278] test/dewav.c cdrskin/compile_cdrskin.sh Hunting a malloc/free memory problem 15 Oct 2006 2006 [279] libburn/libdax_audioxtr.c Hopefully fixed memory problem which causes sigabrt on free 15 Oct 2006 2006 [280] libburn/libdax_audioxtr.c Hopefully fixed problem with stdin as audio source 15 Oct 2006 2006 [281] test/dewav.c Made helptext print to stderr rather than stdout 2006.10.17.141053 [283] cdrskin/cdrskin.c cdrskin/compile_cdrskin.sh libburn/libdax_audioxtr.h libburn/libdax_audioxtr.c test/dewav.c Roughly implemented automatic .wav extraction in cdrskin 2006.10.17.164140 [284] cdrskin/cdrskin.c libburn/libdax_audioxtr.h libburn/libdax_audioxtr.c test/dewav.c Implemented some cdrecord pickiness for .wav extraction 18 Oct 2006 [285] test/libburner.c Added --audio and multi-track, removed --verbose, hid --burn_for_real 2006.10.18.174334 [286] cdrskin/cdrskin.c Removed assumption BURN_DRIVE_IDLE==0 18 Oct 2006 [287] cdrskin/README Changed audio statements to reflect new situation 18 Oct 2006 [288] cdrskin/README Removed a typo 19 Oct 2006 [292] test/libburner.c Freed all tracks after burning and not only last one 2006.10.19.094543 [293] cdrskin/cdrskin.c cdrskin/compile_cdrskin.sh Made cdrskin use libburn/cleanup.[oh] by default (not cdrskin/cleanup.[ch]) ------------------------------------ cycled - cdrskin-0.2.3 - 2006.10.19.105730 2006.10.19.162254 [294] cdrskin/cdrskin.c cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated documentation about cdrskin-0.2.3 2006.10.19.164742 [295] cdrskin/cdrskin.c Corrected -help text 2006.10.20.113421 [297] libburn/libburn.h libburn/mmc.c libburn/sg-linux.c libburn/sg-freebsd.c libburn/drive.c cdrskin/cdrskin.c Made cdrskin produce "ATIP start of lead" (on non-blank media for now) 20 Oct 2006 [298] doc/comments Updated help text of libburner 2006.10.20.151602 [299] libburn/libburn.h libburn/drive.c libburn/mmc.c libburn/libdax_msgs.h cdrskin/cdrskin.c Classified media with TOC read error as unsuitable (rather than as blank) 2006.10.21.103352 [300] libburn/libburn.h libburn/transport.h libburn/drive.c libburn/mmc.c libburn/spc.c libburn/sg-linux.c libburn/sg-freebsd.c libburn/libdax_msgs.h cdrskin/cdrskin.c Implemented some ATIP functionality 2006.10.21.103653 [301] [302] libburn/libburn.h Clarified relation of burn_disc_read_atip() and burn_drive_get_start_end_lba() 2006.10.21.185102 [303] libburn/sg.h libburn/mmc.h libburn/mmc.c libburn/sbc.h libburn/sbc.c libburn/spc.h libburn/spc.c libburn/drive.h libburn/drive.c libburn/transport.h libburn/sg-linux.c libburn/sg-freebsd.c Split enumerate_common() into logic-layer, command-layer, transport-layer 2006.10.22.130341 [304] libburn/libburn.h libburn/mmc.c libburn/drive.c libburn/cleanup.c cdrskin/cleanup.c cdrskin/cdrskin.c Implemented cdrskin -toc 2006.10.22.131452 [305] cdrskin/cdrskin.c Regulated coexistence of options -toc and -atip 23 Oct 2006 [306] libburn/mmc.c Made clarification in remark about atip speed conversion 2006.10.23.074430 [307] cdrskin/cdrskin.c Made use of burn_drive_info.revision as firmware revision text 2006.10.23.113116 [308] libburn/libburn.h libburn/transport.h libburn/mmc.h libburn/mmc.c libburn/write.c cdrskin/cdrskin.c cdrskin/cdrskin_eng.html cdrskin/README Made available drive buffer fill during write 23 Oct 2006 [309] libburn/sg-freebsd.c Updated tangling of FreeBSD code with mmc.c :( 2006.10.23.134719 [310] cdrskin/cdrskin.c Corrected -toc track counter and notified about "hidden" tracks 2006.10.24.075039 [311] libburn/libdax_audioxtr.h libburn/libdax_audioxtr.c test/dewav.c cdrskin/cdrskin.c Introduced extraction of .au (but not its usage within cdrskin) 2006.10.24.102107 [312] libburn/libburn.h libburn/structure.h libburn/structure.c libburn/sector.c cdrskin/cdrskin.c cdrskin/README Enabled byte swapping for audio track sources, added anti option -swab 2006.10.24.130259 [313] test/dewav.c cdrskin/cdrskin.c cdrskin/README Enabled automatic extraction of .au 24 Oct 2006 [314] Makefile.am + test/fake_au.c Introduced temporary test program to produce SUN .au files ------------------------------------ cycled - cdrskin-0.2.3 - 2006.10.24.144650 2006.10.24.165427 [315] libburn/sector.c Closed some loopholes for byte swapping. 2006.10.24.165610 [316] cdrskin/cdrskin.c Enabled audio byte swapping by default (to be disabled via option -swab) ------------------------------------ cycled - cdrskin-0.2.3 - 2006.10.24.173602 25 Oct 2006 [317] cdrskin/README cdrskin/cdrskin_eng.html cdrskin/wiki_plain.txt Announced full -audio compatibility with cdrecord ------------------------------------ cycled - cdrskin-0.2.3 - 2006.10.25.160540 2006.10.27.114326 [319 branch] - cdrskin/add_ts_changes_to_libburn_0_2_3 + cdrskin/add_ts_changes_to_libburn_0_2_4 cdrskin/README cdrskin/cdrskin.c cdrskin/cdrskin_eng.html cdrskin/changelog.txt README Performed cdrskin version leap to cdrskin-0.2.4 2006.10.28.093922 [320 branch] cdrskin/cdrskin_timestamp.h Set final timestamp 2006.10.28.093922 ------------------------------------ cycled - cdrskin-0.2.4 - 2006.10.28.093922 2006.10.28.115213 [321 trunk] [322 trunk] - cdrskin/add_ts_changes_to_libburn_0_2_3 + cdrskin/add_ts_changes_to_libburn_0_2_4 + cdrskin/add_ts_changes_to_libburn_0_2_5 cdrskin/README cdrskin/cdrskin.c cdrskin/cdrskin_eng.html cdrskin/changelog.txt README Performed cdrskin version leap to cdrskin-0.2.5 28 Oct 2006 [323 branch] - cdrskin/add_ts_changes_to_libburn_0_2_3 cdrskin/add_ts_changes_to_libburn_0_2_4 Corrected misnaming of my development directory 28 Oct 2006 [324 trunk] cdrskin/add_ts_changes_to_libburn_0_2_4 cdrskin/add_ts_changes_to_libburn_0_2_5 Corrected misnaming of my development directory 2006.10.28.132532 [325 branch] cdrskin/cdrskin.c Corrected last-minute bug which made every track from file an audio track 2006.10.28.151521 [326 trunk] cdrskin/cdrskin.c Corrected bug which made every track from file an audio track 29 Oct 2006 [327 branch] [328 trunk] + CONTRIBUTORS Copied missing file from libburn-0.2.2 29 Oct 2006 [329 branch] [330 trunk] Makefile.am Added to EXTRA_DIST cdrskin/cleanup.[ch] 29 Oct 2006 [331 branch] [332 trunk] Makefile.am Added to EXTRA_DIST libburn/sg-*.[ch] 29 Oct 2006 [333 branch] [334 trunk] Makefile.am Deleted from to EXTRA_DIST libburn/sg-*.h 30 Oct 2006 [337] libburn/transport.h libburn/mmc.c libburn/sg-freebsd.c Made MMC command CLOSE TRACK/SESSION available to struct burn_drive 2006.10.31.115606 [338] libburn/transport.h libburn/spc.c libburn/mmc.c libburn/write.h libburn/write.c libburn/sector.c libburn/libdax_msgs.h libburn/sg.h libburn/sg-linux.c cdrskin/cdrskin.c Made single track TAO work (sector i/o still wants fixed size, though) 2006.10.31.184736 [339] libburn/sector.c libburn/source.c libburn/structure.h libburn/structure.c libburn/write.c cdrskin/cdrskin.c Made single track TAO work without fixed size (compile -experimental) 2006.11.01.163934 [340] libburn/libburn.h libburn/source.c libburn/write.h libburn/write.c libburn/structure.c libburn/structure.h libburn/sector.c cdrskin/cdrskin.c cdrskin/compile_cdrskin.sh Adapted cdrskin pacifier to possibly unknown track size 2006.11.01.172004 [341] configure.ac bootstrap test/libburner.c Repaired broken macro settings during Linux build 2 Nov 2006 [342] cdrskin/README Mentioned -tao and experimental compile 2006.11.02.211816 [343] libburn/libburn.h libburn/write.c cdrskin/cdrskin.c Installed status communications about closing session ("Fixating") 3 Nov 2006 [344] test/libburner.c Changed status report during blanking (there are no "sectors") 2006.11.03.063307 [345] cdrskin/cdrskin.c Removed some obsolete debugging messages 2006.11.03.151137 [346] libburn/structure.h libburn/structure.c libburn/write.h libburn/write.c libburn/sector.c libburn/libdax_msgs.h Installed decent abort behavior with TAO 2006.11.03.202403 [347] libburn/write.c Enabled TAO for multiple -data tracks (-audio still ends after 0 bytes) 2006.11.04.092909 [348] libburn/spc.c libburn/sector.c Enabled audio tracks with TAO 2006.11.02.140329 (pl01) [351 tags/CdrskinZeroTwoFourPlZeroOne] ../cdrskin-0.2.4.patch01/configure.ac ../cdrskin-0.2.4.patch01/bootstrap ../cdrskin-0.2.4.patch01/cdrskin/README ../cdrskin-0.2.4.patch01/cdrskin/add_ts_changes_to_libburn_0_2_4_patch01 ../cdrskin-0.2.4.patch01/cdrskin/cdrskin_timestamp.h ../cdrskin-0.2.4.patch01/test/libburner.c Revoked autotools aspect of revision 290 2006.11.06.073810 [352] cdrskin/cdrskin.c cdrskin/cdrskin_eng.html cdrskin/changelog.txt Adapted documentation to reflect experimental TAO mode ------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.06.085056 2006.11.06.114159 [353] libburn/libburn.h libburn/spc.c libburn/options.c cdrskin/cdrskin.c New API burn_write_opts_set_multi(). (But libburn cannot burn next session yet) 2006.11.06.121409 [354] cdrskin/cdrskin.c Made -toc on multiple sessions more compatible 2006.11.06.155237 [355] libburn/libburn.h libburn/drive.c libburn/write.c libburn/mmc.c libburn/sg-linux.c cdrskin/cdrskin.c Made CD with unclosed track blankable (by -force) 2006.11.06.195743 [356] libburn/transport.h libburn/mmc.c libburn/spc.c libburn/write.c libburn/sg-linux.c Cared for some SCSI error conditions which were ignored up to now 2006.11.07.114514 [357] cdrskin/cdrskin.c Made -tao default for single track or stdin, -sao for any other multi-track 7 Nov 2006 [358] cdrskin/cdrskin_eng.html cdrskin/changelog.txt Prepared next cdrskin-0.2.5 upload cycle 7 Nov 2006 [359] cdrskin/changelog.txt cdrskin/README cdrskin/wiki_plain.txt Updated documentation about TAO 2006.11.07.152018 [360] cdrskin/cdrskin.c cdrskin/changelog.txt Updated documentation about TAO ------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.07.152018 * Enabled TAO 2006.11.08.165648 [361] cdrskin/cdrfifo.h cdrskin/cdrfifo.c cdrskin/cdrskin.c Avoided error message and nonzero exit with trailing trash on .wav 8 Nov 2006 [362] cdrskin/cdrskin_eng.html Mentioned bug fix about trailing trash 2006.11.08.172918 [363] libburn/write.c Made track write counter of SAO count rather too much than too few bytes 8 Nov 2006 [364] cdrskin/changelog.txt Prepared next cdrskin-0.2.5 upload cycle ------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.08.181016 * Bug fixed: Trailing trash appended to .wav files caused error message and, if exceeding fifo size, could even stall a burn. 2006.11.09.112832 [365] cdrskin/cdrskin.c Check desired write and block type with burn_drive_info.*_block_types 2006.11.09.113729 [366] libburn/sg-linux.c Silenced SCSI error (debugging) messages about missing media 2006.11.09.151431 [367] cdrskin/cdrskin.c Corrected first speed measurement report in TAO mode (which was random) 2006.11.09.193030 [368] libburn/libburn.h libburn/write.c cdrskin/cdrskin.c Experimentally enabled burning to BURN_DISC_APPENDABLE (tested with TAO only) 2006.11.10.093843 [369] cdrskin/cdrskin.c doc/comments Expressing more self-confidence 2006.11.10.172212 [370] cdrskin/cdrskin.c Provisory -msinfo (very verbous on stderr) 2006.11.10.184047 [371] cdrskin/cdrskin.c Made it work with fifos and other non-plain files as track sources 2006.11.10.185209 [372] cdrskin/cdrskin.c Read -msinfo from first track of last session and not from last track 2006.11.11.122402 [373] libburn/libburn.h libburn/transport.h libburn/mmc.h libburn/mmc.c libburn/write.c libburn/drive.c libburn/libdax_msgs.h New API function burn_disc_track_lba_nwa() 2006.11.11.122907 [374] [375] cdrskin/cdrskin.c Implemented not so provisory -msinfo 2006.11.11.124020 [376] [377] cdrskin/cdrskin.c Reacted on some warnings of gcc -O2 2006.11.11.134752 [378] cdrskin/cdrskin.c Implemented handling of unsuitable disk states with -msinfo 2006.11.11.152037 [379] cdrskin/cdrskin.c Demanded (for now) -tao for writing to appendable CDs 2006.11.11.152748 [380] cdrskin/cdrskin.c Generally enabled -multi, -msinfo and writing to appendable CDs in TAO mode 11 Nov 2006 [381] cdrskin/changelog.txt Prepared next cdrskin-0.2.5 cycle 11 Nov 2006 [382] cdrskin/cdrskin_eng.html cdrskin/README Updated docs about multi-session ------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.11.163625 * Multi-session CDs (for now restricted to write mode TAO), -multi, -msinfo * Bug fixed: False speed with first pacifier cycle. Potential SIGFPE by NaN. 2006.11.12.085808 [383] libburn/mmc.c Made speed 0 in burn_drive_set_speed() really maximum speed (i.e. FFFFh) 2006.11.12.113629 [384] cdrskin/cdrskin.c Made SAO preferrable default write mode, kept TAO as default where needed 2006.11.12.152723 [385] libburn/mmc.c libburn/libdax_msgs.h Reacted on error condition during write operation 2006.11.12.185342 [386] cdrskin/cdrskin.c Made -toc on blank CD exit with value 0 (rather than 7 with no media) 13 Nov 2006 [390] README cdrskin/add_ts_changes_to_libburn_0_2_5 cdrskin/cdrskin_eng.html Changed SVN URLs to reflect new structure 13 Nov 2006 [391] README Refered to both libburn and libisofs in SVN description, updated history ------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.13.122418 * Enabled named pipes and other non-plain file types as track sources (actually already in previous cycle) * Bug fixed: Default speed was not highest possible but announced as "MAX" * SAO is preferred default write mode, TAO is default when needed 2006.11.13.122418 [392] cdrskin/changelog.txt Next cdrskin-0.2.5 cycle 13 Nov 2006 [393] cdrskin/cdrskin_eng.html Adjusted description of write mode default 13 Nov 2006 [394] Makefile.am configure.ac Removed references to libisofs 14 Nov 2006 [399] README Changed references to libisofs 14 Nov 2006 [400] cdrskin/README Corrected a typo reported by George Danchev 2006.11.14.104023 [401] cdrskin/cdrskin.c Implemented try to find on restricted drives a suitable write mode as default 14 Nov 2006 [402] libburn/libburn.h Fixed a wrong name in API description 15 Nov 2006 [403] libburn/write.h libburn/write.c Prepared tests for eventual drive which might support SAO to appendable CD 15 Nov 2006 [404] libburn/sg-linux.c Enhanced optional SCSI command logging 2006.11.15.091329 [405] cdrskin/cdrskin.c Adjusted some texts to new multi-session situation 2006.11.15.170927 [406] libburn/sg.h libburn/sg-linux.c libburn/sg-freebsd.c + libburn/sg-freebsd-port.c libburn/spc.h libburn/spc.c libburn/drive.c Made portability clarifications 15 Nov 2006 [407] libburn/sg-linux.c libburn/sg-freebsd-port.c Narrowed system specific part of enumerate_common() 2006.11.16.111656 [408] [409] Makefile.am + libburn/os.h + libburn/os-linux.h + libburn/os-freebsd.h libburn/sg.h libburn/sg.c libburn/transport.h libburn/cleanup.c libburn/sg-linux.c libburn/sg-freebsd-port.c cdrskin/cleanup.c cdrskin/compile_cdrskin.sh Made consolidaed operating system adapters for ease of porting 2006.11.16.133951 [410] libburn/sg-linux.c libburn/sg-freebsd-port.c Polished porting hints and self-compliance to newly established specs 16 Nov 2006 [411] libburn/sg-linux.c libburn/sg-freebsd-port.c Polished porting hints 16 Nov 2006 [412] test/libburner.c Obsoleted --stdin_size by automatic TAO, cared for non-plain track files 16 Nov 2006 [413] doc/comments Updated help text of libburner 17 Nov 2006 [414] Makefile.am + test/telltoc.c Created info retrieval companion for libburner 17 Nov 2006 [415] test/libburner.c Enabled multi-session with libburner 17 Nov 2006 [416] Makefile.am - test/toc.c Obsoleted old test program 17 Nov 2006 [417] [418] test/telltoc.c Shifted a comma 2006.11.18.194606 [419] libburn/transport.h libburn/sbc.h libburn/sbc.c libburn/drive.c Test wether SCSI 1Bh START UNIT would be helpful with ticket 90 2006.11.19.114413 [420] libburn/libburn.h libburn/mmc.c libburn/sector.c libburn/write.c cdrskin/cdrskin.c Implemented libburn builtin fine granulated drive buffer min-fill recording 2006.11.19.162900 [421] cdrskin/cdrskin.c Avoided self contradiction with "Min drive buffer fill" 2006.11.19.163646 [422] cdrskin/cdrskin.c Fixed missing brackets from revison 421 2006.11.20.090207 [423] libburn/sector.c Silenced compiler warnings 2006.11.20.090503 [424] libburn/drive.c Removed a redundant d->start_unit() of revision 419 ------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.20.092808 * libisofs unbundled from libburn+cdrskin * Hints for porting to other operating systems are now in sg-*.c 20 Nov 2006 [425] cdrskin/changelog.txt Next cdrskin-0.2.5 cycle 2006.11.20.132717 [426] cdrskin/cdrskin.c cdrskin/cdrfifo.h cdrskin/cdrfifo.c cdrskin/wiki_plain.txt Implemented new option fifo_start_at= 20 Nov 2006 [427] cdrskin/wiki_plain.txt Updated in respect to multi-session 2006.11.22.122228 [428] libburn/spc.h libburn/spc.c Coordinated scsi_notify_error() and scsi_error() ------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.22.142517 23 Nov 2006 [433] cdrskin/changelog.txt Last cdrskin-0.2.5 cycle 23 Nov 2006 [434] - test/burn.c - test/master.c - test/tree.py - test/tree.pyc - test/rip.c Removed obsolete test programs 2006.11.23.102340 [435] Makefile.am cdrskin/compile_cdrskin.sh cdrskin/cdrskin.c cdrskin/cdrskin_eng.html cdrskin/README cdrskin/wiki_plain.txt + cdrskin/add_ts_changes_to_libburn_0_2_6 + cdrskin/add_ts_changes_to_libburn_0_2_7 - cdrskin/add_ts_changes_to_libburn_0_2_4 - cdrskin/add_ts_changes_to_libburn_0_2_5 Version leap to 0.2.6 24 Nov 2006 Published cdrskin-0.2.6.pl01 on cdrskin home pages ------------------------------- cycle - cdrskin-0.2.6.pl01 - 2006.11.23.114611 * Enabled TAO * Bug fixed: Trailing trash appended to .wav files caused error message and, if exceeding fifo size, could even stall a burn. * Multi-session CDs (for now restricted to write mode TAO), -multi, -msinfo * Bug fixed: False speed with first pacifier cycle. Potential SIGFPE by NaN. * Enabled named pipes and other non-plain file types as track sources (actually already in previous cycle) * Bug fixed: Default speed was not highest possible but announced as "MAX" * SAO is preferred default write mode, TAO is default when needed * libisofs unbundled from libburn+cdrskin * Hints for porting to other operating systems are now in sg-*.c 2006.11.24.121745 [437] Makefile.am configure.ac cdrskin/compile_cdrskin.sh cdrskin/cdrskin.c cdrskin/README Version leap to 0.2.7 24 Nov 2006 [438] doc/comments Mentioned telltoc and dewav as helpers of libburner ------------------------------------ cycle - cdrskin-0.2.7 - 2006.11.24.125445 24 Nov 2006 [439] cdrskin/changelog.txt First cdrskin-0.2.7 cycle 24 Nov 2006 [440] doc/comments Updated libburner helptext copy 24 Nov 2006 [441] doc/comments Made our claim of burning CD more general 2006.11.25.104047 [442] cdrskin/cdrskin.c Disabled old workaround for ticket 8, burn_disc_read_atip() fixed the problem 25 Nov 2006 [443] libburn/drive.h libburn/drive.c libburn/write.c test/libburner.c Ticket 91: update media state model after content change 25 Nov 2006 [444] test/libburner.c test/telltoc.c Made use of libburn device address translation (/dev/sr0, /dev/cdrom, ...) 25 Nov 2006 [445] test/libburner.c Corrected two minor bugs 2006.11.25.170456 [446] libburn/cleanup.c cdrskin/cleanup.c Trying to keep signal handler from repeating messages 2006.11.25.182841 [447] libburn/write.c libburn/drive.c Prevented premature BURN_DRIVE_IDLE introduced with revision 443 2006.11.25.210321 [448] cdrskin/cdrskin.c Enabled options -vv, -vvv and -vvvv 28 Nov 2006 [449] README Mentioned renaming of umbrella project to libburnia 2006.11.29.205136 [450] cdrskin/cdrskin.c Added preliminary support for new cdrecord 1000+ = ATA busses (input only) 2006.12.01.213845 [451] libburn/transport.h libburn/libdax_msgs.h libburn/mmc.c Ticket 21: media type via 46h GET CONFIGURATION , Current Profile 2006.12.02.111701 [452] libburn/libburn.h libburn/mmc.c libburn/drive.c cdrskin/cdrskin.c New API function to obtain media type: burn_disc_get_profile() 2006.12.02.130631 [453] libburn/mmc.c Correction for drives which return empty tray as profile 0x00 rather than error ------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.02.151257 * Improved recognition of unsuitable media types 2 Dec 2006 [454] cdrskin/changelog.txt cdrskin/cdrskin_eng.html Next cdrskin-0.2.7 cycle 2006.12.02.201405 [456] libburn/drive.c libburn/mmc.c Avoided unsuitable media complaint on burn_drive_grab() with load==0 2006.12.02.201529 [457] cdrskin/cdrskin.c Testing wether the after-grab status waiting loops are necessary 3 Dec 2006 [461] cdrskin/doener_150x200_tr.gif cdrskin/doener_150x200_tr_octx.gif cdrskin/cdrskin_eng.html cdrskin is declared honoray Doenerware (burp) 3 Dec 2006 [463] cdrskin/cdrskin_eng.html Have wikipedia explain doenerism ("Eat the rich" and so) 2006.12.03.155703 [464] README doc/comments test/libburner.c cdrskin/cdrskin_eng.html cdrskin/README cdrskin/cdrskin.c Changed URLs and umbrella names to libburnia 3 Dec 2006 [465] cdrskin/wiki_plain.txt Changed URLs and umbrella names to libburnia 3 Dec 2006 [466] cdrskin/wiki_plain.txt Added Doener logo and link 3 Dec 2006 [468] cdrskin/cdrskin_eng.html cdrskin/add_ts_changes_to_libburn_0_2_7 Excluded doener gifs from cdrskin tarballs ------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.03.204709 2006.12.03.204709 [469] cdrskin/changelog.txt Next cdrskin-0.2.7 cycle 4 Dec 2006 [470] cdrskin/cdrskin_eng.html Removed a newline which made the Mozilla family show a "_" 4 Dec 2006 [471] test/telltoc.c Added reporting of current media profile, changed "Media type" to "Media reuse" 2006.12.09.111108 [475] cdrskin/cdrskin.c Replaced setuid blocker by warning. People must know themselves what they do. ------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.09.141837 * Replaced ban of chmod u+s by loud warning 11 Dec 2006 [484] cdrskin/cdrskin_eng.html cdrskin/changelog.txt Next cdrskin-0.2.7 cycle 2006.12.11.095230 [485] cdrskin/cdrskin_timestamp.h Belated timestamp for changes in cdrskin 11 Dec 2006 [486] Makefile.am Unified mix of tab an blanks which looks ugly in diff 2006.12.11.100021 [487] libburn/sg-linux.c Prepared experiments for new Linux SCSI adventures 2006.12.11.101350 [488] cdrskin/README cdrskin/cdrskin.c cdrskin/wiki_plain.txt Consequences from newly introduces startup file 11 Dec 2006 [489] cdrskin/wiki_plain.txt Repaired README link and planted helptext links 2006.12.11.115802 [490] libburn/sg-linux.c Silenced a compiler warning. Worked further on /dev/srM test. Not done yet. 2006.12.11.125222 [491] libburn/mmc.c libburn/structure.c Prevented SIGSEGVs when using -atip with my SCSI CD-ROM (sr,sg: no matter) 2006.12.11.134452 [492] libburn/sg-linux.c Enabled correct SCSI address parameter registration for /dev/srM. 2006.12.11.145332 [493] libburn/sg-linux.c Removed ban on linux_sg_device_family, warning now of linux_sg_accept_any_type 2006.12.11.161952 [494] libburn/sg-linux.c Reacted better on failing ioctl(SG_GET_SCSI_ID) 2006.12.11.191826 [495] libburn/sg-linux.c cdrskin/cdrskin.c Trying to identfy CD device via ioctl(CDROM_DRIVE_STATUS) 2006.12.11.215017 [496] libburn/libburn.h Appeased doxygen warnings 2006.12.13.170319 [497] cdrskin/cdrskin.c Updated dev=help to versions >= 0.2.4, new option --list_ignored_options 13 Dec 2006 [498] + cdrskin/cdrskin.1 Detailed man page for cdrskin. Based on work of George Danchev. 2006.12.13.195441 [499] Makefile.am Trying to get new man page into release tarball and installation. 13 Dec 2006 [500] cdrskin/README Took care for man page 13 Dec 2006 [501] cdrskin/cdrskin.1 Clarified track content meaning. Corrected some typos and beauty flaws 13 Dec 2006 [502] cdrskin/README cdrskin/cdrskin_eng.html Took more care for man page ------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.13.221921 * detailed man page for cdrskin 13 Dec 2006 [503] cdrskin/changelog.txt Next cdrskin-0.2.7 cycle 13 Dec 2006 [504] cdrskin/cdrskin.1 Explained recordable CD media. Removed a typo. 14 Dec 2006 [505] cdrskin/cdrskin.1 Unified some nomenclature. Removed an error with dev_translation= 14 Dec 2006 [506] CONTRIBUTORS Mentioned George Danchev for his merits about cdrskin man page 2006.12.14.102857 [507] Makefile.am cdrskin/cdrskin_eng.html + cdrskin/convert_man_to_html.sh cdrskin/add_ts_changes_to_libburn_0_2_7 Publishing cdrskin.1 as man_1_cdrskin.html 14 Dec 2006 [508] - doc/comments_test_ts Removed outdated experiment ------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.14.111807 2006.12.14.140001 + cdrskin-0.2.6/cdrskin/cdrskin.1 cdrskin-0.2.6/cdrskin/README cdrskin-0.2.6/cdrskin/cdrskin_eng.html cdrskin-0.2.6/cdrskin/changelog.txt Backported manpage to cdrskin-0.2.6 -> pl02 and libburn-0.2.6 -> 0.2.6.2 14 Dec 2006 [509] cdrskin/cdrskin_eng.html Re-arranged examples of documentation commands 14 Dec 2006 [510] cdrskin/cdrskin.1 Corrected alphabetical sorting error 14 Dec 2006 [511] cdrskin/changelog.txt Next cdrskin-0.2.7 cycle 14 Dec 2006 [512] cdrskin/convert_man_to_html.sh Added some meta info to the document header, changed title to "man 1 cdrskin" 14 Dec 2006 [513] cdrskin/wiki_plain.txt Adapted to existence of man page 15 Dec 2006 [514] cdrskin/cdrskin.1 Clarified drives and their addresses 15 Dec 2006 [515] cdrskin/convert_man_to_html.sh Adapted to changes in cdrskin.1 15 Dec 2006 [516] cdrskin/cdrskin.1 Introduced term "session" 2006.12.16.090001 [518] cdrskin-0.2.6/Makefile.am + cdrskin-0.2.6/cdrskin/cdrskin.1 cdrskin-0.2.6/cdrskin/README cdrskin-0.2.6/cdrskin/cdrskin_eng.html cdrskin-0.2.6/cdrskin/changelog.txt cdrskin-0.2.6/cdrskin/cdrskin_timestamp.h Backported manpage to libburn-0.2.6.1 -> libburn-0.2.6.2 18 Dec 2006 [519] cdrskin/cdrskin.1 Made several minor clarifications 2006.12.18.123242 [520] libburn/mmc.c Noted some insight about necessity of OPC gained from growisofs_mmc.cpp 2006.12.20.111932 [522] libburn/transport.h libburn/options.h libburn/options.c libburn/write.c libburn/sector.c libburn/mmc.c libburn/libdax_msgs.h Prepared experiments for writing to DVD (most easy: DVD+RW) 2006.12.20.142301 [523] libburn/write.c libburn/libdax_msgs.h Refuse to burn audio tracks to DVD 2006.12.20.145222 [524] libburn/drive.c libburn/mmc.c Avoid undefined 43h commands (TOC/ATIP) with non-CD 2006.12.20.170502 [525] libburn/mmc.c Corrected DVD+RW track number and nwa with 52h READ TRACK INFORMATION 2006.12.20.171230 [526] libburn/mmc.c Corrected bug reported by gcc -O2 2006.12.20.174016 [527] yylibburn/write.c Corrected bug reported by gcc -O2 2006.12.20.180214 [528] cdrskin/cdrskin.c With -atip report "booktype" for DVD media and no questionable ATIP info 2006.12.20.195421 [529] cdrskin/cdrskin.c With -atip on DVD report no RAW/RAW96R among "Supported modes" 2006.12.21.122301 [530] libburn/write.c Removed some debugging messages 2006.12.21.122533 [531] cdrskin/cdrskin.c DVD speed reporting (and setting for drives which obey BBh SET CD SPEED) 2006.12.21.200556 [532] libburn/mmc.c libburn/libdax_msgs.h DVD speed setting via B6h SET STREAMING, DVD+RW now enabled in vanilla build 2006.12.21.205702 [533] libburn/write.c Disallowed multi flag with DVD+RW (nurses wrong hopes for now) 21 Dec 2006 [534] test/libburner.c Report media type, avoid self contradicting messages with DVD+RW --multi 2006.12.21.214641 [535] cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/cdrskin_eng.html doc/comments Some bragging about DVD+RW ------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.22.120854 * Burning of DVD+RW media as single-track TAO-like initial session 2006.12.23.102056 [536] libburn/libburn.h libburn/options.h libburn/options.c libburn/write.c Adjustable write position for DVD+RW: burn_write_opts_set_start_byte() 2006.12.23.102201 [537] cdrskin/cdrskin.c New option write_start_address= 23 Dec 2006 [538] cdrskin/cdrskin.1 cdrskin/README cdrskin/convert_man_to_html.sh Added more info about DVD+RW 2006.12.23.141315 [539] libburn/libburn.h libburn/drive.c libburn/write.c cdrskin/cdrskin.c New API function to inquire burn success (and avoid confusing messages) 2006.12.23.184353 [540] libburn/libburn.h libburn/write.c cdrskin/cdrskin.c More appropriate drive status during format and close of DVD+RW 2006.12.24.140904 [547] cdrskin/cdrskin.c libburn/write.c Fixed a bug with speed measurement at end of DVD+RW burning 2006.12.24.142512 [548] libburn/options.h libburn/options.c libburn/write.c Made DVD 32k end padding controllable 2006.12.24.154455 [549] cdrskin/cdrskin.c Made DVD ignore CD write modes of drive. Made TAO default for DVD+RW. 2006.12.24.182307 [550] libburn/write.c Bugfix with DVD+RW : progress indicators were initialized too late 2006.12.24.182410 [551] libburn/options.c Bugfix after changes for DVD+RW: start_byte was initialized 0, but must be -1 24 Dec 2006 [552] test/libburner.c test/telltoc.c Removed unnecessary waiting loops after grab, mentioned DVD+RW 2006.12.25.113534 [553] libburn/libburn.h libburn/spc.c libburn/drive.c Ticket 93: write speeds from mode page 2Ah descriptors 25 Dec 2006 [554] test/telltoc.c Adjusted to new (still immature) speed info semantics 2006.12.25.185747 [555] cdrskin/cdrskin.c Corrected CD speed conversion factor to 2352*75/1000 = 176.4 kB/s 2006.12.25.190055 [556] libburn/spc.c libburn/mmc.h libburn/mmc.c libburn/write.c libburn/libdax_msgs.h Ticket 93: write speeds from ACh GET PERFORMANCE, Type 03h, DVD media capacity 25 Dec 2006 [557] test/telltoc.c Adjusted to new (more mature) speed info semantics 2006.12.25.190811 [558] libburn/transport.h Completed revision 556 2006.12.26.170459 [559] libburn/libburn.h libburn/transport.h libburn/drive.h libburn/drive.c libburn/mmc.c libburn/spc.c New API calls burn_drive_get_speedlist() , burn_drive_free_speedlist() 26 Dec 2006 [560] test/telltoc.c Enabled report of speed descriptor list 2006.12.26.184321 [561] libburn/libburn.h test/telltoc.c Minor corrections to revisions 559 and 560 2006.12.27.125948 [562] libburn/drive.c Disabled obsolete drive-media-state workaround. (Spinoff of ticket 93 :)) 2006.12.27.130125 [563] libburn/mmc.c Corrected kB conversion factor to 176.4 with ATIP speed codes 2006.12.27.130239 [564] cdrskin/cdrskin.c Defended against a race condition on SuSE 9.3 after -atip (hald et.al. ?) 2006.12.27.132653 [565] libburn/mmc.c Avoided self contradicting result of ATIP speed inquiry 2006.12.27.162846 [566] cdrskin/cdrskin.c cdrskin/cdrskin.1 Emulated wodim option -msifile=path 2006.12.27.213729 [567] cdrskin/cdrskin.c Followed revision 644 of wodim (msifile output without newline) 27 Dec 2006 [568] cdrskin/cdrskin_eng.html cdrskin/cdrskin.1 Updated about msifile=path ------------------------------------ cycle - cdrskin-0.2.7 - 2006.12.27.214424 * New option -msifile=path from cdrkit/wodim 2006.12.29.143734 [569] libburn/mmc.c Corrected DVD-RW sequential profile name 2006.12.30.001343 [570] libburn/mmc.h libburn/mmc.c libburn/spc.c libburn/write.c libburn/libdax_msgs.h cdrskin/cdrskin.c Prepared support for DVD-RW in mode Restricted Overwrite 29 Dec 2006 [571] cdrskin/changelog.txt Next cdrskin-0.2.7 cycle 2007.01.01.170824 [572] libburn/libburn.h libburn/transport.h libburn/spc.c libburn/mmc.h libburn/mmc.c libburn/async.c libburn/drive.h libburn/drive.c libburn/write.c libburn/libdax_msgs.h Prepared formatting of DVD-RW 2007.01.01.171725 [573] cdrskin/cdrskin.c cdrskin/README cdrskin/cdrskin.1 cdrskin/wiki_plain.txt cdrskin/cdrskin_eng.html Made use of formatting of DVD-RW 2 Jan 2007 [574] cdrskin/wiki_plain.txt Added links to cdrskin help texts 2007.01.02.090530 [575] COPYRIGHT README cdrskin/cdrskin.c Greeting the new year ------------------------------------ cycle - cdrskin-0.2.7 - 2007.01.02.101027 * Formatting and then burning to DVD-RW like to DVD+RW 2 Jan 2007 [576] cdrskin/changelog.txt Next cdrskin-0.2.7 cycle 2 Jan 2007 [577] cdrskin/wiki_plain.txt cdrskin/README Some DVD-RW statements 2 Jan 2007 [578] test/libburner.c doc/comments Made use of formatting of DVD-RW 2 Jan 2007 [579] cdrskin/cdrskin.1 Some DVD-RW statements 2007.01.03.163026 [583] libburn/mmc.c Made formatting report progress (as good as the drive does) 2007.01.03.164716 [584] libburn/async.c libburn/drive.c Moved blanking suitability test before eventual spwaning of threads 2007.01.05.125715 [587] libburn/mmc.c libburn/write.c Comments and name changes from new findings out of reading MMC-5 2007.01.06.120551 [591] libburn/libburn.h libburn/transport.h libburn/mmc.h libburn/mmc.c libburn/async.c libburn/drive.h libburn/drive.c libburn/write.c cdrskin/cdrskin.c test/libburner.c New formatting parameter "size". Sorry for changing API. Function is a week old. 8 Jan 2007 [592] libburn/os-linux.h libburn/os-freebsd.h Added note that buffer may not be smaller than 32768 2007.01.08.104222 [593] libburn/libburn.h libburn/drive.c libburn/write.c libburn/mmc.c Introduced size parameter to DVD-RW formatting plus writing of zeros. 2007.01.08.104407 [594] cdrskin/cdrskin.1 cdrskin/cdrskin.c Using 128 MB of size plus writing of zeros with blank=format_overwrite 2007.01.09.140302 [595] libburn/drive.c Fixed a SIGFPE with formatting via libburner 9 Jan 2007 [596] libburn/libburn.h libburn/transport.h libburn/libdax_msgs.h libburn/drive.c libburn/write.c libburn/mmc.c Enhanced DVD-RW formatting 2007.01.09.211152 [597] cdrskin/README cdrskin/cdrskin.1 cdrskin/cdrskin.c Now available: "quickest" and "full" formatting for DVD-RW ------------------------------------ cycle - cdrskin-0.2.7 - 2007.01.10.101406 10 Jan 2007 [598] cdrskin/cdrskin_eng.html Updated size estimation of development downloads 10 Jan 2007 [599] cdrskin/changelog.txt Next cdrskin-0.2.7 cycle 2007.01.10.152350 [600] libburn/libburn.h libburn/mmc.c libburn/drive.c libburn/async.c cdrskin/cdrskin.c Option -force enables re-formatting 2007.01.10.152520 [601] libburn/mmc.c Switched full formatting from type 10h to 00h which includes lead-out 2007.01.10.152812 [602] cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README Removed writing of dummy data with blank=format_overwrite_full 2007.01.10.204839 [603] libburn/mmc.c libburn/async.c cdrskin/cdrskin.c cdrskin/cdrskin.1 Enabled explicit full formatting of DVD+RW ("de-icing") 11 Jan 2007 [604] cdrskin/README Removed outdated option from startup file example 2007.01.11.131106 [605] libburn/mmc.c With full formatting prefer format 10h over 13h or 15h 2007.01.11.131302 [606] libburn/os-linux.h libburn/os-freebsd.h libburn/cleanup.c cdrskin/cleanup.c Kept SIGWINCH from spoiling a burn. 2007.01.11.131615 [607] libburn/init.c Sketched better handling of self-inflicted SIGs 2007.01.11.131716 [608] libburn/drive.c Removed surplus newlines from messages 2007.01.12.162239 [609] libburn/write.c libburn/spc.c libburn/mmc.c cdrskin/cdrskin.c cdrskin/cdrskin.1 Enabled writing to DVD-RAM 2007.01.13.140812 [610] [611] libburn/sg-linux.c Implemented debugging messages for ATA enumeration 13 Jan 2007 [612] cdrskin/cdrskin_eng.html cdrskin/README Documentation updates about DVD-RAM 2007.01.13.211425 [613] libburn/transport.h libburn/mmc.c Load array of format capacities into struct burn_drive 2007.01.13.211639 [614] libburn/libburn.h libburn/drive.c libburn/async.c Introduced API for inspection and selection of format capacities 13 Jan 2007 [615] test/telltoc.c Added printing of list of available formats 13 Jan 2007 [616] test/libburner.c Mentioned DVD-RAM where appropriate 2007.01.13.214259 [617] cdrskin/cdrskin.c Shifted fifo reporting to new 4-times -v verbosity level 2007.01.14.101742 [618] cdrskin/cdrskin.c Corrected bug with debug messages for fifo 2007.01.14.115347 [619] libburn/write.c Added missing cache sync in case of aborted DVD-RW burns 2007.01.14.133951 [620] libburn/transport.h libburn/mmc.c libburn/write.c Avoided closing of 0x13-DVD-RW sessions which are not intermediate 15 Jan 2007 [621] cdrskin/wiki_plain.txt Updated about overwriteable DVD and pointer to dvd+rw-tools ------------------------------------ cycle - cdrskin-0.2.7 - 2007.01.15.131357 * Burning to DVD-RAM 15 Jan 2007 [623] cdrskin/changelog.txt Next cdrskin-0.2.7 cycle 2007.01.16.120001 [tag 624] Makefile.am configure.ac README cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/changelog.txt Made version number transition to 0.3.0 15 Jan 2007 [tag 625] - cdrskin/add_ts_changes_to_libburn_0_2_6 - cdrskin/add_ts_changes_to_libburn_0_2_7 + cdrskin/add_ts_changes_to_libburn_0_3_0 Updated cdrskin tarball generator 16 Jan 2007 [tag 626] README Corrected statement about restriction to CD 16 Jan 2007 [tag 627] cdrskin/cdrskin.c Silenced a compiler warning 16 Jan 2007 [tag 628] cdrskin/README Corrected name of tarball 16 Jan 2007 [tag 632] test/telltoc.c Corrected old libburn.pykix.org URL 16 Jan 2007 [tag 634] cdrskin/cdrskin_eng.html Updated web page ------------------------------- cycle - cdrskin-0.3.0.pl00 - 2007.01.16.120001 * Improved recognition of unsuitable media types * Replaced ban of chmod u+s by loud warning * detailed man page for cdrskin * Burning of DVD+RW and DVD-RAM media as single-track TAO-like initial session * Formatting and then burning to DVD-RW like to DVD+RW * New option -msifile=path from cdrkit/wodim 16 Jan 2007 [629] - cdrskin/add_ts_changes_to_libburn_0_2_6 - cdrskin/add_ts_changes_to_libburn_0_2_7 + cdrskin/add_ts_changes_to_libburn_0_3_0 + cdrskin/add_ts_changes_to_libburn_0_3_1 Updated cdrskin tarball generator 2007.01.16.151041 [630] Makefile.am configure.ac cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh Made version number transition to 0.3.1 16 Jan 2007 [631] test/telltoc.c Corrected old libburn.pykix.org URL 16 Jan 2007 [633] cdrskin/cdrskin_eng.html Corrected typo ------------------------------------ cycle - cdrskin-0.3.1 - 2007.01.16.152345 16 Jan 2007 [635] cdrskin/changelog.txt Next cdrskin-0.3.1 cycle 17 Jan 2007 [636] cdrskin/wiki_plain.txt Removed paragraph about obsoleted tao_to_sao_tsize= 18 Jan 2007 [637] + doc/cookbook.txt Wrote down what i learned from implementing support for overwriteable DVD 18 Jan 2007 [638] doc/cookbook.txt More info about Current/Maximum Capacity Descriptor 18 Jan 2007 [639] doc/cookbook.txt Made clarification about formatting state recognition 2007.01.18.211740 [640] libburn/transport.h libburn/mmc.c libburn/write.c Kept DVD+RW from stopping BG formatting if it was not started at all 2007.01.19.110510 [641] libburn/mmc.c Removed forgotten debug message printed to stderr 19 Jan 2007 [642] doc/cookbook.txt Began to describe TAO multi-session CD writing 20 Jan 2007 [643] doc/cookbook.txt Changes with CD part. Especially explanation of TOC. 2007.01.21.190928 [644] cdrskin/cdrskin.c Removed unnecessary after-grab loops 2007.01.21.191058 [645] libburn/write.c Comments learned from studying MMC-3 and MMC-5 for SAO CD cookbook 21 Jan 2007 [646] doc/cookbook.txt Clarification about TAO and mode page 05h per track 2007.01.22.114245 [647] libburn/libburn.h libburn/write.c libburn/async.c libburn/libdax_msgs.h cdrskin/cdrskin.c Bug fix: Banned mixed mode SAO (because broken in libburn and unclear in MMC) 23 Jan 2007 [648] doc/cookbook.txt Added SAO CD Cookbook 23 Jan 2007 [649] doc/cookbook.txt Corrections of typos and text debris 2007.01.25.145846 [652] libburn/structure.c Bug fix: DVD tracks of defined size >=2GB suffered 32-bit integer wraparound 2007.01.25.185214 [655] libburn/libburn.h libburn/write.c libburn/structure.h libburn/structure.c libburn/file.h libburn/file.c Enforce minimum track length with SAO 2007.01.25.180001 [tag 656] libburn_0_3_0_1/libburn/structure.c libburn_0_3_0_1/cdrskin/cdrskin_timestamp.h libburn_0_3_0_1/cdrskin/README Bug fix: DVD tracks of defined size >=2GB suffered 32-bit integer wraparound 26 Jan 2007 [tag 657] libburn_0_3_0_1/cdrskin/cdrskin_eng.html Mentioned new cdrskin patch level 01 26 Jan 2007 [658] cdrskin/cdrskin_eng.html Now offering cdrskin-0.3.0.pl01 for download ------------------------------------ cycle - cdrskin-0.3.1 - 2007.01.26.111920 2007.01.26.173236 [659] libburn/file.h libburn/file.c Unified burn_file_source and burn_fd_source 27 Jan 2007 [660] libburn/null.c Initialized member set_size of burn_source within burn_null_source_new() 27 Jan 2007 [tag 661] libburn_0_3_0_1/libburn/file.c Removed 1.3 GB curbs for sources created by burn_file_source_new() 29 Jan 2007 [662] doc/cookbook.txt Added a statement about blanking of DVD-RW 2007.01.29.175822 [663] libburn/transport.h libburn/drive.c libburn/write.c libburn/mmc.c Experiments about list of features and profiles 2007.01.30.165317 [664] libburn/write.c libburn/mmc.c Preparations for DVD-R[W] Sequential Recording 2007.01.30.191740 [665] libburn/write.c libburn/mmc.c First successful multi-session write to a sequential DVD-RW 30 Jan 2007 [666] doc/cookbook.txt Added snapshot of emerging sequential DVD-R[W] cookbook 2007.01.30.220220 [667] cdrskin/cdrskin.c cdrskin/cdrskin.1 Enabled Burnfree buffer underrun protection by default 2007.01.31.130100 [668] libburn/async.c cdrskin/cdrskin.c cdrskin/cdrskin.1 Blank sequential DVD-RW, deformat overwriteable DVD-RW 2007.01.31.173611 [669] libburn/libburn.h libburn/transport.h libburn/drive.c libburn/mmc.c cdrskin/cdrskin.c cdrskin/cdrskin.1 Provisorily obtain multi-session -C parameters (violates MMC specs but works) 2007.02.01.161634 [670] libburn/libburn.h libburn/transport.h libburn/mmc.c test/telltoc.c cdrskin/cdrskin.c cdrskin/cdrskin.1 Obtain TOC from non-CD via 52h READ TRACK INFORMATION 2007.02.01.163511 [671] cdrskin/cdrskin.c Reacted on justified compiler warning about unitialized sessions variable 2007.02.01.191638 [672] libburn/mmc.c cdrskin/cdrskin.c Allow blanking of DVD-RW which offer no Incremental Streaming 1 Feb 2007 [673] cdrskin/cdrskin_eng.html cdrskin/README cdrskin/cdrskin.1 Prepared next cdrskin-0.3.1 cycle 1 Feb 2007 [674] cdrskin/changelog.txt Prepared next cdrskin-0.3.1 cycle 1 Feb 2007 [675] [676] cdrskin/cdrskin.1 Mentioned DVD-RW multi-session in overview of features ------------------------------------ cycle - cdrskin-0.3.1 - 2007.02.01.203057 * Burnfree enabled by default * Multi-session recording on sequential DVD-RW, including -toc, -msinfo 2 Feb 2007 [677] cdrskin/convert_man_to_html.sh Some sed expressions for beautification 2 Feb 2007 [678] doc/cookbook.txt Updated about DVD-R[W] blanking, multi-session info and TOC 2007.02.02.151327 [679] libburn/mmc.c test/telltoc.c Make mmc_read_multi_session_c1 use TOC if available 2007.02.02.173345 [680] libburn/mmc.c cdrskin/cdrskin.c Improved classification and TOC of finalized DVD-R[W] media ------------------------------------ cycle - cdrskin-0.3.1 - 2007.02.02.183755 3 Feb 2007 [681] cdrskin/cdrskin.1 cdrskin/wiki_plain.txt Mentioned DVD-RW multi-session 2007.02.03.205526 (comitted 4 Feb 2007) [682] libburn/libburn.h libburn/mmc.c libburn/drive.c libburn/async.c test/telltoc.c New in API : struct burn_multi_caps and burn_disc_get_multi_caps() 2007.02.05.132335 [683] libburn/transport.h libburn/mmc.c Preparations for DVD-R[W] DAO 2007.02.06.130410 [684] libburn/libburn.h libburn/mmc.c libburn/write.c libburn/drive.c libburn/libdax_msgs.h Implemented DVD-R[W] DAO as BURN_WRITE_SAO 2007.02.06.170621 [685] libburn/write.c libburn/mmc.c Beautification of debugging messages 2007.02.06.174320 [686] cdrskin/cdrskin.c tao_to_sao_tsize= for DVD-R[W] DAO 2007.02.06.185534 [687] libburn/async.c cdrskin/cdrskin.c Enabled fast blank for DVD-R[W] 6 Feb 2007 [688] cdrskin/cdrskin.1 Clarified CD and DVD peculiarities 6 Feb 2007 [689] doc/cookbook.txt Described DVD-R[W] DAO mode ------------------------------------ cycle - cdrskin-0.3.1 - 2007.02.06.200802 * DVD-R[W] Disk-at-once writing 6 Feb 2007 [690] cdrskin/README cdrskin/changelog.txt cdrskin/cdrskin_eng.html Next cdrskin-0.3.1 cycle 2007.02.07.162836 [691] libburn/libburn.h libburn/drive.h libburn/drive.c libburn/options.c libburn/libdax_msgs.h New API function burn_write_opts_auto_write_type() 7 Feb 2007 [692] test/libburner.c Made use of burn_write_opts_auto_write_type() 7 Feb 2007 [693] test/libburner.c doc/comments Updated documentation aspects 8 Feb 2007 [694] README doc/comments cdrskin/cdrskin.1 cdrskin/README Finally made tests with DVD-R. They burn indeed like DVD-RW. 2007.02.08.210744 [695] cdrskin/cdrskin.c cdrskin/cdrskin.1 New option --prodvd_cli_compatible 8 Feb 2007 [696] cdrskin/wiki_plain.txt cdrskin/cdrskin_eng.html Mentioned DVD-R 2007.02.08.225208 [697] cdrskin/cdrskin.c Silenced compiler warning ------------------------------------ cycle - cdrskin-0.3.1 - 2007.02.09.074058 9 Feb 2006 [698] cdrskin/cdrskin_eng.html Added special thanks towards Andy Polyakov 9 Feb 2006 [699] cdrskin/cdrskin.1 doc/cookbook.txt Small corrections in documentation 10 Feb 2007 [tag 701] [705] - cdrskin/add_ts_changes_to_libburn_0_3_0 - cdrskin/add_ts_changes_to_libburn_0_3_1 + cdrskin/add_ts_changes_to_libburn_0_3_2 + cdrskin/add_ts_changes_to_libburn_0_3_3 Updated cdrskin tarball generator 2007.02.10.120001 [tag 702] [704] Makefile.am configure.ac README cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/wiki_plain.txt cdrskin/cdrskin_eng.html Made number transition and activated development documentation 10 Feb 2007 [tag 703] [707] cdrskin/changelog.txt Documented changes and 0.3.2 release timestamp ----------------------------- release - cdrskin-0.3.2.pl00 - 2007.02.10.120001 * Burnfree enabled by default * Multi-session recording on sequential DVD-R[W], including -toc, -msinfo * DVD-R[W] Disk-at-once recording 10 Feb 2007 [706] libburn/mmc.c Added a comment about DVD-R ------------------------------------ cycle - cdrskin-0.3.3 - 2007.02.10.190528 12 Feb 2007 [708] cdrskin/cdrskin.1 Being exacting about on-the-fly and DVD-RW 12 Feb 2007 [709] cdrskin/cdrskin_eng.html Updated list of keywords 2007.02.12.142245 [710] libburn/mmc.c Made profile 0010h DVD-ROM suitable,full,not erasable. So it delivers a TOC. 2007.02.13.115459 [711] libburn/spc.c Set a suitable page 05h after spc_probe_write_modes() 2007.02.13.143718 [712] libburn/libburn.h libburn/transport.h libburn/mmc.c libburn/spc.c libburn/drive.c Mew API function burn_disc_available_space() 13 Feb 2007 [713] test/libburner.c Removed or updated outdated restriction statements 13 Feb 2007 [714] test/telltoc.c Applied new API function burn_disc_available_space() 2007.02.14.120213 [715] libburn/spc.c Removed outdated ifdef 14 Feb 2007 [716] test/telltoc.c Set the advised write mode before inquiring media space 2007.02.14.121440 [717] libburn/libdax_msgs.h libburn/mmc.c Handle eventual ridiculously high d->last_track_no 2007.02.14.122218 [718] libburn/mmc.h Forgotten part of revision 718 2007.02.14.202944 [719] libburn/libburn.h libburn/options.h libburn/options.c libburn/structure.h libburn/structure.c libburn/write.c libburn/drive.c Optional padding up to full media size when closing (incomplete yet) 2007.02.14.203635 [720] cdrskin/cdrskin.c cdrskin/cdrskin.1 New options --fill_up_media and --tell_media_space 2007.02.15.201506 [722] libburn/options.c libburn/drive.c cdrskin/cdrskin.c libburn/write.c Took fill_up_media into respect with automatic write mode decisions 2007.02.15.201651 [723] libburn/transport.h libburn/mmc.c libburn/libdax_msgs.h Installed a guardian for predicted track end 2007.02.15.201757 [724] libburn/structure.c Corrected bug about open_ended filluped tracks 15 Feb 2007 [725] libburn/libburn.h cdrskin/cdrskin.1 Clarifications about current state of fillup 2007.02.15.203448 [726] libburn/write.c Repaired debugging message spoiled by uninitialized variable 2007.02.16.111941 [728] libburn/write.c Corrected CD TAO bug introduced with DVD bug fix 724 and CD SAO change 655 2007.02.17.085118 [729] libburn/structure.c Another bug fix for revision 724 2007.02.17.085533 [730] libburn/async.c cdrskin/cdrskin.c cdrskin/cdrskin.1 test/libburner.c Allowed forceful blanking of blank media in burn_disc_erase() 17 Feb 2007 [731] test/libburner.c Removed unprecise unnecessary comment ------------------------------------ cycle - cdrskin-0.3.3 - 2007.02.17.121238 * New option --tell_media_space tells the maximum size for the next burn 2007.02.18.094414 [732] libburn/libburn.h Clarified usage comment with burn_drive_info_free() (see ticket 98) 18 Feb 2007 [733] cdrskin/cdrskin.1 cdrskin/cdrskin_eng.html cdrskin/changelog.txt Next cdrskin-0.3.3 cycle 2007.02.18.094858 [734] libburn/mmc.h Adjusted maximum realistic number of tracks to MMC specs 2007.02.19.184132 [735] cdrskin/cdrskin.c Repaired slightly broken pacifier track size display with -audio 2007.02.19.225102 [736] libburn/libburn.h libburn/async.c libburn/structure.h libburn/structure.c libburn/write.h libburn/write.c libburn/drive.h libburn/drive.c libburn/options.c libburn/libdax_msgs.h Re-arranged checking and defaulting of write parameters = New API function burn_track_set_default_size() New API function burn_precheck_write() Make wide use of burn_disc_write_mode_demands() 2007.02.21.205244 [737] libburn/libburn.h libburn/async.c libburn/drive.c libburn/options.c libburn/structure.c libburn/write.c libburn/libdax_msgs.h cdrskin/cdrskin.c Moved tao_to_sao_tsize into libburn, let cdrskin use auto_write_type and precheck 21 Feb 2007 [738] cdrskin/add_ts_changes_to_libburn_0_3_3 cdrskin/add_ts_changes_to_libburn_0_3_2 Added -O2 to binary production 2007.02.22.072700 [739] libburn/libburn.h libburn/drive.c libburn/options.c Re-enabled overwriteable pseudo-pseudo-SAO with unpredicted track size 2007.02.22.073157 [740] libburn/mmc.c Disabled debugging messages about format descriptors 2007.02.22.094939 [741] libburn/libburn.h libburn/options.c libburn/write.c libburn/async.c test/libburner.c cdrskin/cdrskin.c Macro for length of rejection reasons string (old size is still safe) 2007.02.22.113016 [742] libburn/libburn.h libburn/drive.c Made burn_disc_available_space() take into respect burn_write_opts_set_start_byte() 2007.02.23.190937 [743] libburn/libburn.h libburn/drive.c libburn/mmc.c libburn/write.c doc/cookbook.txt Enabled DVD-R/DL Sequential via burn_allow_untested_profiles() 2007.02.23.191117 [744] cdrskin/cdrskin.c cdrskin/cdrskin.1 Enabled DVD-R/DL Sequential via --allow_untested_media_types 2007.02.23.193427 [745] libburn/init.c Forgotten source file for revision 743 ------------------------------------ cycle - cdrskin-0.3.3 - 2007.02.24.102731 2007.02.25.112733 [746] libburn/write.h libburn/write.c libburn/drive.c cdrskin/cdrskin.1 Took into respect deliberate lack of DVD-R/DL multi session capability 2007.03.01.120945 [747] libburn/drive.c libburn/mmc.c libburn/write.c cdrskin/cdrskin.c doc/cookbook.txt Preparations for supporting DVD+R[/DL] 3 Mar 2007 [748] cdrskin/cdrskin.1 Updated DVD-R[W] write mode description 2007.03.03.141240 [749] libburn/transport.h libburn/mmc.c Determine physical interface SCSI,ATA,SATA,USB,... (for future use) 2007.03.03.141435 [750] libburn/libburn.h libburn/write.c libburn/options.h libburn/options.c cdrskin/cdrskin.c Re-enabled -force with write modes which seem unavailable 2007.03.03.151812 [751] libburn/options.c Fixed bug introduced with rev 736ff which prevented audio CD burning 2007.03.04.184720 [752] cdrskin/cdrskin.c cdrskin/cdrfifo.c Fifo got stuck if sum of processed track sizes was exactly aligned to fifo size ------------------------------------ cycle - cdrskin-0.3.3 - 2007.03.04.185202 * Bug fix: Multi-track runs with fifo could stall in rare cases 5 Mar 2007 [753] cdrskin/cdrskin_eng.html cdrskin/add_ts_changes_to_libburn_0_3_2 Released cdrskin-0.3.2.pl01 2007.03.06.195203 [754] libburn/mmc.c libburn/write.c Enabled DVD+R, DVD+R DL via --allow_untested_media_types, always -multi for now 2007.03.06.205312 [755] libburn/mmc.c cdrskin/cdrskin.1 doc/cookbook.txt doc/comments Enabled DVD+R as tested media (-multi is still always on) 2007.03.07.151514 [756] libburn/write.c cdrskin/cdrskin.1 cdrskin/README doc/cookbook.txt Some adjustments for DVD+R recording 2007.03.07.151514 [756] cdrskin/cdrskin_eng.html cdrskin/changelog.txt Next cdrskin-0.3.3 cycle ------------------------------------ cycle - cdrskin-0.3.3 - 2007.03.07.155750 * Multi-session burning to DVD+R 8 Mar 2007 [757] cdrskin/cdrskin.1 cdrskin/convert_man_to_html.sh cdrskin/wiki_plain.txt Polished documentation 2007.03.09.134622 [758] cdrskin/cdrskin.c cdrskin/cdrskin.1 New option assert_write_lba= 10 Mar 2007 [759] cdrskin/cdrskin.1 cdrskin/wiki_plain.txt Polished documentation ------------------------------------ cycle - cdrskin-0.3.3 - 2007.03.10. * New option assert_write_lba= 2007.03.12.110001 [tag 761] Makefile.am configure.ac README cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.3.4 and activated development documentation 12 Mar 2007 [tag 762] - cdrskin/add_ts_changes_to_libburn_0_3_2 - cdrskin/add_ts_changes_to_libburn_0_3_3 + cdrskin/add_ts_changes_to_libburn_0_3_4 + cdrskin/add_ts_changes_to_libburn_0_3_5 Updated cdrskin tarball generator 12 Mar 2007 [tag 763] cdrskin/changelog.txt Documented most recent changes 12 Mar 2007 [tag 764] README Removed redundant sentence ----------------------------- release - cdrskin-0.3.4.pl00 - 2007.03.12.110001 * Multi-session burning to DVD+R * New option --tell_media_space tells the maximum size for the next burn * New option assert_write_lba= * Bug fix: Multi-track runs with fifo could stall in rare cases 2007.03.12.155600 [765] Makefile.am configure.ac README cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.3.5 12 Mar 2007 [766] - cdrskin/add_ts_changes_to_libburn_0_3_2 - cdrskin/add_ts_changes_to_libburn_0_3_3 + cdrskin/add_ts_changes_to_libburn_0_3_4 + cdrskin/add_ts_changes_to_libburn_0_3_5 Updated cdrskin tarball generator ------------------------------------ cycle - cdrskin-0.3.5 - 2007.03.12.160658 14 Mar 2007 [767] cdrskin/cdrskin_eng.html Corrected truncated sentence and file sizes 14 Mar 2007 [768] cdrskin/changelog.txt Next cdrskin-0.3.5 cycle 2007.03.14.133618 [769] libburn/libburn.h libburn/init.c libburn/sg-linux.c cdrskin/cdrskin.c cdrskin/cdrskin.1 New option drive_scsi_dev_family=sr|scd|sg 2007.03.15.194531 [770] libburn/drive.c React properly on drive stating that it cannot write any media 2007.03.15.195005 [771] libburn/spc.h libburn/spc.c libburn/sbc.c After loading tray wait for unit to become ready or to report some clear error 2007.03.15.195251 [772] cdrskin/cdrskin.c Moved manual device family decision to a sufficiently early stage 2007.03.15.195428 [773] libburn/mmc.c Kept mmc_get_configuration() from believing the announcement of 1 GB reply 2007.03.15.195534 [774] libburn/sg-linux.c Trying to recognize kernel >= 2.6 and use /dev/sr by default 15 Mar 2007 [775] cdrskin/cdrskin.1 Updated drive_scsi_dev_family= 15 Mar 2007 [776] cdrskin/changelog.txt Next cdrskin-0.3.5 cycle ------------------------------------ cycle - cdrskin-0.3.5 - 2007.03.16.001311 * Usage of /dev/srN rather than /dev/sgN on Linux >= 2.6 * New option drive_scsi_dev_family=sr|scd|sg 18 Mar 2007 [777] cdrskin/cdrskin_eng.html Mentioned new sr behavior 2007.03.24.093238 [785] libburn/drive.c Fixed bug with burn_disc_available_space(...,NULL) 2007.03.24.093623 [786] cdrskin/cdrskin.c Warning of very small tsize= settings. (Proposal by Eduard Bloch) 2007.03.27.213543 [787] cdrskin/cdrskin.c cdrskin/cdrfifo.h cdrskin/cdrfifo.c Preparations for option -isosize via fifo (only a debug message yet) 2007.03.28.100934 [788] libburn/libburn.h libburn/structure.c cdrskin/cdrskin.c Enabled -isosize for first track by help of fifo and without seeking 2007.03.28.111739 [789] cdrskin/cdrskin.c Silenced error condition about -sao with stdin and -isosize 2007.03.28.160503 [790] cdrskin/cdrskin.c Enabled -isosize with S_IFREG or S_IFBLK files and without fifo 2007.03.28.182419 [791] cdrskin/cdrskin.c cdrskin/cdrskin.1 Made fifo based -isosize read 64k first and the rest only at normal stage 2007.03.28.202802 [792] cdrskin/cdrskin.c Silenced error message if tsize= is smaller than source is willing to deliver ------------------------------------ cycle - cdrskin-0.3.5 - 2007.03.28.212322 * Option -isosize is supported now 29 Mar 2007 [793] cdrskin/cdrskin_eng.html cdrskin/changelog.txt Next cdrskin-0.3.5 cycle 2007.03.30.201034 [794] libburn/write.c cdrskin/cdrskin.1 Allowed finalizing of DVD+R 2007.03.30.214657 [795] libburn/write.c Avoided unconditional finalizing of DVD+R 2007.04.03.140356 [796] libburn/sg-linux.c Added fcntl() locking to O_EXCL locking 2007.04.03.145637 [797] cdrskin/cdrskin.c Make --old_pseudo_scsi_adr -scanbus work with any drive_scsi_dev_family= 2007.04.03.145806 [798] libburn/libdax_msgs.h Added fcntl() locking to O_EXCL locking 2007.04.04.184341 [799] libburn/libburn.h libburn/init.c libburn/sg-linux.c cdrskin/cdrskin.c cdrskin/cdrskin.1 New cdrskin options --drive_not_f_setlk and --drive_not_o_excl 6 Apr 2007 [801] test/libburner.c Updated media list in introduction comment 2007.04.09.105543 [802] libburn/transport.h libburn/os-linux.h libburn/sg-linux.c cdrskin/cdrskin.1 Cleaned up scsi sibling management, sketched grafting of DDLP 2007.04.09.111215 [803] libburn/sg-linux.c Reacted on compiler warning about last_rdev, fixed fresh typo bug ------------------------------------ cycle - cdrskin-0.3.5 - 2007.04.09.112127 * DVD+R now get finalized (if not -multi is given) 10 Apr 2007 [804] cdrskin/changelog.txt Next cdrskin-0.3.5 cycle 2007.04.10.081855 [805] libburn/init.c Fixed bug with drive_scsi_dev_family= introduced by revision 796 (fcntl lock) 2007.04.10.082229 [806] libburn/sg-linux.c Used O_EXCL|O_RDWR and fcntl() even within sg_obtain_scsi_adr() 2007.04.10.083119 [807] libburn/sg-linux.c Fixed bug in sg_open_scsi_siblings() introduced with revision 802 2007.04.10.144840 [808] libburn/spc.c libburn/libdax_msgs.h Avoided SIGSEGV with an old SCSI CD-ROM drive and its wild replies 12 Apr 2007 [810] README Clarified license. People who object shall please come forward now. 2007.04.13.171347 [815] libburn/sg-linux.c libburn/libdax_msgs.h Switched from O_NONBLOCK to O_NDELAY (see http://lkml.org/lkml/2007/4/11/141) 2007.04.13.173008 [816] cdrskin/cdrskin.c cdrskin/cdrskin.1 Made use of fcntl(F_SETLK) switchable (and thus became more free with default) 15 Apr 2007 [819] + libburn/ddlpa.h + libburn/ddlpa.c Began test implementation of DDLP-A 15 Apr 2007 [820] libburn/ddlpa.h libburn/ddlpa.c Implemented ddlpa_lock_btl() 16 Apr 2007 [821] doc/cookbook.txt Finalized DVD+R cookbook 16 Apr 2007 [822] + doc/ddlp.txt Emerging description of DDLP 16 Apr 2007 [823] doc/ddlp.txt libburn/ddlpa.h libburn/ddlpa.c Polished txt and finally threw out getter functions 16 Apr 2007 [824] libburn/ddlpa.h Corrected description of return values 18 Apr 2007 [825] + test/open-cd-excl.c Program for probing access to device files. By Ted Ts'o with modifications by me. 18 Apr 2007 [826] doc/ddlp.txt Allowed for Friendly Programs: O_EXCL | O_RDONLY 18 Apr 2007 [827] libburn/ddlpa.h libburn/ddlpa.c Progress due to tests with test/open-cd-excl 2007.04.18.103734 [828] libburn/mmc.c Updated comments about DVD+R 18 Apr 2007 [829] test/open-cd-excl.c libburn/ddlpa.c doc/ddlp.txt Polished messages, comments and description of DDLP-A 18 Apr 2007 [830] test/open-cd-excl.c doc/ddlp.txt Adaptations to new test results and discussions 18 Apr 2007 [831] doc/ddlp.txt Corrected a list of standard paths 19 Apr 2007 [832] [833] doc/ddlp.txt Clarifications about motivation and duties of the participants 20 Apr 2007 [834] doc/ddlp.txt Beginning to develop DDLP-B 21 Apr 2007 [835] doc/ddlp.txt Declared failure of DDLP to entirely solve the concurrency problem ------------------------------------ cycle - cdrskin-0.3.5 - 2007.04.22.112236 22 Apr 2007 [836] cdrskin/changelog.txt Next cdrskin-0.3.5 cycle 22 Apr 2007 [837] cdrskin/add_ts_changes_to_libburn_0_3_5 cdrskin/add_ts_changes_to_libburn_0_3_4 Repaired autotools bootstrap bug by help of sed 22 Apr 2007 [838] cdrskin/cdrskin.1 Changed many /dev/sg to /dev/sr 2007.04.23.130001 [tag 841] Makefile.am configure.ac README cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/wiki_plain.txt cdrskin/cdrskin_eng.html doc/comments Made number transition and activated development documentation 23 Apr 2007 [tag 842] - cdrskin/add_ts_changes_to_libburn_0_3_4 - cdrskin/add_ts_changes_to_libburn_0_3_5 + cdrskin/add_ts_changes_to_libburn_0_3_6 + cdrskin/add_ts_changes_to_libburn_0_3_7 Updated cdrskin tarball generator 23 Apr 2007 [tag 844] cdrskin/README Corrected false info about outdated addressing method 2007.04.23.154600 [843] Makefile.am configure.ac README cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/wiki_plain.txt cdrskin/cdrskin_eng.html doc/comments Made number transition and activated development documentation 23 Apr 2007 [845] - cdrskin/add_ts_changes_to_libburn_0_3_4 - cdrskin/add_ts_changes_to_libburn_0_3_5 + cdrskin/add_ts_changes_to_libburn_0_3_6 + cdrskin/add_ts_changes_to_libburn_0_3_7 Updated cdrskin tarball generator ----------------------------- release - cdrskin-0.3.6.pl00 - 2007.04.23.130001 * Usage of /dev/srN rather than /dev/sgN on Linux >= 2.6 * New option drive_scsi_dev_family=sr|scd|sg * Option -isosize is supported now * DVD+R now get finalized (if not -multi is given) 2007.04.23.171735 [846] cdrskin/changelog.txt Next cdrskin-0.3.7 cycle ------------------------------------ cycle - cdrskin-0.3.7 - 2007.04.24.113310 2007.05.21.184334 [853] libburn/sg-linux.c Prepared fflushing and stderr output of SCSI command log 2007.05.21.185450 [854] libburn/sbc.c libburn/spc.h libburn/spc.c libburn/mmc.c libburn/toc.c libburn/transport.h For Linux 2.4, USB : Carefully avoided to inquire more data than available 2007.05.21.185644 [855] libburn/sector.c libburn/write.c For Linux 2.4, USB audio : Reduced CD output buffer size to 32 kiB 21 May 2007 [856] cdrskin/cdrskin_eng.html cdrskin/changelog.txt Next cdrskin-0.3.7 cycle ------------------------------------ cycle - cdrskin-0.3.7 - 2007.05.21.201501 * Now able to cope with the peculiarities of Linux 2.4 USB 2007.05.22.154407 [857] libburn/sg-linux.c Report eventual sg_io_hdr_t host_status,driver_status as debug messages 2007.05.22.154504 [858] cdrskin/cdrskin.c Disabled macro Cdrskin_debug_libdax_msgS. Thus getting unqueued error messages. 2007.05.22.164850 [859] libburn/sg-linux.c Added SCSI opcode to output of revision 857 2007.05.28.132412 [860] libburn/os-linux.h libburn/write.c Moved general 32 kiB buffer restriction from write.c to os-linux.h 2007.05.28.165630 [861] libburn/libburn.h libburn/drive.c test/telltoc.c Extended struct burn_multi_caps by .might_simulate 28 May 2007 [862] libburn/libdax_msgs.h Forgotten update of error list with revison 857 2007.05.28.170243 [863] libburn/options.c cdrskin/cdrskin.1 Added check for .might_simulate to burn_write_opts_auto_write_type() 2007.05.28.192421 [864] libburn/sector.c Fixed low transaction size introduced by cooperation of revisions 855 and 860 ------------------------------------ cycle - cdrskin-0.3.7 - 2007.05.28.192853 * Refusal to perform -dummy runs on media which cannot simulate burning 29 May 2007 [865] cdrskin/cdrskin_eng.html cdrskin/changelog.txt Next cdrskin-0.3.7 cycle 8 Jun 2007 [873] - cdrskin/doener_150x200_tr.gif - cdrskin/doener_150x200_tr_octx.gif + cdrskin/doener_150x200_tr.png + cdrskin/doener_150x200_tr_octx.png cdrskin/cdrskin_eng.html Changed logo graphics format from GIF to PNG 8 Jun 2007 [874] cdrskin/wiki_plain.txt cdrskin/add_ts_changes_to_libburn_0_3_6 cdrskin/add_ts_changes_to_libburn_0_3_7 Took into respect change of logo graphics format 8 Jun 2007 [875] cdrskin/cdrskin.1 Prevented macro interpretation of text snippet ".wav" 10 Jun 2007 [876] cdrskin/README cdrskin/cdrskin.1 Clarified MB to MiB if compatibility allows it (ticket 100) 10 Jun 2007 [877] cdrskin/cdrskin.1 doc/cookbook.txt test/libburner.c Changed "KB" to "kiB" ------------------------------------ cycle - cdrskin-0.3.7 - 2007.06.10.081025 2007.07.12.162856 [882] libburn/transport.h libburn/mmc.c libburn/write.c libburn/libdax_msgs.h Preparations to avoid writing which will not fit in drive buffer 2007.07.12.171727 [883] libburn/libburn.h libburn/drive.c New API-Function burn_drive_set_buffer_waiting() 2007.07.12.171832 [884] cdrskin/cdrskin.c cdrskin/cdrskin.1 New options modesty_on_drive= and minbuf= 2007.07.14.111614 [885] libburn/libburn.h libburn/drive.c New API function burn_drive_get_best_speed() 2007.07.14.112029 [886] libburn/mmc.c Only set realistic maximum DVD speeds (for my LG GSA which fails otherwise) 2007.07.14.112326 [887] cdrskin/cdrskin.c Experimental option --adjust_speed_to_drive. Caution: May vanish soon. 17 Jul 2007 [888] cdrskin/cdrskin.1 Clarification on option speed= 2007.07.17.085823 [889] libburn/libburn.h libburn/drive.c libburn/mmc.c cdrskin/cdrskin.c Implemented minimum speed in burn_drive_set_speed() 2007.07.19.072434 [890] libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 Removed ban against speed 0 with burn_drive_set_buffer_waiting() 2007.07.19.143139 [891] cdrskin/cdrskin.c Trying to prevent usage of burn drive as track source 2007.07.19.171947 [892] cdrskin/cdrskin.c Avoided new track-drive test with option --no_convert_fs_adr 19 Jul 2007 [893] cdrskin/cdrskin.1 Documented option --adjust_speed_to_drive (i.e. it will stay) 19 Jul 2007 [894] cdrskin/changelog.txt Next cdrskin-0.3.7 cycle 19 Jul 2007 [895] cdrskin/cdrskin_eng.html Next cdrskin-0.3.7 cycle ------------------------------------ cycle - cdrskin-0.3.7 - 2007.07.19.174859 * New option modesty_on_drive= may help with hda -> hdb burns * New option minbuf= , cdrecord compatible frontend of modesty_on_drive= * New option --adjust_speed_to_drive * Precautions against using the burner drive as track source 2007.07.20.120001 [branch 897] Makefile.am configure.ac README cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.3.8 20 Jul 2007 [branch 898] - cdrskin/add_ts_changes_to_libburn_0_3_6 - cdrskin/add_ts_changes_to_libburn_0_3_7 + cdrskin/add_ts_changes_to_libburn_0_3_8 + cdrskin/add_ts_changes_to_libburn_0_3_9 Updated cdrskin tarball generators 20 Jul 2007 [branch 899] cdrskin/changelog.txt Documented changes and release timestamp 2007.07.20.210001 [900] Makefile.am configure.ac README cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.3.9 20 Jul 2007 [901] - cdrskin/add_ts_changes_to_libburn_0_3_6 - cdrskin/add_ts_changes_to_libburn_0_3_7 + cdrskin/add_ts_changes_to_libburn_0_3_8 + cdrskin/add_ts_changes_to_libburn_0_3_9 Updated cdrskin tarball generators 20 Jul 2007 [902] cdrskin/changelog.txt Documented changes ----------------------------- release - cdrskin-0.3.8.pl00 - 2007.07.20.120001 * Now able to cope with the peculiarities of Linux 2.4 USB * Refusal to perform -dummy runs on media which cannot simulate burning * New option modesty_on_drive= may help with hda -> hdb burns * New option minbuf= , cdrecord compatible frontend of modesty_on_drive= * New option --adjust_speed_to_drive * Precautions against using the burner drive as track source ------------------------------------ cycle - cdrskin-0.3.9 - 2007.07.20.200422 2 Aug 2007 [917] cdrskin/cdrskin_eng.html Corrected a harmless typo 2 Aug 2007 [918] doc/cookbook.txt cdrskin/cdrskin.1 Took into respect new info from Andy Polyakov about closing DVD+-R 9 Aug 2007 [920] cdrskin/cdrskin_eng.html Corrected a dead link 2007.08.09.133137 [921] cdrskin/cdrskin.c cdrskin/cdrskin.1 Allowed speed=any 2007.08.09.133259 [922] libburn/transport.h Changed "unsigned" to "unsigned int" 2007.08.09.133420 [923] libburn/libdax_msgs.h Corrected a typo 9 Aug 2007 [924] + doc/libdax_model.txt + doc/libdax_overview.gif + doc/libdax_equip.gif + doc/libdax_job.gif Obscure backup of my unripe model ideas about libcevap (former libdax) 10 Aug 2007 [936] doc/libdax_model.txt Fiddled on the model attributes 2007.08.10.201450 [937] libburn/libburn.h Updated comments about supported profiles and media types 2007.08.10.203040 [938] configure.ac Changed "libburn-1.pc" to "libburn-5.pc" to re-enable ./bootstrap ; ./configure 2007.08.11.075330 [941] cdrskin/README cdrskin/add_ts_changes_to_libburn_0_3_9 cdrskin/cdrskin.c cdrskin/cdrskin_eng.html cdrskin/wiki_plain.txt doc/comments doc/cookbook.txt test/libburner.c test/telltoc.c Reflected recent URL changes to libburnia-project.org 2007.08.11.202027 [942] libburn/libburn.h libburn/write.c libburn/libdax_msgs.h New API function burn_random_access_write() 2007.08.11.202627 [943] cdrskin/cdrskin.c cdrskin/cdrskin.1 New option direct_write_amount= using new API call burn_random_access_write() 12 Aug 2007 [944] libburn/libburn.h Clarifications about burn_random_access_write() 2007.08.12.095446 [945] libburn/write.c libburn/libdax_msgs.h Checked in burn_random_access_write() wether drive is grabbed 2007.08.12.095623 [946] cdrskin/cdrskin.c Debug message explaining why burn_drive_convert_fs_adr() acts on track source 2007.08.12.152937 [947] libburn/libburn.h libburn/transport.h libburn/mmc.h libburn/mmc.c libburn/write.c libburn/read.c libburn/libdax_msgs.h New API function burn_read_data() 12 Aug 2007 [948] test/telltoc.c Testing burn_read_data() by option --read_and_print ------------------------------------ cycle - cdrskin-0.3.9 - 2007.08.12.161808 * New option direct_write_amount= 13 Aug 2007 [949] cdrskin/changelog.txt Next cdrskin-0.3.9 cycle 13 Aug 2007 [950] cdrskin/cdrskin.1 cdrskin/cdrskin_eng.html Some polishing about option direct_write_amount= 13 Aug 2007 [951] doc/libdax_model.txt Beautified implementation names and added some more attributes 15 Aug 2007 [952] doc/libdax_model.txt Added more attributes and distinguished read-write, read-only, private ones 2007.08.17.081901 [953] libburn/transport.h Corrected harmless type declaration flaws 17 Aug 2007 [954] doc/libdax_model.txt Added more attributes and comments 19 Aug 2007 [955] + libcevap/ + libcevap/cgen.c + libcevap/cgen.h + libcevap/ctyp.c + libcevap/ctyp.h + libcevap/smem.c + libcevap/smem.h + libcevap/cgen.txt The C code generator mentioned in doc/libdax_model.txt. See there. 19 Aug 2007 [956] doc/libdax_model.txt More comments, new capabilities of C code generator 19 Aug 2007 [957] + libcevap/libcevap_gen.sh + libcevap/extract_cgen_input.sh + libcevap/main.c doc/libdax_model.txt Generator frontend scripts (./libcevap_gen.sh to be run in libcevap/) 19 Aug 2007 [958] doc/libdax_model.txt Corrected description of compiling and generating 20 Aug 2007 [959] libcevap/cgen.c Corrected a bug about inclusion of cevap*.h files 21 Aug 2007 [960] libcevap/smem.h Added some function type declarations 2007.08.22.134731 [961] libburn/mmc.c Corrected dangerous typo with error message production of mmc_read_10() 22 Aug 2007 [962] test/telltoc.c Retrieving my old backups which are hit by the Linux Read-Ahead-Bug 22 Aug 2007 [963] libburn/read.c test/telltoc.c Avoiding libburn read-ahead-bug 22 Aug 2007 [964] test/telltoc.c Avoided locked drive with interrupted telltoc read. (eject unlocks anyway) 2007.08.22.173459 [965] libburn/libburn.h libburn/drive.c libburn/read.c libburn/write.c Taking synchronous read/write into respect with abort handling 2007.08.23.150423 [966] libburn/libburn.h libburn/mmc.c libburn/read.c Allowed to suppress error message from failed burn_read_data() 23 Aug 2007 [967] test/telltoc.c Try to read last 2 blocks of CD track without eventual error message 2007.08.25.085709 [968] libburn/libburn.h libburn/mmc.c libburn/file.c libburn/source.c libburn/structure.c Corrected memory management flaws found by Joris Dobbelsteen 25 Aug 2007 [969] test/telltoc.c Reacted on false compiler warning about potentially unused variable 2007.08.25.155931 [970] libburn/mmc.c libburn/file.c More memory management changes proposed by Joris Dobbelsteen 2007.08.26.200829 [971] cdrskin/cdrskin.c cdrskin/cdrfifo.h cdrskin/cdrfifo.c cdrskin/cdrskin.1 New cdrskin option --grow_overwriteable_iso 2007.08.28.143057 [974] cdrskin/cdrskin.c cdrskin/cdrskin.1 Made program behavior with --grow_overwriteable_iso more consistent 28 Aug 2007 [975] cdrskin/README Mentioned --grow_overwriteable_iso 29 Aug 2007 [976] doc/libdax_model.txt Work goes on 29 Aug 2007 [977] cdrskin/cdrskin_eng.html cdrskin/changelog.txt Next cdrskin-0.3.9 cycle ------------------------------------ cycle - cdrskin-0.3.9 - 2007.08.29.124057 * New option --grow_overwriteable_iso 29 Aug 2007 [978] cdrskin/compile_cdrskin.sh Added missing file to link list: read.o 29 Aug 2007 [979] - doc/libdax_model.txt - doc/libdax_overview.gif - doc/libdax_job.gif - doc/libdax_equip.gif + libcevap/libdax_model.txt + libcevap/libdax_overview.gif + libcevap/libdax_job.gif + libcevap/libdax_equip.gif Moved libdax-libcevap model stuff to libcevap/ 29 Aug 2007 [980] libcevap/libcevap_gen.sh Adapted C code generator script to new address of libdax_model.txt 2007.09.01.182319 [984] libburn/libburn.h libburn/options.c New API function burn_write_opts_get_drive() 2007.09.04.224905 [987] libburn/libburn.h libburn/transport.h libburn/async.c libburn/drive.c libburn/write.c libburn/read.c libburn/options.c libburn/spc.c libburn/libdax_msgs.h New API calls burn_drive_grab_dummy(), burn_drive_get_drive_role() 5 Sep 2007 [988] test/telltoc.c test/libburner.c Testing new API functions via --drive stdio: 2007.09.04.225558 [989] libburn/write.c libburn/read.c Reacted on compiler warnings 2007.09.05.194124 [991] libburn/libburn.h libburn/drive.c test/libburner.c test/telltoc.c burn_drive_grab_dummy() becomes invisible part of burn_drive_scan_and_grab() 2007.09.05.195248 [992] libburn/write.c Fixed a bug with failed opening of pseudo-drive 2007.09.06.094402 [995] libburn/drive.c Added forgotten handling of pseudo-drives in burn_drive_grab() 2007.09.06.095954 [996] libburn/write.c Added forgotten read/write counters in burn_stdio_write_track() 2007.09.06.100100 [997] cdrskin/cdrskin.c Removed obstacles for use of stdio-drives 2007.09.06.120844 [999] libburn/libburn.h libburn/drive.c Promoted burn_drive_raw_get_adr() to API function burn_drive_d_get_adr() 2007.09.07.102728 [1002] libburn/write.c Corrected write counter in burn_stdio_write_track() 2007.09.07.123748 [1003] libburn/libburn.h libburn/async.c libburn/drive.c libburn/libdax_msgs.h Made burn_drive_info_free() only delete the drive of its parameter 7 Sep 2007 [1004] test/telltoc.c Reacted on changed media profile of stdio-drives 2007.09.07.154951 [1005] libburn/drive.h libburn/drive.c libburn/async.c libburn/init.c Made burn_drive_scan() refuse work on non-empty drive list. 2007.09.07.163949 [1006] libburn/async.c libburn/drive.c Corrected memory leak introduced by revision 1005 2007.09.07.164532 [1007] libburn/drive.h Forgotten file for revision 1006 2007.09.07.184631 [1008] libburn/init.c libburn/libdax_msgs.h Avoided locked tray after failed burn_finish() because of busy drive 7 Sep 2007 [1009] test/telltoc.c Lowered report severity to LIBDAX_MSGS_SEV_WARNING. 2007.09.07.190916 [1010] libburn/libburn.h libburn/drive.c libburn/libdax_msgs.h Made burn_drive_scan_and_grab() extend the drive list rather than replacing it 2007.09.07.234122 [1011] libburn/drive.c cdrskin/cdrskin.c Report media profile in cdrskin blank, format, burn runs 2007.09.07.234704 [1012] libburn/drive.c Bug fix about stdio: 2007.09.08.102151 [1013] libburn/drive.c Fixed memory leak and possible SIGSEGV with pseudo-drives 2007.09.08.102620 [1014] cdrskin/cdrskin.c Made cdrskin work with null-drive (which it mistook for something like ATA:) 2007.09.08.132058 [1015] libburn/drive.c libburn/write.c Allowed -dummy burns with stdio-drives (because /dev/null is no block device) 2007.09.08.132206 [1016] cdrskin/cdrskin.c Changed speed measurement of stdio-drives to DVD 1x units 8 Sep 2007 [1017] libburn/libburn.h Documentation of stdio-drives 2007.09.08.164924 [1018] cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/convert_man_to_html.sh New option --allow_emulated_drives 2007.09.08.174757 [1019] libburn/libburn.h libburn/async.c libburn/write.c Made Libburn_precheck_write_ruleS unconditional code 8 Sep 2007 [1020] cdrskin/cdrskin_eng.html cdrskin/changelog.txt Next cdrskin-0.3.9 cycle ------------------------------------ cycle - cdrskin-0.3.9 - 2007.09.08.212130 * New option --allow_emulated_drives dev=stdio: 2007.09.09.093535 [1021] libburn/drive.c libburn/libdax_msgs.h cdrskin/cdrskin.1 Called statvfs() for size estimation of regular stdio-files. 2007.09.09.093637 [1022] cdrskin/cdrskin.c Fixed bug with dev=stdio: where path contains a digit 2007.09.09.133136 [1023] libburn/init.c libburn/sg.h libburn/sg-linux.c libburn/sg-freebsd-port.c libburn/sg-freebsd.c Enabled os dependend stdio size estimation 2007.09.09.133922 [1024] libburn/drive.c Made use of os dependend stdio size estimation 2007.09.09.182827 [1026] libburn/async.c libburn/mmc.c Ended falsely alleged erasability of DVD-RAM and DVD+RW 2007.09.10.110050 [1028] libburn/drive.c Added forgotten return 0 to an error case 2007.09.12.104626 [1032] libburn/transport.h libburn/libdax_msgs.h libburn/write.c libburn/drive.c libburn/spc.c libburn/sbc.c libburn/mmc.c libburn/sg-linux.c libburn/sg-freebsd.c Brought burn_stdio_write_track() onto sector_data() for outmost realism 2007.09.12.115850 [1033] libburn/mmc.c libburn/spc.c Reacted on compiler -O2 warnings 2007.09.12.115946 [1034] libburn/write.c Implemented realistic speed simulation with stdio-drives 2007.09.12.195206 [1039] libburn/write.c Implemented cache syncing for stdio-drives in burn_random_access_write() 2007.09.12.200106 [1040] cdrskin/cdrskin.c Fixed bug with direct_write_amount=0 2007.09.14.122437 [1063] libburn/write.c Took into respect time granularity with stdio speed control 2007.09.14.122531 [1064] libburn/libburn.h Documented burn_write_opts_set_multi @param opts 14 Sep 2007 [1065] src/burn_wrap.c Changed include form of libisofs.h and libburn.h 2007.09.15.112311 [1066] libburn/libdax_msgs.h libburn/libdax_msgs.c Imported Range "vreixo" into libburn/libdax_msgs.h 2007.09.15.171844 [1067] libburn/libdax_msgs.h Prepared for neat sed translation. Explained concept of libdax_msgs variants. 15 Sep 2007 [1068] libburn/libdax_msgs_to_xyz_msgs.sh A sed converter which creates libiso_msgs.[ch] from libdax_msgs.[ch] 2007.09.15.172141 [1069] libburn/libburn.h libburn/init.c New API function burn_set_messenger() 2007.09.15.204320 [1070] libburn/libburn.h libburn/libdax_msgs.h libburn/libdax_msgs.c libburn/init.c Equipped libdax_msgs with reference counter. Completed its mutex protection. ------------------------------------ cycle - cdrskin-0.3.9 - 2007.09.15.205752 16 Sep 2007 [1071] cdrskin/changelog.txt Next cdrskin-0.3.9 cycle 2007.09.17.163735 [1072] cdrskin/cdrskin.c cdrskin/cdrskin.1 Implemented emulation for cdrecord options -inq , -format , -load 2007.09.18.072234 [1073] cdrskin/cdrskin.c cdrskin/cdrskin.1 Learned helptexts for -inq, -format, -load from cdrecord (they are wrong, btw) 2007.09.18.090713 [1074] libburn/libburn.h libburn/drive.c New API function burn_drive_leave_locked() 2007.09.18.090839 [1075] cdrskin/cdrskin.c cdrskin/cdrskin.1 Implemented emulation for cdrecord option -lock 2007.09.18.130344 [1076] libburn/mmc.c Made use of Immed bit with 5Bh CLOSE TRACK/SESSION 2007.09.18.200343 [1077] libburn/mmc.c libburn/spc.h libburn/spc.c libburn/sbc.c libburn/libdax_msgs.h Made use of Immed bit with 1Bh START STOP UNIT and 35h SYNCHRONIZE CACHE 2007.09.18.200454 [1078] cdrskin/cdrskin.c Corrected an outdated HINT text 2007.09.18.201556 [1079] libburn/sbc.c libburn/spc.c Changed some comments, reacted on harmless compiler warning 2007.09.18.204043 [1080] cdrskin/cdrskin.c cdrskin/cdrskin.1 Implemented emulation for cdrecord option -immed 2007.09.19.094046 [1081] cdrskin/cdrskin.c Implemented emulation for cdrecord option -waiti 19 Sep 2007 [1082] cdrskin/cdrskin.1 Did a little overhaul of general paragraphs, mentioned new option -waiti ------------------------------------ cycle - cdrskin-0.3.9 - 2007.09.19.112330 * More cdrecord options supported: -format, -inq, -load, -lock, -immed, -waiti 19 Sep 2007 [1083] cdrskin/changelog.txt cdrskin/cdrskin_eng.html Next cdrskin-0.3.9 cycle 2007.09.19.141650 [1084] cdrskin/cdrskin.c Made cdrskin/compile_cdrskin.sh -do_diet work again 19 Sep 2007 [1085] cdrskin/cdrskin.1 Made minor corrections 2007.09.19.212659 [1086] cdrskin/cdrskin.c cdrskin/cdrskin.1 New option fallback_program= 2007.09.20.125854 [1087] cdrskin/cdrskin.c cdrskin/cdrskin.1 Triggered fallback by unsuitable media, made -version report fallback program 2007.09.21.120333 [1088] libburn/sbc.c libburn/spc.c Had to revoke Immed bit on load command. LG GSA-4082B : premature "no media" 2007.09.22.140613 [1096] cdrskin/cdrskin.c Updated list of unsupported cdrecord and wodim options 2007.09.22.151712 [1097] libburn/libburn.h libburn/init.c New API function burn_msgs_submit() 2007.09.22.151712 [1101] libburn/sbc.c Reacted on compiler warning 2007.09.23.163301 [1107] libburn/libburn.h libburn/drive.c New API function burn_drive_equals_adr() 2007.09.23.163419 [1108] cdrskin/cdrskin.c Made use of burn_drive_equals_adr() 2007.09.23.163529 [1109] libburn/sbc.c Updated a comment about Immed and a debug message with tray loading 2007.09.24.062354 [1110] libburn/drive.c Added forgotten handling of "sdtio:" with burn_drive_equals_adr() 2007.09.24.135440 [1113] libburn/libburn.h libburn/transport.h libburn/drive.h libburn/drive.c libburn/read.c libburn/write.c Implemented drive role 3, sequential write-only stdio drives (e.g. stdout) 2007.09.24.135845 [1114] cdrskin/cdrskin.c cdrskin/cdrskin.1 Took into respect new drive role 3 2007.09.24.181340 [1115] libburn/sg-linux.c libburn/drive.c Made stdio-drives work on readonly CD block devices 24 Sep 2007 [1116] test/libburner.c Blocked against file descriptor drives. Too dangerous for a demo. 2007.09.26.155301 [1123] cdrskin/cdrskin.c cdrskin/cdrskin.1 Disabled --allow_emulated_drives in setuid runs 26 Sep 2007 [1124] cdrskin/README Streamlined and moved legal stuff to end of text 2007.09.26.173840 [1125] cdrskin/compile_cdrskin.sh Made -O2 default if not -g is given 2007.09.27.083351 [1126] cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/convert_man_to_html.sh cdrskin/README Disallowed emulated drives for superuser, allowed stdio:/dev/null for all 2007.09.27.093129 [1127] cdrskin/cdrskin.c Corrected announcement with dev=help about stdio: "Open via UNIX device" ------------------------------------ cycle - cdrskin-0.3.9 - 2007.09.27.093301 * New option fallback_program= 27 Sep 2007 [1128] cdrskin/changelog.txt Next cdrskin-0.3.9 cycle 28 Sep 2007 [1129] libisofs libisofs/libiso_msgs.h Removed apostrophes which my compiler does not like 2007.09.29.185007 [1131] libburn/transport.h libburn/init.h libburn/init.c libburn/drive.h libburn/drive.c libburn/async.c Trying to catch signals from within the writer thread 2007.09.29.191558 [1132] libburn/init.c Added forgotten handling of non-writer-non-control threads 2007.09.30.212517 [1135] libburn/libburn.h libburn/async.h libburn/async.c libburn/file.h libburn/file.c Implemented a simple fifo to decouple from burn_source signals 30 Sep 2007 [1136] test/telltoc.c Disallowed --read_and_print raw:- , allowed to write to chardev+pipe+socket 2007.10.02.120659 [1145] libburn/write.c Moved minimum tracksize padding out of TAO track closing. Now done before sync. 2007.10.02.135538 [1146] libburn/libburn.h Clarified role of burn_source 2007.10.02.180003 [1148] libburn/write.c Corrected error with revision 1145 2007.10.03.084206 [1150] libburn/libburn.h More documentation for burn_source 2007.10.03.112547 [1151] libburn/transport.h libburn/mmc.c libburn/write.c libburn/drive.c Ensured synchronize cache before release 2007.10.03.115550 [1153] libburn/libburn.h More documentation for burn_source 2007.10.03.223649 [1155] libburn/file.h libburn/file.c libburn/async.c Implemented the ring buffer of burn_fifo_source_new() object 2007.10.03.223851 [1156] libburn/libburn.h More documentation for burn_source, new API call burn_fifo_inquire_status() 3 Oct 2007 [1157] test/libburner.c Made use of 4 MB fifo 2007.10.04.200221 [1158] libburn/libburn.h libburn/file.h libburn/file.c libburn/libdax_msgs.h Inserted the necessary error messages and magic numbers 4 Oct 2007 [1159] test/libburner.c Adjusted pacifier messages and change with burn_fifo_inquire_status() 2007.10.04.210245 [1160] libburn/libburn.h Minor adjustments with comment text 2007.10.04.213107 [1161] libburn/drive.c Corrected abort preventing bug introduced with revision 1131 2007.10.05.085929 [1162] libburn/libburn.h libburn/file.c Revoked urge to have a magic[4] in burn_source (free_data is magic enough) 2007.10.05.231712 [1163] libburn/file.c Fixed data spoiling bug with ring buffer introduced with rev 1155 2007.10.07.110506 [1164] libburn/file.c Corrected status reply for unstarted fifo 2007.10.07.110644 [1165] libburn/file.c Corrected status reply for unstarted fifo (2nd try) 7 Oct 2007 [1166] test/libburner.c Minor changes with waiting for drive and fifo status display 2007.10.15.115448 [1179] cdrskin/cdrskin.c Corrected 4-byte buffer overflow (which did no detectable harm) 2007.10.15.115728 [1180] libburn/read.c Made possible to silence error message about missing pseudo drive 2007.10.15.115851 [1181] libburn/drive.c Corrected SIGSEGV with changing from one drive to the other 2007.10.15.144050 [1182] libburn/drive.c Activated re-usal of disposed global drive_array slots 2007.10.16.212205 [1189] libburn/libburn.h libburn/init.c New API function burn_text_to_sev() 18 Oct 2007 [1209] test/telltoc.c Calmed down hyperactive sleep interval with drive scanning 2007.10.18.200336 [1210] libburn/init.c Prevented SIGSEGV with burn_msgs_obtain() on non-initialized library 2007.10.19.115326 [1215] libburn/async.c libburn/libdax_msgs.h Starting threads detached, providing two alternatives. But zombies still there. 2007.10.19.132821 [1216] libburn/init.c Small change with debug verbosity of abort handler 2007.10.19.133310 [1217] libburn/async.c Removed useless alternative after zombies turned out to be caused by gdb ------------------------------------ cycle - cdrskin-0.3.9 - 2007.10.23.150436 2007.10.23.150436 [1240] cdrskin/changelog.txt Next cdrskin-0.3.9 cycle 23 Oct 2007 [1241] libcevap/main.c Preparations for lowercase class and function names 23 Oct 2007 [1242] libcevap/libdax_model.txt Work goes on 23 Oct 2007 [1243] libcevap/cgen.c libcevap/ctyp.c Fixed a bug about arrays 2007.10.24.184233 [1248] branch/ZeroFourZero Makefile.am configure.ac README cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/wiki_plain.txt cdrskin/cdrskin_eng.html doc/comments Made number transition to 0.4.0 25 Oct 2007 [1249] branch/ZeroFourZero - cdrskin/add_ts_changes_to_libburn_0_3_8 - cdrskin/add_ts_changes_to_libburn_0_3_9 + cdrskin/add_ts_changes_to_libburn_0_4_0 + cdrskin/add_ts_changes_to_libburn_0_4_1 Updated cdrskin tarball generator 2007.10.25.091106 [1250] Makefile.am configure.ac README cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/wiki_plain.txt cdrskin/cdrskin_eng.html doc/comments Made number transition to 0.4.1 25 Oct 2007 [1251] - cdrskin/add_ts_changes_to_libburn_0_3_8 - cdrskin/add_ts_changes_to_libburn_0_3_9 + cdrskin/add_ts_changes_to_libburn_0_4_0 + cdrskin/add_ts_changes_to_libburn_0_4_1 Updated cdrskin tarball generator 25 Oct 2007 [1252] branch/ZeroFourZero cdrskin/cdrskin.c Added forgotten help text lines 2007.10.25.131841 [1253] cdrskin/cdrskin.c Added forgotten help text lines 2007.10.27.090421 [1254] [1256] branch/ZeroFourZero libburn/sg-linux.c Reacted on cdwrite@ message about INT_MAX in cdrom.h of kernel 2.6.23 2007.10.27.075309 [1255] libburn/sg-linux.c Reacted on cdwrite@ message about INT_MAX in cdrom.h of kernel 2.6.23 29 Oct 2007 [1272] - cdrskin/add_ts_changes_to_libburn_0_3_9 + cdrskin/add_ts_changes_to_libburn_0_4_0 Updated cdrskin release generator scripts ----------------------------- release - cdrskin-0.4.0.pl00 - 2007.10.27.090421 * New option direct_write_amount= * New option --grow_overwriteable_iso * New option --allow_emulated_drives dev=stdio: * More cdrecord options supported: -format, -inq, -load, -lock, -immed, -waiti * New option fallback_program= ------------------------------------ cycle - cdrskin-0.4.1 - 2007.10.27.114207 2007.11.18.093952 [1307] libburn/libburn.h Marked loss of binary backward compatibility back in rev 655, libburn-0.3.1 18 Nov 2007 [1308] cdrskin/cdrskin.1 Corrected a typo in cdrskin man page 2007.11.18.094209 [1309] cdrskin/cdrskin.c Reacted on build warnings on a 64 Bit system 2007.11.26.154817 [1310] libburn/libdax_audioxtr.c Reacted on build warnings on another system ------------------------------------ cycle - cdrskin-0.4.1 - 2007.11.27.214003 2007.11.29.185342 [1312] libburn/drive.c libburn/mmc.c libburn/spc.c libburn/libdax_msgs.h Enabled reading of TOC from ROM drives (direly needed for xorriso) 29 Nov 2007 [1313] test/telltoc.c Adjusted meaning of --read_and_print count= -1 2007.12.07.185030 [1323] configure.ac An attempt to rectify .so numbering: SONAME=10, REV=1, AGE=6 2007.12.07.185206 [1324] libburn/async.c Made postponed change in thread management 8 Dec 2007 [1325] configure.ac Some changes in the comments 24 Dec 2007 [1338] libburn/libburn.h libburn/source.h libburn/source.c libburn/file.c libburn/write.c libburn/sector.c Implemented burn_source.cancel() in a binary backwards compatible way 2008.01.17.185051 [1383] libburn/libdax_msgs.h libburn/libdax_msgs.c Changed meaning of .driveno to .origin, introduced LIBDAX_MSGS_ORIGIN_* macros 17 Jan 2008 [1384] cdrskin/README Removed a reference to future GPL versions 2008.01.19.201702 [1396] libburn/read.c Fixed small bug about error messages with burn_read_data 2008.01.23.193345 [1405] libburn/read.c Made burn_read_data() issue messages about hopeless drive access errors 2008.01.23.193843 [1406] libburn/libburn.h libburn/libdax_msgs.h libburn/libdax_msgs.c Introduced message severity "FAILURE" 2008.01.23.211731 [1408] cdrskin/cdrskin.c configure.ac Implemented run time check of libburn version. 2008.01.23.213607 [1409] Makefile.am Dynamic cdrskin linking patch by Simon Huggins. 26 Jan 2008 [1420] cdrskin/convert_man_to_html.sh Adapted to man -H on my new system 2008.01.26.123054 [1421] libburn/libdax_msgs.h Ported change in vreixo message range from isoburn_msgs 2008.01.26.131519 [1422] libburn/drive.c Made a sudden end to all stdio drives in burn_abort() 2008.01.26.180241 [1426] [branch 1427] libburn/async.c Disabled debugging messages about thread properties 2008.01.26.200001 [branch 1428] Makefile.am configure.ac libburn-5.pc.in README cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/wiki_plain.txt cdrskin/cdrskin_eng.html Made number transition to 0.4.2 , libburn.so.4.7.0 27 Jan [branch 1429] cdrskin/cdrskin.c cdrskin/cdrskin_eng.html cdrskin/changelog.txt Adjustments after first round of testing 27 Jan [branch 1430] cdrskin/README cdrskin/cdrskin_eng.html Adjustments after testing 2008.01.27.143022 [1431] Makefile.am configure.ac libburn-5.pc.in README cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/wiki_plain.txt cdrskin/cdrskin_eng.html cdrskin/changelog.txt Made number transition to 0.4.3 , still libburn.so.4.7.0 27 Jan 2008 [branch 1433] [1432] - cdrskin/add_ts_changes_to_libburn_0_4_0 - cdrskin/add_ts_changes_to_libburn_0_4_1 + cdrskin/add_ts_changes_to_libburn_0_4_2 + cdrskin/add_ts_changes_to_libburn_0_4_3 Updated cdrskin tarball generator 2008.01.29.210821 [1442] configure.ac libburn/libburn.h Moving the major.minor.micro definition from configure.ac to libburn.h 2008.01.28.213001 [branch 1444] libburn/libburn.h Introduced copy of major.minor.micro definition in libburn.h of version 0.4.2 29 Jan 2008 [branch 1445] [branch 1447] Corrected description of major.minor.micro definition in libburn.h of version 0.4.2 2008.01.29.214110 [1446] libburn/libburn.h Corrected description of major.minor.micro definition in libburn.h 2008.01.31.111057 [1448] cdrskin/cdrskin.c Introduced compile time check of libburn header version 2008.02.01.100302 [1451] [branch 1453] - libburn-5.pc.in + libburn-1.pc.in configure.ac Makefile.am Renamed libburn-5.pc to libburn-1.pc 2008.02.01.100530 [1452] cdrskin/cdrskin.c Changed "libburn interface :" version message to libburn.h macros [branch ] cdrskin/cdrskin_timestamp.h cdrskin/changelog.txt Updated changelog before release ----------------------------- release - cdrskin-0.4.2.pl00 - 2008.02.01.100001 * Safe dynamic linking possible with libburn.so.4 ------------------------------------ cycle - cdrskin-0.4.3 - 2008.02.01.225039 2008.02.03.092013 [1463] libburn/libdax_msgs.h Registered error code range "libisofs-xorriso" 2008.02.03.092134 [1464] configure.ac Incremented LT_* to get libburn.so.4.8.0 (forgot to do 4.7.1 anyway) 2008.02.03.092509 [1465] libburn/libburn.h libburn/init.c New API call burn_sev_to_text() 4 Feb 2008 [1473] README Announced deprecation of libisofs-0.2.x, temporory employment of libisofs-0.6.1 2008.02.04.175209 [1474] libburn/libdax_msgs.h Registered range "libisoburn" 0x00060000 to 0x00006ffff 2008.02.06.174009 [1482] libburn/libdax_msgs.h Re-instated range "vreixo" with old and new codes, adjusted severity definitions 2008.02.06.182222 [1483] libburn/libburn.h Copied usage discussion about *_header_version_* from libisoburn 2008.02.06.230041 [1488] libburn/read.c Installed a simple address logger in burn_read_data 2008.02.07.232820 [1492] libburn/read.c Added debug message in case of burn_read_data() return 0 2008.02.08.073414 [1493] libburn/mmc.c Allowed DVD +/- DL for reading 2008.02.11.190802 [1518] libburn/libdax_msgs.h libburn/libdax_msgs.c Introduced LIBDAX_MSGS_SEV_MISHAP 2008.02.14.074108 [1522] libburn/libburn.h Micro corrections in comment text 14 Feb 2008 [1523] COPYRIGHT Updated year 14 Feb 2008 [1524] cdrskin/README Pointed to ldconfig 2008.02.16.121102 [1541] configure.ac Corrected typo in comment 16 Feb 2008 [1542] doc/comments README cdrskin/wiki_plain.txt Updated project interrelations 2008.02.21.200956 [1554] libburn/sg-linux.c Testwise inquiry of ioctl(CDROM_MEDIA_CHANGED) (disabled by default) 21 Feb 2008 [1555] test/libburner.c Directed error message to proper output channel 2008.02.21.201238 [1556] libburn/libdax_msgs.h libburn/libdax_msgs.c Introduced severity ERRFILE 2008.02.21.202216 [1557] libburn/libdax_msgs.c Mapped unknown severity text to ALL rather than NEVER 2008.02.21.215250 [1560] libburn/libdax_msgs.h Weakened demand to print file path in following message 2008.02.22.150939 [1562] libburn/libdax_msgs.h libburn/init.c New error code 0x00040008 2008.02.22.213527 [1563] libburn/libdax_msgs.h libburn/libdax_msgs.c Imported changes from libiso_msgs.h 2008.02.22.213726 [1564] libburn/init.c Changed meaning of unrecognized severity texts from FATAL to ALL 2008.02.28.123152 [1565] libburn/libburn.h libburn/spc.c libburn/mmc.c libburn/write.c cdrskin/cdrskin.1 Made support for DVD+R DL official, enabled untested support for BD-RE 2008.02.28.132325 [1586] libburn/mmc.c cdrskin/README cdrskin/cdrskin.c cdrskin/cdrskin_eng.html cdrskin/wiki_plain.txt test/libburner.c Mentioned support for DVD+R/DL. ------------------------------------ cycle - cdrskin-0.4.3 - 2008.03.01.154319 * Support for DVD+R/DL media is now official * Experimental code for BD-RE with --allow_untested_media 1 Mar 2008 [1595] cdrskin/changelog.txt Updated changelog 2008.03.03.202930 [1598] libburn/libburn.h libburn/drive.c libburn/read.c Got rid of a cumbersome open-close cycle with burn_read_data() on stdio: 2 Apr 2008 [1654] COPYRIGHT Corrected GPL version ------------------------------------ cycle - cdrskin-0.4.3 - 2008.04.07.152331 * libburn: Improved read performance from stdio: pseudo-drives 2008.04.08.100001 [ZeroFourFour 1669] Makefile.am configure.ac README libburn/libburn.h (burn_header_version_*) cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/wiki_plain.txt cdrskin/cdrskin_eng.html doc/comments Made number transition and activated development documentation 8 Apr 2008 [ZeroFourFour 1670] - cdrskin/add_ts_changes_to_libburn_0_4_2 - cdrskin/add_ts_changes_to_libburn_0_4_3 + cdrskin/add_ts_changes_to_libburn_0_4_4 + cdrskin/add_ts_changes_to_libburn_0_4_5 Updated cdrskin tarball generator 8 Apr 2008 [ZeroFourFour 1671] cdrskin/changelog.txt Documented changes and release timestamp 2008.04.08.100001 [ZeroFourFour 1672] libburn/util.c Switched from configure.ac versioning to libburn.h versioning 8 Apr 2008 [ZeroFourFour 1673] cdrskin/changelog.txt Documented last minute changes ----------------------------- release - cdrskin-0.4.4.pl00 - 2008.04.08.100001 * Support for DVD+R/DL media is now official * Experimental code for BD-RE with --allow_untested_media * libburn: Improved read performance from stdio: pseudo-drives 2008.04.08.132344 [1674] Makefile.am configure.ac README libburn/libburn.h (burn_header_version_*) cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/wiki_plain.txt cdrskin/cdrskin_eng.html doc/comments cdrskin/changelog.txt Made number transition 8 Apr 2008 [1675] - cdrskin/add_ts_changes_to_libburn_0_4_2 - cdrskin/add_ts_changes_to_libburn_0_4_3 + cdrskin/add_ts_changes_to_libburn_0_4_4 + cdrskin/add_ts_changes_to_libburn_0_4_5 Updated cdrskin tarball generator 2008.04.08.133452 [1676] libburn/util.c Switched from configure.ac versioning to libburn.h versioning ------------------------------------ cycle - cdrskin-0.4.5 - 2008.04.08.134413 2008.04.10.211529 [1681] libburn/mmc.c libburn/drive.c cdrskin/cdrskin.c Trying to fix bugs about BD-RE, macro for simulating BD-RE on DVD-RAM ------------------------------------ cycle - cdrskin-0.4.5 - 2008.04.10.211529 2008.04.12.164244 [1683] libburn/libburn.h libburn/transport.h libburn/options.h libburn/options.c libburn/write.c libburn/drive.c libburn/mmc.c New API call burn_write_opts_set_stream_recording() 2008.04.12.164606 [1684] cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/cdrskin_eng.html New option stream_recording=on|off ------------------------------------ cycle - cdrskin-0.4.5 - 2008.04.12.164930 * New option stream_recording=on can speed up DVD-RAM 2008.04.15.094133 [1685] libburn/os-linux.h libburn/write.c Enforced tail padding with stream_recording, enlarged transport buffer 15 Apr 2008 [1686] cdrskin/cdrskin.1 Some adjustments of cdrskin man page ------------------------------------ cycle - cdrskin-0.4.5 - 2008.04.15.094545 2008.04.16.082208 [1687] libburn/read.c Made burn_read_data() obey its flag bit1 2008.04.18.092715 [1688] libburn/libburn.h libburn/async.c libburn/drive.c libburn/mmc.c Began to implement formatting of DVD-RAM and experimentally of BD-RE 2008.04.18.092816 [1689] cdrskin/cdrskin.c cdrskin/cdrskin.1 Began to implement formatting of DVD-RAM and experimentally of BD-RE 2008.04.18.194602 [1691] libburn/mmc.c Adjustments with DVD-RAM formatting 2008.04.22.161139 [1695] libburn/libburn.h libburn/transport.h libburn/mmc.c libburn/drive.c cdrskin/cdrskin.c cdrskin/cdrskin.1 Adjustments with DVD-RAM formatting 2008.04.22.200949 [1696] cdrskin/cdrskin.c cdrskin/cdrskin.1 New option --list_formats 2008.04.23.110116 [1697] libburn/mmc.c cdrskin/cdrskin.c cdrskin/cdrskin.1 New blank type blank=format_by_index_ 24 Apr 2008 [1698] doc/cookbook.txt Wrote down what was learned about DVD-RAM formatting 24 Apr 2008 [1699] cdrskin/cdrskin_eng.html Mentioned new features 2008.04.25.131531 [1700] libburn/mmc.c Preparations for formatting BD-RE 2008.04.25.132353 [1701] cdrskin/cdrskin.c cdrskin/cdrskin.1 Documented experimental support for BD-RE formatting 25 Apr 2008 [1702] configure.ac Incremented LT_CURRENT and LT_AGE to reflect API enhancements 25 Apr 2008 [1703] cdrskin/cdrskin_eng.html Documented newest enhancements ------------------------------------ cycle - cdrskin-0.4.5 - 2008.04.25.134438 * New blank type blank=format_defectmgt for DVD-RAM * New option --list_formats * New blank type blank=format_by_index_ * Experimental support for formatting BD-RE 2008.04.26.150646 [1704] libburn/libburn.h libburn/mmc.c libburn/drive.c doc/cookbook.txt Enabled quick formatting with DVD-RAM, made slow formatting default with BD-RE 2008.04.26.150945 [1705] cdrskin/cdrskin.c cdrskin/cdrskin.1 New blank subtypes format_defectmgt_cert_[on|off], on is default 2008.04.27.084704 [1706] cdrskin/cdrskin.c cdrskin/cdrskin.1 New blank type format_if_needed 2008.04.27.140144 [1707] cdrskin/cdrskin.c cdrskin/cdrskin.1 New blank type as_needed 27 Apr 2008 [1708] cdrskin/cdrskin_eng.html Documented newest enhancements ------------------------------------ cycle - cdrskin-0.4.5 - 2008.04.27.163625 * New blank type blank=as_needed for automatic handling of media type and state 3 May 2008 [1723] README Updated project history 2008.05.06.082429 [1729] libburn/drive.c Avoided to report negative burn_multi_caps.start_range_high with DVD-RW 2008.05.06.084156 [1730] libburn/mmc.c Mapped undefined size to 0 with burn_disc_get_formats() and DVD-RW 2008.05.06.180813 [1733] libburn/libburn.h libburn/drive.c libburn/mmc.c doc/cookbook.txt doc/comments test/libburner.c Declared BD-RE to be supported 2008.05.06.181100 [1734] cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/wiki_plain.txt Declared BD-RE to be supported 2008.05.06.181723 [1735] libburn/mmc.c Declared BD-RE to be supported 6 May 2008 [1736] cdrskin/cdrskin_eng.html cdrskin/cdrskin.1 Declared BD-RE to be supported ------------------------------------ cycle - cdrskin-0.4.5 - 2008.05.06.183436 * Support for BD-RE media is now official 2008.05.09.143130 [1748] libburn/libburn.h Documented read-only profiles CD-ROM and DVD-ROM 2008.05.09.145205 [1749] libburn/mmc.c Allowed BD-ROM and BD-R for read-only purposes 9 May 2008 [1750] cdrskin/cdrskin.1 Changed blank examples to blank=as_needed ------------------------------------ cycle - cdrskin-0.4.5 - 2008.05.09.151327 2008.05.10.080001 [1754] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/wiki_plain.txt cdrskin/cdrskin_eng.html doc/comments cdrskin/cdrskin.1 Made number transition to 0.4.6 and activated development documentation 10 May 2008 [1755] cdrskin/README Made number transition to 0.4.6 and activated development documentation 10 May 2008 [1756] - cdrskin/add_ts_changes_to_libburn_0_4_4 - cdrskin/add_ts_changes_to_libburn_0_4_5 + cdrskin/add_ts_changes_to_libburn_0_4_6 + cdrskin/add_ts_changes_to_libburn_0_4_7 Updated cdrskin tarball generator 10 May 2008 [1757] cdrskin/changelog.txt Documented changes and release timestamp ----------------------------- release - cdrskin-0.4.6.pl00 - 2008.05.10.080001 * Support for BD-RE media is now official * New option stream_recording=on can speed up DVD-RAM and BD-RE * New option --list_formats * New blank types for expert formatting of DVD-RAM and BD-RE * New blank type blank=as_needed for automatic handling of media type and state 2008.05.10.132543 [1758] Makefile.am configure.ac README libburn/libburn.h (burn_header_version_*) cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/wiki_plain.txt cdrskin/cdrskin_eng.html doc/comments cdrskin/cdrskin.1 Made number transition to 0.4.7 10 May 2008 [1759] - cdrskin/add_ts_changes_to_libburn_0_4_4 - cdrskin/add_ts_changes_to_libburn_0_4_5 + cdrskin/add_ts_changes_to_libburn_0_4_6 + cdrskin/add_ts_changes_to_libburn_0_4_7 Updated cdrskin tarball generator 10 May 2008 [1760] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-0.4.7 - 2008.05.10.132543 2008.05.14.165025 [1776] libburn/write.c Bug fix: random access addressing for DVD-RAM and BD-RE did not work 2008.05.14.165157 [1777] libburn/libburn.h Added format types 0x30 and 0x32 to list in API comments 2008.05.14.165258 [1778] cdrskin/cdrskin.c Made inability to get format list a reason to abort the program ------------------------------------ cycle - cdrskin-0.4.7 - 2008.05.14.165258 * Bug fix: random access addressing for DVD-RAM and BD-RE did not work 2008.05.17.080001 [1788] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html cdrskin/cdrskin.1 Made number transition to 0.4.8 and activated development documentation 17 May 2008 [1789] - cdrskin/add_ts_changes_to_libburn_0_4_6 - cdrskin/add_ts_changes_to_libburn_0_4_7 + cdrskin/add_ts_changes_to_libburn_0_4_8 + cdrskin/add_ts_changes_to_libburn_0_4_9 Updated cdrskin tarball generator 17 May 2008 [1792] cdrskin/changelog.txt Documented changes and release timestamp ----------------------------- release - cdrskin-0.4.8.pl00 - 2008.05.17.080001 * Bug fix: random access addressing for DVD-RAM and BD-RE did not work 2008.05.17.115434 [1790] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html cdrskin/cdrskin.1 Made number transition to 0.4.9 17 May 2008 [1791] - cdrskin/add_ts_changes_to_libburn_0_4_6 - cdrskin/add_ts_changes_to_libburn_0_4_7 + cdrskin/add_ts_changes_to_libburn_0_4_8 + cdrskin/add_ts_changes_to_libburn_0_4_9 Updated cdrskin tarball generator 17 May 2008 [1793] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-0.4.9 - 2008.05.17.121250 14 Jun 2008 [1852] README Updated release history 2008.06.14.140711 [1853] libburn/libburn.h Inserted @since tags for all functions older than 0.2.0 2008.07.02.093933 [1882] libburn/sg-linux.c With auto device family: scd is now fallback if sr does not exist ------------------------------------ cycle - cdrskin-0.4.9 - 2008.07.12.164045 * Ability to use /dev/scd as fallback if /dev/sr does not exist * Bug fix: option drive_scsi_dev_family=scd lead to buffer overflow 2008.07.14.112935 [1914] libburn/sg-linux.c libburn/libdax_msgs.h Trying to avoid SORRY messages when hitting busy hard disk /dev/hdX 2008.07.14.113050 [1915] cdrskin/cdrskin.c Making visible the new NOTE and HINT about busy alleged hard disks 2008.07.14.113152 [1916] configure.ac Did LT_CURRENT++, LT_AGE++ because of new API call 2008.07.14.113903 [1917] libburn/libburn.h libburn/file.c libburn/async.c New API call burn_fifo_peek_data() 2008.07.14.164528 [1922] libburn/sg-linux.c Followed hint of Giulio Orsero to recognize disk by /proc/ide/hdX/media ------------------------------------ cycle - cdrskin-0.4.9 - 2008.07.14.164906 * New API call burn_fifo_peek_data() 2008.07.16.070001 [1927] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.5.0 and activated development documentation 16 Jul 2008 [1928] - cdrskin/add_ts_changes_to_libburn_0_4_8 - cdrskin/add_ts_changes_to_libburn_0_4_9 + cdrskin/add_ts_changes_to_libburn_0_5_0 + cdrskin/add_ts_changes_to_libburn_0_5_1 Updated cdrskin tarball generator 16 Jul 2008 [1929] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.5.0.pl00 - 2008.07.16.070001 * Ability to use /dev/scd as fallback if /dev/sr does not exist * Bug fix: option drive_scsi_dev_family=scd lead to buffer overflow * New API call burn_fifo_peek_data() 2008.07.16.090816 [1930] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.5.1 and activated development documentation 16 Jul 2008 [1931] - cdrskin/add_ts_changes_to_libburn_0_4_8 - cdrskin/add_ts_changes_to_libburn_0_4_9 + cdrskin/add_ts_changes_to_libburn_0_5_0 + cdrskin/add_ts_changes_to_libburn_0_5_1 Updated cdrskin tarball generator 16 Jul 2008 [1932] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-0.5.1 - 2008.07.14.164528 2008.08.01.101053 [1954] libburn/drive.h libburn/drive.c libburn/sg-linux.c Avoiding drive scan if single drive is given 2008.08.05.175930 [1963] libburn/os-linux.h libburn/drive.h libburn/drive.c libburn/sg-linux.c libburn/libdax_msgs.h cdrskin/cdrskin.1 Taking into respect drive list from /proc/sys/dev/cdrom/info 5 Aug 2008 [1964] cdrskin/cdrskin_eng.html Updated for next 0.5.1 cycle ------------------------------------ cycle - cdrskin-0.5.1 - * Larger set of possibly acceptable drive device file names 2008.08.09.071742 [1975] libburn/mmc.c libburn/structure.h libburn/structure.c CD burn_toc_entries now bear extension_valid data 2008.08.09.071854 [1976] libburn/libburn.h libburn/read.c New flag bit2 with burn_read_data() 2008.08.19.122535 [1991] libburn/libburn.h API clarification about CD burn_toc_entries 2008.08.19.123513 [1992] libburn/structure.c Reacted on harmless compiler warning ------------------------------------ cycle - cdrskin-0.5.1 - 2008.08.19.123513 2008.08.20.080001 [1994] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.5.2 and activated development documentation 20 Aug 2008 [1995] - cdrskin/add_ts_changes_to_libburn_0_5_0 - cdrskin/add_ts_changes_to_libburn_0_5_1 + cdrskin/add_ts_changes_to_libburn_0_5_2 + cdrskin/add_ts_changes_to_libburn_0_5_3 Updated cdrskin tarball generator 20 Aug 2008 [1996] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.5.2.pl00 - 2008.08.20.080001 * Larger set of possibly acceptable drive device file names 2008.08.20.100045 [1997] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.5.3 20 Aug 2008 [1998] - cdrskin/add_ts_changes_to_libburn_0_5_0 - cdrskin/add_ts_changes_to_libburn_0_5_1 + cdrskin/add_ts_changes_to_libburn_0_5_2 + cdrskin/add_ts_changes_to_libburn_0_5_3 Updated cdrskin tarball generator 20 Aug 2008 [1999] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-0.5.3 - 2008.08.20.110457 30 Aug 2008 [2023] README Mentioned release of libisoburn-0.2.4 2008.08.30.104339 [2024] libburn/mmc.c libburn/spc.c Issueing many SCSI error messages in cleartext now 2008.09.09.131915 [2039] libburn/sg-linux.c Trying to avoid unnecessary access to sibling device objects 12 Sep 2008 [2043] doc/cookbook.txt Described ISO 9660 multi-session on overwriteable media 2008.09.14.174344 [2048] libburn/util.c Gave up problematic and unused version.h 2008.09.16.060250 [2052] cdrskin/cdrskin.c Corrected pacifier text (Ticket 141) 24 Sep 2008 [2079] README Mentioned recent releases of libisofs and libisoburn 2008.09.28.193802 [2086] libburn/spc.c Bug fix: Potential buffer overflow introduced with revision 2024 2008.09.28.211741 [2087] libburn/sg-linux.c Bug fix: /dev/sr0 was accepted as enumerable address on Linux 2.4 2008.10.04.072657 [2096] libburn/write.c libburn/read.c libburn/drive.c Prevented SIGSEGV after illegal drive operations during sync write 2008.10.04.072657 [2097] cdrskin/cdrskin.1 Mentioned new xorriso capabilities in man cdrskin 2008.10.04.073814 [2098] configure.ac Incremented LT_CURRENT and LT_AGE to get libburn.so.4.18.0 ------------------------------------ cycle - cdrskin-0.5.3 - 2008.10.04.072657 * Bug fix: /dev/sr0 was accepted as enumerable address on Linux 2.4 2008.10.05.073001 [2102] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition 5 Oct 2008 [2103] - cdrskin/add_ts_changes_to_libburn_0_5_2 - cdrskin/add_ts_changes_to_libburn_0_5_3 + cdrskin/add_ts_changes_to_libburn_0_5_4 + cdrskin/add_ts_changes_to_libburn_0_5_5 Updated cdrskin tarball generator 5 Oct 2008 [2104] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.5.4.pl00 - 2008.10.06.073001 * Bug fix: /dev/sr0 was accepted as enumerable address on Linux 2.4 * Issueing many SCSI error messages in cleartext now 2008.10.05.123737 [2106] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to next development cycle 5 Oct 2008 [2107] - cdrskin/add_ts_changes_to_libburn_0_5_2 - cdrskin/add_ts_changes_to_libburn_0_5_3 + cdrskin/add_ts_changes_to_libburn_0_5_4 + cdrskin/add_ts_changes_to_libburn_0_5_5 Updated cdrskin tarball generator 5 Oct 2008 [2108] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-0.5.5 - 2008.10.05.130109 2008.10.15.103224 [2126] libburn/libburn.h A clarification in comment about burn_disc_format() 15 Oct 2008 [2127] cdrskin/cdrskin.1 Fixed incomplete sentence in man cdrskin 2008.11.01.121240 [2157] libburn/libburn.h libburn/async.c Bug fix: Unsuitable write modes were caught silently and later than desired ------------------------------------ cycle - cdrskin-0.5.5 - 2008.11.01.121445 2008.11.08.134828 [2173] libburn/libburn.h Clarified behavior of burn_source with pipes 2008.11.08.141734 [2174] libburn/file.h libburn/file.c libburn/async.c Cancelling libburn fifo thread before freeing the fifo object 2008.11.08.202456 [2175] libburn/async.c Disabling the sigsegv provoking new debug message 2008.11.12.075111 [2180] libburn/async.h Avoiding warning message about implicitely declared burn_fifo_abort() ------------------------------------ cycle - cdrskin-0.5.5 - 2008.11.12.075411 * Bug fix: libburn fifo thread was not aborted when burn run was aborted 12 Nov 2008 [2181] svn copy http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/ZeroFiveSix Preparing for libburn-0.5.6 12 Nov 2008 [2182] svn delete http://svn.libburnia-project.org/libburn/branches/ZeroFourSix Removing obsolete libburn branch ZeroFourSix 2008.11.12.120001 [2183] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.5.6 12 Nov 2008 [2184] - cdrskin/add_ts_changes_to_libburn_0_5_4 - cdrskin/add_ts_changes_to_libburn_0_5_5 + cdrskin/add_ts_changes_to_libburn_0_5_6 + cdrskin/add_ts_changes_to_libburn_0_5_7 Updated cdrskin tarball generator 12 Nov 2008 [2185] cdrskin/changelog.txt Documented changes and release timestamp 12 Nov 2008 [2186] cdrskin/cdrskin.c Corrected wrong version number of cdrskin ------------------------------ release - cdrskin-0.5.6.pl00 - 2008.11.12.120001 * Bug fix: libburn fifo thread was not aborted when burn run was aborted which could lead to use of freed memory 2008.11.12.121832 [2187] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.5.7 12 Nov 2008 [2188] - cdrskin/add_ts_changes_to_libburn_0_5_4 - cdrskin/add_ts_changes_to_libburn_0_5_5 + cdrskin/add_ts_changes_to_libburn_0_5_6 + cdrskin/add_ts_changes_to_libburn_0_5_7 Updated cdrskin tarball generator 12 Nov 2008 [2189] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-0.5.7 - 2008.11.12.130026 12 Nov 2008 [2191] svn move \ http://svn.libburnia-project.org/libburn/"$svn_name" \ http://svn.libburnia-project.org/libburn/"$tag_name" libburn release 0.5.6 is ready 2008.11.15.220652 [2197] libburn/sg-freebsd.c libburn/sg-freebsd-port.c Removed remark that use of statvfs() was untested with FreeBSD 21 Nov 2008 [2208] README cdrskin/README cdrskin/cdrskin_eng.html Mentioned FreeBSD peculiarities in our docs 2008.11.26.210608 [2213] libburn/structure.c Added tests against the SIGSEGV of ticket 146 2008.11.27.081027 [2214] libburn/libdax_msgs.h libburn/structure.c Truncating eventually detected damaged CD table-of-content ------------------------------------ cycle - cdrskin-0.5.7 - 2008.11.27.081027 * Bug fix: Session without leadout entry on CD caused SIGSEGV 2008.11.27.172124 [2215] libburn/libdax_msgs.h libburn/structure.c Changed error severity with TOC truncation to MISHAP 2008.11.29.140115 [2217] libburn/spc.c Translating ASC=0x31 formatting error messages, reporting command names 2008.11.29.140404 [2218] libburn/mmc.c Circumventing BD-RE Quick Certification refusal of LG GGW-H20L YL03 ------------------------------------ cycle - cdrskin-0.5.7 - 2008.11.29.140404 2008.12.03.085219 [2239] libburn/mmc.c libburn/structure.c libburn/libdax_msgs.h Defaulting sessions without leadout entry ------------------------------------ cycle - cdrskin-0.5.7 - 2008.12.03.085219 Dec 07 2008 [2248] svn copy http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/ZeroFiveEight Preparing for libburn-0.5.8 2008.12.07.140001 [2249] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.5.8 Dec 07 2008 [2250] - cdrskin/add_ts_changes_to_libburn_0_5_6 - cdrskin/add_ts_changes_to_libburn_0_5_7 + cdrskin/add_ts_changes_to_libburn_0_5_8 + cdrskin/add_ts_changes_to_libburn_0_5_9 Updated cdrskin tarball generator Dec 07 2008 [2251] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.5.8.pl00 - 2008.12.07.140001 * Bug fix: A session without leadout entry on CD caused a SIGSEGV by NULL * Improvements about BD-RE formatting 2008.12.07.155219 [2252] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.5.9 Dec 07 2008 [2253] - cdrskin/add_ts_changes_to_libburn_0_5_6 - cdrskin/add_ts_changes_to_libburn_0_5_7 + cdrskin/add_ts_changes_to_libburn_0_5_8 + cdrskin/add_ts_changes_to_libburn_0_5_9 Updated cdrskin tarball generator Dec 07 2008 [2254] cdrskin/changelog.txt Documented changes and release timestamp Dec 07 2008 [2255] svn move -m "libburn release 0.5.8 is ready" \ http://svn.libburnia-project.org/libburn/branches/ZeroFiveEigh \ http://svn.libburnia-project.org/libburn/tags/ZeroFiveEight svn delete -m 'Deleted obsolete branch' \ http://svn.libburnia-project.org/libburn/branches/"$svn_name" ------------------------------------ cycle - cdrskin-0.5.9 - 2008.12.07.155219 2008.12.09.123314 [2264] libburn/libburn.h libburn/drive.c libburn/async.c libburn/write.c libburn/mmc.c Beginning to implement write code for BD-R SRM without POW 2008.12.09.123558 [2265] cdrskin/cdrskin.c Beginning to implement write code for BD-R SRM without POW 2008.12.10.092535 [2271] libburn/transport.h libburn/async.c libburn/mmc.h libburn/mmc.c Formatting of BD-R SRM to default size and by index 2008.12.10.114241 [2273] cdrskin/cdrskin.c Formatting of BD-R SRM to default size and by index 10 Dec 2008 [2274] README cdrskin/README cdrskin/cdrskin.1 cdrskin/wiki_plain.txt Mentioning BD-R in documentation 2008.12.11.072308 [2276] libburn/libdax_msgs.h libburn/async.c Rejecting unformattable BD-R more early 2008.12.11.092204 [2278] libburn/libdax_msgs.h libburn/async.c libburn/mmc.c cdrskin/cdrskin.1 Catching BD-R zero spare formatting with NOTE rather than SORRY 2008.12.12.112129 [2279] libburn/libburn.h libburn/async.c libburn/mmc.c Making format size of BD-RE and BD-R quite freely adjustable 2008.12.12.214013 [2280] libburn/transport.h libburn/async.c libburn/mmc.c Interpreting feature 0023h for BD formatting capabilities 2008.12.13.144909 [2284] configure.ac Now producing libburn.so.4.24.0 ------------------------------------ cycle - cdrskin-0.5.9 - 2008.12.13.144909 * Formatting and writing of BD-R media 14 Dec 2008 [2289] test/libburner.c Updated libburner to BD-R 14 Dec 2008 [2290] cdrskin/cdrskin_eng.html Updated cdrskin web page 2008.12.17.091905 [2297] libburn/libburn.h libburn/drive.c New API function burn_get_read_capacity() 17 Dec 2008 [2298] [2302] doc/comments Removed project overview and references to libisofs and libisoburn ------------------------------------ cycle - cdrskin-0.5.9 - 2008.12.19.070912 * New API function burn_get_read_capacity() 2008.12.19.203523 [2306] libburn/libburn.h Clarified blank, appendable, closed burn_disc_status 2008.12.22.130527 [2323] libburn/async.c Fixed denial of fast formatting with BD-RE introduced by revision 2280 ------------------------------------ cycle - cdrskin-0.5.9 - 2008.12.23.144853 2008.12.29.105341 [2342] libburn/libburn.h test/libburner.c Cosmetic changes Jan 02 2009 [2346] svn copy http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/ZeroSixZero Preparing for libburn-0.6.0 02 Jan 2009 [2347] COPYRIGHT libburn/libdax_msgs.c libburn/libdax_msgs.h test/libburner.c Updated copyright claims to year 2009 2009.01.02.160001 [2348] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.6.0 02 Jan 2009 [2349] - cdrskin/add_ts_changes_to_libburn_0_5_8 - cdrskin/add_ts_changes_to_libburn_0_5_9 + cdrskin/add_ts_changes_to_libburn_0_6_0 + cdrskin/add_ts_changes_to_libburn_0_6_1 Updated cdrskin tarball generator 02 Jan 2009 [2350] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.6.0.pl00 - 2009.01.02.160001 * Formatting and writing of BD-R media * New API function burn_get_read_capacity() 2009.01.04.112716 [2351] COPYRIGHT libburn/libdax_msgs.c libburn/libdax_msgs.h test/libburner.c Updated copyright claims to year 2009 2009.01.04.113401 [2352] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.6.1 04 Jan 2009 [2353] - cdrskin/add_ts_changes_to_libburn_0_5_8 - cdrskin/add_ts_changes_to_libburn_0_5_9 + cdrskin/add_ts_changes_to_libburn_0_6_0 + cdrskin/add_ts_changes_to_libburn_0_6_1 Updated cdrskin tarball generator 03 Jan 2009 [2354] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-0.6.1 - 2009.01.04.113401 04 Jan 2009 [2356] svn move http://svn.libburnia-project.org/libburn/branches/ZeroSixZero http://svn.libburnia-project.org/libburn/tags/ZeroSixZero libburn release 0.6.0 is ready 2009.01.06.122534 [2366] libburn/spc.c Error texts for ASC 73 : power calibration and program memory 2009.01.06.122808 [2367] [2369] libburn/async.c libburn/libdax_msgs.h Complaining and refusing more early with unformatted BD-RE 7 Jan 2009 [2370] cdrskin/cdrskin.1 Clarification about one-time DVD and BD media 2009.01.07.154414 [2371] libburn/write.c Bug fix: BD-R were not correctly finalized 7 Jan 2009 [2372] cdrskin/cdrskin_eng.html Mentioned bug fix and pl01 ------------------------------------ cycle - cdrskin-0.6.1 - 2009.01.07.154414 7 Jan 2009 [2373] svn copy -m "Branching for libburn bugfix release 0.6.0.pl01" \ http://svn.libburnia-project.org/libburn/ \ http://svn.libburnia-project.org/libburn/ 7 Jan 2009 [2374] svn rm -m 'Removing falsly copied tag' \ http://svn.libburnia-project.org/libburn/libburn 7 Jan 2009 [2375] svn copy -m "Branching for libburn bugfix release 0.6.0.pl01" \ http://svn.libburnia-project.org/libburn/tags/ZeroSixZero \ http://svn.libburnia-project.org/libburn/branches/ZeroSixZeroPl01 2009.01.07.140001 [branch 2376] README cdrskin/README cdrskin/cdrskin_eng.html cdrskin/cdrskin_timestamp.h Mentioned bug fix and pl01 2009.01.07.140001 [branch 2377] libburn/write.c Bug fix: BD-R were not correctly finalized 7 Jan 2009 [2378] svn move -m 'libburn bugfix release 0.6.0.pl01 is ready' \ http://svn.libburnia-project.org/libburn/branches/ZeroSixZeroPl01 \ http://svn.libburnia-project.org/libburn/tags/ZeroSixZeroPl01 ------------------------------------ cycle - cdrskin-0.6.1 - 2009.01.07.154414 2009.01.09.095943 [2381] libburn/transport.h libburn/write.c libburn/mmc.c libburn/libdax_msgs.h Recognizing BD-R media spoiled by the close bug and handling them as appendable ------------------------------------ cycle - cdrskin-0.6.1 - 2009.01.09.100210 2009.01.09.161704 [2383] libburn/write.c Preventing a possible bug with a burn run of more than one session at once 2009.01.11.102711 [2390] libburn/write.c libburn/libdax_msgs.h Prepared eventual closing of spoiled BD-R media by a pseudo write run 11 Jan 2009 [2391] doc/cookbook.txt Updated cookbook about BD-R media 27 Jan 2009 [2431] README libburn/libburn.h Mentioned the need for 64 bit file i/o 2009.02.04.102822 [2447] Makefile.am Linking with $LIBBURN_ARCH_LIBS to get -lcam on FreeBSD 4 Feb 2009 [2448] README cdrskin/README Mentioned hald as possibly conflicting service 2009.02.19.192801 [2479] libburn/spc.c Human readable error messages with asynchronous SCSI errors 20 Feb 2009 [2482] svn copy http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/ZeroSixZero Preparing for libburn-0.6.0 2009.02.20.090001 [2483] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.6.2 20 Feb 2009 [2484] - cdrskin/add_ts_changes_to_libburn_0_6_0 - cdrskin/add_ts_changes_to_libburn_0_6_1 + cdrskin/add_ts_changes_to_libburn_0_6_2 + cdrskin/add_ts_changes_to_libburn_0_6_3 Updated cdrskin tarball generator ------------------------------ release - cdrskin-0.6.2.pl00 - 2009.02.20.090001 * Improvements with build system for FreeBSD 2009.02.20.124909 [2485] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.6.3 20 Feb 2009 [2486] - cdrskin/add_ts_changes_to_libburn_0_6_0 - cdrskin/add_ts_changes_to_libburn_0_6_1 + cdrskin/add_ts_changes_to_libburn_0_6_2 + cdrskin/add_ts_changes_to_libburn_0_6_3 Updated cdrskin tarball generator ------------------------------------ cycle - cdrskin-0.6.3 - 2009.02.20.132535 20 Feb 2009 [2489] svn move -m libburn release 0.6.2 is ready http://svn.libburnia-project.org/libburn/branches/ZeroSixTwo http://svn.libburnia-project.org/libburn/tags/ZeroSixTwo 2009.02.27.143100 [2500] libburn/libburn.h libburn/transport.h libburn/drive.c libburn/options.c libburn/write.c libburn/mmc.c New API function burn_drive_set_stream_recording() 2009.02.27.211707 [2501] cdrskin/cdrskin.c cdrskin/cdrskin.1 New stream_recording mode with start number 2009.03.02.170126 [2513] libburn/os.h libburn/libdax_msgs.h libburn/sg.c + libburn/os-dummy.h + libburn/sg-dummy.c New operating system adapter "dummy" for stdio on POSIX-like systems 2009.03.02.193353 [2514] libburn/drive.c Fixed a race condition on abort with stdio writing which could cause SIGSEGV 2009.03.02.200132 [2515] libburn/sg.c Added a dummy function with loud compiler warning to sg.c dummy case 2009.03.03.092057 [2516] configure.ac libburn/sg-dummy.c Making optional use of statvfs() in sg-dummy ------------------------------------ cycle - cdrskin-0.6.3 - 2009.03.03.092057 * New API function burn_drive_set_stream_recording() * New stream recording mode with start number 2009.03.05.145309 [2520] acinclude.m4 Lifted the ban on operating systems other than Linux and FreeBSD ------------------------------------ cycle - cdrskin-0.6.3 - 2009.03.05.145309 * New operating system adapter "dummy" for stdio on POSIX-like systems 2009.03.08.140120 [2522] libburn/cleanup.c Reacted on compiler warnings of SchilliX-0.6.7 (based on Solaris 5.11) 13 Mar 2009 [2529] svn copy -m "Branching for libburn release 0.6.4" \ http://svn.libburnia-project.org/libburn/trunk \ http://svn.libburnia-project.org/libburn/branches/ZeroSixFour 2009.03.13.080001 [2530] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made libburn number transition to 0.6.4 13 Mar 2009 [2531] - cdrskin/add_ts_changes_to_libburn_0_6_2 - cdrskin/add_ts_changes_to_libburn_0_6_3 + cdrskin/add_ts_changes_to_libburn_0_6_4 + cdrskin/add_ts_changes_to_libburn_0_6_5 Updated cdrskin tarball generator 13 Mar 2009 [2534] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.6.4.pl00 - 2009.03.13.080001 * New operating system adapter "dummy" for stdio on general X/Open systems * New API function burn_drive_set_stream_recording() * New stream recording mode with start address 2009.03.13.135818 [2532] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made libburn number transition to 0.6.5 13 Mar 2009 [2533] - cdrskin/add_ts_changes_to_libburn_0_6_2 - cdrskin/add_ts_changes_to_libburn_0_6_3 + cdrskin/add_ts_changes_to_libburn_0_6_4 + cdrskin/add_ts_changes_to_libburn_0_6_5 Updated cdrskin tarball generator 13 Mar 2009 [2535] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-0.6.5 - 2009.03.13.162355 13 Mar 2009 [2538] svn move -m "libburn release 0.6.4 is ready" \ http://svn.libburnia-project.org/libburn/branches/ZeroSixFour \ http://svn.libburnia-project.org/libburn/tags/ZeroSixFour 2009.03.16.190745 [2547] configure.ac acinclude.m4 Makefile.am Get on FreeBSD pkgconfigdir=.../libdata , on Linux and others: .../lib 2009.03.18.124558 [2553] libburn/spc.c Human readable error messages for asc=08 "Logical unit communication failure" 2009.03.18.172512 [2554] libburn/drive.c Bug fix: burn_abort() did not work with broken output pipe (since rev 2514) 2009.04.30.065653 [2623] libburn/write.c Marked alleged use of uninitialized memory reported by valgrind 2009.04.30.065918 [2624] libburn/write.c Replaced 8 blanks by a tab 2009.04.30.070813 [2625] libburn/sg-freebsd.c Bug fix: Device scan stalled on FreeBSD. Ticket 148 jwehle ------------------------------------ cycle - cdrskin-0.6.5 - 2009.04.30.070813 Bug fix: burn_abort() did not work with broken output pipe (since rev 2514) Bug fix: Device scan stalled on FreeBSD. Ticket 148 jwehle 7 May 2009 [2628] svn copy -m "Branching for libburn release 0.6.6" http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/ZeroSixSix 7 May 2009 [2629] cdrskin/cdrskin.1 Mentioned BD were it was missing 2009.05.07.100001 [2630] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition and activated development documentation 7 May 2009 [2631] - cdrskin/add_ts_changes_to_libburn_0_6_4 - cdrskin/add_ts_changes_to_libburn_0_6_5 + cdrskin/add_ts_changes_to_libburn_0_6_6 + cdrskin/add_ts_changes_to_libburn_0_6_7 Updated cdrskin tarball generator 7 May 2009 [2632] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.6.6.pl00 - 2009.05.07.100001 Bug fix: burn_abort() did not work with broken output pipe (since rev 2514) Bug fix: Device scan stalled on FreeBSD. Ticket 148 jwehle 7 May 2009 [2633] cdrskin/cdrskin.1 Mentioned BD were it was missing 2009.05.07.181034 [2634] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition and activated development documentation 7 May 2009 [2635] - cdrskin/add_ts_changes_to_libburn_0_6_4 - cdrskin/add_ts_changes_to_libburn_0_6_5 + cdrskin/add_ts_changes_to_libburn_0_6_6 + cdrskin/add_ts_changes_to_libburn_0_6_7 Updated cdrskin tarball generator 7 May 2009 [2636] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-0.6.7 - 2009.05.07.181034 8 May 2009 [2637] svn move -m libburn release 0.6.6 is ready http://svn.libburnia-project.org/libburn/branches/ZeroSixSix http://svn.libburnia-project.org/libburn/tags/ZeroSixSix 2009.05.17.144304 [2645] cdrskin/cdrskin.c Made -scanbus work with SCSI bus numbers like 85 (USB, kernel 2.6.18.2) 2009.06.02.162841 [2655] libburn/drive.c libburn/async.c libburn/options.c libburn/spc.c libburn/libdax_msgs.h Rectified use of burn_drive.mdata->valid 2 Jun 2009 [2656] test/libburner.c Avoided SIGSEGV if no drives were found by scan 2009.06.02.172201 [2657] libburn/drive.c Avoided to enumerate faulty drive objects 2009.06.03.085637 [2658] libburn/spc.c Bug fix: Old MMC-1 drives were rejected because of mode page 2Ah length 2009.06.03.185118 [2659] libburn/transport.h libburn/drive.c libburn/spc.c libburn/mmc.c Bug fix: No usable media was detected with old MMC-1 drives 2009.06.14.095004 [2669] libburn/sg-linux.c Retrying 3 times on EBUSY drives with generous usleep intervals ------------------------------------ cycle - cdrskin-0.6.7 - 2009.06.14.095833 Made -scanbus work with high SCSI bus numbers Bug fix: Old MMC-1 drives were rejected because of mode page 2Ah length Bug fix: No usable media was detected with old MMC-1 drives 7 Jul 2009 [2691] acinclude.m4 configure.ac configure options --enable-libdir-pkgconfig and --enable-pkgconfig-path=DIR 2009.07.07.191134 [2692] Timestamp for revision 2691 14 Jul 2009 [2894] svn copy -m "Branching for libburn release 0.6.8" http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/ZeroSixEight 2009.07.14.100001 [2695] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition and activated development documentation 14 Jul 2009 [2696] - cdrskin/add_ts_changes_to_libburn_0_6_6 - cdrskin/add_ts_changes_to_libburn_0_6_7 + cdrskin/add_ts_changes_to_libburn_0_6_8 + cdrskin/add_ts_changes_to_libburn_0_6_9 Updated cdrskin tarball generator 14 Jul 2009 [2697] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.6.8.pl00 - 2009.07.14.100001 Made -scanbus work with high SCSI bus numbers Bug fix: Old MMC-1 drives were rejected because of mode page 2Ah length Bug fix: No usable media was detected with old MMC-1 drives 2009.07.14.133159 [2698] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition and activated development documentation 14 Jul 2009 [2699] - cdrskin/add_ts_changes_to_libburn_0_6_6 - cdrskin/add_ts_changes_to_libburn_0_6_7 + cdrskin/add_ts_changes_to_libburn_0_6_8 + cdrskin/add_ts_changes_to_libburn_0_6_9 Updated cdrskin tarball generator 14 Jul 2009 [2700] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-0.6.9 - 2009.07.14.133159 14 Jul 2009 [2702] svn move -m libburn release 0.6.8 is ready http://svn.libburnia-project.org/libburn/branches/ZeroSixEight http://svn.libburnia-project.org/libburn/tags/ZeroSixEight 2009.08.15.133055 [2721] libburn/write.c Added test code about output blocks size as comment. 2009.08.15.133332 [2722] libburn/libburn.h libburn/transport.h libburn/drive.c libburn/mmc.h libburn/mmc.c New API calls burn_drive_get_all_profiles(), burn_obtain_profile_name() 2009.08.15.133744 [2723] cdrskin/cdrskin.c Listing all profiles with cdrskin -v -atip 2009.08.23.130326 [2735] libburn/drive.c libburn/spc.c libburn/mmc.c Adapted to pitfalls of U3 memory sticks which appear as CD-ROM drives ------------------------------------ cycle - cdrskin-0.6.9 - 2009.08.23.132942 New API calls burn_drive_get_all_profiles(), burn_obtain_profile_name() Adapted to pitfalls of U3 memory sticks which appear as CD-ROM drives 2009.08.24.131146 [2736] libburn/libburn.h libburn/transport.h libburn/drive.c libburn/sbc.c New API call burn_drive_snooze() 2009.08.24.161646 [2737] libburn/libburn.h libburn/drive.c Made burn_drive_snooze() safe for emulated drives 2009.08.24.202517 [2740] libburn/transport.h libburn/sbc.c libburn/mmc.c Implemented automatic START UNIT after STOP UNIT before any other SCSI command 2009.08.27.123001 [2746] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition and activated development documentation 27 Aug 2009 [2747] - cdrskin/add_ts_changes_to_libburn_0_6_8 - cdrskin/add_ts_changes_to_libburn_0_6_9 + cdrskin/add_ts_changes_to_libburn_0_7_0 + cdrskin/add_ts_changes_to_libburn_0_7_1 Updated cdrskin tarball generator 27 Aug 2009 [2748] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.7.0.pl00 - 2009.08.27.123001 New API calls burn_drive_get_all_profiles(), burn_obtain_profile_name() New API call burn_drive_snooze() Adapted to pitfalls of U3 memory sticks which appear as CD-ROM drives Listing all supported profiles with option -atip 2009.08.27.143351 [2749] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to development version 0.7.1 27 Aug 2009 [2750] - cdrskin/add_ts_changes_to_libburn_0_6_8 - cdrskin/add_ts_changes_to_libburn_0_6_9 + cdrskin/add_ts_changes_to_libburn_0_7_0 + cdrskin/add_ts_changes_to_libburn_0_7_1 Updated cdrskin tarball generator 27 Aug 2009 [2751] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-0.7.1 - 2009.08.27.143351 2009.09.01.082602 [2773] cdrskin/cdrskin.c cdrskin/cdrskin.1 Disabled write mode -raw96r 2009.09.01.095750 [2774] [2775] libburn/libburn.h libburn/sector.h libburn/sector.c libburn/lec.h libburn/lec.c libburn/read.c libburn/write.c Disabled code using libburn/lec.c 2009.09.01.113957 [2777] cdrskin/cdrskin.c Aligned the output columns of cdrskin --devices 2009.09.01.133520 [2778] cdrskin/cdrskin.c cdrskin/cdrskin.1 New option --long_toc, now printing media summary at end of TOC 2009.09.02.131514 [2779] Makefile.am libburn/sector.c - libburn/lec.h - libburn/lec.c cdrskin/compile_cdrskin.sh Removed lec.c from libburn 2009.09.02.133307 [2780] libburn/libburn.h libburn/util.c + doc/mediainfo.txt cdrskin/cdrskin.c New API call burn_guess_cd_manufacturer() 2009.09.04.202255 [2781] libburn/libburn.h libburn/drive.c libburn/util.c libburn/spc.c libburn/mmc.h libburn/mmc.c New API calls burn_get_media_product_id() and burn_guess_manufacturer() 2009.09.04.202357 [2782] cdrskin/cdrskin.c Option -atip now reports Product Id and Manufacturer for most CD, DVD, BD types 4 Sep 2009 [2783] doc/mediainfo.txt Updated media info documentation 2009.09.04.204432 [2784] cdrskin/cdrskin.c Adjusted column width of media info messages 2009.09.05.072512 [2785] cdrskin/cdrskin.c Adjusted column width of media summary message ------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.05.072659 * New option --long_toc, now printing media summary at end of TOC * New API call burn_guess_cd_manufacturer() * New API calls burn_get_media_product_id() and burn_guess_manufacturer() 2009.09.05.113043 [2786] libburn/util.h libburn/util.c libburn/mmc.c Made product ID surely a single printable word 2009.09.05.114326 [2787] libburn/sg-linux.c Silenced a valgrind warning caused by not recognizing side effects of a ioctl 2009.09.05.115112 [2788] cdrskin/cdrskin.c Closed a small memory leak with drive inquiry 2009.09.05.120947 [2789] cdrskin/cdrskin.c Made cdrskin/compile_cdrskin.sh -libburn_0_7_0 work again ------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.05.121334 2009.09.05.131859 [2790] cdrskin/cdrskin.c Re-enabled output of product id and manufacturer ------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.05.132208 2009.09.05.165127 [2791] libburn/libburn.h libburn/drive.c libburn/util.c libburn/mmc.c Introduced flag bit0 for API call burn_get_media_product_id() 2009.09.05.165257 [2792] cdrskin/cdrskin.c "Manufacturer:", "Media type:" as cdrecord, own "Product Id:" and "Producer:" ------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.05.172818 * New -atip report lines "Product Id:" and "Producer:" 2009.09.06.092330 [2793] libburn/util.c Made recognition of CD media codes work in burn_guess_manufacturer() 2009.09.06.092654 [2794] libburn/libburn.h libburn/drive.c cdrskin/cdrskin.c Changed new API call burn_get_media_product_id() to burn_disc_get_media_id() 06 Sep 2009 [2795] doc/mediainfo.txt Corrected misformatted manufacturer strings of DVD- media 2009.09.06.112121 [2796] libburn/mmc.c doc/mediainfo.txt Avoided to read third sixpack of manufacturer bytes with DVD-R media ------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.06.115138 2009.09.09.125305 [2801] libburn/libburn.h libburn/transport.h libburn/drive.c libburn/mmc.c New API call burn_disc_get_cd_info() 2009.09.09.134030 [2802] libburn/transport.h libburn/drive.c libburn/mmc.c Correction with erasable bit of burn_disc_get_cd_info() 2009.09.09.153951 [2803] libburn/libburn.h libburn/mmc.c New struct burn_toc_entry extension for Last Recorded Address 2009.09.09.173452 [2804] cdrskin/cdrskin.c cdrskin/cdrskin.1 Emulation of some -minfo output 2009.09.09.174337 [2805] cdrskin/cdrskin.c Silenced compiler warning ------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.09.174446 * New API call burn_disc_get_cd_info() * Emulation of some -minfo output 2009.09.10.071253 [2806] cdrskin/cdrskin.c Removed Media summary from -minfo because of incompatible counting rules ------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.10.084912 2009.09.11.112528 [2807] libburn/libburn.h libburn/transport.h libburn/structure.h libburn/structure.c libburn/sector.c New API call burn_track_set_cdxa_conv() 2009.09.11.120959 [2808] cdrskin/cdrskin.c cdrskin/cdrskin.1 Interpreting options -mode2, -xa, -xa1, -xa2 but producing CD-ROM Mode 1 tracks 12 Sep 2009 [2809] cdrskin/cdrskin_eng.html Updated cdrskin web page ------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.12.134510 * New API call burn_track_set_cdxa_conv() * Better interpretation of options -mode2, -xa, -xa1, -xa2 * New option --xa1-ignore 2009.09.13.095055 [2810] libburn/drive.c Evaluating read capacity with role 2 drives (regular files and block devices) 2009.09.18.161214 [2815] libburn/sector.c Fixing SIGSEGV with CD SAO introduced with revision 2807 2009.09.18.161944 [2816] libburn/read.c libburn/libdax_msgs.h Refusing to read beyond media_read_capacity ------------------------------------ cycle - cdrskin-0.7.1 - 2009.09.18.164446 2009.09.20.111911 [2820] libburn/libburn.h Mentioned need for 2056 byte fifo chunks with burn_track_set_cdxa_conv() 2009.09.22.192545 [2822] libburn/sbc.c Revoked instruction to retry STOP UNIT 2009.09.22.192802 [2823] libburn/sg-linux.c Restricted retry to the timeout for single SCSI commands (200 seconds) 2009.10.04.151239 [2830] libburn/write.c libburn/libdax_msgs.h Fixed CD TAO multi-track -dummy bug reported by Philippe Rouquier ------------------------------------ cycle - cdrskin-0.7.1 - 2009.10.08.174136 * Bug fix: CD TAO sessions with multiple tracks did not work in -dummy mode 2009.10.09.123651 [2847] libburn/mmc.c Determining read capacity for DVD-RAM 2009.10.09.123840 [2848] cdrskin/cdrskin.c Made -minfo stupidly report overwriteable media as "erasable" and "complete" 2009.10.09.200411 [2849] libburn/read.c Made read_capacity error message of burn_read_data() depending on flag bit1 2009.10.09.200730 [2850] cdrskin/cdrskin.c Made -minfo subtract 2 from track size if "Data" and last 2 blocks unreadable 12 Oct 2009 [2854] svn copy -m "Branching for libburn release 0.7.2" \ http://svn.libburnia-project.org/libburn/trunk \ http://svn.libburnia-project.org/libburn/branches/ZeroSevenTwo 2009.10.12.080001 [2855] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition and activated development documentation 12 Oct 2009 [2856] - cdrskin/add_ts_changes_to_libburn_0_7_0 - cdrskin/add_ts_changes_to_libburn_0_7_1 + cdrskin/add_ts_changes_to_libburn_0_7_2 + cdrskin/add_ts_changes_to_libburn_0_7_3 Updated cdrskin tarball generator 12 Oct 2009 [2867] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.7.2.pl00 - 2009.10.12.080001 * Bug fix: CD TAO sessions with multiple tracks did not work in -dummy mode * Better interpretation of options -mode2, -xa, -xa1, -xa2 * New option --xa1-ignore * Emulation of some -minfo output * New -atip report lines "Product Id:" and "Producer:" * New API call burn_guess_cd_manufacturer() * New API calls burn_get_media_product_id() and burn_guess_manufacturer() * New API call burn_disc_get_cd_info() * New API call burn_track_set_cdxa_conv() 2009.10.12.103503 [2858] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.7.3 12 Oct 2009 [2859] - cdrskin/add_ts_changes_to_libburn_0_7_0 - cdrskin/add_ts_changes_to_libburn_0_7_1 + cdrskin/add_ts_changes_to_libburn_0_7_2 + cdrskin/add_ts_changes_to_libburn_0_7_3 Updated cdrskin tarball generator 12 Oct 2009 [2860] cdrskin/changelog.txt Documented changes and release timestamp 12 Oct 2009 [2861] svn move -m "libburn release 0.7.2 is ready" \ http://svn.libburnia-project.org/libburn/branches/ZeroSevenTwo \ http://svn.libburnia-project.org/libburn/tags/ZeroSevenTwo ------------------------------------ cycle - cdrskin-0.7.3 - 2009.10.12.105750 2009.10.17.131852 [2865] [2864] Makefile.am libburn/libburn.h libburn/sector.c + libburn/ecma130ab.h + libburn/ecma130ab.c cdrskin/compile_cdrskin.sh Re-implemented ECMA-130 P-parity, Q-parity and scrambling for BURN_WRITE_RAW ------------------------------------ cycle - cdrskin-0.7.3 - * Re-implemented ECMA-130 P-parity, Q-parity and scrambling for BURN_WRITE_RAW 2009.10.19.115722 [2867] libburn/ecma130ab.c Optimizations with parity computation, clarification about nature of logarithms 2009.10.20.160131 [2868] libburn/libburn.h libburn/ecma130ab.c More optimizations with parity computation ------------------------------------ cycle - cdrskin-0.7.3 - 2009.10.20.160131 2009.10.27.100637 [2871] libburn/mmc.c Bug fix: burn_drive->disc_id or burn_drive->disc_app_code altered by stray 0. Thanks to George Danchev. 2009.10.27.101031 [2872] libburn/sg-freebsd.c Bug fix: Closed memory leak with failure to open device file under FreeBSD. Thanks to George Danchev. 2009.10.30.134640 [2880] libburn/libburn.h libburn/spc.c libburn/mmc.c libburn/sg-linux.c Test macros for finding reason of stall problem with Pioneer DVD-216D on DVD-R ------------------------------------ cycle - cdrskin-0.7.3 - 2009.10.30.134640 2009.11.03.184626 [2881] libburn/libburn.h libburn/mmc.c Test macro for SL_V in mode page 05 2009.11.04.084506 [2882] libburn/libburn.h libburn/write.c Test macro for SEND OPC INFORMATION before DVD-R track 2009.11.05.170409 [2883] libburn/libburn.h libburn/transport.h libburn/write.c libburn/sbc.c libburn/mmc.c Test macros for double START UNIT and SET CD SPEED 2009.11.08.120917 [2884] libburn/mmc.c Corrected allocation length with GET CONFIGURATION 2009.11.08.121334 [2885] libburn/spc.h libburn/spc.c libburn/mmc.h libburn/sg-linux.c Made SCSI command log more complete and more readable 2009.11.10.132154 [2886] libburn/mmc.c Avoiding START UNIT before the drive gets released 2009.11.10.203412 [2887] libburn/libburn.h libburn/write.c libburn/spc.c libburn/sbc.c libburn/mmc.c libburn/sg-linux.c Hopefully solved the endless burn problem with Pioneer DVR-216D 2009.11.11.100714 [2888] libburn/write.c Increased stdio flush interval from 1 MB to 16 MB 2009.11.11.100822 [2889] cdrskin/cdrskin.c Reporting number of pending bytes while thanking for patience in -vvv mode 2009.11.11.105028 [2890] cdrskin/cdrskin.c Silenced a compiler warning about potentially uninitialized variable ------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.11.105159 * Workaround for Pioneer DVR-216D which got stuck on DVD-R burns 11 Nov 2009 [2891] cdrskin/cdrskin_eng.html Updated cdrskin home page about DVR-216D workaround 2009.11.12.175514 [2892] libburn/libburn.h libburn/init.c libburn/write.c libburn/sg-linux.c Made SCSI logger permanent and controllable via API call 2009.11.12.175735 [2893] libburn/spc.c libburn/sbc.c libburn/mmc.c Workaround for Pioneer DVR-216D refusal to eject 2009.11.12.180048 [2894] libburn/drive.c Macro Libburn_pioneer_dvr_216d_dummy_probe_wM for omitting write mode probe 2009.11.12.180241 [2895] cdrskin/cdrskin.c Implemented option -V for logging of SCSI commands 2009.11.12.193617 [2896] cdrskin/cdrskin.c cdrskin/cdrskin.1 Man page entry and help text for option -V ------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.12.194644 * Implemented option -V for logging of SCSI commands * Workaround for Pioneer DVR-216D refusal to eject 13 Nov 2009 [2898] svn copy -m Branching for libburn bugfix release 0.7.2.pl01 \ http://svn.libburnia-project.org/libburn/tags/ZeroSevenTwo \ http://svn.libburnia-project.org/libburn/branches/ZeroSevenTwoPl01 2009.11.12.230001 [2899] README libburn/write.c libburn/sbc.c cdrskin/README cdrskin/cdrskin_eng.html cdrskin/cdrskin_timestamp.h libburn-0.7.2.pl01 : Workarounds for Pioneer DVR-216D rev 1.09 13 Nov 2009 [2900] svn move -m libburn bugfix release 0.7.2.pl01 is ready \ http://svn.libburnia-project.org/libburn/branches/ZeroSevenTwoPl01 \ http://svn.libburnia-project.org/libburn/tags/ZeroSevenTwoPl01 -------------------------------- patch - cdrskin-0.7.2.pl01 - 2009.11.12.230001 * Workaround for Pioneer DVR-216D which got stuck on DVD-R burns * Workaround for Pioneer DVR-216D refusal to eject 2009.11.15.115923 [2901] libburn/file.c Corrected an outdated remark 2009.11.15.152541 [2902] cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/compile_cdrskin.sh New compile_cdrskin.sh option -o_direct (Linux only) 2009.11.15.153140 [2903] libburn/libburn.h libburn/options.h libburn/options.c libburn/write.c New API calls burn_write_opts_set_dvd_obs(), burn_write_opts_set_stdio_fsync() 2009.11.15.165016 [2904] cdrskin/cdrskin.c cdrskin/cdrskin.1 New options dvd_obs= and stdio_fsync= 2009.11.16.165420 [2905] configure.ac Makefile.am libburn/write.c cdrskin/cdrskin.c cdrskin/cdrfifo.h cdrskin/cdrfifo.c cdrskin/cdrskin.1 Configure options --enable-cdrskin-fifo-odirect, --enable-dvd-obs-64k 16 Nov 2009 [2906] cdrskin/compile_cdrskin.sh New compile_cdrskin.sh option -dvd_obs_64k, adapted to new .o names of libburn 16 Nov 2009 [2907] README cdrskin/README cdrskin/cdrskin_eng.html Updated build instructions of libburn and cdrskin ------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.16.180334 * New API calls burn_write_opts_set_dvd_obs(), burn_write_opts_set_stdio_fsync() * New options dvd_obs= and stdio_fsync= * Configure option --enable-dvd-obs-64k * New compile_cdrskin.sh option -dvd_obs_64k 2009.11.17.093602 [2908] configure.ac Makefile.am cdrskin/compile_cdrskin.sh Revoked usage of libburn_libburn_la_CFLAGS in Makefile.am (ugly .o names) ------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.17.100248 17 Nov 2009 [2912] cdrskin/compile_cdrskin.sh Corrected help text of cdrskin static compile script 2009.11.18.122713 [2913] libburn/drive.c libburn/spc.c libburn/mmc.h libburn/mmc.c Split automatic drive start function from mmc_function_spy() 2009.11.18.185733 [2914] libburn/write.c Reserving enough track space for 64 kB write chunks ------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.18.190403 2009.11.20.134952 [2915] libburn/libburn.h libburn/write.c libburn/sg-linux.c Experiment about SG_FLAG_DIRECT_IO 2009.11.20.175854 [2916] cdrskin/cdrfifo.c Avoided use of uninitialized variable ------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.21.122202 2009.11.21.191516 [2917] libburn/write.c Longer READ BUFFER CAPACITY interval with DVD/BD writing 2009.11.21.191717 [2918] cdrskin/cdrskin.c Enabled cdrskin O_DIRECT with fs=0 2009.11.22.115227 [2919] libburn/write.c Bug fix: DVD DAO track size was rounded up much too generously 2009.11.23.185725 [2920] configure.ac Makefile.am README libburn/libburn.h libburn/file.h libburn/file.c libburn/async.c libburn/write.c libburn/sg-linux.c libburn/sg-freebsd.c libburn/sg-freebsd-port.c libburn/sg-dummy.c New API calls burn_os_open_track_src() , burn_os_alloc_buffer() 2009.11.23.193252 [2921] cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/compile_cdrskin.sh cdrskin/README Gave up cdrskin specific O_DIRECT compile option 2009.11.25.122233 [2920] libburn/libburn.h libburn/sg-linux.c libburn/sg-freebsd.c libburn/sg-freebsd-port.c libburn/sg-dummy.c Gave up call burn_os_close_track_src() introduced by rev 2920 2009.11.25.160153 [2923] libburn/libburn.h libburn/file.c libburn/libdax_msgs.h New API call burn_fifo_fill() 2009.11.26.144452 [2924] libburn/libburn.h libburn/file.h libburn/file.c New API calls burn_fifo_get_statistics(), burn_fifo_next_interval() 2009.11.26.211418 [2925] cdrskin/cdrskin.c cdrskin/compile_cdrskin.sh Compiler option -use_libburn_fifo to switch non-CD from cdrfifo to libburn fifo 2009.11.28.122436 [2926] libburn/sg-linux.c Using mmap() by default for allocating read buffers ------------------------------------ cycle - cdrskin-0.7.3 - 2009.11.28.165658 * Bug fix: DVD DAO track size was rounded up much too generously * New API call burn_fifo_fill() * New API calls burn_fifo_get_statistics(), burn_fifo_next_interval() * Configure option --enable-track-src-odirect 2009.11.30.100152 [2929] libburn/sbc.c Documented meaning of START/STOP UNIT bits 2009.12.02.103036 [2930] libburn/write.c Gave up CLOSE TRACK with CD TAO burn runs 2009.12.05.111822 [2931] libburn/mmc.c Bug fix: SIGSEGV with LG GH22LS30 when inquiring media product id ------------------------------------ cycle - cdrskin-0.7.3 - 2009.12.05.112623 * Bug fix: SIGSEGV from NULL pointer with media product id inquiry on LG GH22LS30 2009.12.05.142309 [2932] libburn/libburn.h libburn/write.c Made effect of macro Libburn_pioneer_dvr_216d_read_buf_caP unconditional 2009.12.05.143707 [2933] libburn/mmc.c libburn/sg-linux.c Converted stderr experiment messages to DEBUG messages 2009.12.06.073404 [2934] libburn/mmc.c Some clarifications about the GH22LS30 problem 2009.12.06.074622 [2935] libburn/write.c Some clarifications about the Linux throughput problem 2009.12.06.093847 [2936] cdrskin/compile_cdrskin.sh Now default in cdrskin: use of libburn fifo with DVD and BD single track 06 Dec 2009 [2937] svn copy -m Branching for libburn release 0.7.4 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/ZeroSevenFour 2009.12.06.160001 [2938] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.7.4 06 Dec 2009 [2939] - cdrskin/add_ts_changes_to_libburn_0_7_2 - cdrskin/add_ts_changes_to_libburn_0_7_3 + cdrskin/add_ts_changes_to_libburn_0_7_4 + cdrskin/add_ts_changes_to_libburn_0_7_5 Updated cdrskin tarball generator 06 Dec 2009 [2940] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.7.4.pl00 - 2009.12.06.160001 * Configure options --enable-dvd-obs-64k, --enable-track-src-odirect * New API calls burn_write_opts_set_dvd_obs(), burn_write_opts_set_stdio_fsync() * New API call burn_set_scsi_logging() * New API calls burn_fifo_get_statistics(), burn_fifo_next_interval(), burn_fifo_fill() * Re-implemented ECMA-130 P-parity, Q-parity and scrambling for BURN_WRITE_RAW * Workaround for Pioneer DVR-216D which got stuck on DVD-R burns * Workaround for Pioneer DVR-216D refusal to eject * Bug fix: SIGSEGV from NULL pointer with media product id inquiry on LG GH22LS30 * Bug fix: DVD DAO track size was rounded up much too generously * cdrskin option -V for logging of SCSI commands * New cdrskin options dvd_obs= and stdio_fsync= * New compile_cdrskin.sh option -dvd_obs_64k 2009.12.07.070029 [2941] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.7.5 07 Dec 2009 [2942] - cdrskin/add_ts_changes_to_libburn_0_7_2 - cdrskin/add_ts_changes_to_libburn_0_7_3 + cdrskin/add_ts_changes_to_libburn_0_7_4 + cdrskin/add_ts_changes_to_libburn_0_7_5 Updated cdrskin tarball generator 07 Dec 2009 [2943] cdrskin/changelog.txt Documented changes and release timestamp 07 Dec 2009 [2946] svn move -m libburn release 0.7.4 is ready http://svn.libburnia-project.org/libburn/branches/ZeroSevenFour http://svn.libburnia-project.org/libburn/tags/ZeroSevenFour ------------------------------------ cycle - cdrskin-0.7.5 - 2009.12.07.083850 16 Dec 2009 [2955] doc/cookbook.txt Mentioned in cookbook the change about TAO close track 2009.12.19.140015 [2957] libburn/spc.c Corrected CDB length of command 55h MODE SELECT from 12 to 10 2009.12.19.142456 [2958] libburn/spc.h libburn/sg-linux.c Moved sg_log_cmd() to spc.c scsi_log_cmd() 2009.12.19.142456 [2959] configure.ac Makefile.am libburn/os.h libburn/sg.c libburn/sg-freebsd-port.c + libburn/os-libcdio.h + libburn/sg-libcdio.c Experimental SCSI transport adapter via GNU libcdio 2009.12.24.170601 [2960] configure.ac libburn/spc.h libburn/spc.c libburn/sg-linux.c libburn/sg-libcdio.c Making use of libcdio function mmc_get_cmd_scsi_sense() 25 Dec 2009 [2961] cdrskin/compile_cdrskin.sh Option -use_libcdio for cdrskin development compile script 2009.12.25.101433 [2962] libburn/libdax_msgs.h Commited file forgotten with rev 2960 2009.12.25.143326 [2963] libburn/sg-libcdio.c Resolving symbolic links in libcdio drive list 2009.12.25.144122 [2964] configure.ac Added PKG_CHECK_MODULES for libcdio-0.82 (must become 0.83 when released) 2009.12.25.205704 [2965] configure.ac libburn/sg-libcdio.c Adapted to libcdio-0.83 and its runtime version telling 2009.12.25.223915 [2966] libburn/init.c libburn/sg.h libburn/sg-freebsd.c libburn/sg-freebsd-port.c libburn/sg-libcdio.c libburn/sg-linux.c New internal sg-API function sg_initialize() 2009.12.26.080301 [2967] libburn/init.c libburn/sg-dummy.c libburn/sg-freebsd.c libburn/sg-freebsd-port.c libburn/sg-libcdio.c libburn/sg-linux.c New API function burn_scsi_transport_id() 26 Dec 2009 [2968] Makefile.am Added os-dummy.h and sg-dummy.h to libburn tarball 26 Dec 2009 [2969] svn copy -m Branching for libburn bugfix release 0.7.4.pl01 http://svn.libburnia-project.org/libburn/tags/ZeroSevenTwo http://svn.libburnia-project.org/libburn/branches/ZeroSevenTwoPl01 26 Dec 2009 [2970] svn mv -m Branching for libburn bugfix release 0.7.4.pl01 http://svn.libburnia-project.org/libburn/branches/ZeroSevenTwoPl01 http://svn.libburnia-project.org/libburn/branches/ZeroSevenFourPl01 26 Dec 2009 [2971] svn rm -m Branching for libburn bugfix release 0.7.4.pl01 http://svn.libburnia-project.org/libburn/branches/ZeroSevenFourPl01 26 Dec 2009 [2972] svn copy -m Branching for libburn bugfix release 0.7.4.pl01 http://svn.libburnia-project.org/libburn/tags/ZeroSevenFour http://svn.libburnia-project.org/libburn/branches/ZeroSevenFourPl01 2009.12.26.110001 [2973] README Makefile.am cdrskin/cdrskin_timestamp.h Bug fix: Added missing system adapter for generic X/Open to libburn release tarball 26 Dec 2009 [2974] svn move -m libburn bugfix release 0.7.4.pl01 is ready http://svn.libburnia-project.org/libburn/branches/ZeroSevenFourPl01 http://svn.libburnia-project.org/libburn/tags/ZeroSevenFourPl01 2009.12.26.193707 [2975] cdrskin/cdrskin.c Reporting burn_scsi_transport_id() in cdrskin as debug message 2009.12.26.222656 [2976] libburn/libburn.h doc/comments Reacted on some doxygen warnings of Debian hurd build 2009.12.27.092057 [2979] libburn/os-libcdio.h libburn/sg-libcdio.c Showing libburn users drive name link targets, using in libcdio its own names 2009.12.27.102342 [2981] libburn/sg-libcdio.c Shorter sg_initialize message with sg-libcdio 2009.12.27.144620 [2982] libburn/init.c libburn/drive.c libburn/sg.h libburn/os-libcdio.h libburn/sg-dummy.c libburn/sg-freebsd.c libburn/sg-freebsd-port.c libburn/sg-libcdio.c libburn/sg-linux.c Extended sg-API by sg_shutdown(), sg_dispose_drive(), sg_id_string() 2009.12.27.144733 [2983] cdrskin/cdrskin.c Reporting system adapter id with cdrskin -version 2009.12.29.115717 [2984] configure.ac Incremented middle .so number 2009.12.29.115854 [2985] libburn/spc.c Corrected a mode page size computation error which for now had no bad effect 2009.12.29.132537 [2986] acinclude.m4 libburn/os.h libburn/sg.c Experimentally enabled FreeBSD system adapter for Debian kfreebsd ------------------------------------ cycle - cdrskin-0.7.5 - 2009.12.29.134637 * Experimental SCSI transport adapter via GNU libcdio * Experimentally using FreeBSD system adapter for Debian kfreebsd * Bug fix: System adapter for generic X/Open was missing in libburn release tarball 2009.12.29.224506 [2987] acinclude.m4 Adaptions for Debian kfreebsd requested by Petr Salinger 2009.12.30.154140 [2988] libburn/drive.c libburn/sg-libcdio.c Making use of new libcdio capability to obtain SCSI address tuple on Linux 2009.12.30.201025 [2990] libburn/sg-libcdio.c Silenced libcdio warnings 2010.01.01.124042 [2994] libburn/drive.c Bug fix: with non-Linux adapters there were 0 readable bytes on block devices 2010.01.01.124415 [2995] libburn/sg-libcdio.c Enabled block device size recognition with sg-libcdio on Linux ------------------------------------ cycle - cdrskin-0.7.5 - 2010.01.01.143104 * Bug fix: with non-Linux adapters there were 0 readable bytes on block devices 2010.01.04.134949 [3001] libburn/write.c libburn/libdax_msgs.h Avoiding stream recording on BD if not 64 kB buffer 2010.01.04.135427 [3002] libburn/os-libcdio.h Enlarged buffer size of libcdio adapter on Linux to 64k 2010.01.09.142027 [3004] libburn/sg-libcdio.c Forgot to forward sense reply to higher levels 2010.01.09.142642 [3005] libburn/spc.c Better error message with unknown SCSI error codes 2010.01.09.143428 [3006] libburn/spc.c libburn/sbc.c Revoked asynchronous eject, as we cannot distinguish out from unready 2010.01.12.165214 [3009] libburn/sg-dummy.c libburn/sg-freebsd-port.c libburn/sg-libcdio.c libburn/sg-linux.c Corrected free capacity measurement of stdio: drives in regular files 12 Jan 2010 [3010] doc/cookbook.txt Fixed typos in MMC cookbook 2010.01.13.074028 [3011] libburn/drive.c Experimentally regard FreeBSD /dev/da[0-9] and /dev/cd[0-9] as block device 2010.01.13.074640 [3012] libburn/sg-freebsd.c Adaptions after encounter with FreeBSD 8.0 2010.01.13.171546 [3013] libburn/libburn.h Carified that apps must use 64 bit off_t or the lib must be tweaked. 14 Jan 2010 [3014] 14 Jan 2010 [3015] test/libburner.c Carified in libburner.c that apps must use 64 bit off_t. 2010.01.14.160633 [3016] libburn/libburn.h libburn/drive.c Giving up drive probing by mode page sending 2010.01.14.160748 [3017] libburn/sg-libcdio.c Provisory rejection of FreeBSD ATAPI drives in sg-libcdio 2010.01.15.182615 [3018] libburn/os-freebsd.h libburn/sg-freebsd.c Implemented adivisory FreeBSD drive locking via flock(2) 2010.01.16.125258 [3019] libburn/drive.c libburn/sg.h libburn/sg-dummy.c libburn/sg-freebsd.c libburn/sg-freebsd-port.c libburn/sg-libcdio.c libburn/sg-linux.c New OS adapter burn_os_is_2k_seekrw() replaces S_ISBLK() with pseudo-drives 2010.01.18.103410 [3023] libburn/sg-linux.c Changed a comment in sg-linux.c ------------------------------------ cycle - cdrskin-0.7.5 - 2010.01.18.104011 * Made FreeBSD system adapter safe from mutal burn spoiling and drive deadlock 21 Jan 2010 [3028] cdrskin/cdrskin.1 Changed man page example from -toc to -minfo 2010.01.21.104741 [3029] libburn/sg-freebsd.c libburn/sg-libcdio.c Learned how to inquire size of disk-like FreeBSD devices 22 Jan 2010 [3030] svn copy -m "Branching for libburn release 0.7.6" \ http://svn.libburnia-project.org/libburn/trunk \ http://svn.libburnia-project.org/libburn/branches/ZeroSevenSix 2010.01.22.130001 [] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.7.6 22 Jan 2010 [3032] COPYRIGHT cdrskin/cdrskin.c doc/cookbook.txt libburn/libdax_msgs.h libburn/libdax_msgs.c test/libburner.c test/telltoc.c Lifted ban to derive GPLv3, extended copyright range to 2010 22 Jan 2009 [3033] - cdrskin/add_ts_changes_to_libburn_0_7_4 - cdrskin/add_ts_changes_to_libburn_0_7_5 + cdrskin/add_ts_changes_to_libburn_0_7_6 + cdrskin/add_ts_changes_to_libburn_0_7_7 Updated cdrskin tarball generator 22 Jan 2009 [3034] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.7.6.pl00 - 2010.01.22.130001 * Bug fix: System adapter for generic X/Open was missing in libburn release tarball * Bug fix: with non-Linux adapters there were 0 readable bytes on block devices * Made FreeBSD system adapter safe from mutal burn spoiling and drive deadlock * Enabled FreeBSD system adapter for Debian kfreebsd * Experimental SCSI transport adapter via GNU libcdio 0.83git 2010.01.23.103338 [3035] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.7.7 2010.01.23.104423 [3036] COPYRIGHT doc/cookbook.txt libburn/libdax_msgs.h libburn/libdax_msgs.c test/libburner.c test/telltoc.c Lifted ban to derive GPLv3, extended copyright range to 2010 23 Jan 2010 [3037] - cdrskin/add_ts_changes_to_libburn_0_7_4 - cdrskin/add_ts_changes_to_libburn_0_7_5 + cdrskin/add_ts_changes_to_libburn_0_7_6 + cdrskin/add_ts_changes_to_libburn_0_7_7 Updated cdrskin tarball generator ------------------------------------ cycle - cdrskin-0.7.7 - 2010.01.23.104423 23 Jan 2010 [3038] svn move -m 'libburn release 0.7.6 is ready' http://svn.libburnia-project.org/libburn/branches/ZeroSevenSix http://svn.libburnia-project.org/libburn/tags/ZeroSevenSix 2010.02.04.083315 [3054] acinclude.m4 configure.ac Forcing use of /usr/local on FreeBSD by LDFLAGS and CPPFLAGS 2010.02.12.173236 [3063] libburn/os-linux.h libburn/sg-linux.c Changed system adapter id and some remarks from "Linux" to "GNU/Linux" 2010.02.12.212818 [3064] libburn/libburn.h libburn/sg.c libburn/mmc.c libburn/drive.c libburn/init.c libburn/cleanup.c libburn/os-linux.h libburn/sg-linux.c libburn/write.c libburn/read.c libburn/sg-libcdio.c libburn/os-libcdio.h libburn/os.h libburn/toc.c Changed docs and comments to "GNU/Linux" where appropriate 2010.02.14.084452 [3066] libburn/sbc.c libburn/file.h libburn/os-libcdio.h libburn/os-dummy.h libburn/cleanup.h libburn/sector.h libburn/libiso_msgs.c libburn/async.c libburn/libdax_audioxtr.h libburn/ecma130ab.c libburn/back_hacks.h libburn/libdax_msgs.h libburn/drive.h libburn/read.c libburn/source.c libburn/util.h libburn/cleanup.c libburn/sg.c libburn/init.c libburn/write.c libburn/transport.h libburn/write.h libburn/libburn.h libburn/options.c libburn/mmc.h libburn/sg.h libburn/sbc.h libburn/sg-dummy.c libburn/ecma130ab.h libburn/null.c libburn/structure.c libburn/mmc.c libburn/spc.h libburn/drive.c libburn/sg-linux.c libburn/options.h libburn/os-linux.h libburn/sg-libcdio.c libburn/os-freebsd.h libburn/sg-freebsd-port.c libburn/sector.c libburn/debug.c libburn/util.c libburn/toc.h libburn/file.c libburn/libdax_audioxtr.c libburn/libdax_msgs.c libburn/toc.c libburn/sg-freebsd.c libburn/spc.c libburn/structure.h Added or adjusted copyright and license statements in single files 2010.02.14.171833 [3069] libburn/write.c libburn/read.c libburn/sector.c libburn/crc.h Created opportunity to omit source module libburn/crc.c 2010.02.15.125922 [3071] libburn/crc.h Changed a comment in libburn/crc.h 2010.02.16.194147 [3073] libburn/file.c Bug fix on FreeBSD: Piped input was falsely attributed a small fixed size 2010.02.17.141409 [3075] libburn/async.c libburn/drive.c libburn/write.c Avoided random percentage display at start of blanking 2010.02.22.134904 [3080] libburn/init.c Made burn_set_signal_handling() more suitable for cdrskin 2010.02.25.070635 [3090] libburn/write.c Corrected optional speed curb for stdio: drives. Was damaged by revision 2903. 2010.02.28.104003 [3091] cdrskin/cdrskin.c Added forgotten initialization of a variable 2010.02.28.110749 [3092] cdrskin/cdrskin.c Bug fix: cdrskin fs=0 lead to SIGSEGV. Regression introduced by revision 2936. 28 Feb 2010 [3093] cdrskin/cdrskin.1 Corrected spelling errors in cdrskin man page 2010.03.03.140639 [3096] libburn/drive.c Enabled patience 0 within burn_abort() 2010.03.03.141407 [3097] cdrskin/cdrskin.c cdrskin/cdrfifo.c Adapted cdrskin abort handler to FreeBSD peculiarities 3 Mar 2010 [3098] [3099] cdrskin/compile_cdrskin.sh Enabled static compile script compile_cdrskin.sh for FreeBSD 2010.03.04.121441 [3100] libburn/sg-linux.c Showing more patience with temporarily busy drives on Linux 2010.03.04.180102 [3101] cdrskin/cdrskin.c Changed burn_abort(0) to burn_abort(-1) 2010.03.05.090948 [3102] libburn/libburn.h libburn/transport.h libburn/drive.h libburn/drive.c libburn/init.h libburn/init.c libburn/async.c libburn/write.c libburn/sector.c libburn/libdax_msgs.h libburn/libdax_msgs.c Introduced alternative signal handling actions 2010.03.05.091432 [3103] cdrskin/cdrskin.c Enabled optional use of new signal action 2 with libburn built-in handler 2010.03.05.111712 [3104] libburn/init.c libburn/drive.h libburn/drive.c Removed some debugging printing ------------------------------------ cycle - cdrskin-0.7.7 - 2010.03.05.111954 Bug fix on FreeBSD: Piped input was falsely attributed a small fixed size Bug fix: cdrskin fs=0 led to SIGSEGV. Regression introduced by revision 2936. 2010.03.05.190110 [3105] libburn/async.c Protected blanker and formatter thread from signals 06 Mar 2010 [3106] test/libburner.c Adapted libburner to new advise about signal handling 07 Mar 2010 [3107] libburn/libburn.h test/libburner.c Changed examples burn_set_signal_handling(...,48) to (...,0x30) 2010.03.08.092608 [3108] libburn/libburn.h libburn/init.c Prevented potential memory fault with burn_set_signal_handling() 2010.03.09.140341 [3111] libburn/libburn.h Clarifications in API description of burn_set_signal_handling() 10 Mar 2010 [3112] svn copy -m "Branching for libburn release 0.7.8" \ http://svn.libburnia-project.org/libburn/trunk \ http://svn.libburnia-project.org/libburn/branches/ZeroSevenEight 2010.03.10.120001 [3113] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.7.8 10 Mar 2010 [3114] - cdrskin/add_ts_changes_to_libburn_0_7_6 - cdrskin/add_ts_changes_to_libburn_0_7_7 + cdrskin/add_ts_changes_to_libburn_0_7_8 + cdrskin/add_ts_changes_to_libburn_0_7_9 Updated cdrskin tarball generator 10 Mar 2010 [3115] cdrskin/cdrskin.c Removed unused variable 10 Mar 2010 [3116] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.7.8.pl00 - 2010.03.10.120001 Bug fix on FreeBSD: Piped input was falsely attributed a small fixed size Bug fix: cdrskin fs=0 led to SIGSEGV. Regression introduced by version 0.7.4. 2010.03.10.134802 [3117] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.7.9 10 Mar 2010 [3118] - cdrskin/add_ts_changes_to_libburn_0_7_6 - cdrskin/add_ts_changes_to_libburn_0_7_7 + cdrskin/add_ts_changes_to_libburn_0_7_8 + cdrskin/add_ts_changes_to_libburn_0_7_9 Updated cdrskin tarball generator 10 Mar 2010 [3119] cdrskin/changelog.txt Documented changes and release timestamp 10 Mar 2010 [3120] svn move -m 'libburn release 0.7.8 is ready' http://svn.libburnia-project.org/libburn/branches/ZeroSevenEight http://svn.libburnia-project.org/libburn/tags/ZeroSevenEight ------------------------------------ cycle - cdrskin-0.7.9 - 2010.03.10.143607 2010.03.17.185222 [3123] cdrskin/cdrskin.c Small bug fix about track size with cdrskin -minfo 17 Mar 2010 [3124] configure.ac Corrected initialization of configure option --enable-dvd-obs-64k 2010.03.25.113536 [3131] libburn/spc.c libburn/sg-freebsd.c Changed sg-freebsd.c to work with ahci, advise by Alexander Motin 2010.03.26.083158 [3132] libburn/sg-freebsd.c Had to make ahci change conditional for now: -DLibburn_for_freebsd_ahcI 2010.03.27.155659 [3133] libburn/mmc.c Avoiding to inquire NWA of unwritable media or states 2010.03.27.172644 [3134] libburn/os-freebsd.h libburn/sg-freebsd.c Trying to detect FreeBSD ahci devices and to handle others the old way ------------------------------------ cycle - cdrskin-0.7.9 - 2010.03.27.184614 * Now able to work with ahci driver of FreeBSD 8-STABLE 2010.03.29.103141 [3135] libburn/spc.c libburn/sg-linux.c libburn/sg-freebsd.c libburn/sg-libcdio.c Adjusted libcdio system adapter to FreeBSD peculiarities 2010.04.04.181237 [3146] test/libburner.c Let libburner warn programmers if they forget to set 64 bit off_t 2010.04.09.090645 [3155] libburn/sg-linux.c Reporting eventual SCSI sense in sg-linux repeat loop 9 Apr 2010 [3156] svn copy -m "Branching for libburn release 0.8.0" \ http://svn.libburnia-project.org/libburn/trunk \ http://svn.libburnia-project.org/libburn/branches/ZeroEightZero 2010.04.09.100001 [3157] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.8.0 09 Apr 2010 [3158] - cdrskin/add_ts_changes_to_libburn_0_7_8 - cdrskin/add_ts_changes_to_libburn_0_7_9 + cdrskin/add_ts_changes_to_libburn_0_8_0 + cdrskin/add_ts_changes_to_libburn_0_8_1 Updated cdrskin tarball generator 09 Apr 2010 [3159] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.8.0.pl00 - 2010.04.09.100001 * Now able to work with ahci driver of FreeBSD 8-STABLE 2010.04.09.104907 [3160] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.8.1 09 Apr 2010 [3161] - cdrskin/add_ts_changes_to_libburn_0_7_8 - cdrskin/add_ts_changes_to_libburn_0_7_9 + cdrskin/add_ts_changes_to_libburn_0_8_0 + cdrskin/add_ts_changes_to_libburn_0_8_1 Updated cdrskin tarball generator 09 Apr 2010 [3162] cdrskin/changelog.txt Documented changes and release timestamp 09 Apr 2009 [3163] svn move -m libburn release 0.8.0 is ready http://svn.libburnia-project.org/libburn/branches/ZeroEightZero http://svn.libburnia-project.org/libburn/tags/ZeroEightZero ------------------------------------ cycle - cdrskin-0.8.1 - 2010.04.09.110341 2010.04.30.180350 [3206] libburn/mmc.c Avoided to create track without toc_entry from "hidden first track" on CD ------------------------------------ cycle - cdrskin-0.8.1 - 2010.04.30.182846 * Bug fix: CD TOC was not read if the first track did not start at LBA 0 2010.05.01.082808 [3207] libburn/util.c libburn/mmc.c Bug fix: CD-ROM media got attributed random lead-in and lead-out adresses ------------------------------------ cycle - cdrskin-0.8.1 - 2010.05.01.083316 * Bug fix: CD-ROM media got attributed random lead-in and lead-out adresses 2010.05.16.090624 [3219] libburn/async.c libburn/cleanup.c libburn/crc.c libburn/ddlpa.c libburn/debug.c libburn/drive.c libburn/ecma130ab.c libburn/file.c libburn/init.c libburn/libdax_audioxtr.c libburn/libdax_msgs.c libburn/mmc.c libburn/null.c libburn/options.c libburn/read.c libburn/sbc.c libburn/sector.c libburn/sg.c libburn/sg-dummy.c libburn/sg-freebsd.c libburn/sg-freebsd-port.c libburn/sg-libcdio.c libburn/sg-linux.c libburn/source.c libburn/spc.c libburn/structure.c libburn/toc.c libburn/util.c libburn/write.c Eventually including ../config.h generated by autotools 2010.05.29.074318 [3237] libburn/sg-libcdio.c Bug fix: SIGSEGV of libcdio system adapter if drive list is empty 2010.06.07.171552 [3239] libburn/sg-libcdio.c Avoiding to resolve /dev/rdsk/cXtYdZs2 drive addresses on Solaris libcdio 2010.06.07.171706 [3240] libburn/init.c cdrskin/cdrskin.c Reacted on harmless compiler warnings on Solaris 2010.06.07.172925 [3241] README cdrskin/README Makefile.am libburn/os.h libburn/sg.c + libburn/os-solaris.h + libburn/sg-solaris.c libburn/libdax_msgs.h cdrskin/cdrskin_eng.html New system adapter for Solaris uscsi (tested on snv134, kernel 5.11) 2010.06.08.173156 [3244] libburn/os-solaris.h Corrected comment 2010.06.08.174946 [3245] libburn/sg-solaris.c Handled SCSI status "BUSY" on Solaris ------------------------------------ cycle - cdrskin-0.8.1 - 2010.06.08.175125 * Bug fix: SIGSEGV of libcdio system adapter if drive list is empty * New system adapter for Solaris uscsi (tested on snv134, kernel 5.11) 2010.06.08.200313 [3246] libburn/sg-libcdio.c libburn/sg-solaris.c Implemented block device capacity determination fo Solaris 2010.06.10.170901 [3247] libburn/sg-solaris.c Removed obsolete development remark 2010.06.10.171018 [3248] README cdrskin/README cdrskin/cdrskin.1 Mentioned Solaris and system dependent drive permission settings [] svn copy -m "Branching for libburn release 0.8.2" \ http://svn.libburnia-project.org/libburn/trunk \ http://svn.libburnia-project.org/libburn/branches/ZeroEightTwo 2010.06.11.080001 [3251] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.8.2 11 Jun 2010 [3252] - cdrskin/add_ts_changes_to_libburn_0_8_0 - cdrskin/add_ts_changes_to_libburn_0_8_1 + cdrskin/add_ts_changes_to_libburn_0_8_2 + cdrskin/add_ts_changes_to_libburn_0_8_3 Updated cdrskin tarball generator 11 Jun 2010 [3253] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.8.2.pl00 - 2010.06.11.080001 * Bug fix: CD TOC was not read if the first track did not start at LBA 0 * Bug fix: CD-ROM media got attributed random lead-in and lead-out adresses * Bug fix: SIGSEGV of experimental libcdio system adapter if drive list is empty * New system adapter for Solaris uscsi (tested on snv134, kernel 5.11) 2010.06.11.104830 [3254] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.8.3 11 Jun 2010 [3255] - cdrskin/add_ts_changes_to_libburn_0_8_0 - cdrskin/add_ts_changes_to_libburn_0_8_1 + cdrskin/add_ts_changes_to_libburn_0_8_2 + cdrskin/add_ts_changes_to_libburn_0_8_3 Updated cdrskin tarball generator 11 Jun 2010 [3256] cdrskin/changelog.txt Documented changes and release timestamp 11 Jun 2010 [3257] svn move -m libburn release 0.8.2 is ready http://svn.libburnia-project.org/libburn/branches/ZeroEightTwo http://svn.libburnia-project.org/libburn/tags/ZeroEightTwo ------------------------------------ cycle - cdrskin-0.8.3 - 2010.06.11.104830 2010.06.13.190707 [3267] libburn/sg-solaris.c New less obtrusive implementation of sg_is_enumerable_adr for Solaris 2010.06.15.155423 [3272] libburn/sg-libcdio.c Avoided compiler warning about unused variable on non-Solaris systems 2010.06.15.155625 [3273] libburn/os-dummy.h libburn/os-libcdio.h Let general POSIX system adapters ignore SIGWINCH and SIGURG if defined 2010.06.15.155739 [3274] configure.ac Incremented LT_CURRENT and LT_AGE of libburn 2010.06.16.082457 [3277] libburn/os-solaris.h libburn/os-libcdio.h libburn/os-freebsd.h Allowed 64 kB max output buffer size on all OSes 30 Jun 2010 [3307] svn copy -m "Branching for libburn release 0.8.4" \ http://svn.libburnia-project.org/libburn/trunk \ http://svn.libburnia-project.org/libburn/branches/ZeroEightFour 2010.06.30.100001 [3308] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.8.4 30 Jun 2010 [3309] - cdrskin/add_ts_changes_to_libburn_0_8_2 - cdrskin/add_ts_changes_to_libburn_0_8_3 + cdrskin/add_ts_changes_to_libburn_0_8_4 + cdrskin/add_ts_changes_to_libburn_0_8_5 Updated cdrskin tarball generator 30 Jun 2010 [3310] cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.8.4.pl00 - 2010.06.30.100001 * Let general POSIX system adapters ignore SIGWINCH and SIGURG if defined * Allowed 64 kB max output buffer size on all OSes 2010.06.30.112709 [3311] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.8.5 30 Jun 2010 [3312] - cdrskin/add_ts_changes_to_libburn_0_8_2 - cdrskin/add_ts_changes_to_libburn_0_8_3 + cdrskin/add_ts_changes_to_libburn_0_8_4 + cdrskin/add_ts_changes_to_libburn_0_8_5 Updated cdrskin tarball generator 30 Jun 2010 [3313] cdrskin/changelog.txt Documented changes and release timestamp 30 Jun 2010 [3314] svn move -m libburn release 0.8.4 is ready \ http://svn.libburnia-project.org/libburn/branches/ZeroEightFour \ http://svn.libburnia-project.org/libburn/tags/ZeroEightFour ------------------------------------ cycle - cdrskin-0.8.5 - 2010.06.30.112709 2010.07.04.131219 [3323] libburn/libburn.h libburn/libdax_audioxtr.h libburn/libdax_audioxtr.c cdrskin/cdrskin.c test/dewav.c Moved public part of libdax_audioxtr.h to libburn.h 2010.07.04.170035 [3325] configure.ac Makefile.am + libburn/libburn.ver Hiding all non-API symbols from the linker by use of --version-script 04 Jul [3326] README Mentioned new configure option --disable-versioned-libs 2010.07.06.113304 [3329] configure.ac acinclude.m4 Let configure perform linker test with --version-script if enabled 2010.07.06.113356 [3330] libburn/libburn.h Mentioned that public API calls must be in libisofs/libisofs.ver 2010.07.12.193201 [3334] cdrskin/cdrskin.c cdrskin/cdrfifo.c Changed all malloc() to calloc() 2010.07.12.193644 [3335] libburn/async.c libburn/drive.c libburn/file.c libburn/libdax_audioxtr.c libburn/libdax_msgs.c libburn/mmc.c libburn/null.c libburn/options.c libburn/read.c libburn/sg-freebsd.c libburn/sg-freebsd-port.c libburn/util.c libburn/write.c Changed all malloc() to calloc() 2010.07.29.083453 [3336] libburn/spc.h libburn/spc.c libburn/mmc.c libburn/sg-freebsd.c libburn/sg-solaris.c libburn/sg-libcdio.c Recognizing sense data format 0x72 if given instead of 0x70 2010.07.29.164100 [3337] Makefile.am Detached make target "doc" from target "all". 30 Jul 2010 [3341] doc/doxygen.conf.in Removed problematic DETAILS_AT_TOP to silence warning of Debian buildd 2010.08.02.100630 [3345] libburn/spc.h libburn/spc.c libburn/sg-linux.c libburn/sg-freebsd.c libburn/sg-libcdio.c Reporting sense data with burn_set_scsi_logging() 2010.08.02.131946 [3346] libburn/sg-linux.c Added error simulation code to Linux system adapter 2010.08.02.141233 [3347] libburn/sg-solaris.c Committed Solaris system adapter which was forgotten with rev 3345 ------------------------------------ cycle - cdrskin-0.8.5 - 2010.08.02.141233 * Hiding all non-API symbols from the linker by use of --version-script 2010.08.03.191151 [3348] libburn/sg-linux.c libburn/sg-freebsd.c libburn/sg-libcdio.c libburn/sg-solaris.c Obeying burn_set_scsi_logging() with errors of class RETRY 2010.08.08.091224 [3349] libburn/spc.h libburn/spc.c libburn/sg-linux.c libburn/sg-freebsd.c libburn/sg-libcdio.c libburn/sg-solaris.c New SCSI comand response "GO_ON" 2010.08.08.091317 [3350] libburn/libburn.h Hopefully silenced a warning of doxygen on Debian buildd 2010.08.13.114139 [3352] libburn/libdax_msgs.h libburn/libdax_audioxtr.h Corrected typo in macro names (which shall never be defined anyway) 2010.08.21.095456 [3356] libburn/libburn.h libburn/mmc.c cdrskin/cdrskin.1 cdrskin/cdrskin_eng.html Lifted test reservation on DVD-R DL media. Thanks to Kevin Kieffer for testing. 28 Aug 2010 [3358] test/libburner.c Clarified the meaning of 0x0 and 0x30 signal handlers 2010.09.14.124934 [3368] libburn/spc.h libburn/spc.c libburn/sg-linux.c libburn/sg-libcdio.c libburn/sg-freebsd.c libburn/sg-solaris.c Centralized interpretation of SCSI command outcome ------------------------------------ cycle - cdrskin-0.8.5 - 2010.09.14.135405 * Lifted test reservation on DVD-R DL media. 15 Sep 2010 [3369] ChangeLog Meaningful change log file derived by George Danchev from web site 16 Sep 2010 [3373] svn copy -m Branching for libburn release 0.8.6 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/ZeroEightSix 2010.09.16.113001 [3374] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.8.6 16 Sep 2010 [3375] - cdrskin/add_ts_changes_to_libburn_0_8_4 - cdrskin/add_ts_changes_to_libburn_0_8_5 + cdrskin/add_ts_changes_to_libburn_0_8_6 + cdrskin/add_ts_changes_to_libburn_0_8_7 Updated cdrskin tarball generator 16 Sep 2010 [3376] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.8.6.pl00 - 2010.09.16.113001 * Lifted test reservation on DVD-R DL media. * Hiding all non-API symbols from the linker by use of --version-script 2010.09.17.073827 [3377] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.8.7 17 Sep 2010 [3378] - cdrskin/add_ts_changes_to_libburn_0_8_4 - cdrskin/add_ts_changes_to_libburn_0_8_5 + cdrskin/add_ts_changes_to_libburn_0_8_6 + cdrskin/add_ts_changes_to_libburn_0_8_7 Updated cdrskin tarball generator 17 Sep 2010 [3379] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp 17 Sep 2010 [3381] svn move -m libburn release 0.8.6 is ready http://svn.libburnia-project.org/libburn/branches/ZeroEightSix http://svn.libburnia-project.org/libburn/tags/ZeroEightSix ------------------------------------ cycle - cdrskin-0.8.7 - 2010.09.17.082926 2010.09.22.105426 [3395] acinclude.m4 configure.ac Makefile.am README On Linux: Run ldconfig during make install,if not --disable-ldconfig-at-install 2010.09.22.175054 [3397] libburn/libburn.h libburn/libburn.ver libburn/libdax_msgs.h libburn/file.h libburn/file.c libburn/source.h libburn/source.c New API call burn_offst_source_new() 2010.09.22.180921 [3398] Makefile.am + test/offst_source.c Temporarily added test program for burn_offst_source_new() 22 Sep 2010 [3399] test/offst_source.c Better default input file for test/offst_source.c 2010.09.24.090631 [3401] libburn/libburn.h libburn/drive.c libburn/mmc.h libburn/mmc.c ChangeLog New API call burn_disc_get_bd_spare_info() 2010.09.24.090731 [3402] cdrskin/cdrskin.c Displaying eventual BD spare area information with -minfo 2010.09.24.091902 [3403] cdrskin/cdrskin.c Displaying eventual BD spare area information with --list_formats 2010.09.24.092535 [3404] libburn/libburn.ver Making new API call available in dynamic library 2010.09.24.100255 [3405] cdrskin/cdrskin.c Polished appearance of BD spare info with --list_formats ------------------------------------ cycle - cdrskin-0.8.7 - 2010.09.24.100255 * New API call burn_offst_source_new() * New API call burn_disc_get_bd_spare_info() 2010.09.28.101043 [3408] libburn/mmc.c Avoiding to inquire spare area of unsuitable media 2010.10.15.191717 [3445] libburn/write.c libburn/libdax_msgs.h Issue warning after writing a BD-R with more than 300 sessions 2010.10.19.165908 [3450] libburn/async.c libburn/write.c libburn/libdax_msgs.h Issueing messages with all cases of burn canceling 20 Oct 2010 [3453] svn copy -m Branching for libburn release 0.8.8 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/ZeroEightEight 2010.10.20.120001 [3454] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.8.8 20 Oct 2010 [3455] - cdrskin/add_ts_changes_to_libburn_0_8_6 - cdrskin/add_ts_changes_to_libburn_0_8_7 + cdrskin/add_ts_changes_to_libburn_0_8_8 + cdrskin/add_ts_changes_to_libburn_0_8_9 Updated cdrskin tarball generator 20 Oct 2010 [3456] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.8.8.pl00 - 2010.10.20.120001 * New API call burn_offst_source_new() * New API call burn_disc_get_bd_spare_info() 2010.10.20.125207 [3457] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.8.9 20 Oct 2010 [3458] - cdrskin/add_ts_changes_to_libburn_0_8_6 - cdrskin/add_ts_changes_to_libburn_0_8_7 + cdrskin/add_ts_changes_to_libburn_0_8_8 + cdrskin/add_ts_changes_to_libburn_0_8_9 Updated cdrskin tarball generator 20 Oct 2010 [3459] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp 20 Oct 2010 svn move -m libburn release 0.8.8 is ready http://svn.libburnia-project.org/libburn/branches/ZeroEightEight http://svn.libburnia-project.org/libburn/tags/ZeroEightEight ------------------------------------ cycle - cdrskin-0.8.9 - 2010.10.20.134102 2010.10.29.164059 [3474] libburn/mmc.c libburn/libdax_msgs.h Issueing error messages if cache syncing or closing fails 2010.10.29.174455 [3476] libburn/spc.c Regression fix: SCSI reply data logging was disabled in rev 3368, 0.8.6 2010.11.16.131221 [3483] libburn/sg-linux.c libburn/sg-freebsd.c libburn/sg-libcdio.c libburn/sg-solaris.c Removed outdated development macros 08 Dec 2010 [3502] svn copy -m Branching for libburn release 0.9.0 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/ZeroNineZero 2010.12.08.133001 [3503] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.9.0 08 Dec 2010 [3504] - cdrskin/add_ts_changes_to_libburn_0_8_8 - cdrskin/add_ts_changes_to_lisburn_0_8_9 + cdrskin/add_ts_changes_to_libburn_0_9_0 + cdrskin/add_ts_changes_to_libburn_0_9_1 Updated cdrskin tarball generator 08 Dec 2010 [3505] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-0.9.0.pl00 - 2010.12.08.133001 Regression fix: SCSI reply data logging was disabled in release 0.8.6 2010.12.08.131934 [3506] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 0.9.1 08 Dec 2010 [3507] - cdrskin/add_ts_changes_to_libburn_0_8_8 - cdrskin/add_ts_changes_to_lisburn_0_8_9 + cdrskin/add_ts_changes_to_libburn_0_9_0 + cdrskin/add_ts_changes_to_libburn_0_9_1 Updated cdrskin tarball generator 08 Dec 2010 [3508] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp 08 Dec 2010 [3509] svn move -m libburn release 0.9.0 is ready http://svn.libburnia-project.org/libburn/branches/ZeroNineZero http://svn.libburnia-project.org/libburn/tags/ZeroNineZero ------------------------------------ cycle - cdrskin-0.9.1 - 2010.12.08.154833 2010.12.13.075956 [3522] configure.ac Prepending ./configure generated options to CFLAGS rather than appending them 23 Dec 2010 [3528] doc/comments Updated API introduction 2010.12.28.071904 [3537] libburn/write.c Allowed umask to create stdio-drive files with rw-permissions for all 2011.01.03.195125 [3542] libburn/write.c libburn/sector.c libburn/structure.h libburn/structure.c Allowed stdio tracks of known size to end in TAO mode on premature EOF 2011.01.09.135915 [3546] cdrskin/cdrskin.c cdrskin/cdrskin.1 Refusing to burn if foreseeable size exceeds media capacity ------------------------------------ cycle - cdrskin-0.9.1 - 2011.01.09.140240 * Allowed umask to create stdio-drive files with rw-permissions for all * cdrskin now refuses to burn if the foreseeable size exceeds media capacity 09 Jan 2011 [3547] cdrskin/cdrskin_eng.html ChangeLog Updated change log and web page 16 Jan 2011 [3548] svn copy -m "Branching for libburn release 1.0.0" \ http://svn.libburnia-project.org/libburn/trunk \ http://svn.libburnia-project.org/libburn/1.0.0" 2011.01.16.123001 [3549] [3550] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.0.0 16 Jan 2011 [3551] - cdrskin/add_ts_changes_to_libburn_0_9_0 - cdrskin/add_ts_changes_to_libburn_0_9_1 + cdrskin/add_ts_changes_to_libburn_1_0_0 + cdrskin/add_ts_changes_to_libburn_1_0_1 Updated cdrskin tarball generator 16 Jan 2011 [3552] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-1.0.0.pl00 - 2011.01.16.123001 * Allowed umask to create stdio-drive files with rw-permissions for all * cdrskin now refuses to burn if the foreseeable size exceeds media capacity 2011.01.16.140456 [3553] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.0.1 16 Jan 2011 [3554] - cdrskin/add_ts_changes_to_libburn_0_9_0 - cdrskin/add_ts_changes_to_libburn_0_9_1 + cdrskin/add_ts_changes_to_libburn_1_0_0 + cdrskin/add_ts_changes_to_libburn_1_0_1 Updated cdrskin tarball generator 16 Jan 2011 [3555] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp 16 Jan 2011 [3556] svn move -m "libburn release 1.0.0 is ready" \ http://svn.libburnia-project.org/libburn/branches/1.0.0 \ http://svn.libburnia-project.org/libburn/tags/1.0.0 ------------------------------------ cycle - cdrskin-1.0.1 - 2011.01.16.152923 2011.01.18.162859 [3570] libburn/file.c Using usleep() instead of nanosleep() which is not available on Solaris 9 2011.02.09.114311 [3586] libburn/write.c libburn/drive.c Forced role 3 on drives which stem from open file descriptors without O_RDWR 2011.02.14.085951 [3589] libburn/drive.c Reacted on compiler warnings about uninitialized variables 2011.02.18.165542 [3592] libburn/drive.c libburn/sector.c DEBUG message with burn_drive_cancel, FAILURE with premature end-of-input 23 Feb 2011 [3604] svn copy -m Branching for libburn release 1.0.2 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.0.2 2011.02.23.130001 [3605] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.0.2 23 Feb 2011 [3606] - cdrskin/add_ts_changes_to_libburn_1_0_0 - cdrskin/add_ts_changes_to_libburn_1_0_1 + cdrskin/add_ts_changes_to_libburn_1_0_2 + cdrskin/add_ts_changes_to_libburn_1_0_3 Updated cdrskin tarball generator 23 Feb 2011 [3607] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-1.0.2.pl00 - 2011.02.23.130001 * Removed compilation obstacles on Solaris 9. * Improved recognition of non-seekable stdio pseudo-drives. 2011.02.23.193502 [3611] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.0.3 23 Feb 2011 [3612] - cdrskin/add_ts_changes_to_libburn_1_0_0 - cdrskin/add_ts_changes_to_libburn_1_0_1 + cdrskin/add_ts_changes_to_libburn_1_0_2 + cdrskin/add_ts_changes_to_libburn_1_0_3 Updated cdrskin tarball generator 23 Feb 2011 [3613] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-1.0.3 - 2011.02.23.193502 23 Feb 2011 [3614] svn move -m libburn release 1.0.2 is ready http://svn.libburnia-project.org/libburn/branches/1.0.2 http://svn.libburnia-project.org/libburn/tags/1.0.2 2011.02.23.195650 [3615] libburn/libdax_msgs.h Registered new error code 2011.02.24.191718 [3619] libburn/async.c Corrected a flaw found by George Danchev with cpp 2011.03.01.144625 [3625] libburn/drive.c Bug fix: Read-only file descriptors were classified as write-only pseudo drives svn copy -m Branching for libburn release 1.0.4 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.0.4 2011.03.10.080001 [3651] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.0.4 10 Mar 2011 [3652] - cdrskin/add_ts_changes_to_libburn_1_0_2 - cdrskin/add_ts_changes_to_libburn_1_0_3 + cdrskin/add_ts_changes_to_libburn_1_0_4 + cdrskin/add_ts_changes_to_libburn_1_0_5 Updated cdrskin tarball generator 10 Mar 2011 [3653] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-1.0.4.pl00 - 2011.03.10.080001 * Bug fix: Read-only file descriptors were classified as write-only pseudo drives 2011.03.10.132603 [3657] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.0.5 10 Mar 2011 [3659] - cdrskin/add_ts_changes_to_libburn_1_0_2 - cdrskin/add_ts_changes_to_libburn_1_0_3 + cdrskin/add_ts_changes_to_libburn_1_0_4 + cdrskin/add_ts_changes_to_libburn_1_0_5 Updated cdrskin tarball generator 10 Mar 2011 [3660] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp 10 Mar 2011 [3658] svn move -m libburn release 1.0.4 is ready http://svn.libburnia-project.org/libburn/branches/1.0.4 http://svn.libburnia-project.org/libburn/tags/1.0.4 ------------------------------------ cycle - cdrskin-1.0.5 - 2011.03.10.132603 12 Mar 2011 [3664] COPYRIGHT Updated copyright year 2011.03.12.093520 [3665] libburn/write.c libburn/mmc.c Burning DVD-R DAO with 2 kB size granularity rather than 32 kB 2011.03.13.130746 [3666] libburn/libburn.h libburn/init.c libburn/drive.c libburn/async.c libburn/write.c libburn/libdax_msgs.h libburn/libburn.ver New API call burn_allow_drive_role_4() 2011.03.13.130850 [3667] cdrskin/cdrskin.c Using burn_allow_drive_role_4() in cdrskin 2011.03.13.192627 [3671] libburn/read.c libburn/libdax_msgs.h Changed severity of "Read attempt on write-only drive" from FATAL to FAILURE 2011.03.18.093128 [3672] cdrskin/cdrskin.c Prepared cdrskin for drive role 5 2011.03.18.093410 [3673] libburn/transport.h libburn/drive.c libburn/async.c libburn/write.c libburn/read.c libburn/libdax_msgs.h Provisory introduction of drive role 5, random access write-only 2011.03.18.153326 [3674] libburn/async.c libburn/mmc.c Enabled BD formatting by index on Pioneer BDR-205 which offers no Cert or QCert 2011.03.19.222152 [3675] libburn/sector.c Silenced an error message with input that is not aligned to 2 kB 2011.03.21.090313 [3676] libburn/drive.c Corrected nwa computation for drive role 5 2011.03.21.090430 [3677] libburn/write.c Adjustments for drive role 5, random access write-only 2011.03.21.092220 [3678] libburn/async.c Enabled blanking of drive with role 5 2011.03.22.085956 [3685] libburn/libburn.h libburn/init.c libburn/drive.c Avoiding appendable role 5 if not explicitely enabled 2011.03.24.182148 [3687] libburn/libburn.h libburn/read.c libburn/libdax_msgs.h Better handling of read attempt on pseudo-drive without read-permission 8 Apr 2011 [3712] svn copy -m Branching for libburn release 1.0.6 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.0.6 2011.04.08.180001 [3713] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.0.6 08 Apr 2011 [3714] - cdrskin/add_ts_changes_to_libburn_1_0_4 - cdrskin/add_ts_changes_to_libburn_1_0_5 + cdrskin/add_ts_changes_to_libburn_1_0_6 + cdrskin/add_ts_changes_to_libburn_1_0_7 Updated cdrskin tarball generator 08 Apr 2011 [3715] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ------------------------------ release - cdrskin-1.0.6.pl00 - 2011.04.08.180001 * Burning DVD-R DAO with 2 kB size granularity rather than 32 kB * New API call burn_allow_drive_role_4() 2011.04.09.090001 [3719] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.0.7 09 Apr 2011 [3720] - cdrskin/add_ts_changes_to_libburn_1_0_4 - cdrskin/add_ts_changes_to_libburn_1_0_5 + cdrskin/add_ts_changes_to_libburn_1_0_6 + cdrskin/add_ts_changes_to_libburn_1_0_7 Updated cdrskin tarball generator 09 Apr 2011 [3721] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp 09 Apr 2011 [3722] svn move -m libburn release 1.0.6 is ready http://svn.libburnia-project.org/libburn/branches/1.0.6 http://svn.libburnia-project.org/libburn/tags/1.0.6 ------------------------------------ cycle - cdrskin-1.0.7 - 2011.04.09.090001 15 Apr 2011 [3735] doc/doxygen.conf.in Disabled HAVE_DOT in doxygen.conf 2011.05.01.144546 [3743] libburn/init.c Closed tiny memory leak found by valgrind 2011.05.12.120503 [3791] libburn/drive.h Including header pthread.h on request of Mats Andersson for OpenBSD 2011.05.12.134723 [3792] libburn/init.c libburn/drive.c libburn/structure.c libburn/mmc.c libburn/sg-linux.c Reacted on -Wsign-compare warnings of gcc 2011.05.12.135008 [3793] cdrskin/cdrskin.c Reacted on -Wsign-compare warnings of gcc 2011.05.12.135116 [3794] test/libburner.c Reacted on -Wsign-compare warnings of gcc 2011.05.12.135217 [3795] test/telltoc. Reacted on -Wsign-compare warnings of gcc 12 May 2011 [3796] test/poll.c Reacted on -Wsign-compare warnings of gcc 14 May 2011 [3803] Makefile.am doc/cookbook.txt doc/mediainfo.txt Added cookbook.txt and mediainfo.txt to tarball on request of George Danchev 2011.05.15.104727 [3804] libburn/init.h Macros BURN_ALLOC_MEM, BURN_FREE_MEM for replaceing local variables 2011.05.15.104926 [3805] libburn/async.c Replaced some large local variables by other means in libburn/async.c 2011.05.15.181724 [3806] libburn/init.h Polished macro BURN_ALLOC_MEM 2011.05.15.191420 [3807] libburn/drive.c Replaced some large local variables by other means in libburn/drive.c 2011.05.15.203140 [3808] libburn/init.h libburn/async.c Added forgotten return value to BURN_ALLOC_MEM 22 May 2011 [3830] doc/mediainfo.txt Added a DVD+RW product id to the list 22 May 2011 [3831] configure.ac Added options -Wextra -Wno-unused-parameter for gcc 2011.05.23.154011 [3834] libburn/mmc.c Replaced some large local variables by other means in libburn/mmc.c 2011.05.23.155217 [3835] libburn/read.c Replaced some large local variables by other means in libburn/read.c 2011.05.23.163506 [3836] libburn/sg-dummy.c Replaced some large local variables by other means in libburn/sg-dummy.c 2011.05.23.174016 [3837] libburn/sg-freebsd.c Replaced some large local variables by other means in libburn/sg-freebsd.c 2011.05.23.182627 [3838] libburn/sg-freebsd-port.c Replaced some large local variables by other means in libburn/sg-freebsd-port.c 2011.05.26.145626 [3839] libburn/transport.h libburn/libdax_msgs.h libburn/mmc.c Improved reaction on Damage Bit and missing NWA_V of READ TRACK INFORMATION 2011.05.26.150020 [3840] libburn/libburn.h libburn/drive.c libburn/libburn.ver New API call burn_disc_next_track_is_damaged() 2011.05.31.103124 [3846] libburn/write.h libburn/write.c libburn/libdax_msgs.h libburn/libburn.ver New API call burn_disc_close_damaged() 2011.06.02.083808 [3848] libburn/sg-freebsd.c libburn/sg-freebsd-port.c Gave up use of bzero() in FreeBSD system adapters 2011.06.02.132554 [3849] libburn/sg-libcdio.c Replaced some large local variables by other means in libburn/sg-libcdio.c 2011.06.05.170431 [3850] libburn/sg-linux.c Replaced some large local variables by other means in libburn/sg-linux.c 2011.06.06.104404 [3851] libburn/sg-solaris.c Replaced some large local variables by other means in libburn/sg-solaris.c 2011.06.06.105052 [3852] libburn/sg-solaris.c Reacted on warnings of gcc about mixed sign comparison 2011.06.06.173021 [3853] libburn/spc.c Replaced some large local variables by other means in libburn/spc.c 2011.06.06.173611 [3854] libburn/init.h libburn/init.c Leaner implementation of macro BURN_ALLOC_MEM 2011.06.07.084343 [3855] libburn/write.c Closed a small memory leak with CD SAO found by valgrind 2011.06.07.143930 [3856] libburn/transport.h libburn/spc.c libburn/sbc.c libburn/mmc.c Consolidated several local struct command to a new member of struct burn_drive 2011.06.08.081338 [3858] libburn/structure.c Replaced some large local variables by other means in libburn/structure.c 2011.06.08.082201 [3859] libburn/toc.c Replaced some large local variables by other means in libburn/toc.c 2011.06.08.181204 [3860] libburn/mmc.c Bug fix: burn_disc_format() on DVD-RW issued wrong block size with type 00h 08 Jun 2011 [3861] configure.ac Makefile.am Introduced AC_CONFIG_MACRO_DIR() and ACLOCAL_AMFLAGS on advise of George Danchev 2011.06.08.200329 [3863] libburn/write.c Replaced some large local variables by other means in libburn/write.c 09 Jun 2011 [3864] bootstrap Added option -I . to aclocal in bootstrap script on advise of George Danchev 2011.06.14.152832 [3866] libburn/spc.c libburn/mmc.c Reporting SCSI error if command RESERVE TRACK fails Release 1.0.8 was skipped to get back in sync with libisofs and libisoburn. 18 Jun 2011 [3868] svn copy -m Branching for libburn release 1.1.0 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.1.1 2011.06.18.100001 [3869] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.1.0 18 Jun 2011 [3870] - cdrskin/add_ts_changes_to_libburn_1_0_6 - cdrskin/add_ts_changes_to_libburn_1_0_7 + cdrskin/add_ts_changes_to_libburn_1_1_0 + cdrskin/add_ts_changes_to_libburn_1_1_1 Updated cdrskin tarball generator 18 Jun 2011 [3871] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ----------------------------------- release - cdrskin-1.1.0 - 2011.06.18.100001 * New API call burn_disc_next_track_is_damaged() * New API call burn_disc_close_damaged() * Bug fix: burn_disc_format() on DVD-RW issued wrong block size with type 00h 2011.06.18.154942 [3875] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.1.1 18 Jun 2011 [3876] - cdrskin/add_ts_changes_to_libburn_1_0_6 - cdrskin/add_ts_changes_to_libburn_1_0_7 + cdrskin/add_ts_changes_to_libburn_1_1_0 + cdrskin/add_ts_changes_to_libburn_1_1_1 Updated cdrskin tarball generator 18 Jun 2011 [3877] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp 18 Jun 2011 [3878] svn move -m libburn release 1.1.0 is ready http://svn.libburnia-project.org/libburn/branches/1.1.0 http://svn.libburnia-project.org/libburn/tags/1.1.0 ------------------------------------ cycle - cdrskin-1.1.1 - 2011.06.18.154942 2011.06.19.203515 [3882] libburn/sg-dummy.c Bug fix: libburn-1.1.0 did only compile on Linux, FreeBSD, and Solaris 20 Jun 2011 [3883] svn copy -m Branching for libburn release 1.1.0.pl01 http://svn.libburnia-project.org/libburn/tags/1.1.0 http://svn.libburnia-project.org/libburn/branches/1.1.0.pl01 2011.06.20.110001 [3884] [3885] (branch 1.0.0.pl01) libburn/sg-dummy.c ChangeLog cdrskin/cdrskin_timestamp.h Bug fix: libburn-1.1.0 did only compile on Linux, FreeBSD, and Solaris 20 Jun 2011 [3886] svn move -m "libburn bugfix release 1.0.0.pl01 is ready" http://svn.libburnia-project.org/libburn/branches/1.1.0.pl01 http://svn.libburnia-project.org/libburn/tags/1.1.0.pl01 ------------------------------ release - libburn-1.1.0.pl01 - 2011.06.20.110001 * Bug fix: libburn-1.1.0 compiled only on Linux, FreeBSD, and Solaris 2011.06.22.093127 [3888] libburn/sg-libcdio.c Silenced compiler warnings about signedness 22 Jun 2011 [3889] releng/releng_build_os Beginning to create a test suite for libburn and cdrskin 2011.07.04.143922 [4041] libburn/spc.c Avoiding to load speed descriptor list twice 2011.07.04.171649 [4047] libburn/file.c Reacted on warnings of -Wunused-but-set-variable 2011.07.06.142532 [4071] libburn/init.h libburn/async.c Reacted on warnings of -Wunused-but-set-variable 2011.07.06.143104 [4072] libburn/mmc.c Reacted on warnings of -Wunused-but-set-variable 2011.07.06.143313 [4073] libburn/spc.c Reacted on warnings of -Wunused-but-set-variable 2011.07.06.143441 [4074] libburn/toc.c Reacted on warnings of -Wunused-but-set-variable 2011.07.06.143536 [4075] libburn/write.c Reacted on warnings of -Wunused-but-set-variable 2011.07.06.143724 [4076] cdrskin/cdrskin.c Reacted on warnings of -Wtype-limits and -Wunused-but-set-variable 2011.07.06.143904 [4077] cdrskin/cdrfifo.c Reacted on warnings of -Wunused-but-set-variable 2011.07.07.114911 [4083] libburn/mmc.c Silenced warning of cppcheck 2011.07.07.115006 [4084] libburn/sg-linux.c Silenced warning of cppcheck 2011.07.07.120719 [4085] libburn/util.c Reacted on warning of cppcheck 2011.07.11.143934 [4133] libburn/sg.c Implemented macro Libburn_use_sg_freebsd_porT 2011.07.12.095936 [4138] cdrskin/cdrfifo.c Reacted on warning of cppcheck about cdrskin/cdrfifo.c 2011.07.12.105955 [4139] cdrskin/cleanup.c Reacted on warning of cppcheck about cdrskin/cleanup.c 2011.07.12.110131 [4140] libburn/async.c Reacted on warning of cppcheck about libburn/async.c 2011.07.12.110223 [4142] libburn/cleanup.c Reacted on warning of cppcheck about libburn/cleanup.c 2011.07.12.110306 [4143] libburn/drive.c Reacted on warning of cppcheck about libburn/drive.c 2011.07.12.110405 [4144] libburn/read.c Reacted on warning of cppcheck about libburn/read.c 2011.07.12.110453 [4145] test/poll.c Reacted on warning of cppcheck about test/poll.c 2011.07.12.141659 [4147] libburn/sg.c Improved intentional compiler warning about lack of suitable system adapter 2011.07.12.141838 [4148] libburn/sg-dummy.c Reacted on warning of cppcheck about libburn/sg-dummy.c 2011.07.12.141932 [4149] libburn/sg-freebsd-port.c Reacted on warning of cppcheck about libburn/sg-freebsd-port.c 2011.07.12.142018 [4150] libburn/sg-freebsd.c Reacted on warning of cppcheck about libburn/sg-freebsd.c 2011.07.12.142141 [4151] libburn/sg-libcdio.c Reacted on warning of cppcheck about libburn/sg-libcdio.c 2011.07.12.155335 [4152] cdrskin/cdrskin.c Improved -atip and -minfo with empty drive 2011.07.12.155417 [4153] libburn/drive.c Bug fix: Empty ROM drive was mistaken to hold an unsuitable disc 2011.07.12.173727 [4154] libburn/sg-solaris.c Reacted on warning of cppcheck about libburn/sg-solaris.c 2011.07.14.162150 [4164] cdrskin/cdrskin.c Fixed possible uninitialized variable use introduced by rev 4152 2011.07.15.081325 [4170] libburn/sg-freebsd.c Reacted on warning of cppcheck about libburn/sg-freebsd.c 2011.07.28.191202 [4222] libburn/libburn.h libburn/drive.c libburn/libburn.ver New API call burn_lookup_device_link() 2011.07.28.191536 [4223] cdrskin/cdrskin.c cdrskin/cdrskin.1 New option --device_links 2011.07.31.080152 [4227] libburn/transport.h libburn/drive.h libburn/drive.c libburn/spc.c libburn/mmc.c Bug fix: Some drives returned wrong CD sizes after having burnt DVD-R 2011.07.31.083046 [4228] libburn/spc.c libburn/mmc.c Bug fix: Some drives return -150 as NWA of blank CD, rather than 0 2011.08.01.125405 [4232] libburn/libburn.h libburn/drive.c libburn/mmc.c libburn/libburn.ver New API call burn_disc_get_phys_format_info() 2011.08.01.153305 [4237] libburn/mmc.h Forgot to upload the header which defines mmc_get_phys_format_info Release 1.1.2 was skipped to get back in sync with libisoburn. 07 Aug 2011 [4247] copy -m Branching for libburn release 1.1.4 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.1.4 2011.08.07.100001 [4249] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.1.4 07 Aug 2011 [4250] - cdrskin/add_ts_changes_to_libburn_1_1_0 - cdrskin/add_ts_changes_to_libburn_1_1_1 + cdrskin/add_ts_changes_to_libburn_1_1_4 + cdrskin/add_ts_changes_to_libburn_1_1_5 Updated cdrskin tarball generator 07 Aug 2011 [4251] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp 07 Aug 2011 [4255] cdrskin/cdrskin_eng.html ChangeLog cdrskin/changelog.txt Forgot to mention cdrskin option --device_links ----------------------------------- release - cdrskin-1.1.4 - 2011.08.07.100001 * Bug fix: Some drives return -150 as NWA of blank CD, rather than 0. libburn forwarded this misleading information to the application. * Bug fix: Some drives returned wrong CD sizes after having burned DVD-R * Bug fix: Empty ROM drive was mistaken to hold an unsuitable disc * Bug fix: Avoiding to load speed descriptor list twice * New API call burn_lookup_device_link() * New API call burn_disc_get_phys_format_info() * New cdrskin option --device_links 2011.08.08.121355 [4259] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.1.5 08 Aug 2011 [4260] - cdrskin/add_ts_changes_to_libburn_1_1_0 - cdrskin/add_ts_changes_to_libburn_1_1_1 + cdrskin/add_ts_changes_to_libburn_1_1_4 + cdrskin/add_ts_changes_to_libburn_1_1_5 Updated cdrskin tarball generator 08 Aug 2011 [4261] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-1.1.5 - 2011.08.08.121355 08 Aug 2011 [4262] svn move -m libburn release 1.1.4 is ready http://svn.libburnia-project.org/libburn/branches/1.1.4 http://svn.libburnia-project.org/libburn/tags/1.1.4 2011.08.17.160854 [4280] libburn/libburn.h libburn/transport.h libburn/drive.c Bug fix: stdio sizes > 4 TB - 32 kB caused integer rollover 2011.08.17.162201 [4281] libburn/drive.c Reacted on compiler warning about previous revision 2011.08.29.213339 [4302] libburn/write.c More generous ignoring of failure of fsync() on inappropriate fd 2011.09.20.131602 [4306] libburn/sg-linux.c Simulation of large disks 2011.09.20.131843 [4307] libburn/sg-linux.c Working around collision with udev by closing and re-opening device file 2011.09.20.132438 [4308] libburn/spc.c Slowing down test cycle while waiting for drive to become ready 2011.09.21.133344 [4310] README cdrskin/README libburn/sg-linux.c Improved and documented workaround for udev 21 Sep 2011 [4311] README cdrskin/README Corrected outdated sentence in documented workaround for udev 26 Sep 2011 [4321] svn copy -m Branching for libburn release 1.1.6 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.1.6 2011.09.27.060001 [4322] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.1.6 27 Sep 2011 [4323] - cdrskin/add_ts_changes_to_libburn_1_1_4 - cdrskin/add_ts_changes_to_libburn_1_1_5 + cdrskin/add_ts_changes_to_libburn_1_1_6 + cdrskin/add_ts_changes_to_libburn_1_1_7 Updated cdrskin tarball generator 27 Sep 2011 [4324] ChangeLog cdrskin/changelog.txt Updated change log ----------------------------------- release - libburn-1.1.6 - 2011.09.27.060001 * Bug fix: stdio sizes > 4 TB - 32 kB caused integer rollover * Worked around a collision with Linux udev which lets links vanish 2011.09.27.124555 [4329] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.1.7 27 Sep 2011 [4330] - cdrskin/add_ts_changes_to_libburn_1_1_4 - cdrskin/add_ts_changes_to_libburn_1_1_5 + cdrskin/add_ts_changes_to_libburn_1_1_6 + cdrskin/add_ts_changes_to_libburn_1_1_7 Updated cdrskin tarball generator 27 Sep 2011 [4331] ChangeLog cdrskin/changelog.txt Updated change log 27 Sep 2011 [4332] svn move -m libburn release 1.1.6 is ready http://svn.libburnia-project.org/libburn/branches/1.1.6 http://svn.libburnia-project.org/libburn/tags/1.1.6 ------------------------------------ cycle - cdrskin-1.1.7 - 2011.09.27.124555 2011.10.02.134037 [4336] libburn/sg-linux.c Avoided open()-close() cycles during drive scan and grab on Linux 2011.10.02.172004 [4337] libburn/sg-linux.c Avoided one more open()-close() cycles during drive scan and grab on Linux 2011.10.05.075531 [4339] libburn/libburn.h libburn/drive.h libburn/drive.c libburn/write.c libburn/libburn.ver New API call burn_drive_re_assess() 2011.10.06.082649 [4344] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_stream_recordinG 2011.10.06.082934 [4345] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_drive_equals_adR 2011.10.06.083228 [4346] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_get_drive_rolE 2011.10.06.084026 [4347] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_random_access_rW 2011.10.06.085426 [4348] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_get_best_speeD 2011.10.06.085713 [4349] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_set_waitinG 2011.10.06.085848 [4350] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_track_set_sizE 2011.10.06.090207 [4351] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_preset_device_familY 2011.10.06.090422 [4352] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_set_forcE 2011.10.06.090557 [4353] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_allow_untested_profileS 2011.10.06.091334 [4554] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_write_mode_ruleS 2011.10.06.091641 [4355] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_get_spacE 2011.10.06.091949 [4356] [4357] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_set_filluP 2011.10.06.092242 [4358] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_get_multi_capS 2011.10.06.095055 [4359] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_toc_entry_extensionS 2011.10.06.095322 [4360] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_get_msc1 2011.10.06.100025 [4361] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_burn_disc_formaT 2011.10.06.101239 [4362] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_bd_formattinG 2011.10.06.101442 [4363] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_wrote_welL 2011.10.06.101918 [4364] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_set_start_bytE 2011.10.06.102127 [4365] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_get_profilE 2011.10.06.102557 [4366] cdrskin/cdrskin.c Now unconditional: Cdrskin_atip_speed_is_oK 2011.10.06.102816 [4367] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_buffer_min_filL 2011.10.06.103102 [4368] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_multI 2011.10.06.103325 [4369] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_pretend_fulL 2011.10.06.103445 [4370] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_buffer_progresS 2011.10.06.103619 [4371] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_read_atiP 2011.10.06.103832 [4372] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_burn_disc_unsuitablE 2011.10.06.104012 [4373] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_get_start_end_lbA 2011.10.06.104233 [4374] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_audioxtR 2011.10.06.104519 [4375] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_cleanup_handleR 2011.10.06.104908 [4376] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_burn_aborT 2011.10.06.105627 [4377] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_burn_msgS 2011.10.06.105922 [4378] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_convert_scsi_adR 2011.10.06.111115 [4379] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_convert_fs_adR 2011.10.06.111245 [4380] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_is_enumerablE 2011.10.06.111758 [4381] cdrskin/cdrskin.c Now unconditional: Cdrskin_allow_libburn_taO 2011.10.06.112031 [4382] cdrskin/cdrskin.c Now unconditional: Cdrskin_grab_abort_does_worK 2011.10.06.112239 [4383] cdrskin/cdrskin.c Now unconditional: Cdrskin_is_erasable_on_load_does_worK 2011.10.06.112456 [4384] cdrskin/cdrskin.c Now unconditional: Cdrskin_progress_track_does_worK 2011.10.06.112732 [4385] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_has_drive_get_adR 2011.10.06.113103 [4386] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_does_ejecT 2011.10.06.113315 [4387] cdrskin/cdrskin.c Now unconditional: Cdrskin_all_tracks_with_sector_paD 2011.10.06.114009 [4388] cdrskin/cdrskin.c Removed obsolete macro case 2011.10.06.120014 [4389] cdrskin/cdrskin.c cdrskin/compile_cdrskin.sh Removed outdated code and Cdrskin_oldfashioned_api_usE 2011.10.06.121140 [4390] cdrskin/cdrskin.c Now unconditional: Cdrskin_libburn_from_pykix_svN 2011.10.07.075117 [4391] libburn/drive.h libburn/drive.c Avoided to release drive prematurely if interrupted while grabbing drive 08 Oct 2011 [4392] cdrskin/convert_man_to_html.sh Replaced HTML minus by a flat ASCII minus in HTML man page generator 2011.10.09.155415 [4394] libburn/drive.c Reacted on warning of cppcheck about burn_disc_get_cd_info() 2011.10.09.162355 [4395] libburn/drive.c Reacted on nitpicking of cppcheck 2011.10.10.125950 [4397] libburn/libburn.h Changed term "persistent address" to "device file address" 2011.10.10.130029 [4398] libburn/drive.c Changed debug message which called "stdio:" addresses enumerable 2011.10.11.141313 [4399] [4400] cdrskin/cdrskin.c Avoided release-grab cycle in cdrskin -msinfo 2011.10.11.163842 [4401] cdrskin/cdrskin.c Avoided release-grab cycles between tasks of a cdrskin run 2011.10.12.100050 [4402] libburn/libburn.h libburn/libdax_msgs.h libburn/drive.c libburn/write.c libburn/structure.c libburn/read.c libburn/debug.c libburn/mmc.c libburn/spc.c libburn/sg-solaris.c libburn/sg-linux.c libburn/sg-libcdio.c libburn/sg-freebsd-port.c libburn/sg-freebsd.c Gave up use of burn_print() in libburn 2011.10.12.100155 [4403] test/structest.c Added madatory library initialization to test/structest.c 2011.10.27.091014 [4413] libburn/sg-libcdio.c Silenced a warnings in libcdio adapter 2011.10.27.094705 [4414] libburn/sg-libcdio.c Enabled optional reporting of drive replies in libcdio adapter 2011.10.27.094705 [4415] libburn/sg-solaris.c Enabled optional reporting of drive replies in Solaris adapter 2011.10.31.164236 [4416] libburn/spc.c libburn/mmc.c Enabled recognition of QEMU DVD-ROM 0.12 as ATAPI drive 2011.11.01.221713 [4417] libburn/spc.c Small correction of rev 4416 2011.11.02.074334 [4418] libburn/sg-linux.c Avoiding on Linux to list drives from /proc if device family is non-default 2011.11.02.130735 [4419] libburn/mmc.c Corrected a bug with obtaining Physical Interface Standard information 2011.11.03.130710 [4422] libburn/spc.c Bug fix: Misinterpreted mode page 2A if block descriptors are present 2011.11.09.111408 [4424] libburn/sg-linux.c Enabled recognition of CD drive at /dev/vda of Linux guest on qemu virtio 2011.11.10.134432 [4430] libburn/libburn.h Removed remark about SCSI logging being restricted to Linux system adapter 2011.11.10.153659 [4431] libburn/spc.h libburn/spc.c libburn/sg-linux.c Logging of early SCSI commands of Linux system adapter to stderr 2011.11.14.170612 [4435] libburn/sg-linux.c Clarified a remark in libburn/sg-linux.c 2011.11.16.094244 [4336] libburn/libdax_msgs.h Clarified a remark in libburn/libdax_msgs.h 2011.11.16.094436 [4437] libburn/write.c Distinguished failure messages about write(2) and fsync(2) 2011.11.18.155410 [4440] libburn/sg-libcdio.c Bug fix: licdio adapter reacted inconsistently on symbolic links to drives 20 Nov 2011 [4441] svn copy -m Branching for libburn release 1.1.8 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.1.8 2011.11.20.100001 [4442] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.1.8 20 Nov 2011 [4443] - cdrskin/add_ts_changes_to_libburn_1_1_6 - cdrskin/add_ts_changes_to_libburn_1_1_7 + cdrskin/add_ts_changes_to_libburn_1_1_8 + cdrskin/add_ts_changes_to_libburn_1_1_9 Updated cdrskin tarball generator 20 Nov 2011 [4444] ChangeLog cdrskin/changelog.txt Updated change log ----------------------------------- release - libburn-1.1.8 - 2011.11.20.100001 * New API call burn_drive_re_assess() * Enabled recognition of QEMU DVD-ROM 0.12 as ATAPI drive * Bug fix: Misinterpreted mode page 2A if block descriptors are present 2011.11.20.214751 [4448] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.1.9 20 Nov 2011 [4449] - cdrskin/add_ts_changes_to_libburn_1_1_6 - cdrskin/add_ts_changes_to_libburn_1_1_7 + cdrskin/add_ts_changes_to_libburn_1_1_8 + cdrskin/add_ts_changes_to_libburn_1_1_9 Updated cdrskin tarball generator 20 Nov 2011 [4450] ChangeLog cdrskin/changelog.txt Updated change log 21 Nov 2011 [4451] svn move -m libburn release 1.1.8 is ready http://svn.libburnia-project.org/libburn/branches/1.1.8 http://svn.libburnia-project.org/libburn/tags/1.1.8 ------------------------------------ cycle - cdrskin-1.1.9 - 2011.11.20.220318 2011.11.22.191621 [4455] libburn/sg-linux.c Reacted on compiler warning of Debian buildd 2011.11.23.104948 [4458] libburn/libdax_msgs.h libburn/sg-linux.c Reporting about failure to open existing /dev/sr or /dev/scd on Linux 2011.11.26.153142 [4460] libburn/sg-solaris.c Bug fix: Solaris adapter mishandled write commands which failed on first try 2011.11.30.081130 [4462] libburn/transport.h libburn/libdax_msgs.h libburn/spc.h libburn/spc.c libburn/sbc.c libburn/mmc.c libburn/sg-linux.c libburn/sg-freebsd.c libburn/sg-libcdio.c libburn/sg-solaris.c Made SCSI timeout settable at level of SPC, SBC, MMC functions 2011.11.30.131608 [4463] libburn/mmc.c Improved debugging of drive feature list 2011.12.02.100148 [4464] libburn/drive.c libburn/spc.c Bug fix: Interrupting libburn while drive tray is loading led to endless loop 2011.12.02.171436 [4466] libburn/libburn.h libburn/drive.c libburn/mmc.h libburn/mmc.c libburn/libburn.ver New API call burn_disc_get_leadin_text() 2011.12.02.171710 [4467] cdrskin/cdrskin.c cdrskin/cdrskin.1 Storing CD-TEXT as cdtext.dat with cdrskin option -toc -vv, reporting with -vvv 2011.12.02.204513 [4468] libburn/libburn.h Clarified meaning of Character Position byte of burn_disc_get_leadin_text() 2011.12.03.113345 [4469] libburn/mmc.c Corrected a style deviation in code of mmc.c 03 Dec 2011 [4470] Makefile.am Removing cdrskin/.libs and test/.libs witch make clean 03 Dec 2011 [4471] Makefile.am Added ./bootstrap script to release tarball 2011.12.05.203600 [4475] libburn/libburn.h libburn/libdax_msgs.h libburn/options.h libburn/options.c libburn/mmc.c libburn/write.c libburn/libburn.ver doc/cookbook.txt New API call burn_write_opts_set_leadin_text() 2011.12.05.203821 [4476] cdrskin/cdrskin.c cdrskin/cdrskin.1 Implemented cdrskin option textfile= 2011.12.06.134651 [4477] libburn/write.c Reacted on compiler warning about uninitialized variable in rev 4476 06 Dec 2011 [4478] doc/cookbook.txt Documented CD-TEXT pack type 0x8f 2011.12.07.075031 [4479] Makefile.am Removed version.h from list of source files because generated by configure 08 Dec 2011 [4481] doc/cookbook.txt Updated documentation of CD-TEXT pack types 0x87 and 0x8f 08 Dec 2011 [4482] doc/cookbook.txt Updated documentation of CD-TEXT pack types 0x86, 0x8d, 0x8e 2011.12.09.092152 [4483] cdrskin/cdrskin.c Tolerating trailing zero of Sony CDTEXT files 10 Dec 2011 [4484] doc/cookbook.txt Updated documentation of CD-TEXT pack types 0x88 and 0x89 10 Dec 2011 [4485] doc/cookbook.txt Some polishing in cookbook 12 Dec 2011 [4486] doc/cookbook.txt + doc/cdtext.txt Outsourced cdtext.txt from cookbook.txt 12 Dec 2011 [4487] cdrskin/cdrskin.1 Pointing from man cdrskin to doc/cdtext.txt for CD-TEXT details 2011.12.12.092602 [4488] libburn/libburn.h libburn/libdax_msgs.h libburn/init.c libburn/options.h libburn/options.c libburn/structure.h libburn/structure.c libburn/write.h libburn/write.c libburn/libburn.ver New API calls for composing CD-TEXT 2011.12.12.164452 [4490] libburn/libburn.h libburn/write.c doc/cdtext.txt New macros for CD-TEXT genre and language names 2011.12.12.181346 [4491] libburn/write.c Enabled repetition of track texts by TAB character 2011.12.13.164329 [4492] libburn/libburn.h More macros for CD-TEXT genre and language names 2011.12.14.124906 [4494] libburn/write.c Changed default CD-TEXT for tracks from track number text to empty text 2011.12.14.132551 [4495] cdrskin/cdrskin.c cdrskin/cdrskin.1 doc/cdtext.txt doc/cookbook.txt New option input_sheet_v07t= 14 Dec 2011 [4496] ChangeLog cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.1.9 - 2011.12.14.132711 * Bug fix: Solaris adapter mishandled write commands which failed on first try * Bug fix: Interrupting libburn while drive tray is loading led to endless loop * New API calls burn_disc_get_leadin_text(), burn_write_opts_set_leadin_text() * New API calls for composing CD-TEXT * Implemented cdrskin option textfile= * Implemented cdrskin option combination -vv -toc for cdtext.dat production * New cdrskin option input_sheet_v07t= 14 Dec 2011 [4497] cdrskin/cdrskin.1 cdrskin/cdrskin_eng.html Improved cdrskin man page and web page 15 Dec 2011 [4498] Makefile.am Included doc/cdtext.txt in libburn tarball 2011.12.15.104259 [4499] cdrskin/cdrskin.c cdrskin/cdrskin.1 Refusing to burn CD-TEXT if non-audio tracks are present 2011.12.15.154818 [4502] Makefile.am libburn/libburn.h libburn/libdax_msgs.h + libburn/cdtext.c libburn/libburn.ver doc/cdtext.txt New API call burn_session_input_sheet_v07t() 2011.12.15.155642 [4503] cdrskin/cdrskin.c cdrskin/compile_cdrskin.sh Moved reading of Sony Input Sheet v07 from cdrskin to libburn 2011.12.15.161349 [4504] libburn/libburn.h Documented new API call 2011.12.15.174914 [4505] libburn/cdtext.c libburn/write.c Moved burn_cdtext_from_session() from write.c to cdtext.c 2011.12.16.105329 [4506] libburn/util.c libburn/util.h libburn/cdtext.c Moved a function from cdtext.c to util.c 2011.12.16.111300 [4507] libburn/util.c libburn/util.h libburn/cdtext.c Moved a function from cdtext.c to util.c 2011.12.18.161107 [4508] cdrskin/cdrskin.c cdrskin/cdrskin.1 New options --cdtext_dummy and --cdtext_verbose 2011.12.22.112138 [4509] cdrskin/cdrskin.c Changed libburn print threshold of cdrskin -v to severity NOTE 2011.12.25.103852 [4510] libburn/libburn.h libburn/libdax_msgs.h libburn/structure.c libburn/file.c libburn/source.c libburn/cdtext.c libburn/libburn.ver New API calls burn_cdtext_from_packfile() and burn_session_by_cue_file() 2011.12.25.105032 [4511] cdrskin/cdrskin.c cdrskin/cdrskin.1 Partly implemented options cuefile= and -text 2011.12.25.110440 [4512] libburn/structure.c libburn/cdtext.c Reacted on compiler warnings about uninitialized variables 26 Dec 2011 [4514] doc/cdtext.txt Updated doc/cdtext.txt 2011.12.27.115645 [4515] libburn/libburn.h libburn/structure.h libburn/structure.c libburn/libburn.ver New API call burn_track_set_isrc_string() 2011.12.27.133733 [4516] libburn/write.c libburn/cdtext.c doc/cookbook.txt doc/cdtext.txt cdrskin/cdrskin.1 Writing CATALOG and ISRC into Q sub-channel of CD SAO sessions 2011.12.28.104044 [4517] libburn/libburn.h libburn/transport.h libburn/drive.c libburn/spc.h libburn/spc.c libburn/mmc.h libburn/mmc.c doc/cookbook.txt doc/cdtext.txt Transmitting CATALOG by mode page 5. ISRC too, if TAO. 2011.12.28.104536 [4518] cdrskin/cdrskin.c cdrskin/cdrskin.1 Implemented options mcn= and isrc= 2011.12.28.152153 [4519] libburn/transport.h libburn/write.c libburn/mmc.h libburn/mmc.c Improved reaction on failure of SEND CUE SHEET 2011.12.30.142742 [4521] libburn/spc.h libburn/spc.c libburn/mmc.c Reduced waiting time between retries of WRITE commands 2011.12.30.164755 [4522] cdrskin/cdrskin.c cdrskin/cdrskin.1 Coordinated option cuefile= with option input_sheet_v07t= 2011.12.30.174416 [4523] cdrskin/cdrskin.c cdrskin/cdrskin.1 Corrected precedence of media catalog and ISRC options 2011.12.31.120007 [4524] libburn/libburn.h libburn/write.c doc/cookbook.txt New track mode modifier BURN_SCMS 2012.01.01.124121 [4525] libburn/write.c Corrected CTL of lead-in CUE SHEET entry 2012.01.01.124330 [4526] libburn/mmc.c doc/cookbook.txt Writing SCMS into mode page 5 if TAO 2012.01.01.124424 [4627] libburn/drive.c Fixed a wrong assumption about track.mode 2012.01.01.124645 [4628] libburn/libburn.h libburn/structure.c doc/cdtext.txt Interpreting CDRWIN command FLAGS 2012.01.01.125539 [4529] cdrskin/cdrskin.c cdrskin/cdrskin.1 Implemented options -scms -copy -nocopy -preemp -nopreemp 2012.01.01.125539 [4530] cdrskin/cdrskin.c cdrskin/cdrskin.1 New cdrskin options --four_channel --two_channel 2012.01.01.173446 [4531] libburn/structure.c doc/cdtext.txt cdrskin/cdrskin.1 Implemented cue sheet file commands ARRANGER, COMPOSER, MESSAGE 2012.01.02.143655 [4532] libburn/libdax_msgs.h libburn/structure.c Reporting line number in case of cue sheet file problems 2012.01.02.164110 [4533] libburn/write.c Changed debug messages about CUE SHEET MSF from hex to decimal 2012.01.03.194322 [4534] libburn/libburn.h libburn/libdax_msgs.h libburn/structure.h libburn/structure.c libburn/write.c doc/cdtext.txt cdrskin/cdrskin.1 libburn/libburn.ver New API calls burn_track_set_index(), burn_track_clear_indice() 2012.01.04.131808 [4535] cdrskin/cdrskin.c cdrskin/cdrskin.1 Implemented option index= 2012.01.05.115516 [4536] libburn/drive.c libburn/write.c libburn/spc.h libburn/spc.c Bug fix: Progress report with blanking and formatting could be bogus 2012.01.05.120246 [4537] cdrskin/cdrskin.c Reacted on compiler warnings about rev 4535 2012.01.05.123114 [4538] ChangeLog cdrskin/changelog.txt cdrskin/cdrskin_eng.html Updated change log and web page 2012.01.05.140927 [4539] libburn/write.c Reacted on compiler warning about rev 4519 ------------------------------------ cycle - cdrskin-1.1.9 - 2012.01.05.141340 * New API call burn_session_input_sheet_v07t() * New API calls burn_cdtext_from_packfile() and burn_session_by_cue_file() * New API call burn_track_set_isrc_string() * New API calls burn_track_set_index(), burn_track_clear_indice() * New cdrskin options --cdtext_dummy and --cdtext_verbose * New cdrskin options --four_channel --two_channel * Implemented cdrskin options mcn= and isrc= * Implemented cdrskin options -scms -copy -nocopy -preemp -nopreemp * Implemented cdrskin option index= * Partly implemented cdrskin options cuefile= and -text * Bug fix: Progress report with blanking and formatting could be bogus 2012.01.06.125849 [4540] libburn/libburn.h libburn/structure.c cdrskin/cdrskin.1 doc/cdtext.txt Implemented data extraction from cue sheet FILE type WAVE 2012.01.07.190901 [4541] libburn/libburn.h libburn/libdax_msgs.h libburn/drive.c libburn/structure.c libburn/write.c libburn/spc.c libburn/mmc.c libburn/libburn.ver New API call burn_session_set_start_tno() 2012.01.08.132304 [4542] libburn/libburn.h libburn/structure.c libburn/cdtext.c doc/cdtext.txt Implemented track number starts > 1 with .cue and v07t.txt files 2012.01.08.140810 [4543] libburn/libburn.h libburn/libburn.ver libburn/structure.c New API call burn_session_get_start_tno() 2012.01.08.141104 [4544] cdrskin/cdrskin.c While writing: Reporting track numbers with correct start number offset 2012.01.08.154822 [4545] cdrskin/cdrskin.c Corrected display of track number offset with -toc and -minfo 2012.01.08.191443 [4546] cdrskin/cdrskin.c cdrskin/cdrskin.1 New cdrskin option cd_start_tno= 2012.01.10.122000 [4547] libburn/libburn.h libburn/libburn.ver libburn/structure.h libburn/structure.c libburn/write.c New API call burn_track_set_pregap_size() 2012.01.10.124916 [4548] libburn/write.c Removed a development test contraption introduced by previous revision 2012.01.11.111602 [4549] libburn/write.c Enabled pre-gap size for tracks other than the first one 2012.01.11.122013 [4550] libburn/write.c Reacted on compiler warning 2012.01.11.122158 [4551] libburn/structure.c doc/cdtext.txt cdrskin/cdrskin.1 Interpreting .cue file command PREGAP 2012.01.11.122442 [4552] libburn/structure.h libburn/structure.c libburn/mmc.h libburn/mmc.c Obtaining more accurate track sizes for CD with pre-gap 2012.01.11.132847 [4553] cdrskin/cdrskin.c Corrected track number of blank track reported by cdrskin -minfo 2012.01.11.132946 [4554] libburn/libburn.h Clarified meaning of parameter trackno with call burn_disc_track_lba_nwa() 2012.01.12.084429 [4555] libburn/libburn.h libburn/libburn.ver libburn/libdax_msgs.h libburn/structure.h libburn/structure.c libburn/write.c New API call burn_track_set_postgap_size() 2012.01.12.120316 [4556] libburn/libburn.h libburn/structure.c doc/cdtext.txt cdrskin/cdrskin.1 Interpreting .cue file command POSTGAP 2012.01.12.191143 [4557] cdrskin/cdrskin.c cdrskin/cdrskin.1 New cdrskin options sao_pregap=, sao_postgap= 2012.01.13.101844 [4558] cdrskin/cdrskin.c Avoided display of negative speed numbers at track change 2012.01.13.122205 [4559] libburn/write.c libburn/sector.h libburn/sector.c Flushing buffer to MMC before a new track begins 2012.01.13.141517 [4560] cdrskin/cdrskin.c Corrected type and sector size for .cue generated tracks 2012.01.13.142723 [4561] libburn/write.c Corrected write count for tracks with post-gap 2012.01.13.150949 [4562] libburn/write.c Removed development macro which sneaked in with previous revision 2012.01.13.151126 [4563] cdrskin/cdrskin.c Corrected total size summary of cdrskin before burn start 2012.01.13.170914 [4564] libburn/write.c libburn/sector.c Implemented index reporting for struct burn_progress 15 Jan 2012 [4568] doc/cookbook.txt Updated SAO cookbook about indice, pre-gap, post-gap 2012.01.16.112048 [4570] libburn/sector.c Corrected a comment 2012.01.16.112258 [4571] libburn/libburn.h libburn/write.c Preventing CD-TEXT with sessions that are not pure audio 2012.01.18.120429 [4573] libburn/structure.c Detecting address sequence errors with .cue command INDEX 2012.01.18.141954 [4574] libburn/spc.c Made conversion of ASC ASCQ to text independent of key and its text 2012.01.19.210418 [4575,4577] delayed_commit/B20119.200204 libburn/libburn.h libburn/structure.h libburn/structure.c libburn/write.c Counting post-gap as part of track with burn_track_get_sectors() 2012.01.20.091016 [4576,4578] delayed_commit/B20120.091044 libburn/libburn.h libburn/file.h libburn/file.c libburn/structure.c Introduced burn_offst_source_new() flag bit0 which prevents later size changes 2012.01.21.174103 [4579] delayed_commit/B20121.174108 libburn/read.c Allowing SCSI read operations > 32 kB 2012.01.22.124929 [4580] delayed_commit/B20122.124935 libburn/spc.c Revoked an aspect of rev 4465 which aborted operation before drive is ready 2012.01.22.125048 [4581] delayed_commit/B20122.125048 libburn/async.c Prevented SIGSEGV with testing drive for being prepared for writing 2012.01.22.125357 [4582] delayed_commit/B20122.125357 libburn/drive.c Improved reaction time on interrupt during writing of lead-in 2012.01.22.182854 [4583] delayed_commit/B20122.182858 libburn/libburn.h libburn/init.h libburn/init.c libburn/drive.c Disabled dangerous abort handler actions while BURN_DRIVE_GRABBING 2012.01.22.194131 [4584] delayed_commit/B20122.194131 cdrskin/cdrskin.c Bug fix: cdrskin produced a memory fault if interupted before writing began 2012.01.23.102720 [4585] libburn/libdax_msgs.h libburn/mmc.c libburn/options.c libburn/sg-freebsd-port.c libburn/crc.c libburn/write.c Corrected some minor differences between SVN and local development tree 23 Jan 2012 [4590] cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log 23 Jan 2012 [4591] ChangeLog Updated change log 23 Jan 2012 [4592] cdrskin/wiki_plain.txt Updated cdrskin wiki text 2012.01.23.164346 [4593] cdrskin/cdrskin.c Fixed an endless loop with cdrskin signal handling ------------------------------------ cycle - cdrskin-1.1.9 - 2012.01.23.164529 * Bug fix: cdrskin produced a memory fault if interupted before writing began * New API calls burn_session_set_start_tno(), burn_session_get_start_tno() * New API calls burn_track_set_pregap_size(), burn_track_set_postgap_size() * New cdrskin option cd_start_tno= * New cdrskin options sao_pregap=, sao_postgap= 2012.01.25.092650 [4595] cdrskin/cdrskin.c Removed overdone part of rev 4593 27 Jan 2012 [4598] svn copy -m Branching for libburn release 1.2.0 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.2.0 2012.01.27.103001 [4599] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.2.0 27 Jan 2012 [4600] - cdrskin/add_ts_changes_to_libburn_1_1_8 - cdrskin/add_ts_changes_to_libburn_1_1_9 + cdrskin/add_ts_changes_to_libburn_1_2_0 + cdrskin/add_ts_changes_to_libburn_1_2_1 Updated cdrskin tarball generator 27 Jan 2012 [4601] ChangeLog cdrskin/changelog.txt Updated change log ----------------------------------- release - libburn-1.2.0 - 2012.01.27.103001 * Bug fix: cdrskin produced a memory fault if interupted before writing began * Bug fix: Solaris adapter mishandled write commands which failed on first try * Bug fix: Interrupting libburn while drive tray is loading led to endless loop * Bug fix: Progress report with blanking and formatting could be bogus * New API calls burn_disc_get_leadin_text(), burn_write_opts_set_leadin_text() * New API calls for composing CD-TEXT, see doc/cdtext.txt * New API call burn_session_by_cue_file() for reading CDRWIN .cue files * New API call burn_track_set_isrc_string() * New API calls burn_track_set_index(), burn_track_clear_indice() * New API calls burn_session_set_start_tno(), burn_session_get_start_tno() * New API calls burn_track_set_pregap_size(), burn_track_set_postgap_size() * Implemented cdrskin option textfile= * Implemented cdrskin option combination -vv -toc for cdtext.dat production * Implemented cdrskin options mcn= and isrc= * Implemented cdrskin options -scms -copy -nocopy -preemp -nopreemp * Implemented cdrskin option index= * Partly implemented cdrskin options cuefile= and -text * New cdrskin option input_sheet_v07t= for CD-TEXT definition * New cdrskin options --cdtext_dummy and --cdtext_verbose * New cdrskin options --four_channel --two_channel * New cdrskin option cd_start_tno= * New cdrskin options sao_pregap=, sao_postgap= 2012.01.27.150951 [4605] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.2.1 27 Jan 2012 [4606] - cdrskin/add_ts_changes_to_libburn_1_1_8 - cdrskin/add_ts_changes_to_libburn_1_1_9 + cdrskin/add_ts_changes_to_libburn_1_2_0 + cdrskin/add_ts_changes_to_libburn_1_2_1 Updated cdrskin tarball generator 27 Jan 2012 [4607] ChangeLog cdrskin/changelog.txt Updated change log 27 Jan 2012 [4608] svn move -m libburn release 1.2.0 is ready http://svn.libburnia-project.org/libburn/branches/1.2.0 http://svn.libburnia-project.org/libburn/tags/1.2.0 27 Jan 2012 [tag:4612] [4613] libburn/write.c ChangeLog Corrected small flaws of libburn release tarball 27 Jan 2012 [4614] [4615] libburn/write.c One of the flaws was phony. Corrected back. ------------------------------------ cycle - cdrskin-1.2.1 - 2012.01.27.150951 2012.02.02.190720 [4624] libburn/write.c libburn/spc.c Reacted on warnings of Debian buildd 06 Feb 2012 [4625] doc/cdtext.txt Small corrections in CD-TEXT documentation 08 Feb 2012 [4626] doc/cdtext.txt Small corrections in CD-TEXT documentation 2012.02.11.171228 [4627] libburn/crc.c Mathematical description of crc_ccitt() versus crc_11021() 2012.02.13.102837 [4628] libburn/spc.c Added LG drive sense code 2 06 00 to error list (officially listed is 3 06 00) 2012.02.19.101022 [4631] libburn/crc.h libburn/crc.c Re-implemented the CRC functions under own copyright 2012.02.22.103056 [4632] libburn/util.c doc/mediainfo.txt Added to DVD manufacturer list: "UmeDisc Limited" 2012.03.21.193320 [4671] cdrskin/cdrskin.c Reacted on warning of cppcheck 2 Apr 2012 [4684] svn copy -m Branching for libburn release 1.2.2 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.2.2 2012.04.02.110001 [4685] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.2.2 02 Apr 2012 [4686] - cdrskin/add_ts_changes_to_libburn_1_2_0 - cdrskin/add_ts_changes_to_libburn_1_2_1 + cdrskin/add_ts_changes_to_libburn_1_2_2 + cdrskin/add_ts_changes_to_libburn_1_2_3 Updated cdrskin tarball generator 02 Apr 2012 [4687] ChangeLog cdrskin/changelog.txt Updated change log ----------------------------------- release - libburn-1.2.2 - 2012.04.02.110001 * Small internal refinements 2012.04.02.172347 [4691] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.2.3 02 Apr 2012 [4692] - cdrskin/add_ts_changes_to_libburn_1_2_0 - cdrskin/add_ts_changes_to_libburn_1_2_1 + cdrskin/add_ts_changes_to_libburn_1_2_2 + cdrskin/add_ts_changes_to_libburn_1_2_3 Updated cdrskin tarball generator 02 Apr 2012 [4693] ChangeLog cdrskin/changelog.txt Updated change log 02 Apr 2012 [4694] svn move -m libburn release 1.2.2 is ready http://svn.libburnia-project.org/libburn/branches/1.2.2 http://svn.libburnia-project.org/libburn/tags/1.2.2 ------------------------------------ cycle - cdrskin-1.2.3 - 2012.04.02.172347 2012.04.04.100754 [4698] cdrskin/cdrskin.c Bug fix: cdrskin SIGSEGV if track source was added when no drive was available 2012.04.04.183902 [4699] libburn/crc.c Reacted on warning of Debian buildd 2012.04.08.112703 [4701] libburn/libburn.h libburn/options.h libburn/options.c libburn/write.c libburn/libdax_msgs.h libburn/libburn.ver New API call burn_write_opts_set_obs_pad() 2012.04.08.112825 [4702] cdrskin/cdrskin.c cdrskin/cdrskin.1 New option --obs_pad 2012.04.10.181239 [4704] cdrskin/cdrskin.c Now recognizing long options with double dash 13 Apr 2012 [4706] cdrskin/cdrskin.1 Small correction in cdrskin man page 2012.04.13.202654 [4707] configure.ac libburn/libburn.h libburn/options.c cdrskin/cdrskin.1 Compile time option for obs_pad 18 Apr 2012 [4708] doc/cdtext.txt Augmented CD-TEXT documentation by a complete example of packs 18 Apr 2012 [4709] doc/cookbook.txt Small change in cookbook.txt about DVD-R DL 2012.05.08.080449 [4731] libburn/crc.c Corrections of CRC-32 algorithm for 32 bit systems. Mentioning of start value. 2012.05.30.202249 [4744] libburn/write.c libburn/mmc.c doc/cookbook.txt Bug fix: CD SAO sessions with data tracks started by an audio pause 2012.06.17.173420 [4762] libburn/sector.c Improved reported number of missing bytes in case of track source shortage 2012.07.08.103007 [4778] libburn/structure.c Bug fix: CD tracks were perceived 2 sectors too short. Nice with TAO, bad with SAO. 20 Jul 2012 [4794] svn copy -m "Branching for libburn release 1.2.4" http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.2.4 2012.07.20.113001 [4795] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.2.4 20 Jul 2012 [4796] - cdrskin/add_ts_changes_to_libburn_1_2_2 - cdrskin/add_ts_changes_to_libburn_1_2_3 + cdrskin/add_ts_changes_to_libburn_1_2_4 + cdrskin/add_ts_changes_to_libburn_1_2_5 Updated cdrskin tarball generator 20 Jul 2012 [] ChangeLog cdrskin/changelog.txt Updated change log ----------------------------------- release - libburn-1.2.4 - 2012.07.20.113001 * New API call burn_write_opts_set_obs_pad(), ./configure --enable-dvd-obs-pad * New cdrskin option --obs_pad * Bug fix: CD SAO sessions with data tracks started by an audio pause * Bug fix: CD tracks were perceived 2 sectors too short. Nice with TAO, bad with SAO. * Bug fix: cdrskin SIGSEGV if track source was added when no drive was available 2012.07.20.164346 [4801] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.2.5 20 Jul 2012 [4802] - cdrskin/add_ts_changes_to_libburn_1_2_2 - cdrskin/add_ts_changes_to_libburn_1_2_3 + cdrskin/add_ts_changes_to_libburn_1_2_4 + cdrskin/add_ts_changes_to_libburn_1_2_5 Updated cdrskin tarball generator 20 Jul 2012 [4803] ChangeLog cdrskin/changelog.txt Updated change log 20 Jul 2012 [4806] svn move -m libburn release 1.2.4 is ready http://svn.libburnia-project.org/libburn/branches/1.2.4 http://svn.libburnia-project.org/libburn/tags/1.2.4 ------------------------------------ cycle - cdrskin-1.2.5 - 2012.07.20.164346 2012.07.26.122909 [4811] cdrskin/cdrskin.c cdrskin/cdrskin.1 New option --no_load 26 Jul 2012 [4813] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.2.5 - 2012.07.26.195346 * New option --no_load 1 Aug 2012 [4814] doc/cdtext.txt Small grammatical correction in CD-TEXT documentaion 2012.08.28.161949 [4820] libburn/drive.c libburn/util.h libburn/util.c libburn/sg-freebsd.c Removed buggy burn_strdup() and burn_strndup(). Thanks to Rich Felker. 2012.09.13.085623 [4828] libburn/mmc.c Bug fix: Speed setting had no effect on BD media. Thanks to Dennis Vshivkov. 13 Sep 2012 [4829] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.2.5 - 2012.09.13.090310 Bug fix: Speed setting had no effect on BD media. Thanks to Dennis Vshivkov. 2012.10.02.134821 [4841] configure.ac Configuration for use of libcdio on cygwin. Thanks Rocky Bernstein. 2012.10.24.095725 [4850] libburn/async.c Reporting (still cryptic) details about refusal to blank 2012.10.25.123837 [4852] libburn/libburn.h libburn/transport.h libburn/read.c libburn/spc.c libburn/mmc.c New flag bit4 of burn_read_data() for better handling of TAO end blocks 2012.10.25.173408 [4854] libburn/read.c Corrected error handling which was spoiled by rev 4852 2012.11.18.184006 [4858] libburn/libburn.h libburn/read.c libburn/spc.c libburn/mmc.h libburn/mmc.c Preserving an immature sketch of media quality scanning 2012.11.18.184114 [4859] cdrskin/cdrskin.c Preserving an immature sketch of media quality scanning 2012.11.24.181347 [4860] libburn/mmc.c Better reaction on non-plausible ATIP info from CD-ROM 2012.11.29.111344 [4865] libburn/libburn.h libburn/transport.h libburn/libdax_msgs.h libburn/options.h libburn/options.c libburn/toc.c libburn/read.c libburn/spc.c libburn/mmc.h libburn/sg-freebsd.c libburn/libburn.ver Beginning to create new API call burn_read_audio 2012.11.29.112506 [4866] libburn/mmc.c Forgot mcc.c with the previous commit 2012.11.29.112605 [4867] libburn/libdax_msgs.h libburn/mmc.c Let mmc_format_unit issue failure message on SCSI error 2012.11.30.193330 [4868] libburn/libburn.h libburn/read.c New API call burn_read_audio 2012.11.30.193415 [4869] test/telltoc.c Made telltoc ready for reading CD audio 2012.12.14.145101 [4878] libburn/libburn.h libburn/init.c libburn/libdax_msgs.h libburn/libdax_msgs.c libburn/libburn.ver New API call burn_list_sev_texts() 14 Dec 2012 [4879] doc/cookbook.txt Small change to burn cookbook about ISO multi-session emulation 08 Jan 2013 [4935] svn copy -m "Branching for libburn release 1.2.6" http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.2.6 2013.01.08.090001 [4936] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.2.6 08 Jan 2013 [4937] - cdrskin/add_ts_changes_to_libburn_1_2_4 - cdrskin/add_ts_changes_to_libburn_1_2_5 + cdrskin/add_ts_changes_to_libburn_1_2_6 + cdrskin/add_ts_changes_to_libburn_1_2_7 Updated cdrskin tarball generator 08 Jan 2013 [4938] ChangeLog cdrskin/changelog.txt Updated change log ----------------------------------- release - libburn-1.2.6 - 2013.01.08.090001 Bug fix: Speed setting had no effect on BD media. Thanks to Dennis Vshivkov. * New API call burn_read_audio() * New API call burn_list_sev_texts() * New cdrskin option --no_load 2013.01.08.144634 [] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.2.7 08 Jan 2013 [4947] - cdrskin/add_ts_changes_to_libburn_1_2_4 - cdrskin/add_ts_changes_to_libburn_1_2_5 + cdrskin/add_ts_changes_to_libburn_1_2_6 + cdrskin/add_ts_changes_to_libburn_1_2_7 Updated cdrskin tarball generator 08 Jan 2013 [4948] ChangeLog cdrskin/changelog.txt Updated change log 08 Jan 2013 [4949] svn move -m libburn release 1.2.6 is ready http://svn.libburnia-project.org/libburn/branches/1.2.6 http://svn.libburnia-project.org/libburn/tags/1.2.6 ------------------------------------ cycle - cdrskin-1.2.7 - 2013.01.08.150303 2013.01.12.195030 [4955] libburn/libburn.h libburn/transport.h libburn/structure.h libburn/structure.c libburn/mmc.c libburn/libburn.ver New API call burn_disc_get_incomplete_sessions(), new burn_toc_entry.track_status_bits 2013.01.12.195311 [4956] cdrskin/cdrskin.c Made use of new API features to handle incomplete sessions 2013.01.15.104005 [4961] libburn/structure.c Bug fix: All CD tracks were reported with the sizes of the tracks in the first session. Regression introduced with version 1.2.0 (rev 4552). 16 Jan 2013 [4965] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log 2013.01.16.181124 [4966] cdrskin/cdrskin.c Updated cdrskin copyright message ------------------------------------ cycle - cdrskin-1.2.7 - 2013.01.16.181124 * New API call burn_disc_get_incomplete_sessions() * New burn_toc_entry component .track_status_bits * -toc and -minfo now report about tracks in the incomplete session * Bug fix: All CD tracks were reported with the sizes of the tracks in the first session. Regression introduced with version 1.2.0 (rev 4552). 2013.02.26.080127 [4972] libburn/drive.c Corrected wrong use of sizeof 2013.03.04.211258 [4975] libburn/mmc.c Bug fix: On some drives the request for minimum speed yielded maximum speed 2013.03.04.232436 [4976] libburn/mmc.c Corrected previous bug fix which caused speed descriptors to appear twice 2013.03.05.124217 [4977] libburn/mmc.c Still correcting the bug fix of rev 4975 2013.03.05.124508 [4978] cdrskin/cdrskin.c New cdrskin option --list_speeds 05 Mar 2013 [4979] cdrskin/cdrskin.1 Mentioned --list_speeds in manual page 05 Mar 2013 [4980] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log 2013.03.05.185356 [4981] libburn/mmc.c Always considering mode page 2A when looking for min and max speed 2013.03.05.185440 [4982] cdrskin/cdrskin.c Always considering mode page 2A when looking for min and max speed ------------------------------------ cycle - cdrskin-1.2.7 - 2013.03.05.185655 * Bug fix: On some drives the request for minimum speed yielded maximum speed * New cdrskin option --list_speeds 2013.03.12.114739 [4987] libburn/write.c Avoiding SYNCHRONIZE CACHE if DVD track preparation has failed 18 Mar 2013 [4994] svn copy -m "Branching for libburn release 1.2.8" http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.2.8 2013.03.18.080001 [4997] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.2.8 18 Mar 2013 [4998] - cdrskin/add_ts_changes_to_libburn_1_2_6 - cdrskin/add_ts_changes_to_libburn_1_2_7 + cdrskin/add_ts_changes_to_libburn_1_2_8 + cdrskin/add_ts_changes_to_libburn_1_2_9 Updated cdrskin tarball generator 18 Mar 2013 [4999] ChangeLog cdrskin/changelog.txt Updated change log ----------------------------------- release - libburn-1.2.8 - 2013.03.18.080001 * -toc and -minfo now report about tracks in the incomplete session * New API call burn_disc_get_incomplete_sessions() * New burn_toc_entry component .track_status_bits * Bug fix: All CD tracks were reported with the sizes of the tracks in the first session. Regression introduced with version 1.2.0 (rev 4552). * Bug fix: On some drives the request for minimum speed yielded maximum speed * New cdrskin option --list_speeds 2013.03.18.210519 [5007] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.2.9 18 Mar 2013 [4996] - cdrskin/add_ts_changes_to_libburn_1_2_6 - cdrskin/add_ts_changes_to_libburn_1_2_7 + cdrskin/add_ts_changes_to_libburn_1_2_8 + cdrskin/add_ts_changes_to_libburn_1_2_9 Updated cdrskin tarball generator 18 Mar 2013 [5008] ChangeLog cdrskin/changelog.txt Updated change log 18 Mar 2013 [5009] svn move -m libburn release 1.2.8 is ready http://svn.libburnia-project.org/libburn/branches/1.2.8 http://svn.libburnia-project.org/libburn/tags/1.2.8 ------------------------------------ cycle - cdrskin-1.2.9 - 2013.03.18.211611 2013.04.01.121637 [5013] cdrskin/cdrskin.c Forgot to increment cdrskin version number 2013.04.01.121851 [5014] libburn/mmc.c libburn/libdax_msgs.h Bug fix: Formatting of BD-RE used certification regardless of drive capabilities 01 Apr 2013 [5015] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.2.9 - 2013.04.01.125750 Bug fix: Formatting of BD-RE used certification regardless of drive capabilities 2013.05.10.064018 [5034] cdrskin/cdrskin.c Bug fix: DVD+R with damaged TOC were reported by -minfo with wrong end address 2013.05.10.072542 [5035] libburn/libdax_msgs.h Added new item to list of error codes 10 May 2013 [5036] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.2.9 - 2013.05.10.073212 Bug fix: DVD+R with damaged TOC were reported by -minfo with wrong end address 17 May 2013 [5044] svn copy -m Branching for libburn release 1.3.0 2013.05.17.090001 [5045] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.3.0 17 May 2013 [5046] - cdrskin/add_ts_changes_to_libburn_1_2_8 - cdrskin/add_ts_changes_to_libburn_1_2_9 + cdrskin/add_ts_changes_to_libburn_1_3_0 + cdrskin/add_ts_changes_to_libburn_1_3_1 Updated cdrskin tarball generator 17 May 2013 [5047] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ----------------------------------- release - libburn-1.3.0 - 2013.05.17.090001 * Bug fix: Full formatting of BD-RE used certification regardless of drive capabilities * Bug fix: DVD+R with damaged TOC were reported by -minfo with wrong end address 17 May 2013 [5051] - cdrskin/add_ts_changes_to_libburn_1_2_8 - cdrskin/add_ts_changes_to_libburn_1_2_9 + cdrskin/add_ts_changes_to_libburn_1_3_0 + cdrskin/add_ts_changes_to_libburn_1_3_1 Updated cdrskin tarball generator 2013.05.17.180032 [5052] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.3.1 17 May 2013 [5053] ChangeLog cdrskin/changelog.txt Updated change log 17 May 2013 [5054] svn move -m libburn release 1.3.0 is ready ------------------------------------ cycle - cdrskin-1.3.1 - 2013.05.17.181442 2013.05.19.114643 [5059] libburn/libburn.h libburn/cdtext.c doc/cdtext.txt libburn/libburn.ver New API call burn_make_input_sheet_v07t() 2013.05.19.114854 [5060] cdrskin/cdrskin.c cdrskin/cdrskin.1 New option textfile_to_v07t= 2013.05.19.154838 [5061] cdrskin/cdrskin.c cdrskin/cdrskin.1 New options cdtext_to_textfile= and cdtext_to_v07t= 2013.05.20.104814 [5062] libburn/libburn.h libburn/cdtext.c libburn/libdax_msgs.h API call burn_session_input_sheet_v07t(): read multiple blocks from same file 2013.05.20.110128 [5063] cdrskin/cdrskin.c cdrskin/cdrskin.1 Allowed option input_sheet_v07t= to read multiple blocks from same file 2013.05.20.124448 [5064] libburn/cdtext.c Bug fixes with new API call burn_make_input_sheet_v07t() 2013.05.20.124520 [5065] cdrskin/cdrskin.c Closed memory leak introduced by rev 5063 20 May 2013 [5066] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.3.1 - 2013.05.20.141737 * New API call burn_make_input_sheet_v07t() * New option textfile_to_v07t= * New options cdtext_to_textfile= and cdtext_to_v07t= * API call burn_session_input_sheet_v07t(): read multiple blocks from same file 2013.05.21.081819 [5067] cdrskin/cdrskin.c Defaulting -sao -multi to -tao -multi if -sao -multi is not possible 2013.05.23.154249 [5068] libburn/libburn.h libburn/file.c libburn/util.h libburn/util.c libburn/libdax_msgs.h libburn/libdax_msgs.c libburn/libburn.ver New API calls burn_drive_extract_audio(), burn_drive_extract_audio_track() 2013.05.23.154249 [5069] cdrskin/cdrskin.c cdrskin/cdrskin.1 New cdrskin options extract_audio_to= , extract_tracks= , extract_basename= , --extract_dap 23 May 2013 [5070] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.3.1 - 2013.05.23.155617 * New API calls burn_drive_extract_audio(), burn_drive_extract_audio_track() * New cdrskin options extract_audio_to= , extract_tracks= , extract_basename= , --extract_dap 23 May 2013 [5071] doc/cdtext.txt Updated documentation about CD-TEXT 2013.05.26.185945 [5072] cdrskin/cdrskin.c Luring K3B into using -xa rather than -xa1 2013.05.30.133008 [5076] cdrskin/cdrskin.c Bug fix: cdrskin -msinfo on DVD and BD reported old session start == next writable address. Regression introduced by version 1.2.8 (rev 4956). [] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.3.1 - 2013.05.30.133756 * Bug fix: cdrskin -msinfo on DVD and BD reported old session start == next writable address. Regression introduced by version 1.2.8 (rev 4956). 31 May 2013 [5079] svn copy -m Branching for libburn bugfix release 1.3.0.pl01 http://svn.libburnia-project.org/libburn/tags/1.3.0 http://svn.libburnia-project.org/libburn/branches/1.3.0.pl01 2013.05.31.080001 [5080] cdrskin/cdrskin.c Bug fix: cdrskin -msinfo on DVD and BD reported old session start = next writable address. Regression introduced by version 1.2.8 (rev 4956). 31 May 2013 [5081] README cdrskin/README ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Mentioned bug fix and pl01 31 May 2013 [5082] svn move -m libburn bugfix release 1.3.0.pl01 is ready http://svn.libburnia-project.org/libburn/branches/1.3.0.pl01 http://svn.libburnia-project.org/libburn/tags/1.3.0.pl01 ------------------------------ release - libburn-1.3.0.pl01 - 2013.05.31.080001 * Bug fix: cdrskin -msinfo on DVD and BD reported old session start = next writable address. Regression introduced by version 1.2.8 (rev 4956). 31 May 2013 [5083] README ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Mentioned bug fix and pl01 2013.06.09.152602 [5087] libburn/transport.h libburn/init.h libburn/init.c libburn/util.h libburn/util.c libburn/spc.h libburn/spc.c libburn/sg-linux.c libburn/sg-solaris.c libburn/sg-libcdio.c libburn/sg-freebsd.c Improved granularity of SCSI log time measurement and added absolute timestamp 2013.06.09.154237 [5088] libburn/util.c Prepared for optional use of clock_gettime() 2013.06.09.163052 [5089] libburn/util.c libburn/spc.c Some polishing of SCSI log time measurement 12 Jun 2013 [5090] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.3.1 - 2013.06.12.104457 * Improved granularity of SCSI log time measurement, now with timestamp 24 Jun 2013 [5091] test/telltoc.c Removed inactive test code from telltoc.c 2013.06.28.104134 [5093] cdrskin/cdrskin.c Making sure in cdrskin that off_t is large enough before starting libburn 2013.06.28.104316 [5094] cdrskin/cdrskin.c Removed an obsolete note message from cdrskin --devices 2013.07.01.155958 [5097] libburn/libburn.h libburn/init.c libburn/cleanup.c New mode bit8 with burn_set_signal_handling() to particularly ignore SIGPIPE 2013.07.08.145600 [5110] libburn/cleanup.c Corrected typo in a comment 2013.07.08.151826 [5111] cdrskin/cdrskin.c cdrskin/cdrskin.1 New option --pacifier_with_newline 2013.07.21.170714 [5112] libburn/os-dummy.h libburn/os-freebsd.h libburn/os-libcdio.h libburn/os-linux.h libburn/os-solaris.h Bug fix: The signal handler aborted on SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU 2013.07.29.091415 [5114] libburn/libburn.h Reacted on warnings of Debian build service about doxygen flaws 2013.08.04.100247 [5116] configure.ac libburn/write.c cdrskin/cdrskin.1 Changed default write chunk size for BD to 64 KiB 2013.08.04.124449 [5122] libburn/libburn.h Reacted on advise from Helmut Grohne to avoid confusion of doxygen 04 Aug 2013 [5124] doc/doxygen.conf.in Update of doxygen configuration for version 1.8.4 provided by George Danchev 07 Aug 2013 [5125] svn copy -m Branching for libburn release 1.3.2 2013.08.07.093001 [5126] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.3.2 07 Aug 2013 [5127] - cdrskin/add_ts_changes_to_libburn_1_3_0 - cdrskin/add_ts_changes_to_libburn_1_3_1 + cdrskin/add_ts_changes_to_libburn_1_3_2 + cdrskin/add_ts_changes_to_libburn_1_3_3 Updated cdrskin tarball generator 07 Aug 2013 [5128] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ----------------------------------- release - cdrskin-1.3.2 - 2013.08.07.093001 * Bug fix: cdrskin -msinfo on DVD and BD reported old session start = next writable address. Regression introduced by version 1.2.8 (rev 4956). * Bug fix: The signal handler aborted on SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU * New API call burn_make_input_sheet_v07t() * API call burn_session_input_sheet_v07t(): read multiple blocks from same file * New API calls burn_drive_extract_audio(), burn_drive_extract_audio_track() * New cdrskin option textfile_to_v07t= * New cdrskin options cdtext_to_textfile= and cdtext_to_v07t= * New cdrskin options extract_audio_to= , extract_tracks= , extract_basename= , --extract_dap * New cdrskin option --pacifier_with_newline * Improved granularity of SCSI log time measurement, now with timestamp * Optional "make doc" now demands doxygen 1.8.4 2013.08.07.134744 [5132] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.3.3 07 Aug 2013 [5133] - cdrskin/add_ts_changes_to_libburn_1_3_0 - cdrskin/add_ts_changes_to_libburn_1_3_1 + cdrskin/add_ts_changes_to_libburn_1_3_2 + cdrskin/add_ts_changes_to_libburn_1_3_3 Updated cdrskin tarball generator 07 Aug 2013 [5134] ChangeLog cdrskin/changelog.txt Updated change log 07 Aug 2013 [5135] svn move -m libburn release 1.3.2 is ready http://svn.libburnia-project.org/libburn/branches/1.3.2 http://svn.libburnia-project.org/libburn/tags/1.3.2 ------------------------------------ cycle - cdrskin-1.3.3 - 2013.08.07.135823 2013.09.04.105934 [5145] libburn/mmc.c Catching and defaulting mad responses to READ BUFFER CAPACITY 2013.09.04.110910 [5146] [5147] cdrskin/cdrskin.c Closed a small memory leak with cdrskin -msinfo 2013.09.04.141706 [5148] cdrskin/cdrskin.c Reacted on warnings of PLD Linux 2013.09.05.084834 [5152] libburn/file.c libburn/mmc.c Reacted on warnings of PLD Linux build log 2013.09.16.172745 [5157] libburn/async.c Reacted on warning of Debian buildd with clang 2013.10.03.100011 [5158] cdrskin/cdrskin.c Made -version message more acceptable for K3B. Proposal of Omegaweapon. 2013.10.09.092306 [5161] libburn/mmc.c Separately determining the maximum speeds for writing and reading 2013.10.09.134543 [5163] libburn/mmc.c Forgot to disable a debugging message 2013.10.10.161931 [5164] libburn/drive.c libburn/spc.c libburn/libdax_msgs.h Bug fix: Drive error reports were ignored during blanking and formatting 2013.10.28.104957 [5168] libburn/libburn.h libburn/transport.h libburn/drive.c libburn/options.h libburn/options.c libburn/write.c libburn/mmc.c libburn/libburn.ver New API calls burn_drive_was_feat21_failure(), burn_write_opts_set_fail21h_sev() 2013.11.08.095023 [5172] libburn/write.c Closed potential memory leak introduced by rev 5168 2013.11.08.095213 [5173] [5174] libburn/mmc.c Enforcing reasonable maximum read speeds even if the drive is lying 2013.11.10.163403 [5175] libburn/libburn.h libburn/drive.c libburn/libburn.ver New API call burn_disc_pretend_full_uncond() 2013.11.11.160915 [5177] libburn/mmc.c Bug fix: Drive LG BH16NS40 stalled on inspection of unformatted DVD+RW 11 Nov 2013 [] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.3.3 - 2013.11.11.172016 * Bug fix: Drive error reports were ignored during blanking and formatting * Bug fix: Drive LG BH16NS40 stalled on inspection of unformatted DVD+RW * New API call burn_disc_pretend_full_uncond() 2013.11.14.101636 [5180] libburn/libdax_msgs.h libburn/spc.h libburn/spc.c libburn/sg-linux.c Improved reaction on Linux SG_IO transport problems 2013.11.15.102314 [5182] libburn/mmc.c Enforcing reasonable minimum read speeds even if the drive is lying 2013.11.16.170431 [5184] libburn/options.c Prevented a memory leak that in most cases was closed by a race condition 2013.11.17.102351 [5185] libburn/write.c Resetting the drive failure status before starting random-access writing 2013.11.17.152544 [5187] libburn/sg-linux.c Corrected bugs introduced with rev 5180 2013.11.21.092012 [5189] libburn/drive.c Better reaction on drive errors during burn_drive_scan_and_grab() 12 Dec 2013 [5192] svn copy -m Branching for libburn release 1.3.4 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.3.4 2013.12.12.093001 [5193] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.3.4 12 Dec 2013 [5194] - cdrskin/add_ts_changes_to_libburn_1_3_2 - cdrskin/add_ts_changes_to_libburn_1_3_3 + cdrskin/add_ts_changes_to_libburn_1_3_4 + cdrskin/add_ts_changes_to_libburn_1_3_5 Updated cdrskin tarball generator 12 Dec 2013 [5195] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ----------------------------------- release - cdrskin-1.3.4 - 2013.12.12.093001 * Bug fix: Drive error reports were ignored during blanking and formatting * Bug fix: Drive LG BH16NS40 stalled on inspection of unformatted DVD+RW * New API call burn_disc_pretend_full_uncond() 2013.12.12.140531 [5199] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.3.5 12 Dec 2013 [5200] - cdrskin/add_ts_changes_to_libburn_1_3_2 - cdrskin/add_ts_changes_to_libburn_1_3_3 + cdrskin/add_ts_changes_to_libburn_1_3_4 + cdrskin/add_ts_changes_to_libburn_1_3_5 Updated cdrskin tarball generator 12 Dec 2013 [5201] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-1.3.5 - 2013.12.12.140531 12 Dec 2013 [5202] svn move -m libburn release 1.3.4 is ready http://svn.libburnia-project.org/libburn/branches/1.3.4 http://svn.libburnia-project.org/libburn/tags/1.3.4 2014.01.07.115938 [5215] libburn/transport.h libburn/drive.h libburn/drive.c libburn/mmc.c Registering all drive-media feature descriptors in burn_drive 2014.01.09.132159 [5216] libburn/libburn.h libburn/spc.c libburn/mmc.h libburn/mmc.c Interpreting feature 0x107 when deciding from where to get speed info 2014.01.09.214841 [5218] libburn/sg-linux.c Adapted Linux SG_IO adapter to scsi/sg.h of git.kernel.org 2014.01.15.174741 [5225] libburn/sg-linux.c Updated copyright claim in sg-linux.c 2014.01.15.174907 [5226] libburn/drive.h libburn/drive.c libburn/write.c Improved handling of stdio pseudo-drives after aborted burn runs 2014.02.04.112944 [5233] libburn/transport.h libburn/spc.h libburn/spc.c Implemented a generic verification whether a SPC device is a MMC device 2014.02.05.124803 [5234] libburn/spc.c Trying to better handle MMC violating replies of MODE SENSE (Block Descriptors) 2014.02.05.185801 [5235] libburn/spc.c Fixed bugs introduced with previous commit 2014.02.05.191839 [5237] libburn/libburn.h libburn/transport.h libburn/drive.c libburn/options.c libburn/async.c libburn/spc.c libburn/mmc.c libburn/sg-freebsd.c Prepared for possible demise of mode page 2A 2014.02.07.180650 [5238] libburn/write.c Avoiding to have two file descriptors open to the same stdio drive 2014.02.10.213159 [5244] libburn/drive.c libburn/spc.c libburn/mmc.c Being more rugged towards missing MODE SENSE info 2014.02.10.213500 [5245] libburn/os.h libburn/sg.c + libburn/os-netbsd.h + libburn/sg-netbsd.c Inmplemented a system adapter for NetBSD 10 Feb 2014 [5246] Makefile.am Introduced NetBSD system adapter in tarball generator 11 Feb 2014 [5247] configure.ac acinclude.m4 Silenced warnings about -Wchar-subscripts, added /usr/local for NetBSD 12 Feb 2014 [5249] doc/comments cdrskin/README Mentioned support for NetBSD 2014.02.13.205358 [5252] libburn/sg-netbsd.c Corrected size determination of NetBSD block devices 2014.02.14.200129 [5253] libburn/drive.c Improved workaround for missing mode page 2A 2014.02.16.203859 [5254] libburn/spc.c Inquiring GET PERFORMANCE independently of existence of mode page 2A 2014.02.19.111017 [5255] libburn/os.h libburn/sg.c libburn/sg-netbsd.c Removed Linux compilability mock-up from sg-netbsd.c 2014.03.01.101537 [5257] libburn/libburn.h libburn/transport.h libburn/drive.c Improved emulation of mode page 2A 04 Mar 2014 [5260] svn copy -m Branching for libburn release 1.3.6 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.3.6 2014.03.04.110001 [5261] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.3.6 04 Mar 2014 [5262] - cdrskin/add_ts_changes_to_libburn_1_3_4 - cdrskin/add_ts_changes_to_libburn_1_3_5 + cdrskin/add_ts_changes_to_libburn_1_3_6 + cdrskin/add_ts_changes_to_libburn_1_3_7 Updated cdrskin tarball generator 04 Mar 2014 [5263] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ----------------------------------- release - cdrskin-1.3.6 - 2014.03.04.110001 * New system adapter for NetBSD 2014.03.04.161835 [5269] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.3.7 04 Mar 2014 [5270] - cdrskin/add_ts_changes_to_libburn_1_3_4 - cdrskin/add_ts_changes_to_libburn_1_3_5 + cdrskin/add_ts_changes_to_libburn_1_3_6 + cdrskin/add_ts_changes_to_libburn_1_3_7 Updated cdrskin tarball generator 04 Mar 2014 [5271] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-1.3.7 - 2014.03.04.162814 04 Mar 2014 [5272] svn move -m libburn release 1.3.6 is ready http://svn.libburnia-project.org/libburn/branches/1.3.6 http://svn.libburnia-project.org/libburn/tags/1.3.6 2014.03.14.095413 [5281] libburn/write.c Bug fix: CD TAO with multiple tracks could cause a buffer overrun 14 Mar 2013 [5282] ChangeLog cdrskin/cdrskin_eng.html Updated change log ------------------------------------ cycle - cdrskin-1.3.7 - 2014.03.14.100747 * Bug fix: CD TAO with multiple tracks could cause a buffer overrun 2014.03.17.221810 [5284] libburn/sg.c Bug fix: Compilation warning for unsupported systems mutated into an error 18 Mar 2014 [5285] svn copy -m Branching for libburn bugfix release 1.3.6.pl01 http://svn.libburnia-project.org/libburn/tags/1.3.6 http://svn.libburnia-project.org/libburn/branches/1.3.6.pl01 2014.03.18.100001 [5286 tag 1.3.6.pl01] libburn/write.c Bug fix: CD TAO with multiple tracks could cause a buffer overrun 2014.03.18.100001 [5287 tag 1.3.6.pl01] libburn/sg.c Bug fix: Compilation warning for unsupported systems mutated into an error 18 Mar 2014 [5291] svn move -m libburn bugfix release 1.3.6.pl01 is ready http://svn.libburnia-project.org/libburn/branches/1.3.6.pl01 http://svn.libburnia-project.org/libburn/tags/1.3.6.pl01 ------------------------------ release - cdrskin-1.3.6.pl01 - 2014.03.18.100001 * Bug fix: Compilation warning for unsupported systems mutated into an error * Bug fix: CD TAO with multiple tracks could cause a buffer overrun 2014.04.06.111004 [5297] cdrskin/cdrskin.c Bug fix: Minimum drive buffer fill was measured before the buffer could get full 2014.04.09.152035 [5302] libburn/libburn.h libburn/options.c libburn/write.c Bug fix: A final fsync(2) was performed with stdio drives, even if not desired 2014.04.13.120906 [5309] libburn/write.c Restoring capability of burn_random_access_write() to fsync() (lost in rev 5302) 2014.04.14.103311 [5310] libburn/write.c Retrying write(2) if it returns a short non-negative write count 2014.04.19.114816 [5315] libburn/libburn.h libburn/read.c Improved read retrying with DVD and BD media 2014.04.29.061645 [5324] libburn/async.c Bug fix: Wrong stack usage caused SIGBUS on sparc when compiled by gcc -O2 2014.05.03.103448 [5328] libburn/mmc.c Bug fix: A failed MMC BLANK command did not cause error indication by libburn 2014.06.09.183251 [5335] libburn/libburn.h libburn/drive.c libburn/util.h libburn/util.c libburn/sg.h libburn/sg-dummy.c libburn/sg-freebsd.c libburn/sg-freebsd-port.c libburn/sg-libcdio.c libburn/sg-linux.c libburn/sg-netbsd.c libburn/sg-solaris.c Improved drive capacity estimation for sparse regular files 2014.06.10.130721 [5336] libburn/drive.c Fixed a SIGSEGV introduced by previous revision 2014.06.20.145224 [5346] libburn/sg-netbsd.c Reacted on compiler warning of gcc on NetBSD-current 27 Jun 2014 [5348] svn copy -m Branching for libburn release 1.3.8 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.3.8 2014.06.28.060001 [5349] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.3.8 27 Jun 2014 [5350] - cdrskin/add_ts_changes_to_libburn_1_3_6 - cdrskin/add_ts_changes_to_libburn_1_3_7 + cdrskin/add_ts_changes_to_libburn_1_3_8 + cdrskin/add_ts_changes_to_libburn_1_3_9 Updated cdrskin tarball generator 27 Jun 2014 [5351] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ----------------------------------- release - cdrskin-1.3.8 - 2014.06.28.060001 * Bug fix: Wrong stack usage caused SIGBUS on sparc when compiled by gcc -O2 * Bug fix: Minimum drive buffer fill was measured by cdrskin before the buffer could get full * Bug fix: A failed MMC BLANK command did not cause error indication by libburn * Bug fix: A final fsync(2) was performed with stdio drives, even if not desired 2014.06.28.062807 [5356] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.3.9 28 Jun 2014 [5357] - cdrskin/add_ts_changes_to_libburn_1_3_6 - cdrskin/add_ts_changes_to_libburn_1_3_7 + cdrskin/add_ts_changes_to_libburn_1_3_8 + cdrskin/add_ts_changes_to_libburn_1_3_9 Updated cdrskin tarball generator 28 Jun 2014 [5358] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - cdrskin-1.3.9 - 2014.06.28.062807 28 Jun 2014 [5359] svn move -m libburn release 1.3.8 is ready http://svn.libburnia-project.org/libburn/branches/1.3.8 http://svn.libburnia-project.org/libburn/tags/1.3.8 06 Jul 2014 [5364] COPYRIGHT Updated copyright date 2014.07.14.180122 [5366] libburn/drive.c Fixed a wrong read access to memory. Reported by valgrind of lian jianfei, 2014.07.31.115829 [5367] libburn/spc.h libburn/spc.c New internal function for SCSI-logging arbitrary texts 2014.07.31.122752 [5368] libburn/sg-linux.c Debugging macro Libburn_debug_dxferP to check sg_io_hdr_t.dxferp 2014.07.31.123105 [5369] libburn/libdax_msgs.h libburn/mmc.c Keeping GET CONFIGURATION from requesting more than 4 KB of data 2014.08.14.085604 [5370] libburn/spc.c Improved error message for erase failures 2014.08.14.085850 [5371] libburn/mmc.c Small correction of rev5369 2014.08.31.110308 [5373] libburn/drive.c libburn/mmc.c Reduced number of GET CONFIGURATION transactions 2014.08.31.121421 [5374] libburn/drive.c Removed obsolete conditional code 2014.09.01.161217 [5375] libburn/libburn.h libburn/transport.h libburn/read.c libburn/spc.c libburn/mmc.c New flag bit5 with burn_read_data() and burn_read_audio() 2014.09.01.183908 [5376] libburn/spc.c Fixed a bug introduced with previous revision 2014.11.23.190942 [5395] cdrskin/cdrskin.c Bug fix: Double free with cdrskin -vvv. Introduced with rev 5065, version 1.3.1 2014.11.26.164119 [5397] libburn/drive.c libburn/file.c libburn/read.c libburn/write.c libburn/structure.c libburn/sector.c libburn/mmc.c libburn/sg-dummy.c libburn/sg-libcdio.c libburn/libdax_audioxtr.c libburn/ddlpa.c Equipped all non-system-dependent open(2) calls with O_BINARY 2014.12.06.175109 [5399] libburn/sg-solaris.c Added debugging messages for drive recognition on Solaris 2014.12.21.221245 [5400] acinclude.m4 libburn/sg-solaris.c Using libvolmgt on older Solaris 2014.12.29.105910 [5403] Makefile.am Fixed a typo in message of make install. Thanks to Jakub Wilk. 2015.03.08.073635 [5412] libburn/spc.c Human readable error message for 3 32 00 2015.05.17.083420 [5426] libburn/libburn.h doc/mediainfo.txt Corrected outdated references to function name burn_get_media_product_id() 17 May 2015 [5427] svn copy -m Branching for libburn release 1.4.0 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.4.0 2015.05.17.110001 [5428] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.4.0 17 May 2015 [5429] - cdrskin/add_ts_changes_to_libburn_1_3_8 - cdrskin/add_ts_changes_to_libburn_1_3_9 + cdrskin/add_ts_changes_to_libburn_1_4_0 + cdrskin/add_ts_changes_to_libburn_1_4_1 Updated cdrskin tarball generator 17 May 2015 [5430] ChangeLog cdrskin/changelog.txt Documented changes and release timestamp 17 May 2015 [5435] acinclude.m4 Made sure that cdrskin on Solaris gets linked with libvolmgt ----------------------------------- release - cdrskin-1.4.0 - 2015.05.17.110001 * Bug fix: Double free with cdrskin -vvv. Introduced with rev 5065, version 1.3.1 * Bug fix: Wrong read access to memory. Reported by valgrind of lian jianfei. 2015.05.17.193727 [5437] acinclude.m4 Made sure that cdrskin on Solaris gets linked with libvolmgt 2015.05.17.200218 [5438] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.4.1 17 May 2015 [5439] - cdrskin/add_ts_changes_to_libburn_1_3_8 - cdrskin/add_ts_changes_to_libburn_1_3_9 + cdrskin/add_ts_changes_to_libburn_1_4_0 + cdrskin/add_ts_changes_to_libburn_1_4_1 Updated cdrskin tarball generator 17 May 2015 [5440] ChangeLog cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.4.1 - 2015.05.17.202234 17 May 2015 [5441] svn move -m libburn release 1.4.0 is ready http://svn.libburnia-project.org/libburn/branches/1.4.0 http://svn.libburnia-project.org/libburn/tags/1.4.0 18 May 2015 [5442] doc/doxygen.conf.in Trying to become suitable for Debian reproducible builds 2015.05.25.204252 [5448] libburn/util.c doc/mediainfo.txt Registered BD-R producer Millenniata. Thanks to Lucjan Bryndza. 2015.05.26.054224 [5449] libburn/mmc.c libburn/util.c Bug fix: burn_disc_get_media_id() returned BD identifiers 2 chars too long 25 Jun 2015 [5452] cdrskin/README Mentioned option -use_libcdio of cdrskin/compile_cdrskin.sh 2015.06.25.192439 [5453] libburn/sg-linux.c Removed unused macro definitions 2015.08.01.135727 [5463] libburn/libburn.h libburn/sg-freebsd-port.c libburn/sg-libcdio.c libburn/sg-solaris.c libburn/sg-netbsd.c libburn/sg-linux.c libburn/sg-dummy.c cdrskin/cdrskin.c cdrskin/cdrfifo.h libburn/ecma130ab.c configure.ac README cdrskin/cdrskin.1 cdrskin/cdrskin_eng.html cdrskin/compile_cdrskin.sh cdrskin/make_timestamp.sh doc/cdtext.txt doc/comments doc/cookbook.txt doc/ddlp.txt test/libburner.c cdrskin/add_ts_changes_to_libburn_1_4_0 cdrskin/add_ts_changes_to_libburn_1_4_1 Changed wrong use of "resp." in docs 2015.08.02.141552 [5464] cdrskin/convert_man_to_html.sh + cdrskin/unite_html_b_line.c Reacting on changes in the output of my local man -H 2015.08.30.185208 [5471] libburn/libburn.h Small change in API documentation about burn_drive_set_buffer_waiting() 2015.08.30.185714 [5472] cdrskin/cdrskin.c cdrskin/cdrskin.1 New paramaters for option modesty_on_drive= timeout_sec, min_usec, max_usec 30 Aug 2015 [5473] libburn/mmc.c libburn/libdax_msgs.h cdrskin/cdrskin.1 Fixed typos reported by J.S.Junior 30Aug 2015 [5474] cdrskin/cdrskin.1 Avoiding to trigger lintian check "allows to allows one to" 2015.09.01.065852 [5475] libburn/libburn.h libburn/options.h libburn/mmc.c libburn/ecma130ab.c libburn/sg-linux.c libburn/sg-solaris.c libburn/sg-netbsd.c libburn/sg-libcdio.c libburn/sg-freebsd-port.c test/libburner.c test/telltoc.c README cdrskin/cdrskin.1 cdrskin/cdrskin_eng.html cdrskin/wiki_plain.txt doc/cookbook.txt More hunt for "allow to" 2015.09.17.160121 [5480] acinclude.m4 Recognizing again Debian GNU/kFreeBSD, regression by rev 5437 2015.09.23.110012 [5486] libburn/drive.c Bug fix: burn_disc_get_multi_caps() returned 2048 bytes too many in .start_range_high 23 Sep 2015 [5487] test/libburner.c test/telltoc.c Mutally mentioned the other demo program in their introdution comments 2015.10.18.125353 [5493] libburn/libburn.h libburn/transport.h libburn/drive.c libburn/spc.h libburn/spc.c libburn/mmc.c New API calls burn_drive_get_serial_no() and burn_drive_get_media_sno() 2015.10.18.133327 [5495] cdrskin/cdrskin.c New -toc output lines "Drive id" and "Media id" 2015.10.19.174459 [5496] cdrskin/cdrskin.c Bug fix: Media summary session count of blank and closed media was short by 1 23 Oct 2015 [5498] libburn/sg-linux.c Prevented potential use of file pointer after fopen() failed 2015.10.23.103324 [5499] libburn/spc.c Made sure that only existent CDB bytes get accessed by SCSI command logging. Coverity CID 21806. 2015.10.23.121415 [5500] libburn/sg-linux.c Completed rev 5498, which is a reaction on Coverity CID 21805. 2015.10.23.122644 [5501] libburn/spc.c Bug fix: Endless loop if transport error occurs while waiting for drive ready. 2015.10.23.123606 [5502] test/fake_au.c Checking program arguments of test/fake_au. Coverity CID 21832. 2015.10.23.124544 [5503] cdrskin/cdrskin.c Prevented use of an uninitialized variable. Coverity CID 21842. 2015.10.23.130707 [5504] libburn/sg-linux.c Removed surplus code statement which used uninitialized variable. Coverity CID 21843. 23 Oct 2015 [5505] [5506] test/poll.c Completed initialization of struct sigaction in test/poll.c. Coverity CID 21845. 2015.10.23.135526 [5507] cdrskin/cdrskin.c Moved unused variable into its ifdef case. Coverity CID 21799. 2015.10.23.140731 [5508] cdrskin/cdrskin.c Enabled display of pacifier state "formatting" in cdrskin. Coverity CID 21793. 2015.10.23.160546 [5509] libburn/drive.c Avoided a potential memory leak with debug messages. Coverity CID 21808. 2015.10.23.162527 [5510] libburn/options.c Closed a potential memory leak with CD-TEXT production. Coverity CID 21809. 2015.10.23.164931 [5511] libburn/write.c Closed a potential memory leak with writing to stdio pseudo drives. Coverity CID 21810. 2015.10.24.075251 [5512] cdrskin/cdrskin.c Closed memory leak with errors while attaching fifo. Coverity CID 21811. 24 Oct 2015 [5513] test/telltoc.c Closed memory leak in telltoc. Coverity CID 21812. 24 Oct 2015 [5514] test/telltoc.c Updated telltoc to BD and other old novelties. 24 Oct [5515] test/telltoc.c Closing output file pointer of telltoc --read_and_print. Coverity CID 21813. 24 Oct 2015 [5516] test/libburner.c Closed various memory leaks in libburner. Coverity CID 21814, CID 21815. 2015.10.26.092929 [5518] cdrskin/cdrskin.c Closed file pointer leak in case of error with audio source. Coverity CID 21816. 2015.10.26.102430 [5519] cdrskin/cdrskin.c Closed memory leak in case of burn failure. Coverity CID 21817. 2015.10.26.105324 [5520] libburn/cdtext.c libburn/libdax_msgs.h Avoiding to allocate empty buffer for texp packs. Indirectly Coverity CID 21818. 2015.10.26.105754 [5521] cdrskin/cdrskin.c Clarified the scope of an allocated variable. Coverity CID 21818. 2015.10.26.145305 [5522] cdrskin/cdrskin.c Distinguishing between self-opened and inherited file descriptors as track input. Coverity CID 21819. 2015.10.26.145941 [5523] libburn/structure.c Closed a memory leak with error opening audio input of CUE sheet. Coverity CID 21820. 2015.10.26.150529 [5524] libburn/structure.c Closed a file pointer leak with CUE file interpretation. Coverity CID 21821. 2015.10.26.151914 [5525] libburn/sg-linux.c Added missing break statement to SG_IO error reporting. Coverity CID 21803. 2015.10.26.153745 [5526] libburn/sector.c Corrected reaction on unsupported track modes. Coverity CID 21804. 2015.10.26.154728 [5527] libburn/mmc.c Preventing very improbable signed 32 bit overflow with MMC speed range. Coverity CID 21823. 2015.10.26.183813 [5528] libburn/drive.c Fixed memory waste by oversized feature descriptor objects. Coverity CID 21824. 2015.10.26.184231 [5529] libburn/libdax_audioxtr.c Removed overly optimistic test ocde for SUN audio. Coverity CID 21846. 2015.10.28.153929 [5530] libburn/write.c Removed a surplus test. Coverity CID 21796. 2015.10.28.162205 [5531] libburn/drive.c Closed memory leak with minor cdrskin jobs. Found by valgrind. 28 Oct 2015 [5532] test/fake_au.c Testing all arguments of fake_au for oversize. Coverity CID 21830. 2015.10.28.162537 [5533] [5534] cdrskin/cdrskin.c Prevented a quite unlikely buffer overflow by argument. Coverity CID 21827. 28 Oct 2015 [5535] test/telltoc.c Prevented a quite unlikely buffer overflow by argument. Coverity CID 21831. 2015.10.29.082150 [5536] cdrskin/cdrskin.c Cared for an improbable error case. Coverity CID 21784. 29 Oct 2015 [5537] Makefile.am test/structest.c Removed useless and faulty test/structest.c. Coverity CID 21785. 29 Oct 2015 [5538] libburn/libburn.ver Forgot to put burn_drive_get_serial_no and burn_drive_get_media_sno into API 29 Oct 2015 [5539] test/telltoc.c Handling failure to write to disk file. Coverity CID 21786. 2015.10.29.105609 [5540] libburn/mmc.c Removed a surplus development variable. Coverity CID 21795. 2015.10.31.122151 [5541] cdrskin/cdrskin.c Reacting on (improbable) failure of burn_disc_pretend_full(). Coverity CID 21847. 2015.11.01.102608 [5542] libburn/mmc.c Unified the tests against rogue GET CONFIGURATION replies. Coverity CID 21794. 2015.11.01.125502 [5543] libburn/mmc.c Corrected a mistake made in rev 5542. Coverity CID 28636. 2015.11.01.184659 [5544] libburn/structure.c Closed a memory leak with error around C-TEXT. Another try on Coverity CID 21818. 2015.11.01.194734 [5545] libburn/structure.c Took into respect that burn_session_by_cue_file() parameter text_packs may be NULL. 2015.11.01.195218 [5546] libburn/mmc.c Completed revision 5527. Coverity CID 21823. 02 Nov 2015 [5547] Corrected misperception of telltoc --read_and_print 1:. 2015.11.02.151241 [5548] libburn/mmc.c Completed revision 5527. Coverity CID 21823. 2015.11.25.093414 [5613] libburn/sg-freebsd.c Fixed a theoretical memory leak in actually unused code. Reported by cppcheck. 28 Nov 2015 [5614] svn copy -m Branching for libburn release 1.4.2 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.4.2 2015.11.28.120001 [5615] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.4.2 28 Nov 2015 [5616] - cdrskin/add_ts_changes_to_libburn_1_4_0 - cdrskin/add_ts_changes_to_libburn_1_4_1 + cdrskin/add_ts_changes_to_libburn_1_4_2 + cdrskin/add_ts_changes_to_libburn_1_4_3 Updated cdrskin tarball generator 28 Nov 2015 [5617] ChangeLog cdrskin/changelog.txt Updated change log ----------------------------------- release - cdrskin-1.4.2 - 2015.11.28.120001 * Bug fix: burn_disc_get_media_id() returned BD identifiers 2 chars too long * Bug fix: burn_disc_get_multi_caps() returned 2048 bytes too many in caps.start_range_high * Bug fix: Media summary session count of blank and closed media was short by 1 * Bug fix: Endless loop if transport error occurs while waiting for drive ready * New API calls burn_drive_get_serial_no() and burn_drive_get_media_sno() * Result of a Coverity audit: 40+ code changes, but no easy-to-trigger bugs 2015.11.28.210034 [5621] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.4.3 28 Nov 2015 [5622] - cdrskin/add_ts_changes_to_libburn_1_4_0 - cdrskin/add_ts_changes_to_libburn_1_4_1 + cdrskin/add_ts_changes_to_libburn_1_4_2 + cdrskin/add_ts_changes_to_libburn_1_4_3 Updated cdrskin tarball generator 28 Nov 2015 [5623] ChangeLog cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.4.3 - 2015.11.28.211845 28 Nov 2015 [5624] svn move -m libburn release 1.4.2 is ready http://svn.libburnia-project.org/libburn/branches/1.4.2 http://svn.libburnia-project.org/libburn/tags/1.4.2 10 Dec 2015 [5634] configure.ac acinclude.m4 Checking for availability of LIBBURN_ARCH_LIBS 2016.01.27.174553 [5653] cdrskin/cdrskin.c Bug fix: "failed to attach fifo" when burning from stdin. Regression of 1.4.2, rev 5522. 28 Jan 2016 [5564] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.4.3 - 2016.01.28.101456 * Bug fix: cdrskin "failed to attach fifo" when burning from stdin. Regression of 1.4.2, rev 5522. 29 Jan 2016 [5655] svn copy -m Branching for libburn bugfix release 1.4.2.pl01 http://svn.libburnia-project.org/libburn/tags/1.4.2 http://svn.libburnia-project.org/libburn/branches/1.4.2.pl01 2016.01.29.100001 [5656] cdrskin/cdrskin_timestamp.h cdrskin/cdrskin.c Bug fix: cdrskin "failed to attach fifo" when burning from stdin. Regression of 1.4.2, rev 5522. 29 Jan 2016 [5657] README cdrskin/README ChangeLog cdrskin/changelog.txt cdrskin/cdrskin_eng.html Mentioned bug fix and pl01 29 Jan 2016 [5658] svn move -m libburn bugfix release 1.4.2.pl01 is ready http://svn.libburnia-project.org/libburn/branches/1.4.2.pl01 http://svn.libburnia-project.org/libburn/tags/1.4.2.pl01 29 Jan 2016 [5659] README ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated README and change log ------------------------------ release - cdrskin-1.4.2.pl01 - 2016.01.29.100001 * Bug fix: cdrskin "failed to attach fifo" when burning from stdin. Regression of 1.4.2, rev 5522. 2016.02.05.130154 [5663] libburn/structure.c Fixed a typo found by lintian 2016.02.05.130456 [5664] cdrskin/cdrskin.c Fixed a typo found by lintian 2016.02.18.114857 [5667] libburn/transport.h Changed type of length counters of serial numbers from char to int. Warning of gcc on Ubuntu powerpc. 2016.03.08.073402 [5672] libburn/libburn.h libburn/transport.h libburn/drive.c libburn/structure.c libburn/mmc.c Trying to find out whether READ CAPACITY on CD media includes 2 TAO end blocks 2016.03.10.195636 [5675] libburn/libdax_msgs.c libburn/mmc.c libburn/write.c Replaced unused timezone parameter of gettimeofday() by NULL 2016.03.17.203356 [5680] libburn/sg-linux.c Bug fix: Linux /proc/sys/dev/cdrom/info overrode SCSI dev family conversion 18 Mar 2016 [i5682] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.4.3 - 2016.03.18.130253 * Bug fix: Option drive_scsi_dev_family=sg did not convert /dev/sr* to /dev/sg* 2016.03.30.113137 [5686] libburn/cdtext.c Bug fix: burn_make_input_sheet_v07t() falsly recognized double byte encoding 2016.03.30.185545 [5687] libburn/options.c Bug fix: Double free at end of run if burn_write_opts_set_leadin_text() is used 30 Mar 2016 [5688] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.4.3 - 2016.03.30.191115 * Bug fix: burn_make_input_sheet_v07t() falsly recognized double byte encoding * Bug fix: Double free at end of run if burn_write_opts_set_leadin_text() is used 2016.03.30.200445 [5689] libburn/mmc.c Bug fix: DVD book type of DVD+RW DL and DVD+R DL was reported wrong. Thanks to Etienne Bergeron. 2016.03.31.124348 [5690] libburn/cdtext.c Bug fix: burn_make_input_sheet_v07t() used character code of block 0 for all blocks 19 Apr 2016 [5693] acinclude.m4 Makefile.am Prevented option --version-script with linker runs of binaries. By Matthias Klose. 27 Apr 2016 [5697] configure.ac Removed option --silent from libtool runs 01 Jul 2016 [5700] svn copy -m Branching for libburn release 1.4.4 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.4.4 2016.07.01.110001 [5701] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.4.4 01 Jul 2016 [5702] - cdrskin/add_ts_changes_to_libburn_1_4_2 - cdrskin/add_ts_changes_to_libburn_1_4_3 + cdrskin/add_ts_changes_to_libburn_1_4_4 + cdrskin/add_ts_changes_to_libburn_1_4_5 Updated cdrskin tarball generator 01 Jul 2016 [5703] ChangeLog cdrskin/changelog.txt Updated change log ----------------------------------- release - cdrskin-1.4.4 - 2016.07.01.110001 * Bug fix: Option drive_scsi_dev_family=sg did not convert /dev/sr* to /dev/sg* * Bug fix: burn_make_input_sheet_v07t() falsly recognized double byte encoding * Bug fix: Double free at end of run if burn_write_opts_set_leadin_text() is used * Bug fix: DVD book type of DVD+RW DL and DVD+R DL was reported wrong. Thanks to Etienne Bergeron. 2016.07.01.190459 [5707] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.4.5 01 Jul 2016 [5708] - cdrskin/add_ts_changes_to_libburn_1_4_2 - cdrskin/add_ts_changes_to_libburn_1_4_3 + cdrskin/add_ts_changes_to_libburn_1_4_4 + cdrskin/add_ts_changes_to_libburn_1_4_5 Updated cdrskin tarball generator 01 Jul 2016 [5709] ChangeLog cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.4.5 - 2016.07.01.192618 01 Jul 2016 [5710] svn move -m libburn release 1.4.4 is ready http://svn.libburnia-project.org/libburn/branches/1.4.4 http://svn.libburnia-project.org/libburn/tags/1.4.4 03 Jul 2016 [5716] cdrskin/cdrskin.1 Fixed three spelling errors found by lintian 2016.07.21.102919 [5720] libburn/os.h libburn/os-netbsd.h libburn/sg.c libburn/sg-netbsd.c Enabled SCSI command execution on OpenBSD. Thanks to SASANO Takayoshi. 2016.07.21.141139 [5722] libburn/sg-netbsd.c Fixed a macro typo in rev 5720. Thanks again to SASANO Takayoshi. 2016.07.21.175105 [5723] libburn/os.h libburn/os-netbsd.h libburn/sg-netbsd.c Mentioned OpenBSD in adapter files 2016.07.23.104100 [5724] README libburn/libburn.h libburn/drive.c libburn/write.c libburn/sector.c libburn/async.c libburn/libdax_msgs.h libburn/transport.h libburn/spc.c libburn/mmc.h libburn/mmc.c libburn/crc.c libburn/sg.h libburn/sg-dummy.c libburn/sg-netbsd.c libburn/sg-linux.c libburn/sg-libcdio.c libburn/sg-solaris.c libburn/sg-freebsd.c cdrskin/cdrskin.c cdrskin/cdrfifo.h cdrskin/cdrfifo.c cdrskin/README test/libburner.c test/telltoc.c cdrskin/wiki_plain.txt doc/cookbook.txt doc/mediainfo.txt Reacted on some of the complaints of codespell 2016.07.30.140328 [5725] libburn/libburn.h libburn/transport.h libburn/drive.c libburn/os-netbsd.h libburn/spc.h libburn/sbc.c libburn/mmc.c New API call burn_drive_set_immed() 2016.07.30.140859 [5726] cdrskin/cdrskin.c cdrskin/cdrskin.1 New cdrskin option use_immed_bit= 30 Jul 2016 [5727] libburn/libburn.ver Forgot to commit the libburn.ver file with the new API symbols 2016.07.30.200623 [5728] libburn/os-netbsd.h Enabled overriding of OpenBSD default for Immed usage 2016.07.31.190417 [5732] libburn/structure.c Bug fix: SAO CD could be perceived 2 blocks too short. Regression in 1.4.4 by rev 5672. 23 Aug 2016 [5752] configure.ac Eased re-enabling of libtool --silent at bootstrap time 28 Aug 2016 [5754] doc/doxygen.conf.in Removed @abs_top_builddir@ from doxygen.conf.in where it hampered reproducibe build (Removed tags XML_SCHEMA, XML_DTD from doxygen.conf.in because doxygen-1.8.11 says they are obsolete) 2016.09.10.083123 [5756] libburn/spc.c Added a few ASCQ texts to error code 5,30,X 16 Sep 2016 [5756] svn copy -m Branching for libburn release 1.4.6 http://svn.libburnia-project.org/libburn/trunk http://svn.libburnia-project.org/libburn/branches/1.4.6 2016.09.16.113001 [5761] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.4.6 16 Sep 2016 [5762] - cdrskin/add_ts_changes_to_libburn_1_4_4 - cdrskin/add_ts_changes_to_libburn_1_4_5 + cdrskin/add_ts_changes_to_libburn_1_4_6 + cdrskin/add_ts_changes_to_libburn_1_4_7 Updated cdrskin tarball generator 16 Sep 2016 [5763] ChangeLog cdrskin/changelog.txt Updated change log ----------------------------------- release - cdrskin-1.4.6 - 2016.09.16.113001 * Bug fix: SAO CD could be perceived 2 blocks to short. Regression in 1.4.4 by rev 5672. * Now operating drives on OpenBSD * New API call burn_drive_set_immed() * New cdrskin option use_immed_bit= 2016.09.16.190600 [5768] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.4.7 16 Sep 2016 [5769] - cdrskin/add_ts_changes_to_libburn_1_4_4 - cdrskin/add_ts_changes_to_libburn_1_4_5 + cdrskin/add_ts_changes_to_libburn_1_4_6 + cdrskin/add_ts_changes_to_libburn_1_4_7 Updated cdrskin tarball generator 16 Sep 2016 [5770] ChangeLog cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.4.7 - 2016.09.16.190600 16 Sep 2016 [5771] libburn release 1.4.6 is ready http://svn.libburnia-project.org/libburn/branches/1.4.6 http://svn.libburnia-project.org/libburn/tags/1.4.6 2016.11.18.131813 [80175e0] [5778] libburn/libburn.h libburn/transport.h libburn/drive.c libburn/write.c libburn/mmc.c libburn/libburn.ver Let random access writing obey simulation mode of previous sequential run. New API call burn_drive_reset_simulate(). 2016.11.18.132335 [19a1b8e] [5579] cdrskin/cdrskin.c Bug fix: Option -dummy did not affect writing by direct_write_amount= 11 Jan 2017 [074f88f] [5791] cdrskin/cdrskin.1 Corrected documentation of bytes/second value of 1x CD speed 27 Jan 2017 [6b020a3] [5792] configure.ac acinclude.m4 COPYRIGHT Detecting Linux systems without generic SCSI support by scsi/scsi.h 27 Jan 2017 [6f6bf68] [5795] acinclude.m4 Small adjustement for previous commit 2017.01.28.200155 [724d518] [5797] libburn/file.h libburn/file.c libburn/async.h libburn/async.c Giving up use of function pthread_cancel() 2017.01.28.200802 [858e42d] [5798] cdrskin/cdrskin.c cdrskin/cdrskin.1 Corrected harmless typos found by lintian 2017.02.06.135800 [d71d80d] [5800] libburn/libburn.h libburn/drive.c libburn/libburn.ver New API call burn_drive_get_bd_r_pow() 2017.02.06.141213 [a4b688a] [5801] libburn/libburn.h libburn/options.c libburn/write.c Refusing to write to BD-R if formatted to Pseudo Overwrite 2017.02.06.161439 [2bb8bf4] [5802] cdrskin/cdrskin.c Reporting POW formatted BD-R as unusable 2017.07.15.194129 [113f587] libburn/read.c Fixed the block count in error message about read attempt after medium end 2017.09.12.110001 [516dc2c] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.4.8 12 Sep 2017 [ea486a1] - cdrskin/add_ts_changes_to_libburn_1_4_6 - cdrskin/add_ts_changes_to_libburn_1_4_7 + cdrskin/add_ts_changes_to_libburn_1_4_8 + cdrskin/add_ts_changes_to_libburn_1_4_9 Updated cdrskin tarball generator 12 Sep 2017 [260fc29] ChangeLog cdrskin/changelog.txt Updated change log ----------------------------------- release - cdrskin-1.4.8 - 2017.09.12.110001 * Bug fix: Option -dummy did not affect writing by direct_write_amount= * New API call burn_drive_get_bd_r_pow() * Refusing to write to BD-R if formatted to Pseudo Overwrite 13 Sep 2017 git tag "release-1.4.8" 2017.09.13.075728 [f1fc16b] Makefile.am configure.ac libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/cdrskin_timestamp.h Made number transition to 1.4.9 13 Sep 2017 [ef0d713] ChangeLog cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.4.9 - 2017.09.13.075728 2017.09.16.095011 [2886b82] libburn/mmc.c Reacted on compiler warning about theoretical overflow of an sprintf 2017.09.16.105142 [99e828c] libburn/write.c Reacted on compiler warning about theoretical overflow of an sprintf 2017.09.16.105713 [b5b5cc1] libburn/write.c Reacted on compiler warning about theoretical overflow of an sprintf 2017.12.31.111507 [ef2fa1d] libburn/libdax_audioxtr.c + doc/waveformat.txt Adapting to surplus chunks and subchunks in .wav files 2017.12.31.114757 [95f5b1c] libburn/libdax_audioxtr.h Added missing header file for previous commit 2018.02.05.103304 [84fad99] cdrskin/cdrfifo.c Bug fix: cdrskin threw errno 22 on data file input if libburn is configured --enable-track-src-odirect 2018.02.05.103650 [c84889b] libburn/sg-linux.c Reporting in SCSI log when commands get repeated 05 Feb 2018 [e35995b] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.4.9 - 2018.02.05.124100 * Bug fix: cdrskin threw errno 22 on data file input if libburn is configured with --enable-track-src-odirect 2018.08.08.152622 [ff039e8] libburn/libdax_msgs.h libburn/file.c Bug fix: SIGSEGV could happen if a track ended by reaching its fixed size while the track source still was willing to deliver bytes 2018.08.26.141619 [2fe4b3c] cdrskin/cdrskin.c Restricted enforcement of minimum track size to CD media profiles 2018.09.02.200129 [3fdf118] libburn/sg-linux.c Bug fix: Device file comparison parameters were recorded wrong with Linux sg 2018.09.06.100353 [d32bb30] cdrskin/cdrfifo.c With --enable-track-src-odirect, do not report error 22 due to unaligned EOF 2018.09.15.083001 [c75ad31] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.5.0 15 Sep 2018 [255afdd] - cdrskin/add_ts_changes_to_libburn_1_4_8 - cdrskin/add_ts_changes_to_libburn_1_4_9 + cdrskin/add_ts_changes_to_libburn_1_5_0 + cdrskin/add_ts_changes_to_libburn_1_5_1 Updated cdrskin tarball generator 15 Sep 2018 [9ee80ee] ChangeLog cdrskin/changelog.txt Updated change log 15 Sep 2018 [ef3609e] cdrskin/cdrskin.c cdrskin/changelog.txt Fixed a typo that was made during version leap 15 Sep 2018 [c2bcf78] cdrskin/add_ts_changes_to_libburn_1_5_0 cdrskin/add_ts_changes_to_libburn_1_5_1 cdrskin/changelog.txt Added to cdrskin standalone scripts a compile run for unite_html_b_line.c ----------------------------------- release - cdrskin-1.5.0 - 2018.09.15.083001 * Bug fix: cdrskin threw errno 22 on data file input if libburn is configured with --enable-track-src-odirect * Bug fix: SIGSEGV could happen if a track ended by reaching its fixed size while the track source still was willing to deliver bytes. Thanks to user swordragon. * Bug fix: Device file comparison parameters were recorded wrong with Linux sg 2018.09.16.134959 [04f9a30] Makefile.am configure.ac libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/cdrskin_timestamp.h Made number transition to 1.5.1 16 Sep 2018 [f525071] ChangeLog cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.5.1 - 2018.09.16.134959 2018.10.10.160024 [af65852] libburn/mmc.c Silenced gcc warnings about switch-cases and printf target buffers 2018.10.10.160301 [042fe17] libburn/async.c Bug fix: No lock was obtained for setting up a fifo object 2018.10.11.074048 [7282eaa] libburn/write.c Silenced gcc warnings about printf target buffer 2018.12.12.125445 [d72fb13] libburn/spc.c Emitting cleartext for errors with ASC 6F (CSS problems) 05 Apr 2019 [1c06669] acinclude.m4 Made libburn ready for building out-of-source. Thanks Ross Burton. 07 Apr 2019 [9ac8efa] configure.ac Disabled autotools macro AM_MAINTAINER_MODE on advise of Ross Burton. 2019.04.13.173355 [39f2712] libburn/libdax_msgs.h libburn/drive.c libburn/write.c libburn/mmc.c Bug fix: Stream recording was applied regardless whether the drive offers it 2019.04.13.173610 [4d5486a] libburn/drive.c Applying stream recording only to DVD-RAM, BD-R, BD-RE media 2019.04.17.085941 [610e213] libburn/spc.h libburn/spc.c libburn/sg-linux.c Putting out more info in case of failed Linux SG_IO ioctl 2019.04.17.092323 [c5bc9f6] libburn/libburn.h libburn/libburn.ver libburn/mmc.c libburn/drive.c New API calls burn_drive_get_feature_codes(), burn_drive_get_feature() 2019.04.17.095742 [0e73afd] cdrskin/cdrskin.c cdrskin/cdrskin.1 New cdrskin option --list_features 2019.07.05.133618 [6905bb4] libburn/transport.h libburn/libdax_msgs.h libburn/spc.h libburn/spc.c libburn/mmc.c libburn/write.c libburn/sg-linux.c libburn/sg-freebsd.c libburn/sg-freebsd-port.c libburn/sg-netbsd.c libburn/sg-solaris.c libburn/sg-libcdio.c Showing more info in case of drive errors and transport problems 2019.09.20.103726 [354d45e] libburn/util.c Bug fix: TDK Corporation was not recognized as manufacturer of DVD-R "TTH02". Thanks Steve. 2019.10.26.143001 [2298dfd] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.5.2 26 Oct 2019 [ce2ddab] - cdrskin/add_ts_changes_to_libburn_1_5_0 - cdrskin/add_ts_changes_to_libburn_1_5_1 + cdrskin/add_ts_changes_to_libburn_1_5_2 + cdrskin/add_ts_changes_to_libburn_1_5_3 Updated cdrskin tarball generator 26 Oct 2019 [a6fdbc7] ChangeLog cdrskin/changelog.txt Updated change log 26 Oct 2019 [3a94030] libburn/mmc.h cdrskin/changelog.txt Committed forgotten header file ----------------------------------- release - cdrskin-1.5.2 - 2019.10.26.143001 * Bug fix: No lock was obtained for setting up a fifo object * Bug fix: Stream recording was applied regardless whether the drive offers it * Bug fix: TDK Corporation was not recognized as manufacturer of DVD-R "TTH02" * Made libburn ready for building out-of-source. Thanks Ross Burton. * New API calls burn_drive_get_feature_codes(), burn_drive_get_feature() * New cdrskin option --list_features 2019.10.27.142135 [0a37e8c] Makefile.am configure.ac libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/cdrskin_timestamp.h Made number transition to 1.5.3 27 Oct 2019 [95ad059] ChangeLog cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.5.3 - 2019.10.27.142135 27 Oct 2019 [1c235e8] cdrskin/cdrskin.1 Corrected alphabetical sort rank of --list_features in man cdrskin 2019.10.28.155736 [ba9b69b] libburn/libdax_audioxtr.c libburn/async.c libburn/libdax_msgs.h libburn/file.c libburn/options.c libburn/spc.c libburn/structure.h libburn/sector.c libburn/libdax_audioxtr.h libburn/transport.h libburn/sg-dummy.c libburn/sg-linux.c libburn/sg-netbsd.c libburn/cdtext.c libburn/drive.c libburn/init.c libburn/write.c libburn/read.c libburn/libburn.h libburn/options.h libburn/mmc.c libburn/ddlpa.c libburn/sg-solaris.c libburn/ecma130ab.c libburn/sg-libcdio.c libburn/sg-freebsd-port.c libburn/init.h Fixed spelling errors found by fossies.org with codespell 2019.11.25.091234 [91f7d4d] [release-1.5.2.pl01 30c9f6d] configure.ac libburn/sg-linux.c cdrskin/cdrfifo.c Bug fix: cdrskin multi-track burning was slow and stalled after track 1. Regression introduced in version 1.5.0 by commit 84fad99, 2018.02.05 [] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log 25 Nov 2019 [branch-1.5.2.pl01 4c0ebf1] README cdrskin/README ChangeLog cdrskin/changelog.txt cdrskin/cdrskin_eng.html Mentioned bug fix and pl01 25 Nov 2019 [d4593f4] README Updated README about 1.5.2.pl01 ---------------------- bug fix release - cdrskin-1.5.2.pl01 - 2019.11.25.094931 ------------------------------------ cycle - cdrskin-1.5.3 - 2019.11.25.194241 * Bug fix: cdrskin multi-track burning was slow and stalled after track 1. Regression introduced in version 1.5.0 by commit 84fad99, 2018.02.05 O_DIRECT is now disabled for track sources. 26 Nov 2019 [b57c05c] configure.ac Incremented .so numbering to libburn.so.4.106.0 2020.04.15.185125 [7991750] libburn/sg-linux.c Bug fix: Early SCSI commands from sg-linux.c were not logged 11 Jul 2020 [e09acf3] cdrskin/cdrskin.1 Corrected a typo in man cdrskin found by lintian 2020.08.22.132046 [8d934ee] libburn/transport.h libburn/drive.c libburn/write.c libburn/sg-linux.c Testing use of experimental ioctl CDROM_REVALIDATE after media state changes 2020.08.22.132046 [8d934ee] libburn/transport.h libburn/drive.c libburn/write.c libburn/sg-linux.c Testing use of experimental ioctl CDROM_REVALIDATE after media state changes 2020.08.24.132739 [0e1f5dc] libburn/libburn.h libburn/libburn.ver libburn/transport.h libburn/drive.c libburn/mmc.c New API call burn_drive_set_speed_exact() 2020.08.26.140343 [8b9a8cf] libburn/libburn.h libburn/libburn.ver libburn/write.c New API call burn_nominal_slowdown() 2020.09.20.084444 [ab6b103] libburn/sg-linux.c Changed experimental ioctl from CDROM_REVALIDATE to CDROM_SIMUL_CHANGE 2020.09.20.204320 [9bc9900] libburn/sg-linux.c For now using CDROM_SIMUL_CHANGE only if -DLibburn_use_linux_ioctl_simul_changE 2020.09.30.185335 [3468a2a] libburn/spc.c Tolerating all sense replies of form 6,28,* 2021.01.30.103001 [2a58d5a] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.5.4 30 Jan 2021 [d95d5a2] - cdrskin/add_ts_changes_to_libburn_1_5_2 - cdrskin/add_ts_changes_to_libburn_1_5_3 + cdrskin/add_ts_changes_to_libburn_1_5_4 + cdrskin/add_ts_changes_to_libburn_1_5_5 Updated cdrskin tarball generator 30 Jan 2021 [d146886] configure.ac Corrected SONAME parameters to produce libburn.so.4.107.0 30 Jan 2021 [d3bd97c] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ----------------------------------- release - cdrskin-1.5.4 - 2021.01.30.103001 * Bug fix: Early SCSI commands from sg-linux.c were not logged * New API call burn_drive_set_speed_exact() * New API call burn_nominal_slowdown() 2021.01.30.153517 [f1d5c67] Makefile.am configure.ac libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/cdrskin_timestamp.h Made number transition to 1.5.5 2021.02.28.121708 [2a977cf] libburn/libdax_msgs.h libburn/structure.c libburn/write.c libburn/sector.c Improved error message if a track is larger than 4 TiB - 32 KiB 2021.02.28.121943 [31e8f5c] libburn/transport.h libburn/drive.h libburn/drive.c Unified size of large or unlimited pseudo drives to 4 TiB - 32 KiB 2021.03.12.082731 [892df64] acinclude.m4 Made optional the use of libcam and thus SCSI passthrough on GNU/FreeBSD systems 02 Sep 2021 [8d270b1] configure.ac Removed unneeded configure.ac macro AC_C_BIGENDIAN 02 Sep 2021 [c76145f] cdrskin/cdrskin.1 Corrected man page about the default of extract_basename= 02 Sep 2021 [0a1bbb1] Makefile.am Added doc/waveformat.txt to libburn tarball 2021.09.02.181259 [27e1568] libburn/mmc.c Let mmc_read_cd() for CD-DA set command.dxfer_len 2021.09.12.084139 [31591b8] libburn/libburn.h libburn/options.h libburn/options.c libburn/write.c libburn/libburn.ver New API call burn_write_opts_set_bdr_obs_exempt() 2021.09.12.084556 [da44c70] cdrskin/cdrskin.c cdrskin/cdrskin.1 New cdrskin option --bdr_obs_exempt 2021.11.24.114417 [7765fbd] libburn/libburn.h libburn/mmc.c Enabled overburning with burn_write_opts_set_force(,1) 24 Nov 2021 [d4d6321] cdrskin/cdrskin.1 Improved man page of cdrskin about -force after enabling overburning in libburn 25 Nov 2021 [879cb99] ChangeLog cdrskin/cdrskin_eng.html cdrskin/changelog.txt Updated change log ------------------------------------ cycle - cdrskin-1.5.5 - 2021.11.25.094022 * New API call burn_write_opts_set_bdr_obs_exempt() * New cdrskin option --bdr_obs_exempt * Enabled overburning with burn_write_opts_set_force(,1) * Bug fix: Overburning with cdrskin option -force ended by a libburn error 2022.04.05.135930 [629a4fa] cdrskin/cdrskin.c Let cdrskin take options -VV and -VVV as -V 2022.04.05.140409 [1954514] cdrskin/cdrskin.c Updated copyright dates of cdrskin 2022.12.10.093508 [a904ae9] cdrskin/cdrskin.c Enlarged maximum allowed tsize= value to 4 TiB - 2 KiB (just in case) 2023.06.07.143001 [1172ef7] Makefile.am configure.ac README libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/compile_cdrskin.sh cdrskin/cdrskin_timestamp.h cdrskin/cdrskin_eng.html Made number transition to 1.5.6 07 Jun 2023 [9acb8ad] - cdrskin/add_ts_changes_to_libburn_1_5_4 - cdrskin/add_ts_changes_to_libburn_1_5_5 + cdrskin/add_ts_changes_to_libburn_1_5_6 + cdrskin/add_ts_changes_to_libburn_1_5_7 Updated cdrskin tarball generator 07 Jun 2023 [f795db5] cdrskin/cdrskin_eng.html Last minute correction in cdrskin web page 07 Jun 2023 [4628231] ChangeLog cdrskin/changelog.txt Updated change log ----------------------------------- release - cdrskin-1.5.6 - 2023.06.07.143001 * New API call burn_write_opts_set_bdr_obs_exempt() * New cdrskin option --bdr_obs_exempt * Enabled overburning with burn_write_opts_set_force(,1) * Bug fix: Overburning with cdrskin option -force ended by a libburn error 2023.06.07.183206 [] Makefile.am configure.ac libburn/libburn.h cdrskin/cdrskin.c cdrskin/cdrskin.1 cdrskin/README cdrskin/cdrskin_timestamp.h Made number transition to 1.5.7 07 Jun 2023 [] ChangeLog cdrskin/changelog.txt Updated change log [] libburn/cleanup.c >>> SIGCONT in non_signal_list : local but not in git ------------------------------------ cycle - cdrskin-1.5.7 - ------------------------------------ cycle - cdrskin-1.5.7 - ((( [] libburn/spc.h libburn/spc.c libburn/sg-linux.c Making commands retryable after particular Linux kernel transport errors (currently as libburn/*.[ch].after_1_4_8) ))) ********************************************************************** Important: When adding a public API function then add its name to file libburn/libburn.ver ********************************************************************** =============================================================================== TODO =============================================================================== CD-TEXT: - with SAO - libburn: - optionally genererate TOC pack type 0x88 with A[123] and track POINTS - .inf files - enforcing constraints: - examine how drives react on wodim's cue sheet of INDEX 00 == INDEX 01 qemu: - Re-assess benefit of BLKFLSBUF as of dvd+rw-tools/transport.hxx - Centralize SCSI log /tmp file opening Get sg-freebsd-port.c functional. Expose BD type from bytes 8+4 to 10+4 out of READ DISC STRUCTURE form 0. Expose Disc Size/Class/Version from byte 11+4. Better motivation of burn_set_signal_handling() in libburn.h. Explore MMC command GET EVENT STATUS NOTIFICATION -------------------------------- Solaris ----------------------------------- Locking of device files. Unmounting. Could need ioctl to obtain SCSI bus,target,lun from fd rather than name ( Subject: Re: [osol-help] DHCP configuration with fixed addresses pntadm -A 10.0.0.24 -f MANUAL -i 010008544255E7 -m 10.0.0.0 -y 10.0.0.0 ) --------------------------------- bugs ------------------------------------- - handle HD DVD profiles 0x50 "HD DVD-ROM", 0x51 "HD DVD-R", 0x52 "HD DVD-RAM" as readable. - Do something about drive->buffer asynchronous race conditions and dangerous use of temporary dynamic memory. (The various asynchronous operations use the same buffer pointer in struct burn_drive and let it point to their private memory. Of course, any problem is due to faulty application ... but then it is really hard to detect.) - Why are DVD+R tracks labeled "invisible" by dvd+rw-mediainfo ? Why does the DVD drive only show the first session ? READ TRACK INFORMATION[#5]: Track State: invisible Track Start Address: 954960*2KB Free Blocks: 0*2KB Track Size: 22784*2KB ROM Compatibility LBA: 265696 ------------------------------ end of bugs --------------------------------- Support for BD-R SRM+POW ? Enable profile 0x42 BD-R random recording Provide DVD+R DL layer break setter Check all SORRY and FATAL errors whether they should become FAILUREs What about cdrskin rc files ? Forward with fallback runs ? [] Emulate -dummy on overwriteables ? [] Emulate -dummy on DVD+R ? What about minimum track sizes ? (POWER OFF/ON , BUS RESET ?) After cooking: review of -do_diet ? growisofs.c : _LINUX_CAPABILITY_VERSION CAP_SYS_RAWIO SYS_capset ? Rectify mmc_read_atip speed interpretation. 12x media are reported as 10x. I never heard of 6x media. ----------------------------------------- long term intentions: [] -reset: ioctl(fd,CDROMRESET) ioctl(fd,SG_SCSI_RESET,SG_SCSI_RESET_DEVICE) http://developer.osdl.org/dev/robustmutexes/src/fusyn.hg/Documentation/ioctl/cdrom.txt [] Convert burn_print() into libdax_msgs_submit() [] Clear outdated persistent read buffer after small CD image was read (ticket 57) =============================================================================== This is the dirty end of the todo list. The recent changelog entries are above the headline "TODO". =============================================================================== /* cleanup.c , Copyright 2006 Thomas Schmitt A signal handler which cleans up an application and exits. Provided under GPL license within GPL projects, BSD license elsewise. */ /* cc -g -o cleanup -DCleanup_standalonE cleanup.c */ #include #include #include #include #include #include #include typedef void (*sighandler_t)(int); #include "cleanup.h" #ifndef Cleanup_has_no_libburn_os_H #include "../libburn/os.h" /* see os.h for name of particular os-*.h where this is defined */ static int signal_list[]= { BURN_OS_SIGNAL_MACRO_LIST , -1}; static char *signal_name_list[]= { BURN_OS_SIGNAL_NAME_LIST , "@"}; static int signal_list_count= BURN_OS_SIGNAL_COUNT; static int non_signal_list[]= { BURN_OS_NON_SIGNAL_MACRO_LIST, -1}; static int non_signal_list_count= BURN_OS_NON_SIGNAL_COUNT; #else /* ! Cleanup_has_no_libburn_os_H */ /* Outdated. Linux only. For backward compatibility with pre-libburn-0.2.3 */ /* Signals to be caught */ static int signal_list[]= { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, SIGTTOU, SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP, SIGVTALRM, SIGXCPU, SIGXFSZ, -1 }; static char *signal_name_list[]= { "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGBUS", "SIGPOLL", "SIGPROF", "SIGSYS", "SIGTRAP", "SIGVTALRM", "SIGXCPU", "SIGXFSZ", "@" }; static int signal_list_count= 24; /* Signals not to be caught */ static int non_signal_list[]= { SIGKILL, SIGCHLD, SIGSTOP, SIGURG, SIGWINCH, -1 }; static int non_signal_list_count= 5; #endif /* Cleanup_has_no_libburn_os_H */ /* run time dynamic part */ static char cleanup_msg[4096]= {""}; static int cleanup_exiting= 0; static int cleanup_has_reported= -1234567890; static void *cleanup_app_handle= NULL; static Cleanup_app_handler_T cleanup_app_handler= NULL; static int cleanup_perform_app_handler_first= 0; static int Cleanup_handler_exit(int exit_value, int signum, int flag) { int ret; if(cleanup_msg[0]!=0 && cleanup_has_reported!=signum) { fprintf(stderr,"\n%s\n",cleanup_msg); cleanup_has_reported= signum; } if(cleanup_perform_app_handler_first) if(cleanup_app_handler!=NULL) { ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0); if(ret==2 || ret==-2) return(2); } if(cleanup_exiting) { fprintf(stderr,"cleanup: ABORT : repeat by pid=%d, signum=%d\n", getpid(),signum); return(0); } cleanup_exiting= 1; alarm(0); if(!cleanup_perform_app_handler_first) if(cleanup_app_handler!=NULL) { ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0); if(ret==2 || ret==-2) return(2); } exit(exit_value); } static void Cleanup_handler_generic(int signum) { int i; sprintf(cleanup_msg,"UNIX-SIGNAL caught: %d errno= %d",signum,errno); for(i= 0; imax_sig) max_sig= signal_list[i]; if(signal_list[i]=non_signal_list_count) { if(i==SIGABRT && (flag&8)) signal(i,Cleanup_handler_generic); else signal(i,sig_handler); } } return(1); } #ifdef Cleanup_standalonE struct Demo_apP { char *msg; }; int Demo_app_handler(struct Demo_apP *demoapp, int signum, int flag) { printf("Handling exit of demo application on signal %d. msg=\"%s\"\n", signum,demoapp->msg); return(1); } main() { struct Demo_apP demoapp; demoapp.msg= "Good Bye"; Cleanup_set_handlers(&demoapp,(Cleanup_app_handler_T) Demo_app_handler,0); if(1) { /* change to 0 in order to wait for external signals */ char *cpt= NULL, c= ' '; printf("Intentionally provoking SIGSEGV ...\n"); c= *cpt; printf("Strange: The system ignored a SIGSEGV: c= %u\n", (unsigned int) c); } else { printf("killme: %d\n",getpid()); sleep(3600); } Cleanup_set_handlers(NULL,NULL,1); exit(0); } #endif /* Cleanup_standalonE */ /* cleanup.c , Copyright 2006 Thomas Schmitt A signal handler which cleans up an application and exits. Provided under GPL license within GPL projects, BSD license elsewise. */ #ifndef Cleanup_includeD #define Cleanup_includeD 1 /** Layout of an application provided cleanup function using an application provided handle as first argument and the signal number as second argument. The third argument is a flag bit field with no defined bits yet. If the handler returns 2 or -2 then it has delegated exit() to some other instance and the Cleanup handler shall return rather than exit. */ typedef int (*Cleanup_app_handler_T)(void *, int, int); /** Establish exiting signal handlers on (hopefully) all signals that are not ignored by default or non-catchable. @param handle Opaque object which knows how to cleanup application @param handler Function which uses handle to perform application cleanup @param flag Control Bitfield bit0= reset to default signal handling */ int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler, int flag); #endif /* ! Cleanup_includeD */ #!/bin/sh # compile_cdrskin.sh # Copyright 2005 - 2023 Thomas Schmitt, scdbackup@gmx.net, GPL v2 or later # to be executed within ./libburn-* or./cdrskin-* debug_opts="-O2" def_opts= largefile_opts="-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1" fifo_opts="" libvers="-DCdrskin_libburn_1_5_6" # To be used if Makefile.am uses libburn_libburn_la_CFLAGS # burn="libburn/libburn_libburn_la-" burn="libburn/" cleanup_src_or_obj="$burn"cleanup.o libdax_msgs_o="$burn"libdax_msgs.o libdax_audioxtr_o="$burn"libdax_audioxtr.o do_strip=0 static_opts= warn_opts="-Wall -Wextra -Wno-unused-parameter" libcdio= fifo_source="cdrskin/cdrfifo.c" compile_cdrskin=1 compile_cdrfifo=0 compile_dewav=0 libcam= os=$(uname -s) case $os in *FreeBSD) libcam="-lcam" esac for i in "$@" do if test "$i" = "-compile_cdrfifo" then compile_cdrfifo=1 elif test "$i" = "-compile_dewav" then compile_dewav=1 elif test "$i" = "-libburn_1_5_6" then libvers="-DCdrskin_libburn_1_5_6" libdax_audioxtr_o="$burn"libdax_audioxtr.o libdax_msgs_o="$burn"libdax_msgs.o cleanup_src_or_obj="$burn"cleanup.o elif test "$i" = "-libburn_svn" then libvers="-DCdrskin_libburn_1_5_7" libdax_audioxtr_o="$burn"libdax_audioxtr.o libdax_msgs_o="$burn"libdax_msgs.o cleanup_src_or_obj="$burn"cleanup.o elif test "$i" = "-newapi" -o "$i" = "-experimental" then def_opts="$def_opts -DCdrskin_new_api_tesT" elif test "$i" = "-no_largefile" then largefile_opts= elif test "$i" = "-dvd_obs_64k" then def_opts="$def_opts -DCdrskin_dvd_obs_default_64K" elif test "$i" = "-do_not_compile_cdrskin" then compile_cdrskin=0 elif test "$i" = "-do_diet" then fifo_source= def_opts="$def_opts -DCdrskin_extra_leaN" warn_opts= elif test "$i" = "-do_strip" then do_strip=1 elif test "$i" = "-use_libburn_fifo" then fifo_opts="-DCdrskin_use_libburn_fifO" elif test "$i" = "-use_no_libburn_fifo" then fifo_opts="" elif test "$i" = "-use_no_cdrfifo" then fifo_source= fifo_opts="-DCdrskin_use_libburn_fifO -DCdrskin_no_cdrfifO" elif test "$i" = "-use_libburn_cleanup" then fifo_source= fifo_opts="-DCdrskin_use_libburn_cleanuP -DCdrskin_use_libburn_fifO -DCdrskin_no_cdrfifO" elif test "$i" = "-use_libcdio" then libcdio="-lcdio" elif test "$i" = "-g" then debug_opts="-g" elif test "$i" = "-help" -o "$i" = "--help" -o "$i" = "-h" then echo "cdrskin/compile_cdrskin.sh : to be executed within top level directory" echo "Options:" echo " -compile_cdrfifo compile program cdrskin/cdrfifo." echo " -compile_dewav compile program test/dewav without libburn." echo " -libburn_1_5_6 set macro to match libburn-1.5.6" echo " -libburn_svn set macro to match current libburn git." echo " -dvd_obs_64k 64 KB default size for DVD/BD writing." echo " -use_libcdio link with -lcdio because libburn uses it." echo " -do_not_compile_cdrskin omit compilation of cdrskin/cdrskin." echo " -use_no_libburn_fifo use cdrfifo even for single track non-CD" echo " -use_no_cdrfifo always use fifo of libburn and never cdrfifo" echo " -experimental use newly introduced libburn features." echo " -do_diet produce capability reduced lean version." echo " -do_strip apply program strip to compiled programs." echo " -g produce debuggable programm." echo " -static compile with cc option -static." exit 0 elif test "$i" = "-static" then static_opts="-static" fi done timestamp="$(date -u '+%Y.%m.%d.%H%M%S')" echo "Version timestamp : $(sed -e 's/#define Cdrskin_timestamP "//' -e 's/"$//' cdrskin/cdrskin_timestamp.h)" echo "Build timestamp : $timestamp" if test "$compile_cdrskin" then echo "compiling program cdrskin/cdrskin.c $fifo_source $static_opts $debug_opts $libvers $fifo_opts $def_opts $cleanup_src_or_obj $libcdio $libcam" cc -I. \ $warn_opts \ $static_opts \ $debug_opts \ $libvers \ $largefile_opts \ $fifo_opts \ $def_opts \ \ -DCdrskin_build_timestamP='"'"$timestamp"'"' \ \ -o cdrskin/cdrskin \ \ cdrskin/cdrskin.c \ $fifo_source \ \ $cleanup_src_or_obj \ \ "$burn"async.o \ "$burn"cdtext.o \ "$burn"debug.o \ "$burn"drive.o \ "$burn"file.o \ "$burn"init.o \ "$burn"options.o \ "$burn"source.o \ "$burn"structure.o \ \ "$burn"sg.o \ "$burn"write.o \ "$burn"read.o \ $libdax_audioxtr_o \ $libdax_msgs_o \ \ "$burn"mmc.o \ "$burn"sbc.o \ "$burn"spc.o \ "$burn"util.o \ \ "$burn"sector.o \ "$burn"toc.o \ \ "$burn"crc.o \ "$burn"ecma130ab.o \ \ $libcdio \ $libcam \ -lpthread ret=$? if test "$ret" = 0 then dummy=dummy else echo >&2 echo "+++ FATAL : Compilation of cdrskin failed" >&2 echo >&2 exit 1 fi fi if test "$compile_cdrfifo" = 1 then echo "compiling program cdrskin/cdrfifo.c $static_opts $debug_opts" cc $static_opts $debug_opts \ -DCdrfifo_standalonE \ -o cdrskin/cdrfifo \ cdrskin/cdrfifo.c ret=$? if test "$ret" = 0 then dummy=dummy else echo >&2 echo "+++ FATAL : Compilation of cdrfifo failed" >&2 echo >&2 exit 2 fi fi if test "$compile_dewav" = 1 then echo "compiling program test/dewav.c -DDewav_without_libburN $static_opts $debug_opts" cc $static_opts $debug_opts \ -DDewav_without_libburN \ -o test/dewav \ test/dewav.c \ "$burn"libdax_audioxtr.o \ "$burn"libdax_msgs.o \ \ -lpthread ret=$? if test "$ret" = 0 then dummy=dummy else echo >&2 echo "+++ FATAL : Compilation of test/dewav failed" >&2 echo >&2 exit 2 fi fi if test "$do_strip" = 1 then echo "stripping result cdrskin/cdrskin" strip cdrskin/cdrskin if test "$compile_cdrfifo" = 1 then echo "stripping result cdrskin/cdrfifo" strip cdrskin/cdrfifo fi fi echo 'done.' #!/bin/sh # # convert_man_to_html.sh - ts A61214 , B50802 # # Generates a HTML version of man page cdrskin.1 # # To be executed within the libburn toplevel directory (like ./libburn-0.2.7) # # set -x man_dir=$(pwd)"/cdrskin" export MANPATH="$man_dir" manpage="cdrskin" raw_html=$(pwd)/"cdrskin/raw_man_1_cdrskin.html" htmlpage=$(pwd)/"cdrskin/man_1_cdrskin.html" if test -r "$man_dir"/"$manpage".1 then dummy=dummy else echo "Cannot find readable man page source $1" >&2 exit 1 fi if test -e "$man_dir"/man1 then dummy=dummy else ln -s . "$man_dir"/man1 fi if test "$1" = "-work_as_filter" then # set -x sed \ -e 's///' \ -e 's///' \ -e 's/CDRSKIN<\/title>/<title>man 1 cdrskin<\/title>/' \ -e 's/<h1 align=center>CDRSKIN<\/h1>/<h1 align=center>man 1 cdrskin<\/h1>/' \ -e 's/<body>/<body BGCOLOR="#F5DEB3" TEXT=#000000 LINK=#0000A0 VLINK=#800000>/' \ -e 's/<b>Overview of features:<\/b>/<b>Overview of features:<\/b><BR>/' \ -e 's/<b>General information paragraphs:<\/b>/<b>General information paragraphs:<\/b><BR>/' \ -e 's/<b>Track recording model:<\/b>/\ <BR><b>Track recording model:<\/b><BR>/' \ -e 's/^In general there are two types of tracks: data and audio./\ <BR>In general there are two types of tracks: data and audio./' \ -e 's/^While audio tracks just contain a given/\ <BR>While audio tracks just contain a given/' \ -e 's/<b>Write mode selection:<\/b>/<b>Write mode selection:<\/b><BR>/' \ -e 's/<b>Recordable CD Media:<\/b>/<b>Recordable CD Media:<\/b><BR>/' \ -e 's/<b>Overwriteable DVD or BD Media:<\/b>/<b>Overwriteable DVD or BD Media:<\/b><BR>/' \ -e 's/<b>Sequentially Recordable DVD or BD Media:<\/b>/<b>Sequentially Recordable DVD or BD Media:<\/b><BR>/' \ -e 's/^The write modes for DVD+R/\ <BR>The write modes for DVD+R/' \ -e 's/<b>Drive preparation and addressing:<\/b>/<b>Drive preparation and addressing:<\/b><BR>/' \ -e 's/^If you only got one CD capable drive/\ <BR>If you only got one CD capable drive/' \ -e 's/<b>Emulated drives:<\/b>/<b>Emulated drives:<\/b><BR>/' \ -e 's/for normal use: <b><br>/for normal use: <b><br><BR>/' \ -e 's/original cdrecord by Joerg Schilling:<\/p>/original cdrecord by Joerg Schilling:<\/p><BR>/' \ -e 's/<\/body>/<BR><HR><FONT SIZE=-1><CENTER>(HTML generated from '"$manpage"'.1 on '"$(date)"' by '$(basename "$0")' )<\/CENTER><\/FONT><\/body>/' \ -e 's/See section FILES/See section <A HREF="#FILES">FILES<\/A>/' \ -e 's/See section EXAMPLES/See section <A HREF="#EXAMPLES">EXAMPLES<\/A>/' \ -e 's/−/-/g' \ <"$2" >"$htmlpage" set +x chmod u+rw,go+r,go-w "$htmlpage" echo "Emerged file:" ls -lL "$htmlpage" else # export BROWSER='cp "%s" '"$raw_html" export BROWSER=$(pwd)/'cdrskin/unite_html_b_line "%s" '"$raw_html" man -H "$manpage" # cp "$raw_html" /tmp/x.html "$0" -work_as_filter "$raw_html" rm "$raw_html" rm "$man_dir"/man1 fi ‰PNG  ��� IHDR���–���È���=[èW���PLTE"?&!90+&$2-*62.:63,16<Y0;D>a;G)Mu3EP.Ro7ivVnB=:P-(r4/c#M=rEB=SQ.Zr1oV2qo6__LHGRNLTQNYVTIPUK[fJToUmNTgoa^\iOGda_spMjgfqnluslywvjswTZ¢k’u¨*P’0s0n®1\¥({ÄRU•Ro“Cj©l_“mph¢5”x0¦nm.v¡!O³wp†Tv‰upºpd˜^qÆzˆ¯6Œ4§¬•ѨҦæ(ŠÌ(¥ÙH‡ˆu‡‘u“ªZ¯žu±ÍW¨Ðoņ“2,®)±.(˜.ŽO-uŒq.¶CµQ!¦q*ŒTNŒkU~}‘jq­gr¨tUÈ, ÞzjÒmjãuiáulÞi_Ê@ˆd‡º|ˆÜ‚’Œ‹,…¯'®’.±¬­±. £ŽŒR„ƒ{ŽmŽ´S¯h«K£›o°¨p¯®P±Ð£Í&”ÈM±ÏHŸ×UбÒ®1ê² ë·è­ë»&ò©<ò·3Æ–"܈zבlʯMîšHðNíšgç‰qï¡Sð¥Zñ¬Lñªeñ³vÓ›VÏÎ ÌÓ+ùÙíÓíÂ*îÈ8öÇ'úèþõ�ëçÓè ÔÓIÍÇnîÊDðÎGñÒIòØWïÍRôÔnõêyòãjŠˆ‡‘ŽŒ““™—–‡•‹›¨œ¥—–«¹—ªµ¡žœ¬–¬™¼µ”­¤¢›±¹Œ©§¦¨ª¶±¯®²²¯º¹¸¬±¶•³Å©Á¬¨Ç§¹Å²«Ãµ¸Å½Æ»³×¤™Ê”®ÁÍ»ÇÆ¼ÒظÌѼÙçºÞó¤ÎáØˆ†×˜‰É—Ѩ—ɸ¨À¿½Ñ­¬ï¹ˆÛ¡žØ¼ÅáºÈÎÏ–ÂÄºÎÆ±öÐ÷Ñ­÷ê†÷ñ‡øì—úð­ÙëµÇÇÆÉÓ×ÑÈÉÒÔÎØ××ËÏÎÉÝçÜäÙÐèöêÙÏúäËûé×õòÑèççéôüúôêþþþôïé´\±^���tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ�S÷%���bKGDÿ¥òÅ��� pHYs���H���H�FÉk>��$ðIDATxÚí |UöǧÙþóXYh5à†×$SÐ&M«‚,H—‡vUD\˺«¨ì •„Í Ra¡¥T´<Ë"6”ŠÐÒvÒ)”8$S $HAÿÔ¶ KiiÙФD[ÒLò?÷NúÀµ´…õóÿxÑf23™ûÍ9¿{™¹„/芕Þ[ïUŠÍþÅ–hBjþÈí…ú¼šq?¶…P¼°è”„îÎb¹eaü± F‚¹Z ¢ña¹¤Aw‚%u ‹±ˆÐÞY¬N­EƒµüX¼öŽk auf­V,Ÿ›µwÇX`ñNŽs •xíœÓo-ÞÑÀ·8ì¼ÒÑâks¢Çîæ}žFô ÞáàáÓhEc¹¥"‘D‡ìШ‹äFA[UR)G‹Å oWŠ ‰¦9Q$súx½ˆtù˜P9|€“J8#|ZÎËuK,¯†À죑HX:B$…öFr@‰^Ý~m™ÅaoQ"ÖçK AûÀVI°B»%- €<j¼�µˆhOáBJ Ô+`¹à½¸AÐO…ˆð§!ˆi®[a¹dþjenNŒ,…Èp•x­˜‚À)Á*#8Ñ¥ƒÅH[*ìƒÈ%h«ÜMÀX |W’e)‰Ž×À"Åê½h_ 6Ì„X÷;õi¶ ÒFŸ€åQ‡ˆD:g•°G±€…[¡y9[Üpt¹ËÇGc,^¨ÒÇ!Cñ|Ä|(HI|D°±;ZêÄ:Uõ(–¤Byð¢C,Ä+¼hô!¡!Ï8À‰¡šF$£àm$#¿¶Ô!!*ø°EÔãXí= àà.Ú),‚{=/…$-1zq€€â >l­@m©a«ˆ­¾]XUpp„Õj Wé³Ipc£yŸ1sEz;X £CÇýã9GÏX‹Y…UB€ "o' ƒ„$às°Z„€^ÝóNk„˜Ó Œk Âi Õ–º¸id0îY1ެÎVÉû­%êa,td 4&žç‘T^¤l‘?&¡¶À;œ<AŠêG©Å[ÝRQ«µ<· yÃÉP,Š¡´“•¸%RÎEí2‰Î…Û+3»u5àè·V´R¥µg±ì¡À%ZH¯i[‚{ŸvÉ£¨ CP¢ØðÐW:—Lˆm„ˆjTÙÃÖòh„~ úD°‰¿KĺaKÑÛˆ¹Xò4#sÉím’÷[«§±|N²-ƒàp,6Ê ð E1ÔKÆx|,tS^˜‹;•!"sû>Bâèa,H²D¨K†ˆÎ³(¤'é )Tb—àl…çUãF=B(j©N1&T£ »T‚¬äÑŠ4=œA@ñšišÃ#CÞQHÛ<-,çÏ<ñ:lFI*d±8Qæ]¼¾»³Ã>^{ Drù;R~Æúëg¬ŸBùëg,TœÞî£ç±œEÖn3ê1,È ;¼c§îsþ4°š:`ñEûНý$°šÏ{:`íÛWÚóâê VíæŽXEûŽþ·°<8|üÖõÍ6(mù/a5oí’‘^Û²è@ñËZüÖóí*oÞ”^ÓaÓ¾Åÿ‘óÝ j«fS»¹šÒÓ·¶;•ßW| å1Ãç©í&W€XµüÖ´>}s;¤»¨ø€ûƘX[=·›XMé[Ûê­MOï�éN(mùcïöòdÔvÜ.c ÞiZ#Vû;oÂQÐVSÆòÔëù­Ý3×-±šk=ØZíšm¥¯oj}÷}8‘?/„2ŸæÆr[¬U‹tÒщMkצ¯÷›ÅÇŸGXÍ›ž¦Mµ~¬Žõv`ùš3 YÕÞ€•šž¾Áï$χ ¥ÌùÍ邵À×?–°?ßܵ&yk,pP“gkG¬-ÛÁ~§Öþ3¡´(}mÖzš7§ æãk»æÌ�ZbmzÆVÀh{ß´%+m}ú&\¿'ciBé¾Õ;ü!d·iZ…?ÐÕžï’¹ÀBO´‚›¥gKÖötÄÕÜÜ´õŸÑSOí{Ì×Ôºëú­µ5›Òý+@m]2WÀXXËÍ¨ŽæÍÛw¦¡U›6­ÿ§&®øLqÜê-‚ÓšÖ§á-éàVAüÍ›ºd®à°šÂšÒ·ïÜ.Tž¾tHÂÑSG­…xä¼4a Ð XžÍ]2W XëÛ°jÓÏ77o¬;Ö¢•ë—›ZZZšðÞäææ¦Œô´h1-«+½+?8m!)$`íÜÞZ›¶Z³¯¸¸4aQ¢Dø;vfmOÛ‘µs­_[nlí‚ÆBák°ÖfíD%kûöÛW\z*nÑÎíkç ›vno“|zÇ~½'±j×¢*‘¤={¶§mŒå/Û�«äTÜJ€Ü±víÚ~*ÀÚ,䆵mm²‡±øó;PëZ_ YÔd£¬±¦Ÿ:•ðvÇu¨ìH:Eþü éFby¶nÇ­kóùóíÔn­âSqïüpýöÖˆ’>ïBˆ�kSV–?¥µU›••ýɶm«Þ2ikñÂUÙŸdgÝ€•Ý¡ç<Š]Ðü­±jAÆÛÛ¥ž•½mÕÂù/½4pú˧¿¹[+áÑ7§OŸ>ð¥—^Z¸p›@_dCM-²UVÚæàs¯['6çw ˜ii™YYÛΟ>hÆÌY³æÌ{-cKnNÎ{ -Û©„%»rsönzmάYŠš>ᶬ¬µë C°é6`5oòûE-)ü¢¢¼üìÜ9¯mÎÉݵråÛCµâW®Ü•³wü¹åœm?5á~ª;Ößo…¡ 1e/\µd¿Ív©ò`­›;w»V®Xñ;Ð`=¶bpmž;÷lE¥­ººÌVz@¯þòêÌ,Ü*ƒoŠ·ü… úåìUó¥Ùj[µ ¸ÎU|[~öìܹ·| XµŽžIˆaeÌ]^qÉf++³ž8~üÐßÿ î̤ßo…U»6{áôøý —ÊÊŠŠò³ëæ¾¶iï®o/ÁQ>!áÀÚ»Ëf³Z꛳y—kÞ¾0;kmðã[`y¶¾<(ÉbëXª+Á^ë€+c!mÅ¿³|øÚÙo+«me�uì›r€ºråJ}ÞÒ—æ§/®›cñ•3l§Ï”ý€«²ùqÃî]+ÞñK°¶lœ]^Y]}¡ôø‰C`©z`ºœŸ·eóæ¿¾ørÐã›byëŽ~åÚW§Ëª;p]ª}Í»y×JÐÖÑ£gŠV¬ÌÙ€¨@UÇÝŠ¡òóöflÞ°qãë¯Ï˜éêA,MŸþú»–¾å»ï¾þêô™Y~vöÆ-+S#kM{WƇ啶²Ç•oͯCååedlظnî믿þÍ7úÄžûõÕ´ßf?ýÕ5Æ·´ ²vo^ª,/Ÿ»a—·ŠbVî[^ ª¨ËõÈP›36¾~!:tðà±¢± =„å¦÷£úËΜþ ™¬‘]ë`²Êгë6¿7 Æú§Št[6”Wž:q¨â|Í•úl§¹g_ÿ;B:vüرãÇÐe¯N±¼•M@ûê«ï¾k%ë`°ï¡–Xº/áà ›õ`Åùúúzpèéì7ÈLÇ0ÑñãV«µìˆ.˜†;Å2ÇßÐüÀ§‘;¡\»æ÷%k2°t…‡ ¨ËˆéìYÁJÇŸ°¢bûWï/ "¨v†å޾‘ ºˆÞ§¯ h`²’Ãûm—°ŽžÚ—Ðh7³ó¶f¬kEÂDÀSÖˆ£ƒ¸n±3,sÑ ¦ª¶Wî¬ £ÙÌn¯ÓÁáp X^¯Óò9 ! ÎZ† dG_¦ÃAö»Å'~q£©þÕð/T.\²ÛËlîo£ÃL ‰¬qe ðÞÎZ­vÁcmìpKtàêê ÷8ÕÕmÇ=iöºÔp©á»ÌsÕ©Ô s�aÙ]ޝ×n»iÑT;Árkðw=‰åÚXVv¦ñÌé3gNãrõÚÕkno ¸Ð¥=Â&@_]D5:^o‹£„ã8‡?T]ýCªêø†nciQ\(C_¡òõµk¨òÇ/(`-¯³ñ‚1ñ¾˜˜"#ç°»[Z\'Bä<ˆË‘'J@dU\,]cw±<E("�Î×_ 0ˆÇz(W¯ž);Õ�s;­¬¥„k¼êD”vp´ÕZrâà Lwìà‰c' X­›½ûNä–3~(lŒs ºì3vheVˆç5µßƒÉÜnüûÎÝx®üÛ6»pÕV޳r©Äz ìÖøîK^[è8±¾þîÚ×__ûê«Óß  ÄYOü¦¼¦&ïÛC'ìç¾ýöÈÿžt]ÿòcƾ2gNyEe«Ó:*Kh v‹4ðË);ÁrHHÇAVÈŽ¿nDÈãG•Ÿ¯©9_~脵ªrݼWÆNÿåõ“Œ§Ÿ5g¶žþhqDq{F'XœH¼ßeû´ç §¬à”Cß”o­©Ùúá7[«*ÖÍž¥óÌøÇÿ}åËqãÆŒ `s?Dé|õP±ŠPiÀ^ì‹!d¤ÅáïtªË.Xñ á›³j�j‚²ÙÎ7ïïcÇŒ÷ÈÉëÿþÓøq#Ç ®9¯­+ÿ“9ì”RÜXRɹP?ž+…ìüС³ÀPy ×;^ TåëæÍÑ3fÜø/ÿýï“fÌÃcǾžÜøŸ`ŽjµR%ê6–]"ŽP)Y—Ú$L]·á¼êàñàÚÊò×çÎû»~ô˜gÆÌõåøñ“ÆŒ‹6oöºòo+*/µG,—")9ºP¶{XnS¤t¸JA_½züøApÞÆŒ¼¼Ë[7¬ûæÐ1”=UCX>gμY‚µùòßWNþnü¸gÆ€À^™5kÞìg?¬¨l19\‡•*Ї»+ùEýÜ´iÏÏàœç^}cÆ^Èî6Ψcà@°ÕIpáºÙóf=™ëO‚¹×X$°y0ìG"OžltÙg¾ðü´i÷ŠÈÀ3›Î¬%SÙf>ÿ—¿LoÿþÛ £LêJ[`„†Õ,=š„ÌuýËGüæÒ#®¹ë>¬@#ìF—£pÀô—ÿò·Ää/»·Ü$YÂL˜øþòå –œk:_ƒ!»Ãn‘5 `gÏÂNDæ.ý¸gž3sÍ›·n]EeµÃÕX¸`á§ËߟØ[BJƒ¸:½³(¯“H(–{g[ÖÎgþ¾á‚” ûXkÝÆys� Ì%È Yk’ß‹«âÒu÷¤|º3{~o™’Ä7@tË×ÈÊåõ‡.}ñ‡ÉS¦<44"Áât]ml®kãFðâ+À… 6iüøG )b'"q•ŸûÞe/òëQðéÉãË•ÃûI‚¹«¦Ó\Þdîûü«Ï½Â–=ûû‡FÜì=*=äÉ.§åζ“­æBözlÒ¸qwÃËèßê??Wûý÷ZÖ'öþQ#| ŒTœöj?Äb‘´yÆŸ_4áQ“­têC±}”JI%±ÐÜ¡8ÁfÏž3ëïzýر£GÿvÌÔ©Sot:íl‘FI’J*¼OlŸ»~IR&,øó½bRÖC ·,TgŸ¸|ù' ,a­e¦xµJ¥„ÚH¥:š6sÕ‡ë{Tþuî\ƒóêÕ«öF{Y ËèÔJ…vR©H™D LQomÛ¶|bo©R* âšùΰ¼”H¦9bû,ûÓ<þäƒ#®Êb¢µ¥4ª™¤"5jM|"M¤&’‚õr´>B¥Rk´F3­P :ò‰'ÿññ'ó{+¨ ß)oÑIT*W=õ÷S¦<ýð¯ûÜ34ÆÈrUœ™¡µÚHŠ¢°éŠ¿*•°N­Õê3Ø’3%hÂï1ê7“§L~ ·\©RÊM'ß:U»¸¤¾Ï½ðÜ+–²â§GÅ‚æÃ#”ʵ6‰a9è|8ŽeÙÃ&}R"M'ÍðVWUÙa5­ÓP*Rº?6öž^äð~Ó¦õÉ‚&vŽÅsÕúþö—D3VëԇáW‚¼” J£Ñ&ÐF£‘5³ÉHi:Z«Q«"H,,ØE¥ŠøÕ]½Â”÷Ý7ý/{þ2i0×Ìw~ÆÆE±¦¨‰ï¿¿|á[K˜’*6Iƒj‹è0)¸‘^10åß /2‰T®ºoúÂåËßcB¨BÔ­Ãc5J¤4÷îÇÙ úGžG³œ éJ£BæBõ#‚¡ mÐô kãÔ*õ˜ÿ°âÓOwn,UªdAÝhÝ9–‰ÔI4lõgx 4ÿ›Ñ#ú„Ó²6;WšãtàGŠèe¤Z£Ñét [ÂUU™™Í{bG?üô”)S& è©)–Xz‹³Ñ}Ÿ›ÉZ‹q˜¿?vDò¥ÑÑÌa®ÄÎÙí%f®„³X8®¤äg‡A˜™1&jÕ�¡ÄŠ1êÁ{z)”"õ%´Á\|vk2.ñ…iƒ-ÉOáw P/¸L£‰‰ÖE£¢ÕP3°Ø°‡q ïÓçW½ÂT÷ xéϯÞû 2¨:Dz«$*Ö4hâû y“µŠÑ¡Pu£˜TXgHìTÇM°š"¥i˜jø‚…ÛPZ#%© îéËËÑ}«.~¼ýÓ0ª)2sUl¡Nà”?P{GP°•Zg4qzåpõÝãW~š½°·œ”FõKÆMÎ4;,úžŸÁÚ¿xò©)“Gœ«2³´Q^<ŠB†5(J«¡QŒ·r¦¤˜¡}bG< Aþ©Iýýå¹,¨Kyo‚ÅÓ&Ó  B”ý£ç‘âÕÑz *ç`XÏâõ0[ÍÙ9ÎjaZ‰ú¥ˆ¡± Ídž…Qý¥訃ºµúf?%4·lyöªÅE%¥G̓~mH¿à5ÑIzZOÓIIqZ$z¥ gJÁTª°»îº«—2jÁüÞ½Uª`2æ[`YÄbùÒg?ùÄÈÚl1#eµÊJ…=ˆB† Çw¥UaF(‰L®r÷ÈÇ÷…mÊÐ`ò‡›c9S2¥–=õìï'ˆís·Ž±T•°…Ñ +!Ϋ¼¬˜½9G+‡„‡?øÀ=2Åðá*JLþps,ž3“0À›ù¹í(ä§XYÚýŠ•e}|<tÍu$Äwˆð”&^¯3²&ò[˜ú ƒÔ´WØ}ç½úê´çBƒ [7ÿ…Ì¥=2óÅ—'LˆJÚoµ0Ñ¥V!ÐÓ¬Ål¶Vqöê2»ÕÎAFc±@Œ§!SÀT ™LF’Q &N\°àå~! ò–»›byeJËþ‰Ë?ÍÞ¶èѸ"Kîªq~Ú–øù…Fµ­…¤Gg<ÌE“ª¡#û4kgö§ËKIiO¹)¯—É÷_úìãüéÉ)“Ç Sd²¢ü£h´MUk…ÌzKÖl…ž1)&<|ôCO?õÔ“ZùÖàÞ ƒ¢ºÅ¯¯v–ì7ƒážýý”ߌ!d¨¸«†T´ÊŠƒW{±£0V¤×@æCFàÔtĨɓ¸KA„ÁXCOby9óŒiÏE"Í#(° ÎÿHœ¡jÑ?]Œ ²Ð‰+ý=õ±±}~¦4íÕ ¥u«ŸÐŒyæÄ‰o,XÆZiUmÙAkOP”?Ì ñ…ô—a UÔo¼1øäR}pT·¼Ý›3r_ìÊÎ~ÿ±‘ DTF‡’¿ˆŽxÿÑYƒõûi…‚šÏÞ6±wi0#×@°|nËáDËEÈP'?üëð!1E *èøtÑZJ!(U›æ#¡¿Ô1G,ôæÃ@óOžòÄ}ýÃúõ•Ð=þ&މLâNýñ!ÜY£LJ›È06ÍV@/¶Änƒw…h8æ×üˆØ{Â" è+ .× Ë$Ör… ³¥ÅS‡Rxhƒsb¥Z­Ö$êtq:TÐ_ ¥VáþQè¨eÐO“QoÍï/Zð`¹ÙpæÒáI#kâ¸ýz-¥”…ü†!ð­£1¬vŠÒé Æ+a¨?n°œ”ˆÙ`©¸~‹eú=?£ðÔÿ>Þgh.c uÚ¶TT¥Tù«¸@P3BP+1R}bcƒáŽ¡Ú }ÀÕn´Úü×é&,³XŽ¡HIËıV‹‰5&$ÑBÑÓ,»ß\Å•@¿¨Ó@¯¨èuWX/2j„~}A>9#0, Iùâãìmo<3•­„Ì!çz`}¼.шÒÓÃÐ4uI ºH5%lÓ$jeŸ>¶({ù`Yér XN‹®ß²jˆO=4:|XJ’'ó*ÿX_!¼ FÓŒÉ $&|è¨ÉS&MèOÊÅ1]¸-ãÖX.ûÊ Ó‹J ¡¿ÿþ>áx�Ë0f`@h6[>§L–‹ÙÄÙÁr…8—èƒOÖ„õØïTWn à‰RrSøÆ[‹Þ3•MÀ™©7AŠRkü±A‘B«ÆãC4°…?Š^ ¬óßÐW|tËêzÛg?þÄÈ8æ°e� àÕÚqð¿A1B mB-'‡Œœô»…ƒärq)M X>'«žöbÒ™gÿH †@ŽguêÑ`õ@‘¨ÛA£Äh­ÎÈ”°%,“8¤ÏˆQO<x|`¿Ðà#|€X|!;kúÄÅË,¥…N BÜD�û¹Ã,g³–•1L5: h†¾P­† ¢ ë9rЀÁ÷sÖ;8,_¡ÆüÅÊìåo?SIJŸ£3ˆ„¿*èÐ#2'ÍBº^¤¤r2ü¾EÛ&ÊdÁ>á*,“<úsóÅgŸš<jèÝq›Q€PûÇú*×ãmˆq DY6&,|ôdèxH2TÕµ›¶Ár›Tºð`é³C„Öh*a¡ÂÂ$YÍ%f[…O¢jÕ*Ò‡ðð099\Ú%Á†å¡M3^ŒßϰŠ‹ *ù3Ru$¨çºDˆ|.6DP $¦$Ùà }#»æÃ€°xJÉ.›¸hXƒ¦Æ£A8­¥ñ ]U„:Ú‚é°VA*¼5@úK:€ÃwËG‹IæbÁÛOŽL0±¬‰aXøÌD£V(‰0p…ÀoÁgÃõ`©¡O<²hP™(²‹wu„e¥ú>7Ãtôƒz=C3@Å0Eða!SÈ€ f™‰=Ì5á±±#¼‡ì×/ôd·Ñ‰>«,òÅ—&.~÷ÈÑ©Ã"HÕâh4V,d¡E¢“ ¸|kd襋Õ(…èã°þ&¼ØW.QÜN¬*¹\sߢì÷3•a?ÿè£Ïf¶ÿX‰‚Ä ”CL3R«g"åw?A«·Bø%]ÀâäÃû͈ŽròC£G„´§ ¤¹Ÿ 05d9M­g?J=ÌÐ1á}Füfò¸þŠçž¿WÒÅhVƒ\®0aqtÜÈØXõªÌeè')ȬŠ3ÇYL&t>žahÝâ”Õá=‚´`0½àýebõí”<OËûio{c\LŒöÍ5ÉQÊÖ}Zs&ToîY=P©RÈz‘á“Vl{«ÿ¾m×½-X>'=|à ÝÔ§ŸÑ„/HNYªAƒ/%dÈhX¡Æc ENÏÍ}3::ZŽÓ}?ÝÕ»ù¼¯ÚIG/Xøh\üêw§æ¼‰ÎDjµ1Zå_Y”9 ¼‹^Ÿ6hæbè°Ãûܱà­Á$Óå›ù}ð‚Ûœ´ø™G~¶:5w5…^Aþ‹PF­þH诇¯ÊM>)EÁ1|$Œò5\×ïøy¼Ó—³*%ewêRÈø´û¡³ƒRV£.TQ«v§¼¹:5I=(¼OŸð^š#ÞnÜ·Äc*š.Z–&'g¦~@¡¨àÆeKtÑšv¬ŽZ²L­Û’™¹jwò›äK{EÒº –goýõ=9Ãê%Zm¤zàbåË» †¾œlø ê£(…BI奿$ç$§.yó=»»»®ëJrÁnC¾ÁPïàØ¢Å«ß]šù®F;}!óƒ¥iK4Z]ü®”½){’ µÉy?|ÃmÄâ÷®ÉIIÉIμŽî-O5\ܽç³lÕîœ5‹?Jù Æÿï®I1R2 ¹)yàðëwË“–bÈ\“¼&eÁyk ™×ùÝiù;V‘’Ràñ5¥$çìÉÌ4ìÍÏÌã}×÷zºe°À±Röäçdæf",!9ÇPZ%¥ ?å£Õkv_ö�iNNJfnN]nrÏwÝÐ|ýŽ`ñC]~]ÝžTpOsjjJrnnÚÞÌü‚ÌUkR Ý|Úžœ5™ù)™Í>_}j}Awü¸äóRrëêêò3Óòêk?3ós ù99†ÌÜw‡5z — ÀÎMÎlíRvî VsÚžºüüºüCj ØÅiHÎ/ÈÏ5ä.& »ê¹|ÙWW—³fË•¦ú½©©†Ì”;ƒå»²7??o7X¤®` påÖÔÕ­vÛ¦Þw•¯IÉÍ[å\NMNNÍ-HINéÎó3‚Àâ¯ä]çó2/#O]~æÃå‚O ™ŸíkíÎDës®_‡vY—oÈMÝ{§ž/Ï{<¾š”ËHa`³ü”\ÃnP~ÝžÌwV*XÑp…÷RòAu9©ÝzØHnâ÷&çÔùK&¸jr KÊ|ͰT{¥ùr2Z0d^éUÐXyØGÈbu™™™ù—/×|´„…-uõ—¡9®äç¤äuá†øn`ÀRR yõ«þr8ó‹“/îvÄç«É­»Î×ç×{®ï­oºCÏHêÈu¹Æã¹Xü¹_b;ºoØŸ'¯â:zìÏ•îù¯kX~8Ïõ+<_WwÝ‘P\ ƒÚ#>xïßæé'ÐuùIxÐ{®\÷¹œ:°ÏÝýg[õ –Ïÿ 3ç¾Ò}%=þ„¾n?7?P:õjOSõ�Ö‘£ ?çÞˆU|4á'i­ÒŸ¢µZJŠ‹þ[è»IáK&tñlÑíÄò”ø)bñö}G~ýW÷†õ· ë¶”Ÿ±þ?bñPZº*bo Ó/ÝêtªŸðñ-n¯ÛÝÚœ¼´ÆiR9Bkqk¥/j›Â>ï¶Û7ü¨h¦nzq‹›¡´vÂÇ‘2R&'uþ¬UDèä!]<nèaNÞ~m¼[' GXÚè&C79ÕËk "DIðš"þ#ÄÑhg§\ÌR¢ ¯·òר© OÕ„„¶^Fæ"ÑìDˆ¼Ä-!nv³©CLˆ5&t"Õš*='ÍYån°{±õ½>»U,<,á à¥Íãðäïx¿ \fH&<Tû„44!"éD¥$ºÅ/šJFæò¶¯ÂÙƒìãBѤ)š¢ˆ}ÒŽž0¢”É5ÈæœLêd2 zFo¦d2ŠK{ë Òó½E®17CÊ”f 3ÊŒ<šF¤¤±AÐ<",V/¼iaѹqºAFHô ¹÷¤.=|Ò¤•q>¯FÆè J4É‹†%<þ þOxr4'Ha ïŸè‹Pº}^](J·[†' a„oêß%T>#áÐ|927ƒV…„à]øÈ‘‚qbÕzµb¬–x8„($D¢åQu!øjŸU,LP‚?-¬…¿:š­$¯&E`84©ŠŒ¦Åh*/– (ÎßÈæ÷y,X#3ªà8”QF„hÙ8´`-2QH -hZ- Åzѽ„HIÊM2‚ H$nÀÕQhšŠçBED¨še r‰ºaáIЄ-j§”s¼[ƒ¦¥‚:l´(f ¬˜ 2"ÔØª·0´ M²Þ7¡ù—lÜ>¾ƒ¶xV  q#ÅA=nØIÜàs+Q,J8MÀX"‘ÄŒgÙ!(ˆ[maE„†µÎ¢¤ö¸4A"Ï¡¶±Ë:4#ôÑ*AÂøhKêº $ÍÑJð?ù%¶ ÚÉÏ^%T‡ýT%ÂRž_ [ 5g7XÇMpÉ¢IqphŽ(F7h´ŒSÖ6u£Ï3Vå¯ÆŽŽê’ak©oœ†÷ÒPDÒŠ‡àÕ"‘½J¨îªÎˆ°Z["ëuÛ5"BŠfû1îFþBà@Ö2!FDb Ž×n,ê¶úÔÈ nY¬¬…·"ßÚZƒ”ÕáåùBpš‚Këv;íÀŽLŠØÑ÷eÜ¥ -¢ÕZ”`-‘T&iŠa²#™Í®ä·&æ¹�RÖ3I Bç’t°Ú¥Úo­ªV':;:ÑK‰Ä¤F-FaÂŒ¦à“‹Cõ²v¢‰Ó¤AòÂä`¨›ÁXþV.’¡§ò¸u¨éˆäžA3À&Ðä=šé.„Y¼{ˆ)H¼déö5ˆ -‹r¨QÒ:wœ7Rhñ"]‹G­š ¤¬°S4þ¬M®6‰F-_€Ö ‰,ŸÂö1Â7VC*iBíJ<ÿZ¤Íäã U$µßÉûX²C?kWÆyÐ=p°ª%Qi÷/ú.([ûfw’ Ú Éâ‰u8Iêl¼°“]™‘ßËR¤®Q7úøDáÞyèÿ�ŸOÏCîci����IEND®B`‚‰PNG  ��� IHDR���–���©���þó4¨���PLTE"?&!90+&$2-*62.:63,16<Y0;D>a;G)Mu3EP.Ro7ivVnB=:P-(r4/c#M=rEB=SQ.Zr1oV2qo6__LHGRNLTQNYVTIPUK[fJToUmNTgoa^\iOGda_spMjgfqnluslywvjswTZ¢k’u¨*P’0s0n®1\¥({ÄRU•Ro“Cj©l_“mph¢5”x0¦nm.v¡!O³wp†Tv‰upºpd˜^qÆzˆ¯6Œ4§¬•ѨҦæ(ŠÌ(¥ÙH‡ˆu‡‘u“ªZ¯žu±ÍW¨Ðoņ“2,®)±.(˜.ŽO-uŒq.¶CµQ!¦q*ŒTNŒkU~}‘jq­gr¨tUÈ, ÞzjÒmjãuiáulÞi_Ê@ˆd‡º|ˆÜ‚’Œ‹,…¯'®’.±¬­±. £ŽŒR„ƒ{ŽmŽ´S¯h«K£›o°¨p¯®P±Ð£Í&”ÈM±ÏHŸ×UбÒ®1ê² ë·è­ë»&ò©<ò·3Æ–"܈zבlʯMîšHðNíšgç‰qï¡Sð¥Zñ¬Lñªeñ³vÓ›VÏÎ ÌÓ+ùÙíÓíÂ*îÈ8öÇ'úèþõ�ëçÓè ÔÓIÍÇnîÊDðÎGñÒIòØWïÍRôÔnõêyòãjŠˆ‡‘ŽŒ““™—–‡•‹›¨œ¥—–«¹—ªµ¡žœ¬–¬™¼µ”­¤¢›±¹Œ©§¦¨ª¶±¯®²²¯º¹¸¬±¶•³Å©Á¬¨Ç§¹Å²«Ãµ¸Å½Æ»³×¤™Ê”®ÁÍ»ÇÆ¼ÒظÌѼÙçºÞó¤ÎáØˆ†×˜‰É—Ѩ—ɸ¨À¿½Ñ­¬ï¹ˆÛ¡žØ¼ÅáºÈÎÏ–ÂÄºÎÆ±öÐ÷Ñ­÷ê†÷ñ‡øì—úð­ÙëµÇÇÆÉÓ×ÑÈÉÒÔÎØ××ËÏÎÉÝçÜäÙÐèöêÙÏúäËûé×õòÑèççéôüúôêÿÿÿôïé���í~5f���þtRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ�ØÙ q���bKGDÿ¥òÅ��� pHYs���H���H�FÉk>�� hIDATxÚíœ XWÚÇC–/!­+P£Ýx›dða’@[­e½´l[km Û®µ•m•ºÕâ¦Þ+¶ÚZ]‘ÒâaÕ&"Zåš0\L•ŽÉ€`bEÛ©+rmä"-žï=3cw?Mn}¾gÉLfæ—ÿû?ïyÏ$3ûÙ8¿6À± ¬¶Þ«-×ô bQaymV§ó“ܼ‚[VWÍéY^^é`škàXõ‰ÎX¹yÅ¿–͙Þ¼§ËiUî©Òž_ «+Ù$)¡Þi]î©‚_-ˆÉ5NŒûêœVå*°þZXuûoËÕ™ìÔ¼‚SÿD[½K{½o¬z§¸uîIH¼ iÍ-8eýYΰےm.ïú~°:’œ ­ÊðV_ÎpÐØ’,—{X{îĺý¬WY ÞêLb€l}¦KN¨\.buÕÛµn{¼•°§?h?)QkØTf룹#‡ü"jÕ#Ÿ8±s÷î„=ý}±au%²<ûÁK¾£³þX]È'õw`Å&$ìísÑgÊR²&1U bms`9^ï¶h.{«&±Ó–ìŒuà0„ÑÔú¿+Ksv÷cíaäêJLp„³Þ].—±ê’’œ½u %nO.[ÒfeiÞÎ#Ž”¶Ûƒ¼½Â‘èêk\=Œ»XÈâ N^±H9œ€¸ºº:“ÿ.»˜÷È×Ù÷Ò=Éõuû ÀmnÊå&tŒ®ÄÃGãТýû÷ü)¸\²ó�´Î=qÌš+kþ®ýnÊ5¬Îdæùᣇك'l§,¾X¬Ü°›}A}B»èX,[¢›r¹Žµ§«>¡¦«+°ŽÙîÙ<1¬´´Tùñæ®Î¤„¸£èa\Êm¬÷þ@¼…¬ €uô0DkwÜN"¯  T¹!Q"ü#GSÇI9ºÛá-(7’]=Ð@°‹Ô1íN9ŠZÊáÃGMÌ+(½²áèáÝlðØUG÷[>Áy\L¬úÝèÈÒ¶“‡ã°b9ÚAÀ*º² ìÞ½ûˆƒ °ÙÚ°¾¿O6VÍÔ» OÚê RŽ:c…\¼¨üÀyjGƒbÍåÆ bÙ’3½+±¦ÆY§Ûj\ ùðçË÷e”$ˆ¹[)Âe¬ý))Ždר””ÔÔ/Üñþ¸0ä­ëw¤~‘šrVÈe«A¹Â-Ï»\A€ß¶zJêÁëW/^<:ü­Ñáë62j)ŸY>zñâÅë×déàì­«GZ¥Ä%ºS{¹ŠUsÁ‰‹S§¤\¿:|Ì’¥Ë–­XùnÒô´´Á[æ‹ÊMÇÓÓ2ö¿»bÙ²¿„¯^0%e÷VaöÿX]û±¤1›r¾®¬¨(\ñnbZúñíÛ?§Dj…nß~<-cïÊÈŠÊJÚœ¯ X½þSÕÑ#{ÜéŠ.bÕíFL©ë×lÊ7›oT]¬øÈÈȽÇ·oÛöð`=»mp%FF–WV™kkËÌ¥§TĤ·vªS˜^éNWt ËãrêŽÕÏh©Zs­¸®V~_Q^¹ïÀ!ÀzÔ*¾¬ ý�a%E.¯¨¼a6—•™Î—”œyï/ÎT0@‚;]Ñ5¬úÝ©ëÃCó$¦UUUVV”ÇG¾»?ãø¶61Y^©ü°2ö1b™Í&@])OÊl¨û(|}jÊnwæ.aÙ’ße4;·Ú*Ð+¸’,ä­ÜзA ß-ÿ¾ªÖ\Pç®T�TsssSææÅ«Ü1—KXUK•æK—Ë~ÆUU‰â¸÷Äñm:,Xö-¯¨ª­½VZrþ (ÕL Y™ÿòÆ[nÌ7\À²*Š„vëÛKeµN\7ªÀ_‘‘‰Ç·ƒ·Š‹/ç*·mOÛ‹¨ÀUçΔ'3PY™I‰{÷í[µjÉÒŽAÄêÐj/}÷cO½çÇ¿ûöÒå;YQ¾|ßíÏJ‘Za¡Oú¬¢Ê\v¾äLErV•™™”´w_|äªU«®\QE´–5*ßl¹ôí-fïéAd·£y£ª¢"rïq6oånψ¬¨WTC*)1iߪr„tæÌéÓçrg´ –U›Ž_vùÒ·H²DvËI²ªÊòøÄ'Â\ÿb®âÀÞŠª‹çÏTÖÔ57Õ1:E–¯z!+9w®¤¤ä”ÂE½îÕËR™Y´o¿ýñÇ>2'Áö}Œzbižò³J³éteMSSüT~ÉtŽ!*)1™Leg®»–!ôŽîἄ íÖ-G,)½¾ÅRäžEP ˆ©¼œU©¤ä¼ 53óòÚüœAÀ²Ê車!²÷¥›,HVT˜oníh¬â‹yÊv‹ºž™œ߇ÄOÙíD,·Ü?–!÷©j-µðƒàÊÊ4³¡ÝÚÛÖJ3é°z{ÛŒ_¢MeH z3N;É×Ý?VÄ×wJÕÒÒòÏ–¢Örí†ÅRf¶öô¶·ðq!€u*¤¬ž[(“ÉÂF¬C§宸ëîẌS[Û¿ß fF«-7Z®A³XAž›mbbbð)„eéhíéíµ˜ïÚ®$Õ»bY æ½^`ìÚ^Vv¹ýò¥Ë—/1íæ­›·¬½=ÂÙYJ cu.ÞÞÖÚÛÛÓZDÓ´™f6ª­ý9Um¨+¹ëîX2”ÊÅ·¨}wë…ù ¨ÕÛÖ~M1!00WG·Z¬==çOCæ<Í´³çÏɪi',EûýbÙrQF�œï¾ca “ë¡Ý¼y¹ìb fmm7QÆ"ºýf¢´@ M¦¢ó§Ï3tçNŸ?wšÉIJYî?ˆvñ²Š‡Á¹Cöe ô2äóʺúŸ@2«µþýhm¿Zñ}¿.t­‰¦M4B*2G‰‚ kèý[^–Óz‰ÁúîÇ[ß}wëÛo/A}ƒ’m:úJE]]æ÷gÎ[®~ÿýÙ\èèþæÁ3Þ^±¢¢²ª/hÎÎb{‚ÅèEß/V+k½ÌÚ Y˜·[²äì™ŠšººšŠ3çMÕUñ+ßž1wÖ7Ýžž¢Z¶b9[žþÛÖ*çà÷‹EsyùfÐçvÍpÑA9s¥"¹®.ù³+§KLÕ•ñË—©‚Ìzî‡æofÎ ž6À"?Cå|í¿¡¢Dž^.Dñ®X$G€[ƒNmÙ53i¸R¾· â”Ù|µ|åÊ÷fÏœùô…îþ4kæ”`ĵâÝøŠ#Y«óËK‚Ñh„È•Bu~æLù^*s/Œz%¥@UY¿r…jFpðÌYßüðÃ…çf-ž>cÆÛÉ}ÿ ÖZ+K¸÷eáóü$bªÃý ¦ÓgÊã÷Ö8 N—œ‡ÐVU¬Š\ùžjZð‚™³ž¹¾™5knpð ä°•Ëã+¾¯¬ºq;cu˜q r°ûŲêý½&IDÚ›7KJNCðö%ef6$ï¿r檞j¡¬X±bå2V­§¿ù¡ùÂfÍ\ {{Ù²•Ë÷•VYÕ7cjí(KpŒçéÊX}W¬…QºpÑ¢×–ÐmWW­Ú—”ÕݾH€:­.@ã—¯\6cZ0’ëO¬\ˆk2ØJ˜öC Q$/´wX–¾þÚ¢Er1ËýbYóÒ×Þ|3|T¨å§ï÷&AŒ*©s¨lIšb|TËTk.’«û›§r©Wdüg•h†ÝÞÑš3*ü­7ÿú:{è¾ó–ãø‘³ç|²uëšMW;k*`²ÀVwLX`f Øå˘ "¹€ L?sÁ‚ài ×Ê•ññ•Uµ­í9kÖÛúÉœ¡|Ì‹{ßXvŸSô‡SŽzvJˆá§–klQÎfì Vü¾•+� äbí…Ôšëˆ"ª¼ÑÝA‡>2÷oÇŽ¦®*c\/W¾]rw¬vJ(œ$;sãë?Λ?ÿÉñ~Jc[ÇÍö¾ìаöíƒ(¾ \H°¹³f= ]‘ "2WÅÕŸ:,¹ã~7¶ž7s¬P<iŸ£pê^µ¼Þ0üµw¾M•½òò““{,h˜DurG[+ªÍúäBzMG‚Í9óø3í÷ª¯®Öÿô“Q+õñzlêä'÷ÁÄ£½3‚‹¹Ã{aaZÃ’?/3û½¹4ìÉ o±X„áQtwh­-H°åËW,{O¥š1cڴ߇……*(iok³P¹„Ãĸ¯w÷Ãaø¨Ùkþü(XKà©°„ÎÙºõ‹õk6Q¦2}¨T"ÃÑ0±T®5е­­?¡öÏ«W[ÚnÞ¼ii·”Q¤B*‰àE &àó€)àýƒ·Îê%öâšî«ç ˆ³æ/Sýí¹ž˜AÒÕF½V†ã@¡#c¸?!%B# þ„?Ë…h¹ŸD"%d:ƒV$™4~Êó/üíЫ‡Šp¯é«QÁ—HtmØËóç¿4ýwÞÃÆê(ºš6Z™ÌÇqF:„"dþŠÅ°L*“©HhIë•„ï°ÉSŸš7ÞãC…b‰XÀ!\:ùv¬:jøÂ×¾m,+xijxÞ×O,ö“Ê¢HІÁ‡¦)Š*Ô«¢"´ÚžÀâêj ,Ö*\‚‰}a£Ç‚‚† Á&X´hWàÒ4ñž§FèZÕë}óQrÒd ›î £Dì%ßà!Sju:e ’N«Ójå2B*ñÃcÁK$¿ß><ÄG<aBø›}í7/bP°:pJ0ç“O¶®YTMEèh~,ƒ‡±aÄØ¿ 0îX |/¡dBøú­[?Y;ÛS$r©6½7V;ßKKt(LÿôóSB´mF¾"$H.t|DàÔ Z=AERfZ*‘?ñÇmÇŽ=8ÖK,¸–MÇ|‚ªýò/‚矚6ÙÛw¢"‡2[è"J¢€8â8K‡(ý¥¡P(Hªˆ®®6Jbܰ iÓ_š?þÜQ#¡tÃx|ã `iy”|øÂ¥”©€IóMöCqà …–,¤‹,´ÅRd ‹h£‘¦‹Š®Ñ˜„H]„L bÆñA“§>1lˆH %ÒpŽÌµ/ŸÝS-Ž€Žx}ÑØgrÀòs'ûú9B…BFr…5CÎ`ÌÆD˜i¾ÞÞ¿â#™0jñŸßyô7˜É%ª{bY$| ¥3ç°¼ÞTM*PªºÓLÆg~Èì¸ó*XŒc^|/ɤ5ë¢²Æ Ã]üJܽ°ziíðѺêë‡Û³š\]Må(¤Nü3·;ƒ‚VR…NO«Ä“¤@Žß~,uýP!æåïâ'÷<ÓÜjT½þÚÊòõ /Ο7uš¯ïDÈàtµÒê56¢(e°YÇe²@-Êñ&Z8Þ;hòtHò/Î))ä .~•÷Þçåµzý˜ÙPB”¿Êæyäx©\¥§Àå4Lë)¦AF-¤ji M›Œ©Uø£qÉo|ÐãÈóA>>øH/> Ô–ÁÂÒñµô–­©;6æ•Oσ6˜Ãð„<J¥UiµQQ!2dz±„©0ÄL“H|~øá!â€5«‡•H\«˜]Â2òx„áÆ—Ï½ðü”@­Áh@Î곕„‰ J&¿³,}ƒ4‚óBñ¸G¦<;v8¬{ºV?¸‚ÕVˆ Ä2êâ+/Ï›äýˆ‚4VQ9r°›ç%,Þ†G*FÀhNkÅã|}Ÿx|˜@4i’xºV?¸ôQmÀ`‚·ô+s1Ô§Œ³­ÆE‘ªÐPš ©?äwÈð8ªRè(=$yšÊ‰ù@i:Äg¤Eï¼³h¡§«iˬÙÙ¥o¼5{v@T¾ÉHÊq&•BZ…D¯¥Œƒ©š¶Ô–YL*£r¼J1 L"°fΜ5kÞzý7×Ê×°zbcþœ­ÇRnx&$×XÍ ÕL}Ú_ø9Œ†÷/…¢G¡+¤å˜dü”g¥M=¶u¬æåZùà–]%æßøòÐßþôÂüyÁãÇæêM¨þÓ‚(⃾)éK¢P9ÃhIL02FúúN{ò¥_|áOÛß;ÒƒkŸ¬¸ˆe¡°KHú•—ç?5u2[¡2C5”¢Õ&&yÝn”ÆrUT>˜SšNž:oÞã‹°Ñ0sõs;—°ziÃ’E rçhÃÔS¡ÊÐ?E  ÒŽÄÅŽ‘zÜä  ïßúøHÆ,zÇ k¹öYuiX:gÎÚ5[(ÓÙ\™¤¿:詊øçu!³FäõH°víÚ±ÿ#ôR .–ÖÑ_OMýäÙ)J2*©@ÅŸŸ3Þ¿ Ö £*_+áÁó©ç éåÚÌÕ ,«±0Âx*ÔyÓç;.0LŸB.Ã¥ .é÷¼?Œ—Š\ò¬‘†Ñ|"x~ú¼ùÏOé3b8_ë2•«_f!CHÿ(úâ«O2ƒ5ª¤d$idÒ¦ÁÉ (à‘*²˜áYšŽ9<?9h˜ß¨Qý\­µÜÀÒódtΚTiAØxœ™Ú05±X*• Eˆ5ô›À¥f|djŒÓ˜_Àû«G Ý0¼ËX¤`y£pî”ßëi:_%ÃŬ³PÜæ§o6ƸÇ Èñb˜êÏ+Äø<jð±ì9âµ%9ÿñ’¯÷øBFæ(dý¥¨D,qLV™^�IMI­H‡ûy=îÓ‹G=eî\Bâ*–VjøKøìÙ[Œ¦â`õô4e2ê)CŽ2JË6•–¢ò ÕtŒ‹ FEч}†`³g.r±Òrˈág¿>”zpí‚0ª *¦ÖaT¡Š*O ¡k*¢” )ή#"d¨OŸÝºu¬ÀårÙ=¬6£bÄ–ZÈ/>9Íw¢É$ÉóÇ\_ÄþA1”kI½2H ïø©óæÏ=òݺ,ÃU¬ õöëá¹EPÐ?ö˜·/3%I$0 4Œ_i•zc‘Ñ §- \SËûy3'k|FŽñܽË]Ų dÎÚ÷7|¬/-V2•©˜é‚8.%¹A™B&eæ‡hb ¿DCÀY£W¯5\èNvpËFIE*󗇞{~JYhÌW�J^}=‘IŽ'(GH¡OH…ظ)sÿ°~ŒPÈs¹¤qËÞFI½uù•—Ÿ‚¢`ÔxpWŽ MvÐä†4K”Ë:²ˆ*¢ÈˆqÞ“§>ÿÄ0áèžîdx·°ì9Ô²ð9·K_ŠN BÞD�ùt!E›Mee$Y‹Î †ÏQÈ¥DÄ>ÞPӈnjû¨kg½†E¾Þžºõƒ¹õ:‰Ø0ø-M1üý™¢™-· Џ—ó°áà@èîÕx®cé…ò¯ ×_yqÞÔñ„@Yl@ Bê˜ëKCÌ·!?„e©@ßió`àÁ0O‰»m¹ŽeÕKmÎéÒW¦C „L§/¢`œ(˜Yë E=UÍœD•I%”¾¾>Bl’ÐÓMûƒeÓê—¼šOR§ r!+9*R©?¸œ©%æ\¬ðÃE(L1läèׇû»}E¥WÜábjËœ [@ º©“{ZKÌ"2't%~Rè úB™‰F¯y”×CnÔîciyy=ûƒ¦(õ¥'IJ1Óku¤Nƶ˜¸Bâ72gÃU ÔøçŸÞ0f¤€ëÿKb™ðá —è‹_}uro õô¤–*’Ì…ÿ99d ’ä=UHêß  ÉO ÃFŒðüÁ/D“ÀÿÅs6~t¶8l¢&Ù(GsÅ z$: BCh!¶:R»y£•C`6rÔì7† ù¢_«Z($&lHýäéà0’úêóÏ¿\zûà ”´ W "|¿LEú ýy’ÖP‘ÐÓ•¯ ‹N±Dú¼'§MöýüdvX j?$0)T9‘«UQŸÇ’Ú@_ïÉOÍ›9R´ðµGùngSw°Z„Bé¨Ùå!S‚‚¤;Ô[ÐGRPYå’š6êõè|<IjcvN‚þÖ‚ÉôšµkF xRw©Üº$W+!ÛxpíÌÀ@Ùº]Ñ~â¾}új”&$ëNî-–ˆC0ß¹Û¾?räpžáÅjÓN=FöÒÂwMtÌfM¾ÄP!£i…”™Ip,<=}\.—ø2å˜~„@ëþÕün]Àܦ•¯YÿLHèÎ7Ʀ­Cg"e²@Êòooˆ‚žÉwfÅYºl_ï‡}üÖ¼?#p1¿{7^°¢6.xbʧq;cÓwâÌÇ+(~~‟³ãõ¤é1“VoÆq˜"úNY>ánM3�,L¢ Üs"v3T|2¨ýÐÙ@¿11;Ñ¿€'bÖ팒ŽñõööBœØ}ܾMEçuãæèhuì§8: aܲI!'>=²3`Ó…\®8 Vï8½[<zˆ¿öÚ@oÆà.–-£©ûdšF³s“Læ/½QüÖqÈ¡oEk> ø<@$ã™±iÑiѱ›Ö}l±üÆîb5GgŸÐdi4M­4•»qçG›Õ²ð]õ§›ã62Eèñ˜Œ˜“ÑšúèÌ3 �+cWZLLZ´ºwÅj®Ÿ8yƒÜ²ãDÚ®ŸÇ| óÿvÅh41jMz `uvÿ‡°lq1õ®è]é¨ ÎÜ•­ÀqYGv~“m³wÆD§T«5YjÀêÎø…ïpÐs2+M®FX6Mtš&;¶©9&;+æó»N4Ø€4--FžÖ˜]Xš®êån5šÆ¬ÆÆ“±Ý(†±1Ñééqê¬lõŽ]1Ù9V{ÜÉ´]êìì¬ìu—ÝÞÛ”=0.w±2cÒ³Ôq™Mu(žê¬´lMVZšFþÑÄv›¦A£ìôhu'pkbNhþ3X]q'³²³Ò4±1 ‹F­‰ÎÊÎJפk®+'Þ´54h2Óvhîlʈըcþ3Xö挬¬Ì Hcö.àÊÒìÊnl­N˜Ã&ܴ׍gVi ±ÑѱéÙ1Ñ1»†û7#jÎì¶gªP$.K½KÓý…&[ýe¨uB–§uwC¿lÌÒ¤Çf ˆj ÷H²Ù@”ä0Ð,+&]sœßxRýŠ5ÍÐKc²Àui±¼ÙÈÀnÝ”ÖèhjÕ. <H×l*—VzsWC4z Q7Œj€X™LŒbÙêlµ:«¡¡1ûóM¬Énlj€î iÎJ‹Étë‚øûDz7ÇÄj2›VSC6óë ׯçM<k·×¥7vÛ›²šlÝM†øý·šêl¶fÀ²Ùà×õÐ"sqÞÄSv[fÊ044~÷…Å6[7¼±±»UYP “Ú³öî~˜ßi°˜ã7wÛ­ÊSO)óïVWƒ€…tiË+Í+4¨Á»AߩҰ› ÖÙbårßÀ;ZA±òT«ôAT«§¨ ÷׺AßÝZi±òW»ÞÿÝlE§D,»%ïì R ÞmXõ.¬ÿïoZû_¬ kÀ‰±wp’DÂê±öZ­}©WK´é¥ÕÛUæE õ:¿/«ÅÒzGMhÀïúµ+‰Ë,; 0SÐ̶&.G!ôpÿD:Ó,\úv0-¼ý­x«ÂËÓ“ççôå}+æ!º[6‘q8bŽðàxÀ‡'G/nò(œëþ'mìÅ\ „‘ðð´8–t`°g8Žð6ˆ•ϹÛe¦­< 9v܃‹Iq!l·U[[,ÌN °6kÿúY™öþ'½Ì3‡ : PFØpNÿW—µ.¦óå=ŽôÚ­^AGoÿ>˜-Ñ»gÚ“ƒÛÀ[ÌNz¬: ´ {‹ˆBiN J"­¬œ„ uCU:æ¸ìÞ($ô`ÒJb1ú¨B'ÐÁø®X˾ )‡K1î…Ç=:+®mpø*‘PÄŒ¡*ØR/Ðö^B@*à8‡Ã'(ŽMêxoS½ ã Õy9=v‚Ã6±ÕÞ«ðäxzr8„Õ*àpá$‹åx‰§mçí°V`%Ñ"ö%þ\ÙÆ¸¶WÆcÜ »àzxp=dvt8fR»‰{F?hk®C-ôÖ¹Ü.‡ÝK1.NÊá´Z‡CÙ)§Íð>t é(,è$°\'àÀaÛ´Ê@-,‚- °=‡S½èªWâ õ# .—×ÂÇÐ x\ާ”"áà|iÂòd°h.GÚæÅáÁ›´è˜$#zÈ#[[(kp<u}~ë¡õv }=lÍØÆj·;yËN‰+rœY ^@V‡[ ù(| —Ë7Ø ¼Ç~[-ŠË!Ð2¡ÅÕr89(r¨6s7‚M«Y 3{C½:îIJ[i­âOÂNbD/r°W³‡câTÍeÓR5z Ó­ Žô¦P°™cÚu 쑇.# dd›�18šMêÁbÁa,h¯F-)³åíÖ«…CÁNø}XhR.×RÍî,:œ‘ùëè‰T¯ÕBp9^mHÒÚŽÁoHH-ÇC‰ˆ‡ú¦Õbµ²v4)Ô*ð€E¬ZÌZ[sŸV¦V8n¢Þ¶Ìjm³�;’±£÷KZ[Ŭ·8}já¬Z\/�¬ ((fþ·9ÔÒ"o]+«È(GÑÁwR ½¤Ö¡Vu_ÛœƒØ‹sy!å¡4a�ð…<O•à¶aï\/>ky.‹er`9z9W€¾qbU ®Ã‚8rp¦½BkÛrÆÞ;Fc^̳Ú[xzˆ>––1[2Xþlç*zÐ÷(ÐC/Š}‘œÙ–ðD:ð8rÔs˜¯žµð<äË‚3_é#Ù›*Ù(kÑc‹]gõ—¢ µj%žJP˜Ó8k‡ØÐÕo(cFˆ-އök∾±Ù%€¾‚QÌsZa ³ãEq öR8¦h!¥Ÿ#WÍ3;ú_a|XïH/����IEND®B`‚#!/bin/sh # Create version timestamp cdrskin/cdrskin_timestamp.h # to be executed within ./libburn-* or ./cdrskin-* timestamp="$(date -u '+%Y.%m.%d.%H%M%S')" echo "Version timestamp : $timestamp" echo '#define Cdrskin_timestamP "'"$timestamp"'"' >cdrskin/cdrskin_timestamp.h /* ( cd cdrskin ; cc -g -Wall -o unite_html_b_line unite_html_b_line.c ) */ /* Specialized converter for the output of man -H, which unites lines where the line end is between <b> and </b>. Copyright 2015 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <errno.h> int unite_lines(char *buffer, int *b_open, int *b_state, int flag) { char *cpt; int last_was_nl= 0; for(cpt= buffer; *cpt != 0; cpt++) { if(*b_open) { if(*b_state == 0 && *cpt == '<') { *b_state= 1; } else if(*b_state == 1) { if(*cpt == '/') *b_state= 2; else *b_state= 0; } else if(*b_state == 2) { if(*cpt == 'b' || *cpt == 'B') *b_state= 3; else *b_state= 0; } else if(*b_state == 3) { if(*cpt == '>') *b_open= 0; *b_state= 0; } } else { if(*b_state == 0 && *cpt == '<') { *b_state= 1; } else if(*b_state == 1) { if(*cpt == 'b' || *cpt == 'B') *b_state= 2; else *b_state= 0; } else if(*b_state == 2) { if(*cpt == '>') *b_open= 1; *b_state= 0; } } last_was_nl= (*cpt == '\n'); } if(*b_open && last_was_nl) { /* replace newline */ *(cpt - 1)= ' '; } return(1); } int main(int argc, char **argv) { FILE *fpin, *fpout; char buffer[4096], *respt; int ret, b_open= 0, b_state= 0; if(argc != 3) { fprintf(stderr, "usage: %s input_path output_path\n", argv[0]); return(1); } if(strcmp(argv[1], "-") == 0) { fpin= stdin; } else { fpin= fopen(argv[1], "rb"); if(fpin == 0) { fprintf(stderr, "Error with input file '%s' : %s\n", argv[1], strerror(errno)); return(2); } } if(strcmp(argv[2], "-") == 0) { fpout= stdout; } else { fpout= fopen(argv[2], "wb"); if(fpout == 0) { fprintf(stderr, "Error with output file '%s' : %s\n", argv[2], strerror(errno)); return(3); } } while(1) { respt= fgets(buffer, sizeof(buffer), fpin); if(respt == NULL) break; ret= unite_lines(buffer, &b_open, &b_state, 0); if(ret <= 0) break; ret= fputs(buffer, fpout); if(ret < 0) { fprintf(stderr, "Error writing to output file '%s' : %s\n", argv[2], strerror(errno)); return(4); } } if(fpin != stdin) fclose(fpin); if(fpout != stdout) fclose(stdout); return(0); } -------------------------------------------------------------------------- cdrskin Wiki - plain text copy -------------------------------------------------------------------------- [[Image(source:/libburn/trunk/cdrskin/doener_150x200_tr.png)]] [http://en.wikipedia.org/wiki/D%C3%B6ner_kebab Doener] '''cdrskin is the cdrecord compatibility middleware of libburn.''' Its paragon, cdrecord, is a powerful GPL'ed burn program included in Joerg Schilling's cdrtools. cdrskin strives to be a second source for the services traditionally provided by cdrecord. Currently it does CD-R and CD-RW this way. Overwriteable media DVD-RAM, DVD+RW, DVD-RW, and BD-RE are handled differently than with cdrecord-ProDVD in order to offer TAO-like single track recording. Sequential DVD-R[W], DVD+R, DVD+R DL, BD-R are handled like CD-R[W] with TAO and multi-session. Additionally cdrskin offers cdrecord-ProDVD-like mode DAO with DVD-R[W]. cdrskin does not contain any bytes copied from cdrecord's sources. Many bytes have been copied from the message output of cdrecord runs, though. The most comprehensive technical overview of cdrskin can be found in [http://libburnia-project.org/browser/libburn/trunk/cdrskin/README?format=txt cdrskin/README]. About libburn API for burning CD, DVD, and BD: http://api.libburnia-project.org -------------------------------------------------------------------------- About the command line options of cdrskin: They are described in detail in [http://scdbackup.sourceforge.net/man_1_cdrskin_devel.html#OPTIONS section OPTIONS] of [http://scdbackup.sourceforge.net/man_1_cdrskin_devel.html man cdrskin] There are two families of options: cdrecord-compatible ones and options which are specific to cdrskin. The latter are mostly used to configure cdrskin for its task to emulate cdrecord. There are some, nevertheless, which provide rather exotic unique features of cdrskin. The cdrecord-compatible options are listed in the output of {{{ cdrskin -help }}} where the option "help" has *one* dash. Online: [http://scdbackup.sourceforge.net/cdrskin_help_devel cdrskin -help] For these options you may expect program behavior that is roughly the same as described in original man cdrecord . Online: http://cdrecord.berlios.de/old/private/man/cdrecord-2.0.html The cdrskin-specific options are listed by {{{ cdrskin --help }}} where the option "help" has *two* dashes. Online: [http://scdbackup.sourceforge.net/cdrskin__help_devel cdrskin --help] Some are very experimental and should only be used in coordination with the libburnia developer team. Some are of general user interest, though: -------------------------------------------------------------------------- --devices can be used by the sysadmin to scan the system for possible drives and displays their detected properties. The drives are listed one per line, with fields: libburn-drive-number, sysadmin-device-file, permissions, vendor, type {{{ 0 dev='/dev/sr0' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B' }}} This feature is valuable since cdrskin -scanbus will not give you the device file name and its current permissions. cdrskin will accept of course the proposed dev= option as address for any usage of the drive. Different from cdrecord, cdrskin is intended to be run without special privileges, i.e. no superuser setuid. It is intended that the sysadmin controls drive accessability by rw-permissions of the drive rather than by x-permission of the burn binary. To be usable with cdrskin, the drive has to offer both, r- and w-permission. -------------------------------------------------------------------------- blank=as_needed applies the suitable blanking or formatting to make any supported type of media ready for writing from scratch. If this is not possible, e.g. because the media is written and not re-usable, then the program run fails. Option blank= offers several specialized blanking and formatting types, which one may use for particular purposes on DVD-RW, DVD-RAM and BD-RE. (See also below: blank=format_overwrite) The drive offers a list of possible formats by cdrskin option --list_formats. One should acquire MMC background information before making use of them. -------------------------------------------------------------------------- cdrskin does not only read from and write to optical drives which comply to the MMC standard but also does the same with regular files or block devices other than optical drives. Because the power to alter a disk file might be a bad surprise for a traditional user of cdrecord, it is necessary to give option --allow_emulated_drives before an emulated drive may be addressed. Eventually one of the startup files would be a good place for it. See man page, section FILES. The addresses of emulated drives begin with the prefix "stdio:". {{{ dev=stdio:/tmp/pseudo_drive dev=stdio:/dev/usbstick }}} Regular files and block devices behave much like DVD-RAM. Other file types may be valid targets for write-only operations. This includes standard output, named pipes, character devices {{{ dev=stdio:/dev/fd/1 dev=stdio:/tmp/named_pipe dev=stdio:/dev/ptyxy }}} These files behave much like blank DVD-R. All files used as pseudo-drives have to offer rw-permission. -------------------------------------------------------------------------- The DVD capabilities of cdrskin differ from those of cdrecord-ProDVD. cdrskin offers TAO-like multi-session with DVD-R[W], DVD+R[ DL] and TAO-like single session with overwriteable DVD media. It also offers DAO on DVD-R[W] which is probably the same as the traditional cdrecord-ProDVD write mode. Non-cdrecord blank mode blank=format_overwrite brings a DVD-RW disc from its initial profile "Sequential Recording" into profile state "Restricted Overwrite". {{{ cdrskin dev=/dev/sr0 -v blank=format_overwrite }}} DVD-RAM, DVD+RW, BD-RE and overwriteable DVD-RW appear to cdrskin as blank media which are capable of taking only a single track. This track may be positioned on a 32KiB aligned address, though. {{{ cdrskin ... write_start_address=2412m ... }}} Non-cdrecord blank mode blank=deformat_sequential brings an overwriteable DVD-RW back into state "Sequential Recording" with the capability of doing multi-session, if the drive is capable of "Incremental Streaming" (MMC feature 21h). Used sequential DVD-RW media may be blanked by blank=fast or blank=all which normally both do full blanking. Thus sequential DVD-RW behave much like large CD-RW with possibly more than 99 tracks. blank=deformat_sequential does minimal blanking of DVD-RW which usually yields media incapable of "Incremental Streaming". Option --prodvd_cli_compatible activates blank=fast and blank=all for overwriteable DVD-RW which normally ignore those two options. It also makes option -multi tolerable with media and write modes which are not suitable for multi-session. (The default behavior of cdrskin deems me to be preferrable.) Option --grow_overwriteable_iso gives cdrskin ISO pseudo-multi-session capabilities on DVD-RAM, DVD+RW, BD-RE similar to growisofs. Associated options blank=, -multi, -msinfo and -toc are available in this case. They either pretend a blank media (if there is no ISO 9660 image) or appendable media with a single session and track on it. blank= invalidates ISO images. -------------------------------------------------------------------------- assert_write_lba=<lba> ensures that the start block address which was used with the formatter program (e.g. mkisofs -C) matches the start block address which will be used by the upcoming burn. E.g. cdrskin aborts with an error message if {{{ assert_write_lba=0 }}} is given but an appendable media is to be burned which would start at block 68432. An ISO-9660 file system image must be prepared according to a particular block address on media. If the prepared address and the real address on media do not match then the filesystem will not be mountable or may even cause system trouble. A sequential archive format like afio or star will not necessarily need such a coordination of addresses. It might nevertheless be confusing to a reader if the archive does not start at block 0. -------------------------------------------------------------------------- fifo_start_at=<num> is a throughput enhancer for unsteady data streams like they are produced by a compressing archiver program when piping to CD on-the-fly. It makes better use of the general property of a FIFO buffer to transport surplus bandwidth into the future. Yep. A time machine. One-way, i fear. FIFO originally was introduced by cdrecord's author Joerg Schilling in order to protect mediocre burner hardware from suffering buffer underruns and thus producing misburns (at 1x speed on CD-R media at the price of a DVD-RAM nowadays). This purpose would not justify a fifo any more - given the limited life time of burners and the seamless underrun protection of contemporary consumer drives. With an unsteady data stream the task of the buffer is to soak up peak performance and to release it steadily at the drive's maximum speed. The larger the buffer the more reserves can be built up and the longer input drought can be compensated. Original cdrecord has the historical property, though, to first wait until the buffer is completely filled. Best practice for fighting drive underruns, of course. With a very fat fs=# buffer (128 MB for 12x CD is not unrealistic) this can cause a big delay until burning finally starts and takes its due time. fifo_start_at=<num> makes cdrskin start burning after the given number of bytes is read rather than waiting for the FIFO to be completely full or the data stream to end. It risks a few drive buffer underruns at the beginning of burn - but modern drives stand this. Usage examples: {{{ cdrskin ... fs=128m fifo_start_at=20m ... cdrskin ... fifo_start_at=0 ... }}} Note: no FIFO can give you better average throughput than the average throughput of the data source and the throughput of the burner. It can be used, though, to bring the effective throughput very close to the theoretical limit. Especially with high speed media. -------------------------------------------------------------------------- --no_rc allows you to surely ban influence from systemwide or user specific default settings of cdrskin. Possible locations for such settings: /etc/default/cdrskin /etc/opt/cdrskin/rc /etc/cdrskin/cdrskin.conf $HOME/.cdrskinrc -------------------------------------------------------------------------- dev_translation=<sep><from><sep><to> may be needed to foist cdrskin to frontend programs of cdrecord which do *not* ask cdrecord -scanbus but which make own assumptions and guesses about cdrecord's device addresses. Normally, cdrskin understands all addresses which are suitable for cdrecord under Linux. See cdrskin/README, "Pseudo-SCSI Adresses". This option is mainly for (yet unknown) exotic configurations or very stubborn frontend programs. If a frontend refuses to work with cdrskin, look into the error protocol of that frontend, look at the output of a run of cdrskin --devices and give cdrskin the necessary hint. Example: Your frontend insists in using "0,0,0" and --devices reported dev='/dev/hdc' resp. cdrskin dev=ATA -scanbus reported "1,0,0" then this would be the appropriate translation: {{{ dev_translation=+0,0,0+/dev/hdc }}} The "+" character is a separator to be chosen by you. Currently i am not aware of the need to choose any other than "+" unless you get playful with custom translations like {{{ dev_translation=-"cd+dvd"-1,0,0 }}} See http://scdbackup.sourceforge.net/k3b_on_cdrskin.html for an illustrated example with K3b 0.10 . -------------------------------------------------------------------------- Advanced multi-session use cases as of dvd+rw-tools: A special feature of dvd+rw-tools is growing of ISO-9660 filesystems on overwriteable media. This is not the same as multi-session writing of cdrskin with CD media, but retrieves additional information from the existing ISO image and finally manipulates the start sectors of this existing image. So, inspired by growisofs, cdrskin can offer DVD multi-session not only with sequential DVD-R[W] and with DVD+R [DL], but also with DVD-RAM, DVD+RW, BD-RE and even regular disk files or block devices other than CD/DVD writers. This is enabled by option --grow_overwriteable_iso. The libburnia project provides an integrated ISO-9660 multi-session tool named [wiki:Xorriso xorriso] which tries to go one step beyond growisofs. It uses [wiki:Libburn libburn] , [wiki:Libisofs libisofs] and [wiki:Libisoburn libisoburn]. See [http://scdbackup.sourceforge.net/man_1_xorriso.html man xorriso]. -------------------------------------------------------------------------- AC_INIT([libburn], [1.5.7], [http://libburnia-project.org]) AC_PREREQ([2.50]) dnl AC_CONFIG_HEADER([config.h]) AC_CANONICAL_HOST AC_CANONICAL_TARGET LIBBURNIA_SET_FLAGS AM_INIT_AUTOMAKE([subdir-objects]) AC_CONFIG_MACRO_DIR([./]) dnl Notes about version numbers and .so numbers: dnl dnl Regrettably the meaning of the various version types was misunderstood dnl before version 0.4.1. dnl dnl In the past MAJOR.MINOR.MICRO versions led to the following SONAME numbers: dnl 0.2.2 = 2 , 0.2.3 = 3 , 0.2.6 = 6 dnl 0.3.0 = 0 , 0.3.2 = 2 , 0.3.4 = 4 . 0.3.6 = 6 , 0.3.8 = 4 dnl 0.4.0 = 0 (also released as SONAME 4) dnl dnl Meanwhile the following schemes are maintained in parallel: dnl dnl BURN_MAJOR_VERSION , BURN_MINOR_VERSION , BURN_MICRO_VERSION dnl are three small non-negative integers which describe the evolution dnl steps of the library. dnl Older applications are able to use younger libraries over dnl quite a long range of such steps. Some day, nevertheless, dnl compatibility might get terminated, after due notice. dnl dnl SONAME (libburn.so.4) dnl is a small positive integer which marks a family of compatible dnl evolution steps. Libraries with a particular SONAME allow a binary dnl with the same SONAME to start up. Any further compatibility check is to dnl be done by own runtime means. Especially *_version() calls in the API dnl which return BURN_MAJOR_VERSION, BURN_MINOR_VERSION, BURN_MICRO_VERSION. dnl See below. dnl dnl CURRENT, AGE, REVISION dnl are three integers used by libtool. CURRENT is positive, the others dnl non-negative. The use at runtime is not known yet. But libtool computes dnl at build time SONAME = CURRENT - AGE. dnl So this is a superspace of the SONAME version space. To avoid dnl ill SONAME, the value of CURRENT must be larger than AGE. dnl See also http://www.gnu.org/software/libtool/manual.html#Interfaces dnl dnl On Linux the name of the dynamic library will be dnl libburn.so.$SONAME.$AGE.$REV dnl In the terminology of this file: dnl CURRENT = LT_CURRENT dnl AGE = LT_AGE dnl REVISION= LT_REVISION dnl dnl Beginning with libburn-0.4.1 a rectified counting was introduced as dnl CURRENT=10, REVISION=1, AGE=6 dnl This rectification declared that version to be binary compatible up dnl from libburn-0.3.4. dnl Real compatibility was given since libburn-0.3.2. dnl Beware of libburn-0.2.6 which had SONAME=6 and is not binary compatible. dnl Applications for libburn-0.2 to libburn-0.3.1 need recompilation but no dnl source code changes. dnl dnl Neatly versioned stable releases meanwhile: dnl 0.4.2 = libburn.so.4.7.0 dnl 0.4.4 = libburn.so.4.9.0 dnl 0.4.6 = libburn.so.4.11.0 dnl 0.4.8 = libburn.so.4.13.0 dnl 0.5.0 = libburn.so.4.15.0 dnl 0.5.2 = libburn.so.4.17.0 dnl 0.5.4 = libburn.so.4.19.0 dnl 0.5.6 = libburn.so.4.21.0 dnl 0.5.8 = libburn.so.4.23.0 dnl 0.6.0 = libburn.so.4.25.0 dnl 0.6.2 = libburn.so.4.27.0 dnl 0.6.4 = libburn.so.4.29.0 dnl 0.6.6 = libburn.so.4.31.0 dnl 0.6.8 = libburn.so.4.33.0 dnl 0.7.0 = libburn.so.4.35.0 dnl 0.7.2 = libburn.so.4.37.0 dnl 0.7.4 = libburn.so.4.39.0 dnl 0.7.6 = libburn.so.4.41.0 dnl 0.7.8 = libburn.so.4.43.0 dnl 0.8.0 = libburn.so.4.45.0 dnl 0.8.2 = libburn.so.4.47.0 dnl 0.8.4 = libburn.so.4.49.0 dnl 0.8.6 = libburn.so.4.51.0 dnl 0.8.8 = libburn.so.4.53.0 dnl 0.9.0 = libburn.so.4.55.0 dnl 1.0.0 = libburn.so.4.57.0 dnl 1.0.2 = libburn.so.4.59.0 dnl 1.0.4 = libburn.so.4.61.0 dnl 1.0.6 = libburn.so.4.63.0 dnl 1.1.0 = libburn.so.4.65.0 dnl 1.1.4 = libburn.so.4.67.0 dnl 1.1.6 = libburn.so.4.69.0 dnl 1.1.8 = libburn.so.4.71.0 dnl 1.2.0 = libburn.so.4.73.0 dnl 1.2.2 = libburn.so.4.75.0 dnl 1.2.4 = libburn.so.4.77.0 dnl 1.2.6 = libburn.so.4.79.0 dnl 1.2.8 = libburn.so.4.81.0 dnl 1.3.0 = libburn.so.4.83.0 dnl 1.3.2 = libburn.so.4.85.0 dnl 1.3.4 = libburn.so.4.87.0 dnl 1.3.6 = libburn.so.4.89.0 dnl 1.3.8 = libburn.so.4.91.0 dnl 1.4.0 = libburn.so.4.93.0 dnl 1.4.2 = libburn.so.4.95.0 dnl 1.4.4 = libburn.so.4.97.0 dnl 1.4.6 = libburn.so.4.99.0 dnl 1.4.8 = libburn.so.4.101.0 dnl 1.5.0 = libburn.so.4.103.0 dnl 1.5.2 = libburn.so.4.105.0 dnl 1.5.4 = libburn.so.4.107.0 dnl 1.5.6 = libburn.so.4.109.0 dnl dnl So LT_CURRENT, LT_REVISION and LT_AGE get set directly here. dnl SONAME of the emerging library is LT_CURRENT - LT_AGE. dnl The linker will do no finer checks. Especially no age range check for dnl the application binary. If SONAME matches, then the couple starts. dnl dnl Therefore at run time info is provided by libburn function burn_version(). dnl It returns the major, minor and micro revision of the library. dnl Before using any API feature, a program should check for age. dnl dnl The variables BURN_*_VERSION are mere copies for informing libtool. dnl The true values which get issued and should be compared are macros dnl defined in libburn/libburn.h . dnl dnl Normally one can allow a program to run with a library which passed the dnl linker SONAME test and which is not older than the library it was dnl developed for. Library2 is younger than library1 if: dnl major2>major1 || (major2==major1 && dnl (minor2>minor1 || (minor2==minor1 && micro2 > micro1))) dnl dnl If BURN_*_VERSION changes, be sure to change AC_INIT above to match. dnl dnl As said: Only copies. Original in libburn/libburn.h : burn_header_version_* BURN_MAJOR_VERSION=1 BURN_MINOR_VERSION=5 BURN_MICRO_VERSION=7 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_VERSION) dnl Libtool versioning LT_RELEASE=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION.$BURN_MICRO_VERSION dnl dnl ### This is the release version libburn-1.5.6 dnl This is the development version after above release version dnl ### LT_CURRENT++, LT_AGE++ has not yet happened. dnl LT_CURRENT++, LT_AGE++ has happened meanwhile. dnl dnl SONAME = 114 - 110 = 4 . Linux library name = libburn.so.4.110.0 LT_CURRENT=114 LT_AGE=110 LT_REVISION=0 LT_CURRENT_MINUS_AGE=`expr $LT_CURRENT - $LT_AGE` AC_SUBST(LT_RELEASE) AC_SUBST(LT_CURRENT) AC_SUBST(LT_REVISION) AC_SUBST(LT_AGE) AC_SUBST(LT_CURRENT_MINUS_AGE) dnl ts A71207: This is done only not to break any old components BURN_INTERFACE_AGE=$LT_REVISION BURN_BINARY_AGE=`expr $LT_AGE + $BURN_INTERFACE_AGE` AC_SUBST(BURN_INTERFACE_AGE) AC_SUBST(BURN_BINARY_AGE) AC_PREFIX_DEFAULT([/usr/local]) test "$prefix" = "NONE" && prefix=$ac_default_prefix dnl ts B90405 : Disabled on advise of Ross Burton dnl AM_MAINTAINER_MODE AM_PROG_CC_C_O AC_C_CONST AC_C_INLINE dnl Large file support AC_SYS_LARGEFILE AC_FUNC_FSEEKO AC_CHECK_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 AC_SUBST(ARCH) AC_SUBST(LIBBURNIA_PKGCONFDIR) AC_SUBST(LIBBURN_ARCH_LIBS) dnl ts A90303 dnl Check the preconditions for using statvfs() in sg-dummy dnl (sg-linux and sg-freebsd use statvfs() unconditionally) STATVFS_DEF=-DLibburn_os_has_statvfS AC_CHECK_HEADER(sys/statvfs.h, X=, STATVFS_DEF=) AC_CHECK_FUNC([statvfs], X=, STATVFS_DEF=) dnl If this would be done more specifically in Makefile.am dnl via libburn_libburn_la_CFLAGS then undesired .o file names would emerge CFLAGS="$STATVFS_DEF $CFLAGS" dnl ts A91122 AC_ARG_ENABLE(track-src-odirect, [ --enable-track-src-odirect Banned for now: (Enable use of O_DIRECT with track input, default=no)], , enable_track_src_odirect=no) if test x$enable_track_src_odirect = xyes; then # LIBBURN_O_DIRECT_DEF="-DLibburn_read_o_direcT" # echo "enabled use of O_DIRECT with track input" echo "REFUSED to enable use of O_DIRECT with track input because of cdrskin multi-track bug" else LIBBURN_O_DIRECT_DEF= echo "disabled use of O_DIRECT with track input" fi dnl Avoid the need for libburn_libburn_la_CFLAGS in Makefile.am (ugly .o names) dnl ### AC_SUBST(LIBBURN_O_DIRECT_DEF) CFLAGS="$LIBBURN_O_DIRECT_DEF $CFLAGS" dnl ts A91116 AC_ARG_ENABLE(dvd-obs-64k, [ --enable-dvd-obs-64k 64 KB default size for DVD writing, default=no], , enable_dvd_obs_64k=no) if test x$enable_dvd_obs_64k = xyes; then LIBBURN_DVD_OBS_64K="-DLibburn_dvd_obs_default_64K" echo "enabled write size default 64 KB on DVD" else LIBBURN_DVD_OBS_64K= echo "disabled write size default 64 KB on DVD" fi CFLAGS="$LIBBURN_DVD_OBS_64K $CFLAGS" dnl ts B20413 AC_ARG_ENABLE(dvd-obs-pad, [ --enable-dvd-obs-pad pad DVD DAO sessions to 32 or 64 KB, default=no], , enable_dvd_obs_pad=no) if test x$enable_dvd_obs_pad = xyes; then LIBBURN_DVD_OBS_PAD="-DLibburn_dvd_always_obs_paD" echo "enabled padding of DVD DAO sessions to 32 or 64 KB" else LIBBURN_DVD_OBS_64K= echo "disabled padding of DVD DAO sessions to 32 or 64 KB" fi CFLAGS="$LIBBURN_DVD_OBS_PAD $CFLAGS" dnl ts A91218 - B21002 case $host_os in cygwin*|mingw*) default_libcdio=yes ;; *) default_libcdio=no ;; esac # Check for proper library versions if this is desired. # (It fails too often on too many systems.) AC_ARG_ENABLE(pkg-check-modules, [ --enable-pkg-check-modules Enable pkg-config check for libcdio , default=no], , enable_pkg_check_modules=no) AC_ARG_ENABLE(libcdio, [ --enable-libcdio Enable use of libcdio as system adapter, default=no (except on MSWindows)], , enable_libcdio=$default_libcdio) PKG_PROG_PKG_CONFIG if test x$enable_libcdio = xyes; then dnl Check whether there is libcdio-devel and libcdio-runtime. dnl If not, erase this macro LIBCDIO_DEF="-DLibburn_use_libcdiO" dnl The empty yes case obviously causes -lcdio to be linked AC_CHECK_HEADER(cdio/cdio.h, AC_CHECK_LIB(cdio, mmc_last_cmd_sense, , LIBCDIO_DEF= ), LIBCDIO_DEF= ) else LIBCDIO_DEF= fi if test x$LIBCDIO_DEF = x then if test x$enable_libcdio = xyes then echo "WARNING: could not enable use of libcdio as system adapter" fi else echo "enabled use of libcdio as system adapter" CFLAGS="$LIBCDIO_DEF $CFLAGS" if test x$enable_pkg_check_modules = xyes; then LIBCDIO_REQUIRED=0.83 PKG_CHECK_MODULES(LIBCDIO, libcdio >= $LIBCDIO_REQUIRED) else echo "checking for LIBCDIO... skipped, no --enable-pkg-check-modules" fi fi dnl ts B70127 # There are Linuxes with no public generic SCSI interface LIBBURNIA_CHECK_LINUX_SCSI dnl ts B00704 # Library versioning normally serves a complex purpose. # Since libburn obeys strict ABI backward compatibility, it needs only the # simple feature to declare function names "global:" or "local:". Only the # global ones are visible to applications at library load time. AC_ARG_ENABLE(versioned-libs, [ --enable-versioned-libs Enable strict symbol encapsulation , default=yes], , enable_versioned_libs=yes) if test x$enable_versioned_libs = xyes; then vers_libs_test=no LIBBURN_ASSERT_VERS_LIBS if test x$vers_libs_test = xno then echo "disabled strict symbol encapsulation (test failed)" else echo "enabled strict symbol encapsulation" fi else echo "disabled strict symbol encapsulation" fi # Check for system dependent mandatory libraries (LIBBURN_ARCH_LIBS) LIBBURNIA_CHECK_ARCH_LIBS(mandatory) AC_ARG_ENABLE(ldconfig-at-install, [ --enable-ldconfig-at-install On GNU/Linux run ldconfig, default=yes], , ldconfig_at_install=yes) if test x$ldconfig_at_install = xyes; then dummy=dummy else LIBBURNIA_LDCONFIG_CMD="echo 'NOTE: ldconfig is disabled. If needed, configure manually for:'" echo "disabled run of ldconfig during installation on GNU/Linux" fi AC_SUBST(LIBBURNIA_LDCONFIG_CMD) 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="-O3 $CFLAGS" CFLAGS="-fexpensive-optimizations $CFLAGS" fi CFLAGS="-DNDEBUG $CFLAGS" else if test x$GCC = xyes; then CFLAGS="-g -pedantic -Wall -Wextra -Wno-unused-parameter -Wno-char-subscripts $CFLAGS" fi CFLAGS="-DDEBUG $CFLAGS" fi dnl Determine target directory for libburn-*.pc dnl Important: Must be performed _after_ TARGET_SHIZZLE dnl LIBBURNIA_SET_PKGCONFIG AC_CONFIG_FILES([ Makefile doc/doxygen.conf version.h libburn-1.pc ]) AC_OUTPUT all clean: $(MAKE) -C .. -$(MAKEFLAGS) $@ .PHONY: all clean Description of CD-TEXT Guided by Leon Merten Lohse via libcdio-devel@gnu.org by reading mmc3r10g.pdf from http://www.t10.org/ftp/t10/drafts/mmc3/ by docs and results of cdtext.zip from http://www.sonydadc.com/file/ by reading http://digitalx.org/cue-sheet/syntax by reading source of libcdio from http://www.gnu.org/s/libcdio which quotes source of cdrecord from ftp://ftp.berlios.de/pub/cdrecord/alpha by reading cdrecord.1 from ftp://ftp.berlios.de/pub/cdrecord/alpha Language codes were learned from http://tech.ebu.ch/docs/tech/tech3264.pdf Genre codes were learned from libcdio and confirmed by http://helpdesk.audiofile-engineering.com/index.php?pg=kb.page&id=123 For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> Content: - CD-TEXT from the view of the user - Content specifications of particular pack types - Format of a CD-TEXT packs array - Overview of libburn API calls for CD-TEXT - Sony Text File Format (Input Sheet Version 0.7T) - CDRWIN cue sheet files ------------------------------------------------------------------------------- CD-TEXT from the view of the user: CD-TEXT records attributes of disc and tracks on audio CD. The attributes are grouped into blocks which represent particular languages. Up to 8 blocks are possible. There are 13 defined attribute categories, which are called Pack Types and are identified by a single-byte code: 0x80 = Title 0x81 = Names of Performers 0x82 = Names of Songwriters 0x83 = Names of Composers 0x84 = Names of Arrangers 0x85 = Messages 0x86 = text-and-binary: Disc Identification 0x87 = text-and-binary: Genre Identification 0x88 = binary: Table of Content information 0x89 = binary: Second Table of Content information (0x8a to 0x8c are reserved.) 0x8d = Closed Information 0x8e = UPC/EAN code of the album and ISRC code of each track 0x8f = binary: Size Information of the Block Some of these categories apply to the whole disc only: 0x86, 0x87, 0x88, 0x89, 0x8d Some have to be additionally attributed to each track, if they are present for the whole disc: 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x8e One describes the overall content of a block and in part of all other blocks: 0x8f The total size of a block's attribute set is restricted by the fact that it has to be stored in at most 253 records with 12 bytes of payload. These records are called Text Packs. A shortcut for repeated identical track texts is provided, so that a text that is identical to the one of the previous track occupies only 2 or 4 bytes. ------------------------------------------------------------------------------- Content specification of particular pack types: Pack types 0x80 to 0x85 and 0x8e contain 0-terminated cleartext. If double byte characters are used, then two 0-bytes terminate the cleartext. The meaning of 0x80 to 0x85 should be clear by above list. They are encoded according to the Character Code of their block. Either as ISO-8859-1 single byte characters, or as 7-bit ASCII single byte characters, or as MS-JIS double byte characters. More info to 0x8e is given below. Pack type 0x86 (Disc Identification) is documented by Sony as "Catalog Number: (use ASCII Code) Catalog Number of the album". So it is not really binary but might be non-printable, and should contain only bytes with bit7 = 0. Pack type 0x87 contains 2 binary bytes, followed by 0-terminated cleartext. The two binary bytes form a big-endian index to the following list. 0x0000 = "Not Used" (Sony prescribes to use this if no genre applies) 0x0001 = "Not Defined" 0x0002 = "Adult Contemporary" 0x0003 = "Alternative Rock" 0x0004 = "Childrens Music" 0x0005 = "Classical" 0x0006 = "Contemporary Christian" 0x0007 = "Country" 0x0008 = "Dance" 0x0009 = "Easy Listening" 0x000a = "Erotic" 0x000b = "Folk" 0x000c = "Gospel" 0x000d = "Hip Hop" 0x000e = "Jazz" 0x000f = "Latin" 0x0010 = "Musical" 0x0011 = "New Age" 0x0012 = "Opera" 0x0013 = "Operetta" 0x0014 = "Pop Music" 0x0015 = "Rap" 0x0016 = "Reggae" 0x0017 = "Rock Music" 0x0018 = "Rhythm & Blues" 0x0019 = "Sound Effects" 0x001a = "Spoken Word" 0x001b = "World Music" Sony documents the cleartext part as "Genre information that would supplement the Genre Code, such as 'USA Rock music in the 60s'". Always ASCII encoded. Pack type 0x88 records information from the CD's Table of Content, as of READ PMA/TOC/ATIP Format 0010b (mmc3r10g.pdf, table 237 TOC Track Descriptor Format, Q Sub-channel). See below, Format of a CD-TEXT packs array, for more details about the content of pack type 0x88. Pack type 0x89 is yet quite unclear. It might be a representation of Playback Skip Interval, Mode-5 Q sub-channel, POINT 01 to 40 (mmc3r10g.pdf 4.2.3.6.3). If so, then this seems not to apply to write type SAO, because the CUE SHEET format offers no way to express Mode-5 Q. See below, Format of a CD-TEXT packs array, for an example of this pack type. Pack type 0x8d is documented by Sony as "Closed Information: (use 8859-1 Code) Any information can be recorded on disc as memorandum. Information in this field will not be read by CD TEXT players available to the public." Always ISO-8859-1 encoded. Pack type 0x8e is documented by Sony as "UPC/EAN Code (POS Code) of the album. This field typically consists of 13 characters." Always ASCII encoded. It applies to tracks as "ISRC code [which] typically consists of 12 characters" and is always ISO-8859-1 encoded. MMC calls these information entities Media Catalog Number and ISRC. The catalog number consists of 13 decimal digits. ISRC consists of 12 characters: 2 country code [0-9A-Z], 3 owner code [0-9A-Z], 2 year digits (00 to 99), 5 serial number digits (00000 to 99999). Pack type 0x8f summarizes the whole list of text packs of a block. See below, Format of a CD-TEXT packs array, for details. ------------------------------------------------------------------------------- Format of a CD-TEXT packs array: The attributes are represented on CD as Text Packs in the sub-channel of the Lead-in of the disc. See doc/cookbook.txt for a description how to write the readily formatted CD-TEXT pack array to CD, and how to read CD-TEXT packs from CD. The format is explained in part in MMC-3 (mmc3r10g.pdf, Annex J) and in part by the documentation in Sony's cdtext.zip : Each pack consists of a 4-byte header, 12 bytes of payload, and 2 bytes of CRC. The first byte of each pack tells the pack type. See above for a list of types. The second byte tells the track number to which the first text piece in a pack is associated. Number 0 means the whole album. Higher numbers are valid for types 0x80 to 0x85, and 0x8e. With these types, there should be one text for the disc and one for each track. With types 0x88 and 0x89, the second byte bears a track number, too. With type 0x8f, the second byte counts the record parts from 0 to 2. The third byte is a sequential counter. The fourth byte is the Block Number and Character Position Indicator. It consists of three bit fields: bit7 = Double Bytes Character Code (0= single byte characters) bit4-6 = Block Number (groups text packs in language blocks) bit0-3 = Character position. Either the number of characters which the current text inherited from the previous pack, or 15 if the current text started before the previous pack. The 12 payload bytes contain pieces of 0-terminated texts or binary data. A text may span over several packs. Unused characters in a pack are used for the next text of the same pack type. If no text of the same type follows, then the remaining text bytes are set to 0. The CRC algorithm uses divisor 0x11021. The resulting 16-bit residue of the polynomial division gets inverted and written as big-endian number to bytes 16 and 17 of the pack. The text packs are grouped in up to 8 blocks of at most 256 packs. Each block is in charge for one language. Sequence numbers of each block are counted separately. All packs of block 0 come before the packs of block 1. The limitation of block number and sequence numbers imply that there are at most 2048 text packs possible. (READ TOC/PMA/ATIP could retrieve 3640 packs, as it is limited to 64 kB - 2.) If a text of a track (pack types 0x80 to 0x85 and 0x8e) repeats identically for the next track, then it may be represented by a TAB character (ASCII 9) for single byte texts, and two TAB characters for double byte texts. (This should be used because 256 * 12 bytes is few space for 99 tracks.) The two binary bytes of pack type 0x87 are written to the first 0x87 pack of a block. They may or may not be repeated at the start of the follow-up packs of type 0x87. The first pack of type 0x88 in a block records in its payload bytes: 0 : PMIN of POINT A1 = First Track Number 1 : PMIN of POINT A2 = Last Track Number 2 : unknown, 0 in Sony example 3 : PMIN of POINT A2 = Start position of Lead-Out 4 : PSEC of POINT A2 = Start position of Lead-Out 5 : PFRAME of POINT A2 = Start position of Lead-Out 6 to 11 : unknown, 0 in Sony example The following packs record PMIN, PSEC, PFRAME of the POINTs between the lowest track number (min 01h) and the highest track number (max 63h). The payload of the last pack is padded by 0s. The Sony .TOC example: A0 01 A1 14 A2 63:02:18 01 00:02:00 02 04:11:25 03 08:02:50 04 11:47:62 ... 13 53:24:25 14 57:03:25 yields 88 00 23 00 01 0e 00 3f 02 12 00 00 00 00 00 00 12 00 88 01 24 00 00 02 00 04 0b 19 08 02 32 0b 2f 3e 67 2d ... 88 0d 27 00 35 18 19 39 03 19 00 00 00 00 00 00 ea af Pack type 0x89 is yet quite unclear. Especially what the information shall mean to the user of the CD. The time points in the Sony example are in the time range of the tracks numbers that are given before the time points: 01 02:41:48 01 02:52:58 06 23:14:25 06 23:29:60 07 28:30:39 07 28:42:30 13 55:13:26 13 55:31:50 yields 89 01 28 00 01 04 00 00 00 00 02 29 30 02 34 3a f3 0c 89 06 29 00 02 04 00 00 00 00 17 0e 19 17 1d 3c 73 92 89 07 2a 00 03 04 00 00 00 00 1c 1e 27 1c 2a 1e 72 20 89 0d 2b 00 04 04 00 00 00 00 37 0d 1a 37 1f 32 0b 62 The track numbers are stored in the track number byte of the packs. The two time points are stored in byte 6 to 11 of the payload. Byte 0 of the payload seems to be a sequential counter. Byte 1 always 4 ? Byte 2 to 5 always 0 ? Pack type 0x8f summarizes the whole list of text packs of a block. So there is one group of three 0x8f packs per block. Nevertheless each 0x8f group tells the highest sequence number and the language code of all blocks. The payload bytes of three 0x8f packs form a 36 byte record. The track number bytes of the three packs have the values 0, 1, 2. Byte : 0 : Character code for pack types 0x80 to 0x85: 0x00 = ISO-8859-1 0x01 = 7 bit ASCII 0x80 = MS-JIS (japanese Kanji, double byte characters) 1 : Number of first track 2 : Number of last track 3 : libcdio source states: "cd-text information copyright byte" Probably 3 means "copyrighted", 0 means "not copyrighted". 4 - 19 : Pack count of the various types 0x80 to 0x8f. Byte number N tells the count of packs of type 0x80 + (N - 4). I.e. the first byte in this field of 16 counts packs of type 0x80. 20 - 27 : Highest sequence byte number of blocks 0 to 7. 28 - 36 : Language code for blocks 0 to 7 (tech3264.pdf appendix 3) Not all of these Codes have ever been seen with CD-TEXT, though. 0x00 = Unknown 0x01 = Albanian 0x02 = Breton 0x03 = Catalan 0x04 = Croatian 0x05 = Welsh 0x06 = Czech 0x07 = Danish 0x08 = German 0x09 = English 0x0a = Spanish 0x0b = Esperanto 0x0c = Estonian 0x0d = Basque 0x0e = Faroese 0x0f = French 0x10 = Frisian 0x11 = Irish 0x12 = Gaelic 0x13 = Galician 0x14 = Icelandic 0x15 = Italian 0x16 = Lappish 0x17 = Latin 0x18 = Latvian 0x19 = Luxembourgian 0x1a = Lithuanian 0x1b = Hungarian 0x1c = Maltese 0x1d = Dutch 0x1e = Norwegian 0x1f = Occitan 0x20 = Polish 0x21 = Portuguese 0x22 = Romanian 0x23 = Romansh 0x24 = Serbian 0x25 = Slovak 0x26 = Slovenian 0x27 = Finnish 0x28 = Swedish 0x29 = Turkish 0x2a = Flemish 0x2b = Wallon 0x45 = Zulu 0x46 = Vietnamese 0x47 = Uzbek 0x48 = Urdu 0x49 = Ukrainian 0x4a = Thai 0x4b = Telugu 0x4c = Tatar 0x4d = Tamil 0x4e = Tadzhik 0x4f = Swahili 0x50 = Sranan Tongo 0x51 = Somali 0x52 = Sinhalese 0x53 = Shona 0x54 = Serbo-croat 0x55 = Ruthenian 0x56 = Russian 0x57 = Quechua 0x58 = Pushtu 0x59 = Punjabi 0x5a = Persian 0x5b = Papamiento 0x5c = Oriya 0x5d = Nepali 0x5e = Ndebele 0x5f = Marathi 0x60 = Moldavian 0x61 = Malaysian 0x62 = Malagasay 0x63 = Macedonian 0x64 = Laotian 0x65 = Korean 0x66 = Khmer 0x67 = Kazakh 0x68 = Kannada 0x69 = Japanese 0x6a = Indonesian 0x6b = Hindi 0x6c = Hebrew 0x6d = Hausa 0x6e = Gurani 0x6f = Gujurati 0x70 = Greek 0x71 = Georgian 0x72 = Fulani 0x73 = Dari 0x74 = Churash 0x75 = Chinese 0x76 = Burmese 0x77 = Bulgarian 0x78 = Bengali 0x79 = Bielorussian 0x7a = Bambora 0x7b = Azerbaijani 0x7c = Assamese 0x7d = Armenian 0x7e = Arabic 0x7f = Amharic E.g. these three packs 42 : 8f 00 2a 00 01 01 03 00 06 05 04 05 07 06 01 02 48 65 43 : 8f 01 2b 00 00 00 00 00 00 00 06 03 2c 00 00 00 c0 20 44 : 8f 02 2c 00 00 00 00 00 09 00 00 00 00 00 00 00 11 45 decode to Byte :Value Meaning 0 : 01 = ASCII 7-bit 1 : 01 = first track is 1 2 : 03 = last track is 3 3 : 00 = copyright (0 = public domain, 3 = copyrighted ?) 4 : 06 = 6 packs of type 0x80 5 : 05 = 5 packs of type 0x81 6 : 04 = 4 packs of type 0x82 7 : 05 = 5 packs of type 0x83 8 : 07 = 7 packs of type 0x84 9 : 06 = 6 packs of type 0x85 10 : 01 = 1 pack of type 0x86 11 : 02 = 2 packs of type 0x87 12 : 00 = 0 packs of type 0x88 13 : 00 = 0 packs of type 0x89 14 : 00 00 00 00 = 0 packs of types 0x8a to 0x8d 18 : 06 = 6 packs of type 0x8e 19 : 03 = 3 packs of type 0x8f 20 : 2c = last sequence for block 0 This matches the sequence number of the last text pack (0x2c = 44) 21 : 00 00 00 00 00 00 00 = last sequence numbers for block 1..7 (none) 28 : 09 = language code for block 0: English 29 : 00 00 00 00 00 00 00 = language codes for block 1..7 (none) ------------------------------------------------------------------------------- Overview of libburn API calls for CD-TEXT (see libburn/libburn.h for details): libburn can retrieve the array of text packs from a CD: int burn_disc_get_leadin_text(struct burn_drive *d, unsigned char **text_packs, int *num_packs, int flag); It can write a text pack set with a CD SAO session. This set may be attached as array of readily formatted text packs by: int burn_write_opts_set_leadin_text(struct burn_write_opts *opts, unsigned char *text_packs, int num_packs, int flag); The array of text packs may be read from a file by int burn_cdtext_from_packfile(char *path, unsigned char **text_packs, int *num_packs, int flag); Alternatively the pack set may be defined by attaching CD-TEXT attributes to burn_session and burn_track: int burn_session_set_cdtext_par(struct burn_session *s, int char_codes[8], int copyrights[8], int languages[8], int flag); int burn_session_set_cdtext(struct burn_session *s, int block, int pack_type, char *pack_type_name, unsigned char *payload, int length, int flag); int burn_track_set_cdtext(struct burn_track *t, int block, int pack_type, char *pack_type_name, unsigned char *payload, int length, int flag); Macros list the texts for genre and language codes: BURN_CDTEXT_LANGUAGES_0X00 BURN_CDTEXT_FILLER BURN_CDTEXT_LANGUAGES_0X45 BURN_CDTEXT_GENRE_LIST BURN_CDTEXT_NUM_GENRES There is a reader for Sony Input Sheet Version 0.7T: int burn_session_input_sheet_v07t(struct burn_session *session, char *path, int block, int flag); and a writer which converts an array of text packs to such a Sony Input Sheet: int burn_make_input_sheet_v07t(unsigned char *text_packs, int num_packs, int start_tno, int track_count, char **result, int *char_code, int flag); CD-TEXT can be read from a CDRWIN cue sheet file which defines the tracks of a session int burn_session_by_cue_file(struct burn_session *session, char *path, int fifo_size, struct burn_source **fifo, unsigned char **text_packs, int *num_packs, int flag); The session and track attributes can then be converted into an array of text packs by: int burn_cdtext_from_session(struct burn_session *s, unsigned char **text_packs, int *num_packs, int flag); or they can be written as array of text packs to CD when burning begins and no array of pre-formatted packs was attached to the write options by burn_write_opts_set_leadin_text(). There are calls for inspecting the attached attributes: int burn_session_get_cdtext_par(struct burn_session *s, int char_codes[8], int copyrights[8], int block_languages[8], int flag); int burn_session_get_cdtext(struct burn_session *s, int block, int pack_type, char *pack_type_name, unsigned char **payload, int *length, int flag); int burn_track_get_cdtext(struct burn_track *t, int block, int pack_type, char *pack_type_name, unsigned char **payload, int *length, int flag); and for removing attached attributes: int burn_session_dispose_cdtext(struct burn_session *s, int block); int burn_track_dispose_cdtext(struct burn_track *t, int block); UPC/EAN and ISRC not only affect CD-TEXT but also information that is written along with the tracks in Q sub-channel. These can be influenced by burn_session_input_sheet_v07t(), burn_session_by_cue_file() and by void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, unsigned char mediacatalog[13]); void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, int has_mediacatalog); void burn_track_set_isrc(struct burn_track *t, char *country, char *owner, unsigned char year, unsigned int serial); int burn_track_set_isrc_string(struct burn_track *t, char isrc[13], int flag); ------------------------------------------------------------------------------- Sony Text File Format (Input Sheet Version 0.7T): This text file format provides comprehensive means to define the text attributes of session and tracks for a single block. More than one such file has to be read to form an attribute set with multiple blocks. The information is given by text lines of the following form: purpose specifier [whitespace] = [whitespace] content text [whitespace] is zero or more ASCII 32 (space) or ASCII 9 (tab) characters. The purpose specifier tells the meaning of the content text. Empty content text does not cause a CD-TEXT attribute to be attached. The following purpose specifiers apply to the session as a whole: Specifier = Meaning ------------------------------------------------------------------------- Text Code = Character code for pack type 0x8f "ASCII", "8859" Language Code = One of the language names for pack type 0x8f Album Title = Content of pack type 0x80 Artist Name = Content of pack type 0x81 Songwriter = Content of pack type 0x82 Composer = Content of pack type 0x83 Arranger = Content of pack type 0x84 Album Message = Content of pack type 0x85 Catalog Number = Content of pack type 0x86 Genre Code = One of the genre names for pack type 0x87 Genre Information = Cleartext part of pack type 0x87 Closed Information = Content of pack type 0x8d UPC / EAN = Content of pack type 0x8e Text Data Copy Protection = Copyright value for pack type 0x8f "ON" = 0x03, "OFF" = 0x00 First Track Number = The lowest track number used in the file Last Track Number = The highest track number used in the file The following purpose specifiers apply to particular tracks: Track NN Title = Content of pack type 0x80 Track NN Artist = Content of pack type 0x81 Track NN Songwriter = Content of pack type 0x82 Track NN Composer = Content of pack type 0x83 Track NN Arranger = Content of pack type 0x84 Track NN Message = Content of pack type 0x85 ISRC NN = Content of pack type 0x8e The following purpose specifiers have no effect on CD-TEXT: Remarks = Comments with no influence on CD-TEXT Disc Information NN = Supplementary information for use by record companies. ISO-8859-1 encoded. NN ranges from 01 to 04. Input Sheet Version = "0.7T" libburn peculiarties: libburn may read files of the described format by burn_session_input_sheet_v07t() after the burn_session has been establiched and all burn_track objects have been added. It can convert an array of CD-TEXT packs into this format by burn_make_input_sheet_v07t() The following purpose specifiers accept byte values of the form 0xXY. Text Code , Language Code , Genre Code , Text Data Copy Protection E.g. to indicate MS-JIS character code (of which the exact name is unknown): Text Code = 0x80 Genre Code is settable by 0xXY or 0xXYZT or 0xXY 0xZT. Genre Code = 0x001b Purpose specifiers which have the meaning "Content of pack type 0xXY" may be replaced by the pack type codes. E.g.: 0x80 = Session content of pack type 0x80 Track 02 0x80 = Track content of pack type 0x80 for track 2. Applicable are pack types 0x80 to 0x86, 0x8d, 0x8e. Text Code may be specified only once. It gets speficied to "ISO-8850-1" automatically as soon as content is defined which depends on the text encoding of the block. I.e with pack types 0x80 to 0x85. If a track attribute is set, but the corresponding session attribute is not defined or defined with empty text, then the session attribute gets attached as empty test. (Normally empty content is ignored.) Example cdrskin run with three tracks: $ cdrskin dev=/dev/sr0 -v input_sheet_v07t=NIGHTCATS.TXT \ -audio -swab track_source_1 track_source_2 track_source_3 ---------------------------------------------------------- Content of file NIGHTCATS.TXT : ---------------------------------------------------------- Input Sheet Version = 0.7T Text Code = 8859 Language Code = English Album Title = Joyful Nights Artist Name = United Cat Orchestra Songwriter = Various Songwriters Composer = Various Composers Arranger = Tom Cat Album Message = For all our fans Catalog Number = 1234567890 Genre Code = Classical Genre Information = Feline classic music Closed Information = This is not to be shown by CD players UPC / EAN = 1234567890123 Text Data Copy Protection = OFF First Track Number = 1 Last Track Number = 3 Track 01 Title = Song of Joy Track 01 Artist = Felix and The Purrs Track 01 Songwriter = Friedrich Schiller Track 01 Composer = Ludwig van Beethoven Track 01 Arranger = Tom Cat Track 01 Message = Fritz and Louie once were punks ISRC 01 = XYBLG1101234 Track 02 Title = Humpty Dumpty Track 02 Artist = Catwalk Beauties Track 02 Songwriter = Mother Goose Track 02 Composer = unknown Track 02 Arranger = Tom Cat Track 02 Message = Pluck the goose ISRC 02 = XYBLG1100005 Track 03 Title = Mee Owwww Track 03 Artist = Mia Kitten Track 03 Songwriter = Mia Kitten Track 03 Composer = Mia Kitten Track 03 Arranger = Mia Kitten Track 03 Message = ISRC 03 = XYBLG1100006 ---------------------------------------------------------- ------------------------------------------------------------------------------- CDRWIN cue sheet files: A CDRWIN cue sheet file defines the track data source (FILE), various text attributes (CATALOG, TITLE, PERFORMER, SONGWRITER, ISRC), track block types (TRACK), track start addresses (INDEX). The rules for CDRWIN cue sheet files are described at http://digitalx.org/cue-sheet/syntax/ There are three more text attributes mentioned in man cdrecord for defining the corresponding CD-TEXT attributes: ARRANGER, COMPOSER, MESSAGE. -------------------------------------------------------- Example of a CDRWIN cue sheet file named NIGHTCATS.CUE : -------------------------------------------------------- CATALOG 1234567890123 FILE "audiodata.bin" BINARY TITLE "Joyful Nights" TRACK 01 AUDIO FLAGS DCP TITLE "Song of Joy" PERFORMER "Felix and The Purrs" SONGWRITER "Friedrich Schiller" ISRC XYBLG1101234 INDEX 01 00:00:00 TRACK 02 AUDIO FLAGS DCP TITLE "Humpty Dumpty" PERFORMER "Catwalk Beauties" SONGWRITER "Mother Goose" ISRC XYBLG1100005 INDEX 01 08:20:12 TRACK 03 AUDIO FLAGS DCP TITLE "Mee Owwww" PERFORMER "Mia Kitten" SONGWRITER "Mia Kitten" ISRC XYBLG1100006 INDEX 01 13:20:33 By $ cdrskin -v dev=/dev/sr0 -text cuefile=NIGHTCATS.CUE this yields as text packs: 0 : 80 00 00 00 J o y f u l N i g h t f0 f7 1 : 80 00 01 0c s 00 S o n g o f J o 43 1c 2 : 80 01 02 0a y 00 H u m p t y D u m 43 f9 3 : 80 02 03 0a p t y 00 M e e O w w w 24 72 4 : 80 03 04 08 w 00 00 00 00 00 00 00 00 00 00 00 6e af 5 : 81 00 05 00 00 F e l i x a n d T 4d 51 6 : 81 01 06 0b h e P u r r s 00 C a t a7 40 7 : 81 02 07 03 w a l k B e a u t i e 59 80 8 : 81 02 08 0f s 00 M i a K i t t e n 30 c9 9 : 81 03 09 0a 00 00 00 00 00 00 00 00 00 00 00 00 ad 19 10 : 82 00 0a 00 00 F r i e d r i c h S 70 8f 11 : 82 01 0b 0b c h i l l e r 00 M o t h 33 43 12 : 82 02 0c 04 e r G o o s e 00 M i a d6 f5 13 : 82 03 0d 03 K i t t e n 00 00 00 00 00 f5 83 14 : 8e 00 0e 00 1 2 3 4 5 6 7 8 9 0 1 2 92 3e 15 : 8e 00 0f 0c 3 00 X Y B L G 1 1 0 1 2 c0 2b 16 : 8e 01 10 0a 3 4 00 X Y B L G 1 1 0 0 bb b3 17 : 8e 02 11 09 0 0 5 00 X Y B L G 1 1 0 f3 bf 18 : 8e 03 12 08 0 0 0 6 00 00 00 00 00 00 00 00 5b 5c 19 : 8f 00 13 00 00 01 03 00 05 05 04 00 00 00 00 00 9b fe 20 : 8f 01 14 00 00 00 00 00 00 00 05 03 15 00 00 00 11 0b 21 : 8f 02 15 00 00 00 00 00 09 00 00 00 00 00 00 00 da 77 -------------------------------------- Some restrictions apply in the libburn call burn_session_by_cue_file(): Only FILE types BINARY, MOTOROLA, WAVE are allowed. Only TRACK datatypes AUDIO, MODE1/2048 are allowed. They may not be mixed in the same session. On the other hand, ARRANGER, COMPOSER, MESSAGE are supported unconditionally. ------------------------------------------------------------------------------- This text is copyright 2011 - 2012 Thomas Schmitt <scdbackup@gmx.net>. Permission is granted to copy, modify, and distribute it, as long as the references to the original information sources are maintained. There is NO WARRANTY, to the extent permitted by law. ------------------------------------------------------------------------------- /** @author Mario Danic, Thomas Schmitt @mainpage Libburn Documentation Index @section intro Introduction Libburnia is an open-source project for reading, mastering and writing optical discs. This page is about its capability to handle optical media. For now this means CD-R, CD-RW, DVD-RAM, DVD+RW, DVD+R, DVD+R/DL, DVD-RW, DVD-R, DVD-R/DL, BD-R, BD-RE. Our scope is currently Linux 2.4 and 2.6, FreeBSD, OpenSolaris, or NetBSD. For ports to other systems we would need : login on a development machine or an OS that is installable on an AMD 64-bit PC, advise from a system person about the equivalent of Linux sg or FreeBSD CAM, volunteers for testing of realistic use cases. libburn is the library by which preformatted data get onto optical media. Its code is independent of cdrecord. Its DVD capabilities are learned from studying the code of dvd+rw-tools and MMC-5 specs. No code but only the pure SCSI knowledge has been taken from dvd+rw-tools, though. cdrskin is a limited cdrecord compatibility wrapper for libburn. cdrecord is a powerful GPL'ed burn program included in Joerg Schilling's cdrtools. cdrskin strives to be a second source for the services traditionally provided by cdrecord. Additionally it provides libburn's DVD/BD capabilities, where only -sao is compatible with cdrecord. cdrskin does not contain any bytes copied from cdrecord's sources. Many bytes have been copied from the message output of cdrecord runs, though. See cdrskin/README for more. The burn API example of libburn is named test/libburner.c . The API for media information inquiry is demonstrated in test/telltoc.c . Explore these examples if you look for inspiration. SONAME: libburn.so.4 (since 0.3.4, March 2007), @section using Using libburn Our build system is based on autotools. User experience tells us that you will need at least autotools version 1.7. To build libburn and its companion applications go into its toplevel directory and execute - ./bootstrap (needed if you downloaded from SVN) - ./configure - make To make the libraries accessible for running and developing applications - make install @section libburner Libburner libburner is a minimal demo application for the library libburn (see: libburn/libburn.h) as provided on http://libburnia-project.org . It can list the available devices, can burn to recordable CD, DVD, or BD, can blank a CD-RW or DVD-RW, and can format unformatted DVD-RW, BD-R, or BD-RE. It's main purpose, nevertheless, is to show you how to use libburn and also to serve the libburnia team as reference application. libburner does indeed define the standard way how above gestures can be implemented and stay upward compatible for a good while. @subsection libburner-help Libburner --help <pre> Usage: test/libburner [--drive address|driveno|"-"] [--audio] [--blank_fast|--blank_full|--format] [--try_to_simulate] [--multi] [one or more imagefiles|"-"] Examples A bus scan (needs rw-permissions to see a drive): test/libburner --drive - Burn a file to drive chosen by number, leave appendable: test/libburner --drive 0 --multi my_image_file Burn a file to drive chosen by persistent address, close: test/libburner --drive /dev/hdc my_image_file Blank a used CD-RW (is combinable with burning in one run): test/libburner --drive /dev/hdc --blank_fast Blank a used DVD-RW (is combinable with burning in one run): test/libburner --drive /dev/hdc --blank_full Format a DVD-RW, BD-RE or BD-R: test/libburner --drive /dev/hdc --format Burn two audio tracks (to CD only): lame --decode -t /path/to/track1.mp3 track1.cd test/dewav /path/to/track2.wav -o track2.cd test/libburner --drive /dev/hdc --audio track1.cd track2.cd Burn a compressed afio archive on-the-fly: ( cd my_directory ; find . -print | afio -oZ - ) | \ test/libburner --drive /dev/hdc - To be read from *not mounted* media via: afio -tvZ /dev/hdc </pre> libburner has two companions, telltoc and dewav, which help to perform some peripheral tasks of burning. telltoc prints a table of content (sessions, tracks and leadouts), it tells about type and state of media, and also is able to provide the necessary multi-session information for program mkisofs option -C. Especially helpful are its predictions with "Write multi" and "Write modes" where availability of "TAO" indicates that tracks of unpredicted length can be written. See: test/telltoc --help. dewav extracts raw byte-swapped audio data from files of format .wav (MS WAVE) or .au (SUN Audio). See example in libburner --help. @subsection libburner-source Sourceode of libburner Click on blue names of functions, structures, variables, etc in oder to get to the according specs of libburn API or libburner sourcecode. @include libburner.c */ ------------------------------------------------------------------------------- Note: This is about how libburn operates optical drives. Not about how to operate libburn. The libburn API is described in libburn/libburn.h ------------------------------------------------------------------------------- libburnia-project.org Optical Media Rotisserie Recipes as of December 2011 Content: - TAO Multi-Session CD Cookbook (CD-R, CD-RW) - SAO CD Cookbook (CD-R, CD-RW, pure audio or pure data only) - Overwriteable DVD Cookbook (DVD-RAM, DVD+RW, DVD-RW, BD-RE) - Sequential DVD-R[W] Cookbook - DVD+R[/DL] Cookbook - BD-R Cookbook ------------------------------------------------------------------------------- TAO Multi-Session CD Cookbook ------------------------------------------------------------------------------- Guided by reading mmc-r10a.pdf , O.8 "Write a Track" from http://www.t10.org/ftp/t10/drafts/mmc/ backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/ by reading spc3r23.pdf from http://www.t10.org/ftp/t10/drafts/spc3/ by reading libburn/* from http://icculus.org/burn and by experiments with drives NEC ND-4570A, LG GSA-4082B, LITE-ON LTR48125S which used in part code from http://icculus.org/burn. For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> ------------------------------------------------------------------------------- Media type can be recognized by Current Profile from 46h GET CONFIGURATION. (mmc5r03c.pdf 6.6.2.1) CD-R 0009h CD-RW 000ah The following topics are covered in this text: - About blank, appendable and finalized CD media - Writing a session to CD in TAO mode - Obtaining CD multi-session info for extending ISO-9660 filesystems - Obtaining a Table Of Content from CD ------------------------------------------------------------------------------- About blank, appendable and finalized CD media : CD media have to be blank or appendable in order to be writeable in TAO mode. The according status may be inquired by 51h READ DISC INFORMATION requesting Data Type 000b Standard Disc Information, where reply value Disc Status indicates: 00b blank 01b appendable 10b finalized 11b others (unsuitable for this recipe) (mmc5r03c.pdf 6.22.3.1.4) CD-RW which are finalized or appendable may be blanked by command A1h BLANK with blanking types 000b "Blank the disc" or 001b "Minimally blank the disc". The Start Address/Track Number will be ignored so it may well be 0. Because the operation is long running it is advised to set the Immed bit and to watch the progress by commands 00h TEST UNIT READY and 03h REQUEST SENSE with DESC bit set to 0 for fixed format reply. It is done when 00h succeeds and 03h reports 0 in PROGRESS INDICATION (byte 1+2 in Table 22 = byte 16+17 SENSE KEY SPECIFIC in table 26). (mmc5r03c.pdf 6.2 BLANK) (spc3r23.pdf 4.5.2.4.4 table 22, 4.5.3 table 26, 6.27 REQUEST SENSE, 6.33 TEST UNIT READY) ------------------------------------------------------------------------------- Writing a session to CD in TAO mode : The writing method for blank or appendable media is the same. A new session will get created automatically by the first track when it is written. If the media is blank then the new session will be the first and only one in the table of content. If the media is appendable then a new session will be appended to the existing sessions. In any case the new track will be the first one in the new session. Speed may be set by BBh SET CD SPEED parameter Drive Write Speed. Note that kbytes/sec means 1000 bytes/sec and not 1024/sec. Rotational control should be set to 00b. 1x CD speed is 176.4 kbytes/sec. Speed is usually set to the next lower possible value by the drive. So it is helpful to add a few kbytes/sec just in case the drive has rounding problems. (mmc5r03c.pdf 6.37) Before writing can occur, a Write Parameters mode page 05h has to be composed and transmitted via 55h MODE SELECT. Mode page 05h describes several burn parameters: BUFE Buffer Underrun protection 0=off, 1=on Test Write -dummy mode for writing 0=off, 1=on Write Type Packet/TAO/SAO/RAW 01h = TAO Multi-session Whether to keep appendable 00b = finalize 11b = keep appendable Copy Whether to deny copying 1 = deny by SCMS , 0 = allow Track Mode Describes frame type 4 for data , 0 for audio Data Block Type Layout of payload blocks 8 for 2048 byte data blocks 0 for 2352 byte audio blocks Audio Pause Length 150 = 2 seconds Media Catalog Number A property of the disc 0x80 if valid 13 decimal digits as ASCII ISRC A property of the track 0x80 if valid 12 letters and digits, ASCII Any other parameters may be set to 0. Mode page data as of MMC-5 table 644 are preceded by a Mode Parameter Header as of SPC-3 table 240. This 8-byte header may be filled with zeros. (mmc5r03c.pdf 7.5.4 The Mode Page, 4.2.3.4 Table 17 CONTROL = Track Mode) (spc3r23.pdf 6.8 MODE SELECT, 7.4.3 Mode parameter header formats) Writing has to begin at the address returned by 52h READ TRACK INFORMATION with Address/Number Type set to 01b and Logical Block Address/Track/Session Number set to FFh. The Next Writeable Address as of table 500 is the number to start writing with. (mmc5r03c.pdf 6.27 ) Writing is performed by one or more 2Ah WRITE transactions with the Logical Block Address counted up from the initial number in sync with the number of blocks written. I.e the Transfer Length of the previous 2Ah WRITE has to be added to the Logical Block Address for the next 2Ah WRITE. Only full blocks can be written. (mmc5r03c.pdf, 6.44) When writing is done, it is mandatory to force the drive's buffer to media by 35h SYNCHRONIZE CACHE. (mmc5r03c.pdf, 6.41) A track must at least contain 300 payload blocks: 4 seconds of audio or 600 KiB of data. (mmc5r03c.pdf 6.3.3.1.2) Up to december 2009 the track was closed by 5Bh CLOSE TRACK SESSION Close Function 001b. Older MMC specifies a valid Logical Track Number FFh to depict the open track. MMC-5 is quite silent about this. FFh worked for my drives. (mmc5r03c.pdf 6.3.3.1.2) This is omitted since libburn-0.7.4, relying entirely on 35h SYNCHRONIZE CACHE. First appeared a drive where CLOSE TRACK fails in simulation mode, later another one produced error replies even with real burning. After that, a new track may be written beginning with sending the mode page 05h again. It is not tested whether 05h can be omitted if Track Mode and Data Block Type are the same as with the previous track. The new track will be added to the session which was opened by the first track. After the last track of a session, 5Bh CLOSE TRACK SESSION Close Function 010b with Logical Track Number 0 closes the session. It depends on the Multi-Session value in mode page 05h whether the disc is finalized or stays appendable. (mmc5r03c.pdf 6.3.3.1.3) ------------------------------------------------------------------------------- Obtaining CD multi-session info for extending ISO-9660 filesystems : Program mkisofs expects two numbers with its option -C which describe the situation on an appendable CD which already contains a ISO-9660 filesystem in the first track of the last session. The first number is the Logical Block Address of that track containing the existing ISO-9660 filesystem image. This number is needed for mkisofs option -M to connect to the existing image. The new image will refer to files in the previously existing image. mkisofs option -M needs read access to the CD or a blockwise copy of it on hard disk. The number is gained by 43h READ TOC/PMA/ATIP. (mmc5r03c.pdf 6.26) Untested is Format 0001b which in table 478 promises quick access via Start Address Of First Track In Last Session. (mmc5r03c.pdf 6.26.2.5 table 478, 6.26.3.3.1) libburn gets the number from its Table Of Content model which is obtained by 43h READ TOC/PMA/ATIP, Format 0010b. See below. The second number is an exact prediction of the Logical Block Address of the new track which will contain the newly generated ISO-9660 image. Even without mkisofs option -M this second number is still needed to make the inner block address pointers of the image match the Logical Block Addresses on CD. For that one may inquire 52h READ TRACK INFORMATION with Address/Number Type set to 01b and Logical Block Address/Track/Session Number set to FFh. The Next Writeable Address as of table 500 is the number to use. (mmc5r03c.pdf 6.27 ) ------------------------------------------------------------------------------- Obtaining a Table Of Content from CD : The structure of a CD is comprised of sessions. Each session contains one or more tracks and is followed by a lead-out. A track has an address and a length. Table of content information is gained by 43h READ TOC/PMA/ATIP, Format 0010b. (mmc5r03c.pdf 6.26.2.5 table 478) The number of sessions is given by Last Complete Session Number. The number of TOC Track descriptors is: (TOC Data Length - 2)/11 . Each TOC Track Descriptor contains a Session Number. If POINT is >= 1 and <= 99 (63h) then the descriptor is about the track of which POINT tells the number. The start address of this track can be read from PMIN, PSEC, PFRAME where it is encoded in MSF format: If M is smaller than 90: LBA = (M * 60 + S) * 75 + F - 150 Else : LBA = (M * 60 + S) * 75 + F - 450150 The length of the track is given by MIN,SEC,FRAME in the same format. If POINT = A0h then the descriptor tells in PMIN the first track number of its session. POINT = A1h tells in PMIN the last track number of its session. POINT = A2h describes in PMIN, PSEC, PFRAME the lead-out of a session, i.e the first address after the session's end. (Next writeable address typically is lead-out + 11400 after the first session, lead-out + 6900 after further sessions.) POINT = B0h tells in MIN,SEC,FRAME this next writeable address or FFh,FFh,FFh for finalized disc. (mmc5r03c.pdf 6.26.3.4 table 489, 4.2.3.7 Mode-1 Q, Mode-5 Q) In libburn the address of the first track in the last session is obtained from the last session's POINT = A0h and from the track descriptor with the POINT value matching the PMIN value of the A0h descriptor. Untested is whether POINT = B0h and 52h READ TRACK INFORMATION are always in sync. libburn uses the info provided by 52h READ TRACK INFORMATION. ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- SAO CD Cookbook ------------------------------------------------------------------------------- Guided by reading libburn/* from http://icculus.org/burn backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/ backed by reading scms.html from http://www.barrel-of-monkeys.com/graphics/prod/dvdplayers/ and by experiments with drives NEC ND-4570A, LG GSA-4082B, LITE-ON LTR48125S, Optiarc BD RW BD-5300S, LG BDDVDRW GGC-H20L For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> ------------------------------------------------------------------------------- Recognition of media type and state (blank, appendable, finalized) is as described in the TAO Multi-Session CD Cookbook. See there. The MMC specs do not give much hint about the combination of SAO and multi-session. My drives refused not only on a few experiments which i did in libburn but also failed with cdrecord -sao on an appendable CD. So for now only blank CD seem to be suitable for SAO writing. Different from TAO mode, the whole session layout is announced to the drive by sending a Cue Sheet. This implies that the sizes of the tracks have to be known in advance, which is a heavy drawback when dealing with track data sources like stdin, named pipes or sockets. Nevertheless, SAO seems to be best writing mode for audio purposes, as our audio expert Lorenzo Taylor found out. A SAO session in libburn may either consist entirely of audio tracks or entirely of data tracks. For mixed sessions, only TAO is usable yet. - Composing a SAO CD Cue Sheet (either audio or data, but not mixed) - Writing the prepared SAO CD session - What is known about mixed mode sessions ------------------------------------------------------------------------------- Composing a Cue Sheet (either audio or data, but not mixed) : The Cue Sheet will get submitted to the drive by 5Dh SEND CUE SHEET. Each entry of the sheet is of 8 bytes size. Its fields are named CTL|ADR, TNO, INDEX, DATA FORM, SCMS, MIN, SEC, FRAME . (mmc5r03c.pdf 6.33) CTL is comprised of four bits: bit4 = Pre-emphasis (audio only) bit5 = Digital copy permission: 0 = prohibited (one-time copy is permitted if SCMS is 00h) 1 = permitted (unlimited) bit6 = Data track indicator (bit4 and bit7 shall be 0) bit7 = 4-channel audio Usually CTL is 40h for data and 00h for audio. (mmc5r03c.pdf 6.33.3.4) ADR is 01h for entries which define time points. It is 02h for media catalog entries and it is 03h for track ISRC entries. The bits of CTL and ADR are combined in the CTL|ADR byte. TNO is the track number. The TNO of the first track may be chosen in the range of 1 to 99. The TNO of following tracks must be the TNO of their predecessor plus 1. The last track must not have a TNO larger than 99. INDEX is a subaddress within tracks. INDEX 1 is mandatory and marks the start of the payload area of a track. The range between INDEX 0 and 1 is called pre-gap. It should contain zeros if it exists. Further cue sheet entries with consecutive INDEX numbers mark ranges within the track. The range of the last index may contain a post-gap with zeros. (mmc5r03c.pdf 4.2.3.5.2) A pre-gap of 2 seconds is mandatory only for the first track. Pre-gap and post-gap may be needed with further tracks if they have neighbors with different DATA FORM values. (Such mixing is not yet supported by libburn.) DATA FORM is 00h for audio payload, 01h for audio pause (Lead-in and Lead-out), 10h for data, 14h for data pause (Lead-in and Lead-out). This shall be ored with 40h for CD-TEXT in Lead-in. (mmc5r03c.pdf 6.33.3.11 CD-DA Data Form, 6.33.3.12 CD-ROM mode 1 Form) SCMS value 80h in conjunction with bit5 of CTL is an indicator for exhausted one-time-copy permission. If this permission is still intact, then SCMS is 00h. MIN, SEC, FRAME give the MSF address where the described data entity starts. LBA = frames - 150, 75 frames = 1 sec , 60 sec = 1 min. This address must increase from entry to entry (or at least stay equal). The first two entries in a Cue Sheet may describe the Media Catalog Number, a string of 13 characters, also known with CD-TEXT as "UPC/EAN". (02h, catalog characters 1 to 7) (02h, catalog characters 8 to 13, 00h) These two entries shall be omitted if no catalog number is given. The next entry (eventually being the first one) describes the Lead-in. Its content is (CTL|ADR ,00h,00h, DATA FORM ,00h,00h,00h,00h) With the CTL|ADR for the first track: 41h for data, 01h for audio. DATA FORM is pause (audio=01h, data=14h). Ored with 40h if CD-TEXT shall be stored in Lean-in. The LBA for the first write is negative: -150. This corresponds to MSF address 00h:00h:00h. All addresses are to be given in MSF format. Each track may be preceded by two entries describing an ISRC string of 12 characters. (CTL | 03h, TNO, characters 1 to 6) (CTL | 03h, TNO, characters 7 to 12) These entries shall be omitted if no ISRC is given for the track. CTL shall be the same as with the track. The first information track on disc is preceded by a pause encoding or pre-gap of at least 2 seconds: (CTL|ADR,01h,00h, DATA FORM ,00h,00h,00h,00h) with DATA FORM = 00h for audio and 10h for data. By those 2 seconds the MSF address increases to 00h:02h:00h = LBA 0. Optional further sectors may occupy addresses larger than 0. This entry has to come after ISRC, if ISRC is given for the track. INDEX has to be 0. Each track is represented by one or more entries, with increasing index number. At least the entry for INDEX 1 has to exist: (CTL|ADR, TNO ,01h,DATA FORM,00h, MIN , SEC , FRAME) TNO gives the track number. MIN, SEC, FRAME give the MSF address which becomes the start address of the track. The MSF address is then increased by the size of the track (to be used with next track or with lead-out). There may be more entries with INDEX 2 to 99. Their MSF address tells the sector where their range starts. This range ends at the MSF of the next entry in the cue sheet. INDEX information is stored in the sub-channel of the sectors but not in the Table-of-Content of the disc. A track must at least contain 300 payload blocks: 4 seconds of audio or 600 KiB of data. (mmc5r03c.pdf 6.33.3.6) At the end of the session there is a lead-out entry (CTL|ADR,AAh,01h,DATA FORM,00h,MIN,SEC,FRAME) marking the end of the last track. (With libburn CTL is as of the last track.) DATA FORM is 01h for audio, 14h for data. ------------------------------------------------------------------------------- Writing the prepared session : Speed may be set by BBh SET CD SPEED parameter Drive Write Speed. Note that kbytes/sec means 1000 bytes/sec and not 1024/sec. Rotational control should be set to 00b. 1x CD speed is 176.4 kbytes/sec. Speed is usually set to the next lower possible value by the drive. So it is helpful to add a few kbytes/sec just in case the drive has rounding problems. (mmc5r03c.pdf 6.37) If CD-TEXT shall be written into Lead-in, then it is necessary to obtain the Start Time of Lead-in by 43h READ TOC/PMA/ATIP Format 0100b. It is an MFS address which varies from media manufacturer to media manufacturer. Minute will be >= 90. Therefore this conversion applies: LBA = (M * 60 + S) * 75 + F - 450150 A Write Parameters mode page 05h has to be composed and transmitted via 55h MODE SELECT. This page describes the following parameters: BUFE Buffer Underrun protection 0=off, 1=on Test Write -dummy mode for writing 0=off, 1=on Write Type Packet/TAO/SAO/RAW 02h = SAO Multi-session Whether to keep appendable 00b = finalize 11b = keep appendable Track Mode Describes frame type 0 (is ignored) Data Block Type Layout of payload blocks 0 (is ignored) Audio Pause Length 150 = 2 seconds (ignored ?) Media Catalog Number 0x80 if valid See also Cue Sheet ADR 02h 13 decimal digits as ASCII (With SAO, ISRC is transmitted only by the Cue Sheet.) Any other parameters may be set to 0. Mode page data as of MMC-5 table 644 are preceded by a Mode Parameter Header as of SPC-3 table 240. This 8-byte header may be filled with zeros. (mmc5r03c.pdf 7.5.4 The Mode Page, 4.2.3.4 Table 17 CONTROL = Track Mode) (spc3r23.pdf 6.8 MODE SELECT, 7.4.3 Mode parameter header formats) The Cue Sheet is submitted to the drive by 5Dh SEND CUE SHEET. Cue Sheet Size is 8 times the number of entries. (mmc5r03c.pdf 6.33) Writing is performed by multiple 2Ah WRITE transactions with the Logical Block Address counted up from the initial number in sync with the number of blocks written. I.e the Transfer Length of the previous 2Ah WRITE has to be added to the Logical Block Address for the next 2Ah WRITE. Only full blocks can be written. (mmc5r03c.pdf, 6.44) Block addresses may be negative for areas before the normally readable data. Data representation of addresses is 4-byte, big-endian, two's-complement. E.g: -150 = FFh FFh FFh 6Ah. This is the natural form found with about any 32-bit processor, so only the endianness has to be taken into respect when converting a 32-bit integer into a LBA for command 2Ah WRITE. If CD-TEXT shall be written into Lead-in, then writing begins at the start address of Lead-in, which was obtained above. The 18 bytes of each text pack have to be split up to 24 bytes with only the lowest six bits used in each byte. E.g. text pack 8F 00 2A 00 01 01 03 00 06 05 04 05 07 06 01 02 48 65 becomes 23 30 00 2A 00 00 04 01 00 30 00 06 01 10 10 05 01 30 18 01 00 24 21 25 4 of these 24 byte packs form a block of DATA FORM 41h. I.e. only 96 bytes payload per block. The whole range from Lead-in start to LBA -150 has to be filled with blocks of this form. Therefore it is necessary to write the list of given packs in repeated cycles. A typical Lead-in start address is -11635 = FFh FFh D2h 8Dh. A description of the CD-TEXT pack format is given in file doc/cdtext.txt . Writing without CD-TEXT begins at LBA -150 = FFh FFh FFh 6Ah. In both cases, the mandatory pause preceding the first track has to be written as 150 blocks of the matching sector size: 2048 for data, 2352 for audio. By this, the LBA increases from -150 to 0. Next the tracks' payload is sent. For each track exactly the number of blocks has to be transmitted as is announced in the Cue Sheet by the difference of the track's own start address and the start address of the next entry in the Cue Sheet. After each write the LBA for the next write has to be increased by the number of blocks transmitted. Just like with TAO writing. There is no separator between the tracks of a pure mode SAO session. (If the session was mixed mode, there would be extended Pre-gaps and Post-gaps between data mode tracks and audio mode tracks.) (libburn sends its own buffer to the drive at the end of each track but does not sync the drive's chache. It is unclear whether this separation of tracks on the level of 2Ah WRITE is necessary with a pure mode session. It does not harm in any case and would probably be unavoidable if audio and data tracks were mixed.) When writing of all tracks is done, it is mandatory to force the drive's buffer to media by 35h SYNCHRONIZE CACHE. (mmc5r03c.pdf, 6.41) No further finalization is necessary. (I.e. no 5Bh CLOSE TRACK SESSION.) ------------------------------------------------------------------------------- Obtaining CD-TEXT from Lead-in : Audio CDs may contain CD-TEXT information in their Lead-in. It is gained by 43h READ TOC/PMA/ATIP, Format 0101b. The reply consists of 4 bytes header, of which the first two bytes give the number of following bytes as big-endian 16 bit number. The other two bytes are 0. Following are text packs of 18 bytes each. (mmc5r03c.pdf 6.26.3.7.1 table 495) A description of CD-TEXT packs and of the applicable libburn API calls is given in file doc/cdtext.txt . ---------------------------------------------------------------------------- What is known about mixed mode sessions : For now, SAO sessions with a mix of data and audio are not supported in libburn. Here are the reasons why. Obviously the code of http://icculus.org/burn is incomplete in this aspect. In mmc5r03c.pdf comparison of table 555 and 6.33.3.18 seems self-contradicting. (The second Pre-gap in table 555 does not match any of the criteria of 6.33.3.18. Also, there is no Post-gap shown in table 555 although 6.33.3.19 would prescribe some.) If a data track follows an audio track then the data track gets a preceding extended Pre-gap: (CTL|ADR, TNO ,01h,DATA FORM,00h, MIN , SEC , FRAME) with TNO already the number of the data track. The MSF address is to be increased by 3 seconds. The first second of the extended Pre-gap needs to be written in the audio track's mode and the other 2 seconds are to be written in the data track's mode. (libburn compares DATA FORM rather than burn_track.mode . Wrong ?) (libburn currently does only 2 seconds and the second part of Pre-gap. There is an issue with burn_track.pregap1 about this. Seems libburn mistakes the pause preceding track 1 for a part 2 of an extended Pre-gap.) If a data track is followed by an audio track then it gets a Post-gap of at least two seconds. No example of Post-gap is given for Cue Sheet. Maybe it is to be added to the track, or maybe it gets an own Cue Sheet entry ... who knows ? (libburn contains write code for pregap1, pregap2 and postgap. But only pregap2 ever gets activated. Once hackingly for the first 2 second pause, once incompletely for a change of DATA FORM.) Seems nobody ever tested this. Libburnia simply knows no use case where the correctness of Pre-gap and Post-gap would become evident. ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- Overwriteable DVD Cookbook ------------------------------------------------------------------------------- Inspired by Andy Polyakov's http://fy.chalmers.se/~appro/linux/DVD+RW/tools , backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/ by own experiments with drives NEC ND-4570A, LG GSA-4082B, PHILIPS SPD3300L, LG GGW H20L, and by BD-RE experiments done by Giulio Orsero on LG BE06LU10. For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> ------------------------------------------------------------------------------- Media type can be recognized by Current Profile from 46h GET CONFIGURATION. (mmc5r03c.pdf 6.6.2.1) DVD-RAM 0012h DVD-RW Restricted Overwrite 0013h DVD-RW Sequential Recording 0014h (i.e. unformatted) DVD+RW 001Ah BD-RE 0043h A short compilation of the write model: - Overwriting in general The recipes described here are depending on formatting state: - DVD-RAM, fully formatted DVD+RW, DVD-RW, BD-RE - Unformatted DVD+RW - Partly formatted DVD+RW - Unformatted DVD-RW - Partly formatted DVD-RW - Intermediate state DVD-RW - DVD-RAM and BD-RE formatting - DVD-RAM and BD-RE speed tuning Slightly off topic are - ISO 9660 multi-session emulation on overwriteable media - ISO 9660 based TOC emulation on overwriteable media ------------------------------------------------------------------------------- Overwriting in general : Depending on media type, some kind of formatting has to have happened before data can be written. Formatting may happen separately from writing or simultaneously. See the particular recipes below. No Write Parameters mode page 05h is to be sent. Speed can be influenced by B6h SET STREAMING , speed capabilities can be inquired by ACh GET PERFORMANCE. It is advised to set only speeds and sizes which are returned by ACh. (mmc5r03c.pdf 6.39 SET STREAMING, 6.8 GET PERFORMANCE) Optimal performance is promised without any speed setting. But my experiments showed that SET STREAMING values persist after media change. In the formatted area of the media, coarse random access is possible. For DVD-RAM, DVD+RW, BD-RE write addresses and data size need to be aligned to 2 KiB. For DVD-RW alignment has to be 32 KiB. Within these limitations the write address is at the discretion of the sending program. Just use 2Ah WRITE to send data. (mmc5r03c.pdf, 6.44) When writing is done, it is mandatory to force the drive's buffer to media by 35h SYNCHRONIZE CACHE. (mmc5r03c.pdf, 6.41) The size of the formatted area can be inquired by 23h READ FORMAT CAPACITIES. The Number Of Blocks value in the Current/Maximum Capacity Descriptor gives this size in 2 KiB blocks. But this is true only if Descriptor Type is 10b ("Formatted Media"). (mmc5r03c.pdf, 6.24.3.2.1, 6.24.3.2.3) Not yet formatted areas may be completely forbidden or they may be allowed for sequential writing (DVD-RW Intermediate state) or they may be allowed for random access only after the necessary waiting time for formatting to reach the desired address (DVD+RW with background formatting active). Already written areas can be overwritten without special precaution. Blanking a DVD-RW actually destroys its formatting. Most of the concepts usually expressed in Write Parameters mode page 05h do not apply to the recipes here: Test-Write, Buffer Underrun protection, Multi-session, Write Type, Block Type, Track Mode, ... There are hints for multi-session formats with DVD-RW but both of my drives do not offer "Add Session" Format Types 12h or 14h. (mmc5r03c.pdf 6.5.4.2.7 , 6.5.4.2.9) Caution: Drive and media compatibility seems still to be quite an adventure. If you experience problems, especially problems with readability, then try different drives and media brands. Failure does not necessarily mean that the software did anything wrong. ------------------------------------------------------------------------------- DVD-RAM, fully formatted DVD+RW, DVD-RW, BD-RE : Full format is the natural state of DVD-RAM. BD-RE are sold unformatted and need to be fully formatted first. See paragraph about DVD-RAM and BD-RE formatting below. DVD+RW reaches this state if Background Formatting is allowed to finish without being stopped by 5Bh CLOSE TRACK SESSION. (mmc5r03c.pdf, 6.5 FORMAT UNIT, 6.5.4.2.14 Format Type = 26h) The formatting state of a DVD+RW may be inquired by 51h READ DISC INFORMATION requesting Data Type 000b "Standard Disc Information". In the reply, BG Format 3 indicates fully formatted media. (mmc5r03c.pdf 6.22.3.1.13) DVD-RW reaches this state either by Format Type 00h (or 10h) with maximum size given as Number Of Blocks, or by writing sequentially until the disc is completely full into an intermediate session opened by format 15h or 13h. (mmc5r03c.pdf, 6.5 FORMAT UNIT, 6.5.4.2.1, 6.5.4.2.10, 6.5.4.2.8) A fully formatted DVD-RW can be recognized by 23h READ FORMAT CAPACITIES. The Descriptor Type of the Current/Maximum Capacity Descriptor is 10b ("Formatted Media") and 0 blocks are offered with Format Types 13h or 11h. (mmc5r03c.pdf, 6.24.3.2.1, 6.24.3.3) See also discussion of unformatted or partially formatted DVD-RW below. In fully formatted state there is no need for any formatting before writing nor for any finalizing other than forcing the drive's buffer to media by 35h SYNCHRONIZE CACHE (which is mandatory for writing, anyway). (mmc5r03c.pdf, 6.41) (It seems to do no harm to send to DVD+RW or DVD-RW a 5Bh CLOSE TRACK SESSION with Close Function 010b despite there is no session open in this scenario.) ------------------------------------------------------------------------------- Unformatted DVD+RW : This is the state of previously unused DVD+RW media. The formatting state of a DVD+RW may be inquired by 51h READ DISC INFORMATION requiring Data Type 000b "Standard Disc Information". In the reply, BG Format 0 indicates unformatted media (or unsuitable media). (mmc5r03c.pdf 6.22.3.1.13) Formatting has to be started by command 04h FORMAT UNIT, Format Type 26h. Different from other format types, 26h is allowed to send a fantasy size of 0xffffffff blocks and does not require the caller to know the exact maximum size offered with that format. (mmc5r03c.pdf, 6.5 FORMAT UNIT, 6.5.4.2.14 Format Type = 26h) As its name suggests, one has not to wait for background formatting to end but may very soon start writing as on formatted media. Random access to yet unformatted areas can last long, though. If backup formatting has been started at the beginning of the session, then it may get stopped after the final cache sync by 5Bh CLOSE TRACK SESSION with Close Function 010b. (mmc5r03c.pdf 6.3.3.6) Formatting of DVD+RW is called "de-icing" because unformatted areas offer no hold for random access addressing and are thus slippery like ice. One can also see a color change from shiny unformatted to more dull formatted media. ------------------------------------------------------------------------------- Partly formatted DVD+RW : This state is achieved by stopping background formatting before the media was completely formmatted. The formatting state of a DVD+RW is obtained by 51h READ DISC INFORMATION requiring Data Type 000b "Standard Disc Information". In the reply, BG Format 1 indicates partly formatted media. (mmc5r03c.pdf 6.22.3.1.13) If the data of the session surely fit into the formatted area, then it would be unnecessary to restart background formatting. But in order to make the DVD+RW surely accept its maximum number of bytes, formatting may be restarted by command 04h FORMAT UNIT, Format Type 26h, with the Restart bit set and Number of Blocks set to 0xffffffff. (mmc5r03c.pdf, 6.5 FORMAT UNIT, 6.5.4.2.14 Format Type = 26h) From then on, the same rules apply as for previously unformatted DVD+RW. ------------------------------------------------------------------------------- Unformatted DVD-RW (media profile is 0014h) : This state is present with previously unused media. It is also present with media blanked by programs cdrecord, wodim or dvd+rw-format and with media which were sequentially written from blank state. Profile transition from formatted 0013h to unformatted 0014h is done by A1h BLANK. (mmc5r03c.pdf, 6.2) Experiments on my drives indicate that only Blanking Type 000b "Blank the disc" achieves neat media. Media blanked via type 001b offer no feature 0021h and stall cdrecord or libburn already when those media get examined. growisofs can burn them - but only via DAO (feature 002Fh which prescribes Write Type 2). (mmc5r03c.pdf 5.3.11, 5.3.25) For becoming overwriteable such media need to be treated by command 04h FORMAT UNIT. (mmc5r03c.pdf, 6.5) The Format Type has to be chosen from the list replied by 23h READ FORMAT CAPACITIES. Suitable are Format Types 00h, 10h, 15h. (mmc5r03c.pdf 6.24) Format Types 00h and 10h provide a writeable area of a size given by Number of Blocks. Type 00h seems to be the most traditional and complete one. It needs no closing of a session at the end of writing. The Number Of Blocks may be at most the value reported by 23h READ FORMAT CAPACITIES in the entry for the desired format type. Full format is achieved by sending exactly the reported value. (mmc5r03c.pdf, 6.5.4.2.1 Format Type = 00h, 6.5.4.2.5 Format Type = 10h) Format Type 15h provides a writeable area of given size too, but this area can be expanded by sequential writing and afterwards marked as overwriteable by closing the session. It is even allowed to format with size 0 and to leave the size claim entirely to a sequential write process beginning at LBA 0. (mmc5r03c.pdf, 6.5.4.2.10 Format Type = 15h) When writing is done and cache is synced, one should send 5Bh CLOSE TRACK SESSION with Close Function 010b in order to bring the session out of Intermediate state. (mmc5r03c.pdf 6.3.3.2.3) If not written up to the last 32 KiB block, the DVD-RW is only partly formatted after that. ------------------------------------------------------------------------------- Partly formatted DVD-RW (media profile is 0013h) : This state is achieved by formatting a DVD-RW with a number of blocks which is less than offered for the Format Type by the drive's reply to 23h READ FORMAT CAPACITIES. If the media was most recently formatted by Format Types 015h or 013h then it must have got written some bytes and afterwards treated by 5Bh CLOSE TRACK SESSION, 010b in order to be partly formatted. (mmc5r03c.pdf 6.3.3.2.3 CLOSE TRACK SESSION 010b, 6.24 READ FORMAT CAPACITIES) Elsewise the media is in Intermediate state. See below. A partly formatted DVD-RW can be recognized by 23h READ FORMAT CAPACITIES. The Descriptor Type of the Current/Maximum Capacity Descriptor is 10b ("Formatted Media") and the Number Of Blocks with formats 00h, 10h or 15h is larger than the currently formatted size, or more than 0 blocks are offered with Format Types 13h or 11h. (mmc5r03c.pdf, 6.24.3.2.1, 6.24.3.3) If the data of the session surely fit into the formatted area, then it would be unnecessary to do any further formatting. But in order to make the DVD-RW surely accept its maximum number of bytes, partial formatting may be expanded by command 04h FORMAT UNIT, Format Type 13h, which is supposed to be offered by the drive in this state. This brings the session again into Intermediate state and thus enables expansion by sequential writing. As with Format Type 15h it is ok to set Number Of Blocks to 0, so that no fixed size formatting work is done and writing can begin soon after. (mmc5r03c.pdf, 6.5.4.2.8 Format Type = 13h) When writing is done and cache is synced, one should send 5Bh CLOSE TRACK SESSION with Close Function 010b in order to bring the session out of Intermediate state. (mmc5r03c.pdf 6.3.3.2.3) If not written up to the last 32 KiB block, the DVD-RW is only partly formatted after that. Format Type 13h has been tested only with expanding sessions formatted by 15h. Nevertheless it is offered with sessions from 00h and 10h, too. According to the specs, Format Type 11h would expand a session by a fixed size. This has not been tested yet because it is less appealing than 13h. (mmc5r03c.pdf, 6.5.4.2.6 Format Type = 11h) ------------------------------------------------------------------------------- Intermediate state DVD-RW (media profile is 0013h) : This state is achieved by formatting a DVD-RW with Format Type 15h or 13h without subsequentially writing data and sending 5Bh CLOSE TRACK SESSION with Close Function 010b. Such media behave very unpleasing with my DVD-ROM drive under Linux 2.4 ide-cd. One should therefore better avoid to release media in this state. This state can be recognized by 23h READ FORMAT CAPACITIES. The Descriptor Type of the Current/Maximum Capacity Descriptor is 11b ("Unknown Capacity") and no formats 13h or 11h are offered. (mmc5r03c.pdf, 6.24.3.2.1, 6.24.3.3) One may treat such media as if Format Type 15h or 13h had been freshly applied. I.e. sequential writing from LBA 0. After cache sync bring the session out of Intermediate state by 5Bh CLOSE TRACK SESSION with Close Function 010b. (mmc5r03c.pdf 6.3.3.2.3) ------------------------------------------------------------------------------- DVD-RAM and BD-RE formatting : Although DVD-RAM usually are sold formatted, there may still arise the wish to adjust formatting. BD-RE are sold unformatted and need to be formatted prior to usage. Two format types are relevant for DVD-RAM : 00h and 01h. 00h offers the default size format and usually a maximum payload size format. Even with that maximum size payload there is hardware defect management. (mmc5r03c.pdf 6.5.4.2.1.2) 01h can convert payload capacity into spare blocks for defect management. There is no way to increase payload capacity by format 01h. (mmc5r03c.pdf 6.5.4.2.2.1) With BD-RE there are three format types : 00h, 30h and 31h. 00h offers the default size format. This may be the only fast formatting mode that is offered by the drive. Feature 0023h tells whether format 31h and certain 30h subtypes are available. (mmc5r03c.pdf 5.3.13) 30h offers several sizes with defect management. Usually there are three sizes given: #1: default size, #2: maximum spare area, #3: minimal spare. One may demand any spare size between maximum and minimum. There may be quick certification and full certification. See feature 0023h. 31h offers a single size and disables defect management. This has the side effect to speed up writing to nominal speed. (mmc5r03c.pdf 6.5.4.2.15, 6.24.3.3, Table 472) Only format sizes from the list of format descriptors are permissible for DVD-RAM. The format list can be obtained by 23h READ FORMAT CAPACITIES. It also includes a description of the current formatting state. (mmc5r03c.pdf 6.24, 6.24.3.2, 6.24.3.3) Formatting is done by command 04h FORMAT UNIT. Its data payload consists of a Format List Header and a Format Descriptor. It is advisable to set the Immed bit and the FOV bit in header byte number 1. The descriptor should be a copy of a descriptor from 23h READ FORMAT CAPACITIES. (mmc5r03c.pdf 6.5, 6.5.3.2, 6.5.3.3) With nearly all formats Sub-type should be set to 0. But with BD-RE formats 30h and 31h the Sub-type selects the certification mode. Usable with 30h seem 10b Full Certification and 11b Quick Certification. Usable with 31h seem also 00b Quick Reformat and 01b No Certification. (mmc5r03c.pdf 6.5.4.2.15.1) Other format types have their certification intensity controlled by a pair of bits: CmpList and DCRT. CmpList resides in CDB byte 1 as bit 3. DCRT resides in the payload byte 1 as bit 5. Both together should request a quick size change without lengthy certification but maintaining the list of known defects. (mmc5r03c.pdf 6.5, table 249, 6.5.3.2) With DVD-RAM on my PHILIPS SPD3300L drive they prevent any format size change though. The TSSTcorp SH-S203B works properly. With BD-RE format 00h, the default is specified to be Quick Reformat, and with 00h in general certification can only be disabled not enabled. (mmc5r03c.pdf 6.5.4.2.1.7) ------------------------------------------------------------------------------- DVD-RAM and BD-RE tuning : A special aspect of DVD-RAM and BD-RE is their low speed with write operations, which usually is only half than the nominal speed of media and drive. This is blamed to the automatic checkreading which happens for managing eventual defects. Defect management of BD-RE can be disabled by format type 31h. See above. There is no drive known yet which would apply command 2Ah WRITE10 to DVD-RAM with full speed. The only known way to get full speed from DVD-RAM or BD-RE with enabled defect management is the use of AAh WRITE12 with Streaming Bit set to 1. (mmc5r03c.pdf 6.45) With some DVD-RAM drives this fails if a write buffer is not full 32 kB. With the tested BD-RE one must write full 64 kB buffers, or else writing might not get into effect at all. Although it seems not optimal, this is specified not only to disable the cumbersome checkread but also to ignore known defects and to write data to these defective addresses. (mmc5r03c.pdf 4.8.5) So the speed-up is only advisable as long as the media are free of incorrectable errors. Caveat: MMC-5 does not guarantee AAh WRITE12 to work on DVD-RAM or BD-RE at all. None of the features of profiles 0012h and 0043h promises the existence of AAh WRITE12. (mmc5r03c.pdf 5.4.13, 6.45) Nevertheless it worked on all tested drives if proper alignment and block size was observed. ------------------------------------------------------------------------------- ISO 9660 multi-session emulation on overwriteable media : Overwriteable media provide a single overwriteable track which may grow up to the full media capacity. There is no builtin table-of-content which records the history of write sessions. mount -t iso9660 will use sbsector=0 as default. The term "superblock" shall depict the first 64 KiB after the sbsector address. ISO 9660 multi-session depends on typical TOC information in two ways: It needs the superblock address MSC1 of the most recently recorded session and it needs the Next Writeable Address NWA for which to prepare the address offset. The following is learned from growisofs and from ECMA-119: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf ISO 9660 filesystems provide information about the number of sectors which is also the lowest unused block address and thus a suitable NWA. This block address is stored in the Primary Volume Descriptor which is supposed to be stored in block 16 (eventually relative to MSC1). The bytes 0 to 5 of a PVD block are 0x01 'C' 'D' '0' '0' '1' The sector count can then be read from byte 80 to 83 sectors= pvd[80] | (pvd[81] << 8) | (pvd[82] << 16) | (pvd[83] << 24); (Ecma-119.pdf 8.4) To support CD, DVD and BD media alike, it is advisable to round the NWA to the next multiple of 32 (= 64 KiB). So one can use 0 as MSC1 and prepare a new ISO session for the computed NWA. After writing the session it is necessary to copy the PVD from session start plus 16 to LBA 16 and to adjust it to its new location. The minimal change would be to update the number of image sectors. It is stored in both notations LSB and MSB: for(i= 0; i < 4; i++) pvd[87 - i]= pvd[80 + i]= (sectors >> (8 * i)) & 0xff; cdrskin --grow_overwriteable_iso not only patches the sector fields of the PVD block but also the blocks up to LBA 31 which begin with 0xff 'C' 'D' '0' '0' '1' libisoburn submits 64 KiB data buffer to libisofs before image generation and afterwards writes these 64 KiB as new superblock to LBA 0. ------------------------------------------------------------------------------- ISO 9660 based TOC emulation on overwriteable media : Above method of multi-session emulation yields a single session image after each add-on session. No reliable session history can be read because the sector size of the existing session gets overwritten by the new one. A TOC with session history is nevertheless desirable with incremental backups in order to access older backup states by mounting older superblocks at the start addresses of older sessions. All usual ISO 9660 formatter programs write a complete superblock to the start of each session. With a uniform NWA rounding rule it is possible to compute the address of superblock N+1 as the NWA after session N. The only problem is N=1 because it gets overwritten by later sessions. libisoburn preserves the information of session 1 by writing the first session to LBA 32 rather than LBA 0. Afterwards it writes the overall superblock to LBA 0 (up to 31). So with all further add-on sessions the superblock at LBA 0 will enclose the overall image, while the superblocks of the sessions form a chain beginning at LBA 32. Each session superblock points to the next one by its sector count rounded up to 32. The chain end is marked by the overall image size. This chain gives the start addresses of sessions. The sector count minus start address gives the size of a particular session. ECMA-119 explains how to retrieve more info from the PVD (e.g. the volume id). See also the multi-session example in libisofs/doc/checksums.txt. ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- Sequential DVD-R[W] Cookbook ------------------------------------------------------------------------------- Inspired by Andy Polyakov's http://fy.chalmers.se/~appro/linux/DVD+RW/tools , backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/ and by experiments with drives NEC ND-4570A and LG GSA-4082B. For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> ------------------------------------------------------------------------------- Media type can be recognized by Current Profile from 46h GET CONFIGURATION. (mmc5r03c.pdf 6.6.2.1) DVD-R 0011h DVD-RW Restricted Overwrite 0013h DVD-RW Sequential Recording 0014h DVD-R/DL Sequential Recording 0015h (can only do single-session) There are two approaches for writing to sequential DVD-R[W]: DAO and Incremental. Not all media and drives offer Incremental which can do multi-session as with CD media and does not demand a predicted track size. DAO seems to be the older method. It can only write one single session and track, and it demands an exactly predicted track size. - About overwriteable, blank, appendable and finalized DVD-R[W] media - Incremental writing - DAO writing - Obtaining DVD-R[W] multi-session info for extending ISO-9660 filesystems - Obtaining a Table Of Content from DVD-R[W] - Hearsay about DVD-R/DL (Dual Layer) ------------------------------------------------------------------------------- About overwriteable, blank, appendable and finalized DVD-R[W] media : DVD-RW can be either in formatted state Restricted Overwrite or in unformatted state Sequential Recording. Sequential media can be either blank, appendable or finalized. Only blank and appendable media are sequentially writeable. For overwriteable DVD-RW see the Overwriteable DVD Cookbook. Overwriteable DVD-RW can be detected by their profile number 0013h in contrast to profile number 0014h for sequential DVD-RW. The status of sequential media can be inquired like with CD by 51h READ DISC INFORMATION requesting Data Type 000b Standard Disc Information, where reply value Disc Status indicates: 00b blank 01b appendable 10b finalized 11b others (unsuitable for this recipe) (mmc5r03c.pdf 6.22.3.1.4) Finalized, appendable or overwriteable DVD-RW can be brought into blank sequential state by command A1h BLANK with blanking type 000b "Blank the disc". See TAO Multi-Session CD Cookbook for details about blanking. After minimal blanking (type 001b) DVD-RW my two drives do not offer the Incremental Streaming feature 0021h the media any more. Full blanking (000b) brings back this feature. (mmc5r03c.pdf 6.2) ------------------------------------------------------------------------------- Incremental writing : Incremental writing can produce multi-session DVDs. It is indicated by feature 0021h being marked current in the reply of 46h GET CONFIGURATION. growisofs inquires 0021h by setting Starting Feature Number to 0x21 and Allocation Length to 16 in order to get only this one. The feature descriptor begins at byte 8 of the reply. Its availability is indicated by the Current Bit. libburn obtains the full feature list for this and other info. (mmc5r03c.pdf 5.2.2. Feature Descriptor format, 5.3.11 Feature 0021h, 6.2 46h GET CONFIGURATION, ) In mode page 05h this method is selected by Write Type 00h. Speed can be influenced by B6h SET STREAMING , speed capabilities can be inquired by ACh GET PERFORMANCE. It is advised to set only speeds and sizes which are returned by ACh. (mmc5r03c.pdf 6.39 SET STREAMING, 6.8 GET PERFORMANCE) growisofs fetches a mode page 05h template by MODE SENSE and inserts its own parameters. It sets Multi-session to 11b, unless dvd_compat is nonzero. libburn composes its mode page 05h from zero and enables the application to control Multi-Session. BUFE Buffer Underrun protection 0=off, 1=on LS_V Link size valid 1=true Test Write -dummy mode for writing 0=off, 1=on Write Type Packet/TAO/SAO/RAW 00h = Incremental (Packet) Multi-session Whether to keep appendable 00b = finalize 11b = keep appendable Track Mode Describes frame type 5 [*1] Data Block Type Layout of payload blocks 8 [*2] Link Size ??? 16 [*3] FP Fixed Packet Size Bit 1 Packet Size 16 [*4] (mmc5r03c.pdf 7.5.4 The Mode Page, 4.2.3.4 Table 17 CONTROL = Track Mode) (spc3r23.pdf 6.8 MODE SELECT, 7.4.3 Mode parameter header formats) [*1:] growisofs takes the Track Mode from 52h READ TRACK INFORMATION, Address/Number Type 1, Track 1, Track Information Block byte 5 & 0xf. (mmc5r03.pdf 6.27) The specs predict that this will be Track Mode 4 (6.27.3.8) and also state that default is 5 (7.5.4.12). 4 means: uninterrupted, do not copy. 5 means increment, do not copy. [*2:] 8 means: 2048 byte data blocks. growisofs sets this value if Data Mode from above 52h READ TRACK INFORMATION is 1 or Fh, which is predicted by the specs to be always true. (mmc5r03.pdf 6.27.3.10) [*3:] growisofs (transport.hxx) sets Link Size to 16 for profiles 0011h and 0014h. libburn now records the first link size from feature 0021h in its burn_drive structure. If another link size item is 16, then 16 is used. [*4:] growisofs takes Packet Size from 52h. Specs predict it will be 16 (= 32 kiB). (mmc5r03.pdf 7.5.4.16) The writing process is much like in "Writing a session to CD in TAO mode" : Next Writeable Address is fetched from the reply of 52h READ TRACK INFORMATION. libburn writes full 32 kiB buffers via 2Ah WRITE. (mmc5r03c.pdf, 6.27 READ TRACK INFORMATION, 6.44 WRITE) When writing is done, it is mandatory to force the drive's buffer to media by 35h SYNCHRONIZE CACHE. (mmc5r03c.pdf, 6.41) The track has to be closed by 5Bh CLOSE TRACK SESSION Close Function 001b. growisofs uses the logical track number for that and not FFh like libburn does with TAO CD. So libburn obtains the Last Track Number in Last Session from the reply of 51h READ DISC INFORMATION requesting Data Type 000b "Standard Disc Information". (mmc5r03c.pdf 6.3.3.2.2 CLOSE TRACK, 6.22.3.1.) Multiple tracks are permissible in a single session. After all of them have been written, 5Bh CLOSE TRACK SESSION Close Function 010b with Logical Track Number 0 closes the session. It depends on the Multi-Session value in mode page 05h whether the disc is finalized or stays appendable. (mmc5r03c.pdf 6.3.3.2.3) ------------------------------------------------------------------------------- DAO writing : DAO is the mode described by feature 002Fh. This feature also gives information about capabilities for Burnfree (BUF), Test Write and DVD-RW. (mmc5r03c.pdf 5.3.25) Experiments with growisofs showed that the track size needs to be predicted and may not be exceeded during the write process. (growisofs ran into SCSI errors with piped non-ISO-9660 images and with piped ISO-9660 which have trailing data.) Speed can be influenced by B6h SET STREAMING , speed capabilities can be inquired by ACh GET PERFORMANCE. It is advised to set only speeds and sizes which are returned by ACh. (mmc5r03c.pdf 6.39 SET STREAMING, 6.8 GET PERFORMANCE) The mode page 05h to be sent : BUFE Buffer Underrun protection 0=off, 1=on LS_V Link size valid 0=false [*3] Test Write -dummy mode for writing 0=off, 1=on Write Type Packet/TAO/SAO/RAW 02h = DAO (same code as SAO) Multi-session Whether to keep appendable 00b = finalize Track Mode Describes frame type 5 [*1] Data Block Type Layout of payload blocks 8 [*2] Link Size ??? 0 [*3] FP Fixed Packet Size Bit 0 [*3] Packet Size 0 [*3] (mmc5r03c.pdf 7.5.4 The Mode Page, 4.2.3.4 Table 17 CONTROL = Track Mode) (spc3r23.pdf 6.8 MODE SELECT, 7.4.3 Mode parameter header formats) [*1:] growisofs takes the Track Mode from 52h READ TRACK INFORMATION, Address/Number Type 1, Track 1, Track Information Block byte 5 & 0xf. (mmc5r03.pdf 6.27) [*2:] 8 means: 2048 byte data blocks. growisofs sets this value if Data Mode from above 52h READ TRACK INFORMATION is 1 or Fh, which is predicted by the specs to be always true. (If not: growisofs aborts.) (mmc5r03.pdf 6.27.3.10) [*3:] Link Size, Packet Size and their companions only apply to Write Type 00h. The session layout must be described by 53h RESERVE TRACK, RMZ=ARSV=0. Reservation size should better already be aligned to 32 KiB. It has not been tested yet, what happens if not enough data get written. (mmc5r03c.pdf 6.31) Next Writeable Address is fetched from the reply of 52h READ TRACK INFORMATION. The reply is supposed to be 0. libburn writes full 32 kiB buffers via 2Ah WRITE. (mmc5r03c.pdf, 6.27 READ TRACK INFORMATION, 6.44 WRITE) If the track source delivers less than the announced size then libburn pads up by zeros. When writing is done, it is mandatory to force the drive's buffer to media by 35h SYNCHRONIZE CACHE. (mmc5r03c.pdf, 6.41) No further finalization is necessary. (I.e. no 5Bh CLOSE TRACK SESSION.) ------------------------------------------------------------------------------- Obtaining DVD-R[W] multi-session info for extending ISO-9660 filesystems : (valid for DVD+R too) Like with CD it is necessary to obtain the two numbers for mkisofs option -C in order to prepare a ISO-9660 filesystem image which by its inner pointers matches the block addresses of the future location on media. These are the start address of the first track in the last complete session and the predicted start address of the track which will host the new image. See TAO Multi-Session CD Cookbook for some more info about mkisofs aspects. The first number may be gained by 43h READ TOC/PMA/ATIP Format 0001b which in table 478 promises quick access via Start Address Of First Track In Last Session. (mmc5r03c.pdf 6.26.2.5 table 478, 6.26.3.3.1) Regrettably the MMC-5 specs still define a useless reply for non-CD media which obviously stems from MMC-3 times when no multi-session was possible with non-CD. (mmc5r03c.pdf 6.26.3.3.3) Both my drives do give a useful reply with the correct number for appendable DVD-RW. But not being backed by the specs this method appears unappealing . Another approach would be a formatted Table of Content, obtained by 43h READ TOC/PMA/ATIP Format 0000b. The specs do not totally outrule that this returns useful data with non-CD but they define a crippled TOC for multi-session. (mmc5r03c.pdf 6.26.3.2.4) My LG drive returns a more detailed TOC, my NEC drive stays with the rather suboptimal specs. So one would get different TOCs on different drives. Nevertheless, the MMC-5 compliant TOC would return the desired number in the Track Start address of the track with the highest number before AAh. Most stable seems the approach to obtain the desired number from the reply of 52h READ TRACK INFORMATION, Address/Number Type 01b. The field Logical Block Address/Track/Session has to bear the track number of the first track in the last complete session. To determine this number one has to determine the number of the last session and the number of the last track from 51h READ DISC INFORMATION and to iterate over the tracknumber by 52h READ TRACK INFORMATION until the first track with the desired session number appears and reveils its start address. (mmc5r03c.pdf 6.22 51h DISC, 6.27 52h TRACK) This method is very near to fabricating an own TOC. So libburn does this when inspecting the media. If the first number for -C is needed, libburn inquires its TOC model for the address of the first track in the last complete session. See below for a detailed description of TOC fabrication. The second -C number is the exact prediction of future track start address. It is gained like with CD by 52h READ TRACK INFORMATION Type 01b. Different from CD one may not use track number FFh but has to use the Last Track in Last Session from 51h READ DISC INFORMATION. (mmc5r03c.pdf 6.22 51h DISC, 6.27 52h TRACK) ------------------------------------------------------------------------------- Obtaining a Table Of Content from DVD-R[W]: (valid for DVD+R too) The raw TOC entries from 43h READ TOC/PMA/ATIP Format 0010b as described with CD media are not available with non-CD. There is a Format 0000b "Formatted TOC" but this is with non-CD a fictional information much at the discretion of the drive. Two drives with the same disc may well return different Formatted TOC. They are supposed to be consistent only about the last complete session and even there the MMC-5 specification 6.26.3.2.5 seems to prescribe a structure which does not match the true structure of incremental writing to sequential DVD-R[W]. (mmc5r03c.pdf 6.26.3.2) So i prefer not to use this method of getting a TOC. The alternative is to produce an own TOC from information gained by 51h READ DISC INFORMATION and by 52h READ TRACK INFORMATION which reveil a CD-like structure of sessions and 1:n related tracks. 51h READ DISC INFORMATION Data Type 000b, fields Number of Sessions (Least Significant Byte) and Number of Sessions (Most Significant Byte) give the number of sessions. The last complete session number of an appendable disc is one less because there is an incomplete session at its end. libburn only records complete sessions in its TOC model. libburn uses Last Track in Last Session as a hint for the range of track numbers. (mmc5r03c.pdf 6.22) Next step is to iterate from 1 up to the last track number and to obtain the according track info by 52h READ TRACK INFORMATION. Each track tells its Session Number (LSB at byte 2, MSB at 33), its Logical Track Start Address, its Logical Track Size, and much more which is not needed for a fake CD TOC. One may analyze the track info more finely but for this special purpose it is enough to discard the tracks which do not belong to complete sessions. (mmc5r03c.pdf 6.27) At the end of each session libburn inserts fake leadout entries into its TOC model. Their start address is computed from the start and size of the last track of the session. ------------------------------------------------------------------------------- Hearsay about DVD-R/DL (Dual Layer) : Meanwhile confirmed by one user: DVD-R/DL can assume profile 0015h DVD-R Dual Layer Sequential which is supposed to behave like DVD-R or 0016h DVD-R Dual Layer Jump which has no counterpart with DVD-R. A half-sentence in mmc5r03c.pdf 6.3.3.3.3 indicates that closing a session by 5Bh CLOSE TRACK SESSION Close Function 010b overrides the multi-session bits in mode page 05h. growisofs applies this function in case of not DAO, though. A comment in growisofs_mmc.cpp states: "// DVD-R DL Seq has no notion of multi-session". I am not reading this from the specs - but not explicitely the contrary either. For now libburn will close the session but there is a macro prepared in libburn/write.c Libburn_dvd_r_dl_multi_no_close_sessioN which will suppress close session if multi-session is demanded. ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- DVD+R[/DL] Cookbook ------------------------------------------------------------------------------- Inspired by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/ backed by Andy Polyakov's http://fy.chalmers.se/~appro/linux/DVD+RW/tools , For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> ------------------------------------------------------------------------------- Media type can be recognized by Current Profile from 46h GET CONFIGURATION. (mmc5r03c.pdf 6.6.2.1) DVD+R 001bh DVD+R/DL 002bh - About empty, appendable and finalized DVD+R - Writing a Pseudo Session to DVD+R - DVD+R/DL (Dual Layer The following two chapters of the Sequential DVD-R[W] Cookbook are valid for DVD+R media too: - Obtaining DVD-R[W] multi-session info for extending ISO-9660 filesystems - Obtaining a Table Of Content from DVD-R[W] ------------------------------------------------------------------------------- About blank, appendable and finalized DVD+R : In the beginning a DVD+R holds an empty session and the Incomplete Fragment. From these one may spawn reserved fragments or one may write directly to the incomplete fragment. As soon as this is done the empty session becomes the open session which finally needs to get closed. By closing fragments and session a new empty session with empty Incomplete Fragment gets spawned. So the disc stays appendable. A DVD+R may hold 153 closed sessions with a single track each. The open session may hold up to 15 open fragments. But on closure of the session those fragments together form a single logical track. So one will usually only use a single fragment for sequential writing. (mmc5r03c.pdf 4.3.6.2) The disc may get finalized by another close command so that no more data can be written. (mmc5r03c.pdf 6.3.3.4.4) ------------------------------------------------------------------------------- Writing a Pseudo Session to DVD+R : Session writing has to be pseudo because only one logical track per session can be distinguished. So actually there have to be written multiple sessions to mark multiple tracks. The pseudo session cannot get marked on disc and thus the tracks of a pseudo session cannot be grouped accordingly in a TOC. Speed can be influenced by B6h SET STREAMING , speed capabilities can be inquired by ACh GET PERFORMANCE. It is advised to set only speeds and sizes which are returned by ACh. (mmc5r03c.pdf 6.39 SET STREAMING, 6.8 GET PERFORMANCE) No mode page 05h is to be sent. growisofs sends a page but the specs clearly state that one shall not do. (mmc5r03c.pdf 7.5.3) It is optional whether a track size is reserved in advance or not. Eventually this is done by 53h RESERVE TRACK, RMZ=ARSV=0. Reservation size should better already be aligned to 32 KiB. (mmc5r03c.pdf 6.31) The specs promise to pad up the track if not enough data get written. (mmc5r03c.pdf 6.3.3.4.2) Next Writeable Address is fetched from the reply of 52h READ TRACK INFORMATION with track number FFh. (mmc5r03c.pdf 6.27) Since the fixely set write type is 16-block packet, full 32 kiB buffers have to be transmitted via 2Ah WRITE. (mmc5r03c.pdf 4.3.6.2.2) When writing is done, it is mandatory to force the drive's buffer to media by 35h SYNCHRONIZE CACHE. (mmc5r03c.pdf 6.41) The written fragment (i.e. track-to-be) has to be closed by 5Bh CLOSE TRACK SESSION Close Function 001b. (mmc5r03c.pdf 6.3.3.4.2) libburn obtains the necessary logical track number from Last Track Number in Last Session from the reply of 51h READ DISC INFORMATION requesting Data Type 000b. (mmc5r03c.pdf 6.22) After each track 5Bh CLOSE TRACK SESSION Close Function 010b with Logical Track Number 0 closes the DVD+R session but keeps the media appendable. (mmc5r03c.pdf 6.3.3.4.3) If the media shall not stay appendable then the last DVD+R session is to be closed by Close Function 101b rather than 010b. This finalizes the media "with minimal radius". (mmc5r03c.pdf 6.3.3.4.4) Note: growisofs has code for that gesture but explicitly avoids to use it, if the media was appendable before writing began. Instead it recommends to fill up the media with zeros. This gesture nevertheless caused error replies from the drives in my own experiments. The reason given by Andy Polyakov is that some DVD-ROM drives get mislead by the lead-out information of (formerly) appendable media unless the media is fully written. (http://fy.chalmers.se/~appro/linux/DVD+RW/ , "Compatibility: caveat lector") Own experiments showed no such problems with PC attached PATA DVD-ROM drives. For best DVD-ROM compatibility one should avoid appendable media at all by closing them already after the first session. ------------------------------------------------------------------------------- DVD+R/DL (Dual Layer) : libburn treats DL media just like their single layer equivalents. This seems to work fine for DVD+R/DL, according to a report by nightmorph in http://libburnia-project.org/ticket/13 . ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- BD-R Cookbook ------------------------------------------------------------------------------- Inspired by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/ backed by experiments iwith drive LG GGW H20L. For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> ------------------------------------------------------------------------------- Media type can be recognized by Current Profile from 46h GET CONFIGURATION. (mmc5r03c.pdf 6.6.2.1) BD-R 0042h There are two basic recording modes defined: Sequential Recording Mode SRM and Random Recording Mode RRM. The latter is optional and for now not topic of this text. (mmc5r03c.pdf 4.5.3.5) - SRM Formatting - Writing a session in SRM-POW (- Pseudo-OverWrite SRM+POW) ------------------------------------------------------------------------------- SRM Formatting: Despite being write-once media BD-R can optionally carry some formatting. SRM has a disc structure model with tracks and sessions. Several tracks may be open at the same time, each having its own NWA. (mmc5r03c.pdf 4.5.3.5.2.2) This structure is formatted onto blank media automatically as soon as the first serious write attempt occurs. (mmc5r03c.pdf 4.5.3.5) Before such a write attempt, blank media may be explicitely formatted with spares, which provide defect management. (mmc5r03c.pdf 4.5.3.5.3) Tracks get created from other tracks via RESERVE TRACK splitting. (mmc5r03c.pdf 4.5.3.5.2.5) On top of defect management there may be Pseudo-OverWrite SRM+POW, a costly way to write several times to the same LBA. See below. Fully sequential states are called SRM-POW. (mmc5r03c.pdf 4.5.3.5.4) Explicite formatting is done by 04h FORMAT UNIT. Its data payload consists of a Format List Header and a Format Descriptor. It is advisable to set the Immed bit and the FOV bit in header byte number 1. The descriptor should be a copy of a descriptor from 23h READ FORMAT CAPACITIES but the size may be adjusted within a certain range. (mmc5r03c.pdf 6.5, 6.5.3.2, 6.5.3.3) Format type 00h creates SRM layouts with a default number of spares (or eventually RRM) chosen by the format sub-type: 00b = SRM with support for POW 01b = SRM without POW (but with some spares for defect management) 10b = (RRM) (mmc5r03c.pdf 6.5.4.2.1.6) Format type 32h uses the same sub-types but can allocate non-default amounts of spares. Similar to BD-RE format 31h, three format descriptors are offered: #1: default size, #2: maximum spare area, #3: minimal spare. The size may be chosen within that range. The sense behind the Type Dependent Parameters is obscure to me. Best will be to set ISA_V and TDMA_V to 0. (mmc5r03c.pdf 6.5.4.2.1.17) ------------------------------------------------------------------------------- Writing a session in SRM: The procedure and constraints for writing BD-R SRM-POW are very similar to DVD+R. libburn flatly re-uses its DVD+R code except the Close Function for finalizing a disc. In short: If all written sessions are closed, then there is exactly one NWA. In the beginning there is an empty session and track. A new track can be written either with pre-announced size (by RESERVE TRACK) or open-end by simply starting to write to the NWA. When done the track gets closed by close function 001b. Then either session or disc gets closed depending on the Close Function used: - Close Function 010b closes the session and keeps the media appendable (same as with DVD+R) - Close Function 110b finalizes the media and makes it read-only. (differs from libburn DVD+R procedure which uses 101b) ------------------------------------------------------------------------------- Pseudo-OverWrite POW: (no used yet by libburn) This enhancement of SRM emulates overwriting of existing data blocks. (mmc5r03c.pdf 4.5.3.5.4) POW establishes a virtual vLBA space on top of the real address space rLBA. All read and write commands deal with vLBA. It seems that track NWAs are assumed to be identical in vLBA space and in rLBA space. It is not clear whether one may write to vLBA blocks which are neither written yet nor at one of the track NWAs. Probably not, or else one could make NWAs run into vLBAs which are associated with older rLBAs. Replacing invalidated blocks consumes addresses in rLBA space at the NWA of some track. I.e. no spares are consumed by POW. Nevertheless it is costly by a special map called Orphanage. It covers rLBA which have been consumed by differing vLBAs. It never shrinks and can grow with each write to remapped addresses. To avoid heavy Orphanage growth it is advised to write mostly to vLBA which still coincide with their rLBA. E.g. those addresses which have neither been written as rLBA nor as vLBA yet. So one should begin the vLBA of new sessions at the NWA of a sufficiently sized track. (mmc5r03c.pdf 4.5.3.5.4.2 , 4.5.3.6.9) ------------------------------------------------------------------------------- This text is copyright 2011 - 2012 Thomas Schmitt <scdbackup@gmx.net>. Permission is granted to copy, modify, and distribute it, as long as the references to the original information sources are maintained. There is NO WARRANTY, to the extent permitted by law. ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- Users of modern desktop Linux installations report misburns with CD/DVD recording due to concurrency problems. This text describes two locking protocols which have been developed by our best possible effort. But finally they rather serve as repelling example of what would be needed in user space to achieve an insufficient partial solution. Ted Ts'o was so friendly to help as critic with his own use cases. It turned out that we cannot imagine a way in user space how to cover reliably the needs of callers of libblkid and the needs of our burn programs. ------------------------------------------------------------------------------- Content: The "Delicate Device Locking Protocol" shall demonstrate our sincere consideration of the problem. "What are the Stumble Stones ?" lists reasons why the effort finally failed. ----------------------------------------------------------------------------- Delicate Device Locking Protocol (a joint sub project of cdrkit and libburnia) (contact: scdbackup@gmx.net ) Our projects provide programs which allow recording of data on CD or DVD. We encounter an increasing number of bug reports about spoiled burn runs and wasted media which obviously have one common cause: interference by other programs which access the drive's device files. There is some riddling about which gestures exactly are dangerous for ongoing recordings or can cause weirdly misformatted drive replies to MMC commands. We do know, nevertheless, that these effects do not occur if no other program accesses a device file of the drive while our programs use it. DDLP shall help to avoid collisions between programs in the process of recording to a CD or DVD drive and other programs which access that drive. The protocol intends to provide advisory locking. So any good-willing program has to take some extra precautions to participate. If a program does not feel vulnerable to disturbance, then the precautions impose much less effort than if the program feels the need for protection. Two locking strategies are specified: DDLP-A operates on device files only. It is very Linux specific. DDLP-B adds proxy lock files, inspired by FHS /var/lock standard. DDLP-A This protocol relies on the hardly documented feature open(O_EXCL | O_RDWR) with Linux device files and on POSIX compliant fcntl(F_SETLK). Other than the original meaning of O_EXCL with creating regular files, the effect on device files is mutual exclusion of access. I.e. if one filedescriptor is open on that combination of major-minor device number, then no other open(O_EXCL) will succeed. But open() without O_EXCL would succeed. So this is advisory and exclusive locking. With kernel 2.6 it seems to work on all device drivers which might get used to access a CD/DVD drive. The vulnerable programs shall not start their operation before they occupied a wide collection of drive representations. Non-vulnerable programs shall take care to detect the occupation of _one_ such representation. So for Friendly Programs A program which does not feel vulnerable to disturbance is urged to access CD/DVD drives by opening a file descriptor which will uphold the lock as long as it does not get closed. There are two alternative ways to achieve this. Very reliable is open( some_path , O_EXCL | ...) But O_EXCL imposes restrictions and interferences: - O_EXCL | O_RDONLY does not succeed with /dev/sg* ! - O_EXCL cannot provide shared locks for programs which only want to lock against burn programs but not against their own peers. - O_EXCL keeps from obtaining information by harmless activities. - O_EXCL already has a meaning with devices which are mounted as filesystems. This priority meaning is more liberal than the one needed for CD/DV recording protection. So it may be necessary to use a cautious open() without O_EXCL and to aquire a POSIX lock via fcntl(). "Cautious" means to add O_NDELAY to the flags of open(), because this is declared to avoid side effects within open(). With this gesture it is important to use the paths expected by our burn programs: /dev/sr[0..255] /dev/scd[0..255] /dev/sg[0..255] /dev/hd[a..z] because fcntl(F_SETLK) does not lock the device but only a device-inode. std_path = one of the standard device files: /dev/sr[0..255] /dev/scd[0..255] /dev/sg[0..255] /dev/hd[a..z] or a symbolic link pointing to one of them. open( std_path , ... | O_NDELAY) fcntl(F_SETLK) and close() on failure ... eventually disable O_NDELAY by fcntl(F_SETFL) ... There is a pitfall mentioned in man 2 fcntl : "locks are automatically released [...] if it closes any file descriptor referring to a file on which locks are held. This is bad [...]" So you may have to re-lock after some temporary fd got closed. Vulnerable Programs For programs which do feel vulnerable, O_EXCL would suffice for the /dev/hd* device file family and their driver. But USB and SATA recorders appear with at least two different major-minor combinations simultaneously. One as /dev/sr* alias /dev/scd*, the other as /dev/sg*. The same is true for ide-scsi or recorders attached to SCSI controllers. So, in order to lock any access to the recorder, one has to open(O_EXCL) not only the device file that is intended for accessing the recorder but also a device file of any other major-minor representation of the recorder. This is done via the SCSI address parameter vector (Host,Channel,Id,Lun) and a search on standard device file paths /dev/sr* /dev/scd* /dev/sg*. In this text the alternative device representations are called "siblings". For finding them, it is necessary to apply open() to many device files which might be occupied by delicate operations. On the other hand it is very important to occupy all reasonable representations of the drive. So the reading of the (Host,Channel,Id,Lun) parameters demands an open(O_RDONLY | O_NDELAY) _without_ fcntl() in order to find the outmost number of representations among the standard device files. Only ioctls SCSI_IOCTL_GET_IDLUN and SCSI_IOCTL_GET_BUS_NUMBER are applied. Hopefully this gesture is unable to cause harmful side effects on kernel 2.6. At least one file of each class sr, scd and sg should be found to regard the occupation as satisfying. Thus corresponding sr-scd-sg triplets should have matching ownerships and access permissions. One will have to help the sysadmins to find those triplets. A spicy detail is that sr and scd may be distinct device files for the same major-minor combination. In this case fcntl() locks on both are needed but O_EXCL can only be applied to one of them. An open and free implementation ddlpa.[ch] is provided as http://libburnia.pykix.org/browser/libburn/trunk/libburn/ddlpa.h?format=txt http://libburnia.pykix.org/browser/libburn/trunk/libburn/ddlpa.c?format=txt The current version of this text is http://libburnia.pykix.org/browser/libburn/trunk/doc/ddlp.txt?format=txt Put ddlpa.h and ddlpa.c into the same directory and compile as test program by cc -g -Wall -DDDLPA_C_STANDALONE -o ddlpa ddlpa.c Use it to occupy a drive's representations for a given number of seconds ./ddlpa /dev/sr0 300 It should do no harm to any of your running activities. If it does: Please, please alert us. Your own programs should not be able to circumvent the occupation if they obey above rules for Friendly Programs. Of course ./ddlpa should be unable to circumvent itself. A successfull occupation looks like DDLPA_DEBUG: ddlpa_std_by_rdev("/dev/scd0") = "/dev/sr0" DDLPA_DEBUG: ddlpa_collect_siblings() found "/dev/sr0" DDLPA_DEBUG: ddlpa_collect_siblings() found "/dev/scd0" DDLPA_DEBUG: ddlpa_collect_siblings() found "/dev/sg0" DDLPA_DEBUG: ddlpa_occupy() : '/dev/scd0' DDLPA_DEBUG: ddlpa_occupy() O_EXCL : '/dev/sg0' DDLPA_DEBUG: ddlpa_occupy() O_EXCL : '/dev/sr0' ---------------------------------------------- Lock gained ddlpa: opened /dev/sr0 ddlpa: opened siblings: /dev/scd0 /dev/sg0 slept 1 seconds of 300 Now an attempt via device file alias /dev/NEC must fail: DDLPA_DEBUG: ddlpa_std_by_rdev("/dev/NEC") = "/dev/sg0" DDLPA_DEBUG: ddlpa_collect_siblings() found "/dev/sr0" DDLPA_DEBUG: ddlpa_collect_siblings() found "/dev/scd0" DDLPA_DEBUG: ddlpa_collect_siblings() found "/dev/sg0" Cannot exclusively open '/dev/sg0' Reason given : Failed to open O_RDWR | O_NDELAY | O_EXCL : '/dev/sr0' Error condition : 16 'Device or resource busy' With hdc, of course, things are trivial DDLPA_DEBUG: ddlpa_std_by_rdev("/dev/hdc") = "/dev/hdc" DDLPA_DEBUG: ddlpa_occupy() O_EXCL : '/dev/hdc' ---------------------------------------------- Lock gained ddlpa: opened /dev/hdc slept 1 seconds of 1 Ted Ts'o provided program open-cd-excl which allows to explore open(2) on device files with combinations of read-write, O_EXCL, and fcntl(). (This does not mean that Ted endorsed our project yet. He helps exploring.) Friendly in the sense of DDLP-A would be any run which uses at least one of the options -e (i.e. O_EXCL) or -f (i.e. F_SETLK, applied to a file descriptor which was obtained from a standard device file path). The code is available under GPL at http://libburnia.pykix.org/browser/libburn/trunk/test/open-cd-excl.c?format=txt To be compiled by cc -g -Wall -o open-cd-excl open-cd-excl.c Options: -e : open O_EXCL -f : aquire lock by fcntl(F_SETLK) after sucessful open -i : do not wait in case of success but exit 0 immediately -r : open O_RDONLY , with -f use F_RDLCK -w : open O_RDWR , with -f use F_WRLCK plus the path of the devce file to open. Friendly Programs would use gestures like: ./open-cd-excl -e -r /dev/sr0 ./open-cd-excl -e -w /dev/sg1 ./open-cd-excl -e -w /dev/black-drive ./open-cd-excl -f -r /dev/sg1 ./open-cd-excl -e -f -w /dev/sr0 Ignorant programs would use and cause potential trouble by: ./open-cd-excl -r /dev/sr0 ./open-cd-excl -w /dev/sg1 ./open-cd-excl -f -w /dev/black-drive where "/dev/black-drive" is _not_ a symbolic link to any of /dev/sr* /dev/scd* /dev/sg* /dev/hd*, but has an own inode. Prone to failure without further reason is: ./open-cd-excl -e -r /dev/sg1 ---------------------------------------------------------------------------- DDLP-B This protocol relies on proxy lock files in some filesystem directory. It can be embedded into DDLP-A or it can be used be used standalone, outside DDLP-A. DDLP-A shall be kept by DDLP-B from trying to access any device file which might already be in use. There is a problematic gesture in DDLP-A when SCSI address parameters are to be retrieved. For now this gesture seems to be harmless. But one never knows. Vice versa DDLP-B may get from DDLP-A the service to search for SCSI device file siblings. So they are best as a couple. But they are not perfect. Not even as couple. fcntl() locking is flawed. There is a proxy file locking protocol described in FHS: http://www.pathname.com/fhs/pub/fhs-2.3.html#VARLOCKLOCKFILES But it has shortcommings (see below). Decisive obstacle for its usage are the possibility for stale locks and the lack of shared locks. DDLP-B rather defines a "path prefix" which is advised to be /tmp/ddlpb-lock- This prefix will get appended "device specific suffixes" and then form the path of a "lockfile". Not the existence of a lockfile but its occupation by an fcntl(F_SETLK) will constitute a lock. Lockfiles may get prepared by the sysadmin in directories where normal users are not allowed to create new files. Their rw-permissions then act as additional access restriction to the device files. The use of fcntl(F_SETLK) will prevent any stale locks after the process ended. It will also allow to obtain shared locks as well as exclusive locks. There are two classes of device specific suffixes: - Device file path suffix. Absolute paths only. "/" gets replaced by "_-". Eventual "_-" in path gets replaced by "_-_-". The leading group of "_-" is always interpreted as a group of "/", though. E.g.: /dev/sr0 <-> "_-dev_-sr0" /mydevs/burner/nec <-> "_-mydevs_-burners_-nec" /dev/rare_-name <-> "_-dev_-rare_-_-name" ///strange/dev/x <-> "_-_-_-strange_-dev_-x" - st_rdev suffix. A hex representation of struct stat.st_rdev. Capital letters. The number of characters is pare with at most one leading 0. I.e. bytewise printf("%2.2X") beginning with the highest order byte that is not zero. E.g. : "0B01", "2200", "01000000000004001" If a lockfile does not exist and cannot be created then this shall not keep a program from working on a device. But if a lockfile exists and if permissions or locking state do not allow to obtain a lock of the appropirate type, then this shall prevent any opening of device file in question and shall cause immediate close(2) of an already opened device file. The vulnerable programs shall not start their operation before they locked a wide collection of drive representations. Non-vulnerable programs shall take care to lock the suffix resulting from the path they will be using and the suffix from the st_rdev from that path. The latter is to be obtained by call stat(2). Locks get upheld as long as their file descriptor is not closed or no other incident as described in man 2 fcntl releases the lock. So with shared locks there are no imandatory further activities after they have been obtained. In case of exclusive locks, the file has to have been opened for writing and must be truncated to 0 bytes length immediately after obtaining the lock. When releasing an exclusive lock it is a nice gesture to already do this truncation. Then a /var/lock/ compatible first line has to be written. E.g. by: printf("%10u\n",(unsigned) getpid()) yielding " 1230\n". Any further lines are optional. They shall have the form Name=Value and must be printable cleartext. If such further lines exist, then the last one must have the name "endmark". Defined Names are: hostid =hostname of the machine where the process number of line 1 is valid start =start time of lock in seconds since 1970. E.g: 1177147634.592410 program =self chosen name of the program which obtained the lock argv0 =argv[0] of that program mainpath =device file path which will be used for operations by that program path =device file path which lead to the lock st_rdev =st_rdev suffix which is associated with path scsi_hcil=eventual SCSI parameters Host,Channel,Id,Lun scsi_bus =eventual SCSI parameter Bus endmark =declares the info as complete. Any undefined name or a line without "=" shall be handled as comment. "=" in the value is allowed. Any line beginning with an "=" character is an extension of the previous value. If programs encounter an exclusive lock, they are invited to read the content of the lockfile anyway. But they should be aware that the info might be in the progress of emerging. There is a race condition possible in the short time between obtaining the exclusive lock and erasing the file content. If it is not crucial to obtain most accurate info then one may take the newline of the first line as indicator of a valid process number and the "endmark" name as indicator that the preceding lines are valid. Very cautious readers should obtain the info twice with a decent waiting period inbetween. Only if both results are identical they should be considered valid. There is no implementation of DDLP-B yet. ---------------------------------------------------------------------------- What are the Stumble Stones ? ---------------------------------------------------------------------------- Any of the considered locking mechanisms has decisive shortcommings which keeps it from being the solution to all known legitimate use cases. The attempt has failed to compose a waterproof locking mechanism from means of POSIX, FHS and from hardly documented Linux open(O_EXCL) on device files. The resulting mechanisms would need about 1000 lines of code and still do not close all gaps and cover the well motivated use cases. This attempt you see above: DDLP-A and DDLP-B. Summary of the reasons why the established locking mechanisms do not suffice: None of the mechanisms can take care of the double device driver identity sr versus sg. To deduce the one device file from the other involves the need to open many other (possibly unrelated) device files with the risk to disturb them. This hard to solve problem is aggravated by the following facts. Shortcommings of Linux specific open(O_EXCL) : - O_EXCL | O_RDONLY does not succeed with /dev/sg* - O_EXCL cannot provide shared locks for programs which only want to lock against burn programs but not against their own peers. - O_EXCL keeps from obtaining information by harmless activities. - O_EXCL already has a meaning with devices which are mounted as filesystems. This priority meaning is more liberal than the one needed for CD/DV recording protection. Shortcommings of POSIX fcntl(F_SETLK) : - fcntl() demands an open file descriptor. open(2) might have side effects. - fcntl() locks can be released inadvertedly by submodules which just open and close the same file (inode ?) without refering to fcntl locks in any way. See man 2 fcntl "This is bad:". Stacking of software modules is a widely used design pattern. But fcntl() cannot cope with that. Shortcommings of FHS /var/lock/ : - Stale locks are possible. - It is necessary to create a file (using the _old_ meaning of O_EXCL flag ?) but /var/lock/ might not be available early during system start and it often has restrictive permission settings. - There is no way to indicate a difference between exclusive and shared locks. - The FHS prescription relies entirely on the basename of the device file path. # Doxyfile 1.8.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed # in front of the TAG it is preceding . # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = @PACKAGE_NAME@ # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @PACKAGE_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, # Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, # Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. Note that you specify absolute paths here, but also # relative paths, which will be relative from the directory where doxygen is # started. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, # and language is one of the parsers supported by doxygen: IDL, Java, # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, # C++. For instance to make doxygen treat .inc files as Fortran files (default # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES (the # default) will make doxygen replace the get and set methods by a property in # the documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields or simple typedef fields will be shown # inline in the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO (the default), structs, classes, and unions are shown on a separate # page (for HTML and Man pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can # be an expensive process and often the same symbol appear multiple times in # the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too # small doxygen will become slower. If the cache is too large, memory is wasted. # The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid # range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 # symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if section-label ... \endif # and \cond section-label ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command <command> <input-file>, where <command> is the value of # the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. Do not use # file names with spaces, bibtex cannot handle them. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = libburn \ doc \ test # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = libburn.h \ comments \ libburner.c # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = test # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command <filter> <input-file>, where <filter> # is the value of the INPUT_FILTER tag, and <input-file> is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be ignored. # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = # If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = YES # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = OB \ OTK \ _ #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = doc/html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If left blank doxygen will # generate a default style sheet. Note that it is recommended to use # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this # tag will in the future become obsolete. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional # user-defined cascading style sheet that is included after the standard # style sheets created by doxygen. Using this option one can overrule # certain style aspects. This is preferred over using HTML_STYLESHEET # since it does not replace the standard style sheet and is therefor more # robust against future updates. Doxygen will copy the style sheet file to # the output directory. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely # identify the documentation publisher. This should be a reverse domain-name # style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> # Qt Help Project / Custom Filters</a>. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> # Qt Help Project / Filter Attributes</a>. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 200 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and # SVG. The default value is HTML-CSS, which is slower, but has the best # compatibility. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript # pieces of code that will be used on startup of the MathJax code. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. # There are two flavours of web server based search depending on the # EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for # searching and an index file used by the script. When EXTERNAL_SEARCH is # enabled the indexing and searching needs to be provided by external tools. # See the manual for details. SERVER_BASED_SEARCH = NO # When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain # the search results. Doxygen ships with an example indexer (doxyindexer) and # search engine (doxysearch.cgi) which are based on the open source search # engine library Xapian. See the manual for configuration details. EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will returned the search results when EXTERNAL_SEARCH is enabled. # Doxygen ships with an example search engine (doxysearch) which is based on # the open source search engine library Xapian. See the manual for configuration # details. SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the # SEARCHDATA_FILE tag the name of this file can be specified. SEARCHDATA_FILE = searchdata.xml # When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple # projects and redirect the results back to the right project. EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id # of to a relative location where the documentation can be found. # The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4 will be used. PAPER_TYPE = letter # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images # or other source files which should be copied to the LaTeX output directory. # Note that the files will be copied as-is; there are no commands or markers # available. LATEX_EXTRA_FILES = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- # If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files # that can be used to generate PDF. GENERATE_DOCBOOK = NO # The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be put in # front of it. If left blank docbook will be used as the default path. DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = DOXYGEN # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # If the EXTERNAL_PAGES tag is set to YES all external pages will be listed # in the related pages index. If set to NO, only the current project's # pages will be listed. EXTERNAL_PAGES = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # manageable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = NO # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES ------------------------------------------------------------------------------- Note: This is about how libburn operates optical drives. Not about how to operate libburn. The libburn API is described in libburn/libburn.h ------------------------------------------------------------------------------- Overview of class specific or individual media information especially manufacturer and exact product type Inspired by Andy Polyakov's http://fy.chalmers.se/~appro/linux/DVD+RW/tools , and by Joerg Schilling's http://cdrecord.berlios.de/private/cdrecord.html, backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/, ECMA-279 (DVD-R), ECMA-337 (DVD+RW), ECMA-349 (DVD+R), and by searching the web for media manufacturer IDs (see list of URLs below). For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> September 2009 ------------------------------------------------------------------------------- CD: The manufacturers vary the lead-in address in ATIP ATIP start of lead in: -12490 (97:15/35) Media sizes and nominal speeds often get expressed in the ATIP lead-out address. ATIP start of lead out: 359849 (79:59/74) (example media labeled "LITEON CD-RW 4x-12x", by "Nan-Ya Plastics Corporation") This is available only with profile 0x09 (CD-R) and 0x0A (CD-RW). CD-ROM media (0x08) cannot be inquired by command 43h READ TOC/PMA/ATIP. 6.26 READ TOC/PMA/ATIP Format 4 with MSF bit produces these data as described in 6.26.3.6 Response Format 0100b: ATIP. The minute, second, and frame number of lead-in can be read from byte 4 to 6 of the ATIP descriptor. The lead-out bytes are at position 8 to 10. The ATIP descriptor is preceded by a four byte header. I could not find out yet whether (lead-in,lead-out) is mandatorily unique for each particular media product. The parameters can be obtained via libburn by : burn_disc_read_atip(d), burn_drive_get_start_end_lba(d, start_lba, end_lba, 0), burn_lba_to_msf(start_lba, &m_li, &s_li, &f_li) and burn_lba_to_msf(end_lba, &m_lo, &s_lo, &f_lo) There seems to be no public and free list of manufacturers and codes. Nevertheless many people report codes and manufacturer names in the web. Especially helpful are media compatibility lists by drive manufacturers. The items from the CD media list below are the foundation of libburn API call burn_guess_cd_manufacturer() which rounds the lead-in address down to multiples of 10 frames and then looks them up in its list. ------------------------------------------------------------------------------- DVD + BD: dvd+rw-mediainfo uses MMC-5 6.23, ADh READ DISC STRUCTURE which returns media type specific information if the drive supports the particular Format Code. With DVD+ and BD there is a 8 character Manufacturer ID, a 3 character Media Id and a single character Media Revision. With DVD-R, ECMA-279 promises a Manufacturer ID of up to 18 characters. With DVD-RW, ECMA-338 gives up the meaning of the last six-pack. Both, DVD-RW RITEKW01 and DVD-R RITEKF1 bear unprintable characters there. With DVD-ROM and DVD-RAM there seem to be no such ids. 6.23.3.2.1 Format Code 00h: Physical Format Information Disk Category in bits 4 to 7 of byte 0 gives the "Book Type". MMC-5 Table 401 gives this list 0x0 DVD-ROM 0x1 DVD-RAM 0x2 DVD-R 0x3 DVD-RW 0x4 HD DVD-ROM 0x5 HD DVD-RAM 0x6 HD DVD-R 0x9 DVD+RW 0xA DVD+R 0xD DVD+RW DL 0xE DVD+R DL Bits 0 to 3 is Part Version "[revision %d]" 6.23.3.1.7 Format Code FFh: Disc Structure List Returns a list of 4-byte list entries: Byte0 gives an available format code. Byte1,bit6 confirms that it is readable. Bytes 2 and 3 predict the reply length. DVD+RW and DVD+R: dvd+rw-mediainfo prefers this if available: Format code 11h : "ADIP Information" dvd+rw-mediainfo: printf (" Media ID: %.8s/%.3s\n",dvd._11+23,dvd._11+31), = ECMA-337 (DVD+RW) and ECMA-349 (DVD+R) 14.4.2 Physical format information in ADIP, Table 3 byte 19: Disk Manufacturer ID byte 27: Media Type ID (byte 28: Product revision number) MMC-5 6.23.3.2.17 , Table 426 shows 4 byte header: MSB,LSB of Disc Structure Data Length 2 bytes reserved Two of my burners do not perform this operation on DVD+ media and reply [5 24 00] "Invalid field in cdb": 'HL-DT-ST' 'BD-RE GGW-H20L' 'HL-DT-ST' 'BDDVDRW GGC-H20L' The other one does it with the same media: 'TSSTcorp' 'CDDVDW SH-S203B' This refusal seems not uncommon. dvd+rw-mediainfo later does: Format code 00h : "Physical Format Information" dvd+rw-mediainfo: if (dvd_plus && !plus_mediaid_printed) printf (" Media ID: %.8s/%.3s\n",header+23,header+31); = ECMA-337 (DVD+RW) and ECMA-349 (DVD+R) 14.4.2 Physical format information in ADIP, Table 3 byte 19: Disk Manufacturer ID byte 27: Media Type ID (byte 28: Product revision number) MMC-5 6.23.3.2.1 , Table 400, Table 407 says for DVD+RW bytes 19 - 255 are a copy of bytes 19 -255 from ADIP dvd+rw-mediainfo applies this to DVD+R too DVD-R , DVD-RW, (DVD-R DL) : Format code 0Eh "Pre-recorded Information in Lead-in" dvd+rw-mediainfo: if (!dvd_plus && dvd_0E && dvd._e[4+16]==3 && dvd._e[4+24]==4) printf (" Media ID: %6.6s%-6.6s\n",dvd._e+4+17,dvd._e+4+25); = ECMA-279 (DVD-R) 27.3.7 Field ID3 to Field ID5 28.3.2.1 RMD Field 0, Table 20 - Copy of Pre-pit Information It appears that the reply format of MMC ADh format 0Eh is the data record shown in Table 20 of ECMA-279 with offset 22: Manufacturer ID at bytes 17 to 22, 25 to 30, 33 to 38. Bytes 16, 24, 32 have content 3, 4, 5. ECMA-338 (DVD-RW) says the same in: 29.3.3.1.1 Format1 RMD Field0 Table 24 - Copy of Pre-pit Information Manufacturer ID at bytes 17 to 22, 25 to 30, but not at 33 6.23.3.2.14 Format Code 0Eh: Pre-recorded Information in Lead-in Table 423 shows 4 byte header: MSB,LSB of Disc Structure Data Length 2 bytes reserved >>> still to evaluate: Format code 0Fh : "Unique Disc Identifier" = 6.23.3.2.15 Format Code 0Fh: Unique Disc Identifier DVD-RAM : dvd+rw-mediainfo lists no info about manufacturer or type. No format code of ADh READ DISC STRUCTURE promises such info for DVD-RAM. BD-R and BD-RE: dvd+rw-mediainfo: if (di[4+0]=='D' && di[4+1]=='I') printf (" Media ID: %6.6s/%-3.3s\n",di+4+100,di+4+106); = MMC-5 6.23.3.3.1 Format Code 00h: Disc Information (DI) Table 288 says that Media Type Code (byte 1 of CDB) for BD media is 1. Table 446 says that Disc Information is preceded by 4 bytes of header. Table 448 says that bytes 0 to 1 are Disc Information Identifier "DI". that bytes 8 to 10 are Disc Type Identifier BDO for BD-ROM, BDW for BD-RE, BDR for BD-R that bytes 100 to 105 are Disc Manufactuer ID that bytes 106 to 108 are Media Type ID that byte 111 is Product Revision Number Via libburn these manufacturer and media ids can be retrieved by API call burn_disc_get_media_id() as a single printable word product_id and as two printable words media_code1, media_code2. The latter can be translated into a manufacturer company name by API call burn_guess_manufacturer(). (Both calls work for CD, too. burn_disc_get_media_id() eventually calls burn_disc_read_atip().) =============================================================================== Collection of Media IDs Sources: [Gig] http://www.gigabyte.com.tw/FileList/WebPage/old_system_oddwebpage/media_support_list_r5232a.pdf [Aopen] http://download.aopen.com.tw/Download.aspx?RecNo=11411&Model=2597&Section=1&DL=yes [Msi] http://ru.msi.com/products/multimedia/optical/spec/recommend_disc.pdf [Btc] http://www.btc.com.tw/english/3-2-1MediaSupport_combo5216_cdr.htm [Mits] http://www.mitsubishielectric.com.au/assets/vis/DD16C48_Media_Compatibility.pdf [Hij] http://hijacker.rpc1.org/toshiba/SD-R5372V/MediaList_SD-R5372V_TU15.pdf [Teac] http://www.google.com/url?sa=t&source=web&ct=res&cd=4&url=http%3A%2F%2Fdspd.teac.de%2Ffileadmin%2Fredaktion%2Fdownloads%2Ffirmware%2Fmedialist%2Fmedialist_dvw50e_130.pdf&ei=0smfSt-TBNaosgbnm6wZ&rct=j&q=cd+%22manufacturer+id%22+97&usg=AFQjCNH4hSHb7yphONuO_NnaaWLV4g5v9A [Blu] http://www.blu-raydisc.info/licensee_info.php [Edig] http://www.editgrid.com/user/idrs/DVR112_MediaList_109_20070118_2final.xml Found by : www.google.com : NAN-YA cmc Princo Ricoh lead-in Try also : www.google.com : cd "manufacturer id" 97 ------------------------------------------------------------------------------ CD Media IDs The findings from the web have been cross checked with the lists of cdrecord. But no codes or manufacturer ids have been taken from diskid.c out of cdrtools-2.01.01a39/cdrecord/. X = not in cdrecord D = different in cdrecord The exact lettering of manufacturer names may differ from cdrecord. For any exact comparison use rather the minute,second,frame tuples of the ATIP lead-in address directly. The vast majority of these tuples begin with 97. (libburn API call burn_disc_get_media_id() encodes both tuples in its reply parameters as e.g. "97m15s35f/79m59s74f" , "97m15s35f", "79m59s74f") Chosen Name Lead-in Lead-out Name [source] ======================================================== "SKC" X 96 40 05 79 59 74 - SKC(90) 24X [Msi] "Ritek Corp" X 96 43 33 79 59 74 702.83MB TRAXDATA 24X [Msi] 96 43 33 79 59 74 90 32x "Prodisc Technology Inc." [Gig] 96 43 37 79 59 73 99 48x "Ritek Corp" [Gig] 96 43 37 79 59 73 Ritek [Aopen] 96 43 37 79 59 74 702.83MB PHIL(Ritek) Multi Speed [Msi] 96 43 37 79 59 74 90 48x "Ritek Corp" [Gig] 96 43 37 79 59 74 Ritek [Aopen] 96 43 37 79 59 74 Ritek [Btc] "TDK / Ritek" X 97 10 00 79 59 74 702.83MB RiDATA(Ritek) N/A 12X [Msi] 97 10 00 79 59 74 702.83MB TRAXDATA(Ritek) 10X [Msi] 97 10 00 79 59 74 702.83MB TRAXDATA(Ritek) N/A 10X [Msi] 97 10 00 79 59 74 80 12X "TDK Corporation" [Gig] 97 10 00 79 59 74 TDK(RITEK) [Aopen] 97 10 01 79 59 74 702.83MB Ritek.Co. 16X [Msi] 97 10 01 79 59 74 Ritek [Aopen] 97 10 03 79 59 74 80 32X "RITEK" [Gig] "TDK Corporation" 97 15 00 79 59 74 702.83MB 52X TDK Multi Speed [Msi] 97 15 00 79 59 74 TDK [Btc] 97 15 01 79 59 74 80 24X "TDK Corporation" [Gig] 97 15 05 79 59 74 80 52X "TDK Corporation" [Gig] 97 15 05 79 59 74 TDK 52X [Msi] 97 15 05 79 59 74 TDK [Aopen] 97 15 05 79 59 74 TDK [Btc] 97 15 05 91 01 48 800MB TDK 40X 800MB [Msi] 97 15 05 91 01 48 800MB TDK 40X [Msi] 97 15 05 91 01 48 90 24X "TDK Corporation" [Gig] 97 15 05 91 01 48 TDK [Aopen] "Ritek Corp" 97 15 12 79 59 73 702.83MB Ritek Co. 52X [Msi] 97 15 12 79 59 73 80 52x "Ritek Corp" [Gig] 97 15 12 79 59 73 Ritek,80 [Aopen] 97 15 12 79 59 73 Ritek [Btc] 97 15 12 79 59 73 Ritek CD-R 52X 702.83MB [Mits] 97 15 12 79 59 74 80 24x "Ritek Corp" [Gig] 97 15 12 79 59 74 Ritek [Btc] 97 15 17 24 15 01 Ritek [Btc] 97 15 17 74 45 01 Ritek [Btc] 97 15 17 79 59 70 702.83MB 7-plus (Ritek) Multi Speed [Msi] 97 15 17 79 59 70 702.83MB Aopen (Ritek) Multi Speed [Msi] 97 15 17 79 59 70 702.83MB PONY (Ritek) Multi Speed [Msi] 97 15 17 79 59 70 702.83MB PONY(Ritek) Multi Speed [Msi] 97 15 17 79 59 70 702.83MB Power Source(Ritek) 32X [Msi] 97 15 17 79 59 70 702.83MB Ritek 40X [Msi] 97 15 17 79 59 70 702.83MB Ritek Co. 52X [Msi] 97 15 17 79 59 70 702.83MB TDK (Ritek ) 40X [Msi] 97 15 17 79 59 70 702.83MB TDK(Ritek ) 40X [Msi] 97 15 17 79 59 70 702.83MB TDK(Ritek ) 48X [Msi] 97 15 17 79 59 70 702.83MB TDK(Ritek) 48X [Msi] 97 15 17 79 59 70 702.83MB TRAXDATA (Ritek ) 40X [Msi] 97 15 17 79 59 70 80 52x "Ritek Corp" [Gig] 97 15 17 79 59 70 Aopen CD-R Multi Speed 702.83MB [Mits] 97 15 17 79 59 70 HiCO CD-R Multi Speed 702.83MB [Mits] 97 15 17 79 59 70 PHILIPS CD-R 48X 702.83MB [Mits] 97 15 17 79 59 70 PONY CD-R Multi Speed 702.83MB [Mits] 97 15 17 79 59 70 Primdisc CD-R 52X 702.83MB [Mits] 97 15 17 79 59 70 RiDATA CD-R 40X 702.83MB [Mits] 97 15 17 79 59 70 Ritek [Aopen] 97 15 17 79 59 70 Ritek [Btc] 97 15 17 79 59 70 Ritek CD-R 40X 702.83MB [Mits] 97 15 17 79 59 70 Ritek CD-R 52X 702.83MB [Mits] 97 15 17 79 59 70 TDK CD-R 40X 702.82MB [Mits] 97 15 17 79 59 70 TDK CD-R 40X 702.83MB [Mits] 97 15 17 79 59 70 TRAXDATA CD-R 40X 702.83MB [Mits] 97 15 17 79 59 70 Victor.JVC CD-R 48X 702.82MB [Mits] 97 15 17 79 59 71 702.82MB PONY(Ritek) Multi Speed [Msi] 97 15 17 79 59 71 702.83MB TRAXDATA(Ritek) 24X [Msi] 97 15 17 79 59 71 80 24x "Ritek Corp" [Gig] 97 15 17 79 59 71 Ritek [Btc] 97 15 17 79 59 72 Ritek [Btc] 97 15 17 79 59 73 702.83MB OPTI STORAGE (Ritek) 40X [Msi] 97 15 17 79 59 73 702.83MB Ritek 48X [Msi] 97 15 17 79 59 73 702.83MB Ritek.Co (Trax) 40X [Msi] 97 15 17 79 59 73 702.83MB Samsung Digit(Ritek) 40X [Msi] 97 15 17 79 59 73 80 52x "Ritek Corp" [Gig] 97 15 17 79 59 73 Ritek [Aopen] 97 15 17 79 59 74 Ritek [Btc] 97 15 18 79 59 74 80 32x "Ritek Corp" [Gig] "Mitsubishi Chemical Corporation" 97 15 21 79 59 74 MCC ("Mitsubishi Chemical Corporation") [Btc] "Nan-Ya Plastics Corporation" 97 15 35 79 59 73 702.83MB NANYA 4-12X [Msi] 97 15 35 79 59 73 80 12X "Nan-Ya" [Gig] 97 15 35 79 59 73 NanYa [Aopen] 97 15 35 79 59 74 702.83MB Hatron(NANYA) 4-10X [Msi] 97 15 35 79 59 74 702.83MB MMore(NANYA) 4-10X [Msi] 97 15 35 79 59 74 (NanYa) [Aopen] 97 15 36 79 59 74 80 40x "Nan-Ya plastics Corporation" [Gig] 97 15 36 79 59 74 NanYa [Aopen] 97 15 36 79 59 74 NANYA [Btc] 97 15 37 79 59 73 NANYA [Btc] 97 15 37 79 59 74 702.83MB NANYA 1-52X [Msi] 97 15 37 79 59 74 702.83MB NANYA 48X [Msi] 97 15 37 79 59 74 80 52x "Nan-Ya plastics Corporation" [Gig] 97 15 37 79 59 74 Acer CD-R 48X 702.83MB [Mits] 97 15 37 79 59 74 LITEON CD-R 48X 702.83MB [Mits] 97 15 37 79 59 74 LITEON CD-R 52X 702.83MB [Mits] 97 15 37 79 59 74 NANYA [Btc] 97 15 37 79 59 74 NANYA CD-R 1-52X 702.83MB [Mits] 97 15 39 79 59 74 80 52x "Nan-Ya plastics Corporation" [Gig] 97 15 39 79 59 74 NanYa [Aopen] 97 15 39 79 59 74 NANYA [Btc] "Delphi" X 97 15 57 79 59 73 Delphi [Aopen] 97 15 57 79 59 74 Delphi [Btc] "Shenzhen SG&SAST" 97 16 29 79 59 73 702.83MB SAST(SHENZHEN) Multi peed [Msi] 97 16 29 79 59 73 80 52x "Shenzhen SG&SAST [Gig] 97 16 29 79 59 73 SAST [Btc] 97 16 29 79 59 73 SAST(ultra [Aopen] 97 16 29 79 59 74 80 52x "Shenzhen SG&SAST" [Gig] "Moser Baer India Limited" 97 17 00 4x "Moser Baer India Limited" [Gig] 97 17 01 79 59 74 MBI [Aopen] 97 17 06 79 59 74 702.83MB EMTEC (Moser Bear India) 48X [Msi] 97 17 06 79 59 74 702.83MB Intenso (Moser Bear India) 1-48X [Msi] 97 17 06 79 59 74 702.83MB Moser Bear India 52X [Msi] 97 17 06 79 59 74 702.83MB YAKUMO(Moser Bear India) 52X [Msi] 97 17 06 79 59 74 80 52x "Moser Baer India Limited" [Gig] 97 17 06 79 59 74 MBI [Aopen] 97 17 06 79 59 74 Moser Bear India CD-R 52X 702.83MB [Mits] 97 17 06 79 59 74 PLATINUM CD-R 52X 702.83MB [Mits] 97 17 06 79 59 74 Silver Circle CD-R 52X 702.83MB [Mits] "SKY media Manufacturing SA" X 97 17 16 79 59 74 80 48x "SKY media Manufacturing SA" [Gig] 97 17 16 79 59 74 SKY [Aopen] "Wing" D "WEALTH FAIR INVESTMENT LIMITED" 97 18 17 79 59 74 Wing [Aopen] "DDT" X 97 18 28 79 59 74 DDT [Aopen] "Taroko International Co.Ltd" 97 18 67 79 59 73 80 48x "Taroko International Co.Ltd" [Gig] 97 18 67 79 59 74 Taroko [Aopen] 97 18 67 79 59 74 TAROKO [Btc] "Daxon Technology Inc. / Acer" D "Acer Media Technology, Inc." 97 22 60 4x "Daxon Technology Inc." [Gig] 97 22 60 74 41 50 74 12X "Daxon Technology Inc." [Gig] 97 22 60 74 41 50 Acer CD-RW 4X 656.20MB [Mits] 97 22 60 74 41 50 Acer US-RW 24X 656.20MB [Mits] 97 22 60 74 41 50 Daxon [Aopen] 97 22 60 79 59 74 702.83MB Maxmax(Acer) 8-10X [Msi] 97 22 60 79 59 74 80 24x "Daxon Technology Inc." [Gig] 97 22 60 79 59 74 Acer CD-RW 4X 702.83MB [Mits] 97 22 60 79 59 74 Daxon [Aopen] 97 22 60 79 59 74 Diamond Data HS-RW 4-16X 702.83MB [Mits] 97 22 60 79 59 74 Maxmax HS-RW 8-10X 702.83MB [Mits] 97 22 62 79 59 74 702.83MB Acer 4-16X [Msi] 97 22 62 79 59 74 702.83MB Acer N/A 4-16X [Msi] 97 22 62 79 59 74 Acer [Aopen] 97 22 66 79 59 74 80 52x "Daxon Technology Inc." [Gig] 97 22 66 79 59 74 Acer [Aopen] 97 22 67 79 59 74 702.83MB Acer 52X [Msi] 97 22 67 79 59 74 702.83MB BenQ(Acer) 40X [Msi] 97 22 67 79 59 74 702.83MB BenQ (Acer) 48X [Msi] 97 22 67 79 59 74 702.83MB Daxon(Acer) 48X [Msi] 97 22 67 79 59 74 702.83MB gold(Acer) 32X [Msi] 97 22 67 79 59 74 702.83MB SONY(Acer) 48X [Msi] 97 22 67 79 59 74 80 52x "Daxon Technology Inc." [Gig] 97 22 67 79 59 74 Acer [Btc] 97 22 67 79 59 74 Acer CD-R 52X 702.83MB [Mits] 97 22 67 79 59 74 Acer,DAXON [Aopen] "Taiyo Yuden Company Limited" 97 24 01 74 43 00 74 16x "Taiyo Yuden Company Limited" [Gig] 97 24 01 74 43 01 656.40MB Maxell(Taiyo Yuden) 32X [Msi] 97 24 01 74 43 01 656.40MB Taiyo Yuden 32X [Msi] 97 24 01 74 43 01 74 48x "Taiyo Yuden Company Limited" [Gig] 97 24 01 74 43 01 Taiyo Yuden [Btc] 97 24 01 74 43 01 TY,tuned [Aopen] 97 24 01 74 43 02 656.40MB Taiyo Yuden 40-48X [Msi] 97 24 01 74 43 02 74 52x "Taiyo Yuden Company Limited" [Gig] 97 24 01 74 43 02 FUJIFILM CD-R 48X 656.40MB [Mits] 97 24 01 74 43 02 Taiyo Yuden [Btc] 97 24 01 74 43 02 TY [Aopen] 97 24 01 79 59 72 702.83MB Taiyo Yuden 40-48X [Msi] 97 24 01 79 59 72 702.83MB Taiyo Yuden 48X [Msi] 97 24 01 79 59 72 702.83MB Taiyo Yuden Multi Speed [Msi] 97 24 01 79 59 72 80 52x "Taiyo Yuden Company Limited" [Gig] 97 24 01 79 59 72 Maxell CD-R 48X 702.83MB [Mits] 97 24 01 79 59 72 SONY CD-R 48X 702.83MB [Mits] 97 24 01 79 59 72 Taiyo Yuden [Btc] 97 24 01 79 59 72 Taiyo Yuden CD-R 52X 702.83MB [Mits] 97 24 01 79 59 72 Taiyo Yuden CD-R Multi Speed 702.83MB [Mits] 97 24 01 79 59 72 TY [Aopen] 97 24 01 79 59 73 702.83MB SONY(Taiyo Yuden) 32X [Msi] 97 24 01 79 59 73 702.83MB Taiyo Yuden 32X [Msi] 97 24 01 79 59 73 80 48x "Taiyo Yuden Company Limited" [Gig] 97 24 01 79 59 73 Taiyo Yuden [Btc] 97 24 01 79 59 73 TY,tuned [Aopen] 97 24 01 79 59 74 702.83MB FUJIFILM(Tayio Yuden) 48X [Msi] 97 24 01 79 59 74 80 16x "Taiyo Yuden Company Limited" [Gig] "Sony Corporation" 97 24 11 74 43 00 74 24x "Sony Corporation" [Gig] 97 24 11 79 59 11 80 24x "Sony Corporation" [Gig] 97 24 11 79 59 74 Sony [Aopen] 97 24 11 79 59 74 SONY [Btc] 97 24 12 74 43 00 74 48x "Sony Corporation" [Gig] 97 24 12 74 43 00 LeadData,Sony [Aopen] 97 24 12 79 59 74 80 32x "Sony Corporation" [Gig] 97 24 12 79 59 74 SONY [Btc] 97 24 15 74 30 00 Imation [Aopen] 97 24 15 74 43 00 654.49MB imation(Sony) 4X-10X [Msi] 97 24 15 79 59 74 702.83MB (SONY) 2-24X [Msi] 97 24 15 79 59 74 702.83MB SONY(Lead data) 48X [Msi] 97 24 15 79 59 74 80 48x "Sony Corporation" [Gig] 97 24 15 79 59 74 Sony [Aopen] 97 24 16 79 59 74 702.83MB SONY(Lead data) 52X [Msi] 97 24 16 79 59 74 80 52x "Sony Corporation" [Gig] 97 24 16 79 59 74 Sony [Aopen] 97 24 16 79 59 74 SONY [Btc] "Computer Support Italcard s.r.l" 97 24 26 79 59 74 80 52x "Computer Support Italcard s.r.l" [Gig] 97 24 26 79 59 74 Csita [Aopen] "Unitech Japan Inc." 97 24 39 79 59 74 80 52x "Unitech Japan Inc." [Gig] 97 24 39 79 59 74 RA [Aopen] "MPO, France" 97 25 00 79 59 74 TDK CD-R80(25) [Teac] 97 25 06 74 45 00 74 52x "MPO" [Gig] 97 25 06 74 45 00 MPO,France [Aopen] 97 25 06 79 59 74 80 52x "MPO" [Gig] 97 25 07 79 59 00 MPO,France [Aopen] "Hitachi Maxell Ltd." 97 25 22 79 59 74 80 16x "Hitachi Maxell Ltd." [Gig] 97 25 29 74 30 00 654.49MB Hitachi Maxell Multi Speed [Msi] 97 25 29 74 30 00 74 52x "Hitachi Maxell Ltd." [Gig] 97 25 29 74 30 00 MAXELL [Aopen] 97 25 29 74 30 00 Maxell [Btc] 97 25 29 79 59 74 702.83MB Hitachi Maxell(Acer OEM) 32X [Msi] 97 25 29 79 59 74 702.83MB Hitachi Maxell Multi Speed [Msi] 97 25 29 79 59 74 80 52x "Hitachi Maxell Ltd." [Gig] 97 25 29 79 59 74 Eigen CD-R Multi Speed 702.83MB [Mits] 97 25 29 79 59 74 Maxell [Btc] 97 25 29 79 59 74 MAXWELL [Aopen] "Infodisc Technology Co,Ltd." 97 25 30 24 01 05 Infodisk 8cm HS-RW 12X 210.80MB [Mits] 97 25 30 - - - 4x "Infodisc Technology Co,Ltd." [Gig] 97 25 30 74 44 07 INFODISC [Aopen] 97 25 30 79 59 72 INFODISC [Aopen] 97 25 30 79 59 73 MEMOREX [Aopen] 97 25 30 79 59 74 80 10X "Infodisc Technology Co,Ltd." [Gig] 97 25 30 79 59 74 Infodisk CD-RW 4X 702.83MB [Mits] 97 25 30 79 59 74 MEMOREX(INFODISC) [Aopen] 97 25 31 79 59 73 80 24x "Infodisc Technology Co,Ltd." [Gig] 97 25 35 79 59 74 702.83MB Infodisc 52X [Msi] 97 25 35 79 59 74 702.83MB SPEEDA(Infodisc) 40X [Msi] 97 25 35 79 59 74 80 48x "Infodisc Technology Co,Ltd." [Gig] 97 25 35 79 59 74 Lead data [Btc] "Xcitec" D "Xcitec Inc." 97 25 66 79 59 73 XGITEK [Aopen] "Fornet International Pte Ltd" 97 26 00 4x "Fornet International Pte Ltd" [Gig] 97 26 00 79 59 74 80 12X "Fornet International Pte Ltd" [Gig] 97 26 00 79 59 74 COMPUSA,Fornet [Aopen] 97 26 01 74 56 00 74 24x "Fornet International Pte Ltd" [Gig] 97 26 01 79 59 74 80 12X "Fornet International Pte Ltd" [Gig] 97 26 06 79 59 74 80 24x "Fornet International Pte Ltd" [Gig] 97 26 07 79 59 64 90 24x "Fornet International Pte Ltd" [Gig] 97 26 07 79 59 64 Formet [Btc] 97 26 07 79 59 71 702.82MB Cdhouse (Fornet International) 52X [Msi] 97 26 07 79 59 71 80 52x "Fornet International Pte Ltd" [Gig] 97 26 07 79 59 71 Formet [Btc] 97 26 07 79 59 71 Fornet [Aopen] 97 26 07 79 59 74 Fornet [Aopen] "Postech Corporation" 97 26 11 22 23 60 Postech [Btc] 97 26 11 79 59 73 702.83MB Postech 52X [Msi] 97 26 11 79 59 73 80 52x "Postech Corporation" [Gig] 97 26 11 79 59 73 Postech [Aopen] 97 26 11 79 59 73 POSTECH [Aopen] 97 26 11 79 59 73 Postech [Btc] 97 26 11 79 59 73 Postech CD-R 52X 702.83MB [Mits] 97 26 11 79 59 74 702.83MB Postech corp Multi Speed [Msi] 97 26 11 79 59 74 702.83MB Postech N/A 12X [Msi] 97 26 11 79 59 74 80 12X "Postech Corporation" [Gig] 97 26 11 79 59 74 80 40x "Postech Corporation" [Gig] 97 26 11 79 59 74 Mr.Platinum [Aopen] 97 26 11 79 59 74 POSTECH [Aopen] 97 26 11 79 59 74 Postech [Btc] 97 26 15 79 59 74 80 32x "Postech Corporation" [Gig] "SKC Co Ltd." 97 26 21 74 59 73 74 12X "SKC Co Ltd." [Gig] 97 26 21 74 59 73 SKC74-Korea [Aopen] 97 26 21 79 59 74 80 12X "SKC Co Ltd." [Gig] 97 26 21 79 59 74 SKC80-Korea [Aopen] 97 26 26 79 59 73 702.83MB SKC 48X [Msi] 97 26 26 79 59 73 80 52x "SKC Co Ltd." [Gig] 97 26 26 79 59 73 Infinite CD-R 48X 702.83MB [Mits] 97 26 26 79 59 73 SKC [Btc] 97 26 26 79 59 73 SKC(Korea) [Aopen] "Fuji Photo Film Co,Ltd." 97 26 45 79 59 73 702.83MB FUJI 52X [Msi] 97 26 45 79 59 73 80 52x "Fuji Photo Film Co,Ltd." [Gig] 97 26 45 79 59 73 Fujifilm [Aopen] 97 26 45 79 59 74 80 32x "Fuji Photo Film Co,Ltd." [Gig] "Lead Data Inc." 97 26 50 4x "Lead Data Inc." [Gig] 97 26 50 74 59 74 SONY CD-RW 1-4X 702.83MB [Mits] 97 26 51 79 59 74 702.83MB Lead Data 12X [Msi] 97 26 51 79 59 74 80 12X "Lead Data Inc." [Gig] 97 26 51 79 59 74 Leaddata [Aopen] 97 26 52 79 59 74 80 32x "Lead Data Inc." [Gig] 97 26 53 79 59 74 702.83MB Lead Data 48X [Msi] 97 26 53 79 59 74 Gigastorage [Btc] 97 26 54 79 59 74 702.83MB Lead Data 48X [Msi] 97 26 54 79 59 74 80 48x "Lead Data Inc." [Gig] 97 26 54 79 59 74 LeadData [Aopen] 97 26 54 79 59 74 Lead data [Btc] 97 26 54 79 59 74 Lead Data CD-R 48X 702.83MB [Mits] 97 26 56 79 59 74 80 52x "Lead Data Inc." [Gig] 97 26 56 79 59 74 LeadData [Aopen] 97 26 56 79 59 74 Lead data [Btc] 97 26 57 79 59 74 80 52x "Lead Data Inc." [Gig] 97 26 57 79 59 74 MIRAGE [Aopen] "CMC Magnetics Corporation" 97 26 60 74 41 50 74 32x "Daxon Technology Inc." [Gig] 97 26 61 79 59 74 80 32x "CMC Magnetics Corporation" [Gig] 97 26 65 21 59 74 Verbatim 8mm CD-RW 2-4X 193.06MB [Mits] 97 26 65 24 30 00 215.04MB Memorex(CMC)8cm 4X [Msi] 97 26 65 24 30 00 Bi-Winner 8cm HS-RW 4-8X 215.04MB [Mits] 97 26 65 24 30 00 Memorex 8cm CD-RW 4X 215.04MB [Mits] 97 26 65 4x "CMC Magnetics Corporation" [Gig] 97 26 65 74 12 00 PLEXTOR [Aopen] 97 26 65 74 59 64 658.89MB CMC 16X [Msi] 97 26 65 74 59 64 CMC [Aopen] 97 26 65 75 00 00 658.89MB YAMAHA(CMC) 4-10X [Msi] 97 26 65 75 00 00 658.89MB YAMAHA(CMC) N/A 4-10X [Msi] 97 26 65 75 00 00 74 12X "CMC Magnetics Corporation" [Gig] 97 26 65 75 00 00 CMC [Aopen] 97 26 65 75 00 00 CMC CD-RW 4X 658.89MB [Mits] 97 26 65 79 59 64 CMC [Aopen] 97 26 65 79 59 74 702.83MB Melody(CMC) 10X [Msi] 97 26 65 79 59 74 702.83MB Office DEPOT(CMC) 1-4X [Msi] 97 26 65 79 59 74 702.83MB Philips(CMC) 4-12X [Msi] 97 26 65 79 59 74 702.83MB Philips(CMC) N/A 4-12X [Msi] 97 26 65 79 59 74 CMC [Aopen] 97 26 65 79 59 74 CMC CD-RW 4X 702.83MB [Mits] 97 26 65 79 59 74 Office DEPOT CD-RW 1-4X 702.83MB [Mits] 97 26 66 23 00 00 CMC [Btc] 97 26 66 23 59 74 CMC [Btc] 97 26 66 75 00 00 74 40x "CMC Magnetics Corporation" [Gig] 97 26 66 75 00 00 CMC [Aopen] 97 26 66 75 10 00 CMC [Aopen] 97 26 66 75 10 00 CMC [Btc] 97 26 66 75 10 00 CMC CD-R 52X 660.35MB [Mits] 97 26 66 79 59 71 702.83MB CMC 52X [Msi] 97 26 66 79 59 71 702.83MB eMARK(CMC) 48X [Msi] 97 26 66 79 59 71 702.83MB imation(CMC) 48X [Msi] 97 26 66 79 59 71 702.83MB imation(CMC) 48X [Msi] 97 26 66 79 59 71 80 52x "CMC Magnetics Corporation" [Gig] 97 26 66 79 59 71 CMC [Btc] 97 26 66 79 59 71 CMC CD-R 52X 702.83MB [Mits] 97 26 66 79 59 71 CMC,MEMOREX [Aopen] 97 26 66 79 59 73 702.83MB CMC 52X [Msi] 97 26 66 79 59 73 702.83MB HyperMedia(CMC) 52X [Msi] 97 26 66 79 59 73 702.83MB imation(CMC) 48X [Msi] 97 26 66 79 59 73 702.83MB imation(CMC) 48X [Msi] 97 26 66 79 59 73 702.83MB Melody (CMC) 40X [Msi] 97 26 66 79 59 73 702.83MB Melody(CMC) 40X [Msi] 97 26 66 79 59 73 702.83MB Samsung(CMC) 48X [Msi] 97 26 66 79 59 73 80 52x "CMC Magnetics Corporation" [Gig] 97 26 66 79 59 73 CMC [Aopen] 97 26 66 79 59 73 CMC [Btc] 97 26 66 79 59 73 CMC CD-R 52X 702.83MB [Mits] 97 26 66 79 59 73 Imation CD-R 48X 702.83MB [Mits] 97 26 66 79 59 73 Shintaro CD-R 40X 702.83MB [Mits] 97 26 66 79 59 73 Techworks CD-R 48X 702.83MB [Mits] 97 26 66 79 59 74 702.83MB Bi-Winner(CMC)99min 40X [Msi] 97 26 66 79 59 74 702.83MB Bi-Winner(CMC)99min 40X [Msi] 97 26 66 79 59 74 702.83MB Memorex(CMC) 48X [Msi] 97 26 66 79 59 74 702.83MB PHILIPS(CMC) 40X [Msi] 97 26 66 79 59 74 702.83MB PHILIPS(CMC) Multi Speed [Msi] 97 26 66 79 59 74 80 16x "CMC Magnetics Corporation" [Gig] 97 26 66 79 59 74 CMC [Aopen] 97 26 66 79 59 74 CMC [Btc] 97 26 67 75 00 00 658.89MB CMC 24X [Msi] 97 26 67 75 00 00 74 24x "CMC Magnetics Corporation" [Gig] 97 26 67 75 00 00 CMC US-RW 24X 658.89MB [Mits] 97 26 67 79 59 64 80 32X "CMC Magnetics Corporation" [Gig] "Ricoh Company Limited" D "DIGITAL STORAGE TECHNOLOGY CO.,LTD" 97 27 00 4x "Ricoh Company Limited" [Gig] 97 27 00 74 12 00 651.86MB RICOH N/A 4-10X [Msi] 97 27 00 74 12 00 651.86MB Sony(Digital Storage) 4X-10X [Msi] 97 27 00 74 12 00 651.86MB Sony(Digital Storage) 4X-10X [Msi] 97 27 00 74 12 00 74 10X "Ricoh Company Limited" [Gig] 97 27 00 74 12 00 74 24x "Ricoh Company Limited" [Gig] 97 27 00 74 12 00 RICOH [Aopen] 97 27 00 74 12 00 Ricoh HS-RW 4X-10X 651.86MB [Mits] 97 27 00 74 12 00 Sony HS-RW 4X-10X 651.86MB [Mits] 97 27 00 75 00 00 74 32x "Digital Storage Techology" [Gig] 97 27 00 79 59 74 702.83MB Laser 48X [Msi] 97 27 00 79 59 74 80 40x "Digital Storage Techology" [Gig] 97 27 00 79 59 74 Csita [Aopen] 97 27 00 79 59 74 RICOH [Aopen] 97 27 06 79 59 72 Digital Storage CD-R 52X 53.318MB [Mits] 97 27 06 79 59 72 DST [Aopen] 97 27 06 79 59 73 702.83MB Digital Storage 32X [Msi] 97 27 06 79 59 73 702.83MB Digital Storage 52X [Msi] 97 27 06 79 59 73 DST [Btc] 97 27 06 79 59 74 80 40x "Digital Storage Techology" [Gig] 97 27 06 79 59 74 DST [Aopen] "Plasmon Data systems Ltd" 97 27 10 21 30 00 188.67MB Plasmon 4-10X [Msi] 97 27 10 21 30 00 Ritek 8cm HS-RW 4-10X 188.67MB [Mits] 97 27 10 4x "Ritek Corp" [Gig] 97 27 10 74 41 00 656.10MB TDK(Plasmon) 4X-10X [Msi] 97 27 10 74 41 00 74 12X "Ritek Corp" [Gig] 97 27 10 74 41 00 EMTEC CD-RW 1-4X 656.10MB [Mits] 97 27 10 74 41 00 Ritek [Aopen] 97 27 10 74 41 00 TDK HS-RW 4-10X 651.86MB [Mits] 97 27 11 74 41 00 Ritek [Aopen] 97 27 11 74 41 01 656.10MB Plasmon 16X [Msi] 97 27 12 74 41 00 656.10MB RiDATA(Plasmon) 24X [Msi] 97 27 12 74 41 00 656.10MB Riteck co. 24X [Msi] 97 27 12 74 41 00 74 24x "Ritek Corp" [Gig] 97 27 12 74 41 00 Plasmon US-RW 24X 656.10MB [Mits] 97 27 18 79 59 74 702.83MB ALPHAPET(Plasmon)() 1-40X [Msi] 97 27 18 79 59 74 702.83MB MANIA(Plasmon)() 48X [Msi] 97 27 18 79 59 74 80 52x "Plasmon Data systems Ltd" [Gig] 97 27 18 79 59 74 Plasmon [Aopen] 97 27 18 79 59 74 Plasmon [Btc] 97 27 19 79 59 74 4M [Aopen] 97 27 19 79 59 74 80 48x "Plasmon Data systems Ltd" [Gig] "Princo Corporation" 97 27 21 79 59 74 Princo Co CD-RW 4X 702.83MB [Mits] 97 27 28 4x "Princo Corporation" [Gig] 97 27 28 74 50 01 74 40x "Princo Corporation" [Gig] 97 27 28 74 50 01 Princo [Aopen] 97 27 28 79 59 74 702.83MB Princo 2-48X [Msi] 97 27 28 79 59 74 702.83MB Princo 52X [Msi] 97 27 28 79 59 74 80 52x "Princo Corporation" [Gig] 97 27 28 79 59 74 Princo [Aopen] 97 27 28 79 59 74 Princo [Btc] 97 27 29 79 59 74 80 12X "Princo Corporation" [Gig] 97 27 29 79 59 74 Princo [Aopen] 97 27 29 79 59 74 Princo Co HS-RW 4-12X 702.83MB [Mits] "Pioneer" 97 27 30 PIONEER [Hij] "Eastman Kodak Company" 97 27 45 74 05 01 74 32x "Eastman Kodak Company" [Gig] "Mitsui Chemicals Inc." 97 27 55 74 05 10 74 16x "Mitsui Chemicals Inc." [Gig] 97 27 56 74 05 11 74 16x "Mitsui Chemicals Inc." [Gig] 97 27 56 79 59 74 80 24x "Mitsui Chemicals Inc." [Gig] 97 27 57 79 59 74 80 40x "Mitsui Chemicals Inc." [Gig] 97 27 57 79 59 74 MITSUI [Aopen] 97 27 58 74 05 13 MAM-A [Aopen] 97 27 58 79 59 74 80 52x "Mitsui Chemicals Inc." [Gig] 97 27 58 79 59 74 Mitsui [Aopen] 97 27 58 79 59 74 TDK [Btc] "Ricoh Company Limited" 97 27 66 74 12 00 RICOH [Btc] 97 27 66 74 12 02 74 48x "Ricoh Company Limited" [Gig] 97 27 66 74 12 02 Ricoh [Aopen] 97 27 66 74 12 03 74 52x "Ricoh Company Limited" [Gig] 97 27 66 74 12 03 Ritek [Btc] 97 27 66 74 12 03 Ritek,RS [Aopen] 97 27 66 79 59 71 702.82MB RICOH 40X [Msi] 97 27 66 79 59 71 80 48x "Ricoh Company Limited" [Gig] 97 27 66 79 59 71 RICOH [Btc] 97 27 66 79 59 71 RICOH CD-R 40X 702.82MB [Mits] 97 27 66 79 59 71 Ritek,RS [Aopen] 97 27 66 79 59 72 80 48x "Ricoh Company Limited" [Gig] 97 27 66 79 59 72 Ricoh [Aopen] 97 27 66 79 59 74 RICOH [Btc] "Gigastorage Corporation" 97 28 12 79 59 72 702.83MB Gigastorage 52X [Msi] 97 28 12 79 59 72 80 52x "Gigastorage Corporation" [Gig] 97 28 12 79 59 72 GigaSto [Aopen] 97 28 12 79 59 74 702.83MB Gigastorage 40X [Msi] 97 28 12 79 59 74 80 48x "Gigastorage Corporation" [Gig] 97 28 12 79 59 74 GigaSto, MaxMax [Aopen] 97 28 12 79 59 74 Gigastorage [Btc] 97 28 15 4x "Gigastorage Corporation" [Gig] 97 28 15 4x "Nan-Ya" [Gig] 97 28 15 74 12 00 74 10X "Gigastorage Corporation" [Gig] 97 28 15 74 12 00 CURSOR [Aopen] 97 28 15 79 59 72 702.83MB Gigastorage 52X [Msi] 97 28 15 79 59 72 702.83MB MaxMax (Gigastorage) 40X [Msi] 97 28 15 79 59 72 GigaSto [Aopen] 97 28 15 79 59 72 Gigastorage [Btc] 97 28 15 79 59 74 702.83MB Gigastorage 40X [Msi] 97 28 15 79 59 74 80 48x "Gigastorage Corporation" [Gig] 97 28 15 79 59 74 80 52x "Gigastorage Corporation" [Gig] 97 28 15 79 59 74 GigaSto [Aopen] 97 28 15 79 59 74 Gigastorage [Btc] 97 28 15 79 59 74 Gigastorage HS-RW 4X-8X 702.83MB [Mits] "Multi Media Masters&Machinary SA" 97 28 22 79 59 67 80 24x "Multi Media Masters&Machinary SA" [Gig] 97 28 22 79 59 67 King [Aopen] 97 28 22 79 59 67 Multi Media CD-R 48X 702.82MB [Mits] 97 28 26 79 59 74 702.83MB Mmirex(Multi Media) 24X [Msi] 97 28 26 79 59 74 80 52x "Multi Media Masters&Machinary SA" [Gig] 97 28 26 79 59 74 KingPro [Aopen] "Ritek Corp" 97 31 01 74 45 00 74 24x "Ritek Corp" [Gig] 97 31 01 74 45 00 Ritek [Btc] 97 31 01 74 45 01 656.68MB Ritek Co. 40X [Msi] 97 31 01 74 45 01 656.68MB Ritek Co. 52X [Msi] 97 31 01 74 45 01 74 52x "Ritek Corp" [Gig] 97 31 01 74 45 01 Ritek,74 [Aopen] 97 31 01 74 45 01 Ritek [Btc] 97 31 07 21 15 01 Ritek [Btc] 97 31 07 74 45 00 Ritek [Btc] 97 31 07 74 45 01 656.69MB Ritek Co. 48X [Msi] 97 31 07 74 45 01 656.69MB TDK(Ritek) 40X [Msi] 97 31 07 74 45 01 74 52x "Ritek Corp" [Gig] 97 31 07 74 45 01 Ritek [Aopen] 97 31 07 74 45 01 Ritek [Btc] 97 31 07 74 45 02 Ritek [Btc] "Grand Advance Technology Sdn. Bhd." 97 31 35 79 59 74 GAT [Aopen] "TDK Corporation" 97 32 00 74 59 00 74 16x "TDK Corporation" [Gig] 97 32 01 74 59 74 74 24X "TDK Corporation" [Gig] 97 32 05 74 59 00 74 52X "TDK Corporation" [Gig] 97 32 05 74 59 00 TDK [Aopen] "Prodisc Technology Inc." 97 32 10 79 59 74 702.83MB Prodisc 12X [Msi] 97 32 10 79 59 74 702.83MB Smartbuy(Prodisc) 8-12X [Msi] 97 32 10 79 59 74 702.83MB Smartbuy(Prodisc) N/A 8-12X [Msi] 97 32 10 79 59 74 80 12X "Prodisc Technology Inc." [Gig] 97 32 10 79 59 74 PRODISC [Aopen] 97 32 10 79 59 74 Smartbuy HS-RW 8-12X 702.83MB [Mits] 97 32 11 22 00 00 Prodisc CD-RW 1-4X 193.07MB [Mits] 97 32 11 4x "Prodisc Technology Inc." [Gig] 97 32 11 79 59 74 Prodisc CD-RW 1X-4X 702.83MB [Mits] 97 32 12 79 59 74 80 10X "Prodisc Technology Inc." [Gig] 97 32 12 79 59 74 PRODISC [Aopen] 97 32 13 79 59 74 (PRODISC) [Aopen] 97 32 19 22 00 00 Prodisc [Btc] 97 32 19 74 30 01 74 52x "Prodisc Technology Inc." [Gig] 97 32 19 74 30 01 Prodisc [Aopen] 97 32 19 74 30 01 Prodisc [Btc] 97 32 19 79 59 71 702.83MB Mitsubishi (Prodisc) 52X [Msi] 97 32 19 79 59 71 702.83MB Prodise 52X [Msi] 97 32 19 79 59 71 80 52x "Prodisc Technology Inc." [Gig] 97 32 19 79 59 71 Prodisc [Aopen] 97 32 19 79 59 71 Prodisc [Btc] 97 32 19 79 59 72 654.49MB Prodise 48X [Msi] 97 32 19 79 59 72 654.49MB Prodise 48X [Msi] 97 32 19 79 59 72 702.83MB Digmaster(Prodisc) 40X [Msi] 97 32 19 79 59 72 702.83MB Digmaster(Prodisc) 48X [Msi] 97 32 19 79 59 72 702.83MB LG(Prodisc) 1-48X [Msi] 97 32 19 79 59 72 702.83MB Mitsubishi (Prodisc) Multi Speed [Msi] 97 32 19 79 59 72 702.83MB Prodise 48X [Msi] 97 32 19 79 59 72 80 52x "Prodisc Technology Inc." [Gig] 97 32 19 79 59 72 Mitsubishi CD-R Multi Speed 702.83MB [Mits] 97 32 19 79 59 72 Prodisc [Aopen] 97 32 19 79 59 72 Prodisc [Btc] 97 32 19 79 59 73 654.49MB Prodisc 40X [Msi] 97 32 19 79 59 73 702.83MB Media Market(Prodisc) 1-40X [Msi] 97 32 19 79 59 73 80 48x "Prodisc Technology Inc." [Gig] 97 32 19 79 59 73 Prodisc [Aopen] 97 32 19 79 59 73 Prodisc [Btc] 97 32 19 79 59 74 80 48x "Prodisc Technology Inc." [Gig] 97 32 19 79 59 74 Prodisc [Aopen] 97 32 19 79 59 74 Prodisc [Btc] "Mitsubishi Chemical Corporation" 97 34 21 74 43 00 74 24x "Mitsubishi Chemical Corporation" [Gig] 97 34 21 74 43 00 MCC [Btc] 97 34 21 79 59 74 702.83MB Mitsubishi Multi Speed [Msi] 97 34 21 79 59 74 MCC [Btc] 97 34 21 79 59 74 MCC(Metal [Aopen] 97 34 22 4x "Mitsubishi Chemical Corporation" [Gig] 97 34 22 74 43 00 656.40MB Mitsubishi N/A 1-4X [Msi] 97 34 22 74 43 00 74 32x "Mitsubishi Chemical Corporation" [Gig] 97 34 22 74 43 00 MCC [Btc] 97 34 22 74 43 00 Mitsubishi CD-RW 1-4X 656.40MB [Mits] 97 34 22 79 59 74 702.83MB Mitsubishi 32X [Msi] 97 34 22 79 59 74 80 32x "Mitsubishi Chemical Corporation" [Gig] 97 34 22 79 59 74 MCC [Btc] 97 34 22 79 59 74 Mitsubishi CD-RW 1-4X 702.83MB [Mits] 97 34 23 74 43 00 656.40MB Mitsubishi 4-10X [Msi] 97 34 23 74 43 00 74 10X "Mitsubishi Chemical Corporation" [Gig] 97 34 23 74 43 00 MCC [Aopen] 97 34 23 74 43 00 Mitsubishi HS-RW 4-10X 656.40MB [Mits] 97 34 23 74 43 00 Mitsubishi HS-RW 4-12X 656.40MB [Mits] 97 34 23 74 43 00 Verbatim HS-RW 4-12X 656.40MB [Mits] 97 34 23 74 43 01 656.40MB Mitsubishi 52X [Msi] 97 34 23 74 43 01 MCC [Aopen] 97 34 23 74 43 01 MCC [Btc] 97 34 23 74 43 01 Mitsubishi CD-R 52X 656.40MB [Mits] 97 34 23 79 59 73 702.83MB Mitsubishi 52X [Msi] 97 34 23 79 59 73 702.83MB Mitsubishi 52X [Msi] 97 34 23 79 59 73 702.83MB Mitsubishi [Msi] 97 34 23 79 59 73 702.83MB Verbatim(Mitsubishi) 48X [Msi] 97 34 23 79 59 73 702.83MB Verbatim(Mitsubishi) 48X [Msi] 97 34 23 79 59 73 702.83MB YAMAHA(Mitsubishi) 48X [Msi] 97 34 23 79 59 73 MCC [Aopen] 97 34 23 79 59 73 MCC [Btc] 97 34 23 79 59 73 Mitsubishi CD-R 52X 702.83MB [Mits] 97 34 23 79 59 73 Mitsubishi CD-R 52X 702.83MB [Mits] 97 34 23 79 59 74 702.83MB Mitsubishi 40X [Msi] 97 34 23 79 59 74 702.83MB Verbatim(Mitsubishi) 40X [Msi] 97 34 23 79 59 74 702.83MB Verbatim(Mitsubishi) N/A 4-10X [Msi] 97 34 23 79 59 74 80 48x "Mitsubishi Chemical Corporation" [Gig] 97 34 23 79 59 74 MCC [Aopen] 97 34 23 79 59 74 MCC [Aopen] 97 34 23 79 59 74 MCC [Btc] 97 34 23 79 59 74 Mitsubishi HS-RW 12X 702.83MB [Mits] 97 34 23 79 59 74 Verbatim HS-RW 4-10X 702.83MB [Mits] 97 34 23 79 59 76 80 52x "Mitsubishi Chemical Corporation" [Gig] 97 34 24 74 43 00 656.40MB Mitsubishi 24X [Msi] 97 34 24 74 43 00 74 24x "Mitsubishi Chemical Corporation" [Gig] 97 34 24 74 43 00 Mitsubishi US-RW 24X 656.40MB [Mits] 97 34 24 79 59 74 702.83MB Mitsubishi 24X [Msi] 97 34 24 79 59 74 Mitsubishi US-RW 24X 702.83MB [Mits] 97 34 24 79 59 74 Verbatim US-RW 24X 702.83MB [Mits] 97 34 25 74 43 00 656.40MB Mitsubishi(standard) 32X [Msi] 97 34 25 74 43 00 74 32x "Mitsubishi Chemical Corporation" [Gig] "Mitsui Chemicals Inc." 97 48 55 63 04 00 MCI(Mitsui) MTCDR63G [Teac] 97 48 55 63 04 00 MCI(MITSUI) MTCDR63G [Edig] "TDK Corporation" 97 49 00 63 52 00 TDK CD-R63S [Teac] 97 49 00 63 52 00 TDK CD-R63S [Edig] ------------------------------------------------------------------------------- DVD and BD Media IDs "UML" AML 003 UME 8X [Hij] "BeAll Developers, Inc." BeAll000 P40 BeAll 4X [Hij] BeAll G40001 BeAll 4X [Hij] "CMC Magnetics Corporation" CMC MAG M01 CMC 16X [Hij] CMC MAG F01 CMC 4X [Hij] CMC MAG R01 CMC 2.5X [Hij] CMC MAG W01 CMC 2.5x [Hij] CMC MAG. AE1 CMC 8X [Hij] CMC MAG. AF1 CMC 4X [Hij] CMC MAG. AM1 CMC 12X [Hij] CMCW02 CMC 2x [Hij] CMC00RG200 CMC 2X [Hij] CMCMAG CMC 12X [Hij] CMCMAG BA2 CMC Magnetics Corporation 1-2X HTL 25GB (12cm) [Blu] CMCMAG BA3 CMC Magnetics Corporation 1-4X HTL 25GB (12cm) [Blu] CMCMAG BA5 CMC Magnetics Corporation 1-6X HTL 25GB (12cm) [Blu] CMCMAG CN2 CMC Magnetics Corporation 1-2X HTL 25GB(12cm) [Blu] "Daxon Technology Inc. / Acer" DAXON 016 DAXON 8X [Hij] DAXON AZ3 DAXON 16X [Hij] DAXON CY3 DAXON 12X [Hij] DAXON D42/52 (user reported DVD+RW) Daxon R2X Daxon Technology Inc. 1-2X HTL 25GB(12cm) [Blu] Daxon R4X Daxon Technology Inc. 1-4X HTL 25GB(12cm) [Blu] "Fujifilm Holdings Corporation" FUJI Fujifilm Corporation [Blu] FUJIFILM02 FUJIFILM 4X [Hij] FUJIFILM03 FUJIFILM 8X [Hij] FUJIFILM04 FUJIFILM 12X [Hij] "New Star Digital Co., Ltd." INFODISC R20 NSD 8X [Hij] INFODISC-R20 NSD 8X [Hij] "InfoMedia Inc." INFOME E20 INFOMEDIA INC. 1-2X [Blu] INFOME R30 INFOMEDIA 16X [Hij] INFOME R20 INFOMEDIA INC. 1-2X [Blu] INFOME R30 INFOMEDIA INC. 1-4X [Blu] "Info Source Multi Media Ltd." ISMMBD R01 Info Source Multi Media Ltd. 1-4X HTL 12cm [Blu] ISMMBD R02 Info Source Multi Media Ltd. 1-6X HTL 12cm [Blu] ISMMBD RE1 Info Source Multi Media Ltd. 1-2X HTL 12cm [Blu] "JVC Limited" JVC0VictorD7 JVC 4X [Hij] JVC1Victord7 JVC 6X [Hij] JVC/VictorT7 JVC 1x [Hij] JVC_VictorW7 JVC 2x [Hij] JVCRE1 Victor Company of Japan, Limited [Blu] "AMC" KIC01RG160 AMC 12X [Hij] "Lead Data Inc." LD M04 LEADDATA 8X [Hij] LD S04 LEADDATA 8X [Hij] LEADDATA01 LEADDATA 4X [Hij] "LG Electronics" LGE04 LG 2X [Hij] LGEBRA S04 LG Electronics Inc. 1-4X HTL 12cm (25GB) [Blu] LGEBRA S06 LG Electronics Inc. 1-6X HTL 12cm (25GB) [Blu] LGEBRE S01 LG Electronics, Inc. 1-2X HTL 12cm(25GB) [Blu] LGEP16 001 LGE 8X [Hij] "Mitsui Advanced Media, Inc. Europe" MAM M02 MAM-E 8X [Hij] "Hitachi Maxell Ltd." MAXELL 001 HITACHI MAXELL 4X [Hij] MAXELL 002 HITACHI MAXELL 8X [Hij] MAXELL 003 HITACHI MAXELL 16X [Hij] MAXELL ES1 Hitachi Maxell, Ltd. 1-2X HTL 12cm and 8cm [Blu] MAXELL RS1 Hitachi Maxell, Ltd. 1-2X HTL 12cm and 8cm [Blu] "Moser Baer India Limited" MBI E02 Moser Baer India Ltd 1-2X HTL 25GB [Blu] MBI F01 Moser Baer India Ltd 1-2X LTH 25GB [Blu] MBI F02 Moser Baer India Ltd 1-4X LTH 25GB [Blu] MBI R02 Moser Baer India Ltd 1-2X HTL 25GB [Blu] MBI R04 Moser Baer India Ltd 1-4X HTL 25GB [Blu] MBI R06 Moser Baer India Ltd 1-6X HTL 25GB [Blu] MBI 01RG20 Moser Bear India 4X [Hij] MBI 01RG40 MBI 16X [Hij] MBI 03RG30 MBI 8X [Hij] MBI 03RG40 MBI 12X [Hij] MBIPG101 Moser Bear India 4X [Hij] MBIPG101 R04 MBI 8X [Hij] MBIPG101 R05 MBI 16X [Hij] "Mitsubishi Chemical Corporation" MCC 002 MKM 4X [Hij] MCC 003 MKM 8X [Hij] MCC 00RG200 MKM 2X [Hij] MCC 01RG20 MKM 4X [Hij] MCC 01RW11n9 MKM 2x [Hij] MCC 01RW4X MKM 4X [Hij] MCC 02RG20 MKM 8X [Hij] MCC 03RG20 MKM 16X [Hij] MCC A01 MKM 2.5x [Hij] "Mitsui Chemicals Inc." MCI G01 Mitsui 2X [Hij] "Panasonic Corporation" MEI RA1 Panasonic Corporation 1-6X HTL 12cm [Blu] MEI RB1 Panasonic Corporation 1-6X HTL 12cm [Blu] MEI T01 Panasonic Corporation 1-2X HTL 12cm [Blu] MEI T02 Panasonic Corporation 1-4X HTL 12cm [Blu] MEI 00V001 MATSUSHITA EI 2X [Hij] MEI 00V002 MATSUSHITA EI 4X [Hij] "Millenniata Inc." MILLENMR MR1 Verbatim M-DISC 4x 25 GB (user reported) "Mitsubishi Kagaku Media Co." MKM 001 MKM 6X [Hij] MKM 003 MKM 8X [Hij] MKM A02 MKM 4x [Hij] MKM 01RD30 MKM 6X [Hij] MKM01RW6X01 MKM 6X [Hij] "Mitsubishi Kagaku Media Co." MMC 001 MKM 2.5X [Hij] "Hitachi Maxell Ltd." MXL RG01 MAXELL 2X [Hij] MXL RG02 MAXELL 4X [Hij] MXL RG03 MAXELL 8X [Hij] MXL RG04 MAXELL 16X [Hij] "Nan-Ya Plastics Corporation" NANYA ALX NANYA 8X [Hij] "NESA International Inc." NSDR40 NSD 8X [Hij] "Optodisc Technology Corporation" OPTODISC F16 OPTODISC 12X [Hij] OPTODISC OP1 OPTODISC 2.5x [Hij] OPTODISC OP1 OPTODISC 2.5X [Hij] OPTODISC OR8 OPTODISC 8X [Hij] OPTODISCR004 OPTODISC 4X [Hij] OPTODISCR008 OPTODISC 8X [Hij] OPTODISC R01 OPTODISC 16X [Hij] OPTODISC R02 OPTODISC 4X [Hij] OPTODISCR016 OPTODISC 8X [Hij] "Optodisc Technology Corporation" OTCBDR 001 Optodisc Technology Corporation 1-4X HTL 25GB (12cm) [Blu] OTCBDR 002 Optodisc Technology Corporation 1-6X HTL 25GB (12cm) [Blu] OTCBRE 001 Optodisc Technology Corporation 1-2X HTL 25GB (12cm) [Blu] "Moser Baer India Limited" PHILIP R02 Moser Baer India Ltd 1-2X HTL 25GB [Blu] PHILIP R04 Moser Baer India Ltd 1-4X HTL 25GB [Blu] PHILIPR0 R04 TDK 4x 25 GB (own buy) PHILIP W02 Moser Baer India Ltd 1-2X HTL 25GB [Blu] "Philips" PHILIPS C16 PHILIPS 12X [Hij] PHILIPS CD2 CMC (PHILIPS) 2.5X [Hij] "Princo Corporation" PRINCO PRONCO 4X [Hij] "Prodisc Technology Inc." ProdiscF02 PRODISC 12X [Hij] PRODISC R01 PRODISC 2.5X [Hij] Prodisc R02 PRODISC 4X [Hij] Prodisc R03 PRODISC 8X [Hij] Prodisc R04 PRODISC 16X [Hij] Prodisc R05 PRODISC 16X [Hij] ProdiscS03 PRODISC 4X [Hij] ProdiscS04 PRODISC 8X [Hij] ProdiscS05 PRODISC 16X [Hij] PRODISC W01 PRODISC 2.5x [Hij] "Pioneer" PVC001001 PIONEER VIDEO 2X [Hij] PVCR001002 PIONEER VIDEO 4X [Hij] PVCW00D002K9 PVC 2x [Hij] "Ricoh Company Limited" RICOHJPN D00 RICOH 2.5X [Hij] RICOHJPN D01 RICOH 2.5X [Hij] RICOHJPN R00 RICOHJPN 2.5X [Hij] RICOHJPN R01 RICOH 4X [Hij] RICOHJPN R02 RICOH 12X [Hij] RICOHJPN R03 RICOH 16X [Hij] RICOHJPN W01 RICOH 2.5x [Hij] RICOHJPN W11 RICOH 4x [Hij] RICOHJPN W21 RICOH 8X [Hij] "Ritek Corp" RITEK 001 Ritek 2.5x [Hij] RITEK BR1 RITEK CORPORATION 1-2X HTL 25GB [Blu] RITEK BR2 RITEK CORPORATION 1-4X HTL 25GB [Blu] RITEK BR3 RITEK CORPORATION 1-6X HTL 25GB [Blu] RITEK D01 RITEK 2.5X [Hij] RITEK DR2 RITEK CORPORATION 1-4X HTL 50GB [Blu] RITEK DW1 RITEK CORPORATION 1-2X HTL 50GB [Blu] RITEK F16 RITEK 8X [Hij] RITEKF1 RITEK 12X [Hij] RITEK G03 RITEK 2X [Hij] RITEK G04 RITEK 4X [Hij] RITEKG05 RITEK 4X [Hij] RITEKG06 RITEK 8X [Hij] RITEK M02 RITEK 4X [Hij] RITEKM16 RITEK 12X [Hij] RITEK R01 RITEK 2.5X [Hij] RITEK R02 RITEK 4X [Hij] RITEK R03 RITEK 8X [Hij] RITEK R04 RITEK 12X [Hij] RITEK R05 RITEK 16X [Hij] RITEKW01 RiTEK 2x [Hij] RITEKW06 RITEK 6X [Hij] "Sony Corporation" SONY SONY 2X [Hij] SONY D11 SONY 8X [Hij] SONY D21 SONY 16X [Hij] SONY ED4 Sony Corporation 1-2X HTL 50GB [Blu] SONY ES1 Sony Corporation 1-2X 25GB [Blu] SONY NO1 Sony Corporation 1-2X HTL 25GB [Blu] SONY NS1 Sony Corporation 1-2X HTL 25GB [Blu] SONY NS2 Sony Corporation 1-4X HTL 25GB [Blu] SONY NN1 Sony Corporation 1-2X HTL 25GB [Blu] SONY NN2 Sony Corporation 1-4X HTL 25GB [Blu] SONY NN3 Sony Corporation 1-6X HTL 25GB [Blu] SONY04D1 SONY 4X [Hij] SONY08D1 SONY 12X [Hij] SONY16D1 SONY 16X [Hij] "TDK Corporation" TDK 001 TDK 4X [Hij] TDK 002 TDK 8X [Hij] TDK 003 TDK 16X [Hij] TDKBLD RBA TDK Corporation 1-2X HTL 12cm [Blu] TDKBLD RBB TDK Corporation 1-4X HTL 12cm [Blu] TDKBLD RBD TDK Corporation 1-6x HTL 12cm (25GB) [Blu] TDKBLD RDA TDK Corporation 1-2X HTL 8cm [Blu] TDKBLD RFA TDK Corporation 1-2X HTL 12cm [Blu] TDKBLD RFB TDK Corporation 1-4X HTL 12cm [Blu] TDKBLD RFD TDK Corporation 1-6x HTL 12cm (50GB) [Blu] TDKBLD WBA TDK Corporation 1-2X HTL 12cm [Blu] TDKBLD WDA TDK Corporation 1-2X HTL 8cm [Blu] TDKBLD Wfa TDK Corporation 1-2X HTL 12cm [Blu] TDK502sakuM3 TDK 2x [Hij] TDK601saku TDK 4X [Hij] TDKG02000000 TDK 2X [Hij] "TDK Corporation" TTG01 TDK 4X [Hij] TTG02 TDK 8X [Hij] TTH01 TDK 8X [Hij] TTH02 TDK 12X [Hij] "Taiyo Yuden Company Limited" TYG01 TAIYO YUDEN 4X [Hij] TYG02 TAIYO YUDEN 12X [Hij] TYG03 TAIYO YUDEN 16X [Hij] TYG11 TAIYO YUDEN DVD-R DL 8x TYG-BD Y01 TAIYO YUDEN Co., Ltd. 1-2X LTH [Blu] TYG-BD Y03 TAIYO YUDEN Co., Ltd. 1-4X LTH [Blu] "UmeDisc Limited" UMEDISC DL1 Elite DVD+R DL [User report feb 2012] "Unifino Inc." UTJR001001 UNIFINO 4X [Hij] "Mitsubishi Kagaku Media Co." VERBAT IM0 Mitsubishi Kagaku Media, Co., Ltd. 1-2X 25GB (12cm) and 7.8GB (8cm) [Blu] VERBAT IM1 Mitsubishi kagaku Media, Co., ltd. 1-2X HTL 50GB(12cm) [Blu] VERBAT IMa Mitsubishi Kagaku Media, Co., Ltd. 1-2X HTL 25GB (12cm) and 7.8GB (8cm) [Blu] VERBAT IMb Mitsubishi Kagaku Media, Co., Ltd. 1-2X HTL 50GB (12cm) and 15.6GB (8cm) [Blu] VERBAT IMc Mitsubishi Kagaku Media, Co., Ltd. 1-4X HTL 25GB (12cm) and 7.8GB (8cm) [Blu] VERBAT IMd Mitsubishi Kagaku Media, Co., Ltd. 1-4X HTL 50GB (12cm) [Blu] VERBAT IMe Mitsubishi Kagaku Media, Co., Ltd. 1-6X HTL 25GB (12cm) [Blu] VERBAT IMf Mitsubishi Kagaku Media, Co., Ltd. 1-6X HTL 50GB (12cm) [Blu] VERBAT IMu Mitsubishi Kagaku Media, Co., Ltd. 1-6X LTH 25GB (12cm) [Blu] VERBAT IMv Mitsubishi Kagaku Media, Co., Ltd. 1-4X LTH 25GB (12cm) [Blu] VERBAT IMw Mitsubishi Kagaku Media, Co., Ltd. 1-2X LTH 25GB (12cm) [Blu] "Taiyo Yuden Company Limited" YUDEN000 T01 TAIYO YUDEN 4X [Hij] YUDEN000 T02 TAIYO YUDEN 12x [Hij] YUDEN000 T03 TAIYO YUDEN 16X [Hij] Sound extraction for CD-DA burning from .WAV audio file format Using information and text snippets from https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ in may 2013. The link is now dead. An apparent copy of the page is 2017 at: http://soundfile.sapp.org/doc/WaveFormat/ from https://en.wikipedia.org/wiki/WAV For libburnia-project.org by Thomas Schmitt <scdbackup@gmx.net> December 2017 The WAVE file format is an application of the Microsoft RIFF container format for multimedia files. A RIFF file consists of Chunks which contain Subchunks. The Chunks form a linked list within the file, the Subchunks form a linked list inside their Chunk. All numbers are stored in little-endian byte order. A .WAV file consists at least of one Chunk with id "RIFF", which contains one Subchunk with id "fmt " and one with id "data": Offset Size Name Description 0 4 ChunkID Contains the letters "RIFF" 4 4 ChunkSize The size of the rest of the chunk following this field. I.e. the two fields ChunkID and ChunkSize are not included in this count. 8 4 Format Contains the letters "WAVE" The "fmt " subchunk describes the sound data's format: Offset Size Name Description 0 4 Subchunk1ID Contains the letters "fmt " 4 4 Subchunk1Size The size of the rest of the Subchunk following this field. I.e. Subchunk1ID and Subchunk1Size are not included in this count. 8 2 AudioFormat PCM = 1 (i.e. Linear quantization) Values other than 1 indicate some form of compression. 10 2 NumChannels Mono = 1, Stereo = 2, etc. 12 4 SampleRate 8000, 44100, etc. 16 4 ByteRate == SampleRate * NumChannels * BitsPerSample/8 20 2 BlockAlign == NumChannels * BitsPerSample/8 The number of bytes for one sample including all channels. 22 2 BitsPerSample 8 bits = 8, 16 bits = 16, etc. More data may follow in this Subchunk if AudioFormat is not PCM. The "data" subchunk contains the size of the data and the actual sound: Offset Size Name Description 0 4 Subchunk2ID Contains the letters "data" 4 4 Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8 The number of audio data bytes. 8 * Data The audio data bytes. CD-DA prescribes these "fmt " parameters: AudioFormat == 1 SampleRate == 44100 BitsPerSample == 16 NumChannels == 2 (stereo) (little-endian byte order) If matching parameters are given in the .WAV file, one can directly use the data bytes of Subchunk "data" as payload for burning a CD-DA track. Above simple form can be expanded by other Chunks or Subchunks of Chunk "RIFF". A .wav file appeared which beared a Subchunk "LIST" inside Chunk "RIFF". Wikipedia mentions Chunks "INFO", "CSET", "JUNK", "PAD ". Therefore one should expect such Chunks before Chunk "RIFF" and Subchunks other than "fmt " and "data" inside the "RIFF" Chunk. Multiple Chunks "RIFF" and Subchunks "fmt " or "data" per file have not been seen yet. They would make extraction more cumbersome. prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libburn Description: Library to read/write optical discs Version: @VERSION@ Requires: Libs: -L${libdir} -lburn Libs.private: @THREAD_LIBS@ @LIBBURN_ARCH_LIBS@ Cflags: -I${includedir}/libburn all clean: $(MAKE) -C .. -$(MAKEFLAGS) $@ .PHONY: all clean pkgconfigdir=$(libdir)/pkgconfig libincludedir=$(includedir)/libburn 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 ## ========================================================================= ## List of assert() calls in libburn. 6 Oct 2006. Format: ------------------------------------------------------------------------------ Number) grep'ed line (++ before number means: is fully done, + means is done so far ) function(): Description of abort condition. Possible callers and their relation to the abort condition. : Error Evaluation => Consequences Eventual implementation timestamp ------------------------------------------------------------------------------ ++ 1) libburn/async.c: assert(a != NULL); /* wasn't found.. this should not be possible */ static remove_worker(): A thread describing structure (struct w_list) could not be found in order to be released. Called by API burn_drive_scan() Called by static erase_worker_func() , thread under API burn_disc_erase() Called by static write_disc_worker_func(), thread under API burn_disc_write() All three want to clean up after they are done. : Severe Libburn Error => issue LIBDAX_MSGS_SEV_WARNING ts A61006 ------------------------------------------------------------------------------ ++ 2) libburn/async.c: assert(!(workers && workers->drive)); API burn_drive_scan(): Before spawning a thread, the function refuses work because another drive activity is going on. : Severe Application Error => return -1; redefine @return in API , issue LIBDAX_MSGS_SEV_SORRY ts A61006 ------------------------------------------------------------------------------ + 3) libburn/async.c: assert(workers == NULL); API burn_drive_scan(): After thread is done and remover_worker() succeeded, there is still a worker registered. Shall probably detect roguely appeared burn or erase runs. (I consider to install a mutex shielded function for that.) : Severe Libburn Error => Same as 1) ts A61006 ------------------------------------------------------------------------------ ++ 4) libburn/async.c: assert(drive); libburn/async.c: assert(!SCAN_GOING()); libburn/async.c: assert(!find_worker(drive)); API burn_disc_erase(): Wants to see a drive (assumes NULL == 0), wants to see no scan and wants to see no other worker on that drive. I.e. this would tolerate a parallel activity on another drive. : Severe Application Error => (no return value), issue LIBDAX_MSGS_SEV_SORRY ts A61006 ------------------------------------------------------------------------------ ++ 5) libburn/async.c: assert(!SCAN_GOING()); libburn/async.c: assert(!find_worker(opts->drive)); API burn_disc_write(): Same as 4) : Severe Application Error => Same as 4) ts A61006 --------------------------------------------------------------------- ++ 6) libburn/drive.c: assert(d->busy == BURN_DRIVE_IDLE); API burn_drive_release(): A drive is not idle on release. : Severe Application Error => Same as 4) ts A61007 ------------------------------------------------------------------------------ ++ 7) libburn/drive.c: assert(d->released); burn_wait_all() A drive is found grabbed. Called by burn_drive_scan_sync(), thread under API burn_drive_scan() Called by API burn_finish : Severe Application Error => rename and redefine burn_wait_all() : now burn_drives_are_clear() => change all use of burn_wait_all() => Move tests up to burn_drive_scan() => There: return -1; issue LIBDAX_MSGS_SEV_SORRY ts A61007 ------------------------------------------------------------------------------ ++ 8) libburn/drive.c: assert(!d->released); API burn_disc_get_status() Attempt to read status of non-grabbed drive. : Severe Application Error => extend enum burn_disc_status by BURN_DISC_UNGRABBED => return BURN_DISC_UNGRABBED, issue LIBDAX_MSGS_SEV_SORRY ts A61007 ------------------------------------------------------------------------------ ++ 9) libburn/drive.c: assert( /* (write_type >= BURN_WRITE_PACKET) && */ burn_drive_get_block_types(): Will not work on BURN_WRITE below BURN_WRITE_RAW. Called by -nobody- ? : Severe Application Error => inactivate unused function ts A61007 ------------------------------------------------------------------------------ ++ 10) libburn/drive.c: assert(d->idata); libburn/drive.c: assert(d->mdata); static drive_getcaps(): sg.c:enumerate_common() did not succeed in creating a proper struct burn_drive Called by burn_drive_scan_sync() : Severe System Error => This could possibly really stay an abort() because the reason is a plain failure of the system's memory management. => Detect this failure already in enumerate_common(), issue LIBDAX_MSGS_SEV_FATAL, return ts A61007 ------------------------------------------------------------------------------ ++ 11) libburn/drive.c: assert(burn_running); burn_drive_scan_sync(): The library was not initialized. Called as thread by API burn_drive_scan() : Severe Application Error => Move this test up to burn_drive_scan() => There: return -1; redefine @return in API , issue LIBDAX_MSGS_SEV_FATAL ts A61007 ------------------------------------------------------------------------------ ++ 12) libburn/drive.c: assert(d->released == 1); burn_drive_scan_sync(): Inactivated : (Severe Application Error) => throw out inactivated code ts A61007 ------------------------------------------------------------------------------ ++ 13) libburn/drive.c: assert(strlen(d->devname) < BURN_DRIVE_ADR_LEN); burn_drive_raw_get_adr(): An enumerated device address is longer than the API's maximum length Called by API burn_drive_get_adr() Called by API burn_drive_obtain_scsi_adr() : Severe Libburn Error => return -1; in all three functions, enhance burn_drive_get_adr @return docs => issue LIBDAX_MSGS_SEV_SORRY ts A61007 ------------------------------------------------------------------------------ ++ 14) libburn/drive.c: assert(drive_info->drive!=NULL); API burn_drive_get_adr(): Drive info has no drive attached. : Severe Libburn Error (unlikely, will eventually SIGSEGV on NULL) => delete assert ts A61007 ------------------------------------------------------------------------------ ++ 15) libburn/init.c: assert(burn_running); API burn_finish(): The library is not initialized : Severe Application Error => return (assume no msg system) ts A61007 ------------------------------------------------------------------------------ ++ 16) libburn/init.c: assert(burn_running); API burn_preset_device_open(): The library is not initialized : Severe Application Error => return (assume no msg system) ts A61007 ------------------------------------------------------------------------------ ++ 17) libburn/mmc.c: assert(o->drive == d); mmc_close_disc(): alias: struct burn_drive.close_disc() Parameters struct burn_drive and struct burn_write_opts do not match Called by -nobody- ? ( => Disable unused function ? ) => removed redundant parameter struct burn_drive ts A61009 ------------------------------------------------------------------------------ ++ 18) libburn/mmc.c: assert(o->drive == d); mmc_close_session(): Same as 17) alias: struct burn_drive.close_session() Called by -nobody- ? ( => Disable unused function ? ) => removed redundant parameter struct burn_drive ts A61009 ------------------------------------------------------------------------------ ++ 19) libburn/mmc.c: assert(buf->bytes >= buf->sectors); /* can be == at 0... */ mmc_write_12(): - Unclear what .bytes and .sectors mean in struct buffer - Called by -nobody- ? => problems with filling the write buffer have to be handled by callers => delete assert ts A61009 ------------------------------------------------------------------------------ ++ 20) libburn/mmc.c: assert(buf->bytes >= buf->sectors); /* can be == at 0... */ mmc_write(): - Unclear what .bytes and .sectors mean in struct buffer - libburn/mmc.c: c.page->sectors = errorblock - start + 1; mmc_read_sectors() by toc_find_modes() by mmc_read_toc() alias drive.read_toc() by burn_drive_grab() This seems to be unrelated to mmc_write(). libburn/sector.c: out->sectors++; get_sector() Seems to hand out sector start pointer in opts->drive->buffer and to count reservation transactions as well as reserved bytes. Ensures out->bytes >= out->sectors libburn/mmc.c: c.page->bytes = s->count * 8; mmc_send_cue_sheet() Does not use mmc_write() but directly (sg_)issue_command() libburn/sector.c: out->bytes += seclen; get_sector() See above Ensures out->bytes >= out->sectors libburn/spc.c: c.page->bytes = 8 + 2 + d->mdata->retry_page_length; spc_select_error_params() Does not use mmc_write() but directly (sg_)issue_command() libburn/spc.c: c.page->bytes = 8 + 2 + d->mdata->write_page_length; spc_select_error_params() Does not use mmc_write() but directly (sg_)issue_command() libburn/spc.c: c.page->bytes = 8 + 2 + 0x32; spc_probe_write_modes() Does not use mmc_write() but directly (sg_)issue_command() alias struct burn_drive.write() Called by static get_sector, by many Called by burn_write_flush Called by burn_write_track => problems with filling the write buffer have to be handled by callers => delete assert ts A61009 ------------------------------------------------------------------------------ ++ 21) libburn/mmc.c: assert(((dlen - 2) % 11) == 0); mmc_read_toc(): - Is defunct - => :) ts A61009 ------------------------------------------------------------------------------ ++ 22) libburn/mmc.c: assert(len >= 0); mmc_read_sectors(): Catches a bad parameter alias: struct burn_drive.read_sectors() Called by API burn_disc_read() , - is defunct -, one could catch the problem Called by toc_find_modes(), problem cannot occur: mem.sectors = 1; : Severe Libburn Error (=> in burn_disc_read() check page.sectors before d->read_sectors() ) => :) ts A61009 ------------------------------------------------------------------------------ ++ 23) libburn/mmc.c: assert(d->busy); mmc_read_sectors(): Catches use of a drive that is not marked as busy alias: struct burn_drive.read_sectors() Called by API burn_disc_read() , - is defunct -, busy = BURN_DRIVE_READING; Called by toc_find_modes(), does the same assert. To be solved there. : Severe Libburn Error => :) ts A61009 ------------------------------------------------------------------------------ ++ 24) libburn/options.c: assert(0); API burn_write_opts_set_write_type(): Detects unsuitable enum burn_write_types write_type and int block_type. API promises return 0 on failure : Severe Application Error => issue LIBDAX_MSGS_SEV_SORRY => should also detect problem of 26) : wrong write_type,block_type combination by calling sector_get_outmode() and checking for -1 => should also detect problem of 41) : unknown block_type by spc_block_type() and checking for -1 => delete assert(0) ts A61007 ------------------------------------------------------------------------------ ++ 25) libburn/read.c: assert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000)); libburn/read.c: assert(!d->busy); libburn/read.c: assert(d->toc->valid); libburn/read.c: assert(o->datafd != -1); API burn_disc_read(): - ? - burn_disc_read() is defunct OPTIONS_VERSION does not occur outside this line ( => one would return ) ( 22) => catch page.sectors<0 before d->read_sectors() ) ( 37) => catch ! d->mdata->valid ) => :) ts A61007 ------------------------------------------------------------------------------ ++ 26) libburn/sector.c: assert(0); /* return BURN_MODE_UNIMPLEMENTED :) */ static get_outmode(): burn_write_opts is wrongly programmed with .write_type and .block_type : Severe Application Error => This gets handled by burn_write_opts_set_write_type() ts A61007 by new semi-public sector_get_outmode() => delete assert() ts A61007 ------------------------------------------------------------------------------ ++ 27) libburn/sector.c: assert(outlen >= inlen); libburn/sector.c: assert(outmode & BURN_MODE_RAW); libburn/sector.c: assert(offset != -1); static convert_data(): Several unacceptable settings within struct burn_write_opts Called by sector_toc() sector_pregap() sector_postgap() sector_lout() sector_data() : Severe Application Error => change return type of convert_data() => all callers interpret return value and eventually return failure ts A61007 ------------------------------------------------------------------------------ ++ 28) libburn/sector.c: assert(0); static char_to_isrc(): Called by subcode_user() with data set by API burn_track_set_isrc() Some character conversion fails on wrong input : Severe Application Error => burn_track_set_isrc() has to make sure that only good data are set => char_to_isrc() returns 0 as default => delete assert() ts A61008 ------------------------------------------------------------------------------ ++ 29) libburn/sector.c: assert(qmode == 1 || qmode == 2 || qmode == 3); subcode_user(): - can not happen - : Unknown reason of assert() => remove assert() ts A61010 ------------------------------------------------------------------------------ ++ 30) libburn/sector.c: assert(modebyte == 1); sector_headers(): Does only accept modes BURN_AUDIO, BURN_MODE1 or write_type BURN_WRITE_SAO Called by sector_toc() sector_pregap() sector_postgap() sector_lout() sector_data() : Severe Libburn Error => new functions sector_headers_is_ok(), burn_disc_write_is_ok() help to catch problem in API burn_disc_write() => issue LIBDAX_MSGS_SEV_FATAL ts A61009 ------------------------------------------------------------------------------ ++ 31) libburn/sector.c: assert(0); process_q() - defunct - => :) ts A61009 ------------------------------------------------------------------------------ ++ 32) libburn/sg.c: assert("drive busy" == "non fatal"); sg_handle_busy_device(): Intentional abort preset by the app => change to abort() ts A61007 ------------------------------------------------------------------------------ ++ 33) libburn/sg.c: assert(fd != -1337); sg_grab(): The drive device file could not be opened :Severe External Problem => obsolete by normal drive open failure handling ts A61007 ------------------------------------------------------------------------------ ++ 34) libburn/sg.c: assert(!c->page); sg_issue_command(): An SCSI command of direction NO_TRANSFER may not have a .page != NULL. Since it is about exposing a libburn detail towards the sg driver, i believe it is sufficient to just not use it. : Libburn Error => enhance internal logics of sg_issue_command() ts A61007 ------------------------------------------------------------------------------ ++ 35) libburn/sg.c: assert(c->page->bytes > 0); sg_issue_command(): An SCSI command of direction TO_DRIVE wants to transfer 0 bytes. : Severe Libburn Error => set command.error = 1 and return 0 ts A61010 ------------------------------------------------------------------------------ ++ 36) libburn/sg.c: assert(err != -1); sg_issue_command(): The transfer of the command via ioctl() failed : Severe Transport Level Problem => close drive fd, set idle and released => set command.error = 1 and return -1 ts A61010 ------------------------------------------------------------------------------ ++ 37) libburn/spc.c: assert(d->mdata->valid); spc_select_error_params(): Drive was not properly programmed alias struct burn_drive.send_parameters() Called by burn_disc_read, - defunct - : Severe Application Error => moved up as mangled assert to burn_disc_read() ts A61007 ------------------------------------------------------------------------------ ++ 38) libburn/spc.c: assert(d->mdata->cdr_write || d->mdata->cdrw_write || spc_sense_write_params(): Drive does not offer write of any known media type alias struct burn_drive.read_disc_info() Called by API burn_drive_grab (assert test made there in soft) : Severe Command Level Problem => remove assert() ts A61007 ------------------------------------------------------------------------------ ++ 39) libburn/spc.c: assert(o->drive == d); spc_select_write_params(): Drive does not match struct burn_write_opts alias struct burn_drive.send_write_parameters() Called by mmc_close_disc() (-defunct- ?), mmc_close_session() (-defunct- ?), burn_write_track() (d = o->drive;), burn_disc_write_sync() d = (o->drive;) : Severe Libburn Error => remove assert() ts A61007 ------------------------------------------------------------------------------ ++ 40) libburn/spc.c: assert(d->mdata->valid); spc_select_write_params(): Drive was not properly programmed Called by (see 39) burn_write_track() by burn_write_session() by burn_disc_write_sync() burn_disc_write_sync() indirectly by API burn_disc_write() : Severe Libburn Error => caught in burn_disc_write() now ts A61007 ------------------------------------------------------------------------------ ++ 41) libburn/spc.c: assert(0); spc_block_type(): Unknown value with enum burn_block_types Called by spc_select_write_params, uses burn_write_opts.block_type, set by API burn_write_opts_set_write_type() : Severe Application Error => catch in API burn_write_opts_set_write_type by calling spc_block_type() => delete assert ts A61007 ------------------------------------------------------------------------------ ++ 42) libburn/structure.c: assert(!(pos > BURN_POS_END));\ macro RESIZE An illegal list index is given by the app. ( TO->NEW##s obviusly means to append "s" to cpp result of TO->NEW ) Used by API burn_session_add_track() and API burn_disc_add_session() : Severe Application Error => replace assert by if-and-return-0 ts A61008 ------------------------------------------------------------------------------ ++ 43) libburn/structure.c: assert(s->track != NULL); API burn_session_remove_track() An application supplied pointer is NULL : Severe Application Error => replace by if-and-return-0 ts A61008 ------------------------------------------------------------------------------ ++ 44) libburn/structure.c: assert((country[i] >= '0' || country[i] < '9') && libburn/structure.c: assert((owner[i] >= '0' || owner[i] < '9') && libburn/structure.c: assert(year <= 99); libburn/structure.c: assert(serial <= 99999); API burn_track_set_isrc(): Illegal texts supplied by application. The logical expression is always true ! : Severe Application Error => issue LIBDAX_MSGS_SEV_SORRY and return => delete assert => delete assert 28) in char_to_isrc() ts A61008 ------------------------------------------------------------------------------ ++ 45) libburn/toc.c: assert(0); /* unhandled! find out ccd's static write_clonecd2(): - defunct -, - unused - => mangle assert ts A61008 ------------------------------------------------------------------------------ ++ 46) libburn/toc.c: assert(d->busy); toc_find_modes(): The drive to work on is not marked busy Called by mmc_read_toc() alias read_toc() by ... burn_drive_grab() : Severe Libburn Error => to be prevented on the higher levels => delete assert ts A61008 ------------------------------------------------------------------------------ ++ 47) libburn/util.c: assert(s); burn_strdup() Abort on NULL string which would elsewise cause a SIGSEGV Used once in enumerate_common() with a string that worked with open(2) before : Severe Libburn Error => delete assert ts A61008 ------------------------------------------------------------------------------ ++ 48) libburn/util.c: assert(s); burn_strndup(): - unused - Same as 47 : Severe Libburn Error => return NULL => delete assert ts A61008 ------------------------------------------------------------------------------ ++ 49) libburn/util.c: assert(n > 0); burn_strndup(): - unused - Prevent problems by negative copy length : Severe Libburn Error => return NULL => delete assert ts A61008 ------------------------------------------------------------------------------ ++ 50) libburn/write.c: assert(0); static type_to_ctrl(): Unsuitable mode to be converted into "ctrl" Called by static type_to_form() finally burn_create_toc_entries() : Severe Application Error => to be caught in burn_track_define_data by calling for test type_to_form() => return -1; ts A61008 ------------------------------------------------------------------------------ ++ 51) libburn/write.c: assert(0); libburn/write.c: assert(0); /* XXX someone's gonna want this sometime */ static type_to_form(): Does not like BURN_MODE0 or BURN_MODE2 but tolerates unknown modes Called by static burn_create_toc_entries() by burn_disc_write_sync() : Undocumented Libburn Restriction => set *form = -1 , *ctladr = 0xff , return => make function non-static => call for test in API burn_track_define_data() ts A61009 ------------------------------------------------------------------------------ ++ 52) libburn/write.c: assert(ptr); static add_cue(): realloc() failed Called by burn_create_toc_entries() by burn_disc_write_sync() (burn_create_toc_entries is ignorant towards own potential memory problems) (This could possibly really stay an abort() because the reason is a plain failure of the system's memory management.) : Severe System Error => change return type of add_cue to int => react on return -1 in burn_create_toc_entries, return NULL on failure => abort burn_disc_write_sync() on NULL return ts A61009 ------------------------------------------------------------------------------ ++ 53) libburn/write.c: assert(d->toc_entry == NULL); burn_create_toc_entries(): Multiple usage of struct burn_drive.toc_entry Called by burn_disc_write_sync() This will probably trigger an abort with disc->sessions > 1 (disc->sessions is incremented in macro RESIZE() as "NEW##s") : Design Problem ( => ? disallow multiple sessions ? ) => replace assert by soft means and wait what happens ts A61009 ------------------------------------------------------------------------------ ++ 54) libburn/write.c: assert(0); burn_sector_length(): Only BURN_AUDIO, BURN_MODE_RAW, BURN_MODE1 are allowed Called by get_sector(), convert_data(), ... => call burn_sector_length() for test in API burn_track_define_data() => replace assert by -1 ts A61009 ------------------------------------------------------------------------------ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2024 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ /* ts A71019 */ /* Standard measure should be: Threads are created detached. According to the man pages they should then care for disposing themselves. >>> ??? It is yet unclear why the threads vanish from the process list even if joinable and even if never joined. To be activated after release of libburn-0.4.0 */ #define Libburn_create_detached_threadS 1 /* Alternative : Threads are created joinable. Threads get detached in remove_worker() and thus should dispose themselves. #define Libburn_detach_done_workeR 1 */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libburn.h" #include "transport.h" #include "drive.h" #include "write.h" #include "options.h" #include "file.h" #include "async.h" #include "init.h" #include "back_hacks.h" #include <pthread.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <signal.h> /* #include <a ssert.h> */ #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /* ts A80714 : introduced type codes for the worker list */ #define Burnworker_type_scaN 0 #define Burnworker_type_erasE 1 #define Burnworker_type_formaT 2 #define Burnworker_type_writE 3 #define Burnworker_type_fifO 4 #define SCAN_GOING() (workers != NULL && \ workers->w_type == Burnworker_type_scaN) 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; }; /* ts A61230 */ struct format_opts { struct burn_drive *drive; off_t size; int flag; }; struct write_opts { struct burn_drive *drive; struct burn_write_opts *opts; struct burn_disc *disc; }; struct fifo_opts { struct burn_source *source; int flag; }; union w_list_data { struct scan_opts scan; struct erase_opts erase; struct format_opts format; struct write_opts write; struct fifo_opts fifo; }; struct w_list { /* ts A80714 */ int w_type; /* see above define Burnworker_type_* */ struct burn_drive *drive; pthread_t thread; struct w_list *next; union w_list_data u; }; static struct w_list *workers = NULL; static void *fifo_worker_func(struct w_list *w); int burn_async_manage_lock(int mode) { int ret; static pthread_mutex_t access_lock; static int mutex_initialized = 0; static int mutex_locked = 0; if (mode == BURN_ASYNC_LOCK_INIT) { if (mutex_initialized) return 2; ret = pthread_mutex_init(&access_lock, NULL); if (ret != 0) return 0; mutex_initialized = 1; return 1; } if (!mutex_initialized) return 0; if (mode == BURN_ASYNC_LOCK_OBTAIN) { ret = pthread_mutex_lock(&access_lock); if (ret != 0) return 0; mutex_locked = 1; } else if (mode == BURN_ASYNC_LOCK_RELEASE) { if (!mutex_locked) return 2; ret = pthread_mutex_unlock(&access_lock); if (ret != 0) return 0; mutex_locked = 0; } return 1; } 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(int w_type, struct burn_drive *d, WorkerFunc f, union w_list_data *data) { struct w_list *a; struct w_list *tmp; pthread_attr_t *attr_pt = NULL; #ifdef Libburn_create_detached_threadS pthread_attr_t attr; #endif a = calloc(1, sizeof(struct w_list)); a->w_type = w_type; a->drive = d; a->u = *data; burn_async_manage_lock(BURN_ASYNC_LOCK_INIT); /* insert at front of the list */ a->next = workers; tmp = workers; workers = a; if (d != NULL) d->busy = BURN_DRIVE_SPAWNING; #ifdef Libburn_create_detached_threadS /* ts A71019 : Trying to start the threads detached to get rid of the zombies which do neither react on pthread_join() nor on pthread_detach(). */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); attr_pt= &attr; #endif /* Libburn_create_detached_threadS */ /* Worker specific locks are to be released early by the worker */ if (f == (WorkerFunc) fifo_worker_func) burn_async_manage_lock(BURN_ASYNC_LOCK_OBTAIN); if (pthread_create(&a->thread, attr_pt, 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; #ifdef Libburn_detach_done_workeR /* ts A71019 : burry dead puppy before forgetting it */ /* Alternative : threads get detached and thus should dispose themselves. */ pthread_detach(th); /* int ret; char msg[80]; ret = pthread_detach(th); sprintf(msg, "remove_workers(): pid= %lu pthread_detach(%lu)= %d", (unsigned long) getpid(), (unsigned long) th, ret); libdax_msgs_submit(libdax_messenger, -1, 0x00020158, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_LOW, msg, 0, 0); */ #endif /* Libburn_detach_done_workeR */ free(a); break; } /* ts A61006 */ /* a ssert(a != NULL);/ * wasn't found.. this should not be possible */ if (a == NULL) libdax_msgs_submit(libdax_messenger, -1, 0x00020101, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, "remove_worker() cannot find given worker item", 0, 0); } static void *scan_worker_func(struct w_list *w) { int ret; ret = burn_drive_scan_sync(w->u.scan.drives, w->u.scan.n_drives, 1); if (ret <= 0) w->u.scan.done = -1; else w->u.scan.done = 1; return NULL; } static void reset_progress(struct burn_drive *d, int sessions, int tracks, int indices, off_t sectors, int flag) { /* reset the progress indicator */ d->progress.session = 0; d->progress.sessions = sessions; d->progress.track = 0; d->progress.tracks = tracks; d->progress.index = 0; d->progress.indices = indices; d->progress.start_sector = 0; d->progress.sectors = sectors; d->progress.sector = 0; } int burn_drive_scan(struct burn_drive_info *drives[], unsigned int *n_drives) { union w_list_data o; int ret = 0; /* ts A61006 : moved up from burn_drive_scan_sync , former Assert */ if (!burn_running) { libdax_msgs_submit(libdax_messenger, -1, 0x00020109, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Library not running (on attempt to scan)", 0, 0); *drives = NULL; *n_drives = 0; return -1; } /* cannot be anything working! */ /* ts A61006 */ /* a ssert(!(workers && workers->drive)); */ if (workers != NULL && workers->drive != NULL) { drive_is_active:; libdax_msgs_submit(libdax_messenger, -1, 0x00020102, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "A drive operation is still going on (want to scan)", 0, 0); *drives = NULL; *n_drives = 0; return -1; } if (workers == NULL) { /* start it */ /* ts A61007 : test moved up from burn_drive_scan_sync() was burn_wait_all() */ /* ts A70907 : now demanding freed drives, not only released */ if (!burn_drives_are_clear(1)) goto drive_is_active; *drives = NULL; *n_drives = 0; o.scan.drives = drives; o.scan.n_drives = n_drives; o.scan.done = 0; add_worker(Burnworker_type_scaN, NULL, (WorkerFunc) scan_worker_func, &o); } else if (workers->u.scan.done) { /* its done */ ret = workers->u.scan.done; remove_worker(workers->thread); /* ts A61006 */ /* a ssert(workers == NULL); */ if (workers != NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00020101, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, "After scan a drive operation is still going on", 0, 0); return -1; } } else { /* still going */ } return ret; } static void *erase_worker_func(struct w_list *w) { #define Libburn_protect_erase_threaD 1 #ifdef Libburn_protect_erase_threaD sigset_t sigset, oldset; /* Protect blank thread from being interrupted by external signals */ sigfillset(&sigset); sigdelset(&sigset, SIGSEGV); sigdelset(&sigset, SIGILL); pthread_sigmask(SIG_SETMASK, &sigset, &oldset); #endif /* Libburn_protect_erase_threaD */ burn_disc_erase_sync(w->u.erase.drive, w->u.erase.fast); remove_worker(pthread_self()); #ifdef Libburn_protect_erase_threaD /* (just in case it would not end with all signals blocked) */ pthread_sigmask(SIG_SETMASK, &oldset, NULL); #endif /* Libburn_protect_erase_threaD */ return NULL; } void burn_disc_erase(struct burn_drive *drive, int fast) { union w_list_data o; /* ts A61006 */ /* a ssert(drive); */ /* a ssert(!SCAN_GOING()); */ /* a ssert(!find_worker(drive)); */ if(drive == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00020104, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "NULL pointer caught in burn_disc_erase", 0, 0); return; } if ((SCAN_GOING()) || find_worker(drive) != NULL) { libdax_msgs_submit(libdax_messenger, drive->global_index, 0x00020102, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "A drive operation is still going on (want to erase)", 0, 0); return; } reset_progress(drive, 1, 1, 1, (off_t) 0x10000, 0); /* A70103 : will be set to 0 by burn_disc_erase_sync() */ drive->cancel = 1; /* ts A70103 moved up from burn_disc_erase_sync() */ /* ts A60825 : allow on parole to blank appendable CDs */ /* ts A70131 : allow blanking of overwritable DVD-RW (profile 0x13) */ /* ts A70216 : allow blanking of CD-RW or DVD-RW in any regular state and of any kind of full media */ /* ts A70909 : the willingness to burn any BURN_DISC_FULL media is inappropriate. One would rather need a -force option Note: keep this in sync with mmc_read_disc_info() */ /* ts B10321 : Allowed role 5 to be blanked */ if ((drive->drive_role == 1 && drive->current_profile != 0x0a && drive->current_profile != 0x13 && drive->current_profile != 0x14 && drive->status != BURN_DISC_FULL) || (drive->status != BURN_DISC_FULL && drive->status != BURN_DISC_APPENDABLE && drive->status != BURN_DISC_BLANK) || (drive->drive_role != 1 && drive->drive_role != 5) ) { char msg[160]; sprintf(msg, "Drive and media state unsuitable for blanking. (role= %d , profile= 0x%x , status= %d)", drive->drive_role, (unsigned int) drive->current_profile, drive->status); libdax_msgs_submit(libdax_messenger, drive->global_index, 0x00020130, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); return; } o.erase.drive = drive; o.erase.fast = fast; add_worker(Burnworker_type_erasE, drive, (WorkerFunc) erase_worker_func, &o); } /* ts A61230 */ static void *format_worker_func(struct w_list *w) { #define Libburn_protect_format_threaD 1 #ifdef Libburn_protect_format_threaD sigset_t sigset, oldset; /* Protect format thread from being interrupted by external signals */ sigfillset(&sigset); sigdelset(&sigset, SIGSEGV); sigdelset(&sigset, SIGILL); pthread_sigmask(SIG_SETMASK, &sigset, &oldset); #endif /* Libburn_protect_format_threaD */ burn_disc_format_sync(w->u.format.drive, w->u.format.size, w->u.format.flag); remove_worker(pthread_self()); #ifdef Libburn_protect_format_threaD /* (just in case it would not end with all signals blocked) */ pthread_sigmask(SIG_SETMASK, &oldset, NULL); #endif /* Libburn_protect_format_threaD */ return NULL; } /* ts A61230 */ void burn_disc_format(struct burn_drive *drive, off_t size, int flag) { union w_list_data o; int ok = 0, ret; char msg[40]; reset_progress(drive, 1, 1, 1, (off_t) 0x10000, 0); if ((SCAN_GOING()) || find_worker(drive) != NULL) { libdax_msgs_submit(libdax_messenger, drive->global_index, 0x00020102, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "A drive operation is still going on (want to format)", 0, 0); return; } if (drive->drive_role != 1) { libdax_msgs_submit(libdax_messenger, drive->global_index, 0x00020146, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is a virtual placeholder", 0, 0); drive->cancel = 1; return; } if (flag & 128) /* application prescribed format type */ flag |= 16; /* enforce re-format */ if (drive->current_profile == 0x14) ok = 1; /* DVD-RW sequential */ else if (drive->current_profile == 0x13 && (flag & 16)) ok = 1; /* DVD-RW Restricted Overwrite with force bit */ else if (drive->current_profile == 0x1a) { ok = 1; /* DVD+RW */ size = 0; flag &= ~(2|8); /* no insisting in size 0, no expansion */ flag |= 4; /* format up to maximum size */ } else if (drive->current_profile == 0x12) { ok = 1; /* DVD-RAM */ } else if (drive->current_profile == 0x41) { /* BD-R SRM */ ok= 1; ret = drive->read_format_capacities(drive, 0x00); if (ret > 0 && drive->format_descr_type == BURN_FORMAT_IS_FORMATTED) ok = 0; if (drive->status != BURN_DISC_BLANK) ok = 0; if (!ok) { libdax_msgs_submit(libdax_messenger, drive->global_index, 0x00020162, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "BD-R not unformatted blank any more. Cannot format.", 0, 0); drive->cancel = 1; return; } if (flag & 32) { libdax_msgs_submit(libdax_messenger, drive->global_index, 0x00020163, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, "Blank BD-R left unformatted for zero spare capacity.", 0, 0); return; } } else if (drive->current_profile == 0x43) { ok = 1; /* BD-RE */ if ((flag & 32) && !(drive->current_feat23h_byte4 & 8)) { libdax_msgs_submit(libdax_messenger, drive->global_index, 0x00020164, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Drive does not format BD-RE without spares.", 0, 0); drive->cancel = 1; return; } } if (!ok) { sprintf(msg,"Will not format media type %4.4Xh", drive->current_profile); libdax_msgs_submit(libdax_messenger, drive->global_index, 0x00020129, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); drive->cancel = 1; return; } o.format.drive = drive; o.format.size = size; o.format.flag = flag; add_worker(Burnworker_type_formaT, drive, (WorkerFunc) format_worker_func, &o); } static void *write_disc_worker_func(struct w_list *w) { struct burn_drive *d = w->u.write.drive; char msg[80]; #define Libburn_protect_write_threaD 1 #ifdef Libburn_protect_write_threaD sigset_t sigset, oldset; /* Protect write thread from being interrupted by external signals */ sigfillset(&sigset); sigdelset(&sigset, SIGSEGV); sigdelset(&sigset, SIGILL); pthread_sigmask(SIG_SETMASK, &sigset, &oldset); #endif /* Libburn_protect_write_threaD */ d->thread_pid = getpid(); d->thread_tid = pthread_self(); d->thread_pid_valid= 1; burn_disc_write_sync(w->u.write.opts, w->u.write.disc); d->thread_pid_valid= 0; d->thread_pid = 0; /* the options are refcounted, free out ref count which we added below */ burn_write_opts_free(w->u.write.opts); sprintf(msg, "Write thread on drive %d ended", d->global_index); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020178, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); remove_worker(pthread_self()); d->busy = BURN_DRIVE_IDLE; #ifdef Libburn_protect_write_threaD /* (just in case it would not end with all signals blocked) */ pthread_sigmask(SIG_SETMASK, &oldset, NULL); #endif /* Libburn_protect_write_threaD */ return NULL; } void burn_disc_write(struct burn_write_opts *opts, struct burn_disc *disc) { union w_list_data o; char *reasons= NULL; struct burn_drive *d; int mvalid; d = opts->drive; /* ts A61006 */ /* a ssert(!SCAN_GOING()); */ /* a ssert(!find_worker(opts->drive)); */ if ((SCAN_GOING()) || find_worker(opts->drive) != NULL) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020102, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "A drive operation is still going on (want to write)", 0, 0); return; } reset_progress(d, disc->sessions, disc->session[0]->tracks, disc->session[0]->track[0]->indices, (off_t) 0, 0); /* For the next lines any return indicates failure */ d->cancel = 1; /* ts A70203 : people have been warned in API specs */ if (opts->write_type == BURN_WRITE_NONE) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002017c, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "No valid write type selected", 0, 0); return; } if (d->drive_role == 0) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020146, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is a virtual placeholder (null-drive)", 0, 0); return; } if (d->drive_role == 4) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020181, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Pseudo-drive is a read-only file. Cannot write.", 0, 0); return; } /* ts A61007 : obsolete Assert in spc_select_write_params() */ if (d->drive_role == 1) { mvalid = 0; if (d->mdata != NULL) mvalid = 1; if (!mvalid) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020113, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Drive capabilities not inquired yet", 0, 0); return; } } /* ts A70219 : intended to replace all further tests here and many tests in burn_*_write_sync() */ BURN_ALLOC_MEM_VOID(reasons, char, BURN_REASONS_LEN + 80); strcpy(reasons, "Write job parameters are unsuitable:\n"); if (burn_precheck_write(opts, disc, reasons + strlen(reasons), 1) <= 0) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020139, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, reasons, 0, 0); goto ex; } BURN_FREE_MEM(reasons); reasons= NULL; /* ts A90106 : early catching of unformatted BD-RE */ if (d->current_profile == 0x43) if (d->read_format_capacities(d, 0x00) > 0 && d->format_descr_type != BURN_FORMAT_IS_FORMATTED) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020168, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Media not properly formatted. Cannot write.", 0, 0); return; } d->cancel = 0; /* End of the return = failure area */ o.write.drive = d; o.write.opts = opts; o.write.disc = disc; opts->refcount++; add_worker(Burnworker_type_writE, d, (WorkerFunc) write_disc_worker_func, &o); ex:; BURN_FREE_MEM(reasons); } static void *fifo_worker_func(struct w_list *w) { #define Libburn_protect_fifo_threaD 1 #ifdef Libburn_protect_fifo_threaD sigset_t sigset, oldset; /* Protect fifo thread from being interrupted by external signals */ sigfillset(&sigset); sigdelset(&sigset, SIGSEGV); sigdelset(&sigset, SIGILL); pthread_sigmask(SIG_SETMASK, &sigset, &oldset); #endif /* Libburn_protect_fifo_threaD */ burn_fifo_source_shoveller(w->u.fifo.source, w->u.fifo.flag); remove_worker(pthread_self()); #ifdef Libburn_protect_fifo_threaD /* (just in case it would not end with all signals blocked) */ pthread_sigmask(SIG_SETMASK, &oldset, NULL); #endif /* Libburn_protect_fifo_threaD */ return NULL; } int burn_fifo_start(struct burn_source *source, int flag) { union w_list_data o; struct burn_source_fifo *fs = source->data; fs->is_started = -1; /* create and set up ring buffer */; fs->buf = burn_os_alloc_buffer( ((size_t) fs->chunksize) * (size_t) fs->chunks, 0); if (fs->buf == NULL) { /* >>> could not start ring buffer */; return -1; } o.fifo.source = source; o.fifo.flag = flag; add_worker(Burnworker_type_fifO, NULL, (WorkerFunc) fifo_worker_func, &o); fs->is_started = 1; return 1; } int burn_fifo_abort(struct burn_source_fifo *fs, int flag) { int ret; pthread_t pt; burn_async_manage_lock(BURN_ASYNC_LOCK_OBTAIN); if (fs->thread_is_valid <= 0 || fs->thread_handle == NULL) { burn_async_manage_lock(BURN_ASYNC_LOCK_RELEASE); return 2; } pt = *((pthread_t *) fs->thread_handle); burn_async_manage_lock(BURN_ASYNC_LOCK_RELEASE); fs->do_abort = 1; ret = pthread_join(pt, NULL); return (ret == 0); } #ifdef Libburn_has_burn_async_join_alL /* ts A71019 : never used */ void burn_async_join_all(void) { void *ret; while (workers) pthread_join(workers->thread, &ret); } #endif /* Libburn_has_burn_async_join_alL */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2017 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifndef BURN__ASYNC_H #define BURN__ASYNC_H void burn_async_join_all(void); struct burn_write_opts; /* ts A70930 */ /* To be called when the first read() call comes to a fifo */ int burn_fifo_start(struct burn_source *source, int flag); /* ts A81108 */ /* To abort a running fifo thread before the fifo object gets deleted */ int burn_fifo_abort(struct burn_source_fifo *fs, int flag); /* ts B70126 */ #define BURN_ASYNC_LOCK_RELEASE 0 #define BURN_ASYNC_LOCK_OBTAIN 1 #define BURN_ASYNC_LOCK_INIT 2 int burn_async_manage_lock(int mode); #endif /* BURN__ASYNC_H */ /** Copyright (c) 2006 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. This file bundles variables which disable changes in libburn which are not yet completely accepted. The use of these variables is *strongly discouraged* unless you have sincere reason and are willing to share your gained knowledge with the libburn developers. Do *not silently rely* on these variables with your application. Tell us that you needed one or more of them. They are subject to removal as soon as consense has been found about correctness of the change they revoke. Value 0 means that the new behavior is enabled. Any other value enables the described old time behavior. If you doubt one of the changes here broke your application, then do *in your application*, *not here* : - #include "libburn/back_hacks.h" like you include "libburn/libburn.h" - Set the libburn_back_hack_* variable of your choice to 1. In your app. Not here. - Then start and use libburn as usual. Watch out for results. - If you believe to have detected a flaw in our change, come forward and report it to the libburn developers. Thanks in advance. :) */ /** Do not define this macro in your application. Only libburn/init.c is entitled to set it. */ #ifdef BURN_BACK_HACKS_INIT /** Corresponds to http://libburn.pykix.org/ticket/42 Reinstates the old ban not to blank appendable CD-RW. We see no reason for this ban yet. It appears unusual. But maybe it patches a bug. */ int libburn_back_hack_42= 0; #else /* BURN_BACK_HACKS_INIT */ /* Note: no application programmer info beyond this point */ extern int libburn_back_hack_42; #endif /* ! BURN_BACK_HACKS_INIT */ /* Copyright (c) 2011 - 2016 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #include "libburn.h" #include "init.h" #include "structure.h" #include "options.h" #include "util.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /* --------------------- Production of CD-TEXT packs -------------------- */ struct burn_pack_cursor { unsigned char *packs; int num_packs; int td_used; int hiseq[8]; int pack_count[16]; int track_offset; }; /* @param flag bit0= double_byte characters */ int burn_create_new_pack(int pack_type, int track_no, int double_byte, int block, int char_pos, struct burn_pack_cursor *crs, int flag) { int idx; if (crs->num_packs >= Libburn_leadin_cdtext_packs_maX) { libdax_msgs_submit(libdax_messenger, -1, 0x0002018b, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Too many CD-TEXT packs", 0, 0); return 0; } if (crs->hiseq[block] >= 255) { libdax_msgs_submit(libdax_messenger, -1, 0x0002018e, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Too many CD-TEXT packs in block", 0, 0); return 0; } if (char_pos > 15) char_pos = 15; else if (char_pos < 0) char_pos = 0; idx = crs->num_packs * 18; crs->packs[idx++] = pack_type; crs->packs[idx++] = track_no; crs->packs[idx++] = crs->hiseq[block]; crs->packs[idx++] = ((flag & 1) << 7) | (block << 4) | char_pos; crs->hiseq[block]++; crs->td_used = 0; crs->pack_count[pack_type - Libburn_pack_type_basE]++; return 1; } /* Plain implementation of polynomial division on a Galois field, where addition and subtraction both are binary exor. Euclidean algorithm. Divisor is x^16 + x^12 + x^5 + 1 = 0x11021. */ static int crc_11021(unsigned char *data, int count, int flag) { int acc = 0, i; for (i = 0; i < count * 8 + 16; i++) { acc = (acc << 1); if (i < count * 8) acc |= ((data[i / 8] >> (7 - (i % 8))) & 1); if (acc & 0x10000) acc ^= 0x11021; } return acc; } /* @param flag bit0= repair mismatching checksums bit1= repair checksums if all pack CRCs are 0 @return 0= no mismatch , >0 number of unrepaired mismatches <0 number of repaired mismatches that were not 0 */ int burn_cdtext_crc_mismatches(unsigned char *packs, int num_packs, int flag) { int i, residue, count = 0, repair; unsigned char crc[2]; repair = flag & 1; if (flag & 2) { for (i = 0; i < num_packs * 18; i += 18) if (packs[i + 16] || packs[i + 17]) break; if (i == num_packs * 18) repair = 1; } for (i = 0; i < num_packs * 18; i += 18) { residue = crc_11021(packs + i, 16, 0); crc[0] = ((residue >> 8) & 0xff) ^ 0xff; crc[1] = ((residue ) & 0xff) ^ 0xff; if(crc[0] != packs[i + 16] || crc[1] != packs[i + 17]) { if (repair) { if (packs[i + 16] || packs[i + 17]) count--; packs[i + 16] = crc[0]; packs[i + 17] = crc[1]; } else count++; } } return count; } static int burn_finalize_text_pack(struct burn_pack_cursor *crs, int flag) { int residue = 0, i, idx; idx = 18 * crs->num_packs; for(i = 4 + crs->td_used; i < 16; i++) crs->packs[idx + i] = 0; crs->td_used = 12; /* MMC-3 Annex J : CRC Field consists of 2 bytes. The polynomial is X16 + X12 + X5 + 1. All bits shall be inverted. */ residue = crc_11021(crs->packs + idx, 16, 0) ^ 0xffff; crs->packs[idx + 16] = (residue >> 8) & 0xff; crs->packs[idx + 17] = residue & 0xff; crs->num_packs++; crs->td_used = 0; return 1; } /* @param flag bit0= double_byte characters */ static int burn_create_tybl_packs(unsigned char *payload, int length, int track_no, int pack_type, int block, struct burn_pack_cursor *crs, int flag) { int i, ret, binary_part = 0, char_pos; if (pack_type == 0x87) binary_part = 2; else if ((pack_type >= 0x88 && pack_type <= 0x8c) || pack_type == 0x8f) binary_part = length; for(i = 0; i < length; i++) { if (crs->td_used == 0 || crs->td_used >= 12) { if (crs->td_used > 0) { ret = burn_finalize_text_pack(crs, 0); if (ret <= 0) return ret; } char_pos = (i - binary_part) / (1 + (flag & 1)); ret = burn_create_new_pack(pack_type, track_no, (flag & 1), block, char_pos, crs, flag & 1); if (ret <= 0) return ret; } crs->packs[crs->num_packs * 18 + 4 + crs->td_used] = payload[i]; crs->td_used++; } return 1; } /* Finalize block by 0x8f. Set bytes 20 to 27 to 0 for now. */ static int burn_create_bl_size_packs(int block, unsigned char *char_codes, unsigned char *copyrights, unsigned char *languages, int num_tracks, struct burn_pack_cursor *crs, int flag) { int i, ret; unsigned char payload[12]; payload[0] = char_codes[block]; payload[1] = crs->track_offset; payload[2] = num_tracks + crs->track_offset - 1; payload[3] = copyrights[block]; for (i = 0; i < 8; i++) payload[i + 4] = crs->pack_count[i]; ret = burn_create_tybl_packs(payload, 12, 0, 0x8f, block, crs, 0); if (ret <= 0) return ret; for (i = 0; i < 7; i++) payload[i] = crs->pack_count[i + 8]; payload[7] = 3; /* always 3 packs of type 0x8f */ for (i = 0; i < 4; i++) { /* Will be set when all blocks are done */ payload[i + 8] = 0; } ret = burn_create_tybl_packs(payload, 12, 1, 0x8f, block, crs, 0); if (ret <= 0) return ret; for (i = 0; i < 4; i++) { /* Will be set when all blocks are done */ payload[i] = 0; } for (i = 0; i < 8; i++) { payload[i + 4] = languages[i]; } ret = burn_create_tybl_packs(payload, 12, 2, 0x8f, block, crs, 0); if (ret <= 0) return ret; ret = burn_finalize_text_pack(crs, 0); if (ret <= 0) return ret; for (i = 0; i < 16; i++) crs->pack_count[i] = 0; return 1; } /* Text packs of track for type and block @param flag bit0= write TAB, because content is identical to previous track */ static int burn_create_tybl_t_packs(struct burn_track *t, int track_no, int pack_type, int block, struct burn_pack_cursor *crs, int flag) { int ret, length = 0, idx, double_byte, flags= 0; unsigned char *payload = NULL, dummy[8]; struct burn_cdtext *cdt; cdt = t->cdtext[block]; idx = pack_type - Libburn_pack_type_basE; if (cdt != NULL) { if (cdt->length[idx] > 0) { payload = cdt->payload[idx]; length = cdt->length[idx]; } flags = cdt->flags; } if (payload == NULL) { dummy[0]= 0; payload = dummy; length = strlen((char *) dummy) + 1; } double_byte = !!(flags & (1 <<(pack_type - Libburn_pack_type_basE))); if (flag & 1) { length = 0; dummy[length++] = 9; if (double_byte) dummy[length++] = 9; dummy[length++] = 0; if (double_byte) dummy[length++] = 0; payload = dummy; } ret = burn_create_tybl_packs(payload, length, track_no, pack_type, block, crs, double_byte); return ret; } /* Check whether the content is the same as in the previous pack. If so, advise to use the TAB abbreviation. */ static int burn_decide_cdtext_tab(int block, int pack_type, struct burn_cdtext *cdt_curr, struct burn_cdtext *cdt_prev, int flag) { int length, j, idx; idx = pack_type - Libburn_pack_type_basE; if (cdt_curr == NULL || cdt_prev == NULL) return 0; if (((cdt_curr->flags >> idx) & 1) != ((cdt_prev->flags >> idx) & 1)) return 0; length = cdt_curr->length[idx]; if (length != cdt_prev->length[idx] || length <= 1 + ((cdt_curr->flags >> idx) & 1)) return 0; for (j = 0; j < length; j++) if (cdt_curr->payload[idx][j] != cdt_prev->payload[idx][j]) break; if (j < length) return 0; return 1; } /* Text packs of session and of tracks (if applicable), for type and block */ static int burn_create_tybl_s_packs(struct burn_session *s, int pack_type, int block, struct burn_pack_cursor *crs, int flag) { int i, ret, idx, double_byte, use_tab; struct burn_cdtext *cdt; cdt = s->cdtext[block]; idx = pack_type - Libburn_pack_type_basE; if (cdt->length[idx] == 0 || cdt->payload[idx] == NULL) return 1; double_byte = !!(cdt->flags & (1 <<(pack_type - Libburn_pack_type_basE))); ret = burn_create_tybl_packs(cdt->payload[idx], cdt->length[idx], 0, pack_type, block, crs, double_byte); if (ret <= 0) return ret; if ((pack_type < 0x80 || pack_type > 0x85) && pack_type != 0x8e) { ret = burn_finalize_text_pack(crs, 0); return ret; } for (i = 0; i < s->tracks; i++) { if (i > 0) use_tab = burn_decide_cdtext_tab(block, pack_type, s->track[i]->cdtext[block], s->track[i - 1]->cdtext[block], 0); else use_tab = 0; ret = burn_create_tybl_t_packs(s->track[i], i + crs->track_offset, pack_type, block, crs, use_tab); if (ret <= 0) return ret; } /* Fill up last pack with 0s */ ret = burn_finalize_text_pack(crs, 0); return ret; } /* ts B11210 : API */ /* @param flag bit0= do not return CD-TEXT packs, but return number of packs */ int burn_cdtext_from_session(struct burn_session *s, unsigned char **text_packs, int *num_packs, int flag) { int pack_type, block, ret, i, idx, j, residue; struct burn_pack_cursor crs; if (text_packs == NULL || num_packs == NULL) { flag |= 1; } else if (!(flag & 1)) { *text_packs = NULL; *num_packs = 0; } memset(&crs, 0, sizeof(struct burn_pack_cursor)); crs.track_offset = s->firsttrack; BURN_ALLOC_MEM(crs.packs, unsigned char, Libburn_leadin_cdtext_packs_maX * 18); for (block = 0; block < 8; block++) if (s->cdtext[block] != NULL) break; if (block == 8) {ret = 1; goto ex;} for (block= 0; block < 8; block++) { if (s->cdtext[block] == NULL) continue; for (pack_type = 0x80; pack_type < 0x80 + Libburn_pack_num_typeS; pack_type++) { if (pack_type == 0x8f) continue; ret = burn_create_tybl_s_packs(s, pack_type, block, &crs, 0); if (ret <= 0) goto ex; } ret = burn_create_bl_size_packs(block, s->cdtext_char_code, s->cdtext_copyright, s->cdtext_language, s->tracks, &crs, 0); if (ret <= 0) goto ex; } /* Insert the highest sequence numbers of each block into the 0x8f packs 2 and 3 (bytes 20 to 27) */ for (i = 0; i < crs.num_packs; i++) { idx = i * 18; if (crs.packs[idx] == 0x8f && crs.packs[idx + 1] == 1) { for (j = 0; j < 4; j++) if (crs.hiseq[j] > 0) crs.packs[idx + 4 + 8 + j] = crs.hiseq[j] - 1; else crs.packs[idx + 4 + 8 + j] = 0; } else if (crs.packs[idx] == 0x8f && crs.packs[idx + 1] == 2) { for (j = 0; j < 4; j++) if (crs.hiseq[j + 4] > 0) crs.packs[idx + 4 + j] = crs.hiseq[j + 4] - 1; else crs.packs[idx + 4 + j] = 0; } else continue; /* Re-compute checksum */ residue = crc_11021(crs.packs + idx, 16, 0) ^ 0xffff; crs.packs[idx + 16] = (residue >> 8) & 0xff; crs.packs[idx + 17] = residue & 0xff; } ret = 1; ex:; if (ret <= 0 || (flag & 1)) { if (ret > 0) ret = crs.num_packs; else if (flag & 1) ret = -1; BURN_FREE_MEM(crs.packs); } else { if (crs.num_packs > 0) *text_packs = crs.packs; else BURN_FREE_MEM(crs.packs); *num_packs = crs.num_packs; } return(ret); } /* ---------------- Reader of Sony Input Sheet Version 0.7T ------------- */ /* @param flag bit0= allow two byte codes 0xNNNN or 0xNN 0xNN */ static int v07t_hexcode(char *payload, int flag) { unsigned int x; int lo, hi, l; char buf[10], *cpt; l = strlen(payload); if (strncmp(payload, "0x", 2) != 0) return -1; if ((l == 6 || l == 9) && (flag & 1)) goto double_byte; if (strlen(payload) != 4) return -1; if (!(isxdigit(payload[2]) && isxdigit(payload[3]))) return -1; sscanf(payload + 2, "%x", &x); return x; double_byte:; strcpy(buf, payload); buf[4] = 0; hi = v07t_hexcode(buf, 0); if (strlen(payload) == 6) { buf[4] = payload[4]; buf[2] = '0'; buf[3] = 'x'; cpt = buf + 2; } else { if(payload[4] != 32 && payload[4] != 9) return(-1); cpt = buf + 5; } lo = v07t_hexcode(cpt, 0); if (lo < 0 || hi < 0) return -1; return ((hi << 8) | lo); } static int v07t_cdtext_char_code(char *payload, int flag) { int ret; char *msg = NULL; ret = v07t_hexcode(payload, 0); if (ret >= 0) return ret; if (strstr(payload, "8859") != NULL) return 0x00; else if(strstr(payload, "ASCII") != NULL) return 0x01; else if(strstr(payload, "JIS") != NULL) return 0x80; BURN_ALLOC_MEM(msg, char, 160); sprintf(msg, "Unknown v07t Text Code '%.80s'", payload); libdax_msgs_submit(libdax_messenger, -1, 0x00020191, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = -1; ex:; BURN_FREE_MEM(msg); return ret; } static int v07t_cdtext_lang_code(char *payload, int flag) { int i, ret; static char *languages[128] = { BURN_CDTEXT_LANGUAGES_0X00, BURN_CDTEXT_FILLER, BURN_CDTEXT_LANGUAGES_0X45 }; char *msg = NULL; ret = v07t_hexcode(payload, 0); if (ret >= 0) return ret; if (payload[0] != 0) for(i = 0; i < 128; i++) if(strcmp(languages[i], payload) == 0) return i; BURN_ALLOC_MEM(msg, char, 160); sprintf(msg, "Unknown v07t Language Code '%.80s'", payload); libdax_msgs_submit(libdax_messenger, -1, 0x00020191, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = -1; ex:; BURN_FREE_MEM(msg); return ret; } static int v07t_cdtext_genre_code(char *payload, int flag) { int i, ret; static char *genres[BURN_CDTEXT_NUM_GENRES] = { BURN_CDTEXT_GENRE_LIST }; char *msg = NULL; ret = v07t_hexcode(payload, 1); if(ret >= 0) return ret; for (i= 0; i < BURN_CDTEXT_NUM_GENRES; i++) if (strcmp(genres[i], payload) == 0) return i; BURN_ALLOC_MEM(msg, char, 160); sprintf(msg, "Unknown v07t Genre Code '%.80s'", payload); libdax_msgs_submit(libdax_messenger, -1, 0x00020191, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = -1; ex:; BURN_FREE_MEM(msg); return ret; } static int v07t_cdtext_len_db(char *payload, int *char_code, int *length, int *double_byte, int flag) { if (*char_code < 0) *char_code = 0x00; *double_byte = (*char_code == 0x80); *length = strlen(payload) + 1 + *double_byte; return 1; } static int v07t_cdtext_to_session(struct burn_session *session, int block, char *payload, int *char_code, int pack_type, char *pack_type_name, int flag) { int length, double_byte, ret; ret = v07t_cdtext_len_db(payload, char_code, &length, &double_byte, 0); if (ret <= 0) return ret; ret = burn_session_set_cdtext(session, block, pack_type, pack_type_name, (unsigned char *) payload, length, double_byte); return ret; } static int v07t_cdtext_to_track(struct burn_track *track, int block, char *payload, int *char_code, int pack_type, char *pack_type_name, int flag) { int length, double_byte, ret; ret = v07t_cdtext_len_db(payload, char_code, &length, &double_byte, 0); if (ret <= 0) return ret; ret = burn_track_set_cdtext(track, block, pack_type, pack_type_name, (unsigned char *) payload, length, double_byte); return ret; } static int v07t_apply_to_session(struct burn_session *session, int block, int char_codes[8], int copyrights[8], int languages[8], int session_attr_seen[16], int track_attr_seen[16], int genre_code, char *genre_text, int flag) { int i, ret, length; char *line = NULL; BURN_ALLOC_MEM(line, char, 4096); for (i= 0x80; i <= 0x8e; i++) { if (i > 0x85 && i != 0x8e) continue; if (session_attr_seen[i - 0x80] || !track_attr_seen[i - 0x80]) continue; ret = v07t_cdtext_to_session(session, block, "", char_codes + block, i, NULL, 0); if (ret <= 0) goto ex; } if (genre_code >= 0 && genre_text[0]) { line[0] = (genre_code >> 8) & 0xff; line[1] = genre_code & 0xff; strcpy(line + 2, genre_text); length = 2 + strlen(line + 2) + 1; ret = burn_session_set_cdtext(session, block, 0, "GENRE", (unsigned char *) line, length, 0); if (ret <= 0) goto ex; } ret = burn_session_set_cdtext_par(session, char_codes, copyrights, languages, 0); if (ret <= 0) goto ex; for (i = 0; i < 8; i++) char_codes[i] = copyrights[i] = languages[i]= -1; for (i = 0; i < 16; i++) session_attr_seen[i] = track_attr_seen[i] = 0; genre_text[0] = 0; ret = 1; ex: BURN_FREE_MEM(line); return ret; } /* ts B11215 API */ /* @param flag bit0= permission to read multiple blocks from the same sheet bit1= do not attach CATALOG to session or ISRC to track for writing to Q sub-channel */ int burn_session_input_sheet_v07t(struct burn_session *session, char *path, int block, int flag) { int ret = 0, num_tracks, char_codes[8], copyrights[8], languages[8], i; int genre_code = -1, track_offset = 1, pack_type, tno, tnum; int session_attr_seen[16], track_attr_seen[16]; int int0x00 = 0x00, int0x01 = 0x01; int additional_blocks = -1, line_count = 0, enable_multi_block = 0; struct stat stbuf; FILE *fp = NULL; char *line = NULL, *eq_pos, *payload, *genre_text = NULL, track_txt[3]; char *msg = NULL; struct burn_track **tracks; BURN_ALLOC_MEM(msg, char, 4096); BURN_ALLOC_MEM(line, char, 4096); BURN_ALLOC_MEM(genre_text, char, 160); for (i = 0; i < 8; i++) char_codes[i] = copyrights[i] = languages[i]= -1; for (i = 0; i < 16; i++) session_attr_seen[i] = track_attr_seen[i] = 0; genre_text[0] = 0; tracks = burn_session_get_tracks(session, &num_tracks); if (stat(path, &stbuf) == -1) { cannot_open:; sprintf(msg, "Cannot open CD-TEXT input sheet v07t '%.4000s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x00020193, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), errno, 0); ret = 0; goto ex; } if (!S_ISREG(stbuf.st_mode)) { sprintf(msg, "File is not of usable type: CD-TEXT input sheet v07t '%s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x00020193, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } fp = fopen(path, "rb"); if (fp == NULL) goto cannot_open; while (1) { if (burn_sfile_fgets(line, 4095, fp) == NULL) { if (!ferror(fp)) break; sprintf(msg, "Cannot read all bytes from CD-TEXT input sheet v07t '%.4000s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x00020193, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } line_count++; if (strlen(line) == 0) continue; eq_pos = strchr(line, '='); if (eq_pos == NULL) { sprintf(msg, "CD-TEXT v07t input sheet line without '=' : '%.4000s'", line); libdax_msgs_submit(libdax_messenger, -1, 0x00020194, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } for (payload = eq_pos + 1; *payload == 32 || *payload == 9; payload++); *eq_pos = 0; for (eq_pos--; (*eq_pos == 32 || *eq_pos == 9) && eq_pos > line; eq_pos--) *eq_pos= 0; if (payload[0] == 0) continue; if (strcmp(line, "Text Code") == 0) { ret = v07t_cdtext_char_code(payload, 0); if (ret < 0) goto ex; if (char_codes[block] >= 0 && char_codes[block] != ret) { libdax_msgs_submit(libdax_messenger, -1, 0x00020192, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Unexpected v07t Text Code change", 0, 0); ret = 0; goto ex; } char_codes[block] = ret; } else if (strcmp(line, "Language Code") == 0) { ret = v07t_cdtext_lang_code(payload, 0); if(ret < 0) goto ex; languages[block] = ret; } else if (strcmp(line, "0x80") == 0 || strcmp(line, "Album Title") == 0) { ret = v07t_cdtext_to_session(session, block, payload, char_codes + block, 0, "TITLE", 0); if (ret <= 0) goto ex; session_attr_seen[0x0] = 1; } else if (strcmp(line, "0x81") == 0 || strcmp(line, "Artist Name") == 0) { ret = v07t_cdtext_to_session(session, block, payload, char_codes + block, 0, "PERFORMER", 0); if (ret <= 0) goto ex; session_attr_seen[0x1] = 1; } else if (strcmp(line, "0x82") == 0 || strcmp(line, "Songwriter") == 0) { ret = v07t_cdtext_to_session(session, block, payload, char_codes + block, 0, "SONGWRITER", 0); if (ret <= 0) goto ex; session_attr_seen[0x2] = 1; } else if (strcmp(line, "0x83") == 0 || strcmp(line, "Composer") == 0) { ret = v07t_cdtext_to_session(session, block, payload, char_codes + block, 0, "COMPOSER", 0); if (ret <= 0) goto ex; session_attr_seen[0x3] = 1; } else if (strcmp(line, "0x84") == 0 || strcmp(line, "Arranger") == 0) { ret = v07t_cdtext_to_session(session, block, payload, char_codes + block, 0, "ARRANGER", 0); if (ret <= 0) goto ex; session_attr_seen[0x4] = 1; } else if (strcmp(line, "0x85") == 0 || strcmp(line, "Album Message") == 0) { ret = v07t_cdtext_to_session(session, block, payload, char_codes + block, 0, "MESSAGE", 0); if (ret <= 0) goto ex; session_attr_seen[0x5] = 1; } else if (strcmp(line, "0x86") == 0 || strcmp(line, "Catalog Number") == 0) { ret = v07t_cdtext_to_session(session, block, payload, &int0x01, 0, "DISCID", 0); if(ret <= 0) goto ex; } else if (strcmp(line, "Genre Code") == 0) { genre_code = v07t_cdtext_genre_code(payload, 0); if (genre_code < 0) { ret = 0; goto ex; } } else if (strcmp(line, "Genre Information") == 0) { strncpy(genre_text, payload, 159); genre_text[159] = 0; } else if (strcmp(line, "0x8d") == 0 || strcmp(line, "Closed Information") == 0) { ret = v07t_cdtext_to_session(session, block, payload, &int0x00, 0, "CLOSED", 0); if (ret <= 0) goto ex; } else if(strcmp(line, "0x8e") == 0 || strcmp(line, "UPC / EAN") == 0) { ret = v07t_cdtext_to_session(session, block, payload, &int0x01, 0, "UPC_ISRC", 0); if (ret <= 0) goto ex; if (!(flag & 2)) { memcpy(session->mediacatalog, payload, 13); session->mediacatalog[13] = 0; } session_attr_seen[0xe] = 1; } else if (strncmp(line, "Disc Information ", 17) == 0) { /* >>> ??? is this good for anything ? */; } else if (strcmp(line, "Input Sheet Version") == 0) { if (strcmp(payload, "0.7T") != 0) { sprintf(msg, "Wrong Input Sheet Version '%.4000s'. Expected '0.7T'.", payload); libdax_msgs_submit(libdax_messenger, -1, 0x00020194, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } if (flag & 1) if (line_count == 1) enable_multi_block = 1; if (enable_multi_block) { if (additional_blocks >= 0) { if (block == 7) { libdax_msgs_submit( libdax_messenger, -1, 0x000201a0, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, "Maximum number of CD-TEXT blocks exceeded", 0, 0); break; } ret = v07t_apply_to_session( session, block, char_codes, copyrights, languages, session_attr_seen, track_attr_seen, genre_code, genre_text, 0); if (ret <= 0) goto ex; block++; } additional_blocks++; } } else if (strcmp(line, "Remarks") == 0) { ; } else if (strcmp(line, "Text Data Copy Protection") == 0) { ret = v07t_hexcode(payload, 0); if (ret >= 0) copyrights[block] = ret; else if (strcmp(payload, "ON") == 0) copyrights[block] = 0x03; else if (strcmp(payload, "OFF") == 0) copyrights[block] = 0x00; else { sprintf(msg, "Unknown v07t Text Data Copy Protection '%.4000s'", payload); libdax_msgs_submit(libdax_messenger, -1, 0x00020191, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } } else if (strcmp(line, "First Track Number") == 0) { ret = -1; sscanf(payload, "%d", &ret); if (ret <= 0 || ret > 99) { bad_tno:; sprintf(msg, "Inappropriate v07t First Track Number '%.4000s'", payload); libdax_msgs_submit(libdax_messenger, -1, 0x00020194, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } else { track_offset = ret; ret = burn_session_set_start_tno(session, track_offset, 0); if (ret <= 0) goto ex; } } else if (strcmp(line, "Last Track Number") == 0) { ret = -1; sscanf(payload, "%d", &ret); if (ret < 0) { goto bad_tno; } else { /* >>> ??? Is it good for anything ? */; } } else if (strncmp(line, "Track ", 6) == 0) { tno = -1; sscanf(line + 6, "%d", &tno); if (tno < 1 || tno - track_offset < 0 || tno - track_offset >= num_tracks) { track_txt[0] = line[6]; track_txt[1] = line[7]; track_txt[2] = 0; bad_track_no:; sprintf(msg, "Inappropriate v07t Track number '%.3900s'", track_txt); sprintf(msg + strlen(msg), " (acceptable range: %2.2d to %2.2d)", track_offset, num_tracks + track_offset - 1); libdax_msgs_submit(libdax_messenger, -1, 0x00020194, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } tnum = tno - track_offset; if (strcmp(line, "0x80") == 0 || strcmp(line + 9, "Title") == 0) pack_type = 0x80; else if (strcmp(line + 9, "0x81") == 0 || strcmp(line + 9, "Artist") == 0) pack_type = 0x81; else if (strcmp(line + 9, "0x82") == 0 || strcmp(line + 9, "Songwriter") == 0) pack_type = 0x82; else if (strcmp(line + 9, "0x83") == 0 || strcmp(line + 9, "Composer") == 0) pack_type = 0x83; else if (strcmp(line + 9, "0x84") == 0 || strcmp(line + 9, "Arranger") == 0) pack_type = 0x84; else if (strcmp(line + 9, "0x85") == 0 || strcmp(line + 9, "Message") == 0) pack_type = 0x85; else if (strcmp(line + 9, "0x8e") == 0 || strcmp(line + 9, "ISRC") == 0) { pack_type = 0x8e; if (!(flag & 2)) { ret = burn_track_set_isrc_string( tracks[tnum], payload, 0); if (ret <= 0) goto ex; } } else { sprintf(msg, "Unknown v07t Track purpose specifier '%s'", line + 9); libdax_msgs_submit(libdax_messenger, -1, 0x00020191, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } ret = v07t_cdtext_to_track(tracks[tnum], block, payload, &int0x00, pack_type, "", 0); if (ret <= 0) goto ex; track_attr_seen[pack_type - 0x80] = 1; } else if (strncmp(line, "ISRC ", 5) == 0) { /* Track variation of UPC EAN = 0x8e */ tno = -1; sscanf(line + 5, "%d", &tno); if (tno <= 0 || tno - track_offset < 0 || tno - track_offset >= num_tracks) { track_txt[0] = line[5]; track_txt[1] = line[6]; track_txt[2] = 0; goto bad_track_no; } tnum = tno - track_offset; if (!(flag & 2)) { ret = burn_track_set_isrc_string( tracks[tnum], payload, 0); if (ret <= 0) goto ex; } ret = v07t_cdtext_to_track(tracks[tnum], block, payload, &int0x00, 0x8e, "", 0); if (ret <= 0) goto ex; track_attr_seen[0xe] = 1; } else { sprintf(msg, "Unknown v07t purpose specifier '%.4000s'", line); libdax_msgs_submit(libdax_messenger, -1, 0x00020191, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } } ret = v07t_apply_to_session(session, block, char_codes, copyrights, languages, session_attr_seen, track_attr_seen, genre_code, genre_text, 0); if (ret <= 0) goto ex; ret = 1; if (additional_blocks > 0) ret += additional_blocks;; ex:; if(fp != NULL) fclose(fp); BURN_FREE_MEM(genre_text); BURN_FREE_MEM(line); BURN_FREE_MEM(msg); return ret; } /* ts B11221 API */ int burn_cdtext_from_packfile(char *path, unsigned char **text_packs, int *num_packs, int flag) { int ret = 0, residue = 0; struct stat stbuf; FILE *fp = NULL; unsigned char head[4], tail[1]; char *msg = NULL; BURN_ALLOC_MEM(msg, char, 4096); *text_packs = NULL; if (stat(path, &stbuf) == -1) { cannot_open:; sprintf(msg, "Cannot open CD-TEXT pack file '%.4000s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x00020198, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), errno, 0); ret = 0; goto ex; } if (!S_ISREG(stbuf.st_mode)) goto not_a_textfile; residue = (stbuf.st_size % 18); if(residue != 4 && residue != 0 && residue != 1) { not_a_textfile:; sprintf(msg, "File is not of usable type or content for CD-TEXT packs: '%.4000s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x00020198, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } if (stbuf.st_size < 18) goto not_a_textfile; fp = fopen(path, "rb"); if (fp == NULL) goto cannot_open; if (residue == 4) { /* This is for files from cdrecord -vv -toc */ ret = fread(head, 4, 1, fp); if (ret != 1) { cannot_read:; sprintf(msg, "Cannot read all bytes from CD-TEXT pack file '%.4000s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x00020198, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), errno, 0); ret = 0; goto ex; } if (head[0] * 256 + head[1] != stbuf.st_size - 2) goto not_a_textfile; } *num_packs = (stbuf.st_size - residue) / 18; if (*num_packs > 2048) { /* Each block can have 256 text packs. There are 8 blocks at most. */ sprintf(msg, "CD-Text pack file too large (max. 36864 bytes): '%.4000s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x0002018b, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } if (*num_packs <= 0) { strcpy(msg, "CD-Text pack file contains no complete text pack"); libdax_msgs_submit(libdax_messenger, -1, 0x000201aa, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } BURN_ALLOC_MEM(*text_packs, unsigned char, *num_packs * 18); ret = fread(*text_packs, *num_packs * 18, 1, fp); if (ret != 1) goto cannot_read; if (residue == 1) { /* This is for Sony CDTEXT files */ ret = fread(tail, 1, 1, fp); if (ret != 1) goto cannot_read; if (tail[0] != 0) goto not_a_textfile; } ret= 1; ex:; if (ret <= 0) { BURN_FREE_MEM(*text_packs); *text_packs = NULL; *num_packs = 0; } if (fp != NULL) fclose(fp); BURN_FREE_MEM(msg); return ret; } /* --------------------------------- make_v07t -------------------------- */ static int search_pack(unsigned char *text_packs, int num_packs, int start_no, int pack_type, int block, unsigned char **found_pack, int *found_no, int flag) { int i; for (i = start_no; i < num_packs; i++) { if (pack_type >= 0) if (text_packs[i * 18] != pack_type) continue; if (block >= 0) if (((text_packs[i * 18 + 3] >> 4) & 7) != block) continue; *found_pack = text_packs + i * 18; *found_no = i; return 1; } *found_pack = NULL; *found_no = num_packs; return 0; } static void write_v07t_line(char **respt, char *spec, char *value, int vlen, int *result_len, int flag) { int len; if (vlen == -1) vlen = strlen(value); len = strlen(spec); if (len < 19) len = 19; len += 3 + vlen + 1; if(flag & 1) { *result_len += len; return; } sprintf(*respt, "%-19s = ", spec); if (vlen > 0) memcpy(*respt + strlen(*respt), value, vlen); (*respt)[len - 1] = '\n'; (*respt)[len] = 0; *respt+= len; } /* @return -1 error 0 no pack of block,pack_type found 1 packs found, delimiter is single 0-byte 2 packs found, delimiter is double 0-byte */ static int collect_payload(unsigned char *text_packs, int num_packs, int pack_type, int block, unsigned char **payload, int *payload_count, int flag) { unsigned char *pack; int pack_no, ret, double_byte = 0; *payload_count = 0; for (pack_no = 0; ; pack_no++) { ret = search_pack(text_packs, num_packs, pack_no, pack_type, block, &pack, &pack_no, 0); if (ret <= 0) break; *payload_count += 12; } if (*payload_count == 0) return 0; *payload = burn_alloc_mem(*payload_count + 1, 1, 0); if (*payload == NULL) return -1; *payload_count = 0; for (pack_no = 0; ; pack_no++) { ret = search_pack(text_packs, num_packs, pack_no, pack_type, block, &pack, &pack_no, 0); if (ret <= 0) break; memcpy(*payload + *payload_count, pack + 4, 12); *payload_count += 12; if (pack[3] & 128) double_byte = 1; } (*payload)[*payload_count] = 0; return 1 + double_byte; } /* @param flag bit0= use double 0 as delimiter */ static int is_payload_text_end(unsigned char *payload, int payload_count, int i, int flag) { if (i >= payload_count) return 1; if (payload[i]) return 0; if (!(flag & 1)) return 1; if (i + 1 >= payload_count) return 1; if (payload[i + 1] == 0) return 1; return 0; } /* @param flag Bitfield for control purposes. bit0= use double 0 as delimiter bit1= replace TAB resp. TAB TAB by text of previous tno */ static int pick_payload_text(unsigned char *payload, int payload_count, int tno, unsigned char **text_start, int *text_len, int flag) { int i, skipped = 0, end_found = 0; again:; if (tno <= 0) { *text_start = payload; *text_len = 0; for (i = 0; i < payload_count; i += 1 + (flag & 1)) { end_found = is_payload_text_end(payload, payload_count, i, flag & 1); if (end_found) { *text_len = i; break; } } return 1; } *text_start = NULL; *text_len = 0; for (i = 0; i < payload_count; i += 1 + (flag & 1)) { end_found = is_payload_text_end(payload, payload_count, i, flag & 1); if (end_found) { skipped++; if (skipped == tno) { *text_start = payload + (i + 1 + (flag & 1)); } else if (skipped == tno + 1) { *text_len = i - (*text_start - payload); goto found; } } } if (*text_start == NULL) return 0; *text_len = payload_count - (*text_start - payload); found:; if (flag & 2) { /* If TAB resp. TAB TAB, then look back */ if (flag & 1) { if (*text_len == 2) { if ((*text_start)[0] == '\t' && (*text_start)[1] == '\t') { skipped = 0; tno--; goto again; } } } else if (*text_len == 1) { if ((*text_start)[0] == '\t') { skipped = 0; tno--; goto again; } } } return 1; } static int write_v07t_textline(unsigned char *text_packs, int num_packs, int pack_type, int block, int tno, int first_tno, char *spec, char **respt, int *result_len, int flag) { unsigned char *payload = NULL, *text_start; int ret, payload_count = 0, text_len, tab_flag = 0; char msg[80]; if ((pack_type >= 0x80 && pack_type <= 0x85) || pack_type == 0x8e) tab_flag = 2; ret = collect_payload(text_packs, num_packs, pack_type, block, &payload, &payload_count, 0); if(ret > 0) { ret = pick_payload_text(payload, payload_count, tno, &text_start, &text_len, (ret == 2) | tab_flag); if (ret > 0) { if (tno > 0 && strcmp(spec, "ISRC") == 0) sprintf(msg, "%s %-2.2d", spec, tno + first_tno - 1); else if (tno > 0) sprintf(msg, "Track %-2.2d %s", tno + first_tno - 1, spec); else strcpy(msg, spec); write_v07t_line(respt, msg, (char *) text_start, text_len, result_len, flag & 1); ret = 1; } } BURN_FREE_MEM(payload); return ret; } static int report_track(unsigned char *text_packs, int num_packs, int block, int tno, int first_tno, char **respt, int *result_len, int flag) { int ret, i; static char *track_specs[6] = { "Title", "Artist", "Songwriter", "Composer", "Arranger", "Message" }; for (i = 0; i < 6; i++) { ret = write_v07t_textline(text_packs, num_packs, 0x80 + i, block, tno, first_tno, track_specs[i], respt, result_len, flag & 1); if (ret < 0) return -1; } ret = write_v07t_textline(text_packs, num_packs, 0x8e, block, tno, first_tno, "ISRC", respt, result_len, flag & 1); if (ret < 0) return -1; return 1; } /* @param flag Bitfield for control purposes. bit0= Do not store text in result but only determine the minimum size for the result array. It is permissible to submit result == NULL. Submit the already occupied size as result_size. @return > 0 tells the number of valid text bytes in result resp. with flag bit0 the prediction of that number. This does not include the trailing 0-byte. = 0 indicates that the block is not present < 0 indicates failure. */ static int report_block(unsigned char *text_packs, int num_packs, int block, int first_tno, int last_tno, int char_code, char *result, int result_size, int flag) { char *respt = NULL; unsigned char *pack, *payload = NULL; int result_len = 0, pack_no, ret, i, lang, payload_count = 0, genre; char msg[80]; static char *languages[] = { BURN_CDTEXT_LANGUAGES_0X00, BURN_CDTEXT_FILLER, BURN_CDTEXT_LANGUAGES_0X45 }; static char *volume_specs[7] = { "Album Title", "Artist Name", "Songwriter", "Composer", "Arranger", "Album Message", "Catalog Number", }; static char *genres[BURN_CDTEXT_NUM_GENRES] = { BURN_CDTEXT_GENRE_LIST }; /* Search for any pack of the block. But do not accept 0x8f as first.*/ ret = search_pack(text_packs, num_packs, 0, -1, block, &pack, &pack_no, 0); if (ret <= 0) return 0; if (pack[0] == 0x8f) return 0; if (flag & 1) { result_len = result_size; } else { respt = result + result_size; } write_v07t_line(&respt, "Input Sheet Version", "0.7T", -1, &result_len, flag & 1); sprintf(msg, "Libburn report of CD-TEXT Block %d", block); write_v07t_line(&respt, "Remarks ", msg, -1, &result_len, flag & 1); write_v07t_line(&respt, "Text Code ", char_code == 0 ? "8859" : char_code == 0x01 ? "ASCII" : "MS-JIS", -1, &result_len, flag & 1); pack_no = 0; for (i = 0; i < 3; i++) { ret = search_pack(text_packs, num_packs, pack_no, 0x8f, -1, &pack, &pack_no, 0); if (ret <= 0) { libdax_msgs_submit(libdax_messenger, -1, 0x0002019f, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "No third CD-TEXT pack 0x8f found. No language code defined", 0, 0); goto failure; } pack_no++; } lang = pack[8 + block]; if (lang > 127) { sprintf(msg, "CD-TEXT with unknown language code %2.2x", (unsigned int) lang); libdax_msgs_submit(libdax_messenger, -1, 0x0002019f, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); goto failure; } write_v07t_line(&respt, "Language Code", languages[lang], -1, &result_len, flag & 1); for (i = 0; i < 7; i++) { ret = write_v07t_textline(text_packs, num_packs, 0x80 + i, block, 0, 0, volume_specs[i], &respt, &result_len, flag & 1); if (ret < 0) goto failure; } ret = collect_payload(text_packs, num_packs, 0x87, block, &payload, &payload_count, 0); if(ret > 0) { genre = (payload[0] << 8) | payload[1]; if (genre < BURN_CDTEXT_NUM_GENRES) strcpy(msg, genres[genre]); else sprintf(msg, "0x%-4.4x", (unsigned int) genre); write_v07t_line(&respt, "Genre Code", msg, -1, &result_len, flag & 1); write_v07t_line(&respt, "Genre Information", (char *) payload + 2, -1, &result_len, flag & 1); BURN_FREE_MEM(payload); payload = NULL; } ret = collect_payload(text_packs, num_packs, 0x8d, block, &payload, &payload_count, 0); if(ret > 0) { write_v07t_line(&respt, "Closed Information", (char *) payload, -1, &result_len, flag & 1); BURN_FREE_MEM(payload); payload = NULL; } ret = write_v07t_textline(text_packs, num_packs, 0x8e, block, 0, 0, "UPC / EAN", &respt, &result_len, flag & 1); if (ret < 0) goto failure; ret = search_pack(text_packs, num_packs, 0, 0x8f, -1, &pack, &pack_no, 0); if (ret < 0) goto failure; if (pack[7] == 0x00) strcpy(msg, "OFF"); else if (pack[7] == 0x03) strcpy(msg, "ON"); else sprintf(msg, "0x%2.2x", (unsigned int) pack[7]); write_v07t_line(&respt, "Text Data Copy Protection", msg, -1, &result_len, flag & 1); sprintf(msg, "%d", first_tno); write_v07t_line(&respt, "First Track Number", msg, -1, &result_len, flag & 1); sprintf(msg, "%d", last_tno); write_v07t_line(&respt, "Last Track Number", msg, -1, &result_len, flag & 1); for (i = 0; i < last_tno - first_tno + 1; i++) { ret = report_track(text_packs, num_packs, block, i + 1, first_tno, &respt, &result_len, flag & 1); if (ret < 0) goto failure; } if (flag & 1) return result_len; return respt - result; failure:; BURN_FREE_MEM(payload); return -1; } /* @param result A byte buffer of sufficient size. It will be filled by the text for the v07t sheet file plus a trailing 0-byte. (Be aware that double-byte characters might contain 0-bytes, too.) @param result_size The number of bytes in result. To be determined by a run with flag bit0 set. @param flag Bitfield for control purposes. bit0= Do not store text in result but only determine the minimum size for the result array. It is permissible to submit result == NULL and result_size == 0. @return > 0 tells the number of valid text bytes in result resp. with flag bit0 the prediction of that number. This does not include the trailing 0-byte. <= 0 indicates failure. */ static int burn_make_v07t(unsigned char *text_packs, int num_packs, int first_tno, int track_count, char *result, int result_size, int *char_code, int flag) { int pack_no = 0, ret, block, last_tno = 0; unsigned char *pack; char msg[80]; /* >>> ??? Verify checksums ? */; /* Check character code, reject unknown ones */ ret = search_pack(text_packs, num_packs, 0, 0x8f, -1, &pack, &pack_no, 0); if (ret <= 0) { libdax_msgs_submit(libdax_messenger, -1, 0x0002019f, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "No CD-TEXT pack 0x8f found. No character code defined", 0, 0); return 0; } *char_code = pack[4]; if (*char_code != 0x00 && *char_code != 0x01 && *char_code != 0x80) { sprintf(msg, "CD-TEXT with unknown character code %2.2x", (unsigned int) *char_code); libdax_msgs_submit(libdax_messenger, -1, 0x0002019f, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); return 0; } /* Obtain first_tno and last_tno from type 0x8f if present. */ if (first_tno <= 0) { if (pack[5] > 0 && pack[5] + pack[6] < 100 && pack[5] <= pack[6]) { first_tno = pack[5]; last_tno = pack[6]; } else { sprintf(msg, "CD-TEXT with illegal track range %d to %d", (int) pack[5], (int) pack[6]); libdax_msgs_submit(libdax_messenger, -1, 0x0002019f, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); return 0; } } if (last_tno <= 0) { if (track_count > 0) { last_tno = first_tno + track_count - 1; } else { last_tno = 99; } } /* Report content */ result_size = 0; for (block = 0; block < 8; block++) { /* Obtain character code, reject unknown ones */ ret = search_pack(text_packs, num_packs, 0, 0x8f, block, &pack, &pack_no, 0); if (ret > 0) *char_code = pack[4]; if (*char_code != 0x00 && *char_code != 0x01 && *char_code != 0x80) { sprintf(msg, "CD-TEXT block %d with unknown character code %2.2x", block, (unsigned int) *char_code); libdax_msgs_submit(libdax_messenger, -1, 0x0002019f, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); return 0; } ret = report_block(text_packs, num_packs, block, first_tno, last_tno, *char_code, result, result_size, flag & 1); if (ret < 0) return ret; if (ret == 0) continue; result_size = ret; } return result_size; } /* Convert an array of CD-TEXT packs into the text format of Sony CD-TEXT Input Sheet Version 0.7T . @param text_packs Array of bytes which form CD-TEXT packs of 18 bytes each. For a description of the format of the array, see file doc/cdtext.txt. No header of 4 bytes must be prepended which would tell the number of pack bytes + 2. This parameter may be NULL if the currently attached array of packs shall be removed. @param num_packs The number of 18 byte packs in text_packs. @param start_tno The start number of track counting, if known from CD table-of-content or orther sources. Submit 0 to enable the attempt to read it and the track_count from pack type 0x8f. @param track_count The number of tracks, if known from CD table-of-content or orther sources. @param result Will return the buffer with Sheet text. Dispose by free() when no longer needed. It will be filled by the text for the v07t sheet file plus a trailing 0-byte. (Be aware that double-byte characters might contain 0-bytes, too.) Each CD-TEXT language block starts by the line "Input Sheet Version = 0.7T" and a "Remarks" line that tells the block number. @param char_code Returns the character code of the pack array: 0x00 = ISO-8859-1 0x01 = 7 bit ASCII 0x80 = MS-JIS (japanese Kanji, double byte characters) The presence of a code value that is not in this list will cause this function to fail. @param flag Bitfield for control purposes. Unused yet. Submit 0. @return > 0 tells the number of valid text bytes in result. This does not include the trailing 0-byte. <= 0 indicates failure. */ int burn_make_input_sheet_v07t(unsigned char *text_packs, int num_packs, int start_tno, int track_count, char **result, int *char_code, int flag) { int ret, result_size = 0; ret = burn_make_v07t(text_packs, num_packs, start_tno, track_count, NULL, 0, char_code, 1); if (ret <= 0) return ret; result_size = ret + 1; *result = burn_alloc_mem(result_size, 1, 0); if (*result == NULL) return -1; ret = burn_make_v07t(text_packs, num_packs, start_tno, track_count, *result, result_size, char_code, 0); if (ret <= 0) { free(*result); return ret; } return result_size - 1; } /* cleanup.c , Copyright 2006 - 2011 Thomas Schmitt <scdbackup@gmx.net> A signal handler which cleans up an application and exits. Provided under GPLv2+ license within GPL projects, BSD license elsewise. */ /* cc -g -o cleanup -DCleanup_standalonE cleanup.c */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <sys/types.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <signal.h> typedef void (*sighandler_t)(int); #include "cleanup.h" #ifndef Cleanup_has_no_libburn_os_H #include "../libburn/os.h" /* see os.h for name of particular os-*.h where this is defined */ static int signal_list[]= { BURN_OS_SIGNAL_MACRO_LIST , -1}; static char *signal_name_list[]= { BURN_OS_SIGNAL_NAME_LIST , "@"}; static int signal_list_count= BURN_OS_SIGNAL_COUNT; static int non_signal_list[]= { BURN_OS_NON_SIGNAL_MACRO_LIST, -1}; static int non_signal_list_count= BURN_OS_NON_SIGNAL_COUNT; #else /* ! Cleanup_has_no_libburn_os_H */ /* Outdated. GNU/Linux only. For backward compatibility with pre-libburn-0.2.3 */ /* Signals to be caught */ static int signal_list[]= { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, SIGTTOU, SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP, SIGVTALRM, SIGXCPU, SIGXFSZ, -1 }; static char *signal_name_list[]= { "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGBUS", "SIGPOLL", "SIGPROF", "SIGSYS", "SIGTRAP", "SIGVTALRM", "SIGXCPU", "SIGXFSZ", "@" }; static int signal_list_count= 24; /* Signals not to be caught */ static int non_signal_list[]= { SIGKILL, SIGCHLD, SIGSTOP, SIGURG, SIGWINCH, -1 }; static int non_signal_list_count= 5; #endif /* Cleanup_has_no_libburn_os_H */ /* run time dynamic part */ static char cleanup_msg[4096]= {""}; static int cleanup_exiting= 0; static int cleanup_has_reported= -1234567890; static void *cleanup_app_handle= NULL; static Cleanup_app_handler_T cleanup_app_handler= NULL; static int cleanup_perform_app_handler_first= 0; static int Cleanup_handler_exit(int exit_value, int signum, int flag) { int ret; if(cleanup_msg[0]!=0 && cleanup_has_reported!=signum) { fprintf(stderr,"\n%s\n",cleanup_msg); cleanup_has_reported= signum; } if(cleanup_perform_app_handler_first) if(cleanup_app_handler!=NULL) { ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0); if(ret==2 || ret==-2) return(2); } if(cleanup_exiting) { fprintf(stderr,"cleanup: ABORT : repeat by pid=%.f, signum=%d\n", (double) getpid(), signum); return(0); } cleanup_exiting= 1; alarm(0); if(!cleanup_perform_app_handler_first) if(cleanup_app_handler!=NULL) { ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0); if(ret==2 || ret==-2) return(2); } exit(exit_value); } static void Cleanup_handler_generic(int signum) { int i; sprintf(cleanup_msg,"UNIX-SIGNAL caught: %d errno= %d",signum,errno); for(i= 0; i<signal_list_count; i++) if(signum==signal_list[i]) { sprintf(cleanup_msg,"UNIX-SIGNAL: %s errno= %d", signal_name_list[i],errno); break; } Cleanup_handler_exit(1,signum,0); } static char *Cleanup_signo_to_name(int signo) { int i; for(i= 0; i < signal_list_count; i++) if(signal_list[i] == signo) return(signal_name_list[i]); return(""); } int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler, int flag) /* bit0= set to default handlers bit1= set to ignore bit2= set cleanup_perform_app_handler_first bit3= set SIGABRT to handler (makes sense with bits 0 or 1) bit8= set SIGPIPE to SIGIGN */ { int i,j,max_sig= -1,min_sig= 0x7fffffff; char *sig_name; sighandler_t sig_handler; cleanup_msg[0]= 0; cleanup_app_handle= handle; cleanup_app_handler= handler; /* <<< make cleanup_exiting thread safe to get rid of this */ if(flag&4) cleanup_perform_app_handler_first= 1; if(flag&1) sig_handler= SIG_DFL; else if(flag&2) sig_handler= SIG_IGN; else sig_handler= Cleanup_handler_generic; /* set all signal numbers between the lowest and highest in the list except those in the non-signal list */ for(i= 0; i<signal_list_count; i++) { if(signal_list[i]>max_sig) max_sig= signal_list[i]; if(signal_list[i]<min_sig) min_sig= signal_list[i]; } for(i= min_sig; i<=max_sig; i++) { for(j= 0; j<non_signal_list_count; j++) if(i==non_signal_list[j]) break; if(j>=non_signal_list_count) { /* Avoid to use particular SIG macros which might not be defined. If they are defined, then their names are in the name list. */ if(flag & (8 | 256)) sig_name= Cleanup_signo_to_name(i); else sig_name= ""; if((flag & 8) && strcmp(sig_name, "SIGABRT") == 0) signal(i,Cleanup_handler_generic); else if((flag & 256) && strcmp(sig_name, "SIGPIPE") == 0) signal(i, SIG_IGN); else signal(i,sig_handler); } } return(1); } #ifdef Cleanup_standalonE struct Demo_apP { char *msg; }; int Demo_app_handler(struct Demo_apP *demoapp, int signum, int flag) { printf("Handling exit of demo application on signal %d. msg=\"%s\"\n", signum,demoapp->msg); return(1); } main() { struct Demo_apP demoapp; demoapp.msg= "Good Bye"; Cleanup_set_handlers(&demoapp,(Cleanup_app_handler_T) Demo_app_handler,0); if(1) { /* change to 0 in order to wait for external signals */ char *cpt= NULL, c= ' '; printf("Intentionally provoking SIGSEGV ...\n"); c= *cpt; printf("Strange: The system ignored a SIGSEGV: c= %u\n", (unsigned int) c); } else { printf("killme: %d\n",getpid()); sleep(3600); } Cleanup_set_handlers(NULL,NULL,1); exit(0); } #endif /* Cleanup_standalonE */ /* cleanup.c , Copyright 2006 Thomas Schmitt <scdbackup@gmx.net> A signal handler which cleans up an application and exits. Provided under GPLv2+ within GPL projects, BSD license elsewise. */ #ifndef Cleanup_includeD #define Cleanup_includeD 1 /** Layout of an application provided cleanup function using an application provided handle as first argument and the signal number as second argument. The third argument is a flag bit field with no defined bits yet. If the handler returns 2 or -2 then it has delegated exit() to some other instance and the Cleanup handler shall return rather than exit. */ typedef int (*Cleanup_app_handler_T)(void *, int, int); /** Establish exiting signal handlers on (hopefully) all signals that are not ignored by default or non-catchable. @param handle Opaque object which knows how to cleanup application @param handler Function which uses handle to perform application cleanup @param flag Control Bitfield bit0= reset to default signal handling */ int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler, int flag); #endif /* ! Cleanup_includeD */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2012 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. Containing disabled code pieces from other GPL programs. They are just quotes for reference. The activated code uses plain polynomial division and other primitve algorithms to build tables of pre-computed CRC values. It then computes the CRCs by algorithms which are derived from mathematical considerations and from analysing the mathematical meaning of the disabled code pieces. The comments here are quite detailed in order to prove my own understanding of the topic. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "crc.h" /* Exploration ts B00214 : ECMA-130, 22.3.6 "CRC field" "This field contains the inverted parity bits. The CRC code word must be divisible by the check polynomial. [...] The generating polynomial shall be G(x) = x^16 + x^12 + x^5 + 1 " Also known as CRC-16-CCITT, CRC-CCITT Used in libburn for raw write modes in sector.c. There is also disabled code in read.c which would use it. ts B11222: The same algorithm is prescribed for CD-TEXT in MMC-3 Annex J. "CRC Field consists of 2 bytes. Initiator system may use these bytes to check errors in the Pack. The polynomial is x^16 + x^12 + x^5 + 1. All bits shall be inverted." libburn/cdtext.c uses a simple bit shifting function : crc_11021() ts B20211: Discussion why both are equivalent in respect to their result: Both map the bits of the given bytes to a polynomial over the finite field of two elements "GF(2)". If bytes 0 .. M are given, then bit n of byte m is mapped to the coefficient of x exponent (n + ((M - m) * 8) + 16). I.e. they translate the bits into a polynomial with the highest bit becoming the coefficient of the highest power of x. Then this polynomial is multiplied by (x exp 16). The set of all such polynomials forms a commutative ring. Its addition corresponds to bitwise exclusive or. Addition and subtraction are identical. Multiplication with polynomials of only one single non-zero coefficient corresponds to leftward bit shifting by the exponent of that coefficient. The same rules apply as with elementary school arithmetics on integer numbers, but with surprising results due to the finite nature of the coefficient number space. Note that multiplication is _not_ an iteration of addition here. Function crc_11021() performs a division with residue by the euclidian algorithm. I.e. it splits polynomial d into quotient q(d) and residue r(d) in respect to the polynomial p = x exp 16 + x exp 12 + x exp 5 + x exp 0 d = p * q(d) + r(d) where r(d) is of a polynomial degree lower than p, i.e. only x exp 15 or lower have non-zero coefficients. The checksum crc(D) is derived by reverse mapping (r(d) * (x exp 16)). I.e. by mapping the coefficient of (x exp n) to bit n of the 16 bit word crc(D). The function result is the bit-wise complement of crc(D). Function crc_ccitt uses a table ccitt_table of r(d) values for the polynomials d which represent the single byte values 0x00 to 0xff. It computes r(d) by computing the residues of an iteratively expanded polynomial. The expansion of the processed byte string A by the next byte B from the input byte string happens by shifting the string 8 bits to the left, and by oring B onto bits 0 to 7. In the space of polynomials, the already processed polynomial "a" (image of byte string A) gets expanded by polynomial b (the image of byte B) like this a * X + b where X is (x exp 8), i.e. the single coefficient polynomial of degree 8. The following argumentation uses algebra with commutative, associative and distributive laws. Valid especially with polynomials is this rule: (1): r(a + b) = r(a) + r(b) because r(a) and r(b) are of degree lower than degree(p) and degree(a + b) <= max(degree(a), degree(b)) Further valid are: (2): r(a) = r(r(a)) (3): r(p * a) = 0 The residue of this expanded polynomial can be expressed by means of the residue r(a) which is known from the previous iteration step, and the residue r(b) which may be looked up in ccitt_table. r(a * X + b) = r(p * q(a) * X + r(a) * X + p * q(b) + r(b)) Applying rule (1): = r(p * q(a) * X) + r(r(a) * X) + r(p * q(b)) + r(r(b)) Rule (3) and rule (2): = r(r(a) * X) + r(b) Be h(a) and l(a) chosen so that: r(a) = h(a) * X + l(a), and l(a) has zero coefficients above (x exp 7), and h(a) * X has zero coefficients below (x exp 8). (They correspond to the high and low byte of the 16 bit word crc(A).) So the previous statement can be written as: = r(h(a) * X * X) + r(l(a) * X) + r(b) Since the degree of l(a) is lower than 8, the degree of l(a) * X is lower than 16. Thus it cannot be divisible by p which has degree 16. So: r(l(a) * X) = l(a) * X This yields = l(a) * X + r(h(a) * X * X + b) h(a) * X * X is the polynomial representation of the high byte of 16 bit word crc(A). So in the world of bit patterns the iteration step is: crc(byte string A expanded by byte B) = (low_byte(crc(A)) << 8) ^ crc(high_byte(crc(A)) ^ B) And this is what function crc_ccitt() does, modulo swapping the exor operants and the final bit inversion which is prescribed by ECMA-130 and MMC-3 Annex J. The start value of the table driven byte shifting algorithm may be different from the start value of an equivalent bit shifting algorithm. This is because the final flushing by zero bits is already pre-computed in the table. So the start value of the table driven algorithm must be the CRC of the 0-polynomial under the start value of the bit shifting algorithm. This fact is not of much importance here, because the start value of the bit shifter is 0x0000 which leads to CRC 0x0000 and thus to start value 0x0000 with the table driven byte shifter. */ /* Plain implementation of polynomial division on a Galois field, where addition and subtraction both are binary exor. Euclidian algorithm. Divisor is x^16 + x^12 + x^5 + 1 = 0x11021. This is about ten times slower than the table driven algorithm. */ static int crc_11021(unsigned char *data, int count, int flag) { int acc = 0, i; for (i = 0; i < count * 8 + 16; i++) { acc = (acc << 1); if (i < count * 8) acc |= ((data[i / 8] >> (7 - (i % 8))) & 1); if (acc & 0x10000) acc ^= 0x11021; } return acc; } /* This is my own table driven implementation for which i claim copyright. Copyright (c) 2012 Thomas Schmitt <scdbackup@gmx.net> */ unsigned short crc_ccitt(unsigned char *data, int count) { static unsigned short crc_tab[256], tab_initialized = 0; unsigned short acc = 0; unsigned char b[1]; int i; if (!tab_initialized) { /* Create table of byte residues */ for (i = 0; i < 256; i++) { b[0] = i; crc_tab[i] = crc_11021(b, 1, 0); } tab_initialized = 1; } /* There seems to be a speed advantage on amd64 if (acc << 8) is the second operant of exor, and *(data++) seems faster than data[i]. */ for (i = 0; i < count; i++) acc = crc_tab[(acc >> 8) ^ *(data++)] ^ (acc << 8); /* ECMA-130 22.3.6 and MMC-3 Annex J (CD-TEXT) want the result with inverted bits */ return ~acc; } /* This was the function inherited with libburn-0.2. 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 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; } */ /* Exploration ts B00214 : ECMA-130, 14.3 "EDC field" "The EDC field shall consist of 4 bytes recorded in positions 2064 to 2067. The error detection code shall be a 32-bit CRC applied on bytes 0 to 2063. The least significant bit of a data byte is used first. The EDC codeword must be divisible by the check polynomial: P(x) = (x^16 + x^15 + x^2 + 1) . (x^16 + x^2 + x + 1) The least significant parity bit (x^0) is stored in the most significant bit position of byte 2067. " Used for raw writing in sector.c ts B20211: Discussion why function crc_32() implements above prescription of ECMA-130. See end of this file for the ofunction inherited with libburn-0.2. The mentioned polynomial product (x^16 + x^15 + x^2 + 1) . (x^16 + x^2 + x + 1) yields this sum of x exponents 32 31 18 16 18 17 4 2 17 16 3 1 16 15 2 0 ====================================== 32 31 16 15 4 3 1 0 (The number of x^18 and x^17 is divisible by two and thus 0 in GF(2).) This yields as 33 bit number: 0x18001801b If above prescription gets implemented straight forward by function crc_18001801b(), then its results match the ones of crc_32() with all test strings which i could invent. The function consists of a conventional polynomial division with reverse input order of bits per byte. Further it swaps the bits in the resulting 32 bit word. That is because sector.c:sector_headers writes the 4 bytes of crc_32() as little endian. The ECMA-130 prescription rather demands big endianness and bit swapping towards the normal bit order in bytes: "The EDC field shall consist of 4 bytes recorded in positions 2064 to 2067. [...] The least significant parity bit (x^0) is stored in the most significant bit position of byte 2067." ----------------------------------------------------------------------- */ /* Overall bit mirroring of a 32 bit word */ unsigned int rfl32(unsigned int acc) { unsigned int inv_acc; int i; inv_acc = 0; for (i = 0; i < 32; i++) if (acc & (1 << i)) inv_acc |= 1 << (31 - i); return inv_acc; } /* Plain implementation of polynomial division on a Galois field, where addition and subtraction both are binary exor. Euclidian algorithm. Divisor is (x^16 + x^15 + x^2 + 1) * (x^16 + x^2 + x + 1). This is about ten times slower than the table driven algorithm. @param flag bit0= do not mirror bits in input bytes and result word (Useful for building the byte indexed CRC table) */ static unsigned int crc_18001801b(unsigned char *data, int count, int flag) { unsigned int acc = 0, top; long int i; unsigned int inv_acc; for (i = 0; i < count * 8 + 32; i++) { top = acc & 0x80000000; acc = (acc << 1); if (i < count * 8) { if (flag & 1) /* Normal bit sequence of input bytes */ acc |= ((data[i / 8] >> (7 - (i % 8))) & 1); else /* Bit sequence of input bytes mirrored */ acc |= ((data[i / 8] >> (i % 8)) & 1); } if (top) acc ^= 0x8001801b; } if (flag & 1) return (unsigned int) (acc & 0xffffffff); /* The bits of the whole 32 bit result are mirrored for ECMA-130 output compliance and for sector.c habit to store CRC little endian although ECMA-130 prescribes it big endian. */ inv_acc = rfl32((unsigned int) acc); return inv_acc; } /* ----------------------------------------------------------------------- Above discussion why crc_ccitt() and crc_11021() yield identical results can be changed from 16 bit to 32 bit by chosing h(a) and l(a) so that: r(a) = h(a) * X * X * X + l(a) h(a) corresponds to the highest byte of crc(A), whereas l(a) corresponds to the lower three bytes of crc(A). This yields r(a * X + b) = l(a) * X + r(h(a) * X * X * X * X + b) h(a) * X * X * X * X is the polynomial representation of the high byte of 32 bit word crc(A). So in the world of bit patterns we have: crc(byte string A expanded by byte B) = (lowest_three_bytes(crc(A)) << 8) ^ crc(high_byte(crc(A)) ^ B) Regrettably this does not yet account for the byte-internal mirroring of bits during the conversion from bit pattern to polynomial, and during conversion from polynomial residue to bit pattern. Be rfl8(D) the result of byte-internal mirroring of bit pattern D, and mirr8(d) its corresponding polynom. Be now h(a) and l(a) chosen so that: r(mirr8(a)) = h(a) * X * X * X + l(a) This corresponds to highest byte and lower three bytes of crc(A). r(mirr8(a) * X + mirr8(b)) = r(h(a) * X * X * X * X) + r(l(a) * X) + r(mirr8(b)) = l(a)) * X + r(h(a) * X * X * X * X + mirr8(b)) The corresponding bit pattern operation is crc(mirrored byte string A expanded by mirrored byte B) = (lowest_three_bytes(crc(A)) << 8) ^ crc(high_byte(crc(A)) ^ rfl8(B)) This demands a final result mirroring to meet the ECMA-130 prescription. rfl8() can be implemented as lookup table. The start value of the bit shifting iteration is 0x00000000, which leads to the same start value for the table driven byte shifting. The following function crc32_by_tab() yields the same results as functions crc_18001801b() and crc_32(): ----------------------------------------------------------------------- */ /* Byte-internal bit mirroring function. */ unsigned int rfl8(unsigned int acc) { unsigned int inv_acc; int i, j; inv_acc = 0; for (j = 0; j < 4; j++) for (i = 0; i < 8; i++) if (acc & (1 << (i + 8 * j))) inv_acc |= 1 << ((7 - i) + 8 * j); return inv_acc; } #ifdef Libburn_with_crc_illustratioN /* Not needed for libburn. The new implementation of function crc_32() is the one that is used. */ unsigned int crc32_by_tab(unsigned char *data, int count, int flag) { static unsigned int crc_tab[256], tab_initialized = 0; static unsigned char mirr_tab[256]; unsigned int acc, inv_acc; unsigned char b[1]; int i; if (!tab_initialized) { for (i = 0; i < 256; i++) { b[0] = i; /* Create table of non-mirrored 0x18001801b residues */ crc_tab[i] = crc_18001801b(b, 1, 1); /* Create table of mirrored byte values */ mirr_tab[i] = rfl8(i); } tab_initialized = 1; } acc = 0; for (i = 0; i < count; i++) acc = (acc << 8) ^ crc_tab[(acc >> 24) ^ mirr_tab[data[i]]]; /* The bits of the whole 32 bit result are mirrored for ECMA-130 output compliance and for sector.c habit to store CRC little endian although ECMA-130 prescribes it big endian. */ inv_acc = rfl32((unsigned int) acc); return inv_acc; } #endif /* Libburn_with_crc_illustratioN */ /* ----------------------------------------------------------------------- Above function yields sufficient performance, nevertheless the old function crc_32() (see below) is faster by avoiding the additional mirror table lookup. A test with 10 times 650 MB on 3000 MHz amd64: crc_18001801b : 187 s crc32_by_tab : 27 s crc_32 : 16 s So how does crc_32() avoid the application of bit mirroring to B ?. Inherited crc_32() performs crc = crc32_table[(crc ^ *data++) & 0xffL] ^ (crc >> 8); Above function crc32_by_tab() would be crc = crc_tab[(crc >> 24) ^ mirr_tab[*data++]] ^ (crc << 8); The shortcut does not change the polynomial representation of the algorithm or the mapping from and to bit patterns. It only mirrors the bit direction in the bytes and in the 32-bit words which are involved in the bit pattern computation. This affects input (which is desired), intermediate state (which is as good as unmirrored), and final output (which would be slightly undesirable if libburn could not use the mirrored result anyway). Instead of the high byte (crc >> 24), the abbreviated algorithm uses the low byte of the mirrored intermediate checksum (crc & 0xffL). Instead of shifting the other three intermediate bytes to the left (crc << 8), the abbreviated algorithm shifts them to the right (crc >> 8). In both cases they overwrite the single byte that was used for computing the table index. The byte indexed table of CRC values needs to hold mirrored 32 bit values. The byte index [(crc ^ *data++) & 0xffL] would need to be mirrored, which would eat up the gain of not mirroring the input bytes. But this mirroring can be pre-computed into the table by exchanging each value with the value of its mirrored index. So this relation exists between the CRC table crc_tab[] of crc32_by_tab() and the table crc32_table[] of the abbreviated algorithm crc_32(): crc_tab[i] == rfl32(crc32_table[rfl8(i)]) for i={0..255}. I compared the generated table in crc32_by_tab() by this test for (i = 0; i < 256; i++) { if (rfl32(crc_tab[rfl8(i)]) != crc32_table[i] || crc_tab[i] != rfl32(crc32_table[rfl8(i)])) { printf("DEVIATION : i = %d\n", i); exit(1); } } No screaming abort happened. ----------------------------------------------------------------------- */ /* This is my own mirrored table implementation for which i claim copyright. With gcc -O2 it shows the same efficiency as the inherited implementation below. With -O3, -O1, or -O0 it is only slightly slower. Copyright (c) 2012 Thomas Schmitt <scdbackup@gmx.net> */ unsigned int crc_32(unsigned char *data, int count) { static unsigned int crc_tab[256], tab_initialized = 0; unsigned int acc = 0; unsigned char b[1]; int i; if (!tab_initialized) { /* Create table of mirrored 0x18001801b residues in bit-mirrored index positions. */ for (i = 0; i < 256; i++) { b[0] = i; crc_tab[rfl8(i)] = rfl32(crc_18001801b(b, 1, 1)); } tab_initialized = 1; } for (i = 0; i < count; i++) acc = (acc >> 8) ^ crc_tab[(acc & 0xff) ^ data[i]]; /* The bits of the whole 32 bit result stay mirrored for ECMA-130 output 8-bit mirroring and for sector.c habit to store the CRC little endian although ECMA-130 prescribes it big endian. */ return acc; } /* ----------------------------------------------------------------------- This was the function inherited with libburn-0.2 which implements the abbreviated algorithm. Its obscure existence led me to above insights. My compliments to the (unknown) people who invented this. 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 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; } */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2012 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifndef BURN__CRC_H #define BURN__CRC_H #ifdef Xorriso_standalonE /* Source module crc.c of yet unclear ancestry is excluded from GNU xorriso */ /* ts B20219 : The functions have been re-implemented from scratch after studying texts about CRC computation and understanding the meaning of the underlying ECMA-130 specs. Nevertheless, there is no need to include them into xorriso because it does neither CD-TEXT nor raw CD writing. */ #ifndef Libburn_no_crc_C #define Libburn_no_crc_C 1 #endif #endif #ifndef Libburn_no_crc_C unsigned short crc_ccitt(unsigned char *, int len); unsigned int crc_32(unsigned char *, int len); #endif /* Libburn_no_crc_C */ #endif /* BURN__CRC_H */ /* ddlpa Implementation of Delicate Device Locking Protocol level A. Copyright (C) 2007 Thomas Schmitt <scdbackup@gmx.net> Provided under any of the following licenses: GPL, LGPL, BSD. Choose one. Compile as test program: cc -g -Wall \ -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 -D_LARGEFILE64_SOURCE \ -DDDLPA_C_STANDALONE -o ddlpa ddlpa.c The system macros enable 64-bit off_t and open(2) flag O_LARGEFILE, which are not absolutely necessary but explicitly take into respect that our devices can offer more than 2 GB of addressable data. Run test program: ./ddlpa /dev/sr0 15 ./ddlpa 0,0,0 15 */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <scsi/scsi.h> /* All callers of ddlpa must do this */ #include "ddlpa.h" /* 1 = Enable progress message on stderr, 0 = normal silent operation */ static int ddlpa_debug_mode = 1; /* #define _GNU_SOURCE or _LARGEFILE64_SOURCE to get real O_LARGEFILE */ #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif #ifndef O_BINARY #define O_BINARY 0 #endif /* ----------------------- private -------------------- */ static int ddlpa_new(struct ddlpa_lock **lck, int o_flags, int ddlpa_flags) { int i; struct ddlpa_lock *o; o = *lck = (struct ddlpa_lock *) malloc(sizeof(struct ddlpa_lock)); if (o == NULL) return ENOMEM; for (i = 0; i < sizeof(struct ddlpa_lock); i++) ((char *) o)[i] = 0; o->path = NULL; o->fd = -1; for (i = 0; i < DDLPA_MAX_SIBLINGS; i++) o->sibling_fds[i] = -1; o->errmsg = NULL; o->o_flags = o_flags; o->ddlpa_flags = ddlpa_flags; return 0; } static int ddlpa_enumerate(struct ddlpa_lock *o, int *idx, char path[DDLPA_MAX_STD_LEN + 1]) { if (*idx < 0) *idx = 0; if (*idx < 26) sprintf(path, "/dev/hd%c", 'a' + *idx); else if (*idx < 256 + 26) sprintf(path, "/dev/sr%d", *idx - 26); else if (*idx < 2 * 256 + 26) sprintf(path, "/dev/scd%d", *idx - 256 - 26); else if (*idx < 3 * 256 + 26) sprintf(path, "/dev/sg%d", *idx - 2 * 256 - 26); else return 1; (*idx)++; return 0; } static int ddlpa_std_by_rdev(struct ddlpa_lock *o) { int idx = 0; char try_path[DDLPA_MAX_STD_LEN+1]; struct stat path_stbuf, try_stbuf; if (!o->path_is_valid) return EFAULT; if (stat(o->path, &path_stbuf) == -1) return errno; while (ddlpa_enumerate(o, &idx, try_path) == 0) { if (stat(try_path, &try_stbuf) == -1) continue; if (path_stbuf.st_rdev != try_stbuf.st_rdev) continue; strcpy(o->std_path, try_path); if (ddlpa_debug_mode) fprintf(stderr, "DDLPA_DEBUG: ddlpa_std_by_rdev(\"%s\") = \"%s\"\n", o->path, o->std_path); return 0; } return ENOENT; } /* Caution : these tests are valid only with standard paths */ static int ddlpa_is_scsi(struct ddlpa_lock *o, char *path) { return (strncmp(path, "/dev/s", 6) == 0); } static int ddlpa_is_sg(struct ddlpa_lock *o, char *path) { return (strncmp(path, "/dev/sg", 7) == 0); } static int ddlpa_is_sr(struct ddlpa_lock *o, char *path) { return (strncmp(path, "/dev/sr", 7) == 0); } static int ddlpa_is_scd(struct ddlpa_lock *o, char *path) { return (strncmp(path, "/dev/scd", 8) == 0); } static int ddlpa_fcntl_lock(struct ddlpa_lock *o, int fd, int l_type) { struct flock lockthing; int ret; memset(&lockthing, 0, sizeof(lockthing)); lockthing.l_type = l_type; lockthing.l_whence = SEEK_SET; lockthing.l_start = 0; lockthing.l_len = 0; ret = fcntl(fd, F_SETLK, &lockthing); if (ret == -1) return EBUSY; return 0; } static int ddlpa_occupy(struct ddlpa_lock *o, char *path, int *fd, int no_o_excl) { int ret, o_flags, o_rw, l_type; char *o_rwtext; o_flags = o->o_flags | O_NDELAY | O_BINARY; if(!no_o_excl) o_flags |= O_EXCL; o_rw = (o_flags) & (O_RDONLY | O_WRONLY | O_RDWR); o_rwtext = (o_rw == O_RDONLY ? "O_RDONLY" : (o_rw == O_WRONLY ? "O_WRONLY" : (o_rw == O_RDWR ? "O_RDWR " : "O_?rw-mode?"))); *fd = open(path, o_flags); if (*fd == -1) { o->errmsg = malloc(strlen(path)+160); if (o->errmsg) sprintf(o->errmsg, "Failed to open %s | O_NDELAY %s: '%s'", o_rwtext, (o_flags & O_EXCL ? "| O_EXCL " : ""), path); return (errno ? errno : EBUSY); } if (o_rw == O_RDWR || o_rw == O_WRONLY) l_type = F_WRLCK; else l_type = F_RDLCK; ret = ddlpa_fcntl_lock(o, *fd, l_type); if (ret) { o->errmsg = malloc(strlen(path)+160); if (o->errmsg) sprintf(o->errmsg, "Failed to lock fcntl(F_WRLCK) : '%s'",path); close(*fd); *fd = -1; return ret; } if (ddlpa_debug_mode) fprintf(stderr, "DDLPA_DEBUG: ddlpa_occupy() %s %s: '%s'\n", o_rwtext, (no_o_excl ? " " : "O_EXCL "), path); return 0; } static int ddlpa_obtain_scsi_adr(struct ddlpa_lock *o, char *path, int *bus, int *host, int *channel, int *id, int *lun) { int fd, ret, open_mode = O_RDONLY | O_NDELAY | O_BINARY; struct my_scsi_idlun { int x; int host_unique_id; }; struct my_scsi_idlun idlun; fd = open(path, open_mode); if (fd == -1) return (errno ? errno : EBUSY); if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, bus) == -1) *bus = -1; ret = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun); close(fd); if (ret == -1) return (errno ? errno : EIO); *host = (idlun.x >> 24) & 255; *channel = (idlun.x >> 16) & 255; *id = (idlun.x) & 255; *lun = (idlun.x >> 8 ) & 255; return 0; } static int ddlpa_collect_siblings(struct ddlpa_lock *o) { int idx = 0, ret, have_sg = 0, have_sr = 0, have_scd = 0; dev_t path_dev; ino_t path_inode; struct stat stbuf; char *path, try_path[DDLPA_MAX_STD_LEN+1]; int t_bus, t_host, t_channel, t_id, t_lun; if (o->ddlpa_flags & DDLPA_OPEN_GIVEN_PATH) path = o->path; else path = o->std_path; if (path[0] == 0 || o->num_siblings != 0) return EFAULT; if (!ddlpa_is_scsi(o, o->std_path)) return EFAULT; if (stat(path, &stbuf) == -1) return errno; path_inode = stbuf.st_ino; path_dev = stbuf.st_dev; o->rdev = stbuf.st_rdev; o->dev = stbuf.st_dev; o->ino = stbuf.st_ino; ret = ddlpa_obtain_scsi_adr(o, path, &(o->bus), &(o->host), &(o->channel), &(o->id), &(o->lun)); if (ret) { o->errmsg = strdup( "Cannot obtain SCSI parameters host,channel,id,lun"); return ret; } o->hcilb_is_valid = 1; while (ddlpa_enumerate(o, &idx, try_path) == 0) { if (!ddlpa_is_scsi(o, try_path)) continue; if (stat(try_path, &stbuf) == -1) continue; ret = ddlpa_obtain_scsi_adr(o, try_path, &t_bus, &t_host, &t_channel, &t_id, &t_lun); if (ret) { /* >>> interpret error, memorize busy, no permission */ continue; } if (t_host != o->host || t_channel != o->channel || t_id != o->id || t_lun != o->lun) continue; if (o->num_siblings >= DDLPA_MAX_SIBLINGS) { o->errmsg = strdup("Too many matching device files found"); return ERANGE; } if (ddlpa_is_sg(o, try_path)) have_sg = 1; else if (ddlpa_is_sr(o, try_path)) have_sr = 1; else if (ddlpa_is_scd(o, try_path)) have_scd = 1; strcpy(o->sibling_paths[o->num_siblings], try_path); o->sibling_rdevs[o->num_siblings] = stbuf.st_rdev; o->sibling_devs[o->num_siblings] = stbuf.st_dev; o->sibling_inodes[o->num_siblings] = stbuf.st_ino; if (ddlpa_debug_mode) fprintf(stderr, "DDLPA_DEBUG: ddlpa_collect_siblings() found \"%s\"\n", try_path); (o->num_siblings)++; } if (have_sg && have_sr && have_scd) return 0; if (o->ddlpa_flags & DDLPA_ALLOW_MISSING_SGRCD) return 0; o->errmsg = strdup("Did not find enough siblings"); /* >>> add more info about busy and forbidden paths */ return EBUSY; } static int ddlpa_std_by_btl(struct ddlpa_lock *o) { int idx = 0, ret; char try_path[DDLPA_MAX_STD_LEN+1]; int t_bus, t_host, t_channel, t_id, t_lun; if (!o->inbtl_is_valid) return EFAULT; while (ddlpa_enumerate(o, &idx, try_path) == 0) { if (!ddlpa_is_sr(o, try_path)) continue; ret = ddlpa_obtain_scsi_adr(o, try_path, &t_bus, &t_host, &t_channel, &t_id, &t_lun); if (ret) { /* >>> interpret error, memorize busy, no permission */ continue; } if (t_bus != o->in_bus || t_id != o->in_target || t_lun != o->in_lun) continue; strcpy(o->std_path, try_path); if (ddlpa_debug_mode) fprintf(stderr, "DDLPA_DEBUG: ddlpa_std_by_btl(%d,%d,%d) = \"%s\"\n", t_bus, t_id, t_lun, o->std_path); return 0; } /* >>> add more info about busy and forbidden paths */ return ENOENT; } static int ddlpa_open_all(struct ddlpa_lock *o) { int i, j, ret, no_o_excl; if (ddlpa_is_scsi(o, o->std_path)) { ret = ddlpa_collect_siblings(o); if (ret) return ret; for (i = 0; i < o->num_siblings; i++) { /* Watch out for the main personality of the drive. */ /* No need to occupy identical path or softlink path */ if (o->sibling_devs[i] == o->dev && o->sibling_inodes[i] == o->ino) continue; /* There may be the same rdev but different inode. */ no_o_excl = (o->sibling_rdevs[i] == o->rdev); /* Look for multiply registered device drivers with distinct inodes. */ for (j = 0; j < i; j++) { if (o->sibling_devs[j] == o->sibling_devs[i] && o->sibling_inodes[j] == o->sibling_inodes[i]) break; if (o->sibling_rdevs[j] == o->sibling_rdevs[i]) no_o_excl = 1; } if (j < i) continue; /* inode is already occupied */ ret = ddlpa_occupy(o, o->sibling_paths[i], &(o->sibling_fds[i]), no_o_excl); if (ret) return ret; } } if (o->ddlpa_flags & DDLPA_OPEN_GIVEN_PATH) ret = ddlpa_occupy(o, o->path, &(o->fd), 0); else ret = ddlpa_occupy(o, o->std_path, &(o->fd), 0); if (ret) return ret; /* >>> use fcntl() to adjust O_NONBLOCK */; return 0; } /* ----------------------- public -------------------- */ int ddlpa_destroy(struct ddlpa_lock **lockbundle) { struct ddlpa_lock *o; int i; o= *lockbundle; if (o == NULL) return 0; for (i = 0; i < o->num_siblings; i++) if (o->sibling_fds[i] != -1) close(o->sibling_fds[i]); if(o->fd != -1) close(o->fd); if (o->path != NULL) free(o->path); if (o->errmsg != NULL) free(o->errmsg); free((char *) o); *lockbundle = NULL; return 0; } int ddlpa_lock_path(char *path, int o_flags, int ddlpa_flags, struct ddlpa_lock **lockbundle, char **errmsg) { struct ddlpa_lock *o; int ret; *errmsg = NULL; if (ddlpa_new(&o, o_flags, ddlpa_flags)) return ENOMEM; *lockbundle = o; o->path = strdup(path); if (o->path == NULL) return ENOMEM; o->path_is_valid = 1; ret = ddlpa_std_by_rdev(o); if (ret) { *errmsg = strdup( "Cannot find equivalent of given path among standard paths"); return ret; } ret = ddlpa_open_all(o); if (ret) { *errmsg = o->errmsg; o->errmsg = NULL; ddlpa_destroy(&o); } return ret; } int ddlpa_lock_btl(int bus, int target, int lun, int o_flags, int ddlpa_flags, struct ddlpa_lock **lockbundle, char **errmsg) { struct ddlpa_lock *o; int ret; *errmsg = NULL; ddlpa_flags &= ~DDLPA_OPEN_GIVEN_PATH; if (ddlpa_new(&o, o_flags, ddlpa_flags)) return ENOMEM; *lockbundle = o; o->in_bus = bus; o->in_target = target; o->in_lun = lun; o->inbtl_is_valid = 1; ret = ddlpa_std_by_btl(o); if (ret) { *errmsg = strdup( "Cannot find /dev/sr* with given Bus,Target,Lun"); return ret; } ret = ddlpa_open_all(o); if (ret) { *errmsg = o->errmsg; o->errmsg = NULL; ddlpa_destroy(&o); return ret; } return 0; } #ifdef DDLPA_C_STANDALONE /* ----------------------------- Test / Demo -------------------------- */ int main(int argc, char **argv) { struct ddlpa_lock *lck = NULL; char *errmsg = NULL, *opened_path = NULL, *my_path = NULL; int i, ret, fd = -1, duration = -1, bus = -1, target = -1, lun = -1; if (argc < 3) { usage:; fprintf(stderr, "usage: %s device_path duration\n", argv[0]); exit(1); } my_path = argv[1]; sscanf(argv[2], "%d", &duration); if (duration < 0) goto usage; /* For our purpose, only O_RDWR is a suitable access mode. But in order to allow experiments, o_flags are freely adjustable. Warning: Do _not_ set an own O_EXCL flag with the following calls ! (This freedom to fail may get removed in a final version.) */ if (my_path[0] != '/' && my_path[0] != '.' && strchr(my_path, ',') != NULL) { /* cdrecord style dev=Bus,Target,Lun */ sscanf(my_path, "%d,%d,%d", &bus, &target, &lun); ret = ddlpa_lock_btl(bus, target, lun, O_RDWR | O_LARGEFILE, 0, &lck, &errmsg); } else { /* This substitutes for: fd = open(my_path, O_RDWR | O_EXCL | O_LARGEFILE | O_BINARY); */ ret = ddlpa_lock_path(my_path, O_RDWR | O_LARGEFILE, 0, &lck, &errmsg); } if (ret) { fprintf(stderr, "Cannot exclusively open '%s'\n", my_path); if (errmsg != NULL) fprintf(stderr, "Reason given : %s\n", errmsg); free(errmsg); fprintf(stderr, "Error condition : %d '%s'\n", ret, strerror(ret)); exit(2); } fd = lck->fd; printf("---------------------------------------------- Lock gained\n"); /* Use fd for the usual operations on the device depicted by my_path. */ /* This prints an overview of the impact of the lock */ if (lck->ddlpa_flags & DDLPA_OPEN_GIVEN_PATH) opened_path = lck->path; else opened_path = lck->std_path; printf("ddlpa: opened %s", opened_path); if (strcmp(opened_path, lck->std_path) != 0) printf(" (an alias of '%s')", lck->std_path); printf("\n"); if (lck->num_siblings > 0) { printf("ddlpa: opened siblings:"); for (i = 0; i < lck->num_siblings; i++) if (lck->sibling_fds[i] != -1) printf(" %s", lck->sibling_paths[i]); printf("\n"); } /* This example waits a while. So other lock candidates can collide. */ for (i = 0; i < duration; i++) { sleep(1); fprintf(stderr, "\rslept %d seconds of %d", i + 1, duration); } fprintf(stderr, "\n"); /* When finally done with the drive, this substitutes for: close(fd); */ if (ddlpa_destroy(&lck)) { /* Well, man 2 close says it can fail. */ exit(3); } exit(0); } #endif /* DDLPA_C_STANDALONE */ /* ddlpa Implementation of Delicate Device Locking Protocol level A. Copyright (C) 2007 Thomas Schmitt <scdbackup@gmx.net> Provided under any of the following licenses: GPL, LGPL, BSD. Choose one. See ../doc/ddlp.txt for a description of the protocol. */ #ifndef DDLPA_H_INCLUDED #define DDLPA_H_INCLUDED 1 /* An upper limit for the length of standard paths and sibling paths */ #define DDLPA_MAX_STD_LEN 15 /* An upper limit for the number of siblings */ #define DDLPA_MAX_SIBLINGS 5 struct ddlpa_lock { /* Recorded input parameters of locking call */ char *path; int path_is_valid; int in_bus, in_target, in_lun; int inbtl_is_valid; int ddlpa_flags; int o_flags; /* Result of locking call */ char std_path[DDLPA_MAX_STD_LEN + 1]; int fd; dev_t rdev; dev_t dev; ino_t ino; int host, channel, id, lun, bus; int hcilb_is_valid; int num_siblings; char sibling_paths[DDLPA_MAX_SIBLINGS][DDLPA_MAX_STD_LEN + 1]; int sibling_fds[DDLPA_MAX_SIBLINGS]; dev_t sibling_rdevs[DDLPA_MAX_SIBLINGS]; dev_t sibling_devs[DDLPA_MAX_SIBLINGS]; ino_t sibling_inodes[DDLPA_MAX_SIBLINGS]; /* Is NULL if all goes well. Else it may contain a text message. */ char *errmsg; }; /** Lock a recorder by naming a device file path. Allocate a new container. @param path Gives the file system path of the recorder as known to the calling program. @param o_flags flags for open(2). Do not use O_EXCL here because this is done automatically whenever appropriate. Advised is O_RDWR | O_LARGEFILE, eventually | O_NDELAY. @param ddlpa_flags 0 = default behavior: the standard path will be opened and treated by fcntl(F_SETLK) DDLPA_OPEN_GIVEN_PATH causes the input parameter "path" to be used with open(2) and fcntl(2). DDLPA_ALLOW_MISSING_SGRCD allows to grant a lock although not all three, a sg, a sr and a scd device file have been found during sibling search. Normally this is counted as failure due to EBUSY. @param lockbundle gets allocated and then represents the locking state @param errmsg if *errmsg is not NULL after the call, it contains an error message. Then to be released by free(3). It is NULL in case of success or lack of memory. @return 0=success , else an errno compatible error number */ int ddlpa_lock_path(char *path, int o_flags, int ddlpa_flags, struct ddlpa_lock **lockbundle, char **errmsg); /** Lock a recorder by naming a Bus,Target,Lun number triple. Allocate a new container. @param bus parameter to match ioctl(SCSI_IOCTL_GET_BUS_NUMBER) @param target parameter to match ioctl(SCSI_IOCTL_GET_IDLUN) &0xff @param lun parameter to match ioctl(SCSI_IOCTL_GET_IDLUN) &0xff00 @param o_flags see ddlpa_lock_path(). @param ddlpa_flags see ddlpa_lock_path(). Flag DDLPA_OPEN_GIVEN_PATH will be ignored. @param lockbundle see ddlpa_lock_path(). @param errmsg see ddlpa_lock_path(). @return 0=success , else an errno compatible error number */ int ddlpa_lock_btl(int bus, int target, int lun, int o_flags, int ddlpa_flags, struct ddlpa_lock **lockbundle, char **errmsg); /** Release the lock by closing all filedescriptors and freeing memory. @param lockbundle the lock which is to be released. *lockbundle will be set to NULL by this call. @return 0=success , 1=failure */ int ddlpa_destroy(struct ddlpa_lock **lockbundle); /** Definitions of macros used in above functions */ #define DDLPA_OPEN_GIVEN_PATH 1 #define DDLPA_ALLOW_MISSING_SGRCD 2 #endif /* DDLPA_H_INCLUDED */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #ifdef WIN32 #include <windows.h> #endif #include <stdarg.h> #include <stdio.h> #include "libburn.h" #include "debug.h" static int burn_verbosity = 0; void burn_set_verbosity(int v) { burn_verbosity = v; } /* -*- 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 */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2024 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <dirent.h> /* ts A61007 */ /* #include <a ssert.h> */ #include <stdio.h> #include <string.h> #include <ctype.h> #include <pthread.h> #include <errno.h> #include <fcntl.h> /* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #include "libburn.h" #include "init.h" #include "drive.h" #include "transport.h" #include "debug.h" #include "init.h" #include "toc.h" #include "util.h" #include "sg.h" #include "structure.h" /* ts A70107 : to get BE_CANCELLED */ #include "error.h" /* ts A70219 : for burn_disc_get_write_mode_demands() */ #include "options.h" /* A70225 : to learn about eventual Libburn_dvd_r_dl_multi_no_close_sessioN */ #include "write.h" /* A70903 : for burn_scsi_setup_drive() */ #include "spc.h" /* A90815 : for mmc_obtain_profile_name() */ #include "mmc.h" /* B60730 : for Libburn_do_no_immed_defaulT */ #include "os.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; static struct burn_drive drive_array[255]; static int drivetop = -1; /* ts A80410 : in init.c */ extern int burn_support_untested_profiles; /* ts B10312 : in init.c */ extern int burn_drive_role_4_allowed; /* ts A61021 : the unspecific part of sg.c:enumerate_common() */ int burn_setup_drive(struct burn_drive *d, char *fname) { d->devname = strdup(fname); memset(&d->params, 0, sizeof(struct params)); d->idata = NULL; d->mdata = NULL; d->toc_entry = NULL; d->released = 1; d->stdio_fd = -1; d->status = BURN_DISC_UNREADY; d->erasable = 0; d->current_profile = -1; d->do_stream_recording = 0; d->stream_recording_start= 0; d->role_5_nwa = 0; #ifdef Libburn_do_no_immed_defaulT d->do_no_immed = Libburn_do_no_immed_defaulT; #else d->do_no_immed = 0; #endif d->features = NULL; d->drive_serial_number = NULL; d->drive_serial_number_len = -1; d->media_serial_number = NULL; d->media_serial_number_len = -1; return 1; } /* ts A70903 */ void burn_drive_free_subs(struct burn_drive *d) { if (d->idata != NULL) free((void *) d->idata); d->idata = NULL; if (d->mdata != NULL) { burn_mdata_free_subs(d->mdata); free((void *) d->mdata); } d->mdata = NULL; if(d->toc_entry != NULL) free((void *) d->toc_entry); d->toc_entry = NULL; if (d->devname != NULL) free(d->devname); d->devname = NULL; if (d->stdio_fd >= 0) close (d->stdio_fd); d->stdio_fd = -1; burn_feature_descr_free(&(d->features), 0); BURN_FREE_MEM(d->drive_serial_number); BURN_FREE_MEM(d->media_serial_number); d->drive_serial_number = d->media_serial_number = NULL; d->drive_serial_number_len = d->media_serial_number_len = 0; sg_dispose_drive(d, 0); } /* ts A60904 : ticket 62, contribution by elmom */ /* splitting former burn_drive_free() (which freed all, into two calls) */ void burn_drive_free(struct burn_drive *d) { if (d->global_index == -1) return; /* ts A60822 : close open fds before forgetting them */ if (d->drive_role == 1) if (burn_drive_is_open(d)) { d->unlock(d); d->release(d); } burn_drive_free_subs(d); d->global_index = -1; } void burn_drive_free_all(void) { int i; for (i = 0; i < drivetop + 1; i++) burn_drive_free(&(drive_array[i])); drivetop = -1; memset(drive_array, 0, sizeof(drive_array)); } /* ts A60822 */ int burn_drive_is_open(struct burn_drive *d) { if (d->drive_role != 1) return (d->stdio_fd >= 0); /* ts A61021 : moved decision to sg.c */ return d->drive_is_open(d); } /* ts A60906 */ int burn_drive_force_idle(struct burn_drive *d) { d->busy = BURN_DRIVE_IDLE; return 1; } /* ts A60906 */ int burn_drive_is_released(struct burn_drive *d) { return !!d->released; } /* ts A60906 */ /** Inquires drive status in respect to degree of app usage. @param return -2 = drive is forgotten -1 = drive is closed (i.e. released explicitly) 0 = drive is open, not grabbed (after scan, before 1st grab) 1 = drive is grabbed but BURN_DRIVE_IDLE 2 = drive is grabbed, synchronous read/write interrupted 10 = drive is grabbing (BURN_DRIVE_GRABBING) 100 = drive is busy in cancelable state 1000 = drive is in non-cancelable state Expect a monotonous sequence of usage severity to emerge in future. */ int burn_drive_is_occupied(struct burn_drive *d) { if(d->global_index < 0) return -2; if(!burn_drive_is_open(d)) return -1; if(d->busy == BURN_DRIVE_GRABBING) return 10; if(d->released) return 0; if(d->busy == BURN_DRIVE_IDLE) return 1; if(d->busy == BURN_DRIVE_READING_SYNC || d->busy == BURN_DRIVE_WRITING_SYNC) return 2; if(d->busy == BURN_DRIVE_WRITING || d->busy == BURN_DRIVE_WRITING_LEADIN || d->busy == BURN_DRIVE_WRITING_LEADOUT || d->busy == BURN_DRIVE_WRITING_PREGAP) { /* ts A70928 */ /* >>> how do i learn whether the writer thread is still alive ? */; /* >>> what to do if writer is dead ? At least sync disc ?*/; return 50; } if(d->busy == BURN_DRIVE_READING) { return 50; } return 1000; } /* 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; } /* ts A80801 */ int burn_drive_is_listed(char *path, struct burn_drive **found, int flag) { int i, ret; char *drive_adr = NULL, *off_adr = NULL; BURN_ALLOC_MEM(drive_adr, char, BURN_DRIVE_ADR_LEN); BURN_ALLOC_MEM(off_adr, char, BURN_DRIVE_ADR_LEN); ret = burn_drive_convert_fs_adr(path, off_adr); if (ret <= 0) strcpy(off_adr, path); for (i = 0; i <= drivetop; i++) { if (drive_array[i].global_index < 0) continue; ret = burn_drive_d_get_adr(&(drive_array[i]), drive_adr); if (ret <= 0) continue; if(strcmp(off_adr, drive_adr) == 0) { if (found != NULL) *found= &(drive_array[i]); {ret= 1; goto ex;} } } ret= 0; ex:; BURN_FREE_MEM(drive_adr); BURN_FREE_MEM(off_adr); return ret; } /* ts A61125 : media status aspects of burn_drive_grab() */ int burn_drive_inquire_media(struct burn_drive *d) { /* ts A61225 : after loading the tray, mode page 2Ah can change */ d->getcaps(d); /* ts A61020 : d->status was set to BURN_DISC_BLANK as pure guess */ /* ts A71128 : run read_disc_info() for any recognizable profile */ if (d->current_profile > 0 || d->current_is_guessed_profile || (d->mdata->p2a_valid > 0 && (d->mdata->cdr_write || d->mdata->cdrw_write || d->mdata->dvdr_write || d->mdata->dvdram_write)) ) { d->read_disc_info(d); } else { if (d->current_profile == -1 || d->current_is_cd_profile) d->read_toc(d); /* ts A70314 , B10712 */ if (d->status != BURN_DISC_EMPTY) d->status = BURN_DISC_UNSUITABLE; } return 1; } /* ts B10730 */ /* Send a default mode page 05 to CD and DVD-R-oids */ int burn_drive_send_default_page_05(struct burn_drive *d, int flag) { struct burn_write_opts *opts; if (d->sent_default_page_05) return 0; if (!((d->status == BURN_DISC_APPENDABLE || d->status == BURN_DISC_BLANK) && (d->current_is_cd_profile || d->current_profile == 0x11 || d->current_profile == 0x14 || d->current_profile == 0x15))) return 0; opts = burn_write_opts_new(d); if (opts == NULL) return -1; if (d->status == BURN_DISC_APPENDABLE) burn_write_opts_set_write_type(opts, BURN_WRITE_TAO, BURN_BLOCK_MODE1); else burn_write_opts_set_write_type(opts, BURN_WRITE_SAO, BURN_BLOCK_SAO); d->send_write_parameters(d, NULL, -1, opts); burn_write_opts_free(opts); d->sent_default_page_05 = 1; return 1; } /* ts A70924 */ int burn_drive__fd_from_special_adr(char *adr) { int fd = -1, i; if (strcmp(adr, "-") == 0) fd = 1; if(strncmp(adr, "/dev/fd/", 8) == 0) { for (i = 8; adr[i]; i++) if (!isdigit(adr[i])) break; if (i> 8 && adr[i] == 0) fd = atoi(adr + 8); } return fd; } /* @param flag bit0= accept read-only files and return 2 in this case bit1= accept write-only files and return 3 in this case */ static int burn_drive__is_rdwr(char *fname, int *stat_ret, struct stat *stbuf_ret, off_t *read_size_ret, int flag) { int fd, is_rdwr = 1, ret, getfl_ret, st_ret, mask; struct stat stbuf; off_t read_size = 0; memset(&stbuf, 0, sizeof(struct stat)); fd = burn_drive__fd_from_special_adr(fname); if (fd >= 0) st_ret = fstat(fd, &stbuf); else st_ret = stat(fname, &stbuf); if (st_ret != -1) { is_rdwr = burn_os_is_2k_seekrw(fname, 0); ret = 1; if (S_ISREG(stbuf.st_mode)) read_size = stbuf.st_size; else if (is_rdwr) ret = burn_os_stdio_capacity(fname, 0, &read_size); if (ret <= 0 || read_size >= BURN_DRIVE_MAX_BYTES) read_size = BURN_DRIVE_MAX_BYTES; } if (is_rdwr && fd >= 0) { getfl_ret = fcntl(fd, F_GETFL); /* fprintf(stderr, "LIBBURN_DEBUG: burn_drive__is_rdwr: getfl_ret = %lX , O_RDWR = %lX , & = %lX , O_RDONLY = %lX\n", (unsigned long) getfl_ret, (unsigned long) O_RDWR, (unsigned long) (getfl_ret & O_RDWR), (unsigned long) O_RDONLY); */ mask = O_RDWR | O_WRONLY | O_RDONLY; if (getfl_ret == -1 || (getfl_ret & mask) != O_RDWR) is_rdwr = 0; if ((flag & 1) && getfl_ret != -1 && (getfl_ret & mask) == O_RDONLY) is_rdwr = 2; if ((flag & 2) && getfl_ret != -1 && (getfl_ret & mask) == O_WRONLY) is_rdwr = 3; } if (stat_ret != NULL) *stat_ret = st_ret; if (stbuf_ret != NULL) memcpy(stbuf_ret, &stbuf, sizeof(struct stat)); if (read_size_ret != NULL) *read_size_ret = read_size; return is_rdwr; } /* flag bit0= ( not needed yet: grab even if it is already grabbed ) */ int burn_drive_grab_stdio(struct burn_drive *d, int flag) { int stat_ret = -1, is_rdwr, ret; struct stat stbuf; off_t read_size= 0, size= 0; char fd_name[40], *name_pt = NULL; if(d->stdio_fd >= 0) { sprintf(fd_name, "/dev/fd/%d", d->stdio_fd); name_pt = fd_name; } else if (d->devname[0]) { name_pt = d->devname; } if (name_pt != NULL) { /* re-assess d->media_read_capacity and free space */ is_rdwr = burn_drive__is_rdwr(name_pt, &stat_ret, &stbuf, &read_size, 1 | 2); /* despite its name : last valid address, not size */ d->media_read_capacity = read_size / 2048 - !(read_size % 2048); d->mr_capacity_trusted = 1; if ((stat_ret == -1 || is_rdwr) && d->devname[0]) { ret = burn_os_stdio_capacity(d->devname, 0, &size); if (ret > 0) burn_drive_set_media_capacity_remaining(d, size); } } d->released = 0; d->current_profile = 0xffff; if(d->drive_role == 2 || d->drive_role == 3) { d->status = BURN_DISC_BLANK; } else if(d->drive_role == 4) { if (d->media_read_capacity > 0) d->status = BURN_DISC_FULL; else d->status = BURN_DISC_EMPTY; } else if(d->drive_role == 5) { if (stat_ret != -1 && S_ISREG(stbuf.st_mode) && stbuf.st_size > 0) { d->status = BURN_DISC_APPENDABLE; if (stbuf.st_size >= BURN_DRIVE_MAX_BYTES) { d->status = BURN_DISC_FULL; d->role_5_nwa = (off_t) BURN_DRIVE_MAX_BYTES / (off_t) 2048; } else d->role_5_nwa = stbuf.st_size / 2048 + !!(stbuf.st_size % 2048); } else d->status = BURN_DISC_BLANK; } else { d->status = BURN_DISC_EMPTY; d->current_profile = 0; } d->busy = BURN_DRIVE_IDLE; return 1; } int burn_drive_grab(struct burn_drive *d, int le) { int errcode; /* ts A61125 - B20122 */ int ret, sose, signal_action_mem = -1; sose = d->silent_on_scsi_error; if (!d->released) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020189, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_LOW, "Drive is already grabbed by libburn", 0, 0); return 0; } if(d->drive_role != 1) { ret = burn_drive_grab_stdio(d, 0); return ret; } d->status = BURN_DISC_UNREADY; errcode = d->grab(d); if (errcode == 0) return 0; burn_grab_prepare_sig_action(&signal_action_mem, 0); d->busy = BURN_DRIVE_GRABBING; if (le) d->load(d); if (d->cancel || burn_is_aborting(0)) {ret = 0; goto ex;} d->lock(d); if (d->cancel || burn_is_aborting(0)) {ret = 0; goto ex;} /* ts A61118 */ d->start_unit(d); if (d->cancel || burn_is_aborting(0)) {ret = 0; goto ex;} /* ts A61202 : gave bit1 of le a meaning */ if (!le) d->silent_on_scsi_error = 1; /* ts A61125 : outsourced media state inquiry aspects */ ret = burn_drive_inquire_media(d); if (d->cancel || burn_is_aborting(0)) {ret = 0; goto ex;} burn_drive_send_default_page_05(d, 0); if (d->cancel || burn_is_aborting(0)) {ret = 0; goto ex;} ex:; if (d->cancel || burn_is_aborting(0)) { d->unlock(d); d->release(d); } d->silent_on_scsi_error = sose; d->busy = BURN_DRIVE_IDLE; burn_grab_restore_sig_action(signal_action_mem, 0); return ret; } /* ts A71015 */ #define Libburn_ticket_62_re_register_is_possiblE 1 struct burn_drive *burn_drive_register(struct burn_drive *d) { #ifdef Libburn_ticket_62_re_register_is_possiblE int i; #endif 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->thread_pid = 0; d->thread_pid_valid = 0; memset(&(d->thread_tid), 0, sizeof(d->thread_tid)); d->medium_state_changed = 0; d->set_streaming_exact_bit = 0; d->set_streaming_err = 0; d->toc_entries = 0; d->toc_entry = NULL; d->disc = NULL; d->erasable = 0; d->write_opts = NULL; #ifdef Libburn_ticket_62_re_register_is_possiblE /* ts A60904 : ticket 62, contribution by elmom */ /* Not yet accepted because no use case seen yet */ /* ts A71015 : xorriso dialog imposes a use case now */ /* This is supposed to find an already freed drive struct among all the the ones that have been used before */ for (i = 0; i < drivetop + 1; i++) if (drive_array[i].global_index == -1) break; d->global_index = i; memcpy(&drive_array[i], d, sizeof(struct burn_drive)); pthread_mutex_init(&drive_array[i].access_lock, NULL); if (drivetop < i) drivetop = i; return &(drive_array[i]); #else /* Libburn_ticket_62_re_register_is_possiblE */ /* old A60904 : */ /* Still active by default */ d->global_index = drivetop + 1; memcpy(&drive_array[drivetop + 1], d, sizeof(struct burn_drive)); pthread_mutex_init(&drive_array[drivetop + 1].access_lock, NULL); return &drive_array[++drivetop]; #endif /* ! Libburn_ticket_62_re_register_is_possiblE */ } /* unregister most recently registered drive */ int burn_drive_unregister(struct burn_drive *d) { if(d->global_index != drivetop) return 0; burn_drive_free(d); drivetop--; return 1; } /* ts A61021 : after-setup activities from sg.c:enumerate_common() */ struct burn_drive *burn_drive_finish_enum(struct burn_drive *d) { struct burn_drive *t = NULL; char *msg = NULL; int ret; BURN_ALLOC_MEM(msg, char, BURN_DRIVE_ADR_LEN + 160); d->drive_role = 1; /* MMC drive */ t = burn_drive_register(d); /* ts A60821 */ mmc_function_spy(NULL, "enumerate_common : -------- doing grab"); /* try to get the drive info */ ret = t->grab(t); if (ret) { t->getcaps(t); t->unlock(t); t->released = 1; } else { /* ts A90602 */ d->mdata->p2a_valid = -1; sprintf(msg, "Unable to grab scanned drive %s", d->devname); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002016f, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_LOW, msg, 0, 0); burn_drive_unregister(t); t = NULL; } /* ts A60821 */ mmc_function_spy(NULL, "enumerate_common : ----- would release "); ex: BURN_FREE_MEM(msg); return t; } /* ts A61125 : model aspects of burn_drive_release */ /* @param flag bit3= do not close d->stdio_fd */ int burn_drive_mark_unready(struct burn_drive *d, int flag) { /* ts A61020 : mark media info as invalid */ d->start_lba= -2000000000; d->end_lba= -2000000000; /* ts A61202 */ d->current_profile = -1; d->current_has_feat21h = 0; d->current_feat2fh_byte4 = -1; d->status = BURN_DISC_UNREADY; if (d->toc_entry != NULL) free(d->toc_entry); d->toc_entry = NULL; d->toc_entries = 0; if (d->write_opts != NULL) { burn_write_opts_free(d->write_opts); d->write_opts = NULL; } if (d->disc != NULL) { burn_disc_free(d->disc); d->disc = NULL; } if (!(flag & 8)) { if (d->stdio_fd >= 0) close (d->stdio_fd); d->stdio_fd = -1; } return 1; } /* ts A70918 : outsourced from burn_drive_release() and enhanced */ /** @param flag bit0-2 = mode : 0=unlock , 1=unlock+eject , 2=leave locked bit3= do not call d->release() */ int burn_drive_release_fl(struct burn_drive *d, int flag) { if (d->released) { /* ts A61007 */ libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020105, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Drive is already released", 0, 0); return 0; } /* ts A61007 */ /* ts A60906: one should not assume BURN_DRIVE_IDLE == 0 */ /* a ssert(d->busy == BURN_DRIVE_IDLE); */ if (d->busy != BURN_DRIVE_IDLE) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020106, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Drive is busy on attempt to close", 0, 0); return 0; } if (d->drive_role == 1) { if (d->needs_sync_cache) d->sync_cache(d); if ((flag & 7) != 2) d->unlock(d); if ((flag & 7) == 1) d->eject(d); if (!(flag & 8)) { burn_drive_snooze(d, 0); d->release(d); } } d->needs_sync_cache = 0; /* just to be sure */ if (d->drive_serial_number != NULL) BURN_FREE_MEM(d->drive_serial_number); if (d->media_serial_number != NULL) BURN_FREE_MEM(d->media_serial_number); d->drive_serial_number = d->media_serial_number = NULL; d->drive_serial_number_len = d->media_serial_number_len = 0; d->released = 1; /* ts A61125 : outsourced model aspects */ burn_drive_mark_unready(d, flag & 8); return 1; } /* API */ /* ts A90824 @param flag bit0= wake up (else start snoozing) */ int burn_drive_snooze(struct burn_drive *d, int flag) { if (d->drive_role != 1) return 0; if (flag & 1) d->start_unit(d); else d->stop_unit(d); return 1; } /* API */ void burn_drive_release(struct burn_drive *d, int le) { burn_drive_release_fl(d, !!le); } /* ts B11002 */ /* API */ int burn_drive_re_assess(struct burn_drive *d, int flag) { int ret, signal_action_mem; if (d->released) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020108, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Drive is not grabbed on burn_drive_re_assess()", 0, 0); return 0; } burn_drive_release_fl(d, 2 | 8); if(d->drive_role != 1) { ret = burn_drive_grab_stdio(d, 0); return ret; } burn_grab_prepare_sig_action(&signal_action_mem, 0); d->busy = BURN_DRIVE_GRABBING; ret = burn_drive_inquire_media(d); burn_drive_send_default_page_05(d, 0); d->busy = BURN_DRIVE_IDLE; burn_grab_restore_sig_action(signal_action_mem, 0); d->released = 0; return ret; } /* ts A70918 */ /* API */ int burn_drive_leave_locked(struct burn_drive *d, int flag) { return burn_drive_release_fl(d, 2); } /* ts A61007 : former void burn_wait_all() */ /* @param flag bit0= demand freed drives (else released drives) */ int burn_drives_are_clear(int flag) { int i; for (i = burn_drive_count() - 1; i >= 0; --i) { /* ts A60904 : ticket 62, contribution by elmom */ if (drive_array[i].global_index == -1) continue; if (drive_array[i].released && !(flag & 1)) continue; return 0; } return 1; } #if 0 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) { /* ts A60904 : ticket 62, contribution by elmom */ if (d->global_index==-1) continue; a ssert(d->released); } if (!finished) sleep(1); } } #endif void burn_disc_erase_sync(struct burn_drive *d, int fast) { int ret, was_error = 0; if (d->drive_role == 5) { /* Random access write-only drive */ ret = truncate(d->devname, (off_t) 0); if (ret == -1) { libdax_msgs_submit(libdax_messenger, -1, 0x00020182, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Cannot truncate disk file for pseudo blanking", 0, 0); return; } d->role_5_nwa = 0; d->cancel = 0; d->status = BURN_DISC_BLANK; d->busy = BURN_DRIVE_IDLE; d->progress.sector = 0x10000; return; } d->cancel = 0; #ifdef Libburn_reset_progress_asynC /* <<< This is now done in async.c */ /* 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; #endif /* Libburn_reset_progress_asynC */ d->medium_state_changed = 1; d->erase(d, fast); d->busy = BURN_DRIVE_ERASING; #ifdef Libburn_old_progress_looP /* read the initial 0 stage */ while (!d->test_unit_ready(d) && d->get_erase_progress(d) == 0) sleep(1); while ((d->progress.sector = d->get_erase_progress(d)) > 0 || !d->test_unit_ready(d)) sleep(1); #else /* Libburn_old_progress_looP */ while (1) { /* >>> ??? ts B60730 : abort if user interrupts ? if (d->cancel) break; */ ret = d->get_erase_progress(d); if (ret == -2 || ret > 0) break; if (ret == -3) was_error = 1; sleep(1); } while (1) { /* >>> ??? ts B60730 : abort if user interrupts ? if (d->cancel) break; */ ret = d->get_erase_progress(d); if(ret == -2) break; if (ret == -3) was_error = 1; if (ret >= 0) d->progress.sector = ret; sleep(1); } #endif /* ! Libburn_old_progress_looP */ d->progress.sector = 0x10000; /* ts A61125 : update media state records */ burn_drive_mark_unready(d, 0); if (d->drive_role == 1 && !d->cancel) burn_drive_inquire_media(d); d->busy = BURN_DRIVE_IDLE; if (was_error) d->cancel = 1; } /* @param flag: bit0 = fill formatted size with zeros bit1, bit2 , bit4, bit5, bit7 - bit15 are for d->format_unit() */ void burn_disc_format_sync(struct burn_drive *d, off_t size, int flag) { int ret, buf_secs, err, i, stages = 1, pbase, pfill, pseudo_sector; int was_error = 0; off_t num_bufs; char msg[80]; struct buffer *buf = NULL, *buf_mem = d->buffer; BURN_ALLOC_MEM(buf, struct buffer, 1); #ifdef Libburn_reset_progress_asynC /* <<< This is now done in async.c */ /* 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; #endif /* Libburn_reset_progress_asynC */ stages = 1 + ((flag & 1) && size > 1024 * 1024); d->cancel = 0; d->busy = BURN_DRIVE_FORMATTING; d->medium_state_changed = 1; ret = d->format_unit(d, size, flag & 0xfff6); /* forward bits */ if (ret <= 0) d->cancel = 1; #ifdef Libburn_old_progress_looP while (!d->test_unit_ready(d) && d->get_erase_progress(d) == 0) sleep(1); while ((pseudo_sector = d->get_erase_progress(d)) > 0 || !d->test_unit_ready(d)) { d->progress.sector = pseudo_sector / stages; sleep(1); } #else /* Libburn_old_progress_looP */ while (1) { ret = d->get_erase_progress(d); if (ret == -2 || ret > 0) break; if (ret == -3) was_error = 1; sleep(1); } while (1) { pseudo_sector = d->get_erase_progress(d); if(pseudo_sector == -2) break; if (pseudo_sector == -3) was_error = 1; if (pseudo_sector >= 0) d->progress.sector = pseudo_sector / stages; sleep(1); } #endif /* ! Libburn_old_progress_looP */ d->sync_cache(d); if (size <= 0) goto ex; /* update media state records */ burn_drive_mark_unready(d, 0); burn_drive_inquire_media(d); if (flag & 1) { /* write size in zeros */; pbase = 0x8000 + 0x7fff * (stages == 1); pfill = 0xffff - pbase; buf_secs = 16; /* Must not be more than 16 */ num_bufs = size / buf_secs / 2048; if (num_bufs > 0x7fffffff) { d->cancel = 1; goto ex; } /* <<< */ sprintf(msg, "Writing %.f sectors of zeros to formatted media", (double) num_bufs * (double) buf_secs); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); d->buffer = buf; memset(d->buffer, 0, sizeof(struct buffer)); d->buffer->bytes = buf_secs * 2048; d->buffer->sectors = buf_secs; d->busy = BURN_DRIVE_WRITING; for (i = 0; i < num_bufs; i++) { d->nwa = i * buf_secs; err = d->write(d, d->nwa, d->buffer); if (err == BE_CANCELLED || d->cancel) { d->cancel = 1; break; } d->progress.sector = pbase + pfill * ((double) i / (double) num_bufs); } d->sync_cache(d); if (d->current_profile == 0x13 || d->current_profile == 0x1a) { /* DVD-RW or DVD+RW */ d->busy = BURN_DRIVE_CLOSING_SESSION; /* CLOSE SESSION, 010b */ d->close_track_session(d, 1, 0); d->busy = BURN_DRIVE_WRITING; } } ex:; d->progress.sector = 0x10000; d->busy = BURN_DRIVE_IDLE; d->buffer = buf_mem; if (was_error) d->cancel = 1; BURN_FREE_MEM(buf); } /* ts A70112 API */ int burn_disc_get_formats(struct burn_drive *d, int *status, off_t *size, unsigned *bl_sas, int *num_formats) { int ret; *status = 0; *size = 0; *bl_sas = 0; *num_formats = 0; if (d->drive_role != 1) return 0; ret = d->read_format_capacities(d, 0x00); if (ret <= 0) return 0; *status = d->format_descr_type; *size = d->format_curr_max_size; *bl_sas = d->format_curr_blsas; *num_formats = d->num_format_descr; return 1; } /* ts A70112 API */ int burn_disc_get_format_descr(struct burn_drive *d, int index, int *type, off_t *size, unsigned *tdp) { *type = 0; *size = 0; *tdp = 0; if (index < 0 || index >= d->num_format_descr) return 0; *type = d->format_descriptors[index].type; *size = d->format_descriptors[index].size; *tdp = d->format_descriptors[index].tdp; return 1; } enum burn_disc_status burn_disc_get_status(struct burn_drive *d) { /* ts A61007 */ /* a ssert(!d->released); */ if (d->released) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020108, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Drive is not grabbed on disc status inquiry", 0, 0); return BURN_DISC_UNGRABBED; } return d->status; } int burn_disc_erasable(struct burn_drive *d) { return d->erasable; } void burn_drive_get_status_sig_handling(void) { /* --- Part of asynchronous signal handling --- */ /* The frequently used call burn_drive_get_status*() may be used to react on messages from the libburn built-in signal handler. */ /* ts B00225 : If aborting with action 2: catch control thread after it returned from signal handler. Let it run burn_abort(4440,...) */ burn_init_catch_on_abort(0); /* ts A70928 : inform control thread of signal in sub-threads */ if (burn_builtin_triggered_action < 2 && burn_global_abort_level > 0) burn_global_abort_level++; if (burn_builtin_triggered_action < 2 && burn_global_abort_level > 5) { if (burn_global_signal_handler == NULL) kill(getpid(), burn_global_abort_signum); else (*burn_global_signal_handler) (burn_global_signal_handle, burn_global_abort_signum, 0); burn_global_abort_level = -1; } /* --- End of asynchronous signal handling --- */ } enum burn_drive_status burn_drive_get_status(struct burn_drive *d, struct burn_progress *p) { burn_drive_get_status_sig_handling(); if (p != NULL) { /* TODO: add mutex */ p->sessions = d->progress.sessions; p->session = d->progress.session; p->tracks = d->progress.tracks; p->track = d->progress.track; p->indices = d->progress.indices; p->index = d->progress.index; if(d->progress.start_sector < 0x80000000) p->start_sector = d->progress.start_sector; else p->start_sector = 0x7fffffff; if(d->progress.sectors < 0x80000000) p->sectors = d->progress.sectors; else p->sectors = 0x7fffffff; if(d->progress.sector < 0x80000000) p->sector = d->progress.sector; else p->sector = 0x7fffffff; if(d->progress.buffer_capacity < 0x100000000) p->buffer_capacity = d->progress.buffer_capacity; else p->buffer_capacity = 0xffffffff; if(d->progress.buffer_available < 0x100000000) p->buffer_available = d->progress.buffer_available; else p->buffer_available = 0xffffffff; p->buffered_bytes = d->progress.buffered_bytes; if(d->progress.buffer_min_fill < 0x100000000) p->buffer_min_fill = d->progress.buffer_min_fill; else p->buffer_min_fill = 0xffffffff; } return d->busy; } enum burn_drive_status burn_drive_get_status_v2(struct burn_drive *d, struct burn_progress_v2 *p) { burn_drive_get_status_sig_handling(); if (p != NULL) { /* TODO: add mutex */ memcpy(p, &(d->progress), sizeof(struct burn_progress_v2)); } return d->busy; } int burn_drive_set_stream_recording(struct burn_drive *d, int recmode, int start, int flag) { #ifndef Libburn_force_stream_recordinG struct burn_feature_descr *descr; #endif if (recmode == 1) { #ifdef Libburn_force_stream_recordinG d->do_stream_recording = 1; #else /* Libburn_force_stream_recordinG */ d->do_stream_recording = 0; if (burn_drive_has_feature(d, 0x107, &descr, 0)) { if ((descr->data[0] & 1) && (descr->flags & 1)) d->do_stream_recording = 1; } if (!d->do_stream_recording) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x000201ac, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, "Drive currently does not offer Stream Recording", 0, 0); } else if (d->current_profile != 0x12 && d->current_profile != 0x41 && d->current_profile != 0x43) { d->do_stream_recording = 0; libdax_msgs_submit(libdax_messenger, d->global_index, 0x000201ad, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, "Stream Recording suppressed due to medium type", 0, 0); } #endif /* ! Libburn_force_stream_recordinG */ } else if (recmode == -1) { d->do_stream_recording = 0; } if (d->do_stream_recording) d->stream_recording_start = start; else d->stream_recording_start = 0; return(1); } void burn_drive_cancel(struct burn_drive *d) { /* ts B00225 : these mutexes are unnecessary because "= 1" is atomar. pthread_mutex_lock(&d->access_lock); */ if (!d->cancel) { libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, "burn_drive_cancel() was called", 0, 0); } d->cancel = 1; /* pthread_mutex_unlock(&d->access_lock); */ } static void strip_spaces(char *str, size_t len) { char *tmp, *tmp2; /* Remove trailing blanks */ for (tmp = str + len - 1; tmp >= str && (isspace(*tmp) || !*tmp); tmp--) *tmp = 0; /* Condense remaining blank intervals to single blanks */ for (tmp = str; tmp < str + len - 1 && *tmp; tmp++) { if (isspace(*tmp) && isspace(*(tmp + 1))) { for (tmp2 = tmp + 1; tmp2 < str + len && *tmp2; tmp2++) *(tmp2 - 1) = *tmp2; *(tmp2 - 1) = '\0'; tmp--; /* try same first blank again */ } } } static int drive_getcaps(struct burn_drive *d, struct burn_drive_info *out) { struct burn_scsi_inquiry_data *id; int i, profile; struct burn_feature_descr *feat; /* ts A61007 : now prevented in enumerate_common() */ #if 0 a ssert(d->idata); a ssert(d->mdata); #endif if(d->idata->valid <= 0) return 0; id = (struct burn_scsi_inquiry_data *)d->idata; memcpy(out->vendor, id->vendor, sizeof(id->vendor)); strip_spaces(out->vendor, sizeof(id->vendor)); memcpy(out->product, id->product, sizeof(id->product)); strip_spaces(out->product, sizeof(id->product)); memcpy(out->revision, id->revision, sizeof(id->revision)); strip_spaces(out->revision, sizeof(id->revision)); strncpy(out->location, d->devname, 16); out->location[16] = '\0'; if (d->mdata->p2a_valid > 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; } else { out->buffer_size = out->read_dvdram = out->read_dvdr = 0; out->read_dvdrom = out->read_cdr = out->read_cdrw = 0; out->write_dvdram = out->write_dvdr = out->write_cdr = 0; out->write_cdrw = out->write_simulate = out->c2_errors = 0; for (i = 0; i < d->num_profiles; i++) { profile = (d->all_profiles[i * 4] << 8) | d->all_profiles[i * 4 + 1]; if (profile == 0x09) out->write_cdr = out->read_cdr = 1; else if (profile == 0x0a) out->write_cdrw = out->read_cdrw = 1; else if (profile == 0x10) out->read_dvdrom = 1; else if (profile == 0x11) out->write_dvdr = out->read_dvdr = 1; else if (profile == 0x12) out->write_dvdram = out->read_dvdram = 1; } /* Test Write bit of CD TAO, CD Mastering, DVD-R/-RW Write */ for (i = 0x002D; i <= 0x002F; i++) if (burn_drive_has_feature(d, i, &feat, 0)) if (feat->data_lenght > 0) out->write_simulate |= !!(feat->data[0] & 4); } out->drive = d; #ifdef Libburn_dummy_probe_write_modeS /* ts A91112 */ /* Set default block types. The call d->probe_write_modes() is quite obtrusive. It may be performed explicitly by new API call burn_drive_probe_cd_write_modes(). */ if (out->write_dvdram || out->write_dvdr || out->write_cdrw || out->write_cdr) { out->tao_block_types = d->block_types[BURN_WRITE_TAO] = BURN_BLOCK_MODE1 | BURN_BLOCK_RAW0; out->sao_block_types = d->block_types[BURN_WRITE_SAO] = BURN_BLOCK_SAO; } else { out->tao_block_types = d->block_types[BURN_WRITE_TAO] = 0; out->sao_block_types = d->block_types[BURN_WRITE_SAO] = 0; } out->raw_block_types = d->block_types[BURN_WRITE_RAW] = 0; out->packet_block_types = 0; #else /* Libburn_dummy_probe_write_modeS */ /* 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]; #endif /* ! Libburn_dummy_probe_write_modeS */ return 1; } /* ts A91112 - B00114 API */ /* Probe available CD write modes and block types. */ int burn_drive_probe_cd_write_modes(struct burn_drive_info *dinfo) { struct burn_drive *d = dinfo->drive; if (d == NULL) return 0; if (dinfo->write_dvdram || dinfo->write_dvdr || dinfo->write_cdrw || dinfo->write_cdr) d->probe_write_modes(d); dinfo->tao_block_types = d->block_types[BURN_WRITE_TAO]; dinfo->sao_block_types = d->block_types[BURN_WRITE_SAO]; dinfo->raw_block_types = d->block_types[BURN_WRITE_RAW]; dinfo->packet_block_types = d->block_types[BURN_WRITE_PACKET]; return 1; } /* ts A70907 : added parameter flag */ /* @param flag bit0= reset global drive list */ int burn_drive_scan_sync(struct burn_drive_info *drives[], unsigned int *n_drives, int flag) { /* ts A70907 : There seems to have been a misunderstanding about the role of burn_drive_scan_sync(). It needs no static state because it is only started once during an asynchronous scan operation. Its starter, burn_drive_scan(), is the one which ends immediately and gets called repeatedly. It acts on start of scanning by calling burn_drive_scan_sync(), returns idle while scanning is not done and finally removes the worker object which represented burn_drive_scan_sync(). The scanning itself is not parallel but enumerates sequentially drive by drive (within scsi_enumerate_drives()). I will use "scanned" for marking drives found by previous runs. It will not be static any more. */ /* ts A71015 : this makes only trouble : static int scanning = 0; */ /* ts A70907 : These variables are too small anyway. We got up to 255 drives. static int scanned = 0, found = 0; Variable "found" was only set but never read. */ unsigned char scanned[32]; unsigned count = 0; int i, ret; /* ts A61007 : moved up to burn_drive_scan() */ /* a ssert(burn_running); */ /* ts A61007 : test moved up to burn_drive_scan() burn_wait_all() is obsoleted */ #if 0 /* make sure the drives aren't in use */ burn_wait_all(); /* make sure the queue cleans up before checking for the released state */ #endif /* 0 */ *n_drives = 0; /* ts A70907 : whether to scan from scratch or to extend */ for (i = 0; i < (int) sizeof(scanned); i++) scanned[i] = 0; if (flag & 1) { burn_drive_free_all(); } else { for (i = 0; i <= drivetop; i++) if (drive_array[i].global_index >= 0) scanned[i / 8] |= (1 << (i % 8)); } /* refresh the lib's drives */ /* ts A61115 : formerly sg_enumerate(); ata_enumerate(); */ scsi_enumerate_drives(); count = burn_drive_count(); if (count) { /* ts A70907 : Extra array element marks end of array. */ *drives = calloc(count + 1, sizeof(struct burn_drive_info)); if (*drives == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00000003, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Out of virtual memory", 0, 0); return -1; } else for (i = 0; i <= (int) count; i++) /* invalidate */ (*drives)[i].drive = NULL; } else *drives = NULL; for (i = 0; i < (int) count; ++i) { if (scanned[i / 8] & (1 << (i % 8))) continue; /* device already scanned by previous run */ if (drive_array[i].global_index < 0) continue; /* invalid device */ /* ts A90602 : This old loop is not plausible. See A70907. while (!drive_getcaps(&drive_array[i], &(*drives)[*n_drives])) { sleep(1); } */ /* ts A90602 : A single call shall do (rather than a loop) */ ret = drive_getcaps(&drive_array[i], &(*drives)[*n_drives]); if (ret > 0) (*n_drives)++; scanned[i / 8] |= 1 << (i % 8); } if (*drives != NULL && *n_drives == 0) { free ((char *) *drives); *drives = NULL; } return(1); } /* ts A61001 : internal call */ int burn_drive_forget(struct burn_drive *d, int force) { int occup; occup = burn_drive_is_occupied(d); /* fprintf(stderr, "libburn: experimental: occup == %d\n",occup); */ if(occup <= -2) return 2; if(occup > 0) if(force < 1) return 0; if(occup >= 10) return 0; /* >>> do any drive calming here */; burn_drive_force_idle(d); if(occup > 0 && !burn_drive_is_released(d)) burn_drive_release(d,0); burn_drive_free(d); return 1; } /* API call */ int burn_drive_info_forget(struct burn_drive_info *info, int force) { return burn_drive_forget(info->drive, force); } void burn_drive_info_free(struct burn_drive_info drive_infos[]) { #ifndef Libburn_free_all_drives_on_infO int i; #endif /* ts A60904 : ticket 62, contribution by elmom */ /* clarifying the meaning and the identity of the victim */ if(drive_infos == NULL) return; #ifndef Libburn_free_all_drives_on_infO #ifdef Not_yeT int new_drivetop; /* ts A71015: compute reduced drivetop counter */ new_drivetop = drivetop; for (i = 0; drive_infos[i].drive != NULL; i++) if (drive_infos[i].global_index == new_drivetop && new_drivetop >= 0) { new_drivetop--; i = 0; } #endif /* Not_yeT */ /* ts A70907 : Solution for wrong behavior below */ for (i = 0; drive_infos[i].drive != NULL; i++) burn_drive_free(drive_infos[i].drive); #ifdef Not_yeT drivetop = new_drivetop; #endif /* Not_yeT */ #endif /* ! Libburn_free_all_drives_on_infO */ /* ts A60904 : This looks a bit weird. [ts A70907 : not any more] burn_drive_info is not the manager of burn_drive but only its spokesperson. To my knowledge drive_infos from burn_drive_scan() are not memorized globally. */ free((void *) drive_infos); #ifdef Libburn_free_all_drives_on_infO /* ts A70903 : THIS IS WRONG ! (disabled now) It endangers multi drive usage. This call is not entitled to delete all drives, only the ones of the array which it receives a parameter. Problem: It was unclear how many items are listed in drive_infos Solution: Added a end marker element to any burn_drive_info array The mark can be recognized by having drive == NULL */ burn_drive_free_all(); #endif } struct burn_disc *burn_drive_get_disc(struct burn_drive *d) { /* ts A61022: SIGSEGV on calling this function with blank media */ if(d->disc == NULL) return NULL; d->disc->refcnt++; return d->disc; } void burn_drive_set_speed(struct burn_drive *d, int r, int w) { d->nominal_write_speed = w; if(d->drive_role != 1) return; d->set_speed(d, r, w); } int burn_drive_set_speed_exact(struct burn_drive *d, int r, int w) { int sose; d->nominal_write_speed = w; if(d->drive_role != 1) return 0; sose = d->silent_on_scsi_error; d->silent_on_scsi_error = 3; d->set_streaming_err = 0; d->set_streaming_exact_bit = 1; d->set_speed(d, r, w); d->silent_on_scsi_error = sose; d->set_streaming_exact_bit = 0; return !d->set_streaming_err; } /* ts A70711 API function */ int burn_drive_set_buffer_waiting(struct burn_drive *d, int enable, int min_usec, int max_usec, int timeout_sec, int min_percent, int max_percent) { if (enable >= 0) d->wait_for_buffer_free = !!enable; if (min_usec >= 0) d->wfb_min_usec = min_usec; if (max_usec >= 0) d->wfb_max_usec = max_usec; if (timeout_sec >= 0) d->wfb_timeout_sec = timeout_sec; if (min_percent >= 0) { if (min_percent < 25 || min_percent > 100) return 0; d->wfb_min_percent = min_percent; } if (max_percent >= 0) { if (max_percent < 25 || max_percent > 100) return 0; d->wfb_max_percent = max_percent; } return 1; } int burn_drive_reset_simulate(struct burn_drive *d, int simulate) { if (d->busy != BURN_DRIVE_IDLE) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020140, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is busy on attempt to write random access",0,0); return 0; } d->do_simulate = !!simulate; return 1; } 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; } /* ts A61021 : New API function */ int burn_drive_get_min_write_speed(struct burn_drive *d) { return d->mdata->min_write_speed; } /* ts A51221 */ static char *enumeration_whitelist[BURN_DRIVE_WHITELIST_LEN]; static int enumeration_whitelist_top = -1; /** Add a device to the list of permissible drives. As soon as some entry is in the whitelist all non-listed drives are banned from enumeration. @return 1 success, <=0 failure */ int burn_drive_add_whitelist(char *device_address) { char *new_item; if(enumeration_whitelist_top+1 >= BURN_DRIVE_WHITELIST_LEN) return 0; enumeration_whitelist_top++; new_item = calloc(1, strlen(device_address) + 1); if (new_item == NULL) return -1; strcpy(new_item, device_address); enumeration_whitelist[enumeration_whitelist_top] = new_item; return 1; } /** Remove all drives from whitelist. This enables all possible drives. */ void burn_drive_clear_whitelist(void) { int i; for (i = 0; i <= enumeration_whitelist_top; i++) free(enumeration_whitelist[i]); enumeration_whitelist_top = -1; } int burn_drive_is_banned(char *device_address) { int i; if(enumeration_whitelist_top<0) return 0; for (i = 0; i <= enumeration_whitelist_top; i++) if (strcmp(enumeration_whitelist[i], device_address) == 0) return 0; return 1; } /* ts A80731 */ int burn_drive_whitelist_count(void) { return enumeration_whitelist_top + 1; } char *burn_drive_whitelist_item(int idx, int flag) { if (idx < 0 || idx > enumeration_whitelist_top) return NULL; return enumeration_whitelist[idx]; } static int burn_role_by_access(char *fname, int flag) { /* We normally need _LARGEFILE64_SOURCE defined by the build system. Nevertheless the system might use large address integers by default. */ #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif int fd; fd = open(fname, O_RDWR | O_LARGEFILE | O_BINARY); if (fd != -1) { close(fd); return 2; } fd = open(fname, O_RDONLY | O_LARGEFILE | O_BINARY); if (fd != -1) { close(fd); return 4; } fd = open(fname, O_WRONLY | O_LARGEFILE | O_BINARY); if (fd != -1) { close(fd); return 5; } if (flag & 1) return 0; return 2; } /* ts A70903 : Implements adquiration of pseudo drives */ int burn_drive_grab_dummy(struct burn_drive_info *drive_infos[], char *fname) { int ret = -1, role = 0, fd; int is_rdwr = 0, stat_ret = -1; off_t size = BURN_DRIVE_MAX_BYTES; off_t read_size = -1; struct burn_drive *d= NULL, *regd_d; struct stat stbuf; if (fname[0] != 0) { fd = burn_drive__fd_from_special_adr(fname); is_rdwr = burn_drive__is_rdwr(fname, &stat_ret, &stbuf, &read_size, 1 | 2); if (stat_ret == -1 || is_rdwr) { ret = burn_os_stdio_capacity(fname, 0, &size); if (ret == -1) { libdax_msgs_submit(libdax_messenger, -1, 0x00020009, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Neither stdio-path nor its directory exist", 0, 0); return 0; } else if (ret == -2) { libdax_msgs_submit(libdax_messenger, -1, 0x00020005, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Failed to open device (a pseudo-drive)", errno, 0); return 0; } if (fname[0] != 0) { if (is_rdwr == 2 && (burn_drive_role_4_allowed & 1)) role = 4; else if (is_rdwr == 3 && (burn_drive_role_4_allowed & 1)) role = 5; else role = 2; if (stat_ret != -1 && role == 2 && fd == -1 && (burn_drive_role_4_allowed & 3) == 3) role = burn_role_by_access(fname, !!(burn_drive_role_4_allowed & 4)); } else role = 0; } else { role = 3; } } d= (struct burn_drive *) calloc(1, sizeof(struct burn_drive)); if (d == NULL) return 0; burn_setup_drive(d, fname); d->status = BURN_DISC_EMPTY; d->drive_role = role; ret = burn_scsi_setup_drive(d, -1, -1, -1, -1, -1, 0); if (ret <= 0) goto ex; regd_d = burn_drive_register(d); if (regd_d == NULL) { ret = -1; goto ex; } free((char *) d); /* all sub pointers have been copied to *regd_d */ d = regd_d; if (d->drive_role >= 2 && d->drive_role <= 5) { if (d->drive_role == 4) { if (read_size > 0) d->status = BURN_DISC_FULL; else d->status = BURN_DISC_EMPTY; d->block_types[BURN_WRITE_TAO] = 0; d->block_types[BURN_WRITE_SAO] = 0; } else { if (d->drive_role == 5 && stat_ret != -1 && S_ISREG(stbuf.st_mode) && stbuf.st_size > 0 && (burn_drive_role_4_allowed & 8)) { d->status = BURN_DISC_APPENDABLE; d->block_types[BURN_WRITE_SAO] = 0; if (stbuf.st_size > BURN_DRIVE_MAX_BYTES) { d->status = BURN_DISC_FULL; d->role_5_nwa = (off_t) BURN_DRIVE_MAX_BYTES / (off_t) 2048; ; } else d->role_5_nwa = stbuf.st_size / 2048 + !!(stbuf.st_size % 2048); } else { d->status = BURN_DISC_BLANK; d->block_types[BURN_WRITE_SAO] = BURN_BLOCK_SAO; d->role_5_nwa = 0; } d->block_types[BURN_WRITE_TAO] = BURN_BLOCK_MODE1; } d->current_profile = 0xffff; /* MMC for non-compliant drive */ strcpy(d->current_profile_text,"stdio file"); d->current_is_cd_profile = 0; d->current_is_supported_profile = 1; if (read_size >= 0) { /* despite its name : last valid address, not size */ d->media_read_capacity = read_size / 2048 - !(read_size % 2048); d->mr_capacity_trusted = 1; } burn_drive_set_media_capacity_remaining(d, size); } else d->current_profile = 0; /* Drives return this if empty */ *drive_infos = calloc(2, sizeof(struct burn_drive_info)); if (*drive_infos == NULL) goto ex; (*drive_infos)[0].drive = d; (*drive_infos)[1].drive = NULL; /* End-Of-List mark */ (*drive_infos)[0].tao_block_types = d->block_types[BURN_WRITE_TAO]; (*drive_infos)[0].sao_block_types = d->block_types[BURN_WRITE_SAO]; if (d->drive_role == 2) { strcpy((*drive_infos)[0].vendor,"YOYODYNE"); strcpy((*drive_infos)[0].product,"WARP DRIVE"); strcpy((*drive_infos)[0].revision,"FX01"); } else if (d->drive_role == 3) { strcpy((*drive_infos)[0].vendor,"YOYODYNE"); strcpy((*drive_infos)[0].product,"BLACKHOLE"); strcpy((*drive_infos)[0].revision,"FX02"); } else if (d->drive_role == 4) { strcpy((*drive_infos)[0].vendor,"YOYODYNE"); strcpy((*drive_infos)[0].product,"WARP DRIVE"); strcpy((*drive_infos)[0].revision,"FX03"); } else if (d->drive_role == 5) { strcpy((*drive_infos)[0].vendor,"YOYODYNE"); strcpy((*drive_infos)[0].product,"WARP DRIVE"); strcpy((*drive_infos)[0].revision,"FX04"); } else { strcpy((*drive_infos)[0].vendor,"FERENGI"); strcpy((*drive_infos)[0].product,"VAPORWARE"); strcpy((*drive_infos)[0].revision,"0000"); } d->released = 0; ret = 1; ex:; if (ret <= 0 && d != NULL) { burn_drive_free_subs(d); free((char *) d); } return ret; } /* ts A60823 */ /** Acquire a drive with known persistent address. */ int burn_drive_scan_and_grab(struct burn_drive_info *drive_infos[], char* adr, int load) { unsigned int n_drives; int ret, i; /* check whether drive address is already registered */ for (i = 0; i <= drivetop; i++) if (drive_array[i].global_index >= 0) if (strcmp(drive_array[i].devname, adr) == 0) break; if (i <= drivetop) { libdax_msgs_submit(libdax_messenger, i, 0x0002014b, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Drive is already registered resp. scanned", 0, 0); return -1; } if (strncmp(adr, "stdio:", 6) == 0) { ret = burn_drive_grab_dummy(drive_infos, adr + 6); return ret; } burn_drive_clear_whitelist(); burn_drive_add_whitelist(adr); /* fprintf(stderr,"libburn: experimental: burn_drive_scan_and_grab(%s)\n", adr); */ /* ts A70907 : now calling synchronously rather than looping */ ret = burn_drive_scan_sync(drive_infos, &n_drives, 0); if (ret < 0) return -1; if (n_drives == 0) return 0; /* fprintf(stderr, "libburn: experimental: n_drives %d , drivetop %d\n", n_drives, drivetop); if (n_drives > 0) fprintf(stderr, "libburn: experimental: global_index %d\n", drive_infos[0]->drive->global_index); */ ret = burn_drive_grab(drive_infos[0]->drive, load); if (ret != 1) { burn_drive_forget(drive_infos[0]->drive, 0); return -1; } return 1; } /* ts A60925 */ /** Simple debug message frontend to libdax_msgs_submit(). If arg is not NULL, then fmt MUST contain exactly one %s and no other sprintf() %-formatters. */ int burn_drive_adr_debug_msg(char *fmt, char *arg) { int ret; char *msg = NULL, *msgpt; if(libdax_messenger == NULL) return 0; if(arg != NULL) { BURN_ALLOC_MEM(msg, char, 4096); msgpt = msg; sprintf(msg, fmt, arg); } else { msgpt = fmt; } ret = libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msgpt, 0, 0); ex:; BURN_FREE_MEM(msg); return ret; } /* ts A60923 */ /* ts A70906 : promoted to API */ /** Inquire the persistent address of the given drive. */ int burn_drive_d_get_adr(struct burn_drive *d, char adr[]) { if (strlen(d->devname) >= BURN_DRIVE_ADR_LEN) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020110, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Persistent drive address too long", 0, 0); return -1; } strcpy(adr,d->devname); return 1; } /* ts A60823 - A60923 */ /* A70906 : Now legacy API call */ /** Inquire the persistent address of the given drive. */ int burn_drive_get_adr(struct burn_drive_info *drive_info, char adr[]) { int ret; ret = burn_drive_d_get_adr(drive_info->drive, adr); return ret; } /* ts A60922 ticket 33 */ /** Evaluate whether the given address would be enumerated by libburn */ int burn_drive_is_enumerable_adr(char *adr) { return sg_is_enumerable_adr(adr); } #define BURN_DRIVE_MAX_LINK_DEPTH 20 /* ts A60922 ticket 33 */ /* @param flag bit0= no debug messages bit1= resolve only links, do not rely on drive list for resolving via st_rdev */ int burn_drive_resolve_link(char *path, char adr[], int *recursion_count, int flag) { int ret, link_target_size = 4096; char *link_target = NULL, *msg = NULL, *link_adr = NULL, *adrpt; struct stat stbuf; BURN_ALLOC_MEM(link_target, char, link_target_size); BURN_ALLOC_MEM(msg, char, link_target_size + 100); BURN_ALLOC_MEM(link_adr, char, link_target_size); if (flag & 1) burn_drive_adr_debug_msg("burn_drive_resolve_link( %s )", path); if (*recursion_count >= BURN_DRIVE_MAX_LINK_DEPTH) { if (flag & 1) burn_drive_adr_debug_msg( "burn_drive_resolve_link aborts because link too deep", NULL); {ret = 0; goto ex;} } (*recursion_count)++; ret = readlink(path, link_target, link_target_size); if (ret == -1) { if (flag & 1) burn_drive_adr_debug_msg("readlink( %s ) returns -1", path); {ret = 0; goto ex;} } if (ret >= link_target_size - 1) { sprintf(msg,"readlink( %s ) returns %d (too much)", path, ret); if (flag & 1) burn_drive_adr_debug_msg(msg, NULL); {ret = -1; goto ex;} } link_target[ret] = 0; adrpt = link_target; if (link_target[0] != '/') { strcpy(link_adr, path); if ((adrpt = strrchr(link_adr, '/')) != NULL) { strcpy(adrpt + 1, link_target); adrpt = link_adr; } else adrpt = link_target; } if (flag & 2) { /* Link-only recursion */ if (lstat(adrpt, &stbuf) == -1) { ; } else if((stbuf.st_mode & S_IFMT) == S_IFLNK) { ret = burn_drive_resolve_link(adrpt, adr, recursion_count, flag); } else { strcpy(adr, adrpt); } } else { /* Link and device number recursion */ ret = burn_drive_convert_fs_adr_sub(adrpt, adr, recursion_count); sprintf(msg,"burn_drive_convert_fs_adr( %s ) returns %d", link_target, ret); } if (flag & 1) burn_drive_adr_debug_msg(msg, NULL); ex:; BURN_FREE_MEM(link_target); BURN_FREE_MEM(msg); BURN_FREE_MEM(link_adr); return ret; } /* ts A60922 - A61014 ticket 33 */ /* Try to find an enumerated address with the given stat.st_rdev number */ int burn_drive_find_devno(dev_t devno, char adr[]) { char *fname = NULL, *msg = NULL; int ret = 0, first = 1, fname_size = 4096; struct stat stbuf; burn_drive_enumerator_t enm; BURN_ALLOC_MEM(fname, char, fname_size); BURN_ALLOC_MEM(msg, char, fname_size + 100); while (1) { ret = sg_give_next_adr(&enm, fname, fname_size, first); if(ret <= 0) break; first = 0; ret = stat(fname, &stbuf); if(ret == -1) continue; if(devno != stbuf.st_rdev) continue; if(strlen(fname) >= BURN_DRIVE_ADR_LEN) {ret= -1; goto ex;} sprintf(msg, "burn_drive_find_devno( 0x%lX ) found %s", (long) devno, fname); burn_drive_adr_debug_msg(msg, NULL); strcpy(adr, fname); { ret = 1; goto ex;} } ret = 0; ex:; if (first == 0) sg_give_next_adr(&enm, fname, fname_size, -1); BURN_FREE_MEM(fname); BURN_FREE_MEM(msg); return ret; } /* ts A60923 */ /** Try to obtain host,channel,target,lun from path. @return 1 = success , 0 = failure , -1 = severe error */ int burn_drive_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no) { int ret, i; char *adr = NULL; BURN_ALLOC_MEM(adr, char, BURN_DRIVE_ADR_LEN); /* open drives cannot be inquired by sg_obtain_scsi_adr() */ for (i = 0; i < drivetop + 1; i++) { if (drive_array[i].global_index < 0) continue; ret = burn_drive_d_get_adr(&(drive_array[i]),adr); if (ret < 0) {ret = 1; goto ex;} if (ret == 0) continue; if (strcmp(adr, path) == 0) { *host_no = drive_array[i].host; *channel_no = drive_array[i].channel; *target_no = drive_array[i].id; *lun_no = drive_array[i].lun; *bus_no = drive_array[i].bus_no; if (*host_no < 0 || *channel_no < 0 || *target_no < 0 || *lun_no < 0) {ret = 0; goto ex;} {ret = 1; goto ex;} } } ret = sg_obtain_scsi_adr(path, bus_no, host_no, channel_no, target_no, lun_no); ex:; BURN_FREE_MEM(adr); return ret; } /* ts A60923 */ int burn_drive_convert_scsi_adr(int bus_no, int host_no, int channel_no, int target_no, int lun_no, char adr[]) { char *fname = NULL, *msg = NULL; int ret = 0, first = 1, i_bus_no = -1, fname_size = 4096; int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1; burn_drive_enumerator_t enm; BURN_ALLOC_MEM(fname, char, fname_size); BURN_ALLOC_MEM(msg, char, fname_size + 100); sprintf(msg,"burn_drive_convert_scsi_adr( %d,%d,%d,%d,%d )", bus_no, host_no, channel_no, target_no, lun_no); burn_drive_adr_debug_msg(msg, NULL); while (1) { ret= sg_give_next_adr(&enm, fname, fname_size, first); if(ret <= 0) break; first = 0; ret = burn_drive_obtain_scsi_adr(fname, &i_bus_no, &i_host_no, &i_channel_no, &i_target_no, &i_lun_no); if(ret <= 0) continue; if(bus_no >=0 && i_bus_no != bus_no) continue; if(host_no >=0 && i_host_no != host_no) continue; if(channel_no >= 0 && i_channel_no != channel_no) continue; if(target_no >= 0 && i_target_no != target_no) continue; if(lun_no >= 0 && i_lun_no != lun_no) continue; if(strlen(fname) >= BURN_DRIVE_ADR_LEN) { ret = -1; goto ex;} burn_drive_adr_debug_msg( "burn_drive_convert_scsi_adr() found %s", fname); strcpy(adr, fname); { ret = 1; goto ex;} } ret = 0; ex:; if (first == 0) sg_give_next_adr(&enm, fname, fname_size, -1); BURN_FREE_MEM(fname); BURN_FREE_MEM(msg); return ret; } /* ts A60922 ticket 33 */ /* Try to find an enumerated address with the same host,channel,target,lun as path */ int burn_drive_find_scsi_equiv(char *path, char adr[]) { int ret = 0; int bus_no, host_no, channel_no, target_no, lun_no; char msg[4096]; ret = burn_drive_obtain_scsi_adr(path, &bus_no, &host_no, &channel_no, &target_no, &lun_no); if(ret <= 0) { sprintf(msg,"burn_drive_obtain_scsi_adr( %s ) returns %d", path, ret); burn_drive_adr_debug_msg(msg, NULL); return 0; } sprintf(msg, "burn_drive_find_scsi_equiv( %s ) : %d,%d,%d,%d,%d", path, bus_no, host_no, channel_no, target_no, lun_no); burn_drive_adr_debug_msg(msg, NULL); ret= burn_drive_convert_scsi_adr(bus_no, host_no, channel_no, target_no, lun_no, adr); return ret; } /* ts A60922 ticket 33 */ /** Try to convert a given existing filesystem address into a persistent drive address. */ int burn_drive_convert_fs_adr_sub(char *path, char adr[], int *rec_count) { int ret; struct stat stbuf; burn_drive_adr_debug_msg("burn_drive_convert_fs_adr( %s )", path); if (strncmp(path, "stdio:", 6) == 0 || burn_drive_is_enumerable_adr(path)) { if(strlen(path) >= BURN_DRIVE_ADR_LEN) return -1; if (strncmp(path, "stdio:", 6) != 0) burn_drive_adr_debug_msg( "burn_drive_is_enumerable_adr( %s ) is true", path); strcpy(adr, path); return 1; } if(lstat(path, &stbuf) == -1) { burn_drive_adr_debug_msg("lstat( %s ) returns -1", path); return 0; } if((stbuf.st_mode & S_IFMT) == S_IFLNK) { ret = burn_drive_resolve_link(path, adr, rec_count, 0); if(ret > 0) return 1; burn_drive_adr_debug_msg("link fallback via stat( %s )", path); if(stat(path, &stbuf) == -1) { burn_drive_adr_debug_msg("stat( %s ) returns -1",path); return 0; } } if((stbuf.st_mode&S_IFMT) == S_IFBLK || (stbuf.st_mode&S_IFMT) == S_IFCHR) { ret = burn_drive_find_devno(stbuf.st_rdev, adr); if(ret > 0) return 1; ret = burn_drive_find_scsi_equiv(path, adr); if(ret > 0) return 1; } burn_drive_adr_debug_msg("Nothing found for %s", path); return 0; } /* API */ /** Try to convert a given existing filesystem address into a persistent drive address. */ int burn_drive_convert_fs_adr(char *path, char adr[]) { int ret, rec_count = 0; ret = burn_drive_convert_fs_adr_sub(path, adr, &rec_count); return ret; } /* API */ int burn_lookup_device_link(char *dev_adr, char link_adr[], char *dir_adr, char **ranks, int rank_count, int flag) { DIR *dirpt= NULL; struct dirent *entry; struct stat link_stbuf; char *adr= NULL, *namept, *sys_adr= NULL; int ret, name_rank, found_rank= 0x7fffffff, dirlen, i, rec_count = 0; static char default_ranks_data[][8] = {"dvdrw", "cdrw", "dvd", "cdrom", "cd"}; char *default_ranks[5]; link_adr[0] = 0; if (ranks == NULL) { for (i = 0; i < 5; i++) default_ranks[i] = default_ranks_data[i]; ranks = default_ranks; rank_count= 5; } dirlen= strlen(dir_adr) + 1; if (strlen(dir_adr) + 1 >= BURN_DRIVE_ADR_LEN) { /* >>> Issue warning about oversized directory address */; {ret = 0; goto ex;} } BURN_ALLOC_MEM(adr, char, BURN_DRIVE_ADR_LEN); BURN_ALLOC_MEM(sys_adr, char, BURN_DRIVE_ADR_LEN); dirpt = opendir(dir_adr); if (dirpt == NULL) {ret = 0; goto ex;} strcpy(adr, dir_adr); strcat(adr, "/"); namept = adr + strlen(dir_adr) + 1; while(1) { entry = readdir(dirpt); if(entry == NULL) break; if (strlen(entry->d_name) + dirlen >= BURN_DRIVE_ADR_LEN) continue; strcpy(namept, entry->d_name); if(lstat(adr, &link_stbuf) == -1) continue; if((link_stbuf.st_mode & S_IFMT) != S_IFLNK) continue; /* Determine rank and omit uninteresting ones */ for(name_rank= 0; name_rank < rank_count; name_rank++) if(strncmp(namept, ranks[name_rank], strlen(ranks[name_rank])) == 0) break; /* we look for lowest rank */ if(name_rank >= rank_count || name_rank > found_rank || (name_rank == found_rank && strcmp(namept, link_adr + dirlen) >= 0)) continue; /* Does name point to the same device as dev_adr ? */ ret= burn_drive_resolve_link(adr, sys_adr, &rec_count, 2); if(ret < 0) goto ex; if(ret == 0) continue; if(strcmp(dev_adr, sys_adr) == 0) { strcpy(link_adr, adr); found_rank= name_rank; } } ret= 2; if(found_rank < 0x7fffffff) ret= 1; ex:; if(dirpt != NULL) closedir(dirpt); BURN_FREE_MEM(adr); BURN_FREE_MEM(sys_adr); return(ret); } /** A pacifier function suitable for burn_abort. @param handle If not NULL, a pointer to a text suitable for printf("%s") */ int burn_abort_pacifier(void *handle, int patience, int elapsed) { char *prefix= "libburn : "; if(handle!=NULL) prefix= handle; fprintf(stderr, "\r%sABORT : Waiting for drive to finish ( %d s, %d max)", (char *) prefix, elapsed, patience); return(1); } /* ts B00226 : Outsourced backend of burn_abort() @param flag bit0= do not call burn_finish() */ int burn_abort_5(int patience, int (*pacifier_func)(void *handle, int patience, int elapsed), void *handle, int elapsed, int flag) { int ret, i, occup, still_not_done= 1, pacifier_off= 0, first_round= 1; unsigned long wait_grain= 100000; time_t start_time, current_time, pacifier_time, end_time; #ifndef NIX time_t stdio_patience = 3; #endif /* fprintf(stderr, "libburn_EXPERIMENTAL: burn_abort_5(%d,%d)\n", patience, flag); */ current_time = start_time = pacifier_time = time(0); start_time -= elapsed; end_time = start_time + patience; /* >>> ts A71002 : are there any threads at work ? If not, then one can force abort because the drives will not change status on their own. */ while(current_time < end_time || (patience <= 0 && first_round)) { still_not_done = 0; for(i = 0; i < drivetop + 1; i++) { occup = burn_drive_is_occupied(&(drive_array[i])); if(occup == -2) continue; if(drive_array[i].drive_role != 1) { #ifdef NIX /* ts A90302 <<< this causes a race condition with drive usage and drive disposal. */ drive_array[i].busy = BURN_DRIVE_IDLE; burn_drive_forget(&(drive_array[i]), 1); continue; #else /* NIX */ /* ts A90318 >>> but if a pipe breaks then the drive never gets idle. So for now with a short patience timespan and eventually a deliberate memory leak. */ if (current_time - start_time > stdio_patience) { drive_array[i].global_index = -1; continue; } #endif /* ! NIX */ } if(occup < 10) { if (!drive_array[i].cancel) burn_drive_cancel(&(drive_array[i])); if (drive_array[i].drive_role != 1) /* occup == -1 comes early */ usleep(1000000); burn_drive_forget(&(drive_array[i]), 1); } else if(occup <= 100) { if (!drive_array[i].cancel) burn_drive_cancel(&(drive_array[i])); still_not_done++; } else if(occup <= 1000) { still_not_done++; } } first_round = 0; if(still_not_done == 0 || patience <= 0) break; usleep(wait_grain); current_time = time(0); if(current_time>pacifier_time) { if(pacifier_func != NULL && !pacifier_off) { ret = (*pacifier_func)(handle, patience, current_time-start_time); pacifier_off = (ret <= 0); } pacifier_time = current_time; } } if (!(flag & 1)) burn_finish(); return(still_not_done == 0); } /** Abort any running drive operation and finish libburn. @param patience Maximum number of seconds to wait for drives to finish @param pacifier_func Function to produce appeasing messages. See burn_abort_pacifier() for an example. @return 1 ok, all went well 0 had to leave a drive in unclean state <0 severe error, do no use libburn again */ int burn_abort(int patience, int (*pacifier_func)(void *handle, int patience, int elapsed), void *handle) { int ret, flg = 0; if (patience < 0) { patience = 0; flg |= 1; } ret = burn_abort_5(patience, pacifier_func, handle, 0, flg); return ret; } /* ts A61020 API function */ int burn_drive_get_start_end_lba(struct burn_drive *d, int *start_lba, int *end_lba, int flag) { if (d->start_lba == -2000000000 || d->end_lba == -2000000000) return 0; *start_lba = d->start_lba; *end_lba= d->end_lba; return 1; } /* ts A61020 API function */ int burn_disc_pretend_blank(struct burn_drive *d) { if (d->drive_role == 0) return 0; if (d->status != BURN_DISC_UNREADY && d->status != BURN_DISC_UNSUITABLE) return 0; d->status = BURN_DISC_BLANK; return 1; } /* ts A61106 API function */ int burn_disc_pretend_full(struct burn_drive *d) { if (d->drive_role == 0) return 0; if (d->status != BURN_DISC_UNREADY && d->status != BURN_DISC_UNSUITABLE) return 0; d->status = BURN_DISC_FULL; return 1; } /* ts B31110 API function */ int burn_disc_pretend_full_uncond(struct burn_drive *d) { d->status = BURN_DISC_FULL; return 1; } /* ts A61021: new API function */ int burn_disc_read_atip(struct burn_drive *d) { if (burn_drive_is_released(d)) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010e, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Attempt to read ATIP from ungrabbed drive", 0, 0); return -1; } if(d->drive_role != 1) return 0; if ((d->current_profile == -1 || d->current_is_cd_profile) && ((d->mdata->p2a_valid > 0 && d->mdata->cdrw_write) || d->current_profile != 0x08)) { d->read_atip(d); /* >>> some control of success would be nice :) */ } else { /* mmc5r03c.pdf 6.26.3.6.3 : ATIP is undefined for non-CD (and it seems meaningless for non-burners). ts A90823: Pseudo-CD U3 memory stick stalls with ATIP. It is !cdrw_write and profile is 0x08. */ return 0; } return 1; } /* ts A61110 : new API function */ int burn_disc_track_lba_nwa_v2(struct burn_drive *d, struct burn_write_opts *o, int trackno, off_t *lba, off_t *nwa) { int ret, int_lba, int_nwa; if (burn_drive_is_released(d)) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002011b, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Attempt to read track info from ungrabbed drive", 0, 0); return -1; } if (d->busy != BURN_DRIVE_IDLE) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002011c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Attempt to read track info from busy drive", 0, 0); return -1; } *lba = *nwa = 0; if (d->drive_role == 5 && trackno == 0 && d->status == BURN_DISC_APPENDABLE) { *lba = *nwa = d->role_5_nwa; return 1; } if (d->drive_role != 1) return 0; if (o != NULL) d->send_write_parameters(d, NULL, -1, o); ret = d->get_nwa(d, trackno, &int_lba, &int_nwa); if (ret <= 0) return ret; *lba = int_lba; *nwa = int_nwa; return ret; } /* ts A61110 / C40302 : API */ int burn_disc_track_lba_nwa(struct burn_drive *d, struct burn_write_opts *o, int trackno, int *lba, int *nwa) { int ret; off_t off_lba, off_nwa; ret = burn_disc_track_lba_nwa_v2(d, o, trackno, &off_lba, &off_nwa); if (ret <= 0) return ret; if (off_lba > 0x7fffffff) *lba = 0x7fffffff; else *lba = off_lba; if (off_nwa > 0x7fffffff) { *nwa = 0x7fffffff; return 0; } else { *nwa = off_nwa; } return 1; } /* ts A70131 : new API function */ int burn_disc_get_msc1(struct burn_drive *d, int *start) { int ret, trackno; if (burn_drive_is_released(d)) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002011b, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Attempt to read track info from ungrabbed drive", 0, 0); return -1; } if (d->busy != BURN_DRIVE_IDLE) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002011c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Attempt to read track info from busy drive", 0, 0); return -1; } *start = 0; if (d->drive_role != 1) return 0; ret = d->read_multi_session_c1(d, &trackno, start); return ret; } /* ts A70213 : new API function */ off_t burn_disc_available_space(struct burn_drive *d, struct burn_write_opts *o) { int lba, nwa, ret; off_t bytes, start_byte = 0; if (burn_drive_is_released(d)) return 0; if (d->busy != BURN_DRIVE_IDLE) return 0; if (d->drive_role == 0) return 0; if (d->drive_role != 1) { if (o != NULL) start_byte = o->start_byte; ret = burn_os_stdio_capacity(d->devname, start_byte, &bytes); if (ret != 1) bytes = d->media_capacity_remaining; if (bytes <= 0) bytes = BURN_DRIVE_MAX_BYTES; if (bytes != d->media_capacity_remaining) burn_drive_set_media_capacity_remaining(d, bytes); } else { if (o != NULL) d->send_write_parameters(d, NULL, -1, o); d->get_nwa(d, -1, &lba, &nwa); } if (o != NULL) { if (o->start_byte > 0) { if (o->start_byte > d->media_capacity_remaining) return 0; return d->media_capacity_remaining - o->start_byte; } } return d->media_capacity_remaining; } /* ts A61202 : New API function */ int burn_disc_get_profile(struct burn_drive *d, int *pno, char name[80]) { *pno = d->current_profile; strcpy(name,d->current_profile_text); return *pno >= 0; } /* ts A90815 : New API function */ int burn_drive_get_all_profiles(struct burn_drive *d, int *num_profiles, int profiles[64], char is_current[64]) { int i; *num_profiles = d->num_profiles; for (i = 0; i < d->num_profiles; i++) { profiles[i] = (d->all_profiles[i * 4] << 8) | d->all_profiles[i * 4 + 1]; is_current[i] = d->all_profiles[i * 4 + 2] & 1; } return 1; } /* ts A90815 : New API function */ int burn_obtain_profile_name(int profile_number, char name[80]) { strcpy(name, mmc_obtain_profile_name(profile_number)); return(name[0] != 0); } /* ts A61223 : New API function */ int burn_drive_wrote_well(struct burn_drive *d) { return !d->cancel; } /* ts A61226 */ int burn_speed_descriptor_new(struct burn_speed_descriptor **s, struct burn_speed_descriptor *prev, struct burn_speed_descriptor *next, int flag) { struct burn_speed_descriptor *o; (*s) = o = calloc(1, sizeof(struct burn_speed_descriptor)); if (o == NULL) return -1; o->source = 0; o->profile_loaded = -2; o->profile_name[0] = 0; o->wrc = 0; o->exact = 0; o->mrw = 0; o->end_lba = -1; o->write_speed = 0; o->read_speed = 0; o->prev = prev; if (prev != NULL) { next = prev->next; prev->next = o; } o->next = next; if (next != NULL) next->prev = o; return 1; } /* ts A61226 */ /* @param flag bit0= destroy whole next-chain of descriptors */ int burn_speed_descriptor_destroy(struct burn_speed_descriptor **s, int flag) { struct burn_speed_descriptor *next, *o; if ((*s) == NULL) return 0; if (flag&1) for (o = (*s); o->prev != NULL; o = o->prev); else o = (*s); next = o->next; if (next != NULL) next->prev = o->prev; if (o->prev != NULL) o->prev->next = next; free((char *) (*s)); (*s) = NULL; if (flag&1) return burn_speed_descriptor_destroy(&next, flag&1); return 1; } /* ts A61226 */ int burn_speed_descriptor_copy(struct burn_speed_descriptor *from, struct burn_speed_descriptor *to, int flag) { to->source = from->source; to->profile_loaded = from->profile_loaded; strcpy(to->profile_name, from->profile_name); to->wrc = from->wrc; to->exact = from->exact; to->mrw = from->mrw; to->end_lba = from->end_lba; to->write_speed = from->write_speed; to->read_speed = from->read_speed; return 1; } /* ts A61226 : free dynamically allocated sub data of struct scsi_mode_data */ int burn_mdata_free_subs(struct scsi_mode_data *m) { burn_speed_descriptor_destroy(&(m->speed_descriptors), 1); return 1; } /* ts A61226 : API function */ int burn_drive_get_speedlist(struct burn_drive *d, struct burn_speed_descriptor **speed_list) { int ret; struct burn_speed_descriptor *sd, *csd = NULL; (*speed_list) = NULL; for (sd = d->mdata->speed_descriptors; sd != NULL; sd = sd->next) { ret = burn_speed_descriptor_new(&csd, NULL, csd, 0); if (ret <= 0) return -1; burn_speed_descriptor_copy(sd, csd, 0); } (*speed_list) = csd; return (csd != NULL); } /* ts A70713 : API function */ int burn_drive_get_best_speed(struct burn_drive *d, int speed_goal, struct burn_speed_descriptor **best_descr, int flag) { struct burn_speed_descriptor *sd; int best_speed = 0, best_lba = 0, source= 2, speed; if (flag & 2) source = -1; if (speed_goal < 0) best_speed = 2000000000; *best_descr = NULL; for (sd = d->mdata->speed_descriptors; sd != NULL; sd = sd->next) { if (flag & 1) speed = sd->read_speed; else speed = sd->write_speed; if ((source >= 0 && sd->source != source) || speed <= 0) continue; if (speed_goal < 0) { if (speed < best_speed) { best_speed = speed; *best_descr = sd; } } else if (speed_goal == 0) { if ((source == 2 && sd->end_lba > best_lba) || ((source !=2 || sd->end_lba == best_lba) && speed > best_speed)) { best_lba = sd->end_lba; best_speed = speed; *best_descr = sd; } } else if (speed <= speed_goal) { if (speed > best_speed) { best_speed = speed; *best_descr = sd; } } } if (d->current_is_cd_profile && *best_descr == NULL && ! (flag & 2)) /* Mode page 2Ah is deprecated in MMC-5 although all known burners still support it with CD media. */ return burn_drive_get_best_speed(d, speed_goal, best_descr, flag | 2); return (*best_descr != NULL); } /* ts A61226 : API function */ int burn_drive_free_speedlist(struct burn_speed_descriptor **speed_list) { return burn_speed_descriptor_destroy(speed_list, 1); } /* ts A70203 : API function */ int burn_disc_get_multi_caps(struct burn_drive *d, enum burn_write_types wt, struct burn_multi_caps **caps, int flag) { enum burn_disc_status s; struct burn_multi_caps *o; int status, num_formats, ret, type, i; off_t size; unsigned dummy; *caps = NULL; s = burn_disc_get_status(d); if(s == BURN_DISC_UNGRABBED) return -1; *caps = o = (struct burn_multi_caps *) calloc(1, sizeof(struct burn_multi_caps)); if(*caps == NULL) return -1; /* Default says nothing is available */ o->multi_session = o->multi_track = 0; o-> start_adr = 0; o->start_alignment = o->start_range_low = o->start_range_high = 0; o->might_do_tao = o->might_do_sao = o->might_do_raw = 0; o->advised_write_mode = BURN_WRITE_NONE; o->selected_write_mode = wt; o->current_profile = d->current_profile; o->current_is_cd_profile = d->current_is_cd_profile; o->might_simulate = 0; if (d->drive_role == 0 || d->drive_role == 4) return 0; if (d->drive_role == 2) { /* stdio file drive : random access read-write */ o->start_adr = 1; size = d->media_capacity_remaining; burn_os_stdio_capacity(d->devname, 0, &size); burn_drive_set_media_capacity_remaining(d, size); o->start_range_high = d->media_capacity_remaining - 2048; o->start_alignment = 2048; /* imposting a drive, not a file */ o->might_do_sao = 4; o->might_do_tao = 2; o->advised_write_mode = BURN_WRITE_TAO; o->might_simulate = 1; } else if (d->drive_role == 5) { /* stdio file drive : random access write-only */ o->start_adr = 1; size = d->media_capacity_remaining; burn_os_stdio_capacity(d->devname, 0, &size); burn_drive_set_media_capacity_remaining(d, size); /* >>> start_range_low = file size rounded to 2048 */; o->start_range_high = d->media_capacity_remaining - 2048; o->start_alignment = 2048; /* imposting a drive, not a file */ if (s == BURN_DISC_APPENDABLE) { if (wt == BURN_WRITE_SAO || wt == BURN_WRITE_RAW) return 0; o->might_do_sao = 0; } else o->might_do_sao = 4; o->might_do_tao = 2; o->advised_write_mode = BURN_WRITE_TAO; o->might_simulate = 1; } else if (d->drive_role != 1) { /* stdio file drive : sequential access write-only */ o->might_do_sao = 4; o->might_do_tao = 2; o->advised_write_mode = BURN_WRITE_TAO; o->might_simulate = 1; } else if (s != BURN_DISC_BLANK && s != BURN_DISC_APPENDABLE) { return 0; } else if (s == BURN_DISC_APPENDABLE && (wt == BURN_WRITE_SAO || wt == BURN_WRITE_RAW)) { return 0; } else if (wt == BURN_WRITE_RAW && !d->current_is_cd_profile) { return 0; } else if (d->current_profile == 0x09 || d->current_profile == 0x0a) { /* CD-R , CD-RW */ if (d->block_types[BURN_WRITE_TAO]) { o->multi_session = o->multi_track = 1; o->might_do_tao = 2; if (o->advised_write_mode == BURN_WRITE_NONE) o->advised_write_mode = BURN_WRITE_TAO; } if (d->block_types[BURN_WRITE_SAO]) { o->multi_session = o->multi_track = 1; o->might_do_sao = 1; if (o->advised_write_mode == BURN_WRITE_NONE) o->advised_write_mode = BURN_WRITE_SAO; } if (d->block_types[BURN_WRITE_RAW]) { o->might_do_raw = 1; if (o->advised_write_mode == BURN_WRITE_NONE) o->advised_write_mode = BURN_WRITE_RAW; } if (wt == BURN_WRITE_RAW) o->multi_session = o->multi_track = 0; else if(wt == BURN_WRITE_NONE || wt == BURN_WRITE_SAO || wt == BURN_WRITE_TAO) o->might_simulate = !!(d->mdata->p2a_valid > 0 && d->mdata->simulate); } else if (d->current_profile == 0x11 || d->current_profile == 0x14 || d->current_profile == 0x15) { /* DVD-R , sequential DVD-RW , DVD-R/DL Sequential */ if (s == BURN_DISC_BLANK) { o->might_do_sao = 1; o->advised_write_mode = BURN_WRITE_SAO; } if (d->current_has_feat21h) { #ifndef Libburn_dvd_r_dl_multi_no_close_sessioN if (d->current_profile != 0x15) #endif o->multi_session = 1; o->multi_track = 1; o->might_do_tao = 2; o->advised_write_mode = BURN_WRITE_TAO; } if (wt == BURN_WRITE_SAO) o->multi_session = o->multi_track = 0; if (wt == BURN_WRITE_NONE || wt == BURN_WRITE_SAO || wt == BURN_WRITE_TAO) o->might_simulate = 1; } else if (d->current_profile == 0x12 || d->current_profile == 0x13 || d->current_profile == 0x1a || d->current_profile == 0x43 ) { /* DVD-RAM, overwritable DVD-RW, DVD+RW, BD-RE */ o->start_adr = 1; ret = burn_disc_get_formats(d, &status, &size, &dummy, &num_formats); if (ret == 1) { if (status == BURN_FORMAT_IS_FORMATTED) o->start_range_high = size - 2048; if (d->current_profile == 0x13) { o->start_alignment = 32 * 1024; for (i = 0; i < num_formats; i++) { ret = burn_disc_get_format_descr(d, i, &type, &size, &dummy); if (ret <= 0) continue; if (type == 0x13) /* expandable */ break; } if (i >= num_formats) /* not expandable */ o->start_range_high -= 32 * 1024; if (o->start_range_high < 0) o->start_range_high = 0; } else { o->start_alignment = 2 * 1024; if (d->best_format_size - 2048 > o->start_range_high) o->start_range_high = d->best_format_size - 2048; } } o->might_do_sao = 4; o->might_do_tao = 2; o->advised_write_mode = BURN_WRITE_TAO; } else if (d->current_profile == 0x1b || d->current_profile == 0x2b || d->current_profile == 0x41) { /* DVD+R , DVD+R/DL , BD-R SRM */ o->multi_session = o->multi_track = 1; o->might_do_tao = 2; o->might_do_sao = 1; o->advised_write_mode = BURN_WRITE_TAO; } else /* unknown media */ return 0; if (s == BURN_DISC_APPENDABLE) o->might_do_sao = o->might_do_raw = 0; if (wt == BURN_WRITE_TAO && !o->might_do_tao) return 0; else if (wt == BURN_WRITE_SAO && !o->might_do_sao) return 0; else if (wt == BURN_WRITE_RAW && !o->might_do_raw) return 0; return 1; } /* ts A70203 : API function */ int burn_disc_free_multi_caps(struct burn_multi_caps **caps) { if (*caps == NULL) return 0; free((char *) *caps); *caps = NULL; return 1; } /* ts A70207 : evaluate write mode related peculiarities of a disc @param flag bit0= fill_up_media is active */ int burn_disc_get_write_mode_demands(struct burn_disc *disc, struct burn_write_opts *opts, struct burn_disc_mode_demands *result, int flag) { struct burn_session *session; struct burn_track *track; int i, j, mode, unknown_track_sizes = 0, last_track_is_unknown = 0; enum burn_disc_status s; memset((char *) result, 0, sizeof(struct burn_disc_mode_demands)); if (disc == NULL) return 2; s = burn_disc_get_status(opts->drive); if (s == BURN_DISC_APPENDABLE || disc->sessions > 1) result->will_append = 1; if (disc->sessions > 1) result->multi_session = 1; for (i = 0; i < disc->sessions; i++) { session = disc->session[i]; if (session->tracks <= 0) continue; mode = session->track[0]->mode; if (session->tracks > 1) result->multi_track = 1; for (j = 0; j < session->tracks; j++) { track = session->track[j]; if (burn_track_is_open_ended(track)) { if (burn_track_get_default_size(track) > 0) { if (result->unknown_track_size == 0) result->unknown_track_size = 2; } else result->unknown_track_size = 1; unknown_track_sizes++; last_track_is_unknown = 1; } else last_track_is_unknown = 0; if ((mode & BURN_MODE_BITS) != (track->mode & BURN_MODE_BITS)) result->mixed_mode = 1; if (track->mode & BURN_MODE1) { result->block_types |= BURN_BLOCK_MODE1; } else if (track->mode & BURN_AUDIO) { result->audio = 1; result->block_types |= BURN_BLOCK_RAW0; result->exotic_track = 1; } else { result->block_types |= opts->block_type; result->exotic_track = 1; } } } if (flag&1) {/* fill_up_media will define the size of the last track */ if (unknown_track_sizes == 1 && last_track_is_unknown) result->unknown_track_size = 0; } return (disc->sessions > 0); } /* ts A70903 : API */ int burn_drive_get_drive_role(struct burn_drive *d) { return d->drive_role; } /* ts A70923 Hands out pointers *dpt to directory path and *npt to basename. Caution: the last '/' in adr gets replaced by a 0. */ static int burn__split_path(char *adr, char **dpt, char **npt) { *dpt = adr; *npt = strrchr(*dpt, '/'); if (*npt == NULL) { *npt = *dpt; *dpt = "."; return 1; } **npt = 0; if(*npt == *dpt) *dpt = "/"; (*npt)++; return 2; } /* ts A70923 : API */ int burn_drive_equals_adr(struct burn_drive *d1, char *adr2_in, int role2) { struct stat stbuf1, stbuf2; char *adr1 = NULL, *adr2 = adr2_in; char *conv_adr1 = NULL, *conv_adr2 = NULL; char *npt1, *dpt1, *npt2, *dpt2; int role1, stat_ret1, stat_ret2, conv_ret2, exact_role_matters = 0, fd; int ret; BURN_ALLOC_MEM(adr1, char, BURN_DRIVE_ADR_LEN); BURN_ALLOC_MEM(conv_adr1, char, BURN_DRIVE_ADR_LEN); BURN_ALLOC_MEM(conv_adr2, char, BURN_DRIVE_ADR_LEN); role1 = burn_drive_get_drive_role(d1); burn_drive_d_get_adr(d1, adr1); stat_ret1 = stat(adr1, &stbuf1); /* If one of the candidate paths depicts an open file descriptor then its read-write capability decides about its role and the difference between roles 2 and 3 does matter. */ fd = burn_drive__fd_from_special_adr(d1->devname); if (fd != -1) exact_role_matters = 1; if (strncmp(adr2, "stdio:", 6) == 0) { adr2+= 6; if (adr2[0] == 0) { role2 = 0; } else { fd = burn_drive__fd_from_special_adr(adr2); if (fd != -1) exact_role_matters = 1; ret = burn_drive__is_rdwr(adr2, NULL, NULL, NULL, 1 | 2); if (ret == 2 && (burn_drive_role_4_allowed & 1)) role2 = 4; else if (ret == 3 && (burn_drive_role_4_allowed & 1)) role2 = 5; else if (ret > 0) role2 = 2; else role2 = 3; if (fd == -1 && role2 == 2 && (burn_drive_role_4_allowed & 3) == 3) role2 = burn_role_by_access(adr2, !!(burn_drive_role_4_allowed & 4)); } } if (strlen(adr2) >= BURN_DRIVE_ADR_LEN) {ret = -1; goto ex;} stat_ret2 = stat(adr2, &stbuf2); conv_ret2 = burn_drive_convert_fs_adr(adr2, conv_adr2); if (!exact_role_matters) { /* roles >= 2 have the same name space and object interpretation */ if (role1 >= 2) role1 = 2; if (role2 >= 2) role2 = 2; } if (strcmp(adr1, adr2) == 0 && role1 == role2) {ret = 1; goto ex;} /* equal role and address */ if (role1 == 1 && role2 == 1) { /* MMC drive meets wannabe MMC drive */ if (conv_ret2 <= 0) {ret = 0; goto ex;} /* no MMC drive at adr2 */ if (strcmp(adr1, conv_adr2) == 0) {ret = 1; goto ex;} /* equal real MMC drives */ {ret = 0; goto ex;} } else if (role1 == 0 || role2 == 0) {ret = 0; goto ex;} /* one null-drive, one not */ else if (role1 != 1 && role2 != 1) { /* pseudo-drive meets file object */ if (role1 != role2) {ret = 0; goto ex;} if (stat_ret1 == -1 || stat_ret2 == -1) { if (stat_ret1 != -1 || stat_ret2 != -1) {ret = 0; goto ex;} /* one address existing, one not */ /* Two non-existing file objects */ strcpy(conv_adr1, adr1); burn__split_path(conv_adr1, &dpt1, &npt1); strcpy(conv_adr2, adr2); burn__split_path(conv_adr2, &dpt2, &npt2); if (strcmp(npt1, npt2)) {ret = 0; goto ex;} /* basenames differ */ stat_ret1= stat(adr1, &stbuf1); stat_ret2= stat(adr2, &stbuf2); if (stat_ret1 != stat_ret2) {ret = 0; goto ex;} /* one dir existing, one not */ /* Both directories exist. The basenames are equal. So the addresses are equal if the directories are equal.*/ } if (stbuf1.st_ino == stbuf2.st_ino && stbuf1.st_dev == stbuf2.st_dev) {ret = 1; goto ex;} /* same filesystem object */ if (S_ISBLK(stbuf1.st_mode) && S_ISBLK(stbuf2.st_mode) && stbuf1.st_rdev == stbuf2.st_rdev) {ret = 1; goto ex;}/* same major,minor device number */ if (S_ISCHR(stbuf1.st_mode) && S_ISCHR(stbuf2.st_mode) && stbuf1.st_rdev == stbuf2.st_rdev) {ret = 1; goto ex;}/* same major,minor device number */ /* Are both filesystem objects related to the same MMC drive */ if (conv_ret2 <= 0) {ret = 0; goto ex;} /* no MMC drive at adr2 */ if (burn_drive_convert_fs_adr(adr1, conv_adr1) <= 0) {ret = 0; goto ex;} /* no MMC drive at adr1 */ if (strcmp(conv_adr1, conv_adr2) == 0) {ret = 1; goto ex;} /* same MMC drive */ {ret = 0; goto ex;} /* all filesystem disguises are checked */ } else if (role1 == 1 && role2 != 1) { /* MMC drive meets file object */ if (conv_ret2 <= 0) {ret = 0; goto ex;} /* no MMC drive at adr2 */ if (strcmp(adr1, conv_adr2) == 0) {ret = 1; goto ex;} /* same MMC drive */ {ret = 0; goto ex;} } else if (role1 != 1 && role2 == 1) { /* stdio-drive meets wannabe MMC drive */ if (conv_ret2 <= 0) {ret = 0; goto ex;} /* no MMC drive at adr2 */ if (burn_drive_convert_fs_adr(adr1, conv_adr1) <= 0) {ret = 0; goto ex;} /* no MMC drive at adr1 */ if (strcmp(conv_adr1, conv_adr2) == 0) {ret = 1; goto ex;} /* same MMC drive */ {ret = 0; goto ex;} } ret = 0; ex:; BURN_FREE_MEM(adr1); BURN_FREE_MEM(conv_adr1); BURN_FREE_MEM(conv_adr2); return ret; } int burn_drive_find_by_thread_pid(struct burn_drive **d, pid_t pid, pthread_t tid) { int i; for (i = 0; i < drivetop + 1; i++) { /* if (drive_array[i].thread_pid_valid) fprintf(stderr, "libburn_EXPERIMENTAL : drive %d , thread_pid %d\n", i, drive_array[i].thread_pid); */ if (drive_array[i].thread_pid_valid && drive_array[i].thread_pid == pid && pthread_equal(drive_array[i].thread_tid, tid)) { *d = &(drive_array[i]); return 1; } } return 0; } /* ts A80422 : centralizing this setting for debugging purposes */ int burn_drive_set_media_capacity_remaining(struct burn_drive *d, off_t value) { if (value > BURN_DRIVE_MAX_BYTES) value = BURN_DRIVE_MAX_BYTES; d->media_capacity_remaining = value; return 1; } /* ts C40303 : API */ int burn_get_read_capacity_v2(struct burn_drive *d, off_t *capacity, int flag) { *capacity = d->media_read_capacity + (d->media_read_capacity != 0x7fffffffffffffff); return (d->media_read_capacity != 0x7fffffffffffffff); } /* ts A81215 : API */ int burn_get_read_capacity(struct burn_drive *d, int *capacity, int flag) { int ret; off_t cap; ret= burn_get_read_capacity_v2(d, &cap, flag); if (cap < -0x7fffffff) { *capacity = -0x7fffffff; ret = 0; } else if (cap > 0x7fffffff) { *capacity = 0x7fffffff; ret = 0; } else { *capacity = cap; } return ret; } /* ts A90903 : API */ int burn_disc_get_media_id(struct burn_drive *d, char **product_id, char **media_code1, char **media_code2, char **book_type, int flag) { int ret; *product_id = *media_code1 = *media_code2 = *book_type = NULL; if (burn_drive_get_drive_role(d) != 1) return 0; ret = mmc_get_media_product_id(d, product_id, media_code1, media_code2, book_type, flag & 1); return ret; } /* ts A90909 : API */ /** @param valid Replies bits which indicate the validity of other reply parameters or the state of certain CD info bits: bit0= disc_type valid bit1= disc_id valid bit2= bar_code valid bit3= disc_app_code valid bit4= Disc is unrestricted (URU bit) bit5= Disc is nominally erasable (Erasable bit) This will be set with overwritable media which libburn normally considers to be unerasable blank. */ int burn_disc_get_cd_info(struct burn_drive *d, char disc_type[80], unsigned int *disc_id, char bar_code[9], int *app_code, int *valid) { if (d->disc_type == 0x00) { strcpy(disc_type, "CD-DA or CD-ROM"); } else if (d->disc_type == 0x10) { strcpy(disc_type, "CD-I"); } else if (d->disc_type == 0x20) { strcpy(disc_type, "CD-ROM XA"); } else { strcpy(disc_type, "undefined"); } *disc_id = d->disc_id; memcpy(bar_code, d->disc_bar_code, 8); bar_code[8]= 0; *app_code = d->disc_app_code; *valid = d->disc_info_valid; return 1; } /* ts B00924 : API */ int burn_disc_get_bd_spare_info(struct burn_drive *d, int *alloc_blocks, int *free_blocks, int flag) { int ret; if (burn_drive_get_drive_role(d) != 1) return 0; *alloc_blocks = *free_blocks = 0; ret = mmc_get_bd_spare_info(d, alloc_blocks, free_blocks, 0); return ret; } /* ts B10801 : API */ int burn_disc_get_phys_format_info(struct burn_drive *d, int *disk_category, char **book_name, int *part_version, int *num_layers, int *num_blocks, int flag) { int ret; if (burn_drive_get_drive_role(d) != 1) return 0; *disk_category = *part_version = *num_layers = *num_blocks = 0; ret = mmc_get_phys_format_info(d, disk_category, book_name, part_version, num_layers, num_blocks, 0); return ret; } /* ts B10525 : API */ int burn_disc_next_track_is_damaged(struct burn_drive *d, int flag) { return d->next_track_damaged; } /* ts B11201 : API */ /* Read the CD-TEXT data from the Lead-in of an Audio CD */ int burn_disc_get_leadin_text(struct burn_drive *d, unsigned char **text_packs, int *num_packs, int flag) { int ret; ret = mmc_get_leadin_text(d, text_packs, num_packs, 0); return ret; } /* ts B31023 API */ /* Inquire for DVD-RW failure of TAO */ int burn_drive_was_feat21_failure(struct burn_drive *d) { return !!d->was_feat21h_failure; } /* ts B40106 */ int burn_feature_descr_new(struct burn_feature_descr **new, unsigned char *descr, int descr_len, int flag) { struct burn_feature_descr *o; *new = NULL; if (descr_len < 4) return 0; (*new) = o = calloc(1, sizeof(struct burn_feature_descr)); if (o == NULL) return -1; o->feature_code = (descr[0] << 8) | descr[1]; o->flags = descr[2]; if (descr[3] > descr_len - 4) o->data_lenght = 0; else o->data_lenght = descr[3]; o->data = NULL; o->next = NULL; if (o->data_lenght > 0) { o->data = calloc(1, o->data_lenght); if (o->data == NULL) { burn_feature_descr_free(new, 0); return -1; } memcpy(o->data, descr + 4, o->data_lenght); } return 1; } /* ts B40106 */ int burn_feature_descr_free(struct burn_feature_descr **descr, int flag) { struct burn_feature_descr *o, *next; if (*descr == NULL) return 0; for (o = *descr; o != NULL; o = next) { next = o->next; if (o->data != NULL) free(o->data); free((char *) o); } *descr = NULL; return 1; } /* ts B40107 */ int burn_drive_has_feature(struct burn_drive *d, int feature_code, struct burn_feature_descr **descr, int flag) { struct burn_feature_descr *o; for (o = d->features; o != NULL; o = o->next) { if (o->feature_code == feature_code) { if (descr != NULL) *descr = o; return 1; } } return 0; } /* ts B51016 API */ int burn_drive_get_serial_no(struct burn_drive *d, char **sno, int *sno_len) { int ret; if (*sno != NULL) BURN_FREE_MEM(*sno); if (d->drive_serial_number_len > 0) *sno_len = d->drive_serial_number_len; else *sno_len = 0; BURN_ALLOC_MEM(*sno, char, *sno_len + 1); if (d->drive_serial_number_len > 0) memcpy(*sno, d->drive_serial_number, *sno_len); (*sno)[*sno_len] = 0; ret = 1; ex: return ret; } /* ts B51016 API */ int burn_drive_get_media_sno(struct burn_drive *d, char **sno, int *sno_len) { int ret; #ifdef Libburn_enable_scsi_cmd_ABh struct burn_feature_descr *feat; #endif if (*sno != NULL) BURN_FREE_MEM(*sno); *sno = NULL; if (d->media_serial_number_len == -1) { #ifdef Libburn_enable_scsi_cmd_ABh if (burn_drive_has_feature(d, 0x109, &feat, 0)) #ifndef Libburn_enable_scsi_cmd_ABh_pretend_currenT if (feat->flags & 1) /* current */ #endif spc_read_media_serial_number(d); #else ; #endif /* ! Libburn_enable_scsi_cmd_ABh */ } if (d->media_serial_number_len > 0) *sno_len = d->media_serial_number_len; else *sno_len = 0; BURN_ALLOC_MEM(*sno, char, *sno_len + 1); if (*sno_len > 0) memcpy(*sno, d->media_serial_number, *sno_len); (*sno)[*sno_len] = 0; ret = 1; ex: return ret; } int burn_drive_get_bd_r_pow(struct burn_drive *d) { struct burn_feature_descr *feature; if (d->current_profile == 0x41) if (burn_drive_has_feature(d, 0x38, &feature, 0) == 1) if (feature->flags & 1) return 1; return 0; } int burn_drive_set_immed(struct burn_drive *drive, int enable) { drive->do_no_immed = !enable; return 1; } int burn_drive_get_immed(struct burn_drive *drive) { return !drive->do_no_immed; } /* ts B90412 , API */ int burn_drive_get_feature(struct burn_drive *d, unsigned int feature_code, unsigned char *flags, unsigned char *additional_length, unsigned char **feature_data, char **feature_text) { int ret, i; struct burn_feature_descr *descr; *flags = 0; *additional_length = 0; *feature_data = NULL; if (feature_text != NULL) *feature_text = NULL; if (!burn_drive_has_feature(d, feature_code, &descr, 0)) return 0; *flags = descr->flags; *additional_length = descr->data_lenght; if (*additional_length > 0) BURN_ALLOC_MEM(*feature_data, unsigned char, *additional_length); for (i = 0; i < *additional_length; i++) (*feature_data)[i] = descr->data[i]; if (feature_text != NULL) { ret = burn_make_feature_text(d, feature_code, *flags, *additional_length, *feature_data, feature_text, 0); } else { ret = 1; } ex: return ret; } /* ts B90412 , API */ void burn_drive_get_feature_codes(struct burn_drive *d, int *count, unsigned int **feature_codes) { struct burn_feature_descr *o; int to_alloc; *count = 0; *feature_codes = NULL; for (o = d->features; o != NULL; o = o->next) (*count)++; if (*count == 0) return; to_alloc = *count; *count = 0; BURN_ALLOC_MEM_VOID(*feature_codes, unsigned int, to_alloc); for (o = d->features; o != NULL; o = o->next) { (*feature_codes)[*count] = o->feature_code; (*count)++; } ex:; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2024 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifndef __DRIVE #define __DRIVE #include "libburn.h" #include "toc.h" #include "structure.h" #include <pthread.h> struct burn_drive; struct command; struct mempage; struct scsi_mode_data; struct burn_speed_descriptor; struct burn_feature_descr; #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 *); int burn_drive_unregister(struct burn_drive *d); unsigned int burn_drive_count(void); /* ts A61007 */ /* void burn_wait_all(void); */ /* @param flag bit0= demand freed drives (else released drives) */ int burn_drives_are_clear(int flag); 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(struct burn_drive *d); void burn_drive_free_all(void); /* @param flag bit0= reset global drive list */ int burn_drive_scan_sync(struct burn_drive_info *drives[], unsigned int *n_drives, int flag); 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); int burn_drive_is_open(struct burn_drive *d); int burn_drive_is_occupied(struct burn_drive *d); int burn_drive_forget(struct burn_drive *d, int force); int burn_drive_convert_fs_adr_sub(char *path, char adr[], int *rec_count); /* ts A61021 : the unspecific part of sg.c:enumerate_common() */ int burn_setup_drive(struct burn_drive *d, char *fname); /* ts A61021 : after-setup activities from sg.c:enumerate_common() */ struct burn_drive *burn_drive_finish_enum(struct burn_drive *d); /* ts A61125 : media status aspects of burn_drive_grab() */ int burn_drive_inquire_media(struct burn_drive *d); /* ts A61125 : model aspects of burn_drive_release */ int burn_drive_mark_unready(struct burn_drive *d, int flag); /* ts A61226 */ int burn_speed_descriptor_new(struct burn_speed_descriptor **s, struct burn_speed_descriptor *prev, struct burn_speed_descriptor *next, int flag); /* ts A61226 */ /* @param flag bit0= destroy whole next-chain of descriptors */ int burn_speed_descriptor_destroy(struct burn_speed_descriptor **s, int flag); /* ts A61226 : free dynamically allocated sub data of struct scsi_mode_data */ int burn_mdata_free_subs(struct scsi_mode_data *m); /* ts A61230 */ void burn_disc_format_sync(struct burn_drive *d, off_t size, int flag); /* ts A70207 : evaluate write mode related peculiarities of a disc */ struct burn_disc_mode_demands { int multi_session; int multi_track; int unknown_track_size; /* 0=known, 1=unknown, 2=unknown+defaulted */ int mixed_mode; int audio; int exotic_track; int block_types; int will_append; /* because of media state or multi session disc */ }; int burn_disc_get_write_mode_demands(struct burn_disc *disc, struct burn_write_opts *opts, struct burn_disc_mode_demands *result, int flag); /* ts A70924 : convert a special stdio address into fd number. @return >0 is a valid fd , -1 indicates unsuitable address string. */ int burn_drive__fd_from_special_adr(char *adr); /* ts A70929 : Find the drive which is being worked on by pid , tid */ int burn_drive_find_by_thread_pid(struct burn_drive **d, pid_t pid, pthread_t tid); /* ts A51221 - A80731 : Whitelist inquiry functions */ int burn_drive_is_banned(char *device_address); int burn_drive_whitelist_count(void); char *burn_drive_whitelist_item(int idx, int flag); /* ts A80801 */ int burn_drive_is_listed(char *path, struct burn_drive **found, int flag); /* ts B00226 : Outsourced backend of burn_abort() @param elapsed to be subtracted from start time @param flag bit0= do not shutdown the library */ int burn_abort_5(int patience, int (*pacifier_func)(void *handle, int patience, int elapsed), void *handle, int elapsed, int flag); /* ts B10730 */ /* Send a default mode page 05 to CD and DVD-R-oids */ int burn_drive_send_default_page_05(struct burn_drive *d, int flag); /* ts B40106 */ int burn_feature_descr_new(struct burn_feature_descr **new, unsigned char *descr, int descr_len, int flag); /* ts B40106 */ int burn_feature_descr_free(struct burn_feature_descr **new, int flag); /* ts B40107 */ int burn_drive_has_feature(struct burn_drive *d, int feature_code, struct burn_feature_descr **descr, int flag); int burn_drive_grab_stdio(struct burn_drive *d, int flag); /* ts C10213 */ /* The size of limitless or oversized devices as pseudo drives */ /* Do not lightheartedly change this value because of its meaning to burn_drive.media_read_capacity in libburn/transport.h 64 TiB = 2 exp 46 = 2 exp 35 blocks */ #define BURN_DRIVE_MAX_BYTES ((off_t) (0x800000000) * (off_t) 2048) #endif /* __DRIVE */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /* ts A91016 : libburn/ecma130ab.c is the replacement for old libburn/lec.c Copyright 2009, Thomas Schmitt <scdbackup@gmx.net>, libburnia-project.org Provided under GPL version 2 or later. This code module implements the production of RSPC parity bytes (P- and Q- parity) and the scrambling of raw CD-ROM sectors as specified in ECMA-130: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-130.pdf The following statements about Galois Fields have been learned mostly from http://www.cs.utk.edu/~plank/plank/papers/CS-96-332.pdf by James S. Plank after an e-mail exchange with Norbert Preining. The output has been compared with the output of the old code of libburn which was labeled "borrowed HEAVILY from cdrdao" and claimed by Joerg Schilling to stem from code by Heiko Eissfeldt. ------------------------------------------------------------------------- Note: In this text, "^" denotes exponentiation and not the binary exor operation. Confusingly in the C code "^" is said exor. Note: This is not C1, C2 which is rather mentioned in ECMA-130 Annex C and always performed inside the drive. ------------------------------------------------------------------------- RSPC , P- and Q-Parity ECMA-130 Annex A prescribes to compute the parity bytes for P-columns and Q-diagonals by RSPC based on a Galois Field GF(2^8) with enumerating polynomials x^8+x^4+x^3+x^2+1 (i.e. 0x11d) and x^1 (i.e. 0x02). Bytes 12 to 2075 of a audio-sized sector get ordered in two byte words as 24 rows and 43 columns. Then this matrix is split into a LSB matrix and a MSB matrix of the same layout. Parity bytes are to be computed from these 8-bit values. 2 P-bytes cover each column of 24 bytes. They get appended to the matrix as rows 24 and 25. 2 Q-bytes cover each the 26 diagonals of the extended matrix. Both parity byte pairs have to be computed so that extended rows or diagonals match this linear equation: H x V = (0,0) H is a 2-row matrix of size n matching the length of the V ectors [ 1 1 ... 1 1 ] [ x^(n-1) x^(n-2) x^1 1 ] Vp represents a P-row. It is a byte vector consisting of row bytes at position 0 to 23 and the two parity bytes which shall be determined at position 24 and 25. So Hp has 26 columns. Vq represents a Q-diagonal. It is a byte vector consisting of diagonal bytes at position 0 to 42 and the two parity bytes at position 43 and 44. So Hq has 45 columns. The Q-diagonals cover P-parity bytes. By applying some high school algebra one gets the parity bytes b0, b1 of vector V = (n_payload_bytes, b0 , b1) as b0 = ( H[n] * SUM(n_payload_bytes) - H[0..(n-1)] x n_payload_bytes ) / (H[n+1] - H[n]) b1 = - SUM(n_payload_bytes) - b0 H[i] is the i-the element of the second row of matrix H. E.g. H[0] = x^(n-1) The result has to be computed by Galois field arithmetics. See below. The P-parity bytes of each column get reunited as LSB and MSB of two words. word1 gets written to positions 1032 to 1074, word0 to 1075 to 1117. The Q-parity bytes of each diagonal get reunited too. word1 goes to 1118 to 1143, word0 to 1144 to 1169. >>> I do not read this swap of word1 and word0 from ECMA-130 Annex A. >>> But the new output matches the old output only if it is done that way. >>> See correctness reservation below. Algebra on Galois fields is the same as on Rational Numbers. But arithmetics on its polynomials differ from usual integer arithmetics on binary numbers. Addition and subtraction are identical with the binary exor operator. Multiplication and division would demand polynomial division, e.g. by the euclidean algorithm. The computing path over logarithms and powers follows algebra and reduces the arithmetic task to table lookups, additions modulo 255, and exor operations. Note that the logarithms are natural numbers, not polynomials. They get added or subtracted by the usual addition (not by exor) and their polynomial power depends on their value modulo 255. Needed are a logarithm table and a power table (or inverse logarithm table) for Galois Field GF(2^8) which will serve to perform the peculiar multiplication and division operation of Galois fields. The power table is simply an enumeration of x^n accorting to GF-multiplication. It also serves as second line of matrix H for the parity equations: Hp[i] = gfpow[25-i] , i out of {0,..,25} Hq[i] = gfpow[44-i] , i out of {0,..,44} The logarithm table is the inverse permutation of the power table. Some simplifications apply to the implementation: In the world of Galois fields there is no difference between - and +. The term (H[n+1] - H[n]) is constant: 3. ------------------------------------------------------------------------- Scrambling ECMA-130 Annex B prescribes to exor the byte stream of an audio-sized sector with a sequence of pseudo random bytes. It mentions polynomial x^15+x+1 and a 15-bit register. It shows a diagram of a Feedback Shift Register with 16 bit boxes, though. Comparing this with explanations in http://www.newwaveinstruments.com/resources/articles/m_sequence_linear_feedback_shift_register_lfsr.htm one can recognize the diagram as a Fibonacci Implementation. But there seems really to be one bit box too many. The difference of both lengths is expressed in function next_bit() by the constants 0x3fff,0x4000 for 15 bit versus 0x7fff,0x8000 for 16 bits. Comparing the output of both alternatives with the old scrambler output lets 15 bit win for now. So the prescription is to start with 15 bit value 1, to use the lowest bit as output, to shift the bits down by one, to exor the output bit with the next lowest bit, and to put that exor result into bit 14 of the register. ------------------------------------------------------------------------- Correctness Reservation In both cases, parity and scrambling, the goal for now is to replicate the output of the dismissed old lec.c by output which is based on published specs and own implementation code. Whether they comply to ECMA-130 is a different question which can only be answered by real test cases for raw CD recording. Of course this implementation will be corrected so that it really complies to ECMA-130 as soon as evidence emerges that it does not yet. */ /* ------------------------------------------------------------------------- */ /* Power and logarithm tables for GF(2^8), parity matrices for ECMA-130. Generated by burn_rspc_setup_tables() and burn_rspc_print_tables(). The highest possible sum of gflog[] values is is 508. So the table gfpow[] with period 255 was manually unrolled to 509 elements to avoid one modulo 255 operation in burn_rspc_mult(). Proposed by D. Hugh Redelmeier. */ static unsigned char gfpow[509] = { 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, 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, }; static unsigned char gflog[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 }; #define Libburn_use_h_matriceS 1 #ifdef Libburn_use_h_matriceS /* On my AMD 2x64 bit 3000 MHz processor h[i] costs about 7 % more time than using gfpow[25-i] and gfpow[44-1]. I blame this on the more condensed data representation which slightly increases the rate of cache hits. Nevertheless this effect is very likely depending on the exact cache size and architecture. In general, using h[] saves more than 8000 subtractions per sector. */ /* Parity matrices H as prescribed by ECMA-130 Annex A. Actually just reverted order start pieces of gfpow[]. */ static unsigned char h26[26] = { 3, 143, 201, 234, 117, 180, 90, 45, 152, 76, 38, 19, 135, 205, 232, 116, 58, 29, 128, 64, 32, 16, 8, 4, 2, 1, }; static unsigned char h45[45] = { 238, 119, 181, 212, 106, 53, 148, 74, 37, 156, 78, 39, 157, 192, 96, 48, 24, 12, 6, 3, 143, 201, 234, 117, 180, 90, 45, 152, 76, 38, 19, 135, 205, 232, 116, 58, 29, 128, 64, 32, 16, 8, 4, 2, 1, }; #endif /* Libburn_use_h_matriceS */ /* Pseudo-random bytes which of course are exactly the same as with the previously used code. Generated by function print_ecma_130_scrambler(). */ static unsigned char ecma_130_annex_b[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 }; /* ------------------------------------------------------------------------- */ /* This is the new implementation of P- and Q-parity generation. It needs about the same computing time as the old implementation (both with gcc -O2 on AMD 64 bit). Measurements indicate that about 280 MIPS are needed for 48x CD speed (7.1 MB/s). */ static unsigned char burn_rspc_mult(unsigned char a, unsigned char b) { if (a == 0 || b == 0) return 0; /* Optimization of (a == 0 || b == 0) by D. Hugh Redelmeier if((((int)a - 1) | ((int)b - 1)) < 0) return 0; */ return gfpow[gflog[a] + gflog[b]]; /* % 255 not necessary because gfpow is unrolled up to index 510 */ } /* Divide by polynomial 0x03. Derived from burn_rspc_div() and using the unrolled size of the gfpow[] array. */ static unsigned char burn_rspc_div_3(unsigned char a) { if (a == 0) return 0; return gfpow[230 + gflog[a]]; } static void burn_rspc_p0p1(unsigned char *sector, int col, unsigned char *p0_lsb, unsigned char *p0_msb, unsigned char *p1_lsb, unsigned char *p1_msb) { unsigned char *start, b; unsigned int i, sum_v_lsb = 0, sum_v_msb = 0; unsigned int hxv_lsb = 0, hxv_msb = 0; start = sector + 12 + 2 * col; for(i = 0; i < 24; i++) { b = *start; sum_v_lsb ^= b; #ifdef Libburn_use_h_matriceS hxv_lsb ^= burn_rspc_mult(b, h26[i]); #else hxv_lsb ^= burn_rspc_mult(b, gfpow[25 - i]); #endif b = *(start + 1); sum_v_msb ^= b; #ifdef Libburn_use_h_matriceS hxv_msb ^= burn_rspc_mult(b, h26[i]); #else hxv_msb ^= burn_rspc_mult(b, gfpow[25 - i]); #endif start += 86; } /* 3 = gfpow[1] ^ gfpow[0] , 2 = gfpow[1] */ *p0_lsb = burn_rspc_div_3(burn_rspc_mult(2, sum_v_lsb) ^ hxv_lsb); *p0_msb = burn_rspc_div_3(burn_rspc_mult(2, sum_v_msb) ^ hxv_msb); *p1_lsb = sum_v_lsb ^ *p0_lsb; *p1_msb = sum_v_msb ^ *p0_msb; } void burn_rspc_parity_p(unsigned char *sector) { int i; unsigned char p0_lsb, p0_msb, p1_lsb, p1_msb; /* Loop over P columns */ for(i = 0; i < 43; i++) { burn_rspc_p0p1(sector, i, &p0_lsb, &p0_msb, &p1_lsb, &p1_msb); sector[2162 + 2 * i] = p0_lsb; sector[2162 + 2 * i + 1] = p0_msb; sector[2076 + 2 * i] = p1_lsb; sector[2076 + 2 * i + 1] = p1_msb; #ifdef Libburn_with_lec_generatoR if(verbous) { printf("p %2d : %2.2X %2.2X ", i, (unsigned int) p0_lsb, (unsigned int) p0_msb); printf("%2.2X %2.2X ", (unsigned int) p1_lsb, (unsigned int) p1_msb); printf("-> %d,%d\n", 2162 + 2 * i, 2076 + 2 * i); } #endif /* Libburn_with_lec_generatoR */ } } static void burn_rspc_q0q1(unsigned char *sector, int diag, unsigned char *q0_lsb, unsigned char *q0_msb, unsigned char *q1_lsb, unsigned char *q1_msb) { unsigned char *start, b; unsigned int i, idx, sum_v_lsb = 0, sum_v_msb = 0; unsigned int hxv_lsb = 0, hxv_msb = 0; start = sector + 12; idx = 2 * 43 * diag; for(i = 0; i < 43; i++) { if (idx >= 2236) idx -= 2236; b = start[idx]; sum_v_lsb ^= b; #ifdef Libburn_use_h_matriceS hxv_lsb ^= burn_rspc_mult(b, h45[i]); #else hxv_lsb ^= burn_rspc_mult(b, gfpow[44 - i]); #endif b = start[idx + 1]; sum_v_msb ^= b; #ifdef Libburn_use_h_matriceS hxv_msb ^= burn_rspc_mult(b, h45[i]); #else hxv_msb ^= burn_rspc_mult(b, gfpow[44 - i]); #endif idx += 88; } /* 3 = gfpow[1] ^ gfpow[0] , 2 = gfpow[1] */ *q0_lsb = burn_rspc_div_3(burn_rspc_mult(2, sum_v_lsb) ^ hxv_lsb); *q0_msb = burn_rspc_div_3(burn_rspc_mult(2, sum_v_msb) ^ hxv_msb); *q1_lsb = sum_v_lsb ^ *q0_lsb; *q1_msb = sum_v_msb ^ *q0_msb; } void burn_rspc_parity_q(unsigned char *sector) { int i; unsigned char q0_lsb, q0_msb, q1_lsb, q1_msb; /* Loop over Q diagonals */ for(i = 0; i < 26; i++) { burn_rspc_q0q1(sector, i, &q0_lsb, &q0_msb, &q1_lsb, &q1_msb); sector[2300 + 2 * i] = q0_lsb; sector[2300 + 2 * i + 1] = q0_msb; sector[2248 + 2 * i] = q1_lsb; sector[2248 + 2 * i + 1] = q1_msb; #ifdef Libburn_with_lec_generatoR if(verbous) { printf("q %2d : %2.2X %2.2X ", i, (unsigned int) q0_lsb, (unsigned int) q0_msb); printf("%2.2X %2.2X ", (unsigned int) q1_lsb, (unsigned int) q1_msb); printf("-> %d,%d\n", 2300 + 2 * i, 2248 + 2 * i); } #endif /* Libburn_with_lec_generatoR */ } } /* ------------------------------------------------------------------------- */ /* The new implementation of the ECMA-130 Annex B scrambler. It is totally unoptimized. One should make use of larger word operations. Measurements indicate that about 50 MIPS are needed for 48x CD speed. */ void burn_ecma130_scramble(unsigned char *sector) { int i; unsigned char *s; s = sector + 12; for (i = 0; i < 2340; i++) s[i] ^= ecma_130_annex_b[i]; } /* ------------------------------------------------------------------------- */ /* The following code is not needed for libburn but rather documents the origin of the tables above. In libburn it will not be compiled. */ #ifdef Libburn_with_lec_generatoR /* This function produced the content of gflog[] and gfpow[] */ static int burn_rspc_setup_tables(void) { unsigned int b, l; memset(gflog, 0, sizeof(gflog)); memset(gfpow, 0, sizeof(gfpow)); b = 1; for (l = 0; l < 255; l++) { gfpow[l] = (unsigned char) b; gflog[b] = (unsigned char) l; b = b << 1; if (b & 256) b = b ^ 0x11d; } return 0; } /* This function printed the content of gflog[] and gfpow[] as C code and compared the content with the tables of the old implementation. h26[] and h45[] are reverted order copies of gfpow[] */ static int burn_rspc_print_tables(void) { int i; printf("static unsigned char gfpow[255] = {"); printf("\n\t"); for(i= 0; i < 255; i++) { printf("%3u, ", gfpow[i]); #ifdef Libburn_with_old_lec_comparisoN if(gfpow[i] != gf8_ilog[i]) fprintf(stderr, "*** ILOG %d : %d != %d ***\n", i, gfpow[i], gf8_ilog[i]); #endif if((i % 10) == 9) printf("\n\t"); } printf("\n};\n\n"); printf("static unsigned char gflog[256] = {"); printf("\n\t"); for(i= 0; i < 256; i++) { printf(" %3u,", gflog[i]); #ifdef Libburn_with_old_lec_comparisoN if(gflog[i] != gf8_log[i]) fprintf(stderr, "*** LOG %d : %d != %d ***\n", i, gflog[i], gf8_log[i]); #endif if((i % 10) == 9) printf("\n\t"); } printf("\n};\n\n"); printf("static unsigned char h26[26] = {"); printf("\n\t"); for(i= 0; i < 26; i++) { printf(" %3u,", gfpow[25 - i]); if((i % 10) == 9) printf("\n\t"); } printf("\n};\n\n"); printf("static unsigned char h45[45] = {"); printf("\n\t"); for(i= 0; i < 45; i++) { printf(" %3u,",gfpow[44 - i]); if((i % 10) == 9) printf("\n\t"); } printf("\n};\n\n"); return 0; } /* This code was used to generate the content of array ecma_130_annex_b[]. */ static unsigned short ecma_130_fsr = 1; static int next_bit(void) { int ret; ret = ecma_130_fsr & 1; ecma_130_fsr = (ecma_130_fsr >> 1) & 0x3fff; if (ret ^ (ecma_130_fsr & 1)) ecma_130_fsr |= 0x4000; return ret; } static int print_ecma_130_scrambler(void) { int i, j, b; ecma_130_fsr = 1; printf("static unsigned char ecma_130_annex_b[2340] = {"); printf("\n\t"); for (i = 0; i < 2340; i++) { b = 0; for (j = 0; j < 8; j++) b |= next_bit() << j; printf("%3u, ", b); if ((i % 10) == 9) printf("\n\t"); } printf("\n};\n"); return 1; } #ifdef Libburn_with_general_rspc_diV /* This is a general polynomial division function. burn_rspc_div_3() has been derived from this by setting b to constant 3. */ static unsigned char burn_rspc_div(unsigned char a, unsigned char b) { int d; if (a == 0) return 0; if (b == 0) return -1; d = gflog[a] - gflog[b]; if (d < 0) d += 255; return gfpow[d]; } #endif /* Libburn_with_general_rspc_diV */ #endif /* Libburn_with_lec_generatoR */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* ts A91016 : libburn/ecma130ab.h is the replacement for old libburn/lec.h Copyright 2009, Thomas Schmitt <scdbackup@gmx.net>, libburnia-project.org Provided under GPL version 2 or later. This code module implements the computations prescribed in ECMA-130 Annex A and B. For explanations of the underlying mathematics see ecma130ab.c . */ #ifndef Libburn_ecma130ab_includeD #define Libburn_ecma130ab_includeD 1 void burn_rspc_parity_p(unsigned char *sector); void burn_rspc_parity_q(unsigned char *sector); void burn_ecma130_scramble(unsigned char *sector); #endif /* ! Libburn_ecma130ab_includeD */ /* -*- indent-tabs-mode; t; tab-width: 8; c-basic-offset: 8; -*- */ #ifndef __ERROR_H #define __ERROR_H #define BE_CANCELLED 1 #endif /* __ERROR_H */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2017 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <stdlib.h> #include <sys/types.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <time.h> #include <pthread.h> /* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #include "source.h" #include "libburn.h" #include "file.h" #include "async.h" #include "init.h" #include "util.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /* 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 shortcomings of read() in respect to a guaranteed amount of return data. See man 2 read , paragraph "RETURN VALUE". */ 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_file *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 (fs->fixed_size > 0) return fs->fixed_size; if (fstat(fs->datafd, &buf) != 0) return (off_t) 0; if ((buf.st_mode & S_IFMT) != S_IFREG) return (off_t) 0; return (off_t) buf.st_size; } /* ts A70125 */ static int file_set_size(struct burn_source *source, off_t size) { struct burn_source_file *fs = source->data; fs->fixed_size = size; return 1; } struct burn_source *burn_file_source_new(const char *path, const char *subpath) { struct burn_source_file *fs; struct burn_source *src; int fd1 = -1, fd2 = -1; if (!path) return NULL; fd1 = open(path, O_RDONLY | O_BINARY); if (fd1 == -1) return NULL; if (subpath != NULL) { fd2 = open(subpath, O_RDONLY | O_BINARY); if (fd2 == -1) { close(fd1); return NULL; } } fs = calloc(1, sizeof(struct burn_source_file)); /* ts A70825 */ if (fs == NULL) { failure:; close(fd1); if (fd2 >= 0) close(fd2); return NULL; } fs->datafd = fd1; fs->subfd = fd2; /* ts A70125 */ fs->fixed_size = 0; src = burn_source_new(); /* ts A70825 */ if (src == NULL) { free((char *) fs); goto failure; } src->read = file_read; if (subpath) src->read_sub = file_read_sub; src->get_size = file_size; src->set_size = file_set_size; src->free_data = file_free; src->data = fs; return src; } /* ts A70126 : removed class burn_source_fd in favor of burn_source_file */ struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size) { struct burn_source_file *fs; struct burn_source *src; if (datafd == -1) return NULL; fs = burn_alloc_mem(sizeof(struct burn_source_file), 1, 0); if (fs == NULL) /* ts A70825 */ return NULL; fs->datafd = datafd; fs->subfd = subfd; fs->fixed_size = size; src = burn_source_new(); /* ts A70825 */ if (src == NULL) { free((char *) fs); return NULL; } src->read = file_read; if(subfd != -1) src->read_sub = file_read_sub; src->get_size = file_size; src->set_size = file_set_size; src->free_data = file_free; src->data = fs; return src; } /* ts A71003 */ /* ------------------------------ fifo --------------------------- */ /* The fifo mechanism consists of a burn_source proxy which is here, a thread management team which is located in async.c, and a synchronous shoveller which is here. */ static int fifo_sleep(int flag) { static unsigned long sleeptime = 50000; /* 50 ms */ usleep(sleeptime); return 0; } static int fifo_read(struct burn_source *source, unsigned char *buffer, int size) { struct burn_source_fifo *fs = source->data; int ret, todo, rpos, bufsize, diff, counted = 0; if (fs->end_of_consumption) { /* ??? msg: reading has been ended already */; return 0; } if (fs->is_started == 0) { ret = burn_fifo_start(source, 0); if (ret <= 0) { libdax_msgs_submit(libdax_messenger, -1, 0x00020152, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Cannot start fifo thread", 0, 0); fs->end_of_consumption = 1; return -1; } fs->is_started = 1; } if (size == 0) return 0; /* Reading from the ring buffer */ /* This needs no mutex because each volatile variable has one thread which may write and the other which only reads and is aware of volatility. The feeder of the ringbuffer is in burn_fifo_source_shoveller(). */ todo = size; bufsize = fs->chunksize * fs->chunks; while (todo > 0) { /* readpos is not volatile here , writepos is volatile */ rpos = fs->buf_readpos; while (rpos == fs->buf_writepos) { if (fs->end_of_input) break; if (fs->input_error) { if (todo < size) /* deliver partial buffer */ break; fs->end_of_consumption = 1; libdax_msgs_submit(libdax_messenger, -1, 0x00020154, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, "Forwarded input error ends output", 0, 0); return -1; } if (!counted) fs->empty_counter++; counted = 1; fifo_sleep(0); } diff = fs->buf_writepos - rpos; /* read volatile only once */ if (diff == 0) break; if (diff > 0) /* diff bytes are available */; else /* at least (bufsize - rpos) bytes are available */ diff = bufsize - rpos; if (diff > todo) diff = todo; memcpy(buffer, fs->buf+(size-todo)+rpos, diff); fs->buf_readpos += diff; if (fs->buf_readpos >= bufsize) fs->buf_readpos = 0; todo -= diff; } if (size - todo <= 0) fs->end_of_consumption = 1; else fs->out_counter += size - todo; /* fprintf(stderr, "libburn_EXPERIMENTAL: read= %d , pos= %d , out_count= %.f\n", (size - todo), fs->buf_readpos, (double) fs->out_counter); */ fs->get_counter++; return (size - todo); } static off_t fifo_get_size(struct burn_source *source) { struct burn_source_fifo *fs = source->data; return fs->inp->get_size(fs->inp); } static int fifo_set_size(struct burn_source *source, off_t size) { struct burn_source_fifo *fs = source->data; return fs->inp->set_size(fs->inp, size); } static void fifo_free(struct burn_source *source) { struct burn_source_fifo *fs = source->data; int wait_count; static int wait_max = 30, wait_usleep = 100000; burn_fifo_abort(fs, 0); for (wait_count = 0; wait_count <= wait_max; wait_count++) { if (fs->thread_is_valid <= 0) break; if (wait_count < wait_max) usleep(wait_usleep); } if (wait_count > wait_max) { /* The shoveler thread might still be active. If so, it would use invalid or inappropriate memory if the fifo would be disposed now. A memory and resource leak is the better option here. */ libdax_msgs_submit(libdax_messenger, -1, 0x000201ab, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, "Leaving burn_source_fifo object undisposed because it is possibly stuck but alive", 0, 0); return; } if (fs->inp != NULL) burn_source_free(fs->inp); if (fs->buf != NULL) burn_os_free_buffer(fs->buf, ((size_t) fs->chunksize) * (size_t) fs->chunks, 0); free((char *) fs); } int burn_fifo_source_shoveller(struct burn_source *source, int flag) { struct burn_source_fifo *fs = source->data; int ret, bufsize, diff, wpos, rpos, trans_end, free_bytes, fill; int counted; char *bufpt; pthread_t thread_handle_storage; fs->thread_handle= &thread_handle_storage; *((pthread_t *) fs->thread_handle)= pthread_self(); fs->thread_pid = getpid(); fs->thread_is_valid = 1; /* Lock was obtained by async.c:add_worker() */ burn_async_manage_lock(BURN_ASYNC_LOCK_RELEASE); bufsize = fs->chunksize * fs->chunks; while (!fs->end_of_consumption) { if (fs->do_abort) goto emergency_exit; /* wait for enough buffer space available */ wpos = fs->buf_writepos; counted = 0; while (1) { if (fs->do_abort) goto emergency_exit; rpos = fs->buf_readpos; diff = rpos - wpos; trans_end = 0; if (diff == 0) free_bytes = bufsize - 1; else if (diff > 0) free_bytes = diff - 1; else { free_bytes = (bufsize - wpos) + rpos - 1; if (bufsize - wpos < fs->inp_read_size) trans_end = 1; } if (free_bytes >= fs->inp_read_size) break; if (!counted) fs->full_counter++; counted = 1; fifo_sleep(0); } fill = bufsize - free_bytes - 1; if (fill < fs->total_min_fill) fs->total_min_fill = fill; if (fill < fs->interval_min_fill) fs->interval_min_fill = fill; /* prepare the receiving memory */ bufpt = fs->buf + wpos; if (trans_end) { bufpt = burn_os_alloc_buffer( (size_t) fs->inp_read_size, 0); if (bufpt == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00000003, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Out of virtual memory", 0, 0); fs->input_error = ENOMEM; break; } } /* Obtain next chunk */ if (fs->do_abort) goto emergency_exit; if (fs->inp->read != NULL) ret = fs->inp->read(fs->inp, (unsigned char *) bufpt, fs->inp_read_size); else ret = fs->inp->read_xt( fs->inp, (unsigned char *) bufpt, fs->inp_read_size); if (ret == 0) { /* >>> ??? ts B00326 */ /* >>> report EOF of fifo input and fs->in_counter */; break; /* EOF */ } else if (ret < 0) { libdax_msgs_submit(libdax_messenger, -1, 0x00020153, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Read error on fifo input", errno, 0); fs->input_error = errno; if(errno == 0) fs->input_error = EIO; break; } fs->in_counter += ret; fs->put_counter++; /* activate read chunk */ if (fs->do_abort) goto emergency_exit; if (ret > fs->inp_read_size) /* beware of ill custom burn_source */ ret = fs->inp_read_size; if (trans_end) { /* copy to end of buffer */ memcpy(fs->buf + wpos, bufpt, bufsize - wpos); /* copy to start of buffer */ memcpy(fs->buf, bufpt + (bufsize - wpos), fs->inp_read_size - (bufsize - wpos)); burn_os_free_buffer(bufpt, (size_t) fs->inp_read_size, 0); if (ret >= bufsize - wpos) fs->buf_writepos = ret - (bufsize - wpos); else fs->buf_writepos += ret; } else if (fs->buf_writepos + ret == bufsize) fs->buf_writepos = 0; else fs->buf_writepos += ret; /* fprintf(stderr, "[%2.2d%%] ", (int) (100.0 - 100.0 * ((double) free_bytes) / (double) bufsize)); fprintf(stderr, "libburn_EXPERIMENTAL: writepos= %d ,in_count = %.f\n", fs->buf_writepos, (double) fs->in_counter); */ } if (!fs->end_of_consumption) fs->end_of_input = 1; /* wait for end of reading by consumer */; while (fs->buf_readpos != fs->buf_writepos && !fs->end_of_consumption) { if (fs->do_abort) goto emergency_exit; fifo_sleep(0); } /* destroy ring buffer */; if (!fs->end_of_consumption) fs->end_of_consumption = 2; /* Claim stop of consumption */ /* This is not prone to race conditions because either the consumer indicated hangup by fs->end_of_consumption = 1 or the consumer set fs->buf_readpos to a value indicating the buffer is empty. So in both cases the consumer is aware that reading is futile or even fatal. */ if(fs->buf != NULL) burn_os_free_buffer(fs->buf, ((size_t) fs->chunksize) * (size_t) fs->chunks, 0); fs->buf = NULL; emergency_exit:; burn_async_manage_lock(BURN_ASYNC_LOCK_OBTAIN); fs->thread_handle= NULL; fs->thread_is_valid = 0; burn_async_manage_lock(BURN_ASYNC_LOCK_RELEASE); return (fs->input_error == 0); } int burn_fifo_cancel(struct burn_source *source) { int ret; struct burn_source_fifo *fs = source->data; ret = burn_source_cancel(fs->inp); return ret; } /* @param flag bit0= allow larger read chunks */ struct burn_source *burn_fifo_source_new(struct burn_source *inp, int chunksize, int chunks, int flag) { struct burn_source_fifo *fs; struct burn_source *src; if (((double) chunksize) * ((double) chunks) > 1024.0*1024.0*1024.0) { libdax_msgs_submit(libdax_messenger, -1, 0x00020155, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Desired fifo buffer too large (> 1GB)", 0, 0); return NULL; } if (chunksize < 1 || chunks < 2) { libdax_msgs_submit(libdax_messenger, -1, 0x00020156, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Desired fifo buffer too small", 0, 0); return NULL; } fs = burn_alloc_mem(sizeof(struct burn_source_fifo), 1, 0); if (fs == NULL) return NULL; fs->is_started = 0; fs->thread_handle = NULL; fs->thread_pid = 0; fs->thread_is_valid = 0; fs->do_abort = 0; fs->inp = NULL; /* set later */ if (flag & 1) fs->inp_read_size = 32 * 1024; else fs->inp_read_size = chunksize; fs->chunksize = chunksize; fs->chunks = chunks; fs->buf = NULL; fs->buf_writepos = fs->buf_readpos = 0; fs->end_of_input = 0; fs->input_error = 0; fs->end_of_consumption = 0; fs->in_counter = fs->out_counter = 0; fs->total_min_fill = fs->interval_min_fill = 0; fs->put_counter = fs->get_counter = 0; fs->empty_counter = fs->full_counter = 0; src = burn_source_new(); if (src == NULL) { free((char *) fs); return NULL; } src->read = NULL; src->read_sub = NULL; src->get_size = fifo_get_size; src->set_size = fifo_set_size; src->free_data = fifo_free; src->data = fs; src->version= 1; src->read_xt = fifo_read; src->cancel= burn_fifo_cancel; fs->inp = inp; inp->refcount++; /* make sure inp lives longer than src */ return src; } /* ts A71003 : API */ int burn_fifo_inquire_status(struct burn_source *source, int *size, int *free_bytes, char **status_text) { struct burn_source_fifo *fs = source->data; int ret = 0, diff, wpos, rpos; static char *(states[8]) = { "standby", "active", "ending", "failing", "unused", "abandoned", "ended", "aborted"}; *status_text = NULL; *size = 0; if (source->free_data != fifo_free) { libdax_msgs_submit(libdax_messenger, -1, 0x00020157, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "burn_source is not a fifo object", 0, 0); return -1; } *size = fs->chunksize * fs->chunks; rpos = fs->buf_readpos; wpos = fs->buf_writepos; diff = rpos - wpos; if (diff == 0) *free_bytes = *size - 1; else if (diff > 0) *free_bytes = diff - 1; else *free_bytes = (*size - wpos) + rpos - 1; ret = 0; if (fs->end_of_consumption > 0) ret |= 4; if (fs->input_error) ret |= 3; else if (fs->end_of_input) ret |= 2; else if(fs->buf != NULL) ret |= 1; *status_text = states[ret]; return ret; } /* ts A91125 : API */ void burn_fifo_get_statistics(struct burn_source *source, int *total_min_fill, int *interval_min_fill, int *put_counter, int *get_counter, int *empty_counter, int *full_counter) { struct burn_source_fifo *fs = source->data; *total_min_fill = fs->total_min_fill; *interval_min_fill = fs->interval_min_fill; *put_counter = fs->put_counter; *get_counter = fs->get_counter; *empty_counter = fs->empty_counter; *full_counter = fs->full_counter; } /* ts A91125 : API */ void burn_fifo_next_interval(struct burn_source *source, int *interval_min_fill) { struct burn_source_fifo *fs = source->data; int size, free_bytes, ret; char *status_text; *interval_min_fill = fs->interval_min_fill; ret = burn_fifo_inquire_status(source, &size, &free_bytes, &status_text); if (ret < 0) return; fs->interval_min_fill = size - free_bytes - 1; } /* @param flag bit0= do not copy to buf but only wait until the fifo has read bufsize or input ended. The same happens if buf is NULL. bit1= fill to max fifo size */ int burn_fifo_fill_data(struct burn_source *source, char *buf, int bufsize, int flag) { int size, free_bytes, ret, wait_count= 0; char *status_text; struct burn_source_fifo *fs = source->data; if (buf == NULL) flag |= 1; /* Eventually start fifo thread by reading 0 bytes */ ret = fifo_read(source, (unsigned char *) NULL, 0); if (ret<0) {ret = 0; goto ex;} /* wait for at least bufsize bytes being ready */ while (1) { ret= burn_fifo_inquire_status(source, &size, &free_bytes, &status_text); if (flag & 2) { bufsize = size - (size % fs->inp_read_size) - fs->inp_read_size; if (bufsize <= 0) {ret = 0; goto ex;} } if (size - fs->inp_read_size < bufsize) { if (flag & 1) { bufsize = size - (size % fs->inp_read_size) - fs->inp_read_size; if (bufsize <= 0) {ret = 0; goto ex;} } else { libdax_msgs_submit(libdax_messenger, -1, 0x0002015c, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Fifo size too small for desired peek buffer", 0, 0); {ret = -1; goto ex;} } } if (fs->out_counter > 0 || (ret & 4) || fs->buf == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x0002015e, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Fifo is already under consumption when peeking is desired", 0, 0); {ret = -1; goto ex;} } if(size - free_bytes >= bufsize) { /* <<< fprintf(stderr, "libburn_DEBUG: after waiting cycle %d : fifo %s , %d bytes\n", wait_count, status_text, size - free_bytes); */ if(!(flag & 1)) memcpy(buf, fs->buf, bufsize); {ret = 1; goto ex;} } if (ret & 2) { /* input has ended, not enough data arrived */ if (flag & 1) {ret = 0; goto ex;} libdax_msgs_submit(libdax_messenger, -1, 0x0002015d, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Fifo input ended short of desired peek buffer size", 0, 0); {ret = 0; goto ex;} } if (free_bytes < fs->inp_read_size) { /* Usable fifo size filled, not enough data arrived */ if (flag & 1) {ret = 0; goto ex;} libdax_msgs_submit(libdax_messenger, -1, 0x00020174, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Fifo alignment does not allow desired read size", 0, 0); {ret = 0; goto ex;} } usleep(100000); wait_count++; /* <<< if(wait_count%10==0) fprintf(stderr, "libburn_DEBUG: waiting cycle %d : fifo %s , %d bytes\n", wait_count, status_text, size - free_bytes); */ } ret = 0; ex:; fs->total_min_fill = fs->interval_min_fill = fs->buf_writepos; return(ret); } /* ts A80713 : API */ int burn_fifo_peek_data(struct burn_source *source, char *buf, int bufsize, int flag) { return burn_fifo_fill_data(source, buf, bufsize, 0); } /* ts A91125 : API */ int burn_fifo_fill(struct burn_source *source, int bufsize, int flag) { return burn_fifo_fill_data(source, NULL, bufsize, 1 | ((flag & 1) << 1)); } /* ----------------------------- Offset source ----------------------------- */ /* ts B00922 */ static void offst_free(struct burn_source *source); /* @param flag bit0 = do not check for burn_source_offst, do not return NULL */ static struct burn_source_offst *offst_auth(struct burn_source *source, int flag) { if (source->free_data != offst_free && !(flag & 1)) { libdax_msgs_submit(libdax_messenger, -1, 0x0002017a, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Expected offset source object as parameter", 0, 0); return NULL; } return (struct burn_source_offst *) source->data; } static off_t offst_get_size(struct burn_source *source) { struct burn_source_offst *fs; if ((fs = offst_auth(source, 0)) == NULL) return (off_t) 0; return fs->nominal_size; } static int offst_set_size(struct burn_source *source, off_t size) { struct burn_source_offst *fs; if ((fs = offst_auth(source, 0)) == NULL) return 0; fs->nominal_size = size; if (fs->size <= 0 || fs->size_adjustable) fs->size = size; return 1; } static void offst_free(struct burn_source *source) { struct burn_source_offst *fs; if ((fs = offst_auth(source, 0)) == NULL) return; if (fs->prev != NULL) offst_auth(fs->prev, 1)->next = fs->next; if (fs->next != NULL) offst_auth(fs->next, 1)->prev = fs->prev; if (fs->inp != NULL) burn_source_free(fs->inp); /* i.e. decrement refcount */ free(source->data); } static int offst_read(struct burn_source *source, unsigned char *buffer, int size) { int ret, to_read, todo; struct burn_source_offst *fs; if ((fs = offst_auth(source, 0)) == NULL) return -1; /* Eventually skip bytes up to start position */; if (!fs->running) { if (fs->prev != NULL) fs->pos = offst_auth(fs->prev, 1)->pos; fs->running= 1; } if(fs->pos < fs->start) { todo = fs->start - fs->pos; while (todo > 0) { to_read = todo; if (to_read > size) to_read = size; ret = burn_source_read(fs->inp, buffer, to_read); if (ret <= 0) return ret; todo -= ret; fs->pos += ret; } } /* Produce EOF if source size is exhausted. burn_source delivers no incomplete sector buffers. */ if (fs->pos + size > fs->start + fs->size) return 0; /* Read payload */ ret = burn_source_read(fs->inp, buffer, size); if (ret > 0) fs->pos += ret; return ret; } static int offst_cancel(struct burn_source *source) { int ret; struct burn_source_offst *fs; if ((fs = offst_auth(source, 0)) == NULL) return -1; ret = burn_source_cancel(fs->inp); return ret; } struct burn_source *burn_offst_source_new( struct burn_source *inp, struct burn_source *prev, off_t start, off_t size, int flag) { struct burn_source *src; struct burn_source_offst *fs, *prev_fs = NULL; if (prev != NULL) if ((prev_fs = offst_auth(prev, 0)) == NULL) return NULL; /* Not type burn_source_offst */ fs = calloc(1, sizeof(struct burn_source_offst)); if (fs == NULL) return NULL; src = burn_source_new(); if (src == NULL) { free((char *) fs); return NULL; } src->read = NULL; src->read_sub = NULL; src->get_size = offst_get_size; src->set_size = offst_set_size; src->free_data = offst_free; src->data = fs; src->version= 1; src->read_xt = offst_read; src->cancel= offst_cancel; fs->inp = inp; fs->prev = prev; fs->next = NULL; if (prev != NULL) { if (prev_fs->next != NULL) { offst_auth(prev_fs->next, 1)->prev = src; fs->next = prev_fs->next; } prev_fs->next = src; if (prev_fs->start + prev_fs->size > start) { libdax_msgs_submit(libdax_messenger, -1, 0x00020179, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Offset source start address is before end of previous source", 0, 0); return NULL; } } fs->start = start; fs->size = size; fs->size_adjustable = !(flag & 1); fs->nominal_size = size; fs->running = 0; fs->pos = 0; inp->refcount++; /* make sure inp lives longer than src */ return src; } /* -------------------- WAVE file extractor ------------------- */ /* ts B30522 */ /* API @param flag Bitfield for control purposes: bit0= Report about progress by UPDATE message bit3= Enable DAP : "flaw obscuring mechanisms like audio data mute and interpolate" */ int burn_drive_extract_audio(struct burn_drive *drive, int start_sector, int sector_count, char *target_path, int flag) { int fd = -1, ret, todo, sector_no, val, min, sec, fr; int sectors_done= 0; off_t data_size, data_count = 0; time_t last_pacified = 0, now; char *msg = NULL, *buf = NULL; BURN_ALLOC_MEM(msg, char, 4096); BURN_ALLOC_MEM(buf, char, 24 * 2352); fd = open(target_path, O_WRONLY | O_CREAT | O_BINARY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (fd == -1) { sprintf(msg, "Cannot open disk file for writing: %.4000s", target_path); libdax_msgs_submit(libdax_messenger, -1, 0x000201a1, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); ret = 0; goto ex; } /* WAV header */ strcpy(buf, "RIFF"); val = 4 + 8 + 16 + 8 + sector_count * 2352; /* ChunkSize */ burn_int_to_lsb(val, buf + 4); strcpy(buf + 8, "WAVE"); strcpy(buf + 12, "fmt "); burn_int_to_lsb(16, buf + 16); /* Subchunk1Size */ buf[20] = 1; /* AudioFormat */ buf[21] = 0; buf[22] = 2; /* NumChannels */ buf[23] = 0; burn_int_to_lsb(44100, buf + 24); /* SampleRate */ burn_int_to_lsb(176400, buf + 28); /* ByteRate */ buf[32] = 4; /* BlockAlign */ buf[33] = 0; buf[34] = 16; /* BitsPerSample */ buf[35] = 0; strcpy(buf + 36, "data"); burn_int_to_lsb(sector_count * 2352, buf + 40); /* Subchunk2Size */ ret = write(fd, buf, 44); if (ret == -1) goto write_error; /* Audio data */ todo = sector_count; sector_no = start_sector; while (todo > 0) { if (todo > 24) data_size = 24 * 2352; else data_size = todo * 2352; ret = burn_read_audio(drive, sector_no, buf, data_size, &data_count, flag & 8); if (ret <= 0) { sprintf(msg, "Failure to read audio sectors"); libdax_msgs_submit(libdax_messenger, -1, 0x000201a4, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); goto ex; } ret = write(fd, buf, data_count); if (ret == -1) { write_error:; sprintf(msg, "Error while writing to disk file: %.4000s", target_path); libdax_msgs_submit(libdax_messenger, -1, 0x000201a2, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); ret = 0; goto ex; } todo -= data_count / 2352; sectors_done += data_count / 2352; sector_no += data_count / 2352; if ((flag & 1) && (now = time(NULL)) - last_pacified >= 1) { last_pacified = now; burn_lba_to_msf(sectors_done, &min, &sec, &fr); sprintf(msg, "Minutes:seconds of audio data read: %2d:%2.2d (%6.2f MB)", min, sec, ((double) sectors_done) * 2352.0 / 1048576.0); libdax_msgs_submit(libdax_messenger, -1, 0x000201a3, LIBDAX_MSGS_SEV_UPDATE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 1); } } if ((flag & 1)) { burn_lba_to_msf(sectors_done, &min, &sec, &fr); sprintf(msg, "Minutes:seconds of audio data read: %2d:%2.2d (%6.2f MB)", min, sec, ((double) sectors_done) * 2352.0 / 1048576.0); libdax_msgs_submit(libdax_messenger, -1, 0x000201a3, LIBDAX_MSGS_SEV_UPDATE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } ret = 1; ex:; BURN_FREE_MEM(buf); BURN_FREE_MEM(msg); if (fd != -1) close(fd); return ret; } /* ts B30522 */ /* API @param flag Bitfield for control purposes: bit0= Report about progress by UPDATE message bit3= Enable DAP : "flaw obscuring mechanisms like audio data mute and interpolate" */ int burn_drive_extract_audio_track(struct burn_drive *drive, struct burn_track *track, char *target_path, int flag) { int ret; struct burn_toc_entry toc_entry; burn_track_get_entry(track, &toc_entry); if (!(toc_entry.extensions_valid & 1)) { /* Can only happen if burn_disc_cd_toc_extensions() is skipped in mmc_read_toc_al(). */ libdax_msgs_submit(libdax_messenger, -1, 0x00000004, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Internal libburn error: Outdated burn_toc_entry format encountered", errno, 0); return -1; } ret = burn_drive_extract_audio(drive, toc_entry.start_lba, toc_entry.track_blocks, target_path, flag & (1 | 8)); return ret; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2017 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifndef BURN__FILE_H #define BURN__FILE_H struct burn_source_file { char magic[4]; int datafd; int subfd; off_t fixed_size; }; /* ts A70126 : burn_source_file obsoleted burn_source_fd */ /* ts A70930 */ struct burn_source_fifo { char magic[4]; /* The fifo stays inactive and unequipped with eventual resources until its read() method is called for the first time. Only then burn_fifo_start() gets called, allocates the complete resources, starts a thread with burn_fifo_source_shoveller() which shovels data and finally destroys the resources. This late start is to stay modest in case of multiple tracks in one disc. */ int is_started; void *thread_handle; /* actually a pointer to a thread_t */ int thread_pid; int thread_is_valid; /* The shoveller aborts if this is 1. Resource leaks are possible. */ volatile int do_abort; /* the burn_source for which this fifo is acting as proxy */ struct burn_source *inp; int inp_read_size; /* <<< up to now it was only a pipe. This is on its way out. */ int outlet[2]; /* The ring buffer mechanism */ int chunksize; int chunks; char *buf; volatile int buf_writepos; volatile int buf_readpos; volatile int end_of_input; volatile int input_error; volatile int end_of_consumption; off_t in_counter; off_t out_counter; int total_min_fill; int interval_min_fill; int put_counter; int get_counter; int empty_counter; int full_counter; }; /** The worker behind the fifo thread. Gets started from burn_fifo_start() in async.c */ int burn_fifo_source_shoveller(struct burn_source *source, int flag); /* ts B00922 */ struct burn_source_offst { /* See burn_offst_source_new() */ struct burn_source *inp; struct burn_source *prev; off_t start; off_t size; int size_adjustable; /* for set_size/get_size */ off_t nominal_size; /* To help offst_free() */ struct burn_source *next; /* The current reading position */ int running; off_t pos; }; #endif /* LIBBURN__FILE_H */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2013 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <unistd.h> /* ts A61007 */ /* #include <a ssert.h> */ #include <stdio.h> #include <signal.h> #include <string.h> #include <stdlib.h> #include <pthread.h> /* ts A70928 : init.h is for others, not for init .c #include "init.h" */ #include "sg.h" #include "error.h" #include "libburn.h" #include "drive.h" #include "transport.h" #include "util.h" /* ts A60825 : The storage location for back_hacks.h variables. */ #define BURN_BACK_HACKS_INIT 1 #include "back_hacks.h" /* ts A60924 : a new message handling facility */ #include "libdax_msgs.h" struct libdax_msgs *libdax_messenger= NULL; int burn_running = 0; double lib_start_time; /* ts A60813 : GNU/Linux: whether to use O_EXCL on open() of device files ts B00212 : FreeBSD: whether to use flock(LOCK_EX) after open() */ int burn_sg_open_o_excl = 1; /* ts A70403 : GNU/Linux: whether to use fcntl(,F_SETLK,) after open() of device files */ int burn_sg_fcntl_f_setlk = 1; /* ts A70314 : GNU/Linux: what device family to use : 0= default family 1= sr 2= scd (3= st) 4= sg */ int burn_sg_use_family = 0; /* O_NONBLOCK was hardcoded in enumerate_ata() which i hardly use. For enumerate_sg() it seems ok. So it should stay default mode until enumerate_ata() without O_NONBLOCK has been thoroughly tested. */ int burn_sg_open_o_nonblock = 1; /* whether to take a busy drive as an error */ /* Caution: this is implemented by a rough hack and eventually leads to unconditional abort of the process */ int burn_sg_open_abort_busy = 0; /* The message returned from sg_id_string() and/or sg_initialize() */ static char sg_initialize_msg[1024] = {""}; /* ts A61002 */ #include "cleanup.h" /* Parameters for builtin abort handler */ static char abort_message_prefix[81] = {"libburn : "}; static pid_t abort_control_pid= 0; static pthread_t abort_control_thread; volatile int burn_global_abort_level= 0; int burn_global_abort_signum= 0; void *burn_global_signal_handle = NULL; burn_abort_handler_t burn_global_signal_handler = NULL; int burn_builtin_signal_action = 0; /* burn_set_signal_handling() */ volatile int burn_builtin_triggered_action = 0; /* burn_is_aborting() */ /* ts A70223 : whether implemented untested profiles are supported */ int burn_support_untested_profiles = 0; /* ts A91111 : whether to log SCSI commands (to be implemented in sg-*.c) bit0= log in /tmp/libburn_sg_command_log bit1= log to stderr bit2= flush every line */ int burn_sg_log_scsi = 0; /* ts B10312 : Whether to map random-access readonly files to drive role 4. Else it is role 2 overwritable drive */ int burn_drive_role_4_allowed = 0; /* ts A60925 : ticket 74 */ /** Create the messenger object for libburn. */ int burn_msgs_initialize(void) { int ret; if(libdax_messenger == NULL) { ret = libdax_msgs_new(&libdax_messenger,0); if (ret <= 0) return 0; } libdax_msgs_set_severities(libdax_messenger, LIBDAX_MSGS_SEV_NEVER, LIBDAX_MSGS_SEV_FATAL, "libburn: ", 0); return 1; } /* ts A60924 : ticket 74 : Added use of global libdax_messenger */ int burn_initialize(void) { int ret; if (burn_running) return 1; lib_start_time = burn_get_time(0); burn_support_untested_profiles = 0; ret = burn_msgs_initialize(); if (ret <= 0) return 0; ret = sg_initialize(sg_initialize_msg, 0); if (ret <= 0) { libdax_msgs_submit(libdax_messenger, -1, 0x00020175, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, sg_initialize_msg, 0, 0); return 0; } burn_running = 1; return 1; } void burn_finish(void) { /* ts A61007 : assume no messageing system */ /* a ssert(burn_running); */ if (!burn_running) return; /* ts A61007 */ /* burn_wait_all(); */ if (!burn_drives_are_clear(0)) { libdax_msgs_submit(libdax_messenger, -1, 0x00020107, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, "A drive is still busy on shutdown of library", 0, 0); usleep(1000001); burn_abort(4440, burn_abort_pacifier, abort_message_prefix); } /* ts A60904 : ticket 62, contribution by elmom : name addon "_all" */ burn_drive_free_all(); /* ts A60924 : ticket 74 */ libdax_msgs_destroy(&libdax_messenger,0); sg_shutdown(0); burn_drive_clear_whitelist(); burn_running = 0; } /* ts A91226 */ /** API function. See libburn.h */ char *burn_scsi_transport_id(int flag) { if (!burn_running) sg_id_string(sg_initialize_msg, 0); return sg_initialize_msg; } /* ts A60813 */ /** API function. See libburn.h */ void burn_preset_device_open(int exclusive, int blocking, int abort_on_busy) { /* ts A61007 */ /* a ssert(burn_running); */ if (!burn_running) return; burn_sg_open_o_excl = exclusive & 3; burn_sg_fcntl_f_setlk = !!(exclusive & 32); burn_sg_use_family = (exclusive >> 2) & 7; burn_sg_open_o_nonblock = !blocking; burn_sg_open_abort_busy = !!abort_on_busy; } /* ts A60924 : ticket 74 */ /** Control queueing and stderr printing of messages from libburn. Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", "NOTE", "UPDATE", "DEBUG", "ALL". @param queue_severity Gives the minimum limit for messages to be queued. Default: "NEVER". If you queue messages then you must consume them by burn_msgs_obtain(). @param print_severity Does the same for messages to be printed directly to stderr. @param print_id A text prefix to be printed before the message. @return >0 for success, <=0 for error */ int burn_msgs_set_severities(char *queue_severity, char *print_severity, char *print_id) { int ret, queue_sevno, print_sevno; ret = libdax_msgs__text_to_sev(queue_severity, &queue_sevno, 0); if (ret <= 0) return 0; ret = libdax_msgs__text_to_sev(print_severity, &print_sevno, 0); if (ret <= 0) return 0; ret = libdax_msgs_set_severities(libdax_messenger, queue_sevno, print_sevno, print_id, 0); if (ret <= 0) return 0; return 1; } /* ts A60924 : ticket 74 */ #define BURM_MSGS_MESSAGE_LEN 4096 /** Obtain the oldest pending libburn message from the queue which has at least the given minimum_severity. This message and any older message of lower severity will get discarded from the queue and is then lost forever. Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", "NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER" will discard the whole queue. @param error_code Will become a unique error code as liste in libburn/libdax_msgs.h @param msg_text Must provide at least BURM_MSGS_MESSAGE_LEN bytes. @param os_errno Will become the eventual errno related to the message @param severity Will become the severity related to the message and should provide at least 80 bytes. @return 1 if a matching item was found, 0 if not, <0 for severe errors */ int burn_msgs_obtain(char *minimum_severity, int *error_code, char msg_text[], int *os_errno, char severity[]) { int ret, minimum_sevno, sevno, priority; char *textpt, *sev_name; struct libdax_msgs_item *item = NULL; ret = libdax_msgs__text_to_sev(minimum_severity, &minimum_sevno, 0); if (ret <= 0) return 0; if (libdax_messenger == NULL) return 0; ret = libdax_msgs_obtain(libdax_messenger, &item, minimum_sevno, LIBDAX_MSGS_PRIO_ZERO, 0); if (ret <= 0) goto ex; ret = libdax_msgs_item_get_msg(item, error_code, &textpt, os_errno, 0); if (ret <= 0) goto ex; strncpy(msg_text, textpt, BURM_MSGS_MESSAGE_LEN-1); if(strlen(textpt) >= BURM_MSGS_MESSAGE_LEN) msg_text[BURM_MSGS_MESSAGE_LEN-1] = 0; severity[0]= 0; ret = libdax_msgs_item_get_rank(item, &sevno, &priority, 0); if(ret <= 0) goto ex; ret = libdax_msgs__sev_to_text(sevno, &sev_name, 0); if(ret <= 0) goto ex; strcpy(severity,sev_name); ret = 1; ex: libdax_msgs_destroy_item(libdax_messenger, &item, 0); return ret; } /* ts A70922 : API */ int burn_msgs_submit(int error_code, char msg_text[], int os_errno, char severity[], struct burn_drive *d) { int ret, sevno, global_index = -1; ret = libdax_msgs__text_to_sev(severity, &sevno, 0); if (ret <= 0) sevno = LIBDAX_MSGS_SEV_ALL; if (error_code <= 0) { switch(sevno) { case LIBDAX_MSGS_SEV_ABORT: error_code = 0x00040000; break; case LIBDAX_MSGS_SEV_FATAL: error_code = 0x00040001; break; case LIBDAX_MSGS_SEV_SORRY: error_code = 0x00040002; break; case LIBDAX_MSGS_SEV_WARNING: error_code = 0x00040003; break; case LIBDAX_MSGS_SEV_HINT: error_code = 0x00040004; break; case LIBDAX_MSGS_SEV_NOTE: error_code = 0x00040005; break; case LIBDAX_MSGS_SEV_UPDATE: error_code = 0x00040006; break; case LIBDAX_MSGS_SEV_DEBUG: error_code = 0x00040007; break; default: error_code = 0x00040008; } } if (d != NULL) global_index = d->global_index; ret = libdax_msgs_submit(libdax_messenger, global_index, error_code, sevno, LIBDAX_MSGS_PRIO_HIGH, msg_text, os_errno, 0); return ret; } /* ts A71016 API */ int burn_text_to_sev(char *severity_name, int *sevno, int flag) { int ret; ret = libdax_msgs__text_to_sev(severity_name, sevno, 0); return ret; } /* ts A80202 API */ int burn_sev_to_text(int severity_number, char **severity_name, int flag) { int ret; ret = libdax_msgs__sev_to_text(severity_number, severity_name, 0); return ret; } /* ts B21214 API */ char *burn_list_sev_texts(int flag) { char *sev_list; libdax_msgs__sev_to_text(0, &sev_list, 1); return sev_list; } /* ts B00224 */ char *burn_util_thread_id(pid_t pid, pthread_t tid, char text[80]) { int i, l; sprintf(text, "[%lu,", (unsigned long int) getpid()); l= strlen(text); for(i= 0; i < ((int) sizeof(pthread_t)) && 2 * i < 80 - l - 3; i++) sprintf(text + l + 2 * i, "%2.2X", ((unsigned char *) &tid)[i]); sprintf(text + l + 2 * i, "]"); return text; } /* ts B20122 */ /* @param value 0=return rather than exit(value) */ int burn_abort_exit(int value) { burn_abort(4440, burn_abort_pacifier, abort_message_prefix); fprintf(stderr, "\n%sABORT : Program done. Even if you do not see a shell prompt.\n\n", abort_message_prefix); if (value) exit(value); burn_global_abort_level = -2; return(1); } int burn_builtin_abort_handler(void *handle, int signum, int flag) { #define Libburn_new_thread_signal_handleR 1 /* #define Libburn_signal_handler_verbouS 1 */ int ret; struct burn_drive *d; #ifdef Libburn_signal_handler_verbouS char text[80]; fprintf(stderr, "libburn_ABORT: in = %s\n", burn_util_thread_id(getpid(), pthread_self(), text)); fprintf(stderr, "libburn_ABORT: ctrl = %s\n", burn_util_thread_id(abort_control_pid, abort_control_thread, text)); if (burn_global_signal_handler == burn_builtin_abort_handler) fprintf(stderr, "libburn_ABORT: signal action = %d\n", burn_builtin_signal_action); /* >>> find writing drives and report their tid fprintf(stderr, "libburn_ABORT: wrt = %s\n", burn_util_thread_id(0, burn_write_thread_id, text)); fprintf(stderr, "libburn_ABORT: sig= %d\n", signum); */ #endif burn_builtin_triggered_action = burn_builtin_signal_action; burn_global_abort_level = -1; if (burn_builtin_signal_action > 1) { Cleanup_set_handlers(NULL, NULL, 2); if (burn_builtin_signal_action == 4) return -2; fprintf(stderr,"%sABORT : Trying to shut down busy drives\n", abort_message_prefix); fprintf(stderr, "%sABORT : Wait the normal burning time before any kill -9\n", abort_message_prefix); burn_abort_5(0, burn_abort_pacifier, abort_message_prefix, 0, 1); libdax_msgs_submit(libdax_messenger, -1, 0x00020177, LIBDAX_MSGS_SEV_ABORT, LIBDAX_MSGS_PRIO_HIGH, "Urged drive worker threads to do emergency halt", 0, 0); return -2; } /* ---- old deprecated stuck-in-abort-handler loop ---- */ /* ts A70928: Must be quick. Allowed to coincide with other thread and to share the increment with that one. It must not decrease, though, and yield at least 1 if any thread calls this function. */ burn_global_abort_level++; burn_global_abort_signum= signum; if(getpid() != abort_control_pid) { #ifdef Libburn_new_thread_signal_handleR ret = burn_drive_find_by_thread_pid(&d, getpid(), pthread_self()); if (ret > 0 && d->busy == BURN_DRIVE_WRITING) { /* This is an active writer thread */ #ifdef Libburn_signal_handler_verbouS fprintf(stderr, "libburn_ABORT: pid %d found drive busy with writing, (level= %d)\n", (int) getpid(), burn_global_abort_level); #endif d->sync_cache(d); /* >>> perform a more qualified end of burn process */; d->busy = BURN_DRIVE_IDLE; if (burn_global_abort_level > 0) { /* control process did not show up yet */ #ifdef Libburn_signal_handler_verbouS fprintf(stderr, "libburn_ABORT: pid %d sending signum %d to pid %d\n", (int) getpid(), (int) signum, (int) abort_control_pid); #endif kill(abort_control_pid, signum); } #ifdef Libburn_signal_handler_verbouS fprintf(stderr, "libburn_ABORT: pid %d signum %d returning -2\n", (int) getpid(), (int) signum); #endif return -2; } else { usleep(1000000); /* calm down */ return -2; } #else usleep(1000000); /* calm down */ return -2; #endif /* ! Libburn_new_thread_signal_handleR */ } burn_global_abort_level = -1; Cleanup_set_handlers(NULL, NULL, 2); fprintf(stderr,"%sABORT : Trying to shut down drive and library\n", abort_message_prefix); fprintf(stderr, "%sABORT : Wait the normal burning time before any kill -9\n", abort_message_prefix); close(0); /* somehow stdin as input blocks abort until EOF */ burn_abort_exit(0); return (1); } /* ts A61002 : API */ void burn_set_signal_handling(void *handle, burn_abort_handler_t handler, int mode) { /* fprintf(stderr, "libburn_experimental: burn_set_signal_handling, handler==%lx mode=%d\n", (unsigned long) handler, mode); */ if(handler == NULL) { handler = burn_builtin_abort_handler; /* if ((mode & ~4) == 0) fprintf(stderr, "libburn_experimental: activated burn_builtin_abort_handler() with handle '%s'\n",(handle==NULL ? "libburn : " : (char *) handle)); */ } strcpy(abort_message_prefix, "libburn : "); abort_message_prefix[0] = 0; if(handle != NULL && handler == burn_builtin_abort_handler) strncpy(abort_message_prefix, (char *) handle, sizeof(abort_message_prefix)-1); abort_message_prefix[sizeof(abort_message_prefix)-1] = 0; abort_control_pid = getpid(); abort_control_thread = pthread_self(); burn_builtin_signal_action = (mode >> 4) & 15; if((mode & 11) != 0) burn_builtin_signal_action = 0; if(burn_builtin_signal_action > 1) burn_builtin_triggered_action = 0; if(burn_builtin_signal_action == 0) burn_builtin_signal_action = 1; Cleanup_set_handlers(handle, (Cleanup_app_handler_T) handler, (mode & 15) | 4 | (mode & 256)); burn_global_signal_handle = handle; burn_global_signal_handler = handler; } /* ts B00304 : API */ int burn_is_aborting(int flag) { return burn_builtin_triggered_action; } /* ts B00225 */ /* @return 0= no abort action 2 pending , 1= not control thread */ int burn_init_catch_on_abort(int flag) { if (burn_builtin_triggered_action != 2) return 0; if (abort_control_pid != getpid() || abort_control_thread != pthread_self()) return 1; burn_abort(4440, burn_abort_pacifier, abort_message_prefix); fprintf(stderr, "\n%sABORT : Program done. Even if you do not see a shell prompt.\n\n", abort_message_prefix); exit(1); } /* B20122 */ /* Temporarily disable builtin actions 0,1,2 to avoid that burn_abort() waits for its own thread to end grabbing. */ int burn_grab_prepare_sig_action(int *signal_action_mem, int flag) { *signal_action_mem = -1; if (burn_global_signal_handler == burn_builtin_abort_handler && burn_builtin_signal_action >= 0 && burn_builtin_signal_action <= 2) { *signal_action_mem = burn_builtin_signal_action; burn_builtin_signal_action = 3; } return 1; } /* B20122 */ /* Re-enable builtin actions 0,1,2 and perform delayed signal reactions */ int burn_grab_restore_sig_action(int signal_action_mem, int flag) { if (signal_action_mem >= 0) burn_builtin_signal_action = signal_action_mem; if (burn_is_aborting(0) && signal_action_mem >= 0) { if (signal_action_mem == 0 || signal_action_mem == 1) { burn_abort_exit(1); /* Never comes back */ } else if (signal_action_mem == 2) { burn_builtin_triggered_action = signal_action_mem; } } return 1; } /* ts A70223 : API */ void burn_allow_untested_profiles(int yes) { burn_support_untested_profiles = !!yes; } /* ts A70915 : API */ int burn_set_messenger(void *messenger) { struct libdax_msgs *pt; if (libdax_msgs_refer(&pt, messenger, 0) <= 0) return 0; libdax_msgs_destroy(&libdax_messenger, 0); libdax_messenger = (struct libdax_msgs *) pt; return 1; } /* ts A91111 API */ void burn_set_scsi_logging(int flag) { burn_sg_log_scsi = flag & 7; } /* ts B10312 API */ void burn_allow_drive_role_4(int allowed) { burn_drive_role_4_allowed = (allowed & 0xf); } /* ts B10606 */ void *burn_alloc_mem(size_t size, size_t count, int flag) { void *pt; pt = calloc(count, size); if(pt == NULL) libdax_msgs_submit(libdax_messenger, -1, 0x00000003, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Out of virtual memory", 0, 0); return pt; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2013 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifndef BURN__INIT_H #define BURN__INIT_H extern int burn_running; extern double lib_start_time; /** Indicator for burn_drive_get_status() whether a signal hit parts of the thread team. 0= all works well , 1 to 5 = waiting for eventual signal on control thread > 5 = do abort now -1 = control thread has been informed */ extern volatile int burn_global_abort_level; extern int burn_global_abort_signum; extern void *burn_global_signal_handle; extern burn_abort_handler_t burn_global_signal_handler; extern int burn_builtin_signal_action; /* burn_set_signal_handling() */ extern volatile int burn_builtin_triggered_action; /* burn_is_aborting() */ /* ts B00225 */ /* @return 0= no abort pending , 1= not control thread , -1= surprisingly burn_abort returned */ int burn_init_catch_on_abort(int flag); /* ts B10606 */ void *burn_alloc_mem(size_t size, size_t count, int flag); #define BURN_ALLOC_MEM(pt, typ, count) { \ pt= (typ *) burn_alloc_mem(sizeof(typ), (size_t) (count), 0); \ if(pt == NULL) { \ ret= -1; goto ex; \ } } #define BURN_ALLOC_MEM_VOID(pt, typ, count) { \ pt= (typ *) burn_alloc_mem(sizeof(typ), (size_t) (count), 0); \ if(pt == NULL) { \ goto ex; \ } } #define BURN_FREE_MEM(pt) { \ if(pt != NULL) \ free((char *) pt); \ } /* B20122 */ int burn_grab_prepare_sig_action(int *signal_action_mem, int flag); int burn_grab_restore_sig_action(int signal_action_mem, int flag); #endif /* BURN__INIT_H */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2024 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. This is the official API definition of libburn. */ /* Important: If you add a public API function then add its name to file libburn/libburn.ver in the node LIBBURN4_Major.Minor.Micro with the numbers of the next release version, citing the previous node. */ #ifndef LIBBURN_H #define LIBBURN_H /* Applications must use 64 bit off_t. E.g. by defining #define _LARGEFILE_SOURCE #define _FILE_OFFSET_BITS 64 or take special precautions to interface with the library by 64 bit integers where this .h files prescribe off_t. To prevent 64 bit file i/o in the library would keep the application from processing tracks of more than 2 GB size. */ #include <sys/types.h> /* For struct timeval */ #include <sys/time.h> #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; /* ts A61111 */ /** References a set of write parameters */ struct burn_write_opts; /** 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 Audio data must be 44100Hz 16bit stereo with no riff or other header at beginning. Extra header data will cause pops or clicks. Audio data should also be in little-endian byte order. Big-endian audio data causes static. */ #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) /* ts B11230 */ /** Track mode modifier - Serial Copy Management System, SAO only If this is set and BURN_COPY is not set, then copying the emerging track will be forbidden. @since 1.2.0 */ #define BURN_SCMS (1 << 13) /** Possible disc writing style/modes */ enum burn_write_types { /** Packet writing. currently unsupported, (for DVD Incremental Streaming use TAO) */ BURN_WRITE_PACKET, /** With CD: Track At Once recording 2s gaps between tracks, no fonky lead-ins With sequential DVD-R[W]: Incremental Streaming With DVD+R and BD-R: Track of open size With DVD-RAM, DVD+RW, BD-RE: Random Writeable (used sequentially) With overwritable DVD-RW: Rigid Restricted Overwrite */ BURN_WRITE_TAO, /** With CD: Session At Once Block type MUST be BURN_BLOCK_SAO ts A70122: Currently not capable of mixing data and audio tracks. With sequential DVD-R[W]: Disc-at-once, DAO Single session, single track, fixed size mandatory, (-dvd-compat) With other DVD or BD media: same as BURN_WRITE_TAO but may demand that track size is known in advance. */ BURN_WRITE_SAO, /** With CD: Raw disc at once recording. all subcodes must be provided by lib or user only raw block types are supported With DVD and BD media: not supported. ts A90901: This had been disabled because its implementation relied on code from cdrdao which is not understood currently. A burn run will abort with "FATAL" error message if this mode is attempted. @since 0.7.2 ts A91016: Re-implemented according to ECMA-130 Annex A and B. Now understood, explained and not stemming from cdrdao. @since 0.7.4 */ BURN_WRITE_RAW, /** In replies this indicates that not any writing will work. As parameter for inquiries it indicates that no particular write mode shall is specified. Do not use for setting a write mode for burning. It will not work. */ BURN_WRITE_NONE }; /** 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. It is ready for writing from scratch. Unused multi-session media: CD-R, CD-RW, DVD-R, DVD-RW, DVD+R, BD-R Blanked multi-session media (i.e. treated by burn_disc_erase()) CD-RW, DVD-RW Overwritable media with or without valid data DVD-RAM, DVD+RW, formatted DVD-RW, BD-RE */ BURN_DISC_BLANK, /** There is no disc at all in the drive */ BURN_DISC_EMPTY, /** There is an incomplete disc in the drive. It is ready for appending another session. Written but not yet closed multi-session media CD-R, CD-RW, DVD-R, DVD-RW, DVD+R, BD-R */ BURN_DISC_APPENDABLE, /** There is a disc with data on it in the drive. It is usable only for reading. Written and closed multi-session media CD-R, CD-RW, DVD-R, DVD-RW, DVD+R, BD-R Read-Only media CD-ROM, DVD-ROM, BD-ROM Note that many DVD-ROM drives report any written media as Read-Only media and not by their real media types. */ BURN_DISC_FULL, /* ts A61007 */ /* @since 0.2.4 */ /** The drive was not grabbed when the status was inquired */ BURN_DISC_UNGRABBED, /* ts A61020 */ /* @since 0.2.6 */ /** The media seems to be unsuitable for reading and for writing */ BURN_DISC_UNSUITABLE }; /** 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, /* ts A61102 */ /* @since 0.2.6 */ /** The drive gets written zeroes before the track payload data */ BURN_DRIVE_WRITING_PREGAP, /** The drive is told to close a track (TAO only) */ BURN_DRIVE_CLOSING_TRACK, /** The drive is told to close a session (TAO only) */ BURN_DRIVE_CLOSING_SESSION, /* ts A61223 */ /* @since 0.3.0 */ /** The drive is formatting media */ BURN_DRIVE_FORMATTING, /* ts A70822 */ /* @since 0.4.0 */ /** The drive is busy in synchronous read (if you see this then it has been interrupted) */ BURN_DRIVE_READING_SYNC, /** The drive is busy in synchronous write (if you see this then it has been interrupted) */ BURN_DRIVE_WRITING_SYNC }; /** 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. CAUTION : This structure is prone to future extension ! Do not restrict your application to unsigned char with any counter like "session", "point", "pmin", ... Do not rely on the current size of a burn_toc_entry. */ 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; /* Indicates whether extension data are valid and eventually override older elements in this structure: bit0= DVD extension is valid @since 0.3.2 @since 0.5.2 : DVD extensions are made valid for CD too bit1= LRA extension is valid @since 0.7.2 bit2= Track status bits extension is valid @since 1.2.8 bit3= Long block address is valid: long_start_lba, long_track_blocks, long_last_rec_adr @since 1.5.8 */ unsigned char extensions_valid; /* ts A70201 : DVD extension. extensions_valid:bit0 If invalid the members are guaranteed to be 0. */ /* @since 0.3.2 */ /* Tracks and session numbers are 16 bit. Here are the high bytes. */ unsigned char session_msb; unsigned char point_msb; /* pmin, psec, and pframe may be too small if DVD extension is valid */ /* -1 means that only long_start_lba is valid */ int start_lba; /* min, sec, and frame may be too small if DVD extension is valid */ /* -1 means that only long_track_blocks is valid */ int track_blocks; /* ts A90909 : LRA extension. extensions_valid:bit1 */ /* @since 0.7.2 */ /* MMC-5 6.27.3.18 : The Last Recorded Address is valid for DVD-R, DVD-R DL when LJRS = 00b, DVD-RW, HD DVD-R, and BD-R. This would mean profiles: 0x11, 0x15, 0x13, 0x14, 0x51, 0x41, 0x42 */ /* -1 means that only long_last_rec_adr is valid */ int last_recorded_address; /* ts B30112 : Track status bits extension. extensions_valid:bit2 */ /* @since 1.2.8 */ /* Names as of READ TRACK INFORMATION, MMC-5 6.27.3 : bit0 - bit3 = Track Mode bit4 = Copy bit5 = Damage bit6 - bit7 = LJRS bit8 - bit11 = Data Mode bit12 = FP bit13 = Packet/Inc bit14 = Blank bit15 = RT bit16 = NWA_V bit17 = LRA_V */ int track_status_bits; /* ts C40221 : Long block address extension. extensions_valid:bit3 */ /* @since 1.5.8 */ off_t long_start_lba; off_t long_track_blocks; off_t long_last_rec_adr; }; /** Data source interface for tracks. This allows you to use arbitrary program code as provider of track input data. Objects compliant to this interface are either provided by the application or by API calls of libburn: burn_fd_source_new() , burn_file_source_new(), and burn_fifo_source_new(). The API calls may use any file object as data source. Consider to feed an eventual custom data stream asynchronously into a pipe(2) and to let libburn handle the rest. In this case the following rule applies: Call burn_source_free() exactly once for every source obtained from libburn API. You MUST NOT otherwise use or manipulate its components. In general, burn_source objects can be freed as soon as they are attached to track objects. The track objects will keep them alive and dispose them when they are no longer needed. With a fifo burn_source it makes sense to keep the own reference for inquiring its state while burning is in progress. --- The following description of burn_source applies only to application implemented burn_source objects. You need not to know it for API provided ones. If you really implement an own passive data producer by this interface, then beware: it can do anything and it can spoil everything. In this case the functions (*read), (*get_size), (*set_size), (*free_data) MUST be implemented by the application and attached to the object at creation time. Function (*read_sub) is allowed to be NULL or it MUST be implemented and attached. burn_source.refcount MUST be handled properly: If not exactly as many references are freed as have been obtained, then either memory leaks or corrupted memory are the consequence. All objects which are referred to by *data must be kept existent until (*free_data) is called via burn_source_free() by the last referer. */ struct burn_source { /** Reference count for the data source. MUST be 1 when a new source is created and thus the first reference is handed out. Increment it to take more references for yourself. Use burn_source_free() to destroy your references to it. */ int refcount; /** Read data from the source. Semantics like with read(2), but MUST either deliver the full buffer as defined by size or MUST deliver EOF (return 0) or failure (return -1) at this call or at the next following call. I.e. the only incomplete buffer may be the last one from that source. libburn will read a single sector by each call to (*read). The size of a sector depends on BURN_MODE_*. The known range is 2048 to 2352. If this call is reading from a pipe then it will learn about the end of data only when that pipe gets closed on the feeder side. So if the track size is not fixed or if the pipe delivers less than the predicted amount or if the size is not block aligned, then burning will halt until the input process closes the pipe. IMPORTANT: If this function pointer is NULL, then the struct burn_source is of version >= 1 and the job of .(*read)() is done by .(*read_xt)(). See below, member .version. */ int (*read)(struct burn_source *, unsigned char *buffer, int size); /** Read subchannel data from the source (NULL if lib generated) WARNING: This is an obscure feature with CD raw write modes. Unless you checked the libburn code for correctness in that aspect you should not rely on raw writing with own subchannels. ADVICE: Set this pointer to NULL. */ int (*read_sub)(struct burn_source *, unsigned char *buffer, int size); /** Get the size of the source's data. Return 0 means unpredictable size. If application provided (*get_size) might return 0, then the application MUST provide a fully functional (*set_size). */ off_t (*get_size)(struct burn_source *); /* ts A70125 : BROKE BINARY BACKWARD COMPATIBILITY AT libburn-0.3.1. */ /* @since 0.3.2 */ /** Program the reply of (*get_size) to a fixed value. It is advised to implement this by a attribute off_t fixed_size; in *data . The read() function does not have to take into respect this fake setting. It is rather a note of libburn to itself. Eventually necessary truncation or padding is done in libburn. Truncation is usually considered a misburn. Padding is considered ok. libburn is supposed to work even if (*get_size) ignores the setting by (*set_size). But your application will not be able to enforce fixed track sizes by burn_track_set_size() and possibly even padding might be left out. */ int (*set_size)(struct burn_source *source, off_t size); /** Clean up the source specific data. This function will be called once by burn_source_free() when the last referer disposes the source. */ void (*free_data)(struct burn_source *); /** Next source, for when a source runs dry and padding is disabled WARNING: This is an obscure feature. Set to NULL at creation and from then on leave untouched and uninterpreted. */ struct burn_source *next; /** Source specific data. Here the various source classes express their specific properties and the instance objects store their individual management data. E.g. data could point to a struct like this: struct app_burn_source { struct my_app *app_handle; ... other individual source parameters ... off_t fixed_size; }; Function (*free_data) has to be prepared to clean up and free the struct. */ void *data; /* ts A71222 : Supposed to be binary backwards compatible extension. */ /* @since 0.4.2 */ /** Valid only if above member .(*read)() is NULL. This indicates a version of struct burn_source younger than 0. From then on, member .version tells which further members exist in the memory layout of struct burn_source. libburn will only touch those announced extensions. Versions: 0 has .(*read)() != NULL, not even .version is present. 1 has .version, .(*read_xt)(), .(*cancel)() */ int version; /** This substitutes for (*read)() in versions above 0. */ int (*read_xt)(struct burn_source *, unsigned char *buffer, int size); /** Informs the burn_source that the consumer of data prematurely ended reading. This call may or may not be issued by libburn before (*free_data)() is called. */ int (*cancel)(struct burn_source *source); }; /** 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]; /** Invalid: Was: "Location of the drive in the filesystem." */ /** This string has no meaning any more. Once it stored the drive device file address. Now always use function burn_drive_d_get_adr() to inquire a device file address. ^^^^^ ALWAYS ^^^^^^^*/ char location[17]; /* NOTE: The capability to write particular media types is also announced by their profile number being in the list returned by burn_drive_get_all_profile(). This is the only way to inquire types DVD-RW, DVD+R, DVD+R DL, DVD+RW, BD-R, BD-RE. */ /** 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; /** DEPRECATED: Can the drive report C2 errors */ unsigned int c2_errors:1; /** DEPRECATED: 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; }; /** Operation progress report. All values are 0 based indices. Handed out by burn_drive_get_status(). **/ 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; /** Current index. */ int index; /** The starting logical block address */ int start_sector; /** On write: The number of sectors. On blank: 0x10000 as upper limit for relative progress steps */ int sectors; /** On write: The current sector being processed. On blank: Relative progress steps 0 to 0x10000 */ int sector; /* ts A61023 */ /* @since 0.2.6 */ /** The capacity of the drive buffer */ unsigned buffer_capacity; /** The free space in the drive buffer (might be slightly outdated) */ unsigned buffer_available; /* ts A61119 */ /* @since 0.2.6 */ /** The number of bytes sent to the drive buffer */ off_t buffered_bytes; /** The minimum number of bytes stored in buffer during write. (Caution: Before surely one buffer size of bytes was processed, this value is 0xffffffff.) */ unsigned buffer_min_fill; }; /** Operation progress report with long block numbers. All values are 0 based indices. Handed out by burn_drive_get_status_v2(). @since 1.5.8 **/ struct burn_progress_v2 { /** Revision of the struct format. 0= Elements up to .buffer_min_fill */ int revision; /** 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; /** Current index. */ int index; /** The starting logical block address */ off_t start_sector; /** On write: The number of sectors. On blank: 0x10000 as upper limit for relative progress steps */ off_t sectors; /** On write: The current sector being processed. On blank: Relative progress steps 0 to 0x10000 */ off_t sector; /** The capacity of the drive buffer */ off_t buffer_capacity; /** The free space in the drive buffer (might be slightly outdated) */ off_t buffer_available; /** The number of bytes sent to the drive buffer */ off_t buffered_bytes; /** The minimum number of bytes stored in buffer during write. (Caution: Before surely one buffer size of bytes was processed, this value is 0x7fffffffffffffff.) */ off_t buffer_min_fill; }; /* ts A61226 */ /* @since 0.3.0 */ /** Description of a speed capability as reported by the drive in conjunction with eventually loaded media. There can be more than one such object per drive. So they are chained via .next and .prev , where NULL marks the end of the chain. This list is set up by burn_drive_scan() and gets updated by burn_drive_grab(). A copy may be obtained by burn_drive_get_speedlist() and disposed by burn_drive_free_speedlist(). For technical background info see SCSI specs MMC and SPC: mode page 2Ah (from SPC 5Ah MODE SENSE) , mmc3r10g.pdf , 6.3.11 Table 364 ACh GET PERFORMANCE, Type 03h , mmc5r03c.pdf , 6.8.5.3 Table 312 */ struct burn_speed_descriptor { /** Where this info comes from : 0 = misc 1 = mode page 2Ah 2 = ACh GET PERFORMANCE Type 03h 3 = ACh GET PERFORMANCE Type 00h Data Type 10h (read speed) */ int source; /** The media type that was current at the time of report -2 = state unknown, -1 = no media was loaded , else see burn_disc_get_profile() */ int profile_loaded; char profile_name[80]; /** The attributed capacity of appropriate media in logical block units i.e. 2352 raw bytes or 2048 data bytes. -1 = capacity unknown. */ int end_lba; /** Speed is given in 1000 bytes/s , 0 = invalid. The numbers are supposed to be usable with burn_drive_set_speed() */ int write_speed; int read_speed; /** Expert info from ACh GET PERFORMANCE and/or mode page 2Ah. Expect values other than 0 or 1 to get a meaning in future.*/ /* Rotational control: 0 = CLV/default , 1 = CAV */ int wrc; /* 1 = drive promises reported performance over full media */ int exact; /* 1 = suitable for mixture of read and write */ int mrw; /** List chaining. Use .next until NULL to iterate over the list */ struct burn_speed_descriptor *prev; struct burn_speed_descriptor *next; }; /** 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. It is possible to 'restart' the library by shutting it down and re-initializing it. Once this was necessary if you follow the older and more general way of accessing a drive via burn_drive_scan() and burn_drive_grab(). See burn_drive_scan_and_grab() with its strong urges and its explanations. @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 <i>before</i> calling this. */ void burn_finish(void); /* ts A61002 */ /** Abort any running drive operation and eventually call burn_finish(). You MUST shut down the busy drives if an aborting event occurs during a burn run. For that you may call this function either from your own signal handling code or indirectly by activating the built-in signal handling: burn_set_signal_handling("my_app_name : ", NULL, 0); Else you may eventually call burn_drive_cancel() on the active drives and wait for them to assume state BURN_DRIVE_IDLE. @param patience Maximum number of seconds to wait for drives to finish. @since 0.7.8 : If this is -1, then only the cancel operations will be performed and no burn_finish() will happen. @param pacifier_func If not NULL: a function to produce appeasing messages. See burn_abort_pacifier() for an example. @param handle Opaque handle to be used with pacifier_func @return 1 ok, all went well 0 had to leave a drive in unclean state <0 severe error, do no use libburn again @since 0.2.6 */ int burn_abort(int patience, int (*pacifier_func)(void *handle, int patience, int elapsed), void *handle); /** A pacifier function suitable for burn_abort. @param handle If not NULL, a pointer to a text suitable for printf("%s") @param patience Maximum number of seconds to wait @param elapsed Elapsed number of seconds */ int burn_abort_pacifier(void *handle, int patience, int elapsed); /** ts A61006 : This is for development only. Not suitable for applications. 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); /* ts A91111 */ /** Enable or disable logging of SCSI commands. This call can be made at any time - even before burn_initialize(). It is in effect for all active drives and currently not very thread safe for multiple drives. @param flag Bitfield for control purposes. The default is 0. bit0= log to file /tmp/libburn_sg_command_log bit1= log to stderr bit2= flush output after each line @since 0.7.4 */ void burn_set_scsi_logging(int flag); /* ts A60813 */ /** Set parameters for behavior on opening device files. To be called early after burn_initialize() and before any bus scan. But not mandatory at all. Parameter value 1 enables a feature, 0 disables. Default is (1,0,0). Have a good reason before you change it. @param exclusive 0 = no attempt to make drive access exclusive. 1 = Try to open only devices which are not marked as busy and try to mark them busy if opened successfully. (O_EXCL on GNU/Linux , flock(LOCK_EX) on FreeBSD.) 2 = in case of a SCSI device, also try to open exclusively the matching /dev/sr, /dev/scd and /dev/st . One may select a device SCSI file family by adding 0 = default family 4 = /dev/sr%d 8 = /dev/scd%d 16 = /dev/sg%d Do not use other values ! Add 32 to demand on GNU/Linux an exclusive lock by fcntl(,F_SETLK,) after open() has succeeded. @param blocking Try to wait for drives which do not open immediately but also do not return an error as well. (O_NONBLOCK) This might stall indefinitely with /dev/hdX hard disks. @param abort_on_busy Unconditionally abort process when a non blocking exclusive opening attempt indicates a busy drive. Use this only after thorough tests with your app. @since 0.2.2 */ void burn_preset_device_open(int exclusive, int blocking, int abort_on_busy); /* ts A70223 */ /** Allows the use of media types which are implemented in libburn but not yet tested. The list of those untested profiles is subject to change. - Currently no media types are under test reservation - If you really test such media, then please report the outcome on libburn-hackers@pykix.org If ever then this call should be done soon after burn_initialize() before any drive scanning. @param yes 1=allow all implemented profiles, 0=only tested media (default) @since 0.3.4 */ void burn_allow_untested_profiles(int yes); /* ts A60823 */ /** Acquire a drive with known device file address. This is the sysadmin friendly way to open one drive and to leave all others untouched. It bundles the following API calls to form a non-obtrusive way to use libburn: burn_drive_add_whitelist() , burn_drive_scan() , burn_drive_grab() You are *strongly urged* to use this call whenever you know the drive address in advance. If not, then you have to use directly above calls. In that case, you are *strongly urged* to drop any unintended drive which will be exclusively occupied and not closed by burn_drive_scan(). This can be done by shutting down the library including a call to burn_finish(). You may later start a new libburn session and should then use the function described here with an address obtained after burn_drive_scan() via burn_drive_d_get_adr(drive_infos[driveno].drive,adr). Another way is to drop the unwanted drives by burn_drive_info_forget(). Operating on multiple drives: Different than with burn_drive_scan() it is allowed to call burn_drive_scan_and_grab() without giving up any other scanned drives. So this call can be used to get a collection of more than one acquired drives. The attempt to acquire the same drive twice will fail, though. Pseudo-drives: burn_drive_scan_and_grab() is able to acquire virtual drives which will accept options much like a MMC burner drive. Many of those options will not cause any effect, though. The address of a pseudo-drive begins with prefix "stdio:" followed by a path. Examples: "stdio:/tmp/pseudo_drive" , "stdio:/dev/null" , "stdio:-" If the path is empty, the result is a null-drive = drive role 0. It pretends to have loaded no media and supports no reading or writing. If the path leads to an existing regular file, or to a not yet existing file, or to an existing block device, then the result is a random access stdio-drive capable of reading and writing = drive role 2. If the path leads to an existing file of any type other than directory, then the result is a sequential write-only stdio-drive = drive role 3. The special address form "stdio:/dev/fd/{number}" is interpreted literally as reference to open file descriptor {number}. This address form coincides with real files on some systems, but it is in fact hardcoded in libburn. Special address "stdio:-" means stdout = "stdio:/dev/fd/1". The role of such a drive is determined by the file type obtained via fstat({number}). Roles 2 and 3 perform all their eventual data transfer activities on a file via standard i/o functions open(2), lseek(2), read(2), write(2), close(2). The media profile is reported as 0xffff. Write space information from those media is not necessarily realistic. The capabilities of role 2 resemble DVD-RAM but it can simulate writing. If the path does not exist in the filesystem yet, it is attempted to create it as a regular file as soon as write operations are started. The capabilities of role 3 resemble a blank DVD-R. Nevertheless each burn_disc_write() run may only write a single track. One may distinguish pseudo-drives from MMC drives by call burn_drive_get_drive_role(). @param drive_infos On success returns a one element array with the drive (cdrom/burner). Thus use with driveno 0 only. On failure the array has no valid elements at all. The returned array should be freed via burn_drive_info_free() when it is no longer needed. This is a result from call burn_drive_scan(). See there. Use with driveno 0 only. @param adr The device file address of the desired drive. Either once obtained by burn_drive_d_get_adr() or composed skillfully by application or its user. E.g. "/dev/sr0". Consider to preprocess it by burn_drive_convert_fs_adr(). @param load Nonzero to make the drive attempt to load a disc (close its tray door, etc). @return 1 = success , 0 = drive not found , -1 = other error @since 0.2.2 */ int burn_drive_scan_and_grab(struct burn_drive_info *drive_infos[], char* adr, int load); /* ts A51221 */ /* @since 0.2.2 */ /** Maximum number of particularly permissible drive addresses */ #define BURN_DRIVE_WHITELIST_LEN 255 /** Add a device to the list of permissible drives. As soon as some entry is in the whitelist all non-listed drives are banned from scanning. @return 1 success, <=0 failure @since 0.2.2 */ int burn_drive_add_whitelist(char *device_address); /** Remove all drives from whitelist. This enables all possible drives. */ void burn_drive_clear_whitelist(void); /** Scan for drives. This function MUST be called until it returns nonzero. In case of re-scanning: All pointers to struct burn_drive and all struct burn_drive_info arrays are invalidated by using this function. Do NOT store drive pointers across calls to this function ! To avoid invalid pointers one MUST free all burn_drive_info arrays by burn_drive_info_free() before calling burn_drive_scan() a second time. If there are drives left, then burn_drive_scan() will refuse to work. After this call all drives depicted by the returned array are subject to eventual (O_EXCL) locking. See burn_preset_device_open(). This state ends either with burn_drive_info_forget() or with burn_drive_release(). It is unfriendly to other processes on the system to hold drives locked which one does not definitely plan to use soon. @param drive_infos Returns an array of drive info items (cdroms/burners). The returned array must be freed by burn_drive_info_free() before burn_finish(), and also before calling this function burn_drive_scan() again. @param n_drives Returns the number of drive items in drive_infos. @return 0 while scanning is not complete >0 when it is finished successfully, <0 when finished but failed. */ int burn_drive_scan(struct burn_drive_info *drive_infos[], unsigned int *n_drives); /* ts A60904 : ticket 62, contribution by elmom */ /** Release memory about a single drive and any exclusive lock on it. Become unable to inquire or grab it. Expect FATAL consequences if you try. @param drive_info pointer to a single element out of the array obtained from burn_drive_scan() : &(drive_infos[driveno]) @param force controls degree of permissible drive usage at the moment this function is called, and the amount of automatically provided drive shutdown : 0= drive must be ungrabbed and BURN_DRIVE_IDLE 1= try to release drive even if in state BURN_DRIVE_GRABBING Use these two only. Further values are to be defined. @return 1 on success, 2 if drive was already forgotten, 0 if not permissible, <0 on other failures, @since 0.2.2 */ int burn_drive_info_forget(struct burn_drive_info *drive_info, int force); /** When no longer needed, free a whole burn_drive_info array which was returned by burn_drive_scan(). For freeing single drive array elements use burn_drive_info_forget(). */ void burn_drive_info_free(struct burn_drive_info drive_infos[]); /* ts A60823 */ /* @since 0.2.2 */ /** Maximum length+1 to expect with a drive device file address string */ #define BURN_DRIVE_ADR_LEN 1024 /* ts A70906 */ /** Inquire the device file address of the given drive. @param drive The drive to inquire. @param adr An application provided array of at least BURN_DRIVE_ADR_LEN characters size. The device file address gets copied to it. @return >0 success , <=0 error (due to libburn internal problem) @since 0.4.0 */ int burn_drive_d_get_adr(struct burn_drive *drive, char adr[]); /* A60823 */ /** Inquire the device file address of a drive via a given drive_info object. (Note: This is a legacy call.) @param drive_info The drive to inquire.Usually some &(drive_infos[driveno]) @param adr An application provided array of at least BURN_DRIVE_ADR_LEN characters size. The device file address gets copied to it. @return >0 success , <=0 error (due to libburn internal problem) @since 0.2.6 */ int burn_drive_get_adr(struct burn_drive_info *drive_info, char adr[]); /* ts A60922 ticket 33 */ /** Evaluate whether the given address would be a drive device file address which could be listed by a run of burn_drive_scan(). No check is made whether a device file with this address exists or whether it leads to a usable MMC drive. @return 1 means yes, 0 means no @since 0.2.6 */ int burn_drive_is_enumerable_adr(char *adr); /* ts A60922 ticket 33 */ /** Try to convert a given existing filesystem address into a drive device file address. This succeeds with symbolic links or if a hint about the drive's system address can be read from the filesystem object and a matching drive is found. @param path The address of an existing file system object @param adr An application provided array of at least BURN_DRIVE_ADR_LEN characters size. The device file address gets copied to it. @return 1 = success , 0 = failure , -1 = severe error @since 0.2.6 */ int burn_drive_convert_fs_adr(char *path, char adr[]); /* ts A60923 */ /** Try to convert a given SCSI address of bus,host,channel,target,lun into a drive device file address. If a SCSI address component parameter is < 0 then it is not decisive and the first enumerated address which matches the >= 0 parameters is taken as result. Note: bus and (host,channel) are supposed to be redundant. @param bus_no "Bus Number" (something like a virtual controller) @param host_no "Host Number" (something like half a virtual controller) @param channel_no "Channel Number" (other half of "Host Number") @param target_no "Target Number" or "SCSI Id" (a device) @param lun_no "Logical Unit Number" (a sub device) @param adr An application provided array of at least BURN_DRIVE_ADR_LEN characters size. The device file address gets copied to it. @return 1 = success , 0 = failure , -1 = severe error @since 0.2.6 */ int burn_drive_convert_scsi_adr(int bus_no, int host_no, int channel_no, int target_no, int lun_no, char adr[]); /* ts B10728 */ /** Try to convert a given drive device file address into the address of a symbolic link that points to this drive address. Modern GNU/Linux systems may shuffle drive addresses from boot to boot. The udev daemon is supposed to create links which always point to the same drive, regardless of its system address. This call tries to find such links. @param dev_adr Should contain a drive address as returned by burn_drive_scan(). @param link_adr An application provided array of at least BURN_DRIVE_ADR_LEN characters size. The found link address gets copied to it. @param dir_adr The address of the directory where to look for links. Normally: "/dev" @param templ An array of pointers to name templates, which links have to match. A symbolic link in dir_adr matches a name template if it begins by that text. E.g. link address "/dev/dvdrw1" matches template "dvdrw". If templ is NULL, then the default array gets used: {"dvdrw", "cdrw", "dvd", "cdrom", "cd"} If several links would match, then a link will win, which matches the template with the lowest array index. Among these candidates, the one with the lowest strcmp() rank will be chosen as link_adr. @param num_templ Number of array elements in templ. @param flag Bitfield for control purposes. Unused yet. Submit 0. @return <0 severe error, 0 failed to search, 2 nothing found 1 success, link_adr is valid @since 1.1.4 */ int burn_lookup_device_link(char *dev_adr, char link_adr[], char *dir_adr, char **templ, int num_templ, int flag); /* ts A60923 - A61005 */ /** Try to obtain bus,host,channel,target,lun from path. If there is an SCSI address at all, then this call should succeed with a drive device file address obtained via burn_drive_d_get_adr(). It is also supposed to succeed with any device file of a (possibly emulated) SCSI device. @return 1 = success , 0 = failure , -1 = severe error @since 0.2.6 */ int burn_drive_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no); /** Grab a drive. This must be done before the drive can be used (for reading, writing, etc). @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 it was possible to grab the drive, else 0 */ int burn_drive_grab(struct burn_drive *drive, int load); /* ts B00114 */ /* Probe available CD write modes and block types. In earlier versions this was done unconditionally on drive examination or aquiration. But it is lengthy and obtrusive, up to spoiling burn runs on the examined drives. So now this probing is omitted by default. All drives which announce to be capable of CD or DVD writing, get blindly attributed the capability for SAO and TAO. Applications which are interested in RAW modes or want to rely on the traditional write mode information, may use this call. @param drive_info drive object to be inquired @return >0 indicates success, <=0 means failure @since 0.7.6 */ int burn_drive_probe_cd_write_modes(struct burn_drive_info *drive_info); /* ts A90824 */ /** Calm down or alert a drive. Some drives stay alert after reading for quite some time. This saves time with the startup for the next read operation but also causes noise and consumes extra energy. It makes sense to calm down the drive if no read operation is expected for the next few seconds. The drive will get alert automatically if operations are required. @param d The drive to influence. @param flag Bitfield for control purposes bit0= become alert (else start snoozing) This is not mandatory for further drive operations @return 1= success , 0= drive role not suitable for calming @since 0.7.0 */ int burn_drive_snooze(struct burn_drive *d, int flag); /** Re-assess drive and media status. This should be done after a drive underwent a status change and shall be further used without intermediate burn_drive_release(), burn_drive_grab(). E.g. after blanking or burning. @param d The already grabbed drive to re-assess. @param flag Unused yet. Submit 0. @return 1 success , <= 0 could not determine drive and media state @since 1.1.8 */ int burn_drive_re_assess(struct burn_drive *d, int flag); /** 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); /* ts A70918 */ /** Like burn_drive_release() but keeping the drive tray closed and its eject button disabled. This physically locked drive state will last until the drive is grabbed again and released via burn_drive_release(). Programs like eject, cdrecord, growisofs will break that ban too. @param d The drive to release and leave locked. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 means success, <=0 means failure @since 0.4.0 */ int burn_drive_leave_locked(struct burn_drive *d, int flag); /** 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_disc_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. Note: BURN_DISC_UNGRABBED indicates wrong API usage */ enum burn_disc_status burn_disc_get_status(struct burn_drive *drive); /* ts A61020 */ /** WARNING: This revives an old bug-like behavior that might be dangerous. Sets the drive status to BURN_DISC_BLANK if it is BURN_DISC_UNREADY or BURN_DISC_UNSUITABLE. Thus marking media as writable which actually failed to declare themselves either blank or (partially) filled. @return 1 drive status has been set , 0 = unsuitable drive status @since 0.2.6 */ int burn_disc_pretend_blank(struct burn_drive *drive); /* ts A61106 */ /** WARNING: This overrides the safety measures against unsuitable media. Sets the drive status to BURN_DISC_FULL if it is BURN_DISC_UNREADY or BURN_DISC_UNSUITABLE. Thus marking media as blankable which actually failed to declare themselves either blank or (partially) filled. @since 0.2.6 */ int burn_disc_pretend_full(struct burn_drive *drive); /* ts B31110 */ /** WARNING: This overrides the safety measures against unsuitable media. Sets the drive status to BURN_DISC_FULL unconditionally. @since 1.3.4 */ int burn_disc_pretend_full_uncond(struct burn_drive *drive); /* ts B51016 */ /** Returns the Drive Serial Number as of MMC feature 108h. @param d The drive to inquire. @param sno Returns the bytes of the serial number. A trailing 0-byte is appended for convenience. MMC specifies ASCII 0x20 to 0x7h as possible byte values. But given drive firmware habits there is no warranty that *sno contains no other byte values. Submit *sno as NULL or pointing to free()-able memory. Apply free() to *sno when no longer needed. @param sno_len Returns the number of valid bytes in returned *sno, not counting the appended trailing 0. @return 1= success (but maybe *sno_len is 0), <= 0 severe failure @since 1.4.2 */ int burn_drive_get_serial_no(struct burn_drive *d, char **sno, int *sno_len); /* ts B51016 */ /** Returns the Media Serial Number as of MMC feature 109h and command ABh READ MEDIA SERIAL NUMBER. Note: This call will return an empty result unless the macro Libburn_enable_scsi_cmd_ABh is defined at compile time. This is because the command READ MEDIA SERIAL NUMBER demands superuser authority on Linux, because no medium with serial number could be tested yet, and because this command made one of the test drives unusable until power cycle when it was executed despite feature 109h was not announced as "current". @param d The drive to inquire. @param sno Returns the bytes of the serial number. A trailing 0-byte is appended for convenience. There is no warranty that *sno contains only non-zero printable bytes. Submit *sno as NULL or pointing to free()-able memory. Apply free() to *sno when no longer needed. @param sno_len Returns the number of valid bytes in returned *sno, not counting the appended trailing 0. @return 1= success (but maybe *sno_len is 0), <= 0 severe failure @since 1.4.2 */ int burn_drive_get_media_sno(struct burn_drive *d, char **sno, int *sno_len); /* ts A61021 */ /** Reads ATIP information from inserted media. To be obtained via burn_drive_get_write_speed(), burn_drive_get_min_write_speed(), burn_drive_get_start_end_lba(). The drive must be grabbed for this call. @param drive The drive to query. @return 1=success, 0=no valid ATIP info read, -1 severe error @since 0.2.6 */ int burn_disc_read_atip(struct burn_drive *drive); /* ts B70206 */ /** Tells whether a BD-R medium with Pseudo Overwrite (POW) formatting is in the drive. Such a formatting may have been applied by dvd+rw-tools. It prevents sequential multi-session. libburn will refuse to write to such a medium. @param drive The drive to query. @return 1 if BD-R Pseudo Overwrite , 0 if not BD-R or not POW @since 1.4.8 */ int burn_drive_get_bd_r_pow(struct burn_drive *drive); /* ts A61020 */ /** Returns start and end lba of the media which is currently inserted in the given drive. The drive has to be grabbed to have hope for reply. Shortcoming (not a feature): unless burn_disc_read_atip() was called only blank media will return valid info. @param drive The drive to query. @param start_lba Returns the start lba value @param end_lba Returns the end lba value @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 if lba values are valid , 0 if invalid @since 0.2.6 */ int burn_drive_get_start_end_lba(struct burn_drive *drive, int *start_lba, int *end_lba, int flag); /* ts A90902 */ /** Guess the manufacturer name of CD media from the ATIP addresses of lead-in and lead-out. (Currently only lead-in is interpreted. Lead-out may in future be used to identify the media type in more detail.) The parameters of this call should be obtained by burn_disc_read_atip(d), burn_drive_get_start_end_lba(d, &start_lba, &end_lba, 0), burn_lba_to_msf(start_lba, &m_li, &s_li, &f_li) and burn_lba_to_msf(end_lba, &m_lo, &s_lo, &f_lo). @param m_li "minute" part of ATIP lead-in or start_lba @param s_li "second" of lead-in or start_lba @param f_li "frame" of lead-in @param m_lo "minute" part of ATIP lead-out @param s_lo "second" of lead-out @param f_lo "frame" of lead-out @param flag Bitfield for control purposes, bit0= append a text "(aka ...)" to reply if other brands or vendor names are known. @return Printable text or NULL on memory shortage. Dispose by free() when no longer needed. @since 0.7.2 */ char *burn_guess_cd_manufacturer(int m_li, int s_li, int f_li, int m_lo, int s_lo, int f_lo, int flag); /* ts A90909 */ /** Retrieve some media information which is mainly specific to CD. For other media only the bits in reply parameter valid are supposed to be meaningful. @param d The drive to query. @param disc_type A string saying either "CD-DA or CD-ROM", or "CD-I", or ""CD-ROM XA", or "undefined". @param disc_id A 32 bit number read from the media. (Meaning unclear yet) @param bar_code 8 hex digits from a barcode on media read by the drive (if the drive has a bar code reader built in). @param app_code The Host Application Code which must be set in the Write Parameters Page if the media is not unrestricted (URU==0). @param valid Replies bits which indicate the validity of other reply parameters or the state of certain CD info bits: bit0= disc_type is valid bit1= disc_id is valid bit2= bar_code is valid bit3= disc_app_code is valid bit4= Disc is unrestricted (URU bit, 51h READ DISC INFO) This seems to be broken with my drives. The bit is 0 and the validity bit for disc_app_code is 0 too. bit5= Disc is nominally erasable (Erasable bit) This will be set with overwritable media which libburn normally considers to be unerasable blank. @return 1 success, <= 0 an error occurred @since 0.7.2 */ int burn_disc_get_cd_info(struct burn_drive *d, char disc_type[80], unsigned int *disc_id, char bar_code[9], int *app_code, int *valid); /* ts B11201 */ /** Read the array of CD-TEXT packs from the Lead-in of an audio CD. Each pack consists of 18 bytes, of which 4 are header. 12 bytes are pieces of 0-terminated texts or binary data. 2 bytes hold a CRC. For a description of the format of the array, see file doc/cdtext.txt. @param d The drive to query. @param text_packs Will point to an allocated memory buffer with CD-TEXT. It will only contain text packs, and not be prepended by the TOC header of four bytes, which gets stored with file cdtext.dat by cdrecord -vv -toc. (The first two of these bytes are supposed to hold the number of CD-TEXT bytes + 2. The other two bytes are supposed to be 0.) Dispose this buffer by free(), when no longer needed. @param num_packs Will tell the number of text packs, i.e. the number of bytes in text_packs divided by 18. @param flag Bitfield for control purposes, Unused yet. Submit 0. @return 1 success, 0= no CD-TEXT found, < 0 an error occurred @since 1.2.0 */ int burn_disc_get_leadin_text(struct burn_drive *d, unsigned char **text_packs, int *num_packs, int flag); /* ts B00924 */ /** Read the current usage of the eventual BD Spare Area. This area gets reserved on BD media during formatting. During writing it is used to host replacements of blocks which failed the checkread immediately after writing. This call applies only to recordable BD media. I.e. profiles 0x41 to 0x43. @param d The drive to query. @param alloc_blocks Returns the number of blocks reserved as Spare Area @param free_blocks Returns the number of yet unused blocks in that area @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 = reply prarameters are valid, <=0 = reply is invalid (e.g. because no BD profile) @since 0.8.8 */ int burn_disc_get_bd_spare_info(struct burn_drive *d, int *alloc_blocks, int *free_blocks, int flag); /* ts B10801 */ /** Retrieve some media information which is mainly specific to media of the DVD-R family: DVD-R , DVD-RW , DVD-R DL , HD DVD-R Currently the information cannot be retrieved from other media types. @param d The drive to query. @param disk_category returns DVD Book to which the media complies @param book_name returns a pointer to the book name of disk_category. This memory is static. Do not alter or free it ! @param part_version returns the Media Version in the DVD Book @param num_layers returns the number of media layers @param num_blocks returns the number of blocks between pysical start and physical end of the media @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 = reply prarameters are valid, <=0 = reply is invalid (e.g. because no DVD-R) @since 1.1.4 */ int burn_disc_get_phys_format_info(struct burn_drive *d, int *disk_category, char **book_name, int *part_version, int *num_layers, int *num_blocks, int flag); /* ts A61110 */ /** Read start lba and Next Writeable Address of a track from media. Usually a track lba is obtained from the result of burn_track_get_entry(). This call retrieves an updated lba, eventual nwa, and can address the invisible track to come. The drive must be grabbed for this call. One may not issue this call during ongoing burn_disc_write() or burn_disc_erase(). @param d The drive to query. @param o If not NULL: write parameters to be set on drive before query @param trackno 0=next track to come, >0 number of existing track The first existing track on a CD may have a number higher than 1. Use burn_session_get_start_tno() to inquire this start number. @param lba return value: start lba @param nwa return value: Next Writeable Address @return 1=nwa is valid , 0=nwa is not valid , -1=error @since 0.2.6 */ int burn_disc_track_lba_nwa(struct burn_drive *d, struct burn_write_opts *o, int trackno, int *lba, int *nwa); /* ts C40302 */ /** Like burn_disc_track_lba_nwa(), but with off_t results. @since 1.5.8 */ int burn_disc_track_lba_nwa_v2(struct burn_drive *d, struct burn_write_opts *o, int trackno, off_t *lba, off_t *nwa); /* ts B10525 */ /** Tells whether a previous attempt to determine the Next Writeable Address of the upcoming track reveiled that the READ TRACK INFORMATION Damage Bit is set for this track and that no valid writable address is available. See MMC-5 6.27.3.7 Damage Bit, 6.27.3.11 NWA_V (NWA valid) @param d The drive to query. @param flag Bitfield for control purposes (unused yet, submit 0) @return 0= Looks ok: Damage Bit is not set, NWA_V is set 1= Damaged and theoretically writable (NWA_V is set) 2= Not writable: NWA_V is not set 3= Damaged and not writable (NWA_V is not set), @since 1.1.0 */ int burn_disc_next_track_is_damaged(struct burn_drive *d, int flag); /* ts B10527 */ /** Try to close the last track and session of media which have bit0 set in the return value of call burn_disc_next_track_is_damaged(). Whether it helps depends much on the reason why the media is reported as damaged by the drive. This call works only for profiles 0x09 CD-R, 0x0a CD-RW, 0x11 DVD-R, 0x14 DVD-RW sequential, 0x1b DVD+R, 0x2b DVD+R DL, 0x41 BD-R sequential. Note: After writing it is advised to give up the drive and to grab it again in order to learn about its view on the new media state. @param o Write options created by burn_write_opts_new() and manipulated by burn_write_opts_set_multi(). burn_write_opts_set_write_type() should be set to BURN_WRITE_TAO, burn_write_opts_set_simulate() should be set to 0. @param flag Bitfield for control purposes bit0= force close, even if no damage was seen @return <=0 media not marked as damaged, or media type not suitable, or closing attempted but failed 1= attempt finished without error indication @since 1.1.0 */ int burn_disc_close_damaged(struct burn_write_opts *o, int flag); /* ts A70131 */ /** Read start lba of the first track in the last complete session. This is the first parameter of mkisofs option -C. The second parameter is nwa as obtained by burn_disc_track_lba_nwa() with trackno 0. @param d The drive to query. @param start_lba returns the start address of that track @return <= 0 : failure, 1 = ok @since 0.3.2 */ int burn_disc_get_msc1(struct burn_drive *d, int *start_lba); /* ts A70213 */ /** Return the best possible estimation of the currently available capacity of the media. This might depend on particular write option settings. For inquiring the space with such a set of options, the drive has to be grabbed and BURN_DRIVE_IDLE. If not, then one will only get a canned value from the most recent automatic inquiry (e.g. during last drive grabbing). An eventual start address from burn_write_opts_set_start_byte() will be taken into respect with the capacity estimation. Negative results get defaulted to 0. If the drive is actually a file in a large filesystem or a large block device, then the capacity is curbed to a maximum of 0x7ffffff0 blocks = 4 TB - 32 KB. @param d The drive to query. @param o If not NULL: write parameters to be set on drive before query @return number of most probably available free bytes @since 0.3.4 */ off_t burn_disc_available_space(struct burn_drive *d, struct burn_write_opts *o); /* ts A61202 */ /** Tells the MMC Profile identifier of the loaded media. The drive must be grabbed in order to get a non-zero result. libburn currently writes only to profiles 0x09 "CD-R" 0x0a "CD-RW" 0x11 "DVD-R sequential recording" 0x12 "DVD-RAM" 0x13 "DVD-RW restricted overwrite" 0x14 "DVD-RW sequential recording", 0x15 "DVD-R/DL sequential recording", 0x1a "DVD+RW" 0x1b "DVD+R", 0x2b "DVD+R/DL", 0x41 "BD-R sequential recording", 0x43 "BD-RE", 0xffff "stdio file" Note: 0xffff is not a MMC profile but a libburn invention. Read-only are the profiles 0x08 "CD-ROM", 0x10 "DVD-ROM", 0x40 "BD-ROM", Read-only for now is this BD-R profile (testers wanted) 0x42 "BD-R random recording" Empty drives are supposed to report 0x00 "" @param d The drive where the media is inserted. @param pno Profile Number. See also mmc5r03c.pdf, table 89 @param name Profile Name (see above list, unknown profiles have empty name) @return 1 profile is valid, 0 no profile info available @since 0.3.0 */ int burn_disc_get_profile(struct burn_drive *d, int *pno, char name[80]); /* ts A90903 : API */ /** Obtain product id and standards defined media codes. The product id is a printable string which is supposed to be the same for identical media but should vary with non-identical media. Some media cannot provide such an id at all. The pair (profile_number, product_id) should be the best id to identify media with identical product specifications. The reply parameters media_code1 and media_code2 can be used with burn_guess_manufacturer() The reply parameters have to be disposed by free() when no longer needed. @param d The drive where the media is inserted. @param product_id Reply: Printable text depicting manufacturer and eventually media id. @param media_code1 Reply: The eventual manufacturer identification as read from DVD/BD media or a text "XXmYYsZZf" from CD media ATIP lead-in. @param media_code2 The eventual media id as read from DVD+/BD media or a text "XXmYYsZZf" from CD ATIP lead-out. @param book_type Book type text for DVD and BD. Caution: is NULL with CD, even if return value says ok. @param flag Bitfield for control purposes bit0= do not escape " _/" (not suitable for burn_guess_manufacturer()) @return 1= ok, product_id and media codes are valid, 0= no product id_available, reply parameters are NULL <0= error @since 0.7.2 */ int burn_disc_get_media_id(struct burn_drive *d, char **product_id, char **media_code1, char **media_code2, char **book_type, int flag); /* ts A90904 */ /** Guess the name of a manufacturer by profile number, manufacturer code and media code. The profile number can be obtained by burn_disc_get_profile(), the other two parameters can be obtained as media_code1 and media_code2 by burn_disc_get_media_id(). @param profile_no Profile number (submit -1 if not known) @param manuf_code Manufacturer code from media (e.g. "RICOHJPN") @param media_code Media ID code from media (e.g. "W11") @param flag Bitfield for control purposes, submit 0 @return Printable text or NULL on memory shortage. If the text begins with "Unknown " then no item of the manufacturer list matched the codes. Dispose by free() when no longer needed. @since 0.7.2 */ char *burn_guess_manufacturer(int profile_no, char *manuf_code, char *media_code, int flag); /** Tells whether a disc can be erased or not @param d The drive to inquire. @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); /** Returns the progress with long block numbers and the status of the 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. @since 1.5.8 */ enum burn_drive_status burn_drive_get_status_v2(struct burn_drive *drive, struct burn_progress_v2 *p); /** Creates a write_opts struct for burning to the specified drive. The returned object must later be freed with burn_write_opts_free(). @param drive The drive to write with @return The write_opts, NULL on error */ struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive); /* ts A70901 */ /** Inquires the drive associated with a burn_write_opts object. @param opts object to inquire @return pointer to drive @since 0.4.0 */ struct burn_drive *burn_write_opts_get_drive(struct burn_write_opts *opts); /** 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 read_opts struct for reading from the specified drive must be freed with burn_read_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. Only drive roles 1 (MMC) and 5 (stdio random write-only) support erasing. @param fast Nonzero to do a fast erase, where only the disc's headers are erased; zero to erase the entire disc. With DVD-RW, fast blanking yields media capable only of DAO. */ void burn_disc_erase(struct burn_drive *drive, int fast); /* ts A70101 - A70417 */ /** Format media for use with libburn. This currently applies to DVD-RW in state "Sequential Recording" (profile 0014h) which get formatted to state "Restricted Overwrite" (profile 0013h). DVD+RW can be "de-iced" by setting bit4 of flag. DVD-RAM and BD-RE may get formatted initially or re-formatted to adjust their Defect Management. This function usually returns while the drive is still in the process of formatting. The formatting is done, when burn_drive_get_status() returns BURN_DRIVE_IDLE. This may be immediately after return or may need several thousand seconds to occur. @param drive The drive with the disc to format. @param size The size in bytes to be used with the format command. It should be divisible by 32*1024. The effect of this parameter may depend on the media profile and on parameter flag. @param flag Bitfield for control purposes: bit0= after formatting, write the given number of zero-bytes to the media and eventually perform preliminary closing. bit1+2: size mode 0 = use parameter size as far as it makes sense 1 = insist in size 0 even if there is a better default known (on DVD-RAM or BD-R identical to size mode 0, i.e. they never get formatted with payload size 0) 2 = without bit7: format to maximum available size with bit7 : take size from indexed format descriptor 3 = without bit7: format to default size with bit7 : take size from indexed format descriptor bit3= -reserved- bit4= enforce re-format of (partly) formatted media bit5= try to disable eventual defect management bit6= try to avoid lengthy media certification bit7, bit8 to bit15 = bit7 enables MMC expert application mode (else libburn tries to choose a suitable format type): If it is set then bit8 to bit15 contain the index of the format to use. See burn_disc_get_formats(), burn_disc_get_format_descr(). Acceptable types are: 0x00, 0x01, 0x10, 0x11, 0x13, 0x15, 0x26, 0x30, 0x31, 0x32. If bit7 is set, then bit4 is set automatically. bit16= enable POW on blank BD-R @since 0.3.0 */ void burn_disc_format(struct burn_drive *drive, off_t size, int flag); /* ts A70112 */ /* @since 0.3.0 */ /** Possible formatting status values */ #define BURN_FORMAT_IS_UNFORMATTED 1 #define BURN_FORMAT_IS_FORMATTED 2 #define BURN_FORMAT_IS_UNKNOWN 3 /* ts A70112 */ /** Inquire the formatting status, the associated sizes and the number of available formats. The info is media specific and stems from MMC command 23h READ FORMAT CAPACITY. See mmc5r03c.pdf 6.24 for background details. Media type can be determined via burn_disc_get_profile(). @param drive The drive with the disc to format. @param status The current formatting status of the inserted media. See BURN_FORMAT_IS_* macros. Note: "unknown" is the legal status for quick formatted, yet unwritten DVD-RW. @param size The size in bytes associated with status. unformatted: the maximum achievable size of the media formatted: the currently formatted capacity unknown: maximum capacity of drive or of media @param bl_sas Additional info "Block Length/Spare Area Size". Expected to be constantly 2048 for non-BD media. @param num_formats The number of available formats. To be used with burn_disc_get_format_descr() to obtain such a format and eventually with burn_disc_format() to select one. @return 1 reply is valid , <=0 failure @since 0.3.0 */ int burn_disc_get_formats(struct burn_drive *drive, int *status, off_t *size, unsigned *bl_sas, int *num_formats); /* ts A70112 */ /** Inquire parameters of an available media format. @param drive The drive with the disc to format. @param index The index of the format item. Beginning with 0 up to reply parameter from burn_disc_get_formats() : num_formats - 1 @param type The format type. See mmc5r03c.pdf, 6.5, 04h FORMAT UNIT. 0x00=full, 0x10=CD-RW/DVD-RW full, 0x11=CD-RW/DVD-RW grow, 0x15=DVD-RW quick, 0x13=DVD-RW quick grow, 0x26=DVD+RW background, 0x30=BD-RE with spare areas, 0x31=BD-RE without spare areas @param size The maximum size in bytes achievable with this format. @param tdp Type Dependent Parameter. See mmc5r03c.pdf. @return 1 reply is valid , <=0 failure @since 0.3.0 */ int burn_disc_get_format_descr(struct burn_drive *drive, int index, int *type, off_t *size, unsigned *tdp); /* ts A61109 : this was and is defunct */ /** 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); /* ts A70222 */ /* @since 0.3.4 */ /** The length of a rejection reasons string for burn_precheck_write() and burn_write_opts_auto_write_type() . */ #define BURN_REASONS_LEN 4096 /* ts A70219 */ /** Examines a completed setup for burn_disc_write() whether it is permissible with drive and media. This function is called by burn_disc_write() but an application might be interested in this check in advance. @param o The options for the writing operation. @param disc The description of the disc to be created @param reasons Eventually returns a list of rejection reason statements @param silent 1= do not issue error messages , 0= report problems @return 1 ok, -1= no recordable media detected, 0= other failure @since 0.3.4 */ int burn_precheck_write(struct burn_write_opts *o, struct burn_disc *disc, char reasons[BURN_REASONS_LEN], int silent); /** 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 ot BURN_DISC_APPENDABLE before calling this function. Note: write_type BURN_WRITE_SAO is currently not capable of writing a mix of data and audio tracks. You must use BURN_WRITE_TAO for such sessions. To be set by burn_write_opts_set_write_type(). Note: This function is not suitable for overwriting data in the middle of a valid data area because it is allowed to append trailing data. For exact random access overwriting use burn_random_access_write(). Note: After writing it is advised to give up the drive and to grab it again in order to learn about its view on the new media state. Note: Before mounting the written media it might be necessary to eject and reload in order to allow the operating system to notice the new media state. @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); /* ts A90227 */ /** Control stream recording during the write run and eventually set the start LBA for stream recording. Stream recording is set from struct burn_write_opts when the write run gets started. See burn_write_opts_set_stream_recording(). The call described here can be used later to override this setting and to program automatic switching at a given LBA. It also affects subsequent calls to burn_random_access_write(). @param drive The drive which performs the write operation. @param recmode -1= disable stream recording 0= leave setting as is 1= enable stream recording @param start The LBA where actual stream recording shall start. (0 means unconditional stream recording) @param flag Bitfield for control purposes (unused yet, submit 0). @return 1=success , <=0 failure @since 0.6.4 */ int burn_drive_set_stream_recording(struct burn_drive *drive, int recmode, int start, int flag); /* ts B60730 */ /** Enable or disable use of the Immed bit with long running SCSI commands. If the Immed bit is used, then those SCSI commands end early and leave the drive in not-ready state. libburn then tries periodically whether the drive became ready again. Only then it assumes the command to be completely done. The default setting may depend on the operating system on which libburn was compiled. @param drive The drive which will be affected. @param enable 1= use Immed bit. 0= use no Immed bit. Affected commands can last very long. @return 1=success , <=0 failure @since 1.4.6 */ int burn_drive_set_immed(struct burn_drive *drive, int enable); /* ts B60730 */ /** Inquire the current setting of usage of the Immed bit. Either the still set system dependent default or the value set by call burn_drive_set_immed(). @return The current value. @since 1.4.6 */ int burn_drive_get_immed(struct burn_drive *drive); /** 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); /* ts A61223 */ /** Inquire whether the most recent asynchronous media job was successful. This applies to burn_disc_erase(), burn_disc_format(), burn_disc_write(). Reasons for non-success may be: rejection of burn parameters, abort due to fatal errors during write, blank or format, a call to burn_drive_cancel() by the application thread. @param d The drive to inquire. @return 1=burn seems to have went well, 0=burn failed @since 0.2.6 */ int burn_drive_wrote_well(struct burn_drive *d); /* ts B31023 */ /** Inquire whether a write error occurred which is suspected to have happened due to a false report about DVD-RW capability to be written in write type BURN_WRITE_TAO. @param d The drive to inquire. @return 1= it seems that BURN_WRITE_TAO on DVD-RW caused error, 0= it does not seem so @since 1.3.4 */ int burn_drive_was_feat21_failure(struct burn_drive *d); /** 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 @return Pointer to a burn_disc object or NULL on failure. */ 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 @return Pointer to a burn_session object or NULL on failure. */ 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); /* ts B11219 */ /** Read a CDRWIN cue sheet file and equip the session object by tracks and CD-TEXT according to the content of the file. For a description of CDRWIN file format see http://digitalx.org/cue-sheet/syntax/ Fully supported commands are: CATALOG , CDTEXTFILE , FLAGS , INDEX , ISRC , PERFORMER , POSTGAP , PREGAP , REM , SONGWRITER , TITLE Further supported commands introduced by cdrecord (usage like PERFORMER): ARRANGER , COMPOSER , MESSAGE Partly supported commands are: FILE which supports only types BINARY , MOTOROLA , WAVE TRACK which supports only datatypes AUDIO , MODE1/2048 Unsupported types of FILE or TRACK lead to failure of the call. libburn does not yet support mixing of AUDIO and MODE1/2048. So this call will fail if such a mix is found. CD-TEXT information is allowed only if all tracks are of datatype AUDIO. Empty lines and lines which start by '#' are ignored. @param session Session where to attach tracks. It must not yet have tracks or else this call will fail. @param path Filesystem address of the CDRWIN cue sheet file. Normally with suffix .cue @param fifo_size Number of bytes in fifo. This will be rounded up by the block size of the track mode. <= 0 means no fifo. @param fifo Returns a reference to the burn_source object that was installed as fifo between FILE and the track burn sources. One may use this to inquire the fifo state. Dispose it by burn_source_free() when no longer needed. It is permissible to pass this parameter to libburn as NULL, in order to immediately drop ownership on the fifo. @param text_packs Returns pre-formatted CD-TEXT packs resulting from cue sheet command CDTEXTFILE. To be used with call burn_write_opts_set_leadin_text(). It is permissible to pass this parameter to libburn as NULL, in order to disable CDTEXTFILE. @param num_packs Returns the number of 18 byte records in text_packs. @param flag Bitfield for control purposes. bit0= Do not attach CD-TEXT information to session and tracks. Do not load text_packs. bit1= Do not use media catalog string of session or ISRC strings of tracks for writing to Q sub-channel. @return > 0 indicates success, <= 0 indicates failure @since 1.2.0 */ int burn_session_by_cue_file(struct burn_session *session, char *path, int fifo_size, struct burn_source **fifo, unsigned char **text_packs, int *num_packs, int flag); /** Create a track */ 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); /* ts B20107 */ /** Set the number which shall be written as CD track number with the first track of the session. The following tracks will then get written with consecutive CD track numbers. The resulting number of the last track must not exceed 99. The lowest possible start number is 1, which is also the default. This setting applies only to CD SAO writing. @param session The session to be manipulated @param tno A number between 1 and 99 @param flag Bitfield for control purposes. Unused yet. Submit 0. @return > 0 indicates success, <= 0 indicates failure @since 1.2.0 */ int burn_session_set_start_tno(struct burn_session *session, int tno, int flag); /* ts B20108 */ /** Inquire the CD track start number, as set by default or by burn_session_set_start_tno(). @param session The session to be inquired @param flag Bitfield for control purposes. Unused yet. Submit 0. @return > 0 is the currently set CD track start number <= 0 indicates failure @since 1.2.0 */ int burn_session_get_start_tno(struct burn_session *session, int flag); /* ts B11206 */ /** Set the Character Codes, the Copyright bytes, and the Language Codes for CD-TEXT blocks 0 to 7. They will be used in the block summaries of text packs which get generated from text or binary data submitted by burn_session_set_cdtext() and burn_track_set_cdtext(). Character Code value can be 0x00 = ISO-8859-1 0x01 = 7 bit ASCII 0x80 = MS-JIS (japanesei Kanji, double byte characters) Copyright byte value can be 0x00 = not copyrighted 0x03 = copyrighted Language Code value will typically be 0x09 = English or 0x69 = Japanese. See below macros BURN_CDTEXT_LANGUAGES_0X00 and BURN_CDTEXT_LANGUAGES_0X45, but be aware that many of these codes have never been seen on CD, and that many of them do not have a character representation among the above Character Codes. Default is 0x09 = English for block 0 and 0x00 = Unknown for block 1 to 7. Copyright and Character Code are 0x00 for all blocks by default. See also file doc/cdtext.txt, "Format of a CD-TEXT packs array", "Pack type 0x8f". Parameter value -1 leaves the current setting of the session parameter unchanged. @param s Session where to change settings @param char_codes Character Codes for block 0 to 7 @param copyrights Copyright bytes for block 0 to 7 @param languages Language Codes for block 0 to 7 @param flag Bitfiled for control purposes. Unused yet. Submit 0. @return <=0 failure, > 0 success @since 1.2.0 */ int burn_session_set_cdtext_par(struct burn_session *s, int char_codes[8], int copyrights[8], int languages[8], int flag); /** This is the first list of languages sorted by their Language codes, which start at 0x00. They stem from from EBU Tech 3264, appendix 3. E.g. language 0x00 is "Unknown", 0x08 is "German", 0x10 is "Frisian", 0x18 is "Latvian", 0x20 is "Polish", 0x28 is "Swedish", 0x2b is "Wallon". See also file doc/cdtext.txt. @since 1.2.0 */ #define BURN_CDTEXT_LANGUAGES_0X00 \ "Unknown", "Albanian", "Breton", "Catalan", \ "Croatian", "Welsh", "Czech", "Danish", \ "German", "English", "Spanish", "Esperanto", \ "Estonian", "Basque", "Faroese", "French", \ "Frisian", "Irish", "Gaelic", "Galician", \ "Icelandic", "Italian", "Lappish", "Latin", \ "Latvian", "Luxembourgian", "Lithuanian", "Hungarian", \ "Maltese", "Dutch", "Norwegian", "Occitan", \ "Polish", "Portuguese", "Romanian", "Romansh", \ "Serbian", "Slovak", "Slovenian", "Finnish", \ "Swedish", "Turkish", "Flemish", "Wallon" /** This is the second list of languages sorted by their Language codes, which start at 0x45. They stem from from EBU Tech 3264, appendix 3. E.g. language 0x45 is "Zulu", 0x50 is "Sranan Tongo", 0x58 is "Pushtu", 0x60 is "Moldavian", 0x68 is "Kannada", 0x70 is "Greek", 0x78 is "Bengali", 0x7f is "Amharic". See also file doc/cdtext.txt. @since 1.2.0 */ #define BURN_CDTEXT_LANGUAGES_0X45 \ "Zulu", "Vietnamese", "Uzbek", \ "Urdu", "Ukrainian", "Thai", "Telugu", \ "Tatar", "Tamil", "Tadzhik", "Swahili", \ "Sranan Tongo", "Somali", "Sinhalese", "Shona", \ "Serbo-croat", "Ruthenian", "Russian", "Quechua", \ "Pushtu", "Punjabi", "Persian", "Papamiento", \ "Oriya", "Nepali", "Ndebele", "Marathi", \ "Moldavian", "Malaysian", "Malagasay", "Macedonian", \ "Laotian", "Korean", "Khmer", "Kazakh", \ "Kannada", "Japanese", "Indonesian", "Hindi", \ "Hebrew", "Hausa", "Gurani", "Gujurati", \ "Greek", "Georgian", "Fulani", "Dari", \ "Churash", "Chinese", "Burmese", "Bulgarian", \ "Bengali", "Bielorussian", "Bambora", "Azerbaijani", \ "Assamese", "Armenian", "Arabic", "Amharic" /* This is the list of empty languages names between 0x30 and 0x44. Together the three macros fill an array of 128 char pointers. static char *languages[] = { BURN_CDTEXT_LANGUAGES_0X00, BURN_CDTEXT_FILLER, BURN_CDTEXT_LANGUAGES_0X45 }; */ #define BURN_CDTEXT_FILLER \ "", "", "", "", \ "", "", "", "", \ "", "", "", "", \ "", "", "", "", \ "", "", "", "", \ "", "", "", "", \ "" /* ts B11206 */ /** Obtain the current settings as of burn_session_set_cdtext_par() @param s Session which to inquire @param char_codes Will return Character Codes for block 0 to 7 @param copyrights Will return Copyright bytes for block 0 to 7 @param block_languages Will return Language Codes for block 0 to 7 @param flag Bitfiled for control purposes. Unused yet. Submit 0. @return <=0 failure, reply invalid, > 0 success, reply valid @since 1.2.0 */ int burn_session_get_cdtext_par(struct burn_session *s, int char_codes[8], int copyrights[8], int block_languages[8], int flag); /* ts B11206 */ /** Attach text or binary data as CD-TEXT attributes to a session. They can be used to generate CD-TEXT packs by burn_cdtext_from_session() or to write CD-TEXT packs into the lead-in of a CD SAO session. The latter happens only if no array of CD-TEXT packs is attached to the write options by burn_write_opts_set_leadin_text(). For details of the CD-TEXT format and of the payload content, see file doc/cdtext.txt . @param s Session where to attach CD-TEXT attribute @param block Number of the language block in which the attribute shall appear. Possible values: 0 to 7. @param pack_type Pack type number. 0x80 to 0x8e. Used if pack_type_name is NULL or empty text. Else submit 0 and a name. Pack type 0x8f is generated automatically and may not be set by applications. @param pack_type_name The pack type by name. Defined names are: 0x80 = "TITLE" 0x81 = "PERFORMER" 0x82 = "SONGWRITER" 0x83 = "COMPOSER" 0x84 = "ARRANGER" 0x85 = "MESSAGE" 0x86 = "DISCID" 0x87 = "GENRE" 0x88 = "TOC" 0x89 = "TOC2" 0x8d = "CLOSED" 0x8e = "UPC_ISRC" Names are recognized uppercase and lowercase. @param payload Text or binary bytes. The data will be copied to session-internal memory. Pack types 0x80 to 0x85 contain 0-terminated cleartext encoded according to the block's Character Code. If double byte characters are used, then two 0-bytes terminate the cleartext. Pack type 0x86 is 0-terminated ASCII cleartext. Pack type 0x87 consists of two byte big-endian Genre code (see below BURN_CDTEXT_GENRE_LIST), and 0-terminated ASCII cleartext of genre description. Pack type 0x88 mirrors the session table-of-content. Pack type 0x89 is not understood yet. Pack types 0x8a to 0x8c are reserved. Pack type 0x8d contains ISO-8859-1 cleartext which is not to be shown by commercial audio CD players. Pack type 0x8e is ASCII cleartext with UPC/EAN code. @param length Number of bytes in payload. Including terminating 0-bytes. @param flag Bitfield for control purposes. bit0= payload contains double byte characters (with character code 0x80 MS-JIS japanese Kanji) @return > 0 indicates success , <= 0 is failure @since 1.2.0 */ int burn_session_set_cdtext(struct burn_session *s, int block, int pack_type, char *pack_type_name, unsigned char *payload, int length, int flag); /** This is the list of Genres sorted by their Genre codes. E.g. genre code 0x0000 is "No Used", 0x0008 is "Dance, 0x0010 is "Musical", 0x0018 is "Rhythm & Blues", 0x001b is "World Music". See also file doc/cdtext.txt. @since 1.2.0 */ #define BURN_CDTEXT_GENRE_LIST \ "Not Used", "Not Defined", "Adult Contemporary", "Alternative Rock", \ "Childrens Music", "Classical", "Contemporary Christian", "Country", \ "Dance", "Easy Listening", "Erotic", "Folk", \ "Gospel", "Hip Hop", "Jazz", "Latin", \ "Musical", "New Age", "Opera", "Operetta", \ "Pop Music", "Rap", "Reggae", "Rock Music", \ "Rhythm & Blues", "Sound Effects", "Spoken Word", "World Music" /* The number of genre names in BURN_CDTEXT_GENRE_LIST. */ #define BURN_CDTEXT_NUM_GENRES 28 /* ts B11206 */ /** Obtain a CD-TEXT attribute that was set by burn_session_set_cdtext() @param s Session to inquire @param block Number of the language block to inquire. @param pack_type Pack type number to inquire. Used if pack_type_name is NULL or empty text. Else submit 0 and a name. Pack type 0x8f is generated automatically and may not be inquire in advance. Use burn_cdtext_from_session() to generate all packs including type 0x8f packs. @param pack_type_name The pack type by name. See above burn_session_set_cdtext(). @param payload Will return a pointer to text or binary bytes. Not a copy of data. Do not free() this address. If no text attribute is attached for pack type and block, then payload is returned as NULL. The return value will not indicate error in this case. @param length Will return the number of bytes pointed to by payload. Including terminating 0-bytes. @param flag Bitfield for control purposes. Unused yet. Submit 0. @return 1 single byte char, 2 double byte char, <=0 error @since 1.2.0 */ int burn_session_get_cdtext(struct burn_session *s, int block, int pack_type, char *pack_type_name, unsigned char **payload, int *length, int flag); /* ts B11215 */ /** Read a Sony CD-TEXT Input Sheet Version 0.7T file and attach its text attributes to the given session and its tracks for the given CD-TEXT block number. This overrides previous settings made by burn_session_set_cdtext(), burn_track_set_cdtext(), burn_track_set_isrc(), burn_session_set_start_tno(). It can later be overridden by said function calls. The media catalog number from purpose specifier "UPC / EAN" gets into effect only if burn_write_opts_set_has_mediacatalog() is set to 0. The format of a v07t sheet file is documented in doc/cdtext.txt. @param session Session where to attach CD-TEXT attributes @param path Local filesystem address of the sheet file which shall be read and interpreted. @param block Number of the language block in which the attributes shall appear. Possible values: 0 to 7. @param flag Bitfield for control purposes. bit0= Permission to read multiple blocks from the given sheet file. Each block is supposed to begin by a line "Input Sheet Version = 0.7T". Therefore this permission is only valid if the input file begins by such a line. @since 1.3.2 bit1= Do not use media catalog string of session or ISRC strings of tracks for writing to Q sub-channel. @since 1.2.0 @return > 0 indicates success and the number of interpreted blocks (1 if not flag bit0 is set). <= 0 indicates failure @since 1.2.0 */ int burn_session_input_sheet_v07t(struct burn_session *session, char *path, int block, int flag); /* ts B11210 */ /** Produce an array of CD-TEXT packs that could be submitted to burn_write_opts_set_leadin_text(), or stored as *.cdt file, or submitted to burn_make_input_sheet_v07t(). For a description of the format of the array, see file doc/cdtext.txt. The input data stem from burn_session_set_cdtext_par(), burn_session_set_cdtext(), and burn_track_set_cdtext(). @param s Session from which to produce CD-TEXT packs. @param text_packs Will return the buffer with the CD-TEXT packs. Dispose by free() when no longer needed. @param num_packs Will return the number of 18 byte text packs. @param flag Bitfield for control purposes. bit0= do not return generated CD-TEXT packs, but check whether production would work and indicate the number of packs by the call return value. This happens also if (text_packs == NULL || num_packs == NULL). @return Without flag bit0: > 0 is success, <= 0 failure With flag bit0: > 0 is number of packs, 0 means no packs will be generated, < 0 means failure @since 1.2.0 */ int burn_cdtext_from_session(struct burn_session *s, unsigned char **text_packs, int *num_packs, int flag); /* ts B30519 */ /** Convert an array of CD-TEXT packs into the text format of Sony CD-TEXT Input Sheet Version 0.7T . @param text_packs Array of bytes which form CD-TEXT packs of 18 bytes each. For a description of the format of the array, see file doc/cdtext.txt. No header of 4 bytes must be prepended which would tell the number of pack bytes + 2. This parameter may be NULL if the currently attached array of packs shall be removed. @param num_packs The number of 18 byte packs in text_packs. @param start_tno The start number of track counting, if known from CD table-of-content or other sources. Submit 0 to enable the attempt to read it and the track_count from pack type 0x8f. @param track_count The number of tracks, if known from CD table-of-content or orther sources. @param result Will return the buffer with Sheet text. Dispose by free() when no longer needed. It will be filled by the text for the v07t sheet file plus a trailing 0-byte. (Be aware that double-byte characters might contain 0-bytes, too.) Each CD-TEXT language block starts by the line "Input Sheet Version = 0.7T" and a "Remarks" line that tells the block number. @param char_code Returns the character code of the pack array: 0x00 = ISO-8859-1 0x01 = 7 bit ASCII 0x80 = MS-JIS (japanese Kanji, double byte characters) The presence of a code value that is not in this list will cause this function to fail. @param flag Bitfield for control purposes. Unused yet. Submit 0. @return > 0 tells the number of valid text bytes in result. This does not include the trailing 0-byte. <= 0 indicates failure. @since 1.3.2 */ int burn_make_input_sheet_v07t(unsigned char *text_packs, int num_packs, int start_tno, int track_count, char **result, int *char_code, int flag); /* ts B11206 */ /** Remove all CD-TEXT attributes of the given block from the session. They were attached by burn_session_set_cdtext(). @param s Session where to remove the CD-TEXT attribute @param block Number of the language block in which the attribute shall appear. Possible values: 0 to 7. -1 causes text packs of all blocks to be removed. @return > 0 is success, <= 0 failure @since 1.2.0 */ int burn_session_dispose_cdtext(struct burn_session *s, int block); /* ts B11221*/ /** Read an array of CD-TEXT packs from a file. This array should be suitable for burn_write_opts_set_leadin_text(). The function tolerates and removes 4-byte headers as produced by cdrecord -vv -toc, if this header tells the correct number of bytes which matches the file size. If no 4-byte header is present, then the function tolerates and removes a trailing 0-byte as of Sony specs. @param path Filesystem address of the CD-TEXT pack file. Normally with suffix .cdt or .dat @param text_packs Will return the buffer with the CD-TEXT packs. Dispose by free() when no longer needed. @param num_packs Will return the number of 18 byte text packs. @param flag Bitfield for control purposes. Unused yet.Submit 0. @return 0 is success, <= 0 failure @since 1.2.0 */ int burn_cdtext_from_packfile(char *path, unsigned char **text_packs, int *num_packs, int flag); /** 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); /* ts B11206 */ /** Attach text or binary data as CD-TEXT attributes to a track. The payload will be used to generate CD-TEXT packs by burn_cdtext_from_session() or to write CD-TEXT packs into the lead-in of a CD SAO session. This happens if the CD-TEXT attribute of the session gets generated, which has the same block number and pack type. In this case, each track should have such a CD-TEXT attribute, too. See burn_session_set_cdtext(). Be cautious not to exceed the maximum number of 253 payload packs per language block. Use burn_cdtext_from_session() to learn whether a valid array of CD-TEXT packs can be generated from your attributes. @param t Track where to attach CD-TEXT attribute. @param block Number of the language block in which the attribute shall appear. Possible values: 0 to 7. @param pack_type Pack type number. 0x80 to 0x85 or 0x8e. Used if pack_type_name is NULL or empty text. Else submit 0 and a name. @param pack_type_name The pack type by name. Applicable names are: 0x80 = "TITLE" 0x81 = "PERFORMER" 0x82 = "SONGWRITER" 0x83 = "COMPOSER" 0x84 = "ARRANGER" 0x85 = "MESSAGE" 0x8e = "UPC_ISRC" @param payload 0-terminated cleartext. If double byte characters are used, then two 0-bytes terminate the cleartext. @param length Number of bytes in payload. Including terminating 0-bytes. @param flag Bitfield for control purposes. bit0= payload contains double byte characters (with character code 0x80 MS-JIS japanese Kanji) @return > 0 indicates success , <= 0 is failure @since 1.2.0 */ int burn_track_set_cdtext(struct burn_track *t, int block, int pack_type, char *pack_type_name, unsigned char *payload, int length, int flag); /* ts B11206 */ /** Obtain a CD-TEXT attribute that was set by burn_track_set_cdtext(). @param t Track to inquire @param block Number of the language block to inquire. @param pack_type Pack type number to inquire. Used if pack_type_name is NULL or empty text. Else submit 0 and a name. @param pack_type_name The pack type by name. See above burn_track_set_cdtext(). @param payload Will return a pointer to text bytes. Not a copy of data. Do not free() this address. If no text attribute is attached for pack type and block, then payload is returned as NULL. The return value will not indicate error in this case. @param length Will return the number of bytes pointed to by payload. Including terminating 0-bytes. @param flag Bitfield for control purposes. Unused yet. Submit 0. @return 1=single byte char , 2= double byte char , <=0 error @since 1.2.0 */ int burn_track_get_cdtext(struct burn_track *t, int block, int pack_type, char *pack_type_name, unsigned char **payload, int *length, int flag); /* ts B11206 */ /** Remove all CD-TEXT attributes of the given block from the track. They were attached by burn_track_set_cdtext(). @param t Track where to remove the CD-TEXT attribute. @param block Number of the language block in which the attribute shall appear. Possible values: 0 to 7. -1 causes text packs of all blocks to be removed. @return > 0 is success, <= 0 failure @since 1.2.0 */ int burn_track_dispose_cdtext(struct burn_track *t, int block); /* ts A90910 */ /** Activates CD XA compatibility modes. libburn currently writes data only in CD mode 1. Some programs insist in sending data with additional management bytes. These bytes have to be stripped in order to make the input suitable for BURN_MODE1. @param t The track to manipulate @param value 0= no conversion 1= strip 8 byte sector headers of CD-ROM XA mode 2 form 1 see MMC-5 4.2.3.8.5.3 Block Format for Mode 2 form 1 Data all other values are reserved @return 1=success , 0=unacceptable value @since 0.7.2 */ int burn_track_set_cdxa_conv(struct burn_track *t, int value); /** Set the ISRC details for a track. When writing to CD media, ISRC will get written into the Q sub-channel. @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); /* ts B11226 */ /** Set the composed ISRC string for a track. This is an alternative to burn_track_set_isrc(). @param t The track to be manipulated @param isrc 12 characters which are composed from ISRC details. Format is CCOOOYYSSSSS, terminated by a 0-byte: Country, Owner, Year(decimal digits), Serial(decimal digits). @param flag Bitfield for control purposes. Unused yet. Submit 0. @return > 0 indicates success, <= 0 means failure @since 1.2.0 */ int burn_track_set_isrc_string(struct burn_track *t, char isrc[13], int flag); /** Disable ISRC parameters for a track @param t The track to change */ void burn_track_clear_isrc(struct burn_track *t); /* ts B20103 */ /** Define an index start address within a track. The index numbers inside a track have to form sequence starting at 0 or 1 with no gaps up to the highest number used. They affect only writing of CD SAO sessions. The first index start address of a track must be 0. Blocks between index 0 and index 1 are considered to be located before the track start as of the table-of-content. @param t The track to be manipulated @param index_number A number between 0 and 99 @param relative_lba The start address relative to the start of the burn_source of the track. It will get mapped to the appropriate absolute block address. @param flag Bitfield for control purposes. Unused yet. Submit 0. @return > 0 indicates success, <= 0 means failure @since 1.2.0 */ int burn_track_set_index(struct burn_track *t, int index_number, unsigned int relative_lba, int flag); /* ts B20103 */ /** Remove all index start addresses and reset to the default indexing of CD SAO sessions. This means index 0 of track 1 reaches from LBA -150 to LBA -1. Index 1 of track 1 reaches from LBA 0 to track end. Index 1 of track 2 follows immediately. The same happens for all further tracks after the end of their predecessor. @param t The track to be manipulated @param flag Bitfield for control purposes. Unused yet. Submit 0. @return > 0 indicates success, <= 0 means failure @since 1.2.0 */ int burn_track_clear_indice(struct burn_track *t, int flag); /* ts B20110 */ /** Define whether a pre-gap shall be written before the track and how many sectors this pre-gap shall have. A pre-gap is written in the range of track index 0 and contains zeros (audio silence). No bytes from the track source will be read for writing the pre-gap. This setting affects only CD SAO write runs. The first track automatically gets a pre-gap of at least 150 sectors. Its size may be enlarged by this call. Further pre-gaps are demanded by MMC for tracks which follow tracks of a different mode. (But Mode mixing in CD SAO sessions is currently not supported by libburn.) @param t The track to change @param size Number of sectors in the pre-gap. -1 disables pre-gap, except for the first track. libburn allows 0, but MMC does not propose this. @param flag Bitfield for control purposes. Unused yet. Submit 0. @return > 0 indicates success, <= 0 means failure @since 1.2.0 */ int burn_track_set_pregap_size(struct burn_track *t, int size, int flag); /* ts B20111 */ /** Define whether a post-gap shall be written at the end of the track and how many sectors this gap shall have. A post-gap occupies the range of an additional index of the track. It contains zeros. No bytes from the track source will be read for writing the post-gap. This setting affects only CD SAO write runs. MMC prescribes to add a post-gap to a data track which is followed by a non-data track. (But libburn does not yet support mixed mode CD SAO sessions.) @param t The track to change @param size Number of sectors in the post-gap. -1 disables post-gap. libburn allows 0, but MMC does not propose this. @param flag Bitfield for control purposes. Unused yet. Submit 0. @return > 0 indicates success, <= 0 means failure @since 1.2.0 */ int burn_track_set_postgap_size(struct burn_track *t, int size, int flag); /* ts A61024 */ /** Define whether a track shall swap bytes of its input stream. @param t The track to change @param swap_source_bytes 0=do not swap, 1=swap byte pairs @return 1=success , 0=unacceptable value @since 0.2.6 */ int burn_track_set_byte_swap(struct burn_track *t, int swap_source_bytes); /** 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 or NULL on failure */ 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 occurred */ enum burn_source_status burn_track_set_source(struct burn_track *t, struct burn_source *s); /* ts A70218 */ /** Set a default track size to be used only if the track turns out to be of unpredictable length and if the effective write type demands a fixed size. This can be useful to enable write types CD SAO or DVD DAO together with a track source like stdin. If the track source delivers fewer bytes than announced then the track will be padded up with zeros. @param t The track to change @param size The size to set @return 0=failure 1=success @since 0.3.4 */ int burn_track_set_default_size(struct burn_track *t, off_t size); /** 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) @param path The file address for the main channel payload. @param subpath Eventual address for subchannel data. Only used in exotic raw write modes. Submit NULL for normal tasks. @return Pointer to a burn_source object, NULL indicates failure */ struct burn_source *burn_file_source_new(const char *path, const char *subpath); /* ts A91122 : An interface to open(O_DIRECT) or similar OS tricks. */ /** Opens a file with eventual acceleration preparations which may depend on the operating system and on compile time options of libburn. You may use this call instead of open(2) for opening file descriptors which shall be handed to burn_fd_source_new(). This should only be done for tracks with BURN_BLOCK_MODE1 (2048 bytes per block). If you use this call then you MUST allocate the buffers which you use with read(2) by call burn_os_alloc_buffer(). Read sizes MUST be a multiple of a safe buffer amount. Else you risk that track data get altered during transmission. burn_disk_write() will allocate a suitable read/write buffer for its own operations. A fifo created by burn_fifo_source_new() will allocate suitable memory for its buffer if called with flag bit0 and a multiple of a safe buffer amount. @param path The file address to open @param open_flags The flags as of man 2 open. Normally just O_RDONLY. @param flag Bitfield for control purposes (unused yet, submit 0). @return A file descriptor as of open(2). Finally to be disposed by close(2). -1 indicates failure. @since 0.7.4 */ int burn_os_open_track_src(char *path, int open_flags, int flag); /** Allocate a memory area that is suitable for reading with a file descriptor opened by burn_os_open_track_src(). @param amount Number of bytes to allocate. This should be a multiple of the operating system's i/o block size. 32 KB is guaranteed by libburn to be safe. @param flag Bitfield for control purposes (unused yet, submit 0). @return The address of the allocated memory, or NULL on failure. A non-NULL return value has finally to be disposed via burn_os_free_buffer(). @since 0.7.4 */ void *burn_os_alloc_buffer(size_t amount, int flag); /** Dispose a memory area which was obtained by burn_os_alloc_buffer(), @param buffer Memory address to be freed. @param amount The number of bytes which was allocated at that address. @param flag Bitfield for control purposes (unused yet, submit 0). @return 1 success , <=0 failure @since 0.7.4 */ int burn_os_free_buffer(void *buffer, size_t amount, int flag); /** Creates a data source for an image file (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 of subchannel data. Only used in exotic raw write modes. Submit -1 for normal tasks. @param size The eventual fixed size of eventually both fds. If this value is 0, the size will be determined from datafd. @return Pointer to a burn_source object, NULL indicates failure */ struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size); /* ts B00922 */ /** Creates an offset source which shall provide a byte interval of a stream to its consumer. It is supposed to be chain-linked with other offset sources which serve neighboring consumers. The chronological sequence of consumers and the sequence of offset sources must match. The intervals of the sources must not overlap. A chain of these burn_source objects may be used to feed multiple tracks from one single stream of input bytes. Each of the offset sources will skip the bytes up to its start address and provide the prescribed number of bytes to the track. Skipping takes into respect the bytes which have been processed by eventual predecessors in the chain. Important: It is not allowed to free an offset source before its successor has ended its work. Best is to keep them all until all tracks are done. @param inp The burn_source object from which to read stream data. E.g. created by burn_file_source_new(). @param prev The eventual offset source object which shall read data from inp before the new offset source will begin its own work. This must either be a result of burn_offst_source_new() or it must be NULL. @param start The byte address where to start reading bytes for the consumer. inp bytes may get skipped to reach this address. @param size The number of bytes to be delivered to the consumer. If size is <= 0 then it may be set later by a call of method set_size(). If it is >= 0, then it can only be changed if flag bit0 was set with burn_offst_source_new(). @param flag Bitfield for control purposes bit0 = Prevent set_size() from overriding interval sizes > 0. If such a size is already set, then the new one will only affect the reply of get_size(). See also above struct burn_source. @since 1.2.0 @return Pointer to a burn_source object, later to be freed by burn_source_free(). NULL indicates failure. @since 0.8.8 */ struct burn_source *burn_offst_source_new( struct burn_source *inp, struct burn_source *prev, off_t start, off_t size, int flag); /* ts A70930 */ /** Creates a fifo which acts as proxy for an already existing data source. The fifo provides a ring buffer which shall smoothen the data stream between burn_source and writer thread. Each fifo serves only for one data source. It may be attached to one track as its only data source by burn_track_set_source(), or it may be used as input for other burn sources. A fifo starts its life in "standby" mode with no buffer space allocated. As soon as its consumer requires bytes, the fifo establishes a worker thread and allocates its buffer. After input has ended and all buffer content is consumed, the buffer space gets freed and the worker thread ends. This happens asynchronously. So expect two buffers and worker threads to exist for a short time between tracks. Be modest in your size demands if multiple tracks are to be expected. @param inp The burn_source for which the fifo shall act as proxy. It can be disposed by burn_source_free() immediately after this call. @param chunksize The size in bytes of a chunk. Use 2048 for sources suitable for BURN_BLOCK_MODE1, 2352 for sources which deliver for BURN_BLOCK_AUDIO, 2056 for sources which shall get treated by burn_track_set_cdxa_conv(track, 1). Some variations of burn_source might work only with a particular chunksize. E.g. libisofs demands 2048. @param chunks The number of chunks to be allocated in ring buffer. This value must be >= 2. @param flag Bitfield for control purposes: bit0= The read method of inp is capable of delivering arbitrary amounts of data per call. Not only one sector. Suitable for inp from burn_file_source_new() and burn_fd_source_new() if not the fd has exotic limitations on read size. You MUST use this on inp which uses an fd opened with burn_os_open_track_src(). Better do not use with other inp types. @since 0.7.4 @return A pointer to the newly created burn_source. Later both burn_sources, inp and the returned fifo, have to be disposed by calling burn_source_free() for each. inp can be freed immediately, the returned fifo may be kept as handle for burn_fifo_inquire_status(). @since 0.4.0 */ struct burn_source *burn_fifo_source_new(struct burn_source *inp, int chunksize, int chunks, int flag); /* ts A71003 */ /** Inquires state and fill parameters of a fifo burn_source which was created by burn_fifo_source_new() . Do not use with other burn_source variants. @param fifo The fifo object to inquire @param size The total size of the fifo @param free_bytes The current free capacity of the fifo @param status_text Returns a pointer to a constant text, see below @return <0 reply invalid, >=0 fifo status code: bit0+1=input status, bit2=consumption status, i.e: 0="standby" : data processing not started yet 1="active" : input and consumption are active 2="ending" : input has ended without error 3="failing" : input had error and ended, 4="unused" : ( consumption has ended before processing start ) 5="abandoned" : consumption has ended prematurely 6="ended" : consumption has ended without input error 7="aborted" : consumption has ended after input error @since 0.4.0 */ int burn_fifo_inquire_status(struct burn_source *fifo, int *size, int *free_bytes, char **status_text); /* ts A91125 */ /** Inquire various counters which reflect the fifo operation. @param fifo The fifo object to inquire @param total_min_fill The minimum number of bytes in the fifo. Beginning from the moment when fifo consumption is enabled. @param interval_min_fill The minimum byte number beginning from the moment when fifo consumption is enabled or from the most recent moment when burn_fifo_next_interval() was called. @param put_counter The number of data transactions into the fifo. @param get_counter The number of data transactions out of the fifo. @param empty_counter The number of times the fifo was empty. @param full_counter The number of times the fifo was full. @since 0.7.4 */ void burn_fifo_get_statistics(struct burn_source *fifo, int *total_min_fill, int *interval_min_fill, int *put_counter, int *get_counter, int *empty_counter, int *full_counter); /* ts A91125 */ /** Inquire the fifo minimum fill counter for intervals and reset that counter. @param fifo The fifo object to inquire @param interval_min_fill The minimum number of bytes in the fifo. Beginning from the moment when fifo consumption is enabled or from the most recent moment when burn_fifo_next_interval() was called. @since 0.7.4 */ void burn_fifo_next_interval(struct burn_source *fifo, int *interval_min_fill); /* ts A80713 */ /** Obtain a preview of the first input data of a fifo which was created by burn_fifo_source_new(). The data will later be delivered normally to the consumer track of the fifo. bufsize may not be larger than the fifo size (chunk_size * chunks) - 32k. This call will succeed only if data consumption by the track has not started yet, i.e. best before the call to burn_disc_write(). It will start the worker thread of the fifo with the expectable side effects on the external data source. Then it waits either until enough data have arrived or until it becomes clear that this will not happen. The call may be repeated with increased bufsize. It will always yield the bytes beginning from the first one in the fifo. @param fifo The fifo object to start and to inquire @param buf Pointer to memory of at least bufsize bytes where to deliver the peeked data. @param bufsize Number of bytes to peek from the start of the fifo data @param flag Bitfield for control purposes (unused yet, submit 0). @return <0 on severe error, 0 if not enough data, 1 if bufsize bytes read @since 0.5.0 */ int burn_fifo_peek_data(struct burn_source *fifo, char *buf, int bufsize, int flag); /* ts A91125 */ /** Start the fifo worker thread and wait either until the requested number of bytes have arrived or until it becomes clear that this will not happen. Filling will go on asynchronously after burn_fifo_fill() returned. This call and burn_fifo_peek_data() do not disturb each other. @param fifo The fifo object to start @param fill Number of bytes desired. Expect to get return 1 if at least fifo size - 32k were read. @param flag Bitfield for control purposes. bit0= fill fifo to maximum size @return <0 on severe error, 0 if not enough data, 1 if desired amount or fifo full @since 0.7.4 */ int burn_fifo_fill(struct burn_source *fifo, int fill, int flag); /* ts A70328 */ /** Sets a fixed track size after the data source object has already been created. @param t The track to operate on @param size the number of bytes to use as track size @return <=0 indicates failure , >0 success @since 0.3.6 */ int burn_track_set_size(struct burn_track *t, off_t size); /** Tells how many sectors a track will have on disc, or already has on disc. This includes offset, payload, tail, and post-gap, but not pre-gap. The result is NOT RELIABLE with tracks of undefined length */ int burn_track_get_sectors(struct burn_track *); /* ts C40302 */ /** Like burn_track_get_sectors(), but with return type off_t. @since 1.5.8 */ off_t burn_track_get_sectors_v2(struct burn_track *); /* ts A61101 */ /** Tells how many source bytes have been read and how many data bytes have been written by the track during burn. @param t The track to inquire @param read_bytes Number of bytes read from the track source @param written_bytes Number of bytes written to track @since 0.2.6 */ int burn_track_get_counters(struct burn_track *t, off_t *read_bytes, off_t *written_bytes); /** Sets drive read and write speed. Note: "k" is 1000, not 1024. 1xCD = 176.4 k/s, 1xDVD = 1385 k/s, 1xBD = 4496 k/s. Fractional speeds should be rounded up. Like 4xCD = 706. @param d The drive to set speed for @param read Read speed in k/s (0 is max, -1 is min). @param write Write speed in k/s (0 is max, -1 is min). */ void burn_drive_set_speed(struct burn_drive *d, int read, int write); /* ts C00822 */ /** Sets drive read and write speed using the "Exact" bit of SCSI command SET STREAMING. This command will be used even if a CD medium is present. MMC specifies that with the Exact bit the desired speed settings shall either be obeyed by the drive exactly, or that the drive shall indicate failure and not accept the settings. But many drives reply no error and nevertheless adjust their read speed only coarsly or ignore the setting after a few MB of fast read attempts. The call parameters have the same meaning as with burn_drive_set_speed(). @param d The drive to set speed for. It must be a role 1 drive. @param read Read speed in k/s (0 is max, -1 is min). @param write Write speed in k/s (0 is max, -1 is min). @return 1=success , 0=failure @since 1.5.4 */ int burn_drive_set_speed_exact(struct burn_drive *d, int read, int write); /* ts C00822 */ /** Waits until the time has elapsed since the given previous time to transmit the given byte count with the given speed in KB/second (KB = 1000 bytes). This call may be used between random access read operations like burn_read_data() in order to force a slower speed than the drive is willing to use if it gets read requests as fast as it delivers data. The parameter us_corr carries microseconds of time deviations from one call to the next one. Such deviations may happen because of small inexactnesses of the sleeper function and because of temporary delays in the data supply so that sleeping for a negative time span would have been necessary. The next call will reduce or enlarge its own sleeping period by this value. @param kb_per_second the desired speed in 1000 bytes per second. Supplied by the caller. @max_corr the maximum backlog in microseconds which shall be compensated by the next call. Supplied by the caller. Not more than 1 billion = 1000 seconds. @param prev_time time keeper updated by burn_nominal_slowdown(). The caller provides the memory and elsewise should carry it unchanged from call to call. @param us_corr updated by burn_nominal_slowdown(). See above. The caller provides the memory and elsewise should carry it unchanged from call to call. @param b_since_prev byte count since the previous call. This number has to be counted and supplied by the caller. @param flag Bitfield for control purposes: bit0= initialize *prev_time and *us_corr, ignore other parameters, do not wait @return 2=no wait because no usable kb_per_second , 1=success , 0=failure @since 1.5.4 */ int burn_nominal_slowdown(int kb_per_second, int max_corr, struct timeval *prev_time, int *us_corr, off_t b_since_prev, int flag); /* ts A70711 */ /** Controls the behavior with writing when the drive buffer is suspected to be full. To check and wait for enough free buffer space before writing will move the task of waiting from the operating system's device driver to libburn. While writing is going on and waiting is enabled, any write operation will be checked whether it will fill the drive buffer up to more than max_percent. If so, then waiting will happen until the buffer fill is predicted with at most min_percent. Thus: if min_percent < max_percent then transfer rate will oscillate. This may allow the driver to operate on other devices, e.g. a disk from which to read the input for writing. On the other hand, this checking might reduce maximum throughput to the drive or even get misled by faulty buffer fill replies from the drive. If a setting parameter is < 0, then this setting will stay unchanged by the call. Known burner or media specific pitfalls: To have max_percent larger than the burner's best reported buffer fill has the same effect as min_percent==max_percent. Some burners do not report their full buffer with all media types. Some are not suitable because they report their buffer fill with delay. Some do not go to full speed unless their buffer is full. @param d The drive to control @param enable 0= disable , 1= enable waiting , (-1 = do not change setting) @param min_usec Shortest possible sleeping period (given in micro seconds) @param max_usec Longest possible sleeping period (given in micro seconds) @param timeout_sec If a single write has to wait longer than this number of seconds, then waiting gets disabled and mindless writing starts. A value of 0 disables this timeout. @param min_percent Minimum of desired buffer oscillation: 25 to 100 @param max_percent Maximum of desired buffer oscillation: 25 to 100 @return 1=success , 0=failure @since 0.3.8 */ int burn_drive_set_buffer_waiting(struct burn_drive *d, int enable, int min_usec, int max_usec, int timeout_sec, int min_percent, int max_percent); /* ts B61116 */ /** Control the write simulation mode before or after burn_write_opts get into effect. Beginning with version 1.4.8 a burn run by burn_disc_write() brings the burn_drive object in the simulation state as set to the burn_write_opts by burn_write_opts_set_simulate(). This state is respected by call burn_random_access_write() until a new call of burn_disc_write() happens or until burn_drive_reset_simulate() is called. This call may only be made when burn_drive_get_status() returns BURN_DRIVE_IDLE. @param d The drive to control @param simulate 1 enables simulation, 0 enables real writing @return 1=success , 0=failure @since 1.4.8 */ int burn_drive_reset_simulate(struct burn_drive *d, int simulate); /* these are for my [Derek Foreman's ?] debugging, they will disappear */ /* ts B11012 : Of course, API symbols will not disappear. But these functions are of few use, as they only print DEBUG messages. */ 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. Note: write_type BURN_WRITE_SAO is currently not capable of writing a mix of data and audio tracks. You must use BURN_WRITE_TAO for such sessions. @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); /* ts A70207 */ /** As an alternative to burn_write_opts_set_write_type() this function tries to find a suitable write type and block type for a given write job described by opts and disc. To be used after all other setups have been made, i.e. immediately before burn_disc_write(). @param opts The nearly complete write opts to change @param disc The already composed session and track model @param reasons This text string collects reasons for decision or failure @param flag Bitfield for control purposes: bit0= do not choose type but check the one that is already set bit1= do not issue error messages via burn_msgs queue (is automatically set with bit0) @return Chosen write type. BURN_WRITE_NONE on failure. @since 0.3.2 */ enum burn_write_types burn_write_opts_auto_write_type( struct burn_write_opts *opts, struct burn_disc *disc, char reasons[BURN_REASONS_LEN], int flag); /** 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 . This corresponds to the Test Write bit in MMC mode page 05h. Several media types do not support this. See struct burn_multi_caps.might_simulate for actual availability of this feature. If the media is suitable, the drive will perform burn_disc_write() as a simulation instead of effective write operations. This means that the media content and burn_disc_get_status() stay unchanged. Note: With stdio-drives, the target file gets eventually created, opened, lseeked, and closed, but not written. So there are effects on it. Note: Up to version 1.4.6 the call burn_random_access_write() after burn_disc_write() did not simulate because it does not get any burn_write_opts and the drive did not memorize the simulation state. This has changed now. burn_random_access_write() will not write after a simulated burn run. Use burn_drive_reset_simulate(drive, 0) if you really want to end simulation before you call burn_disc_write() with new write options. @param opts The write opts to change @param sim Non-zero enables simulation, 0 enables real writing @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. This is only needed with CD media and possibly with old DVD-R drives. All other media types are not vulnerable to burn failure due to buffer underrun. @param opts The write opts to change @param underrun_proof if non-zero, buffer underrun protection is enabled @return Returns 1 if the drive announces to be capable of underrun prevention, Returns 0 if not. */ 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); /** The Q sub-channel of a CD may contain a Media Catalog Number of 13 decimal digits. This call sets the string of digits, but does not yet activate it for writing. @param opts The write opts to change @param mediacatalog The 13 decimal digits as ASCII bytes. I.e. '0' = 0x30. */ void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, unsigned char mediacatalog[13]); /** This call activates the Media Catalog Number for writing. The digits of that number have to be set by call burn_write_opts_set_mediacatalog(). @param opts The write opts to change @param has_mediacatalog 1= activate writing of catalog to Q sub-channel 0= deactivate it */ void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, int has_mediacatalog); /* ts A61106 */ /** Sets the multi flag which eventually marks the emerging session as not being the last one and thus creating a BURN_DISC_APPENDABLE media. Note: DVD-R[W] in write mode BURN_WRITE_SAO are not capable of this. DVD-R DL are not capable of this at all. libburn will refuse to write if burn_write_opts_set_multi() is enabled under such conditions. @param opts The option object to be manipulated @param multi 1=media will be appendable, 0=media will be closed (default) @since 0.2.6 */ void burn_write_opts_set_multi(struct burn_write_opts *opts, int multi); /* ts B31024 */ /** Set the severity to be used with write error messages which are potentially caused by not using write type BURN_WRITE_SAO on fast blanked DVD-RW. Normally the call burn_write_opts_auto_write_type() can prevent such errors by looking for MMC feature 21h "Incremental Streaming Writable" which anounnces the capability for BURN_WRITE_TAO and multi session. Regrettable many drives announce feature 21h even if they only can do BURN_WRITE_SAO. This mistake becomes obvious by an early write error. If you plan to call burn_drive_was_feat21_failure() and to repeat the burn attempt with BURN_WRITE_SAO, then set the severity of the error message low enough, so that the application does not see reason to abort. @param opts The option object to be manipulated @param severity Severity as with burn_msgs_set_severities(). "ALL" or empty text means the default severity that is attributed to other kinds of write errors. */ void burn_write_opts_set_fail21h_sev(struct burn_write_opts *opts, char *severity); /* ts B11204 */ /** Submit an array of CD-TEXT packs which shall be written to the Lead-in of a SAO write run on CD. @param opts The option object to be manipulated @param text_packs Array of bytes which form CD-TEXT packs of 18 bytes each. For a description of the format of the array, see file doc/cdtext.txt. No header of 4 bytes must be prepended which would tell the number of pack bytes + 2. This parameter may be NULL if the currently attached array of packs shall be removed. @param num_packs The number of 18 byte packs in text_packs. This parameter may be 0 if the currently attached array of packs shall be removed. @param flag Bitfield for control purposes. bit0= do not verify checksums bit1= repair mismatching checksums bit2= repair checksums if they are 00 00 with each pack @return 1 on success, <= 0 on failure @since 1.2.0 */ int burn_write_opts_set_leadin_text(struct burn_write_opts *opts, unsigned char *text_packs, int num_packs, int flag); /* ts A61222 */ /** Sets a start address for writing to media and write modes which are able to choose this address at all (for now: DVD+RW, DVD-RAM, formatted DVD-RW). now). The address is given in bytes. If it is not -1 then a write run will fail if choice of start address is not supported or if the block alignment of the address is not suitable for media and write mode. Alignment to 32 kB blocks is supposed to be safe with DVD media. Call burn_disc_get_multi_caps() can obtain the necessary media info. See resulting struct burn_multi_caps elements .start_adr , .start_alignment , .start_range_low , .start_range_high . @param opts The write opts to change @param value The address in bytes (-1 = start at default address) @since 0.3.0 */ void burn_write_opts_set_start_byte(struct burn_write_opts *opts, off_t value); /* ts A70213 */ /** Caution: still immature and likely to change. Problems arose with sequential DVD-RW on one drive. Controls whether the whole available space of the media shall be filled up by the last track of the last session. @param opts The write opts to change @param fill_up_media If 1 : fill up by last track, if 0 = do not fill up @since 0.3.4 */ void burn_write_opts_set_fillup(struct burn_write_opts *opts, int fill_up_media); /* ts A70303 */ /** Lets libburn ignore the failure of some conformance checks: - the check whether CD write+block type is supported by the drive - the check whether the media profile supports simulated burning - @since 1.5.6 The check whether a write operation exceeds the size of the medium as announced by the drive. This is known as "overburn" and may work for a few thousand additional blocks on CD media with write type SAO. @param opts The write opts to change @param use_force 1=ignore above checks, 0=refuse work on failed check @since 0.3.4 */ void burn_write_opts_set_force(struct burn_write_opts *opts, int use_force); /* ts A80412 */ /** Eventually makes use of the more modern write command AAh WRITE12 and sets the Streaming bit. With DVD-RAM and BD this can override the traditional slowdown to half nominal speed. But if it speeds up writing then it also disables error management and correction. Weigh your priorities. This affects the write operations of burn_disc_write() and subsequent calls of burn_random_access_write(). @param opts The write opts to change @param value 0=use 2Ah WRITE10, 1=use AAh WRITE12 with Streaming bit @since 0.6.4: >=16 use WRITE12 but not before the LBA given by value @since 0.4.6 */ void burn_write_opts_set_stream_recording(struct burn_write_opts *opts, int value); /* ts A91115 */ /** Overrides the write chunk size for DVD and BD media which is normally determined according to media type and setting of stream recording. A chunk size of 64 KB may improve throughput with bus systems which show latency problems. @param opts The write opts to change @param obs Number of bytes which shall be sent by a single write command. 0 means automatic size, 32768 and 65336 are the only other accepted sizes for now. @since 0.7.4 */ void burn_write_opts_set_dvd_obs(struct burn_write_opts *opts, int obs); /* ts B20406 */ /** Overrides the automatic decision whether to pad up the last write chunk to its full size. This applies to DVD, BD and stdio: pseudo-drives. Note: This override may get enabled fixely already at compile time by defining macro Libburn_dvd_always_obs_paD . @param opts The write opts to change @param pad 1 means to pad up in any case, 0 means automatic decision. @since 1.2.4 */ void burn_write_opts_set_obs_pad(struct burn_write_opts *opts, int pad); /* ts C10909 */ /** Exempts BD-R media from the elsewise unavoidable automatic padding of the last write chunk to its full size. Even if this exempt is granted it gets into effect only if stream recording is disabled and burn_write_opts_set_obs_pad() is set to 0. @param opts The write opts to change @param value 1= possibly exempt BD-R from end chunk padding. 0= always act on BD-R as if burn_write_opts_set_obs_pad(opts, 1) is in effect. @since 1.5.6 */ void burn_write_opts_set_bdr_obs_exempt(struct burn_write_opts *opts, int value); /* ts A91115 */ /** Sets the rhythm by which stdio pseudo drives force their output data to be consumed by the receiving storage device. This forcing keeps the memory from being clogged with lots of pending data for slow devices. @param opts The write opts to change @param rhythm Number of 2KB output blocks after which fsync(2) is performed. -1 means no fsync() 0 means default 1 means fsync() only at end, @since 1.3.8 (noop before 1.3.8) elsewise the value must be >= 32. Default is currently 8192 = 16 MB. @since 0.7.4 */ void burn_write_opts_set_stdio_fsync(struct burn_write_opts *opts, int rhythm); /** 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); /* ts A90815 */ /** Gets the list of profile codes supported by the drive. Profiles depict the feature sets which constitute media types. For known profile codes and names see burn_disc_get_profile(). @param d is the drive to query @param num_profiles returns the number of supported profiles @param profiles returns the profile codes @param is_current returns the status of the corresponding profile code: 1= current, i.e. the matching media is loaded 0= not current, i.e. the matching media is not loaded @return always 1 for now @since 0.7.0 */ int burn_drive_get_all_profiles(struct burn_drive *d, int *num_profiles, int profiles[64], char is_current[64]); /* ts A90815 */ /** Obtains the profile name associated with a profile code. @param profile_code the profile code to be translated @param name returns the profile name (e.g. "DVD+RW") @return 1= known profile code , 0= unknown profile code @since 0.7.0 */ int burn_obtain_profile_name(int profile_code, char name[80]); /* ts B90414 */ /** Obtains the list of SCSI Feature Codes from feature descriptors which were obtained from the drive when it was most recently acquired or re-assessed. @param d Drive to query @param count Returns the number of allocated elements in feature_codes @param feature_codes Returns the allocated array of feature codes. If returned *feature_codes is not NULL, dispose it by free() when it is no longer needed. @since 1.5.2 */ void burn_drive_get_feature_codes(struct burn_drive *d, int *count, unsigned int **feature_codes); /* ts B90414 */ /** Obtains the fields and data of a particular feature which were obtained from the drive when it was last acquired or re-assessed. See MMC specs for full detail. @param d Drive to query @param feature_code A number as learned by burn_drive_get_feature_codes() @param flags Returns byte 2 of the feature descriptor: bit0= Current bit1= Persistent bit2-5= Version @param additional_length Returns byte 3 of descriptor. This is the size of feature_data. @param feature_data Returns further bytes of descriptor. If returned *feature_data is not NULL, dispose it by free() when it is no longer needed. @param feature_text Returns text representation of the feature descriptor: Code +/- : Name : Version,P/N : Hex bytes : Parsed info Current features are marked by "+", others by "-". Persistent features are marked by "P", others by "N". feature_text may be submitted as NULL. In this case no text is generated and returned. If returned *feature_text is not NULL, dispose it by free() when it is no longer needed. @return 0 feature descriptor is not present -1 out of memory >0 success @since 1.5.2 */ int burn_drive_get_feature(struct burn_drive *d, unsigned int feature_code, unsigned char *flags, unsigned char *additional_length, unsigned char **feature_data, char **feature_text); /** Gets the maximum write speed for a drive and eventually loaded media. The return value might change by the media type of already loaded media, again by call burn_drive_grab() and again by call burn_disc_read_atip(). @param d Drive to query @return Maximum write speed in K/s */ int burn_drive_get_write_speed(struct burn_drive *d); /* ts A61021 */ /** Gets the minimum write speed for a drive and eventually loaded media. The return value might change by the media type of already loaded media, again by call burn_drive_grab() and again by call burn_disc_read_atip(). @param d Drive to query @return Minimum write speed in K/s @since 0.2.6 */ int burn_drive_get_min_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); /* ts A61226 */ /** Obtain a copy of the current speed descriptor list. The drive's list gets updated on various occasions such as burn_drive_grab() but the copy obtained here stays untouched. It has to be disposed via burn_drive_free_speedlist() when it is not longer needed. Speeds may appear several times in the list. The list content depends much on drive and media type. It seems that .source == 1 applies mostly to CD media whereas .source == 2 applies to any media. @param d Drive to query @param speed_list The copy. If empty, *speed_list gets returned as NULL. @return 1=success , 0=list empty , <0 severe error @since 0.3.0 */ int burn_drive_get_speedlist(struct burn_drive *d, struct burn_speed_descriptor **speed_list); /* ts A70713 */ /** Look up the fastest speed descriptor which is not faster than the given speed_goal. If it is 0, then the fastest one is chosen among the descriptors with the highest end_lba. If it is -1 then the slowest speed descriptor is chosen regardless of end_lba. Parameter flag decides whether the speed goal means write speed or read speed. @param d Drive to query @param speed_goal Upper limit for speed, 0=search for maximum speed , -1 search for minimum speed @param best_descr Result of the search, NULL if no match @param flag Bitfield for control purposes bit0= look for best read speed rather than write speed bit1= look for any source type (else look for source==2 first and for any other source type only with CD media) @return >0 indicates a valid best_descr, 0 = no valid best_descr @since 0.3.8 */ int burn_drive_get_best_speed(struct burn_drive *d, int speed_goal, struct burn_speed_descriptor **best_descr, int flag); /* ts A61226 */ /** Dispose a speed descriptor list copy which was obtained by burn_drive_get_speedlist(). @param speed_list The list copy. *speed_list gets set to NULL. @return 1=list disposed , 0= *speedlist was already NULL @since 0.3.0 */ int burn_drive_free_speedlist(struct burn_speed_descriptor **speed_list); /* ts A70203 */ /* @since 0.3.2 */ /** The reply structure for burn_disc_get_multi_caps() */ struct burn_multi_caps { /* Multi-session capability can keep the media appendable after writing a session. It also guarantees that the drive will be able to predict and use the appropriate Next Writeable Address to place the next session on the media without overwriting the existing ones. It does not guarantee that the selected write type is able to do an appending session after the next session. (E.g. CD SAO is capable of multi-session by keeping a disc appendable. But .might_do_sao will be 0 afterwards, when checking the appendable media.) 1= media may be kept appendable by burn_write_opts_set_multi(o,1) 0= media will not be appendable */ int multi_session; /* Multi-track capability can write more than one track source during a single session. The written tracks can later be found in libburn's TOC model with their start addresses and sizes. 1= multiple tracks per session are allowed 0= only one track per session allowed */ int multi_track; /* Start-address capability can set a non-zero address with burn_write_opts_set_start_byte(). Eventually this has to respect .start_alignment and .start_range_low, .start_range_high in this structure. 1= non-zero start address is allowed 0= only start address 0 is allowed (to depict the drive's own idea about the appropriate write start) */ int start_adr; /** The alignment for start addresses. ( start_address % start_alignment ) must be 0. */ off_t start_alignment; /** The lowest permissible start address. */ off_t start_range_low; /** The highest addressable start address. */ off_t start_range_high; /** Potential availability of write modes 4= needs no size prediction, not to be chosen automatically 3= needs size prediction, not to be chosen automatically 2= available, no size prediction necessary 1= available, needs exact size prediction 0= not available With CD media (profiles 0x09 and 0x0a) check also the elements *_block_types of the according write mode. */ int might_do_tao; int might_do_sao; int might_do_raw; /** Generally advised write mode. Not necessarily the one chosen by burn_write_opts_auto_write_type() because the burn_disc structure might impose particular demands. */ enum burn_write_types advised_write_mode; /** Write mode as given by parameter wt of burn_disc_get_multi_caps(). */ enum burn_write_types selected_write_mode; /** Profile number which was current when the reply was generated */ int current_profile; /** Whether the current profile indicates CD media. 1=yes, 0=no */ int current_is_cd_profile; /* ts A70528 */ /* @since 0.3.8 */ /** Whether the current profile is able to perform simulated write */ int might_simulate; }; /** Allocates a struct burn_multi_caps (see above) and fills it with values which are appropriate for the drive and the loaded media. The drive must be grabbed for this call. The returned structure has to be disposed via burn_disc_free_multi_caps() when no longer needed. @param d The drive to inquire @param wt With BURN_WRITE_NONE the best capabilities of all write modes get returned. If set to a write mode like BURN_WRITE_SAO the capabilities with that particular mode are returned and the return value is 0 if the desired mode is not possible. @param caps returns the info structure @param flag Bitfield for control purposes (unused yet, submit 0) @return < 0 : error , 0 : writing seems impossible , 1 : writing possible @since 0.3.2 */ int burn_disc_get_multi_caps(struct burn_drive *d, enum burn_write_types wt, struct burn_multi_caps **caps, int flag); /** Removes from memory a multi session info structure which was returned by burn_disc_get_multi_caps(). The pointer *caps gets set to NULL. @param caps the info structure to dispose (note: pointer to pointer) @return 0 : *caps was already NULL, 1 : memory object was disposed @since 0.3.2 */ int burn_disc_free_multi_caps(struct burn_multi_caps **caps); /** 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 complete sessions for the disc THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A SESSION The result array contains *num + burn_disc_get_incomplete_sessions() elements. All above *num are incomplete sessions. Typically there is at most one incomplete session with one empty track. DVD+R and BD-R seem support more than one track with even readable data. @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); /* ts B30112 */ /* @since 1.2.8 */ /** Obtains the number of incomplete sessions which are recorded in the result array of burn_disc_get_sessions() after the complete sessions. See above. @param d Disc object to inquire @return Number of incomplete sessions */ int burn_disc_get_incomplete_sessions(struct burn_disc *d); /** Tells how many sectors a disc will have, or already has. This is the sum of all burn_session_get_sectors() results of the disc. The result is NOT RELIABLE with tracks of undefined length */ int burn_disc_get_sectors(struct burn_disc *d); /* ts C40302 */ /** Like burn_disc_get_sectors(), but with return type off_t. @since 1.5.8 */ off_t burn_disc_get_sectors_v2(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); /** Tells how many sectors a session will have, or already has on disc. This is the sum of all burn_track_get_sectors() results of the session. The result is NOT RELIABLE with tracks of undefined length */ int burn_session_get_sectors(struct burn_session *s); /* ts C40302 */ /** Like burn_session_get_sectors(), but with return type off_t. @since 1.5.8 */ off_t burn_session_get_sectors_v2(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. This is the runtime counterpart of the three build time macros burn_header_version_* below. @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); /* ts A80129 */ /* @since 0.4.4 */ /** These three release version numbers tell the revision of this header file and of the API it describes. They are memorized by applications at build time. Immediately after burn_initialize() an application should do this check: burn_version(&major, &minor, µ); if(major > burn_header_version_major || (major == burn_header_version_major && (minor > burn_header_version_minor || (minor == burn_header_version_minor && micro >= burn_header_version_micro)))) { ... Young enough. Go on with program run .... } else { ... Too old. Do not use this libburn version ... } */ #define burn_header_version_major 1 #define burn_header_version_minor 5 #define burn_header_version_micro 7 /** Note: Above version numbers are also recorded in configure.ac because libtool wants them as parameters at build time. For the library compatibility check, BURN_*_VERSION in configure.ac are not decisive. Only the three numbers above do matter. */ /** Usage discussion: Some developers of the libburnia project have differing opinions how to ensure the compatibility of libraries and applications. It is about whether to use at compile time and at runtime the version numbers isoburn_header_version_* provided here. Thomas Schmitt advises to use them. Vreixo Formoso advises to use other means. At compile time: Vreixo Formoso advises to leave proper version matching to properly programmed checks in the the application's build system, which will eventually refuse compilation. Thomas Schmitt advises to use the macros defined here for comparison with the application's requirements of library revisions and to eventually break compilation. Both advises are combinable. I.e. be master of your build system and have #if checks in the source code of your application, nevertheless. At runtime (via *_is_compatible()): Vreixo Formoso advises to compare the application's requirements of library revisions with the runtime library. This is to enable runtime libraries which are young enough for the application but too old for the lib*.h files seen at compile time. Thomas Schmitt advises to compare the header revisions defined here with the runtime library. This is to enforce a strictly monotonous chain of revisions from app to header to library, at the cost of excluding some older libraries. These two advises are mutually exclusive. */ /* ts A91226 */ /** Obtain the id string of the SCSI transport interface. This interface may be a system specific adapter module of libburn or an adapter to a supporting library like libcdio. @param flag Bitfield for control puposes, submit 0 for now @return A pointer to the id string. Do not alter the string content. @since 0.7.6 */ char *burn_scsi_transport_id(int flag); /* ts A60924 : ticket 74 */ /** Control queueing and stderr printing of messages from libburn. Severity may be one of "NEVER", "ABORT", "FATAL", "FAILURE", "SORRY", "WARNING", "HINT", "NOTE", "UPDATE", "DEBUG", "ALL". @param queue_severity Gives the minimum limit for messages to be queued. Default: "NEVER". If you queue messages then you must consume them by burn_msgs_obtain(). @param print_severity Does the same for messages to be printed directly to stderr. Default: "FATAL". @param print_id A text prefix to be printed before the message. @return >0 for success, <=0 for error @since 0.2.6 */ int burn_msgs_set_severities(char *queue_severity, char *print_severity, char *print_id); /* ts A60924 : ticket 74 */ /* @since 0.2.6 */ #define BURN_MSGS_MESSAGE_LEN 4096 /** Obtain the oldest pending libburn message from the queue which has at least the given minimum_severity. This message and any older message of lower severity will get discarded from the queue and is then lost forever. @param minimum_severity may be one of "NEVER", "ABORT", "FATAL", "FAILURE", "SORRY", "WARNING", "HINT", "NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER" will discard the whole queue. @param error_code Will become a unique error code as listed in libburn/libdax_msgs.h @param msg_text Must provide at least BURN_MSGS_MESSAGE_LEN bytes. @param os_errno Will become the eventual errno related to the message @param severity Will become the severity related to the message and should provide at least 80 bytes. @return 1 if a matching item was found, 0 if not, <0 for severe errors @since 0.2.6 */ int burn_msgs_obtain(char *minimum_severity, int *error_code, char msg_text[], int *os_errno, char severity[]); /* ts A70922 */ /** Submit a message to the libburn queueing system. It will be queued or printed as if it was generated by libburn itself. @param error_code The unique error code of your message. Submit 0 if you do not have reserved error codes within the libburnia project. @param msg_text Not more than BURN_MSGS_MESSAGE_LEN characters of message text. @param os_errno Eventual errno related to the message. Submit 0 if the message is not related to a operating system error. @param severity One of "ABORT", "FATAL", "FAILURE", "SORRY", "WARNING", "HINT", "NOTE", "UPDATE", "DEBUG". Defaults to "FATAL". @param d An eventual drive to which the message shall be related. Submit NULL if the message is not specific to a particular drive object. @return 1 if message was delivered, <=0 if failure @since 0.4.0 */ int burn_msgs_submit(int error_code, char msg_text[], int os_errno, char severity[], struct burn_drive *d); /* ts A71016 */ /** Convert a severity name into a severity number, which gives the severity rank of the name. @param severity_name A name as with burn_msgs_submit(), e.g. "SORRY". @param severity_number The rank number: the higher, the more severe. @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success, <=0 failure @since 0.4.0 */ int burn_text_to_sev(char *severity_name, int *severity_number, int flag); /* ts A80202 */ /** Convert a severity number into a severity name @param severity_number The rank number: the higher, the more severe. @param severity_name A name as with burn_msgs_submit(), e.g. "SORRY". @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success, <=0 failure @since 0.4.4 */ int burn_sev_to_text(int severity_number, char **severity_name, int flag); /* ts B21214 */ /** Return a blank separated list of severity names. Sorted from low to high severity. @param flag Bitfield for control purposes (unused yet, submit 0) @return A constant string with the severity names @since 1.2.6 */ char *burn_list_sev_texts(int flag); /* ts A70915 */ /** Replace the messenger object handle of libburn by a compatible handle obtained from a related library. See also: libisofs, API function iso_get_messenger(). @param messenger The foreign but compatible message handle. @return 1 : success, <=0 : failure @since 0.4.0 */ int burn_set_messenger(void *messenger); /* ts A61002 */ /* @since 0.2.6 */ /** The prototype of a handler function suitable for burn_set_signal_handling() Such a function has to return -2 if it does not want the process to exit with value 1. */ typedef int (*burn_abort_handler_t)(void *handle, int signum, int flag); /** Control built-in signal handling. Either by setting an own handler or by activating the built-in signal handler. A function parameter handle of NULL activates the built-in abort handler. Depending on mode it may cancel all drive operations, wait for all drives to become idle, exit(1). It may also prepare function burn_drive_get_status() for waiting and performing exit(1). Parameter handle may be NULL or a text that shall be used as prefix for pacifier messages of burn_abort_pacifier(). Other than with an application provided handler, the prefix char array does not have to be kept existing until the eventual signal event. Before version 0.7.8 only action 0 was available. I.e. the built-in handler waited for the drives to become idle and then performed exit(1) directly. But during burn_disc_write() onto real CD or DVD, FreeBSD 8.0 pauses the other threads until the signal handler returns. The new actions try to avoid this deadlock. It is advised to use action 3 at least during burn_disc_write(), burn_disc_erase(), burn_disc_format(): burn_set_signal_handling(text, NULL, 0x30); and to call burn_is_aborting(0) when the drive is BURN_DRIVE_IDLE. If burn_is_aborting(0) returns 1, then call burn_abort() and exit(1). @param handle Opaque handle eventually pointing to an application provided memory object @param handler A function to be called on signals, if the handling bits in parameter mode are set 0. It will get parameter handle as argument. flag will be 0. It should finally call burn_abort(). See there. If the handler function returns 2 or -2, then the wrapping signal handler of libburn will return and let the program continue its operations. Any other return value causes exit(1). @param mode : bit0 - bit3: Handling of received signals: 0 Install libburn wrapping signal handler, which will call handler(handle, signum, 0) on nearly all signals 1 Enable system default reaction on all signals 2 Try to ignore nearly all signals 10 like mode 2 but handle SIGABRT like with mode 0 bit4 - bit7: With handler == NULL : Action of built-in handler. "control thread" is the one which called burn_set_signal_handling(). All actions activate receive mode 2 to ignore further signals. 0 Same as 1 (for pre-0.7.8 backward compatibility) @since 0.7.8 1 Catch the control thread in abort handler, call burn_abort() with a patience value > 0 and finally exit(1). Does not always work with FreeBSD. 2 Call burn_abort() with patience -1 and return from handler. When the control thread calls burn_drive_get_status(), then call burn_abort() with patience 1 instead, and finally exit(1). Does not always work with FreeBSD. 3 Call burn_abort() with patience -1, return from handler. It is duty of the application to detect a pending abort condition by calling burn_is_aborting() and to wait for all drives to become idle. E.g. by calling burn_abort() with patience >0. 4 Like 3, but without calling burn_abort() with -1. Only the indicator of burn_is_aborting() gets set. bit8: @since 1.3.2 try to ignore SIGPIPE (regardless of bit0 - bit3) @since 0.2.6 */ void burn_set_signal_handling(void *handle, burn_abort_handler_t handler, int mode); /* ts B00304 */ /* Inquire whether the built-in abort handler was triggered by a signal. This has to be done to detect pending abort handling if signal handling was set to the built-in handler and action was set to 2 or 3. @param flag Bitfield for control purposes (unused yet, submit 0) @return 0 = no abort was triggered >0 = action that was triggered (action 0 is reported as 1) @since 0.7.8 */ int burn_is_aborting(int flag); /* ts A70811 */ /** Write data in random access mode. The drive must be grabbed successfully before calling this function which circumvents usual libburn session processing and rather writes data without preparations or finalizing. This will work only with overwritable media which are also suitable for burn_write_opts_set_start_byte(). The same address alignment restrictions as with this function apply. I.e. for DVD it is best to align to 32 KiB blocks (= 16 LBA units). The amount of data to be written is subject to the same media dependent alignment rules. Again, 32 KiB is most safe. Call burn_disc_get_multi_caps() can obtain the necessary media info. See resulting struct burn_multi_caps elements .start_adr , .start_alignment , .start_range_low , .start_range_high . Other than burn_disc_write() this is a synchronous call which returns only after the write transaction has ended (successfully or not). So it is wise not to transfer giant amounts of data in a single call. Important: Data have to fit into the already formatted area of the media. If the burn_drive object is in simulation mode, then no actual write operation or synchronization of the drive buffer will happen. See burn_drive_reset_simulate(). @param d The drive to which to write @param byte_address The start address of the write in byte (1 LBA unit = 2048 bytes) (do respect media alignment) @param data The bytes to be written @param data_count The number of those bytes (do respect media alignment) data_count == 0 is permitted (e.g. to flush the drive buffer without further data transfer). @param flag Bitfield for control purposes: bit0 = flush the drive buffer after eventual writing @return 1=successful , <=0 : number of transferred bytes * -1 @since 0.4.0 */ int burn_random_access_write(struct burn_drive *d, off_t byte_address, char *data, off_t data_count, int flag); /* ts A81215 */ /** Inquire the maximum amount of readable data. On DVD and BD it is supposed that all LBAs in the range from 0 to capacity - 1 can be read via burn_read_data() although some of them may never have been recorded. With multi-session CD there have to be expected unreadable TAO Run-out blocks. If tracks are recognizable then it is better to only read LBAs which are part of some track and on CD to be cautious about the last two blocks of each track which might be TAO Run-out blocks. If the drive is actually a large file or block device, then the capacity is curbed to a maximum of 0x7ffffff0 blocks = 4 TB - 32 KB. @param d The drive from which to read @param capacity Will return the result if valid @param flag Bitfield for control purposes: Unused yet, submit 0. @return 1=successful , <=0 an error occurred @since 0.6.0 */ int burn_get_read_capacity(struct burn_drive *d, int *capacity, int flag); /* ts C40303 */ /** Like burn_get_read_capacity(), but with off_t result. @since 1.5.8 */ int burn_get_read_capacity_v2(struct burn_drive *d, off_t *capacity, int flag); /* ts A70812 */ /** Read data in random access mode. The drive must be grabbed successfully before calling this function. With all currently supported drives and media the byte_address has to be aligned to 2048 bytes. Only data tracks with 2048 bytes per sector can be read this way. I.e. not CD-audio, not CD-video-stream ... This is a synchronous call which returns only after the full read job has ended (successfully or not). So it is wise not to read giant amounts of data in a single call. @param d The drive from which to read @param byte_address The start address of the read in byte (aligned to 2048) @param data A memory buffer capable of taking data_size bytes @param data_size The amount of data to be read. This does not have to be aligned to any block size. @param data_count The amount of data actually read (interesting on error) The counted bytes are supposed to be valid. @param flag Bitfield for control purposes: bit0= - reserved - bit1= do not submit error message if read error bit2= on error do not try to read a second time with single block steps. @since 0.5.2 bit3= return -2 on permission denied error rather than issuing a warning message. @since 1.0.6 bit4= return -3 on SCSI error 5 64 00 ILLEGAL MODE FOR THIS TRACK and prevent this error from being reported as event message. Do not retry reading in this case. (Useful to try the last two blocks of a CD track which might be non-data because of TAO.) @since 1.2.6 bit5= issue messages with severity DEBUG if they would be suppressed by bit1. @since 1.4.0 @return 1=successful , <=0 an error occurred with bit3: -2= permission denied error @since 0.4.0 */ int burn_read_data(struct burn_drive *d, off_t byte_address, char data[], off_t data_size, off_t *data_count, int flag); /* ts B21119 */ /** Read CD audio sectors in random access mode. The drive must be grabbed successfully before calling this function. Only CD audio tracks with 2352 bytes per sector can be read this way. I.e. not data tracks, not CD-video-stream, ... Note that audio data do not have exact block addressing. If you read a sequence of successive blocks then you will get a seamless stream of data. But the actual start and end position of this audio stream will differ by a few dozens of milliseconds, depending on individual CD and individual drive. Expect leading and trailing zeros, as well as slight truncation. @param d The drive from which to read. It must be a real MMC drive (i.e. not a stdio file) and it must have a CD loaded (i.e. not DVD or BD). @param sector_no The sector number (Logical Block Address) It may be slightly below 0, depending on drive and medium. -150 is a lower limit. @param data A memory buffer capable of taking data_size bytes @param data_size The amount of data to be read. This must be aligned to full multiples of 2352. @param data_count The amount of data actually read (interesting on error) @param flag Bitfield for control purposes: bit0= - reserved - bit1= do not submit error message if read error bit2= on error do not try to read a second time with single block steps. bit3= Enable DAP : "flaw obscuring mechanisms like audio data mute and interpolate" bit4= return -3 on SCSI error 5 64 00 ILLEGAL MODE FOR THIS TRACK and prevent this error from being reported as event message. Do not retry reading in this case. (Useful to try the last two blocks of a CD track which might be non-audio because of TAO.) bit5= issue messages with severity DEBUG if they would be suppressed by bit1. @since 1.4.0 @return 1=successful , <=0 an error occurred with bit3: -2= permission denied error @since 1.2.6 */ int burn_read_audio(struct burn_drive *d, int sector_no, char data[], off_t data_size, off_t *data_count, int flag); /* ts B30522 */ /** Extract an interval of audio sectors from CD and store it as a WAVE audio file on hard disk. @param drive The drive from which to read. @param start_sector The logical block address of the first audio sector which shall be read. @param sector_count The number of audio sectors to be read. Each sector consists of 2352 bytes. @param target_path The address of the file where to store the extracted audio data. Will be opened O_WRONLY | O_CREAT. The file name should have suffix ".wav". @param flag Bitfield for control purposes: bit0= Report about progress by UPDATE messages bit3= Enable DAP : "flaw obscuring mechanisms like audio data mute and interpolate" @since 1.3.2 */ int burn_drive_extract_audio(struct burn_drive *drive, int start_sector, int sector_count, char *target_path, int flag); /* ts B30522 */ /** Extract all audio sectors of a track from CD and store them as a WAVE audio file on hard disk. @param drive The drive from which to read. @param track The track which shall be extracted. @param target_path The address of the file where to store the extracted audio data. Will be opened O_WRONLY | O_CREAT. The file name should have suffix ".wav". @param flag Bitfield for control purposes: bit0= Report about progress by UPDATE messages bit3= Enable DAP : "flaw obscuring mechanisms like audio data mute and interpolate" @since 1.3.2 */ int burn_drive_extract_audio_track(struct burn_drive *drive, struct burn_track *track, char *target_path, int flag); /* ts A70904 */ /** Inquire whether the drive object is a real MMC drive or a pseudo-drive created by a stdio: address. @param d The drive to inquire @return 0= null-drive 1= real MMC drive 2= stdio-drive, random access, read-write 3= stdio-drive, sequential, write-only 4= stdio-drive, random access, read-only (only if enabled by burn_allow_drive_role_4()) 5= stdio-drive, random access, write-only (only if enabled by burn_allow_drive_role_4()) @since 0.4.0 */ int burn_drive_get_drive_role(struct burn_drive *d); /* ts B10312 */ /** Allow drive role 4 "random access read-only" and drive role 5 "random access write-only". By default a random access file assumes drive role 2 "read-write" regardless whether it is actually readable or writeable. If enabled, random-access file objects which recognizably permit no writing will be classified as role 4 and those which permit no reading will get role 5. Candidates are drive addresses of the form stdio:/dev/fd/# , where # is the integer number of an open file descriptor. If this descriptor was opened read-only or write-only, then it gets role 4 or role 5, respectively. Other paths may get tested by an attempt to open them for read-write (role 2) or read-only (role 4) or write-only (role 5). See bit1. @param allowed Bitfield for control purposes: bit0= Enable roles 4 and 5 for drives which get acquired after this call bit1= with bit0: Test whether the file can be opened for read-write, read-only, or write-only. Classify as roles 2, 4, 5. bit2= with bit0 and bit1: Classify files which cannot be opened at all as role 0 : useless dummy. Else classify as role 2. bit3= Classify non-empty role 5 drives as BURN_DISC_APPENDABLE with Next Writeable Address after the end of the file. It is nevertheless possible to change this address by call burn_write_opts_set_start_byte(). @since 1.0.6 */ void burn_allow_drive_role_4(int allowed); /* ts A70923 */ /** Find out whether a given address string would lead to the given drive object. This should be done in advance for track source addresses with parameter drive_role set to 2. Although a real MMC drive should hardly exist as two drive objects at the same time, this can easily happen with stdio-drives. So if more than one drive is used by the application, then this gesture is advised: burn_drive_d_get_adr(d2, adr2); if (burn_drive_equals_adr(d1, adr2, burn_drive_get_drive_role(d2))) ... Both drive objects point to the same storage facility ... @param d1 Existing drive object @param adr2 Address string to be tested. Prefix "stdio:" overrides parameter drive_role2 by either 0 or 2 as appropriate. The string must be shorter than BURN_DRIVE_ADR_LEN. @param drive_role2 Role as burn_drive_get_drive_role() would attribute to adr2 if it was a drive. Use value 2 for checking track sources or pseudo-drive addresses without "stdio:". Use 1 for checking drive addresses including those with prefix "stdio:". @return 1= adr2 leads to d1 , 0= adr2 seems not to lead to d1, -1 = adr2 is bad @since 0.4.0 */ int burn_drive_equals_adr(struct burn_drive *d1, char *adr2, int drive_role2); /* Audio track data extraction facility. */ /* Maximum size for address paths and fmt_info strings */ #define LIBDAX_AUDIOXTR_STRLEN 4096 /** Extractor object encapsulating intermediate states of extraction. The clients of libdax_audioxtr shall only allocate pointers to this struct and get a storage object via libdax_audioxtr_new(). Appropriate initial value for the pointer is NULL. */ struct libdax_audioxtr; /** Open an audio file, check whether suitable, create extractor object. @param xtr Opaque handle to extractor. Gets attached extractor object. @param path Address of the audio file to extract. "-" is stdin (but might be not suitable for all futurely supported formats). @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success 0 unsuitable format -1 severe error -2 path not found @since 0.2.4 */ int libdax_audioxtr_new(struct libdax_audioxtr **xtr, char *path, int flag); /** Obtain identification parameters of opened audio source. @param xtr Opaque handle to extractor @param fmt Gets pointed to the audio file format id text: ".wav" , ".au" @param fmt_info Gets pointed to a format info text telling parameters @param num_channels e.g. 1=mono, 2=stereo, etc @param sample_rate e.g. 11025, 44100 @param bits_per_sample e.g. 8= 8 bits per sample, 16= 16 bits ... @param msb_first Byte order of samples: 0= Intel = Little Endian 1= Motorola = Big Endian @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success, <=0 failure @since 0.2.4 */ int libdax_audioxtr_get_id(struct libdax_audioxtr *xtr, char **fmt, char **fmt_info, int *num_channels, int *sample_rate, int *bits_per_sample, int *msb_first, int flag); /** Obtain a prediction about the extracted size based on internal information of the formatted file. @param o Opaque handle to extractor @param size Gets filled with the predicted size @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 prediction was possible , 0 no prediction could be made @since 0.2.4 */ int libdax_audioxtr_get_size(struct libdax_audioxtr *o, off_t *size, int flag); /** Obtain next buffer full of extracted data in desired format (only raw audio for now). @param xtr Opaque handle to extractor @param buffer Gets filled with extracted data @param buffer_size Maximum number of bytes to be filled into buffer @param flag Bitfield for control purposes bit0= do not stop at predicted end of data @return >0 number of valid buffer bytes, 0 End of file -1 operating system reports error -2 usage error by application @since 0.2.4 */ int libdax_audioxtr_read(struct libdax_audioxtr *xtr, char buffer[], int buffer_size, int flag); /** Try to obtain a file descriptor which will deliver extracted data to normal calls of read(2). This may fail because the format is unsuitable for that, but WAVE (.wav) is ok. If this call succeeds the xtr object will have forgotten its file descriptor and libdax_audioxtr_read() will return a usage error. One may use *fd after libdax_audioxtr_destroy() and will have to close it via close(2) when done with it. @param o Opaque handle to extractor @param fd Returns the file descriptor number @param flag Bitfield for control purposes bit0= do not dup(2) and close(2) but hand out original fd @return 1 success, 0 cannot hand out fd , -1 severe error @since 0.2.4 */ int libdax_audioxtr_detach_fd(struct libdax_audioxtr *o, int *fd, int flag); /** Clean up after extraction and destroy extractor object. @param xtr Opaque handle to extractor, *xtr is allowed to be NULL, *xtr is set to NULL by this function @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 = destroyed object, 0 = was already destroyed @since 0.2.4 */ int libdax_audioxtr_destroy(struct libdax_audioxtr **xtr, int flag); #ifndef DOXYGEN BURN_END_DECLS #endif /* ts A91205 */ /* The following experiments may be interesting in future: */ /* Perform OPC explicitly. # define Libburn_pioneer_dvr_216d_with_opC 1 */ /* Load mode page 5 and modify it rather than composing from scratch. # define Libburn_pioneer_dvr_216d_load_mode5 1 */ /* Inquire drive events and react by reading configuration or starting unit. # define Libburn_pioneer_dvr_216d_get_evenT 1 */ /* ts A91112 */ /* Do not probe CD modes but declare only data and audio modes supported. For other modes or real probing one has to call burn_drive_probe_cd_write_modes(). */ #define Libburn_dummy_probe_write_modeS 1 /* ts B30112 */ /* Handle DVD+R with reserved tracks in incomplete first session by loading info about the incomplete session into struct burn_disc */ #define Libburn_disc_with_incomplete_sessioN 1 /* Early experimental: Do not define Libburn_develop_quality_scaN unless you want to work towards a usable implementation. If it gets enabled, then the call must be published in libburn/libburn.ver */ #ifdef Libburn_develop_quality_scaN /* ts B21108 */ /* Experiments mit quality scan command F3 on Optiarc drive */ int burn_nec_optiarc_rep_err_rate(struct burn_drive *d, int start_lba, int rate_period, int flag); #endif /* Libburn_develop_quality_scaN */ /* Linux 3.16 problems with ABh Read Media Serial Number: - as normal user lets ioctl(SG_IO) return -1 and errno = EFAULT - as superuser renders LG BH16NS40 unusable until power cycle #de fine Libburn_enable_scsi_cmd_ABh yes #de fine Libburn_enable_scsi_cmd_ABh_pretend_currenT yes */ #endif /*LIBBURN_H*/ LIBBURN4 { global: burn_abort; burn_abort_pacifier; burn_allow_drive_role_4; burn_allow_untested_profiles; burn_cdtext_from_session; burn_cdtext_from_packfile; burn_disc_add_session; burn_disc_available_space; burn_disc_close_damaged; burn_disc_create; burn_disc_erasable; burn_disc_erase; burn_disc_format; burn_disc_free; burn_disc_free_multi_caps; burn_disc_get_bd_spare_info; burn_disc_get_cd_info; burn_disc_get_format_descr; burn_disc_get_formats; burn_disc_get_incomplete_sessions; burn_disc_get_leadin_text; burn_disc_get_media_id; burn_disc_get_msc1; burn_disc_get_multi_caps; burn_disc_get_phys_format_info; burn_disc_get_profile; burn_disc_get_sectors; burn_disc_get_sessions; burn_disc_get_status; burn_disc_next_track_is_damaged; burn_disc_pretend_blank; burn_disc_pretend_full; burn_disc_pretend_full_uncond; burn_disc_read; burn_disc_read_atip; burn_disc_remove_session; burn_disc_track_lba_nwa; burn_disc_write; burn_drive_add_whitelist; burn_drive_cancel; burn_drive_clear_whitelist; burn_drive_convert_fs_adr; burn_drive_convert_scsi_adr; burn_drive_d_get_adr; burn_drive_equals_adr; burn_drive_extract_audio; burn_drive_extract_audio_track; burn_drive_free_speedlist; burn_drive_get_adr; burn_drive_get_all_profiles; burn_drive_get_bd_r_pow; burn_drive_get_best_speed; burn_drive_get_disc; burn_drive_get_drive_role; burn_drive_get_feature; burn_drive_get_feature_codes; burn_drive_get_immed; burn_drive_get_media_sno; burn_drive_get_min_write_speed; burn_drive_get_read_speed; burn_drive_get_serial_no; burn_drive_get_speedlist; burn_drive_get_start_end_lba; burn_drive_get_status; burn_drive_get_write_speed; burn_drive_grab; burn_drive_info_forget; burn_drive_info_free; burn_drive_is_enumerable_adr; burn_drive_leave_locked; burn_drive_obtain_scsi_adr; burn_drive_probe_cd_write_modes; burn_drive_re_assess; burn_drive_release; burn_drive_reset_simulate; burn_drive_scan; burn_drive_scan_and_grab; burn_drive_set_buffer_waiting; burn_drive_set_immed; burn_drive_set_speed; burn_drive_set_speed_exact; burn_drive_set_stream_recording; burn_drive_snooze; burn_drive_was_feat21_failure; burn_drive_wrote_well; burn_fd_source_new; burn_fifo_fill; burn_fifo_get_statistics; burn_fifo_inquire_status; burn_fifo_next_interval; burn_fifo_peek_data; burn_fifo_source_new; burn_file_source_new; burn_finish; burn_get_read_capacity; burn_guess_cd_manufacturer; burn_guess_manufacturer; burn_initialize; burn_is_aborting; burn_lba_to_msf; burn_list_sev_texts; burn_lookup_device_link; burn_make_input_sheet_v07t; burn_msf_to_lba; burn_msf_to_sectors; burn_msgs_obtain; burn_msgs_set_severities; burn_msgs_submit; burn_nominal_slowdown; burn_obtain_profile_name; burn_offst_source_new; burn_os_alloc_buffer; burn_os_free_buffer; burn_os_open_track_src; burn_precheck_write; burn_preset_device_open; burn_random_access_write; burn_read_audio; burn_read_data; burn_read_opts_free; burn_read_opts_new; burn_read_opts_read_subcodes_audio; burn_read_opts_read_subcodes_data; burn_read_opts_report_recovered_errors; burn_read_opts_set_c2errors; burn_read_opts_set_hardware_error_recovery; burn_read_opts_set_hardware_error_retries; burn_read_opts_set_raw; burn_read_opts_transfer_damaged_blocks; burn_scsi_transport_id; burn_sectors_to_msf; burn_session_add_track; burn_session_by_cue_file; burn_session_create; burn_session_dispose_cdtext; burn_session_free; burn_session_get_cdtext; burn_session_get_cdtext_par; burn_session_get_hidefirst; burn_session_get_leadout_entry; burn_session_get_sectors; burn_session_get_start_tno; burn_session_get_tracks; burn_session_hide_first_track; burn_session_input_sheet_v07t; burn_session_remove_track; burn_session_set_cdtext; burn_session_set_cdtext_par; burn_session_set_start_tno; burn_set_messenger; burn_set_scsi_logging; burn_set_signal_handling; burn_set_verbosity; burn_sev_to_text; burn_source_free; burn_structure_print_disc; burn_structure_print_session; burn_structure_print_track; burn_text_to_sev; burn_track_clear_indice; burn_track_clear_isrc; burn_track_create; burn_track_define_data; burn_track_dispose_cdtext; burn_track_free; burn_track_get_cdtext; burn_track_get_counters; burn_track_get_entry; burn_track_get_mode; burn_track_get_sectors; burn_track_set_byte_swap; burn_track_set_cdxa_conv; burn_track_set_cdtext; burn_track_set_default_size; burn_track_set_index; burn_track_set_isrc; burn_track_set_isrc_string; burn_track_set_postgap_size; burn_track_set_pregap_size; burn_track_set_size; burn_track_set_source; burn_version; burn_write_opts_auto_write_type; burn_write_opts_free; burn_write_opts_get_drive; burn_write_opts_new; burn_write_opts_set_bdr_obs_exempt; burn_write_opts_set_dvd_obs; burn_write_opts_set_fail21h_sev; burn_write_opts_set_fillup; burn_write_opts_set_force; burn_write_opts_set_format; burn_write_opts_set_has_mediacatalog; burn_write_opts_set_leadin_text; burn_write_opts_set_mediacatalog; burn_write_opts_set_multi; burn_write_opts_set_obs_pad; burn_write_opts_set_perform_opc; burn_write_opts_set_simulate; burn_write_opts_set_start_byte; burn_write_opts_set_stdio_fsync; burn_write_opts_set_stream_recording; burn_write_opts_set_toc_entries; burn_write_opts_set_underrun_proof; burn_write_opts_set_write_type; libdax_audioxtr_destroy; libdax_audioxtr_detach_fd; libdax_audioxtr_get_id; libdax_audioxtr_get_size; libdax_audioxtr_new; libdax_audioxtr_read; local: *; }; LIBBURN4_1.5.8 { burn_disc_get_sectors_v2; burn_disc_track_lba_nwa_v2; burn_drive_get_status_v2; burn_get_read_capacity_v2; burn_session_get_sectors_v2; burn_track_get_sectors_v2; } LIBBURN4; /* libdax_audioxtr Audio track data extraction facility of libdax and libburn. Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPLv2+ */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <errno.h> /* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /* Only this single source module is entitled to do this */ #define LIBDAX_AUDIOXTR_H_INTERNAL 1 /* All clients of the extraction facility must do this or include libburn.h */ #define LIBDAX_AUDIOXTR_H_PUBLIC 1 #include "libdax_audioxtr.h" int libdax_audioxtr_new(struct libdax_audioxtr **xtr, char *path, int flag) { int ret= -1; struct libdax_audioxtr *o; o= *xtr= (struct libdax_audioxtr *) calloc(1, sizeof(struct libdax_audioxtr)); if(o==NULL) return(-1); strncpy(o->path,path,LIBDAX_AUDIOXTR_STRLEN-1); o->path[LIBDAX_AUDIOXTR_STRLEN-1]= 0; o->fd= -1; strcpy(o->fmt,"unidentified"); o->fmt_info[0]= 0; o->data_size= 0; o->extract_count= 0; o->num_channels= 0; o->sample_rate= 0; o->bits_per_sample= 0; o->msb_first= 0; o->wav_data_location= 44; o->wav_subchunk2_size= 0; o->au_data_location= 0; o->au_data_size= 0xffffffff; ret= libdax_audioxtr_open(o,0); if(ret<=0) {ret= -2*(ret<0); goto failure;} return(1); failure: libdax_audioxtr_destroy(xtr,0); return(ret); } int libdax_audioxtr_destroy(struct libdax_audioxtr **xtr, int flag) { struct libdax_audioxtr *o; o= *xtr; if(o==NULL) return(0); if(o->fd>=0 && strcmp(o->path,"-")!=0) close(o->fd); free((char *) o); *xtr= NULL; return(1); } static int libdax_audioxtr_open(struct libdax_audioxtr *o, int flag) { int ret; char msg[LIBDAX_AUDIOXTR_STRLEN+80]; if(strcmp(o->path,"-")==0) o->fd= 0; else o->fd= open(o->path, O_RDONLY | O_BINARY); if(o->fd<0) { sprintf(msg,"Cannot open audio source file : %s",o->path); libdax_msgs_submit(libdax_messenger,-1,0x00020200, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); return(-1); } ret= libdax_audioxtr_identify(o,0); if(ret<=0) { sprintf(msg,"Audio source file has unsuitable format : %s",o->path); libdax_msgs_submit(libdax_messenger,-1,0x00020201, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); return(0); } ret= libdax_audioxtr_init_reading(o,0); if(ret<=0) { sprintf(msg,"Failed to prepare reading of audio data : %s",o->path); libdax_msgs_submit(libdax_messenger,-1,0x00020202, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); return(0); } return(1); } /* @param flag: bit0= sequential file, skip by reading data */ static int libdax_audioxtr_skip(struct libdax_audioxtr *o, off_t *old_pos, off_t pos, int flag) { int ret; size_t to_read; static char buf[256]; /* Thread safe because the content does not matter */ if((flag & 1) || o->fd == 0) { /* stdin */ while(pos - *old_pos > 0) { to_read= pos - *old_pos; if(to_read > sizeof(buf)) to_read= sizeof(buf); ret= read(o->fd, buf, to_read); if(ret < (int) to_read) return(0); *old_pos+= to_read; } } else { ret= lseek(o->fd, pos, SEEK_SET); if(ret == -1) return(0); *old_pos= pos; } return(1); } static int libdax_audioxtr_identify_wav(struct libdax_audioxtr *o, int flag) { int ret, fmt_seen= 0, data_seen= 0; off_t pos= 0, old_pos= 0, riff_end= 0; char buf[16]; unsigned char *ubuf; /* check whether this is a MS WAVE file .wav */ /* info used: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ https://en.wikipedia.org/wiki/WAV see summary in: doc/waveformat.txt */ ubuf= (unsigned char *) buf; /* Look for ChunkID "RIFF" , tolerate other known chunks */ while(1) { ret= libdax_audioxtr_skip(o, &old_pos, pos, 0); if(ret <= 0) return(0); ret= read(o->fd, buf, 8); if(ret < 8) return(0); old_pos+= 8; pos= old_pos + libdax_audioxtr_to_int(o, ubuf + 4, 4, 0); if(pos > 0xffffffff || pos - old_pos < 4) /* Too large or no Format word */ return(0); if(strncmp(buf, "RIFF", 4) == 0) break; /* Wikipedia mentions these known ChunkId values */ if(strncmp(buf, "INFO", 4) == 0 || strncmp(buf, "CSET", 4) == 0 || strncmp(buf, "JUNK", 4) == 0 || strncmp(buf, "PAD ", 4) == 0) continue; return(0); } /* Read RIFF Format header */ ret= read(o->fd, buf, 4); if(ret < 4) return(0); old_pos+= 4; if(strncmp(buf, "WAVE", 4) != 0) /* Format */ return(0); riff_end= pos; /* Look for SubchunkID "fmt " and "data" */ pos= old_pos; while(old_pos < riff_end) { ret= libdax_audioxtr_skip(o, &old_pos, pos, 0); if(ret <= 0) return(0); ret= read(o->fd, buf, 8); if(ret < 8) return(0); old_pos= pos + 8; pos= old_pos + libdax_audioxtr_to_int(o, ubuf + 4, 4, 0); /* SubchunkSize */ if(strncmp(buf,"fmt ", 4) == 0) { if(pos - old_pos < 16) return(0); ret= read(o->fd, buf, 16); if(ret < 16) return(0); old_pos+= 16; if(buf[0]!=1 || buf[1]!=0) /* AudioFormat (1 = Linear quantization) */ return(0); o->msb_first= 0; o->num_channels= libdax_audioxtr_to_int(o, ubuf + 2 , 2, 0); o->sample_rate= libdax_audioxtr_to_int(o, ubuf + 4, 4, 0); o->bits_per_sample= libdax_audioxtr_to_int(o, ubuf + 14, 2, 0); sprintf(o->fmt_info, ".wav , num_channels=%d , sample_rate=%d , bits_per_sample=%d", o->num_channels, o->sample_rate, o->bits_per_sample); fmt_seen= 1; } else if(strncmp(buf,"data", 4) == 0) { o->wav_data_location= old_pos; o->wav_subchunk2_size= pos - old_pos; o->data_size= o->wav_subchunk2_size; data_seen= 1; } if(fmt_seen && data_seen) { strcpy(o->fmt,".wav"); return(1); } } return(0); } static int libdax_audioxtr_identify_au(struct libdax_audioxtr *o, int flag) { int ret,encoding; char buf[24]; /* Check whether this is a Sun Audio, .au file */ /* info used: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */ if(o->fd!=0) { ret= lseek(o->fd,0,SEEK_SET); if(ret==-1) return(0); } ret= read(o->fd, buf, 24); if(ret<24) return(0); if(strncmp(buf,".snd",4)!=0) return(0); strcpy(o->fmt,".au"); o->msb_first= 1; o->au_data_location= libdax_audioxtr_to_int(o,(unsigned char *)buf+4,4,1); o->au_data_size= libdax_audioxtr_to_int(o,(unsigned char *)buf+8,4,1); encoding= libdax_audioxtr_to_int(o,(unsigned char *)buf+12,4,1); if(encoding==2) o->bits_per_sample= 8; else if(encoding==3) o->bits_per_sample= 16; else if(encoding==4) o->bits_per_sample= 24; else if(encoding==5) o->bits_per_sample= 32; else o->bits_per_sample= -encoding; o->sample_rate= libdax_audioxtr_to_int(o,(unsigned char *)buf+16,4,1); o->num_channels= libdax_audioxtr_to_int(o,(unsigned char *)buf+20,4,1); if(o->au_data_size!=0xffffffff) o->data_size= o->au_data_size; else o->data_size= 0; sprintf(o->fmt_info, ".au , num_channels=%d , sample_rate=%d , bits_per_sample=%d", o->num_channels,o->sample_rate,o->bits_per_sample); return(o->bits_per_sample>0); /* Audio format must be linear PCM */ } static int libdax_audioxtr_identify(struct libdax_audioxtr *o, int flag) { int ret; ret= libdax_audioxtr_identify_wav(o, 0); if(ret!=0) return(ret); if(o->fd==0) /* cannot rewind stdin */ return(0); ret= libdax_audioxtr_identify_au(o, 0); if(ret!=0) return(ret); return(0); } /* @param flag bit0=msb_first */ static unsigned libdax_audioxtr_to_int(struct libdax_audioxtr *o, unsigned char *bytes, int len, int flag) { unsigned int ret= 0; int i; if(flag&1) for(i= 0; i<len; i++) ret= ret*256+bytes[i]; else for(i= len-1; i>=0; i--) ret= ret*256+bytes[i]; return(ret); } static int libdax_audioxtr_init_reading(struct libdax_audioxtr *o, int flag) { int ret; /* currently this only works for MS WAVE files .wav and Sun .au*/; if(o->fd==0) /* stdin: hope no read came after libdax_audioxtr_identify() */ return(1); o->extract_count= 0; if(strcmp(o->fmt,".wav")==0) ret= lseek(o->fd, o->wav_data_location, SEEK_SET); else if(strcmp(o->fmt,".au")==0) ret= lseek(o->fd,o->au_data_location,SEEK_SET); else ret= -1; if(ret==-1) return(0); return(1); } int libdax_audioxtr_get_id(struct libdax_audioxtr *o, char **fmt, char **fmt_info, int *num_channels, int *sample_rate, int *bits_per_sample, int *msb_first, int flag) { *fmt= o->fmt; *fmt_info= o->fmt_info; *num_channels= o->num_channels; *sample_rate= o->sample_rate; *bits_per_sample= o->bits_per_sample; *msb_first= o->msb_first; return(1); } int libdax_audioxtr_get_size(struct libdax_audioxtr *o, off_t *size, int flag) { *size= o->data_size; return(1); } int libdax_audioxtr_read(struct libdax_audioxtr *o, char buffer[], int buffer_size, int flag) { int ret; if(buffer_size<=0 || o->fd<0) return(-2); if(o->data_size>0 && !(flag&1)) if(buffer_size > o->data_size - o->extract_count) buffer_size= o->data_size - o->extract_count; if(buffer_size<=0) return(0); ret= read(o->fd,buffer,buffer_size); if(ret>0) o->extract_count+= ret; return(ret); } int libdax_audioxtr_detach_fd(struct libdax_audioxtr *o, int *fd, int flag) { if(o->fd<0) return(-1); if(strcmp(o->fmt,".wav")!=0 && strcmp(o->fmt,".au")!=0) return(0); if(flag&1) { *fd= o->fd; } else { *fd= dup(o->fd); if(*fd>=0 && strcmp(o->path,"-")!=0) close(o->fd); } if(*fd>=0) { o->fd= -1; return(1); } return(-1); } /* libdax_audioxtr Audio track data extraction facility of libdax and libburn. Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPLv2+ */ #ifndef LIBDAX_AUDIOXTR_H_INCLUDED #define LIBDAX_AUDIOXTR_H_INCLUDED 1 /* Normally this public API is defined in <libburn/libburn.h> Macro LIBDAX_AUDIOXTR_H_PUBLIC enables the definition for programs which only include this file. */ #ifdef LIBDAX_AUDIOXTR_H_PUBLIC /* Public Macros */ /* Maximum size for address paths and fmt_info strings */ #define LIBDAX_AUDIOXTR_STRLEN 4096 /* Public Opaque Handles */ /** Extractor object encapsulating intermediate states of extraction. The clients of libdax_audioxtr shall only allocate pointers to this struct and get a storage object via libdax_audioxtr_new(). Appropriate initial value for the pointer is NULL. */ struct libdax_audioxtr; /* Public Functions */ /* Calls initiated from inside libdax/libburn */ /* Calls from applications (to be forwarded by libdax/libburn) */ /** Open an audio file, check whether suitable, create extractor object. @param xtr Opaque handle to extractor. Gets attached extractor object. @param path Address of the audio file to extract. "-" is stdin (but might be not suitable for all futurely supported formats). @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success 0 unsuitable format -1 severe error -2 path not found */ int libdax_audioxtr_new(struct libdax_audioxtr **xtr, char *path, int flag); /** Obtain identification parameters of opened audio source. @param xtr Opaque handle to extractor @param fmt Gets pointed to the audio file format id text: ".wav" , ".au" @param fmt_info Gets pointed to a format info text telling parameters @param num_channels e.g. 1=mono, 2=stereo, etc @param sample_rate e.g. 11025, 44100 @param bits_per_sample e.g. 8= 8 bits per sample, 16= 16 bits ... @param msb_first Byte order of samples: 0=Intel 1=Motorola @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success, <=0 failure */ int libdax_audioxtr_get_id(struct libdax_audioxtr *xtr, char **fmt, char **fmt_info, int *num_channels, int *sample_rate, int *bits_per_sample, int *msb_first, int flag); /** Obtain a prediction about the extracted size based on internal information of the formatted file. @param xtr Opaque handle to extractor @param size Gets filled with the predicted size @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 prediction was possible , 0 no prediction could be made */ int libdax_audioxtr_get_size(struct libdax_audioxtr *o, off_t *size, int flag); /** Obtain next buffer full of extracted data in desired format (only raw audio for now). @param xtr Opaque handle to extractor @param buffer Gets filled with extracted data @param buffer_size Maximum number of bytes to be filled into buffer @param flag Bitfield for control purposes bit0= do not stop at predicted end of data @return >0 number of valid buffer bytes, 0 End of file -1 operating system reports error -2 usage error by application */ int libdax_audioxtr_read(struct libdax_audioxtr *xtr, char buffer[], int buffer_size, int flag); /** Try to obtain a file descriptor which will deliver extracted data to normal calls of read(2). This may fail because the format is unsuitable for that, but ".wav" is ok. If this call succeeds the xtr object will have forgotten its file descriptor and libdax_audioxtr_read() will return a usage error. One may use *fd after libdax_audioxtr_destroy() and will have to close it via close(2) when done with it. @param xtr Opaque handle to extractor @param fd Eventually returns the file descriptor number @param flag Bitfield for control purposes bit0= do not dup(2) and close(2) but hand out original fd @return 1 success, 0 cannot hand out fd , -1 severe error */ int libdax_audioxtr_detach_fd(struct libdax_audioxtr *o, int *fd, int flag); /** Clean up after extraction and destroy extractor object. @param xtr Opaque handle to extractor, *xtr is allowed to be NULL, *xtr is set to NULL by this function @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 = destroyed object, 0 = was already destroyed */ int libdax_audioxtr_destroy(struct libdax_audioxtr **xtr, int flag); #endif /* LIBDAX_AUDIOXTR_H_PUBLIC */ #ifdef LIBDAX_AUDIOXTR________________ -- place documentation text here --- #endif /* LIBDAX_AUDIOXTR_________________ */ /* *Never* set this macro outside libdax_audioxtr.c ! The entrails of this facility are not to be seen by the other library components or the applications. */ #ifdef LIBDAX_AUDIOXTR_H_INTERNAL /* Internal Structures */ /** Extractor object encapsulating intermediate states of extraction */ struct libdax_audioxtr { /* Source of the encoded audio data */ char path[LIBDAX_AUDIOXTR_STRLEN]; /* File descriptor to path. Anything else than 0 must be lseek-able */ int fd; /* Format identifier. E.g. ".wav" */ char fmt[80]; /* Format parameter info text */ char fmt_info[LIBDAX_AUDIOXTR_STRLEN]; /* 1= mono, 2= stereo, etc. */ int num_channels; /* 8000, 44100, etc. */ int sample_rate; /* 8 bits = 8, 16 bits = 16, etc. */ int bits_per_sample; /* Byte order of samples: 0=Intel 1=Motorola */ int msb_first; /* Number of bytes to extract (0= unknown/unlimited) */ off_t data_size; /* Number of extracted data bytes */ off_t extract_count; /* Format dependent parameters */ /* MS WAVE Format */ /* see description in: doc/waveformat.txt */ /* Offset to "data" subchunk */ unsigned int wav_data_location; /* == NumSamples * NumChannels * BitsPerSample/8 This is the number of bytes in the data. */ unsigned wav_subchunk2_size; /* Sun Audio, .au */ /* info used: http://www.opengroup.org/public/pubs/external/auformat.html */ /* Number of bytes in non-payload header part */ unsigned au_data_location; /* Number of payload bytes or 0xffffffff */ unsigned au_data_size; }; /* Internal Functions */ /** Open the audio source pointed to by .path and evaluate suitability. @return -1 failure to open, 0 unsuitable format, 1 success */ static int libdax_audioxtr_open(struct libdax_audioxtr *o, int flag); /** Identify format and evaluate suitability. @return 0 unsuitable format, 1 format is suitable */ static int libdax_audioxtr_identify(struct libdax_audioxtr *o, int flag); /** Specialized identifier for .wav */ static int libdax_audioxtr_identify_wav(struct libdax_audioxtr *o, int flag); /** Specialized identifier for .au */ static int libdax_audioxtr_identify_au(struct libdax_audioxtr *o, int flag); /** Convert a byte string into a number (currently only little endian) @param flag Bitfield for control purposes bit0=msb_first @return The resulting number */ static unsigned libdax_audioxtr_to_int(struct libdax_audioxtr *o, unsigned char *bytes, int len, int flag); /** Prepare for reading of first buffer. @return 0 error, 1 success */ static int libdax_audioxtr_init_reading(struct libdax_audioxtr *o, int flag); #endif /* LIBDAX_AUDIOXTR_H_INTERNAL */ #endif /* ! LIBDAX_AUDIOXTR_H_INCLUDED */ /* libdax_msgs Message handling facility of libdax. Copyright (C) 2006 - 2016 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <sys/time.h> #include <pthread.h> /* Only this single source module is entitled to do this */ #define LIBDAX_MSGS_H_INTERNAL 1 /* All participants in the messaging system must do this */ #include "libdax_msgs.h" /* ----------------------------- libdax_msgs_item ------------------------- */ static int libdax_msgs_item_new(struct libdax_msgs_item **item, struct libdax_msgs_item *link, int flag) { int ret; struct libdax_msgs_item *o; struct timeval tv; (*item)= o= (struct libdax_msgs_item *) calloc(1, sizeof(struct libdax_msgs_item)); if(o==NULL) return(-1); o->timestamp= 0.0; ret= gettimeofday(&tv, NULL); if(ret==0) o->timestamp= tv.tv_sec+0.000001*tv.tv_usec; o->process_id= getpid(); o->origin= -1; o->severity= LIBDAX_MSGS_SEV_ALL; o->priority= LIBDAX_MSGS_PRIO_ZERO; o->error_code= 0; o->msg_text= NULL; o->os_errno= 0; o->prev= link; o->next= NULL; if(link!=NULL) { if(link->next!=NULL) { link->next->prev= o; o->next= link->next; } link->next= o; } return(1); } /** Detaches item from its queue and eventually readjusts start, end pointers of the queue */ int libdax_msgs_item_unlink(struct libdax_msgs_item *o, struct libdax_msgs_item **chain_start, struct libdax_msgs_item **chain_end, int flag) { if(o->prev!=NULL) o->prev->next= o->next; if(o->next!=NULL) o->next->prev= o->prev; if(chain_start!=NULL) if(*chain_start == o) *chain_start= o->next; if(chain_end!=NULL) if(*chain_end == o) *chain_end= o->prev; o->next= o->prev= NULL; return(1); } int libdax_msgs_item_destroy(struct libdax_msgs_item **item, int flag) { struct libdax_msgs_item *o; o= *item; if(o==NULL) return(0); libdax_msgs_item_unlink(o,NULL,NULL,0); if(o->msg_text!=NULL) free((char *) o->msg_text); free((char *) o); *item= NULL; return(1); } int libdax_msgs_item_get_msg(struct libdax_msgs_item *item, int *error_code, char **msg_text, int *os_errno, int flag) { *error_code= item->error_code; *msg_text= item->msg_text; *os_errno= item->os_errno; return(1); } int libdax_msgs_item_get_origin(struct libdax_msgs_item *item, double *timestamp, pid_t *process_id, int *origin, int flag) { *timestamp= item->timestamp; *process_id= item->process_id; *origin= item->origin; return(1); } int libdax_msgs_item_get_rank(struct libdax_msgs_item *item, int *severity, int *priority, int flag) { *severity= item->severity; *priority= item->priority; return(1); } /* ------------------------------- libdax_msgs ---------------------------- */ int libdax_msgs_new(struct libdax_msgs **m, int flag) { struct libdax_msgs *o; (*m)= o= (struct libdax_msgs *) calloc(1, sizeof(struct libdax_msgs)); if(o==NULL) return(-1); o->refcount= 1; o->oldest= NULL; o->youngest= NULL; o->count= 0; o->queue_severity= LIBDAX_MSGS_SEV_ALL; o->print_severity= LIBDAX_MSGS_SEV_NEVER; strcpy(o->print_id,"libdax: "); #ifndef LIBDAX_MSGS_SINGLE_THREADED pthread_mutex_init(&(o->lock_mutex),NULL); #endif return(1); } static int libdax_msgs_lock(struct libdax_msgs *m, int flag) { #ifndef LIBDAX_MSGS_SINGLE_THREADED int ret; ret= pthread_mutex_lock(&(m->lock_mutex)); if(ret!=0) return(0); #endif return(1); } static int libdax_msgs_unlock(struct libdax_msgs *m, int flag) { #ifndef LIBDAX_MSGS_SINGLE_THREADED int ret; ret= pthread_mutex_unlock(&(m->lock_mutex)); if(ret!=0) return(0); #endif return(1); } int libdax_msgs_destroy(struct libdax_msgs **m, int flag) { struct libdax_msgs *o; struct libdax_msgs_item *item, *next_item; o= *m; if(o==NULL) return(0); if(o->refcount > 1) { if(libdax_msgs_lock(*m,0)<=0) return(-1); o->refcount--; libdax_msgs_unlock(*m,0); *m= NULL; return(1); } #ifndef LIBDAX_MSGS_SINGLE_THREADED if(pthread_mutex_destroy(&(o->lock_mutex))!=0) { pthread_mutex_unlock(&(o->lock_mutex)); pthread_mutex_destroy(&(o->lock_mutex)); } #endif for(item= o->oldest; item!=NULL; item= next_item) { next_item= item->next; libdax_msgs_item_destroy(&item,0); } free((char *) o); *m= NULL; return(1); } int libdax_msgs_refer(struct libdax_msgs **pt, struct libdax_msgs *m, int flag) { if(libdax_msgs_lock(m,0)<=0) return(0); m->refcount++; *pt= m; libdax_msgs_unlock(m,0); return(1); } int libdax_msgs_set_severities(struct libdax_msgs *m, int queue_severity, int print_severity, char *print_id, int flag) { if(libdax_msgs_lock(m,0)<=0) return(0); m->queue_severity= queue_severity; m->print_severity= print_severity; strncpy(m->print_id,print_id,80); m->print_id[80]= 0; libdax_msgs_unlock(m,0); return(1); } int libdax_msgs__text_to_sev(char *severity_name, int *severity, int flag) { if(strncmp(severity_name,"NEVER",5)==0) *severity= LIBDAX_MSGS_SEV_NEVER; else if(strncmp(severity_name,"ABORT",5)==0) *severity= LIBDAX_MSGS_SEV_ABORT; else if(strncmp(severity_name,"FATAL",5)==0) *severity= LIBDAX_MSGS_SEV_FATAL; else if(strncmp(severity_name,"FAILURE",7)==0) *severity= LIBDAX_MSGS_SEV_FAILURE; else if(strncmp(severity_name,"MISHAP",6)==0) *severity= LIBDAX_MSGS_SEV_MISHAP; else if(strncmp(severity_name,"SORRY",5)==0) *severity= LIBDAX_MSGS_SEV_SORRY; else if(strncmp(severity_name,"WARNING",7)==0) *severity= LIBDAX_MSGS_SEV_WARNING; else if(strncmp(severity_name,"HINT",4)==0) *severity= LIBDAX_MSGS_SEV_HINT; else if(strncmp(severity_name,"NOTE",4)==0) *severity= LIBDAX_MSGS_SEV_NOTE; else if(strncmp(severity_name,"UPDATE",6)==0) *severity= LIBDAX_MSGS_SEV_UPDATE; else if(strncmp(severity_name,"DEBUG",5)==0) *severity= LIBDAX_MSGS_SEV_DEBUG; else if(strncmp(severity_name,"ERRFILE",7)==0) *severity= LIBDAX_MSGS_SEV_ERRFILE; else if(strncmp(severity_name,"ALL",3)==0) *severity= LIBDAX_MSGS_SEV_ALL; else { *severity= LIBDAX_MSGS_SEV_ALL; return(0); } return(1); } int libdax_msgs__sev_to_text(int severity, char **severity_name, int flag) { if(flag&1) { *severity_name= "ALL ERRFILE DEBUG UPDATE NOTE HINT WARNING SORRY MISHAP FAILURE FATAL ABORT NEVER"; return(1); } *severity_name= ""; if(severity>=LIBDAX_MSGS_SEV_NEVER) *severity_name= "NEVER"; else if(severity>=LIBDAX_MSGS_SEV_ABORT) *severity_name= "ABORT"; else if(severity>=LIBDAX_MSGS_SEV_FATAL) *severity_name= "FATAL"; else if(severity>=LIBDAX_MSGS_SEV_FAILURE) *severity_name= "FAILURE"; else if(severity>=LIBDAX_MSGS_SEV_MISHAP) *severity_name= "MISHAP"; else if(severity>=LIBDAX_MSGS_SEV_SORRY) *severity_name= "SORRY"; else if(severity>=LIBDAX_MSGS_SEV_WARNING) *severity_name= "WARNING"; else if(severity>=LIBDAX_MSGS_SEV_HINT) *severity_name= "HINT"; else if(severity>=LIBDAX_MSGS_SEV_NOTE) *severity_name= "NOTE"; else if(severity>=LIBDAX_MSGS_SEV_UPDATE) *severity_name= "UPDATE"; else if(severity>=LIBDAX_MSGS_SEV_DEBUG) *severity_name= "DEBUG"; else if(severity>=LIBDAX_MSGS_SEV_ERRFILE) *severity_name= "ERRFILE"; else if(severity>=LIBDAX_MSGS_SEV_ALL) *severity_name= "ALL"; else { *severity_name= ""; return(0); } return(1); } /* @param flag Bitfield for control purposes bit0= If direct output to stderr: CarriageReturn rather than LineFeed */ int libdax_msgs_submit(struct libdax_msgs *m, int origin, int error_code, int severity, int priority, char *msg_text, int os_errno, int flag) { int ret; char *textpt,*sev_name,sev_text[81]; struct libdax_msgs_item *item= NULL; if(severity >= m->print_severity) { if(msg_text==NULL) textpt= ""; else textpt= msg_text; sev_text[0]= 0; ret= libdax_msgs__sev_to_text(severity,&sev_name,0); if(ret>0) sprintf(sev_text,"%s : ",sev_name); fprintf(stderr, "%s%s%s%c", m->print_id, sev_text, textpt, (flag & 1) ? '\r' : '\n'); if(os_errno!=0) { ret= libdax_msgs_lock(m,0); if(ret<=0) return(-1); fprintf(stderr,"%s( Most recent system error: %d '%s' )\n", m->print_id,os_errno,strerror(os_errno)); libdax_msgs_unlock(m,0); } } if(severity < m->queue_severity) return(0); ret= libdax_msgs_lock(m,0); if(ret<=0) return(-1); ret= libdax_msgs_item_new(&item,m->youngest,0); if(ret<=0) goto failed; item->origin= origin; item->error_code= error_code; item->severity= severity; item->priority= priority; if(msg_text!=NULL) { item->msg_text= calloc(1, strlen(msg_text)+1); if(item->msg_text==NULL) goto failed; strcpy(item->msg_text,msg_text); } item->os_errno= os_errno; if(m->oldest==NULL) m->oldest= item; m->youngest= item; m->count++; libdax_msgs_unlock(m,0); /* fprintf(stderr,"libdax_experimental: message submitted to queue (now %d)\n", m->count); */ return(1); failed:; libdax_msgs_item_destroy(&item,0); libdax_msgs_unlock(m,0); return(-1); } int libdax_msgs_obtain(struct libdax_msgs *m, struct libdax_msgs_item **item, int severity, int priority, int flag) { int ret; struct libdax_msgs_item *im, *next_im= NULL; *item= NULL; ret= libdax_msgs_lock(m,0); if(ret<=0) return(-1); for(im= m->oldest; im!=NULL; im= next_im) { for(; im!=NULL; im= next_im) { next_im= im->next; if(im->severity>=severity) break; libdax_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0); libdax_msgs_item_destroy(&im,0); /* severity too low: delete */ } if(im==NULL) break; if(im->priority>=priority) break; } if(im==NULL) {ret= 0; goto ex;} libdax_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0); *item= im; ret= 1; ex:; libdax_msgs_unlock(m,0); return(ret); } int libdax_msgs_destroy_item(struct libdax_msgs *m, struct libdax_msgs_item **item, int flag) { int ret; ret= libdax_msgs_lock(m,0); if(ret<=0) return(-1); ret= libdax_msgs_item_destroy(item,0); libdax_msgs_unlock(m,0); return(ret); } /* libdax_msgs Message handling facility of libburn and libisofs. Copyright (C) 2006-2021 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL version 2 or later. */ /* *Never* set this macro outside libdax_msgs.c ! The entrails of the message handling facility are not to be seen by the other library components or the applications. */ #ifdef LIBDAX_MSGS_H_INTERNAL #ifndef LIBDAX_MSGS_SINGLE_THREADED #include <pthread.h> #endif struct libdax_msgs_item { double timestamp; pid_t process_id; int origin; int severity; int priority; /* Apply for your developer's error code range at libburn-hackers@pykix.org Report introduced codes in the list below. */ int error_code; char *msg_text; int os_errno; struct libdax_msgs_item *prev,*next; }; struct libdax_msgs { int refcount; struct libdax_msgs_item *oldest; struct libdax_msgs_item *youngest; int count; int queue_severity; int print_severity; char print_id[81]; #ifndef LIBDAX_MSGS_SINGLE_THREADED pthread_mutex_t lock_mutex; #endif }; #endif /* LIBDAX_MSGS_H_INTERNAL */ #ifndef LIBDAX_MSGS_H_INCLUDED #define LIBDAX_MSGS_H_INCLUDED 1 #ifndef LIBDAX_MSGS_H_INTERNAL /* Architectural aspects */ /* libdax_msgs is designed to serve in libraries which want to offer their applications a way to control the output of library messages. It shall be incorporated by an owner, i.e. a software entity which encloses the code of the .c file. Owner of libdax_msgs is libburn. A fully compatible variant named libiso_msgs is owned by libisofs and can get generated by a script of the libburn project: libburn/libdax_msgs_to_xyz_msgs.sh . Reason: One cannot link two owners of the same variant together because both would offer the same functions to the linker. For that situation one has to create a compatible variant as it is done for libisofs. Compatible variants may get plugged together by call combinations like burn_set_messenger(iso_get_messenger()); A new variant would demand a _set_messenger() function if it has to work with libisofs. If only libburn is planned as link partner then a simple _get_messenger() does suffice. Take care to shutdown libburn before its provider of the *_msgs object gets shut down. */ /* Public Opaque Handles */ /** A pointer to this is a opaque handle to a message handling facility */ struct libdax_msgs; /** A pointer to this is a opaque handle to a single message item */ struct libdax_msgs_item; #endif /* ! LIBDAX_MSGS_H_INTERNAL */ /* Public Macros */ /* Registered Severities */ /* It is well advisable to let applications select severities via strings and forwarded functions libdax_msgs__text_to_sev(), libdax_msgs__sev_to_text(). These macros are for use by the owner of libdax_msgs. */ /** Use this to get messages of any severity. Do not use for submitting. */ #define LIBDAX_MSGS_SEV_ALL 0x00000000 /** Messages of this severity shall transport plain disk file paths whenever an event of severity SORRY or above is related with an individual disk file. No message text shall be added to the file path. The ERRFILE message shall be issued before the human readable message which carries the true event severity. That message should contain the file path so it can be found by strstr(message, path)!=NULL. The error code shall be the same as with the human readable message. */ #define LIBDAX_MSGS_SEV_ERRFILE 0x08000000 /** Debugging messages not to be visible to normal users by default */ #define LIBDAX_MSGS_SEV_DEBUG 0x10000000 /** Update of a progress report about long running actions */ #define LIBDAX_MSGS_SEV_UPDATE 0x20000000 /** Not so usual events which were gracefully handled */ #define LIBDAX_MSGS_SEV_NOTE 0x30000000 /** Possibilities to achieve a better result */ #define LIBDAX_MSGS_SEV_HINT 0x40000000 /** Warnings about problems which could not be handled optimally */ #define LIBDAX_MSGS_SEV_WARNING 0x50000000 /** Non-fatal error messages indicating that parts of an action failed but processing may go on if one accepts deviations from the desired result. SORRY may also be the severity for incidents which are severe enough for FAILURE but happen within already started irrevocable actions, like ISO image generation. A precondition for such a severity ease is that the action can be continued after the incident. See below MISHAP for what xorriso would need instead of this kind of SORRY and generates for itself in case of libisofs image generation. E.g.: A pattern yields no result. A speed setting cannot be made. A libisofs input file is inaccessible during image generation. After SORRY a function should try to go on if that makes any sense and if no threshold prescribes abort on SORRY. The function should nevertheless indicate some failure in its return value. It should - but it does not have to. */ #define LIBDAX_MSGS_SEV_SORRY 0x60000000 /** A FAILURE (see below) which can be tolerated during long lasting operations just because they cannot simply be stopped or revoked. xorriso converts libisofs SORRY messages issued during image generation into MISHAP messages in order to allow its evaluators to distinguish image generation problems from minor image composition problems. E.g.: A libisofs input file is inaccessible during image generation. After a MISHAP a function should behave like after SORRY. */ #define LIBDAX_MSGS_SEV_MISHAP 0x64000000 /** Non-fatal error indicating that an important part of an action failed and that only a new setup of preconditions will give hope for sufficient success. E.g.: No media is inserted in the output drive. No write mode can be found for inserted media. A libisofs input file is inaccessible during grafting. After FAILURE a function should end with a return value indicating failure. It is at the discretion of the function whether it ends immediately in any case or whether it tries to go on if the eventual threshold allows. */ #define LIBDAX_MSGS_SEV_FAILURE 0x68000000 /** An error message which puts the whole operation of the program in question E.g.: Not enough memory for essential temporary objects. Irregular errors from resources. Programming errors (soft assert). After FATAL a function should end very soon with a return value indicating severe failure. */ #define LIBDAX_MSGS_SEV_FATAL 0x70000000 /** A message from an abort handler which will finally finish libburn */ #define LIBDAX_MSGS_SEV_ABORT 0x71000000 /** A severity to exclude resp. discard any possible message. Do not use this severity for submitting. */ #define LIBDAX_MSGS_SEV_NEVER 0x7fffffff /* Registered Priorities */ /* Priorities are to be selected by the programmers and not by the user. */ #define LIBDAX_MSGS_PRIO_ZERO 0x00000000 #define LIBDAX_MSGS_PRIO_LOW 0x10000000 #define LIBDAX_MSGS_PRIO_MEDIUM 0x20000000 #define LIBDAX_MSGS_PRIO_HIGH 0x30000000 #define LIBDAX_MSGS_PRIO_TOP 0x7ffffffe /* Do not use this priority for submitting */ #define LIBDAX_MSGS_PRIO_NEVER 0x7fffffff /* Origin numbers of libburn drives may range from 0 to 1048575 */ #define LIBDAX_MSGS_ORIGIN_DRIVE_BASE 0 #define LIBDAX_MSGS_ORIGIN_DRIVE_TOP 0xfffff /* Origin numbers of libisofs images may range from 1048575 to 2097152 */ #define LIBDAX_MSGS_ORIGIN_IMAGE_BASE 0x100000 #define LIBDAX_MSGS_ORIGIN_IMAGE_TOP 0x1fffff /* Public Functions */ /* Calls initiated from inside the direct owner (e.g. from libburn) */ /** Create new empty message handling facility with queue and issue a first official reference to it. @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success, <=0 failure */ int libdax_msgs_new(struct libdax_msgs **m, int flag); /** Destroy a message handling facility and all its eventual messages. The submitted pointer gets set to NULL. Actually only the last destroy call of all official references to the object will really dispose it. All others just decrement the reference counter. Call this function only with official reference pointers obtained by libdax_msgs_new() or libdax_msgs_refer(), and only once per such pointer. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 for success, 0 for pointer to NULL, -1 for fatal error */ int libdax_msgs_destroy(struct libdax_msgs **m, int flag); /** Create an official reference to an existing libdax_msgs object. The references keep the object alive at least until it is released by a matching number of destroy calls. So each reference MUST be revoked by exactly one call to libdax_msgs_destroy(). @param pt The pointer to be set and registered @param m A pointer to the existing object @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 for success, 0 for failure */ int libdax_msgs_refer(struct libdax_msgs **pt, struct libdax_msgs *o, int flag); /** Submit a message to a message handling facility. @param origin program specific identification number of the originator of a message. E.g. drive number. Programs should have an own range of origin numbers. See above LIBDAX_MSGS_ORIGIN_*_BASE Use -1 if no number is known. @param error_code Unique error code. Use only registered codes. See below. The same unique error_code may be issued at different occasions but those should be equivalent out of the view of a libdax_msgs application. (E.g. "cannot open ATA drive" versus "cannot open SCSI drive" would be equivalent.) @param severity The LIBDAX_MSGS_SEV_* of the event. @param priority The LIBDAX_MSGS_PRIO_* number of the event. @param msg_text Printable and human readable message text. @param os_errno Eventual error code from operating system (0 if none) @param flag Bitfield for control purposes bit0= If direct output to stderr: CarriageReturn rather than LineFeed @return 1 on success, 0 on rejection, <0 for severe errors */ int libdax_msgs_submit(struct libdax_msgs *m, int origin, int error_code, int severity, int priority, char *msg_text, int os_errno, int flag); /* Calls from applications (to be forwarded by direct owner) */ /** Convert a registered severity number into a severity name @param flag Bitfield for control purposes: bit0= list all severity names in a blank separated string @return >0 success, <=0 failure */ int libdax_msgs__sev_to_text(int severity, char **severity_name, int flag); /** Convert a severity name into a severity number, @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success, <=0 failure */ int libdax_msgs__text_to_sev(char *severity_name, int *severity, int flag); /** Set minimum severity for messages to be queued (default LIBDAX_MSGS_SEV_ALL) and for messages to be printed directly to stderr (default LIBDAX_MSGS_SEV_NEVER). @param print_id A text of at most 80 characters to be printed before any eventually printed message (default is "libdax: "). @param flag Bitfield for control purposes (unused yet, submit 0) @return always 1 for now */ int libdax_msgs_set_severities(struct libdax_msgs *m, int queue_severity, int print_severity, char *print_id, int flag); /** Obtain a message item that has at least the given severity and priority. Usually all older messages of lower severity are discarded then. If no item of sufficient severity was found, all others are discarded from the queue. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 if a matching item was found, 0 if not, <0 for severe errors */ int libdax_msgs_obtain(struct libdax_msgs *m, struct libdax_msgs_item **item, int severity, int priority, int flag); /** Destroy a message item obtained by libdax_msgs_obtain(). The submitted pointer gets set to NULL. Caution: Copy eventually obtained msg_text before destroying the item, if you want to use it further. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 for success, 0 for pointer to NULL, <0 for severe errors */ int libdax_msgs_destroy_item(struct libdax_msgs *m, struct libdax_msgs_item **item, int flag); /** Obtain from a message item the three application oriented components as submitted with the originating call of libdax_msgs_submit(). Caution: msg_text becomes a pointer into item, not a copy. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 on success, 0 on invalid item, <0 for servere errors */ int libdax_msgs_item_get_msg(struct libdax_msgs_item *item, int *error_code, char **msg_text, int *os_errno, int flag); /** Obtain from a message item the submitter identification submitted with the originating call of libdax_msgs_submit(). @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 on success, 0 on invalid item, <0 for servere errors */ int libdax_msgs_item_get_origin(struct libdax_msgs_item *item, double *timestamp, pid_t *process_id, int *origin, int flag); /** Obtain from a message item severity and priority as submitted with the originating call of libdax_msgs_submit(). @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 on success, 0 on invalid item, <0 for servere errors */ int libdax_msgs_item_get_rank(struct libdax_msgs_item *item, int *severity, int *priority, int flag); #ifdef LIBDAX_MSGS_________________ /* Registered Error Codes */ Format: error_code (LIBDAX_MSGS_SEV_*,LIBDAX_MSGS_PRIO_*) = explanation If no severity or priority are fixely associated, use "(,)". ------------------------------------------------------------------------------ Range "libdax_msgs" : 0x00000000 to 0x0000ffff 0x00000000 (ALL,ZERO) = Initial setting in new libdax_msgs_item 0x00000001 (DEBUG,ZERO) = Test error message 0x00000002 (DEBUG,ZERO) = Debugging message 0x00000003 (FATAL,HIGH) = Out of virtual memory 0x00000004 (FATAL,HIGH) = Generic fatal error ------------------------------------------------------------------------------ Range "elmom" : 0x00010000 to 0x0001ffff ------------------------------------------------------------------------------ Range "scdbackup" : 0x00020000 to 0x0002ffff Accessing and defending drives: 0x00020001 (SORRY,LOW) = Cannot open busy device 0x00020002 (SORRY,HIGH) = Encountered error when closing drive 0x00020003 (SORRY,HIGH) = Could not grab drive 0x00020004 (NOTE,HIGH) = Opened O_EXCL scsi sibling 0x00020005 (SORRY,HIGH) = Failed to open device 0x00020006 (FATAL,HIGH) = Too many scsi siblings 0x00020007 (NOTE,HIGH) = Closed O_EXCL scsi siblings 0x00020008 (SORRY,HIGH) = Device busy. Failed to fcntl-lock 0x00020009 (SORRY,HIGH) = Neither stdio-path nor its directory exist 0x0002000a (FAILURE,HIGH) = Cannot accept '...' as SG_IO CDROM drive 0x0002000b (FAILURE,HIGH) = File object '...' not found 0x0002000c (FAILURE,HIGH) = Cannot start device file enumeration 0x0002000d (FAILURE,HIGH) = Cannot enumerate next device 0x0002000e (NOTE,HIGH) = Failed to open device during General library operations: 0x00020101 (WARNING,HIGH) = Cannot find given worker item 0x00020102 (SORRY,HIGH) = A drive operation is still going on 0x00020103 (WARNING,HIGH) = After scan a drive operation is still going on 0x00020104 (SORRY,HIGH) = NULL pointer caught 0x00020105 (SORRY,HIGH) = Drive is already released 0x00020106 (SORRY,HIGH) = Drive is busy on attempt to close 0x00020107 (WARNING,HIGH) = A drive is still busy on shutdown of library 0x00020108 (SORRY,HIGH) = Drive is not grabbed on disc status inquiry 0x00020108 (FATAL,HIGH) = Could not allocate new drive object 0x00020109 (FATAL,HIGH) = Library not running 0x0002010a (FATAL,HIGH) = Unsuitable track mode 0x0002010b (FATAL,HIGH) = Burn run failed 0x0002010c (FATAL,HIGH) = Failed to transfer command to drive 0x0002010d (DEBUG,HIGH) = Could not inquire TOC 0x0002010e (FATAL,HIGH) = Attempt to read ATIP from ungrabbed drive 0x0002010f (DEBUG,HIGH) = SCSI error condition on command 0x00020110 (FATAL,HIGH) = Persistent drive address too long 0x00020111 (FATAL,HIGH) = Could not allocate new auxiliary object 0x00020112 (SORRY,HIGH) = Bad combination of write_type and block_type 0x00020113 (FATAL,HIGH) = Drive capabilities not inquired yet 0x00020114 (SORRY,HIGH) = Attempt to set ISRC with bad data 0x00020115 (SORRY,HIGH) = Attempt to set track mode to unusable value 0x00020116 (FATAL,HIGH) = Track mode has unusable value 0x00020117 (FATAL,HIGH) = toc_entry of drive is already in use 0x00020118 (DEBUG,HIGH) = Closing track 0x00020119 (DEBUG,HIGH) = Closing session 0x0002011a (NOTE,HIGH) = Padding up track to minimum size 0x0002011b (FATAL,HIGH) = Attempt to read track info from ungrabbed drive 0x0002011c (FATAL,HIGH) = Attempt to read track info from busy drive 0x0002011d (FATAL,HIGH) = SCSI error on write 0x0002011e (SORRY,HIGH) = Unsuitable media detected 0x0002011f (SORRY,HIGH) = Burning is restricted to a single track 0x00020120 (NOTE,HIGH) = FORMAT UNIT ignored 0x00020121 (FATAL,HIGH) = Write preparation setup failed 0x00020122 (FAILURE,HIGH) = SCSI error on format_unit 0x00020123 (SORRY,HIGH) = DVD Media are unsuitable for desired track type 0x00020124 (SORRY,HIGH) = SCSI error on set_streaming 0x00020125 (SORRY,HIGH) = Write start address not supported 0x00020126 (SORRY,HIGH) = Write start address not properly aligned 0x00020127 (NOTE,HIGH) = Write start address is ... 0x00020128 (FATAL,HIGH) = Unsupported inquiry_type with mmc_get_performance 0x00020129 (SORRY,HIGH) = Will not format media type 0x0002012a (FATAL,HIGH) = Cannot inquire write mode capabilities 0x0002012b (FATAL,HIGH) = Drive offers no suitable write mode with this job 0x0002012c (SORRY,HIGH) = Too many logical tracks recorded 0x0002012d (FATAL,HIGH) = Exceeding range of permissible write addresses 0x0002012e (NOTE,HIGH) = Activated track default size 0x0002012f (SORRY,HIGH) = SAO is restricted to single fixed size session 0x00020130 (SORRY,HIGH) = Drive and media state unsuitable for blanking 0x00020131 (SORRY,HIGH) = No suitable formatting type offered by drive 0x00020132 (SORRY,HIGH) = Selected format is not suitable for libburn 0x00020133 (SORRY,HIGH) = Cannot mix data and audio in SAO mode 0x00020134 (NOTE,HIGH) = Defaulted TAO to DAO 0x00020135 (SORRY,HIGH) = Cannot perform TAO, job unsuitable for DAO 0x00020136 (SORRY,HIGH) = DAO burning restricted to single fixed size track 0x00020137 (HINT,HIGH) = TAO would be possible 0x00020138 (FATAL,HIGH) = Cannot reserve track 0x00020139 (SORRY,HIGH) = Write job parameters are unsuitable 0x0002013a (FATAL,HIGH) = No suitable media detected 0x0002013b (DEBUG,HIGH) = SCSI command indicates host or driver error 0x0002013c (SORRY,HIGH) = Malformed capabilities page 2Ah received 0x0002013d (DEBUG,LOW) = Waiting for free buffer space takes long time 0x0002013e (SORRY,HIGH) = Timeout with waiting for free buffer. Now disabled 0x0002013f (DEBUG,LOW) = Reporting total time spent with waiting for buffer 0x00020140 (FATAL,HIGH) = Drive is busy on attempt to write random access 0x00020141 (SORRY,HIGH) = Write data count not properly aligned 0x00020142 (FATAL,HIGH) = Drive is not grabbed on random access write 0x00020143 (SORRY,HIGH) = Read start address not properly aligned 0x00020144 (SORRY,HIGH) = SCSI error on read 0x00020145 (FATAL,HIGH) = Drive is busy on attempt to read data 0x00020146 (FATAL,HIGH) = Drive is a virtual placeholder 0x00020147 (SORRY,HIGH) = Cannot address start byte 0x00020148 (SORRY,HIGH) = Cannot write desired amount of data 0x00020149 (SORRY,HIGH) = Unsuitable filetype for pseudo-drive 0x0002014a (SORRY,HIGH) = Cannot read desired amount of data 0x0002014b (SORRY,HIGH) = Drive is already registered resp. scanned 0x0002014c (FATAL,HIGH) = Emulated drive caught in SCSI function 0x0002014d (SORRY,HIGH) = Asynchronous SCSI error 0x0002014f (SORRY,HIGH) = Timeout with asynchronous SCSI command 0x00020150 (DEBUG,LOW) = Reporting asynchronous waiting time 0x00020151 (FAILURE,HIGH) = Read attempt on write-only drive 0x00020152 (FATAL,HIGH) = Cannot start fifo thread 0x00020153 (SORRY,HIGH) = Read error on fifo input 0x00020154 (NOTE,HIGH) = Forwarded input error ends output 0x00020155 (SORRY,HIGH) = Desired fifo buffer too large 0x00020156 (SORRY,HIGH) = Desired fifo buffer too small 0x00020157 (FATAL,HIGH) = burn_source is not a fifo object 0x00020158 (DEBUG,LOW) = Reporting thread disposal precautions 0x00020159 (DEBUG,HIGH) = TOC Format 0 returns inconsistent data 0x0002015a (NOTE,HIGH) = Could not examine busy device 0x0002015b (HINT,HIGH) = Busy '...' seems to be a hard disk, as '...1' exists 0x0002015c (FAILURE,HIGH) = Fifo size too small for desired peek buffer 0x0002015d (FAILURE,HIGH) = Fifo input ended short of desired peek buffer size 0x0002015e (FATAL,HIGH) = Fifo is already under consumption when peeking 0x0002015f (MISHAP,HIGH) = Damaged CD table-of-content detected and truncated 0x00020160 (WARNING,HIGH) = Session without leadout encountered 0x00020161 (WARNING,HIGH) = Empty session deleted 0x00020162 (SORRY,HIGH) = BD-R not unformatted blank any more. Cannot format 0x00020163 (NOTE,HIGH) = Blank BD-R left unformatted for zero spare capacity 0x00020164 (SORRY,HIGH) = Drive does not format BD-RE without spares 0x00020165 (WARNING,HIGH) = Drive does not support fast formatting 0x00020166 (WARNING,HIGH) = Drive does not support full formatting 0x00020167 (SORRY,HIGH) = Drive does not support non-default formatting 0x00020168 (FAILURE,HIGH) = Media not properly formatted. Cannot write. 0x00020169 (WARNING,HIGH) = Last session on media is still open 0x0002016a (FAILURE,HIGH) = No MMC transport adapter is present 0x0002016b (WARNING,HIGH) = No MMC transport adapter is present 0x0002016c (DEBUG,HIGH) = No MMC transport adapter is present 0x0002016e (DEBUG,HIGH) = MODE SENSE page 2A too short 0x0002016f (DEBUG,HIGH) = Unable to grab scanned drive 0x00020170 (NOTE,HIGH) = Closing open session before writing new one 0x00020171 (NOTE,HIGH) = Closing BD-R with accidentally open session 0x00020172 (SORRY,HIGH) = Read start address larger than number of readable blocks 0x00020173 (FAILURE,HIGH) = Drive tells NWA smaller than last written address 0x00020174 (SORRY,HIGH) = Fifo alignment does not allow desired read size 0x00020175 (FATAL,HIGH) = Supporting library is too old 0x00020176 (NOTE,HIGH) = Stream recording disabled because of small OS buffer 0x00020177 (ABORT,HIGH) = Urged drive worker threads to do emergency halt 0x00020178 (DEBUG,HIGH) = Write thread ended 0x00020179 (FAILURE,HIGH) = Offset source start address is before end of previous source 0x0002017a (FAILURE,HIGH) = Expected offset source object as parameter 0x0002017b (WARNING,HIGH) = Sequential BD-R media likely to soon fail writing 0x0002017c (FAILURE,HIGH) = No valid write type selected 0x0002017d (FATAL,HIGH) = Invalid file descriptor with stdio pseudo-drive 0x0002017e (FAILURE,HIGH) = Failed to close track, session, or disc 0x0002017f (FAILURE,HIGH) = Failed to synchronize drive cache 0x00020180 (FAILURE,HIGH) = Premature end of input encountered 0x00020181 (FAILURE,HIGH) = Pseudo-drive is a read-only file. Cannot write. 0x00020182 (FAILURE,HIGH) = Cannot truncate disk file for pseudo blanking 0x00020183 (WARNING,HIGH) = Failed to open device (a pseudo-drive) for reading 0x00020184 (WARNING,HIGH) = No Next-Writable-Address 0x00020185 (WARNING,HIGH) = Track damaged, not closed and not writable 0x00020186 (WARNING,HIGH) = Track damaged and not closed 0x00020187 (NOTE,HIGH) = Track not marked as damaged. No action taken. 0x00020188 (FAILURE,HIGH) = Cannot close damaged track on given media type 0x00020189 (FATAL,HIGH) = Drive is already grabbed by libburn 0x0002018a (SORRY,HIGH) = Timeout exceeded. Retry canceled. 0x0002018b (FAILURE,HIGH) = Too many CD-TEXT packs 0x0002018c (FAILURE,HIGH) = CD-TEXT pack type out of range 0x0002018d (FAILURE,HIGH) = CD-TEXT block number out of range 0x0002018e (FAILURE,HIGH) = Too many CD-TEXT packs in block 0x0002018f (FAILURE,HIGH) = CD-TEXT pack CRC mismatch 0x00020190 (WARNING,HIGH) = CD-TEXT pack CRC mismatch had to be corrected 0x00020191 (FAILURE,HIGH) = Unknown parameter in text input file 0x00020192 (FAILURE,HIGH) = Text input file sequence error 0x00020193 (FAILURE,HIGH) = Text input file readability problem 0x00020194 (FAILURE,HIGH) = Text input file syntax error or specs violation 0x00020195 (WARNING,HIGH) = Text input file warning 0x00020196 (FAILURE,HIGH) = Session has already defined tracks 0x00020197 (FAILURE,HIGH) = Unsupported text input file feature 0x00020198 (FAILURE,HIGH) = CD-TEXT pack file readability problem 0x00020199 (SORRY,HIGH) = Text input file reading aborted 0x0002019a (SORRY,HIGH) = Bad track index number 0x0002019b (SORRY,HIGH) = CD track number exceeds range of 1 to 99 0x0002019c (SORRY,HIGH) = Session has no defined tracks 0x0002019d (SORRY,HIGH) = Audio read size not properly aligned 0x0002019e (NOTE,HIGH) = Drive does not support media certification 0x0002019f (FAILURE,HIGH) = CD-TEXT binary pack array faulty 0x000201a0 (WARNING,HIGH) = Maximum number of CD-TEXT blocks exceeded 0x000201a1 (FAILURE,HIGH) = Cannot open disk file for writing 0x000201a2 (FAILURE,HIGH) = Error while writing to disk file 0x000201a3 (UPDATE,HIGH) = Progress message of burn_drive_extract_audio() 0x000201a4 (FAILURE,HIGH) = Failure to read audio sectors 0x000201a5 (FAILURE,HIGH) = Asynchronous SCSI error 0x000201a6 (FATAL,HIGH) = Lost connection to drive 0x000201a7 (FAILURE,HIGH) = SCSI command yielded host problem 0x000201a8 (FAILURE,HIGH) = SCSI command yielded driver problem 0x000201a9 (FAILURE,HIGH) = Implausible length from GET CONFIGURATION 0x000201aa (FAILURE,HIGH) = No CD-TEXT packs in file 0x000201ab (WARN,HIGH) = Leaving burn_source_fifo object undisposed 0x000201ac (NOTE,HIGH) = Drive currently does not offer Stream Recording 0x000201ad (NOTE,HIGH) = WRITE commands have been repeated 0x000201ae (FAILURE,HIGH) = Track size exceeds 4 TiB - 32 KiB libdax_audioxtr: 0x00020200 (SORRY,HIGH) = Cannot open audio source file 0x00020201 (SORRY,HIGH) = Audio source file has unsuitable format 0x00020202 (SORRY,HIGH) = Failed to prepare reading of audio data ------------------------------------------------------------------------------ Range "vreixo" : 0x00030000 to 0x0003ffff 0x0003ffff (FAILURE,HIGH) = Operation canceled 0x0003fffe (FATAL,HIGH) = Unknown or unexpected fatal error 0x0003fffd (FAILURE,HIGH) = Unknown or unexpected error 0x0003fffc (FATAL,HIGH) = Internal programming error 0x0003fffb (FAILURE,HIGH) = NULL pointer where NULL not allowed 0x0003fffa (FATAL,HIGH) = Memory allocation error 0x0003fff9 (FATAL,HIGH) = Interrupted by a signal 0x0003fff8 (FAILURE,HIGH) = Invalid parameter value 0x0003fff7 (FATAL,HIGH) = Cannot create a needed thread 0x0003fff6 (FAILURE,HIGH) = Write error 0x0003fff5 (FAILURE,HIGH) = Buffer read error 0x0003ffc0 (FAILURE,HIGH) = Trying to add a node already added to another dir 0x0003ffbf (FAILURE,HIGH) = Node with same name already exist 0x0003ffbe (FAILURE,HIGH) = Trying to remove a node that was not added to dir 0x0003ffbd (FAILURE,HIGH) = A requested node does not exist 0x0003ffbc (FAILURE,HIGH) = Image already bootable 0x0003ffbb (FAILURE,HIGH) = Trying to use an invalid file as boot image 0x0003ff80 (FAILURE,HIGH) = Error on file operation 0x0003ff7f (FAILURE,HIGH) = Trying to open an already opened file 0x0003ff7e (FAILURE,HIGH) = Access to file is not allowed 0x0003ff7d (FAILURE,HIGH) = Incorrect path to file 0x0003ff7c (FAILURE,HIGH) = The file does not exist in the filesystem 0x0003ff7b (FAILURE,HIGH) = Trying to read or close a file not opened 0x0003ff7a (FAILURE,HIGH) = Directory used where no dir is expected 0x0003ff79 (FAILURE,HIGH) = File read error 0x0003ff78 (FAILURE,HIGH) = Not dir used where a dir is expected 0x0003ff77 (FAILURE,HIGH) = Not symlink used where a symlink is expected 0x0003ff76 (FAILURE,HIGH) = Cannot seek to specified location 0x0003ff75 (HINT,MEDIUM) = File not supported in ECMA-119 tree and ignored 0x0003ff74 (HINT,MEDIUM) = File bigger than supported by used standard 0x0003ff73 (MISHAP,HIGH) = File read error during image creation 0x0003ff72 (HINT,MEDIUM) = Cannot convert filename to requested charset 0x0003ff71 (SORRY,HIGH) = File cannot be added to the tree 0x0003ff70 (HINT,MEDIUM) = File path breaks specification constraints 0x0003ff00 (FAILURE,HIGH) = Charset conversion error 0x0003feff (FAILURE,HIGH) = Too much files to mangle 0x0003fec0 (FAILURE,HIGH) = Wrong or damaged Primary Volume Descriptor 0x0003febf (SORRY,HIGH) = Wrong or damaged RR entry 0x0003febe (SORRY,HIGH) = Unsupported RR feature 0x0003febd (FAILURE,HIGH) = Wrong or damaged ECMA-119 0x0003febc (FAILURE,HIGH) = Unsupported ECMA-119 feature 0x0003febb (SORRY,HIGH) = Wrong or damaged El-Torito catalog 0x0003feba (SORRY,HIGH) = Unsupported El-Torito feature 0x0003feb9 (SORRY,HIGH) = Cannot patch isolinux boot image 0x0003feb8 (SORRY,HIGH) = Unsupported SUSP feature 0x0003feb7 (WARNING,HIGH) = Error on a RR entry that can be ignored 0x0003feb6 (HINT,MEDIUM) = Error on a RR entry that can be ignored 0x0003feb5 (WARNING,HIGH) = Multiple ER SUSP entries found 0x0003feb4 (HINT,MEDIUM) = Unsupported volume descriptor found 0x0003feb3 (WARNING,HIGH) = El-Torito related warning 0x0003feb2 (MISHAP,HIGH) = Image write cancelled 0x0003feb1 (WARNING,HIGH) = El-Torito image is hidden Outdated codes which may not be re-used for other purposes than re-instating them, if ever: X 0x00031001 (SORRY,HIGH) = Cannot read file (ignored) X 0x00031002 (FATAL,HIGH) = Cannot read file (operation canceled) X 0x00031000 (FATAL,HIGH) = Unsupported ISO-9660 image X 0x00031001 (HINT,MEDIUM) = Unsupported Vol Desc that will be ignored X 0x00031002 (FATAL,HIGH) = Damaged ISO-9660 image X 0x00031003 (SORRY,HIGH) = Cannot read previous image file X 0x00030101 (HINT,MEDIUM) = Unsupported SUSP entry that will be ignored X 0x00030102 (SORRY,HIGH) = Wrong/damaged SUSP entry X 0x00030103 (WARNING,MEDIUM)= Multiple SUSP ER entries where found X 0x00030111 (SORRY,HIGH) = Unsupported RR feature X 0x00030112 (SORRY,HIGH) = Error in a Rock Ridge entry X 0x00030201 (HINT,MEDIUM) = Unsupported Boot Vol Desc that will be ignored X 0x00030202 (SORRY,HIGH) = Wrong El-Torito catalog X 0x00030203 (HINT,MEDIUM) = Unsupported El-Torito feature X 0x00030204 (SORRY,HIGH) = Invalid file to be an El-Torito image X 0x00030205 (WARNING,MEDIUM)= Cannot properly patch isolinux image X 0x00030206 (WARNING,MEDIUM)= Copying El-Torito from a previous image without X enough info about it X 0x00030301 (NOTE,MEDIUM) = Unsupported file type for Joliet tree ------------------------------------------------------------------------------ Range "application" : 0x00040000 to 0x0004ffff 0x00040000 (ABORT,HIGH) : Application supplied message 0x00040001 (FATAL,HIGH) : Application supplied message 0x00040002 (SORRY,HIGH) : Application supplied message 0x00040003 (WARNING,HIGH) : Application supplied message 0x00040004 (HINT,HIGH) : Application supplied message 0x00040005 (NOTE,HIGH) : Application supplied message 0x00040006 (UPDATE,HIGH) : Application supplied message 0x00040007 (DEBUG,HIGH) : Application supplied message 0x00040008 (*,HIGH) : Application supplied message ------------------------------------------------------------------------------ Range "libisofs-xorriso" : 0x00050000 to 0x0005ffff This is an alternative representation of libisofs.so.6 error codes in xorriso. If values returned by iso_error_get_code() do not fit into 0x30000 to 0x3ffff then they get truncated to 16 bit and mapped into this range. (This should never need to happen, of course.) ------------------------------------------------------------------------------ Range "libisoburn" : 0x00060000 to 0x00006ffff 0x00060000 (*,*) : Message which shall be attributed to libisoburn >>> the messages of libisoburn need to be registered individually ------------------------------------------------------------------------------ #endif /* LIBDAX_MSGS_________________ */ #ifdef LIBDAX_MSGS_H_INTERNAL /* Internal Functions */ /** Lock before doing side effect operations on m */ static int libdax_msgs_lock(struct libdax_msgs *m, int flag); /** Unlock after effect operations on m are done */ static int libdax_msgs_unlock(struct libdax_msgs *m, int flag); /** Create new empty message item. @param link Previous item in queue @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success, <=0 failure */ static int libdax_msgs_item_new(struct libdax_msgs_item **item, struct libdax_msgs_item *link, int flag); /** Destroy a message item obtained by libdax_msgs_obtain(). The submitted pointer gets set to NULL. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 for success, 0 for pointer to NULL */ static int libdax_msgs_item_destroy(struct libdax_msgs_item **item, int flag); #endif /* LIBDAX_MSGS_H_INTERNAL */ #endif /* ! LIBDAX_MSGS_H_INCLUDED */ #!/bin/sh # libdax_msgs_to_iso_msgs.sh # generates ${xyz}_msgs.[ch] from libdax_msgs.[ch] # To be executed within ./libburn-* resp ./cdrskin-* # The module name for the generated sourcecode in several # uppercase-lowercase forms xyz="libiso" Xyz="Libiso" XYZ="LIBISO" # The project name for which the generated code shall serve project="libisofs" for i in libburn/libdax_msgs.[ch] do target_adr=$(echo "$i" | sed -e "s/libdax_/${xyz}_/") echo "$target_adr" sed \ -e "s/^\/\* libdax_msgs/\/* ${xyz}_msgs (generated from XYZ_msgs : $(date))/" \ -e "s/Message handling facility of libdax/Message handling facility of ${project}/" \ -e "s/libdax_/${xyz}_/g" \ -e "s/libdax:/${xyz}:/g" \ -e "s/Libdax_/${Xyz}_/g" \ -e "s/LIBDAX_/${XYZ}_/g" \ -e "s/generated from XYZ_msgs/generated from libdax_msgs/" \ -e "s/${xyz}_msgs is designed to serve in libraries/libdax_msgs is designed to serve in libraries/" \ -e "s/Owner of ${xyz}_msgs is libburn/Owner of libdax_msgs is libburn/" \ \ <"$i" >"$target_adr" done /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2024 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /* ts A61009 */ /* #include <a ssert.h> */ #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/time.h> #include <pthread.h> #include <ctype.h> /* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #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" #include "util.h" #include "init.h" /* ts A70223 : in init.c */ extern int burn_support_untested_profiles; static int mmc_get_configuration_al(struct burn_drive *d, int *alloc_len); #ifdef Libburn_log_in_and_out_streaM /* <<< ts A61031 */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #endif /* Libburn_log_in_and_out_streaM */ /* ts A61005 */ #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /* ts A61219 : Based on knowledge from dvd+rw-tools-7.0 and mmc5r03c.pdf */ #define Libburn_support_dvd_plus_rW 1 /* ts A61229 */ #define Libburn_support_dvd_minusrw_overW 1 /* ts A70112 */ /* ts A80410 : applies to BD-RE too */ #define Libburn_support_dvd_raM 1 /* ts A70129 */ #define Libburn_support_dvd_r_seQ 1 /* ts A70306 */ #define Libburn_support_dvd_plus_R 1 /* ts A70509 : handling 0x41 as read-only type */ #define Libburn_support_bd_r_readonlY 1 /* ts A81208 */ #define Libburn_support_bd_plus_r_srM 1 /* ts A80410 : <<< Dangerous experiment: Pretend that DVD-RAM is BD-RE # define Libburn_dvd_ram_as_bd_rE yes */ /* ts A80509 : <<< Experiment: pretend that DVD-ROM and CD-ROM are other media like BD-ROM (0x40), BD-R seq (0x41), BD-R random (0x42) # define Libburn_rom_as_profilE 0x40 */ /* ts A80425 : Prevents command FORMAT UNIT for DVD-RAM or BD-RE. Useful only to test the selection of format descriptors without actually formatting the media. # define Libburn_do_not_format_dvd_ram_or_bd_rE 1 */ /* ts A90603 : Simulate the command restrictions of an old MMC-1 drive # define Libisofs_simulate_old_mmc1_drivE 1 */ /* DVD/BD progress report: ts A61219 : It seems to work with a used (i.e. thoroughly formatted) DVD+RW. Error messages of class DEBUG appear because of inability to read TOC or track info. Nevertheless, the written images verify. ts A61220 : Burned to a virgin DVD+RW by help of new mmc_format_unit() (did not test whether it would work without). Burned to a not completely formatted DVD+RW. (Had worked before without mmc_format_unit() but i did not exceed the formatted range as reported by dvd+rw-mediainfo.) ts A61221 : Speed setting now works for both of my drives. The according functions in dvd+rw-tools are a bit intimidating to the reader. I hope it is possible to leave much of this to the drive. And if it fails ... well, it's only speed setting. :)) ts A61229 : Burned to several DVD-RW formatted to mode Restricted Overwrite by dvd+rw-format. Needs Libburn_support_dvd_minusrw_overW. ts A61230 : Other than growisofs, libburn does not send a mode page 5 for such DVD-RW (which the MMC-5 standard does deprecate) and it really seems to work without such a page. ts A70101 : Formatted DVD-RW media. Success is varying with media, but dvd+rw-format does not do better with the same media. ts A70112 : Support for writing to DVD-RAM. ts A70130 : Burned a first non-multi sequential DVD-RW. Feature 0021h Incremental Recording vanishes after that and media thus gets not recognized as suitable any more. After a run with -multi another disc still offers 0021h . dvd+rw-mediainfo shows two tracks. The second, an afio archive is readable by afio. Third and forth veryfy too. Suddenly dvd+rw-mediainfo sees lba 0 with track 2. But #2 still verifies if one knows its address. ts A70203 : DVD-RW need to get blanked fully. Then feature 0021h persists. Meanwhile Incremental streaming is supported like CD TAO: with unpredicted size, multi-track, multi-session. ts A70205 : Beginning to implement DVD-R[W] DAO : single track and session, size prediction mandatory. ts A70208 : Finally made tests with DVD-R. Worked exactly as new DVD-RW. ts A70306 : Implemented DVD+R (always -multi for now) ts A70330 : Allowed finalizing of DVD+R. ts A80228 : Made DVD+R/DL support official after nightmorph reported success in http://libburnia-project.org/ticket/13 ts A80416 : drive->do_stream_recording brings DVD-RAM to full nominal writing speed at cost of no defect management. ts A80416 : Giulio Orsero reports success with BD-RE writing. With drive->do_stream_recording it does full nominal speed. ts A80506 : Giulio Orsero reports success with BD-RE formatting. BD-RE is now an officially supported profile. ts A81209 : The first two sessions have been written to BD-R SRM (auto formatted without Defect Management). ts A90107 : BD-R is now supported media type */ /* ts A70519 : With MMC commands of data direction FROM_DRIVE: Made struct command.dxfer_len equal to Allocation Length of MMC commands. Made sure that not more bytes are allowed for transfer than there are available. */ /* ts A70711 Trying to keep writing from clogging the SCSI driver due to full buffer at burner drive: 0=waiting disabled, 1=enabled These are only defaults which can be overwritten by burn_drive_set_buffer_waiting() */ #define Libburn_wait_for_buffer_freE 0 #define Libburn_wait_for_buffer_min_useC 10000 #define Libburn_wait_for_buffer_max_useC 100000 #define Libburn_wait_for_buffer_tio_seC 120 #define Libburn_wait_for_buffer_min_perC 65 #define Libburn_wait_for_buffer_max_perC 95 /* ts B31107 The minimum values to be applied if maximum read speed is requested. Some drives tell only the currently set speed and thus cannot be made faster by using the highest told value. (The fractions get added or subtracted to yield an integer number on the safe side of the intended limit.) */ #define Libburn_cd_max_read_speeD (52 * 150) #define Libburn_dvd_max_read_speeD (24 * 1385) #define Libburn_bd_max_read_speeD (20 * 4495.625 + 0.5) /* ts B31114 The maximum values for minimum speed */ #define Libburn_cd_min_read_speeD ( 1 * 150) #define Libburn_dvd_min_read_speeD ( 1 * 1385) #define Libburn_bd_min_read_speeD ( 1 * 4495.625 - 0.625) static unsigned char MMC_GET_MSINFO[] = { 0x43, 0, 1, 0, 0, 0, 0, 16, 0, 0 }; static unsigned char MMC_GET_TOC[] = { 0x43, 2, 2, 0, 0, 0, 0, 16, 0, 0 }; static unsigned char MMC_GET_TOC_FMT0[] = { 0x43, 0, 0, 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_LEADTEXT[] = { 0x43, 2, 5, 0, 0, 0, 0, 4, 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_BLANK[] = { 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 }; /* ts A61201 : inserted 0, before 16, */ static unsigned char MMC_GET_CONFIGURATION[] = { 0x46, 0, 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, 0x7e, 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 }; /* ts A61023 : get size and free space of drive buffer */ static unsigned char MMC_READ_BUFFER_CAPACITY[] = { 0x5C, 0, 0, 0, 0, 0, 0, 16, 0, 0 }; /* ts A61219 : format DVD+RW (and various others) */ static unsigned char MMC_FORMAT_UNIT[] = { 0x04, 0x11, 0, 0, 0, 0 }; /* ts A61221 : To set speed for DVD media (0xBB is for CD but works on my LG GSA drive) */ static unsigned char MMC_SET_STREAMING[] = { 0xB6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ts A61225 : To obtain write speed descriptors (command can do other things too) */ static unsigned char MMC_GET_PERFORMANCE[] = { 0xAC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ts A70108 : To obtain info about drive and media formatting opportunities */ static unsigned char MMC_READ_FORMAT_CAPACITIES[] = { 0x23, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ts A70205 : To describe the layout of a DVD-R[W] DAO session */ static unsigned char MMC_RESERVE_TRACK[] = { 0x53, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ts A70812 : Read data sectors (for types with 2048 bytes/sector only) */ static unsigned char MMC_READ_10[] = { 0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ts A81210 : Determine the upper limit of readable data size */ static unsigned char MMC_READ_CAPACITY[] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ts A90903 : Obtain media type specific information. E.g. manufacturer. */ static unsigned char MMC_READ_DISC_STRUCTURE[] = { 0xAD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ts B21125 : An alternative to BEh READ CD */ static unsigned char MMC_READ_CD_MSF[] = { 0xB9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static int mmc_function_spy_do_tell = 0; int mmc_function_spy(struct burn_drive *d, char * text) { if (mmc_function_spy_do_tell) fprintf(stderr,"libburn: experimental: mmc_function_spy: %s\n", text); if (d == NULL) return 1; if (d->drive_role != 1) { char msg[4096]; sprintf(msg, "Emulated drive caught in SCSI adapter \"%s\"", text); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002014c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); d->cancel = 1; return 0; } return 1; } int mmc_function_spy_ctrl(int do_tell) { mmc_function_spy_do_tell= !!do_tell; return 1; } /* ts A70201 */ int mmc_four_char_to_int(unsigned char *data) { return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; } /* ts C40226 */ unsigned int mmc_four_char_to_uint(unsigned char *data) { return (((unsigned int) data[0]) << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; } /* ts A70201 */ int mmc_int_to_four_char(unsigned char *data, int num) { data[0] = (num >> 24) & 0xff; data[1] = (num >> 16) & 0xff; data[2] = (num >> 8) & 0xff; data[3] = num & 0xff; return 1; } /* ts C40226 */ int mmc_uint_to_four_char(unsigned char *data, unsigned int num) { data[0] = (num >> 24) & 0xff; data[1] = (num >> 16) & 0xff; data[2] = (num >> 8) & 0xff; data[3] = num & 0xff; return 1; } static int mmc_start_for_bit0 = 0; /* @param flag bit0= the calling function should need no START UNIT. (Handling depends on mmc_start_for_bit0) */ int mmc_start_if_needed(struct burn_drive *d, int flag) { if (!d->is_stopped) return 2; if ((flag & 1) && !mmc_start_for_bit0) return 2; d->start_unit(d); d->is_stopped = 0; return 1; } int mmc_send_cue_sheet(struct burn_drive *d, struct cue_sheet *s) { struct buffer *buf = NULL; struct command *c; c = &(d->casual_command); mmc_start_if_needed(d, 0); if (mmc_function_spy(d, "mmc_send_cue_sheet") <= 0) return 0; BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); scsi_init_command(c, MMC_SEND_CUE_SHEET, sizeof(MMC_SEND_CUE_SHEET)); c->retry = 1; 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); ex:; BURN_FREE_MEM(buf); if (c->error) { d->cancel = 1; scsi_notify_error(d, c, c->sense, 18, 2); } return !c->error; } /* ts A70205 : Announce size of a DVD-R[W] DAO session. @param size The size in bytes to be announced to the drive. It will get rounded up to align to 32 KiB. */ int mmc_reserve_track(struct burn_drive *d, off_t size) { struct command *c; int lba; char msg[80]; c = &(d->casual_command); mmc_start_if_needed(d, 0); if (mmc_function_spy(d, "mmc_reserve_track") <= 0) return 0; scsi_init_command(c, MMC_RESERVE_TRACK, sizeof(MMC_RESERVE_TRACK)); c->retry = 1; lba = size / 2048; if (size % 2048) lba++; mmc_int_to_four_char(c->opcode+5, lba); sprintf(msg, "reserving track of %d blocks", lba); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); c->page = NULL; c->dir = NO_TRANSFER; c->timeout = Libburn_mmc_reserve_timeouT; d->issue_command(d, c); if (c->error) { d->cancel = 1; scsi_notify_error(d, c, c->sense, 18, 2); } return !c->error; } /* ts A70201 : Common track info fetcher for mmc_get_nwa() and mmc_fake_toc() */ int mmc_read_track_info(struct burn_drive *d, int trackno, struct buffer *buf, int alloc_len) { struct command *c; c = &(d->casual_command); mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_read_track_info") <= 0) return 0; scsi_init_command(c, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO)); c->dxfer_len = alloc_len; c->opcode[7] = (c->dxfer_len >> 8) & 0xff; c->opcode[8] = c->dxfer_len & 0xff; c->retry = 1; c->opcode[1] = 1; if(trackno<=0) { if (d->current_profile == 0x1a || d->current_profile == 0x13 || d->current_profile == 0x12 || d->current_profile == 0x42 || d->current_profile == 0x43) /* DVD+RW , DVD-RW restricted overwrite , DVD-RAM BD-R random recording, BD-RE */ trackno = 1; else if (d->current_profile == 0x10 || d->current_profile == 0x11 || d->current_profile == 0x14 || d->current_profile == 0x15 || d->current_profile == 0x40 || d->current_profile == 0x41) /* DVD-ROM , DVD-R[W] Sequential , BD-ROM , BD-R sequential */ trackno = d->last_track_no; else /* mmc5r03c.pdf: valid only for CD, DVD+R, DVD+R DL */ trackno = 0xFF; } mmc_int_to_four_char(c->opcode + 2, trackno); c->page = buf; memset(buf->data, 0, BUFFER_SIZE); c->dir = FROM_DRIVE; d->issue_command(d, c); if (c->error) return 0; return 1; } /* ts A61110 : added parameters trackno, lba, nwa. Redefined return value. @return 1=nwa is valid , 0=nwa is not valid , -1=error */ /* ts A70201 : outsourced 52h READ TRACK INFO command */ int mmc_get_nwa(struct burn_drive *d, int trackno, int *lba, int *nwa) { struct buffer *buf = NULL; int ret, num, alloc_len = 20, err; unsigned char *data; char *msg = NULL; if (trackno <= 0) d->next_track_damaged = 0; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_get_nwa") <= 0) {ret = -1; goto ex;} /* ts B00327 : Avoid to inquire unsuitable media states */ if (d->status != BURN_DISC_BLANK && d->status != BURN_DISC_APPENDABLE) {ret = 0; goto ex;} BURN_ALLOC_MEM(buf, struct buffer, 1); ret = mmc_read_track_info(d, trackno, buf, alloc_len); if (ret <= 0) goto ex; data = buf->data; *lba = mmc_four_char_to_int(data + 8); *nwa = mmc_four_char_to_int(data + 12); num = mmc_four_char_to_int(data + 16); /* Pioneer BD-RW BDR-205 and LITE-ON LTR-48125S return -150 as *nwa of blank media */ if (*nwa < *lba && d->status == BURN_DISC_BLANK) *nwa = *lba; #ifdef Libburn_pioneer_dvr_216d_load_mode5 /* >>> memorize track mode : data[6] & 0xf */; #endif { static int fake_damage = 0; /* bit0= damage on , bit1= NWA_V off */ if (fake_damage & 1) data[5] |= 32; /* Damage bit */ if (fake_damage & 2) data[7] &= ~1; } BURN_ALLOC_MEM(msg, char, 160); if (trackno > 0) sprintf(msg, "Track number %d: ", trackno); else sprintf(msg, "Upcoming track: "); if (d->current_profile == 0x1a || d->current_profile == 0x13 || d->current_profile == 0x12 || d->current_profile == 0x43) { /* overwritable */ *lba = *nwa = num = 0; } else if (data[5] & 32) { /* ts B10534 : MMC-5 6.27.3.7 Damage Bit */ if (!(data[7] & 1)) { /* NWA_V is set to zero */ /* "not closed due to an incomplete write" */ strcat(msg, "Damaged, not closed and not writable"); err= 0x00020185; } else { /* "may be recorded further in an incremental manner"*/ strcat(msg, "Damaged and not closed"); err= 0x00020186; } libdax_msgs_submit(libdax_messenger, d->global_index, err, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); if (trackno <= 0) d->next_track_damaged |= ((!(data[7] & 1)) << 1) | 1; {ret = 0; goto ex;} } else if (!(data[7] & 1)) { /* ts A61106 : MMC-1 Table 142 : NWA_V = NWA Valid Flag */ strcat(msg, "No Next-Writable-Address"); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020184, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); if (trackno <= 0) d->next_track_damaged |= 2; {ret = 0; goto ex;} } if (num > 0) { burn_drive_set_media_capacity_remaining(d, ((off_t) num) * ((off_t) 2048)); d->media_lba_limit = *nwa + num; } else d->media_lba_limit = 0; /* fprintf(stderr, "LIBBURN_DEBUG: media_lba_limit= %d\n", d->media_lba_limit); */ ret = 1; ex: BURN_FREE_MEM(buf); BURN_FREE_MEM(msg); return ret; } /* ts A61009 : function is obviously unused. */ /* void mmc_close_disc(struct burn_drive *d, struct burn_write_opts *o) */ void mmc_close_disc(struct burn_write_opts *o) { struct burn_drive *d = o->drive; if (mmc_function_spy(d, "mmc_close_disc") <= 0) return; libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, "HOW THAT ? mmc_close_disc() was called", 0, 0); /* ts A61009 : made impossible by removing redundant parameter d */ /* a ssert(o->drive == d); */ o->multi = 0; spc_select_write_params(d, NULL, 0, o); mmc_close(d, 1, 0); } /* ts A61009 : function is obviously unused. */ /* void mmc_close_session(struct burn_drive *d, struct burn_write_opts *o) */ void mmc_close_session(struct burn_write_opts *o) { struct burn_drive *d = o->drive; if (mmc_function_spy(d, "mmc_close_session") <= 0) return; libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, "HOW THAT ? mmc_close_session() was called", 0, 0); /* ts A61009 : made impossible by removing redundant parameter d */ /* a ssert(o->drive == d); */ o->multi = 3; spc_select_write_params(d, NULL, 0, o); mmc_close(d, 1, 0); } /* ts A70227 : extended meaning of session to address all possible values of 5Bh CLOSE TRACK SESSION to address any Close Function. @param session contains the two high bits of Close Function @param track if not 0: sets the lowest bit of Close Function */ void mmc_close(struct burn_drive *d, int session, int track) { struct command *c; char msg[256]; int key, asc, ascq; c = &(d->casual_command); if (mmc_function_spy(d, "mmc_close") <= 0) return; scsi_init_command(c, MMC_CLOSE, sizeof(MMC_CLOSE)); c->retry = 1; if (!d->do_no_immed) c->opcode[1] |= 1; /* ts A70918 : Immed */ /* (ts A61030 : shifted !!session rather than or-ing plain session ) */ c->opcode[2] = ((session & 3) << 1) | !!track; c->opcode[4] = track >> 8; c->opcode[5] = track & 0xFF; c->page = NULL; c->dir = NO_TRANSFER; if (d->do_no_immed) c->timeout = Libburn_mmc_close_noim_timeouT; else c->timeout = Libburn_mmc_close_timeouT; d->issue_command(d, c); /* ts A70918 : Immed : wait for drive to complete command */ if (c->error) { sprintf(msg, "Failed to close %s (%d)", session > 1 ? "disc" : session > 0 ? "session" : "track", ((session & 3) << 1) | !!track); sprintf(msg + strlen(msg), ". SCSI error : "); scsi_error_msg(d, c->sense, 14, msg + strlen(msg), &key, &asc, &ascq); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002017e, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); d->cancel = 1; return; } spc_human_readable_cmd(c, msg, 160, 0); if (spc_wait_unit_attention(d, 3600, msg, 0) <= 0) d->cancel = 1; } void mmc_get_event(struct burn_drive *d) { struct buffer *buf = NULL; struct command *c; int alloc_len = 8, len, evt_code, loops = 0; unsigned char *evt; c = &(d->casual_command); BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); if (mmc_function_spy(d, "mmc_get_event") <= 0) goto ex; again:; scsi_init_command(c, MMC_GET_EVENT, sizeof(MMC_GET_EVENT)); c->dxfer_len = 8; /* >>> have a burn_drive element for Notification Class */; c->opcode[4] = 0x7e; c->opcode[7] = (c->dxfer_len >> 8) & 0xff; c->opcode[8] = c->dxfer_len & 0xff; c->retry = 1; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); if (c->error) goto ex; evt = c->page->data; len = ((evt[0] << 8) | evt[1]) + 2; if (len < 8) goto ex; /* >>> memorize evt[3] in burn_drive element for Notification Class */; if (evt[3] == 0) /* No event */ goto ex; evt_code = evt[4] & 0xf; if (evt_code == 0) /* No change */ goto ex; switch (evt[2] & 7) { case 0: /* no events supported */ goto ex; case 1: /* Operational change */ if (((evt[6] << 8) | evt[7])) { alloc_len = 8; mmc_get_configuration_al(d, &alloc_len); } break; case 2: /* Power Management */ if (evt[5] >= 2) d->start_unit(d); break; case 3: /* External request */ /* >>> report about external request */; break; case 4: /* Media */ if (evt_code == 2) { d->start_unit(d); alloc_len = 8; mmc_get_configuration_al(d, &alloc_len); } break; case 5: /* Multiple Host Events */ /* >>> report about foreign host interference */; break; case 6: /* Device busy */ if (evt_code == 1 && evt[5]) { /* >>> wait the time announced in evt[6],[7] as 100ms units */; } break; default: /* reserved */ break; } loops++; if (loops < 100) goto again; ex:; BURN_FREE_MEM(buf); } /* ts A70711 This has become a little monster because of the creative buffer reports of my LG GSA-4082B : Belated, possibly statistically dampened. But only with DVD media. With CD it is ok. */ static int mmc_wait_for_buffer_free(struct burn_drive *d, struct buffer *buf) { int usec= 0, reported_3s = 0, first_wait = 1; off_t need; struct timeval t0,tnow; double max_fac, min_fac, waiting; /* Enable to get reported waiting activities and total time. #define Libburn_mmc_wfb_debuG 1 */ #ifdef Libburn_mmc_wfb_debuG char sleeplist[32768]; static int buffer_still_invalid = 1; #endif max_fac = ((double) d->wfb_max_percent) / 100.0; /* Buffer info from the drive is valid only after writing has begun. Caring for buffer space makes sense mostly after max_percent of the buffer was transmitted. */ if (d->progress.buffered_bytes <= 0 || d->progress.buffer_capacity <= 0 || d->progress.buffered_bytes + buf->bytes <= d->progress.buffer_capacity * max_fac) return 2; #ifdef Libburn_mmc_wfb_debuG if (buffer_still_invalid) fprintf(stderr, "\nLIBBURN_DEBUG: Buffer considered valid now\n"); buffer_still_invalid = 0; #endif /* The pessimistic counter does not assume any buffer consumption */ if (d->pessimistic_buffer_free - buf->bytes >= ( 1.0 - max_fac) * d->progress.buffer_capacity) return 1; /* There is need to inquire the buffer fill */ d->pessimistic_writes++; min_fac = ((double) d->wfb_min_percent) / 100.0; gettimeofday(&t0, NULL); #ifdef Libburn_mmc_wfb_debuG sleeplist[0]= 0; sprintf(sleeplist,"(%d%s %d)", (int) (d->pessimistic_buffer_free - buf->bytes), (d->pbf_altered ? "? -" : " -"), (int) ((1.0 - max_fac) * d->progress.buffer_capacity)); #endif while (1) { if ((!first_wait) || d->pbf_altered) { d->pbf_altered = 1; mmc_read_buffer_capacity(d); } #ifdef Libburn_mmc_wfb_debuG if(strlen(sleeplist) < sizeof(sleeplist) - 80) sprintf(sleeplist+strlen(sleeplist)," (%d%s %d)", (int) (d->pessimistic_buffer_free - buf->bytes), (d->pbf_altered ? "? -" : " -"), (int) ((1.0 - min_fac) * d->progress.buffer_capacity)); #endif gettimeofday(&tnow, NULL); waiting = (tnow.tv_sec - t0.tv_sec) + ((double) (tnow.tv_usec - t0.tv_usec)) / 1.0e6; if (d->pessimistic_buffer_free - buf->bytes >= (1.0 - min_fac) * d->progress.buffer_capacity) { #ifdef Libburn_mmc_wfb_debuG if(strlen(sleeplist) >= sizeof(sleeplist) - 80) strcat(sleeplist," ..."); sprintf(sleeplist+strlen(sleeplist)," -> %d [%.6f]", (int) ( d->pessimistic_buffer_free - buf->bytes - (1.0 - min_fac) * d->progress.buffer_capacity ), waiting); fprintf(stderr, "\nLIBBURN_DEBUG: sleeplist= %s\n",sleeplist); #endif return 1; } /* Waiting is needed */ if (waiting >= 3 && !reported_3s) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002013d, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_LOW, "Waiting for free buffer takes more than 3 seconds", 0,0); reported_3s = 1; } else if (d->wfb_timeout_sec > 0 && waiting > d->wfb_timeout_sec) { d->wait_for_buffer_free = 0; libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002013d, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Timeout with waiting for free buffer. Now disabled.", 0,0); break; } need = (1.0 - min_fac) * d->progress.buffer_capacity + buf->bytes - d->pessimistic_buffer_free; usec = 0; if (d->nominal_write_speed > 0) usec = ((double) need) / 1000.0 / ((double) d->nominal_write_speed) * 1.0e6; else usec = d->wfb_min_usec * 2; /* >>> learn about buffer progress and adjust usec */ if (usec < (int) d->wfb_min_usec) usec = d->wfb_min_usec; else if (usec > (int) d->wfb_max_usec) usec = d->wfb_max_usec; usleep(usec); if (d->waited_usec < 0xf0000000) d->waited_usec += usec; d->waited_tries++; if(first_wait) d->waited_writes++; #ifdef Libburn_mmc_wfb_debuG if(strlen(sleeplist) < sizeof(sleeplist) - 80) sprintf(sleeplist+strlen(sleeplist)," %d", usec); #endif first_wait = 0; } return 0; } void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf) { struct command *c; int len; c = &(d->casual_command); mmc_start_if_needed(d, 0); if (mmc_function_spy(d, "mmc_write_12") <= 0) return; len = buf->sectors; scsi_init_command(c, MMC_WRITE_12, sizeof(MMC_WRITE_12)); c->retry = 1; mmc_int_to_four_char(c->opcode + 2, start); mmc_int_to_four_char(c->opcode + 6, len); c->page = buf; c->dir = TO_DRIVE; c->timeout = Libburn_scsi_write_timeouT; d->issue_command(d, c); /* ts A70711 */ d->pessimistic_buffer_free -= buf->bytes; d->pbf_altered = 1; } #ifdef Libburn_write_time_debuG static int print_time(int flag) { static struct timeval prev = {0, 0}; struct timeval now; int ret, diff; ret = gettimeofday(&now, NULL); if (ret == -1) return 0; if (now.tv_sec - prev.tv_sec < Libburn_scsi_write_timeouT) { diff = (now.tv_sec - prev.tv_sec) * 1000000 + ((int) (now.tv_usec) - (int) prev.tv_usec); fprintf(stderr, "\nlibburn_DEBUG: %d.%-6d : %d\n", (int) now.tv_sec, (int) now.tv_usec, diff); } memcpy(&prev, &now, sizeof(struct timeval)); return 1; } #endif /* Libburn_write_time_debuG */ int mmc_write(struct burn_drive *d, off_t start_long, struct buffer *buf) { int cancelled; struct command *c; int len, key, asc, ascq, start; char *msg = NULL; #ifdef Libburn_write_time_debuG extern int burn_sg_log_scsi; #endif /* fprintf(stderr, "libburn_DEBUG: buffer sectors= %d bytes= %d\n", buf->sectors, buf->bytes); */ if (start_long > (off_t) 0x7fffffff) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002012d, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Exceeding range of signed integer block addresses", 0, 0); d->cancel = 1; return BE_CANCELLED; } start = start_long; c = &(d->casual_command); #ifdef Libburn_log_in_and_out_streaM /* <<< ts A61031 */ static int tee_fd= -1; if(tee_fd==-1) tee_fd= open("/tmp/libburn_sg_written", O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR); #endif /* Libburn_log_in_and_out_streaM */ mmc_start_if_needed(d, 0); if (mmc_function_spy(d, "mmc_write") <= 0) return BE_CANCELLED; cancelled = d->cancel; if (cancelled) return BE_CANCELLED; /* ts A70215 */ if (d->media_lba_limit > 0 && start >= d->media_lba_limit && !d->write_opts->force_is_set) { msg = calloc(1, 320); if (msg != NULL) { sprintf(msg, "Exceeding range of permissible write addresses (%d >= %d)", start, d->media_lba_limit); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002012d, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); free(msg); } d->cancel = 1; /* No need for mutexing because atomic */ return BE_CANCELLED; } len = buf->sectors; /* ts A61009 : buffer fill problems are to be handled by caller */ /* a ssert(buf->bytes >= buf->sectors);*/ /* can be == at 0... */ /* ts A70711 */ if(d->wait_for_buffer_free) mmc_wait_for_buffer_free(d, buf); #ifdef Libburn_write_time_debuG if (burn_sg_log_scsi & 3) print_time(0); #endif /* ts A80412 */ if(d->do_stream_recording > 0 && start >= d->stream_recording_start) { scsi_init_command(c, MMC_WRITE_12, sizeof(MMC_WRITE_12)); mmc_int_to_four_char(c->opcode + 2, start); mmc_int_to_four_char(c->opcode + 6, len); c->opcode[10] = 1<<7; /* Streaming bit */ } else { scsi_init_command(c, MMC_WRITE_10, sizeof(MMC_WRITE_10)); mmc_int_to_four_char(c->opcode + 2, start); c->opcode[6] = 0; c->opcode[7] = (len >> 8) & 0xFF; c->opcode[8] = len & 0xFF; } c->retry = 1; c->page = buf; c->dir = TO_DRIVE; c->timeout = Libburn_scsi_write_timeouT; #ifdef Libburn_log_in_and_out_streaM /* <<< ts A61031 */ if(tee_fd!=-1) { write(tee_fd, c->page->data, c->page->bytes); } #endif /* Libburn_log_in_and_out_streaM */ d->issue_command(d, c); /* ts A70711 */ d->pessimistic_buffer_free -= buf->bytes; d->pbf_altered = 1; /* ts A61112 : react on eventual error condition */ spc_decode_sense(c->sense, 0, &key, &asc, &ascq); if (c->error && key != 0) { int key, asc, ascq; int err_sev = LIBDAX_MSGS_SEV_FATAL; msg = calloc(1, 320); if (msg != NULL) { sprintf(msg, "SCSI error on write(%d,%d): ", start, len); scsi_error_msg(d, c->sense, 14, msg + strlen(msg), &key, &asc, &ascq); } /* ts B31023 */ /* Memorize if on DVD-RW write mode is TAO/Incremental and error [5 64 00] occurs within the first drive buffer fill. */ if (d->current_profile == 0x14 && d->write_opts != NULL && (d->progress.buffer_capacity == 0 || start < (off_t) d->progress.buffer_capacity / 2048) && key == 5 && asc == 0x64 && ascq == 0) { if (d->write_opts->write_type == BURN_WRITE_TAO) { d->was_feat21h_failure = 1 + (start == 0); if (d->write_opts->feat21h_fail_sev != 0) err_sev = d->write_opts->feat21h_fail_sev; } } if (msg != NULL) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002011d, err_sev, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); strcpy(msg, "CDB= "); if (spc_human_readable_cmd(c, msg + strlen(msg), 320 - strlen(msg), 0) > 0) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002011d, err_sev, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } free(msg); } d->cancel = 1; return BE_CANCELLED; } d->write_retry_count += c->retry_count; return 0; } /* ts A70201 : Set up an entry for mmc_fake_toc() */ int mmc_fake_toc_entry(struct burn_toc_entry *entry, int session_number, int track_number, unsigned char *size_data, unsigned char *start_data, unsigned char *last_adr_data) { int min, sec, frames; unsigned int num; /* mark extensions as valid: DVD, Track Info, Long block address */ entry->extensions_valid |= (1 | 2 | 8); /* defaults are as of mmc5r03.pdf 6.26.3.2.4 Fabricated TOC */ entry->session = session_number & 0xff; entry->session_msb = (session_number >> 8) & 0xff; entry->adr = 1; entry->control = 4; entry->tno = 0; entry->point = track_number & 0xff; entry->point_msb = (track_number >> 8) & 0xff; num = mmc_four_char_to_uint(size_data); if (num < 0x80000000) { entry->track_blocks = num; burn_lba_to_msf(num, &min, &sec, &frames); } else { entry->track_blocks = -1; min = 477218; /* ~=LBA 0x7fffffff */ sec = frames = 0; } entry->long_track_blocks = num; if (min > 255) { min = 255; sec = 255; frames = 255; } entry->min = min; entry->sec = sec; entry->frame = frames; entry->zero = 0; num = mmc_four_char_to_uint(start_data); if (num < 0x80000000) { entry->start_lba = num; burn_lba_to_msf(num, &min, &sec, &frames); } else { entry->start_lba = -1; min = 477218; sec = frames = 0; } entry->long_start_lba = num; if (min > 255) { min = 255; sec = 255; frames = 255; } entry->pmin = min; entry->psec = sec; entry->pframe = frames; num = mmc_four_char_to_uint(last_adr_data); if (num < 0x80000000) { entry->last_recorded_address = num; } else { entry->last_recorded_address = -1; } entry->long_last_rec_adr = num; return 1; } /* ts A71128 : for DVD-ROM drives which offer no reliable track information */ static int mmc_read_toc_fmt0_al(struct burn_drive *d, int *alloc_len) { struct burn_track *track; struct burn_session *session; struct burn_toc_entry *entry; struct buffer *buf = NULL; struct command *c = NULL; int dlen, i, old_alloc_len, session_number, prev_session = -1, ret; unsigned int lba, size; unsigned char *tdata, size_data[4], start_data[4], end_data[4]; if (*alloc_len < 4) {ret = 0; goto ex;} BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); scsi_init_command(c, MMC_GET_TOC_FMT0, sizeof(MMC_GET_TOC_FMT0)); c->dxfer_len = *alloc_len; c->opcode[7] = (c->dxfer_len >> 8) & 0xff; c->opcode[8] = c->dxfer_len & 0xff; c->retry = 1; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); if (c->error) { err_ex:; libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010d, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "Could not inquire TOC", 0,0); d->status = BURN_DISC_UNSUITABLE; d->toc_entries = 0; /* Preferring memory leaks over fandangos */ d->toc_entry = calloc(1, sizeof(struct burn_toc_entry)); {ret = 0; goto ex;} } dlen = c->page->data[0] * 256 + c->page->data[1]; old_alloc_len = *alloc_len; *alloc_len = dlen + 2; if (old_alloc_len < 12) {ret = 1; goto ex;} if (dlen + 2 > old_alloc_len) dlen = old_alloc_len - 2; d->complete_sessions = 1 + c->page->data[3] - c->page->data[2]; #ifdef Libburn_disc_with_incomplete_sessioN /* ts B30112 : number of open sessions */ d->incomplete_sessions = 0; #endif d->last_track_no = d->complete_sessions; if (dlen - 2 < (d->last_track_no + 1) * 8) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020159, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "TOC Format 0 returns inconsistent data", 0,0); goto err_ex; } d->toc_entries = d->last_track_no + d->complete_sessions; if (d->toc_entries < 1) {ret = 0; goto ex;} d->toc_entry = calloc(d->toc_entries, sizeof(struct burn_toc_entry)); if(d->toc_entry == NULL) {ret = 0; goto ex;} d->disc = burn_disc_create(); if (d->disc == NULL) {ret = 0; goto ex;} for (i = 0; i < d->complete_sessions; i++) { session = burn_session_create(); if (session == NULL) {ret = 0; goto ex;} burn_disc_add_session(d->disc, session, BURN_POS_END); burn_session_free(session); } for (i = 0; i < d->last_track_no; i++) { tdata = c->page->data + 4 + i * 8; session_number = i + 1; if (session_number != prev_session && prev_session > 0) { /* leadout entry previous session */ entry = &(d->toc_entry[(i - 1) + prev_session]); lba = mmc_four_char_to_uint(start_data) + mmc_four_char_to_uint(size_data); mmc_uint_to_four_char(start_data, lba); mmc_uint_to_four_char(size_data, 0); mmc_uint_to_four_char(end_data, lba - 1); mmc_fake_toc_entry(entry, prev_session, 0xA2, size_data, start_data, end_data); entry->min= entry->sec= entry->frame= 0; d->disc->session[prev_session - 1]->leadout_entry = entry; } /* ??? >>> d->media_capacity_remaining , d->media_lba_limit as of mmc_fake_toc() */ entry = &(d->toc_entry[i + session_number - 1]); track = burn_track_create(); if (track == NULL) {ret = -1; goto ex;} burn_session_add_track( d->disc->session[session_number - 1], track, BURN_POS_END); track->entry = entry; burn_track_free(track); memcpy(start_data, tdata + 4, 4); /* size_data are estimated from next track start */ memcpy(size_data, tdata + 8 + 4, 4); mmc_uint_to_four_char(end_data, mmc_four_char_to_uint(size_data) - 1); size = mmc_four_char_to_uint(size_data) - mmc_four_char_to_uint(start_data); mmc_uint_to_four_char(size_data, size); mmc_fake_toc_entry(entry, session_number, i + 1, size_data, start_data, end_data); if (prev_session != session_number) d->disc->session[session_number - 1]->firsttrack = i+1; d->disc->session[session_number - 1]->lasttrack = i+1; prev_session = session_number; } if (prev_session > 0 && prev_session <= d->disc->sessions) { /* leadout entry of last session of closed disc */ tdata = c->page->data + 4 + d->last_track_no * 8; entry = &(d->toc_entry[(d->last_track_no - 1) + prev_session]); memcpy(start_data, tdata + 4, 4); mmc_uint_to_four_char(size_data, 0); mmc_uint_to_four_char(end_data, mmc_four_char_to_uint(start_data) - 1); mmc_fake_toc_entry(entry, prev_session, 0xA2, size_data, start_data, end_data); entry->min= entry->sec= entry->frame= 0; d->disc->session[prev_session - 1]->leadout_entry = entry; } ret = 1; ex:; BURN_FREE_MEM(buf); BURN_FREE_MEM(c); return ret; } /* ts A71128 : for DVD-ROM drives which offer no reliable track information */ static int mmc_read_toc_fmt0(struct burn_drive *d) { int alloc_len = 4, ret; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_read_toc_fmt0") <= 0) return -1; ret = mmc_read_toc_fmt0_al(d, &alloc_len); if (alloc_len >= 12) ret = mmc_read_toc_fmt0_al(d, &alloc_len); return ret; } /* ts A70131 : compose a disc TOC structure from d->complete_sessions and 52h READ TRACK INFORMATION */ int mmc_fake_toc(struct burn_drive *d) { struct burn_track *track; struct burn_session *session; struct burn_toc_entry *entry; struct buffer *buf = NULL; int i, session_number, prev_session = -1, ret, alloc_len = 34; unsigned int lba; unsigned char *tdata, size_data[4], start_data[4], end_data[4]; char *msg = NULL; if (mmc_function_spy(d, "mmc_fake_toc") <= 0) {ret = -1; goto ex;} BURN_ALLOC_MEM(buf, struct buffer, 1); #ifdef Libburn_disc_with_incomplete_sessioN if (d->last_track_no <= 0 || d->complete_sessions + d->incomplete_sessions <= 0 || d->status == BURN_DISC_BLANK) {ret = 2; goto ex;} #else if (d->last_track_no <= 0 || d->complete_sessions <= 0 || d->status == BURN_DISC_BLANK) {ret = 2; goto ex;} #endif /* ! Libburn_disc_with_incomplete_sessioN */ if (d->last_track_no > BURN_MMC_FAKE_TOC_MAX_SIZE) { msg = calloc(1, 160); if (msg != NULL) { sprintf(msg, "Too many logical tracks recorded (%d , max. %d)\n", d->last_track_no, BURN_MMC_FAKE_TOC_MAX_SIZE); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002012c, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); free(msg); } {ret = 0; goto ex;} } /* ts A71128 : My DVD-ROM drive issues no reliable track info. One has to try 43h READ TOC/PMA/ATIP Form 0. */ if ((d->current_profile == 0x10) && d->last_track_no <= 1) { ret = mmc_read_toc_fmt0(d); goto ex; } d->disc = burn_disc_create(); if (d->disc == NULL) {ret = -1; goto ex;} d->toc_entries = d->last_track_no + d->complete_sessions + d->incomplete_sessions; d->toc_entry = calloc(d->toc_entries, sizeof(struct burn_toc_entry)); if (d->toc_entry == NULL) {ret = -1; goto ex;} memset(d->toc_entry, 0,d->toc_entries * sizeof(struct burn_toc_entry)); #ifdef Libburn_disc_with_incomplete_sessioN for (i = 0; i < d->complete_sessions + d->incomplete_sessions; i++) { #else for (i = 0; i < d->complete_sessions; i++) { #endif session = burn_session_create(); if (session == NULL) {ret = -1; goto ex;} burn_disc_add_session(d->disc, session, BURN_POS_END); burn_session_free(session); } #ifdef Libburn_disc_with_incomplete_sessioN d->disc->incomplete_sessions = d->incomplete_sessions; #endif memset(size_data, 0, 4); memset(start_data, 0, 4); /* Entry Layout : session 1 track 1 entry 0 ... session 1 track N entry N-1 leadout 1 entry N session 2 track N+1 entry N+1 ... session 2 track M+1 entry M+1 leadout 2 entry M+2 session X track K entry (K-1)+(X-1) ... session X track i+1 entry i+(X-1) leadout X entry i+X */ for (i = 0; i < d->last_track_no; i++) { ret = mmc_read_track_info(d, i+1, buf, alloc_len); if (ret <= 0) goto ex; tdata = buf->data; session_number = (tdata[33] << 8) | tdata[3]; if (session_number <= 0) continue; if (session_number != prev_session && prev_session > 0) { /* leadout entry previous session */ entry = &(d->toc_entry[(i - 1) + prev_session]); lba = mmc_four_char_to_uint(start_data) + mmc_four_char_to_uint(size_data); mmc_uint_to_four_char(start_data, lba); mmc_uint_to_four_char(size_data, 0); mmc_uint_to_four_char(end_data, lba - 1); mmc_fake_toc_entry(entry, prev_session, 0xA2, size_data, start_data, end_data); entry->min= entry->sec= entry->frame= 0; d->disc->session[prev_session - 1]->leadout_entry = entry; } #ifdef Libburn_disc_with_incomplete_sessioN if (session_number > d->complete_sessions) { #else if (session_number > d->disc->sessions) { #endif if (i == d->last_track_no - 1) { /* ts A70212 : Last track field Free Blocks */ burn_drive_set_media_capacity_remaining(d, ((off_t) mmc_four_char_to_uint(tdata + 16)) * ((off_t) 2048)); d->media_lba_limit = 0; } #ifdef Libburn_disc_with_incomplete_sessioN if (session_number > d->disc->sessions ) continue; #else continue; #endif } entry = &(d->toc_entry[i + session_number - 1]); track = burn_track_create(); if (track == NULL) {ret = -1; goto ex;} burn_session_add_track( d->disc->session[session_number - 1], track, BURN_POS_END); track->entry = entry; burn_track_free(track); memcpy(size_data, tdata + 24, 4); memcpy(start_data, tdata + 8, 4); memcpy(end_data, tdata + 28, 4); mmc_fake_toc_entry(entry, session_number, i + 1, size_data, start_data, end_data); entry->track_status_bits = tdata[5] | (tdata[6] << 8) | (tdata[7] << 16); entry->extensions_valid |= 4; if (prev_session != session_number) d->disc->session[session_number - 1]->firsttrack = i+1; d->disc->session[session_number - 1]->lasttrack = i+1; prev_session = session_number; } if (prev_session > 0 && prev_session <= d->disc->sessions) { /* leadout entry of last session of closed disc */ entry = &(d->toc_entry[(d->last_track_no - 1) + prev_session]); lba = mmc_four_char_to_uint(start_data) + mmc_four_char_to_uint(size_data); mmc_uint_to_four_char(start_data, lba); mmc_uint_to_four_char(size_data, 0); mmc_uint_to_four_char(end_data, lba - 1); mmc_fake_toc_entry(entry, prev_session, 0xA2, size_data, start_data, end_data); entry->min= entry->sec= entry->frame= 0; d->disc->session[prev_session - 1]->leadout_entry = entry; } ret = 1; ex:; BURN_FREE_MEM(buf); return ret; } static int mmc_register_leadout(struct burn_drive *d, int *highest_leadout, int toc_idx) { int lba; lba = burn_msf_to_lba(d->toc_entry[toc_idx].pmin, d->toc_entry[toc_idx].psec, d->toc_entry[toc_idx].pframe); if (lba > *highest_leadout) *highest_leadout = lba; return 1; } static int mmc_read_toc_al(struct burn_drive *d, int *alloc_len) { /* read full toc, all sessions, in m/s/f form, 4k buffer */ /* ts A70201 : or fake a toc from track information */ struct burn_track *track; struct burn_session *session; struct buffer *buf = NULL; struct command *c = NULL; int dlen; int i, old_alloc_len, t_idx, ret, highest_leadout = -1; unsigned char *tdata; char *msg = NULL; if (*alloc_len < 4) {ret = 0; goto ex;} BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); BURN_ALLOC_MEM(msg, char, 321); if (!(d->current_profile == -1 || d->current_is_cd_profile)) { /* ts A70131 : MMC_GET_TOC uses Response Format 2 For DVD this fails with 5,24,00 */ /* mmc_read_toc_fmt0() uses Response Format 0: mmc5r03.pdf 6.26.3.2 which does not yield the same result with the same disc on different drives. */ /* ts A70201 : This uses the session count from 51h READ DISC INFORMATION and the track records from 52h READ TRACK INFORMATION. mmc_read_toc_fmt0() is used as fallback for dull DVD-ROM. */ mmc_fake_toc(d); if (d->status == BURN_DISC_UNREADY) d->status = BURN_DISC_FULL; {ret = 1; goto ex;} } /* ts A90823: SanDisk Cruzer U3 memory stick stalls on format 2. Format 0 seems to be more conservative with read-only drives. */ if (!((d->mdata->p2a_valid > 0 && d->mdata->cdrw_write) || d->current_profile != 0x08)) { ret = mmc_read_toc_fmt0(d); goto ex; } scsi_init_command(c, MMC_GET_TOC, sizeof(MMC_GET_TOC)); c->dxfer_len = *alloc_len; c->opcode[7] = (c->dxfer_len >> 8) & 0xff; c->opcode[8] = c->dxfer_len & 0xff; c->retry = 1; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); if (c->error) { /* ts A61020 : this snaps on non-blank DVD media */ /* ts A61106 : also snaps on CD with unclosed track/session */ /* Very unsure whether this old measure is ok. Obviously higher levels do not care about this. outdated info: DVD+RW burns go on after passing through here. d->busy = BURN_DRIVE_IDLE; */ libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010d, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "Could not inquire TOC", 0,0); d->status = BURN_DISC_UNSUITABLE; d->toc_entries = 0; /* Preferring memory leaks over fandangos */ d->toc_entry = calloc(1, sizeof(struct burn_toc_entry)); {ret = 0; goto ex;} } dlen = c->page->data[0] * 256 + c->page->data[1]; old_alloc_len = *alloc_len; *alloc_len = dlen + 2; if (old_alloc_len < 15) {ret = 1; goto ex;} if (dlen + 2 > old_alloc_len) dlen = old_alloc_len - 2; d->toc_entries = (dlen - 2) / 11; if (d->toc_entries < 1) {ret = 0; goto ex;} /* some drives fail this check. ts A61007 : if re-enabled then not via Assert. a ssert(((dlen - 2) % 11) == 0); */ /* ts A81202: plus number of sessions as reserve for leadout default */ d->toc_entry = calloc(d->toc_entries + (unsigned char) c->page->data[3], sizeof(struct burn_toc_entry)); if(d->toc_entry == NULL) /* ts A70825 */ {ret = 0; goto ex;} tdata = c->page->data + 4; d->disc = burn_disc_create(); if (d->disc == NULL) /* ts A70825 */ {ret = 0; goto ex;} for (i = 0; i < c->page->data[3]; i++) { session = burn_session_create(); if (session == NULL) /* ts A70825 */ {ret = 0; goto ex;} burn_disc_add_session(d->disc, session, BURN_POS_END); burn_session_free(session); } /* ts A61022 */ for (i = 0; i < d->toc_entries; i++, tdata += 11) { /* fprintf(stderr, "libburn_experimental: toc entry #%d : %d %d %d\n",i,tdata[8], tdata[9], tdata[10]); */ #ifdef Libburn_allow_first_hiddeN /* ts B00430 : this causes problems because the track has no entry. One would have to coordinate this with other parts of libburn. */ 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); } } #endif /* Libburn_allow_first_hiddeN */ if (tdata[0] <= 0 || tdata[0] > d->disc->sessions) tdata[0] = d->disc->sessions; if (tdata[3] < 100 && tdata[0] > 0) { 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]; /* ts B60305 */ mmc_register_leadout(d, &highest_leadout, i); } } /* ts A70131 : was (d->status != BURN_DISC_BLANK) */ if (d->status == BURN_DISC_UNREADY) d->status = BURN_DISC_FULL; toc_find_modes(d); /* ts A81202 ticket 146 : a drive reported a session with no leadout */ for (i = 0; i < d->disc->sessions; i++) { if (d->disc->session[i]->leadout_entry != NULL) continue; sprintf(msg, "Session %d of %d encountered without leadout", i + 1, d->disc->sessions); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020160, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); /* Produce default leadout entry from last track of session which will thus get its size set to 0 */; if (d->disc->session[i]->track != NULL && d->disc->session[i]->tracks > 0) { t_idx = d->toc_entries++; memcpy(d->toc_entry + t_idx, d->disc->session[i]->track[ d->disc->session[i]->tracks - 1]->entry, sizeof(struct burn_toc_entry)); d->toc_entry[t_idx].point = 0xA2; d->disc->session[i]->leadout_entry = d->toc_entry + t_idx; } else { burn_disc_remove_session(d->disc, d->disc->session[i]); sprintf(msg, "Empty session %d deleted. Now %d sessions.", i + 1, d->disc->sessions); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020161, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); i--; } } /* A80808 */ burn_disc_cd_toc_extensions(d, 0); /* ts B60304 Most drives report READ CAPACITY of TAO CD too high by 2 blocks. TOC format 2 always reports 2 blocks more than are readable. So here it is possible to check and mark as trusted. */ if (highest_leadout > 0 && d->media_read_capacity != 0x7fffffffffffffff && !d->mr_capacity_trusted) { if (highest_leadout - 3 == d->media_read_capacity) { d->mr_capacity_trusted = 1; libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, "Trusting READ CAPACITY by 2 extra blocks in TOC. Assuming TAO.", 0, 0); } } ret = 1; ex:; BURN_FREE_MEM(msg); BURN_FREE_MEM(c); BURN_FREE_MEM(buf); return ret; } void mmc_read_toc(struct burn_drive *d) { int alloc_len = 4, ret; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_read_toc") <= 0) return; ret = mmc_read_toc_al(d, &alloc_len); /* fprintf(stderr, "LIBBURN_DEBUG: 43h READ TOC alloc_len = %d , ret = %d\n", alloc_len, ret); */ if (alloc_len >= 15) ret = mmc_read_toc_al(d, &alloc_len); if (ret <= 0) return; } /* ts C40226 : long address reply version of mmc_read_multi_session_c1 */ /* man mkisofs , option -C : The first number is the sector number of the first sector in the last session of the disk that should be appended to. */ int mmc_read_msc1_long(struct burn_drive *d, int *trackno, off_t *start) { struct buffer *buf = NULL; struct command *c = NULL; unsigned char *tdata; int num_sessions, session_no, num_tracks, alloc_len = 12, ret; struct burn_disc *disc; struct burn_session **sessions; struct burn_track **tracks; struct burn_toc_entry toc_entry; BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_read_multi_session_c1") <= 0) {ret = 0; goto ex;} /* First try to evaluate the possibly loaded TOC before issuing a MMC command. This search obtains the first track of the last complete session which has a track. */ *trackno = 0; disc = burn_drive_get_disc(d); if (disc == NULL) goto inquire_drive; sessions = burn_disc_get_sessions(disc, &num_sessions); for (session_no = 0; session_no<num_sessions; session_no++) { tracks = burn_session_get_tracks(sessions[session_no], &num_tracks); if (tracks == NULL || num_tracks <= 0) continue; burn_track_get_entry(tracks[0], &toc_entry); if (toc_entry.extensions_valid & 1) { /* DVD extension valid */ if (toc_entry.extensions_valid & 8) { /* Long adr */ *start = toc_entry.long_start_lba; } else { *start = toc_entry.start_lba; } *trackno = (toc_entry.point_msb << 8)| toc_entry.point; } else { *start = burn_msf_to_lba(toc_entry.pmin, toc_entry.psec, toc_entry.pframe); *trackno = toc_entry.point; } } burn_disc_free(disc); if(*trackno > 0) {ret = 1; goto ex;} inquire_drive:; /* mmc5r03.pdf 6.26.3.3.3 states that with non-CD this would be a useless fake always starting at track 1, lba 0. My drives return useful data, though. MMC-3 states that DVD had no tracks. So maybe this mandatory fake is a forgotten legacy ? */ scsi_init_command(c, MMC_GET_MSINFO, sizeof(MMC_GET_MSINFO)); c->dxfer_len = alloc_len; c->opcode[7]= (c->dxfer_len >> 8) & 0xff; c->opcode[8]= c->dxfer_len & 0xff; c->retry = 1; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); if (c->error) {ret = 0; goto ex;} tdata = c->page->data + 4; *trackno = tdata[2]; *start = mmc_four_char_to_uint(tdata + 4); ret = 1; ex:; BURN_FREE_MEM(buf); BURN_FREE_MEM(c); return ret; } /* ts A70131 : This tries to get the start of the last complete session */ int mmc_read_multi_session_c1(struct burn_drive *d, int *trackno, int *start) { int ret; off_t num; ret = mmc_read_msc1_long(d, trackno, &num); if (ret <= 0) return ret; if(num >= 0x80000000) { *start = 0x7fffffff; return 0; } *start = num; return 1; } /* ts A61201 */ char *mmc_obtain_profile_name(int profile_number) { static char *texts[0x53] = {NULL}; int i, max_pno = 0x53; if (texts[0] == NULL) { for (i = 0; i<max_pno; i++) texts[i] = ""; /* mmc5r03c.pdf , Table 89, Spelling: guessed cdrecord style */ texts[0x01] = "Non-removable disk"; texts[0x02] = "Removable disk"; texts[0x03] = "MO erasable"; texts[0x04] = "Optical write once"; texts[0x05] = "AS-MO"; texts[0x08] = "CD-ROM"; texts[0x09] = "CD-R"; texts[0x0a] = "CD-RW"; texts[0x10] = "DVD-ROM"; texts[0x11] = "DVD-R sequential recording"; texts[0x12] = "DVD-RAM"; texts[0x13] = "DVD-RW restricted overwrite"; texts[0x14] = "DVD-RW sequential recording"; texts[0x15] = "DVD-R/DL sequential recording"; texts[0x16] = "DVD-R/DL layer jump recording"; texts[0x1a] = "DVD+RW"; texts[0x1b] = "DVD+R"; texts[0x2a] = "DVD+RW/DL"; texts[0x2b] = "DVD+R/DL"; texts[0x40] = "BD-ROM"; texts[0x41] = "BD-R sequential recording"; texts[0x42] = "BD-R random recording"; texts[0x43] = "BD-RE"; texts[0x50] = "HD-DVD-ROM"; texts[0x51] = "HD-DVD-R"; texts[0x52] = "HD-DVD-RAM"; } if (profile_number<0 || profile_number>=max_pno) return ""; return texts[profile_number]; } /* ts A90603 : to be used if the drive knows no GET CONFIGURATION */ static int mmc_guess_profile(struct burn_drive *d, int flag) { int cp; cp = 0; if (d->status == BURN_DISC_BLANK || d->status == BURN_DISC_APPENDABLE) { cp = 0x09; } else if (d->status == BURN_DISC_FULL) { cp = 0x08; } if (cp) if (d->erasable) cp = 0x0a; d->current_profile = cp; if (cp == 0) return 0; d->current_is_cd_profile = 1; d->current_is_supported_profile = 1; strcpy(d->current_profile_text, mmc_obtain_profile_name(cp)); return 1; } static int mmc_read_disc_info_al(struct burn_drive *d, int *alloc_len) { struct buffer *buf = NULL; unsigned char *data; struct command *c = NULL; char *msg = NULL; /* ts A70131 : had to move mmc_read_toc() to end of function */ int do_read_toc = 0, disc_status, len, old_alloc_len; int ret, number_of_sessions = -1; int key, asc, ascq; BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); /* ts A61020 */ d->start_lba = d->end_lba = -2000000000; d->erasable = 0; d->last_track_no = 1; /* ts B10730 */ d->sent_default_page_05 = 0; /* ts A70212 - A70215 */ d->media_capacity_remaining = 0; d->media_lba_limit = 0; /* ts A81210 */ d->media_read_capacity = 0x7fffffffffffffff; d->mr_capacity_trusted = -1; /* ts A61202 */ d->toc_entries = 0; if (d->status == BURN_DISC_EMPTY) {ret = 1; goto ex;} mmc_get_configuration(d); scsi_init_command(c, MMC_GET_DISC_INFO, sizeof(MMC_GET_DISC_INFO)); c->dxfer_len = *alloc_len; c->opcode[7]= (c->dxfer_len >> 8) & 0xff; c->opcode[8]= c->dxfer_len & 0xff; c->retry = 1; c->page = buf; c->page->sectors = 0; c->page->bytes = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); if (c->error) { spc_decode_sense(c->sense, 0, &key, &asc, &ascq); if (key == 5 && asc == 0x20 && ascq == 0) { /* ts B11031 : qemu -cdrom does not know 051h READ DISC INFORMATION */ ret = mmc_read_toc_fmt0(d); if (ret > 0) { /* >>> ??? anything more to be set ? */; mmc_read_capacity(d); *alloc_len = 0; goto ex; } } d->busy = BURN_DRIVE_IDLE; {ret = 0; goto ex;} } data = c->page->data; len = (data[0] << 8) | data[1]; old_alloc_len = *alloc_len; *alloc_len = len + 2; if (old_alloc_len < 34) {ret = 1; goto ex;} if (*alloc_len < 24) /* data[23] is the last mandatory byte here */ {ret = 0; goto ex;} if (len + 2 > old_alloc_len) len = old_alloc_len - 2; d->erasable = !!(data[2] & 16); /* ts A90908 */ d->disc_type = data[8]; d->disc_info_valid = 1; d->disc_id = mmc_four_char_to_int(data + 12); d->disc_info_valid |= (!!(data[7] & 128)) << 1; if (len + 2 > 31 && (data[7] & 64)) { memcpy(d->disc_bar_code, data + 24, 8); d->disc_bar_code[8] = 0; d->disc_info_valid |= 4; } if (len + 2 > 32 && (data[7] & 16)) { d->disc_app_code = data[32]; d->disc_info_valid |= 8; } if (data[7] & 32) d->disc_info_valid |= 16; if (data[2] & 16) d->disc_info_valid |= 32; disc_status = data[2] & 3; d->state_of_last_session = (data[2] >> 2) & 3; number_of_sessions = (data[9] << 8) | data[4]; if (d->current_profile == 0x10 || d->current_profile == 0x40) { /* DVD-ROM , BD-ROM */ disc_status = 2; /* always full and finalized */ d->erasable = 0; /* never erasable */ } #ifdef Libburn_support_bd_r_readonlY /* <<< For now: declaring BD-R read-only */ #ifndef Libburn_support_bd_plus_r_srM if (d->current_profile == 0x41) { /* BD-R seq as readonly dummy */ disc_status = 2; /* always full and finalized */ d->erasable = 0; /* never erasable */ } #endif if (d->current_profile == 0x42) { /* BD-R rnd */ disc_status = 2; /* always full and finalized */ d->erasable = 0; /* never erasable */ } #endif /* Libburn_support_bd_r_readonlY */ /* MMC-5 6.22.3.1.16: Last Session Lead-in Start Address bytes 16 to 19 Last Possible Lead-out Start Address bytes 20 to 23 MSF for CD, LBA else */ if(d->current_profile == 0x08 || d->current_profile == 0x09 || d->current_profile == 0x0a) { d->last_lead_in = burn_msf_to_lba(data[17], data[18], data[19]); d->last_lead_out = burn_msf_to_lba(data[21], data[22], data[23]); } else { d->last_lead_in = mmc_four_char_to_int(data + 16); d->last_lead_out = mmc_four_char_to_int(data + 20); } switch (disc_status) { case 0: regard_as_blank:; d->toc_entries = 0; /* fprintf(stderr, "libburn_experimental: start_lba = %d (%d %d %d) , end_lba = %d (%d %d %d)\n", d->start_lba, data[17], data[18], data[19], d->end_lba, data[21], data[22], data[23]); */ d->status = BURN_DISC_BLANK; d->start_lba = d->last_lead_in; d->end_lba = d->last_lead_out; break; case 1: case 2: if (disc_status == 2) d->status = BURN_DISC_FULL; else d->status = BURN_DISC_APPENDABLE; /* ts A81210 */ ret = mmc_read_capacity(d); /* Freshly formatted, unwritten BD-R pretend to be appendable but in our model they need to be regarded as blank. Criterion: BD-R seq, read capacity known and 0, declared appendable, single empty session */ if (d->current_profile == 0x41 && d->status == BURN_DISC_APPENDABLE && ret > 0 && d->media_read_capacity == 0 && d->state_of_last_session == 0 && number_of_sessions == 1) goto regard_as_blank; if (d->current_profile == 0x41 && d->status == BURN_DISC_APPENDABLE && d->state_of_last_session == 1) { /* ??? apply this test to other media types ? */ libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020169, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, "Last session on media is still open.", 0, 0); } do_read_toc = 1; break; case 3: /* ts A91009 : DVD-RAM has disc status "others" */ mmc_read_capacity(d); break; } /* ts A90603 : An MMC-1 drive might not know the media type yet */ if (d->current_is_guessed_profile && d->current_profile == 0) mmc_guess_profile(d, 0); if ((d->current_profile != 0 || d->status != BURN_DISC_UNREADY) && ! d->current_is_supported_profile) { if (!(d->silent_on_scsi_error == 1 || d->silent_on_scsi_error == 2)) { msg = calloc(1, 160); if (msg != NULL) { sprintf(msg, "Unsuitable media detected. Profile %4.4Xh %s", d->current_profile, d->current_profile_text); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002011e, d->silent_on_scsi_error == 3 ? LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); free(msg); } } d->status = BURN_DISC_UNSUITABLE; {ret = 0; goto ex;} } /* ts A61217 : growisofs performs OPC if (data[0]<<8)|data[1]<=32 which indicates no OPC entries are attached to the reply from the drive. ts A91104 : Actually growisofs performs OPC only on DVD-R[W]. */ d->num_opc_tables = 0; if(((data[0] << 8) | data[1]) > 32) /* i.e. > 34 bytes are available */ d->num_opc_tables = data[33]; /* ts A61219 : mmc5r03c.pdf 6.22.3.1.13 BG Format Status 0=blank (not yet started) 1=started but neither running nor complete 2=in progress 3=completed */ d->bg_format_status = data[7] & 3; /* Preliminarily declare blank: ts A61219 : DVD+RW (is not bg_format_status==0 "blank") ts A61229 : same for DVD-RW Restricted overwrite ts A70112 : same for DVD-RAM */ if (d->current_profile == 0x1a || d->current_profile == 0x13 || d->current_profile == 0x12 || d->current_profile == 0x43) d->status = BURN_DISC_BLANK; #ifdef Libburn_disc_with_incomplete_sessioN /* ts B30112 : number of open sessions */ d->incomplete_sessions = 0; #endif if (d->status == BURN_DISC_BLANK) { d->last_track_no = 1; /* The "incomplete track" */ d->complete_sessions = 0; } else { /* ts A70131 : number of closed sessions */ d->complete_sessions = number_of_sessions; /* mmc5r03c.pdf 6.22.3.1.3 State of Last Session: 3=complete */ if (d->state_of_last_session != 3 && d->complete_sessions >= 1) { d->complete_sessions--; #ifdef Libburn_disc_with_incomplete_sessioN d->incomplete_sessions++; #endif } /* ts A70129 : mmc5r03c.pdf 6.22.3.1.7 This includes the "incomplete track" if the disk is appendable. I.e number of complete tracks + 1. */ d->last_track_no = (data[11] << 8) | data[6]; } if (d->current_profile != 0x0a && d->current_profile != 0x13 && d->current_profile != 0x14 && d->status != BURN_DISC_FULL) d->erasable = 0; /* stay in sync with burn_disc_erase() */ if (do_read_toc) mmc_read_toc(d); ret = 1; ex: BURN_FREE_MEM(buf); BURN_FREE_MEM(c); return ret; } void mmc_read_disc_info(struct burn_drive *d) { int alloc_len = 34, ret; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_read_disc_info") <= 0) return; ret = mmc_read_disc_info_al(d, &alloc_len); /* fprintf(stderr,"LIBBURN_DEBUG: 51h alloc_len = %d , ret = %d\n", alloc_len, ret); */ if (ret <= 0) return; /* for now there is no need to inquire the variable length part */ } /* @param flag bit= do not allocate text_packs */ static int mmc_get_leadin_text_al(struct burn_drive *d, unsigned char **text_packs, int *alloc_len, int flag) { struct buffer *buf = NULL; struct command *c = NULL; unsigned char *data; int ret, data_length; *text_packs = NULL; BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); scsi_init_command(c, MMC_GET_LEADTEXT, sizeof(MMC_GET_LEADTEXT)); c->dxfer_len = *alloc_len; c->opcode[7]= (c->dxfer_len >> 8) & 0xff; c->opcode[8]= c->dxfer_len & 0xff; c->retry = 1; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); if (c->error) {ret = 0; goto ex;} data = c->page->data; data_length = (data[0] << 8) + data[1]; *alloc_len = data_length + 2; if (*alloc_len >= 22 && !(flag & 1)) { BURN_ALLOC_MEM(*text_packs, unsigned char, *alloc_len - 4); memcpy(*text_packs, data + 4, *alloc_len - 4); } ret = 1; ex:; BURN_FREE_MEM(c); BURN_FREE_MEM(buf); return ret; } /* ts B11201 */ /* Read the CD-TEXT data from the Lead-in of an Audio CD */ int mmc_get_leadin_text(struct burn_drive *d, unsigned char **text_packs, int *num_packs, int flag) { int alloc_len = 4, ret; *num_packs = 0; if (mmc_function_spy(d, "mmc_get_leadin_text") <= 0) return -1; ret = mmc_get_leadin_text_al(d, text_packs, &alloc_len, 1); if (ret <= 0 || alloc_len < 22) return (ret > 0 ? 0 : ret); ret = mmc_get_leadin_text_al(d, text_packs, &alloc_len, 0); if (ret <= 0 || alloc_len < 22) { if (*text_packs != NULL) free(*text_packs); *text_packs = NULL; return (ret > 0 ? 0 : ret); } *num_packs = (alloc_len - 4) / 18; return ret; } void mmc_read_atip(struct burn_drive *d) { struct buffer *buf = NULL; struct command *c = NULL; int alloc_len = 28; /* ts A61021 */ unsigned char *data; /* Speed values from A1: With 4 cdrecord tells "10" or "8" where MMC-1 says "8". cdrecord "8" appear on 4xCD-RW and thus seem to be quite invalid. My CD-R (>=24 speed) tell no A1. The higher non-MMC-1 values are hearsay. */ /* 0, 2, 4, 6, 10, -, 16, -, */ static int speed_value[16]= { 0, 353, 706, 1059, 1764, -5, 2824, -7, 4234, 5646, 7056, 8468, -12, -13, -14, -15}; /* 24, 32, 40, 48, -, -, -, - */ BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); BURN_ALLOC_MEM_VOID(c, struct command, 1); mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_read_atip") <= 0) goto ex; scsi_init_command(c, MMC_GET_ATIP, sizeof(MMC_GET_ATIP)); c->dxfer_len = alloc_len; c->opcode[7] = (c->dxfer_len >> 8) & 0xff; c->opcode[8] = c->dxfer_len & 0xff; c->retry = 1; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); /* ts B00501 : now caring for error */ if (c->error) { d->erasable = 0; d->start_lba = 0; d->end_lba = 0; goto ex; } /* ts A61021 */ data = c->page->data; d->erasable = !!(data[6]&64); d->start_lba = burn_msf_to_lba(data[8],data[9],data[10]); d->end_lba = burn_msf_to_lba(data[12],data[13],data[14]); /* ts B21124 : LITE-ON LTR-48125S returns crap on pressed audio CD and CD-ROM */ if (d->start_lba >= d->end_lba) { d->start_lba = 0; d->end_lba = 0; } if (data[6]&4) { if (speed_value[(data[16]>>4)&7] > 0) { d->mdata->min_write_speed = speed_value[(data[16]>>4)&7]; if (speed_value[(data[16])&15] <= 0) d->mdata->max_write_speed = speed_value[(data[16]>>4)&7]; } if (speed_value[(data[16])&15] > 0) { d->mdata->max_write_speed = speed_value[(data[16])&15]; if (speed_value[(data[16]>>4)&7] <= 0) d->mdata->min_write_speed = speed_value[(data[16])&15]; } } #ifdef Burn_mmc_be_verbous_about_atiP { int i; fprintf(stderr,"libburn_experimental: Returned ATIP Data\n"); for(i= 0; i<28; i++) fprintf(stderr,"%3.3d (0x%2.2x)%s", data[i],data[i],(((i + 1) % 5) ? " " : "\n")); fprintf(stderr,"\n"); fprintf(stderr, "libburn_experimental: Indicative Target Writing Power= %d\n", (data[4]>>4)&7); fprintf(stderr, "libburn_experimental: Reference speed= %d ->%d\n", data[4]&7, speed_value[data[4]&7]); fprintf(stderr, "libburn_experimental: Is %sunrestricted\n", (data[5]&64?"":"not ")); fprintf(stderr, "libburn_experimental: Is %serasable, sub-type %d\n", (data[6]&64?"":"not "),(data[6]>>3)&3); fprintf(stderr, "libburn_experimental: lead in: %d (%-2.2d:%-2.2d/%-2.2d)\n", burn_msf_to_lba(data[8],data[9],data[10]), data[8],data[9],data[10]); fprintf(stderr, "libburn_experimental: lead out: %d (%-2.2d:%-2.2d/%-2.2d)\n", burn_msf_to_lba(data[12],data[13],data[14]), data[12],data[13],data[14]); if(data[6]&4) fprintf(stderr, "libburn_experimental: A1 speed low %d speed high %d\n", speed_value[(data[16]>>4)&7], speed_value[(data[16])&7]); if(data[6]&2) fprintf(stderr, "libburn_experimental: A2 speed low %d speed high %d\n", speed_value[(data[20]>>4)&7], speed_value[(data[20])&7]); if(data[6]&1) fprintf(stderr, "libburn_experimental: A3 speed low %d speed high %d\n", speed_value[(data[24]>>4)&7], speed_value[(data[24])&7]); } #endif /* Burn_mmc_be_verbous_about_atiP */ /* ts A61020 http://www.t10.org/ftp/t10/drafts/mmc/mmc-r10a.pdf , table 77 : 0 ATIP Data Length MSB 1 ATIP Data Length LSB 2 Reserved 3 Reserved 4 bit7=1, bit4-6="Indicative Target Writing Power", bit3=reserved , bit0-2="Reference speed" 5 bit7=0, bit6="URU" , bit0-5=reserved 6 bit7=1, bit6="Disc Type", bit3-4="Disc Sub-Type", bit2="A1", bit1="A2", bit0="A3" 7 reserved 8 ATIP Start Time of lead-in (Min) 9 ATIP Start Time of lead-in (Sec) 10 ATIP Start Time of lead-in (Frame) 11 reserved 12 ATIP Last Possible Start Time of lead-out (Min) 13 ATIP Last Possible Start Time of lead-out (Sec) 14 ATIP Last Possible Start Time of lead-out (Frame) 15 reserved 16 bit7=0, bit4-6="Lowest Usable CLV Recording speed" bit0-3="Highest Usable CLV Recording speed" 17 bit7=0, bit4-6="Power Multiplication Factor p", bit1-3="Target y value of the Modulation/Power function", bit0=reserved 18 bit7=1, bit4-6="Recommended Erase/Write Power Ratio (P(inf)/W(inf))" bit0-3=reserved 19 reserved 20-22 A2 Values 23 reserved 24-26 A3 Values 27 reserved Disc Type - zero indicates CD-R media; one indicates CD-RW media. Disc Sub-Type - shall be set to zero. A1 - when set to one, indicates that bytes 16-18 are valid. Lowest Usable CLV Recording Speed 000b Reserved 001b 2X 010b - 111b Reserved Highest CLV Recording Speeds 000b Reserved 001b 2X 010b 4X 011b 6X 100b 8X 101b - 111b Reserved MMC-3 seems to recommend MODE SENSE (5Ah) page 2Ah rather than A1, A2, A3. This page is loaded in libburn function spc_sense_caps() . Speed is given in kbytes/sec there. But i suspect this to be independent of media. So one would habe to associate the speed descriptor blocks with the ATIP media characteristics ? How ? */ ex:; BURN_FREE_MEM(buf); BURN_FREE_MEM(c); } int mmc_eval_read_error(struct burn_drive *d, struct command *c, char *what, int start_m, int start_s, int start_f, int end_m, int end_s, int end_f, int flag) { char *msg = NULL; int key, asc, ascq, silent; if (!c->error) return 0; msg = calloc(1, 256); if (msg != NULL) { if (start_s < 0 || start_f < 0 || end_s < 0 || end_f < 0) { sprintf(msg, "SCSI error on %s(%d,%d): ", what, start_m, end_m); } else { sprintf(msg, "SCSI error on %s(%dm%ds%df,%dm%ds%df): ", what, start_m, start_s, start_f, end_m, end_s, end_f); } scsi_error_msg(d, c->sense, 14, msg + strlen(msg), &key, &asc, &ascq); silent = (d->silent_on_scsi_error == 1); if (key == 5 && asc == 0x64 && ascq == 0x0) { d->had_particular_error |= 1; if (d->silent_on_scsi_error == 2) silent = 1; } if(!silent) libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020144, d->silent_on_scsi_error == 3 ? LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); free(msg); } return BE_CANCELLED; } /* ts B21119 : Derived from older mmc_read_sectors() @param flag bit0= set DAP bit (also with o->dap_bit) */ int mmc_read_cd_msf(struct burn_drive *d, int start_m, int start_s, int start_f, int end_m, int end_s, int end_f, int sec_type, int main_ch, const struct burn_read_opts *o, struct buffer *buf, int flag) { int req, ret, dap_bit; int subcodes_audio = 0, subcodes_data = 0; struct command *c; #ifdef Libburn_mmc_report_recovereD int report_recovered_errors = 0; #endif c = &(d->casual_command); mmc_start_if_needed(d, 0); if (mmc_function_spy(d, "mmc_read_cd_msf") <= 0) return -1; dap_bit = flag & 1; if (o != NULL) { subcodes_audio = o->subcodes_audio; subcodes_data = o->subcodes_data; dap_bit |= o->dap_bit; #ifdef Libburn_mmc_report_recovereD report_recovered_errors = o->report_recovered_errors; #endif } scsi_init_command(c, MMC_READ_CD_MSF, sizeof(MMC_READ_CD_MSF)); c->retry = 1; c->opcode[1] = ((sec_type & 7) << 2) | ((!!dap_bit) << 1); c->opcode[3] = start_m; c->opcode[4] = start_s; c->opcode[5] = start_f; c->opcode[6] = end_m; c->opcode[7] = end_s; c->opcode[8] = end_f; req = main_ch & 0xf8; #ifdef Libburn_mmc_report_recovereD /* ts A61106 : LG GSA-4082B dislikes this. key=5h asc=24h ascq=00h */ if (d->busy == BURN_DRIVE_GRABBING || report_recovered_errors) req |= 2; #endif /* Libburn_mmc_report_recovereD */ c->opcode[9] = req; c->opcode[10] = 0; /* always read the subcode, throw it away later, since we don't know what we're really reading */ /* >>> ts B21125 : This is very obscure: MMC-3 has sub channel selection 001b as "RAW" MMC-5 does neither mention 001b nor "RAW". And why should a non-grabbed drive get here ? */ if (d->busy == BURN_DRIVE_GRABBING || subcodes_audio || subcodes_data) c->opcode[10] = 1; /* <<< ts B21125 : test with sub channel selection 100b no data, only sub channel c->opcode[9] = 0; c->opcode[10] = 4; Did not help either with reading before LBA -150 */ /* <<< ts B21125 : test with sub channel selection 001b and no user data c->opcode[9] = 0; c->opcode[10] = 1; */ c->page = buf; c->dir = FROM_DRIVE; d->issue_command(d, c); ret = mmc_eval_read_error(d, c, "read_cd_msf", start_m, start_s, start_f, end_m, end_s, end_f, 0); return ret; } /* ts B21119 : Derived from older mmc_read_sectors() @param flag bit0= set DAP bit (also with o->dap_bit) */ int mmc_read_cd(struct burn_drive *d, int start, int len, int sec_type, int main_ch, const struct burn_read_opts *o, struct buffer *buf, int flag) { int temp, req, ret, dap_bit, len_shift; int subcodes_audio = 0, subcodes_data = 0; struct command *c; #ifdef Libburn_mmc_report_recovereD int report_recovered_errors = 0; #endif /* # define Libburn_read_cd_by_msF 1 */ #ifdef Libburn_read_cd_by_msF int start_m, start_s, start_f, end_m, end_s, end_f; burn_lba_to_msf(start, &start_m, &start_s, &start_f); burn_lba_to_msf(start + len, &end_m, &end_s, &end_f); ret = mmc_read_cd_msf(d, start_m, start_s, start_f, end_m, end_s, end_f, sec_type, main_ch, o, buf, flag); return ret; #endif /* Libburn_read_cd_by_msF */ c = &(d->casual_command); mmc_start_if_needed(d, 0); if (mmc_function_spy(d, "mmc_read_cd") <= 0) return -1; dap_bit = flag & 1; if (o != NULL) { subcodes_audio = o->subcodes_audio; subcodes_data = o->subcodes_data; dap_bit |= o->dap_bit; #ifdef Libburn_mmc_report_recovereD report_recovered_errors = o->report_recovered_errors; #endif } scsi_init_command(c, MMC_READ_CD, sizeof(MMC_READ_CD)); /* (this was never tested with anything but sec_type = 1 = CD-DA) */ if(sec_type == 1 && main_ch == 0x10) c->dxfer_len = len * 2352; c->retry = 1; c->opcode[1] = ((sec_type & 7) << 2) | ((!!dap_bit) << 1); 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; len_shift = len; c->opcode[8] = len_shift & 0xFF; len_shift >>= 8; c->opcode[7] = len_shift & 0xFF; len_shift >>= 8; c->opcode[6] = len_shift & 0xFF; req = main_ch & 0xf8; #ifdef Libburn_mmc_report_recovereD /* ts A61106 : LG GSA-4082B dislikes this. key=5h asc=24h ascq=00h */ if (d->busy == BURN_DRIVE_GRABBING || report_recovered_errors) req |= 2; #endif /* Libburn_mmc_report_recovereD */ c->opcode[9] = req; c->opcode[10] = 0; /* always read the subcode, throw it away later, since we don't know what we're really reading */ /* >>> ts B21125 : This is very obscure: MMC-3 has sub channel selection 001b as "RAW" MMC-5 does neither mention 001b nor "RAW". And why should a non-grabbed drive get here ? */ if (d->busy == BURN_DRIVE_GRABBING || subcodes_audio || subcodes_data) c->opcode[10] = 1; /* <<< ts B21125 : test with sub channel selection 100b c->opcode[10] = 4; */ /* <<< ts B21125 : test with sub channel selection 001b and no user data c->opcode[9] = 0; c->opcode[10] = 1; */ c->page = buf; c->dir = FROM_DRIVE; d->issue_command(d, c); ret = mmc_eval_read_error(d, c, "read_cd", start, -1, -1, len, -1, -1, 0); return ret; } void mmc_erase(struct burn_drive *d, int fast) { struct command *c; c = &(d->casual_command); mmc_start_if_needed(d, 0); if (mmc_function_spy(d, "mmc_erase") <= 0) return; scsi_init_command(c, MMC_BLANK, sizeof(MMC_BLANK)); if (!d->do_no_immed) c->opcode[1] = 16; /* IMMED set to 1 */ c->opcode[1] |= !!fast; c->retry = 1; c->page = NULL; c->dir = NO_TRANSFER; if (d->do_no_immed) c->timeout = Libburn_mmc_blank_noim_timeouT; else c->timeout = Libburn_mmc_blank_timeouT; d->issue_command(d, c); if (c->error) { d->cancel = 1; scsi_notify_error(d, c, c->sense, 14, 2); } } void mmc_read_lead_in(struct burn_drive *d, struct buffer *buf) { struct command *c; c = &(d->casual_command); mmc_start_if_needed(d, 0); if (mmc_function_spy(d, "mmc_read_lead_in") <= 0) return; scsi_init_command(c, MMC_READ_CD, sizeof(MMC_READ_CD)); c->retry = 1; 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; c = &(d->casual_command); mmc_start_if_needed(d, 0); if (mmc_function_spy(d, "mmc_perform_opc") <= 0) return; scsi_init_command(c, MMC_SEND_OPC, sizeof(MMC_SEND_OPC)); c->retry = 1; c->opcode[1] = 1; c->page = NULL; c->dir = NO_TRANSFER; c->timeout = Libburn_mmc_opc_timeouT; d->issue_command(d, c); } /* ts A61221 : Learned much from dvd+rw-tools-7.0 set_speed_B6h() but then made own experiments on base of mmc5r03c.pdf 6.8.3 and 6.39 in the hope to achieve a leaner solution ts A70712 : That leaner solution does not suffice for my LG GSA-4082B. Meanwhile there is a speed descriptor list anyway. */ int mmc_set_streaming(struct burn_drive *d, int r_speed, int w_speed, int end_lba) { struct buffer *buf = NULL; struct command *c = NULL; int b, eff_end_lba, ret; char *msg = NULL; unsigned char *pd; int key, asc, ascq; BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); BURN_ALLOC_MEM(msg, char, 256); mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_set_streaming") <= 0) {ret = 0; goto ex;} scsi_init_command(c, MMC_SET_STREAMING, sizeof(MMC_SET_STREAMING)); c->retry = 1; c->page = buf; c->page->bytes = 28; c->opcode[9] = (c->page->bytes >> 8) & 0xff; c->opcode[10] = c->page->bytes & 0xff; c->page->sectors = 0; c->dir = TO_DRIVE; memset(c->page->data, 0, c->page->bytes); pd = c->page->data; pd[0] = 0; /* WRC=0 (Default Rotation Control), RDD=Exact=RA=0 */ if (d->set_streaming_exact_bit) pd[0] |= 2; /* Exact= 1 */ if (w_speed == 0) w_speed = 0x10000000; /* ~ 2 TB/s */ else if (w_speed < 0) w_speed = 177; /* 1x CD */ if (r_speed == 0) r_speed = 0x10000000; /* ~ 2 TB/s */ else if (r_speed < 0) r_speed = 177; /* 1x CD */ if (end_lba == 0) { /* Default computed from 4.7e9 */ eff_end_lba = 2294921 - 1; if (d->mdata->max_end_lba > 0) eff_end_lba = d->mdata->max_end_lba - 1; } else eff_end_lba = end_lba; sprintf(msg, "mmc_set_streaming: end_lba=%d , r=%d , w=%d , exact=%d", eff_end_lba, r_speed, w_speed, !!(pd[0] & 2)); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); /* start_lba is 0 , 1000 = 1 second as base time for data rate */ for (b = 0; b < 4 ; b++) { pd[8+b] = (eff_end_lba >> (24 - 8 * b)) & 0xff; pd[12+b] = (r_speed >> (24 - 8 * b)) & 0xff; pd[16+b] = (1000 >> (24 - 8 * b)) & 0xff; pd[20+b] = (w_speed >> (24 - 8 * b)) & 0xff; pd[24+b] = (1000 >> (24 - 8 * b)) & 0xff; } /* <<< fprintf(stderr,"LIBBURN_EXPERIMENTAL : B6h Performance descriptor:\n"); for (b = 0; b < 28 ; b++) fprintf(stderr, "%2.2X%c", pd[b], ((b+1)%4 ? ' ' : '\n')); */ d->issue_command(d, c); if (c->error) { spc_decode_sense(c->sense, 0, &key, &asc, &ascq); if (key != 0 && d->silent_on_scsi_error != 1 && d->silent_on_scsi_error != 2) { sprintf(msg, "SCSI error on set_streaming(%d): ", w_speed); scsi_error_msg(d, c->sense, 14, msg + strlen(msg), &key, &asc, &ascq); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020124, d->silent_on_scsi_error == 3 ? LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } if (key != 0) d->set_streaming_err = 1; {ret = 0; goto ex;} } ret = 1; ex:; BURN_FREE_MEM(msg); BURN_FREE_MEM(c); BURN_FREE_MEM(buf); return ret; } void mmc_set_speed(struct burn_drive *d, int r, int w) { struct command *c; int ret, end_lba = 0, get_max, get_min; struct burn_speed_descriptor *best_sd = NULL; c = &(d->casual_command); mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_set_speed") <= 0) return; if (r <= 0 || w <= 0) { /* ts A70712 : now searching for best speed descriptor */ /* ts B31030 : keeping max read speed from sinking too low */ if (r <= 0) { get_max = (r == 0); get_min = (r == -1); burn_drive_get_best_speed(d, r, &best_sd, 1 | 2); if (best_sd != NULL) { r = best_sd->read_speed; end_lba = best_sd->end_lba; } if (get_max) { if (d->current_is_cd_profile) { if (r < Libburn_cd_max_read_speeD) r = Libburn_cd_max_read_speeD; } else if (d->current_profile >= 0x10 && d->current_profile <= 0x2f) { if (r < Libburn_dvd_max_read_speeD) r = Libburn_dvd_max_read_speeD; } else if (d->current_profile >= 0x40 && d->current_profile <= 0x43) { if (r < Libburn_bd_max_read_speeD) r = Libburn_bd_max_read_speeD; } } else if(get_min) { if (d->current_is_cd_profile) { if (r > Libburn_cd_min_read_speeD) r = Libburn_cd_min_read_speeD; } else if (d->current_profile >= 0x10 && d->current_profile <= 0x2f) { if (r > Libburn_dvd_min_read_speeD) r = Libburn_dvd_min_read_speeD; } else if (d->current_profile >= 0x40 && d->current_profile <= 0x43) { if (r > Libburn_bd_min_read_speeD) r = Libburn_bd_min_read_speeD; } } } if (w <= 0) { burn_drive_get_best_speed(d, w, &best_sd, 2); if (best_sd != NULL) { w = best_sd->write_speed; d->nominal_write_speed = w; if (end_lba < best_sd->end_lba) end_lba = best_sd->end_lba; } } } /* A70711 */ d->nominal_write_speed = w; /* ts A61221 : try to set DVD speed via command B6h */ if (strstr(d->current_profile_text, "DVD") == d->current_profile_text || strstr(d->current_profile_text, "BD") == d->current_profile_text || d->set_streaming_exact_bit) { ret = mmc_set_streaming(d, r, w, end_lba); if (ret != 0) return; /* success or really fatal failure */ } /* ts A61112 : MMC standards prescribe FFFFh as max speed. But libburn.h prescribes 0. ts A70715 : <0 now means minimum speed */ if (r == 0 || r > 0xffff) r = 0xffff; else if (r < 0) r = 177; /* 1x CD */ if (w == 0 || w > 0xffff) w = 0xffff; else if (w < 0) w = 177; /* 1x CD */ scsi_init_command(c, MMC_SET_SPEED, sizeof(MMC_SET_SPEED)); c->retry = 1; 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); } /* ts A61201 : found in unfunctional state */ static int mmc_get_configuration_al(struct burn_drive *d, int *alloc_len) { struct buffer *buf = NULL; int len, cp, descr_len = 0, feature_code, only_current = 1, i; int old_alloc_len, key, asc, ascq, ret; int feature_is_current; unsigned char *descr, *prf, *up_to, *prf_end; struct command *c = NULL; int phys_if_std = 0; char *phys_name = ""; struct burn_feature_descr *recent_feature = NULL, *new_feature; char *msg = NULL; /* Enable this to get loud and repeated reports about the feature set : # define Libburn_print_feature_descriptorS 1 */ #ifdef Libburn_print_feature_descriptorS int prf_number; only_current = 0; #endif if (*alloc_len < 8) {ret = 0; goto ex;} BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); d->current_profile = 0; d->current_profile_text[0] = 0; d->current_is_cd_profile = 0; d->current_is_supported_profile = 0; d->current_is_guessed_profile = 0; d->num_profiles = 0; if (d->features != NULL) burn_feature_descr_free(&(d->features), 0); d->current_has_feat21h = 0; d->current_feat21h_link_size = -1; d->current_feat23h_byte4 = 0; d->current_feat23h_byte8 = 0; d->current_feat2fh_byte4 = -1; scsi_init_command(c, MMC_GET_CONFIGURATION, sizeof(MMC_GET_CONFIGURATION)); c->dxfer_len= *alloc_len; c->retry = 1; c->opcode[7] = (c->dxfer_len >> 8) & 0xff; c->opcode[8] = c->dxfer_len & 0xff; c->page = buf; c->page->sectors = 0; c->page->bytes = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); #ifdef Libisofs_simulate_old_mmc1_drivE c->error = 1; c->sense[0] = 0x70; /* Fixed format sense data */ c->sense[2] = 0x5; c->sense[12] = 0x20; c->sense[13] = 0x0; #endif /* Libisofs_simulate_old_mmc1_drivE */ if (c->error) { /* ts A90603 : MMC-1 drive do not know 46h GET CONFIGURATION */ spc_decode_sense(c->sense, 0, &key, &asc, &ascq); if (key == 0x5 && asc == 0x20 && ascq == 0x0) { d->current_is_guessed_profile = 1; /* Will yield a non-zero profile only after mmc_read_disc_info_al() was called */ mmc_guess_profile(d, 0); } {ret = 0; goto ex;} } old_alloc_len = *alloc_len; *alloc_len = len = mmc_four_char_to_int(c->page->data) + 4; if (len > old_alloc_len) len = old_alloc_len; if (len < 8) {ret = 0; goto ex;} if (len > 4096) { /* MMC-5 6.6.2.1, Note 11: The maximum is less than 1 KB */ BURN_ALLOC_MEM(msg, char, 256); sprintf(msg, "Implausible length announcement from SCSI command GET CONFIGURATION: %d", *alloc_len); libdax_msgs_submit(libdax_messenger, d->global_index, 0x000201a9, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); ret = 0; goto ex; } cp = (c->page->data[6]<<8) | c->page->data[7]; #ifdef Libburn_rom_as_profilE if (cp == 0x08 || cp == 0x10 || cp==0x40) cp = Libburn_rom_as_profilE; #endif /* Libburn_rom_as_profilE */ d->current_profile = cp; strcpy(d->current_profile_text, mmc_obtain_profile_name(cp)); /* Read-only supported media */ if (cp == 0x08) /* CD-ROM */ d->current_is_supported_profile = d->current_is_cd_profile = 1; if (cp == 0x10) /* DVD-ROM */ d->current_is_supported_profile = 1; if (cp == 0x40) /* BD-ROM */ d->current_is_supported_profile = 1; #ifdef Libburn_support_bd_r_readonlY #ifndef Libburn_support_bd_plus_r_srM if (cp == 0x41) /* BD-R sequential (here as read-only dummy) */ d->current_is_supported_profile = 1; #endif if (cp == 0x42) /* BD-R random recording */ d->current_is_supported_profile = 1; #endif /* Write supported media (they get declared suitable in burn_disc_get_multi_caps) */ if (cp == 0x09 || cp == 0x0a) d->current_is_supported_profile = d->current_is_cd_profile = 1; #ifdef Libburn_support_dvd_plus_rW if (cp == 0x1a) d->current_is_supported_profile = 1; #endif #ifdef Libburn_support_dvd_minusrw_overW if (cp == 0x13) d->current_is_supported_profile = 1; #endif #ifdef Libburn_support_dvd_raM if (cp == 0x12 || cp == 0x43) { /* DVD-RAM , BD-RE */ d->current_is_supported_profile = 1; #ifdef Libburn_dvd_ram_as_bd_rE cp = d->current_profile = 0x43; strcpy(d->current_profile_text, mmc_obtain_profile_name(cp)); #endif } #endif #ifdef Libburn_support_dvd_r_seQ if (cp == 0x11 || cp == 0x14) /* DVD-R, DVD-RW */ d->current_is_supported_profile = 1; if (cp == 0x15) /* DVD-R/DL */ d->current_is_supported_profile = 1; #endif #ifdef Libburn_support_dvd_plus_R if (cp == 0x1b || cp == 0x2b) /* DVD+R , DVD+R/DL */ d->current_is_supported_profile = 1; #endif #ifdef Libburn_support_bd_plus_r_srM if (cp == 0x41) /* BD-R SRM */ d->current_is_supported_profile = 1; #endif /* ts A70127 : Interpret list of profile and feature descriptors. see mmc5r03c.pdf 5.2 */ up_to = c->page->data + len; #ifdef Libburn_print_feature_descriptorS fprintf(stderr, "-----------------------------------------------------------------\n"); fprintf(stderr, "LIBBURN_EXPERIMENTAL : feature list length = %d , shown = %d\n", len, (int) (up_to - c->page->data)); #endif /* Libburn_print_feature_descriptorS */ for (descr = c->page->data + 8; descr + 3 < up_to; descr += descr_len){ descr_len = 4 + descr[3]; feature_code = (descr[0] << 8) | descr[1]; feature_is_current = descr[2] & 1; ret = burn_feature_descr_new(&new_feature, descr, up_to - descr, 0); if (ret > 0) { if (d->features == NULL) d->features = new_feature; else recent_feature->next = new_feature; recent_feature = new_feature; } if (only_current && !feature_is_current) continue; #ifdef Libburn_print_feature_descriptorS fprintf(stderr, "LIBBURN_EXPERIMENTAL : %s feature %4.4Xh :", (descr[2] & 1) ? "+" : "-", feature_code); if (feature_code != 0x00) for (i = 2; i < descr_len; i++) fprintf(stderr, " %2.2X", descr[i]); fprintf(stderr, "\n"); #endif /* Libburn_print_feature_descriptorS */ if (feature_code == 0x0) { prf_end = descr + 4 + descr[3]; d->num_profiles = descr[3] / 4; if (d->num_profiles > 64) d->num_profiles = 64; if (d->num_profiles > 0) memcpy(d->all_profiles, descr + 4, d->num_profiles * 4); for (prf = descr + 4; prf + 2 < prf_end; prf += 4) { #ifdef Libburn_print_feature_descriptorS prf_number = (prf[0] << 8) | prf[1]; fprintf(stderr, "LIBBURN_EXPERIMENTAL : %s profile %4.4Xh \"%s\"\n", prf[2] & 1 ? "+" : "-", prf_number, mmc_obtain_profile_name(prf_number)); #endif /* Libburn_print_feature_descriptorS */ } } else if (feature_code == 0x21) { d->current_has_feat21h = feature_is_current; for (i = 0; i < descr[7]; i++) { if (i == 0 || descr[8 + i] == 16) d->current_feat21h_link_size = descr[8 + i]; #ifdef Libburn_print_feature_descriptorS fprintf(stderr, "LIBBURN_EXPERIMENTAL : + Link Size = %d\n", descr[8 + i]); #endif /* Libburn_print_feature_descriptorS */ } } else if (feature_code == 0x23) { if (feature_is_current) { d->current_feat23h_byte4 = descr[4]; d->current_feat23h_byte8 = descr[8]; } #ifdef Libburn_print_feature_descriptorS if (cp >= 0x41 && cp <= 0x43) fprintf(stderr, "LIBBURN_EXPERIMENTAL : BD formats: %s%s%s%s%s\n", descr[4] & 1 ? " Cert" : "", descr[4] & 2 ? " QCert" : "", descr[4] & 4 ? " Expand" : "", descr[4] & 8 ? " RENoSA" : "", descr[8] & 1 ? " RRM" : ""); #endif /* Libburn_print_feature_descriptorS */ } else if (feature_code == 0x2F) { if (feature_is_current) d->current_feat2fh_byte4 = descr[4]; #ifdef Libburn_print_feature_descriptorS fprintf(stderr, "LIBBURN_EXPERIMENTAL : BUF = %d , Test Write = %d , DVD-RW = %d\n", !!(descr[4] & 64), !!(descr[4] & 4), !!(descr[4] & 2)); #endif /* Libburn_print_feature_descriptorS */ } else if (feature_code == 0x01) { phys_if_std = (descr[4] << 24) | (descr[5] << 16) | (descr[6] << 8) | descr[7]; if (phys_if_std == 1) phys_name = "SCSI Family"; else if(phys_if_std == 2) phys_name = "ATAPI"; else if(phys_if_std == 3 || phys_if_std == 4 || phys_if_std == 6) phys_name = "IEEE 1394 FireWire"; else if(phys_if_std == 7) phys_name = "Serial ATAPI"; else if(phys_if_std == 8) phys_name = "USB"; d->phys_if_std = phys_if_std; strcpy(d->phys_if_name, phys_name); #ifdef Libburn_print_feature_descriptorS fprintf(stderr, "LIBBURN_EXPERIMENTAL : Phys. Interface Standard %Xh \"%s\"\n", phys_if_std, phys_name); } else if (feature_code == 0x107) { fprintf(stderr, "LIBBURN_EXPERIMENTAL : CD SPEED = %d , page 2Ah = %d , SET STREAMING = %d\n", !!(descr[4] & 8), !!(descr[4] & 4), !!(descr[4] & 2)); #endif /* Libburn_print_feature_descriptorS */ } else if (feature_code == 0x108 || feature_code == 0x10c) { int c_limit; #ifdef Libburn_print_feature_descriptorS int i; fprintf(stderr, "LIBBURN_EXPERIMENTAL : %s = ", feature_code == 0x108 ? "Drive Serial Number" : "Drive Firmware Date"); #endif /* Libburn_print_feature_descriptorS */ c_limit = descr[3] - 2 * (feature_code == 0x10c); if (feature_code == 0x108) { if (d->drive_serial_number != NULL) BURN_FREE_MEM(d->drive_serial_number); BURN_ALLOC_MEM(d->drive_serial_number, char, c_limit + 1); memcpy(d->drive_serial_number, descr + 4, c_limit); d->drive_serial_number[c_limit] = 0; d->drive_serial_number_len = c_limit; } #ifdef Libburn_print_feature_descriptorS for (i = 0; i < c_limit; i++) if (descr[4 + i] < 0x20 || descr[4 + i] > 0x7e || descr[4 + i] == '\\') fprintf(stderr,"\\%2.2X",descr[4 + i]); else fprintf(stderr, "%c", descr[4 + i]); fprintf(stderr, "\n"); #endif /* Libburn_print_feature_descriptorS */ } } ret = 1; ex: BURN_FREE_MEM(msg); BURN_FREE_MEM(buf); BURN_FREE_MEM(c); return ret; } void mmc_get_configuration(struct burn_drive *d) { int alloc_len = 8, ret; if (d->current_profile > 0 && d->current_profile < 0xffff) return; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_get_configuration") <= 0) return; /* first command execution to learn Allocation Length */ ret = mmc_get_configuration_al(d, &alloc_len); /* fprintf(stderr,"LIBBURN_DEBUG: 46h alloc_len = %d , ret = %d\n", alloc_len, ret); */ if (alloc_len > 8 && ret > 0) { /* second execution with announced length */ mmc_get_configuration_al(d, &alloc_len); } } /* ts A70108 */ /* mmc5r03c.pdf 6.24 */ static int mmc_read_format_capacities_al(struct burn_drive *d, int *alloc_len, int top_wanted) { struct buffer *buf = NULL; int len, type, score, num_descr, max_score = -2000000000, i, sign = 1; int old_alloc_len, ret; off_t size, num_blocks; struct command *c = NULL; unsigned char *dpt; BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); if (*alloc_len < 4) {ret = 0; goto ex;} d->format_descr_type = 3; d->format_curr_max_size = 0; d->format_curr_blsas = 0; d->best_format_type = -1; d->best_format_size = 0; scsi_init_command(c, MMC_READ_FORMAT_CAPACITIES, sizeof(MMC_READ_FORMAT_CAPACITIES)); c->dxfer_len = *alloc_len; c->retry = 1; c->opcode[7]= (c->dxfer_len >> 8) & 0xff; c->opcode[8]= c->dxfer_len & 0xff; c->page = buf; c->page->sectors = 0; c->page->bytes = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); if (c->error) {ret = 0; goto ex;} len = c->page->data[3]; old_alloc_len = *alloc_len; *alloc_len = len + 4; if (old_alloc_len < 12) {ret = 1; goto ex;} if (len + 4 > old_alloc_len) len = old_alloc_len - 4; if (len < 8) {ret = 0; goto ex;} dpt = c->page->data + 4; /* decode 6.24.3.2 Current/Maximum Capacity Descriptor */ d->format_descr_type = dpt[4] & 3; d->format_curr_max_size = (((off_t) dpt[0]) << 24) + (dpt[1] << 16) + (dpt[2] << 8) + dpt[3]; if (d->format_descr_type == BURN_FORMAT_IS_UNKNOWN) d->format_curr_max_size = 0; d->format_curr_blsas = (dpt[5] << 16) + (dpt[6] << 8) + dpt[7]; d->format_curr_max_size *= (off_t) 2048; if((d->current_profile == 0x12 || d->current_profile == 0x43) && d->media_capacity_remaining == 0) { burn_drive_set_media_capacity_remaining(d, d->format_curr_max_size); d->media_lba_limit = d->format_curr_max_size / 2048; } #ifdef Libburn_dvd_ram_as_bd_rE /* <<< dummy format descriptor list as obtained from dvd+rw-mediainfo by Giulio Orsero in April 2008 */ d->num_format_descr = 5; d->format_descriptors[0].type = 0x00; d->format_descriptors[0].size = (off_t) 11826176 * (off_t) 2048; d->format_descriptors[0].tdp = 0x3000; d->format_descriptors[1].type = 0x30; d->format_descriptors[1].size = (off_t) 11826176 * (off_t) 2048; d->format_descriptors[1].tdp = 0x3000; d->format_descriptors[2].type = 0x30; d->format_descriptors[2].size = (off_t) 11564032 * (off_t) 2048; d->format_descriptors[2].tdp = 0x5000; d->format_descriptors[3].type = 0x30; d->format_descriptors[3].size = (off_t) 12088320 * (off_t) 2048; d->format_descriptors[3].tdp = 0x1000; d->format_descriptors[4].type = 0x31; d->format_descriptors[4].size = (off_t) 12219392 * (off_t) 2048; d->format_descriptors[4].tdp = 0x800; d->best_format_type = 0x00; d->best_format_size = (off_t) 11826176 * (off_t) 2048; /* silencing compiler warnings about unused variables */ num_blocks = size = sign = i = max_score = num_descr = score = type = 0; if (d->current_profile == 0x12 || d->current_profile == 0x43) {ret = 1; goto ex;} d->num_format_descr = 0; #endif /* Libburn_dvd_ram_as_bd_rE */ if (top_wanted == 0x00 || top_wanted == 0x10) sign = -1; /* the caller clearly desires full format */ /* 6.24.3.3 Formattable Capacity Descriptors */ num_descr = (len - 8) / 8; for (i = 0; i < num_descr; i++) { dpt = c->page->data + 12 + 8 * i; num_blocks = mmc_four_char_to_int(dpt); size = num_blocks * (off_t) 2048; type = dpt[4] >> 2; if (i < 32) { d->format_descriptors[i].type = type; d->format_descriptors[i].size = size; d->format_descriptors[i].tdp = (dpt[5] << 16) + (dpt[6] << 8) + dpt[7]; d->num_format_descr = i + 1; } /* Criterion is proximity to quick intermediate state */ if (type == 0x00) { /* full format (with lead out) */ score = 1 * sign; } else if (type == 0x10) { /* DVD-RW full format */ score = 10 * sign; } else if(type == 0x13) { /* DVD-RW quick grow last session */ score = 100 * sign; } else if(type == 0x15) { /* DVD-RW Quick */ score = 50 * sign; if(d->current_profile == 0x13) { burn_drive_set_media_capacity_remaining(d, size); d->media_lba_limit = num_blocks; } } else if(type == 0x26) { /* DVD+RW */ score = 1 * sign; burn_drive_set_media_capacity_remaining(d, size); d->media_lba_limit = num_blocks; } else { continue; } if (type == top_wanted) score += 1000000000; if (score > max_score) { d->best_format_type = type; d->best_format_size = size; max_score = score; } } ret = 1; ex: BURN_FREE_MEM(buf); BURN_FREE_MEM(c); return ret; } int mmc_read_format_capacities(struct burn_drive *d, int top_wanted) { int alloc_len = 4, ret; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_read_format_capacities") <= 0) return 0; ret = mmc_read_format_capacities_al(d, &alloc_len, top_wanted); /* fprintf(stderr,"LIBBURN_DEBUG: 23h alloc_len = %d , ret = %d\n", alloc_len, ret); */ if (alloc_len >= 12 && ret > 0) ret = mmc_read_format_capacities_al(d, &alloc_len, top_wanted); return ret; } void mmc_sync_cache(struct burn_drive *d) { struct command *c = NULL; char *msg = NULL; int key, asc, ascq; if (mmc_function_spy(d, "mmc_sync_cache") <= 0) goto ex; BURN_ALLOC_MEM_VOID(c, struct command, 1); BURN_ALLOC_MEM_VOID(msg, char, 256); scsi_init_command(c, MMC_SYNC_CACHE, sizeof(MMC_SYNC_CACHE)); c->retry = 1; if (!d->do_no_immed) c->opcode[1] |= 2; /* ts A70918 : Immed */ c->page = NULL; c->dir = NO_TRANSFER; if (d->do_no_immed) c->timeout = Libburn_mmc_sync_noim_timeouT; else c->timeout = Libburn_mmc_sync_timeouT; libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, "syncing cache", 0, 0); if(d->wait_for_buffer_free) { sprintf(msg, "Checked buffer %u times. Waited %u+%u times = %.3f s", d->pessimistic_writes, d->waited_writes, d->waited_tries - d->waited_writes, ((double) d->waited_usec) / 1.0e6); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002013f, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_LOW, msg, 0,0); } d->issue_command(d, c); /* ts A70918 */ if (c->error) { sprintf(msg, "Failed to synchronize drive cache"); sprintf(msg + strlen(msg), ". SCSI error : "); scsi_error_msg(d, c->sense, 14, msg + strlen(msg), &key, &asc, &ascq); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002017f, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); d->cancel = 1; goto ex; } spc_human_readable_cmd(c, msg, 160, 0); if (spc_wait_unit_attention(d, 3600, msg, 0) <= 0) d->cancel = 1; else d->needs_sync_cache = 0; ex: BURN_FREE_MEM(msg); BURN_FREE_MEM(c); } /* ts A61023 : http://libburn.pykix.org/ticket/14 get size and free space of drive buffer */ int mmc_read_buffer_capacity(struct burn_drive *d) { struct buffer *buf = NULL; struct command *c = NULL; unsigned char *data; int alloc_len = 12, ret; BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); if (mmc_function_spy(d, "mmc_read_buffer_capacity") <= 0) {ret = 0; goto ex;} scsi_init_command(c, MMC_READ_BUFFER_CAPACITY, sizeof(MMC_READ_BUFFER_CAPACITY)); c->dxfer_len = alloc_len; c->opcode[7] = (c->dxfer_len >> 8) & 0xff; c->opcode[8] = c->dxfer_len & 0xff; c->retry = 1; c->page = buf; memset(c->page->data, 0, alloc_len); c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); /* >>> ??? error diagnostics */ if (c->error) {ret = 0; goto ex;} data = c->page->data; d->progress.buffer_capacity = (data[4]<<24)|(data[5]<<16)|(data[6]<<8)|data[7]; d->progress.buffer_available = (data[8]<<24)|(data[9]<<16)|(data[10]<<8)|data[11]; if (d->progress.buffer_capacity < d->progress.buffer_available) { /* Default mad buffer usage to 50 percent */ d->progress.buffer_available = d->progress.buffer_capacity / 2; } d->pessimistic_buffer_free = d->progress.buffer_available; d->pbf_altered = 0; if (d->progress.buffered_bytes >= d->progress.buffer_capacity){ off_t fill; fill = d->progress.buffer_capacity - d->progress.buffer_available; if (fill < d->progress.buffer_min_fill && fill>=0) d->progress.buffer_min_fill = fill; } ret = 1; ex:; BURN_FREE_MEM(c); BURN_FREE_MEM(buf); return ret; } /* ts A61219 : learned much from dvd+rw-tools-7.0: plus_rw_format() and mmc5r03c.pdf, 6.5 FORMAT UNIT */ /* @param size The size (in bytes) to be sent with the FORMAT command @param flag bit1+2: size mode 0 = use parameter size as far as it makes sense 1 = insist in size 0 even if there is a better default known 2 = without bit7: format to maximum available size with bit7 : take size from indexed format descriptor 3 = format to default size bit3= expand format up to at least size bit4= enforce re-format of (partly) formatted media bit5= try to disable eventual defect management bit6= try to avoid lengthy media certification bit7= bit8 to bit15 contain the index of the format to use bit8-bit15 = see bit7 bit16= enable POW on blank BD-R */ int mmc_format_unit(struct burn_drive *d, off_t size, int flag) { struct buffer *buf = NULL; struct command *c = NULL; int ret, tolerate_failure = 0, return_immediately = 0, i, format_type; int index, format_sub_type = 0, format_00_index, size_mode; int accept_count = 0; off_t num_of_blocks = 0, diff, format_size, i_size, format_00_max_size; off_t min_size = -1, max_size = -1; char *msg = NULL, descr[80 + 10 + 22]; /* profile+filltext+index */ int key, asc, ascq; int full_format_type = 0x00; /* Full Format (or 0x10 for DVD-RW ?) */ BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); BURN_ALLOC_MEM(msg, char, 256); mmc_start_if_needed(d, 0); if (mmc_function_spy(d, "mmc_format_unit") <= 0) {ret = 0; goto ex;} size_mode = (flag >> 1) & 3; scsi_init_command(c, MMC_FORMAT_UNIT, sizeof(MMC_FORMAT_UNIT)); c->retry = 1; c->page = buf; c->page->bytes = 12; c->page->sectors = 0; c->dir = TO_DRIVE; if (d->do_no_immed) c->timeout = Libburn_mmc_blank_noim_timeouT; else c->timeout = Libburn_mmc_blank_timeouT; memset(c->page->data, 0, c->page->bytes); descr[0] = 0; if (!d->do_no_immed) c->page->data[1] = 0x02; /* Immed */ c->page->data[3] = 8; /* Format descriptor length */ num_of_blocks = size / 2048; mmc_int_to_four_char(c->page->data + 4, num_of_blocks); if (flag & 128) { /* explicitly chosen format descriptor */ /* use case: the app knows what to do */ ret = mmc_read_format_capacities(d, -1); if (ret <= 0) goto selected_not_suitable; index = (flag >> 8) & 0xff; if(index < 0 || index >= d->num_format_descr) { selected_not_suitable:; libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020132, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Selected format is not suitable for libburn", 0, 0); {ret = 0; goto ex;} } if (!(d->current_profile == 0x13 || d->current_profile == 0x14 || d->current_profile == 0x1a || d->current_profile == 0x12 || d->current_profile == 0x41 || d->current_profile == 0x43)) goto unsuitable_media; format_type = d->format_descriptors[index].type; if (!(format_type == 0x00 || format_type == 0x01 || format_type == 0x10 || format_type == 0x11 || format_type == 0x13 || format_type == 0x15 || format_type == 0x26 || format_type == 0x30 || format_type == 0x31 || format_type == 0x32)) goto selected_not_suitable; if (flag & 4) { num_of_blocks = d->format_descriptors[index].size / 2048; mmc_int_to_four_char(c->page->data + 4, num_of_blocks); } if (format_type != 0x26) for (i = 0; i < 3; i++) c->page->data[9 + i] = ( d->format_descriptors[index].tdp >> (16 - 8 * i)) & 0xff; if (format_type == 0x30 || format_type == 0x31) { format_sub_type = 0; if (flag & 64) { if (d->current_feat23h_byte4 & 2) /* Quick certification */ format_sub_type = 3; } else { if (d->current_feat23h_byte4 & 1) /* Full certification */ format_sub_type = 2; } } else if (format_type == 0x32 || (format_type == 0x00 && d->current_profile == 0x41)) { if (flag & (1 << 16)) format_sub_type = 0; /* SRM + POW */ else format_sub_type = 1; /* SRM (- POW) */ } if (d->current_profile == 0x12 && format_type !=0x01 && (flag & 64)) { /* DCRT and CmpList, see below */ c->page->data[1] |= 0x20; c->opcode[1] |= 0x08; } c->page->data[1] |= 0x80; /* FOV = this flag vector is valid */ sprintf(descr, "%s (descr %d)", d->current_profile_text,index); return_immediately = 1; /* caller must do the waiting */ } else if (d->current_profile == 0x1a) { /* DVD+RW */ /* use case: background formatting during write !(flag&4) de-icing as explicit formatting action (flag&4) */ /* mmc5r03c.pdf , 6.5.4.2.14, DVD+RW Basic Format */ format_type = 0x26; /* >>> ??? is this "| 8" a bug ? */ if ((size <= 0 && !(flag & 2)) || (flag & (4 | 8))) { /* maximum capacity */ memset(c->page->data + 4, 0xff, 4); num_of_blocks = 0xffffffff; } if(d->bg_format_status == 2 || (d->bg_format_status == 3 && !(flag & 16))) { sprintf(msg,"FORMAT UNIT ignored. Already %s.", (d->bg_format_status == 2 ? "in progress" : "completed")); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020120, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); {ret = 2; goto ex;} } if (!(flag & 16)) /* if not re-format is desired */ if (d->bg_format_status == 1) /* is partly formatted */ c->page->data[11] = 1; /* Restart bit */ sprintf(descr, "DVD+RW (fs=%d,rs=%d)", d->bg_format_status, (c->page->data[11] == 1)); if (flag & 4) return_immediately = 1;/* caller must do the waiting */ } else if (d->current_profile == 0x13 && !(flag & 16)) { /*DVD-RW restricted overwrite*/ /* use case: quick grow formatting during write */ ret = mmc_read_format_capacities(d, 0x13); if (ret > 0) { if (d->best_format_type == 0x13) { if (d->best_format_size <= 0) {ret = 1; goto ex;} } else { if (d->format_descr_type == 2) /* formatted */ {ret = 1; goto ex;} if (d->format_descr_type == 3){/*intermediate*/ d->needs_close_session = 1; {ret = 1; goto ex;} } /* does trying make sense at all ? */ tolerate_failure = 1; } } if (d->best_format_type == 0x13 && (flag & (4 | 8))) { num_of_blocks = d->best_format_size / 2048; if (flag & 8) { /* num_of_blocks needed to reach size */ diff = (size - d->format_curr_max_size) /32768; if ((size - d->format_curr_max_size) % 32768) diff++; diff *= 16; if (diff < num_of_blocks) num_of_blocks = diff; } if (num_of_blocks > 0) mmc_int_to_four_char(c->page->data + 4, num_of_blocks); } /* 6.5.4.2.8 , DVD-RW Quick Grow Last Border */ format_type = 0x13; c->page->data[11] = 16; /* block size * 2k */ sprintf(descr, "DVD-RW quick grow"); } else if (d->current_profile == 0x14 || (d->current_profile == 0x13 && (flag & 16))) { /* DVD-RW sequential recording (or Overwrite for re-format) */ /* use case : transition from Sequential to Overwrite re-formatting of Overwrite media */ /* To Restricted Overwrite */ /* 6.5.4.2.10 Format Type = 15h (DVD-RW Quick) */ /* or 6.5.4.2.1 Format Type = 00h (Full Format) */ /* or 6.5.4.2.5 Format Type = 10h (DVD-RW Full Format) */ mmc_read_format_capacities(d, (flag & 4) ? full_format_type : 0x15); if (d->best_format_type == 0x15 || d->best_format_type == full_format_type) { if ((flag & 4) || d->best_format_type == full_format_type) { num_of_blocks = d->best_format_size / 2048; mmc_int_to_four_char(c->page->data + 4, num_of_blocks); } } else { no_suitable_formatting_type:; libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020131, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "No suitable formatting type offered by drive", 0, 0); {ret = 0; goto ex;} } format_type = d->best_format_type; sprintf(descr, "DVD-RW %s", format_type == 0x15 ? "quick" : "full"); return_immediately = 1; /* caller must do the waiting */ } else if (d->current_profile == 0x12) { /* ts A80417 : DVD-RAM */ /* 6.5.4.2.1 Format Type = 00h (Full Format) 6.5.4.2.2 Format Type = 01h (Spare Area Expansion) */ index = format_00_index = -1; format_size = format_00_max_size = -1; for (i = 0; i < d->num_format_descr; i++) { format_type = d->format_descriptors[i].type; i_size = d->format_descriptors[i].size; if (format_type != 0x00 && format_type != 0x01) continue; if (flag & 32) { /* No defect mgt */ /* Search for largest 0x00 format descriptor */ if (format_type != 0x00) continue; if (i_size < format_size) continue; format_size = i_size; index = i; continue; } else if (flag & 4) { /*Max or default size with mgt*/ /* Search for second largest 0x00 format descriptor. For max size allow format type 0x01. */ if (format_type == 0x00) { if (i_size < format_size) continue; if (i_size < format_00_max_size) { format_size = i_size; index = i; continue; } format_size = format_00_max_size; index = format_00_index; format_00_max_size = i_size; format_00_index = i; continue; } if (size_mode==3) continue; if (i_size > format_size) { format_size = i_size; index = i; } continue; } /* Search for smallest 0x0 or 0x01 descriptor >= size */; if (d->format_descriptors[i].size >= size && (format_size < 0 || i_size < format_size) ) { format_size = i_size; index = i; } } if(index < 0 && (flag & 4) && !(flag & 32)) { format_size = format_00_max_size; index = format_00_index; } if(index < 0) goto no_suitable_formatting_type; format_type = d->format_descriptors[index].type; num_of_blocks = d->format_descriptors[index].size / 2048; mmc_int_to_four_char(c->page->data + 4, num_of_blocks); for (i = 0; i < 3; i++) c->page->data[9 + i] = ( d->format_descriptors[index].tdp >> (16 - 8 * i)) & 0xff; sprintf(descr, "%s", d->current_profile_text); return_immediately = 1; /* caller must do the waiting */ c->page->data[1] |= 0x80; /* FOV = this flag vector is valid */ if ((flag & 64) && format_type != 0x01) { /* MMC-5 6.5.3.2 , 6.5.4.2.1.2 DCRT: Disable Certification and maintain number of blocks CmpList: Override maintaining of number of blocks with DCRT */ /* ts A80426 : prevents change of formatted size with PHILIPS SPD3300L and Verbatim 3x DVD-RAM and format_type 0x00. Works on TSSTcorp SH-S203B */ c->page->data[1] |= 0x20; c->opcode[1] |= 0x08; } } else if (d->current_profile == 0x41) { /* BD-R SRM */ index = -1; format_size = -1; if (d->num_format_descr <= 0) goto no_suitable_formatting_type; if (d->format_descriptors[0].type != 0) goto no_suitable_formatting_type; for (i = 0; i < d->num_format_descr; i++) { format_type = d->format_descriptors[i].type; i_size = d->format_descriptors[i].size; if (format_type != 0x00 && format_type != 0x32) continue; if (flag & 32) { /* No defect mgt */ /* ts A81211 : MMC-5 6.5.4.2.17.1 When formatted with Format Type 32h, the BD-R disc is required to allocate a non-zero number of spares. */ goto no_suitable_formatting_type; } else if(size_mode == 2) { /* max payload size */ /* search largest 0x32 format descriptor */ if(format_type != 0x32) continue; } else if(size_mode == 3) { /* default payload size */ if (format_type == 0x00) { index = i; break; } continue; } else { /* defect managed format with size wish */ #ifdef Libburn_bd_r_format_olD /* search for smallest 0x32 >= size */ if(format_type != 0x32) continue; if (i_size < size) continue; if (format_size >= 0 && i_size >= format_size) continue; index = i; format_size = i_size; continue; #else /* Libburn_bd_r_format_olD */ /* search largest and smallest 0x32 */ if(format_type != 0x32) continue; if (i_size < min_size || min_size < 0) min_size = i_size; if (i_size > max_size) max_size = i_size; #endif /* ! Libburn_bd_r_format_olD */ } /* common for all cases which search largest descriptors */ if (i_size > format_size) { format_size = i_size; index = i; } } if (size_mode == 2 && index < 0 && !(flag & 32)) index = 0; if (index < 0) goto no_suitable_formatting_type; format_type = d->format_descriptors[index].type; if (flag & (1 << 16)) format_sub_type = 0; /* SRM + POW */ else format_sub_type = 1; /* SRM (- POW) */ #ifdef Libburn_bd_r_format_olD if (0) { #else if (size_mode == 0 || size_mode == 1) { #endif /* ! Libburn_bd_r_format_olD */ if (min_size < 0 || max_size < 0) goto no_suitable_formatting_type; if (size <= 0) size = min_size; if (size % 0x10000) size += 0x10000 - (size % 0x10000); if (size < min_size) goto no_suitable_formatting_type; else if(size > max_size) goto no_suitable_formatting_type; num_of_blocks = size / 2048; mmc_int_to_four_char(c->page->data + 4, num_of_blocks); for (i = 0; i < 3; i++) c->page->data[9 + i] = 0; } else { num_of_blocks = d->format_descriptors[index].size / 2048; mmc_int_to_four_char(c->page->data + 4, num_of_blocks); for (i = 0; i < 3; i++) c->page->data[9 + i] = ( d->format_descriptors[index].tdp >> (16 - 8 * i)) & 0xff; } sprintf(descr, "%s", d->current_profile_text); return_immediately = 1; /* caller must do the waiting */ c->page->data[1] |= 0x80; /* FOV = this flag vector is valid */ } else if (d->current_profile == 0x43) { /* BD-RE */ index = -1; format_size = -1; if (d->num_format_descr <= 0) goto no_suitable_formatting_type; if (d->format_descriptors[0].type != 0) goto no_suitable_formatting_type; for (i = 0; i < d->num_format_descr; i++) { format_type = d->format_descriptors[i].type; i_size = d->format_descriptors[i].size; if (format_type != 0x00 && format_type != 0x30 && format_type != 0x31) continue; if (flag & 32) { /* No defect mgt */ /* search largest format 0x31 */ if(format_type != 0x31) continue; } else if(size_mode == 2) { /* max payload size */ /* search largest 0x30 format descriptor */ if(format_type != 0x30) continue; } else if(size_mode == 3) { /* default payload size */ if (accept_count < 1) index = 0; /* this cannot certify */ /* ts A81129 LG GGW-H20L YL03 refuses on 0x30 with "Quick certification". dvd+rw-format does 0x00 by default and succeeds quickly. */ if ((flag & 64) && format_type == 0x00) { index = i; break; } if(format_type != 0x30) continue; accept_count++; if (accept_count == 1) index = i; continue; } else { /* defect managed format with size wish */ #ifdef Libburn_bd_re_format_olD /* search for smallest 0x30 >= size */ if(format_type != 0x30) continue; if (i_size < size) continue; if (format_size >= 0 && i_size >= format_size) continue; index = i; format_size = i_size; continue; #else /* Libburn_bd_re_format_olD */ /* search largest and smallest 0x30 */ if(format_type != 0x30) continue; if (i_size < min_size || min_size < 0) min_size = i_size; if (i_size > max_size) max_size = i_size; #endif /* ! Libburn_bd_re_format_olD */ } /* common for all cases which search largest descriptors */ if (i_size > format_size) { format_size = i_size; index = i; } } if (size_mode == 2 && index < 0 && !(flag & 32)) index = 0; if (index < 0) goto no_suitable_formatting_type; format_type = d->format_descriptors[index].type; if (format_type == 0x30 || format_type == 0x31) { if ((flag & 64) || !(d->current_feat23h_byte4 & 3)) { format_sub_type = 0; if (!(flag & 64)) libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002019e, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, "Drive does not support media certification", 0, 0); } else { /* With Certification */ if (d->current_feat23h_byte4 & 1) format_sub_type = 2; /* Full */ else format_sub_type = 3; /* Quick */ } } #ifdef Libburn_bd_re_format_olD if (0) { #else if (size_mode == 0 || size_mode == 1) { #endif /* ! Libburn_bd_re_format_olD */ if (min_size < 0 || max_size < 0) goto no_suitable_formatting_type; if (size <= 0) size = min_size; if (size % 0x10000) size += 0x10000 - (size % 0x10000); if (size < min_size) goto no_suitable_formatting_type; else if(size > max_size) goto no_suitable_formatting_type; num_of_blocks = size / 2048; mmc_int_to_four_char(c->page->data + 4, num_of_blocks); for (i = 0; i < 3; i++) c->page->data[9 + i] = 0; } else { num_of_blocks = d->format_descriptors[index].size / 2048; mmc_int_to_four_char(c->page->data + 4, num_of_blocks); for (i = 0; i < 3; i++) c->page->data[9 + i] = ( d->format_descriptors[index].tdp >> (16 - 8 * i)) & 0xff; } sprintf(descr, "%s", d->current_profile_text); return_immediately = 1; /* caller must do the waiting */ c->page->data[1] |= 0x80; /* FOV = this flag vector is valid */ } else { /* >>> other formattable types to come */ unsuitable_media:; sprintf(msg, "Unsuitable media detected. Profile %4.4Xh %s", d->current_profile, d->current_profile_text); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002011e, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); {ret = 0; goto ex;} } c->page->data[8] = (format_type << 2) | (format_sub_type & 3); /* MMC-5 Table 253 , column Type Dependent Parameter */ if (format_type == 0x00 || format_type == 0x01 || format_type == 0x31) { /* Block Length 0x0800 = 2k */ c->page->data[ 9] = 0x00; c->page->data[10] = 0x08; c->page->data[11] = 0x00; } else if (format_type >= 0x10 && format_type <= 0x15) { /* ECC block size = 16 * 2k */ c->page->data[ 9] = 0; c->page->data[10] = 0; c->page->data[11] = 16; } sprintf(msg, "Format type %2.2Xh \"%s\", blocks = %.f", format_type, descr, (double) num_of_blocks); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); sprintf(msg, "CDB: "); for (i = 0; i < 6; i++) sprintf(msg + strlen(msg), "%2.2X ", c->opcode[i]); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); sprintf(msg, "Format list: "); for (i = 0; i < 12; i++) sprintf(msg + strlen(msg), "%2.2X ", c->page->data[i]); strcat(msg, "\n"); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); #ifdef Libburn_do_not_format_dvd_ram_or_bd_rE if(d->current_profile == 0x43 || d->current_profile == 0x12) { sprintf(msg, "Formatting of %s not implemented yet - This is a dummy", d->current_profile_text); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); {ret = 1; goto ex;} } #endif /* Libburn_do_not_format_dvd_ram_or_bd_rE */ /* <<< fprintf(stderr, "\nlibburn_DEBUG: FORMAT UNIT temporarily disabled.\n"); ret = 1; goto ex; */ d->issue_command(d, c); if (c->error && !tolerate_failure) { spc_decode_sense(c->sense, 0, &key, &asc, &ascq); if (key != 0) { sprintf(msg, "SCSI error on format_unit(%s): ", descr); scsi_error_msg(d, c->sense, 14, msg + strlen(msg), &key, &asc, &ascq); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020122, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } {ret = 0; goto ex;} } else if ((!c->error) && (format_type == 0x13 || format_type == 0x15)) d->needs_close_session = 1; if (return_immediately) {ret = 1; goto ex;} usleep(1000000); /* there seems to be a little race condition */ for (ret = 0; ret <= 0 ;) { usleep(50000); ret = spc_test_unit_ready(d); } mmc_sync_cache(d); ret = 1; ex:; BURN_FREE_MEM(msg); BURN_FREE_MEM(c); BURN_FREE_MEM(buf); return ret; } /* ts B40107 : Outsourced from mmc_get_performance_al() */ static int new_burn_speed_descr(struct burn_drive *d, int sd_source, struct burn_speed_descriptor **sd, int flag) { int ret; ret = burn_speed_descriptor_new(&(d->mdata->speed_descriptors), NULL, d->mdata->speed_descriptors, 0); if (ret <= 0) return ret; *sd = d->mdata->speed_descriptors; (*sd)->source = sd_source; if (d->current_profile > 0) { (*sd)->profile_loaded = d->current_profile; strcpy((*sd)->profile_name, d->current_profile_text); } return 1; } /* ts B40107 : Outsourced from mmc_get_performance_al() and extended for descr_type 0x00 @param flag bit0= register speed descriptors */ static int interpret_performance(struct burn_drive *d, struct command *c, int descr_type, int *alloc_len, int *max_descr, int *num_descr, int flag) { int len, i, b, ret, old_alloc_len; int exact_bit, read_speed, write_speed, start_speed; int min_write_speed = 0x7fffffff, max_write_speed = 0; int min_read_speed = 0x7fffffff, max_read_speed = 0; unsigned long end_lba; unsigned char *pd; struct burn_speed_descriptor *sd; /* ts A61225 : 1 = report about speed descriptors */ static int speed_debug = 0; len = mmc_four_char_to_int(c->page->data); old_alloc_len = *alloc_len; *alloc_len = len + 4; if (len + 4 > old_alloc_len) len = old_alloc_len - 4; *num_descr = ( *alloc_len - 8 ) / 16; if (*max_descr == 0) { *max_descr = *num_descr; {ret = 1; goto ex;} } if (old_alloc_len < 16) {ret = 1; goto ex;} if (len < 12) {ret = 0; goto ex;} min_write_speed = d->mdata->min_write_speed; max_write_speed = d->mdata->max_write_speed; pd = c->page->data; if (*num_descr > *max_descr) *num_descr = *max_descr; for (i = 0; i < *num_descr && (flag & 1); i++) { end_lba = read_speed = write_speed = start_speed = 0; if (descr_type == 0x03) { exact_bit = !!(pd[8 + i*16] & 2); for (b = 0; b < 4 ; b++) { end_lba += ((unsigned long int) pd[8 + i*16 + 4 + b]) << (24 - 8 * b); read_speed += pd[8 + i*16 + 8 + b] << (24 - 8 * b); write_speed += pd[8 + i*16 + 12 + b] << (24 - 8 * b); } if (end_lba > 0x7ffffffe) end_lba = 0x7ffffffe; if (speed_debug) fprintf(stderr, "LIBBURN_DEBUG: kB/s: write=%d read=%d end=%lu exact=%d\n", write_speed, read_speed, end_lba, exact_bit); ret = new_burn_speed_descr(d, 2, &sd, 0); if (ret > 0) { sd->wrc = (pd[8 + i*16] >> 3 ) & 3; sd->exact = exact_bit; sd->mrw = pd[8 + i*16] & 1; sd->end_lba = end_lba; sd->write_speed = write_speed; sd->read_speed = read_speed; } } else { /* descr_type == 0 */ for (b = 0; b < 4 ; b++) { start_speed += pd[8 + i*16 + 4 + b] << (24 - 8 * b); end_lba += ((unsigned long int) pd[8 + i*16 + 8 + b]) << (24 - 8 * b); read_speed += pd[8 + i*16 + 12 + b] << (24 - 8 * b); } if (speed_debug) fprintf(stderr, "LIBBURN_DEBUG: start=%d end=%d lba=%lu\n", start_speed, read_speed, end_lba); if (end_lba > 0x7ffffffe) end_lba = 0x7ffffffe; ret = new_burn_speed_descr(d, 3, &sd, 0); if (ret > 0) { sd->end_lba = end_lba; sd->read_speed = start_speed; } if (start_speed > 0 && start_speed < min_read_speed) min_read_speed = start_speed; if (start_speed > max_read_speed) max_read_speed = start_speed; ret = new_burn_speed_descr(d, 3, &sd, 0); if (ret > 0) { sd->end_lba = end_lba; sd->read_speed = read_speed; } } if ((int) end_lba > d->mdata->max_end_lba) d->mdata->max_end_lba = end_lba; if ((int) end_lba < d->mdata->min_end_lba) d->mdata->min_end_lba = end_lba; if (write_speed > 0 && write_speed < min_write_speed) min_write_speed = write_speed; if (write_speed > max_write_speed) max_write_speed = write_speed; if (read_speed > 0 && read_speed < min_read_speed) min_read_speed = read_speed; if (read_speed > max_read_speed) max_read_speed = read_speed; } if (min_write_speed < 0x7fffffff) d->mdata->min_write_speed = min_write_speed; if (max_write_speed > 0) d->mdata->max_write_speed = max_write_speed; /* there is no mdata->min_read_speed yet if (min_read_speed < 0x7fffffff) d->mdata->min_read_speed = min_read_speed; */ if (max_read_speed > 0) d->mdata->max_read_speed = max_read_speed; ret = 1; ex:; return ret; } /* ts A61225 */ /* @param flag bit0= register speed descriptors */ static int mmc_get_performance_al(struct burn_drive *d, int descr_type, int *alloc_len, int *max_descr, int flag) { int num_descr, ret; struct buffer *buf = NULL; struct command *c = NULL; BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); if (d->current_profile < 0) mmc_get_configuration(d); if (*alloc_len < 8) {ret = 0; goto ex;} if (descr_type != 0x00 && descr_type != 0x03) {ret = 0; goto ex;} scsi_init_command(c, MMC_GET_PERFORMANCE, sizeof(MMC_GET_PERFORMANCE)); /* >>> future: maintain a list of write descriptors if (max_descr > d->max_write_descr - d->num_write_descr) max_descr = d->max_write_descr; */ c->dxfer_len = *alloc_len; if (descr_type == 0x00) c->opcode[1] = 0x10; /* Data Type: nominal read performance */ c->opcode[8] = ( *max_descr >> 8 ) & 0xff; c->opcode[9] = ( *max_descr >> 0 ) & 0xff; c->opcode[10] = descr_type; c->retry = 1; c->page = buf; c->page->sectors = 0; c->page->bytes = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); #ifdef Libisofs_simulate_old_mmc1_drivE c->error = 1; c->sense[0] = 0x70; /* Fixed format sense data */ c->sense[2] = 0x5; c->sense[12] = 0x20; c->sense[13] = 0x0; #endif /* Libisofs_simulate_old_mmc1_drivE */ if (c->error) {ret = 0; goto ex;} ret = interpret_performance(d, c, descr_type, alloc_len, max_descr, &num_descr, flag); if (ret <= 0) goto ex; ret = num_descr; ex:; BURN_FREE_MEM(buf); BURN_FREE_MEM(c); return ret; } int mmc_get_performance(struct burn_drive *d, int descr_type, int flag) { int alloc_len = 8, max_descr = 0, ret; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_get_write_performance") <= 0) return 0; /* first command execution to learn number of descriptors and dxfer_len */ ret = mmc_get_performance_al(d, descr_type, &alloc_len, &max_descr, 0); if (max_descr > 0 && ret > 0) { /* Some drives announce only 1 descriptor if asked for 0. So ask twice for non-0 descriptors. */ ret = mmc_get_performance_al(d, descr_type, &alloc_len, &max_descr, 0); } /* fprintf(stderr,"LIBBURN_DEBUG: ACh alloc_len = %d , ret = %d\n", alloc_len, ret); */ if (max_descr > 0 && ret > 0) { /* final execution with announced length */ max_descr = (alloc_len - 8) / 16; ret = mmc_get_performance_al(d, descr_type, &alloc_len, &max_descr, 1); } return ret; } int mmc_get_write_performance(struct burn_drive *d) { int ret; ret = mmc_get_performance(d, 0x03, 0); return ret; } /* ts A61229 : outsourced from spc_select_write_params() */ /* Note: Page data is not zeroed here in order not to overwrite preset defaults. Thus memset(pd, 0, 2 + d->mdata->write_page_length); is the eventual duty of the caller. */ int mmc_compose_mode_page_5(struct burn_drive *d, struct burn_session *s, int tnum, const struct burn_write_opts *o, unsigned char *pd) { unsigned char *catalog = NULL; char isrc_text[13 + 21]; /* should suffice for 64 bit oversize */ struct isrc *isrc; pd[0] = 5; pd[1] = d->mdata->write_page_length; if (d->current_profile == 0x13) { /* A61229 : DVD-RW restricted overwrite */ /* learned from transport.hxx : page05_setup() and mmc3r10g.pdf table 347 */ /* BUFE (burnproof), no LS_V (i.e. default Link Size, i hope), no simulate, write type 0 = packet */ pd[2] = (1 << 6); /* no multi, fixed packet, track mode 5 */ pd[3] = (1 << 5) | 5; /* Data Block Type */ pd[4] = 8; /* Link size dummy */ pd[5] = 0; } else if ((d->current_profile == 0x14 || d->current_profile == 0x11 || d->current_profile == 0x15) && o->write_type == BURN_WRITE_SAO) { /* ts A70205 : DVD-R[W][/DL] : Disc-at-once, DAO */ /* Learned from dvd+rw-tools and mmc5r03c.pdf . See doc/cookbook.txt for more detailed references. */ /* BUFE , LS_V = 0, Test Write, Write Type = 2 SAO (DAO) */ pd[2] = ((!!o->underrun_proof) << 6) | ((!!o->simulate) << 4) | 2; /* No multi-session , FP = 0 , Copy = 0, Track Mode = 5 */ pd[3] = 5; #ifdef Libburn_pioneer_dvr_216d_load_mode5 /* >>> use track mode from mmc_get_nwa() */ /* >>> pd[3] = (pd[3] & ~0xf) | (d->track_inf[5] & 0xf); */ #endif /* Data Block Type = 8 */ pd[4] = 8; } else if (d->current_profile == 0x14 || d->current_profile == 0x11 || d->current_profile == 0x15) { /* ts A70128 : DVD-R[W][/DL] Incremental Streaming */ /* Learned from transport.hxx : page05_setup() and mmc5r03c.pdf 7.5, 4.2.3.4 Table 17 and spc3r23.pdf 6.8, 7.4.3 */ /* BUFE , LS_V = 1, Test Write, Write Type = 0 Packet/Incremental */ pd[2] = ((!!o->underrun_proof) << 6) | (1 << 5) | ((!!o->simulate) << 4); /* Multi-session , FP = 1 , Track Mode = 5 */ pd[3] = ((3 * !!o->multi) << 6) | (1 << 5) | 5; /* Data Block Type = 8 */ pd[4] = 8; /* Link Size */ if (d->current_feat21h_link_size >= 0) pd[5] = d->current_feat21h_link_size; else pd[5] = 16; if (d->current_feat21h_link_size != 16) { char msg[80]; sprintf(msg, "Feature 21h Link Size = %d (expected 16)\n", d->current_feat21h_link_size); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); } /* Packet Size */ pd[13] = 16; } else if (d->current_profile == 0x1a || d->current_profile == 0x1b || d->current_profile == 0x2b || d->current_profile == 0x12 || d->current_profile == 0x41 || d->current_profile == 0x42 || d->current_profile == 0x43) { /* not with DVD+R[W][/DL] or DVD-RAM or BD-R[E] */; return 0; } else { /* Traditional setup for CD */ pd[2] = ((!!o->underrun_proof) << 6) | ((!!o->simulate) << 4) | (o->write_type & 0x0f); /* ts A61106 : MMC-1 table 110 : multi==0 or multi==3 */ pd[3] = ((3 * !!o->multi) << 6) | (o->control & 0x0f); pd[4] = spc_block_type(o->block_type); /* fprintf(stderr, "libburn_EXPERIMENTAL: block_type = %d, pd[4]= %u\n", o->block_type, (unsigned int) pd[4]); */ /* ts A61104 */ if(!(o->control&4)) /* audio (MMC-1 table 61) */ if(o->write_type == BURN_WRITE_TAO) pd[4] = 0; /* Data Block Type: Raw Data */ pd[14] = 0; /* audio pause length MSB */ pd[15] = 150; /* audio pause length LSB */ /*XXX need session format! */ /* ts A61229 : but session format (pd[8]) = 0 seems ok */ /* Media Catalog Number at byte 16 to 31, MMC-5, 7.5, Tables 664, 670 */ if (o->has_mediacatalog) catalog = (unsigned char *) o->mediacatalog; else if (s != NULL) { if (s->mediacatalog[0]) catalog = s->mediacatalog; } if (catalog != NULL && d->mdata->write_page_length >= 30) { pd[16] = 0x80; /* MCVAL */ memcpy(pd + 17, catalog, 13); } /* ISRC at bytes 32 to 47. Tables 664, 671 */ /* SCMS at byte 3 bit 4 */ isrc_text[0] = 0; if (s != NULL && o->write_type == BURN_WRITE_TAO) { if (tnum >= 0 && tnum < s->tracks) { if (s->track[tnum]->isrc.has_isrc) { isrc = &(s->track[tnum]->isrc); isrc_text[0] = isrc->country[0]; isrc_text[1] = isrc->country[1]; isrc_text[2] = isrc->owner[0]; isrc_text[3] = isrc->owner[1]; isrc_text[4] = isrc->owner[2]; sprintf(isrc_text + 5, "%-2.2u", (unsigned int) isrc->year); sprintf(isrc_text + 7, "%-5.5u", isrc->serial); isrc_text[12]= 0; } if ((s->track[tnum]->mode & BURN_SCMS) && !(s->track[tnum]->mode & BURN_COPY)) pd[3] |= 0x10; } } if (isrc_text[0] != 0 && d->mdata->write_page_length >= 46) { pd[32] = 0x80; /* TCVAL */ memcpy(pd + 33, isrc_text, 12); } } return 1; } /* A70812 ts */ int mmc_read_10(struct burn_drive *d, int start,int amount, struct buffer *buf) { struct command *c; char *msg = NULL; int key, asc, ascq, silent; c = &(d->casual_command); mmc_start_if_needed(d, 0); if (mmc_function_spy(d, "mmc_read_10") <= 0) return -1; if (amount > BUFFER_SIZE / 2048) return -1; scsi_init_command(c, MMC_READ_10, sizeof(MMC_READ_10)); c->dxfer_len = amount * 2048; c->retry = 1; mmc_int_to_four_char(c->opcode + 2, start); c->opcode[7] = (amount >> 8) & 0xFF; c->opcode[8] = amount & 0xFF; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); /* <<< replace by mmc_eval_read_error */; if (c->error) { msg = calloc(1, 256); if (msg != NULL) { sprintf(msg, "SCSI error on read_10(%d,%d): ", start, amount); scsi_error_msg(d, c->sense, 14, msg + strlen(msg), &key, &asc, &ascq); silent = (d->silent_on_scsi_error == 1); if (key == 5 && asc == 0x64 && ascq == 0x0) { d->had_particular_error |= 1; if (d->silent_on_scsi_error == 2) silent = 1; } if(!silent) libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020144, (d->silent_on_scsi_error == 3) ? LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); free(msg); } return BE_CANCELLED; } buf->sectors = amount; buf->bytes = amount * 2048; return 0; } #ifdef Libburn_develop_quality_scaN /* B21108 ts : Vendor specific command REPORT ERROR RATE, see http://liggydee.cdfreaks.com/ddl/errorcheck.pdf */ int mmc_nec_optiarc_f3(struct burn_drive *d, int sub_op, int start_lba, int rate_period, int *ret_lba, int *error_rate1, int *error_rate2) { struct buffer *buf = NULL; struct command *c; char *msg = NULL; int key, asc, ascq, ret; static unsigned char MMC_NEC_OPTIARC_F3[] = { 0xF3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); mmc_start_if_needed(d, 0); if (mmc_function_spy(d, "mmc_nec_optiarc_f3") <= 0) return -1; scsi_init_command(c, MMC_NEC_OPTIARC_F3, sizeof(MMC_NEC_OPTIARC_F3)); if (sub_op == 3) { c->dxfer_len = 8; c->dir = FROM_DRIVE; } else { c->dxfer_len = 0; c->dir = NO_TRANSFER; } c->retry = 0; c->opcode[1] = sub_op; mmc_int_to_four_char(c->opcode + 2, start_lba); c->opcode[8] = rate_period; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; d->issue_command(d, c); if (c->error) { msg = calloc(1, 256); if (msg != NULL) { sprintf(msg, "SCSI error on nec_optiarc_f3(%d, %d, %d): ", sub_op, start_lba, rate_period); scsi_error_msg(d, c->sense, 14, msg + strlen(msg), &key, &asc, &ascq); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020144, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); free(msg); } return BE_CANCELLED; } if (sub_op == 3) { *ret_lba = mmc_four_char_to_int(c->page->data); *error_rate1 = c->page->data[4] * 256 + c->page->data[5]; *error_rate2 = c->page->data[6] * 256 + c->page->data[7]; } ret = 1; ex:; BURN_FREE_MEM(c); BURN_FREE_MEM(buf); return ret; } #endif /* Libburn_develop_quality_scaN */ /* ts A81210 : Determine the upper limit of readable data size */ int mmc_read_capacity(struct burn_drive *d) { struct buffer *buf = NULL; struct command *c = NULL; int alloc_len= 8, ret; BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); d->media_read_capacity = 0x7fffffffffffffff; d->mr_capacity_trusted = -1; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_read_capacity") <= 0) {ret = 0; goto ex;} scsi_init_command(c, MMC_READ_CAPACITY, sizeof(MMC_READ_CAPACITY)); c->dxfer_len = alloc_len; c->retry = 1; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); d->media_read_capacity = mmc_four_char_to_uint(c->page->data); if (d->current_profile >= 0x08 && d->current_profile <= 0x0A) d->mr_capacity_trusted = 0; else d->mr_capacity_trusted = 1; ret = 1; ex:; BURN_FREE_MEM(c); BURN_FREE_MEM(buf); return ret; } /* ts A90903 */ /* mmc5r03c.pdf 6.23 ADh READ DISC STRUCTURE obtains media specific information */ static int mmc_read_disc_structure_al(struct burn_drive *d, int *alloc_len, int media_type, int layer_number, int format, int min_len, char **reply, int *reply_len, int flag) { struct buffer *buf = NULL; int old_alloc_len, len, ret; struct command *c = NULL; unsigned char *dpt; BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); *reply = NULL; *reply_len = 0; if (*alloc_len < 4) {ret = 0; goto ex;} scsi_init_command(c, MMC_READ_DISC_STRUCTURE, sizeof(MMC_READ_DISC_STRUCTURE)); c->dxfer_len = *alloc_len; c->retry = 1; c->opcode[1]= media_type; c->opcode[7]= format; c->opcode[8]= (c->dxfer_len >> 8) & 0xff; c->opcode[9]= c->dxfer_len & 0xff; c->page = buf; c->page->sectors = 0; c->page->bytes = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); if (c->error) {ret = 0; goto ex;} len = (c->page->data[0] << 8) | (c->page->data[1]); old_alloc_len = *alloc_len; *alloc_len = len + 2; if (old_alloc_len <= 4) {ret = 1; goto ex;} if (len + 2 > old_alloc_len) len = old_alloc_len - 2; if (len < 4) {ret = 0; goto ex;} dpt = c->page->data + 4; if (len - 2 < min_len) {ret = 0; goto ex;} *reply = calloc(len - 2, 1); if (*reply == NULL) {ret = 0; goto ex;} *reply_len = len - 2; memcpy(*reply, dpt, len - 2); ret = 1; ex:; BURN_FREE_MEM(c); BURN_FREE_MEM(buf); return ret; } int mmc_read_disc_structure(struct burn_drive *d, int media_type, int layer_number, int format, int min_len, char **reply, int *reply_len, int flag) { int alloc_len = 4, ret; char msg[80]; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "mmc_read_disc_structure") <= 0) return 0; ret = mmc_read_disc_structure_al(d, &alloc_len, media_type, layer_number, format, min_len, reply, reply_len, 0); /* fprintf(stderr,"LIBBURN_DEBUG: ADh alloc_len = %d , ret = %d\n", alloc_len, ret); */ if (ret <= 0) return ret; if (alloc_len < 12) { sprintf(msg, "READ DISC STRUCTURE announces only %d bytes of reply\n", alloc_len); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); ret = 0; /* ts A91205 LG GH22LS30 revision 1.00 returns for DVD-R format code 0x0E an allocation length of 4 (= 0 payload). A MS-Windows tool can inquire media code "RITEKF1", though. This macro causes a try to unconditionally read the desired payload bytes. The drive then returns 35 bytes as requested and the media id is "RITEKF1". Nevertheless this is not a generally usable gesture because older GNU/Linux USB dislikes requests to fetch more bytes than the drive will deliver. # define Libburn_enforce_structure_code_0x0E 1 */ #ifdef Libburn_enforce_structure_code_0x0E if (format == 0x0E) { alloc_len = min_len + 4; ret = mmc_read_disc_structure_al(d, &alloc_len, media_type, layer_number, format, min_len, reply, reply_len, 0); if (*reply_len < min_len || *reply == NULL) ret = 0; sprintf(msg, "READ DISC STRUCTURE returns %d bytes of required %d\n", *reply_len + 4, min_len + 4); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); } #endif } else ret = mmc_read_disc_structure_al(d, &alloc_len, media_type, layer_number, format, min_len, reply, reply_len, 0); return ret; } /* ts A90903 */ /* @param flag bit0= set bit1 in flag for burn_util_make_printable_word and do not append media revision bit1= truncate media_code1 to 6 characters (else 8) */ static int mmc_set_product_id(char *reply, int manuf_idx, int type_idx, int rev_idx, char **product_id, char **media_code1, char **media_code2, int flag) { int ret; *product_id = calloc(17, 1); *media_code1 = calloc(9, 1); *media_code2 = calloc(8, 1); if (*product_id == NULL || *media_code1 == NULL || *media_code2 == NULL) return -1; if (flag & 2) sprintf(*media_code1, "%.6s", reply + manuf_idx); else sprintf(*media_code1, "%.8s", reply + manuf_idx); ret = burn_util_make_printable_word(media_code1, 1 | ((flag & 1) << 1)); if (ret <= 0) return -1; sprintf(*media_code2, "%.3s%s", reply + type_idx, (flag & 1) ? "" : "xxxx"); ret = burn_util_make_printable_word(media_code2, 1 | ((flag & 1) << 1)); if (ret <= 0) return -1; if (!(flag & 1)) { sprintf(*media_code2 + strlen(*media_code2) - 4, "/%d", (int) ((unsigned char *) reply)[rev_idx]); } sprintf(*product_id, "%s/%s", *media_code1, *media_code2); return 1; } /* ts A90903 */ /* MMC backend of API call burn_disc_get_media_id() See also doc/mediainfo.txt @param flag Bitfield for control purposes bit0= do not escape " _/" (not suitable for burn_guess_manufacturer()) */ int mmc_get_media_product_id(struct burn_drive *d, char **product_id, char **media_code1, char **media_code2, char **book_type, int flag) { int prf, ret, reply_len, i, has_11h = -1, bt, start_lba, end_lba; int min, sec, fr, media_type = 0; char *reply = NULL, *wpt; static char *books[16] = { "DVD-ROM", "DVD-RAM", "DVD-R", "DVD-RW", "HD DVD-ROM", "HD DVD-RAM", "HD DVD-R", "unknown", "unknown", "DVD+RW", "DVD+R", "unknown", "unknown", "DVD+RW DL", "DVD+R DL", "unknown"}; *product_id = *media_code1 = *media_code2 = *book_type = NULL; prf = d->current_profile; if (prf == 0x09 || prf == 0x0A) { *product_id = calloc(20, 1); *media_code1 = calloc(10, 1); *media_code2 = calloc(10, 1); if (*product_id == NULL || *media_code1 == NULL || *media_code2 == NULL) { ret = -1; goto ex; } ret = burn_disc_read_atip(d); if (ret <= 0) goto ex; ret = burn_drive_get_start_end_lba(d, &start_lba, &end_lba, 0); if (ret <= 0) goto ex; burn_lba_to_msf(start_lba, &min, &sec, &fr); sprintf(*media_code1, "%2.2dm%2.2ds%2.2df", min, sec, fr); burn_lba_to_msf(end_lba, &min, &sec, &fr); sprintf(*media_code2, "%2.2dm%2.2ds%2.2df", min, sec, fr); sprintf(*product_id, "%s/%s", *media_code1, *media_code2); ret = 1; goto ex; /* No booktype with CD media */ } else if (prf == 0x11 || prf == 0x13 || prf == 0x14 || prf == 0x15) { /* DVD-R */ ret = mmc_read_disc_structure(d, 0, 0, 0x0E, 31, &reply, &reply_len, 0); if (ret <= 0) goto ex; /* ECMA-279 for DVD-R promises a third sixpack in field 5, but ECMA-338 for DVD-RW defines a different meaning. DVD-R and DVD-RW bear unprintable characters in there. */ if (reply[16] != 3 || reply[24] != 4) { ret = 0; goto ex; } *media_code1 = calloc(19, 1); *media_code2 = strdup(""); if (*media_code1 == NULL || *media_code2 == NULL) { ret = -1; goto ex; } memcpy(*media_code1, reply + 17, 6); memcpy(*media_code1 + 6, reply + 25, 6); /* Clean out 0 bytes */ wpt = *media_code1; for (i = 0; i < 18; i++) if ((*media_code1)[i]) *(wpt++) = (*media_code1)[i]; *wpt = 0; ret = burn_util_make_printable_word(media_code1, 1 | ((flag & 1) << 1)); if (ret <= 0) goto ex; *product_id = strdup(*media_code1); if (*product_id == NULL) { ret = -1; goto ex; } } else if (prf == 0x1a || prf == 0x1b || prf == 0x2b) { /* DVD+R[W] */ /* Check whether the drive supports format 11h */ has_11h = 0; ret = mmc_read_disc_structure(d, 0, 0, 0xff, 4, &reply, &reply_len, 0); if (ret > 0) { for (i = 0; i < reply_len; i += 4) { if (reply[i] == 0x11 && (reply[i + 1] & 64)) has_11h = 1; } } if (reply != NULL) free(reply); reply = NULL; ret = mmc_read_disc_structure(d, 0, 0, 0x11, 29, &reply, &reply_len, 0); if (ret <= 0) { /* Hope for format 00h */ has_11h = 0; } else { /* Dig out manufacturer, media type and revision */ ret = mmc_set_product_id(reply, 19, 27, 28, product_id, media_code1, media_code2, flag & 1); if (ret <= 0) goto ex; } } else if (prf == 0x41 || prf == 0x43 || prf == 0x40 || prf == 0x42) { /* BD */ media_type = 1; ret = mmc_read_disc_structure(d, 1, 0, 0x00, 112, &reply, &reply_len, 0); if (ret <= 0) goto ex; if (reply[0] != 'D' || reply[1] != 'I') { ret = 0; goto ex; } /* Dig out manufacturer, media type and revision */ ret = mmc_set_product_id(reply, 100, 106, 111, product_id, media_code1, media_code2, 2 | (flag & 1)); if (ret <= 0) goto ex; } else { /* Source of DVD-RAM manufacturer and media id not found yet */ ret = 0; goto ex; } if (reply != NULL) free(reply); reply = NULL; ret = mmc_read_disc_structure(d, media_type, 0, 0x00, 1, &reply, &reply_len, 0); if (ret <= 0) goto ex; bt = (reply[0] >> 4) & 0xf; *book_type = calloc(80 + strlen(books[bt]), 1); if (*book_type == NULL) { ret = -1; goto ex; } sprintf(*book_type, "%2.2Xh, %s book [revision %d]", bt, books[bt], reply[0] & 0xf); if (has_11h == 0 && *product_id == NULL && reply_len > 28) { /* DVD+ with no format 11h */ /* Get manufacturer and media type from bytes 19 and 27 */ ret = mmc_set_product_id(reply, 19, 27, 28, product_id, media_code1, media_code2, flag & 1); if (*product_id == NULL) { ret = 0; goto ex; } } ret = 1; ex:; if (reply != NULL) free(reply); if (ret <= 0) { if (*product_id != NULL) free(*product_id); if (*media_code1 != NULL) free(*media_code1); if (*media_code2 != NULL) free(*media_code2); if (*book_type != NULL) free(*book_type); *product_id = *media_code1 = *media_code2 = *book_type = NULL; } return ret; } /* ts B00924 MMC-5, 6.23.3.3.4 Format Code 0Ah: Spare Area Information */ int mmc_get_bd_spare_info(struct burn_drive *d, int *alloc_blocks, int *free_blocks, int flag) { int ret, reply_len, prf; char *reply = NULL; prf = d->current_profile; if (!(prf == 0x41 || prf == 0x43 || prf == 0x42)) return 0; /* Not a BD loaded */ ret = mmc_read_disc_structure(d, 1, 0, 0x0a, 12, &reply, &reply_len, 0); if (ret <= 0) goto ex; *alloc_blocks = mmc_four_char_to_int((unsigned char *) reply + 8); *free_blocks = mmc_four_char_to_int((unsigned char *) reply + 4); ret = 1; ex:; if (reply != NULL) free(reply); return ret; } /* ts B10801 MMC-5, 6.23.3.2.1 Format Code 00h: Physical Format Information 6.23.3.2.16 Format Code 10h: Format Information of Control Data Zone in the Lead-in disk_category */ int mmc_get_phys_format_info(struct burn_drive *d, int *disk_category, char **book_name, int *part_version, int *num_layers, int *num_blocks, int flag) { int ret, reply_len, prf; char *reply = NULL; static char book_names[][16] = { "DVD-ROM", "DVD-RAM", "DVD-R", "DVD-RW", "HD DVD-ROM", "HD DVD-RAM", "HD DVD-R", "unknown", "unknown", "DVD+RW", "DVD+R", "unknown", "unknown", "unknown", "DVD+RW DL", "DVD+R DL", "unknown" }; prf = d->current_profile; if (!(prf == 0x11 || prf == 0x13 || prf == 0x14 || prf == 0x15 || prf == 0x51)) return 0; /* Not a [HD] DVD-R[W] loaded */ ret = mmc_read_disc_structure(d, 0, 0, 0x10, 12, &reply, &reply_len, 0); if (ret <= 0) goto ex; if(reply_len < 12) { libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, "READ DISC STRUCTURE format 10h: Less than 12 bytes", 0, 0); {ret = 0; goto ex;} } *disk_category = (reply[0] >> 4) & 0xf; *book_name = book_names[*disk_category]; *part_version = reply[0] & 0xf; *num_layers = ((reply[2] >> 5) & 0x3) + 1; *num_blocks = ((reply[9] << 16) | (reply[10] << 8) | reply[11]) - ((reply[5] << 16) | (reply[6] << 8) | reply[7]) + 1; ret = 1; ex:; if (reply != NULL) free(reply); return ret; } /* ts A61021 : the mmc specific part of sg.c:enumerate_common() */ int mmc_setup_drive(struct burn_drive *d) { d->read_atip = mmc_read_atip; d->read_toc = mmc_read_toc; d->write = mmc_write; d->erase = mmc_erase; d->read_cd = mmc_read_cd; d->perform_opc = mmc_perform_opc; d->set_speed = mmc_set_speed; d->send_cue_sheet = mmc_send_cue_sheet; d->reserve_track = mmc_reserve_track; d->sync_cache = mmc_sync_cache; d->get_nwa = mmc_get_nwa; d->read_multi_session_c1 = mmc_read_multi_session_c1; d->close_disc = mmc_close_disc; d->close_session = mmc_close_session; d->close_track_session = mmc_close; d->read_buffer_capacity = mmc_read_buffer_capacity; d->format_unit = mmc_format_unit; d->read_format_capacities = mmc_read_format_capacities; d->read_10 = mmc_read_10; /* ts A70302 */ d->phys_if_std = -1; d->phys_if_name[0] = 0; /* ts A61020 */ d->start_lba = -2000000000; d->end_lba = -2000000000; d->do_simulate= 0; /* ts A61201 - A90815*/ d->erasable = 0; d->current_profile = -1; d->current_profile_text[0] = 0; d->current_is_cd_profile = 0; d->current_is_supported_profile = 0; d->current_is_guessed_profile = 0; memset(d->all_profiles, 0, 256); d->num_profiles = 0; d->current_has_feat21h = 0; d->current_feat21h_link_size = -1; d->current_feat23h_byte4 = 0; d->current_feat23h_byte8 = 0; d->current_feat2fh_byte4 = -1; d->next_track_damaged = 0; d->needs_close_session = 0; d->needs_sync_cache = 0; d->bg_format_status = -1; d->num_opc_tables = -1; d->last_lead_in = -2000000000; d->last_lead_out = -2000000000; d->disc_type = 0xff; d->disc_id = 0; memset(d->disc_bar_code, 0, 9); d->disc_app_code = 0; d->disc_info_valid = 0; d->num_format_descr = 0; d->complete_sessions = 0; #ifdef Libburn_disc_with_incomplete_sessioN d->incomplete_sessions = 0; #endif d->state_of_last_session = -1; d->last_track_no = 1; d->media_capacity_remaining = 0; d->media_lba_limit = 0; d->media_read_capacity = 0x7fffffffffffffff; d->mr_capacity_trusted = 0; d->pessimistic_buffer_free = 0; d->pbf_altered = 0; d->wait_for_buffer_free = Libburn_wait_for_buffer_freE; d->nominal_write_speed = 0; d->pessimistic_writes = 0; d->waited_writes = 0; d->waited_tries = 0; d->waited_usec = 0; d->wfb_min_usec = Libburn_wait_for_buffer_min_useC; d->wfb_max_usec = Libburn_wait_for_buffer_max_useC; d->wfb_timeout_sec = Libburn_wait_for_buffer_tio_seC; d->wfb_min_percent = Libburn_wait_for_buffer_min_perC; d->wfb_max_percent = Libburn_wait_for_buffer_max_perC; d->sent_default_page_05 = 0; return 1; } /* @param flag bit0= really add to text, else only count */ static void burn__add_to_text(char *text, int *text_len, char *to_add, int flag) { (*text_len) += strlen(to_add); if (flag & 1) strcat(text, to_add); } /* @param flag bit0= really add to text, else only count */ static void burn__add_hex_to_text(char *text, int *text_len, unsigned char *to_add, int add_length, int flag) { int i, l; (*text_len) += 3 * add_length; if (!(flag & 1)) return; l = strlen(text); for (i = 0; i < add_length; i++) sprintf(text + l + 3 * i, " %2.2x", to_add[i]); } int burn_make_feature_text(struct burn_drive *d, unsigned int feature_code, unsigned char flags, unsigned char additional_length, unsigned char *feature_data, char **text, int flag) { char *feature_name, addon[320], *cpt; int text_len, ret, i, pass; unsigned int phys_is, lmt, num; static unsigned short feature_codes[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x10, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x33, 0x37, 0x38, 0x3a, 0x3b, 0x40, 0x41, 0x42, 0x50, 0x51, 0x80, 0x100, 0x101, 0x102, 0x104, 0x105, 0x106, 0x107, 0x108, 0x109, 0x10a, 0x10b, 0x10c, 0x10d, 0x110, 0xffff }; static char feature_names[][40] = { "Profile List", "Core", "Morphing", "Removable Medium", "Write Protect", "Random Readable", "Multi-Read", "CD Read", "DVD Read", "Random Writable", "Incremental Streaming Writable", "Sector Erasable", "Formattable", "Hardware Defect Management", "Write Once", "Restricted Overwrite", "CD-RW CAV Write", "MRW", "Enhanced Defect Reporting", "DVD+RW", "DVD+R", "Rigid Restricted Overwrite", "CD Track at Once", "CD Mastering", "DVD-R/-RW Write", "Layer Jump Recording", "CD-RW Media Write Support", "BD-R POW", "DVD+RW Dual Layer", "DVD+R Dual Layer", "BD Read Feature", "BD Write Feature", "TSR", "HD DVD Read", "HD DVD Write", "Hybrid Disc", "Power Management", "SMART", "Embedded Changer", "Microcode Upgrade", "Timeout", "DVD-CSS", "Real Time Streaming", "Drive Serial Number", "Media Serial Number", "DCBs", "DVD CPRM", "Firmware Information", "AACS", "VCPS", "" }; static unsigned short legacy_codes[] = { 0x30, 0x31, 0x32, 0x103, 0xffff }; static unsigned int phys_is_codes[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xffff, 0xffffffff }; static char phys_is_names[][40] = { "Unspecified", "SCSI_Family", "ATAPI", "IEEE_1394-1995", "IEEE_1394A", "Fibre_Channel", "IEEE_1394B", "Serial_ATAPI", "USB", "Vendor_Unique", "" }; static char load_mech_names[8][40] = { "Caddy/Slot", "Tray", "Pop-up", "(Reserved)", "Embedded_changer_individually", "Embedded_changer_magazine", "(Reserved)", "(Reserved)" }; feature_name = "(Reserved)"; for (i = 0; feature_codes[i] != 0xffff; i++) { if (feature_codes[i] == feature_code) { feature_name = feature_names[i]; break; } } if (feature_codes[i] == 0xffff) { for (i = 0; legacy_codes[i] != 0xffff; i++) { if (legacy_codes[i] == feature_code) { feature_name = "(Legacy)"; break; } } } if (feature_code >= 0xff00 && feature_code <= 0xffff) feature_name = "(Vendor Specific)"; *text = NULL; for (pass = 0; pass < 2; pass++) { if (pass == 1) { BURN_ALLOC_MEM(*text, char, text_len + 1); } text_len = 0; sprintf(addon, "%4.4x %c : ", feature_code, (flags & 1) ? '+' : '-'); burn__add_to_text(*text, &text_len, addon, pass); burn__add_to_text(*text, &text_len, feature_name, pass); /* Version, Persistent, Current */ sprintf(addon, " : %1.1x,%c :", (flags >> 2) & 15, (flags & 2) ? 'P' : 'N'); burn__add_to_text(*text, &text_len, addon, pass); burn__add_hex_to_text(*text, &text_len, feature_data, (int) additional_length, pass); burn__add_to_text(*text, &text_len, " :", pass); if (feature_code == 0x01 && additional_length >= 4) { /* Core : Physical Interface Standard , INQ2, DBE */ phys_is = mmc_four_char_to_int(feature_data); cpt = "(Not_Recognizable)"; for (i = 0; phys_is_codes[i] != 0xffffffff; i++) { if (phys_is_codes[i] == phys_is) { cpt = phys_is_names[i]; break; } } num = 0; if (additional_length >= 9) num = feature_data[8]; sprintf(addon, " PhysInterface=%x/%s , INQ2=%d , DBE=%d", phys_is, cpt, (num >> 1) & 1, num & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x03 && additional_length >= 1) { /* Removable Medium : Lock, Pvnt Jmpr, Eject, Loading mechanism Type */ num = feature_data[0]; lmt = (num >> 5) & 7; sprintf(addon, " LoadMechType=%x/%s , Eject=%d , PvntJmpr=%d , Lock=%d", lmt, load_mech_names[lmt], (num >> 3) & 1, (num >> 2) & 1, num & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x10 && additional_length >= 4) { /* Random Readable: Logical Block Size, Blocking */ num = 0; if (additional_length >= 6) num = (feature_data[4] << 8) | feature_data[5]; sprintf(addon, " BlockSize=%d , Blocking=%u", mmc_four_char_to_int(feature_data), num); num = 0; if (additional_length >= 7) num = feature_data[6]; sprintf(addon + strlen(addon), " , PP=%d", num & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x1e && additional_length >= 1) { /* CD Read: DAP, C2 Flags, CD-Text */ sprintf(addon, " DAP=%d , C2Flags=%d , CDText=%d", (feature_data[0] >> 7) & 1, (feature_data[0] >> 1) & 1, feature_data[0] & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x1f && additional_length >= 1) { /* DVD Read: MULTI110, Dual-R */ num = 0; if (additional_length >= 3) num = feature_data[2]; sprintf(addon, " MULTI10=%d , DualR=%d", feature_data[0] & 1, num & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x20 && additional_length >= 4) { /* Random Writable: Last LBA, Logical Block Size, Blocking, PP */ num = 0; if (additional_length >= 8) num = mmc_four_char_to_int(feature_data + 4); sprintf(addon, " LastLBA=%d , BlockSize=%u", mmc_four_char_to_int(feature_data), num); num = 0; if (additional_length >= 10) num = (feature_data[8] << 8) | feature_data[9]; sprintf(addon + strlen(addon), " , Blocking=%u", num); num = 0; if (additional_length >= 11) num = feature_data[10]; sprintf(addon + strlen(addon), " , PP=%u", num); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x21 && additional_length >= 2) { /* Incremental Streaming Writable : Data Block Types , TRIO , ARSV , BUF Number of Link Sizes */ num = 0; if (additional_length >= 3) num = feature_data[2]; sprintf(addon, " DataBlockTypes=%2.2x%2.2x , TRIO=%d , ARSV=%d , BUF=%d", feature_data[0], feature_data[1], (num >> 2) & 1, (num >> 1) & 1, num & 1); num = 0; if (additional_length >= 4) num = feature_data[3]; sprintf(addon + strlen(addon), " , NumLinkSizes=%d", num); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x23 && additional_length >= 1) { /* Formattable: RENoSA, Expand, QCert, Cert, RRM */ num = feature_data[0]; sprintf(addon, " RENoSA=%d , Expand=%d , QCert=%d , Cert=%d", (num >> 3) & 1, (num >> 2) & 1, (num >> 1) & 1, num & 1); num = 0; if (additional_length >= 5) num = feature_data[4]; sprintf(addon + strlen(addon), " , RRM=%d", num & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x24 && additional_length >= 1) { /* Defect Management : SSA */ sprintf(addon, " SSA=%d", (feature_data[0] >> 7) & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x28 && additional_length >= 1) { /* MRW */ num = feature_data[0]; sprintf(addon, " DVDPWrite=%d , DVDPRead=%d , CDWrite=%d", (num >> 2) & 1, (num >> 1) & 1, num & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x2a && additional_length >= 1) { /* DVD+RW */ num = 0; if (additional_length >= 2) num = feature_data[1]; sprintf(addon, " Write=%d , QuickStart=%d , CloseOnly=%d", feature_data[0] & 1, (num >> 1) & 1, num & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x2b && additional_length >= 1) { /* DVD+R */ sprintf(addon, " Write=%d", feature_data[0] & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x2c && additional_length >= 1) { /* Rigid Restricted Overwrite */ num = feature_data[0]; sprintf(addon, " DSDG=%d , DSDR=%d , Intermediate=%d , Blank=%d", (num >> 3) & 1, (num >> 2) & 1, (num >> 1) & 1, num & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x2d && additional_length >= 1) { /* CD Track at Once */ num= feature_data[0]; sprintf(addon, " BUF=%d , RWRaw=%d , RWPack=%d , TestWrite=%d , CD-RW=%d , RWSubcode=%d", (num >> 6) & 1, (num >> 4) & 1, (num >> 3) & 1, (num >> 2) & 1, (num >> 1) & 1, num & 1); num = 0; if (additional_length >= 4) num = (feature_data[2] << 8) | feature_data[3]; sprintf(addon + strlen(addon), " , DataTypeSupp=%4.4x", num); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x2e && additional_length >= 1) { /* CD Mastering (SAO) */ num = feature_data[0]; sprintf(addon, " BUF=%d , SAO=%d , RawMS=%d , Raw=%d , TestWrite=%d , CD-RW=%d , RW=%d", (num >> 6) & 1, (num >> 5) & 1, (num >> 4) & 1, (num >> 3) & 1, (num >> 2) & 1, (num >> 1) & 1, num & 1); num = 0; if (additional_length >= 4) num = (feature_data[1] << 16) | (feature_data[2] << 8) | feature_data[3]; sprintf(addon + strlen(addon), " , MaxCueSheetLen=%u", num); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x2f && additional_length >= 1) { /* DVD-R/RW Write */ num = feature_data[0]; sprintf(addon, " BUF=%d , RDL=%d , TestWrite=%d , DVDRW=%d", (num >> 6) & 1, (num >> 3) & 1, (num >> 2) & 1, (num >> 1) & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x33 && additional_length >= 4) { /* Layer Jump Recording */ sprintf(addon, " NumLinkSizes=%d", feature_data[3]); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x37 && additional_length >= 2) { /* CD-RW Media Write Support */ addon[0]= 0; for (i = 7; i >= 0; i--) { sprintf(addon + strlen(addon), " Subtype%d=%d%s", i, (feature_data[1] >> i) & 1, i > 0 ? " ," : ""); } burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x3a && additional_length >= 2) { /* DVD+RW Dual Layer */ sprintf(addon, " Write=%d , QuickStart=%d , CloseOnly=%d", feature_data[0] & 1, (feature_data[1] >> 1) & 1, feature_data[1] & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x3b && additional_length >= 1) { /* DVD+R Dual Layer */ sprintf(addon, " Write=%d", feature_data[0] & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x50 && additional_length >= 1) { /* HD DVD Read */ num = 0; if (additional_length >= 3) num = feature_data[2]; sprintf(addon, " HDDVDR=%d , HDDVDRAM=%d", feature_data[0] & 1, num & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x51 && additional_length >= 1) { /* HD DVD Write */ num = 0; if (additional_length >= 3) num = feature_data[2]; sprintf(addon, " HDDVDR=%d , HDDVDRAM=%d", feature_data[0] & 1, num & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x101 && additional_length >= 1) { /* SMART */ sprintf(addon, " PP=%d", feature_data[0] & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x102 && additional_length >= 1) { /* Embedded Changer */ num = 0; if (additional_length >= 4) num = feature_data[3]; sprintf(addon, " SCC=%d , SDP=%d , HighestSlotNo=%u", (feature_data[0] >> 4) & 1, (feature_data[0] >> 2) & 1, num & 31); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x105 && additional_length >= 1) { /* Timeout */ num = 0; if (additional_length >= 4) num = (feature_data[2] << 8) | feature_data[3]; sprintf(addon, " Group3=%d , UnitLength=%u", feature_data[0] & 1, num); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x106 && additional_length >= 4) { /* DVD CSS */ sprintf(addon, " CSSVersion=%d", (int) feature_data[3]); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x107 && additional_length >= 1) { /* Real Time Streaming */ num = feature_data[0]; sprintf(addon, " RBCB=%d , SCS=%d , MP2A=%d , WSPD=%d , SW=%d", (num >> 4) & 1, (num >> 3) & 1, (num >> 2) & 1, (num >> 1) & 1, num & 1); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x108 && additional_length >= 1) { /* Drive Serial Number */ strcpy(addon, " SerialNumber="); for (i = 0; i < additional_length; i++) { num = feature_data[i]; if (num >= ' ' && num < 127) addon[14 + i] = feature_data[i]; else addon[14 + i] = '?'; } addon[14 + i] = 0; burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x10a && additional_length >= 4) { /* DCBs */ addon[0] = 0; for (i = 0; i < additional_length / 4; i++) sprintf(addon + strlen(addon), " SuppEntry%d=%2.2x%2.2x%2.2x%2.2x%s", i, feature_data[i * 4 + 0], feature_data[i * 4 + 1], feature_data[i * 4 + 2], feature_data[i * 4 + 3], i < additional_length / 4 - 1 ? " ," : ""); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x10b && additional_length >= 4) { /* DVD CPRM */ sprintf(addon, " CPRMVersion=%d", feature_data[3]); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x10c && additional_length >= 14) { /* Firmware Information */ strcpy(addon, " Date="); for (i = 0; i < 14; i += 2) sprintf(addon + 6 + i, "%2.2d", ((feature_data[i] & 0xf) % 10) * 10 + ((feature_data[i + 1] & 0xf) % 10)); burn__add_to_text(*text, &text_len, addon, pass); } else if (feature_code == 0x10d && additional_length >= 4) { /* AACS */ sprintf(addon, " BNG=%d , BlockCountBindingNonce=%d , NumberAGIDs=%d , AACSVersion=%d", feature_data[0] & 1, feature_data[1], feature_data[2] & 0xf, feature_data[3]); burn__add_to_text(*text, &text_len, addon, pass); } } ret = 1; ex:; return ret; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2024 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #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 *); /* ts A61009 : removed redundant parameter d in favor of o->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_session(struct burn_write_opts *o); void mmc_close_disc(struct burn_write_opts *o); void mmc_close(struct burn_drive *, int session, int track); void mmc_get_event(struct burn_drive *); int mmc_write(struct burn_drive *, off_t 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 *); int mmc_read_cd(struct burn_drive *d, int start, int len, int sec_type, int main_ch, const struct burn_read_opts *o, struct buffer *buf, int flag); 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 *); /* ts A61110 : added parameters trackno, lba, nwa. Redefined return value. @return 1=nwa is valid , 0=nwa is not valid , -1=error */ int mmc_get_nwa(struct burn_drive *d, int trackno, int *lba, int *nwa); /* ts B11228 : changed from void to int */ int mmc_send_cue_sheet(struct burn_drive *, struct cue_sheet *); /* ts A61023 : get size and free space of drive buffer */ int mmc_read_buffer_capacity(struct burn_drive *d); /* ts A61021 : the mmc specific part of sg.c:enumerate_common() */ int mmc_setup_drive(struct burn_drive *d); /* ts A61219 : learned much from dvd+rw-tools-7.0: plus_rw_format() and mmc5r03c.pdf, 6.5 FORMAT UNIT */ int mmc_format_unit(struct burn_drive *d, off_t size, int flag); /* ts A61225 : obtain write speed descriptors via ACh GET PERFORMANCE */ int mmc_get_write_performance(struct burn_drive *d); /* ts A61229 : outsourced from spc_select_write_params() */ /* Note: Page data is not zeroed here to allow preset defaults. Thus memset(pd, 0, 2 + d->mdata->write_page_length); is the eventual duty of the caller. */ int mmc_compose_mode_page_5(struct burn_drive *d, struct burn_session *s, int tno, const struct burn_write_opts *o, unsigned char *pd); /* ts A70201 */ int mmc_four_char_to_int(unsigned char *data); /* ts C40226 */ unsigned int mmc_four_char_to_uint(unsigned char *data); /* ts A70201 : Common track info fetcher for mmc_get_nwa() and mmc_fake_toc() */ int mmc_read_track_info(struct burn_drive *d, int trackno, struct buffer *buf, int alloc_len); /* ts A70812 : return 0 = ok , return BE_CANCELLED = error occurred */ int mmc_read_10(struct burn_drive *d, int start, int amount, struct buffer *buf); /* ts A81210 : Determine the upper limit of readable data size */ int mmc_read_capacity(struct burn_drive *d); /* ts A61201 */ char *mmc_obtain_profile_name(int profile_number); /* mmc5r03c.pdf 4.3.4.4.1 d) "The maximum number of RZones is 2 302." */ #define BURN_MMC_FAKE_TOC_MAX_SIZE 2302 /* ts A90903 */ /* MMC backend of API call burn_get_media_product_id() */ int mmc_get_media_product_id(struct burn_drive *d, char **product_id, char **media_code1, char **media_code2, char **book_type, int flag); /* ts A60910 (estimated) */ int mmc_function_spy(struct burn_drive *d, char * text); /* ts A91118 */ int mmc_start_if_needed(struct burn_drive *d, int flag); /* ts B00924 */ int mmc_get_bd_spare_info(struct burn_drive *d, int *alloc_blocks, int *free_blocks, int flag); /* ts B10801 */ int mmc_get_phys_format_info(struct burn_drive *d, int *disk_category, char **book_name, int *part_version, int *num_layers, int *num_blocks, int flag); /* ts B11201 */ int mmc_get_leadin_text(struct burn_drive *d, unsigned char **text_packs, int *num_packs, int flag); /* ts B40107 */ int mmc_get_performance(struct burn_drive *d, int descr_type, int flag); /* ts B90414 */ int burn_make_feature_text(struct burn_drive *d, unsigned int feature_code, unsigned char flags, unsigned char additional_length, unsigned char *feature_data, char **text, int flag); #ifdef Libburn_develop_quality_scaN /* B21108 ts */ int mmc_nec_optiarc_f3(struct burn_drive *d, int sub_op, int start_lba, int rate_period, int *eba, int *error_rate1, int *error_rate2); #endif #endif /*__MMC*/ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "null.h" #include "libburn.h" #include <stdlib.h> #include <string.h> 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 = calloc(1, sizeof(struct burn_source)); src->refcount = 1; src->read = null_read; src->read_sub = NULL; src->get_size = 0; /* ts A70126 */ src->set_size = NULL; src->free_data = NULL; src->data = NULL; return src; } /* -*- 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 */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2017 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libburn.h" #include "options.h" #include "drive.h" #include "transport.h" #include "init.h" #include "write.h" /* ts A61007 */ /* #include <a ssert.h> */ #include <stdlib.h> #include <string.h> #include <stdio.h> #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive) { struct burn_write_opts *opts; opts = calloc(1, sizeof(struct burn_write_opts)); if (opts == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00020111, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Could not allocate new auxiliary object", 0, 0); return NULL; } 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->p2a_valid > 0 && drive->mdata->underrun_proof; opts->perform_opc = 1; opts->obs = -1; #ifdef Libburn_dvd_always_obs_paD opts->obs_pad = 1; #else opts->obs_pad = 0; #endif opts->bdr_obs_exempt = 0; opts->start_byte = -1; opts->fill_up_media = 0; opts->force_is_set = 0; opts->do_stream_recording = 0; opts->dvd_obs_override = 0; opts->stdio_fsync_size = Libburn_stdio_fsync_limiT; opts->text_packs = NULL; opts->num_text_packs = 0; opts->no_text_pack_crc_check = 0; 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) return; if (opts->text_packs != NULL) free(opts->text_packs); free(opts); } int burn_write_opts_clone(struct burn_write_opts *from, struct burn_write_opts **to, int flag) { if (*to != NULL) burn_write_opts_free(*to); if (from == NULL) return 1; *to = calloc(1, sizeof(struct burn_write_opts)); if (*to == NULL) { out_of_mem:; libdax_msgs_submit(libdax_messenger, -1, 0x00000003, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Out of virtual memory", 0, 0); return -1; } memcpy(*to, from, sizeof(struct burn_write_opts)); (*to)->text_packs = NULL; (*to)->num_text_packs = 0; if (from->text_packs != NULL && from->num_text_packs > 0) { (*to)->text_packs = calloc(1, from->num_text_packs * 18); if ((*to)->text_packs == NULL) goto out_of_mem; memcpy((*to)->text_packs, from->text_packs, from->num_text_packs * 18); } (*to)->refcount= 1; return 1; } struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive) { struct burn_read_opts *opts; opts = calloc(1, 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; opts->dap_bit = 0; 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) { int sector_get_outmode(enum burn_write_types write_type, enum burn_block_types block_type); int spc_block_type(enum burn_block_types b); /* ts A61007 */ if (! ( (write_type == BURN_WRITE_SAO && block_type == BURN_BLOCK_SAO) || (opts->drive->block_types[write_type] & block_type) ) ) { bad_combination:; libdax_msgs_submit(libdax_messenger, -1, 0x00020112, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Bad combination of write_type and block_type", 0, 0); return 0; } /* ts A61007 : obsoleting Assert in sector.c:get_outmode() */ if (sector_get_outmode(write_type, (enum burn_block_types) block_type) == -1) goto bad_combination; /* ts A61007 : obsoleting Assert in spc.c:spc_block_type() */ if (spc_block_type((enum burn_block_types) block_type) == -1) goto bad_combination; opts->write_type = write_type; opts->block_type = block_type; return 1; /* a ssert(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 = calloc(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) { opts->simulate = !!sim; return 1; } int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts, int underrun_proof) { if (opts->drive->mdata->p2a_valid <= 0 || 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); } /* ts A61106 */ void burn_write_opts_set_multi(struct burn_write_opts *opts, int multi) { opts->multi = !!multi; } /* ts B31024 */ /* API */ void burn_write_opts_set_fail21h_sev(struct burn_write_opts *opts, char *severity) { int ret, sevno; ret = libdax_msgs__text_to_sev(severity, &sevno, 0); if (ret <= 0) opts->feat21h_fail_sev = 0; else opts->feat21h_fail_sev = sevno; } /* ts B11204 */ /* @param flag bit0=do not verify checksums bit1= repair mismatching checksums bit2= repair checksums if they are 00 00 with each pack */ int burn_write_opts_set_leadin_text(struct burn_write_opts *opts, unsigned char *text_packs, int num_packs, int flag) { int ret; unsigned char *pack_buffer = NULL; if (num_packs > Libburn_leadin_cdtext_packs_maX ) { libdax_msgs_submit(libdax_messenger, opts->drive->global_index, 0x0002018b, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Too many CD-TEXT packs", 0, 0); ret= 0; goto ex; } if (num_packs > 0) BURN_ALLOC_MEM(pack_buffer, unsigned char, num_packs * 18); if (opts->text_packs != NULL) { free(opts->text_packs); opts->text_packs = NULL; } if (flag & 1) { opts->no_text_pack_crc_check = 1; } else { opts->no_text_pack_crc_check = 0; ret = burn_cdtext_crc_mismatches(text_packs, num_packs, (flag >> 1) & 3); if (ret > 0) { libdax_msgs_submit(libdax_messenger, -1, 0x0002018f, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "CD-TEXT pack CRC mismatch", 0, 0); ret = 0; goto ex; } else if (ret < 0) { libdax_msgs_submit(libdax_messenger, -1, 0x00020190, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, "CD-TEXT pack CRC mismatch had to be corrected", 0, 0); } } if (num_packs > 0) { memcpy(pack_buffer, text_packs, num_packs * 18); opts->text_packs = pack_buffer; pack_buffer = NULL; } opts->num_text_packs = num_packs; ret = 1; ex:; BURN_FREE_MEM(pack_buffer); return ret; } /* ts A61222 */ void burn_write_opts_set_start_byte(struct burn_write_opts *opts, off_t value) { opts->start_byte = value; } /* ts A70207 API */ /** @param flag Bitfield for control purposes: bit0= do not choose type but check the one that is already set bit1= do not issue error messages via burn_msgs queue */ enum burn_write_types burn_write_opts_auto_write_type( struct burn_write_opts *opts, struct burn_disc *disc, char reasons[BURN_REASONS_LEN], int flag) { struct burn_multi_caps *caps = NULL; struct burn_drive *d = opts->drive; struct burn_disc_mode_demands demands; enum burn_write_types wt; int ret, would_do_sao = 0; char *reason_pt; reasons[0] = 0; if (burn_drive_get_bd_r_pow(d)) { strcat(reasons, "MEDIA: unsuitable BD-R Pseudo Overwrite formatting, "); return BURN_WRITE_NONE; } if (d->status != BURN_DISC_BLANK && d->status != BURN_DISC_APPENDABLE){ if (d->status == BURN_DISC_FULL) strcat(reasons, "MEDIA: closed or not recordable, "); else strcat(reasons,"MEDIA: no writeable media detected, "); if (!(flag & 3)) libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002013a, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "No suitable media detected", 0, 0); return BURN_WRITE_NONE; } ret = burn_disc_get_write_mode_demands(disc, opts, &demands, !!opts->fill_up_media); if (ret <= 0) { strcat(reasons, "cannot recognize job demands, "); {wt = BURN_WRITE_NONE; goto ex;} } if (demands.exotic_track && !d->current_is_cd_profile) { if (demands.audio) strcat(reasons, "audio track prohibited by non-CD, "); else strcat(reasons, "exotic track prohibited by non-CD, "); {wt = BURN_WRITE_NONE; goto ex;} } if ((flag & 1) && opts->write_type != BURN_WRITE_SAO) goto try_tao; reason_pt = reasons + strlen(reasons); strcat(reasons, "SAO: "); if (d->status != BURN_DISC_BLANK) { strcat(reasons, "write type SAO works only on blank media, "); goto try_tao; } burn_disc_free_multi_caps(&caps); ret = burn_disc_get_multi_caps(d, BURN_WRITE_SAO, &caps, 0); if (ret < 0) { no_caps:; strcat(reasons, "cannot inquire write mode capabilities, "); {wt = BURN_WRITE_NONE; goto ex;} } else if (ret == 0) { strcat(reasons, "no SAO offered by drive and media, "); goto no_sao; } if ((opts->multi || demands.multi_session) && !caps->multi_session) strcat(reasons, "multi session capability lacking, "); if (demands.will_append) strcat(reasons, "appended session capability lacking, "); if (demands.multi_track && !caps->multi_track) strcat(reasons, "multi track capability lacking, "); if (demands.unknown_track_size == 1 && (caps->might_do_sao == 1 || caps->might_do_sao == 3)) strcat(reasons, "track size unpredictable, "); if (demands.mixed_mode) strcat(reasons, "tracks of different modes mixed, "); if (demands.exotic_track && !d->current_is_cd_profile) strcat(reasons, "non-data track on non-cd, "); else if (d->current_is_cd_profile) if ((d->block_types[BURN_WRITE_TAO] & demands.block_types) != demands.block_types) strcat(reasons, "drive dislikes block type, "); if (d->current_is_cd_profile && opts->fill_up_media) strcat(reasons, "cd sao cannot do media fill up yet, "); if (strcmp(reason_pt, "SAO: ") != 0) goto no_sao; would_do_sao = 1; if (demands.unknown_track_size == 2 && (!(flag & 1)) && (caps->might_do_sao == 1 || caps->might_do_sao == 3)) { strcat(reasons, "would have to use default track sizes, "); goto no_sao; } else if (caps->might_do_sao >= 3 && !(flag & 1)) goto try_tao; do_sao:; if (caps->might_simulate == 0 && opts->simulate && !opts->force_is_set) goto no_simulate; if (!(flag & 1)) burn_write_opts_set_write_type( opts, BURN_WRITE_SAO, BURN_BLOCK_SAO); {wt = BURN_WRITE_SAO; goto ex;} no_sao:; try_tao:; if (opts->num_text_packs > 0) { strcat(reasons, "CD-TEXT: write type SAO required, "); {wt = BURN_WRITE_NONE; goto ex;} } if ((flag & 1) && opts->write_type != BURN_WRITE_TAO) goto try_raw; reason_pt = reasons + strlen(reasons); strcat(reasons, "TAO: "); burn_disc_free_multi_caps(&caps); ret = burn_disc_get_multi_caps(d, BURN_WRITE_TAO, &caps, 0); if (ret < 0) goto no_caps; if (ret == 0) { strcat(reasons, "no TAO offered by drive and media, "); goto no_tao; } if ((opts->multi || demands.multi_session) && !caps->multi_session) strcat(reasons, "multi session capability lacking, "); if (demands.multi_track && !caps->multi_track) strcat(reasons, "multi track capability lacking, "); if (demands.exotic_track && !d->current_is_cd_profile) strcat(reasons, "non-data track on non-cd, "); if (d->current_is_cd_profile && !opts->force_is_set) if ((d->block_types[BURN_WRITE_TAO] & demands.block_types) != demands.block_types) strcat(reasons, "drive dislikes block type, "); if (strcmp(reason_pt, "TAO: ") != 0) goto no_tao; /* ( TAO data/audio block size will be handled automatically ) */ if (caps->might_simulate == 0 && opts->simulate && !opts->force_is_set) goto no_simulate; if (!(flag & 1)) burn_write_opts_set_write_type( opts, BURN_WRITE_TAO, BURN_BLOCK_MODE1); {wt = BURN_WRITE_TAO; goto ex;} no_tao:; if (would_do_sao && !(flag & 1)) goto do_sao; if (!d->current_is_cd_profile) goto no_write_mode; try_raw:; if ((flag & 1) && opts->write_type != BURN_WRITE_RAW) goto no_write_mode; if (!(flag & 1)) /* For now: no automatic raw write modes */ goto no_write_mode; reason_pt = reasons + strlen(reasons); strcat(reasons, "RAW: "); if (!d->current_is_cd_profile) strcat(reasons, "write type RAW prohibited by non-cd, "); else if (d->status != BURN_DISC_BLANK) strcat(reasons, "write type RAW works only on blank media, "); else if ((d->block_types[BURN_WRITE_TAO] & demands.block_types) != demands.block_types) strcat(reasons, "drive dislikes block type, "); if (strcmp(reason_pt, "RAW: ") != 0) goto no_write_mode; if (!opts->force_is_set) goto no_simulate; /* For now: no setting of raw write modes */ {wt = BURN_WRITE_RAW; goto ex;} no_write_mode:; {wt = BURN_WRITE_NONE; goto ex;} no_simulate:; strcat(reasons, "simulation of write job not supported by drive and media, "); {wt = BURN_WRITE_NONE; goto ex;} ex:; burn_disc_free_multi_caps(&caps); if (wt == BURN_WRITE_NONE && !(flag & 3)) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002012b, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Drive offers no suitable write mode with this job", 0, 0); } return wt; } /* ts A70213 : new API function */ void burn_write_opts_set_fillup(struct burn_write_opts *opts,int fill_up_media) { opts->fill_up_media = !!fill_up_media; return; } /* ts A70303: API */ void burn_write_opts_set_force(struct burn_write_opts *opts, int use_force) { opts->force_is_set = !!use_force; } /* ts A80412: API */ void burn_write_opts_set_stream_recording(struct burn_write_opts *opts, int value) { opts->do_stream_recording = value; } /* ts A91115: API */ void burn_write_opts_set_dvd_obs(struct burn_write_opts *opts, int obs) { if (obs != 0 && obs != 32 * 1024 && obs != 64 * 1024) return; opts->dvd_obs_override = obs; } /* ts B20406: API */ void burn_write_opts_set_obs_pad(struct burn_write_opts *opts, int pad) { opts->obs_pad = 2 * !!pad; } /* ts C10909: API */ void burn_write_opts_set_bdr_obs_exempt(struct burn_write_opts *opts, int value) { opts->bdr_obs_exempt = !!value; } /* ts A91115: API */ void burn_write_opts_set_stdio_fsync(struct burn_write_opts *opts, int rhythm) { if (rhythm == -1) opts->stdio_fsync_size = -1; /* never */ else if (rhythm == 0) opts->stdio_fsync_size = Libburn_stdio_fsync_limiT; else if (rhythm == 1) opts->stdio_fsync_size = 0; /* only at end of writing */ else if (rhythm >= 32) opts->stdio_fsync_size = rhythm; } /* ts A70901: API */ struct burn_drive *burn_write_opts_get_drive(struct burn_write_opts *opts) { return opts->drive; } 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; } /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2013 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #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; /* ts A61219 : Output block size to trigger buffer flush if hit. -1 with CD, 32 kB with DVD */ int obs; int obs_pad; /* >0 pad up last block to obs, 0 do not 2 indicates burn_write_opts_set_obs_pad(,1) */ /* 1= do not apply obs_pad=1 to BD-R if not stream recording. */ int bdr_obs_exempt; /* ts A61222 : Start address for media which offer a choice */ off_t start_byte; /* ts A70213 : Whether to fill up the available space on media */ int fill_up_media; /* ts A70303 : Whether to override conformance checks: - the check whether CD write+block type is supported by the drive */ int force_is_set; /* ts A80412 : whether to use WRITE12 with Streaming bit set rather than WRITE10. Speeds up DVD-RAM. Might help with BD-RE. This gets transferred to burn_drive.do_stream_recording */ int do_stream_recording; /* ts A91115 : override value for .obs on DVD media. Only values 0, 32K and 64K are allowed for now. */ int dvd_obs_override; /* ts A91115 : size of the fsync() interval for stdio writing. Values 0 or >= 32 counted in 2 KB blocks. */ int stdio_fsync_size; /* ts B11203 : CD-TEXT */ unsigned char *text_packs; int num_text_packs; int no_text_pack_crc_check; /** 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; /* Whether to keep medium appendable */ unsigned char multi; /* ts B31024 */ /* The severity to be attributed to error messages about failed write attempt with blank DVD-RW, possibly due to falsely reported feature 21h Incremental Streaming Writable */ int feat21h_fail_sev; }; /* Default value for burn_write_opts.stdio_flush_size */ #define Libburn_stdio_fsync_limiT 8192 /* Maximum number of Lead-in text packs. READ TOC/PMA/ATIP can at most return 3640.7 packs. The sequence counters of the packs have 8 bits. There are 8 blocks at most. Thus max 2048 packs. */ #define Libburn_leadin_cdtext_packs_maX 2048 /** 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; /* ts B21119 */ /* >>> Needs API access */ /** Whether to set DAP bit which allows the drive to apply "flaw obscuring mechanisms like audio data mute and interpolate" */ unsigned int dap_bit; }; int burn_write_opts_clone(struct burn_write_opts *from, struct burn_write_opts **to, int flag); #endif /* BURN__OPTIONS_H */ /* os-dummy.h Operating system specific libburn definitions and declarations. Included by os.h in case of compilation for Unknown POSIX like systems with the dummy MMC transport adapter sg-dummy.c Copyright (C) 2009 - 2013 Thomas Schmitt <scdbackup@gmx.net> Provided under GPLv2+ */ /** List of all signals which shall be caught by signal handlers and trigger a graceful abort of libburn. (See man 7 signal.) */ /* Once as system defined macros */ #define BURN_OS_SIGNAL_MACRO_LIST \ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \ SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \ SIGUSR1, SIGUSR2, SIGXCPU /* Once as text 1:1 list of strings for messages and interpreters */ #define BURN_OS_SIGNAL_NAME_LIST \ "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \ "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \ "SIGUSR1", "SIGUSR2", "SIGXCPU" /* The number of above list items */ #define BURN_OS_SIGNAL_COUNT 13 /** The list of all signals which shall surely not be caught. It depends on the particular signal whether it can be ignored or whether it will lead to sudden death of the process. Some signals are not POSIX, but nevertheless ought to be ignored if they are defined. */ #ifdef SIGWINCH #define BURN_OS_SIG_WINCH ,SIGWINCH #define BURN_OS_SIG_WINCH_CNT 1 #else #define BURN_OS_SIG_WINCH #define BURN_OS_SIG_WINCH_CNT 0 #endif #ifdef SIGURG #define BURN_OS_SIG_URG ,SIGURG #define BURN_OS_SIG_URG_CNT 1 #else #define BURN_OS_SIG_URG #define BURN_OS_SIG_URG_CNT 0 #endif /** The combined list of all signals which shall not be caught. */ #define BURN_OS_NON_SIGNAL_MACRO_LIST \ SIGKILL, SIGCHLD, SIGSTOP, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU \ BURN_OS_SIG_WINCH BURN_OS_SIG_URG /* The number of above list items */ #define BURN_OS_NON_SIGNAL_COUNT \ ( 7 + BURN_OS_SIG_WINCH_CNT + BURN_OS_SIG_URG_CNT ) /* The maximum size for a (SCSI) i/o transaction */ /* Important : MUST be at least 32768 ! */ #define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 /* To hold the position of the most recently delivered address from device enumeration. */ struct burn_drive_enumerator_struct { int pos; int info_count; char **info_list; }; #define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ typedef struct burn_drive_enumerator_struct burn_drive_enumerator_t; /* The list of operating system dependent elements in struct burn_drive. Usually they are initialized in sg-*.c:enumerate_common(). */ #define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \ int just_a_dummy; /* os-freebsd.h Operating system specific libburn definitions and declarations. Included by os.h in case of compilation for FreeBSD with CAM Copyright (C) 2006 - 2013 Thomas Schmitt <scdbackup@gmx.net>, Provided under GPLv2+ */ /** List of all signals which shall be caught by signal handlers and trigger a graceful abort of libburn. (See man 7 signal.) */ /* Once as system defined macros */ #define BURN_OS_SIGNAL_MACRO_LIST \ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \ SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \ SIGUSR1, SIGUSR2, SIGXCPU, SIGBUS, SIGPROF, \ SIGSYS, SIGTRAP, SIGVTALRM, SIGXCPU, SIGXFSZ /* Once as text 1:1 list of strings for messages and interpreters */ #define BURN_OS_SIGNAL_NAME_LIST \ "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \ "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \ "SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGBUS", "SIGPROF", \ "SIGSYS", "SIGTRAP", "SIGVTALRM", "SIGXCPU", "SIGXFSZ" /* The number of above list items */ #define BURN_OS_SIGNAL_COUNT 20 /** To list all signals which shall surely not be caught */ #define BURN_OS_NON_SIGNAL_MACRO_LIST \ SIGKILL, SIGCHLD, SIGSTOP, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU, SIGURG, SIGWINCH /* The number of above list items */ #define BURN_OS_NON_SIGNAL_COUNT 9 /* The maximum size for a (SCSI) i/o transaction */ /* Important : MUST be at least 32768 ! */ /* Older BSD info says that 32 kB is maximum. But 64 kB seems to work well on 8-STABLE. It is by default only used with BD in streaming mode. So older systems should still be quite safe with this buffer max size. */ #define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 /** To hold all state information of BSD device enumeration which are now local in sg_enumerate() . So that sg_give_next_adr() can work in BSD and sg_enumerate() can use it. */ #define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ struct burn_drive_enumeration_state; \ typedef struct burn_drive_enumeration_state *burn_drive_enumerator_t; /* The list of operating system dependent elements in struct burn_drive. To be initialized and used within sg-*.c . */ #define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \ struct cam_device* cam; \ int lock_fd; \ int is_ahci; \ /* os-libcdio.h Operating system specific libburn definitions and declarations. Included by os.h in case of compilation for Unknown X/Open-like systems with GNU libcdio MMC transport adapter sg-libcdio.c Copyright (C) 2009 - 2013 Thomas Schmitt <scdbackup@gmx.net> Provided under GPLv2+ */ /** List of all signals which shall be caught by signal handlers and trigger a graceful abort of libburn. (See man 7 signal.) */ /* Once as system defined macros */ #define BURN_OS_SIGNAL_MACRO_LIST \ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \ SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \ SIGUSR1, SIGUSR2, SIGXCPU /* Once as text 1:1 list of strings for messages and interpreters */ #define BURN_OS_SIGNAL_NAME_LIST \ "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \ "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \ "SIGUSR1", "SIGUSR2", "SIGXCPU" /* The number of above list items */ #define BURN_OS_SIGNAL_COUNT 13 /** The list of all signals which shall surely not be caught. It depends on the particular signal whether it can be ignored or whether it will lead to sudden death of the process. Some signals are not POSIX, but nevertheless ought to be ignored if they are defined. */ #ifdef SIGWINCH #define BURN_OS_SIG_WINCH ,SIGWINCH #define BURN_OS_SIG_WINCH_CNT 1 #else #define BURN_OS_SIG_WINCH #define BURN_OS_SIG_WINCH_CNT 0 #endif #ifdef SIGURG #define BURN_OS_SIG_URG ,SIGURG #define BURN_OS_SIG_URG_CNT 1 #else #define BURN_OS_SIG_URG #define BURN_OS_SIG_URG_CNT 0 #endif /** The combined list of all signals which shall not be caught. */ #define BURN_OS_NON_SIGNAL_MACRO_LIST \ SIGKILL, SIGCHLD, SIGSTOP, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU \ BURN_OS_SIG_WINCH BURN_OS_SIG_URG /* The number of above list items */ #define BURN_OS_NON_SIGNAL_COUNT \ ( 7 + BURN_OS_SIG_WINCH_CNT + BURN_OS_SIG_URG_CNT ) /* The maximum size for a (SCSI) i/o transaction */ /* My Blu-ray burner LG GGW-H20 writes junk if stream recording is combined with buffer size 32 kB. So stream recording is allowed only with size 64k. Older BSD info says that 32 kB is maximum. But 64 kB seems to work well on 8-STABLE. It is by default only used with BD in streaming mode. So older systems should still be quite safe with this buffer max size. */ /* Important : MUST be at least 32768 ! */ #define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 /* To hold the position of the most recently delivered address from device enumeration. */ struct burn_drive_enumerator_struct { char **ppsz_cd_drives; char **pos; }; #define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ typedef struct burn_drive_enumerator_struct burn_drive_enumerator_t; /* The list of operating system dependent elements in struct burn_drive. Usually they are initialized in sg-*.c:enumerate_common(). */ #define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \ void *p_cdio; /* actually a pointer to CdIo_t */ \ char libcdio_name[4096]; /* The drive path as used by libcdio */ \ /* os-linux.h Operating system specific libburn definitions and declarations. Included by os.h in case of compilation for Linux kernels 2.4 and 2.6, GNU/Linux SCSI Generic (sg) Copyright (C) 2006 - 2013 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ /** List of all signals which shall be caught by signal handlers and trigger a graceful abort of libburn. (See man 7 signal.) */ /* Once as system defined macros */ #define BURN_OS_SIGNAL_MACRO_LIST \ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \ SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \ SIGUSR1, SIGUSR2, SIGXCPU, SIGBUS, SIGPOLL, \ SIGPROF, SIGSYS, SIGTRAP, SIGVTALRM, SIGXCPU, \ SIGXFSZ /* Once as text 1:1 list of strings for messages and interpreters */ #define BURN_OS_SIGNAL_NAME_LIST \ "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \ "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \ "SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGBUS", "SIGPOLL", \ "SIGPROF", "SIGSYS", "SIGTRAP", "SIGVTALRM", "SIGXCPU", \ "SIGXFSZ" /* The number of above list items */ #define BURN_OS_SIGNAL_COUNT 21 /** To list all signals which shall surely not be caught */ #define BURN_OS_NON_SIGNAL_MACRO_LIST \ SIGKILL, SIGCHLD, SIGSTOP, SIGTSTP, SIGCONT, SIGURG, SIGWINCH, SIGTTIN, SIGTTOU /* The number of above list items */ #define BURN_OS_NON_SIGNAL_COUNT 9 /* The maximum size for a (SCSI) i/o transaction */ /* Important : MUST be at least 32768 ! */ /* ts A70523 : >32k seems not good with kernel 2.4 USB drivers and audio #define BURN_OS_TRANSPORT_BUFFER_SIZE 32768 */ /* ts A80414 : curbed in write.c CD media to Libburn_cd_obS = 32 kiB re-enlarged transport to 64 kiB for BD-RE experiments */ #define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 /* To hold the position of the most recently delivered address from device enumeration. */ struct burn_drive_enumerator_struct { int pos; int info_count; char **info_list; }; #define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ typedef struct burn_drive_enumerator_struct burn_drive_enumerator_t; /* Parameters for sibling list. See sibling_fds, sibling_fnames */ #define BURN_OS_SG_MAX_SIBLINGS 5 #define BURN_OS_SG_MAX_NAMELEN 16 /* The list of operating system dependent elements in struct burn_drive. Usually they are initialized in sg-*.c:enumerate_common(). */ #define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \ int fd; \ \ /* ts A60926 : trying to lock against growisofs /dev/srN, /dev/scdN */ \ int sibling_count; \ int sibling_fds[BURN_OS_SG_MAX_SIBLINGS]; \ /* ts A70409 : DDLP */ \ char sibling_fnames[BURN_OS_SG_MAX_SIBLINGS][BURN_OS_SG_MAX_NAMELEN]; /* os-netbsd.h Operating system specific libburn definitions and declarations. Included by os.h in case of compilation for NetBSD 6 or OpenBSD 5.9 with MMC transport adapter sg-netbsd.c Copyright (C) 2010 - 2016 Thomas Schmitt <scdbackup@gmx.net> provided under GPLv2+ Derived 2014 from libburn/os-solaris.c Adapted 2016 to OpenBSD by help of SASANO Takayoshi <uaa@mx5.nisiq.net> */ /** List of all signals which shall be caught by signal handlers and trigger a graceful abort of libburn. (See man signal.h) */ /* Once as system defined macros */ #define BURN_OS_SIGNAL_MACRO_LIST \ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, \ SIGABRT, SIGEMT, SIGFPE, SIGBUS, SIGSEGV, \ SIGSYS, SIGPIPE, SIGALRM, SIGTERM, SIGXCPU, \ SIGXFSZ, SIGVTALRM, SIGPROF, SIGUSR1, SIGUSR2 /* Once as text 1:1 list of strings for messages and interpreters */ #define BURN_OS_SIGNAL_NAME_LIST \ "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", \ "SIGABRT", "SIGEMT", "SIGFPE", "SIGBUS", "SIGSEGV", \ "SIGSYS", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGXCPU", \ "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGUSR1", "SIGUSR2" /* The number of above list items */ #define BURN_OS_SIGNAL_COUNT 20 #ifdef __OpenBSD__ /** To list all signals which shall surely not be caught */ #define BURN_OS_NON_SIGNAL_MACRO_LIST \ SIGKILL, SIGURG, SIGSTOP, SIGTSTP, SIGCONT, \ SIGCHLD, SIGTTIN, SIGTTOU, SIGIO, SIGWINCH, \ SIGINFO /* The number of above list items */ #define BURN_OS_NON_SIGNAL_COUNT 11 /* ts B60730 */ /* Either OpenBSD or SASANO Takayoshi's LG BH14NS48 throw 2,0,0 on Immed bit with BLANK and SYNCHRONIZE CACHE. Until it is clear that the drive is to blame, the OpenBSD default is not to use Immed. This may be overridden at ./configure time by export CFLAGS CFLAGS="$CFLAGS -DLibburn_do_no_immed_defaulT=0" */ #ifndef Libburn_do_no_immed_defaulT #define Libburn_do_no_immed_defaulT 1 #endif #else /* __OpenBSD__ */ /** To list all signals which shall surely not be caught */ #define BURN_OS_NON_SIGNAL_MACRO_LIST \ SIGKILL, SIGURG, SIGSTOP, SIGTSTP, SIGCONT, \ SIGCHLD, SIGTTIN, SIGTTOU, SIGIO, SIGWINCH, \ SIGINFO, SIGPWR /* The number of above list items */ #define BURN_OS_NON_SIGNAL_COUNT 12 #endif /* ! __OpenBSD__ */ /* The maximum size for a (SCSI) i/o transaction */ /* Important : MUST be at least 32768 ! */ /* My Blu-ray burner LG GGW-H20 writes junk if stream recording is combined with buffer size 32 kB. So stream recording is allowed only with size 64k. */ /* >>> ??? Does it do 64 kB ? */ #define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 /* To hold the position of the most recently delivered address from device enumeration. */ struct burn_drive_enumerator_struct { int cdno; }; #define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ typedef struct burn_drive_enumerator_struct burn_drive_enumerator_t; /* The list of operating system dependent elements in struct burn_drive. Usually they are initialized in sg-*.c:enumerate_common(). */ #define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \ int fd; /* os-solaris.h Operating system specific libburn definitions and declarations. Included by os.h in case of compilation for Solaris based systems, e.g. SunOS 5.11 with Solaris uscsi MMC transport adapter sg-solaris.c Copyright (C) 2010 - 2013 Thomas Schmitt <scdbackup@gmx.net> provided under GPLv2+ */ /** List of all signals which shall be caught by signal handlers and trigger a graceful abort of libburn. (See man signal.h) */ /* Once as system defined macros */ #define BURN_OS_SIGNAL_MACRO_LIST \ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \ SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \ SIGUSR1, SIGUSR2, SIGXCPU /* Once as text 1:1 list of strings for messages and interpreters */ #define BURN_OS_SIGNAL_NAME_LIST \ "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \ "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \ "SIGUSR1", "SIGUSR2", "SIGXCPU" /* The number of above list items */ #define BURN_OS_SIGNAL_COUNT 13 /** To list all signals which shall surely not be caught */ #define BURN_OS_NON_SIGNAL_MACRO_LIST \ SIGKILL, SIGCHLD, SIGSTOP, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU, SIGURG, SIGWINCH /* The number of above list items */ #define BURN_OS_NON_SIGNAL_COUNT 9 /* The maximum size for a (SCSI) i/o transaction */ /* Important : MUST be at least 32768 ! */ /* My Blu-ray burner LG GGW-H20 writes junk if stream recording is combined with buffer size 32 kB. So stream recording is allowed only with size 64k. */ #define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 /* >>> */ /* To hold the position of the most recently delivered address from device enumeration. */ struct burn_drive_enumerator_struct { void *dir; }; #define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ typedef struct burn_drive_enumerator_struct burn_drive_enumerator_t; /* The list of operating system dependent elements in struct burn_drive. Usually they are initialized in sg-*.c:enumerate_common(). */ #define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \ int fd; /* os.h Operating system specific libburn definitions and declarations. The macros defined here are used by libburn modules in order to avoid own system dependent case distinctions. Copyright (C) 2009 - 2016 Thomas Schmitt <scdbackup@gmx.net>, provided under GPLv2+ */ #ifndef BURN_OS_H_INCLUDED #define BURN_OS_H_INCLUDED 1 /* Operating system case distinction */ #ifdef Libburn_use_sg_dummY /* --------- Any other system. With dummy MMC transport sg-dummy.c --------- */ #include "os-dummy.h" #else #ifdef Libburn_use_libcdiO /* -------------------------- X/Open with GNU libcdio ---------------------- */ #include "os-libcdio.h" #else #ifdef __NetBSD__ /* -------------------------- NetBSD with SCIOCCOMMAND --------------------- */ #include "os-netbsd.h" #else #ifdef __OpenBSD__ /* -------------------------- OpenBSD with SCIOCCOMMAND -------------------- */ #include "os-netbsd.h" #else #ifdef __FreeBSD__ /* ----------------------------- FreeBSD with CAM -------------------------- */ #include "os-freebsd.h" #else #ifdef __FreeBSD_kernel__ /* ----------------------- FreeBSD with CAM under Debian ------------------- */ #include "os-freebsd.h" #else #ifdef __linux /* ------- Linux kernels 2.4 and 2.6 with GNU/Linux SCSI Generic (sg) ------ */ #include "os-linux.h" #else #ifdef __sun /* ------- Solaris (e.g. SunOS 5.11) with uscsi ------ */ #include "os-solaris.h" #else /* --------- Any other system. With dummy MMC transport sg-dummy.c --------- */ #include "os-dummy.h" #endif /* ! __sun*/ #endif /* ! __linux */ #endif /* ! __FreeBSD__kernel__ */ #endif /* ! __FreeBSD__ */ #endif /* ! __OpenBSD__ */ #endif /* ! __NetBSD__ */ #endif /* ! Libburn_use_libcdiO */ #endif /* ! Libburn_use_sg_dummY */ #endif /* ! BURN_OS_H_INCLUDED */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2024 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <stdlib.h> #include <unistd.h> #include <signal.h> /* ts A61007 */ /* #include <a ssert.h> */ #include <stdio.h> #include <string.h> #include <ctype.h> #include <fcntl.h> #include <errno.h> /* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #include "sector.h" #include "libburn.h" #include "drive.h" #include "transport.h" /* ts A60925 : obsoleted by libdax_msgs.h #include "message.h" */ #include "crc.h" #include "debug.h" #include "init.h" #include "toc.h" #include "util.h" #include "mmc.h" #include "sg.h" #include "read.h" #include "options.h" /* ts A70812 */ #include "error.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; 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; <- needs to become dynamic memory int speed; /* ts A61007 : if this function gets revived, then these tests have to be done more graceful */ a ssert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000)); a ssert(!d->busy); a ssert(d->toc->valid); a ssert(o->datafd != -1); /* moved up from spc_select_error_params alias d->send_parameters() */ a ssert(d->mdata->valid); /* 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; #ifdef Libburn_no_crc_C crc = 0; /* dummy */ #else crc = crc_ccitt(fakesub + 12, 10); #endif fakesub[22] = crc >> 8; fakesub[23] = crc & 0xFF; write(o->subfd, fakesub, 96); } while (1) { seclen = burn_sector_length_read(d, o); 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))) { 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++; /* session switch to d->currsession */ /* skipping a lead out */ drive_lba = CURRENT_SESSION_START(d); /* XXX more of the same end = burn_track_end(d, d->currsession, 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); /* >>> ts A61009 : ensure page.sectors >= 0 before calling */ /* >>> ts B21123 : Would now be d->read_cd() with with sectype = 0 , mainch = 0xf8 */ d->r ead_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]; int ptr = 2352, i, j, code, fb; int audio = 1; #ifndef Libburn_no_crc_C unsigned short crc; #endif if (o->c2errors) { fb = bitcount(data + ptr, 294); if (fb) { /* bitcount(data + ptr, 294) damaged bits */; } 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]++; } } } #ifndef Libburn_no_crc_C 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"); */; } #endif /* 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 instead of crap static void write_empty_sector(int fd) { static char sec[2352], initialized = 0; if (!initialized) { memset(sec, 0, 2352); initialized = 1; } 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); } */ /** @param flag bit1= be silent on failure bit5= report failure with severity DEBUG */ static int burn_stdio_seek(int fd, off_t byte_address, struct burn_drive *d, int flag) { char msg[80]; if (lseek(fd, byte_address, SEEK_SET) != -1) return 1; if (!(flag & 2)) { sprintf(msg, "Cannot address start byte %.f", (double) byte_address); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020147, (flag & 32) ? LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); } return 0; } /* ts A70904 */ /** @param flag bit0= be silent on data shortage bit5= report data shortage with severity DEBUG */ int burn_stdio_read(int fd, char *buf, int bufsize, struct burn_drive *d, int flag) { int todo, count = 0; for(todo = bufsize; todo > 0; ) { count = read(fd, buf + (bufsize - todo), todo); if(count <= 0) break; todo -= count; } if(todo > 0 && !(flag & 1)) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002014a, (flag & 32) ? LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Cannot read desired amount of data", errno, 0); } if (count < 0) return -1; return (bufsize - todo); } /* With DVD and BD media, the minimum ECC entity is read instead of single blocks. @param flag see burn_read_data() in libburn.h */ static int retry_mmc_read(struct burn_drive *d, int chunksize, int sose_mem, int start, char **wpt, off_t *data_count, int flag) { int i, err, todo; int retry_at, retry_size; retry_at = start; retry_size = chunksize; todo = chunksize; retry_size = 16; /* DVD ECC block size */ if (d->current_is_cd_profile) { retry_size = 1; /* CD block size */ } else if (d->current_profile >= 0x40 && d->current_profile <= 0x43) { retry_size = 32; /* BD cluster size */ } for (i = 0; todo > 0; i++) { if (flag & 2) d->silent_on_scsi_error = 1; else if (flag & 32) d->silent_on_scsi_error = 3; retry_at = start + i * retry_size; if (retry_size > todo) retry_size = todo; err = d->read_10(d, retry_at, retry_size, d->buffer); if (flag & (2 | 32)) d->silent_on_scsi_error = sose_mem; if (err == BE_CANCELLED) return 0; memcpy(*wpt, d->buffer->data, retry_size * 2048); *wpt += retry_size * 2048; *data_count += retry_size * 2048; todo -= retry_size; } return 1; } /* @param flag see burn_read_data() in libburn.h */ static int retry_stdio_read(struct burn_drive *d, int fd, int chunksize, int start, char **wpt, off_t *data_count, int flag) { int i, ret, to_read, todo; ret = burn_stdio_seek(fd, ((off_t) start) * 2048, d, flag & 2); if (ret <= 0) return ret; todo = chunksize * 2048; for (i = 0; todo > 0; i += 2048) { to_read = todo; if (to_read > 2048) to_read = 2048; ret = burn_stdio_read(fd, (char *) d->buffer->data, to_read, d, 1); if (ret <= 0) return 0; memcpy(*wpt, d->buffer->data, to_read); *wpt += to_read; *data_count += to_read; todo -= to_read; } return 1; } /* ts A70812 : API function */ int burn_read_data(struct burn_drive *d, off_t byte_address, char data[], off_t data_size, off_t *data_count, int flag) { int alignment = 2048, start, upto, chunksize = 1, err, cpy_size; int sose_mem = 0, fd = -1, ret; char msg[81], *wpt; struct buffer *buf = NULL, *buffer_mem = d->buffer; /* #define Libburn_read_data_adr_logginG 1 */ #ifdef Libburn_read_data_adr_logginG static FILE *log_fp= NULL; if(log_fp == NULL) log_fp = fopen("/tmp/burn_read_data_log", "a"); if(log_fp!=NULL) fprintf(log_fp, "%d\n", (int) (byte_address / 2048)); #endif /* Libburn_read_data_logginG */ BURN_ALLOC_MEM(buf, struct buffer, 1); *data_count = 0; sose_mem = d->silent_on_scsi_error; if (d->released) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020142, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is not grabbed on random access read", 0, 0); {ret = 0; goto ex;} } if (d->drive_role == 0) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020146, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is a virtual placeholder (null-drive)", 0, 0); {ret = 0; goto ex;} } else if (d->drive_role == 3) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020151, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Read attempt on write-only drive", 0, 0); {ret = 0; goto ex;} } if ((byte_address % alignment) != 0) { sprintf(msg, "Read start address not properly aligned (%d bytes)", alignment); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020143, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); {ret = 0; goto ex;} } if (d->media_read_capacity != 0x7fffffffffffffff && byte_address >= (d->media_read_capacity + 1) * (off_t) 2048) { if (!(flag & 2)) { sprintf(msg, "Read start address %.fs larger than number of readable blocks %.f", (double) (byte_address / 2048 + !!(byte_address % 2048)), (double) (d->media_read_capacity + 1)); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020172, (flag & 32) ? LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } {ret = 0; goto ex;} } if (d->busy != BURN_DRIVE_IDLE) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020145, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is busy on attempt to read data", 0, 0); {ret = 0; goto ex;} } if (d->drive_role != 1) { /* <<< We need _LARGEFILE64_SOURCE defined by the build system. */ #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif fd = d->stdio_fd; if (fd < 0) d->stdio_fd = fd = open(d->devname, O_RDONLY | O_LARGEFILE | O_BINARY); if (fd == -1) { if (errno == EACCES && (flag & 2)) { if (!(flag & 8)) libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020183, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, "Failed to open device (a pseudo-drive) for reading", errno, 0); } else if (errno != ENOENT || !(flag & 2)) libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020005, (flag & 32) && errno == ENOENT ? LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Failed to open device (a pseudo-drive) for reading", errno, 0); ret = 0; if (errno == EACCES && (flag & 8)) ret= -2; goto ex; } ret = burn_stdio_seek(fd, byte_address, d, flag & (2 | 32)); if (ret <= 0) goto ex; } d->busy = BURN_DRIVE_READING_SYNC; d->buffer = buf; start = byte_address / 2048; upto = start + data_size / 2048; if (data_size % 2048) upto++; wpt = data; for (; start < upto; start += chunksize) { chunksize = upto - start; if (chunksize > (BUFFER_SIZE / 2048)) { chunksize = (BUFFER_SIZE / 2048); cpy_size = BUFFER_SIZE; } else cpy_size = data_size - *data_count; if (flag & 2) d->silent_on_scsi_error = 1; else if (flag & 32) d->silent_on_scsi_error = 3; if (flag & 16) { d->had_particular_error &= ~1; if (!d->silent_on_scsi_error) d->silent_on_scsi_error = 2; } if (d->drive_role == 1) { err = d->read_10(d, start, chunksize, d->buffer); } else { ret = burn_stdio_read(fd, (char *) d->buffer->data, cpy_size, d, (flag & 32) | !!(flag & 2)); err = 0; if (ret <= 0) err = BE_CANCELLED; } if (flag & (2 | 16 | 32)) d->silent_on_scsi_error = sose_mem; if (err == BE_CANCELLED) { if ((flag & 16) && (d->had_particular_error & 1)) {ret = -3; goto ex;} /* Retry: with CD read by single blocks with other media: retry in full chunks */ if(flag & 4) goto bad_read; if (d->drive_role == 1) { ret = retry_mmc_read(d, chunksize, sose_mem, start, &wpt, data_count, flag); } else { ret = retry_stdio_read(d, fd, chunksize, start, &wpt, data_count, flag); } if (ret <= 0) goto bad_read; } else { memcpy(wpt, d->buffer->data, cpy_size); wpt += cpy_size; *data_count += cpy_size; } } ret = 1; ex:; BURN_FREE_MEM(buf); d->buffer = buffer_mem; d->busy = BURN_DRIVE_IDLE; return ret; bad_read:; if (!(flag & 2)) libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020000, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "burn_read_data() returns 0", 0, 0); ret = 0; goto ex; } /* ts B21119 : API function*/ int burn_read_audio(struct burn_drive *d, int sector_no, char data[], off_t data_size, off_t *data_count, int flag) { int alignment = 2352, start, upto, chunksize = 1, err, cpy_size, i; int sose_mem = 0, ret; char msg[81], *wpt; struct buffer *buf = NULL, *buffer_mem = d->buffer; BURN_ALLOC_MEM(buf, struct buffer, 1); *data_count = 0; sose_mem = d->silent_on_scsi_error; if (d->released) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020142, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is not grabbed on random access read", 0, 0); {ret = 0; goto ex;} } if (d->drive_role != 1) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020146, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is a virtual placeholder (stdio-drive or null-drive)", 0, 0); {ret = 0; goto ex;} } if ((data_size % alignment) != 0) { sprintf(msg, "Audio read size not properly aligned (%d bytes)", alignment); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002019d, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); {ret = 0; goto ex;} } if (d->busy != BURN_DRIVE_IDLE) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020145, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is busy on attempt to read audio", 0, 0); {ret = 0; goto ex;} } d->busy = BURN_DRIVE_READING_SYNC; d->buffer = buf; start = sector_no; upto = start + data_size / alignment; wpt = data; for (; start < upto; start += chunksize) { chunksize = upto - start; if (chunksize > (BUFFER_SIZE / alignment)) chunksize = (BUFFER_SIZE / alignment); cpy_size = chunksize * alignment; if (flag & 2) d->silent_on_scsi_error = 1; else if (flag & 32) d->silent_on_scsi_error = 3; if (flag & 16) { d->had_particular_error &= ~1; if (!d->silent_on_scsi_error) d->silent_on_scsi_error = 2; } err = d->read_cd(d, start, chunksize, 1, 0x10, NULL, d->buffer, (flag & 8) >> 3); if (flag & (2 | 16 | 32)) d->silent_on_scsi_error = sose_mem; if (err == BE_CANCELLED) { if ((flag & 16) && (d->had_particular_error & 1)) {ret = -3; goto ex;} if(!(flag & 4)) for (i = 0; i < chunksize - 1; i++) { if (flag & 2) d->silent_on_scsi_error = 1; else if (flag & 32) d->silent_on_scsi_error = 3; err = d->read_cd(d, start + i, 1, 1, 0x10, NULL, d->buffer, (flag & 8) >> 3); if (flag & (2 | 32)) d->silent_on_scsi_error = sose_mem; if (err == BE_CANCELLED) break; memcpy(wpt, d->buffer->data, alignment); wpt += alignment; *data_count += alignment; } ret = 0; goto ex; } memcpy(wpt, d->buffer->data, cpy_size); wpt += cpy_size; *data_count += cpy_size; } ret = 1; ex: BURN_FREE_MEM(buf); d->buffer = buffer_mem; d->busy = BURN_DRIVE_IDLE; return ret; } #ifdef Libburn_develop_quality_scaN /* B21108 ts */ int burn_nec_optiarc_rep_err_rate(struct burn_drive *d, int start_lba, int rate_period, int flag) { int ret, lba = 0, error_rate1 = 0, error_rate2 = 0, enabled = 0, dret; /* Sub Operation Code 1 : Enable Error Rate reporting function */ ret = mmc_nec_optiarc_f3(d, 1, start_lba, rate_period, &lba, &error_rate1, &error_rate2); if (ret <= 0) goto ex; enabled = 1; /* >>> Sub Operation Code 2 : Seek to starting address start_lba , rate_period */; /* >>> Loop with Sub Operation Code 3 : Send Error Rate information reply: 4-byte LBA , 2-byte C1/PIE , 2-byte C2/PIF */; ret = 1; ex:; if (enabled) { /* Code F : Disable Error Rate reporting function */ dret = mmc_nec_optiarc_f3(d, 0xf, 0, 0, &lba, &error_rate1, &error_rate2); if (dret < ret) ret = dret; } return ret; } #endif /* Libburn_develop_quality_scaN */ /* -*- 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 */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* scsi block commands */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2010 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <string.h> #include <unistd.h> #include "transport.h" #include "sbc.h" #include "spc.h" #include "options.h" /* ts A70910 debug: for tracing calls which might use open drive fds or for catching SCSI usage of emulated drives. */ int mmc_function_spy(struct burn_drive *d, char * text); /* START STOP UNIT as of SBC-1 and SBC-2 0: Opcode 0x1B 1: bit0= Immed bit1-7= reserved 2: reserved 3: reserved 4: bit0= Start (else Stop unit) bit1= Load/Eject (according to Start resp. Stop) bit2-3= reserved bit4-7= Power Condition 0= Start Valid: process Start and Load/Eject bits 1= assume Active state 2= assume Idle state 3= assume Stanby state (5= SBC-1 only: assume Sleep state) 7= transfer control of power conditions to logical unit 10= force idle condition timer to 0 11= force standby condition timer to 0 All others are reserved. 5: Control (set to 0) */ static unsigned char SBC_LOAD[] = { 0x1b, 0, 0, 0, 3, 0 }; static unsigned char SBC_UNLOAD[] = { 0x1b, 0, 0, 0, 2, 0 }; static unsigned char SBC_START_UNIT[] = { 0x1b, 0, 0, 0, 1, 0 }; static unsigned char SBC_STOP_UNIT[] = { 0x1b, 0, 0, 0, 0, 0 }; void sbc_load(struct burn_drive *d) { struct command *c; c = &(d->casual_command); if (mmc_function_spy(d, "load") <= 0) return; scsi_init_command(c, SBC_LOAD, sizeof(SBC_LOAD)); c->retry = 1; /* ts A70921 : Had to revoke Immed because of LG GSA-4082B */ /* c->opcode[1] |= 1; / * ts A70918 : Immed */ c->dir = NO_TRANSFER; c->timeout = Libburn_mmc_load_timeouT; d->issue_command(d, c); if (c->error) return; /* ts A70923 : Needed regardless of Immed bit. Was once 1 minute, now 5 minutes for loading. If this does not suffice then other commands shall fail righteously. */ spc_wait_unit_attention(d, 300, "waiting after START UNIT (+ LOAD)",0); } void sbc_eject(struct burn_drive *d) { struct command *c; c = &(d->casual_command); if (mmc_function_spy(d, "eject") <= 0) return; scsi_init_command(c, SBC_UNLOAD, sizeof(SBC_UNLOAD)); /* c->opcode[1] |= 1; / * ts A70918 : Immed , ts B00109 : revoked */ c->page = NULL; c->dir = NO_TRANSFER; d->issue_command(d, c); /* ts A70918 : Wait long. A late eject could surprise or hurt user. ts B00109 : Asynchronous eject revoked, as one cannot reliably distinguish out from unready. if (c->error) return; spc_wait_unit_attention(d, 1800, "STOP UNIT (+ EJECT)", 0); */ } /* ts A91112 : Now with flag */ /* @param flag bit0= asynchronous waiting */ int sbc_start_unit_flag(struct burn_drive *d, int flag) { struct command *c; int ret; c = &(d->casual_command); if (mmc_function_spy(d, "start_unit") <= 0) return 0; scsi_init_command(c, SBC_START_UNIT, sizeof(SBC_START_UNIT)); c->retry = 1; if (d->do_no_immed && (flag & 1)) c->timeout = 1800 * 1000; else c->opcode[1] |= (flag & 1); /* ts A70918 : Immed */ c->dir = NO_TRANSFER; d->issue_command(d, c); if (c->error) return 0; if (d->do_no_immed || !(flag & 1)) return 1; /* ts A70918 : asynchronous */ ret = spc_wait_unit_attention(d, 1800, "START UNIT", 0); return ret; } int sbc_start_unit(struct burn_drive *d) { int ret; d->is_stopped = 0; /* no endless starting attempts */ /* Asynchronous, not to block controller by waiting */ ret = sbc_start_unit_flag(d, 1); if (ret <= 0) return ret; /* Synchronous to catch Pioneer DVR-216D which is ready too early. A pending START UNIT can prevent ejecting of the tray. */ ret = sbc_start_unit_flag(d, 0); return ret; } /* ts A90824 : Trying to reduce drive noise */ int sbc_stop_unit(struct burn_drive *d) { struct command *c; int ret; c = &(d->casual_command); if (mmc_function_spy(d, "stop_unit") <= 0) return 0; scsi_init_command(c, SBC_STOP_UNIT, sizeof(SBC_STOP_UNIT)); c->retry = 0; c->opcode[1] |= 1; /* Immed */ c->dir = NO_TRANSFER; d->issue_command(d, c); if (c->error) return 0; ret = spc_wait_unit_attention(d, 1800, "STOP UNIT", 0); d->is_stopped = 1; return ret; } /* ts A61021 : the sbc specific part of sg.c:enumerate_common() */ int sbc_setup_drive(struct burn_drive *d) { d->eject = sbc_eject; d->load = sbc_load; d->start_unit = sbc_start_unit; d->stop_unit = sbc_stop_unit; d->is_stopped = 0; return 1; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2010 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifndef __SBC #define __SBC struct burn_drive; void sbc_load(struct burn_drive *); void sbc_eject(struct burn_drive *); /* ts A61118 */ int sbc_start_unit(struct burn_drive *); /* ts A61021 : the sbc specific part of sg.c:enumerate_common() */ int sbc_setup_drive(struct burn_drive *d); #endif /* __SBC */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2021 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <stdio.h> /* ts A61010 */ /* #include <a ssert.h> */ #include <unistd.h> #include <string.h> #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 "toc.h" #include "write.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; #include "ecma130ab.h" #ifdef Libburn_log_in_and_out_streaM /* ts A61031 */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #endif /* Libburn_log_in_and_out_streaM */ /* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif /*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; } } } } /* @return >=0 : valid , <0 invalid */ int sector_get_outmode(enum burn_write_types write_type, enum burn_block_types block_type) { /* ts A61103 : extended SAO condition to TAO */ if (write_type == BURN_WRITE_SAO || write_type == BURN_WRITE_TAO) return 0; else switch (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; default: return -1; } /* ts A61007 : now handled in burn_write_opts_set_write_type() */ /* a ssert(0); */ /* return BURN_MODE_UNIMPLEMENTED :) */ } /* 0 means "same as inmode" */ static int get_outmode(struct burn_write_opts *o) { /* ts A61007 */ return sector_get_outmode(o->write_type, o->block_type); /* -1 is prevented by check in burn_write_opts_set_write_type() */ /* a ssert(0); */ /* return BURN_MODE_UNIMPLEMENTED :) */ } static void get_bytes(struct burn_track *track, int count, unsigned char *data) { int valid, shortage, curr, i, tr; #ifdef Libburn_log_in_and_out_streaM /* ts A61031 */ static int tee_fd= -1; if(tee_fd==-1) tee_fd= open("/tmp/libburn_sg_readin", O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR); #endif /* Libburn_log_in_and_out_streaM */ /* 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) goto ex; /* Next we use source data */ curr = valid; if (!track->eos) { if (track->source->read != NULL) valid = track->source->read(track->source, data + curr, count - curr); else valid = track->source->read_xt(track->source, data + curr, count - curr); } else valid = 0; if (valid <= 0) { /* ts A61031 : extended from (valid == -1) */ track->eos = 1; valid = 0; } track->sourcecount += valid; #ifdef Libburn_log_in_and_out_streaM /* ts A61031 */ if(tee_fd!=-1 && valid>0) { write(tee_fd, data + curr, valid); } #endif /* Libburn_log_in_and_out_streaM */ curr += valid; shortage = count - curr; if (!shortage) goto ex; /* 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) goto ex; /* ts A61031 - B10103 */ if (shortage >= count) track->track_data_done = 1; if (track->end_on_premature_eoi && shortage >= count && !track->open_ended) { char msg[80]; off_t missing, inp_block_size, track_blocks; inp_block_size = burn_sector_length(track->mode); track_blocks = burn_track_get_sectors_2_v2(track, 1); if (track_blocks < 0) track_blocks = 0; missing = track_blocks * inp_block_size - track->sourcecount; sprintf(msg, "Premature end of input encountered. Missing: %.f bytes", (double) missing); libdax_msgs_submit(libdax_messenger, -1, 0x00020180, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); /* Memorize that premature end of input happened */ track->end_on_premature_eoi = 2; } if (track->open_ended || track->end_on_premature_eoi) goto ex; /* 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; } } ex:; /* ts A61024 : general finalizing processing */ if(shortage) memset(data + curr, 0, shortage); /* this is old icculus.org */ if (track->swap_source_bytes == 1) { for (i = 1; i < count; i += 2) { tr = data[i]; data[i] = data[i-1]; data[i-1] = tr; } } } /* ts B20113 : outsourced from get_sector() */ int sector_write_buffer(struct burn_drive *d, struct burn_track *track, int flag) { int err, i; struct buffer *out; out = d->buffer; if (out->sectors <= 0) return 2; err = d->write(d, d->nwa, out); if (err == BE_CANCELLED) return 0; /* ts A61101 */ if(track != NULL) { track->writecount += out->bytes; track->written_sectors += out->sectors; /* Determine current index */ for (i = d->progress.index; i + 1 < track->indices; i++) { if (track->index[i + 1] > d->nwa + out->sectors) break; d->progress.index = i + 1; } } /* ts A61119 */ d->progress.buffered_bytes += out->bytes; d->nwa += out->sectors; out->bytes = 0; out->sectors = 0; return 1; } /* ts A61009 : seems to hand out sector start pointer in opts->drive->buffer and to count hand outs as well as reserved bytes */ /* ts A61101 : added parameter track for counting written bytes */ static unsigned char *get_sector(struct burn_write_opts *opts, struct burn_track *track, int inmode) { struct burn_drive *d = opts->drive; struct buffer *out = d->buffer; int outmode, seclen, write_ret; unsigned char *ret; outmode = get_outmode(opts); if (outmode == 0) outmode = inmode; /* ts A61009 : react on eventual failure of burn_sector_length() (should not happen if API tested properly). Ensures out->bytes >= out->sectors */ seclen = burn_sector_length(outmode); if (seclen <= 0) return NULL; seclen += burn_subcode_length(outmode); /* ts A61219 : opts->obs is eventually a 32k trigger for DVD */ /* (there is enough buffer size reserve for track->cdxa_conversion) */ if (out->bytes + seclen > BUFFER_SIZE || (opts->obs > 0 && out->bytes + seclen > opts->obs)) { write_ret = sector_write_buffer(d, track, 0); if (write_ret <= 0) return NULL; } ret = out->data + out->bytes; out->bytes += seclen; out->sectors++; return ret; } /* ts A61031 */ /* Revoke the counting of the most recent sector handed out by get_sector() */ static void unget_sector(struct burn_write_opts *opts, int inmode) { struct burn_drive *d = opts->drive; struct buffer *out = d->buffer; int outmode; int seclen; outmode = get_outmode(opts); if (outmode == 0) outmode = inmode; /* ts A61009 : react on eventual failure of burn_sector_length() (should not happen if API tested properly). Ensures out->bytes >= out->sectors */ seclen = burn_sector_length(outmode); if (seclen <= 0) return; seclen += burn_subcode_length(outmode); out->bytes -= seclen; out->sectors--; } /* either inmode == outmode, or outmode == raw. anything else is bad news */ /* ts A61010 : changed type to int in order to propagate said bad news */ /** @return 1 is ok, <= 0 is failure */ static int 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); /* ts A61010 */ /* a ssert(outlen >= inlen); */ if (outlen < inlen || outlen < 0 || inlen < 0) return 0; if ((outmode & BURN_MODE_BITS) == (inmode & BURN_MODE_BITS)) { /* see MMC-5 4.2.3.8.5.3 Block Format for Mode 2 form 1 Data Table 24 Mode 2 Formed Sector Sub-header Format */ if (track != NULL) if (track->cdxa_conversion == 1) inlen += 8; get_bytes(track, inlen, data); if (track != NULL) if (track->cdxa_conversion == 1) memmove(data, data + 8, inlen - 8); return 1; } /* ts A61010 */ /* a ssert(outmode & BURN_MODE_RAW); */ if (!(outmode & BURN_MODE_RAW)) return 0; if (inmode & BURN_MODE1) offset = 16; if (inmode & BURN_MODE_RAW) offset = 0; if (inmode & BURN_AUDIO) offset = 0; /* ts A61010 */ /* a ssert(offset != -1); */ if (offset == -1) return 0; get_bytes(track, inlen, data + offset); return 1; } 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; /* ts A61119 : to silence compiler warnings */ default:; } } 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); #ifdef Libburn_no_crc_C crc = 0; /* dummy */ #else crc = crc_ccitt(q, 10); #endif 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, NULL, mode); if (data == NULL) return 0; /* ts A61010 */ if (convert_data(o, NULL, mode, data) <= 0) return 0; subcode_toc(d, mode, subs); convert_subs(o, mode, subs, data); if (sector_headers(o, data, mode, 1) <= 0) return 0; 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, NULL, mode); if (data == NULL) return 0; /* ts A61010 */ if (convert_data(o, NULL, mode, data) <= 0) return 0; subcode_user(o, subs, tno, control, 0, NULL, 1); convert_subs(o, mode, subs, data); if (sector_headers(o, data, mode, 0) <= 0) return 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, NULL, mode); if (data == NULL) return 0; /* ts A61010 */ if (convert_data(o, NULL, mode, data) <= 0) return 0; /* use last index in track */ subcode_user(o, subs, tno, control, 1, NULL, 1); convert_subs(o, mode, subs, data); if (sector_headers(o, data, mode, 0) <= 0) return 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); #ifdef Libburn_no_crc_C crc = 0; /* dummy */ #else crc = crc_ccitt(q, 10); #endif 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'); /* ts A61008 : obsoleted by test in burn_track_set_isrc() */ /* a ssert(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; } } /* ts A61010 : this cannot happen. Assert for fun ? */ /* a ssert(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; #ifdef Libburn_no_crc_C crc = 0; /* dummy */ #else crc = crc_ccitt(q, 10); #endif 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, NULL, mode); if (!data) return 0; /* ts A61010 */ if (convert_data(o, NULL, mode, data) <= 0) return 0; subcode_lout(o, control, subs); convert_subs(o, mode, subs, data); if (sector_headers(o, data, mode, 0) <= 0) return 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, t->mode); if (data == NULL) return 0; /* ts A61010 */ if (convert_data(o, t, t->mode, data) <= 0) return 0; /* ts A61031 */ if ((t->open_ended || t->end_on_premature_eoi) && t->track_data_done) { unget_sector(o, t->mode); return 2; } /* ts A61219 : allow track without .entry */ if (t->entry == NULL) ; else 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); if (sector_headers(o, data, t->mode, 0) <= 0) return 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; } int sector_headers_is_ok(struct burn_write_opts *o, int mode) { if (mode & BURN_AUDIO) /* no headers for "audio" */ return 1; if (o->write_type == BURN_WRITE_SAO) return 1; /* ts A61031 */ if (o->write_type == BURN_WRITE_TAO) return 1; if (mode & BURN_MODE1) return 2; return 0; } /* ts A90830 : changed return type to int @return 0= failure 1= success */ int sector_headers(struct burn_write_opts *o, unsigned char *out, int mode, int leadin) { #ifdef Libburn_ecma130ab_includeD struct burn_drive *d = o->drive; unsigned int crc; int min, sec, frame; int modebyte = -1; int ret; ret = sector_headers_is_ok(o, mode); if (ret != 2) return !!ret; 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) { #ifdef Libburn_no_crc_C crc = 0; /* dummy */ #else crc = crc_32(out, 2064); #endif 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); burn_rspc_parity_p(out); burn_rspc_parity_q(out); } burn_ecma130_scramble(out); return 1; #else /* Libburn_ecma130ab_includeD */ int ret; ret = sector_headers_is_ok(o, mode); if (ret != 2) return (!! ret); /* ts A90830 : lec.c is copied from cdrdao. I have no idea yet how lec.c implements the Reed-Solomon encoding which is described in ECMA-130 for CD-ROM. So this got removed for now. */ libdax_msgs_submit(libdax_messenger, o->drive->global_index, 0x0002010a, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Raw CD write modes are not supported", 0, 0); return 0; #endif /* ! Libburn_ecma130ab_includeD */ } #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 do not 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: /* ts A61009 : if reactivated then without Assert */ a ssert(0); } } #endif /* this needs more info. subs in the data? control/adr? */ /* ts A61119 : One should not use unofficial compiler extensions. >>> Some day this function needs to be implemented. At least for now the result does not match the "mode" of cdrecord -toc. */ /* #warning sector_identify needs to be written */ int sector_identify(unsigned char *data) { /* burn_ecma130_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; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2010 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #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); /* ts B20113 */ int sector_write_buffer(struct burn_drive *d, struct burn_track *track, int flag); /* ts A61009 */ int sector_headers_is_ok(struct burn_write_opts *o, int mode); int 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 */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2009 - 2011 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /* This is the main operating system dependent SCSI part of libburn. It implements the transport level aspects of SCSI control and command i/o. Present implementation: default dummy which enables libburn only to work with stdio: pseudo drive addresses. For real implementations see sg-linux.c, sg-freebsd.c, sg-libcdio.c */ #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <errno.h> #include <fcntl.h> #include <sys/stat.h> #include <string.h> #include <stdlib.h> #ifdef Libburn_os_has_statvfS #include <sys/statvfs.h> #endif /* Libburn_os_has_stavtfS */ /* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #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" #include "init.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /** Returns the id string of the SCSI transport adapter and eventually needed operating system facilities. This call is usable even if sg_initialize() was not called yet. In that case a preliminary constant message might be issued if detailed info is not available yet. @param msg returns id string @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_id_string(char msg[1024], int flag) { strcpy(msg, "internal X/Open adapter sg-dummy"); return 1; } /** Performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility supporting software components. @param msg returns ids and/or error messages of eventual helpers @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_initialize(char msg[1024], int flag) { return sg_id_string(msg, 0); } /** Performs global finalization of the SCSI transport adapter and eventually needed operating system facilities. Releases globally acquired resources. @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_shutdown(int flag) { return 1; } /** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of struct burn_drive which are defined in os-*.h. This will be called when a burn_drive gets disposed. @param d the drive to be finalized @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_dispose_drive(struct burn_drive *d, int flag) { return 1; } /** Returns the next index number and the next enumerated drive address. The enumeration has to cover all available and accessible drives. It is allowed to return addresses of drives which are not available but under some (even exotic) circumstances could be available. It is on the other hand allowed, only to hand out addresses which can really be used right in the moment of this call. (This implementation chooses the former.) @param idx An opaque handle. Make no own theories about it. @param adr Takes the reply @param adr_size Gives maximum size of reply including final 0 @param initialize 1 = start new, 0 = continue, use no other values for now -1 = finish @return 1 = reply is a valid address , 0 = no further address available -1 = severe error (e.g. adr_size too small) */ int sg_give_next_adr(burn_drive_enumerator_t *idx, char adr[], int adr_size, int initialize) { return 0; } /** Brings all available, not-whitelist-banned, and accessible drives into libburn's list of drives. */ /* ts A61115: replacing call to sg-implementation internals from drive.c */ int scsi_enumerate_drives(void) { libdax_msgs_submit(libdax_messenger, -1, 0x0002016b, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, "No MMC transport adapter is present. Running on sg-dummy.c.", 0, 0); return 1; } /** Tells whether libburn has the given drive in use or exclusively reserved. If it is "open" then libburn will eventually call sg_release() on it when it is time to give up usage and reservation. */ /** Published as burn_drive.drive_is_open() */ int sg_drive_is_open(struct burn_drive * d) { return 0; } /** Opens the drive for SCSI commands and - if burn activities are prone to external interference on your system - obtains an exclusive access lock on the drive. (Note: this is not physical tray locking.) A drive that has been opened with sg_grab() will eventually be handed over to sg_release() for closing and unreserving. */ int sg_grab(struct burn_drive *d) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002016a, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "No MMC transport adapter is present. Running on sg-dummy.c.", 0, 0); return 0; } /** Gives up the drive for SCSI commands and releases eventual access locks. (Note: this is not physical tray locking.) */ int sg_release(struct burn_drive *d) { return 0; } /** Sends a SCSI command to the drive, receives reply and evaluates whether the command succeeded or shall be retried or finally failed. Returned SCSI errors shall not lead to a return value indicating failure. The callers get notified by c->error. An SCSI failure which leads not to a retry shall be notified via scsi_notify_error(). The Libburn_log_sg_commandS facility might be of help when problems with a drive have to be examined. It shall stay disabled for normal use. @return: 1 success , <=0 failure */ int sg_issue_command(struct burn_drive *d, struct command *c) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002016a, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "No MMC transport adapter is present. Running on sg-dummy.c.", 0, 0); return -1; } /** Tries to obtain SCSI address parameters. @return 1 is success , 0 is failure */ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no) { libdax_msgs_submit(libdax_messenger, -1, 0x0002016c, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "No MMC transport adapter is present. Running on sg-dummy.c.", 0, 0); return 0; } /** Tells whether a text is a persistent address as listed by the enumeration functions. */ int sg_is_enumerable_adr(char *adr) { return(0); } /* Return 1 if the given path leads to a regular file or a device that can be fseeked, read, and possibly written with 2 kB granularity. */ int burn_os_is_2k_seekrw(char *path, int flag) { struct stat stbuf; if (stat(path, &stbuf) == -1) return 0; if (S_ISREG(stbuf.st_mode)) return 1; if (S_ISBLK(stbuf.st_mode)) return 1; return 0; } /** Estimate the potential payload capacity of a file address. @param path The address of the file to be examined. If it does not exist yet, then the directory will be inquired. @param bytes The pointed value gets modified, but only if an estimation is possible. @return -2 = cannot perform necessary operations on file object -1 = neither path nor dirname of path exist 0 = could not estimate size capacity of file object 1 = estimation has been made, bytes was set */ int burn_os_stdio_capacity(char *path, off_t write_start, off_t *bytes) { struct stat stbuf; #ifdef Libburn_os_has_statvfS struct statvfs vfsbuf; #endif char *testpath = NULL, *cpt; off_t add_size = 0; int ret; BURN_ALLOC_MEM(testpath, char, 4096); testpath[0] = 0; if (stat(path, &stbuf) == -1) { strcpy(testpath, path); cpt = strrchr(testpath, '/'); if(cpt == NULL) strcpy(testpath, "."); else if(cpt == testpath) testpath[1] = 0; else *cpt = 0; if (stat(testpath, &stbuf) == -1) {ret = -1; goto ex;} #ifdef Libburn_if_this_was_linuX } else if(S_ISBLK(stbuf.st_mode)) { long blocks; blocks = *bytes / 512; fd = open(path, open_mode | O_BINARY); if (fd == -1) {ret = -2; goto ex;} ret = ioctl(fd, BLKGETSIZE, &blocks); close(fd); if (ret == -1) {ret = -2; goto ex;} *bytes = ((off_t) blocks) * (off_t) 512; #endif /* Libburn_if_this_was_linuX */ } else if(S_ISREG(stbuf.st_mode)) { add_size = burn_sparse_file_addsize(write_start, &stbuf); strcpy(testpath, path); } else {ret = 0; goto ex;} if (testpath[0]) { #ifdef Libburn_os_has_statvfS if (statvfs(testpath, &vfsbuf) == -1) {ret = -2; goto ex;} *bytes = add_size + ((off_t) vfsbuf.f_frsize) * (off_t) vfsbuf.f_bavail; #else /* Libburn_os_has_statvfS */ {ret = 0; goto ex;} #endif /* ! Libburn_os_has_stavtfS */ } ret = 1; ex:; BURN_FREE_MEM(testpath); return ret; } /* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ #ifdef Libburn_read_o_direcT /* No special O_DIRECT-like precautions are implemented here */ #endif /* Libburn_read_o_direcT */ int burn_os_open_track_src(char *path, int open_flags, int flag) { int fd; fd = open(path, open_flags | O_BINARY); return fd; } void *burn_os_alloc_buffer(size_t amount, int flag) { void *buf = NULL; buf = calloc(1, amount); return buf; } int burn_os_free_buffer(void *buffer, size_t amount, int flag) { if (buffer == NULL) return 0; free(buffer); return 1; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2006 - 2011 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ /* THIS CODE IS NOT FUNCTIONAL YET !!! */ /* This is the main operating system dependent SCSI part of libburn. It implements the transport level aspects of SCSI control and command i/o. Present implementation: FreeBSD CAM (untested) PORTING: Porting libburn typically will consist of adding a new operating system case to the following switcher files: os.h Operating system specific libburn definitions and declarations. sg.c Operating system dependent transport level modules. and of deriving the following system specific files from existing examples: os-*.h Included by os.h. You will need some general system knowledge about signals and knowledge about the storage object needs of your transport level module sg-*.c. sg-*.c This source module. You will need special system knowledge about how to detect all potentially available drives, how to open them, eventually how to exclusively reserve them, how to perform SCSI transactions, how to inquire the (pseudo-)SCSI driver. You will not need to care about CD burning, MMC or other high-level SCSI aspects. Said sg-*.c operations are defined by a public function interface, which has to be implemented in a way that provides libburn with the desired services: sg_id_string() returns an id string of the SCSI transport adapter. It may be called before initialization but then may return only a preliminary id. sg_initialize() performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility of supporting software components. sg_shutdown() performs global finalizations and releases globally acquired resources. sg_give_next_adr() iterates over the set of potentially useful drive address strings. scsi_enumerate_drives() brings all available, not-whitelist-banned, and accessible drives into libburn's list of drives. sg_dispose_drive() finalizes adapter specifics of struct burn_drive on destruction. Releases resources which were acquired underneath scsi_enumerate_drives(). sg_drive_is_open() tells whether libburn has the given drive in use. sg_grab() opens the drive for SCSI commands and ensures undisturbed access. sg_release() closes a drive opened by sg_grab() sg_issue_command() sends a SCSI command to the drive, receives reply, and evaluates whether the command succeeded or shall be retried or finally failed. sg_obtain_scsi_adr() tries to obtain SCSI address parameters. burn_os_is_2k_seekrw() tells whether the given path leads to a file object that can be used in 2 kB granularity by lseek(2), read(2), and possibly write(2) if not read-only. E.g. a USB stick or a hard disk. burn_os_stdio_capacity() estimates the emulated media space of stdio-drives. burn_os_open_track_src() opens a disk file in a way that offers best throughput with file reading and/or SCSI write command transmission. burn_os_alloc_buffer() allocates a memory area that is suitable for file descriptors issued by burn_os_open_track_src(). The buffer size may be rounded up for alignment reasons. burn_os_free_buffer() delete a buffer obtained by burn_os_alloc_buffer(). Porting hints are marked by the text "PORTING:". Send feedback to libburn-hackers@pykix.org . */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /** PORTING : ------- OS dependent headers and definitions ------ */ #include <errno.h> #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <stdlib.h> #include <string.h> #include <sys/poll.h> #include <camlib.h> #include <cam/scsi/scsi_message.h> #include <cam/scsi/scsi_pass.h> #include <err.h> /* XXX */ /* ts A70909 */ #include <sys/statvfs.h> /** PORTING : ------ libburn portable headers and definitions ----- */ #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" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /* is in portable part of libburn */ int burn_drive_is_banned(char *device_address); /* ------------------------------------------------------------------------ */ /* ts A61115: Private functions. Port only if needed by public functions */ /* (Public functions are listed below) */ /* ------------------------------------------------------------------------ */ /* Helper function for scsi_give_next_adr() */ static int sg_init_enumerator(burn_drive_enumerator_t *idx) { idx->skip_device = 0; if ((idx->fd = open(XPT_DEVICE, O_RDWR)) == -1) { warn("couldn't open %s", XPT_DEVICE); return -1; } memset(&(idx->ccb), 0, sizeof(union ccb)); idx->ccb.ccb_h.path_id = CAM_XPT_PATH_ID; idx->ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; idx->ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; idx->ccb.ccb_h.func_code = XPT_DEV_MATCH; idx->bufsize = sizeof(struct dev_match_result) * 100; idx->ccb.cdm.match_buf_len = idx->bufsize; idx->ccb.cdm.matches = (struct dev_match_result *) calloc(1, idx->bufsize); if (idx->ccb.cdm.matches == NULL) { warnx("cannot allocate memory for matches"); close(idx->fd); return -1; } idx->ccb.cdm.num_matches = 0; idx->i = idx->ccb.cdm.num_matches; /* to trigger buffer load */ /* * We fetch all nodes, since we display most of them in the default * case, and all in the verbose case. */ idx->ccb.cdm.num_patterns = 0; idx->ccb.cdm.pattern_buf_len = 0; return 1; } /* Helper function for scsi_give_next_adr() */ static int sg_next_enumeration_buffer(burn_drive_enumerator_t *idx) { /* * We do the ioctl multiple times if necessary, in case there are * more than 100 nodes in the EDT. */ if (ioctl(idx->fd, CAMIOCOMMAND, &(idx->ccb)) == -1) { warn("error sending CAMIOCOMMAND ioctl"); return -1; } if ((idx->ccb.ccb_h.status != CAM_REQ_CMP) || ((idx->ccb.cdm.status != CAM_DEV_MATCH_LAST) && (idx->ccb.cdm.status != CAM_DEV_MATCH_MORE))) { warnx("got CAM error %#x, CDM error %d\n", idx->ccb.ccb_h.status, idx->ccb.cdm.status); return -1; } return 1; } static int sg_close_drive(struct burn_drive * d) { if (d->cam != NULL) { cam_close_device(d->cam); d->cam = NULL; } return 0; } /* ----------------------------------------------------------------------- */ /* PORTING: Private functions which contain publicly needed functionality. */ /* Their portable part must be performed. So it is probably best */ /* to replace the non-portable part and to call these functions */ /* in your port, too. */ /* ----------------------------------------------------------------------- */ /** Wraps a detected drive into libburn structures and hands it over to libburn drive list. */ static void enumerate_common(char *fname, int bus_no, int host_no, int channel_no, int target_no, int lun_no) { int ret; struct burn_drive out; /* General libburn drive setup */ burn_setup_drive(&out, fname); /* This transport adapter uses SCSI-family commands and models (seems the adapter would know better than its boss, if ever) */ ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no, target_no, lun_no, 0); if (ret<=0) return; /* PORTING: ------------------- non portable part --------------- */ /* Operating system adapter is CAM */ /* Adapter specific handles and data */ out.cam = NULL; /* PORTING: ---------------- end of non portable part ------------ */ /* Adapter specific functions with standardized names */ out.grab = sg_grab; out.release = sg_release; out.drive_is_open = sg_drive_is_open; out.issue_command = sg_issue_command; /* Finally register drive and inquire drive information */ burn_drive_finish_enum(&out); } /* ts A61115 */ /* ------------------------------------------------------------------------ */ /* PORTING: Public functions. These MUST be ported. */ /* ------------------------------------------------------------------------ */ /** Returns the id string of the SCSI transport adapter and eventually needed operating system facilities. This call is usable even if sg_initialize() was not called yet. In that case a preliminary constant message might be issued if detailed info is not available yet. @param msg returns id string @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_id_string(char msg[1024], int flag) { strcpy(msg, "internal FreeBSD CAM adapter sg-freebsd-port"); return 1; } /** Performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility supporting software components. @param msg returns ids and/or error messages of eventual helpers @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_initialize(char msg[1024], int flag) { return sg_id_string(msg, 0); } /** Performs global finalization of the SCSI transport adapter and eventually needed operating system facilities. Releases globally acquired resources. @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_shutdown(int flag) { return 1; } /** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of struct burn_drive which are defined in os-*.h. The eventual initialization of those components was made underneath scsi_enumerate_drives(). This will be called when a burn_drive gets disposed. @param d the drive to be finalized @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_dispose_drive(struct burn_drive *d, int flag) { return 1; } /** Returns the next index number and the next enumerated drive address. The enumeration has to cover all available and accessible drives. It is allowed to return addresses of drives which are not available but under some (even exotic) circumstances could be available. It is on the other hand allowed, only to hand out addresses which can really be used right in the moment of this call. (This implementation chooses the latter.) @param idx An opaque handle. Make no own theories about it. @param adr Takes the reply @param adr_size Gives maximum size of reply including final 0 @param initialize 1 = start new, 0 = continue, use no other values for now -1 = finish @return 1 = reply is a valid address , 0 = no further address available -1 = severe error (e.g. adr_size too small) */ int sg_give_next_adr(burn_drive_enumerator_t *idx, char adr[], int adr_size, int initialize) { int ret; if (initialize == 1) { ret = sg_init_enumerator(idx); if (ret<=0) return ret; } else if (initialize == -1) { if(idx->fd != -1) close(idx->fd); idx->fd = -1; return 0; } try_item:; /* This spaghetti loop keeps the number of tabs small */ /* Loop content from old scsi_enumerate_drives() */ while (idx->i >= idx->ccb.cdm.num_matches) { ret = sg_next_enumeration_buffer(idx); if (ret<=0) return -1; if (!((idx->ccb.ccb_h.status == CAM_REQ_CMP) && (idx->ccb.cdm.status == CAM_DEV_MATCH_MORE)) ) return 0; idx->i = 0; } switch (idx->ccb.cdm.matches[idx->i].type) { case DEV_MATCH_BUS: break; case DEV_MATCH_DEVICE: { struct device_match_result* result; result = &(idx->ccb.cdm.matches[i].result.device_result); if (result->flags & DEV_RESULT_UNCONFIGURED) idx->skip_device = 1; else idx->skip_device = 0; break; } case DEV_MATCH_PERIPH: { struct periph_match_result* result; char buf[64]; result = &(idx->ccb.cdm.matches[i].result.periph_result); if (idx->skip_device || strcmp(result->periph_name, "pass") == 0) break; snprintf(buf, sizeof (buf), "/dev/%s%d", result->periph_name, result->unit_number); if(adr_size <= strlen(buf)) return -1; strcpy(adr, buf); /* Found next enumerable address */ return 1; } default: /* printf(stderr, "unknown match type\n"); */ break; } (idx->i)++; goto try_item; /* Regular function exit is return 1 above */ } /** Brings all available, not-whitelist-banned, and accessible drives into libburn's list of drives. */ int scsi_enumerate_drives(void) { burn_drive_enumerator_t idx; int initialize = 1, ret; char buf[64]; while(1) { ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); initialize = 0; if (ret <= 0) break; if (burn_drive_is_banned(buf)) continue; enumerate_common(buf, idx.result->path_id, idx.result->path_id, 0, idx.result->target_id, idx.result->target_lun); } sg_give_next_adr(&idx, buf, sizeof(buf), -1); return 1; } /** Tells whether libburn has the given drive in use or exclusively reserved. If it is "open" then libburn will eventually call sg_release() on it when it is time to give up usage and reservation. */ /** Published as burn_drive.drive_is_open() */ int sg_drive_is_open(struct burn_drive * d) { return (d->cam != NULL); } /** Opens the drive for SCSI commands and - if burn activities are prone to external interference on your system - obtains an exclusive access lock on the drive. (Note: this is not physical tray locking.) A drive that has been opened with sg_grab() will eventually be handed over to sg_release() for closing and unreserving. */ int sg_grab(struct burn_drive *d) { struct cam_device *cam; if(d->cam != NULL) { d->released = 0; return 1; } cam = cam_open_device(d->devname, O_RDWR); if (cam == NULL) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Could not grab drive", 0/*os_errno*/, 0); return 0; } d->cam = cam; fcntl(cam->fd, F_SETOWN, getpid()); d->released = 0; return 1; } /** PORTING: Is mainly about the call to sg_close_drive() and whether it implements the demanded functionality. */ /** Gives up the drive for SCSI commands and releases eventual access locks. (Note: this is not physical tray locking.) */ int sg_release(struct burn_drive *d) { if (d->cam == NULL) return 0; sg_close_drive(d); return 0; } /** Sends a SCSI command to the drive, receives reply and evaluates whether the command succeeded or shall be retried or finally failed. Returned SCSI errors shall not lead to a return value indicating failure. The callers get notified by c->error. An SCSI failure which leads not to a retry shall be notified via scsi_notify_error(). The Libburn_log_sg_commandS facility might be of help when problems with a drive have to be examined. It shall stay disabled for normal use. @return: 1 success , <=0 failure */ int sg_issue_command(struct burn_drive *d, struct command *c) { int done = 0; int err; union ccb *ccb; if (d->cam == NULL) { c->error = 0; return 0; } c->error = 0; ccb = cam_getccb(d->cam); cam_fill_csio(&ccb->csio, 1, /* retries */ NULL, /* cbfncp */ CAM_DEV_QFRZDIS, /* flags */ MSG_SIMPLE_Q_TAG, /* tag_action */ NULL, /* data_ptr */ 0, /* dxfer_len */ sizeof (ccb->csio.sense_data), /* sense_len */ 0, /* cdb_len */ 30*1000); /* timeout */ switch (c->dir) { case TO_DRIVE: ccb->csio.ccb_h.flags |= CAM_DIR_OUT; break; case FROM_DRIVE: ccb->csio.ccb_h.flags |= CAM_DIR_IN; break; case NO_TRANSFER: ccb->csio.ccb_h.flags |= CAM_DIR_NONE; break; } ccb->csio.cdb_len = c->oplen; memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen); if (c->page) { ccb->csio.data_ptr = c->page->data; if (c->dir == FROM_DRIVE) { ccb->csio.dxfer_len = BUFFER_SIZE; /* touch page so we can use valgrind */ memset(c->page->data, 0, BUFFER_SIZE); } else { /* ts A61115: removed a ssert() */ if(c->page->bytes <= 0) return 0; ccb->csio.dxfer_len = c->page->bytes; } } else { ccb->csio.data_ptr = NULL; ccb->csio.dxfer_len = 0; } /* ts B90523 : Record effective transfer length request for debugging*/ c->dxfer_len = ccb->csio.dxfer_len; do { memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); err = cam_send_ccb(d->cam, ccb); if (err == -1) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Failed to transfer command to drive", errno, 0); cam_freeccb(ccb); sg_close_drive(d); d->released = 1; d->busy = BURN_DRIVE_IDLE; c->error = 1; return -1; } /* XXX */ memcpy(c->sense, &ccb->csio.sense_data, ccb->csio.sense_len); c->sense_len = ccb->csio.sense_len; if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (!c->retry) { c->error = 1; cam_freeccb(ccb); return 1; } switch (scsi_error(d, c->sense, 0)) { case RETRY: done = 0; break; case FAIL: done = 1; c->error = 1; break; } } else { done = 1; } if (!done) spc_register_retry(c); } while (!done); cam_freeccb(ccb); return 1; } /** Tries to obtain SCSI address parameters. @return 1 is success , 0 is failure */ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no) { burn_drive_enumerator_t idx; int initialize = 1, ret; char buf[64]; struct periph_match_result* result; while(1) { ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); initialize = 0; if (ret <= 0) break; if (strcmp(path, buf) != 0) continue; result = &(idx->ccb.cdm.matches[i].result.periph_result); *bus_no = result->path_id; *host_no = result->path_id; *channel_no = 0; *target_no = result->target_id *lun_no = result->target_lun; sg_give_next_adr(&idx, buf, sizeof(buf), -1); return 1; } sg_give_next_adr(&idx, buf, sizeof(buf), -1); return (0); } /** Tells whether a text is a persistent address as listed by the enumeration functions. */ int sg_is_enumerable_adr(char* adr) { burn_drive_enumerator_t idx; int initialize = 1, ret; char buf[64]; while(1) { ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); initialize = 0; if (ret <= 0) break; if (strcmp(adr, buf) == 0) { sg_give_next_adr(&idx, buf, sizeof(buf), -1); return 1; } } sg_give_next_adr(&idx, buf, sizeof(buf), -1); return (0); } /* ts B00115 */ /* Return 1 if the given path leads to a regular file or a device that can be seeked, written, and read with 2 kB granularity. */ int burn_os_is_2k_seekrw(char *path, int flag) { struct stat stbuf; char *spt; int i, e; if (stat(path, &stbuf) == -1) return 0; if (S_ISREG(stbuf.st_mode)) return 1; if (!S_ISCHR(stbuf.st_mode)) return 0; spt = strrchr(path, '/'); if (spt == NULL) spt = path; else spt++; e = strlen(spt); for (i = strlen(spt) - 1; i > 0; i--) if (spt[i] >= '0' && spt[i] <= '9') e = i; if (strncmp(spt, "da", e) == 0) /* SCSI disk. E.g. USB stick. */ return 1; if (strncmp(spt, "cd", e) == 0) /* SCSI CD drive might be writeable. */ return 1; if (strncmp(spt, "ad", e) == 0) /* IDE hard drive */ return 1; if (strncmp(spt, "acd", e) == 0) /* IDE CD drive might be writeable */ return 1; if (strncmp(spt, "fd", e) == 0) /* Floppy disk */ return 1; if (strncmp(spt, "fla", e) == 0) /* Flash drive */ return 1; return 0; } /* ts A70909 */ /** Estimate the potential payload capacity of a file address. @param path The address of the file to be examined. If it does not exist yet, then the directory will be inquired. @param bytes This value gets modified if an estimation is possible @return -2 = cannot perform necessary operations on file object -1 = neither path nor dirname of path exist 0 = could not estimate size capacity of file object 1 = estimation has been made, bytes was set */ int burn_os_stdio_capacity(char *path, off_t write_start, off_t *bytes) { struct stat stbuf; struct statvfs vfsbuf; char *testpath = NULL, *cpt; off_t add_size = 0; int fd, ret; BURN_ALLOC_MEM(testpath, char, 4096); testpath[0] = 0; if (stat(path, &stbuf) == -1) { strcpy(testpath, path); cpt = strrchr(testpath, '/'); if(cpt == NULL) strcpy(testpath, "."); else if(cpt == testpath) testpath[1] = 0; else *cpt = 0; if (stat(testpath, &stbuf) == -1) {ret = -1; goto ex;} #ifdef Libburn_if_this_was_linuX } else if(S_ISBLK(stbuf.st_mode)) { int open_mode = O_RDWR, fd, ret; long blocks; blocks = *bytes / 512; if(burn_sg_open_o_excl) open_mode |= O_EXCL; fd = open(path, open_mode); if (fd == -1) {ret = -2; goto ex;} ret = ioctl(fd, BLKGETSIZE, &blocks); close(fd); if (ret == -1) {ret = -2; goto ex;} *bytes = ((off_t) blocks) * (off_t) 512; #endif /* Libburn_if_this_was_linuX */ } else if(S_ISCHR(stbuf.st_mode)) { fd = open(path, O_RDONLY); if (fd == -1) {ret = -2; goto ex;} ret = ioctl(fd, DIOCGMEDIASIZE, &add_size); close(fd); if (ret == -1) {ret = -2; goto ex;} *bytes = add_size; } else if(S_ISREG(stbuf.st_mode)) { add_size = burn_sparse_file_addsize(write_start, &stbuf); strcpy(testpath, path); } else {ret = 0; goto ex;} if (testpath[0]) { if (statvfs(testpath, &vfsbuf) == -1) {ret = -2; goto ex;} *bytes = add_size + ((off_t) vfsbuf.f_frsize) * (off_t) vfsbuf.f_bavail; } ret = 1; ex: BURN_FREE_MEM(testpath); return ret; } /* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ #ifdef Libburn_read_o_direcT /* No special O_DIRECT-like precautions are implemented here */ #endif /* Libburn_read_o_direcT */ int burn_os_open_track_src(char *path, int open_flags, int flag) { int fd; fd = open(path, open_flags); return fd; } void *burn_os_alloc_buffer(size_t amount, int flag) { void *buf = NULL; buf = calloc(1, amount); return buf; } int burn_os_free_buffer(void *buffer, size_t amount, int flag) { if (buffer == NULL) return 0; free(buffer); return 1; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2006 - 2013 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later and under FreeBSD license revised, i.e. without advertising clause. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <errno.h> #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <sys/file.h> #include <stdlib.h> #include <string.h> #include <sys/poll.h> #include <camlib.h> #include <cam/scsi/scsi_message.h> #include <cam/scsi/scsi_pass.h> #include <err.h> /* XXX */ /* ts A70909 */ #include <sys/statvfs.h> /* ts B00121 */ #include <sys/disk.h> /* DIOCGMEDIASIZE */ /* ts B00326 : For use of CAM_PASS_ERR_RECOVER with ahci */ #define Libburn_for_freebsd_ahcI yes /* ts B00327 : for debugging of cam_send_cdb() failures # define Libburn_ahci_verbouS yes */ /* ts B00327 : Apply CAM_PASS_ERR_RECOVER to drives even if not ahci # define libburn_ahci_style_for_alL yes */ #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" #include "init.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; struct burn_drive_enumeration_state { int fd; union ccb ccb; unsigned int i; int skip_device; }; static void enumerate_common(char *fname, int bus_no, int host_no, int channel_no, int target_no, int lun_no); /* ts A51221 */ int burn_drive_is_banned(char *device_address); /* ts A60821 debug: for tracing calls which might use open drive fds or for catching SCSI usage of emulated drives. */ int mmc_function_spy(struct burn_drive *d, char * text); /* ts B00113 Whether to log SCSI commands: bit0= log in /tmp/libburn_sg_command_log bit1= log to stderr bit2= flush every line */ extern int burn_sg_log_scsi; /* ts B00114 */ /* Storage object is in libburn/init.c whether to strive for exclusive access to the drive */ extern int burn_sg_open_o_excl; /* ts A91227 */ /** Returns the id string of the SCSI transport adapter and eventually needed operating system facilities. This call is usable even if sg_initialize() was not called yet. In that case a preliminary constant message might be issued if detailed info is not available yet. @param msg returns id string @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_id_string(char msg[1024], int flag) { strcpy(msg, "internal FreeBSD CAM adapter sg-freebsd"); return 1; } /* ts A91227 */ /** Performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility supporting software components. @param msg returns ids and/or error messages of eventual helpers @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_initialize(char msg[1024], int flag) { return sg_id_string(msg, 0); } /* ts A91227 */ /** Performs global finalization of the SCSI transport adapter and eventually needed operating system facilities. Releases globally acquired resources. @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_shutdown(int flag) { return 1; } /** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of struct burn_drive which are defined in os-*.h. The eventual initialization of those components was made underneath scsi_enumerate_drives(). This will be called when a burn_drive gets disposed. @param d the drive to be finalized @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_dispose_drive(struct burn_drive *d, int flag) { return 1; } /* ts A61021 : Moved most code from scsi_enumerate_drives under sg_give_next_adr() */ /* Some helper functions for scsi_give_next_adr() */ static int sg_init_enumerator(burn_drive_enumerator_t *idx_) { struct burn_drive_enumeration_state *idx; int bufsize; idx = calloc(1, sizeof(*idx)); if (idx == NULL) { warnx("cannot allocate memory for enumerator"); return -1; } idx->skip_device = 0; if ((idx->fd = open(XPT_DEVICE, O_RDWR)) == -1) { warn("could not open %s", XPT_DEVICE); free(idx); idx = NULL; return -1; } memset(&(idx->ccb), 0, sizeof(union ccb)); idx->ccb.ccb_h.path_id = CAM_XPT_PATH_ID; idx->ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; idx->ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; idx->ccb.ccb_h.func_code = XPT_DEV_MATCH; bufsize = sizeof(struct dev_match_result) * 100; idx->ccb.cdm.match_buf_len = bufsize; idx->ccb.cdm.matches = (struct dev_match_result *) calloc(1, bufsize); if (idx->ccb.cdm.matches == NULL) { warnx("cannot allocate memory for matches"); close(idx->fd); free(idx); return -1; } idx->ccb.cdm.num_matches = 0; idx->i = idx->ccb.cdm.num_matches; /* to trigger buffer load */ /* * We fetch all nodes, since we display most of them in the default * case, and all in the verbose case. */ idx->ccb.cdm.num_patterns = 0; idx->ccb.cdm.pattern_buf_len = 0; *idx_ = idx; return 1; } static void sg_destroy_enumerator(burn_drive_enumerator_t *idx_) { struct burn_drive_enumeration_state *idx = *idx_; if(idx->fd != -1) close(idx->fd); free(idx->ccb.cdm.matches); free(idx); *idx_ = NULL; } static int sg_next_enumeration_buffer(burn_drive_enumerator_t *idx_) { struct burn_drive_enumeration_state *idx = *idx_; /* * We do the ioctl multiple times if necessary, in case there are * more than 100 nodes in the EDT. */ if (ioctl(idx->fd, CAMIOCOMMAND, &(idx->ccb)) == -1) { warn("error sending CAMIOCOMMAND ioctl"); return -1; } if ((idx->ccb.ccb_h.status != CAM_REQ_CMP) || ((idx->ccb.cdm.status != CAM_DEV_MATCH_LAST) && (idx->ccb.cdm.status != CAM_DEV_MATCH_MORE))) { warnx("got CAM error %#x, CDM error %d\n", idx->ccb.ccb_h.status, idx->ccb.cdm.status); return -1; } return 1; } /** Returns the next index object state and the next enumerated drive address. @param idx An opaque handle. Make no own theories about it. @param adr Takes the reply @param adr_size Gives maximum size of reply including final 0 @param initialize 1 = start new, 0 = continue, use no other values for now -1 = finish @return 1 = reply is a valid address , 0 = no further address available -1 = severe error (e.g. adr_size too small) */ int sg_give_next_adr(burn_drive_enumerator_t *idx_, char adr[], int adr_size, int initialize) { struct burn_drive_enumeration_state *idx; int ret; if (initialize == 1) { ret = sg_init_enumerator(idx_); if (ret<=0) return ret; } else if (initialize == -1) { sg_destroy_enumerator(idx_); return 0; } idx = *idx_; do { if (idx->i >= idx->ccb.cdm.num_matches) { ret = sg_next_enumeration_buffer(idx_); if (ret<=0) return -1; idx->i = 0; } else (idx->i)++; while (idx->i < idx->ccb.cdm.num_matches) { switch (idx->ccb.cdm.matches[idx->i].type) { case DEV_MATCH_BUS: break; case DEV_MATCH_DEVICE: { struct device_match_result* result; result = &(idx->ccb.cdm.matches[idx->i].result.device_result); if (result->flags & DEV_RESULT_UNCONFIGURED) idx->skip_device = 1; else idx->skip_device = 0; break; } case DEV_MATCH_PERIPH: { struct periph_match_result* result; result = &(idx->ccb.cdm.matches[idx->i].result.periph_result); /* ts B00112 : we really want only "cd" devices. if (idx->skip_device || strcmp(result->periph_name, "pass") == 0) break; */ if (idx->skip_device || strcmp(result->periph_name, "cd") != 0) break; ret = snprintf(adr, adr_size, "/dev/%s%d", result->periph_name, result->unit_number); if(ret >= adr_size) return -1; /* Found next enumerable address */ return 1; } default: /* fprintf(stderr, "unknown match type\n"); */ break; } (idx->i)++; } } while ((idx->ccb.ccb_h.status == CAM_REQ_CMP) && (idx->ccb.cdm.status == CAM_DEV_MATCH_MORE)); return 0; } int sg_is_enumerable_adr(char* adr) { burn_drive_enumerator_t idx; int ret; char buf[64]; ret = sg_init_enumerator(&idx); if (ret <= 0) return 0; while(1) { ret = sg_give_next_adr(&idx, buf, sizeof(buf), 0); if (ret <= 0) break; if (strcmp(adr, buf) == 0) { sg_destroy_enumerator(&idx); return 1; } } sg_destroy_enumerator(&idx); return (0); } /** Try to obtain SCSI address parameters. @return 1 is success , 0 is failure */ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no) { burn_drive_enumerator_t idx; int ret; char buf[64]; struct periph_match_result* result; ret = sg_init_enumerator(&idx); if (ret <= 0) return 0; while(1) { ret = sg_give_next_adr(&idx, buf, sizeof(buf), 0); if (ret <= 0) break; if (strcmp(path, buf) == 0) { result = &(idx->ccb.cdm.matches[idx->i].result.periph_result); *bus_no = result->path_id; *host_no = result->path_id; *channel_no = 0; *target_no = result->target_id; *lun_no = result->target_lun; sg_destroy_enumerator(&idx); return 1; } } sg_destroy_enumerator(&idx); return (0); } int sg_close_drive(struct burn_drive * d) { if (d->cam != NULL) { cam_close_device(d->cam); d->cam = NULL; } if (d->lock_fd > 0) { close(d->lock_fd); d->lock_fd = -1; } return 0; } int sg_drive_is_open(struct burn_drive * d) { return (d->cam != NULL); } int scsi_enumerate_drives(void) { burn_drive_enumerator_t idx; int ret; char buf[64]; struct periph_match_result* result; ret = sg_init_enumerator(&idx); if (ret <= 0) return 0; while(1) { ret = sg_give_next_adr(&idx, buf, sizeof(buf), 0); if (ret <= 0) break; if (burn_drive_is_banned(buf)) continue; result = &idx->ccb.cdm.matches[idx->i].result.periph_result; enumerate_common(buf, result->path_id, result->path_id, 0, result->target_id, result->target_lun); } sg_destroy_enumerator(&idx); return 1; } #ifdef Scsi_freebsd_make_own_enumeratE /* ts A61021: The old version which mixes SCSI and operating system adapter */ static void enumerate_common(char *fname, int bus_no, int host_no, int channel_no, int target_no, int lun_no) { struct burn_drive *t; struct burn_drive out; /* Initialize pointers to managed memory */ out.devname = NULL; out.idata = NULL; out.mdata = NULL; /* ts A60923 */ out.bus_no = bus_no; out.host = host_no; out.id = target_no; out.channel = channel_no; out.lun = lun_no; out.devname = strdup(fname); if (out.devname == NULL) goto could_not_allocate; out.cam = NULL; out.lock_fd = -1; out.is_ahci = 0; out.start_lba= -2000000000; out.end_lba= -2000000000; out.read_atip = mmc_read_atip; out.grab = sg_grab; out.release = sg_release; out.drive_is_open= sg_drive_is_open; 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_cd = mmc_read_cd; 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.close_track_session = mmc_close; out.read_buffer_capacity = mmc_read_buffer_capacity; out.idata = calloc(1, sizeof(struct burn_scsi_inquiry_data)); out.idata->valid = 0; out.mdata = calloc(1, sizeof(struct scsi_mode_data)); if (out.idata == NULL || out.mdata == NULL) { could_not_allocate:; libdax_msgs_submit(libdax_messenger, -1, 0x00020108, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Could not allocate new drive object", 0, 0); if (out.devname != NULL) free(out.devname); out.devname = NULL; if (out.idata != NULL) free(out.idata); out.idata = NULL; if (out.mdata != NULL) free(out.mdata); out.mdata = NULL; return; } out.mdata->p2a_valid = 0; memset(&out.params, 0, sizeof(struct params)); t = burn_drive_register(&out); /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ mmc_function_spy(NULL, "enumerate_common : -------- doing grab"); /* try to get the drive info */ if (t->grab(t)) { t->getcaps(t); t->unlock(t); t->released = 1; } /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ mmc_function_spy(NULL, "enumerate_common : ----- would release "); } #else /* Scsi_freebsd_make_own_enumeratE */ /* The new, more concise version of enumerate_common */ static void enumerate_common(char *fname, int bus_no, int host_no, int channel_no, int target_no, int lun_no) { int ret; struct burn_drive out; /* General libburn drive setup */ burn_setup_drive(&out, fname); /* This transport adapter uses SCSI-family commands and models (seems the adapter would know better than its boss, if ever) */ ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no, target_no, lun_no, 0); if (ret<=0) return; /* Operating system adapter is CAM */ /* Adapter specific handles and data */ out.cam = NULL; out.lock_fd = -1; out.is_ahci = 0; /* Adapter specific functions */ out.grab = sg_grab; out.release = sg_release; out.drive_is_open = sg_drive_is_open; out.issue_command = sg_issue_command; /* Finally register drive and inquire drive information */ burn_drive_finish_enum(&out); } #endif /* ! Scsi_freebsd_make_own_enumeratE */ /* Lock the inode associated to dev_fd and the inode associated to devname. Return OS errno, number of pass device of dev_fd, locked fd to devname, error message. A return value of > 0 means success, <= 0 means failure. */ static int freebsd_dev_lock(int dev_fd, char *devname, int *os_errno, int *pass_dev_no, int *lock_fd, char msg[4096], int flag) { int lock_denied = 0, fd_stbuf_valid, name_stbuf_valid, i, pass_l = 100; int max_retry = 3, tries = 0; struct stat fd_stbuf, name_stbuf; char pass_name[16], *lock_name; *os_errno = 0; *pass_dev_no = -1; *lock_fd = -1; msg[0] = 0; fd_stbuf_valid = !fstat(dev_fd, &fd_stbuf); /* Try to find name of pass device by inode number */ lock_name = (char *) "effective device"; if(fd_stbuf_valid) { for (i = 0; i < pass_l; i++) { sprintf(pass_name, "/dev/pass%d", i); if (stat(pass_name, &name_stbuf) != -1) if(fd_stbuf.st_ino == name_stbuf.st_ino && fd_stbuf.st_dev == name_stbuf.st_dev) break; } if (i < pass_l) { lock_name = pass_name; *pass_dev_no = i; } } name_stbuf_valid = !stat(devname, &name_stbuf); for (tries= 0; tries <= max_retry; tries++) { lock_denied = flock(dev_fd, LOCK_EX | LOCK_NB); *os_errno = errno; if (lock_denied) { if (errno == EAGAIN && tries < max_retry) { /* <<< debugging fprintf(stderr, "\nlibcdio_DEBUG: EAGAIN pass, tries= %d\n", tries); */ usleep(2000000); continue; } sprintf(msg, "Device busy. flock(LOCK_EX) failed on %s of %s", strlen(lock_name) > 2000 || *pass_dev_no < 0 ? "pass device" : lock_name, strlen(devname) > 2000 ? "drive" : devname); return 0; } break; } /* fprintf(stderr, "libburn_DEBUG: flock obtained on %s of %s\n", lock_name, devname); */ /* Eventually lock the official device node too */ if (fd_stbuf_valid && name_stbuf_valid && (fd_stbuf.st_ino != name_stbuf.st_ino || fd_stbuf.st_dev != name_stbuf.st_dev)) { *lock_fd = open(devname, O_RDONLY); if (*lock_fd == 0) { close(*lock_fd); *lock_fd = -1; } if (*lock_fd > 0) { for (tries = 0; tries <= max_retry; tries++) { lock_denied = flock(*lock_fd, LOCK_EX | LOCK_NB); if (lock_denied) { if (errno == EAGAIN && tries < max_retry) { /* <<< debugging fprintf(stderr, "\nlibcdio_DEBUG: EAGAIN dev, tries= %d\n", tries); */ usleep(2000000); continue; } close(*lock_fd); *lock_fd = -1; sprintf(msg, "Device busy. flock(LOCK_EX) failed on %s", strlen(devname) > 4000 ? "drive" : devname); return 0; } break; } } /* fprintf(stderr, "libburn_DEBUG: flock obtained on %s\n", devname); */ } return 1; } static int sg_lock(struct burn_drive *d, int flag) { int ret, os_errno, pass_dev_no = -1, flock_fd = -1; char *msg = NULL; BURN_ALLOC_MEM(msg, char, 4096); ret = freebsd_dev_lock(d->cam->fd, d->devname, &os_errno, &pass_dev_no, &flock_fd, msg, 0); if (ret <= 0) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020008, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0); sg_close_drive(d); {ret = 0; goto ex;} } if (d->lock_fd > 0) close(d->lock_fd); d->lock_fd = flock_fd; ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } int sg_grab(struct burn_drive *d) { struct cam_device *cam; char path_string[80]; if (mmc_function_spy(d, "sg_grab") <= 0) return 0; if (burn_drive_is_open(d)) { d->released = 0; return 1; } cam = cam_open_device(d->devname, O_RDWR); if (cam == NULL) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Could not grab drive", errno, 0); return 0; } d->cam = cam; if (burn_sg_open_o_excl & 63) if (sg_lock(d, 0) <= 0) return 0; fcntl(cam->fd, F_SETOWN, getpid()); cam_path_string(d->cam, path_string, sizeof(path_string)); #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: CAM path = %s\n", path_string); #endif if (strstr(path_string, ":ahcich") != NULL) d->is_ahci = 1; else d->is_ahci = -1; d->released = 0; return 1; } /* 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 (mmc_function_spy(d, "sg_release") <= 0) return 0; if (d->cam == NULL) return 0; mmc_function_spy(NULL, "sg_release ----------- closing."); sg_close_drive(d); d->released = 1; return 0; } int sg_issue_command(struct burn_drive *d, struct command *c) { int done = 0, err, sense_len = 0, ret, ignore_error, i; int cam_pass_err_recover = 0, key, asc, ascq, timeout_ms; union ccb *ccb; static FILE *fp = NULL; time_t start_time; mmc_function_spy(NULL, "sg_issue_command"); c->error = 0; memset(c->sense, 0, sizeof(c->sense)); if (d->cam == NULL) return 0; if (burn_sg_log_scsi & 1) { if (fp == NULL) { fp= fopen("/tmp/libburn_sg_command_log", "a"); fprintf(fp, "\n-----------------------------------------\n"); } } if (burn_sg_log_scsi & 3) scsi_log_cmd(c,fp,0); c->error = 0; if (c->timeout > 0) timeout_ms = c->timeout; else timeout_ms = 200000; ccb = cam_getccb(d->cam); cam_fill_csio(&ccb->csio, 1, /* retries */ NULL, /* cbfncp */ CAM_DEV_QFRZDIS, /* flags */ MSG_SIMPLE_Q_TAG, /* tag_action */ NULL, /* data_ptr */ 0, /* dxfer_len */ sizeof (ccb->csio.sense_data), /* sense_len */ 0, /* cdb_len */ timeout_ms); /* timeout */ switch (c->dir) { case TO_DRIVE: ccb->csio.ccb_h.flags |= CAM_DIR_OUT; break; case FROM_DRIVE: ccb->csio.ccb_h.flags |= CAM_DIR_IN; break; case NO_TRANSFER: ccb->csio.ccb_h.flags |= CAM_DIR_NONE; break; } #ifdef Libburn_for_freebsd_ahcI /* ts B00325 : Advise by Alexander Motin */ /* Runs well on 8-STABLE (23 Mar 2003) But on 8-RELEASE cam_send_ccb() returns non-zero with errno 6 on eject. Long lasting TEST UNIT READY cycles break with errno 16. */ #ifdef Libburn_ahci_style_for_alL { #else if (d->is_ahci > 0) { #endif ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; cam_pass_err_recover = 1; } #endif /* Libburn_for_freebsd_ahcI */ ccb->csio.cdb_len = c->oplen; memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen); if (c->page) { ccb->csio.data_ptr = c->page->data; if (c->dir == FROM_DRIVE) { /* ts A90430 : Ticket 148 , by jwehle : "On ... FreeBSD 6.4 which has a usb memory reader in addition to a ATAPI DVD burner sg_issue_command will hang while the SCSI bus is being scanned" */ if (c->dxfer_len >= 0) ccb->csio.dxfer_len = c->dxfer_len; else ccb->csio.dxfer_len = BUFFER_SIZE; /* touch page so we can use valgrind */ memset(c->page->data, 0, BUFFER_SIZE); } else { ccb->csio.dxfer_len = c->page->bytes; } } else { ccb->csio.data_ptr = NULL; ccb->csio.dxfer_len = 0; } /* ts B90523 : Record effective transfer length request for debugging*/ c->dxfer_len = ccb->csio.dxfer_len; start_time = time(NULL); for (i = 0; !done; i++) { memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); memset(c->sense, 0, sizeof(c->sense)); c->start_time = burn_get_time(0); err = cam_send_ccb(d->cam, ccb); c->end_time = burn_get_time(0); ignore_error = sense_len = 0; /* ts B00325 : CAM_AUTOSNS_VALID advised by Alexander Motin */ if (ccb->ccb_h.status & CAM_AUTOSNS_VALID) { /* ts B00110 */ /* Better curb sense_len */ sense_len = ccb->csio.sense_len; if (sense_len > (int) sizeof(c->sense)) sense_len = sizeof(c->sense); memcpy(c->sense, &ccb->csio.sense_data, sense_len); spc_decode_sense(c->sense, sense_len, &key, &asc, &ascq); if (sense_len >= 14 && cam_pass_err_recover && key) ignore_error = 1; } if (err == -1 && cam_pass_err_recover && ! ignore_error) { #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: errno = %d . cam_errbuf = '%s'\n", errno, cam_errbuf); #endif if (errno == ENXIO && c->opcode[0] != 0) { /* Operations on empty or ejected tray */ /* MEDIUM NOT PRESENT */ #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: Emulating [2,3A,00] MEDIUM NOT PRESENT\n"); #endif c->sense[0] = 0x70; /*Fixed format sense data*/ c->sense[2] = 0x02; c->sense[12] = 0x3A; c->sense[13] = 0x00; sense_len = 14; ignore_error = 1; } else if (c->opcode[0] == 0 && (errno == EBUSY || errno == ENXIO)) { /* Timeout of TEST UNIT READY loop */ /* Inquiries while tray is being loaded */ /*LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE*/ #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: Emulating [2,04,00] LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE\n"); #endif c->sense[0] = 0x70; /*Fixed format sense data*/ c->sense[2] = 0x02; c->sense[12] = 0x04; c->sense[13] = 0x00; sense_len = 14; ignore_error = 1; } else if (errno == EINVAL) { /* Inappropriate MODE SENSE */ /* INVALID FIELD IN CDB */ #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: Emulating [5,24,00] INVALID FIELD IN CDB\n"); #endif c->sense[0] = 0x70; /*Fixed format sense data*/ c->sense[2] = 0x05; c->sense[12] = 0x24; c->sense[13] = 0x00; sense_len = 14; ignore_error = 1; } } if (err == -1 && !ignore_error) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Failed to transfer command to drive", errno, 0); sg_close_drive(d); d->released = 1; d->busy = BURN_DRIVE_IDLE; c->error = 1; {ret = -1; goto ex;} } /* XXX */ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (sense_len < 14) { /*LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE*/ #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: CAM_STATUS= %d .Emulating [2,04,00] LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE\n", (ccb->ccb_h.status & CAM_STATUS_MASK)); #endif c->sense[0] = 0x70; /*Fixed format sense data*/ c->sense[2] = 0x02; c->sense[12] = 0x04; c->sense[13] = 0x00; done = 1; } } done = scsi_eval_cmd_outcome(d, c, fp, c->sense, sense_len, start_time, timeout_ms, i, !!ignore_error); if (d->cancel) done = 1; if (!done) spc_register_retry(c); } while (!done); ret = 1; ex:; cam_freeccb(ccb); return ret; } /* ts B00115 */ /* Return 1 if the given path leads to a regular file or a device that can be fseeked, read and eventually written with 2 kB granularity. */ int burn_os_is_2k_seekrw(char *path, int flag) { struct stat stbuf; #ifdef Libburn_DIOCGMEDIASIZE_ISBLK int fd, ret; off_t add_size; #else char *spt; int i, e; #endif /* ! Libburn_DIOCGMEDIASIZE_ISBLK */ if (stat(path, &stbuf) == -1) return 0; if (S_ISREG(stbuf.st_mode)) return 1; if (!S_ISCHR(stbuf.st_mode)) return 0; #ifdef Libburn_DIOCGMEDIASIZE_ISBLK /* If it throws no error with DIOCGMEDIASIZE then it is a 'block device' */ fd = open(path, O_RDONLY); if (fd == -1) return 0; ret = ioctl(fd, DIOCGMEDIASIZE, &add_size); close(fd); return (ret != -1); #else /* Libburn_DIOCGMEDIASIZE_ISBLK */ spt = strrchr(path, '/'); if (spt == NULL) spt = path; else spt++; e = strlen(spt); for (i = strlen(spt) - 1; i > 0; i--) if (spt[i] >= '0' && spt[i] <= '9') e = i; if (strncmp(spt, "da", e) == 0) /* SCSI disk. E.g. USB stick. */ return 1; if (strncmp(spt, "cd", e) == 0) /* SCSI CD drive might be writeable. */ return 1; if (strncmp(spt, "ad", e) == 0) /* IDE hard drive */ return 1; if (strncmp(spt, "acd", e) == 0) /* IDE CD drive might be writeable */ return 1; if (strncmp(spt, "fd", e) == 0) /* Floppy disk */ return 1; if (strncmp(spt, "fla", e) == 0) /* Flash drive */ return 1; return 0; #endif /* ! Libburn_DIOCGMEDIASIZE_ISBLK */ } /* ts A70909 */ /** Estimate the potential payload capacity of a file address. @param path The address of the file to be examined. If it does not exist yet, then the directory will be inquired. @param bytes This value gets modified if an estimation is possible @return -2 = cannot perform necessary operations on file object -1 = neither path nor dirname of path exist 0 = could not estimate size capacity of file object 1 = estimation has been made, bytes was set */ int burn_os_stdio_capacity(char *path, off_t write_start, off_t *bytes) { struct stat stbuf; struct statvfs vfsbuf; char *testpath = NULL, *cpt; off_t add_size = 0; int fd, ret; BURN_ALLOC_MEM(testpath, char, 4096); testpath[0] = 0; if (stat(path, &stbuf) == -1) { strcpy(testpath, path); cpt = strrchr(testpath, '/'); if(cpt == NULL) strcpy(testpath, "."); else if(cpt == testpath) testpath[1] = 0; else *cpt = 0; if (stat(testpath, &stbuf) == -1) {ret = -1; goto ex;} #ifdef Libburn_if_this_was_linuX } else if(S_ISBLK(stbuf.st_mode)) { int open_mode = O_RDWR, fd, ret; long blocks; blocks = *bytes / 512; if(burn_sg_open_o_excl) open_mode |= O_EXCL; fd = open(path, open_mode); if (fd == -1) {ret = -2; goto ex;} ret = ioctl(fd, BLKGETSIZE, &blocks); close(fd); if (ret == -1) {ret = -2; goto ex;} *bytes = ((off_t) blocks) * (off_t) 512; #endif /* Libburn_if_this_was_linuX */ } else if(S_ISCHR(stbuf.st_mode)) { fd = open(path, O_RDONLY); if (fd == -1) {ret = -2; goto ex;} ret = ioctl(fd, DIOCGMEDIASIZE, &add_size); close(fd); if (ret == -1) {ret = -2; goto ex;} *bytes = add_size; } else if(S_ISREG(stbuf.st_mode)) { add_size = burn_sparse_file_addsize(write_start, &stbuf); strcpy(testpath, path); } else {ret = 0; goto ex;} if (testpath[0]) { if (statvfs(testpath, &vfsbuf) == -1) {ret = -2; goto ex;} *bytes = add_size + ((off_t) vfsbuf.f_frsize) * (off_t) vfsbuf.f_bavail; } ret = 1; ex: BURN_FREE_MEM(testpath); return ret; } /* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ #ifdef Libburn_read_o_direcT /* No special O_DIRECT-like precautions are implemented here */ #endif /* Libburn_read_o_direcT */ int burn_os_open_track_src(char *path, int open_flags, int flag) { int fd; fd = open(path, open_flags); return fd; } void *burn_os_alloc_buffer(size_t amount, int flag) { void *buf = NULL; buf = calloc(1, amount); return buf; } int burn_os_free_buffer(void *buffer, size_t amount, int flag) { if (buffer == NULL) return 0; free(buffer); return 1; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2009 - 2016 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ /* This is the main operating system dependent SCSI part of libburn. It implements the transport level aspects of SCSI control and command i/o. Present implementation: GNU libcdio , for X/Open compliant operating systems PORTING: Porting libburn typically will consist of adding a new operating system case to the following switcher files: os.h Operating system specific libburn definitions and declarations. sg.c Operating system dependent transport level modules. and of deriving the following system specific files from existing examples: os-*.h Included by os.h. You will need some general system knowledge about signals and knowledge about the storage object needs of your transport level module sg-*.c. sg-*.c This source module. You will need special system knowledge about how to detect all potentially available drives, how to open them, eventually how to exclusively reserve them, how to perform SCSI transactions, how to inquire the (pseudo-)SCSI driver. You will not need to care about CD burning, MMC or other high-level SCSI aspects. Said sg-*.c operations are defined by a public function interface, which has to be implemented in a way that provides libburn with the desired services: sg_id_string() returns an id string of the SCSI transport adapter. It may be called before initialization but then may return only a preliminary id. sg_initialize() performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility of supporting software components. sg_shutdown() performs global finalizations and releases globally acquired resources. sg_give_next_adr() iterates over the set of potentially useful drive address strings. scsi_enumerate_drives() brings all available, not-whitelist-banned, and accessible drives into libburn's list of drives. sg_dispose_drive() finalizes adapter specifics of struct burn_drive on destruction. Releases resources which were acquired underneath scsi_enumerate_drives(). sg_drive_is_open() tells whether libburn has the given drive in use. sg_grab() opens the drive for SCSI commands and ensures undisturbed access. sg_release() closes a drive opened by sg_grab() sg_issue_command() sends a SCSI command to the drive, receives reply, and evaluates whether the command succeeded or shall be retried or finally failed. sg_obtain_scsi_adr() tries to obtain SCSI address parameters. burn_os_is_2k_seekrw() tells whether the given path leads to a file object that can be used in 2 kB granularity by lseek(2), read(2), and possibly write(2) if not read-only.. E.g. a USB stick or a hard disk. burn_os_stdio_capacity() estimates the emulated media space of stdio-drives. burn_os_open_track_src() opens a disk file in a way that offers best throughput with file reading and/or SCSI write command transmission. burn_os_alloc_buffer() allocates a memory area that is suitable for file descriptors issued by burn_os_open_track_src(). The buffer size may be rounded up for alignment reasons. burn_os_free_buffer() delete a buffer obtained by burn_os_alloc_buffer(). Porting hints are marked by the text "PORTING:". Send feedback to libburn-hackers@pykix.org . */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /** PORTING : ------- OS dependent headers and definitions ------ */ #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <errno.h> #include <fcntl.h> #include <sys/stat.h> #include <string.h> #include <stdlib.h> #ifdef Libburn_os_has_statvfS #include <sys/statvfs.h> #endif /* Libburn_os_has_stavtfS */ #ifdef __linux /* for ioctl(BLKGETSIZE) */ #include <sys/ioctl.h> #include <linux/fs.h> #endif #ifdef __FreeBSD__ #define Libburn_is_on_freebsD 1 #endif #ifdef __FreeBSD_kernel__ #define Libburn_is_on_freebsD 1 #endif #ifdef Libburn_is_on_freebsD /* To avoid ATAPI devices */ #define Libburn_guess_freebsd_atapi_devicE 1 /* To obtain size of disk-like devices */ #include <sys/disk.h> /* DIOCGMEDIASIZE */ #endif /* Libburn_is_on_freebsD */ #define Libburn_guess_freebsd_atapi_devicE 1 #ifdef sun #define Libburn_is_on_solariS 1 #endif #ifdef __sun #define Libburn_is_on_solariS 1 #endif /* Proposal by Rocky Bernstein to avoid macro clashes with cdio_config.h */ #define __CDIO_CONFIG_H__ 1 #include <cdio/cdio.h> #include <cdio/logging.h> #include <cdio/mmc.h> /* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif /* The waiting time before eventually retrying a failed SCSI command. Before each retry wait Libburn_sg_linux_retry_incR longer than with the previous one. */ #define Libburn_sg_libcdio_retry_usleeP 100000 #define Libburn_sg_libcdio_retry_incR 100000 /** PORTING : ------ libburn portable headers and definitions ----- */ #include "transport.h" #include "drive.h" #include "sg.h" #include "spc.h" /* collides with symbols of <cdio/mmc.h> #include "mmc.h" */ #include "sbc.h" #include "debug.h" #include "toc.h" #include "util.h" #include "init.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /* is in portable part of libburn */ int burn_drive_is_banned(char *device_address); int burn_drive_resolve_link(char *path, char adr[], int *recursion_count, int flag); /* drive.c */ /* Whether to log SCSI commands: bit0= log in /tmp/libburn_sg_command_log bit1= log to stderr bit2= flush every line */ extern int burn_sg_log_scsi; /* ------------------------------------------------------------------------ */ /* PORTING: Private definitions. Port only if needed by public functions. */ /* (Public functions are listed below) */ /* ------------------------------------------------------------------------ */ /* Storage object is in libburn/init.c whether to strive for exclusive access to the drive */ extern int burn_sg_open_o_excl; /* ------------------------------------------------------------------------ */ /* PORTING: Private functions. Port only if needed by public functions */ /* (Public functions are listed below) */ /* ------------------------------------------------------------------------ */ static int sg_close_drive(struct burn_drive * d) { CdIo_t *p_cdio; if (d->p_cdio != NULL) { p_cdio = (CdIo_t *) d->p_cdio; cdio_destroy(p_cdio); d->p_cdio = NULL; } return 0; } static int sg_give_next_adr_raw(burn_drive_enumerator_t *idx, char adr[], int adr_size, int initialize) { char **pos; int count = 0; if (initialize == 1) { idx->pos = idx->ppsz_cd_drives = cdio_get_devices(DRIVER_DEVICE); if (idx->ppsz_cd_drives == NULL) return 0; for (pos = idx->ppsz_cd_drives ; pos != NULL; pos++) { if (*pos == NULL) break; count++; } } else if (initialize == -1) { if (idx->ppsz_cd_drives != NULL) if (*(idx->ppsz_cd_drives) != NULL) cdio_free_device_list(idx->ppsz_cd_drives); idx->ppsz_cd_drives = NULL; } #ifdef Libburn_guess_freebsd_atapi_devicE try_next:; #endif if (idx->pos == NULL) return 0; if (*(idx->pos) == NULL) return 0; #ifdef Libburn_guess_freebsd_atapi_devicE if (strncmp(*(idx->pos), "/dev/acd", 8) == 0) { (idx->pos)++; goto try_next; } #endif if ((ssize_t) strlen(*(idx->pos)) >= adr_size) return -1; strcpy(adr, *(idx->pos)); (idx->pos)++; return 1; } /* ----------------------------------------------------------------------- */ /* PORTING: Private functions which contain publicly needed functionality. */ /* Their portable part must be performed. So it is probably best */ /* to replace the non-portable part and to call these functions */ /* in your port, too. */ /* ----------------------------------------------------------------------- */ /** Wraps a detected drive into libburn structures and hands it over to libburn drive list. */ static void enumerate_common(char *fname, char *cdio_name, int bus_no, int host_no, int channel_no, int target_no, int lun_no) { int ret; struct burn_drive out; /* General libburn drive setup */ burn_setup_drive(&out, fname); /* This transport adapter uses SCSI-family commands and models (seems the adapter would know better than its boss, if ever) */ ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no, target_no, lun_no, 0); if (ret <= 0) return; /* PORTING: ------------------- non portable part --------------- */ /* Transport adapter is libcdio */ /* Adapter specific handles and data */ out.p_cdio = NULL; strcpy(out.libcdio_name, fname); if (strlen(cdio_name) < sizeof(out.libcdio_name)) strcpy(out.libcdio_name, cdio_name); /* PORTING: ---------------- end of non portable part ------------ */ /* Adapter specific functions with standardized names */ out.grab = sg_grab; out.release = sg_release; out.drive_is_open = sg_drive_is_open; out.issue_command = sg_issue_command; /* Finally register drive and inquire drive information */ burn_drive_finish_enum(&out); } /* ------------------------------------------------------------------------ */ /* PORTING: Public functions. These MUST be ported. */ /* ------------------------------------------------------------------------ */ /** Returns the id string of the SCSI transport adapter and eventually needed operating system facilities. This call is usable even if sg_initialize() was not called yet. In that case a preliminary constant message might be issued if detailed info is not available yet. @param msg returns id string @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_id_string(char msg[1024], int flag) { char *version_text; sprintf(msg, "sg-libcdio h%d with libcdio ", LIBCDIO_VERSION_NUM); #if LIBCDIO_VERSION_NUM < 83 LIBBURN_MISCONFIGURATION = 0; INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_cdio_version_dot_h_TOO_OLD__NEED_libcdio_VERSION_NUM_83 = 0; LIBBURN_MISCONFIGURATION_ = 0; #endif /* LIBCDIO_VERSION_NUM < 83 */ version_text = (char *) cdio_version_string; strncat(msg, version_text, 800); return 1; } /** Performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility of supporting software components. @param msg returns ids and/or error messages of eventual helpers @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_initialize(char msg[1024], int flag) { int cdio_ver; char *msg_pt; cdio_loglevel_default = CDIO_LOG_ASSERT; msg[0] = 0; sg_id_string(msg, 0); cdio_ver = libcdio_version_num; libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg , 0, 0); if (cdio_ver < LIBCDIO_VERSION_NUM) { strcat(msg, " ---> "); msg_pt = msg + strlen(msg); sprintf(msg_pt, "libcdio TOO OLD: numeric version %d , need at least %d", cdio_ver, LIBCDIO_VERSION_NUM); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg_pt, 0, 0); return 0; } return 1; } /** Performs global finalization of the SCSI transport adapter and eventually needed operating system facilities. Releases globally acquired resources. @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_shutdown(int flag) { return 1; } /** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of struct burn_drive which are defined in os-*.h. The eventual initialization of those components was made underneath scsi_enumerate_drives(). This will be called when a burn_drive gets disposed. @param d the drive to be finalized @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_dispose_drive(struct burn_drive *d, int flag) { return 1; } /** Returns the next index number and the next enumerated drive address. The enumeration has to cover all available and accessible drives. It is allowed to return addresses of drives which are not available but under some (even exotic) circumstances could be available. It is on the other hand allowed, only to hand out addresses which can really be used right in the moment of this call. (This implementation chooses the latter.) @param idx An opaque handle. Make no own theories about it. @param adr Takes the reply @param adr_size Gives maximum size of reply including final 0 @param initialize 1 = start new, 0 = continue, use no other values for now -1 = finish @return 1 = reply is a valid address , 0 = no further address available -1 = severe error (e.g. adr_size too small) */ int sg_give_next_adr(burn_drive_enumerator_t *idx, char adr[], int adr_size, int initialize) { int ret, recursion_count = 0, path_size = 4096; char *path = NULL; #ifdef Libburn_is_on_solariS int l; #endif BURN_ALLOC_MEM(path, char, path_size); ret = sg_give_next_adr_raw(idx, adr, adr_size, initialize); if (ret <= 0) goto ex; if ((ssize_t) strlen(adr) >= path_size) goto ex; #ifdef Libburn_is_on_solariS /* >>> provisory : preserve Solaris /dev/rdsk/cXtYdZs2 addresses */ l = strlen(adr); if (l >= 18) if (strncmp(adr, "/dev/rdsk/c", 11) == 0 && adr[11] >= '0' && adr[11] <= '9' && strcmp(adr + (l - 2), "s2") == 0) {ret = 1; goto ex;} #endif /* Libburn_is_on_solariS */ ret = burn_drive_resolve_link(adr, path, &recursion_count, 2); if(ret > 0 && (ssize_t) strlen(path) < adr_size) strcpy(adr, path); ret = (ret >= 0); ex: BURN_FREE_MEM(path); return ret; } /** Brings all available, not-whitelist-banned, and accessible drives into libburn's list of drives. */ int scsi_enumerate_drives(void) { burn_drive_enumerator_t idx; int initialize = 1, ret, i_bus_no = -1, recursion_count = 0; int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1; int buf_size = 4096; char *buf = NULL, *target = NULL; #ifdef Libburn_is_on_solariS int l; #endif BURN_ALLOC_MEM(buf, char, buf_size); BURN_ALLOC_MEM(target, char, buf_size); while(1) { ret = sg_give_next_adr_raw(&idx, buf, buf_size, initialize); initialize = 0; if (ret <= 0) break; ret = 1; #ifdef Libburn_is_on_solariS /* >>> provisory : preserve Solaris /dev/rdsk/cXtYdZs2 */ l = strlen(buf); if (l >= 18) if (strncmp(buf, "/dev/rdsk/c", 11) == 0 && buf[11] >= '0' && buf[11] <= '9' && strcmp(buf + (l - 2), "s2") == 0) ret = 0; #endif /* Libburn_is_on_solariS */ if (ret == 1) { ret = burn_drive_resolve_link(buf, target, &recursion_count,2); } if (ret <= 0) strcpy(target, buf); if (burn_drive_is_banned(target)) continue; sg_obtain_scsi_adr(buf, &i_bus_no, &i_host_no, &i_channel_no, &i_target_no, &i_lun_no); enumerate_common(target, buf, i_bus_no, i_host_no, i_channel_no, i_target_no, i_lun_no); } sg_give_next_adr(&idx, buf, buf_size, -1); ret = 1; ex:; BURN_FREE_MEM(buf); BURN_FREE_MEM(target); return ret; } /** Tells whether libburn has the given drive in use or exclusively reserved. If it is "open" then libburn will eventually call sg_release() on it when it is time to give up usage and reservation. */ /** Published as burn_drive.drive_is_open() */ int sg_drive_is_open(struct burn_drive * d) { return (d->p_cdio != NULL); } /** Opens the drive for SCSI commands and - if burn activities are prone to external interference on your system - obtains an exclusive access lock on the drive. (Note: this is not physical tray locking.) A drive that has been opened with sg_grab() will eventually be handed over to sg_release() for closing and unreserving. */ int sg_grab(struct burn_drive *d) { CdIo_t *p_cdio; char *am_eff, *msg = NULL, *am_wanted; int os_errno, second_try = 0, ret; if (d->p_cdio != NULL) { d->released = 0; {ret = 1; goto ex;} } if (d->libcdio_name[0] == 0) /* just to be sure it is initialized */ strcpy(d->libcdio_name, d->devname); am_wanted = (burn_sg_open_o_excl & 63) ? "MMC_RDWR_EXCL" : "MMC_RDWR"; try_to_open:; p_cdio = cdio_open_am(d->libcdio_name, DRIVER_DEVICE, am_wanted); if (p_cdio == NULL) { BURN_ALLOC_MEM(msg, char, 4096); os_errno = errno; sprintf(msg, "Could not grab drive '%s'", d->devname); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0); {ret = 0; goto ex;} } am_eff = (char *) cdio_get_arg(p_cdio, "access-mode"); if (strncmp(am_eff, "MMC_RDWR", 8) != 0) { cdio_destroy(p_cdio); if (!second_try) { am_wanted = (burn_sg_open_o_excl & 63) ? "MMC_RDWR" : "MMC_RDWR_EXCL"; second_try = 1; goto try_to_open; } libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "libcdio provides no MMC_RDWR access mode", 0, 0); {ret = 0; goto ex;} } d->p_cdio = p_cdio; d->released = 0; ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } /** PORTING: Is mainly about the call to sg_close_drive() and whether it implements the demanded functionality. */ /** Gives up the drive for SCSI commands and releases eventual access locks. (Note: this is not physical tray locking.) */ int sg_release(struct burn_drive *d) { if (d->p_cdio == NULL) return 0; sg_close_drive(d); return 0; } /** Sends a SCSI command to the drive, receives reply and evaluates whether the command succeeded or shall be retried or finally failed. Returned SCSI errors shall not lead to a return value indicating failure. The callers get notified by c->error. An SCSI failure which leads not to a retry shall be notified via scsi_notify_error(). The Libburn_log_sg_commandS facility might be of help when problems with a drive have to be examined. It shall stay disabled for normal use. @return: 1 success , <=0 failure */ int sg_issue_command(struct burn_drive *d, struct command *c) { int sense_valid = 0, i, timeout_ms, sense_len; int key = 0, asc = 0, ascq = 0, done = 0; time_t start_time; driver_return_code_t i_status; unsigned int dxfer_len; static FILE *fp = NULL; mmc_cdb_t cdb = {{0, }}; cdio_mmc_direction_t e_direction; CdIo_t *p_cdio; cdio_mmc_request_sense_t *sense_pt = NULL; c->error = 0; memset(c->sense, 0, sizeof(c->sense)); if (d->p_cdio == NULL) { return 0; } p_cdio = (CdIo_t *) d->p_cdio; if (burn_sg_log_scsi & 1) { if (fp == NULL) { fp= fopen("/tmp/libburn_sg_command_log", "a"); fprintf(fp, "\n-----------------------------------------\n"); } } if (burn_sg_log_scsi & 3) scsi_log_cmd(c,fp,0); memcpy(cdb.field, c->opcode, c->oplen); if (c->dir == TO_DRIVE) { dxfer_len = c->page->bytes; e_direction = SCSI_MMC_DATA_WRITE; } else if (c->dir == FROM_DRIVE) { if (c->dxfer_len >= 0) dxfer_len = c->dxfer_len; else dxfer_len = BUFFER_SIZE; e_direction = SCSI_MMC_DATA_READ; /* touch page so we can use valgrind */ memset(c->page->data, 0, BUFFER_SIZE); } else { dxfer_len = 0; e_direction = SCSI_MMC_DATA_NONE; } /* ts B90523 : Record effective transfer length request for debugging*/ c->dxfer_len = dxfer_len; /* retry-loop */ start_time = time(NULL); if (c->timeout > 0) timeout_ms = c->timeout; else timeout_ms = 200000; for(i = 0; !done; i++) { memset(c->sense, 0, sizeof(c->sense)); c->start_time = burn_get_time(0); i_status = mmc_run_cmd(p_cdio, timeout_ms, &cdb, e_direction, dxfer_len, c->page->data); c->end_time = burn_get_time(0); sense_valid = mmc_last_cmd_sense(p_cdio, &sense_pt); if (sense_valid >= 18) { memcpy(c->sense, (unsigned char *) sense_pt, (size_t) sense_valid >= sizeof(c->sense) ? sizeof(c->sense) : (size_t) sense_valid ); spc_decode_sense(c->sense, 0, &key, &asc, &ascq); } else key = asc = ascq = 0; if (sense_pt != NULL) free(sense_pt); /* Regrettably mmc_run_cmd() does not clearly distinguish between transport failure and SCSI error reply. This reaction here would be for transport failure: if (i_status != 0 && i_status != DRIVER_OP_ERROR) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Failed to transfer command to drive", errno, 0); sg_close_drive(d); d->released = 1; d->busy = BURN_DRIVE_IDLE; c->error = 1; return -1; } */ if ((!sense_valid) || (key == 0 && asc == 0 && ascq == 0)) { memset(c->sense, 0, sizeof(c->sense)); if (i_status != 0) { /* set dummy sense */ /*LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE*/ c->sense[0] = 0x70; /*Fixed format sense data*/ c->sense[2] = 0x02; c->sense[12] = 0x04; done = 1; } } if (key || asc || ascq) sense_len = 18; else sense_len = 0; done = scsi_eval_cmd_outcome(d, c, fp, c->sense, sense_len, start_time, timeout_ms, i, 0); if (d->cancel) done = 1; if (!done) spc_register_retry(c); } /* end of retry-loop */ return 1; } /** Tries to obtain SCSI address parameters. @return 1 is success , 0 is failure */ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no) { CdIo_t *p_cdio; char *tuple; *bus_no = *host_no = *channel_no = *target_no = *lun_no = -1; p_cdio = cdio_open(path, DRIVER_DEVICE); if (p_cdio == NULL) return 0; /* Try whether a bus,host,channel,target,lun address tuple is available */ tuple = (char *) cdio_get_arg(p_cdio, "scsi-tuple"); if (tuple != NULL) if (tuple[0]) { sscanf(tuple, "%d,%d,%d,%d,%d", bus_no, host_no, channel_no, target_no, lun_no); } cdio_destroy(p_cdio); return (*bus_no >= 0); } /** Tells whether a text is a persistent address as listed by the enumeration functions. */ int sg_is_enumerable_adr(char* adr) { burn_drive_enumerator_t idx; int initialize = 1, ret; char buf[64]; while(1) { ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); initialize = 0; if (ret <= 0) break; if (strcmp(adr, buf) == 0) { sg_give_next_adr(&idx, buf, sizeof(buf), -1); return 1; } } sg_give_next_adr(&idx, buf, sizeof(buf), -1); return (0); } #ifdef __FreeBSD__ #define Libburn_guess_block_devicE 1 #endif #ifdef __FreeBSD_kernel__ #define Libburn_guess_block_devicE 1 #endif #ifdef Libburn_guess_block_devicE /* ts B00115 */ /* The FreeBSD implementation of burn_os_is_2k_seekrw(). On FreeBSD there are no block devices. */ static int freebsd_is_2k_seekrw(char *path, int flag) { struct stat stbuf; char *spt; int i, e; if (stat(path, &stbuf) == -1) return 0; if (S_ISREG(stbuf.st_mode)) return 1; if (!S_ISCHR(stbuf.st_mode)) return 0; spt = strrchr(path, '/'); if (spt == NULL) spt = path; else spt++; e = strlen(spt); for (i = strlen(spt) - 1; i > 0; i--) if (spt[i] >= '0' && spt[i] <= '9') e = i; if (strncmp(spt, "da", e) == 0) /* SCSI disk. E.g. USB stick. */ return 1; if (strncmp(spt, "cd", e) == 0) /* SCSI CD drive might be writeable. */ return 1; if (strncmp(spt, "ad", e) == 0) /* IDE hard drive */ return 1; if (strncmp(spt, "acd", e) == 0) /* IDE CD drive might be writeable */ return 1; if (strncmp(spt, "fd", e) == 0) /* Floppy disk */ return 1; if (strncmp(spt, "fla", e) == 0) /* Flash drive */ return 1; return 0; } #endif /* Libburn_guess_block_devicE */ /* Return 1 if the given path leads to a regular file or a device that can be fseeked, read, and possibly written with 2 kB granularity. */ int burn_os_is_2k_seekrw(char *path, int flag) { #ifdef Libburn_guess_block_devicE return freebsd_is_2k_seekrw(path, flag); #else struct stat stbuf; if (stat(path, &stbuf) == -1) return 0; if (S_ISREG(stbuf.st_mode)) return 1; if (S_ISBLK(stbuf.st_mode)) return 1; return 0; #endif /* ! Libburn_guess_block_devicE */ } /** Estimate the potential payload capacity of a file address. @param path The address of the file to be examined. If it does not exist yet, then the directory will be inquired. @param bytes The pointed value gets modified, but only if an estimation is possible. @return -2 = cannot perform necessary operations on file object -1 = neither path nor dirname of path exist 0 = could not estimate size capacity of file object 1 = estimation has been made, bytes was set */ int burn_os_stdio_capacity(char *path, off_t write_start, off_t *bytes) { struct stat stbuf; #ifdef Libburn_os_has_statvfS struct statvfs vfsbuf; #endif char *testpath = NULL, *cpt; off_t add_size = 0; int ret; BURN_ALLOC_MEM(testpath, char, 4096); testpath[0] = 0; if (stat(path, &stbuf) == -1) { strcpy(testpath, path); cpt = strrchr(testpath, '/'); if(cpt == NULL) strcpy(testpath, "."); else if(cpt == testpath) testpath[1] = 0; else *cpt = 0; if (stat(testpath, &stbuf) == -1) {ret = -1; goto ex;} #ifdef __linux /* GNU/Linux specific determination of block device size */ } else if(S_ISBLK(stbuf.st_mode)) { int open_mode = O_RDONLY | O_BINARY, fd; long blocks; blocks = *bytes / 512; fd = open(path, open_mode); if (fd == -1) {ret = -2; goto ex;} ret = ioctl(fd, BLKGETSIZE, &blocks); close(fd); if (ret == -1) {ret = -2; goto ex;} *bytes = ((off_t) blocks) * (off_t) 512; #endif /* __linux */ #ifdef Libburn_is_on_freebsD } else if(S_ISCHR(stbuf.st_mode)) { int fd; fd = open(path, O_RDONLY | O_BINARY); if (fd == -1) {ret = -2; goto ex;} ret = ioctl(fd, DIOCGMEDIASIZE, &add_size); close(fd); if (ret == -1) {ret = -2; goto ex;} *bytes = add_size; #endif /* Libburn_is_on_freebsD */ #ifdef Libburn_is_on_solariS } else if(S_ISBLK(stbuf.st_mode)) { int open_mode = O_RDONLY | O_BINARY, fd; fd = open(path, open_mode); if (fd == -1) {ret = -2; goto ex;} *bytes = lseek(fd, 0, SEEK_END); close(fd); if (*bytes == -1) { *bytes = 0; {ret = 0; goto ex;} } #endif /* Libburn_is_on_solariS */ } else if(S_ISREG(stbuf.st_mode)) { add_size = burn_sparse_file_addsize(write_start, &stbuf); strcpy(testpath, path); } else {ret = 0; goto ex;} if (testpath[0]) { #ifdef Libburn_os_has_statvfS if (statvfs(testpath, &vfsbuf) == -1) {ret = -2; goto ex;} *bytes = add_size + ((off_t) vfsbuf.f_frsize) * (off_t) vfsbuf.f_bavail; #else /* Libburn_os_has_statvfS */ {ret = 0; goto ex;} #endif /* ! Libburn_os_has_stavtfS */ } ret = 1; ex:; BURN_FREE_MEM(testpath); return ret; } /* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ #ifdef Libburn_read_o_direcT /* No special O_DIRECT-like precautions are implemented here */ #endif /* Libburn_read_o_direcT */ int burn_os_open_track_src(char *path, int open_flags, int flag) { int fd; fd = open(path, open_flags | O_BINARY); return fd; } void *burn_os_alloc_buffer(size_t amount, int flag) { void *buf = NULL; buf = calloc(1, amount); return buf; } int burn_os_free_buffer(void *buffer, size_t amount, int flag) { if (buffer == NULL) return 0; free(buffer); return 1; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2020 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ /* <<< ts A91112 : experiments to get better speed with USB #define Libburn_sgio_as_growisofS 1 */ /* This is the main operating system dependent SCSI part of libburn. It implements the transport level aspects of SCSI control and command i/o. Present implementation: GNU/Linux SCSI Generic (sg) PORTING: Porting libburn typically will consist of adding a new operating system case to the following switcher files: os.h Operating system specific libburn definitions and declarations. sg.c Operating system dependent transport level modules. and of deriving the following system specific files from existing examples: os-*.h Included by os.h. You will need some general system knowledge about signals and knowledge about the storage object needs of your transport level module sg-*.c. sg-*.c This source module. You will need special system knowledge about how to detect all potentially available drives, how to open them, eventually how to exclusively reserve them, how to perform SCSI transactions, how to inquire the (pseudo-)SCSI driver. You will not need to care about CD burning, MMC or other high-level SCSI aspects. Said sg-*.c operations are defined by a public function interface, which has to be implemented in a way that provides libburn with the desired services: sg_id_string() returns an id string of the SCSI transport adapter. It may be called before initialization but then may return only a preliminary id. sg_initialize() performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility of supporting software components. sg_shutdown() performs global finalizations and releases globally acquired resources. sg_give_next_adr() iterates over the set of potentially useful drive address strings. scsi_enumerate_drives() brings all available, not-whitelist-banned, and accessible drives into libburn's list of drives. sg_dispose_drive() finalizes adapter specifics of struct burn_drive on destruction. Releases resources which were acquired underneath scsi_enumerate_drives(). sg_drive_is_open() tells whether libburn has the given drive in use. sg_grab() opens the drive for SCSI commands and ensures undisturbed access. sg_release() closes a drive opened by sg_grab() sg_issue_command() sends a SCSI command to the drive, receives reply, and evaluates whether the command succeeded or shall be retried or finally failed. sg_obtain_scsi_adr() tries to obtain SCSI address parameters. burn_os_is_2k_seekrw() tells whether the given path leads to a file object that can be used in 2 kB granularity by lseek(2) and read(2), and possibly write(2) if not read-only. E.g. a USB stick or a hard disk. burn_os_stdio_capacity() estimates the emulated media space of stdio-drives. burn_os_open_track_src() opens a disk file in a way that offers best throughput with file reading and/or SCSI write command transmission. burn_os_alloc_buffer() allocates a memory area that is suitable for file descriptors issued by burn_os_open_track_src(). The buffer size may be rounded up for alignment reasons. burn_os_free_buffer() delete a buffer obtained by burn_os_alloc_buffer(). Porting hints are marked by the text "PORTING:". Send feedback to libburn-hackers@pykix.org . Hint: You should also look into sg-freebsd-port.c, which is a younger and in some aspects more straightforward implementation of this interface. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /** PORTING : ------- OS dependent headers and definitions ------ */ #ifdef Libburn_read_o_direcT /* ts B91124: DISABLED, because this spoils multi-track burning of cdrskin by a hard to fix bug in cdrskin/cdrfifo.c DO NOT ENABLE before the wait code in that source file is fixed. */ #undef Libburn_read_o_direcT #endif #ifdef Libburn_read_o_direcT # ifndef _GNU_SOURCE # define _GNU_SOURCE # endif #endif /* Libburn_read_o_direcT */ #include <errno.h> #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <string.h> #include <sys/poll.h> #include <linux/hdreg.h> #include <stdlib.h> #include <sys/utsname.h> #include <scsi/scsi.h> #include <sys/statvfs.h> /* for ioctl(BLKGETSIZE) */ #include <linux/fs.h> /* for mmap() */ #include <sys/mman.h> #include <scsi/sg.h> /* Values within sg_io_hdr_t indicating success after ioctl(SG_IO) : */ /* .host_status : from http://tldp.org/HOWTO/SCSI-Generic-HOWTO/x291.html */ #define Libburn_sg_host_oK 0 /* .driver_status : from http://tldp.org/HOWTO/SCSI-Generic-HOWTO/x322.html */ #define Libburn_sg_driver_oK 0 /* ts A61211 : to eventually recognize CD devices on /dev/sr* */ #include <limits.h> #include <linux/cdrom.h> /** Indication of the Linux kernel this software is running on */ /* -1 = not evaluated , 0 = unrecognizable , 1 = 2.4 , 2 = 2.6 */ static int sg_kernel_age = -1; /** PORTING : Device file families for bus scanning and drive access. Both device families must support the following ioctls: SG_IO, SG_GET_SCSI_ID SCSI_IOCTL_GET_BUS_NUMBER SCSI_IOCTL_GET_IDLUN as well as mutual exclusively locking with open(O_EXCL). If a device family is left empty, then it will not be used. To avoid misunderstandings: both families are used via identical transport methods as soon as a device file is accepted as CD drive by the family specific function <family>_enumerate(). One difference remains throughout usage: Host,Channel,Id,Lun and Bus address parameters of ATA devices are considered invalid. */ /* Set this to 1 in order to get on stderr messages from sg_enumerate() */ static int linux_sg_enumerate_debug = 0; /* The device file family to use for (emulated) generic SCSI transport. This must be a printf formatter with one single placeholder for int in the range of 0 to 31 . The resulting addresses must provide SCSI address parameters Host, Channel, Id, Lun and also Bus. E.g.: "/dev/sg%d" sr%d is supposed to map only CD-ROM style devices. Additionally a test with ioctl(CDROM_DRIVE_STATUS) is made to assert that it is such a drive, If no such assertion is made, then this adapter performs INQUIRE and looks for first reply byte 0x05. This initial setting may be overridden in sg_select_device_family() by settings made via burn_preset_device_open(). */ static char linux_sg_device_family[80] = {"/dev/sg%d"}; /* Set this to 1 if you want the default linux_sg_device_family chosen depending on kernel release: sg for <2.6 , sr for >=2.6 */ static int linux_sg_auto_family = 1; /* Set this to 1 in order to accept any TYPE_* (see scsi/scsi.h) */ /* But try with 0 first. There is hope via CDROM_DRIVE_STATUS. */ /* !!! DO NOT SET TO 1 UNLESS YOU PROTECTED ALL INDISPENSABLE DEVICES chmod -rw !!! */ static int linux_sg_accept_any_type = 0; /* The device file family to use for SCSI transport over ATA. This must be a printf formatter with one single placeholder for a _single_ char in the range of 'a' to 'z'. This placeholder _must_ be at the end of the formatter string. E.g. "/dev/hd%c" */ static char linux_ata_device_family[80] = {"/dev/hd%c"}; /* Set this to 1 in order to get on stderr messages from ata_enumerate() */ static int linux_ata_enumerate_verbose = 0; /** PORTING : ------ libburn portable headers and definitions ----- */ #include "libburn.h" #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" #include "init.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /* ts A51221 */ int burn_drive_is_banned(char *device_address); /* ------------------------------------------------------------------------ */ /* PORTING: Private definitions. Port only if needed by public functions. */ /* (Public functions are listed below) */ /* ------------------------------------------------------------------------ */ static void enumerate_common(char *fname, int fd_in, int bus_no, int host_no, int channel_no, int target_no, int lun_no); static int sg_obtain_scsi_adr_fd(char *path, int fd_in, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no); /* ts A60813 : storage objects are in libburn/init.c whether to use O_EXCL with open(2) of devices whether to use fcntl(,F_SETLK,) after open(2) of devices what device family to use : 0=default, 1=sr, 2=scd, (3=st), 4=sg whether to use O_NOBLOCK with open(2) on devices whether to take O_EXCL rejection as fatal error */ extern int burn_sg_open_o_excl; extern int burn_sg_fcntl_f_setlk; extern int burn_sg_use_family; extern int burn_sg_open_o_nonblock; extern int burn_sg_open_abort_busy; /* ts A91111 : whether to log SCSI commands: bit0= log in /tmp/libburn_sg_command_log bit1= log to stderr bit2= flush every line */ extern int burn_sg_log_scsi; /* ts A60821 debug: for tracing calls which might use open drive fds or for catching SCSI usage of emulated drives. */ int mmc_function_spy(struct burn_drive *d, char * text); /* ------------------------------------------------------------------------ */ /* PORTING: Private functions. Port only if needed by public functions */ /* (Public functions are listed below) */ /* ------------------------------------------------------------------------ */ /* ts A70413 */ /* This finds out whether the software is running on kernel >= 2.6 */ static void sg_evaluate_kernel(void) { struct utsname buf; if (sg_kernel_age >= 0) return; sg_kernel_age = 0; if (uname(&buf) == -1) return; sg_kernel_age = 1; if (strcmp(buf.release, "2.6") >= 0) sg_kernel_age = 2; } /* ts A70314 */ /* This installs the device file family if one was chosen explicitly by burn_preset_device_open() */ static void sg_select_device_family(void) { /* >>> ??? do we need a mutex here ? */ /* >>> (It might be concurrent but is supposed to have always the same effect. Any race condition should be harmless.) */ if (burn_sg_use_family == 1) strcpy(linux_sg_device_family, "/dev/sr%d"); else if (burn_sg_use_family == 2) strcpy(linux_sg_device_family, "/dev/scd%d"); else if (burn_sg_use_family == 3) strcpy(linux_sg_device_family, "/dev/st%d"); else if (burn_sg_use_family == 4) strcpy(linux_sg_device_family, "/dev/sg%d"); else if (linux_sg_auto_family) { sg_evaluate_kernel(); if (sg_kernel_age >= 2) strcpy(linux_sg_device_family, "/dev/sr%d"); else strcpy(linux_sg_device_family, "/dev/sg%d"); linux_sg_auto_family = 0; } } /* ts A80701 */ /* This cares for the case that no /dev/srNN but only /dev/scdNN exists. A theoretical case which has its complement in SuSE 10.2 having /dev/sr but not /dev/scd. */ static int sg_exchange_scd_for_sr(char *fname, int flag) { struct stat stbuf; char scd[17], *msg = NULL; if (burn_sg_use_family != 0 || strncmp(fname, "/dev/sr", 7)!=0 || strlen(fname)>9 || strlen(fname)<8) return 2; if (fname[7] < '0' || fname[7] > '9') return 2; if (fname [8] != 0 && (fname[7] < '0' || fname[7] > '9')) return 2; if (stat(fname, &stbuf) != -1) return 2; strcpy(scd, "/dev/scd"); strcpy(scd + 8, fname + 7); if (stat(scd, &stbuf) == -1) return 2; msg = calloc(strlen(scd) + strlen(fname) + 80, 1); if (msg != NULL) { sprintf(msg, "%s substitutes for non-existent %s", scd, fname); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); free(msg); } strcpy(fname, scd); return 1; } /* ts B11110 */ /* This is an early stage version of scsi_log_cmd. >>> It will become obsolete when the /tmp file handler is moved into >>> scsi_log_command(). @param flag bit0= data direction is FROM_DRIVE */ static int sgio_log_cmd(unsigned char *cmd, int cmd_len, FILE *fp_in, int flag) { FILE *fp = fp_in; int ret = 0; int data_dir = NO_TRANSFER; if (flag & 1) data_dir = FROM_DRIVE; /* >>> ts B11110 : move this into scsi_log_command() */ if (fp == NULL && (burn_sg_log_scsi & 1)) { fp= fopen("/tmp/libburn_sg_command_log", "a"); if (fp != NULL) fprintf(fp, "\n=========================================\n"); } if (fp != NULL) ret = scsi_log_command(cmd, cmd_len, data_dir, NULL, 0, fp, flag); if (fp_in == NULL && fp != NULL) fclose(fp); if (fp == stderr || !(burn_sg_log_scsi & 2)) return ret; ret = scsi_log_command(cmd, cmd_len, data_dir, NULL, 0, stderr, 0); return ret; } /* ts B11110 */ static int sgio_log_reply(unsigned char *opcode, int data_dir, unsigned char *data, int dxfer_len, void *fp_in, unsigned char sense[18], int sense_len, double duration, int flag) { int ret; ret = scsi_log_reply(opcode, data_dir, data, dxfer_len, fp_in, sense, sense_len, duration, flag); return ret; } static int sgio_test(int fd) { unsigned char test_ops[] = { 0, 0, 0, 0, 0, 0 }; sg_io_hdr_t s; int ret; double c_start_time, c_end_time; 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; sgio_log_cmd(s.cmdp, s.cmd_len, NULL, 0); c_start_time = burn_get_time(0); ret= ioctl(fd, SG_IO, &s); c_end_time = burn_get_time(0); sgio_log_reply(s.cmdp, NO_TRANSFER, NULL, 0, NULL, (unsigned char *) (s.sbp), s.sb_len_wr, c_end_time - c_start_time, 0); return ret; } static int sgio_inquiry_cd_drive(int fd, char *fname) { unsigned char test_ops[] = { 0x12, 0, 0, 0, 36, 0 }; sg_io_hdr_t s; struct buffer *buf = NULL; unsigned char *sense = NULL; char *msg = NULL, *msg_pt; int ret = 0, i; double c_start_time, c_end_time; BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(sense, unsigned char, 128); BURN_ALLOC_MEM(msg, char, strlen(fname) + 1024); memset(&s, 0, sizeof(sg_io_hdr_t)); s.interface_id = 'S'; s.dxfer_direction = SG_DXFER_FROM_DEV; s.cmd_len = 6; s.cmdp = test_ops; s.mx_sb_len = 32; s.sbp = sense; s.timeout = 30000; s.dxferp = buf; s.dxfer_len = 36; s.usr_ptr = NULL; sgio_log_cmd(s.cmdp, s.cmd_len, NULL, 1); c_start_time = burn_get_time(0); ret = ioctl(fd, SG_IO, &s); c_end_time = burn_get_time(0); if (ret == -1) { sprintf(msg, "INQUIRY on '%s' : ioctl(SG_IO) failed , errno= %d", fname, errno); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); goto ex; } sgio_log_reply(s.cmdp, FROM_DRIVE, buf->data, s.dxfer_len, NULL, (unsigned char *) (s.sbp), s.sb_len_wr, c_end_time - c_start_time, 0); if (s.sb_len_wr > 0 || s.host_status != Libburn_sg_host_oK || s.driver_status != Libburn_sg_driver_oK) { sprintf(msg, "INQUIRY failed on '%s' : host_status= %hd , driver_status= %hd", fname, s.host_status, s.driver_status); if (s.sb_len_wr > 0) { sprintf(msg + strlen(msg), " , sense data="); msg_pt = msg + strlen(msg); for (i = 0 ; i < s.sb_len_wr; i++) sprintf(msg_pt + i * 3, " %2.2X", ((unsigned char *) (s.sbp))[i]); } libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); ret = -1; goto ex; } ret = 0; if (buf->data[0] == 0x5) { /* Peripheral qualifier 0, device type 0x5 = CD/DVD device. SPC-3 tables 82 and 83 */ ret = 1; } else { sprintf(msg, "INQUIRY on '%s' : byte 0 = 0x%2.2X", fname, buf->data[0]); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } ex:; BURN_FREE_MEM(msg); BURN_FREE_MEM(sense); BURN_FREE_MEM(buf); return ret; } /* ts A60924 */ static int sg_handle_busy_device(char *fname, int os_errno) { char *msg = NULL; struct stat stbuf; int looks_like_hd= 0, fd, ret; BURN_ALLOC_MEM(msg, char, 4096); /* ts A80713 : check existence of /dev/hdX1 as hint for hard disk rather than CD Hint by Giulio Orsero: check /proc/ide/hdX/media for "disk" */ if (strncmp(fname, "/dev/hd", 7)==0) { sprintf(msg, "%s1", fname); if (stat(msg, &stbuf) != -1) looks_like_hd= 1; sprintf(msg, "/proc/ide/hd%c/media", fname[7]); fd = open(msg, O_RDONLY); if (fd != -1) { ret = read(fd, msg, 10); if (ret < 0) ret = 0; msg[ret]= 0; close(fd); if (strncmp(msg, "disk\n", 5) == 0 || strcmp(msg, "disk") == 0) looks_like_hd= 2; else if (strncmp(msg, "cdrom\n", 6) == 0 || strcmp(msg, "cdrom") == 0) looks_like_hd= 0; } } /* ts A60814 : i saw no way to do this more nicely */ if (burn_sg_open_abort_busy) { fprintf(stderr, "\nlibburn: FATAL : Application triggered abort on busy device '%s'\n", fname); /* ts A61007 */ abort(); /* a ssert("drive busy" == "non fatal"); */ } /* ts A60924 : now reporting to libdax_msgs */ if (looks_like_hd == 2) { /* is surely hard disk */ ; } else if (looks_like_hd) { sprintf(msg, "Could not examine busy device '%s'", fname); libdax_msgs_submit(libdax_messenger, -1, 0x0002015a, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_LOW, msg, os_errno, 0); sprintf(msg, "Busy '%s' seems to be a hard disk, as '%s1' exists. But better check.", fname, fname); libdax_msgs_submit(libdax_messenger, -1, 0x0002015b, LIBDAX_MSGS_SEV_HINT, LIBDAX_MSGS_PRIO_LOW, msg, 0, 0); } else { sprintf(msg, "Cannot open busy device '%s'", fname); libdax_msgs_submit(libdax_messenger, -1, 0x00020001, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_LOW, msg, os_errno, 0); } ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } /* ts A60925 : ticket 74 */ static int sg_close_drive_fd(char *fname, int driveno, int *fd, int sorry) { int ret, os_errno, sevno= LIBDAX_MSGS_SEV_DEBUG; char *msg = NULL; if(*fd < 0) {ret = 0; goto ex;} BURN_ALLOC_MEM(msg, char, 4096 + 100); #ifdef CDROM_MEDIA_CHANGED_disabled_because_not_helpful #ifdef CDSL_CURRENT /* ts A80217 : wondering whether the os knows about our activities */ ret = ioctl(*fd, CDROM_MEDIA_CHANGED, CDSL_CURRENT); sprintf(msg, "ioctl(CDROM_MEDIA_CHANGED) == %d", ret); libdax_msgs_submit(libdax_messenger, driveno, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); #ifdef BLKFLSBUF_disabled_because_not_helpful ret = ioctl(*fd, BLKFLSBUF, 0); sprintf(msg, "ioctl(BLKFLSBUF) == %d", ret); os_errno = 0; if(ret == -1) os_errno = errno; libdax_msgs_submit(libdax_messenger, driveno, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno,0); #endif /* BLKFLSBUF */ #endif /* CDSL_CURRENT */ #endif /* CDROM_MEDIA_CHANGED */ ret = close(*fd); *fd = -1337; if(ret != -1) { /* ts A70409 : DDLP-B */ /* >>> release single lock on fname */ {ret = 1; goto ex;} } os_errno= errno; sprintf(msg, "Encountered error when closing drive '%s'", fname); if (sorry) sevno = LIBDAX_MSGS_SEV_SORRY; libdax_msgs_submit(libdax_messenger, driveno, 0x00020002, sevno, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0); ret = 0; ex:; BURN_FREE_MEM(msg); return ret; } /* ts A70401 : fcntl() has the unappealing property to work only after open(). So libburn will by default use open(O_EXCL) first and afterwards as second assertion will use fcntl(F_SETLK). One lock more should not harm. */ static int sg_fcntl_lock(int *fd, char *fd_name, int l_type, int verbose) { struct flock lockthing; char msg[81]; int ret; if (!burn_sg_fcntl_f_setlk) return 1; memset(&lockthing, 0, sizeof(lockthing)); lockthing.l_type = l_type; lockthing.l_whence = SEEK_SET; lockthing.l_start = 0; lockthing.l_len = 0; /* fprintf(stderr,"LIBBURN_EXPERIMENTAL: fcntl(%d, F_SETLK, %s)\n", *fd, l_type == F_WRLCK ? "F_WRLCK" : "F_RDLCK"); */ ret = fcntl(*fd, F_SETLK, &lockthing); if (ret == -1) { if (verbose) { sprintf(msg, "Device busy. Failed to fcntl-lock '%s'", fd_name); libdax_msgs_submit(libdax_messenger, -1, 0x00020008, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); } close(*fd); *fd = -1; /* ts A70409 : DDLP-B */ /* >>> release single lock on fd_name */ return(0); } return 1; } /* ts A60926 */ /* @param scan_mode 0= open for drivce aquiration 1= open for scanning with guessed names 2= open for scanning with /proc/sys/dev/cdrom/info names */ static int sg_open_drive_fd(char *fname, int scan_mode) { int open_mode = O_RDWR, fd, tries= 0, is_std_adr, report_as_note = 0; char msg[81]; struct stat stbuf; /* ts A70409 : DDLP-B */ /* >>> obtain single lock on fname */ /* ts A60813 - A60927 O_EXCL with devices is a non-POSIX feature of Linux kernels. Possibly introduced 2002. Mentioned in "The Linux SCSI Generic (sg) HOWTO" */ if(burn_sg_open_o_excl) open_mode |= O_EXCL; /* ts A60813 O_NONBLOCK was already hardcoded in ata_ but not in sg_. There must be some reason for this. So O_NONBLOCK is default mode for both now. Disable on own risk. ts B10904: O_NONBLOCK is prescribed by <linux/cdrom.h> ts A70411 Switched to O_NDELAY for LKML statement 2007/4/11/141 by Alan Cox: "open() has side effects. The CD layer allows you to open with O_NDELAY if you want to avoid them." */ if(burn_sg_open_o_nonblock) open_mode |= O_NDELAY; /* <<< debugging fprintf(stderr, "\nlibburn: experimental: o_excl= %d , o_nonblock= %d, abort_on_busy= %d\n", burn_sg_open_o_excl,burn_sg_open_o_nonblock,burn_sg_open_abort_busy); fprintf(stderr, "libburn: experimental: O_EXCL= %d , O_NDELAY= %d\n", !!(open_mode&O_EXCL),!!(open_mode&O_NDELAY)); */ try_open:; fd = open(fname, open_mode); if (fd == -1) { /* <<< debugging fprintf(stderr, "\nlibburn: experimental: fname= %s , errno= %d\n", fname,errno); */ if (errno == EBUSY) { tries++; /* <<< debugging fprintf(stderr, "\nlibburn_DEBUG: EBUSY , tries= %d\n", tries); */ if (tries < 4) { usleep(2000000); goto try_open; } sg_handle_busy_device(fname, errno); return -1; } sprintf(msg, "Failed to open device '%s'",fname); if (scan_mode) { is_std_adr = (strncmp(fname, "/dev/sr", 7) == 0 || strncmp(fname, "/dev/scd", 8) == 0); if(scan_mode == 1 && is_std_adr && stat(fname, &stbuf) != -1) report_as_note = 1; else if(scan_mode == 2 && (!is_std_adr) && stat(fname, &stbuf) != -1) report_as_note = 1; if (report_as_note) libdax_msgs_submit(libdax_messenger, -1, 0x0002000e, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); } else { libdax_msgs_submit(libdax_messenger, -1, 0x00020005, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); } return -1; } sg_fcntl_lock(&fd, fname, F_WRLCK, 1); return fd; } /* ts A60926 */ static int sg_release_siblings(int sibling_fds[], char sibling_fnames[][BURN_OS_SG_MAX_NAMELEN], int *sibling_count) { int i; char msg[81]; for(i= 0; i < *sibling_count; i++) sg_close_drive_fd(sibling_fnames[i], -1, &(sibling_fds[i]), 0); if(*sibling_count > 0) { sprintf(msg, "Closed %d O_EXCL scsi siblings", *sibling_count); libdax_msgs_submit(libdax_messenger, -1, 0x00020007, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); } *sibling_count = 0; return 1; } /* ts C00806 */ /** Urges the operating system to re-assess drive and medium state */ static int sg_os_revalidate_disc(struct burn_drive *d) { #ifdef Libburn_use_linux_ioctl_simul_changE /* <<< only for compiler tests */ #ifndef CDROM_SIMUL_CHANGE /* # def ine CDROM_SIMUL_CHANGE 0x5332 */ #endif #ifdef CDROM_SIMUL_CHANGE int fd, ret; long old_blocks, new_blocks; char *msg = NULL; BURN_ALLOC_MEM(msg, char, 161); ret = ioctl(d->fd, BLKGETSIZE, &old_blocks); if (ret == -1) old_blocks = -1; /* Schedule a simulated medium change event. Although the implemented ioctl cannot fail, the kernel might be too old to know it and then throw errors like ENOTTY. */ ret = ioctl(d->fd, CDROM_SIMUL_CHANGE, 0); if (ret == -1) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x02, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "ioctl(CDROM_SIMUL_CHANGE) failed", errno, 0); ret = 0; goto ex; } libdax_msgs_submit(libdax_messenger, d->global_index, 0x02, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "ioctl(CDROM_SIMUL_CHANGE) was performed", 0, 0); /* Try to trigger actual device assessment by a open(2) call */ fd = open(d->devname, O_RDONLY | O_NDELAY); if (fd == -1) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x02, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "Failed to open device file after ioctl(CDROM_SIMUL_CHANGE)", errno, 0); ret = 0; goto ex; } close(fd); ret = ioctl(d->fd, BLKGETSIZE, &new_blocks); if (ret == -1) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x02, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "BLKGETSIZE failed after ioctl(CDROM_SIMUL_CHANGE)", errno, 0); } else if (old_blocks != new_blocks) { sprintf(msg, "BLKGETSIZE indicates size change from %ld to %ld blocks", old_blocks , new_blocks); libdax_msgs_submit(libdax_messenger, d->global_index, 0x02, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } ex: BURN_FREE_MEM(msg); return ret; #else /* CDROM_SIMUL_CHANGE */ return 0; #endif /* ! CDROM_SIMUL_CHANGE */ #else /* Libburn_use_linux_ioctl_simul_changE */ return 0; #endif /* ! Libburn_use_linux_ioctl_simul_changE */ } /* ts A60926 */ static int sg_close_drive(struct burn_drive *d) { int ret; if (!burn_drive_is_open(d)) return 0; sg_release_siblings(d->sibling_fds, d->sibling_fnames, &(d->sibling_count)); if(d->medium_state_changed > 0) sg_os_revalidate_disc(d); d->medium_state_changed = -1; ret = sg_close_drive_fd(d->devname, d->global_index, &(d->fd), 0); return ret; } /* ts A60926 */ static int sg_open_scsi_siblings(char *path, int driveno, int sibling_fds[], char sibling_fnames[][BURN_OS_SG_MAX_NAMELEN], int *sibling_count, int host_no, int channel_no, int id_no, int lun_no) { int tld, i, ret, fd, i_bus_no = -1; int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1; char *msg = NULL, fname[40]; struct stat stbuf; dev_t last_rdev = 0, path_rdev; static char tldev[][20]= {"/dev/sr%d", "/dev/scd%d", "/dev/sg%d", ""}; /* ts A70609: removed "/dev/st%d" */ if (strlen(path) > BURN_MSGS_MESSAGE_LEN - 160) {ret = 0; goto ex;} BURN_ALLOC_MEM(msg, char, BURN_MSGS_MESSAGE_LEN); if(stat(path, &stbuf) == -1) {ret = 0; goto ex;} path_rdev = stbuf.st_rdev; sg_select_device_family(); if (linux_sg_device_family[0] == 0) {ret = 1; goto ex;} if(host_no < 0 || id_no < 0 || channel_no < 0 || lun_no < 0) {ret = 2; goto ex;} if(*sibling_count > 0) sg_release_siblings(sibling_fds, sibling_fnames, sibling_count); for (tld = 0; tldev[tld][0] != 0; tld++) { if (strcmp(tldev[tld], linux_sg_device_family)==0) continue; for (i = 0; i < 32; i++) { sprintf(fname, tldev[tld], i); if(stat(fname, &stbuf) == -1) continue; if (path_rdev == stbuf.st_rdev) continue; if (*sibling_count > 0 && last_rdev == stbuf.st_rdev) continue; ret = sg_obtain_scsi_adr(fname, &i_bus_no, &i_host_no, &i_channel_no, &i_target_no, &i_lun_no); if (ret <= 0) continue; if (i_host_no != host_no || i_channel_no != channel_no) continue; if (i_target_no != id_no || i_lun_no != lun_no) continue; fd = sg_open_drive_fd(fname, 0); if (fd < 0) goto failed; if (*sibling_count>=BURN_OS_SG_MAX_SIBLINGS) { sprintf(msg, "Too many scsi siblings of '%s'", path); libdax_msgs_submit(libdax_messenger, driveno, 0x00020006, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); goto failed; } sprintf(msg, "Opened O_EXCL scsi sibling '%s' of '%s'", fname, path); libdax_msgs_submit(libdax_messenger, driveno, 0x00020004, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); sibling_fds[*sibling_count] = fd; strcpy(sibling_fnames[*sibling_count], fname); (*sibling_count)++; last_rdev= stbuf.st_rdev; } } ret = 1; ex:; BURN_FREE_MEM(msg); return ret; failed:; sg_release_siblings(sibling_fds, sibling_fnames, sibling_count); ret = 0; goto ex; } /* ts A80731 */ static int is_ata_drive(char *fname, int fd_in) { int fd; struct hd_driveid tm; if (fd_in >= 0) fd = fd_in; else fd = sg_open_drive_fd(fname, 1); if (fd == -1) { if (linux_ata_enumerate_verbose) fprintf(stderr,"open failed, errno=%d '%s'\n", errno, strerror(errno)); return 0; } memset(&tm, 0, sizeof(tm)); ioctl(fd, HDIO_GET_IDENTITY, &tm); /* not atapi */ if (!(tm.config & 0x8000) || (tm.config & 0x4000)) { if (linux_ata_enumerate_verbose) fprintf(stderr, "not marked as ATAPI\n"); if (fd_in < 0) sg_close_drive_fd(fname, -1, &fd, 0); return 0; } /* if SG_IO fails on an atapi device, we should stop trying to use hd* devices */ if (sgio_test(fd) == -1) { if (linux_ata_enumerate_verbose) fprintf(stderr, "FATAL: sgio_test() failed: errno=%d '%s'\n", errno, strerror(errno)); if (fd_in < 0) sg_close_drive_fd(fname, -1, &fd, 0); return 0; } if (fd_in >= 0) return 1; if (sg_close_drive_fd(fname, -1, &fd, 1) <= 0) { if (linux_ata_enumerate_verbose) fprintf(stderr, "cannot close properly, errno=%d '%s'\n", errno, strerror(errno)); return 0; } return 1; } static int is_scsi_drive(char *fname, int fd_in, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no) { int fd = -1, sid_ret = 0, ret, fail_sev_sorry = 0; struct sg_scsi_id sid; int *sibling_fds = NULL, sibling_count= 0; typedef char burn_sg_sibling_fname[BURN_OS_SG_MAX_NAMELEN]; burn_sg_sibling_fname *sibling_fnames = NULL; BURN_ALLOC_MEM(sibling_fds, int, BURN_OS_SG_MAX_SIBLINGS); BURN_ALLOC_MEM(sibling_fnames, burn_sg_sibling_fname, BURN_OS_SG_MAX_SIBLINGS); if (fd_in >= 0) fd = fd_in; else fd = sg_open_drive_fd(fname, 1); if (fd == -1) { if (linux_sg_enumerate_debug) fprintf(stderr, "open failed, errno=%d '%s'\n", errno, strerror(errno)); {ret = 0; goto ex;} } sid_ret = ioctl(fd, SG_GET_SCSI_ID, &sid); if (sid_ret == -1) { sid.scsi_id = -1; /* mark SCSI address as invalid */ if(linux_sg_enumerate_debug) fprintf(stderr, "ioctl(SG_GET_SCSI_ID) failed, errno=%d '%s' , ", errno, strerror(errno)); if (sgio_test(fd) == -1) { if (linux_sg_enumerate_debug) fprintf(stderr, "FATAL: sgio_test() failed: errno=%d '%s'", errno, strerror(errno)); {ret = 0; goto ex;} } #ifdef CDROM_DRIVE_STATUS /* http://developer.osdl.org/dev/robustmutexes/ src/fusyn.hg/Documentation/ioctl/cdrom.txt */ sid_ret = ioctl(fd, CDROM_DRIVE_STATUS, 0); if(linux_sg_enumerate_debug) fprintf(stderr, "ioctl(CDROM_DRIVE_STATUS) = %d , ", sid_ret); if (sid_ret != -1 && sid_ret != CDS_NO_INFO) sid.scsi_type = TYPE_ROM; else sid_ret = -1; #endif /* CDROM_DRIVE_STATUS */ } if (sid_ret == -1) { /* ts B11109 : Try device type from INQUIRY byte 0 */ if (sgio_inquiry_cd_drive(fd, fname) == 1) { sid_ret = 0; sid.scsi_type = TYPE_ROM; } } #ifdef SCSI_IOCTL_GET_BUS_NUMBER /* Hearsay A61005 */ if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, bus_no) == -1) *bus_no = -1; #endif fail_sev_sorry = (sid.scsi_type == TYPE_ROM); if ( (sid_ret == -1 || sid.scsi_type != TYPE_ROM) && !linux_sg_accept_any_type) { if (linux_sg_enumerate_debug) fprintf(stderr, "sid.scsi_type = %d (!= TYPE_ROM)\n", sid.scsi_type); {ret = 0; goto ex;} } /* ts A61211 : employ a more general ioctl */ /* ts B11001 : re-use fd */ /* ts B80902 : call unconditionally because host_no differs between SG_GET_SCSI_ID and SCSI_IOCTL_GET_IDLUN */ ret = sg_obtain_scsi_adr_fd(fname, fd, bus_no, host_no, channel_no, target_no, lun_no); if (ret <= 0) { if (linux_sg_enumerate_debug) fprintf(stderr, "sg_obtain_scsi_adr_fd() failed\n"); {ret = 0; goto ex;} } /* ts A60927 : trying to do locking with growisofs */ if(burn_sg_open_o_excl>1) { ret = sg_open_scsi_siblings( fname, -1, sibling_fds, sibling_fnames, &sibling_count, sid.host_no, sid.channel, sid.scsi_id, sid.lun); if (ret<=0) { if (linux_sg_enumerate_debug) fprintf(stderr, "cannot lock siblings\n"); sg_handle_busy_device(fname, 0); {ret = 0; goto ex;} } /* the final occupation will be done in sg_grab() */ sg_release_siblings(sibling_fds, sibling_fnames, &sibling_count); } ret = 1; ex:; if (fd_in < 0 && fd >= 0) { if (sg_close_drive_fd(fname, -1, &fd, fail_sev_sorry) <= 0) { if (linux_sg_enumerate_debug) fprintf(stderr, "cannot close properly, errno=%d '%s'\n", errno, strerror(errno)); if (ret > 0) ret = 0; } } BURN_FREE_MEM(sibling_fds); BURN_FREE_MEM(sibling_fnames); return ret; } /* @param flag bit0= do not complain about failure to open /dev/sr /dev/scd */ static int sg_open_for_enumeration(char *fname, int flag) { int fd; fd = sg_open_drive_fd(fname, 1 + (flag & 1)); if (fd < 0) { if (linux_sg_enumerate_debug || linux_ata_enumerate_verbose) fprintf(stderr, "open failed, errno=%d '%s'\n", errno, strerror(errno)); return -1; } return fd; } /** Speciality of GNU/Linux: detect non-SCSI ATAPI (EIDE) which will from then on used used via generic SCSI as is done with (emulated) SCSI drives */ static void ata_enumerate(void) { int ret, i, fd = -1; char fname[10]; if (linux_ata_enumerate_verbose) fprintf(stderr, "libburn_debug: linux_ata_device_family = %s\n", linux_ata_device_family); if (linux_ata_device_family[0] == 0) return; for (i = 0; i < 26; i++) { sprintf(fname, linux_ata_device_family, 'a' + i); if (linux_ata_enumerate_verbose) fprintf(stderr, "libburn_debug: %s : ", fname); /* ts A51221 */ if (burn_drive_is_banned(fname)) { if (linux_ata_enumerate_verbose) fprintf(stderr, "not in whitelist\n"); continue; } fd = sg_open_for_enumeration(fname, 0); if (fd < 0) continue; ret = is_ata_drive(fname, fd); if (ret < 0) break; if (ret == 0) continue; if (linux_ata_enumerate_verbose) fprintf(stderr, "accepting as drive without SCSI address\n"); enumerate_common(fname, fd, -1, -1, -1, -1, -1); } } /** Detects (probably emulated) SCSI drives */ static void sg_enumerate(void) { int i, ret, fd = -1; int bus_no= -1, host_no= -1, channel_no= -1, target_no= -1, lun_no= -1; char fname[17]; sg_select_device_family(); if (linux_sg_enumerate_debug) fprintf(stderr, "libburn_debug: linux_sg_device_family = %s\n", linux_sg_device_family); if (linux_sg_device_family[0] == 0) return; for (i = 0; i < 32; i++) { sprintf(fname, linux_sg_device_family, i); /* ts A80702 */ sg_exchange_scd_for_sr(fname, 0); if (linux_sg_enumerate_debug) fprintf(stderr, "libburn_debug: %s : ", fname); /* ts A51221 */ if (burn_drive_is_banned(fname)) { if (linux_sg_enumerate_debug) fprintf(stderr, "not in whitelist\n"); continue; } fd = sg_open_for_enumeration(fname, 0); if (fd < 0) continue; ret = is_scsi_drive(fname, fd, &bus_no, &host_no, &channel_no, &target_no, &lun_no); if (ret < 0) break; if (ret == 0) continue; if (linux_sg_enumerate_debug) fprintf(stderr, "accepting as SCSI %d,%d,%d,%d bus=%d\n", host_no, channel_no, target_no, lun_no, bus_no); enumerate_common(fname, fd, bus_no, host_no, channel_no, target_no, lun_no); } } /* ts A80805 : eventually produce the other official name of a device file */ static int fname_other_name(char *fname, char other_name[80], int flag) { if(strncmp(fname, "/dev/sr", 7) == 0 && (fname[7] >= '0' && fname[7] <= '9') && (fname[8] == 0 || (fname[8] >= '0' && fname[8] <= '9' && fname[9] == 0))) { sprintf(other_name, "/dev/scd%s", fname + 7); return 1; } if(strncmp(fname, "/dev/scd", 8) == 0 && (fname[8] >= '0' && fname[8] <= '9') && (fname[9] == 0 || (fname[9] >= '0' && fname[9] <= '9' && fname[10] == 0))) { sprintf(other_name, "/dev/sr%s", fname + 8); return 1; } return 0; } /* ts A80805 */ static int fname_drive_is_listed(char *fname, int flag) { char other_fname[80]; if (burn_drive_is_listed(fname, NULL, 0)) return 1; if (fname_other_name(fname, other_fname, 0) > 0) if (burn_drive_is_listed(other_fname, NULL, 0)) return 2; return 0; } /* ts A80731 : Directly open the given address. @param flag bit0= do not complain about missing file bit1= do not check whether drive is already listed bit2= do not complain about failure to open /dev/sr /dev/scd */ static int fname_enumerate(char *fname, int flag) { int is_ata= 0, is_scsi= 0, ret, fd = -1; int bus_no= -1, host_no= -1, channel_no= -1, target_no= -1, lun_no= -1; char *msg = NULL; struct stat stbuf; BURN_ALLOC_MEM(msg, char, BURN_DRIVE_ADR_LEN + 80); if (!(flag & 2)) if (fname_drive_is_listed(fname, 0)) {ret = 2; goto ex;} if (stat(fname, &stbuf) == -1) { sprintf(msg, "File object '%s' not found", fname); if (!(flag & 1)) libdax_msgs_submit(libdax_messenger, -1, 0x0002000b, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); {ret = -1; goto ex;} } fd = sg_open_for_enumeration(fname, !!(flag & 4)); if (fd < 0) {ret = 0; goto ex;} is_ata = is_ata_drive(fname, fd); if (is_ata < 0) {ret = -1; goto ex;} if (is_ata) { /* In most cases Linux returns SCSI bus,...,lun even for ATA and SATA drives */ sg_obtain_scsi_adr_fd(fname, fd, &bus_no, &host_no, &channel_no, &target_no, &lun_no); } else { is_scsi = is_scsi_drive(fname, fd, &bus_no, &host_no, &channel_no, &target_no, &lun_no); if (is_scsi < 0) {ret = -1; goto ex;} } if (is_ata == 0 && is_scsi == 0) {ret = 0; goto ex;} if (linux_sg_enumerate_debug) fprintf(stderr, "(single) accepting as SCSI %d,%d,%d,%d bus=%d\n", host_no, channel_no, target_no, lun_no, bus_no); enumerate_common(fname, fd, bus_no, host_no, channel_no, target_no, lun_no); ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } /* ts A80731 : Directly open the given address from a single-item whitlist */ static int single_enumerate(int flag) { int ret, wl_count; char *fname, *msg = NULL; wl_count= burn_drive_whitelist_count(); if (wl_count != 1) {ret = 0; goto ex;} fname= burn_drive_whitelist_item(0, 0); if (fname == NULL) {ret = 0; goto ex;} ret = fname_enumerate(fname, 2); if (ret <= 0) { BURN_ALLOC_MEM(msg, char, BURN_DRIVE_ADR_LEN + 80); sprintf(msg, "Cannot access '%s' as SG_IO CDROM drive", fname); libdax_msgs_submit(libdax_messenger, -1, 0x0002000a, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); ret = -1; } ex:; BURN_FREE_MEM(msg); return ret; } /* ts A80801 : looking up drives listed in /proc/sys/dev/cdrom/info line like: drive name: sr1 hdc hda sr0 @parm flag bit0= release list memory and exit */ static int proc_sys_dev_cdrom_info(char ***list, int *count, int flag) { FILE *fp; char *line = NULL, *fname = NULL, *cpt, *retpt, *list_data; int maxl= 0, pass, i, line_size = 1024, ret; BURN_ALLOC_MEM(line, char, line_size); BURN_ALLOC_MEM(fname, char, line_size + 5); if (*list != NULL) { if ((*list)[0] != NULL) free((*list)[0]); free(*list); *list = NULL; *count = 0; } if (flag & 1) {ret = 1; goto ex;} *count = 0; sg_evaluate_kernel(); if (sg_kernel_age < 2) /* addresses are not suitable for kernel 2.4 */ {ret = 1; goto ex;} fp = fopen("/proc/sys/dev/cdrom/info", "r"); if (fp == NULL) {ret = 0; goto ex;} while (1) { retpt = fgets(line, line_size, fp); if (retpt == NULL) break; if(strncmp(line, "drive name:", 11) == 0) break; } fclose(fp); if (retpt == NULL) {ret = 0; goto ex;} strcpy(fname, "/dev/"); for(pass = 0; pass < 2; pass++) { *count = 0; cpt = line + 11; while (*cpt != 0) { for(; *cpt == ' ' || *cpt == '\t'; cpt++); if (*cpt == 0 || *cpt == '\n') break; sscanf(cpt, "%s", fname + 5); if ((int) strlen(fname) > maxl) maxl = strlen(fname); if (pass == 1) strcpy((*list)[*count], fname); (*count)++; for(cpt++; *cpt != ' ' && *cpt != '\t' && *cpt != 0 && *cpt != '\n'; cpt++); } if (pass == 0) { list_data = calloc(*count + 1, maxl+1); *list = calloc(*count + 1, sizeof(char *)); if(list_data == NULL || *list == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00000003, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Out of virtual memory", 0, 0); if (list_data != NULL) free(list_data); if (*list != NULL) free((char *) *list); {ret = -1; goto ex;} } for (i = 0; i <= *count; i++) (*list)[i] = list_data + i * (maxl + 1); } } ret = 1; ex:; BURN_FREE_MEM(line); BURN_FREE_MEM(fname); return ret; } static int add_proc_info_drives(int flag) { int ret, list_count, count = 0, i; char **list= NULL; if (burn_sg_use_family != 0) return(1); /* Looking only for sr , scd , sg */ ret = proc_sys_dev_cdrom_info(&list, &list_count, 0); if (ret <= 0) return ret; for (i = 0; i < list_count; i++) { if (burn_drive_is_banned(list[i])) continue; ret = fname_enumerate(list[i], 1 | 4); if (ret == 1) count++; } proc_sys_dev_cdrom_info(&list, &list_count, 1); /* free memory */ return 1 + count; } /* ts A61115 */ /* ----------------------------------------------------------------------- */ /* PORTING: Private functions which contain publicly needed functionality. */ /* Their portable part must be performed. So it is probably best */ /* to replace the non-portable part and to call these functions */ /* in your port, too. */ /* ----------------------------------------------------------------------- */ /** Wraps a detected drive into libburn structures and hands it over to libburn drive list. */ /* ts A60923 - A61005 : introduced new SCSI parameters */ /* ts A61021 : moved non os-specific code to spc,sbc,mmc,drive */ static void enumerate_common(char *fname, int fd_in, int bus_no, int host_no, int channel_no, int target_no, int lun_no) { int ret, i; struct burn_drive out; /* General libburn drive setup */ burn_setup_drive(&out, fname); /* This transport adapter uses SCSI-family commands and models (seems the adapter would know better than its boss, if ever) */ ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no, target_no, lun_no, 0); if (ret<=0) return; /* PORTING: ------------------- non portable part --------------- */ /* Operating system adapter is GNU/Linux Generic SCSI (sg) */ /* Adapter specific handles and data */ out.fd = -1337; out.sibling_count = 0; for(i= 0; i<BURN_OS_SG_MAX_SIBLINGS; i++) out.sibling_fds[i] = -1337; /* PORTING: ---------------- end of non portable part ------------ */ /* Adapter specific functions with standardized names */ out.grab = sg_grab; out.release = sg_release; out.drive_is_open= sg_drive_is_open; out.issue_command = sg_issue_command; if (fd_in >= 0) out.fd = fd_in; /* Finally register drive and inquire drive information. out is an invalid copy afterwards. Do not use it for anything. */ burn_drive_finish_enum(&out); } /* ts A61115 */ /* ------------------------------------------------------------------------ */ /* PORTING: Public functions. These MUST be ported. */ /* ------------------------------------------------------------------------ */ /** Returns the id string of the SCSI transport adapter and eventually needed operating system facilities. This call is usable even if sg_initialize() was not called yet. In that case a preliminary constant message might be issued if detailed info is not available yet. @param msg returns id string @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_id_string(char msg[1024], int flag) { strcpy(msg, "internal GNU/Linux SG_IO adapter sg-linux"); return 1; } /** Performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility supporting software components. @param msg returns ids and/or error messages of eventual helpers @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_initialize(char msg[1024], int flag) { return sg_id_string(msg, 0); } /** Performs global finalization of the SCSI transport adapter and eventually needed operating system facilities. Releases globally acquired resources. @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_shutdown(int flag) { return 1; } /** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of struct burn_drive which are defined in os-*.h. The eventual initialization of those components was made underneath scsi_enumerate_drives(). This will be called when a burn_drive gets disposed. @param d the drive to be finalized @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_dispose_drive(struct burn_drive *d, int flag) { return 1; } /** PORTING: In this GNU/Linux implementation, this function mirrors the enumeration done in sg_enumerate and ata_enumerate(). It would be better to base those functions on this sg_give_next_adr() but the situation is not inviting. */ /* ts A60922 ticket 33 : called from drive.c */ /** Returns the next index number and the next enumerated drive address. The enumeration has to cover all available and accessible drives. It is allowed to return addresses of drives which are not available but under some (even exotic) circumstances could be available. It is on the other hand allowed, only to hand out addresses which can really be used right in the moment of this call. (This implementation chooses the former.) @param idx An opaque handle. Make no own theories about it. @param adr Takes the reply @param adr_size Gives maximum size of reply including final 0 @param initialize 1 = start new, 0 = continue, use no other values for now -1 = finish @return 1 = reply is a valid address , 0 = no further address available -1 = severe error (e.g. adr_size too small) */ int sg_give_next_adr(burn_drive_enumerator_t *idx, char adr[], int adr_size, int initialize) { /* os-linux.h : typedef int burn_drive_enumerator_t; */ static int sg_limit = 32, ata_limit = 26; int baseno = 0, i; char other_name[80]; if (initialize == -1) { proc_sys_dev_cdrom_info(&(idx->info_list), &(idx->info_count), 1); return 0; } sg_select_device_family(); if (linux_sg_device_family[0] == 0) sg_limit = 0; if (linux_ata_device_family[0] == 0) ata_limit = 0; if (initialize == 1) { idx->pos = -1; idx->info_count= 0; idx->info_list= NULL; proc_sys_dev_cdrom_info(&(idx->info_list), &(idx->info_count), 0); } (idx->pos)++; if (idx->pos >= sg_limit) goto next_ata; if (adr_size < 11) return -1; sprintf(adr, linux_sg_device_family, idx->pos); sg_exchange_scd_for_sr(adr, 0); goto return_1_pre_proc; next_ata:; baseno += sg_limit; if (idx->pos - baseno >= ata_limit) goto next_proc_info; if (adr_size < 9) return -1; sprintf(adr, linux_ata_device_family, 'a' + (idx->pos - baseno)); goto return_1_pre_proc; next_proc_info:; baseno += ata_limit; if (burn_sg_use_family != 0) /* Only with default enumeration */ return 0; for (i = 0; i < idx->info_count; i++) { if ((idx->info_list)[i][0] == 0) continue; if (baseno == idx->pos) { if (adr_size < (int) strlen((idx->info_list)[i]) + 1) return -1; strcpy(adr, (idx->info_list)[i]); return 1; } baseno++; } return 0; return_1_pre_proc:; for (i = 0; i < idx->info_count; i++) { if (strcmp((idx->info_list)[i], adr) == 0) (idx->info_list)[i][0] = 0; if (fname_other_name(adr, other_name, 0) > 0) if (strcmp((idx->info_list)[i], other_name) == 0) (idx->info_list)[i][0] = 0; } return 1; } /** Brings all available, not-whitelist-banned, and accessible drives into libburn's list of drives. */ /** PORTING: If not stricken with an incompletely unified situation like in GNU/Linux one would rather implement this by a loop calling sg_give_next_adr(). If needed with your sg_give_next_adr() results, do a test for existence and accessability. If burn activities are prone to external interference on your system it is also necessary to obtain exclusive access locks on the drives. Hand over each accepted drive to enumerate_common() or its replacement within your port. See FreeBSD port sketch sg-freebsd-port.c for such an implementation. */ /* ts A61115: replacing call to sg-implementation internals from drive.c */ int scsi_enumerate_drives(void) { int ret; /* Direct examination of eventually single whitelisted name */ ret = single_enumerate(0); if (ret < 0) return -1; if (ret > 0) return 1; sg_enumerate(); ata_enumerate(); add_proc_info_drives(0); return 1; } /** Tells whether libburn has the given drive in use or exclusively reserved. If it is "open" then libburn will eventually call sg_release() on it when it is time to give up usage and reservation. */ /** Published as burn_drive.drive_is_open() */ int sg_drive_is_open(struct burn_drive * d) { /* a bit more detailed case distinction than needed */ if (d->fd == -1337) return 0; if (d->fd < 0) return 0; return 1; } /** Opens the drive for SCSI commands and - if burn activities are prone to external interference on your system - obtains an exclusive access lock on the drive. (Note: this is not physical tray locking.) A drive that has been opened with sg_grab() will eventually be handed over to sg_release() for closing and unreserving. */ int sg_grab(struct burn_drive *d) { int fd, os_errno= 0, ret; int max_tries = 3, tries = 0; /* ts A60813 */ int open_mode = O_RDWR; /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ if (mmc_function_spy(d, "sg_grab") <= 0) return 0; /* ts A60813 - A60927 O_EXCL with devices is a non-POSIX feature of Linux kernels. Possibly introduced 2002. Mentioned in "The Linux SCSI Generic (sg) HOWTO". */ if(burn_sg_open_o_excl) open_mode |= O_EXCL; /* ts A60813 O_NONBLOCK was hardcoded here. So it should stay default mode. ts A70411 Switched to O_NDELAY for LKML statement 2007/4/11/141 */ if(burn_sg_open_o_nonblock) open_mode |= O_NDELAY; /* ts A60813 - A60822 After enumeration the drive fd is probably still open. -1337 is the initial value of burn_drive.fd and the value after release of drive. Unclear why not the official error return value -1 of open(2) war used. */ if(! burn_drive_is_open(d)) { char msg[120]; /* >>> SINGLE_OPEN : This case should be impossible now, since enumeration transfers the fd from scanning to drive. So if close-wait-open is desired, then it has to be done unconditionally. */ #ifndef Libburn_udev_wait_useC #define Libburn_udev_wait_useC 100000 #endif #ifndef Libburn_udev_extra_open_cyclE if (Libburn_udev_wait_useC > 0) { /* ts B10921 : workaround for udev which might get a kernel event from open() and might remove links if it cannot inspect the drive. This waiting period shall allow udev to act after it was woken up by the drive scan activities. */ sprintf(msg, "To avoid collision with udev: Waiting %lu usec before grabbing", (unsigned long) Libburn_udev_wait_useC); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); usleep(Libburn_udev_wait_useC); } #endif /* Libburn_udev_extra_open_cyclE */ try_open:; /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ mmc_function_spy(NULL, "sg_grab ----------- opening"); /* ts A70409 : DDLP-B */ /* >>> obtain single lock on d->devname */ /* ts A60926 */ if(burn_sg_open_o_excl>1) { fd = -1; ret = sg_open_scsi_siblings(d->devname, d->global_index,d->sibling_fds, d->sibling_fnames,&(d->sibling_count), d->host, d->channel, d->id, d->lun); if(ret <= 0) goto drive_is_in_use; } fd = open(d->devname, open_mode); os_errno = errno; #ifdef Libburn_udev_extra_open_cyclE /* ts B10920 : workaround for udev which might get a kernel event from open() and might remove links if it cannot inspect the drive. ts B10921 : this is more obtrusive than above waiting before open(). The drive scan already has opened and closed the drive several times. So it seems to be merely about giving an opportunity to udev, before long term grabbing happens. */ if (fd >= 0 && Libburn_udev_wait_useC > 0) { close(fd); sprintf(msg, "To avoid collision with udev: Waiting %lu usec before re-opening", (unsigned long) Libburn_udev_wait_useC); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); usleep(Libburn_udev_wait_useC); fd = open(d->devname, open_mode); os_errno = errno; } #endif /* Libburn_udev_extra_open_cyclE */ if (fd >= 0) { sg_fcntl_lock(&fd, d->devname, F_WRLCK, 1); if (fd < 0) goto drive_is_in_use; } } else fd= d->fd; if (fd >= 0) { d->fd = fd; fcntl(fd, F_SETOWN, getpid()); d->released = 0; return 1; } else if (errno == EBUSY) goto drive_is_in_use; libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Could not grab drive", os_errno, 0); return 0; drive_is_in_use:; tries++; if (tries < max_tries) { libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "Drive is in use. Waiting 2 seconds before re-try", 0, 0); usleep(2000000); goto try_open; } libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Could not grab drive - already in use", 0, 0); sg_close_drive(d); d->fd = -1337; return 0; } /** PORTING: Is mainly about the call to sg_close_drive() and whether it implements the demanded functionality. */ /** Gives up the drive for SCSI commands and releases eventual access locks. (Note: this is not physical tray locking.) */ int sg_release(struct burn_drive *d) { /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ if (mmc_function_spy(d, "sg_release") <= 0) return 0; if (d->fd < 1) return 0; /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ mmc_function_spy(NULL, "sg_release ----------- closing"); sg_close_drive(d); return 0; } /* @return -1= transport failed, give up drive 0= transport failed, do not retry 1= transport succeeded 2- transport failed, please retry */ static int evaluate_transport_success(struct burn_drive *d, struct command *c, FILE *fp, unsigned short host_status, unsigned short driver_status) { int ret, do_retry= 0, give_up_drive= 0, sev; char *msg = NULL, *host_problem, *driver_problem, *driver_sugg; BURN_ALLOC_MEM(msg, char, 161); if ((host_status == Libburn_sg_host_oK && (driver_status & 0xf7) == Libburn_sg_driver_oK) || c->error) {ret = 1; goto ex;} /* No transport problems */ /* See http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/x291.html */ switch(host_status) { case 0x00: host_problem = "SG_ERR_DID_OK (No error)"; break; case 0x01: host_problem = "SG_ERR_DID_NO_CONNECT (Could not connect before timeout period)"; give_up_drive= 1; break; case 0x02: host_problem = "SG_ERR_DID_BUS_BUSY (Bus stayed busy through time out period)"; break; case 0x03: host_problem = "SG_ERR_DID_TIME_OUT (Timed out for miscellaneous reasons)"; break; case 0x04: host_problem = "SG_ERR_DID_BAD_TARGET (Bad target, device not responding ?)"; give_up_drive= 1; break; case 0x05: host_problem = "SG_ERR_DID_ABORT (Told to abort)"; break; case 0x06: host_problem = "SG_ERR_DID_PARITY (Parity error)"; break; case 0x07: host_problem = "SG_ERR_DID_ERROR (Internal error detected in the host adapter)"; give_up_drive= 1; break; case 0x08: host_problem = "SG_ERR_DID_RESET (The SCSI bus or the device have been reset)"; give_up_drive= 1; break; case 0x09: host_problem = "SG_ERR_DID_BAD_INTR (Got an unexpected interrupt)"; break; case 0x0a: host_problem = "SG_ERR_DID_PASSTHROUGH (Force command past mid-layer)"; break; case 0x0b: host_problem = "SG_ERR_DID_SOFT_ERROR (The low level driver wants a retry)"; do_retry = 1; break; default: host_problem = "? (unknown host_status code)"; } if (host_status != Libburn_sg_host_oK) { sprintf(msg, "SCSI command %2.2Xh yielded host problem: ", (unsigned int) c->opcode[0]); sprintf(msg+strlen(msg), "0x%x %s", (unsigned int) host_status, host_problem); sev = LIBDAX_MSGS_SEV_FAILURE; if (do_retry && !give_up_drive) sev = LIBDAX_MSGS_SEV_NOTE; libdax_msgs_submit(libdax_messenger, d->global_index, 0x000201a7, sev, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); strcpy(msg, "Command: "); if (spc_human_readable_cmd(c, msg + strlen(msg), 160 - strlen(msg), 0) > 0) libdax_msgs_submit(libdax_messenger, d->global_index, 0x000201a7, sev, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); sprintf(msg, "--- SG_IO: host_status= 0x%x %s", (unsigned int) host_status, host_problem); scsi_log_message(d, fp, msg, 0); } switch (driver_status & 0x07) { case 0: driver_problem = "SG_ERR_DRIVER_OK"; break; case 1: driver_problem = "SG_ERR_DRIVER_BUSY"; break; case 2: driver_problem = "SG_ERR_DRIVER_SOFT"; break; case 3: driver_problem = "SG_ERR_DRIVER_MEDIA"; break; case 4: driver_problem = "SG_ERR_DRIVER_ERROR"; break; case 5: driver_problem = "SG_ERR_DRIVER_INVALID"; break; case 6: driver_problem = "SG_ERR_DRIVER_TIMEOUT"; break; case 7: driver_problem = "SG_ERR_DRIVER_HARD"; break; default: driver_problem = "(unknown driver_status code)"; } switch (driver_status & 0xf0) { case 0: driver_sugg = "(no suggestion)"; break; case 0x10: driver_sugg = "SG_ERR_SUGGEST_RETRY"; do_retry = 1; break; case 0x20: driver_sugg = "SG_ERR_SUGGEST_ABORT"; give_up_drive= 1; break; case 0x30: driver_sugg = "SG_ERR_SUGGEST_REMAP"; give_up_drive= 1; break; case 0x40: driver_sugg = "SG_ERR_SUGGEST_DIE"; give_up_drive= 1; break; case 0x80: driver_sugg = "SG_ERR_SUGGEST_SENSE"; break; default: driver_sugg = "(unknown driver_status suggestion)"; } if ((driver_status & 0xf7) != Libburn_sg_driver_oK) { sprintf(msg, "SCSI command %2.2Xh yielded driver problem: ", (unsigned int) c->opcode[0]); sprintf(msg+strlen(msg), "driver_status= 0x%x %s / %s", (unsigned int) driver_status, driver_problem, driver_sugg); sev = LIBDAX_MSGS_SEV_FAILURE; if (do_retry && !give_up_drive) sev = LIBDAX_MSGS_SEV_NOTE; libdax_msgs_submit(libdax_messenger, d->global_index, 0x000201a8, sev, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); strcpy(msg, "Command: "); if (spc_human_readable_cmd(c, msg + strlen(msg), 160 - strlen(msg), 0) > 0) libdax_msgs_submit(libdax_messenger, d->global_index, 0x000201a8, sev, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); sprintf(msg, "--- SG_IO: driver_status= 0x%x %s / %s", (unsigned int) driver_status, driver_problem, driver_sugg); scsi_log_message(d, fp, msg, 0); } if (! do_retry) c->error = 1; ret = give_up_drive ? -1 : do_retry ? 2 : 0; ex:; BURN_FREE_MEM(msg); return ret; } static void react_on_drive_loss(struct burn_drive *d, struct command *c, FILE *fp) { sg_close_drive(d); d->released = 1; d->busy = BURN_DRIVE_IDLE; d->cancel = 1; c->error = 1; libdax_msgs_submit(libdax_messenger, d->global_index, 0x000201a6, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Lost connection to drive", 0, 0); scsi_log_message(d, fp, "--- SG_IO: Gave up connection to drive", 0); } /** Sends a SCSI command to the drive, receives reply and evaluates whether the command succeeded or shall be retried or finally failed. Returned SCSI errors shall not lead to a return value indicating failure. The callers get notified by c->error. An SCSI failure which leads not to a retry shall be notified via scsi_notify_error(). @return: 1 success , <=0 failure */ int sg_issue_command(struct burn_drive *d, struct command *c) { int done = 0, no_c_page = 0, i, ret; int err; time_t start_time; sg_io_hdr_t s; /* ts A61030 */ static FILE *fp= NULL; char *msg = NULL; BURN_ALLOC_MEM(msg, char, 161); c->error = 0; memset(c->sense, 0, sizeof(c->sense)); /* <<< ts A60821 debug: for tracing calls which might use open drive fds */ sprintf(msg, "sg_issue_command d->fd= %d d->released= %d\n", d->fd, d->released); mmc_function_spy(NULL, msg); /* >>> ts B11110 : move this into scsi_log_cmd() together with the static fp */ /* ts A61030 */ if (burn_sg_log_scsi & 1) { if (fp == NULL) { fp= fopen("/tmp/libburn_sg_command_log", "a"); fprintf(fp, "\n-----------------------------------------\n"); } } /* ts A61010 : with no fd there is no chance to send an ioctl */ if (d->fd < 0) { c->error = 1; {ret = 0; goto ex;} } c->error = 0; memset(&s, 0, sizeof(sg_io_hdr_t)); if (burn_sg_log_scsi & 3) scsi_log_cmd(c,fp,0); s.interface_id = 'S'; #ifdef Libburn_sgio_as_growisofS /* ??? ts A91112 : does this speed up USB ? (from growisofs) --- did not help */ s.flags = SG_FLAG_DIRECT_IO; #endif /* Libburn_sgio_as_growisofS */ 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; /* ts A61007 */ /* a ssert(!c->page); */ no_c_page = 1; } s.cmd_len = c->oplen; s.cmdp = c->opcode; s.mx_sb_len = 32; s.sbp = c->sense; if (c->timeout > 0) s.timeout = c->timeout; else s.timeout = Libburn_scsi_default_timeouT; if (c->page && !no_c_page) { s.dxferp = c->page->data; /* # def ine Libburn_debug_dxferP 1 */ #ifdef Libburn_debug_dxferP { char text[1024], *content; int i = c->page->bytes; if (c->dir == FROM_DRIVE) { for (i = 0; i < c->page->bytes && c->page->data[i] == 0; i++); content = (i < c->page->bytes) ? " (some nonzero)" : " (all zero)"; } else { i = c->page->bytes; content = ""; } sprintf(text, "dxferp before = %lx%s", (unsigned long) s.dxferp, content); scsi_log_text(text, fp, 0); } #endif if (c->dir == FROM_DRIVE) { /* ts A70519 : kernel 2.4 usb-storage seems to expect exact dxfer_len for data fetching commands. */ if (c->dxfer_len >= 0) s.dxfer_len = c->dxfer_len; else s.dxfer_len = BUFFER_SIZE; /* touch page so we can use valgrind */ memset(c->page->data, 0, BUFFER_SIZE); } else { /* ts A61010 */ /* a ssert(c->page->bytes > 0); */ if (c->page->bytes <= 0) { c->error = 1; {ret = 0; goto ex;} } s.dxfer_len = c->page->bytes; } } else { s.dxferp = NULL; s.dxfer_len = 0; } s.usr_ptr = c; /* ts B90523 : Record effective transfer length request for debugging*/ c->dxfer_len = s.dxfer_len; start_time = time(NULL); for(i = 0; !done; i++) { memset(c->sense, 0, sizeof(c->sense)); c->start_time = burn_get_time(0); err = ioctl(d->fd, SG_IO, &s); c->end_time = burn_get_time(0); #ifdef Libburn_debug_dxferP if (c->page && !no_c_page) { char text[1024]; sprintf(text, "dxferp after = %lx", (unsigned long) s.dxferp); scsi_log_text(text, fp, 0); } #endif /* ts A61010 */ /* a ssert(err != -1); */ if (err == -1) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Failed to transfer command to drive", errno, 0); sprintf(msg, "--- SG_IO: return= -1 , "); sprintf(msg + strlen(msg), "errno= %d , ", errno); sprintf(msg + strlen(msg), "host_status= 0x%x , driver_status= 0x%x", (unsigned int) s.host_status, (unsigned int) s.driver_status); scsi_log_message(d, fp, msg, 0); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); sprintf(msg, "Attempted command: "); spc_human_readable_cmd(c, msg + strlen(msg), 160 - strlen(msg), 0); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); react_on_drive_loss(d, c, fp); {ret = -1; goto ex;} } done = scsi_eval_cmd_outcome(d, c, fp, (unsigned char *) (s.sbp), s.sb_len_wr, start_time, s.timeout, i, 0); if (d->cancel) break; ret = evaluate_transport_success(d, c, fp, s.host_status, s.driver_status); if (ret == -1) react_on_drive_loss(d, c, fp); if (ret <= 0) {ret = -1; goto ex;} if (d->cancel) break; /* if ! done : loop for retry */; if (!done) { spc_register_retry(c); if (burn_sg_log_scsi & 3) { scsi_log_text("+++ Repeating command", fp, 0); scsi_log_cmd(c, fp, 0); } } } ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } /* ts B11001 : outsourced from non-static sg_obtain_scsi_adr() */ /** Tries to obtain SCSI address parameters. @return 1 is success , 0 is failure */ static int sg_obtain_scsi_adr_fd(char *path, int fd_in, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no) { int fd, ret, l, open_mode = O_RDONLY; struct my_scsi_idlun { int x; int host_unique_id; }; struct my_scsi_idlun idlun; /* valgrind called idlun uninitialized because it is blind for ioctl */ idlun.x = 0; idlun.host_unique_id = 0; l = strlen(linux_ata_device_family) - 2; if (l > 0 && strncmp(path, linux_ata_device_family, l) == 0 && path[7] >= 'a' && path[7] <= 'z' && path[8] == 0) return 0; /* on RIP 14 all hdx return SCSI adr 0,0,0,0 */ /* ts A70409 : DDLP-B */ /* >>> obtain single lock on path */ if(burn_sg_open_o_nonblock) open_mode |= O_NDELAY; if(burn_sg_open_o_excl) { /* O_EXCL | O_RDONLY does not work with /dev/sg* on SuSE 9.0 (kernel 2.4) and SuSE 9.3 (kernel 2.6) */ /* so skip it for now */; } if (fd_in >= 0) fd = fd_in; else fd = open(path, open_mode); if(fd < 0) return 0; sg_fcntl_lock(&fd, path, F_RDLCK, 0); if(fd < 0) return 0; #ifdef SCSI_IOCTL_GET_BUS_NUMBER /* Hearsay A61005 */ if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, bus_no) == -1) *bus_no = -1; #endif /* http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/scsi_g_idlun.html */ ret = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun); if (fd_in < 0) sg_close_drive_fd(path, -1, &fd, 0); if (ret == -1) return(0); *host_no= (idlun.x>>24)&255; *channel_no= (idlun.x>>16)&255; *target_no= (idlun.x)&255; *lun_no= (idlun.x>>8)&255; #ifdef SCSI_IOCTL_GET_BUS_NUMBER if(*bus_no == -1) *bus_no = 1000 * (*host_no + 1) + *channel_no; #else *bus_no= *host_no; #endif return 1; } /* ts A60922 */ /** Tries to obtain SCSI address parameters. @return 1 is success , 0 is failure */ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no) { return sg_obtain_scsi_adr_fd(path, -1, bus_no, host_no, channel_no, target_no, lun_no); } /* ts A60922 ticket 33 : called from drive.c */ /** Tells whether a text is a persistent address as listed by the enumeration functions. */ int sg_is_enumerable_adr(char *adr) { char *fname = NULL; int ret = 0, first = 1, fname_size = 4096; burn_drive_enumerator_t idx; BURN_ALLOC_MEM(fname, char, fname_size); while (1) { ret= sg_give_next_adr(&idx, fname, fname_size, first); if(ret <= 0) break; first = 0; if (strcmp(adr, fname) == 0) { sg_give_next_adr(&idx, fname, fname_size, -1); {ret = 1; goto ex;} } } ret = 0; ex:; if (first == 0) sg_give_next_adr(&idx, fname, fname_size, -1); BURN_FREE_MEM(fname); return ret; } /* ts B00115 */ /* Return 1 if the given path leads to a regular file or a device that can be fseeked, read, and possibly written with 2 kB granularity. */ int burn_os_is_2k_seekrw(char *path, int flag) { struct stat stbuf; if (stat(path, &stbuf) == -1) return 0; if (S_ISREG(stbuf.st_mode)) return 1; if (S_ISBLK(stbuf.st_mode)) return 1; return 0; } /* ts A70909 */ /** Estimate the potential payload capacity of a file address. @param path The address of the file to be examined. If it does not exist yet, then the directory will be inquired. @param bytes The pointed value gets modified, but only if an estimation is possible. @return -2 = cannot perform necessary operations on file object -1 = neither path nor dirname of path exist 0 = could not estimate size capacity of file object 1 = estimation has been made, bytes was set */ int burn_os_stdio_capacity(char *path, off_t write_start, off_t *bytes) { struct stat stbuf; struct statvfs vfsbuf; char *testpath = NULL, *cpt; long blocks; int open_mode = O_RDONLY, fd, ret; off_t add_size = 0; BURN_ALLOC_MEM(testpath, char, 4096); testpath[0] = 0; if (stat(path, &stbuf) == -1) { strcpy(testpath, path); cpt = strrchr(testpath, '/'); if(cpt == NULL) strcpy(testpath, "."); else if(cpt == testpath) testpath[1] = 0; else *cpt = 0; if (stat(testpath, &stbuf) == -1) {ret = -1; goto ex;} } else if(S_ISBLK(stbuf.st_mode)) { fd = open(path, open_mode); if (fd == -1) {ret = -2; goto ex;} ret = ioctl(fd, BLKGETSIZE, &blocks); close(fd); if (ret == -1) {ret = -2; goto ex;} *bytes = ((off_t) blocks) * (off_t) 512; } else if(S_ISREG(stbuf.st_mode)) { add_size = burn_sparse_file_addsize(write_start, &stbuf); strcpy(testpath, path); } else {ret = 0; goto ex;} if (testpath[0]) { if (statvfs(testpath, &vfsbuf) == -1) {ret = -2; goto ex;} *bytes = add_size + ((off_t) vfsbuf.f_frsize) * (off_t) vfsbuf.f_bavail; } #ifdef NIX /* <<< */ fprintf(stderr, "libburn_DEBUG: Faking 4.5 TB of disk space\n"); *bytes = ((off_t) 2415919104) * (off_t) 2048; if (*bytes / (off_t) 2048 > (off_t) 0x7ffffff0) { *bytes = ((off_t) 0x7ffffff0) * (off_t) 2048; fprintf(stderr, "libburn_DEBUG: Reducing disk space to 4 TB - 2 kB\n"); } /* <<< */ #endif ret = 1; ex:; BURN_FREE_MEM(testpath); return ret; } /* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ #ifdef PROT_READ #ifdef PROT_WRITE #ifdef MAP_SHARED #ifdef MAP_ANONYMOUS #ifdef MAP_FAILED #define Libburn_linux_do_mmaP 1 #endif #endif #endif #endif #endif #ifdef Libburn_read_o_direcT #ifdef O_DIRECT #define Libburn_linux_do_o_direcT 1 #endif #endif /* Libburn_read_o_direcT */ int burn_os_open_track_src(char *path, int open_flags, int flag) { int fd; #ifdef Libburn_linux_do_o_direcT libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "Opening track source with O_DIRECT" , 0, 0); fd = open(path, open_flags | O_DIRECT); #else fd = open(path, open_flags); #endif return fd; } void *burn_os_alloc_buffer(size_t amount, int flag) { void *buf = NULL; #ifdef Libburn_linux_do_mmaP /* >>> check whether size is suitable */; libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "Allocating buffer via mmap()" , 0, 0); buf = mmap(NULL, amount, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, (off_t) 0); if (buf == MAP_FAILED) buf = NULL; else memset(buf, 0, amount); #else buf = calloc(1, amount); #endif /* ! Libburn_linux_do_mmaP */ return buf; } int burn_os_free_buffer(void *buffer, size_t amount, int flag) { int ret = 0; if (buffer == NULL) return 0; #ifdef Libburn_linux_do_mmaP ret = munmap(buffer, amount); #else free(buffer); #endif return (ret == 0); } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2010 - 2016 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. Derived 2014 from libburn/sg-solaris.c with information learned from dvd+rw-tools, http://fxr.watson.org/fxr/source/sys/scsiio.h?v=NETBSD, http://netbsd.gw.com/cgi-bin/man-cgi?scsi+4+NetBSD-current, and experiments made by Freddy Fisker. Adapted 2016 to OpenBSD by help of SASANO Takayoshi <uaa@mx5.nisiq.net>. */ /* This is the main operating system dependent SCSI part of libburn. It implements the transport level aspects of SCSI control and command i/o. Present implementation: NetBSD 6, ioctl SCIOCCOMMAND OpenBSD 5.9, ioctl SCIOCCOMMAND PORTING: Porting libburn typically will consist of adding a new operating system case to the following switcher files: os.h Operating system specific libburn definitions and declarations. sg.c Operating system dependent transport level modules. and of deriving the following system specific files from existing examples: os-*.h Included by os.h. You will need some general system knowledge about signals and knowledge about the storage object needs of your transport level module sg-*.c. sg-*.c This source module. You will need special system knowledge about how to detect all potentially available drives, how to open them, eventually how to exclusively reserve them, how to perform SCSI transactions, how to inquire the (pseudo-)SCSI driver. You will not need to care about CD burning, MMC or other high-level SCSI aspects. Said sg-*.c operations are defined by a public function interface, which has to be implemented in a way that provides libburn with the desired services: sg_id_string() returns an id string of the SCSI transport adapter. It may be called before initialization but then may return only a preliminary id. sg_initialize() performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility of supporting software components. sg_shutdown() performs global finalizations and releases globally acquired resources. sg_give_next_adr() iterates over the set of potentially useful drive address strings. scsi_enumerate_drives() brings all available, not-whitelist-banned, and accessible drives into libburn's list of drives. sg_dispose_drive() finalizes adapter specifics of struct burn_drive on destruction. Releases resources which were acquired underneath scsi_enumerate_drives(). sg_drive_is_open() tells whether libburn has the given drive in use. sg_grab() opens the drive for SCSI commands and ensures undisturbed access. sg_release() closes a drive opened by sg_grab() sg_issue_command() sends a SCSI command to the drive, receives reply, and evaluates whether the command succeeded or shall be retried or finally failed. sg_obtain_scsi_adr() tries to obtain SCSI address parameters. burn_os_is_2k_seekrw() tells whether the given path leads to a file object that can be used in 2 kB granularity by lseek(2), read(2), and possibly write(2) if not read-only.. E.g. a USB stick or a hard disk. burn_os_stdio_capacity() estimates the emulated media space of stdio-drives. burn_os_open_track_src() opens a disk file in a way that offers best throughput with file reading and/or SCSI write command transmission. burn_os_alloc_buffer() allocates a memory area that is suitable for file descriptors issued by burn_os_open_track_src(). The buffer size may be rounded up for alignment reasons. burn_os_free_buffer() delete a buffer obtained by burn_os_alloc_buffer(). Porting hints are marked by the text "PORTING:". Send feedback to libburn-hackers@pykix.org . */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /** PORTING : ------- OS dependent headers and definitions ------ */ #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <errno.h> #include <fcntl.h> #include <sys/stat.h> #include <string.h> #include <stdlib.h> #ifdef Libburn_os_has_statvfS #include <sys/statvfs.h> #endif /* Libburn_os_has_stavtfS */ #include <sys/ioctl.h> #include <sys/scsiio.h> /** PORTING : ------ libburn portable headers and definitions ----- */ #include "transport.h" #include "drive.h" #include "sg.h" #include "spc.h" #include "sbc.h" #include "debug.h" #include "toc.h" #include "util.h" #include "init.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /* is in portable part of libburn */ int burn_drive_is_banned(char *device_address); int burn_drive_resolve_link(char *path, char adr[], int *recursion_count, int flag); /* drive.c */ /* Whether to log SCSI commands: bit0= log in /tmp/libburn_sg_command_log bit1= log to stderr bit2= flush every line */ extern int burn_sg_log_scsi; /* ------------------------------------------------------------------------ */ /* PORTING: Private definitions. Port only if needed by public functions. */ /* (Public functions are listed below) */ /* ------------------------------------------------------------------------ */ /* Storage object is in libburn/init.c whether to strive for exclusive access to the drive */ extern int burn_sg_open_o_excl; /* ------------------------------------------------------------------------ */ /* PORTING: Private functions. Port only if needed by public functions */ /* (Public functions are listed below) */ /* ------------------------------------------------------------------------ */ static int sg_close_drive(struct burn_drive * d) { if (d->fd != -1) { close(d->fd); d->fd = -1; return 1; } return 0; } /* ----------------------------------------------------------------------- */ /* PORTING: Private functions which contain publicly needed functionality. */ /* Their portable part must be performed. So it is probably best */ /* to replace the non-portable part and to call these functions */ /* in your port, too. */ /* ----------------------------------------------------------------------- */ /** Wraps a detected drive into libburn structures and hands it over to libburn drive list. */ static void enumerate_common(char *fname, int bus_no, int host_no, int channel_no, int target_no, int lun_no) { int ret; struct burn_drive out; /* General libburn drive setup */ burn_setup_drive(&out, fname); /* This transport adapter uses SCSI-family commands and models (seems the adapter would know better than its boss, if ever) */ ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no, target_no, lun_no, 0); if (ret <= 0) return; /* PORTING: ------------------- non portable part --------------- */ /* Transport adapter is NetBSD/OpenBSD ioctl SCIOCCOMMAND */ /* Adapter specific handles and data */ out.fd = -1; /* PORTING: ---------------- end of non portable part ------------ */ /* Adapter specific functions with standardized names */ out.grab = sg_grab; out.release = sg_release; out.drive_is_open = sg_drive_is_open; out.issue_command = sg_issue_command; /* Finally register drive and inquire drive information */ burn_drive_finish_enum(&out); } static int start_enum_rcdNx(burn_drive_enumerator_t *idx, int flag) { idx->cdno = -1; return 1; } /* Trying /dev/rcd[0..63][dc] */ #define Libburn_netbsd_max_cdnuM 63 static int next_enum_rcdNx(burn_drive_enumerator_t *idx, char adr[], int adr_size, int flag) { static char suffix[2] = {'d', 'c'}; struct stat stbuf; int i, stat_ret; char path[16]; while (idx->cdno < Libburn_netbsd_max_cdnuM) { idx->cdno++; for (i = 0; i < 2; i++) { sprintf(path, "/dev/rcd%d%c", idx->cdno, suffix[i]); stat_ret = stat(path, &stbuf); if (stat_ret == -1) continue; if (!S_ISCHR(stbuf.st_mode)) continue; if ((int) strlen(path) >= adr_size) continue; strcpy(adr, path); return 1; } } return 0; } /* Searching the first byte address that cannot be lseeked and read */ static int guess_size_by_seek_set(int fd, off_t *bytes, int flag) { static off_t abs_limit = ((off_t) 1024) * 1024 * 1024 * 1024 * 1024; off_t i, step = ((off_t) 1024) * 1024 * 1024 * 1024, ret; char buf[1]; *bytes = 0; for (i = step; i < abs_limit; i += step) { ret = lseek(fd, i, SEEK_SET); if (ret == -1) { i -= step; step = step >> 1; if (step > 0) continue; return 1; } ret = read(fd, buf, 1); if (ret == -1) { i -= step; step = step >> 1; if (step > 0) continue; return 1; } *bytes = i + 1; } return 0; } /* ------------------------------------------------------------------------ */ /* PORTING: Public functions. These MUST be ported. */ /* ------------------------------------------------------------------------ */ /** Returns the id string of the SCSI transport adapter and eventually needed operating system facilities. This call is usable even if sg_initialize() was not called yet. In that case a preliminary constant message might be issued if detailed info is not available yet. @param msg returns id string @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_id_string(char msg[1024], int flag) { #ifdef __OpenBSD__ sprintf(msg, "internal OpenBSD SCIOCCOMMAND adapter sg-netbsd"); #else sprintf(msg, "internal NetBSD SCIOCCOMMAND adapter sg-netbsd"); #endif return 1; } /** Performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility of supporting software components. @param msg returns ids and/or error messages of eventual helpers @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_initialize(char msg[1024], int flag) { return sg_id_string(msg, 0); } /** Performs global finalization of the SCSI transport adapter and eventually needed operating system facilities. Releases globally acquired resources. @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_shutdown(int flag) { return 1; } /** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of struct burn_drive which are defined in os-*.h. The eventual initialization of those components was made underneath scsi_enumerate_drives(). This will be called when a burn_drive gets disposed. @param d the drive to be finalized @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_dispose_drive(struct burn_drive *d, int flag) { return 1; } /** Returns the next index number and the next enumerated drive address. The enumeration has to cover all available and accessible drives. It is allowed to return addresses of drives which are not available but under some (even exotic) circumstances could be available. It is on the other hand allowed, only to hand out addresses which can really be used right in the moment of this call. (This implementation chooses the former.) @param idx An opaque handle. Make no own theories about it. @param adr Takes the reply @param adr_size Gives maximum size of reply including final 0 @param initialize 1 = start new, 0 = continue, use no other values for now -1 = finish @return 1 = reply is a valid address , 0 = no further address available -1 = severe error (e.g. adr_size too small) */ int sg_give_next_adr(burn_drive_enumerator_t *idx, char adr[], int adr_size, int initialize) { int ret; if (initialize == 1) { ret = start_enum_rcdNx(idx, 0); if (ret <= 0) return ret; } else if (initialize == -1) { return 0; } ret = next_enum_rcdNx(idx, adr, adr_size, 0); return ret; } /** Brings all available, not-whitelist-banned, and accessible drives into libburn's list of drives. */ int scsi_enumerate_drives(void) { burn_drive_enumerator_t idx; int initialize = 1, ret, i_bus_no = -1, buf_size = 4096; int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1; char *buf = NULL; BURN_ALLOC_MEM(buf, char, buf_size); while(1) { ret = sg_give_next_adr(&idx, buf, buf_size, initialize); initialize = 0; if (ret <= 0) break; if (burn_drive_is_banned(buf)) continue; sg_obtain_scsi_adr(buf, &i_bus_no, &i_host_no, &i_channel_no, &i_target_no, &i_lun_no); enumerate_common(buf, i_bus_no, i_host_no, i_channel_no, i_target_no, i_lun_no); } sg_give_next_adr(&idx, buf, buf_size, -1); ret = 1; ex:; BURN_FREE_MEM(buf); return ret; } /** Tells whether libburn has the given drive in use or exclusively reserved. If it is "open" then libburn will eventually call sg_release() on it when it is time to give up usage and reservation. */ /** Published as burn_drive.drive_is_open() */ int sg_drive_is_open(struct burn_drive * d) { return (d->fd != -1); } /** Opens the drive for SCSI commands and - if burn activities are prone to external interference on your system - obtains an exclusive access lock on the drive. (Note: this is not physical tray locking.) A drive that has been opened with sg_grab() will eventually be handed over to sg_release() for closing and unreserving. */ int sg_grab(struct burn_drive *d) { char *msg = NULL; int os_errno, ret; BURN_ALLOC_MEM(msg, char, 4096); if (d->fd != -1) { d->released = 0; {ret = 1; goto ex;} } d->fd = open(d->devname, O_RDWR | O_NDELAY); if (d->fd == -1) { os_errno = errno; sprintf(msg, "Could not grab drive '%s'", d->devname); /* (errno == ENXIO is a device file with no drive attached) */ libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, errno == ENXIO ? LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0); {ret = 0; goto ex;} } d->released = 0; /* Make sure by INQUIRY that this is really a MMC drive */ ret = spc_confirm_cd_drive(d, 0); if (ret <= 0) goto revoke; /* # define Libburn_sg_netbsd_scsi_debuG */ #ifdef Libburn_sg_netbsd_scsi_debuG { static int sc_db = SC_DB_CMDS | SC_DB_FLOW; ret = ioctl(d->fd, SCIOCDEBUG, &sc_db); if (ret == -1) fprintf(stderr, "libburn_DEBUG: ioctl(%d, SCIOCDEBUG, &(0x%X)) returns %d, errno = %d\n", d->fd, (unsigned int) sc_db, ret, errno); } #endif {ret = 1; goto ex;} revoke:; sprintf(msg, "Could not grab drive '%s'.", d->devname); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); if (d->fd >= 0) { close(d->fd); d->fd = -1; d->released = 1; } ret = 0; ex:; BURN_FREE_MEM(msg); return ret; } /** PORTING: Is mainly about the call to sg_close_drive() and whether it implements the demanded functionality. */ /** Gives up the drive for SCSI commands and releases eventual access locks. (Note: this is not physical tray locking.) */ int sg_release(struct burn_drive *d) { if (d->fd < 0) return 0; sg_close_drive(d); return 0; } /** Sends a SCSI command to the drive, receives reply and evaluates whether the command succeeded or shall be retried or finally failed. Returned SCSI errors shall not lead to a return value indicating failure. The callers get notified by c->error. An SCSI failure which leads not to a retry shall be notified via scsi_notify_error(). The Libburn_log_sg_commandS facility might be of help when problems with a drive have to be examined. It shall stay disabled for normal use. @return: 1 success , <=0 failure */ int sg_issue_command(struct burn_drive *d, struct command *c) { int i, timeout_ms, ret, key, asc, ascq, done = 0, sense_len, max_sl; time_t start_time; scsireq_t req; char msg[160]; static FILE *fp = NULL; c->error = 0; if (d->fd == -1) return 0; if (burn_sg_log_scsi & 1) { if (fp == NULL) { fp= fopen("/tmp/libburn_sg_command_log", "a"); fprintf(fp, "\n-----------------------------------------\n"); } } if (burn_sg_log_scsi & 3) scsi_log_cmd(c,fp,0); if (c->timeout > 0) timeout_ms = c->timeout; else timeout_ms = 200000; memset (&req, 0, sizeof(req)); memcpy(req.cmd, c->opcode, c->oplen); req.cmdlen = c->oplen; req.databuf = (caddr_t) c->page->data; req.flags = SCCMD_ESCAPE; /* probably to make req.cmdlen significant */ req.timeout = timeout_ms; max_sl = sizeof(c->sense) > SENSEBUFLEN ? SENSEBUFLEN : sizeof(c->sense); req.senselen = max_sl; if (c->dir == TO_DRIVE) { req.datalen = c->page->bytes; req.flags |= SCCMD_WRITE; } else if (c->dir == FROM_DRIVE) { req.flags |= SCCMD_READ; if (c->dxfer_len >= 0) req.datalen = c->dxfer_len; else req.datalen = BUFFER_SIZE; /* touch page so we can use valgrind */ memset(c->page->data, 0, BUFFER_SIZE); } else { req.flags |= SCCMD_READ; req.datalen = 0; } /* ts B90523 : Record effective transfer length request for debugging*/ c->dxfer_len = req.datalen; /* retry-loop */ start_time = time(NULL); for(i = 0; !done; i++) { memset(c->sense, 0, sizeof(c->sense)); c->start_time = burn_get_time(0); ret = ioctl(d->fd, SCIOCCOMMAND, &req); /* <<< Fault mock-up if (c->opcode[0] == 0x28) { ret = -1; errno = 9; } */ c->end_time = burn_get_time(0); /* #define Libburn_debug_sg_netbsD */ #ifdef Libburn_debug_sg_netbsD fprintf(stderr, "libburn_DEBUG: ret= %d, retsts = 0x%X, senselen_used = %d, status = 0x%X, error= 0x%X\n", ret, (unsigned int) req.retsts, (int) req.senselen_used, (unsigned int) req.status, req.error); fprintf(stderr, "libburn_DEBUG: datalen_used = %u\n", (unsigned int) req.datalen_used); #endif if (ret != 0 || (req.retsts != SCCMD_SENSE && req.retsts != SCCMD_OK)) { sprintf(msg, "Failed to transfer command to drive. (ioctl(%d, SCIOCCOMMAND) = %d, scsireq_t.retsts = 0x%X, errno= %d)", d->fd, ret, (unsigned int) req.retsts, errno); if (burn_sg_log_scsi & 3) scsi_log_message(d, fp, msg, 0); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); sg_close_drive(d); d->released = 1; d->busy = BURN_DRIVE_IDLE; c->error = 1; return -1; } sense_len = 0; if (req.retsts == SCCMD_SENSE) { memcpy(c->sense, req.sense, max_sl); sense_len = req.senselen > max_sl ? max_sl : req.senselen; } spc_decode_sense(c->sense, sense_len, &key, &asc, &ascq); if (key || asc || ascq) sense_len = req.senselen; else sense_len = 0; /* <<< Fault mock-up if (c->opcode[0] == 0x5a) { req.datalen_used = 0; memset(c->page->data, 0, BUFFER_SIZE); } */ if (c->dir == FROM_DRIVE && sense_len == 0 && req.datalen > 0 && req.datalen_used < req.datalen) { sprintf(msg, "Short reply from SCSI command %2.2X: expected: %d, got: %d, req.retsts: 0x%X", (unsigned int) c->opcode[0], (int) req.datalen, (int) req.datalen_used, (unsigned int) req.retsts); if (burn_sg_log_scsi & 3) scsi_log_message(d, fp, msg, 0); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); if (req.datalen_used == 0) c->error = 1; c->dxfer_len = req.datalen_used; } done = scsi_eval_cmd_outcome(d, c, fp, c->sense, sense_len, start_time, timeout_ms, i, 0); if (d->cancel) done = 1; if (!done) spc_register_retry(c); } /* end of retry-loop */ return 1; } /** Tries to obtain SCSI address parameters. @return 1 is success , 0 is failure */ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no) { int ret, fd = -1; struct scsi_addr addr; fd = open(path, O_RDWR | O_NDELAY); if (fd == -1) return 0; *bus_no = *host_no = *channel_no = *target_no = *lun_no = 0; memset(&addr, 0, sizeof(addr)); ret = ioctl(fd, SCIOCIDENTIFY, &addr); if (ret != 0) {ret = 0; goto ex;} if (addr.type != TYPE_SCSI) {ret = 0; goto ex;} #ifdef __OpenBSD__ *bus_no = *host_no = addr.scbus; *target_no = addr.target; *lun_no = addr.lun; #else /* __OpenBSD__ */ *bus_no = *host_no = addr.addr.scsi.scbus; *target_no = addr.addr.scsi.target; *lun_no = addr.addr.scsi.lun; #endif /* ! __OpenBSD__ */ ret = 1; ex:; if (fd != -1) close(fd); return (0); } /** Tells whether a text is a persistent address as listed by the enumeration functions. */ int sg_is_enumerable_adr(char* adr) { burn_drive_enumerator_t idx; int initialize = 1, ret; char buf[64]; while(1) { ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); initialize = 0; if (ret <= 0) break; if (strcmp(adr, buf) == 0) { sg_give_next_adr(&idx, buf, sizeof(buf), -1); return 1; } } sg_give_next_adr(&idx, buf, sizeof(buf), -1); return (0); } /* Return 1 if the given path leads to a regular file or a device that can be fseeked, read, and possibly written with 2 kB granularity. */ int burn_os_is_2k_seekrw(char *path, int flag) { struct stat stbuf; int l, i, dev, tl; char try[16]; /* >>> ??? Is this a comprehensive list of lseek()-capable devices ? */ /* http://www.netbsd.org/docs/guide/en/chap-rmmedia.html */ static char dev_names[][4] = { "fd", "rfd", "sd" , "cd", "rcd", "wd", ""}; if (path[0] == 0) return 0; if (stat(path, &stbuf) == -1) return 0; if (S_ISREG(stbuf.st_mode)) return 1; if (S_ISBLK(stbuf.st_mode)) return 1; /* Look for known device names which promise the desired capabilities */ if (strncmp(path, "/dev/", 5) != 0) return 0; l = strlen(path); for (dev = 0; dev_names[dev][0] != 0; dev++) { sprintf(try, "/dev/%s", dev_names[dev]); tl = strlen(try); if (strncmp(path, try, tl) != 0) continue; l -= tl; for (i = 0; i < Libburn_netbsd_max_cdnuM; i++) { sprintf(try + tl, "%d", i); if (strncmp(path, try, strlen(try)) == 0) break; } if (i >= Libburn_netbsd_max_cdnuM) continue; tl += strlen(try + tl); if (l == tl) return 1; if (l > tl + 1) continue; if (path[l - 1] >= 'a' && path[l - 1] <= 'z') return 1; } return 0; } /** Estimate the potential payload capacity of a file address. @param path The address of the file to be examined. If it does not exist yet, then the directory will be inquired. @param bytes The pointed value gets modified, but only if an estimation is possible. @return -2 = cannot perform necessary operations on file object -1 = neither path nor dirname of path exist 0 = could not estimate size capacity of file object 1 = estimation has been made, bytes was set */ int burn_os_stdio_capacity(char *path, off_t write_start, off_t *bytes) { struct stat stbuf; int ret; #ifdef Libburn_os_has_statvfS struct statvfs vfsbuf; #endif char *testpath = NULL, *cpt; off_t add_size = 0; BURN_ALLOC_MEM(testpath, char, 4096); testpath[0] = 0; if (stat(path, &stbuf) == -1) { strcpy(testpath, path); cpt = strrchr(testpath, '/'); if(cpt == NULL) strcpy(testpath, "."); else if(cpt == testpath) testpath[1] = 0; else *cpt = 0; if (stat(testpath, &stbuf) == -1) {ret = -1; goto ex;} } else if(S_ISBLK(stbuf.st_mode)) { int open_mode = O_RDONLY, fd; fd = open(path, open_mode); if (fd == -1) {ret = -2; goto ex;} *bytes = lseek(fd, 0, SEEK_END); if (*bytes <= 0) guess_size_by_seek_set(fd, bytes, 0); close(fd); if (*bytes == -1) { *bytes = 0; {ret = 0; goto ex;} } } else if(S_ISREG(stbuf.st_mode)) { add_size = burn_sparse_file_addsize(write_start, &stbuf); strcpy(testpath, path); } else {ret = 0; goto ex;} if (testpath[0]) { #ifdef Libburn_os_has_statvfS if (statvfs(testpath, &vfsbuf) == -1) {ret = -2; goto ex;} *bytes = add_size + ((off_t) vfsbuf.f_frsize) * (off_t) vfsbuf.f_bavail; #else /* Libburn_os_has_statvfS */ {ret = 0; goto ex;} #endif /* ! Libburn_os_has_stavtfS */ } ret = 1; ex:; BURN_FREE_MEM(testpath); return ret; } /* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ #ifdef Libburn_read_o_direcT /* No special O_DIRECT-like precautions are implemented here */ #endif /* Libburn_read_o_direcT */ int burn_os_open_track_src(char *path, int open_flags, int flag) { int fd; fd = open(path, open_flags); return fd; } void *burn_os_alloc_buffer(size_t amount, int flag) { void *buf = NULL; buf = calloc(1, amount); return buf; } int burn_os_free_buffer(void *buffer, size_t amount, int flag) { if (buffer == NULL) return 0; free(buffer); return 1; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2010 - 2016 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ /* This is the main operating system dependent SCSI part of libburn. It implements the transport level aspects of SCSI control and command i/o. Present implementation: Solaris uscsi, e.g. for SunOS 5.11 PORTING: Porting libburn typically will consist of adding a new operating system case to the following switcher files: os.h Operating system specific libburn definitions and declarations. sg.c Operating system dependent transport level modules. and of deriving the following system specific files from existing examples: os-*.h Included by os.h. You will need some general system knowledge about signals and knowledge about the storage object needs of your transport level module sg-*.c. sg-*.c This source module. You will need special system knowledge about how to detect all potentially available drives, how to open them, eventually how to exclusively reserve them, how to perform SCSI transactions, how to inquire the (pseudo-)SCSI driver. You will not need to care about CD burning, MMC or other high-level SCSI aspects. Said sg-*.c operations are defined by a public function interface, which has to be implemented in a way that provides libburn with the desired services: sg_id_string() returns an id string of the SCSI transport adapter. It may be called before initialization but then may return only a preliminary id. sg_initialize() performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility of supporting software components. sg_shutdown() performs global finalizations and releases globally acquired resources. sg_give_next_adr() iterates over the set of potentially useful drive address strings. scsi_enumerate_drives() brings all available, not-whitelist-banned, and accessible drives into libburn's list of drives. sg_dispose_drive() finalizes adapter specifics of struct burn_drive on destruction. Releases resources which were acquired underneath scsi_enumerate_drives(). sg_drive_is_open() tells whether libburn has the given drive in use. sg_grab() opens the drive for SCSI commands and ensures undisturbed access. sg_release() closes a drive opened by sg_grab() sg_issue_command() sends a SCSI command to the drive, receives reply, and evaluates whether the command succeeded or shall be retried or finally failed. sg_obtain_scsi_adr() tries to obtain SCSI address parameters. burn_os_is_2k_seekrw() tells whether the given path leads to a file object that can be used in 2 kB granularity by lseek(2), read(2), and possibly write(2) if not read-only.. E.g. a USB stick or a hard disk. burn_os_stdio_capacity() estimates the emulated media space of stdio-drives. burn_os_open_track_src() opens a disk file in a way that offers best throughput with file reading and/or SCSI write command transmission. burn_os_alloc_buffer() allocates a memory area that is suitable for file descriptors issued by burn_os_open_track_src(). The buffer size may be rounded up for alignment reasons. burn_os_free_buffer() delete a buffer obtained by burn_os_alloc_buffer(). Porting hints are marked by the text "PORTING:". Send feedback to libburn-hackers@pykix.org . */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /** PORTING : ------- OS dependent headers and definitions ------ */ #include <unistd.h> #include <stropts.h> #include <stdio.h> #include <sys/types.h> #include <errno.h> #include <fcntl.h> #include <sys/stat.h> #include <string.h> #include <stdlib.h> #include <dirent.h> #ifdef Libburn_os_has_statvfS #include <sys/statvfs.h> #endif /* Libburn_os_has_stavtfS */ #include <volmgt.h> #include <sys/dkio.h> #include <sys/vtoc.h> #include <sys/scsi/impl/uscsi.h> /* The waiting time before eventually retrying a failed SCSI command. Before each retry wait Libburn_sg_linux_retry_incR longer than with the previous one. */ #define Libburn_sg_solaris_retry_usleeP 100000 #define Libburn_sg_solaris_retry_incR 100000 /** PORTING : ------ libburn portable headers and definitions ----- */ #include "transport.h" #include "drive.h" #include "sg.h" #include "spc.h" #include "sbc.h" #include "debug.h" #include "toc.h" #include "util.h" #include "init.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /* is in portable part of libburn */ int burn_drive_is_banned(char *device_address); int burn_drive_resolve_link(char *path, char adr[], int *recursion_count, int flag); /* drive.c */ /* Whether to log SCSI commands: bit0= log in /tmp/libburn_sg_command_log bit1= log to stderr bit2= flush every line */ extern int burn_sg_log_scsi; /* ------------------------------------------------------------------------ */ /* PORTING: Private definitions. Port only if needed by public functions. */ /* (Public functions are listed below) */ /* ------------------------------------------------------------------------ */ /* Storage object is in libburn/init.c whether to strive for exclusive access to the drive */ extern int burn_sg_open_o_excl; /* ------------------------------------------------------------------------ */ /* PORTING: Private functions. Port only if needed by public functions */ /* (Public functions are listed below) */ /* ------------------------------------------------------------------------ */ static int sg_close_drive(struct burn_drive * d) { if (d->fd != -1) { close(d->fd); d->fd = -1; return 1; } return 0; } static int decode_btl_number(char **cpt, int stopper, int *no) { *no = 0; for ((*cpt)++; **cpt != stopper; (*cpt)++) { if (**cpt < '0' || **cpt > '9') return 0; *no = *no * 10 + **cpt - '0'; } return 1; } /* Read bus, target, lun from name "cXtYdZs2" or "cXtYdZ/...". Return 0 if name is not of the desired form. */ static int decode_btl_solaris(char *name, int *busno, int *tgtno, int *lunno, int flag) { char *cpt, *cpt_mem; int ret; *busno = *tgtno = *lunno = -1; cpt = name; if (*cpt != 'c') return 0; ret = decode_btl_number(&cpt, 't', busno); if (ret <= 0) return ret; ret = decode_btl_number(&cpt, 'd', tgtno); if (ret <= 0) return ret; cpt_mem = cpt; ret = decode_btl_number(&cpt, 's', lunno); if (ret <= 0) { cpt = cpt_mem; ret = decode_btl_number(&cpt, '/', lunno); if (ret <= 0) return ret; return(1); } cpt++; if (*cpt != '2' || *(cpt + 1) != 0) return 0; return 1; } static int start_enum_cXtYdZs2(burn_drive_enumerator_t *idx, int flag) { DIR *dir; idx->dir = NULL; dir = opendir("/dev/rdsk"); if (dir == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x0002000c, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Cannot start device file enumeration. opendir(\"/dev/rdsk\") failed.", errno, 0); return 0; } idx->dir = dir; return 1; } static int sg_solaris_convert_devname(char *path, char **dev_to_open, int flag) { char *sym_name = NULL, *media_name = NULL, *curr_name, *msg = NULL; int ret; BURN_ALLOC_MEM(msg, char, 4096); BURN_FREE_MEM(*dev_to_open); *dev_to_open = NULL; curr_name = path; if (! volmgt_running()) goto set_name; sym_name = volmgt_symname(path); sprintf(msg, "Volume Management symbolic name: '%s' -> %s", path, sym_name == NULL ? "NULL" : sym_name); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); if (sym_name != NULL) media_name = media_findname(sym_name); else media_name = media_findname(path); if (media_name != NULL) curr_name = media_name; sprintf(msg, "Media name: %s -> %s", sym_name == NULL ? path : sym_name, media_name == NULL ? "NULL" : media_name); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); set_name: BURN_ALLOC_MEM(*dev_to_open, char, strlen(curr_name) + 1); strcpy(*dev_to_open, curr_name); ret = 1; ex: if (media_name != NULL) free(media_name); if (sym_name != NULL) free(sym_name); BURN_FREE_MEM(msg); return(ret); } static int next_enum_cXtYdZs2(burn_drive_enumerator_t *idx, char adr[], int adr_size, int flag) { int busno, tgtno, lunno, ret, fd = -1, volpath_size = 160, os_errno; char *volpath = NULL, *msg = NULL, *dev_to_open = NULL; struct dirent *entry; struct dk_cinfo cinfo; DIR *dir; BURN_ALLOC_MEM(volpath, char, volpath_size); BURN_ALLOC_MEM(msg, char, 4096); dir = idx->dir; while (1) { errno = 0; entry = readdir(dir); if (entry == NULL) { if (errno) { libdax_msgs_submit(libdax_messenger, -1, 0x0002000d, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Cannot enumerate next device. readdir() from \"/dev/rdsk\" failed.", errno, 0); {ret = -1; goto ex;} } break; } if (strlen(entry->d_name) > (size_t) (volpath_size - 11)) continue; ret = decode_btl_solaris(entry->d_name, &busno, &tgtno, &lunno, 0); if (ret <= 0) continue; /* not cXtYdZs2 */ sprintf(volpath, "/dev/rdsk/%s", entry->d_name); if (burn_drive_is_banned(volpath)) continue; ret = sg_solaris_convert_devname(volpath, &dev_to_open, 0); if (ret <= 0) continue; fd = open(dev_to_open, O_RDONLY | O_NDELAY); if (fd < 0) { os_errno = errno; sprintf(msg, "Could not open '%s' , errno = %d", dev_to_open, os_errno); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0); continue; } /* See man dkio */ ret = ioctl(fd, DKIOCINFO, &cinfo); close(fd); if (ret < 0) { os_errno = errno; sprintf(msg, "ioctl(DKIOCINFO) failed on drive '%s', errno = %d", volpath, os_errno); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0); continue; } if (cinfo.dki_ctype != DKC_CDROM) { sprintf(msg, "ioctl(DKIOCINFO) classifies drive '%s' as dki_ctype %ld, not as DKC_CDROM = %ld", volpath, (long int) cinfo.dki_ctype, (long int) DKC_CDROM); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); continue; } if (adr_size <= (int) strlen(volpath)) { sprintf(msg, "Device path '%s' too long. (Max. %d)", volpath, adr_size - 1); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); {ret = -1; goto ex;} } strcpy(adr, volpath); sprintf(msg, "Accepted as valid drive '%s'", volpath); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); {ret = 1; goto ex;} } ret = 0; ex:; BURN_FREE_MEM(dev_to_open); BURN_FREE_MEM(msg); BURN_FREE_MEM(volpath); return ret; } static int end_enum_cXtYdZs2(burn_drive_enumerator_t *idx, int flag) { DIR *dir; dir = idx->dir; if(dir != NULL) closedir(dir); idx->dir = NULL; return 1; } /* ----------------------------------------------------------------------- */ /* PORTING: Private functions which contain publicly needed functionality. */ /* Their portable part must be performed. So it is probably best */ /* to replace the non-portable part and to call these functions */ /* in your port, too. */ /* ----------------------------------------------------------------------- */ /** Wraps a detected drive into libburn structures and hands it over to libburn drive list. */ static void enumerate_common(char *fname, int bus_no, int host_no, int channel_no, int target_no, int lun_no) { int ret; struct burn_drive out; /* General libburn drive setup */ burn_setup_drive(&out, fname); /* This transport adapter uses SCSI-family commands and models (seems the adapter would know better than its boss, if ever) */ ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no, target_no, lun_no, 0); if (ret <= 0) return; /* PORTING: ------------------- non portable part --------------- */ /* Transport adapter is Solaris uscsi */ /* Adapter specific handles and data */ out.fd = -1; /* PORTING: ---------------- end of non portable part ------------ */ /* Adapter specific functions with standardized names */ out.grab = sg_grab; out.release = sg_release; out.drive_is_open = sg_drive_is_open; out.issue_command = sg_issue_command; /* Finally register drive and inquire drive information */ burn_drive_finish_enum(&out); } /* ------------------------------------------------------------------------ */ /* PORTING: Public functions. These MUST be ported. */ /* ------------------------------------------------------------------------ */ /** Returns the id string of the SCSI transport adapter and eventually needed operating system facilities. This call is usable even if sg_initialize() was not called yet. In that case a preliminary constant message might be issued if detailed info is not available yet. @param msg returns id string @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_id_string(char msg[1024], int flag) { sprintf(msg, "internal Solaris uscsi adapter sg-solaris"); return 1; } /** Performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility of supporting software components. @param msg returns ids and/or error messages of eventual helpers @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_initialize(char msg[1024], int flag) { return sg_id_string(msg, 0); } /** Performs global finalization of the SCSI transport adapter and eventually needed operating system facilities. Releases globally acquired resources. @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_shutdown(int flag) { return 1; } /** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of struct burn_drive which are defined in os-*.h. The eventual initialization of those components was made underneath scsi_enumerate_drives(). This will be called when a burn_drive gets disposed. @param d the drive to be finalized @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_dispose_drive(struct burn_drive *d, int flag) { return 1; } /** Returns the next index number and the next enumerated drive address. The enumeration has to cover all available and accessible drives. It is allowed to return addresses of drives which are not available but under some (even exotic) circumstances could be available. It is on the other hand allowed, only to hand out addresses which can really be used right in the moment of this call. (This implementation chooses the latter.) @param idx An opaque handle. Make no own theories about it. @param adr Takes the reply @param adr_size Gives maximum size of reply including final 0 @param initialize 1 = start new, 0 = continue, use no other values for now -1 = finish @return 1 = reply is a valid address , 0 = no further address available -1 = severe error (e.g. adr_size too small) */ int sg_give_next_adr(burn_drive_enumerator_t *idx, char adr[], int adr_size, int initialize) { int ret; if (initialize == 1) { ret = start_enum_cXtYdZs2(idx, 0); if (ret <= 0) return ret; } else if (initialize == -1) { ret = end_enum_cXtYdZs2(idx, 0); return 0; } ret = next_enum_cXtYdZs2(idx, adr, adr_size, 0); return ret; } /** Brings all available, not-whitelist-banned, and accessible drives into libburn's list of drives. */ int scsi_enumerate_drives(void) { burn_drive_enumerator_t idx; int initialize = 1, ret, i_bus_no = -1, buf_size = 4096; int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1; char *buf = NULL; BURN_ALLOC_MEM(buf, char, buf_size); while(1) { ret = sg_give_next_adr(&idx, buf, buf_size, initialize); initialize = 0; if (ret <= 0) break; if (burn_drive_is_banned(buf)) continue; sg_obtain_scsi_adr(buf, &i_bus_no, &i_host_no, &i_channel_no, &i_target_no, &i_lun_no); enumerate_common(buf, i_bus_no, i_host_no, i_channel_no, i_target_no, i_lun_no); } sg_give_next_adr(&idx, buf, buf_size, -1); ret = 1; ex:; BURN_FREE_MEM(buf); return ret; } /** Tells whether libburn has the given drive in use or exclusively reserved. If it is "open" then libburn will eventually call sg_release() on it when it is time to give up usage and reservation. */ /** Published as burn_drive.drive_is_open() */ int sg_drive_is_open(struct burn_drive * d) { return (d->fd != -1); } /** Opens the drive for SCSI commands and - if burn activities are prone to external interference on your system - obtains an exclusive access lock on the drive. (Note: this is not physical tray locking.) A drive that has been opened with sg_grab() will eventually be handed over to sg_release() for closing and unreserving. */ int sg_grab(struct burn_drive *d) { char *msg = NULL, *dev_to_open = NULL; int os_errno, ret; struct dk_cinfo cinfo; BURN_ALLOC_MEM(msg, char, 4096); if (d->fd != -1) { d->released = 0; {ret = 1; goto ex;} } ret = sg_solaris_convert_devname(d->devname, &dev_to_open, 0); if (ret <= 0) goto ex; d->fd = open(dev_to_open, O_RDONLY | O_NDELAY); if (d->fd == -1) { os_errno = errno; sprintf(msg, "Could not grab drive '%s'", d->devname); if (strcmp(d->devname, dev_to_open)) sprintf(msg + strlen(msg), " via '%s'", dev_to_open); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0); {ret = 0; goto ex;} } ret = ioctl(d->fd, DKIOCINFO, &cinfo); if (ret < 0) { os_errno = errno; sprintf(msg, "ioctl(DKIOCINFO) failed on drive '%s'", d->devname); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0); goto revoke; } if (cinfo.dki_ctype != DKC_CDROM) { sprintf(msg, "ioctl(DKIOCINFO) classifies drive '%s' as dki_ctype %ld, not as DKC_CDROM = %ld", d->devname, (long int) cinfo.dki_ctype, (long int) DKC_CDROM); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); goto revoke; } /* >>> obtain eventual locks */; d->released = 0; {ret = 1; goto ex;} revoke:; sprintf(msg, "Could not grab drive '%s'. Not a CDROM device.", d->devname); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); ret = 0; ex:; BURN_FREE_MEM(dev_to_open); BURN_FREE_MEM(msg); return ret; } /** PORTING: Is mainly about the call to sg_close_drive() and whether it implements the demanded functionality. */ /** Gives up the drive for SCSI commands and releases eventual access locks. (Note: this is not physical tray locking.) */ int sg_release(struct burn_drive *d) { if (d->fd < 0) return 0; sg_close_drive(d); return 0; } /** Sends a SCSI command to the drive, receives reply and evaluates whether the command succeeded or shall be retried or finally failed. Returned SCSI errors shall not lead to a return value indicating failure. The callers get notified by c->error. An SCSI failure which leads not to a retry shall be notified via scsi_notify_error(). The Libburn_log_sg_commandS facility might be of help when problems with a drive have to be examined. It shall stay disabled for normal use. @return: 1 success , <=0 failure */ int sg_issue_command(struct burn_drive *d, struct command *c) { int i, timeout_ms, ret, key, asc, ascq, done = 0, sense_len; time_t start_time; struct uscsi_cmd cgc; char msg[80]; static FILE *fp = NULL; c->error = 0; memset(c->sense, 0, sizeof(c->sense)); if (d->fd == -1) return 0; if (burn_sg_log_scsi & 1) { if (fp == NULL) { fp= fopen("/tmp/libburn_sg_command_log", "a"); fprintf(fp, "\n-----------------------------------------\n"); } } if (burn_sg_log_scsi & 3) scsi_log_cmd(c,fp,0); if (c->timeout > 0) timeout_ms = c->timeout; else timeout_ms = 200000; memset (&cgc, 0, sizeof (struct uscsi_cmd)); /* No error messages, no retries, do not execute with other commands, request sense data */ cgc.uscsi_flags = USCSI_SILENT | USCSI_DIAGNOSE | USCSI_ISOLATE | USCSI_RQENABLE; cgc.uscsi_timeout = timeout_ms / 1000; cgc.uscsi_cdb = (caddr_t) c->opcode; cgc.uscsi_bufaddr = (caddr_t) c->page->data; if (c->dir == TO_DRIVE) { cgc.uscsi_flags |= USCSI_WRITE; cgc.uscsi_buflen = c->page->bytes; } else if (c->dir == FROM_DRIVE) { cgc.uscsi_flags |= USCSI_READ; if (c->dxfer_len >= 0) cgc.uscsi_buflen = c->dxfer_len; else cgc.uscsi_buflen = BUFFER_SIZE; /* touch page so we can use valgrind */ memset(c->page->data, 0, BUFFER_SIZE); } else { cgc.uscsi_buflen = 0; } cgc.uscsi_cdblen = c->oplen; cgc.uscsi_rqlen = sizeof(c->sense); cgc.uscsi_rqbuf = (caddr_t) c->sense; /* ts B90523 : Record effective transfer length request for debugging*/ c->dxfer_len = cgc.uscsi_buflen; /* retry-loop */ start_time = time(NULL); for(i = 0; !done; i++) { memset(c->sense, 0, sizeof(c->sense)); c->start_time = burn_get_time(0); ret = ioctl(d->fd, USCSICMD, &cgc); c->end_time = burn_get_time(0); /* For cgc.uscsi_status see SAM-3 5.3.1, Table 22 0 = GOOD , 2 = CHECK CONDITION : Sense Data are delivered 8 = BUSY */ if (ret != 0 && cgc.uscsi_status != 2) { sprintf(msg, "Failed to transfer command to drive. (uscsi_status = 0x%X)", (unsigned int) cgc.uscsi_status), libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); sg_close_drive(d); d->released = 1; d->busy = BURN_DRIVE_IDLE; c->error = 1; return -1; } /* >>> Should replace "18" by realistic sense length. What's about following older remark ? */ /* >>> valid sense: cgc.uscsi_rqlen - cgc.uscsi_rqresid */; spc_decode_sense(c->sense, 0, &key, &asc, &ascq); if (key || asc || ascq) sense_len = 18; else sense_len = 0; done = scsi_eval_cmd_outcome(d, c, fp, c->sense, sense_len, start_time, timeout_ms, i, 0); if (d->cancel) done = 1; if (!done) spc_register_retry(c); } /* end of retry-loop */ return 1; } /** Tries to obtain SCSI address parameters. @return 1 is success , 0 is failure */ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no) { int ret; /* Try to guess from path */ if (strncmp("/dev/rdsk/", path, 10) == 0) { ret = decode_btl_solaris(path + 10, bus_no, target_no, lun_no, 0); if (ret > 0) { *host_no = *bus_no; *channel_no = 0; return 1; } } *bus_no = *host_no = *channel_no = *target_no = *lun_no = -1; /* >>> Could need a ioctl which gives SCSI numbers */; return (0); } /** Tells whether a text is a persistent address as listed by the enumeration functions. */ #ifndef NIX int sg_is_enumerable_adr(char* path) { int ret; int bus_no, target_no, lun_no; struct stat stbuf; if (strncmp("/dev/rdsk/", path, 10) != 0) return 0; ret = decode_btl_solaris(path + 10, &bus_no, &target_no, &lun_no, 0); if (ret <= 0) return 0; if (stat(path, &stbuf) == -1) return 0; return 1; } #else /* ! NIX */ int sg_is_enumerable_adr(char* adr) { burn_drive_enumerator_t idx; int initialize = 1, ret; char buf[64]; while(1) { ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); initialize = 0; if (ret <= 0) break; if (strcmp(adr, buf) == 0) { sg_give_next_adr(&idx, buf, sizeof(buf), -1); return 1; } } sg_give_next_adr(&idx, buf, sizeof(buf), -1); return (0); } #endif /* NIX */ /* Return 1 if the given path leads to a regular file or a device that can be fseeked, read, and possibly written with 2 kB granularity. */ int burn_os_is_2k_seekrw(char *path, int flag) { struct stat stbuf; if (stat(path, &stbuf) == -1) return 0; if (S_ISREG(stbuf.st_mode)) return 1; if (S_ISBLK(stbuf.st_mode)) return 1; return 0; } /** Estimate the potential payload capacity of a file address. @param path The address of the file to be examined. If it does not exist yet, then the directory will be inquired. @param bytes The pointed value gets modified, but only if an estimation is possible. @return -2 = cannot perform necessary operations on file object -1 = neither path nor dirname of path exist 0 = could not estimate size capacity of file object 1 = estimation has been made, bytes was set */ int burn_os_stdio_capacity(char *path, off_t write_start, off_t *bytes) { struct stat stbuf; int ret; #ifdef Libburn_os_has_statvfS struct statvfs vfsbuf; #endif char *testpath = NULL, *cpt; off_t add_size = 0; BURN_ALLOC_MEM(testpath, char, 4096); testpath[0] = 0; if (stat(path, &stbuf) == -1) { strcpy(testpath, path); cpt = strrchr(testpath, '/'); if(cpt == NULL) strcpy(testpath, "."); else if(cpt == testpath) testpath[1] = 0; else *cpt = 0; if (stat(testpath, &stbuf) == -1) {ret = -1; goto ex;} } else if(S_ISBLK(stbuf.st_mode)) { int open_mode = O_RDONLY, fd; fd = open(path, open_mode); if (fd == -1) {ret = -2; goto ex;} *bytes = lseek(fd, 0, SEEK_END); close(fd); if (*bytes == -1) { *bytes = 0; {ret = 0; goto ex;} } } else if(S_ISREG(stbuf.st_mode)) { add_size = burn_sparse_file_addsize(write_start, &stbuf); strcpy(testpath, path); } else {ret = 0; goto ex;} if (testpath[0]) { #ifdef Libburn_os_has_statvfS if (statvfs(testpath, &vfsbuf) == -1) {ret = -2; goto ex;} *bytes = add_size + ((off_t) vfsbuf.f_frsize) * (off_t) vfsbuf.f_bavail; #else /* Libburn_os_has_statvfS */ {ret = 0; goto ex;} #endif /* ! Libburn_os_has_stavtfS */ } ret = 1; ex:; BURN_FREE_MEM(testpath); return ret; } /* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ #ifdef Libburn_read_o_direcT /* No special O_DIRECT-like precautions are implemented here */ #endif /* Libburn_read_o_direcT */ int burn_os_open_track_src(char *path, int open_flags, int flag) { int fd; fd = open(path, open_flags); return fd; } void *burn_os_alloc_buffer(size_t amount, int flag) { void *buf = NULL; buf = calloc(1, amount); return buf; } int burn_os_free_buffer(void *buffer, size_t amount, int flag) { if (buffer == NULL) return 0; free(buffer); return 1; } /* sg.c Switcher for operating system dependent transport level modules of libburn. Copyright (C) 2009 - 2016 Thomas Schmitt <scdbackup@gmx.net>, provided under GPLv2+ */ #ifdef HAVE_CONFIG_H #include "../config.h" #undef HAVE_CONFIG_H #endif #ifdef Libburn_use_sg_dummY #include "sg-dummy.c" #else #ifdef Libburn_use_libcdiO #include "sg-libcdio.c" #else #ifdef __NetBSD__ #include "sg-netbsd.c" #else #ifdef __OpenBSD__ #include "sg-netbsd.c" #else #ifdef __FreeBSD__ #ifdef Libburn_use_sg_freebsd_porT #include "sg-freebsd-port.c" #else #include "sg-freebsd.c" #endif #else #ifdef __FreeBSD_kernel__ #ifdef Libburn_use_sg_freebsd_porT #include "sg-freebsd-port.c" #else #include "sg-freebsd.c" #endif #else #ifdef __linux #include "sg-linux.c" #else #ifdef __sun #include "sg-solaris.c" #else /* The dummy adapter formally fulfills the expectations of libburn towards its SCSI command transport. It will show no drives and perform no SCSI commands. libburn will then be restricted to using its stdio pseudo drives. */ static int intentional_compiler_warning(void) { int INTENTIONAL_COMPILER_WARNING_; int Cannot_recognize_supported_operating_system_; int Like_GNU_Linux_or_FreeBSD_or_Solaris_or_NetBSD_; int Have_to_use_dummy_MMC_transport_adapter_; int This_libburn_will_not_be_able_to_operate_on_real_CD_drives; int Have_to_use_dummy_MMC_transport_adapter; int Like_GNU_Linux_or_FreeBSD_or_Solaris_or_NetBSD; int Cannot_recognize_supported_operating_system; int INTENTIONAL_COMPILER_WARNING; return(0); } #include "sg-dummy.c" #endif /* ! __sun */ #endif /* ! __linux */ #endif /* ! __FreeBSD_kernel__ */ #endif /* ! __FreeBSD__ */ #endif /* ! __OpenBSD__ */ #endif /* ! __NetBSD__ */ #endif /* ! Libburn_use_libcdiO */ #endif /* ! Libburn_use_sg_dummY */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (C) 2009 Thomas Schmitt <scdbackup@gmx.net>, provided under GPLv2+ */ #ifndef __SG #define __SG #include "os.h" /* see os.h for name of particular os-*.h where this is defined */ BURN_OS_DEFINE_DRIVE_ENUMERATOR_T struct burn_drive; struct command; /* ts A60922 ticket 33 */ int sg_give_next_adr(burn_drive_enumerator_t *enm_context, char adr[], int adr_size, int initialize); int sg_is_enumerable_adr(char *adr); int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no); int sg_grab(struct burn_drive *); int sg_release(struct burn_drive *); int sg_issue_command(struct burn_drive *, struct command *); /* ts A61115 : formerly sg_enumerate();ata_enumerate() */ int scsi_enumerate_drives(void); int sg_drive_is_open(struct burn_drive * d); int burn_os_is_2k_seekrw(char *path, int flag); int burn_os_stdio_capacity(char *path, off_t write_start, off_t *bytes); /* ts A91227 */ /** Returns the id string of the SCSI transport adapter and eventually needed operating system facilities. This call is usable even if sg_initialize() was not called yet. In that case a preliminary constant message might be issued if detailed info is not available yet. @param msg returns id string @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_id_string(char msg[1024], int flag); /* ts A91225 */ /** Performs global initialization of the SCSI transport adapter and eventually needed operating system facilities. Checks for compatibility supporting software components. @param msg returns ids and/or error messages of eventual helpers @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_initialize(char msg[1024], int flag); /* ts A91227 */ /** Performs global finalization of the SCSI transport adapter and eventually needed operating system facilities. Releases globally acquired resources. @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_shutdown(int flag); /* ts A91227 */ /** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of struct burn_drive which are defined in os-*.h. The eventual initialization of those components was made underneath scsi_enumerate_drives(). This will be called when a burn_drive gets disposed. @param d the drive to be finalized @param flag unused yet, submit 0 @return 1 = success, <=0 = failure */ int sg_dispose_drive(struct burn_drive *d, int flag); #endif /* __SG */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2010 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <stdlib.h> #include <string.h> #include "libburn.h" #include "source.h" #include "structure.h" #include "init.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) { s->refcount++; t->source = s; /* ts A61031 */ t->open_ended = (s->get_size(s) <= 0); return BURN_SOURCE_OK; } struct burn_source *burn_source_new(void) { struct burn_source *out; /* ts A70825 , B11219 */ out = burn_alloc_mem(sizeof(struct burn_source), 1, 0); if (out == NULL) return NULL; out->refcount = 1; return out; } /* ts A71223 */ int burn_source_cancel(struct burn_source *src) { if(src->read == NULL) if(src->version > 0) if(src->cancel != NULL) src->cancel(src); return 1; } /* ts B00922 */ int burn_source_read(struct burn_source *src, unsigned char *buffer, int size) { int ret; if (src->read != NULL) ret = src->read(src, buffer, size); else ret = src->read_xt(src, buffer, size); return ret; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ #ifndef __SOURCE #define __SOURCE struct burn_source *burn_source_new(void); int burn_source_cancel(struct burn_source *src); int burn_source_read(struct burn_source *src, unsigned char *buffer, int size); #endif /*__SOURCE*/ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2019 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ /* scsi primary commands */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <string.h> /* ts A61008 */ /* #include <a ssert.h> */ #include <stdlib.h> #include "libburn.h" #include "transport.h" #include "spc.h" #include "mmc.h" #include "sbc.h" #include "drive.h" #include "debug.h" #include "options.h" #include "init.h" #include "util.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /* ts A91111 : whether to log SCSI commands: bit0= log in /tmp/libburn_sg_command_log bit1= log to stderr bit2= flush every line */ extern int burn_sg_log_scsi; /* spc command set */ /* ts A70519 : allocation length byte 3+4 was 0,255 */ static unsigned char SPC_INQUIRY[] = { 0x12, 0, 0, 0, 36, 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 }; 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 }; #ifdef Libburn_enable_scsi_cmd_ABh static unsigned char SPC_READ_MEDIA_SERIAL_NUMBER[] = { 0xAB, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; #endif /* ts A70519 : An initializer for the abstract SCSI command structure */ int scsi_init_command(struct command *c, unsigned char *opcode, int oplen) { if (oplen > 16) return 0; memset(c, 0, sizeof(struct command)); memcpy(c->opcode, opcode, oplen); c->oplen = oplen; c->dir = NO_TRANSFER; c->dxfer_len = -1; memset(c->sense, 0, sizeof(c->sense)); c->sense_len = 0; c->error = 0; c->retry = 0; c->page = NULL; c->timeout = Libburn_scsi_default_timeouT; c->start_time = c->end_time = 0.0; c->retry_count = 0; c->last_retry_key = 0; c->last_retry_asc = 0; c->last_retry_ascq = 0; return 1; } /* ts B00728 */ int spc_decode_sense(unsigned char *sense, int senselen, int *key, int *asc, int *ascq) { *key = *asc = *ascq = 0; if ((sense[0] & 0x7f) == 0x72 || (sense[0] & 0x7f) == 0x73) { if (senselen <= 0 || senselen > 1) *key = sense[1] & 0x0f; if (senselen <= 0 || senselen > 2) *asc = sense[2]; if (senselen <= 0 || senselen > 3) *ascq = sense[3]; return 1; } if (senselen <= 0 || senselen > 2) *key = sense[2] & 0x0f; if (senselen <= 0 || senselen > 12) *asc = sense[12]; if (senselen <= 0 || senselen > 13) *ascq = sense[13]; return 1; } int spc_test_unit_ready_r(struct burn_drive *d, int *key, int *asc, int *ascq, int *progress) { struct command *c; c = &(d->casual_command); if (mmc_function_spy(d, "test_unit_ready") <= 0) return 0; scsi_init_command(c, SPC_TEST_UNIT_READY,sizeof(SPC_TEST_UNIT_READY)); c->retry = 0; c->dir = NO_TRANSFER; d->issue_command(d, c); *key = *asc = *ascq = 0; *progress = -1; if (c->error) { spc_decode_sense(c->sense, 0, key, asc, ascq); if (c->sense[0] == 0x70 && ((c->sense[2] & 0x0f) == 0 || (c->sense[2] & 0x0f) == 2) && (c->sense[15] & 0x80)) *progress = (c->sense[16] << 8) + c->sense[17]; return (*key == 0); } return 1; } int spc_test_unit_ready(struct burn_drive *d) { int key, asc, ascq, progress; return spc_test_unit_ready_r(d, &key, &asc, &ascq, &progress); } /* ts A70315 */ /** @param flag bit0=do not wait 0.1 seconds before first test unit ready bit1=do not issue success message */ /** Wait until the drive state becomes clear or until max_usec elapsed */ int spc_wait_unit_attention(struct burn_drive *d, int max_sec, char *cmd_text, int flag) { int i, ret = 1, key = 0, asc = 0, ascq = 0, clueless_start = 0; static double tests_per_second = 2.0; int sleep_usecs, loop_limit, clueless_timeout, progress; char *msg = NULL, *cmd_name = NULL, *cmd_cpt; unsigned char sense[14]; BURN_ALLOC_MEM(msg, char, 320); BURN_ALLOC_MEM(cmd_name, char, 320); clueless_timeout = 5 * tests_per_second + 1; loop_limit = max_sec * tests_per_second + 1; sleep_usecs = 1000000 / tests_per_second; strcpy(cmd_name, cmd_text); cmd_cpt = strchr(cmd_name, ':'); if (cmd_cpt != NULL) *cmd_cpt = 0; if (!(flag & 1)) usleep(sleep_usecs); for(i = !(flag & 1); i < loop_limit; i++) { ret = spc_test_unit_ready_r(d, &key, &asc, &ascq, &progress); if (ret > 0) /* ready */ break; if (key!=0x2 || asc!=0x4) { if (key == 0x2 && asc == 0x3A) { ret = 1; /* medium not present = ok */ /* <<< ts A70912 : My LG GSA-4082B on asynchronous load: first it reports no media 2,3A,00, then it reports not ready 2,04,00, further media inquiry retrieves wrong data if(i<=100) goto slumber; */ break; } if (key == 0x6 && asc == 0x28) /* medium change notice or alike = try again */ goto slumber; handle_error:; /* ts A90213 */ sprintf(msg, "Asynchronous SCSI error on %s: ", cmd_name); sense[0] = 0x70; /* Fixed format sense data */ sense[2] = key; sense[12] = asc; sense[13] = ascq; scsi_error_msg(d, sense, 14, msg + strlen(msg), &key, &asc, &ascq); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002014d, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); if (cmd_cpt != NULL) { sprintf(msg, "Attempted SCSI CDB: %s", cmd_cpt + 1); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002014d, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } d->cancel = 1; break; } else if (ascq == 0x00) { /* CAUSE NOT REPORTABLE */ /* Might be a clueless system adapter */ if (clueless_start == 0) clueless_start = i; if (i - clueless_start > clueless_timeout) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "Ended clueless NOT READY cycle", 0, 0); if (cmd_cpt != NULL) { sprintf(msg, "Attempted SCSI CDB: %s", cmd_cpt + 1); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } ret = 1; /* medium not present = ok */ break; } } else if (ascq == 0x02 || ascq == 0x03) goto handle_error; slumber:; usleep(sleep_usecs); } if (ret <= 0 || !(flag & 2)) { sprintf(msg, "Async %s %s after %d.%d seconds", cmd_name, (ret > 0 ? "succeeded" : "failed"), i / 10, i % 10); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020150, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_LOW, msg, 0, 0); } if (i < max_sec * 10) {ret = (ret > 0); goto ex;} sprintf(msg, "Timeout (%d s) with asynchronous SCSI command %s\n", max_sec, cmd_name); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002014f, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); if (cmd_cpt != NULL) { sprintf(msg, "Attempted SCSI CDB: %s", cmd_cpt + 1); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002014f, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } ret = 0; ex:; BURN_FREE_MEM(msg); BURN_FREE_MEM(cmd_name); return ret; } void spc_request_sense(struct burn_drive *d, struct buffer *buf) { struct command *c; c = &(d->casual_command); if (mmc_function_spy(d, "request_sense") <= 0) return; scsi_init_command(c, SPC_REQUEST_SENSE, sizeof(SPC_REQUEST_SENSE)); c->retry = 0; c->dxfer_len= c->opcode[4]; c->retry = 0; c->page = buf; c->page->sectors = 0; c->page->bytes = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); } static int spc_report_async_error(struct burn_drive *d, int key, int asc, int ascq, int flag) { char *msg = NULL; unsigned char sense[14]; int ret; BURN_ALLOC_MEM(msg, char, BURN_DRIVE_ADR_LEN + 160); sprintf(msg, "Asynchronous SCSI error : "); sense[0] = 0x70; /* Fixed format sense data */ sense[2] = key; sense[12] = asc; sense[13] = ascq; scsi_error_msg(d, sense, 14, msg + strlen(msg), &key, &asc, &ascq); libdax_msgs_submit(libdax_messenger, d->global_index, 0x000201a5, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } /* @return -3 = other error reported -2 = drive is ready , -1 = not ready, but no progress reported , >= 0 progress indication between 0 and 65535 */ int spc_get_erase_progress(struct burn_drive *d) { struct buffer *b = NULL; int ret, key, asc, ascq, progress; if (mmc_function_spy(d, "get_erase_progress") <= 0) {ret = 0; goto ex;} /* ts B20104 : TEST UNIT READY seems to be more reliable than REQUEST SENSE. Nevertheless growisofs still uses the latter as fallback. */ ret = spc_test_unit_ready_r(d, &key, &asc, &ascq, &progress); if (ret > 0) {ret = -2; goto ex;} /* Check key, asc, ascq for errors other than "not yet ready" */ if (key != 0 && (key != 0x2 || asc != 0x04 || ascq == 0x02 || ascq ==0x03)) { spc_report_async_error(d, key, asc, ascq, 0); ret= -3; goto ex; } if (progress >= 0) {ret = progress; goto ex;} /* Fallback to request sense */ BURN_ALLOC_MEM(b, struct buffer, 1); spc_request_sense(d, b); /* Checking the preconditions as of SPC-3 4.5.2.4.4 and 4.5.3 */ ret = -1; if (b->data[0] == 0x70 && ((b->data[2] & 0x0f) == 0 || (b->data[2] & 0x0f) == 2) && (b->data[15] & 0x80)) ret = (b->data[16] << 8) | b->data[17]; ex:; BURN_FREE_MEM(b); return ret; } void spc_inquiry(struct burn_drive *d) { struct buffer *buf = NULL; struct burn_scsi_inquiry_data *id; struct command *c = NULL; if (mmc_function_spy(d, "inquiry") <= 0) return; BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); BURN_ALLOC_MEM_VOID(c, struct command, 1); scsi_init_command(c, SPC_INQUIRY, sizeof(SPC_INQUIRY)); c->dxfer_len = (c->opcode[3] << 8) | c->opcode[4]; c->retry = 1; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); id = (struct burn_scsi_inquiry_data *)d->idata; id->peripheral = 0x7f; /* SPC-3: incabable undefined peripheral type */ id->version = 0; /* SPC-3: no claim for conformance */ memset(id->vendor, 0, 9); memset(id->product, 0, 17); memset(id->revision, 0, 5); if (c->error) { id->valid = -1; goto ex; } id->peripheral = ((char *) c->page->data)[0]; id->version = ((char *) c->page->data)[2]; 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; ex:; BURN_FREE_MEM(buf); BURN_FREE_MEM(c); return; } void spc_prevent(struct burn_drive *d) { struct command *c; c = &(d->casual_command); if (mmc_function_spy(d, "prevent") <= 0) return; scsi_init_command(c, SPC_PREVENT, sizeof(SPC_PREVENT)); c->retry = 1; c->dir = NO_TRANSFER; d->issue_command(d, c); #ifdef Libburn_pioneer_dvr_216d_get_evenT mmc_get_event(d); #endif } void spc_allow(struct burn_drive *d) { struct command *c; c = &(d->casual_command); if (mmc_function_spy(d, "allow") <= 0) return; scsi_init_command(c, SPC_ALLOW, sizeof(SPC_ALLOW)); c->retry = 1; c->dir = NO_TRANSFER; d->issue_command(d, c); } /* ts B40216 : Outsourced from spc_sense_caps_al(). To be called by spc_sense_caps() after spc_sense_caps_al() */ static int spc_try_get_performance(struct burn_drive *d, int flag) { int ret; struct burn_feature_descr *feature_descr; /* ts B40107 : Feature 0x107 announces availability of GET PERFORMANCE Its WSPD bit announces Type 3. Try this even if the feature is not current. */ ret = burn_drive_has_feature(d, 0x107, &feature_descr, 0); if (ret <= 0) return ret; if (feature_descr->data_lenght <= 0) return 1; if (feature_descr->data[0] & 2) /* WSPD */ ret = mmc_get_write_performance(d); /* Get read performance */ mmc_get_performance(d, 0x00, 0); return 1; } /* ts A70518 - A90603 : Do not call with *alloc_len < 10 */ /** @param flag bit0= do only inquire alloc_len @return 1=ok , <=0 error , 2=Block Descriptor Length > 0, retry with flag bit1 */ static int spc_sense_caps_al(struct burn_drive *d, int *alloc_len, int flag) { struct buffer *buf = NULL; struct scsi_mode_data *m; int page_length, num_write_speeds = 0, i, speed, ret; int old_alloc_len, was_error = 0, block_descr_len; unsigned char *page; struct command *c = NULL; struct burn_speed_descriptor *sd; char *msg = NULL; /* ts A61225 : 1 = report about post-MMC-1 speed descriptors */ static int speed_debug = 0; if (*alloc_len < 10) {ret = 0; goto ex;} BURN_ALLOC_MEM(msg, char, BURN_DRIVE_ADR_LEN + 160); BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); /* ts A90602 : Clearing mdata before command execution */ m = d->mdata; m->p2a_valid = 0; burn_mdata_free_subs(m); memset(buf, 0, sizeof(struct buffer)); scsi_init_command(c, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); c->dxfer_len = *alloc_len; c->opcode[7] = (c->dxfer_len >> 8) & 0xff; c->opcode[8] = c->dxfer_len & 0xff; c->retry = 1; c->opcode[2] = 0x2A; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); if (c->error) { memset(buf, 0, sizeof(struct buffer)); m->p2a_valid = -1; was_error = 1; } /* ts B11103 : qemu SCSI CD-ROM has Block Descriptor Length > 0. The descriptors come between header and page. */ block_descr_len = c->page->data[6] * 256 + c->page->data[7]; if (block_descr_len + 8 + 2 > *alloc_len) { if (block_descr_len + 8 + 2 > BUFFER_SIZE || !(flag & 1)) { m->p2a_valid = -1; sprintf(msg, "MODE SENSE page 2A with oversized Block Descriptors: %s : %d", d->devname, block_descr_len); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002016e, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_LOW, msg, 0, 0); {ret = 0; goto ex;} } *alloc_len = block_descr_len + 10; {ret = 2; goto ex;} } /* Skip over Mode Data Header and block descriptors */ page = c->page->data + 8 + block_descr_len; /* ts A61225 : Although MODE SENSE indeed belongs to SPC, the returned code page 2Ah is part of MMC-1 to MMC-3. In MMC-1 5.2.3.4. it has 22 bytes, in MMC-3 6.3.11 there are at least 28 bytes plus a variable length set of speed descriptors. In MMC-5 E.11 it is declared "legacy". ts B11031 : qemu emulates an ATAPI DVD-ROM, which delivers only a page length of 18. This is now tolerated. */ /* ts A90603 : SPC-1 8.3.3 enumerates mode page format bytes from 0 to n and defines Page Length as (n-1). */ page_length = page[1]; old_alloc_len = *alloc_len; *alloc_len = page_length + 10 + block_descr_len; if (flag & 1) {ret = !was_error; goto ex;} if (page_length + 10 > old_alloc_len) page_length = old_alloc_len - 10; /* ts A90602 : page_length N asserts page[N+1]. (see SPC-1 8.3.3) */ /* ts B11031 : qemu drive has a page_length of 18 */ if (page_length < 18) { m->p2a_valid = -1; sprintf(msg, "MODE SENSE page 2A too short: %s : %d", d->devname, page_length); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002016e, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_LOW, msg, 0, 0); {ret = 0; goto ex;} } 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->c2_pointers = page[5] & 16; m->underrun_proof = page[4] & 128; /* ts A61021 : these fields are marked obsolete in MMC 3 */ m->max_read_speed = page[8] * 256 + page[9]; m->cur_read_speed = page[14] * 256 + page[15]; m->max_write_speed = m->cur_write_speed = 0; if (page_length >= 18) /* note: page length is page size - 2 */ m->max_write_speed = page[18] * 256 + page[19]; if (page_length >= 20) m->cur_write_speed = page[20] * 256 + page[21]; /* ts A61021 : New field to be set by atip (or following MMC-3 info) */ m->min_write_speed = m->max_write_speed; /* ts A61225 : for ACh GET PERFORMANCE, Type 03h */ m->min_end_lba = 0x7fffffff; m->max_end_lba = 0; if (!was_error) m->p2a_valid = 1; /* ts A61225 : end of MMC-1 , begin of MMC-3 */ if (page_length < 30) /* no write speed descriptors ? */ goto no_speed_descriptors; m->cur_write_speed = page[28] * 256 + page[29]; if (speed_debug) fprintf(stderr, "LIBBURN_DEBUG: cur_write_speed = %d\n", m->cur_write_speed); num_write_speeds = page[30] * 256 + page[31]; m->max_write_speed = m->min_write_speed = m->cur_write_speed; if (32 + 4 * num_write_speeds > page_length + 2) { sprintf(msg, "Malformed capabilities page 2Ah received (len=%d, #speeds=%d)", page_length, num_write_speeds); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002013c, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); {ret = 0; goto ex;} } for (i = 0; i < num_write_speeds; i++) { speed = page[32 + 4 * i + 2] * 256 + page[32 + 4 * i + 3]; if (speed_debug) fprintf(stderr, "LIBBURN_DEBUG: write speed #%d = %d kB/s (rc %d)\n", i, speed, page[32 + 4 * i + 1] & 7); /* ts A61226 */ ret = burn_speed_descriptor_new(&(d->mdata->speed_descriptors), NULL, d->mdata->speed_descriptors, 0); if (ret > 0) { sd = d->mdata->speed_descriptors; sd->source = 1; if (d->current_profile > 0) { sd->profile_loaded = d->current_profile; strcpy(sd->profile_name, d->current_profile_text); } sd->wrc = (( page[32 + 4 * i + 1] & 7 ) == 1 ); sd->write_speed = speed; } if (speed > m->max_write_speed) m->max_write_speed = speed; if (speed < m->min_write_speed) m->min_write_speed = speed; } if (speed_debug) fprintf(stderr, "LIBBURN_DEBUG: 5Ah,2Ah min_write_speed = %d , max_write_speed = %d\n", m->min_write_speed, m->max_write_speed); no_speed_descriptors:; ret = !was_error; ex: BURN_FREE_MEM(msg); BURN_FREE_MEM(buf); BURN_FREE_MEM(c); return ret; } void spc_sense_caps(struct burn_drive *d) { int alloc_len, start_len = 30, minimum_len = 28, ret; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "sense_caps") <= 0) return; mmc_get_configuration(d); /* first command execution to learn Allocation Length */ alloc_len = start_len; ret = spc_sense_caps_al(d, &alloc_len, 1); if (ret == 2) { /* ts B40205: Unexpectedly found Block Descriptors. Repeat with new alloc_len. */ ret = spc_sense_caps_al(d, &alloc_len, 1); if (ret == 2) goto try_get_performance; } /* ts B11103: qemu ATAPI DVD-ROM delivers only 28. SanDisk Cruzer U3 memory stick throws error on alloc_len < 30. MMC-1 prescribes that 30 are available. qemu tolerates 30. */ if (alloc_len >= minimum_len && ret > 0) /* second execution with announced length */ spc_sense_caps_al(d, &alloc_len, 0); try_get_performance:; spc_try_get_performance(d, 0); } void spc_sense_error_params(struct burn_drive *d) { struct buffer *buf = NULL; struct scsi_mode_data *m; int alloc_len = 12 ; unsigned char *page; struct command *c = NULL; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "sense_error_params") <= 0) goto ex; BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); BURN_ALLOC_MEM_VOID(c, struct command, 1); scsi_init_command(c, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); c->dxfer_len = alloc_len; c->opcode[7] = (c->dxfer_len >> 8) & 0xff; c->opcode[8] = c->dxfer_len & 0xff; c->retry = 1; c->opcode[2] = 0x01; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); m = d->mdata; page = c->page->data + 8; d->params.retries = page[3]; m->retry_page_length = page[1]; m->retry_page_valid = 1; ex:; BURN_FREE_MEM(buf); BURN_FREE_MEM(c); } void spc_select_error_params(struct burn_drive *d, const struct burn_read_opts *o) { struct buffer *buf = NULL; struct command *c = NULL; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "select_error_params") <= 0) goto ex; BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); BURN_ALLOC_MEM_VOID(c, struct command, 1); scsi_init_command(c, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); c->retry = 1; if (d->mdata->retry_page_valid <= 0) d->mdata->retry_page_length = 0; c->opcode[8] = 8 + 2 + d->mdata->retry_page_length; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; 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; c->page->data[11] = d->params.retries; c->dir = TO_DRIVE; d->issue_command(d, c); ex:; BURN_FREE_MEM(buf); BURN_FREE_MEM(c); } void spc_sense_write_params(struct burn_drive *d) { struct buffer *buf = NULL; struct scsi_mode_data *m; int dummy1, dummy2, alloc_len = 10; unsigned char *page; struct command *c = NULL; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "sense_write_params") <= 0) goto ex; BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); BURN_ALLOC_MEM_VOID(c, struct command, 1); /* ts A61007 : Done in soft at only caller burn_drive_grab() */ /* a ssert(d->mdata->cdr_write || d->mdata->cdrw_write || d->mdata->dvdr_write || d->mdata->dvdram_write); */ scsi_init_command(c, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); c->dxfer_len = alloc_len; c->opcode[7] = (c->dxfer_len >> 8) & 0xff; c->opcode[8] = c->dxfer_len & 0xff; c->retry = 1; c->opcode[2] = 0x05; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); /* ts A71128 : do not interpret reply if error */ m = d->mdata; if (!c->error) { page = c->page->data + 8; m->write_page_length = page[1]; if (m->write_page_length > 0) m->write_page_valid = 1; else m->write_page_length = 0x32; } mmc_read_disc_info(d); /* ts A70212 : try to setup d->media_capacity_remaining */ if (d->current_profile == 0x1a || d->current_profile == 0x13 || d->current_profile == 0x12 || d->current_profile == 0x43) d->read_format_capacities(d, -1); else if (d->status == BURN_DISC_BLANK || (d->current_is_cd_profile && d->status == BURN_DISC_APPENDABLE)) { burn_drive_send_default_page_05(d, 0); d->get_nwa(d, -1, &dummy1, &dummy2); } /* others are hopefully up to date from mmc_read_disc_info() */ /* fprintf(stderr, "LIBBURN_DEBUG: media_capacity_remaining = %.f\n", (double) d->media_capacity_remaining); */ ex:; BURN_FREE_MEM(buf); BURN_FREE_MEM(c); } /* remark ts A61104 : Although command MODE SELECT is SPC, the content of the Write Parameters Mode Page (05h) is MMC (Table 108 in MMC-1). Thus the filling of the mode page is done by mmc_compose_mode_page_5(). */ void spc_select_write_params(struct burn_drive *d, struct burn_session *s, int tnum, const struct burn_write_opts *o) { struct buffer *buf = NULL; struct command *c = NULL; int alloc_len; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "select_write_params") <= 0) goto ex; BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); BURN_ALLOC_MEM_VOID(c, struct command, 1); /* ts A61007 : All current callers are safe. */ /* a ssert(o->drive == d); */ /* <<< A61030 fprintf(stderr,"libburn_debug: write_type=%d multi=%d control=%d\n", o->write_type,o->multi,o->control); fprintf(stderr,"libburn_debug: block_type=%d spc_block_type=%d\n", o->block_type,spc_block_type(o->block_type)); */ alloc_len = 8 + 2 + d->mdata->write_page_length; memset(&(buf->data), 0, alloc_len); #ifdef Libburn_pioneer_dvr_216d_load_mode5 scsi_init_command(c, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); c->dxfer_len = alloc_len; c->opcode[7] = (alloc_len >> 8) & 0xff; c->opcode[8] = alloc_len & 0xff; c->retry = 1; c->opcode[2] = 0x05; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); if (c->error) memset(&(buf->data), 0, 8 + 2 + d->mdata->write_page_length); #endif /* Libburn_pioneer_dvr_216d_load_mode5 */ scsi_init_command(c, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); c->retry = 1; c->opcode[7] = (alloc_len >> 8) & 0xff; c->opcode[8] = alloc_len & 0xff; c->page = buf; c->page->bytes = 0; c->page->sectors = 0; c->page->bytes = alloc_len; /* ts A61229 */ if (mmc_compose_mode_page_5(d, s, tnum, o, c->page->data + 8) <= 0) goto ex; c->dir = TO_DRIVE; d->issue_command(d, c); ex:; BURN_FREE_MEM(buf); BURN_FREE_MEM(c); } #ifdef Libburn_enable_scsi_cmd_ABh /* At least on Linux kernel 3.16 the command ABh causes EFAULT if not sent from the superuser. For a test it may be replaced by a dummy 28h READ12 on block 0. This causes no EFAULT although it sets the wrong dxfer_len 4 rather than 2048. So it is indeed a permission problem and not bad alignment. */ /* ts B51016 */ int spc_read_media_serial_number_al(struct burn_drive *d, int *alloc_len) { struct buffer *buf = NULL; struct command *c = NULL; unsigned char *data; int ret; if (*alloc_len < 4) {ret = 0; goto ex;} BURN_ALLOC_MEM(buf, struct buffer, 1); BURN_ALLOC_MEM(c, struct command, 1); if (mmc_function_spy(d, "spc_read_media_serial_number") <= 0) {ret = 0; goto ex;} /* #de fine Spc_read_media_serial_number_dummY yes */ #ifdef Spc_read_media_serial_number_dummY { static unsigned char MMC_READ_12[] = { 0x28, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }; scsi_init_command(c, MMC_READ_12, sizeof(MMC_READ_12)); c->dxfer_len = *alloc_len; } #else scsi_init_command(c, SPC_READ_MEDIA_SERIAL_NUMBER, sizeof(SPC_READ_MEDIA_SERIAL_NUMBER)); c->dxfer_len = *alloc_len; /* (Will not accept more than 32 KB anyway) */ c->opcode[8] = (c->dxfer_len >> 8) & 0xff; c->opcode[9] = c->dxfer_len & 0xff; #endif /* ! Spc_read_media_serial_number_dummY */ c->retry = 1; c->page = buf; memset(c->page->data, 0, *alloc_len); c->page->bytes = 0; c->page->sectors = 0; c->dir = FROM_DRIVE; d->issue_command(d, c); if (c->error) {ret = 0; goto ex;} data = c->page->data; #ifdef Spc_read_media_serial_number_dummY d->media_serial_number_len = 0; #else d->media_serial_number_len = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[7]; #endif if (*alloc_len >= d->media_serial_number_len + 4) { if (d->media_serial_number != NULL) BURN_FREE_MEM(d->media_serial_number); BURN_ALLOC_MEM(d->media_serial_number, char, d->media_serial_number_len + 1); if (d->media_serial_number_len > 0) memcpy(d->media_serial_number, data + 4, d->media_serial_number_len); d->media_serial_number[d->media_serial_number_len] = 0; } *alloc_len = d->media_serial_number_len + 4; ret = 1; ex:; BURN_FREE_MEM(c); BURN_FREE_MEM(buf); return ret; } int spc_read_media_serial_number(struct burn_drive *d) { int alloc_len = 4, ret; ret = spc_read_media_serial_number_al(d, &alloc_len); if (alloc_len > 4 && alloc_len <= 0x8000 && ret > 0) ret = spc_read_media_serial_number_al(d, &alloc_len); return ret; } #endif /* Libburn_enable_scsi_cmd_ABh */ void spc_getcaps(struct burn_drive *d) { if (mmc_function_spy(d, "getcaps") <= 0) return; burn_speed_descriptor_destroy(&(d->mdata->speed_descriptors), 1); spc_inquiry(d); spc_sense_caps(d); spc_sense_error_params(d); } /* 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 = NULL; int try_write_type = 1; int try_block_type = 0; int key, asc, ascq, usable_write_type = -1, usable_block_type = -1; int last_try = 0; struct command *c = NULL; mmc_start_if_needed(d, 1); if (mmc_function_spy(d, "spc_probe_write_modes") <= 0) goto ex; BURN_ALLOC_MEM_VOID(buf, struct buffer, 1); BURN_ALLOC_MEM_VOID(c, struct command, 1); /* ts A70213 : added pseudo try_write_type 4 to set a suitable mode */ while (try_write_type != 5) { /* ts A70213 */ if (try_write_type == 4) { /* Pseudo write type NONE . Set a usable write mode */ if (usable_write_type == -1) break; try_write_type = usable_write_type; try_block_type = usable_block_type; last_try= 1; } scsi_init_command(c, SPC_MODE_SELECT,sizeof(SPC_MODE_SELECT)); c->retry = 1; 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->silent_on_scsi_error = 1; d->issue_command(d, c); d->silent_on_scsi_error = 0; if (last_try) break; spc_decode_sense(c->sense, 0, &key, &asc, &ascq); if (key) /* try_block_type not supported */; else { /* try_write_type, try_block_type is supported mode */ 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; /* ts A70213 */ if ((usable_write_type < 0 && try_write_type > 0) || (try_write_type == 1 && try_block_type == 8)) { /* Packet is not supported yet. Prefer TAO MODE_1. */ usable_write_type = try_write_type; usable_block_type = 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: goto ex; } } ex:; BURN_FREE_MEM(buf); BURN_FREE_MEM(c); } /* ( ts A61229 : shouldn't this go to mmc.c too ?) */ /** @return -1 = error */ 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; default: return -1; } /* ts A61007 : already prevented in burn_write_opts_set_write_type() */ /* a ssert(0); */; } /* ts A61021 : the spc specific part of sg.c:enumerate_common() */ int spc_setup_drive(struct burn_drive *d) { d->getcaps = spc_getcaps; d->lock = spc_prevent; d->unlock = spc_allow; d->read_disc_info = spc_sense_write_params; d->get_erase_progress = spc_get_erase_progress; d->test_unit_ready = spc_test_unit_ready; d->probe_write_modes = spc_probe_write_modes; d->send_parameters = spc_select_error_params; d->send_write_parameters = spc_select_write_params; return 1; } /* ts A61021 : the general SCSI specific part of sg.c:enumerate_common() @param flag Bitfiled for control purposes bit0= do not setup spc/sbc/mmc */ int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no, int channel_no, int target_no, int lun_no, int flag) { int ret; /* ts A60923 */ d->bus_no = bus_no; d->host = host_no; d->id = target_no; d->channel = channel_no; d->lun = lun_no; /* ts A61106 */ d->silent_on_scsi_error = 0; /* ts B21023 */ d->had_particular_error = 0; d->idata = calloc(1, sizeof(struct burn_scsi_inquiry_data)); d->mdata = calloc(1, sizeof(struct scsi_mode_data)); /* ts A61007 : obsolete Assert in drive_getcaps() */ if (d->idata == NULL || d->mdata == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00020108, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Could not allocate new drive object", 0, 0); return -1; } d->idata->valid = 0; d->mdata->p2a_valid = 0; d->mdata->max_read_speed = 0; d->mdata->cur_read_speed = 0; d->mdata->max_write_speed = 0; d->mdata->cur_write_speed = 0; d->mdata->speed_descriptors = NULL; d->mdata->write_page_length = 0x32; d->mdata->write_page_valid = 0; if (!(flag & 1)) { ret = spc_setup_drive(d); if (ret<=0) return ret; ret = sbc_setup_drive(d); if (ret<=0) return ret; ret = mmc_setup_drive(d); if (ret<=0) return ret; } return 1; } /* ts A61122 - A80829 */ enum response scsi_error_msg(struct burn_drive *d, unsigned char *sense, int senselen, char msg_data[161], int *key, int *asc, int *ascq) { int ret; char *msg; static char key_def[16][40] = { "(no specific error)", "Recovered error", "Drive not ready", "Medium error", "Drive error", "Illegal request", "Drive event", "Data protected", "Blank/Nonblank", "Vendor specific code", "Copy aborted", "Command aborted", "(obsolete error code)", "Volume overflow", "Miscompare", "(reserved error code)", }; msg= msg_data; *key= *asc= *ascq= -1; ret = spc_decode_sense(sense, senselen, key, asc, ascq); if (ret <= 0) *key= *asc= *ascq= -1; sprintf(msg, "[%X %2.2X %2.2X] ", *key, *asc, *ascq); msg= msg + strlen(msg); if (key_def[*key & 0xf][0] != '(') { sprintf(msg, "%s. ", key_def[*key & 0xf]); msg= msg + strlen(msg); } switch (*asc) { case 0x00: if (*key > 0 || *ascq > 0) break; /* Fall through to unknown error */ sprintf(msg, "(No error reported by SCSI transaction)"); return GO_ON; case 0x02: sprintf(msg, "Not ready"); goto return_retry; case 0x04: if (*ascq == 1) sprintf(msg, "Logical unit is in the process of becoming ready"); else sprintf(msg, "Logical unit is not ready"); goto return_retry; case 0x06: if (*ascq == 0) sprintf(msg, "No reference position found"); else break; goto return_fail; case 0x08: if (*ascq == 0) sprintf(msg, "Logical unit communication failure"); else if (*ascq == 1) sprintf(msg, "Logical unit communication timeout"); else if (*ascq == 2) sprintf(msg, "Logical unit communication parity error"); else if (*ascq == 3) sprintf(msg, "Logical unit communication crc error"); else break; goto return_retry; case 0x09: if (*ascq == 0) sprintf(msg, "Track following error"); else if (*ascq == 1) sprintf(msg, "Tracking servo failure"); else if (*ascq == 2) sprintf(msg, "Focus servo failure"); else if (*ascq == 3) sprintf(msg, "Spindle servo failure"); else if (*ascq == 4) sprintf(msg, "Head select fault"); else break; goto return_fail; case 0x0C: if (*ascq == 0) sprintf(msg, "Write error"); else if (*ascq == 1) sprintf(msg, "Write error, recovered with auto-allocation"); else if (*ascq == 2) sprintf(msg, "Write error, auto reallocation failed"); else if (*ascq == 7) sprintf(msg, "Write error, recovery needed"); else if (*ascq == 8) sprintf(msg, "Write error, recovery failed"); else if (*ascq == 9) sprintf(msg, "Write error, loss of streaming"); else if (*ascq == 0x0f) sprintf(msg, "Defects in error window"); else break; goto return_fail; case 0x11: if (*ascq == 0) sprintf(msg, "Unrecovered read error"); else if (*ascq == 1) sprintf(msg, "Read retries exhausted"); else if (*ascq == 2) sprintf(msg, "Error too long to correct"); else if (*ascq == 5) sprintf(msg, "L-EC uncorrectable error"); else if (*ascq == 6) sprintf(msg, "CIRC uncorrectable error"); else break; goto return_fail; case 0x15: if (*ascq == 0) sprintf(msg, "Random positioning error"); else if (*ascq == 1) sprintf(msg, "Mechanical positioning error"); else break; goto return_fail; case 0x1a: if (*ascq == 0) sprintf(msg, "Parameter list length error"); else break; goto return_fail; case 0x1b: if (*ascq == 0) sprintf(msg, "Synchronous data transfer error"); else break; goto return_fail; case 0x20: if (*ascq == 0) sprintf(msg, "Invalid command operation code"); else break; goto return_fail; case 0x21: if (*ascq == 0) sprintf(msg, "Lba out of range"); else if (*ascq == 1) sprintf(msg, "Invalid element address"); else if (*ascq == 2) sprintf(msg, "Invalid address for write"); else if (*ascq == 3) sprintf(msg, "Invalid write crossing layer jump"); else break; goto return_fail; case 0x24: if (*ascq == 0) sprintf(msg, "Invalid field in cdb"); else break; goto return_fail; case 0x26: if (*ascq == 0) sprintf(msg, "Invalid field in parameter list"); else if (*ascq == 1) sprintf(msg, "Parameter not supported"); else if (*ascq == 2) sprintf(msg, "Parameter value invalid"); else break; goto return_fail; case 0x27: sprintf(msg, "Write protected"); goto return_fail; case 0x28: if (*ascq == 0) sprintf(msg, "Medium may have changed"); else if (*ascq == 1) sprintf(msg, "Import or export element accessed"); else if (*ascq == 2) sprintf(msg, "Format layer may have changed"); else if (*ascq == 3) sprintf(msg, "Import/export element accessed, medium changed"); else if (*key == 6) sprintf(msg, "Unknown ASCQ with drive event ASC 28"); else break; goto return_retry; case 0x29: if (*ascq == 0) sprintf(msg, "Power on, reset, or bus device reset occurred"); else if (*ascq == 1) sprintf(msg, "Power on occurred"); else if (*ascq == 2) sprintf(msg, "Bus reset occurred"); else if (*ascq == 3) sprintf(msg, "Bus device reset function occurred"); else if (*ascq == 4) sprintf(msg, "Device internal reset"); else break; goto return_retry; case 0x2c: if (*ascq == 0) sprintf(msg, "Command sequence error"); else break; goto return_fail; case 0x2e: if (*ascq == 0) sprintf(msg, "Insufficient time for operation"); else break; goto return_fail; case 0x30: if (*ascq == 0) sprintf(msg, "Incompatible medium installed"); else if (*ascq == 1) sprintf(msg, "Cannot read medium, unknown format"); else if (*ascq == 2) sprintf(msg, "Cannot read medium, incompatible format"); else if (*ascq == 4) sprintf(msg, "Cannot write medium, unknown format"); else if (*ascq == 5) sprintf(msg, "Cannot write medium, incompatible format"); else if (*ascq == 6) sprintf(msg, "Cannot format medium, incompatible medium"); else if (*ascq == 7) sprintf(msg, "Cleaning failure"); else if (*ascq == 8) sprintf(msg, "Cannot write, application code mismatch"); else if (*ascq == 9) sprintf(msg, "Current session not fixated for append"); else if (*ascq == 10) sprintf(msg, "Medium not formatted"); else if (*ascq == 11) sprintf(msg, "Cannot write medium, unsupported medium version"); else break; goto return_fail; case 0x31: if (*ascq == 0) sprintf(msg, "Medium unformatted or format corrupted"); else if (*ascq == 1) sprintf(msg, "Format command failed"); else break; goto return_fail; case 0x32: if (*ascq == 0) sprintf(msg, "No defect spare location available"); else break; goto return_fail; case 0x3A: if (*ascq == 0) sprintf(msg, "Medium not present"); else if (*ascq == 1) sprintf(msg, "Medium not present, tray closed"); else if (*ascq == 2) sprintf(msg, "Medium not present, tray open"); else if (*ascq == 3) sprintf(msg, "Medium not present, loadable"); else break; d->status = BURN_DISC_EMPTY; goto return_fail; case 0x3E: if (*ascq == 1) sprintf(msg, "Logical unit failure"); else if (*ascq == 2) sprintf(msg, "Timeout on logical unit"); else break; goto return_fail; case 0x44: if (*ascq == 0) sprintf(msg, "Internal target failure"); else break; goto return_fail; case 0x51: if (*ascq == 0) sprintf(msg, "Erase failure"); else if (*ascq == 1) sprintf(msg, "Erase failure. Incomplete erase operation"); else break; goto return_fail; case 0x57: if (*ascq == 0) sprintf(msg, "Unable to recover Table-of-Content"); else break; goto return_fail; case 0x63: if (*ascq == 0) sprintf(msg, "End of user area encountered on this track"); else if (*ascq == 1) sprintf(msg, "Packet does not fit in available space"); else break; goto return_fail; case 0x64: if (*ascq == 0) sprintf(msg, "Illegal mode for this track"); else if (*ascq == 1) sprintf(msg, "Invalid packet size"); else break; goto return_fail; case 0x6F: if (*ascq == 0) sprintf(msg, "Copy protection key exchange failure – Authentication failure"); else if (*ascq == 1) sprintf(msg, "Copy protection key exchange failure – Key not present"); else if (*ascq == 2) sprintf(msg, "Copy protection key exchange failure – Key not established"); else if (*ascq == 3) sprintf(msg, "Read of scrambled sector without authentication"); else if (*ascq == 4) sprintf(msg, "Media region code is mismatched to logical unit region"); else if (*ascq == 5) sprintf(msg, "Logical unit region must be permanent / Region reset count error"); else if (*ascq == 6) sprintf(msg, "Insufficient block count for binding nonce recording"); else if (*ascq == 7) sprintf(msg, "Conflict in binding nonce recording"); else if (*ascq == 8) sprintf(msg, "Insufficient permission"); else break; goto return_fail; case 0x72: if (*ascq == 0) sprintf(msg, "Session fixation error"); else if (*ascq == 1) sprintf(msg, "Session fixation error writing lead-in"); else if (*ascq == 2) sprintf(msg, "Session fixation error writing lead-out"); else if (*ascq == 3) sprintf(msg, "Session fixation error, incomplete track in session"); else if (*ascq == 4) sprintf(msg, "Empty or partially written reserved track"); else if (*ascq == 5) sprintf(msg, "No more track reservations allowed"); else break; goto return_fail; case 0x73: if (*ascq == 0) sprintf(msg, "CD control error"); else if (*ascq == 1) sprintf(msg, "Power calibration area almost full"); else if (*ascq == 2) sprintf(msg, "Power calibration area is full"); else if (*ascq == 3) sprintf(msg, "Power calibration area error"); else if (*ascq == 4) sprintf(msg, "Program memory area update failure"); else if (*ascq == 5) sprintf(msg, "Program memory area is full"); else break; goto return_fail; } sprintf(msg_data, "See MMC specs: Sense Key %X \"%s\", ASC %2.2X ASCQ %2.2X", *key & 0xf, key_def[(*key) & 0xf], *asc, *ascq); goto return_fail; return_fail: strcat(msg, "."); if (*key == 1) return GO_ON; return FAIL; return_retry:; strcat(msg, "."); if (*key == 1) return GO_ON; return RETRY; } /* ts A61115 moved from sg-*.c */ /* ts A61122 made it frontend to scsi_error_msg() */ enum response scsi_error(struct burn_drive *d, unsigned char *sense, int senselen) { int key, asc, ascq, ret = 0; char *msg = NULL; enum response resp; BURN_ALLOC_MEM(msg, char, 160); resp = scsi_error_msg(d, sense, senselen, msg, &key, &asc, &ascq); ex:; if (ret == -1) resp = FAIL; BURN_FREE_MEM(msg); return resp; } static char *scsi_command_name(unsigned int c, int flag) { switch (c) { case 0x00: return "TEST UNIT READY"; case 0x03: return "REQUEST SENSE"; case 0x04: return "FORMAT UNIT"; case 0x1b: return "START/STOP UNIT"; case 0x12: return "INQUIRY"; case 0x1e: return "PREVENT/ALLOW MEDIA REMOVAL"; case 0x23: return "READ FORMAT CAPACITIES"; case 0x25: return "READ CAPACITY"; case 0x28: return "READ(10)"; case 0x2a: return "WRITE(10)"; case 0x35: return "SYNCHRONIZE CACHE"; case 0x43: return "READ TOC/PMA/ATIP"; case 0x46: return "GET CONFIGURATION"; case 0x4a: return "GET EVENT STATUS NOTIFICATION"; case 0x51: return "READ DISC INFORMATION"; case 0x52: return "READ TRACK INFORMATION"; case 0x53: return "RESERVE TRACK"; case 0x54: return "SEND OPC INFORMATION"; case 0x55: return "MODE SELECT"; case 0x5a: return "MODE SENSE"; case 0x5b: return "CLOSE TRACK/SESSION"; case 0x5c: return "READ BUFFER CAPACITY"; case 0x5d: return "SEND CUE SHEET"; case 0xa1: return "BLANK"; case 0xaa: return "WRITE(12)"; case 0xab: return "READ MEDIA SERIAL NUMBER"; case 0xac: return "GET PERFORMANCE"; case 0xad: return "READ DISC STRUCTURE"; case 0xb6: return "SET STREAMING"; case 0xb9: return "READ CD MSF"; case 0xbb: return "SET CD SPEED"; case 0xbe: return "READ CD"; #ifdef Libburn_develop_quality_scaN case 0xf3: return "NEC/OPTIARC REPORT ERROR RATE"; #endif /* Libburn_develop_quality_scaN */ } return "(NOT IN LIBBURN COMMAND LIST)"; } /* ts B90206: Avoid publishing more inner API functions which begin by scsi_ */ char *spc_command_name(unsigned int c, int flag) { return(scsi_command_name(c, flag)); } /* ts B90616 */ void spc_register_retry(struct command *c) { c->retry_count++; spc_decode_sense(c->sense, c->sense_len, &c->last_retry_key, &c->last_retry_asc, &c->last_retry_ascq); } /* ts B90511 */ /* @param flag bit0= do not prepend command name bit1= do not append dxfer_len */ int spc_human_readable_cmd(struct command *c, char *msg, int msg_max, int flag) { int j, l, lname; if ((flag & 1) && c->retry_count <= 0) { msg[0] = 0; } else { if (msg_max < 60) return -1; strcpy(msg, spc_command_name( (unsigned int) c->opcode[0], 0)); if (c->retry_count > 0) { sprintf(msg + strlen(msg), " #%d", c->retry_count + 1); if (c->last_retry_key > 0) sprintf(msg + strlen(msg), ",[%X %2.2X %2.2X]", c->last_retry_key, c->last_retry_asc, c->last_retry_ascq); } strcat(msg, " : "); } lname = l = strlen(msg); for (j = 0; j < 16 && j < c->oplen; j++) { if (l > msg_max - 3) { if (msg_max - 4 >= lname) { l = msg_max - 4; sprintf(msg + strlen(msg), "... "); } return 0; } sprintf(msg + l, "%2.2x ", c->opcode[j]); l += 3; } if (c->dir != NO_TRANSFER && c->page != NULL && !(flag & 2)) { if (l > msg_max - 24) return 0; sprintf(msg + l, " : dxfer_len= %d", c->dxfer_len); l = strlen(msg); } return 1; } /* ts A61030 - A61115 */ /* @param flag bit0= do report conditions which are considered not an error bit1= report with severity FAILURE rather than DEBUG */ int scsi_notify_error(struct burn_drive *d, struct command *c, unsigned char *sense, int senselen, int flag) { int key= -1, asc= -1, ascq= -1, ret; char *msg = NULL, *scsi_msg = NULL; if (d->silent_on_scsi_error == 1 || d->silent_on_scsi_error == 2) {ret = 1; goto ex;} BURN_ALLOC_MEM(msg, char, 320); BURN_ALLOC_MEM(scsi_msg, char, 160); scsi_error_msg(d, sense, senselen, scsi_msg, &key, &asc, &ascq); if (!(flag & 1)) { /* SPC : TEST UNIT READY command */ if (c->opcode[0] == 0) {ret = 1; goto ex;} /* MMC : READ DISC INFORMATION command */ if (c->opcode[0] == 0x51) if (key == 0x2 && asc == 0x3A && ascq>=0 && ascq <= 0x02) /* MEDIUM NOT PRESENT */ {ret = 1; goto ex;} if (key == 0 && asc == 0 && ascq == 0) {ret = 1; goto ex;} } sprintf(msg, "SCSI error condition on command %2.2Xh %s: ", c->opcode[0], scsi_command_name((unsigned int) c->opcode[0], 0)); strcat(msg, scsi_msg); ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f, (flag & 2) && d->silent_on_scsi_error != 3 ? LIBDAX_MSGS_SEV_FAILURE : LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); strcpy(msg, "CDB= "); if (spc_human_readable_cmd(c, msg + strlen(msg), 320 - strlen(msg), 1) > 0) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f, (flag & 2) && d->silent_on_scsi_error != 3 ? LIBDAX_MSGS_SEV_FAILURE : LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } ex:; BURN_FREE_MEM(msg); BURN_FREE_MEM(scsi_msg); return ret; } /* ts B11110 */ /* @param flag bit0= do not show eventual data payload sent to the drive (never with WRITE commands) */ int scsi_show_command(unsigned char *opcode, int oplen, int dir, unsigned char *data, int bytes, void *fp_in, int flag) { int i; FILE *fp = fp_in; fprintf(fp, "\n%s\n", scsi_command_name((unsigned int) opcode[0], 0)); for(i = 0; i < 16 && i < oplen; i++) fprintf(fp, "%2.2x ", opcode[i]); if (i > 0) fprintf(fp, "\n"); if (flag & 1) return 1; if (opcode[0] == 0x2A) { /* WRITE 10 */ if ((flag & 2) && oplen > 8) fprintf(fp, "%d -> %d\n", (opcode[7] << 8) | opcode[8], mmc_four_char_to_int(opcode + 2)); } else if (opcode[0] == 0xAA) { /* WRITE 12 */ if ((flag & 2) && oplen > 9) fprintf(fp, "%d -> %d\n", mmc_four_char_to_int(opcode + 6), mmc_four_char_to_int(opcode + 2)); } else if (dir == TO_DRIVE && !(flag & 1)) { fprintf(fp, "To drive: %db\n", bytes); for (i = 0; i < bytes; i++) fprintf(fp, "%2.2x%c", data[i], ((i % 20) == 19 ? '\n' : ' ')); if (i % 20) fprintf(fp, "\n"); } return 1; } /* ts A91106 */ /* @param flag bit0= do not show eventual data payload sent to the drive (never with WRITE commands) */ int scsi_show_cmd_text(struct command *c, void *fp_in, int flag) { return scsi_show_command(c->opcode, c->oplen, c->dir, c->page->data, c->page->bytes, fp_in, flag); } /* ts A91106 */ /* ts B11110 */ int scsi_show_command_reply(unsigned char *opcode, int data_dir, unsigned char *data, int dxfer_len, void *fp_in, int flag) { int i; FILE *fp = fp_in; if (data_dir != FROM_DRIVE) return 2; if (opcode[0] == 0x28 || opcode[0] == 0x3C || opcode[0] == 0xA8 || opcode[0] == 0xB9 || opcode[0] == 0xBE) { /* READ commands */ /* >>> report amount of data */; return 2; } fprintf(fp, "From drive: %db\n", dxfer_len); for (i = 0; i < dxfer_len; i++) fprintf(fp, "%2.2x%c", data[i], ((i % 20) == 19 ? '\n' : ' ')); if (i % 20) fprintf(fp, "\n"); return 1; } /* ts B11110 */ /** Logs command (before execution) */ int scsi_log_command(unsigned char *opcode, int oplen, int data_dir, unsigned char *data, int bytes, void *fp_in, int flag) { FILE *fp = fp_in; if (fp != NULL && (fp == stderr || (burn_sg_log_scsi & 1))) { scsi_show_command(opcode, oplen, data_dir, data, bytes, fp, 0); if (burn_sg_log_scsi & 4) fflush(fp); } if (fp == stderr || !(burn_sg_log_scsi & 2)) return 1; scsi_log_command(opcode, oplen, data_dir, data, bytes, stderr, 0); return 1; } /* ts B40731 */ /* Arbitrary SCSI log message */ int scsi_log_text(char *text, void *fp_in, int flag) { FILE *fp = fp_in; if (fp != NULL && (fp == stderr || (burn_sg_log_scsi & 1))) { fprintf(fp, "%s\n", text); if (burn_sg_log_scsi & 4) fflush(fp); } if (fp == stderr || !(burn_sg_log_scsi & 2)) return 1; fprintf(stderr, "%s\n", text); return 1; } /* ts A91218 (former sg_log_cmd ts A70518) */ /** Logs command (before execution) */ int scsi_log_cmd(struct command *c, void *fp_in, int flag) { int ret, bytes = 0; unsigned char *data = NULL; if (c->page != NULL) { data = c->page->data; bytes = c->page->bytes; } ret = scsi_log_command(c->opcode, c->oplen, c->dir, data, bytes, fp_in, flag); return ret; } /* ts B11110 */ /** Logs outcome of a sg command. @param flag bit0 causes an error message bit1 do not print duration */ int scsi_log_reply(unsigned char *opcode, int data_dir, unsigned char *data, int dxfer_len, void *fp_in, unsigned char sense[18], int sense_len, double duration, int flag) { FILE *fp = fp_in; int key, asc, ascq, i, l; if (fp != NULL && (fp == stderr || (burn_sg_log_scsi & 1))) { if (flag & 1) { l = 18; if ((sense[0] & 0x7f) == 0x72 || (sense[0] & 0x7f) == 0x73) l = sense[7] + 7 + 1; /* SPC-3 4.5.2. */ if (l > sense_len) l = sense_len; fprintf(fp, "+++ sense data ="); for (i = 0 ; i < l; i++) fprintf(fp, " %2.2X", sense[i]); fprintf(fp, "\n"); spc_decode_sense(sense, 0, &key, &asc, &ascq); fprintf(fp, "+++ key=%X asc=%2.2Xh ascq=%2.2Xh\n", (unsigned int) key, (unsigned int) asc, (unsigned int) ascq); } else { scsi_show_command_reply(opcode, data_dir, data, dxfer_len, fp, 0); } if (!(flag & 2)) fprintf(fp, " %8.f us [ %.f ]\n", duration * 1.0e6, (burn_get_time(0) - lib_start_time) * 1.0e6); if (burn_sg_log_scsi & 4) fflush(fp); } if (fp == stderr || !(burn_sg_log_scsi & 2)) return 1; scsi_log_reply(opcode, data_dir, data, dxfer_len, stderr, sense, sense_len, duration, flag); return 1; } /* ts A91221 (former sg_log_err ts A91108) */ /** Legacy frontend to scsi_log_reply(). @param flag bit0 causes an error message bit1 do not print duration */ int scsi_log_err(struct burn_drive *d, struct command *c, void *fp_in, unsigned char sense[18], int sense_len, int flag) { int ret; unsigned char *data = NULL; if (c->page != NULL) data = c->page->data; ret= scsi_log_reply(c->opcode, c->dir, data, c->dxfer_len , fp_in, sense, sense_len, c->end_time - c->start_time, flag); return ret; } /* ts B31112 */ int scsi_log_message(struct burn_drive *d, void *fp_in, char * msg, int flag) { int ret; FILE *fp = fp_in; if (fp != NULL && (fp == stderr || (burn_sg_log_scsi & 1))) { fprintf(fp, "%s\n", msg); if (burn_sg_log_scsi & 4) fflush(fp); } if (fp == stderr || !(burn_sg_log_scsi & 2)) return 1; ret = scsi_log_message(d, stderr, msg, flag); return ret; } /* ts B00808 */ /* @param flag bit0 = do not retry bit1 = do not print duration @return 0 = not yet done , 1 = done , -1 = error */ int scsi_eval_cmd_outcome(struct burn_drive *d, struct command *c, void *fp, unsigned char *sense, int sense_len, time_t start_time, int timeout_ms, int loop_count, int flag) { enum response outcome; int done = -1, usleep_time; char *msg = NULL; if (burn_sg_log_scsi & 3) scsi_log_err(d, c, fp, sense, sense_len, (sense_len > 0) | (flag & 2)); if (sense == c->sense) c->sense_len = sense_len; if (sense_len <= 0) {done = 1; goto ex;} outcome = scsi_error(d, sense, sense_len); if (outcome == RETRY && c->retry && !(flag & 1)) { /* Calming down retries and breaking up endless cycle */ if (c->opcode[0] == 0x2A || c->opcode[0] == 0xAA) { /* WRITE(10) , WRITE(12) */ usleep_time = Libburn_scsi_write_retry_usleeP + loop_count * Libburn_scsi_write_retry_incR; if (usleep_time > Libburn_scsi_write_retry_umaX) usleep_time = Libburn_scsi_write_retry_umaX; } else { usleep_time = Libburn_scsi_retry_usleeP + loop_count * Libburn_scsi_retry_incR; if (usleep_time > Libburn_scsi_retry_umaX) usleep_time = Libburn_scsi_retry_umaX; } if (time(NULL) + usleep_time / 1000000 - start_time > timeout_ms / 1000 + 1) { done = -1; /* In case of alloc failure */ BURN_ALLOC_MEM_VOID(msg, char, 320); done = 1; sprintf(msg, "Timeout exceed (%d ms). Retry canceled.\n", timeout_ms); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002018a, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); strcpy(msg, "Command: "); if (spc_human_readable_cmd(c, msg + strlen(msg), 320 - strlen(msg), 0) > 0) libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002018a, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); goto err_ex; } if (d->cancel) {done = 1; goto ex;} if (usleep_time > 0) usleep(usleep_time); if (d->cancel) {done = 1; goto ex;} if (burn_sg_log_scsi & 3) scsi_log_cmd(c, fp, 0); {done = 0; goto ex;} } else if (outcome == RETRY) { done = 1; } else if (outcome == GO_ON) { {done = 1; goto ex;} } else if (outcome == FAIL) { done = 1; } err_ex:; c->error = 1; scsi_notify_error(d, c, sense, sense_len, 0); ex:; BURN_FREE_MEM(msg); return done; } int spc_confirm_cd_drive(struct burn_drive *d, int flag) { char *msg = NULL; int ret; BURN_ALLOC_MEM(msg, char, strlen(d->devname) + 1024); spc_inquiry(d); if (d->idata->valid < 0) { sprintf(msg, "INQUIRY failed with drive '%s'", d->devname); libdax_msgs_submit(libdax_messenger, -1, 0x0002000a, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); ret = 0; goto ex; } if (d->idata->peripheral != 0x5) { sprintf(msg, "Does not identify itself as CD-ROM drive '%s'", d->devname); libdax_msgs_submit(libdax_messenger, -1, 0x0002000a, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); ret = 0; goto ex; } ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2019 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #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 *, struct burn_session *, int, const struct burn_write_opts *); #ifdef Libburn_enable_scsi_cmd_ABh int spc_read_media_serial_number(struct burn_drive *d); #endif 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); /* ts A70315 : test_unit_ready with result parameters */ int spc_test_unit_ready_r(struct burn_drive *d, int *key, int *asc, int *ascq, int *progress); int spc_test_unit_ready(struct burn_drive *d); /* ts A70315 */ /** Wait until the drive state becomes clear in or until max_sec elapsed */ int spc_wait_unit_attention(struct burn_drive *d, int max_sec, char *cmd_text, int flag); /* ts A61021 : the spc specific part of sg.c:enumerate_common() */ int spc_setup_drive(struct burn_drive *d); /* ts A61021 : the general SCSI specific part of sg.c:enumerate_common() @param flag Bitfield for control purposes bit0= do not setup spc/sbc/mmc */ int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no, int channel_no, int target_no, int lun_no, int flag); /* ts A61115 moved from sg-*.h */ enum response { RETRY, FAIL, GO_ON }; enum response scsi_error(struct burn_drive *, unsigned char *, int); /* ts A61122 */ enum response scsi_error_msg(struct burn_drive *d, unsigned char *sense, int senselen, char msg[161], int *key, int *asc, int *ascq); /* ts A61030 */ /* @param flag bit0=do report conditions which are considered not an error */ int scsi_notify_error(struct burn_drive *, struct command *c, unsigned char *sense, int senselen, int flag); /* ts A70519 */ int scsi_init_command(struct command *c, unsigned char *opcode, int oplen); /* ts A91106 */ int scsi_show_cmd_text(struct command *c, void *fp, int flag); /* ts B11110 */ /** Logs command (before execution). */ int scsi_log_command(unsigned char *opcode, int oplen, int data_dir, unsigned char *data, int bytes, void *fp_in, int flag); /* ts B40731 */ /* Arbitrary SCSI log message */ int scsi_log_text(char *text, void *fp_in, int flag); /* ts A91218 (former sg_log_cmd ts A70518) */ /** Legacy frontend to scsi_log_command() */ int scsi_log_cmd(struct command *c, void *fp, int flag); /* ts B11110 */ /** Logs outcome of a sg command. @param flag bit0 causes an error message bit1 do not print duration */ int scsi_log_reply(unsigned char *opcode, int data_dir, unsigned char *data, int dxfer_len, void *fp_in, unsigned char sense[18], int sense_len, double duration, int flag); /* ts A91221 (former sg_log_err ts A91108) */ /** Legacy frontend to scsi_log_reply(). @param flag bit0 causes an error message bit1 do not print duration */ int scsi_log_err(struct burn_drive *d, struct command *c, void *fp, unsigned char sense[18], int sense_len, int flag); /* ts B31112 */ int scsi_log_message(struct burn_drive *d, void *fp, char * msg, int flag); /* ts B00728 */ int spc_decode_sense(unsigned char *sense, int senselen, int *key, int *asc, int *ascq); /* ts B90206 */ char *spc_command_name(unsigned int c, int flag); /* ts B90511 */ int spc_human_readable_cmd(struct command *c, char *msg, int msg_max, int flag); /* ts B90616 */ void spc_register_retry(struct command *c); /* ts B00808 */ /** Evaluates outcome of a single SCSI command, eventually logs sense data, and issues DEBUG error message in case the command is evaluated as done. @param flag bit1 = do not print duration @return 0 = not yet done , 1 = done , -1 = error */ int scsi_eval_cmd_outcome(struct burn_drive *d, struct command *c, void *fp_in, unsigned char *sense, int sense_len, time_t start_time, int timeout_ms, int loop_count, int flag); /* ts B40204 */ /* Verify by INQUIRY that the drive is indeed a MMC device. */ int spc_confirm_cd_drive(struct burn_drive *d, int flag); /* The waiting time before eventually retrying a failed SCSI command. Before each retry wait Libburn_scsi_retry_incR longer than with the previous one. At most wait for Libburn_scsi_retry_umaX microseconds. */ #define Libburn_scsi_retry_usleeP 100000 #define Libburn_scsi_retry_incR 100000 #define Libburn_scsi_retry_umaX 500000 /* The retry waiting time for commands WRITE(10) and WRITE(12). */ #define Libburn_scsi_write_retry_usleeP 0 #define Libburn_scsi_write_retry_incR 2000 #define Libburn_scsi_write_retry_umaX 25000 /* ts B11124 */ /* Millisecond timeout for quickly responding SPC, SBC, and MMC commands */ #define Libburn_scsi_default_timeouT 30000 /* WRITE(10) and WRITE(12) */ #define Libburn_scsi_write_timeouT 200000 /* RESERVE TRACK */ #define Libburn_mmc_reserve_timeouT 200000 /* CLOSE TRACK/SESSION with Immed bit */ #define Libburn_mmc_close_timeouT 200000 /* CLOSE TRACK/SESSION without Immed bit */ #define Libburn_mmc_close_noim_timeouT 3600000 /* BLANK , FORMAT UNIT with Immed bit */ #define Libburn_mmc_blank_timeouT 200000 /* BLANK , FORMAT UNIT without Immed bit */ #define Libburn_mmc_blank_noim_timeouT 18000000 /* SEND OPC INFORMATION */ #define Libburn_mmc_opc_timeouT 200000 /* MMC_SYNC_CACHE with Immed bit */ #define Libburn_mmc_sync_timeouT 200000 /* MMC_SYNC_CACHE without Immed bit */ #define Libburn_mmc_sync_noim_timeouT 3600000 /* START STOP UNIT with Start bit and Load bit set */ #define Libburn_mmc_load_timeouT 300000 #endif /*__SPC*/ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2021 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /* ts A61008 */ /* #include <a ssert.h> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> /* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #include "libburn.h" #include "structure.h" #include "write.h" #include "debug.h" #include "init.h" #include "util.h" #include "transport.h" #include "mmc.h" #include "drive.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /* ts A61008 : replaced Assert by if and return 0 */ /* a ssert(!(pos > BURN_POS_END)); */ #define RESIZE(TO, NEW, pos) {\ void *tmp;\ \ if (pos > BURN_POS_END)\ return 0;\ if (pos == BURN_POS_END)\ pos = TO->NEW##s;\ if ((int) 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 = calloc(1, sizeof(struct burn_disc)); if (d == NULL) /* ts A70825 */ return NULL; d->refcnt = 1; d->sessions = 0; d->session = NULL; #ifdef Libburn_disc_with_incomplete_sessioN d->incomplete_sessions= 0; #endif 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; int i; s = calloc(1, sizeof(struct burn_session)); if (s == NULL) /* ts A70825 */ return NULL; s->firsttrack = 1; s->lasttrack = 0; s->refcnt = 1; s->tracks = 0; s->track = NULL; s->hidefirst = 0; for (i = 0; i < 8; i++) { s->cdtext[i] = NULL; s->cdtext_language[i] = 0x00; /* Unknown */ s->cdtext_char_code[i] = 0x00; /* ISO-8859-1 */ s->cdtext_copyright[i] = 0x00; } s->cdtext_language[0] = 0x09; /* Single-block default is English */ s->mediacatalog[0] = 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) { int i; s->refcnt--; if (s->refcnt == 0) { /* dec refs on all elements */ for (i = 0; i < s->tracks; i++) burn_track_free(s->track[i]); for (i = 0; i < 8; i++) burn_cdtext_free(&(s->cdtext[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; } /* ts A81202: this function was in the API but not implemented. */ int burn_disc_remove_session(struct burn_disc *d, struct burn_session *s) { int i, skip = 0; if (d->session == NULL) return 0; for (i = 0; i < d->sessions; i++) { if (s == d->session[i]) { skip++; continue; } d->session[i - skip] = d->session[i]; } if (!skip) return 0; burn_session_free(s); d->sessions--; return 1; } struct burn_track *burn_track_create(void) { struct burn_track *t; int i; t = calloc(1, sizeof(struct burn_track)); if (t == NULL) /* ts A70825 */ return NULL; t->refcnt = 1; t->indices = 0; for (i = 0; i < 100; i++) t->index[i] = 0x7fffffff; t->offset = 0; t->offsetcount = 0; t->tail = 0; t->tailcount = 0; t->mode = BURN_MODE1; t->isrc.has_isrc = 0; t->pad = 1; /* ts A70213 */ t->fill_up_media = 0; /* ts A70218 */ t->default_size = 0; t->entry = NULL; t->source = NULL; t->eos = 0; /* ts A61101 */ t->sourcecount = 0; t->writecount = 0; t->written_sectors = 0; /* ts A61031 */ t->open_ended = 0; t->track_data_done = 0; /* ts B10103 */ t->end_on_premature_eoi = 0; t->pregap1 = 0; t->pregap2 = 0; t->pregap2_size = 150; t->postgap = 0; t->postgap_size = 150; /* ts A61024 */ t->swap_source_bytes = 0; /* ts B11206 */ for (i = 0; i < 8; i++) t->cdtext[i] = NULL; return t; } void burn_track_free(struct burn_track *t) { int i; t->refcnt--; if (t->refcnt == 0) { /* dec refs on all elements */ if (t->source) burn_source_free(t->source); for (i = 0; i < 8; i++) burn_cdtext_free(&(t->cdtext[i])); 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; /* ts A61008 */ /* a ssert(s->track != NULL); */ if (s->track == NULL) return 0; burn_track_free(t); /* Find the position */ for (i = 0; i < s->tracks; i++) { if (t == s->track[i]) { pos = i; break; } } if (pos == -1) return 0; /* Is it the last track? */ if (pos != s->tracks - 1) { 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) s->track = tmp; return 1; } void burn_structure_print_disc(struct burn_disc *d) { int i; char msg[40]; sprintf(msg, "This disc has %d sessions", d->sessions); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); 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; char msg[40]; sprintf(msg, " Session has %d tracks", s->tracks); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); for (i = 0; i < s->tracks; i++) { burn_structure_print_track(s->track[i]); } } void burn_structure_print_track(struct burn_track *t) { char msg[80]; sprintf(msg, " track size %.f sectors", (double) burn_track_get_sectors_v2(t)); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } void burn_track_define_data(struct burn_track *t, int offset, int tail, int pad, int mode) { int type_to_form(int mode, unsigned char *ctladr, int *form); int burn_sector_length(int tracktype); unsigned char ctladr; int form = -1; /* unchanged form will be considered an error too */ char msg[80]; type_to_form(mode, &ctladr, &form); if (form == -1 || burn_sector_length(mode) <= 0) { sprintf(msg, "Attempt to set track mode to unusable value 0x%X", (unsigned int) mode); libdax_msgs_submit(libdax_messenger, -1, 0x00020115, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); return; } t->offset = offset; t->pad = pad; t->mode = mode; t->tail = tail; } /* ts A61024 */ int burn_track_set_byte_swap(struct burn_track *t, int swap_source_bytes) { if (swap_source_bytes != 0 && swap_source_bytes != 1) return 0; t->swap_source_bytes = swap_source_bytes; return 1; } /* ts A90911 : API */ int burn_track_set_cdxa_conv(struct burn_track *t, int value) { if (value < 0 || value > 1) return 0; t->cdxa_conversion = value; return 1; } void burn_track_set_isrc(struct burn_track *t, char *country, char *owner, unsigned char year, unsigned int serial) { int i; /* ts B11226 */ t->isrc.has_isrc = 0; for (i = 0; i < 2; ++i) { /* ts A61008 : This is always true */ /* a ssert((country[i] >= '0' || country[i] < '9') && (country[i] >= 'a' || country[i] < 'z') && (country[i] >= 'A' || country[i] < 'Z')); */ /* ts A61008 : now coordinated with sector.c: char_to_isrc() */ if (! ((country[i] >= '0' && country[i] <= '9') || (country[i] >= 'a' && country[i] <= 'z') || (country[i] >= 'A' && country[i] <= 'Z') ) ) goto is_not_allowed; t->isrc.country[i] = country[i]; } for (i = 0; i < 3; ++i) { /* ts A61008 : This is always true */ /* a ssert((owner[i] >= '0' || owner[i] < '9') && (owner[i] >= 'a' || owner[i] < 'z') && (owner[i] >= 'A' || owner[i] < 'Z')); */ /* ts A61008 : now coordinated with sector.c: char_to_isrc() */ if (! ((owner[i] >= '0' && owner[i] <= '9') || (owner[i] >= 'a' && owner[i] <= 'z') || (owner[i] >= 'A' && owner[i] <= 'Z') ) ) goto is_not_allowed; t->isrc.owner[i] = owner[i]; } /* ts A61008 */ /* a ssert(year <= 99); */ if (year > 99) goto is_not_allowed; t->isrc.year = year; /* ts A61008 */ /* a ssert(serial <= 99999); */ if (serial > 99999) goto is_not_allowed; t->isrc.serial = serial; /* ts A61008 */ t->isrc.has_isrc = 1; return; is_not_allowed:; libdax_msgs_submit(libdax_messenger, -1, 0x00020114, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Attempt to set ISRC with bad data", 0, 0); return; } /* ts B11226 API */ int burn_track_set_isrc_string(struct burn_track *t, char isrc[13], int flag) { unsigned char year; unsigned int serial = 2000000000; if (strlen(isrc) != 12 || isrc[5] < '0' || isrc[5] > '9' || isrc[6] < '0' || isrc[6] > '9') { libdax_msgs_submit(libdax_messenger, -1, 0x00020114, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Attempt to set ISRC with bad data", 0, 0); return 0; } year = (isrc[5] - '0') * 10 + (isrc[6] - '0'); isrc[12] = 0; sscanf(isrc + 7, "%u", &serial); burn_track_set_isrc(t, isrc, isrc + 2, year, serial); return t->isrc.has_isrc; } void burn_track_clear_isrc(struct burn_track *t) { t->isrc.has_isrc = 0; } /* ts B20103 API */ int burn_track_set_index(struct burn_track *t, int index_number, unsigned int relative_lba, int flag) { if (index_number < 0 || index_number > 99) { libdax_msgs_submit(libdax_messenger, -1, 0x0002019a, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Bad track index number", 0, 0); return 0; } /* >>> if track size known : check index */; t->index[index_number] = relative_lba; if (index_number >= t->indices) t->indices = index_number + 1; return 1; } /* ts B20103 API */ int burn_track_clear_indice(struct burn_track *t, int flag) { int i; for (i = 0; i < 100; i++) t->index[i] = 0x7fffffff; t->indices = 0; return 1; } /* ts B20110 API */ int burn_track_set_pregap_size(struct burn_track *t, int size, int flag) { t->pregap2 = (size >= 0); t->pregap2_size = size; return 1; } /* ts B20111 API */ int burn_track_set_postgap_size(struct burn_track *t, int size, int flag) { t->postgap = (size >= 0); t->postgap_size = size; return 1; } /* ts B20119 / C40302: outsourced from burn_track_get_sectors() @param flag bit0= do not add post-gap */ off_t burn_track_get_sectors_2_v2(struct burn_track *t, int flag) { /* ts A70125 : was int */ off_t size = 0, sectors; int seclen; seclen = burn_sector_length(t->mode); if (t->cdxa_conversion == 1) /* ts A90911 : will read blocks of 2056 bytes and write 2048 */ seclen += 8; if (t->source != NULL) { /* ts A80808 : mending sigsegv */ size = t->offset + t->source->get_size(t->source) + t->tail; /* ts B20119 : adding post-gap */ if (t->postgap && !(flag & 1)) size += t->postgap_size; } else if(t->entry != NULL) { /* ts A80808 : all burn_toc_entry of track starts should now have (extensions_valid & 1), even those from CD. ts C40302 : Now there should be long_track_blocks. */ if (t->entry->extensions_valid & 8) { size = t->entry->long_track_blocks * (off_t) 2048; } else if (t->entry->extensions_valid & 1) { size = ((off_t) t->entry->track_blocks) * (off_t) 2048; } } if (size > BURN_DRIVE_MAX_BYTES) { char msg[80]; sprintf(msg, "Track size exceeds limit of %.f bytes", (double) (BURN_DRIVE_MAX_BYTES)); libdax_msgs_submit(libdax_messenger, -1, 0x000201ae, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); return -1; } sectors = size / seclen; if (size % seclen) sectors++; return sectors; } int burn_track_get_sectors_2(struct burn_track *t, int flag) { /* ts A70125 : was int */ off_t sectors = 0; sectors = burn_track_get_sectors_2_v2(t, flag); if (sectors > (off_t) 0x7ffffff0) { libdax_msgs_submit(libdax_messenger, -1, 0x000201ae, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Track size exceeds 4 TiB - 32 KiB", 0, 0); return -1; } return (int) sectors; } int burn_track_get_sectors(struct burn_track *t) { return burn_track_get_sectors_2(t, 0); } /* ts C40302 : API */ off_t burn_track_get_sectors_v2(struct burn_track *t) { return burn_track_get_sectors_2_v2(t, 0); } /* ts A70125 */ int burn_track_set_sectors(struct burn_track *t, off_t sectors) { off_t size, seclen; int ret; seclen = burn_sector_length(t->mode); size = seclen * (off_t) sectors - (off_t) t->offset - (off_t) t->tail; if (size < 0) return 0; ret = t->source->set_size(t->source, size); t->open_ended = (t->source->get_size(t->source) <= 0); return ret; } /* ts A70218 , API since A70328 */ int burn_track_set_size(struct burn_track *t, off_t size) { if (t->source == NULL) return 0; if (t->source->set_size == NULL) return 0; t->open_ended = (size <= 0); return t->source->set_size(t->source, size); } /* ts A70213 */ int burn_track_set_fillup(struct burn_track *t, int fill_up_media) { t->fill_up_media = fill_up_media; if (fill_up_media) t->open_ended = 0; return 1; } /* ts A70213 */ /** @param flag bit0= force new size even if existing track size is larger */ int burn_track_apply_fillup(struct burn_track *t, off_t max_size, int flag) { int ret = 2; off_t max_sectors, track_sectors; char msg[80]; if (t->fill_up_media <= 0) return 2; if (max_size > BURN_DRIVE_MAX_BYTES) { sprintf(msg, "Track size exceeds limit of %.f bytes", (double) (BURN_DRIVE_MAX_BYTES)); libdax_msgs_submit(libdax_messenger, -1, 0x000201ae, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); return 0; } max_sectors = max_size / 2048; track_sectors = burn_track_get_sectors_v2(t); if (track_sectors < 0) return 0; if (track_sectors < max_sectors || (flag & 1)) { sprintf(msg, "Setting total track size to %.fs (payload %.fs)\n", (double) max_sectors, (double) (t->source->get_size(t->source) / 2048)); libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); ret = burn_track_set_sectors(t, max_sectors); t->open_ended = 0; } return ret; } /* ts A61031 */ int burn_track_is_open_ended(struct burn_track *t) { return !!t->open_ended; } /* ts A70218 : API */ int burn_track_set_default_size(struct burn_track *t, off_t size) { t->default_size = size; return 1; } /* ts A70218 */ off_t burn_track_get_default_size(struct burn_track *t) { return t->default_size; } /* ts A61101 : API function */ int burn_track_get_counters(struct burn_track *t, off_t *read_bytes, off_t *written_bytes) { /* fprintf(stderr, "libburn_experimental: sizeof(off_t)=%d\n", sizeof(off_t)); */ *read_bytes = t->sourcecount; *written_bytes = t->writecount; return 1; } /* ts A61031 */ int burn_track_is_data_done(struct burn_track *t) { return !!t->track_data_done; } int burn_track_get_shortage(struct burn_track *t) { off_t 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, track_sectors; for (i = 0; i < s->tracks; i++) { track_sectors = burn_track_get_sectors(s->track[i]); if (track_sectors < 0) track_sectors = 0; sectors += track_sectors; } return sectors; } /* ts C40302: API */ off_t burn_session_get_sectors_v2(struct burn_session *s) { int i; off_t sectors = 0, track_sectors; for (i = 0; i < s->tracks; i++) { track_sectors = burn_track_get_sectors_v2(s->track[i]); if (track_sectors < 0) track_sectors = 0; sectors += track_sectors; } 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; } /* ts C40302: API */ off_t burn_disc_get_sectors_v2(struct burn_disc *d) { int i; off_t sectors = 0; for (i = 0; i < d->sessions; i++) sectors += burn_session_get_sectors_v2(d->session[i]); return sectors; } void burn_track_get_entry(struct burn_track *t, struct burn_toc_entry *entry) { if (t->entry == NULL) memset(entry, 0, sizeof(struct burn_toc_entry)); else memcpy(entry, t->entry, sizeof(struct burn_toc_entry)); } void burn_session_get_leadout_entry(struct burn_session *s, struct burn_toc_entry *entry) { if (s->leadout_entry == NULL) memset(entry, 0, sizeof(struct burn_toc_entry)); else memcpy(entry, s->leadout_entry, sizeof(struct burn_toc_entry)); } struct burn_session **burn_disc_get_sessions(struct burn_disc *d, int *num) { #ifdef Libburn_disc_with_incomplete_sessioN *num = d->sessions - d->incomplete_sessions; #else *num = d->sessions; #endif return d->session; } /* ts B30112 : API */ int burn_disc_get_incomplete_sessions(struct burn_disc *d) { #ifdef Libburn_disc_with_incomplete_sessioN return d->incomplete_sessions; #else return 0; #endif } 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; } /* ts A80808,C40226 : Enhance CD toc to DVD toc with Long block addresses */ int burn_disc_cd_toc_extensions(struct burn_drive *drive, int flag) { int sidx= 0, tidx= 0, ret, track_offset, alloc_len = 34; struct burn_toc_entry *entry, *prev_entry= NULL; struct burn_disc *d; /* ts A81126 : ticket 146 : There was a SIGSEGV in here */ char *msg_data = NULL, *msg; struct buffer *buf = NULL; d = drive->disc; BURN_ALLOC_MEM(msg_data, char, 321); BURN_ALLOC_MEM(buf, struct buffer, 1); strcpy(msg_data, "Damaged CD table-of-content detected and truncated."); strcat(msg_data, " In burn_disc_cd_toc_extensions: "); msg = msg_data + strlen(msg_data); if (d->session == NULL) { strcpy(msg, "d->session == NULL"); goto failure; } if (d->sessions <= 0) { ret = 1; goto ex; } for (sidx = 0; sidx < d->sessions; sidx++) { track_offset = burn_session_get_start_tno(d->session[sidx], 0); if (track_offset <= 0) track_offset = 1; if (d->session[sidx] == NULL) { sprintf(msg, "d->session[%d of %d] == NULL", sidx, d->sessions); goto failure; } if (d->session[sidx]->track == NULL) { sprintf(msg, "d->session[%d of %d]->track == NULL", sidx, d->sessions); goto failure; } if (d->session[sidx]->leadout_entry == NULL) { sprintf(msg, " Session %d of %d: Leadout entry missing.", sidx, d->sessions); goto failure; } for (tidx = 0; tidx < d->session[sidx]->tracks + 1; tidx++) { if (tidx < d->session[sidx]->tracks) { if (d->session[sidx]->track[tidx] == NULL) { sprintf(msg, "d->session[%d of %d]->track[%d of %d] == NULL", sidx, d->sessions, tidx, d->session[sidx]->tracks); goto failure; } entry = d->session[sidx]->track[tidx]->entry; if (entry == NULL) { sprintf(msg, "session %d of %d, track %d of %d, entry == NULL", sidx, d->sessions, tidx, d->session[sidx]->tracks); goto failure; } } else entry = d->session[sidx]->leadout_entry; entry->session_msb = 0; entry->point_msb = 0; entry->start_lba = burn_msf_to_lba(entry->pmin, entry->psec, entry->pframe); entry->long_start_lba = entry->start_lba; if (tidx > 0) { prev_entry->track_blocks = entry->start_lba - prev_entry->start_lba; /* The drive might know size restrictions like pre-gaps */ ret = mmc_read_track_info(drive, tidx - 1 + track_offset, buf, alloc_len); if (ret > 0) { ret = mmc_four_char_to_int( buf->data + 24); if (ret < prev_entry->track_blocks && ((!drive->current_is_cd_profile) || ret < prev_entry->track_blocks - 2)) prev_entry->track_blocks = ret; } prev_entry->long_track_blocks = prev_entry->track_blocks; prev_entry->extensions_valid |= 1 | 8; } if (tidx == d->session[sidx]->tracks) { entry->session_msb = 0; entry->point_msb = 0; entry->track_blocks = 0; entry->long_track_blocks = entry->track_blocks; entry->extensions_valid |= 1 | 8; } prev_entry = entry; } } {ret = 1; goto ex;} failure: libdax_msgs_submit(libdax_messenger, -1, 0x0002015f, LIBDAX_MSGS_SEV_MISHAP, LIBDAX_MSGS_PRIO_HIGH, msg_data, 0, 0); d->sessions= sidx; ret = 0; ex:; BURN_FREE_MEM(buf); BURN_FREE_MEM(msg_data); return ret; } /* ts B20107 API */ int burn_session_set_start_tno(struct burn_session *session, int tno, int flag) { if (tno < 1 || tno > 99) { libdax_msgs_submit(libdax_messenger, -1, 0x0002019b, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "CD start track number exceeds range of 1 to 99", 0, 0); return 0; } if (tno + session->tracks - 1 > 99) { libdax_msgs_submit(libdax_messenger, -1, 0x0002019b, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "CD track number exceeds 99", 0, 0); return 0; } session->firsttrack = tno; return 1; } /* ts B20108 API */ int burn_session_get_start_tno(struct burn_session *session, int flag) { return (int) session->firsttrack; } struct burn_cdtext *burn_cdtext_create(void) { struct burn_cdtext *t; int i; t = burn_alloc_mem(sizeof(struct burn_cdtext), 1, 0); if (t == NULL) return NULL; for(i = 0; i < Libburn_pack_num_typeS; i ++) { t->payload[i] = NULL; t->length[i] = 0; } return t; } void burn_cdtext_free(struct burn_cdtext **cdtext) { struct burn_cdtext *t; int i; t = *cdtext; if (t == NULL) return; for (i = 0; i < Libburn_pack_num_typeS; i++) if (t->payload[i] != NULL) free(t->payload[i]); free(t); } static int burn_cdtext_name_to_type(char *pack_type_name) { int i, j; static char *pack_type_names[] = { Libburn_pack_type_nameS }; for (i = 0; i < Libburn_pack_num_typeS; i++) { if (pack_type_names[i][0] == 0) continue; for (j = 0; pack_type_names[i][j]; j++) if (pack_type_names[i][j] != pack_type_name[j] && tolower(pack_type_names[i][j]) != pack_type_name[j]) break; if (pack_type_names[i][j] == 0) return Libburn_pack_type_basE + i; } return -1; } /* @param flag bit0= double byte characters */ static int burn_cdtext_set(struct burn_cdtext **cdtext, int pack_type, char *pack_type_name, unsigned char *payload, int length, int flag) { int i; struct burn_cdtext *t; if (pack_type_name != NULL) if (pack_type_name[0]) pack_type = burn_cdtext_name_to_type(pack_type_name); if (pack_type < Libburn_pack_type_basE || pack_type >= Libburn_pack_type_basE + Libburn_pack_num_typeS) { libdax_msgs_submit(libdax_messenger, -1, 0x0002018c, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "CD-TEXT pack type out of range", 0, 0); return 0; } t = *cdtext; if (t == NULL) { *cdtext = t = burn_cdtext_create(); if (t == NULL) return -1; } i = pack_type - Libburn_pack_type_basE; if (t->payload[i] != NULL) free(t->payload[i]); t->payload[i] = burn_alloc_mem((size_t) length, 1, 0); if (t->payload[i] == NULL) return -1; memcpy(t->payload[i], payload, length); t->length[i] = length; t->flags = (t->flags & ~(1 << i)) | (flag & (1 << i)); return 1; } /* @return 1=single byte char , 2= double byte char , <=0 error */ static int burn_cdtext_get(struct burn_cdtext *t, int pack_type, char *pack_type_name, unsigned char **payload, int *length, int flag) { if (pack_type_name != NULL) if (pack_type_name[0]) pack_type = burn_cdtext_name_to_type(pack_type_name); if (pack_type < Libburn_pack_type_basE || pack_type >= Libburn_pack_type_basE + Libburn_pack_num_typeS) { libdax_msgs_submit(libdax_messenger, -1, 0x0002018c, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "CD-TEXT pack type out of range", 0, 0); return 0; } *payload = t->payload[pack_type - Libburn_pack_type_basE]; *length = t->length[pack_type - Libburn_pack_type_basE]; return 1 + ((t->flags >> (pack_type - Libburn_pack_type_basE)) & 1); } static int burn_cdtext_check_blockno(int block) { if (block < 0 || block > 7) { libdax_msgs_submit(libdax_messenger, -1, 0x0002018d, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "CD-TEXT block number out of range", 0, 0); return 0; } return 1; } /* ts B11206 API */ /* @param flag bit0= double byte characters */ int burn_track_set_cdtext(struct burn_track *t, int block, int pack_type, char *pack_type_name, unsigned char *payload, int length, int flag) { int ret; if (burn_cdtext_check_blockno(block) <= 0) return 0; ret = burn_cdtext_set(&(t->cdtext[block]), pack_type, pack_type_name, payload, length, flag & 1); return ret; } /* ts B11206 API */ /* @return 1=single byte char , 2= double byte char , <=0 error */ int burn_track_get_cdtext(struct burn_track *t, int block, int pack_type, char *pack_type_name, unsigned char **payload, int *length, int flag) { int ret; if (burn_cdtext_check_blockno(block) <= 0) return 0; if (t->cdtext[block] == NULL) { *payload = NULL; *length = 0; return 1; } ret = burn_cdtext_get(t->cdtext[block], pack_type, pack_type_name, payload, length, 0); return ret; } /* ts B11206 API */ int burn_track_dispose_cdtext(struct burn_track *t, int block) { int i; if (block == -1) { for (i= 0; i < 8; i++) burn_cdtext_free(&(t->cdtext[i])); return 1; } if (burn_cdtext_check_blockno(block) <= 0) return 0; burn_cdtext_free(&(t->cdtext[0])); return 1; } /* ts B11206 API */ /* @param flag bit0= double byte characters */ int burn_session_set_cdtext(struct burn_session *s, int block, int pack_type, char *pack_type_name, unsigned char *payload, int length, int flag) { int ret; if (burn_cdtext_check_blockno(block) <= 0) return 0; ret = burn_cdtext_set(&(s->cdtext[block]), pack_type, pack_type_name, payload, length, flag & 1); return ret; } /* ts B11206 API */ /* @return 1=single byte char , 2= double byte char , <=0 error */ int burn_session_get_cdtext(struct burn_session *s, int block, int pack_type, char *pack_type_name, unsigned char **payload, int *length, int flag) { int ret; if (burn_cdtext_check_blockno(block) <= 0) return 0; if (s->cdtext[block] == NULL) { *payload = NULL; *length = 0; return 1; } ret = burn_cdtext_get(s->cdtext[block], pack_type, pack_type_name, payload, length, 0); return ret; } /* ts B11206 API */ int burn_session_set_cdtext_par(struct burn_session *s, int char_codes[8], int copyrights[8], int block_languages[8], int flag) { int i; for (i = 0; i < 8; i++) { if (char_codes[i] >= 0 && char_codes[i] <= 255) s->cdtext_char_code[i] = char_codes[i]; if (copyrights[i] >= 0 && copyrights[i] <= 255) s->cdtext_copyright[i] = copyrights[i]; if (block_languages[i] >= 0 && block_languages[i] <= 255) s->cdtext_language[i] = block_languages[i]; } return 1; } /* ts B11206 API */ int burn_session_get_cdtext_par(struct burn_session *s, int char_codes[8], int copyrights[8], int block_languages[8], int flag) { int i; for (i = 0; i < 8; i++) { char_codes[i] = s->cdtext_char_code[i]; copyrights[i] = s->cdtext_copyright[i]; block_languages[i]= s->cdtext_language[i]; } return 1; } /* ts B11206 API */ int burn_session_dispose_cdtext(struct burn_session *s, int block) { int i; if (block == -1) { for (i= 0; i < 8; i++) { burn_session_dispose_cdtext(s, i); s->cdtext_char_code[i] = 0x01; /* 7 bit ASCII */ s->cdtext_copyright[i] = 0; s->cdtext_language[i] = 0; } return 1; } if (burn_cdtext_check_blockno(block) <= 0) return 0; burn_cdtext_free(&(s->cdtext[block])); s->cdtext_language[block] = 0x09; /* english */ return 1; } /* --------------------- Reading CDRWIN cue sheet files ----------------- */ struct burn_cue_file_cursor { char *cdtextfile; char *source_file; off_t source_size; struct burn_source *file_source; int fifo_size; struct burn_source *fifo; int swap_audio_bytes; int no_cdtext; int no_catalog_isrc; int start_track_no; struct burn_source *offst_source; int current_file_ba; int current_index_ba; struct burn_track *prev_track; int prev_file_ba; int prev_block_size; struct burn_track *track; int track_no; int track_current_index; int track_has_source; int block_size; int block_size_locked; int track_mode; int flags; }; static int cue_crs_new(struct burn_cue_file_cursor **reply, int flag) { int ret; struct burn_cue_file_cursor *crs; BURN_ALLOC_MEM(crs, struct burn_cue_file_cursor, 1); crs->cdtextfile = NULL; crs->source_file = NULL; crs->source_size = -1; crs->file_source = NULL; crs->fifo_size = 0; crs->fifo = NULL; crs->swap_audio_bytes = 0; crs->no_cdtext = 0; crs->no_catalog_isrc = 0; crs->start_track_no = 1; crs->offst_source = NULL; crs->current_file_ba = -1000000000; crs->current_index_ba = -1000000000; crs->prev_track = NULL; crs->prev_file_ba = -1000000000; crs->prev_block_size = 0; crs->track = NULL; crs->track_no = 0; crs->track_current_index = -1; crs->track_has_source = 0; crs->block_size = 0; crs->block_size_locked = 0; crs->track_mode = 0; crs->flags = 0; *reply = crs; ret = 1; ex:; return ret; } static int cue_crs_destroy(struct burn_cue_file_cursor **victim, int flag) { struct burn_cue_file_cursor *crs; if (*victim == NULL) return 2; crs = *victim; if (crs->cdtextfile != NULL) free(crs->cdtextfile); if (crs->source_file != NULL) free(crs->source_file); if (crs->file_source != NULL) burn_source_free(crs->file_source); if (crs->fifo != NULL) burn_source_free(crs->fifo); if (crs->offst_source != NULL) burn_source_free(crs->offst_source); if (crs->prev_track != NULL) burn_track_free(crs->prev_track); if (crs->track != NULL) burn_track_free(crs->track); BURN_FREE_MEM(crs); *victim = NULL; return 1; } static char *cue_unquote_text(char *text, int flag) { char *ept, *spt; spt = text; for (ept = text + strlen(text); ept > text; ept--) if (*(ept - 1) != 32 && *(ept - 1) != 9) break; if (text[0] == '"') { spt = text + 1; if (ept > spt) if (*(ept - 1) == '"') ept--; } *ept = 0; return spt; } /* @param flag bit0= insist in having a track object bit1= remove quotation marks if present */ static int cue_set_cdtext(struct burn_session *session, struct burn_track *track, int pack_type, char *text, struct burn_cue_file_cursor *crs, int flag) { int ret; char *payload; if (crs->no_cdtext == 1) { libdax_msgs_submit(libdax_messenger, -1, 0x00020195, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, "In cue sheet file: Being set to ignore all CD-TEXT aspects", 0, 0); crs->no_cdtext = 2; } if (crs->no_cdtext) return 2; if ((flag & 1) && track == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00020192, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Track attribute set before first track in cue sheet file", 0, 0); ret = 0; goto ex; } if (flag & 2) payload = cue_unquote_text(text, 0); else payload = text; if (track != NULL) { ret = burn_track_set_cdtext(track, 0, pack_type, "", (unsigned char *) payload, strlen(payload) + 1, 0); } else { ret = burn_session_set_cdtext(session, 0, pack_type, "", (unsigned char *) payload, strlen(payload) + 1, 0); } ex:; return ret; } static int cue_attach_track(struct burn_session *session, struct burn_cue_file_cursor *crs, int flag) { int ret; if (crs->track == NULL) return 2; if (!crs->track_has_source) { libdax_msgs_submit(libdax_messenger, -1, 0x00020194, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "In cue sheet file: TRACK without INDEX 01", 0, 0); return 0; } if (crs->track_current_index < 1) { libdax_msgs_submit(libdax_messenger, -1, 0x00020192, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "No INDEX 01 defined for last TRACK in cue sheet file", 0, 0); return 0; } if (session->tracks == 0) { crs->start_track_no = crs->track_no; ret = burn_session_set_start_tno(session, crs->track_no, 0); if (ret <= 0) return ret; } if (session->tracks + crs->start_track_no - 1 > 99) { libdax_msgs_submit(libdax_messenger, -1, 0x0002019b, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "CD track number exceeds 99", 0, 0); return 0; } ret = burn_session_add_track(session, crs->track, BURN_POS_END); if (ret <= 0) return ret; if (crs->prev_track != NULL) burn_track_free(crs->prev_track); /* release reference */ crs->prev_track = crs->track; crs->prev_file_ba = crs->current_file_ba; crs->prev_block_size = crs->block_size; crs->track = NULL; crs->track_current_index = -1; crs->track_has_source = 0; crs->current_file_ba = -1; crs->current_index_ba = -1; if (!crs->block_size_locked) crs->block_size = 0; return 1; } /* @param flag bit0= do not alter the content of *payload do not change *payload */ static int cue_read_number(char **payload, int *number, int flag) { int ret, at_end = 0; char *apt, *msg = NULL; for(apt = *payload; *apt != 0 && *apt != 32 && *apt != 9; apt++); if (*apt == 0) at_end = 1; else if (!(flag & 1)) *apt = 0; ret = sscanf(*payload, "%d", number); if (ret != 1) { BURN_ALLOC_MEM(msg, char, 4096); sprintf(msg, "Unsuitable number in cue sheet file: '%.4000s'", *payload); libdax_msgs_submit(libdax_messenger, -1, 0x00020194, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } /* Find start of next argument */ if (!at_end) for (apt++; *apt == 32 || *apt == 9; apt++); if (!(flag & 1)) *payload = apt; ret = 1; ex: BURN_FREE_MEM(msg); return ret; } /* @param flag bit0-7: desired type : 0=any , 1=.wav */ static int cue_open_audioxtr(char *path, struct burn_cue_file_cursor *crs, int *fd, int flag) { struct libdax_audioxtr *xtr= NULL; char *fmt, *fmt_info; int ret, num_channels, sample_rate, bits_per_sample, msb_first; char *msg = NULL; BURN_ALLOC_MEM(msg, char, 4096); ret= libdax_audioxtr_new(&xtr, path, 0); if (ret <= 0) goto ex; libdax_audioxtr_get_id(xtr, &fmt, &fmt_info, &num_channels, &sample_rate, &bits_per_sample, &msb_first, 0); if ((flag & 255) == 1) { if (strcmp(fmt, ".wav") != 0) { sprintf(msg, "In cue sheet: Not recognized as WAVE : FILE '%.4000s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x00020193, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } } ret = libdax_audioxtr_get_size(xtr, &(crs->source_size), 0); if (ret <= 0) { sprintf(msg, "In cue sheet: Cannot get payload size of FILE '%.4000s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x00020193, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } ret = libdax_audioxtr_detach_fd(xtr, fd, 0); if (ret <= 0) { sprintf(msg, "In cue sheet: Cannot represent payload as plain fd: FILE '%.4000s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x00020193, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } crs->swap_audio_bytes = (msb_first == 1); ret = 1; ex: if (xtr != NULL) libdax_audioxtr_destroy(&xtr, 0); BURN_FREE_MEM(msg); return ret; } /* @param flag bit0-7: desired type : 0=any , 1=.wav bit8= open by libdax_audioxtr functions */ static int cue_create_file_source(char *path, struct burn_cue_file_cursor *crs, int flag) { int fd, ret; char *msg = NULL; BURN_ALLOC_MEM(msg, char, 4096); if (flag & 256) { ret = cue_open_audioxtr(path, crs, &fd, flag & 255); if (ret <= 0) goto ex; } else { fd = open(path, O_RDONLY | O_BINARY); if (fd == -1) { sprintf(msg, "In cue sheet: Cannot open FILE '%.4000s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x00020193, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), errno, 0); ret = 0; goto ex; } } crs->file_source = burn_fd_source_new(fd, -1, crs->source_size); if (crs->file_source == NULL) { ret = -1; goto ex; } ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } static int cue_read_timepoint_lba(char *apt, char *purpose, int *file_ba, int flag) { int ret, minute, second, frame; char *msg = NULL, msf[3], *msf_pt; BURN_ALLOC_MEM(msg, char, 4096); if (strlen(apt) < 8) { no_time_point:; sprintf(msg, "Inappropriate cue sheet file %s '%.4000s'", purpose, apt); libdax_msgs_submit(libdax_messenger, -1, 0x00020194, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } if (apt[2] != ':' || apt[5] != ':' || (apt[8] != 0 && apt[8] != 32 && apt[8] != 9)) goto no_time_point; msf[2] = 0; msf_pt = msf; strncpy(msf, apt, 2); ret = cue_read_number(&msf_pt, &minute, 1); if (ret <= 0) goto ex; strncpy(msf, apt + 3, 2); ret = cue_read_number(&msf_pt, &second, 1); if (ret <= 0) goto ex; strncpy(msf, apt + 6, 2); ret = cue_read_number(&msf_pt, &frame, 1); if (ret <= 0) goto ex; *file_ba = ((minute * 60) + second ) * 75 + frame; ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } static int cue_check_for_track(struct burn_cue_file_cursor *crs, char *cmd, int flag) { int ret; char *msg = NULL; if (crs->track == NULL) { BURN_ALLOC_MEM(msg, char, 4096); sprintf(msg, "In cue sheet file: %s found before TRACK", cmd); libdax_msgs_submit(libdax_messenger, -1, 0x00020192, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); ret = 0; goto ex; } ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } static int cue_interpret_line(struct burn_session *session, char *line, struct burn_cue_file_cursor *crs, int flag) { int ret, mode, index_no, file_ba, chunks; int block_size, step, audio_xtr = 0; off_t size; char *cmd, *apt, *msg = NULL, *cpt, *filetype; struct burn_source *src, *inp_src; enum burn_source_status source_status; struct stat stbuf; BURN_ALLOC_MEM(msg, char, 4096); if (line[0] == 0 || line[0] == '#') { ret = 1; goto ex; } for (cmd = line; *cmd == 32 || *cmd == 9; cmd++); for(apt = cmd; *apt != 0 && *apt != 32 && *apt != 9; apt++); if (*apt != 0) { *apt = 0; for (apt++; *apt == 32 || *apt == 9; apt++); } if (strcmp(cmd, "ARRANGER") == 0) { ret = cue_set_cdtext(session, crs->track, 0x84, apt, crs, 2); if (ret <= 0) goto ex; } else if (strcmp(cmd, "CATALOG") == 0) { for (cpt = apt; (cpt - apt) < 13 && *cpt == (*cpt & 0x7f); cpt++); if ((cpt - apt) < 13) { libdax_msgs_submit(libdax_messenger, -1, 0x00020194, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "In cue sheet file: Inappropriate content of CATALOG", 0, 0); ret = 0; goto ex; } ret = cue_set_cdtext(session, NULL, 0x8e, apt, crs, 0); if (ret <= 0) goto ex; if (!crs->no_catalog_isrc) { memcpy(session->mediacatalog, apt, 13); session->mediacatalog[13] = 0; } } else if (strcmp(cmd, "CDTEXTFILE") == 0) { if (crs->no_cdtext) { ret = 1; goto ex; } apt = cue_unquote_text(apt, 0); if (crs->cdtextfile != NULL) free(crs->cdtextfile); crs->cdtextfile = strdup(apt); if (crs->cdtextfile == NULL) { out_of_mem:; libdax_msgs_submit(libdax_messenger, -1, 0x00000003, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Out of virtual memory", 0, 0); ret = -1; goto ex; } } else if (strcmp(cmd, "COMPOSER") == 0) { ret = cue_set_cdtext(session, crs->track, 0x83, apt, crs, 2); if (ret <= 0) goto ex; } else if (strcmp(cmd, "FILE") == 0) { if (crs->file_source != NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00020192, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "In cue sheet file: Multiple occurrences of FILE", 0, 0); ret = 0; goto ex; } /* Obtain type */ for (cpt = apt + (strlen(apt) - 1); cpt > apt && (*cpt == 32 || *cpt == 9); cpt--); cpt[1] = 0; for (; cpt > apt && *cpt != 32 && *cpt != 9; cpt--); if (cpt <= apt) { libdax_msgs_submit(libdax_messenger, -1, 0x00020194, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "In cue sheet file: FILE without type word", 0, 0); ret = 0; goto ex; } *cpt = 0; filetype = cpt + 1; if (strcmp(filetype, "BINARY") == 0) { crs->swap_audio_bytes = 0; } else if (strcmp(filetype, "MOTOROLA") == 0) { crs->swap_audio_bytes = 1; } else if (strcmp(filetype, "WAVE") == 0) { audio_xtr = 0x101; } else { sprintf(msg, "In cue sheet file: Unsupported FILE type '%.4000s'", filetype); libdax_msgs_submit(libdax_messenger, -1, 0x00020197, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } apt = cue_unquote_text(apt, 0); if (*apt == 0) ret = -1; else ret = stat(apt, &stbuf); if (ret == -1) { not_usable_file:; sprintf(msg, "In cue sheet file: Unusable FILE '%.4000s'", apt); libdax_msgs_submit(libdax_messenger, -1, 0x00020194, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } if (!S_ISREG(stbuf.st_mode)) goto not_usable_file; crs->source_size = stbuf.st_size; if (crs->source_file != NULL) free(crs->source_file); crs->source_file = strdup(apt); if (crs->source_file == NULL) goto out_of_mem; ret = cue_create_file_source(apt, crs, audio_xtr); if (ret <= 0) goto ex; } else if (strcmp(cmd, "FLAGS") == 0) { ret = cue_check_for_track(crs, cmd, 0); if (ret <= 0) goto ex; while (*apt) { if (strncmp(apt, "DCP", 3) == 0) { crs->track_mode |= BURN_COPY; step = 3; } else if (strncmp(apt, "4CH", 3) == 0) { crs->track_mode |= BURN_4CH; step = 3; } else if (strncmp(apt, "PRE", 3) == 0) { crs->track_mode |= BURN_PREEMPHASIS; step = 3; } else if (strncmp(apt, "SCMS", 4) == 0) { crs->track_mode |= BURN_SCMS; step = 4; } else { bad_flags:; for (cpt = apt; *cpt != 32 && *cpt != 9 && *cpt != 0; cpt++); *cpt = 0; sprintf(msg, "In cue sheet file: Unknown FLAGS option '%.4000s'", apt); libdax_msgs_submit(libdax_messenger, -1, 0x00020194, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } /* Look for start of next word */ if (apt[step] != 0 && apt[step] != 32 && apt[step] != 9) goto bad_flags; for (apt += step; *apt == 32 || *apt == 9; apt++); } burn_track_define_data(crs->track, 0, 0, 1, crs->track_mode); } else if (strcmp(cmd, "INDEX") == 0) { ret = cue_check_for_track(crs, cmd, 0); if (ret <= 0) goto ex; ret = cue_read_number(&apt, &index_no, 0); if (ret <= 0) goto ex; ret = cue_read_timepoint_lba(apt, "index time point", &file_ba, 0); if (ret <= 0) goto ex; if (file_ba < crs->prev_file_ba) { overlapping_ba:; libdax_msgs_submit(libdax_messenger, -1, 0x00020192, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Backward INDEX address in cue sheet file", 0, 0); ret = 0; goto ex; } if (file_ba < crs->current_index_ba) goto overlapping_ba; if (crs->prev_track != NULL && crs->track_current_index < 0) { size = (file_ba - crs->prev_file_ba) * crs->prev_block_size; if (size <= 0) goto overlapping_ba; burn_track_set_size(crs->prev_track, size); } if (crs->track_current_index + 1 != index_no && !(crs->track_current_index < 0 && index_no <= 1)) { libdax_msgs_submit(libdax_messenger, -1, 0x00020192, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Unacceptable INDEX number in cue sheet file", 0, 0); ret = 0; goto ex; } crs->track_current_index = index_no; if (crs->current_file_ba < 0) crs->current_file_ba = file_ba; crs->current_index_ba = file_ba; /* Set index address relative to track source start */ ret = burn_track_set_index(crs->track, index_no, file_ba - crs->current_file_ba, 0); if (ret <= 0) goto ex; if (crs->track_has_source) { ret = 1; goto ex; } if (crs->block_size_locked && crs->fifo == NULL && crs->fifo_size > 0) { /* Now that the block size is known from TRACK: Create fifo and use it for creating the offset sources. This will fixate the block size to one common value. */ chunks = crs->fifo_size / crs->block_size + !!(crs->fifo_size % crs->block_size); if (chunks < 4) chunks = 4; crs->fifo = burn_fifo_source_new(crs->file_source, crs->block_size, chunks, 0); if (crs->fifo == NULL) { ret = -1; goto ex; } } if (crs->fifo != NULL) inp_src = crs->fifo; else inp_src = crs->file_source; src = burn_offst_source_new(inp_src, crs->offst_source, (off_t) (file_ba * crs->block_size), (off_t) 0, 1); if (src == NULL) goto out_of_mem; /* >>> Alternative to above fifo creation: Create a fifo for each track track. This will be necessary if mixed-mode sessions get supporded. */; source_status = burn_track_set_source(crs->track, src); if (source_status != BURN_SOURCE_OK) { ret = -1; goto ex; } /* Switch current source in crs */ if (crs->offst_source != NULL) burn_source_free(crs->offst_source); crs->offst_source = src; crs->track_has_source = 1; } else if (strcmp(cmd, "ISRC") == 0) { ret = cue_check_for_track(crs, cmd, 0); if (ret <= 0) goto ex; ret = cue_set_cdtext(session, crs->track, 0x8e, apt, crs, 1 | 2); if (ret <= 0) goto ex; if (!crs->no_catalog_isrc) { ret = burn_track_set_isrc_string(crs->track, apt, 0); if (ret <= 0) goto ex; } } else if (strcmp(cmd, "MESSAGE") == 0) { ret = cue_set_cdtext(session, crs->track, 0x85, apt, crs, 2); if (ret <= 0) goto ex; } else if (strcmp(cmd, "PERFORMER") == 0) { ret = cue_set_cdtext(session, crs->track, 0x81, apt, crs, 2); if (ret <= 0) goto ex; } else if (strcmp(cmd, "POSTGAP") == 0) { ret = cue_check_for_track(crs, cmd, 0); if (ret <= 0) goto ex; ret = cue_read_timepoint_lba(apt, "post-gap duration", &file_ba, 0); if (ret <= 0) goto ex; ret = burn_track_set_postgap_size(crs->track, file_ba, 0); if (ret <= 0) goto ex; } else if (strcmp(cmd, "PREGAP") == 0) { ret = cue_check_for_track(crs, cmd, 0); if (ret <= 0) goto ex; ret = cue_read_timepoint_lba(apt, "pre-gap duration", &file_ba, 0); if (ret <= 0) goto ex; ret = burn_track_set_pregap_size(crs->track, file_ba, 0); if (ret <= 0) goto ex; } else if (strcmp(cmd, "REM") == 0) { ; } else if (strcmp(cmd, "SONGWRITER") == 0) { ret = cue_set_cdtext(session, crs->track, 0x82, apt, crs, 2); if (ret <= 0) goto ex; } else if (strcmp(cmd, "TITLE") == 0) { ret = cue_set_cdtext(session, crs->track, 0x80, apt, crs, 2); if (ret <= 0) goto ex; } else if (strcmp(cmd, "TRACK") == 0) { if (crs->file_source == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00020192, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "No FILE defined before TRACK in cue sheet file", 0, 0); ret = 0; goto ex; } /* Attach previous track to session */ ret = cue_attach_track(session, crs, 0); if (ret <= 0) goto ex; /* Create new track */; ret = cue_read_number(&apt, &(crs->track_no), 0); if (ret <= 0) goto ex; if (crs->track_no < 1 || crs->track_no > 99) { sprintf(msg, "Inappropriate cue sheet file track number %d", crs->track_no); libdax_msgs_submit(libdax_messenger, -1, 0x00020194, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } if (strcmp(apt, "AUDIO") == 0) { mode = BURN_AUDIO; block_size = 2352; } else if (strcmp(apt, "MODE1/2048") == 0) { mode = BURN_MODE1; block_size = 2048; } else { sprintf(msg, "Unsupported cue sheet file track datatype '%.4000s'", apt); libdax_msgs_submit(libdax_messenger, -1, 0x00020197, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } if (block_size != crs->block_size && crs->block_size > 0 && crs->block_size_locked) { libdax_msgs_submit(libdax_messenger, -1, 0x00020197, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "In cue sheet file: Unsupported mix track block sizes", 0, 0); ret = 0; goto ex; } crs->block_size = block_size; crs->track = burn_track_create(); if (crs->track == NULL) goto out_of_mem; crs->track_has_source = 0; crs->track_mode = mode; burn_track_define_data(crs->track, 0, 0, 1, mode); if (mode & BURN_AUDIO) burn_track_set_byte_swap(crs->track, !!crs->swap_audio_bytes); } else { sprintf(msg, "Unknown cue sheet file command '%.4000s'", line); libdax_msgs_submit(libdax_messenger, -1, 0x00020191, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } /* ts B11216 API */ /* @param flag bit0= do not attach CD-TEXT information to session and tracks bit1= do not attach CATALOG to session or ISRC to track for writing to Q sub-channel */ int burn_session_by_cue_file(struct burn_session *session, char *path, int fifo_size, struct burn_source **fifo, unsigned char **text_packs, int *num_packs, int flag) { int ret, num_tracks, i, pack_type, length, double_byte = 0; int line_counter = 0; struct burn_track **tracks; char *msg = NULL, *line = NULL; unsigned char *payload; struct stat stbuf; FILE *fp = NULL; struct burn_cue_file_cursor *crs = NULL; static unsigned char dummy_cdtext[2] = {0, 0}; if (fifo != NULL) *fifo = NULL; if (text_packs != NULL) *text_packs = NULL; *num_packs = 0; BURN_ALLOC_MEM(msg, char, 4096); BURN_ALLOC_MEM(line, char, 4096); ret = cue_crs_new(&crs, 0); if (ret <= 0) goto ex; crs->no_cdtext = (flag & 1); crs->no_catalog_isrc = !!(flag & 2); crs->fifo_size = fifo_size; crs->block_size_locked = 1; /* No mixed sessions for now */ tracks = burn_session_get_tracks(session, &num_tracks); if (num_tracks > 0) { sprintf(msg, "Cue sheet file reader called while session has already defined tracks"); libdax_msgs_submit(libdax_messenger, -1, 0x00020196, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } if (stat(path, &stbuf) == -1) { cannot_open:; sprintf(msg, "Cannot open cue sheet file '%.4000s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x00020193, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), errno, 0); ret = 0; goto ex; } if (!S_ISREG(stbuf.st_mode)) { sprintf(msg, "File is not of usable type: Cue sheet file '%.4000s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x00020193, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } fp = fopen(path, "rb"); if (fp == NULL) goto cannot_open; while (1) { if (burn_sfile_fgets(line, 4095, fp) == NULL) { if (!ferror(fp)) break; sprintf(msg, "Cannot read all bytes from cue sheet file '%.4000s'", path); libdax_msgs_submit(libdax_messenger, -1, 0x00020193, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); ret = 0; goto ex; } line_counter++; ret = cue_interpret_line(session, line, crs, 0); if (ret <= 0) { sprintf(msg, "Cue sheet file '%.4000s': Reading aborted after line %d", path, line_counter); libdax_msgs_submit(libdax_messenger, -1, 0x00020199, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, burn_printify(msg), 0, 0); goto ex; } } /* Attach last track to session */ if (crs->track != NULL) { /* Set track size up to end of file */ if (crs->current_file_ba < 0 || crs->track_current_index < 1) { libdax_msgs_submit(libdax_messenger, -1, 0x00020192, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "No INDEX 01 defined for last TRACK in cue sheet file", 0, 0); ret = 0; goto ex; } if (crs->current_file_ba * crs->block_size >= crs->source_size) { libdax_msgs_submit(libdax_messenger, -1, 0x00020194, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "TRACK start time point exceeds size of FILE from cue sheet file", 0, 0); ret = 0; goto ex; } ret = burn_track_set_size(crs->track, crs->source_size - (off_t) (crs->current_file_ba * crs->block_size)); if (ret <= 0) goto ex; ret = cue_attach_track(session, crs, 0); if (ret <= 0) goto ex; } if (crs->cdtextfile != NULL) { if (text_packs == NULL) { /* >>> Warn of ignored text packs */; } else { ret = burn_cdtext_from_packfile(crs->cdtextfile, text_packs, num_packs, 0); if (ret <= 0) goto ex; } } /* Check which tracks have data of pack types where session has not */ tracks = burn_session_get_tracks(session, &num_tracks); for (pack_type = 0x80; pack_type < 0x8f; pack_type++) { if (pack_type > 0x86 && pack_type != 0x8e) continue; ret = burn_session_get_cdtext(session, 0, pack_type, "", &payload, &length, 0); if (ret <= 0) goto ex; if (payload != NULL) continue; for (i = 0; i < num_tracks; i++) { ret = burn_track_get_cdtext(tracks[i], 0, pack_type, "", &payload, &length, 0); if (ret <= 0) goto ex; double_byte = (ret > 1); if (payload != NULL) break; } if (i < num_tracks) { ret = burn_session_set_cdtext(session, 0, pack_type, "", dummy_cdtext, 1 + double_byte, double_byte); if (ret <= 0) goto ex; } } ret = 1; ex: if (ret <= 0) { tracks = burn_session_get_tracks(session, &num_tracks); for (i = 0; i < num_tracks; i++) burn_track_free(tracks[i]); if(text_packs != NULL) { if(*text_packs != NULL) free(*text_packs); *text_packs = NULL; *num_packs = 0; } } else { if (fifo != NULL) { *fifo = crs->fifo; crs->fifo = NULL; } } cue_crs_destroy(&crs, 0); BURN_FREE_MEM(line); BURN_FREE_MEM(msg); if (fp != NULL) fclose(fp); return ret; } /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2011 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #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 */ }; /* ts B11206 */ #define Libburn_pack_type_basE 0x80 #define Libburn_pack_num_typeS 0x10 #define Libburn_pack_type_nameS \ "TITLE", "PERFORMER", "SONGWRITER", "COMPOSER", \ "ARRANGER", "MESSAGE", "DISCID", "GENRE", \ "TOC", "TOC2", "", "", \ "", "CLOSED", "UPC_ISRC", "BLOCKSIZE" struct burn_cdtext { unsigned char *(payload[Libburn_pack_num_typeS]); int length[Libburn_pack_num_typeS]; int flags; /* bit0 - bit15= double byte characters */ }; struct burn_track { int refcnt; struct burn_toc_entry *entry; unsigned char indices; /* lba address of the index. CD only. 0x7fffffff means undefined index. To be programmed relative to track source start before burning, but to hold absolute addresses after burning or reading. */ int index[100]; /** 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; /* ts A70213 : whether to expand this track to full available media */ int fill_up_media; /* ts A70218 : a track size to use if it is mandarory to have some */ off_t default_size; /** Data source */ struct burn_source *source; /** End of Source flag */ int eos; /* ts A61101 */ off_t sourcecount; off_t writecount; off_t written_sectors; /* ts A61031 */ /** Source is of undefined length */ int open_ended; /** End of open ended track flag : offset+payload+tail are delivered */ int track_data_done; /* ts B10103 */ /** End track writing on premature End-of-input if source is of defined length. 0= normal operation in case of eoi 1= be ready to end track writing on eoi 2= eoi was encountered with previously set value of 1 */ int end_on_premature_eoi; /** 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; /* ts B20110 */ /** The number of sectors in pre-gap 2, if .pregap2 is set */ int pregap2_size; /** The track contains a postgap */ int postgap; /* ts B20111 */ /** The number of sectors in post-gap, if .postgap is set */ int postgap_size; struct isrc isrc; /* ts A61024 */ /** Byte swapping on source data stream : 0=none , 1=pairwise */ int swap_source_bytes; /* ts A90910 : conversions from CD XA prepared input */ int cdxa_conversion; /* 0=none, 1=remove -xa1 headers (first 8 bytes)*/ /* ts B11206 */ struct burn_cdtext *cdtext[8]; }; 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; /* ts B11206 */ struct burn_cdtext *cdtext[8]; unsigned char cdtext_char_code[8]; unsigned char cdtext_copyright[8]; unsigned char cdtext_language[8]; /* ts B11226 */ unsigned char mediacatalog[14]; /* overridable by burn_write_opts */ }; struct burn_disc { int sessions; struct burn_session **session; #ifdef Libburn_disc_with_incomplete_sessioN int incomplete_sessions; #endif int refcnt; }; int burn_track_get_shortage(struct burn_track *t); /* ts A61031 : might go to libburn.h */ int burn_track_is_open_ended(struct burn_track *t); int burn_track_is_data_done(struct burn_track *t); /* ts A70125 : sets overall sectors of a track: offset+payload+padding */ int burn_track_set_sectors(struct burn_track *t, off_t sectors); /* ts A70218 : sets the payload size alone */ int burn_track_set_size(struct burn_track *t, off_t size); /* ts A70213 */ int burn_track_set_fillup(struct burn_track *t, int fill_up_media); int burn_track_apply_fillup(struct burn_track *t, off_t max_size, int flag); /* ts A70218 */ off_t burn_track_get_default_size(struct burn_track *t); /* ts A80808 : Enhance CD toc to DVD toc */ int burn_disc_cd_toc_extensions(struct burn_drive *drive, int flag); /* ts B11206 */ struct burn_cdtext *burn_cdtext_create(void); void burn_cdtext_free(struct burn_cdtext **cdtext); /* ts B20119 */ /* @param flag bit0= do not add post-gap */ int burn_track_get_sectors_2(struct burn_track *t, int flag); off_t burn_track_get_sectors_2_v2(struct burn_track *t, int flag); #endif /* BURN__STRUCTURE_H */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2011 - 2011 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /* ts A61008 */ /* #include <a ssert.h> */ #include <string.h> #include <stdio.h> #include <stdlib.h> #include "toc.h" #include "transport.h" #include "libburn.h" #include "sector.h" #include "options.h" #include "init.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: /* ts A61008 : do this softly without Assert */ a ssert(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) { int i, j; struct buffer *mem = NULL; struct burn_toc_entry *e; /* ts A70519 : the code which needs this does not work with GNU/Linux 2.4 USB int lba; struct burn_read_opts o; 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; */ BURN_ALLOC_MEM_VOID(mem, struct buffer, 1); mem->bytes = 0; mem->sectors = 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; /* XXX | in the subcodes if appropriate! */ if (e && !(e->control & 4)) { t->mode = BURN_AUDIO; } else { t->mode = BURN_MODE1; /* ts A70519 : this does not work with GNU/Linux 2.4 USB because one cannot predict the exact dxfer_size without knowing the sector type. if (!e) lba = 0; else lba = burn_msf_to_lba(e->pmin, e->psec, e->pframe); mem->sectors = 1; ts B21119 : Would now be d->read_cd() with with sectype = 0 , mainch = 0xf8 d->read_sectors(d, lba, mem.sectors, &o, mem); t->mode = sector_identify(mem->data); */ } } ex: BURN_FREE_MEM(mem); } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Provided under GPL version 2 or later. */ #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*/ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2024 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifndef __TRANSPORT #define __TRANSPORT #include "libburn.h" #include "os.h" #include <pthread.h> /* sg data structures */ #include <sys/types.h> /* see os.h for name of particular os-*.h where this is defined */ #define BUFFER_SIZE BURN_OS_TRANSPORT_BUFFER_SIZE 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 { /* ts A61219: Added 4096 bytes reserve against possible buffer overflows. (Changed in sector.c buffer flush test from >= to > BUFFER_SIZE . This can at most cause a 1 sector overlap. Sometimes an offset of 16 byte is applied to the output data (in some RAW mode). ) burn_write_opts.cdxa_conversion can imply an offset of 8 bytes. */ unsigned char data[BUFFER_SIZE + 4096]; int sectors; int bytes; }; struct command { unsigned char opcode[16]; int oplen; int dir; int dxfer_len; unsigned char sense[128]; int sense_len; int error; int retry; struct buffer *page; int timeout; /* milliseconds */ double start_time; double end_time; int retry_count; int last_retry_key; int last_retry_asc; int last_retry_ascq; }; struct burn_scsi_inquiry_data { char peripheral; /* bit0-4: device type should be 5 bit5-7: qualifier must be 0 */ char version; /* should be 3 (SPC-1) to 5 (SPC-3) (or higher ?) but is often 0. */ char vendor[9]; char product[17]; char revision[5]; int valid; }; struct scsi_mode_data { int p2a_valid; 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 c2_pointers; int underrun_proof; int max_read_speed; int cur_read_speed; int max_write_speed; int cur_write_speed; /* ts A61021 */ int min_write_speed; /* ts A61225 : Results from ACh GET PERFORMANCE, Type 03h Speed values go into *_*_speed */ int min_end_lba; int max_end_lba; struct burn_speed_descriptor *speed_descriptors; int retry_page_length; int retry_page_valid; int write_page_length; int write_page_valid; }; /* ts A70112 : represents a single Formattable Capacity Descriptor as of mmc5r03c.pdf 6.24.3.3 . There can at most be 32 of them. */ struct burn_format_descr { /* format type: e.g 0x00 is "Full", 0x15 is "Quick" */ int type; /* the size in bytes derived from Number of Blocks */ off_t size; /* the Type Dependent Parameter (usually the write alignment size) */ unsigned int tdp; }; /* ts B40106 : represents a Feature Descriptor as of mmc5r03c.pdf 5.2.2 There can be many of them. Thus a linked list. */ struct burn_feature_descr { unsigned short feature_code; unsigned char flags; /* bit0= current bit1= persistent bit2-5= version */ unsigned char data_lenght; /* Additional bytes after the first 4 bytes of the descriptor */ unsigned char *data; struct burn_feature_descr *next; }; /** Gets initialized in enumerate_common() and burn_drive_register() */ struct burn_drive { /* ts A70902: 0=null-emulation 1=MMC drive , 2=stdio random read-write 3=stdio sequential write-only 4=stdio random read-only 5=stdio random write-only */ int drive_role; int bus_no; int host; int id; int channel; int lun; char *devname; /* ts A70302: mmc5r03c.pdf 5.3.2 Physical Interface Standard */ int phys_if_std; /* 1=SCSI, 2=ATAPI, 3,4,6=FireWire, 7=SATA, 8=USB */ char phys_if_name[80]; /* MMC-5 5.3.2 table 91 , e.g. "SCSI Family" */ /* see os.h for name of particular os-*.h where this is defined */ BURN_OS_TRANSPORT_DRIVE_ELEMENTS /* ts A60904 : ticket 62, contribution by elmom */ /** Tells the index in scanned burn_drive_info array. -1 if fallen victim to burn_drive_info_forget() */ int global_index; pthread_mutex_t access_lock; enum burn_disc_status status; int erasable; /* ts A61201 from 46h GET CONFIGURATION */ int current_profile; char current_profile_text[80]; int current_is_cd_profile; int current_is_supported_profile; /* ts A90603 */ int current_is_guessed_profile; /* ts A90815 */ unsigned char all_profiles[256]; int num_profiles; /* ts B40106 : All feature descriptors as read from drive */ struct burn_feature_descr *features; /* ts A70128 : MMC-to-MMC feature info from 46h for DVD-RW. Quite internal. Regard as opaque :) */ /* 1 = incremental recording available, 0 = not available */ int current_has_feat21h; /* Some drives announce feature 21h on fast-blanked DVD-RW although they cannot write them in Incremental mode. 0= does not look like the recent write run failed due to Incremental on fast blanked DVD-RW 1= it seems to have happened 2= it seems to have happened with write address 0 */ int was_feat21h_failure; /* Link Size item number 0 from feature 0021h descriptor */ int current_feat21h_link_size; /* Flags from feature 0023h for formatting BD mmc5r03c.pdf 5.3.13 Byte 4 BD-RE: bit0= Cert format 30h sub-type 10b bit1= QCert format 30h sub-type 11b bit2= Expand format 01h bit3= RENoSA format 31h Byte 8 BD-R: bit0= RRM format 32h sub-type 10b */ int current_feat23h_byte4; int current_feat23h_byte8; /* Flags from feature 002Fh feature descriptor mmc5r03c.pdf 5.3.25 : bit1= DVD-RW supported bit2= Test Write available bit3= DVD-R DL supported bit6= Buffer Under-run Free recording available (page 05h BUFE) Value -1 indicates that no 002Fh was current in the features list. */ int current_feat2fh_byte4; /* ts B51016 : Result from feature 108h : Drive Serial Number */ char *drive_serial_number; int drive_serial_number_len; /* ts B51016 : Result from command AB READ MEDIA SERIAL NUMBER */ char *media_serial_number; int media_serial_number_len; /* ts B10524 : whether the damage bit was set for the future track. bit0= damage bit , bit1= nwa valid bit */ int next_track_damaged; /* ts A70114 : whether a DVD-RW media holds an incomplete session (which could need closing after write) */ int needs_close_session; /* ts A71003 : whether a random write operation was done and no synchronize cache has happened yet */ int needs_sync_cache; /* ts A80412 : whether to use WRITE12 with Streaming bit set rather than WRITE10. Speeds up DVD-RAM. Might help with BD-RE */ int do_stream_recording; /* ts A90227 : the LBA where stream recording shall start. Writing to lower LBA will be done without streaming. */ int stream_recording_start; /* ts A61218 from 51h READ DISC INFORMATION */ int last_lead_in; int last_lead_out; int num_opc_tables; /* ts A91104: -1= not yet known */ int bg_format_status; /* 0=needs format start, 1=needs format restart*/ int disc_type; /* 0="CD-DA or CD-ROM", 0x10="CD-I", 0x20="CD-ROM XA" */ unsigned int disc_id; /* a "32 bit binary integer" */ char disc_bar_code[9]; int disc_app_code; int disc_info_valid; /* bit0= disc_type , bit1= disc_id , bit2= disc_bar_code , bit3= disc_app_code bit4= URU bit is set (= unrestricted use) bit5= Erasable bit was set in reply */ /* ts A70108 from 23h READ FORMAT CAPACITY mmc5r03c.pdf 6.24 */ int format_descr_type; /* 1=unformatted, 2=formatted, 3=unclear */ off_t format_curr_max_size; /* meaning depends on format_descr_type */ unsigned int format_curr_blsas; /* dito */ int best_format_type; off_t best_format_size; /* The complete list of format descriptors as read with 23h */ int num_format_descr; struct burn_format_descr format_descriptors[32]; volatile int released; /* ts A61106 */ /* 0= report errors 1= do not report errors 2= do not report errors which the libburn function indicates in member .had_particular_error 3= report errors with severity DEBUG */ int silent_on_scsi_error; /* ts B21023 */ /* bit0= 5 64 00 occurred with READ10 in mmc_read_10() */ int had_particular_error; int stdio_fd; off_t nwa; /* next writeable address */ int alba; /* absolute lba */ int rlba; /* relative lba in section */ int start_lba; int end_lba; /* ts B61116 */ int do_simulate; /* ts A70131 : from 51h READ DISC INFORMATION Number of Sessions (-1)*/ int complete_sessions; /* ts A90107 */ int state_of_last_session; #ifdef Libburn_disc_with_incomplete_sessioN /* ts B30112 */ int incomplete_sessions; #endif /* ts A70129 : from 51h READ DISC INFORMATION Last Track Number in Last Session */ int last_track_no; /* ts B10730 : whether a default mode page 05 was already sent. */ int sent_default_page_05; /* ts A70212 : from various sources : free space on media (in bytes) With CD this might change after particular write parameters have been set and nwa has been inquired. (e.g. by d->send_write_parameters() ; d->get_nwa()). */ off_t media_capacity_remaining; /* ts A70215 : if > 0 : first lba on media that is too high for write*/ int media_lba_limit; /* ts A81210 / C40303 : Upper limit of readable data size, 0x7fffffffffffffff = unknown BURN_DRIVE_MAX_BYTES / 2048 = possibly truncated or unknown stdio size */ off_t media_read_capacity; /* ts B60305 : Whether READ CAPACITY of CD is credible: -1= no READ CAPACITY yet , 0= untrusted READ CAPACITY 1= READ CAPACITY confirmed or corrected by other commands */ int mr_capacity_trusted; /* ts B10314 / C40302 : Next Writeable Address for drive_role == 5 */ off_t role_5_nwa; /* ts B60730 */ int do_no_immed; int toc_temp; struct burn_disc *disc; /* disc structure */ int block_types[4]; struct buffer *buffer; struct burn_progress_v2 progress; /* To be used by mmc.c, sbc.c, spc.c for SCSI commands where the struct content surely does not have to persist while another command gets composed and executed. (Inherently, sending SCSI commands to the same drive cannot be thread-safe. But there are functions which send SCSI commands and also call other such functions. These shall use own allocated command structs and not this struct here.) */ struct command casual_command; /* ts A70711 : keeping an eye on the drive buffer */ off_t pessimistic_buffer_free; int pbf_altered; int wait_for_buffer_free; int nominal_write_speed; unsigned int wfb_min_usec; unsigned int wfb_max_usec; unsigned int wfb_timeout_sec; unsigned int wfb_min_percent; unsigned int wfb_max_percent; unsigned int pessimistic_writes; unsigned int waited_writes; unsigned int waited_tries; unsigned int waited_usec; volatile int cancel; volatile enum burn_drive_status busy; /* During write runs, this points to a copy of the applied struct burn_write_opts. Only read this underneath burn_disc_write_sync() which removes the copy when done. Especially do not read it from outside the write thread. */ struct burn_write_opts *write_opts; /* ts A70929 */ pid_t thread_pid; int thread_pid_valid; /* ts B00225 */ pthread_t thread_tid; /* ts B90513 */ unsigned int write_retry_count; /* ts C00806 */ /* 0=no change, 1=change, -1=already urged OS to revalidate medium */ int medium_state_changed; /* ts C00822 */ /* If set, use Exact bit with SET STREAMING and use SET STREAMING even if the medium is a CD. */ int set_streaming_exact_bit; int set_streaming_err; /* transport functions */ int (*grab) (struct burn_drive *); int (*release) (struct burn_drive *); /* ts A61021 */ int (*drive_is_open) (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 *); /* ts A61021 */ void (*read_atip) (struct burn_drive *); int (*write) (struct burn_drive *, off_t, 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 *); int (*start_unit) (struct burn_drive *); /* ts A90824 : Calming down noisy drives */ int (*stop_unit) (struct burn_drive *); int is_stopped; void (*read_disc_info) (struct burn_drive *); int (*read_cd) (struct burn_drive *, int start, int len, int sec_type, int main_ch, const struct burn_read_opts *, struct buffer *, int flag); 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 *, struct burn_session *, int tno, const struct burn_write_opts *); int (*send_cue_sheet) (struct burn_drive *, struct cue_sheet *); /* ts A70205 : Announce size of a DVD-R[W] DAO session. */ int (*reserve_track) (struct burn_drive *d, off_t size); void (*sync_cache) (struct burn_drive *); int (*get_erase_progress) (struct burn_drive *); int (*get_nwa) (struct burn_drive *, int trackno, int *lba, int *nwa); /* ts A70131 : obtain (possibly fake) TOC number and start lba of first track in last complete session */ int (*read_multi_session_c1)(struct burn_drive *d, int *trackno, int *start); /* ts A61009 : removed d in favor of o->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); */ void (*close_disc) (struct burn_write_opts * o); void (*close_session) ( struct burn_write_opts * o); /* ts A61029 */ void (*close_track_session) ( struct burn_drive *d, int session, int track); int (*test_unit_ready) (struct burn_drive * d); void (*probe_write_modes) (struct burn_drive * d); struct params params; struct burn_scsi_inquiry_data *idata; struct scsi_mode_data *mdata; int toc_entries; struct burn_toc_entry *toc_entry; /* ts A61023 : get size and free space of drive buffer */ int (*read_buffer_capacity) (struct burn_drive *d); /* ts A61220 : format media (e.g. DVD+RW) */ int (*format_unit) (struct burn_drive *d, off_t size, int flag); /* ts A70108 */ /* mmc5r03c.pdf 6.24 : get list of available formats */ int (*read_format_capacities) (struct burn_drive *d, int top_wanted); /* ts A70812 */ /* mmc5r03c.pdf 6.15 : read data sectors (start and amount in LBA) */ int (*read_10) (struct burn_drive *d, int start, int amount, struct buffer *buf); }; /* end of generic 'drive' data structures */ /* ts A80422 : centralizing this setting for debugging purposes */ int burn_drive_set_media_capacity_remaining(struct burn_drive *d, off_t value); #endif /* __TRANSPORT */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2013 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <string.h> /* ts A61008 */ /* #include <a ssert.h> */ #include <stdlib.h> #include <stdio.h> #include <time.h> #include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> /* ts A80914 : This is unneeded. Version info comes from libburn.h. #include "v ersion.h" */ #include "util.h" #include "libburn.h" void burn_version(int *major, int *minor, int *micro) { /* ts A80408 : switched from configure.ac versioning to libburn.h versioning */ *major = burn_header_version_major; *minor = burn_header_version_minor; *micro = burn_header_version_micro; } struct cd_mid_record { char *manufacturer; int m_li; int s_li; int f_li; char *other_brands; }; typedef struct cd_mid_record cd_mid_record_t; /* ts A90902 */ /** API @param flag Bitfield for control purposes, bit0= append "(aka %s)",other_brands to reply */ char *burn_guess_cd_manufacturer(int m_li, int s_li, int f_li, int m_lo, int s_lo, int f_lo, int flag) { static cd_mid_record_t mid_list[]= { {"SKC", 96, 40, 0, ""}, {"Ritek Corp" , 96, 43, 30, ""}, {"TDK / Ritek" , 97, 10, 0, "TRAXDATA"}, {"TDK Corporation" , 97, 15, 0, ""}, {"Ritek Corp" , 97, 15, 10, "7-plus, Aopen, PONY, Power Source, TDK, TRAXDATA, HiCO, PHILIPS, Primdisc, Victor.JVC, OPTI STORAGE, Samsung"}, {"Mitsubishi Chemical Corporation" , 97, 15, 20, ""}, {"Nan-Ya Plastics Corporation" , 97, 15, 30, "Hatron, MMore, Acer, LITEON"}, {"Delphi" , 97, 15, 50, ""}, {"Shenzhen SG&SAST" , 97, 16, 20, ""}, {"Moser Baer India Limited" , 97, 17, 0, "EMTEC, Intenso, YAKUMO, PLATINUM, Silver Circle"}, {"SKY media Manufacturing SA" , 97, 17, 10, ""}, {"Wing" , 97, 18, 10, ""}, {"DDT" , 97, 18, 20, ""}, {"Daxon Technology Inc. / Acer" , 97, 22, 60, "Maxmax, Diamond Data, BenQ, gold, SONY"}, {"Taiyo Yuden Company Limited" , 97, 24, 0, "Maxell, FUJIFILM, SONY"}, {"Sony Corporation" , 97, 24, 10, "LeadData, Imation"}, {"Computer Support Italcard s.r.l" , 97, 24, 20, ""}, {"Unitech Japan Inc." , 97, 24, 30, ""}, {"MPO, France" , 97, 25, 0, "TDK"}, {"Hitachi Maxell Ltd." , 97, 25, 20, ""}, {"Infodisc Technology Co,Ltd." , 97, 25, 30, "MEMOREX, SPEEDA, Lead data"}, {"Xcitec" , 97, 25, 60, ""}, {"Fornet International Pte Ltd" , 97, 26, 0, "COMPUSA, Cdhouse"}, {"Postech Corporation" , 97, 26, 10, "Mr.Platinum"}, {"SKC Co Ltd." , 97, 26, 20, "Infinite"}, {"Fuji Photo Film Co,Ltd." , 97, 26, 40, ""}, {"Lead Data Inc." , 97, 26, 50, "SONY, Gigastorage, MIRAGE"}, {"CMC Magnetics Corporation" , 97, 26, 60, "Daxon, Verbatim, Memorex, Bi-Winner, PLEXTOR, YAMAHA, Melody, Office DEPOT, Philips, eMARK, imation, HyperMedia, Samsung, Shintaro, Techworks"}, {"Ricoh Company Limited" , 97, 27, 0, "Sony, Digital Storage, Csita"}, {"Plasmon Data Systems Ltd" , 97, 27, 10, "Ritek, TDK, EMTEC, ALPHAPET, MANIA"}, {"Princo Corporation" , 97, 27, 20, ""}, {"Pioneer" , 97, 27, 30, ""}, {"Eastman Kodak Company" , 97, 27, 40, ""}, {"Mitsui Chemicals Inc." , 97, 27, 50, "MAM-A, TDK"}, {"Ricoh Company Limited" , 97, 27, 60, "Ritek"}, {"Gigastorage Corporation" , 97, 28, 10, "MaxMax, Nan-Ya"}, {"Multi Media Masters&Machinary SA" , 97, 28, 20, "King, Mmirex"}, {"Ritek Corp" , 97, 31, 0, "TDK"}, {"Grand Advance Technology Sdn. Bhd." , 97, 31, 30, ""}, {"TDK Corporation" , 97, 32, 00, ""}, {"Prodisc Technology Inc." , 97, 32, 10, "Smartbuy, Mitsubishi, Digmaster, LG, Media Market"}, {"Mitsubishi Chemical Corporation" , 97, 34, 20, "YAMAHA, Verbatim"}, {"Mitsui Chemicals Inc." , 97, 48, 50, ""}, {"TDK Corporation" , 97, 49, 0, ""}, {"", 0, 0, 0, ""} }; int i, f_li_0; char buf[1024]; char *result = NULL; if (m_li == 0 && s_li == 2 && f_li == 0) { result = strdup("(no manufacturer code)"); return result; } f_li_0 = f_li - (f_li % 10); for (i = 0; mid_list[i].manufacturer[0]; i++) { if (m_li == mid_list[i].m_li && s_li == mid_list[i].s_li && (f_li_0 == mid_list[i].f_li || f_li == mid_list[i].f_li)) break; } if (mid_list[i].manufacturer[0] == 0) { sprintf(buf, "Unknown CD manufacturer. Please report code '%2.2dm%2.2ds%2.2df/%2.2dm%2.2ds%2.2df', the human readable brand, size, and speed to scdbackup@gmx.net.", m_li, s_li, f_li, m_lo, s_lo, f_lo); result = strdup(buf); return result; } /* Compose, allocate and copy result */ if ((flag & 1) && mid_list[i].other_brands[0]) { sprintf(buf, "%s (aka %s)", mid_list[i].manufacturer, mid_list[i].other_brands); result = strdup(buf); } else result = strdup(mid_list[i].manufacturer); return result; } /* ts A90904 */ struct dvd_mid_record { char *mc1; int mc1_sig_len; char *manufacturer; }; typedef struct dvd_mid_record dvd_mid_record_t; /* ts A90904 */ char *burn_guess_manufacturer(int prf, char *media_code1, char *media_code2, int flag) { int i, l = 0, m_li, s_li, f_li, m_lo, s_lo, f_lo; char buf[1024]; char *result = NULL, *cpt; /* Important Note: media_code1 and media_code2 are supposed to be encoded by burn_util_make_printable_word(). Especially: ' ' -> '_' , {"_%/" unprintables -> %XY) */ static dvd_mid_record_t mid_list[]= { {"AML", 8, "UML"}, {"BeAll", 5, "BeAll Developers, Inc."}, {"CMC", 3, "CMC Magnetics Corporation"}, {"DAXON", 5, "Daxon Technology Inc. / Acer"}, {"Daxon", 5, "Daxon Technology Inc. / Acer"}, {"FUJI", 4, "Fujifilm Holdings Corporation"}, {"INFODISC", 8, "New Star Digital Co., Ltd."}, {"INFOME", 6, "InfoMedia Inc."}, {"ISMMBD", 6, "Info Source Multi Media Ltd."}, {"JVC", 3, "JVC Limited"}, {"KIC01RG", 7, "AMC"}, {"LD", 8, "Lead Data Inc."}, {"LGE", 3, "LG Electronics"}, {"MAM", 8, "Mitsui Advanced Media, Inc. Europe"}, {"MAXELL", 6, "Hitachi Maxell Ltd."}, {"MBI", 3, "Moser Baer India Limited"}, {"MCC", 8, "Mitsubishi Chemical Corporation"}, {"MCI", 8, "Mitsui Chemicals Inc."}, {"MEI", 3, "Panasonic Corporation"}, {"MILLEN", 8, "Millenniata Inc."}, {"MKM", 3, "Mitsubishi Kagaku Media Co."}, {"MMC", 8, "Mitsubishi Kagaku Media Co."}, {"MXL", 8, "Hitachi Maxell Ltd."}, {"NANYA", 5, "Nan-Ya Plastics Corporation"}, {"NSD", 8, "NESA International Inc."}, {"OPTODISC", 8, "Optodisc Technology Corporation"}, {"OTCBDR", 8, "Optodisc Technology Corporation"}, {"PHILIP", 8, "Moser Baer India Limited"}, {"PHILIPS", 8, "Philips"}, {"PRINCO", 6, "Princo Corporation"}, {"PRODISC", 7, "Prodisc Technology Inc."}, {"Prodisc", 7, "Prodisc Technology Inc."}, {"PVC", 3, "Pioneer"}, {"RICOHJPN", 8, "Ricoh Company Limited"}, {"RITEK", 5, "Ritek Corp"}, {"SONY", 4, "Sony Corporation"}, {"TDK", 3, "TDK Corporation"}, {"TTG", 3, "TDK Corporation"}, {"TTH", 3, "TDK Corporation"}, {"TY", 8, "Taiyo Yuden Company Limited"}, {"TYG", 3, "Taiyo Yuden Company Limited"}, {"UME", 3, "UmeDisc Limited"}, {"UTJR001", 7, "Unifino Inc."}, {"VERBAT", 5, "Mitsubishi Kagaku Media Co."}, {"YUDEN", 5, "Taiyo Yuden Company Limited"}, {"", 0, ""} }; if (media_code2 != NULL && (prf == -1 || prf == 0x09 || prf == 0x0A)) { if (strlen(media_code2) == 9 && media_code1[0] == '9' && media_code1[2] == 'm' && media_code1[5] == 's' && media_code1[8] == 'f' && strchr(media_code1, '%') == NULL) { sscanf(media_code1, "%dm%ds%df", &m_li, &s_li, &f_li); sscanf(media_code2, "%dm%ds%df", &m_lo, &s_lo, &f_lo); if (m_li >= 96 && m_li <= 97 && m_lo > 0) { result = burn_guess_cd_manufacturer( m_li, s_li, f_li, m_lo, s_lo, f_lo, 0); return result; } } } /* DVD-R do not keep manufacturer id apart from media id. Some manufacturers use a blank as separator which would now be '_'. */ cpt = strchr(media_code1, '_'); if (cpt != NULL && (prf == -1 || prf == 0x11 || prf == 0x13 || prf == 0x14 || prf == 0x15)) l = cpt - media_code1; for (i = 0; mid_list[i].mc1[0]; i++) { if (strncmp(mid_list[i].mc1, media_code1, mid_list[i].mc1_sig_len) == 0) break; if (l > 0) if (strncmp(mid_list[i].mc1, media_code1, l) == 0) break; } if (mid_list[i].mc1[0] == 0) { sprintf(buf, "Unknown DVD/BD manufacturer. Please report code '%s/%s', the human readable brand, size, and speed to scdbackup@gmx.net.", media_code1, media_code2); result = strdup(buf); return result; } result = strdup(mid_list[i].manufacturer); return result; } /* ts A90905 */ /* Make *text a single printable word */ /* IMPORTANT: text must be freeable memory ! @param flag bit0=escape '/' too bit1=(overrides bit0) do not escape " _/" */ int burn_util_make_printable_word(char **text, int flag) { int i, esc_add = 0, ret; char *wpt, *rpt, *new_text = NULL; if (flag & 2) flag &= ~1; for (i = 0; (*text)[i]; i++) { rpt = (*text) + i; if (*rpt < 32 || *rpt > 126 || *rpt == 96 || ((*rpt == '_' || *rpt == '%') && (!(flag & 2))) || (*rpt == '/' && (flag & 1))) esc_add += 2; } if (esc_add) { new_text = calloc(strlen(*text) + esc_add + 1, 1); if (new_text == NULL) { ret = -1; goto ex; } wpt = new_text; for (i = 0; (*text)[i]; i++) { rpt = (*text) + i; if (*rpt < 32 || *rpt > 126 || *rpt == 96 || ((*rpt == '_' || *rpt == '%') && (!(flag & 2))) || (*rpt == '/' && (flag & 1))) { sprintf(wpt, "%%%2.2X", (unsigned int) *((unsigned char *) rpt)); wpt+= 3; } else *(wpt++) = *rpt; } *wpt = 0; free(*text); *text = new_text; } if (!(flag & 2)) for (i = 0; (*text)[i]; i++) if ((*text)[i] == ' ') (*text)[i] = '_'; ret = 1; ex: return ret; } /* ts B11216 */ /** Read a line from fp and strip LF or CRLF */ char *burn_sfile_fgets(char *line, int maxl, FILE *fp) { int l; char *ret; ret = fgets(line, maxl, fp); if (ret == NULL) return NULL; l = strlen(line); if (l > 0) if (line[l - 1] == '\r') line[--l] = 0; if (l > 0) if (line[l - 1] == '\n') line[--l] = 0; if(l > 0) if(line[l - 1] == '\r') line[--l] = 0; return ret; } char *burn_printify(char *msg) { char *cpt; for (cpt = msg; *cpt != 0; cpt++) if (*cpt < 32 || *cpt > 126) *cpt = '#'; return msg; } /* ts B30521 */ void burn_int_to_lsb(int val, char *target) { unsigned char *buf; buf = (unsigned char *) target; buf[0] = val & 0xff; buf[1] = (val >> 8) & 0xff; buf[2] = (val >> 16) & 0xff; buf[3] = (val >> 24) & 0xff; } /* ts B30609 */ double burn_get_time(int flag) { int ret; struct timeval tv; #ifdef Libburn_use_clock_gettime_monotoniC #ifdef _POSIX_TIMERS #ifdef _POSIX_MONOTONIC_CLOCK /* Enable by export CFLAGS=-DLibburn_use_clock_gettime_monotoniC export LIBS=-lrt ./configure ... && make clean && make */ struct timespec tp; ret = clock_gettime(CLOCK_MONOTONIC, &tp); if (ret == 0) return ((double) tp.tv_sec) + ((double) tp.tv_nsec) * 1.0e-9; #endif /* _POSIX_MONOTONIC_CLOCK */ #endif /* _POSIX_TIMERS */ #endif /* Xorriso_use_clock_gettime_monotoniC */ ret = gettimeofday(&tv, NULL); if (ret == 0) return ((double) tv.tv_sec) + ((double) tv.tv_usec) * 1.0e-6; return (double) time(NULL); } /* ts B40609 */ off_t burn_sparse_file_addsize(off_t write_start, struct stat *stbuf) { off_t add_size; add_size = stbuf->st_blocks * (off_t) 512; if (add_size < stbuf->st_size) { /* Sparse file */ if (write_start < stbuf->st_size) { /* Might write into sparse gaps */ if (write_start > add_size) add_size = write_start; } else { /* Will not write into sparse area */ add_size = stbuf->st_size; } } return add_size; } #ifndef __UTIL #define __UTIL /* for struct stat */ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> /* ts A90905 */ int burn_util_make_printable_word(char **text, int flag); /* ts B11216 */ char *burn_sfile_fgets(char *line, int maxl, FILE *fp); char *burn_printify(char *msg); /* ts B30521 */ void burn_int_to_lsb(int val, char *target); /* ts B30609 */ double burn_get_time(int flag); /* ts B40609 */ off_t burn_sparse_file_addsize(off_t write_start, struct stat *stbuf); #endif /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2024 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <unistd.h> #include <signal.h> /* ts A61009 */ /* #include <a ssert.h> */ /* ts A61106 : Deliberate defect provocation macros DO NOT DEFINE THESE IF YOU WANT SUCCESSFUL TAO ! #define Libburn_experimental_no_close_tracK 1 #define Libburn_experimental_no_close_sessioN 1 */ /* ts A61114 : Highly experimental : try to achieve SAO on appendables THIS DOES NOT WORK YET ! #define Libburn_sao_can_appenD 1 */ #include <sys/types.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <sys/stat.h> #include <sys/time.h> /* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #include "error.h" #include "sector.h" #include "libburn.h" #include "drive.h" #include "transport.h" #include "debug.h" #include "init.h" #include "toc.h" #include "util.h" #include "sg.h" #include "write.h" #include "options.h" #include "structure.h" #include "source.h" #include "mmc.h" #include "spc.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; /* ts A91120 : <<< experimental */ #ifdef Libburn_mmap_write_buffeR #include <sys/mman.h> #endif /* The maximum output size to be used with CD media. This is also curbed by BURN_OS_TRANSPORT_BUFFER_SIZE. The smaller number gets into effect. */ #define Libburn_cd_obS (32 * 1024) /* The size to be used with DVD media. */ #define Libburn_dvd_obS (32 * 1024) /* The size to be used with BD-RE media in normal, not streamed mode. */ #define Libburn_bd_re_obS (64 * 1024) /* The size to be used with BD-R media in normal, not streamed mode. */ #define Libburn_bd_r_obS (64 * 1024) /* The size to be used with BD-RE and BD-R media in streamed mode. */ #define Libburn_bd_streamed_obS (64 * 1024) /* The number of retries if write(2) returns a short, non-negative write count. */ #define Libburn_stdio_write_retrieS 16 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 /* ts A61008 */ /* a ssert(0); */ return -1; if (mode & BURN_COPY) ctrl |= 2; return ctrl; } /* only the ctrl nibble is set here (not adr) */ /* ts A61009 : removed "static" , reacted on type_to_ctrl() == -1 preserved ignorance towards unknown modes (for now) */ void type_to_form(int mode, unsigned char *ctladr, int *form) { int ret; ret = type_to_ctrl(mode) << 4; if (ret == -1) { *ctladr = 0xff; *form = -1; return; } *ctladr = ret; if (mode & BURN_AUDIO) *form = 0; if (mode & BURN_MODE0) { /* ts A61009 */ /* a ssert(0); */ *form = -1; return; } if (mode & BURN_MODE1) *form = 0x10; if (mode & BURN_MODE2) { /* ts A61009 */ /* a ssert(0); */ /* XXX someone's gonna want this sometime */ *form = -1; return; } 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; } /* ts A71002 : outsourced from burn_write_flush() : no sync cache here */ int burn_write_flush_buffer(struct burn_write_opts *o,struct burn_track *track) { 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; /* A61101 */ if(track != NULL) { track->writecount += d->buffer->bytes; track->written_sectors += d->buffer->sectors; } /* ts A61119 */ d->progress.buffered_bytes += d->buffer->bytes; d->nwa += d->buffer->sectors; d->buffer->bytes = 0; d->buffer->sectors = 0; } return 1; } int burn_write_flush(struct burn_write_opts *o, struct burn_track *track) { int ret; struct burn_drive *d = o->drive; ret = burn_write_flush_buffer(o, track); if (ret <= 0) return ret; d->sync_cache(d); return 1; } /* ts A71002 : outsourced from burn_write_close_track() */ int burn_write_track_minsize(struct burn_write_opts *o, struct burn_session *s, int tnum) { char msg[81]; struct burn_drive *d; struct burn_track *t; int todo, step, cancelled, seclen; d = o->drive; t = s->track[tnum]; /* ts A61103 : pad up track to minimum size of 600 sectors */ if (t->written_sectors < 300) { todo = 300 - t->written_sectors; sprintf(msg,"Padding up track to minimum size (+ %d sectors)", todo); libdax_msgs_submit(libdax_messenger, o->drive->global_index, 0x0002011a, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg,0,0); step = BUFFER_SIZE / 4096; /* shall fit any sector size */ seclen = burn_sector_length(t->mode); if (seclen <= 0) seclen = 2048; memset(d->buffer, 0, sizeof(struct buffer)); cancelled = d->cancel; for (; todo > 0; todo -= step) { if (step > todo) step = todo; d->buffer->bytes = step*seclen; d->buffer->sectors = step; d->cancel = 0; d->write(d, d->nwa, d->buffer); d->nwa += d->buffer->sectors; t->writecount += d->buffer->bytes; t->written_sectors += d->buffer->sectors; d->progress.buffered_bytes += d->buffer->bytes; } d->cancel = cancelled; } return 1; } /* ts A61030 */ int burn_write_close_track(struct burn_write_opts *o, struct burn_session *s, int tnum) { char msg[81]; struct burn_drive *d; /* ts A61106 */ #ifdef Libburn_experimental_no_close_tracK return 1; #endif d = o->drive; d->busy = BURN_DRIVE_CLOSING_TRACK; sprintf(msg, "Closing track %2.2d", tnum+1); libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0); /* MMC-1 mentions track number 0xFF for "the incomplete track", MMC-3 does not. I tried both. 0xFF was in effect when other bugs finally gave up and made way for readable tracks. */ /* ts A70129 Probably the right value for appendables is d->last_track_no */ d->close_track_session(o->drive, 0, 0xff); d->busy = BURN_DRIVE_WRITING; return 1; } /* ts A61030 */ int burn_write_close_session(struct burn_write_opts *o) { /* ts A61106 */ #ifdef Libburn_experimental_no_close_sessioN return 1; #endif libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "Closing session", 0, 0); /* ts A61102 */ o->drive->busy = BURN_DRIVE_CLOSING_SESSION; o->drive->close_track_session(o->drive, 1, 0); /* ts A61102 */ o->drive->busy = BURN_DRIVE_WRITING; return 1; } /* ts A60819, B20101: This is useful only when changes about CD SAO get tested. # define Libburn_write_with_function_print_cuE yes */ #ifdef Libburn_write_with_function_print_cuE static char cue_printify(char c) { if (c >= 32 && c < 127) return c; return '#'; } static void print_cue(struct cue_sheet *sheet) { int i; unsigned char *unit; printf("\n"); printf("ctladr|trno|indx|form|scms| msf | text\n"); printf("------+----+----+----+----+----------+--------\n"); for (i = 0; i < sheet->count; i++) { unit = sheet->data + 8 * i; if ((unit[0] & 0xf) == 2) { printf( " %1X %1X | | | | | | %c%c%c%c%c%c%c\n", (unit[0] & 0xf0) >> 4, unit[0] & 0xf, cue_printify(unit[1]), cue_printify(unit[2]), cue_printify(unit[3]), cue_printify(unit[4]), cue_printify(unit[5]), cue_printify(unit[6]), unit[7] == 0 ? ' ' : cue_printify(unit[7])); } else if ((unit[0] & 0xf) == 3) { printf( " %1X %1X | %2d | | | | | %c%c%c%c%c%c\n", (unit[0] & 0xf0) >> 4, unit[0] & 0xf, unit[1], cue_printify(unit[2]), cue_printify(unit[3]), cue_printify(unit[4]), cue_printify(unit[5]), cue_printify(unit[6]), cue_printify(unit[7])); } else if (unit[1] > 99) { printf(" %1X %1X |0x%02X| %2d | %02X | %02X |", (unit[0] & 0xf0) >> 4, unit[0] & 0xf, unit[1], unit[2], unit[3], unit[4]); printf(" %02d:%02d:%02d |\n", unit[5], unit[6], unit[7]); } else { printf(" %1X %1X | %2d | %2d | %02X | %02X |", (unit[0] & 0xf0) >> 4, unit[0] & 0xf, unit[1], unit[2], unit[3], unit[4]); printf(" %02d:%02d:%02d |\n", unit[5], unit[6], unit[7]); } } fflush(stdout); } #endif /* Libburn_write_with_print_cuE */ /* ts B11226 */ static int new_cue(struct cue_sheet *sheet, int number, int flag) { unsigned char *ptr; ptr = realloc(sheet->data, (sheet->count + number) * 8); if (ptr == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00020111, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Could not allocate new auxiliary object (cue_sheet->data)", 0, 0); return -1; } sheet->data = ptr; sheet->count += number; return 1; } /* ts B11226 : outsourced new_cue() */ /** @return 1 = success , <=0 failure */ static int 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; int m, s, f, ret; burn_lba_to_msf(lba, &m, &s, &f); ret = new_cue(sheet, 1, 0); if (ret <= 0) return -1; 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; return 1; } /* ts B11226 */ static int add_catalog_cue(struct cue_sheet *sheet, unsigned char catalog[13]) { unsigned char *unit; int i, ret; ret = new_cue(sheet, 2, 0); if (ret <= 0) return -1; unit = sheet->data + (sheet->count - 2) * 8; unit[0] = unit[8] = 0x02; for (i = 0; i < 13; i++) unit[1 + (i >= 7) * 8 + (i % 7)] = catalog[i]; unit[15] = 0x00; return 1; } /* ts B11226 */ static int add_isrc_cue(struct cue_sheet *sheet, unsigned char ctladr, int tno, struct isrc *isrc) { unsigned char *unit; int i, ret; char text[8 + 21]; /* should suffice for 64 bit oversize */ ret = new_cue(sheet, 2, 0); if (ret <= 0) return -1; unit = sheet->data + (sheet->count - 2) * 8; unit[0] = unit[8] = (ctladr & 0xf0) | 0x03; unit[1] = unit[9] = tno; unit[2] = isrc->country[0]; unit[3] = isrc->country[1]; unit[4] = isrc->owner[0]; unit[5] = isrc->owner[1]; unit[6] = isrc->owner[2]; sprintf(text, "%-2.2u%-5.5u", (unsigned int) isrc->year, isrc->serial); sprintf(text, "%-2.2u", (unsigned int) isrc->year); sprintf(text + 2, "%-5.5u", isrc->serial); text[7] = 0; unit[7] = text[0]; for (i = 1; i < 7; i++) unit[9 + i] = text[i]; return 1; } /* ts A61114: added parameter nwa */ struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o, struct burn_session *session, int nwa) { int i, m, s, f, form, runtime = -150, ret, track_length; int leadin_form, leadin_start, pregap = 150, postgap; unsigned char ctladr, scms; 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; #define Libburn_track_multi_indeX yes #ifdef Libburn_track_multi_indeX int j; #else int pform; #endif if (ntr < 1) { libdax_msgs_submit(libdax_messenger, -1, 0x0002019c, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Session has no defined tracks", 0, 0); return NULL; } d = o->drive; #ifdef Libburn_sao_can_appenD if (d->status == BURN_DISC_APPENDABLE) runtime = nwa-150; #endif sheet = calloc(1, sizeof(struct cue_sheet)); /* ts A61009 : react on failures of calloc(), add_cue_sheet() type_to_form() */ if (sheet == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00020111, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Could not allocate new auxiliary object (cue_sheet)", 0, 0); return NULL; } sheet->data = NULL; sheet->count = 0; type_to_form(tar[0]->mode, &ctladr, &form); if (form == -1) { libdax_msgs_submit(libdax_messenger, -1, 0x00020116, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Track mode has unusable value", 0, 0); goto failed; } if (tar[0]->mode & BURN_AUDIO) leadin_form = 0x01; else leadin_form = 0x14; if (o->num_text_packs > 0) { leadin_form |= 0x40; } else { /* Check for CD-TEXT in session. Not the final creation, because the cue sheet content might be needed for CD-TEXT pack type 0x88 "TOC". */ if (o->text_packs == NULL) { ret = burn_cdtext_from_session(session, NULL, NULL, 1); if (ret < 0) goto failed; else if (ret > 0) leadin_form |= 0x40; } } if (o->has_mediacatalog) ret = add_catalog_cue(sheet, o->mediacatalog); else if (session->mediacatalog[0]) ret = add_catalog_cue(sheet, session->mediacatalog); else ret = 1; if (ret <= 0) goto failed; /* ts B11225 MMC-5 6.33.3.15 Data Form of Sub-channel seems to indicate that for leadin_form 0x41 one should announce d->start_lba as start of the leadin (e.g. -12490) and that data block type should 2 or 3 with mode page 05h. But my drives refuse on that. It works with LBA -150 and data block type 0. Shrug. */ leadin_start = runtime; ret = add_cue(sheet, (ctladr & 64) | 1, 0, 0, leadin_form, 0, leadin_start); if (ret <= 0) goto failed; d->toc_entries = ntr + 3; /* ts A61009 */ /* a ssert(d->toc_entry == NULL); */ if (d->toc_entry != NULL) { /* ts A61109 : this happens with appendable CDs >>> Open question: is the existing TOC needed ? */ /* ts A61109 : for non-SAO, this sheet is thrown away later */ free((char *) d->toc_entry); /* libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020117, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "toc_entry of drive is already in use", 0, 0); goto failed; */ } if (session->firsttrack + ntr - 1 > 99) { libdax_msgs_submit(libdax_messenger, -1, 0x0002019b, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "CD track number exceeds 99", 0, 0); goto failed; } session->lasttrack = session->firsttrack + ntr - 1; d->toc_entry = calloc(d->toc_entries, sizeof(struct burn_toc_entry)); e = d->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 = session->firsttrack; e[0].psec = o->format; e[0].adr = 1; e[1].point = 0xA1; e[1].pmin = session->lasttrack; 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; if (tar[0]->pregap2_size < 150) tar[0]->pregap2_size = 150; #ifndef Libburn_track_multi_indeX pform = form; #endif for (i = 0; i < ntr; i++) { /* ts A70125 : Still not understanding the sense behind linking tracks, i decided to at least enforce the MMC specs' minimum track length. */ track_length = burn_track_get_sectors_2(tar[i], 1); if (track_length < 0) goto failed; if (track_length < 300 && !burn_track_is_open_ended(tar[i])) { track_length = 300; if (!tar[i]->pad) tar[i]->pad = 1; burn_track_set_sectors(tar[i], (off_t) track_length); } type_to_form(tar[i]->mode, &ctladr, &form); if (tar[i]->mode & BURN_SCMS) scms = 0x80; else scms = 0; if (tar[i]->isrc.has_isrc) { ret = add_isrc_cue(sheet, ctladr, i + session->firsttrack, &(tar[i]->isrc)); if (ret <= 0) goto failed; } pregap = 0; if (tar[i]->pregap2) pregap = tar[i]->pregap2_size; postgap = 0; if (tar[i]->postgap) { if (tar[i]->indices >= 99) { libdax_msgs_submit(libdax_messenger, -1, 0x0002019a, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Post-gap index number exceeds 99", 0, 0); goto failed; } if (tar[i]->indices < 2) tar[i]->indices = 2; tar[i]->index[tar[i]->indices] = track_length; postgap = tar[i]->postgap_size; } #ifdef Libburn_track_multi_indeX for(j = 0; j < (tar[i]->indices + !!tar[i]->postgap) || j < 2; j++) { if(tar[i]->index[j] == 0x7fffffff) { if (j > 1) break; if (j == 0 && pregap <= 0) continue; /* force existence of mandatory index */ tar[i]->index[j] = 0; } else if (j == 0) { tar[i]->index[j] = 0; } else if (j == 1 && tar[i]->index[0] == 0x7fffffff) { tar[i]->index[j] = 0; } if (j == 1) { tar[i]->entry = &e[3 + i]; e[3 + i].point = i + session->firsttrack; 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); } /* >>> ??? else if j == 0 && mode change to -data : Extended pregap */; /* >>> check index with track size */; tar[i]->index[j] += runtime; ret = add_cue(sheet, ctladr | 1, i + session->firsttrack, j, form, scms, tar[i]->index[j]); if (ret <= 0) goto failed; runtime += pregap; pregap = 0; } runtime += track_length + postgap; #else /* Libburn_track_multi_indeX */ if (i == 0) { ret = add_cue(sheet, ctladr | 1, session->firsttrack, 0, form, 0, runtime); if (ret <= 0) goto failed; runtime += 150; } else if (pform != form) { /* ts A70121 : This seems to be the wrong test. Correct would be to compare tar[]->mode or bit2 of ctladr. */ ret = add_cue(sheet, ctladr | 1, i + session->firsttrack, 0, form, scms, runtime); if (ret <= 0) goto failed; runtime += 150; /* XXX fix pregap interval 1 for data tracks */ /* ts A60813 silence righteous compiler warning about C++ style comments This is possibly not a comment but rather a trace of Derek Foreman experiments. Thus not to be beautified - but to be preserved rectified. / / if (!(form & BURN_AUDIO)) / / tar[i]->pregap1 = 1; */ /* ts A70121 : it is unclear why (form & BURN_AUDIO) should prevent pregap1. I believe, correct would be: runtime += 75; tar[i]->pregap1 = 1; The test for pform != form is wrong anyway. Next one has to care for Post-gap: table 555 in mmc5r03c.pdf does not show any although 6.33.3.19 would prescribe some. ts B20111: Table 1 of MMC-1 shows two post-gaps. The first matches the precriptions with SEND CUE SHEET. The second one is riddling. Both are part of a track and occupy the range of the last index of the track. Length is 2 seconds for each. Nobody seems to have ever tested this situation, up to now. It is banned for now in burn_disc_write(). Warning have been placed in libburn.h . */ 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 + session->firsttrack; 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); ret = add_cue(sheet, ctladr | 1, i + session->firsttrack, 1, form, scms, runtime); if (ret <= 0) goto failed; runtime += track_length; #endif /* ! Libburn_track_multi_indeX */ /* 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]); /* ts A61101 : I doubt that linking would yield a desirable effect. With TAO it is counterproductive in any way. */ if (o->write_type == BURN_WRITE_TAO) tar[i]->source->next = NULL; else 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--; } #ifndef Libburn_track_multi_indeX pform = form; #endif } burn_lba_to_msf(runtime, &m, &s, &f); e[2].pmin = m; e[2].psec = s; e[2].pframe = f; ret = add_cue(sheet, ctladr | 1, 0xAA, 1, leadin_form & 0x3f, 0, runtime); if (ret <= 0) goto failed; return sheet; failed:; if (sheet != NULL) free((char *) sheet); return NULL; } 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; /* ts A61009 */ /* a ssert(0); */ return -1; } 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; 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; 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; } static int burn_create_text_packs(struct burn_write_opts *o, struct burn_session *s, int flag) { int ret, num_packs = 0; unsigned char *text_packs = NULL; ret = burn_cdtext_from_session(s, &text_packs, &num_packs, 0); if (ret > 0) { if (o->text_packs != NULL) free(o->text_packs); o->text_packs = text_packs; o->num_text_packs = num_packs; } return(ret); } static int burn_write_leadin_cdtext(struct burn_write_opts *o, struct burn_session *s, int flag) { int ret, i, j, si, lba, sub_cursor = 0, err, write_lba, sectors = 0; int self_made_text_packs = 0; unsigned char *subdata = NULL; struct burn_drive *d = o->drive; struct buffer *buf = NULL; enum burn_drive_status was_busy = o->drive->busy; #ifdef Libburn_debug_cd_texT unsigned char *packs; #endif if (o->num_text_packs <= 0) { if (o->text_packs != NULL) {ret = 1; goto ex;} /* Try to create CD-TEXT from .cdtext_* of session and track */ ret = burn_create_text_packs(o, s, 0); self_made_text_packs = 1; if (ret <= 0) goto ex; if (o->num_text_packs <= 0) {ret = 1; goto ex;} } if (!o->no_text_pack_crc_check) { ret = burn_cdtext_crc_mismatches(o->text_packs, o->num_text_packs, 0); if (ret != 0) { libdax_msgs_submit(libdax_messenger, -1, 0x0002018f, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Program error: CD-TEXT pack CRC mismatch", 0, 0); { ret = 0; goto ex; } } } d->busy = BURN_DRIVE_WRITING_LEADIN; #ifdef Libburn_debug_cd_texT packs = o->text_packs; fprintf(stderr, "libburn_DEBUG: 8 bit CD-TEXT packs to be transmitted:\n"); for (i = 0; i < 18 * o->num_text_packs; i += 18) { fprintf(stderr, "%4d :", i / 18); for (j = 0; j < 18; j++) { if (j >= 4 && j <= 15 && packs[i + j] >= 32 && packs[i + j] <= 126 && packs[i] != 0x88 && packs[i] != 0x89 && packs[i] != 0x8f) fprintf(stderr, " %c", packs[i + j]); else fprintf(stderr, " %2.2X", packs[i + j]); } fprintf(stderr, "\n"); } #endif /* Libburn_debug_cd_texT */ /* Chop from 8 bit text pack to 6 bit subchannel */ BURN_ALLOC_MEM(subdata, unsigned char, o->num_text_packs * 24); for (i = 0; i < 18 * o->num_text_packs; i += 3) { si = i / 3 * 4; subdata[si + 0] = (o->text_packs[i + 0] >> 2) & 0x3f; subdata[si + 1] = (o->text_packs[i + 0] << 4) & 0x30; subdata[si + 1] |= (o->text_packs[i + 1] >> 4) & 0x0f; subdata[si + 2] = (o->text_packs[i + 1] << 2) & 0x3c; subdata[si + 2] |= (o->text_packs[i + 2] >> 6) & 0x03; subdata[si + 3] = (o->text_packs[i + 2] >> 0) & 0x3f; } /* Start at Lead-in address of ATIP and write blocks up to -150 */ BURN_ALLOC_MEM(buf, struct buffer, 1); write_lba = d->start_lba; for (lba = d->start_lba; lba < -150; lba++) { /* Collect subdata in buf */ for (j = 0; j < 4; j++) { memcpy(buf->data + buf->bytes, subdata + sub_cursor * 24, 24); sub_cursor = (sub_cursor + 1) % o->num_text_packs; buf->bytes += 24; } buf->sectors++; sectors++; /* When full or last sector : perform WRITE */ if (buf->bytes + 96 >= 32768 || lba == -151) { #ifdef Libburn_debug_cd_texT fprintf(stderr, "libburn_DEBUG: 6 bit data to be transmitted:\n"); for (i = 0; i < buf->bytes; i += 24) { fprintf(stderr, "%4d :", i / 24); for (j = 0; j < 24; j++) fprintf(stderr, " %2.2X", buf->data[i + j]); fprintf(stderr, "\n"); } #endif /* Libburn_debug_cd_texT */ err = d->write(d, (off_t) write_lba, buf); if (err == BE_CANCELLED) { ret = 0; goto ex; } write_lba += sectors; sectors = buf->sectors = buf->bytes = 0; } } ret = 1; ex:; if (self_made_text_packs) { if (o->text_packs != NULL) free(o->text_packs); o->text_packs = NULL; o->num_text_packs = 0; } BURN_FREE_MEM(subdata); BURN_FREE_MEM(buf); d->busy = was_busy; return ret; } int burn_write_session(struct burn_write_opts *o, struct burn_session *s) { struct burn_drive *d = o->drive; int i, ret; if (o->write_type == BURN_WRITE_SAO) { ret = burn_write_leadin_cdtext(o, s, 0); if (ret <= 0) goto ex; } d->rlba = 0; for (i = 0; i < s->tracks; i++) { if (!burn_write_track(o, s, i)) { ret = 0; goto ex; } } /* ts A61103 */ ret = 1; ex:; if (o->write_type == BURN_WRITE_TAO) burn_write_close_session(o); return ret; } /* ts A61218 / C40303 : outsourced from burn_write_track() */ int burn_disc_init_track_status(struct burn_write_opts *o, struct burn_session *s, struct burn_track *t, int tnum, off_t sectors) { struct burn_drive *d = o->drive; /* Update progress */ d->progress.start_sector = d->nwa; d->progress.sectors = sectors; d->progress.sector = 0; /* ts A60831: added tnum-line, extended print message on proposal by bonfire-app@wanadoo.fr in http://libburn.pykix.org/ticket/58 */ d->progress.track = tnum; /* ts B20113 */ d->progress.indices = t->indices; d->progress.index = 0; if (d->progress.indices > 1) if (t->index[0] == 0x7fffffff) d->progress.index = 1; /* ts A61102 */ d->busy = BURN_DRIVE_WRITING; 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, open_ended = 0, ret= 0, nwa, lba; int sectors; char msg[160]; d->rlba = -150; /* XXX for tao, we don't want the pregaps but still want post? */ if (o->write_type != BURN_WRITE_TAO) { /* ts A61102 */ d->busy = BURN_DRIVE_WRITING_PREGAP; if (t->pregap1) d->rlba += 75; if (t->pregap2) d->rlba += t->pregap2_size; if (t->pregap1) { struct burn_track *pt; /* ts A70121 : Removed pseudo suicidal initializer = s->track[tnum - 1]; */ if (tnum == 0) { /* ts A70121 : This is not possible because track 1 cannot have a pregap at all. MMC-5 6.33.3.2 precribes a mandatory pause prior to any track 1. Pre-gap is prescribed for mode changes like audio-to-data. To set burn_track.pregap1 for track 1 is kindof a dirty hack. */ printf("first track should not have a pregap1\n"); pt = t; } else pt = s->track[tnum - 1]; /* ts A70121 */ for (i = 0; i < 75; i++) if (!sector_pregap(o, t->entry->point, pt->entry->control, pt->mode)) { ret = 0; goto ex; } } if (t->pregap2) for (i = 0; i < t->pregap2_size; i++) if (!sector_pregap(o, t->entry->point, t->entry->control, t->mode)) { ret = 0; goto ex; } /* ts B20113 : Flush buffer to avoid influence pregap on track counter */ ret = sector_write_buffer(d, NULL, 0); if (ret <= 0) goto ex; } else { o->control = t->entry->control; d->send_write_parameters(d, s, tnum, o); /* ts A61103 */ ret = d->get_nwa(d, -1, &lba, &nwa); /* ts A70213: CD-TAO: eventually expand size of track to max */ burn_track_apply_fillup(t, d->media_capacity_remaining, 0); /* <<< */ sprintf(msg, "TAO pre-track %2.2d : get_nwa(%d)=%d, d=%.f , demand=%.f , cap=%.f\n", tnum+1, nwa, ret, (double) d->nwa, (double) burn_track_get_sectors_2(t, 1) * 2048.0, (double) d->media_capacity_remaining); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); /* ts A91003 */ if (nwa < d->nwa) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020173, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Drive tells NWA smaller than last written address", 0, 0); d->sync_cache(d); return 0; } d->nwa = nwa; } /* user data */ sectors = burn_track_get_sectors_2(t, 1); if (sectors < 0) { ret = 0; goto ex; } open_ended = burn_track_is_open_ended(t); burn_disc_init_track_status(o, s, t, tnum, (off_t) sectors); /* ts A61030 : this cannot happen. tnum is always < s->tracks */ if (tnum == s->tracks) tmp = sectors > 150 ? 150 : sectors; for (i = 0; open_ended || i < sectors - tmp; i++) { /* ts A61023 : http://libburn.pykix.org/ticket/14 From time to time inquire drive buffer */ if ((i%64)==0) d->read_buffer_capacity(d); if (!sector_data(o, t, 0)) { ret = 0; goto ex; } /* ts A61031 */ if (open_ended) { d->progress.sectors = sectors = i; if (burn_track_is_data_done(t)) break; } /* update current progress */ d->progress.sector++; } for (; i < sectors; i++) { /* ts A61030: program execution never gets to this point */ fprintf(stderr,"LIBBURN_DEBUG: TNUM=%d TRACKS=%d TMP=%d\n", tnum, s->tracks, tmp); /* ts A61023 */ if ((i%64)==0) d->read_buffer_capacity(d); if (!sector_data(o, t, 1)) { ret = 0; goto ex; } /* update progress */ d->progress.sector++; } /* ts B20113 : Flush buffer to get buffered bytes assigned to the track counter */ ret = sector_write_buffer(d, t, 0); if (ret <= 0) goto ex; if (t->postgap && o->write_type != BURN_WRITE_TAO) { for (i = 0; i < t->postgap_size; i++) if (!sector_postgap(o, t->entry->point, t->entry->control, t->mode)) { ret = 0; goto ex; } ret = sector_write_buffer(d, NULL, 0); if (ret <= 0) goto ex; } /* ts A61103 */ ret = 1; ex:; if (d->cancel) burn_source_cancel(t->source); if (o->write_type == BURN_WRITE_TAO) { /* ts A71002 */ if (!burn_write_flush_buffer(o, t)) ret = 0; /* Ensure that at least 600 kB get written */ burn_write_track_minsize(o, s, tnum); d->sync_cache(d); /* ts A61030 */ /* ts A91003 : At least in simulation mode this causes NWA=0 for the next track. cdrecord does not use CLOSE TRACK at all but ends the tracks by SYNCHRONIZE CACHE alone. */ /* ts A91202 : Peng Shao reports that his LG GH22LS30 issues an SCSI error on CLOSE TRACK even in non-dummy mode. So i better give up this gesture which seems not be needed by any drive. if (!o->simulate) if (burn_write_close_track(o, s, tnum) <= 0) ret = 0; */ } return ret; } /* ts A61009 */ /* @param flag bit1 = do not libdax_msgs_submit() */ int burn_disc_write_is_ok(struct burn_write_opts *o, struct burn_disc *disc, int flag) { int i, t; char msg[80]; for (i = 0; i < disc->sessions; i++) for (t = 0; t < disc->session[i]->tracks; t++) if (sector_headers_is_ok( o, disc->session[i]->track[t]->mode) != 1) goto bad_track_mode_found; return 1; bad_track_mode_found:; sprintf(msg, "Unsuitable track mode 0x%x in track %d of session %d", disc->session[i]->track[t]->mode, i+1, t+1); if (!(flag & 2)) libdax_msgs_submit(libdax_messenger, -1, 0x0002010a, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); return 0; } /* ts A61218 : outsourced from burn_disc_write_sync() */ int burn_disc_init_write_status(struct burn_write_opts *o, struct burn_disc *disc) { struct burn_drive *d = o->drive; struct burn_track *t = NULL; int sx, tx, ret; d->cancel = 0; #ifdef Libburn_reset_progress_asynC /* <<< This is now done in async.c */ /* 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->progress.track = 0; #endif /* Libburn_reset_progress_asynC */ /* ts A61023 */ d->progress.buffer_capacity = 0; d->progress.buffer_available = 0; d->progress.buffered_bytes = 0; d->progress.buffer_min_fill = 0x7fffffffffffffff; /* ts A70711 */ d->pessimistic_buffer_free = 0; d->pbf_altered = 0; d->pessimistic_writes = 0; d->waited_writes = 0; d->waited_tries = 0; d->waited_usec = 0; /* Set eventual media fill up for last track only */ for (sx = 0; sx < disc->sessions; sx++) for (tx = 0 ; tx < disc->session[sx]->tracks; tx++) { t = disc->session[sx]->track[tx]; burn_track_set_fillup(t, 0); } if (o->fill_up_media && t != NULL) burn_track_set_fillup(t, 1); d->was_feat21h_failure = 0; if(d->write_opts != NULL) burn_write_opts_free(d->write_opts); ret = burn_write_opts_clone(o, &(d->write_opts), 0); if (ret <= 0) return ret; d->write_retry_count = 0; d->busy = BURN_DRIVE_WRITING; return 1; } static int precheck_write_is_audio(struct burn_disc *disc, int flag) { struct burn_session **sessions; int num_sessions, i, j; sessions = burn_disc_get_sessions(disc, &num_sessions); for (i = 0; i < num_sessions; i++) for (j = 0; j < sessions[i]->tracks; j++) if (!(sessions[i]->track[j]->mode & BURN_AUDIO)) return 0; return 1; } static int precheck_disc_has_cdtext(struct burn_disc *disc, int flag) { struct burn_session **sessions; int num_sessions, i, ret; sessions = burn_disc_get_sessions(disc, &num_sessions); for (i = 0; i < num_sessions; i++) { ret = burn_cdtext_from_session(sessions[i], NULL, NULL, 1); if (ret > 0) return 1; } return 0; } /* ts A70219 : API */ int burn_precheck_write(struct burn_write_opts *o, struct burn_disc *disc, char reasons[BURN_REASONS_LEN], int silent) { enum burn_write_types wt; struct burn_drive *d = o->drive; char *msg = NULL, *reason_pt; int no_media = 0, ret, has_cdtext, is_bd_pow = 0; reason_pt= reasons; reasons[0] = 0; if (d->drive_role == 0 || d->drive_role == 4) { if (d->drive_role == 0) sprintf(reasons, "DRIVE: is a virtual placeholder (null-drive)"); else sprintf(reasons, "DRIVE: read-only pseudo drive"); no_media = 1; goto ex; } /* check write mode against write job */ wt = burn_write_opts_auto_write_type(o, disc, reasons, 1); if (wt == BURN_WRITE_NONE) { if (strncmp(reasons, "MEDIA: ", 7)==0) no_media = 1; goto ex; } sprintf(reasons, "%s: ", d->current_profile_text); reason_pt= reasons + strlen(reasons); if (d->status == BURN_DISC_UNSUITABLE) goto unsuitable_profile; if (o->num_text_packs > 0) { has_cdtext = 1; } else { has_cdtext = precheck_disc_has_cdtext(disc, 0); } if (has_cdtext > 0) { if (d->current_profile == 0x09 || d->current_profile == 0x0a) { ret = precheck_write_is_audio(disc, 0); if (ret <= 0) strcat(reasons, "CD-TEXT supported only with pure audio CD media, "); } else { strcat(reasons, "CD-TEXT supported only with CD media, "); } } if (d->drive_role == 2 || d->drive_role == 5 || d->current_profile == 0x1a || d->current_profile == 0x12 || d->current_profile == 0x43) { /* DVD+RW , DVD-RAM , BD-RE, emulated drive on stdio file */ if (o->start_byte >= 0 && (o->start_byte % 2048)) strcat(reasons, "write start address not properly aligned to 2048, "); } else if (d->current_profile == 0x09 || d->current_profile == 0x0a) { /* CD-R , CD-RW */ if (!burn_disc_write_is_ok(o, disc, (!!silent) << 1)) strcat(reasons, "unsuitable track mode found, "); if (o->start_byte >= 0) strcat(reasons, "write start address not supported, "); if (o->num_text_packs > 0) { if (o->write_type != BURN_WRITE_SAO) strcat(reasons, "CD-TEXT supported only with write type SAO, "); if (d->start_lba == -2000000000) strcat(reasons, "No Lead-in start address known with CD-TEXT, "); } } else if (d->current_profile == 0x13) { /* DVD-RW Restricted Overwrite */ if (o->start_byte >= 0 && (o->start_byte % 32768)) strcat(reasons, "write start address not properly aligned to 32k, "); } else if (d->drive_role == 3 || d->current_profile == 0x11 || d->current_profile == 0x14 || d->current_profile == 0x15 || d->current_profile == 0x1b || d->current_profile == 0x2b || d->current_profile == 0x41) { /* DVD-R* Sequential , DVD+R[/DL] , BD-R, sequential stdio "drive" */ if (o->start_byte >= 0) strcat(reasons, "write start address not supported, "); is_bd_pow = burn_drive_get_bd_r_pow(d); if (is_bd_pow && !silent) libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002011e, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Unsuitable media detected: BD-R formatted to POW.", 0, 0); if (is_bd_pow) { strcat(reasons, "unsuitable media formatting POW detected, "); return 0; } } else { unsuitable_profile:; msg = calloc(1, 160); if (msg != NULL && !silent) { sprintf(msg, "Unsuitable media detected. Profile %4.4Xh %s", d->current_profile, d->current_profile_text); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002011e, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } if (msg != NULL) free(msg); strcat(reasons, "no suitable media profile detected, "); return 0; } ex:; if (reason_pt[0]) { if (no_media) { if (!silent) libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002013a, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "No suitable media detected", 0, 0); return -1; } if (!silent) libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020139, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Write job parameters are unsuitable", 0, 0); return 0; } return 1; } /* ts A70129 : learned much from dvd+rw-tools-7.0/growisofs_mmc.cpp */ int burn_disc_open_track_dvd_minus_r(struct burn_write_opts *o, struct burn_session *s, int tnum) { struct burn_drive *d = o->drive; char *msg = NULL; int ret, lba, nwa; off_t size; BURN_ALLOC_MEM(msg, char, 160); d->send_write_parameters(d, NULL, -1, o); ret = d->get_nwa(d, -1, &lba, &nwa); sprintf(msg, "DVD pre-track %2.2d : get_nwa(%d), ret= %d , d->nwa= %.f", tnum+1, nwa, ret, (double) d->nwa); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg,0,0); if (nwa > d->nwa) d->nwa = nwa; /* ts A70214 : eventually adjust already expanded size of track */ burn_track_apply_fillup(s->track[tnum], d->media_capacity_remaining,1); #ifdef Libburn_pioneer_dvr_216d_with_opC fprintf(stderr, "libburn_DEBUG: Libburn_pioneer_dvr_216d_with_opC : num_opc_tables = %d\n", d->num_opc_tables); if (d->num_opc_tables <= 0 && !o->simulate) { fprintf(stderr, "libburn_DEBUG: Libburn_pioneer_dvr_216d_with_opC : performing OPC\n"); d->perform_opc(d); fprintf(stderr, "libburn_DEBUG: Libburn_pioneer_dvr_216d_with_opC : done\n"); } #endif #ifdef Libburn_pioneer_dvr_216d_get_evenT mmc_get_event(d); #endif if (o->write_type == BURN_WRITE_SAO) { /* DAO */ size = ((off_t) burn_track_get_sectors_2(s->track[tnum], 1)) * (off_t) 2048; if (size < 0) {ret = 0; goto ex;} /* Eventually round track size up to write chunk */ if (o->obs_pad && (size % o->obs)) size += (off_t) (o->obs - (size % o->obs)); ret = d->reserve_track(d, size); if (ret <= 0) { sprintf(msg, "Cannot reserve track of %.f bytes", (double) size); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020138, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); {ret = 0; goto ex;} } } ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } /* ts A70226 */ int burn_disc_open_track_dvd_plus_r(struct burn_write_opts *o, struct burn_session *s, int tnum) { struct burn_drive *d = o->drive; char *msg = NULL; int ret, lba, nwa; off_t size; BURN_ALLOC_MEM(msg, char, 160); ret = d->get_nwa(d, -1, &lba, &nwa); sprintf(msg, "DVD+R pre-track %2.2d : get_nwa(%d), ret= %d , d->nwa= %.f", tnum+1, nwa, ret, (double) d->nwa); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg,0,0); if (nwa > d->nwa) d->nwa = nwa; /* ts A70214 : eventually adjust already expanded size of track */ burn_track_apply_fillup(s->track[tnum], d->media_capacity_remaining,1); if (o->write_type == BURN_WRITE_SAO && ! burn_track_is_open_ended(s->track[tnum])) { /* Reserve track */ size = ((off_t) burn_track_get_sectors_2(s->track[tnum], 1)) * (off_t) 2048; if (size < 0) {ret = 0; goto ex;} if (o->obs_pad) { /* Round track size up to write chunk size */ /* o->obs should be 32k or 64k already. But 32k alignment was once performed in d->reserve_track()*/ if (o->obs % 32768) o->obs += 32768 - (o->obs % 32768); if (size % o->obs) size += (off_t) (o->obs - (size % o->obs)); } ret = d->reserve_track(d, size); if (ret <= 0) { sprintf(msg, "Cannot reserve track of %.f bytes", (double) size); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020138, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); {ret = 0; goto ex;} } } ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } /* ts A70129 */ int burn_disc_close_track_dvd_minus_r(struct burn_write_opts *o, int tnum) { struct burn_drive *d = o->drive; char msg[80]; /* only with Incremental writing */ if (o->write_type != BURN_WRITE_TAO) return 2; sprintf(msg, "Closing track %2.2d (absolute track number %d)", tnum + 1, d->last_track_no); libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0); d->busy = BURN_DRIVE_CLOSING_SESSION; /* Ignoring tnum here and hoping that d->last_track_no is correct */ d->close_track_session(d, 0, d->last_track_no); /* CLOSE TRACK, 001b */ d->busy = BURN_DRIVE_WRITING; d->last_track_no++; return 1; } /* ts A70229 */ int burn_disc_finalize_dvd_plus_r(struct burn_write_opts *o) { struct burn_drive *d = o->drive; char msg[40 + 80]; /* filltext + profile */ sprintf(msg, "Finalizing %s ...", d->current_profile_text); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); if(d->current_profile == 0x41) { /* BD-R */ /* CLOSE SESSION, 110b, Finalize Disc */ d->close_track_session(d, 3, 0); /* (3<<1)|0 = 6 */ } else { /* CLOSE SESSION, 101b, Finalize with minimal radius */ d->close_track_session(d, 2, 1); /* (2<<1)|1 = 5 */ } sprintf(msg, "... finalizing %s done ", d->current_profile_text); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); return 1; } /* ts A70226 */ int burn_disc_close_track_dvd_plus_r(struct burn_write_opts *o, int tnum, int is_last_track) { struct burn_drive *d = o->drive; char msg[80]; sprintf(msg, "Closing track %2.2d (absolute track and session number %d)", tnum + 1, d->last_track_no); libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0); d->busy = BURN_DRIVE_CLOSING_SESSION; d->close_track_session(d, 0, d->last_track_no); /* CLOSE TRACK, 001b */ /* Each session becomes a single logical track. So to distinguish them, it is mandatory to close the session together with each track. */ if (is_last_track && !o->multi) burn_disc_finalize_dvd_plus_r(o); else d->close_track_session(d, 1, 0); /* CLOSE SESSION, 010b */ d->busy = BURN_DRIVE_WRITING; d->last_track_no++; return 1; } /* <<< #define Libburn_simplified_dvd_chunk_transactioN 1 */ #ifdef Libburn_simplified_dvd_chunk_transactioN /* ts A91114 : EXPERIMENTAL, NOT COMPLETELY IMPLEMENTED Simplified data transmission for DVD. libburn via GNU/Linux USB is 30 % slower than growisofs or cdrecord when transmitting 32 KB chunks. With 64 KB chunks it is 20% faster than the competitors. No heavy CPU load is visible but there might be subtle race conditions in the USB driver which work better with shorter time gaps between WRITE commands. Insight: It is actually about the interference of track source reading with SCSI writing via USB. growisofs reads with O_DIRECT into a mmap()ed buffer. When doing the same, libburn with 32 KB chunks reaches similar write speed. On the other hand, 64 KB chunks are 20% faster than that and are not improved by reading O_DIRECT. O_DIRECT is a property of the input fd of struct burn_source. It can only be done with properly aligned memory and with aligned read size. Alignment size is file system system specific. System call mmap(NULL, (size_t) buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, (off_t) 0); is supposed to allocate a properly aligned buffer. 64 KB is supposed to be a safe size. Actually mmap() seems to be the main cause for a positive effect of O_DIRECT. This simplified transmission function did not bring visible benefit. So for now it is not worth to teach it all applicable details of old CD sector oriented transmission. @return 1= ok, go on , 2= no input with track->open_ended = nothing written <= 0 = error */ static int transact_dvd_chunk(struct burn_write_opts *opts, struct burn_track *track) { int curr = 0, valid, err; struct burn_drive *d = opts->drive; struct buffer *out = d->buffer; unsigned char *data = out->data; #ifdef Libburn_log_in_and_out_streaM /* ts A61031 */ static int tee_fd= -1; if(tee_fd==-1) tee_fd= open("/tmp/libburn_sg_readin", O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR); #endif /* Libburn_log_in_and_out_streaM */ /* Read a chunk full of data */ /* ??? Do we have offset padding ? >>> First produce offset padding */; /* <<<< */ if (0 && !track->eos) { for (curr = 0; curr < opts->obs; curr += 2048) { if (track->source->read != NULL) valid = track->source->read(track->source, data + curr, 2048); else valid = track->source->read_xt(track->source, data + curr, 2048); if (valid <= 0) { track->eos = 1; break; } track->sourcecount += valid; #ifdef Libburn_log_in_and_out_streaM if(tee_fd!=-1 && valid>0) { write(tee_fd, data + curr, valid); } #endif /* Libburn_log_in_and_out_streaM */ } } else if (!track->eos){ valid = track->source->read(track->source, data, opts->obs); if (valid <= 0) { track->eos = 1; } else { track->sourcecount += valid; curr = valid; #ifdef Libburn_log_in_and_out_streaM if(tee_fd!=-1 && valid>0) { write(tee_fd, data, valid); } #endif /* Libburn_log_in_and_out_streaM */ } } if (curr == 0 && track->open_ended) { /* >>> allow tail padding */; return 2; } if (curr < opts->obs) memset(data + curr , 0, opts->obs - curr); /* Write chunk */ out->bytes = opts->obs; out->sectors = out->bytes / 2048; err = d->write(d, d->nwa, out); if (err == BE_CANCELLED) return 0; track->writecount += out->bytes; track->written_sectors += out->sectors; d->progress.buffered_bytes += out->bytes; d->nwa += out->sectors; out->bytes = 0; out->sectors = 0; return 1; } #endif /* Libburn_simplified_dvd_chunk_transactioN */ /* ts A61218 - A81208 */ int burn_dvd_write_track(struct burn_write_opts *o, struct burn_session *s, int tnum, int is_last_track) { struct burn_track *t = s->track[tnum]; struct burn_drive *d = o->drive; struct buffer *out = d->buffer; int sectors; int i, open_ended = 0, ret= 0, is_flushed = 0, track_open = 0; off_t first_buf_cap = 0, further_cap = 0, buf_cap_step = 1024; /* ts A70213 : eventually expand size of track to max */ burn_track_apply_fillup(t, d->media_capacity_remaining, 0); /* ts C00806 : Consider the track state changed by try to open */ d->medium_state_changed = 1; if (d->current_profile == 0x11 || d->current_profile == 0x14 || d->current_profile == 0x15) { /* DVD-R, DVD-RW Sequential, DVD-R/DL Sequential */ ret = burn_disc_open_track_dvd_minus_r(o, s, tnum); if (ret <= 0) goto ex; /* Pioneer DVR-216D rev 1.09 hates multiple buffer inquiries before the drive buffer is full. */ first_buf_cap = 0; further_cap = -1; } else if (d->current_profile == 0x1b || d->current_profile == 0x2b) { /* DVD+R , DVD+R/DL */ ret = burn_disc_open_track_dvd_plus_r(o, s, tnum); if (ret <= 0) goto ex; } else if (d->current_profile == 0x41) { /* BD-R SRM */ ret = burn_disc_open_track_dvd_plus_r(o, s, tnum); if (ret <= 0) goto ex; } track_open = 1; sectors = burn_track_get_sectors_2(t, 1); if (sectors < 0) {ret = 0; goto ex;} open_ended = burn_track_is_open_ended(t); /* (offset padding is done within sector_data()) */ burn_disc_init_track_status(o, s, t, tnum, (off_t) sectors); for (i = 0; open_ended || i < sectors; i++) { /* From time to time inquire drive buffer */ /* ts A91110: Eventually avoid to do this more than once before the drive buffer is full. See above DVD- */ if (i == first_buf_cap || ((i % buf_cap_step) == 0 && (i >= further_cap || further_cap < 0))) { d->read_buffer_capacity(d); if (further_cap < 0) further_cap = d->progress.buffer_capacity / 2048 + 128; } #ifdef Libburn_simplified_dvd_chunk_transactioN ret = transact_dvd_chunk(o, t); if (ret <= 0) {ret = 0; goto ex;} i += o->obs / 2048 - 1; d->progress.sector += o->obs / 2048 - 1; #else /* transact a (CD sized) sector */ if (!sector_data(o, t, 0)) { ret = 0; goto ex; } #endif if (open_ended) { d->progress.sectors = sectors = i; if (burn_track_is_data_done(t)) break; } /* update current progress */ d->progress.sector++; } /* (tail padding is done in sector_data()) */ /* Pad up buffer to next full o->obs (usually 32 kB) */ if (o->obs_pad && out->bytes > 0 && out->bytes < o->obs) { memset(out->data + out->bytes, 0, o->obs - out->bytes); out->sectors += (o->obs - out->bytes) / 2048; out->bytes = o->obs; } ret = burn_write_flush(o, t); if (ret <= 0) goto ex; is_flushed = 1; /* Eventually finalize track */ if (d->current_profile == 0x11 || d->current_profile == 0x14 || d->current_profile == 0x15) { /* DVD-R, DVD-RW Sequential, DVD-R/DL Sequential */ ret = burn_disc_close_track_dvd_minus_r(o, tnum); if (ret <= 0) goto ex; } else if (d->current_profile == 0x1b || d->current_profile == 0x2b) { /* DVD+R , DVD+R/DL */ ret = burn_disc_close_track_dvd_plus_r(o, tnum, is_last_track); if (ret <= 0) goto ex; } else if (d->current_profile == 0x41) { /* BD-R SRM */ ret = burn_disc_close_track_dvd_plus_r(o, tnum, is_last_track); if (ret <= 0) goto ex; } ret = 1; ex:; if (d->cancel) burn_source_cancel(t->source); if (track_open && !is_flushed) d->sync_cache(d); /* burn_write_flush() was not called */ return ret; } /* ts A61219 */ int burn_disc_close_session_dvd_plus_rw(struct burn_write_opts *o, struct burn_session *s) { struct burn_drive *d = o->drive; d->busy = BURN_DRIVE_CLOSING_SESSION; /* This seems to be a quick end : "if (!dvd_compat)" */ /* >>> Stop de-icing (ongoing background format) quickly by mmc_close() (but with opcode[2]=0). Wait for unit to get ready. return 1; */ /* Else: end eventual background format in a "DVD-RO" compatible way */ d->close_track_session(d, 1, 0); /* same as CLOSE SESSION for CD */ d->busy = BURN_DRIVE_WRITING; return 1; } /* ts A61228 */ int burn_disc_close_session_dvd_minus_rw(struct burn_write_opts *o, struct burn_session *s) { struct burn_drive *d = o->drive; d->busy = BURN_DRIVE_CLOSING_SESSION; if (d->current_profile == 0x13) { d->close_track_session(d, 1, 0); /* CLOSE SESSION, 010b */ /* ??? under what circumstances to use close functiom 011b "Finalize disc" ? */ } d->busy = BURN_DRIVE_WRITING; return 1; } /* ts A70129 : for profile 0x11 DVD-R, 0x14 DVD-RW Seq, 0x15 DVD-R/DL Seq */ int burn_disc_close_session_dvd_minus_r(struct burn_write_opts *o) { struct burn_drive *d = o->drive; /* only for Incremental writing */ if (o->write_type != BURN_WRITE_TAO) return 2; #ifdef Libburn_dvd_r_dl_multi_no_close_sessioN if (d->current_profile == 0x15 && o->multi) return 2; #endif libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "Closing session", 0, 0); d->busy = BURN_DRIVE_CLOSING_SESSION; d->close_track_session(d, 1, 0); /* CLOSE SESSION, 010b */ d->busy = BURN_DRIVE_WRITING; return 1; } /* ts A61218 */ int burn_dvd_write_session(struct burn_write_opts *o, struct burn_session *s, int is_last_session) { int i, ret, multi_mem; struct burn_drive *d = o->drive; /* ts A90108 */ if (d->current_profile == 0x41 && d->status == BURN_DISC_APPENDABLE && d->state_of_last_session == 1) { /* last session on BD-R is still open */; /* BR-R were not closed by libburn-0.6.0.pl00 if o->multi==0. This leads to an unreadable, but recoverable) media state. Technically they are appendable although the last session is not readable. By default the open session gets closed here before the new session is written. E.g. after writing a small dummy session number 2 one can read session 1 and write session 3 which points to data of session 1. For the case that no media with 3 sessions is desired it is possible to activate the following coarse single-session closing code: No new session will be written but calling programs will report success. Quite misleading. Activate only if really needed by # define Libburn_bug_A90108_close_disC yes */ #ifdef Libburn_bug_A90108_close_disC /* Close open session and media. That was the goal of the failed run which led to the unreadable (but recoverable) media state. It is not easy to implement a general close function for all media types. Therefore this pseudo write code is under control of #ifdef. */ libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020171, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, "Closing BD-R with accidentally open session", 0, 0); d->close_track_session(d, 3, 0); /* CLOSE SESSION, 110b */ d->state_of_last_session = 3; /* mark as complete session */ d->status = BURN_DISC_FULL; sleep(3); /* The caller might need time to arrange itself */ return 1; #else /* Libburn_bug_A90108_close_disC */ /* This is the default mode. */ libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020170, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, "Closing open session before writing new one", 0, 0); d->close_track_session(d, 1, 0); /* CLOSE SESSION, 010b */ d->state_of_last_session = 3; /* mark as complete session */ #endif /* ! Libburn_bug_A90108_close_disC */ } for (i = 0; i < s->tracks; i++) { ret = burn_dvd_write_track(o, s, i, is_last_session && i == (s->tracks - 1)); if (ret <= 0) break; } if (d->current_profile == 0x11 || d->current_profile == 0x14 || d->current_profile == 0x15) { /* DVD-R , DVD-RW Sequential, DVD-R/DL Sequential */ /* If feature 21h failed on write 0: do not close session */ if (d->was_feat21h_failure != 2) { multi_mem = o->multi; if (!is_last_session) o->multi = 1; ret = burn_disc_close_session_dvd_minus_r(o); o->multi = multi_mem; if (ret <= 0) return 0; } } else if (d->current_profile == 0x12 || d->current_profile == 0x43) { /* DVD-RAM , BD-RE */ /* ??? any finalization needed ? */; } else if (d->current_profile == 0x13) { /* DVD-RW restricted overwrite */ if (d->needs_close_session) { ret = burn_disc_close_session_dvd_minus_rw(o, s); if (ret <= 0) return 0; } } else if (d->current_profile == 0x1a) { /* DVD+RW */ if (d->needs_close_session) { ret = burn_disc_close_session_dvd_plus_rw(o, s); if (ret <= 0) return 0; } } else if (d->current_profile == 0x1b || d->current_profile == 0x2b) { /* DVD+R , DVD+R/DL do each track as an own session */; } else if (d->current_profile == 0x41) { /* BD-R SRM do each track as an own session */; } return 1; } /* ts A61218 : learned much from dvd+rw-tools-7.0/growisofs_mmc.cpp */ int burn_disc_setup_dvd_plus_rw(struct burn_write_opts *o, struct burn_disc *disc) { struct burn_drive *d = o->drive; int ret; if (d->bg_format_status==0 || d->bg_format_status==1) { d->busy = BURN_DRIVE_FORMATTING; /* start or re-start dvd_plus_rw formatting */ ret = d->format_unit(d, (off_t) 0, 0); if (ret <= 0) return 0; d->busy = BURN_DRIVE_WRITING; d->needs_close_session = 1; } /* >>> perform OPC if needed */; /* >>> ? what else ? */; return 1; } /* ts A61228 : learned much from dvd+rw-tools-7.0/growisofs_mmc.cpp */ int burn_disc_setup_dvd_minus_rw(struct burn_write_opts *o, struct burn_disc *disc) { struct burn_drive *d = o->drive; char msg[60]; int ret; d->nwa = 0; if (o->start_byte >= 0) { d->nwa = o->start_byte / 32768; /* align to 32 kB */ sprintf(msg, "Write start address is %.f * 32768", (double) d->nwa); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020127, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); d->nwa *= 16; /* convert to 2048 block units */ } /* ??? mmc5r03c.pdf 7.5.2 : "For DVD-RW media ... If a medium is in Restricted overwrite mode, this mode page shall not be used." But growisofs composes a page 5 and sends it. mmc5r03c.pdf 5.3.16 , table 127 specifies that mode page 5 shall be supported with feature 0026h Restricted Overwrite. 5.3.22 describes a feature 002Ch Rigid Restrictive Overwrite which seems to apply to DVD-RW and does not mention page 5. 5.4.14 finally states that profile 0013h includes feature 002Ch rather than 0026h. d->send_write_parameters(d, NULL, -1, o); */ d->busy = BURN_DRIVE_FORMATTING; /* "quick grow" to at least byte equivalent of d->nwa */ ret = d->format_unit(d, (off_t) d->nwa * (off_t) 2048, (d->nwa > 0) << 3); if (ret <= 0) return 0; d->busy = BURN_DRIVE_WRITING; /* >>> perform OPC if needed */; return 1; } /* ts A70129 : for DVD-R[W] Sequential Recoding */ int burn_disc_setup_dvd_minus_r(struct burn_write_opts *o, struct burn_disc *disc) { struct burn_drive *d = o->drive; /* most setup is in burn_disc_setup_track_dvd_minus_r() */; d->nwa = 0; return 1; } /* ts A70226 : for DVD+R , DVD+R/DL */ int burn_disc_setup_dvd_plus_r(struct burn_write_opts *o, struct burn_disc *disc) { struct burn_drive *d = o->drive; /* most setup is in burn_disc_setup_track_dvd_plus_r() */; d->nwa = 0; return 1; } /* ts A61218 - A70415 */ int burn_dvd_write_sync(struct burn_write_opts *o, struct burn_disc *disc) { int i, ret, o_end; off_t default_size = 0; struct burn_drive *d = o->drive; struct burn_track *t; char *msg = NULL; BURN_ALLOC_MEM(msg, char, 160); d->needs_close_session = 0; /* buffer flush trigger for sector.c:get_sector() */ o->obs = Libburn_dvd_obS; if (d->current_profile == 0x1a || d->current_profile == 0x12 || d->current_profile == 0x43) { /* DVD+RW , DVD-RAM , BD-RE */ ret = 1; if (d->current_profile == 0x1a) ret = burn_disc_setup_dvd_plus_rw(o, disc); if (ret <= 0) { sprintf(msg, "Write preparation setup failed for DVD+RW"); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020121, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); goto early_failure; } d->nwa = 0; if (o->start_byte >= 0) { d->nwa = o->start_byte / 2048; sprintf(msg, "Write start address is %.f * 2048", (double) d->nwa); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020127, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } if (o->obs_pad < 2) o->obs_pad = 0; /* no filling-up of last 32k buffer */ if (d->current_profile == 0x43) /* BD-RE */ o->obs = Libburn_bd_re_obS; if (d->do_stream_recording) { if (o->obs_pad < 2) o->obs_pad = 1; if (d->current_profile == 0x43) /* BD-RE */ o->obs = Libburn_bd_streamed_obS; } } else if (d->current_profile == 0x13) { /* DVD-RW Restricted Overwrite */ ret = burn_disc_setup_dvd_minus_rw(o, disc); if (ret <= 0) { sprintf(msg, "Write preparation setup failed for DVD-RW"); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020121, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); goto early_failure; } /* _Rigid_ Restricted Overwrite demands this */ o->obs_pad = 1; /* fill-up track's last 32k buffer */ } else if (d->current_profile == 0x11 || d->current_profile == 0x14 || d->current_profile == 0x15) { /* DVD-R , DVD-RW Sequential , DVD-R/DL Sequential */ t = disc->session[0]->track[0]; o_end = ( burn_track_is_open_ended(t) && !o->fill_up_media ); default_size = burn_track_get_default_size(t); if (o->write_type == BURN_WRITE_SAO && o_end) { sprintf(msg, "Activated track default size %.f", (double) default_size); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002012e, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); burn_track_set_size(t, default_size); } /* Whether to fill-up last 32k buffer of track. */ if (o->obs_pad < 2) o->obs_pad = (o->write_type != BURN_WRITE_SAO); ret = burn_disc_setup_dvd_minus_r(o, disc); if (ret <= 0) { sprintf(msg, "Write preparation setup failed for DVD-R[W]"); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020121, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); goto early_failure; } } else if (d->current_profile == 0x1b || d->current_profile == 0x2b || d->current_profile == 0x41) { /* DVD+R , DVD+R/DL , BD-R SRM */ /* >>> ts A81208 : with BD-R set o->obs to 64 kB ? */ t = disc->session[0]->track[0]; o_end = ( burn_track_is_open_ended(t) && !o->fill_up_media ); default_size = burn_track_get_default_size(t); if (o->write_type == BURN_WRITE_SAO && o_end) { sprintf(msg, "Activated track default size %.f", (double) default_size); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002012e, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); burn_track_set_size(t, default_size); } ret = burn_disc_setup_dvd_plus_r(o, disc); if (ret <= 0) { sprintf(msg, "Write preparation setup failed for %s", d->current_profile == 0x41 ? "BD-R" : "DVD+R"); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020121, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); goto early_failure; } /* Unaligned BD-R track end works with various drives and produces exact READ CAPACITY results. Nevertheless stream recording hates unaligned WRITE. With DVD+R it seems that obs_pad is silently applied by the drive if a non-aligned final WRITE is received. */ if (o->obs_pad < 2 && !(d->current_profile == 0x41 && !d->do_stream_recording && o->bdr_obs_exempt)) o->obs_pad = 1; /* fill-up track's last obs buffer */ if (d->current_profile == 0x41) /* BD-R */ o->obs = Libburn_bd_r_obS; if (d->do_stream_recording) { if (d->current_profile == 0x41) /* BD-R */ o->obs = Libburn_bd_streamed_obS; } } #ifdef Libburn_dvd_obs_default_64K o->obs = 64 * 1024; #endif /* <<< test only : Does this increase effective speed with USB ? ts A90801 : 64kB: speed with 16x DVD-R is 12 rather than 8 128kB: glibc complains about double free With BURN_OS_TRANSPORT_BUFFER_SIZE enlarged to 128 MB, the first WRITE fails with an i/o error. o->obs = 64 * 1024; */ if (o->dvd_obs_override >= 32 * 1024) o->obs = o->dvd_obs_override; if (o->obs > BUFFER_SIZE) { sprintf(msg, "Chosen write chunk size %d exceeds system dependent buffer size", o->obs); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); o->obs = 32 * 1024; /* This size is required to work */ } if (d->do_stream_recording && (d->current_profile == 0x43 || d->current_profile == 0x41) && o->obs < Libburn_bd_streamed_obS) { /* LG GGW-H20 writes junk with stream recording and obs=32k */ sprintf(msg, "Stream recording disabled because of small output buffer"); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020176, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); d->do_stream_recording = 0; } sprintf(msg, "dvd/bd Profile= %2.2Xh , obs= %d , obs_pad= %d", d->current_profile, o->obs, o->obs_pad); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); for (i = 0; i < disc->sessions; i++) { /* update progress */ d->progress.session = i; d->progress.tracks = disc->session[i]->tracks; ret = burn_dvd_write_session(o, disc->session[i], i == (disc->sessions - 1)); if (ret <= 0) goto ex; /* XXX: currently signs an end of session */ d->progress.sector = 0; d->progress.start_sector = 0; d->progress.sectors = 0; } ret = 1; ex:; /* >>> eventual emergency finalization measures */ /* update media state records */ burn_drive_mark_unready(d, 0); burn_drive_inquire_media(d); if (d->current_profile == 0x41 && d->complete_sessions >= 300) { sprintf(msg, "Sequential BD-R media now contains %d sessions. It is likely to soon fail writing.", d->complete_sessions); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002017b, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); } BURN_FREE_MEM(msg); return ret; early_failure:; BURN_FREE_MEM(msg); return 0; } /* ts A70904 */ int burn_stdio_open_write(struct burn_drive *d, off_t start_byte, int sector_size, int flag) { /* We normally need _LARGEFILE64_SOURCE defined by the build system. Nevertheless the system might use large address integers by default. */ #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif int fd = -1; int mode = O_RDWR | O_CREAT | O_LARGEFILE; char msg[60]; off_t lseek_res; if(d->drive_role == 4) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020181, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Pseudo-drive is a read-only file. Cannot write.", 0, 0); return 0; } if (d->drive_role == 5 || d->drive_role == 3) mode = O_WRONLY | O_CREAT | O_LARGEFILE; if (d->devname[0] == 0) /* null drives should not come here */ return -1; fd = burn_drive__fd_from_special_adr(d->devname); if (fd >= 0) fd = dup(fd); /* check validity and make closeable */ else fd = open(d->devname, mode | O_BINARY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (fd == -1) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020005, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Failed to open device (a pseudo-drive)", errno, 0); d->cancel = 1; return -1; } if (start_byte < 0) start_byte = 0; if (d->drive_role == 2 || d->drive_role == 5) { lseek_res = lseek(fd, start_byte, SEEK_SET); if (lseek_res == -1) { sprintf(msg, "Cannot address start byte %.f", (double) start_byte); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020147, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); close(fd); d->cancel = 1; fd = -1; } } d->nwa = start_byte / sector_size; return fd; } /* ts A70904 */ int burn_stdio_read_source(struct burn_source *source, char *buf, int bufsize, struct burn_write_opts *o, int flag) { int count= 0, todo; for(todo = bufsize; todo > 0; todo -= count) { if(source->read!=NULL) count = source->read(source, (unsigned char *) (buf + (bufsize - todo)), todo); else count = source->read_xt(source, (unsigned char *) (buf + (bufsize - todo)), todo); if (count <= 0) break; } return (bufsize - todo); } /* ts A70904 */ int burn_stdio_write(int fd, char *buf, int count, struct burn_drive *d, int flag) { int ret = 0; char *msg = NULL; int todo, done, retries; if (d->cancel || count <= 0) return 0; if(d->do_simulate) return 1; todo = count; done = 0; for (retries = 0; todo > 0 && retries <= Libburn_stdio_write_retrieS; retries++) { /* fprintf(stderr, "libburn_DEBUG: write(%d, %lX, %d)\n", fd, (unsigned long) buf, count); */ ret = write(fd, buf + done, todo); if (ret < 0) break; done += ret; todo -= ret; } if (done != count) { BURN_ALLOC_MEM(msg, char, 160); sprintf(msg, "Cannot write desired amount of %d bytes.", count); if (retries > 1) sprintf(msg + strlen(msg), " Did %d retries. Last", retries - 1); sprintf(msg + strlen(msg), " write(2) returned %d.", ret); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020148, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); d->cancel = 1; ret = 0; goto ex; } ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } /* ts A70910 : to be used as burn_drive.write(), emulating mmc_write() */ int burn_stdio_mmc_write(struct burn_drive *d, off_t start, struct buffer *buf) { int ret; off_t start_byte; if (d->cancel) return BE_CANCELLED; if (d->stdio_fd < 0) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002017d, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Invalid file descriptor with stdio pseudo-drive", 0, 0); d->cancel = 1; return BE_CANCELLED; } if (start != d->nwa) { char msg[80]; start_byte = ((off_t) start) * (off_t) (buf->bytes / buf->sectors); if (lseek(d->stdio_fd, start_byte, SEEK_SET)==-1) { sprintf(msg, "Cannot address start byte %.f", (double) start_byte); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020147, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); d->cancel = 1; return BE_CANCELLED; } d->nwa = start; } ret = burn_stdio_write(d->stdio_fd,(char *)buf->data, buf->bytes, d,0); if (ret <= 0) return BE_CANCELLED; d->nwa += buf->sectors; return 0; } /* ts A70910 : to be used as burn_drive.write(), emulating mmc_write() with simulated writing. */ int burn_stdio_mmc_dummy_write(struct burn_drive *d, off_t start, struct buffer *buf) { if (d->cancel) return BE_CANCELLED; d->nwa = start + buf->sectors; return 0; } /* ts A70911 */ /* Flush stdio system buffer to physical device. @param flag bit0= do not report debug message (intermediate sync) bit1= do fsync(2) unconditionally */ int burn_stdio_sync_cache(int fd, struct burn_drive *d, int flag) { int ret, do_fsync; char *msg = NULL; if (fd < 0) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002017d, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Invalid file descriptor with stdio pseudo-drive", 0, 0); d->cancel = 1; ret = 0; goto ex; } d->needs_sync_cache = 0; do_fsync = 0; if (flag & 2) do_fsync = 1; else if (d->write_opts != NULL) do_fsync = (d->write_opts->stdio_fsync_size >= 0); if (do_fsync) { if (!(flag & 1)) libdax_msgs_submit(libdax_messenger, -1, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, "syncing cache (stdio fsync)", 0, 0); ret = fsync(fd); } else { ret = 0; } if (ret != 0 && errno == EIO) { BURN_ALLOC_MEM(msg, char, 160); sprintf(msg, "Cannot write desired amount of data. fsync(2) returned %d.", ret); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020148, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); d->cancel = 1; ret = 0; goto ex; } ret = 1; ex:; BURN_FREE_MEM(msg); return ret; } /* ts A70911 : to be used as burn_drive.sync_cache(), emulating mmc_sync_cache() */ void burn_stdio_mmc_sync_cache(struct burn_drive *d) { burn_stdio_sync_cache(d->stdio_fd, d, 0); } /* ts C00824 : API */ /* Enforces nominal write speed */ int burn_nominal_slowdown(int kb_per_second, int max_corr, struct timeval *prev_time, int *us_corr, off_t b_since_prev, int flag) { struct timeval tnow; double to_wait, goal, corr; int abs_max_corr; if (flag & 1) { gettimeofday(prev_time, NULL); *us_corr = 0; return 1; } if (kb_per_second <= 0) return 2; if (max_corr < -1.0e9 || max_corr > 1.0e9) abs_max_corr = 1000000000; else abs_max_corr = abs(max_corr); gettimeofday(&tnow, NULL); goal = ((double) b_since_prev) / 1000.0 / ((double) kb_per_second) + ((double) prev_time->tv_sec) + ((double) prev_time->tv_usec) / 1.0e6 + ((double) *us_corr) / 1.0e6 ; to_wait = goal - ((double) tnow.tv_sec) - ((double) tnow.tv_usec) / 1.0e6; /* usleep might be restricted to 999999 microseconds */ while (to_wait > 0.0) { if (to_wait >= 0.5) { usleep(500000); to_wait -= 0.5; } else if (to_wait >= 0.00001) { usleep((int) (to_wait * 1000000.0)); to_wait = 0.0; } else { to_wait = 0.0; } } gettimeofday(prev_time, NULL); corr = (goal - ((double) prev_time->tv_sec) - ((double) prev_time->tv_usec) / 1.0e6) * 1.0e6; if (corr > abs_max_corr) *us_corr = abs_max_corr; else if (corr < -abs_max_corr) *us_corr = -abs_max_corr; else *us_corr = corr; return 1; } /* ts A70904 */ int burn_stdio_write_track(struct burn_write_opts *o, struct burn_session *s, int tnum, int flag) { int open_ended, bufsize = 16 * 2048, ret; struct burn_track *t = s->track[tnum]; struct burn_drive *d = o->drive; char *buf = NULL; int us_corr = 0, max_corr = 250000; off_t prev_sync_sector = 0, sectors, i; struct buffer *out = d->buffer; struct timeval prev_time; BURN_ALLOC_MEM(buf, char, bufsize); sectors = burn_track_get_sectors_2_v2(t, 1); if (sectors < 0) {ret = 0; goto ex;} burn_disc_init_track_status(o, s, t, tnum, sectors); open_ended = burn_track_is_open_ended(t); t->end_on_premature_eoi = (o->write_type == BURN_WRITE_TAO); /* attach stdio emulators for mmc_*() functions */ if (o->simulate) d->write = burn_stdio_mmc_dummy_write; else d->write = burn_stdio_mmc_write; d->do_simulate = o->simulate; d->sync_cache = burn_stdio_mmc_sync_cache; /* initialize */ burn_nominal_slowdown(d->nominal_write_speed, max_corr, &prev_time, &us_corr, (off_t) 0, 1); for (i = 0; open_ended || i < sectors; i++) { /* transact a (CD sized) sector */ if (!sector_data(o, t, 0)) {ret= 0; goto ex;} if (open_ended) d->progress.sectors = sectors = d->progress.sector; if (open_ended || t->end_on_premature_eoi) { if (burn_track_is_data_done(t)) break; } d->progress.sector++; /* Flush to disk from time to time */ if (o->stdio_fsync_size > 0) { if (d->progress.sector - prev_sync_sector >= o->stdio_fsync_size) { if (!o->simulate) burn_stdio_sync_cache(d->stdio_fd, d, 1); burn_nominal_slowdown( d->nominal_write_speed, max_corr, &prev_time, &us_corr, (off_t) (d->progress.sector - prev_sync_sector) * (off_t) 2048, 0); prev_sync_sector = d->progress.sector; } } else if ((d->progress.sector % 512) == 0) { burn_nominal_slowdown(d->nominal_write_speed, max_corr, &prev_time, &us_corr, (off_t) (512 * 2048), 0); } } /* Pad up buffer to next full o->obs (usually 32 kB) */ if (o->obs_pad && out->bytes > 0 && out->bytes < o->obs) { memset(out->data + out->bytes, 0, o->obs - out->bytes); out->sectors += (o->obs - out->bytes) / 2048; out->bytes = o->obs; } ret = burn_write_flush(o, t); ret= 1; ex:; if (d->cancel) burn_source_cancel(t->source); if (t->end_on_premature_eoi == 2) d->cancel = 1; BURN_FREE_MEM(buf); return ret; } /* ts A70904 */ int burn_stdio_write_sync(struct burn_write_opts *o, struct burn_disc *disc) { int ret; struct burn_drive *d = o->drive; d->needs_close_session = 0; if (o->obs_pad < 2) o->obs_pad = 0; /* no filling-up of track's last 32k buffer */ o->obs = 32*1024; /* buffer size */ if (disc->sessions != 1) {ret= 0 ; goto ex;} if (disc->session[0]->tracks != 1) {ret= 0 ; goto ex;} /* update progress */ d->progress.session = 0; d->progress.tracks = 1; /* >>> adjust sector size (2048) to eventual audio or even raw */ /* >>> ??? ts B11004 : Why this eagerness to close and open ? */ /* open target file */ if (d->stdio_fd >= 0) close(d->stdio_fd); if (d->drive_role == 5 && d->status == BURN_DISC_APPENDABLE && o->start_byte < 0) o->start_byte = d->role_5_nwa * 2048; d->stdio_fd = burn_stdio_open_write(d, o->start_byte, 2048, 0); if (d->stdio_fd == -1) {ret = 0; goto ex;} ret = burn_stdio_write_track(o, disc->session[0], 0, 0); if (ret <= 0) goto ex; /* XXX: currently signs an end of session */ d->progress.sector = 0; d->progress.start_sector = 0; d->progress.sectors = 0; ret = 1; ex:; /* >>> ??? ts B11004 : Why this eagerness to close ? */ if (d->stdio_fd >= 0) close(d->stdio_fd); d->stdio_fd = -1; /* update pseudo-media state records by re-grabbing */ burn_drive_mark_unready(d, 8); burn_drive_grab_stdio(d, 1); return ret; } 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 *buffer_mem = o->drive->buffer; struct burn_session *s; struct burn_track *lt, *t; int first = 1, i, ret, lba, nwa = 0, multi_mem, stream_recording_start; off_t default_size; char msg[80]; /* ts A60924 : libburn/message.c gets obsoleted burn_message_clear_queue(); */ /* ts A61224 */ burn_disc_init_write_status(o, disc); /* must be done very early */ /* ts A80412 , A90227 , B90411 */ if (o->do_stream_recording >= 16) stream_recording_start = o->do_stream_recording; else stream_recording_start = 0; burn_drive_set_stream_recording(d, !!o->do_stream_recording, stream_recording_start, 0); /* ts A91122 : Get buffer suitable for sources made by burn_os_open_track_src() */ d->buffer = burn_os_alloc_buffer(sizeof(struct buffer), 0); if (d->buffer == NULL) goto fail_wo_sync; /* >>> ts A90321 memset(d->buffer, 0, sizeof(struct buffer)); fprintf(stderr, "libburn_DEBUG: d->buffer = %lX , size = %d\n", (unsigned long) d->buffer, (int) sizeof(struct buffer)); calloc() seems not to have the desired effect. valgrind warns: ==18251== Syscall param write(buf) points to uninitialised byte(s) ==18251== at 0x5071DEB: (within /lib64/libpthread-2.5.so) ==18251== by 0x4723FA: burn_stdio_write (write.c:1850) ==18251== by 0x4725DC: burn_stdio_mmc_write (write.c:1894) ==18251== by 0x483B7A: get_sector (sector.c:229) ==18251== by 0x484F11: sector_data (sector.c:639) ==18251== by 0x4729FE: burn_stdio_write_track (write.c:2012) ==18251== by 0x472CF4: burn_stdio_write_sync (write.c:2072) ==18251== by 0x472E8D: burn_disc_write_sync (write.c:2125) <<< we are here ==18251== by 0x460254: write_disc_worker_func (async.c:514) ==18251== by 0x506B09D: start_thread (in /lib64/libpthread-2.5.so) ==18251== by 0x55484CC: clone (in /lib64/libc-2.5.so) */ d->rlba = -150; d->toc_temp = 9; if(d->drive_role == 4) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020181, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Pseudo-drive is a read-only file. Cannot write.", 0, 0); goto fail_wo_sync; } /* ts A70904 */ if (d->drive_role != 1) { ret = burn_stdio_write_sync(o, disc); if (ret <= 0) goto fail_wo_sync; goto ex; } /* ts A61218 */ if (! d->current_is_cd_profile) { ret = burn_dvd_write_sync(o, disc); if (ret <= 0) goto fail_wo_sync; goto ex; } /* ts A70521 : GNU/Linux 2.4 USB audio fails with 64 kiB */ /* ts A80414 : might need 64 kiB for BD-RE streaming */ /* buffer flush trigger for sector.c:get_sector() */ o->obs = Libburn_cd_obS; sprintf(msg, "cd Profile= %2.2Xh , obs= %d , obs_pad= %d", d->current_profile, o->obs, o->obs_pad); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); /* ts A70218 */ if (o->write_type == BURN_WRITE_SAO) { for (i = 0 ; i < disc->session[0]->tracks; i++) { t = disc->session[0]->track[i]; if (burn_track_is_open_ended(t)) { default_size = burn_track_get_default_size(t); sprintf(msg, "Activated track default size %.f", (double) default_size); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002012e, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); burn_track_set_size(t, default_size); } } } /* Apparently some drives require this command to be sent, and a few drives return crap. so we send the command, then ignore the result. */ /* ts A61107 : moved up send_write_parameters because LG GSA-4082B seems to dislike get_nwa() in advance */ d->alba = d->start_lba; /* ts A61114: this looks senseless */ d->nwa = d->alba; if (o->write_type == BURN_WRITE_TAO) { nwa = 0; /* get_nwa() will be called in burn_track() */ } else { if (disc->sessions > 0) s = disc->session[0]; else s = NULL; d->send_write_parameters(d, s, -1, o); ret = d->get_nwa(d, -1, &lba, &nwa); sprintf(msg, "SAO|RAW: Inquired nwa: %d , ret= %d , cap=%.f\n", nwa, ret, (double) d->media_capacity_remaining); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg,0, 0); /* >>> ts A70212 : CD-DAO/SAO : eventually expand size of last track to maximum */; } for (i = 0; i < disc->sessions; i++) { /* update progress */ d->progress.session = i; d->progress.tracks = disc->session[i]->tracks; /* ts A61114: added parameter nwa */ sheet = burn_create_toc_entries(o, disc->session[i], nwa); /* ts A61009 */ if (sheet == NULL) goto fail_wo_sync; #ifdef Libburn_write_with_function_print_cuE print_cue(sheet); /* goto fail_wo_sync; */ #endif /* Libburn_write_with_function_print_cuE */ d->medium_state_changed = 1; ret = 1; if (o->write_type == BURN_WRITE_SAO) ret = d->send_cue_sheet(d, sheet); if (sheet->data != NULL) free(sheet->data); free(sheet); if (ret <= 0) goto fail_wo_sync; /* --- From here on, final sync is needed. --- */ if (o->write_type == BURN_WRITE_RAW) { if (!burn_write_leadin(o, disc->session[i], first)) goto fail; } else { if (first) { /* ts A61030 : 0 made the burner take data. */ /* ts A61103 : Meanwhile d->nwa is updated in burn_write_track() */ if(o->write_type == BURN_WRITE_TAO) { d->nwa= d->alba = 0; } else { #ifdef Libburn_sao_can_appenD /* ts A61114: address for d->write() */ if (d->status == BURN_DISC_APPENDABLE && o->write_type == BURN_WRITE_SAO) { d->nwa = d->alba = nwa-150; sprintf(msg, "SAO appendable d->nwa= %d\n", d->nwa); libdax_msgs_submit( libdax_messenger, d->global_index, 0x00000002, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, msg, 0, 0); } else { d->nwa = -150; d->alba = -150; } #else d->nwa = -150; d->alba = -150; #endif /* ! Libburn_sao_can_appenD */ } } else { d->nwa += 4500; d->alba += 4500; } } multi_mem = o->multi; if(i < disc->sessions - 1) o->multi = 1; ret = burn_write_session(o, disc->session[i]); o->multi = multi_mem; if (!ret) 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 { /* ts A61030 */ if (o->write_type != BURN_WRITE_TAO) if (!burn_write_flush(o, NULL)) 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; } /* ts A61030: extended skipping of flush to TAO: session is closed */ if (o->write_type != BURN_WRITE_SAO && o->write_type != BURN_WRITE_TAO) if (!burn_write_flush(o, NULL)) goto fail; sleep(1); /* ts A61125 : update media state records */ burn_drive_mark_unready(d, 0); burn_drive_inquire_media(d); /* ts A61012 : This return was traditionally missing. I suspect this to have caused Cdrskin_eject() failures */ goto ex; fail: d->sync_cache(d); fail_wo_sync:; usleep(500001); /* ts A61222: to avoid a warning from remove_worker()*/ libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010b, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Burn run failed", 0, 0); d->cancel = 1; /* <<< d->busy = BURN_DRIVE_IDLE; */ ex:; d->do_stream_recording = 0; if (d->buffer != NULL) burn_os_free_buffer((char *) d->buffer, sizeof(struct buffer), 0); d->buffer = buffer_mem; if (d->write_opts != NULL) { burn_write_opts_free(d->write_opts); d->write_opts = NULL; } if (d->write_retry_count > 0) { sprintf(msg, "WRITE command repetition happened %u times", d->write_retry_count); libdax_msgs_submit(libdax_messenger, d->global_index, 0x000201ad, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } return; } /* ts A70811 : API function */ int burn_random_access_write(struct burn_drive *d, off_t byte_address, char *data, off_t data_count, int flag) { int alignment = 0, start, upto, chunksize, err, fd = -1, ret; int do_close = 0, getfl_ret; char msg[81], *rpt; struct buffer *buf = NULL, *buffer_mem = d->buffer; BURN_ALLOC_MEM(buf, struct buffer, 1); if (d->released) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020142, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is not grabbed on random access write", 0, 0); {ret = 0; goto ex;} } if(d->drive_role == 0) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020146, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is a virtual placeholder (null-drive)", 0, 0); {ret = 0; goto ex;} } if(d->drive_role == 4) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020181, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Pseudo-drive is a read-only file. Cannot write.", 0, 0); {ret = 0; goto ex;} } if(d->drive_role == 2 || d->drive_role == 5) alignment = 2 * 1024; if (d->current_profile == 0x12) /* DVD-RAM */ alignment = 2 * 1024; if (d->current_profile == 0x13) /* DVD-RW restricted overwrite */ alignment = 32 * 1024; if (d->current_profile == 0x1a) /* DVD+RW */ alignment = 2 * 1024; if (d->current_profile == 0x43) /* BD-RE */ alignment = 2 * 1024; if (alignment == 0) { sprintf(msg, "Write start address not supported"); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020125, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Write start address not supported", 0, 0); {ret = 0; goto ex;} } if ((byte_address % alignment) != 0) { sprintf(msg, "Write start address not properly aligned (%d bytes)", alignment); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020126, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); {ret = 0; goto ex;} } if ((data_count % alignment) != 0) { sprintf(msg, "Write data count not properly aligned (%ld bytes)", (long) alignment); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020141, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); {ret = 0; goto ex;} } if (d->busy != BURN_DRIVE_IDLE) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020140, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is busy on attempt to write random access",0,0); {ret = 0; goto ex;} } if (d->drive_role != 1) { if (d->stdio_fd >= 0) { /* Avoid to have a read-only fd open */ getfl_ret = fcntl(d->stdio_fd, F_GETFL); if (((O_RDWR | O_WRONLY | O_RDONLY) & getfl_ret) == O_RDONLY) { close(d->stdio_fd); d->stdio_fd = -1; } } if (d->stdio_fd >= 0) { /* Avoid to have two fds open */ fd = d->stdio_fd; } else { fd = burn_stdio_open_write(d, byte_address, 2048, 0); if (fd == -1) {ret = 0; goto ex;} do_close = 1; } } d->cancel = 0; d->busy = BURN_DRIVE_WRITING_SYNC; d->buffer = buf; start = byte_address / 2048; upto = start + data_count / 2048; rpt = data; for (; start < upto; start += 16) { chunksize = upto - start; if (chunksize > 16) chunksize = 16; d->buffer->bytes = chunksize * 2048; memcpy(d->buffer->data, rpt, d->buffer->bytes); rpt += d->buffer->bytes; d->buffer->sectors = chunksize; d->nwa = start; if(d->do_simulate) { err = 0; } else if(d->drive_role == 1) { err = d->write(d, d->nwa, d->buffer); } else { ret = burn_stdio_write(fd, (char *) d->buffer->data, d->buffer->bytes, d, 0); err = 0; if (ret <= 0) err = BE_CANCELLED; } if (err == BE_CANCELLED) { d->busy = BURN_DRIVE_IDLE; if(fd >= 0 && do_close) close(fd); {ret = -(start * 2048 - byte_address); goto ex;} } } if(d->drive_role == 1) d->needs_sync_cache = 1; if(flag & 1) { if(d->do_simulate) { ; } else if(d->drive_role == 1) d->sync_cache(d); else burn_stdio_sync_cache(fd, d, 2); d->needs_sync_cache = 0; } if(fd >= 0 && do_close) close(fd); d->buffer = buffer_mem; d->busy = BURN_DRIVE_IDLE; ret = 1; ex: BURN_FREE_MEM(buf); return ret; } /* ts B10527 */ /* @param bit0= force close, even if no damage was seen */ int burn_disc_close_damaged(struct burn_write_opts *o, int flag) { struct burn_drive *d; int ret; enum burn_drive_status busy; d = o->drive; busy = d->busy; if (busy != BURN_DRIVE_IDLE) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020106, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Drive is busy on attempt to close damaged session", 0, 0); {ret = 0; goto ex;} } if (!((d->next_track_damaged & 1) || (flag & 1))) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020187, LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, "Track not marked as damaged. No action taken.", 0, 0); {ret = 0; goto ex;} } d->busy = BURN_DRIVE_WRITING; if (d->current_profile == 0x09 || d->current_profile == 0x0a) { /* Close CD track and session */ o->write_type = BURN_WRITE_TAO; /* no action without TAO */ /* Send mode page 5 */; d->send_write_parameters(d, NULL, -1, o); ret = burn_write_close_session(o); if (ret <= 0) goto ex; } else if(d->current_profile == 0x11 || d->current_profile == 0x14) { /* Close DVD-R[W] track and session */ o->write_type = BURN_WRITE_TAO; /* no action without TAO */ /* Send mode page 5 */; d->send_write_parameters(d, NULL, -1, o); ret = burn_disc_close_track_dvd_minus_r(o, 0); if (ret <= 0) goto ex; ret = burn_disc_close_session_dvd_minus_r(o); if (ret <= 0) goto ex; } else if(d->current_profile == 0x1b || d->current_profile == 0x2b) { /* Close DVD+R track and session */ ret = burn_disc_close_track_dvd_plus_r(o, d->last_track_no, 1); if (ret <= 0) goto ex; } else if(d->current_profile == 0x41) { /* Close BD-R track and session */ ret = burn_disc_close_track_dvd_plus_r(o, d->last_track_no, 1); if (ret <= 0) goto ex; } else { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020188, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Cannot close damaged track on given media type", 0, 0); {ret = 0; goto ex;} } ret = 1; ex:; d->busy = busy; /* Record with drive that repair was attempted */ d->next_track_damaged &= ~1; return ret; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2010 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #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 nwa); int burn_sector_length(int trackmode); int burn_subcode_length(int trackmode); /* ts A61009 */ int burn_disc_write_is_ok(struct burn_write_opts *o, struct burn_disc *disc, int flag); 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, struct burn_track *track); /* ts A61030 : necessary for TAO */ int burn_write_close_track(struct burn_write_opts *o, struct burn_session *s, int tnum); int burn_write_close_session(struct burn_write_opts *o); /* @param flag bit0= repair checksum bit1= repair checksum if all pack CRCs are 0 @return 0= no mismatch , >0 number of unrepaired mismatches <0 number of repaired mismatches */ int burn_cdtext_crc_mismatches(unsigned char *packs, int num_packs, int flag); /* mmc5r03c.pdf 6.3.3.3.3: DVD-R DL: Close Function 010b: Close Session "When the recording mode is Incremental Recording, the disc is single session." Enable this macro to get away from growisofs which uses Close Session but also states "// DVD-R DL Seq has no notion of multi-session". #define Libburn_dvd_r_dl_multi_no_close_sessioN 1 */ #endif /* BURN__WRITE_H */ #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <sys/stat.h> #include "smem.h" char *Sfile_fgets(); int Sregex_string(); int Sregex_trimline(); #include "ctyp.h" #include "cgen.h" /* ----------------------------- CgeN ------------------------- */ int Cgen_new(cgen,flag) struct CgeN **cgen; int flag; { int ret; struct CgeN *c; *cgen= c= TSOB_FELD(struct CgeN,1); if(c==NULL) { fprintf(stderr,"+++ Cannot create cgen object : %s\n",strerror(errno)); return(-1); } c->classname= NULL; c->structname= NULL; c->functname= NULL; c->is_managed_list= 0; c->is_bossless_list= 0; c->gen_for_stic= 1; c->make_ansi= 0; c->make_lowercase= 0; c->global_include_file[0]= 0; c->global_include_fp= NULL; c->elements= NULL; c->last_element= NULL; c->may_overwrite= 0; c->fp= NULL; c->filename[0]= 0; c->ptt_fp= NULL; c->ptt_filename[0]= 0; c->msg[0]= 0; return(1); } int Cgen_destroy(cgen,flag) struct CgeN **cgen; int flag; { struct CgeN *c; struct CtyP *ct,*next_ct; c= *cgen; if(c==NULL) return(0); if(c->fp!=NULL) fclose(c->fp); if(c->ptt_fp!=NULL) fclose(c->ptt_fp); Sregex_string(&(c->classname),NULL,0); Sregex_string(&(c->structname),NULL,0); Sregex_string(&(c->functname),NULL,0); for(ct= c->elements; ct!=NULL; ct= next_ct) { next_ct= ct->next; Ctyp_destroy(&ct,0); } free((char *) c); *cgen= NULL; return(1); } int Cgen_make_names(cgen,flag) struct CgeN *cgen; int flag; { int l; if(Sregex_string(&(cgen->structname),cgen->classname,0)<=0) return(-1); if(Sregex_string(&(cgen->functname),cgen->classname,0)<=0) return(-1); if(!cgen->make_lowercase) { cgen->structname[0]= toupper(cgen->structname[0]); l= strlen(cgen->structname); cgen->structname[l-1]= toupper(cgen->structname[l-1]); cgen->functname[0]= toupper(cgen->functname[0]); } return(1); } int Cgen_read_fp(cgen,fp,flag) struct CgeN *cgen; FILE *fp; int flag; /* bit0= return 0 if eof at classname */ { char line[4096],*cpt,*bpt; int ret; line[0]= 0; while(1) { printf("[-list] classname ?\n"); if(Sfile_fgets(line,sizeof(line)-1,fp)==NULL) { if(!(flag&1)) return(2); no_name:; sprintf(cgen->msg,"No classname given."); return(0); } printf("%s\n",line); if(strcmp(line,"@@@")==0) return(2); if(line[0]==0 || line[0]=='#') { /* >>> record class comments */; } else break; } cpt= line; while(cpt[0]=='-') { /* look for management specifiers: -l* listable by prev-next chain */ if(cpt[1]=='l' || cpt[1]=='L') { cgen->is_managed_list= 1; } else if(cpt[1]=='b' || cpt[1]=='B') { cgen->is_bossless_list= 1; } while(*cpt!=0 && !isspace(*cpt)) cpt++; while(*cpt!=0 && isspace(*cpt)) cpt++; if(*cpt==0) goto no_name; } if(Sregex_string(&(cgen->classname),cpt,0)<=0) return(-1); ret= Cgen_make_names(cgen,0); if(ret<=0) return(ret); while(1) { ret= Ctyp_read_fp(&(cgen->last_element),fp,cgen->msg, !!cgen->make_lowercase); if(ret<=0) return(ret); if(ret==2) break; if(cgen->elements==NULL) cgen->elements= cgen->last_element; } if(cgen->is_managed_list) { sprintf(line,"-c struct %s *prev",cgen->structname); ret= Ctyp_new_from_line(&(cgen->last_element),cgen->last_element, line,cgen->msg,0); if(ret<=0) return(ret); if(cgen->elements==NULL) cgen->elements= cgen->last_element; sprintf(line,"-c struct %s *next",cgen->structname); ret= Ctyp_new_from_line(&(cgen->last_element),cgen->last_element, line,cgen->msg,0); if(ret<=0) return(ret); } return(1); } int Cgen_open_wfile(cgen,flag) struct CgeN *cgen; int flag; /* bit0-3: modes 0= open cgen->fp 1= open cgen->ptt_fp 2= open cgen->global_include_fp */ { struct stat stbuf; int ret, mode; char *name, fmode[4]; FILE *fp; mode= flag&15; strcpy(fmode,"w"); if(mode==0) { name= cgen->filename; fp= cgen->fp; cgen->fp= NULL; } else if(mode==1) { name= cgen->ptt_filename; fp= cgen->ptt_fp; cgen->ptt_fp= NULL; } else if(mode==2) { strcpy(fmode,"a"); name= cgen->global_include_file; fp= cgen->global_include_fp; cgen->global_include_fp= NULL; } else { fprintf(stderr,"+++ Cgen_open_wfile : program error : unknown mode %d\n", mode); ret= -1; goto ex; } if(fmode[0]=='w' && stat(name,&stbuf)!=-1 && !cgen->may_overwrite) { sprintf(cgen->msg,"File '%s' already existing.",name); ret= 0; goto ex; } if(fp!=NULL) {fclose(fp); fp= NULL;} fp= fopen(name,fmode); if(fp==NULL) { sprintf(cgen->msg,"Cannot open file '%s' in %s-mode. %s", name,fmode,strerror(errno)); ret= 0; goto ex; } ret= 1; ex:; if(mode==0) cgen->fp= fp; else if(mode==1) cgen->ptt_fp= fp; else if(mode==2) cgen->global_include_fp= fp; return(ret); } int Cgen_write_datestr(cgen,flag) struct CgeN *cgen; int flag; /* bit0= operate on ptt (= ANSI prototype) file rather than on internal header */ { time_t t0; char timetext[81]; FILE *fp; if(flag&1) fp= cgen->ptt_fp; else fp= cgen->fp; t0= time(0); strftime(timetext,sizeof(timetext),"%a, %d %b %Y %H:%M:%S GMT", gmtime(&t0)); fprintf(fp,"/* ( derived from stub generated by CgeN on %s ) */\n", timetext); return(1); } int Cgen_write_h(cgen,flag) struct CgeN *cgen; int flag; { int ret,i,pointer_level; FILE *fp= NULL; struct CtyP *ct; char pvt[16],macro_name[4096],*cpt; if(cgen->make_ansi) { sprintf(cgen->filename,"%s_private.h",cgen->classname); strcpy(pvt,"_private"); } else { sprintf(cgen->filename,"%s.h",cgen->classname); strcpy(pvt,""); } ret= Cgen_open_wfile(cgen,0); if(ret<=0) goto ex; sprintf(macro_name,"%s%s_includeD",cgen->functname,pvt); macro_name[0]= toupper(macro_name[0]); fp= cgen->fp; /* >>> print class comments */; fprintf(fp,"\n"); fprintf(fp,"#ifndef %s\n",macro_name); fprintf(fp,"#define %s\n",macro_name); fprintf(fp,"\n"); if(strlen(cgen->global_include_file)!=0) { fprintf(fp,"#include \"%s\"\n",cgen->global_include_file); fprintf(fp,"\n\n"); } if(cgen->make_ansi) fprintf(fp,"/* For function prototypes see file %s.h */\n",cgen->classname); fprintf(fp,"\n\n"); fprintf(fp,"struct %s {\n",cgen->structname); fprintf(fp,"\n"); ct= cgen->elements; for(ct= cgen->elements;ct!=NULL;ct= ct->next) { if(ct->is_comment) { if(ct->name[0]==0) { fprintf(fp,"\n"); continue; } fprintf(fp," /* "); for(cpt= ct->name; *cpt!=0; cpt++) { fprintf(fp,"%c",*cpt); if(cpt[0]=='*' && cpt[1]=='/') fprintf(fp," "); } fprintf(fp," */\n"); continue; } if(ct->is_volatile) fprintf(fp," volatile"); if(Ctyp_is_struct(ct,0)) fprintf(fp," struct"); else if(ct->is_unsigned) fprintf(fp," unsigned"); fprintf(fp," %s ",ct->dtype); pointer_level= Ctyp_get_pointer_level(ct,0); for(i=0;i<pointer_level;i++) fprintf(fp,"*"); fprintf(fp,"%s",ct->name); if(ct->array_size>0) fprintf(fp,"[%lu]",ct->array_size); fprintf(fp,";\n"); } fprintf(fp,"\n"); fprintf(fp,"};\n"); fprintf(fp,"\n"); fprintf(fp,"\n"); fprintf(fp,"#endif /* %s */\n",macro_name); fprintf(fp,"\n"); Cgen_write_datestr(cgen,0); /* Eventually write start of ANSI prototype include file */ if(!cgen->make_ansi) goto after_ansi_h; sprintf(cgen->ptt_filename,"%s.h",cgen->classname); ret= Cgen_open_wfile(cgen,1); if(ret<=0) goto ex; sprintf(macro_name,"%s_includeD",cgen->functname); macro_name[0]= toupper(macro_name[0]); fp= cgen->ptt_fp; /* >>> print class comments */; fprintf(fp,"\n"); fprintf(fp,"#ifndef %s\n",macro_name); fprintf(fp,"#define %s\n",macro_name); fprintf(fp,"\n\n"); if(strlen(cgen->global_include_file)!=0) { fprintf(fp,"#include \"%s\"\n",cgen->global_include_file); } else { fprintf(fp,"struct %s;\n",cgen->structname); } fprintf(fp,"\n\n"); fprintf(fp,"/* For inner details see file %s_private.h */\n",cgen->classname); fprintf(fp,"\n\n"); after_ansi_h:; if(strlen(cgen->global_include_file)==0) goto after_global_include; ret= Cgen_open_wfile(cgen,2); if(ret<=0) goto ex; fprintf(cgen->global_include_fp,"struct %s;\n",cgen->structname); after_global_include:; ret= 1; ex:; if(cgen->fp!=NULL) {fclose(cgen->fp); cgen->fp= NULL;} /* ( note: cgen->ptt_fp stays open ) */ if(cgen->global_include_fp!=NULL) {fclose(cgen->global_include_fp); cgen->global_include_fp= NULL;} return(ret); } int Cgen_write_to_ptt(cgen,ptt,flag) struct CgeN *cgen; char *ptt; int flag; { if(cgen->ptt_fp==NULL) return(-1); fprintf(cgen->ptt_fp,"%s;\n",ptt); return(1); } int Cgen_finish_public_h(cgen,flag) struct CgeN *cgen; int flag; { char macro_name[4096]; if(cgen->ptt_fp==NULL) return(-1); fprintf(cgen->ptt_fp,"\n"); fprintf(cgen->ptt_fp,"\n"); sprintf(macro_name,"%s_includeD",cgen->functname); macro_name[0]= toupper(macro_name[0]); fprintf(cgen->ptt_fp,"#endif /* %s */\n",macro_name); fprintf(cgen->ptt_fp,"\n"); Cgen_write_datestr(cgen,1); if(cgen->ptt_fp!=NULL) {fclose(cgen->ptt_fp); cgen->ptt_fp= NULL;} return(1); } int Cgen_write_c_head(cgen,flag) struct CgeN *cgen; int flag; { int ret,is_pointer,is_struct,array_size; FILE *fp= NULL; struct CtyP *ct,*hct; char *dtype= NULL,*name= NULL; fp= cgen->fp; fprintf(fp,"\n"); fprintf(fp,"/*\n"); fprintf(fp," cc -g -c %s.c\n",cgen->classname); fprintf(fp,"*/\n"); Cgen_write_datestr(cgen,0); fprintf(fp,"\n"); fprintf(fp,"#include <sys/types.h>\n"); fprintf(fp,"#include <stdlib.h>\n"); fprintf(fp,"#include <stdio.h>\n"); fprintf(fp,"#include <string.h>\n"); fprintf(fp,"#include <errno.h>\n"); fprintf(fp,"\n"); fprintf(fp,"#include \"%s.h\"\n",cgen->classname); if(cgen->make_ansi) { fprintf(fp,"#include \"%s_private.h\"\n",cgen->classname); } fprintf(fp,"\n"); for(ct= cgen->elements; ct!=NULL; ct= ct->next) { if(ct->is_comment) continue; Ctyp_get_dtype(ct,&dtype,0); Ctyp_get_type_mod(ct,&is_pointer,&is_struct,&array_size,0); /* fprintf(stderr,"DEBUG: %s %s\n",(is_struct?"struct ":""),dtype); */ /* already included ? */ if(strcmp(dtype,cgen->structname)==0) continue; for(hct= cgen->elements; hct!=NULL && hct!=ct; hct= hct->next) { if(hct->is_comment) continue; if(hct->dtype!=NULL) if(strcmp(hct->dtype,dtype)==0) break; } if(hct!=ct && hct!=NULL) continue; if(is_struct && (isupper(dtype[0]) && isupper(dtype[strlen(dtype)-1]))) { dtype[0]= tolower(dtype[0]); dtype[strlen(dtype)-1]= tolower(dtype[strlen(dtype)-1]); fprintf(fp,"#include \"%s.h\"\n",dtype); } } fprintf(fp,"\n"); if(cgen->gen_for_stic==1) { fprintf(fp,"#include \"../s_tools/smem.h\"\n"); fprintf(fp,"#include \"../s_tools/sfile.h\"\n"); fprintf(fp,"#include \"../s_tools/sregex.h\"\n"); fprintf(fp,"\n"); } else if(cgen->gen_for_stic==2) { fprintf(fp,"#include \"smem.h\"\n"); fprintf(fp,"\n"); } fprintf(fp,"\n"); fprintf(fp,"/* -------------------------- %s ----------------------- */\n", cgen->structname); fprintf(fp,"\n"); if(dtype!=NULL) Sregex_string(&dtype,NULL,0); if(name!=NULL) Sregex_string(&name,NULL,0); return(1); } int Cgen_write_c_new(cgen,flag) struct CgeN *cgen; int flag; { int ret,pointer_level,management,boss_parm= 0; unsigned long array_size; FILE *fp= NULL; struct CtyP *ct; char ptt[4096]; fp= cgen->fp; if(!cgen->is_bossless_list) { if(cgen->elements!=NULL) if(strcmp(cgen->elements->name,"boss")==0 && cgen->elements->is_struct && cgen->elements->is_pointer==1 && cgen->elements->no_initializer==0) boss_parm= 1; if(cgen->is_managed_list && boss_parm==0) fprintf(stderr, "+++ Warning: -l %s without -v struct ... *boss as first attribute\n", cgen->classname); } fprintf(fp,"\n"); if(cgen->make_ansi) { sprintf(ptt,"int %s_new(struct %s **objpt, ", cgen->functname,cgen->structname); if(boss_parm) sprintf(ptt+strlen(ptt),"struct %s *boss, ",cgen->elements->dtype); sprintf(ptt+strlen(ptt),"int flag)"); fprintf(fp,"%s\n",ptt); ret= Cgen_write_to_ptt(cgen, ptt, 0); if(ret<=0) return(ret); } else { fprintf(fp,"int %s_new(objpt,\n",cgen->functname); if(boss_parm) fprintf(fp,"boss,"); fprintf(fp,"flag)\n"); fprintf(fp,"struct %s **objpt;\n",cgen->structname); if(boss_parm) fprintf(fp,"struct %s *boss;",cgen->elements->dtype); fprintf(fp,"int flag;\n"); } fprintf(fp,"{\n"); fprintf(fp," struct %s *o;\n",cgen->structname); /* Is an array index i needed ? */ for(ct= cgen->elements; ct!=NULL; ct= ct->next) { if(ct->is_comment || ct->no_initializer) continue; if(ct->array_size>0) if(strcmp(ct->dtype,"char")!=0) { fprintf(fp," int i;\n"); break; } } fprintf(fp,"\n"); if(cgen->gen_for_stic) fprintf(fp," *objpt= o= TSOB_FELD(struct %s,1);\n",cgen->structname); else fprintf(fp," *objpt= o= (struct %s *) malloc(sizeof(struct %s));\n", cgen->structname, cgen->structname); fprintf(fp," if(o==NULL)\n"); fprintf(fp," return(-1);\n"); fprintf(fp,"\n"); for(ct= cgen->elements; ct!=NULL; ct= ct->next) { if(ct->is_comment || ct->no_initializer) continue; array_size= Ctyp_get_array_size(ct,0); pointer_level= Ctyp_get_pointer_level(ct,0); if(ct==cgen->elements && boss_parm) { fprintf(fp," o->boss= boss;\n"); } else if(array_size>0) { if(strcmp(ct->dtype,"char")==0) { fprintf(fp," o->%s[0]= 0;\n;",ct->name); } else if(pointer_level>0) { fprintf(fp," for(i=0;i<%lu;i++)\n",array_size); fprintf(fp," o->%s[i]= NULL;\n",ct->name); } else { fprintf(fp," for(i=0;i<%lu;i++)\n",array_size); fprintf(fp," o->%s[i]= 0;\n",ct->name); } } else if(pointer_level>0) { fprintf(fp," o->%s= NULL;\n",ct->name); } else fprintf(fp," o->%s= 0;\n",ct->name); } fprintf(fp,"\n"); fprintf(fp," return(1);\n"); fprintf(fp,"/*\n"); fprintf(fp,"failed:;\n"); fprintf(fp," %s_destroy(objpt,0);\n",cgen->functname); fprintf(fp," return(-1);\n"); fprintf(fp,"*/\n"); fprintf(fp,"}\n"); fprintf(fp,"\n"); for(ct= cgen->elements; ct!=NULL; ct= ct->next) { if(ct->is_comment) continue; management= Ctyp_get_management(ct,0); if(management==4) { if(ct->next==NULL) { no_last_pt:; sprintf(cgen->msg, "Lonely -l found. A -v of same type must follow.\nName is : %s", ct->name); return(0); } if(strcmp(ct->next->dtype,ct->dtype)!=0 || ct->next->is_pointer!=ct->is_pointer) goto no_last_pt; ct->next->with_getter= ct->next->with_setter= 0; ret= Cgen_write_c_new_type(cgen,ct,ct->next,0); if(ret<=0) return(ret); } } return(1); } int Cgen_write_c_new_type(cgen,ct_first,ct_last,flag) struct CgeN *cgen; struct CtyP *ct_first,*ct_last; int flag; { int ret,l,management,pointer_level,i; FILE *fp= NULL; char funct[4096],classname[4096],*npt,ptt[4096]; strcpy(funct,ct_first->dtype); strcpy(classname,funct); l= strlen(funct); if(l>0) { if(cgen->make_lowercase) funct[0]= tolower(funct[0]); else funct[0]= toupper(funct[0]); funct[l-1]= tolower(funct[l-1]); classname[0]= tolower(funct[0]); classname[l-1]= funct[l-1]; } fp= cgen->fp; fprintf(fp,"\n"); if(cgen->make_ansi) { sprintf(ptt, "int %s_new_%s(struct %s *o, int flag)", cgen->functname,ct_first->name,cgen->structname); fprintf(fp,"%s\n",ptt); if(ct_first->with_setter) { ret= Cgen_write_to_ptt(cgen, ptt, 0); if(ret<=0) return(ret); } } else { fprintf(fp,"int %s_new_%s(o,flag)\n",cgen->functname,ct_first->name); fprintf(fp,"struct %s *o;\n",cgen->structname); fprintf(fp,"int flag;\n"); } fprintf(fp,"{\n"); fprintf(fp," int ret;\n"); fprintf(fp," struct %s *c= NULL;\n",ct_first->dtype); fprintf(fp,"\n"); if(ct_first->bossless_list) fprintf(fp," ret= %s_new(&c,0);\n",funct); else fprintf(fp," ret= %s_new(&c,o,0);\n",funct); fprintf(fp," if(ret<=0)\n"); fprintf(fp," return(ret);\n"); fprintf(fp," %s_link(c,o->%s,0);\n",funct,ct_last->name); fprintf(fp," o->%s= c;\n",ct_last->name); fprintf(fp," if(o->%s==NULL)\n",ct_first->name); fprintf(fp," o->%s= c;\n",ct_first->name); fprintf(fp," return(1);\n"); fprintf(fp,"}\n"); fprintf(fp,"\n"); ret= 1; ex:; return(ret); } int Cgen_write_c_destroy(cgen,flag) struct CgeN *cgen; int flag; { int ret,l,management,pointer_level,i; FILE *fp= NULL; struct CtyP *ct,*next; char funct[4096],*npt,ptt[4096]; fp= cgen->fp; fprintf(fp,"\n"); if(cgen->make_ansi) { sprintf(ptt, "int %s_destroy(struct %s **objpt, int flag)", cgen->functname,cgen->structname); fprintf(fp,"%s\n",ptt); ret= Cgen_write_to_ptt(cgen, ptt, 0); if(ret<=0) return(ret); } else { fprintf(fp,"int %s_destroy(objpt,flag)\n",cgen->functname); fprintf(fp,"struct %s **objpt;\n",cgen->structname); fprintf(fp,"int flag;\n"); } fprintf(fp,"{\n"); fprintf(fp," struct %s *o;\n",cgen->structname); fprintf(fp,"\n"); fprintf(fp," o= *objpt;\n"); fprintf(fp," if(o==NULL)\n"); fprintf(fp," return(0);\n"); fprintf(fp,"\n"); for(ct= cgen->elements; ct!=NULL; ct= ct->next) { if(ct->is_comment) continue; management= Ctyp_get_management(ct,0); if(management==1 || management==4) { strcpy(funct,ct->dtype); l= strlen(funct); if(l>0) { if(cgen->make_lowercase) funct[0]= tolower(funct[0]); else funct[0]= toupper(funct[0]); funct[l-1]= tolower(funct[l-1]); } if(strcmp(ct->dtype,"char")==0) { if(cgen->gen_for_stic==1) fprintf(fp," Sregex_string("); else if(cgen->gen_for_stic==2) fprintf(fp," Smem_freE((char *) "); else fprintf(fp," free("); } else if(strcmp(ct->dtype,"LstrinG")==0 || management==4) fprintf(fp," %s_destroy_all(",funct); else fprintf(fp," %s_destroy(",funct); pointer_level= Ctyp_get_pointer_level(ct,0)-2; for(i=0; i>pointer_level; i--) fprintf(fp,"&"); for(i=0; i<pointer_level; i++) fprintf(fp,"*"); fprintf(fp,"(o->%s)",ct->name); if(strcmp(ct->dtype,"char")==0) { if(cgen->gen_for_stic==1) fprintf(fp,",NULL,0);\n"); else fprintf(fp,");\n"); } else fprintf(fp,",0);\n"); } else if(management==2) { next= ct->next; if(next==NULL) { broken_chain:; sprintf(cgen->msg, "Lonely -c found. They have to appear in pairs.\nName is : %s", ct->name); ret= 0; goto ex; } if(next->management!=3) goto broken_chain; fprintf(fp," if(o->%s!=NULL)\n",ct->name); fprintf(fp," o->%s->%s= o->%s;\n",ct->name,next->name,next->name); fprintf(fp," if(o->%s!=NULL)\n",next->name); fprintf(fp," o->%s->%s= o->%s;\n",next->name,ct->name,ct->name); ct= next; } } fprintf(fp,"\n"); if(cgen->gen_for_stic) fprintf(fp," Smem_freE((char *) o);\n"); else fprintf(fp," free((char *) o);\n"); fprintf(fp," *objpt= NULL;\n"); fprintf(fp," return(1);\n"); fprintf(fp,"}\n"); fprintf(fp,"\n"); if(cgen->is_managed_list){ ret= Cgen_write_c_destroy_all(cgen,0); if(ret<=0) goto ex; } ret= 1; ex:; return(ret); } int Cgen_write_c_destroy_all(cgen,flag) struct CgeN *cgen; int flag; { int ret,l,management,pointer_level,i; FILE *fp= NULL; struct CtyP *ct; char ptt[4096]; fp= cgen->fp; fprintf(fp,"\n"); if(cgen->make_ansi) { sprintf(ptt, "int %s_destroy_all(struct %s **objpt, int flag)", cgen->functname, cgen->structname); fprintf(fp,"%s\n",ptt); ret= Cgen_write_to_ptt(cgen, ptt, 0); if(ret<=0) return(ret); } else { fprintf(fp,"int %s_destroy_all(objpt,flag)\n",cgen->functname); fprintf(fp,"struct %s **objpt;\n",cgen->structname); fprintf(fp,"int flag;\n"); } fprintf(fp,"{\n"); fprintf(fp," struct %s *o,*n;\n",cgen->structname); fprintf(fp,"\n"); fprintf(fp," o= *objpt;\n"); fprintf(fp," if(o==NULL)\n"); fprintf(fp," return(0);\n"); fprintf(fp," for(;o->prev!=NULL;o= o->prev);\n"); fprintf(fp," for(;o!=NULL;o= n) {\n"); fprintf(fp," n= o->next;\n"); fprintf(fp," %s_destroy(&o,0);\n",cgen->functname); fprintf(fp," }\n"); fprintf(fp," *objpt= NULL;\n"); fprintf(fp," return(1);\n"); fprintf(fp,"}\n"); fprintf(fp,"\n"); ret= 1; ex:; return(ret); } int Cgen_write_c_access(cgen,flag) struct CgeN *cgen; int flag; { int ret,l,mgt,pointer_level,i; FILE *fp= NULL; struct CtyP *ct; char funct[4096],*npt,ptt[4096]; fp= cgen->fp; for(ct= cgen->elements; ct!=NULL; ct= ct->next) { if(ct->is_comment) continue; pointer_level= Ctyp_get_pointer_level(ct,0); if(Ctyp_get_with_getter(ct,0)<=0) goto after_getter; fprintf(fp,"\n"); if(cgen->make_ansi) { sprintf(ptt, "int %s_get_%s(struct %s *o, ", cgen->functname,ct->name,cgen->structname); if(Ctyp_is_struct(ct,0)) strcat(ptt,"struct "); strcat(ptt,ct->dtype); strcat(ptt," "); for(i=0; i<pointer_level+1; i++) strcat(ptt,"*"); if(Ctyp_get_array_size(ct,0)>0) strcat(ptt,"*"); strcat(ptt,"pt"); if(ct->management==4) strcat(ptt,", int idx"); strcat(ptt,", int flag)"); fprintf(fp,"%s\n",ptt); ret= Cgen_write_to_ptt(cgen, ptt, 0); if(ret<=0) return(ret); } else { fprintf(fp,"int %s_get_%s(o,pt",cgen->functname,ct->name); if(ct->management==4) fprintf(fp,",idx"); fprintf(fp,",flag)\n"); fprintf(fp,"struct %s *o;\n",cgen->structname); if(Ctyp_is_struct(ct,0)) fprintf(fp,"struct "); fprintf(fp,"%s ",ct->dtype); for(i=0; i<pointer_level+1; i++) fprintf(fp,"*"); if(Ctyp_get_array_size(ct,0)>0) fprintf(fp,"*"); fprintf(fp,"pt;\n"); if(ct->management==4) fprintf(fp,"int idx;\n"); fprintf(fp,"int flag;\n"); } if(ct->management==4) fprintf(fp,"/* Note: idx==-1 fetches the last item of the list */\n"); fprintf(fp,"{\n"); if(ct->management==4) { strcpy(funct,ct->dtype); l= strlen(funct); if(cgen->make_lowercase) funct[0]= tolower(funct[0]); if(l>1) funct[l-1]= tolower(funct[l-1]); fprintf(fp," if(idx==-1) {\n"); fprintf(fp," *pt= o->%s;\n",ct->next->name); fprintf(fp," return(*pt!=NULL);\n"); fprintf(fp," }\n"); fprintf(fp," return(%s_by_idx(o->%s,(flag&1?1:idx),pt,flag&1));\n", funct,ct->name); } else { fprintf(fp," *pt= o->%s;\n",ct->name); fprintf(fp," return(1);\n"); } fprintf(fp,"}\n"); fprintf(fp,"\n"); after_getter:; if(Ctyp_get_with_setter(ct,0)<=0) goto after_setter; /* <<< provisory : develop a setter for arrays */ if(Ctyp_get_array_size(ct,0)>0) goto after_setter; mgt= Ctyp_get_management(ct,0); if(mgt==0 || (mgt==1 && pointer_level==1)) { /* -value or -managed pointers */ /* was: -value or -managed char * */ /* (mgt==1 && strcmp(ct->dtype,"char")==0 && pointer_level==1)) { */ fprintf(fp,"\n"); if(cgen->make_ansi) { sprintf(ptt, "int %s_set_%s(struct %s *o, ", cgen->functname,ct->name,cgen->structname); if(Ctyp_is_struct(ct,0)) strcat(ptt,"struct "); strcat(ptt,ct->dtype); strcat(ptt," "); for(i=0; i<pointer_level; i++) strcat(ptt,"*"); strcat(ptt,"value, int flag)"); fprintf(fp,"%s\n",ptt); ret= Cgen_write_to_ptt(cgen, ptt, 0); if(ret<=0) return(ret); } else { fprintf(fp,"int %s_set_%s(o,value,flag)\n",cgen->functname,ct->name); fprintf(fp,"struct %s *o;\n",cgen->structname); if(Ctyp_is_struct(ct,0)) fprintf(fp,"struct "); fprintf(fp,"%s ",ct->dtype); for(i=0; i<pointer_level; i++) fprintf(fp,"*"); fprintf(fp,"value;\n"); fprintf(fp,"int flag;\n"); } fprintf(fp,"{\n"); if(mgt==1 && strcmp(ct->dtype,"char")==0) { if(cgen->gen_for_stic==1) { fprintf(fp," if(Sregex_string(&(o->%s),value,0)<=0)\n",ct->name); fprintf(fp," return(-1);\n"); } else if(cgen->gen_for_stic==2) { fprintf(fp," if(Smem_clone_string(&(o->%s),value)<=0)\n",ct->name); fprintf(fp," return(-1);\n"); } else { fprintf(fp," char *cpt;\n"); fprintf(fp,"\n"); fprintf(fp," cpt= malloc(strlen(value)+1);\n"); fprintf(fp," if(cpt==NULL)\n"); fprintf(fp," return(-1);\n"); fprintf(fp," o->%s= cpt;\n",ct->name); fprintf(fp," \n"); } } else { fprintf(fp," o->%s= value;\n",ct->name); } fprintf(fp," return(1);\n"); fprintf(fp,"}\n"); fprintf(fp,"\n"); } after_setter:; } if(cgen->is_managed_list) { fprintf(fp,"\n"); if(cgen->make_ansi) { sprintf(ptt,"int %s_link(struct %s *o, struct %s *link, int flag)", cgen->functname,cgen->structname,cgen->structname); fprintf(fp,"%s\n",ptt); /* if(cgen->readonly) */ { ret= Cgen_write_to_ptt(cgen, ptt, 0); if(ret<=0) return(ret); } } else { fprintf(fp,"int %s_link(o,link,flag)\n",cgen->functname); fprintf(fp,"struct %s *o;\n",cgen->structname); fprintf(fp,"struct %s *link;\n",cgen->structname); fprintf(fp,"int flag;\n"); } fprintf(fp,"/*\n"); fprintf(fp," bit0= insert as link->prev rather than as link->next\n"); fprintf(fp,"*/\n"); fprintf(fp,"{\n"); fprintf(fp," if(o->prev!=NULL)\n"); fprintf(fp," o->prev->next= o->next;\n"); fprintf(fp," if(o->next!=NULL)\n"); fprintf(fp," o->next->prev= o->prev;\n"); fprintf(fp," o->prev= o->next= NULL;\n"); fprintf(fp," if(link==NULL)\n"); fprintf(fp," return(1);\n"); fprintf(fp," if(flag&1) {\n"); fprintf(fp," o->next= link;\n"); fprintf(fp," o->prev= link->prev;\n"); fprintf(fp," if(o->prev!=NULL)\n"); fprintf(fp," o->prev->next= o;\n"); fprintf(fp," link->prev= o;\n"); fprintf(fp," } else {\n"); fprintf(fp," o->prev= link;\n"); fprintf(fp," o->next= link->next;\n"); fprintf(fp," if(o->next!=NULL)\n"); fprintf(fp," o->next->prev= o;\n"); fprintf(fp," link->next= o;\n"); fprintf(fp," }\n"); fprintf(fp," return(1);\n"); fprintf(fp,"}\n"); fprintf(fp,"\n"); fprintf(fp,"\n"); if(cgen->make_ansi) { sprintf(ptt,"int %s_count(struct %s *o, int flag)", cgen->functname,cgen->structname); fprintf(fp,"%s\n",ptt); ret= Cgen_write_to_ptt(cgen, ptt, 0); if(ret<=0) return(ret); } else { fprintf(fp,"int %s_count(o,flag)\n",cgen->functname); fprintf(fp,"struct %s *o;\n",cgen->structname); fprintf(fp,"int flag;\n"); } fprintf(fp,"/* flag: bit1= count from start of list */\n"); fprintf(fp,"{\n"); fprintf(fp," int counter= 0;\n"); fprintf(fp,"\n"); fprintf(fp," if(flag&2)\n"); fprintf(fp," for(;o->prev!=NULL;o= o->prev);\n"); fprintf(fp," for(;o!=NULL;o= o->next)\n"); fprintf(fp," counter++;\n"); fprintf(fp," return(counter);\n"); fprintf(fp,"}\n"); fprintf(fp,"\n"); fprintf(fp,"\n"); if(cgen->make_ansi) { sprintf(ptt, "int %s_by_idx(struct %s *o, int idx, struct %s **pt, int flag)", cgen->functname,cgen->structname,cgen->structname); fprintf(fp,"%s\n",ptt); ret= Cgen_write_to_ptt(cgen, ptt, 0); if(ret<=0) return(ret); } else { fprintf(fp,"int %s_count(o,idx,pt,flag)\n",cgen->functname); fprintf(fp,"struct %s *o;\n",cgen->structname); fprintf(fp,"int idx;\n"); fprintf(fp,"struct %s **pt;\n",cgen->structname); fprintf(fp,"int flag;\n"); } fprintf(fp, "/* flag: bit0= fetch first (idx<0) or last (idx>0) item in list\n"); fprintf(fp, " bit1= address from start of list */\n"); fprintf(fp,"{\n"); fprintf(fp," int i,abs_idx;\n"); fprintf(fp," struct %s *npt;\n",cgen->structname); fprintf(fp,"\n"); fprintf(fp," if(flag&2)\n"); fprintf(fp," for(;o->prev!=NULL;o= o->prev);\n"); fprintf(fp," abs_idx= (idx>0?idx:-idx);\n"); fprintf(fp," *pt= o;\n"); fprintf(fp," for(i= 0;(i<abs_idx || (flag&1)) && *pt!=NULL;i++) {\n"); fprintf(fp," if(idx>0)\n"); fprintf(fp," npt= o->next;\n"); fprintf(fp," else\n"); fprintf(fp," npt= o->prev;\n"); fprintf(fp," if(npt==NULL && (flag&1))\n"); fprintf(fp," break;\n"); fprintf(fp," *pt= npt;\n"); fprintf(fp," }\n"); fprintf(fp," return(*pt!=NULL);\n"); fprintf(fp,"}\n"); fprintf(fp,"\n"); } return(1); } int Cgen_write_c_method_include(cgen,flag) struct CgeN *cgen; int flag; { FILE *fp= NULL; char filename[4096],line[4096]; struct stat stbuf; time_t t0; sprintf(filename,"%s.c.methods",cgen->classname); if(stat(filename,&stbuf)!=-1) goto write_include; fp= fopen(filename,"w"); if(fp==NULL) { sprintf(cgen->msg,"Cannot open file '%s' in %s-mode. %s", filename,"w",strerror(errno)); return(0); } fprintf(fp,"\n"); fprintf(fp,"/* File %s */\n",filename); fprintf(fp,"/* Manually provided C code for class %s */\n", cgen->classname); fprintf(fp,"/* This file gets copied to the end of %s.c */\n", cgen->classname); fprintf(fp,"\n"); fclose(fp); fp= NULL; write_include:; fp= fopen(filename,"r"); if(fp==NULL) { sprintf(cgen->msg,"Cannot open file '%s' in %s-mode. %s", filename,"r",strerror(errno)); return(0); } fprintf(cgen->fp,"\n"); fprintf(cgen->fp, "/* -------------- end of automatically regenerated code -------------- */\n"); fprintf(cgen->fp,"\n"); while(1) { if(Sfile_fgets(line,sizeof(line)-1,fp)==NULL) break; fprintf(cgen->fp,"%s\n",line); } fclose(fp); fp= NULL; return(1); } int Cgen_write_c(cgen,flag) struct CgeN *cgen; int flag; /* bit0= also write access functions *_set_* *_get_* [*_link_*] */ { int ret; sprintf(cgen->filename,"%s.c",cgen->classname); ret= Cgen_open_wfile(cgen,0); if(ret<=0) goto ex; ret= Cgen_write_c_head(cgen,0); if(ret<=0) goto ex; ret= Cgen_write_c_new(cgen,0); if(ret<=0) goto ex; ret= Cgen_write_c_destroy(cgen,0); if(ret<=0) goto ex; if(flag&1) { ret= Cgen_write_c_access(cgen,0); if(ret<=0) goto ex; } ret= Cgen_write_c_method_include(cgen,0); if(ret<=0) goto ex; if(cgen->make_ansi) { /* public .h file collected ANSI prototypes */ ret= Cgen_finish_public_h(cgen,0); if(ret<=0) goto ex; } ret= 1; ex:; if(cgen->fp!=NULL) {fclose(cgen->fp); cgen->fp= NULL;} return(ret); } int Cgen__write_global_include(global_include_file,flag) char *global_include_file; int flag; /* bit0= write footer rather than header bit1= allow overwriting of existing file */ { FILE *fp= NULL; int ret; char fmode[4],timetext[81],macro_name[4096],*cpt; time_t t0; struct stat stbuf; strcpy(macro_name,global_include_file); for(cpt= macro_name; *cpt!=0; cpt++) { if(*cpt>='A' && *cpt<='Z') *cpt= tolower(*cpt); else if((*cpt>='a' && *cpt<='z') || (*cpt>='0' && *cpt<='9') || *cpt=='_') ; else *cpt= '_'; } macro_name[0]= toupper(macro_name[0]); strcat(macro_name,"_includeD"); strcpy(fmode,"w"); if(flag&1) { strcpy(fmode,"a"); } else { if(stat(global_include_file,&stbuf)!=-1 && !(flag&2)) { fprintf(stderr,"+++ File '%s' already existing.",global_include_file); ret= 0; goto ex; } } fp= fopen(global_include_file,fmode); if(fp==NULL) { fprintf(stderr,"+++ Cannot open file '%s' in %s-mode. %s", global_include_file,fmode,strerror(errno)); ret= 0; goto ex; } if(flag&1) { fprintf(fp,"\n"); fprintf(fp,"#endif /* %s */\n\n",macro_name); t0= time(0); strftime(timetext,sizeof(timetext),"%a, %d %b %Y %H:%M:%S GMT", gmtime(&t0)); fprintf(fp,"/* ( derived from stub generated by CgeN on %s ) */\n", timetext); } else { fprintf(fp,"\n"); fprintf(fp,"#ifndef %s\n",macro_name); fprintf(fp,"#define %s\n",macro_name); fprintf(fp,"\n"); } ex:; if(fp!=NULL) fclose(fp); return(ret); } /* ---------------- Sfile and Sregex Emancipation copies ---------------- */ char *Sfile_fgets(line,maxl,fp) char *line; int maxl; FILE *fp; { int l; char *ret; ret= fgets(line,maxl,fp); if(ret==NULL) return(NULL); l= strlen(line); if(l>0) if(line[l-1]=='\r') line[--l]= 0; if(l>0) if(line[l-1]=='\n') line[--l]= 0; if(l>0) if(line[l-1]=='\r') line[--l]= 0; return(ret); } int Sregex_string_cut(handle,text,len,flag) char **handle; char *text; int len; int flag; /* bit0= append (text!=NULL) bit1= prepend (text!=NULL) */ { int l=0; char *old_handle; if((flag&(1|2))&&*handle!=NULL) l+= strlen(*handle); old_handle= *handle; if(text!=NULL) { l+= len; *handle= TSOB_FELD(char,l+1); if(*handle==NULL) { *handle= old_handle; return(0); } if((flag&2) && old_handle!=NULL) { strncpy(*handle,text,len); strcpy((*handle)+len,old_handle); } else { if((flag&1) && old_handle!=NULL) strcpy(*handle,old_handle); else (*handle)[0]= 0; if(len>0) strncat(*handle,text,len); } } else { *handle= NULL; } if(old_handle!=NULL) Smem_freE(old_handle); return(1); } int Sregex_string(handle,text,flag) char **handle; char *text; int flag; /* bit0= append (text!=NULL) bit1= prepend (text!=NULL) */ { int ret,l=0; if(text!=NULL) l= strlen(text); /* #define Sregex_looking_for_contenT 1 */ #ifdef Sregex_looking_for_contenT /* a debugging point if a certain text content has to be caught */ if(text!=NULL) if(strcmp(text,"clear")==0) ret= 0; #endif ret= Sregex_string_cut(handle,text,l,flag&(1|2)); return(ret); } int Sregex_trimline(line,flag) /* removes line endings as well as leading and trailing blanks */ char *line; int flag; /* bit0= do not remove line end (protects trailing blanks if line end is present) bit1= do not remove leading blanks bit2= do not remove trailing blanks bit3= remove surrounding quotation marks (after removing line end) */ { char *cpt,*wpt; int l; if(!(flag&1)){ l= strlen(line); if(l>0) if(line[l-1]=='\r') line[--l]= 0; if(l>0) if(line[l-1]=='\n') line[--l]= 0; if(l>0) if(line[l-1]=='\r') line[--l]= 0; } if(flag&3){ l= strlen(line); if(l>1) if(line[0]==34 && line[l-1]==34) { wpt= line; cpt= wpt+1; while(*cpt!=0) *(wpt++)= *(cpt++); line[l-2]= 0; } } if(!(flag&2)){ wpt= cpt= line; while(*(cpt)!=0) { if(!isspace(*cpt)) break; cpt++; } while(*(cpt)!=0) *(wpt++)= *(cpt++); *wpt= 0; } if(!(flag&4)){ l= strlen(line); if(l<=0) return(1); cpt= line+l; while(cpt-->=line){ if(!isspace(*cpt)) break; *(cpt)= 0; } } return(1); } /* -------------------------------------------------------------- */ main(argc,argv) int argc; char **argv; { struct CgeN *cgen= NULL; int ret, msg_printed= 0,first=1,gen_for_stic= 1, make_ansi= 0, i; int make_lowercase= 0, may_overwrite= 0; char global_include_file[4096]; global_include_file[0]= 0; for(i= 1; i<argc; i++) { if(strcmp(argv[i],"-no_stic")==0) gen_for_stic= 0; else if(strcmp(argv[i],"-smem_local")==0) gen_for_stic= 2; else if(strcmp(argv[i],"-ansi")==0) make_ansi= 1; else if(strcmp(argv[i],"-global_include")==0) { if(i+1>=argc) strcpy(global_include_file,"global_include.h"); else { i++; strcpy(global_include_file,argv[i]); } } else if(strcmp(argv[i],"-lowercase")==0) { make_lowercase= 1; } else if(strcmp(argv[i],"-overwrite")==0) { may_overwrite= 1; } else { fprintf(stderr,"+++ %s: Unrecognized option: %s\n",argv[0],argv[i]); {ret= 0; goto ex;} } } if(strlen(global_include_file)>0) { /* begin */ ret= Cgen__write_global_include(global_include_file,(!!may_overwrite)<<1); if(ret<=0) goto ex; } while(!feof(stdin)) { ret= Cgen_new(&cgen,0); if(ret<=0) goto ex; /* <<< can be done neater */ cgen->gen_for_stic= gen_for_stic; cgen->make_ansi= make_ansi; strcpy(cgen->global_include_file,global_include_file); cgen->make_lowercase= make_lowercase; cgen->may_overwrite= may_overwrite; ret= Cgen_read_fp(cgen,stdin,first); if(ret<=0) goto ex; if(ret==2) break; first= 0; ret= Cgen_write_h(cgen,0); if(ret<=0) goto ex; ret= Cgen_write_c(cgen,1); if(ret<=0) goto ex; } if(strlen(global_include_file)>0) { /* finalize */ ret= Cgen__write_global_include(global_include_file,1); if(ret<=0) goto ex; } ret= 1; ex: if(cgen!=NULL) if(cgen->msg[0]!=0) { fprintf(stderr,"+++ %s\n",cgen->msg); msg_printed= 1; } if(ret<=0 &&!msg_printed) { if(errno>0) fprintf(stderr,"+++ Error : %s\n",strerror(errno)); else if(ret==-1) fprintf(stderr, "+++ Program run failed (probably due to lack of memory)\n"); else fprintf(stderr,"+++ Program run failed\n"); } Cgen_destroy(&cgen,0); exit(1-ret); } #ifndef Cgen_includeD #define Cgen_includeD Yes struct CgeN { char *classname; char *structname; char *functname; int is_managed_list; int is_bossless_list; int gen_for_stic; /* 0=no smem,srgex,sfile , 1=all three, 2=smem only */ int make_ansi; int make_lowercase; char global_include_file[4096]; FILE *global_include_fp; struct CtyP *elements; struct CtyP *last_element; int may_overwrite; FILE *fp; char filename[4096]; FILE *ptt_fp; char ptt_filename[4096]; char msg[8192]; }; #endif /* Cgen_includeD */ Description of the helper program stic*/bin/cgen cgen is copyright 2001 to 2007, Thomas Schmitt <stic-source@gmx.net> and provided under BSD license. Compilation: cc -g -o cgen cgen.c ctyp.c smem.c cgen produces a class stub in C programming language. The data structure of the class is described by some lines which get read from stdin. The stub will consist of four files which emerge in the current working directory: <classname>.h public header file of the class <classname>.c automatically generated C code of the class plus a copy of <classname>.c.methods <classname>_private.h private header file of the class <classname>.c.methods safe storage for manually created functions. From here they get copied into the generated stub. If such a file is missing, a dummy gets created. It will define a struct <ClassnamE> for representing the class data aspects, construtor <Classname>_new(), destructor <Classname>_destroy(), getter <Classname>_<element>_get() for each structure element. Some more functions get added for particular class and element roles. cgen normally refuses to overwrite existing files because it supposes that those contain code added by the human programmer. Human programmer enhancements may be explanatory comments, class specific methods, initial element values and other special precautions within the generated functions. As long as the modelling phase is going on, one may store such human code in <classname>.c.methods and may use command line option -overwrite for modelling development cycles. At some point of model matureness one may decide to give up cgen and the .c.method files and to go on only with _private.h , .h and .c files. Command line options -no_stic prevents usage of stic_dir/s_tools/*.[ch] -ansi generates ANSI C function heads and makes file <classname>.h hold only public definitions: an opaque declaration of the class struct and a list of function prototypes. The definiton of the class struct is then in <classname>_private.h . -global_include filename sets the name of a file which will contain globally necessary declarations. Currently it lists the existence of all class structs. -lowercase generate struct <classname> rather than struct <ClassnamE> and function names <classname>_func() rather than <Classname>_func() . -overwrite allows to overwrite files <classname>_private.h, <classname>.h and <classname>.c, but not <classname>.c.methods. Input line format: There are two states of input: class level and element level. Exampes are shown below with class roles and element roles. Input starts at class level. A class level line may be one of - Comment. A line which begins with '#' is ignored on class level. - Empty. A line with no characters is a comment with empty text (i.e. ignored). - Class. Options which begin with '-' and finally a word in lowercase letters which defines the <classname>. The classname leads to a struct ClassnamE and some class methods implemented as C functions <Classnname>_<func>(). - End of input. Line "@@@" or EOF at stdin end the program run. After a class line, input switches to element level where a line may be: - Comment. A line which after some white space begins with '#' is considered a comment. The preceeding white space is ignored and the text after '#' is eventuellay trimmed by a single blank at both ends. This text will be part of the class struct definition within file <classname_private>.h as a single C comment line /* ... */. The sequence of elements and comments is preserved. An empty comment text leads to an empty line in <classname_private>.h. - Empty. A line with no characters is a comment with empty text. - Element. Options which begin with '-', eventual C keywords "unsigned" or "volatile", type or "struct <NamE>", element name. This leads to a struct element which is taken into respect in some class methods. Depending on the options in this line, some element methods <Classnname>_<func>_<element>() may get generated. - End of class. A single '@' marks the end of the element list and brings input back to class level. I.e. next is expected another class name or "@@@" or EOF at stdin. Input semantics: A class can have one of two roles: - Standalone class. Input example: my_class - Listable class, which has pointers to peer instances: .prev and .next Such classes get a list destructor <Classname>_destroy_all() which destroys all members of a list (which is given by any of the list members). Such a class should have a pointer *boss as first element in which case the constructor will look like <Classname>_new(struct <ClassnamE> **o,struct <Some_clasS> *boss,int flag); There is a function <Classname>_link() which inserts an instance into a list and a function <Classname>_count() which tells the number of list members. For pseudo random access there is function <Classname>_by_idx(). Input example: -l my_class A modifier is defined for classes: - Bossless. Disables a special boss-subordinate relationship which is created if the first element of a class is a struct pointer with the name "boss". Like -l <classname> -v struct Some_clasS *boss Normally such a parameter *boss becomes part of the constructor method <Classname>_new(struct <ClassnamE> **o, struct Some_clasS *boss, int flag); This relationship is typical for a listable class and a single class which is designed to host instances of that listable class. Therefore one gets a warning if a listable class does not begin with a struct pointer *boss. But if -b is given, then CgeN ill not include a parameter *boss into the constructor. It will rather look normal: <Classname>_new(struct <ClassnamE> **o, int flag); It will not warn if the first element of a listable class is not struct pointer *boss. Elements have one of the following roles: - Value. It provides only storage for a C data type (which may be a C pointer despite the role name "value"), a getter method <Classname>_<element>_get(), and a setter method <Classname>_<element>_set(). Input examples: -v int i -v int a[100] -v char *cpt -v struct xyz x -v struct xyz *xpt - Managed. This has to be a pointer to a struct <XyZ> or to char. It will not get attached to an object by the stub's code but its destructor <Xyz>_destroy() will be called by <Classname>_destruct(). In case of (char *) it is supposed that a non-NULL value has been allocated by malloc(). Managed (char *) types get a setter function <Classname>_<element>_set() which allocates memory and copies the textstring from its parameter. Input examples: -m struct XyZ *xyzpt -m char *textstring - Chainlink. A pair of prev-next-style pointers to the own class struct. Function <Classname>_destruct() will unlink the affected instance and put together its link partners. Input example (there must always be two consequtive -c lines): -c struct My_clasS *up -c struct My_clasS *down - List. A pair of pointers to the struct <XyZ> of a listable class. The first one <ls> holds the start of the list, the second one <eol> holds the end. The getter function has an additional argument idx: <Classname>_get_<ls>(struct <ClassnamE> *o, int idx, struct <XyZ> **pt, int flag) idx == 0 is the start of the list, idx=1 the next element, ... idx == -1 retrieves the last element of the list. For insertion of list items there is provided method <Classname>_new_<ls>(). The inserted item is reachable via the getter function with idx == -1 <Classname>_destroy() instance calls <Xyz>_destroy_all(). Note that the end pointer is always generated as private element (-p). Input example (there must always be a -l and a -v line): -l struct XyZ *list_start -v struct XyZ *list_end The availability of getter method <Classname>_get_<element>(), and setter method <Classname>_set_<element>_set() can be controled by two modifiers: - Readonly. Only a getter method. Input example -r -v int broadcasted_status - Private. Neither getter nor setter method. Input example -p -v int private_value - Bossless listable. This marks elements which are listable objects but do not expect a boss pointer in their constructor. See above: Listable class and the bossless modifier for classes. Input example -b -l struct XyZ *list -v struct XyZ *last_in_list - Initialization free. The class constructor will not initialize this element. This modifier has to be used if neither NULL nor 0 are suitable initialization values. Example run: rm class_x.c class_x.h class_y.c class_y.h bin/cgen <<+ -l class_x -r -v struct Boss_clasS *boss -v int x -r -v struct stat stbuf -m struct Class_Y *y -m char *text -c struct Class_X *master -c struct Class_X *slave -b -l struct Class_X *provider -p -v struct Class_X *last_provider @ -b -l class_y -r -v char providername[80] @ + /* cc -g -o ctyp.c */ #include <sys/types.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include "smem.h" extern char *Sfile_fgets(); extern int Sregex_string(); extern int Sregex_trimline(); #include "ctyp.h" /* -------------------------- CtyP ----------------------- */ int Ctyp_new(objpt,link,flag) struct CtyP **objpt; struct CtyP *link; int flag; { struct CtyP *o; int ret; *objpt= o= TSOB_FELD(struct CtyP,1); if(o==NULL) return(-1); o->is_comment= 0; o->is_pointer= 0; o->is_struct= 0; o->is_unsigned= 0; o->is_volatile= 0; o->array_size= 0; o->management= 0; o->with_getter= 1; o->with_setter= 1; o->bossless_list= 0; o->no_initializer= 0; o->dtype= NULL; o->name= NULL; o->prev= NULL; o->next= NULL; if(link!=NULL) link->next= o; o->prev= link; return(1); failed:; Ctyp_destroy(objpt,0); return(-1); } int Ctyp_destroy(objpt,flag) struct CtyP **objpt; int flag; { struct CtyP *o; o= *objpt; if(o==NULL) return(0); if(o->prev!=NULL) o->prev->next= o->next; if(o->next!=NULL) o->next->prev= o->prev; Sregex_string(&(o->dtype),NULL,0); Sregex_string(&(o->name),NULL,0); free((char *) o); *objpt= NULL; return(1); } int Ctyp_get_pointer_level(ct,flag) struct CtyP *ct; int flag; { return(ct->is_pointer); } int Ctyp_is_struct(ct,flag) struct CtyP *ct; int flag; { return(ct->is_struct); } int Ctyp_get_array_size(ct,flag) struct CtyP *ct; int flag; { return(ct->array_size); } int Ctyp_get_management(ct,flag) struct CtyP *ct; int flag; { return(ct->management); } int Ctyp_get_with_getter(ct,flag) struct CtyP *ct; int flag; { return(ct->with_getter); } int Ctyp_get_with_setter(ct,flag) struct CtyP *ct; int flag; { return(ct->with_setter); } int Ctyp_get_dtype(ct,text,flag) struct CtyP *ct; char **text; /* must point to NULL of freeable memory */ int flag; /* bit0=eventually prepend "struct " */ { if((flag&1) && ct->is_struct) { if(Sregex_string(text,"struct ",0)<=0) return(-1); } else { if(Sregex_string(text,"",0)<=0) return(-1); } if(Sregex_string(text,ct->dtype,1)<=0) return(-1); return(1); } int Ctyp_get_name(ct,text,flag) struct CtyP *ct; char **text; /* must point to NULL of freeable memory */ int flag; { if(Sregex_string(text,ct->name,0)<=0) return(-1); return(1); } int Ctyp_get_type_mod(ct,is_spointer,is_struct,array_size,flag) struct CtyP *ct; int *is_spointer,*is_struct,*array_size; int flag; { *is_spointer= ct->is_pointer; *is_struct= ct->is_struct; *array_size= ct->array_size; } int Ctyp_new_from_line(ct,link,line,msg,flag) struct CtyP **ct; struct CtyP *link; char *line; char *msg; int flag; /* bit0= make struct ClassnamE to struct classname */ { struct CtyP *o; char *cpt,*bpt; int ret,l; char orig_line[4096]; ret= Ctyp_new(ct,*ct,0); if(ret<=0) { sprintf(msg,"Failed to create CtyP object (due to lack of memory ?)"); goto ex; } o= *ct; strcpy(orig_line,line); cpt= line; while(*cpt!=0 && isspace(*cpt)) cpt++; if(cpt[0]=='#') { cpt++; if(cpt[1]==' ') cpt++; l= strlen(cpt); if(cpt[0]==' ') cpt++; if(l>1) if(cpt[l-1]==' ') cpt[l-1]= 0; if(Sregex_string(&(o->name),cpt,0)<=0) {ret= -1; goto ex;} o->is_comment= 1; {ret= 1; goto ex;} } else if(cpt[0]==0) { if(Sregex_string(&(o->name),cpt,0)<=0) {ret= -1; goto ex;} o->is_comment= 1; {ret= 1; goto ex;} } else if(cpt[0]=='/' && cpt[1]=='*') { sprintf(msg, "C-style multi line comments (/* ... */) not supported yet. Use #."); goto ex; /* >>> */ } cpt= line; while(cpt[0]=='-') { /* look for management specifiers: -v* just a value -m* allocated memory which needs to be freed -c* mutual link (like prev+next) -l* list of -m chained by mutual links prev and next -r* read-only : no setter function -p* private : neither setter nor getter function -b* bossless_list : Class_new(o,flag), not Class_new(o,boss,flag) -i* no_initializer : do not initialize element in <Class>_new() #... line is a comment */ if(cpt[1]=='v' || cpt[1]=='V') { o->management= 0; } else if(cpt[1]=='m' || cpt[1]=='M') { o->management= 1; } else if(cpt[1]=='c' || cpt[1]=='C') { o->management= 2; if(o->prev!=NULL) if(o->prev->management==2) o->management= 3; } else if(cpt[1]=='l' || cpt[1]=='L') { o->management= 4; } else if(cpt[1]=='r' || cpt[1]=='R') { o->with_setter= 0; } else if(cpt[1]=='p' || cpt[1]=='P') { o->with_setter= 0; o->with_getter= 0; } else if(cpt[1]=='b' || cpt[1]=='B') { o->bossless_list= 1; } else if(cpt[1]=='i' || cpt[1]=='I') { o->no_initializer= 1; } while(*cpt!=0 && !isspace(*cpt)) cpt++; while(*cpt!=0 && isspace(*cpt)) cpt++; if(*cpt==0) goto no_name; } if(strncmp(cpt,"struct ",7)==0) { o->is_struct= 1; cpt+= 7; } else if(strncmp(cpt,"unsigned ",9)==0) { o->is_unsigned= 1; cpt+= 9; } else if(strncmp(cpt,"volatile ",9)==0) { o->is_volatile= 1; cpt+= 9; if(strncmp(cpt,"unsigned ",9)==0) { o->is_unsigned= 1; cpt+= 9; } } if(*cpt==0) goto no_name; while(*cpt!=0 && isspace(*cpt)) cpt++; bpt= cpt; while(*bpt!=0 && !isspace(*bpt)) bpt++; if(*bpt==0) goto no_name; if(*bpt==0) { no_name:; sprintf(msg,"No name found after type description : %s",orig_line); ret= 0; goto ex; } *bpt= 0; if(Sregex_string(&(o->dtype),cpt,0)<=0) {ret= -1; goto ex;} if((flag&1) && o->is_struct && strlen(o->dtype)>=3) if(isupper(o->dtype[0]) && islower(o->dtype[1]) && isupper(o->dtype[strlen(o->dtype)-1])) { o->dtype[0]= tolower(o->dtype[0]); o->dtype[strlen(o->dtype)-1]= tolower(o->dtype[strlen(o->dtype)-1]); } cpt= bpt+1; while(*cpt!=0 && isspace(*cpt)) cpt++; if(*cpt==0) goto no_name; for(;*cpt=='*';cpt++) o->is_pointer++; if(*cpt==0) goto no_name; bpt= strchr(cpt,'['); if(bpt!=NULL) { if(strchr(bpt,']')!=NULL) *strchr(bpt,']')= 0; sscanf(bpt+1,"%lu",&(o->array_size)); *bpt= 0; } if(Sregex_string(&(o->name),cpt,0)<=0) {ret= -1; goto ex;} if(o->management==1) { if((!(o->is_pointer>=1 && o->is_pointer<=2)) || ((!o->is_struct) && strcmp(o->dtype,"char")!=0 && (strcmp(o->dtype,"unsigned char")!=0))) { sprintf(msg,"-m can only be applied to pointers of struct or char : %s", orig_line); ret= 0; goto ex; } } ret= 1; ex:; return(ret); } int Ctyp_read_fp(ct,fp,msg,flag) struct CtyP **ct; FILE *fp; char msg[]; /* at least [4096+256] */ int flag; /* bit0= make struct ClassnamE to struct classname */ { int ret; char line[4096]; struct CtyP *o; line[0]= 0; printf( "[-value|-managed|-chain|-list] class element ? (e.g.: -l struct XyZ)\n"); if(Sfile_fgets(line,sizeof(line)-1,fp)==NULL) {ret= 2; goto ex;} printf("%s\n",line); Sregex_trimline(line,0); if(strcmp(line,"@")==0) {ret= 2; goto ex;} ret= Ctyp_new_from_line(ct,*ct,line,msg,flag&1); if(ret<=0) goto ex; ret= 1; ex:; return(ret); } #ifndef Ctyp_includeD #define Ctyp_includeD struct CtyP { /* if 1 : .name contains comment text, all other elements are invalid */ int is_comment; int is_pointer; /* number of asterisks */ int is_struct; int is_unsigned; int is_volatile; unsigned long array_size; int management; /* -v 0= just a value -m 1= allocated memory which needs to be freed -c 2= mutual link with the next element -c 3= mutual link with the prev element -l 4= list of -m , chained by -c pair named 'prev','next' supposed to be followed by a -v of the same type which will mark the end of the list */ int with_getter; int with_setter; int bossless_list; int no_initializer; char *dtype; char *name; struct CtyP *prev; struct CtyP *next; }; #endif /* Ctyp_includeD */ #!/bin/sh copy_mode=0 while true do read line if test "$copy_mode" = "0" then if echo " $line" | grep '^ Cgen=' >/dev/null 2>&1 then copy_mode=1 if echo " $line" | grep '^ Cgen=..' >/dev/null 2>&1 then echo " $line" | sed -e 's/^ Cgen=//' fi elif echo " $line" | grep '^ =end Model=' >/dev/null 2>&1 then break fi else if test " $line" = " @" then copy_mode=0 echo "@" else echo " $line" | sed -e 's/^ //' fi fi done #!/bin/sh test_dir=. model_file=./libdax_model.txt xtr_dir=. cgen_dir=. # cgen_dir=~/projekte/cdrskin_dir/libburn-develop/libcevap cd "$test_dir" || exit 1 test -e smem.h || exit 1 cat "$model_file" | \ "$xtr_dir"/extract_cgen_input.sh | \ "$cgen_dir"/cgen -smem_local -ansi -global_include cevap_global.h \ -overwrite "$@" GIF87a¸¤÷�����­­®°°°±±±³³³¶¶¶···¿¿ÀÀÀÁÄÄÄÆÆÆÈÈÈÊÊÊÌÌÌÿÿÿ@��àÜ�>Ú�ÿ�¿�h�A������L����³l�Hë��@�`d�óB�2�@��À��Û��ÿ��¿���ˆ�0*����0ì9�Ú�ÿ�¿kà��Û��ÿ��¿È Ü��ÿ��¿��yÀ�æÛ�ÿ�@¿�0��9����Xè��%����0.¸9�àN‰ˆZßÀ2ÿ @¿c����À�� ��Q� –�p�@�c��`ÿ�ÿ8X�ÝC�ÿú�‰�ì�Û�ÿ0f�c¼�Ú`Àÿo ¿@HÝ�Üÿ�ÿ¿�¿—œü-ÚÛ ÿÿ¿¿S��¦�������`��o��@ˆ�Ý�ø¿�øˆ��*øÀ @��S��t��­p0 ò9ãÿ1¿@˜8ÝÜ�ÿÿ�¿¿�ˆœ�îÚÀÿ@0e9���d¨��?������$O�BÚ�ÿ�¿�¤L�áÚ�&ÿ�@¿�0��9����X�Ü��±��²����@�Ø�pï�g2�@@�kÿ˜ˆÝ*�ÿ�¿@�Ž 9ë� p�pg�¸hdAÚ¤L��¤�l�ë��� 0�ÜB�ÿ�¿ðˆ€Ý*Ûÿÿ¿@¿ôˆøÏýÝ÷ÿÿÿ¿@ÿüàÝ>#ÿ9¿�phòA�ÿ�˜LÝ�€�6ÛÀ&ÿ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������,����¸¤�þ� 4H°`A *ˆpᆠBŒHp"Å)fŒ¸ÑaLJ /b yñ£B“IjTÉ‘¥G— E2„y’fJ™#q¢<h“§ÎžÎüITæÎ EEjThΦIK:]úTéÔ«QWfm¹õeטPÃZýZ“,γhÓª]˶­Û·pãÊK·®Ý»xóêÝË·¯ß¿€ L¸°áÈ+^̸±ãÇ#KžL¹²å˘3kÞ̹³çÏ C‹Mº´éÓ¨S«^ͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀƒ N¼¸ñãÈ“+_μ¹óçУKŸN½ºõëØ³kßν»÷ïàÃþ‹O¾¼ùóèÓ«_Ͼ½û÷ðãËŸO¿¾ýûøóëß¿«X©fùôŸVrU W‚5V‚e1x“ƒ.(!€"U!S˜¡"Ø¡‚~Ø ˆ’aˆ(j˜"‡+zØ"ˆ*ÆÈ¢Œ.Ò㌠h¡‰;¾8¢%y¢?¤‘CâX£’7.éd“Pɤ”ORå‘SbY¥–W&¹¥—]öˆ¤˜Y‚iå™\¢ifšl®é&™_Â&†üÕiçxæ©çž|öé矀*è „j衈&ªè¢Œ6êè£F*餔Vj饘fªé¦œvêé§ †*ꨤ–jꩨ¦ªêª¬¶êê«þ°Æ*무ÖjkwþñH瘻–)§š¿¶웽ÆYìœU 9ì²ÇÛ¬°Ï›,¯Óú-³Õ›-²Tu›«²×†»­³ãB[®´Þê(nºž‹mGßRË®®î®¯µõæ;/¸úÞ«í¾òúË­À䌯Áÿl.«ۯà ¬0º¿[±½ߪñÆwìñÇ ‡,òÈ$—lòÉ(§¬òÊ,·ìòË0Ç,óÌ4×lóÍ8ç¬óÎ<÷ìóÏ@-ôÐDmôÑH'­ôÒ;_üp»/ìtÔC<õÕVg 5Ö[kM/Õ{Í/Øwmö×\£-vÀkçxöØiÃÝvÂs7ü6Ûw7þÞjçM·ßv÷-¸Ü€K\·á…K}¸¹L7îøãG.ùä”Wnùå˜g®ùæœwîùç ‡.ú褗nú騧®úꬷîúë°Ç.ûì´×n»å{ë�¼÷îûïÀ/üðÄ ¿@¹ÿ=8µÅ7ïüóÄ/À@ÙËëžxAÐg¯}óÒSOxõÊ_´ýøä÷ÞýÓàÎoùì³OÀôéS™¼úàÀýøç¯ÿþü÷ïÿÿû@0?ÄÅÏ\ö È@þ pÞãÛ÷&X¿Zð‚ÿ{`­wÀªÉ$ a5ˆ> rp}"L¡  �v[ ¡ g¸@�@ב! w¨?â0;þ:ä¡}¨ q‡DLŽxÄ$ng‰L¤!�ðÃê@1Š#¤b±8D-‘‹<œbsE0ŠPŒÝ)£-ˆFë¨q�l#vÞGÿÉ1‡uLáCÇ<ÆÑ‹Oôã I> {´â!/˜ÈB.’„äã#ÏÉ9:Àw \bï¢ØHèÈ“5ìß&E™ÉJ:ò’£T &{˜AD:Дӱ(U)ÊTfP–—|å%9Ë8ÖÒÜ¥%Iʲ“ÏùdïGÌÂR: l¦AÈ»Vfð™ÑÁ%]iMn:S˜ÄѦÙÈHlÆ2—ùKe4™ÉÌj¢“ðD%*©¹ÎjÚÓœÌþQf:݉ÎuÎÓ›ÔÌ¥8£ùI|:ÇŸˆä§? JÏ„:´U§A›#Nˆrs¡ u¥6Ý9ÊFs¢¼©è?á‰Ñ€î3¡ í§FÛyIt¤](IãiR}ªÔž¸DæKQ:ÓžÊô èJêRå¤T¥>MjNgÓt2³¨J„)M“ŠTþ4¢L…*ndÉÑ©"õ§=½hX»ŠJ­õU½ªW… P”n”:¥¨SÅJU°šô¦¥¦Y%‰Ö¦~•©lŤ_;º×âTt°uí(]±:OY–6‡õªZaªN²úµ±q…f_%KUÀÎõ®ÍìrlšÖº®µ¦UEäc‡ƒÐÒþ¯o¥þk`Ÿ Îs굯Õ!n[ÐÚ '²@5íf?ÛÙ»ŠÖ“Ãèd×*ÛÛÂuµ¿!mS-ÛÙàÎö›[L®F'»ÜÒ.ºÁ®r…ÛVïÆ³¥¾ ¯váÊݹ.–½åEokѡՖŸ´é&ezܨºµ¾p%©BùyÚ}ê5½È¥ïHÜÏŒº·£,xÃUŽ8¶êdåCƒKOÇ"8º ÆïWÌÓ¼z¶¯½›ðoíHËnΰ¿²á[ÔHc.Âø¬,ö%o<¯Š56#Eãcµ�¹Åy2Žˆd&‡QŤÀ@¤*+$È.n"”{sE,cQɯ¡²•2fìå8ËœÜrþeÄ\?‡YÇIVó[ ¨¸N²œ ³ÁÇf IùÏW¾s ½H¶JðÐU´* ­ç°ù­ÏUt™ Í8ÿ°Ð˜ö¤'"é@+Ú˜ø°¡•Ôè ºùÓƒÎóâölBJO¹G½#3ªíÈèU;ú…âªtÛ˜i[›zÓ$‰µ§gÀZ_ïÖ-6öŠlfbÇ1ÔÇW MkQ÷øÕnvõ°©MÛìr»Û¿Å6™µm]ÃÌžô•ÉÝìoÛÚÉt7v£+n´˜{è§¼ånvŸåÞB–ódÔýï}Ïû‰íK8ônHázÿ[áÃñŠûnâáuøî,ÎqŒÃ†àç¸Èþyçñ/ü’%©•A.ky;.O¹oŠÌòmsûå_¹Æ[~r”Ã{§ǹfcþó%[è§t7ÒYÛsfÊÜ=Ï7…÷-õ»\à¹øŽ±Ž­ïšëºñzÀ‹ÎW¥ƒÝ6bO3Ù‘“v&V=6mßúÚ~õ¹3½îÞ>úÙk÷.ÚïMçw Oþv.S}ï=üßõwÿFñhüâ§ÞxKVæfŸ|Œ_x«òaV|Þoz¸‹Þñ„/}%y½k^½—‡zì ËyÕo>õ¯7üì“îúÑS»ó®éû—mozÜ·žô¹ñîϹ|Ï¿1¥õ© ?òê[_äT¿¾ö·þ¯ðìsÿûà‡ž÷ÃOþ‘sÚ¸ÎVô -|-«:ÚÒ§¾Ú/Ec§¿×ð§ÿ‹íßjù‡y§‡÷ç4ú7Dè×ä2mϧHÍ—M§gy™Gwè{½'{hrÈvµ—|°÷yvhx|¿G|Q€O‚Ê‚&èlÀ§{ø‚ă¬¡‚H„‚aV~ãótÉă Ç‚»„AXDDè<>^G¨=IÈ|AׄùDuPtG7…rEtˆ…܃+d…¨ws^8ZR(„"¨…ØtKÇ€.H‚ÈG5¨ƒ«Á…dƒqØóµ€¼×†X‚lx‚d˜r8H8„¬×‡0‡þªˆÁ䆳F‡‰h‡[Xˆjȇ“¨‡h‰d‰ƒ·†•臌ˆjŽ˜u#H{hˆˆ7(‰u1iŒ¡Št¡ˆÆ$g¬Ø±(®XJB8‹‹‹pQ‹ƒv‹…¡‹oÁ‹å4ˆeȉ©(ÀèÂøG¾HÉØËxMÍ8p{Õ†ÏhÙøcšØ¾6gvï÷5É(€ãfj<W`#eæh$VÆ~X1ì¸~6ñŽ:2‹cc5t€ˆæ$ôØ7ŽÊ<¨%Á1¿ÓŽJ²÷؃ð×IIÉgÀÃQ'D- hŒ­ˆŒÕ8Š´¸f)Ù€ÊÈ’záþº„¿Ø’¨¸‡. (“*ÛH?™2ùn4éŒ6I‰+ ”Gi’—8ƒ'Aio=¹‹0™CypÇh”<™“3V“[y“M‰” æ”xˆÄy\)ƒxˆ“dé€i…o)†qYŒk–˜è„b9tm‰—wIŠ`™CKø<aHFI<ƒ©D…9<‡Év‰)qgY‰„y �€�˜™™š¹™œÙ™žù™ ©™�.T•y™¡™šª¹š›9š¥y§©™Sš³¹šµI›žéšÚ›¬Ù›¾Ù™º™¼ù›ÄYœ˜œÛÁ�  �ÌÙœÎùœÐÒ9Ô çSÊYÚþ¹Ü)×yÙéœ�PãÙåIžÓùÖžÝÙžîéðƒìùžôYŸÍ©ž·#)¸˜À•ùùŸ³ŸööA�Z -# ÿF º (ƒ »£  ¡#ã Úfúy‘:€ß‘ÿ7’ ú¡"J?¬Øº¸ˆÀè¡þ×¢”¬.Zgª¡s³¡0Šl2ú¢;J£9ŠŽ<¢3£#Y¢q¢ ‘¢°¢8Ú£D:¢A ¥>ê¤:*¤Q:¤0t¡9$>*þ©¥`j*ꥦf:+cÊ¥dz¦l*+ij¡jÚ¦rÚ*oZee:§x*¦qz§ç˜§~Z*ušmkú§þ„*Ú§pZ¨ŠÊ)‡ºnƒº¨Š)Zn|©– )“Š=•z©œº(™JýÙ©¢ú(ŸºsŽ:ª¨š(¥êo‚šª¢Ñ¤V:¥±ú¤X ¤²*¥´z¥ºz«$êHjªAÁ¤ «¸Z¥Åj«¹Ê«»š¬Ìj¬ÄZ«à¨¬ÒÚ¬Èj¬ÐÊ¢Ëj­Ùº+Fʪ ¢¤Âj£âZ£äú£ÑJ­çª­Óª®®Úp‰ú¨vÚ®ò*(«ªóz¯~R¯›Š¯üªú ¯ý°þº§�‹¨{°÷ñ¯‹° + û® ±îñ°ñ*±ÛÛª»±ä¡sõóŒöʱ"[e »¯#›²Vªâsþ²«²0›Méf¾ó²”³8ëF,û± j³9ûϺ­éZ­D;´F‹­ëZ´H‹®éR²7Ál`®ãj®KË®V«´A›´G›µ^µW»µÃ¶SK¥X+¶åªNëPû­R®g;«_[µe;¶p;·o{¬?k°yÛ·µ°Ë·~;¸J¸¸„›¸ád¸{¸Šû¸¬Å¸‚{ª[¹î긒»³–»¹\–¹7빜ºaºš‹¸¢{º£kº“û¹¨ÛºGºÀª©®;»Á»ÞJ¹´›»§±·¸«»¾Kd¶²¿;¼1¼(K¼a¶Tëµv»¼ÊK¶`K·x›Ýú«·þ¬Ò{­Ì½w«½Ï[·ÜÛ"Û+·á ½ä{¾ã›¾Ï[½ "µÝ+´èû½Ó«¾Ù ¿Ñ‡¼J©º½»ºøÛ¿;‰¹ú»þ;À²h¼>KÀœ¼Ëº ÜÀ¹hÀ¶ëÀ\À|ÁlÁ¥ ÀÜÁ|±À²ëÁ"Ü ¼Á#|ÂÚXÁŒÂ,Œ*ܸ-Ã/©Á\Ã2|Ù˟NjÃ<Ì¿×ëÃ6ŒÀôû¾Z¿õ[ÄCì¼c˾â¾JLÄè™Ñƒ<òë½GÌ´É“Äæ«ÅàkÄP·\üLL N¼ÅU,´Wé@ÆVüÅÍK’=Ü¥+ Ä?¼‰cÇ1SÂALƒžˆÇ/£Çþuü‰o8™~¬§4Ȉl—}\È,È_o½„f�æwŒ| /LÇôLn'x•œ2Ž\"•a#Zò´R\UÊÆ;¦ØÉ—òÉ+UÖÕ^ÍEPò¤[ÏEȬ *®<âõ^£<V¾,U¡˜Ë€zÉûë½,\­uY²ìaÄÜ ÆÌÀF•ÉlUäåZ¹•U¸üÌŒÍ!<Z±ËŠvT Fκ¥_ca«ÌÍ‘²Ëà|»Vng6ÏcÇÎ%ãÎF¬Â&ÏÅ$ÉÃ·ÍŽÂÆö{ÆHLÐXlÐ`ŒÐÜê«íÛ¶WœÐ¢l,«Ši\liÆýÆ]œÅ ]¾ÝÑ^üÄ"Ñn<-cl eüþÑ-í¥)нÒ!mÏ(«Ã¹‰ÌÏLIÓêÍ&Œ˜;ÛŸÉÓƒÏà!ÅŠ ÐDM)FýÝC‹¹Ô«ÒÔÈuQ-Õ©BÕw¸ÓX]+Z‰uÙÕ^íÓ{lˆ})Ö´òÕv<—h*j-ȸÎmM(oÍ–y9×®R׊l–xm+ŽŒÔ€}aÝ×yMÖçñÔé$׃Ó-ÓŒ Ñ%=Ó*!' J Ò=e/ÙœíØ7ÚÙ‘Ù=¿ =Ò“"•µ¦MÚ+تuѧMÒ«ÝÆæJÔ€l® n„í¦†ÛC½ÛSÝÛ üÛÀÕÂýʃ]ÜÆ}ȼ…ý;W­Ü›þrÛÃMxÑ-Ý™BÝÈý„JÝôzܼLÜÞ=*ÚÞÉ=Þä Þâ‘ÛœŒÞ§RÞ3·”{éÞ¢ßû•SIߺ¬Þ÷m•â­ßbßY<Øøß¬"Ú’ÍÚ¡]Ú²Ýà±=#©Í¶®à@ÂíÂÞSÛ>á´ÍáQŠà Îà¾á#Þá±árÙž8N/Ž�nâ$>ã2­¶Íß!%ßw àŽãNmà<žÝ>^ÕçäÓ=ä[ßF®)þãE¾ä’Šä`ÍÕPÎäRŽpÏÝ;×]åŠÒäÞ1œÆ檉œ\ÞãÌ=óiŸjÎøYæB~ænŽÀ^ç›;çt^¹v~ç›çþ â+¾à>Û臆â¢â€žè‚ÓŠîጾÚ~Þè%>è’N㔾èCbè‚è˜ٮіþèŽÞØ%}ãp®çøËç¨>¸ª¾ê}Ûê®þ³°ë8;ë´³¶~ë)›ëº.²¼ÞëûëÀn±Â>ì[ìÆÎ°Èžì»ìºéŸ>Ú5~é£îéÕþƚΜ~íÒNí“.êßNêÑî¡ÐÎí >íàêânîñ’íO±íáníñêè¾îóÞíéÞÖÎÎìýºïüޝþþïóðß®_ð©zð?ª ¿ðÚð©ñ‘:ñ¿¨ñ…šñÿ§ßñyúñ[î÷~þîÞ®îòŽòôΣãNò*ïöó&Ÿî0_ï)ó+Ÿï'¯óåÛòIúò"Nó7ßóFÏóZkês|Ì ³"ßômúôP¦R?õaZõV¯¥XŸõºõ\Ï ^ÿõöb d_öùyöho;j¿ö´Óön/;p³Eô3ïó5÷G¯÷S ôœ÷9¿÷o÷ƒÏ÷†Ÿòuø;¯ø2_øŽ¿øÖî÷u ø_ùoù”ùš_hJÇ÷÷:÷ ï:¢?ú¬Sú¦¯:¨Ÿú¨³ú¬o:®ÿú¤û²/:´_û sû¸ï9º¿ûœÓû¾¯9Àü˜3ü›ø¿ùÈùËÿó þÝÄBOèó—å÷™ýaÜüØ?ôx¯ýå.ùðNôÜŽ7B¤ÉøÛ/ýÚÒù˜\Öæ]þ]ØÝÄoÆeð¿ò?ÿQvåp}ÿÒ¨ÿ�Ñ@à@‚ D˜PáB† >l�`¡Ä‡!^ĘQãFŽ8�RäH’%MžD™R%J�t„SæLš5mÞÄ™SçNž=}þThL‹‹2<:TéRŒ<ý¸2*I¨SO>]™UkË—L½~VìX²eÍžE{6iÁµ Û¦…kÓ©Ö”S­R¥›·®Ë¸}ýþXð`Â…åN„øÖðb¶îB U2HÈ”#Sž\5²]DZbþÅœY$hÇ’ù2FZõjÖ­]V1ñkÕNAO¶ÜY7nΙqïÆü3Þáºs;=M[ùræÍ?o;6[èƒç^6ÎûxhÉ9oÏ>4pã¥?'¯ž^ýzöíÝÓ”>û=ϧËÿÖNþñhìú±ã·,¿Ü(Co>D0AS‹¯¢k¢H±¶®Ï6ð~0¼ ÿP4ò2äêAG$±DéCÌÁ7ŠÐ(Ù’²í6Ò<;Ž´»ÌÓ0¸Å3ï¶Í6C®«‡$²H#KlСéŽ$¨E£ê3èÆþ®Ò«J+M ‘I-·ä²K×’lhÉ-<H¢µ¤ÌË+×\3K/i3À!âlþhN9édÈÎ:ñ\HÏ<ùTÈÏ>MHÐ@ EÈÐB=HÑD5ÈÑF!-HÒH)%ÈÒJ1HÓL9ÈÓNAm@ÔPI5µT@Û²s:=cK5VTg½³Ö=ËÈ¢TÑd³W_±DOV[ÿvÐb=vÑd]–Ö[Ÿ%Zc¥E–Ze­eÛI—ÝVÛK¹ýÖÛMÁWÜOÉ=—ZVãt•NX6Þiå­–Þ\ï=UÉ_÷å·$7UEwÔ€ó5Wà‚ ¶7Û„»}³a‡©‡Ë´ˆL…xí7c+ÿ¸c?Y¦ˆS ™EOö•ã’Wf¹åG¾Øå¦ª¢¹f›oÆ9gw湪„þ”9h¡‡.f·ˆe¥¯àg¤Ÿ†:êùŒvQê2—Æ:¯¦¶ºk¯¿Fj\ÁÎ5k³¥*lµ×f{,±£\ã³ÏV¹m»ïÆ{§·©S[î¹³®;oÁ'¼)’ÃŒûoÅG ¼pLJ\¶˜U$ÛïÅQn<rÍ7g{ï&¿|ñÌ9'½ô¨=¨gÕWg½u×_‡õÐEOÛtÛou»-Ÿ½ßÑq>ø‰uo›÷Þ÷ý]ø<øàæúr¥O—zƒ­Gx^íëÝþZìŸï^áð?úò§Ç÷Ûv™Öùö¿¢ãçþ7~îï÷þüêõ¿žÿìñ_§Ø/ÿ€ä; ùþˆ¾î¯ý{àÿòANЂ¼ 3¨À 2p]’£ ý:èÀB°„Ä QæG·`0„.<aS¨Ár°† T^ID¼Î­ÐlÉÓa…¸ÃÃAÎx>lS톸D&>ˆ‡ C¢Ò€ØD*V:Oì[—6E+vÑ‹­Ábåb7F2¾Îi_Dc—F°Q‹zÙšå8ÇÚñqn|#]âHG>ö10lüó˜.úч ½&ÈAª¤ˆ„d$o¢H"‰Édô%¹INNÒŽ³¤}0ɯGvÒ”§”ØäVÊŒ0r”Àâ*e9Kùíe5É+_IMÒÒ—¿LÝ'=þÆÊ¦ìòW¥" oHÂeš°™(¤a4m(M>S†Ôd&6©MhNÓ›Õäæ5¿™ÍqBðƒësŸ2Ã)ÂêJQ2fÊZ8@z¦³žð{g5óINp–³›ý(?ºMŠ3 =è?ªP„.Ô eç:÷ÙPs2 ø¼g#š¾6&žlªßD:Ò‡R”¤…¨HK L–N•‘ÓåGÓË–Ö´“”´ZL?ŠL›öTŽ8•šNãÉSŸÕ‹@=L“K£6ÕH…šPIT§VUˆP}šTwIU«vUxXEZÅ:ÖÑ«gM#X‰¶€�o…k\å:WºÖÕ®wÅk^ëzþ€,�­ý¢Z‡Æ€(À°‡Elb»XÆ6Ö±…¬cÀ�ÀV¶Š‚µlf5k:ÌnÖ³Ÿ-\gA;ZÒÆM˜¥Emj9'ZÕO¥(5éJe Û“¾Ö¶}!ncHÛÙÞÖžŸÛÖE5ê[Œþ¶¸Ç®n­É[æ7¹ÆÍ”r%*Ý‚6—º ííuSªÝØZº¹®ú ÞñîÖ¹ä}ny—{^ór·¶î•mkÉZùÖ×¾Ã<í}õ»ß æ—¿ÿ°Ëè`8A6p‚¬/ØÁ¦Mƒ!<a /FÂÆp†ûra wØÃbáð‡E<âDú—Ä'F1`Bœb·˜#+vqŒeœJþ[Îxsì]/|½«Þé~·½>Î1{Ìãê®Ê¢DÆîŽÓ»dô6ùÇHÞ®‹¬cãxÈL~2–ƒ åîZyÊRN²—Ã|¬srù½`޲™³‹æ.SÙÍl>³šSecÇÁ˜Îwž±ñ¼gë™Ï±Ÿ=h šÐ‡ž°¡½h+šÑ°£!=iýJšÒ—n­¥1½iÒjšÓŸÞ¬§A=jÀŠšÔ§îª©Qí1§YËWvò–_ýe9·ºÍp^óxË<ë1ãZɱ†u–Mk^»zؽV¯­ã\ì[×úÍÎöµ²sÍìeßo×Ç6¶°µ-kl7›ÚÓîvµ·lnÇpÕ2Sõ¹Õ]Ótþ¯Ûݾl÷»å}ÊxÏÛÞ’¬÷½õýTïÛß©Í÷¿ŽÆ€Üà—í÷Á^ê„/Üá©nøÃ%nÔ‚OÜâœøÅ5þËŠo,Òþõ¸‰np‹Ùßy¹UNn^ÛäÙ^ùÈ_îm’§œå2ùÉk®L÷üÙ(÷y´¾ó¡ Ûå9‡ùÍu>sq#æL/¹Ó›®ô¤Ôã'êøÕµž·¬oÝëËø×ÅîÇ®Ýìý}éÙÕɲ¯ÝíèûÛå¾Ä¶ÏÝîøMûÝõnźïÝï\êûßo¤ÀÞðXûá?¸Â/~yB‡<´%ô¢K=êTºå utÌOç¿|”È:ú§LþÖóK×|Ð'kÕS>ò®_=ÔmþyÚ£ô›?2ìuOtçiUc�èëé«nûÚßø™§ã•Óøöø>c{Tþ¨™Ïçg’©Ñ¿ôô×S}RÒûŒÖ>ƒÉÕïç9ñmì!±4#z¿ü‡z(Ô&ÆQ m×?øÏÈòàhCþ·ŠÊÀâ@ÓÀ¿üC´ø«Ž©ÿÃÉï D@BSÀ+ºñ)Àä ¬ÀìÛ¿EÊ@ ñ@qÀ41À¤´ |Žùùñ@ö[A„4tŽéŽ�ÔñAîØ»° ÷ÃÁ;ÓÁè°?–Ø"#<BKBæà¿á¾ÑpþBZj=Þ‹½Ô«¼ÛËBÙûBnã¼âC>2¾2ô¨<Ê’Ù#¾6<¾3„è C/ìB3 =7DÃ7¼C=dC>4²àÚB:|= ªÂc:>ÌÃDŒCD\DRyBˆÂ5¿+|D‹Äå›D ¬D(Á®!=Ò3«M´KŒ°ñ EQä3R|BL™SDE<SÅ/ÉÄWÅNÌ©Y¤ÅT´Å ÂÅ\„Å]LªA"?_L´‰øÄcDÆdTÆ›éEbäÄbÅ^Fgt0bª³f¤Æ³ÆÐÂÆll±m$œhl?M¦9´CFÜCELÇF¬C<dÇAœ ‚G.œÇpÇ¥zÇ@<GstGâG?þlG€¬Ç} HuDÇ_“G}ìǃT©{dšCüGƒŒÈ|ü6o, pd¼n´HÃHÁqȡĤ°Žä:É@Ó¡¬’i<I�#I¼YF2rÅ–±—¼•„£™¤I³ÉÝ1ErÜÉbD»œÀI­ É L°žì¥¼a<J¤,0¦½1I¨¬0©$<\š¤ª´J¡<ªtJ ìʤÌ)°TçKþÂJ&YËÔáÊ–šÈu”K†,HŠÔÂÝáKH¼¤G…t¨¢Ô#ˆ¬Ë¹ŒKºÄ–ÂDÌÁ4L¿”HÅLÌðZÈÇdL’Ì­ÌŒLÇÔÌ´$‹¶ä¿°äÌBKÉ· MóLÍþÒ¼ÊÑLMÕôʯ"M×tIÖ<K±”ÍJKɘŒ¼M;Í®a+·Ò«á$N½â+¿êÍjÔ!ЬætÎç4½ä\°ß”Îêü ê´Îì ìÔÎîô îôÎð$Jñ ÉäKAœLÂÜÌô\L&ÛË»„ϼÄLdOóŒÏ¾Œ®ùlLý´Kù¬Ïõ<O‚„Lú ÐÌäOõ<Ðö¼Oô¬Hòô…P‹Ð ð¤Ð ­% ÕP°°Ð õбùЊÑ%ÑõÐEQ UѽÐuÑ …Ñ…Ð¥Qò´Ñä´Oÿ,P]PýÏ=¯÷äÑ5Ð EÒÝÏ$e=�5R-Ò(ÅþÏ']R%ý"R)eP*íÏ,õÒ-ÕR;¼QÓ2e3EÓMÓ5¥6uÓ{Ó8å9¥Óô©S9ÍÑ; Í<ÕÓ´äÓ>íÊ?T¨ÔA ÊB5TšDÔÐÜÑ/R+EP& Ó#Ý=,SG¥T.THUÐI…ÒjÔKÕGÕÔNÅÔOÝRK%UO­ÒR ÕU=ÕVeU3]ÔDÉZµU‹ÄÕ\ÍÆ]åUgôÕ_õÅ`VZ$ÖbEÅcEÖMTÖe}ÄfuÖ#„ÖheÁi¥Ö ´ÖkÍ¿lÝÈWÍÔYÝTWuRp5Õ–‹Õ.Õo=×p%WoEUrWY]×r…Õt}WuMÕyWtþ­×~ÅW½×€­1åVmý¾‚5ØèCØ„u¼…eXÅs؇5¼ˆ•XÁ£ØŠõ»‹ÅX½Óص»ŽõX¹Ùu»‘%Yµ3Ù“5»”­Ä}eW}W˜Ò˜…5Uý×›XÊÔÍeŒÎ—µWyýÙ¦sYzÅY X£-Z~ÍT›ÍYŸ=ZÊT*ª>¥mZ¢­ZG¼Q–=ÊôÞTYÒÑZáÚî³Í¯ -ÚŒZ4[à Û©I[+,ÛµõÈÑÔ ©P“Ä‹ú{ȸ•[˜¤Ûc"»•*–ì[OüÛѰ‘ ±‘ÀõÔ�œ‘óàÛÂ-º•Áï8Á¹¸Ž¤ÙÀ™š\Ê5-h,AþÌMAÍMÁ�¡ ü´ÝÊ9ÜÜ@ÑØÜÿë\y@Ð}]°],ÔՎ˽Œýè\$ÜÝ +ËBþàÄÅÛÚU\Ä¥™Ñ]äÕœ¶})t$ú¹ ×µÞ²$Ý»eBºéÞÐU£¡E_š}Z§MZöõQ¦EÚ«̱Ežä°ÚøÅ_ªu¦ôÙþ•T™ýß 5 øÕßvߨ¢_C’û5`>`ZEÛ´=Þï ìu–'ó¥à¯„ÆUF¯ÝàÊ ß·u áÑU –F>aØá·à. n>V[æºŽÚÆaPÒa¥âáî¦>®^!Þ–© Fb‡!þâí3b&nâ7ñà*¶â+~(öÞ)þ4G,Tßöuà1æß�^_2ã¦õb|là2æTžKãü}à9Fcÿ}ã8nc<–Ë5Þ[9®c3îG..-Ö`AÞ4>ÉB6äì#äEþ7D^É-väIÃâOdáI>5HÎIEÆdð‹âKîdPÓd£ääP†¿F6eyåÀ,åTŽJ³„aIveÓ„e –åYöÍZÞá[Æåý²™\BeYrcqµcbdù-ævÕcMq'æYeË\àaVæd.•e–f�¾cjà?Îf@næÞ fn6æn. ^–PxfBâes¦³t¾?v^5wv¤u†g“ç½þhåz¶ç`Öç.æç~>äJ=PèA ÎâDè„Vh8΂þ4æ|Έ–è‰6¬žuè‹ÆèŒÖèæèަÖkÞf>c‘c’Fæcöc“Nikn–Öæ‘vi”Îã—.é˜&çqÆéi–iæéžöéŸê ê¡&ê¢6ê£Fê¤Vê¥fê¦v꧆ê¨N •žég¾i¬ÎilÞê¶é®†éHùj­&k®.k¯¦é“Îj³fk´¶jª–ê¸–ë¹¦ëº¶ë»Æë¼Öë½æë¾öë¿ìÀìÁ&ìÂ^¸¾ê¶ë·kÅ®é´^iȮ굾TĶìÆvkÊ^lÍ~lÆ–l:¾ì 5ìÑ&íÒÎÅ€��;GIF87aÂK÷�����­­®°°°³³³···¿¿ÀÀÀÁÄÄÄÆÆÆÈÈÈÊÊÊÌÌÌÿÿÿèÌ�ÿ�@��àÜ�>Ú�ÿ�¿�h�A������L����³l�Hë��@�`d�óB�2�@��À��Û��ÿ��¿���ˆ�0*����0ì9�Ú�ÿ�¿kà��Û��ÿ��¿È Ü��ÿ��¿��yÀ�æÛ�@¿�0��9����Xè��%����0.¸9�àN‰ˆZßÀ2ÿ @¿c����À�� ��Q� –�p�@�c��`ÿ�ÿ8X�ÝC�ÿú�‰ˆì*Ûÿ@¿0�c0�9`Ào @HÝÜÜ¿¿¿—ˆü-îÛ ÿS0�¦9���¨��?`�o�@ˆX�ÝCøÿ�¿øˆ��*øÀ @��S��t��­p0 ò9ãÿ1¿@˜8ÝÜ�ÿÿ�¿¿�ˆœ�îÚÀÿ@0e9��d¨��?������$O�BÚ�ÿ�¿�¤L�áÚ�&ÿ�@¿�0��9����X�Ü��±l�²ë��@@ØdpïC§2@@kÿ`�ÿÛ�ÿÿ�ÿ¿˜ˆÝ*�ÿ�¿@�Ž 9ë� p�p§�ÂhdAÚKL��K�l�ë����@� 0�ÜB�ÿ�¿ðˆ€Ý*Ûÿÿ¿@¿ôˆøÏýÝ÷ÿÿÿ¿@ÿüàÝ>#ÿ9¿�phòA�˜LÝ�€�6ÛÀ&ÿ���������������������������������������������������������������������������������������������������������������������������������������������������������������������,����ÂK�þ� ,H°`A *ˆpᆠBŒHp"Å)fŒ¸ÑaLJ /b yñ£B“IjTÉ‘¥G— E2„y’fJ™#q¢<h“§ÎžÎüITæÎ EEjThΦIK:]úTéÔ«QWfm¹õeטPÃZýZ“ìMœhÓª]˶­Û·pãÊK·®Ý»xóêÝË·¯ß¿€ L¸°áÈ+^̸±ãÇ#KžL¹²å˘3kÞ̹³çÏ C‹Mº´éÓ¨S«^ͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀƒ N¼¸ñãÈ“+_μ¹óçУKŸN½ºõëØ³kßν»÷ïàÃþ‹O¾¼ùóèÓ«_Ͼ½û÷ðãËŸO¿¾ýûøóëßÏ¿¿]¬bIe–OjU Wz• Xc-X–ƒg5(¡€8¡"˜¡‚2Ha‡‚á‡$bX¢†'r˜¢‡&¶ˆ¢‹*ÂÈâ‹4ÆXãŒ6æ€"Z¸bˆ?Ž(#C y#‘G©#’K*‰ã“LBéd”TNi¥Eb™¤–Mr)¥—U‚y%R’Ù£™AŠ©&šY²¹¥›]Âù¥œaÒ9¦xæ©çž|öé矀*è „j衈&ªè¢Œ6êè£F*餔Vj饘ZÀ¦œvêé§ †*ꨞ& @¦¨¦ªên�0àê«þ°Æ*무Öjë«�Àª¼öêëj­Þ*ì°ÄÎ @üªì²Ìnl±ÐFK+�$ÛìµØf«Ø³Òv+-µÖj+î¸äæÅ­·è n¹ì¶ëîZÏž‹ë´ò‹îºïæ«ï¾ Ä;­½ ÔëªÀÐâË/ª<¦içšLٰ·ñ›ÇYñœ×™ñUAܱÄS²Å!û;p¼›ÂÊm«œâš2Êóž›òÀÕ‚LÕÍ Ûœ³È8¸ðÏ3<2ÆCk\4Ç=—4R;“œ´ÃG ý´ÇSëìóÒXG ´ÖYWÍsÓD{mr°d<oÌf§]vÀ'³6®5}5×t{í4ØFÛ6þÞHó-µß[ë·SÞïÙk¯í¶ÙŠ'®öÛ‹·á”WÞëØ/¾²Û˜sžùÙ’‡kùè¤Wڹ㈎:ê \úë°3jrÀ(Ë;3í2kÞ9í*OûïÀÊ2ÁįïÁ'¯|žÆÛ|·®//ýô÷=ŸîõÑS¯ýöíYýñ¢s/þøåyÿ=ôÈ“¯þúÛ™~´Ù³/ÿüÑ‘jÿýøŠ,ýü÷ïœûïsÞþüGÀ€T×� ÈÀê ¼Uü(ÀuÍ‚uààú6· þ­ƒ !G¨´ *‚¬Õ/HB¨y0p",á Y(úІTà ­VÃæÐ‡þ<¼!åÖÃ" ш'Ba •¾"1ˆ?|"HÅ(VŠXœ¢Ç(è(%.1V+ä¢ÇH/~ná#£×ø3ž1tlŒ£õÆï…qŽ•ËŸ÷ÈÇ>úñ€ ¤ IÈBòˆìc±×D<Ž�ŽÄÏxÇH ’–¬Í$¿XÉLê “ž”Í&—ØÉPº ”¦|Í(SXÊT– •®dÍ*#ØÊXŠ –¶LÍ,XË\b —¾4Í.ØË`2 ˜ÆM"¹ÀdžÒ™¨æûŽ•FhVg‡Zl$ghB)ÞmˆßôæÞÀ9Nqî„‹L5³Î+fóí´¢<ÙYNwÆ“žeþÁæ=ß´M}Ösžðü'>9hN‚ÚS 4’æùÂÈ͆úóœuâA!:QƒtŸ… 2­é…Ú±‘ýeH˘Nðô•'õËFçâQFV3¥Ç„)_V*—–ª¤2õMsú–ÂŦ&åéµ|*T´€Ê\%õV1‹:)¢2Õ¨3M*ú^úÔT9µª"¹*[€ªTœbõRZýªCª®NU¬—C+eÌú-¯ªURd}ë_Ø ?·ÊõQq½kT¥ZWªêRyý+RùZ0» VQ=¬]èZX¿*¶6µ¨6»YQ‰^¡¥èe5;PËfq™†l&g:ÚÌJ–´§5­gQ»ZÕÂ(‘mþíœúIÎÒbö¶¶Ímjq»[ÝÊI 8@ @\â �ÅM®r‹Üå:÷¸Î}.r  «Î¾6¢×­lv7ÛÛîþÖ»ÛµnÖtùXÅ( H¯z×ËÞöº÷½ð¯zMUÞ¦Ö÷¾ÖL,~÷;Fýò÷¿ ô/€ì?øÀò30‚<>3øÁÔs0„'œ< SøÂ°³0†7l9 søÃ—±ˆ×çá›ø:± /?)ËÝïºXÅ®¥¡veÜbóöÅ4/‹uìÐÚ‚7Ǭµ±o…üc¦Èq¢-v‘|c"ãxÇAfò¥\ä/Ê1Ær“©üd+ÏXËSs•1HÞ›yz%>³þšcºæ6Ç.Ín޳UåLç<ÖùÎ!Ƴžßç=û±ô-Mè¡úÐÊê3¢Í<F;UŠ~´¤5sd1¯IÉ_ör¹ìäNsúÓ–öt¨A­iWºÔQµªQeVoÙÈ>î2P0½éUŸúÖ±µ«Ã¼ë1çšÔ¸¾r¯eýk[{Ø¢.ó¤— Xf;»‹ÏŽ6 ¥MíBEºÚØϵ³Ííîl»ÛàÆÎ·ÃMî錻Üèþ_º×]Ÿž›Ýð¾»ãMïïÌ»ÞøÖÎJRÐúûß�¸ÀNð‚üà}DN°µKëêzáµF6°}d�HÜØÂ†¸©)ÎñŒw|,;½¸Èþ¢ñ 7Ü  ùÇ#^òV·ü„*÷¸Ì3ýr^×Ü×ù͉m›{ŸôÝÒzõÞ*ôg½ÝDÏwMyut.6}ÙO—OÔ¬ô¸L>W7`Ö½õî%½ê=eú×ÁÞ–®³ÇìýC{¡Õž¶Ï–n¿sÜÍ3w£œìP]UÝÕw¼ãdïã|ƒÂo¿Uìjí»á/"xÂì¼Ó'wùÊ7>s–OžbÉJcNóËK¾ò”ï<èå¦sÏ¿º!çtäOŸzŒ‹þõ–WZæ ²ùÖs>ö°=îw¯{­”~ôŸ×äØÿwÄ£µñuF¾½‡Oü¬_¬Ê—sô½ÍüæSdúÜÁ¾ô´þ¿fîgÇû¶~ñõ^}ñ/ü×A?ðÔoböSÇý6¿óÉŸxù3þù_…?ˆõ_¿òÛŸöø‡UüÇa(­‡pzD_®Ç{€ù£€·§cˆ?h{4÷S¦gsņö£=ô{¹—j¨·(GXS] Ø{ ˆ‚Σ‚˜jŒõ‚»²‚$è3X<0h&(49¨.;8B"È€Á§).X<¢ÕsG¨@Ž¥QK(,ëô@O(AIX>Sh+Q8S€òƒPhX­Á…ä…²t…*$†ÀB†Ób†ú††LÔ„K÷'`ˆ…j¨Kl(+KMuFsˆ‡yˆFë‡eè†V·…}è‡RXˆþptˆ…x‡§ˆi(ˆÞ†ˆ‰¨…pˆ…ÿ¢2"AÅ*Px‰˜¸9A• h‡vx;γ‰ŠHŠ�ã2-S0a‰Ù§BžÈŠ#‹]‹nQ€Nh‹ªFꂊ¢Ø‰«˜: µ‡¨2³9oĈÂÄ‹¾Ø‹®(‡¸¸†É:©ó<ØhŒi¡‹ªd‰ms2¹ƒ‰Ÿè2¯È‰šø¸ƒŒª3嘊옎µÈ;äøïÈ2z8t艷ӊÖhü¸ÖhˆmÇ‹ûhŠeóŽò8;v¨yç'ij:Š©Œ÷hŽÞ8‘šó9ùˆé9©¬9«c)I¶ˆ‘"™;á(0ÌX3‘µ“þ9Šã‘ÙÜè)“©Ž0)y‰¹; é“"ÉŠ IŒ!Ù’$”F¹“´(3¦X‘êñ’F‰9(É5I‰y‘Oy”©‘ ‰|è•aù639“DŒJ)–j •=Y’òv’_9Šé‹/ÉïG—gé9Y¹‘½C–eEˆ|¹”<y•ki’ç”hy˜ê¢—Êä˜!I“ꈘ”É”‘é”} –~ùŒ, ™Òa•g‰•u©R„©1Y™èòˆ?¹Š¹2ÙšSÙ†Y³9ޝ 蘖 ¢Ù›S9Џ3<­ƒ›UI« ŽhI›¹•{q“_H/Sˆ™¢ñ?þˆÂÙšdÈ Q/·‰‚àùâ‡åyx•ˆˆéÙލœl¹ˆÁž’ØžÌñžc‰šÇq€õ71˜eøI•ÿùjzš6X„×ןÖ2 xejˆ=6„,(yüÉžþɃ%ô 4³  5šˆ ZdŠ/!Ês_6¢*„#ˆ "³(*˜Íh¡0Z/›lhŸËQ£6™š}ˆ£š¡£ñÙ£óÙF ú‡Eº£pèú3£e¤¤¢R…´á¤OʤÊ$¥ ¥g¥Ÿ‚¥uAcÈž\#¦»¸ˆdÚ`J¥¡YŸg:ˆëi¦jÚij£h˜…Š §F:§HÚ•2J§ß9¤u¤Aš‡þ>š@:<J¨€êž‚z§B§ôÙ§Ë)©\Ù'‡:—”ꨊ ©Dš©çq©æ’¨uX¨˜ªšÔ¨˜*Ÿœú?¨ªoú¨~z…¤z¦¥­j…žêª|ºˆZ:õÙ«ø«ÀŠpÂ:¬ÆºL·z^z†¹zªÍj«Ï:µxɺX¢z£‹ÊÓ*JÕʭѪmÝJ˪Û*¦áZ¦°šŠ›š§ªªŸÆQ¡íz ¬÷wGZ¢¢¦£ö jøÚ¡DH4ûª¢ýÚ c¯éš¯ªö¯ÀG õʯ ƒ°+:¯º°�;¡ 5.z®h¯ƒ:ªÙJGßåÊR×Z§û£‹“'[ !ûþ†»š®‹­«Ê¨+›±0‹-ë¦/»®êʱ3«­)û¥‹³A›‹[x¬mú…Hû³Î²´¬â´U µ•Ê'cÊ´&«§1[§I+´=ºµíæV €¯J¨^K®`«×Y¶ùˆ§“ʶS»'9«²C›ª7K·<;5«³–Z´ )²ÓÉ·ã'®{5·‚Û¥ƒ[·…[F‡{·ˆ:¶>««~«q;˜†û·y /Öj¹„¹Ê´¸»§{{¹”›¸ƒµ¹.˹x1¹Û˜¹’ ¸2á}i¦ºe7²Ê©…WvŸRºˆûS¹ÛS½«¬®Û€¿‹»ž¢»Œ[Së­É+®Á»ºŽ+³{º©Û¼×ǺÆþû¹Êj½Ó+ºÎ«¸š»»Ùû¼$û³»ÔÛ€Ú ¼ÜÛ·Ò«¾¦«·Ùé¹Ð ºÄA°<ûB«W%)w[&ùk%û;7«0ÿ+&œ¡ý{&üpfqÀšÀ?$¡7Èqöû¹ËML±a–Á±’zÁ Û‚,BÌ¢&,²»¶à+­ç›™ï ®ë»U´+«%›)\–+ì­/ ²-<;‹½š Äv+ÄJÃâÖÃ÷'¾µ‹¶O8«FhÄÇÅÖqãº;Œ®Ç[ÄWl³YŒ«[ ¿p‹Ä‘úÅr›Ãæ*Å.)Æè›¤Æª¶Ñ$µ’Çò&ÇtGÇ© (Á5\ѵÇ|ÜÇ~¬\þÔUƒ¹‘Ç\Ȇ\ȼ„|ÈŒÜÈɕȺ±È‡L-ŽL\”ìÇœ’\ÉœŒÉ*ø¶zr^ò5ʤ\Ê鹿¹!ʦ¼Ê¬ _¨ŒªÜʲ,˯|±¼Ê�0Ëé•Ë¥\Ëæq˺ÌòåËwüvü3®Æ<¥Ì¶ÌÛ‡ÌôãÌÐüÍœvÕ¼<×œÌ ×ƒ œ°°8÷°á¬Áã,ÎU{Õ;qà|Îæl¢|¢<Â'\ÎLÎî|ÏèüÍø Ï¢Îk,¯ö\ÏùLÐTÂÃ)Þ<^Ù Û<̓IÝЖòÐo'˜”ÑM)`J ½Ñp%Í=WJÔ,þÒ=GÒí®„ww*Ý(Í>}†K6Ó²ÃÒ4M¿/íp8(3mwëÓDýÓÖ¦ÓB½'ÐF-<HÍw’»ÎêÙÔ‚ÔP]ÌL}~T]ÕOM>¶S`½Õ[ØÕƒ—¾ZíÃb ·d->å¸I¬ô\±ûÌÎýÌÏs-Ð-×ïl&�=Õ ¼ÐxíÏŒÐ0÷w8ˆkq=Ðz­Ï{M×wý×í,ØíÔ×gØvÝØ–íØVCØcq€Mi=VíÕ…‘Ò¡­JkÍ=çfÚ§ý…©½=«ÝÚˆ2ÚeѲ}(´ÍÖ¥}Û†’Ûª½Û¼M(¾ ÛÀÜ\m´ÜL¬mÜÂôÚÚÛÌNþuÓnÝÒ¶ÝCWÝý¾;­ÜØÝ¯[Ôo]Ò×ýÝñq»SÞ•MÞmÞH§ÝÓÍÞMiŸØy­Øø}ßú-Ù‹mÐÙÕ×Ô}4üØöíƒÇ ‚BÄÙ>8ÏõÝà‘}ÙŒáþ­ÙÞßõàDŽÙýÀì„!(ÏîÞÁ1ÜÖ|¶<mÒ$®ÖÈ­Tܽí½âzbâîñâÜ­â2Ž'4Þ=j<Õ1žãþ±ãg÷‹Ã蜺ÓVaÛÓå äü!äìêÐhš4«ÐÍäMîÜ5NäÄiœÃC6ÞCåÅmå.Žå<.ŒŒÙ–€™Ÿ)îÒb.Ýd>äfþ”­ˆ’‰ÕóÝæùáämk–þ¬#‘b ›kèx~Þoþä…©:’išðY¹b»äƒþÞ³»Ø8® –ÙŽÚ[åé2,锃í©x޾éRWè{ÞŹػš~ážÙ°Þá>â³nD”á­Ná� ÁY‚I ¡à[ãÙ>¹ëÅ.ë~ìÊ>â·ÓÆNëÈë[âëìY#ìuÍФN£¦Ž·d ãÛ}çÛ^ê-žç=¾Þ=îô¡çmgÇá;êêîuå.Ý(Êlïòé.^ïCýãø^ãÝN´h¬Ý8þïùÞéû>ðá-îÿ‡ÿ©ç.Þ€±Ü Ÿì.ðß>ï_ñ¹žÎžæ³Oþwï¢#ïðo6¾V)_•%ïÅfž/ßv1O-?{·ìÃþì=ìžßü-ôüÜìK­ëNô×.í¯NØ>ë<Ÿí>/õ@ßôQ_ëTdô^õI/¢Lßõ í5¿+ßn'¿-cÿ©7O­ ÂiOwkÃÿ£o_>qÏÃm_÷÷îÕãîË÷Úv÷,Ëï(øƒ_öënøÍø,Køövöïø¯øÌ,ùˆ±÷”Õoîyš¿ùf}ãfÿùú¢OºŸðsoéŸ}ï9ïö¯¯o±‰¦ï;/öVÏû`ïê¿ôJ?õX¯õJÎõÃÁ_Ÿü‡ÝàWþŸì¾ÏüÑ¿ßÀ/ý?¯yE²áAOý‚#<ý']ìµò–/u˜¨?þŸþzþ†Áþêßù«ïù­_ªñŸ~·‹¹ßðÿ~ÿÿ�±@à@‚ D˜PáB† >„q �-^ĘQãFŽ-0€@âH’% 0™RåJ–-]¾„SæLš5mÞÄ™SçN”%{î´&EEÕR¤Ð›?™>…UêTªU­^ÅšÕ§I§Z½ VìX²eÍžE›–l¥_·º…Wî\ºuíÞµÚ5¢^¼}%&8PÀÀ`Â… FœXñb$ð»odÊ•-_ÆœYóÈÉ;oþ®¬ Ò¥MŸFZõjÖ¥(�½àslÚµm߯{ålɺ}ÿn \øpâÅËå­0ùqæÆ—7‡]útê²¹VÇ>üyvîݽ§»Ý xðå‘›GŸ^ýz¦äO²‡_×=T Ôoxß>~†úóó_È¿þ�THÀ� LÈÀDHÁ<ÈÁ!4HÂ),È 1$Hà 9ÈÃAHÄI\ÀÄQT1E_Ü/Æÿd´îAåN¤ñÀÜÑÆ°Ç …¼È üÉ“`RÇ™ ²I¥ô1J+¼rÊ,«Ä²K-½äòK1Ã$sH*ÍÜM0Õ“Í2‹<ÎÝ|þoE%qŒóÈ<“ÜsÄ;íìÏ4å\“Ð6 M�( ÕsÐø…4ÒóÞ’´Òªæ³4SM7Ó995'OC%µTSå»îTU_uUW_…•§Tc¥¢VkÅ5W]muW_'ú5Xa‡å•Rbs½õXe—ÝÔÓd™…ïYh§¥6=g«Å6[m·ÍîZn¿7\qcóv\sÏE7ݬÊU·]wß…&v㥷^{ï­“$iñå·_‘íõ_t=´QFùtáƒý Є~XÐ…%.XáŠ!žØáŒ]ü“ã†=ŽXã‹)~³ÑÏ<ü©E• .Ùâ–1Yä—I¦Ñš ŽäWî˜g–oþvh˜g–YhšmFg¢uΙç™é¥¡Þù礃¾zh£‹–ód S¶Zé­¥›ê¦ÃÆZì¬V»í´ßÖzm®ÝŽ[`»·÷n½÷æÖ¼û<pÁ%ý{pÃGœ»Âg¼qÇ; øqÉ'§\³Å+Ç<sÍ¿º|óöÔ=tÑÏò¼th;7'¤Vg£}S‡ðÈcЍÖooýuÚw_uÞi²wáÒýwã¿óýxy‡g¾¨â•‡žºä£g)øæ¯¿èyê·?nzî¹+{Ü­?¬ïÏÔ{ô% ë#áÉ¿"í+ìíºáfÿ¹õ'[îþéÎßý@îo€,àÿþøg¶©Ù/ ,”¼v#VP¼àhAú¯ƒ�ä ŠÚç¾öÐ#dÙˆ ;"tåƒIÚ`CX?>-†/Ä 5Xèép†6äaÕ‚x6`µ lCl #¨DÞЉž Q¤ØBùeÏvSüÈ[H>Jq1\_Qe,1¶D…X!E¦¨E*V±%ÄåWF:‚G}u”LÝxÅ4ÆÑoÜ£ÙB<;w4äAºè¾66Ò‘ƒ$‰ÆDV:ˆ´äDÂ'?r2‹Vì¢Kh½4–0“§$&O ?ì5o~¨„åºfKå´Ò–s¤e.k£ÊWΗ¿¦X^þ£º[¶ò•ºDf{f9,Vó–�x 1éÊdV3¼tU3§‰=�°EšÛ|Ÿ5ÅIl®J›àdžR¾Çs®…ãg<ñRNUµã É:™‘s²ñvÇ”g@]BÏSÙóž¹Ë§N´ ¿w&¥üä#:Q¸ÔTjL!;z”(.ò‘®K¨¨RXJñyrŸ|Ü#FùIQ–jÅ¢¥R)?“òÏ’Êô£q )NúÇF’ò¤¤bL㨓'Ѩ=,*R™B6Õ‡NMª*U¥N•ˆ™`¢vhÕ$ru‰ ‰i(}JÂ-¢ô*ä;o~•!;í©?·øSHnt¨9„QT¯ZþU¾RÕ¯{ýkWû X 6°n=li”U„Ñ«Š},SŸªR9:²£~«P# Ô¶²oiZTºJÒ®ÙûbÇZºÚ—’вœ­©eáJN²j•Ÿgu:Ò>ú3¬£å>“²ZâR¥µ¡zífGÛÓ*úT’°}¤sÚ”ƒÂ³¸×}ÊqA•ÜÊòÔ¶Ñm® »»ÇéÚÄ Õ½+vÕ++2*ë·Y«wYèѹ–Ô¶U§BÑ›»õöWTËZ÷û¾ò%˜¦<�ë¼>a<aHi—S†0HÛBaW¸Á¿Âp†“"á—Ø<n–ˆÓIb·¸;(ÖTˆUœ=þ»ØÆÒû°¯¼c‹E·7ò!s¼+Ï8·r’¥ãLÙÈÝD²’¥Ü=ðõØÊWÆr–µ¼I##4ʡҫa ‹Ø16±’ 3™Å¼f5·ÙÌeFóbáœÚÈÒy°KsòŒóûf6+2ÍŠýsœëŒ×¥Úù̆ž3¡½Õ;»YÐæ3š{hECúÑ”žH—ÿIâ§N6Јžr,Š,M{™œ¡FuJF«<«xÏ}YuªkZµZįž§¬u½—zÙ:ø¾ ­w]aû­Ô¬¶]Š=lâ.;›Ç^]²ÇÈlj+²×ÐFŠ´ÃSmnÄÙæÄ6Gk—ow[žå.h¸‰7îŠþšÛÜ辨ºÇn·ÀÛÝÕ´·kåá/OûÞÌÎ7•·ïÒ›sÿ®vÀcà-7¼,?6©­ðâÌÏ׆ò©éòéJcú‡–µ£Ci’_:ÑŒö8T Ýq-MúäO9Í2Y‚»â(¹l8¾ÃÃ<çî¹Êíó>›¼è#7ºÈY~ôJ»œé8:ÑóEóš¸ßBù’Vu‰â©JÕ­®ñ®ëúëÚ »Øqzõ¹œ½ì‰t»¥.>`m·ýíf¿¶ÚiÌöIÝÕq¯ÔÜ÷[÷¾û}Ê€'œÞ÷NvÃKñéSüG î•Ç7}•”àÑKxr[þðy<çÛíy%cþ>>š¯®èëMúÒƒ^ñª?8ëƒlúhE^~“w©ìgïz½Ãžòº2íÙƒúƒú>÷ߺÔKŽtæ/]é\_¾ó£ý¤O_ùÕÇ>œþüë7ŸúÞǪÃÅï㫽cŠJ¾Êƒnþ¡c=æë‡úÙŸõïK?ûö·óö­ïþŸÇ¿!�Á` €�ÀÁ(À$@<ÀlŒhâ>þKÿ‹àÃá[Ñh äÀô@Ò¦ÈÀ@ ¤Ô%$ÁÔAAZZÁôœ„%tÁÌ‘ÁUšÁ«ÁLÊÁ”œ¬$äÁÆBC Dœ"Ä#$4ÂÁQB:jÂ%œ'#)„½¡þÂõ¹Â*´›,<.ÔBñBî¡= |?ù#Ãþ3Ã4l¿ù+Ã5¤@ý»¿ú“Ãîÿ:œC ŒÀA?7Œ‘3Œ:<Ü?6DC>$Dú£Ã;Ä?œ?8?@ŒÃC„DGlÄDŒÀ=4DIüÂ#$½0ÌÄ{áDêùÄN¤—P„RÅw1ÅãIÅST—UüWdÅsÅÝ™ÅX—ZŒ\´ÅoÑEÜE–êEÓ Æ_Ä–a|Abœ(cÜeDFfaFlÆ€zÆ ŒÆsÛĺðÃ<ÌÆ@ÜÆGÄDDìÆI G;ü8F$ÇoŒDJäF5¼DOcGGp|ÇAlÇxLG{DÇ|TGo„s”ÇBþlÃzœGEŒ!K È©F|™ÆÊYÈ„±ktÈBzކœŠŒÈ {‹|¼È&ËHˆìH,,‹·É £‰,I“¼¼ô<ŽTIóŒ”|I”É™üÁš´I"ÄÉœLÂäI'ôɬÀÇ}ÇÔF£\G€¤Çƒ\ʽòÇ{DJ~Ê©L?ƒlJ¡J¥$Ȩ,J­<J¯LJK“¹Ê¯˳dÊ­l?«TË üÉqqIƉ˷l– ¤Ë•lÉ»„;»ÔK1ä˾Å¿ÌRÌÁTÅÂ4ÌWDÌĤÅÅdÌ\tÌÇôż”L¼´¼¹¬LõÀÌÃC®<G¢ü̬D˲ Ë´4KÓü¬þ§ÔGÑDM©ôÌ|+ªdMÒtͱìÊÑlËÖ¼MÝ M9ËMÚÜMàìMÛ<G¶<ÍœÉÌÓ‰ÌäÔœÍ4çdNäYÎèdÈé¤ÎŠ´ÎëÜÈìÔNw¿+ AûÎñ‹ð,¼îìâ3&1ÛK!ö<Oô´B‚Ë8sϤ¸¹Ø“Ïô¤OÜ‹·û\;ÛO½QOnòO˜Ð�ݶ国ãûϽ=¾Ã èdP´© …¯m‚Pe%.°t¢¯Z¼½Ð»á¢ŒŠ­iêP}ûPá²¥ñrµ;ä#NØäMNýÍ=N-M¬ÊP±Z.“Ú¬à2R R ­MÐ2«²*Òú:þR#õ(Óº¬¸J(ÐL ã\Ò!™M& N0NÜüÑ2íÒ35ºU.¹ª,$Õ£ª;-…ÊÒ6=+ûâ¬çº­ï‚¤´ÊR|äÒ&Eȵ›²ÓÑ:T6=­E®5õ®I‚.ÙÂ/èê(é¢Ð«°PB 6CTJ*ËÒ°�sTðU6/O%«:P¨ÈTM•NU.HM+ÓZQ%%²R=ÕY½Ó"MÔG ªUÍ.W5•Ò†Ú¨3êS"UÐö|Rc=#=ÒÓˆª-D½R>Vaõ:+‚6FÝ.ݼkÅÖíñÖ»Ôg“Ð WQëÏr·sUUvMW,\WQuWn×RœWû¬Wpþ½WUÌ×öÜWxíW1$ÏËÏZ)Ø‚=ØBú\ØX×Ô{X†½<‡Ø•Ðú¤X‰üWs×w•/5SAÝÑ9%S4 Ó‘MY>‹Xc;9]Í×Ò€5Ù…J‘EÙ1µÙ˜¥Sž=Y’S¨kY|zYœÚ±ZtÊ/˜eÚ ÛØéHZSÓ×sµ×§ÍÅŽ½UšµÚ:ŠZdã×ûتÝZaÄZ]éZýÚ±}Á²%5­UÛ“d[V Û´}[¤Ï„ÅÛ¼%Ϲ½Øº…³Í6º <¾õ[yÝ7±m2Â-Ü.Œ[„UÜÅÃÆ­µÇ…\P”\ˆ¥ÜÊ-E½ý¥‰í%Îu8ÏÕ\´Xþz¥ZÑ]£]Ù£ÍYUY-YŸÝY\ŽZئ•Ù|¬Ýgj+ÜíYŸÉ]ßýÙÕ%^Ø Úš]á“Ý·¢ ^Õ= ÅUÞä£ÑÕ æå7ÓÍXÁµÞ#¼\csÛî5ì-¸¾Õ·ð_Þ!ßPÚí5ßôÝÈïÕÛ°¸Ì…ß«=\î-"Ú¨_ô½ß¿•_Πߴ£Zýýß àŒÉÌ0 ™X_W2à&Ðæµ]"`÷•à¤àbáß NPÄÍ`þÌß÷í 8ÿ áæÜ`ŒýàNa0\aEáfÈ>ß&aï€^×m]ä=^Ù¥Þçb®]_ºÝöa¤-â-ë]$þb¬âá5Þ×åaÖµââ­b,&:À@ÂpÀübÆãÅH@Ç€ŒžÞ?À.&cc7c8ncÅ0ã$îá'¦áÚÐÀäã>F óÜ•=öãA&ä ä<FäDVäEfäFväG†äH–äI¦äJ¶äKÆäLÖäMæäNöäOåPž'Nc*¶ã+žâ;.eU&åVŽâÙuåß}¢X–â,NeT¶å\>e-ÆcV~å –å_Fc'åb6æcFædVæefæfvæg†æh–æi¦æj¶ækÆælÖæmæænöfY£eXfSîe\Þå[æåU6çrNçpæZ¾Ùqöå`¦gxfgt/Æg]¾g}Vçv–çuîç|¶ o&è‚6èƒFè„Vè…fè†v臆舖艦èô �; # # libdax is the early working name for the successor software of libburn, # a library for writing information onto optical media, i.e. CD and DVD. # The current working name is libcevap, refering to various kinds of roasts in # Europe and Asia which share the property to be structured in smaller pieces. # # The reason for the replacement is the unclear copyright situation as well # as libburn's sketchy original state and the subsequential evolutionary # damages done by us in libburn code. # This does not mean libburn is shaky. Its current state just reflects the # virtual conflict of at least two programmer personalities and their goals. # # # Please: Nobody shall take the development of libcevap as a reason for not # programming an application which uses libburn. # libburn works now. libcevap is planned to work in future. # # libcevap will replace libburn in a controlled, application friendly way. # The first application of libcevap will be a libburn API wrapper which will # allow to perform all API calls of libburn which are proveable to work in # the current implementation. (Some CD stuff is not understood by us yet. # We will have to learn.) # # The libburn API will be frozen when libcevap has closed up to its current # capabilities. Nevertheless it may take profit from some of the future # progress in libcevap (e.g. new media types). # We hope that finally libcevap will have capabilities superior to libburn. # This will then be a reason to port applications to the libcevap API. # # Application programmers are advised to encapsulate their libburn API calls # in an own abstraction layer. The semantic concepts of burning will be # compatible between libburn and libcevap. I.e you will have a library object # to be started up, drives to be found and grabbed, media and their states # to be identified, sessions, tracks, burn options to be set, blanking, # formatting, and so on. # Data types, function calls, and quirks will be incompatible between both # APIs, though. # # ------------------------------------------------------------------------- # Originally this was a backup of text input clicketitoggled into ArgoUML # Meanwhile it becomes an intermediate storage for attributes and # class interconnections in the notation of my C stub generator CgeN # (see also end of this text) # next : work on completeness : cevapformat # Open questions: # - how to connect to GESTURES ? Globally ? Model=libdax ClassDiagram=Overview Class=API Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=11.03.2007 Documentation=\ The API is the only layer visible to the applications. It exposes MMC concepts which it reflects and augments by its own architectural concepts. Subordinates=EQUIP,JOB,AUX Cgen=\ cevapi -r -m struct CevapequiP *equip -r -m struct CevapjoB *job -r -m struct CevapauX *aux -r -m struct CevapgestureS *gestures @ =end Class Class=EQUIP Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=11.03.2007 Documentation=\ EQUIP represents the physical and logical equipment in reach of libdax. This includes the system, drives, media, and their current states. PeerToPeer=GESTURES Boss=API Cgen=\ cevapequip -r -v struct CevapI *boss -r -m struct CevapsysteM *sys -v struct CevapgestureS *gestures @ =end Class Class=JOB Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=11.03.2007 Documentation=\ JOB models the tasks to be performed via libdax. This includes disc, session, track, source, fifo, dewav, burn options. PeerToPeer=GESTURES Boss=API Cgen=\ cevapjob -r -v struct CevapI *boss -r -m struct CevaptodO *todo -v struct CevapgestureS *gestures # >>> @ =end Class Class=AUX Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=11.03.2007 Documentation=\ AUX bundles any models which are neither EQUIP nor JOB. This includes abort handler and message system. PeerToPeer=GESTURES Boss=API Cgen=\ cevapaux -r -v struct CevapI *boss -v struct CevapgestureS *gestures # >>> @ =end Class Class=GESTURES Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=11.03.2007 Documentation=\ GESTURES ist the procedural repertoire which interconnects EQUIP, JOB, and AUX and also provides to them the services from the SCSI oriented layers. PeerToPeer=EQUIP,JOB,AUX Subordinates=SCSI_CMD Cgen=\ cevapgestures -r -v struct CevapI *boss -v struct CevapequiP *equip -v struct CevapjoB *job -v struct CevapauX *aux -r -m struct CevapscmD *scsi_cmd # >>> @ =end Class Class=SCSI_CMD Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=11.03.2007 Documentation=\ SCSI_CMD represents the semantic part of SCSI (i.e. mainly MMC) specs. This layer models each SCSI command that is used by libdax. It knows about its parameters and constraints with particular equipment and jobs. Boss=GESTURES Subordinates=Classes with SCSI_EXEC Interface Cgen=\ cevapscmd -r -v struct CevapgestureS *boss -r -m struct CevapsexeC *scsi_exec # >>> @ =end Class Interface=SCSI_EXEC Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=16.03.2007 Documentation=\ SCSI_EXEC hides the difference between the implementation principle of SCSI format+transport and the principle of SCSI service. Boss=SCSI_CMD Implementations=SCSI_FORMAT,SCSI_SERVICE Cgen=\ cevapsexec -r -v struct CevapscmD *boss -p -v struct CevapsforM *scsi_format -p -v struct CevapsservicE *scsi_service -v int silent_on_scsi_error # >>> @ =end Interface Class=OSDriveAspect Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=18.08.2007 Documentation=\ OSDriveAspect encapsulates operating system specific properties of an individual drive. It shall be handed out by SCSI_EXEC via the GESTURES layer to EquipDrive where it forms the link between cevap drive model and operating system driver. This class description models the implementation specific to Linux. Cgen=\ cevaposdrv -r -v int fd # >>> ??? implement the sibling stuff which never worked properly ? @ =end Class Class=SCSI_FORMAT Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=11.03.2007 Documentation=\ SCSI_FORMAT translates parameters of SCSI commands into CDBs, takes care for transport and decodes the reply into parameters. Boss=SCSI_CMD via SCSI_EXEC Subordinates=SCSI_TRANSPORT Cgen=\ cevapsform -r -v struct CevapsexeC *boss -p -v struct CevapstransP *scsi_transport # former struct command -v unsigned char opcode[16] -v int oplen -v int dir -v int dxfer_len -v unsigned char sense[128] -v int error -v int retry -v struct CevapbuffeR *page # >>> @ =end Class Class=SCSI_TRANSPORT Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=11.03.2007 Documentation=\ SCSI_TRANSPORT takes a formatted CDB from SCSI_FORMAT and makes the operating system perform a SCSI transaction. It then returns the reply data in raw form. Boss=SCSI_FORMAT Os_specific=yes Cgen=\ cevapstransp -r -v struct CevapsforM *boss # >>> @ =end Class Class=SCSI_SERVICE Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=11.03.2007 Documentation=\ SCSI_SERVICE provides the combined services of SCSI_FORMAT and SCSI_TRANSPORT via a set of parametrized functions which abstract SCSI command transactions. Boss=SCSI_CMD via SCSI_EXEC Os_specific=yes Cgen=\ cevapsservice -r -v struct CevapsexeC *boss # >>> @ =end Class =end ClassDiagram=Overview ClassDiagram=Equip_overview Class=EquipSystem Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=16.03.2007 Documentation=\ EquipSystem is the inner root class of EQUIP. It describes the system on which libdax is working. This includes the operating system, the system adapter classes, the drives. Boss=EQUIP Subordinates=EquipDrive*N Cgen=\ cevapsystem -r -v struct CevapequiP *boss -r -m char *infotext -r -l struct CevapdrivE *drive -p -v struct CevapdrivE *eol_drive # >>> be boss of SCSI_CMD ? (Rather than GESTURES) # >>> @ =end Class Class=EquipDrive Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=16.03.2007 Documentation=\ EquipDrive represents a drive, including its capabilities, its processing status, the media loaded. Subordinates=EquipMedia Boss=EquipSystem Cgen=\ -l cevapdrive -r -v struct CevapsysteM *boss # Drive number -r -v int global_index # Persistent system drive address -r -m char *devname # Traditional SCSI address parameters (-1 if not applicable) -r -v int bus_no -r -v int host -r -v int id -r -v int channel -r -v int lun # (former struct burn_scsi_inquiry_data idata) # From 12h INQUIRY , spc3r23.pdf , 6.4.2 , Table 81 -r -v char vendor[9] -r -v char product[17] -r -v char revision[5] # 1= above elements contain valid information -r -v int idata_valid # mc5r03c.pdf 5.3.2 Physical Interface Standard # 1=SCSI, 2=ATAPI, 3,4,6=FireWire, 7=SATA, 8=USB -r -v int phys_if_std # MMC-5 5.3.2 table 91 , e.g. "SCSI Family" -r -m char *phys_if_name # System despendent aspect of the drive (e.g. int fd;) -r -v struct CevaposdrV *system_dep_drive_info # Result of the CD write mode x block type tests: # Index is for write mode : 0=packet , 1=TAO , 2=SAO , 3=raw # Bits are for block type # Numbering as in mc5r03c.pdf 7.5.4.13 Data Block Type, Table 668 : # 0=RAW0 (2352, Raw data) # 1=RAW16 (2368, Raw data with P and Q Sub-channel # 2=RAW96P (2448, Raw data with P-W Sub-channel appended) # 3=RAW96R (2448, Raw data with raw P-W Sub-channel appended) # 8=MODE1 (2048, ISO/IEC 10149) # 9=MODE2R (2336, Mode 2 formless) # 10=MODE2F1 (2048, CD-ROM XA, form 1) # 11=MODE2F1X (2056, CD-ROM XA, form 1 plus 8 byte subheader) # 12=MODE2F2 (2324, CD-ROM XA, form 2) # 13=MODE2MIX (2332, CD-ROM XA, form 1, form 2, or mixed form) -r -v int block_types[4] # (former struct scsi_mode_data) # Information about the drive's capabilities, obtained via 5Ah MODE SENSE # from mode page 2Ah , mmc3r10g.pdf , 6.3.11 , Table 361 # (which is deprecated in MMC-5 E.11) -p -v int mdata_buffer_size -p -v int mdata_dvdram_read -p -v int mdata_dvdram_write -p -v int mdata_dvdr_read -p -v int mdata_dvdr_write -p -v int mdata_dvdrom_read -p -v int mdata_cdrw_read -p -v int mdata_cdrw_write -p -v int mdata_cdr_read -p -v int mdata_cdr_write -p -v int mdata_max_read_speed -p -v int mdata_max_write_speed -p -v int madata_min_write_speed -p -v int mdata_cur_read_speed -p -v int mdata_cur_write_speed -p -v int mdata_simulate -p -v int mdata_c2_pointers -r -v int mdata_underrun_proof # Results from ACh GET PERFORMANCE, Type 03h # (Speed values go into *_*_speed) # (speed_descriptors became cevapperf which is under cevapmedia) -p -v int min_end_lba -p -v int max_end_lba # from mode page 01h , mmc5r03c.pdf , 7.3.2.1 , Table 657 -p -v int mdata_retry_page_length -p -v int mdata_retry_page_valid # from mode page 05h , mmc5r03c.pdf , 7.5.4.1 , Table 664 -p -v int mdata_write_page_length -p -v int mdata_write_page_valid # 1= above elements contain valid information -p -v int mdata_valid # The mutex shall be used to coordinate access to the drive in situations # where multi-threaded race conditions could disturb operations. # E.g. lock, read busy state, interpret, set busy state, unlock # A mere reader of the busy state does not have to lock because # reading of the state itself is atomar. -i -v pthread_mutex_t access_lock # Flags from feature 002Fh feature descriptor mmc5r03c.pdf 5.3.25 : # bit1= DVD-RW supported # bit2= Test Write available # bit3= DVD-R DL supported # bit6= Buffer Under-run Free recording available (page 05h BUFE) # Value -1 indicates that no 002Fh was current in the features list. -r -v int current_feat2fh_byte4 # 0= drive is grabbed, 1= drive is not grabbed -v volatile int released # File descriptor of an eventual emulated drive -v int stdio_fd # >>> ??? # (do we need a drive owned buffer to carry data from call to call or what ?) -v struct CevapbuffeR *buffer # List of profiles as reported by the drive -r -l struct CevapprofilE *profile -p -v struct CevapprofilE *eol_profile # Media currently loaded in the drive -r -m struct CevapmediA *media # >>> transport.h : toc_temp (what is this ? It belongs to BURN_WRITE_RAW) # >>> @ =end Class Class=EquipMedia Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=16.03.2007 Documentation=\ EquipMedia represents an optical disc, including its type, its writeability, its formatting, its available formats and performances. Subordinates=\ EquipProfile*N,EquipFormat*N,EquipPerformance*N,EquipStatus,EquipMulticaps Boss=EquipDrive Cgen=\ cevapmedia -r -v struct CevapdrivE *boss # Volatile and/or public properties of the media -r -m struct CevapstatuS *status # MMC-to-MMC feature info from 46h for DVD-RW. # Quite internal. Regard as opaque :) # 1 = incremental recording available, 0 = not available -r -v int current_has_feat21h # Link Size item number 0 from feature 0021h descriptor -r -v int current_feat21h_link_size # Wether a DVD-RW media holds an incomplete session # (which could need closing after write) -v int needs_close_session # From 51h READ DISC INFORMATION # 0=needs format start, 1=needs format restart -r -v int bg_format_status # From 23h READ FORMAT CAPACITY mmc5r03c.pdf 6.24 # 1=unformatted, 2=formatted, 3=unclear -r -v int format_descr_type # meaning depends on format_descr_type -r -v off_t format_curr_max_size # dito -r -v unsigned int format_curr_blsas -r -v int best_format_type -r -v off_t best_format_size -r -l struct CevapformaT *format_descriptor -p -v struct CevapformaT *eol_format_descriptor # The specific capabilities and restrictions of the media -r -m struct CevapmcapS *multicaps # Results from ACh GET PERFORMANCE, Type 03h # (Speed values go into drive.mdata_*_*_speed) -r -l struct CevapperF *speed_descriptor -p -v struct CevapperF *eol_speed_descriptor # >>> @ =end Class Class=EquipProfile Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=16.03.2007 Documentation=\ EquipProfile maps a MMC profile into libdax (See mmc5r03c.pdf chapter 5). A profile describes a set of features and may be either current, possible, disabled, or unavailable. Subordinates=EquipFeature*N Boss=EquipMedia Cgen=\ -l cevapprofile -r -v struct CevapdrivE *boss -r -v int is_current -r -v int profile_code -r -v char *profile_text -r -v int is_cd_profile -r -v int is_supported_profile -r -l struct CevapfeaturE *feature -p -v struct CevapfeaturE *eol_feature @ =end Class Class=EquipFeature Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=16.03.2007 Documentation=\ EquipFeature maps a MMC feature into libdax (See mmc5r03c.pdf chapter 5). A feature describes a set of SCSI commands and (implicitely) of use cases. Boss=EquipProfile Cgen=\ -l cevapfeature -r -v struct CevapprofilE *boss # >>> @ =end Class Class=EquipFormat Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since= Documentation=\ EquipFormat represents a single Formattable Capacity Descriptor as of mmc5r03c.pdf 6.24.3.3 . Boss=EquipMedia Cgen=\ -l cevapformat -r -v struct CevapmediA *boss # format type: e.g 0x00 is "Full", 0x15 is "Quick" -r -v int type # the size in bytes derived from Number of Blocks -r -v off_t size # the Type Dependent Parameter (usually the write alignment size) -r -v unsigned int tdp # >>> @ =end Class Class=EquipPerformance Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since= Documentation=\ >>> EquipPerformance Boss=EquipMedia Cgen=\ -l cevapperf -r -v struct CevapmediA *boss # >>> @ =end Class Class=EquipStatus Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=17.3.2007 Documentation=\ EquipStatus represents the status of media and drive. This includes blank/appendable/closed, progress indicator. Boss=EquipMedia Cgen=\ cevapstatus -r -v struct CevapmediA *boss -v int status -m char *status_text -v volatile int busy # From various sources : free space on media (in bytes) # With CD this might change after particular write # parameters have been set and nwa has been inquired. -v off_t media_capacity_remaining # Current write address during write jobs. (Next address to be written) # <<< does this belong to JOB ? -r -v int nwa # if > 0 : first lba on media that is too high for write -v int media_lba_limit -v struct CevapprogresS *progress # >>> @ =end Class Class=EquipMulticaps Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=14.8.2007 Documentation=\ EquipMulticaps represents media dependent properties and media states which are either volatile or especially interesting to several other modules. This includes eventually existing sessions, closure status, profile dependent capabilities. Boss=EquipMedia Cgen=\ cevapmcaps # The current profile out of the drive profile list -v struct CevapprofilE *current_profile # Wether the media is erasable (or overwriteable) -v int erasable # A description of the existing media content structure -r -m struct CevapdisC *disc # Start and end addresses out of ATIP. # From 43h READ TOC/PMA/ATIP , mmc5r03c.pdf , 6.26 -r -v int start_lba -r -v int end_lba # From 51h READ DISC INFORMATION Number of Sessions (-1) -v int complete_sessions # From 51h READ DISC INFORMATION Last Track Number in Last Session -v int last_track_no # >>> libburn.h:struct burn_multi_caps @ =end Class Class=EquipTocItem Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=14.8.2007 Boss= Cgen=\ -l cevaptocitem -r -v struct CevapdisC *boss -v int session -v int valid -v int control # obscure info from CD TOC : possibly length of track -v unsigned char point -v unsigned char min -v unsigned char sec -v unsigned char frame -v int pmin -v int psec -v int pframe -v int start_lba -v int track_blocks @ =end Class =end ClassDiagram=Equip_overview ClassDiagram=Job_overview Class=JobTodo Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=18.3.2007 Documentation=\ JobTodo records what is to be done during a job. This includes peripheral actions like tray load/eject and central actions like blank, format, burn. Subordinates=JobDisc,JobOptions Cgen=\ cevaptodo -v volatile int cancel # >>> @ =end Class Class=JobDisc Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=18.3.2007 Documentation=\ JobDisc models a disc structure. Either one which already exists or one which is to be created in a job run. Subordinates=JobSession*N Boss=JobTodo Cgen=\ cevapdisc -l struct CevapsessioN *session -p -v struct CevapsessioN *eol_session -l struct CevaptociteM *toc_entry -p -v struct CevaptociteM *eol_toc_entry # >>> take over services of struct burn_disc @ =end Class Class=JobSession Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=18.3.2007 Documentation=\ JobSession represents a recording session. A session usually bundles several tracks. Traditionally the last session of a disc is recognized by operating systems as the thing to be mounted. Subordinates=JobTrack*N,JobFifo Boss=JobDisc Cgen=\ -l cevapsession -r -v struct CevapdisC *boss # >>> -l struct CevaptracK *track -p -v struct CevaptracK *eol_track # >>> @ =end Class Class=JobTrack Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=18.3.2007 Documentation=\ JobTrack represents a track to be recorded. A track mainly is associated with a data source but in many cases it also becomes a recognizable entity on the target media. Subordinates=JobBlock*N,JobTrackFilter,JobSource Boss=JobSession Cgen=\ -l cevaptrack -r -v struct CevapsessioN *boss # >>> @ =end Class Class=JobBlock Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=18.3.2007 Documentation=\ JobBlock represents a single output data transaction unit. On CD this is the same as an addressable media block resp. sector. On DVD this might be an addressable block od 2k or a packet of e.g. 32k. Boss=JobTrack Cgen=\ cevapblock -v int alba -v int rlba # >>> @ =end Class Class=JobSource Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=8.4.2007 Documentation=\ JobSource represents a data source for a track. Typically this is a disk file or a stream file descriptor like stdin. Subordinates=JobSourceBlock*N Boss=JobTrack =end Class Class=JobSourceBlock Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=8.4.2007 Documentation=\ JobSourceBlock represents a single input data transaction unit. Boss=JobSource =end Class Class=JobFifo Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=8.4.2007 Documentation=\ JobFifo reads data via JobTrackFilter and buffers them until JobBlock can accept them. Boss=JobSession =end Class Class=JobTrackFilter Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=8.4.2007 Documentation=\ JobTrackFilter reads data from JobSourceBlock, processes them and presents them to JobFifo or JobBlock. This includes stripping of .wav headers. Boss=JobTrack =end Class Class=JobOptions Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=18.3.2007 Documentation=\ JobOptions bundles the adjustable parameters of a job. This includes dummy mode, speed, appendability, blank mode, format selection, write mode, underrun protection, random access addressing. Boss=JobTodo Cgen=\ cevapjobopts # >>> # Keeping an eye on the drive buffer -v int wait_for_buffer_free -v unsigned int wfb_min_usec -v unsigned int wfb_max_usec -v unsigned int wfb_timeout_sec -v unsigned int wfb_min_percent -v unsigned int wfb_max_percent # >>> -m struct params params (used by disabled read cd funtionality) @ =end Class Class=JobBuffer Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=13.8.2007 Documentation=\ JobBuffer is an intermediate storage for the content of several JobBlock or JobSourceBlock. Cgen=\ cevapbuffer -r -m unsigned char *data -v int sectors -v int bytes @ =end Class Class=JobProgress Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since=13.8.2007 Documentation=\ JobProgress reflects the state and parts of the history of a job Cgen=\ cevapprogress # Keeping an eye on the drive buffer -v int nominal_write_speed -v off_t pessimistic_buffer_free -v int pbf_altered -v unsigned int pessimistic_writes -v unsigned int waited_writes -v unsigned int waited_tries -v unsigned int waited_usec # >>> the info provided by struct burn_progress # >>> @ =end Class Class= Author=Thomas Schmitt <scdbackup@gmx.net> Version=1.0 Since= Documentation=\ =end Class =end ClassDiagram=Equip_overview ClassDiagram=Gestures_overview # >>> =end ClassDiagram=Gestures_overview =end Model=libdax ---------------------------------------------------------------------------- Notes: ---------------------------------------------------------------------------- Compile cgen: ( cd libcevap && cc -g -o cgen cgen.c ctyp.c smem.c ) Generate C stubs: ( cd libcevap && ./libcevap_gen.sh ) Option -lowercase would generate all lowercase struct and function names Compile test: ( cd libcevap && ( rm a.out ; cc -g main.c cevap*.c smem.c ) ) Option -DCevap_lowercasE would tell main.c that -lowercase was used above. ---------------------------------------------------------------------------- For a description of CgeN see libcevap/cgen.txt The generated code uses smem.[ch] out of one of my BSD licensed projects. For a description see end of libcevap/smem.h . ------------------------------------------------------------------------ GIF87a´ø÷�����­­®°°°±±±³³³¶¶¶···¿¿ÀÀÀÁÄÄÄÆÆÆÈÈÈÊÊÊÌÌÌÿÿÿ@��àÜ�>Ú�ÿ�¿�h�A������L����³l�Hë��@�`d�óB�2�@��À��Û��ÿ��¿���ˆ�0*����0ì9�Ú�ÿ�¿kà��Û��ÿ��¿È Ü��ÿ��¿��yÀ�æÛ�ÿ�@¿�0��9����Xè��%����0.¸9�àN‰ˆZßÀ2ÿ @¿c����À�� ��Q� –�p�@�c��`ÿ�ÿ8X�ÝC�ÿú�‰ˆì*Ûÿ@¿0�c0�9`Ào @HÝÜÜ¿¿¿—ˆü-îÛ ÿS0�¦9���¨��?`�o�@ˆX�ÝCøÿ�¿øˆ��*øÀ @��S��t��­p0 ò9ãÿ1¿@˜8ÝÜ�ÿÿ�¿¿�ˆœ�îÚÀÿ@0e9��d¨��?������$O�BÚ�ÿ�¿�¤L�áÚ�&ÿ�@¿0O9v�e�r��w��r��i�Xe�Ü �ÿe�¿xß±i²s t�@i¤Ønïg�2 �@fÿi�ÿl�ÿ èˆ'*/�h�@oèŽmëe�/�@t�h��o��m��a�hs�A/�p�o�Ls�t��/��ci�dŒ�r��s��k� iÜn�ÿ/�¿lHˆi�*b�d�@a´ˆx²Ü_¸ÿoF¿v�Ïe�÷r�vlàeÎ>wù.Eg�hi�Af�'�?lL�Î�ù��E���€�6Û���������������������������������������������������������������������������������������������������������������,����´ø�þ� h@° Aƒ*,˜p¡Â†Œ(‘bE‹!ZÔH‘cDA.ùp"F’Q^<ircËŽ/?Æ 9sdÍ’3ÞL¹s¥Ëœ T2ì9¨PF‰"Íy4¨R§I£2}Ú´*Õ«RYfý9u+L¯2ÁÒkSªU²8»ªÕº–+Û·nã~m;n]¹aéæµ»ïX½ùö[paÁ‡ §E¼X1O´ 7¾K¹oåÁ—gžl¹3fÏšŠMº´éÓ¨S«^ͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀƒ N¼¸ñãÈ“+_μ¹óçУKŸN½ºõëØ³kßν»÷ïàÃþ‹O¾¼ùóèÓ«_Ͼ½û÷ðãËŸO¿¾ýûøóëßÏ¿¿ÿÿ�(à€hà&¨à‚ 6èàƒF(á„Vhá…f¨á†vèᇠ†(âˆ$–hâ‰(¦¨âŠ,¶èâ‹0Æ(ãŒ4ÖãY’EÆ˜ŽŽÀ@)äDiä‘H&©ä’L6éä“PF)å”TViå•Xf©å–\véå—`†)æ˜J.ÀÀŽ>mÆã\8¢YTŽi‚¦�Ôiçxæ©çž|öé矀*è „j衈&ªè¢Œ6êè£F*餔Vj饘fê'� ,àæRŸB§~tjjꩨ¦ªêª¬¶êê«þ°Æ*무ê À Ø[©µöêë¯À+ì°Äk챋€@®ººÆ+²ÐF+í´ÔVkíµØVª,³Í²öl¶à†+î¸ä–knÛmkߢëî»ðúl´ï½øÞ«îº«Ù›ï¿�K'¯ûðÁO»/¿©œðà ;°GlñÅ/˰jcìñǨN\gÅ —lrÈoŒZÇ'·ìr¢ d½/×l3£ «\Ë7÷ì3ÅôÒLñÏDmkÊ:ïlôÒEÇ,ôÈLGísÎI‹Æ³ÔX?ü­ÈPgíµÉTWÓÕ_—möÙhW¶Ø‘öÛpÇ-w«ks×fo†z7þEnÏí÷߀ŽhÝq~fxhí-çš‹ŽøB} .ùä”NxÞxƒš¹¨F^ùç ‡ŽõålÏ)ú騧î5é¥ûHæë°Ç.ûì´×nûí¸ç®ûî¼÷n$®­å¹êÄo<À·r<ßÇ7ïüó'¿<FÃCoýõØËúòÕgïý÷௺}ð݇oþùè?:~ëå§ïþûðoŠôôµÿýø»¿~éöçïÿÿØÛÛúÀxÈÀR.U[ 'HÁ¸A:Š;g4È8ú¨‚ ¡êX—ÁÇm°0%<¡ ;hBšN„0ŒáäHˆ•QiNMû‘  wÈÓ]Pgþ:졇±ª,ˆDL¢õ5?ú½p‰PŒ¢›èD×ùîŠXÌ¢·ÈÅ.zñ‹` ã•€WEÈIñŒh¼˜ôÊøÄ4ºñøZ#?Ç:Úñ\FÜïÈÇ>ŠŠlÜ£IHKå‘a‚,¤"‰3@–1‘ŒŒ¤$uH~Ar’˜Ì$ž*¹®Kjò““ädrRèÂR:n…§<ˆ'AÉJEÒ2©Œå›H)Ë6s8,È*[ÉË>¾Ò†œÛ\Sî³Ë^Ž¢lV1ÉÌ3&SWËl¦4•øLEsšØìa5ktÍlz†Û¤Q7¿IN †sFbL§:×ÉÎvºóðŒ'™È8Ç‚, þ@€>÷ÉÏ~úóŸ� ¨@JЂ” Ê:¨BÊІ:ô¡¨D'JQ€&´¢ͨF7ÊÑŽz4 ý¨HGÐtªžaÀÀÒ–ºô¥0©LgJÓšÚô¦8½)�rÊÓžúô§@ ªP‡JÔ¢u¦;=ªR—ÊÔ¦:õ©P¥iR£JÕªÖÔL(u�²J­rU:^ýªX‡Ö±6§¬f]ZÓÊVܬµ­Æy+\‰#×¹Ú•]wEN]ó꛽òõ¯£ñ+`s#ØÁÞ¦°Y¥%.oiK.Ö±ma-ƒ™ËÈræ­Šµ¬)+Y΢ҳ›Õìd3KÙÎ`¶†zC­0U[ÙÒBÖµþ-hãtZXÎö¶²Í-l?«[ÒâÈ·¬eìnC;ÜÑ÷µÀSl‹‹[æöö¸ËM.0¥Ë—ÚNº¼u®v©»ZÛ>,ÖM­w·‹]âr·µÃ4lÛÔIJW6î}¯zã+_gÕ·7ô½/_ó«ß†õW7üý/\,`Ò¸ÀVC°~¬`ê5˜6 ~0J#,aÈU86¾p3¬a‚p¸Ãî0ùDŒW÷ËÄûE1ÇTì_Ï5Ćq…eìb¹—ƃ½qu™àOOÇ>òŽ…ìcöõxÈG®1‘‹œ4"9ØÅN^/”‡ôd»žW¸WFny;^„—ÇÙÍrt·Ü\1‡™Ì¶ü²þ‡ÇÜe4“·Íp¾nœ}5cÙÍf6/ž÷ü]9ûY¼în ×|Y6úÎs4 ;ÛYˉF/Ÿ éC;ÚÐŒ–t¤Méå¦XxE^2‹EÍä —Ô§®rªM½j¾µÚÕ¯®'Ž<k×:Ö6Ƶ…u­J^?Ò×t6¡…Í=b7àÖF¶±E¤ìÿ6»¿Ï^¶‡¢}_j××ÚÒV©Q¼mw;Û+ú¶ˆÅ â$ƒûEäÖpºclîs«(ʰ5•¥,o!©ÚÝ-Z÷ŒÛ­b}×3Ï\îô™7]éêÖÓŠN¸¦®g‚¼ÐgxÀ3ÍiмÑo¸ÄË<i‹#œMWxÅþE~ñ…â÷xÄUžr’Ü´Ÿ¸ËW>ó–›¼æ´9ÇNó›û|ä­E0¶ß;töß:ºa•žc¤ÑØLlÔΠ©ZØV§z‚²~W®ÛÕëZ/Ø õ°[²ìĻ٤v¶¶=­o_{â>Vº‹ÕîrÏÞ¹º÷¬ö=ïõù»¬ÑxqØ‚/||ÈÃ+FŒÿuÚó ðoÜÃò̼扄նmþóŸï|D@Ozwš©ò}f êß|ùc’Sžj(a/{IÂ~õ«'æëé9úÙóÞŒ¶ÿ=ß_ïH:j’tã4à9­I|åÕoöÅ÷p(£ÈæSúÎoã"—þÏM­^óšÐ¾¯F껾eóJò‘…|Þ)fNû÷ÅéýMvíi¥Ê¿øËŸ}íƒÌi�è2íW2\€ë§=æ·aõ·ã'~úwÃ:X,Ø56Ô7ÁR€ø4ò—€N¤µ23Xh~"(1˜?ò2øø·+ø+ó‡N Ø‚÷w‚$ˆ1(€:Ø€è—Ø;8Eý7a9X‚G(„ÿ§‚@È„NˆI¨K4H…p·„N€è}Pˆ~F8…H„2hXƒ½rƒ2¢†±ò}=~^ø„Uh…b4ï÷‚h†\è‚&X†uX}5£‡?óƒ`ã†þÔƒŒÄ†1‚ˆ÷cˆÇLJ‹‚Ù{!×ZŽ?˜IЏ}Ëb‰qŠ­×™?›ˆIèJŸx‰@'\º‰x~°X{‘Ĉg}´È‰’艱¸a¥÷‹í$|sŒÄ¨NÂhEŘŒYtŒ²¶{±XŠ¦ÈŒÒ|½‚Îx}×È<Ô¸…³˜‹¨¸‹ªXôú†ÝØ{¶G‰?†‹ÚxŽÏ—Žâ¸ŽîøŽóè…d‹èÆŽèXª4}ñÈ=úHºˆä>øè"‰>§èÞŽÜ8ØÈÆ7‰ÿH> |i‡µ¨Ž×!Š,7Y y> y«Ès6—‰sÍ’æ3’Iþþ¸’?÷.iæ‘íH‘ùψ}%IH™oi1”ƒ4”6V”ýȔɓf¥ŒRÉEÒˆySy•WT•X¹•¸S•”;‰ŠZJ^©€Çç•7ic¹Qé”Oé“HéGJnn9i—PYwu)‹:¹yyw{™–à“Š&Y‘ì˜@–Ù–w‰—p©˜…Ù“ —‹é—¼x4ÙŠ¯%˜Þ“‰‘™™•&š/·™ˆY—œ =ÛBš= ©@9ù˜–Y™€Ù˜|)›y™ŒÙ—³I’)”i‘¶ùš°)“¾©—Ù˜½ œ†É?§Ù˜ÄÙ<sùn\Y·ƒ–Ö™´þ£•ÚÙaR–NtOùDRäIž!UžèùQ&{!žéùžužð9Ÿµžážô™Ÿ%ŸúÙŸeŸf¥RV5 ª�SU U¢š ÊTú zT º :¡JTš¡JSúx‡åxX¢è&¢ˆG¢i¢¾y(j,‚Ùö¢-:2úc1:W¬™’%§™¥Ù£('s5é£:*¤@Ê£­ùhDjy©¤,™zMº[e•£Eº£Tš¤K!¥L¤­Y¥[ªsNª¥CÚ¥b¦dZ¤H:¦Sj¥¬·¤_j¤.¥¬È¥eš¥nš¦ì6yÒV£3 z:byº§ï¦¢¼Ö§þ€Ê„Ê?7Z¨'r¨”¨ŠZ"Œª@Žú¨#©4©”"–Úd˜š©²©O÷§žÊl‚ªk :ª}Åoܦª£Æª¨Š þö`±ºo¨öªZåªã†«Þ¦«¶êšóoýö«£jÂZ?8§jŠ¥m*§9÷£tʬϚ¬ÈЦtziÔº¬ÒŠ’v:§Øzl§§Ú­×º¦Oú[ÓÊ­ä ¦Ûº®‹e­èª¬éZ§âú®1á®ìÚ­ô®øz¯ñ f–®£x®ü ¯éÕu¼z§csj³Ú«UWª¸vª «Wk±q5±¯V±KWÛj»±Âñ±Ý"²g²B²ÐÔ©&›t»þj(»²»ñ²Ü¤²0k“K« [j [³ò±³Év³ê´<K>+`E+tB;´ïoÃ:eöFoJV¬»¦´®™´¶fµ²ŠµËC°;¯ËŠöêµb»¯cÛ¯Ùji^j¶ãʵúª¶a °Së¶_Û¶l«u˦r[¶W¶dÛ·yë·w+r|û·„¸êª·Ø·Ê…¸tÛµ€ë¸W‹§Ë&³T;”‹N4[¹°Ú²©v¹šûžÛˆ™û¹bǹ Kº º7º¨+ ª[¢¢Úº[gº:+»ÚF»Löº¶»3¸j»»¹’Û»¿;º›o¬;¼9$¼H†¼R¼¹»le¸òj•ÞY½Öþ{½Ø›½ÚË$p&x+]Ò ­ŽYNäëJÀž‡û¸bW¾ì»}à)œí¿¾œ $¿ö{GÓi"Ñy¿ü+8ù ©ýë.$ˆ…/($M‡ô‡,0ôA l.[ó~y~h8ƒøÀäò¿$²¿<ÁÌ€A(„ñÇÁ¼'\©%.íÒƒLƒ,‡œÂØrÂÌ&ÃÙ2‡ HÁãGˆ_HÂ6\/ Üd?|-8ìÂFÌ5_˜ÄC-4¬©Kl-'h+9l„(ÃOÌ~A DW¬6z˜À<¼5„€C²Å‰˜ÅGDÆhŒ?M ">œÆn1kü᫦müÆv|0��½X·s<®u|Çþ€Gz°mûŠ|ȪiÆz„ÈŒì<q<mÛÉ’<É”\ɤ÷¾‡ÙÈšŒ@˜ìœ›üɧ#G~ʤ :Ü!\ʪŒÅÍ)Ä«üÊsÊ’ʰ\Ë6¨Èˆd˺œ6²¼!´¼ËÀü*½¬!¿ÌƬ*Ü!Å|Ì̬)É|²ÍÍ…ˆËËÑÇèºÌҜ͑’Ç{ ¹æÚ¸à<6Ú<Îè7Èß ¹†LÎêìƒÔÜIëüÎüW›ð<ÏÑÓÎ#KÏø0Ï|!–ÜÏþüÏ�Ð×ÙÊZœÏ­/<@½Ðï"ÊJÈÐ].ûl!ØÑù<ÑRÑMÏM!½ÑðÜÑòÑ ½Î"t%þÒeLÐg¬Ò.ÍÊäaÍüJÒ/-ÍÜLÈ~&ÓeKÓ5ÝÌ7}Î…l<ÝÓÇ|Ò2ÔDÌF !HÔ»¼ÔÒÔNmËP­US}Õ²RÕ©‹Õ\í*Z]uÖb=Öd]ÖH’ÐbƒŸþ¹ÖlÝÖnýÖpÍŸp=×t]×v}×  \u¡Ú×~ý׀غ¡‚]؆}؈Ø7õ¡Ìë²ýØTë¼=ÙdGÙ–½±’}Ùšíw›m!:­¾8 Ô¡]®Þ\ÚáLqg𝣾…kÚ«=½­}Ú¤ Ú¢]Û³ýÚâÛ¸}¶¶ýs©=·»½¶®ÝÛ°ýÙºMܢɽܚÙÌý܈ ÝÒþM¢Î=ÝÖ]²×ÝfWÝÚÝÝmèÝàoÜÞä"ã]Þè½Áé½Þ­Z«ìýÞAëÞð=ß´¦µô}ßmÅ´ÆŠßùaÜþ=ܷܼà^ƒûß²ÍÚܪ-àÂMÛ^Ü�®à.á ná¯}àžÛ îà á¾ájÊߤbß$~âK‡â*ް+Þâ?ëâ0îl1>ãÕFã6nt7žãR§ã<n°=þã6*ß@>äœ*äD~äØ}oH¾äÜ$µ‹ËäPž²Få)âÎàÀíáYâNᛦáâaÞå®åŒ‹åg®zVþá^ŽæÞæfþæe.¸iÛá\~åq~ÜwÎæþsÎÚTîçýç‚>>è†î…~芞‰¾èŽÎ±é«+é”þ¼•~鋊éš©›Þéšêé ŽÊ¡>êÊLꦞѧžêG­êÂaç˜hÖ°ë²>ëØK�0æçêÂ%Õ]MÊ� �€ëèšÎ½^ì˜âÐmìÊnHö|ÏËþì“òÕUíÔ)Ò¾ ¼žMÜÅaÌ'3Ó |Õ×®mâþÁ1ì/采d8Õã¾uå.Áðïò~ê^ÅNÝî°ŠÕ: ÂöÁó‡\ï’íÚÂ!<ïÿÞ…]-ðBð¯Älœð/îÍ®L oð/ïQ¼î÷~ñÐ$î< Ãìþí,ñáÎî Ï|ÕnÂä×òGÃÒ- ó4Ÿ.+_ºnš5¿óò� ì—óËåð<ÅyüónN¸Ä^ôL?Á7OMõN/ózDëVõXŸõŸ‡Öõ+õ^?2\ïÀ_ïõÈÞŒc/õ ¿¾gõiß¼kÏöOƒoÏômß9sO÷q߆w_ôuïº{Ïó}Ïv¿ó_óm/ôÙEô‡/¿Gô–§øÄÅøß¾Ÿô¤µô•¯ì…o“›_íï”ÿùåúsGúÐnúü¡õ¬ßú®ÿúXö®Œúœ/ûMûÆ^ös4ú¸MªŸC½Ÿûy߈Á_ì¿O*ÅßëǯwÉ¿ðÃþ‹Í¯ïÏŸÑoñTÏ’’Õ/î>ù}–ý-¹ýìÞý˜ïfš/þ*½üøÁû诜Ã×þ=­þçÿ-ÿBMÿ5mÿ‡ÿ/­ÿD û�@à@‚ D˜PáB† >„QâDŠ-^ĘQãFŽ=~R¤Ã œD™RåJ–-]¾„SæLš5mÞÄ™rA‚|þTèP¢EEšTéR¦M>…UêTªU­JpUëV®]½~VìX©,È™VíZ¶mݾ]É`ºuíÞÅ›Wï^¾}ýþXð`Â… FœXñbƉ4†YòdÊ•-_Æœ9ñp=þZôhÒ¥MŸFZõjÖ'´†[ölÚµm߯[÷nÞ½}ÿî:øpâÅGž\ùræÍý\útêÕ­_Çž]ûvãѹ^üxòåÍŸOíýzöí00¾üø2çÛ¯ó¾þü0÷ûïï¥ÿ Ð¥ ,°¥L¥lp¥%ŒP¥ -¬0¥ 5Ì¥ =ìð¤E ±M,ñÄÕ‘>ñ{‘¿œ‘À¼‘Á!Ü‘Â1ü‘à [„±H¤Å!I\RI$m|Ç(uœ’Ç*}¼È,…Ü’È#¿LL(Å”’L*Éd‘É.Õ4ÓÊ6±|SË8¹œþÓË0ïÏ2õ<“O7ý„P9¥“P÷E4ÑæÒT´QG…4RI'½ŽQJ/Å4SM7å´Óš,õ4TQG%µTS©õTUWeµUW_…+UXg¥µV[oåTV\wåµW_­Xa‡%¶XcwÓõXe—e¶Yg[JöYi§¥¶ZX£µ6[m·å–<l±í6\qÇ%·;šÀ-7]u×e7½sÛ…7^yçÕ ˜Ð¥7ßçTL±ß&ùý×ß5¬“MC Î3á=î“á?âA%.TaFW|xaÞ8cŽ?ö8äˆ;ä’Ež˜ä”M^åŠU~™å˜]&àm.˜àƒu¦ØÎžþ†Ùç –Y袉¦é“—n™é™ú⥸ê›Îçµþ™ç¡½>ì¤Å†Zé¦Ï~m£õe½oÛ†;n¹çŽn»ïÆ›\|óæ»o¿oÝûoÁ'\ÔÀ G<qÅ=|qLJ¼Æ#§¼rË—›ürÍ7ç<·Ì;=tÑÓz{tÓOG=ÖwSg½u×ï]ýuÙgGý ØiÇ=÷ÐK×s®¿þ=ìàǾl²Õ6;må×6¾xä_>yæŸwþeªUº>k¬·Þ¾ëê§zðÇ—¾|ñÍÿýî__øúÒ??~øçoŸøú›¿Ÿúüçßjíý×SöP"@ïí|ò3 úþ�XÀ²¯î{ ý"h¼ÞåîsÄ`ývA vЃqãàE8Bx…„'Da¸L˜B¶PZ+ta eH,ÎІ7u¸CjÕ‡?¢§|D"QRC4b•è$.щOO¡8E*ZGŠUìUÿ®6AìÄ‹_cÅ8F2–ÑŒgDcÕ¸F6¶‘"(�ù'0-þo‹2€ô¸G>öÑd 9HBÒ‡Dd"¹HF6Ò‘„d$%9IJVÒ’—Äd&5¹INvÒ“€� ljŸDe*U¹JV¶Ò•¯„e,e9KZÖÒ–~@Iî–Ç[öÒ—¿f0…þ9LbÓ˜Ç|dVLb7^"әτf4¥9MjVÓš™Tæ.¯¹MnvÓ›ßg8ÅÙÈl2sœçDg:ûØÌE Dï„g<ùXNº±Sž÷Äç1í¹Ç}â2Ÿÿè6é9·~Ô m%/ó¨Pw걡 ˆC*Q~¡Åè&*7‹fÔ£5d?JÑ‘:Ô¤õ§Ø9R‘‚Ô¥/ )–YO˜ÖÔ¦µgDMªÒ6“ <Ý)NƒªRê´¢½iRºQ*Õ©hESZRŸÎ“ŸWêJUŠ>Õ«�e*ÜúU²žÓ¨BíiZ‡ZUµjuž\=kYåªÎ°¶m¬sÅ«7[šþÕ úÔlÅ)C ÔuæÕ°é¬+ÛîzXÆ6Ö±ýfbõµXÈVÖ²—Ål/%Ëœ:r”®ÉlhE;ZÒ&4�ø,_ÔYÞñ^¥…mle;[Fà´©em¤(K[ÞöÖ·Ý,½vû[â׸I {\æ6×¹`•©6Ÿ;]êVº3%¨uµ»]î†3¹òZnwÅ;^òÊò»ñ oy›{Ö¢NT¤?í){¹ª^òž·„ô¥ï>ÝjÕ”ž«üÅ/~íÛ®ôø·9-l‚ÌSÀÕÀå0» üàÞî÷¿F+a‰Ú` 7Âëšp‡iËáµþÁÖ°ˆ»ûau…XŲ-i÷ê_þ»øÅ0Ž®9o¼]vÆ FqŠwL]§ËÆCÆl\#Ê^Ã7«ò=2’/[dàä‚®…I”¥\Ü‚lùŶE-–[{$+KPÌ.Ѳ—Õ¼æØ‚· Œß)Ù<g:W—Êz«sžõlÜ;+Í{t ÚgqýYЇFôG ­B76Úцt¤%=iJWÚÒ—®´.uœhNw:¯¹Ä.G==jR{ÔÒ-uªU ÒEwËЫ†u¬£Ùjn½ZַƵ0i½-[çÚ׿žå®µÕk`ÛØ©v¶ˆ}lf7û’ɶֲ=mj“3ÇÖ)3þR+íjwÛÛƒtó™¯Lf8g[3áö·Õ½n•þÞVÜfΙœÙ=ozSÚÕJw½õmì{÷pßÿø!û=­|Üà«ø ¾p†'üYgxÄípgaÚâÇxÆ5¾qŽwÜã×4ÝvÒ²”Üä'çJVP¾r–·Üå/‡yÌ™¢r™×ÜæQ1 Zì&ÍôÜç?§Ìc€>t¢ÝèGGzÒ#t¥7Ýéˆá uE©‡êUïàÕ±Þ­o½‚]÷ºçÂ^E°Ý6e7»ìОvÙ¬Ýræžã›åþnmÓýÜx,wÞçnG¾{Öîqÿ{)õžeÕ^†/|ßÇìwrïñ_|äˆx¸pðŠŸ<æÇ ùÌs~ó’ÿ|Ð^SùÃkþÞž?=èSÏv³><nwýé`{wÑÞˆ³·½ipŸ{Îíž÷¢ñýï-|á{†øÅ‡Üñ‘ßå/_qÍw>é¢CèOÿ&Õ·þß°Ÿ}ts_†Û÷~ëÃoÁØÿ8à7‹ËŸ~â Ÿýz[ÿûã~ùóÚ^â¯ÿüqHúÄ£¾î¦ÿ?ÿ»»œ€)@ÇS½�LÀ@À@\@D³þ‹@ÁCÀΫÀÒó;þãÀËk@ ¤<|‰Ä» ½\=T@ dA„ÀÄ „ÁtA Ì@¬Á‚"Þɿߠ¿/ Ô?!Ô |?#$BKB®[B BÂô{Â&ô3)Ô(¤þBW»BܰÂ,6.<;/œ-ô>1C_áÁ2TîCÃÊ9Ã5d¾øsÃçƒÃ8”>5¤Ãıü»CœÈÃÜCÇiÃ?¼¾9d¡4ÁDDLÄ ¼œAF\DŒDJÌA ¼Áï½ÙÄCTÄO”DP´ÄI¬H¼D$EL¬D|ÀðqÄ,3ÅTDEQdÅTÅS4ÄÀëDÂËÅV¬ÅLŒEZ¼Eá#Ãì+FA–cœ>eDÆ_aFç{Æfä•hD>j”ƺÆÑ°Æl¤•mü=oäÆWÇÜÇpd•r¤=t4ÇSQG×kÇu$•wd;y„ÇP¡G³»ÇzÌ}|‹|äÇLþñG¯ Ȥ”Ä:ƒ¬–^´Åøù¸†tȇ̸¨[H…FO Y ÅŒEaÄÈÔÈR´HtE‰»)�8 _FޤÈUÜE”ìÈ_\Å”üH™ÉD8’”¦S„\¢›ÄIh¢8e#ÈOùI˜ ÊhÊs)Ê—:J|KJt«°¨j¯“¯·â°ýê² »¶ zJ<â-ý° K±£*¿º* ûÊ­ìžT"ŸÌ¨ ²s«²”*ðÙjÊêÊ×úʰ¬1 Kªò¯…:K¿1µä¶L"·ô(ª4Ì +1ÈL¾ê+âÊK‚ÛË,;°»ô1À¼0–*LÂÜ0÷²LÄÌż½þͼKÇä/¶LÊìÌ3M¼AÍ"bL¨‚«¿’JÏ|+�ÍÂlÍÒ µÄÌÌô0'“²Ë|¡âD³¥t)å|¸ý Æ™|IÐrNE›Íš¤I˜ÄÅé\ž–œHïäNY\ÉîÜÎó¬N†¼NìN•Oô,OšÏŠ ÁO÷L-y[O¨ÊNfbNhÙÏ·ìÏzúO–€ÈEÐu£ãÊÅž�Å(Ô>}PE( mP µN 5(è¬8  =(m–Ú$¢ÛQ["Qf1Ñ BÑ¥¥]–¢…QóP‚Ñ ½Ñ{’Qe¡Ñ²Ñ}¥=– å¡!%Ò„ÊQŽÚÑPÒ%]þ%#5$5–øÄO¯”Òx .,¥N/­ÉùŒÉôÓûüÒ÷t’2…OÜR.ÍQ5%S4­<15Ïú´S—„ÓÑÏ6E'*-+Õ¡(åSOòSzRATN*Ôd<ÔDE¬&¡CUÐI¥ÔJ ]ËFuT³ÂTâÜQDÝTLÂÐLýÔP§E@¥>S'TU½!PeU{ƒT±ÒÔYÕ«Zµ«[ÅUnrUgäÕ^½¦_5Ã`Öj"Ö^UaÉS4•Õcu¤.•ÓiµÏí¤ÓÕ³ÔlÝ›iÖmƒVzSj½Óð$¥k]Ág ­„³=ýÖœÔUÅR tͬeµ ymWDJÖiŒ×<þ£W¢¼WjÊ×]Ñ:{2W!Ø%¤€Å•åWƒEØiRXlÔ=„X°uŠ1QÝ­ùj2Î|L¢z&ýr؇µw,Ôx ”ÊØOº«ãM믖Õ5ƒÕÖ™¥Ùš]ˆN=Íxµ0=Kˆ*$žµØž%”úYåYÝ„K\ÒÍÁÚv²(÷ÙV¹›£ÚªÕ š³Ú¬ÕZȹÁX­"Z%K«Ý,¬¥õØÐô1¸Ú¸[ŸU+«ôÍøŠÍÊÔXøª*Ò¤1³ìWšà¹§óÛ¿… ¦ÜÁ%\ºÈ M•ÍßL0ÁºÛ ý+ß,ÎÔ[àÄ*ÇýÍ‹m+Ø$ÌÇ2£ª¼]Ü'»\þk½ T Î';±×”ÌÒMÛÿÊ\µ}[±¥L²Ä°²¼X(C+ͽݰíÝÕU©Ó}RU]ØEË×dMÁ¬ÝU0Ü ÝÛ…ÝÕÝY²Ì÷úÌ·Õ0²$ÞâÁÄe(ˆ2‹=ˆ³5_È4ÛÎEÚÀÌ%3Ë£ߥ-*òíÍȬ_£5[¸%©ôßÞ]¨îõÞ‹L;Ëhà�†Ò}µ3 %§œèÖq¥O<W<––àjS ¶`C1×<ØÊR×Õš`r…à1ÝàL¬àŽ`¾à®Ó®“¦Ä†¬˜ØC]êS`:ËaÄéabØŸ½ÉÊ ©|c`—UÔ­:à�â¢%þ_¿ú³ô"¶¤u¦&+þ¾>ߊ}¨ÑÝ*ÊZß#®ØJbÜŽÊ_¥­-3ž\\ZbïýÚ»Uܤu[ú}ݵrÝ÷ ZÒõß‹+Ë­c¢…ÍÖœ/»]Ûíý\óÅâD`6Y¹uÍ[ã¿ìLÐä\»N°õK/%C–ÞÏe\K¦^Ò¥ä³åDFÜEfXàÕ^NÞâùM[çÍ‚ÂÊðÝc¦}^äÛ »\Ú¥åßmÞkãâýÚNäÍ2bÞÙÊ<fN.cá}̬ìä_nYýâãU~äy æCÝWñcäM]~]%ÓÛ.k¯_»•Ê^~eɬÊÏÌ)hÖ_üuÛ.î_R6å þEà&VÙ§bÙh½gå"D~ DmD61nLƒN¤ÿå¾þG† ®1 h}thЀhÇúg»??|Ê>œÀ®³À9aVa’Öà©à>é•vaò,éæ`ñ‘a˜|Q Eè—e%"ö%n¼–ÎRfá FaùPiŸ>S–êVj–”é ®PX2cŒ¥çg#&žÞ™)ªèzÌjã3/A’e(®Ë/Þ¦-bôE׌ÎÙEN`†jëU\Gn\Úg¦e²ú…Þ-Ž%´¦Mµ^ëFÎë\ŽåÉ,Û­²ªAn\»ôâGT½Þ%¾ÖgWÒ]eë©’k=ÖßȾÞé­%ÆöOµ~ì„ÒÞþk®ãì=lß5f9–lÍÊæOÕbÈNçp¦âÀª[÷¥æÉîªýý©r^æ)e"›õíßîà‹Æ×ÕÑÏ>hã)äÖPåö2ÎVB;Ìgîë–nõ£n&æëB›h Íîî¶èþhTo,Ün7–N¤j“6jí$Ó”f牢iúvéùvo?éúÎïö®Óý¾ï÷6Søæ@ùð5ÅoþVð�OjÑsê_ðŸöoYÄaÇ>oá²ð BÏÖð 7ï/¡ q ñ×î7ñ¯îWqîfq#+ñ÷n—qø£ñŸqÇqôÖqpŽÄî rŒ;\4ò#GòH+þò¿»§NæY‡:IÍŒòrûæHv­òªÆÙ§ÞrcU»úrd .(òˆU3Ù2s}"Y#ks0ÏQ5X4gGÒâÝpVÚ©„dÙ¥g<†í>ž²9?°tVç÷ ßzË8f_­lÏÉ-°dÛi¦ÛºÔ\Ïíݶ,6¯°—•ôL^ÏK.{ór¡óåbœ&mQWe?åôGO‰S¨TcÔž[M†õ6+u<‹tÃŒíÌFìCžåWçØM'tßBËQ÷ôVÇô=—MY®ÒbMÊgÅfåÜä`O²dWv±\veçÄVõÔvå‚-ÓžÛ>‡â°mÝÀeZJoþÿöeîgçE0×Ýõtïu?Kwp/à¹LíV–äL—÷¥÷N_MX‡eL^wMç÷s¯HÏME·wÉìø"1·Þ÷Nx«ø?†vö­f;Nteï÷B#2ož3N?NAW1;7•ƒÿV—s]KyÞprø¦yhµù›&ieéðzœ—øž?V¡?ð8k¥ÖŸ?zÍÊyW›ú`’ú«ß쪯µ$÷ú¯W£.Ÿu°'û²#±Ÿv­ßi´çQµßl¶Ÿx·¿¥0_ ¨ïUº‡t¹WÑ×{¨æz^ëû­OúÀÑ¿6Â6¾Gü)5|e[ü¼VüÇG%™/»ÇÕ¬—üÉoühË|þÈŽüÎWÔÍÇ7ÐÏéÏ'}l};}d3ýÕ·$ÊG××üÁ—}BMýÙ zô´üYÅüÚýpú—fp*÷}jýâ‡$¦‡ð±Ñrä§UÚw~Q½}…‹~é‡þê'ãé8ì¶ãçþ„ÖþŠûþì¿þñOþð/Q³Wÿõ÷¸gÿ÷‡‹€{ô2ÿIÂûY¯Iºÿ´Ïÿdrÿþ,hp € 0lèð!Ĉ'R¬hñ"ÆŒ7rìÈÀÁ"G’,iò$Ê”*W²léò%̘2gÒ$ �ÁŠ kòìéó'РB‡-JðfNJ—2mêô)ÔF§R­jõ*Ö¬5‘ZÜþ©õ+ذbÇ’MÉ5*Ú´jײ•èµ,ܸrçÒy–âÛºz÷òí[önÛÀ‚ž˜×/âÄŠoÅÙ•1äÈ’'‡\ø2æÌOSîìùs\Ë9ƒ.mú´QÑšW³ní5ìØ²{ª~Hz6îܺ Övíû÷åÛ»‡GÝ»¡ðâÊ—K>Ü££O—~‘úuë±o×®“9øð“7H.þ<ú¿Ž+rgïb{øï'bO¾DûùñGÔߟ?DþAd^zxy¸ ƒA‘'àC:$aC2dásÀ)Ø ‡Æ” ‡!ŠÈyšx¢Rޏ"‹Ø"Œ1ŽT"Š5ÚøŒþ9êèÀ‹;úÈ"7 9dy?9bG*É`D:y¢ŠKJ¹[’SZ^“Ojù[”WzyZ•_Š©[–[š¹�iª¹&›mºù&œqÊ9'uÚy'žyê¹'Ÿ}úù' ê©PW‚z(¢‰*º(£:ú(ž„ž9i $p�™jº)§zú)¨¡Š:*©¥šz*ª©ªº*«­ºú*¬±šz@ Xd)¦²êº+¯½úú+°Á ;,©´ÚJ)²2°€Í:û,´ÑJ;-µÕZ{-¶Ùj»-·Ýzû-¸áŠ;.¹år»�-k.»íºû.¼ñÊ;/½õn‹n²ùê»/¿ýúû/À <0ûÁ|0 +¼0à ;ü0ÄK<1Å[|1Æk¼1Ç{ü1È!‹<2É%›|2Ê)³†!Ë�Fèò„0W(ó…47Ðru9g§sw<»ç³|@ß'ô~Dÿgt€6ã¼3Ó=7ýóÓAG=ôÔEW}ôÕI#ýòÖ1w=ó×5‡}³Òe½´ÓN£ uÚl»-uÛp¿MuÜtÏmuÝxßuÞ|ï­uÖ\îõà`.öádŸmvâkËý¸Ýë-¹ß”Þ÷å ŽùæšÎùçž®2饛~:ê©«¾:ë­»þ:ì±Ë>;íµÛ~;î¹ë¾;ï½ûþ;ðÁ ?<ñÅ<òÉ+?i@�; /* cc -g -DCevap_lowercasE -c main.c */ #include <stdio.h> #include <stdlib.h> #include "cevapi.h" #include "smem.h" int main(int argc, char **argv) { #ifdef Cevap_lowercasE struct cevapi *cevap= NULL; #else struct CevapI *cevap= NULL; #endif int ret; /* full memory supervision */ Smem_set_record_items(1); /* one short trip for testing */ #ifdef Cevap_lowercasE ret= cevapi_new(&cevap,0); if(ret>0) cevapi_destroy(&cevap,0); #else /* Cevap_lowercasE */ ret= Cevapi_new(&cevap,0); if(ret>0) Cevapi_destroy(&cevap,0); #endif /* ! Cevap_lowercasE */ /* report any leaked memory */ Smem_stderr(1|2); exit(ret<=0); } #include <sys/types.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #define Smem_included_by_smem_C #include "smem.h" /* ------------------------------ SmemiteM ----------------------------- */ int Smemitem_new(item,data,size,next,hash_start,flag) struct SmemiteM **item; char *data; size_t size; struct SmemiteM *next; struct SmemiteM **hash_start; int flag; { struct SmemiteM *t; *item= t= (struct SmemiteM *) malloc(sizeof(struct SmemiteM)); if(t==NULL) return(-1); t->data= data; t->size= size; t->prev= NULL; t->next= next; #ifdef Smem_with_hasH t->hash_next= NULL; t->hash_prev= NULL; #endif /* Smem_with_hasH */ if(next!=NULL) { if(next->prev!=NULL) { t->prev= next->prev; next->prev->next= t; } next->prev= t; } #ifdef Smem_with_hasH if(hash_start!=NULL) { t->hash_next= *hash_start; if(t->hash_next!=NULL) { t->hash_next->hash_prev= t; } *hash_start= t; } #endif /* Smem_with_hasH */ return(1); } int Smemitem_destroy(in_item,hash_start,flag) struct SmemiteM **in_item; struct SmemiteM **hash_start; int flag; { struct SmemiteM *item; item= *in_item; if(item==NULL) return(0); if(item==Smem_start_iteM) Smem_start_iteM= item->next; if(item->prev!=NULL) item->prev->next= item->next; if(item->next!=NULL) item->next->prev= item->prev; #ifdef Smem_with_hasH if(hash_start!=NULL) { if(item==*hash_start) *hash_start= item->hash_next; if(item->hash_prev!=NULL) item->hash_prev->hash_next= item->hash_next; if(item->hash_next!=NULL) item->hash_next->hash_prev= item->hash_prev; } #endif /* Smem_with_hasH */ free((char *) item); *in_item= NULL; return(1); } int Smemitem_report(item,line,flag) struct SmemiteM *item; char line[1024]; int flag; { char *cpt; int i,upto; sprintf(line,"%4lu bytes at %8.8lx ",(unsigned long) item->size, (unsigned long) item->data); cpt= line+strlen(line); if(item->size<=256) upto= item->size; else upto= 256; if(item->data!=NULL) { strcpy(cpt,"= \""); cpt+= 3; for(i=0;i<upto;i++){ if(item->data[i]<32 || item->data[i]>=127 || item->data[i]=='\\') { sprintf(cpt,"\\%2.2X",(unsigned char) item->data[i]); cpt+= 3; } else { *(cpt++)= item->data[i]; } } if(i<item->size) { sprintf(cpt,"\" [truncated]"); } else { *(cpt++)= '"'; *cpt= 0; } } return(1); } int Smemitem_stderr(item,flag) struct SmemiteM *item; int flag; { char line[1024]; Smemitem_report(item,line,0); fprintf(stderr,"%s\n",line); return(1); } /* -------------------------------- Smem ------------------------------ */ int Smem_protest(line,flag) char *line; int flag; { fprintf(stderr,"%s\n",line); return(1); } int Smem_hashindex(ptr,flag) char *ptr; int flag; { unsigned long idx; idx= (unsigned long) ptr; return((idx>>Smem_hashshifT)%(Smem_hashsizE)); } /* find a certain memory item */ struct SmemiteM *Smem_find_item(ptr,flag) char *ptr; int flag; { int misscount= 0,idx; struct SmemiteM *current; #ifdef Smem_with_hasH idx= Smem_hashindex(ptr,0); for(current= Smem_hasH[idx];current!=NULL;current= current->hash_next) { if(current->data==ptr) return(current); misscount++; } #else /* Smem_with_hasH */ for(current= Smem_start_iteM;current!=NULL;current= current->next) { if(current->data==ptr) return(current); misscount++; } #endif /* ! Smem_with_hasH */ return(NULL); } int Smem_search_and_delete(ptr,flag) char *ptr; int flag; /* bit0= revoke registration : decrement counters */ { int idx; struct SmemiteM *current; current= Smem_find_item(ptr,0); if(current==NULL) return(0); Smem_record_counT--; Smem_record_byteS-= current->size; idx= Smem_hashindex(ptr,0); Smemitem_destroy(¤t,&(Smem_hasH[idx]),0); Smem_hash_counteR[idx]-= 1.0; if(flag&1) { Smem_malloc_counT--; Smem_pending_counT--; } return(1); } char *Smem_malloc(size) size_t size; { int idx; char *cpt; if(size==0) { Smem_protest("########### smem.c : malloc(0) caught",0); return(NULL); } /* if(size==1032) cpt= NULL; / * set breakpoint here to find requests of certain size */ cpt= (char *) malloc(size); if(cpt==NULL) { char text[161]; sprintf(text,"########### smem.c : malloc( %lu ) returned NULL", (unsigned long) size); Smem_protest(text,0); return(NULL); } /* if(cpt==0x080a1e20) cpt= NULL; / * set breakpoint here to find origin of certain address */ Smem_malloc_counT++; Smem_pending_counT++; if(Smem_record_itemS) { idx= Smem_hashindex(cpt,0); Smem_hash_counteR[idx]+= 1.0; if(Smemitem_new(&Smem_start_iteM,cpt,size,Smem_start_iteM, &(Smem_hasH[idx]),0)<=0) { Smem_protest( "########### smem.c : malloc( sizeof(SmemiteM) ) returned NULL",0); return(NULL); } Smem_record_counT++; Smem_record_byteS+= size; } return(cpt); } int Smem_free(ptr) char *ptr; { if(ptr==NULL) { Smem_protest("########### smem.c : free() of NULL pointer caught",0); return(0); } if(Smem_record_itemS) { if(Smem_search_and_delete(ptr,0)<=0) { Smem_protest("########### smem.c : free() of unrecorded pointer caught",0); return(0); } } Smem_free_counT++; Smem_pending_counT--; free(ptr); return(1); } int Smem_report(line,flag) char line[1024]; int flag; { sprintf(line,"malloc= %.f , free= %.f , pending= %.f", Smem_malloc_counT,Smem_free_counT,Smem_pending_counT); if(Smem_record_itemS) { sprintf(line+strlen(line)," , bytes=%.f , records= %.f", Smem_record_byteS,Smem_record_counT); } return(1); } int Smem_stderr(flag) int flag; /* bit0= report 50 youngest pending items too bit1= do not report if nothing is pending */ { struct SmemiteM *current; char line[1024]; int i= 0; if(flag&2) if(Smem_pending_counT==0.0 && Smem_record_counT==0.0 && Smem_record_byteS==0.0) return(2); Smem_report(line,0); fprintf(stderr,"%s\n",line); if(flag&1) { for(current= Smem_start_iteM;current!=NULL;current= current->next) { Smemitem_stderr(current,0); if(++i>=50) break; } if(current!=NULL) if(current->next!=NULL) fprintf(stderr,"[list truncated]\n"); } return(1); } int Smem_set_record_items(value) int value; { int i; if(!Smem_hash_initializeD) { for(i=0;i<Smem_hashsizE;i++) { Smem_hasH[i]= NULL; Smem_hash_counteR[i]= 0.0; } Smem_hash_initializeD= 1; } Smem_record_itemS= value; return(1); } int Smem_is_recorded(ptr,flag) char *ptr; int flag; /* bit0= complain if return(0) */ { if(Smem_record_itemS==0) return(2); if(Smem_find_item(ptr,0)!=NULL) return(1); if(flag&1) Smem_protest("########### smem.c : free() of unrecorded pointer caught",0); return(0); } /* A simple C string cloner */ int Smem_clone_string(ptr,text) char **ptr; char *text; { *ptr= Smem_malloC(strlen(text)+1); if(*ptr==NULL) return(-1); strcpy(*ptr,text); return(1); } /* ----------------- for usage via debugger commands --------------------- */ /* find a certain memory item */ struct SmemiteM *Smem_find_data(ptr) char *ptr; { return(Smem_find_item(ptr,0)); } /* browsing the list */ struct SmemiteM *Smem_fetch_item(step,flag) int step; int flag; /* bit0= reset cursor (and therefore address absolutely) */ { static struct SmemiteM *current= NULL; if((flag&1)||current==NULL) current= Smem_start_iteM; if(step>0) { for(;current!=NULL;current= current->next) { if(step==0) return(current); step--; } } else if(step<0) { for(;current!=NULL;current= current->prev) { if(step==0) return(current); step++; } } else { return(current); } return(NULL); } int Smem_print_hash_counter() { int i; for(i=0;i<Smem_hashsizE;i++) printf("%4d : %10.f\n",i,Smem_hash_counteR[i]); return(1); } /* delete all recorded memory items */ int Smem_delete_all_items() { int ret; while(Smem_start_iteM!=NULL) { ret= Smem_free(Smem_start_iteM->data); if(ret<=0) return(0); } return(1); } #ifndef Smem_includeD #define Smem_includeD /* compile time adjustable parameters : */ /* if not defined, flat malloc() and free() is used */ #define Smem_own_functionS #ifdef Smem_no_own_functionS #undef Smem_own_functionS #endif /* Smem_no_own_functionS */ /* if not defined, the record items will be smaller by 8 byte but deletion of items may be much slower */ #define Smem_with_hasH struct SmemiteM { char *data; size_t size; struct SmemiteM *prev,*next; struct SmemiteM *hash_prev,*hash_next; }; #ifdef Smem_own_functionS char *Smem_malloc(); int Smem_free(); #define TSOB_FELD(typ,anz) (typ *) Smem_malloc((anz)*sizeof(typ)); #define Smem_malloC Smem_malloc #define Smem_freE Smem_free #else /* Smem_own_functionS */ #define TSOB_FELD(typ,anz) (typ *) malloc((anz)*sizeof(typ)); #define Smem_malloC malloc #define Smem_freE free #endif /* ! Smem_own_functionS */ int Smem_set_record_items(); int Smem_stderr(); int Smem_clone_string(); #define Smem_hashsizE 251 #define Smem_hashshifT 8 #ifdef Smem_included_by_smem_C double Smem_malloc_counT= 0.0; double Smem_free_counT= 0.0; double Smem_pending_counT= 0.0; struct SmemiteM *Smem_start_iteM= NULL; struct SmemiteM *Smem_hasH[Smem_hashsizE]; double Smem_hash_counteR[Smem_hashsizE]; /* these both init values are essential, since setting Smem_record_itemS=1 by use of Smem_set_record_items() initializes the hash array (i do not really trust the compiler producers to have read K&R) */ int Smem_hash_initializeD= 0; int Smem_record_itemS= 0; double Smem_record_counT= 0.0; double Smem_record_byteS= 0.0; #else /* Smem_included_by_smem_C */ extern double Smem_malloc_counT; extern double Smem_free_counT; extern double Smem_pending_counT; extern struct SmemiteM *Smem_start_iteM; extern struct SmemiteM *Smem_hasH[Smem_hashsizE]; extern double Smem_hash_counteR[Smem_hashsizE]; extern int Smem_hash_initializeD; extern int Smem_record_itemS; extern double Smem_record_counT; extern double Smem_record_byteS; #endif /* ! Smem_included_by_smem_C */ #endif /* ! Smem_includeD */ /* smem Functions to replace malloc() and free() in order to get more control over memory leaks or spurious errors caused by faulty usage of malloc() and free(). Sourcecode provisions: Use only the following macros for memory management: TSOB_FELD(type,count) creates an array of items of given type Smem_malloC() analogue of malloc() Smem_freE() analogue of free() One may #define malloc Smem_malloC resp. #define free Smem_freE but better would be to review (and often to streamline) the sourcecode in respect to those two functions. Speed versus control: In production versions, where maximum speed is required, one may undefine the macro Smem_own_functionS in smem.h . This causes the above macros to directly invoke malloc() and free() without any speed reduction (and without any additional use). Undefinitio can be done globaly by modifying smem.h or locally by defining Smem_no_own_functionS before including smem.h . If Smem_own_functionS remains defined, then the functions Smem_malloc() Smem_free() are used rather than malloc() and free(). They count the number of calls to maintain a rough overview of memory usage. Smem_malloc() additionally checks for 0 size and Smem_free() checks for NULL pointers, which they both report to stderr. Eventually one should set a breakpoint in function Smem_protest() to learn about the origin of such messages. A status line may be obtained by Smem_report() or printed by Smem_stderr(). As long as the variable Smem_record_itemS is set to 0, there is not very much overhead compared with malloc() and free(). If the variable is set to 1 by Smem_set_record_items() then all malloc() results are kept in a list where they will be deleted by their corresponding Smem_free() calls. If a pointer is to be freed, which is not recorded in the list then an error message will be printed to stderr. The memory will not be freed ! This mode not only may be very slow, it also consumes at least 16 byte per piece of data which was obtained by malloc as long as it has not been freed. Due to the current nature of the list, large numbers of memory items are freed much faster in the reverse order of their creation. If there is a list of 100000 strings to delete, it is very rewarding to free the youngest ones first. A shortcut via hashing is available but consumes 24 bytes rather than 16. (see above Smem_with_hasH ) The function Smem_is_recorded() can be used to check wether a pointer is valid according to the list. It returns : 0 = is not in list , 1 = is in list , 2 = recording is off If one decides to start recording malloc() results in the midst of a program run, one has to be aware of false protests of Smem_free() if a memory piece has been allocated before recording started. This will also cause those pieces to be memory leaks because Smem_free() refuses to delete them. (Freeing memory that was not obtained by malloc or was already freed previously can result in deferred SIGSEGV or similar trouble, depending on OS and library.) Also in that case one should stop recording before ending the program, to avoid a lot of false complaints about longliving memory objects. */ all clean: $(MAKE) -C .. -$(MAKEFLAGS) $@ .PHONY: all clean /* dewav Demo of libburn extension libdax_audioxtr Audio track data extraction facility of libdax and libburn. Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL */ #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <errno.h> /* libdax_audioxtr is quite independent of libburn. It only needs the messaging facility libdax_msgs. So we got two build variations: */ #ifdef Dewav_without_libburN /* This build environment is standalone relying only on libdax components */ #include "../libburn/libdax_msgs.h" struct libdax_msgs *libdax_messenger= NULL; /* The API for .wav extraction */ #define LIBDAX_AUDIOXTR_H_PUBLIC 1 #include "../libburn/libdax_audioxtr.h" #else /* Dewav_without_libburN */ /* This build environment uses libdax_msgs and libdax_audioxtr via libburn */ /* Thus the API header of libburn */ #include "../libburn/libburn.h" #endif /* ! Dewav_without_libburN */ int main(int argc, char **argv) { /* This program acts as filter from in_path to out_path */ char *in_path= "", *out_path= "-"; /* The read-and-extract object for use with in_path */ struct libdax_audioxtr *xtr= NULL; /* The file descriptor eventually detached from xtr */ int xtr_fd= -2; /* Default output is stdout */ int out_fd= 1; /* Inquired source parameters */ char *fmt, *fmt_info; int num_channels, sample_rate, bits_per_sample, msb_first; off_t data_size; /* Auxiliary variables */ int ret, i, be_strict= 1, buf_count, detach_fd= 0, extract_all= 0; char buf[2048]; if(argc < 2) goto help; for(i= 1; i<argc; i++) { if(strcmp(argv[i],"-o")==0) { if(i>=argc-1) { fprintf(stderr,"%s: option -o needs a file address as argument.\n", argv[0]); exit(1); } i++; out_path= argv[i]; } else if(strcmp(argv[i],"--lax")==0) { be_strict= 0; } else if(strcmp(argv[i],"--strict")==0) { be_strict= 1; } else if(strcmp(argv[i],"--detach_fd")==0) { /* Test the dirty detach method. Always --extract_all */ detach_fd= 1; } else if(strcmp(argv[i],"--extract_all")==0) { /* Dirty : read all available bytes regardless of data_size */ extract_all= 1; } else if(strcmp(argv[i],"--help")==0) { help:; fprintf(stderr, "usage: %s [-o output_path|\"-\"] [--lax|--strict] [source_path|\"-\"]\n", argv[0]); exit(0); } else { if(in_path[0]!=0) { fprintf(stderr,"%s: only one input file is allowed.\n", argv[0]); exit(2); } in_path= argv[i]; } } if(in_path[0] == 0) in_path= "-"; /* Depending on wether this was built standalone or with full libburn : */ #ifdef Dewav_without_libburN /* Initialize and set up libdax messaging system */ ret= libdax_msgs_new(&libdax_messenger,0); if(ret<=0) { fprintf(stderr,"Failed to create libdax_messenger object.\n"); exit(3); } libdax_msgs_set_severities(libdax_messenger, LIBDAX_MSGS_SEV_NEVER, LIBDAX_MSGS_SEV_NOTE, "", 0); fprintf(stderr, "dewav on libdax\n"); #else /* Dewav_without_libburN */ /* Initialize libburn and set up its messaging system */ if(burn_initialize() == 0) { fprintf(stderr,"Failed to initialize libburn.\n"); exit(3); } /* Print messages of severity NOTE or more directly to stderr */ burn_msgs_set_severities("NEVER", "NOTE", ""); fprintf(stderr, "dewav on libburn\n"); #endif /* ! Dewav_without_libburN */ /* Open audio source and create extractor object */ ret= libdax_audioxtr_new(&xtr, in_path, 0); if(ret<=0) exit(4); if(strcmp(out_path,"-")!=0) { out_fd= open(out_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if(out_fd == -1) { fprintf(stderr, "Cannot open file: %s\n", out_path); fprintf(stderr, "Error reported: '%s' (%d)\n",strerror(errno), errno); exit(5); } } /* Obtain and print parameters of audio source */ libdax_audioxtr_get_id(xtr, &fmt, &fmt_info, &num_channels, &sample_rate, &bits_per_sample, &msb_first, 0); fprintf(stderr, "Detected format: %s\n", fmt_info); libdax_audioxtr_get_size(xtr, &data_size, 0); fprintf(stderr, "Data size : %.f bytes\n", (double) data_size); if((strcmp(fmt,".wav")!=0 && strcmp(fmt,".au")!=0) || num_channels!=2 || sample_rate!=44100 || bits_per_sample!=16) { fprintf(stderr, "%sAudio source parameters do not comply to cdrskin/README specs\n", (be_strict ? "" : "WARNING: ")); if(be_strict) exit(6); } if(msb_first==0) fprintf(stderr, "NOTE: Extracted data to be written with cdrskin option -swab\n"); if(detach_fd) { /* Take over fd from xtr */; ret= libdax_audioxtr_detach_fd(xtr, &xtr_fd, 0); if(ret<=0) { fprintf(stderr, "Cannot detach file descriptor from extractor\n"); exit(8); } /* not needed any more */ libdax_audioxtr_destroy(&xtr, 0); fprintf(stderr, "Note: detached fd and freed extractor object.\n"); } /* Extract and put out raw audio data */; while(1) { if(detach_fd) { buf_count= read(xtr_fd, buf, sizeof(buf)); if(buf_count==-1) fprintf(stderr,"Error while reading from detached fd\n(%d) '%s'\n", errno, strerror(errno)); } else { buf_count= libdax_audioxtr_read(xtr, buf, sizeof(buf), !!extract_all); } if(buf_count < 0) exit(7); if(buf_count == 0) break; ret= write(out_fd, buf, buf_count); if(ret == -1) { fprintf(stderr, "Failed to write buffer of %d bytes to: %s\n", buf_count, out_path); fprintf(stderr, "Error reported: '%s' (%d)\n", strerror(errno), errno); exit(5); } } /* Shutdown */ if(out_fd>2) close(out_fd); /* ( It is permissible to do this with xtr==NULL ) */ libdax_audioxtr_destroy(&xtr, 0); #ifdef Dewav_without_libburN libdax_msgs_destroy(&libdax_messenger,0); #else /* Dewav_without_libburN */ burn_finish(); #endif /* ! Dewav_without_libburN */ exit(0); } /* fake_au Fakes a file in SUN .au format from a raw little-endian PCM audio file (e.g. a file extracted from .wav by test/dewav). The input data are assumed to be 16 bit, stereo, 44100 Hz. Copyright (C) 2006 - 2015 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. Info used: http://www.opengroup.org/public/pubs/external/auformat.html */ #include <ctype.h> #include <sys/types.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <string.h> int fake_write(unsigned char *buf, size_t size, FILE *fp) { int ret; ret= fwrite(buf,size,1,fp); if(ret==1) return(1); fprintf(stderr,"Error %d while writing: '%s'\n",errno,strerror(errno)); return(0); } int main(int argc, char **argv) { int ret, i; unsigned data_size= 0,byte_count,exit_value= 0; FILE *fp_out= stdout,*fp_in= stdin; unsigned char buf[4]; char out_path[4096],in_path[4096]; struct stat stbuf; strcpy(out_path,"-"); strcpy(in_path,""); if(argc < 2) { exit_value= 1; goto help; } for(i= 1; i<argc; i++) { if(strlen(argv[i]) >= 4096) { fprintf(stderr,"%s: argument at position %d is much too long.\n", argv[0], i); exit(1); } } for(i= 1; i<argc; i++) { if(strcmp(argv[i],"-o")==0) { if(i>=argc-1) { fprintf(stderr,"%s: option -o needs a file address as argument.\n", argv[0]); exit(1); } i++; strcpy(out_path, argv[i]); } else if(strcmp(argv[i],"--stdin_size")==0) { if(i>=argc-1) { fprintf(stderr,"%s: option --stdin_size needs a number as argument.\n", argv[0]); exit(1); } i++; sscanf(argv[i],"%u",&data_size); } else if(strcmp(argv[i],"--help")==0) { exit_value= 0; help:; fprintf(stderr,"usage: %s \\\n", argv[0]); fprintf(stderr," [-o output_path|\"-\"] [source_path | --stdin_size size]\n"); fprintf(stderr, "Disguises an extracted .wav stream as .au stereo, 16bit, 44100Hz\n"); fprintf(stderr, "stdin gets byte-swapped and appended up to the announced data_size.\n"); exit(exit_value); } else { if(in_path[0]!=0) { fprintf(stderr,"%s: only one input file is allowed.\n", argv[0]); exit(1); } strcpy(in_path, argv[i]); } } if(strcmp(in_path,"-")==0 || in_path[0]==0) { if(data_size==0) { fprintf(stderr,"%s: input from stdin needs option --stdin_size.\n", argv[0]); exit(6); } fp_in= stdin; } else { fp_in= fopen(in_path,"r"); if(stat(in_path,&stbuf)!=-1) data_size= stbuf.st_size; } if(fp_in==NULL) { fprintf(stderr,"Error %d while fopen(\"%s\",\"r\") : '%s'\n", errno,in_path,strerror(errno)); exit(2); } if(strcmp(out_path,"-")==0) { fp_out= stdout; } else { if(stat(out_path,&stbuf)!=-1) { fprintf(stderr,"%s: file '%s' already existing\n",argv[0],out_path); exit(4); } fp_out= fopen(out_path,"w"); } if(fp_out==NULL) { fprintf(stderr,"Error %d while fopen(\"%s\",\"w\") : '%s'\n", errno,out_path,strerror(errno)); exit(2); } fake_write((unsigned char *) ".snd",4,fp_out); /* magic number */ buf[0]= buf[1]= buf[2]= 0; buf[3]= 32; fake_write(buf,4,fp_out); /* data_location */ buf[0]= (data_size>>24)&0xff; buf[1]= (data_size>>16)&0xff; buf[2]= (data_size>>8)&0xff; buf[3]= (data_size)&0xff; fake_write(buf,4,fp_out); /* data_size */ buf[0]= buf[1]= buf[2]= 0; buf[3]= 3; fake_write(buf,4,fp_out); /* encoding 16 Bit PCM */ buf[0]= buf[1]= 0; buf[2]= 172; buf[3]= 68; fake_write(buf,4,fp_out); /* sample rate 44100 Hz */ buf[0]= buf[1]= buf[2]= 0; buf[3]= 2; fake_write(buf,4,fp_out); /* number of channels */ buf[0]= buf[1]= buf[2]= buf[3]= 0; fake_write(buf,4,fp_out); /* padding */ fake_write(buf,4,fp_out); /* padding */ for(byte_count= 0; byte_count<data_size; byte_count+=2) { ret= fread(buf,2,1,fp_in); if(ret<=0) { fprintf(stderr,"Premature end end of input\n"); exit_value= 5; break; } buf[3]= buf[0]; buf[2]= buf[1]; ret= fake_write(buf+2,2,fp_out); if(ret<=0) { exit_value= 3; break; } } if(fp_out!=stdout) fclose(fp_out); if(fp_in!=stdin) fclose(fp_in); fprintf(stderr, "Swapped and appended: %u stdin bytes\n",byte_count); exit(exit_value); } /* test/libburner.c , API illustration of burning data or audio tracks to CD */ /* Copyright (C) 2005 - 2016 Thomas Schmitt <scdbackup@gmx.net> */ /* Provided under GPL, see also "License and copyright aspects" at file end */ /** Overview libburner is a minimal demo application for the library libburn as provided on http://libburnia-project.org . It can list the available devices, can blank a CD-RW or DVD-RW, can format DVD-RW and BD, can burn to CD-R, CD-RW, DVD-R, DVD+R, DVD+R/DL, DVD+RW, DVD-RW, DVD-RAM, BD-R, BD-RE. Not supported yet: DVD-R/DL. It's main purpose, nevertheless, is to show you how to use libburn and also to serve the libburnia team as reference application. libburner.c does indeed define the standard way how above three gestures can be implemented and stay upward compatible for a good while. There is another demo program, test/telltoc.c, which inspects drive, media state, and media contents. Before you can do anything, you have to initialize libburn by burn_initialize() and provide some signal and abort handling, e.g. by the builtin handler, by burn_set_signal_handling("libburner : ", NULL, 0x0) as it is done in main() at the end of this file. Then you acquire a drive in an appropriate way conforming to the API. The two main approaches are shown here in application functions: libburner_aquire_by_adr() demonstrates usage as of cdrecord traditions libburner_aquire_by_driveno() demonstrates a scan-and-choose approach With that acquired drive you can blank a CD-RW or DVD-RW as shown in libburner_blank_disc() or you can format a DVD-RW to profile "Restricted Overwrite" (needed once) or an unused BD to default size with spare blocks libburner_format() With the acquired drive you can burn to CD, DVD, BD. See libburner_payload() These three functions switch temporarily to a non-fatal signal handler while they are waiting for the drive to become idle again: burn_set_signal_handling("libburner : ", NULL, 0x30) After the waiting loop ended, they check for eventual abort events by burn_is_aborting(0) The 0x30 handler will eventually execute burn_abort() but not wait for the drive to become idle and not call exit(). This is needed because the worker threads might block as long as the signal handler has not returned. The 0x0 handler would wait for them to finish. Take this into respect when implementing own signal handlers. When everything is done, main() releases the drive and shuts down libburn: burn_drive_release(); burn_finish() Applications must use 64 bit off_t. E.g. by defining #define _LARGEFILE_SOURCE #define _FILE_OFFSET_BITS 64 or take special precautions to interface with the library by 64 bit integers where libburn/libburn.h prescribes off_t. This program gets fed with appropriate settings externally by libburn's autotools generated build system. */ /** See this for the decisive API specs . libburn.h is The Original */ /* For using the installed header file : #include <libburn/libburn.h> */ /* This program insists in the own headerfile. */ #include "../libburn/libburn.h" /* libburn works on Linux systems with kernel 2.4 or 2.6, FreeBSD, Solaris */ #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <errno.h> #include <sys/stat.h> #include <fcntl.h> /** For simplicity i use global variables to represent the drives. Drives are systemwide global, so we do not give away much of good style. */ /** This list will hold the drives known to libburn. This might be all CD drives of the system and thus might impose severe impact on the system. */ static struct burn_drive_info *drive_list; /** If you start a long lasting operation with drive_count > 1 then you are not friendly to the users of other drives on those systems. Beware. */ static unsigned int drive_count; /** This variable indicates wether the drive is grabbed and must be finally released */ static int drive_is_grabbed = 0; /** A number and a text describing the type of media in acquired drive */ static int current_profile= -1; static char current_profile_name[80]= {""}; /* Some in-advance definitions make possible a more comprehensive ordering of the functions and their explanations in here */ int libburner_aquire_by_adr(char *drive_adr); int libburner_aquire_by_driveno(int *drive_no); /* ------------------------------- API gestures ---------------------------- */ /** You need to acquire a drive before burning. The API offers this as one compact call and alternatively as application controllable gestures of whitelisting, scanning for drives and finally grabbing one of them. If you have a persistent address of the drive, then the compact call is to prefer because it only touches one drive. On modern Linux kernels, there should be no fatal disturbance of ongoing burns of other libburn instances with any of our approaches. We use open(O_EXCL) by default. On /dev/hdX it should cooperate with growisofs and some cdrecord variants. On /dev/sgN versus /dev/scdM expect it not to respect other programs. */ int libburner_aquire_drive(char *drive_adr, int *driveno) { int ret; if(drive_adr != NULL && drive_adr[0] != 0) ret = libburner_aquire_by_adr(drive_adr); else ret = libburner_aquire_by_driveno(driveno); if (ret <= 0 || *driveno <= 0) return ret; burn_disc_get_profile(drive_list[0].drive, ¤t_profile, current_profile_name); if (current_profile_name[0]) printf("Detected media type: %s\n", current_profile_name); return 1; } /** If the persistent drive address is known, then this approach is much more un-obtrusive to the systemwide livestock of drives. Only the given drive device will be opened during this procedure. */ int libburner_aquire_by_adr(char *drive_adr) { int ret; char libburn_drive_adr[BURN_DRIVE_ADR_LEN]; /* Some not-so-harmless drive addresses get blocked in this demo */ if (strncmp(drive_adr, "stdio:/dev/fd/", 14) == 0 || strcmp(drive_adr, "stdio:-") == 0) { fprintf(stderr, "Will not work with pseudo-drive '%s'\n", drive_adr); return 0; } /* This tries to resolve links or alternative device files */ ret = burn_drive_convert_fs_adr(drive_adr, libburn_drive_adr); if (ret<=0) { fprintf(stderr, "Address does not lead to a CD burner: '%s'\n", drive_adr); return 0; } fprintf(stderr,"Aquiring drive '%s' ...\n", libburn_drive_adr); ret = burn_drive_scan_and_grab(&drive_list, libburn_drive_adr, 1); if (ret <= 0) { fprintf(stderr,"FAILURE with persistent drive address '%s'\n", libburn_drive_adr); } else { fprintf(stderr,"Done\n"); drive_is_grabbed = 1; } return ret; } /** This method demonstrates how to use libburn without knowing a persistent drive address in advance. It has to make sure that after assessing the list of available drives, all unwanted drives get closed again. As long as they are open, no other libburn instance can see them. This is an intended locking feature. The application is responsible for giving up the locks by either burn_drive_release() (only after burn_drive_grab() !), burn_drive_info_forget(), burn_drive_info_free(), or burn_finish(). @param driveno the index number in libburn's drive list. This will get set to 0 on success and will then be the drive index to use in the further dourse of processing. @return 1 success , <= 0 failure */ int libburner_aquire_by_driveno(int *driveno) { char adr[BURN_DRIVE_ADR_LEN]; int ret, i; printf("Beginning to scan for devices ...\n"); while (!burn_drive_scan(&drive_list, &drive_count)) usleep(100002); if (drive_count <= 0 && *driveno >= 0) { printf("FAILED (no drives found)\n"); return 0; } printf("Done\n"); /* Interactive programs may choose the drive number at this moment. drive[0] to drive[drive_count-1] are struct burn_drive_info as defined in libburn/libburn.h . This structure is part of API and thus will strive for future compatibility on source level. Have a look at the info offered. Caution: do not take .location for drive address. Always use burn_drive_get_adr() or you might become incompatible in future. Note: bugs with struct burn_drive_info - if any - will not be easy to fix. Please report them but also strive for workarounds on application level. */ printf("\nOverview of accessible drives (%d found) :\n", drive_count); printf("-----------------------------------------------------------------------------\n"); for (i = 0; i < (int) drive_count; i++) { if (burn_drive_get_adr(&(drive_list[i]), adr) <=0) strcpy(adr, "-get_adr_failed-"); printf("%d --drive '%s' : '%s' '%s'\n", i,adr,drive_list[i].vendor,drive_list[i].product); } printf("-----------------------------------------------------------------------------\n\n"); /* On multi-drive systems save yourself from sysadmins' revenge. Be aware that you hold reserved all available drives at this point. So either make your choice quick enough not to annoy other system users, or set free the drives for a while. The tested way of setting free all drives is to shutdown the library and to restart when the choice has been made. The list of selectable drives should also hold persistent drive addresses as obtained above by burn_drive_get_adr(). By such an address one may use burn_drive_scan_and_grab() to finally acquire exactly one drive. A not yet tested shortcut should be to call burn_drive_info_free() and to call either burn_drive_scan() or burn_drive_scan_and_grab() before accessing any drives again. In both cases you have to be aware that the desired drive might get acquired in the meantime by another user or libburn process. */ /* We already made our choice via command line. (default is 0) So we just have to keep our desired drive and drop all others. No other libburn instance will have a chance to steal our drive. */ if (*driveno < 0) { printf("Pseudo-drive \"-\" given : bus scanning done.\n"); return 2; /* the program will end after this */ } if ((int) drive_count <= *driveno) { fprintf(stderr, "Found only %d drives. Number %d not available.\n", drive_count, *driveno); return 0; /* the program will end after this */ } /* Drop all drives which we do not want to use */ for (i = 0; i < (int) drive_count; i++) { if (i == *driveno) /* the one drive we want to keep */ continue; ret = burn_drive_info_forget(&(drive_list[i]),0); if (ret != 1) fprintf(stderr, "Cannot drop drive %d. Please report \"ret=%d\" to libburn-hackers@pykix.org\n", i, ret); else printf("Dropped unwanted drive %d\n",i); } /* Make the one we want ready for blanking or burning */ ret= burn_drive_grab(drive_list[*driveno].drive, 1); if (ret != 1) return 0; drive_is_grabbed = 1; return 1; } /** Makes a previously used CD-RW or unformatted DVD-RW ready for thorough re-usal. To our knowledge it is hardly possible to abort an ongoing blank operation because after start it is entirely handled by the drive. So expect signal handling to wait the normal blanking timespan until it can allow the process to end. External kill -9 will not help the drive. */ int libburner_blank_disc(struct burn_drive *drive, int blank_fast) { enum burn_disc_status disc_state; struct burn_progress p; double percent = 1.0; disc_state = burn_disc_get_status(drive); printf( "Drive media status: %d (see libburn/libburn.h BURN_DISC_*)\n", disc_state); if (current_profile == 0x13) { ; /* formatted DVD-RW will get blanked to sequential state */ } else if (disc_state == BURN_DISC_BLANK) { fprintf(stderr, "IDLE: Blank media detected. Will leave it untouched\n"); return 2; } else if (disc_state == BURN_DISC_FULL || disc_state == BURN_DISC_APPENDABLE) { ; /* this is what libburner is willing to blank */ } else if (disc_state == BURN_DISC_EMPTY) { fprintf(stderr,"FATAL: No media detected in drive\n"); return 0; } else { fprintf(stderr, "FATAL: Unsuitable drive and media state\n"); return 0; } if(!burn_disc_erasable(drive)) { fprintf(stderr, "FATAL : Media is not of erasable type\n"); return 0; } /* Switch to asynchronous signal handling for the time of waiting */ burn_set_signal_handling("libburner : ", NULL, 0x30); printf("Beginning to %s-blank media.\n", (blank_fast?"fast":"full")); burn_disc_erase(drive, blank_fast); sleep(1); while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) { if(p.sectors>0 && p.sector>=0) /* display 1 to 99 percent */ percent = 1.0 + ((double) p.sector+1.0) / ((double) p.sectors) * 98.0; printf("Blanking ( %.1f%% done )\n", percent); sleep(1); } if (burn_is_aborting(0) > 0) return -1; /* Back to synchronous handling */ burn_set_signal_handling("libburner : ", NULL, 0x0); printf("Done\n"); return 1; } /** Formats unformatted DVD-RW to profile 0013h "Restricted Overwrite" which needs no blanking for re-use but is not capable of multi-session. Expect a behavior similar to blanking with unusual noises from the drive. Formats unformatted BD-RE to default size. This will allocate some reserve space, test for bad blocks and make the media ready for writing. Expect a very long run time. Formats unformatted blank BD-R to hold a default amount of spare blocks for eventual mishaps during writing. If BD-R get written without being formatted, then they get no such reserve and will burn at full speed. */ int libburner_format(struct burn_drive *drive) { struct burn_progress p; double percent = 1.0; int ret, status, num_formats, format_flag= 0; off_t size = 0; unsigned dummy; enum burn_disc_status disc_state; if (current_profile == 0x13) { fprintf(stderr, "IDLE: DVD-RW media is already formatted\n"); return 2; } else if (current_profile == 0x41 || current_profile == 0x43) { disc_state = burn_disc_get_status(drive); if (disc_state != BURN_DISC_BLANK && current_profile == 0x41) { fprintf(stderr, "FATAL: BD-R is not blank. Cannot format.\n"); return 0; } ret = burn_disc_get_formats(drive, &status, &size, &dummy, &num_formats); if (ret > 0 && status != BURN_FORMAT_IS_UNFORMATTED) { fprintf(stderr, "IDLE: BD media is already formatted\n"); return 2; } size = 0; /* does not really matter */ format_flag = 3<<1; /* format to default size, no quick */ } else if (current_profile == 0x14) { /* sequential DVD-RW */ size = 128 * 1024 * 1024; format_flag = 1; /* write initial 128 MiB */ } else { fprintf(stderr, "FATAL: Can only format DVD-RW or BD\n"); return 0; } burn_set_signal_handling("libburner : ", NULL, 0x30); printf("Beginning to format media.\n"); burn_disc_format(drive, size, format_flag); sleep(1); while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) { if(p.sectors>0 && p.sector>=0) /* display 1 to 99 percent */ percent = 1.0 + ((double) p.sector+1.0) / ((double) p.sectors) * 98.0; printf("Formatting ( %.1f%% done )\n", percent); sleep(1); } if (burn_is_aborting(0) > 0) return -1; burn_set_signal_handling("libburner : ", NULL, 0x0); burn_disc_get_profile(drive_list[0].drive, ¤t_profile, current_profile_name); if (current_profile == 0x14 || current_profile == 0x13) printf("Media type now: %4.4xh \"%s\"\n", current_profile, current_profile_name); if (current_profile == 0x14) { fprintf(stderr, "FATAL: Failed to change media profile to desired value\n"); return 0; } return 1; } /** Brings preformatted track images (ISO 9660, audio, ...) onto media. To make sure a data image is fully readable on any Linux machine, this function adds 300 kiB of padding to the (usualy single) track. Audio tracks get padded to complete their last sector. A fifo of 4 MB is installed between each track and its data source. Each of the 4 MB buffers gets allocated automatically as soon as a track begins to be processed and it gets freed as soon as the track is done. The fifos do not wait for buffer fill but writing starts immediately. In case of external signals expect abort handling of an ongoing burn to last up to a minute. Wait the normal burning timespan before any kill -9. */ int libburner_payload(struct burn_drive *drive, char source_adr[][4096], int source_adr_count, int multi, int simulate_burn, int all_tracks_type) { struct burn_source *data_src = NULL, *fifo_src[99]; struct burn_disc *target_disc = NULL; struct burn_session *session = NULL; struct burn_write_opts *burn_options = NULL; enum burn_disc_status disc_state; struct burn_track *track, *tracklist[99]; struct burn_progress progress; time_t start_time; int last_sector = 0, padding = 0, trackno, unpredicted_size = 0, fd; int fifo_chunksize = 2352, fifo_chunks = 1783; /* ~ 4 MB fifo */ int ret; off_t fixed_size; char *adr, reasons[BURN_REASONS_LEN]; struct stat stbuf; for (trackno = 0 ; trackno < source_adr_count; trackno++) { fifo_src[trackno] = NULL; tracklist[trackno] = NULL; } if (all_tracks_type != BURN_AUDIO) { all_tracks_type = BURN_MODE1; /* a padding of 300 kiB helps to avoid the read-ahead bug */ padding = 300*1024; fifo_chunksize = 2048; fifo_chunks = 2048; /* 4 MB fifo */ } target_disc = burn_disc_create(); session = burn_session_create(); burn_disc_add_session(target_disc, session, BURN_POS_END); for (trackno = 0 ; trackno < source_adr_count; trackno++) { tracklist[trackno] = track = burn_track_create(); burn_track_define_data(track, 0, padding, 1, all_tracks_type); /* Open file descriptor to source of track data */ adr = source_adr[trackno]; fixed_size = 0; if (adr[0] == '-' && adr[1] == 0) { fd = 0; } else { fd = open(adr, O_RDONLY); if (fd>=0) if (fstat(fd,&stbuf)!=-1) if((stbuf.st_mode&S_IFMT)==S_IFREG) fixed_size = stbuf.st_size; } if (fixed_size==0) unpredicted_size = 1; /* Convert this filedescriptor into a burn_source object */ data_src = NULL; if (fd >= 0) data_src = burn_fd_source_new(fd, -1, fixed_size); if (data_src == NULL) { fprintf(stderr, "FATAL: Could not open data source '%s'.\n",adr); if(errno!=0) fprintf(stderr,"(Most recent system error: %s )\n", strerror(errno)); {ret = 0; goto ex;} } /* Install a fifo object on top of that data source object */ fifo_src[trackno] = burn_fifo_source_new(data_src, fifo_chunksize, fifo_chunks, 0); if (fifo_src[trackno] == NULL) { fprintf(stderr, "FATAL: Could not create fifo object of 4 MB\n"); {ret = 0; goto ex;} } /* Use the fifo object as data source for the track */ if (burn_track_set_source(track, fifo_src[trackno]) != BURN_SOURCE_OK) { fprintf(stderr, "FATAL: Cannot attach source object to track object\n"); {ret = 0; goto ex;} } burn_session_add_track(session, track, BURN_POS_END); printf("Track %d : source is '%s'\n", trackno+1, adr); /* Give up local reference to the data burn_source object */ burn_source_free(data_src); data_src = NULL; } /* trackno loop end */ /* Evaluate drive and media */ disc_state = burn_disc_get_status(drive); if (disc_state != BURN_DISC_BLANK && disc_state != BURN_DISC_APPENDABLE) { if (disc_state == BURN_DISC_FULL) { fprintf(stderr, "FATAL: Closed media with data detected. Need blank or appendable media.\n"); if (burn_disc_erasable(drive)) fprintf(stderr, "HINT: Try --blank_fast\n\n"); } else if (disc_state == BURN_DISC_EMPTY) fprintf(stderr,"FATAL: No media detected in drive\n"); else fprintf(stderr, "FATAL: Cannot recognize state of drive and media\n"); {ret = 0; goto ex;} } burn_options = burn_write_opts_new(drive); burn_write_opts_set_perform_opc(burn_options, 0); burn_write_opts_set_multi(burn_options, !!multi); if(simulate_burn) printf("\n*** Will TRY to SIMULATE burning ***\n\n"); burn_write_opts_set_simulate(burn_options, simulate_burn); burn_drive_set_speed(drive, 0, 0); burn_write_opts_set_underrun_proof(burn_options, 1); if (burn_write_opts_auto_write_type(burn_options, target_disc, reasons, 0) == BURN_WRITE_NONE) { fprintf(stderr, "FATAL: Failed to find a suitable write mode with this media.\n"); fprintf(stderr, "Reasons given:\n%s\n", reasons); {ret = 0; goto ex;} } burn_set_signal_handling("libburner : ", NULL, 0x30); printf("Burning starts. With e.g. 4x media expect up to a minute of zero progress.\n"); start_time = time(0); burn_disc_write(burn_options, target_disc); while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) usleep(100002); while (burn_drive_get_status(drive, &progress) != BURN_DRIVE_IDLE) { if (progress.sectors <= 0 || (progress.sector >= progress.sectors - 1 && !unpredicted_size) || (unpredicted_size && progress.sector == last_sector)) printf( "Thank you for being patient since %d seconds.", (int) (time(0) - start_time)); else if(unpredicted_size) printf("Track %d : sector %d", progress.track+1, progress.sector); else printf("Track %d : sector %d of %d",progress.track+1, progress.sector, progress.sectors); last_sector = progress.sector; if (progress.track >= 0 && progress.track < source_adr_count) { int size, free_bytes, ret; char *status_text; ret = burn_fifo_inquire_status( fifo_src[progress.track], &size, &free_bytes, &status_text); if (ret >= 0 ) printf(" [fifo %s, %2d%% fill]", status_text, (int) (100.0 - 100.0 * ((double) free_bytes) / (double) size)); } printf("\n"); sleep(1); } printf("\n"); if (burn_is_aborting(0) > 0) {ret = -1; goto ex;} if (multi && current_profile != 0x1a && current_profile != 0x13 && current_profile != 0x12 && current_profile != 0x43) /* not with DVD+RW, formatted DVD-RW, DVD-RAM, BD-RE */ printf("NOTE: Media left appendable.\n"); if (simulate_burn) printf("\n*** Did TRY to SIMULATE burning ***\n\n"); ret = 1; ex:; /* Dispose objects */ if (burn_options != NULL) burn_write_opts_free(burn_options); for (trackno = 0 ; trackno < source_adr_count; trackno++) { if (fifo_src[trackno] != NULL) burn_source_free(fifo_src[trackno]); if (tracklist[trackno]) burn_track_free(tracklist[trackno]); } if (data_src != NULL) burn_source_free(data_src); if (session != NULL) burn_session_free(session); if (target_disc != NULL) burn_disc_free(target_disc); return ret; } /** The setup parameters of libburner */ static char drive_adr[BURN_DRIVE_ADR_LEN] = {""}; static int driveno = 0; static int do_blank = 0; static char source_adr[99][4096]; static int source_adr_count = 0; static int do_multi = 0; static int simulate_burn = 0; static int all_tracks_type = BURN_MODE1; /** Converts command line arguments into above setup parameters. */ int libburner_setup(int argc, char **argv) { int i, insuffient_parameters = 0, print_help = 0; for (i = 1; i < argc; ++i) { if (!strcmp(argv[i], "--audio")) { all_tracks_type = BURN_AUDIO; } else if (!strcmp(argv[i], "--blank_fast")) { do_blank = 1; } else if (!strcmp(argv[i], "--blank_full")) { do_blank = 2; } else if (!strcmp(argv[i], "--burn_for_real")) { simulate_burn = 0; } else if (!strcmp(argv[i], "--drive")) { ++i; if (i >= argc) { fprintf(stderr,"--drive requires an argument\n"); return 1; } else if (strcmp(argv[i], "-") == 0) { drive_adr[0] = 0; driveno = -1; } else if (isdigit(argv[i][0])) { drive_adr[0] = 0; driveno = atoi(argv[i]); } else { if(strlen(argv[i]) >= BURN_DRIVE_ADR_LEN) { fprintf(stderr,"--drive address too long (max. %d)\n", BURN_DRIVE_ADR_LEN-1); return 2; } strcpy(drive_adr, argv[i]); } } else if ((!strcmp(argv[i], "--format_overwrite")) || (!strcmp(argv[i], "--format"))) { do_blank = 101; } else if (!strcmp(argv[i], "--multi")) { do_multi = 1; } else if (!strcmp(argv[i], "--stdin_size")) { /* obsoleted */ i++; } else if (!strcmp(argv[i], "--try_to_simulate")) { simulate_burn = 1; } else if (!strcmp(argv[i], "--help")) { print_help = 1; } else if (!strncmp(argv[i], "--",2)) { fprintf(stderr, "Unidentified option: %s\n", argv[i]); return 7; } else { if(strlen(argv[i]) >= 4096) { fprintf(stderr, "Source address too long (max. %d)\n", 4096-1); return 5; } if(source_adr_count >= 99) { fprintf(stderr, "Too many tracks (max. 99)\n"); return 6; } strcpy(source_adr[source_adr_count], argv[i]); source_adr_count++; } } insuffient_parameters = 1; if (driveno < 0) insuffient_parameters = 0; if (source_adr_count > 0) insuffient_parameters = 0; if (do_blank) insuffient_parameters = 0; if (print_help || insuffient_parameters ) { printf("Usage: %s\n", argv[0]); printf(" [--drive <address>|<driveno>|\"-\"] [--audio]\n"); printf(" [--blank_fast|--blank_full|--format] [--try_to_simulate]\n"); printf(" [--multi] [<one or more imagefiles>|\"-\"]\n"); printf("Examples\n"); printf("A bus scan (needs rw-permissions to see a drive):\n"); printf(" %s --drive -\n",argv[0]); printf("Burn a file to drive chosen by number, leave appendable:\n"); printf(" %s --drive 0 --multi my_image_file\n", argv[0]); printf("Burn a file to drive chosen by persistent address, close:\n"); printf(" %s --drive /dev/hdc my_image_file\n", argv[0]); printf("Blank a used CD-RW (is combinable with burning in one run):\n"); printf(" %s --drive /dev/hdc --blank_fast\n",argv[0]); printf("Blank a used DVD-RW (is combinable with burning in one run):\n"); printf(" %s --drive /dev/hdc --blank_full\n",argv[0]); printf("Format a DVD-RW, BD-RE or BD-R:\n"); printf(" %s --drive /dev/hdc --format\n", argv[0]); printf("Burn two audio tracks (to CD only):\n"); printf(" lame --decode -t /path/to/track1.mp3 track1.cd\n"); printf(" test/dewav /path/to/track2.wav -o track2.cd\n"); printf(" %s --drive /dev/hdc --audio track1.cd track2.cd\n", argv[0]); printf("Burn a compressed afio archive on-the-fly:\n"); printf(" ( cd my_directory ; find . -print | afio -oZ - ) | \\\n"); printf(" %s --drive /dev/hdc -\n", argv[0]); printf("To be read from *not mounted* media via: afio -tvZ /dev/hdc\n"); if (insuffient_parameters) return 6; } return 0; } int main(int argc, char **argv) { int ret; /* A warning to programmers who start their own projekt from here. */ if (sizeof(off_t) != 8) { fprintf(stderr, "\nFATAL: Compile time misconfiguration. off_t is not 64 bit.\n\n"); exit(39); } ret = libburner_setup(argc, argv); if (ret) exit(ret); printf("Initializing libburnia-project.org ...\n"); if (burn_initialize()) printf("Done\n"); else { printf("FAILED\n"); fprintf(stderr,"\nFATAL: Failed to initialize.\n"); exit(33); } /* Print messages of severity SORRY or more directly to stderr */ burn_msgs_set_severities("NEVER", "SORRY", "libburner : "); /* Activate the synchronous signal handler which eventually will try to properly shutdown drive and library on aborting events. */ burn_set_signal_handling("libburner : ", NULL, 0x0); /** Note: driveno might change its value in this call */ ret = libburner_aquire_drive(drive_adr, &driveno); if (ret<=0) { fprintf(stderr,"\nFATAL: Failed to acquire drive.\n"); { ret = 34; goto finish_libburn; } } if (ret == 2) { ret = 0; goto release_drive; } if (do_blank) { if (do_blank > 100) ret = libburner_format(drive_list[driveno].drive); else ret = libburner_blank_disc(drive_list[driveno].drive, do_blank == 1); if (ret<=0) { ret = 36; goto release_drive; } } if (source_adr_count > 0) { ret = libburner_payload(drive_list[driveno].drive, source_adr, source_adr_count, do_multi, simulate_burn, all_tracks_type); if (ret<=0) { ret = 38; goto release_drive; } } ret = 0; release_drive:; if (drive_is_grabbed) burn_drive_release(drive_list[driveno].drive, 0); finish_libburn:; if (burn_is_aborting(0) > 0) { burn_abort(4400, burn_abort_pacifier, "libburner : "); fprintf(stderr,"\nlibburner run aborted\n"); exit(1); } /* This app does not bother to know about exact scan state. Better to accept a memory leak here. We are done anyway. */ /* burn_drive_info_free(drive_list); */ burn_finish(); exit(ret); } /* License and copyright aspects: This all is provided under GPL. Read. Try. Think. Play. Write yourself some code. Be free of my copyright. Be also invited to study the code of cdrskin/cdrskin.c et al. History: libburner is a compilation of my own contributions to test/burniso.c and fresh code which replaced the remaining parts under copyright of Derek Foreman. My respect and my thanks to Derek for providing me a start back in 2005. */ /* cc -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS -g -o test/offst_source test/offst_source.c -lburn */ #include "../libburn/libburn.h" /* Just everything from test/libburner.c */ #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <errno.h> #include <sys/stat.h> #include <fcntl.h> static int create_original(struct burn_source **original, char *path, int flag) { printf("create_original: path='%s'\n", path); *original = burn_file_source_new(path, NULL); if (*original == NULL) return 0; return 1; } static int set_up_offst_sources(struct burn_source *original, struct burn_source *offsetters[], int count, int flag) { int i; off_t start = 3, size = 10, gap = 7; for (i = 0; i < count; i++) { offsetters[i] = burn_offst_source_new(original, i > 0 ? offsetters[i - 1] : NULL, start, size, 0); if (offsetters[i] == NULL) return 0; printf("set_up_offst_sources: idx=%d, start=%d\n", i, (int) start); start += size + gap; } return 1; } static int consume_source(struct burn_source *src, int flag) { int ret, count = 0; unsigned char buf[1]; while (1) { ret = src->read_xt(src, buf, 1); if (ret < 0) { printf("\n"); fprintf(stderr, "consume_source: count=%d, ret=%d\n", count, ret); return 0; } if (ret == 0) break; printf("%u ", buf[0]); count++; } printf(" count=%d\n", count); return 1; } static int consume_all_sources(struct burn_source *offsetters[], int count, int flag) { int i, ret; for (i = 0; i < count; i++) { printf("consume_source: idx=%d\n", i); ret = consume_source(offsetters[i], 0); if (ret <= 0) return ret; } return 1; } static int free_all_sources(struct burn_source *original, struct burn_source *offsetters[], int count, int flag) { int i; for (i = 0; i < count; i++) burn_source_free(offsetters[i]); burn_source_free(original); return 1; } int main(int argc, char **argv) { int ret; char *path = "./COPYRIGHT"; struct burn_source *original = NULL, *offsetters[4]; if (argc > 1) path = argv[1]; if (burn_initialize() == 0) exit(1); ret = create_original(&original, path, 0); if (ret <= 0) exit(2); ret = set_up_offst_sources(original, offsetters, 4, 0); if (ret <= 0) exit(3); ret = consume_all_sources(offsetters, 4, 0); if (ret <= 0) exit(4); ret = free_all_sources(original, offsetters, 4, 0); if (ret <= 0) exit(5); burn_finish(); exit(0); } /* * open-cd-excl.c --- This program tries to open a block device * by various exclusive and non-exclusive gestures in order to explore * their impact on running CD/DVD recordings. * * Copyright 2007, by Theodore Ts'o. * * Detail modifications 2007, by Thomas Schmitt. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #define _GNU_SOURCE /* for O_LARGEFILE *//*ts A70417: or _LARGEFILE64_SOURCE */ #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <getopt.h> const char *progname; static void usage(void) { fprintf(stderr, "Usage: %s [-feirw] device\n", progname); exit(1); } /* ts A70417: added parameter do_rdwr */ static void init_flock(struct flock *fl, int do_rdwr) { memset(fl, 0, sizeof(struct flock)); if (do_rdwr) fl->l_type = F_WRLCK; else fl->l_type = F_RDLCK; fl->l_whence = SEEK_SET; fl->l_start = 0; fl->l_len = 0; } int main(int argc, char **argv) { struct flock fl; char *device_name; int fd, c, f_opt = 0, do_rdwr = 0, end_immediately = 0; int flags = O_NONBLOCK|O_LARGEFILE; progname = argv[0]; /* ts A70417: added -w , -r , -i */ while ((c = getopt (argc, argv, "feirw")) != EOF) { switch (c) { case 'e': flags |= O_EXCL; break; case 'f': f_opt++; break; case 'i': end_immediately = 1; break; case 'r': do_rdwr = 0; break; case 'w': do_rdwr = 1; break; case '?': usage(); exit(1); } } if (optind == argc) usage(); device_name = argv[optind++]; /* ts A70417 : made read-write adjustable independently of f_opt */ if (do_rdwr) { flags |= O_RDWR; printf("Using O_RDWR\n"); } else { flags |= O_RDONLY; printf("Using O_RDONLY\n"); } if (flags & O_EXCL) printf("Trying to open %s with O_EXCL ...\n", device_name); fd = open(device_name, flags, 0); if (fd < 0) { perror("open"); printf("failed\n"); exit(1); } if (flags & O_EXCL) printf("succeeded\n"); if (f_opt) { init_flock(&fl, do_rdwr); if (fcntl(fd, F_GETLK, &fl) < 0) { perror("fcntl: F_GETLK: "); exit(1); } printf("fcntl lock apparently %sLOCKED\n", (fl.l_type == F_UNLCK) ? "NOT " : ""); init_flock(&fl, do_rdwr); printf("Trying to grab fcntl lock...\n"); if (fcntl(fd, F_SETLK, &fl) < 0) { perror("fcntl: F_SETLK: "); printf("failed\n"); exit(1); } printf("succeeded\n"); } /* ts A70417: added end_immediately */ printf("Holding %s open.\n", device_name); usleep(100000); if (end_immediately) exit(0); printf("Press ^C to exit.\n"); while (1) { sleep(300); } /* NOTREACHED */ return 0; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ #include "libburn/libburn.h" #include "libburn/toc.h" #include "libburn/mmc.h" #include <unistd.h> #include <stdio.h> #include <signal.h> #include <string.h> #include <assert.h> 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) { 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 (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; } memset(&newact, 0, sizeof(newact)); newact.sa_handler = catch_int; sigaction(SIGINT, &newact, &oldact); for (i = 0; i < (int) n_drives; i++) { NEXT=0; poll_drive(i); } sigaction(SIGINT, &oldact, NULL); burn_drive_info_free(drives); burn_finish(); return 0; } /* test/telltoc.c , API illustration of obtaining media status info */ /* Copyright (C) 2006 - 2016 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL */ /** Overview telltoc is a minimal demo application for the library libburn as provided on http://libburnia-project.org . It can list the available devices, can display some drive properties, the type of media, eventual table of content, multisession info for mkisofs option -C, and can read audio or data tracks. It's main purpose, nevertheless, is to show you how to use libburn and also to serve the libburn team as reference application. telltoc.c does indeed define the standard way how above gestures can be implemented and stay upward compatible for a good while. The burn aspects of libburn are demonstrated by program test/libburner.c . Before you can do anything, you have to initialize libburn by burn_initialize() as it is done in main() at the end of this file. Then you acquire a drive in an appropriate way conforming to the API. The two main approaches are shown here in application functions: telltoc_aquire_by_adr() demonstrates usage as of cdrecord traditions telltoc_aquire_by_driveno() demonstrates a scan-and-choose approach With that acquired drive you can call telltoc_media() prints some information about the media in a drive telltoc_toc() prints a table of content (if there is content) telltoc_msinfo() prints parameters for mkisofs option -C telltoc_read_and_print() reads from audio or data CD or from DVD or BD and prints 7-bit to stdout (encodings 0,2) or 8-bit to file (encoding 1) When everything is done, main() releases the drive and shuts down libburn: burn_drive_release(); burn_finish() */ /** See this for the decisive API specs . libburn.h is The Original */ /* For using the installed header file : #include <libburn/libburn.h> */ /* This program insists in the own headerfile. */ #include "../libburn/libburn.h" /* libburn is intended for Linux systems with kernel 2.4 or 2.6 for now */ #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <errno.h> #include <sys/stat.h> #include <fcntl.h> /** For simplicity i use global variables to represent the drives. Drives are systemwide global, so we do not give away much of good style. */ /** This list will hold the drives known to libburn. This might be all CD drives of the system and thus might impose severe impact on the system. */ static struct burn_drive_info *drive_list; /** If you start a long lasting operation with drive_count > 1 then you are not friendly to the users of other drives on those systems. Beware. */ static unsigned int drive_count; /** This variable indicates wether the drive is grabbed and must be finally released */ static int drive_is_grabbed = 0; /* Some in-advance definitions to make possible a more comprehensive ordering of the functions and their explanations in here */ int telltoc_aquire_by_adr(char *drive_adr); int telltoc_aquire_by_driveno(int *drive_no, int silent); /* Messages from --toc to --read_and_print (CD tracksize is a bit tricky) */ static int last_track_start = 0, last_track_size = -1; static int medium_is_cd_profile = 0; /* 0 = undecided , -1 = no , 1 = yes */ static int cd_is_audio = 0; /* 0 = undecided , -1 = no , 1 = yes */ /* ------------------------------- API gestures ---------------------------- */ /** You need to acquire a drive before burning. The API offers this as one compact call and alternatively as application controllable gestures of whitelisting, scanning for drives and finally grabbing one of them. If you have a persistent address of the drive, then the compact call is to prefer because it only touches one drive. On modern Linux kernels, there should be no fatal disturbance of ongoing burns of other libburn instances with any of our approaches. We use open(O_EXCL) by default. On /dev/hdX it should cooperate with growisofs and some cdrecord variants. On /dev/sgN versus /dev/scdM expect it not to respect other programs. */ int telltoc_aquire_drive(char *drive_adr, int *driveno, int silent_drive) { int ret; if(drive_adr != NULL && drive_adr[0] != 0) ret = telltoc_aquire_by_adr(drive_adr); else ret = telltoc_aquire_by_driveno(driveno, silent_drive); return ret; } /** If the persistent drive address is known, then this approach is much more un-obtrusive to the systemwide livestock of drives. Only the given drive device will be opened during this procedure. Special drive addresses stdio:<path> direct output to a hard disk file which will behave much like a DVD-RAM. */ int telltoc_aquire_by_adr(char *drive_adr) { int ret; char libburn_drive_adr[BURN_DRIVE_ADR_LEN]; /* This tries to resolve links or alternative device files */ ret = burn_drive_convert_fs_adr(drive_adr, libburn_drive_adr); if (ret<=0) { fprintf(stderr, "Address does not lead to a CD burner: '%s'\n", drive_adr); return 0; } fprintf(stderr,"Acquiring drive '%s' ...\n", libburn_drive_adr); ret = burn_drive_scan_and_grab(&drive_list, libburn_drive_adr, 1); if (ret <= 0) { fprintf(stderr,"FAILURE with persistent drive address '%s'\n", libburn_drive_adr); } else { fprintf(stderr,"Done\n"); drive_is_grabbed = 1; } return ret; } /** This method demonstrates how to use libburn without knowing a persistent drive address in advance. It has to make sure that after assessing the list of available drives, all unwanted drives get closed again. As long as they are open, no other libburn instance can see them. This is an intended locking feature. The application is responsible for giving up the locks by either burn_drive_release() (only after burn_drive_grab() !), burn_drive_info_forget(), burn_drive_info_free(), or burn_finish(). @param driveno the index number in libburn's drive list. This will get set to 0 on success and will then be the drive index to use in the further dourse of processing. @param silent_drive 1=do not print "Drive found :" line with *driveno >= 0 @return 1 success , <= 0 failure */ int telltoc_aquire_by_driveno(int *driveno, int silent_drive) { char adr[BURN_DRIVE_ADR_LEN]; int ret, i; fprintf(stderr, "Beginning to scan for devices ...\n"); while (!burn_drive_scan(&drive_list, &drive_count)) usleep(100002); if (drive_count <= 0 && *driveno >= 0) { fprintf(stderr, "FAILED (no drives found)\n"); return 0; } fprintf(stderr, "Done\n"); for (i = 0; i < (int) drive_count; i++) { if (*driveno >= 0 && (silent_drive || *driveno != i)) continue; if (burn_drive_get_adr(&(drive_list[i]), adr) <=0) strcpy(adr, "-get_adr_failed-"); printf("Drive found : %d --drive '%s' : ", i,adr); printf("%-8s %-16s (%4s)\n", drive_list[i].vendor,drive_list[i].product, drive_list[i].revision); } if (*driveno < 0) { fprintf(stderr, "Pseudo-drive \"-\" given : bus scanning done.\n"); return 2; /* the program will end after this */ } /* We already made our choice via command line. (default is 0) So we just have to keep our desired drive and drop all others. */ if ((int) drive_count <= *driveno) { fprintf(stderr, "Found only %d drives. Number %d not available.\n", drive_count, *driveno); return 0; /* the program will end after this */ } /* Drop all drives which we do not want to use */ for (i = 0; i < (int) drive_count; i++) { if (i == *driveno) /* the one drive we want to keep */ continue; ret = burn_drive_info_forget(&(drive_list[i]),0); if (ret != 1) fprintf(stderr, "Cannot drop drive %d. Please report \"ret=%d\" to libburn-hackers@pykix.org\n", i, ret); else fprintf(stderr, "Dropped unwanted drive %d\n",i); } /* Make the one we want ready for inquiry */ ret= burn_drive_grab(drive_list[*driveno].drive, 1); if (ret != 1) return 0; drive_is_grabbed = 1; return 1; } /** This gesture is necessary to get my NEC DVD_RW ND-4570A out of a state of noisy overexcitement after its tray was loaded and it then was inquired for Next Writeable Address. The noise then still lasts 20 seconds. Same with cdrecord -toc, btw. This opens a small gap for losing the drive to another libburn instance. Not a problem in telltoc. This is done as very last drive operation. Eventually the other libburn instance will have the same sanitizing effect. */ int telltoc_regrab(struct burn_drive *drive) { int ret; if (drive_is_grabbed) burn_drive_release(drive, 0); drive_is_grabbed = 0; ret = burn_drive_grab(drive, 0); if (ret != 0) { drive_is_grabbed = 1; } return !!ret; } int telltoc_media(struct burn_drive *drive) { int ret, media_found = 0, profile_no = -1, num_profiles = 0, i; int profiles[64]; char is_current_profile[64]; double max_speed = 0.0, min_speed = 0.0, speed_conv; off_t available = 0; enum burn_disc_status s; char profile_name[80], speed_unit[40]; struct burn_multi_caps *caps = NULL; struct burn_write_opts *o = NULL; printf("Media current: "); ret = burn_disc_get_profile(drive, &profile_no, profile_name); if (profile_no > 0 && ret > 0) { if (profile_name[0]) printf("%s\n", profile_name); else printf("%4.4Xh\n", profile_no); } else printf("is not recognizable\n"); /* Determine speed unit before profile_name gets reused */ speed_conv = 176.4; strcpy(speed_unit,"176.4 kB/s (CD, data speed 150 KiB/s)"); if (strstr(profile_name, "DVD") == profile_name) { speed_conv = 1385.0; strcpy(speed_unit,"1385.0 kB/s (DVD)"); } else if (strstr(profile_name, "BD") == profile_name) { speed_conv = 4495.625; strcpy(speed_unit,"4495.625 kB/s (BD)"); } ret = burn_drive_get_all_profiles(drive, &num_profiles, profiles, is_current_profile); if (ret > 0) { for (i = 0; i < num_profiles; i++) { ret = burn_obtain_profile_name(profiles[i], profile_name); if (ret <= 0) sprintf(profile_name, "Unknown media type 0x%4.4X", (unsigned int) profiles[i]); printf("Drive can do : %s%s\n", profile_name, is_current_profile[i] ? " (current)" : ""); } } printf("Media status : "); s = burn_disc_get_status(drive); if (s == BURN_DISC_FULL) { printf("is written , is closed\n"); media_found = 1; } else if (s == BURN_DISC_APPENDABLE) { printf("is written , is appendable\n"); media_found = 1; } else if (s == BURN_DISC_BLANK) { printf("is blank\n"); media_found = 1; } else if (s == BURN_DISC_EMPTY) printf("is not present\n"); else printf("is not recognizable\n"); printf("Media reuse : "); if (media_found) { if (burn_disc_erasable(drive)) printf("is erasable\n"); else printf("is not erasable\n"); } else printf("is not recognizable\n"); ret = burn_disc_get_multi_caps(drive, BURN_WRITE_NONE, &caps, 0); if (ret > 0) { /* Media appears writeable */ printf("Write multi : "); printf("%s multi-session , ", caps->multi_session == 1 ? "offers" : "cannot do"); if (caps->multi_track) printf("offers multiple tracks\n"); else printf("offers only single track\n"); printf("Write start : "); if (caps->start_adr == 1) printf( "offers addresses [%.f , %.f]s , alignment=%.fs\n", (double) caps->start_range_low / 2048 , (double) caps->start_range_high / 2048 , (double) caps->start_alignment / 2048 ); else printf("prohibits write start addressing\n"); printf("Write modes : "); if (caps->might_do_tao) printf("TAO%s", caps->advised_write_mode == BURN_WRITE_TAO ? " (advised)" : ""); if (caps->might_do_sao) printf("%sSAO%s", caps->might_do_tao ? " , " : "", caps->advised_write_mode == BURN_WRITE_SAO ? " (advised)" : ""); if (caps->might_do_raw) printf("%sRAW%s", caps->might_do_tao | caps->might_do_sao ? " , " : "", caps->advised_write_mode == BURN_WRITE_RAW ? " (advised)" : ""); printf("\n"); printf("Write dummy : "); if (caps->might_simulate) printf("supposed to work with non-RAW modes\n"); else printf("will not work\n"); o= burn_write_opts_new(drive); if (o != NULL) { burn_write_opts_set_perform_opc(o, 0); if(caps->advised_write_mode == BURN_WRITE_TAO) burn_write_opts_set_write_type(o, BURN_WRITE_TAO, BURN_BLOCK_MODE1); else if (caps->advised_write_mode == BURN_WRITE_SAO) burn_write_opts_set_write_type(o, BURN_WRITE_SAO, BURN_BLOCK_SAO); else { burn_write_opts_free(o); o = NULL; } } available = burn_disc_available_space(drive, o); printf("Write space : %.1f MiB (%.fs)\n", ((double) available) / 1024.0 / 1024.0, ((double) available) / 2048.0); burn_disc_free_multi_caps(&caps); if (o != NULL) burn_write_opts_free(o); } ret = burn_drive_get_write_speed(drive); max_speed = ((double ) ret) / speed_conv; ret = burn_drive_get_min_write_speed(drive); min_speed = ((double ) ret) / speed_conv; if (!media_found) printf("Drive speed : max=%.1f , min=%.1f\n", max_speed, min_speed); else printf("Avail. speed : max=%.1f , min=%.1f\n", max_speed, min_speed); ret = 0; if (media_found) ret = burn_disc_read_atip(drive); if(ret>0) { ret = burn_drive_get_min_write_speed(drive); min_speed = ((double ) ret) / speed_conv; ret = burn_drive_get_write_speed(drive); max_speed = ((double ) ret) / speed_conv; printf("Media speed : max=%.1f , min=%.1f\n", max_speed, min_speed); } printf("Speed unit 1x: %s\n", speed_unit); if (caps != NULL) burn_disc_free_multi_caps(&caps); return 1; } int telltoc_speedlist(struct burn_drive *drive) { int ret, has_modern_entries = 0; struct burn_speed_descriptor *speed_list, *sd; ret = burn_drive_get_speedlist(drive, &speed_list); if (ret <= 0) { fprintf(stderr, "SORRY: Cannot obtain speed list info\n"); return 2; } for (sd = speed_list; sd != NULL; sd = sd->next) if (sd->source == 2) has_modern_entries = 1; for (sd = speed_list; sd != NULL; sd = sd->next) { if (has_modern_entries && sd->source < 2) continue; if (sd->write_speed <= 0) continue; printf("Speed descr. : %d kB/s", sd->write_speed); if (sd->end_lba >= 0) printf(", %.1f MiB", ((double) sd->end_lba) / 512.0); if (sd->profile_name[0]) printf(", %s", sd->profile_name); printf("\n"); } burn_drive_free_speedlist(&speed_list); return 1; } int telltoc_formatlist(struct burn_drive *drive) { int ret, i, status, num_formats, profile_no, type; off_t size; unsigned dummy; char status_text[80], profile_name[90]; ret = burn_disc_get_formats(drive, &status, &size, &dummy, &num_formats); if (ret <= 0) { fprintf(stderr, "SORRY: Cannot obtain format list info\n"); return 2; } if (status == BURN_FORMAT_IS_UNFORMATTED) sprintf(status_text, "unformatted, up to %.1f MiB", ((double) size) / 1024.0 / 1024.0); else if(status == BURN_FORMAT_IS_FORMATTED) sprintf(status_text, "formatted, with %.1f MiB", ((double) size) / 1024.0 / 1024.0); else if(status == BURN_FORMAT_IS_UNKNOWN) { burn_disc_get_profile(drive, &profile_no, profile_name); if (profile_no > 0) sprintf(status_text, "intermediate or unknown"); else sprintf(status_text, "no media or unknown media"); } else sprintf(status_text, "illegal status according to MMC-5"); printf("Format status: %s\n", status_text); for (i = 0; i < num_formats; i++) { ret = burn_disc_get_format_descr(drive, i, &type, &size, &dummy); if (ret <= 0) continue; printf("Format descr.: %2.2Xh , %.1f MiB (%.fs)\n", type, ((double) size) / 1024.0 / 1024.0, ((double) size) / 2048.0); } return 1; } void telltoc_detect_cd(struct burn_drive *drive) { int pno; char profile_name[80]; if (burn_disc_get_profile(drive, &pno, profile_name) > 0) { if (pno >= 0x08 && pno <= 0x0a) medium_is_cd_profile = 1; else medium_is_cd_profile = -1; } } int telltoc_toc(struct burn_drive *drive) { int num_sessions = 0 , num_tracks = 0 , lba = 0, pmin, psec, pframe; int track_count = 0, track_is_audio; int session_no, track_no; struct burn_disc *disc= NULL; struct burn_session **sessions; struct burn_track **tracks; struct burn_toc_entry toc_entry; disc = burn_drive_get_disc(drive); if (disc==NULL) { fprintf(stderr, "SORRY: Cannot obtain Table Of Content\n"); return 2; } sessions = burn_disc_get_sessions(disc, &num_sessions); for (session_no = 0; session_no<num_sessions; session_no++) { tracks = burn_session_get_tracks(sessions[session_no], &num_tracks); if (tracks==NULL) continue; for(track_no= 0; track_no<num_tracks; track_no++) { track_count++; burn_track_get_entry(tracks[track_no], &toc_entry); if (toc_entry.extensions_valid & 1) { /* DVD extension valid */ lba = toc_entry.start_lba; burn_lba_to_msf(lba, &pmin, &psec, &pframe); } else { pmin = toc_entry.pmin; psec = toc_entry.psec; pframe = toc_entry.pframe; lba= burn_msf_to_lba(pmin, psec, pframe); } if ((toc_entry.control & 7) < 4) { if (cd_is_audio == 0) cd_is_audio = 1; track_is_audio = 1; } else { track_is_audio = 0; cd_is_audio = -1; } printf("Media content: session %3d ", session_no+1); printf("track %3d %s lba: %9d %4.2d:%2.2d:%2.2d\n", track_count, (track_is_audio ? "audio" : "data "), lba, pmin, psec, pframe); last_track_start = lba; } burn_session_get_leadout_entry(sessions[session_no], &toc_entry); if (toc_entry.extensions_valid & 1) { lba = toc_entry.start_lba; burn_lba_to_msf(lba, &pmin, &psec, &pframe); } else { pmin = toc_entry.pmin; psec = toc_entry.psec; pframe = toc_entry.pframe; lba= burn_msf_to_lba(pmin, psec, pframe); } printf("Media content: session %3d ", session_no+1); printf("leadout lba: %9d %4.2d:%2.2d:%2.2d\n", lba, pmin, psec, pframe); last_track_size = lba - last_track_start; telltoc_detect_cd(drive); } if (disc!=NULL) burn_disc_free(disc); return 1; } int telltoc_msinfo(struct burn_drive *drive, int msinfo_explicit, int msinfo_alone) { int ret, lba, nwa = -123456789, aux_lba; enum burn_disc_status s; struct burn_write_opts *o= NULL; s = burn_disc_get_status(drive); if (s!=BURN_DISC_APPENDABLE) { if (!msinfo_explicit) return 2; fprintf(stderr, "SORRY: --msinfo can only operate on appendable media.\n"); return 0; } /* man mkisofs , option -C : The first number is the sector number of the first sector in the last session of the disk that should be appended to. */ ret = burn_disc_get_msc1(drive, &lba); if (ret <= 0) { fprintf(stderr, "SORRY: Cannot obtain start address of last session\n"); { ret = 0; goto ex; } } /* man mkisofs , option -C : The second number is the starting sector number of the new session. */ /* Set some roughly suitable write opts to be sent to drive. */ o= burn_write_opts_new(drive); if(o!=NULL) { burn_write_opts_set_perform_opc(o, 0); burn_write_opts_set_write_type(o, BURN_WRITE_TAO, BURN_BLOCK_MODE1); } /* Now try to inquire nwa from drive */ ret= burn_disc_track_lba_nwa(drive,o,0,&aux_lba,&nwa); telltoc_regrab(drive); /* necessary to calm down my NEC drive */ if(ret<=0) { fprintf(stderr, "SORRY: Cannot obtain next writeable address\n"); { ret = 0; goto ex; } } if (!msinfo_alone) printf("Media msinfo : mkisofs ... -C "); printf("%d,%d\n",lba,nwa); ret = 1; ex:; if (o != NULL) burn_write_opts_free(o); return ret; } /** @param encoding determins how to format output on stdout: 0 = default , 1 = raw 8 bit (dangerous for tty) , 2 = hex */ int telltoc_read_and_print(struct burn_drive *drive, int start_sector, int sector_count, char *raw_file, int encoding) { int j, i, request = 16, done, lbas = 0, final_cd_try = -1, todo; int ret = 0, sector_size, chunk_size, read_audio = 0; char buf[16 * 2048], line[81]; off_t data_count, total_count= 0, last_reported_count= 0; struct stat stbuf; FILE *raw_fp = NULL; if (medium_is_cd_profile == 0) telltoc_detect_cd(drive); if (start_sector == -1) start_sector = last_track_start; if (sector_count == -1) { sector_count = last_track_start + last_track_size - start_sector; if (medium_is_cd_profile > 0) /* In case it is a TAO track */ final_cd_try = 0; /* allow it (-1 is denial) */ } if (sector_count <= 0) sector_count = 2147483632; if (encoding == 1) { if (stat(raw_file,&stbuf) != -1) { if (!(S_ISCHR(stbuf.st_mode) || S_ISFIFO(stbuf.st_mode) || (stbuf.st_mode & S_IFMT) == S_IFSOCK )) { fprintf(stderr, "SORRY: target file '%s' already existing\n", raw_file); return 1; } } raw_fp = fopen(raw_file,"w"); if (raw_fp == NULL) { fprintf(stderr,"SORRY: cannot open target file '%s' (%s)\n", raw_file, strerror(errno)); return 1; } printf( "Data : start=%ds , count=%ds , read=0s , encoding=%d:'%s'\n", start_sector, sector_count, encoding, raw_file); } else printf( "Data : start=%ds , count=%ds , read=0 , encoding=%d\n", start_sector, sector_count, encoding); /* Whether to read audio or data */ if (cd_is_audio > 0) { read_audio = 1; } else if (medium_is_cd_profile > 0 && cd_is_audio == 0) { /* Try whether the start sector is audio */ ret = burn_read_audio(drive, start_sector, buf, (off_t) 2352, &data_count, 2 | 4); if (ret > 0) read_audio = 1; } if (read_audio) { sector_size = 2352; chunk_size = 12; } else { sector_size = 2048; chunk_size = 16; if (start_sector < 0) start_sector = 0; } todo = sector_count - 2*(final_cd_try > -1); for (done = 0; done < todo && final_cd_try != 1; done += request) { if (todo - done > chunk_size) request = chunk_size; else request = todo - done; if (read_audio) { ret = burn_read_audio(drive, start_sector + done, buf, (off_t) (request * sector_size), &data_count, 0); } else { ret = burn_read_data(drive, ((off_t) start_sector + done) * (off_t) sector_size, buf, (off_t) (request * sector_size), &data_count, 1); } print_result:; total_count += data_count; if (encoding == 1) { if (data_count > 0) { ret = fwrite(buf, data_count, 1, raw_fp); if (ret < 1) { fprintf(stderr, "FAILURE: writing to '%s' : %s\n", raw_file, strerror(errno)); fclose(raw_fp); return 1; } } } else for (i = 0; i < data_count; i += 16) { if (encoding == 0) { sprintf(line, "%8ds + %4d : ", start_sector + done + i / sector_size, i % sector_size); lbas = strlen(line); } for (j = 0; j < 16 && i + j < data_count; j++) { if (buf[i + j] >= ' ' && buf[i + j] <= 126 && encoding != 2) sprintf(line + lbas + 3 * j, " %c ", (int) buf[i + j]); else sprintf(line + lbas + 3 * j, "%2.2X ", (unsigned char) buf[i + j]); } line[lbas + 3 * (j - 1) + 2] = 0; printf("%s\n",line); } if (encoding == 1 && total_count - last_reported_count >= 1000 * sector_size) { fprintf(stderr, "\rReading data : start=%ds , count=%ds , read=%ds ", start_sector, sector_count, (int) (total_count / (off_t) sector_size)); last_reported_count = total_count; } if (ret <= 0) { fprintf(stderr, "SORRY : Reading failed.\n"); break; } } if (ret > 0 && medium_is_cd_profile > 0 && final_cd_try == 0) { /* In a SAO track the last 2 frames should be data too */ final_cd_try = 1; if (read_audio) { ret = burn_read_audio(drive, start_sector + todo, buf, (off_t) (2 * sector_size), &data_count, 2); } else { burn_read_data(drive, ((off_t) start_sector + todo) * (off_t) sector_size, buf, (off_t) (2 * sector_size), &data_count, 2); } if (data_count < 2 * sector_size) fprintf(stderr, "\rNOTE : Last two frames of CD track unreadable. This is normal if TAO track.\n"); if (data_count > 0) goto print_result; } if (last_reported_count > 0) fprintf(stderr, "\r \r"); printf("End Of Data : start=%ds , count=%ds , read=%ds\n", start_sector, sector_count, (int) (total_count / (off_t) sector_size)); if (raw_fp != NULL) fclose(raw_fp); return ret; } /** The setup parameters of telltoc */ static char drive_adr[BURN_DRIVE_ADR_LEN] = {""}; static int driveno = 0; static int do_media = 0; static int do_toc = 0; static int do_msinfo = 0; static int print_help = 0; static int do_capacities = 0; static int read_start = -2, read_count = -2, print_encoding = 0; static char print_raw_file[4096] = {""}; /** Converts command line arguments into above setup parameters. drive_adr[] must provide at least BURN_DRIVE_ADR_LEN bytes. source_adr[] must provide at least 4096 bytes. */ int telltoc_setup(int argc, char **argv) { int i; for (i = 1; i < argc; ++i) { if (strlen(argv[i]) >= 4096) { fprintf(stderr, "Argument at position %d is much too long. (Max 4095)\n", i); return 2; } } for (i = 1; i < argc; ++i) { if (!strcmp(argv[i], "--drive")) { ++i; if (i >= argc) { fprintf(stderr,"--drive requires an argument\n"); return 1; } else if (strcmp(argv[i], "-") == 0) { drive_adr[0] = 0; driveno = -1; } else if (isdigit(argv[i][0])) { drive_adr[0] = 0; driveno = atoi(argv[i]); } else { if(strlen(argv[i]) >= BURN_DRIVE_ADR_LEN) { fprintf(stderr,"--drive address too long (max. %d)\n", BURN_DRIVE_ADR_LEN-1); return 2; } strcpy(drive_adr, argv[i]); } } else if (strcmp(argv[i],"--media")==0) { do_media = 1; } else if (!strcmp(argv[i], "--msinfo")) { do_msinfo = 1; } else if (!strcmp(argv[i], "--capacities")) { do_capacities = 1; } else if (!strcmp(argv[i], "--toc")) { do_toc = 1; } else if (!strcmp(argv[i], "--read_and_print")) { i+= 3; if (i >= argc) { fprintf(stderr,"--read_and_print requires three arguments: start count encoding(try 0, not 1)\n"); return 1; } sscanf(argv[i-2], "%d", &read_start); sscanf(argv[i-1], "%d", &read_count); print_encoding = 0; if(strncmp(argv[i], "raw:", 4) == 0 || strncmp(argv[i], "1:", 2) == 0) { print_encoding = 1; strcpy(print_raw_file, strchr(argv[i], ':') + 1); if (strcmp(print_raw_file, "-") == 0) { fprintf(stderr, "--read_and_print does not write to \"-\" as stdout.\n"); return 1; } } else if(strcmp(argv[i], "hex") == 0 || strcmp(argv[i], "2") == 0) print_encoding = 2; } else if (!strcmp(argv[i], "--help")) { print_help = 1; } else { fprintf(stderr, "Unidentified option: %s\n", argv[i]); return 7; } } if (argc==1) print_help = 1; if (print_help) { printf("Usage: %s\n", argv[0]); printf(" [--drive <address>|<driveno>|\"-\"]\n"); printf(" [--media] [--capacities] [--toc] [--msinfo]\n"); printf(" [--read_and_print <start> <count> \"0\"|\"hex\"|\"raw\":<path>]\n"); printf("Examples\n"); printf("A bus scan (needs rw-permissions to see a drive):\n"); printf(" %s --drive -\n",argv[0]); printf("Obtain info about the type of loaded media:\n"); printf(" %s --drive /dev/hdc --media\n",argv[0]); printf("Obtain table of content:\n"); printf(" %s --drive /dev/hdc --toc\n",argv[0]); printf("Obtain parameters for option -C of program mkisofs:\n"); printf(" msinfo=$(%s --drive /dev/hdc --msinfo 2>/dev/null)\n", argv[0]); printf(" mkisofs ... -C \"$msinfo\" ...\n"); printf("Obtain what is available about drive 0 and its media\n"); printf(" %s --drive 0\n",argv[0]); printf("View blocks 16 to 19 of audio or data CD or DVD or BD in human readable form\n"); printf(" %s --drive /dev/sr1 --read_and_print 16 4 0 | less\n", argv[0]); printf("Copy last track from CD to file /tmp/data\n"); printf(" %s --drive /dev/sr1 --toc --read_and_print -1 -1 raw:/tmp/data\n", argv[0]); } return 0; } int main(int argc, char **argv) { int ret, toc_failed = 0, msinfo_alone = 0, msinfo_explicit = 0; int full_default = 0; ret = telltoc_setup(argc, argv); if (ret) exit(ret); /* Behavior shall be different if --msinfo is only option */ if (do_msinfo) { msinfo_explicit = 1; if (!(do_media || do_toc)) msinfo_alone = 1; } /* Default option is to do everything if possible */ if (do_media==0 && do_msinfo==0 && do_capacities==0 && do_toc==0 && (read_start < 0 || read_count <= 0) && driveno!=-1) { if(print_help) exit(0); full_default = do_media = do_msinfo = do_capacities= do_toc= 1; } fprintf(stderr, "Initializing libburnia-project.org ...\n"); if (burn_initialize()) fprintf(stderr, "Done\n"); else { fprintf(stderr,"\nFATAL: Failed to initialize.\n"); exit(33); } /* Print messages of severity WARNING or more directly to stderr */ burn_msgs_set_severities("NEVER", "WARNING", "telltoc : "); /* Activate the default signal handler */ burn_set_signal_handling("telltoc : ", NULL, 0); /** Note: driveno might change its value in this call */ ret = telltoc_aquire_drive(drive_adr, &driveno, !full_default); if (ret<=0) { fprintf(stderr,"\nFATAL: Failed to aquire drive.\n"); { ret = 34; goto finish_libburn; } } if (ret == 2) { ret = 0; goto release_drive; } if (do_media) { ret = telltoc_media(drive_list[driveno].drive); if (ret<=0) {ret = 36; goto release_drive; } } if (do_capacities) { ret = telltoc_speedlist(drive_list[driveno].drive); if (ret<=0) {ret = 39; goto release_drive; } ret = telltoc_formatlist(drive_list[driveno].drive); if (ret<=0) {ret = 39; goto release_drive; } } if (do_toc) { ret = telltoc_toc(drive_list[driveno].drive); if (ret<=0) {ret = 37; goto release_drive; } if (ret==2) toc_failed = 1; } if (do_msinfo) { ret = telltoc_msinfo(drive_list[driveno].drive, msinfo_explicit, msinfo_alone); if (ret<=0) {ret = 38; goto release_drive; } } if (read_start != -2 && (read_count > 0 || read_count == -1)) { ret = telltoc_read_and_print(drive_list[driveno].drive, read_start, read_count, print_raw_file, print_encoding); if (ret<=0) {ret = 40; goto release_drive; } } ret = 0; if (toc_failed) ret = 37; release_drive:; if (drive_is_grabbed) burn_drive_release(drive_list[driveno].drive, 0); finish_libburn:; /* This app does not bother to know about exact scan state. Better to accept a memory leak here. We are done anyway. */ /* burn_drive_info_free(drive_list); */ burn_finish(); exit(ret); } /* License and copyright aspects: See test/libburner.c */ #define BURN_MAJOR_VERSION @BURN_MAJOR_VERSION@ #define BURN_MINOR_VERSION @BURN_MINOR_VERSION@ #define BURN_MICRO_VERSION @BURN_MICRO_VERSION@ Thomas Schmitt Vreixo Formoso Lopes Nio Wiklund alias sudodus 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 Mario Danic <mario.danic@gmail.com>, Vreixo Formoso <metalpain2002@yahoo.es> Thomas Schmitt <scdbackup@gmx.net> libisoburn is Copyright (C) 2007-2017 Vreixo Formoso, Thomas Schmitt xorriso is Copyright (C) 2007-2017 Thomas Schmitt This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later as published by the Free Software Foundation. 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 git clone git@dev.lovelyhq.com:libburnia/libisoburn.git (to become libisoburn-1.5.8 or higher) =============================================================================== * Bug fix: -boot_image and -append_partition were not perceived as image manipulation which makes production of an ISO image worthwhile. Thanks Cameron Seader. * Bug fix: -outdev holding an ISO filesystem could get attached wrong start LBA * Bug fix: Command -load "volid" did not work with constant search text * Bug fix: Command -truncate_overwritable on ISO image without MD5 caused double free of memory * Bug fix: -boot_image "any" "replay" failed after the legacy BIOS boot image file was replaced by -map. Thanks Brian C. Lane. * Bug fix: -boot_image system_area=/dev/zero preserved system area of loaded ISO * Bug fix: Size assessment of ISO images smaller than 32 KiB yielded random values * New bit1 with API call Xorriso_change_is_pending() issues a note if return is 0 and indev and outdev point to different drives * New API calls isoburn_toc_disc_get_sectors_v2, isoburn_toc_session_get_sectors_v2, isoburn_toc_track_get_emul_v2 * New API calls isoburn_read_iso_head_v2(), isoburn_get_mount_params_v2() * New API calls isoburn_igopt_get_effective_lba_v2(), isoburn_igopt_get_data_start_v2() * New API call isoburn_get_attached_start_lba_v2() * New API calls isoburn_attach_start_lba_v2(), isoburn_disc_get_msc1_v2(), isoburn_disc_track_lba_nwa_v2() * New API call isoburn_prepare_blind_grow_v2() * New info return modes 3 "Creation Time" and 4 "Modification Time" with isoburn_read_iso_head() and isoburn_read_iso_head_v2() * New address modes 5 to 9 for isoburn_set_msc1(), isoburn_get_mount_params(), isoburn_get_mount_params_v2() * New -append_partition pseudo partition_number "all" and pseudo type_code "revoke" * New -as mkisofs options -cut_out and -hide_iso_path * Improved handling of hidden boot images in -boot_image cmd/as_mkisofs/replay * The maximum number of appended GPT partitions was increased from 4 to 8 * New command -toc_info_type * New entities "at_time", "before", "after", "not_after", "not_before" for commands -load, -mount, -mount_cmd, -session_string, -truncate_overwritable GNU xorriso-1.5.6.pl02.tar.gz Wed Jun 14 2023 =============================================================================== * Bug fix: On non-GNU/Linux systems ssize_t was not defined in libisofs file rockridge.h . Report and fix proposal by Rui Chen. GNU xorriso-1.5.6.pl01.tar.gz Mon Jun 12 2023 =============================================================================== (A failed attempt to create a tarball with a fixed libisofs/rockridge.h) libisoburn-1.5.6.tar.gz Wed Jun 07 2023 =============================================================================== * Bug fix: False -status failure with -boot_image --interval:appended_partition * Bug fix: -no_rc prevented pre-scanning of arguments for stdio output and others. Introduced by mistake in a62f6af5, 2011.10.18.162119. * Bug fix: -not_leaf and -not_paths were not applied to -extract and alike * Bug fix: -report_system_area cmd misperceived -part_like_isohybrid with -isohybrid-gpt-basdat * Bug fix: -report_system_area cmd misperceived combination of isohybrid and appended partition in GPT * Bug fix: -as mkisofs option -part_like_isohybrid did not cause a MBR partition table if the partitions are data files in the ISO rather than appended * Bug fix: Split file directories (-split_size) were created with wrong permissions * Bug fix: libisofs did not mark clones of imported files as imported. This could cause that original and clone occupy data storage in the newly written session. Thanks to Ivan Shmakov. * Bug fix: Partition offset was preserved from -indev rather than from -outdev * Bug fix: libisofs could misrepresent Rock Ridge information if many symbolic links or AAIP data were recorded in a directory * Bug fix: Data files named /boot.catalog or ./boot.cat could be left out of the emerging ISO if the boot catalog was set to be hidden * Bug fix: -toc reported wrong track LBA with overwritable media with unrecognized content (pseudo-closed) * Bug fix: -find test -has_xattr matched "isofs." attributes in -xattr mode "any" * New API call isoburn_assess_written_features() * New API calls isoburn_igopt_set_max_ce_entries(), isoburn_igopt_get_max_ce_entries() * New flag bit12 with isoburn_read_iso_head(): Read even if start of multi-session emulation is damaged * New -boot_image settings gpt_iso_bootable= and gpt_iso_not_ro= * New -as mkisofs options --gpt-iso-bootable and --gpt-iso-not-ro * New -as cdrecord option --obs_pad. Automatic no_emul_toc with -as cdrecord. * New parameters "obs_pad" and "bdr_obs_exempt" for -dvd_obs * New -as cdrecord option --bdr_obs_exempt * New command -assess_indev_features * New -find test -size * New -compliance rules max_ce_entries=, max_ce_drop= * Allowed lseekable device files with -cut_out. Proof-of-concept by Ivan Shmakov on bugs.debian.org. (Closes: #1010098) libisoburn-1.5.4.tar.gz Sat Jan 30 2021 =============================================================================== * Bug fix: -report_system_area as_mkisofs misrepresented GPT with appended partition and forced boot flag as -part_like_isohybrid * Bug fix: Boot catalog could get a wrong name if cat_path= is explicitely given but not containing a slash character * New helper script xorriso-dd-target * New command -truncate_overwritable * Switched to usage of libjte-2.0.0 * New -jigdo parameters "checksum_algorithm", "demand_checksum", "-checksum-list" * New -as mkisofs options "-jigdo-checksum-algorithm", "-checksum-list", "-jigdo-force-checksum" * New -read_speed prefixes "soft_force:" and "soft_corr:" * New -check_media option data_to="-" for standard output * New -zisofs parameters version_2=, block_size_v2=, max_bpt=, max_bpt_f=, bpt_target=, bpt_free_ratio=, by_magic=v2, susp_z2= * New -as mkisofs options --zisofs-version-2, --zisofs2-susp-z2, --zisofs2-susp-zf * Enabled recognition of zisofs by magic without zlib support * New -osirrox option sparse= controls extraction into sparse files * New libisoburn extension options isoburn_ropt_map_joliet_stripped and isoburn_ropt_map_joliet_unmapped * New command -joliet_map * New command -extract_boot_images * New API call isoburn_ropt_get_tree_loaded() libisoburn-1.5.2.tar.gz Sat Oct 26 2019 =============================================================================== * Bug fix: -disk_pattern on -add ./ -- mistook "./" for the root directory Thanks JBThiel. * Bug fix: -disk_pattern on -add "" -- yielded SIGSEGV * Bug fix: -osirrox "concat_split_on" worked only together with -split_size larger than 0. Thanks William Willems. * New command -drive_access, new -as cdrecord option --drive_not_exclusive * New API calls isoburn_igopt_set_part_type_guid(), isoburn_igopt_get_part_type_guid(), isoburn_igopt_set_iso_type_guid(), isoburn_igopt_get_iso_type_guid() * Enabled GPT type GUIDs with -append_partition, -boot_image any iso_mbr_part_type=, and -as mkisofs -iso_mbr_part_type * Made libisoburn and GNU xorriso ready for building out-of-source. Thanks Ross Burton. libisoburn-1.5.0.tar.gz Sat Sep 15 2018 =============================================================================== * Bug fix: Multi-session emulation was not recognized with non-zero partition offset * New bit10 of isoburn_drive_aquire() to accept all xattr namespaces * New -xattr mode "any" to process all xattr namespaces of local filesystem * New -as mkisofs option --xattr-any * New -as mkisofs options -uid and -gid libisoburn-1.4.8.tar.gz Tue Sep 12 2017 =============================================================================== * Bug fix: -as mkisofs -no-emul-boot without -boot-load-size defaulted to size 4, instead of full boot image size * Bug fix: -read_fs "norock" did not prevent reading of root Rock Ridge info * Bug fix: Mix of absolute and relative disk paths could cause SIGSEGV with pattern expansion * Bug fix: --mbr-force-bootable did not get into effect with -append_partition * Bug fix: Exit value of failed -mount command was reported as 0 * Bug fix: -boot_image actions "keep" and "patch" did not work any more. Regression by libisofs 1.4.4. * New -find tests -maxdepth and -mindepth * New commands -update_lxi and -update_li * New API calls isoburn_igopt_set_iso_mbr_part_type(), isoburn_igopt_get_iso_mbr_part_type() * New -boot_image bootspec iso_mbr_part_type= * New -as mkisofs option -iso_mbr_part_type * New -as mkisofs option -eltorito-platform * Properly refusing on Pseudo Overwritable formatted BD-R libisoburn-1.4.6.tar.gz Fri Sep 16 2016 =============================================================================== * Bug fix: The default setting of -compliance did not apply rec_mtime to Joliet and ISO:1999. mkisofs emulation was not affected by this bug. * Bug fix: -file_size_limit did not increase ISO level if necessary. Thanks to Mattias Schlenker. * New API calls isoburn_igopt_set_gpt_guid(), isoburn_igopt_get_gpt_guid() * New command -use_immed_bit, new -as cdrecord option use_immed_bit= * New -volume_date mode "all_file_dates" * New -as mkisofs option --set_all_file_dates * New bootspec "gpt_disk_guid=", new -as mkisofs option --gpt_disk_guid * New -report_system_area modes "gpt_disk_guid", "make_guid" * New environment variable SOURCE_DATE_EPOCH * New -find action "set_to_mtime" libisoburn-1.4.4.tar.gz Fri Jul 01 2016 =============================================================================== * Bug fix: -as mkisofs did not unescape "\=" in the source part of pathspecs * Bug fix: -boot_image "any" "system_area=/dev/zero" did not zeroize loaded data * Bug fix: -pathspecs "on" did not properly handle "\\=" * Bug fix: HFS+ production could cause MBR partition of type 0xEE without GPT * Bug fix: HFS+ directories could announce more children than they actually have * Bug fix: The HFS+ filesystem was not marked by in GPT of GRUB2 hybrid layout * Bug fix: When reading an ISO filesystem, the presence of --protective-msdos-label was not recognized if a partition is appended * Bug fix: xorrisofs option --protective-msdos-label did not work if no system area data were given by option -G or alike * Bug fix: -modesty_on_drive properties timeout_sec, min_usec, max_usec read wrong numbers from the parameter text * Letting -as mkisofs --norock revoke the special effect of -r * Letting -blank on overwritable media invalidate UDF extended descriptors * New -pathspecs mode "as_mkisofs" * New -boot_image setting mbr_force_bootable=, -as mkisofs --mbr-force-bootable * New -boot_image bootspecs appended_part_as=apm, part_like_isohybrid=on * New -as mkisofs options -appended_part_as_apm, -part_like_isohybrid * New command -scsi_dev_family, new -as cdrecord option drive_scsi_dev_family= libisoburn-1.4.2.tar.gz Sat Nov 28 2015 =============================================================================== * Bug fix: -backslash_codes "on" did not work outside quotes and with showing "\r" * New API calls isoburn_ropt_set_truncate_mode() isoburn_ropt_get_truncate_mode() * New options with isoburn_ropt_set_extensions(): isoburn_ropt_map_* * New command -modesty_on_drive, new -as cdrecord -immed, minbuf=, modesty_on_drive= * New command -ecma119_map * New command -read_fs * New -boot_image action "replay" * New command -file_name_limit, -as mkisofs -file_name_limit * New -find test -name_limit_blocker. * Result of a Coverity audit: 50+ code changes, but no easy-to-trigger bugs libisoburn-1.4.0.tar.gz Sun May 17 2015 =============================================================================== * Bug fix: -dev or -indev of medium with non-ISO data caused SIGSEGV by NULL * New API calls isoburn_igopt_set_appended_as_gpt(), isoburn_igopt_get_appended_as_gpt() * New API call isoburn_igopt_set_part_flag() and libisofs interval reader flags * New -find action "show_stream_id" * Optional libisofs interval reader with -append_partition and System Area * New -boot_image bootspec "appended_part_as=", new -as mkisofs option -appended_part_as_gpt * New -report_system_area formats "cmd" and "as_mkisofs" libisoburn-1.3.8.tar.gz Sat Jun 28 2014 =============================================================================== * Bug fix: -boot_image grub grub2_mbr= did not work (but -as mkisofs --grub2-mbr did work) * Bug fix: -boot_image grub2_mbr= prevented -boot_image partition_table=on * Bug fix: libburn: A final fsync(2) was performed with stdio drives, even if -stdio_sync was set to "off". * Bug fix: libburn: Wrong stack usage caused SIGBUS on sparc when compiled by gcc -O2 * Bug fix: -blank force:all on DVD+RW had no effect * Enabled use of libedit as alternative to libreadline * Enabled recording and restoring of extattr on NetBSD * New API calls isoburn_igopt_set_stdio_endsync() and isoburn_igopt_get_stdio_endsync * New bootspecs hppa_*, new -as mkisofs options -hppa-* for HP-PA via PALO * New -find pseudo tests -use_pattern , -or_use_pattern * New -find action report_sections * New command -concat * New commands -report_system_area and -report_el_torito libisoburn-1.3.6.pl01.tar.gz Tue Mar 18 2014 =============================================================================== * Bug fix: Command -status produced FAILURE event if no drive was acquired Regression introduced by release 1.3.6 * Bug fix: libburn : Compilation warning for unsupported systems mutated into an error. Regression introduced by release 1.3.6. * Bug fix: libburn : CD TAO with add-on sessions could cause a buffer overrun. Introduced 7 years ago by release 0.3.2. libisoburn-1.3.6.tar.gz Tue Mar 04 2014 =============================================================================== * Bug fix: libisofs: Division by zero if HFS+ was combined with TOC emulation for overwritable media. * Bug fix: -list_speeds did not work any more with old CD drives. Regression introduced by release 1.3.4 * Bug fix: -check_media marked untested sectors in sector map as valid * Bug fix: Paths with symbolic links preceding ".." were not interpreted properly * New isoburn_igopt_set_relaxed() relaxation isoburn_igopt_joliet_utf16 * New -compliance rule joliet_utf16, new -as mkisofs option -joliet-utf16 * New -find test -bad_outname, new -find action print_outname * New API call isoburn_conv_name_chars() * libburn: New system adapter for NetBSD libisoburn-1.3.4.tar.gz Thu Dec 12 2013 =============================================================================== * Bug fix: Command -blank "as_needed" formatted blank BD-R. * Bug fix: -as mkisofs option -log-file put the log file into the image * Bug fix: -cut_out did not add x-permission to r-permission of directory * Bug fix: Command -zisofs did not accept all options emitted by -status -zisofs * Bug fix: -blank force:... failed on appendable or closed media * Bug fix: libburn: Drive LG BH16NS40 stalled on inspection of unformatted DVD+RW * libisofs: Default sort weight of El Torito boot images is now 2 * libisofs: Encoding HFS+ names in UTF-16 rather than UCS-2 * New command -read_speed * New -close mode "as_needed", new -as cdrecord option --multi_if_possible * New -alter_date types: a-c , m-c , b-c , c libisoburn-1.3.2.tar.gz Wed Aug 07 2013 =============================================================================== * Bug fix: -find -exec "sort_weight" did not mark the image as having pending changes * Bug fix: -backslash_codes "with_program_arguments" was interpreted too late * Bug fix: Missing or empty parameter with -dus was interpreted as "*" rather than "." * Bug fix: readline history was spammed by -msg_op parsing and pipe loops * Bug fix: xorriso aborted on SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU * Improved granularity of SCSI log time measurement, now with timestamp * New -pacifier behavior code "interval=" * New -as mkisofs options --sort-weight-list and --sort-weight-patterns * New -format mode "without_spare" (for BD-RE) * New command -named_pipe_loop * New command -sh_style_result * New -msg_op opcodes "parse_silently" and "parse_bulk_silently" * New command -application_use and new -as mkisofs option --application_use libisoburn-1.3.0.tar.gz Fri May 17 2013 =============================================================================== * Bug fix: Disk paths with components '.' or '..' could be mistaken for directories. * Bug fix: -as mkisofs -print-size failed with -isohybrid-mbr and a single boot image. Regression introduced by libisoburn-1.2.8. * Bug fix: -as mkisofs -path-list did not switch to --no-emul-toc by default. * Bug fix: Unspecified Expiration Time and Effective Time of ISO volume was represented by 0-bytes rather than ASCII '0' digits. * Bug fix: Reserved and unused fields of APM entries were not zeroed. * Bug fix: GPT header CRC was computed from all 512 bytes rather than from 92. * Bug fix: The protective MBR partition for GPT started at block 0 instead of 1. * New -boot_image bootspecs grub2_mbr= and grub2_boot_info= * New -boot_image bootspec grub2_sparc_core= * New -as mkisofs options --grub2-mbr , --grub2-boot-info , --grub2-sparc-core * New -hardlinks mode "lsl_count" / "no_lsl_count" libisoburn-1.2.8.tar.gz Mon Mar 18 2013 =============================================================================== * Bug fix: -tell_media_space altered the pointers to MD5 of data files which stem from a previous session. This produced false mismatches with -check_md5_r. * Bug fix: CD tracks were reported with the sizes of the tracks in the first session. * Bug fix: -check_media use=outdev sector_map= stored TOC of input drive * Bug fix: -hide hfsplus and -as mkisofs -hide-hfsplus had no effect. Thanks to Davy Ho. * Bug fix: ./configure did not abort if libburn.h or libisofs.h were missing * New command -move * New -as mkisofs options -eltorito-id , -eltorito-selcrit libisoburn-1.2.6.tar.gz Tue Jan 08 2013 =============================================================================== * Bug fix: SIGSEGV by uninitialized local variable with -check_media patch_lba0="on". Regression by version 1.0.6 * Bug fix: -partition_offset 16 kept -isohybrid-gpt-basdat from writing MBR partition table entries of type 0xef * Bug fix: -rollback did not work if indev and outdev were empty * New API calls Xorriso_parse_line() and Xorriso__dispose_words() * New API calls Xorriso_fetch_outlists() and Xorriso_peek_outlists() * New API call Xorriso_start_msg_watcher() * New API calls Xorriso__severity_cmp() and Xorriso__severity_list() * New API calls Xorriso_sieve_add_filter, Xorriso_sieve_get_result, Xorriso_sieve_clear_results, Xorriso_sieve_dispose, Xorriso_sieve_big * New -boot_image partition_cyl_align mode "all" * New -blank mode prefix "force:" * New -osirrox settings "blocked" and "unblock" * New command -lns for creating symbolic links * New command -toc_of * New command -msg_op * New command -launch_frontend * Proof-of-concept of a GUI frontend program: xorriso-tcltk written in Tcl/Tk. libisoburn-1.2.4.tar.gz Fri Jul 20 2012 =============================================================================== * New API call isoburn_igopt_set_hfsp_serial_number() * New API calls isoburn_igopt_set_prep_partition, isoburn_igopt_set_efi_bootp * New API calls isoburn_igopt_set_hfsp_block_size() and isoburn_igopt_get_hfsp_block_size() * New -check_media option async_chunks= * New xorriso command -write_type * New xorriso command -rockridge * New xorriso command -hfsplus * New -find tests -has_hfs_crtp, has_hfs_bless * New -find actions set/get_hfs_crtp, set/get_hfs_bless * New -find test -disk_path * New -boot_image bootspec hfsplus_serial= * New -boot_image bootspecs hfsplus_block_size= and apm_block_size= * New -boot_image bootspecs efi_boot_part, prep_boot_part, chrp_boot_part * Command -hide allows hiding in HFS+ filesystem * New -as cdrecord options -tao -sao -dao * New -as mkisofs option -log-file * New -as mkisofs option --norock * New -as mkisofs option -hfsplus * New -as mkisofs option -hfsplus-file-creator-type * New -as mkisofs options -hfs-bless and -hfs-bless-by * New -as mkisofs option -hfsplus-serial-no * New -as mkisofs options -hfsplus-block-size and -apm-block-size * New -as mkisofs option -hide-hfsplus, -hide-hfsplus-list * New -as mkisofs options -prep-boot-part, -efi-boot-part, -chrp-boot-part * New -as mkisofs options -isohybrid-gpt-basdat, -isohybrid-gpt-hfsplus, -isohybrid-apm-hfsplus * Bug fix: Memory corruption when reading bootable image that was truncated * Bug fix: -update deleted MD5 of files of which only attributes had changed libisoburn-1.2.2.tar.gz Mon Apr 02 2012 =============================================================================== * New API calls isoburn_get_attached_start_lba(), isoburn_attach_start_lba() * New API calls isoburn_igopt_set_rr_reloc(), isoburn_igopt_get_rr_reloc() * New API calls isoburn_ropt_set_data_cache(), isoburn_ropt_get_data_cache() * New commands -x, -list_arg_sorting * New command -rr_reloc_dir * New command -data_cache_size * New -as mkisofs option -rr_reloc, implemented option -hide-rr-moved * Now ignoring -as mkisofs -no-split-symlink-components -no-split-symlink-fields * Bug fix: -osirrox on:sort_lba_on -extract from / restored nearly nothing * Bug fix: -as mkisofs without -graft-points could not handle names with "=" * Bug fix: Relaxation options joliet_rec_mtime and iso1999_rec_mtime had wrong values libisoburn-1.2.0.tar.gz Sat Jan 28 2012 =============================================================================== * Bug fix: mkisofs emulation did not record mtime in ECMA-119 directories * Bug fix: Program abort while drive tray is loading led to endless loop * Bug fix: Solaris adapter mishandled write commands which failed on first try * Bug fix: libisoburn.ver had a duplicate function entry * New relaxations isoburn_igopt_joliet_rec_mtime, isoburn_igopt_iso1999_rec_mtime * Made -compliance "rec_mtime" default for xorriso libisoburn-1.1.8.tar.gz Mon Nov 21 2011 =============================================================================== * Info document and man page for xorrecord * New option -sleep * Enabled recognition of QEMU DVD-ROM 0.12 * Enabled out-of the box use of xorriso on Linux guest on qemu virtio-blk-pci libisoburn-1.1.6.tar.gz Tue Sep 27 2011 =============================================================================== * Bug fix: -extract_single extracted directory content * Bug fix: -extract was not immediately aborted if -abort_on was triggered * Bug fix: xorriso did not write to files in filesystems with >= 4 TB free space * Bug fix: libisofs: ACL entries of groups and of user id 0 were not properly recorded and cannot be restored * Bug fix: libisofs: No ACLs were recorded on FreeBSD * Enabled recording and restoring of extattr on FreeBSD. * New option -list_extras * New -osirrox option strict_acl * New -find and -findx action list_extattr * Workaround for collision with Linux udev which lets device links vanish libisoburn-1.1.4.tar.gz Mon Aug 8 2011 =============================================================================== * Bug fix: xorriso native mode on some drives wrote unreadble ISO images to CD * Bug fix: -assert_volid did not work. Regression since version 1.1.0. * Bug fix: -acl or -xattr worked with -extract only on Linux and FreeBSD * New option -device_links libisoburn-1.1.2.tar.gz Fri Jul 8 2011 =============================================================================== * Bug fix: Since 1.0.6: Unreadable image produced by: xorrisofs ... >image.iso * Bug fix: -update_r scheduled non-existing files for hardlink update * Enabled extraction of the boot catalog file to disk filesystem * New option -list_speeds GNU xorriso-1.1.0.pl01.tar.gz Mon Jun 20 2011 =============================================================================== * Bug fix: Due to a bug in libburn-1.1.0, GNU xorriso-1.1.0 compiled only on GNU/Linux, FreeBSD, and Solaris, but not on other systems. libisoburn-1.1.0.tar.gz Sat Jun 18 2011 =============================================================================== * Bug fix: -mount_opts shared worked only with -osirrox o_excl_off * Bug fix: xorriso command -add_plainly "any" did not add all files to the image * Bug fix: The attempt to blank already blank DVD-RW was not gracefully blocked * Bug fix: -as mkisofs -isohybrid-mbr without -no-pad was not cylinder aligned * New option -signal_handling * New option -close_damaged * Dropped suffix .plXY from tarball name libisoburn-1.0.8.pl00.tar.gz Thu Apr 14 2011 =============================================================================== * Bug fix: mkisofs emulation could ignore options (regression in 0.1.6) libisoburn-1.0.6.pl00.tar.gz Sat Apr 9 2011 =============================================================================== * Bug fix: -as mkisofs padding did not work (regression in 1.0.4) * Bug fix: Options -gid and -uid had no effect * New API call isoburn_set_truncate() * New relax option isoburn_igopt_joliet_long_names * New option -early_stdio_test * New options -print_info and -print_mark * New -compliance option joliet_long_names * -as mkisofs option -joliet-long is now fully functional * Burning DVD-R DAO with 2 kB size granularity rather than 32 kB libisoburn-1.0.4.pl00.tar.gz Thu Mar 10 2011 =============================================================================== libisoburn novelties: * New isoburn_ropt_set_extensions() option isoburn_ropt_nomd5tag xorriso novelties: * Bug fix: xorrisofs did not work under growisofs -M (version 1.0.0 was ok) * Bug fix: -as mkisofs -C attempted to read volume header of blank media * Bug fix: -as mkisofs -old-root did not work with -graft-points * Bug fix: -as mkisofs -partition_hd_cyl had no effect * Bug fix: -as mkisofs did not properly unescape target part of pathspecs * Bug fix: isohybrid image size was not aligned to cylinder boundary * Bug fix: Compilation without zlib failed * New -padding modes "included" and "appended" * New bootspec partition_cyl_align=, new -as mkisofs option -partition_cyl_align * New -as mkisofs and -as cdrecord option --no_rc * Own man page and info document for xorrisofs libisoburn-1.0.2.pl00.tar.gz Mon Feb 23 2011 =============================================================================== libisoburn novelties: * Removed compiler obstacles of GNU xorriso on Solaris 9 * New isoburn_igopt_set_extensions() option isoburn_igopt_old_empty xorriso novelties: * Bug fix: Images produced with -for_backup might be unreadable and also fail -check_md5 verification. * Bug fix: mkisofs emulation options -l , -full-iso9660-filenames did not work. * Bug fix: Option -mkdir yielded SIGSEGV due to a NULL pointer * Bug fix: ECMA-119 standards violation with Volume Descriptor Set Terminator * New options -clone and -cp_clone * New -find actions update_merge, rm_merge, clear_merge * New -as mkisofs option -max-iso9660-filenames * New -as mkisofs option --old-empty * New -as mkisofs options -root , -old-root * New -as mkisofs options --old-root-no-md5, --old-root-no-ino, --old-root-dev libisoburn-1.0.0.pl00.tar.gz Mon Jan 17 2011 =============================================================================== libisoburn novelties: * New API call isoburn_igopt_set_untranslated_name_len() xorriso novelties: * Bug fix: -as mkisofs -print-size did not account for -partition_offset * Default -abort_on value is now "FAILURE" with batch and "NEVER" with dialog * New -compliance options untranslated_names , untranslated_name_len= * New -as mkisofs option -untranslated_name_len * New -compliance option iso_9660_1999, -as mkisofs option -iso-level 4 * New -compliance option iso_9660_level=number * New -compliance option allow_dir_id_ext, -as mkisofs -disallow_dir_id_ext * Disabled TOC emulation with -as mkisofs. May be re-enabled by --emul-toc. libisoburn-0.6.6.pl00.tar.gz Sun Dec 12 2010 =============================================================================== libisoburn novelties: * New API calls isoburn_igopt_set_disc_label(), isoburn_igopt_get_disc_label() * New API calls isoburn_ropt_set_displacement(), isoburn_ropt_get_displacement() xorriso novelties: * Bug fix: -as mkisofs -print-size printed the size but also produced ISO image * Build problem fix on Linux 2.4 in GNU xorriso libjte/checksum.c * New -as mkisofs option -joliet-long * New bootspec sparc_label=, new -as mkisofs options -sparc-boot , -sparc-label * New option -displacement libisoburn-0.6.4.pl00.tar.gz Tue Oct 26 2010 =============================================================================== libisoburn novelties: * New API call isoburn_libjte_req() * New API calls isoburn_igopt_attach_jte() and isoburn_igopt_detach_jte() * New API call isoburn_igopt_set_tail_blocks() * New API call isoburn_libjte_req() xorriso novelties: * New option -jigdo * New -as mkisofs options -jigdo-* and -md5-list as of genisoimage * New -as mkisofs options -checksum_algorithm_iso, -checksum_algorithm_template * New bootspecs mips_path= and mipsel_path= for Debian MIPS releases * New -as mkisofs options -mips-boot and -mipsel-boot * New option -append_partition, -as mkisofs -append_partition libisoburn-0.6.2.pl00.tar.gz Sat Sep 18 2010 =============================================================================== libisoburn novelties: * New API function isoburn_igopt_set_part_offset() * Hiding all non-API symbols from the linker by use of --version-script * Now with history of release notes in ./ChangeLog file. xorriso novelties: * Bug fix: Regression with -hardlinks and -compliance old_rr, 0.4.2, Aug 2009 * New option -preparer_id, -as mkisofs options -p and -preparer * New -boot_image specifier emul_type=none|hard_disk|floppy * New boot_image boot specs partition_offset,partition_hd_cyl,partition_sec_hd * Made behavior of -as mkisofs with unknown options more similar to original * New -as mkisofs option -hard-disk-boot, enabled -b without -no-emul-boot * New -as mkisofs option -e from Fedora genisoimage * New -as mkisofs options -partition_offset,-partition_hd_cyl,-partition_sec_hd libisoburn-0.6.0.pl00.tar.gz Fri Jul 02 2010 =============================================================================== libisoburn novelties: xorriso novelties: * New option -read_mkisofsrc interprets .mkisofsrc To be performed before -as mkisofs. Performed automatically with program alias name "xorrisofs". * Implemented -as mkisofs options -dir-mode, -file-mode, -abstract, -biblio, -copyright * Implemented -as mkisofs options -hide, -hide-joliet, -hide-list, -hide-joliet-list * New -as mkisofs option --boot-catalog-hide * New option -hide, -find action -hide, -find test -hidden * New -boot_image bootspec cat_hidden=on * New options -copyright_file , -biblio_file , -abstract_file * New find test -disk_name * Enabled use of libreadline on Solaris * Bug fix: -check_media patch_lba0= could install wrong image size * Bug fix: -as mkisofs option -volset was wrongly interpreted like -volid libisoburn-0.5.8.pl00.tar.gz Mon Jun 14 2010 =============================================================================== libisoburn novelties: * xorriso source split into more modules, object code moved into libisoburn xorriso novelties: * New write extension option isoburn_igopt_no_emul_toc * New -compliance rule no_emul_toc, new -as mkisofs --no-emul-toc * Implemented -as cdrecord -V * Implemented -as mkisofs options -U, -N, -l, -d, -allow-lowercase libisoburn-0.5.6.pl00.tar.gz Tue May 04 2010 =============================================================================== libisoburn novelties: xorriso novelties: * Allowing up to 32 boot images * New -boot_image bootspecs efi_path=, platform_id=, sel_crit=, id_string=, next * New -as mkisofs options --efi-boot, -eltorito-alt-boot libisoburn-0.5.4.pl00.tar.gz Mon Apr 19 2010 =============================================================================== libisoburn novelties: * New API call isoburn_igopt_set_system_area() * New API call isoburn_igopt_set_pvd_times() * New isoburn_igopt_set_relaxed() options: only_iso_versions, no_j_force_dots xorriso novelties: * New -boot_image any system_area=, -as mkisofs option -G * New -boot_image grub partition_table=on, -as mkisofs --protective-msdos-label * New -boot_image isolinux partition_table=on, -as mkisofs -isohybrid-mbr * New option -volume_date, -as mkisofs --modification-date= * New -find action mkisofs_r,-as mkisofs -r * New -find action sort_weight, -as mkisofs --sort-weight * New -compliance options only_iso_version, no_j_force_dots avoid a bug in GRUB 1.96. They are default now. libisoburn-0.5.2.pl00.tar.gz Tue Mar 30 2010 =============================================================================== xorriso novelties: * xorriso documentation is now based on a hybrid format of Texinfo and man- page. Copies included: .texi, .info, .1 (man page) libisoburn-0.5.0.pl00.tar.gz Tue Feb 22 2010 =============================================================================== xorriso novelties: * The former xorriso-standalone project is now GNU xorriso under GPLv3+. This affects some documentation and the generator script, but not the license of libisoburn or its program xorriso. * Bug fix: xorriso -update_r could lead to SIGSEGV if applied to a data file rather than a directory. * Bug fix on FreeBSD: xorriso could leave the drive tray locked. * New option -scsi_log libisoburn-0.4.8.pl00.tar.gz Tue Jan 26 2010 =============================================================================== xorriso novelties: * Bug fix: xorriso did not blank CD-RW with images that were prepared on hard disk. * New configure option --enable-libcdio for system adapter to libcdio-0.83git libisoburn-0.4.6.pl00.tar.gz Wed Dec 09 2009 =============================================================================== xorriso novelties: * New options -dvd_obs and -stdio_sync * New configure option --enable-dvd-obs-64k libisoburn-0.4.4.pl00.tar.gz Wed Oct 28 2009 =============================================================================== xorriso novelties: * Bug fix: With -as cdrecord : -xa1 and -xamix were ignored although they do matter. * Option -toc now reports the individual media type. E.g. with a DVD+RW: "Media product: RICOHJPN/W11/49 , Ricoh Company Limited" * New option -pvd_info displays image id strings. New options -system_id , - volset_id allow to set such image id strings. * New option -mount_opts tries to circumvent an eventual ban to mount the same device twice. Some Linux systems allow to mount two sessions of the same media only if they get fooled via the loop device. * New option -scdbackup_tag performs the task of the scdbackup MD5 checksum filter inside xorriso. Already fixed by patch releases of xorriso-0.4.2: * Bug fix: -cut_out deleted previously cut-out pieces of the same file * Bug fix libisofs: Filenames could lose blanks during a multi-session cycle * Bug fix: -for_backup did not enable -xattr and -md5 if no drive was chosen yet * Bug fix: xorrisofs -help, xorrecord -help displayed original xorriso -help libisoburn-0.4.2.pl02.tar.gz Thu Oct 08 2009 =============================================================================== * Bug fix: -for_backup did not enable -xattr and -md5 if no drive was chosen yet * Bug fix: xorrisofs -help, xorrecord -help displayed original xorriso -help Libisoburn 0.4.2.pl01 =============================================================================== * Bug fix: xorriso option -cut_out deleted previously cut-out pieces of the same file. The bug was introduced with release 0.1.4 in March 2008. Libisoburn 0.4.2 =============================================================================== * New write options isoburn_igopt_session_md5, isoburn_igopt_file_md5, isoburn_igopt_file_stability allow to record MD5 checksums of session and single data files. * New read option isoburn_ropt_nomd5 allows to read those MD5 sums when importing an ISO image. xorriso novelties: * New option -md5, new -as mkisofs option --md5 allow to record in the image MD5 checksums for the whole session and for each single data file. * New options -check_md5, -check_md5_r allow to verify the whole session or single files by comparing their image data with their MD5 sums. * Options -compare, -compare_r, -update, update_r now can use recorded MD5. * New -find actions check_md5, get_md5, make_md5 allow to check, to display or to recompute MD5 checksums of data files. New find test -has_md5 distinguishes data files which have recorded MD5 from files which have none. * New -find test -has_any_xattr and action get_any_xattr allow to inspect the libisofs specific attributes of namespace "isofs". * Options -lsl and lsdl now display correct link counts if -hardlinks is on. * New option -calm_drive allows to reduce drive noise if no data reading is intended for a while. * New option -list_profiles allows to inquire and process the list of supported media types. * Bug fix: xorriso -as mkisofs did not understand the -C option of growisofs any more. (Already fixed by release 0.4.0.pl01, 20 Jul 2009) libisoburn-0.4.0.pl01.tar.gz Mon Jul 20 2009 =============================================================================== xorriso novelties: * New option -hardlinks enables recording and restoring of hard link relations. * Improved reading performance with -update_r and -extract. * New option -for_backup as shortcut for -acl -xattr -hardlinks * Operators with option -find : -not, -or, -and, (, ), -if, -then, -else * New -find tests -wholename, -prune * Bug fix: SIGSEGV with option -status and no search string * Bug fix: -load volid did not perform pattern search * Bug fix: Copies of overwriteable media on sequential were mistaken in ROM drives Libisoburn 0.4.0.pl01 release notes: * Bug fix: xorriso -as mkisofs did not understand the -C option of growisofs any more libisoburn-0.3.8.pl00.tar.gz Sun Apr 19 2009 =============================================================================== libisoburn novelties: * New API calls isoburn_ropt_set_auto_incharset() and isoburn_ropt_get_auto_incharset() xorriso novelties: * New options -set_filter, -set_filter_r, -find -exec set_filter allow to manipulate the content of data files on the fly. * New option -zisofs, built-in filters --zisofs , --gzip , --gunzip enable compression and decompression of data file content. * New options -external_filter , -unregister_filter, -close_filter_list allow arbitrary external processes to do content filtering. * New options -show_stream, -show_stream_r allow to inspect the origin and the filters of data files in an emerging image. * New option -auto_charset based on xattr "isofs.cs" allows to tag an ISO filesystem image with the character set name that is in use on the current terminal. libisoburn-0.3.6.pl00.tar.gz Mon Mar 16 2009 =============================================================================== xorriso novelties: * Dummy MMC adapter of libburn allows compilation on systems other than Linux, FreeBSD * Default of -compliance has been changed to "old_rr", new rule "new_rr" * New -stream_recording modes with start address or "data". "on" is now 32s. libisoburn-0.3.4.pl00.tar.gz Sun Mar 01 2009 =============================================================================== * New isoburn_read_opts values: isoburn_ropt_noaaip, isoburn_ropt_noacl, isoburn_ropt_noea xorriso novelties: * New option -acl enables ACL import and export * New options -getfacl, -getfacl_r, -setfacl, -setfacl_r, -setfacl_list * New find tests -has_acl, -has_no_acl , new find actions getfacl, setfacl * New option -xattr enables import and export of Extended Attributes * New options -getfattr, -getfattr_r, -setfattr, -setfattr_r, -setfattr_list * New find tests -has_xattr, -has_aaip, new find actions getfattr, setfattr * New -as mkisofs options --acl and --xattr * New option -disk_dev_ino accelerates incremental backups libisoburn-0.3.2.pl00.tar.gz Tue Jan 06 2009 =============================================================================== * New API function isoburn_get_mount_params() * Now depending on libburn-0.6.0 which supports BD-R media xorriso novelties: * Bug fix: Options -extract and -extract_single were enabled with -osirrox off * New options -mount , -mount_cmd , -session_string * New -format modes by_size_ and fast_by_size_ * New option -assert_volid * New option -drive_class for safety management of pseudo-drive access libisoburn-0.3.0.pl00.tar.gz Tue Dec 2 2008 =============================================================================== * Now depending on libisofs-0.6.12 and libburn-0.5.6 to ensure use of their recent bug fixes xorriso novelties: * New options -quoted_path_list, -quoted_not_list * New option -backslash_codes for weird file names and terminal safety * New options -charset, -in_charset, -out_charset * New option -local_charset allows to override locale * New option -application_id * New option -compliance allows certain deviations from standards * Suitable ISOLINUX boot images are made alternatively bootable via an MBR * Bug fix: Forgot exit value registration to -return_with. Thanks to Steve Dodd. * Bug fix: -format as_needed did not recognize unformatted BD-RE * Bug fix: disk patterns with relative addresses were not properly resolved libisoburn-0.2.8.pl00.tar.gz Wed Oct 15 2008 =============================================================================== * Now depending on libisofs-0.6.10 and libburn-0.5.4 to ensure use of their recent bug fixes xorriso novelties: * Ability to write and maintain bootable ISO images based on ISOLINUX * New ./configure option --disable-libreadline to make binary more portable * Bug fix: -as mkisofs -iso-level was accused to be an unknown option * Bug fix: -follow link attributed random target filenames to looping links libisoburn-0.2.6.pl00.tar.gz Sat Sep 20 2008 =============================================================================== xorriso novelties: * Capability to insert and extract files far larger than 4 GB * New option -file_size_limit, -as mkisofs now supports -iso-level 1 to 3 * New option -extract_cut * New -error_behavior "file_extraction" behavior "best_effort" * New option -check_media_defaults * New option -list_delimiter * Bug fix: -format full did not re-format already formatted DVD+RW libisoburn-0.2.4.pl00.tar.gz Mo Aug 25 2008 =============================================================================== xorriso novelties: * New option -check_media * New -find test -damaged, new -find actions "report_damage", "report_lba" * New -error_behavior occasion "file_extraction" libisoburn-0.2.2.pl01.tar.gz Fr Jul 25 2008 =============================================================================== Libisoburn 0.2.2 release notes (Jul 19 2008) * New API function isoburn_prepare_blind_grow() * New flag bits with isoburn_set_msc1(), isoburn_read_iso_head() xorriso novelties: * New option -grow_blindly * Options -C and -M for -as mkisofs emulation * Options for -as cdrecord emulation: -multi, -msinfo, -isosize, tsize, -- grow_overwriteable_iso, write_start_address, * New option -pacifier, more compatible pacifier with -as mkisofs * make install creates aliases as symbolic links: osirrox, xorrisofs, xorrecord * Can serve growisofs if started as xorrisofs, genisofs, mkisofs, genisoimage pl01 changes (Jul 25 2008): * Bug fix: Variable DESTDIR was not properly respected during make install libisoburn-0.2.0.pl00.tar.gz Mon Jun 23 2008 =============================================================================== Libisoburn 0.2.0 release notes: * New API functions isoburn_set_msgs_submit(), isoburn_drive_set_msgs_submit() xorriso novelties: * Bug fix: -chmod unintentionally performed o-x as first operation * New options -cpax, -cp_rx, -cp_rax, -extract to restore files and trees from ISO image to disk filesystem. * New option -paste_in to copy ISO files into parts of disk files * New options -map_l, -compare_l, -update_l, -extract_l libisoburn-0.1.8.pl00.tar.gz Tue Jun 3 2008 =============================================================================== * Bug fix: Major,minor numbers of device files appeared as 0,1 in next session * Bug fix: modifying to overwriteable target yielded unmountable results xorriso novelties: * New option -stream_recording for full speed with DVD-RAM and BD-RE * New options -osirrox and -cpx allow to extract single files from ISO image libisoburn-0.1.6.pl00.tar.gz Mon May 19 2008 =============================================================================== * Support for BD-RE (by depending on libburn-0.4.8) * New API wrapper calls isoburn_toc_*() around libburn TOC inquiry calls * New API call isoburn_read_iso_head() identifies ISO 9660 filesystems * New API call isoburn_set_msc1() (like mount -o sbsector=) xorriso novelties: * Bug fix: -update_r and others did not work properly with relative paths * New options -map and -map_single * New options -not_paths, -not_leaf, -not_list, -not_mgt, -as mkisofs -m * Emulated -toc on overwriteable media, new -toc layout with volume id * New option -load makes alternative sessions accessible * New -blank and -format modes 'as_needed' * New option -list_formats and -format mode 'by_index_' libisoburn-0.1.4.pl00.tar.gz Wed Apr 30 2008 =============================================================================== * Improved performance with reading directory trees * xorriso: Improved attribute transfer from disk for target * xorriso: Incremental backup functionality by new option -update_r * xorriso: Options -cut_out and split_size map byte intervals of oversized files into ISO files * xorriso: Emulation of some basic mkisofs and cdrecord options * Dynamically linkable with release version 0.6.4 of libisofs Version 0.1.2 was not released as libisoburn but only as xorriso standalone version. Wed Mar 12 2008 =============================================================================== * Bug fix: -report_about HINT or higher did not report at all * Bug fix: speed=number without unit or media type letter was always CD speed * Bug fix: it was possible to write to appendable media which was not -indev * Bug fix: -follow param did not work for adding non-directory symbolic links * Bug fix: It was not possible to -add /THIS=a /b=THIS * Improved attribute transfer from disk for implicit target directories * New option -as "cdrecord" emulates a narrow set of cdrecord gestures * New option -as "mkisofs" emulates a narrow set of mkisofs gestures * New option -publisher * New option -errfile_log * Support for DVD+R/DL media * New options -compare, -compare_r and according -find -exec action * New options -update, -update_r and according -find -exec action * New -find actions "rm", "rm_r", new -findx -type "m" -exec "empty_iso_dir" * New option -cut_out libisoburn-0.1.0.pl01.tar.gz Fri Feb 15 2008 =============================================================================== * Initial release of libisoburn/xorriso * libisoburn connects libisofs and libburn * libisoburn emulates multi-session on media without session history * xorriso is an integrated multi-session tool for ISO 9660 Rock Ridge images * due to a subtle mistake in ABI usage with libisofs this release had to be restricted to dynamic linking with exactly libisofs-0.6.2 where the mistake does no harm. A version of libisoburn which is open to all future libisofs versions will be released shortly after libisofs.0.6.4. See file README for libisoburn and xorriso specific installation instructions. This file here is rather a manual for advanced usage of ./configure ------------------------------------------------------------------- Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== Briefly, the shell commands `./configure; make; make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf bug. Until the bug is fixed you can use this workaround: CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. # Copyright (C) 2006 - 2008 Vreixo Formoso <metalpain2002@yahoo.es> # Copyright (C) 2008 - 2019 Thomas Schmitt <scdbackup@gmx.net> # Provided under GPL version 2 or later. # ts A90315 : LIBBURNIA_PKGCONFDIR is defined OS specific in acinclude.m4 # was: pkgconfigdir=$(libdir)/pkgconfig pkgconfigdir=$(LIBBURNIA_PKGCONFDIR) libincludedir=$(includedir)/libisoburn lib_LTLIBRARIES = libisoburn/libisoburn.la ACLOCAL_AMFLAGS = -I ./ ## ========================================================================= ## # Build libraries libisoburn_libisoburn_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) $(LIBLDFLAGS) libisoburn_libisoburn_la_SOURCES = \ libisoburn/isoburn.h \ libisoburn/libisoburn.h \ libisoburn/burn_wrap.c \ libisoburn/data_source.c \ libisoburn/isoburn.c \ libisoburn/isofs_wrap.c \ \ xorriso/xorriso.h \ xorriso/xorriso_private.h \ xorriso/sfile.h \ xorriso/sfile.c \ xorriso/aux_objects.h \ xorriso/aux_objects.c \ xorriso/findjob.h \ xorriso/findjob.c \ xorriso/check_media.h \ xorriso/check_media.c \ xorriso/misc_funct.h \ xorriso/misc_funct.c \ xorriso/text_io.h \ xorriso/text_io.c \ xorriso/match.h \ xorriso/match.c \ xorriso/emulators.h \ xorriso/emulators.c \ xorriso/disk_ops.h \ xorriso/disk_ops.c \ xorriso/cmp_update.h \ xorriso/cmp_update.c \ xorriso/parse_exec.h \ xorriso/parse_exec.c \ xorriso/opts_a_c.c \ xorriso/opts_d_h.c \ xorriso/opts_i_o.c \ xorriso/opts_p_z.c \ \ xorriso/xorrisoburn.h \ xorriso/base_obj.h \ xorriso/base_obj.c \ xorriso/lib_mgt.h \ xorriso/lib_mgt.c \ xorriso/sort_cmp.h \ xorriso/sort_cmp.c \ xorriso/drive_mgt.h \ xorriso/drive_mgt.c \ xorriso/iso_img.h \ xorriso/iso_img.c \ xorriso/iso_tree.h \ xorriso/iso_tree.c \ xorriso/iso_manip.h \ xorriso/iso_manip.c \ xorriso/write_run.h \ xorriso/write_run.c \ xorriso/read_run.h \ xorriso/read_run.c \ xorriso/filters.h \ xorriso/filters.c \ xorriso/xorriso_timestamp.h \ xorriso/xorriso_buildstamp.h libisoburn_libisoburn_la_LIBADD = \ $(THREAD_LIBS) \ -lisofs \ -lburn libinclude_HEADERS = \ libisoburn/libisoburn.h \ xorriso/xorriso.h # libisoburn_libisoburn_la_CFLAGS = $(READLINE_DEF) $(LIBACL_DEF) $(XATTR_DEF) \ # $(EXTF_DEF) $(EXTF_SUID_DEF) $(ZLIB_DEF) \ # $(XORRISO_DVD_OBS_64K) ## ========================================================================= ## # This is the reference application of libisoburn. See man xorriso/xorriso.1 # bin_PROGRAMS = \ xorriso/xorriso # This looks quite ugly with make install: xorriso.c is compiled twice again # # Trying to create a build timestamp file # # BUILT_SOURCES = xorriso/xorriso_buildstamp.h # # phony targets get rebuilt every time # # .PHONY: xorriso/xorriso_buildstamp.h # xorriso/xorriso_buildstamp.h: # date -u '+#define Xorriso_build_timestamP "%Y.%m.%d.%H%M%S"' >xorriso/xorriso_buildstamp.h # cat xorriso/xorriso_buildstamp.h xorriso_xorriso_CPPFLAGS = -Ilibisoburn # xorriso_xorriso_CFLAGS = $(READLINE_DEF) $(LIBACL_DEF) $(XATTR_DEF) \ # $(EXTF_DEF) $(EXTF_SUID_DEF) $(ZLIB_DEF) \ # $(XORRISO_DVD_OBS_64K) xorriso_xorriso_LDADD = libisoburn/libisoburn.la -lisofs -lburn \ $(THREAD_LIBS) $(LIBBURN_ARCH_LIBS) xorriso_xorriso_SOURCES = \ xorriso/xorriso.h \ xorriso/xorriso_main.c # Install symbolic links to the xorriso binary # install-exec-hook: if test -e "$(DESTDIR)$(bindir)"/xorrisofs ; then rm "$(DESTDIR)$(bindir)"/xorrisofs ; else echo ; fi ln -s xorriso "$(DESTDIR)$(bindir)"/xorrisofs if test -e "$(DESTDIR)$(bindir)"/osirrox ; then rm "$(DESTDIR)$(bindir)"/osirrox ; else echo ; fi ln -s xorriso "$(DESTDIR)$(bindir)"/osirrox if test -e "$(DESTDIR)$(bindir)"/xorrecord ; then rm "$(DESTDIR)$(bindir)"/xorrecord ; else echo ; fi ln -s xorriso "$(DESTDIR)$(bindir)"/xorrecord $(LIBBURNIA_LDCONFIG_CMD) "$(DESTDIR)$(libdir)" || echo 'NOTE: Explicit dynamic library configuration failed. If needed, configure manually for:' "$(DESTDIR)$(libdir)" # Alternative to the disabled .PHONY above. # Trying to create a build timestamp file semi-manually: make buildstamped # buildstamp: date -u '+#define Xorriso_build_timestamP "%Y.%m.%d.%H%M%S"' >xorriso/xorriso_buildstamp.h cat xorriso/xorriso_buildstamp.h # For now make buildstamped has to be performed explicitly. buildstamped: buildstamp make # "make clean" shall remove a few stubborn .libs directories # which George Danchev reported Dec 03 2011. # Learned from: http://www.gnu.org/software/automake/manual/automake.html#Clean clean-local: -rm -rf test/.libs # Will be executed by "make check" check-local: xorriso/xorriso -no_rc -version -list_extras all ## Build companion applications noinst_PROGRAMS = \ test/compare_file \ xorriso/make_xorriso_1 # A program to compare two files in mirrored trees in mounted filesystems # To compare tree /media/dvd and /original/dir : # find /media/dvd -exec test/compare_file '{}' /media/dvd /original/dir ';' # test_compare_file_CPPFLAGS = test_compare_file_CFLAGS = test_compare_file_LDADD = test_compare_file_SOURCES = test/compare_file.c # Specialized converter from xorriso/xorriso.texi to xorriso/xorriso.1 # xorriso_make_xorriso_1_CPPFLAGS = xorriso_make_xorriso_1_CFLAGS = xorriso_make_xorriso_1_LDADD = xorriso_make_xorriso_1_SOURCES = xorriso/make_xorriso_1.c # A Proof-of-concept for frontends, and xorriso-dd-target if on Linux kernel bin_SCRIPTS = \ frontend/xorriso-tcltk \ $(XORRISO_DD_TARGET) EXTRA_SCRIPTS = xorriso-dd-target/xorriso-dd-target ## ========================================================================= ## ## Build documentation (You need Doxygen for this to work) webhost = http://libburn-api.pykix.org webpath = / docdir = $(DESTDIR)$(prefix)/share/doc/$(PACKAGE)-$(VERSION) doc: doc/html doc/html: doc/doxygen.conf if [ -f ./doc/doc.lock ]; then \ $(RM) -r doc/html; \ doxygen doc/doxygen.conf; \ fi doc-upload: doc/html scp -r $</* $(webhost):$(webpath) ## Not by default any more. ## It is unclear who is supposed to create file ./doc/doc.lock # all: doc install-data-local: if [ -f ./doc/doc.lock ]; then \ $(mkinstalldirs) $(docdir)/html; \ $(INSTALL_DATA) doc/html/* $(docdir)/html; \ fi uninstall-local: rm -rf $(docdir) ## ========================================================================= ## # Indent source files indent_files = \ $(libisoburn_libisoburn_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 ## ========================================================================= ## # Extra things nodist_pkgconfig_DATA = \ libisoburn-1.pc man_MANS = \ xorriso/xorriso.1 \ xorriso/xorrisofs.1 \ xorriso/xorrecord.1 \ xorriso/xorriso-tcltk.1 \ $(XORRISO_DD_TARGET_MAN) EXTRA_MANS = xorriso-dd-target/xorriso-dd-target.1 info_TEXINFOS = \ xorriso/xorriso.texi \ xorriso/xorrisofs.texi \ xorriso/xorrecord.texi \ xorriso/xorriso-tcltk.texi \ xorriso-dd-target/xorriso-dd-target.texi # xorriso-dd-target.texi is hardcoded for now because of # Makefile.am: error: texinfo file '@XORRISO_DD_TARGET_TEXI@' has # unrecognized extension # $(XORRISO_DD_TARGET_TEXI) # # EXTRA_TEXINFOS = xorriso-dd-target/xorriso-dd-target.texi # To have a file in releng_generated_data. If the directory itself is listed # in Makefile.am EXTRA_DIST, then its .svn subdir gets into the tarball. releng/releng_generated_data/placeholder: touch releng/releng_generated_data/placeholder EXTRA_DIST = \ bootstrap \ libisoburn-1.pc.in \ version.h.in \ doc/comments \ doc/doxygen.conf.in \ doc/partition_offset.wiki \ doc/qemu_xorriso.wiki \ doc/startup_file.txt \ frontend/frontend_pipes_xorriso.c \ frontend/README-tcltk \ frontend/xorriso-tcltk \ frontend/sh_on_named_pipes.sh \ frontend/xorriso_broker.sh \ frontend/grub-mkrescue-sed.sh \ xorriso-dd-target/xorriso-dd-target \ README \ AUTHORS \ CONTRIBUTORS \ COPYRIGHT \ COPYING \ INSTALL \ ChangeLog \ libisoburn/libisoburn.ver \ xorriso/changelog.txt \ xorriso/xorriso_buildstamp_none.h \ xorriso/README_gnu_xorriso \ xorriso/make_xorriso_standalone.sh \ xorriso/make_docs.sh \ xorriso/unite_html_b_line.c \ xorriso/convert_man_to_html.sh \ xorriso/man_xorriso_to_html.sh \ xorriso/man_xorrisofs_to_html.sh \ xorriso/man_xorrecord_to_html.sh \ releng/README \ releng/auto_cxx \ releng/auto_isocontent \ releng/auto_printsize \ releng/change_shell_to_use \ releng/codesamples/api_3lib.cpp \ releng/codesamples/api_xorriso.cpp \ releng/inc/releng_getopts.inc \ releng/jigdo-gen-md5-list \ releng/jigdo-gen-md5-list.1 \ releng/manual_burn \ releng/manual_devices \ releng/manual_isojigdo \ releng/releng_generated_data/placeholder \ releng/run_all_auto \ releng/template_new \ $(man_MANS) Please see ChangeLog for bug fixes and novelties. ------------------------------------------------------------------------------ http:libburnia-project.org ------------------------------------------------------------------------------ libisoburn and xorriso. By Vreixo Formoso <metalpain2002@yahoo.es> and Thomas Schmitt <scdbackup@gmx.net> Integrated sub project of libburnia-project.org. http://files.libburnia-project.org/releases/libisoburn-1.5.6.tar.gz Copyright (C) 2006-2009 Vreixo Formoso, Copyright (C) 2006-2023 Thomas Schmitt. Provided under GPL version 2 or later. ------------------------------------------------------------------------------ libisoburn is a frontend for libraries libburn and libisofs which enables creation and expansion of ISO-9660 filesystems on all CD/DVD/BD media supported by libburn. This includes media like DVD+RW, which do not support multi-session management on media level and even plain disk files or block devices. The price for that is thorough specialization on data files in ISO-9660 filesystem images. So libisoburn is not suitable for audio (CD-DA) or any other CD layout which does not entirely consist of ISO-9660 sessions. xorriso is an application of libisoburn, libisofs, and libburn, which reads commands from program arguments, files, stdin, or readline. Its features are also available via a C language API of libisoburn. Currently they are fully supported on Linux with kernels >= 2.4, on FreeBSD with ATAPI/CAM support enabled in the kernel, see atapicam(4), on OpenSolaris (tested with kernel 5.11), on NetBSD (tested with 6.1.2 and 6.1.3). On other X/Open compliant systems libburn will only offer POSIX i/o with disk file objects, but no direct MMC operation on CD/DVD/BD drives. By using this software you agree to the disclaimer at the end of this text: "... without even the implied warranty ..." Compilation, First Glimpse, Installation Dynamic library and compile time header requirements for libisoburn-1.5.6 : - libburn.so.4 , version libburn-1.5.6 or higher - libisofs.so.6 , version libisofs-1.5.6 or higher libisoburn and xorriso will not start with libraries which are older than their include headers seen at compile time. Obtain libisoburn-1.5.6.tar.gz, take it to a directory of your choice and do: tar xzf libisoburn-1.5.6.tar.gz cd libisoburn-1.5.6 Within that directory execute: ./configure --prefix=/usr make Then become superuser and execute make install which will make available libisoburn.so.1 and the program xorriso. On GNU/Linux it will try to run program ldconfig with the library installation directory as only argument. Failure to do so will not abort installation. One may disable ldconfig by ./configure option --disable-ldconfig-at-install . By use of a version script, the libisoburn.so library exposes no other function names but those of the API definitions in <libisoburn/libisoburn.h> and <libisoburn/xorriso.h>. If -Wl,--version-script=... makes problems with the local compiler, then disable this encapsulation feature by ./configure --disable-versioned-libs make clean ; make The ./configure script of libisoburn can check via pkg-config whether suitable libburn and libisoburn are installed. Regrettably this test failed on several systems due to local pkg-config problems. So it is disabled by default and may be enabled by: ./configure --enable-pkg-check-modules In this case, ./configure fails if no suitable installations are found. xorriso libisoburn comes with a command line and dialog application named xorriso, which offers a substantial part of libisoburn features to shell scripts and users. Its file xorriso/README_gnu_xorriso describes the tarball of the derived package GNU xorriso as first preference for a statically linked xorriso installation. The libisoburn installation described above produces a dynamically linked xorriso binary depending on libburn.so, libisofs.so, libisoburn.so. After installation documentation is available via man xorriso man xorrisofs man xorrecord Several alias links point to the xorriso binary: xorrisofs starts xorriso with -as mkisofs emulation already enabled xorrecord starts xorriso with -as cdrecord emulation already enabled osirrox starts with -osirrox image-to-disk copying already enabled By default libisoburn will depend on libreadline if the library and its development header files are present at compile time. If not, then it will try to depend on libedit and its header file. Both conditional dependencies can be avoided by running ./configure --prefix=/usr --disable-libreadline make clean ; make Never omit the "make clean" command after switching enabling of libreadline. Note that depending on libreadline-6 will cause libisoburn's license to become "GPL version 3 or later". If you want to explictely allow only the use of libedit, then do ./configure --prefix=/usr --disable-libreadline --enable-libedit Other deliberate dependency reduction options of ./configure are: --disable-libacl avoid use of ACL functions like acl_to_text() --disable-xattr avoid use of xattr functions like listxattr() on Linux or extattr_list_file() on FreeBSD --disable-zlib avoid use of zlib functions like compress2() --disable-libjte avoid use of libjte for -jigdo command xorriso allows to use external processes as file content filters. This is a potential security risk which may be avoided by ./configure option --disable-external-filters By default the filter feature is disabled if effective user id and real user id differ. This ban can be lifted by --enable-external-filters-setuid In some situations Linux may deliver a better write performance to DVD drives if 64 KB rather than 32 KB are transmitted in each write operation. 64k can be made default at configure time by: --enable-dvd-obs-64k libisoburn, libisofs, and libburn C language API For the lower API concepts and calls see ./libisoburn/libisoburn.h as well as /usr/include/libisofs/libisofs.h /usr/include/libburn/libburn.h xorriso C language API Actually the dynamically linked xorriso binary is only a small start program for the xorriso API that is implemented inside libisoburn. There are API calls for command readers and interpreters, and there are API calls for each single command of xorriso. Interested programmers should have a look into the API definition at xorriso/xorriso.h and the start program xorriso/xorriso_main.c The header file xorriso.h gets installed suitable for #include <libisoburn/xorriso.h> So after installation of a binary libisoburn package you may find it e.g. as /usr/include/libisoburn/xorriso.h xorriso under control of a (GUI) frontend process The dialog mode allows frontend programs to connect via pipes to the standard input and output of xorriso. Several commands of xorriso help with receiving and parsing of reply messages. As a proof of concept, there is the Tcl/Tk script xorriso-tcltk which can be launched by this shell command: xorriso-tcltk Or in the xorriso build directory, without installation of xorriso: xorriso/xorriso -launch_frontend frontend/xorriso-tcltk --stdio -- In the running GUI, click with the rightmost mouse button on any GUI element to get its particular help text. The "Help" button at the upper right corner gives a short introduction and instructions for some common use cases. See also file frontend/README-tcltk. See its Tcl code for getting an idea how this gets achieved. The script is part of the tarball and gets installed by make install. If a xorriso distro package does not install it, you may get it directly from https://dev.lovelyhq.com/libburnia/libisoburn/blob/master/frontend/xorriso-tcltk Further there is the C program frontend/frontend_pipes_xorriso.c which forks a xorriso process and shows similar communication gestures as xorriso-tcltk. In particular it connects to xorriso via two pipes, sends commands, waits for all replies of a command, picks info out of the xorriso message sieve, and parses reply message lines into words. The bash script frontend/sh_on_named_pipes.sh forks a xorriso process connected to two pipes. It then runs a dialog loop, sends commands to xorriso, and displays the replies. The sh script frontend/xorriso_broker.sh is intended to execute xorriso commands on a permanently running xorriso process. It gets an id_string by which it looks for named pipes with a running xorriso process. If no such pipe is found, then it starts a xorriso connected to newly created pipes. After this is done, the optionally given xorriso arguments are written into the stdin pipe from where xorriso will read and execute them. The script will end but the xorriso process will go on and wait for more commands. Drives and Disk File Objects The user of libisoburn applications needs operating system dependent permissions for the CD/DVD/BD drives which shall be used. On Linux, FreeBSD, NetBSD this means -rw-permissions, even if only reading is intended. On Solaris one needs privileges "basic,sys_devices" and r-permission, even if writing is intended. A list of rw-accessible drives can be obtained by xorriso -devices or by xorriso API call Xorriso_option_devices() or by libburn API call burn_drive_scan() A possible source of problems are hald or other automounters. If you can spot a process "hald-addon-storage" with the address of your desired drive, then consider to kill it. A similar process "udisks-daemon: polling ..." can be seen on newer Linuxes. On Debian GNU/Linux 6.0.2 amd64 there is /lib/udev/rules.d/80-udisks.rules where one can remove all CD drives ("sr*") from the list of automountable devices: KERNEL=="sd*|hd*|mmcblk*|mspblk*", ENV{UDISKS_PRESENTATION_NOPOLICY}="0" # KERNEL=="sd*|hd*|sr*|mmcblk*|mspblk*", ENV{UDISKS_PRESENTATION_NOPOLICY}="0" Copying the recognition criterion from /etc/udev/rules.d/70-persistent-cd.rules one can prevent automounting a single drive, too. E.g.: SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:11.0-scsi-2:0:0:0", ENV{UDISKS_PRESENTATION_NOPOLICY}:="1" If you cannot get rid of the automounter, try whether it helps to always load the drive tray manually before starting a write run of xorriso. Wait until the drive light is off and the mounted media appears. Then try to unmount the mounted media before a write run. Besides true optical drives, libisoburn can also address disk files as input or output drives. The addresses of the disk files have to be preceded by "stdio:". Like: "stdio:/tmp/pseudo_drive" Note: xorriso by default prefixes "stdio:" to addresses outside the /dev tree if they do not lead to an optical drive device file. xorriso-dd-target libisoburn comes with a script named xorriso-dd-target/xorriso-dd-target which uses the util-linux program lsblk to find suitable hard-disk-like target devices for copying hard-disk bootable ISO images onto them. Such images are offered by GNU/Linux distributions for installing their system. xorriso-dd-target gets installed only if ./configure detects to run on a GNU/Linux system. It refuses to start on non-Linux kernels or if program lsblk is not found in /usr/sbin, /sbin, /usr/bin, /bin. For introduction, examples, and details see in the build directory man xorriso-dd-target/xorriso-dd-target.1 info xorriso-dd-target/xorriso-dd-target.info Testing For automated and manual tests of xorriso's functionality see file releng/README. Result comparison with self produced ISO images We are quite sure that libisofs produces accurate representations of the disk files. This opinion is founded on a lot of test burns and checks by a little test program which compares files from the mounted image with the orignals on disk. It uses the normal POSIX filesystem calls, i.e. no libburnia stuff. This program is not installed systemwide but stays in the installation directory of the libisoburn tarball as test/compare_file . Usually it is run as -exec payload of a find command. It demands at least three arguments: The path of the file to compare, the prefix1 to be cut off from path and the prefix2 which gets prepended afterwards to obtain the path of the second file to compare. As further argument there can be -no_ctime which suppresses the comparison of ctime date stamps. The exit value is 0 if no difference was detected, non-0 else. Example: After xorriso ... -pathspecs on -add /=/original/dir -- mount /media/dvd cd test compare tree /media/dvd with tree /original/dir : find /original/dir -exec ./compare_file '{}' /original/dir /media/dvd ';' \ | less and vice versa: find /media/dvd -exec ./compare_file '{}' /media/dvd /original/dir ';' \ | less ------------------------------------------------------------------------------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later as published by the Free Software Foundation. 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 ------------------------------------------------------------------------------ Based on and sub project of: libburnia-project.org By Mario Danic <mario.danic@gmail.com>, Vreixo Formoso <metalpain2002@yahoo.es> Thomas Schmitt <scdbackup@gmx.net> Copyright (C) 2006-2019 Mario Danic, Vreixo Formoso, Thomas Schmitt. We will not raise any legal protest to dynamic linking of our libraries with applications that are not under GPL, as long as they fulfill the condition of offering the library source code used, whether altered or unaltered, under the GPLv2+, along with the application. Nevertheless, the safest legal position is not to link libburn with non-GPL compatible programs. libburnia-project.org is inspired by and in other components still containing parts of old Libburn. By Derek Foreman <derek@signalmarketing.com> and Ben Jansens <xor@orodu.net> Copyright (C) 2002-2006 Derek Foreman and Ben Jansens libisoburn does not stem from their code. The libisoburn release contains xorriso-dd-target Copyright (C) 2019 Nio Wiklund alias sudodus, Thomas Schmitt [Task] Figure out how to use "Requires" in pc.in (libisoburn and libisofs would benefit) [Task] Figure out the usage of Libs.private (used in libburn) [Task] Improve build system [Task] Investigate build system, so other libburnia components can benefit [Task] Write Doxygen files [Task] Explain to Thomas & Vreixo about NEWS importance (all libburnia components will benefit [Task] Write a document about ABI & API [Task] Create following targets for make: Src, Indent, Docs, Test, All [Any other suggestions?) All those tasks are currently assigned to Mario. dnl Copyright (C) 2008 - 2019 Thomas Schmitt <scdbackup@gmx.net> dnl Copyright (C) 2000-2002, 2007-2009 Free Software Foundation, Inc. dnl Provided under GPL version 2 or later. AC_DEFUN([LIBBURNIA_SET_FLAGS], [ case $target_os in freebsd*) LDFLAGS="$LDFLAGS -L/usr/local/lib" CPPFLAGS="$CPPFLAGS -I/usr/local/include" ;; netbsd*) LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/pkg/lib" CPPFLAGS="$CPPFLAGS -I/usr/local/include -I/usr/pkg/include" ;; solaris*) LDFLAGS="$LDFLAGS -L/usr/local/lib" esac ]) AC_DEFUN([TARGET_SHIZZLE], [ ARCH="" LIBBURNIA_PKGCONFDIR="$libdir"/pkgconfig AC_MSG_CHECKING([target operating system]) libburn_check_libcam= LIBBURNIA_SUPP_ACL=none LIBBURNIA_SUPP_FATTR=none LIBBURNIA_LDCONFIG_CMD="echo 'No ldconfig run performed. If needed, configure manually for:'" case $target in *-*-linux*) ARCH=linux LIBBURN_ARCH_LIBS= LIBBURNIA_SUPP_ACL=libacl LIBBURNIA_SUPP_FATTR=xattr LIBBURNIA_LDCONFIG_CMD=ldconfig ;; *-*-freebsd*) ARCH=freebsd LIBBURN_ARCH_LIBS=-lcam LIBBURNIA_SUPP_ACL=libacl LIBBURNIA_SUPP_FATTR=extattr # This may later be overridden by configure --enable-libdir-pkgconfig LIBBURNIA_PKGCONFDIR=$(echo "$libdir" | sed 's/\/lib$/\/libdata/')/pkgconfig ;; *-kfreebsd*-gnu*) ARCH=freebsd LIBBURN_ARCH_LIBS=-lcam libburn_check_libcam=yes ;; *-solaris*) ARCH=solaris LIBBURN_ARCH_LIBS=-lvolmgt ;; *) ARCH= LIBBURN_ARCH_LIBS= # AC_ERROR([You are attempting to compile for an unsupported platform]) ;; esac AC_MSG_RESULT([$ARCH]) if test x"$libburn_check_libcam" = xyes then LIBBURNIA_CHECK_LIBCAM fi ]) dnl LIBBURNIA_CHECK_ICONV is by Thomas Schmitt, libburnia project dnl It is based on gestures from: dnl iconv.m4 serial AM7 (gettext-0.18) dnl Copyright (C) 2000-2002, 2007-2009 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. dnl AC_DEFUN([LIBBURNIA_CHECK_ICONV], [ dnl Check whether it is allowed to link with -liconv AC_MSG_CHECKING([for iconv() in separate -liconv ]) libburnia_liconv="no" libburnia_save_LIBS="$LIBS" LIBS="$LIBS -liconv" AC_TRY_LINK([#include <stdlib.h> #include <iconv.h>], [iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);], [libburnia_liconv="yes"], [LIBS="$libburnia_save_LIBS"] ) AC_MSG_RESULT([$libburnia_liconv]) if test x"$libburnia_save_LIBS" = x"$LIBS" then dnl GNU iconv has no function iconv() but libiconv() and a macro iconv() dnl It is not tested whether this is detected by above macro. AC_CHECK_LIB(iconv, libiconv, , ) fi dnl Check for iconv(..., const char **inbuf, ...) AC_MSG_CHECKING([for const qualifier with iconv() ]) AC_TRY_COMPILE([ #include <stdlib.h> #include <iconv.h> size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); ], [], [libburnia_iconv_const=""], [libburnia_iconv_const="const"] ) if test x$libburnia_iconv_const = xconst then AC_DEFINE_UNQUOTED([ICONV_CONST], [const]) else AC_DEFINE_UNQUOTED([ICONV_CONST], []) fi test -z "$libburnia_iconv_const" && libburnia_iconv_const="no" AC_MSG_RESULT([$libburnia_iconv_const]) ]) dnl LIBBURNIA_ASSERT_ICONV is by Thomas Schmitt, libburnia project dnl AC_DEFUN([LIBBURNIA_ASSERT_ICONV], [ if test x$XORRISO_ASSUME_ICONV = x then dnl Check for the essential gestures of libisofs/util.c AC_MSG_CHECKING([for iconv() to be accessible now ]) AC_TRY_LINK([ #include <stdlib.h> #include <wchar.h> #include <string.h> #include <errno.h> #include <ctype.h> #include <stdio.h> #include <limits.h> #include <iconv.h> #include <locale.h> #include <langinfo.h> #include <unistd.h>], [iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd); ], [iconv_test="yes"], [iconv_test="no"] ) AC_MSG_RESULT([$iconv_test]) if test x$iconv_test = xno then echo >&2 echo "Cannot get function iconv() to work. Configuration aborted." >&2 echo "Check whether your system needs a separate libiconv installed." >&2 echo "If it is installed but not found, try something like" >&2 echo ' export LDFLAGS="$LDFLAGS -L/usr/local/lib"' >&2 echo ' export CPPFLAGS="$CPPFLAGS -I/usr/local/include"' >&2 echo ' export LIBS="$LIBS -liconv"' >&2 echo "You may override this test by exporting variable" >&2 echo " XORRISO_ASSUME_ICONV=yes" >&2 echo >&2 (exit 1); exit 1; fi fi ]) dnl LIBBURNIA_TRY_EDITLINE is by Thomas Schmitt, libburnia project dnl It performs the actual test compilation for editline. dnl Variable LIBS has to be set by the caller. AC_DEFUN([LIBBURNIA_TRY_EDITLINE], [ AC_TRY_LINK([ #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <histedit.h>], [EditLine *editline_handle; History *editline_history; HistEvent ev; int count; editline_handle= el_init("dummy", stdin, stdout, stderr); el_set(editline_handle, EL_EDITOR, "emacs"); editline_history= history_init(); history(editline_history, &ev, H_SETSIZE, 1000); el_gets(editline_handle, &count); ], [editline_test="yes"], [editline_test="no"] ) ]) dnl LIBBURNIA_ASSERT_EDITLINE is by Thomas Schmitt, libburnia project dnl It disables xorriso editline if not all needed functions are present AC_DEFUN([LIBBURNIA_ASSERT_EDITLINE], [ if test x$XORRISO_ASSUME_EDITLINE = x then dnl Check for the essential gestures of xorriso/text_io.c AC_MSG_CHECKING([for desired functions in libedit]) libburnia_save_LIBS="$LIBS" LIBS="$LIBS -ledit" LIBBURNIA_TRY_EDITLINE if test x$editline_test = xno then LIBS="$libburnia_save_LIBS" LIBS="$LIBS -ledit" LIBBURNIA_TRY_EDITLINE fi if test x$editline_test = xno then READLINE_DEF= LIBS="$libburnia_save_LIBS" fi AC_MSG_RESULT([$editline_test $editline_msg]) fi ]) dnl LIBBURNIA_TRY_READLINE is by Thomas Schmitt, libburnia project dnl It performs the actual test compilation for readline. dnl Variable LIBS has to be set by the caller. AC_DEFUN([LIBBURNIA_TRY_READLINE], [ AC_TRY_LINK([ #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <readline/readline.h> #include <readline/history.h>], [HIST_ENTRY **hl; readline(""); add_history(""); hl= history_list(); ], [readline_test="yes"], [readline_test="no"] ) ]) dnl LIBBURNIA_ASSERT_READLINE is by Thomas Schmitt, libburnia project dnl It disables xorriso readline if not all needed functions are present AC_DEFUN([LIBBURNIA_ASSERT_READLINE], [ if test x$XORRISO_ASSUME_READLINE = x then dnl Check for the essential gestures of xorriso/text_io.c AC_MSG_CHECKING([for desired functions in libreadline]) readline_msg= libburnia_save_LIBS="$LIBS" LIBS="$LIBS -lreadline" LIBBURNIA_TRY_READLINE if test x$readline_test = xno then LIBS="$libburnia_save_LIBS" LIBS="$LIBS -lreadline -lcurses" LIBBURNIA_TRY_READLINE if test x$readline_test = xyes then readline_msg=", with -lcurses" fi fi if test x$readline_test = xno then READLINE_DEF= LIBS="$libburnia_save_LIBS" fi AC_MSG_RESULT([$readline_test $readline_msg]) fi ]) dnl LIBISOBURN_ASSERT_VERS_LIBS is by Thomas Schmitt, libburnia project dnl It tests whether -Wl,--version-script=... works with the compiler AC_DEFUN([LIBISOBURN_ASSERT_VERS_LIBS], [ libburnia_save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -Wl,--version-script=$srcdir/libisoburn/libisoburn.ver" AC_TRY_LINK([#include <stdio.h>], [printf("Hello\n");], [vers_libs_test="yes"], [vers_libs_test="no"]) if test x$vers_libs_test = xyes then LIBLDFLAGS="-Wl,--version-script=$srcdir/libisoburn/libisoburn.ver" fi LDFLAGS="$libburnia_save_LDFLAGS" AC_SUBST(LIBLDFLAGS) ]) dnl LIBBURNIA_SET_PKGCONFIG is by Thomas Schmitt, libburnia project dnl It determines the install directory for the *.pc file. dnl Important: Must be performed _after_ TARGET_SHIZZLE dnl AC_DEFUN([LIBBURNIA_SET_PKGCONFIG], [ ### for testing --enable-libdir-pkgconfig on Linux ### LIBBURNIA_PKGCONFDIR="$libdir"data/pkgconfig if test "x$LIBBURNIA_PKGCONFDIR" = "x$libdir"/pkgconfig then dummy=dummy else AC_ARG_ENABLE(libdir-pkgconfig, [ --enable-libdir-pkgconfig Install to $libdir/pkgconfig on any OS, default=no], , enable_libdir_pkgconfig="no") AC_MSG_CHECKING([for --enable-libdir-pkgconfig]) if test "x$enable_libdir_pkgconfig" = xyes then LIBBURNIA_PKGCONFDIR="$libdir"/pkgconfig fi AC_MSG_RESULT([$enable_libdir_pkgconfig]) fi libburnia_pkgconfig_override="no" AC_ARG_ENABLE(pkgconfig-path, [ --enable-pkgconfig-path=DIR Absolute path of directory for libisofs-*.pc], libburnia_pkgconfig_override="yes" , enable_pkgconfig_path="none") AC_MSG_CHECKING([for overridden pkgconfig directory path]) if test "x$enable_pkgconfig_path" = xno then libburnia_pkgconfig_override="no" fi if test "x$enable_pkgconfig_path" = x -o "x$enable_pkgconfig_path" = xyes then libburnia_pkgconfig_override="invalid argument" fi if test "x$libburnia_pkgconfig_override" = xyes then LIBBURNIA_PKGCONFDIR="$enable_pkgconfig_path" AC_MSG_RESULT([$LIBBURNIA_PKGCONFDIR]) else AC_MSG_RESULT([$libburnia_pkgconfig_override]) fi AC_SUBST(LIBBURNIA_PKGCONFDIR) dnl For debugging only ### AC_MSG_RESULT([LIBBURNIA_PKGCONFDIR = $LIBBURNIA_PKGCONFDIR]) ]) dnl LIBBURNIA_TRY_TIMEZONE is by Thomas Schmitt, libburnia project dnl It tests whether the global variable exists and is suitable for dnl integer arithmetics. AC_DEFUN([LIBBURNIA_TRY_TIMEZONE], [ echo -n "checking for timezone variable ... " AC_TRY_LINK([ #include <time.h> ], [long int i; i = 1 - timezone; ], [LIBBURNIA_TIMEZONE="timezone"], [LIBBURNIA_TIMEZONE="0"] ) echo "$LIBBURNIA_TIMEZONE" ]) dnl LIBBURNIA_CHECK_ARCH_LIBS is by Thomas Schmitt, libburnia project dnl It tests whether the OS dependent libraries are available. dnl With libisoburn they are needed only for the case that indirect linking dnl does not work. So it is worth a try to omit them. dnl $1 = "mandatory" or "optional" define the action if test linking fails. dnl "silent" is like "optional" but without message. AC_DEFUN([LIBBURNIA_CHECK_ARCH_LIBS], [ libburnia_save_LIBS="$LIBS" if test "x$LIBBURN_ARCH_LIBS" = x then dummy=dummy else LIBS="$LIBS $LIBBURN_ARCH_LIBS" AC_TRY_LINK([#include <stdio.h>], [printf("Hello\n");], [archlibs_test="yes"], [archlibs_test="no"]) LIBS="$libburnia_save_LIBS" if test x$archlibs_test = xno then if test x"$1" = xmandatory then echo >&2 echo "FATAL: Test linking with mandatory library options failed: $LIBBURN_ARCH_LIBS" >&2 echo >&2 (exit 1); exit 1; else if test x"$1" = xoptional then echo "disabled linking with $LIBBURN_ARCH_LIBS (because not found)" fi LIBBURN_ARCH_LIBS="" fi else if test x"$1" = xsilent then dummy=dummy else echo "enabled linking with $LIBBURN_ARCH_LIBS" fi fi fi ]) dnl LIBBURNIA_CHECK_LINUX_SCSI is by Thomas Schmitt, libburnia project dnl AC_DEFUN([LIBBURNIA_CHECK_LINUX_SCSI], [ dnl Check whether it is a Linux without scsi/scsi.h libburn_scsi_disabled= if test x"$ARCH" = xlinux then AH_TEMPLATE([Libburn_use_sg_dummY], [Define to compile without OS specific SCSI features]) AC_MSG_CHECKING([for missing scsi/scsi.h on Linux]) AC_TRY_COMPILE([ #ifdef __linux #include <scsi/scsi.h> #endif ], [;], [AC_MSG_RESULT([no])], [AC_DEFINE([Libburn_use_sg_dummY], [yes]) libburn_scsi_disabled=yes AC_MSG_RESULT([yes])] ) fi if test x"$libburn_scsi_disabled" = xyes then echo "disabled operation of optical drives via SCSI" fi ]) dnl LIBBURNIA_CHECK_LIBCAM is by Thomas Schmitt, libburnia project dnl AC_DEFUN([LIBBURNIA_CHECK_LIBCAM], [ dnl Check whether libcam is requested for FreeBSD kernel but missing libburn_scsi_disabled= if test x"$LIBBURN_ARCH_LIBS" = x"-lcam" then AH_TEMPLATE([Libburn_use_sg_dummY], [Define to compile without OS specific SCSI features]) AC_MSG_CHECKING([for missing libcam for SCSI on FreeBSD kernel]) dnl If libcam is not available, LIBBURN_ARCH_LIBS will be made empty LIBBURNIA_CHECK_ARCH_LIBS(silent) if test x"$LIBBURN_ARCH_LIBS" = x then AC_DEFINE([Libburn_use_sg_dummY], [yes]) libburn_scsi_disabled=yes AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi fi if test x"$LIBBURN_ARCH_LIBS" = x"-lcam" then AC_MSG_CHECKING([for missing libcam headers]) AC_TRY_COMPILE([ #include <stdio.h> #include <camlib.h> #include <cam/scsi/scsi_message.h> #include <cam/scsi/scsi_pass.h> ], [;], [AC_MSG_RESULT([no])], [AC_DEFINE([Libburn_use_sg_dummY], [yes]) libburn_scsi_disabled=yes AC_MSG_RESULT([yes])] ) fi if test x"$libburn_scsi_disabled" = xyes then echo "disabled operation of optical drives via SCSI" fi ]) #!/bin/sh -x aclocal -I . libtoolize --copy --force autoconf # Not with libisoburn # autoheader automake --foreign --add-missing --copy --include-deps AC_INIT([libisoburn], [1.5.7], [http://libburnia-project.org]) AC_PREREQ([2.50]) dnl AC_CONFIG_HEADER([config.h]) AC_CANONICAL_HOST AC_CANONICAL_TARGET LIBBURNIA_SET_FLAGS AM_INIT_AUTOMAKE([subdir-objects]) AC_CONFIG_MACRO_DIR([./]) dnl Hint: Search list for version code aspects: dnl /AC_INIT dnl /ISOBURN_.*_VERSION dnl /LT_.* dnl /LIB.*_REQUIRED dnl The API version codes are defined in libisoburn/libisoburn.h dnl #define isoburn_header_version_* dnl configure.ac only rules the libtool revision numbering about dnl LT_CURREN, LT_AGE, LT_REVISION where SONAME becomes LT_CURRENT - LT_AGE dnl dnl These three are only copies to provide libtool with unused LT_RELEASE ISOBURN_MAJOR_VERSION=1 ISOBURN_MINOR_VERSION=5 ISOBURN_MICRO_VERSION=7 dnl ISOBURN_VERSION=$ISOBURN_MAJOR_VERSION.$ISOBURN_MINOR_VERSION.$ISOBURN_MICRO_VERSION AC_SUBST(ISOBURN_MAJOR_VERSION) AC_SUBST(ISOBURN_MINOR_VERSION) AC_SUBST(ISOBURN_MICRO_VERSION) dnl AC_SUBST(ISOBURN_VERSION) dnl Libtool versioning dnl Generate libisoburn.so.1.x.y dnl SONAME will become LT_CURRENT - LT_AGE dnl dnl ts C30607 dnl ### This is the release version 1.5.6 = libisoburn.so.1.113.0 dnl This is the development version after above stable release dnl ### LT_CURRENT++, LT_AGE++ have not happened yet. dnl LT_CURRENT++, LT_AGE++ has happened meanwhile. dnl dnl SONAME = 115 - 114 = 1 . Library name = libisoburn.so.1.114.0 LT_RELEASE=$ISOBURN_MAJOR_VERSION.$ISOBURN_MINOR_VERSION LT_CURRENT=115 LT_AGE=114 LT_REVISION=0 LT_CURRENT_MINUS_AGE=`expr $LT_CURRENT - $LT_AGE` AC_SUBST(LT_RELEASE) AC_SUBST(LT_CURRENT) AC_SUBST(LT_REVISION) AC_SUBST(LT_AGE) AC_SUBST(LT_CURRENT_MINUS_AGE) AC_PREFIX_DEFAULT([/usr/local]) test "$prefix" = "NONE" && prefix=$ac_default_prefix dnl ts B90405 : Disabled on advise of Ross Burton dnl AM_MAINTAINER_MODE AM_PROG_CC_C_O AC_C_CONST AC_C_INLINE dnl Large file support AC_SYS_LARGEFILE AC_FUNC_FSEEKO AC_CHECK_FUNC([fseeko]) if test ! $ac_cv_func_fseeko; then AC_ERROR([Libburn requires largefile support.]) fi if test x$LIBISOBURN_OLD_ICONV_CONFIGURE = x then dnl ts B00410 : To detect the need for -liconv and const argument of iconv() LIBBURNIA_CHECK_ICONV else dnl Outdated: produces double -liconv and warnings about parameter mismatch dnl If iconv(3) is in an extra lib, then it gets added to variable LIBS. dnl If not, then no -liconv will be added. AC_CHECK_LIB(iconv, iconv, , ) dnl GNU iconv has no function iconv() but libiconv() and a macro iconv() AC_CHECK_LIB(iconv, libiconv, , ) fi AC_PROG_LIBTOOL AC_SUBST(LIBTOOL_DEPS) # LIBTOOL="$LIBTOOL --silent" AC_PROG_INSTALL AC_CHECK_HEADERS() dnl Check for tm_gmtoff field in struct tm AC_CHECK_MEMBER([struct tm.tm_gmtoff], [AC_DEFINE(HAVE_TM_GMTOFF, 1, [Define this if tm structure includes a tm_gmtoff entry.])], , [#include <time.h>]) dnl Whether timezone is an integer variable AH_TEMPLATE([Libburnia_timezonE], [Either timezone or 0]) LIBBURNIA_TRY_TIMEZONE if test x$LIBBURNIA_TIMEZONE = xtimezone then AC_DEFINE([Libburnia_timezonE], [timezone]) else AC_DEFINE([Libburnia_timezonE], [0]) fi THREAD_LIBS=-lpthread AC_SUBST(THREAD_LIBS) TARGET_SHIZZLE AC_SUBST(ARCH) AC_SUBST(LIBBURNIA_PKGCONFDIR) AC_SUBST(LIBBURN_ARCH_LIBS) # Do not link libcam or libvolmgt if not available. # (They are indirect dependency on FreeBSD or Solaris.) LIBBURNIA_CHECK_ARCH_LIBS(optional) dnl Determine target directory for libisoburn-*.pc dnl Important: Must be performed _after_ TARGET_SHIZZLE dnl LIBBURNIA_SET_PKGCONFIG dnl xorriso-dd-target is addicted to the Linux kernel and util-linux lsblk if uname -s | grep '^Linux' >/dev/null then XORRISO_DD_TARGET=xorriso-dd-target/xorriso-dd-target XORRISO_DD_TARGET_MAN=xorriso-dd-target/xorriso-dd-target.1 XORRISO_DD_TARGET_TEXI=xorriso-dd-target/xorriso-dd-target.texi echo "enabled installation of xorriso-dd-target/xorriso-dd-target" else XORRISO_DD_TARGET= XORRISO_DD_TARGET_MAN= XORRISO_DD_TARGET_TEXI= echo "disabled installation of xorriso-dd-target/xorriso-dd-target" fi AC_SUBST(XORRISO_DD_TARGET) AC_SUBST(XORRISO_DD_TARGET_MAN) AC_SUBST(XORRISO_DD_TARGET_TEXI) AC_ARG_ENABLE(libreadline, [ --enable-libreadline Enable use of libreadline by xorriso, default=yes], , enable_libreadline=yes) if test x$enable_libreadline = xyes; then dnl Check whether there is readline-devel and readline-runtime. dnl If not, erase this macro which would enable use of readline(),add_history() READLINE_DEF="-DXorriso_with_readlinE" if test x$XORRISO_OLD_READLINE_CONFIGURE = x then dnl ts B00411 : To disable readline if not all needed functions are present LIBBURNIA_ASSERT_READLINE else dnl The empty yes case obviously causes -lreadline to be linked AC_CHECK_HEADER(readline/readline.h, AC_CHECK_LIB(readline, readline, , READLINE_DEF= ), READLINE_DEF= ) dnl The X= in the yes case prevents that -lreadline gets linked twice AC_CHECK_HEADER(readline/history.h, AC_CHECK_LIB(readline, add_history, X= , READLINE_DEF= ), READLINE_DEF= ) fi else READLINE_DEF= echo "disabled libreadline" fi if test x$READLINE_DEF = x; then if test x$enable_libreadline = xyes; then libedit_deflt=yes else libedit_deflt=no fi AC_ARG_ENABLE(libedit, [ --enable-libedit Enable use of libedit by xorriso if not libreadline, default= setting of --enable-libreadline], , enable_libedit=$libedit_deflt) if test x$enable_libedit = xyes; then READLINE_DEF="-DXorriso_with_editlinE" LIBBURNIA_ASSERT_EDITLINE else READLINE_DEF= echo "disabled libedit" fi fi AC_SUBST(READLINE_DEF) dnl ts A90329 dnl ACL and xattr do not need to be enabled in libisoburn or xorriso source dnl but without AC_CHECK_LIB() xorriso will not be linked with -lacl . dnl On my Linux this does work with an ACL enabled libisofs but in general dnl it seems not be right. dnl So for now it seems to be best to do the same configuration for libisoburn dnl and xorriso as for libisofs. AC_ARG_ENABLE(libacl, [ --enable-libacl Enable use of libacl by libisofs, default=yes], , enable_libacl=yes) LIBACL_DEF= if test x$LIBBURNIA_SUPP_ACL = xlibacl then if test x$enable_libacl = xyes; then dnl Check whether there is libacl-devel and libacl-runtime. dnl If not, erase this macro which would enable use of acl_to_text and others LIBACL_DEF="-DLibisofs_with_aaip_acL" dnl The empty yes case obviously causes -lacl to be linked has_acl_h_but_no_func=0 AC_CHECK_HEADER(sys/acl.h, AC_CHECK_LIB(acl, acl_to_text, , has_acl_h_but_no_libacl=1 ), LIBACL_DEF= ) if test "$has_acl_h_but_no_libacl" = 1 then AC_CHECK_LIB(c, acl_to_text, X= , LIBACL_DEF= ) fi fi fi if test x$LIBACL_DEF = x-DLibisofs_with_aaip_acL then if test x$has_acl_h_but_no_libacl = x1 then echo "enabled local processing of ACL" else echo "enabled libacl, local processing of ACL" fi else echo "disabled local processing of ACL" fi AC_SUBST(LIBACL_DEF) AC_ARG_ENABLE(xattr, [ --enable-xattr Enable use of extended file attributes by libisofs, default=yes], , enable_xattr=yes) XATTR_DEF= if test x"$LIBBURNIA_SUPP_FATTR" = xxattr then if test "x$enable_xattr" = xyes; then dnl Check whether there is the header for Linux xattr. dnl If not, erase this macro which would enable use of listxattr and others XATTR_DEF="-DLibisofs_with_aaip_xattR" AC_CHECK_HEADER(attr/xattr.h, AC_CHECK_LIB(c, listxattr, X= , XATTR_DEF= ), XATTR_DEF= ) fi elif test x"$LIBBURNIA_SUPP_FATTR" = xextattr then if test "x$enable_xattr" = xyes; then XATTR_DEF="-DLibisofs_with_freebsd_extattR" AC_CHECK_HEADER(sys/extattr.h, AC_CHECK_LIB(c, extattr_list_file, X=, XATTR_DEF= ), XATTR_DEF= ) fi fi if test x$XATTR_DEF = x-DLibisofs_with_aaip_xattR then echo "enabled xattr, local processing of extended file attributes Linux style" elif test x$XATTR_DEF = x-DLibisofs_with_freebsd_extattR then echo "enabled extattr, local processing of extended file attributes FreeBSD style" else echo "disabled local processing of extended file attributes" fi AC_SUBST(XATTR_DEF) dnl ts A90409 dnl Same situation as with xattr and ACL: libisoburn does not depend directly dnl on zlib. But if it is enabled in libisofs then it seems wise to link it dnl with libisoburn apps. AC_ARG_ENABLE(zlib, [ --enable-zlib Enable use of zlib by libisofs, default=yes], , enable_zlib=yes) if test x$enable_zlib = xyes; then dnl Check whether there is the header for zlib. dnl If not, erase this macro which would enable use of compress2() and others. dnl Linking fails on SuSE 9.0 because zlib has compress2() but lacks dnl compressBound(). So compressBound is the more modern thing to test. dnl The empty parameter after "compressBound" causes -lz. ZLIB_DEF="-DLibisofs_with_zliB" AC_CHECK_HEADER(zlib.h, AC_CHECK_LIB(z, compressBound, , ZLIB_DEF= ), ZLIB_DEF= ) else ZLIB_DEF= fi AC_SUBST(ZLIB_DEF) dnl ts B00928 AC_ARG_ENABLE(libjte, [ --enable-libjte Enable use of libjte >= 2.0 by xorriso, default=yes], , enable_libjte=yes) if test "x$enable_libjte" = xyes; then LIBJTE_DEF="-DXorriso_with_libjtE" AC_CHECK_HEADER(libjte/libjte.h, AC_CHECK_LIB(jte, libjte_set_checksum_algorithm, , LIBJTE_DEF= ), LIBJTE_DEF= ) else LIBJTE_DEF= fi AC_SUBST(LIBJTE_DEF) dnl ts B00107 dnl Just for the case that it is necessary to give link option -lcdio not only dnl with libburn but also with libburn apps like xorriso. dnl On SuSE 10.2 this is not needed. libburn finds libcdio on its own. case $host_os in cygwin*|mingw*) default_libcdio=yes ;; *) default_libcdio=no ;; esac AC_ARG_ENABLE(libcdio, [ --enable-libcdio Enable use of libcdio as system adapter, default=no (except on MSWindows)], , enable_libcdio=$default_libcdio) if test x$enable_libcdio = xyes; then dnl Check whether there is libcdio-devel and libcdio-runtime. dnl If not, erase this macro LIBCDIO_DEF="-DLibburn_use_libcdiO" dnl The empty yes case obviously causes -lcdio to be linked AC_CHECK_HEADER(cdio/cdio.h, AC_CHECK_LIB(cdio, mmc_last_cmd_sense, , LIBCDIO_DEF= ), LIBCDIO_DEF= ) else LIBCDIO_DEF= fi if test x$LIBCDIO_DEF = x then if test x$enable_libcdio = xyes then echo "WARNING: could not enable use of libcdio as system adapter" fi else echo "enabled use of libcdio as system adapter" fi AC_SUBST(LIBCDIO_DEF) AC_ARG_ENABLE(external-filters, [ --enable-external-filters Enable use of external filter programs by xorriso, default=yes], , enable_external_filters=yes) if test x"$enable_external_filters" = xyes; then EXTF_DEF="-DXorriso_allow_external_filterS" echo "enabled xorriso external filter programs" else EXTF_DEF= echo "disabled xorriso external filter programs" fi AC_SUBST(EXTF_DEF) AC_ARG_ENABLE(external-filters-setuid, [ --enable-external-filters-setuid Enable xorriso external filter programs under setuid, default=no], , enable_external_filters_setuid=no) if test x$enable_external_filters_setuid = xyes; then EXTF_SUID_DEF="-DXorriso_allow_extf_suiD" echo "enabled xorriso external filter programs under setuid" else EXTF_SUID_DEF= echo "disabled xorriso external filter programs under setuid" fi AC_SUBST(EXTF_SUID_DEF) AC_ARG_ENABLE(launch-frontend, [ --enable-launch-frontend Enable start of piped frontend program by xorriso, default=yes], , enable_launch_frontend=yes) if test x"$enable_launch_frontend" = xyes; then LFRONT_DEF="-DXorriso_allow_launch_frontenD" echo "enabled xorriso command -launch_frontend" else LFRONT_DEF= echo "disabled xorriso command -launch_frontend" fi AC_SUBST(LFRONT_DEF) AC_ARG_ENABLE(launch-frontend-setuid, [ --enable-launch-frontend-setuid Enable start of piped frontend program under setuid, default=no], , enable_launch_frontend_setuid=no) if test x$enable_launch_frontend_setuid = xyes; then LFRONT_SUID_DEF="-DXorriso_allow_extf_suiD" echo "enabled xorriso command -launch_frontend under setuid" else LFRONT_SUID_DEF= echo "disabled xorriso command -launch_frontend under setuid" fi AC_SUBST(LFRONT_SUID_DEF) AC_ARG_ENABLE(dvd-obs-64k, [ --enable-dvd-obs-64k 64 KB default size for xorriso DVD writing, default=no], , enable_dvd_obs_64=no) if test x$enable_dvd_obs_64k = xyes; then XORRISO_DVD_OBS_64K="-DXorriso_dvd_obs_default_64K" echo "enabled xorriso write size default 64 KB on DVD" else XORRISO_DVD_OBS_64K= echo "disabled xorriso write size default 64 KB on DVD" fi AC_SUBST(XORRISO_DVD_OBS_64K) # Library versioning normally serves a complex purpose. # Since libisoburn obeys strict ABI backward compatibility, it needs only the # simple feature to declare function names "global:" or "local:". Only the # global ones are visible to applications at library load time. AC_ARG_ENABLE(versioned-libs, [ --enable-versioned-libs Enable strict symbol encapsulation , default=yes], , enable_versioned_libs=yes) if test x$enable_versioned_libs = xyes; then vers_libs_test=no LIBISOBURN_ASSERT_VERS_LIBS if test x$vers_libs_test = xno then echo "disabled strict symbol encapsulation (test failed)" else echo "enabled strict symbol encapsulation" fi else echo "disabled strict symbol encapsulation" fi AC_ARG_ENABLE(ldconfig-at-install, [ --enable-ldconfig-at-install On GNU/Linux run ldconfig, default=yes], , ldconfig_at_install=yes) if test x$ldconfig_at_install = xyes; then dummy=dummy else LIBBURNIA_LDCONFIG_CMD="echo 'NOTE: ldconfig is disabled. If needed, configure manually for:'" echo "disabled run of ldconfig during installation on GNU/Linux" fi AC_SUBST(LIBBURNIA_LDCONFIG_CMD) AC_CHECK_HEADER(libburn/libburn.h, LIBBURNIA_HAVE_LIBBURN=1, LIBBURNIA_HAVE_LIBBURN=0) AC_CHECK_HEADER(libisofs/libisofs.h, LIBBURNIA_HAVE_LIBISOFS=1, LIBBURNIA_HAVE_LIBISOFS=0) if test x$LIBBURNIA_HAVE_LIBBURN = x0; then echo "FATAL: Lacking libburn development header file <libburn/libburn.h>" 2>&1 echo "HINT: You first have to install libburn before you can build libisoburn" 2>&1 fi if test x$LIBBURNIA_HAVE_LIBISOFS = x0; then echo "FATAL: Lacking libisofs development header file <libisofs/libisofs.h>" 2>&1 echo "HINT: You first have to install libisofs before you can build libisoburn" 2>&1 fi if test x$LIBBURNIA_HAVE_LIBBURN = x0 -o x$LIBBURNIA_HAVE_LIBISOFS = x0; then echo "ABORT: Lacking mandatory prerequisites" 2>&1 exit 1 fi # ------- Visible mark in configure : Start of library check # Check for proper library versions if this is desired. # (It fails too often on too many systems.) AC_ARG_ENABLE(pkg-check-modules, [ --enable-pkg-check-modules Enable pkg-config check for libburn and libisofs , default=no], , enable_pkg_check_modules=no) if test x$enable_pkg_check_modules = xyes; then dnl If PKG_CHECK_MODULES is to be used after this if-block, dnl then it might be necessary to perform PKG_PROG_PKG_CONFIG before the block. LIBBURN_REQUIRED=1.5.7 LIBISOFS_REQUIRED=1.5.6 PKG_CHECK_MODULES(LIBBURN, libburn-1 >= $LIBBURN_REQUIRED) PKG_CHECK_MODULES(LIBISOFS, libisofs-1 >= $LIBISOFS_REQUIRED) if test x$LIBCDIO_DEF = x; then dummy=dummy else LIBCDIO_REQUIRED=0.83 PKG_CHECK_MODULES(LIBCDIO, libcdio >= $LIBCDIO_REQUIRED) fi else echo "checking for LIBBURN... skipped, no --enable-pkg-check-modules" echo "checking for LIBISOFS... skipped, no --enable-pkg-check-modules" if test x$LIBCDIO_DEF = x; then dummy=dummy else echo "checking for LIBCDIO... skipped, no --enable-pkg-check-modules" fi fi # ------- Visible mark in configure : End of library check 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="-O3 $CFLAGS" CFLAGS="-fexpensive-optimizations $CFLAGS" fi CFLAGS="-DNDEBUG $CFLAGS" else if test x$GCC = xyes; then CFLAGS="-g -pedantic -Wall -Wextra -Wno-unused-parameter -Wno-char-subscripts $CFLAGS" fi CFLAGS="-DDEBUG $CFLAGS" fi CFLAGS="$READLINE_DEF $LIBACL_DEF $XATTR_DEF $EXTF_DEF $EXTF_SUID_DEF $LFRONT_DEF $LFRONT_SUID_DEF $ZLIB_DEF $LIBJTE_DEF $XORRISO_DVD_OBS_64K $CFLAGS" AC_CONFIG_FILES([ Makefile doc/doxygen.conf version.h libisoburn-1.pc ]) AC_OUTPUT /** @author Mario Danic, Vreixo Formoso, Thomas Schmitt @mainpage Libisoburn Documentation Index @section intro Introduction Libburnia is an open-source project for reading, mastering and writing optical discs. This page is about its capability to read, manipulate, and write ISO 9660 filesystems with Rock Ridge extensions. Media can be optical media or filesystem objects. Our scope is currently Linux 2.4 and 2.6, FreeBSD, OpenSolaris, or NetBSD. libisoburn is an add-on to libburn and libisofs which coordinates both and also allows to grow ISO-9660 filesystem images on multi-session media as well as on overwriteable media via the same API. All media peculiarities are handled automatically. xorriso is an application of all three libraries which creates, loads, manipulates and writes ISO 9660 filesystem images with Rock Ridge extensions. Manipulation is not only adding or overwriting of files but also deletion, renaming, and attribute changing. An incremental backup feature is provided. The xorriso features are accessible via built-in command interpreters and via a C language API. SONAME: libisoburn.so.1 (since 0.1.0, February 2008). @section using Using the libraries Our build system is based on autotools. User experience tells us that you will need at least autotools version 1.7. To build libisoburn go into its toplevel directory and execute: - ./bootstrap (needed if you downloaded from SVN) - ./configure - make To make the library and the xorriso application accessible for running and software development: - make install For direct use as command line tool use the xorriso binary which among many other features provides a mkisofs emulation via command "-as mkisofs". See man page xorriso/xorriso.1 or GNU info document xorriso/xorriso.info. If you want to link an own application with libisoburn, you have two alternative APIs for choice: - libisoburn, together with libburn and libisofs. - xorriso, a complete representation of xorriso command line options. It encapsulates the three lower level libraries. Calls of both API families shall not be mixed. For a description of the lbisoburn API read libisoburn/libisoburn.h See file README for download and installation of a release tarball. You will also have to install and understand the two libraries of the Libburnia project which provide fundamental services: libburn is the library by which preformatted data get onto optical media. See libburn/libburn.h for its API description. libisofs is the library to handle ISO 9660 filesystems with Rock Ridge extensions. Its API is described in libisofs/libisofs.h . For xorriso features see its man page xorriso/xorriso.1 or its GNU info document xorriso/xorriso.info. For the corresponding C language API see libisoburn/xorriso.h (or xorriso/xorriso.h in the build directory). The implementation this API is part of libisoburn. The xorriso command line tool gets installed as dynamically linked binary together with libisoburn. There is also a statically linked release named GNU xorriso. See xorriso/README_gnu_xorriso for its download and installation. */ # Doxyfile 1.5.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file that # follow. The default is UTF-8 which is also the encoding used for all text before # the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into # libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of # possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = @PACKAGE_NAME@ # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @PACKAGE_VERSION@ # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, # Italian, Japanese, Japanese-en (Japanese with English messages), Korean, # Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, # Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = @top_srcdir@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to # include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be extracted # and appear in the documentation as a namespace called 'anonymous_namespace{file}', # where file will be replaced with the base name of the file that contains the anonymous # namespace. By default anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from the # version control system). Doxygen will invoke the program by executing (via # popen()) the command <command> <input-file>, where <command> is the value of # the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = libisoburn \ doc \ test # This tag can be used to specify the character encoding of the source files that # doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default # input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. # See http://www.gnu.org/software/libiconv for the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py FILE_PATTERNS = libisoburn.h \ xorriso.h \ comments # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the output. # The symbol name can be a fully qualified name, a word, or if the wildcard * is used, # a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = test # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command <filter> <input-file>, where <filter> # is the value of the INPUT_FILTER tag, and <input-file> is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH # then you must also enable this option. If you don't then doxygen will produce # a warning and turn it on anyway SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = YES # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentstion. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = OB \ OTK \ _ #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = doc/html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 200 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = letter # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = DOXYGEN # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to # produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to # specify the directory where the mscgen tool resides. If left empty the tool is assumed to # be found in the default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) # ts B10415: dot causes sigsegv on Debian buildd HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a caller dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected # functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = NO # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the number # of direct children of the root node in a graph is already larger than # MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO ### Libburnia Frequently Asked Questions Please post your questions to [GNU xorriso mailing list](https://lists.gnu.org/mailman/listinfo/bug-xorriso). ---------------------------------------------------------------------------- ### Content: Google favorites: [xorriso not found](FAQ#xorriso_not_found) [xorriso tutorial](FAQ#xorriso_tutorial) [xorriso create ISO image](FAQ#xorriso_create_iso) Burning: [What is the difference between cdrskin and xorriso ?](FAQ#diff_cdrskin_xorriso) [What does that SCSI error message mean ?](FAQ#scsi_error) [Why is simultaneous burning with multiple drives so slow ?](FAQ#concurrent_burn) Imaging: [Is there a way to edit files inside the ISO image ?](FAQ#edit_files) [For which architectures xorriso is able to create bootable images ?](FAQ#boot_arch) [How to enable booting from USB stick ?](FAQ#isohybrid) [What is partition offset feature all about?](FAQ#partition_offset) [Partition offset bad on Apple ?](FAQ#partition_offset_apple) Development: [Where are the APIs of libburnia libraries described ?](FAQ#api_specs) [I want to write a GUI on the top of libburnia libraries. Any pointers or recommendations ?](FAQ#gui_advise) Miscellaneous: [Where to see examples ?](FAQ#example_links) [What personalities are supported by xorriso ?](FAQ#xorriso_aliases) [What is xorriso dialog mode useful for ?](FAQ#xorriso_dialog_mode) [Why is every second release missing ?](FAQ#version_numbers) ------------------------------------------------------------------------ ### Google favorites ------------------------------------------------------------------------ ##### xorriso not found <A NAME="xorriso_not_found"> This message is issued by programs which use [xorriso](Xorriso) for producing ISO 9660 filesystem images. E.g. by GRUB2's grub-mkrescue. Executable xorriso binaries are normally contained in software packages named "libisoburn" or "xorriso". If your operating system does not offer such a package, then consider to get the [GNU xorriso](http://www.gnu.org/software/xorriso#download) source tarball. For instructions read in its [README file](http://www.gnu.org/software/xorriso/README_xorriso) the paragraph "Compilation, First Glimpse, Installation". With grub-mkrescue it is possible to use the resulting binary without further installation. Just submit its absolute path with option `--xorriso=`. E.g.: ``` grub-mkrescue --xorriso=$HOME/xorriso-1.3.8/xorriso/xorriso -o output.iso ``` ##### xorriso tutorial <A NAME="xorriso_tutorial"> There is not much more than the [man xorriso examples](http://www.gnu.org/software/xorriso/man_1_xorriso.html#EXAMPLES) Volunteers are wanted who make a collection of use cases, ask at bug-xorriso for xorriso instructions to fulfill the needs, and describe both in a user-readable manner. Up to then, the GUI demo [xorriso-tcltk](https://dev.lovelyhq.com/libburnia/libisoburn/raw/branch/master/frontend/README-tcltk) [(screenshot)](http://www.gnu.org/software/xorriso/xorriso-tcltk-screen.gif) may serve as interactive exploration tool. It needs `xorriso` >= 1.2.6, `Tcl`, `Tk` >= 8.4, optionally Tcl / Tk package `BWidget`. ``` xorriso-tcltk --script_log_file - ``` starts the GUI and will log the essential xorriso commands in the start terminal. I.e. click on "Scan for drives" and learn that this operation is triggered by xorriso command `-devices`. Click the rightmost mouse button while being over any of the GUI elements in order to get the particular help text for that element. Have [man xorriso](http://www.gnu.org/software/xorriso/man_1_xorriso.html) ready to learn what the particular commands mean. ##### xorriso create ISO image <A NAME="xorriso_create_iso"> ``` xorriso -outdev $HOME/result.iso \ -map /home/me/sounds /sounds \ -map /home/me/pictures /pictures ``` This points the output to file `$HOME/result.iso`, which should not yet exist. Then it maps disk directory `/home/me/sounds` to ISO directory `/sounds`, and `/home/me/pictures` to `/pictures`. At program end, the ISO image gets produced and the contents of the two directory trees gets copied into the ISO. If you have experience with program `mkisofs`, you may also use its emulation by xorriso: ``` xorriso -as mkisofs \ -o $HOME/result.iso \ -graft-points \ /sounds=/home/me/sounds \ /pictures=/home/me/pictures ``` See [man xorriso](http://www.gnu.org/software/xorriso/man_1_xorriso.html) for xorriso native commands. See [man xorrisofs](http://www.gnu.org/software/xorriso/man_1_xorrisofs.html) for its mkisofs emulation. ---------------------------------------------------------------------------- ### Burning ---------------------------------------------------------------------------- ##### What is the difference between cdrskin and xorriso ? <A NAME="diff_cdrskin_xorriso"> [cdrskin](Cdrskin) is a dedicated emulator of program cdrecord, based on libburn. It tries to be as similar to cdrecord as is possible under that premise. [xorriso](Xorriso) is an integrated tool which creates, loads, manipulates, and writes ISO 9660 filesystem images with Rock Ridge extensions. It is based on libburn, libisofs, and libisoburn. One of its features is the emulation of the corresponding tasks as done by mkisofs and cdrecord. ##### What does that SCSI error message mean ? <A NAME="scsi_error"> Error messages labled as "SCSI" stem from the drive. They are codes of three hexadecimal numbers, like `[3 0C 00]`. The first number gives an overall classification of the problem. The other two numbers give the particular error description. libburn translates known error codes into text messages. They consist of two statements: the overall classification and the error description. E.g. `[3 0C 00] Medium error. Write error.` The classification allows a guess where the problem cause might sit: 2 `Drive not ready` : This is a well normal drive state and should be handled by libburn. If you see this outside of DEBUG messages then it happened at an unexpected occasion. Either libburn did its job wrong, or the hardware suffers from blackouts. Hardware can be: drive, cable, bus controller. Workaround: Check cables. If possible, try the drive at a different bus controller. 3 `Medium error` : This indicates a problem between drive and medium. libburn cannot directly cause such an error by any mistake. If drive and medium are balancing on the edge of defect, it is possible that optional settings can cause or prevent such errors. But in many cases of drive-medium conflicts it is mere incident whether a burn run succeeds or not. Workaround: Try other media or another drive. 4 `Drive error` : The drive or the bus controller accuse themselves of doing it wrong. As with "Medium error" this might be aggravated or eased by optional settings. Workaround: Check cables. If possible, try the drive at a different bus controller. 5 `Illegal request` : The drive did not like a command sent by libburn. This may be normal. But if you see this outside of DEBUG messages, then either the drive does not comply to MMC or libburn does not do its job right. Workaround: Submit an error report to [GNU xorriso mailing list](https://lists.gnu.org/mailman/listinfo/bug-xorriso). B `Command aborted` : Seems to be generated by some bus controllers or operating system SCSI drivers. The newest outbreak is said to be due to USB 3 and drivers which do not prevent power saving. Workaround: Plug USB drives to USB 2 sockets or have a recent operating system kernel. If this does not help, contact [https://lists.gnu.org/mailman/listinfo/bug-xorriso GNU xorriso mailing list] and be ready for experiments. ##### Why is simultaneous burning with multiple drives so slow ? <A NAME="concurrent_burn"> It is a known regression of Linux since about 2010 that operating more than one drive at the same time via SCSI commands shows severe throughput problems. See [the wiki page about this problem](ConcurrentLinuxSr) which offers two alternative workarounds in userspace, explanantions of the reason, and a link to a remedy proposal by courageous kernel modification. ---------------------------------------------------------------------------- ### Imaging ---------------------------------------------------------------------------- ##### Is there a way to edit files inside the ISO image ? <A NAME="edit_files"> File content cannot be altered. But files may be replaced by new copies from the disk filesystem. The main method of manipulating an existing ISO image is to append a session with a new complete directory tree and the file content of the added or overwritten files. Depending on the media type you get gaps between sessions of up to 20 MB. So better try to do all foreseeable changes by one add-on session. ##### For which architectures xorriso is able to create bootable images ? <A NAME="boot_arch"> Currently it supports systems with PC-BIOS for booting from CD, DVD, or BD media, and from memory sticks or hard disks. The same feature range is supported for systems with EFI firmware with x86 or ARM processor. Further it supports machines with MIPS processor from SGI (Big Endian) and DEC (Little Endian), and SUN SPARC machines. (See [libisofs/doc/boot_sectors.txt](https://dev.lovelyhq.com/libburnia/libisofs/raw/branch/master/doc/boot_sectors.txt) for technical details.) Examples how to get an impression of the boot equipment of existing ISO 9660 images are on the wiki page about xorriso [commands -report_system_area and -report_el_torito](ReportSystemArea). ##### How to enable booting from USB stick ? <A NAME="isohybrid"> In most cases, ISOs are prepared for PC-BIOS to boot the ISOLINUX boot loader. This boot loader is normally started from CD, DVD or BD media via an El Torito boot record. But if the ISO image resides on an USB stick or another hard-disk-like device, then PC-BIOS ignores El Torito and rather expects a Master Boot Record (MBR). Both boot record types can reside in the same ISO image. Therefore it is possible to create an MBR that starts the boot image file of ISOLINUX which is already target of the El Torito boot record. This kind of MBR is called `isohybrid`. ISOLINUX provides a program named `isohybrid` to patch existing images, but libisofs can create an MBR already when producing the ISO image. See in [manual page of xorriso](http://www.gnu.org/software/xorriso/man_1_xorriso.html) option `-boot_image` with arguments `isolinux system_area=`, and `-as mkisofs` option `-isohybrid-mbr`. See [Wikipedia on MBR](http://en.wikipedia.org/wiki/Master_boot_record) for general information about PC-DOS Master Boot Records, and [ISOLINUX wiki](http://syslinux.zytor.com/wiki/index.php/ISOLINUX) for special information about ISOLINUX. The wiki example with mkisofs can be performed as well by help of xorriso option -as mkisofs. A similar combination of El Torito and MBR is created by GRUB2 tool grub-mkrescue. See [homepage of GNU GRUB 2](http://www.gnu.org/software/grub/) for general information. EFI firmware in its native mode boots by El Torito from CD, DVD or BD media, and by partition table from USB stick or hard disk. Both, El Torito and partition table, point to a FAT filesystem image, the EFI System Partition. The partiton table may be either a DOS-style MBR partition table or a [GUID Partition Table](https://en.wikipedia.org/wiki/GUID_Partition_Table). The x86 program program in the MBR is ignored by EFI, which rather starts a program from the FAT directory "\EFI\BOOT". The name of the program file depends on the processor architecture: BOOTX64.EFI, BOOTIA32.EFI, BOOTARM.EFI, BOOTAA64.EFI for x86 64 bit, x86 32 bit, ARM 32 bit, and ARM 64 bit, respectively. The boot equipment for other systems may well work from USB stick too. But libburnia project has no tangible information about this. ##### What is partition offset feature all about? <A NAME="partition_offset"> If an MBR is present, then it contains a partition table with up to four entries. The MBR is located at the very start of the ISO image. By tradition the first partition should begin only after the range of MBR and eventual supporting data blocks. On hard disk one often sees partition 1 starting at byte `63*512`. Further it is tradition that the payload filesystem is mountable via one of the partitions. The isohybrid MBR has its only partition start at byte 0. Thus it is mountable but does not obey the tradition to begin only after the MBR. The `grub-mkrescue` MBR on the other hand has partition 1 start at byte 512,i which makes it unmountable. Only the unpartitioned base device can be mounted. (On GNU/Linux e.g. `/dev/sdb` is the base device whereas `/dev/sdb1` is its partition 1.) The compromise offered by libisofs is to create a second superblock at address `16*2048` and to let start partition 1 at this address. The second superblock leads to a second directory tree which takes into account the address difference between partition 1 and the base device. So the image gets mountable via both devices and reserves 32 kB for boot manager software where it may manipulate and augment the MBR. (See [Partition Offset Wiki](PartitionOffset) for examples.) There are reports of machines which will not boot from USB stick if partition offset is 0. ##### Partition offset bad on Apple ? <A NAME="partition_offset_apple"> Apple's "Snow Leopard" operating system refuses to mount Debian CD images with non-zero partition offset. The issue is not yet fully understood. For now one has to choose between mountability on Apple "Snow Leopard" or bootability from USB stick on Kontron CG2100 "carrier grade server". ---------------------------------------------------------------------------- ### Developing ---------------------------------------------------------------------------- ##### Where are the APIs of libburnia libraries described ? <A NAME="api_specs"> The decisive references are the inclusion headers of the libraries `<libburn/libburn.h>`, `<libisofs/libisofs.h>`, `<libisoburn/libisoburn.h>`, and `<libisoburn/xorriso.h>`. Current git versions of these files: [libburn/libburn.h](https://dev.lovelyhq.com/libburnia/libburn/raw/branch/master/libburn/libburn.h) , [libisofs/libisofs.h](https://dev.lovelyhq.com/libburnia/libisofs/raw/branch/master/libisofs/libisofs.h) , [libisoburn/libisoburn.h](https://dev.lovelyhq.com/libburnia/libisoburn/raw/branch/master/libisoburn/libisoburn.h) , [libisoburn/xorriso.h](https://dev.lovelyhq.com/libburnia/libisoburn/raw/branch/master/xorriso/xorriso.h) ##### I want to write a GUI on the top of libburnia libraries. Any pointers or recommendations ? <A NAME="gui_advise"> Most appreciated would be a GUI for xorriso which allows to copy files from a view of the hard disk filesystem to a view of the ISO filesystem, and vice versa. The xorriso implementation is located inside libisoburn. Each option that is described in [man 1 xorriso](http://www.gnu.org/software/xorriso/man_1_xorriso.html) can be performed by a corresponding C function that is defined in [xorriso.h](https://dev.lovelyhq.com/libburnia/libisoburn/raw/branch/master/xorriso/xorriso.h) Further there are calls for library startup and shutdown, for problem handling, and for the interpreters of xorriso's command line interface. The xorriso API encapsulates calls to libisofs, libburn, and libisoburn. An alternative to the xorriso C API is xorriso dialog mode. [See below](FAQ#xorriso_dialog_mode). The script [xorriso-tcltk](https://dev.lovelyhq.com/libburnia/libisoburn/raw/branch/master/frontend/xorriso-tcltk) demonstrates this approach. It is part of the libisoburn release tarball and of the GNU xorriso tarball. The known existing GUIs [Xfburn](http://goodies.xfce.org/projects/applications/xfburn), [Brasero](http://projects.gnome.org/brasero/), [FlBurn](http://flburn.sourceforge.net/) rather use libisofs and libburn directly. (Please submit an URI if you want your libburnia GUI application mentioned here.) ---------------------------------------------------------------------------- ### Miscellaneous ---------------------------------------------------------------------------- ##### Where to see examples ? <A NAME="example_links"> [xorriso examples](http://www.gnu.org/software/xorriso/man_1_xorriso.html#EXAMPLES) , [cdrskin examples](http://scdbackup.sourceforge.net/man_1_cdrskin_devel.html#EXAMPLES) , [libburner.c a minimal but complete burn program](https://dev.lovelyhq.com/libburnia/libburn/raw/branch/master/test/libburner.c). ##### What personalities are supported by xorriso ? <A NAME="xorriso_aliases"> The name by which xorriso is started may trigger certain features which normally would need to be enabled by program options. xorrisofs starts up in mkisofs emulation mode, which otherwise would have to be entered by command `-as mkisofs`. xorrecord starts up in cdrecord emulation mode, which is normally entered by command `-as cdrecord`. This emulation is only able to write a single data track as new session to blank or appendable media. No audio. No multiple tracks in one session. osirrox can copy files from ISO image to disk and to apply option -mount to one or more of the existing ISO sessions. This is normally enabled by option `-osirrox on:o_excl_off`. ##### What is xorriso dialog mode useful for ? <A NAME="xorriso_dialog_mode"> Dialog mode is initiated if `-dialog on` is among the program arguments. It can be used to inspect and exploit existing ISO 9660 images or to explore xorriso's behavior in order to develop the command sequence for a batch run. Frontend programmers may fork xorriso initiating a xorriso dialog session (`-dialog on -use_readline off -pkt_output on -mark done`), and interact with it from their own program via pipes connected to xorriso's stdin and stdout. This is more efficient than forking xorriso every now and then to perform various commands in order to complete complex tasks like image size prediction. The script [xorriso-tcltk](https://dev.lovelyhq.com/libburnia/libisoburn/raw/branch/master/frontend/xorriso-tcltk) demonstrates this approach. It is part of the libisoburn release tarball and of the GNU xorriso tarball. ##### Why is every second release missing ? <A NAME="version_numbers"> Releases have an even third version number. Like 0.5.6 or 1.0.4. During development the next higher odd number is used. E.g. 0.5.7 or 1.0.5. The content of release tarballs does not get changed without changing their name. The development tarballs of xorriso and cdrskin may change their content without notice. ---------------------------------------------------------------------------- Site maintainer: Do not edit this wiki directly but rather the git version of `libisoburn/doc/faq.wiki`. When done, paste it into the wiki editor. The argument names listed here have been reserved by Vladimir Serbinenko for grub-mkrescue. Do not use them as options of -as mkisofs or as generic commands. Keep in mind with generic commands: Leading '-' is ignored, inner '-' is mapped to '_'. http://lists.gnu.org/archive/html/grub-devel/2015-04/msg00122.html Date: Wed, 29 Apr 2015 16:05:04 +0200 From: Vladimir '?-coder/phcoder' Serbinenko <phcoder@gmail.com> Subject: Re: grub-mkrescue problems in argp_parse ------------------------------------------------------------------- --arcs-boot --compress --core-compress --directory --fonts --grub-glue-efi --grub-mkimage --grub-render-label --install-modules --label-bgcolor --label-color --label-font --locale-directory --locales --modules --output --override-directory --product-name --product-version --pubkey --rom-directory --sparc-boot --themes --themes-directory --verbose --xorriso -? -k ------------------------------------------------------------------- Known traditional collisions: ------------------------------------------------------------------- -o intentionally overloaded -v unintentional -d unintentional The partition offset feature of libisofs can produce ISO 9660 images which bear a quite conventional partition table if copied onto a USB stick. The first partition marks the size of the ISO image but starts at a non-zero address. Thus it marks a small part of the device as unclaimed by partitions and available for storing boot loader code. Nevertheless the USB stick is mountable via its overall device file as well as via the partition device file. E.g. on GNU/Linux: `/dev/sdb` and `/dev/sdb1`. This is achieved by two distinct sets of meta-data which refer to the same file content. The dual-mount feature supports Rock Ridge and Joliet too. It is capable of multi-session. Currently only offset 32 kB seems to make sense. Smaller offsets are prohibited by fundamental assumptions of libisofs and libisoburn. Larger offsets would extend the unclaimed area into vital blocks of the ISO image. -------------------------------------------------------------------------- According to a [thread of march 2011](http://www.syslinux.org/archives/2011-March/016527.html) on Syslinux mailing list this enabled booting of a Kontron CG2100 server from USB stick, which otherwise failed. Regrettably the feature seems to prevent mounting of ISO 9660 images on Apple "Snow Leopard" systems. At least this is the outcome of a [debian-cd thread of april 2011](http://lists.debian.org/debian-cd/2011/04/msg00029.html). -------------------------------------------------------------------------- Example: Testing mountability and ISOLINUX bootability from USB stick and CD. Overview: The test image was derived from one year old RIPLinux-9.3-non-X.iso which has an isohybrid MBR. Syslinux version seems to be 3.82. That MBR and the file tree from the mounted RIPLinux image was used to build a new ISO image with 16 \* 2kB partition offset. Isohybrid MBR patching was done by xorriso. Details: The first 32 kB of an ISO 9660 image are called System Area and may host any byte pattern. The first 512 bytes of RIPLinux-9.3-non-X.iso contain the isohybrid capable MBR, which will be re-used in this example. ``` dd if=RIPLinux-9.3-non-X.iso bs=512 count=1 of=RIPLinux-9.3-non-X.mbr ``` Normally the isohybrid MBR is provided by the Syslinux installation under the name `isohdp[fp]x*.bin` . E.g. `/usr/lib/syslinux/isohdpfx.bin` The files of the image are made accessible for reading ``` mount -o loop RIPLinux-9.3-non-X.iso /mnt ``` A new ISO image gets composed. The first three lines of arguments are taken from the prescriptions of ISOLINUX wiki and adapted to the names used in RIPLinux-9.3-non-X.iso. Option `-isohybrid-mbr` imports the copied MBR and patches it according to rules published by hpa on Syslinux mailing list. Option `-partition_offset 16` causes the first partition to start at 2 kB block number 16. It also prepares the image to be mountable by this partition, too. ``` xorriso -as mkisofs \ -o new_image.iso \ -b boot/isolinux/isolinux.bin -c boot/boot.cat \ -no-emul-boot -boot-load-size 4 -boot-info-table \ -isohybrid-mbr RIPLinux-9.3-non-X.mbr \ -partition_offset 16 \ /mnt ``` The image was copied onto a USB stick ``` dd if=new_image.iso of=/dev/sdc ``` and plugged into a Debian system. ``` fdisk -lu /dev/sdb ``` yields ``` Device Boot Start End Blocks Id System /dev/sdb1 * 64 120831 60384 17 Hidden HPFS/NTFS ``` I can mount `/dev/sdb` and `/dev/sdb1` alike: ``` mount /dev/sdb1 /mnt1 mount -o loop /dev/sdb /mnt ``` `-o loop` avoids failure with "mount: /dev/sdb already mounted or /mnt busy". A comparison by ``` diff -r /mnt /mnt1 ``` reports no difference. Human readable files look ok. Test-reading all content by ``` tar cf - /mnt | wc ``` yields a reasonable byte count of 60743680 and no errors. The machine boots RIPLinux from this USB stick with no visible problems. It can then mount `/dev/sdb` as well as `/dev/sdb1`. The ISO image boots from CD too. Mounting the partition can be simulated with an image file on hard disk by cutting off the first partition_offset blocks of 2 KB: ``` dd if=new_image.iso of=partition_image.iso bs=2048 skip=16 mount -o loop partition_image.iso /mnt1 ``` -------------------------------------------------------------------------- Another test was made with GRUB 2 by downloading ``` bzr branch http://bzr.savannah.gnu.org/r/grub/trunk/grub ``` Before building GRUB 2, the file ``` util/grub-mkrescue.in ``` was edited to replace in the options of the xorriso command: ``` --protective-msdos-label ``` by ``` -partition_offset 16 -no-pad ``` Then GRUB 2 was built and installed. The resulting image from ``` ./grub-mkrescue -o image.iso ``` was put onto USB stick. It passed the same tests on Debian as above RIPLinux example. It boots to a GRUB prompt. Due to option `-no-pad` the image is about 250 kB smaller than the image produced by original `grub-mkrescue`. Else it would have grown by about 50 kB. Unpadded ISO images are safe except for burning on CD in TAO mode. In this case problems may occur with reading the last few data blocks. So when burning onto CD make sure to require SAO mode and/or to require padding by 300 kB. Burning on DVD or BD needs no such caution. Neither does copying on USB stick or hard disk. Program `fdisk` will complain about "different physical/logical" addresses. This can be silenced by adding option ``` -partition_cyl_align on ``` at the cost of image padding up to the next full MB. E.g. by 402 kB to 2 MB. -------------------------------------------------------------------------- Open questions: - Shall the partition of an isohybrid image be marked bootable ? Currently xorriso keeps the 0x80 mark of an imported MBR and the 0x80 mark which xorriso sets by its own MBR preparations. - If not to be marked bootable: What equipment would the partition need to justify having the mark ? ------------------------------------------------------------------------ Application: The partition offset feature can be controlled by libisofs API calls ``` int iso_write_opts_set_part_offset(IsoWriteOpts *opts, uint32_t block_offset_2k, int secs_512_per_head, int heads_per_cyl); int iso_write_opts_set_system_area(IsoWriteOpts *opts, char data[32768], int options, int flag); ``` or by libisoburn calls ``` int isoburn_igopt_set_part_offset(struct isoburn_imgen_opts *opts, uint32_t block_offset_2k, int secs_512_per_head, int heads_per_cyl); int isoburn_igopt_get_part_offset(struct isoburn_imgen_opts *opts, uint32_t *block_offset_2k, int *secs_512_per_head, int *heads_per_cyl); int isoburn_igopt_set_system_area(struct isoburn_imgen_opts *o, char data[32768], int options); int isoburn_igopt_get_system_area(struct isoburn_imgen_opts *o, char data[32768], int *options); ``` or by xorriso options ``` -boot_image any partition_offset=(2kb_block_adr) -boot_image any partition_sec_hd=(number) -boot_image any partition_hd_cyl=(number) -boot_image any partition_cyl_align(on|auto|off) -as mkisofs ... -partition_offset (2kb_block_adr) \ -partition_hd_cyl (number) \ -partition_sec_hd (number) \ -partition_cyl_align (on|auto|off) ... ``` As stated above, an offset larger than 16 would expose vital parts of the ISO image as unclaimed space. Values smaller than 16 are not accepted. So use either an offset of 16 blocks or keep the feature disabled by offset 0. This text describes how to set up a qemu virtual machine so that xorriso on its guest GNU/Linux can operate a CD, DVD or BD recorder of the host system. The options follow proposals of Paolo Bonzini on qemu-devel mailing list. My compliments for his patient guidance. Basic knowledge about Debian and qemu was learned from [GNU Hurd qemu page](http://www.gnu.org/s/hurd/hurd/running/qemu.html GNU Hurd qemu page). ---------------------------------------------------------------------- This start command works with `qemu-1.0-rc3`: ``` $ qemu \ -enable-kvm \ -nographic \ -m 512 \ -net nic,model=ne2k_pci \ -net user,hostfwd=tcp::5557-:22 \ -hda /dvdbuffer/i386-install.qemu \ -drive file=/dev/sr0,if=none,id=scsicd,format=raw \ -device virtio-blk-pci,drive=scsicd,logical_block_size=2048,physical_block_size=2048 \ -cdrom .../some_image.iso ``` This start command works with `qemu-2.1.2`: ``` $ qemu \ -enable-kvm \ -nographic \ -m 512 \ -net nic,model=ne2k_pci \ -net user,hostfwd=tcp::5557-:22 \ -hda /dvdbuffer/i386-install.qemu \ -cdrom .../some_image.iso \ -drive file=/dev/sr0,index=2,if=virtio ``` With these setups of `-drive` and `-device` it is necessary to have a medium in the drive, when qemu gets started. Else it will refuse. The guest system is accessible via ssh and scp at port 5557 of the host system. `/dev/sr0` is the address of the DVD drive which is handed over to the guest system. `.../some_image.iso` may be any readable file which shall serve as virtual DVD-ROM. qemu is not happy without such a thing. `/dvdbuffer/i386-install.qemu` is the disk image, where the guest operating system was installed by: ``` $ qemu-img create /dvdbuffer/i386-install.qemu 8G $ qemu \ -enable-kvm \ -m 512 \ -net nic,model=ne2k_pci \ -hda /dvdbuffer/i386-install.qemu \ -cdrom debian-6.0.3-i386-netinst.iso \ -boot d ``` Host system of my `qemu-1.0-rc3` test is Debian GNU/Linux 6.0.2 amd64. With `qemu-2.1.2` it is Debian 8.1 amd64. Both had access to the Internet when the guest was installed. ---------------------------------------------------------------------- Preparations on guest system Debian GNU/Linux 6.0.3 i386 There appears no `/dev/sr` for the passthrough drive. Thus libburn will not list it by its drive search function. One may use it nevertheless. But xorriso will only do so if prefix "mmc:" is used with the address: ``` -dev mmc:/dev/vda ``` The drive will be listed by libburn if there is a symbolic link `/dev/sr*` pointing to it. On Debian 6, this link persists only if it is created by an udev rule. In `/lib/udev/rules.d/50-udev-default.rules`: ``` KERNEL=="vda", SYMLINK+="sr1" ``` libburn on Linux needs rw-permission for the drive's device node. The virtual device `/dev/vda` is in group "disk". Usual for CD drives is group "cdrom", to which i (or the Debian installer ?) have added my normal user when i installed the guest system. Like with the symbolic link, such a change persists on Debian 6 only as udev rule. In `/lib/udev/rules.d/91-permissions.rules`: ``` KERNEL=="vda", GROUP="cdrom" ``` This should yield ``` lrwxrwxrwx 1 root root 3 Nov 8 11:19 /dev/sr1 -> vda brw-rw---- 1 root cdrom 254, 0 Nov 8 11:19 /dev/vda ``` xorriso version must be >= 1.1.8 ``` $ xorriso -version ``` tells the versions of its components on stdout: ``` ... xorriso version : 1.1.8 ... ``` If your distro's xorriso is too old, consider to get and build GNU xorriso. ``` http://ftpmirror.gnu.org/xorriso/xorriso-1.1.8.tar.gz ``` Do ``` $ tar xzf xorriso-1.1.8.tar.gz $ cd xorriso-1.1.8 $ ./configure && make ``` Either do as superuser ``` # make install ``` or execute it where it was built as ``` $ ./xorriso/xorriso ...arguments... ``` After compilation, this binary does not depend on files in the build directory. You may move it to any other location. For details about the following xorriso commands, read ``` man xorriso man ./xorriso/xorriso.1 ``` or with the same content ``` info xorriso info ./xorriso/xorriso.info ``` Or read the [online man page of xorriso](http://scdbackup.sourceforge.net/man_1_xorriso_devel.html). Note that the sequence of xorriso arguments matters. They are commands which get performed one after the other. This differs from the behavior of mkisofs, cdrecord, et.al., which parse all arguments and then perform actions in a hardcoded sequence. Writing happens automatically if ISO filetree changes are pending at the end of the program run. This is like with other burn tools. (There is a command `-commit` for intermediate writing e.g. in dialog mode.) ---------------------------------------------------------------------- Listing accessible drives: ``` $ xorriso -devices ``` shows on stdout: ``` 0 -dev '/dev/sr0' rwrw-- : 'QEMU ' 'QEMU DVD-ROM' 1 -dev '/dev/sr1' rwrw-- : 'Optiarc ' 'BD RW BD-5300S' ``` ---------------------------------------------------------------------- The burn tests are presented here for unformatted DVD-RW media. The xorriso commands apply also to other types of optical media. See "Other applicable media types:" further below. ---------------------------------------------------------------------- Inspecting drive and medium: ``` $ xorriso -outdev /dev/sr1 -toc ``` should show on stdout something like ``` Drive current: -dev '/dev/sr1' Drive type : vendor 'Optiarc' product 'BD RW BD-5300S' revision '1.04' Media current: DVD-RW sequential recording Media product: RITEKW04 , Ritek Corp Media status : is written , is closed Media blocks : 306592 readable , 0 writable , 2298496 overall TOC layout : Idx , sbsector , Size , Volume Id ISO session : 1 , 0 , 106696s , ISOIMAGE ISO session : 2 , 135536 , 108385s , ISOIMAGE ISO session : 3 , 250240 , 56202s , ISOIMAGE Media summary: 3 sessions, 271744 data blocks, 531m data, 0 free ``` ---------------------------------------------------------------------- Blanking to single session capability: This medium has to be blanked before further writing. For the DAO test, one can save time by fast blanking, which xorriso normally dislikes because the result is not capable of multi-session: ``` $ xorriso -outdev /dev/sr1 -blank deformat_quickest ``` should report on stderr ``` ... xorriso : UPDATE : Blanking ( 1.0% done in 2 seconds ) ... xorriso : UPDATE : Blanking ( 95.4% done in 36 seconds ) xorriso : UPDATE : Blanking ( 99.0% done in 37 seconds ) ... Media current: DVD-RW sequential recording Media status : is blank Media summary: 0 sessions, 0 data blocks, 0 data, 4489m free ``` Do not worry if the pacifier messages show no neat percentage progress. Some drives report "1.0%" until they are done. Some report "1.0%" after "99%". ---------------------------------------------------------------------- Writing a DAO session: Use one or more moderately sized directories as input. Here: `/usr/bin`. Terminate the list of `-add` arguments by argument `--`. It is important to have command `-close on` among the arguments. ``` $ xorriso -md5 on -outdev /dev/sr1 -close on -add /usr/bin -- ``` should report on stderr ``` ... xorriso : UPDATE : 594 files added in 1 seconds ... xorriso : UPDATE : Thank you for being patient. Working since 2 seconds. xorriso : UPDATE : Writing: 32s 0.1% fifo 100% buf 0% 0.1xD ... xorriso : UPDATE : Writing: 2704s 5.1% fifo 11% buf 0% 3.9xD ... xorriso : UPDATE : Writing: 20208s 38.2% fifo 52% buf 99% 4.0xD ... xorriso : UPDATE : Writing: 52885s 100.0% fifo 0% buf 99% 0.0xD ISO image produced: 52735 sectors Written to media : 52885 sectors at LBA 0 Writing to '/dev/sr1' completed successfully. ``` Do not worry if there is no progress to see for a few dozen seconds at the beginning. The run will last at least as long as writing of 1 GB would need. If you write less data, then there will be a lot of zero progress messages at the end of writing. ---------------------------------------------------------------------- Checkreading the result: ``` $ xorriso -md5 on -indev /dev/sr1 -check_md5_r sorry / -- ``` The word "sorry" sets the severity class of the event message, which is emitted in case of MD5 mismatch. (See man xorriso, "Exception processing".) This should report on stderr ``` ... Drive current: -indev '/dev/sr1' Media current: DVD-RW sequential recording Media status : is written , is closed Media summary: 1 session, 52885 data blocks, 103m data, 0 free Volume id : 'ISOIMAGE' xorriso : UPDATE : 568079 content bytes read in 5 seconds xorriso : UPDATE : 17074k content bytes read in 10 seconds ... xorriso : UPDATE : 103.7m content bytes read in 35 seconds File contents and their MD5 checksums match. ``` and the exit value should be 0, if no mismatch was reported. A mismatch message would look like ``` ... MD5 MISMATCH: '/usr/bin/ncursesw5-config' ... Mismatch detected between file contents and MD5 checksums. xorriso : SORRY : Event triggered by MD5 comparison mismatch xorriso : NOTE : Tolerated problem event of severity 'SORRY' xorriso : NOTE : -return_with SORRY 32 triggered by problem severity SORRY ``` and the exit value would be non-zero. ---------------------------------------------------------------------- Blanking to multi-session capability: ``` $ xorriso -outdev /dev/sr1 -blank as_needed ``` This will need as long as writing the DVD-RW up to its end. Blanking option `as_needed` lets xorriso decide what to do in order to make the medium writable from scratch. With DVD-RW it will decide for `-blank all`. The report on stderr should end by ``` ... xorriso : UPDATE : Blanking ( 98.9% done in 902 seconds ) xorriso : UPDATE : Blanking ( 99.0% done in 903 seconds ) xorriso : UPDATE : Blanking ( 99.0% done in 904 seconds ) Blanking done xorriso : NOTE : Re-assessing -outdev '/dev/sr1' Drive current: -outdev '/dev/sr1' Media current: DVD-RW sequential recording Media status : is blank Media summary: 0 sessions, 0 data blocks, 0 data, 4489m free ``` ---------------------------------------------------------------------- Writing multiple sessions (DVD-RW write type Incremental): This time do not perform command `-close on`, so that the medium stays writable: ``` $ xorriso -md5 on -dev /dev/sr1 -add /usr/lib -- ... xorriso : UPDATE : Writing: 105280s 98.6% fifo 0% buf 77% 3.5xD xorriso : UPDATE : Writing: 106796s 100.0% fifo 0% buf 62% 2.2xD xorriso : UPDATE : Closing track/session. Working since 44 seconds ... xorriso : UPDATE : Closing track/session. Working since 77 seconds ISO image produced: 106646 sectors Written to media : 106800 sectors at LBA 0 Writing to '/dev/sr1' completed successfully. ``` Checkread like after the DAO test: ``` $ xorriso -md5 on -indev /dev/sr1 -check_md5_r sorry / -- ... xorriso : UPDATE : 204.0m content bytes read in 63 seconds File contents and their MD5 checksums match. ``` Writing the second session looks like the first one. Just use another set of input files to get a visible change in the ISO 9660 file tree: ``` $ xorriso -md5 on -dev /dev/sr1 -add /usr/bin -- ... Written to media : 53408 sectors at LBA 135488 Writing to '/dev/sr1' completed successfully. ``` And checkread the whole tree of files (i.e. both sessions): ``` $ xorriso -md5 on -indev /dev/sr1 -check_md5_r sorry / -- ... xorriso : UPDATE : 307.8m content bytes read in 89 seconds File contents and their MD5 checksums match. ``` At the end of writing a final session, the medium can be closed. It will not take more writing unless it gets blanked or formatted. So use command `-close on` to demand closing after writing. ``` $ xorriso -md5 on -dev /dev/sr1 -close on -add /usr/sbin -- ... Written to media : 16160 sectors at LBA 195056 Writing to '/dev/sr1' completed successfully. ``` Checkread ``` $ xorriso -md5 on -indev /dev/sr1 -check_md5_r sorry / -- ... Media current: DVD-RW sequential recording Media status : is written , is closed Media summary: 3 sessions, 176368 data blocks, 344m data, 4064m free ... xorriso : UPDATE : 337.7m content bytes read in 97 seconds File contents and their MD5 checksums match. ``` ----------------------------------------------------------------------------- If the drive tray can move by itself, you may now eject the medium: ``` $ xorriso -outdev /dev/sr1 -eject all ``` ----------------------------------------------------------------------------- Other applicable media types: These test runs for sequential DVD-RW may be performed on CD-RW with the same xorriso arguments. Be aware that `/usr/lib` will hardly fit on a CD. So choose smaller directories for CD. `-blank deformat_quickest` addresses a peculiarity of DVD-RW. It will work on other media like `-blank fast`. Except the blanking runs, the tests may also be performed on BD-R, DVD-R, DVD+R, and CD-R. But you would waste two media by this. The first session on CD will always be written with write type SAO, further sessions will be written with TAO. CD-R and DVD-R have a simulation mode. It can be enabled by xorriso command `-dummy on`, but of course it will not produce readable results. So this simulation is usable only for first sessions on blank media. ----------------------------------------------------------------------------- Now for formatted overwritable media: All blank, write and check runs of above tests "Writing multiple sessions" may also be performed with DVD+RW, DVD-RAM, formatted DVD-RW, and BD-RE. There is no way to close formatted media. The command `-close on` gets silently ignored. The write methods and states of formatted media differ from those of sequential media. But xorriso presents to the user a unified multi-session usage model, under the assumption that all emulated sessions contain ISO 9660 filesystem images, which successively build on each other. So from the view of xorriso commands, the only task which makes them differ from sequential media, is to apply optional formatting or re-formatting. A special case are BD-R, which xorriso may format but will not bring into (pseudo-) overwritable state. Formatted BD-R perform Defect Management by default, which checkreads during writing and replaces bad block. The mandatory formatting of unused DVD+RW and BD-RE is done by xorriso automatically. Just start a normal write run. DVD-RAM are sold formatted. xorriso treats overwritable media with a valid ISO 9660 filesystem as appendable media. To make then writable from scratch, apply `-blank as_needed`, which will actually write a few bytes into the PVD (superblock) of the ISO filesystem to invalidate it. De-formatting is only possible with DVD-RW. E.g. by `-blank deformat`. ----------------------------------------------------------------------------- Format DVD-RW for overwriting without intermediate blanking, or format BD-R for Defect Management: ``` $ xorriso -outdev /dev/sr1 -format as_needed ``` should report on stderr ``` ... xorriso : UPDATE : Formatting ( 99.0% done in 912 seconds ) Formatting done xorriso : NOTE : Re-assessing -outdev '/dev/sr1' Drive current: -outdev '/dev/sr1' Media current: DVD-RW restricted overwrite Media status : is blank Media summary: 0 sessions, 0 data blocks, 0 data, 4488m free ``` As with blanking, one should not worry if the progress messages show unplausible percentages. Some drives are more equal than others. Formatting is said to be much stress to the medium. `-format` option `as_needed` applies it only to yet unformatted media. When performing above write tests, take care to use `-blank as_needed` rather than `-blank deformat_quickest`. Else you will get a sequential unformatted DVD-RW rather than a formatted DVD-RW which xorriso is willing to write from scratch. There is no use in a separate "DAO" test on overwritable media anyway. ----------------------------------------------------------------------------- Change the formatted size of a BD-RE: First learn about formatted size and proposals of other sizes. (One can issue own wishes, too. See in man xorriso, command `-format`.) ``` $ xorriso -outdev /dev/sr1 -list_formats ``` should tell on stdout ``` ... Format status: formatted, with 23610.0 MiB BD Spare Area: 0 blocks consumed, 131072 blocks available Format idx 0 : 00h , 11826176s , 23098.0 MiB Format idx 1 : 01h , 11564032s , 22586.0 MiB Format idx 2 : 30h , 11826176s , 23098.0 MiB Format idx 3 : 30h , 11564032s , 22586.0 MiB Format idx 4 : 30h , 12088320s , 23610.0 MiB Format idx 5 : 31h , 12219392s , 23866.0 MiB ``` So lets go back from 23610.0 MiB to the default size of 23098.0 MiB ``` $ xorriso -outdev /dev/sr1 -format by_index_2 -blank as_needed ... Media summary: 2 sessions, 105470 data blocks, 206m data, 22.4g free ``` Although the heads of the old sessions might remain readable after `-format`, better do not rely on this and a append `-blank as_needed` to avoid any data corruption. If you want to keep the data, then make at least a checkread run. Check whether the size has changed: ``` $ xorriso -outdev /dev/sr1 -list_formats ``` should tell on stdout ``` ... Format status: formatted, with 23098.0 MiB BD Spare Area: 0 blocks consumed, 393216 blocks available ... ``` # This is an example for a xorriso startup file. # If found at one of the following addresses then its text lines will get # executed by xorriso as commands before any of its program arguments: # /etc/default/xorriso # /etc/opt/xorriso/rc # /etc/xorriso/xorriso.conf # $HOME/.xorrisorc # Note: Command -no_rc as first program argument prevents this execution. # Disallow the use of hard disk /dev/sda and its partitions as # pseudo-drive (e.g. as output target of an ISO image). -drive_class banned /dev/sda* # Allow the use of /dev/sdb, /dev/sdc, and /dev/sdd as pseudo-drives # without the prefix "stdio:" which is usually required for device addresses # which begin by "/dev/" but represent no CD drives. -drive_class harmless /dev/sd[bcd] ------------------------------------------------------------------------------ xorriso-tcltk ------------------------------------------------------------------------------ Copyright (C) 2012 - 2013 Thomas Schmitt <scdbackup@gmx.net>, libburnia-project.org Provided under BSD license: Use, modify, and distribute as you like. ------------------------------------------------------------------------------ xorriso-tcltk is mainly a proof of concept for a frontend that operates xorriso in dialog mode. Dependencies: - xorriso ISO 9660 Rock Ridge filesystem manipulator and CD/DVD/BD burn program - Tcl programming language - Tk widget toolkit - optionally the Tcl/Tk package BWidget It exercises several fundamental gestures of communication: - connecting via two pipes - sending commands - receiving replies - inquiring the xorriso message sieve - using the xorriso parsing service Note that any other language than Tcl/Tk could be used, if it only can do i/o via standard input and standard output or via named pipes. Further it has to perform integer arithmetics and string manipulations. And, well, a graphical widget set would be nice. See man xorriso for a documentation of xorriso concepts and commands. See man xorrecord for details of the burn image file feature. Quick start In the xorriso build directory, without installation of xorriso: xorriso/xorriso -launch_frontend frontend/xorriso-tcltk --stdio -- After installation of xorriso by make install: xorriso-tcltk Overview of GUI The window is separated into three main areas: - Connection to xorriso. - Management of drives and image files. - Inspection, manipulation, and exploitation of xorriso ISO image model. Click the rightmost mouse button while being over any of the GUI elements in order to get the particular help text for that element. There is no need to close the help window. Just click another element to get another help text. The "Help" button in the upper right corner gives a short overview and instructions for several common use cases. Program start options The Tcl shell "wish" is allergic to options which start by "-h". So here is the output of xorriso-tcltk --help : ------------------------------------------------------------------------ Usage: frontend/xorriso-tcltk [options] Options: All options must be given with two dashes ("--option") in order to distinguish them from any options of the Tcl shell. --help Print this text and exit. --stdio Establish connection to xorriso via stdin and stdout. E.g. when letting xorriso start this frontend program: xorriso -launch_frontend $(which xorriso-tcltk) --stdio -- --named_pipes cmd_fifo reply_fifo Establish connection to a xorriso process started by: xorriso -dialog on <cmd_fifo >reply_fifo which is then ready for a run of: xorriso-tcltk --named_pipes cmd_fifo reply_fifo It is important that the parent of xorriso and of this tcl/tk frontend opens the named pipe for commands before it opens the named pipe for replies. This avoids deadlock. --silent_start Do not issue the start message xorriso-tcltk-version. This works only if --silent_start is the first argument. --no_extract Do not allow extraction of files from ISO filesystem to hard disk. This is not revokable during the program run. --no_bwidget Do not try to load the Tcl/Tk package BWidget which is a prerequisite for the "/" file browser buttons. --geometry {+|-}X{+|-}Y Sets the position of the main window. --click_to_focus Chooses that input fields and list boxes get the keyboard focus only when being clicked by the mouse. (Default) --auto_focus Chooses that the keyboard focus is where the mouse pointer is. --pipe_log_file path Set a file address for logging of xorriso commands and reply messages and enable this logging. The log lines will be appended. Path "-" means stderr. --script_log_file path Set a file address for logging of essential xorriso commands and enable this logging. The log lines will be appended. Path "-" means stderr. --script_log_all_commands With logging of commands log non-essential commands too. --use_command_move Use xorriso command -move for the "Move to: button if xorriso version is >= 1.2.8 --use_command_mv Use xorriso command -mv for the "Move to:" button. If neither --stdio nor --named_pipes is given, then this script will try to locate itself in the filesystem and start a xorriso run that launches it again. In the running GUI, click with the rightmost mouse button on any GUI element to get its particular help text. ------------------------------------------------------------------------ /* Beefed-up example from man 2 pipe to illustrate how xorriso is to be used by frontend programs via two pipes. Additionally there is a standalone implementation of Xorriso_parse_line(). Copyright 2012 Thomas Schmitt, <scdbackup@gmx.net> Unaltered provided under BSD license. You may issue licenses of your choice for derived code, provided that they do not infringe anybody's right to do the same for this original code. Build: cc -g -o frontend_pipes_xorriso frontend_pipes_xorriso.c Usage: ./frontend_pipes_xorriso [path_to_xorriso_binary | -h] */ #include <sys/wait.h> #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <ctype.h> static int usage() { static char helptext[][80] = { "usage: frontend_pipes_xorriso [path_to_xorriso|-h]", "", "Forks a process that runs xorriso and communicates with it via two pipes.", "The command pipe sends payload commands and -mark commands. The reply pipe", "receives -pkt_output lines which it dispatches to stdout and stderr.", "The communication between both processes is made synchronous by the parent", "awaiting the -mark message of the child.", "Optionally the reply lines can be parsed into words. This is initiated by", "meta command", " @parse [prefix [separators [max_words [flag]]]]", "which sets the four parameters for a function equivalent to", "Xorriso_parse_line() (see xorriso.h). All reply will then be parsed and", "non-empty word arrays are displayed. Meta command", " @noparse", "ends this mode.", "Meta command", " @drain_sieve", "reports all recorded results of all installed message sieve filter rules.", "For illustration perform this xorriso command sequence", " -msg_op start_sieve - -outdev /dev/sr0 -msg_op clear_sieve - -toc", "and then @drain_sieve.", "@END@" }; int i; for (i = 0; strcmp(helptext[i], "@END@") != 0; i++) fprintf(stderr, "%s\n", helptext[i]); return(1); } /* Local helpers of parent process */ struct boss_state { /* What the parent needs to know about its connection to xorriso */ /* The ends of the dialog pipes */ int command_fd; int reply_fd; /* For synchronization between boss and xorriso */ int mark_count; char pending_mark[16]; /* Parsing_parameters. See xorriso.h Xorriso_parse_line */ int do_parse; char progname[1024]; char prefix[1024]; char separators[256]; int max_words; int flag; /* A primitive catcher for result lines */ int reply_lines_size; /* 0= catching disabled */ char **reply_lines; int reply_lines_count; }; #define Frontend_xorriso_max_resulT 1000 /* Some basic gestures of this program: */ static int prompt_for_command(struct boss_state *boss, char *line, int line_size); static int transmit_command(struct boss_state *boss, char *line); static int await_all_replies(struct boss_state *boss); static int de_pkt_result(struct boss_state *boss); static int drain_sieve(struct boss_state *boss); int parse_line(char *progname, char *line, char *prefix, char *separators, int max_words, int *argc, char ***argv, int flag); int dispose_parsed_words(int *argc, char ***argv); /* Parent and child */ int main(int argc, char *argv[]) { int command_pipe[2], reply_pipe[2]; pid_t cpid; char *xorriso_path = "/usr/bin/xorriso"; if (argc > 1) { if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || strcmp(argv[1], "--help") == 0) { usage(); exit(0); } xorriso_path = argv[1]; } if (pipe(command_pipe) == -1) { perror("pipe"); exit(1); } if (pipe(reply_pipe) == -1) { perror("pipe"); exit(1); } cpid = fork(); if (cpid == -1) { perror("fork"); exit(2); } if (cpid == 0) { /* Child redirects stdin and stdout. Then it becomes xorriso. */ char *xargv[8]; close(command_pipe[1]); /* Close unused write end */ close(reply_pipe[0]); /* Close unused read end */ /* Attach pipe ends to stdin and stdout */ close(0); if (dup2(command_pipe[0], 0) == -1) { perror("dup2(,0)"); exit(1); } close(1); if (dup2(reply_pipe[1], 1) == -1) { perror("dup2(,1)"); exit(1); } xargv[0] = xorriso_path; xargv[1] = "-dialog"; xargv[2] = "on"; xargv[3] = "-pkt_output"; xargv[4] = "on"; xargv[5] = "-mark"; xargv[6] = "0"; /* corresponds to mark_count = 0 in parent */ xargv[7] = NULL; execv(xorriso_path, xargv); perror("execv"); exit(1); } else { /* Parent prompts user for command lines and prints xorriso replies. It knows when all reply text of the pending command line has arrived by watching for -mark reply pending_mark. */ int ret; char line[4096]; struct boss_state boss; close(command_pipe[0]); /* Close unused read end */ close(reply_pipe[1]); /* Close unused write end */ memset(&boss, 0, sizeof(boss)); boss.command_fd = command_pipe[1]; boss.reply_fd = reply_pipe[0]; strcpy(boss.progname, argv[0]); boss.reply_lines = NULL; /* Dialog loop */ sprintf(boss.pending_mark, "%d", boss.mark_count); while (1) { /* Wait for pending mark and print all replies */ ret = await_all_replies(&boss); if (ret < 0) break; /* Prompt for command line */ ret = prompt_for_command(&boss, line, sizeof(line)); if (ret <= 0) break; /* Send line and -mark command */ ret = transmit_command(&boss, line); if (ret <= 0) break; } /* >>> if child is still operational: send -rollback_end */; /* >>> wait a short while */; /* >>> if not yet ended: kill child */; wait(NULL); /* Wait for child */ exit(0); } } /* ------------------- Local helpers of parent process -------------------- */ static int show_parsed(struct boss_state *boss, char *line); static int record_reply_line(struct boss_state *boss, char *line); static int make_reply_lines(struct boss_state *boss); static int input_interpreter(char *line, struct boss_state *boss); /* Ask the user for command input and trigger processing of meta commands. */ static int prompt_for_command(struct boss_state *boss, char *line, int line_size) { int l, ret; char *line_res; while (1) { fprintf(stderr, "+++ Enter a command and its parameters :\n"); line_res = fgets(line, line_size - 1, stdin); if (line_res == NULL) return(0); l = strlen(line); if (l == 0) { line[0] = '\n'; line[1] = 0; } else if (line[l - 1] != '\n') { line[l] = '\n'; line[l + 1] = 0; } /* Interpret meta commands which begin by @ */ ret = input_interpreter(line, boss); if (ret == 0) return(1); if (ret < 0) return(-1); } } /* Transmit a command (which must end by white space, e.g. newline) and its unique synchronization mark. */ static int transmit_command(struct boss_state *boss, char *line) { int ret; char mark_line[32]; ret = write(boss->command_fd, line, strlen(line)); if (ret == -1) { perror("write"); return(0); } /* Produce new unique -mark text to watch for */ (boss->mark_count)++; sprintf(boss->pending_mark, "%d", boss->mark_count); sprintf(mark_line, "-mark %s\n", boss->pending_mark); ret = write(boss->command_fd, mark_line, strlen(mark_line)); if (ret == -1) { perror("write"); return(0); } return(1); } /* Read reply messages from xorriso and wait for the expected synchronization mark. Messages can be printed or collected in boss->reply_lines. */ static int await_all_replies(struct boss_state *boss) { int count, remainder = 0, ret; char buf[32769], *line, *npt; while (1) { count = read(boss->reply_fd, buf + remainder, sizeof(buf) - 1 - remainder); if (count == -1) { perror("read"); return(-1); } if (count == 0) { fprintf(stderr, "+++ EOF encountered by Master process\n"); return(-2); } for (npt = buf + remainder; npt < buf + count; npt++) { if (*npt == 0) { fprintf(stderr, "+++ Protocol error : Reply contains 0-chars\n"); return(-1); } } /* Split buf into lines */ buf[remainder + count] = 0; /* for convenience */ line = buf; while (1) { npt = strchr(line, '\n'); if (npt == NULL) { /* Move line to start of buffer and set remainder */ if (line != buf) { remainder = 0; for (npt = line; *npt; npt++) buf[remainder++] = *npt; } /* Now read more data in the hope to get a newline char */ break; } /* Interpret line */ *npt = 0; if (line[0] == 'M') { /* M-replies will be outdated until the pending command line is completely done and the appended -mark command gets into effect. */ if (strlen(line) < 6) { fprintf(stderr, "+++ Protocol error : M-channel line shorter than 6 chars\n"); return(-1); } if (strcmp(line + 5, boss->pending_mark) == 0) { if ((line - buf) + strlen(line) + 1 < count) { fprintf(stderr, "+++ Protocol error : Surplus reply data after M-match\n"); fprintf(stderr, "%s\n", line + strlen(line) + 1); return(-1); } return (1); /* Expected mark has arrived */ } } else if (line[0] == 'R') { /* R-replies are result lines of inquiry commands, like -ls. They should be handled by specialized code which knows how to parse and interpret them. */ if (boss->reply_lines_count < boss->reply_lines_size) { ret = record_reply_line(boss, line); if (ret <= 0) return(ret); } else printf("%s\n", line); } else { /* I-replies are pacifiers, notifications, warnings, or error messages. They should be handled by a general message interpreter which determines their severity and decides whether to bother the user. */ if (boss->reply_lines_count < boss->reply_lines_size) { ret = record_reply_line(boss, line); if (ret <= 0) return(ret); } else fprintf(stderr, "%s\n", line); } /* Parse line and show words */ if (strlen(line) >= 5) show_parsed(boss, line + 5); line = npt + 1; } } return(1); } /* Throw away I channel. Unpack and reconstruct payload of R channel lines. */ static int de_pkt_result(struct boss_state *boss) { int i, l, w; char *payload = NULL, *new_payload = NULL; w = 0; for (i = 0; i < boss->reply_lines_count; i++) { if (boss->reply_lines[i][0] != 'R' || strlen(boss->reply_lines[i]) < 5) continue; if (payload == NULL) { payload = strdup(boss->reply_lines[i] + 5); } else { l = strlen(payload); new_payload = calloc(l + strlen(boss->reply_lines[i] + 5) + 1, 1); if (new_payload == NULL) goto no_mem; strcpy(new_payload, payload); strcpy(new_payload + l, boss->reply_lines[i] + 5); free(payload); payload = new_payload; } if (payload == NULL) goto no_mem; l = strlen(payload); if (l > 0) if (payload[l - 1] == '\n') payload[l - 1] = 0; if (boss->reply_lines[i][2] != '0') { free(boss->reply_lines[w]); boss->reply_lines[w] = payload; w++; payload = NULL; } } for (i = w ; i < boss->reply_lines_count; i++) { free(boss->reply_lines[i]); boss->reply_lines[i] = NULL; } boss->reply_lines_count = w; return(1); no_mem:; fprintf(stderr, "FATAL: Out of memory !\n"); return(-1); } /* Inquire and print all recorded message sieve results. */ static int drain_sieve(struct boss_state *boss) { int ret, i, j, names_size = 0, names_count = 0, first_result; int number_of_strings, available, xorriso_ret, number_of_lines, k, r; char **names = NULL, line[1024]; /* Install catcher for reply_lines */ ret = make_reply_lines(boss); if (ret <= 0) goto ex; boss->reply_lines_size = Frontend_xorriso_max_resulT; boss->reply_lines_count = 0; /* Get list of filter rule names from -msg_op show_sieve */ ret = transmit_command(boss, "-msg_op show_sieve -\n"); if (ret <= 0) goto ex; ret = await_all_replies(boss); if (ret <= 0) goto ex; ret = de_pkt_result(boss); if (ret <= 0) goto ex; names = boss->reply_lines; boss->reply_lines = NULL; names_size = Frontend_xorriso_max_resulT; names_count= boss->reply_lines_count; ret = make_reply_lines(boss); if (ret <= 0) goto ex; boss->reply_lines_size = Frontend_xorriso_max_resulT; /* Inquire caught results of each name by -msg_op read_sieve until return value is <= 0 */ printf("--------------------------------------------------\n"); for (i = 0; i < names_count; i++) { available = 1; first_result = 1; while (available > 0) { boss->reply_lines_count = 0; sprintf(line, "-msg_op read_sieve '%s'\n", names[i]); ret = transmit_command(boss, line); if (ret <= 0) goto ex; ret = await_all_replies(boss); if (ret <= 0) goto ex; ret = de_pkt_result(boss); if (ret <= 0) goto ex; if (boss->reply_lines_count < 2) { fprintf(stderr, "drain_sieve: illegible result reply\n"); {ret= 0; goto ex;} } xorriso_ret = -1; sscanf(boss->reply_lines[0], "%d", &xorriso_ret); if(xorriso_ret <= 0) break; number_of_strings = -1; sscanf(boss->reply_lines[1], "%d", &number_of_strings); if(xorriso_ret < 0) break; if (first_result) printf(" '%s' |\n", names[i]); first_result = 0; for (j = 0; names[i][j] != 0; j++) printf("-"); printf("-----\n"); r = 2; for (j = 0; j < number_of_strings && r < boss->reply_lines_count; j++) { number_of_lines = -1; sscanf(boss->reply_lines[r], "%d", &number_of_lines); r++; printf("|"); for (k = 0; k < number_of_lines && r < boss->reply_lines_count; k++) { printf("%s%s", boss->reply_lines[r], k < number_of_lines - 1 ? "\n" : ""); r++; } printf("|\n"); } } if (first_result == 0) printf("--------------------------------------------------\n"); } /* Dispose all recorded results */ ret = transmit_command(boss, "-msg_op clear_sieve -\n"); if (ret <= 0) goto ex; ret = await_all_replies(boss); if (ret <= 0) goto ex; ret = 1; ex:; /* Disable result catcher */ boss->reply_lines_size = 0; if (names != NULL) dispose_parsed_words(&names_size, &names); return(ret); } /* ------------------------- Helpers of local helpers ---------------------- */ static int show_parsed(struct boss_state *boss, char *line) { int argc, ret = 0, i; char **argv = NULL; if (!boss->do_parse) return(2); ret = parse_line(boss->progname, line, boss->prefix, boss->separators, boss->max_words, &argc, &argv, boss->flag); if (ret <= 0 || argc <= 0) return(0); fprintf(stderr, "-----------------------------------------------------\n"); fprintf(stderr, "parse_line returns %d, argc = %d\n", ret, argc); for (i = 0; i < argc; i++) fprintf(stderr, "%2d : %s\n", i, argv[i]); fprintf(stderr, "-----------------------------------------------------\n"); dispose_parsed_words(&argc, &argv); /* release memory */ return(1); } static int make_reply_lines(struct boss_state *boss) { int i; if (boss->reply_lines != NULL) return(1); boss->reply_lines = calloc(Frontend_xorriso_max_resulT, sizeof(char *)); if (boss->reply_lines == 0) { fprintf(stderr, "FATAL: Out of memory !\n"); return(-1); } boss->reply_lines_count = 0; boss->reply_lines_size = 0; for (i = 0; i < Frontend_xorriso_max_resulT; i++) boss->reply_lines[i] = NULL; return(1); } static int record_reply_line(struct boss_state *boss, char *line) { if (boss->reply_lines[boss->reply_lines_count] != NULL) free(boss->reply_lines[boss->reply_lines_count]); boss->reply_lines[boss->reply_lines_count] = strdup(line); if (boss->reply_lines[boss->reply_lines_count] == NULL) { fprintf(stderr, "FATAL: Out of memory !\n"); return(-1); } boss->reply_lines_count++; return(1); } static int input_interpreter(char *line, struct boss_state *boss) { int argc, ret = 0; char **argv = NULL; ret = parse_line(boss->progname, line, "", "", 6, &argc, &argv, 0); if (ret <= 0 || argc <= 0) return(0); if (strcmp(argv[0], "@parse") == 0) { boss->do_parse = 1; boss->prefix[0] = 0; if (argc > 1) strcpy(boss->prefix, argv[1]); boss->separators[0] = 0; if (argc > 2) strcpy(boss->separators, argv[2]); boss->max_words = 0; if (argc > 3) sscanf(argv[3], "%d", &(boss->max_words)); boss->max_words = 0; if (argc > 4) sscanf(argv[4], "%d", &(boss->flag)); ret = 1; } else if(strcmp(argv[0], "@noparse") == 0) { boss->do_parse = 0; ret = 1; } else if(strcmp(argv[0], "@drain_sieve") == 0) { ret= drain_sieve(boss); } else { ret = 0; } dispose_parsed_words(&argc, &argv); /* release memory */ return ret; } /* -------- Line-to-word parser equivalent to Xorriso_parse_line() -------- */ static int Sfile_sep_make_argv(char *progname, char *line, char *separators, int max_words, int *argc, char ***argv, int flag); int parse_line(char *progname, char *line, char *prefix, char *separators, int max_words, int *argc, char ***argv, int flag) { int ret, bsl_mode; char *to_parse; *argc = 0; *argv = NULL; to_parse = line; bsl_mode = (flag >> 1) & 15; if (prefix[0]) { if (strncmp(line, prefix, strlen(prefix)) == 0) { to_parse = line + strlen(prefix); } else { ret = 2; goto ex; } } ret = Sfile_sep_make_argv(progname, to_parse, separators, max_words, argc, argv, (!(flag & 32)) | 4 | (bsl_mode << 5)); if (ret < 0) { fprintf(stderr, "%s : Severe lack of resources during command line parsing\n", progname); goto ex; } if (ret == 0) { fprintf(stderr, "%s : Incomplete quotation in %s line: %s\n", progname, (flag & 32) ? "command" : "parsed", to_parse); goto ex; } ex:; if (ret <= 0) Sfile_sep_make_argv("", "", "", 0, argc, argv, 2); /* release memory */ return(ret); } int dispose_parsed_words(int *argc, char ***argv) { Sfile_sep_make_argv("", "", "", 0, argc, argv, 2); return(1); } /* -------------- Some helpers copied from xorriso/sfile.c ----------------- */ static int Sfile_destroy_argv(int *argc, char ***argv, int flag) { int i; if(*argc>0 && *argv!=NULL){ for(i=0;i<*argc;i++){ if((*argv)[i]!=NULL) free((*argv)[i]); } free((char *) *argv); } *argc= 0; *argv= NULL; return(1); } /* Converts backslash codes into single characters: \a BEL 7 , \b BS 8 , \e ESC 27 , \f FF 12 , \n LF 10 , \r CR 13 , \t HT 9 , \v VT 11 , \\ \ 92 \[0-9][0-9][0-9] octal code , \x[0-9a-f][0-9a-f] hex code , \cX control-x (ascii(X)-64) @param upto maximum number of characters to examine for backslash. The scope of a backslash (0 to 3 characters) is not affected. @param eaten returns the difference in length between input and output @param flag bit0= only determine *eaten, do not convert bit1= convert \000 to binary 0 */ static int Sfile_bsl_interpreter(char *text, int upto, int *eaten, int flag) { char *rpt, *wpt, num_text[8], wdummy[8]; unsigned int num= 0; *eaten= 0; wpt= text; for(rpt= text; *rpt != 0 && rpt - text < upto; rpt++) { if(flag & 1) wpt= wdummy; if(*rpt == '\\') { rpt++; (*eaten)++; if(*rpt == 'a') { *(wpt++)= 7; } else if(*rpt == 'b') { *(wpt++)= 8; } else if(*rpt == 'e') { *(wpt++)= 27; } else if(*rpt == 'f') { *(wpt++)= 12; } else if(*rpt == 'n') { *(wpt++)= 10; } else if(*rpt == 'r') { *(wpt++)= 13; } else if(*rpt == 't') { *(wpt++)= 9; } else if(*rpt == 'v') { *(wpt++)= 11; } else if(*rpt == '\\') { *(wpt++)= '\\'; } else if(rpt[0] >= '0' && rpt[0] <= '7' && rpt[1] >= '0' && rpt[1] <= '7' && rpt[2] >= '0' && rpt[2] <= '7') { num_text[0]= '0'; num_text[1]= *(rpt + 0); num_text[2]= *(rpt + 1); num_text[3]= *(rpt + 2); num_text[4]= 0; sscanf(num_text, "%o", &num); if((num > 0 || (flag & 2)) && num <= 255) { rpt+= 2; (*eaten)+= 2; *(wpt++)= num; } else goto not_a_code; } else if(rpt[0] == 'x' && ((rpt[1] >= '0' && rpt[1] <= '9') || (rpt[1] >= 'A' && rpt[1] <= 'F') || (rpt[1] >= 'a' && rpt[1] <= 'f')) && ((rpt[2] >= '0' && rpt[2] <= '9') || (rpt[2] >= 'A' && rpt[2] <= 'F') || (rpt[2] >= 'a' && rpt[2] <= 'f')) ) { num_text[0]= *(rpt + 1); num_text[1]= *(rpt + 2); num_text[2]= 0; sscanf(num_text, "%x", &num); if(num > 0 && num <= 255) { rpt+= 2; (*eaten)+= 2; *(wpt++)= num; } else goto not_a_code; } else if(*rpt == 'c') { if(rpt[1] > 64 && rpt[1] < 96) { *(wpt++)= rpt[1] - 64; rpt++; (*eaten)++; } else goto not_a_code; } else { not_a_code:; *(wpt++)= '\\'; rpt--; (*eaten)--; } } else *(wpt++)= *rpt; } *wpt= *rpt; return(1); } #define SfileadrL 4096 static int Sfile_sep_make_argv(char *progname, char *line, char *separators, int max_words, int *argc, char ***argv, int flag) /* bit0= read progname as first argument from line bit1= just release argument list argv and return bit2= abort with return(0) if incomplete quotes are found bit3= eventually prepend missing '-' to first argument read from line bit4= like bit2 but only check quote completeness, do not allocate memory bit5+6= interpretation of backslashes: 0= no interpretation, leave unchanged 1= only inside double quotes 2= outside single quotes 3= everywhere bit7= append a NULL element to argv */ { int i,pass,maxl=0,l,argzaehl=0,bufl,line_start_argc, bsl_mode, ret= 0, eaten; char *cpt,*start; char *buf= NULL; Sfile_destroy_argv(argc,argv,0); if(flag&2) {ret= 1; goto ex;} if(flag & 16) flag|= 4; bsl_mode= (flag >> 5) & 3; buf= calloc(strlen(line) + SfileadrL, 1); if(buf == NULL) {ret= -1; goto ex;} for(pass=0;pass<2;pass++) { cpt= line-1; if(!(flag&1)){ argzaehl= line_start_argc= 1; if(pass==0) maxl= strlen(progname); else strcpy((*argv)[0],progname); } else { argzaehl= line_start_argc= 0; if(pass==0) maxl= 0; } while(*(++cpt)!=0){ if(*separators) { if(strchr(separators, *cpt) != NULL) continue; } else if(isspace(*cpt)) continue; start= cpt; buf[0]= 0; cpt--; if(max_words > 0 && argzaehl >= max_words && *cpt != 0) { /* take uninterpreted up to the end */ cpt+= strlen(cpt) - 1; } while(*(++cpt)!=0) { if(*separators) { if(strchr(separators, *cpt) != NULL) break; } else if(isspace(*cpt)) break; if(*cpt=='"'){ l= cpt-start; bufl= strlen(buf); if(l>0) { strncpy(buf + bufl, start, l); buf[bufl + l]= 0; if(bsl_mode >= 3) { ret= Sfile_bsl_interpreter(buf, l, &eaten, 0); if(ret <= 0) goto ex; } } l= strlen(buf); start= cpt+1; while(*(++cpt)!=0) if(*cpt=='"') break; if((flag&4) && *cpt==0) {ret= 0; goto ex;} l= cpt-start; bufl= strlen(buf); if(l>0) { strncpy(buf + bufl, start, l); buf[bufl + l]= 0; if(bsl_mode >= 1) { ret= Sfile_bsl_interpreter(buf + bufl, l, &eaten, 0); if(ret <= 0) goto ex; } } start= cpt+1; }else if(*cpt=='\''){ l= cpt-start; bufl= strlen(buf); if(l>0) { strncpy(buf + bufl, start, l); buf[bufl + l]= 0; if(bsl_mode >= 3) { ret= Sfile_bsl_interpreter(buf, l, &eaten, 0); if(ret <= 0) goto ex; } } l= strlen(buf); start= cpt+1; while(*(++cpt)!=0) if(*cpt=='\'') break; if((flag&4) && *cpt==0) {ret= 0; goto ex;} l= cpt-start; bufl= strlen(buf); if(l>0) { strncat(buf,start,l);buf[bufl+l]= 0; if(bsl_mode >= 2) { ret= Sfile_bsl_interpreter(buf + bufl, l, &eaten, 0); if(ret <= 0) goto ex; } } start= cpt+1; } if(*cpt==0) break; } l= cpt-start; bufl= strlen(buf); if(l>0) { strncpy(buf + bufl, start, l); buf[bufl + l]= 0; if(bsl_mode >= 3) { ret= Sfile_bsl_interpreter(buf, l, &eaten, 0); if(ret <= 0) goto ex; } } l= strlen(buf); if(pass==0){ if(argzaehl==line_start_argc && (flag&8)) if(buf[0]!='-' && buf[0]!=0 && buf[0]!='#') l++; if(l>maxl) maxl= l; }else{ strcpy((*argv)[argzaehl],buf); if(argzaehl==line_start_argc && (flag&8)) if(buf[0]!='-' && buf[0]!=0 && buf[0]!='#') sprintf((*argv)[argzaehl],"-%s", buf); } argzaehl++; if(*cpt==0) break; } if(pass==0){ if(flag & 16) {ret= 1; goto ex;} *argc= argzaehl; if(argzaehl>0 || (flag & 128)) { *argv= (char **) calloc((argzaehl + !!(flag & 128)), sizeof(char *)); if(*argv==NULL) {ret= -1; goto ex;} } for(i=0;i<*argc;i++) { (*argv)[i]= (char *) calloc(maxl + 1, 1); if((*argv)[i]==NULL) {ret= -1; goto ex;} } if(flag & 128) (*argv)[*argc]= NULL; } } ret= 1; ex: if(buf != NULL) free(buf); return(ret); } #!/bin/sh # Copyright (C) 2015 - 2022 # Thomas Schmitt <scdbackup@gmx.net>, libburnia-project.org # Provided under BSD license: Use, modify, and distribute as you like. echo >&2 echo "frontend/grub-mkrescue-sed.sh manipulating xorriso arguments" >&2 echo >&2 # This script may be handed by its absolute path to grub-mkrescue # via option --xorriso= . E.g. # # mkdir minimal # touch minimal/empty-file.txt # grub-mkrescue -o output.iso minimal \ # --xorriso=/home/thomas/xorriso-1.4.3./frontend/grub-mkrescue-sed.sh # # It will manipulate the xorriso arguments before they get executed by a # xorriso program. Default is the neighboring ../../xorriso/xorriso program or, # if that neighbor cannot be found, the system-wide installed xorriso. # # The mode "mjg" implements a layout which resembles Fedora LiveCD and Debian # ISOs which are bootable by ISOLINUX for BIOS and GRUB2 for EFI. # Its GPT is considered to be surplus, according to UEFI specs. # # The mode "mbr_only" implements an alternative layout according to UEFI 2.4, # section 2.5.1 and table 16. No GTP, HFS+, or APM. # This mode produces a mountable ISO 9660 partition 1 only if variable # MKRESCUE_SED_PROTECTIVE is empty or set to "no". # # The mode "mbr_hfs" is like "mbr_only" but with HFS+ mentioned in APM. # It is still compliant to UEFI with no potentially deceiving GPT. # If you add xorrisofs option -part_like_isohybrid then no gap fillig APM # partition will emerge. # # Mode "gpt_appended" represents the same layout as "mbr_only" by GPT rather # than by MBR partition table. It differs from "original" by the fact that # option -partition_offset 16 is implied and that the first partition may # be used to mount the ISO 9660 filesystem. MKRESCUE_SED_PROTECTIVE is ignored, # because neat GPT is indicated by the existence of a Protective MBR. # # These modes avoid duplicate storing of the EFI system partition "efi.img" # by xorrisofs option -e "--interval:appended_partition_${partno}:all::" # which is new to xorriso-1.4.4. # If "_copy" is appended to the mode name, then the file /efi.img will # appear in the ISO 9660 filesystem and traditional -e "/efi.img" is used. # # "mbr_only_copy" is supposed to work with unmodified xorriso >= 1.3.2 # # Variation settings # # The environment variables MKRESCUE_SED_* override the following # default settings: # Manipulation mode: # "mjg" = ESP in MBR+GPT+APM, with HFS+ # "mbr_only" = ESP in MBR, without HFS+ # "mbr_hfs" = ESP in MBR, HFS+ in APM # "gpt_appended" = ESP in GPT, without HFS+ # $mode"_copy" = one of above modes, ESP in ISO and as appended partition # "original" = pass arguments unchanged mode="mbr_only" if test -n "$MKRESCUE_SED_MODE" then mode="$MKRESCUE_SED_MODE" fi # First argument of -append_partition with mode "mjg". Values: 1 or 2. partno=1 if test -n "$MKRESCUE_SED_PARTNO" then partno="$MKRESCUE_SED_PARTNO" fi # Replacement for option --protective-msdos-label. Either itself or empty text. # If the environment variable contains the word "no", this means empty. protective="" if test -n "$MKRESCUE_SED_PROTECTIVE" then if test x"$MKRESCUE_SED_PROTECTIVE" = xno then protective="" elif test x"$MKRESCUE_SED_PROTECTIVE" = xyes then protective="--protective-msdos-label" else protective="$MKRESCUE_SED_PROTECTIVE" fi fi # "yes" shows xorriso arguments, "extra" additionally shows all input files. debug=no if test -n "$MKRESCUE_SED_DEBUG" then debug="$MKRESCUE_SED_DEBUG" fi # The path to the program that will be executed with the converted arguments. if test -n "$MKRESCUE_SED_XORRISO" then xorriso="$MKRESCUE_SED_XORRISO" else # Prefer neighboring xorriso binary over system-wide installed one. self_dir="$(dirname $(dirname "$0") )" if test -x "$self_dir"/xorriso/xorriso then xorriso="$self_dir"/xorriso/xorriso else xorriso="xorriso" fi fi # MKRESCUE_SED_XORRISO_ARGS will be used as first arguments of the xorriso run. # (Trailing xorriso arguments may be simply added to the grub-mkrescue # command line.) # Each argument must be a single word. No whitespace. No quotation marks. # "yes" in MKRESCUE_SED_IN_EFI_NO_PT overwrites the MBR partition table area # in the EFI boot image by zeros. Some EFI implementations get stuck when # seeing in the EFI partition a partition table entry which begins at LBA 0. # "extra" not only zeros the partition table but also the MBR signature. efi_zero_inner_pt=no if test -n "$MKRESCUE_SED_IN_EFI_NO_PT" then efi_zero_inner_pt="$MKRESCUE_SED_IN_EFI_NO_PT" fi # "yes" in MKRESCUE_SED_UNPACK_EFI_TO_ISO causes the file tree from the FAT # image efi.img to be unpacked by mcopy to a directory in /tmp which then # gets mapped into the root directory of the emerging ISO 9660 filesystem. # This enables a way of installing the result image onto a USB stick with # FAT filesystem by simply copying its full file tree from ISO to FAT. # Whether the payload files of the ISO will be able to work from FAT depends # on their expectation towards file names and attributes. # WARNING: Make sure that the files in efi.img do not collide with your # payload files or with files added to the ISO by GRUB. Before using # MKRESCUE_SED_UNPACK_EFI_TO_ISO, make a vanilla grub-mkrescue ISO # with your payload, mount it and then its /efi.img file, then use # program "find" on the mount point of efi.img to see all its files. # Compare this with the output of "find" on the ISO mount point. efi_unpack_to_iso=no if test -n "$MKRESCUE_SED_UNPACK_EFI_TO_ISO" then efi_unpack_to_iso="$MKRESCUE_SED_UNPACK_EFI_TO_ISO" fi # # Do the work # # grub-mkrescue inquires features by running these arguments if test "$*" = "-as mkisofs -help" then "$xorriso" "$@" exit $? fi echo "frontend/grub-mkrescue-sed.sh mode: $mode" >&2 echo >&2 if test x"$debug" = xyes -o x"$debug" = xextra then # Show arguments echo "##### Begin of received arguments" >&2 echo "$0" >&2 for i in "$@" do echo "$i" >&2 done echo "##### End of received arguments" >&2 echo >&2 fi # Check for option -iso_mbr_part_type which is new in 1.4.8 iso_mbr_part_type= if "$xorriso" -as mkisofs -help 2>&1 | grep iso_mbr_part_type >/dev/null then iso_mbr_part_type="-iso_mbr_part_type 0x00" fi # Look for the name of the /tmp directory with the GRUB2 files. # It is the next argument after -r. But as default accept any /tmp/grub.* next_is_dir=0 dir="." for i in "$@" do if test x"$i" = x"-r" then next_is_dir=1 elif test $next_is_dir = 1 then next_is_dir=0 if echo "$i" | grep '^/tmp/grub.' >/dev/null 2>&1 then test -d "$i" && dir="$i" fi elif test "$dir" = "." then if echo "$i" | grep '^/tmp/grub.' >/dev/null 2>&1 then test -d "$i" && dir="$i" fi fi done if test x"$debug" = xextra then # Show files on disk find "$dir" 2>&1 echo 2>&1 fi if test x"$efi_zero_inner_pt" = xyes -o x"$efi_zero_inner_pt" = xextra then did_dd=0 if test -e "$dir"/efi.img then # Look for 0x55 0xAA in bytes 510 and 511 magic=$(dd bs=1 skip=510 count=2 if="$dir"/efi.img 2>/dev/null | \ od -c | head -1 | awk '{print $2 " " $3}') if test "$magic" = "U 252" then echo "Performing actions for MKRESCUE_SED_IN_EFI_NO_PT=$efi_zero_inner_pt" >&2 dd if=/dev/zero bs=1 seek=446 count=64 conv=notrunc of="$dir"/efi.img did_dd=1 if test "$efi_zero_inner_pt" = extra then dd if=/dev/zero bs=1 seek=510 count=2 conv=notrunc of="$dir"/efi.img fi echo >&2 fi fi if test "$did_dd" = 0 then echo >&2 echo "$0 : NOTE : No EFI image found or no MBR signature in it." >&2 echo "$0 : NOTE : Will not obey MKRESCUE_SED_IN_EFI_NO_PT=$efi_zero_inner_pt" >&2 echo >&2 fi fi efi_temp_tree= if test x"$efi_unpack_to_iso" = xyes then if test -e "$dir"/efi.img then temp_tree=/tmp/grub-mkrescue-sed-et."$$" # The mcopy command is the inverse of what grub-mkrescue does to pack it up if mcopy -s -i "$dir"/efi.img ::/ "$temp_tree" then efi_temp_tree="$temp_tree" if test x"$debug" = xyes -o x"$debug" = xextra then echo "Temporarily extracted $dir/efi.img to $temp_tree" >&2 echo >&2 if test x"$debug" = xextra then # Show extracted files find "$temp_tree" >&2 echo >&2 fi fi elif test -e "$temp_tree" then rm -r "$temp_tree" fi if test -z "$efi_temp_tree" then echo >&2 echo "$0 : NOTE : Could not extract efi.img to $temp_tree" >&2 echo "$0 : NOTE : Thus cannot obey MKRESCUE_SED_UNPACK_EFI_TO_ISO." >&2 echo >&2 fi fi fi efi_tmp_name= if test x"$mode" = xmjg \ -o x"$mode" = xmbr_only \ -o x"$mode" = xgpt_appended \ -o x"$mode" = xmbr_hfs then # Move EFI partition image file out of the "$dir" tree, i.e. out of the ISO efi_tmp_name=grub-mkrescue-sed-ei.$$ if test -e "$dir"/efi.img then mv "$dir"/efi.img /tmp/$efi_tmp_name if test x"$debug" = xyes -o x"$debug" = xextra then echo "Temporarily moved $dir/efi.img to /tmp/$efi_tmp_name" >&2 echo >&2 fi elif test -e /tmp/$efi_tmp_name then rm /tmp/$efi_tmp_name fi fi if test x"$mode" = xmjg then # Exchange arguments for the experimental GRUB2 mjg layout x=$(echo " $*" | sed \ -e "s/-efi-boot-part --efi-boot-image/-no-pad -append_partition $partno 0xef \/tmp\/$efi_tmp_name/" \ -e "s/--efi-boot efi\.img/-eltorito-alt-boot -e --interval:appended_partition_${partno}:all:: -no-emul-boot -isohybrid-gpt-basdat/" \ -e "s/--protective-msdos-label/$protective -part_like_isohybrid/" \ ) elif test x"$mode" = xmjg_copy then # Exchange arguments for the experimental GRUB2 mjg layout x=$(echo " $*" | sed \ -e "s/-efi-boot-part --efi-boot-image/-no-pad -append_partition $partno 0xef \/tmp\/$(basename "$dir")\/efi.img/" \ -e "s/--efi-boot efi\.img/-eltorito-alt-boot -e efi.img -no-emul-boot -isohybrid-gpt-basdat/" \ -e "s/--protective-msdos-label/$protective -part_like_isohybrid/" \ ) elif test x"$mode" = xmbr_only then # Exchange arguments for no-HFS MBR-only layout x=$(echo " $*" | sed \ -e "s/-efi-boot-part --efi-boot-image/$iso_mbr_part_type -no-pad -append_partition 2 0xef \/tmp\/$efi_tmp_name/" \ -e "s/--efi-boot efi\.img/-eltorito-alt-boot -e --interval:appended_partition_2:all:: -no-emul-boot/" \ -e "s/-hfsplus .*CoreServices\/boot.efi//" \ -e "s/--protective-msdos-label/$protective/" \ ) elif test x"$mode" = xmbr_only_copy then # Exchange arguments for no-HFS MBR-only layout x=$(echo " $*" | sed \ -e "s/-efi-boot-part --efi-boot-image/$iso_mbr_part_type -no-pad -append_partition 2 0xef \/tmp\/$(basename "$dir")\/efi.img/" \ -e "s/-hfsplus .*CoreServices\/boot.efi//" \ -e "s/--protective-msdos-label/$protective/" \ ) elif test x"$mode" = xmbr_hfs then # Exchange arguments for MBR and HFS+ layout x=$(echo " $*" | sed \ -e "s/-efi-boot-part --efi-boot-image/$iso_mbr_part_type -no-pad -append_partition 2 0xef \/tmp\/$efi_tmp_name/" \ -e "s/--efi-boot efi\.img/-eltorito-alt-boot -e --interval:appended_partition_2:all:: -no-emul-boot/" \ -e "s/--protective-msdos-label/$protective/" \ ) elif test x"$mode" = xmbr_hfs_copy then # Exchange arguments for MBR and HFS+ layout x=$(echo " $*" | sed \ -e "s/-efi-boot-part --efi-boot-image/$iso_mbr_part_type -no-pad -append_partition 2 0xef \/tmp\/$(basename "$dir")\/efi.img/" \ -e "s/--protective-msdos-label/$protective/" \ ) elif test x"$mode" = xgpt_appended then # Exchange arguments for no-HFS MBR-only layout x=$(echo " $*" | sed \ -e "s/-efi-boot-part --efi-boot-image/-no-pad -append_partition 2 0xef \/tmp\/$efi_tmp_name -appended_part_as_gpt -partition_offset 16/" \ -e "s/--efi-boot efi\.img/-eltorito-alt-boot -e --interval:appended_partition_2:all:: -no-emul-boot/" \ -e "s/-hfsplus .*CoreServices\/boot.efi//" \ ) elif test x"$mode" = xgpt_appended_copy then # Exchange arguments for no-HFS MBR-only layout x=$(echo " $*" | sed \ -e "s/-efi-boot-part --efi-boot-image/-no-pad -append_partition 2 0xef \/tmp\/$(basename "$dir")\/efi.img -appended_part_as_gpt -partition_offset 16/" \ -e "s/-hfsplus .*CoreServices\/boot.efi//" \ ) elif test x"$mode" = xoriginal then # Pass arguments unchanged x=" $*" else echo >&2 echo "$0 : FATAL : Unknown manipulation mode '$mode'." >&2 echo >&2 exit 1 fi if echo "$efi_temp_tree" | grep '^/tmp/' >/dev/null then # Does the xorriso run end in native command mode ? separator_seen=0 for i in "$@" do if test x"$i" = x-- then separator_seen=1 fi done if test "$separator_seen" = 1 then # Native mode possible: Enable it for sure and then use -map x=" $x -- -map $efi_temp_tree /" else # Hopefully nobody finds a way to leave mkisofs emulation without "--" arg x=" $x -graft-points /=$efi_temp_tree" fi fi if test x"$debug" = xyes -o x"$debug" = xextra then echo "+ converted xorriso arguments:" >&2 echo " $x" >&2 echo >&2 fi # Run xorriso binary with the converted arguments use_gdb=no if test "$use_gdb" = yes then gdb_file=/tmp/grub-mkrescue-sed-gdb echo b assess_appended_gpt >$gdb_file echo run $MKRESCUE_SED_XORRISO_ARGS $x >>$gdb_file gdb -x $gdb_file "$xorriso" ret=0 else "$xorriso" $MKRESCUE_SED_XORRISO_ARGS $x ret=$? fi # Move back the ESP if it was separated if test -n "$efi_tmp_name" -a -e /tmp/$efi_tmp_name then mv /tmp/$efi_tmp_name "$dir"/efi.img fi # Remove possible extracted EFI partition tree if echo "$efi_temp_tree" | grep '^/tmp/' >/dev/null then rm -r "$efi_temp_tree" fi exit $ret ˆF��bîwà� éËßÀ«À¨T à�Ÿ}6¿OÐ #nµ ¤�2Ë„ý¢ó� “-tŒ‡,íçE²QsÌöæ£ä#!/bin/bash # # Demo of a shell frontend that communicates with a xorriso slave via # two named pipes. # # This script creates two named pipes and starts xorriso with command # -named_pipes_loop cleanup /tmp/xorriso_stdin_pipe_$$ xorriso_stdin_pipe_$$ - # Its main loop prompts the user for commands, sends them to xorriso, # receives the replies, and parses them by xorriso command # -msg_op parse_silently. The resulting words are printed to stdout. # # xorriso removes the two pipes when it finishes execution of -named_pipes_loop # regularly. (E.g. because of commands -end or -rollback_end or because of # name loop control message "end_named_pipe_loop".) # The vanishing of the pipe files tells this script that xorriso is gone. # # # Copyright (C) 2013 # Thomas Schmitt <scdbackup@gmx.net>, libburnia-project.org # Provided under BSD license: Use, modify, and distribute as you like. # # What xorriso program to use xorriso=xorriso if test o"$1" = o"-xorriso" then xorriso="$2" fi # Version of xorriso and minimum requirement by this script export xorriso_version= export xorriso_version_req=1.3.1 # Info about the xorriso slave process export xorriso_is_running=0 export xorriso_pid=0 export xorriso_will_end=0 # Will be set to 1 before this script ends normally export normal_end=0 # ---------------- An interpreter for quoted xorriso replies ---------------- # xorriso commands like -lsl wrap filenames into quotation marks in order # to unambiguously represent any character byte except the 0-byte. # This piece of code parses input strings into words by letting xorriso # command -msg_op "parse_silently" do the hard work. # The input strings should be composed by concatenating input lines with # newline characters between them. Begin by submitting a single line (without # newline at its end) and retry with an appended further line, if # xorriso_parse # returns 1. See below xorriso_cmd_and_handle_result() for an example. # The parsed reply words. # Valid are reply_array[0] to reply_array[reply_count-1)] export reply_array export reply_count # Interpret reply of -msg_op parse xorriso_recv_parse_reply() { reply_count=0 unset reply_array export reply_array ret=-1 read ret if test "$ret" -lt 0 -o -z "$ret" then echo "Unexpected text as first reply line of -msg_op parse" >&2 xorriso_is_running=0 return 2 fi test "$ret" = 0 && return "1" read num_strings string_count=0 while true do test "$string_count" -ge "$num_strings" && break read num_lines line_count=0 acc= while true do test "$line_count" -ge "$num_lines" && break read line test "$line_count" -gt 0 && acc="$acc"$'\n' acc="$acc""$line" line_count=$(($line_count + 1)) done reply_array["$string_count"]="$acc" string_count=$(($string_count + 1)) done reply_count="$num_strings" return 0 } # Parse a quoted multi-line string into words xorriso_parse() { # $1 : The string which shall be parsed # $2 : The number of concatenated input lines (= number of newlines + 1) # return: 0= array is valid , 1= line incomplete , 2= other error test "$xorriso_is_running" = 0 && return 1 xorriso_send_cmd "msg_op parse_silently "'"'"'' '' 0 0 $2"'"'$'\n'"$1" || \ return 2 xorriso_recv_parse_reply <"$result_pipe" || xorriso_is_running=0 ret=$? test "$xorriso_is_running" = 0 && ret=2 return "$ret" } # ------------- End of interpreter for quoted xorriso replies -------------- # Send one or more command lines to xorriso xorriso_send_cmd() { # $1 : the lines to send # >>> is it possible to have a timeout on echo ? if test -p "$cmd_pipe" then echo -E "$1" >"$cmd_pipe" else xorriso_is_running=0 return 1 fi } # Make filenames safe for transport by wrapping them in quotes and # escaping quotes in their text xorriso_esc() { echo -n "'" echo -n "$1" | sed -e "s/'/'"'"'"'"'"'"'/g" echo -n "'" } # A handler function for xorriso_cmd_and_handle_result xorriso_reply_to_stdout() { echo "${reply_array[*]}" } # Let a handler inspect the result lines of a xorriso command line xorriso_cmd_and_handle_result() { # $1: handler command word and possibly argument words # $2: command line for xorriso if test "$xorriso_is_running" = 0 then return 1 fi handler="$1" xorriso_send_cmd "$2" || return 1 res=$(cat "$result_pipe") ret=$? if test "$xorriso_will_end" = 1 -o "$xorriso_is_running" = 0 -o "$ret" -ne 0 then test -n "$res" && echo -n "$res" xorriso_is_running=0 test "$ret" = 0 || return 1 return 0 fi test -z "$res" && return 0 echo "$res" | \ while read line do line_count=1 while true do xorriso_parse "$line" "$line_count" ret=$? test "$ret" = 0 && break if test "$ret" = 2 then return 1 fi read addon line="$line"$'\n'"$addon" line_count=$(expr "$line_count" + 1) done # One can now make use of reply_array[0...(reply_count-1)] $handler done return 0 } # Execute -version and let xorriso_version_handler interpret reply xorriso_check_version() { lookfor='^xorriso version : ' xorriso_version=$("$xorriso" -version 2>/dev/null | grep "$lookfor" | \ sed -e "s/${lookfor}//") ret=$? if test "$ret" -ne 0 -o "$xorriso_version" = "" then echo "SORRY: Program run '${xorriso}' -version did not yield a result." >&2 echo >&2 exit 2 fi smallest=$((echo "$xorriso_version_req" ; echo "$xorriso_version" ) | \ sort | head -1) test "$smallest" = "$xorriso_version_req" && return 0 echo "SORRY: xorriso version too old: ${xorriso_version} . Need at least xorriso-${xorriso_version_req} ." >&2 echo >&2 exit 2 } # To be executed on exit xorriso_cleanup() { send_end_cmd=0 if test -p "$cmd_pipe" -a "$xorriso_is_running" = 1 then if test "$normal_end" = 0 then echo "Checking whether xorriso is still running ..." >&2 set -x # Give xorriso time to abort sleep 1 if ps | grep '^'"$xorriso_pid" >/dev/null then # >>> try to further confirm xorriso identity send_end_cmd=1 fi else send_end_cmd=1 fi fi test "$normal_end" = 0 && set -x if test "$send_end_cmd" = 1 then echo "Sending xorriso an -end command ..." >&2 xorriso_send_cmd "end" && \ test -p "$result_pipe" && cat "$result_pipe" >/dev/null fi test -p "$cmd_pipe" && rm "$cmd_pipe" test -p "$result_pipe" && rm "$result_pipe" } # ---------------------------------- main --------------------------------- # Choose pipe names export cmd_pipe=/tmp/xorriso_stdin_pipe_$$ export result_pipe=/tmp/xorriso_stdout_pipe_$$ # Check the program whether it is modern enough xorriso_check_version "$xorriso" # Prepare for then end of this script trap xorriso_cleanup EXIT # Create the pipes and start xorriso mknod "$cmd_pipe" p mknod "$result_pipe" p "$xorriso" -abort_on NEVER -for_backup \ -named_pipe_loop cleanup:buffered "$cmd_pipe" "$result_pipe" "-" & xorriso_pid=$! xorriso_is_running=1 # Get a sign of life from xorriso before issuing the loop prompt xorriso_cmd_and_handle_result xorriso_reply_to_stdout \ "print_info 'xorriso process ${xorriso_pid} started by $0'" echo >&2 # Now get commands from the user, send them to xorriso and display them # via the simple handler xorriso_reply_to_stdout() while test "$xorriso_is_running" = 1 do if test -p "$cmd_pipe" then echo -n "xorriso> " >&2 else echo "$0 : Lost contact to xorriso process $xorriso_pid" >&2 xorriso_is_running=0 break fi read line if echo "$line" | grep '^-*end$' >/dev/null then break fi if echo "$line" | grep '^-*rollback_end$' >/dev/null then xorriso_will_end=1 fi xorriso_cmd_and_handle_result xorriso_reply_to_stdout "$line" done # Prevent set -x in the exit handler normal_end=1 #!/usr/bin/wish # # xorriso-tcltk # Copyright (C) 2012 - 2023 # Thomas Schmitt <scdbackup@gmx.net>, libburnia project. # Provided under BSD license: Use, modify, and distribute as you like. # # This is mainly a proof of concept for xorriso serving under a frontend. # It exercises several fundamental gestures of communication: # - connecting via two named pipes # - sending commands # - receiving replies # - inquiring the xorriso message sieve # - using the xorriso parsing service # # It also may serve as educational frontend to xorriso which tells by # its message window how to operate xorriso by commands and what it will # reply. # Note that any other language than Tcl/Tk could be used, if it only can # do i/o via standard input and standard output or via named pipes. # Further it has to perform integer arithmetics and string manipulations. # And, well, a graphical widget set would be nice. set own_version "1.5.7" # Minimum version of xorriso to be used as backend process. # Older versions of xorriso do not offer commands -msg_op and -launch_frontend set min_xorriso_version "1.2.6" proc print_usage {argv0} { puts stderr "Usage:" puts stderr " $argv0 \[options\]" puts stderr "Options:" puts stderr " All options must be given with two dashes (\"--option\") in" puts stderr " order to distinguish them from any options of the Tcl shell." puts stderr " --help" puts stderr " Print this text and exit." puts stderr " --stdio" puts stderr " Establish connection to xorriso via stdin and stdout." puts stderr " E.g. when letting xorriso start this frontend program:" puts stderr " xorriso -launch_frontend \$(which xorriso-tcltk) --stdio --" puts stderr " --named_pipes cmd_fifo reply_fifo" puts stderr " Establish connection to a xorriso process started by:" puts stderr " xorriso -dialog on <cmd_fifo >reply_fifo" puts stderr " which is then ready for a run of:" puts stderr " xorriso-tcltk --named_pipes cmd_fifo reply_fifo" puts stderr " It is important that the parent of xorriso and of this" puts stderr " tcl/tk frontend opens the named pipe for commands before" puts stderr " it opens the named pipe for replies. This avoids deadlock." puts stderr " --silent_start" puts stderr " Do not issue the start message xorriso-tcltk-version." puts stderr " This works only if --silent_start is the first argument." puts stderr " --no_extract" puts stderr " Do not allow extraction of files from ISO filesystem to" puts stderr " hard disk. This is not revokable during the program run." puts stderr " --no_bwidget" puts stderr " Do not try to load the Tcl/Tk package BWidget which is" puts stderr " a prerequisite for the \"/\" file browser buttons." puts stderr " --geometry {+|-}X{+|-}Y" puts stderr " Sets the position of the main window." puts stderr " --click_to_focus" puts stderr " Chooses that input fields and list boxes get the keyboard" puts stderr " focus only when being clicked by the mouse. (Default)" puts stderr " --auto_focus" puts stderr " Chooses that the keyboard focus is where the mouse" puts stderr " pointer is." puts stderr " --pipe_log_file path" puts stderr " Set a file address for logging of xorriso commands and" puts stderr " reply messages and enable this logging." puts stderr " The log lines will be appended. Path \"-\" means stderr." puts stderr " --script_log_file path" puts stderr " Set a file address for logging of essential xorriso" puts stderr " commands and enable this logging." puts stderr " The log lines will be appended. Path \"-\" means stderr." puts stderr " --script_log_all_commands" puts stderr " With logging of commands log non-essential commands too." puts stderr " --use_command_move" puts stderr " Use xorriso command -move for the \"Move to:\" button" puts stderr " if xorriso version is >= 1.2.8" puts stderr " --use_command_mv" puts stderr " Use xorriso command -mv for the \"Move to:\" button." puts stderr "" puts stderr "If neither --stdio nor --named_pipes is given, then this script" puts stderr "will try to locate itself in the filesystem and start a xorriso" puts stderr "run that launches it again." puts stderr "" puts stderr "In the running GUI, click with the rightmost mouse button on" puts stderr "any GUI element to get its particular help text." puts stderr "" } # ------------------------------- the frontend ---------------------------- # # Connects to a xorriso process, sends commands, receives replies, # prepares replies for GUI # Connection to xorriso set cmd_conn "" set reply_conn "" # The addresses of the named pipes, if such are used (see option -named_pipe) set cmd_pipe_adr "" set reply_pipe_adr "" # The command to send (or the command most recently sent) set cmdline "" # Whether to clear the cmdline after sending set cmdline_clear true # Command counter set cmd_sent 0 # Current -mark synchronization text set mark_count 0 # Results of most recent await_all_replies set info_list "" set info_count 0 set emerging_info "" set result_list "" set result_count 0 set emerging_result "" # Highest severities encountered in total and with most recent command set highest_total_sev ALL set highest_total_sev_msg "" set highest_cmd_sev ALL set highest_cmd_sev_msg "" # This one registers like highest_cmd_sev with threshold ALL set highest_seen_cmd_sev ALL # State of last read_sieve command set sieve_ret 0 # How many texts to pass with one parse_bulk command set bulk_parse_max_chunk 200 # Parse parameters set bulk_parse_prefix "" set bulk_parse_separators "" set bulk_parse_max_words "" set bulk_parse_flag "" # The announced number of texts to parse set bulk_parse_num_texts "" # Whether to complain on stderr about broken pipes. # This may be expected when xorriso is being shut down by this frontend. set expect_broken_pipes "0" # Whether to use command -move rather than -mv. Possible since xorriso-1.2.8. set use_command_move 1 # Whether to enable -hardlinks mode "on". Too slow before xorriso-1.3.0. set use_command_hardlinks_on 1 # Local copies of xorriso state # Addresses of drives (or image files) as shown by their text fields in the GUI set outdev_adr "" set indev_adr "" # Addresses of drives (or image files) as set in xorriso (after inquire_dev) set eff_outdev_adr "" set eff_indev_adr "" # Whether the medium is blank, appendable, closed, missing set indev_medium_status "missing" set outdev_medium_status "missing" # What kind of medium is presented by the drive set indev_profile "" set outdev_profile "" # List of known drive addresses set devlist "" # Intermediate storage for messages until the GUI is up with .msglist box set pre_msglist "" # Whether overwriting of files in the ISO model is allowed set overwrite_iso_files 1 # If overwrite_iso_files is 1: Whether overwriting of ISO directories is allowed set overwrite_iso_dirs 0 # Whether overwriting of files on disk is allowed set overwrite_disk_files 0 # The file where to log commands and replies for debugging purposes set debug_log_file "" set debug_log_conn stderr # Whether to log all commands and replies to the debug_log_file set debug_logging 0 # The result of the most recent isofs_ls run set isofs_ls_result "" # The result of the most recent localfs_ls run set localfs_ls_result "" # The communication channel where to log files (if it is not the empty text) set cmd_log_conn "" # The address under which cmd_log_conn was opened set cmd_log_target "" # Whether to log essential commands: 0=off , 1=no extract , 2=with extract set cmd_logging_mode 0 # Whether to log all commands if cmd_logging_mode is 1: 0=off , 1=on set cmd_logging_all 0 # The last logged -cd path. Used to prevent redundant logging of -cd. set recent_cd_path "" # The file address and the channel for xorriso command script execution set execute_script_adr "" set execute_script_conn "" # Whether extraction to disk shall be allowed in scripts set script_with_osirrox 0 # Whether extraction to disk is allowed set osirrox_allowed 1 # xorriso specific constants # List of severities (gets later overridden by -msg_op list_sev -) set xorriso_severity_list { ALL DEBUG UPDATE NOTE HINT WARNING SORRY MISHAP FAILURE FATAL ABORT NEVER } set scan_event_threshold HINT # --------- Communication between frontend and xorriso ---------- # Open the connection to a pair of named pipes. Program option -named_pipes # proc init_frontend_named_pipes {cmd_pipe reply_pipe} { global cmd_conn global reply_conn set cmd_conn [open $cmd_pipe w] set reply_conn [open $reply_pipe r] # Note: dissuaded flags would be necessary for opening idle fifo # set reply_conn [open $reply_pipe {RDONLY NONBLOCK}] } # Send a command line to the xorriso process. Do not wait for reply. # proc send_async_cmd {cmd} { global cmd_sent cmd_conn debug_logging debug_log_conn display_busy 1 log_command $cmd 0 debug_log_puts \ " ==============================================================" debug_log_puts " $cmd" display_msg "======> $cmd" incr cmd_sent puts $cmd_conn $cmd flush $cmd_conn } # Send a command line and a -mark command to xorriso. Wait until the # mark message confirms that all command output has been received. # proc send_marked_cmd {cmd} { global cmd_conn mark_count send_async_cmd $cmd incr mark_count set mark_cmd "-mark $mark_count" debug_log_puts " $mark_cmd" puts $cmd_conn $mark_cmd flush $cmd_conn await_all_replies } # Send a command and make it a candidate for the log script # proc send_loggable_cmd {cmd} { log_command $cmd 1 send_marked_cmd $cmd } # Send a command that shall not be displayed in the message log # proc send_silent_cmd {cmd} { set disp_en_mem [set_display_msg 0] send_marked_cmd $cmd set_display_msg $disp_en_mem } # Wait for the currently pending mark message to arrive. # Buffer all received result lines and info messages. # proc await_all_replies {} { global reply_conn mark_count result_count result_list global info_count info_list expect_broken_pipes global .busy_text clear_reply_lists while {1} { set ret [gets $reply_conn line] if {$ret < 0} { if {$expect_broken_pipes != 1} { puts stderr "EOF at reply pipe" } break } debug_log_puts $line if {[string range $line 0 0] == "M"} { if {[string range $line 5 end] == $mark_count} { break } else { # outdated mark message continue } } de_pkt_line $line } display_busy 0 } # Decode -pkt_output format to complete lines and buffer them. # proc de_pkt_line {line} { global info_list global info_count global emerging_info global result_list global result_count global emerging_result # Distinguish R and I set ch [string range $line 0 0] set payload [string range $line 5 end] if {$ch == "R"} { set emerging_result "$emerging_result$payload" } else { if {$ch == "I"} { set emerging_info "$emerging_info$payload" } else { return "" }} # if line end : add to list if {[string range $line 2 2] == "1"} { if {$ch == "R"} { lappend result_list $emerging_result incr result_count display_msg $emerging_result set emerging_result "" } else { lappend info_list $emerging_info incr info_count display_msg $emerging_info scan_info_for_event $emerging_info set emerging_info "" } } } # Search in the decoded info messages for the most severe event reports. # proc scan_info_for_event {line} { global highest_total_sev highest_total_sev_msg global highest_cmd_sev highest_cmd_sev_msg highest_seen_cmd_sev global scan_event_threshold global display_msg_enabled # check for word : CAPS : text ... set ret [regexp {[a-z][a-z]*[ ]*: [A-Z][A-Z]* :} $line] if {$ret != 1} {return ""} # retrieve severity set pos [string first ":" $line] set sev [string range $line [expr $pos+2] end] set pos [string first ":" $sev] set sev [string range $sev 0 [expr $pos-2]]; if {[compare_sev $sev $highest_seen_cmd_sev] > 0} { set highest_seen_cmd_sev $sev } if {[compare_sev $sev $scan_event_threshold] < 0} {return ""} if {$display_msg_enabled == 0} { set display_msg_enabled 1 display_msg $line set display_msg_enabled 0 } if {[compare_sev $sev $highest_total_sev] >= 0} { set highest_total_sev $sev set highest_total_sev_msg [escape_newline $line 0] } if {[compare_sev $sev $highest_cmd_sev] >= 0} { set highest_cmd_sev $sev set highest_cmd_sev_msg [escape_newline $line 0] } } # Unpack the output format of -msg_op read_sieve into a result_list # of strings which each hold one parsed word. # proc de_sieve {} { global result_list global sieve_ret set sieve_ret [lindex $result_list 0] set sieve_result_count [lindex $result_list 1] set payload "" set sieve_result_count 0 for {set i 2} {$i < [llength $result_list]} {incr i} { set line "" set num_lines [lindex $result_list $i] for {set j 0} {$j < $num_lines} {incr j} { incr i set line "$line[lindex $result_list $i]" if {$j < $num_lines - 1} { set line "$line\n" } else { lappend payload $line incr sieve_result_count } } } set result_list $payload set result_count $sieve_result_count } # Alternative to proc await_all_replies. It waits for a line at one of the # three channels and displays all lines which it receives before that line. # Used before this frontend had the opportunity to set up xorriso by commands # like -pkt_output "on". # proc wait_for_msg {prefix channel} { global reply_conn if {$channel == "M"} { set channel_prefix "M:0: " } else { set channel_prefix "$channel:1: " } set prefix_l [string length $prefix] while {1} { # >>> Have a timeout set ret [gets $reply_conn line] if {$ret < 0} { break } debug_log_puts $line if {[string length $line] < $prefix_l} { display_msg $line continue } if {[string range $line 0 [expr $prefix_l-1]] == $prefix} { return [string range $line $prefix_l end] } if {[string length $line] >= [expr $prefix_l+5]} { if {[string range $line 0 4] == $channel_prefix} { if {[string range $line 5 [expr $prefix_l+4]] == $prefix} { return [string range $line [expr $prefix_l+5] end] } } } display_msg $line } } # Reset the buffer for result lines and info messages. # proc clear_reply_lists {} { global info_list global info_count global emerging_info global result_list global result_count global emerging_result set info_list "" set info_count 0 set emerging_info "" set result_list "" set result_count 0 set emerging_result "" } # Reset the register of the most severe event for command sequences. # Typically this is done before executing the commands of a procedure # that is triggered by the user interface. # proc reset_highest_cmd_sev {} { global highest_cmd_sev highest_cmd_sev_msg highest_seen_cmd_sev set highest_cmd_sev ALL set highest_cmd_sev_msg "" set highest_seen_cmd_sev ALL } # Clear the recordings of the xorriso message sieve. # proc clear_sieve {} { send_silent_cmd "-msg_op clear_sieve -" } # Obtain a recorded item from the xorriso message sieve. # proc read_sieve {name} { send_silent_cmd "-msg_op read_sieve '$name'" de_sieve } # ------- Inquiring xorriso status ------- # Get more information about drive that was inquired by recent -toc_of. # proc obtain_drive_info {dev} { global result_list global sieve_ret global indev_medium_status outdev_medium_status global indev_profile outdev_profile set line "" if {$dev == "in"} { set indev_medium_status "missing" set indev_profile "" } else { set outdev_medium_status "missing" set outdev_profile "" } read_sieve "Media status :" if {$sieve_ret > 0} { set reply [lindex $result_list 0] foreach i {blank appendable closed} { if {[string first $i $reply] != -1} { set line "$i " if {$dev == "in"} { set indev_medium_status $i } else { set outdev_medium_status $i } break } } } read_sieve "Media current:" if {$sieve_ret > 0} { set profile [lindex $result_list 0] if {$profile == "is not recognizable"} { set profile "no recognizable medium" set line "$line$profile" return $line } else { set line "$line$profile, " if {$dev == "in"} { set indev_profile $profile } else { set outdev_profile $profile } } } read_sieve "Media summary:" if {$sieve_ret > 0} { set line "$line[lindex $result_list 0] sessions, " if {$dev == "in"} { set line "$line[lindex $result_list 2] used" } else { set line "$line[lindex $result_list 3] free" } } return $line } # Inquire whether changes of the ISO image are pending. # This is a precondition for writing the session. Vice versa pending changes # block a change of the input drive or the program end. # proc changes_are_pending {} { global result_count result_list send_silent_cmd "-changes_pending show_status" if {$result_count >= 1} { if {[lindex $result_list 0] == "-changes_pending no"} { return "0" } return "1" } return "" } # Inquire whether an ISO image model has been created inside xorriso. # This is a precondition for inserting files into the ISO tree model. # proc assert_iso_image {with_msg} { global highest_seen_cmd_sev scan_event_threshold set highest_seen_cmd_sev "" set set_mem $scan_event_threshold set scan_event_threshold "FATAL" send_silent_cmd "-lsd / --" set scan_event_threshold $set_mem if {[compare_sev $highest_seen_cmd_sev "FAILURE"] >= 0} { if {$with_msg == 1} { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : First you need to create or load an ISO image by selecting a drive or an image file" } return "0" } return "1" } # Obtain the list of possible event severity names, sorted in ascending order # proc inquire_severity_list {} { global xorriso_severity_list global result_list set disp_en_mem [set_display_msg 0] send_marked_cmd "-msg_op list_sev -" set_display_msg $disp_en_mem if {[lindex $result_list 0] != ""} { set xorriso_severity_list [split [lindex $result_list 0] " "] } } # Parse-by-xorriso handler function for proc inquire_dev # proc set_inquired_dev {} { global result_list eff_indev_adr eff_outdev_adr if {[llength $result_list] < 2} {return ""} set what [lindex $result_list 0] if {$what == "-dev" || $what == "-indev"} { set eff_indev_adr [lindex $result_list 1] } if {$what == "-dev" || $what == "-outdev"} { set eff_outdev_adr [lindex $result_list 1] } } # Inquire -indev and -outdev from xorriso and install in eff_indev_adr # and eff_outdev_adr. # (This could be done by -toc_of like in proc refresh_indev. But here # i demonstrate the use of command -status and parsing its result by # help of xorriso.) # proc inquire_dev {} { set disp_en_mem [set_display_msg 0] send_marked_cmd "-status -dev" handle_result_list set_inquired_dev "''" "''" 2 0 set_display_msg $disp_en_mem update idletasks return "" } # Inquire -indev and -outdev from xorriso and install in indev_adr # and outdev_adr. # proc update_dev_var {} { global result_list eff_indev_adr eff_outdev_adr indev_adr outdev_adr inquire_dev set indev_adr $eff_indev_adr set outdev_adr $eff_outdev_adr } # Parse-by-xorriso handler function for proc isofs_ls # proc isofs_ls_handler {} { global result_list isofs_ls_result if {[lindex $result_list 0] == "total"} {return ""} lappend isofs_ls_result \ "[string range [lindex $result_list 0] 0 0] [lindex $result_list 8]" } # Produce a list of all files in a directory of the ISO model # proc isofs_ls {dir} { global isofs_ls_result set isofs_ls_result "" set disp_en_mem [set_display_msg 0] send_marked_cmd "-lsl [make_text_shellsafe $dir]" handle_result_list isofs_ls_handler "''" "''" 0 0 set_display_msg $disp_en_mem return $isofs_ls_result } # Tells the file type of an absolute path in the ISO model. # Indicator characters like with ls -l. Empty text means non existing file. # proc isofs_filetype {path} { global result_list result_count scan_event_threshold set scan_event_mem $scan_event_threshold set scan_event_threshold "SORRY" send_silent_cmd "-lsdl [make_text_shellsafe $path]" set scan_event_threshold $scan_event_mem if {$result_count < 1} {return ""} return [string range [lindex $result_list 0] 0 0] } # Inspection of hard disk is done via xorriso. # The xorriso commands have the advantage to be always available and to # need no unescaping. On the other hand, shell and tcl lstat would be # faster with large directories. # Parse-by-xorriso handler function for proc localfs_ls # proc localfs_ls_handler {} { global result_list localfs_ls_result if {[lindex $result_list 0] == "total"} {return ""} lappend localfs_ls_result \ "[string range [lindex $result_list 0] 0 0] [lindex $result_list 8]" } # Return the list of files of a hard disk filesystem directory # proc localfs_ls {dir} { global localfs_ls_result set localfs_ls_result "" if {[localfs_filetype $dir] != "d"} {return ""} set disp_en_mem [set_display_msg 0] send_marked_cmd "-lslx [make_text_shellsafe $dir]" handle_result_list localfs_ls_handler "''" "''" 0 0 set_display_msg $disp_en_mem return $localfs_ls_result } # Tells the file type of an absolute path in the ISO model. # Indicator characters like with ls -l. Empty text means non existing file. # proc localfs_filetype {path} { global result_list result_count scan_event_threshold set scan_event_mem $scan_event_threshold set scan_event_threshold "SORRY" send_silent_cmd "-lsdlx [make_text_shellsafe $path]" set scan_event_threshold $scan_event_mem if {[llength $result_list] < 1} {return ""} return [string range [lindex $result_list 0] 0 0] } # Verify that the connected process runs a xorriso program that is modern # enough. This is done before sending xorriso the setup commands. # proc check_xorriso_version {} { global sieve_ret result_list pre_msglist xorriso_version min_xorriso_version global use_command_move use_command_hardlinks_on global reply_conn set version "0.0.0 (= unknown)" set disp_en_mem [set_display_msg 0] # In order to see the pre-frontend messages of xorriso # set an individual -mark and use send_async_cmd set mark_text "xorriso-tcltk-version-check-[clock seconds]" send_async_cmd "-mark [make_text_shellsafe $mark_text]" set_display_msg $disp_en_mem wait_for_msg $mark_text "M" set_display_msg 0 send_async_cmd "-version" set xorriso_version [wait_for_msg "xorriso version : " "R"] if {$xorriso_version < $min_xorriso_version} { puts stderr "xorriso-tcltk: xorriso-$xorriso_version is too old." puts stderr "xorriso-tcltk: Need at least version $min_xorriso_version" window_ack \ "xorriso-$xorriso_version is too old. Need at least version $min_xorriso_version" \ "red" "embedded" central_exit 2 } if {$xorriso_version < "1.2.8"} { set use_command_move 0 } if {$xorriso_version < "1.3.0"} { set use_command_hardlinks_on 0 } set_display_msg $disp_en_mem } # Commands which bring the connected xorriso process into the state that # is expected by this frontend. # proc setup_xorriso {} { global osirrox_allowed set cmd "" # Invalidate possible -mark 1 set cmd "$cmd -mark 0" # Make replies digestible for await_all_replies set cmd "$cmd -pkt_output on" # Report version early set cmd "$cmd -version" # This frontend relies heavily on the message sieve set cmd "$cmd -msg_op start_sieve -" # -reassure questions from xorriso would not be properly handled by # this frontend set cmd "$cmd -reassure off" set cmd "$cmd -abort_on NEVER" set cmd "$cmd -return_with ABORT 32" set cmd "$cmd -report_about UPDATE" set cmd "$cmd -iso_rr_pattern off" set cmd "$cmd -disk_pattern off" if {$osirrox_allowed == 0} { set cmd "$cmd -osirrox banned" } set cmd "$cmd [xorriso_loggable_init_cmds]" send_marked_cmd $cmd inquire_severity_list } # Commands which should also be at the start of a log script # proc xorriso_loggable_init_cmds {} { global use_command_hardlinks_on set cmd "-for_backup" # Before xorriso-1.3.0 there is a performance problem with -hardlinks "on" # and image manipulations before xorriso-1.3.0. if {$use_command_hardlinks_on == 0} { set cmd "$cmd -hardlinks off" } else { set cmd "$cmd -hardlinks on" } set cmd "$cmd -backslash_codes on" set cmd "$cmd -follow mount:limit=100" return $cmd } proc effectuate_permission_policy {} { global permission_policy if {$permission_policy == "readable"} { send_loggable_cmd \ "-find / -exec chmod a+r -- -find / -type d -exec chmod a+x --" } if {$permission_policy == "readonly"} { send_loggable_cmd \ "-find / -exec chmod a=r -- -find / -type d -exec chmod a+x --" } if {$permission_policy == "mkisofs_r"} { send_loggable_cmd \ "-find / -exec mkisofs_r" } } # ------ Parsing by help of xorriso ------ # Parsing by xorriso takes from the frontend the burden to understand # and implement the quoting rules of xorriso input and output. # Lines which are supposed to consist of several words get sent to # xorriso command -msg_op. The result lines of this command encode # the words unambiguously in one or more text lines. # This is supposed to be safe for even the weirdest file names. # Only NUL characters cannot be part of names. # Start a bulk parser job by which xorriso shall split the output of e.g. -lsl # into single words from which this frontend can pick information. # proc start_bulkparse {prefix separators max_words flag num_lines} { global bulk_parse_prefix bulk_parse_separators global bulk_parse_max_words bulk_parse_flag bulk_parse_num_texts if {$num_lines <= 0} {return ""} set bulk_parse_prefix $prefix set bulk_parse_separators $separators set bulk_parse_max_words $max_words set bulk_parse_flag $flag set bulk_parse_num_texts $num_lines set cmd "-msg_op parse_bulk \"$prefix $separators $max_words $flag $num_lines\"" send_async_cmd $cmd # Do not wait for mark } # Submit a new input line to the xorriso bulk parser job. # proc submit_bulkparse {text} { global cmd_conn reply_conn global result_list result_count global bulk_parse_prefix bulk_parse_separators global bulk_parse_max_words bulk_parse_flag set disp_en_mem [set_display_msg 0] set num_lines [expr [count_newlines $text] + 1] debug_log_puts ">>>>> $num_lines" puts $cmd_conn $num_lines debug_log_puts ">>>>> $text" puts $cmd_conn $text flush $cmd_conn set_display_msg $disp_en_mem } # Read the expected number of bulk parsing replies into the result buffer # and call handler_proc to inspect them. # Each input line of the parser yields one reply buffer full of parsed words. # proc read_bulkparse {handler_proc num_texts} { set disp_en_mem [set_display_msg 0] for {set i 0} {$i < $num_texts} {incr i} { clear_reply_lists read_parse_reply $handler_proc } set_display_msg $disp_en_mem } # Read and decode the xorriso parser reply for one input line. # proc read_parse_reply {} { global reply_conn global result_list result_count set sieve_result_count 0 set payload "" set num_lines 0 set acc "" set loop_limit 2 while {$result_count < $loop_limit} { set ret [gets $reply_conn line] if {$ret < 0} { return ""} debug_log_puts $line de_pkt_line $line set line [lindex $result_list [expr $result_count-1]] if {$result_count == 1} { set parse_ret $line } else { if {$result_count == 2} { set num_replies $line # The minimum number of lines set loop_limit [expr "$num_replies * 2 + 2"] } else { if {$num_lines <= 0} { set num_lines $line if {$num_lines > 1} { # Need to read extra lines incr loop_limit [expr $num_lines-1] } set acc "" } else { incr num_lines -1 if {$acc != ""} { set acc "$acc\n$line" } else { set acc $line } if {$num_lines <= 0} { lappend payload $acc incr sieve_result_count } } }} } set result_list $payload set result_count $sieve_result_count } # Let xorriso parse the lines in the result buffer and call handler_proc # with the parse result of each line. # This is used to split the result lines of -lsl into words from which # handler proc isolist_parse_handler picks the info which it displays # in .stbox isolist . # Note that all parameters must be xorriso words. E.g. empty prefix or # separator have to be submitted as tcl string "''" rather than "". # proc handle_result_list {handler_proc \ prefix separators max_words flag } { global result_list global bulk_parse_max_chunk set raw_list $result_list set raw_line_count [expr [llength $raw_list]] if {$raw_line_count > $bulk_parse_max_chunk} { set chunk_size $bulk_parse_max_chunk } else { set chunk_size $raw_line_count } start_bulkparse $prefix $separators $max_words $flag $chunk_size set submit_count 0 set submit_in_chunk_count 0 foreach i $raw_list { submit_bulkparse $i incr submit_count incr submit_in_chunk_count if {$submit_in_chunk_count == $chunk_size} { read_bulkparse $handler_proc $chunk_size set todo [expr "$raw_line_count - $submit_count"] if {$todo <= 0} { break } if {$todo > $bulk_parse_max_chunk} { set chunk_size $bulk_parse_max_chunk } else { set chunk_size $todo } start_bulkparse $prefix $separators $max_words $flag \ $chunk_size set submit_in_chunk_count 0 } } display_busy 0 } # ------------------------------- the GUI ---------------------------- # ------ State variables ------ # Whether to display messages in .msglist set display_msg_enabled 1 # Whether a device list is already displayed set devices_scanned 0 # Currently displayed ISO directory set isodir_adr "" set isodir_is_pwd 0 # The plain names and types matching listbox .isolist set isolist_names "" set isolist_types "" # The name which to select after isodir_return set isodir_return_name "" # The address where to move selected ISO files set isomanip_move_target "" # Memorized isolist selection set memorized_isolist_selection "" # Image file address for .burn_write_image set burn_write_image_adr "" # Whether to close medium after writing set burn_write_close 0 # Whether to force CD TAO, DVD-R Inremental, DVD+R/BD-R open ended track set burn_write_tao 0 # Whether to engage Defect Management on formatted BD media set burn_write_defect_mgt 0 # Answer of yes/no window set answer_of_yesno "" # Semi-persistent answers of yes/no window set yesno_to_all 0 # The hard disk filesystem address to be mapped into isodir_adr set insert_from_adr "" # Whether to insert with leafname of insert_from_adr underneath isodir_adr # (else: -map $insert_from_adr $isodir_adr) set insert_underneath 1 # Whether to insert at or under the selected .isolist item # rather than isodir_adr set insert_at_selected 0 # The hard disk filesystem address to which to extract from isodir_adr set extract_to_adr "" # Whether to insert with leafname of insert_from_adr underneath isodir_adr # (else: -map $insert_from_adr $isodir_adr) set extract_underneath 1 # Whether to insert at or under the selected .isolist item set extract_from_selected 1 # Whether to temporarily enforce rwx permissions for target directories on disk set extract_auto_chmod 0 # Whether the display label .busy_text is already usable set busy_text_exists 0 # Whether to demand a click before focus goes to entry or listbox set click_to_focus 1 # Whether .ack_window , .yesno_window , .help_window, .main_help_window # are already displayed. set ack_window_is_active 0 set yesno_window_is_active 0 set help_window_is_active 0 set main_help_window_is_active 0 # Positions of above windows when they were last closed set yesno_window_geometry "" set ack_window_geometry "" set help_window_geometry "" set main_help_window_geometry "" # Whether the help window already has a scroll bar set help_window_has_scroll 0 # Whether there is the BWidget package available: 0=unknown, 1=yes, -1=banned # set have_bwidget 0 set bwidget_version "" # Whether the .browse_disk_window is already displayed set browse_disk_window_is_active 0 set browse_disk_window_var "" # Position of window when it was last closed set browse_disk_window_geometry "" # Whether the window is grabbed set browse_disk_window_is_grabbed 0 # Whether the .browse_iso_window is already displayed set browse_iso_window_is_active 0 set browse_iso_window_var "" # Position of window when it was last closed set browse_iso_window_geometry "" # Whether the window is grabbed set browse_iso_window_is_grabbed 0 # Whether to modify the ISO file permissions before writing the ISO session # Policies: as_is , readable , readonly , mkisofs_r set permission_policy "as_is" # ------ GUI callback procedures ---- # Called when the Return key is hit in commandline. # proc cmdline_return {} { global cmdline cmdline_clear global .cmdline .cmdline_text .cmdline_entry global highest_cmd_sev global highest_cmd_sev_msg reset_highest_cmd_sev set_display_msg 1 send_loggable_cmd $cmdline set cmdline "" # To force display of GUI changes now and not some time later update idletasks } # Called when the input drive address shall be brought into effect with # xorriso. # proc indev_return {} { global indev_adr global .indev_entry global .outdev_entry if {[assert_no_changes] == 0} { update_dev_var return "0" } reset_highest_cmd_sev send_loggable_cmd "-indev [make_text_shellsafe $indev_adr]" set indev_mem_adr $indev_adr .indev_entry icursor 0 refresh_indev return "1" } # Called when the "Eject" button for the input drive is hit. # proc eject_indev {} { if {[assert_no_changes] == 0} {return ""} reset_highest_cmd_sev send_loggable_cmd "-eject indev" refresh_outdev refresh_indev } # Called when the output drive address shall be brought into effect with # xorriso. # proc outdev_return {} { global outdev_adr indev_adr global .outdev_entry reset_highest_cmd_sev send_loggable_cmd "-outdev [make_text_shellsafe $outdev_adr]" set outdev_mem_adr $outdev_adr .outdev_entry icursor 0 refresh_outdev return "1" } # Called when the "Eject" button for the output drive is hit. # proc eject_outdev {} { global outdev_adr indev_adr if {$outdev_adr == $indev_adr} { if {[assert_no_changes] == 0} {return ""} } reset_highest_cmd_sev send_loggable_cmd "-eject outdev" refresh_outdev refresh_indev } # Called when both drive addresses shall be brought into effect with xorriso. # proc dev_return {} { global outdev_adr indev_adr global .outdev_entry .indev_entry if {$outdev_adr != $indev_adr} { if {[indev_return] == 0} {return "0"} outdev_return } else { if {[assert_no_changes] == 0} { update_dev_var return "0" } reset_highest_cmd_sev send_loggable_cmd "-dev [make_text_shellsafe $outdev_adr]" .outdev_entry icursor 0 refresh_outdev .indev_entry icursor 0 refresh_indev } } # Obtain and display the input drive status. # Called after the input drive address may have changed. # proc refresh_indev {} { global result_list global indev_adr global sieve_ret global .indev_summary .indev_summary configure -text "" set indev_adr "" update idletasks set disp_en_mem [set_display_msg 0] clear_sieve send_marked_cmd "-toc_of in:short" read_sieve "Drive current:" set_display_msg $disp_en_mem if {$sieve_ret > 0} { set cmd [lindex $result_list 0] if {$cmd == "-indev" || $cmd == "-dev"} { set indev_adr [lindex $result_list 1] } set line [obtain_drive_info in] .indev_summary configure -text $line } .avail_label configure -text "" update idletasks isodir_return "refresh_indev" } # Obtain and display the output drive status. # Called after the output drive address may have changed. # proc refresh_outdev {} { global result_list global outdev_adr global sieve_ret .outdev_summary configure -text "" set outdev_adr "" update idletasks set disp_en_mem [set_display_msg 0] clear_sieve send_marked_cmd "-toc_of out:short" read_sieve "Drive current:" set_display_msg $disp_en_mem if {$sieve_ret > 0} { set cmd [lindex $result_list 0] if {$cmd == "-outdev" || $cmd == "-dev"} { set outdev_adr [lindex $result_list 1] } set line [obtain_drive_info out] .outdev_summary configure -text $line } .avail_label configure -text "" update idletasks } # Scan the system for optical drives with rw permission # Called when the "Scan for drives button" is hit. # proc scan_for_drives {} { global .drivelist .drive_drop_both .drive_scan global sieve_ret result_list devlist devices_scanned indev_adr outdev_adr if {[assert_no_changes] == 0} {return ""} if {$indev_adr != "" || $outdev_adr != ""} { if {[window_yesno \ "Really give up acquired drives for scanning a new drive list ?"] \ != 1} { return "" } } set max_idx [.drivelist index end] .drivelist delete 0 [expr $max_idx-1] set devlist "" reset_highest_cmd_sev clear_sieve send_loggable_cmd "-devices" set max_idx 0 while {1} { read_sieve "? -dev" if {$sieve_ret > 0} { .drivelist insert end "[lindex $result_list 0] : [lindex $result_list 2] [lindex $result_list 3]" lappend devlist [lindex $result_list 0] } else { break } } while {1} { read_sieve "?? -dev" if {$sieve_ret > 0} { .drivelist insert end "[lindex $result_list 0] : [lindex $result_list 2] [lindex $result_list 3]" lappend devlist [lindex $result_list 0] } else { break } } set devices_scanned 1 reset_to_normal_background .drive_scan # Command -devices drops all acquired drives refresh_outdev refresh_indev } # Refresh the display after some xorriso may have changed the status # Called by the "Refresh disp" button and others. # proc refresh_state {} { refresh_indev refresh_outdev } # Reset the recorded Worst problem message. # Called when the "Clear" button is hit. # proc clear_total_errmsg {} { global highest_total_sev global highest_total_sev_msg set highest_total_sev ALL set highest_total_sev_msg "" update idletasks } # Called when the "Pick input drive button" is hit. # proc pick_indev {} { pick_drive indev } # Called when the "Pick output drive button" is hit. # proc pick_outdev {} { pick_drive outdev } # Called when the "Pick drive for both roles" button is hit. # or when an item in the scanned drive list is double-clicked. # proc pick_dev {} { pick_drive dev } # Perform the actual work of pick_dev, pick_indev, and pick_outdev # proc pick_drive {role} { global .drivelist global devlist global highest_cmd_sev_msg outdev_adr indev_adr devices_scanned set selected [.drivelist curselection] if {[llength $selected] != 1} { set must_scan "" if {$devices_scanned == 0} { set must_scan " scan and"} xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : First you must$must_scan select a single drive" return "" } set drive_idx [lindex $selected 0] if {$role == "dev"} { set outdev_adr [lindex $devlist $drive_idx] set indev_adr [lindex $devlist $drive_idx] dev_return } if {$role == "outdev"} { set outdev_adr [lindex $devlist $drive_idx] outdev_return } if {$role == "indev"} { set indev_adr [lindex $devlist $drive_idx] indev_return } .drivelist selection clear 0 end } # Called when the "Give up drives" button is hit. # proc give_up_dev {} { global outdev_adr indev_adr if {[assert_no_changes] == 0} {return ""} set outdev_adr "" outdev_return set indev_adr "" indev_return } # Obtain and display the content of the current ISO directory. # Called when the Return key is hit in the .isodir_entry text field # and by many others which change variable isodir_adr or the # content of the directory in xorriso's tree model. # proc isodir_return {caller} { global isodir_adr result_list isolist_names isolist_types isodir_return_name global isodir_is_pwd highest_cmd_sev highest_cmd_sev_msg global indev_adr outdev_adr eff_indev_adr global .isolist global bulk_parse_max_chunk set chunk_size 0 set max_idx [.isolist index end] .isolist delete 0 [expr "$max_idx-1"] update idletasks set isolist_names "" set isolist_types "" inquire_dev if {$eff_indev_adr == "" && [changes_are_pending] == "0"} {return ""} normalize_isodir_adr set file_type [isofs_filetype $isodir_adr] if {$file_type != "d" && $file_type != ""} { .isolist insert end "@@@ exists but is not a directory @@@" set isodir_is_pwd 0 return "" } set disp_en_mem [set_display_msg 0] set highest_cmd_sev_mem $highest_cmd_sev set highest_cmd_sev_msg_mem $highest_cmd_sev_msg reset_highest_cmd_sev send_loggable_cmd "-cd [make_text_shellsafe $isodir_adr]" if {[compare_sev $highest_cmd_sev "WARNING"] < 0} { send_marked_cmd "-lsl --" set isodir_is_pwd 1 } else { send_marked_cmd "-lsl [make_text_shellsafe $isodir_adr] --" set isodir_is_pwd 0 } handle_result_list isolist_parse_handler "''" "''" 0 0 set_display_msg $disp_en_mem set highest_cmd_sev $highest_cmd_sev_mem set highest_cmd_sev_msg $highest_cmd_sev_msg_mem if {$isodir_return_name != ""} { set idx [lsearch -exact $isolist_names $isodir_return_name] if {$idx != -1} { .isolist see $idx .isolist selection set $idx } set isodir_return_name "" } update idletasks } # The handler procedure that is submitted to proc handle_result_list # and will be called for every parsed line. # It records file names and types in separate lists and displays them # in the .isolist box. # proc isolist_parse_handler {} { global result_list isolist_names isolist_types global .isolist if {[lindex $result_list 0] == "total"} {return ""} set name [lindex $result_list 8] set ftype [string range [lindex $result_list 0] 0 0] lappend isolist_names $name lappend isolist_types $ftype .isolist insert end "$ftype $name" } # Make current the ISO directory that was selected from the .isolist box. # Called when an item in the .isolist box is double-clicked. # proc pick_isodir {} { global isolist_names isolist_types isodir_adr isodir_return_name global .isolist set selected [.isolist curselection] if {[llength $selected] != 1} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : First you must select a single directory" return "" } set idx [lindex $selected 0] if {[lindex $isolist_types $idx] != "d"} { return "" } if {$isodir_adr == "/"} { set isodir_adr "" } set isodir_adr "$isodir_adr/[lindex $isolist_names $idx]" set isodir_return_name "" isodir_return "pick_isodir" } # Make current the parent directory of the current ISO directory. # Called when the "Up" button is hit. # proc isodir_up {} { global isodir_adr isodir_return_name set isodir_return_name "" set idx [string last "/" $isodir_adr] set l [string length $isodir_adr] if {$idx == -1} { set isodir_return_name $isodir_adr set isodir_adr "/" } else { if {$idx > 0} { if {$idx < [expr $l - 1]} { set isodir_return_name \ [string range $isodir_adr [expr $idx+1] end] } set isodir_adr [string range $isodir_adr 0 [expr $idx - 1]] } else { if {$l > 1} { set isodir_return_name [string range $isodir_adr 1 end] } set isodir_adr "/" } } isodir_return "isodir_up" } # Rename or move the files which are selected in the .isolist box. # The target is defined by the .isomanip_move_target text field. # Called when the "Move to:" button is hit. # proc isomanip_mv {} { global .isolist global isomanip_move_target isolist_names isodir_is_pwd isodir_adr global isodir_return_name use_command_move if {$isomanip_move_target == ""} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : First you must enter a target address" return "" } set selected [.isolist curselection] set num_selected [llength $selected] if {$num_selected < 1} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : First you must select one or more ISO files" return "" } set target $isomanip_move_target if {$isodir_is_pwd == 0 && [string range $target 0 0] != "/"} { set target [combine_dir_and_name $isodir_adr $target] } set target_ftype [isofs_filetype $target] # If more than one selected : target must be directory if {$num_selected > 1} { if {$target_ftype != "d" && $target_ftype != ""} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : If multiple files are selected then the target must be a directory" return "" } if {$target_ftype == ""} { set isomanip_move_target_mem $isomanip_move_target set isomanip_move_target $target isomanip_mkdir set isomanip_move_target $isomanip_move_target_mem } } enforce_overwrite_settings "isofs" reset_highest_cmd_sev reset_yesno_to_all set multi_source 0 if {[llength $selected] != 1} {set multi_source 1} foreach i $selected { set name [lindex $isolist_names $i] if {$isodir_is_pwd == 0} { set name [combine_dir_and_name $isodir_adr $name] } set name_ftype [isofs_filetype $name] # Ask for confirmation if overwriting is about to happen if {$target_ftype == "d" && $use_command_move == 0} { set eff_target [combine_dir_and_name $target $name] set eff_target_ftype [isofs_filetype $eff_target] } else { set eff_target $target set eff_target_ftype $target_ftype } if {[handle_overwriting "isofs" $eff_target $eff_target_ftype \ "isofs" $name $name_ftype $multi_source \ "" "" "replace"] == "0"} { if {$multi_source == 0} { return "" } continue } if {$use_command_move == 0} { send_loggable_cmd \ "-mv [make_text_shellsafe $name] [make_text_shellsafe $target] --" } else { send_loggable_cmd \ "-move [make_text_shellsafe $name] [make_text_shellsafe $target] --" } } if {[llength $selected] == 1} { set isodir_return_name [path_touches_isodir $target] } browse_iso_refresh isodir_return "isomanip_mv" } # Create an empty ISO directory with address given by variable # isomanip_move_target. # Called when the "Make dir" button is hit or by other functions. # proc isomanip_mkdir {} { global isomanip_move_target isodir_adr isodir_return_name if {$isomanip_move_target == ""} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : First you must enter a target address" return "" } if {[string range $isomanip_move_target 0 0] == "/"} { set abs_adr $isomanip_move_target } else { set abs_adr [combine_dir_and_name $isodir_adr $isomanip_move_target] } reset_highest_cmd_sev send_loggable_cmd "-mkdir [make_text_shellsafe $abs_adr] --" # Refresh only if new dir in isodir_adr # or if a parent directory of new dir is created in isodir_adr set touch_name [path_touches_isodir $abs_adr] if {$touch_name != ""} { if {[llength [.isolist curselection]] != 0} { memorize_isolist_selection set selection_memorized 1 } else { set isodir_return_name $touch_name set selection_memorized 0 } isodir_return "isomanip_mkdir" if {$selection_memorized != 0} { restore_isolist_selection } } browse_iso_refresh } # Remove a file or directory tree from the ISO image. # Called when the "Delete" button is hit. # proc isomanip_rm_r {} { global .isolist global isomanip_move_target isolist_names isodir_is_pwd isodir_adr set selected [.isolist curselection] if {[llength $selected] < 1} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : First you must select one or more ISO files" return "" } if {[window_yesno "Really delete the selected files from ISO image ?"] \ != 1} { return "" } reset_highest_cmd_sev foreach i $selected { set name [lindex $isolist_names $i] if {$isodir_is_pwd == 0} { set name [combine_dir_and_name $isodir_adr $name] } send_loggable_cmd "-rm_r [make_text_shellsafe $name] --" } browse_iso_refresh isodir_return "isomanip_rm_r" } # Perform a blanking run on the output drive. # Called when the "Blank" button is hit. # proc burn_blank {} { global outdev_profile eff_outdev_adr eff_indev_adr refresh_outdev if {[assert_outdev blanking] <= 0} {return ""} if {$eff_outdev_adr == $eff_indev_adr} { if {[assert_no_changes] <= 0} {return ""} } set victim "medium in" if {[string first "stdio" $outdev_profile] == 0} { set victim "image file" } if {[window_yesno \ "Really blank the $victim [make_text_shellsafe $eff_outdev_adr] ?"] \ != 1} { return "" } reset_highest_cmd_sev send_loggable_cmd "-blank as_needed" refresh_indev refresh_outdev } # Perform a formatting run on the output drive. # Called when the "Format" button is hit. # proc burn_format {} { global outdev_profile eff_outdev_adr eff_indev_adr refresh_outdev if {[assert_outdev formatting] <= 0} {return ""} if {$eff_outdev_adr == $eff_indev_adr} { if {[assert_no_changes] <= 0} {return ""} } if {[string first "stdio" $outdev_profile] == 0} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : Image files cannot be formatted" return "" } if {[window_yesno "Really format the medium in $eff_outdev_adr ?"] \ != 1} { return "" } reset_highest_cmd_sev send_loggable_cmd "-format as_needed" refresh_indev refresh_outdev } # Write pending changes in the xorriso ISO model as session to the output # drive. This will be an add-on session if the drive is output and input drive # and if its medium is not blank. # Else it will be a new independent ISO image. # proc burn_commit {} { global outdev_adr result_list result_count outdev_medium_status global burn_write_close burn_write_tao burn_write_defect_mgt global indev_adr outdev_adr permission_policy if {[assert_outdev "writing an ISO session"] <= 0} {return ""} if {$outdev_adr == $indev_adr} { if {$outdev_medium_status != "blank" && \ $outdev_medium_status != "appendable"} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : Medium in output drive is neither blank nor appendable" return "" } } else { if {$outdev_medium_status != "blank"} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : Medium in output drive is not blank" return "" } } if {[changes_are_pending] == "0"} { window_ack "No changes are pending. Will not write ISO session." \ "grey" "toplevel" return "" } if {$outdev_adr == $indev_adr} { if {[window_yesno "Really write ISO changes as session to $outdev_adr ?"] \ != 1} { return "" } } else { if {[window_yesno "Really write a new ISO filesystem to $outdev_adr ?"] \ != 1} { return "" } } reset_highest_cmd_sev effectuate_permission_policy set cmd "-close" if {$burn_write_close == 1} { set cmd "$cmd on" } else { set cmd "$cmd off" } set cmd "$cmd -write_type" if {$burn_write_tao == 1} { set cmd "$cmd tao" } else { set cmd "$cmd auto" } set cmd "$cmd -stream_recording" if {$burn_write_defect_mgt == 1} { set cmd "$cmd off" } else { set cmd "$cmd data" } set cmd "$cmd -commit" send_loggable_cmd $cmd refresh_indev refresh_outdev } # Verify the MD5 checksums of the data files in the tree underneath the # current ISO directory. # Called when the "Verify" in the "ISO directory:" line is hit. # proc isodir_verify {} { global isodir_adr reset_highest_cmd_sev send_loggable_cmd "-check_md5_r sorry [make_text_shellsafe $isodir_adr] --" # >>> select mismatching files or directories with mismatching files } # Verify the MD5 checksums of the data files orch are selected or which # sit in the trees underneath the selected items in the isolist box. # Called when the "Verify" in the "Selection:" line is hit. # proc isomanip_verify {} { global .isolist global isomanip_move_target isolist_names isodir_is_pwd isodir_adr set selected [.isolist curselection] if {[llength $selected] < 1} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : First you must select one or more ISO files" return "" } reset_highest_cmd_sev foreach i $selected { set name [combine_dir_and_name $isodir_adr \ [lindex $isolist_names $i]] send_loggable_cmd "-check_md5_r sorry [make_text_shellsafe $name] --" } # >>> select mismatching files or directories with mismatching files } # Slow down the spinning of the acquired optical drives. # Called when button "Calm drives" is hit. # proc calm_drives {} { reset_highest_cmd_sev send_loggable_cmd "-calm_drive all" } # Burn a data file from disk as session to the output drive. # Called when the "Burn image file:" button is hit. # proc burn_write_image {} { global burn_write_image_adr burn_write_close outdev_adr outdev_medium_status global outdev_profile burn_write_tao burn_write_defect_mgt indev_adr update_dev_var if {$indev_adr != ""} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : You may not have an input drive open when writing an image file" return "" } if {[assert_outdev "writing an image file"] <= 0} {return ""} if {$burn_write_image_adr == ""} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : You have to set an image file address in the hard disk filesystem first" return "" } if {$outdev_medium_status != "blank"} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : You must have a blank medium in the output drive for burning an image data file" return "" } if {[file readable $burn_write_image_adr] == 0 || \ [file isfile $burn_write_image_adr] == 0 || [file exists $burn_write_image_adr] == 0} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : Image file '$burn_write_image_adr' is not a readable data file" return "" } if {[window_yesno "Really write '$burn_write_image_adr' as image to $outdev_adr ?"] \ != 1} { return "" } set cmd "-as cdrecord -v" if {[regexp "^CD" $outdev_profile] == 1 && \ ( $outdev_medium_status == "appendable" || $burn_write_tao == 1 )} { set cmd "$cmd padsize=150s" } set cmd "$cmd dev=[make_text_shellsafe $outdev_adr]" set cmd "$cmd [make_text_shellsafe $burn_write_image_adr]" if {$burn_write_tao == 1} { set cmd "$cmd -tao" } if {$burn_write_close != 1} { set cmd "$cmd -multi" } if {$burn_write_defect_mgt == 1} { set cmd "$cmd stream_recording=off" } else { set cmd "$cmd stream_recording=32s" } reset_highest_cmd_sev send_loggable_cmd $cmd refresh_state } # Discard all image modifications and reload ISO image model from input drive. # Called when the "Rollback" button is hit. # proc iso_rollback {} { if {[window_yesno \ "Really discard all pending changes and reload from input drive ?"] \ != 1} { return "" } reset_highest_cmd_sev send_loggable_cmd "-rollback" .avail_label configure -text "" isodir_return "iso_rollback" } # Inquire an accurate prediction of free space after writing a session with # the pending changes of the ISO image. # Called when button "Refresh avail:" is hit. # proc refresh_avail {} { global result_list highest_cmd_sev global sieve_ret if {[assert_outdev "refreshing available space prediction"] <= 0} {return ""} set line "n.a." reset_highest_cmd_sev clear_sieve send_loggable_cmd "-tell_media_space" if {[compare_sev $highest_cmd_sev "FAILURE"] < 0} { set ac "" read_sieve "After commit :" if {$sieve_ret > 0} { set ac [lindex $result_list 0] set ac [string range $ac 0 [expr [string length $ac] - 2]] set line "[format "%7dm" [expr "$ac / 512"]]" } } .avail_label configure -text $line } # Warn and prompt the user for confirmation if there is the risk to overwrite # existing files on hard disk or in the ISO image model. # Called from several procedures which cause side effects on directory trees. # proc handle_overwriting {target_fs target target_ftype source_fs source source_ftype multi_source selected_adr selected_ftype dir_action} { global overwrite_iso_files overwrite_iso_dirs overwrite_disk_files if {$target_fs == "localfs"} { set to_fs "hard disk" set overwrite_fs "disk" set overwrite_dirs 0 set overwrite_files $overwrite_disk_files } else { set to_fs "ISO" set overwrite_fs "ISO" set overwrite_dirs $overwrite_iso_dirs set overwrite_files $overwrite_iso_files } if {$source_fs == "localfs"} { set from_fs "hard disk" } else { set from_fs "ISO" } if {$multi_source == 1} { set what_window window_yesno_ever } else { set what_window window_yesno } # >>> Nicer would be: # >>> Check if any file will get overwritten. Not only the direct target. # >>> Then silently allow directories to be merged if {$target_ftype != ""} { if {$target_ftype == "d"} { if {$source_ftype == "d"} { if {$dir_action == "replace"} { if {$overwrite_iso_dirs != 1} { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : You would have to enable \"Overwrite $overwrite_fs dirs\" for\n[make_text_shellsafe $target]" return "0" } if {[$what_window \ "Really replace existing $to_fs directory\n\n[make_text_shellsafe $target]\n\nby $from_fs directory\n[make_text_shellsafe $source]\n?"] \ != 1} { return "0" } return "1" } if {[$what_window \ "Really merge existing $to_fs directory\n\n[make_text_shellsafe $target]\n\nwith $from_fs directory\n[make_text_shellsafe $source]\n?"] \ != 1} { return "0" } } else { if {$target_fs != "isofs"} { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : Will not replace directory on hard disk by file of other type\n[make_text_shellsafe $target]" return "0" } if {$overwrite_dirs == 1} { if {[$what_window \ "Really overwrite $to_fs directory\n\n[make_text_shellsafe $target]\n\nby $from_fs file\n[make_text_shellsafe $source]\n?"] \ != 1} { return "0" } } else { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : You would have to enable \"Overwrite $overwrite_fs dirs\" for\n[make_text_shellsafe $target]" return "0" } } } else { if {$overwrite_files == 1} { if {[$what_window \ "Really overwrite $to_fs file\n\n[make_text_shellsafe $target]\n\nby $from_fs file\n[make_text_shellsafe $source]\n?"] != 1} { return "0" } } else { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : You would have to enable \"Overwrite $overwrite_fs files\" for\n[make_text_shellsafe $target]" return "0" } } } if {$selected_adr != $target && $selected_adr != "" && \ $selected_ftype != "d" && $selected_ftype != ""} { if {[$what_window \ "Really replace existing $to_fs file\n\n[make_text_shellsafe $target]\n\nby $from_fs directory\n[make_text_shellsafe $source]\n?"] != 1} { return "0" } } return "1" } # Insert a file or directory tree into the ISO model tree and schedule it # for being copied when "Write ISO session" is hit. # Called when button "Insert from disk:" is hit. # proc insert_from {} { global insert_from_adr isodir_adr isolist_names isodir_return_name global insert_at_selected insert_underneath if {[assert_iso_image 1] == 0} {return ""} if {$insert_from_adr == ""} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : You have to set a source address in the hard disk filesystem first" return "" } set selected_ftype "" set selected_adr "" if {$insert_at_selected == 1} { set selected [.isolist curselection] if {[llength $selected] != 1} { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : You must select exactly one ISO file as insertion target" return "" } set target "[lindex $isolist_names [lindex $selected 0]]" set selected_ftype [isofs_filetype $target] set selected_adr $target } else { set target $isodir_adr } set source_ftype [localfs_filetype $insert_from_adr] set name [file tail $insert_from_adr] if {$insert_underneath == 1 || $source_ftype == "d"} { set target [combine_dir_and_name $target $name] } set target_ftype [isofs_filetype $target] reset_yesno_to_all if {[handle_overwriting "isofs" $target $target_ftype \ "localfs" $insert_from_adr $source_ftype 0 \ $selected_adr $selected_ftype "merge"] == "0"} { return "" } set preserve_selection 0 if {$insert_underneath + $insert_at_selected == 1} { set isodir_return_name $name } else { set preserve_selection 1 } reset_highest_cmd_sev enforce_overwrite_settings "isofs" send_loggable_cmd "-map [make_text_shellsafe $insert_from_adr] [make_text_shellsafe $target]" if {$preserve_selection == 1} { memorize_isolist_selection } isodir_return "insert_from" if {$preserve_selection == 1} { restore_isolist_selection } browse_iso_refresh } # Copy a file out of the ISO image model to the hard disk filesystem. # The meta data stem from the ISO model tree. The content data are usually # read from the input drive. # Called when button "Extract to disk:" is hit. # proc extract_to {} { global extract_to_adr extract_from_selected extract_underneath global extract_auto_chmod osirrox_allowed global isodir_adr isolist_names if {$osirrox_allowed != 1} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : Extraction from ISO to hard disk is already irrevocably banned." return "" } if {[assert_iso_image 1] == 0} {return ""} if {$extract_to_adr == ""} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : You have to set a target address in the hard disk filesystem first" return "" } set sources "" set selected_ftype "" set selected_adr "" if {$extract_from_selected == 1} { set selected [.isolist curselection] if {[llength $selected] < 1} { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : You must select at least one ISO file as extraction source" return "" } foreach i $selected { set path [combine_dir_and_name $isodir_adr \ [lindex $isolist_names $i]] lappend sources $path } } else { set sources [list $isodir_adr] } reset_highest_cmd_sev reset_yesno_to_all enforce_overwrite_settings "localfs" set disp_en_mem [set_display_msg 0] if {$extract_auto_chmod == 1} { send_loggable_cmd "-osirrox on:sort_lba_on:auto_chmod_on" } else { send_loggable_cmd "-osirrox on:sort_lba_off:auto_chmod_off" } set_display_msg $disp_en_mem set multi_source 0 if {[llength $sources] != 1} {set multi_source 1} foreach i $sources { if {$extract_underneath == 1} { set name [file tail $i] set target [combine_dir_and_name $extract_to_adr $name] } else { if {[llength $sources] != 1} { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : You must select exactly one ISO file as extraction source" return "" } set target $extract_to_adr } if {$i == ""} { set i "/" } set target_ftype [localfs_filetype $target] set from_is_dir 0 set source_ftype [isofs_filetype $i] if {[handle_overwriting "localfs" $target $target_ftype \ "isofs" $i $source_ftype $multi_source \ "" "" "merge"] == 0} { continue } send_loggable_cmd \ "-extract [make_text_shellsafe $i] [make_text_shellsafe $target]" } browse_tree_populate "localfs" } # Send the currently chosen -overwrite settings of the checkbuttons # "Overwrite ISO files", "Overwrite ISO dirs", "Overwrite hard disk files". # Called before operations which could overwrite files in ISO model # or in the hard disk filesystem. # I.e. any -overwrite command sent via the "Command:" text field will not # be able to override the checkbuttons. # proc enforce_overwrite_settings {which_fs} { global overwrite_iso_files overwrite_iso_dirs overwrite_disk_files if {$which_fs == "isofs"} { if {$overwrite_iso_files == 0} { set mode "off" } else { if {$overwrite_iso_dirs == 0} { set mode "nondir" } else { set mode "on" } } } else { if {$overwrite_disk_files == 1} { set mode "on" } else { set mode "off" } } set disp_en_mem [set_display_msg 0] send_loggable_cmd "-overwrite $mode" set_display_msg $disp_en_mem } # Send xorriso an appropriate end command and wait for the pipes to break. # Called when button "End" is hit. # proc end_xorriso {} { global expect_broken_pipes if {[window_yesno "Really end this program and its xorriso backend ?"] \ != 1} { return "" } if {[changes_are_pending] == 1} { if {[window_yesno \ "Changes of the ISO image are pending.\nReally discard them ?"] \ != 1} { return "" } set expect_broken_pipes "1" send_loggable_cmd "-rollback_end" } else { set expect_broken_pipes "1" send_loggable_cmd "-end" } central_exit 0 } # Check whether an output drive is acquired. Propose refusal if not. # Called by procedures which are about to use the output drive. # proc assert_outdev {purpose} { global eff_outdev_adr inquire_dev if {$eff_outdev_adr == ""} { xorriso_tcltk_errmsg \ "xorriso-tcltk : SORRY : You must choose an output drive before $purpose" return "0" } return "1" } # Check whether changes to the ISO model are pending. If so, propose refusal. # Called by procedures which are about to discard the ISO model. # proc assert_no_changes {} { if {[changes_are_pending] == 1} { window_ack "ISO image changes are pending. You have to do \"Write ISO session\" or \"Rollback\"." "grey" "toplevel" return "0" } return "1" } # Set the text of the "Permissions:" menubutton # Called by the radiobuttons in the menu. # proc show_permission_policy {} { global permission_policy set text $permission_policy if {$permission_policy == "as_is"} { set text "as is" } if {$permission_policy == "mkisofs_r"} { set text "mkisofs -r" } .perm_policy configure -text "Permissions: $text" } # Set the target address of command logging. # Called by the "Script/Log" menu. # proc set_log_script_address {} { browse_tree cmd_log_target "localfs" set w .browse_disk_window tkwait window $w effectuate_command_logging 0 } # Bring into effect the settings for command script logging. # Called by the "Accept" button or the Return key of the # "Set log script address" file browser. # proc effectuate_command_logging {close_window} { global cmd_log_target cmd_logging_mode browse_disk_window_is_active if {$close_window == 1 && $browse_disk_window_is_active == 1} { destroy_browse_disk .browse_disk_window } if {$close_window == 1 || $cmd_logging_mode > 0} { start_command_logging $cmd_log_target $cmd_logging_mode } } # Set the target address of communication pipe logging. # Called by the "Script/Log" menu. # proc set_debug_log_address {} { browse_tree debug_log_file "localfs" set w .browse_disk_window tkwait window $w effectuate_debug_logging 0 } # Bring into effect the settings for communication pipe logging. # Called by the "Accept" button or the Return key of the # "Set pipe log address" file browser. # proc effectuate_debug_logging {close_window} { global debug_log_file debug_logging browse_disk_window_is_active if {$close_window == 1 && $browse_disk_window_is_active == 1} { destroy_browse_disk .browse_disk_window } if {$close_window == 1 || $debug_logging > 0} { start_debug_logging $debug_log_file $debug_logging } } # Trigger execution of a script of xorriso commands. # Called by the "Script/Log" menu. # proc start_script_execution {} { browse_tree execute_script_adr "localfs" # actual script start is done by browse_tree_accept -> execute_script } # Permanently ban any extraction from ISO to hard disk # proc osirrox_banned {} { global osirrox_allowed reset_yesno_to_all if {[window_yesno \ "Really irrevocably ban any extraction from ISO to hard disk ?"] \ != 1} { return "" } send_loggable_cmd "-osirrox banned" set osirrox_allowed 0 set m ".script_log.menu" $m entryconfigure "Allow extract to disk" -state "disabled" $m entryconfigure "Permanently ban extraction" -state "disabled" .extract_button configure -state "disabled" } # ------ A primitive file tree browser for hard disk filesystem and ISO model # Write a directory content list into a Tree widget # proc browse_tree_fill_dir {tr parent children} { if {$parent == "/"} { set parent_name root set parent_dir / } else { set parent_name [escape_to_tree $parent] set parent_dir $parent_name } if {[$tr exists $parent_name] == 0} {return ""} $tr delete [$tr nodes $parent_name] foreach i $children { set name [string range $i 2 end] set escpd [escape_to_tree $name] set adr [combine_dir_and_name $parent_dir $escpd] $tr insert end $parent_name $adr -text $name if {[string range $i 0 0] == "d"} { set dir_dummy [combine_dir_and_name $adr "_"] $tr insert end $adr $dir_dummy -text " " } } } # The command to be executed when the user double-clicks a node. # proc browse_tree_accept {adr_var_name do_return tr value} { global have_bwidget global extract_to_adr insert_from_adr burn_write_image_adr isodir_adr global isomanip_move_target indev_adr outdev_adr cmd_log_target global debug_log_file execute_script_adr # Caution: Before using $tr, check for $have_bwidget if {$adr_var_name == "burn_write_image_adr"} { set burn_write_image_adr $value if {$do_return == 1} {burn_write_image} } if {$adr_var_name == "extract_to_adr"} { set extract_to_adr $value if {$do_return == 1} {extract_to} } if {$adr_var_name == "insert_from_adr"} { set insert_from_adr $value if {$do_return == 1} {insert_from} } if {$adr_var_name == "isodir_adr"} { set isodir_adr $value if {$do_return == 1} {isodir_return "browse_tree_accept"} } if {$adr_var_name == "isomanip_move_target"} { set isomanip_move_target $value if {$do_return == 1} {isomanip_mv} } if {$adr_var_name == "indev_adr"} { set indev_adr $value if {$do_return == 1} {indev_return} } if {$adr_var_name == "outdev_adr"} { set outdev_adr $value if {$do_return == 1} {outdev_return} } if {$adr_var_name == "cmd_log_target"} { set cmd_log_target $value if {$do_return == 1} {effectuate_command_logging 1} } if {$adr_var_name == "debug_log_file"} { set debug_log_file $value if {$do_return == 1} {effectuate_debug_logging 1} } if {$adr_var_name == "execute_script_adr"} { set execute_script_adr $value if {$do_return == 1} {execute_script 1} } } # Translate a browser tree variable in a human readable topic text # proc browse_tree_topic {adr_var_name} { if {$adr_var_name == "burn_write_image_adr"} { return "Burn image file:" } if {$adr_var_name == "extract_to_adr"} { return "Extract to disk:" } if {$adr_var_name == "insert_from_adr"} { return "Insert from disk:" } if {$adr_var_name == "isodir_adr"} { return "ISO directory:" } if {$adr_var_name == "isomanip_move_target"} { return "Move to:" } if {$adr_var_name == "indev_adr"} { return "Input drive/image" } if {$adr_var_name == "outdev_adr"} { return "Output drive/image" } if {$adr_var_name == "cmd_log_target"} { return "Set log script address" } if {$adr_var_name == "debug_log_file"} { return "Set pipe log address" } if {$adr_var_name == "execute_script_adr"} { return "Execute command script" } return $adr_var_name } # Unescape &|^! from Bwidget tree browser # proc unescape_from_tree {text} { return [string map [list "\{\{\}" "\{" "\{+\}" "&" "\{I\}" "|" \ "\{A\}" "^" "\{.\}" "!"] \ $text] # <<< alternative encoding # set escpd [string map [list "\\\\" "\\" "\\+" "&" "\\I" "|" \ # "\\A" "^" "\\." "!"] \ } # Escape &|^! which are special to BWidget Tree # proc escape_to_tree {text} { return [string map [list "\{" "\{\{\}" "&" "\{+\}" "|" "\{I\}" \ "^" "\{A\}" "!" "\{.\}"] \ $text] } # Accept the single selected item of the tree browser # Called by the \"Accept\" button in the browser window. # proc browse_tree_accept_sel {adr_var_name do_return tr} { set selected [$tr selection get] if {[llength $selected] != 1} { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : You must select a single tree item before clicking the \"Accept\" button." return "" } browse_tree_accept $adr_var_name $do_return $tr \ [unescape_from_tree [lindex $selected 0]] } # Hit the Return key on the text entry of the browser # proc browse_tree_accept_entry {adr_var_name do_return tr} { global extract_to_adr insert_from_adr burn_write_image_adr isodir_adr global isomanip_move_target indev_adr outdev_adr cmd_log_target global debug_log_file execute_script_adr eval set text $$adr_var_name browse_tree_accept $adr_var_name $do_return $tr $text } # Submit a Tree-escaped path to browse_tree_accept. # Called by Double-click in browser. # proc browse_tree_accept_escd {adr_var_name do_return tr escd_path} { browse_tree_accept $adr_var_name $do_return $tr \ [unescape_from_tree $escd_path] } # Move up one directory level of the file browser selection # proc browse_tree_up {adr_var_name tr which_fs} { global extract_to_adr insert_from_adr burn_write_image_adr isodir_adr global isomanip_move_target indev_adr outdev_adr set selected [$tr selection get] if {[llength $selected] != 1} { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : You must select a single tree item before clicking the \"Up\" button." return "" } set old_adr [lindex $selected 0] set adr [file dirname $old_adr] catch { $tr see $adr if {[$tr nodes $old_adr 0] != ""} { $tr closetree $old_adr } } if {$adr != "/" && $adr != ""} { $tr selection clear $tr selection set $adr } } # Move down one directory level of the file browser selection # proc browse_tree_down {adr_var_name tr which_fs} { global extract_to_adr insert_from_adr burn_write_image_adr isodir_adr global isomanip_move_target indev_adr outdev_adr set selected [$tr selection get] if {[llength $selected] != 1} { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : You must select a single tree item before clicking the \"Down\" button." return "" } set adr [lindex $selected 0] if {$which_fs == "isofs"} { browse_iso_open_dir $tr $adr } else { browse_disk_open_dir $tr $adr } catch { $tr opentree $adr 0 $tr see $adr } } # The command to be executed when the user closes a directory node. # It replaces the directory content list by a single dummy item. # proc browse_tree_close_dir {tr name} { browse_tree_fill_dir $tr $name [list "? "] } # Delete the old content of the browse window and display the freshly # obtained current state down to the current address in the field variable. # proc browse_tree_populate {which_fs} { global have_bwidget global browse_disk_window_var browse_iso_window_var global browse_iso_window_is_active browse_disk_window_is_active global extract_to_adr insert_from_adr burn_write_image_adr isodir_adr global isomanip_move_target indev_adr outdev_adr cmd_log_target global debug_log_file execute_script_adr if {$have_bwidget != 1} {return ""} if {$which_fs == "isofs"} { if {$browse_iso_window_is_active == 0} {return ""} set w {.browse_iso_window} set open_dir_cmd "browse_iso_open_dir" set adr_var $browse_iso_window_var } else { if {$browse_disk_window_is_active == 0} {return ""} set w {.browse_disk_window} set open_dir_cmd "browse_disk_open_dir" set adr_var $browse_disk_window_var } # Variable indirection eval set adr $$adr_var # Install root level $open_dir_cmd $w.tree "/" # Set $adr as current address set comps [split $adr "/"] # Install the stack of directories above current address set path "/" foreach i $comps { if {$i == ""} { continue } set path [combine_dir_and_name $path [escape_to_tree $i]] $open_dir_cmd $w.tree $path catch { $w.tree opentree $path 0 $w.tree see $path } } } # The procedure to be run by mouse button 3 in the file browser. # It has to strip off the surplus parameter added by the Tree widget. # proc browse_tree_help {about_what button_color from_item} { window_help $about_what $button_color } # Destroy the hard disk browser pop-up window. # proc destroy_browse_disk {w} { global browse_disk_window_is_active browse_disk_window_geometry global browse_disk_window_is_grabbed if {$w != "" && $browse_disk_window_is_active == 1} { if {$browse_disk_window_is_grabbed == 1} { grab release $w } set browse_disk_window_is_grabbed 0 set browse_disk_window_geometry [wm geometry $w] destroy $w } set browse_disk_window_is_active 0 } # The command to be executed when the user opens a directory node in # the hard disk filesystem. # proc browse_disk_open_dir {tr name} { set escpd [unescape_from_tree $name] if {[localfs_filetype $escpd] != "d"} {return ""} set lslist [localfs_ls $escpd] browse_tree_fill_dir $tr $escpd $lslist } # Refresh the content of a possibly displayed tree browser for hard disk # proc browse_disk_refresh {} { browse_tree_populate "localfs" } # The command to be executed when the user opens a directory node in # the ISO model. # proc browse_iso_open_dir {tr name} { set escpd [unescape_from_tree $name] if {[isofs_filetype $escpd] != "d"} {return ""} set lslist [isofs_ls $escpd] browse_tree_fill_dir $tr $escpd $lslist } # Destroy the ISO browser pop-up window. # proc destroy_browse_iso {w} { global browse_iso_window_is_active browse_iso_window_geometry global browse_iso_window_is_grabbed if {$w != "" && $browse_iso_window_is_active == 1} { set browse_iso_window_geometry [wm geometry $w] if {$browse_iso_window_is_grabbed == 1} { grab release $w } set browse_iso_window_is_grabbed 0 destroy $w } set browse_iso_window_is_active 0 } # Refresh the content of a possibly displayed tree browser for ISO model # proc browse_iso_refresh {} { browse_tree_populate "isofs" } # Multiplexer for updating both vertical scrollbars # proc browse_tree_yscrollcommand {w arg1 arg2} { $w.treescroll_y_l set $arg1 $arg2 $w.treescroll_y_r set $arg1 $arg2 } # Open a file browser window for hard disk filesystem or ISO model # proc browse_tree {adr_var which_fs} { upvar $adr_var adr global have_bwidget browse_disk_window_is_active browse_iso_window_is_active global browse_disk_window_var browse_iso_window_var global tree_window_lines tree_window_width tree_window_button_width global browse_disk_window_geometry browse_iso_window_geometry set button_color "grey" if {$which_fs == "isofs"} { set w {.browse_iso_window} set window_is_active $browse_iso_window_is_active set title_name "xorriso-tcltk ISO model browser" set open_dir_cmd "browse_iso_open_dir" set destroy_cmd "destroy_browse_iso" if {$browse_iso_window_var != $adr_var && $window_is_active == 1} { destroy_browse_iso $w set window_is_active 0 } set browse_iso_window_var $adr_var set old_geometry $browse_iso_window_geometry set browse_iso_window_is_active 1 } else { set w {.browse_disk_window} set window_is_active $browse_disk_window_is_active set title_name "xorriso-tcltk hard disk filesystem browser" set open_dir_cmd "browse_disk_open_dir" set destroy_cmd "destroy_browse_disk" if {$browse_disk_window_var != $adr_var && $window_is_active == 1} { destroy_browse_disk $w set window_is_active 0 } set browse_disk_window_var $adr_var set old_geometry $browse_disk_window_geometry set browse_disk_window_is_active 1 } set re_use_widgets 0 if {$window_is_active == 0} { toplevel $w -borderwidth 10 -class Browser wm title $w $title_name set_window_position $w $old_geometry } else { set re_use_widgets 1 } if {$re_use_widgets == 0} { if {$have_bwidget == 1} { # BWidget Tree frame $w.tree_frame frame $w.tree_frame_x Tree $w.tree -width $tree_window_width -height $tree_window_lines \ -opencmd "$open_dir_cmd $w.tree" \ -closecmd "browse_tree_close_dir $w.tree" \ -selectfill 1 \ -yscrollcommand "browse_tree_yscrollcommand $w" \ -xscrollcommand "$w.treescroll_x set" # ??? why doesn't <Return> work ? # $w.tree bindText <Return> \ # "browse_tree_accept_bindtext $adr_var 1 $w.tree" # At least double-click does work $w.tree bindText <Double-Button-1> \ "browse_tree_accept_escd $adr_var 1 $w.tree" $w.tree bindText <Button-3> {browse_tree_help "Browse tree" grey} scrollbar $w.treescroll_y_l -command "$w.tree yview" scrollbar $w.treescroll_y_r -command "$w.tree yview" scrollbar $w.treescroll_x -orient horizontal -command "$w.tree xview " pack $w.tree -in $w.tree_frame_x -side top -expand 1 -fill both pack $w.treescroll_x -in $w.tree_frame_x -side top -expand 1 -fill x pack $w.treescroll_y_l -in $w.tree_frame -side left -expand 1 -fill y pack $w.tree_frame_x -in $w.tree_frame -side left -expand 1 -fill both pack $w.treescroll_y_r -in $w.tree_frame -side left -expand 1 -fill y frame $w.button_line button $w.accept -text "Accept" -width $tree_window_button_width \ -command "browse_tree_accept_sel $adr_var 1 $w.tree" bind_help $w.accept "Accept (browse tree)" button $w.to_field -text "Edit" -width $tree_window_button_width \ -command "browse_tree_accept_sel $adr_var 0 $w.tree" bind_help $w.to_field "Edit (browse tree)" button $w.up -text "Up" -width $tree_window_button_width \ -command "browse_tree_up $adr_var $w.tree $which_fs" bind_help $w.up "Up (browse tree)" button $w.down -text "Down" -width $tree_window_button_width \ -command "browse_tree_down $adr_var $w.tree $which_fs" bind_help $w.down "Down (browse tree)" pack $w.up $w.down $w.accept $w.to_field \ -in $w.button_line -side left -expand 0 pack $w.tree_frame -side top -anchor w -expand 1 -fill both } else { frame $w.button_line button $w.accept -text "Accept" -width $tree_window_button_width \ -command "browse_tree_accept_entry $adr_var 1 $w.tree" bind_help $w.accept "Accept (browse tree)" pack $w.accept -in $w.button_line -side left -expand 0 } button $w.help -text "Help" -width $tree_window_button_width \ -command {window_help "Browse tree" grey} bind_help $w.help "Browse tree" button $w.close -text "Close" -width $tree_window_button_width \ -command "$destroy_cmd $w" \ -background $button_color bind_help $w.close "Close (browse tree)" pack $w.help $w.close \ -in $w.button_line -side left -expand 0 pack $w.button_line -side top -anchor center frame $w.text_frame label $w.topic -text "[browse_tree_topic $adr_var]" bind_help $w.topic "Browse tree" entry $w.text_entry -relief sunken -bd 1 -width 40 \ -textvariable $adr_var bind_entry_keys $w.text_entry \ "browse_tree_accept_entry $adr_var 1 $w.tree" bind_help $w.text_entry "Browse tree" pack $w.topic -in $w.text_frame -side left pack $w.text_entry -in $w.text_frame -side left -expand 1 -fill both pack $w.text_frame -side top -expand 1 -fill both } raise $w if {$have_bwidget == 1} { browse_tree_populate $which_fs focus $w.tree } update idletasks } # ------ GUI display procedures ---- # Display a message of xorriso or of this frontend in the .msglist box # proc display_msg {msg} { global .msglist global msglist_max_fill msglist_running pre_msglist display_msg_enabled if {$display_msg_enabled == 0} {return ""} if {$msg == "============================" || \ $msg == "==============================================================" || \ $msg == "enter option and arguments :"} {return ""} if {$msglist_running == 0} { lappend pre_msglist $msg } else { if {[.msglist index end] > $msglist_max_fill} { .msglist delete 0 0 } .msglist insert end [escape_newline $msg 0] .msglist see [expr "[.msglist index end]-1"] update idletasks } } # Set whether messages submitted to proc display_message shall really show up # This is used by callback procedures to hide auxiliary commands and lengthy # reply messages from the user display. # proc set_display_msg {mode} { global display_msg_enabled set old $display_msg_enabled if {$mode == "0"} { set display_msg_enabled 0 } else { set display_msg_enabled "1" } return $old } # Display a frontend error message in the .msglist box and by a pop-up window. # proc xorriso_tcltk_errmsg {msg} { global highest_cmd_sev_msg set highest_cmd_sev_msg [escape_newline $msg 0] display_msg $msg window_ack $msg "grey" "toplevel" update idletasks } # Memorize the current selection in the .isolist box. # proc memorize_isolist_selection {} { global memorized_isolist_selection isolist_names global .isolist set memorized_isolist_selection "" set selected [.isolist curselection] foreach i $selected { lappend memorized_isolist_selection [lindex $isolist_names $i] } } # Restore the memorized selection in the .isolist box as far as the # names have survived in the meantime. # proc restore_isolist_selection {} { global memorized_isolist_selection isolist_names global .isolist .isolist selection clear 0 end foreach i $memorized_isolist_selection { set idx [lsearch -exact $isolist_names $i] if {$idx > -1} { .isolist selection set $idx $idx } } set memorized_isolist_selection "" } # Receive the answer of the yes/no window and destroy it. # proc destroy_yesno {w answer} { global yesno_window_is_active answer_of_yesno yesno_window_geometry global yesno_to_all if {$w != ""} { set yesno_window_geometry [wm geometry $w] grab release $w destroy $w update idletasks } set yesno_window_is_active 0 set answer_of_yesno $answer if {$answer == 2} { set yesno_to_all 1 set answer_of_yesno 1 } if {$answer == -1} { set yesno_to_all -1 set answer_of_yesno 0 } } # Pop-up a window which asks for yes or no. Return 1 if answer is yes. # proc window_yesno {question} { global answer_of_yesno yesno_window_is_active yesno_window_geometry set w {.yesno_window} if {$yesno_window_is_active == 1} { set yesno_window_is_active [window_exists $w] } if {$yesno_window_is_active == 1} { raise $w xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : You still need to answer an older yes/no question" return "0" } set yesno_window_is_active 1 set answer_of_yesno "" toplevel $w -borderwidth 20 -class Dialog wm title $w "xorriso-tcltk yes/no" set_window_position $w $yesno_window_geometry label $w.question -text $question button $w.yes -text "yes" -command "destroy_yesno $w 1" \ -borderwidth 4 -padx 20 -pady 20 bind_help $w.yes "yes/no" button $w.no -text "no" -command "destroy_yesno $w 0" \ -borderwidth 4 -padx 20 -pady 20 bind_help $w.no "yes/no" pack $w.yes $w.question $w.no -side left update idletasks grab set $w tkwait variable answer_of_yesno return $answer_of_yesno } # Pop-up a window which asks for yes, yes-to-all, no, or no-to-all. # Return 1 if answer is yes. # proc window_yesno_ever {question} { global answer_of_yesno yesno_window_is_active yesno_window_geometry global yesno_to_all set w {.yesno_window} if {$yesno_window_is_active == 1} { set yesno_window_is_active [window_exists $w] } if {$yesno_window_is_active == 1} { raise $w xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : You still need to answer an older yes/no question" return "0" } if {$yesno_to_all == 1} { return "1" } if {$yesno_to_all == -1} { return "0" } set yesno_window_is_active 1 set answer_of_yesno "" toplevel $w -borderwidth 20 -class Dialog wm title $w "xorriso-tcltk yes/no" set_window_position $w $yesno_window_geometry frame $w.yes_frame frame $w.no_frame label $w.question -text $question button $w.yes -text "yes" -command "destroy_yesno $w 1" \ -borderwidth 4 -padx 20 -pady 20 -relief raised button $w.no -text "no" -command "destroy_yesno $w 0" \ -borderwidth 4 -padx 20 -pady 20 -relief raised button $w.yes_to_all -text "yes to all" -command "destroy_yesno $w 2" bind_help $w.yes_to_all "yes to all" button $w.no_to_all -text "no to all" -command "destroy_yesno $w -1" bind_help $w.no_to_all "no to all" pack $w.yes $w.yes_to_all -in $w.yes_frame -side top -expand 1 -fill both pack $w.no $w.no_to_all -in $w.no_frame -side top -expand 1 -fill both pack $w.yes_frame $w.question $w.no_frame \ -in $w -side left -expand 1 -fill both raise $w update idletasks grab set $w tkwait variable answer_of_yesno return $answer_of_yesno } proc reset_yesno_to_all {} { global yesno_to_all set yesno_to_all 0 } # Destroy the notification pop-up window. # proc destroy_ack {w had_focus} { global ack_window_is_active ack_window_geometry if {$w != ""} { set ack_window_geometry [wm geometry $w] grab release $w if {$had_focus != "-"} { focus $had_focus } destroy $w update idletasks } set ack_window_is_active 0 } # Pop-up a window which notifies of a problem and asks for a button click. # proc window_ack {question button_color where} { global answer_of_yesno ack_window_is_active ack_window_geometry global continue_from_ack set had_focus [focus] if {$had_focus == ""} {set had_focus "-"} set re_use_widgets 0 if {$where == "embedded"} { set w "" set destroy_cmd "" } else { set w {.ack_window} if {$ack_window_is_active == 1} { set ack_window_is_active [window_exists $w] } if {$ack_window_is_active == 0} { toplevel $w -borderwidth 20 -class Dialog wm title $w "xorriso-tcltk acknowledge" set ack_window_is_active 1 } else { set re_use_widgets 1 } set_window_position $w $ack_window_geometry set destroy_cmd "destroy_ack $w $had_focus" } if {$re_use_widgets == 1} { $w.question configure -text $question } else { label $w.question -text $question button $w.ok -text "Continue" -command $destroy_cmd \ -background $button_color bind $w.ok <Return> $destroy_cmd bind_help $w.ok "Continue" pack $w.question -side top -expand 1 -fill both pack $w.ok -side top } raise $w update idletasks focus $w.ok grab set $w tkwait variable ack_window_is_active } # Destroy the help pop-up window. # proc destroy_help {w help_main} { global help_window_is_active help_window_has_scroll help_window_geometry global main_help_window_is_active main_help_window_geometry if {$w != ""} { if {$help_main == 1} { set main_help_window_geometry [wm geometry $w] } else { set help_window_geometry [wm geometry $w] } destroy $w } if {$help_main == 1} { set main_help_window_is_active 0 } else { set help_window_is_active 0 set help_window_has_scroll 0 } } proc surround_text {text} { return "\n\n [string map {\n "\n "} $text]\n" } # Pop-up a window which shows a help text and a Close button. # proc window_help {about_what button_color} { global help_window_is_active help_window_lines help_window_has_scroll global help_window_border_width help_window_geometry global main_help_window_is_active global main_help_window_lines main_help_window_geometry global .help_window .main_help_window # The main help window is independent of the GUI element help window if {$about_what == "Help"} { set help_main 1 set w {.main_help_window} set window_is_active $main_help_window_is_active set window_has_scroll 1 set old_geometry $main_help_window_geometry set window_lines $main_help_window_lines } else { set help_main 0 set w {.help_window} set window_is_active $help_window_is_active set window_has_scroll $help_window_has_scroll set old_geometry $help_window_geometry set window_lines $help_window_lines } if {$window_is_active == 1} { set window_is_active [window_exists $w] } # Giving the help text some distance from the border decorations set line_width 82 set helptext "\n\n [string map {\n "\n "} [tell_helptext $about_what]]\n" if {[count_newlines $helptext] >= $window_lines} { if {$window_is_active == 1 && $window_has_scroll == 0} { destroy_help $w $help_main set window_is_active 0 } if {$help_main == 1} { set old_geometry $main_help_window_geometry } else { set help_window_has_scroll 1 set window_has_scroll 1 set old_geometry $help_window_geometry } } # Dealing with initiating windows that are grabbed set grabbed [grab current] if {$grabbed == ""} {set grabbed "-"} if {$grabbed != "-" && $window_is_active == 1} { destroy_help $w $help_main set window_is_active 0 } if {$grabbed != "-"} { # Set old_geometry to position underneath grabbed window set value [wm geometry $grabbed] set idx [string first "+" $value] set height_idx [string first "x" $value] if {$idx != -1 && $height_idx != -1 && $idx > $height_idx} { set width [string range $value 0 [expr $height_idx-1]] set height [string range $value [expr $height_idx+1] [expr $idx-1]] set x [string range $value [expr $idx+1] end] set idx [string first "+" $x] if {$idx != -1} { set y [string range $x [expr $idx+1] end] set x [string range $x 0 [expr $idx-1]] set y [expr $y+$height] set old_geometry "${width}x${height}+${x}+${y}" } } } set re_use_widgets 0 if {$window_is_active == 0} { toplevel $w -borderwidth $help_window_border_width -class Help set_window_position $w $old_geometry if {$help_main == 1} { wm title $w "xorriso-tcltk main help text" set main_help_window_is_active 1 reset_to_normal_background .help update idletasks } else { wm title $w "xorriso-tcltk GUI element help text" set help_window_is_active 1 } } else { set re_use_widgets 1 } if {$re_use_widgets == 1} { $w.text configure -state normal $w.text delete 1.0 end $w.text insert end $helptext raise $w } else { set destroy_cmd "destroy_help $w $help_main" frame $w.text_frame text $w.text -width $line_width -height $window_lines \ -relief flat -borderwidth 0 $w.text insert end $helptext pack $w.text -in $w.text_frame -side left -expand 1 -fill both if {$window_has_scroll == 1} { scrollbar $w.scroll_y -command "$w.text yview" $w.text configure -yscrollcommand "$w.scroll_y set" bind_listbox_keys $w.text $window_lines "text" pack $w.scroll_y -in $w.text_frame -side left -fill y } button $w.close -text "Close" -command $destroy_cmd \ -background $button_color pack $w.text_frame -side top -expand 1 -fill both frame $w.middle_spacer -height 6 frame $w.bottom_spacer -height 6 pack $w.middle_spacer $w.close $w.bottom_spacer -side top } $w.text configure -state disabled } # Display the busy/ready state of xorriso. # Called with 1 by sender of commands and with 0 by receivers of replies . # proc display_busy {state} { global busy_text_exists global .busy_text if {$busy_text_exists == 0} {return ""} if {$state == 0} { .busy_text configure -text "ready" .busy_text configure -background "#D0D0D0" } else { .busy_text configure -text "busy" .busy_text configure -background "#808080" } update idletasks } # Tries to make use of the BWidget package for getting its Tree widget # proc check_for_bwidget {} { global have_bwidget bwidget_version if {$have_bwidget == 0} { catch { set bwidget_version [package require BWidget] set have_bwidget 1 } } } # A window to display if no file browser is available # proc browser_dummy {} { window_ack \ "The file browser cannot be used because Tcl/Tk package \"BWidget\" is not loaded" "grey" "toplevel" } # Obtain the geometry string of a window # proc get_window_geometry {w} { wm geometry $w } # Set the position of a window from a geometry string # proc set_window_position {w geometry} { set value $geometry set idx [string first "+" $value] if {$idx == -1} { set value [wm geometry .] set idx [string first "+" $value] } if {$idx == -1} { return "" } set pos [string range $value $idx end] wm geometry $w $pos } # Reset button appearance from startup color to normal color # proc reset_to_normal_background {w} { set normal_color [.drive_drop_both cget -background] $w configure -background $normal_color } # Checks whether a window is really there # proc window_exists {w} { set window_exists 0 catch { $w cget -background set window_exists 1 } return $window_exists } # ------ Building GUI components ------ # ------ GUI layout parameters ------ # The default position of the main window set main_window_geometry "" # How to mark the borders of the main grouping frames set main_framerelief ridge set main_borderwidth 4 # How to mark the borders of the second level grouping frames set borderwidth 1 # Number of lines in msglist display set msglist_lines 8 set msglist_max_fill 1000 set msglist_running 0 # Number of lines in drivelist display set drivelist_lines 2 # Number of lines in ISO directory content display set isolist_lines 8 # Whether the message box shall export its selection to the whole X display set export_msg_selection true # Whether the item lists shall export their selection set export_selection false # The number of lines in the display of the help texts set main_help_window_lines 24 set help_window_lines 16 # The distance of the help text from the help window border set help_window_border_width 0 # The number of items to display in a tree browser window set tree_window_lines 12 # The number of visible characters in a tree browser line set tree_window_width 50 # The width in characters of the six buttons under the tree browser set tree_window_button_width 6 # -------- GUI definition procedures # Overall definition of the GUI # proc init_gui {} { global .input .cmdline_entry .msgbox .errmsg .dev .drivebox global .isobox .localfs global main_framerelief main_borderwidth click_to_focus check_for_bwidget # Main grouping frames frame .connection_block \ -relief $main_framerelief -borderwidth $main_borderwidth frame .drive_block \ -relief $main_framerelief -borderwidth $main_borderwidth frame .iso_block \ -relief $main_framerelief -borderwidth $main_borderwidth init_input init_msgbox init_errmsg init_dev init_drivebox init_isobox init_isomanip init_burn init_localfs pack .input .msgbox .errmsg -in .connection_block \ -side top -expand 1 -fill both pack .drivebox .dev .burn -in .drive_block \ -side top -expand 1 -fill both pack .localfs .isobox .isomanip -in .iso_block \ -side top -expand 1 -fill both pack .connection_block .drive_block .iso_block \ -side top -expand 1 -fill both if {$click_to_focus == 1} { focus .msglist } } # The xorriso headline with End button, xorriso version, busy/ready indicator, # command line, and "Refresh disp" button. # proc init_input {} { global borderwidth busy_text_exists xorriso_version debug_logging global cmd_logging_mode cmd_log_target osirrox_allowed cmd_logging_all global .input .input_line1 .xorriso_version .busy .busy_text global .refresh_state .end_button .cmdline .log_pipes_switch set extract_state "normal" if {$osirrox_allowed == 0} {set extract_state "disabled"} frame .input -borderwidth $borderwidth frame .input_line1 -borderwidth 0 pack .input_line1 -in .input \ -side top -anchor w -expand 1 -fill both button .end_button -text "End" -command "end_xorriso" bind_help .end_button "End" if {[string length $xorriso_version] > 10} { set xorriso_version [string range $xorriso_version 0 9] } label .xorriso_version -text "xorriso-$xorriso_version" bind_help .xorriso_version "version" frame .busy -relief ridge -borderwidth 2 label .busy_text -width 5 -text "busy" bind_help .busy_text "ready/busy" set busy_text_exists 1 pack .busy_text -in .busy button .refresh_state -text "Refresh disp" \ -command "refresh_state" bind_help .refresh_state "Refresh disp" menubutton .script_log -text "Script/Log" -anchor w \ -direction below -relief ridge -indicatoron 1 \ -menu .script_log.menu bind_help .script_log "Script/Log" set m ".script_log.menu" menu $m $m add checkbutton -label "Log command script" \ -indicatoron 1 -selectcolor "" \ -command "effectuate_command_logging 0" \ -variable cmd_logging_mode \ -onvalue 1 -offvalue 0 $m add checkbutton -label "Log non-essential commands" \ -indicatoron 1 -selectcolor "" \ -variable cmd_logging_all \ -onvalue 1 -offvalue 0 $m add command -label "Set log script address" \ -command "set_log_script_address" $m add separator $m add checkbutton -label "Log pipes" \ -indicatoron 1 -selectcolor "" \ -variable debug_logging \ -onvalue 1 -offvalue 0 $m add command -label "Set pipe log address" \ -command "set_debug_log_address" $m add separator $m add separator $m add command -label "Execute command script" \ -command "start_script_execution" $m add checkbutton -label "Allow extract to disk" \ -state $extract_state \ -indicatoron 1 -selectcolor "" \ -variable script_with_osirrox \ -onvalue 1 -offvalue 0 $m add separator $m add command -label "Permanently ban extraction" \ -state $extract_state \ -command "osirrox_banned" button .help -text "Help" -command {window_help "Help" "grey"} \ -background "grey" bind_help .help "Help" init_cmdline pack .end_button .xorriso_version .busy -in .input_line1 -side left pack .cmdline \ -in .input_line1 -side left -expand 1 -fill both pack .refresh_state .script_log .help -in .input_line1 -side left } # The combination of "Command:" label and command line # proc init_cmdline {} { global cmdline borderwidth global .cmdline .cmdline_text .cmdline_entry frame .cmdline -borderwidth 0 label .cmdline_text -width 10 -text "Command:" bind_help .cmdline_text "Command:" entry .cmdline_entry -width 56 -relief sunken -bd 1 \ -textvariable cmdline bind_entry_keys .cmdline_entry {cmdline_return} bind_help .cmdline_entry "Command:" # >>> is there a chance to get a history on an entry ? pack .cmdline_text -in .cmdline -side left pack .cmdline_entry -in .cmdline -side left -expand 1 -fill both } # The listbox where to display commands and reply messages unless this is # disabled for auxiliary commands which shall not clutter the display. # proc init_msgbox {} { global borderwidth global msglist_lines export_msg_selection msglist_running pre_msglist global .msgbox .msglist .msgscroll frame .msgbox -borderwidth $borderwidth listbox .msglist -height $msglist_lines -selectmode extended \ -yscrollcommand ".msgscroll set" \ -exportselection $export_msg_selection bind_listbox_keys ".msglist" $msglist_lines "listbox" bind_help .msglist "message box" set msglist_running 1 foreach i $pre_msglist { display_msg [escape_newline $i 0] } scrollbar .msgscroll -command ".msglist yview" pack .msglist -in .msgbox -side left -expand 1 -fill both pack .msgscroll -in .msgbox -side right -fill y set pre_msglist "" } # Two display lines for most severe event messages. One gets reset with # each important command. The other one stays until the user clears it. # proc init_errmsg {} { global borderwidth global .errmsg .total_errmsg .cmd_errmsg frame .errmsg -borderwidth $borderwidth init_total_errmsg init_cmd_errmsg pack .cmd_errmsg .total_errmsg -in .errmsg \ -side top -anchor w -expand 1 -fill both } # The most severe message display which gets reset automatically. # proc init_cmd_errmsg {} { global borderwidth global .cmd_errmsg .cmd_errmsg_label .cmd_errmsg_msg frame .cmd_errmsg -borderwidth $borderwidth label .cmd_errmsg_label -width 14 -text "Recent problem:" -anchor w bind_help .cmd_errmsg_label "Recent problem:" label .cmd_errmsg_msg -width 80 -relief ridge -bd 2 \ -anchor w \ -textvariable highest_cmd_sev_msg # (no keys, no focus) bind_help .cmd_errmsg_msg "Recent problem:" pack .cmd_errmsg_label -in .cmd_errmsg -side left pack .cmd_errmsg_msg -in .cmd_errmsg -side left -expand 1 -fill both } # The persistent most severe message display that is to be reset by the user. # proc init_total_errmsg {} { global borderwidth global .total_errmsg .total_errmsg_label .total_errmsg_msg global .total_errmsg_clear frame .total_errmsg -borderwidth $borderwidth label .total_errmsg_label -text "Worst problem:" -width 14 -anchor w bind_help .total_errmsg_label "Worst problem:" button .total_errmsg_clear -text "Clear" \ -width 5 \ -command "clear_total_errmsg" bind_help .total_errmsg_clear "Clear" label .total_errmsg_msg -width 80 -relief ridge -bd 2 \ -anchor w \ -textvariable highest_total_sev_msg # (no keys, no focus) bind_help .total_errmsg_msg "Worst problem:" pack .total_errmsg_label -in .total_errmsg -side left pack .total_errmsg_msg -in .total_errmsg -side left -expand 1 -fill both pack .total_errmsg_clear -in .total_errmsg -side left } # The list of drives which were found by scanning, the Scan button, and # buttons for picking a drive from the list, for giving them up, for # calming them down, and for reloading the ISO image from the input drive. # proc init_drivebox {} { global borderwidth drivelist_lines export_selection global .drivebox .drivelistbox .drivelist .drivescroll .drive_scan global .drive_picker .drive_scan .drive_pick_in .drive_pick_out global .drive_pick_both .drive_drop_both .drive_calm .iso_rollback_button frame .drivebox -borderwidth $borderwidth frame .drivelistbox -borderwidth $borderwidth listbox .drivelist -height $drivelist_lines -selectmode extended \ -yscrollcommand ".drivescroll set" \ -exportselection $export_selection bind_listbox_keys ".drivelist" $drivelist_lines "listbox" bind_help .drivelist "drivelist" scrollbar .drivescroll -command ".drivelist yview" pack .drivelist -in .drivelistbox -side left -expand 1 -fill both pack .drivescroll -in .drivelistbox -side right -fill y frame .drive_picker -borderwidth $borderwidth frame .drive_picker_line_1 -borderwidth 0 frame .drive_picker_line_2 -borderwidth 0 frame .drive_aux_buttons_line_1 -borderwidth 0 frame .drive_aux_buttons_line_2 -borderwidth 0 frame .drive_aux_buttons -borderwidth 0 button .drive_scan -text "Scan for drives" \ -background "grey" \ -command "scan_for_drives" bind_help .drive_scan "Scan for drives" button .drive_pick_in -text "Pick input drive" \ -command "pick_indev" bind_help .drive_pick_in "Pick input drive" button .drive_pick_out -text "Pick output drive" \ -command "pick_outdev" bind_help .drive_pick_out "Pick output drive" button .drive_pick_both -text "Pick drive for both roles" \ -command "pick_dev" bind_help .drive_pick_both "Pick drive for both roles" button .drive_drop_both -text "Give up drives" \ -command "give_up_dev" bind_help .drive_drop_both "Give up drives" button .drive_calm -text "Calm drives" \ -command "calm_drives" bind_help .drive_calm "Calm drives" button .iso_rollback_button -text "Rollback" -width 9 \ -command {iso_rollback} bind_help .iso_rollback_button "Rollback" # One button block left, one right pack .drive_pick_in .drive_pick_out \ -in .drive_picker_line_1 -side left -expand 1 -fill none pack .drive_pick_both \ -in .drive_picker_line_2 -side left -expand 1 -fill x pack .drive_picker_line_1 .drive_picker_line_2 \ -in .drive_picker -side top -expand 1 -fill x -anchor w pack .drive_scan .drive_calm \ -in .drive_aux_buttons_line_1 -side left -expand 1 -fill none pack .drive_drop_both .iso_rollback_button \ -in .drive_aux_buttons_line_2 -side left -expand 1 -fill x pack .drive_aux_buttons_line_1 .drive_aux_buttons_line_2 \ -in .drive_aux_buttons -side top -expand 1 -fill x -anchor w pack .drive_picker -in .drivebox -side left -expand 0 -fill none pack .drivelistbox -in .drivebox -side left -expand 1 -fill both pack .drive_aux_buttons -in .drivebox -side left -expand 0 -fill none bind .drivelist <Double-Button-1> { pick_dev } } # The text fields for setting and display of the current input and output # drives. With Eject button and a short text description of the medium status. # proc init_dev {} { global borderwidth global .dev .indev .outdev frame .dev -borderwidth $borderwidth init_indev init_outdev pack .indev .outdev -in .dev \ -side top -anchor w -expand 1 -fill both } # Set and display the current input drive. # proc init_indev {} { global borderwidth indev_adr global .indev .indev_eject .indev_label .indev_entry .indev_summary frame .indev -borderwidth $borderwidth button .indev_eject -text "Eject" -command {eject_indev} bind_help .indev_eject "Eject (indev)" button .indev_label -width 16 -text "Input drive/image " \ -command {indev_return} bind_help .indev_label "Input drive/image" entry .indev_entry -width 34 -relief sunken -bd 1 \ -textvariable indev_adr bind_entry_keys .indev_entry {indev_return} bind_help .indev_entry "Input drive/image" label .indev_summary -width 60 -text "" -relief ridge -borderwidth 2 bind_help .indev_summary "input drive info" create_browser_button .indev_browse_button \ "indev_adr" "localfs" "Browse disk (indev)" pack .indev_eject .indev_label .indev_entry \ -in .indev -side left -expand 1 -fill both pack .indev_browse_button -in .indev -side left pack .indev_summary \ -in .indev -side left -expand 1 -fill both } # Set and display the current output drive. # proc init_outdev {} { global .outdev .outdev_eject .outdev_label .outdev_entry .outdev_summary global borderwidth outdev_adr frame .outdev -borderwidth $borderwidth button .outdev_eject -text "Eject" -command {eject_outdev} bind_help .outdev_eject "Eject (outdev)" button .outdev_label -width 16 -text "Output drive/image" \ -command {outdev_return} bind_help .outdev_label "Output drive/image" entry .outdev_entry -width 34 -relief sunken -bd 1 \ -textvariable outdev_adr bind_entry_keys .outdev_entry {outdev_return} bind_help .outdev_entry "Output drive/image" create_browser_button .outdev_browse_button \ "outdev_adr" "localfs" "Browse disk (outdev)" label .outdev_summary -width 60 -text "" -relief ridge -borderwidth 2 bind_help .outdev_summary "output drive info" pack .outdev_eject .outdev_label .outdev_entry \ -in .outdev -side left -expand 1 -fill both pack .outdev_browse_button -in .outdev -side left pack .outdev_summary \ -in .outdev -side left -expand 1 -fill both } # The button panel for blanking, formatting, and writing to the output drive. # proc init_burn {} { global borderwidth burn_write_image_adr burn_write_close burn_write_tao global burn_write_defect_mgt global .burn .burn_blank_button .burn_format_button .burn_commit_button global .burn_write_image .burn_write_image_entry .burn_write_close global .burn_write_tao .burn_write_defect_mgt frame .burn -borderwidth $borderwidth button .burn_blank_button -text "Blank" \ -command {burn_blank} bind_help .burn_blank_button "Blank" button .burn_format_button -text "Format" \ -command {burn_format} bind_help .burn_format_button "Format" button .burn_commit_button -text "Write ISO session" \ -command {burn_commit} bind_help .burn_commit_button "Write ISO session" button .burn_write_image -text "Burn image file:" \ -command {burn_write_image} bind_help .burn_write_image "Burn image file:" entry .burn_write_image_entry -width 40 -relief sunken -bd 1 \ -textvariable burn_write_image_adr bind_entry_keys .burn_write_image_entry {burn_write_image} bind_help .burn_write_image_entry "Burn image file:" create_browser_button .burn_image_browse_button \ "burn_write_image_adr" "localfs" "Browse disk (burn image)" checkbutton .burn_write_close -text "Close" \ -indicatoron 1 -selectcolor "" \ -relief ridge -borderwidth 2 \ -variable burn_write_close \ -onvalue 1 -offvalue 0 bind_help .burn_write_close "Close" checkbutton .burn_write_tao -text "TAO" \ -indicatoron 1 -selectcolor "" \ -relief ridge -borderwidth 2 \ -variable burn_write_tao \ -onvalue 1 -offvalue 0 bind_help .burn_write_tao "TAO" checkbutton .burn_write_defect_mgt -text "Defect Mgt" \ -indicatoron 1 -selectcolor "" \ -relief ridge -borderwidth 2 \ -variable burn_write_defect_mgt \ -onvalue 1 -offvalue 0 bind_help .burn_write_defect_mgt "Defect Mgt" pack .burn_blank_button .burn_format_button \ .burn_commit_button .burn_write_close .burn_write_tao \ .burn_write_defect_mgt \ .burn_write_image .burn_write_image_entry \ -in .burn -side left -expand 1 -fill both pack .burn_image_browse_button -in .burn -side left } # Set and display the current ISO directory and its content. # proc init_isobox {} { global borderwidth isolist_lines export_selection global .isobox .isodir .isolist .isodir_entry .isodir_up .isodir_up2 global .isodir_label .isodir_verify .isolistbox .isoscroll_y .isoscroll_x frame .isobox -borderwidth $borderwidth frame .isodir -borderwidth 0 label .isodir_label -text "ISO directory:" \ -width 14 bind_help .isodir_label "ISO directory:" entry .isodir_entry -width 60 -relief sunken -bd 1 \ -textvariable isodir_adr bind_entry_keys .isodir_entry {isodir_return "isodir_entry"} bind_help .isodir_entry "ISO directory:" create_browser_button .isodir_browse_button \ "isodir_adr" "isofs" "Browse ISO (isodir)" button .isodir_verify -text "Verify" -command {isodir_verify} bind_help .isodir_verify "Verify" button .isodir_up -text "Up" -command {isodir_up} bind_help .isodir_up "Up" button .isodir_up2 -text "Up" -command {isodir_up} bind_help .isodir_up2 "Up" pack .isodir_label .isodir_up \ -in .isodir -side left pack .isodir_entry \ -in .isodir -side left -expand 1 -fill both pack .isodir_browse_button .isodir_up2 .isodir_verify \ -in .isodir -side left frame .isolistbox -borderwidth 0 listbox .isolist -height $isolist_lines -selectmode extended \ -yscrollcommand ".isoscroll_y set" \ -xscrollcommand ".isoscroll_x set" \ -exportselection $export_selection bind_listbox_keys ".isolist" $isolist_lines "listbox" bind_help .isolist "isolist" scrollbar .isoscroll_y -command ".isolist yview" scrollbar .isoscroll_x -orient horizontal -command ".isolist xview" pack .isolist -in .isolistbox -side left -expand 1 -fill both bind .isolist <Double-Button-1> { pick_isodir } pack .isoscroll_y -in .isolistbox -side right -fill y pack .isodir .isolistbox .isoscroll_x \ -in .isobox -side top -expand 1 -fill both } # The ISO-internal manipulation buttons for the ISO directory or its content. # Plus a text field where to set an ISO path as target for renaming or # directory making. # proc init_isomanip {} { global borderwidth isomanip_move_target global .isomanip .isomanip_move .isomanip_prefix .isomanip_verify_button global .isomanip_move_target .isomanip_rm_r_button .isomanip_move_button global .isomanip_mkdir_button .isomanip_move_target global .avail_label .avail_label_frame .avail_button frame .isomanip -borderwidth $borderwidth frame .isomanip_move -borderwidth 0 label .isomanip_prefix -text "Selection:" bind_help .isomanip_prefix "Selection:" button .isomanip_verify_button -text "Verify" \ -command {isomanip_verify} bind_help .isomanip_verify_button "Verify (selection)" button .isomanip_rm_r_button -text "Delete" \ -command {isomanip_rm_r} bind_help .isomanip_rm_r_button "Delete" button .isomanip_move_button -text "Move to:" \ -command {isomanip_mv} bind_help .isomanip_move_button "Move to:" button .isomanip_mkdir_button -text "Make dir" \ -command {isomanip_mkdir} bind_help .isomanip_mkdir_button "Make dir" entry .isomanip_move_target -width 60 -relief sunken -bd 1 \ -textvariable isomanip_move_target bind_entry_keys .isomanip_move_target {isomanip_mv} bind_help .isomanip_move_target "rename and mkdir target" create_browser_button .isomanip_move_target_button \ "isomanip_move_target" "isofs" "Browse ISO (move target)" pack .isomanip_prefix .isomanip_verify_button .isomanip_rm_r_button \ .isomanip_move_button \ -in .isomanip_move -side left pack .isomanip_move_target \ -in .isomanip_move -side left -expand 1 -fill both pack .isomanip_move_target_button -in .isomanip_move -side left pack .isomanip_mkdir_button \ -in .isomanip_move -side left -expand 1 -fill both pack .isomanip_move \ -in .isomanip -side top -expand 1 -fill both } # The means for interaction of ISO image and hard disk filesystem. # proc init_localfs {} { global borderwidth global .localfs .extract_frame .aux_control_frame .insert_frame frame .localfs -borderwidth $borderwidth init_extract init_aux_control init_insert pack .extract_frame .aux_control_frame .insert_frame \ -in .localfs -side top -expand 1 -fill both } # The means for extracting files from ISO image to disk # proc init_extract {} { global borderwidth extract_to_adr extract_from_selected extract_underneath global osirrox_allowed global .extract_button .extract_frame .extract_entry .extract_from_selected global .extract_underneath set extract_state "normal" if {$osirrox_allowed == 0} {set extract_state "disabled"} frame .extract_frame -borderwidth 0 button .extract_button -text "Extract to disk:" \ -state $extract_state \ -width 17 \ -command {extract_to} bind_help .extract_button "Extract to disk:" entry .extract_entry -width 40 -relief sunken -bd 1 \ -textvariable "extract_to_adr" bind_entry_keys .extract_entry {extract_to} bind_help .extract_entry "Extract to disk:" create_browser_button .extract_browse_button \ "extract_to_adr" "localfs" "Browse disk (extract)" checkbutton .extract_underneath -text "Underneath" \ -indicatoron 1 -selectcolor "" \ -relief ridge -borderwidth 2 \ -variable extract_underneath \ -onvalue 1 -offvalue 0 bind_help .extract_underneath "Underneath (extract)" checkbutton .extract_from_selected -text "Selected" \ -indicatoron 1 -selectcolor "" \ -relief ridge -borderwidth 2 \ -variable extract_from_selected \ -onvalue 1 -offvalue 0 bind_help .extract_from_selected "Selected (extract)" pack .extract_button -in .extract_frame -side left pack .extract_entry \ -in .extract_frame -side left -expand 1 -fill both pack .extract_from_selected .extract_underneath \ -in .extract_frame -side right pack .extract_browse_button -in .extract_frame -side right } # Some controls which apply to insertion, extraction, or both. # proc init_aux_control {} { global borderwidth have_bwidget permission_policy global .aux_control_frame global .overwrite_iso_files_button .overwrite_dir_button .extract_auto_chmod frame .aux_control_frame -borderwidth 0 menubutton .overwriting -text "Overwriting:" -width 16 -anchor w \ -direction above -relief ridge -indicatoron 1 \ -menu .overwriting.menu bind_help .overwriting "Overwriting:" set_overwriting_label set m ".overwriting.menu" menu $m $m add checkbutton -label "Overwrite ISO files" \ -indicatoron 1 -selectcolor "" \ -command set_overwriting_label \ -variable overwrite_iso_files \ -onvalue 1 -offvalue 0 $m add checkbutton -label "Overwrite ISO dirs" \ -indicatoron 1 -selectcolor "" \ -command set_overwriting_label \ -variable overwrite_iso_dirs \ -onvalue 1 -offvalue 0 $m add checkbutton -label "Overwrite hard disk files" \ -indicatoron 1 -selectcolor "" \ -command set_overwriting_label \ -variable overwrite_disk_files \ -onvalue 1 -offvalue 0 $m add checkbutton -label "Enforce disk dir write access" \ -indicatoron 1 -selectcolor "" \ -command set_overwriting_label \ -variable extract_auto_chmod \ -onvalue 1 -offvalue 0 pack .overwriting -in .aux_control_frame -side left menubutton .perm_policy -text "Permissions: as is" -width 22 -anchor w \ -direction above -relief ridge -indicatoron 1 \ -menu .perm_policy.menu set m ".perm_policy.menu" menu $m -tearoff 0 $m add radiobutton -label "as is" -value "as_is" \ -variable permission_policy -command show_permission_policy $m add radiobutton -label "readable" -value "readable" \ -variable permission_policy -command show_permission_policy $m add radiobutton -label "readonly" -value "readonly" \ -variable permission_policy -command show_permission_policy $m add radiobutton -label "mkisofs -r" -value "mkisofs_r" \ -variable permission_policy -command show_permission_policy show_permission_policy bind_help .perm_policy "Permissions:" button .avail_button -text "Refresh avail:" \ -command {refresh_avail} bind_help .avail_button "Refresh avail:" frame .avail_label_frame -relief ridge -borderwidth 2 label .avail_label -width 12 -text "" bind_help .avail_label "Refresh avail:" pack .avail_label -in .avail_label_frame pack .avail_label_frame .avail_button .perm_policy \ -in .aux_control_frame -side right } # The means for inserting files from disk into the ISO image # proc init_insert {} { global borderwidth insert_from_adr insert_at_selected insert_underneath global .insert_button .insert_from_frame .insert_entry .insert_at_selected global .insert_underneath .insert_frame frame .insert_frame -borderwidth 0 frame .insert_from_frame -borderwidth 0 button .insert_button -text "Insert from disk:" \ -width 17 \ -command {insert_from} bind_help .insert_button "Insert from disk:" entry .insert_entry -width 40 -relief sunken -bd 1 \ -textvariable "insert_from_adr" bind_entry_keys .insert_entry {insert_from} bind_help .insert_entry "Insert from disk:" create_browser_button .insert_browse_button \ "insert_from_adr" "localfs" "Browse disk (insert)" checkbutton .insert_underneath -text "Underneath" \ -indicatoron 1 -selectcolor "" \ -relief ridge -borderwidth 2 \ -variable insert_underneath \ -onvalue 1 -offvalue 0 bind_help .insert_underneath "Underneath (insert)" checkbutton .insert_at_selected -text "Selected" \ -indicatoron 1 -selectcolor "" \ -relief ridge -borderwidth 2 \ -variable insert_at_selected \ -onvalue 1 -offvalue 0 bind_help .insert_at_selected "Selected (insert)" pack .insert_button -in .insert_from_frame -side left pack .insert_entry \ -in .insert_from_frame -side left -expand 1 -fill both pack .insert_browse_button -in .insert_from_frame -side left pack .insert_at_selected .insert_underneath \ -in .insert_from_frame -side right pack .insert_from_frame -in .insert_frame -side left -expand 1 -fill both } # Set common behavior of listboxes in respect to focus and navigation keys. # proc bind_listbox_keys {box height what_widget} { global click_to_focus if {$click_to_focus == 1} { bind $box <1> "focus \"$box\"" bind $box <2> "focus \"$box\"" bind $box <3> "focus \"$box\"" } else { bind $box <Enter> "focus \"$box\"" } # No underlining if {$what_widget == "listbox"} { $box configure -activestyle "none" } # Need to evaluate all $box and $height at bind-time. Thus "-quotes. bind $box <Any-KeyPress> " if {\"%K\" == \"Up\"} { $box yview scroll \"-1\" units } if {\"%K\" == \"Down\"} { $box yview scroll 1 units } if {\"%K\" == \"Prior\"} { $box yview scroll -[expr \"$height\" - 1] units # $box yview scroll -1 pages } if {\"%K\" == \"Next\"} { $box yview scroll [expr \"$height\" - 1] units # $box yview scroll 1 pages } if {\"%K\" == \"Home\"} { $box yview 0 } if {\"%K\" == \"End\"} { $box yview end } # >>> Do i need this ? # >>> For now: yes. It prevents double scrolling by PgUp PgDown # Prevent other bindings from being performed break " } # Set common behavior of entries in respect to focus and Return key. # proc bind_entry_keys {entry return_cmd} { global click_to_focus if {$click_to_focus != 1} { bind $entry <Enter> "focus \"$entry\"" } if {$return_cmd != ""} { bind $entry <Return> $return_cmd } } # Bind a help text to a widget. # proc bind_help {to_what help_name} { bind $to_what <Button-3> "window_help \"$help_name\" grey" } # Create a "/" button and wire it with variable, filesystem type,and help text. # proc create_browser_button {button_name var_name which_fs help_name} { global have_bwidget button $button_name -text "/" -command "browse_tree $var_name $which_fs" bind_help $button_name $help_name } proc set_overwriting_label {} { global overwrite_iso_files overwrite_iso_dirs overwrite_disk_files global extract_auto_chmod global .overwriting # Determine text suffix for menubutton from overwrite variables set oif "-" if {$overwrite_iso_files == 1} {set oif "f"} set oid "-" if {$overwrite_iso_dirs == 1} {set oid "d"} set ohf "-" if {$overwrite_disk_files == 1} {set ohf "h"} set fdw "-" if {$extract_auto_chmod == 1} {set fdw "w"} set otext "Overwriting: ${oif}${oid}${ohf}${fdw}" .overwriting configure -text $otext } # The central storage for help texts. # proc tell_helptext {what} { global own_version argv0 bwidget_version use_command_move if {$what == "Help"} { return \ "For getting particular help texts: Click the rightmost mouse button on any button, list box, or text field. For a help text about startup options of this frontend, execute in a shell: $argv0 --help For background info about xorriso and its commands, execute in a shell: man xorriso ----------------------------------------------------------------------------- The GUI window is separated into three main areas: - The area for connection to xorriso - shows xorriso messages, - offers some general activities, - displays the \"ready/busy\" state of the connection, - and allows to toggle xorriso commands into the \"Command:\" field. - The area for management of drives and ISO image data files - allows to scan for optical drives, - to acquire them and load their ISO directory tree, - to acquire ISO image files from hard disk as pseudo drives like DVD+RW, - to blank CD-RW, DVD-RW DVD+RW, BD-RE and format DVD-RW, BD-R, - to trigger writing of ISO sessions (which get defined in the third area), - and to burn image data files from hard disk to optical media. - The area for inspection, manipulation, and exploitation of the ISO model - allows to insert directories and files from hard disk into the ISO model, - to delete and rename file objects in the ISO model, - to verify data files of loaded ISO directory trees by MD5, - to extract directories and files from ISO filesystem to hard disk. ----------------------------------------------------------------------------- Some Use Cases ----------------------------------------------------------------------------- - Burn a directory as only content onto a CD, DVD or BD - Write a directory as only content to an ISO image data file on hard disk - Burn an image data file from hard disk onto CD, DVD or BD - Add more data to an appendable medium or to an ISO image data file - Extract a directory tree from an ISO filesystem to hard disk ----------------------------------------------------------------------------- Burn a directory as only content onto a CD, DVD or BD - Click the \"Scan for drives\" button in the middle area. - Select a drive and click the \"Pick output drive\" button. - If the information field in the \"Output drive/image\" line begins by \"appendable\" or \"closed\" and if the medium is CD-RW, DVD-RW, DVD+RW, or BD-RE then click the \"Blank\" button to erase the old data. (Blanking of \"DVD-RW sequential recording\" will last very long.) - Go to the \"Insert from disk:\" line in the lower area. Either toggle in the address of the hard disk directory, or click on the \"/\" button to the right of the text field to get a file browser. - Hit the Return key in the text field or double click on a name in the browser to schedule the disk directory for writing to the medium. You may of course insert several directories or files that way. - Close the browser and click the \"Write ISO session\" button in the middle area. Confirm in the \"yes/no\" window that pops up. Burning will begin (or refuse on unsuitable medium status). - When the \"busy\" field displays \"ready\" again, you may click \"Eject\". Desktop drives should then put out the tray with the medium. ----------------------------------------------------------------------------- Write a directory as only content to an ISO image data file on hard disk - Go to the text field beside the \"Output drive/image\" button and toggle the address of the image file. Click the button or hit the Return key when the address is complete. Or click on the \"/\" button to the right of the field to get a file browser. - You may click on a name in the browser and bring it into the text field by button \"Edit\". - When the intended file address is composed, hit the Return key in the text field or click the \"Output drive/image\" button. - If the information field in the \"Output drive/image\" line begins by \"appendable\" or \"closed\" then you addressed an existing data file. Warning: Applying the \"Blank\" button to it would damage its content ! You probably do not want this in this special use case. - Go to the \"Insert from disk:\" line in the lower area. Continue like in the above description for CD, DVD, and BD media. ----------------------------------------------------------------------------- Burn an image data file from hard disk onto CD, DVD or BD - Click the \"Scan for drives\" button in the middle area. - Select a drive and click the \"Pick output drive\" button. - If the information field in the \"Output drive/image\" line begins by \"appendable\" or \"closed\" and if the medium is CD-RW, DVD-RW, DVD+RW, or BD-RE then click the \"Blank\" button to erase the old data. (Blanking of \"DVD-RW sequential recording\" will last very long.) - Go to the text field beside the \"Burn image file:\" button and toggle the address of the image file. Or click on the \"/\" button to the right of the field to get a file browser. - Hit the Return key in the text field or double click on a name in the browser to initiate the burn run. Confirm in the \"yes/no\" window that pops up. - When the \"busy\" field displays \"ready\" again, you may click \"Eject\". Desktop drives should then put out the tray with the medium. ----------------------------------------------------------------------------- Add more data to an appendable medium or to an ISO image data file - Like above, \"Scan for drives\" but click button \"Pick drive for both roles\" in order to load the directory tree of the existing ISO filesystem. For an ISO image data file, bring its name into the input fields of both lines \"Input drive/image\" and \"Output drive/image\" and activate it by clicking both buttons or hitting the Return key in both fields. You should now see in both info fields texts which begin by \"appendable\". - Go to the \"Insert from disk:\" line in the lower area. Use the means described in the first use case to add more directories or data files. - If you are interested in \"Delete\" or \"Move to:\" buttons in the bottom line of the GUI: Click them by the rightmost mouse button to see their help texts. - When all intended changes are done: Click \"Write ISO session\" and confirm in the \"yes/no\" window. ----------------------------------------------------------------------------- Extract a directory tree from an ISO filesystem to hard disk - Like above, \"Scan for drives\" but click button \"Pick input drive\" in order to load the directory tree of the existing ISO filesystem. For an ISO image data file, bring its name into the input field of the line \"Input drive/image\". You should now see in its info field a text which begins by \"appendable\" or \"closed\". - Go to the \"ISO directory:\" line and list box in the lower area and select the directory or file you want to copy to hard disk. - To get to see the desired file items, either toggle the address of their parent directory into the text field and hit Return, or double click items to open them as directories, or click the \"/\" button to get a file browser. Select the item in the list box of the main window by a single click. - Go to the \"Extract to disk:\" line and choose the target address on disk. Either toggle in the address of the hard disk directory, or click on the \"/\" button to the right of the text field to get a file browser. - Hit the Return key in the text field or double click on a name in the browser to initiate the extraction run. If a \"yes/no\" window pops up, consider well whether you are up to shooting your own foot right now. Enable the \"Overwrite hard disk files\" switch only if you are really sure that the files from ISO are better than the ones on hard disk. ----------------------------------------------------------------------------- xorriso-tcltk is mainly a proof of concept for a frontend that operates xorriso in dialog mode. It demonstrates some of xorriso's multi-session features with ISO 9660 filesystems on optical media (CD, DVD, BD) or in disk files. Dependencies: xorriso, Tcl language, Tk GUI toolkit, optionally Tcl/Tk package BWidget Copyright (C) 2012 - 2016 Thomas Schmitt <scdbackup@gmx.net>, libburnia-project.org Provided under BSD license: Use, modify, and distribute as you like." } if {$what == "End"} { return \ "The \"End\" button leads to the end of frontend and xorriso process." } if {$what == "version"} { return \ "The field between \"End\" button and ready/busy field displays the version of the serving xorriso program. xorriso is a program which copies file objects from POSIX compliant filesystems into Rock Ridge enhanced ISO 9660 filesystems and allows session-wise manipulation of such filesystems. It can load the management information of existing ISO images and it writes the session results to optical media or to filesystem objects. Vice versa xorriso is able to restore file objects from ISO 9660 filesystems. xorriso-tcltk-$own_version is mainly a proof of concept for a frontend that operates xorriso in dialog mode. It exercises several fundamental gestures of communication: - connecting via two pipes - sending commands - receiving replies - inquiring the xorriso message sieve - using the xorriso parsing service Note that any other language than Tcl/Tk could be used, if it only can do i/o via standard input and standard output or via named pipes. Further it has to perform integer arithmetics and string manipulations. And, well, a graphical widget set would be nice." } if {$what == "Refresh disp"} { return \ "The \"Refresh disp\" button causes several text fields and list boxes to update their display after manually transmitted commands may have changed the state of drives or ISO model." } if {$what == "ready/busy"} { return \ "The ready/busy field indicates whether a xorriso command is being executed and the frontend is still waiting for its reply messages." } if {$what == "Command:"} { return \ "The \"Command:\" field can be used to send commands to xorriso. See the manual page of xorriso for its concepts and commands. Normally the other GUI elements will emit xorriso commands for you. This input field is presented only to make accessible those features of xorriso which are not covered by the GUI. Use the \"Refresh disp\" button to update the display after you have manually transmitted commands." } if {$what == "Script/Log"} { return \ "The \"Script/Log\" menu controls logging of xorriso commands and replies. The \"Log command script\" switch controls whether the essential xorriso commands of the GUI session shall be written to the end of a script file on hard disk. Not written will be the commands by which the GUI inspects the xorriso state, but only those which set up that state and those which get sent via the \"Command:\" field. Commands -osirrox and -extract will be logged only as comments. The \"Log non-essential commands\" switch controls whether all commands shall be logged if \"Log command script\" is enabled. Commands -msg_op \"parse\" and -msg_op \"parse_bulk\" will be logged only as comments. The item \"Set log script address\" pops up a file tree browser window which asks for the target of appending to script. Address \"-\" means standard error. Else it must not yet exist or be a writable data file. The \"Log pipes\" switch controls whether all xorriso commands and replies shall be logged to standard error or to the file that has been given with program argument --pipe_log_file. Caution: This log is verbose. The item \"Set pipe log address\" pops up a file tree browser window which asks for the target of pipe logging . Address \"-\" means standard error. Else it must not yet exist or be a writable data file. The item \"Execute command script\" executes the commands in a script file that should stem from \"Log command script\". At least it must begin by this line: # xorriso-tcltk command log script Be aware that xorriso will slavishly execute those commands. Better check in advance whether the content of the script file is what you expect. See man xorriso for the meaning of the commands. The \"Allow extract to disk\" switch controls whether commands like -extract are allowed in command scripts. If disabled, then command -osirrox is used to temporarily block those commands (unless the script unblocks itself, which would be nasty behavior). The item \"Permanently ban extraction\" disables -extract irrevocably for scripts and GUI alike." } if {$what == "message box"} { return \ "The message box displays commands sent to xorriso and messages received from xorriso. Many commands which are emitted by the GUI will hide themselves and their replies from this display. All event messages with severity WARNING or higher will show up, nevertheless." } if {$what == "Recent problem:"} { return \ "The \"Recent problem:\" field shows the most severe event message that occurred during the execution of the most recent command. It also displays the most recent problem message from the frontend program itself. Several commands emitted by the GUI will not clear this display. But any manually transmitted command and the major GUI gestures will do. " } if {$what == "Worst problem:"} { return \ "The \"Worst problem:\" field shows the most severe event message that occurred since last time the \"Clear\" button was hit. It will not clear automatically." } if {$what == "Clear"} { return \ "The \"Clear\" button removes the message from the \"Worst problem:\" field." } if {$what == "Scan for drives"} { return \ "The \"Scan for drives\" button executes command -devices and puts the list of found optical drives into the box beside the button. Scanning should be done before any ISO image manipulations because xorriso has to give up its acquired drives in order to perform the scan run. To become visible and to be usable, the drives have to offer rw-permission to the user of this program. If drives do not show up, then consider to become superuser and to execute xorriso -devices Then apply chmod a+rw to the listed device files. (Consider to use finer means of permission granting for a permanent solution.)" } if {$what == "Pick input drive"} { return \ "The \"Pick input drive\" button executes command -indev and obtains some information about the medium status. This info is displayed in the \"Input drive/image\" line. Further it causes the display of the ISO image model to be updated. The medium in the input drive must be blank or contain a valid ISO 9660 filesystem. Choosing an input drive causes a root directory to be created in the ISO model of xorriso. If there is a valid ISO filesystem in the input drive then its directory tree gets loaded underneath that model root directory. The input drive may also be a data file on hard disk if that file contains an ISO 9660 filesystem image. See the \"Input drive/image\" button." } if {$what == "Pick output drive"} { return \ "The \"Pick output drive\" button executes command -outdev and obtains some information about the medium status. This info is displayed in the \"Output drive/image\" line. The output drive may be empty or loaded with a medium, that may be blank, appendable or closed. It is usable for writing only if there is a medium inserted which is either blank or appendable. Button \"Blank\" can bring appendable or closed media into blank state. The output drive may also be a data file on hard disk. See field \"Output drive/image\"." It is considered appendable if it contains an ISO 9660 filesystem image. It is considered blank if it is empty or marked as blank by button \"Blank\". It is considered closed if it contains other data." } if {$what == "Pick drive for both roles"} { return \ "The \"Pick drive for both roles\" button executes command -dev and obtains some information about the medium status. This info is displayed in the \"Input drive/image\" line and in the \"Output drive/image\" line. Further it causes the display of the ISO image model to be updated. The medium in the drive must be blank or contain a valid ISO 9660 filesystem. Else the drive will only be acquired as output drive. This drive configuration is the most usual one with xorriso. It loads an eventual ISO image, allows to manipulate it by insertion, deletion, and renaming. When this is done, the changes get written to the drive via button \"Write ISO session\". The drive may also be a data file on hard disk. See the fields beside the \"Input drive/image\" and \"Output drive/image\" buttons. A file is considered appendable if it contains an ISO 9660 filesystem image. It is considered blank if it is empty or marked as blank by button \"Blank\". It is considered closed if it contains other data." } if {$what == "Give up drives"} { return \ "The \"Give up drives\" button executes commands -indev \"\" -outdev \"\" and clears both \"... drive/image\" lines, as well as the ISO model." } if {$what == "Calm drives"} { return \ "The \"Calm drives\" button executes command -calm_drives which tells the acquired optical drives to stop spinning until the next drive activity gets triggered." } if {$what == "Rollback"} { return \ "The \"Rollback\" button executes command -rollback which drops all pending changes of the ISO model and reloads it from the input drive, if one is acquired." } if {$what == "drivelist"} { return \ "The box beside the \"Scan for drives\" button shows the optical drives which were found by the most recent scan run. A double-click on a drive item has the same effect as button \"Pick drive for both roles\". " } if {$what == "Input drive/image"} { return \ "The field beside the \"Input drive/image\" button displays the address of the input drive. You may edit this field. Clicking the button or pressing the Return key causes the execution of command -indev with the field content as drive address. Use this to load the model from an ISO image data file on hard disk. It is of course permissible that input image and output image are the same file. " } if {$what == "input drive info"} { return \ "The text beside the \"Input drive/image\" field displays the medium status of the input drive. It tells about the writability, the medium type, the number of ISO sessions, and the amount of readable data." } if {$what == "Eject (indev)"} { return \ "The \"Eject\" button beside the \"Input drive/image\" button executes command -eject \"in\"." } if {$what == "Output drive/image"} { return \ "The field beside the \"Output drive/image\" button displays the address of the output drive. You may edit this field. Clicking the button or pressing the Return key causes the execution of command -outdev with the field content as drive address. Use this to direct writing to an ISO image data file on hard disk. It is of course permissible that input image and output image are the same file. " } if {$what == "output drive info"} { return \ "The text beside the \"Output drive/image\" field displays the medium status of the output drive. It tells about the writability, the medium type, the number of ISO sessions, and the amount of free space." } if {$what == "Eject (outdev)"} { return \ "The \"Eject\" button beside the \"Output drive/image\" button executes command -eject \"out\"." } if {$what == "Blank"} { return \ "The \"Blank\" button executes command -blank \"as_needed\" on the output drive in order to make a re-usable medium or an ISO image data file writable from scratch. Genuine blanking applies only to CD-RW and DVD-RW. But xorriso emulates ISO 9660 multi-session on DVD+RW, DVD-RAM, formatted DVD-RW, BD-RE, as well as in ISO image data files on hard disk. On those media and pseudo media, blanking will be performed by a small write operation which invalidates their existing ISO filesystem. One-time writable media CD-R, DVD-R, DVD+R, and BD-R cannot be blanked." } if {$what == "Format"} { return \ "The \"Format\" button executes -format \"as_needed\". This only applies to real optical drives and is of interest only with DVD-RW or BD-R media, which both can be used formatted and unformatted. Other media types which mandatorily need formatting will be formatted by the write commands. Formatted DVD-RW media have the advantage of being overwritable and thus being quickly blankable while maintaining the capability for multi-session. Formatted BD-R can perform Defect Management, which is of questionable value." } if {$what == "Write ISO session"} { return \ "The \"Write ISO session\" executes command -commit, which writes a session with all pending changes to the output drive. The output drive must be either blank or it must be the same as the input drive. Writing the session is the last step in the course of creating a new ISO filesystem or an add-on session that expands or changes the ISO filesystem on the medium of the output drive. So first choose a drive, then insert files from hard disk or do other manipulations, and then click \"Write ISO session\" to let xorriso write the data to medium or ISO image file. " } if {$what == "Close"} { return \ "The \"Close\" switch controls whether command -close \"on\" is emitted with \"Write ISO session\" or whether -as cdrecord option -multi is omitted with \"Burn image file:\". Closed optical media cannot be written any more unless they get blanked, which is not possible with CD-R, DVD-R, DVD+R, and BD-R. " } if {$what == "TAO"} { return \ "The \"TAO\" switch controls whether an incremental MMC write type shall be enforced with write commands. See xorriso command -write_type. Normally xorriso will decide by medium status and job parameters which MMC write type to choose. Some drives at the edge of failure might work with the one write type while already failing with the other." } if {$what == "Defect Mgt"} { return \ "The \"Defect Mgt\" switch controls whether slow and error-prone drive internal check-reading shall be enabled when writing to formatted BD-R or BD-RE. See xorriso command -stream_recording." } if {$what == "Burn image file:"} { return \ "The \"Burn image file:\" button executes command -as \"cdrecord\" to burn a data file from hard disk onto the output drive. The address of the disk file is taken from the neighboring text field. If you do not plan to append further data to the medium, then consider to enable the \"Close\" switch. No input drive may be acquired. (Delete all characters from the field \"Input drive/image\" and hit Return to give up the input drive.) The medium in the drive must be blank. (It is well possible to burn image files to appendable media. But the image needs to be prepared for the address offset. Who can do that can as well use one of the command line tools for burning the result. E.g. xorriso -as cdrecord -v dev=/dev/sr0 -multi stream_recording=32s image.iso )" } if {$what == "Extract to disk:"} { return \ "The \"Extract to disk:\" button executes command -extract with the whole tree of the current ISO directory or with the selected items of the box underneath \"ISO directory:\". This copies the selected files or directory trees from the input drive to the address on hard disk which is given by the text field right of the button." } if {$what == "Browse tree"} { return "[tell_file_browser_help 0]" } if {$what == "Close (browse tree)"} { return \ "The \"Close\" button in the file browser closes the browser window without performing other actions." } if {$what == "Up (browse tree)"} { return \ "The \"Up\" button in the file browser brings you to the parent directory of the currently selected file tree item. The parent directory will be opened and become the selected item. All opened directory trees underneath the parent will be closed." } if {$what == "Down (browse tree)"} { return \ "The \"Down\" button in the file browser opens the directory underneath the currently selected file tree item. It has the same effect as clicking the \"+\" node of the selected item." } if {$what == "Accept (browse tree)"} { return \ "The \"Accept\" button in the file browser brings the single selected item from the file browser tree into effect with the associated text field. I.e. it hits the Return key of the field. It works as if the item had been double clicked." } if {$what == "Edit (browse tree)"} { return \ "The \"Edit\" button in the file browser brings the single selected item from the file browser tree into the associated text field. It does not hit the Return key of the field, but gives you the opportunity to edit the file address." } if {$what == "Browse disk (extract)"} { return \ "The \"/\" button in the \"Extract to disk:\" line pops up a file tree browser to select a target address in the hard disk filesystem. [tell_file_browser_help 1]" } if {$what == "Browse disk (burn image)"} { return \ "The \"/\" button beside the \"Burn image file\" field pops up a file tree browser to select a source address in the hard disk filesystem. [tell_file_browser_help 1]" } if {$what == "Browse disk (insert)"} { return \ "The \"/\" button beside the \"Insert from disk\" field pops up a file tree browser to select a source address in the hard disk filesystem. [tell_file_browser_help 1]" } if {$what == "Browse disk (indev)"} { return \ "The \"/\" button in the \"Input drive/image\" line pops up a file tree browser to select a source address in the hard disk filesystem. [tell_file_browser_help 1]" } if {$what == "Browse disk (outdev)"} { return \ "The \"/\" button in the \"Output drive/image\" line pops up a file tree browser to select a source address in the hard disk filesystem. [tell_file_browser_help 1]" } if {$what == "Browse ISO (isodir)"} { return \ "The \"/\" button in the \"ISO directory\" line pops up a file tree browser to select the current directory in the ISO filesystem model. [tell_file_browser_help 1]" } if {$what == "Browse ISO (move target)"} { return \ "The \"/\" button in the \"Selection:\" line pops up a file tree browser to select the current directory in the ISO filesystem model. [tell_file_browser_help 1]" } if {$what == "Browse disk (dummy)"} { return \ "Normally this button would start a file browser to select a file or directory on hard disk. But the browser cannot be displayed because Tcl/Tk package \"BWidget\" is not loaded." } if {$what == "Browse ISO (dummy)"} { return \ "Normally this button would start a file browser to select a file or directory in the ISO model. But the browser cannot be displayed because Tcl/Tk package \"BWidget\" is not loaded." } if {$what == "Underneath (extract)"} { return \ "The \"Underneath\" switch controls the effective hard disk target address of an item if the address in the \"Extract to disk:\" field points to a directory. If \"Underneath\" is enabled, then the file object from the ISO filesystem will be copied to its name underneath the hard disk directory. If \"Underneath\" is disabled then an ISO directory tree item will be merged with the disk directory tree at the given address. Example: Selected are \"/iso_dir\" and \"/iso_file\". Address for hard disk is \"/tmp/from_iso\". Switch \"Selected\" is enabled. \"Underneath\" enabled causes commands: -extract /iso_dir /tmp/from_iso/iso_dir -extract /iso_file /tmp/from_iso/iso_file \"Underneath\" disabled: -extract /iso_dir /tmp/from_iso -extract /iso_file /tmp/from_iso The last command will fail because /tmp/from_iso already exists as directory." } if {$what == "Selected (extract)"} { return \ "The \"Selected\" switch controls whether the whole current ISO directory, or only the selected items shall be copied to hard disk. " } if {$what == "Overwriting:"} { return \ "The \"Overwriting\" menu bundles several switches which control whether existing files or directories may be overwritten. The frontend program will only detect the most obvious name collisions, but xorriso will reliably refuse to overwrite files if this is banned. ---------------------------------------------------------------------------- The \"Overwrite ISO files\" switch controls whether existing files may be overwritten in the ISO image. See xorriso command -overwrite \"nondir\". ---------------------------------------------------------------------------- The \"Overwrite ISO dirs\" switch controls whether it is allowed to replace an ISO directory by a another file. See xorriso command -overwrite \"on\". ---------------------------------------------------------------------------- The \"Overwrite hard disk files\" switch controls whether existing files may be overwritten by extraction to hard disk. See xorriso command -overwrite \"on\". This is DANGEROUS, of course, but comes in handy with restoring of backups. ---------------------------------------------------------------------------- The \"Enforce disk dir write access\" switch enables the -osirrox options \"auto_chmod_on\" and \"sort_lba_on\" which influence file extraction. \"auto_chmod_on\" allows xorriso to give itself temporariy w-permission to all disk directories which are owned by the xorriso user. This is DANGEROUS, of course, but comes in handy with restoring of backups. Option \"sort_lba_on\" reduces head-moves of optical drives and thus can speed up extraction substantially. It is bound to \"auto_chmod_on\" because else it might get into trouble when restoring ISO directories which offer no w-permission." } if {$what == "Permissions:"} { return \ "The \"Permissions\" menu allows to choose a global policy to adjust the access permissions of the files when an ISO session gets written. The default policy \"as is\" leaves the permissions as they are. Usually they have been imported from hard disk or from a loaded ISO image. xorriso commands -chmod , -chmod_r, and -find ... -exec chmod -- may be used to perform permission manipulations. Policy \"readable\" adds read permission to all kinds of files and search permission to all directories. Policy \"readonly\" sets the permissions of all kinds of files to read-only. Directories get added search permission. Policy \"mkisofs -r\" does what option -r of program mkisofs does: User id and group id become 0, all r-permissions get granted, all w denied. If there is any x-permission, then all three x get granted. s- and t-bits get removed. " } if {$what == "Refresh avail:"} { return \ "The \"Refresh avail:\" button triggers command -tell_media_space. It makes a time consuming exact prediction of the free space on the medium in the output drive. For this purpose, the size of an ISO session with the pending changes is computed. With image files rather than real optical drives, the free space of the hosting filesystem is displayed." } if {$what == "Insert from disk:"} { return \ "The \"Insert from disk:\" button executes command -map with the disk file address that is given by the text field right to the button. This inserts files or directory trees into the ISO image model and schedules them for being copied with the next \"Write ISO session\" run. The switches \"Underneath\" and \"Selected\" control what ISO address the inserted files shall have. You may use buttons \"Delete\" and \"Move to:\" for further adjustments. " } if {$what == "Underneath (insert)"} { return \ "The \"Underneath\" switch controls the effective ISO target address if the address in the \"Insert from disk:\" field points to a hard disk directory. If \"Underneath\" is enabled, a directory from disk will not be unpacked to its single files but be put underneath the target address by its own leaf name." If \"Underneath\" is disabled then the directory itself will not show up in the ISO image but only its files and sub directories will do." } if {$what == "Selected (insert)"} { return \ "If the switch \"Selected\" is enabled, then the given disk file or tree will be inserted at or underneath the only selected item in the box underneath \"ISO directory:\"." } if {$what == "ISO directory:"} { return \ "The current ISO directory shall be used to navigate in the ISO image model of xorriso. By default it is the target of file insertions and the source of file extractions. The text field in the \"ISO directory:\" line displays the current ISO directory and can be used to toggle its path directly. Hitting the Return key causes the current directory to change and the display in the box underneath to be refreshed. It is possible to choose the ISO directory by double-clicking an item in the box underneath the \"ISO directory:\" line. " } if {$what == "Up"} { return \ "The \"Up\" buttons move the current ISO directory one directory level up." } if {$what == "Verify"} { return \ "The \"Verify\" button executes -md5_check_r \"SORRY\" with the current ISO directory. This reads the content of all data files which are underneath the current ISO directory and which have MD5 checksums in the ISO image. ISO images bear MD5 checksums for each data file if they were produced by xorriso with -md5 \"on\" or -for_backup. This frontend enables this feature on startup." } if {$what == "isolist"} { return \ "The list box underneath the \"ISO directory:\" line displays the files in the current ISO directory. One or more item can be selected and play a role with extraction or insertion of files. Most of the buttons underneath the box operate on the selected items unconditionally." } if {$what == "Selection:"} { return \ "The ISO selection consists of the items which are selected in the list box above the \"Selection:\" line. If the respective \"Selected\" switches are enabled, then the ISO selection is source of file extraction and target of file insertion. In any case it is the old name of the \"Move to:\" button, the victim of the \"Delete\" button, and the subject of the \"Verify\" button." } if {$what == "Verify (selection)"} { return \ "The \"Verify\" button in the \"Selection:\" line executes command -md5_check_r \"SORRY\" with each of the selected items. This reads the content of all data files which are selected or underneath selected directories and which have MD5 checksums in the ISO image. ISO images bear MD5 checksums for each data file if they were produced by xorriso with -md5 \"on\" or -for_backup. This frontend enables this feature on startup." } if {$what == "Delete"} { return \ "The \"Delete\" button executes command -rm_r with each of the selected items. This removes the affected files and directory trees from the ISO model. They will not show up in the directory tree of the next session that is written via \"Write ISO session\". Nevertheless they will stay present in earlier sessions beginning from the session where they were inserted." } if {$what == "Move to:"} { if {$use_command_move == 0} { return \ "The \"Move to:\" button uses command -mv to move each of the selected items to the address that is given by the text field right to the button. If this address points to an existing ISO directory, then the items will be moved underneath that directory and keep their leaf names. Else there may be only one selected item which will be renamed to the given address." } else { return \ "The \"Move to:\" button uses command -move to rename each of the selected items to the address that is given by the text field right to the button." } } if {$what == "Make dir"} { return \ "The \"Make dir\" button executes command -mkdir with the address in the text field to its left (the same as used by \"Move to:\"). Useful to create a target directory before moving the selection." } if {$what == "rename and mkdir target"} { return \ "The text field between the \"Move to:\" button and the \"Make dir\" button serves both buttons by providing the target address for renaming or directory creation, respectively. If you hit the Return key in this field, it will trigger \"Mode to:\"." } if {$what == "yes to all"} { return \ "The \"yes to all\" button appears in the yes/no window if a GUI action is about to overwrite a file object and more such overwrite situations are to be expected. If the button is clicked, then all further yes/no questions of that GUI action will be answered automatically with yes. [about_help_for_yesno]" } if {$what == "no to all"} { return \ "The \"no to all\" button appears in the yes/no window if a GUI action is about to overwrite a file object and more such overwrite situations are to be expected. If the button is clicked, then all further yes/no questions of that GUI action will be answered automatically with no. [about_help_for_yesno]" } if {$what == "Continue"} { return \ "The \"Continue\" button appears in the notification windows which tell about a failed or rejected GUI action. --------------------------------------------------------------------------- It is impossible to trigger any further GUI action while the notification window is displayed. You either have to click the \"Continue\" button or hit the Return key. You cannot even close this help window before you did that." } if {$what == "yes/no"} { return \ "The \"yes\" and \"no\" buttons appear in the confirmation window which tells about a potentially dangerous GUI action and demands a user decision whether to really perform this action. [about_help_for_yesno]" } return "--- No help text found for topic '$what'" } # Tell the general help text of the file browser. # proc tell_file_browser_help {with_separator} { global have_bwidget set text "" if {$with_separator == 1} { set text \ "-------------------------------------------------------------------------\n\n" } if {$have_bwidget == 1} { set text \ "${text}The file tree browser presents to you a directory tree and lets you bring into effect one of the file addresses in that tree.\n" } else { set text \ "${text}Normally the file tree browser would present to you a directory tree and let you bring into effect one of the file addresses in that tree. But the tree view cannot be displayed because Tcl/Tk package \"BWidget\" is not loaded. -------------------------------------------------------------------------\n" } set text "${text} The bottom line of the browser window tells the associated text field in the GUI. E.g. \"ISO directory:\". Left of this label is a copy of that associated text field. You may edit its content and bring it into effect by hitting the Return key.\n" if {$have_bwidget == 1} { set text "${text} In the tree display click on the \"+\" or \"-\" nodes to open or close directories, respectively. Double click on an item to bring it into effect with the associated text field. I.e. double clicking also hits the Return key in that field.\n" } set text "${text} The \"Accept\" button does the same with the selected item.\n" if {$have_bwidget == 1} { set text "${text} The \"Edit\" button brings the selected item into the text field without hitting the Return key. So you may edit the name before hitting Return yourself. The \"Up\" button brings you to the parent directory of the selected item. The \"Down\" button works like clicking the \"+\" node of the selected item.\n" } set text "${text} The \"Help\" button displays this help text window. The \"Close\" button closes the browser window.\n" } # Tell about pecliarity of help window triggered by yes/no window proc about_help_for_yesno {} { return \ "--------------------------------------------------------------------------- It is impossible to trigger any further GUI action while the confirmation window is displayed. You have to click one of the buttons in that window. You cannot even close this help window before you clicked one of the buttons." } # ------- Misc helper procedures ------- # Equip a text with quotation marks so that xorriso will consider it as # a single word. # proc make_text_shellsafe {text} { set result "'" set rest $text while {[string length $rest]} { set idx [string first "'" $rest] if {$idx == -1} { set result "$result$rest" break } else { if {$idx > 0} { set result "$result[string range $rest 0 [expr $idx - 1]]" } set result "$result'\"'\"'" if {$idx == [expr [string length $rest] - 1]} { break } set rest [string range $rest [expr $idx + 1] end] } } set result "$result'" } # Count the number of newline characters in text. # proc count_newlines {text} { set rest $text set count 0 while {[string length $rest]} { set idx [string first "\n" $rest] if {$idx == -1} { break } else { set count [expr $count + 1] if {$idx == [expr [string length $rest] - 1]} { break } set rest [string range $rest [expr $idx + 1] end] } } return $count } # Append name to dir so that the result is a path to name under dir. # proc combine_dir_and_name {dir name} { set has_slash 0 if {$name == ""} { return $dir } if {[string range $name 0 0] == "/"} { incr has_slash } if {[string last "/" $dir] == [expr [string length $dir] - 1] && $dir != ""} { incr has_slash 1 } if {$has_slash == 2} { return "$dir[string range $name" 1 end]" } if {$has_slash == 1} { return "$dir$name" } return "$dir/$name" } # Force the content of variable isodir_adr to be an absolute address # proc normalize_isodir_adr {} { global isodir_adr if {$isodir_adr == ""} { set isodir_adr "/" } if {[string range $isodir_adr 0 0] != "/"} { set isodir_adr "/$isodir_adr" } } # Inspect path whether one of its components is in isodir_adr # proc path_touches_isodir {path} { global isodir_adr normalize_isodir_adr set cmp_start 0 if {$isodir_adr == "/"} { set cmp_start 1 } if {[string range $path 0 0] != "/"} { if {[string first "/" $path] == -1} { return $path } else { return [file dirname $path] } } set l [expr {[string length $isodir_adr] - $cmp_start}] if {[string length $path] < [expr {$l + 2}]} { return "" } if {$l > 0} { if {[string range $path $cmp_start [expr {$l - 1}]] != \ [string range $isodir_adr $cmp_start end]} { return "" } } if {[string range $path $l $l] != "/"} { return "" } set subpath [string range $path [expr {$l + 1}] end] set slash [string first "/" $subpath] if {$slash == -1} { return $subpath } if {$slash == 0} { return "" } return [string range $subpath 0 [expr {$slash - 1}]] } # Compare two severity names by help of the severity list that was obtained # from xorriso via proc inquire_severity_list. # proc compare_sev {sev1 sev2} { global xorriso_severity_list set idx1 [lsearch -exact $xorriso_severity_list $sev1] set idx2 [lsearch -exact $xorriso_severity_list $sev2] if {$idx1 < $idx2} {return -1} if {$idx1 > $idx2} {return 1} return 0 } # Write a text to the pipe log # proc debug_log_puts {text} { global debug_logging debug_log_conn if {$debug_logging == 1} { puts $debug_log_conn $text flush $debug_log_conn } } # End program and return the given exit value. # proc central_exit {value} { exit $value } # Start a xorriso process which will in return launch another frontend # process. This is necessary until i learned how to create a pair of pipes # and to fork in Tcl. # proc start_xorriso {} { global argv0 argv set self "" if {[string first "/" $argv0] != -1} { set self $argv0 } if {$self == ""} { set self "/usr/bin/xorriso-tcltk" if {[file executable $self] == 0} {set self ""} } if {$self == ""} { set self "/usr/local/bin/xorriso-tcltk" if {[file executable $self] == 0} {set self ""} } if {$self == ""} { catch { set conn [open "|which xorriso-tcltk" r] set self [gets $conn] close $conn } } if {$self == ""} { catch { set conn [open "|sh -c \"type -p xorriso-tcltk\"" r] set self [gets $conn] close $conn } } if {$self == ""} { puts stderr "$argv0 :\n Cannot locate address of script xorriso-tcltk in filesystem.\n" puts stderr "You will have to use --stdio or --named_pipes." puts stderr "See $argv0 --help\n" central_exit 1 } # eval is used to split $argv into single words eval exec xorriso -launch_frontend "\"$self\"" --silent_start --stdio $argv -- 2>@stderr central_exit 0 } # Print a startup message to stderr if not the first argument is --silent_start # proc yell_xorriso_tcltk {} { global argv own_version if {[llength $argv] > 0} { if {[lindex $argv 0] == "--silent_start"} {return ""} } puts stderr "xorriso-tcltk $own_version : Proof of concept for GUI frontends of xorriso\n" } # Log a command (if enabled) # proc log_command {cmd essential} { global cmd_log_conn cmd_logging_mode cmd_logging_all recent_cd_path if {$cmd_logging_mode < 1} {return ""} if {$essential <= 0} { if {$cmd_logging_all <= 0} {return ""} } else { # Leave logging to non-essential call which will come soon after if {$cmd_logging_all > 0} {return ""} } if {[string first "-cd " $cmd] == 0} { set path [string range $cmd 4 end] if {$path == $recent_cd_path && $cmd_logging_all <= 0} {return ""} set recent_cd_path $path } if {$cmd_log_conn == ""} { effectuate_command_logging 0 if {$cmd_logging_mode < 1} {return ""} } set prefix "" if {$cmd_logging_mode == 1} { if {[string first "-osirrox" $cmd] != -1 || \ [string first "-extract" $cmd] != -1} { set prefix "# " } } if {[string first "-msg_op parse" $cmd] != -1} { set prefix "# " } puts $cmd_log_conn $prefix$cmd flush $cmd_log_conn } # Start command logging # Called by setup_by_args and by the "Script/Log" menu. # (target == "." and mode == -1 preserve the current state.) # proc start_command_logging {target mode} { global cmd_log_conn cmd_logging_mode msglist_running cmd_log_target set is_stderr 0 if {$cmd_log_target == "" || $cmd_log_target == "-" || \ $cmd_log_conn == "stderr"} {set is_stderr 1} set errmsg "" if {$target != "." && $cmd_log_conn != "" && $target != $cmd_log_target && \ $is_stderr == 0} { catch "close $cmd_log_conn" set cmd_log_conn "" } set ret 0 if {$cmd_log_conn == "" || $is_stderr == 1} { if {$target == "-" || $target == "" || $target == "."} { set cmd_log_conn stderr } else { set ret [catch {set cmd_log_conn [open $target a]} errmsg] } if {$target != "."} { set cmd_log_target $target } } if {$ret == 0 && $mode >= 0} { set cmd_logging_mode $mode } if {$ret == 1} { set msg "xorriso-tcltk : SORRY : Failed to open command log script [make_text_shellsafe $target] :\n$errmsg" if {$msglist_running == 1} { xorriso_tcltk_errmsg $msg } else { puts stderr $msg } set cmd_logging_mode 0 return 0 } if {$mode > 0} { puts $cmd_log_conn "# xorriso-tcltk command log script" puts $cmd_log_conn [xorriso_loggable_init_cmds] flush $cmd_log_conn } return 1 } # Start communications pipe logging # Called by setup_by_args and by the "Script/Log" menu. # (target == "." and mode == -1 preserve the current state.) # proc start_debug_logging {target mode} { global debug_log_conn debug_log_file debug_logging msglist_running set is_stderr 0 if {$debug_log_file == "" || $debug_log_file == "-" || \ $debug_log_conn == "stderr"} {set is_stderr 1} set errmsg "" if {$target != "." && $debug_log_conn != "" && \ $target != $debug_log_file && $is_stderr == 0} { catch "close $debug_log_conn" set debug_log_conn "" } set ret 0 if {$debug_log_conn == "" || $is_stderr == 1} { if {$target == "-" || $target == "" || $target == "."} { set debug_log_conn stderr } else { set ret [catch {set debug_log_conn [open $target a]} errmsg] } if {$target != "."} { set debug_log_file $target } } if {$ret == 0 && $mode >= 0} { set debug_logging $mode } if {$ret == 1} { set msg "xorriso-tcltk : SORRY : Failed to open pipe log [make_text_shellsafe $target] :\n$errmsg" if {$msglist_running == 1} { xorriso_tcltk_errmsg $msg } else { puts stderr $msg } return 0 } return 1 } proc execute_script {close_window} { global execute_script_conn execute_script_adr browse_disk_window_is_active global osirrox_allowed script_with_osirrox cmd_logging_mode cmd_log_target global highest_cmd_sev if {$close_window == 1 && $browse_disk_window_is_active == 1} { destroy_browse_disk .browse_disk_window } set n1 [file normalize $execute_script_adr] set n2 [file normalize $cmd_log_target] if {$n1 == $n2 && $cmd_logging_mode > 0} { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : You first have to disable command script logging before using the log script" return "" } set errmsg "" set ret [catch {set execute_script_conn [open $execute_script_adr r]} errmsg] if {$ret != 0} { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : Failed to open command script [make_text_shellsafe $execute_script_adr] :\n$errmsg" return "" } set line "" set ret [gets $execute_script_conn line] if {$ret < 0 || $line != "# xorriso-tcltk command log script"} { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : Given file does not look like a xorriso command log script" close $execute_script_conn return "" } # >>> ??? Show script if {[window_yesno "Really perform the xorriso commands in file\n\n[make_text_shellsafe $execute_script_adr]\n\n?"] != 1} { close $execute_script_conn return "" } if {$script_with_osirrox != 1} { send_silent_cmd "-osirrox blocked" } while {1} { set ret [gets $execute_script_conn line] if {$ret < 0} { break } if {$line == "" || [string first "#" $line] == 0} { continue } reset_highest_cmd_sev send_loggable_cmd $line if {[compare_sev $highest_cmd_sev "FAILURE"] >= 0} { xorriso_tcltk_errmsg "xorriso-tcltk : SORRY : Encountered problem event of severity '$highest_cmd_sev'.\nScript execution aborted." break } } close $execute_script_conn if {$script_with_osirrox != 1} { send_silent_cmd "-osirrox unblock" } } # Convert newline into \n # proc escape_newline {text backslash_too} { if {$backslash_too == 0} { return [string map [list "\n" "\\n"] $text] } return [string map [list "\n" "\\n" "\\" "\\\\"] $text] } # -------- start living proc setup_by_args {argv0 argv} { global cmd_pipe_adr reply_pipe_adr main_window_geometry click_to_focus global have_bwidget cmd_conn reply_conn geometry stdout stdin global osirrox_allowed cmd_logging_all use_command_move # wish normally eats the -geometry option and puts the result into $geometry catch {set main_window_geometry $geometry} set connection_defined 0 set pipe_logging 0 set script_logging 0 set loop_limit [llength $argv] for {set i 0} {$i < $loop_limit} {incr i} { set ok "0" set opt [lrange $argv $i $i] if {$opt == "--help"} { set ok "1" print_usage $argv0 central_exit 0 } if {$opt == "--silent_start"} { set ok "1" } if {$opt == "--stdio"} { set ok "1" set connection_defined 1 } if {$opt == "--named_pipes"} { set ok "1" incr i set cmd_pipe_adr [lrange $argv $i $i] incr i set reply_pipe_adr [lrange $argv $i $i] if {$cmd_pipe_adr != "" && $reply_pipe_adr != "" && $cmd_pipe_adr != "-" && $reply_pipe_adr != "-"} { init_frontend_named_pipes $cmd_pipe_adr $reply_pipe_adr } set connection_defined 1 } if {$opt == "--geometry" || $opt == "-geometry"} { set ok "1" # Just in case -geometry does not get eaten by wish incr i set main_window_geometry [lrange $argv $i $i] set give_geometry [lrange $argv $i $i] } if {$opt == "--click_to_focus"} { set ok "1" set click_to_focus "1" } if {$opt == "--auto_focus"} { set ok "1" set click_to_focus "0" } if {$opt == "--pipe_log_file"} { set ok "1" incr i set pipe_log_name [lrange $argv $i $i] # postpone actual log start until start_xorriso has been passed set pipe_logging 1 } if {$opt == "--script_log_file"} { set ok "1" incr i set script_log_name [lrange $argv $i $i] # postpone actual log start until start_xorriso has been passed set script_logging 1 } if {$opt == "--script_log_all_commands"} { set ok "1" set cmd_logging_all 1 } if {$opt == "--no_extract"} { set ok "1" set osirrox_allowed 0 } if {$opt == "--no_bwidget"} { set ok "1" set have_bwidget "-1" } if {$opt == "--use_command_move"} { set ok "1" set use_command_move 1 } if {$opt == "--use_command_mv"} { set ok "1" set use_command_move 0 } if {$ok == 0} { puts stderr "$argv0 : Unknown option '$opt'" print_usage $argv0 central_exit 1 } } if {$connection_defined == 0} { start_xorriso } if {$cmd_pipe_adr == "" || $reply_pipe_adr == "" || $cmd_pipe_adr == "-" || $reply_pipe_adr == "-"} { set cmd_conn stdout set reply_conn stdin } if {$pipe_logging == 1} { set ret [start_debug_logging $pipe_log_name 1] if {$ret <= 0} { puts stderr \ "$argv0 : Cannot open --pipe_log_file '$pipe_log_name' for writing" central_exit 2 } } if {$script_logging == 1} { set ret [start_command_logging $script_log_name 1] if {$ret <= 0} { puts stderr \ "$argv0 : Cannot open --script_log_file '$script_log_name' for writing" central_exit 2 } } if {$main_window_geometry != ""} { wm geometry . $main_window_geometry } } yell_xorriso_tcltk setup_by_args $argv0 $argv check_xorriso_version setup_xorriso init_gui display_busy 0 refresh_state #!/bin/sh # Copyright (C) 2015 # Thomas Schmitt <scdbackup@gmx.net>, libburnia-project.org # Provided under BSD license: Use, modify, and distribute as you like. # set -x # ---------------------------- functions --------------------------- usage() { echo >&2 echo "usage: $0 "'\' >&2 echo " [-xorriso path] id_string [-dev] iso_adr [xorriso_arguments ...]" >&2 echo >&2 echo " This script looks for named pipe" >&2 echo ' /tmp/xorriso_stdin_pipe_${id_string}' >&2 echo " which is supposed to be connected to a xorriso process." >&2 echo " If not found, the stdin pipe and a stdout pipe get created" >&2 echo " and a xorriso dialog process gets started and connected." >&2 echo " Each character in id_string must match [-+:.,=@0-9A-Za-z]." >&2 echo " If iso_adr differs from the previous run with the same id_string," >&2 echo " then any changes on the previous ISO are committed as session" >&2 echo " before command -dev is performed to load the meta data of" >&2 echo " the newly addressed ISO." >&2 echo " After this is done, the optionally given xorriso_arguments" >&2 echo " are written into the stdin pipe from where xorriso will read" >&2 echo " them as commands and their parameters." >&2 echo >&2 } # Make filenames safe for transport by wrapping them in quotes and # escaping quotes in their text xorriso_esc() { echo -n "'" echo -n "$1" | sed -e "s/'/'"'"'"'"'"'"'/g" echo -n "'" } # Send one or more command lines to xorriso xorriso_send_cmd() { # $1 : the lines to send # >>> is it possible to have a timeout on echo ? if test -p "$cmd_pipe" then echo " $1" >"$cmd_pipe" else xorriso_is_running=0 return 1 fi } # Send command and wait for answer xorriso_cmd_and_result() { # $1: command line for xorriso # $2: if not empty, grep expression for stdout if test "$xorriso_is_running" = 0 then return 1 fi xorriso_send_cmd "$1" || return 1 if test -n "$2" then cat "$result_pipe" else grep "$2" <"$result_pipe" fi return 0 } # ------------------------------- main ----------------------------- # Argument interpreter if test "$#" -lt 2 then usage "$0" exit 1 fi xorriso=xorriso if test o"$1" = o"-xorriso" then xorriso="$2" shift 2 fi export xorriso_is_running=0 if test "$#" -lt 2 then usage "$0" exit 1 fi id_string=$(echo "$1" | sed -e 's/[^-+:.,=@0-9A-Za-z]/_/g' ) shift 1 # Ignore second argument -dev if test o"$1" = o"-dev" then shift 1 if test "$#" -lt 1 then usage "$0" exit 1 fi fi device="$1" shift 1 # Perform the action export cmd_pipe=/tmp/xorriso_stdin_pipe_$id_string export result_pipe=/tmp/xorriso_stdout_pipe_$id_string if test -p "$cmd_pipe" then xorriso_is_running=1 else xorriso_is_running=0 fi if test "$xorriso_is_running" = "0" then # xorriso is not started yet # Check for xorriso version which knows command -named_pipe_loop echo "Checking xorriso version ..." >&2 xorriso_version_req="1.3.2" version=$("$xorriso" -version | grep '^xorriso version' | sed -e 's/^xorriso version : //') smallest=$( (echo "$xorriso_version_req" ; echo "$version" ) | \ sort | head -1) if test "$smallest" = "$xorriso_version_req" then dummy=dummy else echo "$0 : FATAL : Need xorriso version >= $xorriso_version_req" >&2 echo "Found version: $version" >&2 exit 2 fi if mknod "$cmd_pipe" p then echo "Created named pipe for xorriso commands: $cmd_pipe" >&2 else echo "Failed to create named pipe for xorriso commands: $cmd_pipe" >&2 exit 3 fi if mknod "$result_pipe" p then echo "Created named pipe for xorriso result channel: $result_pipe" >&2 else echo \ "Failed to create named pipe for xorriso result channel: $result_pipe" >&2 if rm "$cmd_pipe" then echo "Removed named pipe for xorriso commands: $cmd_pipe" >&2 fi exit 3 fi echo "Starting xorriso process ..." >&2 "$xorriso" -abort_on NEVER -for_backup \ -named_pipe_loop cleanup:buffered "$cmd_pipe" "$result_pipe" "-" \ >&2 & # (stdout is redirected to stderr, in order not to keep a pipe waiting for # input from the still open stdout copy of the background process. # -named_pipe_loop will disconnect xorriso result channel from stdout.) xorriso_is_running=1 fi # Inquire current xorriso -dev xorriso_device=$(xorriso_cmd_and_result "-status -dev" "^-dev" | \ sed -e 's/^-dev //') if echo " $device" | grep "^ '" >/dev/null then quoted="$device" else quoted=$(xorriso_esc "$device") fi if test "$xorriso_device" = "$quoted" then dummy=dummy else # Inquire the need for a -commit command pending=$(xorriso_cmd_and_result "-changes_pending show_status" \ "^-changes_pending" \ | sed -e 's/^-changes_pending //') if test "$pending" = "yes" then if xorriso_cmd_and_result "-commit" then dummy=dummy else exit 1 fi fi # Now change ISO filesystem if xorriso_cmd_and_result "-dev $device" then xorriso_device="$device" else exit 1 fi fi test "$*" = "" && exit 0 if xorriso_cmd_and_result "$*" then dummy=dummy else exit 1 fi exit 0 prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libisoburn Description: Multi-session filesystem extension to libisofs, libburn. Version: @VERSION@ Requires: Libs: -L${libdir} -lisoburn Cflags: -I${includedir}/libisoburn /* cc -g -c \ -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 -D_LARGEFILE64_SOURCE \ burn_wrap.c */ /* libburn wrappers for libisoburn Copyright 2007 - 2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /* <<< A70929 : hardcoded CD-RW with fabricated -msinfo #define Hardcoded_cd_rW 1 #define Hardcoded_cd_rw_c1 12999 #define Hardcoded_cd_rw_nwA 152660 */ #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <sys/stat.h> #include <fcntl.h> #include <time.h> #include <regex.h> #ifndef Xorriso_standalonE #include <libburn/libburn.h> #include <libisofs/libisofs.h> #ifdef Xorriso_with_libjtE #include <libjte/libjte.h> #endif #else /* ! Xorriso_standalonE */ #include "../libisofs/libisofs.h" #include "../libburn/libburn.h" #ifdef Xorriso_with_libjtE #include "../libjte/libjte.h" #endif #endif /* Xorriso_standalonE */ #include "libisoburn.h" #include "isoburn.h" /* The global list of isoburn objects. Usually there is only one. */ extern struct isoburn *isoburn_list_start; /* in isoburn.c */ /* Default values for application provided msgs_submit methods. To be attached to newly acquired drives. Storage location is isoburn.c */ extern int (*libisoburn_default_msgs_submit) (void *handle, int error_code, char msg_text[], int os_errno, char severity[], int flag); extern void *libisoburn_default_msgs_submit_handle; extern int libisoburn_default_msgs_submit_flag; static int isoburn_emulate_toc(struct burn_drive *d, int flag); int isoburn_initialize(char msg[1024], int flag) { int major, minor, micro, bad_match= 0, no_iso_init= 0; /* First the ugly compile time checks for header version compatibility. If everything matches, then they produce no C code. In case of mismatch, intentionally faulty C code will be inserted. */ #ifdef iso_lib_header_version_major /* The minimum requirement of libisoburn towards the libisofs header at compile time is defined in libisoburn/libisoburn.h : isoburn_libisofs_req_major isoburn_libisofs_req_minor isoburn_libisofs_req_micro It gets compared against the version macros in libisofs/libisofs.h : iso_lib_header_version_major iso_lib_header_version_minor iso_lib_header_version_micro If the header is too old then the following code shall cause failure of libisoburn compilation rather than to allow production of a program with unpredictable bugs or memory corruption. The compiler messages supposed to appear in this case are: error: 'LIBISOFS_MISCONFIGURATION' undeclared (first use in this function) error: 'INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libisofs_dot_h_TOO_OLD__SEE_libisoburn_dot_h_AND_burn_wrap_dot_h' undeclared (first use in this function) error: 'LIBISOFS_MISCONFIGURATION_' undeclared (first use in this function) */ /* The indentation is an advise of man gcc to help old compilers ignoring */ #if isoburn_libisofs_req_major > iso_lib_header_version_major #define Isoburn_libisofs_dot_h_too_olD 1 #endif #if isoburn_libisofs_req_major == iso_lib_header_version_major && isoburn_libisofs_req_minor > iso_lib_header_version_minor #define Isoburn_libisofs_dot_h_too_olD 1 #endif #if isoburn_libisofs_req_minor == iso_lib_header_version_minor && isoburn_libisofs_req_micro > iso_lib_header_version_micro #define Isoburn_libisofs_dot_h_too_olD 1 #endif #ifdef Isoburn_libisofs_dot_h_too_olD LIBISOFS_MISCONFIGURATION = 0; INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libisofs_dot_h_TOO_OLD__SEE_libisoburn_dot_h_AND_burn_wrap_dot_c = 0; LIBISOFS_MISCONFIGURATION_ = 0; #endif #endif /* iso_lib_header_version_major */ /* The minimum requirement of libisoburn towards the libburn header at compile time is defined in libisoburn/libisoburn.h : isoburn_libburn_req_major isoburn_libburn_req_minor isoburn_libburn_req_micro It gets compared against the version macros in libburn/libburn.h : burn_header_version_major burn_header_version_minor burn_header_version_micro If the header is too old then the following code shall cause failure of cdrskin compilation rather than to allow production of a program with unpredictable bugs or memory corruption. The compiler messages supposed to appear in this case are: error: 'LIBBURN_MISCONFIGURATION' undeclared (first use in this function) error: 'INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libburn_dot_h_TOO_OLD__SEE_libisoburn_dot_h_and_burn_wrap_dot_h' undeclared (first use in this function) error: 'LIBBURN_MISCONFIGURATION_' undeclared (first use in this function) */ /* The indentation is an advise of man gcc to help old compilers ignoring */ #if isoburn_libburn_req_major > burn_header_version_major #define Isoburn_libburn_dot_h_too_olD 1 #endif #if isoburn_libburn_req_major == burn_header_version_major && isoburn_libburn_req_minor > burn_header_version_minor #define Isoburn_libburn_dot_h_too_olD 1 #endif #if isoburn_libburn_req_minor == burn_header_version_minor && isoburn_libburn_req_micro > burn_header_version_micro #define Isoburn_libburn_dot_h_too_olD 1 #endif #ifdef Isoburn_libburn_dot_h_too_olD LIBBURN_MISCONFIGURATION = 0; INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libburn_dot_h_TOO_OLD__SEE_libisoburn_dot_h_and_burn_wrap_dot_h = 0; LIBBURN_MISCONFIGURATION_ = 0; #endif #ifdef Xorriso_with_libjtE /* The minimum requirement of libisoburn towards the libjte header at compile time is the same as the one of a usable libisofs towards libjte. So the requirement is defined in libisofs/libisofs.h : iso_libjte_req_major , iso_libjte_req_minor , iso_libjte_req_micro */ /* The indentation is an advise of man gcc to help old compilers ignoring */ #if iso_libjte_req_major > LIBJTE_VERSION_MAJOR #define Libisofs_libjte_dot_h_too_olD 1 #endif #if iso_libjte_req_major == LIBJTE_VERSION_MAJOR && iso_libjte_req_minor > LIBJTE_VERSION_MINOR #define Libisofs_libjte_dot_h_too_olD 1 #endif #if iso_libjte_req_minor == LIBJTE_VERSION_MINOR && iso_libjte_req_micro > LIBJTE_VERSION_MICRO #define Libisofs_libjte_dot_h_too_olD 1 #endif #ifdef Libisofs_libjte_dot_h_too_olD LIBJTE_MISCONFIGURATION = 0; INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libjte_dot_h_TOO_OLD__SEE_libisofs_dot_h_AND_burn_wrap.c_c = 0; LIBJTE_MISCONFIGURATION_ = 0; #endif #endif /* Xorriso_with_libjtE */ /* End of ugly compile time tests (scroll up for explanation) */ msg[0]= 0; #ifdef Xorriso_with_libjtE libjte__version(&major, &minor, µ); sprintf(msg + strlen(msg), "libjte-%d.%d.%d ", major, minor, micro); if (libjte__is_compatible(LIBJTE_VERSION_MAJOR, LIBJTE_VERSION_MINOR, LIBJTE_VERSION_MICRO, 0)) { sprintf(msg+strlen(msg), "ok, "); } else { sprintf(msg + strlen(msg), "- TOO OLD -, need at least libjte-%d.%d.%d ,\n", LIBJTE_VERSION_MAJOR, LIBJTE_VERSION_MINOR, LIBJTE_VERSION_MICRO); bad_match= 1; no_iso_init= 1; /* iso_init() will fail anyway */ } #endif /* Xorriso_with_libjtE */ if(!no_iso_init) { if(iso_init()<0) { sprintf(msg+strlen(msg), "Cannot initialize libisofs\n"); return(0); } } iso_lib_version(&major, &minor, µ); sprintf(msg+strlen(msg), "libisofs-%d.%d.%d ", major, minor, micro); #ifdef iso_lib_header_version_major if(iso_lib_is_compatible(iso_lib_header_version_major, iso_lib_header_version_minor, iso_lib_header_version_micro)) { sprintf(msg+strlen(msg), "ok, "); } else { sprintf(msg+strlen(msg),"- TOO OLD -, need at least libisofs-%d.%d.%d ,\n", iso_lib_header_version_major, iso_lib_header_version_minor, iso_lib_header_version_micro); bad_match= 1; } #else if(iso_lib_is_compatible(isoburn_libisofs_req_major, isoburn_libisofs_req_minor, isoburn_libisofs_req_micro)) { sprintf(msg+strlen(msg), "suspicious, "); } else { sprintf(msg+strlen(msg),"- TOO OLD -, need at least libisofs-%d.%d.%d ,\n", isoburn_libisofs_req_major, isoburn_libisofs_req_minor, isoburn_libisofs_req_micro); bad_match= 1; } #endif /* ! iso_lib_header_version_major */ if(!burn_initialize()) { sprintf(msg+strlen(msg), "Cannot initialize libburn\n"); return(0); } burn_version(&major, &minor, µ); sprintf(msg+strlen(msg), "libburn-%d.%d.%d ", major, minor, micro); if(major > burn_header_version_major || (major == burn_header_version_major && (minor > burn_header_version_minor || (minor == burn_header_version_minor && micro >= burn_header_version_micro)))) { sprintf(msg+strlen(msg), "ok, "); } else { sprintf(msg+strlen(msg), "- TOO OLD -, need at least libburn-%d.%d.%d ,\n", burn_header_version_major, burn_header_version_minor, burn_header_version_micro); bad_match= 1; } isoburn_version(&major, &minor, µ); sprintf(msg+strlen(msg), "for libisoburn-%d.%d.%d", major, minor, micro); if(bad_match) return(0); isoburn_destroy_all(&isoburn_list_start, 0); /* isoburn_list_start= NULL */ return(1); } /* API @since 0.1.0 */ int isoburn_libisofs_req(int *major, int *minor, int *micro) { *major= iso_lib_header_version_major; *minor= iso_lib_header_version_minor; *micro= iso_lib_header_version_micro; return(1); } /* API @since 0.1.0 */ int isoburn_libburn_req(int *major, int *minor, int *micro) { *major= burn_header_version_major; *minor= burn_header_version_minor; *micro= burn_header_version_micro; return(1); } /* API @since 0.6.4 */ int isoburn_libjte_req(int *major, int *minor, int *micro) { #ifdef Xorriso_with_libjtE *major= LIBJTE_VERSION_MAJOR; *minor= LIBJTE_VERSION_MINOR; *micro= LIBJTE_VERSION_MICRO; #else *major= *minor= *micro= 0; #endif /* ! Xorriso_with_libjtE */ return(1); } int isoburn_set_msgs_submit(int (*msgs_submit)(void *handle, int error_code, char msg_text[], int os_errno, char severity[], int flag), void *submit_handle, int submit_flag, int flag) { libisoburn_default_msgs_submit= msgs_submit; libisoburn_default_msgs_submit_handle= submit_handle; libisoburn_default_msgs_submit_flag= submit_flag; return(1); } int isoburn_is_intermediate_dvd_rw(struct burn_drive *d, int flag) { int profile, ret= 0, format_status, num_formats; char profile_name[80]; enum burn_disc_status s; off_t format_size= -1; unsigned bl_sas; s= isoburn_disc_get_status(d); ret= burn_disc_get_profile(d, &profile, profile_name); if(ret>0 && profile==0x13) ret= burn_disc_get_formats(d, &format_status, &format_size, &bl_sas, &num_formats); if(ret>0 && profile==0x13 && s==BURN_DISC_BLANK && format_status==BURN_FORMAT_IS_UNKNOWN) return(1); return(0); } /** Examines the medium and sets appropriate emulation if needed. @param flag bit0= pretent blank on overwritable media bit3= if the drive reports a -ROM profile then try to read table of content by scanning for ISO image headers. bit4= do not emulate TOC on overwritable media bit5= ignore ACL from external filesystems bit6= ignore POSIX Extended Attributes from external filesystems bit7= pretend -ROM and scan for table of content bit9= when scanning for ISO 9660 sessions on overwritable media: Do not demand a valid superblock at LBA 0 and scan until end of medium. bit10= if not bit6: accept all xattr namespaces from external filesystems, not only "user.". bit11= load lfa_flags (chattr) from external filesystems bit15= ignore non-settable lfa_flags when importing files from disk and do not record "isofs.fa" if the other flags are all zero */ static int isoburn_welcome_media(struct isoburn **o, struct burn_drive *d, int flag) { int ret, profile, readonly= 0, role, random_access; int emulation_started= 0; struct burn_multi_caps *caps= NULL; struct isoburn_toc_entry *t; char profile_name[80]; struct isoburn_toc_disc *disc= NULL; struct isoburn_toc_session **sessions; struct isoburn_toc_track **tracks; int num_sessions= 0, num_tracks= 0, track_count= 0, session_no= 0; char msg[80]; enum burn_disc_status s; #ifndef Hardcoded_cd_rW off_t lba, nwa; #endif s= burn_disc_get_status(d); profile_name[0]= 0; ret= burn_disc_get_profile(d, &profile, profile_name); if(ret<=0) profile= 0x00; ret= burn_disc_get_multi_caps(d, BURN_WRITE_NONE, &caps, 0); if(ret<0) /*== 0 is read-only medium, but it is too early to reject it here */ goto ex; if(ret==0 || (flag & 128)) readonly= 1; if(flag & 128) flag = (flag & ~ 16) | 8; ret= isoburn_find_emulator(o, d, 0); if(ret >= 0 && *o != NULL) isoburn_destroy(o, 0); ret= isoburn_new(o, 0); if(ret<=0) goto ex; (*o)->drive= d; (*o)->msgs_submit= libisoburn_default_msgs_submit; (*o)->msgs_submit_handle= libisoburn_default_msgs_submit_handle; (*o)->msgs_submit_flag= libisoburn_default_msgs_submit_flag; iso_image_set_ignore_aclea((*o)->image, ((flag >> 5 ) & 3) | ((!!(flag & 1024)) << 3) | ((!!(flag & (1 << 11))) << 2) | ((!!(flag & (1 << 15))) << 5)); #ifdef Hardcoded_cd_rW /* <<< A70929 : hardcoded CD-RW with fabricated -msinfo */ caps->start_adr= 0; (*o)->fabricated_disc_status= BURN_DISC_APPENDABLE; #endif role= burn_drive_get_drive_role(d); random_access= caps->start_adr || role == 4; if(random_access) (*o)->emulation_mode= 1; if(random_access && !readonly) { /* set emulation to overwritable */ ret= isoburn_is_intermediate_dvd_rw(d, 0); if(ret>0) { (*o)->min_start_byte= 0; (*o)->nwa= 0; (*o)->zero_nwa= 0; } if((flag & 1) && role != 4 && role != 5) { (*o)->nwa= (*o)->zero_nwa; (*o)->fabricated_disc_status= BURN_DISC_BLANK; } else { ret= isoburn_start_emulation(*o, 0); if(ret<=0) { (*o)->emulation_mode= -1; goto ex; } emulation_started= 1; /* try to read emulated toc */ ret= isoburn_emulate_toc(d, (flag & 16) | ((!!(flag & 512)) << 1)); if(ret<0) { (*o)->emulation_mode= -1; goto ex; } } } else { /* >>> recognize unsuitable media (but allow read-only media) */; if(readonly && s != BURN_DISC_EMPTY) { /* >>> ts B10712: This maps BURN_DISC_UNSUITABLE to BURN_DISC_FULL which can hardly be correct in general. ??? What reason does this have ? */ (*o)->fabricated_disc_status= BURN_DISC_FULL; /* This might be an overwritable medium in a -ROM drive. Pitfall: Multi-session media which bear a xorriso image for overwritables in their first session would get a TOC of that first image rather than of the medium. It is not possible to distinguish a BD-RE from a single session BD-R with an image for overwritables. But as soon as the medium bears 2 logical tracks it cannot be overwritable. So count the number of tracks first. */ disc= isoburn_toc_drive_get_disc(d); if(disc != NULL) { sessions= isoburn_toc_disc_get_sessions(disc, &num_sessions); for(session_no= 0; session_no < num_sessions; session_no++) { tracks= isoburn_toc_session_get_tracks(sessions[session_no], &num_tracks); if(tracks != NULL) track_count+= num_tracks; } isoburn_toc_disc_free(disc); } sprintf(msg, "ROM medium has libburn track count = %d", track_count); isoburn_msgs_submit(*o, 0x00060000, msg, 0, "DEBUG", 0); if((flag & 16) || track_count >= 2) { ret= 0; /* toc emulation off, or not overwritable */ } else { ret= isoburn_start_emulation(*o, 1); if(ret<=0) { (*o)->emulation_mode= -1; goto ex; } emulation_started= 1; ret= isoburn_emulate_toc(d, 1 | ((!!(flag & 512)) << 1)); if(ret<0) goto ex; else if(ret > 0) (*o)->emulation_mode= 1; } if(ret == 0 && (profile != 0x08 || (flag & 128)) && (flag & 8)) { /* This might also be multi-session media which do not get shown with a decent TOC. CD-R TOC (profile 0x08) can be trusted. Others not. Do a scan search of ISO headers. */ if(!emulation_started) { ret= isoburn_start_emulation(*o, 1); if(ret<=0) { (*o)->emulation_mode= -1; goto ex; } } ret= isoburn_emulate_toc(d, 1 | 2); if(ret<0) goto ex; if(ret>0) { /* point msc1 to last session */ if((*o)->toc!=NULL) { for(t= (*o)->toc; t->next!=NULL; t= t->next) ; /* clang wants newline in empty loops */ (*o)->fabricated_msc1= t->start_lba; } } } } #ifdef Hardcoded_cd_rW (*o)->nwa= Hardcoded_cd_rw_nwA; #else ret= burn_disc_track_lba_nwa_v2(d, NULL, 0, &lba, &nwa); if(ret>0) (*o)->nwa= nwa; if((*o)->nwa < (*o)->zero_nwa) (*o)->zero_nwa= 0; #endif } ret= 1; ex: if(caps!=NULL) burn_disc_free_multi_caps(&caps); return(ret); } /** @param flag bit0= load bit1= regard overwritable media as blank bit2= if the drive is a regular disk file: truncate it to the write start address bit3= if the drive reports a -ROM profile then try to read table of content by scanning for ISO image headers. (depending on media type and drive state this might help or it might make the resulting toc even worse) bit4= do not emulate TOC on overwritable media bit5= ignore ACL from external filesystems bit6= ignore POSIX Extended Attributes from external filesystems bit7= pretend -ROM profile and scan for table of content bit8= re-assess (*drive_infos)[0] rather than acquiring adr bit9= when scanning for ISO 9660 sessions on overwritable media: Do not demand a valid superblock at LBA 0 and scan until end of medium. bit10= if not bit6: accept all xattr namespaces from external filesystems, not only "user.". bit11= load lfa_flags (chattr) from external filesystems bit15= ignore non-settable lfa_flags when importing files from disk and do not record "isofs.fa" if the other flags are all zero */ int isoburn_drive_aquire(struct burn_drive_info *drive_infos[], char *adr, int flag) { int ret, drive_grabbed= 0; struct isoburn *o= NULL; int conv_ret; char *libburn_drive_adr= NULL; /* Should be obsolete by new drive addressing of libburn-0.5.2 */ /* but helps with kernel 2.4 to use /dev/sr */ libburn_drive_adr= calloc(1, BURN_DRIVE_ADR_LEN); if(libburn_drive_adr == NULL) {ret= -1; goto ex;} conv_ret= burn_drive_convert_fs_adr(adr, libburn_drive_adr); if(conv_ret<=0) strcpy(libburn_drive_adr, adr); if(flag & 256) ret= burn_drive_re_assess((*drive_infos)[0].drive, 0); else ret= burn_drive_scan_and_grab(drive_infos, libburn_drive_adr, flag&1); if(ret<=0) goto ex; drive_grabbed= 1; ret= isoburn_welcome_media(&o, (*drive_infos)[0].drive, (flag & (8 | 16 | 32 | 64 | 128 | 512 | 1024 | (1 << 11) | ( 1 << 15))) | !!(flag & 2)); if(ret<=0) goto ex; if(flag&4) { ret= isoburn_find_emulator(&o, (*drive_infos)[0].drive, 0); if(ret>0 && o!=NULL) o->truncate= 1; } ret= 1; ex: if(ret<=0) { if(drive_grabbed) burn_drive_release((*drive_infos)[0].drive, 0); isoburn_destroy(&o, 0); } if(libburn_drive_adr != NULL) free(libburn_drive_adr); return(ret); } int isoburn_drive_scan_and_grab(struct burn_drive_info *drive_infos[], char *adr, int load) { int ret; ret= isoburn_drive_aquire(drive_infos, adr, !!load); return(ret); } int isoburn_drive_grab(struct burn_drive *drive, int load) { int ret; struct isoburn *o= NULL; ret= burn_drive_grab(drive, load); if(ret<=0) goto ex; ret= isoburn_welcome_media(&o, drive, 0); if(ret<=0) goto ex; ret= 1; ex: if(ret<=0) isoburn_destroy(&o,0); return(ret); } /** Retrieve medium emulation and eventual isoburn emulator of drive. @return -1 unsuitable medium, 0 generic medium, 1 emulated medium. */ int isoburn_find_emulator(struct isoburn **pt, struct burn_drive *drive, int flag) { int ret; ret= isoburn_find_by_drive(pt, drive, 0); if(ret<=0) return(0); if((*pt)->emulation_mode==-1) { isoburn_msgs_submit(*pt, 0x00060000, "Unsuitable drive and medium state", 0, "FAILURE", 0); return(-1); } if((*pt)->emulation_mode==0) return(0); return(1); } enum burn_disc_status isoburn_disc_get_status(struct burn_drive *drive) { int ret; struct isoburn *o; ret= isoburn_find_emulator(&o, drive, 0); if(ret<0) return(BURN_DISC_UNSUITABLE); if(o!=NULL) if(o->fabricated_disc_status!=BURN_DISC_UNREADY) return(o->fabricated_disc_status); if(ret==0) return(burn_disc_get_status(drive)); /* emulated status */ if(o->emulation_mode==-1) return(BURN_DISC_UNSUITABLE); if(o->nwa>o->zero_nwa) return(BURN_DISC_APPENDABLE); return(BURN_DISC_BLANK); } int isoburn_disc_pretend_full_uncond(struct burn_drive *drive) { int ret; struct isoburn *o; ret= isoburn_find_emulator(&o, drive, 0); if(ret > 0 && o != NULL) o->fabricated_disc_status= BURN_DISC_FULL; ret= burn_disc_pretend_full_uncond(drive); return(ret); } int isoburn_disc_erasable(struct burn_drive *d) { int ret; struct isoburn *o; ret= isoburn_find_emulator(&o, d, 0); if(ret>0) if(o->emulation_mode==1) return(1); return burn_disc_erasable(d); } static int isoburn_is_overwritable(struct burn_drive *drive, int flag) { char name[80]; int profile, ret; ret= burn_disc_get_profile(drive, &profile, name); if(ret <= 0) return(0); if(profile == 0x12 || profile == 0x13 || profile == 0x1a || profile == 0x43) return(1); return(0); } void isoburn_disc_erase(struct burn_drive *drive, int fast) { int ret, do_pseudo_blank= 0, role; struct isoburn *o; enum burn_disc_status s; char *zero_buffer= NULL; struct burn_multi_caps *caps= NULL; zero_buffer= calloc(1, Libisoburn_target_head_sizE); if(zero_buffer == NULL) { /* To cause a negative reply with burn_drive_wrote_well() */ burn_drive_cancel(drive); goto ex; } ret= isoburn_find_emulator(&o, drive, 0); if(ret>0) { if(o->emulation_mode==-1) { /* To cause a negative reply with burn_drive_wrote_well() */ burn_drive_cancel(drive); goto ex; } role = burn_drive_get_drive_role(drive); if (role == 5) { /* libburn will truncate the random-access write-only file to zero size and change its state */ burn_disc_erase(drive, fast); o->fabricated_disc_status= burn_disc_get_status(drive); o->nwa= o->zero_nwa= 0; goto ex; } if(o->emulation_mode > 0) { /* might be readonly with emulated sessions */ ret= burn_disc_get_multi_caps(drive, BURN_WRITE_NONE, &caps, 0); if(ret <= 0) /* Maybe because of burn_disc_pretend_full() */ do_pseudo_blank= isoburn_is_overwritable(drive, 0); /* known profiles */ else if(caps->start_adr) do_pseudo_blank= 1; } if(do_pseudo_blank) { s= isoburn_disc_get_status(drive); if(s==BURN_DISC_FULL) { /* unknown data format in first 64 kB */ memset(zero_buffer, 0, Libisoburn_target_head_sizE); ret= burn_random_access_write(drive, (off_t) 0, zero_buffer, (off_t) Libisoburn_target_head_sizE, 1); } else { ret= isoburn_invalidate_iso(o, 0); } if(ret<=0) burn_drive_cancel(drive); /* mark run as failure */ goto ex; } } burn_disc_erase(drive, fast); ex:; if(caps!=NULL) burn_disc_free_multi_caps(&caps); if(zero_buffer != NULL) free(zero_buffer); } off_t isoburn_disc_available_space(struct burn_drive *d, struct burn_write_opts *opts) { int ret; struct isoburn *o; struct burn_write_opts *eff_opts= NULL, *local_opts= NULL; enum burn_disc_status s; off_t avail; eff_opts= opts; ret= isoburn_find_emulator(&o, d, 0); if(ret>0 && o!=NULL) if(o->emulation_mode!=0) { s= isoburn_disc_get_status(d); if(s==BURN_DISC_FULL) /* unknown data format in first 64 kB */ return((off_t) 0); local_opts= burn_write_opts_new(d); eff_opts= local_opts; burn_write_opts_set_start_byte(eff_opts, o->nwa * (off_t) 2048); } avail= burn_disc_available_space(d, eff_opts); if(local_opts!=NULL) burn_write_opts_free(local_opts); local_opts= NULL; return(avail); } int isoburn_disc_get_msc1_v2(struct burn_drive *d, off_t *start_lba) { int ret, int_lba; struct isoburn *o; #ifdef Hardcoded_cd_rW /* <<< A70929 : hardcoded CD-RW with fabricated -msinfo */ *start_lba= Hardcoded_cd_rw_c1; return(1); #endif if(isoburn_disc_get_status(d)!=BURN_DISC_APPENDABLE && isoburn_disc_get_status(d)!=BURN_DISC_FULL) { isoburn_msgs_submit(NULL, 0x00060000, "Medium contains no recognizable data", 0, "SORRY", 0); return(0); } ret= isoburn_find_emulator(&o, d, 0); if(ret<0) return(0); if(o->fabricated_msc1>=0) { *start_lba= o->fabricated_msc1; return(1); } if(ret>0) if(o->emulation_mode>0) { *start_lba= 0; return(1); } ret= burn_disc_get_msc1(d, &int_lba); if(ret <= 0) return(ret); *start_lba= int_lba; return(ret); } int isoburn_disc_get_msc1(struct burn_drive *d, int *start_lba) { int ret; off_t long_lba; ret= isoburn_disc_get_msc1_v2(d, &long_lba); if(ret <= 0) return(ret); if(long_lba > 0x7fffffff) { *start_lba= 0x7fffffff; ret= 0; } else { *start_lba= long_lba; } return(ret); } int isoburn_disc_track_lba_nwa_v2(struct burn_drive *d, struct burn_write_opts *opts, int trackno, off_t *lba, off_t *nwa) { int ret; struct isoburn *o; enum burn_disc_status s; #ifdef Hardcoded_cd_rW /* <<< A70929 : hardcoded CD-RW with fabricated -msinfo */ *lba= Hardcoded_cd_rw_c1; *nwa= Hardcoded_cd_rw_nwA; return(1); #endif *nwa= *lba= 0; ret= isoburn_find_emulator(&o, d, 0); if(ret<0) return(0); if(ret>0) if(o->emulation_mode>0) { *lba= 0; *nwa= o->nwa; return(1); } if(burn_drive_get_drive_role(d) != 1) return(1); s= isoburn_disc_get_status(d); if(s == BURN_DISC_BLANK) /* We do not believe in anything but nwa = lba = 0 */ return(1); return(burn_disc_track_lba_nwa_v2(d, opts, trackno, lba, nwa)); } int isoburn_disc_track_lba_nwa(struct burn_drive *d, struct burn_write_opts *opts, int trackno, int *lba, int *nwa) { int ret; off_t long_lba, long_nwa; ret= isoburn_disc_track_lba_nwa_v2(d, opts, trackno, &long_lba, &long_nwa); if(ret <= 0) return(ret); if(long_lba > 0x7fffffff) { *lba= 0x7fffffff; } else { *lba= long_lba; } if(long_nwa > 0x7fffffff) { *nwa= 0x7fffffff; ret= 0; } else { *nwa= long_nwa; } return(ret); } int isoburn_get_msc2(struct isoburn *o, struct burn_write_opts *opts, off_t *msc2, int flag) { int ret; off_t lba, nwa; if(o->fabricated_msc2>=0) *msc2= o->fabricated_msc2; else { ret= isoburn_disc_track_lba_nwa_v2(o->drive, opts, 0, &lba, &nwa); if(ret<=0) return(ret); *msc2= nwa; } return(1); } /* @param flag bit0= truncate (else do not truncate) bit1= do not warn if call is inappropriate to drive bit2= only set if truncation is currently enabled */ int isoburn_set_truncate(struct burn_drive *drive, int flag) { int ret; struct isoburn *o; ret= isoburn_find_emulator(&o, drive, 0); if(ret < 0) return ret; if(o == NULL) { if(!(flag & (2 | 4))) isoburn_msgs_submit(o, 0x00060000, "Drive type or role is inappropriate for truncation", 0, "WARNING", 0); return(0); } if(o->truncate || !(flag & 4)) o->truncate= flag & 1; return(1); } void isoburn_disc_write(struct burn_write_opts *opts, struct burn_disc *disc) { int ret; off_t nwa= 0; struct isoburn *o; struct burn_drive *drive; char *reasons= NULL, *msg= NULL, *adr= NULL; struct stat stbuf; enum burn_write_types write_type; drive= burn_write_opts_get_drive(opts); reasons= calloc(1, BURN_REASONS_LEN); msg= calloc(1, 160+BURN_REASONS_LEN); adr= calloc(1, BURN_DRIVE_ADR_LEN); if(reasons == NULL || msg == NULL || adr == NULL) { /* To cause a negative reply with burn_drive_wrote_well() */ burn_drive_cancel(drive); goto ex; } ret= isoburn_find_emulator(&o, drive, 0); if(ret<0) goto ex; if(o == NULL) { sprintf(msg, "Program error: Cannot find isoburn object associated to the drive"); isoburn_msgs_submit(o, 0x00060000, msg, 0, "FAILURE", 0); burn_drive_cancel(drive); goto ex; } o->wrote_well= -1; if(o->emulation_mode!=0) { burn_write_opts_set_multi(opts, 0); if(o->emulation_mode>0 && o->nwa >= 0) { nwa= o->nwa; /* This caters for unwritten formatted DVD-RW. They need to be written sequentially on the first use. Only written areas are random access. If the first session is not written to LBA 0, then re-opening of formatting and padding is needed. This can be done. But when the track gets closed after padding, this lasts a long time. There is a high risk that an app will not poll the message queue while waiting for isoburn_disc_write() to return. The pacifier loop usually happens only afterwards. So automatic formatting might cause a nervous clueless user. */ ret= isoburn_is_intermediate_dvd_rw(drive, 0); if(ret>0 && nwa>0 && nwa <= o->zero_nwa) { /* actually this should not happen since such media get recognized by isoburn_welcome_media and o->zero_nwa gets set to 0 */ sprintf(msg, "DVD-RW insufficiently formatted. (Intermediate State, size unknown)"); isoburn_msgs_submit(o, 0x00060000, msg, 0, "FAILURE", 0); sprintf(msg, "It might help to first deformat it and then format it again"); isoburn_msgs_submit(o, 0x00060000, msg, 0, "HINT", 0); burn_drive_cancel(drive); /* mark run as failure */ goto ex; } /* end of DVD-RW oriented check */ burn_write_opts_set_start_byte(opts, nwa * (off_t) 2048); } } if(o->do_tao) { if (o->do_tao > 0) burn_write_opts_set_write_type(opts, BURN_WRITE_TAO, BURN_BLOCK_MODE1); else burn_write_opts_set_write_type(opts, BURN_WRITE_SAO, BURN_BLOCK_SAO); ret = burn_precheck_write(opts, disc, reasons, 0); if(ret <= 0) { sprintf(msg, "Cannot set write type %s for this medium.", o->do_tao > 0 ? "TAO" : "SAO"); sprintf(msg + strlen(msg), "Reasons given:\n %s", reasons); goto no_write_type; } sprintf(msg, "Explicitly chosen write type: %s", o->do_tao > 0 ? "TAO" : "SAO"); isoburn_msgs_submit(o, 0x00060000, msg, 0, "NOTE", 0); } else { write_type= burn_write_opts_auto_write_type(opts, disc, reasons, 0); if (write_type == BURN_WRITE_NONE) { sprintf(msg, "Failed to find a suitable write type:\n%s", reasons); no_write_type:; isoburn_msgs_submit(o, 0x00060000, msg, 0, "FAILURE", 0); if(o!=NULL) o->wrote_well= 0; /* To cause a negative reply with burn_drive_wrote_well() */ burn_drive_cancel(drive); goto ex; } sprintf(reasons, "%d", (int) write_type); sprintf(msg, "Write_type = %s\n", (write_type == BURN_WRITE_SAO ? "SAO" : (write_type == BURN_WRITE_TAO ? "TAO" : reasons))); isoburn_msgs_submit(o, 0x00060000, msg, 0, "DEBUG", 0); } #ifdef Hardcoded_cd_rW /* <<< A70929 : hardcoded CD-RW with fabricated -msinfo */ fprintf(stderr, "Setting write address to LBA %d\n", Hardcoded_cd_rw_nwA); burn_write_opts_set_start_byte(opts, ((off_t) Hardcoded_cd_rw_nwA) * (off_t) 2048); #endif if(o->truncate) { ret= burn_drive_get_drive_role(drive); if(ret == 2 || ret == 5) { ret= burn_drive_d_get_adr(drive, adr); if(ret>0) { ret= lstat(adr, &stbuf); if(ret!=-1) if(S_ISREG(stbuf.st_mode)) ret= truncate(adr, nwa * (off_t) 2048); /* (result of truncate intentionally ignored) */ } } } burn_disc_write(opts, disc); ex:; if(reasons != NULL) free(reasons); if(msg != NULL) free(msg); if(adr != NULL) free(adr); } void isoburn_drive_release(struct burn_drive *drive, int eject) { int ret; struct isoburn *o; ret= isoburn_find_emulator(&o, drive, 0); if(ret<0) return; if(o!=NULL) { isoburn_destroy(&o, 0); } burn_drive_release(drive, eject); } void isoburn_finish(void) { isoburn_destroy_all(&isoburn_list_start, 0); burn_finish(); iso_finish(); } int isoburn_needs_emulation(struct burn_drive *drive) { int ret; struct isoburn *o; enum burn_disc_status s; s= isoburn_disc_get_status(drive); if(s!=BURN_DISC_BLANK && s!=BURN_DISC_APPENDABLE) return(-1); ret= isoburn_find_emulator(&o, drive, 0); if(ret<0) return(-1); if(ret>0) if(o->emulation_mode>0) return(1); return(0); } int isoburn_set_start_byte(struct isoburn *o, off_t value, int flag) { int ret; struct burn_drive *drive = o->drive; struct burn_multi_caps *caps= NULL; ret= burn_disc_get_multi_caps(drive, BURN_WRITE_NONE, &caps, 0); if(ret<=0) goto ex; if(!caps->start_adr) { isoburn_msgs_submit(o, 0x00060000, "Cannot set start byte address with this type of media", 0, "FAILURE", 0); {ret= 0; goto ex;} } o->min_start_byte= value; if(value % caps->start_alignment) value+= caps->start_alignment - (value % caps->start_alignment); o->nwa= value/2048; if(o->nwa < o->zero_nwa) o->zero_nwa= 0; /* If suitable for media alignment, round up to Libisoburn_nwa_alignemenT */ if((o->nwa % Libisoburn_nwa_alignemenT) && ((Libisoburn_nwa_alignemenT*2048) % caps->start_alignment)==0 ) o->nwa+= Libisoburn_nwa_alignemenT - (o->nwa % Libisoburn_nwa_alignemenT); ret= 1; ex: if(caps!=NULL) burn_disc_free_multi_caps(&caps); return(ret); } int isoburn_get_min_start_byte(struct burn_drive *d, off_t *start_byte, int flag) { int ret; struct isoburn *o; ret= isoburn_find_emulator(&o, d, 0); if(ret<0) return(-1); if(ret==0) return(0); *start_byte= o->min_start_byte; if(o->min_start_byte<=0) return(0); return(1); } int isoburn_drive_wrote_well(struct burn_drive *d) { int ret; struct isoburn *o; ret= isoburn_find_emulator(&o, d, 0); if(ret<0) return(-1); if(o!=NULL) if(o->wrote_well>=0) return(o->wrote_well); ret= burn_drive_wrote_well(d); return ret; } int isoburn_get_fifo_status(struct burn_drive *d, int *size, int *free_bytes, char **status_text) { int ret; struct isoburn *o; size_t hsize= 0, hfree_bytes= 0; ret= isoburn_find_emulator(&o, d, 0); if(ret<0) return(-1); if(o==NULL) return(-1); if(o->iso_source==NULL) return(-1); ret= iso_ring_buffer_get_status(o->iso_source, &hsize, &hfree_bytes); if(hsize > 1024*1024*1024) *size= 1024*1024*1024; else *size= hsize; if(hfree_bytes > 1024*1024*1024) *free_bytes= 1024*1024*1024; else *free_bytes= hfree_bytes; *status_text= ""; if(ret==0) *status_text= "standby"; else if(ret==1) *status_text= "active"; else if(ret==2) *status_text= "ending"; else if(ret==3) *status_text= "failing"; else if(ret==4) *status_text= "unused"; else if(ret==5) *status_text= "abandoned"; else if(ret==6) *status_text= "ended"; else if(ret==7) *status_text= "aborted"; return(ret); } /* @param flag bit0= -reserved- bit1= this is a libburn severity */ int isoburn__sev_to_text(int severity, char **severity_name, int flag) { int ret; ret= iso_sev_to_text(severity, severity_name); if(ret>0) return(ret); ret= burn_sev_to_text(severity, severity_name, 0); return(ret); } int isoburn__text_to_sev(char *severity_name, int *severity_number, int flag) { int ret= 1; ret= iso_text_to_sev(severity_name, severity_number); if(ret>0) return(ret); ret= burn_text_to_sev(severity_name, severity_number, 0); return(ret); } int isoburn_report_iso_error(int iso_error_code, char msg_text[], int os_errno, char min_severity[], int flag) { int error_code, iso_sev, min_sev, ret; char *sev_text_pt, *msg_text_pt= NULL; error_code= iso_error_get_code(iso_error_code); if(error_code < 0x00030000 || error_code >= 0x00040000) error_code= (error_code & 0xffff) | 0x00050000; if(iso_error_code<0) msg_text_pt= (char *) iso_error_to_msg(iso_error_code); if(msg_text_pt==NULL) msg_text_pt= msg_text; iso_sev= iso_error_get_severity(iso_error_code); sev_text_pt= min_severity; isoburn__text_to_sev(min_severity, &min_sev, 0); if(min_sev < iso_sev) isoburn__sev_to_text(iso_sev, &sev_text_pt, 0); ret= iso_msgs_submit(error_code, msg_text_pt, os_errno, sev_text_pt, 0); return(ret); } /* @param flag bit0-7: info return mode 0= do not return anything in info (do not even touch it) 1= copy volume id to info (info needs 33 bytes) 2= do not touch info (caller will copy 64 kB header to it) 3= copy creation time to info (info needs 18 bytes) 4= copy modification time to info (info needs 18 bytes) bit14= -reserved - bit15= -reserved- @return 1 seems to be a valid ISO image , 0 format not recognized, <0 error */ int isoburn_read_iso_head_parse(unsigned char *data, off_t *image_blocks, char *info, int flag) { int i, info_mode; /* is this an ISO image ? */ if(data[0]!=1) return(0); if(strncmp((char *) (data+1),"CD001",5)!=0) return(0); /* believe so */ *image_blocks= data[80] | (data[81]<<8) | (data[82]<<16) | (data[83]<<24); info_mode= flag&255; if(info_mode==0) { ; } else if(info_mode==1) { /* Volume Id */ strncpy(info, (char *) (data+40), 32); info[32]= 0; for(i= strlen(info)-1; i>=0; i--) if(info[i]!=' ') break; else info[i]= 0; } else if(info_mode==2) { ; } else if(info_mode == 3) { /* Creation Time */ strncpy(info, (char *) (data + 813), 17); info[17]= 0; } else if(info_mode == 4) { /* Modification Time */ strncpy(info, (char *) (data + 830), 17); info[17]= 0; } else { isoburn_msgs_submit(NULL, 0x00060000, "Program error: Unknown info mode with isoburn_read_iso_head()", 0, "FATAL", 0); return(-1); } return(1); } /* API @param flag bit0-7: info return mode 0= do not return anything in info (do not even touch it) 1= copy volume id to info (info needs 33 bytes) 2= copy 64 kB header to info (needs 65536 bytes) bit12= read even if the start of multi-session emulation yielded a read error bit13= do not read head from media but use first 64 kB from info bit14= check both half buffers (not only second) return 2 if found in first block bit15= return-1 on read error @return 1 seems to be a valid ISO image , 2 found in first half buffer, 0 format not recognized, <0 error */ int isoburn_read_iso_head_v2(struct burn_drive *d, off_t lba, off_t *image_blocks, char *info, int flag) { unsigned char *buffer= NULL; int ret, info_mode, role; off_t capacity; off_t data_count, to_read; struct isoburn *o; buffer= calloc(1, 64 * 1024); if(buffer == NULL) {ret= -1; goto ex;} info_mode= flag&255; *image_blocks= 0; if(flag&(1<<13)) { memcpy(buffer, info, 64*1024); } else { memset(buffer, 0, 64 * 1024); role = burn_drive_get_drive_role(d); if (role == 3 || role == 5) /* >>> ??? return always 0 ? */ {ret= (-1*!!(flag&(1<<15))); goto ex;} ret = burn_get_read_capacity_v2(d, &capacity, 0); if (ret <= 0 && (role == 2 || role == 4)) { /* Might be a block device on a system where libburn cannot determine its size. Try to read anyway. */ capacity = 0x7fffffffffffffff; ret = 1; } to_read= capacity * ((off_t) 2048); if(ret > 0 && to_read >= (off_t) (36 * 1024)) { ret= isoburn_find_emulator(&o, d, 0); if(ret > 0) if(o->media_read_error && !(flag & (1 << 12))) {ret= (-1 * !!(flag & (1 << 15))); goto ex;} if(to_read >= (off_t) (64 * 1024)) to_read= 64 * 1024; ret = burn_read_data(d, lba * (off_t) 2048, (char *) buffer, to_read, &data_count, 32); /* error messages as DEBUG */ } else ret= 0; if(ret<=0) {ret= (-1*!!(flag&(1<<15))); goto ex;} if(info_mode==2) memcpy(info, buffer, 64*1024); } if(flag&(1<<14)) { ret= isoburn_read_iso_head_parse(buffer, image_blocks, info, info_mode); if(ret<0) goto ex; if(ret>0) {ret= 2; goto ex;} } ret= isoburn_read_iso_head_parse(buffer+32*1024, image_blocks, info, info_mode); if(ret<=0) goto ex; ret= 1; ex:; if(buffer != NULL) free(buffer); return(ret); } int isoburn_read_iso_head(struct burn_drive *d, int lba, int *image_blocks, char *info, int flag) { int ret; off_t long_image_blocks; ret= isoburn_read_iso_head_v2(d, (off_t) lba, &long_image_blocks, info, flag); if(ret <= 0) return(ret); /* The old behavior was to return negative numbers on overflow */ *image_blocks= (int) long_image_blocks; return(ret); } int isoburn_make_toc_entry(struct isoburn *o, int *session_count, off_t lba, off_t track_blocks, char *volid, int flag) { int ret; struct isoburn_toc_entry *item; ret= isoburn_toc_entry_new(&item, o->toc, 0); if(ret<=0) { no_memory:; isoburn_msgs_submit(o, 0x00060000, "Not enough memory for emulated TOC entry object", 0, "FATAL", 0); return(-1); } if(o->toc==NULL) o->toc= item; (*session_count)++; item->session= *session_count; item->track_no= *session_count; item->start_lba= lba; item->track_blocks= track_blocks; if(volid != NULL) { item->volid= strdup(volid); if(item->volid == NULL) goto no_memory; } return(1); } /* @param flag bit0= allow unemulated media bit1= free scanning without enclosing LBA-0-header bit4= represent emulated media as one single session (not with bit1) @return -1 severe error, 0= no neat header chain, 1= credible chain read */ int isoburn_emulate_toc(struct burn_drive *d, int flag) { int ret, session_count= 0, read_flag= 0; int probe_minus_16= 0, role, with_enclosure= 0; off_t image_size= 0, lba, track_blocks, scan_start= 0, scan_count= 0; off_t growisofs_nwa, readable_blocks= -1; struct isoburn *o; char *msg= NULL, *size_text= NULL, *sev, volid[33], *volid_pt= NULL; time_t start_time, last_pacifier, now; msg= calloc(1, 160); size_text= calloc(1, 80); if(msg == NULL || size_text == NULL) {ret= -1; goto ex;} /* is the medium emulated multi-session ? */ ret= isoburn_find_emulator(&o, d, 0); if(ret<0) {ret= -1; goto ex;} if(o==NULL) {ret= -1; goto ex;} if(o->emulation_mode<=0 && !(flag&1)) {ret= 0; goto ex;} ret= burn_get_read_capacity_v2(d, &readable_blocks, 0); if(ret <= 0) { role = burn_drive_get_drive_role(d); if (role == 2 || role == 4) /* Might be a block device on a system where libburn cannot determine its size. Try to read anyway. */ readable_blocks= 0x7fffffffffffffff; /* try to read anyway */ else readable_blocks= -1; } if(o->fabricated_disc_status == BURN_DISC_BLANK) {ret= 0; goto failure;} start_time= last_pacifier= time(NULL); lba= 0; if(flag & 2) { /* If there is a PVD at LBA 32 then this is an image with emulated TOC */ ret= isoburn_read_iso_head_v2(d, 32, &image_size, NULL, 0); if(ret > 0) lba= 32; } else { ret= isoburn_read_iso_head_v2(d, lba, &image_size, NULL, 0); if(ret<=0) {ret= 0; goto failure;} lba= o->target_iso_head_size / 2048; with_enclosure= 1; if((flag & 16) && o->emulation_mode == 1) { ret= 1; goto failure; /* This will represent the medium as single session */ } if(!(lba < image_size || (flag&2))) { /* Not enough readable space for chained session superblocks */ ret= 1; goto failure; } } while(lba<image_size || (flag&2)) { now= time(NULL); if(now - last_pacifier >= 5) { last_pacifier= now; if(scan_count>=10*512) sprintf(size_text, "%.f MB", ((double) scan_count) / 512.0); else sprintf(size_text, "%.f kB", 2 * (double) scan_count); sprintf(msg, "Found %d ISO sessions by scanning %s in %.f seconds", session_count, size_text, (double) (now - start_time)); isoburn_msgs_submit(o, 0x00060000, msg, 0, "UPDATE", 0); } read_flag= 1; if(flag&2) read_flag|= (1<<15)|((session_count>0)<<14); else { /* growisofs aligns to 16 rather than 32. Overwritable TOC emulation relies on not accidentally seeing inter-session trash data. But one can safely access 16 blocks earlier because a xorriso header would have been overwritten with the unused 16 blocks at its start. If libisoburn alignment would increase, then this would not be possible any more. */ if(probe_minus_16) read_flag|= (1<<14); probe_minus_16= 0; } ret= isoburn_read_iso_head_v2(d, lba, &track_blocks, volid, read_flag); if(ret > 0) { volid_pt= volid; } else { volid_pt= NULL; if(session_count>0) { if(flag&2) { if(ret==0) { /* try at next 64 k block (check both 32 k halves) */ lba+= 32; scan_count+= 32; if(lba-scan_start <= Libisoburn_toc_scan_max_gaP) continue; } break; } sprintf(msg, "Chain of ISO session headers broken at #%d, LBA %.fs", session_count+1, (double) lba); isoburn_msgs_submit(o, 0x00060000, msg, 0, "WARNING", 0); if(with_enclosure) { ret= isoburn_make_toc_entry(o, &session_count, 0, image_size, NULL,0); if(ret<=0) goto failure; } break; /* do not return failure */ } {ret= 0; goto failure;} } if(ret==2) /* ISO header was found in first half block */ lba-= 16; if(readable_blocks >= 0 && lba + track_blocks > readable_blocks) { sprintf(msg, "ISO image size %.fs larger than readable size %.fs", (double) (lba + track_blocks), (double) readable_blocks); isoburn_msgs_submit(o, 0x00060000, msg, 0, "WARNING", 0); track_blocks= readable_blocks - lba; } ret= isoburn_make_toc_entry(o, &session_count, lba, track_blocks, volid_pt, 0); if(ret<=0) goto failure; lba+= track_blocks; scan_count+= 32; /* growisofs aligns to 16 rather than 32 */ growisofs_nwa= lba; if(growisofs_nwa % 16) growisofs_nwa+= 16 - (growisofs_nwa % 16); if(lba % Libisoburn_nwa_alignemenT) lba+= Libisoburn_nwa_alignemenT - (lba % Libisoburn_nwa_alignemenT); scan_start= lba; if(lba - growisofs_nwa == 16) probe_minus_16= 1; } if(last_pacifier != start_time) sev= "UPDATE"; else sev= "DEBUG"; now= time(NULL); if(scan_count>=10*512) sprintf(size_text, "%.f MB", ((double) scan_count) / 512.0); else sprintf(size_text, "%.f kB", 2 * (double) scan_count); sprintf(msg, "Found %d ISO sessions by scanning %s in %.f seconds", session_count, size_text, (double) (now - start_time)); isoburn_msgs_submit(o, 0x00060000, msg, 0, sev, 0); {ret= 1; goto ex;} failure:; isoburn_toc_entry_destroy(&(o->toc), 1); if(with_enclosure && o->emulation_mode == 1) { if(readable_blocks >= 0 && image_size > readable_blocks) { sprintf(msg, "ISO image size %.fs larger than readable size %.fs", (double) image_size, (double) readable_blocks); isoburn_msgs_submit(o, 0x00060000, msg, 0, "WARNING", 0); image_size= readable_blocks; } session_count= 0; ret= isoburn_make_toc_entry(o, &session_count, 0, image_size, NULL, 0); } ex:; if(msg != NULL) free(msg); if(size_text != NULL) free(size_text); return(ret); } int isoburn_toc_new_arrays(struct isoburn_toc_disc *o, int session_count, int track_count, int flag) { int i; int isoburn_toc_destroy_arrays(struct isoburn_toc_disc *o, int flag); o->sessions= calloc(session_count, sizeof(struct isoburn_toc_session)); o->session_pointers= calloc(session_count, sizeof(struct isoburn_toc_session *)); o->tracks= calloc(track_count, sizeof(struct isoburn_toc_track)); o->track_pointers= calloc(track_count, sizeof(struct isoburn_toc_track *)); if(o->sessions!=NULL && o->session_pointers!=NULL && o->tracks!=NULL && o->track_pointers!=NULL) { for(i= 0; i<session_count; i++) { o->sessions[i].session= NULL; o->sessions[i].track_pointers= NULL; o->sessions[i].track_count= 0; o->sessions[i].toc_entry= NULL; o->session_pointers[i]= NULL; } for(i= 0; i<track_count; i++) { o->tracks[i].track= NULL; o->tracks[i].toc_entry= NULL; o->track_pointers[i]= NULL; } return(1); } /* failed */ isoburn_toc_destroy_arrays(o, 0); return(-1); } int isoburn_toc_destroy_arrays(struct isoburn_toc_disc *o, int flag) { if(o->sessions!=NULL) free((char *) o->sessions); o->sessions= NULL; if(o->session_pointers!=NULL) free((char *) o->session_pointers); o->session_pointers= NULL; if(o->tracks!=NULL) free((char *) o->tracks); o->tracks= NULL; if(o->track_pointers!=NULL) free((char *) o->track_pointers); o->track_pointers= NULL; return(1); } struct isoburn_toc_disc *isoburn_toc_drive_get_disc(struct burn_drive *d) { int ret, session_count= 0, track_count= 0, num_tracks= 0, i, j; int open_sessions= 0; struct isoburn *o; struct isoburn_toc_entry *t; struct isoburn_toc_disc *toc_disc= NULL; struct burn_session **s; struct burn_track **tracks; toc_disc= calloc(1, sizeof(struct isoburn_toc_disc)); if(toc_disc==NULL) return(NULL); toc_disc->disc= NULL; toc_disc->sessions= NULL; toc_disc->session_pointers= NULL; toc_disc->tracks= NULL; toc_disc->track_pointers= NULL; toc_disc->session_count= 0; toc_disc->incomplete_session_count= 0; toc_disc->track_count= 0; toc_disc->toc= NULL; /* is the medium emulated multi-session ? */ ret= isoburn_find_emulator(&o, d, 0); if(ret<0) goto libburn; if(o->toc==NULL) goto libburn; /* This is an emulated TOC */ toc_disc->toc= o->toc; for(t= toc_disc->toc; t!=NULL; t= t->next) session_count++; ret= isoburn_toc_new_arrays(toc_disc, session_count, session_count, 0); if(ret<=0) goto failure; t= toc_disc->toc; for(i= 0; i<session_count; i++) { toc_disc->sessions[i].track_pointers= toc_disc->track_pointers+i; toc_disc->sessions[i].track_count= 1; toc_disc->sessions[i].toc_entry= t; toc_disc->session_pointers[i]= toc_disc->sessions+i; toc_disc->tracks[i].toc_entry= t; toc_disc->track_pointers[i]= toc_disc->tracks+i; t= t->next; } toc_disc->session_count= session_count; toc_disc->track_count= session_count; return(toc_disc); libburn:; /* This is a libburn provided TOC */ toc_disc->disc= burn_drive_get_disc(d); if(toc_disc->disc == NULL) { failure:; free((char *) toc_disc); return(NULL); } s= burn_disc_get_sessions(toc_disc->disc, &session_count); open_sessions= burn_disc_get_incomplete_sessions(toc_disc->disc); for(i= 0; i < session_count + open_sessions; i++) { tracks = burn_session_get_tracks(s[i], &num_tracks); if(i == session_count + open_sessions - 1 && open_sessions > 0) { /* Do not count the invisible track of the last incomplete session */ num_tracks--; } track_count+= num_tracks; } if(session_count + open_sessions <= 0 || track_count <= 0) goto failure; ret= isoburn_toc_new_arrays(toc_disc, session_count + open_sessions, track_count, 0); if(ret<=0) goto failure; track_count= 0; for(i= 0; i < session_count + open_sessions; i++) { tracks = burn_session_get_tracks(s[i], &num_tracks); if(i == session_count + open_sessions - 1 && open_sessions > 0) num_tracks--; toc_disc->sessions[i].session= s[i]; toc_disc->sessions[i].track_pointers= toc_disc->track_pointers+track_count; toc_disc->sessions[i].track_count= num_tracks; toc_disc->session_pointers[i]= toc_disc->sessions+i; for(j= 0; j<num_tracks; j++) { toc_disc->tracks[track_count+j].track= tracks[j]; toc_disc->track_pointers[track_count+j]= toc_disc->tracks+(track_count+j); } track_count+= num_tracks; } toc_disc->session_count= session_count; toc_disc->incomplete_session_count= open_sessions; toc_disc->track_count= track_count; return(toc_disc); } off_t isoburn_toc_disc_get_sectors_v2(struct isoburn_toc_disc *disc) { struct isoburn_toc_entry *t; int num_sessions, num_tracks, open_sessions= 0, session_idx= -1; off_t ret= 0; struct burn_session **sessions; struct burn_track **tracks; struct burn_toc_entry entry; if(disc==NULL) return(0); if(disc->toc!=NULL) { for(t= disc->toc; t!=NULL; t= t->next) ret= t->start_lba + t->track_blocks; } else if(disc->disc!=NULL) { sessions= burn_disc_get_sessions(disc->disc, &num_sessions); open_sessions= burn_disc_get_incomplete_sessions(disc->disc); if(num_sessions + open_sessions > 0) { session_idx= num_sessions + open_sessions - 1; tracks = burn_session_get_tracks(sessions[session_idx], &num_tracks); if(open_sessions > 0) { /* Do not count the invisible track of the last incomplete session */ num_tracks--; } if(num_tracks <= 0) session_idx--; } if(session_idx >= 0) { tracks = burn_session_get_tracks(sessions[session_idx], &num_tracks); if(session_idx == num_sessions + open_sessions - 1 && open_sessions > 0) { /* Do not count the invisible track of the last incomplete session */ num_tracks--; } if(num_tracks > 0) { burn_track_get_entry(tracks[num_tracks - 1], &entry); if(entry.extensions_valid & 8) ret= entry.long_start_lba + entry.long_track_blocks; else if(entry.extensions_valid & 1) ret= entry.start_lba + entry.track_blocks; } } /* ret= burn_disc_get_sectors(disc->disc); */ } return(ret); } int isoburn_toc_disc_get_sectors(struct isoburn_toc_disc *disc) { off_t ret; ret= isoburn_toc_disc_get_sectors_v2(disc); if(ret > 0x7fffffff) return((int) 0x7fffffff); return((int) ret); } struct isoburn_toc_session **isoburn_toc_disc_get_sessions( struct isoburn_toc_disc *disc, int *num) { *num= disc->session_count; return(disc->session_pointers); } int isoburn_toc_disc_get_incmpl_sess(struct isoburn_toc_disc *disc) { return(disc->incomplete_session_count); } off_t isoburn_toc_session_get_sectors_v2(struct isoburn_toc_session *s) { struct isoburn_toc_entry *t; int i; off_t count= 0; if(s==NULL) return(0); if(s->toc_entry!=NULL) { t= s->toc_entry; for(i= 0; i<s->track_count; i++) { count+= t->track_blocks; t= t->next; } } else if(s->session!=NULL) count= burn_session_get_sectors_v2(s->session); return(count); } int isoburn_toc_session_get_sectors(struct isoburn_toc_session *s) { off_t ret; ret= isoburn_toc_session_get_sectors_v2(s); if(ret > 0x7fffffff) return((int) 0x7fffffff); return((int) ret); } int isoburn_toc_entry_finish(struct burn_toc_entry *entry, int session_no, int track_no, int flag) { int pmin, psec, pframe; entry->extensions_valid= 1 | 8; entry->adr= 1; entry->control= 4; entry->session= session_no & 255; entry->session_msb= (session_no >> 8) & 255; entry->point= track_no & 255; entry->point_msb= (track_no >> 8) & 255; if(entry->start_lba < 0) { pmin= 255; psec= 59; pframe=74; } else { burn_lba_to_msf(entry->start_lba, &pmin, &psec, &pframe); } if(pmin<=255) entry->pmin= pmin; else entry->pmin= 255; entry->psec= psec; entry->pframe= pframe; return(1); } void isoburn_toc_session_get_leadout_entry(struct isoburn_toc_session *s, struct burn_toc_entry *entry) { struct isoburn_toc_track *t; if(s==NULL) return; if(s->session!=NULL && s->toc_entry==NULL) { burn_session_get_leadout_entry(s->session, entry); return; } if(s->track_count<=0 || s->track_pointers==NULL || s->toc_entry==NULL) return; t= s->track_pointers[s->track_count-1]; entry->long_start_lba= t->toc_entry->start_lba + t->toc_entry->track_blocks; if(entry->long_start_lba > 0x7fffffff) { entry->start_lba= -1; } else { entry->start_lba= entry->long_start_lba; } entry->long_track_blocks= 0; entry->track_blocks= 0; isoburn_toc_entry_finish(entry, s->toc_entry->session, t->toc_entry->track_no, 0); } struct isoburn_toc_track **isoburn_toc_session_get_tracks( struct isoburn_toc_session *s, int *num) { *num= s->track_count; return(s->track_pointers); } void isoburn_toc_track_get_entry(struct isoburn_toc_track *t, struct burn_toc_entry *entry) { if(t==0) return; if(t->track!=NULL && t->toc_entry==NULL) { burn_track_get_entry(t->track, entry); return; } if(t->toc_entry==NULL) return; entry->long_start_lba= t->toc_entry->start_lba; if(entry->long_start_lba > 0x7fffffff) { entry->start_lba= -1; } else { entry->start_lba= entry->long_start_lba; } entry->long_track_blocks= t->toc_entry->track_blocks; if(entry->long_track_blocks > 0x7fffffff) { entry->track_blocks= -1; } else { entry->track_blocks= entry->long_track_blocks; } isoburn_toc_entry_finish(entry, t->toc_entry->session, t->toc_entry->track_no, 0); } int isoburn_toc_track_get_emul_v2(struct isoburn_toc_track *t, off_t *start_lba, off_t *image_blocks, char volid[33], int flag) { if(t->toc_entry == NULL) return(0); if(t->toc_entry->volid == NULL) return(0); *start_lba= t->toc_entry->start_lba; *image_blocks= t->toc_entry->track_blocks; strncpy(volid, t->toc_entry->volid, 32); volid[32]= 0; return(1); } int isoburn_toc_track_get_emul(struct isoburn_toc_track *t, int *start_lba, int *image_blocks, char volid[33], int flag) { int ret; off_t long_lba, long_image_blocks; ret= isoburn_toc_track_get_emul_v2(t, &long_lba, &long_image_blocks, volid, flag); if(ret <= 0) return(ret); if(long_lba > 0x7fffffff) { *start_lba= 0x7fffffff; ret= 0; } else { *start_lba= long_lba; } if(long_image_blocks > 0x7fffffff) { *image_blocks= 0x7fffffff; ret= 0; } else { *image_blocks= long_image_blocks; } return(ret); } void isoburn_toc_disc_free(struct isoburn_toc_disc *d) { if(d->disc!=NULL) burn_disc_free(d->disc); isoburn_toc_destroy_arrays(d, 0); free((char *) d); } int isoburn_get_track_lba(struct isoburn_toc_track *track, off_t *lba, int flag) { struct burn_toc_entry entry; isoburn_toc_track_get_entry(track, &entry); if (entry.extensions_valid & 8) { *lba= entry.long_start_lba; } else if (entry.extensions_valid & 1) { *lba= entry.start_lba; } else { *lba= burn_msf_to_lba(entry.pmin, entry.psec, entry.pframe); } return(1); } int isoburn_drive_set_msgs_submit(struct burn_drive *d, int (*msgs_submit)(void *handle, int error_code, char msg_text[], int os_errno, char severity[], int flag), void *submit_handle, int submit_flag, int flag) { struct isoburn *o; int ret; ret= isoburn_find_emulator(&o, d, 0); if(ret<0 || o==NULL) return(-1); o->msgs_submit= msgs_submit; o->msgs_submit_handle= submit_handle; o->msgs_submit_flag= submit_flag; return(1); } /* YYYYMMDDhhmmsscc[LOC] */ /* 2010040711405800 */ static int isoburn_mktime_ecma119_time17(struct tm *erg, char *text, int flag) { int i, l, num, utc= 1; struct tm norm_tm; memset(erg, 0, sizeof(*erg)); erg->tm_isdst= -1; l= strlen(text); if(l == 19) { if(strcmp(text + 16, "LOC") != 0) return(0); utc= 0; l= 16; } if(l != 16) return(0); for(i= 0; i < l; i++) if(text[i] < '0' || text[i] > '9') return(0); num= 0; for(i= 0; i < 4; i++) num= num * 10 + text[i] - '0'; if(num < 1970 || num > 3000) return(0); erg->tm_year = num - 1900; erg->tm_mon= 10*(text[4]-'0')+text[5]-'0'-1; if(erg->tm_mon > 12) return(0); erg->tm_mday= 10*(text[6]-'0')+text[7]-'0'; if(erg->tm_mday > 31) return(0); erg->tm_hour= 10*(text[8]-'0')+text[9]-'0'; if(erg->tm_hour > 23) return(0); erg->tm_min= 10*(text[10]-'0')+text[11]-'0'; if(erg->tm_min > 59) return(0); erg->tm_sec= 10*(text[12]-'0')+text[13]-'0'; if(erg->tm_sec > 59) return(0); /* Let mktime(3) compute erg->tm_wday and erg->tm_yday */ memcpy(&norm_tm, erg, sizeof(struct tm)); mktime(&norm_tm); erg->tm_wday= norm_tm.tm_wday; erg->tm_yday= norm_tm.tm_yday; return(1 + !utc); } /* Convert to time_t: YYYYMMDDhhmmssccN (N = 15 minutes Offset from GMT: -48 West to +52 east) */ static int isoburn_decode_ecma119_time17(time_t *seconds, char *code, int flag) { int ret; char digits[17]; struct tm erg; *seconds= 0; strncpy(digits, code, 16); digits[16]= 0; ret= isoburn_mktime_ecma119_time17(&erg, digits, 0); if(ret <= 0) return(ret); *seconds= mktime(&erg); /* Revert ECMA-119 timezone offset */ *seconds-= (int) code[16] * 60 * 15; /* Convert from local time to GMT */ #ifdef HAVE_TM_GMTOFF *seconds+= erg.tm_gmtoff; #else if(erg.tm_isdst < 0) erg.tm_isdst = 0; #ifndef Libburnia_timezonE #define Libburnia_timezonE timezone #endif *seconds-= Libburnia_timezonE - erg.tm_isdst * 3600; #endif return(1); } /* @param flag bit0= with adr_mode 3: adr_value might be 16 blocks too high bit1= insist in seeing a disc object with at least one session bit2= with adr_mode 4: use adr_value as regular expression with adr_mode 5 to 7: use creation time rather than modification time */ int isoburn_set_msc1(struct burn_drive *d, int adr_mode, char *adr_value, int flag) { int ret, num_sessions= 0, num_tracks, i, j, total_tracks; int re_valid= 0, track_count= 0, info_type; off_t adr_num, lba, best_lba, size; double adr_double; time_t start_time= 0, last_pacifier= 0, now, seconds; char volid[33], *msg= NULL; struct isoburn *o; struct isoburn_toc_disc *disc= NULL; struct isoburn_toc_session **sessions= NULL; struct isoburn_toc_track **tracks= NULL; static char mode_names[][20]= {"auto", "session", "track", "lba", "volid", "at_time", "not_after", "not_before", "before", "after"}; static int max_mode_names= 9; regex_t re; regmatch_t match[1]; enum burn_disc_status s; ret= isoburn_find_emulator(&o, d, 0); if(ret<0) return(-1); if(o==NULL) return(-1); msg= calloc(1, 160); if(msg == NULL) {ret= -1; goto ex;} start_time= last_pacifier= time(NULL); adr_num= -1; adr_double= -1.0; sscanf(adr_value, "%lf", &adr_double); if(adr_double >= -1.0e16 && adr_double <= 1.0e16) /* ~ 2 exp 53 */ adr_num= adr_double; if(adr_mode!=3 || (flag & 2)) { disc= isoburn_toc_drive_get_disc(d); if(disc==NULL) { not_found:; if(adr_mode<0 || adr_mode>max_mode_names) goto unknown_mode; sprintf(msg, "Failed to find \"%s\" \"%s%s\"", mode_names[adr_mode], adr_mode >= 5 && adr_mode <= 9 ? "=" : "", strlen(adr_value)<=80 ? adr_value : "-oversized-string-"); isoburn_msgs_submit(o, 0x00060000, msg, 0, "FAILURE", 0); ret= 0; goto ex; } sessions= isoburn_toc_disc_get_sessions(disc, &num_sessions); if(sessions==NULL || num_sessions<=0) goto not_found; } if(adr_mode==0) { /* Set fabricated_msc1 to last session in TOC */ tracks= isoburn_toc_session_get_tracks(sessions[num_sessions-1], &num_tracks); if(tracks==NULL || num_tracks<=0) goto not_found; isoburn_get_track_lba(tracks[0], &(o->fabricated_msc1), 0); } else if(adr_mode==1) { /* Use adr_num as session index (first session is 1, not 0) */ if(adr_num<1 || adr_num>num_sessions) goto not_found; tracks= isoburn_toc_session_get_tracks(sessions[adr_num-1], &num_tracks); if(tracks==NULL || num_tracks<=0) goto not_found; isoburn_get_track_lba(tracks[0], &(o->fabricated_msc1), 0); } else if(adr_mode==2) { /* use adr_num as track index */ total_tracks= 0; for(i=0; i<num_sessions; i++) { tracks= isoburn_toc_session_get_tracks(sessions[i], &num_tracks); if(tracks==NULL) continue; for(j= 0; j<num_tracks; j++) { total_tracks++; if(total_tracks==adr_num) { isoburn_get_track_lba(tracks[j], &(o->fabricated_msc1), 0); ret= 1; goto ex; } } } goto not_found; } else if(adr_mode==3) { o->fabricated_msc1= adr_num; s= isoburn_disc_get_status(d); if(o->fabricated_msc1 > 0 && s != BURN_DISC_FULL && s != BURN_DISC_APPENDABLE) { isoburn_msgs_submit(o, 0x00060000, "Non-zero load offset given with blank input media", 0, "FAILURE", 0); ret= 0; goto ex; } if((flag & 1) && o->fabricated_msc1 >= 16) { /* adr_num is possibly 16 blocks too high */ ret= isoburn_read_iso_head_v2(d, o->fabricated_msc1, &size, volid, 1 | (1 << 14)); if(ret==2) o->fabricated_msc1-= 16; } } else if(adr_mode >= 4 && adr_mode <= 9) { /* search for volume id that is equal to adr_value */ if(adr_mode == 4 && (flag & 4)) { ret= regcomp(&re, adr_value, 0); if(ret != 0) flag&= ~4; else re_valid= 1; } best_lba= -1; for(i=0; i<num_sessions; i++) { tracks= isoburn_toc_session_get_tracks(sessions[i], &num_tracks); if(tracks==NULL) continue; for(j= 0; j<num_tracks; j++) { now= time(NULL); if(now - last_pacifier >= 5 && track_count > 0) { last_pacifier= now; sprintf(msg, "Scanned %d tracks for matching volid or timestamp in %.f seconds", track_count, (double) (now - start_time)); isoburn_msgs_submit(o, 0x00060000, msg, 0, "UPDATE", 0); } track_count++; ret= isoburn_toc_track_get_emul_v2(tracks[0], &lba, &size, volid, 0); if(ret < 0) continue; if(ret == 0 || (adr_mode >= 5 && adr_mode <= 9)) { isoburn_get_track_lba(tracks[0], &lba, 0); info_type= (adr_mode == 4 ? 1 : 3 + !!(flag & 4)); ret= isoburn_read_iso_head_v2(d, lba, &size, volid, info_type); if(ret<=0) continue; } if(adr_mode == 4) { if(flag & 4) { ret= regexec(&re, volid, 1, match, 0); if(ret != 0) continue; } else { if(strcmp(volid, adr_value)!=0) continue; } } else { /* Convert volid text to seconds GMT */ ret= isoburn_decode_ecma119_time17(&seconds, volid, 0); if(ret <= 0) continue; if(adr_mode == 5) { if(seconds != (time_t) adr_num) continue; } else if(adr_mode == 6) { if(seconds > (time_t) adr_num) continue; } else if(adr_mode == 7) { if(seconds >= (time_t) adr_num) { best_lba= lba; goto take_best_lba; } } else if(adr_mode == 8) { if(seconds >= (time_t) adr_num) continue; } else if(adr_mode == 9) { if(seconds > (time_t) adr_num) { best_lba= lba; goto take_best_lba; } } } best_lba= lba; } } if(best_lba<0) goto not_found; take_best_lba:; o->fabricated_msc1= best_lba; } else { unknown_mode:; sprintf(msg, "Program error: Unknown msc1 address mode %d", adr_mode); isoburn_msgs_submit(o, 0x00060000, msg, 0, "FATAL", 0); ret= 0; goto ex; } ret= 1; ex:; if(start_time != last_pacifier && track_count > 0) { now= time(NULL); sprintf(msg, "Scanned %d tracks for matching volid or timestamp in %.f seconds", track_count, (double) (now - start_time)); isoburn_msgs_submit(o, 0x00060000, msg, 0, "UPDATE", 0); } if(disc!=NULL) isoburn_toc_disc_free(disc); if((flag & 4) && re_valid) regfree(&re); if(msg != NULL) free(msg); return(ret); } int isoburn_get_mount_params_v2(struct burn_drive *d, int adr_mode, char *adr_value, off_t *lba, int *track, int *session, char volid[33], int flag) { int ret, total_tracks, num_sessions, num_tracks, i, j, is_iso= 0; off_t msc1_mem, track_lba, size; struct isoburn *o; struct isoburn_toc_disc *disc= NULL; struct isoburn_toc_session **sessions= NULL; struct isoburn_toc_track **tracks= NULL; *lba= *track= *session= -1; volid[0]= 0; ret= isoburn_find_emulator(&o, d, 0); if(ret < 0 || o == NULL) return(-1); msc1_mem= o->fabricated_msc1; ret= isoburn_set_msc1(d, adr_mode, adr_value, 2 | (flag & 4)); if(ret <= 0) return(ret); *lba= o->fabricated_msc1; disc= isoburn_toc_drive_get_disc(d); if(disc==NULL) {ret= -1; goto ex;} /* cannot happen because checked by isoburn_set_msc1 */ sessions= isoburn_toc_disc_get_sessions(disc, &num_sessions); if(sessions==NULL || num_sessions<=0) {ret= -1; goto ex;} /* cannot happen because checked by isoburn_set_msc1 */ total_tracks= 0; for(i=0; i<num_sessions && *session < 0; i++) { tracks= isoburn_toc_session_get_tracks(sessions[i], &num_tracks); if(tracks==NULL) continue; for(j= 0; j<num_tracks && *track < 0; j++) { total_tracks++; isoburn_get_track_lba(tracks[j], &track_lba, 0); if(track_lba == *lba) { *track= total_tracks; *session= i + 1; } } } ret= isoburn_read_iso_head_v2(d, *lba, &size, volid, 1); if(ret <= 0) volid[0]= 0; else is_iso= 1; ex:; o->fabricated_msc1= msc1_mem; if(disc != NULL) isoburn_toc_disc_free(disc); return(2 - is_iso); } int isoburn_get_mount_params(struct burn_drive *d, int adr_mode, char *adr_value, int *lba, int *track, int *session, char volid[33], int flag) { int ret; off_t long_lba; ret= isoburn_get_mount_params_v2(d, adr_mode, adr_value, &long_lba, track, session, volid, flag); if(ret <= 0) return(ret); if(long_lba > 0x7fffffff) { *lba= 0x7fffffff; ret= 0; } else { *lba= long_lba; } return(ret); } /* data source for libisoburn. Copyright 2007 - 2012 Vreixo Formoso Lopes <metalpain2002@yahoo.es> and Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <stdlib.h> #include <string.h> #include <stdio.h> #ifndef Xorriso_standalonE #include <libburn/libburn.h> #include <libisofs/libisofs.h> #else /* ! Xorriso_standalonE */ #include "../libisofs/libisofs.h" #include "../libburn/libburn.h" #endif /* Xorriso_standalonE */ #include "isoburn.h" /* Cached reading of image tree data by multiple tiles */ /* Debugging only: This reports cache loads on stderr. #define Libisoburn_read_cache_reporT 1 */ struct isoburn_cache_tile { char *cache_data; uint32_t cache_lba; uint32_t last_error_lba; uint32_t last_aligned_error_lba; int cache_hits; int age; }; struct isoburn_cached_drive { struct burn_drive *drive; struct isoburn_cache_tile **tiles; int num_tiles; int tile_blocks; int current_age; /** Offset to be applied to all block addresses to compensate for an eventual displacement of the block addresses relative to the image start block address that was assumed when the image was created. E.g. if track number 2 gets copied into a disk file and shall then be loaded as ISO filesystem. If displacement_sign is 1 then the displacement number will be added to .read_block() addresses, if -1 it will be subtracted. Else it will be ignored. */ uint32_t displacement; int displacement_sign; }; #define Libisoburn_max_agE 2000000000 static int ds_inc_age(struct isoburn_cached_drive *icd, int idx, int flag); int ds_read_block(IsoDataSource *src, uint32_t lba, uint8_t *buffer) { int ret, i, oldest, oldest_age; struct burn_drive *d; off_t count; uint32_t aligned_lba; char msg[80]; struct isoburn_cache_tile **tiles; struct isoburn_cached_drive *icd; if(src == NULL || buffer == NULL) /* It is not required by the specs of libisofs but implicitly assumed by its current implementation that a data source read result <0 is a valid libisofs error code. */ return ISO_NULL_POINTER; icd = (struct isoburn_cached_drive *) src->data; d = (struct burn_drive*) icd->drive; if(d == NULL) { /* This would happen if libisoburn saw output data in the fifo and performed early drive release and afterwards libisofs still tries to read data. That would constitute a bad conceptual problem in libisoburn. */ isoburn_msgs_submit(NULL, 0x00060000, "Programming error: Drive released while libisofs still attempts to read", 0, "FATAL", 0); return ISO_ASSERT_FAILURE; } tiles = icd->tiles; if(icd->displacement_sign == 1) { if(lba + icd->displacement < lba) { address_rollover:; return ISO_DISPLACE_ROLLOVER; } else lba += icd->displacement; } else if(icd->displacement_sign == -1) { if(lba < icd->displacement ) goto address_rollover; else lba -= icd->displacement; } aligned_lba= lba & ~(icd->tile_blocks - 1); for (i = 0; i < icd->num_tiles; i++) { if(aligned_lba == tiles[i]->cache_lba && tiles[i]->cache_lba != 0xffffffff) { (tiles[i]->cache_hits)++; memcpy(buffer, tiles[i]->cache_data + (lba - aligned_lba) * 2048, 2048); count= 2048; ds_inc_age(icd, i, 0); return 1; } } /* find oldest tile */ oldest_age= Libisoburn_max_agE; oldest= 0; for(i = 0; i < icd->num_tiles; i++) { if(tiles[i]->cache_lba == 0xffffffff) { oldest= i; break; } if(tiles[i]->age < oldest_age) { oldest_age= tiles[i]->age; oldest= i; } } tiles[oldest]->cache_lba= 0xffffffff; /* invalidate cache */ if(tiles[oldest]->last_aligned_error_lba == aligned_lba) { ret = 0; } else { ret = burn_read_data(d, (off_t) aligned_lba * (off_t) 2048, (char *) tiles[oldest]->cache_data, icd->tile_blocks * 2048, &count, 2); } if (ret <= 0 ) { tiles[oldest]->last_aligned_error_lba = aligned_lba; /* Read-ahead failure ? Try to read 2048 directly. */ if(tiles[oldest]->last_error_lba == lba) ret = 0; else ret = burn_read_data(d, (off_t) lba * (off_t) 2048, (char *) buffer, 2048, &count, 0); if (ret > 0) return 1; tiles[oldest]->last_error_lba = lba; sprintf(msg, "ds_read_block(%lu) returns %lX", (unsigned long) lba, (unsigned long) ret); isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "DEBUG", 0); return ISO_DATA_SOURCE_MISHAP; } #ifdef Libisoburn_read_cache_reporT fprintf(stderr, "Tile %2.2d : After %3d hits, new load from %8x , count= %d\n", oldest, tiles[oldest]->cache_hits, aligned_lba, (int) count); #endif tiles[oldest]->cache_lba= aligned_lba; tiles[oldest]->cache_hits= 1; ds_inc_age(icd, oldest, 0); memcpy(buffer, tiles[oldest]->cache_data + (lba - aligned_lba) * 2048, 2048); count= 2048; return 1; } static int ds_open(IsoDataSource *src) { /* nothing to do, device is always grabbed */ return 1; } static int ds_close(IsoDataSource *src) { /* nothing to do, device is always grabbed */ return 1; } static int isoburn_cache_tile_destroy(struct isoburn_cache_tile **o, int flag) { if (*o == NULL) return(0); if ((*o)->cache_data != NULL) free((*o)->cache_data); free(*o); *o = NULL; return(1); } static int isoburn_cache_tile_new(struct isoburn_cache_tile **o, int tile_blocks, int flag) { struct isoburn_cache_tile *t; *o = t = calloc(1, sizeof(struct isoburn_cache_tile)); if (t == NULL) goto fail; t->cache_data = NULL; t->cache_lba = 0xffffffff; t->cache_hits = 0; t->last_error_lba = 0xffffffff; t->last_aligned_error_lba = 0xffffffff; t->age= 0; t->cache_data = calloc(1, tile_blocks * 2048); if (t->cache_data == NULL) goto fail; return(1); fail:; isoburn_cache_tile_destroy(o, 0); return(-1); } static int isoburn_cached_drive_destroy(struct isoburn_cached_drive **o, int flag) { struct isoburn_cached_drive *c; int i; if (*o == NULL) return(0); c= *o; if (c->tiles != NULL) { for (i = 0; i < c->num_tiles; i++) isoburn_cache_tile_destroy(&(c->tiles[i]), 0); free(c->tiles); } free(c); *o= NULL; return(1); } static int isoburn_cached_drive_new(struct isoburn_cached_drive **o, struct burn_drive *d, int cache_tiles, int tile_blocks, int flag) { struct isoburn_cached_drive *icd; int i, ret; *o = icd = calloc(1,sizeof(struct isoburn_cached_drive)); if (*o == NULL) return(-1); icd->drive = d; icd->tiles = NULL; icd->num_tiles = cache_tiles; icd->tile_blocks = tile_blocks; icd->current_age = 0; icd->displacement = 0; icd->displacement_sign = 0; icd->tiles = calloc(1, sizeof(struct isoburn_cache_tile *) * icd->num_tiles); if (icd->tiles == NULL) goto fail; for (i = 0; i < icd->num_tiles; i++) { ret = isoburn_cache_tile_new(&(icd->tiles[i]), icd->tile_blocks, 0); if (ret <= 0) goto fail; } return(1); fail:; isoburn_cached_drive_destroy(o, 0); return(-1); } static void ds_free_data(IsoDataSource *src) { struct isoburn_cached_drive *icd; if(src->data != NULL) { icd= (struct isoburn_cached_drive *) src->data; isoburn_cached_drive_destroy(&icd, 0); } src->data= NULL; } int isoburn_data_source_shutdown(IsoDataSource *src, int flag) { struct isoburn_cached_drive *icd; if(src==NULL) return(0); icd= (struct isoburn_cached_drive *) src->data; icd->drive= NULL; return(1); } IsoDataSource *isoburn_data_source_new(struct burn_drive *d, uint32_t displacement, int displacement_sign, int cache_tiles, int tile_blocks) { IsoDataSource *src; struct isoburn_cached_drive *icd= NULL; int ret; if (d==NULL) return NULL; src = malloc(sizeof(IsoDataSource)); if (src == NULL) return NULL; ret = isoburn_cached_drive_new(&icd, d, cache_tiles, tile_blocks, 0); if (ret <= 0) { free(src); return NULL; } src->version = 0; src->refcount = 1; src->read_block = ds_read_block; src->open = ds_open; src->close = ds_close; src->free_data = ds_free_data; src->data = icd; icd->displacement = displacement; icd->displacement_sign = displacement_sign; return src; } static int ds_inc_age(struct isoburn_cached_drive *icd, int idx, int flag) { int i; (icd->current_age)++; if(icd->current_age>=Libisoburn_max_agE) { /* reset all ages (allow waste) */ for(i = 0; i < icd->num_tiles; i++) (icd->tiles)[i]->age= 0; icd->current_age= 1; } (icd->tiles)[idx]->age= icd->current_age; return(1); } /* cc -g -c isoburn.c */ /* Class core of libisoburn. Copyright 2007 - 2009 Vreixo Formoso Lopes <metalpain2002@yahoo.es> Copyright 2007 - 2023 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /* ( derived from stub generated by CgeN on Sat, 01 Sep 2007 12:04:36 GMT ) */ #include <sys/types.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #ifndef Xorriso_standalonE #include <libburn/libburn.h> #include <libisofs/libisofs.h> #else /* ! Xorriso_standalonE */ #include "../libisofs/libisofs.h" #include "../libburn/libburn.h" #endif /* Xorriso_standalonE */ #include "libisoburn.h" #include "isoburn.h" /* Default values for application provided msgs_submit methods. To be attached to newly acquired drives. */ int (*libisoburn_default_msgs_submit) (void *handle, int error_code, char msg_text[], int os_errno, char severity[], int flag)= NULL; void *libisoburn_default_msgs_submit_handle= NULL; int libisoburn_default_msgs_submit_flag= 0; /* ----------------------- isoburn_toc_entry ---------------------- */ int isoburn_toc_entry_new(struct isoburn_toc_entry **objpt, struct isoburn_toc_entry *boss, int flag) { struct isoburn_toc_entry *o, *s; *objpt= o= (struct isoburn_toc_entry *) malloc(sizeof(struct isoburn_toc_entry)); if(o==NULL) { isoburn_msgs_submit(NULL, 0x00060000, "Cannot allocate memory for isoburn toc entry", 0, "FATAL", 0); return(-1); } o->session= 0; o->track_no= 0; o->start_lba= -1; o->track_blocks= 0; o->volid= NULL; o->next= NULL; if(boss!=NULL) { for(s= boss; s->next!=NULL; s= s->next); s->next= o; } return(1); } /* @param flag bit0= delete all subordinates too */ int isoburn_toc_entry_destroy(struct isoburn_toc_entry **o, int flag) { if(*o==NULL) return(0); if(flag&1) isoburn_toc_entry_destroy(&((*o)->next), flag); if((*o)->volid != NULL) free((*o)->volid); free((char *) (*o)); *o= NULL; return(1); } /* --------------------- end isoburn_toc_entry -------------------- */ /* -------------------------- isoburn ----------------------- */ /* The global list of isoburn objects. Usually there is only one. >>> we are not ready for multiple control threads yet. See >>> mutex . Multiple burns under one control thread should work. */ struct isoburn *isoburn_list_start= NULL; int isoburn_new(struct isoburn **objpt, int flag) { struct isoburn *o; int ret; *objpt= o= (struct isoburn *) malloc(sizeof(struct isoburn)); if(o==NULL) { isoburn_msgs_submit(NULL, 0x00060000, "Cannot allocate memory for isoburn control object", 0, "FATAL", 0); return(-1); } o->drive= NULL; o->emulation_mode= 0; o->fabricated_msc1= -1; o->fabricated_msc2= -1; o->zero_nwa= Libisoburn_overwriteable_starT; o->min_start_byte= o->zero_nwa * 2048; o->nwa= o->zero_nwa; o->truncate= 0; o->iso_source= NULL; o->fabricated_disc_status= BURN_DISC_UNREADY; o->media_read_error= 0; o->toc= NULL; o->wrote_well= -1; o->loaded_partition_offset= 0; o->target_iso_head_size= Libisoburn_target_head_sizE; o->target_iso_head= NULL; o->image= NULL; o->image_start_lba= -1; o->iso_data_source= NULL; o->read_pacifier= NULL; o->read_pacifier_handle= NULL; o->msgs_submit= NULL; o->msgs_submit_handle= NULL; o->msgs_submit_flag= 0; o->do_tao= 0; o->do_fsync= 1; o->prev= NULL; o->next= NULL; o->target_iso_head= calloc(1, o->target_iso_head_size); if(o->target_iso_head == NULL) { isoburn_report_iso_error(ISO_OUT_OF_MEM, "Cannot allocate overwrite buffer", 0, "FATAL", 0); goto failed; } ret= iso_image_new("ISOIMAGE", &o->image); if(ret<0) { isoburn_report_iso_error(ret, "Cannot create image object", 0, "FATAL", 0); goto failed; } ret= isoburn_root_defaults(o->image, 0); if(ret <= 0) goto failed; isoburn_link(o, isoburn_list_start, 1); return(1); failed:; isoburn_destroy(objpt, 0); return(-1); } int isoburn_destroy(struct isoburn **objpt, int flag) { struct isoburn *o; o= *objpt; if(o==NULL) return(0); /* >>> mutex */ if(o==isoburn_list_start) isoburn_list_start= o->next; if(o->prev!=NULL) o->prev->next= o->next; if(o->next!=NULL) o->next->prev= o->prev; /* >>> end mutex */ if(o->image!=NULL) iso_image_unref(o->image); if(o->toc!=NULL) isoburn_toc_entry_destroy(&(o->toc), 1); /* all */ if(o->iso_source!=NULL) burn_source_free(o->iso_source); if(o->iso_data_source!=NULL) iso_data_source_unref(o->iso_data_source); if(o->target_iso_head != NULL) free(o->target_iso_head); free((char *) o); *objpt= NULL; return(1); } int isoburn_destroy_all(struct isoburn **objpt, int flag) { struct isoburn *o,*n; o= *objpt; if(o==NULL) return(0); for(;o->prev!=NULL;o= o->prev); for(;o!=NULL;o= n) { n= o->next; isoburn_destroy(&o,0); } *objpt= NULL; return(1); } int isoburn_get_target_image(struct isoburn *o, IsoImage **pt, int flag) { *pt= o->image; return(1); } int isoburn_get_prev(struct isoburn *o, struct isoburn **pt, int flag) { *pt= o->prev; return(1); } int isoburn_get_next(struct isoburn *o, struct isoburn **pt, int flag) { *pt= o->next; return(1); } int isoburn_link(struct isoburn *o, struct isoburn *link, int flag) /* bit0= insert as link->prev rather than as link->next */ { /* >>> mutex */ if(isoburn_list_start==NULL || (isoburn_list_start==link && (flag&1))) isoburn_list_start= o; if(o->prev!=NULL) o->prev->next= o->next; if(o->next!=NULL) o->next->prev= o->prev; o->prev= o->next= NULL; if(link==NULL) return(1); if(flag&1) { o->next= link; o->prev= link->prev; if(o->prev!=NULL) o->prev->next= o; link->prev= o; } else { o->prev= link; o->next= link->next; if(o->next!=NULL) o->next->prev= o; link->next= o; } /* >>> end mutex */ return(1); } int isoburn_count(struct isoburn *o, int flag) /* flag: bit1= count from start of list */ { int counter= 0; if(flag&2) for(;o->prev!=NULL;o= o->prev); for(;o!=NULL;o= o->next) counter++; return(counter); } int isoburn_by_idx(struct isoburn *o, int idx, struct isoburn **pt, int flag) /* flag: bit0= fetch first (idx<0) or last (idx>0) item in list bit1= address from start of list */ { int i,abs_idx; struct isoburn *npt; if(flag&2) for(;o->prev!=NULL;o= o->prev); abs_idx= (idx>0?idx:-idx); *pt= o; for(i= 0;(i<abs_idx || (flag&1)) && *pt!=NULL;i++) { if(idx>0) npt= o->next; else npt= o->prev; if(npt==NULL && (flag&1)) break; *pt= npt; } return(*pt!=NULL); } int isoburn_find_by_drive(struct isoburn **pt, struct burn_drive *d, int flag) { struct isoburn *o; *pt= NULL; for(o= isoburn_list_start;o!=NULL;o= o->next) if(o->drive==d) { *pt= o; return(1); } return(0); } int isoburn_msgs_submit(struct isoburn *o, int error_code, char msg_text[], int os_errno, char severity[], int flag) { int ret, use_drive_method= 0; if(o!=NULL) if(o->msgs_submit!=NULL) use_drive_method= 1; if(use_drive_method) { ret= o->msgs_submit(o->msgs_submit_handle, error_code, msg_text, os_errno, severity, o->msgs_submit_flag); return(ret); } if(libisoburn_default_msgs_submit != NULL) { ret= libisoburn_default_msgs_submit(libisoburn_default_msgs_submit_handle, error_code, msg_text, os_errno, severity, libisoburn_default_msgs_submit_flag); return(ret); } /* Fallback: use message queue of libburn */ burn_msgs_submit(error_code, msg_text, os_errno, severity, NULL); return(1); } /** Check whether the size of target_iso_head matches the given partition offset. Eventually adjust size. */ int isoburn_adjust_target_iso_head(struct isoburn *o, uint32_t offst, int flag) { uint8_t *new_buf; uint32_t new_size; if((uint32_t) o->target_iso_head_size == Libisoburn_target_head_sizE + 2048 * offst) return(1); new_size= Libisoburn_target_head_sizE + 2048 * offst; new_buf= calloc(1, new_size); if(new_buf == NULL) { isoburn_msgs_submit(o, 0x00060000, "Cannot re-allocate overwrite buffer", 0, "FATAL", 0); return(-1); } memcpy(new_buf, o->target_iso_head, (uint32_t) o->target_iso_head_size < new_size ? (uint32_t) o->target_iso_head_size : new_size); free(o->target_iso_head); o->target_iso_head= new_buf; o->target_iso_head_size= new_size; if(o->nwa == o->zero_nwa) o->nwa= Libisoburn_overwriteable_starT + offst; o->zero_nwa= Libisoburn_overwriteable_starT + offst; return(1); } /* @param out_o The output isoburn object may be NULL if no real write run is desired with opts. @param flag bit0= modifying rather than growing */ static int isoburn_make_iso_write_opts(struct isoburn *out_o, struct isoburn_imgen_opts *opts, int fifo_chunks, IsoWriteOpts *wopts, int flag) { int ret, rec_mtime, new_img, i, guid_mode; off_t lba, nwa; struct burn_drive *out_d; new_img= flag&1; iso_write_opts_set_will_cancel(wopts, opts->will_cancel); iso_write_opts_set_iso_level(wopts, opts->level); iso_write_opts_set_rockridge(wopts, opts->rockridge); iso_write_opts_set_joliet(wopts, opts->joliet); iso_write_opts_set_hfsplus(wopts, opts->hfsplus); iso_write_opts_set_hfsp_block_size(wopts, opts->hfsp_block_size, opts->apm_block_size); iso_write_opts_set_fat(wopts, opts->fat); iso_write_opts_set_iso1999(wopts, opts->iso1999); iso_write_opts_set_hardlinks(wopts, opts->hardlinks); if(opts->hardlinks) iso_write_opts_set_rrip_1_10_px_ino(wopts, 1); iso_write_opts_set_aaip(wopts, opts->aaip); iso_write_opts_set_old_empty(wopts, !!opts->old_empty); iso_write_opts_set_untranslated_name_len(wopts, opts->untranslated_name_len); iso_write_opts_set_allow_dir_id_ext(wopts, opts->allow_dir_id_ext); iso_write_opts_set_omit_version_numbers(wopts, opts->omit_version_numbers); iso_write_opts_set_allow_deep_paths(wopts, opts->allow_deep_paths); iso_write_opts_set_rr_reloc(wopts, opts->rr_reloc_dir, opts->rr_reloc_flags); iso_write_opts_set_allow_longer_paths(wopts, opts->allow_longer_paths); iso_write_opts_set_max_37_char_filenames(wopts, opts->max_37_char_filenames); iso_write_opts_set_no_force_dots(wopts, opts->no_force_dots); iso_write_opts_set_allow_lowercase(wopts, opts->allow_lowercase); iso_write_opts_set_allow_full_ascii(wopts, opts->allow_full_ascii); iso_write_opts_set_allow_7bit_ascii(wopts, opts->allow_7bit_ascii); iso_write_opts_set_relaxed_vol_atts(wopts, 1); iso_write_opts_set_joliet_longer_paths(wopts, opts->joliet_longer_paths); iso_write_opts_set_joliet_long_names(wopts, opts->joliet_long_names); iso_write_opts_set_joliet_utf16(wopts, opts->joliet_utf16); iso_write_opts_set_always_gmt(wopts, opts->always_gmt); iso_write_opts_set_rrip_version_1_10(wopts, opts->rrip_version_1_10); rec_mtime= 0; if(opts->dir_rec_mtime) rec_mtime|= 1; else rec_mtime|= (1 << 14); if(opts->joliet_rec_mtime) rec_mtime|= 2; if(opts->iso1999_rec_mtime) rec_mtime|= 4; iso_write_opts_set_dir_rec_mtime(wopts, rec_mtime); iso_write_opts_set_aaip_susp_1_10(wopts, opts->aaip_susp_1_10); iso_write_opts_set_sort_files(wopts, opts->sort_files); iso_write_opts_set_record_md5(wopts, opts->session_md5, opts->file_md5 & 3); if(opts->scdbackup_tag_name[0] && opts->scdbackup_tag_time[0]) iso_write_opts_set_scdbackup_tag(wopts, opts->scdbackup_tag_name, opts->scdbackup_tag_time, opts->scdbackup_tag_written); iso_write_opts_set_replace_mode(wopts, opts->replace_dir_mode, opts->replace_file_mode, opts->replace_uid, opts->replace_gid); iso_write_opts_set_default_dir_mode(wopts, opts->dir_mode); iso_write_opts_set_default_file_mode(wopts, opts->file_mode); iso_write_opts_set_default_uid(wopts, opts->uid); iso_write_opts_set_default_gid(wopts, opts->gid); iso_write_opts_set_output_charset(wopts, opts->output_charset); iso_write_opts_set_fifo_size(wopts, fifo_chunks); ret = iso_write_opts_set_system_area(wopts, opts->system_area_data, opts->system_area_options, 0); if (ret < 0) { isoburn_report_iso_error(ret, "Cannot set content of System Area", 0, "FAILURE", 0); {ret= -1; goto ex;} } iso_write_opts_set_pvd_times(wopts, opts->vol_creation_time, opts->vol_modification_time, opts->vol_expiration_time, opts->vol_effective_time, opts->vol_uuid); guid_mode= opts->gpt_guid_mode; if(opts->vol_uuid[0] == 0 && opts->gpt_guid_mode == 2) guid_mode= 0; iso_write_opts_set_gpt_guid(wopts, opts->gpt_guid, guid_mode); ret = iso_write_opts_set_max_ce_entries(wopts, opts->max_ce_entries, opts->max_ce_entries_flag); if (ret < 0) { isoburn_report_iso_error(ret, "Cannot set limit for number of CE entries", 0, "FAILURE", 0); {ret= -1; goto ex;} } iso_write_opts_attach_jte(wopts, opts->libjte_handle); iso_write_opts_set_hfsp_serial_number(wopts, opts->hfsp_serial_number); if(out_o != NULL) { out_d= out_o->drive; ret= isoburn_adjust_target_iso_head(out_o, opts->partition_offset, 0); if(ret <= 0) {ret= -1; goto ex;} if(out_o->nwa < out_o->zero_nwa) out_o->zero_nwa= 0; if(opts->no_emul_toc || opts->libjte_handle != NULL) { if(out_o->nwa == out_o->zero_nwa && out_o->zero_nwa == Libisoburn_overwriteable_starT + opts->partition_offset && out_o->emulation_mode == 1) { out_o->nwa= 0; out_o->zero_nwa= 0; out_o->min_start_byte= 0; } } ret = isoburn_disc_track_lba_nwa_v2(out_d, NULL, 0, &lba, &nwa); opts->effective_lba= nwa; ret= isoburn_get_msc2(out_o, NULL, &nwa, 0); if (ret != 1) { isoburn_msgs_submit(out_o, 0x00060000, "Cannot determine next writeable address", 0, "FAILURE", 0); /* >>> NWA_V : If burn_next_track_damaged: ??? Close track and session ? ??? Issue a hint for a repair command ? */; {ret= -3; goto ex;} } iso_write_opts_set_ms_block(wopts, nwa); iso_write_opts_set_appendable(wopts, !new_img); iso_write_opts_set_overwrite_buf(wopts, nwa>0 ? out_o->target_iso_head : NULL); } iso_write_opts_set_part_offset(wopts, opts->partition_offset, opts->partition_secs_per_head, opts->partition_heads_per_cyl); iso_write_opts_set_tail_blocks(wopts, opts->tail_blocks); if(opts->prep_partition != NULL) { ret = iso_write_opts_set_prep_img(wopts, opts->prep_partition, opts->prep_part_flag & 1); if(ret < 0) { isoburn_report_iso_error(ret, "Cannot set path for PreP partition", 0, "FAILURE", 0); {ret= -1; goto ex;} } } if(opts->efi_boot_partition != NULL) { ret = iso_write_opts_set_efi_bootp(wopts, opts->efi_boot_partition, opts->efi_boot_part_flag & 1); if(ret < 0) { isoburn_report_iso_error(ret, "Cannot set path for EFI system partition", 0, "FAILURE", 0); {ret= -1; goto ex;} } } for(i= 0; i < Libisoburn_max_appended_partitionS; i++) { if(opts->appended_partitions[i] == NULL) continue; ret= iso_write_opts_set_partition_img(wopts, i + 1, opts->appended_part_types[i], opts->appended_partitions[i], opts->appended_part_flags[i]); if(ret < 0) { isoburn_report_iso_error(ret, "Cannot set path for appended partition", 0, "FAILURE", 0); {ret= -1; goto ex;} } iso_write_opts_set_part_type_guid(wopts, i + 1, opts->appended_part_type_guids[i], opts->appended_part_gpt_flags[i] & 1); } iso_write_opts_set_appended_as_gpt(wopts, opts->appended_as_gpt); iso_write_opts_set_appended_as_apm(wopts, opts->appended_as_apm); iso_write_opts_set_part_like_isohybrid(wopts, opts->part_like_isohybrid); iso_write_opts_set_iso_mbr_part_type(wopts, opts->iso_mbr_part_type); iso_write_opts_set_iso_type_guid(wopts, opts->iso_gpt_type_guid, opts->iso_gpt_flag & 1); iso_write_opts_set_disc_label(wopts, opts->ascii_disc_label); ret= 1; ex: return(ret); } /* @param flag bit0= modifying rather than growing bit1= prepare for early release of input drive: wait until input and then disable image data source */ static int isoburn_prepare_disc_aux(struct burn_drive *in_d, struct burn_drive *out_d, struct burn_disc **disc, struct isoburn_imgen_opts *opts, int flag) { struct burn_source *wsrc; struct burn_session *session; struct burn_track *track; struct isoburn *in_o, *out_o; IsoWriteOpts *wopts= NULL; enum burn_disc_status state; int ret, fifo_chunks, i, new_img, early_indev_release; uint32_t data_start= 0xffffffff; size_t buffer_size= 0, buffer_free= 0; char *msg= NULL; msg= calloc(1, 160); if(msg == NULL) {ret= -1; goto ex;} new_img= flag&1; early_indev_release= flag&2; ret= isoburn_find_emulator(&in_o, in_d, 0); if(ret<0 || in_o==NULL) {ret= -1; goto ex;} ret= isoburn_find_emulator(&out_o, out_d, 0); if(ret<0 || out_o==NULL) {ret= -1; goto ex;} /* early end will be registered as failure */ in_o->wrote_well= out_o->wrote_well= 0; if(new_img && early_indev_release) { isoburn_msgs_submit(in_o, 0x00060000, "Programming error: Wrong session setup: new_img && early_indev_release", 0, "FATAL", 0); {ret= -4; goto ex;} } out_o->do_tao = opts->do_tao; out_o->do_fsync = opts->do_fsync; state = isoburn_disc_get_status(in_d); if (state != BURN_DISC_BLANK && state != BURN_DISC_APPENDABLE && state != BURN_DISC_FULL) { isoburn_msgs_submit(in_o, 0x00060000, "Unsuitable source media state", 0, "FAILURE", 0); {ret= -2; goto ex;} } state = isoburn_disc_get_status(out_d); if (state != BURN_DISC_BLANK && state != BURN_DISC_APPENDABLE) { isoburn_msgs_submit(out_o, 0x00060000, "Unsuitable target media state", 0, "FAILURE", 0); {ret= -2; goto ex;} } if (state != BURN_DISC_BLANK && opts->libjte_handle != NULL) { isoburn_msgs_submit(out_o, 0x00060000, "Jigdo Template Extraction works only on blank target media", 0, "FAILURE", 0); {ret= -2; goto ex;} } fifo_chunks= 32; if(opts->fifo_size >= 64*1024 && opts->fifo_size <= 1024.0 * 1024.0 * 1024.0){ fifo_chunks= opts->fifo_size/2048; if(fifo_chunks*2048 < opts->fifo_size) fifo_chunks++; } ret= iso_write_opts_new(&wopts, 0); if (ret < 0) { isoburn_report_iso_error(ret, "Cannot create iso_write_opts", 0, "FATAL",0); goto ex; } ret= isoburn_make_iso_write_opts(out_o, opts, fifo_chunks, wopts, flag & 1); if(ret < 0) goto ex; ret = iso_image_create_burn_source(in_o->image, wopts, &wsrc); if (ret < 0) { isoburn_report_iso_error(ret, "Cannot create burn source", 0, "FAILURE", 0); {ret= -1; goto ex;} } if (early_indev_release) { for(i= 0; i<300; i++) { /* <<< ??? */ if((i%30) == 0) { sprintf(msg, "Waiting for data in fifo since %d seconds", i/30); isoburn_msgs_submit(in_o, 0x00060000, msg, 0, "DEBUG", 0); } usleep(100000); ret= iso_ring_buffer_get_status(wsrc, &buffer_size, &buffer_free); if(ret >0 && buffer_size != buffer_free) break; } /* <<< ??? */ sprintf(msg, "After %.1f seconds: %d bytes of output available (fifo state=%d)", ((double) i+1) / 10.0, (int) (buffer_size - buffer_free), ret); isoburn_msgs_submit(in_o, 0x00060000, msg, 0, "DEBUG", 0); if(in_o->iso_data_source!=NULL) isoburn_data_source_shutdown(in_o->iso_data_source, 0); } ret= iso_write_opts_get_data_start(wopts, &data_start, 0); opts->data_start_lba= -1; if(ret > 0) opts->data_start_lba= data_start; /* TODO check return values for failure. properly clean-up on error */ out_o->iso_source= wsrc; *disc = burn_disc_create(); session = burn_session_create(); burn_disc_add_session(*disc, session, BURN_POS_END); track = burn_track_create(); burn_track_set_source(track, out_o->iso_source); burn_session_add_track(session, track, BURN_POS_END); /* give up local references */ burn_track_free(track); burn_session_free(session); in_o->wrote_well= out_o->wrote_well= -1; /* neutral */ ret= 1; ex: if(wopts!=NULL) {iso_write_opts_free(wopts); wopts= NULL;} if(msg != NULL) free(msg); return ret; } int isoburn_prepare_disc(struct burn_drive *d, struct burn_disc **disc, struct isoburn_imgen_opts *opts) { return isoburn_prepare_disc_aux(d, d, disc, opts, 0); } int isoburn_prepare_new_image(struct burn_drive *d, struct burn_disc **disc, struct isoburn_imgen_opts *opts, struct burn_drive *out_drive) { int ret; ret= isoburn_prepare_disc_aux(d, out_drive, disc, opts, 1); if (ret<=0) return ret; return 1; } /* API since 1.5.8 */ int isoburn_prepare_blind_grow_v2(struct burn_drive *d, struct burn_disc **disc, struct isoburn_imgen_opts *opts, struct burn_drive *out_drive, off_t nwa) { int ret; struct isoburn *o= NULL; ret= isoburn_find_emulator(&o, out_drive, 0); if(ret<0 || o==NULL) return(-1); if(nwa >= 0) o->fabricated_msc2= nwa; if(o->nwa == o->zero_nwa) o->nwa= o->zero_nwa= 0; else o->zero_nwa= 0; o->min_start_byte= 0; ret= isoburn_prepare_disc_aux(d, out_drive, disc, opts, 2); if (ret<=0) return ret; return(1); } /* API since 0.2.2 */ int isoburn_prepare_blind_grow(struct burn_drive *d, struct burn_disc **disc, struct isoburn_imgen_opts *opts, struct burn_drive *out_drive, int nwa) { int ret; ret= isoburn_prepare_blind_grow_v2(d, disc, opts, out_drive, (off_t) nwa); return(ret); } /* API @since 0.1.0 @param flag bit0= this is a regular end, not an abort give up source reference */ int isoburn_cancel_prepared_write(struct burn_drive *d, struct burn_drive *output_drive, int flag) { int ret; struct isoburn *o= NULL; if(output_drive!=NULL) { ret= isoburn_find_emulator(&o, output_drive, 0); if(ret<0 || o==NULL) o= NULL; else if(o->iso_source==NULL) o= NULL; } if(o==NULL) { ret= isoburn_find_emulator(&o, d, 0); if(ret<0) return(-1); if(o==NULL) return(0); if(o->iso_source==NULL) return(0); } if(o->iso_source->read!=NULL) return(0); if(o->iso_source->version<1) return(0); o->iso_source->cancel(o->iso_source); burn_source_free(o->iso_source); o->iso_source= NULL; return(1); } /* API @since 0.1.0 */ int isoburn_sync_after_write(struct burn_drive *d, struct burn_drive *output_drive, int flag) { return isoburn_cancel_prepared_write(d, output_drive, 1); } void isoburn_version(int *major, int *minor, int *micro) { *major= isoburn_header_version_major; *minor= isoburn_header_version_minor; *micro= isoburn_header_version_micro; /* No more: values from version.h generated from version.h.in and macro values defined in configure.ac *major = ISOBURN_MAJOR_VERSION; *minor = ISOBURN_MINOR_VERSION; *micro = ISOBURN_MICRO_VERSION; */ } int isoburn_is_compatible(int major, int minor, int micro, int flag) { int own_major, own_minor, own_micro; isoburn_version(&own_major, &own_minor, &own_micro); return(own_major > major || (own_major == major && (own_minor > minor || (own_minor == minor && own_micro >= micro)))); } /* ----------------------------------------------------------------------- */ /* Options for image reading. */ /* ----------------------------------------------------------------------- */ int isoburn_ropt_new(struct isoburn_read_opts **new_o, int flag) { struct isoburn_read_opts *o; o= (*new_o)= calloc(1, sizeof(struct isoburn_read_opts)); if(o==NULL) { isoburn_msgs_submit(NULL, 0x00060000, "Cannot allocate memory for read options", 0, "FATAL", 0); return(-1); } o->cache_tiles= Libisoburn_default_cache_tileS; o->cache_tile_blocks= Libisoburn_default_tile_blockS; o->norock= 0; o->nojoliet= 0; o->noiso1999= 1; o->do_ecma119_map= 0; o->map_mode= 1; o->do_joliet_map= 0; o->joliet_map_mode= 1; o->noaaip= 1; o->noacl= 1; o->noea= 1; o->lfa_flags= 0; o->noino= 1; o->nomd5= 1; o->preferjoliet= 0; o->uid= geteuid(); o->gid= getegid(); o->mode= 0444; o->dirmode= 0555; o->input_charset= NULL; o->truncate_mode= 1; o->truncate_length= 255; o->hasRR= 0; o->hasJoliet= 0; o->hasIso1999= 0; o->hasElTorito= 0; o->size= 0; o->pretend_blank= 1; o->displacement= 0; o->displacement_sign= 0; return(1); } int isoburn_ropt_destroy(struct isoburn_read_opts **o, int flag) { if(*o==NULL) return(0); free(*o); *o= NULL; return(1); } int isoburn_ropt_set_data_cache(struct isoburn_read_opts *o, int cache_tiles, int tile_blocks, int flag) { int i; char msg[80]; if(cache_tiles < 1) { isoburn_msgs_submit(NULL, 0x00060000, "Requested number of data cache tiles is too small (< 1)", 0, "SORRY", 0); return(0); } if(((double) cache_tiles) * ((double) tile_blocks) > (double) Libisoburn_cache_max_sizE) { sprintf(msg, "Requested size of data cache exceeds limit of %.f blocks", (double) Libisoburn_cache_max_sizE); isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "SORRY", 0); return(0); } for(i = 1; i <= Libisoburn_cache_max_sizE; i = i << 1) if(i == tile_blocks) break; if(i > Libisoburn_cache_max_sizE) { isoburn_msgs_submit(NULL, 0x00060000, "Requested number of blocks per data cache tiles is not a power of 2", 0, "SORRY", 0); return(0); } if(o != NULL) { o->cache_tiles= cache_tiles; o->cache_tile_blocks= tile_blocks; } return(1); } int isoburn_ropt_get_data_cache(struct isoburn_read_opts *o, int *cache_tiles, int *tile_blocks, int *set_flag, int flag) { if((flag & 1) || o == NULL) { *cache_tiles= Libisoburn_default_cache_tileS; *tile_blocks= Libisoburn_default_tile_blockS; *set_flag= 0; return(1); } *cache_tiles= o->cache_tiles; *tile_blocks= o->cache_tile_blocks; *set_flag= 0; return(1); } int isoburn_ropt_set_extensions(struct isoburn_read_opts *o, int ext) { o->norock= !!(ext&1); o->nojoliet= !!(ext&2); o->noiso1999= !!(ext&4); o->preferjoliet= !!(ext&8); o->pretend_blank= !!(ext&16); o->noaaip= !!(ext & 32); o->noacl= !!(ext & 64); o->noea= !!(ext & 128); o->noino= !!(ext & 256); o->nomd5= (ext >> 9) & 3; o->do_ecma119_map= !!(ext & 2048); o->map_mode= (ext >> 12) & 3; o->do_joliet_map= !!(ext & 16384); o->joliet_map_mode= !!(ext & 32768); o->lfa_flags= !!(ext & (1 << 16)); o->lfa_only_settable= !!(ext & (1 << 17)); return(1); } int isoburn_ropt_get_extensions(struct isoburn_read_opts *o, int *ext) { *ext= (!!o->norock) | ((!!o->nojoliet)<<1) | ((!!o->noiso1999)<<2) | ((!!o->preferjoliet)<<3) | ((!!o->pretend_blank)<<4) | ((!!o->noaaip) << 5) | ((!!o->noacl) << 6) | ((!!o->noea) << 7) | ((!!o->noino) << 8) | ((o->nomd5 & 3) << 9) | ((!!o->do_ecma119_map) << 11) | ((o->map_mode & 3) << 12) | ((!!o->do_joliet_map) << 14) | ((!!o->joliet_map_mode) << 15) | ((!!o->lfa_flags) << 16) | ((!!o->lfa_only_settable) << 17); return(1); } int isoburn_ropt_set_default_perms(struct isoburn_read_opts *o, uid_t uid, gid_t gid, mode_t mode) { mode_t dirmode; o->uid= uid; o->gid= gid; o->mode= mode; dirmode= mode; if(dirmode & S_IRUSR) dirmode|= S_IXUSR; if(dirmode & S_IRGRP) dirmode|= S_IXGRP; if(dirmode & S_IROTH) dirmode|= S_IXOTH; o->dirmode= dirmode; return(1); } int isoburn_ropt_get_default_perms(struct isoburn_read_opts *o, uid_t *uid, gid_t *gid, mode_t *mode) { *uid= o->uid; *gid= o->gid; *mode= o->mode; return(1); } int isoburn_ropt_set_default_dirperms(struct isoburn_read_opts *o, mode_t mode) { o->dirmode= mode; return(1); } int isoburn_ropt_get_default_dirperms(struct isoburn_read_opts *o, mode_t *mode) { *mode= o->dirmode; return(1); } int isoburn_ropt_set_input_charset(struct isoburn_read_opts *o, char *input_charset) { o->input_charset= input_charset; return(1); } int isoburn_ropt_get_input_charset(struct isoburn_read_opts *o, char **input_charset) { *input_charset= o->input_charset; return(1); } int isoburn_ropt_set_auto_incharset(struct isoburn_read_opts *o, int mode) { o->auto_input_charset= mode & 1; return(1); } int isoburn_ropt_get_auto_incharset(struct isoburn_read_opts *o, int *mode) { *mode= o->auto_input_charset; return(1); } int isoburn_ropt_set_displacement(struct isoburn_read_opts *o, uint32_t displacement, int displacement_sign) { o->displacement= displacement; o->displacement_sign= displacement_sign; return(1); } int isoburn_ropt_get_displacement(struct isoburn_read_opts *o, uint32_t *displacement, int *displacement_sign) { *displacement= o->displacement; *displacement_sign= o->displacement_sign; return(1); } int isoburn_ropt_set_truncate_mode(struct isoburn_read_opts *o, int mode, int length) { if(mode < 0 || mode > 1) mode= 1; if(length < 64) length= 64; if(length > 255) length= 255; o->truncate_mode= mode; o->truncate_length= length; return(1); } int isoburn_ropt_get_truncate_mode(struct isoburn_read_opts *o, int *mode, int *length) { *mode= o->truncate_mode; *length= o->truncate_length; return(1); } int isoburn_ropt_get_size_what(struct isoburn_read_opts *o, uint32_t *size, int *has_what) { *size= o->size; *has_what= (!!o->hasRR) | ((!!o->hasJoliet)<<1) | ((!!o->hasIso1999)<<2) | ((!!o->hasElTorito)<<3); return(1); } int isoburn_ropt_get_tree_loaded(struct isoburn_read_opts *o, int *tree, int *rr) { *tree= o->tree_loaded; *rr= o->rr_loaded; return(1); } /* ----------------------------------------------------------------------- */ /* Options for image generation by libisofs and image transport to libburn. */ /* ----------------------------------------------------------------------- */ int isoburn_igopt_new(struct isoburn_imgen_opts **new_o, int flag) { struct isoburn_imgen_opts *o; int i; o= (*new_o)= calloc(1, sizeof(struct isoburn_imgen_opts)); if(o==NULL) { isoburn_msgs_submit(NULL, 0x00060000, "Cannot allocate memory for image generation options", 0, "FATAL", 0); return(-1); } o->level= 2; o->rockridge= 1; o->joliet= 0; o->iso1999= 0; o->hardlinks= 0; o->aaip = 0; o->session_md5= 0; o->file_md5= 0; o->no_emul_toc= 0; o->old_empty= 0; o->untranslated_name_len = 0; o->allow_dir_id_ext = 0; o->omit_version_numbers= 0; o->allow_deep_paths= 1; o->rr_reloc_dir= NULL; o->rr_reloc_flags= 0; o->allow_longer_paths= 0; o->max_37_char_filenames= 0; o->no_force_dots= 0; o->allow_lowercase= 0; o->allow_full_ascii= 0; o->allow_7bit_ascii= 0; o->joliet_longer_paths= 0; o->joliet_long_names= 0; o->joliet_utf16= 0; o->always_gmt= 0; o->rrip_version_1_10= 0; o->dir_rec_mtime= 0; o->aaip_susp_1_10= 0; o->sort_files= 0; o->replace_dir_mode= 0; o->replace_file_mode= 0; o->replace_uid= 0; o->replace_gid= 0; o->dir_mode= 0555; o->file_mode= 0444; o->uid= 0; o->gid= 0; o->output_charset= NULL; o->fifo_size= 4*1024*1024; o->effective_lba= -1; o->data_start_lba= -1; o->system_area_data= NULL; o->system_area_options= 0; o->partition_offset= 0; o->partition_secs_per_head= 0; o->partition_heads_per_cyl= 0; o->vol_creation_time= 0; o->vol_modification_time= 0; o->vol_expiration_time= 0; o->vol_effective_time= 0; o->libjte_handle= NULL; o->tail_blocks= 0; o->prep_partition= NULL; o->prep_part_flag= 0; o->efi_boot_partition= NULL; o->efi_boot_part_flag= 0; for(i= 0; i < Libisoburn_max_appended_partitionS; i++) { o->appended_partitions[i]= NULL; o->appended_part_types[i]= 0; o->appended_part_flags[i]= 0; memset(o->appended_part_type_guids[i], 0, 16); o->appended_part_gpt_flags[i]= 0; } o->appended_as_gpt= 0; o->appended_as_apm= 0; o->part_like_isohybrid= 0; o->iso_mbr_part_type= -1; memset(o->gpt_guid, 0, 16); o->gpt_guid_mode= 0; o->max_ce_entries= 31; /* Linux hates >= RR_MAX_CE_ENTRIES = 32 */ o->max_ce_entries_flag= 2; /* omit non-isofs fattr and ACL if needed */ memset(o->hfsp_serial_number, 0, 8); o->hfsp_block_size= 0; o->apm_block_size= 0; o->do_tao= 0; o->do_fsync= 0; return(1); } int isoburn_igopt_destroy(struct isoburn_imgen_opts **o, int flag) { int i; if(*o==NULL) return(0); if((*o)->rr_reloc_dir != NULL) free((*o)->rr_reloc_dir); if((*o)->prep_partition != NULL) free((*o)->prep_partition); if((*o)->efi_boot_partition != NULL) free((*o)->efi_boot_partition); for(i= 0; i < Libisoburn_max_appended_partitionS; i++) if((*o)->appended_partitions[i] != NULL) free((*o)->appended_partitions[i]); if ((*o)->system_area_data != NULL) free((*o)->system_area_data); free(*o); *o= NULL; return(1); } int isoburn_igopt_set_level(struct isoburn_imgen_opts *o, int level) { o->level= level; return(1); } int isoburn_igopt_get_level(struct isoburn_imgen_opts *o, int *level) { *level= o->level; return(1); } int isoburn_igopt_set_extensions(struct isoburn_imgen_opts *o, int ext) { o->rockridge= !!(ext&1); o->joliet= !!(ext&2); o->iso1999= !!(ext&4); o->hardlinks= !!(ext & 8); o->aaip= !!(ext & 32); o->session_md5= !!(ext & 64); o->file_md5= (ext & (128 | 256)) >> 7; o->no_emul_toc= !!(ext & 512); o->will_cancel= !!(ext & 1024); o->old_empty= !!(ext & 2048); o->hfsplus= !!(ext & 4096); o->fat= !!(ext & 8192); return(1); } int isoburn_igopt_get_extensions(struct isoburn_imgen_opts *o, int *ext) { *ext= (!!o->rockridge) | ((!!o->joliet)<<1) | ((!!o->iso1999)<<2) | ((!!o->hardlinks) << 3) | ((!!o->aaip) << 5) | ((!!o->session_md5) << 6) | ((o->file_md5 & 3) << 7) | ((!!o->no_emul_toc) << 9) | ((o->will_cancel) << 10) | ((!!o->old_empty) << 11) | ((!!o->hfsplus) << 12) | ((!!o->fat) << 13); return(1); } int isoburn_igopt_set_relaxed(struct isoburn_imgen_opts *o, int relax) { o->omit_version_numbers= (!!(relax&1)) | (2 * !!(relax & isoburn_igopt_only_iso_versions)); o->allow_deep_paths= !!(relax&2); o->allow_longer_paths= !!(relax&4); o->max_37_char_filenames= !!(relax&8); o->no_force_dots= (!!(relax&16)) | (2 * !!(relax & isoburn_igopt_no_j_force_dots)); o->allow_lowercase= !!(relax&32); o->allow_full_ascii= !!(relax&64); o->joliet_longer_paths= !!(relax&128); o->always_gmt= !!(relax & isoburn_igopt_always_gmt); o->rrip_version_1_10= !!(relax & isoburn_igopt_rrip_version_1_10); o->dir_rec_mtime= !!(relax & isoburn_igopt_dir_rec_mtime); o->aaip_susp_1_10= !!(relax & isoburn_igopt_aaip_susp_1_10); o->allow_dir_id_ext= !!(relax & isoburn_igopt_allow_dir_id_ext); o->joliet_long_names= !!(relax & isoburn_igopt_joliet_long_names); o->joliet_rec_mtime= !!(relax & isoburn_igopt_joliet_rec_mtime); o->iso1999_rec_mtime= !!(relax & isoburn_igopt_iso1999_rec_mtime); o->allow_7bit_ascii= !!(relax & isoburn_igopt_allow_7bit_ascii); o->joliet_utf16= !!(relax & isoburn_igopt_joliet_utf16); return(1); } int isoburn_igopt_get_relaxed(struct isoburn_imgen_opts *o, int *relax) { *relax= (!!o->omit_version_numbers) | ((!!o->allow_deep_paths)<<1) | ((!!o->allow_longer_paths)<<2) | ((!!o->max_37_char_filenames)<<3) | ((!!(o->no_force_dots & 1))<<4)| ((!!o->allow_lowercase)<<5) | ((!!o->allow_full_ascii)<<6) | ((!!o->joliet_longer_paths)<<7) | ((!!o->always_gmt)<<8) | ((!!o->rrip_version_1_10)<<9) | ((!!o->dir_rec_mtime)<<10) | ((!!o->aaip_susp_1_10)<<11) | ((!!(o->omit_version_numbers & 2))<<12) | ((!!(o->no_force_dots & 2))<<13) | ((!!o->allow_dir_id_ext) << 14) | ((!!o->joliet_long_names) << 15) | ((!!o->joliet_rec_mtime) << 16) | ((!!o->iso1999_rec_mtime) << 17) | ((!!o->allow_full_ascii) << 18) | ((!!o->joliet_utf16) << 19); return(1); } int isoburn_igopt_set_rr_reloc(struct isoburn_imgen_opts *o, char *name, int flags) { if(o->rr_reloc_dir != name) { if(o->rr_reloc_dir != NULL) free(o->rr_reloc_dir); o->rr_reloc_dir= NULL; if(name != NULL) { o->rr_reloc_dir= strdup(name); if(o->rr_reloc_dir == NULL) { isoburn_msgs_submit(NULL, 0x00060000, "Cannot allocate memory for image generation options", 0, "FATAL", 0); return(-1); } } } o->rr_reloc_flags = flags & 1; return 1; } int isoburn_igopt_get_rr_reloc(struct isoburn_imgen_opts *o, char **name, int *flags) { *name= o->rr_reloc_dir; *flags= o->rr_reloc_flags; return(1); } int isoburn_igopt_set_untranslated_name_len(struct isoburn_imgen_opts *o, int len) { int ret; IsoWriteOpts *opts = NULL; char *msg= NULL; msg= calloc(1, 160); if(msg == NULL) {ret= -1; goto ex;} ret= iso_write_opts_new(&opts, 0); if(ret < 0) { isoburn_msgs_submit(NULL, 0x00060000, "Cannot create libisofs write options object", 0, "FATAL", 0); {ret= 0; goto ex;} } ret= iso_write_opts_set_untranslated_name_len(opts, len); if(ret < 0) { ret= iso_write_opts_set_untranslated_name_len(opts, -1); sprintf(msg, "Improper value for maximum length of untranslated names (%d <-> -1 ... %d)", len, ret); isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "FAILURE", 0); iso_write_opts_free(opts); {ret= 0; goto ex;} } o->untranslated_name_len= ret; /* Normalized len value */ iso_write_opts_free(opts); ret= 1; ex:; if(msg != NULL) free(msg); return(ret); } int isoburn_igopt_get_untranslated_name_len(struct isoburn_imgen_opts *o, int *len) { *len = o->untranslated_name_len; return(1); } int isoburn_igopt_set_sort_files(struct isoburn_imgen_opts *o, int value) { o->sort_files= !!(value&1); return(1); } int isoburn_igopt_get_sort_files(struct isoburn_imgen_opts *o, int *value) { *value= !!o->sort_files; return(1); } int isoburn_igopt_set_over_mode(struct isoburn_imgen_opts *o, int replace_dir_mode, int replace_file_mode, mode_t dir_mode, mode_t file_mode) { o->replace_dir_mode= replace_dir_mode%3; o->replace_file_mode= replace_file_mode%3; o->dir_mode= dir_mode; o->file_mode= file_mode; return(1); } int isoburn_igopt_get_over_mode(struct isoburn_imgen_opts *o, int *replace_dir_mode, int *replace_file_mode, mode_t *dir_mode, mode_t *file_mode) { *replace_dir_mode= o->replace_dir_mode%3; *replace_file_mode= o->replace_file_mode%3; *dir_mode= o->dir_mode; *file_mode= o->file_mode; return(1); } int isoburn_igopt_set_over_ugid(struct isoburn_imgen_opts *o, int replace_uid, int replace_gid, uid_t uid, gid_t gid) { o->replace_uid= replace_uid%3; o->replace_gid= replace_gid%3; o->uid= uid; o->gid= gid; return(1); } int isoburn_igopt_get_over_ugid(struct isoburn_imgen_opts *o, int *replace_uid, int *replace_gid, uid_t *uid, gid_t *gid) { *replace_uid= o->replace_uid%3; *replace_gid= o->replace_gid%3; *uid= o->uid; *gid= o->gid; return(1); } int isoburn_igopt_set_out_charset(struct isoburn_imgen_opts *o, char *output_charset) { o->output_charset= output_charset; return(1); } int isoburn_igopt_get_out_charset(struct isoburn_imgen_opts *o, char **output_charset) { *output_charset= o->output_charset; return(1); } int isoburn_igopt_set_fifo_size(struct isoburn_imgen_opts *o, int fifo_size) { o->fifo_size= fifo_size; return(1); } int isoburn_igopt_get_fifo_size(struct isoburn_imgen_opts *o, int *fifo_size) { *fifo_size= o->fifo_size; return(1); } int isoburn_igopt_get_effective_lba_v2(struct isoburn_imgen_opts *o, off_t *lba) { *lba= o->effective_lba; return(1); } int isoburn_igopt_get_effective_lba(struct isoburn_imgen_opts *o, int *lba) { int ret; off_t long_lba; ret= isoburn_igopt_get_effective_lba_v2(o, &long_lba); if(ret <= 0) return(ret); if(long_lba > 0x7fffffff) { *lba= 0x7fffffff; ret= 0; } else { *lba= long_lba; } return(ret); } int isoburn_igopt_get_data_start_v2(struct isoburn_imgen_opts *o, off_t *lba) { *lba= o->data_start_lba; return(1); } int isoburn_igopt_get_data_start(struct isoburn_imgen_opts *o, int *lba) { int ret; off_t long_lba; ret= isoburn_igopt_get_data_start_v2(o, &long_lba); if(ret <= 0) return(ret); if(long_lba > 0x7fffffff) { *lba= 0x7fffffff; ret= 0; } else { *lba= long_lba; } return(ret); } int isoburn_igopt_set_scdbackup_tag(struct isoburn_imgen_opts *o, char *name, char *timestamp, char *tag_written) { strncpy(o->scdbackup_tag_name, name, 80); o->scdbackup_tag_name[80]= 0; strncpy(o->scdbackup_tag_time, timestamp, 18); o->scdbackup_tag_time[18]= 0; o->scdbackup_tag_written = tag_written; if(tag_written != NULL) tag_written[0]= 0; return(1); } int isoburn_igopt_get_scdbackup_tag(struct isoburn_imgen_opts *o, char name[81], char timestamp[19], char **tag_written) { strncpy(name, o->scdbackup_tag_name, 80); name[80]= 0; strncpy(timestamp, o->scdbackup_tag_time, 18); timestamp[18]= 0; *tag_written= o->scdbackup_tag_written; return(1); } int isoburn_igopt_set_system_area(struct isoburn_imgen_opts *opts, char data[32768], int options) { if (data == NULL) { /* Disable */ if (opts->system_area_data != NULL) free(opts->system_area_data); opts->system_area_data = NULL; } else { if (opts->system_area_data == NULL) { opts->system_area_data = calloc(32768, 1); if (opts->system_area_data == NULL) return(-1); } memcpy(opts->system_area_data, data, 32768); } opts->system_area_options = options & 0x3ffff; return(1); } int isoburn_igopt_get_system_area(struct isoburn_imgen_opts *opts, char data[32768], int *options) { *options= opts->system_area_options; if(opts->system_area_data == NULL) return(0); memcpy(data, opts->system_area_data, 32768); return(1); } int isoburn_igopt_set_pvd_times(struct isoburn_imgen_opts *opts, time_t vol_creation_time, time_t vol_modification_time, time_t vol_expiration_time, time_t vol_effective_time, char *vol_uuid) { opts->vol_creation_time = vol_creation_time; opts->vol_modification_time = vol_modification_time; opts->vol_expiration_time = vol_expiration_time; opts->vol_effective_time = vol_effective_time; strncpy(opts->vol_uuid, vol_uuid, 16); opts->vol_uuid[16] = 0; return(1); } int isoburn_igopt_get_pvd_times(struct isoburn_imgen_opts *opts, time_t *vol_creation_time, time_t *vol_modification_time, time_t *vol_expiration_time, time_t *vol_effective_time, char vol_uuid[17]) { *vol_creation_time = opts->vol_creation_time; *vol_modification_time = opts->vol_modification_time; *vol_expiration_time = opts->vol_expiration_time; *vol_effective_time = opts->vol_effective_time; strcpy(vol_uuid, opts->vol_uuid); return(1); } int isoburn_igopt_set_part_offset(struct isoburn_imgen_opts *opts, uint32_t block_offset_2k, int secs_512_per_head, int heads_per_cyl) { if (block_offset_2k > 0 && block_offset_2k < 16) return(0); opts->partition_offset = block_offset_2k; opts->partition_secs_per_head = secs_512_per_head; opts->partition_heads_per_cyl = heads_per_cyl; return(1); } int isoburn_igopt_get_part_offset(struct isoburn_imgen_opts *opts, uint32_t *block_offset_2k, int *secs_512_per_head, int *heads_per_cyl) { *block_offset_2k = opts->partition_offset; *secs_512_per_head = opts->partition_secs_per_head; *heads_per_cyl = opts->partition_heads_per_cyl; return 1; } int isoburn_igopt_attach_jte(struct isoburn_imgen_opts *opts, void *libjte_handle) { opts->libjte_handle = libjte_handle; return 1; } int isoburn_igopt_detach_jte(struct isoburn_imgen_opts *opts, void **libjte_handle) { if(libjte_handle != NULL) *libjte_handle = opts->libjte_handle; opts->libjte_handle = NULL; return 1; } int isoburn_igopt_set_tail_blocks(struct isoburn_imgen_opts *opts, uint32_t num_blocks) { opts->tail_blocks = num_blocks; return 1; } int isoburn_igopt_get_tail_blocks(struct isoburn_imgen_opts *opts, uint32_t *num_blocks) { *num_blocks = opts->tail_blocks; return 1; } int isoburn_igopt_set_prep_partition(struct isoburn_imgen_opts *o, char *path, int flag) { if(o->prep_partition != NULL) free(o->prep_partition); o->prep_partition= NULL; o->prep_part_flag= 0; if(path != NULL) { o->prep_partition= strdup(path); if(o->prep_partition == NULL) { isoburn_report_iso_error(ISO_OUT_OF_MEM, "Out of memory", 0, "FATAL", 0); return(-1); } } o->prep_part_flag= flag & 1; return(1); } int isoburn_igopt_get_prep_partition(struct isoburn_imgen_opts *opts, char **path, int flag) { *path= opts->prep_partition; if(flag & 1) return(1 + (opts->prep_part_flag & 0x3fffffff)); return(1); } int isoburn_igopt_set_efi_bootp(struct isoburn_imgen_opts *o, char *path, int flag) { if(o->efi_boot_partition != NULL) free(o->efi_boot_partition); o->efi_boot_partition= NULL; o->efi_boot_part_flag= 0; if(path != NULL) { o->efi_boot_partition= strdup(path); if(o->efi_boot_partition == NULL) { isoburn_report_iso_error(ISO_OUT_OF_MEM, "Out of memory", 0, "FATAL", 0); return(-1); } } o->efi_boot_part_flag = flag & 1; return(1); } int isoburn_igopt_get_efi_bootp(struct isoburn_imgen_opts *opts, char **path, int flag) { *path= opts->efi_boot_partition; if(flag & 1) return(1 + (opts->efi_boot_part_flag & 0x3fffffff)); return(1); } int isoburn_igopt_set_partition_img(struct isoburn_imgen_opts *opts, int partition_number, uint8_t partition_type, char *image_path) { char msg[80]; if (partition_number < 1 || partition_number > Libisoburn_max_appended_partitionS) { sprintf(msg, "Partition number is out of range (1 ... %d)", Libisoburn_max_appended_partitionS); isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "FAILURE", 0); return(0); } if (opts->appended_partitions[partition_number - 1] != NULL) free(opts->appended_partitions[partition_number - 1]); opts->appended_partitions[partition_number - 1] = strdup(image_path); if (opts->appended_partitions[partition_number - 1] == NULL) return(-1); opts->appended_part_types[partition_number - 1] = partition_type; return(1); } int isoburn_igopt_get_partition_img(struct isoburn_imgen_opts *opts, int num_entries, uint8_t partition_types[], char *image_paths[]) { int i, max_entry= 0; for(i= 0; i < num_entries; i++) image_paths[i]= NULL; for(i= 0; i < Libisoburn_max_appended_partitionS; i++) { if(opts->appended_partitions[i] == NULL) continue; if(i < num_entries) { image_paths[i]= opts->appended_partitions[i]; partition_types[i]= opts->appended_part_types[i]; } max_entry= i + 1; } return(max_entry); } int isoburn_igopt_set_part_flag(struct isoburn_imgen_opts *opts, int partition_number, int flag) { char msg[80]; if (partition_number < 1 || partition_number > Libisoburn_max_appended_partitionS) { sprintf(msg, "Partition number is out of range (1 ... %d)", Libisoburn_max_appended_partitionS); isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "FAILURE", 0); return(0); } opts->appended_part_flags[partition_number - 1]= flag; return(1); } int isoburn_igopt_get_part_flags(struct isoburn_imgen_opts *opts, int num_entries, int part_flags[]) { int i, max_entry= 0; for(i= 0; i < num_entries; i++) part_flags[i]= 0; for(i= 0; i < Libisoburn_max_appended_partitionS; i++) { if(i < num_entries) part_flags[i]= opts->appended_part_flags[i]; max_entry= i + 1; } return(max_entry); } int isoburn_igopt_set_appended_as_gpt(struct isoburn_imgen_opts *opts, int gpt) { opts->appended_as_gpt= !!gpt; return(1); } int isoburn_igopt_get_appended_as_gpt(struct isoburn_imgen_opts *opts, int *gpt) { *gpt= opts->appended_as_gpt; return(1); } int isoburn_igopt_set_part_type_guid(struct isoburn_imgen_opts *opts, int partition_number, uint8_t guid[16], int valid) { char msg[80]; if (partition_number < 1 || partition_number > Libisoburn_max_appended_partitionS) { sprintf(msg, "Partition number is out of range (1 ... %d)", Libisoburn_max_appended_partitionS); isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "FAILURE", 0); return(0); } if(valid) memcpy(opts->appended_part_type_guids[partition_number - 1], guid, 16); if(valid) opts->appended_part_gpt_flags[partition_number - 1]|= 1; else opts->appended_part_gpt_flags[partition_number - 1]&= ~1; return(1); } int isoburn_igopt_get_part_type_guid(struct isoburn_imgen_opts *opts, int num_entries, uint8_t guids[][16], int valids[]) { int i, max_entry= 0; for(i= 0; i < num_entries; i++) { memset(guids[i], 0, 16); valids[i]= 0; } for(i= 0; i < Libisoburn_max_appended_partitionS; i++) { if(i < num_entries) { memcpy(guids[i], opts->appended_part_type_guids[i], 16); valids[i]= opts->appended_part_gpt_flags[i] & 1; } max_entry= i + 1; } return(max_entry); } int isoburn_igopt_set_appended_as_apm(struct isoburn_imgen_opts *opts, int apm) { opts->appended_as_apm= !!apm; return(1); } int isoburn_igopt_get_appended_as_apm(struct isoburn_imgen_opts *opts, int *apm) { *apm= opts->appended_as_apm; return(1); } int isoburn_igopt_set_part_like_isohybrid(struct isoburn_imgen_opts *opts, int alike) { opts->part_like_isohybrid= !!alike; return(1); } int isoburn_igopt_get_part_like_isohybrid(struct isoburn_imgen_opts *opts, int *alike) { *alike= opts->part_like_isohybrid; return(1); } int isoburn_igopt_set_iso_mbr_part_type(struct isoburn_imgen_opts *opts, int part_type) { if(part_type < -1 || part_type > 255) part_type = -1; opts->iso_mbr_part_type = part_type; return(1); } int isoburn_igopt_get_iso_mbr_part_type(struct isoburn_imgen_opts *opts, int *part_type) { *part_type= opts->iso_mbr_part_type; return(1); } int isoburn_igopt_set_iso_type_guid(struct isoburn_imgen_opts *opts, uint8_t guid[16], int valid) { if(valid) memcpy(opts->iso_gpt_type_guid, guid, 16); opts->iso_gpt_flag= (opts->iso_gpt_flag & ~1) | !!valid; return(1); } int isoburn_igopt_get_iso_type_guid(struct isoburn_imgen_opts *opts, uint8_t guid[16]) { memcpy(guid, opts->iso_gpt_type_guid, 16); return(opts->iso_gpt_flag & 1); } int isoburn_igopt_set_gpt_guid(struct isoburn_imgen_opts *opts, uint8_t guid[16], int mode) { if(mode < 0 || mode > 2) { isoburn_msgs_submit(NULL, 0x00060000, "Unrecognized GPT disk GUID setup mode. (0 ... 2)", 0, "FAILURE", 0); return(0); } opts->gpt_guid_mode= mode; if(opts->gpt_guid_mode == 1) memcpy(opts->gpt_guid, guid, 16); return 1; } int isoburn_igopt_get_gpt_guid(struct isoburn_imgen_opts *opts, uint8_t guid[16], int *mode) { if(opts->gpt_guid_mode == 1) memcpy(guid, opts->gpt_guid, 16); *mode = opts->gpt_guid_mode; return(1); } int isoburn_igopt_set_max_ce_entries(struct isoburn_imgen_opts *opts, uint32_t num, int flag) { if(num > 100000) { isoburn_msgs_submit(NULL, 0x00060000, "Too many CE entries enabled for single file (max 100000)", 0, "SORRY", 0); return(0); } if(num == 0) num= 1; opts->max_ce_entries= num; opts->max_ce_entries_flag= (flag & 15); return(1); } int isoburn_igopt_get_max_ce_entries(struct isoburn_imgen_opts *opts, uint32_t *num, int *max_ce_flag) { *num= opts->max_ce_entries; *max_ce_flag= opts->max_ce_entries_flag; return(1); } int isoburn_igopt_set_disc_label(struct isoburn_imgen_opts *opts, char *label) { strncpy(opts->ascii_disc_label, label, Libisoburn_disc_label_sizE - 1); opts->ascii_disc_label[Libisoburn_disc_label_sizE - 1] = 0; return(1); } int isoburn_igopt_get_disc_label(struct isoburn_imgen_opts *opts, char **label) { *label= opts->ascii_disc_label; return(1); } int isoburn_igopt_set_hfsp_serial_number(struct isoburn_imgen_opts *opts, uint8_t serial_number[8]) { memcpy(opts->hfsp_serial_number, serial_number, 8); return(1); } int isoburn_igopt_get_hfsp_serial_number(struct isoburn_imgen_opts *opts, uint8_t serial_number[8]) { memcpy(serial_number, opts->hfsp_serial_number, 8); return(1); } int isoburn_igopt_set_hfsp_block_size(struct isoburn_imgen_opts *opts, int hfsp_block_size, int apm_block_size) { char msg[80]; msg[0]= 0; if(hfsp_block_size != -1) { if(hfsp_block_size != 0 && hfsp_block_size != 512 && hfsp_block_size != 2048) { sprintf(msg, "Not a supported HFS+ size (%d <-> 0, 512, 2048)", hfsp_block_size); isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "FAILURE", 0); } opts->hfsp_block_size = hfsp_block_size; } if(apm_block_size != -1) { if(apm_block_size != 0 && apm_block_size != 512 && apm_block_size != 2048) { sprintf(msg, "Not a supported APM block size (%d <-> 0, 512, 2048)", apm_block_size); isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "FAILURE", 0); } opts->apm_block_size = apm_block_size; } if(msg[0]) return(0); return(1); } int isoburn_igopt_get_hfsp_block_size(struct isoburn_imgen_opts *opts, int *hfsp_block_size, int *apm_block_size) { *hfsp_block_size= opts->hfsp_block_size; *apm_block_size= opts->apm_block_size; return(1); } int isoburn_igopt_set_write_type(struct isoburn_imgen_opts *opts, int do_tao) { if(do_tao < -1 || do_tao > 1) return(0); opts->do_tao= do_tao; return(1); } int isoburn_igopt_get_write_type(struct isoburn_imgen_opts *opts, int *do_tao) { *do_tao= opts->do_tao; return(1); } int isoburn_igopt_set_stdio_endsync(struct isoburn_imgen_opts *opts, int do_sync) { opts->do_fsync= !!do_sync; return(1); } int isoburn_igopt_get_stdio_endsync(struct isoburn_imgen_opts *opts, int *do_sync) { *do_sync= opts->do_fsync; return(1); } int isoburn_conv_name_chars(struct isoburn_imgen_opts *opts, char *name, size_t name_len, char **result, size_t *result_len, int flag) { int ret; IsoWriteOpts *wopts= NULL; ret = iso_write_opts_new(&wopts, 0); if (ret < 0) { isoburn_report_iso_error(ret, "Cannot create iso_write_opts", 0, "FATAL",0); goto ex; } ret= isoburn_make_iso_write_opts(NULL, opts, 0, wopts, 0); if(ret < 0) goto ex; ret= iso_conv_name_chars(wopts, name, name_len, result, result_len, flag); ex:; if(wopts != NULL) iso_write_opts_free(wopts); return(ret); } /* Class struct of libisoburn. Copyright 2007 - 2009 Vreixo Formoso Lopes <metalpain2002@yahoo.es> Copyright 2007 - 2024 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifndef Isoburn_includeD #define Isoburn_includeD /* for uint8_t */ #ifdef HAVE_STDINT_H #include <stdint.h> #else #ifdef HAVE_INTTYPES_H #include <inttypes.h> #endif #endif /* For emulated TOC of overwritable media. Provides minimal info for faking a struct burn_toc_entry. */ struct isoburn_toc_entry { int session; int track_no; /* point */ off_t start_lba; off_t track_blocks; char *volid; /* For caching a volume id from emulated toc on overwritables */ struct isoburn_toc_entry *next; }; int isoburn_toc_entry_new(struct isoburn_toc_entry **objpt, struct isoburn_toc_entry *boss, int flag); /* @param flag bit0= delete all subordinates too */ int isoburn_toc_entry_destroy(struct isoburn_toc_entry **o, int flag); /* Minimal size of target_iso_head which is to be written during isoburn_activate_session(). Within this size there is everything that is needed for image access with no partition offset. The actual target_iso_head buffer must be larger by the evential partition offset. */ #define Libisoburn_target_head_sizE (32*2048) /* Maximum number of appended partitions. Effectively usable number depends on system area type. */ #define Libisoburn_max_appended_partitionS 8 /* Maximum length of a disc label text plus 1. */ #define Libisoburn_disc_label_sizE 129 struct isoburn { /* The libburn drive to which this isoburn object is related Most isoburn calls will use a burn_drive as object handle */ struct burn_drive *drive; /* -1= inappropriate medium state detected 0= libburn multi-session medium, resp. undecided yet 1= random access medium */ int emulation_mode; /* Although rarely used, libburn can operate on several drives simultaneously. */ struct isoburn *prev; struct isoburn *next; /* If >= 0, this address is used as reply for isoburn_disc_get_msc1() */ off_t fabricated_msc1; /* If >= 0, this address is used in isoburn_disc_track_lba_nwa() as reply parameter nwa. (The other nwa parameters below apply only to the effective write address on random access media. msc2 is handed to libisofs but not to libburn.) */ off_t fabricated_msc2; /* The nwa to be used for a first session on the present kind of overwritable media (usually Libisoburn_overwriteable_starT, but might be forced to 0) */ off_t zero_nwa; /* Start address as given by image examination (bytes, not blocks) */ off_t min_start_byte; /* Aligned start address to be used for processing (counted in blocks) */ off_t nwa; /* Truncate to .nwa an eventual regular file serving as output drive */ int truncate; /* Eventual freely fabricated isoburn_disc_get_status(). BURN_DISC_UNREADY means that this variable is disabled and normally emulated status is in effect. */ enum burn_disc_status fabricated_disc_status; /* To be set if read errors occurred during media evaluation. */ int media_read_error; /* Eventual emulated table of content read from the chain of ISO headers on overwritable media. */ struct isoburn_toc_entry *toc; /* Indicator whether the most recent burn run worked : -1 = undetermined, ask libburn , 0 = failure , 1 = success To be inquired by isoburn_drive_wrote_well() */ int wrote_well; /* ISO head buffer to be filled by write run */ int target_iso_head_size; uint8_t *target_iso_head; /* The 2k offset which was read from a loaded image. */ uint32_t loaded_partition_offset; /* Libisofs image context */ IsoImage *image; /* The start LBA of the image */ off_t image_start_lba; /* The block data source from which the existing image is read. */ IsoDataSource *iso_data_source; /* The burn source which transfers data from libisofs to libburn. It has its own fifo. */ struct burn_source *iso_source; /* For iso_tree_set_report_callback() */ int (*read_pacifier)(IsoImage*, IsoFileSource*); /* For iso_image_attach_data() */ void *read_pacifier_handle; /* An application provided method to immediately deliver messages */ int (*msgs_submit)(void *handle, int error_code, char msg_text[], int os_errno, char severity[], int flag); void *msgs_submit_handle; /* specific to application method */ int msgs_submit_flag; /* specific to application method */ /* Forwarding an image generation option to the burn wrapper */ int do_tao; /* Forwarding an image generation option to the burn wrapper */ int do_fsync; }; /* Creation and disposal function */ int isoburn_new(struct isoburn **objpt, int flag); int isoburn_destroy(struct isoburn **objpt, int flag); /* Eventual readers for public attributes */ /* ( put into separate .h file then ) */ int isoburn_get_emulation_mode(struct isoburn *o, int *pt, int flag); int isoburn_get_target_volset(struct isoburn *o, IsoImage **pt, int flag); /* List management */ int isoburn_get_prev(struct isoburn *o, struct isoburn **pt, int flag); int isoburn_get_next(struct isoburn *o, struct isoburn **pt, int flag); int isoburn_destroy_all(struct isoburn **objpt, int flag); int isoburn_link(struct isoburn *o, struct isoburn *link, int flag); int isoburn_count(struct isoburn *o, int flag); int isoburn_by_idx(struct isoburn *o, int idx, struct isoburn **pt, int flag); int isoburn_find_by_drive(struct isoburn **pt, struct burn_drive *d, int flag); /* Non API inner interfaces */ /* Submit a libisofs error to the libburn messenger. An application message reader shall recognize the error code range and attribute it to the libisofs message channel to which one cannot submit via API. @param iso_error_code return value <= 0 from a libisofs API call. @param default_msg_text is to be put out if iso_error_code leads to no error message @param os_errno operating system errno, submit 0 if none is known @param min_severity minimum severity, might be be increased if libisofs error severity surpasses min_severity. @param flag Bitfield, submit 0 for now */ int isoburn_report_iso_error(int iso_error_code, char default_msg_text[], int os_errno, char min_severity[], int flag); /* Calls from burn_wrap.c into isofs_wrap.c */ int isoburn_start_emulation(struct isoburn *o, int flag); int isoburn_invalidate_iso(struct isoburn *o, int flag); /* Calls from isofs_wrap.c into burn_wrap.c */ /** Get an eventual isoburn object which is wrapped around the drive. @param pt Eventually returns a pointer to the found object. It is allowed to become NULL if return value is -1 or 0. In this case, the drive is a genuine libburn drive with no emulation activated by isoburn. @param drive The drive to be searched for @param flag unused yet @return -1 unsuitable medium, 0 generic medium, 1 emulated medium. */ int isoburn_find_emulator(struct isoburn **pt, struct burn_drive *drive, int flag); /* Deliver an event message. Either via a non-NULL o->msgs_submit() method or via burn_msgs_submit() of libburn. */ int isoburn_msgs_submit(struct isoburn *o, int error_code, char msg_text[], int os_errno, char severity[], int flag); /** Set the start address for an emulated add-on session. The value will be rounded up to the alignment necessary for the medium. The aligned value will be divided by 2048 and then put into o->nwa . @param o The isoburn object to be programmed. @param value The start address in bytes @param flag unused yet @return <=0 is failure , >0 success */ int isoburn_set_start_byte(struct isoburn *o, off_t value, int flag); /** Obtains the image address offset to be used with image generation. This is either the (emulated) drive nwa or a value set by isoburn_prepare_blind_grow(). In any case this is the address to tell to iso_write_opts_set_ms_block(). @param o The isoburn object to be inquired @param opts If not NULL: write parameters to be set on drive before query @param msc2 The value to be used with iso_write_opts_set_ms_block() @param flag unused yet @return <=0 is failure , >0 success */ int isoburn_get_msc2(struct isoburn *o, struct burn_write_opts *opts, off_t *msc2, int flag); /** Get a data source suitable for read from a drive using burn_read_data() function. @param d drive to read from. Must be grabbed. @param displacement will be added or subtracted to any block address @param displacement_sign +1 = add , -1= subtract , else keep unaltered @return the data source, NULL on error. Must be freed with libisofs iso_data_source_unref() function. Note: this doesn't release the drive. */ IsoDataSource * isoburn_data_source_new(struct burn_drive *d, uint32_t displacement, int displacement_sign, int cache_tiles, int tile_blocks); /** Default settings for above cache_tiles, tile_blocks in newly created struct isoburn_read_opts. */ #define Libisoburn_default_cache_tileS 32 #define Libisoburn_default_tile_blockS 32 /** Maximum size of the cache in 2 kB blocks (1 GB) */ #define Libisoburn_cache_max_sizE (1024 * 512) /** Disable read capabilities of a data source which was originally created by isoburn_data_source_new(). After this any attempt to read will yield a FATAL programming error event. This is usually done to allow libburn to release the drive while libisofs still holds a reference to the data source object. libisofs is not supposed to use this object for reading any more, nevertheless. The disabled state of the data source is a safety fence around this daring situation. @param src The data source to be disabled @param flag unused yet @return <=0 is failure , >0 success */ int isoburn_data_source_shutdown(IsoDataSource *src, int flag); /** Check whether the size of target_iso_head matches the given partition offset. Eventually adjust size. */ int isoburn_adjust_target_iso_head(struct isoburn *o, uint32_t offst, int flag); /** Initialize the root directory attributes of a freshly created image. */ int isoburn_root_defaults(IsoImage *image, int flag); /** * Options for image reading. (Comments here may be outdated. API getter/setter function descriptions may override the descriptions here. Any difference is supposed to be a minor correction only.) */ struct isoburn_read_opts { int cache_tiles; /* number of cache tiles */ int cache_tile_blocks; unsigned int norock:1; /*< Do not read Rock Ridge extensions */ unsigned int nojoliet:1; /*< Do not read Joliet extensions */ unsigned int noiso1999:1; /*< Do not read ISO 9660:1999 enhanced tree */ unsigned int do_ecma119_map:1; /* call iso_read_opts_set_ecma119_map() */ unsigned int map_mode:2; /* argument for do_ecma119_map */ unsigned int do_joliet_map:1; /* call iso_read_opts_set_joliet_map() */ unsigned int joliet_map_mode:1; /* for iso_read_opts_set_joliet_map */ /* ts A90121 */ unsigned int noaaip:1; /* Do not read AAIP for ACL and EA */ unsigned int noacl:1; /* Do not read ACL from external file objects */ unsigned int noea:1; /* Do not read XFS-style EA from externals */ /* ts C40716 */ unsigned int lfa_flags:1; /* Read Linux file attribute flags (chattr) from external filesystem objects */ /* ts C40826 */ unsigned int lfa_only_settable:1; /* Ignore non-settable flags from from external filesystem objects */ /* ts A90508 */ unsigned int noino:1; /* Discard eventual PX inode numbers */ /* ts A90810 */ unsigned int nomd5:2; /* Do not read eventual MD5 array */ unsigned int preferjoliet:1; /*< When both Joliet and RR extensions are present, the RR * tree is used. If you prefer using Joliet, set this to 1. */ uid_t uid; /**< Default uid when no RR */ gid_t gid; /**< Default uid when no RR */ mode_t mode; /**< Default mode when no RR (only permissions) */ mode_t dirmode; /**< Default mode for directories when no RR (only permissions) */ /** * Input charset for RR file names. NULL to use default locale charset. */ char *input_charset; /** * Enable or disable methods to automatically choose an input charset. * This eventually overrides input_charset. * * bit0= set the input character set automatically from * attribute "isofs.cs" of root directory */ int auto_input_charset; /** * What to do in case of name longer than truncate_length: * 0= throw FAILURE * 1= truncate to truncate_length with MD5 of whole name at end */ int truncate_mode; int truncate_length; /* modified by the function isoburn_read_image */ unsigned int hasRR:1; /*< It will be set to 1 if RR extensions are present, to 0 if not. */ unsigned int hasJoliet:1; /*< It will be set to 1 if Joliet extensions are present, to 0 if not. */ /** * It will be set to 1 if the image is an ISO 9660:1999, i.e. it has * a version 2 Enhanced Volume Descriptor. */ unsigned int hasIso1999:1; /** It will be set to 1 if El-Torito boot record is present, to 0 if not.*/ unsigned int hasElTorito:1; uint32_t size; /**< Will be filled with the size (in 2048 byte block) of * the image, as reported in the PVM. */ int tree_loaded; /* from iso_read_image_features_tree_loaded() */ int rr_loaded; /* from iso_read_image_features_rr_loaded */ unsigned int pretend_blank:1; /* always create empty image */ uint32_t displacement; int displacement_sign; }; /** * Options for image generation by libisofs and image transport to libburn. (Comments here may be outdated. API getter/setter function descriptions may override the descriptions here. Any difference is supposed to be a minor correction only.) */ struct isoburn_imgen_opts { /* Options for image generation */ int will_cancel :1; int level; /**< ISO level to write at. */ /** Which extensions to support. */ unsigned int rockridge :1; unsigned int joliet :1; unsigned int iso1999 :1; unsigned int hfsplus :1; unsigned int fat :1; /* Whether to mark suitable IsoNode as hardlinks in RRIP PX */ unsigned int hardlinks :1; /* Write eventual AAIP info containing ACL and EA */ unsigned int aaip :1; /* Produce and write a MD5 checksum of the whole session stream. */ unsigned int session_md5 :1; /* Produce and write MD5 checksums for each single IsoFile. See parameter "files" of iso_write_opts_set_record_md5(). */ unsigned int file_md5 :2; /* On overwritable media or random access files do not write the first session to LBA 32, but rather to LBA 0 directly. */ unsigned int no_emul_toc :1; /* For empty files, symbolic links, and devices use the old ECMA-119 block addresses in the range [0,31] rather than the address of the dedicated empty block. */ unsigned int old_empty :1; /* relaxed constraints */ /* * Relaxed constraints. Setting any of these to 1 break the specifications, * but it is supposed to work on most moderns systems. Use with caution. */ /* * Extra Caution: This option breaks any assumptions about names that * are supported by ECMA-119 specifications. * Omit any translation which would make a file name compliant to the * ECMA-119 rules. This includes and exceeds omit_version_numbers, * max_37_char_filenames, no_force_dots bit0, allow_lowercase. */ unsigned int untranslated_name_len; /* * Convert directory names for ECMA-119 similar to other file names, but do * not force a dot or add a version number. * This violates ECMA-119 by allowing one "." and especially ISO level 1 * by allowing DOS style 8.3 names rather than only 8 characters. * (mkisofs and its clones seem to do this violation.) */ unsigned int allow_dir_id_ext :1; /** * Omit the version number (";1") at the end of the ISO-9660 identifiers. * Version numbers are usually not used. * bit0= omit version number with ECMA-119 and Joliet * bit1= omit version number with Joliet alone */ unsigned int omit_version_numbers :2; /** * Allow ISO-9660 directory hierarchy to be deeper than 8 levels. */ unsigned int allow_deep_paths :1; /** * If not allow_deep_paths is in effect, then it may become * necessary to relocate directories so that no ECMA-119 file path * has more than 8 components. These directories are grafted into either * the root directory of the ISO image or into a dedicated relocation * directory. For details see libisofs.h, iso_write_opts_set_rr_reloc(). */ char *rr_reloc_dir; /* IsoNode name in root directory. NULL or empty text means root itself. */ int rr_reloc_flags; /* bit0= mark auto-created rr_reloc_dir by RE bit1= not settable via API (used internally) */ /** * Allow path in the ISO-9660 tree to have more than 255 characters. */ unsigned int allow_longer_paths :1; /** * Allow a single file or directory hierarchy to have up to 37 characters. * This is larger than the 31 characters allowed by ISO level 2, and the * extra space is taken from the version number, so this also forces * omit_version_numbers. */ unsigned int max_37_char_filenames :1; /** * ISO-9660 forces filenames to have a ".", that separates file name from * extension. libisofs adds it if original filename doesn't has one. Set * this to 1 to prevent this behavior * bit0= no forced dot with ECMA-119 * bit1= no forced dot with Joliet */ unsigned int no_force_dots :2; /** * Allow lowercase characters in ISO-9660 filenames. By default, only * uppercase characters, numbers and a few other characters are allowed. */ unsigned int allow_lowercase :1; /** * Allow all ASCII characters to be appear on an ISO-9660 filename. Note * that "/" and "\0" characters are never allowed, even in RR names. */ unsigned int allow_full_ascii :1; /** * Like allow_full_ascii, but only allowing 7-bit characters. * Lowercase letters get mapped to uppercase if not allow_lowercase is set. * Gets overridden if allow_full_ascii is enabled. */ unsigned int allow_7bit_ascii :1; /** * Allow paths in the Joliet tree to have more than 240 characters. */ unsigned int joliet_longer_paths :1; /** * Allow leaf names in the Joliet tree to have up to 103 characters * rather than 64. */ unsigned int joliet_long_names :1; /** * Use UTF-16BE rather than its subset UCS-2 */ unsigned int joliet_utf16 :1; /** * Store timestamps as GMT rather than in local time. */ unsigned int always_gmt :1; /** * Write Rock Ridge info as of specification RRIP-1.10 rather than * RRIP-1.12: signature "RRIP_1991A" rather than "IEEE_1282", * field PX without file serial number */ unsigned int rrip_version_1_10 :1; /** * Store as ECMA-119 Directory Record timestamp the mtime * of the source rather than the image creation time. * The same can be ordered for Joliet and ISO 9660:1999 */ unsigned int dir_rec_mtime :1; unsigned int joliet_rec_mtime :1; unsigned int iso1999_rec_mtime :1; /** * Write AAIP as extension according to SUSP 1.10 rather than SUSP 1.12. * I.e. without announcing it by an ER field and thus without the need * to precede the RRIP fields by an ES and to precede the AA field by ES. */ unsigned int aaip_susp_1_10 :1; unsigned int sort_files:1; /**< If files should be sorted based on their weight. */ /** * The following options set the default values for files and directory * permissions, gid and uid. All these take one of three values: 0, 1 or 2. * If 0, the corresponding attribute will be kept as set in the IsoNode. * Unless you have changed it, it corresponds to the value on disc, so it * is suitable for backup purposes. If set to 1, the corresponding attrib. * will be changed by a default suitable value. Finally, if you set it to * 2, the attrib. will be changed with the value specified in the options * below. Note that for mode attributes, only the permissions are set, the * file type remains unchanged. */ unsigned int replace_dir_mode :2; unsigned int replace_file_mode :2; unsigned int replace_uid :2; unsigned int replace_gid :2; mode_t dir_mode; /** Mode to use on dirs when replace_dir_mode == 2. */ mode_t file_mode; /** Mode to use on files when replace_file_mode == 2. */ uid_t uid; /** uid to use when replace_uid == 2. */ gid_t gid; /** gid to use when replace_gid == 2. */ char *output_charset; /**< NULL to use default charset */ /* Options for image transport */ /** The number of bytes to be used for the fifo which decouples libisofs and libburn for better throughput and for reducing the risk of interrupting signals hitting the libburn thread which operates the MMC drive. The size will be rounded up to the next full 2048. Minimum is 64kiB, maximum is 1 GiB (but that is too much anyway). */ int fifo_size; /** Output value: Block address of session start as evaluated from medium and other options by libisoburn and libburn. If <0 : Invalid If >=0: Valid block number. Block size is always 2 KiB. */ off_t effective_lba; /** Output value: Block address of data section start as predicted by libisofs. If < 16: Invalid If >=16: Valid block number. Block size is always 2 KiB. */ off_t data_start_lba; /** * If not empty: Parameters "name" and "timestamp" for a scdbackup stream * checksum tag. See scdbackup/README appendix VERIFY. * It makes sense only for single session images which start at LBA 0. * Such a tag may be part of a libisofs checksum tag block after the * session tag line. It then covers the whole session up to its own start * position. * If scdbackup_tag_written is not NULL then it is a pointer to an * application provided array with at least 512 characters. The effectively * written scdbackup tag will be copied to this memory location. */ char scdbackup_tag_name[81]; char scdbackup_tag_time[19]; char *scdbackup_tag_written; /* Content of an embedded boot image. Valid if not NULL. * In that case it must point to a memory buffer at least 32 kB. */ char *system_area_data; /* * bit0= make bytes 446 - 512 of the system area a partition * table which reserves partition 1 from byte 63*512 to the * end of the ISO image. Assumed are 63 secs/hed, 255 head/cyl. * (GRUB protective msdos label.) * This works with and without system_area_data. */ int system_area_options; /* User settable PVD time stamps */ time_t vol_creation_time; time_t vol_modification_time; time_t vol_expiration_time; time_t vol_effective_time; /* To eventually override vol_modification_time by unconverted string and timezone 0 */ char vol_uuid[17]; /* The number of unclaimed 2K blocks before start of partition 1 as of the MBR in system area. If not 0 this will cause double volume descriptor sets and double tree. */ uint32_t partition_offset; /* Partition table parameter: 1 to 63, 0= disabled/default */ int partition_secs_per_head; /* 1 to 255, 0= disabled/default */ int partition_heads_per_cyl; /* Parameters and state of Jigdo Template Export environment. */ void *libjte_handle; /* A trailing padding of zero bytes which belongs to the image */ uint32_t tail_blocks; /* Disk file paths of content of PreP partition and EFI system partition */ char *prep_partition; int prep_part_flag; char *efi_boot_partition; int efi_boot_part_flag; /* Disk file paths of prepared images which shall be appended after the ISO image and described by partition table entries in a MBR. NULL means unused. */ char *appended_partitions[Libisoburn_max_appended_partitionS]; uint8_t appended_part_types[Libisoburn_max_appended_partitionS]; int appended_part_flags[Libisoburn_max_appended_partitionS]; uint8_t appended_part_type_guids[Libisoburn_max_appended_partitionS][16]; /* Flags in case that appended partitions show up in GPT: bit0= appended_part_type_guids[same_index] is valid */ uint8_t appended_part_gpt_flags[Libisoburn_max_appended_partitionS]; /* If 1: With appended partitions: create protective MBR and mark by GPT */ int appended_as_gpt; /* If 1: With appended partitions: mark by APM */ int appended_as_apm; /* If 1: Apply isohybrid gestures to non-isohybrid situations */ int part_like_isohybrid; /* isoburn_igopt_set_iso_mbr_part_type() */ int iso_mbr_part_type; /* isoburn_igopt_set_iso_type_guid() */ uint8_t iso_gpt_type_guid[16]; /* bit0= iso_gpt_type_guid is valid */ int iso_gpt_flag; /* See libisoburn.h isoburn_igopt_set_gpt_guid() */ uint8_t gpt_guid[16]; int gpt_guid_mode; /* Eventual name of the non-ISO aspect of the image. E.g. SUN ASCII label. */ char ascii_disc_label[Libisoburn_disc_label_sizE]; /* HFS+ image serial number. * 00...00 means that it shall be generated by libisofs. */ uint8_t hfsp_serial_number[8]; /* Allocation block size of HFS+ : 0= auto , 512, or 2048 */ int hfsp_block_size; /* Block size of and in APM : 0= auto , 512, or 2048 */ int apm_block_size; /* Write mode for optical media: * 0 = auto * 1 = TAO, Incremental, no RESERVE TRACK * -1 = SAO, DAO, RESERVE TRACK */ int do_tao; /* Whether to fsync() stdio_drives after isoburn_activate_session() */ int do_fsync; /* See libisoburn.h isoburn_igopt_set_max_ce_entries() */ uint32_t max_ce_entries; int max_ce_entries_flag; }; /* Alignment for session starts on overwritable media. (Increased from 16 to 32 blocks for aligning to BD-RE clusters.) */ #define Libisoburn_nwa_alignemenT 32 /* Alignment for outer session scanning with -ROM drives. (E.g. my DVD-ROM drive shows any DVD type as 0x10 "DVD-ROM" with more or less false capacity and TOC.) */ #define Libisoburn_toc_scan_alignemenT 16 /* Maximum gap to be bridged during a outer TOC scan. Gaps appear between the end of a session and the start of the next session. The longest gap found so far was about 38100 after the first session of a DVD-R. */ #define Libisoburn_toc_scan_max_gaP 65536 /* Creating a chain of image headers which form a TOC: The header of the first session is written after the LBA 0 header. So it persists and can give the end of its session. By help of Libisoburn_nwa_alignemenT it should be possible to predict the start of the next session header. The LBA 0 header is written by isoburn_activate_session() already with the first session. So the medium is mountable. A problem arises with DVD-RW in Intermediate State. They cannot be written by random access before they were written sequentially. In this case, no copy of the session 1 header is maintained and no TOC will be possible. Thus writing begins sequentially at LBA 0. IMPORTANT: This macro gives the minimal size of an image header. It has to be enlarged by the eventual partition offset. */ #define Libisoburn_overwriteable_starT \ ((off_t) (Libisoburn_target_head_sizE/2048)) /* Wrappers for emulation of TOC on overwritable media */ struct isoburn_toc_track { /* Either track or toc_entry are supposed to be NULL */ struct burn_track *track; struct isoburn_toc_entry *toc_entry; }; struct isoburn_toc_session { /* Either session or tracks and toc_entry are supposed to be NULL */ struct burn_session *session; struct isoburn_toc_track **track_pointers; int track_count; struct isoburn_toc_entry *toc_entry; }; struct isoburn_toc_disc { /* Either disc or sessions and toc are supposed to be NULL */ struct burn_disc *disc; struct isoburn_toc_session *sessions; /* storage array */ struct isoburn_toc_session **session_pointers; /* storage array */ struct isoburn_toc_track *tracks; /* storage array */ struct isoburn_toc_track **track_pointers; /* storage array */ int session_count; int incomplete_session_count; int track_count; struct isoburn_toc_entry *toc; }; #endif /* Isoburn_includeD */ /* cc -g -c isofs_wrap.c */ /* libisofs related functions of libisoburn. Copyright 2007 - 2009 Vreixo Formoso Lopes <metalpain2002@yahoo.es> Copyright 2007 - 2024 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <stdlib.h> #include <string.h> #include <stdio.h> #ifndef Xorriso_standalonE #include <libburn/libburn.h> #include <libisofs/libisofs.h> #else /* ! Xorriso_standalonE */ #include "../libisofs/libisofs.h" #include "../libburn/libburn.h" #endif /* Xorriso_standalonE */ #include "libisoburn.h" #include "isoburn.h" #define BP(a,b) [(b) - (a) + 1] struct ecma119_pri_vol_desc { uint8_t vol_desc_type BP(1, 1); uint8_t std_identifier BP(2, 6); uint8_t vol_desc_version BP(7, 7); uint8_t unused1 BP(8, 8); uint8_t system_id BP(9, 40); uint8_t volume_id BP(41, 72); uint8_t unused2 BP(73, 80); uint8_t vol_space_size BP(81, 88); uint8_t unused3 BP(89, 120); uint8_t vol_set_size BP(121, 124); uint8_t vol_seq_number BP(125, 128); uint8_t block_size BP(129, 132); uint8_t path_table_size BP(133, 140); uint8_t l_path_table_pos BP(141, 144); uint8_t opt_l_path_table_pos BP(145, 148); uint8_t m_path_table_pos BP(149, 152); uint8_t opt_m_path_table_pos BP(153, 156); uint8_t root_dir_record BP(157, 190); uint8_t vol_set_id BP(191, 318); uint8_t publisher_id BP(319, 446); uint8_t data_prep_id BP(447, 574); uint8_t application_id BP(575, 702); uint8_t copyright_file_id BP(703, 739); uint8_t abstract_file_id BP(740, 776); uint8_t bibliographic_file_id BP(777, 813); uint8_t vol_creation_time BP(814, 830); uint8_t vol_modification_time BP(831, 847); uint8_t vol_expiration_time BP(848, 864); uint8_t vol_effective_time BP(865, 881); uint8_t file_structure_version BP(882, 882); uint8_t reserved1 BP(883, 883); uint8_t app_use BP(884, 1395); uint8_t reserved2 BP(1396, 2048); }; static uint32_t iso_read_lsb(const uint8_t *buf, int bytes) { int i; uint32_t ret = 0; for (i=0; i<bytes; i++) { ret += ((uint32_t) buf[i]) << (i*8); } return ret; } static uint64_t iso_read_lsb64(const uint8_t *buf) { int i; uint64_t ret = 0; for (i=0; i < 8; i++) ret += ((uint64_t) buf[i]) << (i * 8); return ret; } /* API function. See libisoburn.h */ IsoImage *isoburn_get_attached_image(struct burn_drive *d) { int ret; struct isoburn *o= NULL; ret = isoburn_find_emulator(&o, d, 0); if (ret < 0) return NULL; if (o == NULL) { return NULL; } iso_image_ref(o->image); return o->image; } /* API */ off_t isoburn_get_attached_start_lba_v2(struct burn_drive *d) { int ret; struct isoburn *o= NULL; ret = isoburn_find_emulator(&o, d, 0); if (ret < 0 || o == NULL) return -1; if(o->image == NULL) return -1; return o->image_start_lba; } int isoburn_get_attached_start_lba(struct burn_drive *d) { off_t ret; ret= isoburn_get_attached_start_lba_v2(d); if(ret >= 0 && ret <= 0x7fffffff) return((int) ret); return(-1); } static void isoburn_idle_free_function(void *ignored) { return; } int isoburn_root_defaults(IsoImage *image, int flag) { IsoNode *root_node; mode_t root_mode= 0755; root_node= (IsoNode *) iso_image_get_root(image); iso_node_set_permissions(root_node, root_mode); return(1); } /* @return <=0 = error , 1 = ok , 2 = no ISO filesystem found */ int isoburn_make_iso_read_opts(struct burn_drive *d, struct isoburn *o, struct isoburn_read_opts *read_opts, IsoReadOpts **ropts) { int ret, int_num; off_t off_t_num, dummy; uint32_t ms_block; char *msg= NULL; msg= calloc(1, 160); *ropts= NULL; ret = isoburn_disc_get_msc1_v2(d, &off_t_num); if (ret <= 0) {ret= -2; goto ex;} if(off_t_num > 0xffffffff) { sprintf(msg, "Start address is outside 32 bit range."); isoburn_msgs_submit(o, 0x00060000, msg, 0, "FAILURE", 0); {ret= 0; goto ex;} } ms_block= off_t_num; if (o != NULL) o->image_start_lba= ms_block; ret = isoburn_read_iso_head_v2(d, off_t_num, &dummy, NULL, 0); if (ret <= 0) { sprintf(msg, "No ISO 9660 image at LBA %lu.", (unsigned long int) ms_block); isoburn_msgs_submit(o, 0x00060000, msg, 0, "WARNING", 0); {ret= 2; goto ex;} } if(read_opts->displacement != 0 && abs(read_opts->displacement_sign) == 1) { /* Apply reverse displacement to session start */ if(read_opts->displacement_sign == -1) { if(ms_block+ read_opts->displacement < ms_block) { displacement_rollover:; sprintf(msg, "Displacement offset leads outside 32 bit range."); isoburn_msgs_submit(o, 0x00060000, msg, 0, "FAILURE", 0); {ret= 0; goto ex;} } ms_block+= read_opts->displacement; } else { if(ms_block < read_opts->displacement) goto displacement_rollover; ms_block-= read_opts->displacement; } } ret = iso_read_opts_new(ropts, 0); if (ret < 0) { isoburn_report_iso_error(ret, "Cannot create write opts", 0, "FATAL", 0); goto ex; } iso_read_opts_set_start_block(*ropts, ms_block); iso_read_opts_set_no_rockridge(*ropts, read_opts->norock); iso_read_opts_set_no_aaip(*ropts, read_opts->noaaip); if(read_opts->nomd5 == 2) int_num= 2; else if(read_opts->nomd5 == 1) int_num= 1; else int_num= 0; iso_read_opts_set_no_md5(*ropts, int_num); if(read_opts->do_ecma119_map) iso_read_opts_set_ecma119_map(*ropts, read_opts->map_mode); if(read_opts->do_joliet_map) iso_read_opts_set_joliet_map(*ropts, read_opts->joliet_map_mode); iso_read_opts_set_new_inos(*ropts, read_opts->noino); iso_read_opts_set_no_joliet(*ropts, read_opts->nojoliet); iso_read_opts_set_no_iso1999(*ropts, read_opts->noiso1999); iso_read_opts_set_preferjoliet(*ropts, read_opts->preferjoliet); iso_read_opts_set_default_permissions(*ropts, read_opts->mode, read_opts->dirmode); iso_read_opts_set_default_uid(*ropts, read_opts->uid); iso_read_opts_set_default_gid(*ropts, read_opts->gid); iso_read_opts_set_input_charset(*ropts, read_opts->input_charset); iso_read_opts_auto_input_charset(*ropts, read_opts->auto_input_charset); iso_read_opts_load_system_area(*ropts, 1); iso_read_opts_keep_import_src(*ropts, 1); ret= 1; ex:; if(ret <= 0) { if(*ropts != NULL) iso_read_opts_free(*ropts); *ropts= NULL; } if(msg != NULL) free(msg); return(ret); } /* API function. See libisoburn.h */ int isoburn_read_image(struct burn_drive *d, struct isoburn_read_opts *read_opts, IsoImage **image) { int ret, ignore_aclea= 0; IsoReadOpts *ropts= NULL; IsoReadImageFeatures *features= NULL; char *msg= NULL; enum burn_disc_status status= BURN_DISC_BLANK; IsoDataSource *ds= NULL; struct isoburn *o= NULL; IsoImage *new_image= NULL; msg= calloc(1, 160); if(d != NULL) { ret = isoburn_find_emulator(&o, d, 0); if (ret < 0 || o == NULL) {ret= 0; goto ex;} status = isoburn_disc_get_status(d); o->image_start_lba= -1; } if(read_opts==NULL) { isoburn_msgs_submit(o, 0x00060000, "Program error: isoburn_read_image: read_opts==NULL", 0, "FATAL", 0); {ret= -1; goto ex;} } if (d == NULL || status == BURN_DISC_BLANK || read_opts->pretend_blank) { create_blank_image:; /* * Blank disc, we create a new image without files. */ if (d == NULL) { /* New empty image without relation to a drive */ if (image==NULL) { isoburn_msgs_submit(o, 0x00060000, "Program error: isoburn_read_image: image==NULL", 0, "FATAL", 0); {ret= -1; goto ex;} } /* create a new image */ ret = iso_image_new("ISOIMAGE", image); if (ret < 0) { isoburn_report_iso_error(ret, "Cannot create image", 0, "FATAL", 0); goto ex; } new_image= *image; } else { /* Blank new image for the drive */ if(o->image != NULL) ignore_aclea= iso_image_get_ignore_aclea(o->image); iso_image_unref(o->image); ret = iso_image_new("ISOIMAGE", &o->image); if (ret < 0) { isoburn_report_iso_error(ret, "Cannot create image", 0, "FATAL", 0); goto ex; } if (image != NULL) { *image = o->image; iso_image_ref(*image); /*protects object from premature free*/ } iso_image_set_ignore_aclea(o->image, ignore_aclea); ret= isoburn_root_defaults(o->image, 0); if(ret <= 0) goto ex; new_image= o->image; } ret= iso_image_set_truncate_mode(new_image, read_opts->truncate_mode, read_opts->truncate_length); if(ret < 0) goto ex; {ret= 1; goto ex;} } if (status != BURN_DISC_APPENDABLE && status != BURN_DISC_FULL) { isoburn_msgs_submit(o, 0x00060000, "Program error: isoburn_read_image: incorrect disc status", 0, "FATAL", 0); {ret= -4; goto ex;} } /* create the data source */ ret= isoburn_make_iso_read_opts(d, o, read_opts, &ropts); if(ret <= 0) goto ex; if(ret == 2) { sprintf(msg, "Creating blank image."); isoburn_msgs_submit(o, 0x00060000, msg, 0, "WARNING", 0); goto create_blank_image; } iso_image_set_ignore_aclea(o->image, read_opts->noacl | (read_opts->noea << 1) | (read_opts->lfa_flags << 2) | (read_opts->lfa_only_settable << 5)); /* Important: do not return until iso_read_opts_free() */ ret= iso_image_set_truncate_mode(o->image, read_opts->truncate_mode, read_opts->truncate_length); if(ret < 0) goto ex; ds = isoburn_data_source_new(d, read_opts->displacement, read_opts->displacement_sign, read_opts->cache_tiles, read_opts->cache_tile_blocks); if (ds == NULL) { isoburn_report_iso_error(ret, "Cannot create IsoDataSource object", 0, "FATAL", 0); ret= -1; goto ex; } if(o->iso_data_source!=NULL) iso_data_source_unref(o->iso_data_source); o->iso_data_source= ds; iso_image_attach_data(o->image, o->read_pacifier_handle, isoburn_idle_free_function); if(o->read_pacifier_handle==NULL) iso_tree_set_report_callback(o->image, NULL); else iso_tree_set_report_callback(o->image, o->read_pacifier); ret = iso_image_import(o->image, ds, ropts, &features); iso_tree_set_report_callback(o->image, NULL); iso_read_opts_free(ropts); ropts= NULL; if (ret < 0) { isoburn_report_iso_error(ret, "Cannot import image", 0, "FAILURE", 0); goto ex; } /* Important: do not return until free(features) */ if (image!=NULL) { *image = o->image; iso_image_ref(*image); /*protects object from premature free*/ } read_opts->hasRR = iso_read_image_features_has_rockridge(features); read_opts->hasJoliet = iso_read_image_features_has_joliet(features); read_opts->hasIso1999 = iso_read_image_features_has_iso1999(features); read_opts->hasElTorito = iso_read_image_features_has_eltorito(features); read_opts->size = iso_read_image_features_get_size(features); read_opts->tree_loaded = iso_read_image_features_tree_loaded(features); read_opts->rr_loaded = iso_read_image_features_rr_loaded(features); ret= 1; ex:; if(msg != NULL) free(msg); if(ropts != NULL) iso_read_opts_free(ropts); if(features != NULL) iso_read_image_features_destroy(features); return(ret); } /* API function. See libisoburn.h */ int isoburn_assess_written_features(struct burn_drive *d, struct isoburn_read_opts *read_opts, IsoReadImageFeatures **features, struct isoburn_imgen_opts **imgen_opts, int flag) { int ret, type, ext; int64_t num_value; void *pt_value; size_t pt_size; IsoReadOpts *ropts= NULL; char *msg= NULL; enum burn_disc_status status= BURN_DISC_BLANK; IsoDataSource *ds= NULL; struct isoburn *o= NULL; IsoWriteOpts *write_opts= NULL; msg= calloc(1, 160); if(d != NULL) { ret = isoburn_find_emulator(&o, d, 0); if (ret < 0 || o == NULL) {ret= 0; goto ex;} status = isoburn_disc_get_status(d); o->image_start_lba= -1; } if(read_opts==NULL) { isoburn_msgs_submit(o, 0x00060000, "Program error: isoburn_read_image: read_opts==NULL", 0, "FATAL", 0); {ret= -1; goto ex;} } if (d == NULL || read_opts->pretend_blank || (status != BURN_DISC_APPENDABLE && status != BURN_DISC_FULL)) { isoburn_msgs_submit(o, 0x00060000, "Empty drive, unsuitable medium state, or read option 'pretend_blank'", 0, "FAILURE", 0); {ret= 0; goto ex;} } ret= isoburn_make_iso_read_opts(d, o, read_opts, &ropts); if(ret <= 0) goto ex; if(ret == 2) {ret= 0; goto ex;} /* ??? Only if o->iso_data_source == NULL ? */ ds = isoburn_data_source_new(d, read_opts->displacement, read_opts->displacement_sign, read_opts->cache_tiles, read_opts->cache_tile_blocks); if (ds == NULL) { isoburn_report_iso_error(ret, "Cannot create IsoDataSource object", 0, "FATAL", 0); ret= -1; goto ex; } if(o->iso_data_source != NULL) iso_data_source_unref(o->iso_data_source); o->iso_data_source= ds; ret= iso_assess_written_features(ds, ropts, features, &write_opts); if(ret < 0) { isoburn_report_iso_error(ret, "Failed to assess ISO filesystem features", 0, "FAILURE", 0); ret= 0; goto ex; } ret= isoburn_igopt_new(imgen_opts, 0); if(ret <= 0) goto ex; /* Convert features to imgen_opts */ if(iso_read_image_feature_named(*features, "iso_level", NULL, &type, &num_value, &pt_value, &pt_size) == 1) isoburn_igopt_set_level(*imgen_opts, num_value); ext= 0; if(iso_read_image_feature_named(*features, "rock_ridge", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_rockridge * !!num_value; if(iso_read_image_feature_named(*features, "joliet", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_joliet * !!num_value; if(iso_read_image_feature_named(*features, "iso1999", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_iso1999 * !!num_value; if(iso_read_image_feature_named(*features, "aaip", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_aaip * !!num_value; if(iso_read_image_feature_named(*features, "record_md5_session", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_session_md5 * !!num_value; if(iso_read_image_feature_named(*features, "record_md5_files", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_file_md5 * !!num_value; isoburn_igopt_set_extensions(*imgen_opts, ext); ext= 0; if(iso_read_image_feature_named(*features, "omit_version_numbers", NULL, &type, &num_value, &pt_value, &pt_size) == 1) { if((num_value & 3) == 2) ext|= isoburn_igopt_only_iso_versions; else if(num_value & 3) ext|= isoburn_igopt_omit_version_numbers; } if(iso_read_image_feature_named(*features, "allow_deep_paths", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_allow_deep_paths * !!num_value; if(iso_read_image_feature_named(*features, "allow_longer_paths", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_allow_longer_paths * !!num_value; if(iso_read_image_feature_named(*features, "max_37_char_filenames", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_max_37_char_filenames * !!num_value; if(iso_read_image_feature_named(*features, "no_force_dots", NULL, &type, &num_value, &pt_value, &pt_size) == 1) { if(num_value & 1) ext|= isoburn_igopt_only_iso_versions; if(num_value & 2) ext|= isoburn_igopt_no_force_dots; } if(iso_read_image_feature_named(*features, "allow_lowercase", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_allow_lowercase * !!num_value; if(iso_read_image_feature_named(*features, "allow_full_ascii", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_allow_full_ascii * !!num_value; if(iso_read_image_feature_named(*features, "joliet_longer_paths", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_joliet_longer_paths * !!num_value; if(iso_read_image_feature_named(*features, "rrip_version_1_10", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_rrip_version_1_10 * !!num_value; if(iso_read_image_feature_named(*features, "aaip_susp_1_10", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_aaip_susp_1_10 * !!num_value; if(iso_read_image_feature_named(*features, "allow_dir_id_ext", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_allow_dir_id_ext * !!num_value; if(iso_read_image_feature_named(*features, "joliet_long_names", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_joliet_long_names * !!num_value; if(iso_read_image_feature_named(*features, "joliet_utf16", NULL, &type, &num_value, &pt_value, &pt_size) == 1) ext|= isoburn_igopt_joliet_utf16 * !!num_value; isoburn_igopt_set_relaxed(*imgen_opts, ext); if(iso_read_image_feature_named(*features, "untranslated_name_len", NULL, &type, &num_value, &pt_value, &pt_size) == 1) isoburn_igopt_set_untranslated_name_len(*imgen_opts, num_value); ret= 1; ex:; if(msg != NULL) free(msg); if(ropts != NULL) iso_read_opts_free(ropts); if(write_opts != NULL) iso_write_opts_free(write_opts); return(ret); } /* API function. See libisoburn.h */ int isoburn_attach_image(struct burn_drive *d, IsoImage *image) { int ret; struct isoburn *o; ret = isoburn_find_emulator(&o, d, 0); if (ret < 0 || o == NULL) return 0; if (image == NULL) { isoburn_msgs_submit(o, 0x00060000, "Program error: isoburn_attach_image: image==NULL", 0, "FATAL", 0); return -1; } if(o->image != NULL) iso_image_unref(o->image); o->image = image; o->image_start_lba = -1; return(1); } /* API */ int isoburn_attach_start_lba_v2(struct burn_drive *d, off_t lba, int flag) { int ret; struct isoburn *o; ret = isoburn_find_emulator(&o, d, 0); if(ret < 0) return ret; if(o == NULL) return 0; if(o->image == NULL) return 0; o->image_start_lba = lba; return 1; } int isoburn_attach_start_lba(struct burn_drive *d, int lba, int flag) { int ret; ret= isoburn_attach_start_lba_v2(d, (off_t) lba, flag); return(ret); } /* API function. See libisoburn.h */ int isoburn_activate_session(struct burn_drive *drive) { int ret, do_sync = 1; struct isoburn *o; ret = isoburn_find_emulator(&o, drive, 0); if (ret < 0) return -1; if (o->emulation_mode != 1) return 1; /* don't need to activate session */ if (o->fabricated_msc2 >= 0) return 1; /* blind growing: do not alter anything outside the session */ if (!(o->fabricated_disc_status == BURN_DISC_APPENDABLE || (o->fabricated_disc_status == BURN_DISC_BLANK && o->zero_nwa > 0))) return 1; ret = burn_drive_get_drive_role(drive); if (ret != 1) do_sync = !! o->do_fsync; ret = burn_random_access_write(drive, (off_t) 0, (char*)o->target_iso_head, o->target_iso_head_size, do_sync); return ret; } /** API @since 0.6.2 */ int isoburn_get_img_partition_offset(struct burn_drive *drive, uint32_t *block_offset_2k) { int ret; struct isoburn *o; ret = isoburn_find_emulator(&o, drive, 0); if(ret < 0 || o == NULL) return -1; *block_offset_2k= o->loaded_partition_offset; if(o->loaded_partition_offset == 0) return(0); if(o->target_iso_head_size == (off_t) Libisoburn_target_head_sizE + (off_t) 2048 * (off_t) o->loaded_partition_offset) return(1); return(2); } /* Try to read partition start and size block number from given GPT entry */ int isoburn_get_gpt_entry(struct isoburn *o, int partno, uint64_t *start_lba, uint64_t *size, int flag) { uint32_t part_start, entry_count, entry_size, part_lba, end_lba; uint8_t *gpt, *part; /* Check for GPT header block */ gpt = o->target_iso_head + 512; if(memcmp(gpt, "EFI PART", 8) != 0) return(0); if(gpt[8] != 0x00 || gpt[9] != 0x00 || gpt[10] != 0x01 || gpt[11] != 0x00) return(0); part_start = iso_read_lsb64(gpt + 72); entry_count = iso_read_lsb64(gpt + 80); entry_size = iso_read_lsb64(gpt + 84); /* Read partition entry */ if(partno < 1) return(0); if(((uint64_t) partno) > entry_count) return(0); if(part_start * 512 + partno * entry_size > 32768) return(0); part = o->target_iso_head + part_start * 512 + (partno - 1) * entry_size; part_lba = iso_read_lsb64(part + 32); end_lba = iso_read_lsb64(part + 40); if(end_lba < part_lba) return(0); *start_lba = part_lba; *size = end_lba - part_lba + 1; return(1); } /* Check for MBR signature and a first partition that starts at a 2k block and ends where the image ends. If not too large or too small, accept its start as partition offset. */ static int isoburn_inspect_partition(struct isoburn *o, uint32_t img_size, int flag) { uint8_t *mbr, *part, *buf= NULL; uint32_t offst, numsec; uint64_t gpt_start_lba, gpt_size; struct ecma119_pri_vol_desc *pvm; off_t data_count; int ret; char *msg= NULL; static int max_offst= 512 - 32; buf= (uint8_t *) calloc(1, 2048); msg= calloc(1, 160); if(buf == NULL || msg == NULL) {ret= -1; goto ex;} mbr= o->target_iso_head; part= mbr + 446; if(mbr[510] != 0x55 || mbr[511] != 0xAA) {ret= 2; goto ex;} /* not an MBR */ /* Does the first partition entry look credible ? */ if(part[0] != 0x80 && part[0] != 0x00) {ret= 2; goto ex;} /* Invalid partition status */ if(part[1] == 0 && part[2] == 0 && part[3] == 0) {ret= 2; goto ex;} /* Zero C/H/S start address */ offst= iso_read_lsb(part + 8, 4); numsec= iso_read_lsb(part + 12, 4); /* Is it GPT ? */ if(part[4] == 0xee && offst == 1) { ret= isoburn_get_gpt_entry(o, 1, &gpt_start_lba, &gpt_size, 0); if(ret > 0 && gpt_start_lba < ((uint64_t) 1) << 32 && gpt_size < ((uint64_t) 1) << 32) { offst= gpt_start_lba; numsec= gpt_size; } } /* Does it match the normal ISO image ? */ if(offst < 64) {ret= 2; goto ex;} /* Zero or unusably small partition start */ if((offst % 4) || (numsec % 4)) {ret= 2; goto ex;} /* Not aligned to 2k */ if(numsec < 72) {ret= 2; goto ex;} /* No room for volume descriptors */ offst/= 4; numsec/= 4; if(offst + numsec > img_size) {ret= 2; goto ex;} /* Partition end exceeds image end */ /* Is there a PVD at the partition start ? */ ret = burn_read_data(o->drive, (off_t) (offst + 16) * (off_t) 2048, (char*) buf, 2048, &data_count, 32); if(ret <= 0) {ret= 2; goto ex;} pvm = (struct ecma119_pri_vol_desc *) buf; if (strncmp((char*) pvm->std_identifier, "CD001", 5) != 0) {ret= 2; goto ex;} /* not a PVD */ if (pvm->vol_desc_type[0] != 1 || pvm->vol_desc_version[0] != 1 || pvm->file_structure_version[0] != 1 ) {ret= 2; goto ex;} /* failed sanity check */ if(iso_read_lsb(pvm->vol_space_size, 4) + offst > img_size) {ret= 2; goto ex;} /* Image ends do not match plausibly */ /* Now it is credible. Not yet clear is whether it is acceptable. */ o->loaded_partition_offset= offst; /* If the partition start is too large: Report but do not accept. */ if(offst > (uint32_t) max_offst) {/* Not more than 1 MB of .target_iso_head */ sprintf(msg, "Detected partition offset of %.f blocks. Maximum for load buffer is %d", (double) offst, max_offst); isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "WARNING", 0); {ret= 3; goto ex;} } /* Accept partition start and adjust buffer size */ ret= isoburn_adjust_target_iso_head(o, offst, 0); if(ret <= 0) goto ex; ret= 1; ex:; if(buf != NULL) free(buf); if(msg != NULL) free(msg); return(ret); } /** Initialize the emulation of multi-session on random access media. The need for emulation is confirmed already. @param o A freshly created isoburn object. isoburn_create_data_source() was already called, nevertheless. @param flag bit0= read-only @return <=0 error , 1 = success */ int isoburn_start_emulation(struct isoburn *o, int flag) { int ret, i, role; off_t capacity = -1, dummy; off_t data_count, to_read; struct burn_drive *drive; struct ecma119_pri_vol_desc *pvm; enum burn_disc_status s; char *path= NULL, *msg= NULL; path= calloc(1, BURN_DRIVE_ADR_LEN); msg= calloc(1, 2 * BURN_DRIVE_ADR_LEN); if(path == NULL || msg == NULL) {ret= -1; goto ex;} if(o==NULL) { isoburn_msgs_submit(NULL, 0x00060000, "Program error: isoburn_start_emulation: o==NULL", 0, "FATAL", 0); {ret= -1; goto ex;} } drive= o->drive; if(flag & 1) o->fabricated_disc_status= BURN_DISC_FULL; /* We can assume 0 as start block for image. The data there point to the most recent session. */ role = burn_drive_get_drive_role(drive); ret = burn_get_read_capacity_v2(drive, &capacity, 0); if (ret <= 0) capacity = -1; if (role == 5) { /* random access write-only medium */ s = burn_disc_get_status(drive); o->fabricated_disc_status= s; burn_disc_track_lba_nwa_v2(drive, NULL, 0, &dummy, &(o->nwa)); if(o->nwa < o->zero_nwa) o->zero_nwa= 0; {ret= 1; goto ex;} } else if (capacity > 0 || role == 2 || role == 4) { /* Might be a block device on a system where libburn cannot determine its size. Try to read anyway. */ to_read = o->target_iso_head_size; memset(o->target_iso_head, 0, to_read); if(capacity > 0 && capacity * (off_t) 2048 < to_read) to_read = capacity * (off_t) 2048; ret = burn_read_data(drive, (off_t) 0, (char*)o->target_iso_head, to_read, &data_count, 32 | 8); if (ret <= 0) { /* an error means a disc with no ISO image */ o->media_read_error= 1; if (ret == -2) { path[0]= 0; burn_drive_d_get_adr(drive, path); sprintf(msg, "Pseudo drive '%s' does not allow reading", path); isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "NOTE", 0); o->fabricated_disc_status= BURN_DISC_BLANK; } else if (capacity > 0) o->fabricated_disc_status= BURN_DISC_FULL; else if(!(flag & 1)) o->fabricated_disc_status= BURN_DISC_BLANK; {ret= 1; goto ex;} } } else { /* No read capacity means blank medium */ if(!(flag & 1)) o->fabricated_disc_status= BURN_DISC_BLANK; {ret= 1; goto ex;} } /* check first 64K. If 0's, the disc is treated as a blank disc, and thus overwritten without extra check. */ i = Libisoburn_target_head_sizE; while (i && !o->target_iso_head[i-1]) --i; if (!i) { if(!(flag & 1)) o->fabricated_disc_status= BURN_DISC_BLANK; {ret= 1; goto ex;} } pvm = (struct ecma119_pri_vol_desc *)(o->target_iso_head + 16 * 2048); if (strncmp((char*)pvm->std_identifier, "CD001", 5) == 0) { off_t size; /* sanity check */ if (pvm->vol_desc_type[0] != 1 || pvm->vol_desc_version[0] != 1 || pvm->file_structure_version[0] != 1 ) { /* TODO for now I treat this as a full disc */ o->fabricated_disc_status= BURN_DISC_FULL; {ret= 1; goto ex;} } /* ok, PVM found, set size */ size = (off_t) iso_read_lsb(pvm->vol_space_size, 4); ret= isoburn_inspect_partition(o, (uint32_t) size, 0); if (ret <= 0) goto ex; size *= (off_t) 2048; /* block size in bytes */ isoburn_set_start_byte(o, size, 0); if(!(flag & 1)) o->fabricated_disc_status= BURN_DISC_APPENDABLE; } else if (strncmp((char*)pvm->std_identifier, "CDXX1", 5) == 0 || (strncmp((char*)pvm->std_identifier, "CDxx1", 5) == 0 && pvm->vol_desc_type[0] == 'x')) { /* empty image */ isoburn_set_start_byte(o, o->zero_nwa * 2048, 0); if(!(flag & 1)) o->fabricated_disc_status= BURN_DISC_BLANK; } else { /* treat any disc in an unknown format as full */ o->fabricated_disc_status= BURN_DISC_FULL; } ret= 1; ex:; if(path != NULL) free(path); if(msg != NULL) free(msg); return(ret); } /** Alters and writes the first 64 kB of a "medium" to invalidate an ISO image. (It shall stay restorable by skilled humans, though). The result shall especially keep libisoburn from accepting the medium image as ISO filesystem. @param o A fully activated isoburn object. isoburn_start_emulation() was already called. @return <=0 error , 1 = success */ int isoburn_invalidate_iso(struct isoburn *o, int flag) { int end_ed_found= 0, i; char *head; head= (char *) o->target_iso_head; /* * replace CD001 with CDXX1 in PVM. */ memcpy(head + 16 * 2048 + 1, "CDXX1", 5); /* Invalidate further ECMA-119 volume descriptors and possible UDF volume recognition sequence */ for(i= 17 * 2048; i < 32 * 2048; i+= 2048) { if(end_ed_found) { if(head[i] == 0 && strncmp(head + i + 1, "BEA01", 5) == 0) memcpy(head + i + 1, "BEAX1", 5); else if(head[i] == 0 && strncmp(head + i + 1, "NSR", 3) == 0) memcpy(head + i + 1, "NSRX", 4); else if(head[i] == 0 && strncmp(head + i + 1, "TEA", 3) == 0) memcpy(head + i + 1, "TEAX", 4); } else if(strncmp(head + i + 1, "CD001", 5) == 0) { if(((unsigned char *) head)[i] == 0xff) end_ed_found= 1; memcpy(head + i + 3, "XX", 2); } } return isoburn_activate_session(o->drive); } /* API @since 0.1.0 */ int isoburn_set_read_pacifier(struct burn_drive *drive, int (*read_pacifier)(IsoImage*, IsoFileSource*), void *read_handle) { int ret; struct isoburn *o; ret = isoburn_find_emulator(&o, drive, 0); if(ret < 0 || o == NULL) return -1; o->read_pacifier_handle= read_handle; o->read_pacifier= read_pacifier; return(1); } #ifndef LIBISOBURN_LIBISOBURN_H_ #define LIBISOBURN_LIBISOBURN_H_ /* Lower level API definition of libisoburn. Copyright 2007-2009 Vreixo Formoso Lopes <metalpain2002@yahoo.es> Copyright 2007-2024 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef __cplusplus extern "C" { #endif /** Overview libisoburn is a frontend for libraries libburn and libisofs which enables creation and expansion of ISO-9660 filesystems on all CD/DVD/BD media supported by libburn. This includes media like DVD+RW, which do not support multi-session management on media level and even plain disk files or block devices. The price for that is thorough specialization on data files in ISO-9660 filesystem images. So libisoburn is not suitable for audio (CD-DA) or any other CD layout which does not entirely consist of ISO-9660 sessions. Note that there is a higher level of API: xorriso.h. One should not mix it with the API calls of libisoburn.h, libisofs.h, and libburn.h. Connector functions libisofs and libburn do not depend on each other but share some interfaces by which they can cooperate. libisoburn establishes the connection between both modules by creating the necessary interface objects and attaching them to the right places. Wrapper functions The principle of this frontend is that you may use any call of libisofs or libburn unless it has a isoburn_*() wrapper listed in the following function documentation. E.g. call isoburn_initialize() rather than iso_init(); burn_initialize(); and call isoburn_drive_scan_and_grab() rather than burn_drive_scan_and_grab(). But you may call burn_disc_get_profile() directly if you want to display the media type. The wrappers will transparently provide the necessary emulations which are appropriate for particular target drives and media states. To learn about them you have to read both API descriptions: the one of the wrapper and the one of the underlying libburn or libisofs call. Macros BURN_* and functions burn_*() are documented in <libburn/libburn.h> Macros ISO_* and functions iso_*() are documented in <libisofs/libisofs.h> Usage model There may be an input drive and an output drive. Either of them may be missing with the consequence that no reading or no writing is possible. Both drive roles can be fulfilled by the same drive. Input can be a random access readable libburn drive: optical media, regular files, block devices. Output can be any writeable libburn drive: writeable optical media in burner, writeable file objects (no directories). libburn demands rw-permissions to drive device file or file object. If the input drive provides a suitable ISO RockRidge image, then its tree may be loaded into memory and can then be manipulated by libisofs API calls. The loading is done by isoburn_read_image() under control of struct isoburn_read_opts which the application obtains from libisoburn and manipulates by the family of isoburn_ropt_set_*() functions. Writing of result images is controlled by libisofs related parameters in a struct isoburn_imgen_opts which the application obtains from libisoburn and manipulates by the family of isoburn_igopt_set_*() functions. All multi-session aspects are handled by libisoburn according to these settings. The application does not have to analyze media state and write job parameters. It rather states its desires which libisoburn tries to fulfill, or else will refuse to start the write run. Setup for Growing, Modifying or Blind Growing The connector function family offers alternative API calls for performing the setup for several alternative image generation strategies. Growing: If input and output drive are the same, then isoburn_prepare_disc() is to be used. It will lead to an add-on session on appendable or overwritable media with existing ISO image. With blank media it will produce a first session. Modifying: If the output drive is not the input drive, and if it bears blank media or overwritable without a valid ISO image, then one may produce a consolidated image with old and new data. This will copy file data from an eventual input drive with valid image, add any newly introduced data from the local filesystem, and produce a first session on output media. To prepare for such an image generation run, use isoburn_prepare_new_image(). Blind Growing: This method reads the old image from one drive and writes the add-on session to a different drive. That output drive is nevertheless supposed to finally lead to the same medium from where the session was loaded. Usually it will be stdio:/dev/fd/1 (i.e. stdout) being piped into some burn program like with this classic gesture: mkisofs -M $dev -C $msc1,$nwa | cdrecord -waiti dev=$dev Blind growing is prepared by the call isoburn_prepare_blind_grow(). The input drive should be released immediately after this call in order to allow the consumer of the output stream to access that drive for writing. After either of these setups, some peripheral libburn drive parameter settings like burn_write_opts_set_simulate(), burn_write_opts_set_multi(), burn_drive_set_speed(), burn_write_opts_set_underrun_proof() should be made. Do not set the write mode. It will be chosen by libisoburn so it matches job and media state. Writing the image Then one may start image generation and write threads by isoburn_disc_write(). Progress may be watched at the output drive by burn_drive_get_status() and isoburn_get_fifo_status(). At some time, the output drive will be BURN_DRIVE_IDLE indicating that writing has ended. One should inquire isoburn_drive_wrote_well() to learn about overall success. Finally one must call isoburn_activate_session() which will complete any eventual multi-session emulation. Application Constraints Applications shall include libisofs/libisofs.h , libburn/libburn.h and this file itself: libisoburn/libisoburn.h . They shall link with -lisofs -lburn -lisoburn or with the .o files emerging from building those libraries from their sources. Applications must use 64 bit off_t. E.g. on 32-bit GNU/Linux by defining #define _LARGEFILE_SOURCE #define _FILE_OFFSET_BITS 64 The minimum requirement is to interface with the library by 64 bit signed integers where libisofs.h or libisoburn.h prescribe off_t. Failure to do so may result in surprising malfunction or memory faults. Application files which include libisofs/libisofs.h or libisoburn/libisoburn.h must provide definitions for uint32_t and uint8_t. This can be achieved either: - by using autotools which will define HAVE_STDINT_H or HAVE_INTTYPES_H according to its ./configure tests, - or by defining the macros HAVE_STDINT_H or HAVE_INTTYPES_H according to the local situation, - or by appropriately defining uint32_t and uint8_t by other means, e.g. by including inttypes.h before including libisofs.h and libisoburn.h */ #ifdef HAVE_STDINT_H #include <stdint.h> #else #ifdef HAVE_INTTYPES_H #include <inttypes.h> #endif #endif /* Important: If you add a public API function then add its name to file libisoburn/libisoburn.ver in the node LIBISOBURN1_Major.Minor.Micro with the numbers of the next release version. */ /* API functions */ /** Initialize libisoburn, libisofs and libburn. Wrapper for : iso_init() and burn_initialize() @since 0.1.0 @param msg A character array for eventual messages (e.g. with errors) @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 indicates success, 0 is failure */ int isoburn_initialize(char msg[1024], int flag); /** Check whether all features of header file libisoburn.h from the given major.minor.micro revision triple can be delivered by the library version which is performing this call. An application of libisoburn can easily memorize the version of the libisoburn.h header in its own code. Immediately after isoburn_initialize() it should simply do this check: if (! isoburn_is_compatible(isoburn_header_version_major, isoburn_header_version_minor, isoburn_header_version_micro, 0)) ...refuse to start the program with this dynamic library version... @since 0.1.0 @param major obtained at build time @param minor obtained at build time @param micro obtained at build time @param flag Bitfield for control purposes. Unused yet. Submit 0. @return 1= library can work for caller 0= library is not usable in some aspects. Caller must restrict itself to an earlier API version or must not use this library at all. */ int isoburn_is_compatible(int major, int minor, int micro, int flag); /** Obtain the three release version numbers of the library. These are the numbers encountered by the application when linking with libisoburn, i.e. possibly not before run time. Better do not base the fundamental compatibility decision of an application on these numbers. For a reliable check use isoburn_is_compatible(). @since 0.1.0 @param major The maturity version (0 for now, as we are still learning) @param minor The development goal version. @param micro The development step version. This has an additional meaning: Pare numbers indicate a version with frozen API. I.e. you can rely on the same set of features to be present in all published releases with that major.minor.micro combination. Features of a pare release will stay available and ABI compatible as long as the SONAME of libisoburn stays "1". Currently there are no plans to ever change the SONAME. Odd numbers indicate that API upgrades are in progress. I.e. new features might be already present or they might be still missing. Newly introduced features may be changed incompatibly or even be revoked before release of a pare version. So micro revisions {1,3,5,7,9} should never be used for dynamic linking unless the proper library match can be guaranteed by external circumstances. */ void isoburn_version(int *major, int *minor, int *micro); /** The minimum version of libisofs to be used with this version of libisoburn at compile time. @since 0.1.0 */ #define isoburn_libisofs_req_major 1 #define isoburn_libisofs_req_minor 5 #define isoburn_libisofs_req_micro 6 /** The minimum version of libburn to be used with this version of libisoburn at compile time. @since 0.1.0 */ #define isoburn_libburn_req_major 1 #define isoburn_libburn_req_minor 5 #define isoburn_libburn_req_micro 7 /** The minimum compile time requirements of libisoburn towards libjte are the same as of a suitable libisofs towards libjte. So use these macros from libisofs.h : iso_libjte_req_major iso_libjte_req_minor iso_libjte_req_micro @since 0.6.4 */ /** The minimum version of libisofs to be used with this version of libisoburn at runtime. This is checked already in isoburn_initialize() which will refuse on outdated version. So this call is for information purposes after successful startup only. @since 0.1.0 @param major isoburn_libisofs_req_major as seen at build time @param minor as seen at build time @param micro as seen at build time @return 1 success, <=0 might in future become an error indication */ int isoburn_libisofs_req(int *major, int *minor, int *micro); /** The minimum version of libjte to be used with this version of libisoburn at runtime. The use of libjte is optional and depends on configure tests. It can be prevented by ./configure option --disable-libjte . This is checked already in isoburn_initialize() which will refuse on outdated version. So this call is for information purposes after successful startup only. @since 0.6.4 */ int isoburn_libjte_req(int *major, int *minor, int *micro); /** The minimum version of libburn to be used with this version of libisoburn at runtime. This is checked already in isoburn_initialize() which will refuse on outdated version. So this call is for information purposes after successful startup only. @since 0.1.0 @param major isoburn_libburn_req_major as seen at build time @param minor as seen at build time @param micro as seen at build time @return 1 success, <=0 might in future become an error indication */ int isoburn_libburn_req(int *major, int *minor, int *micro); /** These three release version numbers tell the revision of this header file and of the API it describes. They are memorized by applications at build time. @since 0.1.0 */ #define isoburn_header_version_major 1 #define isoburn_header_version_minor 5 #define isoburn_header_version_micro 7 /** Note: Above version numbers are also recorded in configure.ac because libtool wants them as parameters at build time. For the library compatibility check, ISOBURN_*_VERSION in configure.ac are not decisive. Only the three numbers here do matter. */ /** Usage discussion: Some developers of the libburnia project have differing opinions how to ensure the compatibility of libraries and applications. It is about whether to use at compile time and at runtime the version numbers isoburn_header_version_* provided here. Thomas Schmitt advises to use them. Vreixo Formoso advises to use other means. At compile time: Vreixo Formoso advises to leave proper version matching to properly programmed checks in the the application's build system, which will eventually refuse compilation. Thomas Schmitt advises to use the macros defined here for comparison with the application's requirements of library revisions and to eventually break compilation. Both advises are combinable. I.e. be master of your build system and have #if checks in the source code of your application, nevertheless. At runtime (via *_is_compatible()): Vreixo Formoso advises to compare the application's requirements of library revisions with the runtime library. This is to allow runtime libraries which are young enough for the application but too old for the lib*.h files seen at compile time. Thomas Schmitt advises to compare the header revisions defined here with the runtime library. This is to enforce a strictly monotonous chain of revisions from app to header to library, at the cost of excluding some older libraries. These two advises are mutually exclusive. ----------------------------------------------------- For an implementation of the Thomas Schmitt approach, see libisoburn/burn_wrap.c : isoburn_initialize() This connects libisoburn as "application" with libisofs as "library". The compatible part of Vreixo Formoso's approach is implemented in configure.ac LIBBURN_REQUIRED, LIBISOFS_REQUIRED. In isoburn_initialize() it would rather test by iso_lib_is_compatible(isoburn_libisofs_req_major,... than by iso_lib_is_compatible(iso_lib_header_version_major,... and would leave out the ugly compile time traps. */ /** Announce to the library an application provided method for immediate delivery of messages. It is used when no drive is affected directly or if the drive has no own msgs_submit() method attached by isoburn_drive_set_msgs_submit. If no method is preset or if the method is set to NULL then libisoburn delivers its messages through the message queue of libburn. @param msgs_submit The function call which implements the method @param submit_handle Handle to be used as first argument of msgs_submit @param submit_flag Flag to be used as last argument of msgs_submit @param flag Unused yet, submit 0 @since 0.2.0 */ int isoburn_set_msgs_submit(int (*msgs_submit)(void *handle, int error_code, char msg_text[], int os_errno, char severity[], int flag), void *submit_handle, int submit_flag, int flag); /** Acquire a target drive by its filesystem path or libburn persistent address. Wrapper for: burn_drive_scan_and_grab() @since 0.1.0 @param drive_infos On success returns a one element array with the drive (cdrom/burner). Thus use with driveno 0 only. On failure the array has no valid elements at all. The returned array should be freed via burn_drive_info_free() when the drive is no longer needed. But before this is done one has to call isoburn_drive_release(drive_infos[0].drive). @param adr The persistent address of the desired drive or the path to a file object. @param load 1 attempt to load the disc tray. 0 no attempt,rather failure. @return 1 = success , 0 = drive not found , <0 = other error */ int isoburn_drive_scan_and_grab(struct burn_drive_info *drive_infos[], char* adr, int load); /** Acquire a target drive by its filesystem path or libburn persistent address. This is a modern successor of isoburn_drive_scan_and_grab(). Wrapper for: burn_drive_scan_and_grab() @since 0.1.2 @param drive_infos On success returns a one element array with the drive (cdrom/burner). Thus use with driveno 0 only. On failure the array has no valid elements at all. The returned array should be freed via burn_drive_info_free() when the drive is no longer needed. But before this is done one has to call isoburn_drive_release(drive_infos[0].drive). @param adr The persistent address of the desired drive or the path to a file object. @param flag bit0= attempt to load the disc tray. Else: failure if not loaded. bit1= regard overwritable media as blank bit2= if the drive is a regular disk file: truncate it to the write start address when writing begins bit3= if the drive reports a read-only profile try to read table of content by scanning for ISO image headers. (depending on media type and drive this might help or it might make the resulting toc even worse) bit4= do not emulate table of content on overwritable media bit5= ignore ACL from external filesystems. This can later be overriden by isoburn_read_image() according to its isoburn_read_opts. bit6= ignore POSIX Extended Attributes from external filesystems (xattr) This can later be overriden by isoburn_read_image() according to its isoburn_read_opts. bit7= pretend read-only profile and scan for table of content bit8= re-assess already acquired (*drive_infos)[0] rather than acquiring adr @since 1.1.8 bit9= when scanning for ISO 9660 sessions by bit3: Do not demand a valid superblock at LBA 0, ignore it in favor of one at LBA 32, and scan until end of medium. @since 1.2.6 bit10= if not bit6: accept all xattr namespaces from external filesystems, not only "user.". @since 1.5.0 bit11= do not ignore Linux-like file attributes (chattr) from external filesystems. See occurences of "lfa_flags" and "chattr" in libisofs.h. This can later be overriden by isoburn_read_image() according to its isoburn_read_opts. @since 1.5.8 bit15= ignore non-settable lfa_flags when importing files from disk and do not record "isofs.fa" if the other flags are all zero @return 1 = success , 0 = drive not found , <0 = other error Please excuse the typo "aquire" in the function name. */ int isoburn_drive_aquire(struct burn_drive_info *drive_infos[], char* adr, int flag); /** Acquire a drive from the burn_drive_info[] array which was obtained by a previous call of burn_drive_scan(). Wrapper for: burn_drive_grab() @since 0.1.0 @param drive The drive to grab. E.g. drive_infos[1].drive . Call isoburn_drive_release(drive) when it it no longer needed. @param load 1 attempt to load the disc tray. 0 no attempt, rather failure. @return 1 success, <=0 failure */ int isoburn_drive_grab(struct burn_drive *drive, int load); /** Attach to a drive an application provided method for immediate delivery of messages. If no method is set or if the method is set to NULL then libisoburn delivers messages of the drive through the global msgs_submit() method set by isoburn_set_msgs_submiti() or by the message queue of libburn. @since 0.2.0 @param d The drive to which this function, handle and flag shall apply @param msgs_submit The function call which implements the method @param submit_handle Handle to be used as first argument of msgs_submit @param submit_flag Flag to be used as last argument of msgs_submit @param flag Unused yet, submit 0 */ int isoburn_drive_set_msgs_submit(struct burn_drive *d, int (*msgs_submit)(void *handle, int error_code, char msg_text[], int os_errno, char severity[], int flag), void *submit_handle, int submit_flag, int flag); /** Inquire the medium status. Expect the whole spectrum of libburn BURN_DISC_* with multi-session media. Emulated states with random access media are BURN_DISC_BLANK and BURN_DISC_APPENDABLE. Wrapper for: burn_disc_get_status() @since 0.1.0 @param drive The drive to inquire. @return The status of the drive, or what kind of disc is in it. Note: BURN_DISC_UNGRABBED indicates wrong API usage */ #ifdef __cplusplus enum burn::burn_disc_status isoburn_disc_get_status(struct burn_drive *drive); #else enum burn_disc_status isoburn_disc_get_status(struct burn_drive *drive); #endif /** Sets the medium status to BURN_DISC_FULL unconditionally. @since 1.3.8 @param drive The drive with the medium to be handled as if it was closed. @ */ int isoburn_disc_pretend_full_uncond(struct burn_drive *drive); /** Tells whether the medium can be treated by isoburn_disc_erase(). Wrapper for: burn_disc_erasable() @since 0.1.0 @param d The drive to inquire. @return 0=not erasable , else erasable */ int isoburn_disc_erasable(struct burn_drive *d); /** Mark the medium as blank. With multi-session media this will call burn_disc_erase(). With random access media, an eventual ISO-9660 filesystem will get invalidated by altering its start blocks on the medium. In case of success, the medium is in status BURN_DISC_BLANK afterwards. Wrapper for: burn_disc_erase() @since 0.1.0 @param drive The drive with the medium to erase. @param fast 1=fast erase, 0=thorough erase With DVD-RW, fast erase yields media incapable of multi-session. */ void isoburn_disc_erase(struct burn_drive *drive, int fast); /** Set up isoburn_disc_get_msc1() to return a fabricated value. This makes only sense between acquiring the drive and reading the image. After isoburn_read_image() it will confuse the coordination of libisoburn and libisofs. Note: Sessions and tracks are counted beginning with 1, not with 0. @since 0.1.6 @param d The drive where msc1 is to be set @param adr_mode Determines how to interpret adr_value and to set msc1. If adr_value shall represent a number then decimal ASCII digits are expected. 0= start lba of last session in TOC, ignore adr_value 1= start lba of session number given by adr_value 2= start lba of track number given by adr_value 3= adr_value itself is the lba to be used 4= start lba of last session with volume id given by adr_value @since 1.5.8 : 5= start lba of last session with modification time exactly as given by adr_value. adr_value is seconds since 1970 GMT (i.e. time_t as decimal number string) 6= start lba of last session with modification time not after adr_value (seconds after 1970 GMT) 7= start lba of first session with modification time not before adr_value (seconds after 1970 GMT) 8= start lba of last session with modification time before but not at adr_value (seconds after 1970 GMT) 9= start lba of first session with modification time after but not at adr_value (seconds after 1970 GMT @param adr_value A string describing the value to be eventually used. @param flag Bitfield for control purposes. bit0= @since 0.2.2 with adr_mode 3: adr_value might be 16 blocks too high (e.g. -C stemming from growisofs). Probe for ISO head at adr_value-16 and eventually adjust setting. bit1= insist in seeing a disc object with at least one session bit2= with adr_mode 4: use adr_value as regular expression with adr_mode 5 to 9: use creation time rather than modification time */ int isoburn_set_msc1(struct burn_drive *d, int adr_mode, char *adr_value, int flag); /* ----------------------------------------------------------------------- */ /* Wrappers for emulation of TOC on overwritable media Media which match the overwritable usage model lack of a history of sessions and tracks. libburn will not even hand out a burn_disc object for them and always declare them blank. libisoburn checks for a valid ISO filesystem header at LBA 0 and eventually declares them appendable. Nevertheless one can only determine an upper limit of the size of the overall image (by isoburn_get_min_start_byte()) but not a list of stored sessions and their LBAs, as it is possible with true multi-session media. The following wrappers add the capability to obtain a session and track TOC from emulated multi-session images on overwritables if the first session was written by libisoburn-0.1.6 or later (i.e. with a header copy at LBA 32). Be aware that the structs emitted by these isoburn calls are not compatible with the libburn structs. I.e. you may use them only with isoburn_toc_* calls. isoburn_toc_disc needs to be freed after use. isoburn_toc_session and isoburn_toc_track vanish together with their isoburn_toc_disc. */ /* Opaque handles to media, session, track */ struct isoburn_toc_disc; struct isoburn_toc_session; struct isoburn_toc_track; /** Obtain a master handle for the table of content. This handle governs allocated resources which have to be released by isoburn_toc_disc_free() when no longer needed. Wrapper for: burn_drive_get_disc() @since 0.1.6 @param d The drive with the medium to inspect @return NULL in case there is no content info, else it is a valid handle */ struct isoburn_toc_disc *isoburn_toc_drive_get_disc(struct burn_drive *d); /** Tell the number of 2048 byte blocks covered by the table of content. This number includes the eventual gaps between sessions and tracks. So this call is not really a wrapper for burn_disc_get_sectors(). @since 0.1.6 @param disc The master handle of the medium @return Number of blocks, <=0 indicates unknown or unreadable state */ int isoburn_toc_disc_get_sectors(struct isoburn_toc_disc *disc); /** Like isoburn_toc_disc_get_sectors(), but with off_t return value. @since 1.5.8 */ off_t isoburn_toc_disc_get_sectors_v2(struct isoburn_toc_disc *disc); /** Get the array of session handles and the number of complete sessions from the table of content. The result array contains *num + isoburn_toc_disc_get_incmpl_sess() elements. All above *num are incomplete sessions. Typically there is at most one incomplete session with no track. Wrapper for: burn_disc_get_sessions() @since 0.1.6 @param disc The master handle of the medium @param num returns the number of sessions in the array @return the address of the array of session handles */ struct isoburn_toc_session **isoburn_toc_disc_get_sessions( struct isoburn_toc_disc *disc, int *num); /** Obtain the number of incomplete sessions which are recorded in the result array of isoburn_toc_disc_get_sessions() after the complete sessions. See above. @since 1.2.8 @param disc The master handle of the medium @return the number of incomplete sessions */ int isoburn_toc_disc_get_incmpl_sess(struct isoburn_toc_disc *disc); /** Tell the number of 2048 byte blocks covered by a particular session. Wrapper for: burn_session_get_sectors() @since 0.1.6 @param s The session handle @return number of blocks, <=0 indicates unknown or unreadable state */ int isoburn_toc_session_get_sectors(struct isoburn_toc_session *s); /** Like isoburn_toc_session_get_sectors(), but with off_t return value. @since 1.5.8 */ off_t isoburn_toc_session_get_sectors_v2(struct isoburn_toc_session *s); /** Obtain a copy of the entry which describes the end of a particular session. Wrapper for: burn_session_get_leadout_entry() @since 0.1.6 @param s The session handle @param entry A pointer to memory provided by the caller. It will be filled with info according to struct burn_toc_entry as defined in libburn.h */ void isoburn_toc_session_get_leadout_entry(struct isoburn_toc_session *s, struct burn_toc_entry *entry); /** Get the array of track handles from a particular session. Wrapper for: burn_session_get_tracks() @since 0.1.6 @param s The session handle @param num returns the number of tracks in the array @return the address of the array of track handles, NULL if no tracks are registered with session s */ struct isoburn_toc_track **isoburn_toc_session_get_tracks( struct isoburn_toc_session *s, int *num); /** Obtain a copy of the entry which describes a particular track. Wrapper for: burn_track_get_entry() @since 0.1.6 @param t The track handle @param entry A pointer to memory provided by the caller. It will be filled with info according to struct burn_toc_entry as defined in libburn.h */ void isoburn_toc_track_get_entry(struct isoburn_toc_track *t, struct burn_toc_entry *entry); /** Obtain eventual ISO image parameters of an emulated track. This info was gained with much effort and thus gets cached in the track object. If this call returns 1 then one can save a call of isoburn_read_iso_head() with return mode 1 which could cause an expensive read operation. @since 0.4.0 @param t The track handle @param start_lba Returns the start address of the ISO session @param image_blocks Returns the number of 2048 bytes blocks @param volid Caller provided memory for the volume id @param flag unused yet, submit 0 @return 0= not an emulated ISO session , 1= reply is valid */ int isoburn_toc_track_get_emul(struct isoburn_toc_track *t, int *start_lba, int *image_blocks, char volid[33], int flag); /** Like isoburn_toc_track_get_emul() but with off_t reply type. @since 1.5.8 */ int isoburn_toc_track_get_emul_v2(struct isoburn_toc_track *t, off_t *start_lba, off_t *image_blocks, char volid[33], int flag); /** Release the memory associated with a master handle of a medium. The handle is invalid afterwards and may not be used any more. Wrapper for: burn_disc_free() @since 0.1.6 @param disc The master handle of the medium */ void isoburn_toc_disc_free(struct isoburn_toc_disc *disc); /** Try whether the data at the given address look like a ISO 9660 image header and obtain its alleged size. Depending on the info mode one string of text information or data can be retrieved too. @since 0.1.6 @param d The drive with the medium to inspect @param lba The block number from where to read @param image_blocks Returns the number of 2048 bytes blocks in the session @param info Caller provided memory, enough to take possible info reply @param flag bit0-7: info return mode 0= do not return anything in info (do not even touch it) 1= copy volume id to info (info needs 33 bytes) 2= @since 0.2.2 : copy 64 kB header to info (needs 65536 bytes) . No trailing zero gets appended. 3= @since 1.5.8 : copy creation time to info (info needs 18 bytes). info[16] is the binary timezone offset x 15 minutes east 4= @since 1.5.8 : copy modification time to info (info needs 18 bytes). info[16] is the binary timezone offset x 15 minutes east bit12= @since 1.5.6 : Read even if the start of multi-session emulation yielded a read error bit13= @since 0.2.2: Do not read head from medium but use first 64 kB from info. In this case it is permissible to submit d == NULL. bit14= check both half buffers (not only second) return 2 if found in first block bit15= return -1 on read error @return >0 seems to be a valid ISO image, 0 format not recognized, <0 error */ int isoburn_read_iso_head(struct burn_drive *d, int lba, int *image_blocks, char *info, int flag); /** Like isoburn_read_iso_head() but with off_t block address and count. @since 1.5.8 */ int isoburn_read_iso_head_v2(struct burn_drive *d, off_t lba, off_t *image_blocks, char *info, int flag); /** Try to convert the given entity address into various entity addresses which would describe it. Note: Sessions and tracks are counted beginning with 1, not with 0. @since 0.3.2 @param d The drive where msc1 is to be set @param adr_mode Determines how to interpret the input adr_value. If adr_value shall represent a number then decimal ASCII digits are expected. 0= start lba of last session in TOC, ignore adr_value 1= start lba of session number given by adr_value 2= start lba of track given number by adr_value 3= adr_value itself is the lba to be used 4= start lba of last session with volume id given by adr_value @since 1.5.8 : 5= start lba of last session with modification time exactly as given by adr_value. adr_value is seconds since 1970 GMT (i.e. time_t as decimal number string) 6= start lba of last session with modification time not after adr_value (seconds after 1970 GMT) 7= start lba of first session with modification time not before adr_value (seconds after 1970 GMT) 8= start lba of last session with modification time before but not at adr_value (seconds after 1970 GMT) 9= start lba of first session with modification time after but not at adr_value (seconds after 1970 GMT @param adr_value A string describing the value to be eventually used. @param lba returns the block address of the entity, -1 means invalid @param track returns the track number of the entity, -1 means invalid @param session returns the session number of the entity, -1 means invalid @param volid returns the volume id of the entity if it is a ISO session @param flag Bitfield for control purposes. bit2= with adr_mode 4: use adr_value as regular expression with adr_mode 5 to 9: use creation time rather than modification time @return <=0 error , 1 ok, ISO session, 2 ok, not an ISO session */ int isoburn_get_mount_params(struct burn_drive *d, int adr_mode, char *adr_value, int *lba, int *track, int *session, char volid[33], int flag); /** Like isoburn_get_mount_params() but with off_t block address type. @since 1.5.8 */ int isoburn_get_mount_params_v2(struct burn_drive *d, int adr_mode, char *adr_value, off_t *lba, int *track, int *session, char volid[33], int flag); /* ----------------------------------------------------------------------- */ /* Options for image reading. An application shall create an option set object by isoburn_ropt_new(), program it by isoburn_ropt_set_*(), use it with isoburn_read_image(), and finally delete it by isoburn_ropt_destroy(). */ /* ----------------------------------------------------------------------- */ struct isoburn_read_opts; /** Produces a set of image read options, initialized with default values. @since 0.1.0 @param o the newly created option set object @param flag Bitfield for control purposes. Submit 0 for now. @return 1=ok , <0 = failure */ int isoburn_ropt_new(struct isoburn_read_opts **o, int flag); /** Deletes an option set which was created by isoburn_ropt_new(). @since 0.1.0 @param o The option set to work on @param flag Bitfield for control purposes. Submit 0 for now. @return 1= **o destroyed , 0= *o was already NULL (harmless) */ int isoburn_ropt_destroy(struct isoburn_read_opts **o, int flag); /** Sets the size and granularity of the cache which libisoburn provides to libisofs for reading of ISO image data. This cache consists of several tiles which are buffers of a given size. The ISO image is divided into virtual tiles of that size. A cache tile may hold an in-memory copy of such a virtual image tile. When libisofs requests to read a block, then first the cache is inquired whether it holds that block. If not, then the block is read via libburn together with its neighbors in their virtual image tile into a free cache tile. If no cache tile is free, then the one will be re-used which has the longest time of not being hit by a read attempt. A larger cache might speed up image loading by reducing the number of libburn read calls on the directory tree. It might also help with reading the content of many small files, if for some reason it is not an option to sort access by LBA. Caching will not provide much benefit with libburn "stdio:" drives, because the operating system is supposed to provide the same speed-up in a more flexible way. @since 1.2.2 @param o The option set to work on. It is permissible to submit NULL in order to just have the parameters tested. @param cache_tiles Number of tiles in the cache. Not less than 1. Default is 32. @param tile_blocks Number of blocks per tile. Must be a power of 2. Default is 32. cache_tiles * tile_blocks * 2048 must not exceed 1073741824 (= 1 GiB). @param flag Bitfield for control purposes. Unused yet. Submit 0. @return <=0 error , >0 ok */ int isoburn_ropt_set_data_cache(struct isoburn_read_opts *o, int cache_tiles, int tile_blocks, int flag); /** Inquire the current settings of isoburn_set_data_cache(). @since 1.2.2 @param o The option set to work on. NULL has the same effect as flag bit0. @param cache_tiles Will return the number of tiles in the cache. @param tile_blocks Will return the number of blocks per tile. @param set_flag Will return control bits. None are defined yet. @param flag Bitfield for control purposes bit0= return default values rather than current ones @return <=0 error , >0 reply is valid */ int isoburn_ropt_get_data_cache(struct isoburn_read_opts *o, int *cache_tiles, int *tile_blocks, int *set_flag, int flag); /** Which existing ISO 9660 extensions in the image or in external filesystems to read or not to read, and whether to read the content of an existing image at all. The bits can be combined by | and inquired by &. @since 0.1.0 @param ext Bitfield: bit0= norock Do not read Rock Ridge extensions bit1= nojoliet Do not read Joliet extensions bit2= noiso1999 Do not read ISO 9660:1999 enhanced tree bit3= preferjoliet When both Joliet and RR extensions are present, the RR tree is used. If you prefer using Joliet, set this to 1. bit4= pretend_blank Always create empty image.Ignore any image on input drive. bit5= noaaip @since 0.3.4 Do not load AAIP information from image. This information eventually contains ACL or XFS-style Extended Attributes. bit6= noacl @since 0.3.4 Do not obtain ACL from external filesystem objects (e.g. local filesystem files). This overrides the setting by isoburn_drive_aquire(). bit7= noea @since 0.3.4 Do not obtain XFS-style Extended Attributes (xattr) from external filesystem objects (e.g. local filesystem files). This overrides the setting by isoburn_drive_aquire(). bit8= noino @since 0.4.0 Do not load eventual inode numbers from RRIP entry PX, but generate a new unique inode number for each imported IsoNode object. PX inode numbers mark families of hardlinks by giving all family members the same inode number. libisofs keeps the PX inode numbers unaltered when IsoNode objects get written into an ISO image. bit9= nomd5 @since 0.4.2 Do not load the possibly present MD5 checksum array. Do not check possibly present session_md5 tags. bit10= nomd5tag @since 1.0.4 Do not check session_md5 tags although bit9 is not set. bit11= do_ecma119_map @since 1.4.2 Set iso_read_opts_set_ecma119_map() to map_mode rather than relying on the default setting of libisofs. bit12 - bit13= map_mode @since 1.4.2 How to convert file names if neither Rock Ridge nor Joliet names are present and acceptable. 0 = unmapped: Take name as recorded in ECMA-119 directory record (not suitable for writing them to a new ISO filesystem) 1 = stripped: Like unmapped, but strip off trailing ";1" or ".;1" 2 = uppercase: Like stripped, but map {a-z} to {A-Z} 3 = lowercase: Like stripped, but map {A-Z} to {a-z} bit14= do_joliet_map @since 1.5.4 Set iso_read_opts_set_joliet_map() to joliet_map_mode rather than relying on the default setting of libisofs. bit15= joliet_map_mode @since 1.5.4 How to convert Joliet file names. 0 = unmapped: Take name as recorded in Joliet directory record (not suitable for writing them to a new ISO filesystem) 1 = stripped: strip off trailing ";1" or ".;1" bit16= lfa_flags Obtain Linux file attribute flags (chattr) from external filesystem objects (e.g. local filesystem files). This overrides the setting by isoburn_drive_aquire(). @since 1.5.8 bit17= lfa_only_settable gnore non-settable lfa_flags when importing files from external filesystem objects (e.g. local filesystem files). This overrides the setting by isoburn_drive_aquire(). @since 1.5.8 @return 1 success, <=0 failure */ #define isoburn_ropt_norock 1 #define isoburn_ropt_nojoliet 2 #define isoburn_ropt_noiso1999 4 #define isoburn_ropt_preferjoliet 8 #define isoburn_ropt_pretend_blank 16 #define isoburn_ropt_noaaip 32 #define isoburn_ropt_noacl 64 #define isoburn_ropt_noea 128 #define isoburn_ropt_noino 256 #define isoburn_ropt_nomd5 512 #define isoburn_ropt_nomd5tag 1024 #define isoburn_ropt_map_unmapped ( 2048 | 0 ) #define isoburn_ropt_map_stripped ( 2048 | 4096 ) #define isoburn_ropt_map_uppercase ( 2048 | 8192 ) #define isoburn_ropt_map_lowercase ( 2048 | 12288 ) #define isoburn_ropt_joliet_unmapped ( 16384 | 0) #define isoburn_ropt_joliet_stripped ( 16384 | 32768) #define isoburn_ropt_lfa_flags (1 << 16) #define isoburn_ropt_lfa_only_settable (1 << 17) int isoburn_ropt_set_extensions(struct isoburn_read_opts *o, int ext); int isoburn_ropt_get_extensions(struct isoburn_read_opts *o, int *ext); /** Default attributes to use if no RockRidge extension gets loaded. @since 0.1.0 @param o The option set to work on @param uid user id number (see /etc/passwd) @param gid group id number (see /etc/group) @param mode permissions (not file type) as of man 2 stat. With directories, r-permissions will automatically imply x-permissions. See isoburn_ropt_set_default_dirperms() below. @return 1 success, <=0 failure */ int isoburn_ropt_set_default_perms(struct isoburn_read_opts *o, uid_t uid, gid_t gid, mode_t mode); int isoburn_ropt_get_default_perms(struct isoburn_read_opts *o, uid_t *uid, gid_t *gid, mode_t *mode); /** Default attributes to use on directories if no RockRidge extension gets loaded. Above call isoburn_ropt_set_default_perms() automatically adds x-permissions to r-permissions for directories. This call here may be done afterwards to set independent permissions for directories, especially to override the automatically added x-permissions. @since 0.1.0 @param o The option set to work on @param mode permissions (not file type) as of man 2 stat. @return 1 success, <=0 failure */ int isoburn_ropt_set_default_dirperms(struct isoburn_read_opts *o, mode_t mode); int isoburn_ropt_get_default_dirperms(struct isoburn_read_opts *o, mode_t *mode); /** Set the character set for reading RR file names from ISO images. @since 0.1.0 @param o The option set to work on @param input_charset Set this to NULL to use the default locale charset For selecting a particular character set, submit its name, e.g. as listed by program iconv -l. Example: "UTF-8". @return 1 success, <=0 failure */ int isoburn_ropt_set_input_charset(struct isoburn_read_opts *o, char *input_charset); int isoburn_ropt_get_input_charset(struct isoburn_read_opts *o, char **input_charset); /** Enable or disable methods to automatically choose an input charset. This eventually overrides the name set via isoburn_ropt_set_input_charset() @since 0.3.8 @param o The option set to work on @param mode Bitfield for control purposes: bit0= set the input character set automatically from attribute "isofs.cs" of root directory. Submit any other bits with value 0. @return 1 success, <=0 failure */ int isoburn_ropt_set_auto_incharset(struct isoburn_read_opts *o, int mode); int isoburn_ropt_get_auto_incharset(struct isoburn_read_opts *o, int *mode); /** Control an offset to be applied to all block address pointers in the ISO image in order to compensate for an eventual displacement of the image relative to the start block address for which it was produced. E.g. if track number 2 from CD gets copied into a disk file and shall then be loaded as ISO filesystem, then the directory tree and all data file content of the track copy will become readable by setting the track start address as displacement and -1 as displacement_sign. Data file content outside the track will of course not be accessible and eventually produce read errors. @since 0.6.6 @param o The option set to work on @param displacement 0 or a positive number @param displacement_sign Determines whether to add or subtract displacement to block addresses before applying them to the storage object for reading: +1 = add , -1= subtract , else keep unaltered */ int isoburn_ropt_set_displacement(struct isoburn_read_opts *o, uint32_t displacement, int displacement_sign); int isoburn_ropt_get_displacement(struct isoburn_read_opts *o, uint32_t *displacement, int *displacement_sign); /* If you get here because of a compilation error like /usr/include/libisoburn/libisoburn.h:895: error: expected declaration specifiers or '...' before 'uint32_t' then see above paragraph "Application Constraints" about the definition of uint32_t. */ /** Set the name truncation mode and the maximum name length for imported file objects. @since 1.4.2 @param o The option set to work on @param mode 0= Do not truncate but throw error ISO_RR_NAME_TOO_LONG if a file name is longer than parameter length. 1= Truncate to length and overwrite the last 32 bytes of that length by the hex representation of the MD5 of the whole oversized name. Potential incomplete UTF-8 characters will get their leading bytes replaced by '_'. This is the default. @param length Maximum byte count of a file name. Permissible values are 64 to 255. Default is 255. */ int isoburn_ropt_set_truncate_mode(struct isoburn_read_opts *o, int mode, int length); int isoburn_ropt_get_truncate_mode(struct isoburn_read_opts *o, int *mode, int *length); /** After calling function isoburn_read_image() there are information available in the option set about the size and the available extra trees and extensions in the ISO filesystem. This info can be obtained as bits in parameter has_what. Like: joliet_available = (has_what & isoburn_ropt_has_joliet); @since 0.1.0 @param o The option set to work on @param size Number of image data blocks, 2048 bytes each. @param has_what Bitfield: bit0= has_rockridge RockRidge extension info is available in the ISO 9660 tree (POSIX filesystem) bit1= has_joliet Joliet tree is available (suitable for MS-Windows) bit2= has_iso1999 ISO version 2 Enhanced Volume Descriptor aka ISO 9660:1999 and its tree is available. This is rather exotic. bit3= has_el_torito El-Torito boot record is present @return 1 success, <=0 failure */ #define isoburn_ropt_has_rockridge 1 #define isoburn_ropt_has_joliet 2 #define isoburn_ropt_has_iso1999 4 #define isoburn_ropt_has_el_torito 8 int isoburn_ropt_get_size_what(struct isoburn_read_opts *o, uint32_t *size, int *has_what); /* ts A90122 */ /* >>> to be implemented: #define isoburn_ropt_has_acl 64 #define isoburn_ropt_has_ea 128 */ /** After calling function isoburn_read_image() there are information available in the option set about which tree was used for image loading and whether Rock Ridge information was actually used. @since 1.5.4 @param o The option set to work on @param tree The tree which was loaded: 0= ISO 9660 , 1 = Joliet , 2 = ISO 9660:1999 @param rr 1= Rock Ridge information was used, 0 = No Rock Ridge was used @return 1 success, <=0 failure */ int isoburn_ropt_get_tree_loaded(struct isoburn_read_opts *o, int *tree, int *rr); /* ----------------------------------------------------------------------- */ /* End of Options for image reading */ /* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */ /* Options for image generation by libisofs and image transport to libburn. An application shall create an option set by isoburn_igopt_new(), program it by isoburn_igopt_set_*(), use it with either isoburn_prepare_new_image() or isoburn_prepare_disc(), and finally delete it by isoburn_igopt_destroy(). */ /* ----------------------------------------------------------------------- */ struct isoburn_imgen_opts; /** Produces a set of generation and transfer options, initialized with default values. @since 0.1.0 @param o the newly created option set object @param flag Bitfield for control purposes. Submit 0 for now. @return 1=ok , <0 = failure */ int isoburn_igopt_new(struct isoburn_imgen_opts **o, int flag); /** Deletes an option set which was created by isoburn_igopt_new(). @since 0.1.0 @param o The option set to give up @param flag Bitfield for control purposes. Submit 0 for now. @return 1= **o destroyed , 0= *o was already NULL (harmless) */ int isoburn_igopt_destroy(struct isoburn_imgen_opts **o, int flag); /** ISO level to write at. @since 0.1.0 @param o The option set to work on @param level is a term of the ISO 9660 standard. It should be one of: 1= filenames restricted to form 8.3 2= filenames allowed up to 31 characters 3= file content may be larger than 4 GB - 1. @return 1 success, <=0 failure */ int isoburn_igopt_set_level(struct isoburn_imgen_opts *o, int level); int isoburn_igopt_get_level(struct isoburn_imgen_opts *o, int *level); /** Which extensions to support. @since 0.1.0 @param o The option set to work on @param ext Bitfield: bit0= rockridge Rock Ridge extensions add POSIX file attributes like owner, group, access permissions, long filenames. Very advisable if the designed audience has Unix style systems. bit1= joliet Longer filenames for Windows systems. Weaker than RockRidge, but also readable with GNU/Linux. bit2= iso1999 This is rather exotic. Better do not surprise the readers. bit3= hardlinks Enable hardlink consolidation. IsoNodes which refer to the same source object and have the same properties will get the same ISO image inode numbers. If combined with isoburn_igopt_rrip_version_1_10 below, then the PX entry layout of RRIP-1.12 will be used within RRIP-1.10 (mkisofs does this without causing visible trouble). bit5= aaip The libisofs specific SUSP based extension of ECMA-119 which can encode ACL and XFS-style Extended Attributes. bit6= session_md5 @since 0.4.2 Produce and write MD5 checksum tags of superblock, directory tree, and the whole session stream. bit7= file_md5 @since 0.4.2 Produce and write MD5 checksums for each single IsoFile. bit8= file_stability (only together with file_md5) @since 0.4.2 Compute MD5 of each file before copying it into the image and compare this with the MD5 of the actual copying. If they do not match then issue MISHAP event. See also libisofs.h iso_write_opts_set_record_md5() bit9= no_emul_toc @since 0.5.8 On overwritable media or random access files do not write the first session to LBA 32 and do not copy the first 64kB of the first session to LBA 0, but rather write the first session to LBA 0 directly. bit10= will_cancel @since 0.6.6 Announce to libisofs that only the image size is desired and that the write thread will be cancelled by isoburn_cancel_prepared_write() before actual image writing occurs. Without this, cancellation can cause a MISHAP event. bit11= old_empty @since 1.0.2 Let symbolic links and device files point to block 0, and let empty data files point to the address of the Volume Descriptor Set Terminator. This was done by libisofs in the past. By default there is now a single dedicated block of zero bytes after the end of the directory trees, of which the address is used for all files without own content. bit12= hfsplus @since 1.2.4 Produce a HFS+ partition inside the ISO image and announce it by an Apple Partition Map in the System Area. >>> GPT production ? Caution: Interferes with isoburn_igopt_set_system_area() by overwriting the first 8 bytes of the data, and several blocks of 2 KiB after the first one. bit13= fat @since 1.2.4 >>> Not yet implemented. Planned to co-exist with hfsplus. Produce a FAT32 partition inside the ISO image and announce it by an MBR partition entry in the System Area. Caution: Interferes with isoburn_igopt_set_system_area() by >>> what impact ? @return 1 success, <=0 failure */ #define isoburn_igopt_rockridge 1 #define isoburn_igopt_joliet 2 #define isoburn_igopt_iso1999 4 #define isoburn_igopt_hardlinks 8 #define isoburn_igopt_aaip 32 #define isoburn_igopt_session_md5 64 #define isoburn_igopt_file_md5 128 #define isoburn_igopt_file_stability 256 #define isoburn_igopt_no_emul_toc 512 #define isoburn_igopt_will_cancel 1024 #define isoburn_igopt_old_empty 2048 #define isoburn_igopt_hfsplus 4096 #define isoburn_igopt_fat 8192 int isoburn_igopt_set_extensions(struct isoburn_imgen_opts *o, int ext); int isoburn_igopt_get_extensions(struct isoburn_imgen_opts *o, int *ext); /** Relaxed constraints. Setting any of the bits to 1 break the specifications, but it is supposed to work on most moderns systems. Use with caution. @since 0.1.0 @param o The option set to work on @param relax Bitfield: bit0= omit_version_numbers Omit the version number (";1") at the end of the ISO-9660 and Joliet identifiers. Version numbers are usually not used by readers. bit1= allow_deep_paths Allow ISO-9660 directory hierarchy to be deeper than 8 levels. bit2= allow_longer_paths Allow path in the ISO-9660 tree to have more than 255 characters. bit3= max_37_char_filenames Allow a single file or directory hierarchy to have up to 37 characters. This is larger than the 31 characters allowed by ISO level 2, and the extra space is taken from the version number, so this also forces omit_version_numbers. bit4= no_force_dots ISO-9660 forces filenames to have a ".", that separates file name from extension. libisofs adds it if original filename has none. Set this to 1 to prevent this behavior. bit5= allow_lowercase Allow lowercase characters in ISO-9660 filenames. By default, only uppercase characters, numbers and a few other characters are allowed. bit6= allow_full_ascii Allow all ASCII characters to be appear on an ISO-9660 filename. Note that "/" and "\0" characters are never allowed, even in RR names. bit7= joliet_longer_paths Allow paths in the Joliet tree to have more than 240 characters. bit8= always_gmt Write timestamps as GMT although the specs prescribe local time with eventual non-zero timezone offset. Negative timezones (west of GMT) can trigger bugs in some operating systems which typically appear in mounted ISO images as if the timezone shift from GMT was applied twice (e.g. in New York 22:36 becomes 17:36). bit9= rrip_version_1_10 Write Rock Ridge info as of specification RRIP-1.10 rather than RRIP-1.12: signature "RRIP_1991A" rather than "IEEE_1282", field PX without file serial number. bit10= dir_rec_mtime Store as ECMA-119 Directory Record timestamp the mtime of the source rather than the image creation time. bit11= aaip_susp_1_10 Write AAIP fields without announcing AAIP by an ER field and without distinguishing RRIP fields from the AAIP field by prefixed ES fields. This saves 5 to 10 bytes per file and might avoid problems with readers which only accept RRIP. SUSP-1.10 allows it, SUSP-1.12 frowns on it. bit12= only_iso_versions Same as bit1 omit_version_number but restricted to the names in the eventual Joliet tree. @since 0.5.4 For reasons of backward compatibility it is not possible yet to disable version numbers for ISO 9660 while enabling them for Joliet. bit13= no_j_force_dots Same as no_force_dots but affecting the names in the eventual Joliet tree rather than the ISO 9660 / ECMA-119 names. @since 0.5.4 Previous versions added dots to Joliet names unconditionally. bit14= allow_dir_id_ext Convert directory names for ECMA-119 similar to other file names, but do not force a dot or add a version number. This violates ECMA-119 by allowing one "." and especially ISO level 1 by allowing DOS style 8.3 names rather than only 8 characters. (mkisofs and its clones obviously do this violation.) @since 1.0.0 bit15= joliet_long_names Allow for Joliet leaf names up to 103 characters rather than up to 64. @since 1.0.6 bit16= joliet_rec_mtime Like dir_rec_mtime, but for the Joliet tree. @since 1.2.0 bit17= iso1999_rec_mtime Like dir_rec_mtime, but for the ISO 9660:1999 tree. @since 1.2.0 bit18= allow_7bit_ascii Like allow_full_ascii, but only allowing 7-bit characters. Lowercase letters get mapped to uppercase if not allow_lowercase is set. Gets overridden if allow_full_ascii is enabled. bit19= joliet_utf16 Encode Joliet names by character set UTF-16BE rather than UCS-2. The difference is with characters which are not present in UCS-2 and get encoded in UTF-16 by 2 words of 16 bit each. Both words then stem from a reserved subset of UCS-2. @since 1.3.6 @return 1 success, <=0 failure */ #define isoburn_igopt_omit_version_numbers 1 #define isoburn_igopt_allow_deep_paths 2 #define isoburn_igopt_allow_longer_paths 4 #define isoburn_igopt_max_37_char_filenames 8 #define isoburn_igopt_no_force_dots 16 #define isoburn_igopt_allow_lowercase 32 #define isoburn_igopt_allow_full_ascii 64 #define isoburn_igopt_joliet_longer_paths 128 #define isoburn_igopt_always_gmt 256 #define isoburn_igopt_rrip_version_1_10 512 #define isoburn_igopt_dir_rec_mtime 1024 #define isoburn_igopt_aaip_susp_1_10 2048 #define isoburn_igopt_only_iso_versions 4096 #define isoburn_igopt_no_j_force_dots 8192 #define isoburn_igopt_allow_dir_id_ext 16384 #define isoburn_igopt_joliet_long_names 32768 #define isoburn_igopt_joliet_rec_mtime 0x10000 #define isoburn_igopt_iso1999_rec_mtime 0x20000 #define isoburn_igopt_allow_7bit_ascii 0x40000 #define isoburn_igopt_joliet_utf16 0x80000 int isoburn_igopt_set_relaxed(struct isoburn_imgen_opts *o, int relax); int isoburn_igopt_get_relaxed(struct isoburn_imgen_opts *o, int *relax); /** If not isoburn_igopt_allow_deep_paths is in effect, then it may become necessary to relocate directories so that no ECMA-119 file path has more than 8 components. These directories are grafted into either the root directory of the ISO image or into a dedicated relocation directory. For details see libisofs.h. Wrapper for: iso_write_opts_set_rr_reloc() @since 1.2.2 @param o The option set to work on @param name The name of the relocation directory in the root directory. Do not prepend "/". An empty name or NULL will direct relocated directories into the root directory. This is the default. If the given name does not exist in the root directory when isoburn_disc_write() is called, and if there are directories at path level 8, then directory /name will be created automatically. @param flags Bitfield for control purposes. bit0= Mark the relocation directory by a Rock Ridge RE entry, if it gets created during isoburn_disc_write(). This will make it invisible for most Rock Ridge readers. bit1= not settable via API (used internally) @return > 0 success, <= 0 failure */ int isoburn_igopt_set_rr_reloc(struct isoburn_imgen_opts *o, char *name, int flags); /** Obtain the settings of isoburn_igopt_set_rr_reloc(). @since 1.2.2 @param o The option set to work on @param name Will return NULL or a pointer to the name of the relocation directory in the root directory. Do not alter or dispose the memory which holds this name. @param flags Will return the flags bitfield. @return > 0 success, <= 0 failure */ int isoburn_igopt_get_rr_reloc(struct isoburn_imgen_opts *o, char **name, int *flags); /** Caution: This option breaks any assumptions about names that are supported by ECMA-119 specifications. Try to omit any translation which would make a file name compliant to the ECMA-119 rules. This includes and exceeds omit_version_numbers, max_37_char_filenames, no_force_dots bit0, allow_full_ascii. Further it prevents the conversion from local character set to ASCII. The maximum name length is given by this call. If a filename exceeds this length or cannot be recorded untranslated for other reasons, then image production gets aborted. Currently the length limit is 96 characters, because an ECMA-119 directory record may at most have 254 bytes and up to 158 other bytes must fit into the record. Probably 96 more bytes can be made free for the name in future. @since 1.0.0 @param o The option set to work on @param len 0 = disable this feature and perform name translation according to other settings. >0 = Omit any translation. Eventually abort image production if a name is longer than the given value. -1 = Like >0. Allow maximum possible length. isoburn_igopt_get_untranslated_name_len() will tell the effectively resulting value. @return >0 success, <=0 failure */ int isoburn_igopt_set_untranslated_name_len(struct isoburn_imgen_opts *o, int len); int isoburn_igopt_get_untranslated_name_len(struct isoburn_imgen_opts *o, int *len); /** Whether and how files should be sorted. @since 0.1.0 @param o The option set to work on @param value Bitfield: bit0= sort_files_by_weight files should be sorted based on their weight. Weight is attributed to files in the image by libisofs call iso_node_set_sort_weight(). @return 1 success, <=0 failure */ #define isoburn_igopt_sort_files_by_weight 1 int isoburn_igopt_set_sort_files(struct isoburn_imgen_opts *o, int value); int isoburn_igopt_get_sort_files(struct isoburn_imgen_opts *o, int *value); /** Set the override values for files and directory permissions. The parameters replace_* these take one of three values: 0, 1 or 2. If 0, the corresponding attribute will be kept as set in the IsoNode at the time of image generation. If set to 1, the corresponding attrib. will be changed by a default suitable value. With value 2, the attrib. will be changed with the value specified in the corresponding *_mode options. Note that only the permissions are set, the file type remains unchanged. @since 0.1.0 @param o The option set to work on @param replace_dir_mode whether and how to override directories @param replace_file_mode whether and how to override files of other type @param dir_mode Mode to use on dirs with replace_dir_mode == 2. @param file_mode; Mode to use on files with replace_file_mode == 2. @return 1 success, <=0 failure */ int isoburn_igopt_set_over_mode(struct isoburn_imgen_opts *o, int replace_dir_mode, int replace_file_mode, mode_t dir_mode, mode_t file_mode); int isoburn_igopt_get_over_mode(struct isoburn_imgen_opts *o, int *replace_dir_mode, int *replace_file_mode, mode_t *dir_mode, mode_t *file_mode); /** Set the override values values for group id and user id. The rules are like with above overriding of mode values. replace_* controls whether and how. The other two parameters provide values for eventual use. @since 0.1.0 @param o The option set to work on @param replace_uid whether and how to override user ids @param replace_gid whether and how to override group ids @param uid User id to use with replace_uid == 2. @param gid Group id to use on files with replace_gid == 2. @return 1 success, <=0 failure */ int isoburn_igopt_set_over_ugid(struct isoburn_imgen_opts *o, int replace_uid, int replace_gid, uid_t uid, gid_t gid); int isoburn_igopt_get_over_ugid(struct isoburn_imgen_opts *o, int *replace_uid, int *replace_gid, uid_t *uid, gid_t *gid); /** Set the character set to use for representing RR filenames in the image. @since 0.1.0 @param o The option set to work on @param output_charset Set this to NULL to use the default output charset. For selecting a particular character set, submit its name, e.g. as listed by program iconv -l. Example: "UTF-8". @return 1 success, <=0 failure */ int isoburn_igopt_set_out_charset(struct isoburn_imgen_opts *o, char *output_charset); int isoburn_igopt_get_out_charset(struct isoburn_imgen_opts *o, char **output_charset); /** The number of bytes to be used for the fifo which decouples libisofs and libburn for better throughput and for reducing the risk of interrupting signals hitting the libburn thread which operates the MMC drive. The size will be rounded up to the next full 2048. Minimum is 64kiB, maximum is 1 GiB (but that is too much anyway). @since 0.1.0 @param o The option set to work on @param fifo_size Number of bytes to use @return 1 success, <=0 failure */ int isoburn_igopt_set_fifo_size(struct isoburn_imgen_opts *o, int fifo_size); int isoburn_igopt_get_fifo_size(struct isoburn_imgen_opts *o, int *fifo_size); /** Obtain after image preparation the block address where the session will start on the medium. This value cannot be set by the application but only be inquired. @since 0.1.4 @param o The option set to work on @param lba The block number of the session start on the medium. <0 means that no address has been determined yet. @return 1 success, <=0 failure */ int isoburn_igopt_get_effective_lba(struct isoburn_imgen_opts *o, int *lba); /** Like isoburn_igopt_get_effective_lba(), but with off_t block addresses. @since 1.5.8 */ int isoburn_igopt_get_effective_lba_v2(struct isoburn_imgen_opts *o, off_t *lba); /** Obtain after image preparation the lowest block address of file content data. Failure can occur if libisofs is too old to provide this information, if the result exceeds 31 bit, or if the call is made before image preparation. This value cannot be set by the application but only be inquired. @since 0.3.6 @param o The option set to work on @param lba The block number of the session start on the medium. <0 means that no address has been determined yet. @return 1 success, <=0 failure */ int isoburn_igopt_get_data_start(struct isoburn_imgen_opts *o, int *lba); /** Like isoburn_igopt_get_data_start(), but with off_t block addresses. @since 1.5.8 */ int isoburn_igopt_get_data_start_v2(struct isoburn_imgen_opts *o, off_t *lba); /** Set or get parameters "name" and "timestamp" for a scdbackup checksum tag. It will be appended to the libisofs session tag if the image starts at LBA 0. See isoburn_disc_track_lba_nwa. The scdbackup tag can be used to verify the image by command scdbackup_verify $device -auto_end. See scdbackup/README appendix VERIFY for its inner details. @since 0.4.4 @param o The option set to work on @param name The tag name. 80 characters max. An empty name disables production of an scdbackup tag. @param timestamp A string of up to 13 characters YYMMDD.hhmmss A9 = 2009, B0 = 2010, B1 = 2011, ... C0 = 2020, ... @param tag_written Either NULL or the address of an array with at least 512 characters. In the latter case the eventually produced scdbackup tag will be copied to this array when the image gets written. This call sets scdbackup_tag_written[0] = 0 to mark its preliminary invalidity. @return 1 success, <=0 failure */ int isoburn_igopt_set_scdbackup_tag(struct isoburn_imgen_opts *o, char *name, char *timestamp, char *tag_written); int isoburn_igopt_get_scdbackup_tag(struct isoburn_imgen_opts *o, char name[81], char timestamp[19], char **tag_written); /** Attach 32 kB of binary data which shall get written to the first 32 kB of the ISO image, the System Area. options can cause manipulations of these data before writing happens. If system area data are given or options bit0 is set, then bit1 of el_torito_set_isolinux_options() is automatically disabled. (For the meaning of options see also in libisofs.h iso_write_opts_set_system_area() parameter "options".) @since 0.5.4 @param o The option set to work on @param data Either NULL or 32 kB of data. Do not submit less bytes ! @param options Can cause manipulations of submitted data before they get written: bit0= apply a --protective-msdos-label as of grub-mkisofs. This means to patch bytes 446 to 512 of the system area so that one partition is defined which begins at the second 512-byte block of the image and ends where the image ends. This works with and without system_area_data. bit1= apply isohybrid MBR patching to the system area. This works only with system area data from SYSLINUX plus an ISOLINUX boot image (see iso_image_set_boot_image()) and only if not bit0 is set. bit2-7= System area type 0= with bit0 or bit1: MBR else: unspecified type @since 0.6.4 1= MIPS Big Endian Volume Header Submit up to 15 MIPS Big Endian boot files by iso_image_add_mips_boot_file() of libisofs. This will overwrite the first 512 bytes of the submitted data. 2= DEC Boot Block for MIPS Little Endian The first boot file submitted by iso_image_add_mips_boot_file() will be activated. This will overwrite the first 512 bytes of the submitted data. @since 0.6.6 3= SUN Disk Label for SUN SPARC Submit up to 7 SPARC boot images by isoburn_igopt_set_partition_img() for partition numbers 2 to 8. This will overwrite the first 512 bytes of the submitted data. @since 1.3.8 4= HP-PA PALO boot sector header version 4 Submit all five parameters of iso_image_set_hppa_palo() as non-NULL texts. 5= HP-PA PALO boot sector header version 5 Submit all five parameters of iso_image_set_hppa_palo() as non-NULL texts. bit8-9= Only with System area type 0 = MBR @since 1.0.4 Cylinder alignment mode eventually pads the image to make it end at a cylinder boundary. 0 = auto (align if bit1) 1 = always align to cylinder boundary 2 = never align to cylinder boundary bit10-13= System area sub type @since 1.2.4 With type 0 = MBR: Gets overridden by bit0 and bit1. 0 = no particular sub type 1 = CHRP: A single MBR partition of type 0x96 covers the ISO image. Not compatible with any other feature which needs to have own MBR partition entries. bit14= Only with System area type 0 = MBR GRUB2 boot provisions: @since 1.3.0 Patch system area at byte 0x1b0 to 0x1b7 with (512-block address + 4) of the first boot image file. Little-endian 8-byte. Should be combined with options bit0. Will not be in effect if options bit1 is set. bit15= Only with System area type MBR but not with CHRP @since 1.4.4 Enforce MBR "bootable/active" flag. In worst case by dummy partition of type 0x00 which occupies block 0. bit16= "Legacy BIOS bootable" in GPT @since 1.5.6 If this bit is set and a GPT partition for the ISO 9660 filesystem gets written, then set the GPT partition flags bit 2 "Legacy BIOS bootable". bit17= ISO not read-only @since 1.5.6 Do not set GPT partition flag bit 60 "read-only" for the ISO 9660 filesystem partition, if such a partition gets written. @return 1 success, 0 no data to get, <0 failure */ int isoburn_igopt_set_system_area(struct isoburn_imgen_opts *o, char data[32768], int options); int isoburn_igopt_get_system_area(struct isoburn_imgen_opts *o, char data[32768], int *options); /** Control production of a second set of volume descriptors (superblock) and directory trees, together with a partition table in the MBR where the first partition has non-zero start address and the others are zeroed. The first partition stretches to the end of the whole ISO image. The additional volume descriptor set and trees can be used to mount the ISO image at the start of the first partition, while it is still possible to mount it via the normal first volume descriptor set and tree at the start of the image or storage device. This makes few sense on optical media. But on USB sticks it creates a conventional partition table which makes it mountable on e.g. Linux via /dev/sdb and /dev/sdb1 alike. @since 0.6.2 @param opts The option set to be manipulated. @param block_offset_2k The offset of the partition start relative to device start. This is counted in 2 kB blocks. The partition table will show the according number of 512 byte sectors. Default is 0 which causes no second set and trees. If it is not 0 then it must not be smaller than 16. @param secs_512_per_head Number of 512 byte sectors per head. 1 to 63. 0=automatic. @param heads_per_cyl Number of heads per cylinder. 1 to 255. 0=automatic. @return 1 success, <=0 failure */ int isoburn_igopt_set_part_offset(struct isoburn_imgen_opts *opts, uint32_t block_offset_2k, int secs_512_per_head, int heads_per_cyl); int isoburn_igopt_get_part_offset(struct isoburn_imgen_opts *opts, uint32_t *block_offset_2k, int *secs_512_per_head, int *heads_per_cyl); /** Explicitly set the four timestamps of the emerging ISO image. Default with all parameters is 0. @since 0.5.4 @param opts The option set to work on @param creation_time ECMA-119 Volume Creation Date and Time When "the information in the volume was created." A value of 0 means that the timepoint of write start is to be used. @param modification_time ECMA-119 Volume Modification Date and Time When "the informationin the volume was last modified." A value of 0 means that the timepoint of write start is to be used. @param expiration_time ECMA-119 Volume Expiration Date and Time When "the information in the volume may be regarded as obsolete." A value of 0 means that the information never shall expire. @param effective_time ECMA-119 Volume Effective Date and Time When "the information in the volume may be used." A value of 0 means that not such retention is intended. @param uuid If this text is not empty, then it overrides vol_modification_time by copying the first 16 decimal digits from uuid, eventually padding up with decimal '1', and writing a NUL-byte as timezone GMT. It should express a reasonable time in form YYYYMMDDhhmmsscc E.g.: 2010040711405800 = 7 Apr 2010 11:40:58 (+0 centiseconds) @return 1 success, <=0 failure */ int isoburn_igopt_set_pvd_times(struct isoburn_imgen_opts *opts, time_t creation_time, time_t modification_time, time_t expiration_time, time_t effective_time, char *uuid); int isoburn_igopt_get_pvd_times(struct isoburn_imgen_opts *opts, time_t *creation_time, time_t *modification_time, time_t *expiration_time, time_t *effective_time, char uuid[17]); /** Associate a libjte environment object to the upcoming write run. libjte implements Jigdo Template Extraction as of Steve McIntyre and Richard Atterer. A non-NULL libjte_handle will cause failure to write if libjte was not enabled in libisofs at compile time. @since 0.6.4 @param opts The option set to work on @param libjte_handle Pointer to a struct libjte_env e.g. created by libjte_new(). It must stay existent from the start of image writing by isoburn_prepare_*() until the write thread has ended. E.g. until libburn indicates the end of its write run. @return 1 success, <=0 failure */ int isoburn_igopt_attach_jte(struct isoburn_imgen_opts *opts, void *libjte_handle); /** Remove eventual association to a libjte environment handle. @since 0.6.4 @param opts The option set to work on @param libjte_handle If not submitted as NULL, this will return the previously set libjte handle. @return 1 success, <=0 failure */ int isoburn_igopt_detach_jte(struct isoburn_imgen_opts *opts, void **libjte_handle); /** Set or get the number of trailing zero byte blocks to be written by libisofs. The image size counter of the emerging ISO image will include them. Eventual checksums will take them into respect. They will be written immediately before the eventual image checksum area which is at the very end of the image. For a motivation see iso_write_opts_set_tail_blocks() in libisofs.h . @since 0.6.4 @param opts The option set to work on @param num_blocks Number of extra 2 kB blocks to be written by libisofs. @return 1 success, <=0 failure */ int isoburn_igopt_set_tail_blocks(struct isoburn_imgen_opts *opts, uint32_t num_blocks); int isoburn_igopt_get_tail_blocks(struct isoburn_imgen_opts *opts, uint32_t *num_blocks); /** Copy a data file from the local filesystem into the emerging ISO image. Mark it by an MBR partition entry as PreP partition and also cause protective MBR partition entries before and after this partition. See libisofs.h iso_write_opts_set_prep_img(). @since 1.2.4 @param opts The option set to be manipulated. @param path File address in the local file system. @param flag With isoburn_igopt_set_prep_partition(): Control bits as of iso_write_opts_set_efi_bootp() bit0= The path contains instructions for the interval libisofs reader. See libisofs.h. @since 1.4.0 With isoburn_igopt_get_prep_partition(): bit0= add the current flag setting & 0x3fffffff to return value 1. @return 1 success, <=0 failure */ int isoburn_igopt_set_prep_partition(struct isoburn_imgen_opts *opts, char *path, int flag); int isoburn_igopt_get_prep_partition(struct isoburn_imgen_opts *opts, char **path, int flag); /** Copy a data file from the local filesystem into the emerging ISO image and mark it by a GPT entry as EFI system partition. @since 1.2.4 @param opts The option set to be manipulated. @param path File address in the local file system. Instead of a disk path, the word --efi-boot-image may be given. It exposes in GPT the content of the first El Torito EFI boot image as EFI system partition. @param flag With isoburn_igopt_get_efi_bootp(): Control bits as of iso_write_opts_set_efi_bootp() bit0= The path contains instructions for the interval libisofs reader. See libisofs.h. @since 1.4.0 With isoburn_igopt_set_efi_bootp(): bit0= add the current flag setting & 0x3fffffff to return value 1. @return 1 success, <=0 failure */ int isoburn_igopt_set_efi_bootp(struct isoburn_imgen_opts *opts, char *path, int flag); int isoburn_igopt_get_efi_bootp(struct isoburn_imgen_opts *opts, char **path, int flag); /** Cause an arbitrary data file to be appended to the ISO image and to be described by a partition table entry in an MBR or SUN Disk Label at the start of the ISO image. The partition entry will bear the size of the image file rounded up to the next multiple of 2048 bytes. MBR or SUN Disk Label are selected by isoburn_igopt_set_system_area() system area type: 0 selects MBR partition table. 3 selects a SUN partition table with 320 kB start alignment. @since 0.6.4 @param opts The option set to be manipulated. @param partition_number Depicts the partition table entry which shall describe the appended image. Range with MBR: 1 to 4. 1 will cause the whole ISO image to be unclaimable space before partition 1. @since 0.6.6 Range with SUN Disk Label: 2 to 8. @param image_path File address in the local file system. With SUN Disk Label: an empty name causes the partition to become a copy of the next lower partition. @param partition_type The MBR partition type. E.g. FAT12 = 0x01 , FAT16 = 0x06, Linux Native Partition = 0x83. See fdisk command L. This parameter is ignored with SUN Disk Label. @return <=0 = error, 1 = success */ int isoburn_igopt_set_partition_img(struct isoburn_imgen_opts *opts, int partition_number, uint8_t partition_type, char *image_path); /** Inquire the current settings made by isoburn_igopt_set_partition_img(). @since 0.6.4 @param opts The option set to be inquired. @param num_entries Number of array elements in partition_types[] and image_paths[]. @param partition_types The partition type associated with the partition. Valid only if image_paths[] of the same index is not NULL. @param image_paths Its elements get filled with either NULL or a pointer to a string with a file address or an empty text. @return <0 = error 0 = no partition image set >0 highest used partition number */ int isoburn_igopt_get_partition_img(struct isoburn_imgen_opts *opts, int num_entries, uint8_t partition_types[], char *image_paths[]); /** Set flag bits for a partition defined by isoburn_igopt_set_partition_img(). The bits will be forwarded to libisofs iso_write_opts_set_partition_img(). @since 1.4.0 @param opts The option set to be manipulated. @param partition_number Depicts the partition table entry to which shall the flags bits shall apply. @param flag Control bits as of iso_write_opts_set_partition_img() bit0= The path contains instructions for the interval libisofs reader. See libisofs.h. @since 1.4.0 @return <=0 = error, 1 = success */ int isoburn_igopt_set_part_flag(struct isoburn_imgen_opts *opts, int partition_number, int flag); /** Inquire the current settings made by isoburn_igopt_set_part_flags(). @since 1.4.0 @param opts The option set to be inquired. @param num_entries Number of array elements in part_flags[]. @param part_flags The array elements 0 to num_entries - 1 will get filled by the flag bits of the images of the corresponding partition. @return <0 = error 0 = no partition image set >0 highest used partition number */ int isoburn_igopt_get_part_flags(struct isoburn_imgen_opts *opts, int num_entries, int part_flags[]); /** Control whether partitions created by iso_write_opts_set_partition_img() are to be represented in MBR or as GPT partitions. @since 1.4.0 @param opts The option set to be manipulated. @param gpt 0= represent as MBR partition; as GPT only if other GPT partitions are present 1= represent as GPT partition and cause protective MBR with a single partition which covers the whole output data. This may fail if other settings demand MBR partitions. Do not use other values for now. @return <=0 = error, 1 = success */ int isoburn_igopt_set_appended_as_gpt(struct isoburn_imgen_opts *opts, int gpt); /** Inquire the current setting made by isoburn_igopt_set_appended_as_gpt(). @since 1.4.0 @param opts The option set to be inquired. @param gpt Returns the current value. @return <=0 = error, 1 = success */ int isoburn_igopt_get_appended_as_gpt(struct isoburn_imgen_opts *opts, int *gpt); /** Set the GPT Type GUID for a partition defined by isoburn_igopt_set_partition_img(). @since 1.5.2 @param opts The option set to be manipulated. @param partition_number Depicts the partition table entry which shall get the Type GUID. @param guid 16 bytes of user supplied GUID. Readily byte-swapped from the text form as prescribed by UEFI specs: 4 byte, 2 byte, 2 byte as little-endian. 2 byte, 6 byte as big-endian. @param valid Set to 1 to make this Type GUID valid. Set to 0 in order to invalidate a previously made setting. In this case MBR type 0xEF will become the EFI Type GUID. All others will become the Basic Data Partition Type GUID. @return <=0 = error, 1 = success */ int isoburn_igopt_set_part_type_guid(struct isoburn_imgen_opts *opts, int partition_number, uint8_t guid[16], int valid); /** Inquire the current settings made by isoburn_igopt_set_part_type_guid(). @since 1.5.2 @param opts The option set to be inquired. @param num_entries Number of array elements in part_flags[]. @param guids The array elements 0 to num_entries - 1 will get filled by the 16 flag bits of the images of the corresponding partition. @param valids The array elements 0 to num_entries - 1 will get filled by 1 or 0 to indicate whether the corresponding type_guids element is valid. @return <0 = error 0 = no partition image set >0 highest used partition number */ int isoburn_igopt_get_part_type_guid(struct isoburn_imgen_opts *opts, int num_entries, uint8_t guids[][16], int valids[]); /** Control whether partitions created by iso_write_opts_set_partition_img() are to be represented in Apple Partition Map. @since 1.4.4 @param opts The option set to be manipulated. @param apm 0= do not represent appended partitions in APM 1= represent in APM, even if not iso_write_opts_set_part_like_isohybrid() enables it and no other APM partitions emerge. Do not use other values for now. @return <=0 = error, 1 = success */ int isoburn_igopt_set_appended_as_apm(struct isoburn_imgen_opts *opts, int apm); /** Inquire the current setting made by isoburn_igopt_set_appended_as_apm(). @since 1.4.4 @param opts The option set to be inquired. @param apm Returns the current value. @return <=0 = error, 1 = success */ int isoburn_igopt_get_appended_as_apm(struct isoburn_imgen_opts *opts, int *apm); /** Control whether bits 2 to 8 of el_torito_set_isolinux_options() shall apply even if not isohybrid MBR patching is enabled (bit1 of parameter options of isoburn_igopt_set_system_area()). For details see iso_write_opts_set_part_like_isohybrid() in libisofs.h. @since 1.4.4 @param opts The option set to be manipulated. @param alike 0= Apply isohybrid behavior only with ISOLINUX isohybrid. Do not mention appended partitions in APM unless isoburn_igopt_set_appended_as_apm() is enabled. 1= Apply isohybrid behavior even without ISOLINUX isohybrid. @return <=0 = error, 1 = success */ int isoburn_igopt_set_part_like_isohybrid(struct isoburn_imgen_opts *opts, int alike); /** Inquire the current setting of isoburn_igopt_set_part_like_isohybrid(). @since 1.4.4 @param opts The option set to be inquired. @param alike Returns the current value. @return <=0 = error, 1 = success */ int isoburn_igopt_get_part_like_isohybrid(struct isoburn_imgen_opts *opts, int *alike); /** Set the partition type of the MBR partition which represents the ISO filesystem or at least protects it. This is without effect if no such partition emerges by other settings or if the partition type is prescribed mandatorily like 0xee for GPT protective MBR or 0x96 for CHRP. @since 1.4.8 @param opts The option set to be manipulated. @param part_type 0x00 to 0xff as desired partition type. Any other value (e.g. -1) enables the default types of the various occasions. */ int isoburn_igopt_set_iso_mbr_part_type(struct isoburn_imgen_opts *opts, int part_type); /** Inquire the current setting of isoburn_igopt_set_iso_mbr_part_type(). @since 1.4.8 @param opts The option set to be inquired. @param part_type Returns the current value: -1, 0x00 to 0xff. @return <=0 = error, 1 = success */ int isoburn_igopt_get_iso_mbr_part_type(struct isoburn_imgen_opts *opts, int *part_type); /** Set the GPT Type GUID for the partition which represents the ISO 9660 filesystem, if such a partition emerges in GPT. @since 1.5.2 @param opts The option set to be manipulated. @param guid 16 bytes of user supplied GUID. Readily byte-swapped from the text form as prescribed by UEFI specs: 4 byte, 2 byte, 2 byte as little-endian. 2 byte, 6 byte as big-endian. @param valid Set to 1 to make this Type GUID valid. Set to 0 in order to invalidate a previously made setting. In this case the setting of isoburn_igopt_set_iso_mbr_part_type() or its default gets into effect. @return <=0 = error, 1 = success */ int isoburn_igopt_set_iso_type_guid(struct isoburn_imgen_opts *opts, uint8_t guid[16], int valid); /** Inquire the current setting of isoburn_igopt_set_iso_type_guid(). @since 1.5.2 @param opts The option set to be inquired. @param guid Gets filled with the 16 bytes of GUID. @return <= error, 0= guid is invalid, 1 = guid is valid */ int isoburn_igopt_get_iso_type_guid(struct isoburn_imgen_opts *opts, uint8_t guid[16]); /** Control whether the emerging GPT gets a pseudo-randomly generated disk GUID or whether it gets a user supplied GUID. The partition GUIDs will be generated in a reproducible way by exoring a little-endian 32 bit counter with the disk GUID beginning at byte offset 9. @since 1.4.6 @param opts The option set to be manipulated. @param guid 16 bytes of user supplied GUID. Readily byte-swapped from the text form as prescribed by UEFI specs: 4 byte, 2 byte, 2 byte as little-endian. 2 byte, 6 byte as big-endian. The upper 4 bit of guid[6] and guid[7] should bear the value 4 to express the version 4 in both endiannesses. Bit 7 of byte[8] should be set to 1 and bit 6 be set to 0, in order to express the RFC 4122 variant of GUID, where version 4 means "random". @param mode 0 = ignore parameter guid and produce the GPT disk GUID by a pseudo-random algorithm. This is the default setting. 1 = use parameter guid as GPT disk GUID 2 = ignore parameter guid and derive the GPT disk GUID from parameter uuid of isoburn_igopt_set_pvd_times(). The 16 bytes of uuid get copied and bytes 6, 7, 8 get their upper bits changed to comply to RFC 4122. If no such uuid is given when ISO production starts, then mode 2 defaults to mode 0. */ int isoburn_igopt_set_gpt_guid(struct isoburn_imgen_opts *opts, uint8_t guid[16], int mode); /** Inquire the current setting of isoburn_igopt_set_gpt_guid(). @since 1.4.6 @param opts The option set to be inquired. @param guid Returns the current guid if current mode is 1. @param mode Returns the current value. @return <=0 = error, 1 = success */ int isoburn_igopt_get_gpt_guid(struct isoburn_imgen_opts *opts, uint8_t guid[16], int *mode); /** Set the maximum number of SUSP CE entries and thus continuation areas. Each continuation area can hold at most 2048 bytes of SUSP data (Rock Ridge or AAIP). The first area can be smaller. There might be some waste at the end of each area. When the maximum number is exceeded during ISO filesystem production then possibly xattr and ACL get removed or error ISO_TOO_MANY_CE gets reported and filesystem production is prevented. Files with 32 or more CE entries do not show up in mounted filesystems on Linux. So the default setting is 31 with drop mode 2. If a higher limit is chosen and 31 gets surpassed, then a warning message gets reported. @since 1.5.6 @param opts The option set to be inquired. @param num The maximum number of CE entries per file. Not more than 100000 may be set here. 0 gets silently mapped to 1,because the root directory needs one CE. @param flag bit0-bit3 = Drop mode: What to do with AAIP data on too many CE: 0 = throw ISO_TOO_MANY_CE, without dropping anything 1 = permanently drop non-isofs fattr from IsoNode and retry filesystem production 2 = drop ACL if dropping non-isofs fattr does not suffice @return <=0 = error, 1 = success */ int isoburn_igopt_set_max_ce_entries(struct isoburn_imgen_opts *opts, uint32_t num, int flag); /** Inquire the current setting of isoburn_igopt_(). @since 1.5.6 @param opts The option set to be inquired. @param num Returns the current setting @param max_ce_flag Returns the current flag setting @return <=0 = error, 1 = success */ int isoburn_igopt_get_max_ce_entries(struct isoburn_imgen_opts *opts, uint32_t *num, int *max_ce_flag); /** Set a name for the system area. This setting is ignored unless system area type 3 "SUN Disk Label" is in effect by iso_write_opts_set_system_area(). In this case it will replace the default text at the start of the image: "CD-ROM Disc with Sun sparc boot created by libisofs" @since 0.6.6 @param opts The option set to be manipulated. @param label A text of up to 128 characters. @return <=0 = error, 1 = success */ int isoburn_igopt_set_disc_label(struct isoburn_imgen_opts *opts, char *label); /** Inquire the current setting made by isoburn_igopt_set_disc_label(). @since 0.6.6 @param opts The option set to be inquired. @param label Returns a pointer to the currently set label string. Do not alter this string. Use only as long as the opts object exists. @return <=0 = error, 1 = success */ int isoburn_igopt_get_disc_label(struct isoburn_imgen_opts *opts, char **label); /** Set a serial number for the HFS+ extension of the emerging ISO image. @since 1.2.4 @param opts The option set to be manipulated. @param serial_number 8 bytes which should be unique to the image. If all bytes are 0, then the serial number will be generated as random number by libisofs. This is the default setting. @return <=0 = error, 1 = success */ int isoburn_igopt_set_hfsp_serial_number(struct isoburn_imgen_opts *opts, uint8_t serial_number[8]); /** Inquire the current setting made by isoburn_igopt_set_disc_label() @since 1.2.4 @param opts The option set to be inquired. @param serial_number Will get filled with the current setting. @return <=0 = error, 1 = success */ int isoburn_igopt_get_hfsp_serial_number(struct isoburn_imgen_opts *opts, uint8_t serial_number[8]); /** Set the allocation block size for HFS+ production and the block size for layout and address unit of Apple Partition map. @since 1.2.4 @param opts The option set to be manipulated. @param hfsp_block_size -1 means that this setting shall be left unchanged 0 allows the automatic default setting 512 and 2048 enforce a size. @param apm_block_size -1 means that this setting shall be left unchanged 0 allows the automatic default setting 512 and 2048 enforce a size. Size 512 cannot be combined with GPT production. Size 2048 cannot be mounted -t hfsplus by Linux kernels at least up to 2.6.32. @return <=0 = error, 1 = success */ int isoburn_igopt_set_hfsp_block_size(struct isoburn_imgen_opts *opts, int hfsp_block_size, int apm_block_size); /** Inquire the current setting made by isoburn_igopt_set_hfsp_block_size @since 1.2.4 @param opts The option set to be inquired. @param hfsp_block_size Will be set to a value as described above. Except -1. @param apm_block_size Will be set to a value as described above. Except -1. @return <=0 = error, 1 = success */ int isoburn_igopt_get_hfsp_block_size(struct isoburn_imgen_opts *opts, int *hfsp_block_size, int *apm_block_size); /** Set or inquire the write type for the next write run on optical media. @since 1.2.4 @param opts The option set to be manipulated or inquired. @param do_tao The value to be set or the variable where to return the current setting: 0 = Let libburn choose according to other write parameters. This is advisable unless there are particular reasons not to use one of the two write types. Be aware that 1 and -1 can lead to failure if the write type is not appropriate for the given media situation. 1 = Use BURN_WRITE_TAO which does TAO on CD, Incremental on DVD-R, no track reservation on DVD+R and BD-R -1 = Use BURN_WRITE_SAO which does SAO on CD, DAO on DVD-R, track reservation on DVD+R and BD-R @return <=0 = error, 1 = success */ int isoburn_igopt_set_write_type(struct isoburn_imgen_opts *opts, int do_tao); int isoburn_igopt_get_write_type(struct isoburn_imgen_opts *opts, int *do_tao); /** Set or inquire whether a final fsync(2) is performed when updating the multi-session information of libburn stdio pseudo-drives by isoburn_activate_session(). Note: fsync(2) calls during and at the end of isoburn_disc_write() are controlled by libburn call burn_write_opts_set_stdio_fsync(). @since 1.2.4 @param opts The option set to be manipulated or inquired. @param do_sync 1= call fsync(2) with stdio drives in isoburn_activate_session() 0= do not @return <=0 = error, 1 = success */ int isoburn_igopt_set_stdio_endsync(struct isoburn_imgen_opts *opts, int do_sync); int isoburn_igopt_get_stdio_endsync(struct isoburn_imgen_opts *opts, int *do_sync); /* ----------------------------------------------------------------------- */ /* End of Options for image generation */ /* ----------------------------------------------------------------------- */ /** Frontend of libisofs call iso_conv_name_chars() controlled by struct isoburn_imgen_opts rather than IsoWriteOpts. See libisofs.h for a more detailed description. @since 1.3.6 @param opts Defines options like output charset, UCS-2 versus UTF-16 for Joliet, and naming restrictions. @param name The input text which shall be converted. @param name_len The number of bytes in input text. @param result Will return the conversion result in case of success. Terminated by a trailing zero byte. Use free() to dispose it when no longer needed. @param result_len Will return the number of bytes in result (excluding trailing zero) @param flag Bitfield for control purposes. bit0-bit7= Name space 0= generic (out_charset is valid, no reserved characters, no length limits) 1= Rock Ridge (out_charset is valid) 2= Joliet (out_charset gets overridden by UCS-2 or UTF-16) 3= ECMA-119 (out_charset gets overridden by the dull ISO 9660 subset of ASCII) 4= HFS+ (out_charset gets overridden by UTF-16BE) bit8= Treat input text as directory name (matters for Joliet and ECMA-119) bit9= Do not issue error messages bit15= Reverse operation (best to be done only with results of previous conversions) @return 1 means success, <=0 means error */ int isoburn_conv_name_chars(struct isoburn_imgen_opts *opts, char *name, size_t name_len, char **result, size_t *result_len, int flag); /** Get the image attached to a drive, if any. @since 0.1.0 @param d The drive to inquire @return A reference to attached image, or NULL if the drive has no image attached. This reference needs to be released via iso_image_unref() when it is not longer needed. */ IsoImage *isoburn_get_attached_image(struct burn_drive *d); /** Get the start address of the image that is attached to the drive, if any. @since 1.2.2 @param d The drive to inquire @return The logical block address where the System Area of the image starts. <0 means that the address is invalid. */ int isoburn_get_attached_start_lba(struct burn_drive *d); /** Like isoburn_get_attached_start_lba(), but with off_t return value. @since 1.5.8 */ off_t isoburn_get_attached_start_lba_v2(struct burn_drive *d); /** Load the ISO filesystem directory tree from the medium in the given drive. This will give libisoburn the base on which it can let libisofs perform image growing or image modification. The loaded volset gets attached to the drive object and handed out to the application. Not a wrapper, but peculiar to libisoburn. @since 0.1.0 @param d The drive which holds an existing ISO filesystem or blank media. d is allowed to be NULL which produces an empty ISO image. In this case one has to call before writing isoburn_attach_volset() with the volset from this call and with the intended output drive. @param read_opts The read options which can be chosen by the application @param image the image read, if the disc is blank it will have no files. This reference needs to be released via iso_image_unref() when it is not longer needed. The drive, if not NULL, will hold an own reference which it will release when it gets a new volset or when it gets released via isoburn_drive_release(). You can pass NULL if you already have a reference or you plan to obtain it later with isoburn_get_attached_image(). Of course, if you haven't specified a valid drive (i.e., if d == NULL), this parameter can't be NULL. @return <=0 error , 1 = success */ int isoburn_read_image(struct burn_drive *d, struct isoburn_read_opts *read_opts, IsoImage **image); /** Set a callback function for producing pacifier messages during the lengthy process of image reading. The callback function and the application handle are stored until they are needed for the underlying call to libisofs. Other than with libisofs the handle is managed entirely by the application. An idle .free() function is exposed to libisofs. The handle has to stay valid until isoburn_read_image() is done. It has to be detached by isoburn_set_read_pacifier(drive, NULL, NULL); before it may be removed from memory. @since 0.1.0 @param drive The drive which will be used with isoburn_read_image() It has to be acquired by an isoburn_* wrapper call. @param read_pacifier The callback function @param app_handle The app handle which the callback function can obtain via iso_image_get_attached_data() from its IsoImage* @return 1 success, <=0 failure */ int isoburn_set_read_pacifier(struct burn_drive *drive, int (*read_pacifier)(IsoImage*, IsoFileSource*), void *app_handle); /** Inquire the partition offset of the loaded image. The first 512 bytes of the image get examined whether they bear an MBR signature and a first partition table entry which matches the size of the image. In this case the start address is recorded as partition offset and internal buffers get adjusted. See also isoburn_igopt_set_part_offset(). @since 0.6.2 @param drive The drive with the loaded image @param block_offset_2k returns the recognized partition offset @return <0 = error 0 = no partition offset recognized 1 = acceptable non-zero offset, buffers are adjusted 2 = offset is credible but not acceptable for buffer size */ int isoburn_get_img_partition_offset(struct burn_drive *drive, uint32_t *block_offset_2k); /** Assess features of the importable directory trees and an estimation of the write options which would cause the recognized features. @since 1.5.6 @param d The drive with the ISO filesystem @param read_opts The read options which would be used by isoburn_read_image() @param features Returned information which may be inquired by iso_read_image_features_*() or by iso_read_image_feature_named(). Dispose by iso_read_image_features_destroy() when no longer needed. @param imgen_opts Returned set of write options which were derived from the features. Dispose by isoburn_igopt_destroy when no longer needed. @param flag Bitfield for control purposes, submit 0 for now. @return 1= success, <= 0 = error */ int isoburn_assess_written_features(struct burn_drive *d, struct isoburn_read_opts *read_opts, IsoReadImageFeatures **features, struct isoburn_imgen_opts **imgen_opts, int flag); /** Set the IsoImage to be used with a drive. This eventually releases the reference to the old IsoImage attached to the drive. Caution: Use with care. It hardly makes sense to replace an image that reflects a valid ISO image on the medium. This call is rather intended for writing a newly created and populated image to blank media. The use case in xorriso is to let an image survive the change or demise of the outdev target drive. @since 0.1.0 @param d The drive which shall be write target of the volset. @param image The image that represents the image to be written. This image pointer MUST already be a valid reference suitable for iso_image_unref(). It may have been obtained by appropriate libisofs calls or by isoburn_read_image() with d==NULL. @return <=0 error , 1 = success */ int isoburn_attach_image(struct burn_drive *d, IsoImage *image); /** Set the start address of the image that is attached to the drive, if any. @since 1.2.2 @param d The drive to inquire @param lba The logical block address where the System Area of the image starts. <0 means that the address is invalid. @param flag Bitfield, submit 0 for now. @return <=0 error (e.g. because no image is attached), 1 = success */ int isoburn_attach_start_lba(struct burn_drive *d, int lba, int flag); /** Like isoburn_attach_start_lba(), but with off_t result value. @since 1.5.8 */ int isoburn_attach_start_lba_v2(struct burn_drive *d, off_t lba, int flag); /** Return the best possible estimation of the currently available capacity of the medium. This might depend on particular write option settings and on drive state. An eventual start address for emulated multi-session will be subtracted from the capacity estimation given by burn_disc_available_space(). Negative results get defaulted to 0. Wrapper for: burn_disc_available_space() @since 0.1.0 @param d The drive to query. @param o If not NULL: write parameters to be set on drive before query @return number of most probably available free bytes */ off_t isoburn_disc_available_space(struct burn_drive *d, struct burn_write_opts *o); /** Obtain the start block number of the most recent session on the medium. In case of random access media this will normally be 0. Successful return is not a guarantee that there is a ISO-9660 image at all. The call will fail, nevertheless,if isoburn_disc_get_status() returns not BURN_DISC_APPENDABLE or BURN_DISC_FULL. Note: The result of this call may be fabricated by a previous call of isoburn_set_msc1() which can override the rule to load the most recent session. Wrapper for: burn_disc_get_msc1() @since 0.1.0 @param d The drive to inquire @param start_lba Contains on success the start address in 2048 byte blocks @return <=0 error , 1 = success */ int isoburn_disc_get_msc1(struct burn_drive *d, int *start_lba); /** Like isoburn_disc_get_msc1(), but with off_t result value. @since 1.5.8 */ int isoburn_disc_get_msc1_v2(struct burn_drive *d, off_t *start_lba); /** Use this with trackno==0 to obtain the predicted start block number of the new session. The interesting number is returned in parameter nwa. Wrapper for: burn_disc_track_lba_nwa() @since 0.1.0 @param d The drive to inquire @param o If not NULL: write parameters to be set on drive before query @param trackno Submit 0. @param lba return value: start lba @param nwa return value: Next Writeable Address @return 1=nwa is valid , 0=nwa is not valid , -1=error */ int isoburn_disc_track_lba_nwa(struct burn_drive *d, struct burn_write_opts *o, int trackno, int *lba, int *nwa); /** Like isoburn_disc_track_lba_nwa(), but with off_t result value. @since 1.5.8 */ int isoburn_disc_track_lba_nwa_v2(struct burn_drive *d, struct burn_write_opts *o, int trackno, off_t *lba, off_t *nwa); /** Obtain the size which was attributed to an emulated appendable on actually overwritable media. This value is supposed to be <= 2048 * nwa as of isoburn_disc_track_lba_nwa(). @since 0.1.0 @param d The drive holding the medium. @param start_byte The reply value counted in bytes, not in sectors. @param flag Unused yet. Submit 0. @return 1=stat_byte is valid, 0=not an emulated appendable, -1=error */ int isoburn_get_min_start_byte(struct burn_drive *d, off_t *start_byte, int flag); /** Start production of an ISO 9660 image using the method of Growing: Create a disc object for writing the new session from the created or loaded iso_volset which has been manipulated via libisofs, to the same medium from where the image was eventually loaded. This call starts a libisofs thread which begins to produce the image. It has to be revoked by isoburn_cancel_prepared_write() if for some reason this image data stream shall not be consumed. The returned struct burn_disc is ready for use by a subsequent call to isoburn_disc_write(). After this asynchronous writing has ended and the drive is BURN_DRIVE_IDLE again, the burn_disc object has to be disposed by burn_disc_free(). @since 0.1.0 @param drive The combined source and target drive, grabbed with isoburn_drive_scan_and_grab(). . @param disc Returns the newly created burn_disc object. @param opts Image generation options, see isoburn_igopt_*() @return <=0 error , 1 = success */ int isoburn_prepare_disc(struct burn_drive *drive, struct burn_disc **disc, struct isoburn_imgen_opts *opts); /** Start production of an ISO 9660 image using the method of Modifying: Create a disc object for producing a new image from a previous image plus the changes made by user. The generated burn_disc is suitable to be written to a grabbed drive with blank writeable medium. But you must not use the same drive for input and output, because data will be read from the source drive while at the same time the target drive is already writing. This call starts a libisofs thread which begins to produce the image. It has to be revoked by isoburn_cancel_prepared_write() if for some reason this image data stream shall not be consumed. The resulting burn_disc object has to be disposed when all its writing is done and the drive is BURN_DRIVE_IDLE again after asynchronous burn_disc_write(). @since 0.1.0 @param in_drive The input drive, grabbed with isoburn_drive_aquire() or one of its alternatives. @param disc Returns the newly created burn_disc object. @param opts Options for image generation and data transport to the medium. @param out_drive The output drive, from isoburn_drive_aquire() et.al.. @return <=0 error , 1 = success */ int isoburn_prepare_new_image(struct burn_drive *in_drive, struct burn_disc **disc, struct isoburn_imgen_opts *opts, struct burn_drive *out_drive); /** Start production of an ISO 9660 image using the method of Blind Growing: Create a disc object for writing an add-on session from the created or loaded IsoImage which has been manipulated via libisofs, to a different drive than the one from where it was loaded. Usually output will be stdio:/dev/fd/1 (i.e. stdout) being piped into some burn program like with this classic gesture: mkisofs -M $dev -C $msc1,$nwa | cdrecord -waiti dev=$dev Parameter translation into libisoburn: $dev is the address by which parameter in_drive of this call was acquired $msc1 was set by isoburn_set_msc1() before image reading or was detected from the in_drive medium $nwa is a parameter of this call or can be used as detected from the in_drive medium This call starts a libisofs thread which begins to produce the image. It has to be revoked by isoburn_cancel_prepared_write() if for some reason this image data stream shall not be consumed. This call waits for libisofs output to become available and then detaches the input drive object from the data source object by which libisofs was reading from the input drive. So, as far as libisofs is concerned, that drive may be released immediately after this call in order to allow the consumer to access the drive for writing. The consumer should wait for input to become available and only then open its burn drive. With cdrecord this is caused by option -waiti. The resulting burn_disc object has to be disposed when all its writing is done and the drive is BURN_DRIVE_IDLE again after asynchronous burn_disc_write(). @since 0.2.2 @param in_drive The input drive,grabbed with isoburn_drive_scan_and_grab(). @param disc Returns the newly created burn_disc object. @param opts Options for image generation and data transport to media. @param out_drive The output drive, from isoburn_drive_aquire() et.al.. typically stdio:/dev/fd/1 . @param nwa The address (2048 byte block count) where the add-on session will be finally stored on a mountable medium or in a mountable file. If nwa is -1 then the address is used as determined from the in_drive medium. @return <=0 error , 1 = success */ int isoburn_prepare_blind_grow(struct burn_drive *in_drive, struct burn_disc **disc, struct isoburn_imgen_opts *opts, struct burn_drive *out_drive, int nwa); /** Like isoburn_prepare_blind_grow(), but with off_t nwa value. @since 1.5.8 */ int isoburn_prepare_blind_grow_v2(struct burn_drive *in_drive, struct burn_disc **disc, struct isoburn_imgen_opts *opts, struct burn_drive *out_drive, off_t nwa); /** Revoke isoburn_prepare_*() instead of running isoburn_disc_write(). libisofs reserves resources and maybe already starts generating the image stream when one of above three calls is performed. It is mandatory to either run isoburn_disc_write() or to revoke the preparations by the call described here. If this call returns 0 or 1 then the write thread of libisofs has ended. @since 0.1.0 @param input_drive The drive or in_drive which was used with the preparation call. @param output_drive The out_drive used with isoburn_prepare_new_image(), NULL if none. @param flag Bitfield, submit 0 for now. bit0= -reserved for internal use- @return <0 error, 0= no pending preparations detectable, 1 = canceled */ int isoburn_cancel_prepared_write(struct burn_drive *input_drive, struct burn_drive *output_drive, int flag); /** Override the truncation setting that was made with flag bit2 during the call of isoburn_drive_aquire. This applies only to stdio pseudo drives. @since 0.1.6 @param drive The drive which was acquired and shall be used for writing. @param flag Bitfield controlling the setting: bit0= truncate (else do not truncate) bit1= do not warn if call is inappropriate to drive bit2= only set if truncation is currently enabled do not warn if call is inappropriate to drive @return 1 success, 0 inappropriate drive, <0 severe error */ int isoburn_set_truncate(struct burn_drive *drive, int flag); /** Start writing of the new session. This call is asynchronous. I.e. it returns quite soon and the progress has to be watched by a loop with call burn_drive_get_status() until BURN_DRIVE_IDLE is returned. Wrapper for: burn_disc_write() @since 0.1.0 @param o Options which control the burn process. See burnwrite_opts_*() in libburn.h. @param disc Disc object created either by isoburn_prepare_disc() or by isoburn_prepare_new_image(). */ void isoburn_disc_write(struct burn_write_opts *o, struct burn_disc *disc); /** Inquire state and fill parameters of the fifo which is attached to the emerging track. This should be done in the pacifier loop while isoburn_disc_write() or burn_disc_write() are active. This works only with drives obtained by isoburn_drive_scan_and_grab() or isoburn_drive_grab(). If isoburn_prepare_new_image() was used, then parameter out_drive must have announced the track output drive. Hint: If only burn_write_opts and not burn_drive is known, then the drive can be obtained by burn_write_opts_get_drive(). @since 0.1.0 @param d The drive to which the track with the fifo gets burned. @param size The total size of the fifo @param free_bytes The current free capacity of the fifo @param status_text Returns a pointer to a constant text, see below @return <0 reply invalid, >=0 fifo status code: bit0+1=input status, bit2=consumption status, i.e: 0="standby" : data processing not started yet 1="active" : input and consumption are active 2="ending" : input has ended without error 3="failing" : input had error and ended, 4="unused" : ( consumption has ended before processing start ) 5="abandoned" : consumption has ended prematurely 6="ended" : consumption has ended without input error 7="aborted" : consumption has ended after input error */ int isoburn_get_fifo_status(struct burn_drive *d, int *size, int *free_bytes, char **status_text); /** Inquire whether the most recent write run was successful. Wrapper for: burn_drive_wrote_well() @since 0.1.0 @param d The drive to inquire @return 1=burn seems to have went well, 0=burn failed */ int isoburn_drive_wrote_well(struct burn_drive *d); /** Call this after isoburn_disc_write has finished and burn_drive_wrote_well() indicates success. It will eventually complete the emulation of multi-session functionality, if needed at all. Let libisoburn decide. Not a wrapper, but peculiar to libisoburn. @since 0.1.0 @param d The output drive to which the session was written @return 1 success , <=0 failure */ int isoburn_activate_session(struct burn_drive *d); /** Wait after normal end of operations until libisofs ended all write threads and freed resource reservations. This call is not mandatory. But without it, messages from the ending threads might appear after the application ended its write procedure. @since 0.1.0 @param input_drive The drive or in_drive which was used with the preparation call. @param output_drive The out_drive used with isoburn_prepare_new_image(), NULL if none. @param flag Bitfield, submit 0 for now. @return <=0 error , 1 = success */ int isoburn_sync_after_write(struct burn_drive *input_drive, struct burn_drive *output_drive, int flag); /** Release an acquired drive. Wrapper for: burn_drive_release() @since 0.1.0 @param drive The drive to be released @param eject 1= eject medium from drive , 0= do not eject */ void isoburn_drive_release(struct burn_drive *drive, int eject); /** Shutdown all three libraries. Wrapper for : iso_finish() and burn_finish(). @since 0.1.0 */ void isoburn_finish(void); /* The following calls are for expert applications only. An application should have a special reason to use them. */ /** Inquire whether the medium needs emulation or would be suitable for generic multi-session via libburn. @since 0.1.0 @param d The drive to inquire @return 0 is generic multi-session 1 is emulated multi-session -1 is not suitable for isoburn */ int isoburn_needs_emulation(struct burn_drive *d); /* ---------------------------- Test area ----------------------------- */ /* no tests active, currently */ #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LIBISOBURN_LIBISOBURN_H_ */ LIBISOBURN1 { global: isoburn_activate_session; isoburn_assess_written_features; isoburn_attach_image; isoburn_attach_start_lba; isoburn_conv_name_chars; isoburn_cancel_prepared_write; isoburn_disc_available_space; isoburn_disc_erasable; isoburn_disc_erase; isoburn_disc_get_msc1; isoburn_disc_get_status; isoburn_disc_pretend_full_uncond; isoburn_disc_track_lba_nwa; isoburn_disc_write; isoburn_drive_aquire; isoburn_drive_grab; isoburn_drive_release; isoburn_drive_scan_and_grab; isoburn_drive_set_msgs_submit; isoburn_drive_wrote_well; isoburn_finish; isoburn_get_attached_image; isoburn_get_attached_start_lba; isoburn_get_fifo_status; isoburn_get_min_start_byte; isoburn_get_mount_params; isoburn_igopt_attach_jte; isoburn_igopt_destroy; isoburn_igopt_detach_jte; isoburn_igopt_get_appended_as_apm; isoburn_igopt_get_appended_as_gpt; isoburn_igopt_get_data_start; isoburn_igopt_get_disc_label; isoburn_igopt_get_effective_lba; isoburn_igopt_get_efi_bootp; isoburn_igopt_get_extensions; isoburn_igopt_get_fifo_size; isoburn_igopt_get_gpt_guid; isoburn_igopt_get_hfsp_block_size; isoburn_igopt_get_hfsp_serial_number; isoburn_igopt_get_iso_mbr_part_type; isoburn_igopt_get_iso_type_guid; isoburn_igopt_get_level; isoburn_igopt_get_max_ce_entries; isoburn_igopt_get_out_charset; isoburn_igopt_get_over_mode; isoburn_igopt_get_over_ugid; isoburn_igopt_get_part_flags; isoburn_igopt_get_part_like_isohybrid; isoburn_igopt_get_part_type_guid; isoburn_igopt_get_partition_img; isoburn_igopt_get_prep_partition; isoburn_igopt_get_pvd_times; isoburn_igopt_get_relaxed; isoburn_igopt_get_rr_reloc; isoburn_igopt_get_scdbackup_tag; isoburn_igopt_get_sort_files; isoburn_igopt_get_stdio_endsync; isoburn_igopt_get_system_area; isoburn_igopt_get_tail_blocks; isoburn_igopt_get_untranslated_name_len; isoburn_igopt_get_write_type; isoburn_igopt_new; isoburn_igopt_set_appended_as_apm; isoburn_igopt_set_appended_as_gpt; isoburn_igopt_set_disc_label; isoburn_igopt_set_efi_bootp; isoburn_igopt_set_extensions; isoburn_igopt_set_fifo_size; isoburn_igopt_set_gpt_guid; isoburn_igopt_set_hfsp_block_size; isoburn_igopt_set_hfsp_serial_number; isoburn_igopt_set_iso_mbr_part_type; isoburn_igopt_set_iso_type_guid; isoburn_igopt_set_level; isoburn_igopt_set_max_ce_entries; isoburn_igopt_set_out_charset; isoburn_igopt_set_over_mode; isoburn_igopt_set_over_ugid; isoburn_igopt_set_part_flag; isoburn_igopt_set_part_like_isohybrid; isoburn_igopt_set_part_type_guid; isoburn_igopt_set_partition_img; isoburn_igopt_set_prep_partition; isoburn_igopt_set_pvd_times; isoburn_igopt_set_relaxed; isoburn_igopt_set_rr_reloc; isoburn_igopt_set_scdbackup_tag; isoburn_igopt_set_sort_files; isoburn_igopt_set_stdio_endsync; isoburn_igopt_set_system_area; isoburn_igopt_set_tail_blocks; isoburn_igopt_set_untranslated_name_len; isoburn_igopt_set_write_type; isoburn_initialize; isoburn_is_compatible; isoburn_libburn_req; isoburn_libisofs_req; isoburn_libjte_req; isoburn_needs_emulation; isoburn_prepare_blind_grow; isoburn_prepare_disc; isoburn_prepare_new_image; isoburn_read_image; isoburn_read_iso_head; isoburn_ropt_destroy; isoburn_ropt_get_auto_incharset; isoburn_ropt_get_data_cache; isoburn_ropt_get_default_dirperms; isoburn_ropt_get_default_perms; isoburn_ropt_get_displacement; isoburn_ropt_get_extensions; isoburn_ropt_get_input_charset; isoburn_ropt_get_size_what; isoburn_ropt_get_tree_loaded; isoburn_ropt_get_truncate_mode; isoburn_ropt_new; isoburn_ropt_set_auto_incharset; isoburn_ropt_set_data_cache; isoburn_ropt_set_default_dirperms; isoburn_ropt_set_default_perms; isoburn_ropt_set_displacement; isoburn_ropt_set_extensions; isoburn_ropt_set_input_charset; isoburn_ropt_set_truncate_mode; isoburn_set_msc1; isoburn_set_msgs_submit; isoburn_set_read_pacifier; isoburn_set_truncate; isoburn_sync_after_write; isoburn_toc_disc_free; isoburn_toc_disc_get_incmpl_sess; isoburn_toc_disc_get_sectors; isoburn_toc_disc_get_sessions; isoburn_toc_drive_get_disc; isoburn_toc_session_get_leadout_entry; isoburn_toc_session_get_sectors; isoburn_toc_session_get_tracks; isoburn_toc_track_get_emul; isoburn_toc_track_get_entry; isoburn_version; Xorriso__dispose_words; Xorriso__get_patch_level_text; Xorriso__is_compatible; Xorriso__preset_signal_behavior; Xorriso__severity_cmp; Xorriso__severity_list; Xorriso__version; Xorriso_change_is_pending; Xorriso_destroy; Xorriso_dialog; Xorriso_eval_problem_status; Xorriso_execute_option; Xorriso_fetch_outlists; Xorriso_get_problem_status; Xorriso_interpreter; Xorriso_lst_destroy_all; Xorriso_lst_get_next; Xorriso_lst_get_prev; Xorriso_lst_get_text; Xorriso_make_return_value; Xorriso_msgs_submit; Xorriso_msgs_submit_void; Xorriso_new; Xorriso_option_abort_on; Xorriso_option_abstract_file; Xorriso_option_acl; Xorriso_option_add; Xorriso_option_add_plainly; Xorriso_option_alter_date; Xorriso_option_append_partition; Xorriso_option_application_id; Xorriso_option_application_use; Xorriso_option_as; Xorriso_option_assert_volid; Xorriso_option_assess_indev_features; Xorriso_option_auto_charset; Xorriso_option_backslash_codes; Xorriso_option_ban_stdio_write; Xorriso_option_biblio_file; Xorriso_option_blank; Xorriso_option_boot_image; Xorriso_option_calm_drive; Xorriso_option_cdi; Xorriso_option_cdx; Xorriso_option_changes_pending; Xorriso_option_charset; Xorriso_option_check_md5; Xorriso_option_check_media; Xorriso_option_check_media_defaults; Xorriso_option_chgrpi; Xorriso_option_chmodi; Xorriso_option_chowni; Xorriso_option_clone; Xorriso_option_close; Xorriso_option_close_damaged; Xorriso_option_close_filter_list; Xorriso_option_commit; Xorriso_option_commit_eject; Xorriso_option_compare; Xorriso_option_compliance; Xorriso_option_concat; Xorriso_option_copyright_file; Xorriso_option_cp_clone; Xorriso_option_cpri; Xorriso_option_cpx; Xorriso_option_cut_out; Xorriso_option_data_cache_size; Xorriso_option_dev; Xorriso_option_devices; Xorriso_option_dialog; Xorriso_option_disk_dev_ino; Xorriso_option_disk_pattern; Xorriso_option_displacement; Xorriso_option_drive_access; Xorriso_option_drive_class; Xorriso_option_dummy; Xorriso_option_dvd_obs; Xorriso_option_early_drive_test; Xorriso_option_ecma119_map; Xorriso_option_eject; Xorriso_option_end; Xorriso_option_errfile_log; Xorriso_option_error_behavior; Xorriso_option_external_filter; Xorriso_option_extract; Xorriso_option_extract_boot_images; Xorriso_option_extract_cut; Xorriso_option_file_name_limit; Xorriso_option_file_size_limit; Xorriso_option_find; Xorriso_option_follow; Xorriso_option_fs; Xorriso_option_getfacli; Xorriso_option_gid; Xorriso_option_grow_blindly; Xorriso_option_hardlinks; Xorriso_option_help; Xorriso_option_hfsplus; Xorriso_option_hide; Xorriso_option_history; Xorriso_option_iso_nowtime; Xorriso_option_iso_rr_pattern; Xorriso_option_jigdo; Xorriso_option_joliet; Xorriso_option_joliet_map; Xorriso_option_launch_frontend; Xorriso_option_list_arg_sorting; Xorriso_option_list_delimiter; Xorriso_option_list_extras; Xorriso_option_list_formats; Xorriso_option_list_profiles; Xorriso_option_list_speeds; Xorriso_option_lnsi; Xorriso_option_load; Xorriso_option_logfile; Xorriso_option_lsi; Xorriso_option_lsx; Xorriso_option_map; Xorriso_option_map_l; Xorriso_option_mark; Xorriso_option_md5; Xorriso_option_mkdiri; Xorriso_option_modesty_on_drive; Xorriso_option_mount; Xorriso_option_mount_opts; Xorriso_option_move; Xorriso_option_msg_op; Xorriso_option_mvi; Xorriso_option_named_pipe_loop; Xorriso_option_no_rc; Xorriso_option_not_leaf; Xorriso_option_not_list; Xorriso_option_not_mgt; Xorriso_option_not_paths; Xorriso_option_options_from_file; Xorriso_option_osirrox; Xorriso_option_overwrite; Xorriso_option_pacifier; Xorriso_option_padding; Xorriso_option_page; Xorriso_option_paste_in; Xorriso_option_path_list; Xorriso_option_pathspecs; Xorriso_option_pkt_output; Xorriso_option_preparer_id; Xorriso_option_print; Xorriso_option_print_size; Xorriso_option_prog; Xorriso_option_prog_help; Xorriso_option_prompt; Xorriso_option_publisher; Xorriso_option_pvd_info; Xorriso_option_pwdi; Xorriso_option_pwdx; Xorriso_option_read_fs; Xorriso_option_read_mkisofsrc; Xorriso_option_reassure; Xorriso_option_report_about; Xorriso_option_report_el_torito; Xorriso_option_report_system_area; Xorriso_option_return_with; Xorriso_option_rmi; Xorriso_option_rockridge; Xorriso_option_rollback; Xorriso_option_rom_toc_scan; Xorriso_option_rr_reloc_dir; Xorriso_option_scdbackup_tag; Xorriso_option_scsi_dev_family; Xorriso_option_scsi_log; Xorriso_option_session_log; Xorriso_option_set_filter; Xorriso_option_setfacl_listi; Xorriso_option_setfacli; Xorriso_option_setfattr_listi; Xorriso_option_setfattri; Xorriso_option_sh_style_result; Xorriso_option_signal_handling; Xorriso_option_sleep; Xorriso_option_speed; Xorriso_option_split_size; Xorriso_option_status; Xorriso_option_status_history_max; Xorriso_option_stdio_sync; Xorriso_option_stream_recording; Xorriso_option_system_id; Xorriso_option_tell_media_space; Xorriso_option_temp_mem_limit; Xorriso_option_toc; Xorriso_option_toc_of; Xorriso_option_uid; Xorriso_option_unregister_filter; Xorriso_option_update; Xorriso_option_use_immed_bit; Xorriso_option_use_readline; Xorriso_option_version; Xorriso_option_volid; Xorriso_option_volset_id; Xorriso_option_volume_date; Xorriso_option_write_type; Xorriso_option_xattr; Xorriso_option_zisofs; Xorriso_parse_line; Xorriso_peek_outlists; Xorriso_prescan_args; Xorriso_process_errfile; Xorriso_process_msg_queues; Xorriso_program_arg_bsl; Xorriso_pull_outlists; Xorriso_push_outlists; Xorriso_read_rc; Xorriso_set_problem_status; Xorriso_sieve_add_filter; Xorriso_sieve_big; Xorriso_sieve_clear_results; Xorriso_sieve_dispose; Xorriso_sieve_get_result; Xorriso_start_msg_watcher; Xorriso_startup_libraries; Xorriso_stop_msg_watcher; local: *; }; LIBISOBURN1_1.5.8 { isoburn_attach_start_lba_v2; isoburn_disc_get_msc1_v2; isoburn_disc_track_lba_nwa_v2; isoburn_get_attached_start_lba_v2; isoburn_get_mount_params_v2; isoburn_igopt_get_data_start_v2; isoburn_igopt_get_effective_lba_v2; isoburn_prepare_blind_grow_v2; isoburn_read_iso_head_v2; isoburn_toc_disc_get_sectors_v2; isoburn_toc_session_get_sectors_v2; isoburn_toc_track_get_emul_v2; Xorriso_option_chattri; Xorriso_option_for_backup; Xorriso_option_genisoimage_completion; Xorriso_option_lfa_flags; } LIBISOBURN1; ------------------------------------------------------------------------------ http:libburnia-project.org ------------------------------------------------------------------------------ Release Engineering Check List TEST: releng auto_* tests could be run altogether by ./run_all_auto manual_* tests are to be run individually and manually LOGS: http://people.debian.org/~danchev/libburnia/logs/releng/ TEST: cppcheck - static code checker LOGS: http://people.debian.org/~danchev/libburnia/logs/cppcheck/ TEST: medistimator - checks the dialog mode of xorriso, size estimation facility, and its ability of processing large trees. Running this requires some specific knowledge of how the tool works, in order to interpret the results and compare them with these from any previous runs. The source is heavily commented. FILE: http://anonscm.debian.org/gitweb/?p=users/danchev/medistimator.git;a=summary LOGS: http://people.debian.org/~danchev/libburnia/logs/medistimator/ TEST: Debian ISO image builder logs LOGS: http://cdbuilder.debian.org/cdimage-log/ TEST: Debian build daemon logs - several hardware architectures and kernels LOGS: http://buildd.debian.org ------------------------------------------------------------------------------ http:libburnia-project.org ------------------------------------------------------------------------------ libisoburn/releng. By George Danchev <danchev@spnet.net> and Thomas Schmitt <scdbackup@gmx.net> Test suite for xorriso and libburnia libraries. Copyright (C) 2011 - 2012 George Danchev Copyright (C) 2011, 2012, 2019 Thomas Schmitt Provided under GPL version 2 or later. ------------------------------------------------------------------------------ The impatient tester will build libisoburn according to its README and then do cd ./releng ./run_all_auto -x ../xorriso/xorriso More patient testers will first read the following description. Those who simply lack the interpreter /bin/bash, may do ./change_shell_to_use and then retry. The test suite Directory ./releng of libisoburn contains a collection of test scripts and auxiliary data. They exercise some typical use cases of building libisoburn applications and running the ISO 9660 filesystem manipulation and CD/DVD/BD burn program xorriso. It is assumed that libburn and libisofs are installed, so that libisoburn can be configured and built. It is not mandatory that libisoburn is already installed. The tests may use an installed xorriso program as well as a freshly built one. The test scripts explicitly demand /bin/bash as interpreter, although they would run on certain other shells too. If you get an error message like ./run_all_auto: not found then consider to install /bin/bash. If you decide against that, see below "Alternative Shells". There are two groups of test scripts: auto_* gets started and watched by script run_all_auto. These tests have a moderate resource consumption and do not cause mechanical movements of drive trays. manual_* gets started by the user if desired. Manual tests may create larger sets of temporary files, may download test data from the internet, may need system privileges beyond the reach of a sandbox user, and operate the mechanics of a CD drive. Running automated tests The test scripts expect to get run while the working directory is ./releng of a libisoburn source tree. E.g.: libisoburn-1.1.4/releng They create all their temporary files underneath ./releng/releng_generated_data Some of these files are persistent between tests. Nevertheless it is safe to empty ./releng/releng_generated_data after tests are done. The directory itself must be kept. To run the unobtrusive automatic tests, build libisoburn and xorriso, go to directory ./releng, and execute ./run_all_auto -x ../xorriso/xorriso or if you want to use an installed xorriso program: ./run_all_auto -x $(which xorriso) ./run_all_auto -x $(type -p xorriso) There are several options which work with run_all_auto and any single test. -x absolute or relative path to xorriso binary to be run. -k keep self-generated data. -c cleanup temporary data kept from previous run and exit. -f simulate failure. -h print this help text. -- end of general options, begin of test specific options. After option "--", there may be given options which are specific to particular manually executable test scripts. Manually executable tests Currently there are the following tests which should have the attention of the user or require sysadmin considerations before they are run: ./manual_devices -x ../xorriso/xorriso [-- [--dev device_file_to_use] [--priv_cmd 'command [arg [arg ...]]']] Exercises listing of all accessible optical drives and the examination of a one of these drives. The user needs the permission to operate the CD drives. This might involve the need for superuser authority. The media tray of the examined drive will get loaded if it is not already. If no option --dev is given, then the user gets asked which of the listed drives to examine more closely. If a privilege command and optional arguments are given with --priv_cmd, then this command and arguments are used to launch the xorriso runs. Command and arguments must be single words and be submitted altogether as one single argument. On Solaris use: --priv_cmd pfexec ./manual_burn -x ../xorriso/xorriso [-- [--dev device_file_to_use] [--priv_cmd 'command [arg [arg ...]]'] [--what ...directory...] [--any_media]] Burns the content of the directory given with --what onto re-usable media: CD-RW, DVD-RW, DVD-RAM, DVD+RW, BD-RE. Other media types get refused, unless option --any_media is given. Data, which are possibly present on the media, get overwritten. The result gets check read and compared with the state of the input directory. MD5 mismatch causes a test failure. Differences to the directory state are reported but still regarded as success. If a privilege command and optional arguments are given with --priv_cmd, then this command and arguments are used to launch the xorriso runs. Command and arguments must be single words and be submitted altogether as one single argument. On Solaris use: --priv_cmd pfexec ./manual_isojigdo -x ../xorriso/xorriso [-- [--md5 | --sha256]] Exercises the production of a bootable Debian GNU/Linux image and its Jigdo files. This test downloads a Debian daily image for i386 of about 350 MB, extracts its content and composes a new image. Thus it needs about 1100 MB of disk space in releng/releng_generated_data when unpacked. Adding the daily image size itself, the total space used would peak at about 1.5 GB. This test will only work with GNU xorriso or if libjte was installed already when libisofs was built. Further it needs the program jigit-mkimage. Both are part of package jigit, version >= 1.22, available at: http://www.einval.com/~steve/software/JTE/ Currently jigit builds only in GNU environments. debian-cd currently uses the --md5 format. In future it will use --sha256. Any auto_* script can be run on its own. Some of them demand option -x. All general options are accepted. ./auto_cxx Not included in GNU xorriso. Exercises inclusion of xorriso/xorriso.h and libisoburn/libisoburn.h in C++ programs and linking of the libraries. It is possible to override the use of g++ as compiler by shell variable CC. It might be necessary to set compiler options by shell variable CFLAGS before running the test. It might be necessary to hand over the install directory of libburn and libisofs in shell variable LD_LIBRARY_PATH. E.g. if on FreeBSD the include headers libisofs.h , libburn.h are not found: export CFLAGS="-I/usr/local/include" E.g. on GNU/Hurd, where libburn and libisofs are not found by the linker: export LD_LIBRARY_PATH="/usr/local/lib" ./auto_isocontent -x ../xorriso/xorriso Tests whether xorriso is able to record and restore two families of hard links. ./auto_printsize -x ../xorriso/xorriso Tests how long xorriso needs to compose a medium sized directory tree. If programs mkisofs and/or genisomage are available, then the same test is done with them. ---------------------------------------------------------------------------- What to do with FAIL results The text output of the automatic tests is recorded in file releng_generated_data/log.run_all_auto Script ./run_all_auto will detect failure of particular tests and report lines from the log file which contain problem indicating keywords: NEVER,ABORT,FATAL,FAILURE,MISHAP,SORRY,WARNING,HINT,FAIL,ERROR,WRONG If the program messages in log.run_all_auto do not explain the failure, please contact mailing list libburn-hackers@pykix.org . ---------------------------------------------------------------------------- Alternative Shells If you decided against installing /bin/bash, you may try to use your current $SHELL by running ./change_shell_to_use which will modify the test scripts named run_all_auto , auto_* ,manual_*. Known to be suitable are the following shells GNU/Linux: /bin/bash FreeBSD 8: /bin/sh Solaris: /bin/bash , /bin/i86/ksh93 In general, the shell should have Bourne shell ancestry. The script does not choose an interpreter explicitly and is safe to be run inline: . ./change_shell_to_use One may set any interpreter path by running a sub shell and changing its variable SHELL. E.g. by: ( SHELL=/bin/my_shell" ; . ./change_shell_to_use ) ---------------------------------------------------------------------------- Creating a new test script If you want to provide an own test, manual or auto, then first invent a name for it test_name="releng/manual_"...some.name... or test_name="releng/auto_"...some.name... Then copy file releng/template_new to $test_name. Edit $test_name and process any line that begins by "# === TEMPLATE:". Do what the line prescribes and then remove it from the script. You are not done as long as such a line remains. Your test must not start if no file ./inc/releng_getopts.inc exists, i.e. if the current working directory is not ./releng. If your test creates own files on disk, then it must do this underneath directory ./releng_generated_data/$test_name (or $GEN_DATA_DIR, see below). In case of failure, issue a line to stdout that begins by the word "FAIL", followed by " : " and the name of the test (e.g. $SELF, see below). Make sure that the test script finally returns a non-zero exit value. This value should be between 1 and 28. Each type of failure should have its own exit value. Predefined are: 31 = Unknown option or unusable argument with known option 30 = Unexpected state of own directory for self generated files 29 = Not in ./releng directory or missing essential parts of ./releng When exiting prematurely, make sure to call function cleanup. Variables, general options, helper functions The master script run_all_auto sets this variable: RELENG_SCRIPT_RUN_BY_RUN_ALL_AUTO 1=supervised, the script is run by run_all_auto script else=standalone, the script is run in standalone mode The code piece inc/releng_getopts.inc should get executed inline at the start of a test script. It initializes the following variables and sets some of them according to the general options of the test suite: SELF basename $0 GEN_DATA_DIR releng_generated_data/${SELF} RELENG_XORRISO Path to xorriso binary. "" or "0" means no xorriso. Default "0". Adjustable by option -x. SIMULATE_FAILURE 0=normal operation, 1=test script shall simulate a failure. Default 0. Setable to 1 by option -f. CLEANUP 0=do not cleanup temporary data, 1=normal operation Default 1. Setable to 0 by option -k. SPECIFIC_HELP 0=normal operation, 1=print help text of script and exit 0 Default 0. Setable to 1 by option -h. The code piece inc/releng_getopts.inc defines the following functions for use by the single tests: standalone_or_supervised This is internally called routine to print out the running mode of the scripts - standalone, supervised by run_all_auto. No need to call it from the scripts themselves. print_help Prints the help text for general options. check_for_xorriso [-x] Will exit with value 31 if no path to a xorriso binary was defined by option -x of ./run_all_auto or a single test. Option -x of check_for_xorriso additionally tests whether the given path leads to an executable program. cleanup Removes the directory tree GEN_DATA_DIR after making some safety checks. boldify Try to set the terminal mode for future output to a more noticeable style of writing. unboldify Reset terminal mode to normal style of writing. Specific options Options which are specific to the test should begin with a double dash. They may have further arguments. Implement them in the prepared interpreter loop which begins after line next_is=ignore Specific options shall only be interpreted by tests which get run manually. If you plan to introduce a specific option, look at the description of existing tests whether one of them would match your needs. In that case, please re-use the name of that existing option. * Manpage examples turned into tests Convert most examples from xorriso(1) manpage into tests. * Enhance 'auto_isocontent' Extend it to use some more demanding directory tree. MD5s should be checked. All file types as of stat(2) should be tested. Test various comparisons: xorriso provides built-in means for comparison: xorriso -compare_r disk_path iso_rr_path xorriso -indev my.iso -find / vs. find input_dir bsdtar -xf my.iso vs. input_dir * Test for wrong CD sizes would need a new test and probably an automatic CD changer. * Library unit tests - investigate the possibility to write some cunit-based tests (http://cunit.sourceforge.net) for both xorriso.h and libisoburn.h API's. The code samples could be put into codesamples/ directory and run by auto_cxx or a separate auto_ script. * ??? Still to decide: Delete debian-testing-i386-businesscard.iso with ./run_all_auto -c Contra: Currently remaining large files (like downloaded ISO images) are simply left behind to be re-used and a boldified info message is shown so the users can decide for themselves what to remove or leave as well. Pro: Leaving 70 MB of image is quite obtrusive. Option -c is not run under normal circumstances. So it could well be used for total cleanup. Alternative: Specific option --remove_image. * ??? Still to decide: Have a script ./run_all_manual Contra argument: if a releng sctipt is suitable to be run under a master script run_all*, then this releng script should be put into auto_* namespace , otherwise it is manual_*. Pro: Tests may be manual because they demand lots of resources, not because they need manual attention. In general the run_all_manual script shall spare the user the plight to read the documentation. Instead it shall present the manual tests, give an opportunity to skip the test, and ask for parameters, #!/bin/bash # Copyright 2011 George Danchev <danchev@spnet.net> # Licensed under GNU GPL version 2 or later set -e not_in_releng_exit() { printf "\nPlease execute the tests from releng directory.\n\n" exit 1 } . inc/releng_getopts.inc || not_in_releng_exit print_specific_help() { cat << HLP Specific options: none yet. Overview: Tests both xorriso/xorriso.h and libisoburn/libisoburn.h APIs for C++ cleanness. HLP } if test "$SPECIFIC_HELP" = 1; then print_specific_help exit 0 fi # xorriso binary is not needed for that particular test SAMPLE_CODE_DIR=codesamples CC=${CC:-g++} # check compiler if ! which "${CC}" >/dev/null 2>&1; then printf "\n${SELF}: Not found: ${CC}. Install ${CC}.\n" cleanup exit 5 fi # check data dir if [ -d "${GEN_DATA_DIR}" ]; then printf "\n${SELF}: directory %s exists!" ${GEN_DATA_DIR} printf "\n${SELF}: use '${SELF} -c' to remove.\n" exit 6 else mkdir "${GEN_DATA_DIR}" fi INCLUDE_DIRS="-I../ -I../../libburn -I../../libisofs -I/usr/local/include -I/usr/pkg/include" # process sample code tests for SMPL in `ls "${SAMPLE_CODE_DIR}"/*.cpp`; do # CMD_CPL="${CC} -I../ -L ../libisoburn/.libs/ ${CFLAGS} -lisoburn -o ${SMPL}.obj ${SMPL}" CMD_CPL="${CC} -c ${INCLUDE_DIRS} ${CFLAGS} -o ${SMPL}.obj ${SMPL}" printf "${SELF}: ${CMD_CPL}" set +e ${CMD_CPL} RET_CPL="$?" if [ ${RET_CPL} = 0 -a -f ${SMPL}.obj ]; then mv ${SMPL}.obj ${GEN_DATA_DIR} printf "...ok\n" else printf "\nFAIL : ${SELF}: Compilation of ${SMPL}\n" cleanup exit 7 fi # BASE=$(basename ${SMPL}.obj) # printf "${SELF}: Running LD_LIBRARY_PATH=../libisoburn/.libs/:${LD_LIBRARY_PATH} ${GEN_DATA_DIR}/${BASE}" # LD_LIBRARY_PATH=../libisoburn/.libs/:${LD_LIBRARY_PATH} ${GEN_DATA_DIR}/${BASE} # RET_SMPL="$?" # case ${RET_SMPL} in # 0) # printf "...ok\n" # ;; # *) # printf "exit code: ${RET_SMPL}\n" # cleanup # exit 8 # ;; # esac set -e done # clean cleanup exit 0 #!/bin/bash # Copyright 2011 Thomas Schmitt <scdbackup@gmx.net> # Copyright 2011 George Danchev <danchev@spnet.net> # Licensed under GNU GPL version 2 or later # Test the correct handling of hardlinks by xorriso options # -update_r , -hardlinks perform_update , and -extract # If there is support for ACLs or xattr in xorriso and on the local system, # then test recording and restoring of these features. not_in_releng_exit() { printf "\nPlease execute the tests from releng directory.\n\n" exit 1 } . inc/releng_getopts.inc || not_in_releng_exit print_specific_help() { cat << HLP Specific options: none yet. Overview: Tests ISO image contents by performing various image generation, extractions and comparisons. HLP } if test "$SPECIFIC_HELP" = 1; then print_specific_help exit 0 fi if [ ! -x $RELENG_XORRISO ]; then print_help printf "\n${SELF}: -x absolute or relative path to binary to be run.\n\n" exit 31 fi # check data dir, if any and after checking -x xorriso if [ -d "${GEN_DATA_DIR}" ]; then printf "\n${SELF}: directory %s exists!" ${GEN_DATA_DIR} printf "\n${SELF}: use '${SELF} -c' to remove.\n" exit 8 else mkdir "${GEN_DATA_DIR}" fi export xorriso=${RELENG_XORRISO} export workdir=${GEN_DATA_DIR} export image_file="$workdir"/xorriso_hardlinks.iso export on_disk="$workdir"/xorriso_hardlinks_test_dir export in_iso="" export copy_on_disk="$workdir"/xorriso_hardlinks_copy_dir export failure=0 export simulate_failure=${SIMULATE_FAILURE} export next_is_xorriso=0 export next_is_rc=0 export bad=0 export report_about="-report_about UPDATE" test -z "$in_iso" && in_iso="$on_disk" # mkdir "$workdir" || bad=1 mkdir "$on_disk" || bad=1 if test "$bad" = 1 then echo -e "\nFAIL : ${SELF} : Test environment error : Cannot make directories" exit 3 fi # All must be set at this point printf "\n${SELF}: Setting up $on_disk with several hardlinks\n" >&2 echo test_content >"$on_disk"/file_1 || exit 1 echo test_content >"$on_disk"/file_2 || exit 1 ln "$on_disk"/file_1 "$on_disk"/file_1_link_a || exit 1 ln "$on_disk"/file_1 "$on_disk"/file_1_link_b || exit 1 ln "$on_disk"/file_2 "$on_disk"/file_2_link_a || exit 1 # trivial ISO 9660 image validation routine is_valid_iso9660() { ISOfile="$1" if ! which file >/dev/null 2>&1; then printf "\nFAIL : ${SELF}: Not found: file. Please install the file(1) utility.\n" failure=1 return fi if [ ! -f ${ISOfile} ]; then failure=1 printf "\nFAIL : ${SELF} : Not found: ${ISOfile}\n" return fi file ${ISOfile} if file ${ISOfile} | grep "ISO *9660" >/dev/null 2>&1; then printf "\n${SELF}: Resulting ${ISOfile} OK. Looks like ISO 9660 filesystem.\n" else failure=1 printf "\nFAIL : ${SELF} : ${ISOfile} DOES NOT look like ISO 9660 filesystem data.\n" fi } # Retrieve and evaluate return value of command run under return_wrapper check_xorriso_return() { ret=$(cat "$return_value_file") rm "$return_value_file" if test "$ret" = 0 then return 0 fi failure=1 echo echo "FAIL : ${SELF} : xorriso run exited with value $ret" return 1 } # Create test file and find out whether ACLs and/or xattr are available. # # Users known on GNU/Linux and FreeBSD: root games daemon man # Groups : daemon games bin sshd sys # On both systems, ACLs are manipulated by setfacl/getfacl # acl_xattr_test_file="$on_disk"/acl_xattr_test_file acl_xattr_copy_file="$copy_on_disk"/acl_xattr_test_file acl_xattr_test_dir="$on_disk"/acl_xattr_test_dir acl_xattr_iso_dir="$in_iso"/acl_xattr_test_dir acl_xattr_copy_dir="$copy_on_disk"/acl_xattr_test_dir mkdir "$acl_xattr_test_dir" echo echo hello world >"$acl_xattr_test_file" || exit 1 sys=$(uname -s) acls=no default_acls=no setfacl_opts="" if ( setfacl -m u::rwx,g::r-x,o::---,u:root:rwx,g:sys:rwx,u:daemon:r--,mask::rwx \ "$acl_xattr_test_file" ) >/dev/null 2>&1 then if ( getfacl "$acl_xattr_test_file" ) >/dev/null 2>&1 then if ( setfacl -m u::rwx,g::r-x,o::---,u:root:rwx,g:sys:rwx,u:daemon:r--,mask::rwx \ "$acl_xattr_test_dir" ) >/dev/null 2>&1 then acls=yes # Setting of "default" ACLs will fail on FreeBSD. It will nevertheless be # done in the image by a xorriso command. Restoring is supposed to skip # "default" ACLs if none could be recorded. if setfacl -m u::rwx,g::r-x,o::---,u:root:rwx,g:sys:rwx,u:daemon:r--,mask::rwx,d:u::rwx,d:g::r-x,d:o::---,d:u:root:rwx,d:g:sys:rwx,d:u:daemon:r--,d:mask::rwx "$acl_xattr_iso_dir" 2>/dev/null then default_acls=yes fi setfacl_opts="-setfacl u::rwx,g::r-x,o::---,u:root:rwx,g:sys:rwx,u:daemon:r--,mask::rwx,d:u::rwx,d:g::r-x,d:o::---,d:u:root:rwx,d:g:sys:rwx,d:u:daemon:r--,d:mask::rwx $acl_xattr_iso_dir --" fi fi fi # GNU/Linux and FreeBSD have different tools for Extended Attributes xattrs=no extattrs=no # Try GNU/Linux style setattr/getattr if ( setfattr -n user.test_xattr -v test_value "$acl_xattr_test_file" ) \ >/dev/null 2>&1 then if ( getfattr -d "$acl_xattr_test_file" ) >/dev/null 2>&1 then xattrs=yes setfattr -n user.long_data -v 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 "$acl_xattr_test_file" setfattr -n user.more_data -v 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 "$acl_xattr_test_file" fi fi if test "$xattrs" = no then # Try FreeBSD style setextattr if ( setextattr user test_xattr test_value "$acl_xattr_test_file" ) \ >/dev/null 2>&1 then if ( getextattr user test_xattr "$acl_xattr_test_file" ) >/dev/null 2>&1 then setextattr user long_data 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 "$acl_xattr_test_file" setextattr user more_data 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 "$acl_xattr_test_file" if ( lsextattr user "$acl_xattr_test_file" ) >/dev/null 2>&1 then extattrs=yes fi fi fi fi echo echo "${SELF}: Detected sys='$sys' , acls=$acls , d_acls=$default_acls , xattrs=$xattrs , extattrs=$extattrs" # Examine capabilities of xorriso xorriso_acls=no xorriso_xattrs=no extras=$("$xorriso" -list_extras all 2>/dev/null) if test "$?" = 0 then if echo "$extras" | fgrep 'Local ACL : yes' >/dev/null 2>&1 then xorriso_acls=yes fi if echo "$extras" | fgrep 'Local xattr : yes' >/dev/null 2>&1 then xorriso_xattrs=yes fi fi if test "$xorriso_acls" = no then acls=no setfacl_opts= fi if test "$xorriso_xattrs" = no then xattrs=no extattrs=no fi echo "${SELF}: Detected xorriso_acls=$xorriso_acls , xorriso_xattrs=$xorriso_xattrs" echo ls -l "$on_disk"/* echo -e "\n${SELF}: Producing simple image via -o" >&2 "$xorriso" -as mkisofs "$on_disk" -o "$workdir"/image_minus_o.iso is_valid_iso9660 "$workdir"/image_minus_o.iso echo -e "\n${SELF}: Producing simple image via redirect" >&2 "$xorriso" -as mkisofs "$on_disk" > "$workdir"/image_redirected.iso is_valid_iso9660 "$workdir"/image_redirected.iso echo -e "\n${SELF}: Producing simple image via pipe" >&2 return_wrapper "$xorriso" -as mkisofs "$on_disk" | \ cat > "$workdir"/image_piped.iso check_xorriso_return is_valid_iso9660 "$workdir"/image_piped.iso echo -e "\n${SELF}: Producing simple image with for_backup/update_r/hardlinks" >&2 "$xorriso" \ $report_about \ -version \ -for_backup \ -padding 0 \ -outdev "$image_file" \ -volid TEST_AUTO_ISOCONTENT \ -update_r "$on_disk" "$in_iso" \ $setfacl_opts \ -hardlinks perform_update ret=$? if test "$ret" -gt 0 -a "$ret" -lt 32 then printf "\nFAIL : ${SELF} : xorriso write run failed\n\n" cleanup exit 1 fi is_valid_iso9660 "$image_file" # It must refuse to load and go on with -assert_volid and non-matching pattern. msg=$(\ "$xorriso" \ -abort_on FATAL \ -return_with FAILURE 32 \ -assert_volid 'NON_MATCHING*' FATAL \ -indev "$image_file" \ 2>&1 ) ret=$? if test "$ret" -gt 0 -a "$ret" -lt 32 then printf "\n${SELF}: Ok. -assert_volid snapped.\n" elif test "$ret" -ne 0 then failure=1 echo >&2 echo "$msg" >&2 printf "\nFAIL : ${SELF} : -assert_volid test not properly performed\n\n" else failure=1 printf "\nFAIL : ${SELF} : -assert_volid did not snap\n\n" >&2 fi echo -e "\n${SELF}: Copying from image to temporary disk tree" >&2 "$xorriso" \ $report_about \ -for_backup \ -assert_volid 'TEST_AUTO_ISOCONT*' FATAL \ -indev "$image_file" \ -osirrox on \ -find "$in_iso" -exec lsdl -- \ -extract "$in_iso" "$copy_on_disk" ret=$? if test "$ret" -gt 0 -a "$ret" -lt 32 then printf "\nFAIL : ${SELF} : xorriso file extraction run failed\n\n" cleanup exit 1 fi if test "$simulate_failure" = 1 then echo -e "\n${SELF}: SIMULATING FAILURE BY REMOVING AN EXTRACTED FILE" >&2 echo -e "\nFAIL : ${SELF} : Simulated failure caused by option -fail" rm "$copy_on_disk"/file_1_link_b fi printf "\n${SELF}: Comparing original disk tree and temporary one..." >&2 diff -r "$on_disk" "$copy_on_disk" if test "$?" -ne 0 then echo -e "\nFAIL : ${SELF} : diff -r $on_disk $copy_on_disk reports differences" >&2 echo -e "\nFAIL : ${SELF} : diff -r reports differences" failure=1 else printf "OK" >&2 fi printf "\n${SELF}: Checking for hardlinks being siblings...\n" ls -l "$copy_on_disk"/* x=$(echo $(ls -ld "$copy_on_disk"/*file* | awk '{print $2}')) expected="1 3 3 3 2 2" if test x"$x" = x"$expected" then printf "${SELF}: Checking for hardlinks being siblings done: ok.\n" >&2 else printf "\nFAIL : ${SELF} : Link count of extracted files is not as expected." >&2 printf "\n${SELF}: Expected: $expected" >&2 printf "\n${SELF}: Got : $x\n" >&2 failure=1 fi if test "$acls" = yes then printf "\n${SELF}: Checking ACLs ...\n" >&2 acl_on_disk=$(getfacl "$acl_xattr_test_file" | grep -v '^# file:' | sort) acl_in_copy=$(getfacl "$acl_xattr_copy_file" | grep -v '^# file:' | sort) if test "$acl_on_disk" = "$acl_in_copy" then printf "${SELF}: Checking ACLs done: ok.\n" >&2 else printf "\nFAIL : ${SELF} : ACL mismatch between original and extracted copy\n" printf "\nOriginal:\n${acl_on_disk}\n" printf "\nCopy:\n${acl_in_copy}\n" failure=1 fi fi if test "$xattrs" = yes then printf "\n${SELF}: Checking xattr via getfattr ...\n" >&2 xattr_on_disk=$(getfattr "$acl_xattr_test_file" | \ grep -v '^# file:' | grep -v '^$' | \sort) xattr_in_copy=$(getfattr "$acl_xattr_copy_file" | grep -v '^# file:' | grep -v '^$' | sort) if test "$xattr_on_disk" = "$xattr_in_copy" then num_xattr=$(echo "$xattr_on_disk" | wc -l) printf "${SELF}: Checking xattr done: $num_xattr attributes, ok.\n" >&2 else printf "\nFAIL : ${SELF} : xattr mismatch between original and extracted copy\n" printf "\nOriginal:\n${xattr_on_disk}\n" printf "\nCopy:\n${xattr_in_copy}\n" failure=1 fi elif test "$extattrs" = yes then printf "\n${SELF}: Checking xattr via lsextattr and getextattr ...\n" >&2 lsext_on_disk=$(lsextattr -q user "$acl_xattr_test_file") xattr_on_disk=$(for i in $lsext_on_disk ; do echo $i $(getextattr -q user $i "$acl_xattr_test_file"); done | sort) lsext_in_copy=$(lsextattr -q user "$acl_xattr_copy_file") xattr_in_copy=$(for i in $lsext_in_copy ; do echo $i $(getextattr -q user $i "$acl_xattr_copy_file"); done | sort) if test "$xattr_on_disk" = "$xattr_in_copy" then num_xattr=$(echo "$xattr_on_disk" | wc -l) printf "${SELF}: Checking xattr done: $num_xattr attributes, ok.\n" >&2 else printf "\nFAIL : ${SELF} : xattr mismatch between original and extracted copy\n" printf "\nOriginal:\n${xattr_on_disk}\n" printf "\nCopy:\n${xattr_in_copy}\n" failure=1 fi fi # echo cleanup # Report result echo if test "$failure" = 1 then printf "${SELF}: FAILED" echo exit 1 else printf "${SELF}: Passed" echo fi exit 0 #!/bin/bash # Copyright 2011 George Danchev <danchev@spnet.net> # Licensed under GNU GPL version 2 or later set -e not_in_releng_exit() { printf "\nPlease execute the tests from releng directory.\n\n" exit 1 } # Include common bits . inc/releng_getopts.inc || not_in_releng_exit print_specific_help() { cat << HLP Specific options: none yet. Overview: Test performance of print_size against various input tree. Optionally compare with genisoimage and mkisofs. HLP } if test "$SPECIFIC_HELP" = 1; then print_specific_help exit 0 fi # Each test should decide whether or not it needs # a xorriso binary to test, since some do compilations only. if [ ! -x $RELENG_XORRISO ]; then print_help printf "\n${SELF}: -x absolute or relative path to binary to be run.\n\n" exit 31 fi # check data dir, if any and after checking -x xorriso if [ -d "${GEN_DATA_DIR}" ]; then printf "\n${SELF}: directory %s exists!" ${GEN_DATA_DIR} printf "\n${SELF}: use '${SELF} -c' to remove.\n" exit 8 else mkdir "${GEN_DATA_DIR}" fi # DIR_UPPER=32 FILE_UPPER=10 # All must be set at this point # TODO: work out a smarter way to quickly generate different # types of trees (long, deep, etc) printf "\n${SELF}: Generating sample tree in ${GEN_DATA_DIR} :\n" count=0 date # Hopefully the for-loops are much faster than while-loops with arithmetics # This needs 7/4*DIR_UPPER+FILE_UPPER (= 66) while-iterations # i1_list= i1=0 o1=$(expr ${DIR_UPPER} / 4) while test $i1 -lt $o1 do i1_list="$i1_list $i1" i1=$(expr $i1 + 1) done i2_list= i2=0 o2=$(expr ${DIR_UPPER} / 2) while test $i2 -lt $o2 do i2_list="$i2_list $i2" i2=$(expr $i2 + 1) done i3_list= i3=0 while test $i3 -lt ${DIR_UPPER} do i3_list="$i3_list $i3" i3=$(expr $i3 + 1) done i_file_list= i_file=0 while test $i_file -lt ${FILE_UPPER} do i_file_list="$i_file_list $i_file" i_file=$(expr $i_file + 1) done # # plus 1/8*DIR_UPPER*DIR_UPPER*DIR_UPPER*FILE_UPPER (= 40960) for-iterations # for i1 in $i1_list do for i2 in $i2_list do for i3 in $i3_list do mkdir -p ${GEN_DATA_DIR}/DirOne$i1/DirTwo$i2/DirThree$i3 for i_file in $i_file_list do echo -n \ > ${GEN_DATA_DIR}/DirOne$i1/DirTwo$i2/DirThree$i3/File_${i_file} count=$((count + 1)) done done done echo " ${count} files created ..." done printf "done.\n" date du -s "${GEN_DATA_DIR}" printf "\n${SELF}: Performing several print size runs to neutralize possible disk cache impact.\n" # run xorriso if [ -x ${RELENG_XORRISO} ]; then for run in 1 2 3; do printf "\n${SELF}: Running ${RELENG_XORRISO} -as mkisofs -quiet -print-size ${GEN_DATA_DIR}. Trial: ${run}.\n" time ${RELENG_XORRISO} -as mkisofs -quiet -print-size ${GEN_DATA_DIR} done fi # try to run genisoimage if which genisoimage >/dev/null 2>&1; then RELENG_GENISOIMAGE=`which genisoimage` for run in 1 2 3; do printf "\n${SELF}: Running ${RELENG_GENISOIMAGE} -quiet -print-size ${GEN_DATA_DIR}. Trial: ${run}.\n" time ${RELENG_GENISOIMAGE} -quiet -print-size ${GEN_DATA_DIR} done fi # try to run mkisofs if which mkisofs >/dev/null 2>&1; then RELENG_MKISOFS=`which mkisofs` for run in 1 2 3; do printf "\n${SELF}: Running ${RELENG_MKISOFS} -quiet -print-size ${GEN_DATA_DIR}. Trial: ${run}.\n" time ${RELENG_MKISOFS} -quiet -print-size ${GEN_DATA_DIR} done fi # cleanup # exit 0 # check whether we are in releng and create dir mkdir_ret=29 if test -d releng_generated_data then if test -d releng_generated_data/change_shell_to_use then mkdir_ret=0 else mkdir releng_generated_data/change_shell_to_use mkdir_ret=$? fi fi if test "$mkdir_ret" = 0 then for i in run_all_auto auto_* manual_* do temp=releng_generated_data/change_shell_to_use/temp_file c=$(wc -l "$i" | awk '{print $1}') line=$(head -n 1 "$i") if echo x"$line" | grep '^x#!' >/dev/null 2>&1 then cp "$i" "$temp" echo '#!'"$SHELL" > "$temp" tail -n "$(expr $c - 1)" "$i" >> "$temp" mv "$temp" "$i" echo "Changed to #!$SHELL : $i" fi done rmdir releng_generated_data/change_shell_to_use else echo "change_shell_to_use: Missing directory ./releng_generated_data" >&2 echo "or cannot create directory ./releng_generated_data/change_shell_to_use" >&2 echo "change_shell_to_use: Run aborted" test 1 = 0 fi // Just to ensure we are C++-clean. This should not spit too much noise /* Copyright 2011 George Danchev <danchev@spnet.net> * Released into the public domain */ #if __WORDSIZE == 32 #define _LARGEFILE_SOURCE 1 #define _FILE_OFFSET_BITS 64 #endif #include <iostream> #include <inttypes.h> //extern "C" { #include "libburn/libburn.h" #include "libisofs/libisofs.h" // using namespace burn; // (this was needed to before rev.4062 of libisoburn) #include "libisoburn/libisoburn.h" //} int main() { int major=-1, minor=-1, micro=-1; isoburn_version(&major, &minor, µ); if (major<0 || minor<0 || micro<0) return -1; std::cout << " major:" << major << " minor:" << minor << " micro:" << micro ; return 0; } // Just to ensure we are C++-clean. This should not spit too much noise /* Copyright 2011 George Danchev <danchev@spnet.net> * Released into the public domain */ #if __WORDSIZE == 32 #define _LARGEFILE_SOURCE 1 #define _FILE_OFFSET_BITS 64 #endif #include <iostream> #include <inttypes.h> //extern "C" { #include "xorriso/xorriso.h" //} int main() { int major=-1, minor=-1, micro=-1; Xorriso__version(&major, &minor, µ); if (major<0 || minor<0 || micro<0) return -1; std::cout << " major:" << major << " minor:" << minor << " micro:" << micro ; return 0; } # Copyright 2011 George Danchev <danchev@spnet.net> # Copyright 2011 Thomas Schmitt <scdbackup@gmx.net> # Licensed under GNU GPL version 2 or later SELF=$(basename $0) RELENG_XORRISO=0 SIMULATE_FAILURE=0 CLEANUP=1 SPECIFIC_HELP=0 START_DIR_DONT_CHANGE=`pwd` GEN_DATA_DIR=releng_generated_data/${SELF} ############################################# standalone_or_supervised() { case "${RELENG_SCRIPT_RUN_BY_RUN_ALL_AUTO}" in 1) echo "${SELF}: Running in Supervised mode" ;; *) echo "${SELF}: Running in Standalone mode" ;; esac } # Unconditionally shout out the invocation mode - standalone or supervised standalone_or_supervised ############################################# print_help() { cat << EOF Usage: $SELF -x path/to/xorriso [-k] [-f] [-c] [-h] [-- ...test specific options...] General options: -x absolute or relative path to xorriso binary to be run. -k keep self-generated data. -c cleanup self-generated data kept from previous run and exit. -f simulate failure. -h print this help text -- end of general options, begin of test specific options. EOF } ############################################# boldify() { if which tput >/dev/null 2>&1 then tput smso || dummy_variable=1 fi } ############################################# unboldify() { if which tput >/dev/null 2>&1 then tput rmso || dummy_variable=1 fi } ############################################# cleanup() { if [ ${CLEANUP} -eq 1 ]; then # safety net, just in case -> we want to be in the starting # directory before removing whatever self-generated stuff if [ -d "${GEN_DATA_DIR}" ]; then cd "${START_DIR_DONT_CHANGE}" || exit 2 # Verify once again we are in the releng_generated_data directory # Check for both returned code of grep and returned matching string # There is no "readlink -e" on FreeBSD READ_CANON_EXISTS=`cd "${GEN_DATA_DIR}" 2>/dev/null && pwd` DIR_NAME_GEN_DATA=`dirname "${READ_CANON_EXISTS}"` set +e # There is no "grep -P" on FreeBSD RET_NON_EMPTY_STRING=`echo "${DIR_NAME_GEN_DATA}" | grep "[a-zA-Z0-9_][a-zA-Z0-9_]*/releng_generated_data$"` GREP_RET_GEN_DATA="$?" case "${GREP_RET_GEN_DATA}" in 0) if [ x"${RET_NON_EMPTY_STRING}" != x"" ]; then # now call the nastiness chmod -R +w ${GEN_DATA_DIR} rm -rf ${GEN_DATA_DIR} # boldify printf "${SELF}: Removed (self-generated) %s\n" ${GEN_DATA_DIR} # unboldify else printf "FAIL : ${SELF} : Safety check for being in releng_generated_data directory.\n" printf "FAIL : ${SELF} : GREP returned empty string: ${RET_NON_EMPTY_STRING}.\n" printf "FAIL : ${SELF} : Skipped trying to remove ${GEN_DATA_DIR} directory. Exiting.\n" fi ;; *) printf "FAIL : ${SELF} : Safety check for being in releng_generated_data directory.\n" printf "FAIL : ${SELF} : GREP returned code: ${GREP_RET_GEN_DATA}.\n" printf "FAIL : ${SELF} : Skipped trying to remove ${GEN_DATA_DIR} directory. Exiting.\n" exit 30 ;; esac else printf "${SELF}: ${GEN_DATA_DIR} does not exist. Nothing to clean.\n" fi else # boldify printf "${SELF}: Leaving (self-generated) %s\n" ${GEN_DATA_DIR} # unboldify fi } ############################################# check_for_xorriso() { # $1: if "-x" then check executability if test -z "$RELENG_XORRISO" -o "$RELENG_XORRISO" = "0" then print_help # print_specific_help echo echo "${SELF}: Need -x absolute or relative path to xorriso binary." echo exit 31 fi if [ x"$1" = x"-x" -a ! -x "$RELENG_XORRISO" ] then print_help # print_specific_help echo echo "${SELF}: Path given by option -x does not lead to an executable file." echo "Given is: '$RELENG_XORRISO'" if test "$RELENG_XORRISO" = "xorriso" then xorriso=$(type -p xorriso) if test -n "xorriso" then echo "Hint: Try '$xorriso'" fi fi echo exit 31 fi } ############################################# # To catch the exit value of a command in a pipe return_value_file="$GEN_DATA_DIR"/wrapper_"$$"_return_value return_wrapper() { cmd="$1" shift 1 "$cmd" "$@" RET="$?" echo "$RET" >"$return_value_file" return "$RET" } ############################################# next_is= for i in "$@" do if test "$next_is" = "ignore" then : elif test "$next_is" = "x" then RELENG_XORRISO="$i" next_is= elif test x"$i" = x"-x" then next_is="x" elif test x"$i" = x"-k" then CLEANUP=0 elif test x"$i" = x"-c" then CLEANUP=1 cleanup exit 0 elif test x"$i" = x"-f" then SIMULATE_FAILURE=1 elif test x"$i" = x"-h" then print_help SPECIFIC_HELP=1 elif test x"$i" = x"--" then # Begin of private arguments for caller next_is="ignore" else echo >&2 echo "Unknown general option: $i" >&2 print_help exit 31 fi done if test "$next_is" = x then echo >&2 echo "Option -x expects an argument (the path to the xorriso program)" >&2 print_help exit 31 fi #!/bin/bash set -e . releng_getopts.inc boldify printf "\ntesting boldify and unboldify..." unboldify printf "\nSELF =${SELF}" printf "\nRELENG_XORRISO =${RELENG_XORRISO}" printf "\nSIMULATE_FAILURE =${SIMULATE_FAILURE}" printf "\nCLEANUP =${CLEANUP}" printf "\nSPECIFIC_HELP =${SPECIFIC_HELP}" printf "\nSTART_DIR_DONT_CHANGE =${START_DIR_DONT_CHANGE}" printf "\nGEN_DATA_DIR =${GEN_DATA_DIR}" printf "\n" exit 0 #!/bin/sh # Copyright (c) 2010, 2011 George Danchev <danchev@spnet.net> # Copyright (c) 2010, 2011, 2019 Thomas Schmitt <scdbackup@gmx.net> # This script is distributed according to the terms of the GNU GPL v2. # This should be better rewritten in C at some future point. Ref: pwd code. # Create a list of checksums encoded in hexadecimal format and print to # standard output. Checksum may be MD5 or SHA256. # Format Description # A line in the emerging file is to be composed as follows: # # The checksum of the file content must be encoded in the aprropriate number # of hex digits. # [0-9afAF] # # Next come two blanks. # # The byte size of the file content must be encoded in 12 decimal digits # or blanks. # # Next come two blanks. # # The rest of the line up to the newline character is a semi-literal file # address. Its basename has to be the same as the basename of the data file # when it is used as one of the input files for the jigdo file generator. # The semi-literal address and the address mapping define what will be # listed as file address in the jigdo file. # The address may bear at its start a literal text that shall be recognized by # the address mapping (e.g. -jigdo-map) of the jigdo file generator. # The rest of the address must be usable as file address in both situations: # When the jigdo file gets generated, and when the jigdo file gets read # to inflate the template file into the original payload image. # The address mappings at both occasions can be used to adapt to a change # of the absolute location of the listed files. # Between both mappings, the parent directory is represented by a symbolic # text, like "Debian:". # A simple strategy to cope with this is to write absolute paths into the # checksum file, and to use matching absolute paths in the -jigdo-map # directives. Keep in mind that mapping is purely literal. Symbolic links # are neither resolved nor can they confuse the mapping. set -e SELF=jigdo-gen-md5-list VER=0.3 OPT_ABSOLUTE=1 # The checksum type to produce: md5 , sha256 checksum_type=md5 hex_length=32 md5_cmd= # On FreeBSD there is "md5" rather than "md5sum". # Furthermore, the FreeBSD shell reports missing commands to inherited stderr, # regardless that the attempt itself has redirected stderr. Thus a sub shell # is needed to hide the protest. choose_checksum_cmd() { if test "$checksum_type" = "md5" then if ( md5sum --help ) >/dev/null 2>&1 then md5_cmd=md5sum elif ( md5 -s test ) >/dev/null 2>&1 then md5_cmd=md5 else echo "$0 : Programs md5sum and md5 failed to work" >&2 exit 2 fi elif test "$checksum_type" = "sha256" then if ( sha256sum --help ) >/dev/null 2>&1 then md5_cmd=sha256sum elif ( sha256 -s test ) >/dev/null 2>&1 then md5_cmd=sha256 else echo "$0 : Programs sha256sum and sha256 failed to work" >&2 exit 2 fi fi } usage() { cat << USAGE usage: $SELF [option] DIR FILE ... Print a Jigdo checksum file to stdout. One line per FILE and per file in DIR. -m, --md5 produce MD5 checksums (default) -s, --sha256 produce SHA256 checksums -a, --make-absolute make absolute paths, avoiding any symlinks (default) -l, --keep-literal leave paths untouched, literally as supplied -v, --version print version -h, --help print help -e, --examples print examples USAGE } examples() { cat << EXAMPLES examples: $SELF datadir datafile $SELF --keep-literal datadir datafile find . -type f | xargs $SELF find . -exec $SELF '{}' ';' EXAMPLES } md5list() { item="$1" if test $OPT_ABSOLUTE -eq 1; then dn=`dirname "$item"` # dirname fn=`basename "$item"` # filename od=`pwd -P` # old dir cd "$dn" || exit 1 item=`pwd -P`/"$fn" # absolute physical file path, avoiding all symlinks cd "$od" || exit 1 fi if test "$md5_cmd" = "md5sum" then MD5=`md5sum "$item" | awk '{print $1}'` elif test "$md5_cmd" = "md5" then MD5=`md5 -q "$item"` elif test "$md5_cmd" = "sha256sum" then MD5=`sha256sum "$item" | awk '{print $1}'` elif test "$md5_cmd" = "sha256" then MD5=`sha256 -q "$item"` else echo "$0 : Internal error : Checksum mode unknown : $md5_cmd" >&2 exit 2 fi SIZ=`ls -ld "$item" | awk '{print $5}'` printf '%'"$hex_length"'s %12s %s\n' "$MD5" "$SIZ" "$item" } walkdir() { DR="$1" for item in `find "$DR" -type f` do md5list "$item" done } # main() if test "$1" = "" ; then usage exit 1 fi for i in "$@" do case "$i" in --md5|-m) checksum_type=md5 hex_length=32 ;; --sha256|-s) checksum_type=sha256 hex_length=64 ;; --make-absolute|-a) OPT_ABSOLUTE=1; shift; ;; --keep-literal|-l) OPT_ABSOLUTE=0; shift; ;; --version|-v) printf '%s %s\n' "$SELF" "$VER" exit 0 ;; --help|-h) usage exit 0 ;; --examples|-e) examples exit 0 # *) # usage # exit 1 # ;; esac done choose_checksum_cmd for i in "$@" do if echo "$i" | grep '^-' >/dev/null ; then dummy=dummy elif test -d "$i" ; then DR="$i" if test $OPT_ABSOLUTE -eq 1; then od=`pwd -P` # old dir cd "$DR" || exit 1 DR=`pwd -P` # absolute physical dir path, avoiding all symlinks cd "$od" || exit 1 fi walkdir "$DR" elif test -f "$i" ; then FL="$i" md5list "$FL" else usage exit 1 fi; done exit 0 .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.38.2. .TH JIGDO-GEN-MD5-LIST "1" "October 2010" "jigdo-gen-md5-list 0.1" "User Commands" .SH NAME jigdo-gen-md5-list \- create a list of MD5sums encoded in hexadecimal format and print to standard output .SH DESCRIPTION usage: jigdo\-gen\-md5\-list [option] DIR FILE ... .TP \fB\-a\fR, \fB\-\-make\-absolute\fR make absolute paths, avoiding any symlinks (default) .TP \fB\-l\fR, \fB\-\-keep\-literal\fR leave paths untouched, literally as supplied .TP \fB\-v\fR, \fB\-\-version\fR print version .TP \fB\-h\fR, \fB\-\-help\fR print help .TP \fB\-e\fR, \fB\-\-examples\fR print examples .SH FORMAT File format is described in the beginning of the script itself. .SH "SEE ALSO" xorriso(1), jigdo-lite(1), jigit-mkimage(1) .SH AUTHOR Written by George Danchev <danchev@spnet.net> and Thomas Schmitt <scdbackup@gmx.net> #!/bin/bash # Copyright 2011 George Danchev <danchev@spnet.net> # Copyright 2011 Thomas Schmitt <scdbackup@gmx.net> # # Licensed under GNU GPL version 2 or later set -e # set -x print_specific_help() { cat << HLP Specific options: --dev path use path as drive address. Default: /dev/cdrw --what path use path as address of the directory which shall be copied into an ISO 9660 filesystem on media. --any_media allow non re-usable MMC media, like CD-R or DVD+R. Allow paths to non-existing files, but disallow paths to existing regular files. --priv_cmd 'command [arg [arg ...]]' With drive operations execute xorriso as argument of the given command (e.g. pfexec, sudo) with the optionally given arguments: command arg arg xorriso ... Command and arguments must be single words. Overview: Test burning to re-usable media CD-RW, DVD-RW, DVD-RAM, DVD+RW, BD-RE. By default, one-time usable media will be rejected deliberately. HLP } wait_for_dev () { # $1 = device address timeout=30 counter=0 while test "$counter" -lt "$timeout" do if test -e "$1" then echo return 0 fi if test "$counter" = 0 then echo echo "Not existing: $dev" echo "Will wait up to $timeout seconds for it to appear." fi counter=$(expr $counter + 1) echo -n "$counter " sleep 1 done echo return 1 } getopts_inc=inc/releng_getopts.inc if test -e "$getopts_inc" then . "$getopts_inc" if test "$SPECIFIC_HELP" = 1 then print_specific_help exit 0 fi else echo >&2 echo "File not found: $getopts_inc" >&2 echo "Are we in the ./releng directory of a libisoburn SVN checkout ?" >&2 echo "(Please execute the tests from that ./releng directory.)" >&2 echo >&2 exit 29 fi # Set default values for specific option variables. dev=/dev/cdrw what=../xorriso any_media=0 priv_cmd= # Interpret specific options, they begin after the first --. next_is=ignore for i in "$@" do if test "$next_is" = "ignore" then if test "$i" = "--" then next_is="" fi elif test "$next_is" = "dev" then dev="$i" next_is="" elif test "$next_is" = "what" then what="$i" next_is="" elif test "$next_is" = "priv_cmd" then priv_cmd="$i" next_is="" elif test "$i" = "--dev" then next_is="dev" elif test "$i" = "--what" then next_is="what" elif test "$i" = "--any_media" then any_media=1 elif test "$i" = "--priv_cmd" then next_is="priv_cmd" else echo >&2 echo "Unknown test specific option: $i" >&2 print_help print_specific_help exit 31 fi done check_for_xorriso -x # check data dir, if any and after checking -x xorriso if [ -d "${GEN_DATA_DIR}" ]; then printf "\n${SELF}: directory %s exists!" ${GEN_DATA_DIR} printf "\n${SELF}: use '${SELF} -c' to remove.\n" exit 30 else mkdir "${GEN_DATA_DIR}" fi ##################################################################### # Inspect drive address if test -e "$dev" then if test "$any_media" = 1 -a -f "$dev" then echo "FAIL : ${SELF} : --dev $dev leads to an existing regular file" echo cleanup exit 31 fi else if test "$any_media" = "0" then echo "FAIL : ${SELF} : --dev $dev does not lead to an existing file" echo cleanup exit 31 fi fi # Inspect media set +e wait_for_dev "$dev" res=$(${priv_cmd} "$RELENG_XORRISO" -outdev "$dev" 2>&1) ret=$? set -e if test "$ret" -ne 0 then echo "$res" >&2 echo "FAIL : ${SELF} : Non-zero exit value $ret with: ${priv_cmd} $RELENG_XORRISO -outdev $dev" echo cleanup exit 1 elif echo "$res" | grep '^Media current:' >/dev/null 2>&1 then media=$(echo "$res" | grep '^Media current:' | \ sed -e 's/^Media current: //') echo "Detected media: '$media'" if test "$media" = "CD-RW" -o "$media" = "DVD-RW sequential recording" -o \ "$media" = "DVD-RW restricted overwrite" -o "$media" = "DVD-RAM" -o \ "$media" = "DVD+RW" -o "$media" = "BD-RE" then echo "Recognized as re-usable." elif test "$media" = "is not recognizable" then echo "FAIL : ${SELF} : No recognizable media detected in: '$dev'" echo cleanup exit 2 elif test "$any_media" = 1 then echo "Accepted media only because of option --any_media : '$media'" else echo "FAIL : ${SELF} : No re-usable media detected, but: '$media'" echo cleanup exit 2 fi fi # Perform burn run echo ${priv_cmd} "$RELENG_XORRISO" -for_backup -outdev "$dev" -blank as_needed -map "$what" /test set +e wait_for_dev "$dev" ${priv_cmd} "$RELENG_XORRISO" \ -for_backup \ -outdev "$dev" \ -blank as_needed \ -map "$what" /test ret=$? set -e if test "$ret" -ne 0 then echo "FAIL : ${SELF} : Non-zero exit value with burn run: $ret" echo cleanup exit 1 fi if test "$SIMULATE_FAILURE" = 1 then echo "FAIL : ${SELF} : Simulated failure caused by option -f" if test -f "$dev" then # Alter image dd if=/dev/urandom bs=2K count=1 \ of="$dev" conv=notrunc seek=400 else cleanup exit 1 fi fi # Check read echo ${priv_cmd} "$RELENG_XORRISO" -for_backup -indev "$dev" \ -check_media event=FATAL -- \ -check_md5_r FATAL / -- set +e wait_for_dev "$dev" ${priv_cmd} "$RELENG_XORRISO" \ -for_backup \ -indev "$dev" \ -print '---check_media:' -check_media event=FATAL -- \ -print '---check_md5_r:' -check_md5_r FATAL / -- \ -print '---compare_r:' -md5 off -compare_r "$what" /test ret=$? set -e if test "$ret" -ne 0 then echo "FAIL : ${SELF} : Non-zero exit value with checkread run: $ret" echo cleanup exit 1 fi echo "Ok. Burn test passed." echo cleanup exit 0 #!/bin/bash # Copyright 2011 George Danchev <danchev@spnet.net> # Copyright 2011 Thomas Schmitt <scdbackup@gmx.net> # Licensed under GNU GPL version 2 or later # set -e print_specific_help() { cat << HLP Specific options: --dev path Suppress dialog and use path as drive address. --priv_cmd 'command [arg [arg ...]]' With drive operations execute xorriso as argument of the given command (e.g. pfexec, sudo) with the optionally given arguments: command arg arg xorriso ... Command and arguments must be single words. Overview: Test device scanning and list of speeds. HLP } # Include common bits and interpret general options getopts_inc=inc/releng_getopts.inc if test -e "$getopts_inc" then . "$getopts_inc" if test "$SPECIFIC_HELP" = 1 then print_specific_help exit 0 fi else echo >&2 echo "File not found: $getopts_inc" >&2 echo "Are we in the ./releng directory of a libisoburn SVN checkout ?" >&2 echo "(Please execute the tests from that ./releng directory.)" >&2 echo >&2 exit 2 fi # Interpret private options, they begin after the first --. dev= priv_cmd= next_is=ignore for i in "$@" do if test "$next_is" = "ignore" then if test "$i" = "--" then next_is="" fi elif test "$next_is" = "dev" then dev="$i" next_is="" elif test "$next_is" = "priv_cmd" then priv_cmd="$i" next_is="" elif test "$i" = "--dev" then next_is="dev" elif test "$i" = "--priv_cmd" then next_is="priv_cmd" else echo >&2 echo "Unknown test specific option: $i" >&2 print_help print_specific_help exit 31 fi done # Insist in having a xorriso check_for_xorriso -x has_device_links=$("$RELENG_XORRISO" -help 2>/dev/null | fgrep ' -device_links') if test -n "$has_device_links" then devices_opt="-device_links" else devices_opt="-devices" fi # get_speeds() { echo -e "\n${SELF}: Running: ${priv_cmd} ${RELENG_XORRISO} -report_about WARNING -outdev ${1} -toc -list_formats -list_profiles out -list_speeds" ${priv_cmd} "$RELENG_XORRISO" -report_about WARNING -outdev "$1" \ -print '---toc :' -toc \ -print '---list_formats :' -list_formats \ -print '---list_profiles :' -list_profiles out \ -print '---list_speeds :' -list_speeds } cat_var() { # $1 = variable to put out with line feeds cat <<+ $1 + } get_devices() { # $1 = if not empty: device lines from xorriso -devices or -device_links # $2 = if not empty: suppress dialog and use $2 as input if test -n "$1" then DEVICES="$1" else DEVICES=$( ${priv_cmd} "$RELENG_XORRISO" $devices_opt 2>/dev/null | grep "\-dev") fi NUM_DEV=$(cat_var "$DEVICES" | wc -l) case "${NUM_DEV}" in 0) echo -e "\n${SELF}: No drives found." exit 1 ;; 1) echo -e "\n${SELF}: 1 drive found:\n" ;; *) echo -e "\n${SELF}: ${NUM_DEV} drives found:\n" ;; esac echo ================================================================= echo "$DEVICES" echo ================================================================= OUTDEV=$( cat_var "$DEVICES" | head -1 | \ sed -e "s/[0-9] *-dev '\//\//" -e "s/'.*$//" ) if test -n "$2" then x="$2" else echo >&2 echo "WARNING: The following tests might pull in the drive tray." >&2 echo " Best is if you now put in a suitable media and" >&2 echo " load it manually, so nobody gets surprised. :))" >&2 echo >&2 echo "Which drive to examine ? (Empty input = ${OUTDEV})" >&2 read x fi if test -n "$x" then OUTDEV="$x" fi get_speeds "$OUTDEV" } # main "$RELENG_XORRISO" -version echo -e "\n${SELF}: Running: $RELENG_XORRISO $devices_opt ..." devices=$( ${priv_cmd} "$RELENG_XORRISO" -report_about WARNING $devices_opt | grep "\-dev") RET="$?" if test "$SIMULATE_FAILURE" = 1 then echo "===" >&2 echo "=== SIMULATING FAILURE BY OVERRIDING EXIT VALUE OF XORRISO" >&2 echo "===" >&2 echo "FAIL : ${SELF} : Simulated failure caused by option -f" RET=1 fi case ${RET} in 0) get_devices "$devices" "$dev" RET="$?" if test "$RET" = 0 then : else echo "FAIL : ${SELF} : Device scan or single drive listing failed" exit "$RET" fi ;; *) boldify echo -ne "\n${SELF}: ${priv_cmd} ${RELENG_XORRISO} $devices_opt returned ${RET}." unboldify echo -e "\n${SELF}: Already mounted?" df -kh exit 1 esac exit 0 #!/bin/bash # Copyright 2011, 2019 Thomas Schmitt <scdbackup@gmx.net> # Copyright 2011 George Danchev <danchev@spnet.net> # Licensed under GNU GPL version 2 or later set -e not_in_releng_exit() { printf "\nPlease execute the tests from releng directory.\n\n" exit 1 } . inc/releng_getopts.inc || not_in_releng_exit print_specific_help() { cat << HLP Specific options: --md5 use MD5 checksums (default) --sha256 use SHA256 checksums Overview: Match the resulting ISO image representation against the jigdo representation. HLP } if test "$SPECIFIC_HELP" = 1; then print_specific_help exit 0 fi # Set default values for specific option variables. checksum_type=md5 # Interpret specific options, they begin after the first --. next_is=ignore for i in "$@" do if test "$next_is" = "ignore" then if test "$i" = "--" then next_is="" fi elif test "$i" = "--md5" then checksum_type=md5 elif test "$i" = "--sha256" then checksum_type=sha256 else echo >&2 echo "Unknown test specific option: $i" >&2 print_help print_specific_help exit 31 fi done if [ ! -x $RELENG_XORRISO ]; then print_help printf "\n${SELF}: -x absolute or relative path to binary to be run.\n\n" exit 30 fi # check data dir, if any and after checking -x xorriso if [ -d "${GEN_DATA_DIR}" ]; then printf "\n${SELF}: directory %s exists!" ${GEN_DATA_DIR} printf "\n${SELF}: use './${SELF} -c' to remove.\n" exit 1 else mkdir "${GEN_DATA_DIR}" fi TMP_DATA_DIR=releng_generated_data IMG_EXTRACT_DIR=${GEN_DATA_DIR}/${SELF}_extracted_tree RELENG_DIR="${IMG_EXTRACT_DIR}" RELENG_ISOLINUX_BIN="isolinux/isolinux.bin" RELENG_BOOT_CAT="isolinux/boot.cat" RELENG_IMG=t1 RES="" REMOTE_URL="http://cdimage.debian.org/cdimage/daily-builds/daily/arch-latest/i386/iso-cd" REMOTE_IMG="debian-testing-i386-netinst.iso" # check for required items if [ "${RELENG_XORRISO}" = "" -o "${RELENG_DIR}" = "" -o "${RELENG_IMG}" = "" ]; then echo -e "\n${SELF}: xorriso_cmd IN_dir and OUT_image are required\n" exit 2 fi # All must be set at this point printf "${SELF}: Config items:" printf "\n\txorriso_cmd=${RELENG_XORRISO}\n\tIN_dir=${RELENG_DIR}\n\tOUT_image=${RELENG_IMG}.iso" printf "\n\tIN_isolinux=${RELENG_ISOLINUX_BIN}\n\tOUT_bootcat=${RELENG_BOOT_CAT}\n" RES="${RELENG_IMG}.iso ${RELENG_IMG}.new ${RELENG_IMG}.md5 ${RELENG_IMG}.jigdo ${RELENG_IMG}.template" # xorriso version details, incl. underlying libraries # "${RELENG_XORRISO}" -version # check whether the binary support JTE set +e RETSTR_VER_JTE=`"${RELENG_XORRISO}" --version 2>/dev/null | grep "libjte * in use"` RETCODE_VER_JTE="$?" set -e case ${RETCODE_VER_JTE} in 0) printf "\n${SELF}: Found JTE support with ${RELENG_XORRISO} : ${RETSTR_VER_JTE}" ;; *) printf "\nFAIL : ${SELF} : Not found JTE support in ${RELENG_XORRISO}. Quit." printf "\n${SELF}: JTE not supported with this xorriso build. Install jigit >=1.18 and rebuild." printf "\n${SELF}: http://www.einval.com/~steve/software/JTE/\n" cleanup exit 4 ;; esac # grab remote ISO image, to decompose if [ -L "${TMP_DATA_DIR}"/"${REMOTE_IMG}" ]; then printf "\n${SELF}: Found symbolic link ${TMP_DATA_DIR}"/"${REMOTE_IMG}\n" ls -ld ${TMP_DATA_DIR}"/"${REMOTE_IMG} elif [ ! -f "${TMP_DATA_DIR}"/"${REMOTE_IMG}" ]; then printf "\n${SELF}: Downloading ${REMOTE_URL}/${REMOTE_IMG}\n" if wget -V >/dev/null 2>&1 then set +e wget --no-check-certificate -T20 -t3 \ -O "${TMP_DATA_DIR}"/"${REMOTE_IMG}" "${REMOTE_URL}"/"${REMOTE_IMG}" WGET_RET="$?" set -e elif fetch -T 20 -o "${TMP_DATA_DIR}"/"${REMOTE_IMG}" \ "${REMOTE_URL}"/"${REMOTE_IMG}" then WGET_RET=0 else echo echo "FAIL: ${SELF} : Neither wget nor fetch are present and willing to work" cleanup exit 10 fi case ${WGET_RET} in 0) echo -e "\n${SELF}: Downloading successfully completed.\n" ;; *) echo -e "\nFAIL : ${SELF} : wget returned code: $WGET_RET\n" rm "${TMP_DATA_DIR}"/"${REMOTE_IMG}" cleanup exit 5 ;; esac else printf "\n${SELF}: Found ISO image: ${TMP_DATA_DIR}/${REMOTE_IMG}\n" fi # check for extraction directory existence if [ -d "${IMG_EXTRACT_DIR}" ]; then printf "\n${SELF}: Found ${IMG_EXTRACT_DIR}. Please cleanup.\n" cleanup exit 6 else mkdir "${IMG_EXTRACT_DIR}" fi # extract image content CMD_EXTRACT="${RELENG_XORRISO} -indev ${TMP_DATA_DIR}/${REMOTE_IMG} \ -osirrox on:auto_chmod_on \ -extract / ${IMG_EXTRACT_DIR} \ " # TODO: drop set +e|-e block, catch exit code instead when # the boot catalog warnings get completely resolved. echo -e "${SELF}: Extracting ISO image:\n${CMD_EXTRACT}\n" set +e ${CMD_EXTRACT} set -e # grab an MBR ISOHYBRID_MBR="${GEN_DATA_DIR}/isohybrid.mbr" dd if="${TMP_DATA_DIR}/${REMOTE_IMG}" bs=1K count=32 of="${ISOHYBRID_MBR}" # create FAT partition APPEND_PART="${GEN_DATA_DIR}/fatpart.fat" MKFS_MSDOS="/sbin/mkfs.msdos" if [ -x "${MKFS_MSDOS}" ]; then "${MKFS_MSDOS}" -n Bla -C "${APPEND_PART}" 8192 APPEND_PART_CMD="-append_partition 2 0x01 ${APPEND_PART}" else APPEND_PART_CMD= # printf "\nFAIL : ${SELF} : Not found: ${MKFS_MSDOS}" # printf "\n${SELF}: Not found: "${MKFS_MSDOS}". Install dosfstools." # printf "\n${SELF}: http://www.daniel-baumann.ch/software/dosfstools/\n" # cleanup # exit 7 fi # GENERAL section CMD="${RELENG_XORRISO} \ -as mkisofs \ -quiet \ -o ${GEN_DATA_DIR}/${RELENG_IMG}.iso \ -R \ -V ISOJIGDO \ -partition_offset 16 \ -J -joliet-long \ " # BOOT section if [ -f "${IMG_EXTRACT_DIR}"/"${RELENG_ISOLINUX_BIN}" -a -f "${ISOHYBRID_MBR}" ] then CMD="$CMD \ -b ${RELENG_ISOLINUX_BIN} \ -c ${RELENG_BOOT_CAT} \ -no-emul-boot -boot-load-size 4 -boot-info-table \ -isohybrid-mbr ${ISOHYBRID_MBR} \ -partition_offset 16 \ " else printf "\n${SELF}: FAIL to compose the boot section.\n" cleanup exit 8 fi if [ -n "${APPEND_PART_CMD}" -a -f "${APPEND_PART}" ]; then CMD="$CMD \ ${APPEND_PART_CMD} " fi # JIGDO section JIGDO_JIGDO=${GEN_DATA_DIR}/${RELENG_IMG}.jigdo JIGDO_TEMPLATE=${GEN_DATA_DIR}/${RELENG_IMG}.template JIGDO_MAP_RHV=$(cd ${RELENG_DIR} 2>/dev/null && pwd) JIGDO_MAP="Debian=${JIGDO_MAP_RHV}/" # create jigdo MD5 list in base64 format JIGDO_GEN_MD5=${GEN_DATA_DIR}/${RELENG_IMG}.md5 printf "${SELF}: Creating $checksum_type list in hex format in ${JIGDO_GEN_MD5}..." set +e ./jigdo-gen-md5-list "--""$checksum_type" ${RELENG_DIR} > ${JIGDO_GEN_MD5} ret=$? set -e if test "$ret" = 0 then printf "Done.\n" else printf "\nFAIL : ${SELF}: ./jigdo-gen-md5-list returns $ret" cleanup exit 9 fi CMD="$CMD \ -jigdo-template-compress gzip \ -jigdo-checksum-algorithm "$checksum_type" \ -checksum_algorithm_iso md5,sha1,sha256,sha512 \ -checksum_algorithm_template md5,sha1,sha256,sha512 \ -jigdo-jigdo ${JIGDO_JIGDO} \ -jigdo-template ${JIGDO_TEMPLATE} \ -jigdo-map ${JIGDO_MAP} \ -checksum-list ${JIGDO_GEN_MD5} \ -jigdo-min-file-size 1024 \ " CMD="$CMD ${RELENG_DIR}" # Run the whole compound command echo -e "${SELF}: Creating ISO and jigdo representations:\n$CMD\n" ${CMD} # Create another imange this time from jigdo files if which jigit-mkimage >/dev/null 2>&1; then printf "${SELF}: Creating new ISO from jigdo files..." jigit-mkimage \ -t ${JIGDO_TEMPLATE} \ -j ${JIGDO_JIGDO} \ -m ${JIGDO_MAP} \ -o ${GEN_DATA_DIR}/${RELENG_IMG}.new printf "Done.\n" else printf "\n${SELF}: Not found: jigit-mkimage. Install jigit." printf "\n${SELF}: http://www.einval.com/~steve/software/JTE/\n" cleanup exit 10 fi # trap the exit code of diff and let the Universe explode diff ${GEN_DATA_DIR}/${RELENG_IMG}.iso ${GEN_DATA_DIR}/${RELENG_IMG}.new DIFF_RET="$?" case ${DIFF_RET} in 0) echo -e "${SELF}: Match: diff ${GEN_DATA_DIR}/${RELENG_IMG}.iso ${GEN_DATA_DIR}/${RELENG_IMG}.new" ;; *) echo -e "FAIL : ${SELF} : diff returned code: $DIFF_RET\n" ;; esac # sort out the cruft cleanup # warn about downloaded image left behind if [ -f "${TMP_DATA_DIR}"/"${REMOTE_IMG}" ]; then printf "${SELF}: Leaving " ls -sh "${TMP_DATA_DIR}"/"${REMOTE_IMG}" fi # last hints if [ -d ${GEN_DATA_DIR} ]; then printf "\n${SELF}: HINT: manual checks remained to be done:\n" printf " * ${GEN_DATA_DIR}/${RELENG_IMG}.iso boots from USB stick and/or optical media.\n" printf " * appended FAT partition is mountable.\n" printf " * xorriso -indev ${GEN_DATA_DIR}/${RELENG_IMG}.iso -pvd_info\n" printf " * fdisk -lu ${GEN_DATA_DIR}/${RELENG_IMG}.iso\n" fi exit 0 #!/bin/bash # Copyright 2011 George Danchev <danchev@spnet.net> # Copyright 2011 - 2014 Thomas Schmitt <scdbackup@gmx.net> # Licensed under GNU GPL version 2 or later set -e export RELENG_SCRIPT_RUN_BY_RUN_ALL_AUTO=1 SELF=$(basename "$0") GEN_DATA_DIR=releng_generated_data CLOG=${GEN_DATA_DIR}/log.${SELF} CLOG_PREV=${CLOG}.prev PASSED_OPTIONS="$@" RELENG_XORRISO= CLEANUP_LOG=0 # It is not a good idea to include inc/releng_getopts.inc with the # master script as it calls the subordinate scripts and they include # this file too, and we want to avoid sharing variable with subshells if [ ! -f inc/releng_getopts.inc ]; then printf "\nPlease execute the tests from releng directory.\n\n" exit 1 fi # To catch the exit value of a command in a pipe return_value_file="$GEN_DATA_DIR"/run_all_"$$"_return_value return_wrapper() { cmd="$1" shift 1 "$cmd" "$@" RET="$?" echo "$RET" >"$return_value_file" return "$RET" } # Using only bash builtin commands. # On 4 year old amd64 x2 3000 MHz, xterm local,it counts 22471 lines per second # On 2 year old amd64 x4 2600 MHz, ssh remote, it counts 35348 lines per second count_lines() { # $1 if not empty: start count line= if test -n "$1" then count="$1" else count=0 fi while read line do count=$(($count + 1)) printf "\r %4d lines logged ... " "$count" >&2 printf "%s\n" "$line" done return 0 } ############################################# # copied from releng/inc/releng_getopts.inc which is not included here. boldify() { if which tput >/dev/null 2>&1 then tput smso || dummy_variable=1 fi } unboldify() { if which tput >/dev/null 2>&1 then tput rmso || dummy_variable=1 fi } ############################################# print_usage() { cat << HLP ${SELF} runs executables from releng directory starting with auto_*, and passing them its own options. stdout/stderr output is stored in: ./${CLOG} (last run) and ./${CLOG_PREV} (previous run) Usage: ${SELF} -x path/to/xorriso [-k] [-c] [-h] -x absolute or relative path to xorriso binary to be run. -k keep self-generated data in ./${GEN_DATA_DIR}. -c cleanup self-generated data kept from previous run and exit. -h print this help text Examples: # run xorriso and keep the self-generated data $ ./${SELF} -x path/to/xorriso -k # clean up self-generated data from previous run $ ./${SELF} -c HLP } ############################################# if [ ! "${1}" ]; then print_usage exit 0 fi next_is= for i in "$@" do if test x"$i" = x"-h" -o x"$i" = x"--h" -o x"$i" = x"-help" -o x"$i" = x"--help" then : print_usage exit 0 fi if test "$next_is" = "ignore" then : elif test "$next_is" = "x" then RELENG_XORRISO="$i" next_is= elif test x"$i" = x"-x" then next_is="x" elif test x"$i" = x"-c" then CLEANUP_LOG=1 fi done ############################################# if test "$next_is" = x then echo echo "Option -x expects an argument (the path to the xorriso program)" exit 31 fi ######################################################## if [ -f "${CLOG}" ]; then mv "${CLOG}" "${CLOG_PREV}" fi > ${CLOG} if [ -x "${RELENG_XORRISO}" ]; then echo "_OVERVIEW_______________________________________________________________" >> ${CLOG} date -u >> ${CLOG} ${RELENG_XORRISO} --version >> ${CLOG} echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" >> ${CLOG} fi DSTART=`date -u` echo "${SELF}: Started at ${DSTART}" | tee -a ${CLOG} E1=`date '+%s'` exit_value=0 # require ^auto_, avoid running (your)self explicitly for s in `ls | grep ^auto_ | grep -v ${SELF} | sort -n`; do if [ -x ${s} -a ! -d ${s} ]; then echo >> ${CLOG} echo >> ${CLOG} echo "_STARTING_TEST_________________________________________________________" >> ${CLOG} echo "${SELF}: Running ./${s} ${PASSED_OPTIONS} :" \ | tee -a ${CLOG} T1=`date '+%s'` set +e return_wrapper ./${s} ${PASSED_OPTIONS} 2>&1 | count_lines >> ${CLOG} RET=$(cat "$return_value_file") rm "$return_value_file" # echo "RET='$RET'" >/dev/tty T2=`date '+%s'` TS=`expr ${T2} - ${T1}` case ${RET} in 0) echo "done in ${TS} sec. ok." ;; *) exit_value=2 printf "done in ${TS} sec. " boldify printf "FAIL -> EXIT CODE $RET" unboldify echo ;; esac set -e fi done DEND=`date -u` echo | tee -a ${CLOG} echo -n "${SELF}: Stopped at ${DEND}." | tee -a ${CLOG} if [ "${CLEANUP_LOG}" -eq 1 ]; then if [ -f "${CLOG}" ]; then rm -f "${CLOG}" echo # | tee -a ${CLOG} echo -n "${SELF}: Removed my own log ${CLOG}." # | tee -a ${CLOG} fi if [ -f "${CLOG_PREV}" ]; then rm -f "${CLOG_PREV}" echo # | tee -a ${CLOG} echo "${SELF}: Removed my own log ${CLOG_PREV}." # | tee -a ${CLOG} fi else E2=`date '+%s'` if [ ${E2} -eq ${E1} ]; then echo " Total elapsed 0 sec." | tee -a ${CLOG} else ES=`expr ${E2} - ${E1}` echo " Total elapsed ${ES} sec." | tee -a ${CLOG} fi ##### echo >> ${CLOG} echo "_SUMMARY________________________________________________________________" >> ${CLOG} echo "${SELF}: Trivial log examination: ${CLOG}" | tee -a ${CLOG} echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" | tee -a ${CLOG} # severity classes of libdax_msgs.h in libburn and libisofs # List of boring keywords: # 'UPDATE|NOTE|DEBUG|ALL' - not considered interesting for lazy log inspection. # List of interesting keywords: # thrown by xorriso and underlying libraries LIST_KWD="NEVER|ABORT|FATAL|FAILURE|MISHAP|SORRY|WARNING|HINT" # thrown by others LIST_KWD="${LIST_KWD}|FAIL|ERROR|WRONG" if [ -f "${CLOG}" ]; then set +e # lines, perl regex, leading tabs grep -n -E "${LIST_KWD}" "${CLOG}" RET_GREP="$?" ok=0 case ${RET_GREP} in 0) # found ;; 1) # not found echo "${SELF}: Log file looks clear." # | tee -a ${CLOG} ok=1 ;; *) # echo "${SELF}: grep returned EXIT CODE: ${RET_GREP}." # | tee -a ${CLOG} ;; esac if test "$ok" = 0 && test "$exit_value" = 0 then exit_value=1 fi set -e fi echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" | tee -a ${CLOG} ##### TODO: work out a less noisy diff'ing technique! if [ -f "${CLOG_PREV}" -a -f "${CLOG}" ]; then echo "${SELF}: See diff against previous log file (might be long):" | tee -a ${CLOG} echo "diff -Naur ${CLOG_PREV} ${CLOG} | less" | tee -a ${CLOG} fi fi # boldify echo # | tee -a ${CLOG} echo "${SELF}: Leaving the following cruft in ${GEN_DATA_DIR}:" # | tee -a ${CLOG} unboldify ls -lth "${GEN_DATA_DIR}" # | tee -a ${CLOG} # Fin if test "$exit_value" = 0 then echo "${SELF}: +++ Test run and its log look like success." else echo echo "${SELF}: --- Test run detected some failures." echo fi exit $exit_value #!/bin/bash # Copyright 2011 George Danchev <danchev@spnet.net> # Copyright 2011 Thomas Schmitt <scdbackup@gmx.net> # === TEMPLATE: Add your own copyright here # # Licensed under GNU GPL version 2 or later # === TEMPLATE: Remove this remark before releasing this script. # # This is a template for creating a new libisoburn/releng test. # It is supposed that you have read releng/README before you begin to work # here. # # Step 1: Invent a name for your test # test_name="manual_"...some.name... # or # test_name="auto_"...some.name... # # Step 2: Copy releng/template_new to $test_name # # Step 3: Edit $test_name and process any line that begins by # "# === TEMPLATE:". Do what the line prescribes and then remove it # from the script. You are not done as long as such a line remains. # # === TEMPLATE: End of remark to remove set -e # === TEMPLATE: Describe your own specific options (if any) and the test print_specific_help() { cat << HLP Specific options: --option Explanation of specific option Overview: Short explanation of test purpose and activities. HLP } getopts_inc=inc/releng_getopts.inc if test -e "$getopts_inc" then . "$getopts_inc" if test "$SPECIFIC_HELP" = 1 then print_specific_help exit 0 fi else echo >&2 echo "File not found: $getopts_inc" >&2 echo "Are we in the ./releng directory of a libisoburn SVN checkout ?" >&2 echo "(Please execute the tests from that ./releng directory.)" >&2 echo >&2 exit 29 fi # === TEMPLATE: Decide whether the test will have own options, # === TEMPLATE: apart from those interpreted by inc/releng_getopts.inc # === TEMPLATE: If not, then remove this interpreter code. # Set default values for specific option variables. E.g.: # dev= # Interpret specific options, they begin after the first --. next_is=ignore for i in "$@" do if test "$next_is" = "ignore" then if test "$i" = "--" then next_is="" fi # === TEMPLATE: Implement interpretation of specific options. Like: # elif test "$next_is" = "dev" # then # dev="$i" # next_is="" # elif test "$i" = "--dev" # then # next_is="dev" else echo >&2 echo "Unknown test specific option: $i" >&2 print_help print_specific_help exit 31 fi done # === TEMPLATE: End of own option interpreter code. # Each test should decide whether or not it needs # a xorriso binary to test, since some do compilations only. # === TEMPLATE: Decide whether you need a xorriso program. # === TEMPLATE: If not, then remove this function call check_for_xorriso -x # check data dir, if any and after checking -x xorriso # === TEMPLATE: Decide whether your test will possibly create own files. # === TEMPLATE: If yes, then create your files underneath ${GEN_DATA_DIR}. # === TEMPLATE: The name in this variable is set by inc/releng_getopts.inc . # === TEMPLATE: If not, then remove this if ... fi statement. if [ -d "${GEN_DATA_DIR}" ]; then printf "\n${SELF}: directory %s exists!" ${GEN_DATA_DIR} printf "\n${SELF}: use '${SELF} -c' to remove.\n" exit 30 else mkdir "${GEN_DATA_DIR}" fi ##################################################################### # === TEMPLATE: Perform your test activities here. # === TEMPLATE: In case of failure, issue a line to stdout that begins by # === TEMPLATE: the word "FAIL", and make sure that the test script finally # === TEMPLATE: returns a non-zero exit value. # === TEMPLATE: 31 = Unknown option or unusable argument with known option # === TEMPLATE: 30 = Unexpected state of own directory for self generated files # === TEMPLATE: 29 = Not in ./releng directory or missing essential parts # === TEMPLATE: 1 to 28 = test specific exit values # === TEMPLATE: When exiting prematurely, make sure to call cleanup. cleanup exit 0 /* Compare two copies of a file object in as many aspects as i can imagine to make sense. (E.g.: comparing atime makes no sense.) To compare tree /media/dvd and /original/dir : find /media/dvd -exec compare_file '{}' /media/dvd /original/dir ';' Copyright 2008 - 2015 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. cc -g -o compare_file compare_file.c */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <string.h> #include <time.h> /* O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif /* @param flag bit0= single letters */ char *Ftypetxt(mode_t st_mode, int flag) { if(flag&1) goto single_letters; if(S_ISDIR(st_mode)) return("directory"); else if(S_ISREG(st_mode)) return("regular_file"); else if(S_ISLNK(st_mode)) return("symbolic_link"); else if(S_ISBLK(st_mode)) return("block_device"); else if(S_ISCHR(st_mode)) return("char_device"); else if(S_ISFIFO(st_mode)) return("name_pipe"); else if(S_ISSOCK(st_mode)) return("unix_socket"); return("unknown"); single_letters:; if(S_ISDIR(st_mode)) return("d"); else if(S_ISREG(st_mode)) return("-"); else if(S_ISLNK(st_mode)) return("l"); else if(S_ISBLK(st_mode)) return("b"); else if(S_ISCHR(st_mode)) return("c"); else if(S_ISFIFO(st_mode)) return("p"); else if(S_ISSOCK(st_mode)) return("s"); return("?"); } char *Ftimetxt(time_t t, char timetext[40], int flag) { char *rpt; struct tm tms, *tmpt; static char months[12][4]= { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; tmpt= localtime_r(&t, &tms); rpt= timetext; rpt[0]= 0; if(tmpt==0) sprintf(rpt+strlen(rpt), "%12.f", (double) t); else if(time(NULL)-t < 180*86400 && time(NULL)-t >= 0) sprintf(rpt+strlen(rpt), "%3s %2d %2.2d:%2.2d", months[tms.tm_mon], tms.tm_mday, tms.tm_hour, tms.tm_min); else sprintf(rpt+strlen(rpt), "%3s %2d %4.4d", months[tms.tm_mon], tms.tm_mday, 1900+tms.tm_year); return(timetext); } /* @param flag bit0= compare atime bit1= compare ctime */ int Compare_2_files(char *adr1, char *adr2, char *adrc, int flag) { struct stat s1, s2; int ret, differs= 0, r1, r2, fd1= -1, fd2= -1, i, done; char buf1[4096], buf2[4096], a[4096], ttx1[40], ttx2[40]; off_t r1count= 0, r2count= 0, diffcount= 0, first_diff= -1; double dcount; ret= lstat(adr1, &s1); if(ret==-1) { printf("? %s : cannot lstat() : %s\n", adr1, strerror(errno)); return(0); } strcpy(a, Ftypetxt(s1.st_mode, 1)); strcat(a, " "); if(adrc[0]) { if(strlen(a) + strlen(adrc) < 4096) strcat(a, adrc); } else { strcat(a, "."); } ret= lstat(adr2, &s2); if(ret==-1) { printf("? %s : cannot lstat() : %s\n", adr2, strerror(errno)); return(0); } /* Attributes */ if(s1.st_mode != s2.st_mode) { if((s1.st_mode&~S_IFMT)!=(s2.st_mode&~S_IFMT)) printf("%s : st_mode : %7.7o <> %7.7o\n", a, (unsigned int) (s1.st_mode & ~S_IFMT), (unsigned int) (s2.st_mode & ~S_IFMT)); if((s1.st_mode&S_IFMT)!=(s2.st_mode&S_IFMT)) printf("%s : type : %s <> %s\n", a, Ftypetxt(s1.st_mode, 0), Ftypetxt(s2.st_mode, 0)); differs= 1; } if(s1.st_uid != s2.st_uid) { printf("%s : st_uid : %lu <> %lu\n", a, (unsigned long) s1.st_uid, (unsigned long) s2.st_uid); differs= 1; } if(s1.st_gid != s2.st_gid) { printf("%s : st_gid : %lu <> %lu\n", a, (unsigned long) s1.st_gid, (unsigned long) s2.st_gid); differs= 1; } if((S_ISCHR(s1.st_mode) && S_ISCHR(s2.st_mode)) || (S_ISBLK(s1.st_mode) && S_ISBLK(s2.st_mode))) { if(s1.st_rdev != s2.st_rdev) { printf("%s : %s st_rdev : %lu <> %lu\n", a, (S_ISCHR(s1.st_mode) ? "S_IFCHR" : "S_IFBLK"), (unsigned long) s1.st_rdev, (unsigned long) s1.st_rdev); differs= 1; } } if(S_ISREG(s2.st_mode) && s1.st_size != s2.st_size) { printf("%s : st_size : %.f <> %.f diff= %.f\n", a, (double) s1.st_size, (double) s2.st_size, ((double) s1.st_size) - (double) s2.st_size); differs= 1; } if(s1.st_mtime != s2.st_mtime) { printf("%s : st_mtime : %s <> %s diff= %.f s\n", a, Ftimetxt(s1.st_mtime, ttx1, 0), Ftimetxt(s2.st_mtime, ttx2, 0), ((double) s1.st_mtime) - (double) s2.st_mtime); differs= 1; } if(flag&1) { if(s1.st_atime != s2.st_atime) { printf("%s : st_atime : %s <> %s diff= %.f s\n", a, Ftimetxt(s1.st_atime, ttx1, 0), Ftimetxt(s2.st_atime, ttx2, 0), ((double) s1.st_atime) - (double) s2.st_atime); differs= 1; } } if(flag&2) { if(s1.st_ctime != s2.st_ctime) { printf("%s : st_ctime : %s <> %s diff= %.f s\n", a, Ftimetxt(s1.st_ctime, ttx1, 0), Ftimetxt(s2.st_ctime, ttx2, 0), ((double) s1.st_ctime) - (double) s2.st_ctime); differs= 1; } } if(S_ISREG(s1.st_mode) && S_ISREG(s2.st_mode)) { fd1= open(adr1, O_RDONLY | O_BINARY); if(fd1==-1) { printf("- %s : cannot open() : %s\n", adr1, strerror(errno)); return(0); } fd2= open(adr2, O_RDONLY | O_BINARY); if(fd2==-1) { printf("- %s : cannot open() : %s\n", adr2, strerror(errno)); close(fd1); return(0); } /* Content */ done= 0; while(!done) { r1= read(fd1, buf1, sizeof(buf1)); r2= read(fd2, buf2, sizeof(buf2)); if((r1==EOF && r2==EOF) || (r1==0 && r2==0)) break; if(r1==EOF || r1==0) { if(r1==EOF) r1= 0; if(s1.st_size > r1count + r1) printf("- %s : early EOF after %.f bytes\n", adr1, (double) r1count); differs= 1; } r1count+= r1; if(r2==EOF || r2<r1) { if(r2==EOF) r2= 0; if(s2.st_size > r2count + r2) printf("- %s : early EOF after %.f bytes\n", adr2, (double) r2count); differs= 1; done= 1; } if(r2>r1) { if(s1.st_size > r1count + r1) printf("- %s : early EOF after %.f bytes\n", adr1, (double) r1count); differs= 1; done= 1; } r2count+= r2; if(r1>r2) r1= r2; for(i= 0; i<r1; i++) { if(buf1[i]!=buf2[i]) { if(first_diff<0) first_diff= i; diffcount++; } } } if(diffcount>0 || r1count!=r2count) { if(first_diff<0) first_diff= (r1count>r2count ? r2count : r1count); if(r1count > r2count) dcount= diffcount + (r1count - r2count); else dcount= diffcount + (r2count - r1count); printf("%s : %s : differs by at least %.f bytes. First at %.f\n", a, (s1.st_mtime==s2.st_mtime ? "CONTENT":"content"), dcount, (double) first_diff); differs= 1; } } if(fd1!=-1) close(fd1); if(fd2!=-1) close(fd2); return(!differs); } int main(int argc, char **argv) { int ret, i, with_ctime= 1; char adr1[4096], adr2[4096], adrc[4096]; if(sizeof(off_t) < 8) { fprintf(stderr, "%s : FATAL : Compile time misconfiguration. sizeof(off_t) too small.\n\n", argv[0]); exit(4); } if(argc<4) { fprintf(stderr, "usage: %s path prefix1 prefix2\n", argv[0]); exit(2); } for(i= 4; i<argc; i++) { if(strcmp(argv[i], "-no_ctime")==0) with_ctime= 0; else { fprintf(stderr, "%s : Option not recognized: '%s'\n", argv[0], argv[i]); exit(2); } } if(strncmp(argv[1], argv[2], strlen(argv[2]))!=0) { fprintf(stderr, "%s: path '%s' does not match prefix1 '%s'\n", argv[0], argv[1], argv[2]); exit(2); } if(strlen(argv[1]) >= 4096) { fprintf(stderr, "path exceeds size limit of 4095\n"); exit(3); } if(strlen(argv[1]) - strlen(argv[2]) > 4000) { fprintf(stderr, "common address part exceeds size limit of 4000\n"); exit(3); } if(strlen(argv[3]) + 1 + strlen(argv[1]) - strlen(argv[2]) >= 4096) { fprintf(stderr, "prefix2 exceeds size limit of 4095\n"); exit(3); } strcpy(adr1, argv[1]); strcpy(adrc, argv[1]+strlen(argv[2])); strcpy(adr2, argv[3]); if(adrc[0] == '/' || adrc[0] == 0) strcat(adr2, "/"); strcat(adr2, adrc); ret= Compare_2_files(adr1, adr2, adrc, (with_ctime<<1)); exit(ret<=0); } #!/bin/bash echo >&2 echo "THIS SCRIPT IS DEPRECATED ! USE ITS SUCCESSOR: merge_debian_isos" >&2 echo >&2 echo "The script merge_2_debian_isos still exists only because it was mentioned">&2 echo "in Debian bug 1011343. The successor can merge more than two ISOs.">&2 echo "So do not edit this script to remove this warning and the 'exit 7' line.">&2 echo >&2 exit 7 usage() { echo "usage: $(basename "$0") "'\' >&2 echo " boot_iso boot_mount add_iso add_mount result_iso [for_dist]" >&2 echo >&2 echo "Mounts by sudo the boot_iso at directory boot_mount and add_iso at" >&2 echo "add_mount, if not already mounted that way. Then both Debian pools" >&2 echo "and package lists get merged and a new ISO 9660 image result_iso" >&2 echo "is produced, which must not yet exist." >&2 echo "If boot_iso is bootable then the new image will be bootable by the" >&2 echo "same means." >&2 echo "This script creates the following temporary tree and files which" >&2 echo "must not yet exist in the current working directory:" >&2 echo " ./merged_dists , ./merged_md5sum.txt , ./merged_REAMDE.txt" >&2 echo " ./temp_file" >&2 echo "The optional sixth argument for_dist should only be given if" >&2 echo "this script refuses to work and proposes to give this argument." >&2 echo "Exported non-empty variable MERGE_DATE enforces a particular" >&2 echo "date string in the text which gets prepended to /README.txt ." >&2 echo "Exported non-empty variable XORRISO overrides command xorriso," >&2 echo "which may be needed if installed xorriso is older than 1.4.2." >&2 echo "Example using GNU xorriso-1.5.4 instead of /usr/bin/xorriso:" >&2 echo " export XORRISO="'$HOME'"/xorriso-1.5.4/xorriso/xorriso" >&2 echo " $(basename "$0") debian-11.2.0-amd64-DVD-1.iso /mnt/iso1 "'\' >&2 echo " debian-11.2.0-amd64-DVD-2.iso /mnt/iso2 merged.iso" >&2 } if test "$#" -lt 5 then usage exit 1 fi BOOT_ISO="$1" BOOT_ISO_MOUNT="$2" ADD_ISO="$3" ADD_ISO_MOUNT="$4" RESULT_ISO="$5" # The sixth argument is optional FOR_DIST="$6" ## Check arguments if test -e "$RESULT_ISO" then echo "--- A file '""$RESULT_ISO""' is already existing." >&2 echo "--- Will not overwrite it by the resulting ISO image." >&2 exit 1 fi if test -e merged_dists || test -e merged_md5sum.txt || test -e temp_file \ || test -e merged_README.txt then echo "--- At least one of ./merged_dists, ./merged_md5sum.txt, ./temp_file," >&2 echo "--- or merged_README.txt is already existing. Will not overwrite them." >&2 exit 1 fi if test "$BOOT_ISO" = "$RESULT_ISO" || test "$BOOT_ISO" = "$ADD_ISO" || \ test "$ADD_ISO" = "$RESULT_ISO" then echo "--- At least two of the three given ISO image paths are equal." >&2 echo "--- boot_iso and add_iso must exist and differ, result_iso must not exist." >&2 usage exit 1 fi ## Mount and copy out the files which need to be changed do_mount_1=1 x=$(mount | grep " $BOOT_ISO_MOUNT " | awk '{print $1}') if test -n "$x" then i1=$(ls -i "$x" | awk '{print $1}') i2=$(ls -i "$BOOT_ISO" | awk '{print $1}') if test "$i1" = "$i2" then do_mount_1=0 echo "Note: Found $BOOT_ISO already mounted at $BOOT_ISO_MOUNT" fi fi if test "$do_mount_1" = 1 then echo "Note: sudo mount $BOOT_ISO $BOOT_ISO_MOUNT" sudo mount "$BOOT_ISO" "$BOOT_ISO_MOUNT" || exit 3 fi cp -a "$BOOT_ISO_MOUNT/dists" merged_dists cp -a "$BOOT_ISO_MOUNT/md5sum.txt" merged_md5sum.txt chmod -R u+w merged_dists chmod u+w merged_md5sum.txt do_mount_2=1 x=$(mount | grep " $ADD_ISO_MOUNT " | awk '{print $1}') if test -n "$x" then i1=$(ls -i "$x" | awk '{print $1}') i2=$(ls -i "$ADD_ISO" | awk '{print $1}') if test "$i1" = "$i2" then do_mount_2=0 echo "Note: Found $ADD_ISO already mounted at $ADD_ISO_MOUNT" fi fi if test "$do_mount_2" = 1 then echo "Note: sudo mount $ADD_ISO $ADD_ISO_MOUNT" if sudo mount "$ADD_ISO" "$ADD_ISO_MOUNT" then dummy=dummy else if test "$do_mount_1" then sudo umount "$BOOT_ISO_MOUNT" fi exit 3 fi fi ## Helper functions # Put out the list of checksummed paths as listed in /dists/$dist/Release extract_checksum_paths() { mode=0 cat "$1" | \ while true do read x || break if test "$x" = "MD5Sum:" || test "$x" = "SHA1:" \ || test "$x" = "SHA256:" || test "$x" = "SHA512:" then if test "$mode" = 0 then mode=1 elif test "$mode" = 1 then break fi elif test "$mode" = 1 then echo "$x" fi done } # Put out the part before the first checksum field extract_release_head() { cat "$1" | \ while true do read x || break if test "$x" = "MD5Sum:" || test "$x" = "SHA1:" \ || test "$x" = "SHA256:" || test "$x" = "SHA512:" then break fi echo "$x" done } ## Determine which Debian release is on BOOT_ISO dist= for i in $(ls -1d "$BOOT_ISO_MOUNT"/dists/*) do if test -d "$i" then if test -L "$i" then continue fi test -n "$dist" && dist="$dist " dist="$dist""$(basename $i)" fi done if test -z "$dist" then if test -z "$FOR_DIST" then echo "--- Cannot determine Debian release from directories in /dists" >&2 echo "--- (You may provide the release name as sixth argument)" >&2 echo >&2 usage exit 2 fi elif test "$(echo "$dist" | wc -w)" -gt 1 then if test -z "$FOR_DIST" then echo "--- More than one Debian release found in /dists: $dist" >&2 echo "--- (You may provide the release name as sixth argument)" >&2 echo >&2 usage exit 2 fi fi if test -n "$FOR_DIST" then echo "Note: Overriding release name '$dist' by '""$FOR_DIST""'" >&2 dist="$FOR_DIST" fi if test -d "$BOOT_ISO_MOUNT"/dists/"$dist" then echo "Note: Will work along $BOOT_ISO_MOUNT"/dists/"$dist"/Release >&2 else echo "--- Cannot find directory $BOOT_ISO_MOUNT"/dists/"$dist" >&2 exit 2 fi ## Prepend info to /README.txt if test -z "$MERGE_DATE" then MERGE_DATE=$(date +'%Y%m%d-%H:%M') fi printf 'Result of a run of %s at %s\r\n' \ "$(basename $0)" "$MERGE_DATE" >temp_file printf 'Package pools and Packages lists were merged.\r\n' >>temp_file printf 'The other files stem from the first input ISO.\r\n' >>temp_file printf '\r\n' >>temp_file printf 'Input ISO: %s\r\n' "$BOOT_ISO" >>temp_file head -2 "$BOOT_ISO_MOUNT"/README.txt >>temp_file printf '\r\n' >>temp_file printf 'Input ISO: %s\r\n' "$ADD_ISO" >>temp_file head -2 "$ADD_ISO_MOUNT"/README.txt >>temp_file printf '\r\n' >>temp_file printf '%s%s\r\n' " --------------------------------------" \ "----------------------------------------" >>temp_file printf '\r\n' >>temp_file cat "$BOOT_ISO_MOUNT"/README.txt >>temp_file mv temp_file merged_README.txt ## Merge package description files with those from "$ADD_ISO" # /md5sum.txt seems to be the only overall package list cat merged_md5sum.txt "$ADD_ISO_MOUNT"/md5sum.txt | sort >temp_file mv temp_file merged_md5sum.txt # Determine the files which are mentioned with checksum in main Release files path_list=$( (extract_checksum_paths merged_dists/"$dist"/Release extract_checksum_paths "$ADD_ISO_MOUNT"/dists/"$dist"/Release ) \ | awk '{print $3}' | sort | uniq ) # Merge .gz files (Release should not be merged. Unclear what others need.) for i in $path_list do if echo "$i" | grep -v '.gz$' >/dev/null then continue fi if test -e merged_dists/"$dist"/"$i" then if test -e "$ADD_ISO_MOUNT"/dists/"$dist"/"$i" then if test "$(gunzip <merged_dists/"$dist"/"$i" | tail -1)" = "" then empty_line= else empty_line=echo fi ( gunzip <merged_dists/"$dist"/"$i" $empty_line gunzip <"$ADD_ISO_MOUNT"/dists/"$dist"/"$i" ) | gzip >temp_file mv temp_file merged_dists/"$dist"/"$i" fi elif test -e "$ADD_ISO_MOUNT"/dists/"$dist"/"$i" then if test -e $(dirname merged_dists/"$dist"/"$i") then dummy=dummy else mkdir -p $(dirname merged_dists/"$dist"/"$i") fi cp "$ADD_ISO_MOUNT"/dists/"$dist"/"$i" merged_dists/"$dist"/"$i" fi test -e temp_file && rm temp_file done ## Update dists/"$dist"/Release extract_release_head merged_dists/"$dist"/Release >temp_file # Re-create "MD5Sum:", "SHA256:", "SHA512:" sections for cmd in md5sum sha1sum sha256sum sha512sum do if type "$cmd" >/dev/null then case "$cmd" in md5sum) echo "MD5Sum:" ;; sha1sum) echo "SHA1:" ;; sha256sum) echo "SHA256:" ;; sha512sum) echo "SHA512:" ;; esac for i in $path_list do file=merged_dists/"$dist"/"$i" if test -e "$file" then sum=$("$cmd" "$file" | awk '{print $1}') size=$(stat -c '%s' "$file") elif test -e "$file".gz then sum=$(gunzip <"$file".gz | "$cmd" | awk '{print $1}') size=$(gunzip <"$file".gz | wc -c) else continue fi list_path=$(echo "$file" | sed -e 's/^merged_dists\/'"$dist"'\///') printf ' %s %8ld %s\n' "$sum" "$size" "$list_path" done fi done >>temp_file mv temp_file merged_dists/"$dist"/Release ## Produce the new ISO image if test -z "$XORRISO" then XORRISO=xorriso fi "$XORRISO" \ -indev "$BOOT_ISO" \ -outdev "$RESULT_ISO" \ -map "$ADD_ISO_MOUNT"/pool /pool \ -map merged_dists /dists \ -map merged_md5sum.txt /md5sum.txt \ -map merged_README.txt /README.txt \ -chown_r 0 /dists /md5sum.txt /README.txt -- \ -chgrp_r 0 /dists /md5sum.txt /README.txt -- \ -chmod_r a-w /dists /md5sum.txt -- \ -chmod_r a=r /README.txt -- \ -boot_image any replay \ -stdio_sync off \ -padding included \ -compliance no_emul_toc ## Clean up if test "$do_mount_1" = 1 then sudo umount "$BOOT_ISO_MOUNT" fi if test "$do_mount_2" = 1 then sudo umount "$ADD_ISO_MOUNT" fi rm -r merged_dists merged_md5sum.txt merged_README.txt #!/bin/sh # Copyright 2022-2023 Thomas Schmitt <scdbackup@gmx.net> , libburnia project. # Provided under BSD license: Use, modify, and distribute as you like. # This obtrusive setting shall make the script safe against exotic locales. export LANG=C export LC_ALL=C usage() { echo "usage: $(basename "$0") result_iso mount_template iso1 iso2 [... isoN]" >&2 echo >&2 echo "Mounts by sudo the ISO 9660 images iso1 to isoN at directories" >&2 echo "mount_template1 to mount_templateN, if not already mounted that way." >&2 echo "Then the Debian pools and package lists get merged and a new" >&2 echo "ISO 9660 image result_iso is produced. If iso1 is bootable then" >&2 echo "the new image will be bootable by the same means." >&2 echo >&2 echo "The file depicted by result_iso must not yet exist or has to be a" >&2 echo "device which is acceptable for Linux-specific helper script" >&2 echo "xorriso-dd-target. If xorriso-dd-target agrees and the user" >&2 echo 'confirms by input "yes" then xorriso will be run under sudo.' >&2 echo 'Exempted from this evaluation are addresses which begin by "mmc:"' >&2 echo 'for an optical drive on Linux, BSDs, Solaris, or by "stdio:/dev/"' >&2 echo 'for which the user takes full and dangerous responsibility.' >&2 echo 'Special result_iso path "xorriso-dd-target-plug-test" determines' >&2 echo "on Linux the target USB stick by a dialog around plugging it in." >&2 echo "xorriso will be run under sudo, if xorriso-dd-target agrees and" >&2 echo 'the user confirms by input "yes".' >&2 echo >&2 echo "At least the parent directory of mount_template must already exist" >&2 echo 'or has to be given by a plain name without "/" so that it can be' >&2 echo 'created in the current directory. (I.e. without even "./".)' >&2 echo "All arguments must be single words without using quotation marks." >&2 echo "None of the isoN must be equal to another isoM." >&2 echo >&2 echo "This script creates and finally removes the following temporary tree" >&2 echo "and files which must not yet exist in the current working directory:" >&2 echo " ./merged_dists , ./merged_md5sum.txt , ./merged_REAMDE.txt" >&2 echo " ./temp_file" >&2 echo "Further it creates and finally removes directories mount_template*" >&2 echo "if they are needed and do not exist when the script starts. If the" >&2 echo "parent directory in the mount_template path does not exist and" >&2 echo 'its path contains no "/" then it gets created and finally removed.' >&2 echo "The script depends on the following programs:" >&2 echo " awk, basename, cat, chmod, cp, date, dirname, expr, fgrep, grep," >&2 echo " gunzip, gzip, head, ls, md5sum, mkdir, mount, mv, rm, rmdir," >&2 echo " sed, sh, sha256sum, sort, stat, sudo, umount, uniq, xorriso" >&2 echo "With device writing involving helper script xorriso-dd-target:" >&2 echo " dd, Linux kernel, lsblk, sleep, tr, uname, wc, whoami," >&2 echo " xorriso-dd-target" >&2 echo "Recommended are: sha1sum, sha512sum" >&2 echo >&2 echo "Exported non-empty variable MERGE_DATE enforces a particular" >&2 echo "date string in the text which gets prepended to /README.txt ." >&2 echo "Exported non-empty variable MERGE_FOR_DIST enforces the use of a" >&2 echo "particular directory in /dists of iso1. Normally only one" >&2 echo "such directory is found and thus no need to set MERGE_FOR_DIST." >&2 echo "Exported non-empty variable MERGE_KEEP_ISO prevents the removal" >&2 echo "of result_iso after xorriso indicated failure of production." >&2 echo "Exported non-empty variable XORRISO overrides command xorriso." >&2 echo "This may be needed if installed xorriso is older than 1.4.2." >&2 echo 'If XORRISO is set to "dummy" then no new ISO will emerge.' >&2 echo "Exported non-empty variable XORRISO_DD_TARGET_PATH names the" >&2 echo "directory where to find xorriso-dd-target, which evaluates the" >&2 echo "suitability of result_iso devices or does the plug-test dialog." >&2 echo >&2 echo "Example using GNU xorriso-1.5.6 instead of /usr/bin/xorriso:" >&2 echo " export XORRISO="'"$HOME"'"/xorriso-1.5.6/xorriso/xorriso" >&2 echo " $(basename "$0") merged.iso merge_mount/iso "'\' >&2 echo " debian-11.2.0-amd64-DVD-[12345].iso" >&2 echo >&2 echo "Example writing to optical drive /dev/sr0 :" >&2 echo " $(basename "$0") mmc:/dev/sr0 merge_mount/iso "'\' >&2 echo " debian-11.2.0-amd64-DVD-[12345].iso" >&2 echo >&2 echo \ "Example on Linux writing to USB stick with xorriso-dd-target-plug-test:" >&2 echo " wget https://dev.lovelyhq.com/libburnia/libisoburn/raw/master/xorriso-dd-target/xorriso-dd-target" >&2 echo " chmod u+x xorriso-dd-target" >&2 echo ' export XORRISO_DD_TARGET_PATH="$(pwd)"' >&2 echo " $(basename "$0") xorriso-dd-target-plug-test merge_mount/iso "'\' >&2 echo " debian-11.2.0-amd64-DVD-[12345].iso" >&2 echo "This leads to the first two steps of the xorriso-dd-target device" >&2 echo "plug dialog. See: https://wiki.debian.org/XorrisoDdTarget" >&2 echo 'Finally input "yes" is required to authorize xorriso under sudo.' >&2 } check_single_word() { empty=1 for i in $1 do if test "$i" = "$1" then empty=0 else if test "$2" = "dists" then echo "WARNING: A file name in /dists is not a single word:" >&2 echo " '${1}'" >&2 else echo "--- Argument $2 is not a single word:" >&2 echo "--- '${1}'" >&2 fi return 1 fi done if test "$empty" = 1 then if test "$2" = "dists" then echo "WARNING: A file name in /dists is empty or entirely white space:" \ >&2 echo " '${1}'" >&2 else echo "--- Argument $2 is empty or entirely white space:" >&2 echo "--- '${1}'" >&2 fi return 1 fi return 0 } XORRISO_DD_TARGET=xorriso-dd-target XORRISO_STDIO_DEV= XORRISO_SUDO= XORRISO_BLANK_CMD= XORRISO_STDIO_SYNC=off confirm_xdt_device() { # $1 : path of the device file in question echo "$XORRISO_DD_TARGET agrees that this is a suitable device." >&2 echo >&2 echo \ "If you agree to xorriso running under sudo and overwriting the content" \ >&2 echo "of '${1}', then enter 'yes' :" >&2 answer= read answer if test "$answer" = yes then XORRISO_STDIO_DEV=stdio: XORRISO_SUDO=sudo XORRISO_BLANK_CMD="-blank as_needed" XORRISO_STDIO_SYNC=16m xorriso_rm_result_iso=0 echo "Will use sudo with xorriso and write to device." >&2 return 0 else echo "--- Answer was not 'yes'. Will not write to device." >&2 fi return 1 } MOUNT_LIST= UMOUNT_LIST= RMDIR_LIST= TEMPFILE_LIST= EMERGING_ISO= # Cleanup temporary files, mount points and made directories cleanup_ignore_counter=0 cleanup_ignore_handler() { cleanup_ignore_counter=$(expr "$cleanup_ignore_counter" + 1) if test "$cleanup_ignore_counter" -ge 4 then # Reset traps to default trap - INT TERM QUIT echo "--- Ignored several INT, TERM, or QUIT signals during cleanup." >&2 echo "--- Will give in to next signal." >&2 return 0 fi echo "--- Ignored INT, TERM, or QUIT signal during cleanup." >&2 return 0 } cleanup() { # Make sure to be in cleanup state of traps trap cleanup_ignore_handler INT TERM QUIT echo >&2 echo "Cleaning up temporary files and mount points ..." >&2 ret=0 for i in $TEMPFILE_LIST do if test -e "$i" then if rm -r "$i" then dummy=dummy else echo "--- Note: Cannot remove previously created temporary file: $i" >&2 ret=1 fi fi done for i in $UMOUNT_LIST do if sudo umount "$i" then dummy=dummy else echo "--- Note: Cannot unmount previously mounted $i" >&2 ret=1 fi done for i in $RMDIR_LIST do if rmdir "$i" then dummy=dummy else echo "--- Note: Cannot remove previously created directory $i" >&2 ret=1 fi done if test -n "$EMERGING_ISO" && test -e "$EMERGING_ISO" then if test -n "$MERGE_KEEP_ISO" then echo "Removal of incomplete result ISO suppressed by MERGE_KEEP_ISO." >&2 echo "Remaining: $EMERGING_ISO" >&2 else echo "Removing incomplete result ISO." >&2 echo \ " (This can be suppressed by exporting non-empty variable MERGE_KEEP_ISO.)" \ >&2 if rm "$EMERGING_ISO" then dummy=dummy else echo "--- Note: Cannot remove incomplete result: $EMERGING_ISO" >&2 ret=1 fi fi fi if test "$ret" = 0 then echo "Cleanup completed." >&2 else echo "--- Cleanup could not be fully completed." >&2 fi # Do not come back via trap again trap - INT TERM QUIT return $ret } # Handler for INT TERM QUIT events if "$1" is empty # Procedural program exit if "$1" is not empty cleanup_and_end() { # Early assume cleanup state of traps trap cleanup_ignore_handler INT TERM QUIT trap - EXIT if test -z "$1" then exit_value=6 echo >&2 echo "--- Encountered INT, TERM, or QUIT signal." >&2 else exit_value="$1" fi cleanup echo >&2 if test "$exit_value" -gt 0 then echo "--- Merge run aborted !" >&2 else echo "Merge run ended with success indication." >&2 fi exit "$exit_value" } # Handler for rogue EXIT cleanup_for_exit() { # Early assume cleanup state of traps trap cleanup_ignore_handler INT TERM QUIT trap - EXIT cleanup echo >&2 echo "--- Merge run ended by unexpected EXIT event !" >&2 } ## Check arguments and dependencies if test "$#" -lt 4 then usage exit 1 fi echo >&2 echo "$(basename "$0") starting with $(expr $# - 2) ISO image files ..." >&2 dep="awk basename cat chmod cp date dirname expr fgrep grep" dep="$dep gunzip gzip head ls md5sum mkdir mount mv rm rmdir sed" dep="$dep sha256sum sort stat sudo umount uniq" missing=0 for i in $dep do if type "$i" >/dev/null 2>&1 then dummy=dummy else echo "--- Missing a helper program: $i" >&2 missing=1 fi done if test -z "$XORRISO" then XORRISO=xorriso fi if test "$XORRISO" = dummy then if test "$missing" = 0 then echo 'NOTE: Variable XORRISO is set to "dummy".' >&2 echo ' Will not perform xorriso run but only show its arguments.' >&2 fi elif "$XORRISO" -no_rc -version >/dev/null 2>&1 then if "$XORRISO" -no_rc -help 2>/dev/null | fgrep '"replay"' >/dev/null 2>&1 then dummy=dummy else echo '--- Help text of xorriso program '"$XORRISO"' lacks word "replay".' \ >&2 echo "--- It will fail when trying to make the new ISO bootable." >&2 echo " Consider to get and compile GNU xorriso from" >&2 echo " https://www.gnu.org/software/xorriso" >&2 echo " and to export variable XORRISO with the binary's path." >&2 echo >&2 missing=1 fi else echo "--- Test run of xorriso program failed: $XORRISO -no_rc -version" >&2 missing=1 fi if test "$missing" = 1 then echo "--- Merge run aborted !" >&2 exit 5 fi if test -z "$XORRISO_DD_TARGET_PATH" then XORRISO_DD_TARGET=xorriso-dd-target else XORRISO_DD_TARGET="$XORRISO_DD_TARGET_PATH"/xorriso-dd-target fi existing_tempfiles= for i in merged_dists merged_md5sum.txt merged_README.txt temp_file do if test -e "$i" then existing_tempfiles="$existing_tempfiles $i" else TEMPFILE_LIST="$TEMPFILE_LIST $i" fi done if test -n "$existing_tempfiles" then echo "--- Some temporary files for this script already exist:" >&2 echo "--- $existing_tempfiles" >&2 echo "--- Will not overwrite them." >&2 echo "--- Merge run aborted !" >&2 exit 1 fi # +++ From here on: Always call cleanup_and_end to perform exit +++ trap cleanup_and_end INT TERM QUIT trap cleanup_for_exit EXIT RESULT_ISO="$1" check_single_word "$1" "result_iso" || cleanup_and_end 1 xorriso_rm_result_iso=1 if test "$RESULT_ISO" = xorriso-dd-target-plug-test then # Get device name from xorriso-dd-target echo >&2 echo \ 'Special result_iso path "xorriso-dd-target-plug-test" causes a run of' >&2 echo " $XORRISO_DD_TARGET -with_sudo -plug_test" >&2 echo "to determine the USB stick or memory card:" >&2 echo "---------------------------------------------------------" >&2 plugged="$("$XORRISO_DD_TARGET" -with_sudo -plug_test)" ret=$? echo "$plugged" >&2 echo "---------------------------------------------------------" >&2 advice=NO if test "$ret" = 0 && test -n "$plugged" then name="$(echo "$plugged" | awk '{print $1}')" advice="$(echo "$plugged" | awk '{print $3}')" fi if test "$advice" = YES then if confirm_xdt_device /dev/"$name" then xorriso_rm_result_iso=0 RESULT_ISO=/dev/"$name" echo "Actual target device path is '${RESULT_ISO}'" >&2 else cleanup_and_end 1 fi else echo "--- No suitable device was determined." >&2 cleanup_and_end 1 fi elif echo "$RESULT_ISO" | grep '^mmc:' >/dev/null then xorriso_rm_result_iso=0 # Let xorriso judge over optical drives # (The /dev/sr patterns are Linux specific. The mmc: prefix is not.) if test "$XORRISO" = dummy then echo "Dummy mode: Accepting '${RESULT_ISO}' as optical drive." >&2 XORRISO_BLANK_CMD="-blank as_needed" elif "$XORRISO" -outdev "$RESULT_ISO" 2>/dev/null then echo "${XORRISO} accepts '${RESULT_ISO}' as optical drive." >&2 XORRISO_BLANK_CMD="-blank as_needed" else echo "--- $XORRISO refuses to accept '${RESULT_ISO}' as optical drive." >&2 cleanup_and_end 1 fi elif echo "$RESULT_ISO" | grep '^stdio:/dev/' >/dev/null then xorriso_rm_result_iso=0 echo \ "WARNING: User insists in using '${RESULT_ISO}' without further preparations" \ >&2 echo \ " or safety checks. The device might need blanking by xorriso" >&2 echo \ " before it will be willing to write to it. Permissions might not" >&2 echo \ " suffice. Be cautious when removing such obstacles. They might be" \ >&2 echo \ " there for a good reason." >&2 echo \ "If you agree to xorriso trying to overwrite the content of '${RESULT_ISO}'" \ >&2 echo "then enter 'yes' :" >&2 answer= read answer if test "$answer" = yes then echo \ "Will try to overwrite the device content without sudo or pseudo-blanking." \ >&2 XORRISO_STDIO_SYNC=16m else echo "--- Answer was not 'yes'. Will not write to device." >&2 cleanup_and_end 1 fi elif test -e "$RESULT_ISO" then if test "$(dirname $RESULT_ISO)" = "/dev" then echo >&2 echo "The result_iso path '${RESULT_ISO}' is an existing File in /dev." >&2 echo "Will only write to it if $XORRISO_DD_TARGET agrees." >&2 if type "$XORRISO_DD_TARGET" then echo >&2 echo \ "Performing: $XORRISO_DD_TARGET -with_sudo $(basename $RESULT_ISO)" >&2 echo "---------------------------------------------------------" >&2 "$XORRISO_DD_TARGET" -with_sudo "$(basename $RESULT_ISO)" ret=$? echo "---------------------------------------------------------" >&2 if test "$ret" = 0 then if confirm_xdt_device "$RESULT_ISO" then dummy=dummy else cleanup_and_end 1 fi else echo \ "--- $XORRISO_DD_TARGET refuses to accept this device as result_iso" >&2 cleanup_and_end 1 fi else echo "--- Helper program $XORRISO_DD_TARGET is missing." >&2 echo \ "--- Cannot evaluate suitability of '${RESULT_ISO}' as result_iso path" \ >&2 cleanup_and_end 1 fi fi if test -z "$XORRISO_STDIO_DEV" then echo "--- A file '${RESULT_ISO}' is already existing." >&2 echo "--- Will not overwrite it by the resulting ISO image." >&2 cleanup_and_end 1 fi fi MOUNT_TEMPLATE="$2" check_single_word "$2" "mount_template" || cleanup_and_end 1 x=$(dirname "$MOUNT_TEMPLATE") mount_parent_to_make= mount_parent_made= if test -d "$x" then dummy=dummy else # The parent will be temporarily created only if it is a plain leaf name if echo " $x" | fgrep / >/dev/null then echo "--- The parent directory '${x}' of mount_template does not exist" \ >&2 echo "--- and contains a '/' character. Will not create it." >&2 cleanup_and_end 1 fi mount_parent_to_make="$x" fi shift 2 ISO_LIST= mount_count=0 for i in "$@" do mount_count=$(expr $mount_count + 1) check_single_word "$i" "iso$mount_count" || cleanup_and_end 1 if test "$i" = "$RESULT_ISO" then echo "--- Arguments result_iso and iso$mount_count are equal:" >&2 echo "--- '${i}'" >&2 cleanup_and_end 1 fi if echo "$ISO_LIST" | fgrep " $i " >/dev/null then echo "--- Duplicate file path given as argument iso$mount_count :" >&2 echo "--- '${i}'" >&2 cleanup_and_end 1 fi x="${MOUNT_TEMPLATE}$mount_count" if test -d "$x" then dummy=dummy elif test -e "$x" then echo "--- A file '${x}' is already existing and not a directory." >&2 echo "--- Cannot mount iso$mount_count ('${i}')" >&2 cleanup_and_end 1 fi ISO_LIST="$ISO_LIST $i " if test -z "$iso_1" then iso_1="$i" fi done echo "Arguments look acceptable." >&2 ## Mount and copy out the files which need to be changed echo >&2 echo "Mounting ISO images if not yet mounted ..." >&2 if test -n "$mount_parent_to_make" then if mkdir "$mount_parent_to_make" then mount_parent_made="$mount_parent_to_make" else echo "--- Cannot create the parent directory of '${MOUNT_TEMPLATE}'." >&2 cleanup_and_end 1 fi fi mount_count=0 for i in $ISO_LIST do mount_count=$(expr $mount_count + 1) mount_point="${MOUNT_TEMPLATE}$mount_count" if test -d "$mount_point" then dummy=dummy else if mkdir "$mount_point" then RMDIR_LIST="$RMDIR_LIST $mount_point " else echo "--- Could not create directory '${mount_point}'." >&2 echo "--- Cannot mount iso$mount_count ('${i}')" >&2 cleanup_and_end 3 fi fi do_mount=1 if echo "$mount_point" | grep '^/' >/dev/null then x=$(mount | grep " $mount_point " | awk '{print $1}') elif echo "$mount_point" | grep '^./' >/dev/null then m=$(echo "$mount_point" | sed -e 's/^\.\///') x=$(mount | grep " $(pwd)/$m" | awk '{print $1}') else x=$(mount | grep " $(pwd)/$mount_point " | awk '{print $1}') fi if test -n "$x" then i1=$(ls -i "$x" | awk '{print $1}') i2=$(ls -i "$i" | awk '{print $1}') if test "$i1" = "$i2" then do_mount=0 echo "Note: Found $i already mounted at $mount_point" fi fi if test "$do_mount" = 1 then echo "Note: sudo mount $i $mount_point" if sudo mount "$i" "$mount_point" then dummy=dummy else echo "--- Could not mount '${i}' at '${mount_point}'." >&2 cleanup_and_end 3 fi UMOUNT_LIST="$UMOUNT_LIST $mount_point" fi MOUNT_LIST="$MOUNT_LIST $mount_point" done if test -n "$mount_parent_made" then RMDIR_LIST="$RMDIR_LIST $mount_parent_made " fi echo "Done." >&2 echo >&2 echo "Copying dists directory and md5sum.txt from first ISO ..." >&2 mount_point_1="$MOUNT_TEMPLATE"1 if test -d "$mount_point_1/dists" then echo "Copying: $mount_point_1/dists to merged_dists" >&2 if cp -a "$mount_point_1/dists" merged_dists then if chmod -R u+w merged_dists then dummy=dummy else echo "--- Could not chmod -R u+w merged_dists" >&2 cleanup_and_end 3 fi else echo "--- Could not copy /dists directory from first ISO." >&2 cleanup_and_end 3 fi else echo "--- First ISO does not contain a /dists directory." >&2 cleanup_and_end 2 fi echo "Copying: $mount_point_1/md5sum.txt to merged_md5sum.txt" >&2 if cp -a "$mount_point_1/md5sum.txt" merged_md5sum.txt then if chmod u+w merged_md5sum.txt then dummy=dummy else echo "--- Could not chmod u+w merged_md5sum.txt" >&2 cleanup_and_end 3 fi else echo "--- Could not copy /md5sum.txt from first ISO." >&2 cleanup_and_end 3 fi echo "Done." >&2 ## Helper functions # Put out the list of checksummed paths as listed in /dists/$dist/Release extract_checksum_paths() { mode=0 cat "$1" | \ while true do read -r x || break if test "$x" = "MD5Sum:" || test "$x" = "SHA1:" \ || test "$x" = "SHA256:" || test "$x" = "SHA512:" then if test "$mode" = 0 then mode=1 elif test "$mode" = 1 then break fi elif test "$mode" = 1 then echo "$x" fi done } # Put out the part before the first checksum field extract_release_head() { cat "$1" | \ while true do read -r x || break if test "$x" = "MD5Sum:" || test "$x" = "SHA1:" \ || test "$x" = "SHA256:" || test "$x" = "SHA512:" then break fi echo "$x" done } # Decide whether to re-compute MD5 and whether to put out the md5sum line # Parameters: prev_path prev_md5 check_and_put_out_md5_grep() { if echo "$prev_path" | grep '^\.\/firmware\/' >/dev/null 2>&1 then if test "$was_multiple" = 1 && test "$multi_md5_differs" = 1 then # There is the risk that the surviving file does not match prev_md5. # Better omit it. dummy=dummy else echo "$prev_md5 $prev_path" fi return 0 fi if echo "$prev_path" | grep '^\.\/dists\/' >/dev/null 2>&1 then md5_path="$(echo -n "$prev_path" | sed -e 's/^\.\//merged_/')" prev_md5="$(md5sum <"$md5_path" | awk '{print $1}')" elif test "$prev_path" = ./README.txt then prev_md5="$(md5sum <merged_README.txt | awk '{print $1}')" elif test "$was_multiple" = 1 then # Unchanged file was taken from iso1 # md5sum.txt lines might be permuted though. So compute freshly prev_md5="$(md5sum <"$mount_point_1"/"$prev_path" | awk '{print $1}')" fi if test -z "$prev_md5" then prev_md5="-" return 1 else echo "$prev_md5 $prev_path" fi return 0 } # Filter lines from md5sum.txt by removing duplicates and recomputing # the MD5 if needed and possible. polish_md5sum_txt() { prev_path= prev_md5= was_multiple=0 multi_md5_differs=0 while read md5 path do if test "$path" = "$prev_path" then if test "$md5" = "$prev_md5" then dummy=dummy else multi_md5_differs=1 fi was_multiple=1 continue fi if test -n "$prev_path" then check_and_put_out_md5_grep fi prev_path="$path" prev_md5="$md5" was_multiple=0 multi_md5_differs=0 done if test -n "$prev_path" then check_and_put_out_md5_grep fi return 0 } ## Determine which Debian release is on iso1 echo >&2 echo "Determining Debian release in first ISO ..." >&2 dist= for i in "$mount_point_1"/dists/* do if check_single_word "$i" "dists" then dummy=dummy else continue fi if test -d "$i" then if test -L "$i" then continue fi test -n "$dist" && dist="$dist " dist="${dist}$(basename "$i")" fi done if test -z "$dist" then if test -z "$MERGE_FOR_DIST" then echo "--- Cannot determine Debian release from directories in /dists" >&2 echo "--- (You may provide the release name as variable MERGE_FOR_DIST)" >&2 echo >&2 cleanup_and_end 2 fi elif test "$(echo "$dist" | wc -w)" -gt 1 then if test -z "$MERGE_FOR_DIST" then echo "--- More than one Debian release found in /dists: $dist" >&2 echo "--- (You may provide the release name as variable MERGE_FOR_DIST)" >&2 echo >&2 cleanup_and_end 2 fi fi if test -n "$MERGE_FOR_DIST" then echo "Note: Overriding release name '${dist}' by '${MERGE_FOR_DIST}'" >&2 dist="$MERGE_FOR_DIST" fi if test -d "$mount_point_1"/dists/"$dist" then echo "Will work along $mount_point_1"/dists/"$dist"/Release >&2 else echo "--- Cannot find directory $mount_point_1"/dists/"$dist" >&2 cleanup_and_end 2 fi for i in $MOUNT_LIST do if test -e "$i"/dists/"$dist"/Release then dummy=dummy else echo "--- Cannot find file $i"/dists/"$dist"/Release >&2 echo "--- All participating ISOs must be installation ISOs of the same release." >&2 cleanup_and_end 2 fi done ## Prepend info to /README.txt echo >&2 echo "Composing new /README.txt ..." >&2 if test -z "$MERGE_DATE" then MERGE_DATE=$(date +'%Y%m%d-%H:%M') fi printf 'Result of a run of %s at %s\r\n' \ "$(basename "$0")" "$MERGE_DATE" >temp_file printf 'Package pools and Packages lists were merged.\r\n' >>temp_file printf 'The other files stem from the first input ISO.\r\n' >>temp_file printf '\r\n' >>temp_file mount_count=0 for i in $ISO_LIST do mount_count=$(expr $mount_count + 1) mount_point="${MOUNT_TEMPLATE}$mount_count" printf 'Input ISO: %s\r\n' "$i" >>temp_file head -2 "$mount_point"/README.txt >>temp_file printf '\r\n' >>temp_file done printf '%s%s\r\n' " --------------------------------------" \ "----------------------------------------" >>temp_file printf '\r\n' >>temp_file cat "$mount_point_1"/README.txt >>temp_file mv temp_file merged_README.txt echo "Done." >&2 ## Merge package description files echo >&2 echo "Merging package description files ..." >&2 # Determine the files which are mentioned with checksum in main Release files path_list=$(for i in $MOUNT_LIST do extract_checksum_paths "$i"/dists/"$dist"/Release done | awk '{print $3}' | sort | uniq ) # Merge .gz files (Release should not be merged. Unclear what others need.) for i in $path_list do if echo "$i" | grep -v '.gz$' >/dev/null then continue fi echo "Merging: merged_dists/${dist}/$i" >&2 # make missing directories in merged_dists/"$dist"/ if test -e "$(dirname merged_dists/"$dist"/"$i")" then dummy=dummy else if mkdir -p "$(dirname merged_dists/"$dist"/"$i")" then dummy=dummy else echo "--- Cannot create directory $(dirname merged_dists/"$dist"/"$i")" >&2 cleanup_and_end 3 fi fi test -e temp_file && rm temp_file for mount_point in $MOUNT_LIST do if test -e "$mount_point"/dists/"$dist"/"$i" then if test -e temp_file then if test -n "$(tail -1 temp_file)" then echo >>temp_file fi fi gunzip <"$mount_point"/dists/"$dist"/"$i" >>temp_file fi done if test -e temp_file then gzip <temp_file >merged_dists/"$dist"/"$i" rm temp_file fi done echo "Done." >&2 ## Update dists/"$dist"/Release echo >&2 echo "Updating dists/${dist}/Release ..." >&2 extract_release_head merged_dists/"$dist"/Release >temp_file # Re-create "MD5Sum:", "SHA1:", "SHA256:", "SHA512:" sections for cmd in md5sum sha1sum sha256sum sha512sum do if type "$cmd" >/dev/null then case "$cmd" in md5sum) echo "MD5Sum:" ;; sha1sum) echo "SHA1:" ;; sha256sum) echo "SHA256:" ;; sha512sum) echo "SHA512:" ;; esac for i in $path_list do file=merged_dists/"$dist"/"$i" if test -e "$file" then sum=$("$cmd" "$file" | awk '{print $1}') size=$(stat -c '%s' "$file") elif test -e "$file".gz then sum=$(gunzip <"$file".gz | "$cmd" | awk '{print $1}') size=$(gunzip <"$file".gz | wc -c) else continue fi list_path=$(echo "$file" | sed -e 's/^merged_dists\/'"$dist"'\///') printf ' %s %8d %s\n' "$sum" "$size" "$list_path" done fi done >>temp_file mv temp_file merged_dists/"$dist"/Release echo "Done." >&2 ## Merge md5sum.txt and compute the MD5s which might have changed echo >&2 echo "Merging md5sum.txt files ..." >&2 for i in $MOUNT_LIST do cat "$i"/md5sum.txt done | sort -k 2 >merged_md5sum.txt # ./pool files are surely unchanged. Others need some more examination. ( fgrep ' ./pool/' <merged_md5sum.txt | uniq fgrep -v ' ./pool/' <merged_md5sum.txt | polish_md5sum_txt ) \ | sort -k 2 >temp_file mv temp_file merged_md5sum.txt echo "Done." >&2 ## Produce the new ISO image echo >&2 echo "Producing result ISO image ..." >&2 # Create file with list of /pool and /firmware -map commands for all but the # first ISO for mount_point in $MOUNT_LIST do if test "$mount_point" = "$mount_point_1" then echo "Planned as imported package pool : ${mount_point}/pool" >&2 else echo "Planned for merging into package pool: ${mount_point}/pool" >&2 echo " -map ${mount_point}/pool /pool" >>temp_file fi done for mount_point in $MOUNT_LIST do if test "$mount_point" = "$mount_point_1" then if test -d "${mount_point}/firmware" then echo \ "Planned as imported /firmware : ${mount_point}/firmware" >&2 fi else if test -d "${mount_point}/firmware" then echo \ "Planned for merging into /firmware : ${mount_point}/firmware" >&2 echo " -map ${mount_point}/firmware /firmware" >>temp_file fi fi done if test "$XORRISO" = dummy then echo "Planned xorriso commands in temp_file:" >&2 cat temp_file echo >&2 if test -n "$XORRISO_SUDO" then echo "NOTE: xorriso would run under control of '${XORRISO_SUDO}'" >&2 XORRISO_SUDO= fi echo 'NOTE: Variable XORRISO is set to "dummy".' >&2 echo ' Will not perform xorriso run but only show its arguments:' >&2 XORRISO=echo else echo "Running as xorriso program: $XORRISO" >&2 fi echo >&2 # Mark the result path for possible removal by cleanup if test -z "$XORRISO_STDIO_DEV" then EMERGING_ISO="$RESULT_ISO" else EMERGING_ISO= fi if $XORRISO_SUDO "$XORRISO" \ -no_rc \ -indev "$iso_1" \ -outdev "$XORRISO_STDIO_DEV""$RESULT_ISO" \ $XORRISO_BLANK_CMD \ -options_from_file temp_file \ -map merged_dists /dists \ -map merged_md5sum.txt /md5sum.txt \ -map merged_README.txt /README.txt \ -chown_r 0 /dists /md5sum.txt /README.txt -- \ -chgrp_r 0 /dists /md5sum.txt /README.txt -- \ -chmod_r a-w /dists /md5sum.txt -- \ -chmod_r a=r /README.txt -- \ -boot_image any replay \ -fs 16m \ -stdio_sync "$XORRISO_STDIO_SYNC" \ -padding included \ -stream_recording on \ -joliet on \ -compliance no_emul_toc then # Revoke mark for possible removal by cleanup EMERGING_ISO= test "$XORRISO" = echo || \ echo "Run of xorriso program ended without error indication." >&2 else echo "--- Run of xorriso program ended with error indication." >&2 cleanup_and_end 4 fi ## Finish cleanup_and_end 0 .\" Hey, EMACS: -*- nroff -*- .\" .\" IMPORTANT NOTE: .\" .\" The original of this file is kept in test/merge_debian_isos.texi .\" This here was generated by program xorriso/make_xorriso_1 .\" .\" .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH MERGE_DEBIAN_ISOS 1 "Version 1.5.7, Jun 07, 2023" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp <n> insert n+1 empty lines .\" for manpage-specific macros, see man(7) .nh .SH NAME merge_debian_isos \- Program to merge multiple debian\-cd ISO images .SH SYNOPSIS .B merge_debian_isos result_iso mount_template iso1 iso2 [... isoN] .br .SH DESCRIPTION .PP \fBmerge_debian_isos\fR mounts by \fBsudo\fR the ISO 9660 images iso1 to isoN at directories mount_template1 to mount_templateN, if not already mounted that way. Then the Debian pools and package lists get merged and a new ISO 9660 image result_iso is produced by a run of \fBxorriso\fR. If iso1 is bootable then the new image will be bootable by the same means. .br .PP The file depicted by result_iso must not yet exist or has to be a device which is acceptable for Linux\-specific helper script \fBxorriso\-dd\-target\fR. If xorriso\-dd\-target agrees and the user confirms by input "yes" then xorriso will be run under sudo. .br Exempted from this evaluation are addresses which begin by "mmc:" for an optical drive on Linux, BSDs, Solaris, or by "stdio:/dev/" for which the user takes full and dangerous responsibility. .br Special result_iso path \fBxorriso\-dd\-target\-plug\-test\fR determines on systems with Linux kernel the target USB stick by a dialog around plugging it in. xorriso will be run under sudo, if xorriso\-dd\-target agrees and the user confirms by input "yes". .br .PP At least the parent directory of argument \fBmount_template\fR must already exist or has to be given by a plain name without "/" so that it can be created in the current directory. (I.e. without even "./".) .br All arguments must be single words without using quotation marks. None of the isoN must be equal to another isoM. .br .PP This script creates and finally removes the following temporary tree and files which must not yet exist in the current working directory: ./merged_dists , ./merged_md5sum.txt , ./merged_REAMDE.txt ./temp_file .br Further it creates and finally removes directories mount_template* if they are needed and do not exist when the script starts. If the parent directory in the mount_template path does not exist and its path contains no "/" then it gets created and finally removed. The script depends on the following programs: .br awk, basename, cat, chmod, cp, date, dirname, expr, fgrep, grep, gunzip, gzip, head, ls, md5sum, mkdir, mount, mv, rm, rmdir, sed, sh, sha256sum, sort, stat, sudo, umount, uniq, xorriso .br With device writing involving helper script xorriso\-dd\-target: .br dd, Linux kernel, lsblk, sleep, tr, uname, wc, whoami, xorriso\-dd\-target .br Recommended are: sha1sum, sha512sum .SS .br .SH OPTIONS .br .PP merge_debian_isos has no command line options besides the input words described above, but it reacts on some environment variables. .br See section ENVIRONMENT. .br .PP If less than 4 arguments are given, the script will print its help text to standard error and exit with non\-zero value, indicating failure. .SS .SH EXAMPLES .SS .B Overview of examples: Merge DVD images 1 to 5 .br Use GNU xorriso instead of installed xorriso .br Write result directly to an optical drive .br Write result directly and safely to USB stick (Linux only) .br Write result directly and unsafely to USB stick .SS .B Merge DVD images 1 to 5 The resulting image will be named "merged.iso". .br Directory ./merge_mount/ will be created if not yet existing. .br merge_debian_isos merged.iso merge_mount/iso \\ debian\-11.2.0\-amd64\-DVD\-[12345].iso .br Expect to see some harmless warnings like .br xorriso : WARNING : \-volid text does not comply to ISO 9660 / ECMA 119 rules libisofs: WARNING : Cannot add /debian to Joliet tree. Symlinks can only be added to a Rock Ridge tree. libisofs: WARNING : Image size exceeds 1024 cylinders. Cannot align partition. .SS .B Use GNU xorriso instead of installed xorriso merge_debian_isos will refuse to work if the installed version of xorriso is older than 1.4.2. In this case consider to download and compile the GNU xorriso tarball from .br https://www.gnu.org/software/xorriso/#download .br You may use it without installing it after compilation and thus without disturbing your system's package management. Assumed that you unpacked the xorriso\-1.5.6 tarball in your $HOME directory and have successfully compiled it, do: .br export XORRISO="$HOME"/xorriso\-1.5.6/xorriso/xorriso .br merge_debian_isos merged.iso merge_mount/iso \\ debian\-11.2.0\-amd64\-DVD\-[12345].iso .SS .B Write result directly to an optical drive xorriso is able to burn optical media on GNU/Linux (/dev/sr*), Solaris (/dev/rdsk/*) , FreeBSD (/dev/cd*) , NetBSD (/dev/rcd*), and OpenBSD (/dev/rcd*). Get a list of available optical drive devices by: .br xorriso \-devices .br It might be that you need superuser powers for this and then have to enable access to the device file for the user who runs merge_debian_isos. .br In order to directly write the merged ISO image to an optical medium, use the desired device file path with prefix "mmc:": .br merge_debian_isos mmc:/dev/sr0 merge_mount/iso \\ debian\-11.2.0\-amd64\-DVD\-[12345].iso .SS .B Write result directly and safely to USB stick (Linux only) The Linux\-specific script \fBxorriso\-dd\-target\fR evaluates device files whether they and their content look unimportant enough for being overwritten by an image file. .br This mainly means that the storage device is attached to USB and contains no filesystems which are not of type FAT or ISO 9660. .br You may get xorriso\-dd\-target by: .br wget \\ https://dev.lovelyhq.com/libburnia/libisoburn/raw/master/xorriso\-dd\-target/xorriso\-dd\-target chmod u+x xorriso\-dd\-target .br Consider to also download xorriso\-dd\-target.sig and to verify the script by .br gpg \-\-verify xorriso\-dd\-target.sig xorriso\-dd\-target .br Announce the storage location of the downloaded xorriso\-dd\-target: .br export XORRISO_DD_TARGET_PATH="$(pwd)" .br The prepared use case in merge_debian_isos uses the xorriso\-dd\-target option \-plug_test under \fBsudo\fR, which asks the user for first having the desired USB stick unplugged so that the unwanted devices can get registered. Then it asks the user to plug in the USB stick, so that it gets recognized as desired target. .br xorriso\-dd\-target evaluates the content and decides whether it looks disposable enough. If so, then it allows merge_debian_isos to write its result to the device. .br merge_debian_isos xorriso\-dd\-target\-plug\-test merge_mount/iso \\ debian\-11.2.0\-amd64\-DVD\-[12345].iso .SS .B Write result directly and unsafely to USB stick On operating systems other than GNU/Linux or with storage devices not acceptable to xorriso\-dd\-target it is possible to remove all safety precautions beyond those of xorriso, which can be overcome by "blanking" the device. .br So after due evaluation of the device situation and on your very own risk you may use the device path prefix "stdio:", possibly with superuser powers: .br xorriso \-outdev stdio:/dev/sdd \-blank as_needed merge_debian_isos stdio:/dev/sdd merge_mount/iso \\ debian\-11.2.0\-amd64\-DVD\-[12345].iso For details about "stdio:" and pseudo\-blanking non\-optical devices read man xorriso. .br The xorriso run in merge_debian_isos ignores locally defined xorriso startup files (by command \-no_rc). .SH FILES For now, no files are defined for configuration. .SH ENVIRONMENT The following environment variables influence the program behavior: .br Exported non\-empty variable MERGE_DATE enforces a particular date string in the text which gets prepended to /README.txt . .br Exported non\-empty variable MERGE_FOR_DIST enforces the use of a particular directory in /dists of iso1. Normally only one such directory is found and thus no need to set MERGE_FOR_DIST. .br Exported non\-empty variable MERGE_KEEP_ISO prevents the removal of result_iso after xorriso indicated failure of production. .br Exported non\-empty variable XORRISO gives the path to a xorriso binary, which will be used instead of the system\-wide installed xorriso binary. This may be needed if installed xorriso is older than 1.4.2. .br If XORRISO is set to "dummy" then no new ISO will emerge. .br Exported non\-empty variable XORRISO_DD_TARGET_PATH names the directory where to find xorriso\-dd\-target, which evaluates the suitability of result_iso devices or does the plug\-test dialog. .br .SH SEE ALSO .BR xorriso(1), .BR xorriso-dd-target(1), .SH BUGS To report bugs, request help, or suggest enhancements for \fBmerge_debian_isos\fR, please send electronic mail to the public list <bug\-xorriso@gnu.org>. If more privacy is desired, mail to <scdbackup@gmx.net>. .br Please describe what you expect the program to do, the program arguments which you used, the messages of \fBmerge_debian_isos\fR, and the undesirable outcome of your program run. .br Expect to get asked more questions before solutions can be proposed. .SH AUTHOR Thomas Schmitt <scdbackup@gmx.net> .br for libburnia\-project.org .SH COPYRIGHT Copyright (c) 2023 Thomas Schmitt .br Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of merge_debian_isos. If you make use of the license to derive modified versions of merge_debian_isos then you are entitled to modify this text under that same license. .SH CREDITS \fBmerge_debian_isos\fR was originally developed with advise and testing by Zhang Boyang in the course of Debian bug #1011343. Steve McIntyre provided information about various file aspects of debian\-cd ISO images. This is merge_debian_isos.info, produced by makeinfo version 5.2 from merge_debian_isos.texi. merge_debian_isos - sh script to merge multiple debian-cd ISO images Copyright (C) 2023 Thomas Schmitt Permission is granted to distribute this text freely. INFO-DIR-SECTION Archiving START-INFO-DIR-ENTRY * Merge_debian_isos: (merge_debian_isos). Merge debian-cd ISO images to a single image END-INFO-DIR-ENTRY  File: merge_debian_isos.info, Node: Top, Next: Overview, Up: (dir) merge_debian_isos 1.5.7 *********************** merge_debian_isos - Program to merge multiple debian-cd ISO images * Menu: * Overview:: Overview * Options:: Options * Examples:: Examples * Files:: Files * Environ:: Environment * Seealso:: See also * Bugreport:: Reporting bugs * Legal:: Author, Copyright, Credits * CommandIdx:: Alphabetic Option List * ConceptIdx:: Alphabetic List of Concepts and Objects  File: merge_debian_isos.info, Node: Overview, Next: Options, Prev: Top, Up: Top 1 Overview ********** 'merge_debian_isos' mounts by *sudo* the ISO 9660 images iso1 to isoN at directories mount_template1 to mount_templateN, if not already mounted that way. Then the Debian pools and package lists get merged and a new ISO 9660 image result_iso is produced by a run of *xorriso*. If iso1 is bootable then the new image will be bootable by the same means. The file depicted by result_iso must not yet exist or has to be a device which is acceptable for Linux-specific helper script *xorriso-dd-target*. If xorriso-dd-target agrees and the user confirms by input "yes" then xorriso will be run under sudo. Exempted from this evaluation are addresses which begin by "mmc:" for an optical drive on Linux, BSDs, Solaris, or by "stdio:/dev/" for which the user takes full and dangerous responsibility. Special result_iso path *xorriso-dd-target-plug-test* determines on systems with Linux kernel the target USB stick by a dialog around plugging it in. xorriso will be run under sudo, if xorriso-dd-target agrees and the user confirms by input "yes". At least the parent directory of argument *mount_template* must already exist or has to be given by a plain name without "/" so that it can be created in the current directory. (I.e. without even "./".) All arguments must be single words without using quotation marks. None of the isoN must be equal to another isoM. This script creates and finally removes the following temporary tree and files which must not yet exist in the current working directory: ./merged_dists , ./merged_md5sum.txt , ./merged_REAMDE.txt ./temp_file Further it creates and finally removes directories mount_template* if they are needed and do not exist when the script starts. If the parent directory in the mount_template path does not exist and its path contains no "/" then it gets created and finally removed. The script depends on the following programs: awk, basename, cat, chmod, cp, date, dirname, expr, fgrep, grep, gunzip, gzip, head, ls, md5sum, mkdir, mount, mv, rm, rmdir, sed, sh, sha256sum, sort, stat, sudo, umount, uniq, xorriso With device writing involving helper script xorriso-dd-target: dd, Linux kernel, lsblk, sleep, tr, uname, wc, whoami, xorriso-dd-target Recommended are: sha1sum, sha512sum  File: merge_debian_isos.info, Node: Options, Next: Examples, Prev: Overview, Up: Top 2 Options ********* merge_debian_isos has no command line options besides the input words described above, but it reacts on some environment variables. If less than 4 arguments are given, the script will print its help text to standard error and exit with non-zero value, indicating failure.  File: merge_debian_isos.info, Node: Examples, Next: Files, Prev: Options, Up: Top 3 Examples ********** * Menu: * ExPlain:: Merge DVD images 1 to 5 * ExXorriso:: Use GNU xorriso instead of installed xorriso * ExOptical:: Write result directly to an optical drive * ExDdTarget:: Write result directly and safely to USB stick (Linux only) * ExStdio:: Write result directly and unsafely to USB stick  File: merge_debian_isos.info, Node: ExPlain, Next: ExXorriso, Prev: Options, Up: Examples 3.1 Merge DVD images 1 to 5 =========================== The resulting image will be named "merged.iso". Directory ./merge_mount/ will be created if not yet existing. merge_debian_isos merged.iso merge_mount/iso \ debian-11.2.0-amd64-DVD-[12345].iso Expect to see some harmless warnings like xorriso : WARNING : -volid text does not comply to ISO 9660 / ECMA 119 rules libisofs: WARNING : Cannot add /debian to Joliet tree. Symlinks can only be added to a Rock Ridge tree. libisofs: WARNING : Image size exceeds 1024 cylinders. Cannot align partition.  File: merge_debian_isos.info, Node: ExXorriso, Next: ExOptical, Prev: ExPlain, Up: Examples 3.2 Use GNU xorriso instead of installed xorriso ================================================ merge_debian_isos will refuse to work if the installed version of xorriso is older than 1.4.2. In this case consider to download and compile the GNU xorriso tarball from https://www.gnu.org/software/xorriso/#download You may use it without installing it after compilation and thus without disturbing your system's package management. Assumed that you unpacked the xorriso-1.5.6 tarball in your $HOME directory and have successfully compiled it, do: export XORRISO="$HOME"/xorriso-1.5.6/xorriso/xorriso merge_debian_isos merged.iso merge_mount/iso \ debian-11.2.0-amd64-DVD-[12345].iso  File: merge_debian_isos.info, Node: ExOptical, Next: ExDdTarget, Prev: ExXorriso, Up: Examples 3.3 Write result directly to an optical drive ============================================= xorriso is able to burn optical media on GNU/Linux (/dev/sr*), Solaris (/dev/rdsk/*) , FreeBSD (/dev/cd*) , NetBSD (/dev/rcd*), and OpenBSD (/dev/rcd*). Get a list of available optical drive devices by: xorriso -devices It might be that you need superuser powers for this and then have to enable access to the device file for the user who runs merge_debian_isos. In order to directly write the merged ISO image to an optical medium, use the desired device file path with prefix "mmc:": merge_debian_isos mmc:/dev/sr0 merge_mount/iso \ debian-11.2.0-amd64-DVD-[12345].iso  File: merge_debian_isos.info, Node: ExDdTarget, Next: ExStdio, Prev: ExOptical, Up: Examples 3.4 Write result directly and safely to USB stick (Linux only) ============================================================== The Linux-specific script *xorriso-dd-target* evaluates device files whether they and their content look unimportant enough for being overwritten by an image file. This mainly means that the storage device is attached to USB and contains no filesystems which are not of type FAT or ISO 9660. You may get xorriso-dd-target by: wget \ https://dev.lovelyhq.com/libburnia/libisoburn/raw/master/xorriso-dd-target/xorriso-dd-target chmod u+x xorriso-dd-target Consider to also download xorriso-dd-target.sig and to verify the script by gpg -verify xorriso-dd-target.sig xorriso-dd-target Announce the storage location of the downloaded xorriso-dd-target: export XORRISO_DD_TARGET_PATH="$(pwd)" The prepared use case in merge_debian_isos uses the xorriso-dd-target option -plug_test under *sudo*, which asks the user for first having the desired USB stick unplugged so that the unwanted devices can get registered. Then it asks the user to plug in the USB stick, so that it gets recognized as desired target. xorriso-dd-target evaluates the content and decides whether it looks disposable enough. If so, then it allows merge_debian_isos to write its result to the device. merge_debian_isos xorriso-dd-target-plug-test merge_mount/iso \ debian-11.2.0-amd64-DVD-[12345].iso  File: merge_debian_isos.info, Node: ExStdio, Next: Files, Prev: ExDdTarget, Up: Examples 3.5 Write result directly and unsafely to USB stick =================================================== On operating systems other than GNU/Linux or with storage devices not acceptable to xorriso-dd-target it is possible to remove all safety precautions beyond those of xorriso, which can be overcome by "blanking" the device. So after due evaluation of the device situation and on your very own risk you may use the device path prefix "stdio:", possibly with superuser powers: xorriso -outdev stdio:/dev/sdd -blank as_needed merge_debian_isos stdio:/dev/sdd merge_mount/iso \ debian-11.2.0-amd64-DVD-[12345].iso For details about "stdio:" and pseudo-blanking non-optical devices read man xorriso. The xorriso run in merge_debian_isos ignores locally defined xorriso startup files (by command -no_rc).  File: merge_debian_isos.info, Node: Files, Next: Environ, Prev: Examples, Up: Top 4 Files ******* For now, no files are defined for configuration.  File: merge_debian_isos.info, Node: Environ, Next: Seealso, Prev: Files, Up: Top 5 Environ ********* The following environment variables influence the program behavior: Exported non-empty variable MERGE_DATE enforces a particular date string in the text which gets prepended to /README.txt . Exported non-empty variable MERGE_FOR_DIST enforces the use of a particular directory in /dists of iso1. Normally only one such directory is found and thus no need to set MERGE_FOR_DIST. Exported non-empty variable MERGE_KEEP_ISO prevents the removal of result_iso after xorriso indicated failure of production. Exported non-empty variable XORRISO gives the path to a xorriso binary, which will be used instead of the system-wide installed xorriso binary. This may be needed if installed xorriso is older than 1.4.2. If XORRISO is set to "dummy" then no new ISO will emerge. Exported non-empty variable XORRISO_DD_TARGET_PATH names the directory where to find xorriso-dd-target, which evaluates the suitability of result_iso devices or does the plug-test dialog.  File: merge_debian_isos.info, Node: Seealso, Next: Bugreport, Prev: Environ, Up: Top 6 See also ********** xorriso(1), xorriso-dd-target(1)  File: merge_debian_isos.info, Node: Bugreport, Next: Legal, Prev: Seealso, Up: Top 7 Reporting bugs **************** To report bugs, request help, or suggest enhancements for 'merge_debian_isos', please send electronic mail to the public list <bug-xorriso@gnu.org>. If more privacy is desired, mail to <scdbackup@gmx.net>. Please describe what you expect the program to do, the program arguments which you used, the messages of 'merge_debian_isos', and the undesirable outcome of your program run. Expect to get asked more questions before solutions can be proposed.  File: merge_debian_isos.info, Node: Legal, Next: CommandIdx, Prev: Bugreport, Up: Top 8 Author, Copyright, Credits **************************** 8.1 Author ========== Thomas Schmitt <scdbackup@gmx.net> for libburnia-project.org 8.2 Copyright ============= Copyright (c) 2023 Thomas Schmitt Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of merge_debian_isos. If you make use of the license to derive modified versions of merge_debian_isos then you are entitled to modify this text under that same license. 8.3 Credits =========== 'merge_debian_isos' was originally developed with advise and testing by Zhang Boyang in the course of Debian bug #1011343. Steve McIntyre provided information about various file aspects of debian-cd ISO images.  File: merge_debian_isos.info, Node: CommandIdx, Next: ConceptIdx, Prev: Legal, Up: Top 9 Alphabetic Options List *************************  File: merge_debian_isos.info, Node: ConceptIdx, Next: Top, Prev: CommandIdx, Up: Top 10 Alphabetic List of Concepts and Objects ****************************************** �[index�] * Menu: * Bugs, reporting: Bugreport. (line 6) * Examples: Examples. (line 6) * merge_debian_isos, options: Options. (line 6) * Problems, reporting: Bugreport. (line 6)  Tag Table: Node: Top417 Node: Overview1014 Node: Options3381 Node: Examples3771 Node: ExPlain4196 Node: ExXorriso4870 Node: ExOptical5664 Node: ExDdTarget6448 Node: ExStdio7971 Node: Files8883 Node: Environ9039 Node: Seealso10104 Node: Bugreport10253 Node: Legal10838 Node: CommandIdx11666 Node: ConceptIdx11813  End Tag Table ˆF��d€¬t� éËßÀ«À¨Tcš�œ ã‹ÑÅI$J€Œ$Œ°ŠPØŠ�ž,à¦[ÌJ3™ì®ù&9&\ú\input texinfo @c -*-texinfo-*- @c %**start of header @setfilename merge_debian_isos.info @settitle merge_debian_isos 1.5.7 @c %**end of header @c @c man-ignore-lines begin @dircategory Archiving @direntry * Merge_debian_isos: (merge_debian_isos). Merge debian-cd ISO images to a single image @end direntry @c man-ignore-lines end @c @c Notes about embedded man page: @c This texinfo code contains the necessary info to produce a man page. @c One can produce it by applying the following rules: @c The first line gets discarded. @c Line start "@c man " will become "", the remainder is put out unaltered. @c Lines "@*" will be converted to ".br" @c "@c man-ignore-lines N" will discard N following lines. @c "@c man-ignore-lines begin" discards all following lines @c up to "@c man-ignore-lines end". @c Line blocks of "@menu" "@end menu" will be discarded. @c "@item word words" becomes "\fBword\fR words". @c @b{...}, @command{...}, @dfn{...}, @emph{...}, @strong{...} @c get mapped to \fB...\fR . @c @abbr{...}, @code{...}, @file{...}, @i{...}, @option{...}, @r{...}, @c @ref{...}, @samp{...},@var{...}, get mapped to ... . @c @ref{...}, @xref{...} get mapped to empty text. @c @email{...} gets mapped to <...> . @c Mapped {...} content is subject to the rules except {...} mapping. @c @minus{} will become "-". @c @@ , @{, @} will get stripped of their first @. @c Other lines which begin by "@" will be discarded. @c In lines not stemming from "@c man", "\" becomes "\\" @c "-" which are not preceded by an uneven number of "\" will get @c prepended one "\". @c @c @c man .\" Hey, EMACS: -*- nroff -*- @c man .\" @c man .\" IMPORTANT NOTE: @c man .\" @c man .\" The original of this file is kept in test/merge_debian_isos.texi @c man .\" This here was generated by program xorriso/make_xorriso_1 @c man .\" @c man .\" @c man .\" First parameter, NAME, should be all caps @c man .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection @c man .\" other parameters are allowed: see man(7), man(1) @c man .TH MERGE_DEBIAN_ISOS 1 "Version 1.5.7, Jun 07, 2023" @c man .\" Please adjust this date whenever revising the manpage. @c man .\" @c man .\" Some roff macros, for reference: @c man .\" .nh disable hyphenation @c man .\" .hy enable hyphenation @c man .\" .ad l left justify @c man .\" .ad b justify to both left and right margins @c man .\" .nf disable filling @c man .\" .fi enable filling @c man .\" .br insert line break @c man .\" .sp <n> insert n+1 empty lines @c man .\" for manpage-specific macros, see man(7) @c man .nh @c man-ignore-lines begin @copying merge_debian_isos - sh script to merge multiple debian-cd ISO images Copyright @copyright{} 2023 Thomas Schmitt @quotation Permission is granted to distribute this text freely. @end quotation @end copying @c man-ignore-lines end @titlepage @title Manual of GNU xorriso companion merge_debian_isos 1.5.7 @author Thomas Schmitt @page @vskip 0pt plus 1filll @insertcopying @end titlepage @contents @ifnottex @node Top @top merge_debian_isos 1.5.7 @c man-ignore-lines 1 @c man .SH NAME merge_debian_isos - Program to merge multiple debian-cd ISO images @end ifnottex @menu * Overview:: Overview * Options:: Options * Examples:: Examples * Files:: Files * Environ:: Environment * Seealso:: See also * Bugreport:: Reporting bugs * Legal:: Author, Copyright, Credits * CommandIdx:: Alphabetic Option List * ConceptIdx:: Alphabetic List of Concepts and Objects @end menu @node Overview, Options, Top, Top @chapter Overview @c man .SH SYNOPSIS @c man .B merge_debian_isos @c man result_iso mount_template iso1 iso2 [... isoN] @c man .br @c man .SH DESCRIPTION @c man .PP @command{merge_debian_isos} mounts by @strong{sudo} the ISO 9660 images iso1 to isoN at directories mount_template1 to mount_templateN, if not already mounted that way. Then the Debian pools and package lists get merged and a new ISO 9660 image result_iso is produced by a run of @strong{xorriso}. If iso1 is bootable then the new image will be bootable by the same means. @* @sp 1 @c man .PP The file depicted by result_iso must not yet exist or has to be a device which is acceptable for Linux-specific helper script @strong{xorriso-dd-target}. If xorriso-dd-target agrees and the user confirms by input "yes" then xorriso will be run under sudo. @* Exempted from this evaluation are addresses which begin by "mmc:" for an optical drive on Linux, BSDs, Solaris, or by "stdio:/dev/" for which the user takes full and dangerous responsibility. @* Special result_iso path @strong{xorriso-dd-target-plug-test} determines on systems with Linux kernel the target USB stick by a dialog around plugging it in. xorriso will be run under sudo, if xorriso-dd-target agrees and the user confirms by input "yes". @* @sp 1 @c man .PP At least the parent directory of argument @strong{mount_template} must already exist or has to be given by a plain name without "/" so that it can be created in the current directory. (I.e. without even "./".) @* All arguments must be single words without using quotation marks. None of the isoN must be equal to another isoM. @* @sp 1 @c man .PP This script creates and finally removes the following temporary tree and files which must not yet exist in the current working directory: ./merged_dists , ./merged_md5sum.txt , ./merged_REAMDE.txt ./temp_file @* Further it creates and finally removes directories mount_template* if they are needed and do not exist when the script starts. If the parent directory in the mount_template path does not exist and its path contains no "/" then it gets created and finally removed. The script depends on the following programs: @* awk, basename, cat, chmod, cp, date, dirname, expr, fgrep, grep, gunzip, gzip, head, ls, md5sum, mkdir, mount, mv, rm, rmdir, sed, sh, sha256sum, sort, stat, sudo, umount, uniq, xorriso @* With device writing involving helper script xorriso-dd-target: @* dd, Linux kernel, lsblk, sleep, tr, uname, wc, whoami, xorriso-dd-target @* Recommended are: sha1sum, sha512sum @c man .SS @node Options, Examples, Overview, Top @chapter Options @cindex merge_debian_isos, options @c man .br @c man .SH OPTIONS @c man .br @c man .PP merge_debian_isos has no command line options besides the input words described above, but it reacts on some environment variables. @c man .br @c man See section ENVIRONMENT. @* @sp 1 @c man .PP If less than 4 arguments are given, the script will print its help text to standard error and exit with non-zero value, indicating failure. @c man .SS @node Examples, Files, Options, Top @chapter Examples @c man .SH EXAMPLES @c man .SS @c man .B Overview of examples: @c man Merge DVD images 1 to 5 @c man .br @c man Use GNU xorriso instead of installed xorriso @c man .br @c man Write result directly to an optical drive @c man .br @c man Write result directly and safely to USB stick (Linux only) @c man .br @c man Write result directly and unsafely to USB stick @cindex Examples @menu * ExPlain:: Merge DVD images 1 to 5 * ExXorriso:: Use GNU xorriso instead of installed xorriso * ExOptical:: Write result directly to an optical drive * ExDdTarget:: Write result directly and safely to USB stick (Linux only) * ExStdio:: Write result directly and unsafely to USB stick @end menu @c man .SS @c man .B Merge DVD images 1 to 5 @node ExPlain, ExXorriso, Options, Examples @section Merge DVD images 1 to 5 The resulting image will be named "merged.iso". @* Directory ./merge_mount/ will be created if not yet existing. @* @sp 1 merge_debian_isos merged.iso merge_mount/iso \ debian-11.2.0-amd64-DVD-[12345].iso @* @sp 1 Expect to see some harmless warnings like @* @sp 1 xorriso : WARNING : -volid text does not comply to ISO 9660 / ECMA 119 rules libisofs: WARNING : Cannot add /debian to Joliet tree. Symlinks can only be added to a Rock Ridge tree. libisofs: WARNING : Image size exceeds 1024 cylinders. Cannot align partition. @c man .SS @c man .B Use GNU xorriso instead of installed xorriso @node ExXorriso, ExOptical, ExPlain, Examples @section Use GNU xorriso instead of installed xorriso merge_debian_isos will refuse to work if the installed version of xorriso is older than 1.4.2. In this case consider to download and compile the GNU xorriso tarball from @* https://www.gnu.org/software/xorriso/#download @* You may use it without installing it after compilation and thus without disturbing your system's package management. Assumed that you unpacked the xorriso-1.5.6 tarball in your $HOME directory and have successfully compiled it, do: @* @sp 1 export XORRISO="$HOME"/xorriso-1.5.6/xorriso/xorriso @* @sp 1 merge_debian_isos merged.iso merge_mount/iso \ debian-11.2.0-amd64-DVD-[12345].iso @c man .SS @c man .B Write result directly to an optical drive @node ExOptical, ExDdTarget, ExXorriso, Examples @section Write result directly to an optical drive xorriso is able to burn optical media on GNU/Linux (/dev/sr*), Solaris (/dev/rdsk/*) , FreeBSD (/dev/cd*) , NetBSD (/dev/rcd*), and OpenBSD (/dev/rcd*). Get a list of available optical drive devices by: @* @sp 1 xorriso -devices @* @sp 1 It might be that you need superuser powers for this and then have to enable access to the device file for the user who runs merge_debian_isos. @* @sp 1 In order to directly write the merged ISO image to an optical medium, use the desired device file path with prefix "mmc:": @* @sp 1 merge_debian_isos mmc:/dev/sr0 merge_mount/iso \ debian-11.2.0-amd64-DVD-[12345].iso @c man .SS @c man .B Write result directly and safely to USB stick (Linux only) @node ExDdTarget, ExStdio, ExOptical, Examples @section Write result directly and safely to USB stick (Linux only) The Linux-specific script @strong{xorriso-dd-target} evaluates device files whether they and their content look unimportant enough for being overwritten by an image file. @* This mainly means that the storage device is attached to USB and contains no filesystems which are not of type FAT or ISO 9660. @* You may get xorriso-dd-target by: @* @sp 1 wget \ https://dev.lovelyhq.com/libburnia/libisoburn/raw/master/xorriso-dd-target/xorriso-dd-target chmod u+x xorriso-dd-target @* @sp 1 Consider to also download xorriso-dd-target.sig and to verify the script by @* @sp 1 gpg --verify xorriso-dd-target.sig xorriso-dd-target @* @sp 1 Announce the storage location of the downloaded xorriso-dd-target: @* @sp 1 export XORRISO_DD_TARGET_PATH="$(pwd)" @* @sp 1 The prepared use case in merge_debian_isos uses the xorriso-dd-target option -plug_test under @strong{sudo}, which asks the user for first having the desired USB stick unplugged so that the unwanted devices can get registered. Then it asks the user to plug in the USB stick, so that it gets recognized as desired target. @* xorriso-dd-target evaluates the content and decides whether it looks disposable enough. If so, then it allows merge_debian_isos to write its result to the device. @* @sp 1 merge_debian_isos xorriso-dd-target-plug-test merge_mount/iso \ debian-11.2.0-amd64-DVD-[12345].iso @c man .SS @c man .B Write result directly and unsafely to USB stick @node ExStdio, Files, ExDdTarget, Examples @section Write result directly and unsafely to USB stick On operating systems other than GNU/Linux or with storage devices not acceptable to xorriso-dd-target it is possible to remove all safety precautions beyond those of xorriso, which can be overcome by "blanking" the device. @* So after due evaluation of the device situation and on your very own risk you may use the device path prefix "stdio:", possibly with superuser powers: @* @sp 1 xorriso -outdev stdio:/dev/sdd -blank as_needed merge_debian_isos stdio:/dev/sdd merge_mount/iso \ debian-11.2.0-amd64-DVD-[12345].iso For details about "stdio:" and pseudo-blanking non-optical devices read man xorriso. @* The xorriso run in merge_debian_isos ignores locally defined xorriso startup files (by command -no_rc). @node Files, Environ, Examples, Top @chapter Files @c man .SH FILES For now, no files are defined for configuration. @c man .SH ENVIRONMENT @node Environ, Seealso, Files, Top @chapter Environ The following environment variables influence the program behavior: @* Exported non-empty variable MERGE_DATE enforces a particular date string in the text which gets prepended to /README.txt . @* Exported non-empty variable MERGE_FOR_DIST enforces the use of a particular directory in /dists of iso1. Normally only one such directory is found and thus no need to set MERGE_FOR_DIST. @* Exported non-empty variable MERGE_KEEP_ISO prevents the removal of result_iso after xorriso indicated failure of production. @* Exported non-empty variable XORRISO gives the path to a xorriso binary, which will be used instead of the system-wide installed xorriso binary. This may be needed if installed xorriso is older than 1.4.2. @* If XORRISO is set to "dummy" then no new ISO will emerge. @* Exported non-empty variable XORRISO_DD_TARGET_PATH names the directory where to find xorriso-dd-target, which evaluates the suitability of result_iso devices or does the plug-test dialog. @* @c man .SH SEE ALSO @c man .BR xorriso(1), @c man .BR xorriso-dd-target(1), @c man-ignore-lines begin @node Seealso, Bugreport, Environ, Top @chapter See also xorriso(1), xorriso-dd-target(1) @c man-ignore-lines end @c man .SH BUGS @node Bugreport, Legal, Seealso, Top @chapter Reporting bugs @cindex Bugs, reporting @cindex Problems, reporting To report bugs, request help, or suggest enhancements for @command{merge_debian_isos}, please send electronic mail to the public list @email{bug-xorriso@@gnu.org}. If more privacy is desired, mail to @email{scdbackup@@gmx.net}. @* @sp 1 Please describe what you expect the program to do, the program arguments which you used, the messages of @command{merge_debian_isos}, and the undesirable outcome of your program run. @* @sp 1 Expect to get asked more questions before solutions can be proposed. @c man .SH AUTHOR @node Legal, CommandIdx, Bugreport, Top @chapter Author, Copyright, Credits @section Author Thomas Schmitt <scdbackup@@gmx.net> @* for libburnia-project.org @c man .SH COPYRIGHT @section Copyright Copyright (c) 2023 Thomas Schmitt @* Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of merge_debian_isos. If you make use of the license to derive modified versions of merge_debian_isos then you are entitled to modify this text under that same license. @c man .SH CREDITS @section Credits @command{merge_debian_isos} was originally developed with advise and testing by Zhang Boyang in the course of Debian bug #1011343. Steve McIntyre provided information about various file aspects of debian-cd ISO images. @c man-ignore-lines begin @node CommandIdx, ConceptIdx, Legal, Top @chapter Alphabetic Options List @printindex ky @node ConceptIdx, Top, CommandIdx, Top @chapter Alphabetic List of Concepts and Objects @printindex cp @c man-ignore-lines end @bye /* Little test program for libisoburn. It grows an iso filesystem on a disc. Copyright 2007 Vreixo Formoso Lopes <metalpain2002@yahoo.es> and Thomas Schmitt <scdbackup@gmx.net> */ #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <getopt.h> #include <err.h> #include <string.h> #include <errno.h> #include <sys/stat.h> #include <libisofs/libisofs.h> #include <libburn/libburn.h> #include "../src/libisoburn.h" const char * const optstring = "JRh"; extern char *optarg; extern int optind; /** Activates the usage of function graft_point() rather than plain iso_tree_radd_dir() from libisofs */ #define With_graft_poinT 1 static int graft_point(struct iso_volume *volume, const char *disk_path, const char *img_path, struct iso_tree_radd_dir_behavior *behav) { char path[4096], *apt, *npt; struct iso_tree_node_dir *dir; struct iso_tree_node *node; int done= 0, is_dir= 0; struct stat stbuf; strncpy(path, img_path, sizeof(path)-1); path[sizeof(path)-1]= 0; apt= npt= path; if(lstat(disk_path, &stbuf) == -1) { fprintf(stderr, "Cannot determine attributes of '%s' : %s (%d)\n", disk_path, (errno > 0 ? strerror(errno) : "unknown error"), errno); return(0); } if(S_ISDIR(stbuf.st_mode)) is_dir= 1; else if(!(S_ISREG(stbuf.st_mode) || S_ISLNK(stbuf.st_mode))) { fprintf(stderr, "File object '%s' is of non-supported file type\n", disk_path); return(0); } dir= iso_volume_get_root(volume); if(dir==NULL) { fprintf(stderr, "While grafting '%s' : no root node available\n", img_path); return(0); } for(npt= apt; !done; apt= npt+1) { npt= strchr(apt, '/'); if(npt==NULL) { npt= apt+strlen(apt); done= 1; } else *npt= 0; if(*apt==0) { *apt= '/'; apt++; continue; } node= iso_tree_volume_path_to_node(volume,path); if(node!=NULL) { if(iso_tree_node_get_type(node)!=LIBISO_NODE_DIR) { fprintf(stderr, "While grafting '%s' : '%s' is not a directory\n", img_path, path); return(0); } dir= (struct iso_tree_node_dir *) node; } else { dir= iso_tree_add_dir(dir, apt); if(dir==NULL) { fprintf(stderr, "While grafting '%s' : could not insert '%s'\n", img_path, path); return(0); } } if(done) { if(is_dir) { iso_tree_radd_dir(dir, disk_path, behav); } else { node= iso_tree_add_node(dir, disk_path); if(node == NULL) { fprintf(stderr, "While grafting '%s'='%s' : libisofs_errno = %d\n", img_path, disk_path, libisofs_errno); } } } else *npt= '/'; } fprintf(stderr, "NOTE: added %s '%s'='%s'\n", (is_dir ? "directory" : "node"), img_path, disk_path); return(1); } static void usage() { printf("test [OPTIONS] DRIVE DIRECTORY\n"); } static void help() { printf( "Options:\n" " -J Add Joliet support\n" " -R Add Rock Ridge support\n" " -h Print this message\n" ); } int main(int argc, char **argv) { struct burn_drive_info *drives; struct iso_volset *volset; struct burn_drive *drive; struct burn_disc *disc; enum burn_disc_status state; struct isoburn_read_opts ropts; struct isoburn_source_opts sopts; int c; struct iso_tree_radd_dir_behavior behav = {0,0,0}; int flags=0; int ret=0, i; int size, free_bytes; char *status_text; 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 '?': usage(); exit(1); break; } } if (argc < optind + 1) { fprintf(stderr, "Please supply device name\n"); usage(); exit(1); } if (argc < optind + 2) { fprintf(stderr, "Please supply directory to add to disc\n"); usage(); exit(1); } if (!isoburn_initialize()) { fprintf(stderr, "Can't init libisoburn\n"); exit(1); } /* TODO change this. maybe we can add wrapp in libisoburn */ iso_msgs_set_severities("NEVER", "DEBUG", "libisofs : "); burn_msgs_set_severities("NEVER", "DEBUG", "libburn : "); burn_set_signal_handling("libisoburn/test/test : ", NULL, 0); printf("Growing drive %s\n", argv[optind]); if (isoburn_drive_scan_and_grab(&drives, argv[optind], 1) <= 0) { fprintf(stderr, "Can't open device. Are you sure it is a valid drive?\n"); exit(1); } drive = drives[0].drive; /* check for invalid state */ state = isoburn_disc_get_status(drive); if (state != BURN_DISC_BLANK && state != BURN_DISC_APPENDABLE) { fprintf(stderr, "Unsuitable disc status\n"); goto exit_cleanup; } /* fill read opts */ memset(&ropts, sizeof(ropts), 0); ropts.norock = 0; ropts.nojoliet = 0; ropts.preferjoliet = 0; ropts.uid = 0; ropts.gid = 0; ropts.mode = 0555; ropts.pretend_blank= 0; if (isoburn_read_volset(drive, &ropts, &volset) <= 0) { fprintf(stderr, "Can't read volset\n"); goto exit_cleanup; } #ifdef With_graft_poinT for (i = optind + 1; i < argc; i++) { if (graft_point(iso_volset_get_volume(volset, 0), argv[i], argv[i], &behav) <= 0) { fprintf(stderr, "Canot graft '%s'\n", argv[optind+1]); goto exit_cleanup; } } #else struct iso_tree_node_dir *root; root = iso_volume_get_root(iso_volset_get_volume(volset, 0)); /* add a new dir */ iso_tree_radd_dir(root, argv[optind+1], &behav); #endif /* ! With_graft_poinT */ sopts.level = 2; sopts.flags = flags; sopts.relaxed_constraints = 0; sopts.copy_eltorito = 1; sopts.no_cache_inodes = 0; sopts.sort_files = 1; sopts.default_mode = 0; sopts.replace_dir_mode = 0; sopts.replace_file_mode = 0; sopts.replace_uid = 0; sopts.replace_gid = 0; sopts.dir_mode = 0555; sopts.file_mode = 0444; sopts.gid = 0; sopts.uid = 0; sopts.input_charset = NULL; sopts.ouput_charset = NULL; if (isoburn_prepare_disc(drive, &disc, &sopts) <= 0) { fprintf(stderr, "Can't prepare disc\n"); goto volset_cleanup; } /* a. write the new image */ printf("Adding new data...\n"); { struct burn_write_opts *burn_options; struct burn_progress progress; burn_options = burn_write_opts_new(drive); burn_drive_set_speed(drive, 0, 0); burn_write_opts_set_underrun_proof(burn_options, 1); /* ok, write the new track */ isoburn_disc_write(burn_options, disc); burn_write_opts_free(burn_options); while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) usleep(100002); while (burn_drive_get_status(drive, &progress) != BURN_DRIVE_IDLE) { printf("Writing: sector %d of %d", progress.sector, progress.sectors); ret = isoburn_get_fifo_status(drive, &size, &free_bytes, &status_text); if (ret > 0 ) printf(" [fifo %s, %2d%% fill]", status_text, (int) (100.0 - 100.0 * ((double) free_bytes) / (double) size)); printf("\n"); sleep(1); } } /* b. write the new vol desc */ printf("Writing the new vol desc...\n"); if (isoburn_activate_session(drive) <= 0) { fprintf(stderr, "Ups, new vol desc write failed\n"); } ret= 0; volset_cleanup:; /* iso_volset_free(volset); */ exit_cleanup:; isoburn_drive_release(drive, 0); isoburn_finish(); exit(ret); } /* <<< this file is on its way out #define ISOBURN_MAJOR_VERSION @ISOBURN_MAJOR_VERSION@ #define ISOBURN_MINOR_VERSION @ISOBURN_MINOR_VERSION@ #define ISOBURN_MICRO_VERSION @ISOBURN_MICRO_VERSION@ */ #!/bin/sh # # check_debian_iso, copyright 2011,2024 Thomas Schmitt <scdbackup@gmx.net> # License: GPLv2 or later # Tested on: Little-endian GNU/Linux with bash # Little-endian FreeBSD-8 with sh and "md5 -q" # Little-endian Solaris 5.11 with ksh93 # Big-endian GNU/Linux with bash prog=`basename "$0"` usage() { echo "Usage: $prog Checksum_file [U:]Item [Image_file] [Checksum_command]" >&2 echo "Reads the checksum of a Debian installation image from Checksum_file" >&2 echo "and compares it with the ISO 9660 image in Image_file. Suitable" >&2 echo "for verifying optical media, because trailing garbage is ignored." >&2 echo "The Item in the Checksum_file is depicted either by its complete" >&2 echo "file name (e.g. debian-6.0.3-amd64-CD-1.iso) or by a text piece" >&2 echo "between '-' and '.iso' in the file name. The first match is used." >&2 echo "Text pieces for debian-update images must be prefixed by 'U:'." >&2 echo "If no Image_file is given, then the item file name is used instead." >&2 echo "Checksum_command is normally deduced from Checksum_file name." >&2 echo "It must read data from standard input and its first word written" >&2 echo "to standard output must be the checksum. Default commands are" >&2 echo "md5sum, sha1sum, sha256sum, sha512sum." >&2 echo "Examples:" >&2 echo " $prog MD5SUMS debian-6.0.3-amd64-netinst.iso" >&2 echo " $prog MD5SUMS netinst" >&2 echo " $prog MD5SUMS debian-6.0.3-amd64-DVD-1.iso /dev/dvd" >&2 echo " $prog MD5SUMS 1 /dev/dvd" >&2 echo " $prog MD5SUMS 2 /dev/dvd" >&2 echo " $prog MD5SUMS U:1 /dev/dvd" >&2 echo " $prog MD5SUMS kde-CD-1 /dev/cdrom" >&2 echo " $prog SHA512SUMS businesscard /dev/cdrom" >&2 echo " $prog MD5SUMS 1 /dev/cd0 'md5 -q'" >&2 } if test -z "$1" -o "$1" = "-h" -o "$1" = "--help" -o -z "$2" then usage exit 1 fi sums="$1" vol="$2" file="$3" checksummer=md5sum if test -n "$4" then checksummer="$4" else base=`basename "$sums"` if test "$base" = "SHA1SUMS" then checksummer=sha1sum elif test "$base" = "SHA256SUMS" then checksummer=sha256sum elif test "$base" = "SHA512SUMS" then checksummer=sha512sum fi fi update="" update_v="-v" use_fgrep="" if echo "$vol" | grep '^debian-.*\.iso$' >/dev/null then use_fgrep=1 elif echo "$vol" | grep '^U:' >/dev/null then update=" update" update_v="" vol=`echo "$vol" | sed -e 's/^U://'` fi if test -n "$use_fgrep" then line_from_list=`fgrep "$vol" "$sums" | head -1` else line_from_list=`grep '.*-'"$vol"'\.iso$' "$sums" | grep $update_v "update" | head -1` fi sum_from_list=`echo "$line_from_list" | awk '{print $1}'` name_from_list=`echo "$line_from_list" | awk '{print $2}'` if test -z "$sum_from_list" then if test -n "$use_fgrep" then echo "Could not find item '${vol}' in '$sums'" >&2 else echo "Could not find$update item '.*-${vol}.iso' in '$sums'" >&2 fi exit 4 fi if test -z "$file" then file="$name_from_list" fi # Logical block size is assumed with 2048 bytes. Neither genisoimage # nor xorriso produce other sizes, and even the Linux kernel seems to # have this size hardcoded. # At byte 16 * 2048 starts the Primary Volume Descriptor (superblock) # of the image. The magic number values should be ECMA-119 Volume # Descriptor Type 0x01 and Standard Identifier "CD001". # The way how these 6 bytes group to 16-bit words indicates the byte # sex (endianness) of the local machine. The ECMA-119 Volume Space Size # is stored as little-endian 32-bit number at PVD byte 80, and as big-endian # 32-bit number at PVD byte 84. # od -d is used because it guarantees unsigned integer of predictable # size. Formats -i and -l depend on sizeof(int). magic=`(dd if="$file" bs=2048 skip=16 count=1 | dd bs=1 count=6 | od -x | head -1 | \ awk '{print $2 " " $3 " " $4}') 2>/dev/null` if test "$magic" = "4301 3044 3130" then lo=`(dd if="$file" bs=2048 skip=16 count=1 | \ dd bs=1 skip=80 count=2 | od -d | head -1 | \ awk '{print $2}') 2>/dev/null` hi=`(dd if="$file" bs=2048 skip=16 count=1 | \ dd bs=1 skip=82 count=2 | od -d | head -1 | \ awk '{print $2}') 2>/dev/null` elif test "$magic" = "0143 4430 3031" then lo=`(dd if="$file" bs=2048 skip=16 count=1 | \ dd bs=1 skip=86 count=2 | od -d | head -1 | \ awk '{print $2}') 2>/dev/null` hi=`(dd if="$file" bs=2048 skip=16 count=1 | \ dd bs=1 skip=84 count=2 | od -d | head -1 | \ awk '{print $2}') 2>/dev/null` elif test -e "$file" then echo "Does not look like an ISO 9660 filesystem: '$file' magic='$magic'" >&2 exit 2 else echo "File not found: '$file'" >&2 exit 5 fi # The two 16 bit numbers, which are of the appropriate byte sex, # get combined to a 32 bit number. blocks=`expr "$lo" + "$hi" '*' 65536` echo "Piping $blocks blocks of '$file' through '$checksummer'" >&2 echo "to verify checksum list item '$name_from_list'." >&2 sum_from_file=`dd if="$file" bs=2048 count="$blocks" | $checksummer | head -1 | awk '{print $1}'` if test "$sum_from_list" = "$sum_from_file" then echo "Ok: '$file' matches$update '$name_from_list' in '$sums'" else echo "Found: $sum_from_file" >&2 echo "Expected: $sum_from_list" >&2 echo "MISMATCH: '$file' checksum differs from '$name_from_list' in '$sums'" exit 3 fi exit 0 -----BEGIN PGP ARMORED FILE----- Version: GnuPG v1 Comment: Use "gpg --dearmor" for unpacking iEYEABECAAYFAmbYVioACgkQ6cvfwKvAqFS4IQCeJE88Pf75Lj8Ca83ue80nNR4M Xn4AniMkFTsCsd4DfLHmgXjpeFPtInkN =OytZ -----END PGP ARMORED FILE----- ˆF��fØV*� éËßÀ«À¨T¸!�ž$O<=þù.?kÍî{Í'5 ^~�ž#$;±Þ|±æxéxSí"y #!/bin/sh # Copyright (c) 2019 - 2023 # Nio Wiklund alias sudodus <nio dot wiklund at gmail dot com> # Thomas Schmitt <scdbackup@gmx.net> # Provided under GPL version 2 or later. # All names of variables and functions begin by "xdt_" in order to facilitate # the re-use of this code by inclusion or forking and expansion. # Before using most of the functions it is necessary to run # xdt_init ; xdt_reset_job # The only function which may be run earlier is xdt_set_lang_c . # The function xtd_main gets run by the code at the end of this file # if $no_xorriso_dd_target_run is empty. It implements what xdt_print_usage # announces. xdt_print_version() { echo "xorriso-dd-target , version 1.5.7 , 2023.06.07.200919" } ## This obtrusive setting shall make the script safe against exotic locales. ## Supposed to stabilize grep expression interpretation and error messages. ## It is optional but highly advisable. xdt_set_lang_c() { export LANG=C export LC_ALL=C return 0 } ## This function has to be called before any real work can be done. xdt_init() { # Check whether we are on GNU/Linux if uname -s | grep -v '^Linux' >/dev/null then echo "This program is entirely specialized on Linux kernel device names." >&2 echo "Found to be on: '$(uname -s)'" >&2 return 2 fi # Accept sudo-executable commands only in well known directories. # (Listed with increasing priority.) xdt_lsblk_cmd= xdt_dd_cmd= xdt_umount_cmd= if test "$(whoami)" = "root" then xdt_sudo_x_dir_list="/usr/bin /bin /usr/sbin /sbin" else xdt_sudo_x_dir_list="/usr/sbin /sbin /usr/bin /bin" fi for xdt_i in $xdt_sudo_x_dir_list do if test -x "$xdt_i"/lsblk then xdt_lsblk_cmd="$xdt_i"/lsblk fi if test -x "$xdt_i"/dd then xdt_dd_cmd="$xdt_i"/dd fi if test -x "$xdt_i"/umount then xdt_umount_cmd="$xdt_i"/umount fi done if test -z "$xdt_lsblk_cmd" then echo "No executable program lsblk found in: $xdt_sudo_x_dir_list" >&2 return 5 fi return 0 } xdt_print_usage() { echo "usage: $0 [options] [device_name [device_name ...]]" echo echo "Looks on GNU/Linux for USB and Memory Card devices and evaluates" echo "whether the found devices are plausible targets for image copying." echo "If no device names and no -list_all are given, then a plain list of" echo "advisable device names is printed to stdout. One per line." echo "Device names must not begin by '-' and must be single words. They must" echo "not begin by '/dev/'. E.g. 'sdc' is valid, '/dev/sdc' is not valid." echo "If device names are given, then they get listed with advice shown." echo "If one of the given device names gets not advised, the exit value is 1." echo echo "The option -plug_test can determine the desired target device by" echo "inquiring the system with unplugged device and then with plugged one." echo echo "Only if option -DO_WRITE is given and -list_all is not, and if exactly" echo "one advisable device is listed, it really gets overwritten by the" echo "file content of the given -image_file. In this case the exit value" echo "is zero if writing succeeded, non-zero else." echo "Option -dummy prevents this kind of real action and rather shows the" echo "unmount and write commands on stdout." echo echo "Options:" echo " -plug_test Find the target device by asking the user to press" echo " Enter when the desired target is _not_ plugged in," echo " to then plug it in, and to press Enter again." echo " This overrides device names and option -list_all." echo " The found device is then shown with advice, vendor," echo " and model. Option -DO_WRITE is obeyed if given." echo " -list_all Print list of all found devices with advice, vendor" echo " and model. One per line. Ignore any device names." echo " Ignore -DO_WRITE." echo " -list_long With each line printed by -list_all or a submitted" echo " device name, let lsblk print info which led to the" echo " shown reasons." echo " -with_vendor_model Print vendor and model with each submitted device" echo " name." echo " -max_size n[M|G|T] Set upper byte size limit for advisable devices." echo " Plain numbers get rounded down to full millions." echo " Suffix: M = million, G = billion, T = trillion." echo " Be generous to avoid problems with GB < GiB." echo " -min_size n[M|G|T] Set lower byte size limit for advisable devices." echo " After processing like with -max_size, one million" echo " gets added to the size limit." echo " -look_for_iso Demand presence of an ISO 9660 filesystem. If so," echo " any further filesystem type is acceptable on that" echo " device. Else only ISO 9660 and VFAT are accepted." echo " -with_sudo Run '$xdt_lsblk_cmd -o FSTYPE' by sudo." echo " If no filesystems are detected and the program" echo " has no superuser power, the device is not advised." echo " If -DO_WRITE is given, run umount and dd by sudo." echo " -trust_lsblk_udev Suppress the reason no_fs_while_not_su- if lsblk" echo " is linked with libudev.so. In this case it is" echo " likely that lsblk can retrieve FSTYPE even if" echo " run by a non-priviledged user." echo " -image_file PATH Set the path of the image file which shall be" echo " written to a device. Its size will be set as" echo " -min_size." echo " -DO_WRITE Write the given -image_file to the one advisable" echo " device that is found. If more than one such device" echo " is found, then they get listed but no writing" echo " happens. In this case, re-run with one of the" echo " advised device names to get a real write run." echo " -no_pacifier Do not use dd options to print progress messages" echo " and to perform synchronized output." echo " -dummy Report the -DO_WRITE actions but do not perform" echo " them." echo " -dummy_force If a single device name is given, do a run of" echo " -dummy -DO_WRITE even against the advice of" echo " this program. This probably shows you ways to" echo " shoot your own foot." echo " -version Print version text and then end the program." echo " -help Print this text to stdout and then end the program." echo "Examples:" echo " $0 -with_sudo -list_all" echo " $0 sdc" echo " $0 -with_sudo -image_file debian-live-10.0.0-amd64-xfce.iso -DO_WRITE -dummy" echo " $0 -with_sudo -image_file debian-live-10.0.0-amd64-xfce.iso -DO_WRITE -plug_test" echo return 0 } # Roughly convert human readable sizes and plain numbers to 1 / million xdt_round_down_div_million() { sed \ -e 's/^[0-9][0-9][0-9][0-9][0-9][0-9]$/0/' \ -e 's/^[0-9][0-9][0-9][0-9][0-9]$/0/' \ -e 's/^[0-9][0-9][0-9][0-9]$/0/' \ -e 's/^[0-9][0-9][0-9]$/0/' \ -e 's/^[0-9][0-9]$/0/' \ -e 's/^[0-9]$/0/' \ -e 's/\.[0-9]*//' \ -e 's/[0-9][0-9][0-9][0-9][0-9][0-9]$//' \ -e 's/[Mm]$//' \ -e 's/[Gg]$/000/' \ -e 's/[Tt]$/000000/' return 0 } ## Check for harmless name or number in program argument xdt_check_parameter() { if test "X$2" = "Xdevice_name" then if echo "X$1" | grep '[^A-Za-z0-9_/-]' >/dev/null then echo "SORRY: Given device name contains unexpected character. Ok: [A-za-z0-9_/-]" >&2 return 12 elif echo "X$1" | grep '^X-' >/dev/null then echo "SORRY: Given device name begins by '-' character." >&2 return 15 fi elif test "X$2" = "Ximage_file" then if echo "X$1" | grep '[$`[*?<>|&!{\]' >/dev/null then echo "SORRY: Given image file name contains unexpected character. Not ok: "'[$`[*?<>|&!{\]' >&2 return 15 elif echo "X$1" | grep '^X[-(]' >/dev/null then echo "SORRY: Given image file name begins by problematic character. Not ok: "'[-(]' >&2 return 15 fi else if echo "X$1" | grep -v '^X[0-9][0-9]*[0-9MGTmgt]$' >/dev/null then echo "SORRY: Number for $2 too short or bad character. Ok: [0-9][0-9MGTmgt]" >&2 return 14 fi fi return 0 } ### Assessing arguments and setting up the job # Settings xdt_reset_job() { xdt_list_all= xdt_do_list_long= xdt_show_reasons= xdt_look_for_iso= xdt_devs= xdt_devs_named= xdt_max_size= xdt_with_vendor_model= xdt_with_sudo= xdt_image_file= xdt_do_write= xdt_dummy_run= xdt_dummy_force= xdt_no_pacifier= xdt_do_plug_test= xdt_trust_lsblk_udev= # Status xdt_sudo_cmd= xdt_have_su_power= xdt_end_after_setup= xdt_dev_end= xdt_dd_bs="bs=1M" xdt_dd_silent= return 0 } ## Predict superuser power. Possibly enable sudo with lsblk -o FSTYPE and dd. ## Also predict whether lsblk -o FSTYPE will bring results as non-root. xdt_predict_su_power() { if test "$(whoami)" = "root" then xdt_have_su_power=y elif test -n "$xdt_with_sudo" then echo "Testing sudo to possibly get password prompting done now:" >&2 if sudo "$xdt_lsblk_cmd" -h >/dev/null then echo "sudo $xdt_lsblk_cmd seems ok." >&2 echo >&2 xdt_sudo_cmd=sudo xdt_have_su_power=y else echo "FAILURE: Cannot execute program $xdt_lsblk_cmd by sudo" >&2 return 11 fi fi # lsblk linked with libudev.so usually can obtain filesystem info # without superuser powers. if test -n "$xdt_trust_lsblk_udev" then if ldd "$xdt_lsblk_cmd" | grep '\blibudev\.so' >/dev/null then xdt_lsblk_fs_wo_su=y fi fi return 0 } ## Sets xdt_variables according to its arguments xdt_arg_interpreter() { xdt_next_is= for xdt_i in "$@" do # The next_is option parameter readers get programmed by the -options if test "$xdt_next_is" = "max_size" then xdt_check_parameter "$xdt_i" -max_size || return "$?" xdt_max_size="$(echo "$xdt_i" | xdt_round_down_div_million)" xdt_next_is= elif test "$xdt_next_is" = "min_size" then xdt_check_parameter "$xdt_i" -min_size || return "$?" xdt_min_size="$(echo "$xdt_i" | xdt_round_down_div_million)" xdt_min_size="$(expr $xdt_min_size + 1)" xdt_next_is= elif test "$xdt_next_is" = "image_file" then xdt_check_parameter "$xdt_i" image_file || return "$?" xdt_image_file="$xdt_i" xdt_min_size="$(stat -c '%s' "$xdt_i" \ | xdt_round_down_div_million)" if test -z "$xdt_min_size" then echo "FAILURE: Cannot obtain size of -image_file '$xdt_i'" >&2 return 13 else xdt_min_size="$(expr $xdt_min_size + 1)" fi xdt_next_is= elif test "X$xdt_i" = "X-list_all" then xdt_list_all=y xdt_with_vendor_model=y xdt_show_reasons=y elif test "X$xdt_i" = "X-list_long" then xdt_do_list_long=y elif test "X$xdt_i" = "X-plug_test" then xdt_do_plug_test=y elif test "X$xdt_i" = "X-max_size" then xdt_next_is="max_size" elif test "X$xdt_i" = "X-min_size" then xdt_next_is="min_size" elif test "X$xdt_i" = "X-with_vendor_model" then xdt_with_vendor_model=y elif test "X$xdt_i" = "X-look_for_iso" then xdt_look_for_iso=y elif test "X$xdt_i" = "X-trust_lsblk_udev" then xdt_trust_lsblk_udev=y elif test "X$xdt_i" = "X-with_sudo" then xdt_with_sudo=y elif test "X$xdt_i" = "X-image_file" then xdt_next_is="image_file" elif test "X$xdt_i" = "X-dummy" then xdt_dummy_run=y elif test "X$xdt_i" = "X-dummy_force" then xdt_dummy_run=y xdt_do_write=y xdt_dummy_force=y elif test "X$xdt_i" = "X-DO_WRITE" then xdt_do_write=y elif test "X$xdt_i" = "X-no_pacifier" then xdt_no_pacifier=y elif test "X$xdt_i" = "X-version" then xdt_print_version xdt_end_after_setup=y return 0 elif test "X$xdt_i" = "X-help" then xdt_print_usage xdt_end_after_setup=y return 0 elif echo "X$xdt_i" | grep -v '^X-' >/dev/null then xdt_check_parameter "$xdt_i" device_name || return "$?" xdt_devs_named=y xdt_devs="$xdt_devs $xdt_i" xdt_show_reasons=y else echo "$0 : Unknown option: '$xdt_i'" >&2 echo >&2 echo "For a help text run: $0 -help" >&2 return 16 fi done xdt_predict_su_power || return "$?" # Prepare for using dd pacifier if desired and available # Prepare for using silent dd with GPT backup erasure if available xdt_dd_bs="bs=1M" xdt_dd_silent= if test -n "$xdt_dd_cmd" then if test -z "$xdt_no_pacifier" && \ "$xdt_dd_cmd" if=/dev/zero of=/dev/null count=1 \ bs=1M status=progress oflag=dsync 2>/dev/null then xdt_dd_bs="bs=1M status=progress oflag=dsync" fi if "$xdt_dd_cmd" if=/dev/zero of=/dev/null count=1 bs=512 status=none \ 2>/dev/null then xdt_dd_silent="status=none" fi fi return 0 } ## Obtain a blank separated list of top-level names which do not look like ## CD, floppy, RAM dev, or loop device. xdt_collect_devices() { "$xdt_lsblk_cmd" -d -n -o NAME \ | grep -v '^sr[0-9]' \ | grep -v '^fd[0-9]' \ | grep -v '^zram[0-9]' \ | grep -v '^loop[0-9]' \ | tr '\n\r' ' ' return 0 } ## Let lsblk print extra info for the given devices xdt_list_long() { if test -z "$xdt_do_list_long" then return 0 fi $xdt_sudo_cmd "$xdt_lsblk_cmd" -o NAME,SIZE,FSTYPE,TRAN,LABEL \ /dev/"$1" echo return 0 } ## Trying to find the desired device by watching plug-in effects xdt_plug_in_watcher() { # How long to wait for a first device to appear, how long to wait for more xdt_wait_span_0=10 xdt_wait_span_1=5 xdt_found_devices= echo >&2 echo "Caused by option -plug_test: Attempt to find the desired device" >&2 echo "by watching it appear after being plugged in." >&2 echo >&2 echo "Step 1:" >&2 echo "Please make sure that the desired target device is plugged _out_ now." >&2 echo "If it is currently plugged in, make sure to unmount all its filesystems" >&2 echo "and then unplug it." >&2 echo "Press the Enter key when ready." >&2 read xdt_dummy xdt_old_device_list=' '$(xdt_collect_devices)' ' # <<< Mock-up to save USB socket wear-off by erasing items from old_device_list # <<< Their presence in new_device_list will let them appear as fresh plugs # xdt_old_device_list=' '$(echo -n $xdt_old_device_list | sed -e 's/sdc//')' ' echo "Found and noted as _not_ desired: $xdt_old_device_list" >&2 echo >&2 echo "Step 2:" >&2 echo "Please plug in the desired target device and then press the Enter key." >&2 read xdt_dummy echo -n "Waiting up to $xdt_wait_span_0 seconds for a new device to be listed ..." >&2 xdt_end_time="$(expr $(date +'%s') + "$xdt_wait_span_0")" while test $(date +'%s') -le "$xdt_end_time" do xdt_new_device_list=' '$(xdt_collect_devices)' ' for xdt_i in $xdt_new_device_list do if echo "$xdt_old_device_list $xdt_found_devices " \ | grep -F -v ' '"$xdt_i"' ' >/dev/null then echo " found: $xdt_i" >&2 xdt_found_devices="$xdt_found_devices $xdt_i" xdt_end_time=$(expr $(date +'%s') + "$xdt_wait_span_1") echo -n "Now waiting $xdt_wait_span_1 seconds to let it settle ..." >&2 fi done sleep 1 echo -n '.' >&2 done echo >&2 if test -z "$xdt_found_devices" then echo "SORRY: No new candidate device was found." >&2 return 8 fi xdt_num=$(echo $xdt_found_devices | wc -w) if test "$xdt_num" -gt 1 then echo "SORRY: More than one new candidate device appeared: $xdt_found_devices" >&2 return 9 fi echo "Found and noted as desired device: $xdt_found_devices" >&2 if test -n "$xdt_devs" then echo "(-plug_test is overriding device list given by arguments: $xdt_devs )" >&2 fi if test -n "$xdt_list_all" then echo "(-plug_test is overriding -list_all)" >&2 xdt_list_all= fi xdt_devs_named=y xdt_with_vendor_model=y xdt_show_reasons=y xdt_devs=$(echo -n $xdt_found_devices) echo >&2 return 0 } ## Evaluation of available devices and suitability xdt_list_devices() { if test -n "$xdt_list_all" then xdt_devs= fi if test -z "$xdt_devs" then # Obtain list of top-level names which do not look like CD, floppy, RAM dev xdt_devs=$(xdt_collect_devices) fi xdt_not_advised=0 for xdt_name in $xdt_devs do # Collect reasons xdt_yucky= xdt_reasons= xdt_good_trans= xdt_good_fs= xdt_bad_trans= xdt_bad_fs= # Unwanted device name patterns if (echo "$xdt_name" | grep '^sd[a-z][1-9]' >/dev/null) \ || (echo "$xdt_name" | grep '^mmcblk.*p[0-9]' >/dev/null) \ || (echo "$xdt_name" | grep '^nvme.*p[0-9]' >/dev/null) then xdt_yucky=y xdt_reasons="${xdt_reasons}looks_like_disk_partition- " elif echo "$xdt_name" | grep '^sr[0-9]' >/dev/null then xdt_yucky=y xdt_reasons="${xdt_reasons}looks_like_cd_drive- " elif echo "$xdt_name" | grep '^fd[0-9]' >/dev/null then xdt_yucky=y xdt_reasons="${xdt_reasons}looks_like_floppy- " elif echo "$xdt_name" | grep '^loop[0-9]' >/dev/null then xdt_yucky=y xdt_reasons="${xdt_reasons}looks_like_loopdev- " elif echo "$xdt_name" | grep '^zram[0-9]' >/dev/null then xdt_yucky=y xdt_reasons="${xdt_reasons}looks_like_ramdev- " fi # >>> recognize the device from which Debian Live booted # Connection type. Normally by lsblk TRAN, but in case of mmcblk artificial. if echo "$xdt_name" | grep '^mmcblk[0-9]' >/dev/null then xdt_transports="mmcblk" elif echo "$xdt_name" | grep -F "/" >/dev/null then xdt_transports=not_an_expected_name xdt_reasons="${xdt_reasons}name_with_slash- " else xdt_transports=$("$xdt_lsblk_cmd" -n -o TRAN /dev/"$xdt_name") fi for xdt_trans in $xdt_transports do if test "$xdt_trans" = "usb" || test "$xdt_trans" = "mmcblk" then xdt_good_trans="${xdt_trans}+" elif test -n "$xdt_trans" then xdt_bad_trans="$xdt_trans" xdt_yucky=y if test "$xdt_transports" = "not_an_expected_name" then xdt_dummy=dummy else if echo "$xdt_reasons" | grep -F -v "not_usb" >/dev/null then xdt_reasons="${xdt_reasons}not_usb- " fi fi fi done if test -z "$xdt_good_trans" && test -z "$xdt_bad_trans" then xdt_yucky=y xdt_reasons="${xdt_reasons}no_bus_info- " elif test -z "$xdt_bad_trans" then xdt_reasons="${xdt_reasons}$xdt_good_trans " fi # Wanted or unwanted filesystem types xdt_fstypes=$($xdt_sudo_cmd "$xdt_lsblk_cmd" -n -o FSTYPE \ /dev/"$xdt_name") if test "$?" -gt 0 then xdt_fstypes="lsblk_fstype_error" fi # Get overview of filesystems xdt_has_iso= xdt_has_vfat= xdt_has_other= for xdt_fstype in $xdt_fstypes do if test "$xdt_fstype" = "iso9660" then xdt_has_iso=y if echo "$xdt_good_fs" | grep -F -v "has_$xdt_fstype" >/dev/null then xdt_good_fs="${xdt_good_fs}has_${xdt_fstype}+ " fi elif test "$xdt_fstype" = "vfat" then xdt_has_vfat=y if echo "$xdt_good_fs" | grep -F -v "has_$xdt_fstype" >/dev/null then xdt_good_fs="${xdt_good_fs}has_${xdt_fstype}+ " fi elif test -n "$xdt_fstype" then xdt_has_other=y if echo "$xdt_bad_fs" | grep -F -v "has_$xdt_fstype" >/dev/null then xdt_bad_fs="${xdt_bad_fs}has_${xdt_fstype}- " fi fi done # Decide whether the found filesystems look dispensible enough xdt_reasons="${xdt_reasons}${xdt_good_fs}${xdt_bad_fs}" if test "${xdt_bad_fs}${xdt_good_fs}" = "" \ && test -z "$xdt_have_su_power" && test -z "$xdt_lsblk_fs_wo_su" then xdt_yucky=y xdt_reasons="${xdt_reasons}no_fs_while_not_su- " elif test -n "$xdt_look_for_iso" then if test -n "$xdt_has_iso" then xdt_reasons="${xdt_reasons}look_for_iso++ " else xdt_yucky=y xdt_reasons="${xdt_reasons}no_iso9660- " fi elif test -n "$xdt_has_other" then xdt_yucky=y fi # Optional tests for size if test -n "$xdt_max_size" || test -n "$xdt_min_size" then xdt_dev_bytes=$("$xdt_lsblk_cmd" -n -b -o SIZE /dev/"$xdt_name" | head -1) xdt_size=$(echo "$xdt_dev_bytes" | xdt_round_down_div_million) if test -z "$xdt_size" then xdt_yucky=y xdt_reasons="${xdt_reasons}lsblk_no_size- " fi fi if test -n "$xdt_max_size" && test -n "$xdt_size" then if test "$xdt_size" -gt "$xdt_max_size" then xdt_yucky=y xdt_reasons="${xdt_reasons}size_too_large- " fi fi if test -n "$xdt_min_size" && test -n "$xdt_size" then if test "$xdt_size" -lt "$xdt_min_size" then xdt_yucky=y xdt_reasons="${xdt_reasons}size_too_small- " fi fi # Now decide overall and report xdt_descr= if test -n "$xdt_with_vendor_model" then xdt_descr=": "$("$xdt_lsblk_cmd" -n -o VENDOR,MODEL \ /dev/"$xdt_name" | tr '\n\r' ' ' | tr -s ' ') fi if test -n "$xdt_yucky" then if test -n "$xdt_show_reasons" then echo "$xdt_name : NO : $xdt_reasons$xdt_descr" xdt_list_long "$xdt_name" fi xdt_not_advised=1 else if test -n "$xdt_show_reasons" then echo "$xdt_name : YES : $xdt_reasons$xdt_descr" xdt_list_long "$xdt_name" else echo "$xdt_name" fi fi done return 0; } ## Puts list of mounted (sub-)devices of $1 into $mounted_devs xdt_list_mounted_of() { xdt_partitions=$("$xdt_lsblk_cmd" -l -n -p -o NAME /dev/"$1" \ | grep -v '^'/dev/"$1"'$' \ | tr '\n\r' ' ') xdt_mounted_devs= for xdt_i in /dev/"$1" $xdt_partitions do # Show the found mount lines and add their device paths to list xdt_mount_line=$(mount | grep '^'"$xdt_i"' ') if test -n "$xdt_mount_line" then echo " $xdt_mount_line" xdt_mounted_devs="$xdt_mounted_devs $xdt_i" fi done return 0 } ## Unmount xdt_mounted_devs (maybe filled by xdt_list_mounted_of) ## $1 : base device name xdt_unmount() { if test -z "$xdt_mounted_devs" then return 0 fi for xdt_i in $xdt_mounted_devs do if test -n "$xdt_dummy_run" then echo " $xdt_sudo_cmd $xdt_umount_cmd $xdt_i" elif $xdt_sudo_cmd "$xdt_umount_cmd" "$xdt_i" then echo "Unmounted: $xdt_i" else echo "FAILURE: Non-zero exit value with: $xdt_sudo_cmd $xdt_umount_cmd $xdt_i" >&2 return 7 fi done # Check again if any mount points still exist if test -z "$xdt_dummy_run" then xdt_list_mounted_of "$1" if test -n "$xdt_mounted_devs" then echo "FAILURE: $xdt_sudo_cmd $xdt_umount_cmd could not remove all mounts: $xdt_mounted_devs" >&2 return 7 fi fi return 0 } ## Does the work of unmounting and dd-ing ## $1 : image file path ## $2 : base device name xdt_write_image() { if test -z "$xdt_umount_cmd" then echo "No executable program umount found in: $xdt_sudo_x_dir_list" >&2 return 6 fi echo "Looking for mount points of $2:" xdt_mounted_devs= xdt_list_mounted_of "$2" if test -n "$xdt_dummy_force" then echo "AGAINST THE ADVICE BY THIS PROGRAM, a daring user could do:" xdt_dummy_run=y elif test -n "$xdt_dummy_run" then echo "Would do if not -dummy:" fi xdt_unmount "$2" || return "$?" if test -z "$xdt_dd_cmd" then echo "No executable program dd found in: $xdt_sudo_x_dir_list" >&2 return 6 fi # Erase possible GPT backup table at end of device # if expr can properly divide device size by 512 xdt_dev_bytes=$("$xdt_lsblk_cmd" -n -b -o SIZE /dev/"${2}" | head -1) if test -n "$xdt_dev_bytes" then xdt_dev_end=$(expr "$xdt_dev_bytes" / 512 2>/dev/null) if test "$(expr "$xdt_dev_end" '*' 512 2>/dev/null)" = "$xdt_dev_bytes" then xdt_dev_end=$(expr "$xdt_dev_end" - 1) else xdt_dev_end= fi fi xdt_gpt_erase_line="$xdt_sudo_cmd $xdt_dd_cmd if=/dev/zero of=/dev/'${2}' bs=512 seek='$xdt_dev_end' count=1 $xdt_dd_silent" xdt_copy_line="$xdt_sudo_cmd $xdt_dd_cmd if='${1}' of=/dev/'${2}' $xdt_dd_bs ; sync" if test -n "$xdt_dummy_run" then if test -n "$xdt_dev_end" then echo " $xdt_gpt_erase_line" fi echo " $xdt_copy_line" else echo "Performing:" if test -n "$xdt_dev_end" then echo " $xdt_gpt_erase_line" $xdt_sudo_cmd "$xdt_dd_cmd" if=/dev/zero of=/dev/"${2}" bs=512 seek="$xdt_dev_end" count=1 $xdt_dd_silent fi echo " $xdt_copy_line" $xdt_sudo_cmd "$xdt_dd_cmd" if="${1}" of=/dev/"${2}" $xdt_dd_bs xdt_ret="$?" sync if test "$xdt_ret" -ne 0 then echo "FAILURE: $xdt_sudo_cmd $xdt_dd_cmd was not successful" >&2 return 18 fi fi if test -n "$xdt_dummy_force" then echo "BE SMART. BE CAUTIOUS. BEWARE." fi return 0 } ## Inspects the result of listing, decides whether writing is advisable, ## and starts the write run xdt_decide_writing() { xdt_with_vendor_model= xdt_show_reasons= xdt_candidates=$(xdt_list_devices | tr '\n\r' ' ') xdt_num_cand=$(echo $xdt_candidates | wc -w) xdt_num_devs=$(echo $xdt_devs| wc -w) if test -n "$xdt_dummy_force" && test "$xdt_num_devs" -ne 1 then echo "SORRY: Refusing -dummy_force with not exactly one device given." >&2 return 10 fi if test -n "$xdt_dummy_force" && test -n "$xdt_dummy_run" \ && test "$xdt_num_cand" -ne 1 then # -dummy_force in a situation where the program would normally refuse echo echo "Overriding any advice because of -dummy_force" xdt_candidates="$xdt_devs" xdt_num_cand=1 elif test -n "$xdt_dummy_force" then # Downgrade -dummy_force to -dummy in order to avoid the ugly warning xdt_dummy_force= xdt_dummy_run=y fi if test "$xdt_num_cand" -eq 1 then if test -n "$xdt_image_file" then if test -n "$xdt_do_plug_test" then echo >&2 echo "Step 3:" >&2 if test -n "$xdt_dummy_run" then echo "This would be the last chance to abort. Enter the word 'yes' to see -dummy report." >&2 else echo "Last chance to abort. Enter the word 'yes' to start REAL WRITING." >&2 fi read xdt_dummy if test "X$xdt_dummy" = "Xyes" || test "X$xdt_dummy" = "X'yes'" \ || test "X$xdt_dummy" = 'X"yes"' then xdt_dummy=dummy else echo "WRITE RUN PREVENTED by user input '$xdt_dummy'." >&2 return 17 fi fi xdt_write_image "$xdt_image_file" $xdt_candidates || return "$?" else xdt_candidates=$(echo $xdt_candidates) if test -n "$xdt_dummy_run" then echo "Would simulate writing to /dev/$xdt_candidates if an -image_file were given." else echo "Would write to /dev/$xdt_candidates if an -image_file were given." fi return 0 fi elif test "$xdt_num_cand" -gt 1 then echo "WILL NOT WRITE ! More than one candidate found for target device:" >&2 xdt_show_reasons=y xdt_with_vendor_model=y xdt_devs="$xdt_candidates" xdt_list_devices >&2 echo "HINT: Unplug the unwanted devices from the machine," >&2 echo " or work with option -plug_test," >&2 echo " or add the desired name out of {$(echo $xdt_candidates | sed -e 's/ /,/g')} as additional argument." >&2 return 3 else if test -n "$xdt_devs_named" then echo "NO CANDIDATE FOR TARGET DEVICE AMONG THE GIVEN NAMES !" >&2 else echo "NO CANDIDATE FOR TARGET DEVICE FOUND !" >&2 fi echo "Overall available devices:" >&2 xdt_list_all=y xdt_list_long= xdt_show_reasons=y xdt_with_vendor_model=y xdt_list_devices >&2 return 4 fi return 0 } ## The main function which runs the others in standalone mode xdt_main() { xdt_set_lang_c xdt_init || return "$?" xdt_reset_job xdt_arg_interpreter "$@" || return "$?" if test -n "$xdt_end_after_setup" then return 0 fi if test -n "$xdt_do_plug_test" then xdt_plug_in_watcher || return "$?" fi xdt_list_devices || return "$?" if test -z "$xdt_list_all" && test -n "$xdt_do_write" then xdt_decide_writing || return "$?" fi if test -n "$xdt_devs_named" then return $xdt_not_advised fi return 0 } if test -z "$no_xorriso_dd_target_run" then xdt_main "$@" || exit "$?" fi .\" Hey, EMACS: -*- nroff -*- .\" .\" IMPORTANT NOTE: .\" .\" The original of this file is kept in xorriso/xorriso-dd-target.texi .\" This here was generated by program xorriso/make_xorriso_1 .\" .\" .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH XORRISO-DD-TARGET 1 "Version 1.5.7, Jun 07, 2023" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp <n> insert n+1 empty lines .\" for manpage-specific macros, see man(7) .nh .SH NAME xorriso\-dd\-target \- Device evaluator and disk image copier for GNU/Linux .SH SYNOPSIS .B xorriso-dd-target [ options ] [ device_names ] .br .SH DESCRIPTION .PP \fBxorriso\-dd\-target\fR evaluates block devices of the Linux kernel whether they are suitable targets for a disk image file and optionally copies the image file to one of them. .br It is specialized on the device names of the \fBLinux kernel\fR and uses the capabilities of util\-linux program \fBlsblk\fR. Therefore it refuses to run on non\-Linux kernels. .br .PP The main purpose of xorriso\-dd\-target is to inspect the device files of disk\-like storage media and to judge whether they look like removable devices with disposable content. .br If a single plausible candidate is detected, then the program is willing to copy a disk image file onto it. This will overwrite or make inaccessible the previous partition table and all previous data content of the target device. .br \fBSuperuser power\fR is often needed for filesystem type identification, for possible unmounting, and for possible image writing. Option \fB\-with_sudo\fR offers a way to gain this power only for those tasks and to run the program elsewise with a normal user's power. .br If a particular disk image file is intended as copy source, then its path should be given by option \-image_file, so that its size can be used as decision criterion. .PP Following are use case descriptions with examples: .br - List plain device names .br - List all devices with reasoning .br - Evaluate particular given devices .br - Detect intended device by plugging .br - Write image to an advised device .br - Show commands for writing to a not advised device .br .SS \fBList plain device names:\fR .br The most simple and most boring use case is a program run without device names and without options \-list_all, \-plug_test, \-DO_WRITE, \-dummy_force. It prints on standard output (stdout) only the names of advisable devices without "/dev/" prefix. One name per line and without any reasoning text. .br The possible sudo password prompt, the message line about sudo, and the empty line after it do not go to stdout. .br Example: .br $ xorriso\-dd\-target \-with_sudo .br Testing sudo to possibly get password prompting done now: .br [sudo] password for thomas: .br sudo /bin/lsblk seems ok. sde .SS \fBList all devices with reasoning:\fR .br For the more curious user, there is option \fB\-list_all\fR which prints the evaluation of each disk\-like device that is listed by program lsblk. Optical drives, floppy disks, RAM block devices, loop devices are excluded, though. .br Each device is shown by one line of the form .br name : advice : reasoning : info .br \fBname\fR is the device name without "/dev/" prefix. .br \fBadvice\fR is either "YES" or "NO". "YES" indicates that the device appears to be pluggable disk\-like, not used as system disk or sincere data storage, and \- if tested \- of sufficient or plausible size. .br \fBreasoning\fR is a blank separated list of words with either suffix '+' for an inviting device property or '\-' for a prohibitive property. Normally a single '\-' reason disqualifies the device from being advisable. Only if option \-look_for_iso is given, a reason "has_XYZ\-" can be overridden by the presence of an ISO 9660 filesystem on the device. .br \fBinfo\fR is composed from VENDOR and MODEL as told by lsblk. .br Option \fB\-list_long\fR causes with each line an additional listing of the information provided by lsblk which led to the shown reasons. .br Example: .br $ xorriso\-dd\-target \-with_sudo \-list_all .br ... .br sda : NO : not_usb\- has_vfat+ has_ext4\- : ATA Samsung SSD 850 .br sdb : NO : not_usb\- has_swap\- has_ext4\- : ATA WDC WD20EFRX\-68A .br sdc : YES : usb+ has_iso9660+ has_vfat+ : Intenso Ultra Line .br sdd : NO : usb+ has_iso9660+ has_vfat+ has_ext2\- : SanDisk Cruzer .SS \fBEvaluate particular given devices:\fR .br If \fBdevice names\fR are given instead of option \-list_all, then only these devices are inspected. Their result gets listed without the ": info" part, unless option \fB\-with_vendor_model\fR is given. .br Device names must not begin by '\-' and must be single words composed of the characters [A\-za\-z0\-9_/\-]. They should not contain '/'. E.g. 'sdc' is valid, '/dev/sdc' is not valid. .br If one of the given device names gets not advised, the exit value is 1. .br It makes few sense to give device names which are not listed by \-list_all. .br Examples: .br $ xorriso\-dd\-target \-with_sudo sdc .br ... .br sdc : YES : usb+ has_iso9660+ has_vfat+ .br $ xorriso\-dd\-target \-with_sudo \-with_vendor_model sdc .br ... .br sdc : YES : usb+ has_iso9660+ has_vfat+ : Intenso Ultra Line .br $ xorriso\-dd\-target sdc .br sdc : NO : usb+ no_fs_while_not_su\- .SS \fBDetect intended device by plugging:\fR .br Option \fB\-plug_test\fR triggers an interactive method to unambiguously determine the intended target device candidate. It consists of 2 or 3 steps. .br \fBStep 1\fR is to have the intended storage device \fBunplugged\fR and to confirm this by pressing the Enter key at the program's prompt. The program will then assess the list of not wanted devices. .br \fBStep 2\fR is to \fBplug in\fR the intended storage device and to confirm this by pressing the Enter key a second time. The program will wait up to 10 seconds for a disk\-like storage device which is not in the list of not wanted devices. The user may wait with key pressing until the device blinking looks like it is ready. .br Only if a single new device is found, the program will go on as if a single device name was given. Option \-list_all and any device names given as arguments will be ignored. .br \fBStep 3\fR happens only if options \-DO_WRITE or \-dummy_force are given. The program asks for a final input of the word 'yes' before real or simulated writing begins. .br Example: .br $ xorriso\-dd\-target \-with_sudo \-plug_test .br ... .br Caused by option \-plug_test: Attempt to find the desired device by watching it appear after being plugged in. .br Step 1: .br Please make sure that the desired target device is plugged _out_ now. .br If it is currently plugged in, make sure to unmount all its filesystems .br and then unplug it. .br Press the Enter key when ready. .br .br Found and noted as _not_ desired: sda sdb sdc .br Step 2: .br Please plug in the desired target device and then press the Enter key. .br .br Waiting up to 10 seconds for a new device to be listed ... found: sdd .br Now waiting 5 seconds to let it settle ......... .br Found and noted as desired device: sdd .br .br sdd : NO : usb+ has_iso9660+ has_vfat+ has_ext2\- : SanDisk Cruzer .SS \fBWrite image to an advised device:\fR .br Only if option \fB\-DO_WRITE\fR is given and \-list_all is not, and if exactly one advisable device is listed, it really gets overwritten by the file content of the given \-image_file. In this case the exit value is zero if writing succeeded, non\-zero else. .br Option \fB\-dummy\fR prevents this kind of real action and rather shows the planned umount and dd commands on stdout. .br Example: .br $ xorriso\-dd\-target \-with_sudo \-plug_test \-DO_WRITE \\ .br \-image_file debian\-live\-10.0.0\-amd64\-xfce.iso .br ... sudo messages and above plug test steps 1 and 2 ... .br .br sde : YES : usb+ has_iso9660+ has_vfat+ .br Step 3: .br Last chance to abort. Enter the word 'yes' to start REAL WRITING. .br yes .br Looking for mount points of sde: .br /dev/sde1 on /mnt/iso type iso9660 (ro,relatime) .br /dev/sde2 on /mnt/fat type vfat (rw,...,errors=remount\-ro) .br Unmounted: /dev/sde1 .br Unmounted: /dev/sde2 .br Performing: .br sudo /bin/dd if=/dev/zero of=/dev/'sde' bs=512 seek='245759999' .br count=1 status=none .br sudo /bin/dd if='debian\-live\-10.0.0\-amd64\-xfce.iso' of=/dev/'sde' .br bs=1M status=progress oflag=dsync ; sync .br ... dd messages ... .br The first dd run shall erase a possible GPT backup header. It is performed only if the local program "expr" can deal with the byte size of the device. .SS \fBShow commands for writing to a not advised device:\fR .br There should be no way to convince xorriso\-dd\-target of writing to a target device which it does not deem advisable. Please report any set of arguments that can be misused for that. .br The outmost complicity to potentially unwise actions is offered by option \fB\-dummy_force\fR. If given together with a single device name or with option \-plug_test it will act like \-dummy \-DO_WRITE with this device, even if it looks not advisable. I.e. it will show the shell commands which the program does not dare to perform. .br Example: .br $ xorriso\-dd\-target \-with_sudo \-list_long \-dummy_force sdd \\ .br \-image_file debian\-live\-10.0.0\-amd64\-xfce.iso .br ... .br sdd : NO : usb+ has_iso9660+ has_vfat+ has_ext2\- .br NAME SIZE FSTYPE TRAN LABEL .br sdd 3.8G iso9660 usb d\-live 9.5.0 xf i386 .br |\-sdd1 1.9G iso9660 d\-live 9.5.0 xf i386 .br |\-sdd2 320K vfat .br `\-sdd3 512M ext2 .br .br Overriding any advice because of \-dummy_force .br Looking for mount points of sdd: .br /dev/sdd1 on /mnt/iso type iso9660 (ro,relatime) .br /dev/sdd2 on /mnt/fat type vfat (rw,...,errors=remount\-ro) .br /dev/sdd3 on /mnt/ext type ext2 (rw,relatime) .br AGAINST THE ADVICE BY THIS PROGRAM, a daring user could do: .br sudo /bin/umount /dev/sdd1 .br sudo /bin/umount /dev/sdd2 .br sudo /bin/umount /dev/sdd3 .br sudo /bin/dd if=/dev/zero of=/dev/'sdd' bs=512 seek='7864318' .br count=1 status=none .br sudo /bin/dd if='debian\-live\-10.0.0\-amd64\-xfce.iso' of=/dev/sdd .br bs=1M status=progress oflag=dsync ; sync .br BE SMART. BE CAUTIOUS. BEWARE. .SS \fBAlphabetical List of positive and negative reasons:\fR .br As stated with use case "List all devices", \fBreasons\fR are words with either suffix '+' for an inviting device property or '\-' for a prohibitive property. .br Normally a single '\-' reason disqualifies the device from being advisable. .br .PP \fBhas_XYZ\-\fR .br A filesystem of type XYZ is detected on base device or partition and is spoiling the impression of a device with disposable content. .br \fBhas_iso9660+\fR .br An ISO 9660 filesystem is detected. .br \fBhas_vfat+\fR .br A FAT (MS\-DOS\-like) filesystem is detected. .br \fBlook_for_iso++\fR .br Option \-look_for_iso is given and an ISO 9660 filesystem is detected. This reason overrides any "has_XYZ\-" reason. .br \fBlooks_like_cd_drive\-\fR .br A given device name looks like the name of an optical drive: sr[0\-9]*. Use program \fBxorrecord\fR for this kind of devices. .br \fBlooks_like_disk_partition\-\fR .br A given device name looks like the name of a partition. Expected are names of base devices, like "sde", not of their partitions, like "sde1". .br \fBlooks_like_floppy\-\fR .br A given device name looks like the name of a floppy disk drive: fd[0\-9]*. .br \fBlooks_like_loopdev\-\fR .br A given device name looks like the name of a loop device: loop[0\-9]*. .br \fBlooks_like_ramdev\-\fR .br A given device name looks like the name of a RAM block device: zram[0\-9]*. .br \fBlsblk_no_size\-\fR .br A size test is given by \-max_size, \-min_size, or \-image_file but the size of the device cannot be inquired by lsblk. This is supposed to happen only with given inappropriate device names. .br \fBmmcblk+\fR .br The device name looks like a directly connected memory card. .br \fBname_with_slash\-\fR .br A given device name contains '/' characters. .br \fBno_bus_info\-\fR .br The device is not a memory card and lsblk reports nothing about the way how it is connected to the computer. .br \fBno_fs_while_not_su\-\fR .br No filesystem is reported by lsblk and the program does not believe to have run it with superuser powers. There is the risk that lsblk silently failed to detect existing filesystems. .br \fBno_iso9660\-\fR .br Option \-look_for_iso is given but no ISO 9660 filesystem is detected. .br \fBnot_usb\-\fR .br The device is not a memory card and lsblk reports that it is connected by something other than USB. .br \fBsize_too_large\-\fR .br Option \-max_size is given with a size smaller than the size of the device. .br \fBsize_too_small\-\fR .br Option \-min_size or \-image_file is given with size or file size larger than the size of the device. .br \fBusb+\fR .br The device is reported by lsblk to be connected via USB. .br .SS .br .SH OPTIONS .br .PP .TP \fB\-plug_test\fR Find the target device by asking the user to press the Enter key when the desired target is _not_ plugged in, to then plug it in, and to press Enter again. .br This overrides device names and option \-list_all. The found device is then shown with advice, vendor, and model. .br Option \-DO_WRITE is obeyed if given. In this case, the word 'yes' has to be entered to let unmounting and writing begin. .TP \fB\-list_all\fR Print list of all found devices with advice, vendor and model. One per line. Ignore any device names. Ignore \-DO_WRITE. .TP \fB\-list_long\fR After each result line, which shows reasons, add an additional listing of the information provided by lsblk which led to the reasons and add an empty line. .TP \fB\-with_vendor_model\fR Print vendor and model with each submitted device name. .TP \fB\-max_size\fR n[M|G|T] Set the upper byte size limit for advisable devices. Plain numbers get rounded down to full millions. As suffix are recognized: M = million, G = billion, T = trillion. .br Be generous to avoid problems with GB < GiB. .TP \fB\-min_size\fR n[M|G|T] Set the lower byte size limit for advisable devices. After processing like with \-max_size, one million gets added to the size limit. .TP \fB\-look_for_iso\fR Demand presence of an ISO 9660 filesystem. If so, then any further filesystem type is acceptable on that device. .br If this option is missing, only ISO 9660 and VFAT filesystems are accepted. .TP \fB\-with_sudo\fR Run 'lsblk \-o FSTYPE' by sudo. If no filesystems are detected on a device while the program has no superuser power, then the device is not advised. Option \-with_sudo avoids this refusal without the need to run the whole program as superuser. .br If \-DO_WRITE \-with_sudo is given, then the programs umount and dd will be run by sudo, too. .TP \fB\-trust_lsblk_udev\fR Suppress the reason no_fs_while_not_su\- if lsblk is linked with libudev.so. In this case it is likely that lsblk can retrieve FSTYPE even if run by a non\-priviledged user. .br This option is intended for use by frontend programs which are certain that they do not encounter a udev\-using version of lsblk which nevertheless fails to detect existing filesystems. Human users should better acquire superuser powers if reason no_fs_while_not_su\- is reported. .TP \fB\-image_file\fR PATH Set the path of the image file which shall be written to a device. Its size will be set as \-min_size. .TP \fB\-DO_WRITE\fR Write the given \-image_file to the one advisable device that is found. If more than one such device is found, then they get listed but no writing happens. .br In this case, to get a real write run, consider unplugging unneeded devices, or using option \-plug_test, or a re\-run with one of the advised device names as additional argument. .TP \fB\-no_pacifier\fR Do not use dd options to print progress messages and to perform synchronized output. These options are used by default if program dd offers progress messages. .TP \fB\-dummy\fR Report the \-DO_WRITE actions but do not perform them. .TP \fB\-dummy_force\fR If a single device name is given, do a run of \-dummy \-DO_WRITE even against the advice of this program. This probably shows you ways to shoot your own foot. .TP \fB\-version\fR Print the program name, version text, and timestamp to stdout and then end the program. .TP \fB\-help\fR Print the help text to stdout and then end the program. .SH EXAMPLES Examples are given in the above description of use cases. .SH FILES For now, no files are defined for configuration. .SH SEE ALSO .BR lsblk(8), .BR umount(8), .BR dd(1), .BR xorrecord(1) .SH BUGS To report bugs, request help, or suggest enhancements for \fBxorriso\-dd\-target\fR, please send electronic mail to the public list <bug\-xorriso@gnu.org>. If more privacy is desired, mail to <scdbackup@gmx.net>. .br Please describe what you expect the program to do, the program arguments which you used, the messages of \fBxorriso\-dd\-target\fR, and the undesirable outcome of your program run. .br Expect to get asked more questions before solutions can be proposed. .SH AUTHOR Thomas Schmitt <scdbackup@gmx.net> .br for libburnia\-project.org .SH COPYRIGHT Copyright (c) 2019 \- 2023 Thomas Schmitt .br Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of xorriso\-dd\-target. If you make use of the license to derive modified versions of xorriso\-dd\-target then you are entitled to modify this text under that same license. .SH CREDITS \fBxorriso\-dd\-target\fR is developed in cooperation with Nio Wiklund alias sudodus. This is xorriso-dd-target.info, produced by makeinfo version 5.2 from xorriso-dd-target.texi. xorriso-dd-target - Device evaluator and disk image copier for GNU/Linux Copyright (C) 2023 Thomas Schmitt Permission is granted to distribute this text freely. INFO-DIR-SECTION Archiving START-INFO-DIR-ENTRY * Xorriso-dd-target: (xorriso-dd-target). Device evaluator and disk image copier for GNU/Linux END-INFO-DIR-ENTRY  File: xorriso-dd-target.info, Node: Top, Next: Overview, Up: (dir) xorriso-dd-target 1.5.7 *********************** xorriso-dd-target - Device evaluator and disk image copier for GNU/Linux * Menu: * Overview:: Overview * Options:: Options * Examples:: Examples * Files:: Files * Seealso:: See also * Bugreport:: Reporting bugs * Legal:: Author, Copyright, Credits * CommandIdx:: Alphabetic Option List * ConceptIdx:: Alphabetic List of Concepts and Objects  File: xorriso-dd-target.info, Node: Overview, Next: Options, Prev: Top, Up: Top 1 Overview ********** 'xorriso-dd-target' evaluates block devices of the Linux kernel whether they are suitable targets for a disk image file and optionally copies the image file to one of them. It is specialized on the device names of the *Linux kernel* and uses the capabilities of util-linux program *lsblk*. Therefore it refuses to run on non-Linux kernels. The main purpose of xorriso-dd-target is to inspect the device files of disk-like storage media and to judge whether they look like removable devices with disposable content. If a single plausible candidate is detected, then the program is willing to copy a disk image file onto it. This will overwrite or make inaccessible the previous partition table and all previous data content of the target device. *Superuser power* is often needed for filesystem type identification, for possible unmounting, and for possible image writing. Option *-with_sudo* offers a way to gain this power only for those tasks and to run the program elsewise with a normal user's power. If a particular disk image file is intended as copy source, then its path should be given by option -image_file, so that its size can be used as decision criterion. Following are use case descriptions with examples: * Menu: * Simplenames:: List plain device names * Listall:: List all devices with reasoning * Givendevices:: Evaluate particular given devices * Plugtest:: Detect intended device by plugging * Dowrite:: Write image to an advised device * Unwise:: Show commands for writing to a not advised device * Reasons:: Alphabetical list of positive and negative reasons  File: xorriso-dd-target.info, Node: Simplenames, Next: Listall, Prev: Overview, Up: Overview 2 List plain device names ************************* The most simple and most boring use case is a program run without device names and without options -list_all, -plug_test, -DO_WRITE, -dummy_force. It prints on standard output (stdout) only the names of advisable devices without "/dev/" prefix. One name per line and without any reasoning text. The possible sudo password prompt, the message line about sudo, and the empty line after it do not go to stdout. Example: $ xorriso-dd-target -with_sudo Testing sudo to possibly get password prompting done now: [sudo] password for thomas: sudo /bin/lsblk seems ok. sde  File: xorriso-dd-target.info, Node: Listall, Next: Givendevices, Prev: Simplenames, Up: Overview 3 List all devices with reasoning ********************************* For the more curious user, there is option *-list_all* which prints the evaluation of each disk-like device that is listed by program lsblk. Optical drives, floppy disks, RAM block devices, loop devices are excluded, though. Each device is shown by one line of the form name : advice : reasoning : info *name* is the device name without "/dev/" prefix. *advice* is either "YES" or "NO". "YES" indicates that the device appears to be pluggable disk-like, not used as system disk or sincere data storage, and - if tested - of sufficient or plausible size. *reasoning* is a blank separated list of words with either suffix '+' for an inviting device property or '-' for a prohibitive property. Normally a single '-' reason disqualifies the device from being advisable. Only if option -look_for_iso is given, a reason "has_XYZ-" can be overridden by the presence of an ISO 9660 filesystem on the device. *info* is composed from VENDOR and MODEL as told by lsblk. Option *-list_long* causes with each line an additional listing of the information provided by lsblk which led to the shown reasons. Example: $ xorriso-dd-target -with_sudo -list_all ... sda : NO : not_usb- has_vfat+ has_ext4- : ATA Samsung SSD 850 sdb : NO : not_usb- has_swap- has_ext4- : ATA WDC WD20EFRX-68A sdc : YES : usb+ has_iso9660+ has_vfat+ : Intenso Ultra Line sdd : NO : usb+ has_iso9660+ has_vfat+ has_ext2- : SanDisk Cruzer  File: xorriso-dd-target.info, Node: Givendevices, Next: Plugtest, Prev: Listall, Up: Overview 4 Evaluate particular given devices *********************************** If *device names* are given instead of option -list_all, then only these devices are inspected. Their result gets listed without the ": info" part, unless option *-with_vendor_model* is given. Device names must not begin by '-' and must be single words composed of the characters [A-za-z0-9_/-]. They should not contain '/'. E.g. 'sdc' is valid, '/dev/sdc' is not valid. If one of the given device names gets not advised, the exit value is 1. It makes few sense to give device names which are not listed by -list_all. Examples: $ xorriso-dd-target -with_sudo sdc ... sdc : YES : usb+ has_iso9660+ has_vfat+ $ xorriso-dd-target -with_sudo -with_vendor_model sdc ... sdc : YES : usb+ has_iso9660+ has_vfat+ : Intenso Ultra Line $ xorriso-dd-target sdc sdc : NO : usb+ no_fs_while_not_su-  File: xorriso-dd-target.info, Node: Plugtest, Next: Dowrite, Prev: Givendevices, Up: Overview 5 Detect intended device by plugging ************************************ Option *-plug_test* triggers an interactive method to unambiguously determine the intended target device candidate. It consists of 2 or 3 steps. *Step 1* is to have the intended storage device *unplugged* and to confirm this by pressing the Enter key at the program's prompt. The program will then assess the list of not wanted devices. *Step 2* is to *plug in* the intended storage device and to confirm this by pressing the Enter key a second time. The program will wait up to 10 seconds for a disk-like storage device which is not in the list of not wanted devices. The user may wait with key pressing until the device blinking looks like it is ready. Only if a single new device is found, the program will go on as if a single device name was given. Option -list_all and any device names given as arguments will be ignored. *Step 3* happens only if options -DO_WRITE or -dummy_force are given. The program asks for a final input of the word 'yes' before real or simulated writing begins. Example: $ xorriso-dd-target -with_sudo -plug_test ... Caused by option -plug_test: Attempt to find the desired device by watching it appear after being plugged in. Step 1: Please make sure that the desired target device is plugged _out_ now. If it is currently plugged in, make sure to unmount all its filesystems and then unplug it. Press the Enter key when ready. Found and noted as _not_ desired: sda sdb sdc Step 2: Please plug in the desired target device and then press the Enter key. Waiting up to 10 seconds for a new device to be listed ... found: sdd Now waiting 5 seconds to let it settle ......... Found and noted as desired device: sdd sdd : NO : usb+ has_iso9660+ has_vfat+ has_ext2- : SanDisk Cruzer  File: xorriso-dd-target.info, Node: Dowrite, Next: Unwise, Prev: Plugtest, Up: Overview 6 Write image to an advised device ********************************** Only if option *-DO_WRITE* is given and -list_all is not, and if exactly one advisable device is listed, it really gets overwritten by the file content of the given -image_file. In this case the exit value is zero if writing succeeded, non-zero else. Option *-dummy* prevents this kind of real action and rather shows the planned umount and dd commands on stdout. Example: $ xorriso-dd-target -with_sudo -plug_test -DO_WRITE \ -image_file debian-live-10.0.0-amd64-xfce.iso ... sudo messages and above plug test steps 1 and 2 ... sde : YES : usb+ has_iso9660+ has_vfat+ Step 3: Last chance to abort. Enter the word 'yes' to start REAL WRITING. yes Looking for mount points of sde: /dev/sde1 on /mnt/iso type iso9660 (ro,relatime) /dev/sde2 on /mnt/fat type vfat (rw,...,errors=remount-ro) Unmounted: /dev/sde1 Unmounted: /dev/sde2 Performing: sudo /bin/dd if=/dev/zero of=/dev/'sde' bs=512 seek='245759999' count=1 status=none sudo /bin/dd if='debian-live-10.0.0-amd64-xfce.iso' of=/dev/'sde' bs=1M status=progress oflag=dsync ; sync ... dd messages ... The first dd run shall erase a possible GPT backup header. It is performed only if the local program "expr" can deal with the byte size of the device.  File: xorriso-dd-target.info, Node: Unwise, Next: Reasons, Prev: Dowrite, Up: Overview 7 Show commands for writing to a not advised device *************************************************** There should be no way to convince xorriso-dd-target of writing to a target device which it does not deem advisable. Please report any set of arguments that can be misused for that. The outmost complicity to potentially unwise actions is offered by option *-dummy_force*. If given together with a single device name or with option -plug_test it will act like -dummy -DO_WRITE with this device, even if it looks not advisable. I.e. it will show the shell commands which the program does not dare to perform. Example: $ xorriso-dd-target -with_sudo -list_long -dummy_force sdd \ -image_file debian-live-10.0.0-amd64-xfce.iso ... sdd : NO : usb+ has_iso9660+ has_vfat+ has_ext2- NAME SIZE FSTYPE TRAN LABEL sdd 3.8G iso9660 usb d-live 9.5.0 xf i386 |-sdd1 1.9G iso9660 d-live 9.5.0 xf i386 |-sdd2 320K vfat '-sdd3 512M ext2 Overriding any advice because of -dummy_force Looking for mount points of sdd: /dev/sdd1 on /mnt/iso type iso9660 (ro,relatime) /dev/sdd2 on /mnt/fat type vfat (rw,...,errors=remount-ro) /dev/sdd3 on /mnt/ext type ext2 (rw,relatime) AGAINST THE ADVICE BY THIS PROGRAM, a daring user could do: sudo /bin/umount /dev/sdd1 sudo /bin/umount /dev/sdd2 sudo /bin/umount /dev/sdd3 sudo /bin/dd if=/dev/zero of=/dev/'sdd' bs=512 seek='7864318' count=1 status=none sudo /bin/dd if='debian-live-10.0.0-amd64-xfce.iso' of=/dev/sdd bs=1M status=progress oflag=dsync ; sync BE SMART. BE CAUTIOUS. BEWARE.  File: xorriso-dd-target.info, Node: Reasons, Next: Options, Prev: Unwise, Up: Overview 8 Alphabetical list of positive and negative reasons **************************************************** As stated with use case "List all devices", *reasons* are words with either suffix '+' for an inviting device property or '-' for a prohibitive property. Normally a single '-' reason disqualifies the device from being advisable. 8.1 Reasons =========== *has_XYZ-* A filesystem of type XYZ is detected on base device or partition and is spoiling the impression of a device with disposable content. *has_iso9660+* An ISO 9660 filesystem is detected. *has_vfat+* A FAT (MS-DOS-like) filesystem is detected. *look_for_iso++* Option -look_for_iso is given and an ISO 9660 filesystem is detected. This reason overrides any "has_XYZ-" reason. *looks_like_cd_drive-* A given device name looks like the name of an optical drive: sr[0-9]*. Use program *xorrecord* for this kind of devices. *looks_like_disk_partition-* A given device name looks like the name of a partition. Expected are names of base devices, like "sde", not of their partitions, like "sde1". *looks_like_floppy-* A given device name looks like the name of a floppy disk drive: fd[0-9]*. *looks_like_loopdev-* A given device name looks like the name of a loop device: loop[0-9]*. *looks_like_ramdev-* A given device name looks like the name of a RAM block device: zram[0-9]*. *lsblk_no_size-* A size test is given by -max_size, -min_size, or -image_file but the size of the device cannot be inquired by lsblk. This is supposed to happen only with given inappropriate device names. *mmcblk+* The device name looks like a directly connected memory card. *name_with_slash-* A given device name contains '/' characters. *no_bus_info-* The device is not a memory card and lsblk reports nothing about the way how it is connected to the computer. *no_fs_while_not_su-* No filesystem is reported by lsblk and the program does not believe to have run it with superuser powers. There is the risk that lsblk silently failed to detect existing filesystems. *no_iso9660-* Option -look_for_iso is given but no ISO 9660 filesystem is detected. *not_usb-* The device is not a memory card and lsblk reports that it is connected by something other than USB. *size_too_large-* Option -max_size is given with a size smaller than the size of the device. *size_too_small-* Option -min_size or -image_file is given with size or file size larger than the size of the device. *usb+* The device is reported by lsblk to be connected via USB.  File: xorriso-dd-target.info, Node: Options, Next: Examples, Prev: Overview, Up: Top 9 Options ********* -plug_test Find the target device by asking the user to press the Enter key when the desired target is _not_ plugged in, to then plug it in, and to press Enter again. This overrides device names and option -list_all. The found device is then shown with advice, vendor, and model. Option -DO_WRITE is obeyed if given. In this case, the word 'yes' has to be entered to let unmounting and writing begin. -list_all Print list of all found devices with advice, vendor and model. One per line. Ignore any device names. Ignore -DO_WRITE. -list_long After each result line, which shows reasons, add an additional listing of the information provided by lsblk which led to the reasons and add an empty line. -with_vendor_model Print vendor and model with each submitted device name. -max_size n[M|G|T] Set the upper byte size limit for advisable devices. Plain numbers get rounded down to full millions. As suffix are recognized: M = million, G = billion, T = trillion. Be generous to avoid problems with GB < GiB. -min_size n[M|G|T] Set the lower byte size limit for advisable devices. After processing like with -max_size, one million gets added to the size limit. -look_for_iso Demand presence of an ISO 9660 filesystem. If so, then any further filesystem type is acceptable on that device. If this option is missing, only ISO 9660 and VFAT filesystems are accepted. -with_sudo Run 'lsblk -o FSTYPE' by sudo. If no filesystems are detected on a device while the program has no superuser power, then the device is not advised. Option -with_sudo avoids this refusal without the need to run the whole program as superuser. If -DO_WRITE -with_sudo is given, then the programs umount and dd will be run by sudo, too. -trust_lsblk_udev Suppress the reason no_fs_while_not_su- if lsblk is linked with libudev.so. In this case it is likely that lsblk can retrieve FSTYPE even if run by a non-priviledged user. This option is intended for use by frontend programs which are certain that they do not encounter a udev-using version of lsblk which nevertheless fails to detect existing filesystems. Human users should better acquire superuser powers if reason no_fs_while_not_su- is reported. -image_file PATH Set the path of the image file which shall be written to a device. Its size will be set as -min_size. -DO_WRITE Write the given -image_file to the one advisable device that is found. If more than one such device is found, then they get listed but no writing happens. In this case, to get a real write run, consider unplugging unneeded devices, or using option -plug_test, or a re-run with one of the advised device names as additional argument. -no_pacifier Do not use dd options to print progress messages and to perform synchronized output. These options are used by default if program dd offers progress messages. -dummy Report the -DO_WRITE actions but do not perform them. -dummy_force If a single device name is given, do a run of -dummy -DO_WRITE even against the advice of this program. This probably shows you ways to shoot your own foot. -version Print the program name, version text, and timestamp to stdout and then end the program. -help Print the help text to stdout and then end the program.  File: xorriso-dd-target.info, Node: Examples, Next: Files, Prev: Options, Up: Top 10 Examples *********** Examples are given in the above description of use cases.  File: xorriso-dd-target.info, Node: Files, Next: Seealso, Prev: Examples, Up: Top 11 Files ******** For now, no files are defined for configuration.  File: xorriso-dd-target.info, Node: Seealso, Next: Bugreport, Prev: Files, Up: Top 12 See also *********** lsblk(8), sudo(8), umount(8), dd(1), xorrecord(1)  File: xorriso-dd-target.info, Node: Bugreport, Next: Legal, Prev: Seealso, Up: Top 13 Reporting bugs ***************** To report bugs, request help, or suggest enhancements for 'xorriso-dd-target', please send electronic mail to the public list <bug-xorriso@gnu.org>. If more privacy is desired, mail to <scdbackup@gmx.net>. Please describe what you expect the program to do, the program arguments which you used, the messages of 'xorriso-dd-target', and the undesirable outcome of your program run. Expect to get asked more questions before solutions can be proposed.  File: xorriso-dd-target.info, Node: Legal, Next: CommandIdx, Prev: Bugreport, Up: Top 14 Author, Copyright, Credits ***************************** 14.1 Author =========== Thomas Schmitt <scdbackup@gmx.net> for libburnia-project.org 14.2 Copyright ============== Copyright (c) 2019 - 2023 Thomas Schmitt Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of xorriso-dd-target. If you make use of the license to derive modified versions of xorriso-dd-target then you are entitled to modify this text under that same license. 14.3 Credits ============ 'xorriso-dd-target' is developed in cooperation with Nio Wiklund alias sudodus.  File: xorriso-dd-target.info, Node: CommandIdx, Next: ConceptIdx, Prev: Legal, Up: Top 15 Alphabetic Options List ************************** �[index�] * Menu: * -DO_WRITE write image file to device: Options. (line 57) * -dummy report but do not perform: Options. (line 68) * -dummy_force show raw copy commands: Options. (line 70) * -help print help text to stdout: Options. (line 77) * -image_file set path of disk image file: Options. (line 54) * -list_all print list of disk devices: Options. (line 15) * -list_long print extra device info: Options. (line 18) * -look_for_iso demand presence of ISO 9660: Options. (line 33) * -max_size set size limit for device: Options. (line 24) * -min_size set size limit for device: Options. (line 29) * -no_pacifier do not show dd progress: Options. (line 64) * -plug_test detect target device plugging: Options. (line 7) * -trust_lsblk_udev suppress no_fs_while_not_su-: Options. (line 45) * -version print version text to stdout: Options. (line 74) * -with_sudo run lsblk, umount, dd by sudo: Options. (line 38) * -with_vendor_model add drive info to advice: Options. (line 22)  File: xorriso-dd-target.info, Node: ConceptIdx, Next: Top, Prev: CommandIdx, Up: Top 16 Alphabetic List of Concepts and Objects ****************************************** �[index�] * Menu: * Bugs, reporting: Bugreport. (line 6) * dd progress, do not show, -no_pacifier: Options. (line 64) * Device info, print extra, -list_long: Options. (line 18) * Device size, set limit, -max_size: Options. (line 24) * Device size, set limit, -min_size: Options. (line 29) * Disk devices, print list, -list_all: Options. (line 15) * disk image file, set path, -image_file: Options. (line 54) * disk image file, write to device, -DO_WRITE: Options. (line 57) * Drive info, add to advice, -with_vendor_model: Options. (line 22) * help text, print to stdout, -help: Options. (line 77) * ISO 9660, demand presence on target, -look_for_iso: Options. (line 33) * lsblk, umount, dd, run by sudo, -with_sudo: Options. (line 38) * only report, do not perform, -dummy: Options. (line 68) * Problems, reporting: Bugreport. (line 6) * raw copy commands, show, -dummy_force: Options. (line 70) * Reasons, list of: Reasons. (line 6) * suppress no_fs_while_not_su-, -trust_lsblk_udev: Options. (line 45) * Target device, detect by plugging, -plug_test: Options. (line 7) * Use case, detect intended device by plugging: Plugtest. (line 6) * Use case, evaluate particular given devices: Givendevices. (line 6) * Use case, list advisable device names: Simplenames. (line 6) * Use case, list all devices with reasoning: Listall. (line 6) * Use case, show commands for writing to a not advised device: Unwise. (line 6) * Use case, write image to an advised device: Dowrite. (line 6) * version text, print to stdout, -version: Options. (line 74) * xorriso-dd-target, options: Options. (line 6)  Tag Table: Node: Top429 Node: Overview996 Node: Simplenames2789 Node: Listall3513 Node: Givendevices5086 Node: Plugtest6050 Node: Dowrite7956 Node: Unwise9337 Node: Reasons10959 Node: Options13540 Node: Examples17148 Node: Files17321 Node: Seealso17479 Node: Bugreport17645 Node: Legal18232 Node: CommandIdx18943 Node: ConceptIdx20282  End Tag Table ˆF��d€¬g� éËßÀ«À¨Tá�ž%¯»Ò„Ó/ÕVâ„9¾Ú]Áœ¼� §¸"à+çð…:à»dƒ*IUÇ\input texinfo @c -*-texinfo-*- @c %**start of header @setfilename xorriso-dd-target.info @settitle GNU xorriso-dd-target 1.5.7 @c %**end of header @c @c man-ignore-lines begin @dircategory Archiving @direntry * Xorriso-dd-target: (xorriso-dd-target). Device evaluator and disk image copier for GNU/Linux @end direntry @c man-ignore-lines end @c @c Notes about embedded man page: @c This texinfo code contains the necessary info to produce a man page. @c One can produce it by applying the following rules: @c The first line gets discarded. @c Line start "@c man " will become "", the remainder is put out unaltered. @c Lines "@*" will be converted to ".br" @c "@c man-ignore-lines N" will discard N following lines. @c "@c man-ignore-lines begin" discards all following lines @c up to "@c man-ignore-lines end". @c Line blocks of "@menu" "@end menu" will be discarded. @c "@item word words" becomes "\fBword\fR words". @c @b{...}, @command{...}, @dfn{...}, @emph{...}, @strong{...} @c get mapped to \fB...\fR . @c @abbr{...}, @code{...}, @file{...}, @i{...}, @option{...}, @r{...}, @c @ref{...}, @samp{...},@var{...}, get mapped to ... . @c @ref{...}, @xref{...} get mapped to empty text. @c @email{...} gets mapped to <...> . @c Mapped {...} content is subject to the rules except {...} mapping. @c @minus{} will become "-". @c @@ , @{, @} will get stripped of their first @. @c Other lines which begin by "@" will be discarded. @c In lines not stemming from "@c man", "\" becomes "\\" @c "-" which are not preceded by an uneven number of "\" will get @c prepended one "\". @c @c @c man .\" Hey, EMACS: -*- nroff -*- @c man .\" @c man .\" IMPORTANT NOTE: @c man .\" @c man .\" The original of this file is kept in xorriso/xorriso-dd-target.texi @c man .\" This here was generated by program xorriso/make_xorriso_1 @c man .\" @c man .\" @c man .\" First parameter, NAME, should be all caps @c man .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection @c man .\" other parameters are allowed: see man(7), man(1) @c man .TH XORRISO-DD-TARGET 1 "Version 1.5.7, Jun 07, 2023" @c man .\" Please adjust this date whenever revising the manpage. @c man .\" @c man .\" Some roff macros, for reference: @c man .\" .nh disable hyphenation @c man .\" .hy enable hyphenation @c man .\" .ad l left justify @c man .\" .ad b justify to both left and right margins @c man .\" .nf disable filling @c man .\" .fi enable filling @c man .\" .br insert line break @c man .\" .sp <n> insert n+1 empty lines @c man .\" for manpage-specific macros, see man(7) @c man .nh @c man-ignore-lines begin @copying xorriso-dd-target - Device evaluator and disk image copier for GNU/Linux Copyright @copyright{} 2023 Thomas Schmitt @quotation Permission is granted to distribute this text freely. @end quotation @end copying @c man-ignore-lines end @titlepage @title Manual of GNU xorriso companion xorriso-dd-target 1.5.7 @author Thomas Schmitt @page @vskip 0pt plus 1filll @insertcopying @end titlepage @contents @ifnottex @node Top @top xorriso-dd-target 1.5.7 @c man-ignore-lines 1 @c man .SH NAME xorriso-dd-target - Device evaluator and disk image copier for GNU/Linux @end ifnottex @menu * Overview:: Overview * Options:: Options * Examples:: Examples * Files:: Files * Seealso:: See also * Bugreport:: Reporting bugs * Legal:: Author, Copyright, Credits * CommandIdx:: Alphabetic Option List * ConceptIdx:: Alphabetic List of Concepts and Objects @end menu @node Overview, Options, Top, Top @chapter Overview @c man .SH SYNOPSIS @c man .B xorriso-dd-target @c man [ options ] [ device_names ] @c man .br @c man .SH DESCRIPTION @c man .PP @command{xorriso-dd-target} evaluates block devices of the Linux kernel whether they are suitable targets for a disk image file and optionally copies the image file to one of them. @* It is specialized on the device names of the @strong{Linux kernel} and uses the capabilities of util-linux program @strong{lsblk}. Therefore it refuses to run on non-Linux kernels. @* @sp 1 @c man .PP The main purpose of xorriso-dd-target is to inspect the device files of disk-like storage media and to judge whether they look like removable devices with disposable content. @* If a single plausible candidate is detected, then the program is willing to copy a disk image file onto it. This will overwrite or make inaccessible the previous partition table and all previous data content of the target device. @* @strong{Superuser power} is often needed for filesystem type identification, for possible unmounting, and for possible image writing. Option @strong{-with_sudo} offers a way to gain this power only for those tasks and to run the program elsewise with a normal user's power. @* If a particular disk image file is intended as copy source, then its path should be given by option -image_file, so that its size can be used as decision criterion. @sp 1 @c man .PP Following are use case descriptions with examples: @c man .br @c man - List plain device names @c man .br @c man - List all devices with reasoning @c man .br @c man - Evaluate particular given devices @c man .br @c man - Detect intended device by plugging @c man .br @c man - Write image to an advised device @c man .br @c man - Show commands for writing to a not advised device @c man .br @menu * Simplenames:: List plain device names * Listall:: List all devices with reasoning * Givendevices:: Evaluate particular given devices * Plugtest:: Detect intended device by plugging * Dowrite:: Write image to an advised device * Unwise:: Show commands for writing to a not advised device * Reasons:: Alphabetical list of positive and negative reasons @end menu @c man .SS @node Simplenames, Listall, Overview, Overview @chapter List plain device names @c man \fBList plain device names:\fR @c man .br @cindex Use case, list advisable device names The most simple and most boring use case is a program run without device names and without options -list_all, -plug_test, -DO_WRITE, -dummy_force. It prints on standard output (stdout) only the names of advisable devices without "/dev/" prefix. One name per line and without any reasoning text. @* The possible sudo password prompt, the message line about sudo, and the empty line after it do not go to stdout. @* Example: @* $ xorriso-dd-target -with_sudo @* Testing sudo to possibly get password prompting done now: @* [sudo] password for thomas: @* sudo /bin/lsblk seems ok. sde @c man .SS @node Listall, Givendevices, Simplenames, Overview @chapter List all devices with reasoning @c man \fBList all devices with reasoning:\fR @c man .br @cindex Use case, list all devices with reasoning For the more curious user, there is option @strong{-list_all} which prints the evaluation of each disk-like device that is listed by program lsblk. Optical drives, floppy disks, RAM block devices, loop devices are excluded, though. @* Each device is shown by one line of the form @* name : advice : reasoning : info @* @strong{name} is the device name without "/dev/" prefix. @* @strong{advice} is either "YES" or "NO". "YES" indicates that the device appears to be pluggable disk-like, not used as system disk or sincere data storage, and - if tested - of sufficient or plausible size. @* @strong{reasoning} is a blank separated list of words with either suffix '+' for an inviting device property or '-' for a prohibitive property. Normally a single '-' reason disqualifies the device from being advisable. Only if option -look_for_iso is given, a reason "has_XYZ-" can be overridden by the presence of an ISO 9660 filesystem on the device. @* @strong{info} is composed from VENDOR and MODEL as told by lsblk. @* Option @strong{-list_long} causes with each line an additional listing of the information provided by lsblk which led to the shown reasons. @* Example: @* $ xorriso-dd-target -with_sudo -list_all @* ... @* sda : NO : not_usb- has_vfat+ has_ext4- : ATA Samsung SSD 850 @* sdb : NO : not_usb- has_swap- has_ext4- : ATA WDC WD20EFRX-68A @* sdc : YES : usb+ has_iso9660+ has_vfat+ : Intenso Ultra Line @* sdd : NO : usb+ has_iso9660+ has_vfat+ has_ext2- : SanDisk Cruzer @c man .SS @node Givendevices, Plugtest, Listall, Overview @chapter Evaluate particular given devices @c man \fBEvaluate particular given devices:\fR @c man .br @cindex Use case, evaluate particular given devices If @strong{device names} are given instead of option -list_all, then only these devices are inspected. Their result gets listed without the ": info" part, unless option @strong{-with_vendor_model} is given. @* Device names must not begin by '-' and must be single words composed of the characters [A-za-z0-9_/-]. They should not contain '/'. E.g. 'sdc' is valid, '/dev/sdc' is not valid. @* If one of the given device names gets not advised, the exit value is 1. @* It makes few sense to give device names which are not listed by -list_all. @* Examples: @* $ xorriso-dd-target -with_sudo sdc @* ... @* sdc : YES : usb+ has_iso9660+ has_vfat+ @* $ xorriso-dd-target -with_sudo -with_vendor_model sdc @* ... @* sdc : YES : usb+ has_iso9660+ has_vfat+ : Intenso Ultra Line @* $ xorriso-dd-target sdc @* sdc : NO : usb+ no_fs_while_not_su- @c man .SS @node Plugtest, Dowrite, Givendevices, Overview @chapter Detect intended device by plugging @c man \fBDetect intended device by plugging:\fR @c man .br @cindex Use case, detect intended device by plugging Option @strong{-plug_test} triggers an interactive method to unambiguously determine the intended target device candidate. It consists of 2 or 3 steps. @* @strong{Step 1} is to have the intended storage device @strong{unplugged} and to confirm this by pressing the Enter key at the program's prompt. The program will then assess the list of not wanted devices. @* @strong{Step 2} is to @strong{plug in} the intended storage device and to confirm this by pressing the Enter key a second time. The program will wait up to 10 seconds for a disk-like storage device which is not in the list of not wanted devices. The user may wait with key pressing until the device blinking looks like it is ready. @* Only if a single new device is found, the program will go on as if a single device name was given. Option -list_all and any device names given as arguments will be ignored. @* @strong{Step 3} happens only if options -DO_WRITE or -dummy_force are given. The program asks for a final input of the word 'yes' before real or simulated writing begins. @* Example: @* $ xorriso-dd-target -with_sudo -plug_test @* ... @* Caused by option -plug_test: Attempt to find the desired device by watching it appear after being plugged in. @* Step 1: @* Please make sure that the desired target device is plugged _out_ now. @* If it is currently plugged in, make sure to unmount all its filesystems @* and then unplug it. @* Press the Enter key when ready. @* @* Found and noted as _not_ desired: sda sdb sdc @* Step 2: @* Please plug in the desired target device and then press the Enter key. @* @* Waiting up to 10 seconds for a new device to be listed ... found: sdd @* Now waiting 5 seconds to let it settle ......... @* Found and noted as desired device: sdd @* @* sdd : NO : usb+ has_iso9660+ has_vfat+ has_ext2- : SanDisk Cruzer @c man .SS @node Dowrite, Unwise, Plugtest, Overview @chapter Write image to an advised device @c man \fBWrite image to an advised device:\fR @c man .br @cindex Use case, write image to an advised device Only if option @strong{-DO_WRITE} is given and -list_all is not, and if exactly one advisable device is listed, it really gets overwritten by the file content of the given -image_file. In this case the exit value is zero if writing succeeded, non-zero else. @* Option @strong{-dummy} prevents this kind of real action and rather shows the planned umount and dd commands on stdout. @* Example: @* $ xorriso-dd-target -with_sudo -plug_test -DO_WRITE \ @* -image_file debian-live-10.0.0-amd64-xfce.iso @* ... sudo messages and above plug test steps 1 and 2 ... @* @* sde : YES : usb+ has_iso9660+ has_vfat+ @* Step 3: @* Last chance to abort. Enter the word 'yes' to start REAL WRITING. @* yes @* Looking for mount points of sde: @* /dev/sde1 on /mnt/iso type iso9660 (ro,relatime) @* /dev/sde2 on /mnt/fat type vfat (rw,...,errors=remount-ro) @* Unmounted: /dev/sde1 @* Unmounted: /dev/sde2 @* Performing: @* sudo /bin/dd if=/dev/zero of=/dev/'sde' bs=512 seek='245759999' @* count=1 status=none @* sudo /bin/dd if='debian-live-10.0.0-amd64-xfce.iso' of=/dev/'sde' @* bs=1M status=progress oflag=dsync ; sync @* ... dd messages ... @* The first dd run shall erase a possible GPT backup header. It is performed only if the local program "expr" can deal with the byte size of the device. @c man .SS @node Unwise, Reasons, Dowrite, Overview @chapter Show commands for writing to a not advised device @c man \fBShow commands for writing to a not advised device:\fR @c man .br @cindex Use case, show commands for writing to a not advised device There should be no way to convince xorriso-dd-target of writing to a target device which it does not deem advisable. Please report any set of arguments that can be misused for that. @* The outmost complicity to potentially unwise actions is offered by option @strong{-dummy_force}. If given together with a single device name or with option -plug_test it will act like -dummy -DO_WRITE with this device, even if it looks not advisable. I.e. it will show the shell commands which the program does not dare to perform. @* Example: @* $ xorriso-dd-target -with_sudo -list_long -dummy_force sdd \ @* -image_file debian-live-10.0.0-amd64-xfce.iso @* ... @* sdd : NO : usb+ has_iso9660+ has_vfat+ has_ext2- @* NAME SIZE FSTYPE TRAN LABEL @* sdd 3.8G iso9660 usb d-live 9.5.0 xf i386 @* |-sdd1 1.9G iso9660 d-live 9.5.0 xf i386 @* |-sdd2 320K vfat @* `-sdd3 512M ext2 @* @* Overriding any advice because of -dummy_force @* Looking for mount points of sdd: @* /dev/sdd1 on /mnt/iso type iso9660 (ro,relatime) @* /dev/sdd2 on /mnt/fat type vfat (rw,...,errors=remount-ro) @* /dev/sdd3 on /mnt/ext type ext2 (rw,relatime) @* AGAINST THE ADVICE BY THIS PROGRAM, a daring user could do: @* sudo /bin/umount /dev/sdd1 @* sudo /bin/umount /dev/sdd2 @* sudo /bin/umount /dev/sdd3 @* sudo /bin/dd if=/dev/zero of=/dev/'sdd' bs=512 seek='7864318' @* count=1 status=none @* sudo /bin/dd if='debian-live-10.0.0-amd64-xfce.iso' of=/dev/sdd @* bs=1M status=progress oflag=dsync ; sync @* BE SMART. BE CAUTIOUS. BEWARE. @c man .SS @node Reasons, Options, Unwise, Overview @chapter Alphabetical list of positive and negative reasons @c man \fBAlphabetical List of positive and negative reasons:\fR @c man .br @cindex Reasons, list of As stated with use case "List all devices", @strong{reasons} are words with either suffix '+' for an inviting device property or '-' for a prohibitive property. @* Normally a single '-' reason disqualifies the device from being advisable. @c man .br @c man .PP @section Reasons @strong{has_XYZ-} @* A filesystem of type XYZ is detected on base device or partition and is spoiling the impression of a device with disposable content. @* @strong{has_iso9660+} @* An ISO 9660 filesystem is detected. @* @strong{has_vfat+} @* A FAT (MS-DOS-like) filesystem is detected. @* @strong{look_for_iso++} @* Option -look_for_iso is given and an ISO 9660 filesystem is detected. This reason overrides any "has_XYZ-" reason. @* @strong{looks_like_cd_drive-} @* A given device name looks like the name of an optical drive: sr[0-9]*. Use program @strong{xorrecord} for this kind of devices. @* @strong{looks_like_disk_partition-} @* A given device name looks like the name of a partition. Expected are names of base devices, like "sde", not of their partitions, like "sde1". @* @strong{looks_like_floppy-} @* A given device name looks like the name of a floppy disk drive: fd[0-9]*. @* @strong{looks_like_loopdev-} @* A given device name looks like the name of a loop device: loop[0-9]*. @* @strong{looks_like_ramdev-} @* A given device name looks like the name of a RAM block device: zram[0-9]*. @* @strong{lsblk_no_size-} @* A size test is given by -max_size, -min_size, or -image_file but the size of the device cannot be inquired by lsblk. This is supposed to happen only with given inappropriate device names. @* @strong{mmcblk+} @* The device name looks like a directly connected memory card. @* @strong{name_with_slash-} @* A given device name contains '/' characters. @* @strong{no_bus_info-} @* The device is not a memory card and lsblk reports nothing about the way how it is connected to the computer. @* @strong{no_fs_while_not_su-} @* No filesystem is reported by lsblk and the program does not believe to have run it with superuser powers. There is the risk that lsblk silently failed to detect existing filesystems. @* @strong{no_iso9660-} @* Option -look_for_iso is given but no ISO 9660 filesystem is detected. @* @strong{not_usb-} @* The device is not a memory card and lsblk reports that it is connected by something other than USB. @* @strong{size_too_large-} @* Option -max_size is given with a size smaller than the size of the device. @* @strong{size_too_small-} @* Option -min_size or -image_file is given with size or file size larger than the size of the device. @* @strong{usb+} @* The device is reported by lsblk to be connected via USB. @* @c man .SS @node Options, Examples, Overview, Top @chapter Options @cindex xorriso-dd-target, options @c man .br @c man .SH OPTIONS @c man .br @c man .PP @c man .TP @table @asis @item -plug_test @kindex -plug_test detect target device plugging @cindex Target device, detect by plugging, @minus{}plug_test Find the target device by asking the user to press the Enter key when the desired target is _not_ plugged in, to then plug it in, and to press Enter again. @* This overrides device names and option -list_all. The found device is then shown with advice, vendor, and model. @* Option -DO_WRITE is obeyed if given. In this case, the word 'yes' has to be entered to let unmounting and writing begin. @c man .TP @item -list_all @kindex -list_all print list of disk devices @cindex Disk devices, print list, -list_all Print list of all found devices with advice, vendor and model. One per line. Ignore any device names. Ignore -DO_WRITE. @c man .TP @item -list_long @kindex -list_long print extra device info @cindex Device info, print extra, -list_long After each result line, which shows reasons, add an additional listing of the information provided by lsblk which led to the reasons and add an empty line. @c man .TP @item -with_vendor_model @kindex -with_vendor_model add drive info to advice @cindex Drive info, add to advice, -with_vendor_model Print vendor and model with each submitted device name. @c man .TP @item -max_size n[M|G|T] @kindex -max_size set size limit for device @cindex Device size, set limit, -max_size Set the upper byte size limit for advisable devices. Plain numbers get rounded down to full millions. As suffix are recognized: M = million, G = billion, T = trillion. @* Be generous to avoid problems with GB < GiB. @c man .TP @item -min_size n[M|G|T] @kindex -min_size set size limit for device @cindex Device size, set limit, -min_size Set the lower byte size limit for advisable devices. After processing like with -max_size, one million gets added to the size limit. @c man .TP @item -look_for_iso @kindex -look_for_iso demand presence of ISO 9660 @cindex ISO 9660, demand presence on target, @minus{}look_for_iso Demand presence of an ISO 9660 filesystem. If so, then any further filesystem type is acceptable on that device. @* If this option is missing, only ISO 9660 and VFAT filesystems are accepted. @c man .TP @item -with_sudo @kindex -with_sudo run lsblk, umount, dd by sudo @cindex lsblk, umount, dd, run by sudo, -with_sudo Run 'lsblk -o FSTYPE' by sudo. If no filesystems are detected on a device while the program has no superuser power, then the device is not advised. Option -with_sudo avoids this refusal without the need to run the whole program as superuser. @* If -DO_WRITE -with_sudo is given, then the programs umount and dd will be run by sudo, too. @c man .TP @item -trust_lsblk_udev @kindex -trust_lsblk_udev suppress no_fs_while_not_su- @cindex suppress no_fs_while_not_su-, -trust_lsblk_udev Suppress the reason no_fs_while_not_su- if lsblk is linked with libudev.so. In this case it is likely that lsblk can retrieve FSTYPE even if run by a non-priviledged user. @* This option is intended for use by frontend programs which are certain that they do not encounter a udev-using version of lsblk which nevertheless fails to detect existing filesystems. Human users should better acquire superuser powers if reason no_fs_while_not_su- is reported. @c man .TP @item -image_file PATH @kindex -image_file set path of disk image file @cindex disk image file, set path, -image_file Set the path of the image file which shall be written to a device. Its size will be set as -min_size. @c man .TP @item -DO_WRITE @kindex -DO_WRITE write image file to device @cindex disk image file, write to device, -DO_WRITE Write the given -image_file to the one advisable device that is found. If more than one such device is found, then they get listed but no writing happens. @* In this case, to get a real write run, consider unplugging unneeded devices, or using option -plug_test, or a re-run with one of the advised device names as additional argument. @c man .TP @item -no_pacifier @kindex -no_pacifier do not show dd progress @cindex dd progress, do not show, -no_pacifier Do not use dd options to print progress messages and to perform synchronized output. These options are used by default if program dd offers progress messages. @c man .TP @item -dummy @kindex -dummy report but do not perform @cindex only report, do not perform, -dummy Report the -DO_WRITE actions but do not perform them. @c man .TP @item -dummy_force @kindex -dummy_force show raw copy commands @cindex raw copy commands, show, -dummy_force If a single device name is given, do a run of -dummy -DO_WRITE even against the advice of this program. This probably shows you ways to shoot your own foot. @c man .TP @item -version @kindex -version print version text to stdout @cindex version text, print to stdout, -version Print the program name, version text, and timestamp to stdout and then end the program. @c man .TP @item -help @kindex -help print help text to stdout @cindex help text, print to stdout, -help Print the help text to stdout and then end the program. @end table @node Examples, Files, Options, Top @chapter Examples @c man .SH EXAMPLES Examples are given in the above description of use cases. @node Files, Seealso, Examples, Top @chapter Files @c man .SH FILES For now, no files are defined for configuration. @c man .SH SEE ALSO @c man .BR lsblk(8), @c man .BR umount(8), @c man .BR dd(1), @c man .BR xorrecord(1) @c man-ignore-lines begin @node Seealso, Bugreport, Files, Top @chapter See also lsblk(8), sudo(8), umount(8), dd(1), xorrecord(1) @c man-ignore-lines end @c man .SH BUGS @node Bugreport, Legal, Seealso, Top @chapter Reporting bugs @cindex Bugs, reporting @cindex Problems, reporting To report bugs, request help, or suggest enhancements for @command{xorriso-dd-target}, please send electronic mail to the public list @email{bug-xorriso@@gnu.org}. If more privacy is desired, mail to @email{scdbackup@@gmx.net}. @* @sp 1 Please describe what you expect the program to do, the program arguments which you used, the messages of @command{xorriso-dd-target}, and the undesirable outcome of your program run. @* @sp 1 Expect to get asked more questions before solutions can be proposed. @c man .SH AUTHOR @node Legal, CommandIdx, Bugreport, Top @chapter Author, Copyright, Credits @section Author Thomas Schmitt <scdbackup@@gmx.net> @* for libburnia-project.org @c man .SH COPYRIGHT @section Copyright Copyright (c) 2019 - 2023 Thomas Schmitt @* Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of xorriso-dd-target. If you make use of the license to derive modified versions of xorriso-dd-target then you are entitled to modify this text under that same license. @c man .SH CREDITS @section Credits @command{xorriso-dd-target} is developed in cooperation with Nio Wiklund alias sudodus. @c man-ignore-lines begin @node CommandIdx, ConceptIdx, Legal, Top @chapter Alphabetic Options List @printindex ky @node ConceptIdx, Top, CommandIdx, Top @chapter Alphabetic List of Concepts and Objects @printindex cp @c man-ignore-lines end @bye Derek Foreman Ben Jansens Thomas Schmitt Mario Danic Vreixo Formoso Lopes Colin Plumb Tatu Ylonen Jim Kingdon Scott G. Miller Ulrich Drepper Richard Atterer Steve McIntyre George Danchev Nio Wiklund alias sudodus and possibly others who gave their copyright to Free Software Foundation, Inc. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> 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 3 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, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. Derek Foreman <derek@signalmarketing.com> Ben Jansens <xor@orodu.net> Thomas Schmitt <scdbackup@gmx.net> Mario Danic <mario.danic@gmail.com>, Vreixo Formoso <metalpain2002@yahoo.es> Steve McIntyre <steve@einval.com> George Danchev <danchev@spnet.net> GNU xorriso is a compilation of modules from libburnia-project.org : xorriso Copyright (C) 2007-2011 Thomas Schmitt libisoburn Copyright (C) 2007-2011 Vreixo Formoso, Thomas Schmitt libisofs Copyright (C) 2007-2011 Vreixo Formoso, Mario Danic, Thomas Schmitt libburn Copyright (C) 2002-2006 Derek Foreman, Ben Jansens 2006-2011 Mario Danic, Thomas Schmitt Further included is : libjte Copyright (C) 2000-2007 Free Software Foundation, Inc. 2004-2011 Steve McIntyre 2010-2011 George Danchev, Thomas Schmitt Originally they all are licensed directly or indirectly as GPLv2+. GNU xorriso is licensed by the following statement: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 or later as published by the Free Software Foundation. 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 ------------------------------------------------------------------------------ Contribution of libburnia-project.org to the GNU Operating System ------------------------------------------------------------------------------ GNU xorriso. By Thomas Schmitt <scdbackup@gmx.net> Derived from and supported by libburnia-project.org, published via: http://www.gnu.org/software/xorriso/xorriso_eng.html http://www.gnu.org/software/xorriso/xorriso-1.5.7.tar.gz Provided under GPL version 3 or later. No warranty. ------------------------------------------------------------------------------ xorriso is a program which copies file objects from POSIX compliant filesystems into Rock Ridge enhanced ISO 9660 filesystems and allows session-wise manipulation of such filesystems. It can load the management information of existing ISO images and it writes the session results to optical media or to filesystem objects. Vice versa xorriso is able to restore file objects from ISO 9660 filesystems. A special property of xorriso is that it needs neither an external ISO 9660 formatter program nor an external burn program for CD or DVD but rather incorporates the libraries of libburnia-project.org . Currently it is fully supported on GNU/Linux with kernels >= 2.4, on FreeBSD with ATAPI/CAM support enabled in the kernel, see atapicam(4), on OpenSolaris (tested with kernel 5.11), on NetBSD (tested with 6.1.2 and 6.1.3). On other X/Open compliant systems there will only be POSIX i/o with disk file objects, but no direct MMC operation on CD/DVD/BD drives. By using this software you agree to the disclaimer at the end of this text: "... without even the implied warranty ..." Compilation, First Glimpse, Installation The most simple way to get xorriso from source code is the GNU xorriso tarball. Prerequisites: The tarball contains everything that is needed except the following system components: libc, libpthread plus on Solaris: libvolmgt plus on FreeBSD: libiconv, libcam, IDE and SATA drives need atapicam Optional at compile time are: libreadline and the readline-dev headers, or libedit and its header, make dialog mode more convenient. zlib and zlib-devel allow zisofs compression. on GNU/Linux: libacl and libacl-devel allow getting and setting ACLs. If they were present at compile time, then the optional libraries have to be present at runtime, too. Obtain xorriso-1.5.7.tar.gz, take it to a directory of your choice and do: tar xzf xorriso-1.5.7.tar.gz cd xorriso-1.5.7 Within that directory execute: ./configure --prefix=/usr make This will produce a binary named ./xorriso/xorriso If you want xorriso to report a "Build timestamp" with its option -version : make buildstamped You may strip the binary to reduce it in size strip ./xorriso/xorriso You may copy or move it to a directory where it can be found by the shell, or you may execute xorriso at the place where it was built, or you may execute as superuser: make install For general concepts, options and usage examples see info xorriso info xorrisofs info xorrecord or man 1 xorriso man 1 xorrisofs man 1 xorrecord You may get a first glimpse by e.g. info ./xorriso/xorriso.info man ./xorriso/xorriso.1 The installation creates several alias links pointing to the xorriso binary: xorrisofs starts xorriso with -as mkisofs emulation already enabled xorrecord starts xorriso with -as cdrecord emulation already enabled osirrox starts with -osirrox image-to-disk copying already enabled By default xorriso will depend on libreadline if the library and its development header files are present at compile time. If not, then it will try to depend on libedit and its header file. Both conditional dependencies can be avoided by running ./configure --prefix=/usr --disable-libreadline make clean ; make Never omit the "make clean" command after switching enabling of libreadline. If you want to explicitly allow only the use of libedit, then do ./configure --prefix=/usr --disable-libreadline --enable-libedit Other deliberate dependency reduction options of ./configure are: --disable-libacl avoid use of ACL functions like acl_to_text() --disable-xattr avoid use of xattr functions like listxattr() on Linux or extattr_list_file() on FreeBSD --disable-zlib avoid use of zlib functions like compress2() this also avoids the use of libjte and option -jigdo. xorriso brings own system adapters which allow burning optical media on GNU/Linux, FreeBSD, Solaris, NetBSD. Alternatively it can use libcdio-0.83 or later for sending commands to optical drives: --enable-libcdio xorriso allows to use external processes as file content filters. This is a potential security risk which may be avoided by ./configure option --disable-external-filters By default the filter feature is disabled if effective user id and real user id differ. This ban can be lifted by --enable-external-filters-setuid Sometimes xorriso will yield better write performance on GNU/Linux if 64 KB are transmitted in each write operation rather than 32 KB. See option -dvd_obs . 64k can be made default at configure time by: --enable-dvd-obs-64k For xorriso -as cdrecord emulation only: In some situations GNU/Linux may deliver a better write performance to drives if the track input is read with O_DIRECT (see man 2 open). The included libburn and the cdrecord emulation of xorriso can be told to use this peculiar read mode by: --enable-track-src-odirect Linux only: libburn tries to avoid a collision with udev's drive examination by waiting 0.1 seconds before opening the device file for a longer time, after udev might have been alarmed by drive scanning activities. The waiting time can be set at ./configure time with microsecond granularity. E.g. 2 seconds: CFLAGS="$CFLAGS -DLibburn_udev_wait_useC=2000000" ./configure ...options... Waiting can be disabled by zero waiting time: CFLAGS="$CFLAGS -DLibburn_udev_wait_useC=0" Alternatively, libburn can try to be nice by opening the device file, closing it immediately, waiting, and only then opening it for real: CFLAGS="$CFLAGS -DLibburn_udev_extra_open_cyclE -DLibburn_udev_wait_useC=500000" xorriso under control of a (GUI) frontend process The dialog mode allows frontend programs to connect via pipes to the standard input and output of xorriso. Several commands of xorriso help with receiving and parsing of reply messages. As a proof of concept, there is the Tcl/Tk script xorriso-tcltk which can be launched by this shell command: xorriso-tcltk Or in the xorriso build directory, without installation of xorriso: xorriso/xorriso -launch_frontend frontend/xorriso-tcltk --stdio -- In the running GUI, click with the rightmost mouse button on any GUI element to get its particular help text. The "Help" button at the upper right corner gives a short introduction and instructions for some common use cases. See also file frontend/README-tcltk. See its Tcl code for getting an idea how this gets achieved. The script is part of the tarball and gets installed by make install. If a xorriso distro package does not install it, you may get it directly from https://dev.lovelyhq.com/libburnia/libisoburn/blob/master/frontend/xorriso-tcltk Further there is the C program frontend/frontend_pipes_xorriso.c which forks a xorriso process and shows the same communication gestures as xorriso-tcltk. In particular it connects to xorriso via two pipes, sends commands, waits for all replies of a command, picks info out of the xorriso message sieve, and parses reply message lines into words. The bash script frontend/sh_on_named_pipes.sh forks a xorriso process connected to two pipes. It then runs a dialog loop, sends commands to xorriso, and displays the replies. The sh script frontend/xorriso_broker.sh is intended to execute xorriso commands on a permanently running xorriso process. It gets an id_string by which it looks for named pipes with a running xorriso process. If no such pipe is found, then it starts a xorriso connected to newly created pipes. After this is done, the optionally given xorriso arguments are written into the stdin pipe from where xorriso will read and execute them. The script will end but the xorriso process will go on and wait for more commands. Drives and Disk File Objects The user of libisoburn applications needs rw-permission for the CD/DVD/BD drives which shall be used, even if only reading is intended. A list of rw-accessible drives can be obtained by xorriso -devices CD devices which offer not enough permission are invisible to normal users. The superuser should be able to see any usable drive and then set the permissions as needed. On Linux, FreeBSD, and NetBSD, rw-permissions are needed. On Solaris, the privilege "sys_devices" and r-permission are needed. The output of xorriso -devices might look like 0 -dev '/dev/sr0' rwrw-- : 'TSSTcorp' 'CDDVDW SH-S203B' 1 -dev '/dev/hda' rwrw-- : 'HL-DT-ST' 'DVD-ROM GDR8162B' On Linux, full and insecure enabling of both for everybody would look like chmod a+rw /dev/sr0 /dev/hda This is equivalent to the traditional setup chmod a+x,u+s cdrecord. On FreeBSD, device permissions are to be set in /etc/devfs.rules. On Solaris, pfexec privileges may be restricted to "basic,sys_devices". On NetBSD, rw-permission may be granted by chmod a+rw /dev/rcd?d. See below "System Dependent Drive Permission Examples". I strongly discourage to run xorriso with setuid root or via sudo ! It is not checked for the necessary degree of hacker safety. Better consider to grant the necessary permissions to group "floppy" and to add users to it. A possible source of problems are hald or other automounters. If you can spot a process "hald-addon-storage" with the address of your desired drive, then consider to kill it. A similar process "udisks-daemon: polling ..." can be seen on newer Linuxes. On Debian GNU/Linux 6.0.2 amd64 there is /lib/udev/rules.d/80-udisks.rules where one can remove all CD drives ("sr*") from the list of automountable devices: KERNEL=="sd*|hd*|mmcblk*|mspblk*", ENV{UDISKS_PRESENTATION_NOPOLICY}="0" # KERNEL=="sd*|hd*|sr*|mmcblk*|mspblk*", ENV{UDISKS_PRESENTATION_NOPOLICY}="0" Copying the recognition criterion from /etc/udev/rules.d/70-persistent-cd.rules one can prevent automounting a single drive, too: SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:11.0-scsi-2:0:0:0", ENV{UDISKS_PRESENTATION_NOPOLICY}:="1" If you cannot get rid of the automounter, try whether it helps to always load the drive tray manually before starting a write run of xorriso. Wait until the drive light is off and the mounted media appears. Then try to unmount the mounted media before a write run. Besides true optical drives, xorriso can also address disk files as input or output drives. By default paths to files under /dev are accepted only if the device represents a real optical drive. Other device files may be addressed by prepending "stdio:" to the path. Like: xorriso -dev stdio:/dev/sdb ...more arguments... This rule may be changed by xorriso option -drive_class. Prefix "mmc:" causes a path to be accepted only if it is a real optical drive which is accessible by generic SCSI/MMC commands. xorriso-dd-target GNU xorriso comes with a script named xorriso-dd-target/xorriso-dd-target which uses the util-linux program lsblk to find suitable hard-disk-like target devices for copying hard-disk bootable ISO images onto them. Such images are offered by GNU/Linux distributions for installing their system. xorriso-dd-target gets installed only if ./configure detects to run on a GNU/Linux system. It refuses to start on non-Linux kernels or if program lsblk is not found in /usr/sbin, /sbin, /usr/bin, /bin. For introduction, examples, and details see in the build directory man xorriso-dd-target/xorriso-dd-target.1 info xorriso-dd-target/xorriso-dd-target.info Testing For automated and manual tests of xorriso's functionality see file releng/README Result comparison with self produced ISO images We are quite sure that libisofs produces accurate representations of the disk files. This opinion is founded on a lot of test burns and checks by a little test program which compares files from the mounted image with the orignals on disk. It uses the normal POSIX filesystem calls, i.e. no libburnia stuff. This program is not installed systemwide but stays in the installation directory of the xorriso tarball as test/compare_file . Usually it is run as -exec payload of a find command. It demands at least three arguments: The path of the first file to compare, the prefix1 to be cut off from path and the prefix2 which gets prepended afterwards to obtain the path of the second file to compare. As further argument there can be -no_ctime which suppresses the comparison of ctime date stamps. The exit value is 0 if no difference was detected, non-0 else. Example: After xorriso ... -pathspecs on -add /=/original/dir -- -commit_eject all mount /media/dvd cd test compare tree /media/dvd with tree /original/dir : find /original/dir -exec ./compare_file '{}' /original/dir /media/dvd ';' \ | less and vice versa: find /media/dvd -exec ./compare_file '{}' /media/dvd /original/dir ';' \ | less File Formats Sector Maps Sector maps describe the valid and invalid blocks on a media or a disk copy of a media. xorriso creates and reads these file with its option -check_media. The file begins with 32 bytes of cleartext of which the last one is a newline character. The first 25 say "xorriso sector bitmap v2 " or "xorriso sector bitmap v3 ". The remaining six characters give the size of the info text as decimal number. This number of bytes follows the first 32 and will not be interpreted by xorriso. They are rather to inform a human reader about the media type and its track layout. After the info text there are two signed integers, most significant byte first. The number of bytes per integer is 4 for "v2" and 8 for "v3". In both cases, the highest bit of the integers must be 0. The first integer, N, gives the number of bits in the following bitmap. The second number, S, gives the number of 2 KiB blocks governed by a single bit in the map. Then come the bits in form of 8-bit bytes. Data block M is covered by bit B=M/S in the map, bit number B is stored in byte B/8 as bit B%8. A valid readable data block has its bit set to 1. Checksum Tags Checksum tags are data blocks inside an ISO 9660 image which do not belong to any file but rather tell the MD5 of a certain range of data blocks. The superblock checksum tag is written after the ECMA-119 volume descriptors. The tree checksum tag is written after the ECMA-119 directory entries. The session checksum tag is written after all payload including the checksum array. (Then follows padding.) The tags are single lines of printable text, padded by 0 bytes. They have the following format: Tag_id pos=# range_start=# range_size=# [session_start|next=#] md5=# self=#\n Parameters md5= and self= are 32 digit hex, the others are decimal numbers. Tag_id distinguishes the following tag types "libisofs_rlsb32_checksum_tag_v1" Relocated 64 kB superblock tag "libisofs_sb_checksum_tag_v1" Superblock tag "libisofs_tree_checksum_tag_v1" Directory tree tag "libisofs_checksum_tag_v1" Session end tag A relocated superblock may appear at LBA 0 of an image which was produced for being stored in a disk file or on overwritable media (e.g. DVD+RW, BD-RE). xorriso records the first session at LBA 32. A follow-up session begins at the next block address which is divisible by 32 and higher than the address of the previous session's end tag. Normally no session starts after the address given by relocated superblock parameter session_start=. Session oriented media like CD-R[W], DVD-R, DVD+R, BD-R will have no relocated superblock but rather bear a table-of-content on media level. A tag is valid if pos= tells its own block address and self= tells its own MD5 up to the last hex digit of md5=. range_start= tells the first block that is covered by md5=, range_size= tells the number of blocks covered by md5=. Relocated superblocks tell the block address of their session by session_start=. Superblock and tree tag tell the block address of the next tag by next=. The newline character at the end is mandatory. libisoburn xorriso is based on libisofs which does ISO 9660 filesystem aspects and on libburn which does the input and output aspects. Parts of this foundation are accessed via libisoburn, which is closely related to xorriso. libisoburn provides several services: - Encapsulation of coordination between libisofs and libburn. - Emulation of ISO 9660 multi-session on overwritable media or random access files. - Implementation of the xorriso API. The sourcecode of all three libraries is included in the GNU xorriso tarball. It is compiled with xorriso and linked statically. But you may as well get and install releases of libburn and libisofs, in order to be able to install a release of libisoburn which produces libisoburn.so.1 and a matching dynamically linked xorriso binary. This binary is very lean but depends on properly installed libraries of suitable revision. Dynamic library and compile time header requirements for libisoburn-1.5.6 : - libburn.so.4 , version libburn-1.5.6 or higher - libisofs.so.6 , version libisofs-1.5.6 or higher libisoburn and xorriso will not start with libraries which are older than their headers seen at compile time. So compile in the oldest possible installation setup unless you have reason to enforce a newer bug fix level. GNU xorriso has less runtime dependencies and can be moved more freely. System Dependent Drive Permission Examples Accessing the optical drives requires privileges which usually are granted only to the superuser. GNU/Linux, FreeBSD, Solaris, and NetBSD offer quite different approaches for avoiding the need for unrestricted privileges. First check whether some friendly system setting already allows you to access the drives as normal user: xorriso -devices Those drives of which you see address and type strings are already usable. If there remain drives invisible which the superuser can see by the same command, then the following examples might help: --------------- On all systems: --------------- Add the authorized users of CD drives to group "floppy" in /etc/group. If missing: create this group. Changes to /etc/group often only affect new login sessions. So log out and in before making the first tests. ------------- On GNU/Linux: ------------- Allow rw-access to the drives chgrp floppy /dev/sr0 /dev/sr1 chmod g+rw /dev/sr0 /dev/sr1 It might be necessary to perform chgrp and chmod after each reboot or to edit distro dependent device configuration files for permanent settings. ----------- On FreeBSD: ----------- Edit /etc/devfs.rules and make sure to have these lines [localrules=10] add path 'acd*' mode 0664 group floppy add path 'cd*' mode 0664 group floppy add path 'pass*' mode 0664 group floppy add path 'xpt*' mode 0664 group floppy [localrules=5] add path 'pass*' mode 0664 group floppy add path 'cd*' mode 0664 group floppy add path 'xpt*' mode 0664 group floppy add path 'acd*' mode 0664 group floppy Edit /etc/rc.conf and add the following line if missing devfs_system_ruleset="localrules" This gets into effect by reboot or by command /etc/rc.d/devfs start ----------- On Solaris: ----------- Run xorriso by pfexec xorriso ...arguments... The following settings will make pfexec keep original UID and EUID and prevent most superuser powers. Be aware that you still can manipulate all device files if you have the file permissions for that. Full root privileges for xorriso can then be acquired only by command su. Edit /etc/security/exec_attr and add this line to the other "Media Backup" lines: Media Backup:solaris:cmd:::/usr/local/bin/xorriso:privs=basic,sys_devices Edit /etc/user_attr and add profile "Media Backup" to the user's line: thomas::::profiles=Media Backup,Primary Administrator;roles=root See also man privileges, man exec_attr, man user_attr. Then allow the group r-access to the drives pfexec chgrp floppy /dev/rdsk/c3t0d0s2 /dev/rdsk/c4t0d0s2 pfexec chmod g+r /dev/rdsk/c3t0d0s2 /dev/rdsk/c4t0d0s2 The last two commands have to be executed after each boot. I do not know the relevant device configuration files yet. ---------- On NetBSD: ---------- Allow rw-access to the drives chgrp floppy /dev/rcd[01]d chmod g+rw /dev/rcd[01]d ------------------------------------------------------------------------------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 or later as published by the Free Software Foundation. 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 ------------------------------------------------------------------------------ GNU xorriso is feature-wise equivalent to the dynamic compilation of libburnia libraries and libburnia program xorriso. It restricts itself to a technical form where the legal commitments of the libburnia project and the legal intentions of FSF match completely. Libburnia project is committed to provide support for this copy in the same way as for its own software releases. It is further committed to keep its own licenses open for obtaining future copies under GPLv2+. ------------------------------------------------------------------------------ libburnia program xorriso is based on and sub project of: libburnia-project.org By Mario Danic <mario.danic@gmail.com>, libburn, libisofs Vreixo Formoso <metalpain2002@yahoo.es>, libisofs, libisoburn Thomas Schmitt <scdbackup@gmx.net>, libburn, libisofs, libisoburn, xorriso Copyright (C) 2006-2024 Mario Danic, Vreixo Formoso, Thomas Schmitt. libburnia-project.org is inspired by and in libburn still containing parts of old Libburn. By Derek Foreman <derek@signalmarketing.com> and Ben Jansens <xor@orodu.net> Copyright (C) 2002-2006 Derek Foreman and Ben Jansens GNU xorriso contains libjte out of source package jigit >= 1.17 Copyright (C) 2000-2007 Free Software Foundation, Inc. 2004-2011 Steve McIntyre 2010-2011 George Danchev, Thomas Schmitt GNU xorriso contains xorriso-dd-target Copyright (C) 2019-2021 Nio Wiklund alias sudodus, Thomas Schmitt ------------------------------------------------------------------------------ This text itself is Copyright (c) 2007 - 2024 Thomas Schmitt <scdbackup@gmx.net> and is freely distributable. It shall only be modified in sync with the technical properties of xorriso. If you make use of the license to derive modified versions of xorriso then you are entitled to modify this text under that same license. /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementations of classes: - SplitparT which represents byte intervals of data files. - DirseQ which crawls along a directory's content list. - ExclusionS which manages the list of excluded file paths and leaf patterns. Because of its structural identity it is also used for disk address oriented hiding at insert time as of mkisofs. - Xorriso_lsT which provides a generic double-linked list. - LinkiteM, PermiteM which temporarily record relations and states. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <utime.h> #include <dirent.h> #include <errno.h> #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" /* ---------------------------- SplitparT ------------------------- */ struct SplitparT { char *name; int partno; int total_parts; off_t offset; off_t bytes; off_t total_bytes; }; static char Splitpart_wordS[][16]= {"part_", "_of_", "_at_", "_with_", "_of_"}; int Splitparts_new(struct SplitparT **o, int count, int flag) { int i; (*o)= TSOB_FELD(struct SplitparT, count); if((*o)==NULL) return(-1); for(i= 0; i<count; i++) { (*o)[i].name= NULL; (*o)[i].partno= 0; (*o)[i].total_parts= 0; (*o)[i].offset= 0; (*o)[i].bytes= 0; (*o)[i].total_bytes= 0; } return(1); } int Splitparts_destroy(struct SplitparT **o, int count, int flag) { int i; if((*o)==NULL) return(0); for(i= 0; i<count; i++) { if((*o)[i].name!=NULL) free((*o)[i].name); } free(*o); *o= NULL; return(1); } int Splitparts_set(struct SplitparT *o, int idx, char *name, int partno, int total_parts, off_t offset, off_t bytes, off_t total_bytes, int flag) { if(o[idx].name!=NULL) free(o[idx].name); o[idx].name= strdup(name); if(o[idx].name==NULL) return(-1); o[idx].partno= partno; o[idx].total_parts= total_parts; o[idx].offset= offset; o[idx].bytes= bytes; o[idx].total_bytes= total_bytes; return(1); } int Splitparts_get(struct SplitparT *o, int idx, char **name, int *partno, int *total_parts, off_t *offset, off_t *bytes, off_t *total_bytes, int flag) { *name= o[idx].name; *partno= o[idx].partno; *total_parts= o[idx].total_parts; *offset= o[idx].offset; *bytes= o[idx].bytes; *total_bytes= o[idx].total_bytes; return(1); } int Splitpart__read_next_num(char *base_pt, char **next_pt, off_t *num, int flag) { char *cpt, *ept, scale[4]; double sfak; *num= 0; for(cpt= base_pt; *cpt!=0 && !isdigit(*cpt); cpt++); if(*cpt==0) return(0); for(ept= cpt; *ept!=0 && isdigit(*ept); ept++) *num= (*num)*10+(*ept)-'0'; scale[0]= '1'; scale[1]= *ept; scale[2]= 0; sfak= Scanf_io_size(scale, 0); *num *= (off_t) sfak; if(sfak > 1.0) ept++; *next_pt= ept; return(1); } int Splitpart__parse(char *name, int *partno, int *total_parts, off_t *offset, off_t *bytes, off_t *total_bytes, int flag) { int ret; off_t num; char *cpt, *ept; cpt= name; if(strncmp(cpt, Splitpart_wordS[0], strlen(Splitpart_wordS[0])) != 0) return(0); ret= Splitpart__read_next_num(cpt, &ept, &num, 0); if(ret<=0) return(ret); *partno= num; cpt= ept; if(strncmp(cpt, Splitpart_wordS[1], strlen(Splitpart_wordS[1])) != 0) return(0); ret= Splitpart__read_next_num(cpt, &ept, &num, 0); if(ret<=0) return(ret); *total_parts= num; cpt= ept; if(strncmp(cpt, Splitpart_wordS[2], strlen(Splitpart_wordS[2])) != 0) return(0); ret= Splitpart__read_next_num(cpt, &ept, offset, 0); if(ret<=0) return(ret); cpt= ept; if(strncmp(cpt, Splitpart_wordS[3], strlen(Splitpart_wordS[3])) != 0) return(0); ret= Splitpart__read_next_num(cpt, &ept, bytes, 0); if(ret<=0) return(ret); cpt= ept; if(strncmp(cpt, Splitpart_wordS[4], strlen(Splitpart_wordS[4])) != 0) return(0); ret= Splitpart__read_next_num(cpt, &ept, total_bytes, 0); if(ret<=0) return(ret); if(*ept != 0) return(0); return(1); } int Splitpart__is_part_path(char *path, int flag) { int partno, total_parts, ret; off_t offset, bytes, total_bytes; char *name; name= strrchr(path, '/'); if(name == NULL) name= path; else name++; ret= Splitpart__parse(name, &partno, &total_parts, &offset, &bytes, &total_bytes, 0); return(ret > 0); } /* part_#_of_#_at_#_with_#_of_# */ int Splitpart__compose(char *adr, int partno, int total_parts, off_t offset, off_t bytes, off_t total_bytes, int flag) { sprintf(adr, "%s%d%s%d%s", Splitpart_wordS[0], partno, Splitpart_wordS[1], total_parts, Splitpart_wordS[2]); if((offset % (1024*1024))==0 && offset>0) { Sfile_off_t_text(adr+strlen(adr), offset / (1024*1024), 0); strcat(adr, "m"); } else Sfile_off_t_text(adr+strlen(adr), offset, 0); strcat(adr, Splitpart_wordS[3]); if((bytes % (1024*1024))==0) { Sfile_off_t_text(adr+strlen(adr), bytes / (1024*1024), 0); strcat(adr, "m"); } else Sfile_off_t_text(adr+strlen(adr), bytes, 0); strcat(adr, Splitpart_wordS[4]); Sfile_off_t_text(adr+strlen(adr), total_bytes, 0); return(1); } int Splitparts_cmp(const void *v1, const void *v2) { struct SplitparT *p1, *p2; p1= (struct SplitparT *) v1; p2= (struct SplitparT *) v2; if(p1->partno>p2->partno) return(1); if(p1->partno<p2->partno) return(-1); if(p1->offset>p2->offset) return(1); if(p1->offset<p2->offset) return(-1); return(0); } int Splitparts_sort(struct SplitparT *o, int count, int flag) { qsort(o, (size_t) count, sizeof(struct SplitparT), Splitparts_cmp); return(1); } /* ---------------------------- End SplitparT ------------------------- */ /* ------------------------------ DirseQ ------------------------------ */ static int Dirseq_buffer_sizE= 100; struct DirseQ { char adr[SfileadrL]; DIR *dirpt; int count; char **buffer; int buffer_size; int buffer_fill; int buffer_rpt; struct DirseQ *next; }; int Dirseq_destroy(struct DirseQ **o, int flag); int Dirseq_next_adrblock(struct DirseQ *o, char *replies[], int *reply_count, int max_replies, int flag); int Dirseq_new(struct DirseQ **o, char *adr, int flag) /* bit0= with non-fatal errors do not complain about failed opendir() */ { int ret,i,severe_error; struct DirseQ *m; m= *o= TSOB_FELD(struct DirseQ,1); if(m==NULL) return(-1); m->adr[0]= 0; m->dirpt= NULL; m->count= 0; m->buffer= NULL; m->buffer_size= 0; m->buffer_fill= 0; m->buffer_rpt= 0; m->next= NULL; if(Sfile_str(m->adr, adr, 0)<=0) {ret= -1; goto failed;} m->buffer= TSOB_FELD(char *,Dirseq_buffer_sizE); if(m->buffer==NULL) {ret= -1; goto failed;} m->buffer_size= Dirseq_buffer_sizE; for(i= 0;i<m->buffer_size;i++) m->buffer[i]= NULL; if(adr[0]==0) m->dirpt= opendir("."); else m->dirpt= opendir(adr); if(m->dirpt==NULL) { severe_error= (errno && errno!=ENOENT && errno!=EACCES && errno!=ENOTDIR); if(severe_error || !(flag&1)) fprintf(stderr,"opendir(%s) failed : %s\n",adr,strerror(errno)); ret= -severe_error; goto failed; } return(1); failed:; Dirseq_destroy(o,0); return(ret); } int Dirseq_destroy(struct DirseQ **o, int flag) { int i; if(*o==NULL) return(0); if((*o)->dirpt!=NULL) closedir((*o)->dirpt); if((*o)->buffer!=NULL) { for(i=0;i<(*o)->buffer_size;i++) if((*o)->buffer[i]!=NULL) free((*o)->buffer[i]); free((char *) (*o)->buffer); } free((char *) *o); (*o)= NULL; return(1); } int Dirseq_set_next(struct DirseQ *o, struct DirseQ *next, int flag) { o->next= next; return(1); } int Dirseq_get_next(struct DirseQ *o, struct DirseQ **next, int flag) { *next= o->next; return(1); } int Dirseq_get_adr(struct DirseQ *o, char **adrpt, int flag) { *adrpt= o->adr; return(1); } int Dirseq_rewind(struct DirseQ *o, int flag) { rewinddir(o->dirpt); return(1); } int Dirseq_next_adr(struct DirseQ *o, char reply[SfileadrL], int flag) /* flag: bit0= permission to use buffer bit1= do not increment counter bit2= ignore buffer in any case bit3= do not exclude '.' and '..' bit4= sort buffer bit5= sort only incomplete last buffer return: <0 error 0= no more entries available 1= ok, reply is valid */ { int ret; struct dirent *entry; char *name; static int override_flag_0= 0,override_flag_1= 32; flag= (flag&~override_flag_0)|override_flag_1; if((flag&1) && o->buffer_rpt>=o->buffer_fill) { /* permission to buffer and buffer empty : load a buffer */ ret= Dirseq_next_adrblock(o,o->buffer,&(o->buffer_fill), o->buffer_size,2|4|(flag&16)); if(ret<=0) return(ret); o->buffer_rpt= 0; if((flag&32) && o->buffer_fill<o->buffer_size && o->buffer_fill>0) Sort_argv(o->buffer_fill,o->buffer,0); } if(o->buffer_rpt<o->buffer_fill && !(flag&4)) { ret= Sfile_str(reply,o->buffer[o->buffer_rpt],0); Sregex_string(&(o->buffer[o->buffer_rpt]),NULL,0); if(ret<=0) return(-1); (o->buffer_rpt)++; if(!(flag&2)) o->count++; return(1); } do { entry= readdir(o->dirpt); if(entry==NULL) { /* >>> how to distinguish error from EOF , do i need a (FILE *) ? */ return(0); } if(strlen(entry->d_name)>=SfileadrL) { fprintf(stderr,"--- oversized directory entry (number %d) :\n %s", o->count+1,entry->d_name); return(-1); } name= entry->d_name; if(flag&8) break; /* skip "." and ".." */ } while(name[0]=='.' && ((name[1]=='.' && name[2]==0) || name[1]==0)); if(Sfile_str(reply,name,0)<=0) return(-1); if(!(flag&2)) o->count++; return(1); } int Dirseq_next_adrblock(struct DirseQ *o, char *replies[], int *reply_count, int max_replies, int flag) /* @param replies A vector of Sregex_string pointers */ /* flag: bit0= permission to use buffer bit1= do not increment counter bit2= ignore buffer in any case bit4= sort replies return: <0 error 0= no more entries available 1= ok, reply is valid */ { int i,ret; char *reply= NULL; reply= TSOB_FELD(char, SfileadrL); if(reply == NULL) return(-1); *reply_count= 0; for(i=0;i<max_replies;i++) { ret= Dirseq_next_adr(o,reply,flag&(1|2|4)); if(ret<0) goto ex; if(ret==0) break; if(Sregex_string(&(replies[i]),reply,0)<=0) {ret= -1; goto ex;} (*reply_count)++; } if((*reply_count)==0) {ret= 0; goto ex;} if(flag&16) Sort_argv(*reply_count,replies,0); ret= 1; ex:; free(reply); return(ret); } /* ---------------------------- End DirseQ ----------------------------- */ /* ---------------------------- Xorriso_lsT ----------------------------- */ /* @param flag Bitfield for control purposes bit0= insert before link rather than after it bit1= do not copy data (e.g. because *data is invalid) bit2= attach data directly by pointer rather than by copying */ int Xorriso_lst_new_binary(struct Xorriso_lsT **lstring, char *data, int data_len, struct Xorriso_lsT *link, int flag) { struct Xorriso_lsT *s; s= TSOB_FELD(struct Xorriso_lsT,1); if(s==NULL) return(-1); s->text= NULL; s->next= s->prev= NULL; if(flag & 4) { s->text= data; } else { if(data_len<=0) goto failed; s->text= Smem_malloC(data_len); if(s->text==NULL) goto failed; if(!(flag&2)) memcpy(s->text,data,data_len); } if(link==NULL) { ; } else if(flag&1) { s->next= link; s->prev= link->prev; if(link->prev!=NULL) link->prev->next= s; link->prev= s; } else { s->prev= link; s->next= link->next; if(link->next!=NULL) link->next->prev= s; link->next= s; } *lstring= s; return(1); failed:; *lstring= s; Xorriso_lst_destroy(lstring,0); return(-1); } /* @param flag Bitfield for control purposes see Xorriso_lst_new_binary() */ int Xorriso_lst_new(struct Xorriso_lsT **lstring, char *text, struct Xorriso_lsT *link, int flag) { int ret; ret= Xorriso_lst_new_binary(lstring,text,strlen(text)+1,link,flag); return(ret); } /* @param flag Bitfield for control purposes bit0= do not set *lstring to NULL */ int Xorriso_lst_destroy(struct Xorriso_lsT **lstring, int flag) { struct Xorriso_lsT *s; s= *lstring; if(s==NULL) return(0); if(s->prev!=NULL) s->prev->next= s->next; if(s->next!=NULL) s->next->prev= s->prev; if(s->text!=NULL) Smem_freE(s->text); Smem_freE((char *) s); if(!(flag&1)) *lstring= NULL; return(1); } int Xorriso_lst_destroy_all(struct Xorriso_lsT **lstring, int flag) { struct Xorriso_lsT *s,*next; if(lstring==NULL) return(-1); if((*lstring)==NULL) return(0); for(s= *lstring; s->prev!=NULL; s= s->prev); for(;s!=NULL;s= next){ next= s->next; Xorriso_lst_destroy(&s,0); } *lstring= NULL; return(1); } int Xorriso_lst_append_binary(struct Xorriso_lsT **entry, char *data, int data_len, int flag) { struct Xorriso_lsT *target= NULL,*newby; if(*entry!=NULL) for(target= *entry; target->next!=NULL; target= target->next); if(Xorriso_lst_new_binary(&newby, data, data_len, target, flag & ~1)<=0) return(-1); if(*entry==NULL || (flag & 1)) *entry= newby; return(1); } struct Xorriso_lsT *Xorriso_lst_get_next(struct Xorriso_lsT *entry, int flag) { return(entry->next); } struct Xorriso_lsT *Xorriso_lst_get_prev(struct Xorriso_lsT *entry, int flag) { return(entry->prev); } char *Xorriso_lst_get_text(struct Xorriso_lsT *entry, int flag) { return(entry->text); } int Xorriso_lst_detach_text(struct Xorriso_lsT *entry, int flag) { entry->text= NULL; return(1); } int Xorriso_lst_get_last(struct Xorriso_lsT *entry, struct Xorriso_lsT **last, int flag) { *last= NULL; if(entry != NULL) for((*last)= entry; (*last)->next != NULL; (*last)= (*last)->next); return(1); } int Xorriso_lst_concat(struct Xorriso_lsT *first, struct Xorriso_lsT *second, int flag) { struct Xorriso_lsT *last; Xorriso_lst_get_last(first, &last, 0); if(last != NULL) last->next= second; if(second != NULL) second->prev= last; return(1); } /* --------------------------- End Xorriso_lsT ---------------------------- */ /* ------------------------------ ExclusionS ------------------------------ */ struct ExclusionS { /* Absolute input patterns which lead to not_paths */ struct Xorriso_lsT *not_paths_descr; /* Actually banned absolute paths */ struct Xorriso_lsT *not_paths; /* Input patterns which lead to not_leafs */ struct Xorriso_lsT *not_leafs_descr; /* Compiled not_leaf patterns. Caution: not char[] but regex_t */ struct Xorriso_lsT *not_leafs; }; int Exclusions_new(struct ExclusionS **o, int flag) { struct ExclusionS *m; m= *o= TSOB_FELD(struct ExclusionS, 1); if(m==NULL) return(-1); m->not_paths_descr= NULL; m->not_paths= NULL; m->not_leafs_descr= NULL; m->not_leafs= NULL; return(1); } int Exclusions_destroy(struct ExclusionS **o, int flag) { struct Xorriso_lsT *s,*next; if((*o)==NULL) return(0); Xorriso_lst_destroy_all(&((*o)->not_paths_descr), 0); Xorriso_lst_destroy_all(&((*o)->not_paths), 0); Xorriso_lst_destroy_all(&((*o)->not_leafs_descr), 0); for(s= (*o)->not_leafs; s!=NULL; s= next){ next= s->next; regfree((regex_t *) s->text); Xorriso_lst_destroy(&s, 0); } free((char *) *o); (*o)= NULL; return(1); } int Exclusions_add_not_paths(struct ExclusionS *o, int descrc, char **descrs, int pathc, char **paths, int flag) { struct Xorriso_lsT *s, *new_s; int i, ret; s= NULL; if(o->not_paths_descr!=NULL) for(s= o->not_paths_descr; s->next!=NULL; s= s->next); for(i= 0; i<descrc; i++) { ret= Xorriso_lst_new(&new_s, descrs[i], s, 0); if(ret<=0) return(ret); if(o->not_paths_descr==NULL) o->not_paths_descr= new_s; s= new_s; } s= NULL; if(o->not_paths!=NULL) for(s= o->not_paths; s->next!=NULL; s= s->next); for(i= 0; i<pathc; i++) { ret= Xorriso_lst_new(&new_s, paths[i], s, 0); if(ret<=0) return(ret); if(o->not_paths==NULL) o->not_paths= new_s; s= new_s; } return(1); } /* @return -1=cannot store , 0=cannot compile regex , 1=ok */ int Exclusions_add_not_leafs(struct ExclusionS *o, char *not_leafs_descr, regex_t *re, int flag) { int ret; ret= Xorriso_lst_append_binary(&(o->not_leafs_descr), not_leafs_descr, strlen(not_leafs_descr)+1, 0); if(ret<=0) return(-1); ret= Xorriso_lst_append_binary(&(o->not_leafs), (char *) re, sizeof(regex_t), 0); if(ret<=0) return(-1); return(1); } /* @param flag bit0= whole subtree is banned with -not_paths @return 0=no match , 1=not_paths , 2=not_leafs, <0=error */ int Exclusions_match(struct ExclusionS *o, char *abs_path, int flag) { struct Xorriso_lsT *s; char *leaf= NULL, *leaf_pt; regmatch_t match[1]; int ret, was_non_slash, l; /* test abs_paths */ if(flag&1) { for(s= o->not_paths; s!=NULL; s= s->next) { l= strlen(s->text); if(strncmp(abs_path, s->text, l)==0) if(abs_path[l]=='/' || abs_path[l]==0) {ret= 1; goto ex;} } } else { for(s= o->not_paths; s!=NULL; s= s->next) if(strcmp(abs_path, s->text)==0) {ret= 1; goto ex;} } /* determine leafname */ was_non_slash= 0; for(leaf_pt= abs_path+strlen(abs_path); leaf_pt >= abs_path; leaf_pt--) { if(*leaf_pt=='/') { if(was_non_slash) { leaf_pt++; break; } } else if(*leaf_pt!=0) was_non_slash= 1; } if(strlen(leaf_pt)>=SfileadrL) {ret= -1; goto ex;} leaf= strdup(leaf_pt); leaf_pt= strchr(leaf, '/'); if(leaf_pt!=NULL) *leaf_pt= 0; /* test with leaf expressions */ for(s= o->not_leafs; s!=NULL; s= s->next) { ret= regexec((regex_t *) s->text, leaf, 1, match, 0); if(ret==0) {ret= 2; goto ex;} } ret= 0; ex: if(leaf != NULL) free(leaf); return(ret); } int Exclusions_get_descrs(struct ExclusionS *o, struct Xorriso_lsT **not_paths_descr, struct Xorriso_lsT **not_leafs_descr, int flag) { *not_paths_descr= o->not_paths_descr; *not_leafs_descr= o->not_leafs_descr; return(1); } /* ---------------------------- End ExclusionS ---------------------------- */ /* ------------------------------ LinkiteM -------------------------------- */ struct LinkiteM { char *link_path; dev_t target_dev; ino_t target_ino; int link_count; struct LinkiteM *next; }; int Linkitem_new(struct LinkiteM **o, char *link_path, dev_t target_dev, ino_t target_ino, struct LinkiteM *next, int flag) { struct LinkiteM *m; m= *o= TSOB_FELD(struct LinkiteM,1); if(m==NULL) return(-1); m->target_dev= target_dev; m->target_ino= target_ino; m->next= next; m->link_count= 1; if(next!=NULL) m->link_count= m->next->link_count+1; m->link_path= strdup(link_path); if(m->link_path==NULL) goto failed; return(1); failed:; Linkitem_destroy(o, 0); return(-1); } int Linkitem_destroy(struct LinkiteM **o, int flag) { if((*o)==NULL) return(0); if((*o)->link_path!=NULL) free((*o)->link_path); free((char *) (*o)); *o= NULL; return(1); } int Linkitem_reset_stack(struct LinkiteM **o, struct LinkiteM *to, int flag) { struct LinkiteM *m, *m_next= NULL; /* Prevent memory corruption */ for(m= *o; m!=to; m= m->next) if(m==NULL) { /* this may actually not happen */ *o= to; return(-1); } for(m= *o; m!=to; m= m_next) { m_next= m->next; Linkitem_destroy(&m, 0); } *o= to; return(1); } int Linkitem_find(struct LinkiteM *stack, dev_t target_dev, ino_t target_ino, struct LinkiteM **result, int flag) { struct LinkiteM *m; for(m= stack; m!=NULL; m= m->next) { if(target_dev == m->target_dev && target_ino == m->target_ino) { *result= m; return(1); } } return(0); } int Linkitem_get_link_count(struct LinkiteM *item, int flag) { return(item->link_count); } /* ------------------------------ PermstacK ------------------------------- */ struct PermiteM { char *disk_path; struct stat stbuf; int chattr_flags; /* bit0= set chattr immutable bit bit1= only set chattr bits bit2= set chattr append-only bit */ struct PermiteM *next; }; /* @param flag bit0= Linux chattr immutable bit is set bit1= when popping only set chattr bits bit2= Linux chattr append-only bit is set */ int Permstack_push(struct PermiteM **o, char *disk_path, struct stat *stbuf, int flag) { struct PermiteM *m; m= TSOB_FELD(struct PermiteM,1); if(m==NULL) return(-1); m->disk_path= NULL; memcpy(&(m->stbuf), stbuf, sizeof(struct stat)); m->chattr_flags= flag & 7; m->next= *o; m->disk_path= strdup(disk_path); if(m->disk_path==NULL) goto failed; *o= m; return(1); failed:; if(m->disk_path!=NULL) free(m->disk_path); free((char *) m); return(-1); } /* @param flag bit0= minimal transfer: access permissions only bit1= do not set timestamps bit2= do not set chattr flags */ int Permstack_pop(struct PermiteM **o, struct PermiteM *stopper, struct XorrisO *xorriso, int flag) { int ret; struct utimbuf utime_buffer; struct PermiteM *m, *m_next; if((*o)==stopper) return(1); for(m= *o; m!=NULL; m= m->next) if(m->next==stopper) break; if(m==NULL) { sprintf(xorriso->info_text, "Program error: Permstack_pop() : cannot find stopper"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); return(-1); } for(m= *o; m!=stopper; m= m_next) { if(!(m->chattr_flags & 2)) { ret= chmod(m->disk_path, m->stbuf.st_mode); if(ret==-1) { if(xorriso!=NULL) { sprintf(xorriso->info_text, "Cannot change access permissions of disk directory: chmod %o ", (unsigned int) (m->stbuf.st_mode & 07777)); Text_shellsafe(m->disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); } } } if(!((flag & 1) || (m->chattr_flags & 2))) { ret= chown(m->disk_path, m->stbuf.st_uid, m->stbuf.st_gid); /* don't complain if it fails */ if(!(flag&2)) { utime_buffer.actime= m->stbuf.st_atime; utime_buffer.modtime= m->stbuf.st_mtime; ret= utime(m->disk_path,&utime_buffer); if(ret==-1 && xorriso!=NULL) { sprintf(xorriso->info_text, "Cannot change timestamps of disk directory: "); Text_shellsafe(m->disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); } } } if((m->chattr_flags & (1 | 4)) && !(flag & 4)) { /* It seems tradition here to just complain but to go on to return 1 */ Xorriso_set_local_chattr_ia(xorriso, m->disk_path, (m->chattr_flags & 1) | ((m->chattr_flags & 4) >> 1)); } m_next= m->next; free(m->disk_path); free((char *) m); *o= m_next; } return(1); } /* Look for stack item with disk_path @return 0= nothing found, 1= *stbuf and *chattr_flags are valid */ int Permstack_peek(struct PermiteM **o, struct PermiteM *stopper, struct XorrisO *xorriso, char *disk_path, struct stat **stbuf, int *chattr_flags, int flag) { struct PermiteM *m; if((*o) == stopper) return(0); for(m= *o; m != NULL; m= m->next) { if(strcmp(m->disk_path, disk_path) == 0) { *stbuf= &(m->stbuf); *chattr_flags= m->chattr_flags; return(1); } if(m->next == stopper) break; } if(m == NULL) { sprintf(xorriso->info_text, "Program error: Permstack_peek() : cannot find stopper"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); return(-1); } return(0); } /* ---------------------------- End PermstacK ----------------------------- */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2010 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of classes: - SplitparT which represents byte intervals of data files. - DirseQ which crawls along a directory's content list. - ExclusionS which manages the list of excluded file paths and leaf patterns. - Xorriso_lsT which provides a generic double-linked list. - LinkiteM, PermiteM which temporarily record relations and states. */ #ifndef Xorriso_pvt_auxobj_includeD #define Xorriso_pvt_auxobj_includeD yes struct SplitparT; int Splitparts_new(struct SplitparT **o, int count, int flag); int Splitparts_destroy(struct SplitparT **o, int count, int flag); int Splitparts_set(struct SplitparT *o, int idx, char *name, int partno, int total_parts, off_t offset, off_t bytes, off_t total_bytes, int flag); int Splitparts_get(struct SplitparT *o, int idx, char **name, int *partno, int *total_parts, off_t *offset, off_t *bytes, off_t *total_bytes, int flag); int Splitpart__parse(char *name, int *partno, int *total_parts, off_t *offset, off_t *bytes, off_t *total_bytes, int flag); int Splitpart__is_part_path(char *path, int flag); int Splitpart__compose(char *adr, int partno, int total_parts, off_t offset, off_t bytes, off_t total_bytes, int flag); int Splitpart__read_next_num(char *base_pt, char **next_pt, off_t *num, int flag); int Splitparts_sort(struct SplitparT *o, int count, int flag); struct DirseQ; int Dirseq_new(struct DirseQ **o, char *adr, int flag); int Dirseq_destroy(struct DirseQ **o, int flag); int Dirseq_next_adr(struct DirseQ *o, char reply[SfileadrL], int flag); int Dirseq_rewind(struct DirseQ *o, int flag); struct Xorriso_lsT { char *text; struct Xorriso_lsT *prev,*next; }; /** Create a new list item with arbitrary byte content. @param lstring The newly created object or NULL on failure @param data An array of bytes to be copied into the new object @param data_len Number of bytes to be copied @param link Xorriso_lsT object to which the new object shall be linked @param flag Bitfield for control purposes bit0= insert before link rather than after it bit1= do not copy data (e.g. because *data is invalid) bit2= attach data directly by pointer rather than by copying @return <=0 error, 1 ok */ int Xorriso_lst_new_binary(struct Xorriso_lsT **lstring, char *data, int data_len, struct Xorriso_lsT *link, int flag); /** Create a new list item with a 0-terminated text as content. @param lstring The newly created object or NULL on failure @param text A 0-terminated array of bytes @param link Xorriso_lsT object to which the new object shall be linked @param flag see Xorriso_lst_new_binary @return <=0 error, 1 ok */ int Xorriso_lst_new(struct Xorriso_lsT **lstring, char *text, struct Xorriso_lsT *link, int flag); /** Create a new list item at the end of a given list. @param entry Contains as input a pointer to a pointer to any existing list item. As output this list item pointer may be changed to the address of the new list item: if ((*entry == 0) || (flag & 1)) @param data An array of bytes to be copied into the new object @param data_len Number of bytes to be copied @param flag Bitfield for control purposes bit0= Return new object address in *entry bit1= do not copy data (e.g. because *data is invalid) bit2= attach data directly by pointer rather than by copying @return <=0 error, 1 ok */ int Xorriso_lst_append_binary(struct Xorriso_lsT **entry, char *data, int data_len, int flag); /** Destroy a single list item and connect its eventual list neighbors. @param lstring pointer to the pointer to be freed and set to NULL @param flag unused yet, submit 0 @return 0= *lstring was already NULL, 1= ok */ int Xorriso_lst_destroy(struct Xorriso_lsT **lstring, int flag); struct Xorriso_lsT *Xorriso_lst_get_next(struct Xorriso_lsT *entry, int flag); struct Xorriso_lsT *Xorriso_lst_get_prev(struct Xorriso_lsT *entry, int flag); char *Xorriso_lst_get_text(struct Xorriso_lsT *entry, int flag); int Xorriso_lst_detach_text(struct Xorriso_lsT *entry, int flag); int Xorriso_lst_get_last(struct Xorriso_lsT *entry, struct Xorriso_lsT **last, int flag); int Xorriso_lst_concat(struct Xorriso_lsT *first, struct Xorriso_lsT *second, int flag); int Exclusions_new(struct ExclusionS **o, int flag); int Exclusions_destroy(struct ExclusionS **o, int flag); int Exclusions_get_descrs(struct ExclusionS *o, struct Xorriso_lsT **not_paths_descr, struct Xorriso_lsT **not_leafs_descr, int flag); /* @param flag bit0= whole subtree is banned with -not_paths @return 0=no match , 1=not_paths , 2=not_leafs, <0=error */ int Exclusions_match(struct ExclusionS *o, char *abs_path, int flag); int Exclusions_add_not_leafs(struct ExclusionS *o, char *not_leafs_descr, regex_t *re, int flag); int Exclusions_add_not_paths(struct ExclusionS *o, int descrc, char **descrs, int pathc, char **paths, int flag); struct LinkiteM; /* Trace of hops during symbolic link resolution */ int Linkitem_new(struct LinkiteM **o, char *link_path, dev_t target_dev, ino_t target_ino, struct LinkiteM *next, int flag); int Linkitem_destroy(struct LinkiteM **o, int flag); int Linkitem_reset_stack(struct LinkiteM **o, struct LinkiteM *to, int flag); int Linkitem_find(struct LinkiteM *stack, dev_t target_dev, ino_t target_ino, struct LinkiteM **result, int flag); int Linkitem_get_link_count(struct LinkiteM *item, int flag); struct PermiteM; /* Stack of temporarily altered access permissions */ int Permstack_push(struct PermiteM **o, char *disk_path, struct stat *stbuf, int flag); int Permstack_pop(struct PermiteM **o, struct PermiteM *stopper, struct XorrisO *xorriso, int flag); /* Look for stack item with disk_path @param chattr_flag bit0= when popping: set chattr bit 'i' bit1= when popping: only set chattr bits bit2= when popping: set chattr bit 'a' @return 0= nothing found, 1= *stbuf and *chattr_flags are valid */ int Permstack_peek(struct PermiteM **o, struct PermiteM *stopper, struct XorrisO *xorriso, char *disk_path, struct stat **stbuf, int *chattr_flags, int flag); #endif /* ! Xorriso_pvt_auxobj_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains functions which are needed to read data from ISO image. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include <pthread.h> #ifdef HAVE_STDINT_H #include <stdint.h> #else #ifdef HAVE_INTTYPES_H #include <inttypes.h> #endif #endif #ifdef Xorriso_standalonE #ifdef Xorriso_with_libjtE #include "../libjte/libjte.h" #endif #else #ifdef Xorriso_with_libjtE #include <libjte/libjte.h> #endif #endif /* ! Xorriso_standalonE */ #include "xorriso.h" #include "xorriso_private.h" #include "base_obj.h" #include "lib_mgt.h" /* See Xorriso__preset_signal_behavior() */ static int Xorriso_signal_behavioR= 1; void Xorriso__version(int *major, int *minor, int *micro) { *major= Xorriso_header_version_majoR; *minor= Xorriso_header_version_minoR; *micro= Xorriso_header_version_micrO; } int Xorriso__is_compatible(int major, int minor, int micro, int flag) { int own_major, own_minor, own_micro; Xorriso__version(&own_major, &own_minor, &own_micro); return(own_major > major || (own_major == major && (own_minor > minor || (own_minor == minor && own_micro >= micro)))); } char *Xorriso__get_patch_level_text(int flag) { return(Xorriso_program_patch_leveL); } /** The list of startup file names */ #define Xorriso_rc_nuM 4 static char Xorriso_sys_rc_nameS[Xorriso_rc_nuM][80]= { "/etc/default/xorriso", "/etc/opt/xorriso/rc", "/etc/xorriso/xorriso.conf", "placeholder for $HOME/.xorrisorc" }; int Xorriso_new(struct XorrisO ** xorriso,char *progname, int flag) { int i, ret; struct XorrisO *m; char *leafname= NULL; leafname= TSOB_FELD(char, SfileadrL); if(leafname == NULL) return(-1); *xorriso= m= TSOB_FELD(struct XorrisO,1); if(m==NULL) { free(leafname); return(-1); } /* Base initialization by actions which must not fail */ m->libs_are_started= 0; strncpy(m->progname,progname,sizeof(m->progname)-1); m->progname[sizeof(m->progname)-1]= 0; if(getcwd(m->initial_wdx,sizeof(m->initial_wdx)-1)==NULL) m->initial_wdx[0]= 0; m->no_rc= 0; m->argument_emulation= 0; m->genisoimage_completion= 0; m->current_interpreter= 0; m->rc_filename_count= Xorriso_rc_nuM; for(i=0;i<m->rc_filename_count-1;i++) strcpy(m->rc_filenames[i],Xorriso_sys_rc_nameS[i]); m->rc_filenames[m->rc_filename_count-1][0]= 0; m->arrange_args= 0; m->mkisofsrc_done= 0; m->wdi[0]= 0; strcpy(m->wdx, m->initial_wdx); m->did_something_useful= 0; m->add_plainly= 0; m->split_size= 0; strcpy(m->list_delimiter, "--"); m->ino_behavior= 1 | 2 | 4 | 32; /* off:no_lsl_count */ m->iso_level= 3; m->iso_level_is_default= 1; m->do_joliet= 0; m->do_hfsplus= 0; m->do_fat= 0; m->do_rockridge= 1; m->do_iso1999= 0; m->ecma119_map= 1; m->joliet_map= 1; /* off:read:restore:restore_su_auto:restore_only_known:restore_single */ m->lfa_flags_default= 2 | (11 << 11) | (1 << 16); if(geteuid() != 0) { /* do not restore known superuser lfa_flags */ m->lfa_flags_default|= (1 << 13); } m->lfa_flags_setting= m->lfa_flags_default; m->lfa_restore_mask= ~((uint64_t) 0); strcpy(m->lfa_restore_err_sev, "sorry"); m->do_aaip= 0; if(m->lfa_flags_setting & 1) m->do_aaip|= m->lfa_flags_setting & (15 << 11); m->do_md5= 64; m->no_emul_toc= 0; m->do_old_empty= 0; m->scdbackup_tag_name[0]= 0; m->scdbackup_tag_time[0]= 0; m->scdbackup_tag_written[0]= 0; m->scdbackup_tag_listname[0]= 0; m->relax_compliance= 0; m->allow_dir_id_ext_dflt= 1; m->rr_reloc_dir[0]= 0; m->rr_reloc_flags= 1; m->untranslated_name_len= 0; m->do_follow_pattern= 1; m->do_follow_param= 0; m->do_follow_links= 0; m->follow_link_limit= 100; m->resolve_link_rec_count= 0; m->resolve_link_rec_limit= 100; m->do_follow_concat= 0; m->do_follow_mount= 1; m->do_global_uid= 0; m->global_uid= 0; m->isofs_size= 0; m->isofs_has_what= 0; m->tree_loaded= 0; m->rr_loaded= 0; strcpy(m->volid, "ISOIMAGE"); m->volid_default= 1; m->loaded_volid[0]= 0; m->assert_volid[0]= 0; m->assert_volid_sev[0]= 0; m->preparer_id[0]= 0; m->publisher[0]= 0; m->application_id[0]= 0; m->system_id[0]= 0; m->volset_id[0]= 0; m->copyright_file[0]= 0; m->biblio_file[0]= 0; m->abstract_file[0]= 0; strcpy(m->application_use, " "); m->session_logfile[0]= 0; m->session_lba= -1; m->session_blocks= 0; m->do_global_gid= 0; m->global_gid= 0; m->do_global_mode= 0; m->global_dir_mode= 0555; m->global_file_mode= 0444; m->do_tao= 0; m->filters= NULL; m->filter_list_closed= 0; m->zlib_level_default= m->zlib_level= 6; m->zisofs_block_size= m->zisofs_block_size_default= (1 << 15); m->zisofs_by_magic= 0; m->zisofs_v2_enabled= 0; m->zisofs_max_total_blocks= m->zisofs_max_total_blocks_default= 0x2000000; m->zisofs_max_file_blocks= m->zisofs_max_file_blocks_default= 0x2000000; m->zisofs_block_size= m->zisofs_block_size_default= (1 << 15); m->zisofs_v2_block_size= m->zisofs_v2_block_size_default= (1 << 17); m->zisofs_block_number_target= -1; m->zisofs_bpt_discard_free_ratio= m->zisofs_bpt_discard_free_ratio_default= -1.0; m->zisofs_susp_z2= m->zisofs_susp_z2_default= 0; m->do_overwrite= 2; m->do_reassure= 0; m->drive_blacklist= NULL; m->drive_greylist= NULL; m->drive_whitelist= NULL; m->toc_emulation_flag= 0; m->image_start_mode= 0; m->image_start_value[0]= 0; m->displacement= 0; m->displacement_sign= 0; m->read_fs= 0; m->drives_exclusive= 1; m->drives_access= 1; m->linux_scsi_dev_family= 0; m->early_stdio_test= 0; m->cache_num_tiles= 0; m->cache_tile_blocks= 0; m->cache_default= 1 | 2; m->do_calm_drive= 1; m->indev[0]= 0; m->in_drive_handle= NULL; m->in_volset_handle= NULL; m->in_charset= NULL; m->isofs_st_out= time(0) - 1; m->indev_is_exclusive= 1; m->indev_access= 1; m->indev_off_adr[0]= 0; m->isofs_st_in= 0; m->volset_change_pending= 0; m->commit_attempts= 0; m->print_size_attempts= 0; m->write_session_counter= 0; m->print_size_counter= 0; m->no_volset_present= 0; m->in_sector_map= NULL; m->check_media_default= NULL; m->check_media_bad_limit= Xorriso_read_quality_invaliD; m->outdev[0]= 0; m->out_drive_handle= NULL; m->out_charset= NULL; m->dev_fd_1= -1; m->outdev_is_exclusive= 1; m->outdev_access= 1; m->outdev_off_adr[0]= 0; m->grow_blindly_msc2= -1; m->ban_stdio_write= 0; m->do_dummy= 0; m->do_close= 0; m->auto_close= 0; m->write_speed= 0; /* max */ m->read_speed= -2; /* do not set */ m->read_speed_force= 0; m->read_speed_corr= 250000; /* look back at most 0.25 seconds with _force */ m->fs= 4*512; /* 4 MiB */ m->padding= 300*1024; m->do_padding_by_libisofs= 0; m->alignment= 0; m->do_stream_recording= 0; m->dvd_obs= 0; m->do_obs_pad= 0; m->bdr_obs_exempt= 0; m->modesty_on_drive= 0; m->min_buffer_usec= 5000; m->max_buffer_usec= 25000; m->buffer_timeout_sec= 120; m->min_buffer_percent= 90; m->max_buffer_percent= 95; m->use_immed_bit= 0; m->use_immed_bit_default= 0; m->stdio_sync= 0; m->stdio_sync_is_default= 1; m->keep_boot_image= 0; m->boot_image_cat_path[0]= 0; m->boot_image_cat_hidden= 0; m->boot_count= 0; m->boot_platform_id= 0x00; /* El Torito Boot Catalog Platform ID: 0 = 80x86 */ m->patch_isolinux_image= 0; m->boot_image_bin_path[0]= 0; m->boot_image_bin_form[0]= 0; m->boot_image_emul= 0; m->boot_emul_default= 1; m->boot_image_load_size= 4 * 512; /* hearsay out of libisofs/demo/iso.c */ m->boot_img_size_default= 1; m->boot_img_full_size= 0; memset(m->boot_id_string, 0, sizeof(m->boot_id_string)); memset(m->boot_selection_crit, 0, sizeof(m->boot_selection_crit)); #ifdef Xorriso_with_isohybriD m->boot_image_isohybrid= 1; #else m->boot_image_isohybrid= 0; #endif m->boot_efi_default= 0; m->system_area_disk_path[0]= 0; m->system_area_clear_loaded= 0; m->system_area_options= 0; m->patch_system_area= 0; m->partition_offset= 0; m->partition_secs_per_head= 0; m->partition_heads_per_cyl= 0; m->prep_partition[0]= 0; m->efi_boot_partition[0]= 0; for(i= 0; i < Xorriso_max_appended_partitionS; i++) { m->appended_partitions[i]= NULL; m->appended_part_types[i]= 0; memset(m->appended_part_type_guids[i], 0, 16); m->appended_part_gpt_flags[i]= 0; } m->appended_as_gpt= 0; m->appended_as_apm= 0; m->part_like_isohybrid= 0; m->iso_mbr_part_type= -1; memset(m->iso_gpt_type_guid, 0, 16); m->iso_mbr_part_flag= 0; memset(m->gpt_guid, 0, 16); m->gpt_guid_mode= 0; m->max_ce_entries= 31; /* Linux hates >= RR_MAX_CE_ENTRIES = 32 */ m->max_ce_entries_flag= 2; /* omit non-isofs fattr and ACL if needed */ m->ascii_disc_label[0]= 0; m->grub2_sparc_core[0]= 0; memset(m->hfsp_serial_number, 0, 8); m->hfsp_block_size= 0; m->apm_block_size= 0; m->vol_creation_time= 0; m->vol_modification_time= 0; m->vol_expiration_time= 0; m->vol_effective_time= 0; m->vol_uuid[0]= 0; m->all_file_dates[0]= 0; m->do_override_now_time= 0; m->now_time_override= 0; #ifdef Xorriso_with_libjtE m->libjte_handle= NULL; #endif m->jigdo_params= NULL; m->jigdo_values= NULL; m->libjte_params_given= 0; m->loaded_boot_bin_lba= 0; m->loaded_boot_cat_path[0]= 0; m->allow_graft_points= 0; m->allow_restore= 0; m->do_concat_split= 1; m->do_auto_chmod= 0; m->do_restore_sort_lba= 0; m->do_strict_acl= 0; m->dialog= 0; m->buffered_dialog= NULL; m->bsl_interpretation= 0; m->sh_style_result= 0; m->search_mode= 0; m->structured_search= 1; m->do_iso_rr_pattern= 1; m->do_disk_pattern= 2; m->temp_mem_limit= 16*1024*1024; m->file_size_limit= Xorriso_default_file_size_limiT; m->file_name_limit= 255; m->disk_exclusions= NULL; m->iso_rr_hidings= NULL; m->joliet_hidings= NULL; m->hfsplus_hidings= NULL; m->disk_excl_mode= 1; m->use_stdin= 0; m->tolerate_stdin_eof= 0; m->result_page_length= 0; m->result_page_width= 80; m->mark_text[0]= 0; m->packet_output= 0; for(i=0; i<4; i++) { m->logfile[i][0]= 0; m->logfile_fp[i]= NULL; } m->pktlog_fp= NULL; m->stderr_fp= NULL; for(i= 0; i < Xorriso_max_outlist_stacK; i++) { m->result_msglists[i]= NULL; m->info_msglists[i]= NULL; m->msglist_flags[i]= 0; } m->lib_msg_queue_lock_ini= 0; m->result_msglists_lock_ini= 0; m->write_to_channel_lock_ini= 0; m->msg_watcher_lock_ini= 0; m->msg_watcher_state= 0; m->msgw_result_handler= NULL; m->msgw_result_handle= NULL; m->msgw_info_handler= NULL; m->msgw_info_handle= NULL; m->msgw_stack_handle= -1; m->msgw_msg_pending= 0; m->msgw_fetch_lock_ini= 0; m->msg_sieve= NULL; m->msg_sieve_disabled= 0; m->msglist_stackfill= 0; m->status_history_max= Xorriso_status_history_maX; m->scsi_log= 0; strcpy(m->report_about_text, "UPDATE"); Xorriso__text_to_sev(m->report_about_text, &m->report_about_severity, 0); m->library_msg_direct_print= 0; strcpy(m->abort_on_text,"FAILURE"); Xorriso__text_to_sev(m->abort_on_text, &m->abort_on_severity, 0); m->abort_on_is_default= 1; m->problem_status= 0; m->problem_status_lock_ini= 0; m->problem_status_text[0]= 0; m->errfile_log[0]= 0; m->errfile_mode= 0; m->errfile_fp= NULL; m->img_read_error_mode= 1; /* abort faulty image reading with FAILURE */ m->extract_error_mode= 1; /* keep extracted files after read error */ strcpy(m->return_with_text, "SORRY"); Xorriso__text_to_sev(m->return_with_text, &m->return_with_severity, 0); m->return_with_value= 32; m->eternal_problem_status= 0; m->eternal_problem_status_text[0]= 0; m->re= NULL; /* >>> ??? how to initialize m->match[0] ? */ m->re_constants= NULL; m->re_count= 0; m->re_fill= 0; m->reg_expr[0]= 0; m->run_state= 0; m->is_dialog= 0; m->bar_is_fresh= 0; m->pending_option[0]= 0; m->request_to_abort= 0; m->request_not_to_ask= 0; m->idle_time= 0.0; m->re_failed_at= -1; m->prepended_wd= 0; m->insert_count= 0; m->insert_bytes= 0; m->error_count= 0; m->launch_frontend_banned= 0; m->pacifier_style= 0; m->pacifier_interval= 1.0; m->pacifier_count= 0; m->pacifier_prev_count= 0; m->pacifier_total= 0; m->pacifier_byte_count= 0; m->pacifier_fifo= NULL; m->start_time= 0.0; m->last_update_time= 0.0; m->find_compare_result= 1; m->find_check_md5_result= 0; m->last_abort_file_time= 0.0; m->node_counter= 0; m->node_array_size= 0; m->node_array= NULL; m->node_disk_prefixes= NULL; m->node_img_prefixes= NULL; m->hln_count= 0; m->hln_array= NULL; m->hln_targets= NULL; m->hln_change_pending= 0; m->di_do_widen= NULL; m->di_disk_paths= NULL; m->di_iso_paths= NULL; m->node_targets_availmem= 0; m->di_count= 0; m->di_array= NULL; m->perm_stack= NULL; m->update_flags= 0; m->show_hfs_cmd_flag= 0; m->show_hfs_cmd_count= 0; m->show_hfs_cmds= NULL; m->sparse_min_gap= 0; m->result_line[0]= 0; m->result_line_counter= 0; m->result_page_counter= 0; m->result_open_line_len= 0; m->info_text[0]= 0; m->toc_info_type= 1; m->toc_time_form= 1; /* Here begin actions which might fail */ ret= Sfile_leafname(progname, leafname, 0); if(ret<=0) goto failure; if(strcmp(leafname, "osirrox")==0) { m->allow_restore= 1; m->drives_exclusive= 0; m->drives_access= 0; } else if(strcmp(leafname, "xorrisofs")==0 || strcmp(leafname, "genisofs")==0 || strcmp(leafname, "mkisofs")==0 || strcmp(leafname, "genisoimage")==0) { m->argument_emulation= 1; m->pacifier_style= 1; Xorriso_protect_stdout(*xorriso, 0); } else if(strcmp(leafname, "xorrecord")==0 || strcmp(leafname, "wodim")==0 || strcmp(leafname, "cdrecord")==0 || strcmp(leafname, "cdrskin")==0) { m->argument_emulation= 2; m->pacifier_style= 2; } ret= Exclusions_new(&(m->disk_exclusions), 0); if(ret<=0) goto failure; ret= Exclusions_new(&(m->iso_rr_hidings), 0); if(ret<=0) goto failure; ret= Exclusions_new(&(m->joliet_hidings), 0); if(ret<=0) goto failure; ret= Exclusions_new(&(m->hfsplus_hidings), 0); if(ret<=0) goto failure; Xorriso_relax_compliance(m, "default", 0); ret= Xorriso_lst_new(&(m->drive_greylist), "/dev", m->drive_greylist, 1); if(ret <= 0) goto failure; Xorriso_preparer_string(m, m->preparer_id, 1); /* avoids library calls */ ret= pthread_mutex_init(&(m->lib_msg_queue_lock), NULL); if(ret != 0) goto failure; m->lib_msg_queue_lock_ini= 1; ret= pthread_mutex_init(&(m->result_msglists_lock), NULL); if(ret != 0) goto failure; m->result_msglists_lock_ini= 1; ret= pthread_mutex_init(&(m->write_to_channel_lock), NULL); if(ret != 0) goto failure; m->result_msglists_lock_ini= 1; ret= pthread_mutex_init(&(m->problem_status_lock), NULL); if(ret != 0) goto failure; m->problem_status_lock_ini= 1; ret= pthread_mutex_init(&(m->msg_watcher_lock), NULL); if(ret != 0) goto failure; m->msg_watcher_lock_ini= 1; ret= pthread_mutex_init(&(m->msgw_fetch_lock), NULL); if(ret != 0) goto failure; m->msgw_fetch_lock_ini= 1; if(leafname != NULL) free(leafname); return(1); failure:; Xorriso_destroy(xorriso, 0); if(leafname != NULL) free(leafname); return(-1); } int Xorriso_destroy_re(struct XorrisO *m, int flag) { int i; if(m->re!=NULL) { for(i=0;i<m->re_fill;i++) { if(m->re_constants!=NULL) if(m->re_constants[i]!=NULL) continue; /* ,->re[i] was never subject to regcomp() */ regfree(&(m->re[i])); } free((char *) m->re); m->re= NULL; } if(m->re_constants!=NULL) { for(i=0;i<m->re_fill;i++) if(m->re_constants[i]!=NULL) free(m->re_constants[i]); free((char *) m->re_constants); m->re_constants= NULL; } m->re_count= 0; m->re_fill= 0; return(1); } /* @param flag bit0= global shutdown of libraries */ int Xorriso_destroy(struct XorrisO **xorriso, int flag) { struct XorrisO *m; int i; m= *xorriso; if(m==NULL) return(0); /* Give up drives and image to unref all connected xorriso objects */ Xorriso_give_up_drive(m, 3); if(m->in_charset!=NULL) free(m->in_charset); if(m->out_charset!=NULL) free(m->out_charset); Checkmediajob_destroy(&(m->check_media_default), 0); Sectorbitmap_destroy(&(m->in_sector_map), 0); Xorriso_destroy_re(m,0); Exclusions_destroy(&(m->disk_exclusions), 0); Exclusions_destroy(&(m->iso_rr_hidings), 0); Exclusions_destroy(&(m->joliet_hidings), 0); Exclusions_destroy(&(m->hfsplus_hidings), 0); Xorriso_destroy_all_extf(m, 0); Xorriso_lst_destroy_all(&(m->drive_blacklist), 0); Xorriso_lst_destroy_all(&(m->drive_greylist), 0); Xorriso_lst_destroy_all(&(m->drive_whitelist), 0); Xorriso_destroy_node_array(m, 0); Xorriso_destroy_hln_array(m, 0); Xorriso_destroy_di_array(m, 0); #ifdef Xorriso_with_libjtE if(m->libjte_handle) libjte_destroy(&(m->libjte_handle)); #endif Xorriso_lst_destroy_all(&(m->jigdo_params), 0); Xorriso_lst_destroy_all(&(m->jigdo_values), 0); for(i= 0; i < Xorriso_max_appended_partitionS; i++) if(m->appended_partitions[i] != NULL) free(m->appended_partitions[i]); Xorriso_detach_libraries(m, flag&1); if(m->lib_msg_queue_lock_ini) pthread_mutex_destroy(&(m->lib_msg_queue_lock)); if(m->result_msglists_lock_ini) pthread_mutex_destroy(&(m->result_msglists_lock)); if(m->write_to_channel_lock_ini) pthread_mutex_destroy(&(m->write_to_channel_lock)); if(m->problem_status_lock_ini) pthread_mutex_destroy(&(m->problem_status_lock)); if(m->msg_watcher_lock_ini) pthread_mutex_destroy(&(m->msg_watcher_lock)); if(m->msgw_fetch_lock_ini) pthread_mutex_destroy(&(m->msgw_fetch_lock)); Xorriso_sieve_dispose(m, 0); free((char *) m); *xorriso= NULL; return(1); } int Xorriso_destroy_node_array(struct XorrisO *xorriso, int flag) { int i; if(xorriso->node_array != NULL) { for(i= 0; i < xorriso->node_counter; i++) iso_node_unref((IsoNode *) xorriso->node_array[i]); free(xorriso->node_array); } xorriso->node_array= NULL; xorriso->node_counter= xorriso->node_array_size= 0; Xorriso_lst_destroy_all(&(xorriso->node_disk_prefixes), 0); Xorriso_lst_destroy_all(&(xorriso->node_img_prefixes), 0); return(1); } /* @param flag bit0= do not destroy hln_array but only hln_targets */ int Xorriso_destroy_hln_array(struct XorrisO *xorriso, int flag) { int i; if(xorriso->hln_array != NULL && !(flag & 1)) { for(i= 0; i < xorriso->hln_count; i++) iso_node_unref((IsoNode *) xorriso->hln_array[i]); free(xorriso->hln_array); xorriso->hln_array= NULL; xorriso->hln_count= 0; } if(xorriso->hln_targets != NULL) { for(i= 0; i < xorriso->hln_count; i++) if(xorriso->hln_targets[i] != NULL) free(xorriso->hln_targets[i]); free(xorriso->hln_targets); xorriso->hln_targets= NULL; } xorriso->node_targets_availmem= 0; return(1); } int Xorriso_destroy_di_array(struct XorrisO *xorriso, int flag) { int i; if(xorriso->di_array != NULL) { for(i= 0; i < xorriso->di_count; i++) if(xorriso->di_array[i] != NULL) iso_node_unref((IsoNode *) xorriso->di_array[i]); free(xorriso->di_array); xorriso->di_array= NULL; } if(xorriso->di_do_widen != NULL) { free(xorriso->di_do_widen); xorriso->di_do_widen= NULL; } Xorriso_lst_destroy_all(&(xorriso->di_disk_paths), 0); Xorriso_lst_destroy_all(&(xorriso->di_iso_paths), 0); xorriso->di_count= 0; #ifdef NIX /* <<< */ fprintf(stderr, "xorriso_DEBUG: get_di_count= %lu\n", Xorriso_get_di_counteR); #endif /* NIX */ return(1); } int Xorriso_new_node_array(struct XorrisO *xorriso, off_t mem_limit, int addon_nodes, int flag) { int i; if(xorriso->node_counter <= 0) return(1); xorriso->node_array= calloc(xorriso->node_counter + addon_nodes, sizeof(IsoNode *)); if(xorriso->node_array == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); return(-1); } for(i= 0; i < xorriso->node_counter + addon_nodes; i++) xorriso->node_array[i]= NULL; xorriso->node_array_size= xorriso->node_counter + addon_nodes; xorriso->node_counter= 0; return(1); } /* @param flag bit0= do not allocate hln_array but only hln_targets */ int Xorriso_new_hln_array(struct XorrisO *xorriso, off_t mem_limit, int flag) { int i; Xorriso_destroy_hln_array(xorriso, flag & 1); if(xorriso->hln_count <= 0) return(1); if(!(flag & 1)) { xorriso->hln_array= calloc(xorriso->hln_count, sizeof(char *)); if(xorriso->hln_array == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); return(-1); } for(i= 0; i < xorriso->hln_count; i++) xorriso->hln_array[i]= NULL; } xorriso->hln_targets= calloc(xorriso->hln_count, sizeof(char *)); if(xorriso->hln_targets == NULL) { if(!(flag & 1)) { free(xorriso->hln_array); xorriso->hln_array= NULL; } Xorriso_no_malloc_memory(xorriso, NULL, 0); return(-1); } for(i= 0; i < xorriso->hln_count; i++) xorriso->hln_targets[i]= NULL; xorriso->node_targets_availmem= mem_limit - xorriso->hln_count * sizeof(void *) - xorriso->hln_count * sizeof(char *); if(xorriso->node_targets_availmem < 0) xorriso->node_targets_availmem= 0; return(1); } int Xorriso__preset_signal_behavior(int behavior, int flag) { if(behavior < 0 || behavior > 3) return(0); Xorriso_signal_behavioR= behavior; return(1); } int Xorriso__get_signal_behavior(int flag) { return(Xorriso_signal_behavioR); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2010 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of functions which perform the fundamental operations of the XorrisO object. */ #ifndef Xorriso_pvt_base_obj_includeD #define Xorriso_pvt_base_obj_includeD yes #ifdef NIX /* <<< */ unsigned long Xorriso_get_di_counteR= 0; #endif /* NIX */ struct XorrisO; int Xorriso_destroy_re(struct XorrisO *m, int flag); int Xorriso__get_signal_behavior(int flag); #endif /* ! Xorriso_pvt_base_obj_includeD */ ------------------------------------------------------------------------------ libburnia-project.org libisoburn , xorriso ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ Changelog ------------------------------------------------------------------------------ 1 Sep 2007 [983] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c libisoburn/isofs_wrap.c Initial content of libisoburn 1 Sep 2007 [985] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c Should have used copy+paste when writing Vreixos name 5 Sep 2007 [990] libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c Implemented use of stdio-pseudo-drives 5 Sep 2007 [993] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c Changes in plans as discussed up to Sep 5 2007 5 Sep 2007 [994] libisoburn/libisoburn.h Updated explanations about the usage principles of libisoburn 6 Sep 2007 [998] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c Updated to state of discussion 9 Sep 2007 [1025] libisoburn/burn_wrap.c New wrapper isoburn_disc_erasable() declares ISO DVD-RAM, DVD+RW erasable 10 Sep 2007 [1027] libisoburn/libisoburn.h New wrapper isoburn_disc_erasable() declares ISO DVD-RAM, DVD+RW erasable 11 Sep 2007 [1029] libisoburn/burn_wrap.c libisoburn/isoburn.h New inner function isoburn_set_start_byte() 12 Sep 2007 [1031] libisoburn/libisoburn.h libisoburn/burn_wrap.c Removed isoburn_write_opts_set_start_byte() 13 Sep 2007 [1043] libisoburn/burn_wrap.c Took into respect fabricated_disc_status 21 Sep 2007 [1092] Makefile.am Removed libburn file addresses 22 Sep 2007 [1093] + doc/doxygen.conf.in Added file demanded by build system 22 Sep 2007 [1094] src/burn_wrap.c Prevented SIGSEGV in isoburn_drive_scan_and_grab() 22 Sep 2007 [1095] src/burn_wrap.c Enabled treatment==2 in isoburn_drive_scan_and_grab() 22 Sep 2007 [1099] src/burn_wrap.c Made use of burn_msgs_submit() for error messages 23 Sep 2007 [1102] src/burn_wrap.c Removed all references to isoburn.treatment from burn_wrap.c 23 Sep 2007 [1105] src/burn_wrap.c Called isoburn_create_data_source() and isoburn_free_data_source() 28 Sep 2007 [1129] libisofs libisofs/libiso_msgs.h Removed apostrophes which my compiler does not like 29 Sep 2007 [1133] src/burn_wrap.c Added optional code for a pseudo CD-RW to test the code for MMC multi-session 29 Sep 2007 [1134] src/burn_wrap.c Released drive in case welcome_media fails 1 Oct 2007 [1141] src/burn_wrap.c Allowed isoburn_disc_get_msc1() for closed media 2 Oct 2007 [1149] test/test.c Function graft_point(),some general polishing, call of iso_volset_free disabled 8 Oct 2007 [1169] src/libisoburn.h src/isoburn.h src/isoburn.c src/burn_wrap.c Introduced fifo reference into isoburn object 8 Oct 2007 [1170] test/test.c Several directories and files in one session, added display of fifo 12 Oct 2007 [1171] + test/xorriso.h + test/xorriso.c + test/xorriso.txt The stub of new application xorriso 12 Oct 2007 [1172] + test/compile_xorriso.sh + test/make_timestamp.sh + test/xorriso_timestamp.h A build facility to circumvent autotools during development 12 Oct 2007 [1173] test/xorriso.c Introduced version and build timestamps, library headers 13 Oct 2007 [1174] test/xorriso.c + test/changelog.txt Made -dialog and -options_from_file work 2007.10.13.141503 [1175] test/xorriso.c Implemented -speed and enlarged -status list 2007.10.13.152252 [1176] test/xorriso.c test/xorriso.txt Implemented xorriso setter level of -fs, -gid, -uid 2007.10.14.110003 [1177] test/xorriso.c Implemented setter level of -abort_on, fixed bugs about -f,-no_rc,startup files 2007.10.14.122456 [1178] test/xorriso.c + test/xorriso_private.h + test/xorrisoburn.h + test/xorrisoburn.c Began to implement interface to our libraries 2007.10.15.152705 [1183] test/xorriso.h test/xorriso.c test/xorriso_private.h test/xorrisoburn.h test/xorrisoburn.c Implemented -dev, -add, -commit 2007.10.15.160303 [1184] test/xorriso.c Made -end give up drives 2007.10.15.203554 [1185] test/xorriso.c Some safety precautions against malicious input, enabled -cdx, -cdi for -add 2007.10.15.203714 [1186] test/xorrisoburn.c Corrected image path bug with -add of regular files, and -add /=/some/dir 2007.10.15.224005 [1187] test/xorriso.c test/xorrisoburn.c Implemented -rollback 2007.10.16.210911 [1188] test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c Worked on failure severities, message system, program abort decision 2007.10.17.130041 [1190] [1191 test/xorriso.h test/xorriso_private.h test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c Worked on failure severities, message system, program abort decision 2007.10.17.130311 [1192] src/isofs_wrap.c Told burn_read_data() to stay silent on non-existent drive or read error 2007.10.17.150142 [1193] test/xorriso.c test/xorrisoburn.c Reinstated the distinction of message sources, respected '#' in dialog 2007.10.17.165352 [1194] test/xorriso.c Prepended a "-" to any input line if missing 2007.10.17.183024 [1195] test/xorriso_private.h test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c Implemented core of option -toc 2007.10.17.200241 [1196] test/xorrisoburn.c Continued work with -toc 2007.10.17.213852 [1197] test/compile_xorriso.sh Forgot to make off_t 64 bit 2007.10.17.214228 [1198] src/libisoburn.h src/burn_wrap.c test/xorrisoburn.c Rounding up fabricated nwa to full 32k addresses, API call for exact image size 2007.10.17.215809 [1199] test/xorriso.c Activated -ban_stdio_write 2007.10.17.224924 [1200] test/xorrisoburn.c Fixed obvious bug with -J. (Still wondering wether it works) 2007.10.17.225039 [1201] test/xorriso.c Fixed bug with -speed. 2007.10.17.225837 [1202] test/xorriso.c Fixed bug with -prompt. Fixed bug with # comments. (of rev 1194) 18 Oct 2007 [1203] test/changelog.txt Updated changelog and todo list 2007.10.18.144841 [1205] src/isofs_wrap.c test/xorrisoburn.c isoburn_read_volset() now hands out an official volset reference 2007.10.18.171415 [1206] test/xorriso.c test/xorriso.txt test/xorrisoburn.h test/xorrisoburn.c Implemented option -devices 2007.10.18.183200 [1207] test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c Implemented option -tell_media_space 2007.10.18.185731 [1208] test/xorriso_private.h test/xorriso.c test/xorrisoburn.c Fixed a SIGSEGV with xorriso -version run 2007.10.18.221756 [1211] test/xorrisoburn.c src/libisoburn.h Took care of disposal of burn_disc ovbject 18 Oct 2007 [1212] test/test.c Silenced compile warning 2007.10.18.225654 [1213] src/isofs_wrap.c test/xorriso.c test/xorrisoburn.c Fixed a SIGSEGV with empty drive 2007.10.19.140031 [1218] test/xorrisoburn.c Made reports with -add normal infos (formerly NOTE events) 2007.10.19.151339 [1219] test/xorriso.c test/xorrisoburn.c test/xorriso.txt Implemented -print-size 2007.10.19.164957 [1220] test/xorriso_private.h test/xorriso.c test/xorriso.h test/xorrisoburn.c test/xorriso.txt Implemented verbosity control by option -report_about 2007.10.19.173547 [1221] test/xorriso.c test/xorrisoburn.c test/xorriso.txt Implemented option -eject 2007.10.19.204155 [1222] test/xorriso_private.h test/xorriso.h test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c Implemented option -blank 2007.10.20.170731 [1223] src/burn_wrap.c Directed write mode failure message to libburn queue, repaired wrote_well 2007.10.20.171046 [1224] test/xorrisoburn.c test/xorriso_private.h test/xorriso.c Implemented options -format and -blank deformat, -close and closed media 20 Oct 2007 [1225] test/xorriso.txt + test/xorriso.1 test/changelog.txt Splitted think text from emerging man page, formatted man page 2007.10.20.194918 [1226] test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c Completed -tell_media_space, checked space before burn, failed -end exits >0 20 Oct 2007 [1227] test/xorriso.1 Polished man page 2007.10.21.094818 [1228] test/xorriso_private.h test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c test/xorriso.1 Implemented -rm and -rm_r 2007.10.21.105228 [1229] src/data_source.c Silenced compiler warning about C++ style comment 2007.10.21.124315 [1230] test/xorriso.c test/xorrisoburn.c test/xorrisoburn.h Began to implement -ls and -ls_l, enhanced -cdi, not done yet 2007.10.21.151124 [1231] test/xorriso_private.h test/xorriso.c test/xorrisoburn.c test/xorrisoburn.h Hopefully completed -cd alias -cdi 2007.10.21.185248 [1232] test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c test/xorriso.1 Hopefully completed -cdx 2007.10.21.213303 [1233] test/xorriso_private.h test/xorriso.c test/xorrisoburn.c test/xorriso.1 Implemented sorting of -ls by node name, implemented leaf name search patterns 2007.10.22.211928 [1237] test/xorrisoburn.c test/xorriso.1 Implemented file size and mtime for -ls_l 2007.10.23.122349 [1238] src/libisoburn.h src/isoburn.c Added fifo_size to struct isoburn_source_opts 2007.10.23.122753 [1239] test/xorrisoburn.c test/xorriso.c test/xorriso.1 Made use of isoburn_source_opts.fifo_size 2007.10.24.100156 [1244] test/xorriso.c test/xorrisoburn.c Normalized paths to target and source before adding or removing from image 2007.10.24.105424 [1245] test/xorriso.c test/xorriso.h Implemented option -path-list 2007.10.24.175337 [1247] test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c Made -cd useable with no image loaded 2007.10.27.224148 [1257] test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c test/xorriso.1 Implemented option -mv 2007.10.27.230512 [1258] test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c Bug fixes with option -mv 2007.10.28.125501 [1260] test/xorriso.c test/xorrisoburn.c Prevented some interesting pitfalls with -mv 2007.10.28.165516 [1261] test/xorriso.c test/xorriso.1 test/xorrisoburn.h test/xorrisoburn.c Implemented option -mkdir 2007.10.28.174550 [1262] test/xorriso.c Warning of wildcards in paths 28 Oct 2007 [1263] test/xorriso.1 Updated man page about -path-list 2007.10.29.213920 [1273] test/xorriso_private.h test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c test/xorriso.1 Structured patterns for options -ls and -ls_l 2007.10.30.214242 [1274] test/xorriso_private.h test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c test/xorriso.1 test/compile_xorriso.sh Multiple structured patterns, changed option -ls from single to multi args 2007.10.31.103338 [1275] test/xorriso_private.h test/xorriso.h test/xorriso.c test/xorrisoburn.c test/xorriso.1 Implemented new option -temp_mem_limit 2007.10.31.165413 [1276] test/xorriso.c test/xorrisoburn.c test/xorrisoburn.h Gave simple -ls an implemention with minimal memory consumption 31 Oct 2007 [1277] test/xorriso.1 Overhauled info paragraphs of man page 2007.10.31.175916 [1278] test/xorriso_private.h Overhauled comments in struct XorrisO 31 Oct 2007 [1279] test/changelog.txt Updating changelog 2007.11.01.111351 [1280] test/xorrisoburn.c Got rid of bad pacifier text at end of CD writing 2007.11.01.191106 [1281] test/xorriso.h test/xorriso.c test/xorrisoburn.c test/xorrisoburn.h test/xorriso.1 Implemented option -du 2007.11.02.143549 [1282] test/xorrisoburn.h test/xorrisoburn.c Clearer status messages after reading new volume and after burning 2007.11.02.143658 [1283] test/xorriso.c Made -abort_on and -report_about complain with bad severity names 2007.11.02.184705 [1284] test/xorrisoburn.c test/xorriso.1 Polished write success message and man page 2007.11.06.163305 [1285] test/xorriso_private.h test/xorriso.h test/xorriso.c test/xorrisoburn.c test/xorriso.1 Implemented -overwrite control 2007.11.06.163929 [1286] src/libisoburn.h Corrected a typo in a comment 2007.11.06.164029 [1287] src/isoburn.c Closed memory leak by freeing session and track in isoburn_prepare_disc_aux() 2007.11.07.123744 [1288] test/xorriso_private.h test/xorriso.h test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c test/xorriso.1 Implemented option -reassure 2007.11.07.150157 [1289] test/xorriso.h test/xorriso.c test/xorrisoburn.c test/xorriso.1 Implemented option -rmdir 2007.11.07.191915 [1290] test/xorriso.h test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c Implemented option -chmod (does not get written into image, though) 2007.11.07.225624 [1291] test/xorriso.c test/xorrisoburn.c test/xorrisoburn.h Implemented options -chown and -chgrp (not written into image, though) 2007.11.08.144451 [1292] test/xorriso.c test/xorrisoburn.c test/xorrisoburn.h test/xorriso.1 Implemented option -alter_date, corrected write problem with mode and ownership 2007.11.08.145016 [1293] test/xorriso_private.h Forgotten source file for rev 1292 8 Nov 2007 [1294] test/xorriso.1 Enhanced man page text for options -chmod, -chown, -chgrp 2007.11.08.160302 [1295] test/xorriso.c Fixed bug with -chmod go=r 2007.11.08.161215 [1296] test/xorriso.c test/xorrisoburn.c Enlarged string size limit of Text_shellsafe() 2007.11.09.193142 [1297] test/xorriso_private.h test/xorriso.h test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c test/xorriso.1 New option -iso_rr_pattern, influencing several options with multiple args 2007.11.11.112321 [1298] test/xorriso.h test/xorriso.c test/xorrisoburn.c Gave problem handling finer granularity within loops 11 Nov 2007 [1299] test/xorriso.1 Clarified man page 2007.11.11.154453 [1300] test/xorriso.c Added missing commit to normal end of program 11 Nov 2007 [1301] test/xorriso.1 Added some examples to man page 2007.11.14.142904 [1302] src/libisoburn.h src/isofs_wrap.c src/isoburn.c test/test.c New API call isoburn_attach_volset(), changes with isoburn_read_volset() 2007.11.14.143119 [1303] test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c test/xorriso.1 Implemented options -outdev and -indev 2007.11.14.175446 [1304] test/xorriso.c Corrected premature abort bug with misspelled command words 14 Nov 2007 [1305] test/xorriso.1 Polished xorriso man page 14 Nov 2007 [1306] test/changelog.txt Updated xorriso changelog 2007.11.26.192113 [1311] test/xorriso.c test/xorrisoburn.c Reacted on warnings on a 64 bit system 2007.12.04.074340 [1314] test/xorriso.h test/xorriso_private.h test/xorriso.c test/xorrisoburn.c test/xorriso.1 Implemented -disk_pattern, -lsx, ls_lx, -dux, -du_lx 2007.12.04.205919 [1315] test/xorrisoburn.c test/xorriso_private.h test/xorriso.c Removed some redundancy of disk_pattern and iso_rr_pattern matching 4 Dec 2007 [1316] test/xorriso.1 Polished man page 2007.12.05.090438 [1317] test/xorriso.h test/xorriso.c test/xorriso.1 Option -f-off as counterpart of option -f 2007.12.05.143632 [1318] test/xorriso.c Made dashes at options optional 5 Dec 2007 [1319] test/xorriso.1 Overhauled description of media types, states and expansion methods 2007.12.06.150102 [1320] test/xorriso.h test/xorriso.c test/xorriso.1 Changed -f, -f-off to -follow, -graft-points, -graf-points-off to -pathspecs 6 Dec 2007 [1321] test/xorriso.1 Removed references to option -graft-points 2007.12.06.192437 [1322] test/xorriso.c Corrected warning function about wildcards 8 Dec 2007 [1326] test/xorriso.1 Explained result pager 2007.12.08.175117 [1327] test/xorriso.c test/xorriso_private.h test/xorrisoburn.c Removed more code redundancies and fd leak with Xorriso_obtain_pattern_files_x() 2007.12.15.162039 [1328] test/compile_xorriso.sh Added -O0 to -g to get better gdb behavior on my new system 2007.12.15.183022 [1329] test/xorriso_private.h test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c test/xorriso.1 Implemented option -follow 2007.12.16.103456 [1330] test/xorriso.c test/xorriso.1 Added link hop limit to option -follow 2007.12.16.122626 [1331] test/xorrisoburn.c Added copying of attributes from symbolic link into image 2007.12.16.144615 [1332] test/xorrisoburn.c Removed waste of time in Xorriso_show_du_subs() 2007.12.18.175924 [1333] test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c test/xorriso.1 Changed options -ls* to -lsd*, introduced new family of -ls* without d 2007.12.20.111338 [1334] test/xorriso.h test/xorriso.c test/xorrisoburn.c test/xorriso.1 Changed -du_s* into -dus* 2007.12.21.131538 [1335] test/xorriso.h test/xorriso_private.h test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c test/xorriso.1 Implemented option -find alias -findi 2007.12.21.132017 [1336] test/xorriso.c test/xorriso.1 Corrected documentation about -find 2007.12.22.143803 [1337] test/xorriso.h test/xorriso.c test/xorrisoburn.c test/xorriso.1 Implemented option -findx 2007.12.24.161107 [1339] test/xorriso.c Repaired options -dus and -dusx 24 Dec 2007 [1340] test/changelog.txt Updated changelog 2007.12.25.160100 [1341] test/xorriso.h test/xorriso.c test/xorriso.1 Implemented option -cpr 2007.12.26.160040 [1342] test/xorriso.h test/xorriso_private.h test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c test/xorriso.1 Changed option -J to -joliet "on"|"off", implemented option -volid 2007.12.28.132741 [1343] test/xorriso_private.h test/xorriso.c test/xorrisoburn.c Made leaner the local memory of recursive functions (because of ulimit -s) 2007.12.30.190138 [1344] test/xorriso.h test/xorriso_private.h test/xorriso.c test/xorrisoburn.c test/xorriso.1 Implemented -find option -exec echo, chown, chgrp, chmod, alter_date, lsdl 2007.12.30.203336 [1345] test/xorriso.c Corrected superuser behavior of Sfile_lookup_permissions() 2007.12.31.095229 [1346] test/xorriso.c test/xorrisoburn.c Repaired some bugs in -find and -findx 2007.12.31.135237 [1347] test/xorriso.h test/xorriso.c test/xorriso.1 Implemented options -chown_r, -chgrp_r, -chmod_r, -alter_date_r 2007.12.31.135330 [1348] test/xorrisoburn.c Repaired a bug in -find 2008.01.01.123118 [1349] test/xorriso.h test/xorriso_private.h test/xorriso.c test/xorrisoburn.h test/xorrisoburn.c test/xorriso.1 Implemented -find actions chown_r, chgrp_r, chmod_r, alter_date_r, find 2008.01.02.175011 [1350] test/xorriso.c test/xorriso.1 Implemented -find test -type 2008.01.09.175418 [1351] test/xorrisoburn.c Corrections made during porting to nglibisofs 2008.01.10.114451 [1352] + test/ng_xorrisoburn.h + test/ng_xorrisoburn.c test/compile_xorriso.sh Begin of porting to next generation libisofs 2008.01.10.151924 [1353] Makefile.am + ng_src + ng_src/libisoburn.h + ng_src/isoburn.h + ng_src/isoburn.c + ng_src/isofs_wrap.c + ng_src/data_source.c + ng_src/burn_wrap.c Begin of porting to next generation libisofs 2008.01.10.152353 [1354] test/ng_xorrisoburn.c test/compile_xorriso.sh Made compile_xorriso.sh -nglibisofs work on ./ng_src 2008.01.10.154948 [1355] test/ng_xorrisoburn.c test/compile_xorriso.sh Made compile_xorriso.sh -oglibisofs work on ./og_src 2008.01.11.133319 [1356] test/compile_xorriso.sh Adapted to existence of nglibisofs eltorito.o 2008.01.11.133631 [1357] test/xorriso.c test/ng_xorrisoburn.c test/xorrisoburn.c Removed old and new bugs 2008.01.11.174733 [1358] test/xorriso.c test/ng_xorrisoburn.c test/xorrisoburn.c Tracing the different behavior of isoburn_read_image() 2008.01.11.175423 [1359] test/ng_xorrisoburn.c ng_src/isoburn.c ng_src/libisoburn.h Changed isoburn_source_opts.ouput_charset to output_charset 2008.01.11.212545 [1361] ng_src/isofs_wrap.c Made initialization of iso_read_opts safer 13 Jan 2008 [1364] + og_src - src src/burn_wrap.c - src/isoburn.c - src/isoburn.h - src/data_source.c - src/isofs_wrap.c - src/libisoburn.h + og_src/burn_wrap.c + og_src/isoburn.c + og_src/isoburn.h + og_src/data_source.c + og_src/isofs_wrap.c + og_src/libisoburn.h Moved src to og_src and installed symbolic link instead 13 Jan 2008 [1365] ng_src/isofs_wrap.c Changes about blanking loaded image 2008.01.13.224929 [1366] test/ng_xorrisoburn.c Allowed all filetypes as input, silenced debug during image read 14 Jan 2008 [1367] test/compile_xorriso.sh Added forgotten source module 14 Jan 2006 [1368] ng_src/libisoburn.h ng_src/isoburn.c New members in isoburn_source_opts 2008.01.14.163814 [1369] test/ng_xorrisoburn.c Closed memory leak, adapted to new members in isoburn_source_opts 2008.01.14.164628 [1370] test/ng_xorrisoburn.c Repaired memory hog and a freshly introduced bug 2008.01.14.190220 [1371] test/xorriso.c test/ng_xorrisoburn.c test/xorriso.1 Removed some bugs and made adaptions to nglibisofs 2008.01.14.224645 [1372] test/xorriso_private.h test/xorriso.c test/ng_xorrisoburn.h test/ng_xorrisoburn.c test/xorriso.1 Made improvements around volume id 2008.01.15.174409 [1373] test/xorriso.h test/xorriso_private.h test/xorriso.c test/ng_xorrisoburn.h test/ng_xorrisoburn.c test/xorrisoburn.h test/xorrisoburn.c Made improvements about pattern matching .., closed small memory leaks 2008.01.15.204057 [1374] test/xorriso.c test/ng_xorrisoburn.h test/ng_xorrisoburn.c test/xorrisoburn.h test/xorrisoburn.c Closed a small memory leak 2008.01.15.211511 [1375] test/ng_xorrisoburn.c Added a forgotten iso_dir_iter_free() 2008.01.16.132909 [1376] ng_src/isofs_wrap.c Repaired bug about MMC multi-session image loading 2008.01.16.141631 [1378] ng_src/isoburn.c Changed default name of initial image to ISOIMAGE 16 Jan 2008 [1378] libisoburn.pc.in Set correct name of library 2008.01.16.200942 [1379] test/xorriso.c test/ng_xorrisoburn.c Adaption to Xorriso_standalonE, some consequences from compiler warnings 16 Jan 2008 [1380] + test/make_xorriso_standalone.sh + test/xorriso_makefile_am.txt + test/configure_ac.txt + test/xorriso_pc_in.txt A sketch of a xorriso source standalone release generator 16 Jan 2008 [1381] test/configure_ac.txt test/xorriso_makefile_am.txt Added a test for readline.h and eventually enabled use of libreadline 2008.01.17.145135 [1382] test/ng_xorrisoburn.c Silenced -pedantic compiler warnings about type punning 17 Jan 2008 [1385] test/make_xorriso_standalone.sh Silenced most compiler warnings of libisofs in xorriso-standalone 17 Jan 2008 [1386] + test/README Began a README file for xorriso 18 Jan 2008 [1387] test/make_xorriso_standalone.sh test/README + test/xorriso_eng.html Worked on documentation 2008.01.18.101933 [1388] test/xorriso.c Changed -report_about default to "UPDATE" 18 Jan 2008 [1389] test/xorriso.1 test/make_xorriso_standalone.sh + test/convert_man_to_html.sh Made a generator for HTML man page 2008.01.19.090417 [1390] ng_src/libisoburn.h ng_src/isoburn.c ng_src/isofs_wrap.c test/ng_xorrisoburn.c test/compile_xorriso.sh test/xorriso_makefile_am.txt Adaptions to revision 241 of nglibisofs 2008.01.19.090546 [1391] test/xorriso.c Added startup message. Removed helptext sentence about unimplemented options 19 Jan 2008 [1392] test/xorriso_eng.html test/make_xorriso_standalone.sh Producing a bootstrapped xorriso standalone tarball 19 Jan 2008 [1393] test/README test/xorriso.1 test/xorriso_eng.html Some polishing of xorriso documentation 2008.01.19.162244 [1394] test/xorriso.c test/ng_xorrisoburn.c Small corrections with version number and write counter report 19 Jan 2008 [1395] test/changelog.txt Updated SVN copy of changelog 2008.01.20.102946 [1397] ng_src/burn_wrap.c ng_src/data_source.c ng_src/isoburn.c ng_src/isofs_wrap.c Made forgotten adaption to Xorriso_standalonE 2008.01.20.131050 [1398] test/xorriso.h test/xorriso_private.h test/xorriso.c test/ng_xorrisoburn.c test/xorriso.1 Made -outdev stdio:/dev/fd/1 safe for single session runs 2008.01.20.200254 [1399] test/xorriso.h test/xorriso_private.h test/xorriso.c test/xorriso.1 New option -add_plainly 2008.01.21.221939 [1400] test/xorriso.c test/xorriso.1 Rejected unsuitable addresses "stdio:" , "stdio:/", "stdio:-", "stdio:." 22 Jan 2008 [1401] + test/compare_file.c Program which compares two files in different trees. Attributes and content. 22 Jan 2008 [1402] test/configure_ac.txt Copied test for tm.tm_gmtim from libisofs configure.ac 22 Jan 2008 [1403] test/compare_file.c Prepended type indication letter to report lines 2008.01.22.224321 [1404] test/ng_xorrisoburn.c Better attributes with directories 2008.01.23.195855 [] ng_src/burn_wrap.c test/ng_xorrisoburn.c Adapted to libisofs revision 261 24 Jan 2008 [1410] test/make_xorriso_standalone.sh Removed remover of C++ comment lines 2008.01.24.202206 [1411] ng_src/isoburn.c ng_src/burn_wrap.c ng_src/isoburn.h Gave up use of libburn fifo. Attached -fs and pacifier to libisofs fifo. 2008.01.25.150902 [1413] test/xorriso.h test/xorriso.c test/xorriso.1 New option -rollback_end 25 Jan 2008 [1414] test/compare_file.c Fixed bug about file content comparison. 25 Jan 2008 [1415] test/compare_file.c Better handling of various content difference situations 2008.01.25.175353 [1416] test/xorriso.h test/xorriso.c test/xorriso.1 test/ng_xorrisoburn.c New option -commit_eject 2008.01.26.002011 [1417] - og_src/libisoburn.h - og_src/isoburn.h - og_src/isoburn.c - og_src/isofs_wrap.c - og_src/burn_wrap.c - og_src/data_source.c - og_src/ + libisoburn/ - ng_src/libisoburn.h - ng_src/isoburn.h - ng_src/isoburn.c - ng_src/isofs_wrap.c - ng_src/burn_wrap.c - ng_src/data_source.c + libisoburn/libisoburn.h + libisoburn/isoburn.h *+ libisoburn/isoburn.c *+ libisoburn/isofs_wrap.c *+ libisoburn/burn_wrap.c *+ libisoburn/data_source.c + xorriso/ - test/README - test/changelog.txt - test/compare_file.c - test/compile_xorriso.sh - test/configure_ac.txt - test/convert_man_to_html.sh - test/make_timestamp.sh - test/make_xorriso_standalone.sh - test/ng_xorrisoburn.c - test/ng_xorrisoburn.h - test/xorriso.1 - test/xorriso.c - test/xorriso.h - test/xorriso_eng.html - test/xorriso_makefile_am.txt - test/xorriso_pc_in.txt - test/xorriso_private.h - test/xorriso_timestamp.h - test/xorrisoburn.c - test/xorrisoburn.h + xorriso/README + xorriso/changelog.txt + xorriso/compare_file.c *+ xorriso/compile_xorriso.sh + xorriso/configure_ac.txt *+ xorriso/convert_man_to_html.sh *+ xorriso/make_timestamp.sh *+ xorriso/make_xorriso_standalone.sh + xorriso/xorriso.1 *+ xorriso/xorriso.c + xorriso/xorriso.h + xorriso/xorriso_eng.html + xorriso/xorriso_makefile_am.txt + xorriso/xorriso_pc_in.txt + xorriso/xorriso_private.h + xorriso/xorriso_timestamp.h *+ xorriso/xorrisoburn.c + xorriso/xorrisoburn.h Makefile.am Gave up adapter to old libisofs. Renaming libisoburn and xorriso dirs. 2008.01.26.113604 [1418] libisoburn/libisoburn.h libisoburn/burn_wrap.c xorriso/xorrisoburn.c INCOMPATIBLE API CHANGE: isoburn_initialize(char msg[1024], int flag) 2008.01.26.120534 [1419] libisoburn/libisoburn.h configure.ac version.h.in libisoburn/isoburn.c libisoburn/burn_wrap.c Introduced versioning (still pre-release) and new API function isoburn_version() 2008.01.26.140005 [1423] xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_eng.html Adapted standalone tarball to version functions 2008.01.26.142130 [1424] xorriso/xorriso.c Set program revision to 0.0.1 (previous 0.1.0 was too early) 2008.01.26.171156 [1425] libisoburn/libisoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c xorriso/xorrisoburn.c INCOMPATIBLE API CHANGE: isoburn_prepare_new_image() now gets output drive 2008.01.27.174454 [1434] xorriso/xorriso.c xorriso/xorriso.1 Changed meaning of -add relative/path 2008.01.28.105404 [1435] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Made use of iso_set_abort_severity() with option -abort_on 2008.01.28.140149 [1436] libisoburn/libisoburn.h Updated API introduction and marked API instabilities for mending (soon) 2008.01.28.171915 [1437] libisoburn/isoburn.c libisoburn/isofs_wrap.c xorriso/xorrisoburn.c xorriso/compile_xorriso.sh xorriso/make_xorriso_standalone.sh Adapted to libisofs revison 294 2008.01.28.235717 [1438] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c xorriso/xorrisoburn.c Changed struct isoburn_source_opts to opaque handle struct isoburn_imgen_opts 2008.01.29.125956 [1439] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c xorriso/xorrisoburn.c Changed struct isoburn_read_opts to opaque handle 2008.01.29.184356 [1440] configure.ac version.h.in libisoburn/libisoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c xorriso/xorrisoburn.c xorriso/configure_ac.txt New API call isoburn_is_compatible() 29 Jan 2008 [1441] libisoburn/libisoburn.h Some API documentation enhancements 2008.01.29.211543 [1443] libisoburn/burn_wrap.c Made use of newest libburn version features 2008.01.31.152131 [1449] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c xorriso/make_xorriso_standalone.sh Adapted to libisofs revision 313 2008.01.31.214647 [1450] xorriso/xorrisoburn.h libisoburn/libisoburn.h libisoburn/burn_wrap.c xorriso/make_xorriso_standalone.sh Compile time and runtime checks for library compatibility 2008.02.01.195817 [1455] configure.ac Makefile.am - libisoburn.pc.in + libisoburn-1.pc.in xorriso/README Enabled build of dynamically linked xorriso, joined -1.pc club 2008.02.01.200153 [1456] xorriso/xorriso_makefile_am.txt Corrected some false paths in xorriso-standalone Makefile.am 2008.02.02.131049 [1457] configure.ac Added version checks for libburn and libisofs (by Vreixo) 2008.02.02.131903 [1458] xorriso/xorriso.h xorriso/xorriso.c Added argument to option -commit_eject 2 Feb 2008 [1459] xorriso/xorriso_eng.html Updated to current state of development 2008.02.02.181200 [1460] xorriso/xorrisoburn.c Compile time check of libisoburn. Enforced minimum track size of 300 sectors. 2008.02.03.131525 [1466] libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c libisoburn/isofs_wrap.c xorriso/xorrisoburn.c Made use of ISO_ERR_SEV() and ISO_ERR_PRIO() 2008.02.03.155704 [1467] libisoburn/isofs_wrap.c Adapted to libisofs revisio 328 2008.02.03.164038 [1468] libisoburn/libisoburn.h libisoburn/isoburn.c xorriso/xorrisoburn.c New API call isoburn_cancel_prepared_write() 2008.02.03.164916 [1469] xorriso/xorrisoburn.c Reacted on compiler warning 2008.02.03.181259 [1470] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Made -abort_on and -report_about preemptive if given as dashed start arguments 2008.02.04.093106 [1471] libisoburn/burn_wrap.c xorriso/xorrisoburn.c Adapted to libisofs revision 332 2008.02.04.154405 [1472] libisoburn/libisoburn.h libisoburn/isoburn.c xorriso/xorrisoburn.c New API call isoburn_sync_after_write() 2008.02.04.184038 [1475] libisoburn/libisoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c libisoburn/isofs_wrap.c xorriso/xorrisoburn.c Inserted problem messages where libisoburn API produces own failures 2008.02.04.214133 [1476] libisoburn/libisoburn.h libisoburn/isoburn.c xorriso/xorrisoburn.c Added parameter to new API calls isoburn_cancel_prepared_write, isoburn_sync_after_write 2008.02.05.162621 [1477] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -padding 2008.02.05.175733 [1478] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorriso.1 Implemented reassure for -rollback, -rollback_end, -end, -commit, -commit_eject 2008.02.05.191456 [1479] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorriso.1 Implemented reassure for -blank, -format, -dev, -indev, -devices 2008.02.06.131028 [1480] xorriso/xorriso.c xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorrisoburn.c libisoburn/libisoburn.h libisoburn/burn_wrap.c Now loading libraries before -version, pacifier for -add 2008.02.06.153709 [1481] libisoburn/burn_wrap.c xorriso/xorriso.c xorriso/xorriso.1 xorriso/xorrisoburn.c Rectified SORRY,FAILURE,FATAL classification 2008.02.06.183423 [1484] libisoburn/burn_wrap.c xorriso/xorrisoburn.c Adapted to libisofs revision 337 2008.02.06.183557 [1485] xorriso/xorriso.c Removed some unused code 2009.02.06.184008 [1486] libisoburn/burn_wrap.c xorriso/xorrisoburn.c Added a missing 0 digit 2008.02.06.214426 [1487] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c xorriso/xorrisoburn.c Pacifier for isoburn_read_image() 2008.02.07.074248 [1489] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso_private.h Added final message to image read pacifier 2008.02.07.154947 [1490] libisoburn/data_source.c Implemented a primitive single tile cache for image reading 2008.02.07.211424 [1491] libisoburn/data_source.c Stuffed memory leak with read cache. Economized on error retries. 8 Feb 2008 [1494] - xorriso/compare_file. + test/compare_file.c - ng_src Cleaning up remainings of move to ng_src 8 Feb 2008 [1495] test/compare_file.c Fixed an endless cycle with early EOF 2008.02.08.102122 [1496] xorriso/xorriso_makefile_am.txt xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/README Added test/compare_file.c to xorriso-standalone tarball 2008.02.08.173928 [1497] 2008.02.08.174126 [1498] libisoburn/data_source.c Renamed macro which controls read caching 2008.02.08.175152 [1499] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Protecting volume ids from getting overwritten by predecessor 2008.02.08.195627 [1500] xorriso/xorriso.c Warning of -volid which are not ECMA 119 compliant 2008.02.08.215113 [1501] xorriso/xorriso.h xorriso/xorriso.c Silenced misleading toc messages with -commit_eject "in" 2008.02.09.092805 [1502] libisoburn/libisoburn.h libisoburn/isoburn.c Adapted to libisofs revision 346 2008.02.09.100750 [1503] libisoburn/libisoburn.h libisoburn/burn_wrap.c xorriso/xorriso.c xorriso/xorrisoburn.c New API function isoburn_disc_available_space() fixes bug with DVD+RW capacity 10 Feb 2008 [1504] xorriso/README Some adjustments in description of compare_file and libisoburn 2008.02.10.122020 [1505] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Fixed bugs about -mv /im/age . -cdx / -cpr di/sk im/age -- -cpr di/sk . -- 2008.02.10.130852 [1506] Makefile.am Added build of test/compare_file as noinst binary 2008.02.10.135822 [1507] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Better handling of not-yet-existing -cd 2008.02.11.094742 [1509] xorriso/xorriso_private.h xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Discarding, keeping or patching boot images from existing ISO images 2008.02.11.112917 [1510] xorriso/xorrisoburn.c Corrected message about isolinux patch option 2008.02.11.113235 [1511] xorriso/xorriso.h xorriso/xorriso.c Final UPDATE message after grafting in files from disk 2008.02.11.135418 [1512] xorriso/xorriso_private.h xorriso/xorriso.h xorriso/xorriso.c xorriso/xorriso.1 New option -return_with 2008.02.11.150123 [1513] xorriso/xorrisoburn.c isoburn_igopt_allow_full_ascii for -bootimage any keep 2008.02.11.150700 [1514] xorriso/xorriso.c xorriso/xorriso.1 Clarifications about -volid rules 11 Feb 2008 [1515] xorriso/README Adjustments about libisoburn and xorriso dynamic dependencies, compare_file 2008.02.11.162406 [1516] xorriso/xorrisoburn.c xorriso/xorriso.c Changed failure to find a file for removal from FAILURE to SORRY 2008.02.11.174517 [1517] xorriso/xorriso.c Corrected a bug about -status -return_with 2008.02.11.194807 [1519] libisoburn/burn_wrap.c xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New severity MISHAP 2008.02.11.213918 [1520] xorriso/xorrisoburn.c Mapping in burn runs -abort_on MISHAP to SORRY 2008.02.12.215327 [1521] libisoburn/burn_wrap.c xorriso/xorriso.c xorriso/xorrisoburn.c Improved MISHAP - SORRY mapping and tunneling 2008.02.14.084342 [1525] + README + COPYING + COPYRIGHT + INSTALL libisoburn/libisoburn.h Completing documentation 2008.02.14.101916 [1526] Makefile.am Adjusted EXTRA_DIST list of files 2008.02.14.120557 [1527] xorriso/xorrisoburn.c Made a final abort check before burning begins 2008.02.14.175623 [1528] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Imprinting version of xorriso and libraries into ISO preparer_id 2008.02.14.182351 [1529] xorriso/xorrisoburn.c Made preparer_id more safe against oversize 14 Feb 2008 [1530] xorriso/xorriso.1 Polished man page 14 Feb 2008 [1531] xorriso/changelog.txt Updated changelog 2008.02.15.100001 [branch 1533] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/compile_xorriso.sh xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Made version number leap to 0.1.0 15 Feb 2008 [branch 1534] xorriso/changelog.txt Documented initial release and timestamp 15 Feb 2008 [1536] configure.ac Increased libisofs requirement to 0.6.2 ----------------------------- release - xorriso-0.1.0.pl00 - 2008.02.15.100001 * Operates on an existing ISO image or creates a new one. * Copies files from filesystem into the ISO image. * Renames or deletes file objects in the ISO image. * Changes file properties in the ISO image. * Can write result as completely new image to optical media or filesystem objects. * Can write result as add-on session to appendable multi-session media, to overwriteable media, to regular files, and to block devices. * Scans for optical drives, blanks re-useable optical media. * Reads its instructions from command line arguments, dialog, and batch files. * Provides navigation commands for interactive ISO image manipulation. * Adjustable thresholds for abort, exit value, and problem reporting. 2008.02.15.211836 [1537] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/compile_xorriso.sh xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.1.1 15 Feb 2008 [1538] COPYRIGHT Corrected according to content of libisofs/COPYRIGHT 2008.02.15.212030 [1539] xorriso/xorriso.c Changed pacifier text for 0 nodes read from blank image ------------------------------------ cycle - xorriso-0.1.1 - 2008.02.15.212030 15 Feb 2008 [1540] xorriso/changelog.txt Updated changelog 16 Feb 2008 [1543] xorriso/xorriso.1 Adjusted CREDITS text 2008.02.16.202533 [1544] xorriso/xorriso_private.h xorriso/xorrisoburn.c Transfering properties from disk to implicit directories in image path 2008.02.16.211549 [1545] xorriso/xorriso_eng.html xorriso/changelog.txt Mentioned better directory attribute copying ------------------------------------ cycle - xorriso-0.1.1 - 2008.02.16.211549 * Improved attribute transfer from disk for implicit target directories 2008.02.18.210343 [1546] xorriso/xorriso.c Bug fix: -report_about higher than NOTE did not report at all 19 Feb 2008 [1547] test/compare_file.c Improved report format 2008.02.19.184432 [1548] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -as cdrecord emulates a narrow set of cdrecord gestures 2008.02.19.212322 [1549] xorriso/xorriso.c xorriso/xorriso_private.h xorriso/xorrisoburn.c xorriso/xorriso.1 Improved -as cdrecord 20 Feb 2008 [1550] test/compare_file.c Revoked inflation of file type characters to words 2008.02.20.234726 [1551] libisoburn/libisoburn.h libisoburn/burn_wrap.c xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -as mkisofs emulates a narrow set of mkisofs gestures ------------------------------------ cycle - xorriso-0.1.1 - 2008.02.21.090014 * Bug fix: -report_about HINT or higher did not report at all * Bug fix: speed=number without unit or media type letter was always CD speed * Bug fix: it was possible to write to appendable media which was not -indev * New option -as "cdrecord" emulates a narrow set of cdrecord gestures * New option -as "mkisofs" emulates a narrow set of mkisofs gestures 2008.02.21.090014 [1552] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c New option -publisher 2008.02.21.185203 [1553] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Implemented a more reasonable solution for drive truncation with regular files 2008.02.21.204613 [1558] libisoburn/burn_wrap.c xorriso/xorrisoburn.c Introduced new severity ERRFILE 2008.02.21.211101 [1559] xorriso/xorriso.c Updated -help text 2008.02.22.114917 [1561] libisoburn/burn_wrap.c xorriso/xorriso.c xorriso/xorrisoburn.c Producing ERRFILE messages where appropriate 2008.02.23.101619 [1568] libisoburn/libisoburn.h Changed libisofs requirements to 0.6.3 2008.02.23.101619 [1569] libisoburn/burn_wrap.c xorriso/xorrisoburn.c Testwise implementation of libisofs.0.6.3 features 2008.02.23.101619 [1570] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorriso.1 New option -errfile_log 2008.02.23.102903 [1571] xorriso/configure_ac.txt Made libisofs version leap in xorriso-standalone 2008.02.23.113902 [1572] xorriso/xorriso.h xorriso/xorriso.c Directed -as mkisofs -print-size to real stdout 2008.02.23.125537 [1573] xorriso/xorrisoburn.c Various bugs and deviations around -as mkisofs -print-size 2008.02.23.131825 [1574] xorriso/xorrisoburn.c Wrong description text 2008.02.25.090001 [ZeroOneZero-pl01 1576 1577] libisoburn/burn_wrap.c xorriso/xorriso_timestamp.h Safety cap for ABI usage bug towards libisofs >= 0.6.2 2008.02.25.174229 [1579] libisoburn/burn_wrap.c xorriso/xorrisoburn.c Adapted to libisofs 362 from Vreixo bzr 2008.02.25.204402 [1580] xorriso/xorriso.c Adaptions of -as mkisofs and -as cdrecord to enable full use by scdbackup 25 Feb 2008 [1581] xorriso/changelog.txt Updated changelog ------------------------------------ cycle - xorriso-0.1.1 - 2008.02.25.204402 * New option -publisher * New option -errfile_log 26 Feb 2008 [1582] test/compare_file.c Avoided to read content of non-regular files 2008.02.26.213437 [1583] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -compare 2008.02.27.185744 [1584] xorriso/xorriso.c Installed pacifier for data reading 2008.02.28.132656 [1587] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Added new action "compare" to -find 28 Feb 2008 [1588] xorriso/xorriso_eng.html Mentioned capability to write DVD+R/DL. 2008.02.28.212210 [1589] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c New option -compare_r 28 Feb 2008 [1590] xorriso/xorriso.1 New option -compare_r, new -findx -exec in_iso, -exec no_in_iso 28 Feb 2008 [1591] test/compare_file.c Micro bug fix ported from xorriso 2008.02.28.215343 [1592] xorriso/xorriso.c Better handling of disk to iso address mapping during compare runs 2008.02.29.200510 [1593] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -update (not yet completed) 2008.03.01.151219 [1594] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option update_r, -find actions "update" and "add_missing" (not yet completed) 2008.03.02.172751 [1596] xorriso/xorrisoburn.c Made -update_r and -find -exec update safe against IsoDirIter pitfalls 2008.03.02.201455 [1597] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Made IsoDirIter workaround obey -temp_mem_limit 4 Mar 2008 [libisofs_thomas 1599 1600] Switch to Vreixo development branch 378 2008.03.04.174107 [1601] xorriso/make_xorriso_standalone.sh xorriso/xorriso_makefile_am.txt Adapted to libisofs Vreixo 378 2008.03.04.180738 [1602] xorriso/xorriso.c xorriso/xorrisoburn.c Improvements about -update_r and -compare_r 2008.03.05.125118 [1603] xorriso/xorrisoburn.c Bug fix: -follow param did not work for symbolic links ------------------------------------ cycle - xorriso-0.1.1 - 2008.03.05.125118 * Support for DVD+R/DL media * Bug fix: -follow param did not work for adding non-directory symbolic links 2008.03.05.173329 [1604] xorriso/xorriso.c xorriso/xorrisoburn.c Made -compare* and -update* obey -follow links (-follow mount still not correct) 5 Mar 2008 [1605] xorriso/xorriso.1 Some man page clarification 2008.03.06.114233 [1606] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New -find actions "rm" and "rm_r", silenced comparison results with -update* 2008.03.06.174724 [1608] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Fixed bug about non existing disk_path with -update_r 7 Mar 2008 [1609] xorriso/xorriso_eng.html Re-arranged development download part 2008.03.07.075325 [1610] xorriso/xorriso.c xorriso/xorrisoburn.c New -findx -type "m" for active mount points 2008.03.07.182411 [1611] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New -findx -exec action "empty_iso_dir", workaround for ticket 132 2008.03.07.220442 [1612] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 Made -update_r and -compare_r obey disabling of -follow "mount" 8 Mar 2008 [libisofs_thomas 1613] Switch to Vreixo development branch 383 2008.03.08.104231 [1614] xorriso/xorrisoburn.c Adapted to iso_tree_add_new_node() 8 Mar 2008 [1615] xorriso/xorriso.1 Example use case for -update_r "Incremental backup of a few directory trees" ------------------------------------ cycle - xorriso-0.1.1 - 2008.03.08.122747 * Bug fix: It was not possible to -add /THIS=a /b=THIS * New options -compare, -compare_r and according -find -exec action * New options -update, -update_r and according -find -exec action * New -find actions "rm", "rm_r", new -findx -type "m" -exec "empty_iso_dir" 2008.03.09.142200 [1616] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -cut_out 9 Mar 2008 [libisofs_thomas 1617] Switch to Vreixo development branch 386 2008.03.09.211041 [1618] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Some polishing about -cut_out 2008.03.11.113444 [1619] xorriso/xorriso.c xorriso/xorrisoburn.c Sketched split file scheme with partno,totalparts,offset,count,totalsize 11 Mar 2008 [1620] xorriso/xorriso.1 Updated description of -cut_out ------------------------------------ cycle - xorriso-0.1.1 - 2008.03.11.113444 * New option -cut_out 2008.03.12.100001 [XorrisoZeroOneTwo 1626] xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to xorriso-0.1.2 ---------------------------------- release - xorriso-0.1.2 - 2008.03.12.100001 * Bug fix: -report_about HINT or higher did not report at all * Bug fix: speed=number without unit or media type letter was always CD speed * Bug fix: it was possible to write to appendable media which was not -indev * Bug fix: -follow param did not work for adding non-directory symbolic links * Bug fix: It was not possible to -add /THIS=a /b=THIS * Improved attribute transfer from disk for implicit target directories * New option -as "cdrecord" emulates a narrow set of cdrecord gestures * New option -as "mkisofs" emulates a narrow set of mkisofs gestures * New option -publisher * New option -errfile_log * Support for DVD+R/DL media * New options -compare, -compare_r and according -find -exec action * New options -update, -update_r and according -find -exec action * New -find actions "rm", "rm_r", new -findx -type "m" -exec "empty_iso_dir" * New option -cut_out 2008.03.12.130605 [1627] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.1.3 ------------------------------------ cycle - xorriso-0.1.3 - 2008.03.12.130605 12 Mar 2008 [XorrisoZeroOneTwo 1628] [1629] xorriso/changelog.txt Documented changes and release timestamp 2008.03.19.081837 [1637] xorriso/xorriso.c Bug fix: -as mkisofs -nopad must be -no-pad 19 Mar 2008 [1638] xorriso/xorriso.1 Added a hint about alternating media with -update_r ------------------------------------ cycle - xorriso-0.1.3 - 2008.03.19.081837 * Bug fix: -as mkisofs -no-pad was misspelled -nopad 20 Mar 2008 [1639] xorriso/README Added -commit_eject all to an example which involves mount 2008.03.20.192317 [1640] xorriso/xorriso.c Changed messages of -update and -update_r 2008.03.20.210522 [1641] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Better handling of attempt to manipulate non-existent ISO image 2008.03.22.130031 [1642] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Working towards coordination of -compare, -update and -cut_out 2008.03.22.130128 [1643] xorriso/xorrisoburn.c Bug fix: Implicite directory attribute copying with -cut_out was wrong ------------------------------------ cycle - xorriso-0.1.3 - 2008.03.22.130128 * Bug fix: Implicite directory attribute copying with -cut_out was wrong 22 Mar 2008 [1644] xorriso/xorriso.1 Correction about Linux mount which does not accept session= with DVD. 2008.03.25.170747 [1645] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Working towards coordination of -compare, -update and -cut_out 2008.03.26.092120 [1646] xorriso/xorrisoburn.c Gave up obsoleted macros and their code branches 2008.03.27.103344 [1647] xorriso/xorriso.c xorriso/xorrisoburn.c Working towards coordination of -update and -cut_out 27 Mar 2008 [1648] xorriso/xorriso.1 Published coordination rules for -cut_out, -compare and -update 2008.03.29.164038 [1649] xorriso/xorriso_private.h xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -split_size, automated splitting of oversized files ------------------------------------ cycle - xorriso-0.1.3 - 2008.03.29.164038 * Coordination of -cut_out, -compare and -update * New option -split_size, automated splitting of oversized files 2008.03.31.081347 [1650] xorriso/xorriso.c xorriso/xorriso.1 Adjusted documentation of -split_size 2008.04.01.213121 [1652] libisoburn/data_source.c Replaced single 128 kB cache tile by 32 tiles of 64 kB each 2 Apr 2008 [1653] COPYRIGHT Corrected GPL version ------------------------------------ cycle - xorriso-0.1.3 - 2008.04.03.074309 * Improved performance with reading directory trees 2008.04.03.204051 [1657] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -session_log 5 Apr 2008 [1658] xorriso/xorriso.1 Mentioned that drives close full media automatically 2008.04.05.112055 [1659] xorriso/xorrisoburn.c Reporting amount of non-data with -toc media summary 5 Apr 2008 [1660] xorriso/xorriso_eng.html Mentioned new features ------------------------------------ cycle - xorriso-0.1.3 - 2008.04.05.112055 * New option -session_log 2008.04.07.185727 [1662] xorriso/xorriso.h Added prototype of Xorriso_option_session_log() 2008.04.07.201253 [1664] xorriso/xorriso.c xorriso/xorrisoburn.c Removed some unused code 8 Apr 2008 [1677] xorriso/configure_ac.txt Followed version leaps of libburn 2008.04.08.153508 [1678] libisoburn/libisoburn.h xorriso/xorrisoburn.c Made libisoburn and xorriso require libburn >= 0.4.4 2008.04.09.114815 [1679] xorriso/xorriso.c xorriso/xorrisoburn.c Corrected behavior around image data read error 2008.04.12.112159 [1682] xorriso/xorriso.c Changed behavior of -commit_eject "" 2008.04.18.184517 [1690] libisoburn/data_source.c Restricted ds_read_block() messages about unreadable data to actual data block 2008.04.20.111054 [1692] libisoburn/data_source.c Corrected maximum age value for read buffer tiles 20 Apr 2008 [1693] xorriso/xorriso.1 Mentioned stdin with option -path_list 2008.04.20.111419 [1694] libisoburn/burn_wrap.c xorriso/xorrisoburn.c Displaying free space rather than "non-data" on drive aquiry and -toc 28 Apr 2008 [1709] xorriso/configure_ac.txt Updated libisofs version number in xorriso-standalone ------------------------------------ cycle - xorriso-0.1.3 - 2008.04.28.082539 * Now depending on libisofs-0.6.4 2008.04.28.120001 [branch 1711] configure.ac README libisoburn/libisoburn.h (isoburn_header_version_*) xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h (xorriso_libisoburn_req_*) xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.1.4 28 Apr 2008 [branch 1712] xorriso/changelog.txt Documented changes and release timestamp ---------------------------------- release - xorriso-0.1.4 - 2008.04.28.120001 * Bug fix: -as mkisofs -no-pad was misspelled -nopad * Bug fix: Implicite directory attribute copying with -cut_out was wrong * Coordination of -cut_out, -compare and -update * New option -split_size, automated splitting of oversized files * Improved performance with reading directory trees * New option -session_log * Dynamically linkable with release version 0.6.4 of libisofs 2008.04.28.122244 [1713] configure.ac README libisoburn/libisoburn.h (isoburn_header_version_*) xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h (xorriso_libisoburn_req_*) xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.1.5 28 Apr 2008 [1714] xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-0.1.5 - 2008.04.28.122244 2008.05.01.124248 [1716] libisoburn/isofs_wrap.c xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -error_behavior with a first occasion 'image_loading' 1 May 2008 [1717] xorriso/convert_man_to_html.sh Fine tuning of HTML man page appearance 1 May 2008 [1718] xorriso/xorriso.1 Clarified "best_effort" behavior 2008.05.01.135421 [1719] libisoburn/data_source.c Adjusted ds_read_block() to inofficial libisofs expectations 2008.05.01.174110 [1720] libisoburn/data_source.c Adjusted ds_read_block() to inofficial libisofs expectations 2008.05.02.072505 [1721] libisoburn/data_source.c Adjusted ds_read_block() to inofficial libisofs expectations 2008.05.02.204942 [1722] xorriso/xorriso_private.h xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.c Working towards exclusion of user defined absolute paths and leaf patterns 2008.05.03.151106 [1724] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Working towards exclusion of user defined absolute paths and leaf patterns 2008.05.03.223204 [1725] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Working towards exclusion of user defined absolute paths and leaf patterns 2008.05.04.133525 [1726] xorriso/xorriso.c xorriso/xorriso.1 New options -not_paths, -not_leaf, -not_list, -not_mgt, -as mkisofs -m 4 May 2008 [1727] xorriso/xorriso_eng.html xorriso/changelog.txt Mentioned new options ------------------------------------ cycle - xorriso-0.1.5 - 2008.05.04.133525 * New option -error_behavior with a first occasion 'image_loading' * New options -not_paths, -not_leaf, -not_list, -not_mgt, -as mkisofs -m 2008.05.05.210317 [1728] xorriso/xorrisoburn.c After formatting DVD-RW write 128 MB and close track to end Intermediate State 2008.05.06.084439 [1731] xorriso/xorrisoburn.c Calling isoburn_cancel_prepared_write() after failed isoburn_disc_write() 2008.05.06.144606 [1732] libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c libisoburn/burn_wrap.c Experiment for TOC on overwriteables: Keep a target_head copy of session #1 2008.05.07.071427 [1737] libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c libisoburn/isofs_wrap.c Removed some outdated ifdef cases 2008.05.07.175508 [1738] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/burn_wrap.c Reading emulated toc info from overwriteable media, new API isoburn_toc_*() 2008.05.07.175640 [1739] xorriso/xorrisoburn.c Making use of new isoburn_toc_* functions 2008.05.07.214343 [1740] libisoburn/libisoburn.h libisoburn/burn_wrap.c New API function isoburn_read_iso_head() 2008.05.07.214442 [1741] xorriso/xorrisoburn.c New format with -toc is more concise and shows volume id 2008.05.08.141054 [1742] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/burn_wrap.c Try to read header chain from alleged -ROM media (e.g. DVD+RW in -ROM drive) 2008.05.08.141920 [1743] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/xorriso_eng.html New TOC layout with volume id and sbsector=, new option -rom_toc_scan 8 May 2008 [1744] xorriso/xorriso_eng.html Mentioned new features ------------------------------------ cycle - xorriso-0.1.5 - 2008.05.08.141920 * Emulated TOC on overwriteable media * New TOC layout with volume id and sbsector= * New option -rom_toc_scan 2008.05.08.185350 [1745] libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c Adjusted reply of isoburn_disc_get_msc1() to eventual -rom_toc_scan result 8 May 2008 [1746] xorriso/xorriso.1 Updated man page examples 8 May 2008 [1747] xorriso/xorriso.1 Updated man page examples ------------------------------------ cycle - xorriso-0.1.5 - 2008.05.08.205551 9 May 2008 [1751] xorriso/xorriso_eng.html Updated details in web page 2008.05.09.205517 [1752] libisoburn/isofs_wrap.c Trying to better handle non ISO images on -indev 10 May 2008 [1761] xorriso/configure_ac.txt Adapted xorriso standalone production to new libburn cycle 0.4.7 10 May 2008 [1762] xorriso/xorriso.1 xorriso/xorriso_eng.html Minor change in -update_r example 2008.05.10.194336 [1763] xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.c Better behavior of update_r with no image present 2008.05.12.080812 [1765] configure.ac libisoburn/libisoburn.h Switched requirements to libburn-0.4.6 2008.05.12.081331 [1766] libisoburn/libisoburn.h libisoburn/burn_wrap.c New API call isoburn_set_msc1() 2008.05.12.082733 [1767] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/xorriso_eng.html New option -load session|track|sbsector|volid ------------------------------------ cycle - xorriso-0.1.5 - 2008.05.12.082733 * New option -load session|track|sbsector|volid * Now depending on libburn-0.4.6 2008.05.12.193341 [1768] libisoburn/burn_wrap.c Reacted on harmless compiler warning 2008.05.12.193642 [1769] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New -blank and -format modes as_needed 2008.05.13.115901 [1770] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/xorriso_eng.html New option -list_formats 2008.05.13.135251 [1771] libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c libisoburn/burn_wrap.c xorriso/xorrisoburn.c xorriso/xorriso.1 Corrected strange behavior with non-ISO images on overwriteable media 2008.05.13.153837 [1772] xorriso/xorrisoburn.c Prepared Xorriso_get_profile() for potential call without flag bit1 2008.05.13.180624 [1773] xorriso/xorrisoburn.c Handled -list_format with non MMC drives 2008.05.13.180912 [1774] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorriso.1 Eat up leading dashes with command options, convert inner dashes to underscores ------------------------------------ cycle - xorriso-0.1.5 - 2008.05.13.180912 * New -blank and -format modes as_needed * New option -list_formats 2008.05.14.114548 [1775] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New -format types by_index and fast_by_index 2008.05.14.173201 [1779] configure.ac libisoburn/libisoburn.h Required libburn version is now 0.4.7 (because of DVD-RAM BD-RE bug fix) 2008.05.14.173430 [1780] xorriso/xorrisoburn.c Automatic fast format on attempt to write to unformatted DVD-RAM or BD-RE 2008.05.14.174846 [1781] xorriso/xorriso_private.h Uploaded forgotten enhancements 2008.05.15.092028 [1783] xorriso/xorriso.c Enabled a pacifier for compare_r 2008.05.15.150041 [1784] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/xorriso_eng.html New options -map and -map_single 2008.05.15.192118 [1785] xorriso/xorriso.c Bug fix: -update_r and others did not work properly with relative paths ------------------------------------ cycle - xorriso-0.1.5 - * New options -map and -map_single * Bug fix: -update_r and others did not work properly with relative paths 2008.05.17.162753 [1794] xorriso/xorriso.c xorriso/xorrisoburn.c Corrected ugly message with -update_r and root directory 2008.05.17.170001 [1796] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.1.6, requiring libburn-0.4.8 now 2008.05.18.070001 [1797] xorriso/xorriso.c xorriso/xorrisoburn.c Bug fix: -findi operated on nodes which ceased existence shortly before 18 May 2008 [1799] xorriso/changelog.txt Documented changes and release timestamp ---------------------------------- release - xorriso-0.1.6 - 2008.05.18.070001 * New option -error_behavior with a first occasion 'image_loading' * New options -not_paths, -not_leaf, -not_list, -not_mgt, -as mkisofs -m * Emulated TOC on overwriteable media * New TOC layout with volume id and sbsector= * New option -rom_toc_scan * New option -load session|track|sbsector|volid * Now depending on libburn-0.4.8 * New -blank and -format modes as_needed * New option -list_formats * New options -map and -map_single * Bug fix: -update_r and others did not work properly with relative paths * Bug fix: -findi operated on nodes which ceased existence shortly before 2008.05.18.082208 [1798] xorriso/xorriso.c xorriso/xorrisoburn.c Bug fix: -findi operated on nodes which ceased existence shortly before 2008.05.18.084729 [1800] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/changelog.txt Version leap to 0.1.7, requiring libburn-0.4.8 now ------------------------------------ cycle - xorriso-0.1.7 - 2008.05.18.084729 2008.05.20.075142 [1804] xorriso/xorriso.c Making Xorriso_prescan_args() safe against misunderstandings 2008.05.22.192618 [1808] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c First experiments of osirrox ISO-to-disk copying 2008.05.22.192737 [1809] libisoburn/data_source.c Removed outdated code branch 2008.05.22.210835 [1810] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New options -osirrox and -cpx 2008.05.24.092546 [1812] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Enabled osirrox of more file types, curbed with device files 2008.05.24.092853 [1813] libisoburn/isoburn.c Bug fix: modifying to overwriteable target yielded unmountable results ------------------------------------ cycle - xorriso-0.1.7 - 2008.05.24.092853 * Bug fix: Modifying to overwriteable target yielded unmountable results * New options -osirrox and -cpx 2008.05.24.170109 [1814] xorriso/xorriso.c xorriso/xorrisoburn.c Some polishing with -cpx 26 May 2008 [1815] xorriso/configure_ac.txt standalone version switch to libisofs-0.6.5 2008.05.26.181210 [1816] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Improved access permission restauration by osirrox 2008.05.27.201513 [1817] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/xorriso_eng.html New option -stream_recording 2008.05.31.174045 [1819] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 Implemented concatenation of split files during -cpx ------------------------------------ cycle - xorriso-0.1.7 - 2008.05.31.174045 * New option -stream_recording 2008.06.01.134322 [1820] xorriso/xorrisoburn.c Reacted on harmless compiler warning 2008.06.01.145038 [1821] xorriso/configure_ac.txt standalone version switch to libisofs-0.6.6 2008.06.01.145155 [1822] configure.ac libisoburn/libisoburn.h Switched requirements to libisofs-0.6.6 ------------------------------------ cycle - xorriso-0.1.7 - 2008.06.01.145155 * Bug fix: major,minor numbers of device files appeared as 0,1 in next session 2008.06.02.070301 [1824] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.1.8 2 Jun 2008 [1825] xorriso/changelog.txt Documented changes and release timestamp ---------------------------------- release - xorriso-0.1.8 - 2008.06.02.070301 * Bug fix: Modifying to overwriteable target yielded unmountable results * Bug fix: major,minor numbers of device files appeared as 0,1 in next session * New option -stream_recording for full speed with DVD-RAM and BD-RE * New options -osirrox and -cpx allow to extract single files from ISO image 2008.06.02.141334 [1826] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.1.9 2 Jun 2008 [1827] xorriso/changelog.txt Documented changes and release timestamp 2 Jun 2008 [1828] xorriso/xorriso.1 Beautified documentation of -osirrox ------------------------------------ cycle - xorriso-0.1.9 - 2008.06.02.141334 5 Jun 2008 [1834] xorriso/convert_man_to_html.sh Beautified HTML man page 2008.06.05.165023 [1835] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New options -cpax, -cp_rx, -cp_rax to restore files and trees from ISO to disk 2008.06.06.083432 [1839] xorriso/xorriso.c Bug fix: -chmod unintentionally performed o-x as first operation 2008.06.06.103735 [1840] xorriso/xorrisoburn.c xorriso/xorriso.1 Fine tuning of directory attribute copying with -cp_rx ------------------------------------ cycle - xorriso-0.1.9 - * New options -cpax, -cp_rx, -cp_rax to restore files and trees from ISO to disk * Bug fix: -chmod unintentionally performed o-x as first operation 2008.06.09.134432 [1841] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Allowing to restore from image with pending changes 2008.06.09.165735 [1842] xorriso/xorriso.c xorriso/xorrisoburn.c Removed some outdated code parts 2008.06.10.094304 [1844] libisoburn/isoburn.c libisoburn/burn_wrap.c Removed outdated code parts 2008.06.10.100231 [1845] xorriso/xorrisoburn.c Removed outdated code parts 2008.06.11.131607 [1846] xorriso/xorrisoburn.c xorriso/xorriso.1 Proper handling of restore overwrite situations with directories and softlinks ------------------------------------ cycle - xorriso-0.1.9 - 2008.06.11.131607 2008.06.12.112644 [1847] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Corrections about restoring of directories ------------------------------------ cycle - xorriso-0.1.9 - 2008.06.12.112644 2008.06.13.151630 [1848] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -paste_in 2008.06.14.104745 [1849] xorriso/xorriso.c xorriso/xorriso.1 xorriso/xorriso_eng.html New options -extract and -extract_single 14 Jun 2008 [1850] xorriso/convert_man_to_html.sh Beautification of HTML man page 2008.06.14.140459 [1851] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Correction about -extract / / 2008.06.14.184512 [1854] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorriso.1 xorriso/convert_man_to_html.sh Gave up the unusual parameter sequences of -extract and -paste_in ------------------------------------ cycle - xorriso-0.1.9 - 2008.06.14.184512 * New option -paste_in to copy ISO files into parts of disk files * New option -extract restores with arguments of -map or -update_r 2008.06.17.121524 [1857] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorriso.1 New options -map_l, -compare_l, -update_l, -extract_l 2008.06.17.133914 [1858] xorriso/xorriso.c Reacted on harmless compiler warning 2008.06.17.170622 [1859] xorriso/xorriso.c xorriso/xorrisoburn.c Fixed a bug about -update_l 2008.06.18.132057 [1860] libisoburn/isoburn.h libisoburn/burn_wrap.c Made -rom_toc_scan work on closed DVD-R in a DVD-ROM drive 2008.06.18.161512 [1861] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c libisoburn/burn_wrap.c libisoburn/data_source.c New API functions isoburn_preset_msgs_submit(), isoburn_set_msgs_submit() 2008.06.18.161723 [1862] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.c Made use of isoburn_preset_msgs_submit() ------------------------------------ cycle - xorriso-0.1.9 - 2008.06.18.161723 * New options -map_l, -compare_l, -update_l, -extract_l * New API functions isoburn_set_msgs_submit(), isoburn_drive_set_msgs_submit() 2008.06.18.192913 [1863] libisoburn/burn_wrap.c Improved pacifier with -rom_toc_scan 2008.06.19.090436 [1864] libisoburn/libisoburn.h libisoburn/burn_wrap.c xorriso/xorrisoburn.c Renamed isoburn_*set_msgs_submit() and slightly changed meaning 2008.06.19.092458 [1865] libisoburn/burn_wrap.c Corrected wrong size unit MB to kB in toc scan pacifier text 2008.06.20.091647 [1866] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New -osirrox option auto_chmod_on ------------------------------------ cycle - xorriso-0.1.9 - 2008.06.20.091647 2008.06.20.164105 [1867] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Fixed several bugs with restore operations 2008.06.21.180701 [1870] xorriso/xorriso.1 Corrected mistake in xorriso man page ------------------------------------ cycle - xorriso-0.1.9 - 2008.06.21.180701 2008.06.22.080000 [1872] xorriso/xorriso.c Removed duplicated help text snippet 2008.06.22.090001 [1873] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.2.0 22 Jun 2008 [1874] xorriso/changelog.txt Documented changes and release timestamp ---------------------------------- release - xorriso-0.2.0 - 2008.06.22.090001 * Bug fix: -chmod unintentionally performed o-x as first operation * New options -cpax, -cp_rx, -cp_rax to restore files and trees from ISO to disk * New option -extract restores with arguments of -map or -update_r * New option -paste_in to copy ISO files into parts of disk files * New options -map_l, -compare_l, -update_l, -extract_l * New API functions isoburn_set_msgs_submit(), isoburn_drive_set_msgs_submit() 2008.06.22.111236 [1875] xorriso/xorriso.c Removed duplicated help text snippet 2008.06.22.135202 [1876] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.2.1 22 Jun 2008 [1877] xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-0.2.1 - 2008.06.22.135202 2008.06.27.124201 [1880] xorriso/xorrisoburn.c Bug fix: -as cdrecord -atip falsely announced overwriteable DVD-RW to sdvdbackup 2008.06.27.130235 [1881] xorriso/xorriso.c Extended -as cdrecord blank= by blank type format_overwrite ------------------------------------ cycle - xorriso-0.2.1 - 2008.06.27.130235 2008.07.03.133023 [1882] xorriso/xorriso.c Flushing stdout after each result text to deconfuse stdout/stderr with SSH 2008.07.04.070001 [1884] xorriso/xorriso.c Supporting option -as cdrecord -waiti 2008.07.05.132528 [1885] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c libisoburn/burn_wrap.c libisoburn/data_source.c New API function isoburn_prepare_blind_grow() for -as mkisofs -multi 2008.07.05.133721 [1886] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -grow_blindly 2008.07.05.180241 [1887] libisoburn/isoburn.c Fixed a bug with -grow_blindly to overwriteable media 2008.07.05.182424 [1888] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorriso.1 New options -C and -M for -as mkisofs 2008.07.05.184434 [1889] xorriso/xorriso.c Cared for a peculiarity of growisofs when using mkisofs -C 5 Jul 2008 [1890] xorriso/xorriso_eng.html Updated xorriso homepage ------------------------------------ cycle - xorriso-0.2.1 - 2008.07.05.184434 * New API function isoburn_prepare_blind_grow() * New option -grow_blindly * New options -C and -M for -as mkisofs emulation 2008.07.06.110336 [1891] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Improved effective drive address with -as mkisofs -M 2008.07.07.095531 [1892] xorriso/xorriso.c Fixed bug about -as mkisofs without -C 2008.07.07.102941 [1893] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c Semi-permanent emulation by start names xorrisofs,genisofs,mkisofs,genioimage ------------------------------------ cycle - xorriso-0.2.1 - 2008.07.07.102941 2008.07.07.150241 [1894] xorriso/xorriso.c Correction about -as mkisofs -C if already an input device was set 2008.07.07.150337 [1895] libisoburn/isoburn.c Correction about isoburn_igopt_get_effective_lba() with blind growing 7 Jul 2008 [1896] xorriso/xorriso.1 Clarification about -grow_blindly predicted_nwa 2008.07.07.150337 [1897] xorriso/xorriso.c xorriso/xorriso.1 Made leafname triggered emulation more similar to -as emulation 2008.07.08.092732 [1898] Makefile.am README xorriso/xorriso_makefile_am.txt xorriso/README Installing softlinks xorrisofs and osirrox pointing to the xorriso binary 2008.07.08.102622 [1899] xorriso/xorriso_private.h Header file forgotten with rev 1897 2008.07.08.132054 [1900] xorriso/xorriso.c xorriso/xorrisoburn.c Avoided misleading message about emptied ISO image during -as mkisofs -C 8 Jul 2008 [1901] xorriso/xorriso_eng.html Documented newest progress ------------------------------------ cycle - xorriso-0.2.1 - 2008.07.08.132054 * Can serve growisofs if started as xorrisofs, genisofs, mkisofs, genisoimage * make install creates xorriso aliases as symbolic links: osirrox, xorrisofs 2008.07.09.055133 [1901] xorriso/xorriso.c Avoided to use MMC code with -as mkisofs -M by prepending stdio: to address 2008.07.09.055133 [1902] xorriso/xorriso.h xorriso/xorriso.c Fixed bug with -as mkisofs -x and pattern expansion ------------------------------------ cycle - xorriso-0.2.1 - 2008.07.09.055133 2008.07.09.155540 [1903] Makefile.am xorriso/xorriso_private.h + xorriso/xorriso_buildstamp.h + xorriso/xorriso_buildstamp_none.h xorriso/xorriso_makefile_am.txt xorriso/make_xorriso_standalone.sh Opportunity to generate build timestamp via make buildstamped 9 Jul 2008 [1904] xorriso/xorriso_makefile_am.txt Completed (unused) dist rule of standalone-xorriso 2008.07.10.141731 [1905] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 Enabled -multi and -msinfo with -as cdrecord 2008.07.10.141913 [1906] libisoburn/libisoburn.h Small correction in API introduction text 2008.07.10.144535 [1907] xorriso/xorriso_private.h Header file forgotten with rev 1903 2008.07.10.162809 [1908] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorriso.1 cdrecord emulation by start names xorrecord, cdrecord, wodim, cdrskin 2008.07.10.164015 [1909] xorriso/xorriso.c Reacted on compiler warning 2008.07.10.164412 [1910] Makefile.am README xorriso/xorriso_makefile_am.txt xorriso/README Installing softlink xorrecord pointing to the xorriso binary 10 Jul 2008 [1911] xorriso/xorriso.1 xorriso/xorriso_eng.html xorriso/convert_man_to_html.sh Some documentation updates ------------------------------------ cycle - xorriso-0.2.1 - 2008.07.10.164412 * New options -multi and -msinfo for -as cdrecord emulation * make install creates xorriso alias as symbolic link: xorrecord 2008.07.12.181846 [1912] libisoburn/libisoburn.h libisoburn/burn_wrap.c New info mode 2 with isoburn_read_iso_head() 2008.07.12.184833 [1913] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New options --grow_overwriteable_iso and write_start_address= with -as cdrecord 2008.07.14.114515 [1918] libisoburn/libisoburn.h Required libburn version is now 0.4.9 2008.07.14.114613 [1919] configure.ac Did LT_CURRENT++, LT_AGE++ which was forgotten with revision 1885 2008.07.14.120527 [1920] libisoburn/libisoburn.h libisoburn/burn_wrap.c New flag options with isoburn_read_iso_head() 2008.07.14.125133 [1921] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -isosize with -as cdrecord 14 Jul 2008 [1923] xorriso/convert_man_to_html.sh Small correction with a sed expression 2008.07.15.063040 [1924] xorriso/xorriso_makefile_am.txt Generating automatic build timestamp 2008.07.15.121754 [1925] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option tsize= with -as cdrecord ------------------------------------ cycle - xorriso-0.2.1 - 2008.07.15.121754 * New options --grow_overwriteable_iso and write_start_address= with -as cdrecord * New options -isosize and tsize= with -as cdrecord 2008.07.16.130711 [1933] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -pacifier, more compatible pacifier with -as mkisofs 2008.07.16.130758 [1934] xorriso/configure_ac.txt Updated xorriso standalone configure.ac version number BURN_*_VERSION 2008.07.16.130841 [1935] libisoburn/libisoburn.h Required libburn version is now 0.5.0 2008.07.16.131110 [1936] configure.ac Required libburn version is now 0.5.0 2008.07.16.140043 [1937] xorriso/xorriso.c xorriso/xorriso.1 Recognizing "b" as speed factor for BD media ------------------------------------ cycle - xorriso-0.2.1 - 2008.07.16.140043 * New option -pacifier, more compatible pacifier with -as mkisofs 2008.07.17.110812 [1939] libisoburn/libisoburn.h libisoburn/burn_wrap.c Ability to emulate a featured bug with mkisofs -C : read 16 block too early 2008.07.17.111411 [1940] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Rectified usage of original xorriso options underneath growisofs 2008.07.17.183024 [1941] libisoburn/burn_wrap.c Recognizing growisofs follow-up sessions on xorriso overwriteables 2008.07.17.184520 [1942] libisoburn/burn_wrap.c xorriso/xorriso.c xorriso/xorrisoburn.c Removed MULTI construction site remarks ------------------------------------ cycle - xorriso-0.2.1 - 2008.07.17.184520 2008.07.18.120001 [1944] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt 18 Jul 2008 [1946] README xorriso/README Corrected outdated statement about minimum library requirements ---------------------------------- release - xorriso-0.2.2 - 2008.07.18.120001 * New API function isoburn_prepare_blind_grow() * New option -grow_blindly * Options -C and -M for -as mkisofs emulation * Can serve growisofs if started as xorrisofs, genisofs, mkisofs, genisoimage * make install creates aliases as symbolic links: osirrox, xorrisofs, xorrecord * Options for -as cdrecord emulation: -multi, -msinfo, -isosize, tsize, --grow_overwriteable_iso, write_start_address, * New option -pacifier, more compatible pacifier with -as mkisofs 2008.07.18.135540 [1947] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/changelog.txt Version leap to 0.2.3 ------------------------------------ cycle - xorriso-0.2.3 - 2008.07.18.135540 2008.07.19.113048 [1949] Makefile.am xorriso/compile_xorriso.sh Introduced automatic buildstamp generation for dynamic library version 2008.07.21.155324 [1950] Makefile.am xorriso/xorriso_makefile_am.txt Reacting on ticket 138 by stick, revoked buildstamp due to ugly make install 2008.07.21.161826 [1951] Makefile.am xorriso/xorriso_makefile_am.txt Used quotation marks as does ./bootstrap when generating Makefile.in ------------------------------------ cycle - xorriso-0.2.3 - 2008.07.21.161826 * Bug fix: Variable DESTDIR was not properly respected during make install 2008.07.23.080001 [xorriso-0.2.2.pl01] Makefile.in xorriso/xorriso_timestamp.h xorriso/changelog.txt * Bug fix: external make variable DESTDIR was not used by xorriso link creation ------------------------------- patch - xorriso-0.2.2.pl01 - 2008.07.23.080001 * Bug fix: Variable DESTDIR was not properly respected during make install 2008.07.26.075027 [1953] libisoburn/burn_wrap.c Producing on overwriteables a partial TOC up to first damaged superblock 2008.08.01.101355 [1955] xorriso/xorrisoburn.c Better finish time estimation with -pacifier mkisofs 2008.08.01.141210 [1956] xorriso/xorrisoburn.c Better finish time estimation with -pacifier mkisofs 2008.08.06.143825 [1967] xorriso/xorriso_private.h Removed obsolete type definition 2008.08.06.143922 [1968] xorriso/xorrisoburn.c Removed obsolete function 2008.08.07.112529 [1972] xorriso/xorriso.c Minor changes around display of time and byte counts 2008.08.09.160515 [1977] configure.ac libisoburn/libisoburn.h Demanding libburn-0.5.1 now 2008.08.09.160947 [1978] libisoburn/burn_wrap.c Create emulated toc entry for simple ISO session on overwriteable media 2008.08.09.161311 [1979] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Evaluating readability of media 2008.08.11.201604 [1980] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Adapted media evaluation to CD peculiarities 2008.08.13.185434 [1981] libisoburn/libisoburn.h Corrected documentation of isoburn_disc_get_msc1() 2008.08.13.190704 [1982] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c New capability to redirect program output used for toc in sector map file 2008.08.14.220520 [1983] libisoburn/burn_wrap.c Changed wrong update message from MB to kB 2008.08.14.221412 [1984] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -check_media 2008.08.15.102849 [1985] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c Allowed independent redirecton of result and info channel 2008.08.15.155421 [1986] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 Enabled printing of sector bitmaps by use=sector_map 15 Aug 2008 [1987] xorriso/xorriso_eng.html Mentioned progress of development version 2008.08.17.220043 [1988] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 -find tests -damaged and -lba_range, new -find action report_damage 2008.08.17.221350 [1989] xorriso/xorrisoburn.c xorriso/xorriso_eng.html Reacted on compiler warning ------------------------------------ cycle - xorriso-0.2.3 - * Included libburn has enhanced Linux drive access and listing code * New option -check_media * New -find test -damaged, new -find action "report_damage" 2008.08.18.134140 [1990] xorriso/xorrisoburn.c xorriso/README xorriso/xorriso.1 New -find action report_lba 2008.08.20.101410 [2000] xorriso/configure_ac.txt Adopted to new libburn version 0.5.3 2008.08.20.121008 [2002] libisoburn/libisoburn.h configure.ac Demanding libburn-0.5.2 now 2008.08.20.181734 [2003] xorriso/xorrisoburn.c Made -check_media process first 32 blocks of a multi-session overwriteable 2008.08.21.070602 [2004] xorriso/xorriso.c Synced -lsl display of major,minor numbers with /usr/include/sys/sysmacros.h 2008.08.22.231051 [2005] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New -check_media option -patch_lba0= 2008.08.24.100552 [2007] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/xorriso_eng.html New -error_behavior behavior occasion "file_extraction" 2008.08.24.124424 [2008] xorriso/xorrisoburn.c Fixed a bug with -check_media use=outdev 2008.08.24.125257 [2009] xorriso/xorrisoburn.c Reacted on compiler warning ------------------------------------ cycle - xorriso-0.2.3 - 2008.08.24.125257 * New -find action "report_lba" * New -error_behavior behavior occasion "file_extraction" 2008.08.24.160001 [2011] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.2.4 24 Aug 2008 [2012] xorriso/changelog.txt Documented changes and release timestamp 24 Aug 2008 [2013] configure.ac Corrected ISOBURN_MICRO_VERSION ---------------------------------- release - xorriso-0.2.4 - 2008.08.24.160001 * Included libburn has enhanced Linux drive access and listing code * New option -check_media * New -find test -damaged, new -find actions "report_damage", "report_lba" * New -error_behavior occasion "file_extraction" 2008.08.24.173217 [2014] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.2.5 24 Aug 2008 [2015] xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-0.2.5 - 2008.08.24.173217 26 Aug 2008 [2017] xorriso/convert_man_to_html.sh Adjusted HTML generator to recent man page changes 2008.08.26.163254 [2018] libisoburn/libisoburn.h libisoburn/data_source.c xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/configure_ac.txt Adopting next development step of libisofs 26 Aug 2008 [2019] xorriso/xorriso.1 Adjusted man page statements about file size limits 2008.08.27.121211 [2020] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -file_size_limit, -as mkisofs now supports -iso-level 1 to 3 2008.08.27.122127 [2021] xorriso/xorriso.c Reacted on compiler warning ------------------------------------ cycle - xorriso-0.2.5 - 2008.08.27.122127 * Capability to insert and extract files far larger than 4 GB * New option -file_size_limit, -as mkisofs now supports -iso-level 1 to 3 27 Aug 2008 [2022] xorriso/xorriso.1 Corrected a typo in man page 2008.09.02.164803 [2025] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -extract_cut 2008.09.03.143218 [2026] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New -error_behavior "file_extraction" behavior "best_effort" 2008.09.04.100158 [2027] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -check_media_defaults ------------------------------------ cycle - xorriso-0.2.5 - 2008.09.04.100158 * New option -extract_cut * New -error_behavior "file_extraction" behavior "best_effort" * New option -check_media_defaults 2008.09.05.095344 [2028] xorriso/xorriso_private.h xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -list_delimiter 5 Sep 2008 [2029] xorriso/xorriso_eng.html Updated development feature list 2008.09.05.114936 [2030] xorriso/xorriso.c -commit_eject, -alter_date, -alter_date_r, -pacifier, -prog_help had wrong argument count prediction 5 Sep 2008 [2031] xorriso/xorriso.1 Documented forgotten option -publisher 5 Sep 2008 [2032] xorriso/xorriso_eng.html xorriso/xorriso.1 Minor documentation polishing ------------------------------------ cycle - xorriso-0.2.5 - 2008.09.05.114936 * New option -list_delimiter 6 Sep 2008 [2033] xorriso/xorriso_eng.html xorriso/xorriso.1 Documentation polishing 2008.09.07.144714 [2034] xorriso/xorrisoburn.c Removed outdated alternative code for support of libisofs-0.6.6 2008.09.08.094748 [2035] xorriso/xorriso.c xorriso/xorrisoburn.c Made use of new libisofs call iso_image_update_sizes() 2008.09.08.121548 [2036] xorriso/xorrisoburn.c Bug fix: -format full did not re-format already formatted DVD+RW 2008.09.09.082406 [2037] xorriso/xorrisoburn.c Smoothened time estimation with pacifier mkisofs style 9 Sep 2008 [2038] xorriso/xorriso_eng.html Updated web page ------------------------------------ cycle - xorriso-0.2.5 - 2008.09.09.082406 * Bug fix: -format full did not re-format already formatted DVD+RW 2008.09.11.114227 [2040] xorriso/xorrisoburn.c Issuing message at begin of image loading 2008.09.13.204455 [2047] xorriso/xorrisoburn.c Correction about -check_media report and message about image loading 2008.09.16.060427 [2053] xorriso/xorriso.c xorriso/xorrisoburn.c Corrected pacifier text (Ticket 141) 2008.09.16.185206 [2054] xorriso/xorriso_private.h xorriso/xorriso.1 Corrected falsely computed default setting of -file_size_limit 2008.09.17.193824 [2056] xorriso/xorrisoburn.c Corrected message duplication about emulated sessions on overwriteable media 2008.09.19.090619 [2057] [2058] xorriso/configure_ac.txt standalone version switch to libisofs-0.6.8 19 Sep 2008 [2059] xorriso/xorriso.1 Small change in man page 2008.09.19.104424 [2060] configure.ac libisoburn/libisoburn.h Switched requirements to libisofs-0.6.8 ------------------------------------ cycle - xorriso-0.2.5 - 2008.09.19.104424 2008.09.19.122656 [2061] xorriso/xorrisoburn.c Changed WARNING about non-writeable media to NOTE severity of blank messages 2008.09.19.180001 [2066] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.2.6 20 Sep 2008 [2067] xorriso/changelog.txt Documented changes and release timestamp ---------------------------------- release - xorriso-0.2.6 - 2008.09.19.180001 * Capability to insert and extract files far larger than 4 GB * New option -file_size_limit, -as mkisofs now supports -iso-level 1 to 3 * New option -extract_cut * New -error_behavior "file_extraction" behavior "best_effort" * New option -check_media_defaults * New option -list_delimiter * Bug fix: -format full did not re-format already formatted DVD+RW 2008.09.20.093140 [2068] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.2.7 20 Sep 2008 [2069] xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-0.2.7 - 2008.09.20.093140 2008.09.24.155424 [2077] xorriso/xorrisoburn.c Trying to get pacifiers of simultaneously running emulations into sync 2008.09.26.120934 [2082] xorriso/xorriso.c Bug fix: -as mkisofs -iso-level was accused to be an unknown option 2008.09.26.161331 [2083] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 A first attempt on making bootable ISO images 2008.09.28.112850 [2084] xorriso/configure_ac.txt Standalone version switch to libisofs-0.6.9 (vreixoml 387) to avoid SIGSEGV 2008.09.28.113256 [2085] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/convert_man_to_html.sh xorriso/xorriso_eng.html Made -boot_image isolinux activation obey -overwrite setting 2008.09.30.102243 [2088] libisoburn/libisoburn.h Requiring libisofs-0.6.9 because of bug fixes with El Torito 2008.09.30.102753 [2089] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Adjustments about -boot_image after testing with various media types 30 Sep 2008 [2090] xorriso/xorriso_eng.html Mentioned progress and bug fixes in 0.2.7 ------------------------------------ cycle - xorriso-0.2.7 - 2008.09.30.102753 * Bug fix: -as mkisofs -iso-level was accused to be an unknown option * Ability to write and maintain bootable ISO images based on ISOLINUX 2008.09.30.174925 [2091] xorriso/xorrisoburn.c xorriso/xorriso.1 Small adjustments about -boot_image 2008.10.02.092354 [2092] xorriso/xorrisoburn.c xorriso/xorriso.1 Small adjustments about -boot_image 2008.10.02.092635 [2093] xorriso/xorriso.c Enabled -as cdrecord blank=as_needed 2008.10.02.105442 [2094] xorriso/README xorriso/configure_ac.txt Introduced xorriso-standalone ./configure option --disable-libreadline 2008.10.02.110828 [2095] README xorriso/README xorriso/configure_ac.txt Fine tuning about ./configure option --disable-libreadline ------------------------------------ cycle - xorriso-0.2.7 - 2008.10.02.110828 * New ./configure option --disable-libreadline to make binary more portable 4 Oct 2008 [2099] xorriso/convert_man_to_html.sh xorriso/xorriso.1 Relocated the El Torito paragraph in man xorriso 2008.10.05.075432 [2101] libisoburn/libisoburn.h libisoburn/burn_wrap.c xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 -rom_toc_scan nonrom_off disables toc emulation on overwriteables 2008.10.05.093703 [2105] xorriso/xorrisoburn.c Bug fix: Random target filenames with looping symbolic links 2008.10.05.125046 [2109] xorriso/configure_ac.txt Adapted standalone BURN_*_VERSION to libburn 0.5.5 2008.10.05.125242 [2110] configure.ac libisoburn/libisoburn.h libisoburn now demands libburn-0.5.4 ------------------------------------ cycle - xorriso-0.2.7 - 2008.10.05.125242 * Bug fix: -follow link attributed random target filenames to looping links 2008.10.06.114114 [2113] xorriso/configure_ac.txt Adapted standalone LIBISOFS_*_VERSION to libisofs 0.6.10 2008.10.06.114845 [2114] configure.ac libisoburn/libisoburn.h libisoburn now demands libisofs-0.6.10 ------------------------------------ cycle - xorriso-0.2.7 - 2008.10.06.114845 7 Oct 2008 [2115] xorriso/xorriso.1 Polished man xorriso 2008.10.08.135848 [2116] xorriso/xorriso.c xorriso/xorriso.1 Changed newly introduced -rom_toc_scan nonrom_off to off:emul_off 2008.10.09.145926 [2117] configure.ac README Introduced libburn ./configure option --disable-libreadline 9 Oct 2008 [2118] xorriso/compile_xorriso.sh Introduced xorriso development compiler script option -no_libreadline ------------------------------------ cycle - xorriso-0.2.7 - 2008.10.09.145926 2008.10.10.131102 [2119] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorriso.1 Enabled dialog for multi-lines and newline characters in quotation marks 2008.10.10.134020 [2120] xorriso/xorriso.c Polishing multi-line dialog 2008.10.12.120001 [2122] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.2.8 12 Oct 2008 [2123] xorriso/changelog.txt Documented changes and release timestamp ---------------------------------- release - xorriso-0.2.8 - 2008.10.12.120001 * Bug fix: -as mkisofs -iso-level was accused to be an unknown option * Bug fix: -follow link attributed random target filenames to looping links * Ability to write and maintain bootable ISO images based on ISOLINUX * New ./configure option --disable-libreadline to make binary more portable 2008.10.12.133957 [2124] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.2.9 12 Oct 2008 [2125] xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-0.2.9 - 2008.10.12.133957 15 Oct 2008 [2128] xorriso/xorriso_eng.html Corrected a typo in xorriso web page 2008.10.15.182605 [2130] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New options -quoted_path_list, -quoted_not_list. Multiline for -options_from_file 2008.10.17.074953 [2131] xorriso/xorriso_private.h xorriso/xorriso.h xorriso/xorriso.c xorriso/xorriso.1 New option -backslash_codes for expressing weird file names 2008.10.17.123308 [2132] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorriso.1 Adjustments and documentation about -backslash_codes 17 Oct 2008 [2133] xorriso/xorriso_eng.html Updated xorriso web page ------------------------------------ cycle - xorriso-0.2.9 - 2008.10.17.123308 * New options -quoted_path_list, -quoted_not_list * New option -backslash_codes for weird file names and terminal safety 18 Oct 2008 [2134] xorriso/make_isohybrid_mbr.c Exposed function make_isohybrid_mbr() for discussion 19 Oct 2008 [2135] xorriso/make_isohybrid_mbr.c Corrected a bug in make_isohybrid_mbr() 2008.10.19.172237 [2136] xorriso/configure_ac.txt xorriso/xorriso_makefile_am.txt Adapted to libisofs 0.6.11 19 Oct 2008 [2137] xorriso/compile_xorriso.sh Adapted to libisofs 0.6.11 19 Oct 2008 [2138] - xorriso/make_isohybrid_mbr.c The make_isohybrid_mbr() function is now part of libisofs development 2008.10.19.182512 [2139] libisoburn/libisoburn.h libisoburn now depends on libisofs-0.6.11 2008.10.20.180747 [2140] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Made use of new libisofs isohybrid capability ------------------------------------ cycle - xorriso-0.2.9 - 2008.10.20.180747 * Suitable ISOLINUX boot images are made alternatively bootable via MBR 21 Oct 2008 [2141] xorriso/xorriso_eng.html Mentioned isohybrid capability in xorriso homepage 2008.10.21.112523 [2142] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Implemented in xorriso provisory 1 MB alignement for MBR enhanced ISO images 2008.10.21.123226 [2143] xorriso/xorrisoburn.c Showing a "Media summary:" with blank media ------------------------------------ cycle - xorriso-0.2.9 - 2008.10.21.123226 2008.10.22.110204 [2144] xorriso/xorriso.c xorriso/xorriso.1 New options --quoted_path_list , isolinux_mbr= for -as mkisofs 2008.10.22.201702 [2145] xorriso/xorriso.1 xorriso/xorrisoburn.c Gave up automatic switch to -boot_image "isolinux" "patch" after writing ------------------------------------ cycle - xorriso-0.2.9 - 2008.10.22.201702 2008.10.24.130124 [2146] xorriso/xorrisoburn.c Fixed image size aligment to 1 MB with follow-up sessions ------------------------------------ cycle - xorriso-0.2.9 - 2008.10.24.130124 2008.10.25.123133 [2147] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Eventually reporting boot info with TOC of -indev, redirected drive aquiration TOC to info channel 2008.10.25.170010 [2148] xorriso/xorrisoburn.c Fixed SIGSEGV introduced with previous revision 2008.10.26.134421 [2149] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 Introduced info option -boot_image "any" "show_status" 2008.10.26.142422 [2150] xorriso/xorrisoburn.c Precautions against patching deleted boot images ------------------------------------ cycle - xorriso-0.2.9 - 2008.10.26.142422 2008.10.27.142720 [2151] xorriso/xorrisoburn.c Trying to recognize isohybrid MBR for "Boot record :" message ------------------------------------ cycle - xorriso-0.2.9 - 2008.10.27.142720 2008.10.30.110049 [2152] xorriso/xorriso.c Made -as mkisofs -path-list and --quoted_path_list switch pacifier mode 2008.10.30.114313 [2153] xorriso/xorriso.c Enabled @ as indicator of UTC seconds in timestrings 2008.11.03.115721 [2159] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/convert_man_to_html.sh New options -charset, -in_charset, -out_charset 2008.11.04.162214 [2162] xorriso/xorriso_private.h xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -application_id 4 Nov 2008 [2163] xorriso/xorriso_eng.html Mentioned new options in xorriso homepage 2008.11.06.183736 [2164] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -local_charset ------------------------------------ cycle - xorriso-0.2.9 - 2008.11.06.183736 * New options -charset, -in_charset, -out_charset * New option -application_id * New option -local_charset 2008.11.07.173315 [2170] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Copying -out_charset to -in_charset after -commit 2008.11.07.201909 [2171] xorriso/xorrisoburn.c Removed some defunct code from xorrisoburn.c 2008.11.09.102554 [2176] xorriso/xorriso.c Added a setlocale() call to xorriso main() ------------------------------------ cycle - xorriso-0.2.9 - 2008.11.09.102554 * Bug fix in libisofs: ISOLINUX boot images were not patched correctly 2008.11.10.123332 [2177] xorriso/xorrisoburn.c Disabled experimental weight sorting with MBR generation 2008.11.10.123713 [2178] xorriso/xorriso.c xorriso/xorriso.1 Eventual backslash output conversion outside quotes for more terminal-safety 11 Nov 2008 [2179] xorriso/xorriso_eng.html Updated xorriso home page ------------------------------------ cycle - xorriso-0.2.9 - 2008.11.10.123713 12 Nov 2008 [2190] xorriso/configure_ac.txt Version leap to libburn-0.5.7 2008.11.12.153437 [2192] xorriso/xorriso.c Added forgotten exit value registration to -return_with. Thanks to Steve Dodd. ------------------------------------ cycle - xorriso-0.2.9 - 2008.11.12.153437 * Bug fix: Forgot exit value registration to -return_with. Thanks to Steve Dodd. 15 Nov 2008 [2195] xorriso/xorriso.1 Took into respect that xorriso loads non-RR images 2008.11.15.133724 [2196] xorriso/xorriso.c Adaptions to FreeBSD. Thanks to J.R. Oldroyd. 2008.11.15.231831 [2198] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c Introduced new relax flag isoburn_igopt_always_gmt 2008.11.15.232058 [2199] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Making use of new relax flag isoburn_igopt_always_gmt ------------------------------------ cycle - xorriso-0.2.9 - 2008.11.18.110100 18 Nov 2008 [2204] test/aaip.c Demo of the emerging Arbitrary Attribute Interchange Protocol format 18 Nov 2008 [2205] README xorriso/README xorriso/xorriso.1 xorriso/xorriso_eng.html Mentioned FreeBSD peculiarities in our docs 2008.11.21.160019 [2206] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c Making use of experimental libisofs calls _rrip_version_1_10() _dir_rec_mtime() 2008.11.21.160632 [2207] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/xorriso_eng.html New option -relax_compliance 22 Nov 2008 [2209] - test/aaip.c Revoked proposal AAIP 0.0 in favor of 0.2 which is nearly like RRIP field SL ------------------------------------ cycle - xorriso-0.2.9 - 2008.11.21.160632 26 Nov 2008 [2210] xorriso/configure_ac.txt Promoted standalone libisofs copy to 0.6.12 2008.11.26.174940 [2211] xorriso/xorrisoburn.c Disabled "Starting up libraries ..." message 2008.11.26.180935 [2212] xorriso/xorriso.c Augmented version message by "and burn program" ------------------------------------ cycle - xorriso-0.2.9 - 2008.11.26.180935 2008.11.29.091030 [2216] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Renamed new option -relax_compliance to -compliance, added _off rules ------------------------------------ cycle - xorriso-0.2.9 - 2008.11.29.091030 * New option -compliance allows certain deviations from standards 2008.11.29.140837 [2219] xorriso/xorrisoburn.c Bug fix: -format as_needed did not recognize unformatted BD-RE 2008.11.29.165843 [2220] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Re-aquiring outdev after formatting and blanking already in xorrisoburn 2008.11.29.214208 [2221] xorriso/xorriso.c New -as cdrecord option stream_recording=on|off 2008.11.30.095007 [2222] xorriso/xorrisoburn.c Reporting BD speed units with pacifier rather than DVD units 2008.11.30.123700 [2223] xorriso/xorriso.c Gave -as mkisofs problem handlers rather than to let it abort on any error 2008.11.30.214121 [2224] xorriso/xorriso.c xorriso/xorrisoburn.c Changed layout of xorriso style write pacifier line 2008.12.01.110022 [2225] xorriso/xorriso.c xorriso/xorrisoburn.c Removed outdated macro case Xorriso_fat_local_meM 2008.12.01.122825 [2226] xorriso/xorriso.c Bug fix: disk patterns with relative addresses were not properly resolved 1 Dec 2008 [2227] xorriso/xorriso_eng.html Updated list of bugfixes in xorriso home page ------------------------------------ cycle - xorriso-0.2.9 - * Bug fix: -format as_needed did not recognize unformatted BD-RE * Bug fix: disk patterns with relative addresses were not properly resolved 1 Dec 2008 [2228] Branching for libisoburn release 0.3.0 2008.12.01.200001 [2229] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.3.0 1 Dec 2008 [2230] xorriso/changelog.txt Documented changes and release timestamp 1 Dec 2008 [2231] configure.ac Corrected values of LIB*_REQUIRED ---------------------------------- release - xorriso-0.3.0 - 2008.12.01.200001 * New options -quoted_path_list, -quoted_not_list * New option -backslash_codes for weird file names and terminal safety * New options -charset, -in_charset, -out_charset * New option -local_charset allows to override locale * New option -application_id * New option -compliance allows certain deviations from standards * Suitable ISOLINUX boot images are made alternatively bootable via an MBR * Bug fix in libisofs: ISOLINUX boot images were not patched correctly * Bug fix in libisofs: mkisofs images with empty files got damaged * Bug fix: Forgot exit value registration to -return_with. Thanks to Steve Dodd. * Bug fix: -format as_needed did not recognize unformatted BD-RE * Bug fix: disk patterns with relative addresses were not properly resolved 2008.12.01.202911 [2232] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.3.1 1 Dec 2008 [2233] xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-0.3.1 - 2008.12.01.202911 2 Dec 2008 [2234] Promoted libisoburn 0.3.0 from branch to tag 2 Dec 2008 [2235] [2236] [2237] [2238] Deleted obsoleted branches 2008.12.04.175459 [2240] [2241] xorriso/xorriso.c Bug fix: Options -extract and -extract_single were enabled with -osirrox off 2008.12.05.171005 [2242] libisoburn/libisoburn.h libisoburn/burn_wrap.c New API function isoburn_get_mount_params() 2008.12.05.171700 [2243] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New options -mount and -mount_cmd 2008.12.06.140828 [2244] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso_eng.html Using uname() rather than #ifdef __FreeBSD__ 2008.12.07.122439 [2245] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Got rid of call system() in Xorriso_mount(), new option -session_string Dec 7 2008 [2246] [2247] xorriso/xorriso_eng.html xorriso/xorriso.1 Small documentation adjustments ------------------------------------ cycle - xorriso-0.3.1 - 2008.12.07.122439 * Bug fix: Options -extract and -extract_single were enabled with -osirrox off * New API function isoburn_get_mount_params() * New options -mount , -mount_cmd , -session_string 2008.12.08.120712 [2256] xorriso/configure_ac.txt Standalone version switch to libburn-0.5.9 2008.12.08.121106 [2257] README configure.ac libisoburn/libisoburn.h xorriso/README Requiring at least libburn-0.5.8 8 Dec 2008 [2258] xorriso/xorriso.1 Small documentation adjustments 2008.12.08.154521 [2259] Makefile.am xorriso/xorriso_private.h xorriso/compile_xorriso.sh xorriso/xorriso_makefile_am.txt Got rid of Xorriso_with_regeX 2008.12.10.093424 [2272] xorriso/xorrisoburn.c Enabled formatting of BD-R media to SRM-POW default size 10 Dec 2008 [2275] xorriso/xorriso.1 Mentioned BD-R in man xorriso 2008.12.11.072427 [2277] xorriso/xorrisoburn.c Proper message after failed or refused formatting 2008.12.13.142726 [2281] [2282] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New -format modes by_size_ and fast_by_size_ 2008.12.13.144622 [2283] configure.ac libisoburn/libisoburn.h Demanding libburn-0.5.9 now ------------------------------------ cycle - xorriso-0.3.1 - 2008.12.13.144622 * By using libburn-0.5.9: support for BD-R media * New -format modes by_size_ and fast_by_size_ 2008.12.13.161650 [2285] xorriso/xorrisoburn.c xorriso/xorriso.1 Revoked -format mode by_size for DVD-RW 12 Dec 2008 [2286] xorriso/xorriso_eng.html Updated xorriso web page 2008.12.14.093125 [2288] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -assert_volid 2008.12.14.151550 [2291] xorriso/xorriso.c Removed outdated alternative code 2008.12.14.231145 [2293] xorriso/xorriso.c xorriso/xorriso.1 Options for -as mkisofs: -input-charset, -output-charset 2008.12.16.130139 [2295] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -drive_class for safety management of pseudo-drive access 16 Dec 2008 [2296] xorriso/README xorriso/xorriso_eng.html Updated xorriso documentation ------------------------------------ cycle - xorriso-0.3.1 - 2008.12.16.130139 * New option -assert_volid * New option -drive_class for safety management of pseudo-drive access 17 Dec 2008 [2299] + doc/comments Preparations for a more presentable online API documentation 2008.12.17.102216 [2300] Makefile.am Delivering doxygen input for API documentation with releases 17 Dec 2008 [2301] doc/comments Corrected list of authors in libisoburn API documentation 2008.12.21.101705 [2317] xorriso/xorrisoburn.c Corrected message about non-matching volume id ------------------------------------ cycle - xorriso-0.3.1 - 2008.12.21.154636 21 Dec 2008 [2318] + test/aaip_0_2.h + test/aaip_0_2.c + test/aaip_0_2_test.c A first implementation of AAIP 0.2 encoding and decoding 23 Dec 2008 [2326] test/aaip_0_2.h test/aaip_0_2.c test/aaip_0_2_test.c Gave aaip_0_2 a ring buffer rather than a shifted fifo 25 Dec 2008 [2336] test/aaip_0_2.h test/aaip_0_2.c test/aaip_0_2_test.c Encoder for ACL long text form 26 Dec 2008 [2338] test/aaip_0_2.h test/aaip_0_2.c test/aaip_0_2_test.c Decoder for ACL to long text form 1 Jan 2009 [2343] test/aaip_0_2.h test/aaip_0_2.c test/aaip_0_2_test.c + test/aaip-os-linux.c + test/aaip-os-freebsd.c + doc/susp_aaip_0_2.txt Introduced system adapters for getting and setting EA and ACL 1 Jan 2009 [2344] test/aaip_0_2.c Corrected some bugs with attribute list decoding 1 Jan 2009 [2345] test/aaip_0_2.c Corrected some more bug with attribute list decoding 4 Jan 2009 [2355] xorriso/configure_ac.txt Standalone version switch to libburn-0.6.1 5 Jan 2009 [2357] svn copy -m "Branching for libisoburn release 0.3.2 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroThreeTwo 2009.01.05.120643 [2358] COPYRIGHT libisoburn/burn_wrap.c libisoburn/data_source.c libisoburn/isoburn.c libisoburn/isofs_wrap.c xorriso/compile_xorriso.sh xorriso/xorrisoburn.c xorriso/xorriso.h Updated copyright marks to 2009 2009.01.05.123001 [2359] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.3.2 5 Jan 2009 [2360] [2361] xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.3.2 - 2009.01.05.123001 * Bug fix: Options -extract and -extract_single were enabled with -osirrox off * New API function isoburn_get_mount_params() * New options -mount , -mount_cmd , -session_string * New -format modes by_size_ and fast_by_size_ * New option -assert_volid * New option -drive_class for safety management of pseudo-drive access * By using libburn-0.6.0: support for BD-R media 2009.01.05.145703 [2362] COPYRIGHT libisoburn/burn_wrap.c libisoburn/data_source.c libisoburn/isoburn.c libisoburn/isofs_wrap.c xorriso/compile_xorriso.sh xorriso/xorrisoburn.c xorriso/xorriso.h Updated copyright marks to 2009 2009.01.05.153105 [2363] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_timestamp.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt Version leap to 0.3.3 5 Jan 2009 [2364] xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-0.3.3 - 2009.01.05.153105 5 Jan 2009 [2365] svn move http://svn.libburnia-project.org/libisoburn/branches/ZeroThreeTwo http://svn.libburnia-project.org/libisoburn/tags/ZeroThreeTwo Promoted branch to tag 2009.01.06.123047 [2368] xorriso/xorriso.c xorriso/xorriso_eng.html Some small corrections of documentation 9 Jan 2009 [2382] xorriso/xorriso_eng.html Mentioned bug fix and pl01 ------------------------------------ cycle - xorriso-0.3.3 - 2009.01.09.103251 2009.01.10.154018 [2386] xorriso/xorriso.h xorriso/xorriso.c Avoiding use of function parameter name "class" 14 Jan 2009 [2395] doc/susp_aaip_0_2.txt Clarified that AAIP is only allowed if RRIP is present 2009.01.14.110935 [2396] Makefile.am xorriso/compile_xorriso.sh xorriso/xorriso_makefile_am.txt Introduced AAIP code. Now linking with libacl. 2009.01.17.181500 [2400] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c New option -getfacl 2009.01.18.213952 [2401] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Restoring ACLs with -extract and others 2009.01.21.150243 [2402] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c New API macro isoburn_ropt_noaaip controls enabling of AAIP loading 2009.01.21.203852 [2403] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c libisoburn/burn_wrap.c Had to split isoburn_ropt_noaaip into isoburn_ropt_noacl and isoburn_ropt_noea 2009.01.21.204513 [2404] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c New option -acl enables ACL loading 2009.01.22.130107 [2405] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c Now it is isoburn_ropt_noaaip , isoburn_ropt_noacl and isoburn_ropt_noea 2009.01.22.130255 [2406] xorriso/xorrisoburn.c Adapted to new macro situation in libisoburn 2009.01.22.143210 [2407] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New macro isoburn_igopt_aaip controls writing of AAIP info into images 2009.01.22.143253 [2408] xorriso/xorrisoburn.c Making use of new libisoburn macro isoburn_igopt_aaip 2009.01.22.152252 [2409] xorriso/xorrisoburn.c xorriso/xorriso.1 Restoring ACL only if -acl "on" 2009.01.22.155049 [2410] xorriso/xorrisoburn.c Removing leading slash from -getfacl output of file path ------------------------------------ cycle - xorriso-0.3.3 - 2009.01.22.155049 * New option -acl enables ACL import and export 2009.01.23.101305 [2411] xorriso/configure_ac.txt Promoted standalone libisofs to 0.6.13 2009.01.23.102843 [2412] libisoburn/libisoburn.h libisoburn/burn_wrap.c Demanding libisofs 0.6.13 now 2009.01.23.140824 [2413] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New find tests -has_acl, -has_no_acl, new action getfacl, new option getfacl_r 23 Jan 2009 [2414] xorriso/xorriso_eng.html Updated xorriso web page ------------------------------------ cycle - xorriso-0.3.3 - 2009.01.23.140824 * New find tests -has_acl, -has_no_acl , new find action getfacl * New option -getfacl_r 2009.01.23.172652 [2415] configure.ac Makefile.am xorriso/configure_ac.txt xorriso/xorriso_makefile_am.txt xorriso compilation detects availability of libacl and of Linux listxattr call 2009.01.23.172757 [2416] xorriso/xorrisoburn.c Silenced a FAILURE message with -acl if no image is loaded 23 Jan 2009 [2417] xorriso/xorriso_eng.html Updated xorriso web page ------------------------------------ cycle - xorriso-0.3.3 - 2009.01.23.172757 24 Jan 2009 [2418] xorriso/compile_xorriso.sh Took into respect changed .o file names of libisofs 2009.01.25.141124 [2424] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/xorriso_eng.html New option -setfacl, -setfacl_r, new -find action setfacl ------------------------------------ cycle - xorriso-0.3.3 - 2009.01.25.141124 * New option -setfacl, -setfacl_r, new -find action setfacl 25 Jan 2009 [2425] - test/aaip_0_2.h - test/aaip_0_2.c - test/aaip-os-freebsd.c - test/aaip-os-linux.c - test/aaip_0_2_test.c - doc/susp_aaip_0_2.txt AAIP code now resides in libisofs 2009.01.26.173254 [2426] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New macro isoburn_igopt_aaip_susp_1_10 controls writing of AAIP ER and ES 2009.01.26.173606 [2427] xorriso/xorrisoburn.c xorriso/xorriso.1 Gave user control over isoburn_igopt_aaip_susp_1_10 26 Jan 2009 [2428] xorriso/make_xorriso_standalone.sh Including doc/susp_aaip_0_2.txt in xorriso-standalone 2009.01.27.121417 [2432] libisoburn/libisoburn.h Mentioned the need for 64 bit file i/o 2009.01.28.074917 [2434] xorriso/xorrisoburn.c Bug fixes and enhancements about "default" ACL 2009.01.28.114755 [2435] xorriso/xorrisoburn.c Bug fixes and enhancements about "default" ACL ------------------------------------ cycle - xorriso-0.3.3 - 2009.01.28.114755 2009.01.28.190140 [1436] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Made -getfacl file name escaping more similar to shell command behavior 2009.01.29.165339 [2437] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorriso.1 New option -setfacl_list 28 Jan 2009 [2438] xorriso/xorriso_eng.html Updated xorriso web page ------------------------------------ cycle - xorriso-0.3.3 - 2009.01.29.165339 * New option -setfacl_list 29 Jan 2009 [2439] xorriso/xorriso_makefile_am.txt Added aaip-os-dummy.c to xorriso-standalone 2009.01.29.210606 [2440] xorriso/xorrisoburn.c Bug fix: included sys/wait.h rather than wrong wait.h 2009.01.30.145624 [2442] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Followed changes in iso_node_*acl_text API ------------------------------------ cycle - xorriso-0.3.3 - 2009.01.30.200825 2009.01.31.101122 [2443] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Equipped output of lsl with '+' perm if ACL is present 2009.02.02.134346 [2444] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New options -xattr, -getfattr, find -has_xattr, -has_aaip, -exec getfattr 2009.02.02.201124 [2445] xorriso/xorrisoburn.c xorriso/xorriso.1 Enabled restoring of xattr ------------------------------------ cycle - xorriso-0.3.3 - 2009.02.02.201124 * New options -xattr, -getfattr, find -has_xattr, -has_aaip, -exec getfattr 2009.02.03.162440 [2446] Makefile.am xorriso/xorriso_makefile_am.txt Linking with $LIBBURN_ARCH_LIBS to get -lcam on FreeBSD ------------------------------------ cycle - xorriso-0.3.3 - 2009.02.03.162440 2009.02.04.200055 [2449] xorriso/xorrisoburn.c Took into respect eventual resolution of symbolic links 2009.02.04.200653 [2450] configure.ac xorriso/configure_ac.txt Checking for iconv(3) in separate libiconv (e.g. on FreeBSD) 2009.02.07.090104 [2453] xorriso/xorrisoburn.c Correct group permission bits with -acl off and -extract file that has ACL 2009.02.07.142605 [2454] xorriso/xorrisoburn.c Correct group permission bits with -acl off and disk file that has ACL 2009.02.08.132116 [2455] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h First code for -setfattr (have to fix some ACL problems before going on) 2009.02.08.151354 [2456] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Displaying "+" with lslx if ACL is detected ------------------------------------ cycle - xorriso-0.3.3 - 2009.02.08.151354 ------------------------------------ cycle - xorriso-0.3.3 - 2009.02.09.103308 ------------------------------------ cycle - xorriso-0.3.3 - 2009.02.09.170857 2009.02.09.185940 [2458] xorriso/xorriso.c xorriso/xorrisoburn.c New options -setfattr, -setfattr_r, new find -exec setfattr 10 Feb 2009 [2459] xorriso/xorriso_eng.html Updated xorriso home page 2009.02.10.125831 [2460] xorriso/xorrisoburn.c Closed a memory leak with unreleased IsoImage in boot image inquiry ------------------------------------ cycle - xorriso-0.3.3 - 2009.02.10.195106 12 Feb 2009 [2461] xorriso/convert_man_to_html.sh Adapted html man page generator to textchanges 2009.02.12.110516 [2462] xorriso/xorriso.h xorriso/xorriso.c New -as mkisofs options --acl and --xattr 12 Feb 2009 [2463] xorriso/xorriso.1 Updated xorriso man page 2009.02.13.202539 [2464] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/xorriso_eng.html New option -setfattr_list ------------------------------------ cycle - xorriso-0.3.3 - 2009.02.13.202539 * New -as mkisofs options --acl and --xattr * New option -setfattr_list 2009.02.14.133013 [2465] xorriso/xorriso.c -as mkisofs --acl and --xattr was not properly recognized 2009.02.16.082645 [2466] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 Took into respect ACL and xattr with -compare and -update ------------------------------------ cycle - xorriso-0.3.3 - 2009.02.16.082645 2009.02.17.184231 [2467] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/xorriso_eng.html New option -disk_dev_ino 2009.02.19.123524 [2475] libisoburn/isofs_wrap.c Adapted to removal of Libburn_with_aaiP macro 2009.02.19.123607 [2476] xorriso/xorrisoburn.c Adapted to removal of Libburn_with_aaiP macro 19 Feb 2009 [2477] xorriso/configure_ac.txt Adapted to version leap libisofs-0.6.14 ------------------------------------ cycle - xorriso-0.3.3 - 2009.02.19.123607 * New option -disk_dev_ino accelerates incremental backups 2009.02.19.183707 [2478] xorriso/xorriso_private.h Committing forgotten xorriso_private.h 20 Feb 2009 [2487] xorriso/configure_ac.txt Adapted to version leap libburn-0.6.2 2009.02.25.144045 [2496] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 -disk_dev_ino mode ino_only 2009.02.28.175747 [2502] configure.ac Requiring libisofs-0.6.14 now, libburn-0.6.0 still suffices 2009.02.28.175926 [2503] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New API function isoburn_igopt_get_data_start() 2009.02.28.181358 [2504] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New -stream_recording mode with start address, "on" is now 32s 2009.02.28.181750 [2505] xorriso/configure_ac.txt Switched standalone version to libisofs-0.6.15 ------------------------------------ cycle - xorriso-0.3.3 - 2009.02.28.181750 01 Mar 2009 [2506] svn copy -m Branching for libisoburn release 0.3.4 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroThreeFour 2009.03.01.103001 [2507] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/xorriso.1 xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to 0.3.4 1 Mar 2009 [2508] xorriso/changelog.txt Documented changes and release timestamp 1 Mar 2009 [2509] README xorriso/README Correction about libisofs.so version requirement ----------------------------------- release - xorriso-0.3.4 - 2009.03.01.103001 * New option -acl enables ACL import and export * New options -getfacl, -getfacl_r, -setfacl, -setfacl_r, -setfacl_list * New find tests -has_acl, -has_no_acl , new find actions getfacl, setfacl * New option -xattr enables import and export of Extended Attributes * New options -getfattr, -getfattr_r, -setfattr, -setfattr_r, -setfattr_list * New find tests -has_xattr, -has_aaip, new find actions getfattr, setfattr * New -as mkisofs options --acl and --xattr * New option -disk_dev_ino accelerates incremental backups 2009.03.01.113444 [2510] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/xorriso.1 xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to 0.3.5 1 Mar 2009 [2511] xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-0.3.5 - 2009.03.01.113444 1 Mar 2009 [2512] svn move -m 'Promoted branch to tag' http://svn.libburnia-project.org/libisoburn/branches/ZeroThreeFour http://svn.libburnia-project.org/libisoburn/tags/ZeroThreeFour 2009.03.03.103421 [2517] libisoburn/libisoburn.h isoburn_igopt_get_data_start() @since 0.3.6 because not announced in 0.3.4 2009.03.03.103706 [2518] xorriso/configure_ac.txt Making optional use of statvfs() in sg-dummy ------------------------------------ cycle - xorriso-0.3.5 - 2009.03.03.103706 2009.03.05.121700 [2519] acinclude.m4 Lifted the ban on operating systems other than Linux and FreeBSD ------------------------------------ cycle - xorriso-0.3.5 - 2009.03.05.121700 * Dummy MMC adapter for compilation on systems other than Linux, FreeBSD 2009.03.08.140002 [2521] xorriso/xorriso.c xorriso/xorrisoburn.c test/compare_file.c Reacted on compiler warnings of SchilliX-0.6.7 (based on Solaris 5.11) 2009.03.10.092227 [2523] xorriso/xorrisoburn.c xorriso/xorriso.1 Made -compliance old_rr imply -compliance aaip_susp_1_10 ------------------------------------ cycle - xorriso-0.3.5 - 2009.03.10.092227 2009.03.11.170125 [2524] configure.ac libisoburn/libisoburn.h libisoburn/isoburn.c Requiring libisofs-0.6.16 now 2009.03.11.170236 [2525] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libisofs-0.6.16 11 Mar 2009 [2526] xorriso/xorriso_eng.html Mentioned bug fixes in libisofs 11 Mar 2009 [2527] xorriso/xorriso.1 Minor changes in xorriso man page ------------------------------------ cycle - xorriso-0.3.5 - 2009.03.11.170236 2009.03.12.141647 [2528] xorriso/xorrisoburn.c xorriso/xorriso.1 Made -compliance old_rr the default 2009.03.13.150731 [2536] xorriso/xorrisoburn.c Defaulting -stream_recording "data" to "100m" 2009.03.13.150838 [2537] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libburn-0.6.5 2009.03.14.113811 [2539] xorriso/xorriso.c Fixed the inappropriate refusal on paths like /u/test/../.. 2009.03.14.115711 [2540] configure.ac libisoburn/libisoburn.h Requiring libburn-0.6.4 now ------------------------------------ cycle - xorriso-0.3.5 - 2009.03.14.115711 * default of -compliance has been changed to "old_rr", new rule "new_rr" 16 Mar 2009 [2541] svn copy -m "Branching for libisoburn release 0.3.6" \ http://svn.libburnia-project.org/libisoburn/trunk \ http://svn.libburnia-project.org/libisoburn/branches/ZeroThreeSix 2009.03.16.090001 [2542] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.3.6 16 Mar 2009 [2543] xorriso/changelog.txt Documented changes and release timestamp 16 Mar 2009 [2544] configure.ac Adjusted forgotten version number ----------------------------------- release - xorriso-0.3.6 - 2009.03.16.090001 * Dummy MMC adapter for compilation on systems other than Linux, FreeBSD * Default of -compliance has been changed to "old_rr", new rule "new_rr" * New -stream_recording modes with start address or "data". "on" is now 32s. 2009.03.16.112837 [2545] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.3.7 16 Mar 2009 [2546] xorriso/changelog.txt Documented changes and release timestamp 17 Mar 2009 [2548] svn move -m 'Promoted branch to tag' \ http://svn.libburnia-project.org/libisoburn/branches/ZeroThreeSix \ http://svn.libburnia-project.org/libisoburn/tags/ZeroThreeSix ------------------------------------ cycle - xorriso-0.3.7 - 2009.03.16.112837 2009.03.18.103858 [2550] configure.ac acinclude.m4 Makefile.am Get on FreeBSD pkgconfigdir=.../libdata , on Linux and others: .../lib 2009.03.18.104037 [2551] xorriso/configure_ac.txt xorriso/xorriso_makefile_am.txt Adapted xorriso-standalone to version leap libisofs-0.6.17 and FreeBSD libdata 18 Mar 2009 [2552] configure.ac Removed useless libisoburn configure options --enable-libacl --enable-xattr 2009.03.19.172806 [2555] xorriso/xorriso.c Unified -status filtering decision 2009.03.20.192910 [2557] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c New API functions isoburn_ropt_[sg]et_auto_incharset() 2009.03.20.193334 [2558] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -auto_charset based on xattr "isofs.cs" ------------------------------------ cycle - xorriso-0.3.7 - 2009.03.21.131424 * New option -auto_charset based on xattr "isofs.cs" 22 Mar 2009 [2559] xorriso/xorriso.1 Clarification about -auto_charset pitfalls 2009.03.22.090232 [2560] xorriso/xorriso.c xorriso/xorrisoburn.c Stuffed memory leaks on -commit and loading of El Torito records 2009.03.29.164336 [2561] configure.ac Revoked revision 2552. Dynamic xorriso did not get linked with -lacl any more. 2009.03.29.164411 [2562] libisoburn/libisoburn.h libisoburn now demands libisofs-0.6.17 2009.03.29.164703 [2563] xorriso/compile_xorriso.sh xorriso/xorriso_makefile_am.txt Adapted to new libisofs feature iso_file_add_external_filter() 2009.03.29.164931 [2564] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 Experimental -find action -exec set_filter with some hardcoded filter commands 2009.04.02.162530 [2565] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/xorriso_eng.html New options -external_filter , -unregister_filter, -set_filter , -set_filter_r ------------------------------------ cycle - xorriso-0.3.7 - 2009.04.02.162530 * New options -external_filter , -unregister_filter, -set_filter , -set_filter_r 2009.04.03.172034 [2566] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Fixed a potential blind spot of 1 second in -disk_dev_ino comparison 2009.04.04.082636 [2567] xorriso/xorrisoburn.c Fixed a bug with the error message about external filters and setuid 2009.04.04.144009 [2568] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -close_filter_list 2009.04.04.144241 [2569] configure.ac Makefile.am README xorriso/configure_ac.txt xorriso/xorriso_makefile_am.txt xorriso/compile_xorriso.sh xorriso/README configure options to control availability of external filters in xorriso 2009.04.05.110534 [2570] xorriso/xorriso.c Reporting number of processed filters with -set_filter and -set_filter_r 2009.04.05.110652 [2571] xorriso/xorrisoburn.c Fixed a bug about detection of failure with -set_filter 2009.04.05.131536 [2572] xorriso/xorriso.c Included sys/select.h as prescribed by POSIX and Linux, FreeBSD man pages 2009.04.05.143043 [2573] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorriso.1 New -osirrox mode "banned" 5 Apr 2009 [2574] xorriso/xorriso_eng.html Updated xorriso web page ------------------------------------ cycle - xorriso-0.3.7 - 2009.04.05.143043 * New option -close_filter_list * New -osirrox mode "banned" 2009.04.06.100802 [2575] xorriso/xorrisoburn.c xorriso/xorriso.1 Suffix rollback with -set_filter --remove-all-filters 2009.04.06.132007 [2576] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New options -show_stream, -show_stream_r, -exec show_stream 2009.04.06.143109 [2577] xorriso/xorrisoburn.c xorriso/xorriso.1 Changed appearance of -show_stream, fixed bug introduced with rev 2575 2009.04.06.161541 [2578] xorriso/xorrisoburn.c Fixed bug introduced with 2575 ------------------------------------ cycle - xorriso-0.3.7 - 2009.04.06.161541 * New options -show_stream, -show_stream_r, new -find -exec show_stream 2009.04.07.120250 [2579] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New -find tests -has_filter, -has_no_filter 2009.04.07.122117 [2580] xorriso/xorriso.c xorriso/xorrisoburn.c Removed some unused old code branches 2009.04.10.102558 [2581] configure.ac Makefile.am xorriso/configure_ac.txt xorriso/xorriso_makefile_am.txt xorriso/compile_xorriso.sh Took into respect optional libz dependency of libisofs 2009.04.10.103130 [2582] xorriso/xorrisoburn.c Introduced builtin filters --zisofs and --zisofs-decode 11 Apr 2009 [2583] xorriso/make_xorriso_standalone.sh Included a copy of doc/zisofs_format.txt in xorriso-standalone 2009.04.11.125331 [2584] xorriso/xorrisoburn.c Adjustments with filter --zisofs-decode 2009.04.11.172644 [2585] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -zisofs, builtin filters --zisofs and --zisofs-decode 12 Apr 2009 [2586] xorriso/xorriso_eng.html Updated xorriso web page ------------------------------------ cycle - xorriso-0.3.7 - 2009.04.12.142252 * New option -zisofs, builtin filters --zisofs and --zisofs-decode 14 Apr 2009 [2588] README xorriso/README Updated README texts of libisoburn and xorriso 2009.04.14.092306 [2589] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New -zisofs option by_magic=on ------------------------------------ cycle - xorriso-0.3.7 - 2009.04.14.092306 2009.04.15.071944 [2590] xorriso/compile_xorriso.sh xorriso/xorriso_makefile_am.txt xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Adopted new libisofs filter module gzip.c, builtin filters --gzip, --gunzip 2009.04.15.185131 [2591] libisoburn/isofs_wrap.c xorriso/xorrisoburn.c Removed dependency on Libisofs_has_auto_input_charseT 2009.04.15.185238 [2592] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.18 now 2009.04.15.185359 [2593] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libisofs-0.6.18 2009.04.16.145617 [2594] xorriso/make_xorriso_standalone.sh xorriso/xorriso_makefile_am.txt Fixed bugs with rarely used make dist 2009.04.18.090616 [2596][2597] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libisofs-0.6.19 ------------------------------------ cycle - xorriso-0.3.7 - 2009.04.18.090616 2009.04.18.120001 [2598] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.3.8 18 Apr 2009 [2599][2600] xorriso/xorriso.1 Last minute correction in xorriso man page 18 Apr 2009 [2601] xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.3.8 - 2009.04.18.120001 * New options -set_filter , -set_filter_r, -find -exec set_filter * New option -zisofs, builtin filters --zisofs , --gzip , --gunzip * New options -external_filter , -unregister_filter, -close_filter_list, * New options -show_stream, -show_stream_r * New -osirrox mode "banned" * New option -auto_charset based on xattr "isofs.cs" 2009.04.18.150555 [2602] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.3.9 18 Apr 2009 [2603] xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-0.3.9 - 2009.04.18.150555 19 Apr 2009 [2608] svn move -m 'Promoted branch to tag' \ http://svn.libburnia-project.org/libisoburn/branches/ZeroThreeEight \ http://svn.libburnia-project.org/libisoburn/tags/ZeroThreeEight 2009.04.21.173600 [2612] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Enhanced -find testing by operators -not, -or, -and, (, ), -if, -else, -endif 2009.04.21.184214 [2613] xorriso/xorriso.c Allowed implicit -and operator with -not, -if and -sub 22 Apr 2009 [2614] xorriso/xorriso_eng.html Updated xorriso web page ------------------------------------ cycle - xorriso-0.3.9 - 2009.04.21.184214 * Operators with option -find : -not, -or, -and, (, ), -if, -then, -else 2009.04.22.173603 [2615] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c New API call isoburn_toc_track_get_emul() 2009.04.22.173648 [2616] xorriso/xorrisoburn.c Made use of new libisoburn call to accelerate option -toc for large TOC 2009.04.23.103301 [2617] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New -find tests -wholename, -prune 2009.04.25.160022 [2618] libisoburn/burn_wrap.c Used isoburn_toc_track_get_emul() in isoburn_set_msc1() 2009.04.25.162210 [2619] xorriso/xorrisoburn.c Moved call of isoburn_set_msgs_submit() to Xorriso_startup_libraries() 2009.04.25.162858 [2620] libisoburn/burn_wrap.c Issueing pacifier messages from within isoburn_set_msc1() ------------------------------------ cycle - xorriso-0.3.9 - 2009.04.25.162858 * New -find tests -wholename, -prune 2009.05.06.173600 [2626] xorriso/xorrisoburn.c Bug fix: SIGSEGV with option -status and no search string 2009.05.06.174222 [2627] xorriso/xorrisoburn.c Bug fix: -load volid did not perform pattern search 2009.05.08.073642 [2638] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libburn-0.6.7 2009.05.09.201241 [2639] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c New API options isoburn_ropt_noino and isoburn_igopt_hardlinks 2009.05.09.201742 [2640] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -hardlinks 2009.05.14.082045 [2641] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Enhanced option -extract_cut for handling filtered files 2009.05.16.165616 [2642] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c LBA sorted processing of -compare_r and -update_r for smoother MMC reading 2009.05.16.165940 [2643] xorriso/xorrisoburn.c Disabled use of iso_tree_add_new_symlink() in favor of iso_tree_add_new_node() 2009.05.17.115101 [2644] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New pseudo test with -find : -sort_lba which is actually an option 2009.05.26.140055 [2647] xorriso/xorrisoburn.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Enabled -hardlinks for options -extract, -extract_l and -cp*x 2009.05.26.142912 [2648] xorriso/xorriso.c Revoked LBA sorted reading with -compare_r. It was ugly. ------------------------------------ cycle - xorriso-0.3.9 - 2009.05.26.172355 Bug fix: SIGSEGV with option -status and no search string Bug fix: -load volid did not perform pattern search New option -hardlinks Improved reading performance with -update_r and -extract 2009.05.29.110831 [2649] xorriso/xorriso.c xorriso/xorriso.1 xorriso/xorriso_eng.html New option -for_backup as shortcut for -acl -xattr -hardlinks 2009.05.29.162300 [2650] xorriso/xorrisoburn.c Avoided unnecessary sorting during -extract 2009.05.30.161808 [2561] xorriso/xorriso.c Avoided unconditional output of -hardlinks with option -status 2009.05.31.100140 [2652] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.20 now 2009.05.31.100315 [2653] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libisofs-0.6.20 2009.05.31.185819 [2654] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libisofs-0.6.21 13 Jun 2009 [2668] xorriso/configure_ac.txt xorriso/xorriso_makefile_am.txt xorriso/make_xorriso_standalone.sh Removed xorriso.pc.in from xorriso-standalone tarball 2009.06.15.121525 [2670] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 With -update_r : detecting hardlink splits and fusions on disk 2009.06.20.063853 [2671] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Wider scope of hardlink reconstruction with extract operations 2009.06.22.112850 [2672] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 Gave up risky direct update of hardlink siblings 2009.06.23.065744 [2673] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c + xorriso/makefile_libxorriso_am.txt Introduced alternative Makefile.am for libxorriso production 2009.06.23.070934 [2674] xorriso/xorriso_private.h Added missing function declaration 2009.06.23.123147 [2675] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c Removed from xorriso main() any dependency on non-API features 2009.06.23.133210 [2676] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c Rough test for compile time and run time libxorriso version mismatches 2009.06.23.164351 [2677] xorriso/xorriso.c xorriso/xorrisoburn.c Deprecated -hardlinks modes "start_update", "end_update" for "perform_update" 23 Jun 2009 [2678] xorriso/xorriso.1 Updated xorriso man page 2009.06.24.133521 [2679] xorriso/xorriso.c Reverted order of memorized hardlink update parameters 2009.06.25.125048 [2680] libisoburn/burn_wrap.c Bug fix: Copies of overwriteable media onto sequential were mistaken in ROM drives 2009.06.27.112408 [2681] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 With -extract : made hardlink registration combinable with sort_lba ------------------------------------ cycle - xorriso-0.3.9 - 2009.06.27.112408 New option -for_backup as shortcut for -acl -xattr -hardlinks Bug fix: Copies of overwriteable media on sequential were mistaken in ROM drives 28 Jun 2009 [2681] svn copy -m "Branching for libisoburn release 0.4.0" \ http://svn.libburnia-project.org/libisoburn/trunk \ http://svn.libburnia-project.org/libisoburn/branches/ZeroFourZero 2009.06.28.090001 [2683] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.4.0 28 Jun 2009 [2684] xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.4.0 - 2009.06.28.090001 * New option -hardlinks * Improved reading performance with -update_r and -extract * New option -for_backup as shortcut for -acl -xattr -hardlinks * Operators with option -find : -not, -or, -and, (, ), -if, -then, -else * New -find tests -wholename, -prune * Bug fix: SIGSEGV with option -status and no search string * Bug fix: -load volid did not perform pattern search * Bug fix: Copies of overwriteable media on sequential were mistaken in ROM drives 2009.06.28.100124 [2685] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.4.1 28 Jun 2009 [2686] xorriso/changelog.txt Documented changes and release timestamp 28 Jun 2009 [2687] svn move -m 'Promoted branch to tag' \ http://svn.libburnia-project.org/libisoburn/branches/ZeroFourZero \ http://svn.libburnia-project.org/libisoburn/tags/ZeroFourZero ------------------------------------ cycle - xorriso-0.4.1 - 2009.06.28.100124 2009.06.30.120311 [2688] configure.ac xorriso/configure_ac.txt Provisory patch on ticket 152: GNU libiconv needs -liconv but offers no iconv() 2009.06.30.154606 [2689] xorriso/xorriso.c Bug fix: Ticket 149, one byte overflow of local variable. 2009.07.07.175748 [2690] configure.ac xorriso/configure_ac.txt Aligned configure --help texts provided by own code in configure.ac 2009.07.07.193955 [2693] acinclude.m4 configure.ac xorriso/configure_ac.txt configure options --enable-libdir-pkgconfig and --enable-pkgconfig-path=DIR 2009.07.14.134423 [2701] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libburn-0.6.9 2009.07.19.214008 [2703] xorriso/xorriso.c Bug fix: xorriso -as mkisofs did not understand the -C option of growisofs any more ------------------------------------ cycle - xorriso-0.4.1 - 2009.07.19.214008 Bug fix: xorriso -as mkisofs did not understand the -C option of growisofs any more ------------------------------ release - xorriso-0.4.0.pl01 - 2009.07.20.060001 Bug fix: xorriso -as mkisofs did not understand the -C option of growisofs any more 23 Jul 2009 [2704] xorriso/xorriso.1 More emphasis on early use of -hardlinks, -acl, -xattr etc. 2009.07.23.103339 [2705] xorriso/compile_xorriso.sh xorriso/xorriso_makefile_am.txt Adopted new libisofs source module md5.[cho] 2009.07.23.103728 [2706] xorriso/xorriso.c xorriso/xorrisoburn.c Showing a better link count with -hardlinks on -lsl 2009.07.25.181857 [2707] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso_eng.html Updating sorted link array before usage after image manipulations ------------------------------------ cycle - xorriso-0.4.1 - 2009.07.25.181857 Options -lsl and lsdl now display correct link counts if -hardlinks is on 2009.08.03.075954 [2708] xorriso/xorrisoburn.c Bug fix: Use of freed memory with -hardlinks on and -update 03 Aug 2009 [2709] xorriso/xorriso.1 Hints about -hardlinks and file content filtering 2009.08.10.123146 [2710] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New find actions get_md5, get_any_xattr, test -has_any_xattr 2009.08.10.192240 [2711] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c New write options session_md5, file_md5, read option nomd5 2009.08.10.193843 [2712] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -md5, new -as mkisofs option --md5 2009.08.10.194416 [2713] xorriso/xorriso.h xorriso/xorrisoburn.h Missing changes in rev 2710 and 2712 2009.08.11.194836 [2714] configure.ac libisoburn/libisoburn.h Requiring at least libisofs-0.6.21 now 2009.08.11.194836 [2715] xorriso/xorriso_private.h xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New options -check_md5, -check_md5_r, find action check_md5, test -has_md5 2009.08.12.130401 [2716] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Completed find action check_md5 2009.08.12.202423 [2717] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 Checking MD5 of whole session with -check_md5 and empty path list 2009.08.13.203718 [2718] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Better reaction wich -check_md5 if no MD5 array is loaded 2009.08.14.102355 [2719] xorriso/xorriso.c xorriso/xorrisoburn.c More timely abort of find jobs if the result pager gets aborted 2009.08.14.211648 [2720] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorriso.1 Letting -check_media look for session checksum tags 2009.08.15.173403 [2724] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso.1 Introduced -check_media event= for mismatch events 2009.08.16.200906 [2725] xorriso/xorriso.c xorriso/xorrisoburn.c Adapted to a change in iso_util_decode_md5_tag() 2009.08.17.162456 [2726] libisoburn/burn_wrap.c Giving overwriteable media a single-session toc with isoburn_drive_aquire(bit4) 2009.08.17.162834 [2727] xorriso/xorrisoburn.c -check_media recognizes relocated 64kB superblock tag and hops over session gaps 2009.08.18.173453 [2728] xorriso/xorrisoburn.c Making use of new API call iso_md5_match() and adapted to MD5 image mismatches 2009.08.18.191013 [2729] xorriso/xorriso.c xorriso/xorriso.1 Included -md5 on with option -for_backup ------------------------------------ cycle - xorriso-0.4.1 - 2009.08.18.191013 New option -md5, new -as mkisofs option --md5 New options -check_md5, -check_md5_r New find actions check_md5 get_md5, get_any_xattr New find tests -has_md5, -has_any_xattr libisoburn: New write options session_md5, file_md5, read option nomd5 2009.08.19.180632 [2730] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c Adapted to enhanced iso_write_opts_set_record_md5() 2009.08.19.180842 [2731] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New -md5 mode "all" 19 Aug 2009 [2732] xorriso/xorriso_eng.html Corrections with xorriso web page 2009.08.20.204309 [2733] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 Made -compare and -update work with recorded MD5 sums 2009.08.22.184241 [2734] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New -find action make_md5 to equip files from old images with MD5 2009.08.24.182839 [2738] configure.ac libisoburn/libisoburn.h Demanding libburn-0.6.9 now 2009.08.24.183750 [2739] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -calm_drive 2009.08.25.191217 [2741] xorriso/xorrisoburn.c More timely report of library messages with -check_md5 2009.08.25.191325 [2742] xorriso/xorriso.1 Updated incremental backup example 2009.08.25.191433 [2743] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.22 now 2009.08.25.191543 [2744] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libisofs-0.6.23 ------------------------------------ cycle - xorriso-0.4.1 - 2009.08.25.191543 Options -compare, -compare_r, -update, update_r now can use recorded MD5 New -find action make_md5 to equip files from old images with MD5 New option -calm_drive 2009.08.27.175608 [2752] xorriso/xorriso.c Updated helptext about -find actions, new -as mkisofs option --for_backup 2009.08.27.175719 [2753] libisoburn/libisoburn.h configure.ac Requiring libburn-0.7.0 now 2009.08.27.175806 [2754] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libburn-0.7.1 2009.08.28.100829 [2756] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -list_profiles 2009.08.28.101405 [2757] xorriso/xorriso.c Forgot to store before committing 2009.08.28.101716 [2758] xorriso/xorriso_private.h Forgot to commit xorriso_private.h 2009.08.28.112825 [2759] xorriso/xorrisoburn.c Small correction with reporting profile "Removable disk" ------------------------------------ cycle - xorriso-0.4.1 - 2009.08.28.112825 New option -list_profiles 29 Aug 2009 [2760] svn copy -m "Branching for libisoburn release 0.4.2" http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroFourTwo 2009.08.29.143001 [2761] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.4.2 29 Aug 2009 [2762] xorriso/xorriso.1 Some updates and corrections in xorriso man page 29 Aug 2009 [2765] xorriso/changelog.txt Document changes and release timestamp ----------------------------------- release - xorriso-0.4.2 - 2009.08.29.143001 * libisoburn: New write options session_md5, file_md5, file_stability * libisoburn: New read option nomd5 * Bug fix: xorriso -as mkisofs did not understand the -C option of growisofs any more * Options -lsl and lsdl now display correct link counts if -hardlinks is on * New option -md5, new -as mkisofs option --md5 * New options -check_md5, -check_md5_r * New find actions check_md5 get_md5, get_any_xattr * New find tests -has_md5, -has_any_xattr * Options -compare, -compare_r, -update, update_r now can use recorded MD5 * New -find action make_md5 to equip files from old images with MD5 * New option -calm_drive * New option -list_profiles 29 Aug 2009 [2763] xorriso/xorriso.1 Some updates and corrections in xorriso man page 2009.08.29.180146 [2764] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.4.3 29 Aug 2009 [2766] xorriso/changelog.txt Documented changes and release timestamp 30 Aug 2009 [2767] move -m 'Promoted branch to tag' http://svn.libburnia-project.org/libisoburn/branches/ZeroFourTwo http://svn.libburnia-project.org/libisoburn/tags/ZeroFourTwo ------------------------------------ cycle - xorriso-0.4.3 - 2009.08.29.180146 31 Aug 2009 [2768] xorriso/make_xorriso_standalone.sh Added libisofs/doc/checksums.txt to xorriso standalone tarball 2009.08.31.162746 [2769] xorriso/xorriso.c Bug fix: -for_backup did not enable -xattr and -md5 if no drive was chosen yet 31 Aug 2009 [2770] configure.ac Requiring libisofs-0.6.23 now 2009.08.31.210528 [2771] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New API call iso_write_opts_set_scdbackup_tag() 2009.08.31.211005 [2772] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New option -scdbackup_tag 2009.09.01.110102 [2776] xorriso/compile_xorriso.sh xorriso/xorriso_makefile_am.txt Removed libburn/lec.c from xorriso standalone and development scripts ------------------------------------ cycle - xorriso-0.4.3 - 2009.09.01.110102 * Bug fix: -for_backup did not enable -xattr and -md5 if no drive was chosen yet * New option -scdbackup_tag 2009.09.06.094209 [2796] configure.ac libisoburn/libisoburn.h Requiring libburn-0.7.1 now 2009.09.06.112851 [2798] xorriso/xorrisoburn.c Reporting "Media product:" with xorriso option -toc 2009.09.06.144813 [2799] xorriso/xorrisoburn.c Reporting media product info with -as cdrecord -atip 2009.09.07.161247 [2800] xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Avoided to use MD5 on filtered streams with compare and update ------------------------------------ cycle - xorriso-0.4.3 - 2009.09.07.161247 * Option -toc now reports "Media product:" 2009.09.13.095136 [2811] libisoburn/burn_wrap.c xorriso/xorrisoburn.c Eventually truncate stdio: track size to file size and issue warning 2009.09.16.162424 [2812] xorriso/xorriso.h xorriso/xorriso.c Avoiding a potential memory leak in xorriso main() 2009.09.17.144453 [2813] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c Expanded new API call isoburn_igopt_set_scdbackup_tag 2009.09.17.144849 [2814] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Changing new option -scdbackup_tag 2009.09.19.161026 [2817] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New -osirrox sub-options o_excl_on/off. Program osirrox starts with o_excl_off. 2009.09.20.092811 [2818] xorriso/xorriso.c xorriso/xorrisoburn.c Bug fix: Corrected handling of -as cdrecord -mode2, -xa, -xa2, -xa2, -xamix, xorrisofs -help, xorrecord -help 2009.09.20.110715 [2819] xorriso/xorriso.c xorriso/xorrisoburn.c Adjusted xorriso -as cdrecord fifo chunk size to -xa1 ------------------------------------ cycle - xorriso-0.4.3 - 2009.09.20.110715 * Bug fix: With -as cdrecord : -xa1 and -xamix were ignored although they do matter * Bug fix: xorrisofs -help, xorrecord -help displayed original xorriso -help 2009.09.22.143534 [2821] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Bug fix: -cut_out deleted previously cut-out pieces of the same file ------------------------------------ cycle - xorriso-0.4.3 - 2009.09.22.143534 * Bug fix: -cut_out deleted previously cut-out pieces of the same file 2009.09.27.080401 [2827] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 Narrowed rules for recognizing split file parts 2009.09.28.173322 [2828] xorriso/xorrisoburn.c xorriso/xorriso.1 Restricted split part recognition to directories with a complete part set 2009.09.30.092640 [2829] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New -check_media sub options bad_limit=, slow_limit=, chunk_size= ------------------------------------ cycle - xorriso-0.4.3 - 2009.09.30.092640 2009.10.05.165632 [2831] libisoburn/isofs_wrap.c libisoburn/burn_wrap.c Avoided FAILURE event with welcoming empty disk files 2009.10.05.172143 [2832] xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -pvd_info 2009.10.05.183619 [2833] xorriso/xorrisoburn.c Adjusted -pvd_info to the peculiarities of -volid 2009.10.05.190215 [2834] xorriso/xorrisoburn.c Added code to xorriso for repairing "_" in all three PVD id file names ------------------------------------ cycle - xorriso-0.4.3 - 2009.10.05.190215 * New option -pvd_info 2009.10.06.071704 [2835] xorriso/xorrisoburn.c Made sure that -pvd_info reports the actual PVD address 2009.10.07.072645 [2836] libisoburn/isofs_wrap.c Declared as closed those media which have read errors in the first 64 kB 2009.10.07.075634 [2837] libisoburn/libisoburn.h libisoburn/burn_wrap.c New bit7 with isoburn_drive_aquire(): pretend read-only 2009.10.07.080042 [2838] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New -rom_toc_scan sub option "force" to get DVD/BD table-of-content on Solaris ------------------------------------ cycle - xorriso-0.4.3 - 2009.10.07.080042 2009.10.07.124047 [2839] xorriso/xorriso.c xorriso/xorrisoburn.c Aligned sessions on overwriteables to full 32 blocks 2009.10.07.140521 [2840] libisoburn/burn_wrap.c Avoided FATAL event with -rom_toc_scan force and non-ISO media 2009.10.07.180552 [2841] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New options -system_id , -volset_id ------------------------------------ cycle - xorriso-0.4.3 - 2009.10.07.180552 * New options -system_id , -volset_id * Bug fix libisofs: Filenames could lose blanks during a multi-session cycle ------------------------------- patch - xorriso-0.4.2.pl02 - 2009.10.08.080001 * Bug fix: -for_backup did not enable -xattr and -md5 if no drive was chosen yet * Bug fix: xorrisofs -help, xorrecord -help displayed original xorriso -help * Bug fix libisofs: Filenames could lose blanks during a multi-session cycle 2009.10.08.155605 [2845] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.24 now 2009.10.08.155749 [2846] xorriso/configure_ac.txt xorriso/xorriso_eng.html Adapted xorriso-standalone to version leap libisofs-0.6.25 2009.10.10.142353 [2851] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 xorriso/xorriso_eng.html New option -mount_opts 2009.10.11.141528 [2852] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c Made -application_id, -publisher, -system_id, -volset_id pending image changes 2009.10.11.141815 [2853] xorriso/xorriso.c Changed FreeBSD mount command to mount_cd9660 2009.10.14.114830 [2862] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libburn-0.7.3 2009.10.14.115223 [2863] README xorriso/README configure.ac libisoburn/libisoburn.h Requiring libburn-0.7.2 now ------------------------------------ cycle - xorriso-0.4.3 - 2009.10.14.115223 * New option -mount_opts 2009.10.17.191225 [2866] xorriso/compile_xorriso.sh xorriso/xorriso_makefile_am.txt Added libburn/ecma130ab.[cho] to xorriso standalone and development scripts 2009.10.22.155746 [2869] xorriso/xorrisoburn.c Added a "NOT READBLE" result line to -check_md5 22 Oct 2009 [2870] xorriso/xorriso.1 Added man page example: Burn an existing ISO image file to media 27 Oct 2009 [2873] xorriso/xorriso.1 Adjustments of xorriso man page 27 Oct 2009 [2874] svn copy -m "Branching for libisoburn release 0.4.4" \ http://svn.libburnia-project.org/libisoburn/trunk \ http://svn.libburnia-project.org/libisoburn/branches/ZeroFourFour 2009.10.27.153001 [2875] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.4.4 27 Oct 2009 [2876] xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.4.4 - 2009.10.27.153001 * Bug fix: -cut_out deleted previously cut-out pieces of the same file * Bug fix libisofs: Filenames could lose blanks during a multi-session cycle * Bug fix: -for_backup did not enable -xattr and -md5 if no drive was chosen yet * Bug fix: With -as cdrecord : -xa1 and -xamix were ignored although they do matter * Bug fix: xorrisofs -help, xorrecord -help displayed original xorriso -help * New option -pvd_info * Option -toc now reports "Media product:" * New options -system_id , -volset_id * New option -mount_opts * New option -scdbackup_tag 2009.10.27.205410 [2877] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.4.5 28 Oct 2009 [2878] xorriso/changelog.txt Documented changes and release timestamp 28 Oct 2009 [2879] svn move -m "Promoted branch to tag" http://svn.libburnia-project.org/libisoburn/branches/ZeroFourFour http://svn.libburnia-project.org/libisoburn/tags/ZeroFourFour ------------------------------------ cycle - xorriso-0.4.5 - 2009.10.27.205410 2009.11.12.204523 [2897] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New -calm_drive mode "revoke" ------------------------------- patch - xorriso-0.4.4.pl01 - 2009.11.13.120001 * libburn: Workaround for Pioneer DVR-216D which got stuck on DVD-R burns * libburn: Workaround for Pioneer DVR-216D refusal to eject 2009.11.17.123150 [2909] configure.ac libisoburn/libisoburn.h Requiring libburn-0.7.3 now 2009.11.17.134239 [2910] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.1 New options -dvd_obs and -stdio_sync 2009.11.17.142447 [2911] configure.ac Makefile.am xorriso/configure_ac.txt xorriso/makefile_libxorriso_am.txt New configure option --enable-dvd-obs-64k ------------------------------------ cycle - xorriso-0.4.5 - 2009.11.17.142447 * New options -dvd_obs and -stdio_sync * New configure option --enable-dvd-obs-64k 2009.11.28.123903 [2927] xorriso/xorrisoburn.c Made use of burn_fifo_source_new() flag bit0 and of burn_os_open_track_src() 2009.11.28.124241 [2928] README xorriso/README xorriso/configure_ac.txt xorriso/xorriso_makefile_am.txt New configure option --enable-track-src-odirect ------------------------------------ cycle - xorriso-0.4.5 - 2009.11.28.124241 ------------------------------------ cycle - xorriso-0.4.5 - 2009.12.05.141523 * Bug fix: SIGSEGV with option -toc on LG GH22LS30 07 Dec 2009 [2944] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libburn-0.7.5 2009.12.07.082130 [2945] README xorriso/README configure.ac libisoburn/libisoburn.h Requiring libburn-0.7.4 now 08 Dec 2009 [2947] svn copy -m Branching for libisoburn release 0.4.6 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroFourSix 2009.12.08.130001 [2948] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.4.6 08 Dec 2009 [2949] xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.4.6 - 2009.12.08.130001 * libburn: Bug fix: SIGSEGV from NULL pointer with option -toc on LG GH22LS30 * libburn: Workaround for Pioneer DVR-216D which got stuck on DVD-R burns * libburn: Workaround for Pioneer DVR-216D refusal to eject * New options -dvd_obs and -stdio_sync * New configure option --enable-dvd-obs-64k 2009.12.08.144237 [2950] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.4.7 08 Dec 2009 [2961] xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-0.4.7 - 2009.12.08.144237 2009.12.10.120734 [2953] xorriso/xorrisoburn.c Silenced harmless compiler warning of FreeBSD 6.3 10 Dec 2009 [2954] xorriso/configure_ac.txt Corrected a harmless name error in xorriso standalone configuration 2009.12.14.121636 [2955] xorriso/xorriso.c Mentioned stream_recording=number in helptext of xorriso -as cdrecord 2009.12.26.224409 [2977] configure.ac libisoburn/libisoburn.h Requiring libburn-0.7.5 now 2009.12.26.231134 [2978] xorriso/configure_ac.txt xorriso/xorriso_makefile_am.txt xorriso/xorrisoburn.c Made xorriso-standalone aware of libcdio system adaptor 2009.12.27.095045 [2980] xorriso/xorrisoburn.c Reporting system adapter id with xorriso -version 2009.12.30.163643 [2989] xorriso/xorriso.c Handling -mount on Debian kfreebsd like on FreeBSD 2009.12.31.114951 [2991] xorriso/xorrisoburn.c Changed report title "System adapter" to "libburn OS adapter" 2009.12.31.142331 [2992] xorriso/xorriso.c Reacted on compiler warning of Debian buildd 2009.12.31.144342 [2993] libisoburn/libisoburn.h Reacted on doxygen errors of Debian buildd 2010.01.01.124622 [2996] libisoburn/isoburn.c xorriso/xorriso.c xorriso/xorrisoburn.c Reacted on compiler warnings of Debian kfreebsd-amd64 buildd 2010.01.01.125742 [2997] libisoburn/burn_wrap.c libisoburn/isofs_wrap.c Do not believe libburn if it says a role 2 drive is empty 2010.01.01.130215 [2998] README COPYRIGHT libisoburn/data_source.c xorriso/README xorriso/compile_xorriso.sh xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorrisoburn.h Updated copyright marks to 2010 01 Jan 2010 [2999] xorriso/xorriso.1 Removed a stray option argument from xorriso man page 01 Jan 2010 [3000] xorriso/xorriso_eng.html Updated xorriso homepage ------------------------------------ cycle - xorriso-0.4.7 - 2010.01.01.130215 * New configure option --enable-libcdio for system adapter to libcdio-0.83git 2010.01.07.090517 [3003] configure.ac xorriso/configure_ac.txt Changed configure test for zlib from compress2() to compressBound() 2010.01.11.113303 [3007] libisoburn/isofs_wrap.c Re-enabled recognition of missing or empty disk files as blank drive (rev 2997) 2010.01.11.113457 [3008] xorriso/xorrisoburn.c Changed a comment in xorrisoburn.c 2010.01.17.145019 [3020] libisoburn/burn_wrap.c Bug fix: xorriso did not blank CD-RW with images that were prepared on hard disk 2010.01.17.145139 [3021] xorriso/xorrisoburn.c Made xorriso issue a filure message if blanking was not successful 2010.01.17.145751 [3022] configure.ac Fixed a bug in configure.ac introduced with rev 3003 2010.01.19.133712 [3023] xorriso/xorrisoburn.c Avoided SORRY event for missing disk file during hardlink update 2010.01.20.105755 [3025] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.26 now 2010.01.20.105928 [3026] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libisofs-0.6.27 20 Jan 2010 [3027] xorriso/xorriso_eng.html Updated xorriso webpage ------------------------------------ cycle - xorriso-0.4.7 - 2010.01.20.105928 * Bug fix: xorriso did not blank CD-RW with images that were prepared on hard disk 2010.01.23.153510 [3039] README xorriso/README configure.ac libisoburn/libisoburn.h Requiring libburn-0.7.6 now 2010.01.23.153601 [3040] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libburn-0.7.7 25 Jan 2010 [3041] svn copy -m Branching for libisoburn release 0.4.8 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroFourEight 2010.01.25.120001 [3042] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.4.8 25 Jan 2010 [3043] xorriso/xorriso_private.h test/compare_file.c Lifted ban to derive GPLv3, extended copyright range to 2010 25 Jan 2010 [3045] configure.ac Corrected wrong micro version number 25 Jan 2010 [3048] xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.4.8 - 2010.01.25.120001 * Bug fix: xorriso did not blank CD-RW with images that were prepared on hard disk * New configure option --enable-libcdio for system adapter to libcdio-0.83git 2010.01.25.142705 [3046] configure.ac README libisoburn/libisoburn.h xorriso/README xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.4.9 25 Jan 2010 [3047] xorriso/xorriso_private.h test/compare_file.c Lifted ban to derive GPLv3, extended copyright range to 2010 25 Jan 2010 [3049] ([3044]) xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-0.4.9 - 2010.01.25.142705 25 Jan 2010 [3050] move -m Promoted branch to tag \ http://svn.libburnia-project.org/libisoburn/branches/ZeroFourEight \ http://svn.libburnia-project.org/libisoburn/tags/ZeroFourEight 27 Jan 2010 [3051] README Changed leftover text which disallowed GPLv3 04 Feb 2010 [3052] xorriso/xorriso.1 Small corrections in xorriso man page 2010.02.04.082710 [3053] acinclude.m4 configure.ac xorriso/configure_ac.txt Forcing use of /usr/local on FreeBSD by LDFLAGS and CPPFLAGS 2010.02.08.110814 [3055] xorriso/xorrisoburn.c Removed surplus quotes from an error message 8 Feb 2010 [3056] svn move -m 'Marked libisofs SVN copy as outdated (use bzr on lp)' http://svn.libburnia-project.org/libisofs http://svn.libburnia-project.org/libisofs_outdated 2010.02.10.150252 [3057] README Updated license statement about our legal view and future licenses 2010.02.10.150501 [3058] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.28 now 2010.02.10.150547 [3059] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libisofs-0.6.29 10 Feb 2010 [3060] xorriso/xorriso_eng.html Updated xorriso web page ------------------------------------ cycle - xorriso-0.4.9 - 2010.02.10.150547 Bug fix: Regression in libisofs introduced with xorriso-0.4.2 in August 2009: -boot_image could lead to SIGSEGV 12 Feb 2010 [3061] xorriso/README Switched from xorriso-standalone GPLv2+ to GNU xorriso GPLv3+ svn move -m 'Renamed xorriso/README to xorriso/README_gnu_xorriso' \ xorriso/README xorriso/README_gnu_xorriso 2010.02.12.153945 [3062] xorriso/xorriso.c xorriso/make_xorriso_standalone.sh xorriso/README_gnu_xorriso + xorriso/COPYING_gnu_xorriso xorriso/xorriso_eng.html Switched from xorriso-standalone GPLv2+ to GNU xorriso GPLv3+ 2010.02.12.213121 [3065] xorriso/xorriso.1 libisoburn/libisoburn.h xorriso/xorriso_eng.html Changed docs and comments to "GNU/Linux" where appropriate ------------------------------------ cycle - xorriso-0.4.9 - 2010.02.12.213121 2010.02.14.151045 [3067] README COPYRIGHT libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/data_source.c libisoburn/burn_wrap.c libisoburn/isofs_wrap.c Added or adjusted copyright and license statements in single files 2010.02.14.151610 [3068] xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_makefile_am.txt xorriso/xorriso.1 xorriso/xorriso_eng.html + xorriso/AUTHORS_gnu_xorriso + xorriso/COPYRIGHT_gnu_xorriso Added or adjusted copyright and license statements in single files 2010.02.14.172124 [3070] README xorriso/make_xorriso_standalone.sh xorriso/xorriso_makefile_am.txt Excluded unused libburn source module crc.c from GNU xorriso tarball 2010.02.15.204530 [3072] xorriso/xorriso_eng.html Adjustments with web page of GNU xorriso ------------------------------------ cycle - xorriso-0.4.9 - 2010.02.15.204530 2010.02.17.140002 [3074] xorriso/xorriso.c Bug fix for FreeBSD: xorriso could leave the drive tray locked 2010.02.18.125019 [3076] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.1 New option -scsi_log 2010.02.19.172125 [3077] xorriso/xorrisoburn.c Bug fix: xorriso -update_r could lead to SIGSEGV if applied to a data file 19 Feb 2010 [3078] [3079] xorriso/xorriso_eng.html Adjustments with web page of GNU xorriso ------------------------------------ cycle - xorriso-0.4.9 - 2010.02.19.172125 Bug fix on FreeBSD: xorriso could leave the drive tray locked Bug fix on FreeBSD: Piped input was falsely attributed a small fixed size Bug fix: xorriso -update_r could lead to SIGSEGV if applied to a data file New option -scsi_log 22 Feb 2010 [3081] xorriso/README_gnu_xorriso xorriso/xorriso_eng.html Mentioned bug-xorriso@gnu.org as a contact address for GNU xorriso 2010.02.22.195623 [3082] xorriso/make_xorriso_standalone.sh xorriso/xorriso.h xorriso/xorriso.c Changed versioning and tarball name to match unpack directory name 22 Feb 2010 [3083] svn copy -m Branching for libisoburn release 0.5.0 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroFiveZero 2010.02.22.213001 [3084] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.5.0 22 Feb 2010 [3085] Makefile.am Delivering xorriso/README_gnu_xorriso with libisoburn 22 Feb 2010 [3086] xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.5.0 - 2010.02.22.213001 Bug fix: Regression in libisofs introduced with xorriso-0.4.2 in August 2009: -boot_image could lead to SIGSEGV Bug fix on FreeBSD: xorriso could leave the drive tray locked Bug fix on FreeBSD: Piped input was falsely attributed a small fixed size Bug fix: xorriso -update_r could lead to SIGSEGV if applied to a data file New option -scsi_log 2010.02.23.093924 [3087] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.5.1 23 Feb 2010 [3088] Makefile.am Delivering xorriso/README_gnu_xorriso with libisoburn 23 Feb 2010 [3089] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/ZeroFiveZero http://svn.libburnia-project.org/libisoburn/tags/ZeroFiveZero ------------------------------------ cycle - xorriso-0.5.1 - 2010.02.23.093924 28 Feb 2010 [3094] xorriso/make_xorriso_standalone.sh Adjusted patcher of Makefile.in for GNU xorriso 28 Feb 2010 [3095] xorriso/xorriso.1 Corrected spelling errors in xorriso man page 2010.03.09.065117 [3109] libisoburn/libisoburn.h Requiring libburn-0.7.7 now 2010.03.09.065408 [3110] xorriso/xorriso_private.h xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.c Adapted xorriso signal handling to new libburn advise 2010.03.10.183348 [3121] configure.ac libisoburn/libisoburn.h Requiring libburn-0.7.8 now 2010.03.10.183426 [3122] xorriso/configure_ac.txt Adapted GNU xorriso to version leap libburn-0.7.9 2010.03.18.101202 [3125] Makefile.am xorriso/xorriso_makefile_am.txt xorriso/make_xorriso_standalone.sh + xorriso/xorriso.texi + xorriso/xorriso.info + xorriso/make_xorriso_1.c + xorriso/make_docs.sh xorriso/xorriso.1 xorriso/convert_man_to_html.sh Switched xorriso documentation to a hybrid of texinfo and man page ------------------------------------ cycle - xorriso-0.5.1 - 2010.03.18.101202 * xorriso documentation now also available in (tex)info format 2010.03.20.165317 [3126] xorriso/make_docs.sh xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/convert_man_to_html.sh Restructured xorriso documentation and added index 2010.03.21.124435 [3127] Makefile.am xorriso/xorriso_makefile_am.txt xorriso/xorriso.texi xorriso/xorriso.info Distributing and installing xorriso.info 2010.03.21.124517 [3128] libisoburn/burn_wrap.c Silenced a harmless compiler warning 22 Mar 2010 [3129] xorriso/xorriso.texi Changed global info dir entry of xorriso.texi 22 Mar 2010 [3130] xorriso/xorriso.info Changed global info dir entry of xorriso.info 2010.03.29.103419 [3136] libisoburn/burn_wrap.c Avoided to declare empty tray as written media 2010.03.29.103533 [3137] xorriso/xorrisoburn.c Better handling of empty tray when aquiring drive ------------------------------------ cycle - xorriso-0.5.1 - 2010.03.29.103533 29 Mar 2010 [3138] svn copy -m Branching for libisoburn release 0.5.2 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroFiveTwo 2010.03.29.190001 [3139] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.5.2 30 Mar 2010 [3141] xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.5.2 - 2010.03.29.190001 * xorriso documentation now also available in (tex)info format * Prepared FreeBSD system adapter to work with ahci driver 2010.03.30.082405 [3140] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.5.3 30 Mar 2010 [3142] xorriso/changelog.txt Documented changes and release timestamp 30 Mar 2010 [3143] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/ZeroFiveTwo http://svn.libburnia-project.org/libisoburn/tags/ZeroFiveTwo ------------------------------------ cycle - xorriso-0.5.3 - 2010.03.30.082405 2010.04.01.140653 [3144] libisoburn/burn_wrap.c libisoburn refused to recognize ISO images smaller than 64 kB 2010.04.01.140653 [3145] xorriso/xorriso.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Disabled isohybrid MBR production on request of H. Peter Anvin 2010.04.05.192817 [3147] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.29 now 2010.04.06.125013 [3148] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New API call isoburn_igopt_set_system_area() 2010.04.06.151750 [3149] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -as mkisofs options -G and --protective-msdos-label for GRUB 2010.04.06.192328 [3150] xorriso/xorriso.c xorriso/xorriso_eng.html Polishing of new GRUB supporting features ------------------------------------ cycle - xorriso-0.5.3 - 2010.04.06.192328 * New API call isoburn_igopt_set_system_area() * New -as mkisofs options -G and --protective-msdos-label for GRUB 2010.04.07.073024 [3151] libisoburn/burn_wrap.c Fixed image reading bug introduced with revision 3144 ------------------------------------ cycle - xorriso-0.5.3 - 2010.04.07.073024 2010.04.07.202148 [3152] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New API call isoburn_igopt_set_pvd_times() 2010.04.07.202559 [3153] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New option -volume_date, for grub-mkrescue: -as mkisofs --modification-date= 2010.04.08.185744 [3154] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New find action mkisofs_r and emulation -as mkisofs -r ------------------------------------ cycle - xorriso-0.5.3 - 2010.04.08.185744 * New API call isoburn_igopt_set_pvd_times() * New option -volume_date, for grub-mkrescue: -as mkisofs --modification-date= * New find action mkisofs_r and emulation -as mkisofs -r 2010.04.09.171108 [3164] xorriso/configure_ac.txt Adapted GNU xorriso to version leap libburn-0.8.1 2010.04.10.150747 [3165] configure.ac xorriso/configure_ac.txt Trying to avoid iconv const warning and multiple -liconv options on FreeBSD 2010.04.10.170927 [3166] libisoburn/libisoburn.h libisoburn/isoburn.c New options bit1 of isoburn_igopt_set_system_area() for isohybrid production 2010.04.10.171214 [3167] xorriso/xorriso.c xorriso/xorrisoburn.c New -boot_image isolinux partition_table=on, -as mkisofs -isohybrid-mbr 2010.04.10.180103 [3168] xorriso/xorriso.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Updated help text and documentation ------------------------------------ cycle - xorriso-0.5.3 - 2010.04.10.180103 * New options bit1 of isoburn_igopt_set_system_area() for isohybrid production * New: -boot_image isolinux partition_table=on, -as mkisofs -isohybrid-mbr 2010.04.11.120902 [3169] xorriso/xorriso.c test/compare_file.c Reacted on compiler warnings of cygwin 2010.04.11.121002 [3170] xorriso/xorrisoburn.c Avoided printing of possibly uninitialized integer 2010.04.11.121418 [3171] acinclude.m4 xorriso/configure_ac.txt Realistic iconv() linking test in ./configure with eventual abort 2010.04.11.122253 [3172] xorriso/xorriso.c test/compare_file.c xorriso/xorrisoburn.c acinclude.m4 xorriso/configure_ac.txt (Payload files of revisions 3169, 3170, 3171) ------------------------------------ cycle - xorriso-0.5.3 - 2010.04.11.122253 12 Apr 2010 [3173] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Mentioned xorriso alias names in chapter FILES of documentation 2010.04.12.111137 [3174] xorriso/xorriso.c test/compare_file.c Decided to put out uid_t and gid_t as unsigned long 2010.04.13.103037 [3175] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New isoburn_igopt_set_relaxed() options: only_iso_versions, no_j_force_dots 2010.04.13.103917 [3176] xorriso/xorrisoburn.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -compliance options only_iso_version, no_j_force_dots ------------------------------------ cycle - xorriso-0.5.3 - 2010.04.13.103917 * New isoburn_igopt_set_relaxed() options: only_iso_versions, no_j_force_dots * New -compliance options only_iso_version, no_j_force_dots 2010.04.13.154201 [3177] acinclude.m4 configure.ac xorriso/configure_ac.txt More carefully checking for libreadline suitability ------------------------------------ cycle - xorriso-0.5.3 - 2010.04.13.154201 2010.04.16.195349 [3178] libisoburn/isofs_wrap.c libisoburn now always loads the System Area with the ISO image 2010.04.16.195835 [3179] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Made -boot_image isolinux|grub patch and partition_table=on patch System Area 2010.04.17.171147 [3180] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.30 now 2010.04.17.171232 [3181] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libisofs-0.6.31 ------------------------------------ cycle - xorriso-0.5.3 - 2010.04.17.171232 2010.04.18.095808 [3182] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -find action sort_weight, new -as mkisofs --sort-weight 2010.04.18.100814 [3183] xorriso/xorriso.c Added --sort-weight to -as mkisofs -help ------------------------------------ cycle - xorriso-0.5.3 - 2010.04.18.100814 * New -find action sort_weight, new -as mkisofs --sort-weight 2010.04.18.141143 [3184] xorriso/xorrisoburn.c Corrected image size computation for comparison with partition table 18 Apr 2010 [3185] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Updated documentation about MBR 19 Apr 2010 [3186] svn copy -m Branching for libisoburn release 0.5.4 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroFiveFour 2010.04.19.080001 [3187] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.5.4 19 Apr 2010 [3188] xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.5.4 - 2010.04.19.080001 * New API call isoburn_igopt_set_system_area() * New API call isoburn_igopt_set_pvd_times() * New isoburn_igopt_set_relaxed() options: only_iso_versions, no_j_force_dots * New -boot_image any system_area=, -as mkisofs option -G * New -boot_image grub partition_table=on, --protective-msdos-label * New -boot_image isolinux partition_table=on, -as mkisofs -isohybrid-mbr * New option -volume_date, for grub-mkrescue: -as mkisofs --modification-date= * New -find action mkisofs_r and emulation -as mkisofs -r * New -compliance options only_iso_version, no_j_force_dots * New -find action sort_weight, new -as mkisofs --sort-weight 2010.04.19.095104 [3189] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.5.5 19 Apr 2010 [3190] xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-0.5.5 - 2010.04.19.095104 19 Apr 2010 [3191] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/ZeroFiveFour http://svn.libburnia-project.org/libisoburn/tags/ZeroFiveFour 20 Apr 2010 [3192] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.31 now 2010.04.20.091732 [3193] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c New -boot_image any platform_id= 2010.04.20.103448 [3194] xorriso/xorriso.c Determining boot image size only after it was added to the ISO image ------------------------------------ cycle - xorriso-0.5.5 - 2010.04.20.103448 * New -boot_image any platform_id=, -as mkisofs --efi-boot 2010.04.20.140151 [3195] xorriso/xorrisoburn.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Raised boot catalog weight to 1000000000 2010.04.20.142540 [3196] xorriso/xorrisoburn.c Changed iso_image_set_boot_platform_id() to el_torito_set_boot_platform_id() 2010.04.22.143944 [3197] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Allowing 1 BIOS boot image and 1 EFI boot image: -boot_image grub efi_path= 2010.04.22.160615 [3198] xorriso/xorriso.c xorriso/xorrisoburn.c Bug fixes and improvements of previous revision 3197 ------------------------------------ cycle - xorriso-0.5.5 - 2010.04.22.160615 ------------------------------------ cycle - xorriso-0.5.5 - 2010.04.22.221241 2010.04.26.121051 [3199] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorrisoburn.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Allowing up to 32 boot images, -boot_image any id_string=, sel_crit= 2010.04.26.141229 [3200] xorriso/xorriso.c xorriso/xorrisoburn.c Fixed minor problems with previous revision 3199 2010.04.26.175506 [3201] xorriso/xorriso.c xorriso/xorriso_eng.html xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Some documentation updates 27 Apr 2010 [3202] xorriso/xorriso_eng.html xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Some more documentation updates 2010.04.27.072331 [3203] xorriso/xorriso.c Downgraded message about stdout redirection from NOTE to DEBUG 2010.04.27.074834 [3204] xorriso/xorrisoburn.c Fixed handling of empty current bin_path and already attached boot images 2010.04.29.133234 [3205] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Avoided -boot_image patching for images which obviously have no boot-info-table ------------------------------------ cycle - xorriso-0.5.5 - 2010.04.29.133234 * Allowing up to 32 boot images * New -boot_image bootspecs efi_path=, platform_id=, sel_crit=, id_string=, next * New -as mkisofs options --efi-boot, -eltorito-alt-boot 2010.05.03.123759 [3208] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.32 now 2010.05.03.165446 [3209] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libisofs-0.6.33 04 May 2010 [3210] svn copy -m Branching for libisoburn release 0.5.6 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroFiveSix 2010.05.04.100001 [3211] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.5.6 04 May 2010 [3212] xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.5.6 - 2010.05.04.100001 * Allowing up to 32 boot images * New -boot_image bootspecs efi_path=, platform_id=, sel_crit=, id_string=, next * New -as mkisofs options --efi-boot, -eltorito-alt-boot 2010.05.04.120115 [3213] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorriso.c xorriso/xorrisoburn.h xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.5.7 04 May 2010 [3214] xorriso/changelog.txt Documented changes and release timestamp 04 May 2010 [3215] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/ZeroFiveSix http://svn.libburnia-project.org/libisoburn/tags/ZeroFiveSix ------------------------------------ cycle - xorriso-0.5.7 - 2010.05.04.120115 2010.05.05.194248 [3216] xorriso/xorriso_private.h xorriso/xorriso.c xorriso/xorrisoburn.c Removed development macro Xorriso_multi_booT by making it unconditional 05 May 2010 [3217] xorriso/xorriso_eng.html Avoided to show inappropriate links on www.gnu.org ------------------------------------ cycle - xorriso-0.5.7 - 2010.05.05.194248 2010.05.15.184604 [3218] Makefile.am xorriso/xorriso_makefile_am.txt xorriso/make_xorriso_standalone.sh xorriso/compile_xorriso.sh xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorrisoburn.h - xorriso/xorriso.c - xorriso/xorrisoburn.c + xorriso/xorriso_main.c + xorriso/sfile.h + xorriso/sfile.c + xorriso/aux_objects.h + xorriso/aux_objects.c + xorriso/findjob.h + xorriso/findjob.c + xorriso/check_media.h + xorriso/check_media.c + xorriso/misc_funct.h + xorriso/misc_funct.c + xorriso/text_io.h + xorriso/text_io.c + xorriso/match.h + xorriso/match.c + xorriso/emulators.h + xorriso/emulators.c + xorriso/disk_ops.h + xorriso/disk_ops.c + xorriso/cmp_update.h + xorriso/cmp_update.c + xorriso/parse_exec.h + xorriso/parse_exec.c + xorriso/opts_a_c.c + xorriso/opts_d_h.c + xorriso/opts_i_o.c + xorriso/opts_p_z.c + xorriso/base_obj.h + xorriso/base_obj.c + xorriso/lib_mgt.h + xorriso/lib_mgt.c + xorriso/sort_cmp.h + xorriso/sort_cmp.c + xorriso/drive_mgt.h + xorriso/drive_mgt.c + xorriso/iso_img.h + xorriso/iso_img.c + xorriso/iso_tree.h + xorriso/iso_tree.c + xorriso/iso_manip.h + xorriso/iso_manip.c + xorriso/write_run.h + xorriso/write_run.c + xorriso/read_run.h + xorriso/read_run.c + xorriso/filters.h + xorriso/filters.c Splitted xorriso.c and xorrisoburn.c into smaller source modules 2010.05.16.092546 [3220] libisoburn/burn_wrap.c libisoburn/data_source.c libisoburn/isoburn.c libisoburn/isofs_wrap.c Eventually including ../config.h generated by autotools 2010.05.16.093108 [3221] xorriso/aux_objects.c xorriso/base_obj.c xorriso/check_media.c xorriso/cmp_update.c xorriso/disk_ops.c xorriso/drive_mgt.c xorriso/emulators.c xorriso/filters.c xorriso/findjob.c xorriso/iso_img.c xorriso/iso_manip.c xorriso/iso_tree.c xorriso/lib_mgt.c xorriso/make_xorriso_1.c xorriso/match.c xorriso/misc_funct.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/opts_p_z.c xorriso/parse_exec.c xorriso/read_run.c xorriso/sfile.c xorriso/sort_cmp.c xorriso/text_io.c xorriso/write_run.c xorriso/xorriso_main.c Eventually including ../config.h generated by autotools 2010.05.16.113100 [3222] xorriso/xorriso.h xorriso/xorriso_main.c xorriso/make_xorriso_standalone.sh Made option -version know again whether it is in GNU xorriso 2010.05.16.171056 [3223] xorriso/xorriso_main.c Checking for suitable sizeof(off_t) 2010.05.17.142656 [3224] + xorriso/xorriso_bootstrap.txt xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_makefile_am.txt Made compilation of GNU xorriso less verbous by use of autoheader config.h 2010.05.18.174455 [3225] Makefile.am - xorriso/makefile_libxorriso_am.txt xorriso/xorriso_main.c Moved xorriso implementation into libisoburn 19 May 2010 [3226] test/compare_file.c Eventually including ../config.h generated by autotools 2010.05.19.160540 [3227] configure.ac Makefile.am xorriso/compile_xorriso.sh Moved configuration options from xorriso_xorriso_CFLAGS to CFLAGS 2010.05.21.132725 [3228] libisoburn/libisoburn.h Mentioned both levels of API and their mutual exclusiveness 2010.05.21.133627 [3229] xorriso/xorriso.h xorriso/xorriso_main.c xorriso/base_obj.c xorriso/opts_p_z.c xorriso/write_run.c Adapted version checking to new library situation 2010.05.21.134500 [3230] libisoburn/libisoburn.h Corrected typo in remark 2010.05.23.072450 [3231] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New write extension option isoburn_igopt_no_emul_toc 2010.05.23.072616 [3232] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/write_run.c New -compliance rule no_emul_toc 2010.05.23.072845 [3233] xorriso/emulators.c Implemented -as cdrecord -V, new -as mkisofs option --no-emul-toc 23 May 2010 [3234] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Documentation for recent enhancements 2010.05.26.123017 [3235] xorriso/emulators.c Implemented -as mkisofs options -U, -N, -l, -d, -allow-lowercase 26 May 2010 [3236] xorriso/xorriso_eng.html Updated GNU xorriso web page ------------------------------------ cycle - xorriso-0.5.7 - 2010.05.26.123017 * xorriso source split into more modules, object code moved into libisoburn * New write extension option isoburn_igopt_no_emul_toc * New -compliance rule no_emul_toc, new -as mkisofs --no-emul-toc * Implemented -as cdrecord -V * Implemented -as mkisofs options -U, -N, -l, -d, -allow-lowercase 2010.05.31.095756 [3238] xorriso/aux_objects.h xorriso/aux_objects.c Corrected a misplaced class description comment 2010.06.07.180414 [3242] xorriso/xorriso_makefile_am.txt Taking care of new Solaris adapter of libburn 07 Jun 2010 [3243] README xorriso/README_gnu_xorriso xorriso/xorriso_eng.html Updated documentation about Solaris adapter ------------------------------------ cycle - xorriso-0.5.7 - 2010.06.07.180414 * New libburn system adapter for Solaris uscsi (tested on snv134, kernel 5.11) 10 Jun 2010 [3249] xorriso/README_gnu_xorriso xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Mentioned Solaris and system dependent drive permission settings 2010.06.11.174804 [3258] configure.ac libisoburn/libisoburn.h Requiring libburn-0.8.2 now 11 Jun 2010 [3259] xorriso/configure_ac.txt Adapted xorriso-standalone to version leap libburn-0.8.3 ------------------------------------ cycle - xorriso-0.5.7 - 2010.06.11.174804 2010.06.13.105645 [3260] xorriso/emulators.c Removed surplus test expression 2010.06.13.110316 [3261] README xorriso/xorriso.h xorriso/xorriso_main.c Updated docs about existence of xorriso API in libisoburn 2010.06.13.133537 [3261] xorriso/opts_d_h.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Untangled messed-up relation between -hardlinks and -compliance 13 Jun 2010 [3263] svn copy -m Branching for libisoburn release 0.5.8 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroFiveEight 2010.06.13.140001 [3264] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h (forgotten: xorriso/xorriso_main.c) xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.5.8 13 Jun 2010 [3265] xorriso/opts_p_z.c Avoided double printing of xorriso version header line 13 Jun 2010 [3266] xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.5.8 - 2010.06.13.140001 * New libburn system adapter for Solaris uscsi (tested on snv134, kernel 5.11) * xorriso source split into more modules, object code moved into libisoburn * New write extension option isoburn_igopt_no_emul_toc * New -compliance rule no_emul_toc, new -as mkisofs --no-emul-toc * Implemented -as cdrecord option -V * Implemented -as mkisofs options -U, -N, -l, -d, -allow-lowercase 2010.06.14.073240 [3268] xorriso/opts_p_z.c Avoided double printing of xorriso version header line 2010.06.14.081834 [3269] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.5.9 14 Jun 2010 [3270] xorriso/changelog.txt Documented changes and release timestamp 14 Jun 2010 [3271] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/ZeroFiveEight http://svn.libburnia-project.org/libisoburn/tags/ZeroFiveEight ------------------------------------ cycle - xorriso-0.5.9 - 2010.06.14.081834 2010.06.15.160133 [3275] configure.ac libisoburn/libisoburn.h Requiring libburn-0.8.3 now (because SIGWINCH handling on Solaris) 2010.06.15.160501 [3276] acinclude.m4 Enabled use of libreadline on Solaris by eventually linking -lcurses 2010.06.16.105449 [3278] libisoburn/libisoburn.h Clarified API description of isoburn_read_iso_head() 2010.06.16.105652 [3279] xorriso/write_run.c Bug fix: check_media patch_lba0= could install wrong image size 2010.06.16.140253 [3280] xorriso/write_run.c Made patch_lba0= more modest with overwriting. 2010.06.18.053105 [3281] xorriso/emulators.c Bug fix: -as mkisofs option -volset was wrongly interpreted like -volid 2010.06.18.113147 [3282] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.33 now 2010.06.19.152046 [3283] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/write_run.c xorriso/opts_a_c.c xorriso/iso_img.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -boot_image bootspec cat_hidden= 2010.06.19.152046 [3284] xorriso/iso_tree.h xorriso/iso_tree.c xorriso/findjob.h xorriso/findjob.c xorriso/iso_manip.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New find test -disk_name 2010.06.20.072130 [3285] xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/iso_manip.c xorriso/findjob.h xorriso/findjob.c xorriso/misc_funct.h xorriso/misc_funct.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New find action -hide, new find test -hidden 2010.06.20.141555 [3286] xorriso/aux_objects.c xorriso/emulators.c xorriso/opts_p_z.c xorriso/read_run.c libisoburn/burn_wrap.c Trying to silence compiler warnings of OpenSuse Build Service 2010.06.20.164738 [3287] xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/iso_manip.c xorriso/disk_ops.c xorriso/iso_tree.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New option -hide 2010.06.22.132641 [3288] xorriso/opts_p_z.c Made -quoted_path_list continue to execute words after error in the same line 2010.06.23.134250 [3289] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/aux_objects.c xorriso/parse_exec.c xorriso/opts_i_o.c xorriso/emulators.c xorriso/disk_ops.c xorriso/misc_funct.h xorriso/misc_funct.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -as mkisofs options -hide, -hide-joliet, -hide-list, -hide-joliet-list 2010.06.23.134250 [3290] xorriso/emulators.c Included a missing header file 2010.06.23.151900 [3291] xorriso/parse_exec.h Updated the missing header file 2010.06.23.152337 [3292] xorriso/emulators.c Revoked unneeded change of revision 3290 2010.06.23.175357 [3293] xorriso/xorriso_private.h xorriso/lib_mgt.c xorriso/drive_mgt.c xorriso/iso_img.c Reset xorriso->boot_count when image gets discarded 2010.06.23.175613 [3294] xorriso/write_run.c Moved call of iso_image_set_boot_catalog_hidden() to Xorriso_write_session() ------------------------------------ cycle - xorriso-0.5.9 - 2010.06.23.175613 Bug fix: -check_media patch_lba0= could install wrong image size Bug fix: -as mkisofs option -volset was wrongly interpreted like -volid Enabled use of libreadline on Solaris New find test -disk_name New option -hide, -find action -hide, -find test -hidden New -boot_image bootspec cat_hidden=on New -as mkisofs options -hide, -hide-joliet, -hide-list, -hide-joliet-list 2010.06.24.084800 [3295] xorriso/emulators.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -as mkisofs option --boot-catalog-hide 2010.06.25.095836 [3296] xorriso/emulators.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -as mkisofs option -dir-mode 2010.06.25.102140 [3297] xorriso/emulators.c New -as mkisofs option -file-mode 2010.06.25.174329 [3298] xorriso/text_io.c Appending eventual strerror() to event message line rather than to new line 2010.06.25.175810 [3299] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/sfile.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New option -read_mkisofsrc interprets .mkisofsrc 2010.06.26.113947 [3300] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/text_io.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New options -copright_file , -biblio_file , -abstract_file 2010.06.26.114315 [3301] xorriso/emulators.c New -as mkisofs options -abstract, -biblio, -copyright ------------------------------------ cycle - xorriso-0.5.9 - 2010.06.26.114315 New -as mkisofs option --boot-catalog-hide New options -copright_file , -biblio_file , -abstract_file New option -read_mkisofsrc interprets .mkisofsrc New -as mkisofs options -dir-mode, -file-mode New -as mkisofs options -abstract, -biblio, -copyright 2010.06.28.100903 [3302] xorriso/text_io.c Supressing "Thank you for being patient" with times smaller than 2 2010.06.28.101019 [3303] xorriso/iso_tree.c Made option -lsl mark files in hidden directories as hidden 27 Jun 2010 [3304] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Removed outdated doc statement about hidden files 2010.06.29.102043 [3305] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.34 now 2010.06.29.102229 [3306] xorriso/configure_ac.txt Adapted GNU xorriso to version leap libisofs-0.6.35 2010.06.30.181822 [3315] configure.ac libisoburn/libisoburn.h Requiring libburn-0.8.4 now 2010.06.30.181908 [3316] xorriso/configure_ac.txt Adapted GNU xorriso to version leap libburn-0.8.5 02 Jul 2010 [3317] svn copy -m Branching for libisoburn release 0.6.0 \ http://svn.libburnia-project.org/libisoburn/trunk \ http://svn.libburnia-project.org/libisoburn/branches/ZeroSixZero 2010.07.02.090001 [3318] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.6.0 02 Jul 2010 [3319] xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.6.0 - 2010.07.02.090001 Bug fix: -check_media patch_lba0= could install wrong image size Bug fix: -as mkisofs option -volset was wrongly interpreted like -volid Enabled use of libreadline on Solaris New find test -disk_name New option -hide, -find action -hide, -find test -hidden New -boot_image bootspec cat_hidden=on New options -copright_file , -biblio_file , -abstract_file New option -read_mkisofsrc interprets .mkisofsrc Implemented -as mkisofs options -hide,-hide-joliet,-hide-list,-hide-joliet-list New -as mkisofs option --boot-catalog-hide Implemented -as mkisofs options -dir-mode, -file-mode Implemented -as mkisofs options -abstract, -biblio, -copyright 2010.07.02.195907 [3320] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.6.1 2 Jul 2010 [3321] xorriso/changelog.txt Documented changes and release timestamp 2 Jul 2010 [3322] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/ZeroSixZero http://svn.libburnia-project.org/libisoburn/tags/ZeroSixZero ------------------------------------ cycle - xorriso-0.6.1 - 2010.07.02.195907 4 Jul 2010 [3324] acinclude.m4 Added -L/usr/local/lib to libisoburn LDFLAGS for Solaris 2010.07.05.090409 [3327] xorriso/xorriso.h Removed definition of phantom xorriso options 2010.07.05.122901 [3328] configure.ac Makefile.am README + libisoburn/libisoburn.ver Hiding all non-API symbols from the linker by use of --version-script 2010.07.06.114102 [3331] configure.ac acinclude.m4 Let configure perform linker test with --version-script if enabled 2010.07.06.114503 [3332] libisoburn/libisoburn.h xorriso/xorriso.h Mentioned that public API calls must be in libisofs/libisofs.ver 2010.07.08.155900 [3333] xorriso/emulators.c Made behavior of -as mkisofs with unknown options more similar to original ------------------------------------ cycle - xorriso-0.6.1 - 2010.07.08.155900 * Made behavior of -as mkisofs with unknown options more similar to original 2010.07.29.164843 [3338] Makefile.am Detached make target "doc" from target "all". 29 Jul 2010 [3339] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Corrected manual text about -read_mkisofsrc 2010.07.30.155123 [3340] libisoburn/libisoburn.ver xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/base_obj.c xorriso/lib_mgt.c xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/opts_p_z.c xorriso/emulators.c xorriso/text_io.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New option -preparer_id, -as mkisofs options -p and -preparer 30 Jul 2010 [3342] doc/doxygen.conf.in Removed problematic DETAILS_AT_TOP to silence warning of Debian buildd 30 Jul 2010 [3343] doc/doxygen.conf.in Corrected FILE_PATTERNS 2010.07.31.085437 [3344] xorriso/cmp_update.c Missing device,inode numbers caused -compare_r to report a difference ------------------------------------ cycle - xorriso-0.6.1 - 2010.07.31.085437 * New option -preparer_id, -as mkisofs options -p and -preparer 2010.08.09.092037 [3351] libisoburn/libisoburn.h doc/comments Hopefully silenced warnings of doxygen on Debian buildd 2010.08.18.102709 [3353] xorriso/emulators.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -as mkisofs option -e from Fedora genisoimage 2010.08.18.181640 [3354] xorriso/xorriso_private.h xorriso/opts_a_c.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -boot_image specifier emul_type=none|hard_disk|floppy 2010.08.18.181855 [3355] xorriso/emulators.c New -as mkisofs option -hard-disk-boot, enabled -b without -no-emul-boot 2010.08.21.103003 [3357] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorriso_eng.html Mentioned support for DVD-R DL as single-session media 2010.09.04.100823 [3359] xorriso/iso_manip.c This was obviously forgotten to commit with rev 3289 2010.09.05.113621 [3360] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New API function isoburn_igopt_set_part_offset(), requiring libisofs 0.6.35 2010.09.05.113655 [3361] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/text_io.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New boot_image boot specs partition_offset, partition_hd_cyl, partition_sec_hd 2010.09.05.113945 [3362] xorriso/emulators.c New -as mkisofs options -partition_offset, -partition_hd_cyl, -partition_sec_hd ------------------------------------ cycle - xorriso-0.6.1 - 2010.09.05.113945 * New API function isoburn_igopt_set_part_offset() * New -boot_image specifier emul_type=none|hard_disk|floppy * New -as mkisofs option -hard-disk-boot, enabled -b without -no-emul-boot * New -as mkisofs option -e from Fedora genisoimage * New boot_image boot specs partition_offset, partition_hd_cyl, partition_sec_hd * New -as mkisofs options -partition_offset, -partition_hd_cyl, -partition_sec_hd 2010.09.06.103347 [3363] libisoburn/isoburn.c Bug fix: Re-enabled use of iso_write_opts_set_rrip_1_10_px_ino() 2010.09.10.170925 [3364] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c libisoburn/burn_wrap.c Enabled multi-session with partition offset 2010.09.10.171223 [3365] xorriso/drive_mgt.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Enabled multi-session with partition offset 2010.09.11.125002 [3366] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c Removed development marks 2010.09.11.125039 [3367] xorriso/xorriso_private.h Removed development marks ------------------------------------ cycle - xorriso-0.6.1 - 2010.09.11.125039 * Bug fix: Regression with -hardlinks and -compliance old_rr, 0.4.2, Aug 2009 2010.09.15.065722 [3370] Makefile.am ChangeLog Meaningful change log file derived by George Danchev from web site 2010.09.15.091546 [3371] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.36 now 2010.09.15.092023 [3372] xorriso/configure_ac.txt Version leap to 0.6.37 17 Sep 2010 [3380] xorriso/configure_ac.txt Version leap to libburn-0.8.7 18 Sep 2010 [3382] svn copy -m Branching for libisoburn release 0.6.0 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroSixZero 18 Sep 2010 [3383] svn delete http://svn.libburnia-project.org/libisoburn/branches/ZeroSixZero Removed falsely named branch 18 Sep 2010 [3384] svn copy -m Branching for libisoburn release 0.6.2 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroSixTwo 2010.09.18.120001 [3385] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.6.2 18 Sep 2010 [3386] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 2010.09.18.210001 [3387] libisoburn/isoburn.c Fixed a bug with uninitialized variable introduced by rev 3364 19 Sep 2010 [3388] xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.6.2 - 2010.09.18.210001 libisoburn novelties: * New API function isoburn_igopt_set_part_offset() * Hiding all non-API symbols from the linker by use of --version-script * Now with history of release notes in ./ChangeLog file. xorriso novelties: * Bug fix: Regression with -hardlinks and -compliance old_rr, 0.4.2, Aug 2009 * New option -preparer_id, -as mkisofs options -p and -preparer * New -boot_image specifier emul_type=none|hard_disk|floppy * New boot_image boot specs partition_offset, partition_hd_cyl, partition_sec_hd * Made behavior of -as mkisofs with unknown options more similar to original * New -as mkisofs option -hard-disk-boot, enabled -b without -no-emul-boot * New -as mkisofs option -e from Fedora genisoimage * New -as mkisofs options -partition_offset,-partition_hd_cyl,-partition_sec_hd 2010.09.19.134042 [3389] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.6.3 2010.09.19.135354 [3390] libisoburn/isoburn.c Fixed a bug with uninitialized variable introduced by rev 3364 19 Sep 2010 [3391] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-0.6.3 - 2010.09.19.135354 19 Sep 2010 [3392] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/ZeroSixTwo http://svn.libburnia-project.org/libisoburn/tags/ZeroSixTwo 2010.09.21.142424 [3393] acinclude.m4 configure.ac Makefile.am On Linux: Run ldconfig during make install,if not --disable-ldconfig-at-install 2010.09.21.150325 [3394] acinclude.m4 configure.ac Makefile.am On Linux: More tolerance towards failures of ldconfig 22 Sep 2009 [3396] README Mentioned --disable-ldconfig-at-install 2010.09.23.135551 [3400] Makefile.am xorriso/make_xorriso_standalone.sh + doc/partition_offset.wiki Explaining the partition offset feature 2010.09.24.110727 [3406] configure.ac libisoburn/libisoburn.h Requiring libburn-0.8.7 now 2010.09.24.110841 [3407] xorriso/drive_mgt.c Reporting BD spare area info with -list_formats 29 Sep 2010 [3409] doc/partition_offset.wiki Updated partition offset wike about GRUB experiment 2010.09.29.091541 [3410] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver ChangeLog New API calls isoburn_igopt_attach_jte() and isoburn_igopt_detach_jte() 2010.09.29.092348 [3411] configure.ac xorriso/xorriso.h libisoburn/libisoburn.ver xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_i_o.c xorriso/emulators.c xorriso/lib_mgt.c xorriso/write_run.c ChangeLog New -as mkisofs options -jigdo-* and -md5-list as of genisoimage 2010.09.29.092537 [3412] xorriso/configure_ac.txt Enabled use of libjte for GNU xorriso 2010.09.29.124423 [3413] xorriso/write_run.c Avoided trailing 64 kB alignment with -compliance no_emul_toc 2010.09.29.152827 [3414] xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/emulators.c xorriso/lib_mgt.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/convert_man_to_html.sh ChangeLog New option -jigdo 2010.09.29.164121 [3415] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/lib_mgt.c xorriso/text_io.c xorriso/aux_objects.h xorriso/aux_objects.c Enabled -status -jigdo 2010.09.30.074208 [3416] xorriso/lib_mgt.c Switched from old libjte API names to new ones 2010.10.01.190235 [3417] xorriso/xorriso_private.h xorriso/base_obj.c Made two non-public parameters independent of Xorriso_with_libjtE ------------------------------------ cycle - xorriso-0.6.3 - 2010.10.01.190235 * New API calls isoburn_igopt_attach_jte() and isoburn_igopt_detach_jte() * New -as mkisofs options -jigdo-* and -md5-list as of genisoimage * New option -jigdo 2010.10.02.121147 [3418] xorriso/emulators.c xorriso/lib_mgt.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -as mkisofs option -jigdo-template-compress 2010.10.02.150240 [3419] xorriso/lib_mgt.c Added a blank to xorriso Preparer Id string 2010.10.02.164306 [3420] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.37 now 2010.10.02.210023 [3421] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver ChangeLog New API call isoburn_igopt_set_tail_blocks() 2010.10.02.210305 [3422] xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Letting libisofs do the padding with -jigdo, rather than disabling padding 2010.10.03.082304 [3423] xorriso/opts_d_h.c xorriso/emulators.c xorriso/lib_mgt.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -as mkisofs options -checksum_algorithm_iso, -checksum_algorithm_template 03 Oct 2010 [3424] ChangeLog Forgot to commit ChangeLog 2010.10.03.171305 [3425] xorriso/lib_mgt.c Reacting with FATAL event on failure to create libjte_handle 03 Oct 2010 [3426] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Corrected xorriso docs about meaning of -jigdo-map and .md5 file addresses ------------------------------------ cycle - xorriso-0.6.3 - 2010.10.03.171305 * New API call isoburn_igopt_set_tail_blocks() * New -as mkisofs options -checksum_algorithm_iso, -checksum_algorithm_template 2010.10.04.112234 [3427] xorriso/opts_d_h.c xorriso/emulators.c xorriso/lib_mgt.c xorriso/write_run.c Enabled use of libjte message list 2010.10.04.113140 [3428] xorriso/lib_mgt.c Silenced compiler warning about unused variable 2010.10.04.155110 [3429] libisoburn/libisoburn.h libisoburn/burn_wrap.c libisoburn/libisoburn.ver ChangeLog Tests for version of libjte, new API call isoburn_libjte_req() 2010.10.04.155226 [3430] xorriso/lib_mgt.c Reporting libjte version with option -version 2010.10.05.180016 [3431] xorriso/iso_manip.c Fixed a potential SIGSEGV by pointer offset -1. Detected by valgrind. 2010.10.06.162230 [3432] xorriso/write_run.c Fixed a theoretically possible race condition of burn end and ISO generator end 2010.10.07.114916 [3433] libisoburn/burn_wrap.c xorriso/base_obj.c xorriso/lib_mgt.c xorriso/write_run.c Preparing for GNU xorriso to include libjte source 7 Oct 2010 [3434] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Enhanced xorriso docs about meaning of .md5 file addresses 2010.10.07.180935 [3435] libisoburn/burn_wrap.c xorriso/base_obj.c xorriso/lib_mgt.c xorriso/write_run.c Corrected compile problems introduced with rev 3433 2010.10.08.093338 [3436] libisoburn/burn_wrap.c Fixed inability to compile without enabled libjte. Introduced by rev 3429. 8 Oct 2010 [3437] COPYRIGHT Removed unrelated copyright statements from libisoburn COPYRIGHT 2010.10.12.103027 [3438] libisoburn/libisoburn.h libisoburn/isoburn.c Allowed to set System Area type via isoburn_igopt_set_system_area() 2010.10.12.103157 [3439] xorriso/xorriso_private.h xorriso/opts_a_c.c xorriso/emulators.c Provisorily: -boot_image mips_path= , -as mkisofs -mips-boot. No Eltorito then. 2010.10.12.183125 [3440] xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/opts_a_c.c xorriso/emulators.c xorriso/iso_img.c Lifted the ban to combine MIPS Big Endian boot with El Torito 2010.10.13.170641 [3441] xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/emulators.c xorriso/iso_img.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Polished and documented MIPS booting option 2010.10.15.112050 [3442] xorriso/xorriso_private.h xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/emulators.c xorriso/iso_img.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 -boot_image mipsel_path= , -as mkisofs -mipsel-boot. 2010.10.15.121533 [3443] xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_makefile_am.txt xorriso/COPYRIGHT_gnu_xorriso xorriso/README_gnu_xorriso xorriso/AUTHORS_gnu_xorriso xorriso/xorriso_eng.html Included libjte in GNU xorriso 2010.10.15.125440 [3444] libisoburn/burn_wrap.c xorriso/base_obj.c xorriso/lib_mgt.c xorriso/configure_ac.txt United macro Xorriso_jte_standalonE with macro Xorriso_standalonE ------------------------------------ cycle - xorriso-0.6.3 - 2010.10.15.125440 * New API call isoburn_libjte_req() * New bootspecs mips_path= and mipsel_path= for Debian MIPS releases * New -as mkisofs options -mips-boot and -mipsel-boot 2010.10.18.155353 [3446] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver New API calls isoburn_igopt_set_partition_img, isoburn_igopt_get_partition_img 2010.10.18.160132 [3448] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/emulators.c xorriso/iso_img.c xorriso/write_run.c libisoburn/libisoburn.ver xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New option -append_partition, -as mkisofs -append_partition ------------------------------------ cycle - xorriso-0.6.3 - 2010.10.18.160132 * New option -append_partition, -as mkisofs -append_partition 2010.10.19.104814 [3449] xorriso/xorriso.h xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Minor adjustments ------------------------------------ cycle - xorriso-0.6.3 - 2010.10.19.104814 2010.10.19.170704 [3451] ChangeLog xorriso/write_run.c Avoiding potential endless write pacifier loop. Introduced with rev 3432. 2010.10.20.075221 [3452] xorriso/write_run.c Cancelling libisofs write thread in case of early libburn end 2010.10.20.170430 [3461] xorriso/configure_ac.txt Version leap to libburn-0.8.9 2010.10.20.170546 [3462] configure.ac libisoburn/libisoburn.h Requiring libburn-0.8.8 now 2010.10.21.180343 [3463] xorriso/drive_mgt.c Corrected -check_media handling of last track on BD-R resp. BD-ROM 2010.10.23.162213 [3464] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.38 now 2010.10.23.162302 [3465] xorriso/configure_ac.txt Adapted GNU xorriso to version leap libisofs-0.6.39 26 Oct 2010 [3466] svn copy -m Branching for libisoburn release 0.6.4 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroSixFour 2010.10.26.100001 [3467] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.6.4 26 Oct 2010 [3468] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.6.4 - 2010.10.26.100001 * New API call isoburn_libjte_req() * New API calls isoburn_igopt_attach_jte() and isoburn_igopt_detach_jte() * New API call isoburn_igopt_set_tail_blocks() * New API call isoburn_libjte_req() * New option -jigdo * New -as mkisofs options -jigdo-* and -md5-list as of genisoimage * New -as mkisofs options -checksum_algorithm_iso, -checksum_algorithm_template * New bootspecs mips_path= and mipsel_path= for Debian MIPS releases * New -as mkisofs options -mips-boot and -mipsel-boot * New option -append_partition, -as mkisofs -append_partition 2010.10.26.104219 [3469] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.6.5 26 Oct 2010 [3470] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 26 Oct 2010 [3471] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/ZeroSixFour http://svn.libburnia-project.org/libisoburn/tags/ZeroSixFour ------------------------------------ cycle - xorriso-0.6.5 - 2010.10.26.104219 2010.10.27.063628 [3472] xorriso/emulators.c Bug fix: -as mkisofs -print-size printed the size but also produced ISO image 2010.10.27.063705 [3473] xorriso/emulators.c New -as mkisofs option -joliet-long ------------------------------------ cycle - xorriso-0.6.5 - 2010.10.27.063705 * Bug fix: -as mkisofs -print-size printed the size but also produced ISO image * New -as mkisofs option -joliet-long 2010.10.29.164503 [3475] xorriso/write_run.c Added necessary isoburn_cancel_prepared_write() calls ------------------------------------ cycle - xorriso-0.6.5 - 2010.10.29.164503 ------------------------------------ cycle - xorriso-0.6.5 - 2010.11.01.113243 * Build problem fix on Linux 2.4 in GNU xorriso libjte/checksum.c 2010.11.01.191542 [3477] xorriso/iso_manip.c Allowed to perform -rm_r / 2010.11.03.081124 [3478] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/drive_mgt.h xorriso/drive_mgt.c xorriso/findjob.c xorriso/read_run.c Testing for abort file during -check_media 2010.11.05.143916 [3470] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver New API calls isoburn_igopt_set_disc_label(), isoburn_igopt_get_disc_label() 2010.11.05.144616 [3480] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/emulators.c xorriso/iso_img.c xorriso/write_run.c New bootspec sparc_label=, new -as mkisofs options -sparc-boot , -sparc-label 05 Nov 2010 [3481] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Documentation changes of rev 3480 ------------------------------------ cycle - xorriso-0.6.5 - 2010.11.05.144616 * New API calls isoburn_igopt_set_disc_label(), isoburn_igopt_get_disc_label() * New bootspec sparc_label=, new -as mkisofs options -sparc-boot , -sparc-label 2010.11.06.133454 [3482] libisoburn/libisoburn.h xorriso/write_run.c ChangeLog Declared that isoburn_cancel_prepared_write() waits until iso write thread ends 2010.11.21.065727 [3484] xorriso/make_xorriso_standalone.sh Included jigdo-gen-md5-list in GNU xorriso ------------------------------------ cycle - xorriso-0.6.5 - 2010.11.21.065727 2010.11.23.193708 [3485] xorriso/iso_img.c Improved behavior with loading multiple boot images of identical content 2010.11.23.200346 [3486] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/lib_mgt.c xorriso/write_run.c Avoided to bother libjte if no output paths are given ------------------------------------ cycle - xorriso-0.6.5 - 2010.11.23.200346 2010.11.24.100845 [3487] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New pseudo extension isoburn_igopt_will_cancel to avoid MISHAP event 2010.11.24.101056 [3488] xorriso/write_run.c Making use of isoburn_igopt_will_cancel with xorriso -print_size ------------------------------------ cycle - xorriso-0.6.5 - 2010.11.24.101056 2010.11.27.134702 [3489] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.39 now 2010.11.30.093929 [3490] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c libisoburn/data_source.c libisoburn/libisoburn.ver New API calls isoburn_ropt_set_displacement(), isoburn_ropt_get_displacement() 2010.11.30.094310 [3491] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/drive_mgt.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver ChangeLog New option -displacement 2010.11.30.104719 [3492] xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/disk_ops.h xorriso/disk_ops.c Improved messages at the end of partially failed -extract runs 2010.11.30.112050 [3493] xorriso/opts_p_z.c Processing output of -as mkisofs by -pkt_output if enabled 2010.11.30.170112 [3494] xorriso/parse_exec.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Clarified occasions of -mark message and removed surplus -mark message emitter 2010.12.01.173750 [3495] libisoburn/libisoburn.h Clarified description of isoburn_ropt_set_displacement() 2010.12.02.063209 [3496] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso_main.c xorriso/opts_d_h.c xorriso/emulators.c xorriso/iso_img.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 No -rollback in -as mkisofs -print-size. New flag in Xorriso_change_is_pending. 2010.12.02.111029 [3497] xorriso/xorrisoburn.h xorriso/opts_d_h.c xorriso/disk_ops.c xorriso/iso_manip.c xorriso/findjob.h xorriso/findjob.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New action estimate_size for -find and -findx ------------------------------------ cycle - xorriso-0.6.5 - 2010.12.02.111029 * New API calls isoburn_ropt_set_displacement(), isoburn_ropt_get_displacement() * New option -displacement * New action estimate_size for -find and -findx 2010.12.04.174311 [3498] libisoburn/isofs_wrap.c Fixed a bug about read displacement introduced with rev 3490 2010.12.04.174431 [3499] xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Preliminarily banned writing while read displacement is non-zero 2010.12.06.094347 [3500] xorriso/opts_a_c.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Allowed writing with read displacement if target media is blank 2010.12.07.174921 [3501] xorriso/iso_img.c Made -rom_toc_scan emul_off work with -mount ... sbsector ... 2010.12.08.194225 [3510] xorriso/opts_d_h.c Fixed a potential bug introduced with rev 3497 2010.12.08.194325 [3511] xorriso/iso_img.c Fixed a bug introduced with rev 3501 08 Dec 2010 [3512] xorriso/configure_ac.txt GNU xorriso version leap to libburn-0.9.1 2010.12.10.123534 [3513] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.40 now 2010.12.10.123634 [3514] xorriso/configure_ac.txt Adapted GNU xorriso to version leap libisofs-0.6.41 11 Dec 2010 [3515] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Clarification about -md5, -check_media and -find ... -damaged 12 Dec 2010 [2516] svn copy -m Branching for libisoburn release 0.6.6 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/ZeroSixSix 2010.12.12.090001 [3517] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.6.6 12 Dec 2010 [3518] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-0.6.6 - 2010.12.12.090001 * New API calls isoburn_igopt_set_disc_label(), isoburn_igopt_get_disc_label() * New API calls isoburn_ropt_set_displacement(), isoburn_ropt_get_displacement() * Bug fix: -as mkisofs -print-size printed the size but also produced ISO image * Build problem fix on Linux 2.4 in GNU xorriso libjte/checksum.c * New -as mkisofs option -joliet-long * New bootspec sparc_label=, new -as mkisofs options -sparc-boot , -sparc-label * New option -displacement 2010.12.12.120823 [3519] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-0.6.7 12 Dec 2010 [3520] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 12 Dec 2010 [3521] svn move -m Promoted branch to tag ttp://svn.libburnia-project.org/libisoburn/branches/ZeroSixSix http://svn.libburnia-project.org/libisoburn/tags/ZeroSixSix ------------------------------------ cycle - xorriso-0.6.7 - 2010.12.12.120823 2010.12.13.123226 [3523] configure.ac xorriso/configure_ac.txt Prepending ./configure generated options to CFLAGS rather than appending them 2010.12.22.133742 [3524] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver New API call isoburn_igopt_set_untranslated_name_len() 2010.12.22.133936 [3525] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -compliance options untranslated_names , untranslated_name_len= 2010.12.22.151542 [3526] xorriso/emulators.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -as mkisofs option -untranslated_name_len 23 Dec 2010 [3527] + doc/faq.wiki Took source file of libburnia-project.org/wiki/FAQ into SVN 2010.12.23.190134 [3529] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/emulators.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 ChangeLog New -compliance option iso_9660_1999, -as mkisofs option -iso-level 4 ------------------------------------ cycle - xorriso-0.6.7 - * New API call isoburn_igopt_set_untranslated_name_len() * New -compliance options untranslated_names , untranslated_name_len= * New -as mkisofs option -untranslated_name_len * New -compliance option iso_9660_1999, -as mkisofs option -iso-level 4 24 Dec 2010 [3530] doc/faq.wiki New q in faq 2010.12.24.135515 [3531] libisoburn/libisoburn.h Mentioned ISO 9660 level 3 in libisoburn API 2010.12.25.071212 [3532] configure.ac libisoburn/libisoburn.h Requiring libisofs-0.6.41 now 2010.12.25.071522 [3533] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/write_run.c xorriso/emulators.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -compliance option iso_9660_level=number 2010.12.25.091515 [3534] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New isoburn_igopt_set_relaxed() option isoburn_igopt_allow_dir_id_ext 2010.12.25.092828 [3535] xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -compliance option allow_dir_id_ext 2010.12.25.094309 [3536] xorriso/emulators.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -as mkisofs option -disallow_dir_id_ext, allow_dir_id_ext is default ------------------------------------ cycle - xorriso-0.6.7 - 2010.12.25.094309 * New -compliance option iso_9660_level=number * New -compliance option allow_dir_id_ext * New -as mkisofs option -disallow_dir_id_ext 2010.12.28.093144 [3538] xorriso/write_run.c Made -print_size take into account the toc emulation on blank overwriteables 2010.12.28.123348 [3539] xorriso/emulators.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Disabled TOC emulation with -as mkisofs. May be re-enabled by --emul-toc. ------------------------------------ cycle - xorriso-0.6.7 - 2010.12.28.123348 * Disabled TOC emulation with -as mkisofs. May be re-enabled by --emul-toc. 2010.12.29.104047 [3540] xorriso/emulators.c Bug fix: -as mkisofs -print-size did not account for -partition_offset ------------------------------------ cycle - xorriso-0.6.7 - 2010.12.29.104047 * Bug fix: -as mkisofs -print-size did not account for -partition_offset 30 Dec 2010 [3541] doc/partition_offset.wiki Mentioned Debian daily and weekly builds as examples of partiton_offset 2011.01.04.193042 [3543] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Default -abort_on value is now "FAILURE", for dialog it stays "FATAL" 2011.01.06.111034 [3544] xorriso/parse_exec.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Changed dialog default -abort_on value to "NEVER" 6 Jan 2011 [3545] ChangeLog Updated change log ------------------------------------ cycle - xorriso-0.6.7 - 2011.01.06.111034 * Default -abort_on value is now "FAILURE" with batch and "NEVER" with dialog 2011.01.16.182558 [3557] configure.ac libisoburn/libisoburn.h Requiring libisofs-1.0.0 and libburn-1.0.0 now 16 Jan 2011 [3558] xorriso/configure_ac.txt Adapted GNU xorriso to version leap libisofs-1.0.1 , libburn-1.0.1 16 Jan 2011 [3559] doc/partition_offset.wiki Updated partition offset wiki text about --no-emul-toc 16 Jan 2011 [3560] svn copy -m "Branching for libisoburn release 1.0.0" \ http://svn.libburnia-project.org/libisoburn/trunk \ http://svn.libburnia-project.org/libisoburn/branches/1.0.0 2011.01.16.193257 [3562] xorriso/emulators.c xorriso/opts_p_z.c Updated copyright messages to 2011 2011.01.16.200001 [3563] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-1.0.0 16 Jan 2011 [3564] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.0.0 - 2011.01.16.200001 * New API call isoburn_igopt_set_untranslated_name_len() * New -compliance options untranslated_names , untranslated_name_len= * New -as mkisofs option -untranslated_name_len * New -compliance option iso_9660_1999, -as mkisofs option -iso-level 4 * New -compliance option iso_9660_level=number * New -compliance option allow_dir_id_ext * New -as mkisofs option -disallow_dir_id_ext * Disabled TOC emulation with -as mkisofs. May be re-enabled by --emul-toc. * Default -abort_on value is now "FAILURE" with batch and "NEVER" with dialog * Bug fix: -as mkisofs -print-size did not account for -partition_offset 2011.01.16.193257 [3561] xorriso/emulators.c xorriso/opts_p_z.c Updated copyright messages to 2011 2011.01.17.092111 [3565] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-1.0.1 17 Jan 2011 [3566] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 17 Jan 2011 [3567] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.0.0 http://svn.libburnia-project.org/libisoburn/tags/1.0.0 ------------------------------------ cycle - xorriso-1.0.1 - 2011.01.17.092111 2011.01.18.154443 [3568] libisoburn/isoburn.h Avoiding <stdint.h> if not available. Trying to use <inttypes.h> in that case. 2011.01.18.154717 [3569] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/lib_mgt.c xorriso/write_run.c Avoiding <stdint.h> if not available. Trying to use <inttypes.h> in that case. ------------------------------------ cycle - xorriso-1.0.1 - 2011.01.18.154717 * Removed compiler obstacles of GNU xorriso on Solaris 9 18 Jan 2011 [3571] doc/faq.wiki Small change in FAQ wiki text 19 Jan 2011 [3572] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Mentioned missing mkisofs -old-root functionality in man page ------------------------------------ cycle - xorriso-1.0.1 - 2011.01.24.141621 * Bug fix: ECMA-119 standards violation with Volume Descriptor Set Terminator 2011.01.26.132843 [3573] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New isoburn_igopt_set_extensions() option isoburn_igopt_old_empty 2011.01.26.133107 [3574] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -compliance option old_empty 26 Jan 2011 [3575] configure.ac Requiring libisofs-1.0.1 now ------------------------------------ cycle - xorriso-1.0.1 - 2011.01.26.133107 * New isoburn_igopt_set_extensions() option isoburn_igopt_old_empty 2011.01.26.184140 [3576] libisoburn/isoburn.c Followed name change in the yet unstable part of libisofs API 2011.01.26.210359 [3577] xorriso/emulators.c New -as mkisofs option --old-empty ------------------------------------ cycle - xorriso-1.0.1 - * New -as mkisofs option --old-empty 2011.01.31.135917 [3578] xorriso/iso_manip.c Bug fix: Option -mkdir yielded SIGSEGV due to a NULL pointer 2011.02.01.185830 [3579] xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/iso_manip.c libisoburn/libisoburn.ver xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorriso_eng.html ChangeLog New option -clone 2011.02.02.174154 [3580] xorriso/xorrisoburn.h xorriso/iso_manip.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Disallowed -clone to overwrite existing nodes 2011.02.04.191922 [3581] xorriso/xorriso.h xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/iso_manip.c xorriso/sfile.c libisoburn/libisoburn.ver xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New option -cp_clone 2011.02.07.182947 [3582] xorriso/cmp_update.c xorriso/sort_cmp.c Do not regard file as changed just because no ino was recorded 2011.02.07.184304 [3583] xorriso/xorrisoburn.h xorriso/opts_p_z.c xorriso/emulators.c xorriso/iso_tree.h xorriso/iso_tree.c xorriso/iso_manip.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -as mkisofs options -root , -old-root, --old-root-no-md5, --old-root-no-ino, --old-root-dev 2011.02.07.202851 [3584] xorriso/emulators.c xorriso/iso_manip.c xorriso/sort_cmp.c xorriso/iso_tree.h xorriso/iso_tree.c Some corrections of the previous commit 2011.02.08.133624 [3585] xorriso/emulators.c -as mkisofs: Enabled reading of xattr and md5 before image loading 2011.02.12.171423 [3587] xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/emulators.c xorriso/cmp_update.h xorriso/cmp_update.c xorriso/disk_ops.c xorriso/iso_manip.h xorriso/iso_manip.c xorriso/findjob.h xorriso/lib_mgt.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -find actions update_merge, rm_merge, clear_merge. -cp_clone now merges. 2011.02.12.172930 [3588] xorriso/cmp_update.c Reacted on compiler warning about uninitialized variable 2011.02.14.090737 [3590] xorriso/base_obj.c xorriso/parse_exec.c xorriso/filters.c xorriso/write_run.c Closed memory leaks detected by valgrind 14 Feb 2011 [3591] xorriso/xorriso_eng.html ChangeLog Updated change log ------------------------------------ cycle - xorriso-1.0.1 - 2011.02.14.090737 * Bug fix: Option -mkdir yielded SIGSEGV due to a NULL pointer * New options -clone and -cp_clone * New -find actions update_merge, rm_merge, clear_merge * New -as mkisofs options -root , -old-root * New -as mkisofs options --old-root-no-md5, --old-root-no-ino, --old-root-dev 2011.02.19.112243 [3593] xorriso/emulators.c Small adjustments in mkisofs emulation Bug fix: -as mkisofs -l resp. -full-iso9660-filenames did not work 2011.02.19.112439 [3594] xorriso/filters.c Updated copyright 2011.02.19.112541 [3595] xorriso/xorriso.texi Corrections in xorriso man page 19 Feb 2011 [3596] xorriso/xorriso_eng.html ChangeLog Updated change log 19 Feb 2011 [3597] xorriso/xorriso.info xorriso/xorriso.1 info document and man page for recent texi corrections ------------------------------------ cycle - xorriso-1.0.1 - 2011.02.19.112541 * Bug fix in libisofs: Images produced with -for_backup might be unreadable and also fail -check_md5 verification. * Bug fix: mkisofs emulation options -l , -full-iso9660-filenames did not work. 2011.02.21.130729 [3598] xorriso/emulators.c Correction about -as mkisofs --old-root-no-ino 2011.02.22.073504 [3599] xorriso/opts_a_c.c xorriso/iso_manip.c Issueing messages with -clone and -cp_clone 2011.02.22.073641 [3600] xorriso/emulators.c Corrected a bug that prevented -as mkisofs option -output 2011.02.22.123730 [3601] xorriso/emulators.c Made -as mkisofs ignore option -disable-deep-relocation rather than failing 2011.02.22.143131 [3602] xorriso/emulators.c New -as mkisofs option -max-iso9660-filenames 22 Feb 2011 [3603] xorriso/xorriso_eng.html ChangeLog Updated change log ------------------------------------ cycle - xorriso-1.0.1 - 2011.02.22.143131 * New -as mkisofs option -max-iso9660-filenames 23 Feb 2011 [3608] svn copy -m Branching for libisoburn release 1.0.2 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.0.2 2011.02.23.140001 [3609] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-1.0.2 23 Feb 2011 [3610] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.0.2 - 2011.02.23.140001 * Bug fix: Images produced with -for_backup might be unreadable and also fail -check_md5 verification. * Bug fix: mkisofs emulation options -l , -full-iso9660-filenames did not work. * Bug fix: Option -mkdir yielded SIGSEGV due to a NULL pointer * Bug fix: ECMA-119 standards violation with Volume Descriptor Set Terminator * Removed compiler obstacles of GNU xorriso on Solaris 9 * New isoburn_igopt_set_extensions() option isoburn_igopt_old_empty * New options -clone and -cp_clone * New -find actions update_merge, rm_merge, clear_merge * New -as mkisofs option -max-iso9660-filenames * New -as mkisofs option --old-empty * New -as mkisofs options -root , -old-root * New -as mkisofs options --old-root-no-md5, --old-root-no-ino, --old-root-dev 2011.02.23.202632 [3616] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-1.0.3 23 Feb 2011 [3617] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-1.0.3 - 2011.02.23.202632 23 Feb 2011 [3618] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.0.2 http://svn.libburnia-project.org/libisoburn/tags/1.0.2 2011.02.24.191908 [3620] libisoburn/isofs_wrap.c Corrected a flaw found by George Danchev with cpp 2011.02.24.192029 [3621] xorriso/opts_d_h.c xorriso/text_io.c xorriso/filters.c Corrected flaws found by George Danchev with cpp 2011.02.26.100158 [3622] xorriso/emulators.c Bug fix: -as mkisofs -old-root did not work with -graft-points 2011.02.28.190842 [3623] xorriso/emulators.c Bug fix: -as mkisofs -partition_hd_cyl had no effect 28 Feb 2011 [3624] xorriso/make_xorriso_1.c When producing man page from texi: substitute @minus{} within @item 2011.03.01.145125 [3626] libisoburn/burn_wrap.c Bug fix: -as mkisofs -C attempts to read volume header of blank media 2011.03.01.152159 [3627] libisoburn/burn_wrap.c Installed a test against non-zero msc1 on blank input drives 2011.03.02.093009 [3628] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/emulators.c xorriso/write_run.c Counting padding as part of the image with -as mkisofs 2011.03.02.094049 [3629] xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/text_io.c New -padding modes "included" and "excluded" 02 Mar 2011 [3630] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Updated documentation 02 Mar 2011 [3631] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.0.3 - 2011.03.02.094049 * Bug fix: -as mkisofs -old-root did not work with -graft-points * Bug fix: -as mkisofs -partition_hd_cyl had no effect * Bug fix: -as mkisofs -C attempted to read volume header of blank media * Bug fix: isohybrid image size was not aligned to cylinder boundary * New -padding modes "included" and "appended" 2011.03.03.181601 [3632] libisoburn/libisoburn.h libisoburn/isoburn.c New bits 8 and 9 with options of isoburn_igopt_set_system_area() 2011.03.03.181850 [3633] xorriso/xorriso_private.h xorriso/opts_a_c.c xorriso/emulators.c xorriso/text_io.c New bootspec partition_cyl_align=, new -as mkisofs option -partition_cyl_align 03 Mar 2011 [3634] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Updated documentation 04 Mar 2011 [3635] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Clarifications about cylinder size and alignment 04 Mar 2011 [3626] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.0.3 - 2011.03.03.181850 * New bootspec partition_cyl_align=, new -as mkisofs option -partition_cyl_align 2011.03.05.090434 [3637] xorriso/parse_exec.c xorriso/emulators.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -as mkisofs and -as cdrecord option --no_rc 2011.03.05.141534 [3638] + xorriso/xorrisofs.texi + xorriso/xorrisofs.info + xorriso/xorrisofs.1 Makefile.am xorriso/make_xorriso_standalone.sh xorriso/xorriso_makefile_am.txt xorriso/make_docs.sh Own man page and info document for xorrisofs 05 Mar 2011 [3639] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Mentioned man xorrisofs in man xorriso 2011.03.06.153741 [3640] xorriso/emulators.c Helptext of -as mkisofs now points to man xorrisofs 06 Mar 2011 [3641] xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Polished xorrisofs documentation 2011.03.07.101547 [3642] xorriso/opts_a_c.c xorriso/emulators.c Bug fix: -as mkisofs did not properly unescape target part of pathspecs 07 Mar 2011 [3643] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.0.3 - 2011.03.07.101547 * New -as mkisofs and -as cdrecord option --no_rc * Own man page and info document for xorrisofs * Bug fix: -as mkisofs did not properly unescape target part of pathspecs 2011.03.09.071325 [3644] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c New isoburn_ropt_set_extensions() option isoburn_ropt_nomd5tag 2011.03.09.071502 [3645] xorriso/xorriso_private.h xorriso/opts_i_o.c xorriso/emulators.c xorriso/text_io.c xorriso/drive_mgt.c New -md5 modes load_check_off and load_check_on 08 Mar 2011 [3646] doc/partition_offset.wiki Updated partition offset wiki 2011.03.09.123847 [3647] xorriso/xorriso.h Marked options introduced since 0.5.8 by the promised @since tags 09 Mar 2011 [3648] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Corrections and updates of documentation 10 Mar 2011 [3649] xorriso/README_gnu_xorriso xorriso/make_xorriso_standalone.sh xorriso/convert_man_to_html.sh + xorriso/man_xorrisofs_to_html.sh + xorriso/man_xorriso_to_html.sh xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 xorriso/xorriso_eng.html HTML version of man xorrisofs 10 Mar 2011 [3654] svn copy -m Branching for libisoburn release 1.0.4 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.0.4 2011.03.10.110001 [3655] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-1.0.4 10 Mar 2011 [3656] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.0.4 - 2011.03.10.110001 * New isoburn_ropt_set_extensions() option isoburn_ropt_nomd5tag * Bug fix: xorrisofs did not work under growisofs -M (version 1.0.0 was ok) * Bug fix: -as mkisofs -C attempted to read volume header of blank media * Bug fix: -as mkisofs -old-root did not work with -graft-points * Bug fix: -as mkisofs -partition_hd_cyl had no effect * Bug fix: -as mkisofs did not properly unescape target part of pathspecs * Bug fix: isohybrid image size was not aligned to cylinder boundary * Bug fix: Compilation without zlib failed * New -padding modes "included" and "appended" * New bootspec partition_cyl_align=, new -as mkisofs option -partition_cyl_align * New -as mkisofs and -as cdrecord option --no_rc * Own man page and info document for xorrisofs 2011.03.10.135207 [3661] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-1.0.5 10 Mar 2011 [3662] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 10 Mar 2011 [3663] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.0.4 http://svn.libburnia-project.org/libisoburn/tags/1.0.4 ------------------------------------ cycle - xorriso-1.0.5 - 2011.03.10.135207 13 Mar 2011 [3668] COPYRIGHT Updated copyright year 2011.03.13.131305 [3669] configure.ac libisoburn/libisoburn.h Requiring libburn-1.0.5 2011.03.13.131557 [3670] libisoburn/burn_wrap.c libisoburn/isofs_wrap.c Prepared libisoburn for drive role 4 2011.03.21.093208 [3679] libisoburn/burn_wrap.c libisoburn/isofs_wrap.c Prepared libisoburn for drive role 5 2011.03.21.093705 [3680] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/write_run.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New option -early_drive_test 2011.03.21.113720 [3681] libisoburn/libisoburn.h libisoburn/burn_wrap.c libisoburn/libisoburn.ver New API call isoburn_set_truncate() 2011.03.21.113858 [3682] xorriso/write_run.c Made use of new API call isoburn_set_truncate 2011.03.21.165533 [3683] xorriso/xorriso_private.h xorriso/opts_d_h.c xorriso/drive_mgt.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -early_stdio_test option appendable_wo 2011.03.22.081408 [3684] libisoburn/burn_wrap.c Fixed use of uninitialized variable when blanking rol5 pseudo drives 24 Mar 2011 [3686] xorriso/README_gnu_xorriso Fixed typos in GNU xorriso readme file 2011.03.24.182518 [3688] libisoburn/burn_wrap.c libisoburn/isofs_wrap.c Better handling of pseudo-drive without read-permission 2011.03.25.191936 [3689] configure.ac libisoburn/libisoburn.h Requiring libisofs-1.0.5 2011.03.26.100519 [3690] xorriso/iso_manip.c Reporting target name if adding of new node fails with ISO_WRONG_ARG_VALUE 2011.03.26.143944 [3691] xorriso/write_run.c Unconditionally reporting iso_level with -status -compliance 2011.03.26.144233 [3692] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New relax option isoburn_igopt_joliet_long_names 2011.03.26.144541 [3693] xorriso/emulators.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -compliance option joliet_long_names 26 Mar 2011 [3694] xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Small adjustments to xorrisofs.texi 26 Mar 2011 [3695] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.0.5 - 2011.03.26.144541 * New API call isoburn_set_truncate() * New relax option isoburn_igopt_joliet_long_names * New option -early_stdio_test * New -compliance option joliet_long_names * -as mkisofs option -joliet-long is now fully functional * Burning DVD-R DAO with 2 kB size granularity rather than 32 kB 2011.03.26.203042 [3696] xorriso/iso_manip.c Interpreting new libisofs error codes ISO_RR_NAME_TOO_LONG ISO_RR_NAME_RESERVED 2011.03.29.150930 [3697] xorriso/iso_manip.c Interpreting new libisofs error code ISO_RR_PATH_TOO_LONG 2011.03.29.151144 [3698] xorriso/write_run.c Bug fix: -as mkisofs padding did not work (regression in 1.0.4) 30 Mar 2011 [3699] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.0.5 - 2011.03.29.151144 * Bug fix: -as mkisofs padding did not work (regression in 1.0.4) 2011.04.01.133116 [3700] xorriso/emulators.c Allowing double dashes with all long xorrisofs options 3 Apr 2011 [3701] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Clarified relation of startup files, -options_from_file and quoted input. 3 Apr 2011 [3702] xorriso/COPYRIGHT_gnu_xorriso xorriso/README_gnu_xorriso Updated info about GNU xorriso copyright and libjte 2011.04.03.094634 [3703] xorriso/xorriso.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New options -print_info and -print_mark 2011.04.04.064909 [3704] xorriso/write_run.c Bug fix: Options -gid and -uid had no effect 2011.04.04.071443 [3705] xorriso/drive_mgt.c Avoided to report Volume Id when aquiring drive with blank media 4 Apr 2011 [3706] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.0.5 - 2011.04.04.071443 * Bug fix: Options -gid and -uid had no effect * New options -print_info and -print_mark 2011.04.04.140752 [3707] Makefile.am xorriso/xorriso_makefile_am.txt xorriso/make_xorriso_standalone.sh + doc/startup_file.txt Added an example of a startup file 2011.04.05.072141 [3708] xorriso/xorriso_main.c xorriso/write_run.c Fixed a typo in messages and goto label 07 Apr 2011 [3709] xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Clarification about -as mkisofs pathspecs 07 Apr 2011 [3710] doc/partition_offset.wiki Updated partition offset wiki about cylinder alignment 08 Apr 2011 [3711] doc/faq.wiki FAQ about release version numbers 08 Apr 2011 [3716] svn copy -m Branching for libisoburn release 1.0.6 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.0.6 2011.04.08.193001 [3717] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-1.0.6 08 Apr 2011 [3718] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.0.6 - 2011.04.08.193001 * Bug fix: -as mkisofs padding did not work (regression in 1.0.4) * Bug fix: Options -gid and -uid had no effect * New API call isoburn_set_truncate() * New relax option isoburn_igopt_joliet_long_names * New option -early_stdio_test * New options -print_info and -print_mark * New -compliance option joliet_long_names * -as mkisofs option -joliet-long is now fully functional * Burning DVD-R DAO with 2 kB size granularity rather than 32 kB 2011.04.09.105219 [3723] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-1.0.7 09 Apr 2011 [3724] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 09 Apr 2011 [3725] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.0.6 http://svn.libburnia-project.org/libisoburn/tags/1.0.6 ------------------------------------ cycle - xorriso-1.0.7 - 2011.04.09.105219 2011.04.13.204236 [3726] xorriso/emulators.c Bug fix: mkisofs emulation could ignore options (regression in 0.1.6) 14 Apr 2011 [3727] svn copy -m Branching for libisoburn release 1.0.8 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.0.8 2011.04.15.073001 [3278] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-1.0.8 14 Apr 2011 [3729] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 2011.04.14.073001 [3730] xorriso/xorriso_timestamp.h ChangeLog xorriso/changelog.txt Corrected release date ----------------------------------- release - xorriso-1.0.8 - 2011.04.13.073001 * Bug fix: mkisofs emulation could ignore options (regression in 0.1.6) 2011.04.14.081721 [3731] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h Version leap to libisoburn-1.0.9 14 Apr 2011 [3732] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 14 Apr 2011 [3733] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.0.8 http://svn.libburnia-project.org/libisoburn/tags/1.0.8 ------------------------------------ cycle - xorriso-1.0.9 - 2011.04.14.081721 15 Apr 2011 [3734] doc/doxygen.conf.in Disabled HAVE_DOT in doxygen.conf 18 Apr 2011 [3736] doc/partition_offset.wiki doc/faq.wiki Mentioned success and failure reports of partition offset 2011.04.22.081847 [3737] libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c libisoburn/isofs_wrap.c Avoiding repeated PVD read attempts if first reading of PVD yielded error 2011.04.23.152348 [3738] xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/base_obj.c xorriso/lib_mgt.h xorriso/lib_mgt.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/write_run.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New option -signal_handling 23 Apr 2011 [3739] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.0.9 - 2011.04.23.152348 * New option -signal_handling 2011.04.27.143713 [3740] xorriso/iso_manip.c Saved some CPU cycles when adding wide directory trees 2011.04.27.150829 [3741] xorriso/read_run.c Downgraded extraction error from FAILURE to SORRY (for new -abort_on default) 2011.04.30.121138 [3742] xorriso/iso_manip.c Saved more CPU cycles by optimistic adding of file objects 2011.05.02.090908 [3744] xorriso/aux_objects.c Replaced some large local variables by other means which save stack space 2011.05.02.091632 [3745] xorriso/base_obj.c Replaced some large local variables by other means which save stack space 2011.05.02.100332 [3746] xorriso/check_media.c Replaced some large local variables by other means which save stack space 2011.05.02.121907 [3747] xorriso/cmp_update.c Replaced some large local variables by other means which save stack space 2011.05.02.191704 [3748] xorriso/disk_ops.c Replaced some large local variables by other means in xorriso/disk_ops.c 2011.05.02.191905 [3749] xorriso/cmp_update.c Better reporting of memory shortage with rev 3747 2011.05.02.211310 [3750] xorriso/drive_mgt.c Replaced some large local variables by other means in xorriso/drive_mgt.c 2011.05.02.211401 [3751] xorriso/xorriso.h Clarified and updated API documentation of Xorriso_prescan_args() 2011.05.03.063012 [3752] xorriso/drive_mgt.c Closed a memory leak found by valgrind 2011.05.03.085459 [3753] xorriso/write_run.c Closed memory leaks found by valgrind 2011.05.03.091059 [3754] xorriso/emulators.c Replaced some large local variables by other means in xorriso/emulators.c 2011.05.03.095849 [3755] xorriso/filters.c Replaced some large local variables by other means in xorriso/filters.c 2011.05.03.100744 [3756] xorriso/findjob.c Replaced some large local variables by other means in xorriso/findjob.c 2011.05.03.115103 [3757] xorriso/iso_img.c Replaced some large local variables by other means in xorriso/iso_img.c 2011.05.03.115146 [3758] libisoburn/burn_wrap.c Closed a memory leak found by valgrind 2011.05.03.115226 [3759] xorriso/findjob.c Closed a memory leak found by valgrind 2011.05.04.151605 [3760] xorriso/iso_manip.c Replaced some large local variables by other means in xorriso/iso_manip.c 2011.05.04.151721 [3761] xorriso/cmp_update.c Replaced some large local variables by other means in xorriso/cmp_update.c 2011.05.04.151820 [3762] xorriso/findjob.c Closed a memory leak found by valgrind 2011.05.04.152314 [3763] xorriso/drive_mgt.c xorriso/emulators.c Reacted on compiler warning about rev 3750 and 3754 2011.05.04.165840 [3764] xorriso/iso_tree.c Replaced some large local variables by other means in xorriso/iso_tree.c 2011.05.04.171628 [3765] xorriso/lib_mgt.c Replaced some large local variables by other means in xorriso/lib_mgt.c 2011.05.05.075233 [3766] xorriso/match.c Replaced some large local variables by other means in xorriso/match.c 2011.05.05.081436 [3767] xorriso/misc_funct.c Replaced some large local variables by other means in xorriso/misc_funct.c 2011.05.05.105152 [3768] xorriso/opts_a_c.c Replaced some large local variables by other means in xorriso/opts_a_c.c 2011.05.05.105310 [3769] xorriso/iso_img.c Closed a memory leak found by valgrind 2011.05.05.121640 [3770] xorriso/opts_d_h.c Replaced some large local variables by other means in xorriso/opts_d_h.c 2011.05.05.121957 [3771] xorriso/opts_d_h.c Reacted on compiler warning about rev 3770 2011.05.05.122855 [3772] xorriso/text_io.c Added newline character to output of -errfile_log plain -I 2011.05.05.163832 [3773] xorriso/opts_i_o.c Replaced some large local variables by other means in xorriso/opts_i_o.c 2011.05.05.163926 [3774] xorriso/opts_a_c.c Corrected a mistake introduced with rev 3768 2011.05.06.120600 [3775] xorriso/misc_funct.c Introduced flag bit1 with Text_shellsafe() for double sized target string 2011.05.06.120655 [3776] xorriso/opts_p_z.c Replaced some large local variables by other means in xorriso/opts_p_z.c 2011.05.06.120834 [3777] xorriso/disk_ops.c Corrected use of unitialized variables found by valgrind 2011.05.06.121218 [3778] xorriso/sfile.h Commited definition of Xorriso_alloc_meM which is needed since 30 revisions 2011.05.06.132058 [3779] xorriso/disk_ops.c xorriso/iso_tree.c xorriso/iso_manip.c xorriso/opts_i_o.c Made use of Text_shellsafe bit1 for messages with two file names 2011.05.06.151905 [3780] xorriso/parse_exec.c Replaced some large local variables by other means in xorriso/parse_exec.c 2011.05.07.104932 [3781] [3782] xorriso/iso_img.c Bug fix: -mount_opts "shared" worked only with -osirrox "o_excl_off" 2011.05.07.105142 [3783] xorriso/iso_img.c Enabled drive addresses of form mmc:/dev/srX with option -mount 2011.05.07.165758 [3784] xorriso/read_run.c Replaced some large local variables by other means in xorriso/read_run.c 2011.05.07.184625 [3785] xorriso/text_io.c Replaced some large local variables by other means in xorriso/text_io.c 2011.05.08.083929 [3786] xorriso/write_run.c Replaced some large local variables by other means in xorriso/write_run.c 2011.05.08.174805 [3787] xorriso/opts_a_c.c xorriso/cmp_update.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/sort_cmp.c xorriso/text_io.c xorriso/write_run.c Replaced some large local variables by other means 2011.05.09.155010 [3788] libisoburn/isoburn.c libisoburn/burn_wrap.c libisoburn/isofs_wrap.c Replaced some large local variables by other means in 2011.05.09.181239 [3789] xorriso/parse_exec.c xorriso/opts_p_z.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/iso_tree.c xorriso/iso_manip.c xorriso/sort_cmp.c xorriso/filters.c xorriso/write_run.c xorriso/match.c xorriso/text_io.c xorriso/findjob.c xorriso/misc_funct.c xorriso/sfile.c Reacted on -Wsign-compare warnings of gcc 2011.05.09.181449 [3790] libisoburn/isoburn.c libisoburn/isofs_wrap.c Reacted on -Wsign-compare warnings of gcc 2011.05.12.175155 [3797] configure.ac libisoburn/libisoburn.h Requiring libisofs-1.0.8 now 2011.05.12.175423 [3798] xorriso/configure_ac.txt GNU xorriso now contains libisofs-1.0.9 13 May 2011 [3799] xorriso/xorrisofs.texi Corrected a typo in man page of xorrisofs 13 May 2011 [3800] xorriso/make_xorriso_1.c xorriso/xorriso.texi xorriso/xorriso.1 Added a rule to the conversion prescription for man pages 13 May 2011 [3801] xorriso/xorrisofs.texi xorriso/xorrisofs.1 Added a rule to the conversion prescription for man pages 14 May 2011 [3802] xorriso/make_xorriso_1.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Patch by Mats Erik Anderson: wrap @command{} around the word xorriso 2011.05.16.132939 [3809] xorriso/parse_exec.c Bug fix: xorriso command -add_plainly "any" did not add all file sto the image 2011.05.16.133231 [3810] xorriso/sfile.h Replaced german macro parameter name by an english one 2011.05.16.134341 [3811] xorriso/xorriso.h xorriso/parse_exec.c Checking for unknown xorriso commands already in Xorriso_prescan_args() 16 May 2011 [3812] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Made xorriso version number visible in manuals 16 May 2011 [3813] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Adjusted line lengths to less than 80 chars in source of xorriso manual 17 May 2011 [3814] xorriso/make_xorriso_1.c Implemented texi-to-man translation rule about {...} recursion 17 May 2011 [3815] xorriso/xorriso.texi xorriso/xorrisofs.texi Added to the manual instructions for bug reporting 17 May 2011 [3816] xorriso/xorrisofs.texi Wrapped @command{} around the word xorrisofs in its manual 17 May 2011 [3817] xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.info xorriso/xorrisofs.1 Updated generated manuals 2011.05.17.115159 [3818] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.0.9 - 2011.05.17.115159 * Bug fix: -mount_opts shared worked only with -osirrox o_excl_off * Bug fix (libisofs): Production of MIPS bootable images caused SIGSEGV * Bug fix: xorriso command -add_plainly "any" did not add all files to the image 2011.05.17.151719 [3819] xorriso/xorriso.h xorriso/parse_exec.c xorriso/xorriso_main.c New flag bit1 of API call Xorriso_prescan_args() reports unknown commands 17 May 2011 [3820] xorriso/make_xorriso_1.c xorriso/xorriso.texi xorriso/xorrisofs.texi New rules for translating several texi @-commands to man page ------------------------------------ cycle - xorriso-1.0.9 - 2011.05.17.151719 2011.05.18.063511 [3821] xorriso/parse_exec.c Fixed refusal to run with command -as introduced by rev 3819 18 May 2011 [3822] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Polished node "Bugreport" in manuals ------------------------------------ cycle - xorriso-1.0.9 - 2011.05.18.063511 2011.05.19.111229 [3823] libisoburn/libisoburn.h Mentioned the need to define uint32_t when including libisoburn.h 2011.05.19.133735 [3824] xorriso/write_run.c Bug fix: The attempt to blank already blanked DVD-RW was not gracefully blocked 2011.05.19.133840 [3825] xorriso/check_media.c Replaced some large local variables by other means in xorriso/check_media.c 2011.05.19.133940 [3826] xorriso/opts_p_z.c Reacted on -Wsign-compare warnings of gcc 2011.05.19.134432 [3827] libisoburn/isofs_wrap.c Revoked experimental change which sneaked into SVN by rev 3737 20 May 2011 [3828] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.0.9 - 2011.05.19.134432 * Bug fix: The attempt to blank already blank DVD-RW was not gracefully blocked 2011.05.20.175842 [3829] xorriso/iso_img.c Reacted on compiler warning about inactivated if statement 2011.05.22.143652 [3832] configure.ac xorriso/configure_ac.txt Added options -Wextra -Wno-unused-parameter for gcc 2011.05.22.181525 [3833] xorriso/opts_a_c.c xorriso/opts_i_o.c xorriso/findjob.c Reacted on static code checker warning reported by George Danchev 2011.05.26.151635 [3841] configure.ac libisoburn/libisoburn.h Requiring libburn-1.0.7 now 2011.05.26.151744 [3842] xorriso/drive_mgt.c Report damaged track with "Media status :" 2011.05.29.095524 [3843] xorriso/drive_mgt.c Removed quotation marks in message introduced by rev3842 2011.05.29.100055 [3844] xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Improved error messages if DVD-RW media can only be written with -close on 2011.05.31.092902 [3845] xorriso/text_io.c With -status : always display setting of -abort_on 2011.06.01.165129 [3847] xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New option -close_damaged 2011.06.07.144548 [3857] xorriso/write_run.c Fixed a typo in a hint about fast blanked DVD-RW 8 Jun 2011 [3862] configure.ac Makefile.am xorriso/configure_ac.txt xorriso/xorriso_makefile_am.txt Introduced AC_CONFIG_MACRO_DIR() and ACLOCAL_AMFLAGS on advise of George Danchev 9 Jun 2011 [3865] bootstrap xorriso/xorriso_bootstrap.txt Added option -I . to aclocal in bootstrap script on advise of George Danchev ------------------------------------ cycle - xorriso-1.0.9 - 2011.06.15.153630 * New option -close_damaged * Bug fix (libisofs) : -as mkisofs -isohybrid-mbr without -no-pad was not cylinder aligned 2011.06.17.143813 [3867] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info README doc/faq.wiki doc/partition_offset.wiki xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorriso_eng.html ChangeLog Avoided the use of the term 'eventual' for the meaning 'if applicable'. 18 Jun 2011 [3872] svn copy -m Branching for libisoburn release 1.1.0 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.1.0 2011.06.18.123001 [3973] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Version leap to libisoburn-1.1.0 18 Jun 2011 [3874] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.1.0 - 2011.06.18.123001 * Bug fix: -mount_opts shared worked only with -osirrox o_excl_off * Bug fix: xorriso command -add_plainly "any" did not add all files to the image * Bug fix: The attempt to blank already blank DVD-RW was not gracefully blocked * Bug fix: -as mkisofs -isohybrid-mbr without -no-pad was not cylinder aligned * Bug fix: Production of MIPS bootable images caused SIGSEGV * New option -signal_handling * New option -close_damaged 2011.06.18.173208 [3879] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Version leap to libisoburn-1.1.1 18 Jun 2011 [3880] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 18 Jun 2011 [3881] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.1.0 http://svn.libburnia-project.org/libisoburn/tags/1.1.0 ------------------------------------ cycle - xorriso-1.1.1 - 2011.06.18.173208 ------------------------------------ cycle - xorriso-1.1.1 - 2011.06.19.210656 * Bug fix: GNU xorriso-1.1.0 compiled only on Linux, FreeBSD, and Solaris ------------------------------ release - xorriso-1.1.0.pl01 - 2011.06.20.110001 * Bug fix: GNU xorriso-1.1.0 compiled only on Linux, FreeBSD, and Solaris 22 Jun 2011 [3887] releng/releng_build_jigdo releng/README.releng Beginning to create a test suite for libisoburn and xorriso 27 Jun 2011 [3943] + releng/test_hardlinks New test test_hardlinks 27 Jun 2011 [3944] releng/TODO Added rules to releng/TODO, new options -keep, -clean_up, deprecated -clean 27 Jun 2011 [3945] [3948] [3952] [3957] releng/test_hardlinks Polished hardlink test 2011.06.27.160728 [3956] xorriso/opts_p_z.c Bug fix: -update_r scheduled non-existing files for hardlink update 2011.06.27.172423 [3958] xorriso/parse_exec.c Reporting commands and their arguments as DEBUG messages before execution 28 Jun 2011 [3964] releng/test_hardlinks + tmp Adapted test_hardlinks to newly defined general test rules 28 Jun 2011 [3965] releng/test_hardlinks Adjusted names of config variables in test_hardlinks 28 Jun 2011 [3968] releng/test_hardlinks - tmp + releng/tmp Option -fail for test_hardlinks to simulate failure, moved tmp to releng/tmp 28 Jun 2011 [3969] releng/test_hardlinks Removed suffix .result from tmp/test_hardlinks 2011.06.28.084751 [3970] A test with non-added file releng/tmp/test 28 Jun 2011 [3973] + releng/releng_generated_data - releng/tmp Renamed releng/tmp to releng/releng_generated_data 28 Jun 2011 [3974] releng/test_hardlinks Adapted test_hardlinks to newest agreements with George 28 Jun 2011 [3975] + releng/releng_hardlinks - releng/test_hardlinks Renamed test_hardlinks to releng_hardlinks 2011.07.02.153320 [3991] libisoburn/burn_wrap.c Reacted on warnings of -Wunused-but-set-variable 2011.07.03.085629 [4003] xorriso/aux_objects.c Reacted on warnings of -Wunused-but-set-variable 2011.07.03.091220 [4006] xorriso/misc_funct.c Reacted on warnings of -Wunused-but-set-variable 2011.07.03.091706 [4007] xorriso/misc_funct.c Reacted on warnings of -Wunused-but-set-variable 2011.07.03.092353 [4008] xorriso/disk_ops.c Reacted on warnings of -Wunused-but-set-variable 2011.07.03.093500 [4010] xorriso/parse_exec.c Reacted on warnings of -Wunused-but-set-variable 2011.07.03.093500 [4015] xorriso/sfile.c Reacted on warnings of -Wtype-limits 2011.07.03.181840 [4020] configure.ac libisoburn/libisoburn.h Requiring libsofs-1.1.1 now 2011.07.03.193627 [4029] xorriso/iso_tree.c xorriso/read_run.c Enabled extraction of the boot catalog file to disk filesystem 2011.07.03.194138 [4030] xorriso/opts_a_c.c xorriso/opts_i_o.c xorriso/opts_p_z.c Reacted on warnings of -Wunused-but-set-variable 2011.07.03.194723 [4031] xorriso/drive_mgt.c Reacted on warnings of -Wunused-but-set-variable 2011.07.03.203013 [4033] xorriso/opts_i_o.c Removed a premature reference to upcomming function 2011.07.04.092828 [4034] xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/drive_mgt.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New option -list_speeds 4 Jul 2011 [4035] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.1.1 - 2011.07.04.092828 * Bug fix: -update_r scheduled non-existing files for hardlink update * New option -list_speeds * Enabled extraction of the boot catalog file to disk filesystem 2011.07.04.105800 [4036] xorriso/iso_manip.c Reacted on warnings of -Wunused-but-set-variable 2011.07.04.105854 [4037] xorriso/write_run.c Reacted on warnings of -Wunused-but-set-variable 2011.07.04.121342 [4038] xorriso/read_run.c Reacted on warnings of -Wunused-but-set-variable 2011.07.04.121501 [4039] xorriso/filters.c Reacted on warnings of -Wunused-but-set-variable 2011.07.05.145103 [4062] libisoburn/libisoburn.h Made libisoburn.h ready for use with C++ without importing namespace "burn" 2011.07.06.190111 [4078] libisoburn/isofs_wrap.c Hopefully fixed Debian bug 632865: blank ISO images with drive role 5 2011.07.07.113930 [4082] libisoburn/isoburn.c libisoburn/isofs_wrap.c libisoburn/burn_wrap.c Bug fix: Since 1.0.6: Unreadable image produced by: xorrisofs ... >image.iso ------------------------------------ cycle - xorriso-1.1.1 - 2011.07.07.113930 * Bug fix: Since 1.0.6: Unreadable image produced by: xorrisofs ... >image.iso 7 Jul 2011 [4086] xorriso/xorriso_eng.html ChangeLog Updated change log and web page 8 Jul 2011 [4092] svn copy -m Branching for libisoburn release 1.1.2 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.1.2 2011.07.08.100001 [4094] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Version leap to libisoburn-1.1.2 8 Jul 2011 [4095] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.1.2 - 2011.07.08.100001 * Bug fix: -update_r scheduled non-existing files for hardlink update * New option -list_speeds * Enabled extraction of the boot catalog file to disk filesystem * Bug fix: Since 1.0.6: Unreadable image produced by: xorrisofs ... >image.iso 2011.07.08.131032 [4099] [4100] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Version leap to libisoburn-1.1.3 8 Jul 2011 [4101] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 8 Jul 2011 [4102] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.1.2 http://svn.libburnia-project.org/libisoburn/tags/1.1.2 ------------------------------------ cycle - xorriso-1.1.3 - 2011.07.08.131032 2011.07.10.112539 [4126] libisoburn/isofs_wrap.c Recognizing overwritable media, blanked by cdrskin --grow_overwriteable_iso 2011.07.11.175609 [4134] xorriso/drive_mgt.c Reacted on warnings of cppcheck 2011.07.12.092010 [4137] xorriso/drive_mgt.c Improved output of -list_speeds with CD drives. Empty list now cause SORRY. 2011.07.12.135452 [4146] xorriso/drive_mgt.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Improved output of -list_speeds with ROM media and ROM drives. 13 Jul 2011 [4158] [4159] [4160] + releng/manual_devices_ts Proposal for test ./manual_devices 14 Jul 2011 [4161] [4162] releng/manual_devices_ts + releng/inc/releng_getopts_ts.inc Proposal for test ./manual_devices and inc/releng_getopts_ts.inc 14 Jul 2011 [4163] releng/manual_devices_ts releng/inc/releng_getopts_ts.inc Prepared test proposal for option -f and for incomplete option -x 14 Jul 2011 [4165] releng/manual_devices_ts Issueing failure message and non-zero exit value if manual_devices_ts fails 14 Jul 2011 [4166] releng/inc/releng_getopts_ts.inc Corrected a misunderstanding about meaning of code piece 2011.07.15.083052 [4171] libisoburn/burn_wrap.c Reacted on warnings of cppcheck about libisoburn/burn_wrap.c 15 Jul 2011 [4172] releng/manual_devices Removed remnant reference to releng_getopts_ts.inc 15 Jul 2011 [4173] releng/inc/releng_getopts.inc Removed usage of getopts in releng_getopts.inc 15 Jul 2011 [4174] releng/inc/releng_getopts.inc Removed obsolete input checks from releng_getopts.inc 15 Jul 2011 [4175] releng/inc/releng_getopts.inc releng/manual_devices New function check_for_xorriso in releng_getopts.inc 15 Jul 2011 [4176] releng/inc/releng_getopts.inc Removed call to print_specific_help from releng_getopts.inc 15 Jul 2011 [4177] releng/inc/releng_getopts.inc Changed general option -k -c -f in releng_getopts.inc 17 Jul 2011 [4185] releng/run_all_releng Issueing pacifier messages in run_all_releng about logged line 17 Jul 2011 [4186] releng/run_all_releng Making run_all_releng recognize exit values again (fixing rev 4185) 17 Jul 2011 [4187] releng_printsize Issueing pacifier messages during tree generation of releng_printsize 17 Jul 2011 [4186] releng/run_all_releng Using a more elegant way to obtain pipe component exit value 17 Jul 2011 [4192] releng/run_all_releng Switched error message away from stderr, only pacifier remains on stderr 17 Jul 2011 [4194] releng/run_all_releng Added my copyright to run_all_releng 23 Jul 2011 [4208] [4209] svn mv releng/README releng/README.old releng/README Began to sketch an releng/README file 2011.07.24.193433 [4213] xorriso/drive_mgt.c Improved media summary with overwritable media and unknown content 2011.07.24.203919 [4214] xorriso/drive_mgt.c Improved -check_media with overwritable media and unknown content 2011.07.25.095843 [4215] xorriso/check_media.h xorriso/check_media.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -check_media option what=image 2011.07.25.103809 [4216] xorriso/drive_mgt.c Issueing a warning messages if BD-R with more than 299 sessions is loaded 27 Jul 2011 [4217] releng/auto_printsize Reduced runtime of auto_printsize and balanced xorriso versus genisoimage 2011.07.27.211423 [4218] xorriso/xorriso.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/drive_mgt.c xorriso/aux_objects.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New option -device_links 27 Jul 2011 [4219] releng/manual_devices Switched releng/manual_devices from -devices to -device_links 2011.07.28.111814 [4220] xorriso/drive_mgt.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Prefering dvd over cd with -device_links 28 Jul 2011 [4221] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.1.3 - 2011.07.28.111814 * New option -device_links 2011.07.28.192110 [4224] configure.ac libisoburn/libisoburn.h Requiring libburn 1.1.1 2011.07.28.195103 [4225] xorriso/drive_mgt.c Using libburn call burn_lookup_device_link() 30 Jul 2011 [4226] xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Small correction in xorrisofs man page 2011.07.31.091836 [4229] libisoburn/burn_wrap.c Bug fix: xorriso native mode on some drives wrote unreadble ISO images to CD 2011.07.31.094422 [4230] xorriso/xorrisoburn.h xorriso/opts_a_c.c xorriso/opts_p_z.c Allowed lower case severity names with -abort_on, -return_with, -report_about 2011.07.31.145832 [4231] xorriso/misc_funct.c Bug fix: -assert_volid did not work. Regression since version 1.1.0, rev 3767. 2011.08.01.130400 [4233] xorriso/drive_mgt.c xorriso/misc_funct.c New report line "Media blocks :" with option -toc 1 Aug 2011 [4234] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.1.3 - 2011.08.01.130400 * Bug fix: xorriso native mode on some drives wrote unreadble ISO images to CD * Bug fix: -assert_volid did not work. Regression since version 1.1.0. 2011.08.01.152915 [4236] xorriso/misc_funct.h Forgot to upload the header which defines Xorriso__to_upper 1 Aug 2011 [4238] releng/auto_isocontent Checking -assert_volid in auto_isocontent 3 Aug 2011 [4240] releng/README releng/template_new Added more text pieces to releng/README, updated template for new tests 3 Aug 2011 [4241] releng/inc/releng_getopts.inc Added option -h to general helptext of releng 4 Aug 2011 [4242] releng/template_new Further improvements to releng test template 4 Aug 2011 [4243] + releng/manual_burn New test releng/manual_burn 4 Aug 2011 [4244] releng/README releng/manual_burn Improvements about releng/manual_burn 5 Aug 2011 [4245] releng/auto_cxx releng/auto_isocontent releng/manual_burn releng/manual_isojigdo Made existing tests comply to upcomming prescriptions for failure messages 5 Aug 2011 [4246] releng/inc/releng_getopts.inc Made existing tests comply to upcomming prescriptions for failure messages 7 Aug 2011 [4248] releng/README More work on releng/README 07 Aug 2011 [4252] svn copy -m Branching for libisoburn release 1.1.4 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.1.4 2011.08.07.120001 [4253] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Version leap to libisoburn-1.1.4 07 Aug 2011 [4254] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 2011.08.08.071649 [4256] xorriso/read_run.c Improved error reporting with problems when extracting xattr or ACL 2011.08.08.070301 [4257] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/configure_ac.txt xorriso/xorriso_eng.html xorriso/xorriso_timestamp.h Requiring libisofs-1.1.4 (because of ACL extraction problem in 1.1.2) 08 Aug 2011 [4258] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.1.4 - 2011.08.08.070301 * New option -device_links * Bug fix: xorriso native mode on some drives wrote unreadble ISO images to CD * Bug fix: -assert_volid did not work. Regression since version 1.1.0. * Bug fix: -acl or -xattr worked with -extract only on Linux and FreeBSD 2011.08.08.124228 [4263] xorriso/read_run.c Improved error reporting with problems when extracting xattr or ACL 2011.08.08.124914 [4264] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Version leap to libisoburn-1.1.5 08 Aug 2011 [4265] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 08 Aug 2011 [4266] svn move -m 'Promoted branch to tag' http://svn.libburnia-project.org/libisoburn/branches/1.1.4 http://svn.libburnia-project.org/libisoburn/tags/1.1.4 ------------------------------------ cycle - xorriso-1.1.5 - 2011.08.08.124914 09 Aug 2011 [4267] releng/auto_isocontent Made releng/auto_isocontent ready for Solaris (with bash) 09 Aug 2011 [4268] releng/inc/releng_getopts.inc Made releng/manual_burn and releng/manual_devices work on FreeBSD 09 Aug 2011 [4269] releng/auto_cxx Made releng/auto_cxx ready for FreeBSD (with bash or "bash" linked to /bin/sh) 09 Aug 2011 [4270] releng/auto_printsize Made releng/auto_printsize ready for FreeBSD ("bash" linked to /bin/sh) 10 Aug 2010 [4271] releng/manual_isojigdo Made releng/manual_isojigdo ready for FreeBSD ("bash" linked to /bin/sh) 10 Aug 2010 [4272] releng/run_all_auto Made releng/run_all_auto ready for FreeBSD ("bash" linked to /bin/sh) 10 Aug 2010 [4273] releng/run_all_auto Added some newlines to output of releng/run_all_auto 11 Aug 2011 [4274] releng/manual_burn releng/manual_devices releng/README Option --priv_cmd for releng/manual_burn and releng/manual_devices 14 Aug 2011 [4275] releng/run_all_auto Avoided use of "let" in releng/run_all_auto in favor of "expr" 14 Aug 2011 [4276] releng/README releng/change_shell_to_use Opportunity to choose the shell that runs the releng test scripts 14 Aug 2011 [4277] releng/change_shell_to_use Changed a comment 2011.08.15.132616 [4278] xorriso/disk_ops.c Bug fix: -extract_single extracted directory content 2011.08.15.171835 [4279] xorriso/opts_i_o.c xorriso/read_run.c xorriso/disk_ops.c Bug fix: -extract was not immediately aborted if -abort_on was triggered 17 Aug 2011 [4282] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.1.5 - 2011.08.17.163150 * Bug fix: -extract_single extracted directory content * Bug fix: -extract was not immediately aborted if -abort_on was triggered * Bug fix: xorriso did not write to files in filesystems with >= 4 TB free space 18 Aug 2011 [4283] releng/inc/releng_getopts.inc Made cleanup message of releng less loud 2011.08.18.132218 [4284] configure.ac libisoburn/libisoburn.h Requiring libsofs-1.1.5 now 2011.08.18.151913 [4285] xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/lib_mgt.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c libisoburn/libisoburn.ver xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New option -list_extras 18 Aug 2011 [4286] releng/auto_isocontent Testing recording and extraction of ACLs and xattr if available 19 Aug 2011 [4288] releng/README releng/TODO Moved todo items from releng/README to releng/TODO 19 Aug 2011 [4290] releng/TODO Some "pro" arguments for the disputed items in releng/TODO 2011.08.19.110340 [4291] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.1.5 - 2011.08.19.110340 * Bug fix: libisofs: ACL entries of groups and of user id 0 were not properly recorded and cannot be restored * Bug fix: libisofs: No ACLs were recorded on FreeBSD * New option -list_extras 2011.08.23.104121 [4292] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/text_io.c xorriso/read_run.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info New -osirrox option strict_acl 23 Aug 2011 [4293] releng/auto_isocontent Made releng/auto_isocontent ready to test FreeBSD extattr 23 Aug 2011 [4294] releng/auto_isocontent Made releng/auto_isocontent work on filesystems with no xattr and ACL 23 Aug 2011 [4295] releng/auto_isocontent Made releng/auto_isocontent work with disabled xorriso ACL adapter 2011.08.24.072854 [4296] xorriso/iso_manip.c Reporting specific libisofs error if setting of ACL fails 2011.08.25.185950 [4297] xorriso/xorrisoburn.h xorriso/findjob.h xorriso/opts_d_h.c xorriso/iso_tree.c xorriso/iso_manip.c xorriso/disk_ops.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -find and -findx action list_extattr 29 Aug 2011 [4298] releng/auto_isocontent Small change about Linux getfattr usage in releng/auto_isocontent 29 Aug 2011 [4299] releng/manual_isojigdo Made releng/manual_isojigdo remove .iso file after failed download 29 Aug 2011 [4300] + releng/jigdo-gen-md5-list + releng/jigdo-gen-md5-list.1 xorriso/make_xorriso_standalone.sh Moved jigdo-gen-md5-list of GNU xorriso into libisoburn/releng 29 Aug 2011 [4301] releng/manual_isojigdo Using own jigdo-gen-md5-list to avoid restriction to Linux and FreeBSD 30 Aug 2011 [4303] releng/inc/releng_getopts.inc releng/auto_isocontent releng/README Reacting on xorriso failure in pipe test of releng/auto_isocontent 02 Sep 2011 [4304] configure.ac PKG_CHECK_MODULES test of ./configure now only with --enable-pkg-check-modules 2011.09.03.194744 [4305] configure.ac xorriso/configure_ac.txt Made PKG_CHECK_MODULES with --enable-libcdio conditional 2011.09.20.133438 [4309] xorriso/xorriso_eng.html ChangeLog xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.1.5 - 2011.09.20.133438 * New -osirrox option strict_acl * libisofs: Enabled recording and restoring of extattr on FreeBSD. * New -find and -findx action list_extattr * Worked around a collision with Linux udev which lets device links vanish 21 Sep 2011 [4312] xorriso/README_gnu_xorriso Mentioned configure-time options for udev anti-collision waiting time 2011.09.22.142118 [4313] xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/drive_mgt.c Re-aquiring drives by their burn_drive_convert_fs_adr() names 23 Sep 2011 [4314] README xorriso/README_gnu_xorriso Mentioned my sysadmin workarounds to cope with udisks on Debian 6.0.2 amd64 23 Sep 2011 [4315] Makefile.am releng/README releng/template_new Added releng test suite to release tarball 2011.09.23.131734 [4316] xorriso/drive_mgt.c Improved error message if xorriso shall open a vanished udev link 2011.09.23.135519 [4317] releng/manual_burn Let releng burn test tolerate vanished udev link and wait for its re-appearance 24 Sep 2011 [4318] releng/README Fixed typos in releng/README 25 Sep 2011 [4320] releng/README Small corrections in releng/README 27 Sep 2011 [4325] svn copy -m Branching for libisoburn release 1.1.6 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.1.6 2011.09.27.080001 [4326] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Version leap to libisoburn-1.1.6 27 Sep 2011 [4327] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.1.6 - 2011.09.27.080001 * Bug fix: -extract_single extracted directory content * Bug fix: -extract was not immediately aborted if -abort_on was triggered * Bug fix: xorriso did not write to files in filesystems with >= 4 TB free space * Bug fix: libisofs: ACL entries of groups and of user id 0 were not properly recorded and cannot be restored * Bug fix: libisofs: No ACLs were recorded on FreeBSD * New option -list_extras * New -osirrox option strict_acl * libisofs: Enabled recording and restoring of extattr on FreeBSD. * New -find and -findx action list_extattr * Workaround for collision with Linux udev which lets device links vanish 2011.09.27.133025 [4333] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Version leap to libisoburn-1.1.7 27 Sep 2011 [4334] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 27 Sep 2011 [4335] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.1.6 http://svn.libburnia-project.org/libisoburn/tags/1.1.6 ------------------------------------ cycle - xorriso-1.1.7 - 2011.09.27.133025 2011.10.05.075024 [4338] configure.ac libisoburn/libisoburn.h Requiring libburn 1.1.7 2011.10.05.085704 [4340] libisoburn/libisoburn.h libisoburn/burn_wrap.c New flag bit of API call isoburn_drive_aquire() to cause burn_drive_re_assess() 2011.10.05.172142 [4341] xorriso/drive_mgt.c Avoiding open-close cycles on MMC drives after blanking or burning 2011.10.05.173150 [4342] xorriso/opts_a_c.c xorriso/lib_mgt.c Allowed lowercase severity names for all occasions 06 Oct 2011 [4343] README Pointed readers of README to releng/README 08 Oct 2011 [4393] xorriso/man_xorriso_to_html.sh xorriso/man_xorrisofs_to_html.sh Replaced HTML minus by a flat ASCII minus in HTML man page generator 2011.10.09.162439 [4396] xorriso/drive_mgt.c Reacted on nitpicking of cppcheck 2011.10.13.105921 [4404] xorriso/drive_mgt.c Improved plausibility of xorriso media summary in case of unreliable readbility 13 Oct 2011 [4405] releng/manual_burn Removed potentially dangerous hint from releng/manual_burn 2011.10.14.094257 [4406] xorriso/xorriso.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c libisoburn/libisoburn.ver xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New option -sleep 2011.10.18.162119 [4407] xorriso/parse_exec.c xorriso/emulators.h xorriso/emulators.c Enabled xorrecord dev=- 2011.10.25.103839 [4408] README Makefile.am xorriso/README_gnu_xorriso xorriso/make_xorriso_standalone.sh xorriso/xorriso_makefile_am.txt xorriso/emulators.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 + xorriso/xorrecord.texi + xorriso/xorrecord.info + xorriso/xorrecord.1 xorriso/make_docs.sh xorriso/make_xorriso_1.c xorriso/convert_man_to_html.sh + xorriso/man_xorrecord_to_html.sh Info document and man page for xorrecord 25 Oct 2011 [4409] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.1.7 - * New option -sleep * Info document and man page for xorrecord 2011.10.25.160042 [4410] xorriso/drive_mgt.c Avoided to report "Supported modes: SAO TAO" with xorrecord -atip on ROM drives 2011.10.26.085213 [4411] xorriso/drive_mgt.c Let xorrecord -atip throw SORRY on empty tray, but report profile list of drive 26 Oct 2011 [4412] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Replaced several occurences of the word "media" by "medium" 2011.11.02.140829 [4420] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/burn_wrap.c libisoburn/isofs_wrap.c Replaced several occurences of the word "media" by "medium" 2011.11.02.142105 [4421] xorriso/xorrisoburn.h xorriso/check_media.h xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/drive_mgt.c xorriso/emulators.c xorriso/write_run.c Replaced several occurences of the word "media" by "medium" ------------------------------------ cycle - xorriso-1.1.7 - 2011.11.02.142105 2011.11.04.102805 [4423] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.1.7 - 2011.11.04.102805 * Bug fix: libburn misinterpreted mode page 2A 2011.11.09.131243 [4425] + doc/qemu_xorriso.wiki Added a wiki page about xorriso on qemu. Online: wiki/QemuXorriso ------------------------------------ cycle - xorriso-1.1.7 - 2011.11.09.131243 * Enabled out-of the box use of xorriso on Linux guest on qemu virtio-blk-pci 09 Nov 2011 [4426] xorriso/make_xorriso_standalone.sh Added wiki page about xorriso on qemu to the GNU xorriso tarball generator 09 Nov 2011 [4427] doc/qemu_xorriso.wiki Small corrections in wiki/QemuXorriso 2011.11.09.145155 [4428] Makefile.am Added wiki/QemuXorriso to libisoburn tarball generator 09 Nov 2011 [4429] doc/qemu_xorriso.wiki Mentioned how to use /dev/vda without /dev/sr1 link 10 Nov 2011 [4432] doc/qemu_xorriso.wiki Some clarifications in wiki/QemuXorriso 11 Nov 2011 [4433] doc/qemu_xorriso.wiki Some clarifications in wiki/QemuXorriso 13 Nov 2011 [4434] doc/qemu_xorriso.wiki Some clarifications in wiki/QemuXorriso 16 Nov 2011 [4438] doc/qemu_xorriso.wiki Some clarifications in wiki/QemuXorriso 16 Nov 2011 [4439] doc/qemu_xorriso.wiki Corrected a wrong statement in wiki/QemuXorriso 20 Nov 2011 [4445] svn copy -m Branching for libisoburn release 1.1.8 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.1.8 2011.11.20.173001 [4446] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.1.8 20 Nov 2011 [4447] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.1.8 - 2011.11.20.173001 * Info document and man page for xorrecord * New option -sleep * Enabled recognition of QEMU DVD-ROM 0.12 * Enabled out-of the box use of xorriso on Linux guest on qemu virtio-blk-pci 2011.11.21.081802 [4452] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.1.9 21 Nov 2011 [4453] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 21 Nov 2011 [4454] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.1.8 http://svn.libburnia-project.org/libisoburn/tags/1.1.8 ------------------------------------ cycle - xorriso-1.1.9 - 2011.11.21.081802 22 Nov 2011 [4456] doc/qemu_xorriso.wiki Updated QemuXorriso wiki about xorriso version requirements 23 Nov 2011 [4457] doc/qemu_xorriso.wiki Mentioned GNU Hurd qemu page as source of qemu knowledge 2011.11.24.093151 [4459] xorriso/iso_tree.c Worked around inconsistency of stat.st_rdev and dev_t on Debian mips, mipsel 2011.11.29.130622 [4461] libisoburn/libisoburn.ver Bug fix: libisoburn.ver had a duplicate function entry 2011.12.01.152236 [4464] xorriso/emulators.c xorriso/write_run.c xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 Made verbosity and exit value of xorrecord more similar to cdrecord resp. wodim 03 Dec 2011 [4472] Makefile.am Removing test/.libs with make clean 03 Dec 2011 [4473] Makefile.am Added ./bootstrap script to release tarball 03 Dec 2011 [4474] xorriso/xorriso_makefile_am.txt Removing xorriso/.libs test/.libs with make clean 2011.12.07.080625 [4480] Makefile.am xorriso/xorriso_makefile_am.txt Removed version.h from list of source files because generated by configure 2011.12.25.121944 [4513] xorriso/xorriso_makefile_am.txt Integrated new source file libburn/cdtext.c 28 Dec 2011 [4520] xorriso/compile_xorriso.sh Integrated new source file libburn/cdtext.c 2012.01.14.144233 [4565] configure.ac libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New relaxations isoburn_igopt_joliet_rec_mtime, isoburn_igopt_iso1999_rec_mtime 2012.01.14.144535 [4566] xorriso/xorriso.h xorriso/opts_p_z.c xorriso/emulators.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Using new libisoburn relaxations with "rec_mtime", enabling it with -as mkisofs 14 Jan 2012 [4567] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.1.9 - 2012.01.14.144535 * Bug fix: mkisofs emulation did not record mtime in ECMA-119 directories * Bug fix: Program abort while drive tray is loading led to endless loop * Bug fix: Solaris adapter mishandled write commands which failed on first try * Bug fix: libisoburn.ver had a duplicate function entry * New relaxations isoburn_igopt_joliet_rec_mtime, isoburn_igopt_iso1999_rec_mtime 2012.01.15.104012 [4569] xorriso/write_run.h xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Made -compliance "rec_mtime" default for xorriso 2012.01.18.093532 [4572] xorriso/drive_mgt.c Improved interpretation of -check_media min_lba= 2012.01.21.114233 [4586] delayed_commit/B20121.114249 xorriso/xorriso_private.h xorriso/base_obj.c xorriso/drive_mgt.c xorriso/read_run.c xorriso/text_io.c Reporting speed with -check_media 2012.01.21.173229 [4587] delayed_commit/B20121.173233 xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/opts_p_z.c xorriso/drive_mgt.c xorriso/read_run.c Reporting speed with -check_md5, -compare, -find, -compare_l, -update 2012.01.21.190312 [4587] delayed_commit/B20121.190324 xorriso/drive_mgt.c Curbed -check_media chunk_size= to 64 kB 2012.01.23.104642 [4588] xorriso/cmp_update.c xorriso/iso_tree.c Two files forgotten with rev 4587 24 Jan 2012 [4594] xorriso/make_xorriso_standalone.sh Added libisoburn ChangeLog to GNU xorriso tarball 25 Jan 2012 [4596] ChangeLog Updated ChangeLog 2012.01.25.153732 [4597] xorriso/text_io.c Made rev 4586 safe against division by 0 27 Jan 2012 [4602] svn copy -m Branching for libisoburn release 1.2.0 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.2.0 2012.01.27.120001 [4603] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.2.0 27 Jan 2012 [4604] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.2.0 - 2012.01.27.120001 * Bug fix: mkisofs emulation did not record mtime in ECMA-119 directories * Bug fix: Program abort while drive tray is loading led to endless loop * Bug fix: Solaris adapter mishandled write commands which failed on first try * Bug fix: libisoburn.ver had a duplicate function entry * New relaxations isoburn_igopt_joliet_rec_mtime, isoburn_igopt_iso1999_rec_mtime * Made -compliance "rec_mtime" default for xorriso 2012.01.27.155323 [4609] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.2.1 27 Jan 2012 [4610] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 27 Jan 2012 [4611] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.2.0 http://svn.libburnia-project.org/libisoburn/tags/1.2.0 ------------------------------------ cycle - xorriso-1.2.1 - 2012.01.27.155323 2012.01.31.130405 [4616] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c libisoburn/libisoburn.ver xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New commands -x, -list_arg_sorting 2012.02.01.122709 [4617] xorriso/drive_mgt.c Avoided lots of error messages when checking media in empty drive 2012.02.01.162935 [4618] xorriso/drive_mgt.c xorriso/text_io.c Reporting correct speed unit with -check_media use=outdev 2012.02.01.163200 [4619] xorriso/drive_mgt.c Allowed chunk_size= values up to 1024s 02 Feb 2012 [4620] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Polished man page about option -x 02 Feb 2012 [4621] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.1 - 2012.02.01.163200 * New commands -x, -list_arg_sorting 02 Feb 2012 [4622] xorriso/xorriso_eng.html Fixed a HTML bug in xorriso web page 02 Feb 2012 [4623] xorriso/man_xorriso_to_html.sh Updated generator script for HTML man page 2012.02.14.103107 [4629] libisoburn/libisoburn.h libisoburn/libisoburn.ver libisoburn/isoburn.h libisoburn/isofs_wrap.c libisoburn/isoburn.c New API calls isoburn_get_attached_start_lba(), isoburn_attach_start_lba() 2012.02.14.103256 [4630] xorriso/drive_mgt.c More accurate determination of size with check_media what=image 24 Feb 2012 [4633] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Some adjustments in xorriso man page 2012.02.25.154938 [4634] xorriso/opts_d_h.c xorriso/read_run.c xorriso/text_io.c Enabled speed display with command -extract 2012.02.25.194514 [4635] xorriso/disk_ops.c Bug fix: -osirrox on:sort_lba_on -extract from / restored nearly nothing 27 Feb 2012 [4636] xorriso/disk_ops.c Fixed a problem with directing an -extract to the / directory of disk 2012.02.27.150241 [4637] libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c New default permission on / directory: rwxr-xr-x 27 Feb 2012 [4638] xorriso/man_xorriso_to_html.sh Equipped chapters of xorriso HTML man page with link targets 27 Feb 2012 [4639] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.1 - 2012.02.27.150241 * New API calls isoburn_get_attached_start_lba(), isoburn_attach_start_lba() * Bug fix: -osirrox on:sort_lba_on -extract from / restored nearly nothing 2012.02.29.135454 [4640] xorriso/drive_mgt.c Fixed a problem after blanking, which was introduced by rev 4630. ------------------------------------ cycle - xorriso-1.2.1 - 2012.02.29.135454 2012.03.03.104637 [4641] xorriso/filters.c Bug fix: Setting file content filters did not mark image as changed for commit 2012.03.03.134008 [4642] xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/opts_p_z.c xorriso/emulators.c xorriso/drive_mgt.c xorriso/read_run.c xorriso/write_run.c Centralized the inquiry whether an image change is pending 2012.03.03.182917 [4643] xorriso/xorriso.h xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New command -changes_pending 2012.03.03.185100 [4644] xorriso/filters.c Revoked rev 4641. It is not a bug but a feature. E.g. for reading. 03 Mar 2012 [4645] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Mentioned peculiar -changes_pending behavior of filter attaching 2012.03.03.211636 [4646] xorriso/opts_d_h.c Corrected texts about "command", "argument", "parameter" 2012.03.04.094824 [4647] xorriso/iso_img.c Added a comment 2012.03.04.095952 [4648] xorriso/xorriso.h Added a comment 05 Mar 2012 [4649] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/man_xorriso_to_html.sh Changed xorriso.texi according to proposals by Tony Mancill 05 Mar 2012 [4650] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/man_xorriso_to_html.sh Revised the use of "option" and "ASCII" in xorriso.texi 05 Mar 2012 [4651] xorriso/README_gnu_xorriso Corrected orthographical error in GNU xorriso readme file 2012.03.05.145209 [4652] libisoburn/libisoburn.h Corrected orthographical errors in libisoburn.h 2012.03.05.145346 [4653] xorriso/xorriso.h Replaced many occurences of "option" by "command" in comments of xorriso.h ------------------------------------ cycle - xorriso-1.2.1 - 2012.03.05.145346 2012.03.05.204946 [4654] xorriso/emulators.c Added -hide-rr-moved to the list of ignored -as mkisofs options 2012.03.09.190043 [4655] xorriso/iso_manip.c Corrected missing linefeed in message of command -mkdir 2012.03.10.145124 [4657] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver New API calls isoburn_igopt_set_rr_reloc(), isoburn_igopt_get_rr_reloc() 2012.03.10.150003 [4658] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/write_run.c xorriso/text_io.c libisoburn/libisoburn.ver xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New command -rr_reloc_dir 2012.03.10.153518 [4659] xorriso/parse_exec.c Changed a degugging text about command parameters 2012.03.11.162050 [4660] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c libisoburn/data_source.c libisoburn/libisoburn.ver New API calls isoburn_ropt_set_data_cache(), isoburn_ropt_get_data_cache() 2012.03.11.164130 [4661] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/lib_mgt.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/text_io.c libisoburn/libisoburn.ver xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New command -data_cache_size 2012.03.12.180937 [4662] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs option -rr_reloc, implemented option -hide-rr-moved 2012.03.13.102621 [4663] xorriso/emulators.c Now ignoring -as mkisofs -no-split-symlink-components -no-split-symlink-fields 2012.03.14.152414 [4664] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Adjusted behavior of -as mkisofs option -D 14 Mar 2012 [4665] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Clarified xorriso documentation about deep directories 2012.03.14.195147 [4666] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Adjusted behavior of -as mkisofs option -l 14 Mar 2012 [4667] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.1 - 2012.03.14.195147 * New API calls isoburn_igopt_set_rr_reloc(), isoburn_igopt_get_rr_reloc() * New API calls isoburn_ropt_set_data_cache(), isoburn_ropt_get_data_cache() * New command -rr_reloc_dir * New command -data_cache_size * New -as mkisofs option -rr_reloc, implemented option -hide-rr-moved * Now ignoring -as mkisofs -no-split-symlink-components -no-split-symlink-fields 2012.03.21.141040 [4668] libisoburn/libisoburn.h Bug fix: Relaxation option joliet_rec_mtime and iso1999_rec_mtime had wrong values 2012.03.21.193055 [4669] libisoburn/data_source.c libisoburn/isofs_wrap.c Reacted on warnings of cppcheck 2012.03.21.193153 [4670] xorriso/parse_exec.c Reacted on warning of cppcheck 2012.03.21.193557 [4672] xorriso/text_io.c Reacted on warning of cppcheck 2012.03.22.084956 [4673] xorriso/emulators.c xorriso/misc_funct.h xorriso/misc_funct.c Bug fix: -as mkisofs without -graft-points could noy handle names with "=" 2012.03.22.102053 [4674] libisoburn/libisoburn.h libisoburn/isoburn.c New relaxation option allow_7bit_ascii 2012.03.22.102402 [4675] libisoburn/isoburn.h File which was omitted when committing rev. 4674 2012.03.22.103201 [4676] xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -compliance option 7bit_ascii 2012.03.22.103349 [4677] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Implemented -as mkisofs option -relaxed-filenames 22 Mar 2012 [4678] xorriso/make_xorriso_1.c Updated help text of texi-to-man converter 27 Mar 2012 [4679] configure.ac Demanding libisofs-1.2.1 with ./configure --enable-pkg-check-modules 28 Mar 2012 [4680] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.1 - 2012.03.22.103349 Bug fix: -as mkisofs without -graft-points could not handle names with "=" Bug fix: Relaxation options joliet_rec_mtime and iso1999_rec_mtime had wrong values 2 Apr 2012 [4688] svn copy -m Branching for libisoburn release 1.2.2 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.2.2 2012.04.02.133001 [4689] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.2.0 02 Apr 2012 [4690] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.2.2 - 2012.04.02.133001 * New API calls isoburn_get_attached_start_lba(), isoburn_attach_start_lba() * New API calls isoburn_igopt_set_rr_reloc(), isoburn_igopt_get_rr_reloc() * New API calls isoburn_ropt_set_data_cache(), isoburn_ropt_get_data_cache() * New commands -x, -list_arg_sorting * New command -rr_reloc_dir * New command -data_cache_size * New -as mkisofs option -rr_reloc, implemented option -hide-rr-moved * Now ignoring -as mkisofs -no-split-symlink-components -no-split-symlink-fields * Bug fix: -osirrox on:sort_lba_on -extract from / restored nearly nothing * Bug fix: -as mkisofs without -graft-points could not handle names with "=" * Bug fix: Relaxation options joliet_rec_mtime and iso1999_rec_mtime had wrong values 2012.04.02.192028 [4695] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.2.3 02 Apr 2012 [4696] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 02 Apr 2012 [4697] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.2.2 http://svn.libburnia-project.org/libisoburn/tags/1.2.2 ------------------------------------ cycle - xorriso-1.2.3 - 2012.04.02.192028 2012.04.06.173904 [4700] xorriso/write_run.c Avoided to mention autoformatting in message about missing -outdev at -commit 2012.04.10.071539 [4703] xorriso/emulators.c Now recognizing long -as cdrecord options with double dash 2012.04.11.163237 [4705] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/emulators.c xorriso/text_io.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs option -log-file 2012.04.19.073152 [4710] libisoburn/isofs_wrap.c Fixed a small memory leak in case of failed ISO image reading 2012.04.19.074422 [4711] configure.ac libisoburn/libisoburn.h Requiring libisofs 1.2.3 now 19 Apr 2012 [4712] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.3 - 2012.04.19.073152 * New -as mkisofs option -log-file * Bug fix: Memory corruption when reading bootable image that was truncated 2012.04.21.193504 [4723] xorriso/make_xorriso_standalone.sh releng/README releng/TODO Now copying libisoburn/releng into GNU xorriso 2012.04.22.100352 [4724] xorriso/make_xorriso_standalone.sh releng/README Removing old data from releng/releng_generated_data in GNU xorriso 2012.04.25.191110 [4727] xorriso/emulators.c Now recognizing fused single character -as mkisofs options without parameters 2012.04.28.161532 [4728] xorriso/emulators.c Now producing a FAILURE event with unknown mkisofs arguments which begin by dash 2012.05.01.075022 [4729] xorriso/drive_mgt.c xorriso/check_media.h xorriso/check_media.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -check_media option async_chunks= 2012.05.02.111016 [4730] xorriso/drive_mgt.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Made chunksize=32s default with -check_media 2012.05.22.121401 [4732] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New command -rockridge 2012.05.22.121743 [4733] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs option --norock 2012.05.24.070718 [4734] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c Preparations for ISO/HFS+ hybrid images by Vladimir Serbinenko 2012.05.24.071402 [4735] xorriso/xorriso_makefile_am.txt xorriso/compile_xorriso.sh xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/emulators.c xorriso/iso_img.c xorriso/iso_tree.c xorriso/iso_manip.c xorriso/disk_ops.c xorriso/write_run.c xorriso/text_io.c xorriso/misc_funct.c libisoburn/libisoburn.ver Preparations for ISO/HFS+ hybrid images by Vladimir Serbinenko 2012.05.24.093552 [4736] xorriso/xorriso_makefile_am.txt xorriso/compile_xorriso.sh Welcomimg three new libisofs source files by Vladimir Serbinenko 2012.05.25.084641 [4737] xorriso/xorrisoburn.h xorriso/iso_manip.c xorriso/emulators.c New (yet inofficial) -as mkisofs option -hfsplus-file-creator-type 2012.05.25.190220 [4738] xorriso/xorrisoburn.h xorriso/iso_manip.c xorriso/emulators.c New (yet inofficial) -as mkisofs options -hfs-bless and -hfs-bless-by 2012.05.26.212559 [4739] libisoburn/isoburn.c Closed a memory leak about system area buffer. Found by valgrind. 2012.05.27.165658 [4740] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver New API call isoburn_igopt_set_hfsp_serial_number() 2012.05.27.165938 [4741] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/emulators.c xorriso/write_run.c New (yet inofficial) -as mkisofs option -hfsplus-serial-no 2012.05.28.133310 [4742] xorriso/opts_a_c.c xorriso/emulators.c xorriso/text_io.c New (yet inofficial) -boot_image bootspec hfsplus_serial= 2012.05.30.153449 [4743] xorriso/opts_d_h.c xorriso/findjob.h xorriso/iso_manip.h xorriso/iso_manip.c New (yet inofficial) -find actions set/get_hfs_crtp, set/get_hfs_bless 2012.05.31.070528 [4745] xorriso/opts_d_h.c xorriso/iso_manip.c xorriso/findjob.h xorriso/findjob.c New (yet inofficial) -find tests -has_hfs_crtp, has_hfs_bless 2012.05.31.071033 [4746] xorriso/opts_d_h.c Corrected a mistake in previous revision 2012.06.05.103058 [4747] xorriso/write_run.c Closed a memory leak about -print_size, found by valgrind 2012.06.05.145849 [4748] xorriso/iso_manip.c Silenced harmless compiler warnings 2012.06.06.184910 [4749] xorriso/opts_d_h.c xorriso/emulators.c xorriso/iso_manip.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Updated documentation of xorriso and xorrisofs about HFS+ 2012.06.08.071018 [4750] xorriso/emulators.c xorriso/iso_manip.c xorriso/findjob.h xorriso/drive_mgt.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Made HFS creator, type, blessing persistent in AAIP isofs.hx isofs.hb 2012.06.10.083050 [4751] xorriso/emulators.c Fixed false error with -as mkisofs option -hfsplus. Introduced by rev 4750. 2012.06.10.184039 [4752] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c Provisory new image generation extension isoburn_igopt_fat 2012.06.10.184210 [4753] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/write_run.c xorriso/emulators.c Provisory new -as mkisofs option -fat 2012.06.12.113220 [4754] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver Provisory new API calls isoburn_igopt_set_prep_partition, isoburn_igopt_set_efi_bootp 2012.06.12.113552 [4755] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/write_run.c xorriso/emulators.c Provisory new -as mkisofs options -prep-boot-part, -efi-boot-part 2012.06.12.192702 [4756] libisoburn/libisoburn.h libisoburn/isoburn.c New system area sub type CHRP with isoburn_igopt_set_system_area() 2012.06.12.192817 [4757] xorriso/xorriso_private.h xorriso/emulators.c Provisory new -as mkisofs option -chrp-boot-part 2012.06.14.142958 [4758] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.3 - 2012.06.14.142958 * New API call isoburn_igopt_set_hfsp_serial_number() * New -check_media option async_chunks= * New command -rockridge * New xorriso command -hfsplus * New -find tests -has_hfs_crtp, has_hfs_bless * New -find actions set/get_hfs_crtp, set/get_hfs_bless * New -boot_image bootspec hfsplus_serial= * Command -hide allows hiding in HFS+ filesystem * New -as mkisofs option --norock * New -as mkisofs option -hfsplus * New -as mkisofs option -hfsplus-file-creator-type * New -as mkisofs options -hfs-bless and -hfs-bless-by * New -as mkisofs option -hfsplus-serial-no * New -as mkisofs option -hide-hfsplus, -hide-hfsplus-list >>> Provisory new image generation extension isoburn_igopt_fat >>> Provisory new -as mkisofs option -fat 14 Jun 2012 [4759] xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Correction in xorrisofs manual 14 Jun 2012 [4760] doc/faq.wiki Gave some hints about SCSI errors in faq.wiki 2012.06.15.085732 [4761] xorriso/write_run.c Provisorily banned the combination of -hfsplus and ISO image growing 2012.06.18.082701 [4763] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Documented -as mkisofs options *-boot-part 2012.06.18.112125 [4764] xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/emulators.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New bootspecs efi_boot_part, prep_boot_part, chrp_boot_part 18 Jun 2012 [4765] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.3 - 2012.06.18.112125 New API calls isoburn_igopt_set_prep_partition, isoburn_igopt_set_efi_bootp New -as mkisofs options -prep-boot-part, -efi-boot-part, -chrp-boot-part New -boot_image bootspecs efi_boot_part, prep_boot_part, chrp_boot_part 2012.06.18.181204 [4766] xorriso/iso_manip.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Pseudo blessing "none" to revoke any HFS+ blessing of a node ------------------------------------ cycle - xorriso-1.2.3 - 2012.06.19.093921 2012.06.20.190651 [4767] xorriso/xorriso_private.h xorriso/opts_a_c.c xorriso/emulators.c xorriso/iso_img.c xorriso/write_run.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs options -isohybrid-gpt-basdat, -isohybrid-gpt-hfsplus, -isohybrid-apm-hfsplus 20 Jun 2012 [4768] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.3 - 2012.06.20.190651 * New -as mkisofs options -isohybrid-gpt-basdat, -isohybrid-gpt-hfsplus, -isohybrid-apm-hfsplus 2012.06.21.203531 [4769] xorriso/opts_d_h.c xorriso/iso_manip.c xorriso/iso_tree.c xorriso/findjob.h xorriso/findjob.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -find test -disk_path 2012.06.21.204359 [4770] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Now expecting disk_path with -as mkisofs -hfs-bless 22 Jun 2012 [4771] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.3 - 2012.06.21.204359 * New -find test -disk_path 2012.06.25.125438 [4772] xorriso/drive_mgt.c Loaded images with HFS+ attributes were marked as having changes pending 2012.06.27.184552 [4773] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New API calls isoburn_igopt_set_hfsp_block_size() and isoburn_igopt_get_hfsp_block_size() 2012.06.27.184915 [4774] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_a_c.c orriso/opts_d_h.c xorriso/write_run.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -boot_image bootspecs hfsplus_block_size= and apm_block_size= 2012.06.27.192151 [4775] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs options -hfsplus-block-size and -apm-block-size 28 Jun 2012 [4776] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.3 - 2012.06.27.192151 * New API calls isoburn_igopt_set_hfsp_block_size() and isoburn_igopt_get_hfsp_block_size() * New -boot_image bootspecs hfsplus_block_size= and apm_block_size= * New -as mkisofs options -hfsplus-block-size and -apm-block-size 03 Jul 2012 [4777] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Mentioned HFS+ name mangling in man pages 2012.07.08.134305 [4779] libisoburn/burn_wrap.c Delegated write_type selection to caller of libisoburn 2012.07.08.134840 [4780] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/write_run.c xorriso/text_io.c libisoburn/libisoburn.ver xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New xorriso command -write_type 2012.07.08.135555 [4781] libisoburn/burn_wrap.c Reacted on compiler complaint about rev 4779 2012.07.08.171526 [4782] xorriso/emulators.c xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 New -as cdrecord options -tao -sao -dao 08 Jul 2012 [4783] xorriso/xorriso_eng.html ChangeLog Updated change log and web page 2012.07.08.200735 [4784] xorriso/write_run.c Re-introduced capability of multi-session on overwritables. Spoiled by rev 4780. 2012.07.10.082919 [4785] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c libisoburn/libisoburn.ver New API calls isoburn_igopt_set_write_type() , isoburn_igopt_get_write_type() 2012.07.10.083040 [4786] xorriso/write_run.c Write type selection via libisoburn API to avoid redundant decisions ------------------------------------ cycle - xorriso-1.2.3 - 2012.07.10.083040 * New xorriso command -write_type * New -as cdrecord options -tao -sao -dao * New API calls isoburn_igopt_set_write_type() , isoburn_igopt_get_write_type() 2012.07.12.163117 [4787] xorriso/iso_manip.c Bug fix: -update deleted MD5 of files of which only attributes changed 14 Jul 2012 [4788] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.3 - 2012.07.12.163117 * Bug fix: -update deleted MD5 of files of which only attributes had changed 20 Jul 2012 [4798] svn copy -m Branching for libisoburn release 1.2.4 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.2.4 2012.07.20.130001 [4799] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.2.4 20 Jul 2012 [4800] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.2.4 - 2012.07.20.130001 * New API call isoburn_igopt_set_hfsp_serial_number() * New API calls isoburn_igopt_set_prep_partition, isoburn_igopt_set_efi_bootp * New API calls isoburn_igopt_set_hfsp_block_size() and isoburn_igopt_get_hfsp_block_size() * New -check_media option async_chunks= * New xorriso command -write_type * New xorriso command -rockridge * New xorriso command -hfsplus * New -find tests -has_hfs_crtp, has_hfs_bless * New -find actions set/get_hfs_crtp, set/get_hfs_bless * New -find test -disk_path * New -boot_image bootspec hfsplus_serial= * New -boot_image bootspecs hfsplus_block_size= and apm_block_size= * New -boot_image bootspecs efi_boot_part, prep_boot_part, chrp_boot_part * Command -hide allows hiding in HFS+ filesystem * New -as cdrecord options -tao -sao -dao * New -as mkisofs option -log-file * New -as mkisofs option --norock * New -as mkisofs option -hfsplus * New -as mkisofs option -hfsplus-file-creator-type * New -as mkisofs options -hfs-bless and -hfs-bless-by * New -as mkisofs option -hfsplus-serial-no * New -as mkisofs options -hfsplus-block-size and -apm-block-size * New -as mkisofs option -hide-hfsplus, -hide-hfsplus-list * New -as mkisofs options -prep-boot-part, -efi-boot-part, -chrp-boot-part * New -as mkisofs options -isohybrid-gpt-basdat, -isohybrid-gpt-hfsplus, -isohybrid-apm-hfsplus * Bug fix: Memory corruption when reading bootable image that was truncated * Bug fix: -update deleted MD5 of files of which only attributes had changed 2012.07.20.184237 [4804] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.2.5 20 Jul 2012 [4805] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 20 Jul 2012 [4807] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.2.4 http://svn.libburnia-project.org/libisoburn/tags/1.2.4 ------------------------------------ cycle - xorriso-1.2.5 - 2012.07.20.184237 2012.07.25.181415 [4808] libisoburn/libisoburn.h libisoburn/burn_wrap.c Clarified role of drive when parsing already loaded ISO 9660 superblock 2012.07.25.182507 [4809] xorriso/write_run.c Bug fix: SIGSEGV by uninitialized local variable with -check_media patch_lba0="on". Regression by version 1.0.6 2012.07.25.184207 [4810] xorriso/write_run.c Small beautification of previous commit. 26 Jul 2012 [4812] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Corrected description of check_media patch_lba0="on" 14 Aug 2012 [4815] xorriso/man_xorrisofs_to_html.sh Corrected conversion from man xorrisofs to man_1_xorrisofs.html 2012.08.14.102632 [4816] libisoburn/libisoburn.h libisoburn/burn_wrap.c New flag bit 9 with isoburn_drive_aquire() 2012.08.14.103138 [4817] xorriso/xorriso_private.h xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -rom_toc_scan option "emul_wide" 15 Aug 2012 [4818] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Shortened oversized line in man page 2012.08.20.084354 [4819] xorriso/text_io.c Enhanced command -status to recognize -cdi 2012.09.08.082759 [4821] xorriso/text_io.c Fixed SIGSEGV introduced by rev 4819 2012.09.08.173754 [4822] xorriso/xorriso.h xorriso/parse_exec.c xorriso/sfile.h xorriso/sfile.c libisoburn/libisoburn.ver New API calls Xorriso_parse_line() and Xorriso__dispose_words() 2012.09.08.181144 [4823] xorriso/parse_exec.c libisoburn/libisoburn.ver Reacted on compiler warning about rev 4822 ------------------------------------ cycle - xorriso-1.2.5 - 2012.09.08.181144 * Bug fix: SIGSEGV by uninitialized local variable with -check_media patch_lba0="on". Regression by version 1.0.6 * New API calls Xorriso_parse_line() and Xorriso__dispose_words() 2012.09.09.181723 [4824] Makefile.am xorriso/make_xorriso_standalone.sh xorriso/xorriso_makefile_am.txt test/frontend_pipes_xorriso.c Demonstration program of xorriso at two pipes: test/frontend_pipes_xorriso.c 2012.09.11.092703 [4825] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/lib_mgt.c xorriso/text_io.c libisoburn/libisoburn.ver New API call Xorriso_fetch_outlists() 2012.09.11.122559 [4826] xorriso/text_io.c Disabled a not yet thread-safe part of the new API call 11 Sep 2012 [4827] test/frontend_pipes_xorriso.c Added copyright and licensing to demonstration program 2012.09.13.130910 [4830] xorriso/xorriso_private.h xorriso/parse_exec.c xorriso/base_obj.c xorriso/text_io.c xorriso/lib_mgt.c Made Xorriso_process_msg_queues() thread-safe 2012.09.14.175104 [4831] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorriso_main.c xorriso/base_obj.c xorriso/parse_exec.c xorriso/text_io.c xorriso/aux_objects.h xorriso/aux_objects.c libisoburn/libisoburn.ver New API call Xorriso_start_msg_watcher() 2012.09.15.095146 [4832] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/text_io.c Changed prototype of new API call Xorriso_start_msg_watcher() 2012.09.15.170346 [4833] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/text_io.c libisoburn/libisoburn.ver New API call Xorriso_peek_outlists() 2012.09.16.141845 [4834] xorriso/xorriso.h xorriso/text_io.c Made -pkt_output combinable with message output lists 2012.09.21.115522 [4835] xorriso/xorriso.h Small enhancement of API description 2012.09.21.120245 [4836] xorriso/opts_a_c.c xorriso/emulators.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -boot_image partition_cyl_align mode "all" 22 Sep 2012 [4837] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.5 - 2012.09.21.120245 * New API call Xorriso_fetch_outlists() * New API call Xorriso_peek_outlists() * New API call Xorriso_start_msg_watcher() * New -boot_image partition_cyl_align mode "all" 2012.09.29.173546 [4838] xorriso/parse_exec.c Corrected interpretation of flag in Xorriso_parse_line() 30 Sep 2012 [4389] test/frontend_pipes_xorriso.c Equipped frontend_pipes_xorriso.c with word parser code 2012.10.02.134601 [4840] configure.ac xorriso/configure_ac.txt Configuration for use of libcdio on cygwin. Thanks Rocky Bernstein. 03 Oct 2012 [4842] xorriso/README_gnu_xorriso Mentioned releng tests in README of GNU xorriso. 2012.10.03.124152 [4843] xorriso/xorriso.h xorriso/parse_exec.c Enabled use of Xorriso_parse_line() with xorriso==NULL 07 Oct 2012 [4844] xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Clarified documentation of -as mkisofs -isohybrid-gpt-basdat 07 Oct 2012 [4845] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.5 - 2012.10.07.110444 * Bug fix: -partition_offset 16 kept -isohybrid-gpt-basdat from writing MBR entries 0xef 12 Oct 2012 [4846] xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 man xorrisofs falsely described -p as option for publisher id. Must be -P. 2012.10.14.190352 [4847] xorriso/xorriso.h xorriso/parse_exec.c xorriso/lib_mgt.c libisoburn/libisoburn.ver New API call Xorriso__severity_cmp() 2012.10.19.081758 [4848] xorriso/xorriso.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/iso_manip.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New command -lns for creating symbolic links 19 Oct 2012 [4849] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.5 - 2012.10.19.081758 * New command -lns for creating symbolic links 2012.10.24.095235 [4851] xorriso/xorrisoburn.h xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/drive_mgt.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -blank mode prefix "force:" 2012.10.25.125559 [4853] xorriso/drive_mgt.c Improved -check_media handling of last two blocks in initial CD TAO session 2012.10.25.172830 [4855] xorriso/drive_mgt.c Ceased to count track gaps as read blocks with -check_media 05 Nov 2012 [4856] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Corrections in xorriso manual. Proposed by Paul Menzel. 05 Nov 2012 [4857] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Corrections in xorriso manual. Proposed by Paul Menzel. 2012.11.26.070047 [4861] xorriso/iso_manip.c Command -mv did not mark the emerging image as haveing pending changes 2012.11.26.070128 [4862] xorriso/drive_mgt.c Corrected a typo in a message text 2012.11.26.070212 [4863] xorriso/parse_exec.c Added help text for inofficial command -test 2012.11.26.154951 [4864] xorriso/parse_exec.c Avoided surplus warning messages with -iso_rr_pattern off -mv [wildcards] 2 Dec 2012 [4870] xorriso/README_gnu_xorriso Fixed a typo in GNU xorriso README. Thanks to Omega Weapon. 2012.12.06.132118 [4871] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_p_z.c xorriso/text_io.c libisoburn/libisoburn.ver New API calls Xorriso_sieve_add_filter, Xorriso_sieve_get_result, Xorriso_sieve_clear_results, Xorriso_sieve_dispose, Xorriso_sieve_big 2012.12.07.193658 [4872] xorriso/xorriso.h xorriso/text_io.c Some more filter rules for Xorriso_sieve_big 2012.12.08.175205 [4873] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/text_io.c xorriso/sfile.h xorriso/sfile.c libisoburn/libisoburn.ver xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New command -msg_op 11 Dec 2012 [4874] xorriso/xorriso_eng.html ChangeLog Updated change log and web page 11 Dec 2012 [4875] test/frontend_pipes_xorriso.c Demonstrated use of message sieve by piped frontend program 11 Dec 2012 [4876] test/frontend_pipes_xorriso.c Improved help text of piped frontend demo ------------------------------------ cycle - xorriso-1.2.5 - 2012.12.08.175205 * New -blank mode prefix "force:" * New API calls Xorriso_sieve_add_filter, Xorriso_sieve_get_result, Xorriso_sieve_clear_results, Xorriso_sieve_dispose, Xorriso_sieve_big * New command -msg_op 2012.12.13.193255 [4877] xorriso/xorriso.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c libisoburn/libisoburn.ver xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New command -toc_of 2012.12.15.092821 [4880] xorriso/xorriso.h libisoburn/libisoburn.h xorriso/lib_mgt.c libisoburn/libisoburn.ver New API call Xorriso__severity_list() 2012.12.15.093055 [4881] xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -msg_op opcodes "compare_sev" and "list_sev" 2012.12.16.131128 [4882] xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/text_io.h xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -msg_op opcode "parse_bulk" 2012.12.18.081434 [4883] xorriso/xorriso.h xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Command -toc_of modifier ":short" 2012.12.18.154505 [4884] xorriso/opts_p_z.c Bug fix: -rollback did not work if indev and outdev were empty 2012.12.18.185121 [4885] xorriso/text_io.c Corrected sieve filter rules for -tell_media_space 2012.12.19.210641 [4886] xorriso/opts_d_h.c Bug fix: -dialog "single_line" behaved like -dialog "on" 2012.12.20.202145 [4887] configure.ac xorriso/configure_ac.txt xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_i_o.c xorriso/opts_d_h.c xorriso/text_io.h xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver xorriso/compile_xorriso.sh New command -launch_frontend 2012.12.25.175536 [4888] xorriso/xorriso.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/text_io.h xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Changed the parameter rules for new command -launch_frontend 2012.12.25.180844 [4889] xorriso/text_io.c Xorriso_sieve_clear_results() dereferenced NULL if message sieve never started 27 Dec 2012 [4890] README Makefile.am xorriso/README_gnu_xorriso xorriso/xorriso_makefile_am.txt xorriso/make_xorriso_standalone.sh + frontend/ + frontend/xorriso-tcltk + frontend/README-tcltk + frontend/frontend_pipes_xorriso.c Proof-of-concept of a GUI frontend program. Written in Tcl/Tk. 27 Dec 2012 [4891] - test/frontend_pipes_xorriso.c Moved frontend_pipes_xorriso.c 27 Dec 2012 [4892] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.5 - 2012.12.27.100106 * New command -toc_of * New API call Xorriso__severity_list() * Bug fix: -rollback did not work if indev and outdev were empty * New command -launch_frontend * Proof-of-concept of a GUI frontend program: xorriso-tcltk written in Tcl/Tk. 28 Dec 2012 [4893] frontend/xorriso-tcltk frontend/README-tcltk Equiped xorriso-tcltk with help texts and enabled it to start xorriso by itself 28 Dec 2012 [4894] README xorriso/README_gnu_xorriso Updated general README files about xorriso-tcltk 2012.12.30.100913 [4895] xorriso/emulators.c xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 Corrected a typo in man page of xorrecord, tolerating--grow_over[w]rit[e]able_iso 2012.12.30.115258 [4896] frontend/README-tcltk frontend/xorriso-tcltk Primitive file tree browser for xorriso-tcltk (if package BWidget is available) 2012.12.30.195312 [4897] frontend/xorriso-tcltk Improved xorriso-tcltk file browser 2012.12.30.203203 [4898] frontend/xorriso-tcltk Improved xorriso-tcltk file browser ------------------------------------ cycle - xorriso-1.2.5 - 2012.12.30.203203 2012.12.31.100509 [4899] frontend/xorriso-tcltk Made main Help window independent of help window for GUI elements 2012.12.31.120152 [4900] frontend/xorriso-tcltk Changed layout of drive list and drive buttons 2012.12.31.173306 [4901] frontend/xorriso-tcltk Avoided an error message if xorriso and xorriso-tcltk had differing device addresses 2013.01.01.130149 [4902] frontend/xorriso-tcltk Added a menu for global access permission policy, improved pop-up window positioning 2013.01.01.135853 [4903] frontend/README-tcltk frontend/xorriso-tcltk Changed copyright year and switched xorriso-tcltk to BSD license 2013.01.01.152241 [4904] frontend/xorriso-tcltk Reduced horizontal size of xorriso-tcltk to fit into 1024 pixels 2013.01.02.183011 [4905] xorriso/parse_exec.c Removed obsolete test code and disabled other tests 2013.01.03.101633 [4906] frontend/README-tcltk frontend/xorriso-tcltk Improved xorriso-tcltk dialog about overwriting of existing files 2013.01.03.102627 [4907] frontend/xorriso-tcltk Changed xorriso-tcltk default to --click_to_focus 2013.01.03.125535 [4908] frontend/xorriso-tcltk Blocked GUI activities until yes/no window resp. error window are closed 2013.01.03.134011 [4909] frontend/xorriso-tcltk Consolidated overwrite switches in a single menu burron 2013.01.03.160118 [4910] frontend/xorriso-tcltk Removed surplus quotes around variable evaluation 2013.01.03.182504 [4911] frontend/xorriso-tcltk Equipped file browser with a second vertical scrollbar 2013.01.03.185545 [4912] frontend/xorriso-tcltk Moved and renamed the "Refresh avail:" button 2013.01.03.190805 [4913] frontend/xorriso-tcltk Gave the "Move to:" field a Return key binding 2013.01.04.094503 [4914] frontend/xorriso-tcltk Added start option --script_log_file. Gave file tree browser a fallback mode. 2013.01.04.140336 [4915] frontend/xorriso-tcltk Made script logging and log target addresses controllable from GUI 2013.01.04.143459 [4916] frontend/xorriso-tcltk Avoided redundant logging of -cd commands 2013.01.04.185925 [4917] xorriso/xorriso_private.h xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -osirrox settings "blocked" and "unblock" 2013.01.04.192238 [4918] frontend/xorriso-tcltk Gave xorriso-tcltk an opportunity to execute files with xorriso commands 2013.01.04.204305 [4919] frontend/xorriso-tcltk Small adjustments and bug fixes in xorriso-tcltk 2013.01.04.221013 [4920] frontend/xorriso-tcltk Small adjustments and bug fixes in xorriso-tcltk 2013.01.05.105029 [4921] frontend/xorriso-tcltk Small adjustments and bug fixes in xorriso-tcltk ------------------------------------ cycle - xorriso-1.2.5 - 2013.01.05.105029 * New -osirrox settings "blocked" and "unblock" 2013.01.05.214951 [4922] xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Made -msg_op "parse" obey -backslash_codes. 2013.01.05.220937 [4923] frontend/README-tcltk frontend/xorriso-tcltk Hardened xorriso-tcltk against filenames with newlines 2013.01.06.093550 [4924] frontend/xorriso-tcltk Avoided unnecessary rebuilding of file browser tree with Up and Down buttons 2013.01.06.095957 [4925] frontend/README-tcltk frontend/xorriso-tcltk New xorriso-tcltk start option --no_extract 2013.01.06.100954 [4926] frontend/xorriso-tcltk Decided for xorriso as info source about loal filesystem 2013.01.06.102339 [4927] frontend/xorriso-tcltk Disabled "Extract to disk" button if extraction is banned 2013.01.06.154027 [4928] frontend/xorriso-tcltk Small adjustments in xorriso-tcltk 2013.01.06.171902 [4929] xorriso/match.c Corrected an error message which has undesired effects with fronetnds 2013.01.06.174508 [4930] frontend/xorriso-tcltk Removed the ""File browser text field" switch 2013.01.06.194655 [4931] frontend/xorriso-tcltk Worked around the 4 reserved characters of Bwidget Tree 2013.01.06.205810 [4932] frontend/xorriso-tcltk Promoted drive line labels to buttons 2013.01.07.190934 [4933] frontend/xorriso-tcltk Some polishing of xorriso-tcltk 2013.01.07.215658 [4934] xorriso/text_io.c xorriso/disk_ops.c Closed memory leaks found by valgrind 08 Jan 2013 [4940] svn copy -m Branching for libisoburn release 1.2.6 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.2.6 2013.01.08.103001 [4941] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.2.6 08 Jan 2012 [4942] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 2013.01.08.103001 [4943] libisoburn/libisoburn.h libisoburn.h was missing in commit 4941 2013.01.08.103001 [4944] Makefile.am Had to add xorriso-tcltk to EXTRA_DIST although it is in bin_SCRIPTS 2013.01.08.103001 [4945] xorriso/xorriso_makefile_am.txt Had to add xorriso-tcltk to EXTRA_DIST although it is in bin_SCRIPTS ----------------------------------- release - xorriso-1.2.6 - 2013.01.08.103001 =============================================================================== * Bug fix: SIGSEGV by uninitialized local variable with -check_media patch_lba0="on". Regression by version 1.0.6 * Bug fix: -partition_offset 16 kept -isohybrid-gpt-basdat from writing MBR partition table entries of type 0xef * Bug fix: -rollback did not work if indev and outdev were empty * New API calls Xorriso_parse_line() and Xorriso__dispose_words() * New API calls Xorriso_fetch_outlists() and Xorriso_peek_outlists() * New API call Xorriso_start_msg_watcher() * New API calls Xorriso__severity_cmp() and Xorriso__severity_list() * New API calls Xorriso_sieve_add_filter, Xorriso_sieve_get_result, Xorriso_sieve_clear_results, Xorriso_sieve_dispose, Xorriso_sieve_big * New -boot_image partition_cyl_align mode "all" * New -blank mode prefix "force:" * New -osirrox settings "blocked" and "unblock" * New command -lns for creating symbolic links * New command -toc_of * New command -msg_op * New command -launch_frontend * Proof-of-concept of a GUI frontend program: xorriso-tcltk written in Tcl/Tk. 2013.01.08.151611 [4950] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.2.7 08 Jan 2013 [4951] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 08 Jan 2013 [4952] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.2.6 http://svn.libburnia-project.org/libisoburn/tags/1.2.6 ------------------------------------ cycle - xorriso-1.2.7 - 2013.01.08.151611 09 Jan 2013 [4953] frontend/xorriso-tcltk For now only demanding xorriso >= 1.2.6 for xorriso-tcltk-1.2.7 11 Jan 2013 [4954] xorriso/xorriso_eng.html Presenting xorriso-tcltk on the GNU xorriso web page 2013.01.13.093116 [4957] xorriso/match.c Curbed the number of wildcard warnings to 3 for iso and 3 for disk 2013.01.13.102527 [4958] configure.ac libisoburn/libisoburn.h Requiring libburn-1.2.7 2013.01.13.200742 [4959] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/burn_wrap.c libisoburn/libisoburn.ver New API call isoburn_toc_disc_get_incmpl_sess() 2013.01.14.094419 [4960] xorriso/drive_mgt.c Making use of new libisoburn API call to show incomplete sessions 2013.01.15.104804 [4962] frontend/xorriso-tcltk frontend/README-tcltk New feature "Log non-essential commands" and --script_log_all_commands 15 Jan 2013 [4963] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Clarified meaning of "After commit" prediction of -tell_media_space 16 Jan 2013 [4964] xorriso/xorriso_eng.html ChangeLog Updated change log and web page 2013.01.16.181831 [4967] xorriso/emulators.c xorriso/opts_p_z.c Updated copyright message of xorriso ------------------------------------ cycle - xorriso-1.2.7 - 2013.01.16.181831 * Bug fix: -print_size and -tell_media_space altered the pointers to MD5 of data files which stem from a previous session. This produced false mismatches with -check_md5_r. * Bug fix: CD tracks were reported with the sizes of the tracks in the first session. 12 Feb 2013 [4968] doc/faq.wiki Mentioned xorriso-tcltk in the online FAQ 12 Feb 2013 [4969] doc/faq.wiki Improved mentioning of xorriso-tcltk in the online FAQ 2013.02.18.104738 [4970] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs options -eltorito-id , -eltorito-selcrit 18 Feb 2012 [4971] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.7 - 2013.02.18.104738 * New -as mkisofs options -eltorito-id , -eltorito-selcrit 2013.02.26.082331 [4973] xorriso/check_media.c Corrected wrong use of sizeof 2013.02.26.103233 [4974] xorriso/drive_mgt.c Bug fix: -check_media use=outdev sector_map= stored TOC of input drive 2013.03.06.164347 [4983] xorriso/iso_manip.c Bug fix: -hide hfsplus and -as mkisofs -hide-hfsplus had no effect. Thanks to Davy Ho. 2013.03.06.164906 [4984] xorriso/drive_mgt.c With -list_speeds : reporting the effective speed 0 if it deviates from speed H 2013.03.10.193344 [4985] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/emulators.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Improved interaction of -as mkisofs command with other commands 10 Mar 2013 [4986] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.7 - 2013.03.10.193344 * Bug fix: -check_media use=outdev sector_map= stored TOC of input drive * Bug fix: -hide hfsplus and -as mkisofs -hide-hfsplus had no effect. Thanks to Davy Ho. 2013.03.12.124913 [4988] configure.ac libisoburn/libisoburn.h Requiring libisofs-1.2.7 2013.03.12.194637 [4989] xorriso/write_run.c Prevented rollover of El Torito Sector Count of large EFI boot images 2013.03.12.194842 [4990] configure.ac xorriso/misc_funct.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Corrected the interpretation of user supplied ECMA-119 timestamp strings 2013.03.12.195052 [4991] xorriso/xorriso.h xorriso/iso_img.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Reporting volume timestamps with -pvd_info 2013.03.13.195851 [4992] xorriso/xorriso.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New command -move 2013.03.17.110559 [4993] configure.ac Bug fix: ./configure did not abort if libburn.h or libisofs.h were missing 18 Mar 2013 [5000] svn copy -m Branching for libisoburn release 1.2.8 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.2.8 18 Mar 2013 [5001] frontend/xorriso-tcltk frontend/README-tcltk Making use of command -move in xorriso-tcltk 2013.03.18.093001 [5002] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.2.8 18 Mar 2013 [5004] Makefile.am Had to add xorriso-tcltk to EXTRA_DIST although it is in bin_SCRIPTS 18 Mar 2013 [5003] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.2.8 - 2013.03.18.093001 * Bug fix: -tell_media_space altered the pointers to MD5 of data files which stem from a previous session. This produced false mismatches with -check_md5_r. * Bug fix: CD tracks were reported with the sizes of the tracks in the first session. * Bug fix: -check_media use=outdev sector_map= stored TOC of input drive * Bug fix: -hide hfsplus and -as mkisofs -hide-hfsplus had no effect. Thanks to Davy Ho. * Bug fix: ./configure did not abort if libburn.h or libisofs.h were missing * New command -move * New -as mkisofs options -eltorito-id , -eltorito-selcrit 18 Mar 2013 [5005] frontend/xorriso-tcltk frontend/README-tcltk Making use of command -move in xorriso-tcltk 18 Mar 2013 [5006] Makefile.am Had to add xorriso-tcltk to EXTRA_DIST although it is in bin_SCRIPTS 2013.03.18.204322 [5010] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.2.9 19 Mar 2013 [5011] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 20 Mar 2013 [5012] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.2.8 http://svn.libburnia-project.org/libisoburn/tags/1.2.8 ------------------------------------ cycle - xorriso-1.2.9 - 2013.03.18.204322 2013.04.13.064738 [5017] libisoburn/libisoburn.h libisoburn/isoburn.c New options bit with isoburn_igopt_set_system_area() for GRUB2 MBR patching 2013.04.13.074117 [5018] xorriso/xorriso_private.h xorriso/opts_a_c.c xorriso/iso_img.c xorriso/write_run.c New -boot_image bootspecs grub2_mbr= and grub2_boot_info= 2013.04.13.074309 [5019] xorriso/emulators.c New -as mkisofs options --grub2-mbr and --grub2-boot-info ------------------------------------ cycle - xorriso-1.2.9 - 2013.04.13.074309 * New options bit with isoburn_igopt_set_system_area() for GRUB2 MBR patching * New -boot_image bootspecs grub2_mbr= and grub2_boot_info= * New -as mkisofs options --grub2-mbr and --grub2-boot-info 2013.04.13.144946 [5020] xorriso/iso_tree.c Bug fix: Disk paths with components '.' or '..' could be mistaken for directories 2013.04.13.205230 [5021] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/iso_img.c xorriso/write_run.c New -boot_image bootspec grub2_sparc_core= 2013.04.13.205450 [5022] xorriso/emulators.c New -as mkisofs option --grub2-sparc-core ------------------------------------ cycle - xorriso-1.2.9 - 2013.04.13.205450 2013.04.14.174539 [5023] xorriso/iso_img.c xorriso/text_io.c Reporting grub2_mbr= with -status -boot_image 2013.04.14.174902 [5024] xorriso/opts_d_h.c xorriso/emulators.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Updated man page and help texts about new features 2013.04.16.124331 [5025] xorriso/iso_img.c Recognizing grub2_mbr= on image load 17 Apr 2013 [5026] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.9 - 2013.04.16.124331 * New -boot_image bootspecs grub2_mbr= and grub2_boot_info= * New -boot_image bootspec grub2_sparc_core= * New -as mkisofs options --grub2-mbr , --grub2-boot-info , --grub2-sparc-core * Bug fix: Disk paths with components '.' or '..' could be mistaken for directories 2013.05.03.135424 [5027] xorriso/emulators.c Letting -as cdrecord -help point to man xorrecord rather than to man xorriso 2013.05.06.161651 [5028] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/text_io.c New -hardlinks mode "lsl_count" / "no_lsl_count" 06 May 2013 [5029] frontend/xorriso-tcltk Starting up xorriso-tcltk with -hardlinks "on" if xorriso version >= 1.3.0 07 May 2013 [5030] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Documented new -hardlinks mode 07 May 2013 [5031] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 Clarification that xorriso does neither audio CD nor official video DVD 07 May 2013 [5032] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.9 - 2013.05.07.185653 * New -hardlinks mode "lsl_count" / "no_lsl_count" 07 May 2013 [5033] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Small improvement in layout of xorriso info page 10 May 2013 [5037] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Corrected two examples in man xorriso 10 May 2013 [5038] xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Corrected bold printing of three options in man xorrisofs 2013.05.13.123951 [5039] xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Bug fix: -as mkisofs -print-size failed with -isohybrid-mbr and a single boot image 13 May 2013 [5040] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.9 - 2013.05.13.123951 * Bug fix: -as mkisofs -print-size failed with -isohybrid-mbr and a single boot image 2013.05.16.070643 [5041] xorriso/emulators.c Ignoring -as mkisofs options -gui and -sort 2013.05.16.070643 [5042] xorriso/emulators.c Bug fix: -as mkisofs -path-list did not switch to --no-emul-toc by default 16 May 2013 [5043] xorriso/xorriso_eng.html ChangeLog Updated change log and web page ------------------------------------ cycle - xorriso-1.2.9 - 2013.05.16.070643 * Bug fix: -as mkisofs -path-list did not switch to --no-emul-toc by default 2013.05.17.140001 [5049] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.3.0 17 May [5050] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.3.0 - 2013.05.17.140001 * New -boot_image bootspecs grub2_mbr= and grub2_boot_info= * New -boot_image bootspec grub2_sparc_core= * New -as mkisofs options --grub2-mbr , --grub2-boot-info , --grub2-sparc-core * Bug fix: Disk paths with components '.' or '..' could be mistaken for directories * New -hardlinks mode "lsl_count" / "no_lsl_count" * Bug fix: -as mkisofs -print-size failed with -isohybrid-mbr and a single boot image * Bug fix: -as mkisofs -path-list did not switch to --no-emul-toc by default 2013.05.17.193457 [5055] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.3.1 17 May 2013 [5056] ChangeLog xorriso/changelog.txt Updated change log and web page 17 May 2013 [5057] xorriso/make_xorriso_standalone.sh Corrected release number in make_xorriso_standalone.sh 17 May 2013 [5058] svn move -m 'Promoted branch to tag' ------------------------------------ cycle - xorriso-1.3.1 - 2013.05.17.193457 2013.05.24.111243 [5072] xorriso/iso_manip.c Bug fix: -find -exec "sort_weight" did not mark the image as having pending changes 24 May 2013 [5073] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Corrected statement about sort weight of files which are loaded from ISO image 2013.05.28.175212 [5075] xorriso/xorrisoburn.h xorriso/emulators.c xorriso/iso_manip.c xorriso/disk_ops.c xorriso/iso_tree.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs options --sort-weight-list and --sort-weight-patterns 2013.05.30.192537 [5078] xorriso/xorriso.h xorriso/parse_exec.c xorriso/sfile.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Bug fix: -backslash_codes "with_program_arguments" was interpreted too late 2013.06.02.112441 [5079] xorriso/opts_p_z.c xorriso/write_run.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -pacifier behavior code "interval=" 2013.06.02.112720 [5085] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Letting -as mkisofs -gui cause a higher frequency of pacifier messages 02 Jun 2013 [5086] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.3.1 - 2013.06.02.112720 * Bug fix: -find -exec "sort_weight" did not mark the image as having pending changes * Bug fix: -backslash_codes "with_program_arguments" was interpreted too late * New -pacifier behavior code "interval=" * New -as mkisofs options --sort-weight-list and --sort-weight-patterns ------------------------------------ cycle - xorriso-1.3.1 - 2013.06.10.083720 (Improved granularity of SCSI log time measurement, SCSI log timestamps) 2013.06.27.125407 [5092] xorriso/opts_a_c.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -format mode "without_spare" (for BD-RE) 30 Jun 2013 [5095] frontend/xorriso-tcltk Small fix in xorriso-tcltk help text 2013.06.30.160008 [5096] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/text_io.h xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New command -named_pipe_loop 2013.07.01.180608 [5098] xorriso/xorriso.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/disk_ops.c xorriso/iso_tree.c xorriso/iso_manip.c xorriso/text_io.h xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New command -sh_style_result 2013.07.01.180652 [5099] xorriso/lib_mgt.c Making use of new libburn option to ignore SIGPIPE 01 Jul 2013 [5100] configure.ac libisoburn/libisoburn.h Requiring libburn-1.3.1 2013.07.02.092827 [5101] xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -msg_op opcdoes "parse_silently" and "parse_bulk_silently" 2013.07.02.123642 [5102] xorriso/text_io.c Comitting file that was missing in rev 5101 2013.07.04.094640 [5103] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/text_io.c xorriso/aux_objects.h xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Extended new command -named_pipe_loop by mode parameter 04 Jul 2013 [5104] frontend/sh_on_named_pipes.sh New frontend demo skript frontend/sh_on_named_pipes.sh 04 Jul 2013 [5105] README Makefile.am xorriso/README_gnu_xorriso xorriso/make_xorriso_standalone.sh xorriso/xorriso_makefile_am.txt Integrating frontend/sh_on_named_pipes.sh into docs and delivery system 2013.07.05.072400 [5106] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page 05 Jul 2013 [5107] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Corrected a typo in change logs ------------------------------------ cycle - xorriso-1.3.1 - 2013.07.05.072400 * New -format mode "without_spare" (for BD-RE) * New command -named_pipe_loop * New -msg_op opcodes "parse_silently" and "parse_bulk_silently" 2013.07.05.164001 [5108] xorriso/xorriso.h xorriso/opts_i_o.c Bug fix: Missing or empty parameter with -dus was interpreted as "*" rather than "." 2013.07.06.110101 [5109] xorriso/opts_d_h.c xorriso/text_io.c Bug fix: readline history was spammed by -msg_op parsing and pipe loops 22 Jul 2013 [5113] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.3.1 - 2013.07.22.202742 * Bug fix: Missing or empty parameter with -dus was interpreted as "*" rather than "." * Bug fix: readline history was spammed by -msg_op parsing and pipe loops * Bug fix: (libburn) xorriso aborted on SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU 01 Aug 2013 [5115] xorriso/xorrisofs.texi xorriso/xorrisofs.1 Corrected line breaks in man xorrisofs example about isohybrid 2013.08.04.101212 [5117] configure.ac xorriso/configure_ac.txt Adapted text output of configure option --enable-dvd-obs-64k 2013.08.04.102038 [5118] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/write_run.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New command -application_use 2013.08.04.102243 [5119] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs option --application_use 2013.08.04.102418 [5120] xorriso/emulators.c Bug fix: -as mkisofs -help in dialog mode with pager did not end on request 04 Aug 2013 [5121] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.3.1 - 2013.08.04.102418 * New command -application_use and new -as mkisofs option --application_use 07 Aug 2013 [5129] copy -m Branching for libisoburn release 1.3.2 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.3.2 2013.08.04.172325 [5123] xorriso/base_obj.c xorriso/text_io.c Changed default setting of -application_use from 0x00 to 0x20 2013.08.07.110001 [5130] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.3.2 07 Aug 2013 [5131] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.3.2 - 2013.08.07.110001 * Bug fix: -find -exec "sort_weight" did not mark the image as having pending changes * Bug fix: -backslash_codes "with_program_arguments" was interpreted too late * Bug fix: Missing or empty parameter with -dus was interpreted as "*" rather than "." * Bug fix: readline history was spammed by -msg_op parsing and pipe loops * Bug fix: xorriso aborted on SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU * Improved granularity of SCSI log time measurement, now with timestamp * New -pacifier behavior code "interval=" * New -as mkisofs options --sort-weight-list and --sort-weight-patterns * New -format mode "without_spare" (for BD-RE) * New command -named_pipe_loop * New command -sh_style_result * New -msg_op opcodes "parse_silently" and "parse_bulk_silently" * New command -application_use and new -as mkisofs option --application_use 2013.08.07.141130 [5136] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.3.3 07 Aug 2013 [5137] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 07 Aug 2013 [5138] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.3.2 http://svn.libburnia-project.org/libisoburn/tags/1.3.2 ------------------------------------ cycle - xorriso-1.3.3 - 2013.08.07.141130 2013.08.16.153701 [5139] libisoburn/libisoburn.h Clarified the comments about isoburn_prepare_*() calls 2013.08.16.153743 [5140] xorriso/write_run.c Bug fix: Command -blank "as_needed" formatted blank BD-R. 2013.08.19.152714 [5141] xorriso/write_run.c Downgraded severity of media overflow with -print_size 2013.08.20.144306 [5142] xorriso/emulators.c Closed a small memory leak with -as mkisofs emulation 2013.08.27.095314 [5143] xorriso/xorriso_private.h xorriso/write_run.c Avoiding a futile re-try when ending after a failed session write attempt 27 Aug 2013 [5144] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.3.3 - 2013.08.27.095314 * Bug fix: Command -blank "as_needed" formatted blank BD-R. 2013.09.05.080255 [5149] xorriso/drive_mgt.c Keeping -check_media from reading outside of medium readable area 2013.09.05.081703 [5150] xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Reacted on warnings of PLD Linux build log 2013.09.05.082833 [5151] xorriso/emulators.c Bug fix: -as mkisofs option -log-file added the log file to the image 2013.09.07.193824 [5153] xorriso/iso_manip.c Reacted on warning of valgrind 07 Sep 2013 [5154] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Mentioned the change of default weight for boot image files 10 Sep 2013 [5155] xorriso/xorriso_eng.html Removed false promise from web page example about -osirrox 2013.09.16.164820 [5156] libisoburn/burn_wrap.c xorriso/iso_manip.c Reacted on warnings of Debian buildd with clang 2013.10.05.073918 [5159] xorriso/iso_manip.c Bug fix: -cut_out did not add x-permission to r-permission of directory 2013.10.08.175702 [5160] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/text_io.c xorriso/drive_mgt.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New command -read_speed 09 Oct 2013 [5162] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.3.3 - 2013.10.08.175702 * Bug fix: -as mkisofs option -log-file put the log file into the image * Bug fix: -cut_out did not add x-permission to r-permission of directory * New command -read_speed * libisofs: Default sort weight of El Torito boot images is now 2. 2013.10.14.140028 [5165] xorriso/opts_p_z.c Bug fix: Command -zisofs did not accept all options emitted by -status -zisofs 2013.10.20.125455 [5166] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/emulators.c xorriso/text_io.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -close mode "as_needed" 2013.10.27.115427 [5167] libisoburn/libisoburn.h Now requiring libburn-1.3.3 2013.10.28.150924 [5169] xorriso/xorrisoburn.h xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Automatic re-try with -close "as_needed" and unannounced fast-blanked DVD-RW 2013.10.28.151434 [5170] xorriso/emulators.c xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 New -as cdrecord option --multi_if_possible 2013.10.29.123448 [5171] xorriso/drive_mgt.c Avoiding to list pairs of the same speed with -list_speeds 2013.11.10.163500 [5176] xorriso/drive_mgt.c Bug fix: -blank force:... failed on appendable or closed media 11 Nov 2013 [5178] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.3.3 - 2013.11.10.163500 * Bug fix: Command -zisofs did not accept all options emitted by -status -zisofs * Bug fix: libburn: Drive error reports were ignored during blanking and formatting * Bug fix: -blank force:... failed on appendable or closed media * Bug fix: libburn: Drive LG BH16NS40 stalled on inspection of unformatted DVD+RW * New -close mode "as_needed", new -as cdrecord option --multi_if_possible 2013.11.15.095611 [5181] xorriso/opts_a_c.c Removed an obsolete variable 15 Nov 2013 [5183] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Mentioned built-in limits for minimum and maximum speed 2013.11.17.120422 [5186] libisoburn/burn_wrap.c Releasing loaded ISO image data when no longer needed ------------------------------------ cycle - xorriso-1.3.3 - 2013.11.17.120422 2013.11.18.140456 [5188] xorriso/text_io.c Reacted on warning about unused variable if no readline is enabled ------------------------------------ cycle - xorriso-1.3.3 - 2013.11.26.123204 * libisofs: Encoding HFS+ names in UTF-16 rather than UCS-2. 2013.11.27.094951 [5190] xorriso/parse_exec.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -alter_date types: a-c , m-c , b-c , c 01 Dec 2013 [5191] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Defined the term LBA in man xorriso 12 Dec 2013 [5196] svn copy -m Branching for libisoburn release 1.3.4 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.3.4 2013.12.12.103001 [5197] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.3.4 12 Dec 2013 [5198] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.3.4 - 2013.12.12.103001 * Bug fix: Command -blank "as_needed" formatted blank BD-R. * Bug fix: -as mkisofs option -log-file put the log file into the image * Bug fix: -cut_out did not add x-permission to r-permission of directory * Bug fix: Command -zisofs did not accept all options emitted by -status -zisofs * Bug fix: -blank force:... failed on appendable or closed media * Bug fix: libburn: Drive LG BH16NS40 stalled on inspection of unformatted DVD+RW * libisofs: Default sort weight of El Torito boot images is now 2 * libisofs: Encoding HFS+ names in UTF-16 rather than UCS-2 * New command -read_speed * New -close mode "as_needed", new -as cdrecord option --multi_if_possible * New -alter_date types: a-c , m-c , b-c , c 2013.12.12.154238 [5203] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.3.5 12 Dec 2013 [5204] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.3.4 http://svn.libburnia-project.org/libisoburn/tags/1.3.4 12 Dec 2013 [5205] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-1.3.5 - 2013.12.12.154238 2013.12.18.074049 [5206] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New isoburn_igopt_set_relaxed() relaxation isoburn_igopt_joliet_utf16 2013.12.18.074219 [5207] xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -compliance rule joliet_utf16 2013.12.18.074401 [5208] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info New -as mkisofs option -joliet-utf16 2013.12.28.194725 [5209] libisoburn/libisoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver New API call isoburn_conv_name_chars() 2013.12.29.120027 [5210] xorriso/opts_d_h.c xorriso/findjob.h xorriso/findjob.c xorriso/iso_manip.c xorriso/write_run.h xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -find test -bad_outname, new -find action print_outname 2013.12.30.101707 [5211] xorriso/iso_tree.c Bug fix: -compare reported false differences with directories which have non-trivial ACL but no default ACL 02 Jan 2014 [5212] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Clarified in man xorriso the roles of character sets 2014.01.05.072818 [5212] [5213] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.3.5 - 2014.01.05.072818 * Bug fix: libisofs: Division by zero if HFS+ was combined with TOC emulation for overwritable media. * New isoburn_igopt_set_relaxed() relaxation isoburn_igopt_joliet_utf16 * New -compliance rule joliet_utf16, new -as mkisofs option -joliet-utf16 * New API call isoburn_conv_name_chars() * New -find test -bad_outname, new -find action print_outname 2014.01.09.154437 [5217] xorriso/drive_mgt.c Bug fix: -list_speeds did not work any more with old CD drives 2014.01.09.215206 [5219] xorriso/drive_mgt.c Improved display of -list_speeds 09 Jan 2014 [5220] ChangeLog xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.3.5 - 2014.01.09.215206 * libburn: Adapted Linux SG_IO adapter to scsi/sg.h of git.kernel.org 2014.01.14.084808 [5221] libisoburn/libisoburn.h Mentioned system area type 4 for HP-PA 2014.01.14.090320 [5222] xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/iso_img.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -boot_image settings hppa_cmdline=, hppa_bootloader=, hppa_kernel_32=, hppa_kernel_64=, hppa_ramdisk= 2014.01.14.091348 [5223] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs options -hppa-cmdline, -hppa-bootloader, -hppa-kernel-32, -hppa-kernel-64, -hppa-ramdisk 14 Jan 2014 [5224] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.3.5 - 2014.01.14.091348 * New -boot_image settings hppa_cmdline=, hppa_bootloader=, hppa_kernel_32=, hppa_kernel_64=, hppa_ramdisk= * New -as mkisofs options -hppa-cmdline, -hppa-bootloader, -hppa-kernel-32, -hppa-kernel-64, -hppa-ramdisk 2014.01.16.112242 [5227] libisoburn/libisoburn.h Mentioned system area type 5 for HP-PA version 5 2014.01.16.113247 [5228] xorriso/xorriso_private.h xorriso/opts_a_c.c xorriso/iso_img.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -boot_image setting hppa_hdrversion= 2014.01.16.113503 [5229] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs option -hppa-hdrversion 20 Jan 2014 [5230] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Corrected a wrong statement in man xorriso about -check_media bad_limit= 2014.01.23.200641 [5231] xorriso/drive_mgt.c xorriso/check_media.h xorriso/check_media.c Bug fix: -check_media marked untested sectors in sector map as valid 24 Jan 2014 [5232] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.3.5 - 2014.01.23.200641 * Bug fix: -check_media marked untested sectors in sector map as valid * New -boot_image setting hppa_hdrversion= * New -as mkisofs option -hppa-hdrversion ------------------------------------ cycle - xorriso-1.3.5 - 2014.02.05.121822 (Experimental upload for NetBSD support) 2014.02.05.191021 [5236] configure.ac xorriso/configure_ac.txt xorriso/compile_xorriso.sh Added -Wno-char-subscripts to CFLAGS 2014.02.08.111505 [5239] xorriso/write_run.c Silenced a MISHAP message of libisofs with -tell_media_space caused by rev 5210 2014.02.09.141412 [5240] xorriso/iso_img.c Improved output of -pvd_info with timestamps with non-zero time zone 2014.02.10.104827 [5241] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/disk_ops.c xorriso/iso_tree.c Bug fix: Paths with symbolic links preceding ".." were not interpreted properly 10 Feb 2014 [5242] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.3.5 - 2014.02.10.104827 Bug fix: Paths with symbolic links preceding ".." were not interpreted properly 10 Feb 2014 [5243] configure.ac libisoburn/libisoburn.h Requiring libisofs-1.3.5 and libburn-1.3.5 11 Feb 2014 [5248] acinclude.m4 Added /usr/local for NetBSD 12 Feb 2014 [5250] doc/comments xorriso/README_gnu_xorriso Mentioned support for NetBSD 2014.02.13.154003 [5251] xorriso/iso_img.c Corrected a bug introduced with rev 5240 27 Feb 2014 [5256] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.3.5 - 2014.02.27.091053 * libburn: New system adapter for NetBSD 2014.03.02.102010 [5258] xorriso/iso_img.c Made -status -boot_image more verbous about the pending boot image definition 2014.03.03.202815 [5299] xorriso/opts_d_h.c xorriso/emulators.c xorriso/iso_img.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Temporarily disabled the unfinished HP-PA PALO bootability preparations 04 Mar 2014 [5264] svn copy -m Branching for libisoburn release 1.3.6 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.3.6 2014.03.04.113001 [5265] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.3.6 04 Mar 2014 [5266] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 2014.03.04.120001 [5267] xorriso/drive_mgt.c Last minute regression fix: -list_speeds on non-BD drives used CD speed factor 04 Mar 2014 [5268] xorriso/changelog.txt Documented release timestamp ----------------------------------- release - xorriso-1.3.6 - 2014.03.04.120001 * Bug fix: libisofs: Division by zero if HFS+ was combined with TOC emulation for overwritable media. * Bug fix: -list_speeds did not work any more with old CD drives. Regression introduced by release 1.3.4 * Bug fix: -check_media marked untested sectors in sector map as valid * Bug fix: Paths with symbolic links preceding ".." were not interpreted properly * New isoburn_igopt_set_relaxed() relaxation isoburn_igopt_joliet_utf16 * New -compliance rule joliet_utf16, new -as mkisofs option -joliet-utf16 * New -find test -bad_outname, new -find action print_outname * New API call isoburn_conv_name_chars() * libburn: New system adapter for NetBSD 2014.03.04.164038 [5273] xorriso/drive_mgt.c Last minute regression fix: -list_speeds on non-BD drives used CD speed factor 2014.03.04.165750 [5274] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.3.7 04 Mar 2014 [5275] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.3.6 http://svn.libburnia-project.org/libisoburn/tags/1.3.6 05 Mar 2014 [5276] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-1.3.7 - 2014.03.04.165750 05 Mar 2014 [5277] releng/run_all_auto Gave up use of PIPESTATUS in releng/run_all_auto because of NetBSD /bin/sh 2014.03.06.075858 [5278] xorriso/drive_mgt.c Changed severity of missing -list_format data from FAILURE to SORRY 2014.03.06.164332 [5279] xorriso/iso_img.c Bug fix: Command -status produced FAILURE event if no drive was acquired 06 Mar 2014 [5280] ChangeLog xorriso/xorriso_eng.html Updated change log and web page ------------------------------------ cycle - xorriso-1.3.7 - 2014.03.06.164332 * Bug fix: Command -status produced FAILURE event if no drive was acquired 2014.03.14.134138 [5283] libisoburn/libisoburn.h Corrected outdated description of GRUB2 MBR patching 18 Mar 2014 [5288] svn copy -m Patching libisoburn to pl01 http://svn.libburnia-project.org/libisoburn/tags/1.3.6 http://svn.libburnia-project.org/libisoburn/branches/1.3.6.pl01 2014.03.18.083001 [5289 1.3.6.pl01] README xorriso/README_gnu_xorriso xorriso/iso_img.c xorriso/xorriso_eng.html xorriso/xorriso_timestamp.h Bug fix: Command -status produced FAILURE event if no drive was acquired 18 Mar 2014 [5290] svn move -m Patched libisoburn to 1.3.6.pl01 http://svn.libburnia-project.org/libisoburn/branches/1.3.6.pl01 http://svn.libburnia-project.org/libisoburn/tags/1.3.6.pl01 ------------------------------ release - xorriso-1.3.6.pl01 - 2014.03.18.083001 25 Mar 2014 [5292] ChangeLog xorriso/xorriso_eng.html Mentioned patch level release 1.3.6.pl01 2014.03.25.195908 [5293] xorriso/xorriso_private.h xorriso/iso_img.c Bug fix: -boot_image any partition_cyl_align=on prevented further settings 2014.04.03.194751 [5294] xorriso/iso_img.c Reacted on compiler warning of gcc on AIX. Thanks Richard Nolde. 2014.04.04.142016 [5295] xorriso/opts_a_c.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Bug fix: -boot_image grub grub2_mbr= did not work (but -as mkisofs --grub2-mbr did work) 2014.04.04.153505 [5296] xorriso/opts_a_c.c xorriso/iso_img.c Bug fix: -boot_image grub2_mbr= prevented -boot_image partition_table=on 2014.04.08.091745 [5298] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/emulators.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info New -stdio_sync option "end", -as mkisofs default is now --stdio_sync "off" 2014.04.08.094441 [5299] configure.ac libisoburn/libisoburn.h Requiring libburn-1.3.7 and libisofs-1.3.7 2014.04.09.073038 [5300] xorriso/emulators.c xorriso/iso_img.c xorriso/opts_d_h.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Re-enabled HP-PA PALO boot preparations 09 Apr 2014 [5301] ChangeLog xorriso/xorriso_eng.html Updated change log and web page ------------------------------------ cycle - xorriso-1.3.7 - 2014.04.09.073038 * Bug fix: libburn : Compilation warning for unsupported systems mutated into an error * Bug fix: -boot_image grub grub2_mbr= did not work (but -as mkisofs --grub2-mbr did work) * Bug fix: -boot_image grub2_mbr= prevented -boot_image partition_table=on * Bug fix: libburn: A final fsync(2) was performed with stdio drives, even if -stdio_sync was set to "off". * New bootspecs hppa_*, new -as mkisofs options -hppa-* for HP-PA via PALO 2014.04.10.072212 [5303] xorriso/opts_d_h.c Mentioned bootspec hppa_discard in xorriso -help 2014.04.11.161139 [5304] xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/iso_img.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info libisoburn/libisoburn.ver New command -report_system_area 2014.04.12.130150 [5305] xorriso/iso_img.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -report_system_area form "gpt_crc_of:" 2014.04.13.071908 [5306] xorriso/iso_img.c Adapted -report_system_area to changed output format of libisofs 2014.04.13.120421 [5307] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c libisoburn/libisoburn.ver New API calls isoburn_igopt_set_stdio_endsync(), isoburn_igopt_get_stdio_endsync() 2014.04.13.120743 [5308] xorriso/write_run.c Making use of new libisoburn API call 2014.04.14.190506 [5311] xorriso/opts_p_z.c Updated copyright date in output of xorriso -version 2014.04.16.144146 [5312] xorriso/opts_d_h.c xorriso/findjob.h xorriso/findjob.c xorriso/iso_manip.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -find pseudo tests -use_pattern , -or_use_pattern 2014.04.16.144623 [5313] xorriso/opts_d_h.c Removed a surplus code snippet that was committed by rev 5312 2014.04.16.145517 [5314] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Corrected a statement in the man page. 2014.04.19.115057 [5316] xorriso/drive_mgt.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Adapted -check_media to peculiarities of DVD and BD 2014.04.21.131802 [5317] xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/parse_exec.h xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/iso_img.c xorriso/disk_ops.c xorriso/read_run.c xorriso/filters.c xorriso/sfile.h xorriso/sfile.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info libisoburn/libisoburn.ver New command -concat 2014.04.21.165612 [5318] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_d_h.c xorriso/disk_ops.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -follow occasion "concat" 22 Apr 2014 [5319] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Polished man page about -concat 22 Apr 2014 [5320] ChangeLog xorriso/xorriso_eng.html Updated change log and web page ------------------------------------ cycle - xorriso-1.3.7 - 2014.04.21.165612 * New command -report_system_area * New -find pseudo tests -use_pattern , -or_use_pattern * New command -concat 2014.04.24.084041 [5321] xorriso/opts_d_h.c xorriso/iso_img.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -report_system_area form "el_torito" 2014.04.24.084540 [5322] xorriso/write_run.c Clarified potentially negative el_torito_set_load_size() parameter in xorriso 2014.04.25.064456 [5323] xorriso/iso_img.c Using libisofs system area summary in -toc line "Boot record" 2014.04.29.174832 [5325] xorriso/xorriso.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/iso_img.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info libisoburn/libisoburn.ver New command -report_el_torito (former -report_system_area "el_torito") 2014.05.02.090851 [5326] acinclude.m4 releng/auto_cxx Added -I/usr/pkg/include und -L/usr/pkg/lib for NetBSD 2014.05.03.080034 [5327] xorriso/opts_d_h.c Fixed a bug with recognition of -find test -or_use_pattern 2014.05.03.114648 [5329] libisoburn/libisoburn.h libisoburn/burn_wrap.c libisoburn/libisoburn.ver New API call isoburn_disc_pretend_full_uncond() 2014.05.03.114930 [5330] xorriso/drive_mgt.c Making use of new API call 2014.05.03.115011 [5331] libisoburn/burn_wrap.c Bug fix: -blank force:all on DVD+RW had no effect 2014.05.18.144453 [5332] xorriso/sort_cmp.c Added a few comments about ino_t compatibility 2014.05.28.125704 [5333] xorriso/iso_img.c xorriso/disk_ops.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Anticipating NetBSD mount option -s 2014.06.03.190644 [5334] xorriso/parse_exec.c Fixed a bug introduced by rev 5317, which prevented use of execution paths 2014.06.13.084712 [5337] xorriso/write_run.c Precautions for large stdio filesystems 2014.06.13.090244 [5338] xorriso/opts_d_h.c xorriso/iso_tree.h xorriso/iso_tree.c xorriso/iso_manip.c xorriso/findjob.h xorriso/read_run.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -find action report_sections 2014.06.14.185146 [5339] xorriso/disk_ops.h Commiting a header file that was forgotten with rev 5317 2014.06.14.192149 [5340] configure.ac acinclude.m4 xorriso/configure_ac.txt xorriso/base_obj.c xorriso/opts_d_h.c xorriso/lib_mgt.c xorriso/text_io.c Enabled use of libedit as alternative to libreadline 2014.06.15.090324 [5341] xorriso/lib_mgt.c xorriso/text_io.h xorriso/text_io.c Shutdown of libedit 2014.06.15.094311 [5342] xorriso/opts_p_z.c xorriso/text_io.h xorriso/text_io.c Stating license GPLv3+ if header of libreadline >= 6.x is included 2014.06.15.125555 [5343] README xorriso/README_gnu_xorriso configure.ac xorriso/configure_ac.txt Clarified configuration and license aspects of libreadline and libedit 2014.06.15.183236 [5344] Makefile.am Keeping releng/releng_generated_data/.svn out of libisoburn tarball 15 Jun 2014 [5345] ChangeLog xorriso/xorriso_eng.html Updated change log and web page ------------------------------------ cycle - xorriso-1.3.7 - 2014.06.15.183236 * New command -report_el_torito * New -find action report_sections * Bug fix: -blank force:all on DVD+RW had no effect * Enabled use of libedit as alternative to libreadline ------------------------------------ cycle - xorriso-1.3.7 - 2014.06.20.065402 * Enabled recording and restoring of extattr on NetBSD 27 Jun 2014 [5347] xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Corrected information about -isohybrid-gpt-basdat MBR partition type 27 Jun 2014 [5352] svn copy -m Branching for libisoburn release 1.3.8 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.3.8 2014.06.28.070001 [5353] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.3.8 28 Jun 2014 [5354] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 2014.06.28.071001 [5355] xorriso/parse_exec.h xorriso/xorriso_timestamp.h Corrected a type in declaration ----------------------------------- release - xorriso-1.3.8 - 2014.06.28.071001 * Bug fix: -boot_image grub grub2_mbr= did not work (but -as mkisofs --grub2-mbr did work) * Bug fix: -boot_image grub2_mbr= prevented -boot_image partition_table=on * Bug fix: libburn: A final fsync(2) was performed with stdio drives, even if -stdio_sync was set to "off". * Bug fix: libburn: Wrong stack usage caused SIGBUS on sparc when compiled by gcc -O2 * Bug fix: -blank force:all on DVD+RW had no effect * Enabled use of libedit as alternative to libreadline * Enabled recording and restoring of extattr on NetBSD * New bootspecs hppa_*, new -as mkisofs options -hppa-* for HP-PA via PALO * New -find pseudo tests -use_pattern , -or_use_pattern * New command -concat * New -find action report_sections * New commands -report_system_area and -report_el_torito 2014.06.28.071104 [5360] xorriso/parse_exec.h xorriso/xorriso_timestamp.h Corrected a type in declaration 2014.06.28.071234 [5361] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.3.9 28 Jun 2014 [5362] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-1.3.9 - 2014.06.28.071234 28 Jun 2014 [5363] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.3.8 http://svn.libburnia-project.org/libisoburn/tags/1.3.8 06 Jul 2014 [5365] COPYRIGHT Updated copyright date 20 Aug 2014 [5372] ChangeLog xorriso/xorriso_eng.html Updated change log and web page ------------------------------------ cycle - xorriso-1.3.9 - 2014.08.20.093534 * Bug fix: -dev or -indev of medium with non-ISO data caused SIGSEGV by NULL 2014.09.23.064407 [5377] xorriso/emulators.c Corrected xorrisofs help text about -chrp-boot-part and -prep-boot-part 2014.09.23.064554 [5378] libisoburn/burn_wrap.c libisoburn/isofs_wrap.c Reporting with severity DEBUG about read error messages during image loading 26 Sep 2014 [5379] xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Corrected description of -as mkisofs option -boot-load-size 2014.09.28.085819 [5380] xorriso/emulators.c Officially supporting combination -isohybrid-gpt-basdat -isohybrid-apm-hfsplus 2014.09.30.181738 [5381] xorriso/opts_a_c.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -boot_image bootspecs partition_table=gpt_basdat, gpt_hfsplus, apm_hfsplus 01 Oct 2014 [5382] doc/faq.wiki Added three popular google search texts to FAQ 02 Oct 2014 [5383] doc/faq.wiki Small correction of rev 5382 2014.10.03.180649 [5384] xorriso/emulators.c Supporting -as mkisofs -chrp-boot as alias of -chrp-boot-part 2014.10.28.140220 [5385] xorriso/iso_img.c Preparing for -report_system_area formats "cmd" and "as_mkisofs" 2014.10.28.140313 [5386] xorriso/write_run.c Improved processing of system area in multi-session situations 2014.10.28.140609 [5387] xorriso/opts_a_c.c xorriso/emulators.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Argument "." for system area import commands 2014.10.28.140709 [5388] xorriso/misc_funct.c Interpreting number suffix "d" (disk block) as factor of 512 2014.10.28.141130 [5389] xorriso/xorriso_private.h Added a clarifying comment about XorrisO.patch_system_area 2014.10.28.141959 [5390] xorriso/write_run.c Reacted on compiler warning about rev 5386 2014.11.04.121733 [5391] xorriso/opts_d_h.c xorriso/findjob.h xorriso/iso_manip.c xorriso/iso_tree.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -find action "show_stream_id" 2014.11.11.112623 [5392] xorriso/write_run.c Sort weight 1000000000 for El Torito boot catalog with -boot_image patch 2014.11.13.180252 [5393] xorriso/iso_img.c With -report_system_area as_mkisofs: Adding option -B if --grub2-sparc-core 2014.11.13.180427 [5394] xorriso/emulators.c Letting -as mkisofs -sparc-label perform command -boot_image any sparc_label= 2014.11.26.134709 [5396] acinclude.m4 configure.ac xorriso/configure_ac.txt xorriso/misc_funct.c Checking at configure time for timezone integer variable 2014.11.26.164605 [5398] xorriso/check_media.c xorriso/cmp_update.c xorriso/disk_ops.c xorriso/read_run.c xorriso/text_io.c xorriso/write_run.c test/compare_file.c Equipped all non-system-dependent open(2) calls with O_BINARY ------------------------------------ cycle - xorriso-1.3.9 - 2014.11.28.110954 * New -find action "show_stream_id" ------------------------------------ cycle - xorriso-1.3.9 - 2014.12.06.183523 2014.12.21.222445 [5401] acinclude.m4 xorriso/README_gnu_xorriso xorriso/xorriso_eng.html Using libvolmgt on older Solaris ------------------------------------ cycle - xorriso-1.3.9 - 2014.12.21.222445 2014.12.29.105732 [5402] Makefile.am Fixed a typo in message of make install. Thanks to Jakub Wilk. 2014.12.29.142036 [5404] xorriso/write_run.c Clarified meaning of xorriso->patch_isolinux_image in Xorriso_make_iso_write_opts() 29 Jan 2015 [5405] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Mentioned -report_el_torito parameters cmd and as_mkisofs 2015.02.04.113526 [5406] xorriso/write_run.c Fixed memory error when overwriting boot image of loaded ISO. Caused by rev5386 2015.02.06.114605 [5407] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver New API calls isoburn_igopt_set_appended_as_gpt(), isoburn_igopt_get_appended_as_gpt() 2015.02.06.115405 [5408] xorriso/xorriso_private.h xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/base_obj.c xorriso/emulators.c xorriso/iso_img.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info New -boot_image bootspec "appended_part_as=", new -as mkisofs option -appended_part_as_gpt 2015.02.07.114834 [5409] xorriso/iso_img.c Corrected representation of isohybrid MBR with -report_system_area "cmd" 2015.02.28.142843 [5410] xorriso/xorrisoburn.h xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/emulators.c xorriso/iso_img.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info New bootspec alpha_boot=, new -as mkisofs option -alpha-boot 2015.03.03.201723 [5411] xorriso/drive_mgt.c Corrected error message about drive class "caution" ------------------------------------ cycle - xorriso-1.3.9 - 2015.03.05.144703 * New API calls isoburn_igopt_set_appended_as_gpt(), isoburn_igopt_get_appended_as_gpt() * New -boot_image bootspec "appended_part_as=", new -as mkisofs option -appended_part_as_gpt ------------------------------------ cycle - xorriso-1.3.9 - 2015.04.13.172501 (with libisofs rev 1194 for -appended_part_as_gpt without isohybrid) 2015.04.23.135054 [5413] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c libisoburn/libisoburn.ver New API call isoburn_igopt_set_part_flag() and libisofs interval reader flags 2015.04.23.142257 [5414] xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Enabled use of libisofs interval reader in xorriso 2015.04.23.142407 [5415] xorriso/iso_img.c Counting -volid as pending change only if the text really changed 29 Apr 2015 [5416] doc/grub-mkrescue_reserved_commands.txt New file with list of argument names which are reserved for grub-mkrescue 29 Apr 2015 [5417] doc/grub-mkrescue_reserved_commands.txt Small correction in new doc file 2015.05.07.075329 [5418] xorriso/iso_img.c Emitting interval reader strings with -report_system_area cmd or as_mkisofs 2015.05.07.151926 [5419] xorriso/iso_img.c Emitting sparse zeroizers for system area and SORRY events for some insufficiencies 2015.05.10.093325 [5420] xorriso/iso_img.c Some corrections about -report_system_area cmd and as_mkisofs 2015.05.11.160333 [5421] xorriso/iso_img.c Some corrections about -report_system_area cmd and as_mkisofs 2015.05.14.100737 [5422] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/findjob.h xorriso/iso_img.h xorriso/iso_img.c xorriso/iso_manip.c Enabled output of HFS+ proposal with report_system_area cmd and as_mkisofs 2015.05.14.113925 [5423] xorriso/findjob.h xorriso/iso_img.h xorriso/iso_manip.c Updated some copyright marks 2015.05.14.131152 [5424] xorriso/iso_img.c Enabled output of volume id und modification date with -report_system_area cmd 16 May 2015 [5425] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Updated man xorriso about -report_system_area cmd 17 May 2015 [5432] svn copy -m Branching for libisoburn release 1.4.0 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.4.0 2015.05.17.112001 [5433] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.4.0 17 May 2017 [5434] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 2015.05.17.112001 [5436] xorriso/iso_img.c Reacted on a warning of NetBSD compiler ----------------------------------- release - xorriso-1.4.0 - 2015.05.17.112001 * Bug fix: -dev or -indev of medium with non-ISO data caused SIGSEGV by NULL * New API calls isoburn_igopt_set_appended_as_gpt(), isoburn_igopt_get_appended_as_gpt() * New API call isoburn_igopt_set_part_flag() and libisofs interval reader flags * New -find action "show_stream_id" * Optional libisofs interval reader with -append_partition and System Area * New -boot_image bootspec "appended_part_as=", new -as mkisofs option -appended_part_as_gpt * New -report_system_area formats "cmd" and "as_mkisofs" 2015.05.17.204035 [5442] xorriso/iso_img.c Reacted on a warning of NetBSD compiler 2015.05.17.204220 [5443] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.4.1 17 May 2017 [5444] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-1.4.1 - 2015.05.17.204220 18 May 2015 [5445] svn move -m 'Promoted branch to tag' \ http://svn.libburnia-project.org/libisoburn/branches/1.4.0 \ http://svn.libburnia-project.org/libisoburn/tags/1.4.0 18 May 2015 [5447] xorriso/xorriso_eng.html Corrected a faulty tag in web page of GNU xorriso 2015.06.24.155902 [5450] xorriso/sfile.c Bug fix: -backslash_codes "on" did not work outside quotes and with showing "\r" 24 Jun 2015 [5451] xorriso/configure_ac.txt Silenced libjte compiler warnings by -Wno-strict-aliasing 30 Jun 2015 [5454] xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Added EFI information to manual page example of bootable ISO 2015.06.30.144252 [5455] xorriso/opts_p_z.c Updated copyright year of xorriso command -version 2015.07.04.084911 [5456] xorriso/parse_exec.c Closed memory leaks on lack of memory reported by cppcheck 2015.07.04.085116 [5457] xorriso/read_run.c Reacted on cppcheck warning about uninitialized variable 2015.07.04.094815 [5458] xorriso/text_io.c Silenced a semi-correct error message of cppcheck 2015.07.04.094919 [5459] xorriso/iso_img.c Silenced false positive of cppcheck 2015.07.31.162206 [5460] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/emulators.c xorriso/text_io.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 New command -modesty_on_drive, new -as cdrecord -immed, minbuf=, modesty_on_drive= 2015.07.31.165816 [5461] xorriso/man_xorriso_to_html.sh xorriso/man_xorrisofs_to_html.sh xorriso/man_xorrecord_to_html.sh + xorriso/unite_html_b_line.c Reacting on changes in the output of my local man -H 2015.07.31.171227 [5462] libisoburn/libisoburn.h xorriso/xorriso_main.c xorriso/make_xorriso_1.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 README xorriso/README_gnu_xorriso releng/README doc/partition_offset.wiki doc/comments frontend/xorriso-tcltk Changed wrong use of "resp." in docs 12 Aug 2015 [5465] xorriso/compile_xorriso.sh Added automatic compilation of unite_html_b_line.c to developer compile script 2015.08.12.091220 [5466] xorriso/iso_img.c Avoiding to hand out indigestible --modification-date with -report_el_torito 2015.08.17.200155 [5467] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c New options with isoburn_ropt_set_extensions(): isoburn_ropt_map_* 2015.08.17.200910 [5468] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/text_io.c xorriso/drive_mgt.c libisoburn/libisoburn.ver xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New command -ecma119_map 2015.08.19.122832 [5469] Makefile.am Linking libpthread to libisoburn, not only to xorriso 2015.08.30.184740 [5470] xorriso/base_obj.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 Changed defaults of new command -modesty_on_drive to suit concurrent SG_IO 2015.09.06.194259 [5476] xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/iso_img.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -boot_image action "replay" 2015.09.15.182031 [5477] xorriso/iso_tree.c Hopefully finally enabled correct compilation on Debian mips and mipsel 2015.09.17.120554 [5478] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c libisoburn/libisoburn.ver New API calls isoburn_ropt_set_truncate_mode() isoburn_ropt_get_truncate_mode() 2015.09.17.121258 [5479] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/iso_tree.c xorriso/iso_manip.c xorriso/filters.c xorriso/text_io.c xorriso/emulators.c libisoburn/libisoburn.ver xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New command -file_name_limit, -as mkisofs -file_name_limit 2015.09.17.161956 [5481] acinclude.m4 Recognizing Debian GNU/kFreeBSD (seems to never have worked) 2015.09.20.124012 [5482] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/burn_wrap.c libisoburn/data_source.c Reacted on complaints of lintian spelling check 2015.09.20.125159 [5483] xorriso/xorriso_private.h xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/sfile.c xorriso/sfile.h frontend/frontend_pipes_xorriso.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 Reacted on complaints of lintian spelling check 2015.09.22.155937 [5484] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/drive_mgt.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New command -read_fs 2015.09.22.204735 [5485] xorriso/xorriso.h xorriso/opts_i_o.c xorriso/opts_d_h.c Marked introduction dates of xorriso commands in API description 2015.09.25.171620 [5488] xorriso/opts_d_h.c xorriso/drive_mgt.c xorriso/iso_manip.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Took into respect possible truncate mode change by AAIP variable isofs.nt 2015.09.28.141723 [5489] xorriso/xorriso_private.h xorriso/opts_d_h.c xorriso/iso_manip.h xorriso/iso_manip.c xorriso/findjob.h xorriso/sfile.h xorriso/sfile.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -find test -name_limit_blocker. Made -file_name_limit ready for loaded ISOs 07 Oct 2015 [5490] doc/faq.wiki Mentioned USB 3 problems in FAQ 2015.10.15.170642 [5491] xorriso/drive_mgt.c Closed memory leak with acquiration of drive 2015.10.16.155137 [5492] xorriso/drive_mgt.c xorriso/cmp_update.c Closed memory leak with 2015.10.18.130151 [5494] xorriso/drive_mgt.c xorriso/text_io.c New -toc report lines "Drive id :" and "Media id :". 2015.10.23.085903 [5497] xorriso/write_run.c Record size 0 with oversized EFI boot images to span them up to end of medium 2015.10.25.123601 [5517] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.1 - 2015.10.25.123601 * Bug fix: -backslash_codes "on" did not work outside quotes and with showing "\r" * New API calls isoburn_ropt_set_truncate_mode() isoburn_ropt_get_truncate_mode() * New options with isoburn_ropt_set_extensions(): isoburn_ropt_map_* * New command -modesty_on_drive, new -as cdrecord -immed, minbuf=, modesty_on_drive= * New command -ecma119_map * New command -read_fs * New -boot_image action "replay" * New command -file_name_limit, -as mkisofs -file_name_limit * New -find test -name_limit_blocker. 2015.11.03.125130 [5549] xorriso/text_io.c Closed a memory leak with message sieve. Coverity CID 28738. 2015.11.03.125655 [5550] xorriso/text_io.c Closed a memory leak with message sieve. Coverity CID 28739. 2015.11.03.130551 [5551] xorriso/findjob.c Closed a memory leak with -find. Coverity CID 28740. 2015.11.03.140546 [5552] xorriso/iso_manip.c Closed memory leak in case of error with file hiding. Coverity CID 28746 to 28750. 2015.11.03.145540 [5553] xorriso/check_media.c Closed memory leak with error about -check_media sector bitmapCoverity CID 28751. 2015.11.03.145920 [5554] xorriso/opts_i_o.c Closed memory leak with error about -ls. Coverity CID 28752. 2015.11.03.150839 [5555] xorriso/read_run.c Clarified handling of file descriptor. Coverity CID 28753. 2015.11.03.154102 [5556] libisoburn/burn_wrap.c Made clear that a drive needs an isoburn object for writing. Coverity CID 28758. 2015.11.03.155015 [5557] xorriso/findjob.c Prevented potential NULL pointer dereference. Coverity CID 28759. 2015.11.04.084711 [5558] xorriso/read_run.c Removed a surplus test for NULL. Coverity CID 28760. 2015.11.04.094803 [5559] xorriso/iso_tree.c Removed a wrong and obsolete test. Coverity CID 28762. 2015.11.04.100839 [5560] xorriso/write_run.c Prevented bug when interpreting MBR partition entry > 1 TB. Coverity CID 28764. 2015.11.04.105349 [5561] xorriso/match.c Prevented a potentially severe memory error with disk file pattern expansion. Coverity CID 28765. 2015.11.04.110102 [5562] xorriso/iso_tree.c Same as rev 5561. Here for iso_rr file names. Coverity CID 28766. 2015.11.04.114353 [5563] xorriso/parse_exec.c Prevented a string overflow in case of program error. Coverity CID 28767. 2015.11.04.123926 [5564] xorriso/text_io.c Prevented a possible program error in Xorriso_dialog_input() 2015.11.04.155640 [5565] xorriso/opts_p_z.c Prevented possible buffer overflow inside xorriso object. Coverity CID 28775. 2015.11.04.155640 [5566] xorriso/parse_exec.h xorriso/parse_exec.c xorriso/opts_a_c.c Prevented a possible buffer overrun with -concat. Coverity CID 28782. 05 Nov 2015 [5567] xorriso/make_xorriso_1.c Closed a gap for line buffer overflow with man page production. Coverity CID 28784. 05 Nov 2015 [5568] test/compare_file.c Checking argument length of test/compare_file. Coverity CID 28785, 28786. 2015.11.06.092940 [5569] xorriso/iso_manip.c Prevented a potential corruption in case of memory shortage. Coverity CID 28800. 2015.11.06.123344 [5570] xorriso/iso_tree.c Prevented use of uninitialized variable in case of memory shortage. Coverity CID 28801. 2015.11.06.124252 [5571] xorriso/read_run.c Prevented use of uninitialized variable in case of memory shortage. Coverity CID 28805. 2015.11.06.124702 [5571] xorriso/text_io.c Prevented use of uninitialized variable in case of memory shortage. Coverity CID 28806. 2015.11.06.125101 [5573] xorriso/disk_ops.c Prevented use of uninitialized variable in case of memory shortage. Coverity CID 28808. 2015.11.06.125705 [5574] xorriso/disk_ops.c Prevented use of uninitialized variable in case of memory shortage. Coverity CID 28809. 2015.11.06.130635 [5575] xorriso/drive_mgt.c Removed unneccessary variables which were used uninitialized. Coverity CID 28810. 2015.11.06.181047 [5576] xorriso/parse_exec.c Reacting on failure while reading mkisofsrc. Coverity CID 28817. 2015.11.06.194638 [5577] xorriso/text_io.c Corrected improper reaction on memory shortage. Coverity CID 28825. 2015.11.06.200320 [5578] xorriso/opts_a_c.c Reacting properly on failure to set hppa or alpha boot image. Coverity CID 28828, 28829. 2015.11.07.105529 [5579] xorriso/check_media.c Prevented use of already freed memory. Coverity CID 28830. 2015.11.07.110421 [5580] xorriso/base_obj.c Closed a memory leak wiyh -check_media_defaults 2015.11.07.111034 [5581] xorriso/opts_i_o.c Changed a suspiciously looking but correct code gesture. Coverity CID 28831. 2015.11.08.110314 [5582] xorriso/drive_mgt.c Clarified handling of missing drives when it shall be given up. Coverity CID 28697. 2015.11.08.140544 [5583] xorriso/text_io.c Removed duplicate check for memory shortage. Coverity CID 28701. 2015.11.08.143244 [5584] xorriso/text_io.c Moved variable definition into its proper ifdef case. Coverity CID 28702. 2015.11.08.161658 [5585] xorriso/lib_mgt.c Clarified code path ifndef Xorriso_with_libjtE. Coverity CID 28703. 2015.11.08.163431 [5586] xorriso/iso_tree.c Corrected interpretation of some return values during -find -exec list_extattr. Coverity CID 28704. 2015.11.08.164013 [5587] xorriso/read_run.c Checking the return value of Xorriso_open_job_data_to(). Coverity CID 28705. 2015.11.08.194028 [5588] xorriso/emulators.c Removed a duplicate test statement. Coverity CID 28709. 2015.11.08.194419 [5589] xorriso/disk_ops.c Removed a duplicate test statement. Coverity CID 28710. 2015.11.08.200543 [5590] xorriso/drive_mgt.c Preserved return value of iso_util_decode_md5_tag() until it is tested. Coverity CID 28711. 2015.11.08.202957 [5591] xorriso/iso_img.c With -mount: Checking Xorriso_give_up_drive return value for error. Coverity CID 28712. 2015.11.09.095505 [5592] Removed a surplus test statement with -boot_image any show_status. Coverity CID 28713. 2015.11.09.105122 [5593] xorriso/write_run.c Removed an effectively unused variable. Coverity CID 28715. 2015.11.09.121421 [5594] xorriso/lib_mgt.c Clarified handling of NULL name with Xorriso_set_local_charset(). Coverity CID 28719. 2015.11.09.133135 [5595] libisoburn/isofs_wrap.c Prevented a SIGSEGV with isoburn_read_image() feature to submit NULL parameter. Coverity CID 28721. 2015.11.10.084903 [5596] xorriso/text_io.c Improved handling of potential program error with -msg_op read_sieve. Coverity CID 28723. 2015.11.10.090901 [5597] xorriso/drive_mgt.c Prevented possible NULL pointer dereference. Coverity CID 28724. 2015.11.10.094015 [5598] xorriso/misc_funct.c Corrected an error with conversion from bourne shell patterns to regular expressions. Coverity CID 28727. 2015.11.10.104918 [5599] xorriso/iso_img.c Corrected an error with recognition of APM partitions. Coverity CID 28728. 2015.11.11.192609 [5600] xorriso/text_io.c Corrected reporting of -logfile in -status "short". Coverity CID 28729. 2015.11.12.123345 [5601] xorriso/iso_tree.c Protected output -find -exec list_extattr from potential overflow. Coverity CID 28780. 2015.11.12.142649 [5602] xorriso/iso_tree.c Reacted on compiler warnings about rev 5601 2015.11.12.160620 [5603] xorriso/text_io.c Prevented possible overlapping memory copying 2015.11.13.152751 [5604] xorriso/iso_tree.c Prevented possible buffer overflow with -getfacl. Coverity CID 28695. 2015.11.13.202241 [5605] xorriso/drive_mgt.c Handled failure to release a mutex lock. Coverity CID 28698. 2015.11.15.153822 [5606] xorriso/text_io.c Avoiding to use message system while result_msglists_lock is acquired. Coverity CID 28726. 2015.11.16.100618 [5607] xorriso/lib_mgt.c Rev 5594 was not complete. Coverity CID 28719. 2015.11.16.102246 [5608] xorriso/write_run.c Rev 5560 was not complete. Coverity CID 28764. 16 Nov 2015 [5609] test/compare_file.c Trying to finally silence Coverity CID 28785. 21 Nov 2015 [5610] README xorriso/README_gnu_xorriso + frontend/xorriso_broker.sh New demo script frontend/xorriso_broker.sh (see Debian bug 805495) 22 Nov 2015 [5611] Makefile.am xorriso/make_xorriso_standalone.sh xorriso/xorriso_makefile_am.txt Putting xorriso_broker.sh into release tarballs 22 Nov 2015 [5612] frontend/xorriso_broker.sh Small improvements with xorriso_broker.sh 28 Nov 2015 [5618] svn copy -m Branching for libisoburn release 1.4.2 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.4.2 2015.11.28.140001 [5619] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.4.2 28 Nov 2015 [5620] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.4.2 - 2015.11.28.140001 * Bug fix: -backslash_codes "on" did not work outside quotes and with showing "\r" * New API calls isoburn_ropt_set_truncate_mode() isoburn_ropt_get_truncate_mode() * New options with isoburn_ropt_set_extensions(): isoburn_ropt_map_* * New command -modesty_on_drive, new -as cdrecord -immed, minbuf=, modesty_on_drive= * New command -ecma119_map * New command -read_fs * New -boot_image action "replay" * New command -file_name_limit, -as mkisofs -file_name_limit * New -find test -name_limit_blocker. * Result of a Coverity audit: 50+ code changes, but no easy-to-trigger bugs 2015.11.29.082702 [5625] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.4.3 29 Nov 2015 [5626] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-1.4.3 - 2015.11.29.082702 29 Nov 2015 [5627] svn move -m 'Promoted branch to tag' \ http://svn.libburnia-project.org/libisoburn/branches/1.4.2 \ http://svn.libburnia-project.org/libisoburn/tags/1.4.2 29 Nov 2015 [5628] xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 Fixed typos in manual of xorrecord 02 Dec 2015 [5629] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 Reacted on lintian complaint about oversized lines in man pages 05 Dec 2015 [5630] Makefile.am xorriso/xorriso_makefile_am.txt Letting "make" target "check" run xorriso/xorriso -no_rc -version -list_extras 2015.12.09.132636 [5631] xorriso/xorriso_private.h xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/emulators.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Bug fix: -as mkisofs did not unescape "\=" in the source part of pathspecs 2015.12.09.162402 [5632] xorriso/xorriso_private.h xorriso/opts_a_c.c xorriso/misc_funct.c Bug fix: -pathspecs "on" did not properly handle "\\=" 09 Dec 2015 [5633] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.3 - 2015.12.09.162402 * Bug fix: -as mkisofs did not unescape "\=" in the source part of pathspecs * Bug fix: -pathspecs "on" did not properly handle "\\=" * New -pathspecs mode "as_mkisofs" 10 Dec 2015 [5635] configure.ac xorriso/configure_ac.txt acinclude.m4 Checking for availability of LIBBURN_ARCH_LIBS 2015.12.11.120319 [5636] xorriso/xorrisoburn.h Forgot to commit declaration of Xorriso_graftable_pathspec() in rev 5631 15 Dec 2015 [5637] configure.ac xorriso/configure_ac.txt acinclude.m4 Following build system changes in libisofs about ACL and xattr 2015.12.22.133736 [5638] xorriso/xorriso_timestamp.h Setting timestamp of experimental xorriso-1.4.3 tarball ------------------------------------ cycle - xorriso-1.4.3 - 2015.12.22.133736 2015.12.23.112013 [5639] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/write_run.c Bug fix: -boot_image "any" "system_area=/dev/zero" did not zeroize loaded data 2015.12.24.080343 [5640] xorriso/iso_img.c El Torito load size 0 what not shown with -report_el_torito as_mkisofs or cmd 2015.12.26.090812 [5641] xorriso/iso_img.c Silencing a false warning of gcc on Sid about uninitialized "image" variable 26 Dec 2015 [5642] xorriso/make_xorriso_standalone.sh Updated the production script for GNU xorriso to work with plain repo checkouts 26 Dec 2015 [5643] xorriso/make_xorriso_standalone.sh Letting production of GNU xorriso ignore failure of command "man -H" 27 Dec 2015 [5644] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.3 - 2015.12.27.150821 * Bug fix: -boot_image "any" "system_area=/dev/zero" did not zeroize loaded data 27 Dec 2015 [5645] Makefile.am xorriso/make_xorriso_standalone.sh xorriso/xorriso_makefile_am.txt + frontend/grub-mkrescue-sed.sh Man-in-the-middle script to manipulate the xorriso run of grub-mkrescue 28 Dec 2015 [5646] frontend/grub-mkrescue-sed.sh Added modes "mbr_only", "original" and parameter variables MKRESCUE_SED_* 2015.12.30.175951 [5647] xorriso/emulators.c xorriso/write_run.c frontend/grub-mkrescue-sed.sh Provisory new -e pseudo path --interval:appened_partition_N:all:: 2016.01.01.172059 [5648] libisoburn/isoburn.c Letting bit15 of system_area_options pass to libisofs 2016.01.01.172817 [5649] xorriso/xorriso_private.h xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/emulators.c xorriso/iso_img.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -boot_image setting mbr_force_bootable=, -as mkisofs --mbr-force-bootable ------------------------------------ cycle - xorriso-1.4.3 - 2016.01.01.172817 * New -boot_image setting mbr_force_bootable=, -as mkisofs --mbr-force-bootable 02 Jan 2016 [5650] frontend/grub-mkrescue-sed.sh Fixed a copy+paste bug of rev 5647. Thanks to Alexander E. Patrakov. 2016.01.20.104201 [5651] xorriso/opts_a_c.c Let -boot_image bin_path= , efi_path= recognize --interval:appended_partition 2016.01.20.104342 [5652] xorriso/iso_img.c Changed report format of --interval:appended_partition_X_start_Y_size_Z 2016.02.05.095140 [5660] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c New API calls isoburn_igopt_set_appended_as_apm, isoburn_igopt_set_part_like_isohybrid 2016.02.05.100719 [5661] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/write_run.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -boot_image bootspec appended_part_as=apm, part_like_isohybrid=on 2016.02.05.101337 [5662] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs options -appended_part_as_apm, -part_like_isohybrid 2016.02.05.145901 [5665] libisoburn/isoburn.h xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/opts_d_h.c xorriso/drive_mgt.c xorriso/disk_ops.c xorriso/sort_cmp.c xorriso/text_io.c frontend/xorriso-tcltk xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 Fixed typos reported by lintian 2016.02.07.194339 [5666] libisoburn/libisoburn.h Reacted on doxygen warnings of Fedora s390x build log 2016.02.27.170008 [5668] xorriso/iso_img.c Image size tolerance of 300 kB when recognizing genisoimage -B "..." 2016.03.01.141456 [5669] xorriso/opts_i_o.c Bug fix: -modesty_on_drive properties timeout_sec, min_usec, max_usec read wrong numbers from the parameter text 01 Mar 2016 [5670] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.3 - 2016.03.01.141456 * New -boot_image bootspec appended_part_as=apm, part_like_isohybrid=on * New -as mkisofs options -appended_part_as_apm, -part_like_isohybrid * Bug fix: -modesty_on_drive properties timeout_sec, min_usec, max_usec read wrong numbers from the parameter text 05 Mar 2016 [5671] doc/faq.wiki Added answer to "Why is simultaneous burning with multiple drives so slow ?" 2016.03.08.075457 [5673] xorriso/drive_mgt.c Improved handling of TAO CD tracks by -check_media 2016.03.09.205708 [5674] xorriso/sfile.c Removed an unused function 2016.03.10.200551 [5676] xorriso/parse_exec.c xorriso/sfile.c xorriso/text_io.c Replaced unused timezone parameter of gettimeofday() by NULL 2016.03.15.203403 [5677] libisoburn/libisoburn.h Added to libisoburn.h "extern C" for C++, and protection against double include 2016.03.15.203626 [5678] xorriso/xorriso.h Added "extern C" to xorriso.h 15 Mar 2016 [5679] doc/qemu_xorriso.wiki Updated QemuXorriso wiki article for qemu-2.1.2 2016.03.18.135548 [5681] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/text_io.c xorriso/emulators.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 libisoburn/libisoburn.ver New command -scsi_dev_family, new -as cdrecord option drive_scsi_dev_family= 18 Mar 2016 [5683] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.3 - 2016.03.18.135548 * New command -scsi_dev_family, new -as cdrecord option drive_scsi_dev_family= 20 Mar 2016 [5684] doc/faq.wiki Updated answer to "Why is simultaneous burning with multiple drives so slow ?" 2016.03.22.125444 [5685] xorriso/text_io.c Silenced a warning of gcc on Fedora rawhide s390 8 Apr 2016 [5691] frontend/grub-mkrescue-sed.sh Optional use of gdb in grub-mkrescue-sed.sh 19 Apr 2016 [5692] acinclude.m4 Makefile.am Prevented option --version-script with linker run of xorriso. By Matthias Klose. 2016.04.25.100906 [5694] xorriso/iso_img.c xorriso/findjob.h xorriso/findjob.c xorriso/iso_tree.h xorriso/iso_tree.c xorriso/iso_manip.c Improved assessment of appended partitions 2016.04.27.092902 [5695] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Letting -as mkisofs --norock revoke the special effect of -r 27 Apr 2016 [5696] configure.ac xorriso/configure_ac.txt Removed option --silent from libtool runs 2016.05.03.075722 [5698] libisoburn/isofs_wrap.c Letting isoburn_disc_erase() invalidate UDF extended descriptors if present 03 May 2016 [5699] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.3 - 2016.05.03.075722 * Letting -as mkisofs --norock revoke the special effect of -r * Letting -blank on overwritable media invalidate UDF extended descriptors 01 Jul 2016 [5704] svn copy -m Branching for libisoburn release 1.4.4 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.4.4 2016.07.01.140001 [5705] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.4.4 01 Jul 2016 [5706] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.4.4 - 2016.07.01.140001 * Bug fix: -as mkisofs did not unescape "\=" in the source part of pathspecs * Bug fix: -boot_image "any" "system_area=/dev/zero" did not zeroize loaded data * Bug fix: -pathspecs "on" did not properly handle "\\=" * Bug fix: HFS+ production could cause MBR partition of type 0xEE without GPT * Bug fix: HFS+ directories could announce more children than they actually have * Bug fix: The HFS+ filesystem was not marked by in GPT of GRUB2 hybrid layout * Bug fix: When reading an ISO filesystem, the presence of --protective-msdos-label was not recognized if a partition is appended * Bug fix: xorrisofs option --protective-msdos-label did not work if no system area data were given by option -G or alike * Bug fix: -modesty_on_drive properties timeout_sec, min_usec, max_usec read wrong numbers from the parameter text * Letting -as mkisofs --norock revoke the special effect of -r * Letting -blank on overwritable media invalidate UDF extended descriptors * New -pathspecs mode "as_mkisofs" * New -boot_image setting mbr_force_bootable=, -as mkisofs --mbr-force-bootable * New -boot_image bootspecs appended_part_as=apm, part_like_isohybrid=on * New -as mkisofs options -appended_part_as_apm, -part_like_isohybrid * New command -scsi_dev_family, new -as cdrecord option drive_scsi_dev_family= 2016.07.02.142514 [5711] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.4.5 2016.07.02.144635 [5712] xorriso/opts_p_z.c Updated copyright year of xorriso -version 02 Jul 2016 [5713] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-1.4.5 - 2016.07.02.144635 02 Jul 2016 [5714] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.4.4 http://svn.libburnia-project.org/libisoburn/tags/1.4.4 03 Jul 2016 [5715] Makefile.am xorriso/xorriso_makefile_am.txt Added missing parameter "all" to "make check" run of xorriso 2016.07.03.204237 [5717] xorriso/lib_mgt.c xorriso/iso_manip.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Corrected spelling errors found by lintian 2016.07.16.111233 [5718] xorriso/iso_manip.c xorriso/text_io.c Corrected spelling errors found by lintian.debian.org 2016.07.20.083654 [5719] libisoburn/isoburn.h libisoburn/burn_wrap.c xorriso/xorriso.h xorriso/xorriso_private.h xorriso/lib_mgt.c xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/iso_manip.c xorriso/findjob.h frontend/sh_on_named_pipes.sh frontend/xorriso-tcltk frontend/grub-mkrescue-sed.sh xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info releng/README Reacted on some of the complaints of codespell 2016.07.21.103607 [5721] xorriso/xorriso_private.h Including pthread.h in xorriso_private.h to define pthread_mutex_t on OpenBSD 31 Jul 2016 [5729] libisoburn/libisoburn.h Requiring libburn-1.4.5 2016.07.31.073903 [5730] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/emulators.c xorriso/drive_mgt.c xorriso/write_run.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 libisoburn/libisoburn.ver New command -use_immed_bit, new -as cdrecord option use_immed_bit= 31 Jul 2016 [5731] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.5 - 2016.07.31.073903 * New command -use_immed_bit, new -as cdrecord option use_immed_bit= 2016.08.04.073312 [5733] xorriso/misc_funct.c Corrected weekday name in debug message of --modification-date. Thanks to Chris Lamb. 2016.08.05.112045 [5734] libisoburn/libisoburn.h Mentioned that an empty name with isoburn_igopt_set_scdbackup_tag disables it 05 Aug 2016 [5735] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Mentioned that empty record_name disables scdbackup tagging 2016.08.06.111650 [5736] xorriso/write_run.h xorriso/xorriso_eng.html Bug fix: The default setting of -compliance did not apply rec_mtime to Joliet and ISO:1999. mkisofs emulation was not affected by this bug. 2016.08.06.132358 [5737] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs option --set_all_file_dates 07 Aug 2016 [5738] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Documented new timestamp behavior of El Torito catalog file 2016.08.07.193333 [5739] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.5 - 2016.08.07.193333 * Bug fix: The default setting of -compliance did not apply rec_mtime to Joliet and ISO:1999. mkisofs emulation was not affected by this bug. * New -as mkisofs option --set_all_file_dates 12 Aug 2016 [5740] configure.ac Eased re-enabling of libtool --silent at bootstrap time 2016.08.12.184736 [5741] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver New API calls isoburn_igopt_set_gpt_guid(), isoburn_igopt_get_gpt_guid() 2016.08.12.185822 [5742] xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/emulators.c xorriso/lib_mgt.c xorriso/iso_img.c xorriso/write_run.c xorriso/text_io.c xorriso/misc_funct.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New bootspec "gpt_disk_guid=", new -as mkisofs option --gpt_disk_guid, new -report_system_area mode "make_guid" 12 Aug 2016 [5743] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.5 - 2016.08.12.185822 * New bootspec "gpt_disk_guid=", new -as mkisofs option --gpt_disk_guid * New -report_system_area mode "make_guid" 2016.08.15.102443 [5744] xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Corrected interpretation of RFC 4122 UUID strings for GPT. Thanks Andrei Borzenkov. 2016.08.15.175002 [5745] xorriso/xorrisoburn.h xorriso/lib_mgt.c xorriso/iso_img.c New -report_system_area mode "gpt_disk_guid" 2016.08.16.131434 [5746] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_p_z.c xorriso/emulators.c xorriso/write_run.c xorriso/text_io.c New -volume_date mode "all_file_dates" 2016.08.16.131907 [5747] xorriso/parse_exec.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New environment variable SOURCE_DATE_EPOCH 16 Aug 2016 [5748] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.5 - 2016.08.16.131907 * New -report_system_area mode "gpt_disk_guid" * New -volume_date mode "all_file_dates" * New environment variable SOURCE_DATE_EPOCH 2016.08.20.085236 [5749] xorriso/opts_d_h.c xorriso/findjob.h xorriso/iso_manip.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -find action "set_to_mtime" 2016.08.20.102601 [5750] xorriso/xorriso_private.h xorriso/opts_p_z.c xorriso/emulators.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -volume_date "all_file_dates" pseudo-timestamp "set_to_mtime" 20 Aug 2016 [5751] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.5 - 2016.08.20.102601 * New -find action "set_to_mtime" 2016.08.25.205059 [5753] xorriso/drive_mgt.c Corrected block count messages with -check_media sector_map= 28 Aug 2016 [5755] doc/doxygen.conf.in Removed obsoleted doxygen.conf tags XML_SCHEMA, XML_DTD, SHOW_DIRECTORIES, HTML_ALIGN_MEMBERS 2016.09.12.091506 [5757] xorriso/parse_exec.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Changed SOURCE_DATE_EPOCH effect to --set_all_file_dates "set_to_mtime" 12 Sep 2016 [5758] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Mentioned --interval:appended_partition_NNN in man pages 2016.09.12.163816 [5759] xorriso/opts_d_h.c Bug fix: -file_size_limit did not increase ISO level if necessary. Thanks Mattias Schlenker. 16 Sep 2016 [5764] svn copy -m Branching for libisoburn release 1.4.6 http://svn.libburnia-project.org/libisoburn/trunk http://svn.libburnia-project.org/libisoburn/branches/1.4.6 2016.09.16.133001 [5765] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.4.6 16 Sep 2016 [5766] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 16 Sep 2016 [5767] configure.ac Corrected secondary .so number to 103 ----------------------------------- release - xorriso-1.4.6 - 2016.09.16.133001 * Bug fix: The default setting of -compliance did not apply rec_mtime to Joliet and ISO:1999. mkisofs emulation was not affected by this bug. * Bug fix: -file_size_limit did not increase ISO level if necessary. Thanks Mattias Schlenker. * New API calls isoburn_igopt_set_gpt_guid(), isoburn_igopt_get_gpt_guid() * New command -use_immed_bit, new -as cdrecord option use_immed_bit= * New -volume_date mode "all_file_dates" * New -as mkisofs option --set_all_file_dates * New bootspec "gpt_disk_guid=", new -as mkisofs option --gpt_disk_guid * New -report_system_area modes "gpt_disk_guid", "make_guid" * New environment variable SOURCE_DATE_EPOCH * New -find action "set_to_mtime" 2016.09.16.193454 [5772] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.4.7 16 Sep 2016 [5773] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ------------------------------------ cycle - xorriso-1.4.7 - 2016.09.16.193454 17 Sep 2016 [5774] svn move -m Promoted branch to tag http://svn.libburnia-project.org/libisoburn/branches/1.4.6 http://svn.libburnia-project.org/libisoburn/tags/1.4.6 07 Oct 2016 [5775] libisoburn/libisoburn.ver Corrected sorting in libisoburn.ver 2016.10.23.095558 [76ac0d8] xorriso/changelog.txt A first try to commit to git 2016.11.13.101258 [8adbb1d] [5776] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/emulators.c xorriso/iso_img.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Bug fix: -as mkisofs -no-emul-boot without -boot-load-size defaulted to size 4 13 Nov 2016 [fb7da95] [5777] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.7 - 2016.11.10.134753 * Bug fix: -as mkisofs -no-emul-boot without -boot-load-size defaulted to size 4 * Bug fix: -read_fs "norock" did not prevent reading of root Rock Ridge 2016.11.18.173658 [3da2ac2] [5780] libisoburn/libisoburn.h xorriso/write_run.c Applying -dummy mode explicitely to superblock relocation 2016.11.19.172243 [7371115] [5761] xorriso/opts_d_h.c xorriso/iso_manip.c xorriso/disk_ops.c xorriso/findjob.h xorriso/findjob.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -find tests -maxdepth and -mindepth 27 Nov 2016 [b48c54c] [5782] xorriso/xorriso_eng.html Adapted in web page xorriso-tcltk links and changed SVN/bzr references to git 2016.12.05.101546 [9b75d21] [5783] xorriso/match.c Bug fix: Mix of absolute and relative disk paths could cause SIGSEGV with pattern expansion 2016.12.05.132927 [7b12dd0] [5784] xorriso/xorriso.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/iso_tree.c xorriso/match.c xorriso/misc_funct.h xorriso/misc_funct.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New command -update_lxi 2016.12.14.091711 [85aa000] [5785] xorriso/xorriso.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New command -update_li 2016.12.14.093149 [8a0d936] [5786] xorriso/xorriso_main.c Changed in usage text "option" to "command" 19 Dec 2016 [85c51db] [5787] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.7 - 2016.12.19.195610 * Bug fix: Mix of absolute and relative disk paths could cause SIGSEGV with pattern expansion * New -find tests -maxdepth and -mindepth * New commands -update_lxi and -update_li 23 Dec 2016 [fa3ee61] [5788] frontend/grub-mkrescue-sed.sh New grub-mkrescue-sed.sh mode "gpt_appended" 23 Dec 2016 [08b316d] [5789] frontend/grub-mkrescue-sed.sh Changed default mode of grub-mkrescue-sed.sh to "mbr_only" 23 Dec 2016 [783e706] [5790] frontend/grub-mkrescue-sed.sh Changed in grub-mkrescue-sed.sh default of MKRESCUE_SED_PROTECTIVE to "no" 27 Jan 2017 [aa34637] [5793] acinclude.m4 xorriso/configure_ac.txt COPYRIGHT Detecting Linux systems without generic SCSI support by scsi/scsi.h 27 Jan 2017 [3cac9fe] [5794] acinclude.m4 Small adjustement for previous commit 27 Jan 2017 [db36ca5] [5796] xorriso/configure_ac.txt Disable multi-threading in libjte of GNU xorriso if pthread_cancel() is missing 29 Jan 2017 [9d912df] [5799] xorriso/man_xorriso_to_html.sh Corrected in HTML man page a flaw with headline about Blind Growing 2017.02.13.145207 [0ec0ba8] [5803] xorriso/drive_mgt.c xorriso/write_run.c Properly refusing on Pseudo Overwritable formatted BD-R 22 Feb 2017 [caedaa8] [5804] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Clarified effect of -overwite mode "off" 2017.02.27.091402 [0d07bd1] [5805] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver New API calls isoburn_igopt_set_iso_mbr_part_type(), isoburn_igopt_get_iso_mbr_part_type() 2017.02.27.092840 [f3b8515] [5806] libisoburn/libisoburn.h Corrected an error in description of isoburn_igopt_get_iso_mbr_part_type() 2017.02.27.093756 [3a41524] [5807] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/text_io.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -boot_image bootspec iso_mbr_part_type= 2017.02.27.094332 [274715a] [5808] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs option -iso_mbr_part_type ------------------------------------ cycle - xorriso-1.4.7 - 2017.02.27.094332 * Properly refusing on Pseudo Overwritable formatted BD-R * New API calls isoburn_igopt_set_iso_mbr_part_type(), isoburn_igopt_get_iso_mbr_part_type() * New -boot_image bootspec iso_mbr_part_type= * New -as mkisofs option -iso_mbr_part_type 2017.04.09.175231 [2f24d82] [] xorriso/iso_img.c Avoiding to recognize real GPT as -part_like_isohybrid 2017.04.09.175347 [4c45684] [] xorriso/iso_img.c With -report_system_area as_mkisofs ignore GPT if same start as MBR partition 10 Apr 2017 [e30507d] [] frontend/grub-mkrescue-sed.sh Made -iso_mbr_part_type 0x00 default of grub-mkrescue-sed.sh mode "mbr_only" 2017.04.25.135002 [d812835] [] xorriso/configure_ac.txt Bug fix: Variable LDFLAGS was emptied during ./configure test for jtethreads 05 May 2017 [22d552a] [] xorriso/configure_ac.txt Preliminary blocking --enable-libcdio if CFLAGS, LDFLAGS contain option -static 2017.05.05.123120 [e72d702] [] libisoburn/isofs_wrap.c When pseudo-blanking: Invalidate all ECMA-119 volume descriptors, not only PVD 09 May 2017 [5865cef] README Replaced link to vanished svn repository by link to git repository 2017.05.09.081543 [43c37e3] xorriso/xorriso.h xorriso/xorriso_main.c xorriso/opts_p_z.c xorriso/emulators.c Changed copyright date to 2017 09 May 2017 [3e3fddd] xorriso/README_gnu_xorriso Replaced link to vanished svn repository by link to git repository 2017.06.03.132429 [2126dce] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs option -eltorito-platform 2017.06.03.163742 [92ae870] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Made small adjustment to new -as mkisofs option -eltorito-platform 03 Jun 2017 [c9df1a1] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.7 - 2017.06.03.163742 * New -as mkisofs option -eltorito-platform 2017.06.03.193458 [3d66432] xorriso/iso_img.c Making use of -eltorito-platform with -report_el_torito as_mkisofs 2017.06.29.211523 [3972c72] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Mentioned -append_partition in manual about --mbr-force-bootable 29 Jun 2017 [a2b52b4] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.7 - 2017.06.29.211523 * Bug fix: --mbr-force-bootable did not get into effect with -append_partition 2017.07.21.105310 [4bb3dbf] xorriso/iso_img.c Bug fix: Exit value of failed -mount command was reported as 0 2017.08.07.134036 [2cab210] xorriso/iso_manip.c Fixed a code typo which luckily had no negative effect with existing use cases 08 Aug 2017 [d9c93a6] releng/run_all_auto releng/inc/releng_getopts.inc Bug fix: ./run_all_auto aborted on kojipkgs.fedoraproject.org on tput attempt. Thanks Robert Scheck. 14 Aug 2017 [e8b387c] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.7 - 2017.08.14.160231 * Bug fix: Exit value of failed -mount command was reported as 0 * Bug fix: -boot_image actions "keep" and "patch" did not work any more. Regression by libisofs 1.4.4. 2017.08.24.102256 [e74b04a] xorriso/iso_img.c Issue iso_mbr_part_type info with -report_system_area "cmd" and "as_mkisofs" 2017.08.24.102427 [047b8db] xorriso/write_run.c Accept partition interval strings of type "imported_iso" for multi-session 06 Sep 2017 [650c015] doc/faq.wiki doc/partition_offset.wiki doc/qemu_xorriso.wiki Updated libisoburn doc/*.wiki files by current publicly shown files 2017.09.09.142818 [ea8a759] xorriso/iso_img.c Made sure only legal ECMA-119 timestrings come from -report_system_area as_mkisofs 2017.09.10.164942 [3813a74] xorriso/iso_img.c Prevented in -report_system_area "as_mkisofs" a confusion about -appended_part_as_gpt and -part_like_isohybrid 2017.09.12.143001 [19e1303] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.4.8 12 Sep 2017 [0e85c8c] ChangeLog xorriso/changelog.txt Documented changes and release timestamp 12 Sep 2017 [5d5e7be] libisoburn/libisoburn.h Making a forgotten version leap change in libisoburn.h 12 Sep 2017 [8c4f8da] releng/releng_generated_data/placeholder Making a directory which was not copied from SVN to git ----------------------------------- release - xorriso-1.4.8 - 2017.09.12.143001 * Bug fix: -as mkisofs -no-emul-boot without -boot-load-size defaulted to size 4 * Bug fix: -read_fs "norock" did not prevent reading of root Rock Ridge * Bug fix: Mix of absolute and relative disk paths could cause SIGSEGV with pattern expansion * Bug fix: --mbr-force-bootable did not get into effect with -append_partition * Bug fix: Exit value of failed -mount command was reported as 0 * Bug fix: -boot_image actions "keep" and "patch" did not work any more. Regression by libisofs 1.4.4. * New -find tests -maxdepth and -mindepth * New commands -update_lxi and -update_li * Properly refusing on Pseudo Overwritable formatted BD-R * New API calls isoburn_igopt_set_iso_mbr_part_type(), isoburn_igopt_get_iso_mbr_part_type() * New -boot_image bootspec iso_mbr_part_type= * New -as mkisofs option -iso_mbr_part_type * New -as mkisofs option -eltorito-platform 2017.09.13.094536 [bc544e0] configure.ac libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Version leap to libisoburn-1.4.9 13 Sep 2017 [187d796] ChangeLog xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.9 - 2017.09.13.094536 17 Sep 2017 [2e560d1] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Corrected spelling errors found by lintian 2017.09.23.074650 [f61d818] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Publishing a libisofs bug fix as GNU xorriso development snapshot tarball ------------------------------------ cycle - xorriso-1.4.9 - 2017.09.23.074650 libisofs: * Bug fix: Reading beyond array end for HFS+ production caused SIGSEGV with FreeBSD 11 CLANG -O2. Thanks ASX of GhostBSD. 29 Sep 2017 [c3d302e] Makefile.am xorriso/xorriso_makefile_am.txt xorriso/make_xorriso_standalone.sh frontend/xorriso-tcltk xorriso/make_xorriso_1.c xorriso/make_docs.sh + xorriso/xorriso-tcltk.texi + xorriso/xorriso-tcltk.info + xorriso/xorriso-tcltk.1 Created manual page and info document for xorriso-tcltk 2017.10.23.095252 [25e2491] libisoburn/libisoburn.h libisoburn/isofs_wrap.c libisoburn/burn_wrap.c New bit10 of isoburn_drive_aquire() to accept all xattr namespaces 2017.10.23.100749 [cbc5dbf] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/write_run.c xorriso/read_run.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -xattr mode "any" to process all xattr namespaces of local filesystem 2017.10.23.101135 [761ff8f] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs option --xattr-any 2017.10.30.122625 [3eee060] xorriso/emulators.c Code change which was forgotten with previous commit 2017.11.15.100856 [3794753] xorriso/iso_manip.c xorriso/iso_tree.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Enabling non-user xattr namespaces with -getfattr and -setfattr 2017.11.15.101228 [d574a69] xorriso/opts_p_z.c Fixing bug from rev cbc5dbf: unknown -xattr mode caused mode "off" 2017.11.16.114215 [143f60f] xorriso/text_io.c Adapted field size of small pacifier numbers to size of large numbers 2017.11.17.143122 [d965b81] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs options -uid and -gid 17 Nov 2017 [dd6042a] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.9 - 2017.11.17.143122 * New bit10 of isoburn_drive_aquire() to accept all xattr namespaces * New -xattr mode "any" to process all xattr namespaces of local filesystem * New -as mkisofs option --xattr-any * New -as mkisofs options -uid and -gid 2017.11.25.095955 [2d35076] xorriso/iso_img.c Recognizing appended partitions with partition offset 16 12 Feb 2018 [447b427] xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 Corrected typo "sdc" <> "scd" in xorrecord documentation 22 Mar 2018 [e72ab34] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Small corrections and enhancements in man xorriso 2018.05.08.092636 [198ecef] libisoburn/isofs_wrap.c Bug fix: Multi-session emulation was not recognized with non-zero partition offset 2018.05.18.152119 [b1a47ed] xorriso/configure_ac.txt Preferring Linux include file sys/xattr.h over attr/attr.h Aug 08 2018 [675f9fe] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.4.9 - 2018.08.08.155040 * Bug fix: Multi-session emulation was not recognized with non-zero partition offset libburn: * Bug fix: SIGSEGV could happen if a track ended by reaching its fixed size while the track source still was willing to deliver bytes 2018.09.01.191851 [92665c8] xorriso/drive_mgt.c xorriso/check_media.h xorriso/check_media.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Corrected rank of -checkmedia quality "md5_mismatch" 2018.09.09.125911 [7add413] xorriso/iso_img.c Adapted -report_system_area perception of appended partitions to changes in libisofs 2018.09.09.130316 [8765230] xorriso/iso_img.c Letting -report_system_area propose --mbr-force-bootable more often 2018.09.12.113255 [c30c89d] xorriso/iso_img.c Improved in -report_system_area recognition of appended GPT partitions 2018.09.15.133001 [f0b6ea7] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info xorriso/xorriso-tcltk.texi (should have been done) xorriso/xorriso-tcltk.1 (should have been done) xorriso/xorriso-tcltk.info (should have been done) Version leap to libisoburn-1.5.0 15 Sep 2018 [cb4ac1c] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.5.0 - 2018.09.15.133001 2018.09.16.145527 [08355f7] configure.ac libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info xorriso/xorriso-tcltk.texi xorriso/xorriso-tcltk.1 xorriso/xorriso-tcltk.info Made number transition to 1.5.1 2018.09.16.150917 [c2b33f4] xorriso/opts_p_z.c xorriso/emulators.c Updated copyright messages 16 Sep 2018 [c338597] ChangeLog xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.1 - 2018.09.16.150917 24 Sep 2018 [1efe7aa] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Corrected typo in documentation (found by lintian) 10 Oct 2018 [05732a2] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Corrected typo in documentation (found by younger lintian) 2018.10.10.114818 [1002df9] xorriso/cmp_update.c Displaying data file sizes with -update and -update_r 2018.10.12.104229 [22aa52b] xorriso/parse_exec.c xorriso/emulators.c Silenced gcc warnings about printf target buffer 21 Oct 2018 [9cfd466] test/compare_file.c Silenced gcc warning about printf target buffer 2018.11.01.100352 [ff68bda] xorriso/drive_mgt.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Re-enabled unconditional reporting of MD5 tags with -check_media 11 Nov 2018 [b8ee16a] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Changed prescription for safe loading of tray before xorrisofs add-on session 10 Jan 2019 [c56842e] acinclude.m4 Makefile.am Improved recognizability of copyright and license statements in various files 2019.01.10.194009 [122e43d] xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.1 - 2019.01.10.194009 * Bug fix in libisofs: Appended GPT partitions were not covered by the protective MBR partition * Bug fix in libisofs: Multi-session emulation spoiled GPT production "GPT partitions ... overlap". Regression towards 1.4.8 18 Feb 2019 [caf38e8] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver New API calls isoburn_igopt_set_part_type_guid(), isoburn_igopt_get_part_type_guid(), isoburn_igopt_set_iso_type_guid(), isoburn_igopt_get_iso_type_guid() 18 Feb 2019 [fc71cec] xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/iso_img.c xorriso/write_run.c xorriso/emulators.c xorriso/text_io.c xorriso/lib_mgt.c xorriso/misc_funct.h xorriso/misc_funct.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Enabled GPT type GUIDs with -append_partition, -boot_image any iso_mbr_part_type=, and -as mkisofs -iso_mbr_part_type 2019.02.18.121941 [7a1cac3] xorriso/xorriso_timestamp.h Updated xorriso timestamp 05 Apr 2019 [0722502] acinclude.m4 Made libisoburn ready for building out-of-source. Thanks Ross Burton. 05 Apr 2019 [720d289] xorriso/xorriso_makefile_am.txt Made GNU xorriso ready for building out-of-source. Thanks Ross Burton. 07 Apr 2019 [1ff0913] configure.ac xorriso/configure_ac.txt Disabled autotools macro AM_MAINTAINER_MODE on advise of Ross Burton 2019.04.08.115541 [e9a424a] xorriso/xorrisoburn.h xorriso/cmp_update.c xorriso/iso_tree.c xorriso/disk_ops.h xorriso/disk_ops.c Displaying directory tree sizes with -update and -update_r 2019.04.18.092737 [1f8d9c7] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/xorrisoburn.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/lib_mgt.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 libisoburn/libisoburn.ver New command -iso_nowtime to set fixed timestamp for synthetic files 2019.04.18.092941 [5b62c55] xorriso/parse_exec.c Bug fix: Environment variable SOURCE_DATE_EPOCH did not affect synthetic files 22 Apr 2019 [3a2a3ba] frontend/grub-mkrescue-sed.sh New control variable for grub-mkrescue-sed.sh: MKRESCUE_SED_IN_EFI_NO_PT 2019.04.22.082211 [95c2e7c] xorriso/text_io.c Correction of default output of command -status about -iso_nowtime 22 Apr 2019 [1eb51f4] frontend/grub-mkrescue-sed.sh grub-mkrescue-sed.sh: MKRESCUE_SED_IN_EFI_NO_PT="extra" to erase MBR signature 2019.05.02.175412 [aa50f5c] xorriso/write_run.c Re-assessing drive status after automatic formatting by a write run 2019.05.02.175539 [31450dc] xorriso/opts_p_z.c xorriso/emulators.c Updated year in copyright message texts 2019.07.06.130540 [c0ad17b] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.1 - 2019.07.06.130540 * New API calls isoburn_igopt_set_part_type_guid(), isoburn_igopt_get_part_type_guid(), isoburn_igopt_set_iso_type_guid(), isoburn_igopt_get_iso_type_guid() * Enabled GPT type GUIDs with -append_partition, -boot_image any iso_mbr_part_type=, and -as mkisofs -iso_mbr_part_type * Made libisoburn and GNU xorriso ready for building out-of-source. Thanks Ross Burton. 2019.07.13.153027 [3b0abc1] xorriso/drive_mgt.c Improved -toc summary of sessions with multiple tracks or non-data sessions 2019.07.22.181418 [397551f] xorriso/iso_img.c With report_system_area of iso_mbr_part_type: give MBR precedence over GPT 2019.07.24.073750 [321a047] xorriso/opts_p_z.c xorriso/text_io.h xorriso/text_io.c Bug fix: Very large ACL texts led to SIGSEGV with -setfacl* commands. Thanks Eliska Svobodova. 2019.07.24.141555 [e3ff5a9] xorriso/iso_manip.c Enabled verbous error reporting of iso_node_set_acl_text() 2019.08.12.152719 [fb61be0] xorriso/cmp_update.c test/compare_file.c Fixed a comparation report text problem with files of 4 GiB or larger. Thanks Eliska Svobodova. 2019.08.12.190808 [56a5962] xorriso/cmp_update.c xorriso/disk_ops.c xorriso/drive_mgt.c xorriso/emulators.c xorriso/iso_manip.c xorriso/opts_p_z.c xorriso/read_run.c Changed all occurences of (1<<31) to (1u<<31). Thanks Eliska Svobodova. 2019.08.13.160439 [e17db6a] xorriso/opts_d_h.c xorriso/parse_exec.c Avoided potential dereference of NULL. Thanks Eliska Svobodova. 2019.08.13.161913 [b356291] xorriso/iso_manip.c Added a missing pointer initialization. Thanks Eliska Svobodova. 2019.08.14.094038 [3eba8cc] xorriso/opts_p_z.c xorriso/emulators.c xorriso/text_io.c Closed potential memory leaks. Thanks Eliska Svobodova. 2019.08.14.141748 [06346c1] xorriso/opts_a_c.c Prevented potential string overruns. Thanks Eliska Svobodova. 2019.08.14.144754 [fe5fd8e] libisoburn/isofs_wrap.c Changed strncpy() to memcpy() in order to please static analyzers 2019.09.01.071701 [4db0aab] xorriso/match.c Bug fix: -disk_pattern on -add ./ -- mistook "./" for the root directory 2019.09.01.071948 [7066fd4] xorriso/iso_manip.c Improved rejection message in case case of local root directory as input file 2019.09.01.072438 [8828d6f] xorriso/parse_exec.c Bug fix: -disk_pattern on -add "" -- yielded SIGSEGV 2019.09.08.102946 [9019399] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/base_obj.c xorriso/text_io.c xorriso/emulators.c xorriso/drive_mgt.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 New command -drive_access, new -as cdrecord option --drive_not_exclusive 2019.09.29.205332 [a6328e8] xorriso/iso_img.c Prevented -report_system_area as_mkisofs from combining -efi-boot-part with -e --interval:appended_partition 2019.09.29.205504 [cb597ec] xorriso/text_io.c Clarified the message for file paths which are much too long 2019.10.11.104334 [e2c71a3] xorriso/opts_a_c.c xorriso/iso_tree.c xorriso/read_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Bug fix: -osirrox "concat_split_on" worked only together with -split_size larger than 0. Thanks William Willems. 2019.10.26.180001 [3c8f270] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info xorriso/xorriso-tcltk.texi xorriso/xorriso-tcltk.1 xorriso/xorriso-tcltk.info Version leap to libisoburn-1.5.2 26 Oct 2019 [74d9160] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.5.2 - 2019.10.26.180001 * Bug fix in libisofs: Appended GPT partitions were not covered by the protective MBR partition * Bug fix in libisofs: Multi-session emulation spoiled GPT production "GPT partitions ... overlap". Regression towards 1.4.8 * Bug fix: -disk_pattern on -add ./ -- mistook "./" for the root directory * Bug fix: -disk_pattern on -add "" -- yielded SIGSEGV * Bug fix: -osirrox "concat_split_on" worked only together with -split_size larger than 0. Thanks William Willems. * New API calls isoburn_igopt_set_part_type_guid(), isoburn_igopt_get_part_type_guid(), isoburn_igopt_set_iso_type_guid(), isoburn_igopt_get_iso_type_guid() * New command -drive_access, new -as cdrecord option --drive_not_exclusive * Enabled GPT type GUIDs with -append_partition, -boot_image any iso_mbr_part_type=, and -as mkisofs -iso_mbr_part_type * Made libisoburn and GNU xorriso ready for building out-of-source. Thanks Ross Burton. 2019.10.27.154326 [614e36f] configure.ac libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info xorriso/xorriso-tcltk.texi xorriso/xorriso-tcltk.1 xorriso/xorriso-tcltk.info Made number transition to 1.5.3 27 Oct 2019 [5903885] ChangeLog xorriso/changelog.txt Updated change log ------------------------------------ cycle - xorriso-1.5.3 - 2019.10.27.154326 2019.10.28.143339 [c8b9c9a] xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_private.h xorriso/xorriso_main.c xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/emulators.c xorriso/text_io.h xorriso/text_io.c xorriso/sort_cmp.c xorriso/iso_manip.c xorriso/lib_mgt.h xorriso/lib_mgt.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/findjob.c xorriso/cmp_update.c xorriso/iso_tree.c xorriso/write_run.c xorriso/aux_objects.h xorriso/misc_funct.c xorriso/misc_funct.h xorriso/sfile.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorriso_eng.html xorriso/README_gnu_xorriso libisoburn/libisoburn.h libisoburn/isoburn.c libisoburn/isoburn.h libisoburn/burn_wrap.c releng/jigdo-gen-md5-list.1 releng/jigdo-gen-md5-list releng/README frontend/frontend_pipes_xorriso.c frontend/xorriso-tcltk frontend/sh_on_named_pipes.sh Fixed spelling errors found by fossies.org with codespell ------------------------------------ cycle - xorriso-1.5.3 - 2019.10.28.143339 (upload for spelling check by fossies.org) 30 Oct 2019 [78a2a37] xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info Fixed another spelling error found by fossies.org with codespell 2019.11.23.132942 [c4894d8] xorriso/misc_funct.c xorriso/opts_d_h.c xorriso/make_xorriso_1.c Reacted on compiler warnings of Debian Sid 2019.11.24.123339 [1557304] configure.ac xorriso/xorriso_private.h xorriso/opts_d_h.c xorriso/emulators.c xorriso/lib_mgt.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info Switched to usage of libjte-2.0.0. New -jigdo parameters "checksum_algorithm", "demand_checksum", "checksum_path". New -as mkisofs options-jigdo-checksum-algorithm, "-checksum-list", "-jigdo-force-checksum". 24 Nov 2019 [b35e119] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.3 - 2019.11.24.123339 * Switched to usage of libjte-2.0.0 * New -jigdo parameters "checksum_algorithm", "demand_checksum", "-checksum-list" * New -as mkisofs options-jigdo-checksum-algorithm, "-checksum-list", "-jigdo-force-checksum" 2019.11.26.192814 [9028117] xorriso/emulators.c Removed some surplus blanks 2019.11.26.193722 [ea86330] releng/README releng/manual_isojigdo releng/jigdo-gen-md5-list releng/jigdo-gen-md5-list.1 Added options --md5 and --sha256 to Jigdo releng test 29 Nov 2019 [fe46a2d] + xorriso-dd-target/xorriso-dd-target Beginning public development of xorriso-dd-target 30 Nov 2019 [e36596b] xorriso-dd-target/xorriso-dd-target Assessing which partitions are really mounted. Made sure that a bus related reason is shown if bus was inquired. Improved hint and usage text. 30 Nov 2019 [81272c8] xorriso-dd-target/xorriso-dd-target Introduced option -dummy and enabled real unmounting 30 Nov 2019 [b8f7767] xorriso-dd-target/xorriso-dd-target Introduced option -plug_test 30 Nov 2019 [b0db123] xorriso-dd-target/xorriso-dd-target Added pacifier dots to the waiting loop of -plug_test 30 Nov 2019 [69ce40b] xorriso-dd-target/xorriso-dd-target Added a confirmation prompt to -plug_test controlled write runs 30 Nov 2019 [099d24f] xorriso-dd-target/xorriso-dd-target Added option -dummy_force 30 Nov 2019 [9ffb3ff] xorriso-dd-target/xorriso-dd-target Running sudo early in order to get the possible password prompt 02 Dec 2019 [a6ab055] xorriso-dd-target/xorriso-dd-target Small fixes of problems found while writing documentation 02 Dec 2019 [23bce9c] + xorriso-dd-target/xorriso-dd-target.texi + xorriso-dd-target/xorriso-dd-target.1 + xorriso-dd-target/xorriso-dd-target.info xorriso/make_xorriso_1.c xorriso/make_docs.sh Added info and man page for xorriso-dd-target 02 Dec 2019 [9b711f2] xorriso-dd-target/xorriso-dd-target xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info Reassured success of unmount 02 Dec 2019 [74f05d3] xorriso-dd-target/xorriso-dd-target xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info Made changes to -plug_test step 3 03 Dec 2019 [6de23e9] xorriso-dd-target/xorriso-dd-target Avoided to show mount lines again after -dummy umount 03 Dec 2019 [02e55a9] xorriso-dd-target/xorriso-dd-target Enabled real writing in xorriso-dd-target 03 Dec 2019 [f7f484d] xorriso-dd-target/xorriso-dd-target Wrapped command path variables in double quotes 03 Dec 2019 [3bf01c1] xorriso-dd-target/xorriso-dd-target Replaced fgrep by grep -F 03 Dec 2019 [7fa1f82] xorriso-dd-target/xorriso-dd-target Removed unused variable 03 Dec 2019 [f419f51] xorriso-dd-target/xorriso-dd-target Rejecting device names which are not a single word 04 Dec 2019 [2451020] xorriso-dd-target/xorriso-dd-target Unwrapped the three usages of variable sudo_cmd, because it can be empty 04 Dec 2019 [e226486] xorriso-dd-target/xorriso-dd-target Taking inability to determine -image_file size as reason to abort 04 Dec 2019 [c0b650b] xorriso-dd-target/xorriso-dd-target Simplified the pipeline which obtains the partition device files 05 Dec 2019 [5e727b4] xorriso-dd-target/xorriso-dd-target xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info Rejecting zram[0-9] as candidate device 05 Dec 2019 [a09ed16] xorriso-dd-target/xorriso-dd-target Tell no_bus_info- rather than not_usb- if lsblkd -o TRAN fails 05 Dec 2019 [a28ad87] xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info Clarified in man page the use of option -with_vendor_model 05 Dec 2019 [98c301e] xorriso-dd-target/xorriso-dd-target Letting round_down_div_million() convert small numbers to 0 06 Dec 2019 [3ac5dba] xorriso-dd-target/xorriso-dd-target Excluding loop devices from being considered 06 Dec 2019 [fd04b11] xorriso-dd-target/xorriso-dd-target xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info New reason looks_like_loopdev- 07 Dec 2019 [51994a7] xorriso-dd-target/xorriso-dd-target Experimental options -list_long_inline -list_long_after 07 Dec 2019 [ba505e8] xorriso-dd-target/xorriso-dd-target Imposing stricter checks on option parameters 07 Dec 2019 [d315b65] xorriso/README_gnu_xorriso xorriso/configure_ac.txt xorriso/xorriso_makefile_am.txt xorriso/make_xorriso_standalone.sh Brought xorriso-dd-target into GNU xorriso 07 Dec 2019 [1b22aa5] xorriso-dd-target/xorriso-dd-target xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info Removed option -list_long_after, changed -list_long_inline to -list_long 2019.12.07.203142 [1f6504f] xorriso/README_gnu_xorriso xorriso/AUTHORS_gnu_xorriso ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated authorship, change log and web page ------------------------------------ cycle - xorriso-1.5.3 - 2019.12.07.203142 * New helper script xorriso-dd-target 08 Dec 2019 [2a8894a] xorriso-dd-target/xorriso-dd-target Added forgotten initialization of variable umount_cmd 08 Dec 2019 [03ae735] README AUTHORS configure.ac Makefile.am Brought xorriso-dd-target into libisoburn 08 Dec 2019 [157cb19] xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info Equipped xorriso-dd-target -dummy_force example with -list_long 08 Dec 2019 [697c8cc] xorriso-dd-target/xorriso-dd-target Forcing locale "C" to avoid grep ambiguities and locale bugs 08 Dec 2019 [e5d86b3] xorriso-dd-target/xorriso-dd-target Giving device detected by -plug_test an extra second to settle 09 Dec 2019 [27bc463] xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info Exchanged two-byte UTF-8 characters by ASCII lookalikes 09 Dec 2019 [6bfdd2f] xorriso-dd-target/xorriso-dd-target Disabled -list_long before giving device overview after rejected -DO_WRITE 09 Dec 2019 [1efa98a] xorriso-dd-target/xorriso-dd-target Small fixes with help text and program message 10 Dec 2019 [ac01884] xorriso-dd-target/xorriso-dd-target xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info Trying to avoid race conditions with automounters during and after -plug_test 12 Dec 2019 [ab2af1d] + xorriso-dd-target/xorriso-dd-target.xdt A version of xorriso-dd-target with xdt_-prefixed symbols and return instead of exit 12 Dec 2019 [f6c56fe] xorriso-dd-target/xorriso-dd-target.xdt Some corrections of the previous commit 12 Dec 2019 [a3a351b] xorriso-dd-target/xorriso-dd-target xorriso-dd-target/xorriso-dd-target.xdt Improved handling of dd failure 14 Dec 2019 [b8a2a0b] xorriso-dd-target/xorriso-dd-target.xdt Created function xdt_predict_su_power from end of xdt_arg_interpreter 14 Dec 2019 [8fee0fc] xorriso-dd-target/xorriso-dd-target.xdt Created function xdt_unmount from xdt_write_image 15 Dec 2019 [39163b6] xorriso-dd-target/xorriso-dd-target Replaced xorriso-dd-target by xorriso-dd-target.xdt 15 Dec 2019 [93ebe30] - xorriso-dd-target/xorriso-dd-target.xdt Removed the work copy xorriso-dd-target.xdt 16 Dec 2019 [42df8f9] xorriso-dd-target/xorriso-dd-target Fixed the test for mounted devices after umount. Regression by 8fee0fc. 21 Dec 2019 [b97841b] xorriso-dd-target/xorriso-dd-target Shielded processing of user arguments against leading dashes 22 Dec 2019 [2392441] xorriso-dd-target/xorriso-dd-target Followed advice of Open Group about shell command "test" 23 Dec 2019 [80e3228] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Corrected description of command -preparer_id 01 Jan 2020 [71b6133] xorriso-dd-target/xorriso-dd-target xorriso-dd-target returned exit value 1 if no device name is given and one of the listed devices is not advisable 02 Jan 2020 [3466f23] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/read_run.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -osirrox option check_md5_on|_off|_force 2020.01.02.220502 [56238c3] Timestamp for 3466f23 06 Jan 2020 [557e69a] xorriso-dd-target/xorriso-dd-target xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info Using dd option status=progress if available 2020.01.06.204949 [43d5804] xorriso-dd-target/xorriso-dd-target xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info New xorriso-dd-target option -version 2020.01.07.123808 [5a335e5] xorriso-dd-target/xorriso-dd-target xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info Erasing GPT backup header block, if expr is good enough 2020.01.08.204757 [086a9a6] xorriso-dd-target/xorriso-dd-target Corrected computation of end block for GPT erasure 2020.01.19.095527 [bf0a3f0] xorriso/opts_d_h.c xorriso/emulators.c Added contact information to help texts 2020.01.20.132834 [49690fc] xorriso/opts_d_h.c xorriso/emulators.c Added GNU Coding Style compliant versions of contact information to help texts 2020.02.12.145929 [1170a9b] xorriso-dd-target/xorriso-dd-target xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info New xorriso-dd-target option -trust_lsblk_udev 2020.02.12.195952 [89fe871] xorriso-dd-target/xorriso-dd-target Corrected a functionality spoiling typo in commit 1170a9b 13 Jun 2020 [c51f381] xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Mentioned in man xorrisofs the directory tree limitation of HFS+ 13 Jun 2020 [af0dd13] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Corrected typo in man xorriso. Thanks Jakub Wilk. 2020.07.01.103946 [1667035] libisoburn/libisoburn.h Corrected a few typos and swapped words in API description 01 Jul 2020 [25b445f] + xorriso-dd-target/xorriso-dd-target.sig Offering GPG signature file for repo downloads of xorriso-dd-target 03 Jul 2020 [gitea: 8f872ba] [gitlab: 5d27645] README Updated copyright year in README 08 Jul 2020 [2fda271] doc/faq.wiki Adapted faq.wiki to Gitea 06 Aug 2020 [3f6195d] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Mentioned in man xorriso the directory tree limitation of HFS+ 2020.08.06.101015 [ec49c62] xorriso/write_run.c Silenced an inappropriate note message with -check_media patch_lba0 2020.08.06.153930 [04a82ce] xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New command -truncate_overwritable 20 Aug 2020 [f23b23f] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.3 - 2020.08.06.153930 * New command -truncate_overwritable 2020.08.26.142853 [ffb7fe6] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/drive_mgt.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -read_speed prefixes "soft_force:" and "soft_corr:" 11 Sep 2020 [56b2d88] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Mentioned that partition_entry=gpt_basdat and -isohybrid-gpt-basdat create invalid GPT but also a valid MBR partition table 2020.09.30.190728 [588578f] xorriso/opts_a_c.c xorriso/check_media.c xorriso/drive_mgt.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -check_media option data_to="-" for standard output 30 Sep 2020 [bbded29] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.3 - 2020.09.30.190728 * New -read_speed prefixes "soft_force:" and "soft_corr:" * New -check_media option data_to="-" for standard output 2020.10.14.182119 [9a7dfd7] libisoburn/libisoburn.h Requiring libisofs 1.5.3 2020.10.14.205904 [dcb1395] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/iso_tree.c xorriso/lib_mgt.c xorriso/filters.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -zisofs parameters version_2=, block_size_v2=, max_bpt=, max_bpt_f= 2020.10.14.210148 [8286468] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs option --zisofs-version-2 14 Oct 2020 [fb0d617] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.3 - 2020.10.14.210148 * New -zisofs parameters version_2=, block_size_v2=, max_bpt=, max_bpt_f= * New -as mkisofs option --zisofs-version-2 2020.10.16.091750 [2f44446] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Let -as mkisofs --zisofs-version-2 imply -z 2020.10.17.133001 [3f390f2] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/iso_tree.c xorriso/lib_mgt.c xorriso/filters.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -zisofs parameters bpt_target= 17 Oct 2020 [5a6aa8a] xorriso/make_docs.sh Added makeinfo option --no-split in make_docs.sh 2020.10.22.114038 [c4d66ec] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/lib_mgt.c xorriso/filters.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -zisofs parameter bpt_free_ratio= 2020.10.24.121839 [396d099] xorriso/iso_img.c Bug fix: -report_system_areas as_mkisofs misrepresented GPT with appended partition and forced boot flag 2020.10.25.160005 [c1e6d34] xorriso/base_obj.c xorriso/filters.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 In case of ISO_ZISOFS_TOO_MANY_PTR remove all dispensable block pointers 27 Oct 2020 [c24ffed] xorriso/configure_ac.txt Let ./configure --disable-zlib issue a message 2020.10.27.113831 [1ddb676] xorriso/opts_p_z.c xorriso/emulators.c xorriso/filters.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Enabled recognition of zisofs by magic without zlib support. New by_magic=v2 2020.10.29.082836 [a9d8102] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/lib_mgt.c xorriso/filters.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -zisofs parameter susp_z2= 2020.10.29.083347 [5316687] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 New -as mkisofs option --zisofs-susp-z2 29 Oct 2020 [d2f10a1] xorriso/xorriso_makefile_am.txt xorriso/make_xorriso_standalone.sh Added libisofs/doc/zisofs2_format.txt to GNU xorriso 2020.10.29.191346 [80b4d29] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Changed --zisofs-susp-z2 to --zisofs2-susp-z2, added --zisofs2-susp-zf 2020.10.31.195038 [66fe150] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.3 - 2020.10.31.195038 * New -zisofs parameters bpt_target=, bpt_free_ratio=, by_magic=v2, susp_z2= * New -as mkisofs options --zisofs2-susp-z2, --zisofs2-susp-zf * Enabled recognition of zisofs by magic without zlib support * Bug fix: -report_system_area as_mkisofs misrepresented GPT with appended partition and forced boot flag as -part_like_isohybrid 2020.11.03.082729 [05de7ec] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_d_h.c xorriso/opts_i_o.c xorriso/opts_p_z.c xorriso/read_run.c xorriso/text_io.c xorriso/misc_funct.h xorriso/misc_funct.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -osirrox option sparse= controls extraction into sparse files 2020.11.07.113931 [6fa5f97] xorriso/xorrisoburn.h xorriso/iso_manip.c xorriso/write_run.c Bug fix: Boot catalog could get a wrong name if cat_path= is explicitely given but not containing a slash character 13 Nov 2020 [fc2a2d4] + NEWS Added a NEWS file to please automake without --foreign. Thanks Felipe Franciosi. 13 Nov 2020 [d9176df] xorriso/make_xorriso_standalone.sh Added NEWS to GNU xorriso 2020.11.14.083425 [266a6f4] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.3 - 2020.11.14.083425 * New -osirrox option sparse= controls extraction into sparse files * Bug fix: Boot catalog could get a wrong name if cat_path= is explicitely given but not containing a slash character 15 Nov 2020 [abd65a7] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Documented new opportunity to use with -append_partition the HFS+ type GUID to get APM type "Apple_HFS" 2020.11.18.200311 [9f5a6ea] xorriso/emulators.c Added -translation-table to the list of ignroed -as mkisofs options 2020.11.22.131521 [cf176ee] libisoburn/libisoburn.h libisoburn/isofs_wrap.c libisoburn/isoburn.h libisoburn/isoburn.c New libisoburn extension option isoburn_ropt_map_joliet_stripped 2020.11.22.134545 [8f89ad0] libisoburn/libisoburn.h libisoburn/isofs_wrap.c libisoburn/isoburn.h libisoburn/isoburn.c New libisoburn extension option isoburn_ropt_map_joliet_unmapped 2020.11.22.135052 [2fbb3cf] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/base_obj.c xorriso/text_io.c xorriso/drive_mgt.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New command -joliet_map 2020.12.04.103410 [e97cd48] xorriso/opts_i_o.c Added the source code of Xorriso_option_joliet_map which was omitted with commit 2fbb3cf 2020.12.05.085329 [47be075] xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/read_run.c xorriso/check_media.h xorriso/check_media.c xorriso/sfile.h xorriso/sfile.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New command -extract_boot_images 2020.12.07.183710 [b3adff1] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/isofs_wrap.c libisoburn/libisoburn.ver New API call isoburn_ropt_get_tree_loaded() 2020.12.07.184159 [3542e8a] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/drive_mgt.c New -toc lines "ISO offers :" and "ISO loaded :" 2020.12.08.122752 [5bc1d99] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/drive_mgt.c Omitting "ISO offers :" and "ISO loaded :" if no ISO was loaded at all 2021.01.30.150001 [f81eade] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso-dd-target/xorriso-dd-target xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info xorriso/xorriso-tcltk.texi xorriso/xorriso-tcltk.1 xorriso/xorriso-tcltk.info xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info Version leap to libisoburn-1.5.4 30 Jan 2021 [ac4048d] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.5.4 - 2021.01.30.150001 * Switched to usage of libjte-2.0.0 * New -jigdo parameters "checksum_algorithm", "demand_checksum", "-checksum-list" * New -as mkisofs options-jigdo-checksum-algorithm, "-checksum-list", "-jigdo-force-checksum" * New helper script xorriso-dd-target * New command -truncate_overwritable * New -read_speed prefixes "soft_force:" and "soft_corr:" * New -check_media option data_to="-" for standard output * New -zisofs parameters version_2=, block_size_v2=, max_bpt=, max_bpt_f= * New -as mkisofs option --zisofs-version-2 * New -zisofs parameters bpt_target=, bpt_free_ratio=, by_magic=v2, susp_z2= * New -as mkisofs options --zisofs2-susp-z2, --zisofs2-susp-zf * Enabled recognition of zisofs by magic without zlib support * Bug fix: -report_system_area as_mkisofs misrepresented GPT with appended partition and forced boot flag as -part_like_isohybrid * New -osirrox option sparse= controls extraction into sparse files * Bug fix: Boot catalog could get a wrong name if cat_path= is explicitely given but not containing a slash character * New libisoburn extension options isoburn_ropt_map_joliet_stripped and isoburn_ropt_map_joliet_unmapped * New API call isoburn_ropt_get_tree_loaded() * New command -joliet_map * New command -extract_boot_images 2021.01.30.200107 [f51499c] configure.ac libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso-dd-target/xorriso-dd-target xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info xorriso/xorriso-tcltk.texi xorriso/xorriso-tcltk.1 xorriso/xorriso-tcltk.info xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info Version leap to libisoburn-1.5.5 30 Jan 2021 [d16ab5b] ChangeLog xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.5 - 2021.01.30.200107 2021.02.01.174513 [9fe4c4d] xorriso/xorriso_eng.html xorriso/changelog.txt xorriso/xorriso_timestamp.h Updated change log and web page ------------------------------------ cycle - xorriso-1.5.5 - 2021.02.01.174513 * Bug fix in libisofs: Large amounts of AAIP data or many long file names could cause with zisofs an unreadable filesystem after the warning "Calculated and written ECMA-119 tree end differ" 2021.02.03.115443 [aa1a008] xorriso/changelog.txt xorriso/xorriso_timestamp.h Updated change log ------------------------------------ cycle - xorriso-1.5.5 - 2021.02.03.115443 Heuristic correction of bug fix in021.02.01.174513 which led to contrary miscalculations. As of 06 Feb 2021 the reason for the need of this bug fix has been found. Without it, the block padding in SUSP continuation area are predicted twice, causing double predicted size. ------------------------------ release - xorriso-1.5.4.pl01 - 2021.02.06.113001 Regrettably did not get the bug fix due to a mistake during its production. ------------------------------ release - xorriso-1.5.4.pl02 - 2021.02.06.123001 * Bug fix in libisofs: Large amounts of AAIP data or many long file names could cause with zisofs an unreadable filesystem after the warning "Calculated and written ECMA-119 tree end differ" 07 Feb 2021 [be21a63] xorriso-dd-target/xorriso-dd-target.sig Updated xorriso-dd-target.sig 2021.03.12.083144 [a514680] acinclude.m4 Made optional the use of libcam and thus SCSI passthrough on GNU/FreeBSD systems 12 Mar 2021 [c9e7fe8] xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info Fixed a man page typo found by lintian 2021.04.04.181609 [e3df757] xorriso/iso_img.c Avoided to override EFI type GUID by output of -report_system_area as_mkisofs 2021.05.25.191333 [a1f9d03] libisoburn/libisoburn.h libisoburn/isoburn.c New isoburn_igopt_set_system_area() option bits 16: GPT "Legacy BIOS bootable" and 17: GPT writable 2021.05.25.191826 [db5d4eb] xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/iso_img.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -boot_image settings gpt_iso_bootable= and gpt_iso_not_ro= 2021.05.25.192155 [23d0d8e] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info New -as mkisofs options --gpt-iso-bootable and --gpt-iso-not-ro 2021.05.25.195904 [e9cbf38] xorriso/iso_img.c Bug fix: False -status failure with -boot_image --interval:appended_partition 25 May 2021 [60dfe77] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.5 - 2021.05.25.195904 * New -boot_image settings gpt_iso_bootable= and gpt_iso_not_ro= * New -as mkisofs options --gpt-iso-bootable and --gpt-iso-not-ro * Bug fix: False -status failure with -boot_image --interval:appended_partition 2021.06.04.155151 [49d40a4] xorriso/text_io.c Corrected default expectation of -status in respect to -osirrox and -error_behavior 04 Jun 2021 [5011a5f] xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info Mentioned command -status in man page paragraph "Command processing" 2021.08.28.161158 [7dc45e3] xorriso/xorrisoburn.h xorriso/opts_p_z.c xorriso/lib_mgt.c Warn of volid length for Joliet only if more than 16 characters (was: 16 bytes) 2021.08.28.161305 [e6f6867] libisoburn/libisoburn.h Corrected wording in the API description of isoburn_conv_name_chars() 2021.08.30.094939 [e489c87] xorriso/parse_exec.c Bug fix: -no_rc prevented pre-scanning of arguments for stdio output and others. Introduced by mistake in a62f6af5, 2011.10.18.162119. 2021.08.30.103430 [3f2b619] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/emulators.c xorriso/write_run.c xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 New -as cdrecord option --obs_pad. Automatic no_emul_toc with -as cdrecord. 30 Aug 2021 [b35c2e4] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.5 - 2021.08.30.103430 * Bug fix: -no_rc prevented pre-scanning of arguments for stdio output and others. Introduced by mistake in a62f6af5, 2011.10.18.162119. * New -as cdrecord option --obs_pad. Automatic no_emul_toc with -as cdrecord. 2021.08.30.145223 [d19150a] xorriso/opts_p_z.c Updated copyright year of -version message 02 Sep 2021 [562247b] configure.ac Removed unneeded configure.ac macro AC_C_BIGENDIAN 2021.09.04.075955 [8a11dc6] xorriso/opts_p_z.c Reacted on compiler warning of Debian i386 buildd 04 Sep 2021 [555048a] configure.ac Removed a "(" from a "dnl" comment which confused autoupdate 2021.09.12.092738 [58e7eb0] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_d_h.c xorriso/write_run.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New parameters "obs_pad" and "bdr_obs_exempt" for -dvd_obs 2021.09.12.093333 [3582628] xorriso/emulators.c xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 New -as cdrecord option --bdr_obs_exempt 2021.09.13.151219 [b5a33c3] xorriso/parse_exec.c xorriso/read_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Bug fix: -not_leaf and -not_paths were not applied to -extract and alike 2021.09.13.151337 [b2d9384] xorriso/opts_p_z.c Fixed a message about -paste_bin from and to paths 13 Sep 2021 [0ba1396] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.5 - 2021.09.13.151337 * New parameters "obs_pad" and "bdr_obs_exempt" for -dvd_obs * New -as cdrecord option --bdr_obs_exempt * Bug fix: -not_leaf and -not_paths were not applied to -extract and alike 25 Sep 2021 [6b0bb9e] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Mentioned in man page the classification of BD-R POW as unsuitable medium state 2021.11.24.115053 [e651cb5] xorriso/xorriso.h xorriso/drive_mgt.c xorriso/text_io.c New -toc output line "Overburnt by :" 2022.04.22.113132 [7d79692] xorriso/iso_img.c Bug fix: -report_system_area cmd misperceived -part_like_isohybrid with -isohybrid-gpt-basdat 2022.04.22.113324 [ac7cdfa] xorriso/iso_img.c Bug fix: -report_system_area cmd misperceived combination of isohybrid and appended partition in GPT 22 Apr 2022 [0ef65a7] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.5 - 2022.04.22.113324 * Bug fix: -report_system_area cmd misperceived -part_like_isohybrid with -isohybrid-gpt-basdat * Bug fix: -report_system_area cmd misperceived combination of isohybrid and appended partition in GPT 2022.04.26.101633 [fc58796] xorriso/iso_tree.c xorriso/iso_manip.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Allowed lseekable device files with -cut_out. Proof-of-concept by Ivan Shmakov. 10 May 2022 [0cb2e15] releng/auto_cxx releng/README Allowed to override by variable CC the use of g++ in releng/auto_cxx . Proposal by Tom Stellard. 23 May 2022 [ddfe509] + test/merge_2_debian_isos New experimental script test/merge_2_debian_isos 2022.05.30.164422 [0c0d542] xorriso/disk_ops.h xorriso/disk_ops.c xorriso/iso_manip.c Widened the lseek capacity determination to SEEK_SET with wanted size 2022.05.30.164747 [865115f] xorriso/read_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Allowed lseekable device files with -paste_in 2022.06.02.140943 [5aac3dd] xorriso/iso_manip.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Bug fix: Split file directories (-split_size) were created with wrong permissions 2022.06.13.205546 [3eef041] xorriso/write_run.c Warning if EFI boot equipment is present but no /EFI/BOOT directory is in ISO 15 Jun 2022 [9b4c082] test/merge_2_debian_isos + test/merge_debian_isos + test/merge_debian_isos.sig New experimental script test/merge_debian_isos. Deprecated merge_2_debian_isos. 15 Jun 2022 [65c63cc] test/merge_debian_isos test/merge_debian_isos.sig Changed permissions of test/merge_debian_isos to a+x 17 Jun 2022 [34981b1] test/merge_debian_isos test/merge_debian_isos.sig Switched test/merge_debian_isos to /bin/sh, added signal handling, fixed harmless flaws found by shellcheck, added more sanity checks, enabled XORRISO=dummy for test runs without ISO production 18 Jun 2022 [5897c70] test/merge_debian_isos test/merge_debian_isos.sig Improved signal handling, removing result ISO if xorriso exits with error, redirected a few messages from stdout to stderr 18 Jun 2022 [68c4a39] test/merge_debian_isos test/merge_debian_isos.sig Merging Debian /firmware directories like the /pool directories 20 Jun 2022 [87aab73] test/merge_debian_isos test/merge_debian_isos.sig Corrected sorting of /md5sum.txt by merge_debian_isos 23 Jun 2022 [0bc397c] test/merge_debian_isos test/merge_debian_isos.sig Removing duplicates from md5sum.txt and computing new MD5 for changed files 23 Jun 2022 [a94f22d] test/merge_debian_isos test/merge_debian_isos.sig Properly announced and checked md5sum as new dependency of merge_debian_isos 24 Jun 2022 [072c4e5] test/merge_debian_isos test/merge_debian_isos.sig Listing /firmware/dep11/README.txt in md5sum.txt if all its MD5 lines are equal 26 Jun 2022 [0e8227e] test/merge_debian_isos test/merge_debian_isos.sig Improved speed of md5sum.txt processing by a proposal of Zhang Boyang 02 Jul 2022 [4ff9f8e] releng/run_all_auto Returning non-zero exit value if releng/run_all_auto fails. Proposal by Alexandre Ghiti. 14 Jul 2022 [62700a9] test/merge_debian_isos test/merge_debian_isos.sig Enabled writing by merge_debian_isos to USB sticks and optical media 14 Jul 2022 [71f6ba1] xorriso/xorriso_eng.html Updated the gpg --verify procedure after demise of keys.gnupg.net 14 Jul 2022 [bdc563a] ChangeLog xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.5 - 2022.07.14.195925 * Allowed lseekable device files with -cut_out. Proof-of-concept by Ivan Shmakov on bugs.debian.org. (Closes: #1010098) * Bug fix: Split file directories (-split_size) were created with wrong permissions 15 Jul 2022 [821628a] test/merge_debian_isos test/merge_debian_isos.sig Made adjustments in merge_debian_isos 16 Jul 2022 [f205e42] test/merge_debian_isos test/merge_debian_isos.sig Added a sentence to help text of merge_debian_isos 19 Jul 2022 [9e79f64] test/merge_debian_isos test/merge_debian_isos.sig Automatically creating mount parent directory in merge_debian_isos 06 Aug 2022 [3318fa4] frontend/grub-mkrescue-sed.sh frontend/grub-mkrescue-sed.sh.sig New environment variable MKRESCUE_SED_UNPACK_EFI_TO_ISO 20 Sep 2022 [d2fd48f] libisoburn/libisoburn.h libisoburn/isofs_wrap.c libisoburn/libisoburn.ver New API call isoburn_assess_written_features() 2022.09.20.082738 [e594ba2] xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/parse_exec.c xorriso/opts_a_c.c xorriso/drive_mgt.c xorriso/text_io.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 libisoburn/libisoburn.ver New command -assess_indev_features 2022.10.07.095444 [82dc9b6] xorriso/iso_img.c xorriso/drive_mgt.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Reporting some relaxation commands with -report_el_torito modes "cmd" and "mkisofs" if CHRP is detected. Performing them with -boot_image any "replay". 08 Oct 2022 [847afc0] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.5 - 2022.10.07.095444 * New API call isoburn_assess_written_features() * New command -assess_indev_features 19 Oct 2022 [fd19550] test/merge_debian_isos.texi test/merge_debian_isos.info test/merge_debian_isos.1 Created manual pages for merge_debian_iso 19 Oct 2022 [7d618ef] xorriso/make_xorriso_1.c xorriso/make_docs.sh Enabled production of .info and .1 from merge_debian_isos.texi 19 Oct 2022 [c62ae86] test/merge_debian_isos.texi test/merge_debian_isos.info test/merge_debian_isos.1 Corrected the BUGS section of merge_debian_isos.texi 27 Oct 2022 [0d3d9d7] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page 2022.10.29.084954 [09de0f1] libisoburn/libisoburn.h Demanding (belated) libburn and libisofs versions 1.5.5 2022.11.05.133746 [1ff12c2] xorriso/write_run.h xorriso/write_run.c Bug fix: Partition offset was preserved from -indev rather than from -outdev 05 Nov 2022 [a28040d] xorriso/make_xorriso_standalone.sh Added test/merge_debian_isos and its docs to GNU xorriso tarball 2022.11.10.095102 [008620e] xorriso/opts_d_h.c xorriso/iso_manip.c xorriso/iso_tree.h xorriso/iso_tree.c xorriso/findjob.h xorriso/findjob.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -find test -size 02 Dec 2022 [017363e] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.5 - 2022.12.02.095334 * Bug fix: libisofs did not mark clones of imported files as imported. This could cause that original and clone occupy data storage in the newly written session. Thanks to Ivan Shmakov. * Bug fix: Partition offset was preserved from -indev rather than from -outdev * New -find test -size 2022.12.11.182322 [4eb98d7] xorriso/write_run.c Reacted on (in the end harmless) warning of valgrind 2022.12.17.190005 [098d4f8] xorriso/emulators.c xorriso/write_run.c Bug fix: Data files named /boot.catalog or ./boot.cat could be left out of the emerging ISO if the boot catalog was set to be hidden 18 Dec 2020 [36daf52] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.5 - 2022.12.17.190005 * Bug fix: libisofs could misrepresent Rock Ridge information if many symbolic links or AAIP data were recorded in a directory * Bug fix: Data files named /boot.catalog or ./boot.cat could be left out of the emerging ISO if the boot catalog was set to be hidden 2022.12.28.083915 [1a39dc6] xorriso/iso_img.c Enabled effect of -rom_toc_scan "off:emul_off" for -mount entity "auto" 2023.01.09.144346 [3afa1d5] xorriso/drive_mgt.c Bug fix: -toc reported wrong track LBA with overwritable media with unrecognized content (pseudo-closed) 2023.01.10.180154 [c310e2a] xorriso/iso_tree.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Bug fix: -find test -has_xattr matched "isofs." attributes in -xattr mode "any" 2023.01.22.151757 [9a578a6] libisoburn/libisoburn.h libisoburn/isoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver New API calls isoburn_igopt_set_max_ce_entries(), isoburn_igopt_get_max_ce_entries() 2023.01.22.151757 [b4e10ce] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New -compliance rules max_ce_entries=, max_ce_drop= 2023.01.22.152059 [1a1d31f] xorriso/parse_exec.c Corrected argument count classification of -setfattr_list 2023.01.22.152155 [5600508] xorriso/opts_p_z.c Updated copright year in output of -version 2023.02.08.200727 [163129c] xorriso/opts_d_h.c Added missing help text for -assess_indev_features 2023.02.24.080036 [6a921c7] libisoburn/libisoburn.h libisoburn/burn_wrap.c New flag bit12 with isoburn_read_iso_head() 2023.02.24.162752 [b837ff3] xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Let -truncate "sbsector" "new" work even if LBA-0-superblock is damaged 2023.02.28.135556 [1844014] xorriso/write_run.c Fixed error in format of LBA-0-superblock created by prevous commit 2023.02.28.150706 [72ef04a] xorriso/drive_mgt.c Enabled -check_media with pseudo-blank overwritable media 2023.03.15.194905 [fc03217] xorriso/iso_manip.c xorriso/disk_ops.c Emitting SORRY event for dangling symbolic links if -follow is set to "link" 2023.03.19.153432 [d0ff0a3] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.5 - 2023.03.19.153432 * Bug fix: -toc reported wrong track LBA with overwritable media with unrecognized content (pseudo-closed) * Bug fix: -find test -has_xattr matched "isofs." attributes in -xattr mode "any" * New API calls isoburn_igopt_set_max_ce_entries(), isoburn_igopt_get_max_ce_entries() * New flag bit12 with isoburn_read_iso_head(): Read even if start of multi-session emulation is damaged * New -compliance rules max_ce_entries=, max_ce_drop= 2023.04.14.161945 [ad1b9bf] test/merge_debian_isos test/merge_debian_isos.sig test/merge_debian_isos.texi test/merge_debian_isos.info test/merge_debian_isos.1 Added -joliet "on" to xorriso run in merge_debian_isos 2023.06.07.180001 [be8f86d] configure.ac README libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/xorriso_eng.html xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso-dd-target/xorriso-dd-target test/merge_debian_isos xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info xorriso/xorriso-tcltk.texi xorriso/xorriso-tcltk.1 xorriso/xorriso-tcltk.info xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info test/merge_debian_isos.texi test/merge_debian_isos.1 test/merge_debian_isos.info Version leap to libisoburn-1.5.6 07 Jun 2023 [79997c2] xorriso-dd-target/xorriso-dd-target.sig test/merge_debian_isos.sig Updated xorriso-dd-target.sig and merge_debian_isos.sig 07 Jun 2023 [919c823] ChangeLog xorriso/changelog.txt Documented changes and release timestamp ----------------------------------- release - xorriso-1.5.6 - 2023.06.07.180001 * Bug fix: False -status failure with -boot_image --interval:appended_partition * Bug fix: -no_rc prevented pre-scanning of arguments for stdio output and others. Introduced by mistake in a62f6af5, 2011.10.18.162119. * Bug fix: -not_leaf and -not_paths were not applied to -extract and alike * Bug fix: -report_system_area cmd misperceived -part_like_isohybrid with -isohybrid-gpt-basdat * Bug fix: -report_system_area cmd misperceived combination of isohybrid and appended partition in GPT * Bug fix: -as mkisofs option -part_like_isohybrid did not cause a MBR partition table if the partitions are data files in the ISO rather than appended * Bug fix: Split file directories (-split_size) were created with wrong permissions * Bug fix: libisofs did not mark clones of imported files as imported. This could cause that original and clone occupy data storage in the newly written session. Thanks to Ivan Shmakov. * Bug fix: Partition offset was preserved from -indev rather than from -outdev * Bug fix: libisofs could misrepresent Rock Ridge information if many symbolic links or AAIP data were recorded in a directory * Bug fix: Data files named /boot.catalog or ./boot.cat could be left out of the emerging ISO if the boot catalog was set to be hidden * Bug fix: -toc reported wrong track LBA with overwritable media with unrecognized content (pseudo-closed) * Bug fix: -find test -has_xattr matched "isofs." attributes in -xattr mode "any" * New API call isoburn_assess_written_features() * New API calls isoburn_igopt_set_max_ce_entries(), isoburn_igopt_get_max_ce_entries() * New flag bit12 with isoburn_read_iso_head(): Read even if start of multi-session emulation is damaged * New -boot_image settings gpt_iso_bootable= and gpt_iso_not_ro= * New -as mkisofs options --gpt-iso-bootable and --gpt-iso-not-ro * New -as cdrecord option --obs_pad. Automatic no_emul_toc with -as cdrecord. * New parameters "obs_pad" and "bdr_obs_exempt" for -dvd_obs * New -as cdrecord option --bdr_obs_exempt * New command -assess_indev_features * New -find test -size * New -compliance rules max_ce_entries=, max_ce_drop= * Allowed lseekable device files with -cut_out. Proof-of-concept by Ivan Shmakov on bugs.debian.org. (Closes: #1010098) 2023.06.07.200919 [5a869b1] configure.ac libisoburn/libisoburn.h xorriso/README_gnu_xorriso xorriso/xorriso.h xorriso/xorrisoburn.h xorriso/xorriso_main.c xorriso/make_xorriso_standalone.sh xorriso/configure_ac.txt frontend/xorriso-tcltk xorriso-dd-target/xorriso-dd-target xorriso/xorriso_timestamp.h xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info xorriso/xorrecord.texi xorriso/xorrecord.1 xorriso/xorrecord.info xorriso/xorriso-tcltk.texi xorriso/xorriso-tcltk.1 xorriso/xorriso-tcltk.info xorriso-dd-target/xorriso-dd-target.texi xorriso-dd-target/xorriso-dd-target.1 xorriso-dd-target/xorriso-dd-target.info test/merge_debian_isos.texi test/merge_debian_isos.1 test/merge_debian_isos.info Version leap to libisoburn-1.5.7 07 Jun 2023 [a22f6b8] ChangeLog xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.7 - 2023.06.07.200919 ------------------------------ release - xorriso-1.5.6.pl01 - 2023.06.12.080001 (A failed attempt to create a tarball with a fixed libisofs/rockridge.h) ------------------------------ release - xorriso-1.5.6.pl02 - 2023.06.14.170001 * Bug fix: On non-GNU/Linux systems ssize_t was not defined in libisofs file rockridge.h . Report and fix proposal by Rui Chen. ------------------------------------ cycle - xorriso-1.5.7 - 2023.06.12.125306 * Bug fix: On non-GNU/Linux systems ssize_t was not defined in libisofs file rockridge.h . Report and fix proposal by Rui Chen. 12 Jun 2023 [1807f04] xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page 20 Jun 2023 [9b17a8a] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page 2023.07.12.182812 [11c5f65] libisoburn/isofs_wrap.c Enabled recognition of multi-session emulation with partition offset 16 and GPT 2023.07.21.070020 [1a44cbd] libisoburn/libisoburn.h xorriso/sfile.h xorriso/sfile.c xorriso/misc_funct.h xorriso/misc_funct.c Silenced some harmless warnings of Fedora build process 2023.08.06.132334 [f3af549] xorriso/opts_a_c.c Bug fix: -boot_image and -append_partition were not perceived as image manipulation which makes production of an ISO image worthwhile. Thanks Cameron Seader. 2023.08.06.133040 [aaaa0a9] xorriso/opts_a_c.c Let -boot_image show_status display info about -append_partition status 2023.08.07.132608 [0b7e4f9] xorriso/opts_a_c.c xorriso/xorriso.texi xorriso/xorriso.1 xorriso/xorriso.info New -append_partition pseudo partition_number "all" and pseudo type_code "revoke" 2023.08.08.114313 [ab0bfdb] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/opts_a_c.c xorriso/opts_p_z.c xorriso/iso_img.c xorriso/write_run.c New bit1 with API call Xorriso_change_is_pending() issues a note if return is 0 and indev and outdev point to different drives 2023.08.08.114459 [2de0667] xorriso/xorriso_main.c Using Xorriso_change_is_pending() bit1 at end of program 08 Aug 2023 [e383d3a] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.7 - 2023.08.08.114459 * Bug fix: -boot_image and -append_partition were not perceived as image manipulation which makes production of an ISO image worthwhile. Thanks Cameron Seader. * New -append_partition pseudo partition_number "all" and pseudo type_code "revoke" * New bit1 with API call Xorriso_change_is_pending() issues a note if return is 0 and indev and outdev point to different drives 2023.11.20.105009 [651f429] xorriso/emulators.c xorriso/xorrisofs.texi xorriso/xorrisofs.1 xorriso/xorrisofs.info New -as mkisofs options -cut_out and -hide_iso_path 2023.11.21.103206 [aace531] xorriso/lib_mgt.c xorriso/iso_img.c xorriso/iso_tree.c Improved handling of hidden boot images in -boot_image cmd/as_mkisofs/replay 21 Nov 2023 [92c4d5b] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.7 - 2023.11.21.103206 * New -as mkisofs options -cut_out and -hide_iso_path * Improved handling of hidden boot images in -boot_image cmd/as_mkisofs/replay 2024.03.09.170230 [280c78a] configure.ac libisoburn/libisoburn.h Demanding libburn 1.5.7 2024.03.09.180620 [c1e5633] libisoburn/isoburn.h libisoburn/burn_wrap.c libisoburn/isofs_wrap.c Augmented structs isoburn, isoburn_toc_entry, isoburn_imgen_opts for long block addresses 2024.03.09.183038 [a86d137] libisoburn/libisoburn.h libisoburn/burn_wrap.c libisoburn/libisoburn.ver New API calls isoburn_toc_disc_get_sectors_v2, isoburn_toc_session_get_sectors_v2, isoburn_toc_track_get_emul_v2 2024.03.09.204838 [e7326e0] libisoburn/libisoburn.h libisoburn/burn_wrap.c libisoburn/libisoburn.ver New API calls isoburn_read_iso_head_v2(), isoburn_get_mount_params_v2() 2024.03.09.210426 [423c148] libisoburn/libisoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver New API calls isoburn_igopt_get_effective_lba_v2(), isoburn_igopt_get_data_start_v2() 2024.03.09.212055 [646d15a] libisoburn/libisoburn.h libisoburn/isofs_wrap.c libisoburn/libisoburn.ver New API call isoburn_get_attached_start_lba_v2() 2024.03.09.223619 [dd92a6e] libisoburn/libisoburn.h libisoburn/isofs_wrap.c libisoburn/burn_wrap.c libisoburn/libisoburn.ver New API calls isoburn_attach_start_lba_v2(), isoburn_disc_get_msc1_v2(), isoburn_disc_track_lba_nwa_v2() 2024.03.10.072545 [8d33881] libisoburn/libisoburn.h libisoburn/isoburn.c libisoburn/libisoburn.ver New API call isoburn_prepare_blind_grow_v2() 2024.03.10.075517 [9003e47] libisoburn/burn_wrap.c Made off_t sized burn_toc_entry fields valid in fabricated entries 2024.03.10.075847 [bdc9164] libisoburn/isoburn.c Made isoburn_prepare_disc_aux ready for unsigned 32 bit 2024.03.10.101203 [fbeec72] libisoburn/burn_wrap.c libisoburn/isoburn.h libisoburn/isoburn.c Made use of _v2 calls when creating an isoburn_imgen_opts object 2024.03.10.105732 [aebd2b9] libisoburn/burn_wrap.c Made isoburn_welcome_media() ready for large block adresses 2024.03.10.111205 [d0682a1] libisoburn/burn_wrap.c Made isoburn_set_msc1() ready for long block adresses 2024.03.10.112148 [bae9516] libisoburn/isofs_wrap.c Made isoburn_make_iso_read_opts() ready for long block adresses 2024.03.10.113018 [a07addd] libisoburn/burn_wrap.c Replaced non-API isoburn_get_track_lba() by isoburn_get_track_lba_v2() 2024.03.17.152556 [73b3923] xorriso/check_media.h xorriso/check_media.c xorriso/drive_mgt.c xorriso/iso_tree.c Made struct SectorbitmaP ready for long block adresses 2024.03.17.160830 [86261b7] xorriso/check_media.c xorriso/README_gnu_xorriso Introduced xorriso sector bitmap v3 with 8-byte N and S 2024.03.17.205516 [ba12014] xorriso/opts_a_c.c xorriso/check_media.h xorriso/check_media.c xorriso/read_run.c xorriso/drive_mgt.c Made structs SpotlistiteM and SpotlisT ready for long block adresses 2024.03.18.084408 [969ee9d] xorriso/opts_a_c.c xorriso/check_media.h xorriso/check_media.c xorriso/drive_mgt.c Made struct CheckmediajoB ready for long block adresses 2024.03.18.173836 [19e1cee] xorriso/drive_mgt.h xorriso/drive_mgt.c xorriso/read_run.c Made Xorriso_check_media() ready for long block adresses 2024.03.18.201822 [949a4c3] xorriso/read_run.c Updated copyright date in read_run.c 2024.03.18.204021 [b8b0f41] xorriso/disk_ops.h xorriso/disk_ops.c xorriso/iso_img.c Made Xorriso_make_mount_cmd() ready for long block adresses 2024.03.18.214311 [e6e93a9] xorriso/drive_mgt.c Made Xorriso_make_read_options() ready for long block adresses 2024.03.19.090146 [d24634a] xorriso/drive_mgt.c Bug fix: -outdev holding an ISO filesystem could get attached wrong start LBA 2024.03.19.112303 [82deb54] xorriso/drive_mgt.c Made Xorriso_aquire_drive() ready for long block adresses 2024.03.19.185848 [662b305] xorriso/drive_mgt.c Made Xorriso_toc() ready for long block adresses 2024.03.19.205356 [c64e48d] xorriso/xorrisoburn.h xorriso/opts_p_z.c xorriso/drive_mgt.c Made Xorriso_tell_media_space() ready for long block adresses 2024.03.20.114449 [435521a] xorriso/xorrisoburn.h xorriso/emulators.c xorriso/drive_mgt.c xorriso/iso_img.c Made Xorriso_msinfo() ready for long block adresses 2024.03.20.150514 [cdffa4e] xorriso/findjob.h xorriso/findjob.c xorriso/iso_tree.c xorriso/opts_d_h.c xorriso/iso_manip.c Made struct ExprtesT test -lba_range ready for long block adresses 2024.03.20.213801 [4c7cfe9] xorriso/iso_img.h xorriso/iso_img.c xorriso/drive_mgt.c Made Xorriso_assert_volid() ready for long block adresses 2024.03.20.215145 [5e03c65] xorriso/xorrisoburn.h xorriso/opts_a_c.c xorriso/opts_d_h.c xorriso/opts_p_z.c xorriso/emulators.c xorriso/disk_ops.h xorriso/disk_ops.c xorriso/iso_manip.c xorriso/findjob.h xorriso/findjob.c Updated copyright date in various recently changed files 2024.03.21.084436 [458b0cb] xorriso/xorrisoburn.h xorriso/drive_mgt.c xorriso/iso_img.c xorriso/read_run.c Made Xorriso_obtain_indev_readsize() ready for long block adresses 2024.03.21.150655 [ec7ab52] xorriso/iso_tree.h xorriso/iso_tree.c xorriso/iso_img.c xorriso/write_run.c Made Xorriso_path_from_lba() ready for long block adresses 2024.03.21.165234 [698bee2] xorriso/iso_tree.h xorriso/iso_tree.c xorriso/iso_manip.c xorriso/read_run.c Made Xorriso__start_end_lbas() ready for long block adresses 2024.03.21.182815 [2d3302d] xorriso/iso_tree.h xorriso/iso_tree.c xorriso/iso_img.c xorriso/iso_manip.h xorriso/iso_manip.c xorriso/read_run.c xorriso/sort_cmp.c xorriso/write_run.c Made Xorriso__file_start_lba() ready for long block adresses 2024.03.21.193959 [fb780b5] xorriso/write_run.h xorriso/write_run.c Made Xorriso_sanitize_image_size() ready for long block adresses 2024.03.21.203354 [d635037] xorriso/write_run.c Made Xorriso_set_system_area() ready for long block adresses 2024.03.21.212514 [c878871] xorriso/xorrisoburn.h xorriso/write_run.c xorriso/opts_p_z.c Made Xorriso_write_session() ready for long block adresses 2024.03.22.133412 [ce3f23b] xorriso/write_run.c Made Xorriso_pacifier_loop() ready for long block adresses 2024.03.22.161812 [f1e00c3] xorriso/write_run.c Made Xorriso_burn_track() ready for long block adresses 2024.03.22.165841 [9377f66] xorriso/write_run.c Made Xorriso_overwrite_iso_head() ready for long block adresses 2024.03.22.204740 [8c61d88] xorriso/xorrisoburn.h xorriso/write_run.c xorriso/opts_a_c.c Made Xorriso_update_iso_lba0() ready for long block adresses 2024.03.23.173025 [fa7ba62] xorriso/write_run.c Made Xorriso_refresh_ts_tags() ready for long block adresses 2024.03.23.174931 [41a91df] xorriso/write_run.c Made Xorriso_overwrite_iso_head() ready for long block adresses, part 2 2024.03.23.183351 [2d8678a] xorriso/write_run.c Made Xorriso_adjust_session_size() ready for long block adresses 2024.03.23.212004 [5de4a8d] xorriso/write_run.c Made Xorriso_adjust_relocated_sb() ready for long block adresses 2024.03.24.081207 [c74f175] xorriso/xorriso_private.h xorriso/text_io.c xorriso/write_run.c xorriso/iso_img.c Made struct XorrisO ready for long block adresses 2024.03.24.084501 [fcad081] xorriso/write_run.c Made Xorriso_blank_media() ready for long block adresses 2024.03.24.085148 [14676ef] xorriso/write_run.c Made Xorriso_format_media() ready for long block adresses 2024.03.28.144046 [4fe385b] xorriso/drive_mgt.c Made struct xorriso_md5_state ready for long block addresses 2024.04.02.170748 [ffaa15e] xorriso/iso_img.c Prevented possible overflow of struct elto_img_par.extract_size 2024.04.03.161444 [fb23fe1] xorriso/drive_mgt.c Follow-up to commit ba12014: Made use of more isoburn*_v2() calls 2024.04.19.105210 [7f9d140] libisoburn/isofs_wrap.c Small correction of commit 646d15a 19 Apr 2024 [3955462] xorriso/xorrecord.texi xorriso/xorrecord.info xorriso/xorrecord.1 Mentioned in man xorrecord that write_start_address= expects bytes not blocks 2024.04.20.145515 [abf4375] xorriso/iso_manip.c Corrected a comment about Xorriso_findi_iter() 2024.04.27.163443 [ccb29ea] xorriso/check_media.c Correction to commit 86261b7: Sector bitmaps could not be read any more 2024.04.27.165051 [2af1e90] xorriso/drive_mgt.c Bug fix: Command -load "volid" did not work with constant search text 2024.04.27.165316 [a26df9c] xorriso/sfile.h xorriso/write_run.c Bug fix: Command -truncate_overwritable on ISO image without MD5 caused double free of memory 11 May 2024 [4818eea] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Mentioned in the man page the increased number of appended GPT partitions in libisofs 12 May 2024 [87e1d67] xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Mentioned in the man page that libisofs now memorizes all boot related paths at image load time. Warned of boot file manipulations after -boot_image replay. 2024.05.12.153800 [c8fab51] xorriso/xorrisoburn.h xorriso/opts_a_c.c xorriso/iso_tree.c Issueing warning messages if -boot_image paths currently are unsuitable 2024.05.13.101913 [4dc5edc] xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 Bug fix: -boot_image system_area=/dev/zero preserved system area of loaded ISO 2024.05.19.074013 [2ee3294] xorriso/iso_img.c Fixed a small memory leak introduced with commit aace531 2024.05.19.074151 [d7eba30] xorriso/drive_mgt.c Initialized a variable which has complicated ways of getting set 2024.05.19.074254 [5e79dc3] libisoburn/burn_wrap.c Bug fix: Size assessment of ISO images smaller than 32 KiB yielded random values 19 May 2024 [2c608c9] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.7 - 2024.05.19.074254 * Bug fix: -outdev holding an ISO filesystem could get attached wrong start LBA * Bug fix: Command -load "volid" did not work with constant search text * Bug fix: Command -truncate_overwritable on ISO image without MD5 caused double free of memory * Bug fix: -boot_image "any" "replay" failed after the legacy BIOS boot image file was replaced by -map. Thanks Brian C. Lane. * Bug fix: -boot_image system_area=/dev/zero preserved system area of loaded ISO * Bug fix: Size assessment of ISO images smaller than 32 KiB yielded random values * New API calls isoburn_toc_disc_get_sectors_v2, isoburn_toc_session_get_sectors_v2, isoburn_toc_track_get_emul_v2 * New API calls isoburn_read_iso_head_v2(), isoburn_get_mount_params_v2() * New API calls isoburn_igopt_get_effective_lba_v2(), isoburn_igopt_get_data_start_v2() * New API call isoburn_get_attached_start_lba_v2() * New API calls isoburn_attach_start_lba_v2(), isoburn_disc_get_msc1_v2(), isoburn_disc_track_lba_nwa_v2() * New API call isoburn_prepare_blind_grow_v2() * The maximum number of appended GPT partitions was increased from 4 to 8 2024.05.19.204636 [7e4a714] xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/emulators.c xorriso/iso_tree.c Suppressed warnings about non existing boot equipment files when mkisofs emulation is active 09 Jun 2024 [e4bcfa4] xorriso/make_xorriso_standalone.sh Using locally installed autotools files instead of libisoburn files when generating a GNU xorriso tarball 09 Jun 2024 [fad0413] Makefile.am Included in libisoburn tarball the software needed to produce GNU xorriso 10 Jun 2024 [67e5e48] xorriso/xorriso_makefile_am.txt Included in GNU xorriso tarball the software needed to produce GNU xorriso from library sources. Fixed rarely used "make dist" of GNU xorriso tarball. 17 Jun 2024 [25743e4] xorriso/xorrisofs.texi xorriso/xorrisofs.info xorriso/xorrisofs.1 Fixed a wrong word in man xorrisofs 2024.06.28.065555 [9222d38] xorriso/misc_funct.c Added YYYY_MM_DD_HHMMSS to the decodable forms of timestamps 2024.06.28.135605 [2306175] libisoburn/libisoburn.h libisoburn/burn_wrap.c New info return modes 3 "Creation Time" and 4 "Modification Time" with isoburn_read_iso_head() and isoburn_read_iso_head_v2() 2024.06.28.144204 [7f1abc4] xorriso/xorriso.h xorriso/xorriso_private.h xorriso/base_obj.c xorriso/parse_exec.c xorriso/opts_p_z.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/text_io.c xorriso/misc_funct.h xorriso/misc_funct.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New command -toc_info_type 2024.06.29.162751 [9ea7656] libisoburn/libisoburn.h libisoburn/burn_wrap.c New address modes 5 to 7 for isoburn_set_msc1(), isoburn_get_mount_params(), isoburn_get_mount_params_v2() 2024.06.29.212258 [2e4b824] libisoburn/libisoburn.h libisoburn/burn_wrap.c New address modes 8 and 9 for isoburn_set_msc1(), isoburn_get_mount_params(), isoburn_get_mount_params_v2() 2024.06.29.213042 [5133b9c] xorriso/parse_exec.h xorriso/parse_exec.c xorriso/opts_d_h.c xorriso/drive_mgt.c xorriso/iso_img.c xorriso/write_run.c xorriso/xorriso.texi xorriso/xorriso.info xorriso/xorriso.1 New entities "at_time", "before", "after", "not_after", "not_before" for commands -load, -mount, -mount_cmd, -session_string, -truncate_overwritable 30 Jun 2024 [] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.7 - 2024.06.29.213042 * New command -toc_info_type * New info return modes 3 "Creation Time" and 4 "Modification Time" with isoburn_read_iso_head() and isoburn_read_iso_head_v2() * New entities "at_time", "before", "after", "not_after", "not_before" for commands -load, -mount, -mount_cmd, -session_string, -truncate_overwritable * New address modes 5 to 9 for isoburn_set_msc1(), isoburn_get_mount_params(), isoburn_get_mount_params_v2() [] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.7 - [] ChangeLog xorriso/xorriso_eng.html xorriso/changelog.txt Updated change log and web page ------------------------------------ cycle - xorriso-1.5.7 - ********************************************************************** Important: When adding a public API function then add its name to file libisoburn/libisoburn.ver When adding a new xorriso command or xorrisofs option, check the list in file doc/grub-mkrescue_reserved_commands.txt ********************************************************************** =============================================================================== TODO =============================================================================== Current pending changes: >>> check whether the flat assumption of GPT block size 512 can do harm >>> Issue warning if -hfsplus is "off" and -report_system_area as_mkisofs does not find any blessings. >>> Report HFS+ blessings with -report_system_area "plain" ? >>> SuSE does EFI -boot-load-size 1. Shall xorriso do so with oversized EFI partition ? >>> HFS+ problems on coverty VM # mount /dev/sdb3 /mnt/hfs # ls /mnt/hfs ls: reading directory /mnt/hfs: Input/output error System boot empty-file.txt mach_kernel [ 359.020970] hfsplus: walked past end of dir # mount -o loop /dev/sdb /mnt/iso # ls /mnt/iso System boot boot.catalog efi.img empty-file.txt mach_kernel --------- 2015 - 2022: Known problems reported by /root/xorriso_repack_loop : === /dvdbuffer/dragora-3.0-x86_64-beta1-live.iso - /isolinux/boot.cat : st_mode : 0000555 <> 0000444 - /isolinux/boot.cat : st_mtime : Oct 15 2019 <> Oct 15 2019 diff= 10800 s - /isolinux/boot.cat : st_ctime : Oct 15 2019 <> Oct 15 2019 diff= 10800 s ... === /dvdbuffer/Mageia-8-beta1-i586.iso - /boot/grub2/eltorito.img : CONTENT : differs by at least 8 bytes. First at 12 - /boot.catalog : st_uid : 501 <> 0 - /boot.catalog : st_gid : 501 <> 0 ... Reason: iso_tree_add_boot_node() takes node .mode, .uid, .gid, and .hidden from the parent directory node. It sets .atime, .ctime, .mtime to the result of iso_nowtime(). But later, ecma119_image_new() sets itime to ecma119_determine_now_time() which comes in xorriso_repack_loop from xorrisofs option --modification-date='2019101422594900'. Time differences appear if the original catalog file does not bear the same time as the ISO's modification date. ----- >>> xorriso : SORRY : Cannot make proposal to mark PReP partition by data file: '/CD_GET_V' Drive current: -indev '/dvdbuffer/mkisofs-prep-boot.iso' --- Old: System area options: 0x00000200 System area summary: PReP ISO image size/512 : 3712 Partition offset : 0 MBR heads per cyl : 0 MBR secs per head : 0 MBR partition table: N Status Type Start Blocks MBR partition : 1 0x80 0x41 140 26 MBR partition path : 1 /CD_GET_V PReP boot partition: 35 7 --- New: System area options: 0x00000200 System area summary: PReP ISO image size/512 : 3152 Partition offset : 0 MBR heads per cyl : 64 MBR secs per head : 32 MBR partition table: N Status Type Start Blocks MBR partition : 1 0x00 0xcd 0 148 MBR partition : 2 0x00 0x41 148 28 MBR partition : 3 0x00 0xcd 176 2976 PReP boot partition: 37 7 ----- >>> Unusual MBR partition layout. isohybrid plus head transplant. >>> xorriso : SORRY : Cannot make proposal to mark data file as MBR partition without being in GPT : '/boot/x86_64/efi' Drive current: -indev '/dvdbuffer/openSUSE-13.1-NET-x86_64.iso' --- Old: El Torito catalog : 20 1 El Torito images : N Pltf B Emul Ld_seg Hdpt Ldsiz LBA El Torito boot img : 1 BIOS y none 0x0000 0x00 4 4238 El Torito boot img : 2 UEFI y none 0x0000 0x00 1 70 El Torito img path : 1 /boot/x86_64/loader/isolinux.bin El Torito img opts : 1 boot-info-table isohybrid-suitable El Torito img path : 2 /boot/x86_64/efi System area options: 0x00000202 System area summary: MBR isohybrid cyl-align-off ISO image size/512 : 581424 Partition offset : 0 MBR heads per cyl : 64 MBR secs per head : 32 MBR partition table: N Status Type Start Blocks MBR partition : 1 0x00 0xef 280 8192 MBR partition : 2 0x80 0x17 8472 573160 MBR partition path : 1 /boot/x86_64/efi --- New: El Torito catalog : 46 1 El Torito images : N Pltf B Emul Ld_seg Hdpt Ldsiz LBA El Torito boot img : 1 BIOS y none 0x0000 0x00 4 2095 El Torito boot img : 2 UEFI y none 0x0000 0x00 8192 47 El Torito img path : 1 /boot/x86_64/loader/isolinux.bin El Torito img opts : 1 boot-info-table isohybrid-suitable El Torito img path : 2 /boot/x86_64/efi System area options: 0x00000202 System area summary: MBR isohybrid cyl-align-off GPT ISO image size/512 : 572292 Partition offset : 0 MBR heads per cyl : 64 MBR secs per head : 32 MBR partition table: N Status Type Start Blocks MBR partition : 1 0x80 0x00 0 572292 MBR partition : 2 0x00 0xef 188 8192 MBR partition path : 2 /boot/x86_64/efi GPT : N Info GPT disk GUID : 552ab653be9e044caf952a521d48596c GPT entry array : 2 248 overlapping GPT lba range : 64 572228 572291 GPT partition name : 1 490053004f00480079006200720069006400 GPT partname local : 1 ISOHybrid GPT partition GUID : 1 552ab653be9e044caf972a521d48596c GPT type GUID : 1 a2a0d0ebe5b9334487c068b6b72699c7 GPT partition flags: 1 0x1000000000000001 GPT start and size : 1 0 572228 GPT partition name : 2 490053004f004800790062007200690064003100 GPT partname local : 2 ISOHybrid1 GPT partition GUID : 2 552ab653be9e044caf962a521d48596c GPT type GUID : 2 a2a0d0ebe5b9334487c068b6b72699c7 GPT partition flags: 2 0x1000000000000001 GPT start and size : 2 188 8192 GPT partition path : 2 /boot/x86_64/efi ----- ======== /dvdbuffer/focal-live-server-ppc64el.iso ... libisoburn: WARNING : ISO image size 610150s larger than readable size 610144s xorriso : NOTE : Loading ISO image tree from LBA 0 libburn : SORRY : Read start address 429530328s larger than number of readable blocks 610144 xorriso : UPDATE : 142 nodes read in 1 seconds libisofs: NOTE : Ignored non-empty MBR partition outside of medium capacity ... (There is garbage in bytes 462 to 509.) ----- >>> /dvdbuffer/debian-7.4.0-amd64-netinst.iso grows by 25 MB (/dvdbuffer/debian-testing-amd64-netinst.iso does not) - identical files initrd.gz and vmlinuz do not get unified as is done with newly imported ones. Caused by the fact that the debian ISO was made with hardlinks off and thus with PX entries of length 36. >>> Need a command to unify the inode numbers of files with same LBA and nonzero length ------------- >>> Maintain GPT individual GUIDs ? >>> ??? should giving up of both drives cause dropping of boot images ? >>> coordinate libisofs/image.h : iso_write_opts.system_area_options and iso_write_opts.imported_sa_info->system_area_options possibly by new API call: /* >>> Activates all System Area and El Torito features from the loaded ISO (see iso_image_report_system_area() and iso_image_report_el_torito()) for the upcomming ISO. */ int iso_image_set_boot_as_loaded(IsoImage *img, int flag); >>> Why does -boot_image "grub" "patch", -boot_image "grub" "boot_info_table=on" set bit1 of patch_isolinux_image/el_torito_set_isolinux_options ? - This causes iso_align_isohybrid() to align to full MB - } else if (sa_type == 0 && t->catalog != NULL && (t->catalog->bootimages[0]->isolinux_options & 0x0a) == 0x02) { /* Check for isolinux image with magic number of 3.72 and produce an MBR from our built-in template. (Deprecated since 31 Mar 2010) */ ------------------------------------------------------------------- DVD 95 home_backup_update_zisofs B41104 - load sbsector 2193824 indev /dev/sr3 ... libburn : DEBUG : SCSI error on read_10(0,32): [3 11 05] Medium error. L-EC uncorrectable error. ... Why does it read sector 0 ? indev /dev/sr2 ... libburn : DEBUG : SCSI error condition on command 28h READ(10): [4 3E 02] Drive error. Timeout on logical unit. libburn : DEBUG : SCSI error on read_10(0,32): [4 3E 02] Drive error. Timeout on logical unit. libburn : DEBUG : SCSI error condition on command 28h READ(10): [3 11 05] Medium error. L-EC uncorrectable error. libburn : DEBUG : SCSI error on read_10(0,16): [3 11 05] Medium error. L-EC uncorrectable error. ... dev indev /dev/sr2 ... libburn : SORRY : Drive is already registered resp. scanned Why does libburn have the drive registered while xorriso is not aware of it ? ------------------------------------------------------------------- xorriso -indev /dev/sr0 \ -as mkisofs -r -J -iso-level 3 \ -C 0,1486688 -o /dvdbuffer/ms.iso \ --md5 test_dir xorriso -as cdrecord -v dev=/dev/sr0 --grow_overwritable_iso \ -multi /dvdbuffer/ms.iso xorriso -md5 on -indev /dev/sr0 -check_md5_r sorry / -- libisofs: WARNING : Found copied superblock checksum tag probably ISO loading does not checkread MD5 Surely this does not use MD5s: xorriso -md5 on -indev /dev/sr0 -check_media -- ------------------------------------------------------------------- >>> Does libisofs need an assertion that sizeof(ino_t) >= 4 ? Where to state that libisofs produced ino may not surpass 32 bit. >>> Does libisofs have an assertion for sizeof(off_t) >= 8 ? >>> even libburn lacks it ------------------------------------------------------------------- Ponder this: Date: Sat, 3 May 2014 01:32:15 +0200 From: G.raud <graud@gmx.com> reply: B40503_cdwrite_g_raud > $ xorriso -indev img.iso -find \ > -fexec sh -c 'test "$1" = "$2" || test $ISO_MODE = 755' s "$name" '{}' \; \ > -exec lsdl I understand that "$1" and "$2" shall become the value of environment variable "name" (evaluated by the shell that starts xorriso) and the path of the currently examined ISO file object. The parameter range of -fexec shall end at "\;". "s" is a dummy sacrifice to the -c interpreter of sh, so that "$name" does not become "$0". I am not happy with the semicolon. Probably a user-defined separator word is preferrable. It would be the first parameter of -fexec and its next occurence would mark the end of -fexec. Like -fexec + sh -c ... + Similarly i'm not happy with '{}', because it would be a reserved word among the parameters of fexec. The solution could be user-defined word given as second parameter. Like -fexec + +path sh -c ... s "$name" +path ... + I am not happy with the potential to perform arbitrary actions on the computer system. One can easily shoot one's foot. ------------------------------------------------------------------- Try to forward a DVD burner to NetBSD by virtio. >>> appears as /dev/[r]ld0d >>> libburn insists in /dev/rcd*d address. Why ? >>> /dev/rld0d as softlink target of /dev/rcd9c refuses on ioctl(SCIOCCOMMAND) ------------------------------------------------------------------- >>> NetBSD: Why does wip/libisoburn linking fail without proper -R options and upstream libisoburn work without any -R but only -L ? >>> verify the suspicion that a single -R invalidates all -L at runtime ------------------------------------------------------------------- Expand GPT, MBR and APM end partitions on multi-session. Option to put backup GPT to end of storage device ? ??? xorrisofs option -isohybrid-mbr-efi for MBR partition 0xef but no GPT ? ------------------------------------------------------------------- Option to avoid removal of extraction target file. (for stdout, pipes, devices, fifos, (UNIX sockets) ?) Date: Wed, 16 Apr 2014 06:39:29 +0200 From: G.raud <graud@gmx.com> B40416_cdwrite_g_raud B40416_cdwrite_g_raud_3 B40417_cdwrite_g_raud: - Follow symbolic links to replace or overwrite its target rather than replacing the link itself. - Do not unlink special files but rather overwrite their content while keeping all their POSIX file attributes. (What about ACL and getfattr attributes ?) - Do not unlink regular files but handle them like the special file in the previous option. In case of no-unlink: If the existing target file cannot take content because of file type or permissions, then this is a failure event. The target will not be replaced. Special files from the ISO, which do not deliver content, will not replace target files on disk but rather cause a failure event. ------------------------------------------------------------------- >>> NetBSD >>> Explore mounting with arbitrary offset >>> not possible : (B402XX_netbsd_kernel_mount_offset) >>> 007_*.diff to 009*.diff. kern/48808 >>> test + compile with old and new /usr/include + run new binary on old and new kernel + mount(2) with MNT_GETARGS + -s with wd partitions (acts like a real partition) (+) mount(2) with MNT_UPDATE or MNT_RELOAD MNT_UPDATE does nothing, MNT_RELOAD gets censored away by mount(2) >>> wait for acceptance >>> Large file support >>> patch 011 +++ being tested ------------------------------------------------------- >>> Review output sequence of -status for compliance with -list_arg_sorting >>> ??? Adjust partition tables after add-on session with -boot_image any keep ? ------------- >>> Have an AAIP pointer from any type of file to a (hidden ?) directory which represents Solaris openat(O_XATTR) >>> -map and -extract O_XATTR attributes on Solaris man 2 openat: O_XATTR If set in openat(), a relative path argument is inter- preted as a reference to an extended attribute of the file associated with the supplied file descriptor. This flag therefore requires the presence of a legal fildes argument. If set in open(), the implied file descriptor is that for the current working directory. Extended attributes must be referenced with a relative path; pro- viding an absolute path results in a normal file refer- ence. man 5 fsattr: The set of existing attributes can be browsed by calling openat() with "." as the file name and the O_XATTR flag set, resulting in a file descriptor for the attribute directory. The list of attributes is obtained by calls to getdents(2) on the returned file descriptor. >>> create O_XATTR attributes in ISO from normal directory >>> extract O_XATTR attributes in ISO to normal directory ----------------- - GRUB2 patching -as mkisofs \ -hfsplus \ -no-pad \ -o /u/test/test.iso \ --protective-msdos-label \ --grub2-mbr /u/test/grub2/zero_mbr \ -c /BOOT.cat \ -b /img1 \ -boot-info-table -no-emul-boot -boot-load-size 4 \ --grub2-boot-info \ -eltorito-alt-boot \ -b /img2 \ -boot-info-table -no-emul-boot -boot-load-size 4 \ /u/test/grub2/ --grub2-sparc-core /boot/grub/sparc64-ieee1275/core.img -as mkisofs \ -no-pad \ -o /u/test/test.iso \ -sparc-boot "" \ -G /u/test/grub2/zero_mbr \ --grub2-sparc-core /img1 \ /u/test/grub2/ >>> make optional use of ttk::treeview instead of BWidget (needs Tk 8.5) consider to let -x choose a drive automatically at the sequence position of -dev, if there are commands which would need a drive. >>> needs a scan mode that inspects no other drives after the first one ----------------------------------------------------- Vladimir - Multi-Session >>> hfsplus.c has no protection against 32 bit rollover due to block addresses that are lower than the HFS+ partition start. + Provisorily banned in xorriso the combination of -hfsplus and growing to appendables >>> install a similar ban in libisofs ? Is HFS+ usable if added to a session ? + The system area gets copied to block 0-15 ? Can HFS+ partitions be added ? (we have few) >>> How to let HFS+ access blocks outside the new session ? ? A patchable HFS superblock in the first session ? >>> for image modifying: - Find out at image load time whether it has APM with HFS+ - Have -hfsplus mode "auto" to enable HFS+ writing in that case. + -as mkisofs for HFS+. Rough sketch. Some options will change. genisoimage -hfs -part -no-desktop -r -J -o ${output_image} \ -map ${map_file} -hfs-bless ${boot_dir} -chrp-boot -sysid PPC \ ${iso_dir} ${source} + -hfs -part -> -hfsplus + -map -> -hfsplus-file-creator-type + -hfs-bless + -chrp-boot -> -chrp-boot-part - Prep and ChrP Vladimir: "-chrp-boot produce an MBR entry spanning the whole ISO and of type 0x96. PreP boot may be preferable." "PreP and CHRP refers mainly to IBM hardware. HFS+ is for Apple only. PreP boot is a partition contating only raw ELF and having type 0x41." + -as mkisofs -chrp-boot-part + -as mkisofs -prep-boot-part DISKFILE + xorriso -boot_image any chrp_boot_part=on|off + xorriso -boot_image any prep_boot_part=disk_path ??? Let writer grab Ecma119Node rather than disk file >>> make combinable with isohybrid - FAT for mounting by running systems which do not expect ISO 9660 on USB stick. --- Da Windows AFAIR nur fuer FAT und NTFS auf USB sticks anschaut, braucht man, eine FAT Dateisystem in der Partition und nicht noch eine Kopie von ISO. Andere Desktopsysteme haben manchmal aehnliches Problem. Man kann HFS+ ins kombinierte HFS+/FAT umwandeln aber dazu braucht man die Groesse zwischen erster und zweiter Zone im Voraus. + This is meanwhile available as Ecma119Image.filesrc_blocks --- + -as mkisofs -fat >>> FAT not implemented yet >>> document >>> xorriso -fat on|off , status -fat , -hide-fat* - EFI boot via GPT >>> Get a positive test result from Vladimir Vladimir: > I'm happy to continue to use > mtools for this, xorriso will have just to embed the image as a GPT > parition. + -as mkisofs -efi-boot-part DISKFILE + Let writer grab Ecma119Node rather than disk file + --efi-boot-image as DISKFILE + xorriso -boot_image efi_boot_part= - Vladimir: Was meinst du zur Möglichkeit es zu einer HFS+/FAT/ISO hyvrid machen? "Wir können es in die gleiche Partition machen. im FAT es kann bis 2^16 512B Sektoren = 32M zwischen BPB und FAT geben. Wir können in diesen Platz volumedescr2 reinschleppen." >>> Currently Ecma119.partition_offset is needed before compute_data_blocks. Several components expect the twin sets of Volume Descriptors and of filesystem trees to be stored as one interval per set. Vladimir's proposal would separate the second Volume Descriptors and trees from the first ones. - Overall goals: Date: Thu, 24 May 2012 16:22:44 +0200 ---------------------------------------------------------------------------- Let's look on the goals side and what is achieved and what isn't. My goals: Already achieved with xorriso: + i386_pc (HD and CD) + *_efi (CD) + (i386_qemu, i386_coreboot, i386_multiboot, mips_loongson, mips-qemu_mips) + Accessible by major OS on CD Would be achieved by your work (I suppose) + *_efi (HD) + -efi-boot-part DISKFILE|--efi-boot-image Achieved by HFS+: + Apple EFI (HD and CD) >>> Vladimir: GPT HFS+ entry still missing from hfsplus.c. Is it needed for booting ? + Apple PPC, New world (HD and CD) Other: + IBM (PreP) PPC (HD and CD). We need PreP partition for HD, + -prep-boot-part DISKFILE >>> who can tell ?: Idk what we need for CD. -Ability to have eltorito.img over 32K and free system area from it: the patching like in the patch, I've showed you in the beginning. - B20521_from_grub_proposal >>> Ask Vladimir for refreshing this info -Accessible by major OS on HD. >>> Vladimir: We probably need FAT for this. "but it creates drawbacks like that its allocation table grows with disk size and unlike on HFS+ there is no way to put it at the end of the disk." + solved by early size computation of filesrc area Ecma119Image.filesrc_blocks ---------------------------------------------------------------------------- - make -check_media chunk_size=32s default for DVD ? ---------------------------------------------------------------------------- - -check_media option which aborts the check run on the first error ? Such an option for -check_md5_r ? ??? make -early_stdio_test on by default ? man page for osirrox ? -------------------------- - libisofs: Dynamic hand-out of filesystem id number for user streams and registration of a type string. Deprecate global variable serial_id. demo/custom_stream.c ------------------------------------------------------------------------- ------------------------------------------------- For Debian: - Need specs for the exotic boot options -chrp-boot - needs HFS ------------------------------------------------- For GRUB - multi-boot management libisofs: - replace memory buffered boot images by filtered streams - in eltorito_writer_compute_data_blocks(): from iso_memory_stream_new() to some filter contraption with memorized first block and reading the rest from stream. - let patch_boot_image() read directly from stream (filtered or unfiltered should make no difference for the checksum) - warn that multiple boot images work only if they stem from distinct disk files ------------------------------------------------- bugs - xorriso_eng.html on www.gnu.org it should rather be xorriso.html - DVD-ROM drives report CD tracks with size up to the next track start. Why ? -> libburn Eventually restrict -check_media to ISO image size ? - make iso_init() and iso_finish() safe against multiple calls from multiple threads. - what about split files and hard links ? - xorriso -as cdrecord in rare cases reports intermediate "X of X MB" xorriso : UPDATE : 3671 MB written (fifo 94%) [buf 100%] 2.3x. xorriso : UPDATE : 3680 of 3680 MB written (fifo 87%) [buf 100%] 2.3x. xorriso : UPDATE : 3691 MB written (fifo 78%) [buf 100%] 2.3x. Direct reason: burn_drive_get_status(drive, &progress); if(progress.sector<=progress.sectors) sprintf(xorriso->info_text, "%4d of %4d MB written", So this has to be examined in libburn. ------------------------------------------------- important - improve find action estimate_size. - take into account indispensible RR and eventual Joliet stuff - take into account the eventual file checksums. - Mark data blocks of -check_md5 matching files as valid in sector map ? - tree of name nodes to represent the cached paths of hardlink candidates. - register hard link disk path for files with link count 1 and allow in Xorriso_restore_target_hl() to use the own hln_target of a node when that node is copied to disk a second time. - during -update* - what about differently filtered streams ? It is not wrong but a waste to update them as siblings. - one-time delimiter which overrides -list_delimiter for only the next command. E.g. for --back_to_xorriso in emulations - sudo and setuid chapter in man page ? option -sudo_aware ??? move suffix processing into libisofs ? (currently the suffice are already stored in the filter chain) Can only be done if renaming is made safe with any open IsoDirIter. ------------------------------------------------- development - Special pseudo ACL: "--remove-default" - libisofs , libburn , own system adapter ? : Needed is a system dependend facility: dev_t <-> major, minor - ??? -getfaclx , -getfattrx - -load option to ignore existing images - random access read and write option - change -check_media what=disc to libburn disc rather than libisoburn (seems to need new API capabilities of libburn) ??? Clarify handling of links in ISO and on disk during restore > Relative addressing and pattern matching : iso_rr_pattern on cd /u/FERTIG lsd nonexist/.. - perform any long lasting operation in separate threads while the main thread cares for user and message queues. - Introduce an interrupt key for dialog >>> but how without disturbing readline ? - ??? http://scan.coverity.com/faq.html - ??? curb depth of tree traversal recursion - eject a not yet aquired device (e.g. after modifying commit) - -cd[ix] and pattern - memory curb for image model > Make transactional the tree deletions meant for replacing ------------------------------------------------- libisofs wishes ------ problem fixes : - the error handling system should be comprehensively documented and eventually rectified where needed. - the iterators are still not safe with node renamings: iso_dir_get_children(...); while(...) { iso_dir_iter_next(...); ... iso_node_get_name() returns "xyz" ... iso_node_set_name("xyz.gz"); ... iso_file_add_external_filter() returns 2 ... iso_node_set_name("xyz"); } ------ feature enhancements : - With 100,000 files in a single directory, adding new nodes becomes very slow with high workload. Some hash accelerator would be nice. - Re-use unchanged sub trees in the previous image in order to reduce the session overhead. -------------------------------------------------------------------------- License statement by the author of the HFS+ enhancements in libisofs and libisoburn: Date: Wed, 23 May 2012 18:26:15 +0200 Message-ID: <4FBD0FA7.8040105@gmail.com> From: Vladimir '?-coder/phcoder' Serbinenko <phcoder@gmail.com> To: Thomas Schmitt <scdbackup@gmx.net> Subject: Re: HFS+ writer I'm ok with LGPL but I'd like a mention in documentation that HFS+ writer is of my coding. -- Regards Vladimir '?-coder/phcoder' Serbinenko -------------------------------------------------------------------------- =============================================================================== This is the dirty end of the todo list. The recent changelog entries are above the headline "TODO". =============================================================================== /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of classes SpotlistiteM, SectorbitmaP, CheckmediajoB which perform verifying runs on media resp. images. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <fcntl.h> #include <errno.h> /* O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" /* ------------------------------ SpotlisT -------------------------------- */ struct SpotlistiteM { off_t start_lba; off_t blocks; int quality; struct SpotlistiteM *next; }; int Spotlistitem_new(struct SpotlistiteM **o, off_t start_lba, off_t blocks, int quality, int flag) { struct SpotlistiteM *m; m= TSOB_FELD(struct SpotlistiteM,1); if(m==NULL) return(-1); *o= m; m->start_lba= start_lba; m->blocks= blocks; m->quality= quality; m->next= NULL; return(1); } int Spotlistitem_destroy(struct SpotlistiteM **o, int flag) { if((*o) == NULL) return(0); free((char *) *o); *o= NULL; return(1); } struct SpotlisT { struct SpotlistiteM *list_start; struct SpotlistiteM *list_end; off_t list_count; struct SpotlistiteM *current_item; off_t current_idx; }; int Spotlist_new(struct SpotlisT **o, int flag) { struct SpotlisT *m; m= TSOB_FELD(struct SpotlisT,1); if(m==NULL) return(-1); *o= m; m->list_start= NULL; m->list_end= NULL; m->list_count= 0; m->current_item= NULL; m->current_idx= -1; return(1); } int Spotlist_destroy(struct SpotlisT **o, int flag) { struct SpotlisT *m; struct SpotlistiteM *li, *next_li; if((*o) == NULL) return(0); m= *o; for(li= m->list_start; li != NULL; li= next_li) { next_li= li->next; Spotlistitem_destroy(&li, 0); } free((char *) *o); *o= NULL; return(1); } int Spotlist_add_item(struct SpotlisT *o, off_t start_lba, off_t blocks, int quality, int flag) { int ret; struct SpotlistiteM *li; static int debug_verbous= 0; ret= Spotlistitem_new(&li, start_lba, blocks, quality, 0); if(ret <= 0) return(ret); if(o->list_end != NULL) o->list_end->next= li; o->list_end= li; if(o->list_start == NULL) o->list_start= li; (o->list_count)++; if(debug_verbous) {char quality_name[80]; fprintf(stderr, "debug: lba %10.f , size %10.f , quality '%s'\n", (double) start_lba, (double) blocks, Spotlist__quality_name(quality, quality_name, Xorriso_read_quality_invaliD, 0) + 2); } return(1); } off_t Spotlist_count(struct SpotlisT *o, int flag) { return o->list_count; } off_t Spotlist_block_count(struct SpotlisT *o, int flag) { off_t list_blocks= 0; struct SpotlistiteM *li; for(li= o->list_start; li != NULL; li= li->next) { if(li->start_lba + li->blocks > list_blocks) list_blocks= li->start_lba + li->blocks; } return(list_blocks); } off_t Spotlist_sector_size(struct SpotlisT *o, off_t read_chunk, int flag) { off_t sector_size; struct SpotlistiteM *li; sector_size= read_chunk * 2048; for(li= o->list_start; li != NULL; li= li->next) { if((li->start_lba % read_chunk) || (li->blocks % read_chunk)) { sector_size= 2048; break; } } return(sector_size); } int Spotlist_get_item(struct SpotlisT *o, off_t idx, off_t *start_lba, off_t *blocks, int *quality, int flag) { off_t i; struct SpotlistiteM *li; if(idx < 0 || idx > o->list_count) return(0); if(idx == o->current_idx && o->current_item != NULL) li= o->current_item; else if(idx == o->current_idx + 1 && o->current_item != NULL) { li= o->current_item->next; } else { li= o->list_start; for(i= 0; i < idx; i++) li= li->next; } o->current_item= li; o->current_idx= idx; *start_lba= li->start_lba; *blocks= li->blocks; *quality= li->quality; return(1); } char *Spotlist__quality_name(int quality, char name[80], int bad_limit, int flag) { if(quality == Xorriso_read_quality_untesteD || quality == Xorriso_read_quality_tao_enD || (quality == Xorriso_read_quality_md5_mismatcH && quality > bad_limit) || quality == Xorriso_read_quality_off_tracK) strcpy(name, "0 "); else if(quality <= bad_limit) strcpy(name, "- "); else strcpy(name, "+ "); if(quality == Xorriso_read_quality_gooD) strcat(name, "good"); else if(quality == Xorriso_read_quality_md5_matcH) strcat(name, "md5_match"); else if(quality == Xorriso_read_quality_sloW) strcat(name, "slow"); else if(quality == Xorriso_read_quality_partiaL) strcat(name, "partial"); else if(quality == Xorriso_read_quality_valiD) strcat(name, "valid"); else if(quality == Xorriso_read_quality_untesteD) strcat(name, "untested"); else if(quality == Xorriso_read_quality_md5_mismatcH) strcat(name, "md5_mismatch"); else if(quality == Xorriso_read_quality_invaliD) strcat(name, "invalid"); else if(quality == Xorriso_read_quality_tao_enD) strcat(name, "tao_end"); else if(quality == Xorriso_read_quality_off_tracK) strcat(name, "off_track"); else if(quality == Xorriso_read_quality_unreadablE) strcat(name, "unreadable"); else sprintf(name, "0 0x%8.8X", (unsigned int) quality); return(name); } /* ---------------------------- End SpotlisT ------------------------------ */ /* ---------------------------- SectorbitmaP ------------------------------ */ int Sectorbitmap_new(struct SectorbitmaP **o, off_t sectors, off_t sector_size, int flag) { struct SectorbitmaP *m; m= TSOB_FELD(struct SectorbitmaP,1); if(m==NULL) return(-1); *o= m; m->sectors= sectors; m->sector_size= sector_size; m->map= NULL; m->map_size= sectors / 8 + 1; m->map= calloc(m->map_size, 1); if(m->map == NULL) goto failure; return(1); failure:; Sectorbitmap_destroy(o, 0); return(-1); } int Sectorbitmap_destroy(struct SectorbitmaP **o, int flag) { if((*o) == NULL) return(0); if((*o)->map != NULL) free((char *) (*o)->map); free((char *) *o); *o= NULL; return(1); } int Sectorbitmap_from_file(struct SectorbitmaP **o, char *path, char *msg, int *os_errno, int flag) { int ret, fd= -1, todo, skip, bufsize= 1024; off_t sectors, sector_size; ssize_t i, map_size; unsigned char *map; unsigned char *buf, buf_head[26]; buf= TSOB_FELD(unsigned char, bufsize); if(buf == NULL) return(-1); buf_head[0]= 0; *os_errno= 0; if(msg != NULL) msg[0]= 0; fd= open(path, O_RDONLY | O_BINARY); if(fd == -1) { *os_errno= errno; if(msg != NULL) { strcpy(msg, "Cannot open path "); Text_shellsafe(path, msg+strlen(msg), 0); } {ret= 0; goto ex;} } ret= read(fd, buf, 32); if(ret < 32) { wrong_filetype:; if(ret == -1) *os_errno= errno; if(msg != NULL) { strcpy(msg, "Not a sector bitmap file: "); Text_shellsafe(path, msg+strlen(msg), 0); } ret= 0; goto ex; } if(strncmp((char *) buf, "xorriso sector bitmap v1 ", 32) == 0) { memcpy(buf_head, buf, 25); } else if(strncmp((char *) buf, "xorriso sector bitmap v2 ", 25) == 0 || strncmp((char *) buf, "xorriso sector bitmap v3 ", 25) == 0) { memcpy(buf_head, buf, 25); buf_head[25]= 0; skip= -1; sscanf(((char *) buf) + 25, "%d", &skip); if(skip < 0) {ret= 0; goto wrong_filetype;} for(i= 0; i < skip; i+= bufsize) { todo= bufsize; if(i + todo > skip) todo= skip - i; ret= read(fd, buf, todo); if(ret < todo) goto wrong_filetype; } } else {ret= 0; goto wrong_filetype;} if(strncmp((char *) buf_head, "xorriso sector bitmap v1 ", 25) == 0 || strncmp((char *) buf_head, "xorriso sector bitmap v2 ", 25) == 0) { ret= read(fd, buf, 8); if(ret < 8) goto wrong_filetype; if((buf[0] & 128) || (buf[4] & 128)) goto wrong_filetype; sectors= (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; sector_size= (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; } else { ret= read(fd, buf, 16); if(ret < 16) goto wrong_filetype; if((buf[0] & 128) || (buf[8] & 128)) goto wrong_filetype; sectors= ((off_t) buf[0] << 56) | ((off_t) buf[1] << 48) | ((off_t) buf[2] << 40) | ((off_t) buf[3] << 32) | (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; sector_size= ((off_t) buf[8] << 56) | ((off_t) buf[9] << 48) | ((off_t) buf[10] << 40) | ((off_t) buf[11] << 32) | (buf[12] << 24) | (buf[13] << 16) | (buf[14] << 8) | buf[15]; } ret= Sectorbitmap_new(o, sectors, sector_size, 0); if(ret <= 0) { if(msg != NULL) sprintf(msg, "Cannot allocate bitmap memory for %.f sectors", (double) sectors); ret= -1; goto ex; } map= (*o)->map; map_size= (*o)->map_size; for(i= 0; i < map_size; i+= bufsize) { todo= bufsize; if(i + todo > map_size) todo= map_size - i; ret= read(fd, buf, todo); if(ret != todo) goto wrong_filetype; memcpy(map + i, buf, todo); } ret= 1; ex:; if(fd != -1) close(fd); if(buf != NULL) free(buf); if(ret <= 0) Sectorbitmap_destroy(o, 0); return(ret); } int Sectorbitmap_to_file(struct SectorbitmaP *o, char *path, char *info, char *msg, int *os_errno, int flag) { int fd= -1, j, l, version= 2; ssize_t ret; unsigned char buf[40]; *os_errno= 0; fd= open(path, O_WRONLY | O_CREAT | O_BINARY, S_IRUSR | S_IWUSR); if(fd == -1) { *os_errno= errno; if(msg != NULL) { strcpy(msg, "Cannot open path "); Text_shellsafe(path, msg+strlen(msg), 0); } ret= 0; goto ex; } if(o->sectors > 0x7fffffff || o->sector_size > 0x7fffffff) version= 3; l= 0; if(info != NULL) l= strlen(info); if(l > 999999) { strcpy(msg, "Info string is longer than 999999 bytes"); ret= 0; goto ex; } sprintf((char *) buf, "xorriso sector bitmap v%d %-6d\n", version, l); ret= write(fd, buf, 32); if(ret != 32) { cannot_write:; *os_errno= errno; if(msg != NULL) { strcpy(msg, "Cannot write to "); Text_shellsafe(path, msg+strlen(msg), 0); } ret= 0; goto ex; } if(l > 0) { ret= write(fd, info, l); if(ret != l) goto cannot_write; } if(version == 2) { for(j= 0; j < 4; j++) { buf[j]= o->sectors >> (24 - j * 8); buf[j + 4]= o->sector_size >> (24 - j * 8); } ret= write(fd, buf, 8); if(ret != 8) goto cannot_write; } else { for(j= 0; j < 8; j++) { buf[j]= o->sectors >> (56 - j * 8); buf[j + 8]= o->sector_size >> (56 - j * 8); } ret= write(fd, buf, 16); if(ret != 16) goto cannot_write; } ret= write(fd, o->map, o->map_size); if(ret != o->map_size) goto cannot_write; ret= 1; ex:; if(fd != -1) close(fd); return(ret); } /* @param flag bit0= sector bit value */ int Sectorbitmap_set(struct SectorbitmaP *o, off_t sector, int flag) { if(sector < 0 || sector >= o->sectors) return(0); if(flag & 1) o->map[sector / 8]|= 1 << (sector % 8); else o->map[sector / 8]&= ~(1 << (sector % 8)); return(1); } /* @param flag bit0= sector bit value */ int Sectorbitmap_set_range(struct SectorbitmaP *o, off_t start_sector, off_t sectors, int flag) { off_t start_i, end_i, i; unsigned char value; if(start_sector < 0 || start_sector + sectors > o->sectors || sectors < 1) return(0); if(flag & 1) value= ~0; else value= 0; start_i= start_sector / 8; end_i= (start_sector + sectors - 1) / 8; for(i= start_sector; i / 8 == start_i && i < start_sector + sectors; i++) Sectorbitmap_set(o, i, flag & 1); for(i= start_i + 1; i < end_i; i++) o->map[i]= value; if(end_i > start_i) for(i= end_i * 8; i < start_sector + sectors; i++) Sectorbitmap_set(o, i, flag & 1); return(1); } int Sectorbitmap_is_set(struct SectorbitmaP *o, off_t sector, int flag) { if(sector < 0 || sector >= o->sectors) return(0); return(!! (o->map[sector / 8] & (1 << (sector % 8)))); } int Sectorbitmap_bytes_are_set(struct SectorbitmaP *o, off_t start_byte, off_t end_byte, int flag) { off_t end_sector, i; end_sector= end_byte / o->sector_size; for(i= start_byte / o->sector_size; i <= end_sector; i++) if(!Sectorbitmap_is_set(o, i, 0)) return(0); return(1); } int Sectorbitmap_get_layout(struct SectorbitmaP *o, off_t *sectors, off_t *sector_size, int flag) { *sectors= o->sectors; *sector_size= o->sector_size; return(1); } int Sectorbitmap_copy(struct SectorbitmaP *from, struct SectorbitmaP *to, int flag) { int run_value, start_aligned, end_complete; off_t i, run_start, start_sec, limit_sec; if(((off_t) from->sectors) * ((off_t) from->sector_size) > ((off_t) to->sectors) * ((off_t) to->sector_size)) return(-1); if(from->sector_size == to->sector_size) { for(i= 0; i < from->map_size; i++) to->map[i]= from->map[i]; return(1); } run_start= 0; run_value= Sectorbitmap_is_set(from, (off_t) 0, 0); for(i= 1; i <= from->sectors; i++) { if(i < from->sectors) if(Sectorbitmap_is_set(from, i, 0) == run_value) continue; start_sec= run_start * from->sector_size / to->sector_size; start_aligned= (start_sec * to->sector_size == run_start * from->sector_size); limit_sec= i * from->sector_size / to->sector_size; end_complete= (limit_sec * to->sector_size == i * from->sector_size); if(run_value) { if(!start_aligned) start_sec++; } else { if(!end_complete) limit_sec++; } if(start_sec < limit_sec) Sectorbitmap_set_range(to, start_sec, limit_sec - 1 - start_sec, !!run_value); run_value= !run_value; run_start= i; } return(1); } int Sectorbitmap_clone(struct SectorbitmaP *from, struct SectorbitmaP **clone, int flag) { int ret; ret= Sectorbitmap_new(clone, from->sectors, from->sector_size, 0); if(ret <= 0) return(ret); ret= Sectorbitmap_copy(from, *clone, 0); if(ret <= 0) Sectorbitmap_destroy(clone, 0); return(ret); } /* -------------------------- End SectorbitmaP ---------------------------- */ /* ---------------------------- CheckmediajoB ----------------------------- */ int Checkmediajob_new(struct CheckmediajoB **o, int flag) { struct CheckmediajoB *m; m= TSOB_FELD(struct CheckmediajoB,1); if(m==NULL) return(-1); *o= m; m->use_dev= 0; m->min_lba= -1; m->max_lba= -1; m->min_block_size= 0; m->async_chunks= 0; m->mode= 0; m->start_time= time(NULL); m->time_limit= 28800; m->item_limit= 100000; strcpy(m->abort_file_path, "/var/opt/xorriso/do_abort_check_media"); m->data_to_path[0]= 0; m->data_to_fd= -1; m->data_to_offset= 0; m->data_to_limit= -1; m->data_to_skip= 0; m->patch_lba0= 0; m->patch_lba0_msc1= -1; m->sector_map_path[0]= 0; m->sector_map= NULL; m->map_with_volid= 0; m->retry= 0; m->report_mode= 0; strcpy(m->event_severity, "ALL"); m->slow_threshold_seq= 1.0; return(1); } int Checkmediajob_destroy(struct CheckmediajoB **o, int flag) { if((*o) == NULL) return(0); if((*o)->data_to_fd != -1 && strcmp((*o)->data_to_path, "-") != 0) close((*o)->data_to_fd); Sectorbitmap_destroy(&((*o)->sector_map), 0); free((char *) *o); *o= NULL; return(1); } int Checkmediajob_copy(struct CheckmediajoB *from, struct CheckmediajoB *to, int flag) { to->use_dev= from->use_dev; to->min_lba= from->min_lba; to->max_lba= from->max_lba; to->min_block_size= from->min_block_size; to->mode= from->mode; to->time_limit= from->time_limit; to->item_limit= from->item_limit; strcpy(to->abort_file_path, from->abort_file_path); strcpy(to->data_to_path, from->data_to_path); /* not copied: data_to_fd */ to->data_to_offset= from->data_to_offset; to->data_to_limit= from->data_to_limit; to->data_to_skip= from->data_to_skip; to->patch_lba0= from->patch_lba0; to->patch_lba0_msc1= from->patch_lba0_msc1; strcpy(to->sector_map_path, from->sector_map_path); /* not copied: sector_map */ to->map_with_volid= from->map_with_volid; to->retry= from->retry; to->report_mode= from->report_mode; strcpy(to->event_severity, from->event_severity); to->slow_threshold_seq= from->slow_threshold_seq; return(1); } /* -------------------------- End CheckmediajoB --------------------------- */ int Xorriso_check_media_setup_job(struct XorrisO *xorriso, struct CheckmediajoB *job, char **argv, int old_idx, int end_idx, int flag) { int ret, i, sev; double num; struct CheckmediajoB *default_job; char sev_text[20]; if(xorriso->check_media_default != NULL) Checkmediajob_copy(xorriso->check_media_default, job, 0); for(i= old_idx; i < end_idx; i++) { if(strncmp(argv[i], "abort_file=", 11) == 0) { ret= Sfile_str(job->abort_file_path, argv[i] + 11, 0); if(ret <= 0) goto ex; } else if(strncmp(argv[i], "async_chunks=", 13) == 0) { num= Scanf_io_size(argv[i] + 13, 1); if(num >= 0 && num <= 1024) job->async_chunks= num; else goto bad_value; } else if(strncmp(argv[i], "bad_limit=", 10) == 0) { if(strcmp(argv[i] + 10, "good") == 0) xorriso->check_media_bad_limit= Xorriso_read_quality_gooD; else if(strcmp(argv[i] + 10, "md5_match") == 0) xorriso->check_media_bad_limit= Xorriso_read_quality_md5_matcH; else if(strcmp(argv[i] + 10, "slow") == 0) xorriso->check_media_bad_limit= Xorriso_read_quality_sloW; else if(strcmp(argv[i] + 10, "partial") == 0) xorriso->check_media_bad_limit= Xorriso_read_quality_partiaL; else if(strcmp(argv[i] + 10, "valid") == 0) xorriso->check_media_bad_limit= Xorriso_read_quality_valiD; else if(strcmp(argv[i] + 10, "untested") == 0) xorriso->check_media_bad_limit= Xorriso_read_quality_untesteD; else if(strcmp(argv[i] + 10, "md5_mismatch") == 0) xorriso->check_media_bad_limit= Xorriso_read_quality_md5_mismatcH; else if(strcmp(argv[i] + 10, "invalid") == 0) xorriso->check_media_bad_limit= Xorriso_read_quality_invaliD; else if(strcmp(argv[i] + 10, "tao_end") == 0) xorriso->check_media_bad_limit= Xorriso_read_quality_tao_enD; else if(strcmp(argv[i] + 10, "off_track") == 0) xorriso->check_media_bad_limit= Xorriso_read_quality_off_tracK; else if(strcmp(argv[i] + 10, "unreadable") == 0) xorriso->check_media_bad_limit= Xorriso_read_quality_unreadablE; else goto unknown_value; } else if(strncmp(argv[i], "data_to=", 8) == 0) { ret= Sfile_str(job->data_to_path, argv[i] + 8, 0); if(ret <= 0) goto ex; } else if(strncmp(argv[i], "chunk_size=", 11) == 0) { num= Scanf_io_size(argv[i] + 11, 1); if(num >= 2048 || num == 0) job->min_block_size= num / 2048; else goto bad_value; } else if(strncmp(argv[i], "event=", 6) == 0) { strncpy(sev_text, argv[i] + 6, 19); sev_text[19]= 0; ret= Xorriso__text_to_sev(sev_text, &sev, 0); if(ret <= 0) { strcpy(xorriso->info_text, "-check_media event="); Text_shellsafe(sev_text, xorriso->info_text, 1); strcat(xorriso->info_text, " : Not a known severity name"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto ex; } strcpy(job->event_severity, sev_text); } else if(strncmp(argv[i], "map_with_volid=", 15) == 0) { if(strcmp(argv[i] + 15, "on") == 0) job->map_with_volid= 1; else if(strcmp(argv[i] + 15, "off") == 0) job->map_with_volid= 0; else goto unknown_value; } else if(strncmp(argv[i], "max_lba=", 8) == 0 || strncmp(argv[i], "min_lba=", 8) == 0) { num= -1; sscanf(argv[i] + 8, "%lf", &num); if(num > 0x7fffffffffffffff || num < 0) num= -1; if(strncmp(argv[i], "max_lba=", 8) == 0) job->max_lba= num; else job->min_lba= num; } else if(strncmp(argv[i], "patch_lba0=", 11) == 0) { job->patch_lba0_msc1= -1; if(strcmp(argv[i] + 11, "on") == 0) job->patch_lba0= 1; else if(strcmp(argv[i] + 11, "off") == 0) job->patch_lba0= 0; else if(strcmp(argv[i] + 11, "force") == 0) job->patch_lba0= 2; else if(argv[i][11] >= '1' && argv[i][11] <= '9') { num= -1; sscanf(argv[i] + 11, "%lf", &num); if(num > 0x7fffffffffffffff || num < 0) goto bad_value; job->patch_lba0_msc1= num; job->patch_lba0= (num >= 32) + (strstr(argv[i] + 11, ":force") != NULL); } else goto unknown_value; } else if(strncmp(argv[i], "report=", 7) == 0) { if(strcmp(argv[i] + 7, "blocks") == 0) job->report_mode= 0; else if(strcmp(argv[i] + 7, "files") == 0) job->report_mode= 1; else if(strcmp(argv[i] + 7, "blocks_files") == 0) job->report_mode= 2; else goto unknown_value; } else if(strcmp(argv[i], "reset=now") == 0) { ret= Checkmediajob_new(&default_job, 0); if(ret <= 0) { sprintf(xorriso->info_text, "-check_media: Cannot reset options due to lack of resources"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); ret= -1; goto ex; } Checkmediajob_copy(default_job, job, 0); Checkmediajob_destroy(&default_job, 0); xorriso->check_media_bad_limit= Xorriso_read_quality_invaliD; } else if(strncmp(argv[i], "retry=", 6) == 0) { if(strcmp(argv[i] + 6, "on") == 0) job->retry= 1; else if(strcmp(argv[i] + 6, "off") == 0) job->retry= -1; else if(strcmp(argv[i] + 6, "default") == 0) job->retry= 0; else goto unknown_value; } else if(strncmp(argv[i], "sector_map=", 11) == 0) { ret= Sfile_str(job->sector_map_path, argv[i] + 11, 0); if(ret <= 0) goto ex; } else if(strncmp(argv[i], "slow_limit=", 11) == 0) { sscanf(argv[i] + 11, "%lf", &(job->slow_threshold_seq)); } else if(strncmp(argv[i], "time_limit=", 11) == 0 || strncmp(argv[i], "item_limit=", 11) == 0 ) { num= -1; sscanf(argv[i] + 11, "%lf", &num); if(num > 0x7fffffff || num < 0) num= -1; if(strncmp(argv[i], "time_limit=", 11) == 0) job->time_limit= num; else job->item_limit= num; } else if(strncmp(argv[i], "use=", 4) == 0) { if(strcmp(argv[i] + 4, "outdev") == 0) job->use_dev= 1; else if(strcmp(argv[i] + 4, "indev") == 0) job->use_dev= 0; else if(strcmp(argv[i] + 4, "sector_map") == 0) job->use_dev= 2; else goto unknown_value; } else if(strncmp(argv[i], "what=", 5) == 0) { if(strcmp(argv[i]+5, "tracks") == 0) job->mode= 0; else if(strcmp(argv[i]+5, "image")== 0) job->mode= 1; else if(strcmp(argv[i]+5, "disc")== 0) job->mode= 2; else { unknown_value:; sprintf(xorriso->info_text, "-check_media: Unknown value with option %s", argv[i]); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; bad_value:; sprintf(xorriso->info_text, "-check_media: Unsuitable value with option %s", argv[i]); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } else { sprintf(xorriso->info_text, "-check_media: Unknown option '%s'", argv[i]); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } ret= 1; ex:; return(ret); } /* @param report Buffer of at least 10*SfileadrL @param flag bit0= only report non-default settings @return <=0 error , 1 ok , 2 with bit0: every option is on default setting */ int Xorriso_check_media_list_job(struct XorrisO *xorriso, struct CheckmediajoB *job, char *report, int flag) { int all, ret; char *default_report= NULL, quality_name[80]; struct CheckmediajoB *dflt= NULL; Xorriso_alloc_meM(default_report, char, 161); all= !(flag&1); report[0]= 0; ret= Checkmediajob_new(&dflt, 0); if(ret <= 0) {ret= -1; goto ex;} sprintf(report, "-check_media_defaults"); if(!all) strcat(report, " reset=now"); if(all || job->use_dev != dflt->use_dev) sprintf(report + strlen(report), " use=%s", job->use_dev == 1 ? "outdev" : job->use_dev == 2 ? "sector_map" : "indev"); if(all || job->mode != dflt->mode) sprintf(report + strlen(report), " what=%s", job->mode == 1 ? "disc" : "tracks"); if(all || job->min_lba != dflt->min_lba) sprintf(report + strlen(report), " min_lba=%.f", (double) job->min_lba); if(all || job->max_lba != dflt->max_lba) sprintf(report + strlen(report), " max_lba=%.f", (double) job->max_lba); if(all || job->retry != dflt->retry) sprintf(report + strlen(report), " retry=%s", job->retry == 1 ? "on" : job->retry == -1 ? "off" : "default"); if(all || job->time_limit != dflt->time_limit) sprintf(report + strlen(report), " time_limit=%d", job->time_limit); if(all || job->item_limit != dflt->item_limit) sprintf(report + strlen(report), " item_limit=%d", job->item_limit); if(all || strcmp(job->abort_file_path, dflt->abort_file_path)) { strcat(report, " abort_file="); Text_shellsafe(job->abort_file_path, report + strlen(report), 0); } if(strlen(report) > 4 * SfileadrL) {ret= 0; goto ex;} if(all || strcmp(job->data_to_path, dflt->data_to_path)) { strcat(report, " data_to="); Text_shellsafe(job->data_to_path, report + strlen(report), 0); } if(strlen(report) > 4 * SfileadrL) {ret= 0; goto ex;} if(all || strcmp(job->sector_map_path, dflt->sector_map_path)) { strcat(report, " sector_map="); Text_shellsafe(job->sector_map_path, report + strlen(report), 0); } if(all || job->map_with_volid != dflt->map_with_volid) sprintf(report + strlen(report), " map_with_volid=%s", job->map_with_volid == 1 ? "on" : "off"); if(all || job->patch_lba0 != dflt->patch_lba0) { sprintf(report + strlen(report), " patch_lba0="); if(job->patch_lba0 == 0) sprintf(report + strlen(report), "off"); else if(job->patch_lba0_msc1 >= 0) sprintf(report + strlen(report), "%.f%s", (double) job->patch_lba0_msc1, job->patch_lba0 == 2 ? ":force" : ""); else sprintf(report + strlen(report), "%s", job->patch_lba0 == 2 ? "force" : "on"); } if(all || job->report_mode != dflt->report_mode) sprintf(report + strlen(report), " report=%s", job->report_mode == 0 ? "blocks" : job->report_mode == 1 ? "files" : "blocks_files"); if(all || job->slow_threshold_seq != dflt->slow_threshold_seq) sprintf(report + strlen(report), " slow_limit=%f", job->slow_threshold_seq); if(all || xorriso->check_media_bad_limit != Xorriso_read_quality_invaliD) sprintf(report + strlen(report), " bad_limit=%s", Spotlist__quality_name(xorriso->check_media_bad_limit, quality_name, Xorriso_read_quality_invaliD, 0) + 2); if(all || job->min_block_size != dflt->min_block_size) sprintf(report + strlen(report), " chunk_size=%ds", job->min_block_size); if(all || strcmp(job->event_severity, "ALL") != 0) sprintf(report + strlen(report), " event=%s", job->event_severity); if(strlen(report) > 4 * SfileadrL) {ret= 0; goto ex;} ret= 1; ex:; strcat(report, " "); strcat(report, xorriso->list_delimiter); Checkmediajob_destroy(&dflt, 0); if(default_report != NULL) { sprintf(default_report, "-check_media_defaults reset=now %s", xorriso->list_delimiter); if(ret > 0 && strcmp(report, default_report) == 0) ret= 2; } Xorriso_free_meM(default_report); return(ret); } int Xorriso_sectormap_to_spotlist(struct XorrisO *xorriso, struct CheckmediajoB *job, struct SpotlisT **spotlist, int flag) { struct SectorbitmaP *map; int ret, value, old_value= -1; off_t i, sectors, sector_size, old_start= -1; map= job->sector_map; if(map == NULL) return(-1); ret= Spotlist_new(spotlist, 0); if(ret <= 0) {ret= -1; goto ex;} Sectorbitmap_get_layout(map, §ors, §or_size, 0); sector_size/= 2048; if(job->max_lba >= 0) sectors= (job->max_lba + 1) / sector_size; i= 0; if(job->min_lba >= 0) i= job->min_lba / sector_size; for(; i < sectors; i++) { value= Sectorbitmap_is_set(map, i, 0); if(value == old_value) continue; if(old_value >= 0) { ret= Spotlist_add_item(*spotlist, old_start, i * sector_size - old_start, (old_value ? Xorriso_read_quality_valiD : Xorriso_read_quality_invaliD), 0); if(ret <= 0) goto ex; if(job->item_limit > 0 && Spotlist_count(*spotlist, 0) + 1 >= job->item_limit) { sprintf(xorriso->info_text, "-check_media: Reached item_limit=%d", job->item_limit); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); if(sectors - i > 1) { ret= Spotlist_add_item(*spotlist, i * sector_size, (sectors - i - 1) * sector_size, Xorriso_read_quality_untesteD, 0); if(ret <= 0) goto ex; } ret= 2; goto ex; } } old_value= value; old_start= i * sector_size; } if(old_value >= 0) { ret= Spotlist_add_item(*spotlist, old_start, i * sector_size - old_start, (old_value ? Xorriso_read_quality_valiD : Xorriso_read_quality_invaliD), 0); if(ret <= 0) goto ex; } ret= 1; ex:; if(ret <= 0) Spotlist_destroy(spotlist, 0); return(ret); } /* @param flag bit0= mark untested areas as valid bit1= leave untested areas as they are */ int Xorriso_spotlist_to_sectormap(struct XorrisO *xorriso, struct SpotlisT *spotlist, off_t read_chunk, struct SectorbitmaP **map, int flag) { struct SectorbitmaP *m; int valid; off_t map_sectors= -1, map_sector_size= -1; int replace_map= 0, quality, ret, pass; off_t list_sectors, list_blocks, sector_size, sector_blocks; off_t count, i, lba, blocks; sector_size= Spotlist_sector_size(spotlist, read_chunk, 0); sector_blocks= sector_size / 2048; if(*map != NULL) Sectorbitmap_get_layout(*map, &map_sectors, &map_sector_size, 0); count= Spotlist_count(spotlist, 0); list_blocks= Spotlist_block_count(spotlist, 0); /* >>> ??? insist in list_blocks % sector_blocks == 0 */ list_sectors= list_blocks / sector_blocks; if(list_sectors * sector_blocks < list_blocks) list_sectors++; if(*map != NULL && map_sectors * (map_sector_size / 2048) >= list_blocks && map_sector_size == sector_size) m= *map; else { if(*map != NULL) { if((*map)->sectors * (*map)->sector_size > list_sectors * sector_size) list_sectors= (*map)->sectors * (*map)->sector_size / sector_size + 1; } ret= Sectorbitmap_new(&m, list_sectors, sector_size, 0); if(ret <= 0) return(-1); replace_map= 1; if(*map != NULL) { ret= Sectorbitmap_copy(*map, m, 0); if(ret <= 0) { Sectorbitmap_destroy(&m, 0); return(0); } } } count= Spotlist_count(spotlist, 0); /* first set good bits, then eventually override by bad bits */ for(pass= 0; pass < 2; pass++) { for(i= 0; i < count; i++) { ret= Spotlist_get_item(spotlist, i, &lba, &blocks, &quality, 0); if(ret <= 0) continue; valid= quality > xorriso->check_media_bad_limit; if(quality == Xorriso_read_quality_untesteD && (flag & 1)) valid= 1; if(quality == Xorriso_read_quality_untesteD && (flag & 2)) continue; if(pass == 0 && !valid) continue; else if(pass == 1 && valid) continue; Sectorbitmap_set_range(m, lba / sector_blocks, blocks / sector_blocks, valid); } } if(replace_map) { Sectorbitmap_destroy(map, 0); *map= m; } return(1); } int Xorriso_open_job_data_to(struct XorrisO *xorriso, struct CheckmediajoB *job, int flag) { if(job->data_to_path[0] == 0) return(2); if(strcmp(job->data_to_path, "-") == 0) { job->data_to_fd= 1; } else { job->data_to_fd= open(job->data_to_path, O_RDWR | O_CREAT | O_BINARY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); } if(job->data_to_fd == -1) { sprintf(xorriso->info_text, "Cannot open path "); Text_shellsafe(job->data_to_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); return(0); } return(1); } int Xorriso_update_in_sector_map(struct XorrisO *xorriso, struct SpotlisT *spotlist, off_t read_chunk, struct CheckmediajoB *job, int flag) { int ret; off_t sectors, sector_size, sector_blocks; struct SectorbitmaP *map; Sectorbitmap_destroy(&(xorriso->in_sector_map), 0); if(job->use_dev == 1) return(1); map= job->sector_map; sectors= Spotlist_block_count(spotlist, 0); if(sectors <= 0) return(0); sector_size= Spotlist_sector_size(spotlist, read_chunk, 0); sector_blocks= sector_size / 2048; if(sector_blocks > 1) sectors= sectors / sector_blocks + !!(sectors % sector_blocks); ret= Sectorbitmap_new(&(xorriso->in_sector_map), sectors, sector_size, 0); if(ret <= 0) return(ret); if(map != NULL) Sectorbitmap_copy(map, xorriso->in_sector_map, 0); ret= Xorriso_spotlist_to_sectormap(xorriso, spotlist, read_chunk, &(xorriso->in_sector_map), 1); return(ret); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of classes SpotlistiteM, SpotlisT, SectorbitmaP, CheckmediajoB which represent media checks and their outcome. */ #ifndef Xorriso_pvt_check_includeD #define Xorriso_pvt_check_includeD yes /* For ssize_t */ #include <unistd.h> struct SpotlisT; /* List of intervals with different read qualities */ struct CheckmediajoB; /* Parameters for Xorriso_check_media() */ int Xorriso_check_media_setup_job(struct XorrisO *xorriso, struct CheckmediajoB *job, char **argv, int old_idx, int end_idx, int flag); int Xorriso_sectormap_to_spotlist(struct XorrisO *xorriso, struct CheckmediajoB *job, struct SpotlisT **spotlist, int flag); /* @param flag bit0= mark untested areas as valid */ int Xorriso_spotlist_to_sectormap(struct XorrisO *xorriso, struct SpotlisT *spotlist, off_t read_chunk, struct SectorbitmaP **map, int flag); /* Opens the -check_media data copy in for reading and writing */ int Xorriso_open_job_data_to(struct XorrisO *xorriso, struct CheckmediajoB *job, int flag); /* @param report Buffer of at least 10*SfileadrL @param flag bit0= only report non-default settings @return <=0 error , 1 ok , 2 with bit0: every option is on default setting */ int Xorriso_check_media_list_job(struct XorrisO *xorriso, struct CheckmediajoB *job, char *report, int flag); int Xorriso_update_in_sector_map(struct XorrisO *xorriso, struct SpotlisT *spotlist, off_t read_chunk, struct CheckmediajoB *job, int flag); /* Distiniction between valid and invalid sectors */ struct SectorbitmaP { off_t sectors; off_t sector_size; unsigned char *map; ssize_t map_size; }; int Spotlist_new(struct SpotlisT **o, int flag); int Spotlist_destroy(struct SpotlisT **o, int flag); int Spotlist_add_item(struct SpotlisT *o, off_t start_lba, off_t blocks, int quality, int flag); off_t Spotlist_count(struct SpotlisT *o, int flag); off_t Spotlist_block_count(struct SpotlisT *o, int flag); off_t Spotlist_sector_size(struct SpotlisT *o, off_t read_chunk, int flag); int Spotlist_get_item(struct SpotlisT *o, off_t idx, off_t *start_lba, off_t *blocks, int *quality, int flag); char *Spotlist__quality_name(int quality, char name[80], int bad_limit, int flag); #define Xorriso_read_quality_gooD 0x7fffffff #define Xorriso_read_quality_md5_matcH 0x70000000 #define Xorriso_read_quality_sloW 0x60000000 #define Xorriso_read_quality_partiaL 0x50000000 #define Xorriso_read_quality_valiD 0x40000000 #define Xorriso_read_quality_untesteD 0x3fffffff #define Xorriso_read_quality_md5_mismatcH 0x38000000 #define Xorriso_read_quality_invaliD 0x30000000 #define Xorriso_read_quality_tao_enD 0x20000000 #define Xorriso_read_quality_off_tracK 0x10000000 #define Xorriso_read_quality_unreadablE 0x00000000 struct CheckmediajoB { int use_dev; /* 0= use indev , 1= use outdev , 2= use sector map*/ off_t min_lba; /* if >=0 : begin checking at this address */ off_t max_lba; /* if >=0 : read up to this address, else use mode */ int min_block_size; /* granularity desired by user */ int async_chunks; /* >= 2 : run MD5 thread, use given number of chunks else : synchronous */ int mode; /* 0= track by track 1= single sweep over libisoburn medium capacity 2= single sweep over libburn medium capacity */ time_t start_time; int time_limit; /* Number of seconds after which to abort */ int item_limit; /* Maximum number of medium check list items as result */ char abort_file_path[SfileadrL]; char data_to_path[SfileadrL]; int data_to_fd; off_t data_to_offset; /* usually 0 with image copy, negative with file copy */ off_t data_to_limit; /* used with file copy */ int data_to_skip; /* number of bytes to skip on writing. < 2048 */ int patch_lba0; off_t patch_lba0_msc1; char sector_map_path[SfileadrL]; struct SectorbitmaP *sector_map; int map_with_volid; /* 0=add quick toc to map file, 1=read ISO heads for toc */ int retry; /* -1= only try full read_chunk, 1=retry with 2k blocks 0= retry with CD, full chunk else */ int report_mode; /* 0= print MCL items 1= print damaged files */ char event_severity[20]; /* If not "ALL": trigger event of given severity at the end of a check job if bad blocks were discovered. */ double slow_threshold_seq; /* Time limit in seconds for the decision whether a read operation is considered slow. This does not apply to thr first read of an interval. */ }; int Checkmediajob_new(struct CheckmediajoB **o, int flag); int Checkmediajob_destroy(struct CheckmediajoB **o, int flag); int Checkmediajob_copy(struct CheckmediajoB *from, struct CheckmediajoB *to, int flag); int Sectorbitmap_new(struct SectorbitmaP **o, off_t sectors, off_t sector_size, int flag); int Sectorbitmap_destroy(struct SectorbitmaP **o, int flag); int Sectorbitmap_from_file(struct SectorbitmaP **o, char *path, char *msg, int *os_errno, int flag); int Sectorbitmap_to_file(struct SectorbitmaP *o, char *path, char *info, char *msg, int *os_errno, int flag); int Sectorbitmap_set(struct SectorbitmaP *o, off_t sector, int flag); int Sectorbitmap_set_range(struct SectorbitmaP *o, off_t start_sector, off_t sectors, int flag); int Sectorbitmap_is_set(struct SectorbitmaP *o, off_t sector, int flag); int Sectorbitmap_bytes_are_set(struct SectorbitmaP *o, off_t start_byte, off_t end_byte, int flag); int Sectorbitmap_get_layout(struct SectorbitmaP *o, off_t *sectors, off_t *sector_size, int flag); int Sectorbitmap_copy(struct SectorbitmaP *from, struct SectorbitmaP *to, int flag); int Sectorbitmap_clone(struct SectorbitmaP *from, struct SectorbitmaP **clone, int flag); #endif /* ! Xorriso_pvt_check_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of actions which compare or update files between disk filesystem and ISO filesystem. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <fcntl.h> #include <errno.h> #include <pwd.h> #include <grp.h> /* O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" /* @param result Bitfield indicationg type of mismatch bit11= cannot open regular disk file bit12= cannot open iso file bit13= early eof of disk file bit14= early eof of iso file bit15= content bytes differ @param flag bit0= mtimes of both file objects are equal bit29= do not issue pacifier messages bit31= do not issue result messages @return >0 ok , <=0 error */ int Xorriso_compare_2_contents(struct XorrisO *xorriso, char *common_adr, char *disk_adr, off_t disk_size, off_t offset, off_t bytes, char *iso_adr, off_t iso_size, int *result, int flag) { int fd1= -1, ret, r1, r2, done, wanted, i, was_error= 0, use_md5= 0; void *stream2= NULL; off_t r1count= 0, r2count= 0, diffcount= 0, first_diff= -1; char *respt, *buf1= NULL, *buf2= NULL, offset_text[80]; char disk_md5[16], iso_md5[16]; void *ctx= NULL; int buf_size= 32 * 1024; double dcount; Xorriso_alloc_meM(buf1, char, buf_size); Xorriso_alloc_meM(buf2, char, buf_size); respt= xorriso->result_line; fd1= open(disk_adr, O_RDONLY | O_BINARY); if(fd1==-1) { sprintf(respt, "- %s (DISK) : cannot open() : %s\n", disk_adr, strerror(errno)); cannot_address:; if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= 2048; {ret= 0; goto ex;} } if(offset>0) if(lseek(fd1, offset, SEEK_SET)==-1) { sprintf(respt, "- %s (DISK) : cannot lseek(%.f) : %s\n", disk_adr, (double) offset, strerror(errno)); close(fd1); goto cannot_address; } if(xorriso->do_md5 & 16) { use_md5= 1; ret= Xorriso_is_plain_image_file(xorriso, NULL, iso_adr, 0); if(ret <= 0) ret= 0; /* (reverse) filtered files are likely not to match their MD5 */ else ret= Xorriso_get_md5(xorriso, NULL, iso_adr, iso_md5, 1); if(ret <= 0) use_md5= 0; else { ret= Xorriso_md5_start(xorriso, &ctx, 0); if(ret <= 0) use_md5= 0; } } if (! use_md5) { ret= Xorriso_iso_file_open(xorriso, iso_adr, NULL, &stream2, 0); if(ret<=0) { sprintf(respt, "- %s (ISO) : cannot open() file in ISO image\n",iso_adr); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); close(fd1); (*result)|= 4096; {ret= 0; goto ex;} } } done= 0; while(!done) { wanted= buf_size; if(r1count+offset+wanted>disk_size) wanted= disk_size-r1count-offset; if(r1count+wanted>bytes) wanted= bytes-r1count; r1= 0; while(wanted>0) { ret= read(fd1, buf1, wanted); if(ret<=0) break; wanted-= ret; r1+= ret; } wanted= buf_size; if(r2count+wanted>iso_size) wanted= iso_size-r2count; /* if(r2count+wanted>bytes) wanted= bytes-r2count; */ if(use_md5) r2= r1; else if(wanted>0) r2= Xorriso_iso_file_read(xorriso, stream2, buf2, wanted, 0); else r2= 0; if(r1<0 || r2<0) was_error= 1; if(r1<=0 && r2<=0) break; if(r1<=0) { if(r1<0) r1= 0; if(disk_size > r1count + r1 + offset) { sprintf(respt, "- %s (DISK) : early EOF after %.f bytes\n", disk_adr, (double) r1count); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= 8196; } (*result)|= (1<<15); } r1count+= r1; if(r2<=0 || r2<r1) { if(r2<0) r2= 0; if(iso_size > r2count + r2) { sprintf(respt, "- %s (ISO) : early EOF after %.f bytes\n", iso_adr, (double) r2count); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= (1<<14); } (*result)|= (1<<15); done= 1; } if(r2>r1) { if(disk_size > r1count + r1 + offset) { sprintf(respt, "- %s (DISK) : early EOF after %.f bytes\n", disk_adr, (double) r1count); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= 8196; } (*result)|= (1<<15); done= 1; } r2count+= r2; if(r1>r2) r1= r2; if(use_md5) { Xorriso_md5_compute(xorriso, ctx, buf1, r1, 0); } else { for(i= 0; i<r1; i++) { if(buf1[i]!=buf2[i]) { if(first_diff<0) first_diff= r1count - r1 + i; diffcount++; } } } if(!(flag&(1<<29))) { xorriso->pacifier_count+= r1; xorriso->pacifier_byte_count+= r1; if(flag&(1u<<31)) Xorriso_pacifier_callback(xorriso, "content bytes read", xorriso->pacifier_count, 0, "", 8); else Xorriso_pacifier_callback(xorriso, "bytes", xorriso->pacifier_count, 0, "", 8 | 1<<6); } } if(use_md5) { ret= Xorriso_md5_end(xorriso, &ctx, disk_md5, 0); if(ret <= 0) { *result |= (1 << 15); ret= -1; goto ex; } for(i= 0; i < 16; i++) if(iso_md5[i] != disk_md5[i]) break; if(i < 16 ) { offset_text[0]= 0; if(offset>0) sprintf(offset_text, "%.f+", (double) offset); sprintf(respt, "%s %s : differs by MD5 sums.\n", common_adr, (flag&1 ? "CONTENT": "content")); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= (1<<15); } } else if(diffcount>0 || r1count!=r2count) { if(first_diff<0) first_diff= (r1count>r2count ? r2count : r1count); offset_text[0]= 0; if(offset>0) sprintf(offset_text, "%.f+", (double) offset); if(r1count > r2count) dcount= diffcount + (r1count - r2count); else dcount= diffcount + (r2count - r1count); sprintf(respt, "%s %s : differs by at least %.f bytes. First at %s%.f\n", common_adr, (flag&1 ? "CONTENT": "content"), dcount, offset_text, (double) first_diff); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= (1<<15); } if(fd1!=-1) close(fd1); if(! use_md5) Xorriso_iso_file_close(xorriso, &stream2, 0); if(was_error) {ret= -1; goto ex;} ret= 1; ex:; if(ctx != NULL) Xorriso_md5_end(xorriso, &ctx, disk_md5, 0); Xorriso_free_meM(buf1); Xorriso_free_meM(buf2); return(ret); } /* @param result Bitfield indicationg type of mismatch bit0= disk_adr not existing bit1= iso_adr not existing bit2= access permissions bit3= file type bit4= user id bit5= group id bit6= minor, major with device file bit7= size bit8= mtime bit9= atime bit10= ctime bit11= cannot open regular disk file bit12= cannot open iso file bit13= early eof of disk file bit14= early eof of iso file bit15= content bytes differ bit16= symbolic link on disk pointing to dir, dir in iso bit17= file chunks detected and compared bit18= incomplete chunk collection encountered bit19= ACL differs (this condition sets also bit2) bit20= xattr differ bit21= mismatch of recorded dev,inode bit22= no recorded dev,inode found in node bit23= timestamps younger than xorriso->isofs_st_in bit24= hardlink split bit25= hardlink fusion bit26= Linux file attribute mismatch @param flag bit0= compare atime bit1= compare ctime bit2= check only existence of both file objects count one or both missing as "difference" bit26= do not issue message about missing disk file bit27= for Xorriso_path_is_excluded(): bit0 bit28= examine eventual disk_path link target rather than link bit29= do not issue pacifier messages bit30= omit adr_common_tail in report messages bit31= do not issue result messages @return 1=files match properly , 0=difference detected , -1=error */ int Xorriso_compare_2_files(struct XorrisO *xorriso, char *disk_adr, char *iso_adr, char *adr_common_tail, int *result, int flag) { struct stat s1, s2, stbuf; int ret, missing= 0, is_split= 0, i, was_error= 0, diff_count= 0; int content_shortcut= 0, mask, max_bit; char *respt; char *a= NULL; char ttx1[40], ttx2[40], *lfa_text= NULL; char *a1_acl= NULL, *a2_acl= NULL, *d1_acl= NULL, *d2_acl= NULL; char *attrlist1= NULL, *attrlist2= NULL; struct SplitparT *split_parts= NULL; int split_count= 0; time_t stamp; uint64_t lfa_flags1, lfa_flags2; char *part_path= NULL, *part_name; int partno, total_parts= 0; off_t offset, bytes, total_bytes; Xorriso_alloc_meM(a, char, 5*SfileadrL); Xorriso_alloc_meM(part_path, char, SfileadrL); *result= 0; respt= xorriso->result_line; if(!(xorriso->disk_excl_mode&8)) { ret= Xorriso_path_is_excluded(xorriso, disk_adr, 2 | !!(flag&(1<<27))); if(ret>0) { strcpy(respt, "? "); Text_shellsafe(disk_adr, respt, 1); sprintf(respt + strlen(respt), " (DISK) : excluded by %s\n", (ret==1 ? "-not_paths" : "-not_leaf")); if(! (flag & ((1u << 31) | (1 << 26)))) Xorriso_result(xorriso,0); missing= 1; (*result)|= 1; } } if(!missing) { if(flag&(1<<28)) ret= stat(disk_adr, &s1); else ret= lstat(disk_adr, &s1); if(ret==-1) { strcpy(respt, "? "); Text_shellsafe(disk_adr, respt, 1); sprintf(respt + strlen(respt), " (DISK) : cannot lstat() : %s\n", strerror(errno)); if(! (flag & ((1u << 31) | (1 << 26)))) Xorriso_result(xorriso,0); missing= 1; (*result)|= 1; } } if(missing) strcpy(a, "?"); else strcpy(a, Ftypetxt(s1.st_mode, 1)); strcat(a, " "); if(adr_common_tail[0]) Text_shellsafe(adr_common_tail, a, 1); else { Text_shellsafe(disk_adr, a+strlen(a), 0); strcat(a, " (DISK)"); /* strcat(a, "'.'"); */ } strcat(a, " :"); if(flag&(1<<30)) a[0]= 0; ret= Xorriso_iso_lstat(xorriso, iso_adr, &s2, 0); if(ret<0) { strcpy(respt, "? "); Text_shellsafe(iso_adr, respt, 1); strcat(respt, " (ISO) : cannot find this file in ISO image\n"); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); missing= 1; (*result)|= 2; } if((flag&4)||missing) {ret= !missing; goto ex;} /* Splitfile parts */ if((S_ISREG(s1.st_mode) || S_ISBLK(s1.st_mode)) && S_ISDIR(s2.st_mode)) { is_split= Xorriso_identify_split(xorriso, iso_adr, NULL, &split_parts, &split_count, &s2, 0); if(is_split>0) (*result)|= (1<<17); else is_split= 0; } /* Attributes */ if(s1.st_mode != s2.st_mode) { if((s1.st_mode&~S_IFMT)!=(s2.st_mode&~S_IFMT)) { sprintf(respt, "%s st_mode : %7.7o <> %7.7o\n", a, (unsigned int) (s1.st_mode & ~S_IFMT), (unsigned int) (s2.st_mode & ~S_IFMT)); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= 4; } if((s1.st_mode&S_IFMT)!=(s2.st_mode&S_IFMT)) { sprintf(respt, "%s type : %s <> %s\n", a, Ftypetxt(s1.st_mode, 0), Ftypetxt(s2.st_mode, 0)); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= 8; if((s1.st_mode&S_IFMT) == S_IFLNK) { /* check whether link target type matches */ ret= stat(disk_adr, &stbuf); if(ret!=-1) if(S_ISDIR(stbuf.st_mode) && S_ISDIR(s2.st_mode)) (*result)|= (1<<16); } } } /* ACL */ if(xorriso->do_aaip & 3) { Xorriso_local_getfacl(xorriso, disk_adr, &a1_acl, 16 | ((flag & (1 << 28)) >> 23)); if(S_ISDIR(s1.st_mode)) Xorriso_local_getfacl(xorriso, disk_adr, &d1_acl, 1); ret= Xorriso_getfacl(xorriso, NULL, iso_adr, &a2_acl, 1 | 4 | 16); if(ret < 0) goto ex; if(S_ISDIR(s1.st_mode)) { ret= Xorriso_getfacl(xorriso, NULL, iso_adr, &d2_acl, 1 | 8); if(ret < 0) goto ex; } ret= Compare_text_lines(a1_acl, a2_acl, &diff_count, 0); if(ret < 0) goto ex; if(ret == 0) (*result)|= 4 | (1 << 19); ret= Compare_text_lines(d1_acl, d2_acl, &diff_count, 1); if(ret < 0) goto ex; if(ret == 0) (*result)|= 4 | (1 << 19); if((*result) & (1 << 19)) { sprintf(respt, "%s ACL : %d difference%s\n", a, diff_count, diff_count == 1 ? "" : "s"); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); } } /* xattr */ if(xorriso->do_aaip & 12) { ret= Xorriso_getfattr(xorriso, NULL, disk_adr, &attrlist1, 1 | 2 | ((flag & (1 << 28)) >> 23)); if(ret < 0) goto ex; ret= Xorriso_getfattr(xorriso, NULL, iso_adr, &attrlist2, 1); if(ret < 0) goto ex; ret= Compare_text_lines(attrlist1, attrlist2, &diff_count, 0); if(ret < 0) goto ex; if(ret == 0) { (*result)|= (1 << 20); sprintf(respt, "%s xattr : %d difference%s\n", a, diff_count, diff_count == 1 ? "" : "s"); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); } } /* lfa_flags */ if(xorriso->do_aaip & (3 << 11)) { ret= Xorriso_get_lfa_flags(xorriso, NULL, disk_adr, &lfa_flags1, &max_bit, 2 | ((flag & (1 << 28)) >> 23) | ((!!(xorriso->do_aaip & (1 << 15))) << 7)); if(ret < 0) goto ex; ret= Xorriso_get_lfa_flags(xorriso, NULL, iso_adr, &lfa_flags2, &max_bit, 0); if(ret < 0) goto ex; if(lfa_flags1 != lfa_flags2) { (*result)|= (1 << 26); Xorriso_encode_lfa_flags(xorriso, lfa_flags1, &lfa_text, 1); if(lfa_text != NULL) { sprintf(respt, "%s lsattr : %s <> ", a, lfa_text); free(lfa_text); } Xorriso_encode_lfa_flags(xorriso, lfa_flags2, &lfa_text, 1); if(lfa_text != NULL) { sprintf(respt + strlen(respt), "%s", lfa_text); free(lfa_text); } Xorriso_encode_lfa_flags(xorriso, lfa_flags1 ^ lfa_flags2, &lfa_text, 0); if(lfa_text != NULL) { sprintf(respt + strlen(respt), " diff= %s", lfa_text); free(lfa_text); } strcat(respt, "\n"); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); } } if(s1.st_uid != s2.st_uid) { sprintf(respt, "%s st_uid : %lu <> %lu\n", a, (unsigned long) s1.st_uid, (unsigned long) s2.st_uid); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= 16; } if(s1.st_gid != s2.st_gid) { sprintf(respt, "%s st_gid : %lu <> %lu\n", a, (unsigned long) s1.st_gid, (unsigned long) s2.st_gid); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= 32; } if((S_ISCHR(s1.st_mode) && S_ISCHR(s2.st_mode)) || (S_ISBLK(s1.st_mode) && S_ISBLK(s2.st_mode))) { if(s1.st_rdev != s2.st_rdev) { sprintf(respt, "%s %s st_rdev : %lu <> %lu\n", a, (S_ISCHR(s1.st_mode) ? "S_IFCHR" : "S_IFBLK"), (unsigned long) s1.st_rdev, (unsigned long) s1.st_rdev); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= 64; } } if((!(xorriso->do_aaip & 32)) && S_ISREG(s2.st_mode) && s1.st_size != s2.st_size) { sprintf(respt, "%s st_size : %.f <> %.f diff= %.f\n", a, (double) s1.st_size, (double) s2.st_size, ((double) s1.st_size) - (double) s2.st_size); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= 128; } if(s1.st_mtime != s2.st_mtime) { sprintf(respt, "%s st_mtime : %s <> %s diff= %.f s\n", a, Ftimetxt(s1.st_mtime, ttx1, 0), Ftimetxt(s2.st_mtime, ttx2, 0), ((double) s1.st_mtime) - (double) s2.st_mtime); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= 256; } if(flag&1) { if(s1.st_atime != s2.st_atime) { sprintf(respt, "%s st_atime : %s <> %s diff= %.f s\n", a, Ftimetxt(s1.st_atime, ttx1, 0), Ftimetxt(s2.st_atime, ttx2, 0), ((double) s1.st_atime) - (double) s2.st_atime); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= 512; } } if(flag&2) { if(s1.st_ctime != s2.st_ctime) { sprintf(respt, "%s st_ctime : %s <> %s diff= %.f s\n", a, Ftimetxt(s1.st_ctime, ttx1, 0), Ftimetxt(s2.st_ctime, ttx2, 0), ((double) s1.st_ctime) - (double) s2.st_ctime); if(!(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= 1024; } } if(xorriso->isofs_st_in > 0 && (xorriso->isofs_st_in <= s2.st_mtime || ((flag & 1) && xorriso->isofs_st_in <= s2.st_atime) || ((flag & 2) && xorriso->isofs_st_in <= s2.st_ctime))) (*result)|= 1 << 23; if((xorriso->do_aaip & 32) || !(xorriso->ino_behavior & 2)) { /* dev,inode comparison. For skipping content comparison or for hardlink detection. */ ret= Xorriso_record_dev_inode(xorriso, "", s1.st_dev, s1.st_ino, NULL, iso_adr, 1 | 2 | ((flag & (1 << 28)) >> 23) | (xorriso->do_aaip & 128)); if(ret < 0) { ret= -1; goto ex; } else if(ret == 0) { /* match */ if((xorriso->do_aaip & 64) && S_ISREG(s1.st_mode) && S_ISREG(s2.st_mode)){ if(xorriso->do_aaip & 32) content_shortcut= 1; if((*result) & (8 | 128 | 256 | 512 | 1024 | (1 << 23))) { (*result)|= (1 << 15); /* content bytes differ */ if(((*result) & (1 << 23)) && !((*result) & (8 | 128 | 256 | 512 | 1024))) { sprintf(respt, "%s content : node timestamp younger than image timestamp\n", a); if((xorriso->do_aaip & 32) && !(flag&(1u<<31))) Xorriso_result(xorriso,0); stamp= s2.st_mtime; if((flag & 1) && s2.st_atime >= stamp) stamp= s2.st_atime; if((flag & 2) && s2.st_ctime >= stamp) stamp= s2.st_ctime; sprintf(respt, "%s content : %s > %s diff= %.f s\n", a, Ftimetxt(stamp, ttx1, 3 << 1), Ftimetxt(xorriso->isofs_st_in, ttx2, 3 << 1), ((double) stamp) - (double) xorriso->isofs_st_in); if((xorriso->do_aaip & 32) && !(flag&(1u<<31))) Xorriso_result(xorriso,0); } sprintf(respt, "%s content : assuming inequality due to size or timestamps\n", a); if((xorriso->do_aaip & 32) && !(flag&(1u<<31))) Xorriso_result(xorriso,0); } } } else if(ret == 1) { /* mismatch */ (*result)|= (1 << 21); sprintf(respt, "%s dev_ino : differing\n", a); if((xorriso->do_aaip & 32) && !(flag&(1u<<31))) Xorriso_result(xorriso,0); if((xorriso->do_aaip & 64) && S_ISREG(s1.st_mode) && S_ISREG(s2.st_mode)){ if(xorriso->do_aaip & 32) content_shortcut= 1; (*result)|= (1 << 15); /* content bytes differ */ sprintf(respt, "%s content : assuming inequality after dev_ino mismatch\n", a); if((xorriso->do_aaip & 32) && !(flag&(1u<<31))) Xorriso_result(xorriso,0); } } else { sprintf(respt, "%s dev_ino : no dev_ino stored with image node\n", a); if((xorriso->do_aaip & 32) && !(flag&(1u<<31))) Xorriso_result(xorriso,0); (*result)|= (1 << 22); } } if(S_ISREG(s1.st_mode) && S_ISREG(s2.st_mode) && !content_shortcut) { /* Content */ if(is_split) { for(i= 0; i<split_count; i++) { Splitparts_get(split_parts, i, &part_name, &partno, &total_parts, &offset, &bytes, &total_bytes, 0); strcpy(part_path, iso_adr); if(Sfile_add_to_path(part_path, part_name, 0)<=0) { Xorriso_much_too_long(xorriso, strlen(iso_adr)+strlen(part_name)+1, 2); {ret= -1; goto ex;} } ret= Xorriso_iso_lstat(xorriso, part_path, &stbuf, 0); if(ret<0) continue; ret= Xorriso_compare_2_contents(xorriso, a, disk_adr, s1.st_size, offset, bytes, part_path, stbuf.st_size, result, (s1.st_mtime==s2.st_mtime) | (flag&((1<<29)|(1u<<31)))); if(ret<0) was_error= 1; } if(total_parts>0 && split_count!=total_parts) { sprintf(xorriso->info_text, "- %s/* (ISO) : Not all split parts present (%d of %d)\n", iso_adr, split_count, total_parts); if(!(flag&(1u<<31))) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 1); (*result)|= 1<<18; } } else { ret= Xorriso_compare_2_contents(xorriso, a, disk_adr, s1.st_size, (off_t) 0, s1.st_size, iso_adr, s2.st_size, result, (s1.st_mtime==s2.st_mtime) | (flag&((1<<29)|(1u<<31)))); if(ret<0) was_error= 1; } } if(was_error) ret= -1; else { mask= ~((1 << 17) | (1 << 18) | (1 << 22) | (1 << 23)); if(xorriso->do_aaip & 32) mask|= 1 << 22; ret= (((*result) & mask)==0); } ex:; if(split_parts!=NULL) Splitparts_destroy(&split_parts, split_count, 0); Xorriso_local_getfacl(xorriso, disk_adr, &a1_acl, 1 << 15); Xorriso_local_getfacl(xorriso, disk_adr, &d1_acl, 1 << 15); if(a2_acl != NULL) free(a2_acl); if(d2_acl != NULL) free(d2_acl); if(attrlist1 != NULL) free(attrlist1); if(attrlist2 != NULL) free(attrlist2); Xorriso_free_meM(part_path); Xorriso_free_meM(a); return(ret); } int Xorriso_pfx_disk_path(struct XorrisO *xorriso, char *iso_path, char *iso_prefix, char *disk_prefix, char disk_path[SfileadrL], int flag) { int ret; char *adrc= NULL; Xorriso_alloc_meM(adrc, char, SfileadrL); if(strncmp(iso_path, iso_prefix, strlen(iso_prefix))!=0) {ret= -1; goto ex;} if(strlen(disk_prefix) + strlen(iso_path) - strlen(iso_prefix)+1 >= SfileadrL) {ret= -1; goto ex;} if(iso_path[strlen(iso_prefix)] == '/') strcpy(adrc, iso_path + strlen(iso_prefix) + 1); else strcpy(adrc, iso_path + strlen(iso_prefix)); ret= Xorriso_make_abs_adr(xorriso, disk_prefix, adrc, disk_path, 4 | 8); if(ret <= 0) goto ex; ret= 1; ex:; Xorriso_free_meM(adrc); return(ret); } /* @param boss_iter Opaque handle to be forwarded to actions in ISO image Set to NULL if calling this function from outside ISO world @param flag bit0= update rather than compare bit1= find[ix] is in recursion bit2= update_merge : do not delete but mark visited and found @return <=0 error, 1= ok , 2= iso_path was deleted 3=ok, do not dive into directory (e.g. because it is a split file) */ int Xorriso_find_compare(struct XorrisO *xorriso, void *boss_iter, void *node, char *iso_path, char *iso_prefix, char *disk_prefix, int flag) { int ret, result, uret, follow_links, deleted= 0; char *disk_path= NULL; Xorriso_alloc_meM(disk_path, char, SfileadrL); ret= Xorriso_pfx_disk_path(xorriso, iso_path, iso_prefix, disk_prefix, disk_path, 0); if(ret <= 0) goto ex; /* compare exclusions against disk_path resp. leaf name */ if(xorriso->disk_excl_mode&8) ret= Xorriso_path_is_excluded(xorriso, disk_path, !(flag&2)); else ret= 0; if(ret<0) goto ex; if(ret>0) {ret= 3; goto ex;} follow_links= (xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&2))) <<28; ret= Xorriso_compare_2_files(xorriso, disk_path, iso_path, "", &result, 2 | follow_links | ((!!(flag & 4)) << 26) | ((!(flag&2))<<27) | (((unsigned)(flag&1))<<31)); /* was once: | ((!(flag&1))<<29) */ if(ret<xorriso->find_compare_result) xorriso->find_compare_result= ret; if(flag&1) { if(ret<0) if(Xorriso_eval_problem_status(xorriso, ret, 1|2)<0) goto ex; if(ret > 0) result= 0; uret= Xorriso_update_interpreter(xorriso, boss_iter, node, result, disk_path, iso_path, ((flag & 2) << 1) | ((flag & 4) >> 1)); if(uret<=0) ret= 0; if(uret==2) deleted= 1; } if(ret<0) goto ex; if(deleted) {ret= 2; goto ex;} if(result&(1<<17)) {ret= 3; goto ex;} ex:; Xorriso_free_meM(disk_path); return(ret); } /* @param boss_iter Opaque handle to be forwarded to actions in ISO image Set to NULL if calling this function from outside ISO world @param flag bit0= widen hardlink sibling: Do not call Xorriso_hardlink_update() Overwrite exactly if normal mode would not, else do nothing bit1= do not delete files which are not found under disk_path, but rather mark visited files and mark files which were found. bit2= -follow: this is not a command parameter @return <=0 error, 1= ok , 2= iso_rr_path node object has been deleted , 3= no action taken */ int Xorriso_update_interpreter(struct XorrisO *xorriso, void *boss_iter, void *node, int compare_result, char *disk_path, char *iso_rr_path, int flag) { int ret= 1, deleted= 0, is_split= 0, i, loop_count, late_hardlink_update= 0; struct stat stbuf; struct SplitparT *split_parts= NULL; int split_count= 0; char *part_path= NULL, *part_name; int partno, total_parts, new_total_parts, added_overwrote= 0; off_t offset, bytes, total_bytes, disk_size, first_bytes, du_size; if((compare_result&3)==3) { sprintf(xorriso->info_text, "Missing on disk and in ISO: disk_path "); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 1); xorriso->find_compare_result= -1; ret= 3; goto ex; } Xorriso_alloc_meM(part_path, char, SfileadrL); if((flag & 2) && !(compare_result & 2)) { ret= Xorriso_mark_update_merge(xorriso, iso_rr_path, node, !(compare_result & 1)); if(ret <= 0) goto ex; } if(compare_result == 0) {ret= 1; goto ex;} if(compare_result&((1<<11)|(1<<13))) { if(flag & 1) {ret= 3; goto ex;} /* cannot open regular disk file, early eof of disk file */ sprintf(xorriso->info_text, "Problems with reading disk file "); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 1); xorriso->find_compare_result= -1; ret= 1; goto ex; } xorriso->info_text[0]= 0; is_split= !!(compare_result & (1<<17)); if((!(xorriso->ino_behavior & 2)) && (compare_result & (2 | (3 << 21))) && !(flag & 1)) { if(compare_result & 2) { /* File is not yet in image */ late_hardlink_update= 1; } else { /* Hard link relation has changed resp. was not recorded. */ ret= Xorriso_hardlink_update(xorriso, &compare_result, disk_path, iso_rr_path, (flag & 4) | ((compare_result >> 21) & 2)); if(ret < 0) goto ex; if(ret == 2) {ret= 1; goto ex;} } } if(compare_result&(8|64)) { /* file type, minor+major with device file */ if(flag & 1) {ret= 3; goto ex;} ret= Xorriso_rmi(xorriso, boss_iter, (off_t) 0, iso_rr_path, 1); /* rm_r */ if(ret>0) { deleted= 1; ret= Xorriso_graft_in(xorriso, boss_iter, disk_path, iso_rr_path, (off_t) 0, (off_t) 0, 2|(flag&4)); if(ret <= 0) goto ex; if(flag & 2) { ret= Xorriso_mark_update_merge(xorriso, iso_rr_path, NULL, 1); if(ret <= 0) goto ex; } } sprintf(xorriso->info_text, "Deleted and re-added "); } else if(compare_result&(1)) { delete:; /* disk_adr not existing */ if(!(flag & 2)) { ret= Xorriso_rmi(xorriso, boss_iter, (off_t) 0, iso_rr_path, 1); deleted= 1; sprintf(xorriso->info_text, "Deleted "); } } else if(compare_result&(2|128|(1<<12)|(1<<14)|(1<<15))) { /* iso_adr not existing, size, cannot open iso file, early eof of iso file content bytes differ */ if(flag & 1) {ret= 3; goto ex;} overwrite:; if(is_split) { ret= Xorriso_identify_split(xorriso, iso_rr_path, NULL, &split_parts, &split_count, &stbuf, 0); if(ret<=0) {ret= -1; goto ex;} /* (should not happen) */ ret= lstat(disk_path, &stbuf); if(ret==-1) goto delete; disk_size= stbuf.st_size; Splitparts_get(split_parts, 0, &part_name, &partno, &total_parts, &offset, &first_bytes, &total_bytes, 0); new_total_parts= disk_size/first_bytes; if(disk_size % first_bytes) new_total_parts++; loop_count= split_count; /* If disk file grew over part limit and all parts are present: add new parts */ if(new_total_parts > total_parts && split_count == total_parts) loop_count= new_total_parts; for(i= 0; i<loop_count; i++) { if(i<split_count) { /* Delete old part */ Splitparts_get(split_parts, i, &part_name, &partno, &total_parts, &offset, &bytes, &total_bytes, 0); strcpy(part_path, iso_rr_path); if(Sfile_add_to_path(part_path, part_name, 0)<=0) { Xorriso_much_too_long(xorriso, strlen(iso_rr_path)+strlen(part_path)+1, 2); {ret= -1; goto ex;} } ret= Xorriso_rmi(xorriso, NULL, (off_t) 0, part_path, 1); if(ret<=0) goto ex; deleted= 1; } else { partno= i+1; offset= i*first_bytes; bytes= first_bytes; } if(disk_size<=offset) continue; /* Insert new part */ if(strlen(part_path)+160>SfileadrL) { Xorriso_much_too_long(xorriso, strlen(part_path)+160, 2); ret= 0; goto ex; } Splitpart__compose(part_path+strlen(iso_rr_path)+1, partno, new_total_parts, offset, first_bytes, disk_size, 0); ret= Xorriso_graft_in(xorriso, boss_iter, disk_path, part_path, offset, bytes, 2|(flag&4)|8|128); if(ret<=0) goto ex; } /* Copy file attributes to iso_rr_path, augment r-perms by x-perms */ ret= Xorriso_copy_properties(xorriso, disk_path, iso_rr_path, 2 | 4); if(ret<=0) goto ex; } else { ret= Xorriso_graft_in(xorriso, boss_iter, disk_path, iso_rr_path, (off_t) 0, (off_t) 0, 2|(flag&4)); if(ret>0 && !(compare_result&2)) deleted= 1; } if(late_hardlink_update) { /* Handle eventual hardlink siblings of newly created file */ ret= Xorriso_hardlink_update(xorriso, &compare_result, disk_path, iso_rr_path, 1 | (flag & 4)); if(ret < 0) goto ex; } if(flag & 2) { ret= Xorriso_mark_update_merge(xorriso, iso_rr_path, NULL, 1); if(ret <= 0) goto ex; } if(flag & 1) { sprintf(xorriso->info_text, "Widened hard link "); } else { sprintf(xorriso->info_text, "Added/overwrote "); added_overwrote= 1; } } else if(compare_result & (4|16|32|256|512|1024|(1<<19)|(1<<20)|(1<<22) | (1 << 26))) { /* access permissions, user id, group id, mtime, atime, ctime, ACL, xattr, dev_ino missing, lfa_flags */ if(flag & 1) goto overwrite; if(is_split) { ret= Xorriso_identify_split(xorriso, iso_rr_path, NULL, &split_parts, &split_count, &stbuf, 0); if(ret<=0) {ret= -1; goto ex;} /* (should not happen) */ for(i= 0; i<split_count; i++) { Splitparts_get(split_parts, i, &part_name, &partno, &total_parts, &offset, &bytes, &total_bytes, 0); strcpy(part_path, iso_rr_path); if(Sfile_add_to_path(part_path, part_name, 0)<=0) { Xorriso_much_too_long(xorriso, strlen(iso_rr_path)+strlen(part_path)+1, 2); {ret= -1; goto ex;} } ret= Xorriso_copy_properties(xorriso, disk_path, part_path, 4 * !(compare_result & (1<<21))); /* do not update eventually mismatching dev_ino */ if(ret<=0) goto ex; } /* Copy file attributes to iso_rr_path, augment r-perms by x-perms */ ret= Xorriso_copy_properties(xorriso, disk_path, iso_rr_path, 2 | 4); if(ret<=0) goto ex; } else ret= Xorriso_copy_properties(xorriso, disk_path, iso_rr_path, 4); sprintf(xorriso->info_text, "Adjusted attributes of "); } else if(flag & 1) { goto overwrite; } else ret= 1; if(ret>0 && xorriso->info_text[0]) { Text_shellsafe(iso_rr_path, xorriso->info_text, 1); if(added_overwrote) { ret= Xorriso_iso_lstat(xorriso, iso_rr_path, &stbuf, 0); if(ret == 0 && S_ISREG(stbuf.st_mode)) { strcat(xorriso->info_text, " ("); Sfile_scale((double) stbuf.st_size, xorriso->info_text + strlen(xorriso->info_text), 5, 1e4, 1 | 2); strcat(xorriso->info_text, ")"); } else if (ret == 0 && S_ISDIR(stbuf.st_mode)) { ret= Xorriso_get_dus(xorriso, iso_rr_path, &du_size, (off_t) 0, 0); if(ret > 0 && du_size > 0) { strcat(xorriso->info_text, " ("); Sfile_scale((double) du_size, xorriso->info_text + strlen(xorriso->info_text), 5, 1e4, 1 | 2); strcat(xorriso->info_text, ")"); } } } Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0); } ret= 1; ex:; if(split_parts!=NULL) Splitparts_destroy(&split_parts, split_count, 0); Xorriso_free_meM(part_path); if(ret<=0) return(ret); if(deleted) return(2); return(ret); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2010 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of class DirseQ which crawls along a directory's content list. */ #ifndef Xorriso_pvt_cmp_includeD #define Xorriso_pvt_cmp_includeD yes int Xorriso_compare_2_files(struct XorrisO *xorriso, char *disk_adr, char *iso_adr, char *adr_common_tail, int *result, int flag); int Xorriso_pfx_disk_path(struct XorrisO *xorriso, char *iso_path, char *iso_prefix, char *disk_prefix, char disk_path[SfileadrL], int flag); /* @param boss_iter Opaque handle to be forwarded to actions in ISO image Set to NULL if calling this function from outside ISO world @param node Opaque handle to be forwarded to actions in ISO image Set to NULL if calling this function from outside ISO world @param flag bit0= update rather than compare */ int Xorriso_find_compare(struct XorrisO *xorriso, void *boss_iter, void *node, char *iso_path, char *iso_prefix, char *disk_prefix, int flag); /* @param boss_iter Opaque handle to be forwarded to actions in ISO image Set to NULL if calling this function from outside ISO world @param @node Opaque handle to be forwarded to actions in ISO image Set to NULL if calling this function from outside ISO world */ int Xorriso_update_interpreter(struct XorrisO *xorriso, void *boss_iter, void *node, int compare_result, char *disk_path, char *iso_rr_path, int flag); #endif /* ! Xorriso_pvt_cmp_includeD */ #!/bin/sh # compile_xorriso.sh # Copyright 2005 - 2015 Thomas Schmitt, scdbackup@gmx.net # GPL version 2 or later # # Not intended for general use in production installations ! # Rather use: ./bootstrap ; ./configure ; make # # This is a development tool which expects a special setup of directories # on a Linux system (e.g. SuSE 10.2). # It is to be executed in a common parent of the directories given with # $isofs $isoburn $burn $xorr isofs=./nglibisofs-develop/libisofs/libisofs_libisofs_la- isofs_filter=./nglibisofs-develop/libisofs/filters/libisofs_libisofs_la- burn=./libburn-develop/libburn isoburn=./libisoburn-develop/libisoburn xorr=./libisoburn-develop/xorriso debug_opts="-O2" def_opts="-DXorriso_allow_external_filterS -DXorriso_allow_launch_frontenD" largefile_opts="-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1" do_strip=0 static_opts= warn_opts="-Wall -Wextra -Wno-unused-parameter -Wno-char-subscripts" # warn_opts="-Wall -Wsign-compare" nglibisofs=1 def_have="-DHAVE_STDINT_H" def_libreadline="-DXorriso_with_readlinE" def_libjte="-DXorriso_with_libjtE -DLibisofs_with_libjtE" link_libreadline="-lreadline" link_libcdio= link_libjte="-ljte" for i in "$@" do if test "$i" = "-do_diet" then def_opts="$def_opts -DXorriso_no_helP" warn_opts= elif test "$i" = "-do_strip" then do_strip=1 elif test "$i" = "-g" then debug_opts="-g -O0" elif test "$i" = "-no_libreadline" then def_libreadline="" link_libreadline="" elif test "$i" = "-dvd_obs_64k" then def_opts="$def_opts -DXorriso_dvd_obs_default_64K" elif test "$i" = "-use_libcdio" then link_libcdio="-lcdio" elif test "$i" = "-no_libjte" then def_libjte="" link_libjte="" elif test "$i" = "-help" -o "$i" = "--help" -o "$i" = "-h" then echo \ "$xorr/compile_xorriso.sh : to be executed above top level directories" echo "Options:" echo " -dvd_obs_64k 64 KB default size for DVD/BD writing." echo " -no_libreadline do not compile for and link with libreadline." echo " -use_libcdio link with -lcdio because libburn uses it." echo " -no_libjte do not compile for and link with libjte." echo " -do_diet produce capability reduced lean version." echo " -do_strip apply program strip to compiled programs." echo " -g produce debuggable programm." echo " -static compile with cc option -static." exit 0 elif test "$i" = "-static" then static_opts="-static" fi done libisofs= libisofs="$libisofs $isofs"buffer.o libisofs="$libisofs $isofs"builder.o libisofs="$libisofs $isofs"data_source.o libisofs="$libisofs $isofs"ecma119.o libisofs="$libisofs $isofs"ecma119_tree.o libisofs="$libisofs $isofs"eltorito.o libisofs="$libisofs $isofs"filesrc.o libisofs="$libisofs $isofs"fs_image.o libisofs="$libisofs $isofs"fs_local.o libisofs="$libisofs $isofs"fsource.o libisofs="$libisofs $isofs"image.o libisofs="$libisofs $isofs"iso1999.o libisofs="$libisofs $isofs"joliet.o libisofs="$libisofs $isofs"hfsplus.o libisofs="$libisofs $isofs"hfsplus_decompose.o libisofs="$libisofs $isofs"hfsplus_classes.o libisofs="$libisofs $isofs"hfsplus_case.o libisofs="$libisofs $isofs"libiso_msgs.o libisofs="$libisofs $isofs"messages.o libisofs="$libisofs $isofs"node.o libisofs="$libisofs $isofs"rockridge.o libisofs="$libisofs $isofs"rockridge_read.o libisofs="$libisofs $isofs"stream.o libisofs="$libisofs $isofs"tree.o libisofs="$libisofs $isofs"util.o libisofs="$libisofs $isofs"util_htable.o libisofs="$libisofs $isofs"util_rbtree.o libisofs="$libisofs $isofs"system_area.o libisofs="$libisofs $isofs"make_isohybrid_mbr.o libisofs="$libisofs $isofs"aaip_0_2.o libisofs="$libisofs $isofs"filter.o libisofs="$libisofs $isofs_filter"external.o libisofs="$libisofs $isofs_filter"zisofs.o libisofs="$libisofs $isofs_filter"gzip.o libisofs="$libisofs $isofs"md5.o echo "Version timestamp : $(sed -e 's/#define Xorriso_timestamP "//' -e 's/"$//' "$xorr"/xorriso_timestamp.h)" date -u '+#define Xorriso_build_timestamP "%Y.%m.%d.%H%M%S"' >"$xorr"/xorriso_buildstamp.h echo "Build timestamp : $(sed -e 's/#define Xorriso_build_timestamP "//' -e 's/"$//' "$xorr"/xorriso_buildstamp.h)" echo "compiling program $xorr/xorriso.c $static_opts $debug_opts $def_opts $link_libcdio" cc -I. -DXorriso_with_maiN $def_have $def_libreadline $def_libjte \ $warn_opts \ $static_opts \ $debug_opts \ $def_opts \ $largefile_opts \ \ -o "$xorr"/xorriso \ \ "$xorr"/xorriso_main.c \ "$xorr"/sfile.c \ "$xorr"/aux_objects.c \ "$xorr"/findjob.c \ "$xorr"/check_media.c \ "$xorr"/misc_funct.c \ "$xorr"/text_io.c \ "$xorr"/match.c \ "$xorr"/emulators.c \ "$xorr"/disk_ops.c \ "$xorr"/cmp_update.c \ "$xorr"/parse_exec.c \ "$xorr"/opts_a_c.c \ "$xorr"/opts_d_h.c \ "$xorr"/opts_i_o.c \ "$xorr"/opts_p_z.c \ \ "$xorr"/base_obj.c \ "$xorr"/lib_mgt.c \ "$xorr"/sort_cmp.c \ "$xorr"/drive_mgt.c \ "$xorr"/iso_img.c \ "$xorr"/iso_tree.c \ "$xorr"/iso_manip.c \ "$xorr"/write_run.c \ "$xorr"/read_run.c \ "$xorr"/filters.c \ \ "$isoburn"/isoburn.c \ "$isoburn"/burn_wrap.c \ "$isoburn"/data_source.c \ "$isoburn"/isofs_wrap.c \ \ "$burn"/async.o \ "$burn"/debug.o \ "$burn"/drive.o \ "$burn"/file.o \ "$burn"/init.o \ "$burn"/options.o \ "$burn"/source.o \ "$burn"/structure.o \ \ "$burn"/sg.o \ "$burn"/write.o \ "$burn"/read.o \ "$burn"/libdax_audioxtr.o \ "$burn"/libdax_msgs.o \ "$burn"/cleanup.o \ "$burn"/cdtext.o \ \ "$burn"/mmc.o \ "$burn"/sbc.o \ "$burn"/spc.o \ "$burn"/util.o \ \ "$burn"/sector.o \ "$burn"/toc.o \ \ "$burn"/crc.o \ "$burn"/ecma130ab.o \ \ $libisofs \ \ $link_libreadline \ $link_libcdio \ $link_libjte \ \ -lpthread \ -lacl \ -lz ret=$? if test "$ret" = 0 then dummy=dummy else echo >&2 echo "+++ FATAL : Compilation of xorriso failed" >&2 echo >&2 exit 1 fi if test "$do_strip" = 1 then echo "stripping result $xorr/xorriso" strip "$xorr"/xorriso fi if test -x $xorr/unite_html_b_line then dummy=dummy else echo "compiling helper program $xorr/unite_html_b_line" ( cd $xorr && cc -g -Wall -o unite_html_b_line unite_html_b_line.c ) fi echo 'done.' # configure.ac stems from xorriso/configure_ac.txt and leads to ./configure # Copyright (c) 2007 - 2024 Thomas Schmitt <scdbackup@gmx.net> # Provided under GPL version 2 or later. AC_INIT([xorriso], [1.5.7], [http://libburnia-project.org]) AC_PREREQ([2.50]) AC_CANONICAL_HOST AC_CANONICAL_TARGET LIBBURNIA_SET_FLAGS AM_INIT_AUTOMAKE([subdir-objects]) AC_CONFIG_MACRO_DIR([./]) dnl Let autoheader produce config.h.in and let configure produce config.h dnl This saves about 500 characters of compile message per source file. AC_CONFIG_HEADER(config.h) AH_TEMPLATE([Xorriso_standalonE], [Define to prepare sources for statically linked xorriso]) AC_DEFINE([Xorriso_standalonE], []) BURN_MAJOR_VERSION=1 BURN_MINOR_VERSION=5 BURN_MICRO_VERSION=7 AC_SUBST(BURN_MAJOR_VERSION) AC_SUBST(BURN_MINOR_VERSION) AC_SUBST(BURN_MICRO_VERSION) LIBISOFS_MAJOR_VERSION=1 LIBISOFS_MINOR_VERSION=5 LIBISOFS_MICRO_VERSION=7 AC_SUBST(LIBISOFS_MAJOR_VERSION) AC_SUBST(LIBISOFS_MINOR_VERSION) AC_SUBST(LIBISOFS_MICRO_VERSION) dnl The API version codes are defined in libisoburn/libisoburn.h dnl #define isoburn_header_version_* AC_PREFIX_DEFAULT([/usr/local]) test "$prefix" = "NONE" && prefix=$ac_default_prefix dnl ts B90405 : Disabled on advise of Ross Burton dnl AM_MAINTAINER_MODE AM_PROG_CC_C_O AC_C_CONST AC_C_INLINE AC_C_BIGENDIAN dnl Large file support AC_SYS_LARGEFILE AC_FUNC_FSEEKO AC_CHECK_FUNC([fseeko]) if test ! $ac_cv_func_fseeko; then AC_ERROR([Libburn requires largefile support.]) fi AH_TEMPLATE([ICONV_CONST], [Whether to apply const qualifier to iconv inbuf]) if test x$XORRISO_OLD_ICONV_CONFIGURE = x then dnl ts B00410 : To detect the need for -liconv and const argument of iconv() LIBBURNIA_CHECK_ICONV dnl ts B00411 : To abort configuration if iconv() still cannot be compiled LIBBURNIA_ASSERT_ICONV else dnl Outdated: produces double -liconv and warnings about parameter mismatch dnl If iconv(3) is in an extra libiconv, then it gets added to variable LIBS. dnl If not, then no -liconv will be added. AC_CHECK_LIB(iconv, iconv, , ) dnl GNU iconv has no function iconv() but libiconv() and a macro iconv() AC_CHECK_LIB(iconv, libiconv, , ) fi AC_PROG_LIBTOOL AC_SUBST(LIBTOOL_DEPS) dnl LIBTOOL="$LIBTOOL --silent" AC_PROG_INSTALL AC_CHECK_HEADERS() AC_CHECK_MEMBER([struct tm.tm_gmtoff], [AC_DEFINE(HAVE_TM_GMTOFF, 1, [Define this if tm structure includes a tm_gmtoff entry.])], , [#include <time.h>]) dnl Check if non standard timegm() function is available AC_CHECK_DECL([timegm], [AC_DEFINE(HAVE_TIMEGM, 1, [Define this if timegm function is available])], , [#include <time.h>]) dnl Whether timezone is an integer variable AH_TEMPLATE([Libburnia_timezonE], [Either timezone or 0]) LIBBURNIA_TRY_TIMEZONE if test x$LIBBURNIA_TIMEZONE = xtimezone then AC_DEFINE([Libburnia_timezonE], [timezone]) else AC_DEFINE([Libburnia_timezonE], [0]) fi dnl Check if non standard eaccess() function is available AC_CHECK_DECL([eaccess], [AC_DEFINE(HAVE_EACCESS, 1, [Define this if eaccess function is available])], , [#include <unistd.h>]) THREAD_LIBS=-lpthread AC_SUBST(THREAD_LIBS) TARGET_SHIZZLE AC_SUBST(ARCH) AC_SUBST(LIBBURNIA_PKGCONFDIR) AC_SUBST(LIBBURN_ARCH_LIBS) dnl Check the preconditions for using statvfs() in sg-dummy dnl (sg-linux and sg-freebsd use statvfs() unconditionally) AH_TEMPLATE([Libburn_os_has_statvfS], [Define to use statvfs() with libburn stdio]) STATVFS_DEF=-DLibburn_os_has_statvfS AC_CHECK_HEADER(sys/statvfs.h, X=, STATVFS_DEF=) AC_CHECK_FUNC([statvfs], X=, STATVFS_DEF=) if test x$STATVFS_DEF = x-DLibburn_os_has_statvfS then AC_DEFINE([Libburn_os_has_statvfS], []) fi dnl xorriso-dd-target is addicted to the Linux kernel and util-linux lsblk if uname -s | grep '^Linux' >/dev/null then XORRISO_DD_TARGET=xorriso-dd-target/xorriso-dd-target XORRISO_DD_TARGET_MAN=xorriso-dd-target/xorriso-dd-target.1 XORRISO_DD_TARGET_TEXI=xorriso-dd-target/xorriso-dd-target.texi echo "enabled installation of xorriso-dd-target/xorriso-dd-target" else XORRISO_DD_TARGET= XORRISO_DD_TARGET_MAN= XORRISO_DD_TARGET_TEXI= echo "disabled installation of xorriso-dd-target/xorriso-dd-target" fi AC_SUBST(XORRISO_DD_TARGET) AC_SUBST(XORRISO_DD_TARGET_MAN) AC_SUBST(XORRISO_DD_TARGET_TEXI) 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="-O3 $CFLAGS" CFLAGS="-fexpensive-optimizations $CFLAGS" fi CFLAGS="-DNDEBUG $CFLAGS" else if test x$GCC = xyes; then CFLAGS="-g -pedantic -Wall -Wextra -Wno-unused-parameter -Wno-char-subscripts -Wno-strict-aliasing $CFLAGS" fi CFLAGS="-DDEBUG $CFLAGS" fi dnl Determine target directory for libisoburn-*.pc dnl Important: Must be performed _after_ TARGET_SHIZZLE dnl LIBBURNIA_SET_PKGCONFIG AH_TEMPLATE([Xorriso_with_readlinE], [Define to use libreadline]) AC_ARG_ENABLE(libreadline, [ --enable-libreadline Enable use of libreadline by xorriso, default=yes], , enable_libreadline=yes) if test x$enable_libreadline = xyes; then dnl Check whether there is readline-devel and readline-runtime. dnl If not, erase this macro which would enable use of readline(),add_history() READLINE_DEF="-DXorriso_with_readlinE" if test x$XORRISO_OLD_READLINE_CONFIGURE = x then dnl ts B00411 : To disable readline if not all needed functions are present LIBBURNIA_ASSERT_READLINE else dnl The empty yes case obviously causes -lreadline to be linked AC_CHECK_HEADER(readline/readline.h, AC_CHECK_LIB(readline, readline, , READLINE_DEF= ), READLINE_DEF= ) dnl The X= in the yes case prevents that -lreadline gets linked twice AC_CHECK_HEADER(readline/history.h, AC_CHECK_LIB(readline, add_history, X= , READLINE_DEF= ), READLINE_DEF= ) fi else READLINE_DEF= echo "disabled libreadline" fi if test x$READLINE_DEF = x; then AH_TEMPLATE([Xorriso_with_editlinE], [Define to use libedit if not libreadline]) if test x$enable_libreadline = xyes; then libedit_deflt=yes else libedit_deflt=no fi AC_ARG_ENABLE(libedit, [ --enable-libedit Enable use of libedit by xorriso if not libreadline, default= setting of --enable-libreadline], , enable_libedit=$libedit_deflt) if test x$enable_libedit = xyes; then READLINE_DEF="-DXorriso_with_editlinE" LIBBURNIA_ASSERT_EDITLINE else READLINE_DEF= echo "disabled libedit" fi fi if test x$READLINE_DEF = x-DXorriso_with_readlinE then AC_DEFINE([Xorriso_with_readlinE], []) elif test x$READLINE_DEF = x-DXorriso_with_editlinE then AC_DEFINE([Xorriso_with_editlinE], []) fi AH_TEMPLATE([Libisofs_with_aaip_acL], [Define to use ACL capabilities]) AC_ARG_ENABLE(libacl, [ --enable-libacl Enable use of ACL functions by libisofs, default=yes], , enable_libacl=yes) LIBACL_DEF= has_acl_h_but_no_func=0 if test x$LIBBURNIA_SUPP_ACL = xlibacl then if test x$enable_libacl = xyes; then dnl Check whether there is libacl-devel and libacl-runtime. dnl If not, erase this macro which would enable use of acl_to_text and others LIBACL_DEF="-DLibisofs_with_aaip_acL" dnl The empty yes case obviously causes -lacl to be linked AC_CHECK_HEADER(sys/acl.h, AC_CHECK_LIB(acl, acl_to_text, , has_acl_h_but_no_libacl=1 ), LIBACL_DEF= ) if test "$has_acl_h_but_no_libacl" = 1 then AC_CHECK_LIB(c, acl_to_text, X= , LIBACL_DEF= ) fi fi fi if test x$LIBACL_DEF = x-DLibisofs_with_aaip_acL then AC_DEFINE([Libisofs_with_aaip_acL], []) if test x$has_acl_h_but_no_libacl = x1 then echo "enabled local processing of ACL" else echo "enabled libacl, local processing of ACL" fi else echo "disabled local processing of ACL" fi AH_TEMPLATE([Libisofs_with_aaip_xattR], [Define to use Linux xattr capabilities]) AH_TEMPLATE([Libisofs_with_sys_xattR], [Define to include Linux sys/xattr.h instead of attr/xattr.h]) AH_TEMPLATE([Libisofs_with_freebsd_extattR], [Define to use FreeBSD extattr capabilities]) AC_ARG_ENABLE(xattr, [ --enable-xattr Enable use of extended file attributes by libisofs, default=yes], , enable_xattr=yes) AC_ARG_ENABLE(xattr-h-pref-attr, [ --enable-xattr-h-pref-attr Prefer include file attr/xattr.h over sys/xattr.h, default=no], , enable_xattr_h_pref_attr=no) XATTR_DEF= if test x"$LIBBURNIA_SUPP_FATTR" = xxattr then if test "x$enable_xattr" = xyes; then dnl Check whether there is the header for Linux xattr. dnl If not, erase this macro which would enable use of listxattr and others XATTR_A_DEF= XATTR_S_DEF= if test x"$enable_xattr_h_pref_attr" = xyes then echo "prefering include file attr/xattr.h over sys/attr.h" XATTR_A_DEF=1 AC_CHECK_HEADER(attr/xattr.h, AC_CHECK_LIB(c, listxattr, X= , XATTR_A_DEF= ), XATTR_A_DEF= ) if test x"$XATTR_A_DEF" = x1 then XATTR_DEF="-DLibisofs_with_aaip_xattR" else XATTR_S_DEF=1 AC_CHECK_HEADER(sys/xattr.h, AC_CHECK_LIB(c, listxattr, X= , XATTR_S_DEF= ), XATTR_S_DEF= ) if test x"$XATTR_S_DEF" = x1 then XATTR_DEF="-DLibisofs_with_aaip_xattR" AC_DEFINE([Libisofs_with_sys_xattR], []) fi fi else XATTR_S_DEF=1 AC_CHECK_HEADER(sys/xattr.h, AC_CHECK_LIB(c, listxattr, X= , XATTR_S_DEF= ), XATTR_S_DEF= ) if test x"$XATTR_S_DEF" = x1 then XATTR_DEF="-DLibisofs_with_aaip_xattR" AC_DEFINE([Libisofs_with_sys_xattR], []) else XATTR_A_DEF=1 AC_CHECK_HEADER(attr/xattr.h, AC_CHECK_LIB(c, listxattr, X= , XATTR_A_DEF= ), XATTR_A_DEF= ) if test x"$XATTR_A_DEF" = x1 then XATTR_DEF="-DLibisofs_with_aaip_xattR" fi fi fi if test x"$XATTR_S_DEF" = x1 then echo "decided to include file sys/attr.h" elif test x"$XATTR_A_DEF" = x1 then echo "decided to include file attr/xattr.h" fi fi elif test x"$LIBBURNIA_SUPP_FATTR" = xextattr then if test "x$enable_xattr" = xyes; then XATTR_DEF="-DLibisofs_with_freebsd_extattR" AC_CHECK_HEADER(sys/extattr.h, AC_CHECK_LIB(c, extattr_list_file, X=, XATTR_DEF= ), XATTR_DEF= ) fi fi if test x$XATTR_DEF = x-DLibisofs_with_aaip_xattR then AC_DEFINE([Libisofs_with_aaip_xattR], []) echo "enabled xattr, local processing of extended file attributes Linux style" elif test x$XATTR_DEF = x-DLibisofs_with_freebsd_extattR then AC_DEFINE([Libisofs_with_freebsd_extattR], []) echo "enabled extattr, local processing of extended file attributes FreeBSD style" else echo "disabled local processing of extended file attributes" fi dnl ts C40722 AH_TEMPLATE([Libisofs_with_aaip_lfa_flagS], [Define to use Linux chattr capabilities]) LFA_DEF= AC_ARG_ENABLE(lfa-flags, [ --enable-lfa-flags Enable processing of Linux chattr(1) flags, default=yes], , enable_lfa_flags=yes) if test x"$enable_lfa_flags" = xyes; then AC_CHECK_HEADER(linux/fs.h, LFA_DEF="-DLibisofs_with_aaip_lfa_flagS", LFA_DEF=) fi if test x"$LFA_DEF" = x-DLibisofs_with_aaip_lfa_flagS then AC_DEFINE([Libisofs_with_aaip_lfa_flagS], []) echo "enabled Linux chattr(1) flags" else echo "disabled Linux chattr(1) flags" fi AH_TEMPLATE([Libisofs_with_zliB], [Define to use compression via zlib]) AH_TEMPLATE([LIBJTE_WITH_ZLIB], [Allow libjte to use zlib]) AC_ARG_ENABLE(zlib, [ --enable-zlib Enable use of zlib by libisofs, default=yes], , enable_zlib=yes) if test x$enable_zlib = xyes; then dnl Check whether there is the header for zlib. dnl If not, erase this macro which would enable use of compress2() and others. dnl Linking fails on SuSE 9.0 because zlib has compress2() but lacks dnl compressBound(). So compressBound is the more modern thing to test. dnl The empty parameter after "compressBound" causes -lz. ZLIB_DEF=yes AC_CHECK_HEADER(zlib.h, AC_CHECK_LIB(z, compressBound, , ZLIB_DEF= ), ZLIB_DEF= ) else echo "disabled use of zlib" ZLIB_DEF= fi if test x$ZLIB_DEF = xyes then AC_DEFINE([Libisofs_with_zliB], []) AC_DEFINE([LIBJTE_WITH_ZLIB], []) fi # There are Linuxes with no public generic SCSI interface LIBBURNIA_CHECK_LINUX_SCSI # libjte source is included in GNU xorriso. Enable it if zlib is enabled. AH_TEMPLATE([Xorriso_with_libjtE], [Define to use Jigdo Template Extraction via libjte]) AH_TEMPLATE([Libisofs_with_libjtE], [Define to use Jigdo Template Extraction via libjte]) if test x$ZLIB_DEF = xyes then AC_DEFINE([Xorriso_with_libjtE], []) AC_DEFINE([Libisofs_with_libjtE], []) LIBJTE_DEF=yes echo "enabled built-in libjte" else LIBJTE_DEF= echo "disabled libjte because zlib not enabled or not available" fi AH_TEMPLATE([THREADED_CHECKSUMS], [Define to use multi-threading in built-in libjte]) AC_ARG_ENABLE(jtethreads, [ --enable-jtethreads Enable multi-threading in libjte, default=yes], , enable_jtethreads=yes) if test x$LIBJTE_DEF = xyes then if test "x$enable_jtethreads" = xyes; then mem_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $THREAD_LIBS" AC_MSG_CHECKING([for pthread_cancel()]) AC_TRY_LINK([#include <pthread.h>], [pthread_cancel((pthread_t) NULL);], [], [enable_jtethreads=no]) AC_MSG_RESULT([$enable_jtethreads]) LDFLAGS="$mem_LDFLAGS" fi if test "x$enable_jtethreads" = xyes; then AC_DEFINE([THREADED_CHECKSUMS], []) echo "enabled multi-threading in libjte" else echo "disabled multi-threading in libjte" fi fi AH_TEMPLATE([LIBJTE_WITH_LIBBZ2], [Define to use libbz2 by built-in libjte]) AC_ARG_ENABLE(libbz2, [ --enable-libbz2 Enable use of libbz2 by libjte, default=yes], , enable_libbz2=yes) if test "x$enable_libbz2" = xyes -a x$LIBJTE_DEF = xyes ; then dnl Check whether there is the header for libbz2. BZIP2_DEF=yes AC_CHECK_HEADER(bzlib.h, AC_CHECK_LIB(bz2, BZ2_bzCompressInit, , BZIP2_DEF= ), BZIP2_DEF= ) else BZIP2_DEF= fi if test x$BZIP2_DEF = xyes then AC_DEFINE([LIBJTE_WITH_LIBBZ2], []) BZIP2_DEF="-DLIBJTE_WITH_LIBBZ2" fi case $host_os in cygwin*|mingw*) default_libcdio=yes ;; *) default_libcdio=no ;; esac AH_TEMPLATE([Libburn_use_libcdiO], [Define to use libcdio as system adapter]) AC_ARG_ENABLE(libcdio, [ --enable-libcdio Enable use of libcdio as system adapter, default=no (except on MSWindows)], , enable_libcdio=$default_libcdio) if test x$enable_libcdio = xyes; then dnl Check whether there is libcdio-devel and libcdio-runtime. dnl If not, erase this macro LIBCDIO_DEF="-DLibburn_use_libcdiO" dnl The empty yes case obviously causes -lcdio to be linked AC_CHECK_HEADER(cdio/cdio.h, AC_CHECK_LIB(cdio, mmc_last_cmd_sense, , LIBCDIO_DEF= ), LIBCDIO_DEF= ) else LIBCDIO_DEF= fi if test x$LIBCDIO_DEF = x then if test x$enable_libcdio = xyes then echo "WARNING: could not enable use of libcdio as system adapter" fi else if echo " $CFLAGS $LDFLAGS " | grep ' -static ' >/dev/null then echo "WARNING : found option -static in CFLAGS or LDFLAGS" echo "WARNING : libcdio API conflicts with inner symbols of xorriso" echo "WARNING : use of libcdio as system adapter disabled" LIBCDIO_DEF= else echo "enabled use of libcdio as system adapter" fi fi if test x$LIBCDIO_DEF = x-DLibburn_use_libcdiO then AC_DEFINE([Libburn_use_libcdiO], []) fi AH_TEMPLATE([Xorriso_allow_external_filterS], [Define to allow xorriso to start external filter processes]) AC_ARG_ENABLE(external-filters, [ --enable-external-filters Enable use of external filter programs by xorriso, default=yes], , enable_external_filters=yes) if test x"$enable_external_filters" = xyes; then EXTF_DEF="-DXorriso_allow_external_filterS" echo "enabled xorriso external filter programs" else EXTF_DEF= echo "disabled xorriso external filter programs" fi if test x$EXTF_DEF = x-DXorriso_allow_external_filterS then AC_DEFINE([Xorriso_allow_external_filterS], []) fi AH_TEMPLATE([Xorriso_allow_extf_suiD], [Define to allow external filters to xorriso when running under setuid]) AC_ARG_ENABLE(external-filters-setuid, [ --enable-external-filters-setuid Enable xorriso external filter programs under setuid, default=no], , enable_external_filters_setuid=no) if test x$enable_external_filters_setuid = xyes; then EXTF_SUID_DEF="-DXorriso_allow_extf_suiD" echo "enabled xorriso external filter programs under setuid" else EXTF_SUID_DEF= echo "disabled xorriso external filter programs under setuid" fi if test x$EXTF_SUID_DEF = x-DXorriso_allow_extf_suiD then AC_DEFINE([Xorriso_allow_extf_suiD], []) fi AH_TEMPLATE([Xorriso_allow_launch_frontenD], [Define to allow xorriso command -launch_frontend]) AC_ARG_ENABLE(launch-frontend, [ --enable-launch-frontend Enable start of piped frontend program by xorriso, default=yes], , enable_launch_frontend=yes) if test x"$enable_launch_frontend" = xyes; then LFRONT_DEF="-DXorriso_allow_launch_frontenD" echo "enabled xorriso command -launch_frontend" else LFRONT_DEF= echo "disabled xorriso command -launch_frontend" fi if test x$LFRONT_DEF = x-DXorriso_allow_launch_frontenD then AC_DEFINE([Xorriso_allow_launch_frontenD], []) fi AH_TEMPLATE([Xorriso_allow_extf_suiD], [Define to allow xorriso command -launch_frontend when running under setuid]) AC_ARG_ENABLE(launch-frontend-setuid, [ --enable-launch-frontend-setuid Enable start of piped frontend program under setuid, default=no], , enable_launch_frontend_setuid=no) if test x$enable_launch_frontend_setuid = xyes; then LFRONT_SUID_DEF="-DXorriso_allow_extf_suiD" echo "enabled xorriso command -launch_frontend under setuid" else LFRONT_SUID_DEF= echo "disabled xorriso command -launch_frontend under setuid" fi if test x$LFRONT_SUID_DEF = x-DXorriso_allow_extf_suiD then AC_DEFINE([Xorriso_allow_extf_suiD], []) fi AH_TEMPLATE([Xorriso_dvd_obs_default_64K], [Define to make 64 KB default size for DVD writing]) AC_ARG_ENABLE(dvd-obs-64k, [ --enable-dvd-obs-64k 64 KB default size for DVD writing, default=no], , enable_dvd_obs=no) if test x$enable_dvd_obs_64k = xyes; then XORRISO_DVD_OBS_64K="-DXorriso_dvd_obs_default_64K" echo "enabled xorriso write size default 64 KB on DVD" else XORRISO_DVD_OBS_64K= echo "disabled xorriso write size default 64 KB on DVD" fi if test x$XORRISO_DVD_OBS_64K = x-DXorriso_dvd_obs_default_64K then AC_DEFINE([Xorriso_dvd_obs_default_64K], []) fi AH_TEMPLATE([Libburn_read_o_direcT], [Define to use O_DIRECT with -as cdrskin]) AC_ARG_ENABLE(track-src-odirect, [ --enable-track-src-odirect Enable use of O_DIRECT with -as cdrskin, default=no], , enable_track_src_odirect=no) if test x$enable_track_src_odirect = xyes; then LIBBURN_O_DIRECT_DEF="-DLibburn_read_o_direcT" echo "enabled use of O_DIRECT with input of -as cdrskin" else LIBBURN_O_DIRECT_DEF= echo "disabled use of O_DIRECT with input of -as cdrskin" fi if test x$LIBBURN_O_DIRECT_DEF = x-DLibburn_read_o_direcT then AC_DEFINE([Libburn_read_o_direcT], []) fi # Check for system dependent mandatory libraries (LIBBURN_ARCH_LIBS) LIBBURNIA_CHECK_ARCH_LIBS(mandatory) # ------- Visible mark in configure : Start of library check # Check for proper library versions if this is desired. # (It fails too often on too many systems.) AC_ARG_ENABLE(pkg-check-modules, [ --enable-pkg-check-modules Enable pkg-config check for libburn and libisofs , default=no], , enable_pkg_check_modules=no) if test x$enable_pkg_check_modules = xyes; then dnl If PKG_CHECK_MODULES is to be used after this if-block, dnl then it might be necessary to perform PKG_PROG_PKG_CONFIG before the block. if test x$LIBCDIO_DEF = x; then dummy=dummy else LIBCDIO_REQUIRED=0.83 PKG_CHECK_MODULES(LIBCDIO, libcdio >= $LIBCDIO_REQUIRED) fi else if test x$LIBCDIO_DEF = x; then dummy=dummy else echo "checking for LIBCDIO... skipped, no --enable-pkg-check-modules" fi fi # ------- Visible mark in configure : End of library check AC_CONFIG_FILES([ Makefile version.h ]) AC_OUTPUT # xorriso.pc #!/bin/sh # # convert_man_to_html.sh - ts B11024 # # Generates a HTML version of man pages xorriso.1 , xorrisofs.1 , xorrecord.1 # # To be executed in the libisoburn toplevel directory (eg. ./libisoburn-1.1.8) # xorriso/man_xorriso_to_html.sh xorriso/man_xorrisofs_to_html.sh xorriso/man_xorrecord_to_html.sh /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of actions on onjects of disk filesystems. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <fcntl.h> #include <errno.h> #include <pwd.h> #include <grp.h> /* O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" /* @param flag bit0= simple readlink(): no normalization, no multi-hop bit1= this is potentially a recursion */ int Xorriso_resolve_link(struct XorrisO *xorriso, char *link_path, char result_path[SfileadrL], int flag) { ssize_t l; struct stat stbuf; int link_count= 0, ret, show_errno= 0; char *buf= NULL, *dirbuf= NULL, *lpt, *spt; static int link_limit= 100; if(flag & 1) { xorriso->resolve_link_rec_count++; if(xorriso->resolve_link_rec_count > xorriso->resolve_link_rec_limit) { Xorriso_msgs_submit(xorriso, 0, link_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "Too many link recursions before : "); Text_shellsafe(link_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, show_errno, "FAILURE", 0); {ret= 0; goto ex;} } } else xorriso->resolve_link_rec_count= 0; Xorriso_alloc_meM(buf, char, SfileadrL); Xorriso_alloc_meM(dirbuf, char, SfileadrL); if(!(flag&1)) if(stat(link_path, &stbuf)==-1) if(errno==ELOOP) { show_errno= errno; goto too_many_hops; } lpt= link_path; while(1) { l= readlink(lpt, buf, SfileadrL-1); if(l==-1) { handle_error:; Xorriso_msgs_submit(xorriso, 0, link_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "Cannot obtain link target of : "); Text_shellsafe(link_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0); handle_abort:; if(strcmp(lpt, link_path)!=0) { sprintf(xorriso->info_text, "Problem occurred with intermediate path : "); Text_shellsafe(lpt, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } {ret= 0; goto ex;} } buf[l]= 0; if(l==0) { Xorriso_msgs_submit(xorriso, 0, link_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "Empty link target with : "); Text_shellsafe(link_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); goto handle_abort; } if(flag&1) { strcpy(result_path, buf); {ret= 1; goto ex;} } /* normalize relative to disk_path */ if(Sfile_str(dirbuf, lpt, 0)<=0) {ret= -1; goto ex;} while(1) { spt= strrchr(dirbuf,'/'); if(spt!=NULL) { *spt= 0; if(*(spt+1)!=0) break; } else break; } ret= Xorriso_normalize_img_path(xorriso, dirbuf, buf, result_path, 2|4); if(ret<=0) { goto ex; } if(lstat(result_path, &stbuf)==-1) { lpt= result_path; goto handle_error; } if(!S_ISLNK(stbuf.st_mode)) break; lpt= result_path; link_count++; if(link_count>link_limit) { too_many_hops:; Xorriso_msgs_submit(xorriso, 0, link_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "Too many link hops with : "); Text_shellsafe(link_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, show_errno, "FAILURE", 0); {ret= 0; goto ex;} } } ret= 1; ex:; Xorriso_free_meM(buf); Xorriso_free_meM(dirbuf); if(xorriso->resolve_link_rec_count > 0) xorriso->resolve_link_rec_count--; return(ret); } int Xorriso_convert_uidstring(struct XorrisO *xorriso, char *uid_string, uid_t *uid, int flag) { double num= 0.0; char text[80]; struct passwd *pwd; sscanf(uid_string, "%lf", &num); sprintf(text,"%.f",num); if(strcmp(text,uid_string)==0) { *uid= num; return(1); } pwd= getpwnam(uid_string); if(pwd==NULL) { sprintf(xorriso->info_text, "-uid: Not a known user: '%s'", uid_string); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } *uid= pwd->pw_uid; return(1); } int Xorriso_convert_gidstring(struct XorrisO *xorriso, char *gid_string, gid_t *gid, int flag) { double num= 0.0; char text[80]; struct group *grp; sscanf(gid_string, "%lf", &num); sprintf(text,"%.f",num); if(strcmp(text,gid_string)==0) { *gid= num; return(1); } grp= getgrnam(gid_string); if(grp==NULL) { sprintf(xorriso->info_text, "-gid: Not a known group: '%s'", gid_string); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } *gid= grp->gr_gid; return(1); } int Xorriso_convert_modstring(struct XorrisO *xorriso, char *cmd, char *mode, mode_t *mode_and, mode_t *mode_or, int flag) { int who_val= 0; char *mpt, *vpt, *opt; unsigned int num= 0; mode_t mode_val,mask; *mode_and= ~0; *mode_or= 0; if(mode[0]=='0') { *mode_and= 0; sscanf(mode,"%o",&num); *mode_or= num; } else if(strchr(mode,'+')!=NULL || strchr(mode,'-')!=NULL || strchr(mode,'=')!=NULL) { /* [ugoa][+-][rwxst] */; for(mpt= mode; mpt!=NULL; mpt= strchr(mpt, ',')) { if(*mpt==',') mpt++; if(strlen(mpt)<2) goto unrecognizable; who_val= 0; for(vpt= mpt; *vpt!='+' && *vpt!='-' && *vpt!='='; vpt++) { if(*vpt=='u') who_val|= 4; else if(*vpt=='g') who_val|= 2; else if(*vpt=='o') who_val|= 1; else if(*vpt=='a') who_val|= 7; else goto unrecognizable; } opt= vpt; mode_val= 0; for(vpt= opt+1; *vpt!=0 && *vpt!=','; vpt++) { if(*vpt=='r') { if(who_val&4) mode_val|= S_IRUSR; if(who_val&2) mode_val|= S_IRGRP; if(who_val&1) mode_val|= S_IROTH; } else if(*vpt=='w') { if(who_val&4) mode_val|= S_IWUSR; if(who_val&2) mode_val|= S_IWGRP; if(who_val&1) mode_val|= S_IWOTH; } else if(*vpt=='x') { if(who_val&4) mode_val|= S_IXUSR; if(who_val&2) mode_val|= S_IXGRP; if(who_val&1) mode_val|= S_IXOTH; } else if(*vpt=='s') { if(who_val&4) mode_val|= S_ISUID; if(who_val&2) mode_val|= S_ISGID; } else if(*vpt=='t') { if(who_val&1) mode_val|= S_ISVTX; } else goto unrecognizable; } if(*opt=='+') { (*mode_or)|= mode_val; } else if(*opt=='=') { mask= 0; if(who_val&1) mask|= S_IRWXO|S_ISVTX; if(who_val&2) mask|= S_IRWXG|S_ISGID; if(who_val&4) mask|= S_IRWXU|S_ISUID; (*mode_and)&= ~(mask); (*mode_or)= ((*mode_or) & ~mask) | mode_val; } else if(*opt=='-') { (*mode_or)&= ~mode_val; (*mode_and)&= ~mode_val; } } } else { unrecognizable:; sprintf(xorriso->info_text, "%s: Unrecognizable or faulty permission mode ", cmd); Text_shellsafe(mode, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } return(1); } /* @param flag bit0= for Xorriso_msgs_submit: use pager bit1= do not issue warnings bit2= warn about dangling link bit3= report sorry if dangling link */ int Xorriso_hop_link(struct XorrisO *xorriso, char *link_path, struct LinkiteM **link_stack, struct stat *stbuf, int flag) { int ret; char *severity; struct LinkiteM *litm; if(*link_stack != NULL) { if(Linkitem_get_link_count(*link_stack, 0) >= xorriso->follow_link_limit) { sprintf(xorriso->info_text, "Too many symbolic links in single tree branch at : "); Text_shellsafe(link_path, xorriso->info_text, 1); if(!(flag&2)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0,"WARNING",flag&1); return(0); } } ret= stat(link_path, stbuf); if(ret == -1) { if(flag & (4 | 8)) { if(flag & 8) severity= "SORRY"; else severity= "WARNING"; sprintf(xorriso->info_text, "Non-existing link target with : "); Text_shellsafe(link_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, severity, flag & 1); } return(0); } ret= Linkitem_find(*link_stack, stbuf->st_dev, stbuf->st_ino, &litm, 0); if(ret>0) { sprintf(xorriso->info_text, "Detected symbolic link loop around : "); Text_shellsafe(link_path, xorriso->info_text, 1); if(!(flag&2)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", flag&1); return(0); } ret= Linkitem_new(&litm, link_path, stbuf->st_dev, stbuf->st_ino, *link_stack, 0); if(ret<=0) { sprintf(xorriso->info_text, "Cannot add new item to link loop prevention stack"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", flag&1); return(-1); } *link_stack= litm; return(1); } /* @param flag bit0= do not only sum up sizes but also print subdirs bit1= this is a recursion bit2= do not report result by Xorriso_result() @return <=0 error , 1 ok , 2 could not open directory */ int Xorriso_show_dux_subs(struct XorrisO *xorriso, char *abs_path, char *rel_path, off_t *size, off_t boss_mem, struct LinkiteM *link_stack, int flag) { int i, ret, no_sort= 0, filec= 0, l, j, fc, no_dive, is_link; char **filev= NULL, *namept; off_t sub_size, report_size, mem= 0; struct DirseQ *dirseq= NULL; struct stat stbuf; dev_t dir_dev; struct LinkiteM *own_link_stack; char *path= NULL, *show_path= NULL, *name= NULL, *sfe= NULL; own_link_stack= link_stack; namept= name; *size= 0; Xorriso_alloc_meM(sfe, char, 5 * SfileadrL); Xorriso_alloc_meM(path, char, SfileadrL); Xorriso_alloc_meM(show_path, char, SfileadrL); Xorriso_alloc_meM(name, char, SfileadrL); if(lstat(abs_path, &stbuf)==-1) {ret= 2; goto ex;} dir_dev= stbuf.st_dev; if(S_ISLNK(stbuf.st_mode)) { if(!(xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&2)))) {ret= 2; goto ex;} if(stat(abs_path, &stbuf)==-1) {ret= 2; goto ex;} if(dir_dev != stbuf.st_dev && !(xorriso->do_follow_mount || (xorriso->do_follow_param && !(flag&2)))) {ret= 2; goto ex;} } ret= Dirseq_new(&dirseq, abs_path, 1); if(ret<0) { sprintf(xorriso->info_text, "Cannot obtain disk directory iterator"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } if(ret==0) {ret= 2; goto ex;} while(1) { Linkitem_reset_stack(&own_link_stack, link_stack, 0); ret= Dirseq_next_adr(dirseq,name,0); if(ret<0) goto ex; if(ret==0) break; sub_size= 0; strcpy(show_path, rel_path); if(Sfile_add_to_path(show_path, name, 0)<=0) goto much_too_long; strcpy(path, abs_path); if(Sfile_add_to_path(path, name, 0)<=0) { much_too_long:; Xorriso_much_too_long(xorriso, strlen(path)+strlen(name)+1, 2); {ret= -1; goto ex;} } no_dive= 0; ret= lstat(path, &stbuf); if(ret==-1) continue; is_link= S_ISLNK(stbuf.st_mode); if(is_link && xorriso->do_follow_links) { ret= Xorriso_hop_link(xorriso, path, &own_link_stack, &stbuf, 1); if(ret<0) {ret= -1; goto ex;} if(ret!=1) no_dive= 1; } if(!S_ISDIR(stbuf.st_mode)) no_dive= 1; if(dir_dev != stbuf.st_dev && !xorriso->do_follow_mount) no_dive= 1; if(!no_dive) { filec++; l= strlen(rel_path)+1; mem+= l; if(l % sizeof(char *)) mem+= sizeof(char *)-(l % sizeof(char *)); if(flag&1) /* diving and counting is done further below */ continue; ret= Xorriso_show_dux_subs(xorriso, path, show_path, &sub_size, boss_mem, own_link_stack,2); if(ret<0) goto ex; if(ret==0) continue; } /* sub_size+= stbuf.st_size+strlen(name)+1; */ sub_size+= stbuf.st_size+2048; if(sub_size>0) (*size)+= sub_size; } if(filec<=0 || !(flag&1)) {ret= 1; goto ex;} /* Try to get a sorted list of directory names */ mem+= (filec+1)*sizeof(char *); ret= Xorriso_check_temp_mem_limit(xorriso, mem+boss_mem, 2); if(ret<0) goto ex; Dirseq_rewind(dirseq, 0); if(ret==0) { no_sort_possible:; no_sort= 1; } else { filev= (char **) calloc(filec+1, sizeof(char *)); if(filev==NULL) goto no_sort_possible; else { for(i= 0; i<filec; i++) filev[i]= NULL; fc= 0; while(1) { ret= Dirseq_next_adr(dirseq,name,0); if(ret<0) goto ex; if(ret==0) break; strcpy(path, abs_path); if(Sfile_add_to_path(path, name, 0)<=0) goto much_too_long; ret= lstat(path,&stbuf); if(ret==-1) continue; is_link= S_ISLNK(stbuf.st_mode); if(is_link && xorriso->do_follow_links) { ret= stat(path,&stbuf); if(ret==-1) continue; } if(!S_ISDIR(stbuf.st_mode)) continue; if(dir_dev != stbuf.st_dev && !xorriso->do_follow_mount) continue; if(fc>=filec) { /* Number of files changed (or programming error) */ revoke_sorting:; for(j=0; j<fc; j++) free((char *) filev[j]); free((char *) filev); filev= NULL; goto no_sort_possible; } filev[fc]= strdup(name); if(filev[fc]==NULL) goto revoke_sorting; fc++; } filec= fc; if(filec>1) Sort_argv(filec, filev, 0); } } for(i= 0; (no_sort || i<filec) && !(xorriso->request_to_abort); i++) { Linkitem_reset_stack(&own_link_stack, link_stack, 0); if(no_sort) { ret= Dirseq_next_adr(dirseq,name,0); if(ret<0) goto ex; if(ret==0) break; } else namept= filev[i]; sub_size= 0; strcpy(show_path, rel_path); if(Sfile_add_to_path(show_path, namept, 0)<=0) goto much_too_long; strcpy(path, abs_path); if(Sfile_add_to_path(path, namept, 0)<=0) goto much_too_long; no_dive= 0; ret= lstat(path,&stbuf); if(ret==-1) continue; is_link= S_ISLNK(stbuf.st_mode); if(is_link && xorriso->do_follow_links) { ret= Xorriso_hop_link(xorriso, path, &own_link_stack, &stbuf, 1); if(ret<0) {ret= -1; goto ex;} if(ret!=1) continue; } if(!S_ISDIR(stbuf.st_mode)) continue; if(dir_dev == stbuf.st_dev || xorriso->do_follow_mount) { ret= Xorriso_show_dux_subs(xorriso, path, show_path, &sub_size, boss_mem+mem, own_link_stack, 2|(flag&1)); if(ret<0) goto ex; } /* sub_size+= stbuf.st_size+strlen(namept)+1; */ sub_size+= stbuf.st_size+2048; if(sub_size>0) (*size)+= sub_size; report_size= sub_size/1024; if(report_size*1024<sub_size) report_size++; if(!(flag & 4)) { if(xorriso->sh_style_result) sprintf(xorriso->result_line, "%-7.f ",(double) (report_size)); else sprintf(xorriso->result_line, "%7.f ",(double) (report_size)); sprintf(xorriso->result_line+strlen(xorriso->result_line), "%s\n", Xorriso_esc_filepath(xorriso,show_path, sfe, 0)); Xorriso_result(xorriso, 0); } } ret= 1; ex:; Xorriso_free_meM(sfe); Xorriso_free_meM(path); Xorriso_free_meM(show_path); Xorriso_free_meM(name); Linkitem_reset_stack(&own_link_stack, link_stack, 0); Dirseq_destroy(&dirseq, 0); if(filev!=NULL) { for(i=0; i<filec; i++) if(filev[i]!=NULL) free((char *) filev[i]); free((char *) filev); } return(ret); } /* @param flag bit1= add '+' to perms bit2-7: hidden_state : bit2= hide in ISO/RR bit3= hide in Joliet bit4= hide in HFS+ */ int Xorriso__mode_to_perms(mode_t st_mode, char perms[11], int flag) { int hidden_state; strcpy(perms,"--------- "); if(st_mode&S_IRUSR) perms[0]= 'r'; if(st_mode&S_IWUSR) perms[1]= 'w'; if(st_mode&S_IXUSR) perms[2]= 'x'; if(st_mode&S_ISUID) { if(st_mode&S_IXUSR) perms[2]= 's'; else perms[2]= 'S'; } if(st_mode&S_IRGRP) perms[3]= 'r'; if(st_mode&S_IWGRP) perms[4]= 'w'; if(st_mode&S_IXGRP) perms[5]= 'x'; if(st_mode&S_ISGID) { if(st_mode&S_IXGRP) perms[5]= 's'; else perms[5]= 'S'; } if(st_mode&S_IROTH) perms[6]= 'r'; if(st_mode&S_IWOTH) perms[7]= 'w'; if(st_mode&S_IXOTH) perms[8]= 'x'; if(st_mode&S_ISVTX) { if(st_mode&S_IXOTH) perms[8]= 't'; else perms[8]= 'T'; } hidden_state= (flag >> 2) & 63; if(hidden_state == 1) perms[9]= 'I'; else if(hidden_state == 2) perms[9]= 'J'; else if(hidden_state == 4) perms[9]= 'A'; else if(hidden_state) perms[9]= 'H'; if(flag & 2) { if(hidden_state) perms[9]= tolower(perms[9]); else perms[9]= '+'; } return(1); } /* @param flag bit0= recognize Xorriso_IFBOOT as file type bit1= add '+' to perms bit2-7: hidden_state : bit2= hide in ISO/RR bit3= hide in Joliet bit4= hide in HFS+ */ int Xorriso_format_ls_l(struct XorrisO *xorriso, struct stat *stbuf, int flag) { int show_major_minor= 0, high_shift= 0, high_mask= 0; char *rpt, perms[11], mm_text[80]; mode_t st_mode; dev_t dev, major, minor; rpt= xorriso->result_line; rpt[0]= 0; st_mode= stbuf->st_mode; if(S_ISDIR(st_mode)) strcat(rpt, "d"); else if(S_ISREG(st_mode)) { strcat(rpt, "-"); } else if(S_ISLNK(st_mode)) strcat(rpt, "l"); else if(S_ISBLK(st_mode)) { strcat(rpt, "b"); show_major_minor= 1; } else if(S_ISCHR(st_mode)) { strcat(rpt, "c"); show_major_minor= 1; } else if(S_ISFIFO(st_mode)) strcat(rpt, "p"); else if(S_ISSOCK(st_mode)) strcat(rpt, "s"); else if((flag & 1) && (st_mode & S_IFMT) == Xorriso_IFBOOT) strcat(rpt, "e"); else strcat(rpt, "?"); Xorriso__mode_to_perms(st_mode, perms, flag & (2 | 252)); strcat(rpt, perms); sprintf(rpt+strlen(rpt)," %3u ",(unsigned int) stbuf->st_nlink); sprintf(rpt+strlen(rpt), "%-8lu ", (unsigned long) stbuf->st_uid); sprintf(rpt+strlen(rpt), "%-8lu ", (unsigned long) stbuf->st_gid); if(show_major_minor) { dev= stbuf->st_rdev; /* according to /usr/include/sys/sysmacros.h : gnu_dev_major(),_minor() >>> but this looks as if it should go to some system dependency >>> in FreeBSD dev_t is 32 bit */ if(sizeof(dev_t) > 4) { high_shift= 32; high_mask= ~0xfff; } major= (((dev >> 8) & 0xfff) | ((unsigned int) (dev >> high_shift) & high_mask)) & 0xffffffff; minor= (((dev & 0xff) | ((unsigned int) (dev >> 12) & ~0xff))) & 0xffffffff; sprintf(mm_text, "%u,%u", (unsigned int) major, (unsigned int) minor); sprintf(rpt+strlen(rpt), "%8s ", mm_text); } else sprintf(rpt+strlen(rpt), "%8.f ", (double) stbuf->st_size); Ftimetxt(stbuf->st_mtime, rpt+strlen(rpt), 0); strcat(rpt, " "); return(1); } struct DirentrY { char *adr; struct DirentrY *next; }; int Xorriso_sorted_dir_x(struct XorrisO *xorriso, char *dir_path, int *filec, char ***filev, off_t boss_mem, int flag) { int count= 0, ret; char *name= NULL; struct DirseQ *dirseq= NULL; off_t mem; struct DirentrY *last= NULL, *current= NULL; Xorriso_alloc_meM(name, char, SfileadrL); *filec= 0; *filev= NULL; mem= boss_mem; ret= Dirseq_new(&dirseq, dir_path, 1); if(ret<=0) goto ex; while(1) { /* loop over directory content */ ret= Dirseq_next_adr(dirseq,name,0); if(ret==0) break; if(ret<0) goto ex; mem+= strlen(name)+8+sizeof(struct DirentrY)+sizeof(char *); if(mem>xorriso->temp_mem_limit) {ret= 0; goto ex;} current= (struct DirentrY *) calloc(1, sizeof(struct DirentrY)); if(current==NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); {ret= -1; goto ex;} } current->adr= NULL; current->next= last; last= current; last->adr= strdup(name); if(last->adr==NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); {ret= -1; goto ex;} } count++; } *filec= count; if(count==0) {ret= 1; goto ex;} (*filev)= (char **) calloc(count, sizeof(char *)); if(*filev==NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); {ret= -1; goto ex; } } count= 0; for(current= last; current!=NULL; current= last) { last= current->next; (*filev)[count++]= current->adr; free((char *) current); } Sort_argv(*filec, *filev, 0); ret= 1; ex:; for(current= last; current!=NULL; current= last) { last= current->next; free(current->adr); free((char *) current); } Xorriso_free_meM(name); Dirseq_destroy(&dirseq, 0); return(ret); } /* @param flag bit0= long format bit1= do not print count of nodes bit2= du format bit3= print directories as themselves (ls -d) */ int Xorriso_lsx_filev(struct XorrisO *xorriso, char *wd, int filec, char **filev, off_t boss_mem, int flag) { int i, ret, was_error= 0, dfilec= 0, pass, passes; char *path= NULL, *acl_text= NULL; char *rpt, *link_target= NULL, **dfilev= NULL; off_t size; struct stat stbuf; Xorriso_alloc_meM(path, char, SfileadrL); Xorriso_alloc_meM(link_target, char, SfileadrL); rpt= xorriso->result_line; Sort_argv(filec, filev, 0); /* Count valid nodes, warn of invalid ones */ for(i= 0; i<filec; i++) { ret= Xorriso_make_abs_adr(xorriso, wd, filev[i], path, 1|2|4|8); if(ret<=0) { was_error++; continue; } ret= lstat(path, &stbuf); if(ret==-1) { sprintf(xorriso->info_text, "Not found in local filesystem: "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 1); was_error++; continue; } } if((flag&8) && !(flag&(2|4))) { sprintf(xorriso->info_text,"Valid local files found: %d\n",filec-was_error); Xorriso_info(xorriso,1); if(filec-was_error<=0) {ret= !was_error; goto ex;} } passes= 1+!(flag&(4|8)); for(pass= 0; pass<passes; pass++) for(i= 0; i<filec && !(xorriso->request_to_abort); i++) { ret= Xorriso_make_abs_adr(xorriso, wd, filev[i], path, 1|2|4); if(ret<=0) continue; ret= lstat(path, &stbuf); if(ret==-1) continue; if(S_ISLNK(stbuf.st_mode) && (xorriso->do_follow_links || xorriso->do_follow_param)) { ret= stat(path, &stbuf); if(ret==-1) ret= lstat(path, &stbuf); if(ret==-1) continue; } if(S_ISDIR(stbuf.st_mode) && !(flag&(4|8))) { if(pass==0) continue; if(filec>1) { strcpy(xorriso->result_line, "\n"); Xorriso_result(xorriso,0); Xorriso_esc_filepath(xorriso,filev[i], xorriso->result_line, 0); strcat(xorriso->result_line, ":\n"); Xorriso_result(xorriso,0); } ret= Xorriso_sorted_dir_x(xorriso, path, &dfilec, &dfilev, boss_mem, 0); if(ret<=0) { /* >>> DirseQ loop and single item Xorriso_lsx_filev() */; } else { if(flag&1) { sprintf(xorriso->result_line, "total %d\n", dfilec); Xorriso_result(xorriso,0); } Xorriso_lsx_filev(xorriso, path, dfilec, dfilev, boss_mem, (flag&1)|2|8); } if(dfilec>0) Sfile_destroy_argv(&dfilec, &dfilev, 0); continue; } else if(pass>0) continue; link_target[0]= 0; rpt[0]= 0; if((flag&5)==1) { Xorriso_local_getfacl(xorriso, path, &acl_text, 16); ret= Xorriso_format_ls_l(xorriso, &stbuf, (acl_text != NULL) << 1); Xorriso_local_getfacl(xorriso, path, &acl_text, 1 << 15); if(ret<=0) continue; if(S_ISLNK(stbuf.st_mode)) { ret= Xorriso_resolve_link(xorriso, path, link_target, 1); if(ret<=0) link_target[0]= 0; } } else if(flag&4) { /* -dux or -dusx */ size= stbuf.st_size; if(S_ISDIR(stbuf.st_mode)) { ret= Xorriso_show_dux_subs(xorriso, path, filev[i], &size, boss_mem, NULL, flag&1); if(ret<0) {ret= -1; goto ex;} if(ret==0) continue; } if(xorriso->sh_style_result) sprintf(rpt, "%-7.f ",(double) (size/1024)); else sprintf(rpt, "%7.f ",(double) (size/1024)); } if(link_target[0]) { Xorriso_esc_filepath(xorriso,filev[i], xorriso->result_line, 1); strcat(xorriso->result_line, " -> "); Xorriso_esc_filepath(xorriso,link_target, xorriso->result_line, 1 | 2); } else { Xorriso_esc_filepath(xorriso,filev[i], xorriso->result_line, 1); } strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); } ret= !was_error; ex:; Xorriso_free_meM(path); Xorriso_free_meM(link_target); return(ret); } /* @param flag >>> bit0= remove whole sub tree: rm -r bit1= remove empty directory: rmdir bit2= recursion: do not reassure in mode 2 "tree" bit3= this is for overwriting and not for plain removal bit4= count deleted files in xorriso->pacifier_count bit5= with bit0 only remove directory content, not the directory bit6= permission to call Xorriso_make_accessible() @return <=0 = error 1 = removed leaf file object 2 = removed directory or tree 3 = did not remove on user revocation */ int Xorriso_rmx(struct XorrisO *xorriso, off_t boss_mem, char *path, int flag) { int ret, is_dir= 0, made_accessible= 0; struct stat victim_stbuf; struct DirseQ *dirseq= NULL; char *sfe= NULL, *sub_path= NULL; struct PermiteM *perm_stack_mem; perm_stack_mem= xorriso->perm_stack; /* Avoiding large local memory objects in order to save stack space */ sfe= malloc(5*SfileadrL); sub_path= malloc(2*SfileadrL); if(sfe==NULL || sub_path==NULL) { Xorriso_no_malloc_memory(xorriso, &sfe, 0); {ret= -1; goto ex;} } if(Xorriso_much_too_long(xorriso, strlen(path), 0)<=0) {ret= 0; goto ex;} ret= lstat(path, &victim_stbuf); if(ret==-1) { if((flag & 64) && errno == EACCES) { ret= Xorriso_make_accessible(xorriso, path, 0); if(ret < 0) goto ex; made_accessible= 1; ret= lstat(path, &victim_stbuf); } if(ret==-1) { sprintf(xorriso->info_text, "Cannot lstat(%s)", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); {ret= 0; goto ex;} } } if(strcmp(path, "/")==0) { sprintf(xorriso->info_text, "May not delete root directory"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(S_ISDIR(victim_stbuf.st_mode)) is_dir= 1; if(!is_dir) { if(flag&2) { /* rmdir */ sprintf(xorriso->info_text, "%s in disk filesystem is not a directory", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } else { if(flag&1) { /* rm -r */ #ifdef Osirrox_not_yeT /* >>> */ struct stat *victim_node= NULL; victim_node= &victim_stbuf; if((xorriso->do_reassure==1 && !xorriso->request_not_to_ask) || (flag&32)) { /* Iterate over subordinates and delete them */ mem= boss_mem; ret= Xorriso_findi_iter(xorriso, (IsoDir *) victim_node, &mem, &iter, &node_array, &node_count, &node_idx, &node, 1|2); if(ret<=0) { cannot_create_iter:; Xorriso_cannot_create_iter(xorriso, ret, 0); ret= -1; goto ex; } pl= strlen(path); strcpy(sub_path, path); if(pl==0 || sub_path[pl-1]!='/') { sub_path[pl++]= '/'; sub_path[pl]= 0; } sub_name= sub_path+pl; while(1) { ret= Xorriso_findi_iter(xorriso, (IsoDir *) victim_node, &mem, &iter, &node_array, &node_count, &node_idx, &node, 0); if(ret<0) goto ex; if(ret==0 || xorriso->request_to_abort) break; name= (char *) iso_node_get_name(node); if(Xorriso_much_too_long(xorriso, pl+1+strlen(name), 0)<=0) {ret= 0; goto rm_r_problem_handler;} strcpy(sub_name, name); ret= Xorriso_rmi(xorriso, iter, mem, sub_path, (flag&(1|2|8|16))|4); if(ret==3 || ret<=0 || xorriso->request_to_abort) { rm_r_problem_handler:; not_removed= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret<0) goto dir_not_removed; } } if(flag&32) {ret= 2; goto ex;} if(not_removed) { dir_not_removed:; sprintf(xorriso->info_text, "Directory not removed: %s", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); if(ret>0) ret= 3; goto ex; } } #else /* Osirrox_not_yeT */ sprintf(xorriso->info_text, "-rm_rx is not implemented yet"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; #endif /* !Osirrox_not_yeT */ } else { if(!(flag&2)) { /* not rmdir */ sprintf(xorriso->info_text, "%s in disk filesystem is a directory", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } ret= Dirseq_new(&dirseq, path, 1); if(ret>0) { ret= Dirseq_next_adr(dirseq, sfe, 0); if(ret>0) { sprintf(xorriso->info_text, "Directory not empty on attempt to delete: %s", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } } } if(xorriso->request_to_abort) {ret= 3; goto ex;} ret= Xorriso_reassure_restore(xorriso, path, (flag&(4|8)) | !!is_dir); if(ret<=0 || ret==3) goto ex; if(is_dir) ret= rmdir(path); else ret= unlink(path); if(ret == -1) { if((flag & 64) && errno == EACCES && !made_accessible) { ret= Xorriso_make_accessible(xorriso, path, 0); if(ret < 0) goto ex; made_accessible= 1; if(is_dir) ret= rmdir(path); else ret= unlink(path); } if(ret == -1) { sprintf(xorriso->info_text, "Cannot delete from disk filesystem %s", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= -1; goto ex; } } if(flag&16) xorriso->pacifier_count++; ret= 1+!!is_dir; ex:; if(made_accessible) Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, 0); if(sfe!=NULL) free(sfe); if(sub_path!=NULL) free(sub_path); Dirseq_destroy(&dirseq, 0); return(ret); } /* @param flag bit0= recursion */ int Xorriso_findx_action(struct XorrisO *xorriso, struct FindjoB *job, char *abs_path, char *show_path, int depth, int flag) { int ret= 0, type, action= 0, dpl= 0, compare_result, uret, max_bit; int follow_links; uid_t user= 0; gid_t group= 0; time_t date= 0; mode_t mode_or= 0, mode_and= ~1; uint64_t chattr_flags, lfa_flags; char *target, *text_2, *wdi_mem= NULL, *disk_prefix, *iso_path= NULL; char *basename, *lfa_text= NULL, *acl_text= NULL, *attrlist= NULL; char *leafname= NULL; struct FindjoB *subjob; struct stat stbuf; Xorriso_alloc_meM(iso_path, char, SfileadrL); Xorriso_alloc_meM(leafname, char, SfileadrL); action= Findjob_get_action_parms(job, &target, &text_2, &user, &group, &mode_and, &mode_or, &type, &date, &subjob, &chattr_flags, 0); if(action<0) action= 0; if(action==15 || action==16 || action==18 || action==19 || action==20) { /* in_iso , not_in_iso, add_missing , empty_iso_dir , is_full_in_iso */ Findjob_get_start_path(job, &disk_prefix, 0); if(strncmp(abs_path, disk_prefix, strlen(disk_prefix))!=0) {ret= -1; goto ex;} dpl= strlen(disk_prefix); if(strlen(target)+strlen(abs_path)-dpl >= SfileadrL) {ret= -1; goto ex;} if(abs_path[dpl]=='/') dpl++; ret= Xorriso_make_abs_adr(xorriso, target, abs_path+dpl, iso_path, 4); if(ret<=0) {goto ex;} } if(action==15) { /* in_iso */ ret= Xorriso_iso_lstat(xorriso, iso_path, &stbuf, 0); if(ret<0) {ret= 1; goto ex;} Text_shellsafe(show_path, xorriso->result_line, 0); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); ret= 1; } else if(action==16) { /* not_in_iso */ ret= Xorriso_compare_2_files(xorriso, abs_path, iso_path, abs_path+dpl, &compare_result, 4); if(ret<xorriso->find_compare_result) xorriso->find_compare_result= ret; if(ret>=0) ret= 1; } else if(action==18) { /* add_missing */ ret= Xorriso_compare_2_files(xorriso, abs_path, iso_path, abs_path+dpl, &compare_result, 4|(1u<<31)); if(ret<xorriso->find_compare_result) xorriso->find_compare_result= ret; if(ret==0) { uret= Xorriso_update_interpreter(xorriso, NULL, NULL, compare_result, abs_path, iso_path, ((flag&1)<<2) | 2); if(uret<=0) ret= 0; } if(ret>=0) ret= 1; } else if(action==19) { /* empty_iso_dir */ ret= Xorriso_iso_lstat(xorriso, iso_path, &stbuf, 0); if(ret<0) {ret= 1; goto ex;} if(!S_ISDIR(stbuf.st_mode)) {ret= 1; goto ex;} ret= Xorriso_rmi(xorriso, NULL, (off_t) 0, iso_path, 1|32); if(ret>0) { sprintf(xorriso->info_text, "Emptied directory "); Text_shellsafe(iso_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0); } } else if(action==20) { /* is_full_in_iso */ ret= Xorriso_iso_lstat(xorriso, iso_path, &stbuf, 0); if(ret<0) {ret= 1; goto ex;} if(!S_ISDIR(stbuf.st_mode)) {ret= 1; goto ex;} wdi_mem= strdup(xorriso->wdi); if(wdi_mem == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); {ret= -1; goto ex;} } strcpy(xorriso->wdi, iso_path); ret= Xorriso_ls(xorriso, 4|8); strcpy(xorriso->wdi, wdi_mem); if(ret>0) { strcpy(xorriso->result_line, "d "); Text_shellsafe(iso_path, xorriso->result_line, 1); strcat(xorriso->result_line, " (ISO) : non-empty directory (would not match mount point)\n"); Xorriso_result(xorriso,0); } {ret= 1; goto ex;} } else if(action == 24) { /* getfacl */ follow_links= (xorriso->do_follow_links || (xorriso->do_follow_param && !(flag & 1))) << 5; ret= Xorriso_local_getfacl(xorriso, abs_path, &acl_text, follow_links); if(ret == 1) { ret= lstat(abs_path, &stbuf); if(ret == -1) {ret= 0; goto ex;} ret= Xorriso_report_acl_header(xorriso, show_path, stbuf.st_uid, stbuf.st_gid, 0); if(ret > 0) { strcpy(xorriso->result_line, acl_text); Xorriso_result(xorriso, 0); } } if(acl_text != NULL) Xorriso_local_getfacl(xorriso, "", &acl_text, 1 << 15); } else if(action == 26) { /* getfattr */ follow_links= (xorriso->do_follow_links || (xorriso->do_follow_param && !(flag & 1))) << 5; ret= Xorriso_getfattr(xorriso, NULL, abs_path, &attrlist, 2 | follow_links); } else if(action == 33) { /* get_any_xattr */ follow_links= (xorriso->do_follow_links || (xorriso->do_follow_param && !(flag & 1))) << 5; ret= Xorriso_getfattr(xorriso, NULL, abs_path, &attrlist, 2 | 8 | follow_links); } else if(action == 40) { /* estimate_size */ basename= strrchr(abs_path, '/'); if(basename != NULL) basename++; else basename= abs_path; ret= lstat(abs_path, &stbuf); if(ret != -1) ret= Xorriso_estimate_file_size(xorriso, job, basename, stbuf.st_mode, stbuf.st_size, 0); } else if(action == 44) { /* list_extattr */ ret= Xorriso_list_extattr(xorriso, NULL, abs_path, show_path, target, 2); } else if(action == 50) { /* print_outname */ ret= Sfile_leafname(abs_path, leafname, 0); if(ret <= 0) goto ex; ret= Xorriso_test_outchar(xorriso, NULL, leafname, type, 1); if(ret <= 0) goto ex; } else if(action == 60) { /* lsattrd */ ret= Xorriso_get_lfa_flags(xorriso, NULL, show_path, &lfa_flags, &max_bit, 2); if(ret >= 0) { lfa_text= NULL; ret= Xorriso_encode_lfa_flags(xorriso, lfa_flags, &lfa_text, 1); if(ret > 0) { sprintf(xorriso->result_line, "%-22s ", lfa_text); Xorriso_esc_filepath(xorriso, show_path, xorriso->result_line, 1); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); } if(lfa_text != NULL) free(lfa_text); ret= 1; } else if(ret == 0) { ret= 1; } } else { Xorriso_esc_filepath(xorriso,show_path, xorriso->result_line, 0); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); ret= 1; } ex:; if(action==15 || action==16 || action==18 || action==19 || action==20) if(xorriso->no_volset_present) xorriso->request_to_abort= 1; /* Need an image. No use to try again. */ if(wdi_mem != NULL) free(wdi_mem); Xorriso_free_meM(leafname); Xorriso_free_meM(iso_path); return(ret); } /* @param flag bit0=recursion */ int Xorriso_findx(struct XorrisO *xorriso, struct FindjoB *job, char *abs_dir_parm, char *dir_path, struct stat *dir_stbuf, int depth, struct LinkiteM *link_stack, int flag) { int ret,is_link, no_dive; struct DirseQ *dirseq= NULL; struct stat stbuf; struct LinkiteM *own_link_stack; char *abs_dir_path, *namept; char *name= NULL, *path= NULL, *sfe= NULL; char *abs_dir_path_data= NULL, *abs_path= NULL; job->depth= depth; if(xorriso->request_to_abort) {ret= 0; goto ex;} Xorriso_alloc_meM(sfe, char, 5*SfileadrL); Xorriso_alloc_meM(name, char, SfileadrL); Xorriso_alloc_meM(path, char, SfileadrL); Xorriso_alloc_meM(abs_dir_path_data, char, SfileadrL); Xorriso_alloc_meM(abs_path, char, SfileadrL); own_link_stack= link_stack; abs_dir_path= abs_dir_parm; if(abs_dir_path[0]==0) { ret= Xorriso_make_abs_adr(xorriso, xorriso->wdx, dir_path, abs_dir_path_data, 1|2|8); if(ret<=0) goto ex; abs_dir_path= abs_dir_path_data; ret= Xorriso_path_is_excluded(xorriso, abs_dir_path, !(flag&1)); if(ret<0) goto ex; if(ret>0) {ret= 0; goto ex;} ret= lstat(abs_dir_path, dir_stbuf); if(ret==-1) {ret= 0; goto ex;} if(S_ISLNK(dir_stbuf->st_mode) && (xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&1)))) if(stat(abs_dir_path, &stbuf)!=-1) if(dir_stbuf->st_dev == stbuf.st_dev || (xorriso->do_follow_mount || (xorriso->do_follow_param && !(flag&1)))) memcpy(dir_stbuf, &stbuf, sizeof(struct stat)); namept= strrchr(dir_path, '/'); if(namept==NULL) namept= dir_path; else namept++; ret= Findjob_test_2(xorriso, job, NULL, namept, dir_path, NULL, dir_stbuf, 0); if(ret<0) goto ex; if(ret>0) { ret= Xorriso_findx_action(xorriso, job, abs_dir_path, dir_path, depth, flag&1); if(xorriso->request_to_abort) {ret= 0; goto ex;} if(ret<=0) { if(Xorriso_eval_problem_status(xorriso, ret, 1|2)<0) goto ex; } } } if(xorriso->request_to_abort) {ret= 1; goto ex;} if(!S_ISDIR(dir_stbuf->st_mode)) {ret= 2; goto ex;} ret= Dirseq_new(&dirseq, abs_dir_path, 1); if(ret<0) { sprintf(xorriso->info_text, "Cannot obtain disk directory iterator"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } if(ret==0) {ret= 2; goto ex;} job->depth++; while(!xorriso->request_to_abort) { Linkitem_reset_stack(&own_link_stack, link_stack, 0); ret= Dirseq_next_adr(dirseq,name,0); if(ret==0) break; if(ret<0) { sprintf(xorriso->info_text,"Failed to obtain next directory entry"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } ret= Xorriso_make_abs_adr(xorriso, abs_dir_path, name, abs_path, 1); if(ret<=0) goto ex; ret= Xorriso_make_abs_adr(xorriso, dir_path, name, path, 4); if(ret<=0) goto ex; ret= Xorriso_path_is_excluded(xorriso, abs_path, 0); /* (is never param) */ if(ret<0) goto ex; if(ret>0) continue; ret= lstat(abs_path, &stbuf); if(ret==-1) continue; no_dive= 0; is_link= S_ISLNK(stbuf.st_mode); if(is_link && xorriso->do_follow_links) { ret= Xorriso_hop_link(xorriso, abs_path, &own_link_stack, &stbuf, 2); if(ret<0) {ret= -1; goto ex;} if(ret!=1) no_dive= 1; } ret= Findjob_test_2(xorriso, job, NULL, name, path, dir_stbuf, &stbuf, 0); if(ret<0) goto ex; if(ret>0) { ret= Xorriso_findx_action(xorriso, job, abs_path, path, depth, flag&1); if(xorriso->request_to_abort) {ret= 0; goto ex;} if(ret<=0) { if(Xorriso_eval_problem_status(xorriso, ret, 1|2)<0) goto ex; } } if(!S_ISDIR(stbuf.st_mode)) no_dive= 1; if(dir_stbuf->st_dev != stbuf.st_dev && !xorriso->do_follow_mount) no_dive= 1; if(!no_dive) { ret= Xorriso_findx(xorriso, job, abs_path, path, &stbuf, depth+1, own_link_stack, flag|1); if(ret<0) goto ex; } } ret= 1; ex:; job->depth= depth; Xorriso_free_meM(sfe); Xorriso_free_meM(name); Xorriso_free_meM(path); Xorriso_free_meM(abs_dir_path_data); Xorriso_free_meM(abs_path); Dirseq_destroy(&dirseq, 0); return(ret); } /* @param flag bit0= no hardlink reconstruction bit1= do not set xorriso->node_*_prefixes bit5= -extract_single: eventually do not insert directory tree */ int Xorriso_restore_sorted(struct XorrisO *xorriso, int count, char **src_array, char **tgt_array, int *problem_count, int flag) { int i, ret, with_node_array= 0, hflag= 0, hret; *problem_count= 0; if(!(((xorriso->ino_behavior & 16) && xorriso->do_restore_sort_lba) || (xorriso->ino_behavior & 4) || (flag & 1))) { ret= Xorriso_make_hln_array(xorriso, 0); if(ret<=0) goto ex; } if(xorriso->do_restore_sort_lba) { /* Count affected nodes */ Xorriso_destroy_node_array(xorriso, 0); for(i= 0; i < count; i++) { if(src_array[i] == NULL || tgt_array[i] == NULL) continue; /* sort_lba : Make directories plus node_array and then run array extractor (with eventual hardlink detection) */ hflag= (1 << 7) | ((!!(flag & 2)) << 9) | (flag & 32); ret= Xorriso_restore(xorriso, src_array[i], tgt_array[i], (off_t) 0, (off_t) 0, hflag); if(ret <= 0) { (*problem_count)++; hret= Xorriso_eval_problem_status(xorriso, ret, 1 | 2); if(hret < 0) goto ex; } with_node_array= 1; } } if(with_node_array) { /* Allocate and fill node array */ if(xorriso->node_counter <= 0) {ret= 2; goto ex;} ret= Xorriso_new_node_array(xorriso, xorriso->temp_mem_limit, 0, !xorriso->do_restore_sort_lba); if(ret<=0) goto ex; for(i= 0; i < count; i++) { if(src_array[i] == NULL || tgt_array[i] == NULL) continue; ret= Xorriso_restore(xorriso, src_array[i], tgt_array[i], (off_t) 0, (off_t) 0, (2 << 7) | (flag & 32)); if(ret <= 0) { (*problem_count)++; hret= Xorriso_eval_problem_status(xorriso, ret, 1 | 2); if(hret < 0) goto ex; } } } /* Perform restore operations */ if(xorriso->do_restore_sort_lba) { ret= Xorriso_restore_node_array(xorriso, 0); if(ret <= 0) goto ex; } else { for(i= 0; i < count; i++) { if(src_array[i] == NULL || tgt_array[i] == NULL) continue; ret= Xorriso_restore(xorriso, src_array[i], tgt_array[i], (off_t) 0, (off_t) 0, flag & 32); if(ret <= 0) { (*problem_count)++; hret= Xorriso_eval_problem_status(xorriso, ret, 1 | 2); if(hret < 0) goto ex; } } } ret= 1; ex:; return(ret); } /* @param flag bit0= path is a directory bit2= recursion: do not reassure in mode 2 "tree" bit3-7= question text mode 0= plain removal 1= replacing file object 2= overwriting of content, keeping file object 3= appending of content, keeping file object */ int Xorriso_reassure_restore(struct XorrisO *xorriso, char *path, int flag) { int ret, mode; mode= (flag >> 3) & 31; while((xorriso->do_reassure==1 || (xorriso->do_reassure==2 && !(flag&4))) && !xorriso->request_not_to_ask) { /* ls -ld */ Xorriso_lsx_filev(xorriso, xorriso->wdx, 1, &path, (off_t) 0, 1|2|8); if(flag&1) /* du -s */ Xorriso_lsx_filev(xorriso, xorriso->wdx, 1, &path, (off_t) 0, 2|4); if(mode == 3) sprintf(xorriso->info_text, "File exists. Append content ? n= no, y= yes, x= abort, @= stop asking\n"); else if(mode == 2) sprintf(xorriso->info_text, "File exists. Overwrite content ? n= no, y= yes, x= abort, @= stop asking\n"); else if(mode == 1) sprintf(xorriso->info_text, "File exists. Remove ? n= keep old, y= remove, x= abort, @= stop asking\n"); else sprintf(xorriso->info_text, "Remove above file ? n= keep it, y= remove it, x= abort, @= stop asking\n"); Xorriso_info(xorriso, 4); ret= Xorriso_request_confirmation(xorriso, 1|2|4|16); if(ret<=0) goto ex; if(xorriso->request_to_abort) { sprintf(xorriso->info_text, "File alteration operation aborted by user before file: "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 3; goto ex; } if(ret==3) continue; if(ret==6) /* yes */ break; if(ret==4) { /* yes, do not ask again */ xorriso->request_not_to_ask= 1; break; } if(ret==1) { /* no */ sprintf(xorriso->info_text, "Kept in existing state: "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 3; goto ex; } } ret= 1; ex: return(ret); } /* @param flag bit7= return 4 if restore fails from denied permission do not issue error message @return <=0 failure , 1 success , 4 with bit7: permission to create file was denied */ int Xorriso_make_tmp_path(struct XorrisO *xorriso, char *orig_path, char *tmp_path, int *fd, int flag) { char *cpt; cpt= strrchr(orig_path, '/'); if(cpt==NULL) tmp_path[0]= 0; else { strncpy(tmp_path, orig_path, cpt+1-orig_path); tmp_path[cpt+1-orig_path]= 0; } strcat(tmp_path, "_tmp_xorriso_restore_XXXXXX"); *fd= mkstemp(tmp_path); if(*fd==-1) { if(errno == EACCES && (flag & 128)) return(4); strcpy(xorriso->info_text, "Cannot create temporary file : "); Text_shellsafe(tmp_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); return(0); } fchmod(*fd, S_IRUSR|S_IWUSR); return(1); } /* @param flag bit0= change regardless of xorriso->do_auto_chmod bit1= desired is only rx @return -1=severe error , -2= cannot chmod, 0= nothing to do, 1 = chmoded */ int Xorriso_auto_chmod(struct XorrisO *xorriso, char *disk_path, int flag) { int ret, is_link= 0; char *path_pt, *link_target= NULL; mode_t mode, desired= S_IRUSR | S_IWUSR | S_IXUSR; struct stat stbuf; Xorriso_alloc_meM(link_target, char, SfileadrL); if(!(xorriso->do_auto_chmod || (flag & 1))) {ret= 0; goto ex;} if(flag & 2) desired &= ~S_IWUSR; path_pt= disk_path; ret= lstat(path_pt, &stbuf); if(ret==-1) {ret= 0; goto ex;} if(S_ISLNK(stbuf.st_mode)) { is_link= 1; ret= stat(path_pt, &stbuf); if(ret==-1) {ret= 0; goto ex;} } if(!S_ISDIR(stbuf.st_mode)) {ret= 0; goto ex;} if(is_link) { ret= Xorriso_resolve_link(xorriso, path_pt, link_target, 0); if(ret<=0) goto ex; path_pt= link_target; } if((stbuf.st_mode & desired) == desired) {ret= 0; goto ex;} if(stbuf.st_uid!=geteuid()) {ret= -2; goto ex;} mode= (stbuf.st_mode | desired) & 07777; ret= chmod(path_pt, mode); if(ret==-1) { sprintf(xorriso->info_text, "Cannot change access permissions of disk directory: chmod %o ", (unsigned int) (mode & 07777)); Text_shellsafe(path_pt, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "SORRY", 0); {ret= -2; goto ex;} } ret= Permstack_push(&(xorriso->perm_stack), path_pt, &stbuf, 0); if(ret<=0) goto ex; ret= 1; ex:; Xorriso_free_meM(link_target); return(ret); } int Xorriso_make_accessible(struct XorrisO *xorriso, char *disk_path, int flag) { int done= 0, ret, just_rx= 2; char *npt, *apt, *path, *wpt; Xorriso_alloc_meM(path, char, SfileadrL); apt= disk_path; wpt= path; for(npt= apt; !done; apt= npt + 1) { npt= strchr(apt, '/'); if(npt == NULL) break; if(strchr(npt + 1, '/') == NULL) just_rx= 0; strncpy(wpt, apt, npt + 1 - apt); wpt+= npt + 1 - apt; *wpt= 0; ret= Xorriso_auto_chmod(xorriso, path, just_rx); if(ret == -1) {ret= -1; goto ex;} if(ret == -2) {ret= 0; goto ex;} } ret= 1; ex: Xorriso_free_meM(path); return(ret); } /* @param flag bit0= prefer to find a match after *img_prefixes (but deliver img_prefixes if no other can be found) */ int Xorriso_make_restore_path(struct XorrisO *xorriso, struct Xorriso_lsT **img_prefixes, struct Xorriso_lsT **disk_prefixes, char img_path[SfileadrL], char disk_path[SfileadrL], int flag) { int li; struct Xorriso_lsT *s, *d, *found_s= NULL, *found_d= NULL; char *ipfx, *dpfx; /* Obtain disk_path by replacing start piece of img_path */ d= *disk_prefixes; for(s= *img_prefixes; s != NULL; s= Xorriso_lst_get_next(s, 0), d= Xorriso_lst_get_next(d, 0)) { ipfx= Xorriso_lst_get_text(s, 0); li= strlen(ipfx); dpfx= Xorriso_lst_get_text(d, 0); if(li == 1 && ipfx[0] == '/') { li= 0; if(img_path[0] != '/') continue; } else { if(strncmp(img_path, ipfx, li) != 0) continue; if(img_path[li] != 0 && img_path[li] != '/') continue; } if(strlen(dpfx) + strlen(img_path) - li + 1 >= SfileadrL) return(-1); if(img_path[li]=='/') { if(dpfx[0] == '/' && dpfx[1] == 0) sprintf(disk_path, "/%s", img_path + li + 1); else sprintf(disk_path, "%s/%s", dpfx, img_path + li + 1); } else strcpy(disk_path, dpfx); /* img_path[li] is 0, img_path equals ipfx */ found_s= s; found_d= d; if(s != *img_prefixes || !(flag & 1)) break; } *img_prefixes= found_s; *disk_prefixes= found_d; return(found_s != NULL); } /* @param flag bit0=permission to run Xorriso_make_accessible */ int Xorriso_restore_make_hl(struct XorrisO *xorriso, char *old_path, char *new_path, int flag) { int ret; struct PermiteM *perm_stack_mem; ret= link(old_path, new_path); if(ret == 0) return(1); if(errno == EACCES && (flag & 1)) { perm_stack_mem= xorriso->perm_stack; ret= Xorriso_make_accessible(xorriso, new_path, 0); if(ret > 0) { ret= link(old_path, new_path); if(ret == 0) { Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, 0); return(1); } } Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, 0); } sprintf(xorriso->info_text, "Hardlinking failed: "); Text_shellsafe(new_path, xorriso->info_text, 1); strcat(xorriso->info_text, " -> "); Text_shellsafe(old_path, xorriso->info_text, 1 | 2); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "WARNING", 0); return(0); } int Xorriso_afile_fopen(struct XorrisO *xorriso, char *filename, char *mode, FILE **ret_fp, int flag) /* bit0= do not print error message on failure bit1= do not open stdin */ { FILE *fp= NULL; *ret_fp= NULL; if(strcmp(filename,"-")==0) { if(mode[0]=='a' || mode[0]=='w' || (mode[0]=='r' && mode[1]=='+') || (mode[0]=='r' && mode[1]=='b' && mode[2]=='+')) fp= stdout; else if(flag & 2) { Xorriso_msgs_submit(xorriso, 0, "Not allowed as input path: '-'", 0, "FAILURE", 0); return(0); } else { Xorriso_msgs_submit(xorriso, 0, "Ready for data at standard input", 0, "NOTE", 0); fp= stdin; } } else if(strncmp(filename,"tcp:",4)==0){ Xorriso_msgs_submit(xorriso, 0, "TCP/IP service isn't implemented yet.", 0, "FAILURE", 0); } else if(strncmp(filename,"file:",5)==0){ fp= fopen(filename+5,mode); } else { fp= fopen(filename,mode); } if(fp==NULL){ if(!(flag&1)) { sprintf(xorriso->info_text, "Failed to open file '%s' in %s mode", filename, mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); } return(0); } *ret_fp= fp; return(1); } /* @param flag bit0= make absolute command paths with known systems bit1= do not allow prefixes with cmd bit2= interpret unprefixed cmd as shell: bit3= do not care for device filetype */ int Xorriso_make_mount_cmd(struct XorrisO *xorriso, char *cmd, off_t lba, int track, int session, char *volid, char *devadr, char result[SfileadrL], int flag) { int ret, reg_file= 0, is_safe= 0, sys_code= 0; char *form= NULL, session_text[12], track_text[12], lba_text[24]; char *vars[5][2], *sfe= NULL, *volid_sfe= NULL, *cpt, *sysname; char *cooked_dev= NULL, *eff_dev; struct stat stbuf; Xorriso_alloc_meM(form, char, 6 * SfileadrL); Xorriso_alloc_meM(sfe, char, 5 * SfileadrL); Xorriso_alloc_meM(volid_sfe, char, 5 * 80 + 1); Xorriso_alloc_meM(cooked_dev, char, SfileadrL); if(strlen(cmd) > SfileadrL) { Xorriso_msgs_submit(xorriso, 0, "Argument much too long", 0, "FAILURE", 0); {ret= 0; goto ex;} } eff_dev= devadr; ret= stat(devadr, &stbuf); if(ret != -1 && !(flag & 8)) if(S_ISREG(stbuf.st_mode)) reg_file= 1; if(strncmp(cmd, "linux:", 6) == 0 && !(flag & 2)) { cpt= cmd + 6; sys_code= 1; } else if(strncmp(cmd, "freebsd:", 8) == 0 && !(flag & 2)) { cpt= cmd + 8; sys_code= 2; } else if(strncmp(cmd, "netbsd:", 7) == 0 && !(flag & 2)) { cpt= cmd + 7; sys_code= 3; } else if(strncmp(cmd, "string:", 7) == 0 && !(flag & 2)) { cpt= cmd + 7; strcpy(form, cpt); } else if(flag & 4) { cpt= cmd; strcpy(form, cpt); } else { cpt= cmd; ret= System_uname(&sysname, NULL, NULL, NULL, 0); if(ret <= 0) { Xorriso_msgs_submit(xorriso, 0, "-mount*: Cannot determine current system type", 0, "FAILURE", 0); {ret= 0; goto ex;} } else if(strcmp(sysname, "FreeBSD") == 0 || strcmp(sysname, "GNU/kFreeBSD") == 0) { /* "GNU/kFreeBSD" = Debian kfreebsd */ sys_code= 2; } else if(strcmp(sysname, "NetBSD") == 0) { sys_code= 3; } else if(strcmp(sysname, "Linux") == 0) { sys_code= 1; } else { sprintf(xorriso->info_text, "-mount*: Unsupported system type %s", Text_shellsafe(sysname, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } } if(sys_code == 1) { /* GNU/Linux */ sprintf(form, "%smount -t iso9660 -o %snodev,noexec,nosuid,ro,sbsector=%%sbsector%% %%device%% %s", (flag & 1 ? "/bin/" : ""), (reg_file || (xorriso->mount_opts_flag & 1) ? "loop," : ""), Text_shellsafe(cpt, sfe, 0)); is_safe= 1; } else if(sys_code == 2 || sys_code == 3) { /* FreeBSD , NetBSD */ if(reg_file) { /* <<< Considered to create vnode as of J.R. Oldroyd <fbsd@opal.com>, 20 Nov 2008 but for now refraining from creating that persistent file object strcpy(form, "n=$(mdconfig -a -t vnode -f %device%)"); sprintf(form + strlen(form), " && mount -t cd9660 -o noexec,nosuid -s %%sbsector%% /dev/\"$n\" %s", Text_shellsafe(cmd+8, sfe, 0)); */ Xorriso_msgs_submit(xorriso, 0, "Detected regular file as mount device with BSD style command.", 0, "FAILURE", 0); if(sys_code == 2) { Xorriso_msgs_submit(xorriso, 0, "Command mdconfig -a -t vnode -f can create a device node which uses the file", 0, "HINT", 0); } else { Xorriso_msgs_submit(xorriso, 0, "Command vnconfig -c vndX can create a device node which uses the file", 0, "HINT", 0); } {ret= 0; goto ex;} } else { if(sys_code == 3 && strncmp(devadr, "/dev/rcd", 8) == 0) { sprintf(cooked_dev, "/dev/cd%s", devadr + 8); eff_dev= cooked_dev; } sprintf(form, "%smount_cd9660 -o noexec,nosuid -s %%sbsector%% %%device%% %s", (flag & 1 ? "/sbin/" : ""), Text_shellsafe(cpt, sfe, 0)); } is_safe= 1; } sprintf(session_text, "%d", session); sprintf(track_text, "%d", track); sprintf(lba_text, "%.f", (double) lba); vars[0][0]= "sbsector"; vars[0][1]= lba_text; vars[1][0]= "track"; vars[1][1]= track_text; vars[2][0]= "session"; vars[2][1]= session_text; vars[3][0]= "volid"; vars[3][1]= Text_shellsafe(volid, volid_sfe, 0); vars[4][0]= "device"; vars[4][1]= Text_shellsafe(eff_dev, sfe, 0); ret= Sregex_resolve_var(form, vars, 5, "%", "%", "%", result, SfileadrL, 0); if(ret <= 0) goto ex; ret= 1 + is_safe; ex:; Xorriso_free_meM(cooked_dev); Xorriso_free_meM(volid_sfe); Xorriso_free_meM(sfe); Xorriso_free_meM(form); return(ret); } int Xorriso_append_scdbackup_record(struct XorrisO *xorriso, int flag) { FILE *fp= NULL; char dummy[81], name[81], timestamp[81], size[81], md5[81]; if(xorriso->scdbackup_tag_written[0] == 0) return(1); name[0]= timestamp[0]= size[0]= md5[0]= 0; sscanf(xorriso->scdbackup_tag_written, "%s %s %s %s %s %s %s", dummy, dummy, dummy, name, timestamp, size, md5); sprintf(xorriso->info_text, "scdbackup tag written : %s %s %s %s\n", name, timestamp, size, md5); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); if(xorriso->scdbackup_tag_listname[0]) { fp= fopen(xorriso->scdbackup_tag_listname, "a"); if(fp==0) { strcpy(xorriso->info_text, "-scdbackup_tag: Cannot open file "); Text_shellsafe(xorriso->scdbackup_tag_listname, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } fprintf(fp, "%s %s %s %s\n", name, timestamp, size, md5); fclose(fp); } return(1); } /* @param flag bit0= for append rather than overwrite */ int Xorriso_is_concat_target(struct XorrisO *xorriso, char *target, int *ftype, int *fd, int flag) { int ret; char *why= ""; *ftype= 0; *fd = -1; if(strcmp(target, "-") == 0) { *fd= 1; *ftype= 8; /* character device */ return(1); } *ftype= Sfile_type(target, 1 | 8 | 16); if(*ftype == -1) return(2); /* not yet existing regular */ if(*ftype & 1024) { *fd= Sfile_get_dev_fd_no(target, 0); *ftype &= ~1024; } if(*ftype & 2048) { why= "fstat(2) returned -1 on file descriptor number."; goto not_usable; } if(*ftype == 3) { if(!xorriso->do_follow_concat) { why= "May not follow symbolic link. No -follow \"...:concat:...\"."; goto not_usable; } *ftype= Sfile_type(target, 1 | 4 | 8); if(*ftype == -1) return(2); /* not yet existing regular */ } if(*ftype == 2) { why= "May not write data into a directory."; goto not_usable; } if(*ftype == 0) { why= "Cannot determine file type."; goto not_usable; } if(*ftype == 7) { /* >>> what to do with UNIX socket ? */; why= "Cannot yet handle socket file as target."; goto not_usable; } if(xorriso->do_overwrite != 1 && xorriso->do_overwrite != 2) { why= "May not alter existing file."; goto not_usable; } ret= Xorriso_reassure_restore(xorriso, target, (2 + (flag & 1)) << 3); if(ret <= 0 || ret == 0) { why= "User revoked alteration of existing file."; goto not_usable; } if(*ftype == 1) return(2); /* existing regular */ if(*ftype == 4 || *ftype == 6 || *ftype == 8) return(1); /* named pipe, block device, character device */ not_usable:; sprintf(xorriso->info_text, "Unsuitable -concat target: "); Text_shellsafe(target, xorriso->info_text, 1); sprintf(xorriso->info_text + strlen(xorriso->info_text), ". %s", why); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } int Xorriso_concat(struct XorrisO *xorriso, char *mode, char *target, int progc, char **progv, int filec, char **filev, int flag) { int ret, i, fd= -1, target_is_regular= 0, fd_opened= 0, ftype, prog_forked= 0; int status; int open_mode= O_WRONLY | O_CREAT; struct stat stbuf; pid_t forked_pid; for(i= 0; i < filec; i++) { ret= Xorriso_iso_lstat(xorriso, filev[i], &stbuf, 4); if(ret == -1) goto ex; if(!S_ISREG(stbuf.st_mode)) { sprintf(xorriso->info_text, "-concat: iso_rr_path is not a regular data file: "); Text_shellsafe(filev[i], xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } if(strcmp(mode, "overwrite") == 0) { ret= Xorriso_is_concat_target(xorriso, target, &ftype, &fd, 0); if(ret <= 0) goto ex; if(ret == 2) { target_is_regular= 1; open_mode |= O_TRUNC; } if(fd == -1) { fd= open(target, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666); fd_opened= 1; } } else if(strcmp(mode, "append") == 0) { ret= Xorriso_is_concat_target(xorriso, target, &ftype, &fd, 1); if(ret <= 0) goto ex; target_is_regular= (ret == 2); if(fd == -1) { fd= open(target, O_WRONLY | O_CREAT | O_BINARY, 0666); fd_opened= 1; if(fd != -1 && target_is_regular) { ret= lseek(fd, (off_t) 0, SEEK_END); if(ret == -1) { sprintf(xorriso->info_text, "-concat append: Cannot lseek(2) to file end of "); Text_shellsafe(target, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } } } else if(strcmp(mode, "pipe") == 0) { ret= Xorriso_pipe_open(xorriso, "-concat pipe", progv[0], progc, progv, "", &fd, &forked_pid, 2 | 8); if(ret <= 0) goto ex; fd_opened= 1; prog_forked= 1; } else { sprintf(xorriso->info_text, "-concat: Unknown mode "); Text_shellsafe(mode, xorriso->info_text, 1); strcat(xorriso->info_text, ". Known modes: overwrite, append, pipe"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(fd == -1) { sprintf(xorriso->info_text, "-concat: Cannot open file handle to "); Text_shellsafe(target, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } for(i= 0; i < filec; i++) { ret= Xorriso_iso_file_to_fd(xorriso, filev[i], fd, 0); if(ret <= 0) { if(i < filec - 1) { ret= Xorriso_eval_problem_status(xorriso, ret, 1 | 2); if(ret < 0) { sprintf(xorriso->info_text, "-concat: Aborted although %d files stay unprocessed.", filec - i + 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } } } ret= 1; ex:; if(fd_opened && fd != -1) close(fd); if(prog_forked) Xorriso_wait_child_end(xorriso, forked_pid, &status, 0); return(ret); } /* flag: bit0= *capacity is a desired value. Try SEEK_SET with *capacity bit1= open for writing return: 0= no random access , -1= cannot open path 1= *capacity is valid */ int Xorriso_lseek_capacity(struct XorrisO *xorriso, char *path, off_t *capacity, int flag) { int fd; off_t seek_result; if(flag & 2) fd= open(path, O_WRONLY); else fd= open(path, O_RDONLY); if(fd < 0) { sprintf(xorriso->info_text, "Cannot open for determination of random-access capacity: "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); return(-1); } if(flag & 1) { seek_result= lseek(fd, *capacity, SEEK_SET); } else { seek_result= lseek(fd, 0, SEEK_END); } close(fd); if(seek_result < 0) return(0); *capacity= seek_result; return(1); } /* flag: bit0= *capacity is a desired value. If SEEK_END fails, try SEEK_SET with *capacity bit1= open for writing return: 0= no random access , -1= path does not exist , -2= wrong type -3= cannot open path 1= *capacity is valid */ int Xorriso_determine_capacity(struct XorrisO *xorriso, char *path, off_t *capacity, char **reason, int flag) { int ret; off_t src_size, src_seek_size= -1; struct stat stbuf; if(reason != NULL) *reason= "offers no random access"; if(lstat(path, &stbuf) == -1) { *capacity= 0; if(reason != NULL) *reason= "does not exist"; return(-1); } if(S_ISREG(stbuf.st_mode)) { src_size= stbuf.st_size; } else if(!(S_ISDIR(stbuf.st_mode) || S_ISLNK(stbuf.st_mode) || S_ISFIFO(stbuf.st_mode) || S_ISSOCK(stbuf.st_mode))) { ret= Xorriso_lseek_capacity(xorriso, path, &src_size, flag & 2); if(ret <= 0 || src_size <= 0) { if(ret == -1 || !(flag & 1)) { *capacity= 0; if(ret == -1) { if(reason != NULL) *reason= "cannot be opened"; return(-3); } return(ret > 0); } if(ret > 0) src_seek_size= 0; src_size= *capacity; ret= Xorriso_lseek_capacity(xorriso, path, &src_size, flag & 3); if(ret <= 0 || src_size < src_seek_size) { if(src_seek_size == 0) { src_size= src_seek_size; } else { *capacity= 0; if(ret == -1) { if(reason != NULL) *reason= "cannot be opened"; return(-3); } return(0); } } } } else { *capacity= 0; if(reason != NULL) *reason= "is of wrong type"; return(-2); } *capacity= src_size; if(reason != NULL) *reason= ""; return(1); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of class DirseQ which crawls along a directory's content list. */ #ifndef Xorriso_pvt_diskop_includeD #define Xorriso_pvt_diskop_includeD yes /* @param flag bit0= simple readlink(): no normalization, no multi-hop */ int Xorriso_resolve_link(struct XorrisO *xorriso, char *link_path, char result_path[SfileadrL], int flag); int Xorriso_convert_gidstring(struct XorrisO *xorriso, char *gid_string, gid_t *gid, int flag); int Xorriso_convert_modstring(struct XorrisO *xorriso, char *cmd, char *mode, mode_t *mode_and, mode_t *mode_or, int flag); int Xorriso_convert_uidstring(struct XorrisO *xorriso, char *uid_string, uid_t *uid, int flag); /* @param flag bit0= for Xorriso_msgs_submit: use pager */ int Xorriso_hop_link(struct XorrisO *xorriso, char *link_path, struct LinkiteM **link_stack, struct stat *stbuf, int flag); int Xorriso__mode_to_perms(mode_t st_mode, char perms[11], int flag); int Xorriso_format_ls_l(struct XorrisO *xorriso, struct stat *stbuf, int flag); /* @param flag bit0= long format bit1= do not print count of nodes bit2= du format bit3= print directories as themselves (ls -d) */ int Xorriso_lsx_filev(struct XorrisO *xorriso, char *wd, int filec, char **filev, off_t boss_mem, int flag); /* @param flag bit0= do not only sum up sizes but also print subdirs bit1= this is a recursion bit2= do not report result by Xorriso_result() @return <=0 error , 1 ok , 2 could not open directory */ int Xorriso_show_dux_subs(struct XorrisO *xorriso, char *abs_path, char *rel_path, off_t *size, off_t boss_mem, struct LinkiteM *link_stack, int flag); /* @param flag >>> bit0= remove whole sub tree: rm -r bit1= remove empty directory: rmdir bit2= recursion: do not reassure in mode 2 "tree" bit3= this is for overwriting and not for plain removal bit4= count deleted files in xorriso->pacifier_count bit5= with bit0 only remove directory content, not the directory @return <=0 = error 1 = removed leaf file object 2 = removed directory or tree 3 = did not remove on user revocation */ int Xorriso_rmx(struct XorrisO *xorriso, off_t boss_mem, char *path, int flag); int Xorriso_findx(struct XorrisO *xorriso, struct FindjoB *job, char *abs_dir_parm, char *dir_path, struct stat *dir_stbuf, int depth, struct LinkiteM *link_stack, int flag); /* @param flag bit0= no hardlink reconstruction bit1= do not set xorriso->node_*_prefixes bit5= -extract_single: eventually do not insert directory tree */ int Xorriso_restore_sorted(struct XorrisO *xorriso, int count, char **src_array, char **tgt_array, int *problem_count, int flag); /* @param flag bit0= path is a directory bit2= recursion: do not reassure in mode 2 "tree" bit3= this is for overwriting and not for plain removal */ int Xorriso_reassure_restore(struct XorrisO *xorriso, char *path, int flag); /* @param flag bit7= return 4 if restore fails from denied permission do not issue error message @return <=0 failure , 1 success , 4 with bit7: permission to create file was denied */ int Xorriso_make_tmp_path(struct XorrisO *xorriso, char *orig_path, char *tmp_path, int *fd, int flag); /* @param flag bit0= change regardless of xorriso->do_auto_chmod bit1= desired is only rx */ int Xorriso_auto_chmod(struct XorrisO *xorriso, char *disk_path, int flag); int Xorriso_make_accessible(struct XorrisO *xorriso, char *disk_path,int flag); /* @param flag bit0= prefer to find a match after *img_prefixes (but deliver img_prefixes if no other can be found) */ int Xorriso_make_restore_path(struct XorrisO *xorriso, struct Xorriso_lsT **img_prefixes, struct Xorriso_lsT **disk_prefixes, char img_path[SfileadrL], char disk_path[SfileadrL], int flag); int Xorriso_restore_make_hl(struct XorrisO *xorriso, char *old_path, char *new_path, int flag); int Xorriso_afile_fopen(struct XorrisO *xorriso, char *filename, char *mode, FILE **ret_fp, int flag); int Xorriso_make_mount_cmd(struct XorrisO *xorriso, char *cmd, off_t lba, int track, int session, char *volid, char *devadr, char result[SfileadrL], int flag); int Xorriso_append_scdbackup_record(struct XorrisO *xorriso, int flag); int Xorriso_concat(struct XorrisO *xorriso, char *mode, char *target, int progc, char **progv, int filec, char **filev, int flag); int Xorriso_determine_capacity(struct XorrisO *xorriso, char *path, off_t *capacity, char **reason, int flag); #endif /* ! Xorriso_pvt_diskop_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains functions which operate on drives and media. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include <pthread.h> #include "xorriso.h" #include "xorriso_private.h" #include "lib_mgt.h" #include "drive_mgt.h" #include "iso_img.h" #include "sort_cmp.h" #include "write_run.h" #include "read_run.h" static const char *un0(const char *text) { if(text == NULL) return(""); return(text); } int Xorriso_auto_driveadr(struct XorrisO *xorriso, char *adr, char *result, int flag) { int ret, is_known_mmc= 0, does_exist= 0; char *path_pt, *libburn_adr= NULL; char *abs_pt, *abs_adr= NULL; struct stat stbuf; Xorriso_alloc_meM(libburn_adr, char, BURN_DRIVE_ADR_LEN + SfileadrL); Xorriso_alloc_meM(abs_adr, char, SfileadrL); path_pt= adr; if(strncmp(adr, "stdio:", 6) == 0) path_pt= adr + 6; else if(strncmp(adr, "mmc:", 4) == 0) path_pt= adr + 4; /* <<< replace by Xorriso_normalize_img_path() ? */; if(path_pt[0] != '/') { abs_pt= getcwd(abs_adr, SfileadrL - 1); if(abs_pt == NULL) { Xorriso_msgs_submit(xorriso, 0, "Relative drive path given. Cannot determine working directory.", errno, "FAILURE", 0); {ret= -1; goto ex;} } ret= Sfile_add_to_path(abs_adr, path_pt, 0); if(ret <= 0) {ret= -1; goto ex;} } is_known_mmc= burn_drive_convert_fs_adr(path_pt, libburn_adr); does_exist= (stat(path_pt, &stbuf) != -1); Xorriso_process_msg_queues(xorriso,0); ret= Xorriso_is_in_patternlist(xorriso, xorriso->drive_whitelist, path_pt, 0); if(ret > 0) goto ok; ret= Xorriso_is_in_patternlist(xorriso, xorriso->drive_blacklist, path_pt, 0); if(ret < 0) goto ex; if(ret) { strcpy(xorriso->info_text, "Drive address "); Text_shellsafe(adr, xorriso->info_text, 1); strcat(xorriso->info_text, " rejected because: -drive_class 'banned' "); Text_shellsafe(Xorriso_get_pattern(xorriso, xorriso->drive_blacklist, ret - 1, 0), xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } /* if in greylist and not MMC and not stdio prefix: reject */ if(is_known_mmc < 0) goto ex; if(adr == path_pt && !is_known_mmc) { /* no prefix, no MMC */ ret= Xorriso_is_in_patternlist(xorriso, xorriso->drive_greylist, path_pt,0); if(ret < 0) goto ex; if(ret) { strcpy(xorriso->info_text, "Drive address "); Text_shellsafe(adr, xorriso->info_text, 1); strcat(xorriso->info_text, " rejected because: "); if(does_exist) strcat(xorriso->info_text, "not MMC"); else strcat(xorriso->info_text, "not existing"); strcat(xorriso->info_text, " and -drive_class 'caution' "); Text_shellsafe(Xorriso_get_pattern(xorriso,xorriso->drive_greylist, ret - 1, 0), xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text, "If the address is a legitimate %s, prepend \"stdio:\"", does_exist ? "target" : "address for a new regular file"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); {ret= 0; goto ex;} } } ok:; if(strncmp(adr, "mmc:", 4) == 0) { if(Sfile_str(result, path_pt, 0) <= 0) {ret= 0; goto ex;} } else if(adr == path_pt && is_known_mmc <= 0) { Sfile_str(result, "stdio:", 0); if(Sfile_str(result, adr, 1) <= 0) {ret= 0; goto ex;} } else { if(Sfile_str(result, adr, 0) <= 0) {ret= 0; goto ex;} } if(strncmp(result, "stdio:", 6)==0) { if(xorriso->ban_stdio_write) { strcpy(xorriso->info_text, "Drive address banned by -ban_stdio_write : "); Text_shellsafe(result, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } } ret= 1; ex:; Xorriso_free_meM(libburn_adr); Xorriso_free_meM(abs_adr); return(ret); } static int Xorriso_grasp_loaded_aaip(struct XorrisO *xorriso, IsoImage *volset, int flag) { int ret, change_pending_rec; IsoNode *root_node; size_t value_length; char *value= NULL; double num; struct FindjoB *job= NULL; struct stat dir_stbuf; /* To be re-instated at function end */ change_pending_rec= xorriso->volset_change_pending; /* Look for isofs.st and put it into xorriso->isofs_st_in */ root_node= (IsoNode *) iso_image_get_root(volset); ret= iso_node_lookup_attr(root_node, "isofs.st", &value_length, &value, 0); if(ret > 0) { if(value_length > 0) { sscanf(value, "%lf", &num); if(num > 0) xorriso->isofs_st_in= num; } free(value); } if(!(xorriso->do_aaip & (1 << 11))) { /* lfa_flags not enabled for "read" */ ret= Xorriso_remove_all_lfa_flags(xorriso, 0); if(ret <= 0) goto ex; } if(xorriso->do_hfsplus) { /* Bring isofs.hx to iso_hfsplus_xinfo_func, isofs.hb to IsoImage blessings */ ret= Findjob_new(&job, "/", 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "xorriso", 0); {ret= -1; goto ex;} } Findjob_set_action_target(job, 49, NULL, 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, "/", &dir_stbuf, 0, 0); if(ret <= 0) goto ex; Findjob_destroy(&job, 0); } ret= 1; ex:; xorriso->volset_change_pending= change_pending_rec; Findjob_destroy(&job, 0); return(ret); } /* @param flag: bit0= set read speed bit1= set write speed */ int Xorriso_set_speed(struct XorrisO *xorriso, struct burn_drive *drive, int read_speed, int write_speed, int flag) { int r_speed = 0, w_speed = 0, ret = 0, profile_no= 0; char profile_name[80]; if((flag & 3) == 0) return(0); if(xorriso->read_speed == -2) { if(!(flag & 2)) return(0); } if(flag & 1) r_speed= read_speed; if(flag & 2) w_speed= write_speed; ret= burn_disc_get_profile(drive, &profile_no, profile_name); if(ret <= 0) profile_no= 0; if((r_speed > 0 || w_speed > 0) && profile_no >= 0x10) { ret= burn_drive_set_speed_exact(drive, r_speed, w_speed); if(ret > 0) goto ex; } burn_drive_set_speed(drive, r_speed, w_speed); ret= 2; ex:; Xorriso_process_msg_queues(xorriso,0); return(ret); } /* @param flag bit0= no info message "Loading ISO image tree" */ int Xorriso_make_read_options(struct XorrisO *xorriso, struct burn_drive *drive, struct isoburn_read_opts **ropts, int flag) { int ret, ext; off_t load_lba; enum burn_disc_status state; /* fill read opts */ ret= isoburn_ropt_new(ropts, 0); if(ret<=0) goto ex; ret= Xorriso_set_data_cache(xorriso, *ropts, xorriso->cache_num_tiles, xorriso->cache_tile_blocks, xorriso->cache_default); if(ret<=0) goto ex; ext= isoburn_ropt_noiso1999; if(xorriso->read_fs & 1) ext|= isoburn_ropt_norock; if(xorriso->read_fs & 2) ext|= isoburn_ropt_nojoliet; if((xorriso->ino_behavior & (1 | 2)) && !(xorriso->do_aaip & (1 | 4 | 32 | (1 << 11))) && !(xorriso->do_md5 & 1) && !(xorriso->do_hfsplus)) ext|= isoburn_ropt_noaaip; if(!(xorriso->do_aaip & 1)) ext|= isoburn_ropt_noacl; if(!(xorriso->do_aaip & 4)) ext|= isoburn_ropt_noea; if(xorriso->do_aaip & (1 << 11)) ext|= isoburn_ropt_lfa_flags; if(xorriso->do_aaip & (1 << 15)) ext|= isoburn_ropt_lfa_only_settable; if(xorriso->ino_behavior & 1) ext|= isoburn_ropt_noino; if(!(xorriso->do_md5 & 1)) ext|= isoburn_ropt_nomd5; if(xorriso->do_md5 & 32) ext|= isoburn_ropt_nomd5tag; if(xorriso->ecma119_map == 0) ext|= isoburn_ropt_map_unmapped; else if(xorriso->ecma119_map == 2) ext|= isoburn_ropt_map_uppercase; else if(xorriso->ecma119_map == 3) ext|= isoburn_ropt_map_lowercase; else ext|= isoburn_ropt_map_stripped; if(xorriso->joliet_map == 0) ext|= isoburn_ropt_joliet_unmapped; else ext|= isoburn_ropt_joliet_stripped; isoburn_ropt_set_extensions(*ropts, ext); isoburn_ropt_set_default_perms(*ropts, (uid_t) 0, (gid_t) 0, (mode_t) 0555); isoburn_ropt_set_input_charset(*ropts, xorriso->in_charset); isoburn_ropt_set_auto_incharset(*ropts, !!(xorriso->do_aaip & 512)); isoburn_ropt_set_displacement(*ropts, xorriso->displacement, xorriso->displacement_sign); isoburn_ropt_set_truncate_mode(*ropts, 1, xorriso->file_name_limit); Xorriso_set_image_severities(xorriso, 1); /* No DEBUG messages */ /* <<< Trying to work around too much tolerance on bad image trees. Better would be a chance to instruct libisofs what to do in case of image read errors. There is a risk to mistake other SORRYs. */ if(xorriso->img_read_error_mode>0) iso_set_abort_severity("SORRY"); state= isoburn_disc_get_status(drive); if(state != BURN_DISC_BLANK) { ret= isoburn_disc_get_msc1_v2(drive, &load_lba); if(ret > 0 && !(flag & 1)) { sprintf(xorriso->info_text, "Loading ISO image tree from LBA %.f", (double) load_lba); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } ret= Xorriso_assert_volid(xorriso, load_lba, 0); if(ret <= 0) goto ex; } Xorriso_set_speed(xorriso, drive, xorriso->read_speed, 0, 1); ret= 1; ex: return(ret); } /* @param flag bit0= acquire as isoburn input drive bit1= acquire as libburn output drive (as isoburn drive if bit0) bit2= regard overwritable media as blank bit3= if the drive is a regular disk file: truncate it to the write start address bit5= do not print toc bit6= do not calm down drive after acquiring it bit7= re-assess rather than acquire: Do not give up drives, use isoburn_drive_aquire() with re-assessment bit @return <=0 failure , 1= ok 2=success, but not writeable with bit1 3=success, but not blank and not ISO with bit0 */ int Xorriso_aquire_drive(struct XorrisO *xorriso, char *adr, char *show_adr, int flag) { int ret, hret, not_writeable= 0, has_what, aquire_flag; int track, session, params_flag, adr_mode, read_ret; off_t lba, start_lba; int truncate_mode; uint32_t size, offst; struct burn_drive_info *dinfo= NULL, *out_dinfo= NULL, *in_dinfo= NULL; struct burn_drive *drive= NULL, *out_drive= NULL, *in_drive= NULL; enum burn_disc_status state; IsoImage *volset = NULL; struct isoburn_read_opts *ropts= NULL; char *libburn_adr= NULL, *off_adr= NULL, *boot_fate, *sev; char volid[33], *adr_data= NULL, *adr_pt; Xorriso_alloc_meM(libburn_adr, char, SfileadrL); Xorriso_alloc_meM(off_adr, char, SfileadrL); Xorriso_alloc_meM(adr_data, char, 163); if(show_adr == NULL) { show_adr= adr; ret= burn_drive_convert_fs_adr(adr, off_adr); if(ret > 0) adr= off_adr; } if((flag&3)==0) { sprintf(xorriso->info_text, "XORRISOBURN program error : Xorriso_aquire_drive bit0+bit1 not set"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } if(!(flag & 128)) { ret= Xorriso_give_up_drive(xorriso, (flag&3)|8); if(ret<=0) goto ex; } if(flag & 1) xorriso->isofs_st_out= time(0) - 1; ret= Xorriso_auto_driveadr(xorriso, adr, libburn_adr, 0); if(ret <= 0) goto ex; if(strcmp(libburn_adr,"stdio:/dev/fd/1")==0) { if(xorriso->dev_fd_1<0) { sprintf(xorriso->info_text, "-*dev \"stdio:/dev/fd/1\" was not a start argument. Cannot use it now."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } else { sprintf(libburn_adr, "stdio:/dev/fd/%d", xorriso->dev_fd_1); } } if(flag & 128) { if(flag & 1) Xorriso_get_drive_handles(xorriso, &in_dinfo, &in_drive, "", 16); if(flag & 2) Xorriso_get_drive_handles(xorriso, &out_dinfo, &out_drive, "", 2 | 16); if(in_dinfo != NULL && (out_dinfo == NULL || out_dinfo == in_dinfo)) { dinfo= in_dinfo; if(flag & 2) { xorriso->outdev_is_exclusive= xorriso->indev_is_exclusive; xorriso->outdev_access= xorriso->indev_access; } } else if(out_dinfo != NULL && in_dinfo == NULL) { dinfo= out_dinfo; if(flag & 1) { xorriso->indev_is_exclusive= xorriso->outdev_is_exclusive; xorriso->indev_access= xorriso->outdev_access; } } else if(out_dinfo != NULL || in_dinfo != NULL) { sprintf(xorriso->info_text, "Two different drives shall be re-assed in one call"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= 0; goto ex;} } else { sprintf(xorriso->info_text, "No drive acquired on re-assessment"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= 0; goto ex;} } } else if((flag&3)==1 && xorriso->out_drive_handle!=NULL) { ret= Xorriso_get_drive_handles(xorriso, &out_dinfo, &out_drive, "on attempt to compare new indev with outdev", 2); if(ret<=0) goto ex; ret= burn_drive_equals_adr(out_drive, libburn_adr, 1); if(ret==1) { dinfo= out_dinfo; xorriso->indev_is_exclusive= xorriso->outdev_is_exclusive; xorriso->indev_access= xorriso->outdev_access; } } else if((flag&3)==2 && xorriso->in_drive_handle!=NULL) { ret= Xorriso_get_drive_handles(xorriso, &in_dinfo, &in_drive, "on attempt to compare new outdev with indev", 0); if(ret<=0) goto ex; ret= burn_drive_equals_adr(in_drive, libburn_adr, 1); if(ret==1) { dinfo= in_dinfo; xorriso->outdev_is_exclusive= xorriso->indev_is_exclusive; xorriso->outdev_access= xorriso->indev_access; } } if(dinfo == NULL || (flag & 128)) { aquire_flag= 1 | ((flag&(8|4))>>1) | ((xorriso->toc_emulation_flag & 3)<<3); if(xorriso->toc_emulation_flag & 4) aquire_flag|= 128; if(xorriso->toc_emulation_flag & 8) aquire_flag|= 512; if(!(xorriso->do_aaip & 1)) aquire_flag|= 32; if((xorriso->ino_behavior & (1 | 2)) && !(xorriso->do_aaip & (4 | 32))) { aquire_flag|= 64; } else if(xorriso->do_aaip & 1024) { aquire_flag|= 1024; } if(xorriso->do_aaip & (1 << 11)) aquire_flag|= 1 << 11; if(xorriso->do_aaip & (1 << 15)) aquire_flag|= 1 << 15; if(flag & 128) aquire_flag|= 256; burn_preset_device_open(xorriso->drives_exclusive | (xorriso->linux_scsi_dev_family << 2), 0, 0); burn_allow_drive_role_4(1 | (xorriso->early_stdio_test & 14)); ret= isoburn_drive_aquire(&dinfo, libburn_adr, aquire_flag); burn_preset_device_open(1 | (xorriso->linux_scsi_dev_family << 2), 0, 0); Xorriso_process_msg_queues(xorriso,0); if(ret<=0) { if(flag & 128) sprintf(xorriso->info_text,"Cannot re-assess drive '%s'", adr); else sprintf(xorriso->info_text,"Cannot acquire drive '%s'", adr); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } xorriso->use_immed_bit_default= burn_drive_get_immed(dinfo[0].drive) > 0 ? 1 : -1; if(xorriso->use_immed_bit != 0) burn_drive_set_immed(dinfo[0].drive, xorriso->use_immed_bit > 0); state= isoburn_disc_get_status(dinfo[0].drive); ret= isoburn_get_img_partition_offset(dinfo[0].drive, &offst); if((state == BURN_DISC_APPENDABLE || state == BURN_DISC_FULL) && ret == 1) { sprintf(xorriso->info_text, "ISO image bears MBR with -boot_image any partition_offset=%lu", (unsigned long) offst); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } if(flag&1) if(xorriso->image_start_mode&(1u<<31)) /* used up setting */ xorriso->image_start_mode= 0; /* no need to perform auto setting */ if(flag & 1) { xorriso->indev_is_exclusive= xorriso->drives_exclusive; xorriso->indev_access= xorriso->drives_access; } if(flag & 2) { xorriso->outdev_is_exclusive= xorriso->drives_exclusive; xorriso->outdev_access= xorriso->drives_access; } } drive= dinfo[0].drive; volset= isoburn_get_attached_image(drive); if(volset != NULL) { ret= iso_image_set_truncate_mode(volset, 1, xorriso->file_name_limit); iso_image_unref(volset); volset= NULL; Xorriso_process_msg_queues(xorriso,0); if(ret < 0) {ret= 0; goto ex;} } state= isoburn_disc_get_status(drive); Xorriso_process_msg_queues(xorriso,0); if(flag&1) { if(xorriso->image_start_mode&(1u<<31)) /* used up setting */ xorriso->image_start_mode&= ~0xffff; /* perform auto setting */ if((xorriso->image_start_mode&(1<<30))) { /* if enabled at all */ adr_pt= xorriso->image_start_value; adr_mode= xorriso->image_start_mode & 0xffff; if(adr_mode >= 4 && adr_mode <= 9 && strlen(adr_pt) <= 80) { /* Convert volid search expression or time constraint to lba */ ret= Xorriso_prepare_load_search(xorriso, "-indev", adr_mode, adr_pt, adr_data, ¶ms_flag, 0); if(ret <= 0) goto ex; ret= isoburn_get_mount_params_v2(drive, adr_mode, adr_data, &lba, &track, &session, volid, params_flag); Xorriso_process_msg_queues(xorriso,0); if(ret <= 0) goto ex; if(session <= 0 || track <= 0 || ret == 2) { Xorriso_msgs_submit(xorriso, 0, "-load : Given address does not point to an ISO 9660 session", 0, "FAILURE", 0); ret= 0; goto ex; } sprintf(volid, "%.f", (double) lba); adr_pt= volid; adr_mode= 3; } ret= isoburn_set_msc1(drive, adr_mode, adr_pt, !!(xorriso->image_start_mode & (1<<16))); if(ret<=0) goto ex; if(xorriso->image_start_mode&(1u<<31)) xorriso->image_start_mode= 0; /* disable msc1 setting completely */ else xorriso->image_start_mode|= (1u<<31); /* mark as used up */ } } if(flag&1) { volset= isoburn_get_attached_image(drive); if(volset != NULL) { /* The image object is already created */ iso_image_unref(volset); volset= NULL; } } if(flag&2) { xorriso->out_drive_handle= dinfo; if(Sfile_str(xorriso->outdev, show_adr, 0)<=0) {ret= -1; goto ex;} ret= burn_drive_convert_fs_adr(adr, xorriso->outdev_off_adr); if(ret <= 0) xorriso->outdev_off_adr[0]= 0; if(state != BURN_DISC_BLANK && state != BURN_DISC_APPENDABLE) { sprintf(xorriso->info_text, "Disc status unsuitable for writing"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); not_writeable= 1; } } if(flag&1) { xorriso->in_drive_handle= dinfo; if(Sfile_str(xorriso->indev, show_adr, 0)<=0) {ret= -1; goto ex;} ret= burn_drive_convert_fs_adr(adr, xorriso->indev_off_adr); if(ret <= 0) xorriso->indev_off_adr[0]= 0; } else if(flag&2) { if(xorriso->in_volset_handle==NULL) { /* No volume loaded: create empty one */ ret= Xorriso_create_empty_iso(xorriso, 0); if(ret<=0) goto ex; } else { iso_image_ref((IsoImage *) xorriso->in_volset_handle); start_lba= -1; ret= Xorriso_get_drive_handles(xorriso, &in_dinfo, &in_drive, "on attempt to attach ISO image object to outdev", 16); if(ret > 0) start_lba= isoburn_get_attached_start_lba_v2(in_drive); ret= isoburn_attach_image(drive, (IsoImage *) xorriso->in_volset_handle); if(ret<=0) { sprintf(xorriso->info_text, "Failed to attach ISO image object to outdev"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } if(start_lba >= 0) isoburn_attach_start_lba_v2(drive, start_lba, 0); } if(!(flag&32)) Xorriso_toc(xorriso, 1 | 2 | 8); {ret= 1+not_writeable; goto ex;} } if(xorriso->in_volset_handle!=NULL) iso_image_unref((IsoImage *) xorriso->in_volset_handle); xorriso->in_volset_handle= NULL; Sectorbitmap_destroy(&(xorriso->in_sector_map), 0); Xorriso_destroy_hln_array(xorriso, 0); Xorriso_destroy_di_array(xorriso, 0); xorriso->boot_count= 0; xorriso->system_area_clear_loaded= (strcmp(xorriso->system_area_disk_path, "/dev/zero") == 0); /* check for invalid state */ if(state != BURN_DISC_BLANK && state != BURN_DISC_APPENDABLE && state != BURN_DISC_FULL) { sprintf(xorriso->info_text, "Disc status not blank and unsuitable for reading"); sev= "FAILURE"; if(xorriso->img_read_error_mode==2) sev= "FATAL"; Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, sev, 0); Xorriso_give_up_drive(xorriso, 1|((flag&32)>>2)); ret= 3; goto ex; } ret= Xorriso_make_read_options(xorriso, drive, &ropts, 0); if(ret <= 0) goto ex; Xorriso_pacifier_reset(xorriso, 0); isoburn_set_read_pacifier(drive, Xorriso__read_pacifier, (void *) xorriso); read_ret= ret= isoburn_read_image(drive, ropts, &volset); /* <<< Resetting to normal thresholds, after Xorriso_make_read_options */ if(xorriso->img_read_error_mode>0) Xorriso_set_abort_severity(xorriso, 0); if(ret<=0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_set_image_severities(xorriso, 0); Xorriso_give_up_drive(xorriso, 1|((flag&32)>>2)); sprintf(xorriso->info_text,"Cannot read ISO image tree"); sev= "FAILURE"; if(xorriso->img_read_error_mode==2) sev= "FATAL"; Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, sev, 0); if(read_ret == (int) ISO_SB_TREE_CORRUPTED && (xorriso->do_md5 & 1)) { Xorriso_msgs_submit(xorriso, 0, "This might be false MD5 alarm if an add-on session was written by growisofs.", 0, "HINT", 0); Xorriso_msgs_submit(xorriso, 0, "In this case you get an ISO image tree by option -md5 'load_check_off'", 0, "HINT", 0); } else if(xorriso->img_read_error_mode!=0) { Xorriso_msgs_submit(xorriso, 0, "You might get a partial or altered ISO image tree by option -error_behavior 'image_loading' 'best_effort' if -abort_on is set to be tolerant enough.", 0, "HINT", 0); } ret= 3; goto ex; } Xorriso_pacifier_callback(xorriso, "nodes read", xorriso->pacifier_count, 0, "", 1); /* report end count */ xorriso->in_volset_handle= (void *) volset; xorriso->in_sector_map= NULL; Xorriso_set_image_severities(xorriso, 0); /* Might have changed due to isofs.nt */ iso_image_get_truncate_mode(volset, &truncate_mode, &(xorriso->file_name_limit)); Xorriso_process_msg_queues(xorriso,0); Xorriso_update_volid(xorriso, 0); strncpy(xorriso->application_id, un0(iso_image_get_application_id(volset)), 128); xorriso->application_id[128]= 0; strncpy(xorriso->publisher, un0(iso_image_get_publisher_id(volset)), 128); xorriso->publisher[128]= 0; strncpy(xorriso->system_id, un0(iso_image_get_system_id(volset)), 32); xorriso->system_id[32]= 0; strncpy(xorriso->volset_id, un0(iso_image_get_volset_id(volset)), 128); xorriso->volset_id[128]= 0; strncpy(xorriso->copyright_file, un0(iso_image_get_copyright_file_id(volset)), 37); xorriso->copyright_file[37]= 0; strncpy(xorriso->biblio_file, un0(iso_image_get_biblio_file_id(volset)), 37); xorriso->biblio_file[37]= 0; strncpy(xorriso->abstract_file, un0(iso_image_get_abstract_file_id(volset)), 37); xorriso->abstract_file[37]= 0; if(xorriso->out_drive_handle != NULL && xorriso->out_drive_handle != xorriso->in_drive_handle) { start_lba= -1; ret= Xorriso_get_drive_handles(xorriso, &in_dinfo, &in_drive, "on attempt to attach ISO image volset to outdev", 16); if(ret > 0) start_lba= isoburn_get_attached_start_lba_v2(in_drive); ret= Xorriso_get_drive_handles(xorriso, &out_dinfo, &out_drive, "on attempt to attach ISO image volset to outdev", 2); if(ret<=0) goto ex; iso_image_ref((IsoImage *) xorriso->in_volset_handle); isoburn_attach_image(out_drive, xorriso->in_volset_handle); if(start_lba >= 0) isoburn_attach_start_lba_v2(out_drive, start_lba, 0); } Xorriso_process_msg_queues(xorriso,0); isoburn_ropt_get_size_what(ropts, &size, &has_what); xorriso->isofs_size= size; xorriso->isofs_has_what= has_what; isoburn_ropt_get_tree_loaded(ropts, &(xorriso->tree_loaded), &(xorriso->rr_loaded)); if(has_what & isoburn_ropt_has_el_torito) { if(xorriso->boot_image_bin_path[0]) boot_fate= "replaced by new boot image"; else if(xorriso->patch_isolinux_image & 1) boot_fate= "patched at boot info table"; else if(xorriso->keep_boot_image) boot_fate= "kept unchanged"; else boot_fate= "discarded"; sprintf(xorriso->info_text, "Detected El-Torito boot information which currently is set to be %s", boot_fate); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); Xorriso_record_boot_info(xorriso, 0); } if(flag & 1) { ret= Xorriso_grasp_loaded_aaip(xorriso, volset, 0); if(ret <= 0) goto ex; } if(!(flag&32)) { Xorriso_toc(xorriso, 1 | 8); if(xorriso->loaded_volid[0] != 0 && (state == BURN_DISC_APPENDABLE || state == BURN_DISC_FULL)) { sprintf(xorriso->info_text,"Volume id : '%s'\n", xorriso->loaded_volid); Xorriso_info(xorriso, 0); } if(strcmp(xorriso->loaded_volid, xorriso->volid) != 0 && !xorriso->volid_default) { sprintf(xorriso->info_text, "New volume id: '%s'\n", xorriso->volid); Xorriso_info(xorriso, 0); } } ret= 1+not_writeable; ex: Xorriso_process_msg_queues(xorriso,0); if(ret<=0) { hret= Xorriso_give_up_drive(xorriso, (flag&3)|((flag&32)>>2)); if(hret<ret) ret= hret; } else { if(drive != NULL && (xorriso->do_calm_drive & 1) && !(flag & 64)) burn_drive_snooze(drive, 0); /* No need to make noise from start */ } if(ropts!=NULL) isoburn_ropt_destroy(&ropts, 0); Xorriso_free_meM(off_adr); Xorriso_free_meM(libburn_adr); Xorriso_free_meM(adr_data); return(ret); } /* @param flag bit0=input drive bit1=output drive bit2=eject bit3=no info message or toc */ int Xorriso_give_up_drive(struct XorrisO *xorriso, int flag) { int in_is_out_too, ret, do_eject; struct burn_drive_info *dinfo; struct burn_drive *drive; in_is_out_too= (xorriso->in_drive_handle == xorriso->out_drive_handle); if((flag&4) && in_is_out_too && (flag&(1|2))) { if((flag&3)!=3) { sprintf(xorriso->info_text,"Giving up for -eject whole -dev "); Text_shellsafe(xorriso->indev, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } flag|= 3; /* give up in/out drive to eject it */ } if((flag&1) && xorriso->in_drive_handle != NULL) { Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to give up drive", 0); if(!in_is_out_too) { do_eject= !!(flag&4); if((flag & 4) && xorriso->indev_access == 0) { sprintf(xorriso->info_text, "Will not eject medium in readonly acquired input drive."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); do_eject= 0; } if(drive!=NULL) isoburn_drive_release(drive, do_eject); if(dinfo!=NULL) burn_drive_info_free(dinfo); } xorriso->in_drive_handle= NULL; xorriso->indev[0]= 0; if(xorriso->in_volset_handle!=NULL) iso_image_unref((IsoImage *) xorriso->in_volset_handle); xorriso->in_volset_handle= NULL; Sectorbitmap_destroy(&(xorriso->in_sector_map), 0); Xorriso_destroy_di_array(xorriso, 0); Xorriso_destroy_hln_array(xorriso, 0); xorriso->loaded_volid[0]= 0; xorriso->isofs_st_out= time(0) - 1; xorriso->isofs_st_in= 0; xorriso->volset_change_pending= 0; xorriso->no_volset_present= 0; xorriso->loaded_boot_bin_lba= 0; xorriso->loaded_boot_cat_path[0]= 0; xorriso->boot_count= 0; in_is_out_too= 0; } if((flag&2) && xorriso->out_drive_handle!=NULL) { do_eject= !!(flag&4); if((flag & 4) && xorriso->outdev_access == 0) { sprintf(xorriso->info_text, "Will not eject medium in readonly acquired drive."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); do_eject= 0; } ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to give up drive", 2); if(ret >= 0 && !in_is_out_too) { if(drive!=NULL) isoburn_drive_release(drive, do_eject); if(dinfo!=NULL) burn_drive_info_free(dinfo); } xorriso->out_drive_handle= NULL; xorriso->outdev[0]= 0; xorriso->outdev_off_adr[0]= 0; } else if((flag&1) && xorriso->out_drive_handle!=NULL) { ret= Xorriso_create_empty_iso(xorriso, 0); if(ret<=0) return(ret); if(!(flag&8)) { sprintf(xorriso->info_text, "Only the output drive remains. Created empty ISO image.\n"); Xorriso_info(xorriso, 0); Xorriso_toc(xorriso, 1 | 2 | 8); } } Xorriso_process_msg_queues(xorriso,0); return(1); } int Xorriso_may_burn(struct XorrisO *xorriso, int flag) { if(xorriso->outdev_access == 1) return(1); sprintf(xorriso->info_text, "The output drive was acquired readonly."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text, "Possible remedy: -drive_access \"exclusive:unrestricted\"."); strcat(xorriso->info_text," Then give up and re-acquire the drive."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); if(!xorriso->outdev_is_exclusive) { sprintf(xorriso->info_text, "If you insist in -drive_access \"shared:unrestricted\", first read man xorriso about the risks."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); } return(0); } /* @param flag bit1=report about output drive rather than input drive bit2=do not try to read ISO heads */ int Xorriso_toc_to_string(struct XorrisO *xorriso, char **toc_text, int flag) { int ret, stack_handle, toc_ret, l; struct Xorriso_lsT *results= NULL, *infos= NULL, *lpt; *toc_text= NULL; ret= Xorriso_push_outlists(xorriso, &stack_handle, 1); if(ret <= 0) goto ex; toc_ret= Xorriso_toc(xorriso, flag & (2 | 4)); ret= Xorriso_pull_outlists(xorriso, stack_handle, &results, &infos, 0); if(ret <= 0) goto ex; if(toc_ret <= 0) {ret= toc_ret; goto ex;} l= 0; for(lpt= results; lpt != NULL; lpt= Xorriso_lst_get_next(lpt, 0)) l+= strlen(Xorriso_lst_get_text(lpt, 0)); *toc_text= calloc(l + 1, 1); l= 0; for(lpt= results; lpt != NULL; lpt= Xorriso_lst_get_next(lpt, 0)) { strcpy((*toc_text) + l, Xorriso_lst_get_text(lpt, 0)); l+= strlen(Xorriso_lst_get_text(lpt, 0)); } ex:; Xorriso_lst_destroy_all(&results, 0); Xorriso_lst_destroy_all(&infos, 0); return(ret); } /* @param flag bit0+1= what to give up and/or re-assess in what role 0=give up outdev 1=give up indev if not outdev, re-assess outdev as indev 2=re-assess outdev as outdev 3=give up indev if not outdev, re-assess outdev as dev */ int Xorriso_reaquire_outdev(struct XorrisO *xorriso, int flag) { int ret, aq_flag; char *drive_name= NULL, *off_name= NULL; Xorriso_alloc_meM(drive_name, char, SfileadrL); Xorriso_alloc_meM(off_name, char, SfileadrL); aq_flag= flag&3; strcpy(drive_name, xorriso->outdev); if(xorriso->outdev_off_adr[0]) strcpy(off_name, xorriso->outdev_off_adr); else strcpy(off_name, drive_name); if(aq_flag == 0) { Xorriso_give_up_drive(xorriso, 2); sprintf(xorriso->info_text,"Gave up -outdev "); Text_shellsafe(xorriso->indev, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); {ret= 1; goto ex;} } /* Only give up indev, and only if it is not outdev */; if(xorriso->in_drive_handle != xorriso->out_drive_handle && xorriso->in_drive_handle != NULL && (aq_flag & 1)) Xorriso_give_up_drive(xorriso, 1 | 8); sprintf(xorriso->info_text,"Re-assessing -outdev "); Text_shellsafe(drive_name, xorriso->info_text, 1); if(strcmp(drive_name, off_name) != 0) { strcat(xorriso->info_text, " ("); Text_shellsafe(off_name, xorriso->info_text, 1); strcat(xorriso->info_text, ")"); } Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); /* Re-assess outdev */ ret= Xorriso_aquire_drive(xorriso, off_name, drive_name, aq_flag | 128); if(ret<=0) { sprintf(xorriso->info_text,"Could not re-assess -outdev "); Text_shellsafe(drive_name, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto ex; } ret= 1; ex:; Xorriso_free_meM(drive_name); Xorriso_free_meM(off_name); return(ret); } /* @param flag bit3=report to info channel (else to result channel) */ int Xorriso_toc_line(struct XorrisO *xorriso, int flag) { if(!(flag & 8)) { Xorriso_result(xorriso,0); return(1); } strcpy(xorriso->info_text, xorriso->result_line); Xorriso_info(xorriso, 0); return(1); } /* @param flag bit1=report about output drive bit3=report to info channel (else to result channel) bit4=do no report failure if no drive acquired */ int Xorriso_media_product(struct XorrisO *xorriso, int flag) { int ret, profile_no; struct burn_drive_info *dinfo; struct burn_drive *drive; char *product_id= NULL, *media_code1= NULL, *media_code2= NULL; char *book_type= NULL, *manuf= NULL, profile_name[80], *respt; respt= xorriso->result_line; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to print media product info", flag & (2 | 16)); if(ret <= 0) return(ret); ret= burn_disc_get_media_id(drive, &product_id, &media_code1, &media_code2, &book_type, 0); if(ret > 0) { ret= burn_disc_get_profile(drive, &profile_no, profile_name); if(ret <= 0) return(ret); sprintf(respt, "Media product: %s , ", product_id); manuf= burn_guess_manufacturer(profile_no, media_code1, media_code2, 0); if(manuf != NULL) { if(strncmp(manuf, "Unknown ", 8) == 0) sprintf(respt + strlen(respt), "(not found in manufacturer list)\n"); else sprintf(respt + strlen(respt), "%s\n", manuf); } else sprintf(respt + strlen(respt), "(error during manufacturer lookup)\n"); free(product_id); free(media_code1); free(media_code2); if(book_type != NULL) free(book_type); if(manuf != NULL) free(manuf); Xorriso_toc_line(xorriso, flag & 8); } Xorriso_process_msg_queues(xorriso,0); return(1); } /* @param flag bit0=short report form bit1=report about output drive bit2=do not try to read ISO heads bit3=report to info channel (else to result channel) bit4=do no report failure if no drive acquired bit5=only report "Drive current" and "Drive type" bit6=report "Media product" with bit0 bit7=only report "Drive current" */ int Xorriso_toc(struct XorrisO *xorriso, int flag) { int num_sessions= 0, num_tracks= 0, ret; int track_count= 0, session_no, track_no, profile_no= -1; int is_data= 0; int is_inout_drive= 0, drive_role, status, num_formats; int not_recognizable= 0; int sessions_seen, open_sessions= 0, have_real_open_session= 0; char profile_name[80],*respt,*devadr, *typetext= ""; struct burn_toc_entry toc_entry; struct burn_drive_info *dinfo; struct burn_multi_caps *caps= NULL; struct burn_drive *drive; enum burn_disc_status s; char mem_text[80], *num_free_text, *num_data_text; off_t start_byte= 0, num_free= 0, size; unsigned dummy; struct isoburn_toc_disc *disc= NULL; struct isoburn_toc_session **sessions; struct isoburn_toc_track **tracks; char volume_id[33]; struct burn_toc_entry next_toc_entry; int disk_category, part_version, num_layers, num_blocks; char *book_name; int num_data_from_format= 0; char *sno = NULL; int sno_len, i, is_bdr_pow= 0, int_start_lba= -1, int_end_lba= -1; off_t lba= 0, nwa= -1, track_size, session_size, first_track_start= 0; off_t num_session_data, num_session_other, num_data= 0, other_data= 0; off_t emul_lba, end_lba, image_blocks, overburn_blocks= 0; int info_type; static char *info_type_name[]= {"", "Volume Id", "", "Creation Time", "Modification Time"}; static int max_info_type= 4; info_type= xorriso->toc_info_type; if(info_type < 1 || info_type > max_info_type) info_type= 1; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to print Table Of Content", flag & (2 | 16)); if(ret<=0) {ret= 0; goto ex;} respt= xorriso->result_line; if(strcmp(xorriso->indev, xorriso->outdev)==0) is_inout_drive= 1; if(flag&2) devadr= xorriso->outdev; else devadr= xorriso->indev; sprintf(respt, "Drive current: %s '%s'\n", (is_inout_drive ? "-dev" : (flag&2 ? "-outdev" : "-indev")), devadr); Xorriso_toc_line(xorriso, flag & 8); if(flag & 128) {ret= 1; goto ex;} /* Report -drive_access if non-default or if long form */ respt[0]= 0; if(flag & 2) { if(xorriso->outdev_is_exclusive == 0 || xorriso->outdev_access == 0 || !(flag & 33)) { sprintf(respt, "Drive access : %s:%s\n", xorriso->outdev_is_exclusive ? "exclusive" : "shared", xorriso->outdev_access == 0 ? "readonly" : "unrestricted"); } } else { if(xorriso->indev_is_exclusive == 0 || xorriso->indev_access == 0 || !(flag & 33)) { sprintf(respt, "Drive access : %s:%s\n", xorriso->indev_is_exclusive ? "exclusive" : "shared", xorriso->indev_access == 0 ? "readonly" : "unrestricted"); } } if(respt[0]) Xorriso_toc_line(xorriso, flag & 8); sprintf(respt, "Drive type : vendor '%s' product '%s' revision '%s'\n", dinfo[0].vendor, dinfo[0].product, dinfo[0].revision); if((flag & 32) || !(flag & 1)) Xorriso_toc_line(xorriso, flag & 8); if(flag & 32) {ret= 1; goto ex;} if(!(flag & 1)) { burn_drive_get_serial_no(drive, &sno, &sno_len); if(sno_len > 0) { sprintf(respt, "Drive id : '%s'\n", sno); Xorriso_toc_line(xorriso, flag & 8); } if(sno != NULL) free(sno); sno= NULL; } ret= burn_disc_get_profile(drive, &profile_no, profile_name); s= isoburn_disc_get_status(drive); if(profile_no == 0x0002 && s == BURN_DISC_EMPTY) profile_no= 0; is_bdr_pow= burn_drive_get_bd_r_pow(drive); sprintf(respt, "Media current: "); drive_role= burn_drive_get_drive_role(drive); if (profile_no > 0 && ret > 0) { if (profile_name[0]) sprintf(respt+strlen(respt), "%s", profile_name); else sprintf(respt+strlen(respt), "%4.4Xh", profile_no); if(is_bdr_pow) sprintf(respt+strlen(respt), ", Pseudo Overwrite formatted"); else if(drive_role==2) sprintf(respt+strlen(respt), ", overwriteable"); else if(drive_role == 4) sprintf(respt+strlen(respt), ", random read-only"); else if(drive_role == 5) sprintf(respt+strlen(respt), ", random write-only"); else if(drive_role==0 || drive_role==3) sprintf(respt+strlen(respt), ", sequential"); strcat(respt, "\n"); } else { sprintf(respt+strlen(respt), "is not recognizable\n"); not_recognizable= 1; } Xorriso_toc_line(xorriso, flag & 8); if((flag & 64) || !(flag & 1)) { Xorriso_media_product(xorriso, flag & (2 | 8 | 16)); if(xorriso->request_to_abort) {ret= 1; goto ex;} } sprintf(respt, "Media status : "); if(is_bdr_pow) { sprintf(respt+strlen(respt), "is unsuitable , is POW formatted"); } else if (s == BURN_DISC_FULL) { if(not_recognizable) sprintf(respt+strlen(respt), "is not recognizable\n"); else sprintf(respt+strlen(respt), "is written , is closed"); } else if (s == BURN_DISC_APPENDABLE) { sprintf(respt+strlen(respt), "is written , is appendable"); } else if (s == BURN_DISC_BLANK) { sprintf(respt+strlen(respt), "is blank"); } else if (s == BURN_DISC_EMPTY) sprintf(respt+strlen(respt), "is not present"); else sprintf(respt+strlen(respt), "is not recognizable"); if(s == BURN_DISC_APPENDABLE || s == BURN_DISC_BLANK) { ret= burn_disc_next_track_is_damaged(drive, 0); if(ret & 1) sprintf(respt+strlen(respt), " , but next track is damaged"); else if(ret & 2) sprintf(respt+strlen(respt), " , but no writable address"); else if(profile_no == 0x14) { /* DVD-RW sequential */ ret= burn_disc_get_multi_caps(drive, BURN_WRITE_TAO, &caps, 0); if(ret == 0) sprintf(respt+strlen(respt), " , but will need -close on"); if(caps != NULL) burn_disc_free_multi_caps(&caps); } else if(profile_no == 0x15) { /* DVD-RW DL */ sprintf(respt+strlen(respt), " , but will need -close \"on\""); } } strcat(respt, "\n"); Xorriso_toc_line(xorriso, flag & 8); if((s == BURN_DISC_FULL || s == BURN_DISC_APPENDABLE || s == BURN_DISC_BLANK) && !(flag & 1)) { burn_drive_get_media_sno(drive, &sno, &sno_len); if(sno_len > 0) { sprintf(respt, "Media id : "); respt+= strlen(respt); for(i= 0; i < sno_len && i < 1024; i++) { sprintf(respt, "%2.2X", (unsigned int) ((unsigned char *) sno)[i]); respt+= 2; } if(i < sno_len) strcat(respt, "..."); strcat(respt, "\n"); Xorriso_toc_line(xorriso, flag & 8); } if(sno != NULL) free(sno); sno= NULL; respt= xorriso->result_line; ret= burn_get_read_capacity_v2(drive, &num_data, 0); if(ret != 1 || s == BURN_DISC_BLANK) num_data= 0; num_free= isoburn_disc_available_space(drive, NULL) / 2048; nwa= -1; if (s == BURN_DISC_APPENDABLE) { ret= isoburn_disc_track_lba_nwa_v2(drive, NULL, 0, &lba, &nwa); if(ret <= 0) nwa= -1; } else if(s == BURN_DISC_BLANK) { ret= isoburn_disc_track_lba_nwa_v2(drive, NULL, 0, &lba, &nwa); if(ret == 1) { num_free+= nwa; nwa= 0; } } lba= num_data + num_free; if(nwa >= 0) { lba= nwa + num_free; if(nwa < num_data) num_data= nwa; } /* If closed CD-RW : obtain ATIP lead out address */ if(profile_no == 0x0a) { ret= burn_disc_read_atip(drive); if(ret < 0) goto ex; ret= burn_drive_get_start_end_lba(drive, &int_start_lba, &int_end_lba, 0); end_lba= int_end_lba; if(s == BURN_DISC_FULL && ret == 1) { if(lba > end_lba - 2) { overburn_blocks= lba - end_lba + 2; } else { lba= end_lba - 2; } } else { if(ret == 1 && end_lba - 2 > lba) { sprintf(xorriso->info_text, "ATIP end_lba %.f > overall %.f", (double) end_lba, (double) lba); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } } } else if(profile_no == 0x14) { ret= burn_disc_get_phys_format_info(drive, &disk_category, &book_name, &part_version, &num_layers, &num_blocks, 0); if(ret == 1 && num_blocks > lba) lba= num_blocks; } if(drive_role == 5) num_data_text= "occupied"; else num_data_text= "readable"; if(drive_role == 4 || is_bdr_pow) num_free_text = "unused"; else num_free_text = "writable"; sprintf(respt, "Media blocks : %.f %s , %.f %s , %.f overall\n", (double) num_data, num_data_text, (double) num_free, num_free_text, (double) lba); Xorriso_toc_line(xorriso, flag & 8); if(overburn_blocks > 0) { sprintf(respt, "Overburnt by : %.f blocks\n", (double) overburn_blocks); Xorriso_toc_line(xorriso, flag & 8); } } if(s == BURN_DISC_BLANK) { sprintf(respt, "Media summary: 0 sessions, 0 data blocks, 0 data"); num_free= isoburn_disc_available_space(drive, NULL); Sfile_scale((double) num_free, mem_text,5,1e4,1); sprintf(respt+strlen(respt), ", %s free\n", mem_text); Xorriso_toc_line(xorriso, flag & 8); } if(s != BURN_DISC_FULL && s != BURN_DISC_APPENDABLE) {ret= 1; goto ex;} if(xorriso->request_to_abort) {ret= 1; goto ex;} if(is_bdr_pow) { sprintf(respt, "Media summary: unsuitable Pseudo Overwrite formatted BD-R\n"); Xorriso_toc_line(xorriso, flag & 8); {ret= 1; goto ex;} } if(!(flag & 2)) Xorriso_show_boot_info(xorriso, 1 | (flag & 8) | ((flag & 1) << 1)); if(xorriso->isofs_size > 0 && !(flag & 3)) { sprintf(respt, "ISO offers :%s%s%s%s\n", xorriso->isofs_has_what & 1 ? " Rock_Ridge" : "", xorriso->isofs_has_what & 2 ? " Joliet" : "", xorriso->isofs_has_what & 4 ? " ISO_9660_1999" : "", xorriso->isofs_has_what & 7 ? "" : " Only_ECMA_119"); Xorriso_toc_line(xorriso, flag & 8); sprintf(respt, "ISO loaded : %s\n", xorriso->tree_loaded == 1 ? "Joliet" : xorriso->tree_loaded == 2 ? "ISO_9660_1999" : xorriso->rr_loaded > 0 ? "Rock_Ridge" : "Only_ECMA_119"); Xorriso_toc_line(xorriso, flag & 8); } disc= isoburn_toc_drive_get_disc(drive); if(flag & 4) sprintf(respt, "TOC layout : %3s , %9s , %10s\n", "Idx", "sbsector", "Size"); else sprintf(respt, "TOC layout : %3s , %9s , %10s , %s\n", "Idx", "sbsector", "Size", info_type_name[info_type]); if(!(flag&1)) Xorriso_toc_line(xorriso, flag & 8); if (disc==NULL) { Xorriso_process_msg_queues(xorriso,0); nwa= 0; if(drive_role == 5 && s == BURN_DISC_APPENDABLE) { ret= burn_disc_track_lba_nwa_v2(drive, NULL, 0, &lba, &nwa); if(ret != 1) nwa= 0; } else { ret= isoburn_get_min_start_byte(drive, &start_byte, 0); nwa= start_byte / 2048; if(ret<=0) { Xorriso_process_msg_queues(xorriso,0); if(flag&1) {ret= 1; goto ex;} sprintf(xorriso->info_text, "Cannot obtain Table Of Content"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); {ret= 0; goto ex;} } } /* fabricate TOC */ typetext= "Other session"; if(flag & 4) { ret= 0; typetext= "Session "; } else { ret= isoburn_read_iso_head_v2(drive, 0, &image_blocks, volume_id, info_type); if(ret > 0 && (info_type == 3 || info_type == 4)) Untimezone_ecma119_17byte(volume_id, (xorriso->toc_time_form & 1) | 2); } if(ret>0) { sprintf(respt, "ISO session : %3d , %9d , %9.fs , %s\n", 1, 0, (double) image_blocks, volume_id); nwa= image_blocks; } else { ret= burn_disc_get_formats(drive, &status, &size, &dummy, &num_formats); if(ret <= 0 || status != BURN_FORMAT_IS_FORMATTED) size= 0; if(size <= 0) { ret= burn_get_read_capacity_v2(drive, &num_data, 0); if(ret == 1) size= num_data * (off_t) 2048; } else { num_data_from_format= 1; } num_data= size / 2048; if(num_data == 0 && drive_role == 5 && s == BURN_DISC_APPENDABLE) num_data= nwa; sprintf(respt, "%13s: %3d , %9d , %9.fs , \n", typetext, 1, 0, (double) num_data); } if(!(flag&1)) Xorriso_toc_line(xorriso, flag & 8); num_sessions= 1; } else { num_data= other_data= 0; sessions= isoburn_toc_disc_get_sessions(disc, &num_sessions); open_sessions= isoburn_toc_disc_get_incmpl_sess(disc); for (session_no= 0; session_no < num_sessions + open_sessions && !(xorriso->request_to_abort); session_no++) { num_session_data= num_session_other= 0; tracks= isoburn_toc_session_get_tracks(sessions[session_no], &num_tracks); if (tracks == NULL || num_tracks <= 0) continue; if(session_no == num_sessions + open_sessions - 1 && open_sessions > 0) have_real_open_session= 1; for(track_no= 0; track_no<num_tracks && !(xorriso->request_to_abort); track_no++) { track_count++; is_data= 0; isoburn_toc_track_get_entry(tracks[track_no], &toc_entry); if((toc_entry.control & 7) >= 4) /* data track */ is_data= 1; if(toc_entry.extensions_valid & 8) { lba= toc_entry.long_start_lba; track_size= toc_entry.long_track_blocks; } else if(toc_entry.extensions_valid & 1) { /* DVD extension valid */ lba= toc_entry.start_lba; track_size= toc_entry.track_blocks; } else { lba= burn_msf_to_lba(toc_entry.pmin, toc_entry.psec, toc_entry.pframe); if(track_no==num_tracks-1) { isoburn_toc_session_get_leadout_entry(sessions[session_no], &next_toc_entry); } else { isoburn_toc_track_get_entry(tracks[track_no+1], &next_toc_entry); } track_size= burn_msf_to_lba(next_toc_entry.pmin, next_toc_entry.psec, next_toc_entry.pframe) - lba; } if((flag & (1 | 4)) || !is_data) { ret= 0; } else { ret= isoburn_toc_track_get_emul_v2(tracks[track_no], &emul_lba, &image_blocks, volume_id, 0); if(ret <= 0 || info_type != 1) { ret= isoburn_read_iso_head_v2(drive, lba, &image_blocks, volume_id, info_type); if(ret > 0 && (info_type == 3 || info_type == 4)) Untimezone_ecma119_17byte(volume_id, (xorriso->toc_time_form & 1) | 2); } if(image_blocks > track_size) { sprintf(xorriso->info_text, "Session %d bears ISO image size %.fs larger than track size %.fs", session_no + 1, (double) image_blocks, (double) track_size); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); image_blocks= track_size; } } if(session_no >= num_sessions && track_no == 0) { if(ret <= 0) volume_id[0]= 0; sprintf(respt, "Incmp session: %3d , %9.f , %9.fs , %s\n", session_no + 1, (double) lba, (double) image_blocks, volume_id); } else if(ret>0 && track_no==0) { sprintf(respt, "ISO session : %3d , %9.f , %9.fs , %s\n", session_no + 1, (double) lba, (double) image_blocks , volume_id); } else if(ret>0) { sprintf(respt, "ISO track : %3d , %9.f , %9.fs , %s\n", track_count, (double) lba, (double) image_blocks , volume_id); } else if(track_no==0) { typetext= "Other session"; if(flag & 4) typetext= "Session "; sprintf(respt, "%13s: %3d , %9.f , %9.fs , \n", typetext, session_no+1, (double) lba, (double) track_size); } else { typetext= "Other track "; if(flag & 4) typetext= "Track "; sprintf(respt, "%13s: %3d , %9.f , %9.fs , \n", typetext, track_count, (double) lba, (double) track_size); } if(!(flag&1)) Xorriso_toc_line(xorriso, flag & 8); if(is_data) num_session_data+= track_size; else num_session_other+= track_size; if(track_no == 0) first_track_start= lba; } isoburn_toc_session_get_leadout_entry(sessions[session_no], &toc_entry); if (toc_entry.extensions_valid & 8) { lba= toc_entry.long_start_lba; } else if(toc_entry.extensions_valid & 1) { lba= toc_entry.start_lba; } else { lba= burn_msf_to_lba(toc_entry.pmin, toc_entry.psec, toc_entry.pframe); } session_size= lba - first_track_start; if(num_session_data > 0 && num_session_other > 0) { num_data+= num_session_data; other_data+= num_session_other; } else if(is_data) { num_data+= session_size; } else { other_data+= session_size; } } } if(xorriso->request_to_abort) {ret= 1; goto ex;} Sfile_scale(((double) num_data) * 2048.0, mem_text,5,1e4,1); sessions_seen= num_sessions + open_sessions; if(open_sessions > 0 && !have_real_open_session) sessions_seen--; sprintf(respt, "Media summary: %d session%s, %.f data blocks, %s data", sessions_seen, (sessions_seen == 1 ? "" : "s"), (double) num_data, mem_text); if(num_data_from_format) num_free= 0; else num_free= isoburn_disc_available_space(drive, NULL); Sfile_scale((double) num_free, mem_text,5,1e4,1); sprintf(respt+strlen(respt), ", %s free", mem_text); sprintf(respt+strlen(respt), "\n"); Xorriso_toc_line(xorriso, flag & 8); if(other_data > 0) { sprintf(respt, "Non-data blks: %.f\n", (double) other_data); Xorriso_toc_line(xorriso, flag & 8); } if (s==BURN_DISC_APPENDABLE && nwa!=0) { ret= isoburn_disc_track_lba_nwa_v2(drive, NULL, 0, &lba, &nwa); if(ret>0) { sprintf(respt, "Media nwa : %.fs\n", (double) nwa); if(!(flag&1)) Xorriso_toc_line(xorriso, flag & 8); } } if(profile_no == 0x41 && sessions_seen >= 300) { sprintf(xorriso->info_text, "Sequential BD-R medium now contains %d sessions. It is likely to soon fail writing.", sessions_seen); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } if(have_real_open_session) { sprintf(xorriso->info_text, "Incomplete session encountered !"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } ret= 1; ex:; Xorriso_process_msg_queues(xorriso,0); if (disc!=NULL) isoburn_toc_disc_free(disc); return(ret); } /* @param flag bit0= try to find 'meaningful' links for enumerated devices */ int Xorriso_show_devices(struct XorrisO *xorriso, int flag) { char *adr= NULL, *link_adr= NULL, *adrpt; int i, j, max_dev_len= 1, pad, ret; struct burn_drive_info *drive_list= NULL; unsigned int drive_count; char *respt, perms[8]; struct stat stbuf; Xorriso_alloc_meM(adr, char, BURN_DRIVE_ADR_LEN); Xorriso_alloc_meM(link_adr, char, BURN_DRIVE_ADR_LEN); sprintf(xorriso->info_text, "Beginning to scan for devices ...\n"); Xorriso_info(xorriso,0); burn_drive_clear_whitelist(); while(!burn_drive_scan(&drive_list, &drive_count)) { Xorriso_process_msg_queues(xorriso,0); usleep(100000); } Xorriso_process_msg_queues(xorriso,0); if(drive_count == 0) { /* >>> was a drive_list created at all ? */ /* >>> must it be freed ? */ sprintf(xorriso->info_text, "No drives found"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); {ret= 0; goto ex;} } sprintf(xorriso->info_text, "Full drive scan done\n"); Xorriso_info(xorriso,0); sprintf(xorriso->info_text, "-----------------------------------------------------------------------------\n"); Xorriso_info(xorriso,0); respt= xorriso->result_line; for(i= 0; i < (int) drive_count && !(xorriso->request_to_abort); i++) { if(burn_drive_get_adr(&(drive_list[i]), adr)<=0) strcpy(adr, "-get_adr_failed-"); Xorriso_process_msg_queues(xorriso,0); adrpt= adr; if(flag & 1) { ret= burn_lookup_device_link(adr, link_adr, "/dev", NULL, 0, 0); if(ret < 0) goto ex; if(ret == 1) adrpt= link_adr; } if((int) strlen(adrpt) > max_dev_len) max_dev_len= strlen(adrpt); } for(i= 0; i < (int) drive_count && !(xorriso->request_to_abort); i++) { if(burn_drive_get_adr(&(drive_list[i]), adr)<=0) strcpy(adr, "-get_adr_failed-"); Xorriso_process_msg_queues(xorriso,0); if(stat(adr,&stbuf)==-1) { sprintf(perms,"errno=%d",errno); } else { strcpy(perms,"------"); if(stbuf.st_mode&S_IRUSR) perms[0]= 'r'; if(stbuf.st_mode&S_IWUSR) perms[1]= 'w'; if(stbuf.st_mode&S_IRGRP) perms[2]= 'r'; if(stbuf.st_mode&S_IWGRP) perms[3]= 'w'; if(stbuf.st_mode&S_IROTH) perms[4]= 'r'; if(stbuf.st_mode&S_IWOTH) perms[5]= 'w'; } adrpt= adr; if(flag & 1) { ret= burn_lookup_device_link(adr, link_adr, "/dev", NULL, 0, 0); if(ret < 0) goto ex; if(ret == 1) adrpt= link_adr; } sprintf(respt, "%d -dev '%s' ", i, adrpt); pad= max_dev_len-strlen(adrpt); if(pad>0) for(j= 0; j<pad; j++) strcat(respt, " "); sprintf(respt+strlen(respt), "%s : '%-8.8s' '%s' \n", perms, drive_list[i].vendor, drive_list[i].product); Xorriso_result(xorriso,0); } sprintf(xorriso->info_text, "-----------------------------------------------------------------------------\n"); Xorriso_info(xorriso,0); burn_drive_info_free(drive_list); ret= 1; ex:; Xorriso_process_msg_queues(xorriso,0); Xorriso_free_meM(adr); Xorriso_free_meM(link_adr); return(ret); } int Xorriso_tell_media_space(struct XorrisO *xorriso, off_t *media_space, off_t *free_space, int flag) { off_t ret; struct burn_drive_info *dinfo; struct burn_drive *drive; struct burn_write_opts *burn_options; (*free_space)= (*media_space)= 0; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to -tell_media_space", 2); if(ret<=0) return(0); ret= Xorriso_make_write_options(xorriso, drive, &burn_options, 0); if(ret<=0) return(-1); (*free_space)= (*media_space)= isoburn_disc_available_space(drive, burn_options) / (off_t) 2048; burn_write_opts_free(burn_options); if(Xorriso_change_is_pending(xorriso, 0)) { ret= Xorriso_write_session(xorriso, 1); if(ret>0) { (*free_space)-= ret; } else { Xorriso_process_msg_queues(xorriso,0); return(0); } } Xorriso_process_msg_queues(xorriso,0); return(1); } /* @return <=0 error, 1 success */ int Xorriso_list_formats(struct XorrisO *xorriso, int flag) { int ret, i, status, num_formats, profile_no, type, alloc_blocks, free_blocks; off_t size; unsigned dummy; char status_text[80], profile_name[90], *respt; struct burn_drive_info *dinfo; struct burn_drive *drive; respt= xorriso->result_line; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to obtain format descriptor list", 1 | 2); if(ret<=0) return(0); if(ret == 2) goto ex; ret = burn_disc_get_formats(drive, &status, &size, &dummy, &num_formats); if(ret<=0) { sprintf(xorriso->info_text, "Cannot obtain format list info"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); ret= 0; goto ex; } ret= Xorriso_toc(xorriso, 3); if(ret<=0) goto ex; ret= burn_disc_get_profile(drive, &profile_no, profile_name); if(ret<=0) goto ex; if(status == BURN_FORMAT_IS_UNFORMATTED) sprintf(status_text, "unformatted, up to %.1f MiB", ((double) size) / 1024.0 / 1024.0); else if(status == BURN_FORMAT_IS_FORMATTED) { if(profile_no==0x12 || profile_no==0x13 || profile_no==0x1a || profile_no==0x43) sprintf(status_text, "formatted, with %.1f MiB", ((double) size) / 1024.0 / 1024.0); else sprintf(status_text, "written, with %.1f MiB", ((double) size) / 1024.0 / 1024.0); } else if(status == BURN_FORMAT_IS_UNKNOWN) { if (profile_no > 0) sprintf(status_text, "intermediate or unknown"); else sprintf(status_text, "no media or unknown media"); } else sprintf(status_text, "illegal status according to MMC-5"); sprintf(respt, "Format status: %s\n", status_text); Xorriso_result(xorriso,0); ret= burn_disc_get_bd_spare_info(drive, &alloc_blocks, &free_blocks, 0); if(ret == 1) { sprintf(respt, "BD Spare Area: %d blocks consumed, %d blocks available\n", alloc_blocks - free_blocks, free_blocks); Xorriso_result(xorriso,0); } for (i= 0; i < num_formats; i++) { ret= burn_disc_get_format_descr(drive, i, &type, &size, &dummy); if (ret <= 0) continue; sprintf(respt, "Format idx %-2d: %2.2Xh , %.fs , %.1f MiB\n", i, type, ((double) size) / 2048.0, ((double) size) / 1024.0/1024.0); Xorriso_result(xorriso,0); } ret= 1; ex:; return(ret); } /* @param flag bit0= This is write speed. Consider the existence of combo drives (e.g. DVD-ROM/CD-RW or BD-ROM/DVD+-RW) */ int Xorriso_choose_speed_factor(struct XorrisO *xorriso, int speed, int profile, struct burn_drive *drive, double *speed_factor, char **speed_unit, int flag) { int i, no_dvd_read= 1, no_dvd_write= 1, no_bd_read= 1, no_bd_write= 1, ret; int num_profiles, profiles[64]; char is_current[64]; *speed_unit= "D"; *speed_factor= 1385000.0; if((flag & 1) || profile == 0x00) { ret= burn_drive_get_all_profiles(drive, &num_profiles, profiles, is_current); if(ret > 0) { for(i= 0; i < num_profiles; i++) { if(profiles[i] > 0x10 && profiles[i] < 0x30) no_dvd_read= no_dvd_write= 0; else if(profiles[i] == 0x10) no_dvd_read= 0; else if(profiles[i] > 0x40 && profiles[i] <= 0x43) no_bd_read= no_bd_write= 0; else if(profiles[i] == 0x40) no_bd_read= 0; } } } if(profile == 0x00) { /* No medium loaded, guess from profile list */ if(flag & 1) { if(no_bd_write && no_dvd_write) *speed_unit= "C"; else if(!no_bd_write) *speed_unit= "B"; } else { if(no_bd_read && no_dvd_read) *speed_unit= "C"; else if(!no_bd_read) *speed_unit= "B"; } } else if((profile > 0x00 && profile <= 0x0a) || (((no_dvd_write && no_bd_write) && (flag & 1)))) { *speed_unit= "C"; } else if((profile >= 0x40 && profile <= 0x43) && !(no_bd_write && (flag & 1))) { *speed_unit= "B"; } if((*speed_unit)[0] == 'C') *speed_factor= 75.0 * 2352.0; else if((*speed_unit)[0] == 'B') *speed_factor= 4495625.0; return(1); } /* For sorting the int *speeds array */ int Xorriso__reverse_int_cmp(const void *a, const void *b) { int diff; diff= *((int *) a) - *((int *) b); if(diff < 0) return(1); else if(diff > 0) return(-1); return(0); } /* @flag bit0= do not issue TOC bit1= Report about outdev (else indev) bit2= Report about write speed (else read speed) @return <=0 error, 1 success */ int Xorriso_list_speeds_sub(struct XorrisO *xorriso, int flag) { int ret, high= -1, low= 0x7fffffff, is_cd= 0, i, speed, profile= 0; int inout_flag, prev_speed= -1, speed_count= 0; int *speeds= NULL; char *respt, *speed_unit= "D"; double speed_factor= 1385000.0, cd_factor= 75.0 * 2352; struct burn_drive_info *dinfo; struct burn_drive *drive; struct burn_speed_descriptor *speed_list= NULL, *item, *other; respt= xorriso->result_line; inout_flag= (flag & 2); if(inout_flag && xorriso->out_drive_handle == NULL) inout_flag= 0; else if(xorriso->in_drive_handle == NULL) inout_flag= 2; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to obtain speed descriptor list", 1 | inout_flag); if(ret<=0) return(0); if(ret == 2) goto ex; ret= burn_drive_get_speedlist(drive, &speed_list); if(ret < 0) { sprintf(xorriso->info_text, "Cannot obtain speed list info"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); ret= 0; goto ex; } if(!(flag & 1)) { ret= Xorriso_toc(xorriso, 1 | inout_flag); if(ret<=0) { sprintf(xorriso->info_text, "Cannot obtain overview of drive and media content"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); ret= 0; goto ex; } } speed_count= 0; for(item= speed_list; item != NULL; item= item->next) speed_count++; if(speed_count > 0) Xorriso_alloc_meM(speeds, int, speed_count); speed_count= 0; for(item= speed_list; item != NULL; item= item->next) { sprintf(xorriso->info_text, "read_speed= %5dk , write_speed= %5dk , source= %d", item->read_speed, item->write_speed, item->source); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); if(item->source == 1) { /* CD mode page 2Ah : report only if not same speed by GET PERFORMANCE */ if(!(flag & 4)) continue; /* 2Ah only tells write speed */ for(other= speed_list; other != NULL; other= other->next) if(other->source == 2 && item->write_speed == other->write_speed) break; if(other != NULL) continue; } if(flag & 4) { if(item->write_speed <= 0) continue; speed= item->write_speed; } else { if(item->read_speed <= 0) continue; speed= item->read_speed; } speeds[speed_count]= speed; if(item->profile_loaded > 0) profile= item->profile_loaded; speed_count++; } if(speed_count > 0) qsort(speeds, (size_t) speed_count, sizeof(int), Xorriso__reverse_int_cmp); if(profile >= 0x08 && profile <= 0x0a) is_cd= profile; for(i= 0; i < speed_count; i++) { speed= speeds[i]; if(speed == prev_speed) continue; prev_speed= speed; if(flag & 4) sprintf(respt, "Write speed : "); else sprintf(respt, "Read speed : "); Xorriso_choose_speed_factor(xorriso, speed, profile, drive, &speed_factor, &speed_unit, !!(flag & 4)); sprintf(respt + strlen(respt), " %5dk , %4.1fx%s\n", speed, ((double) speed) * 1000.0 / speed_factor, speed_unit); Xorriso_result(xorriso,0); if(speed > high) high= speed; if(speed < low) low= speed; } /* Maybe there is ATIP info (about write speed only) */ if(is_cd && (flag & 4)) { ret= burn_disc_read_atip(drive); if(ret < 0) goto ex; if(ret > 0) { for(i= 0; i < 2; i++) { if(i == 0) ret= burn_drive_get_min_write_speed(drive); else ret= burn_drive_get_write_speed(drive); if(ret > 0) { if(ret < low || (i == 0 && ret != low)) { sprintf(respt, "Write speed l: "); sprintf(respt + strlen(respt), " %5dk , %4.1fx%s\n", ret, ((double) ret) * 1000.0 / cd_factor, "C"); Xorriso_result(xorriso,0); low= ret; } if(ret > high || (i == 1 && ret != high)) { sprintf(respt, "Write speed h: "); sprintf(respt + strlen(respt), " %5dk , %4.1fx%s\n", ret, ((double) ret) * 1000.0 / cd_factor, "C"); Xorriso_result(xorriso,0); high= ret; } } } } } if(high > -1) { Xorriso_choose_speed_factor(xorriso, low, profile, drive, &speed_factor, &speed_unit, !!(flag & 4)); if(flag & 4) sprintf(respt, "Write speed L: "); else sprintf(respt, "Read speed L : "); sprintf(respt + strlen(respt), " %5dk , %4.1fx%s\n", low, ((double) low) * 1000.0 / speed_factor, speed_unit); Xorriso_result(xorriso,0); Xorriso_choose_speed_factor(xorriso, low, profile, drive, &speed_factor, &speed_unit, !!(flag & 4)); if(flag & 4) sprintf(respt, "Write speed H: "); else sprintf(respt, "Read speed H : "); sprintf(respt + strlen(respt), " %5dk , %4.1fx%s\n", high, ((double) high) * 1000.0 / speed_factor, speed_unit); Xorriso_result(xorriso,0); ret= burn_drive_get_best_speed(drive, 0, &item, 2); if(ret > 0 && item != NULL && (flag & 4)) if(item->write_speed != high) { sprintf(respt, "Write speed 0: %5dk , %4.1fx%s\n", item->write_speed, ((double) item->write_speed) * 1000.0 / speed_factor, speed_unit); Xorriso_result(xorriso,0); } } else { sprintf(xorriso->info_text, "Could not get any %s speed information from drive", (flag & 4) ? "write" : "read"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 2; goto ex; } ret= 1; ex:; if(speed_list != NULL) burn_drive_free_speedlist(&speed_list); Xorriso_free_meM(speeds); return(ret); } int Xorriso_list_speeds(struct XorrisO *xorriso, int flag) { int ret; if(xorriso->out_drive_handle == NULL && xorriso->in_drive_handle == NULL) { Xorriso_msgs_submit(xorriso, 0, "No drive acquired on attempt to list speeds", 0, "FAILURE", 0); return(0); } if(xorriso->in_drive_handle != NULL) { ret= Xorriso_list_speeds_sub(xorriso, 0); if(ret <= 0) return(ret); } if(xorriso->out_drive_handle != NULL && xorriso->out_drive_handle != xorriso->in_drive_handle) { ret= Xorriso_list_speeds_sub(xorriso, 2); if(ret <= 0) return(ret); } if(xorriso->out_drive_handle != NULL) { ret= Xorriso_list_speeds_sub(xorriso, 1 | 2 | 4); if(ret <= 0) return(ret); } return(1); } /* @param flag bit0= cdrecord style bit1= obtain outdrive, else indrive @return <=0 error, 1 success */ int Xorriso_list_profiles(struct XorrisO *xorriso, int flag) { int ret, i; struct burn_drive_info *dinfo; struct burn_drive *drive; int num_profiles, profiles[64]; char is_current[64], profile_name[90], *respt; respt= xorriso->result_line; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to obtain profile list", 1 | (flag & 2)); if(ret<=0) return(0); burn_drive_get_all_profiles(drive, &num_profiles, profiles, is_current); for(i= 0; i < num_profiles; i++) { ret= burn_obtain_profile_name(profiles[i], profile_name); if(ret <= 0) strcpy(profile_name, "unknown"); sprintf(respt, "%s 0x%4.4X (%s)%s\n", flag & 1 ? "Profile:" : "Profile :", (unsigned int) profiles[i], profile_name, is_current[i] ? " (current)" : ""); Xorriso_result(xorriso,0); } return(1); } /* @param flag bit0= -inq bit1= -checkdrive */ int Xorriso_atip(struct XorrisO *xorriso, int flag) { int ret, profile_number= 0, is_bdr_pow= 0; int num_profiles= 0, profiles[64], i, can_write= 0, pf, no_medium= 0; char is_current[64]; char *respt, profile_name[80]; double x_speed_max, x_speed_min= -1.0; struct burn_drive_info *dinfo; struct burn_drive *drive; enum burn_disc_status s; char *manuf= NULL, *media_code1= NULL, *media_code2= NULL; char *book_type= NULL, *product_id= NULL; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to print drive and media info", 2); if(ret<=0) return(0); respt= xorriso->result_line; sprintf(respt, "Device type : "); ret= burn_drive_get_drive_role(drive); if(ret==0) sprintf(respt+strlen(respt), "%s\n", "Emulated (null-drive)"); else if(ret==2) sprintf(respt+strlen(respt), "%s\n", "Emulated (stdio-drive, 2k random read-write)"); else if(ret==3) sprintf(respt+strlen(respt), "%s\n", "Emulated (stdio-drive, sequential write-only)"); else if(ret == 4) sprintf(respt+strlen(respt), "%s\n", "Emulated (stdio-drive, 2k random read-only)"); else if(ret == 5) sprintf(respt+strlen(respt), "%s\n", "Emulated (stdio-drive, 2k random write-only)"); else if(ret!=1) sprintf(respt+strlen(respt), "%s\n","Emulated (stdio-drive)"); else sprintf(respt+strlen(respt), "%s\n","Removable CD-ROM"); sprintf(respt+strlen(respt), "Vendor_info : '%s'\n",dinfo->vendor); sprintf(respt+strlen(respt), "Identifikation : '%s'\n",dinfo->product); sprintf(respt+strlen(respt), "Revision : '%s'\n",dinfo->revision); Xorriso_result(xorriso,1); if(flag&1) return(1); /* Do not report "Supported modes: SAO TAO" with -ROM drives */ burn_drive_get_all_profiles(drive, &num_profiles, profiles, is_current); if(num_profiles > 0) { for(i= 0; i < num_profiles; i++) { pf= profiles[i]; if(pf == 0x09 || pf == 0x0a || pf == 0x11 || pf == 0x12 || pf == 0x13 || pf == 0x14 || pf == 0x15 || pf == 0x1a || pf == 0x1b || pf == 0x2b || pf == 0x41 || pf == 0x43 || pf == 0xffff) { can_write= 1; break; } } } else can_write= 1; if(can_write) { sprintf(respt, "Driver flags : BURNFREE\n"); sprintf(respt+strlen(respt), "Supported modes: SAO TAO\n"); Xorriso_result(xorriso,1); } else if(flag & 2) { sprintf(xorriso->info_text, "Not a CD/DVD/BD recorder"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } if(flag&2) return(1); is_bdr_pow= burn_drive_get_bd_r_pow(drive); s= burn_disc_get_status(drive); ret= burn_disc_get_profile(drive,&profile_number,profile_name); if(ret<=0) { profile_number= 0; strcpy(profile_name, "-unidentified-"); } if(s != BURN_DISC_UNSUITABLE) { ret= burn_disc_read_atip(drive); if(ret>0) { ret= burn_drive_get_min_write_speed(drive); x_speed_min= ((double) ret)/176.4; } } if(s==BURN_DISC_EMPTY) { sprintf(respt, "Current: none\n"); Xorriso_result(xorriso,1); sprintf(xorriso->info_text, "No recognizable medium found in drive"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); no_medium= 1; } else sprintf(respt, "Current: %s%s\n",profile_name, is_bdr_pow ? ", Pseudo Overwrite formatted" : ""); Xorriso_result(xorriso,1); Xorriso_list_profiles(xorriso, 1 | 2); if(no_medium) return(1); if(strstr(profile_name,"BD")==profile_name) { printf("Mounted Media: %2.2Xh, %s\n", profile_number, profile_name); } else if(strstr(profile_name,"DVD")==profile_name) { sprintf(respt, "book type: %s (emulated booktype)\n", profile_name); Xorriso_result(xorriso,1); if(profile_number == 0x13) { sprintf(respt, "xorriso: message for sdvdbackup: \"(growisofs mode Restricted Overwrite)\"\n"); Xorriso_result(xorriso,1); } } else { sprintf(respt, "ATIP info from disk:\n"); Xorriso_result(xorriso,1); if(burn_disc_erasable(drive)) sprintf(respt, " Is erasable\n"); else sprintf(respt, " Is not erasable\n"); Xorriso_result(xorriso,1); { int start_lba,end_lba,min,sec,fr; ret= burn_drive_get_start_end_lba(drive,&start_lba,&end_lba,0); if(ret>0) { burn_lba_to_msf(start_lba,&min,&sec,&fr); sprintf(respt, " ATIP start of lead in: %d (%-2.2d:%-2.2d/%-2.2d)\n", start_lba,min,sec,fr); Xorriso_result(xorriso,1); burn_lba_to_msf(end_lba,&min,&sec,&fr); sprintf(respt, " ATIP start of lead out: %d (%-2.2d:%-2.2d/%-2.2d)\n", end_lba,min,sec,fr); Xorriso_result(xorriso,1); } } ret= burn_drive_get_write_speed(drive); x_speed_max= ((double) ret)/176.4; if(x_speed_min<0) x_speed_min= x_speed_max; sprintf(respt, " 1T speed low: %.f 1T speed high: %.f\n",x_speed_min,x_speed_max); Xorriso_result(xorriso,1); } ret= burn_disc_get_media_id(drive, &product_id, &media_code1, &media_code2, &book_type, 0); if(ret > 0 && media_code1 != NULL && media_code2 != NULL) manuf= burn_guess_manufacturer(profile_number, media_code1, media_code2, 0); if(product_id != NULL) { sprintf(respt, "Product Id: %s\n", product_id); Xorriso_result(xorriso,1); } if(manuf != NULL) { sprintf(respt, "Producer: %s\n", manuf); Xorriso_result(xorriso, 1); } if(manuf != NULL && (profile_number == 0x09 || profile_number == 0x0a)) { sprintf(respt, "Manufacturer: %s\n", manuf); Xorriso_result(xorriso, 1); } else if(product_id != NULL && media_code1 != NULL && media_code2 != NULL){ free(product_id); free(media_code1); free(media_code2); if(book_type != NULL) free(book_type); product_id= media_code1= media_code2= book_type= NULL; ret= burn_disc_get_media_id(drive, &product_id, &media_code1, &media_code2, &book_type, 1); if(ret > 0) { sprintf(respt, "Manufacturer: '%s'\n", media_code1); Xorriso_result(xorriso, 1); if(media_code2[0]) { sprintf(respt, "Media type: '%s'\n", media_code2); Xorriso_result(xorriso, 1); } } } if(manuf != NULL) free(manuf); if(media_code1 != NULL) free(media_code1); if(media_code2 != NULL) free(media_code2); if(book_type != NULL) free(book_type); if(product_id != NULL) free(product_id); return(1); } /* @param flag bit1= outdev rather than indev @return <0 error, 0 = no profile to see , 1= ok , 2= ok, is CD profile 3= ok, is BD profile */ int Xorriso_get_profile(struct XorrisO *xorriso, int *profile_number, char profile_name[80], int flag) { int ret; struct burn_drive_info *dinfo; struct burn_drive *drive; *profile_number= 0; profile_name[0]= 0; if(((flag&2) && xorriso->out_drive_handle==NULL) || ((!(flag&2)) && xorriso->in_drive_handle==NULL)) return(0); ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to determine media type", flag&2); if(ret<=0) return(0); ret=burn_disc_get_profile(drive, profile_number, profile_name); if(ret<=0) return(ret); if(*profile_number==0x08 || *profile_number==0x09 || *profile_number==0x0a) return(2); if(*profile_number == 0x40 || *profile_number == 0x41 || *profile_number == 0x42 || *profile_number == 0x43) return(3); return(0); } /* @param flag bit0= grow_overwriteable_iso bit1= obtain info from outdev bit2= no need to obtain msc2 (NWA) */ int Xorriso_msinfo(struct XorrisO *xorriso, off_t *msc1, off_t *msc2, int flag) { int ret, is_bdr_pow= 0; off_t dummy; struct burn_drive *drive; struct burn_drive_info *dinfo; enum burn_disc_status disc_state; *msc1= *msc2= -1; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to obtain msinfo", flag&2); if(ret<=0) return(ret); is_bdr_pow= burn_drive_get_bd_r_pow(drive); if(is_bdr_pow) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "%s medium is unsuitably POW formatted BD-R. Cannot obtain -msinfo.", (flag&2) ? "Output" : "Input"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(flag&1) disc_state= isoburn_disc_get_status(drive); else disc_state= burn_disc_get_status(drive); if(disc_state != BURN_DISC_APPENDABLE && !(disc_state == BURN_DISC_FULL && (flag & 4))) { Xorriso_process_msg_queues(xorriso,0); if(!(flag & 4)) { sprintf(xorriso->info_text, "%s medium is not appendable. Cannot obtain -msinfo.", (flag&2) ? "Output" : "Input"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } return(0); } ret= isoburn_disc_get_msc1_v2(drive, msc1); if(ret<=0) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "Cannot obtain address of most recent session"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(flag & 4) return(1); ret= isoburn_disc_track_lba_nwa_v2(drive, NULL, 0, &dummy, msc2); if(ret<0) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "Cannot obtain next writeable address on media"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* @param flag bit0=input drive bit1=output drive bit2= wake up rather than calm down */ int Xorriso_drive_snooze(struct XorrisO *xorriso, int flag) { int in_is_out_too, ret; struct burn_drive_info *dinfo; struct burn_drive *drive; in_is_out_too= (xorriso->in_drive_handle == xorriso->out_drive_handle); if((flag & 1) && xorriso->in_drive_handle != NULL) { Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to calm drive", 0); burn_drive_snooze(drive, !!(flag & 4)); if(in_is_out_too) {ret= 1; goto ex;} } if((flag&2) && xorriso->out_drive_handle!=NULL) { Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to calm drive", 2); burn_drive_snooze(drive, !!(flag & 4)); } ret= 1; ex:; Xorriso_process_msg_queues(xorriso,0); return(ret); } /* @param flag bit0= enable SCSI command logging to stderr */ int Xorriso_scsi_log(struct XorrisO *xorriso, int flag) { if(flag == 0) burn_set_scsi_logging(0); else burn_set_scsi_logging(2|4); return(1); } int Xorriso_check_md5_range(struct XorrisO *xorriso, off_t start_lba, off_t end_lba, char md5[16], int flag) { int ret, us_corr = 0; struct burn_drive_info *dinfo= NULL; struct burn_drive *drive= NULL; off_t pos, data_count, to_read, slowdown_count= 0; char *data= NULL, data_md5[16]; void *ctx = NULL; struct timeval prev_time; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to check session MD5 checksum", 0); if(ret <= 0) goto ex; Xorriso_alloc_meM(data, char, 64 * 1024); ret= iso_md5_start(&ctx); if(ret <= 0) { Xorriso_no_malloc_memory(xorriso, NULL, 0); goto ex; } if(xorriso->read_speed_force > 0) /* initialize forced speed limit */ burn_nominal_slowdown(xorriso->read_speed_force, xorriso->read_speed_corr, &prev_time, &us_corr, (off_t) 0, 1); Xorriso_set_speed(xorriso, drive, xorriso->read_speed, 0, 1); Xorriso_process_msg_queues(xorriso,0); for(pos= start_lba; pos < end_lba; pos+= 32) { to_read= 32; if(pos + to_read > end_lba) to_read= end_lba - pos; ret= burn_read_data(drive, pos * (off_t) 2048, data, to_read * (off_t) 2048, &data_count, 0); if(ret <= 0) goto ex; iso_md5_compute(ctx, data, (int) data_count); if(xorriso->read_speed_force > 0 && pos + to_read <= end_lba) { slowdown_count+= data_count; if(slowdown_count >= 128 * 1024) { burn_nominal_slowdown(xorriso->read_speed_force, xorriso->read_speed_corr, &prev_time, &us_corr, slowdown_count, 0); slowdown_count= 0; } } xorriso->pacifier_count+= data_count; xorriso->pacifier_byte_count+= data_count; Xorriso_pacifier_callback(xorriso, "content bytes read", xorriso->pacifier_count, 0, "", 8); } iso_md5_end(&ctx, data_md5); ret= 1; if(! iso_md5_match(md5, data_md5)) ret= 0; ex:; Xorriso_process_msg_queues(xorriso,0); if(ctx != NULL) iso_md5_end(&ctx, data_md5); Xorriso_free_meM(data); return(ret); } int Xorriso_check_session_md5(struct XorrisO *xorriso, char *severity, int flag) { int ret, i; IsoImage *image; uint32_t start_lba, end_lba; char md5[16], md5_text[33]; ret= Xorriso_get_volume(xorriso, &image, 0); if(ret<=0) return(ret); ret= iso_image_get_session_md5(image, &start_lba, &end_lba, md5, 0); Xorriso_process_msg_queues(xorriso,0); if(ret < 0) return(ret); if(ret == 0) { sprintf(xorriso->info_text, "No session MD5 is recorded with the loaded session"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); return(0); } sprintf(xorriso->info_text, "Checking loaded session by its recorded MD5.\n"); Xorriso_info(xorriso, 0); for(i= 0; i < 16; i++) sprintf(md5_text + 2 * i, "%2.2x", ((unsigned char *) md5)[i]); sprintf(xorriso->result_line, "Session MD5 %s , LBA %.f , %.f blocks\n", md5_text, (double) start_lba, (double) end_lba - start_lba); Xorriso_result(xorriso,0); ret= Xorriso_check_md5_range(xorriso, (off_t) start_lba, (off_t) end_lba, md5, 0); return(ret); } int Xorriso_check_for_abort(struct XorrisO *xorriso, char *abort_file_path, double post_read_time, double *last_abort_file_time, int flag) { struct stat stbuf; if(abort_file_path[0] == 0) return(0); if(post_read_time - *last_abort_file_time >= 0.1) { if(stat(abort_file_path, &stbuf) != -1) { if(stbuf.st_mtime >= xorriso->start_time) { sprintf(xorriso->info_text, "-check_media: Found fresh abort_file=%s", abort_file_path); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(1); } } *last_abort_file_time= post_read_time; } return(0); } struct xorriso_md5_state { /* Resources */ struct XorrisO *xorriso; void *ctx; struct SpotlisT *spotlist; pthread_mutex_t spot_mutex; /* Checksum tag cursor */ off_t md5_start; off_t next_tag; int chain_broken; int in_track_gap; int was_sb_tag; int md5_spot_value; off_t md5_spot_lba; /* Asynchronous operation */ int slave_state; /* Operated by slave 0= not yet started 1= slave is started 2= slave has reached its end 3= slave failed to start */ int chunk_size; int num_chunks; char **chunk; int *chunk_state; /* 0= content invalid (set by boss at creation time), 1= content readable (set by boss), 2= content was read (set by MD5 slave), 3= end-of-processing (set by boss when done) */ int *chunk_fill; /* Actual number of valid bytes in chunk */ off_t *chunk_lba; int chunk_w_idx; /* Write index. Operated by boss */ int chunk_r_idx; /* Read index. Operated by MD5 slave */ off_t w_sleeps; off_t r_sleeps; }; int Xorriso__add_spot(struct xorriso_md5_state *state, off_t start_lba, off_t blocks, int quality, int flag) { int ret, uret; if(state->chunk != NULL) { ret= pthread_mutex_lock(&(state->spot_mutex)); if(ret != 0) return(0); } ret= Spotlist_add_item(state->spotlist, start_lba, blocks, quality, 0); if(state->chunk != NULL) { uret= pthread_mutex_unlock(&(state->spot_mutex)); if(uret != 0 && ret > 0) ret= 0; } return(ret); } int Xorriso_chunk_md5(struct XorrisO *xorriso, char *data, int to_read, off_t from_lba, struct xorriso_md5_state *state, int flag) { int j, k, ret= 0, valid, tag_type, decode_ret= 0; uint32_t pos, range_start, range_size, next_tag_uint32; off_t lba; char md5[16], tag_md5[16], *tag_type_name= "", *comparison, *sev_text; char md5_text[33]; void *cloned_ctx= NULL; for(j= 0; j < to_read; j++) { lba= j + from_lba; if(lba > (off_t) 0xffffffff) { if(lba == (off_t) 0x100000000) { printf(xorriso->info_text, "Checkreading exceeds the 32-bit block address limit of libisofs MD5 tags"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING",0); } break; } if(lba < state->md5_start) continue; ret= decode_ret= 0; if(lba > state->md5_start + 16 && (state->next_tag == 0 || state->chain_broken || lba == state->next_tag)){ ret= iso_util_decode_md5_tag(data + j * 2048, &tag_type, &pos, &range_start, &range_size, &next_tag_uint32, tag_md5, !!state->chain_broken); if(ret > 0 && tag_type >= 2 && tag_type <= 4) state->next_tag= next_tag_uint32; decode_ret= ret; } valid= (ret == 1 || ret == (int) ISO_MD5_AREA_CORRUPTED) && pos == lba; if(valid && tag_type == 2 && (lba < state->md5_start + 32 || state->in_track_gap)){ tag_type_name= "superblock"; state->was_sb_tag= 1; if(state->in_track_gap && range_start != state->md5_start && range_start < lba && lba - range_start <= (uint32_t) j) { /* Looking for next session : start computing in hindsight. Session start and superblock tag are supposed to be in the same 64 kB chunk. */ iso_md5_end(&(state->ctx), md5); ret= iso_md5_start(&(state->ctx)); if(ret < 0) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } iso_md5_compute(&(state->ctx), data + (j - (lba - range_start)) * 2048, (int) (lba - range_start) * 2048); state->md5_start= range_start; state->in_track_gap= 0; } } else if(valid && tag_type == 4 && lba < 32) { tag_type_name= "relocated 64kB superblock"; } else if(valid && tag_type == 3 && state->was_sb_tag) { tag_type_name= "tree"; } else if(valid && tag_type == 1) { /* Allow this without superblock and tree tag */ tag_type_name= "session"; } else { tag_type_name= ""; } if (tag_type_name[0]) { if(range_start != state->md5_start) { sprintf(xorriso->info_text, "Found MD5 %s tag which covers different data range", tag_type_name); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE",0); sprintf(xorriso->info_text, " Expected start: %.f Found: %lu", (double) state->md5_start, (unsigned long int) range_start); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE",0); for(k= 0; k < 16; k++) sprintf(md5_text + 2 * k, "%2.2x", ((unsigned char *) tag_md5)[k]); sprintf(xorriso->info_text, " Size: %lu MD5: %s", (unsigned long int) range_size, md5_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE",0); state->chain_broken= 1; valid= 0; } else { ret= iso_md5_clone(state->ctx, &cloned_ctx); if(ret <= 0) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } iso_md5_end(&cloned_ctx, md5); if(decode_ret == (int) ISO_MD5_AREA_CORRUPTED) { comparison= "CORRUPTED"; sev_text= "WARNING"; state->md5_spot_value= Xorriso_read_quality_md5_mismatcH; state->chain_broken= 1; } else if(! iso_md5_match(tag_md5, md5)) { comparison= "NON-MATCHING"; sev_text= "WARNING"; state->md5_spot_value= Xorriso_read_quality_md5_mismatcH; state->chain_broken= 1; } else { comparison= "matching"; sev_text= "UPDATE"; state->md5_spot_value= Xorriso_read_quality_md5_matcH; } state->md5_spot_lba= lba; sprintf(xorriso->info_text, "Found %s MD5 %s tag: start=%.f size=%.f", comparison, tag_type_name, (double) state->md5_start, (double) (lba - state->md5_start)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, sev_text, 0); } if(valid && (tag_type == 1 || (tag_type == 4 && pos == lba && lba < 32))){ if(state->md5_spot_value != Xorriso_read_quality_untesteD) { ret= Xorriso__add_spot(state, state->md5_start, state->md5_spot_lba - state->md5_start, state->md5_spot_value, 0); if(ret <= 0) goto ex; } state->md5_spot_value= Xorriso_read_quality_untesteD; state->md5_start= lba + 1; if(state->md5_start % 32) state->md5_start= state->md5_start + (32 - (state->md5_start % 32)); state->next_tag= 0; iso_md5_end(&(state->ctx), md5); ret= iso_md5_start(&(state->ctx)); if(ret < 0) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } if(tag_type == 1) state->in_track_gap= 1; continue; } } iso_md5_compute(state->ctx, data + j * 2048, 2048); } ret= 1; ex:; return(ret); } static void *Xorriso__md5_slave(void *state_pt) { struct xorriso_md5_state *state; int ret, c_state, c_idx; static int u_wait= 1; state= state_pt; state->slave_state= 1; while(1) { c_idx= state->chunk_r_idx; c_state= state->chunk_state[c_idx]; if(c_state == 1) { ret= Xorriso_chunk_md5(state->xorriso, state->chunk[c_idx], state->chunk_fill[c_idx], state->chunk_lba[c_idx], state, 0); if(ret <= 0) goto ex; state->chunk_state[c_idx]= 2; state->chunk_r_idx= (c_idx + 1) % state->num_chunks; } else if(c_state == 3) { goto ex; } else { /* >>> have a timeout ? */; if(u_wait > 0) usleep(u_wait); state->r_sleeps++; } } ex:; state->slave_state= 2; return NULL; } int Xorriso_start_chunk_md5(struct XorrisO *xorriso, struct xorriso_md5_state *state, int flag) { int ret, u_wait= 1000; pthread_attr_t attr; pthread_attr_t *attr_pt = NULL; pthread_t thread; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); attr_pt= &attr; ret= pthread_create(&thread, attr_pt, Xorriso__md5_slave, state); if(ret != 0) { sprintf(xorriso->info_text, "-check_media: Cannot create thread for MD5 computation"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } while(state->slave_state != 1) { /* >>> have a timeout ? */; /* >>> if this fails set state->slave_state= 3 */ usleep(u_wait); } ret= 1; ex:; return(ret); } int Xorriso__wait_chunk_md5(struct xorriso_md5_state *state, int u_wait, int flag) { if(state->chunk_state == NULL) return(1); while(state->chunk_state[state->chunk_w_idx] == 1) { /* >>> have a timeout ? */; usleep(u_wait); state->w_sleeps++; } return(1); } int Xorriso__wait_slave_md5_end(struct xorriso_md5_state *state, int u_wait, int flag) { while(state->slave_state == 1) { /* >>> have a timeout ? */; usleep(u_wait); } return(1); } int Xorriso__end_slave_md5(struct xorriso_md5_state *state, int u_wait, int flag) { int i, ret; /* Tell slave thread to end */ for(i= 0; i < state->num_chunks; i++) { ret= Xorriso__wait_chunk_md5(state, 10000, 0); if(ret <= 0) return(ret); state->chunk_state[state->chunk_w_idx]= 3; state->chunk_w_idx= (state->chunk_w_idx + 1) % state->num_chunks; } /* Wait for slave to end */ ret= Xorriso__wait_slave_md5_end(state, 10000, 0); if(ret <= 0) return(ret); return(1); } /* @param flag bit0= this is a follow-up session (i.e. on CD: TAO) bit1= no pacifier messages bit2= compute stream MD5 and look out for checksum tag @return <=0 error, 1= done, 2= aborted due to limit */ int Xorriso_check_interval(struct XorrisO *xorriso, struct SpotlisT *spotlist, struct CheckmediajoB *job, off_t from_lba, off_t block_count, off_t read_chunk, off_t md5_start, int flag) { int ret, skip_reading, first_value, fret, suspect_tao_end= 0; off_t i, j, total_count= 0, start_sec, end_sec, start_lba= 0; off_t sectors= -1, sector_size= -1; int prev_quality= -1, quality= -1, retry= 0, profile_no, is_cd= 0; int eccb_size= 16, us_corr = 0, data_skip; char profile_name[80]; struct burn_drive *drive; struct burn_drive_info *dinfo; char *data= NULL, *data_pt; off_t data_count, to_read, read_count= 0, write_amount, skipped_to_read; off_t slowdown_count= 0, seek_adr; struct timeval prev_time; double pre_read_time, post_read_time, time_diff, total_time_diff= 0; double last_abort_file_time= 0; void *ctx= NULL; char md5[16]; size_t data_size; struct xorriso_md5_state state; int num_chunks, async_md5; static off_t chunks_limit= 256 * 1024 * 1024; memset(&state, 0, sizeof(state)); state.chunk= NULL; state.chunk_state= NULL; state.chunk_fill= NULL; state.chunk_lba= NULL; state.spotlist= spotlist; if(read_chunk > 1024) read_chunk= 1024; else if(read_chunk < 1) read_chunk= 1; data_skip= job->data_to_skip; num_chunks= job->async_chunks; if(((off_t) num_chunks) * ((off_t) read_chunk) > chunks_limit) num_chunks= chunks_limit / read_chunk; async_md5= (num_chunks >= 2); if(async_md5) data_size= num_chunks * read_chunk * 2048; else data_size= read_chunk * 2048; Xorriso_alloc_meM(data, char, data_size); data_pt= data; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to check media readability", 2 * !!job->use_dev); if(ret<=0) goto ex; ret= burn_disc_get_profile(drive, &profile_no, profile_name); if(ret > 0) { if(profile_no >= 0x08 && profile_no <= 0x0a) { is_cd= 1; eccb_size= 1; } else if(profile_no >= 0x40 && profile_no <= 0x43) { eccb_size= 32; } else if(burn_drive_get_drive_role(drive) != 1) { eccb_size= 1; } } if(job->sector_map != NULL) { Sectorbitmap_get_layout(job->sector_map, §ors, §or_size, 0); sector_size/= 2048; } if(job->retry > 0) retry= 1; else if(job->retry == 0 && is_cd) retry= 1; if(flag & 4) { ret= iso_md5_start(&ctx); if(ret < 0) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } } state.xorriso= xorriso; state.ctx= ctx; state.spotlist= spotlist; state.md5_start= md5_start; state.next_tag= 0; state.chain_broken= 0; state.in_track_gap= 0; state.was_sb_tag= 0; state.md5_spot_value= Xorriso_read_quality_untesteD; state.md5_spot_lba= 0; state.slave_state= 0; state.chunk_size= read_chunk; if(async_md5) { state.num_chunks= num_chunks; Xorriso_alloc_meM(state.chunk, char *, num_chunks); Xorriso_alloc_meM(state.chunk_state, int, num_chunks); Xorriso_alloc_meM(state.chunk_fill, int, num_chunks); Xorriso_alloc_meM(state.chunk_lba, off_t, num_chunks); for(i= 0; i < state.num_chunks; i++) { state.chunk[i]= data + read_chunk * i * 2048; state.chunk_state[i]= 0; state.chunk_fill[i]= 0; state.chunk_lba[i]= 0; } ret= pthread_mutex_init(&(state.spot_mutex), NULL); if(ret != 0) { sprintf(xorriso->info_text, "-check_media: Cannot initialize thread mutex"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); goto ex; } } else state.num_chunks= 0; state.chunk_w_idx= 0; state.chunk_r_idx= 0; state.w_sleeps= 0; state.r_sleeps= 0; if(async_md5) { ret= Xorriso_start_chunk_md5(xorriso, &state, 0); if(ret <= 0) goto ex; } if(xorriso->read_speed_force > 0) /* initialize forced speed limit */ burn_nominal_slowdown(xorriso->read_speed_force, xorriso->read_speed_corr, &prev_time, &us_corr, (off_t) 0, 1); Xorriso_set_speed(xorriso, drive, xorriso->read_speed, 0, 1); Xorriso_process_msg_queues(xorriso,0); start_lba= from_lba; to_read= read_chunk; post_read_time= Sfile_microtime(0); for(i= 0; i < block_count; i+= to_read) { if(i != 0) data_skip= 0; skip_reading= 0; ret= Xorriso_check_for_abort(xorriso, job->abort_file_path, post_read_time, &last_abort_file_time, 0); if(ret == 1) goto abort_check; if(job->item_limit > 0 && Spotlist_count(spotlist, 0) + 2 >= job->item_limit) { sprintf(xorriso->info_text, "-check_media: Reached item_limit=%d", job->item_limit); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); goto abort_check; } pre_read_time= Sfile_microtime(0); if(job->time_limit > 0 && job->start_time + job->time_limit < pre_read_time) { sprintf(xorriso->info_text, "-check_media: Reached time_limit=%d", job->time_limit); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); abort_check:; if(prev_quality >= 0) { ret= Xorriso__add_spot(&state, start_lba, i + from_lba - start_lba, prev_quality, 0); if(ret <= 0) goto ex; } ret= Xorriso__add_spot(&state, i + from_lba, block_count - i, Xorriso_read_quality_untesteD, 0); if(ret > 0) ret= 2; goto ex; } to_read= read_chunk; skipped_to_read= 0; suspect_tao_end= 0; if(i + to_read > block_count) to_read= block_count - i; if(is_cd && i + to_read + 2 >= block_count) { /* Read last 2 blocks of CD track separately, because with TAO tracks they are always unreadable but with SAO tracks they contain data. */ if(to_read > 2) { to_read-= 2; } else { if(to_read > 1) to_read--; suspect_tao_end= 1; } } if(sector_size == read_chunk && from_lba % read_chunk == 0 && !skip_reading) { if(Sectorbitmap_is_set(job->sector_map, (i + from_lba) / sector_size, 0)){ quality= Xorriso_read_quality_valiD; skip_reading= 1; } } else if(sector_size > 0 && !skip_reading) { start_sec= (i + from_lba) / sector_size; end_sec= (i + to_read + from_lba) / sector_size; first_value= Sectorbitmap_is_set(job->sector_map, start_sec, 0); for(j= start_sec; j < end_sec; j++) if(Sectorbitmap_is_set(job->sector_map, j, 0) != first_value) break; to_read= j * sector_size - i - from_lba; skip_reading= !!first_value; if(skip_reading) quality= Xorriso_read_quality_valiD; } if(skip_reading) { pre_read_time= post_read_time= Sfile_microtime(0); skipped_to_read= to_read; } else { data_count= 0; pre_read_time= Sfile_microtime(0); if(async_md5) { ret= Xorriso__wait_chunk_md5(&state, 1, 0); if(ret <= 0) goto ex; data_pt= state.chunk[state.chunk_w_idx]; } ret= burn_read_data(drive, (i + from_lba) * (off_t) 2048, data_pt, to_read * (off_t) 2048, &data_count, (4 * !retry) | (16 * !!suspect_tao_end)); post_read_time= Sfile_microtime(0); time_diff= post_read_time - pre_read_time; total_time_diff+= time_diff; total_count++; if(ret <= 0) { Xorriso_process_msg_queues(xorriso,0); if(data_count / 2048 < to_read) { if(data_count > 0 && retry) { if(prev_quality >= 0) { ret= Xorriso__add_spot(&state, start_lba, i + from_lba - start_lba, prev_quality, 0); if(ret <= 0) goto ex; } ret= Xorriso__add_spot(&state, i + from_lba, data_count / (off_t) 2048, Xorriso_read_quality_partiaL, 0); if(ret <= 0) goto ex; start_lba= i + from_lba + data_count / 2048; if(suspect_tao_end && ret == -3) prev_quality= Xorriso_read_quality_tao_enD; else prev_quality= Xorriso_read_quality_unreadablE; } if(suspect_tao_end && ret == -3) quality= Xorriso_read_quality_tao_enD; else quality= Xorriso_read_quality_unreadablE; if(retry) /* skip one eccb_size */ to_read= data_count / 2048 + eccb_size; } else { /* (can hardly happen) */ quality= Xorriso_read_quality_partiaL; } fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret<0) goto ex; } else { quality= Xorriso_read_quality_gooD; if(time_diff > job->slow_threshold_seq && job->slow_threshold_seq > 0 && i > 0) quality= Xorriso_read_quality_sloW; } /* MD5 checksumming */ if(ctx != NULL) { if(async_md5) { state.chunk_fill[state.chunk_w_idx]= to_read; state.chunk_lba[state.chunk_w_idx]= i + from_lba; state.chunk_state[state.chunk_w_idx]= 1; /* The MD5 thread will call Xorriso_chunk_md5() */ state.chunk_w_idx= (state.chunk_w_idx + 1) % state.num_chunks; } else { ret= Xorriso_chunk_md5(xorriso, data_pt, to_read, i + from_lba, &state, 0); if(ret <= 0) goto ex; } } write_amount= data_count - data_skip; if(data_count > 0) { read_count+= data_count - data_skip; if(job->data_to_limit >= 0 && read_count > job->data_to_limit) write_amount-= (read_count - job->data_to_limit); } if(xorriso->read_speed_force > 0) { slowdown_count+= data_count; if(slowdown_count >= 128 * 1024) { burn_nominal_slowdown(xorriso->read_speed_force, xorriso->read_speed_corr, &prev_time, &us_corr, slowdown_count, 0); slowdown_count= 0; } } if(write_amount > 0) { if(job->data_to_fd >= 0) { seek_adr= (i + from_lba) * (off_t) 2048 + job->data_to_skip + job->data_to_offset; if(strcmp(job->data_to_path, "-") != 0) { ret= lseek(job->data_to_fd, seek_adr, SEEK_SET); if(ret == -1) { failed_to_write:; sprintf(xorriso->info_text, "Cannot write %d bytes to position %.f in ", (int) data_count, (double) seek_adr); Text_shellsafe(job->data_to_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); {ret= 0; goto ex;} } } ret= write(job->data_to_fd, data_pt + data_skip, write_amount); if(ret == -1) goto failed_to_write; } } } if(quality != prev_quality) { if(prev_quality >= 0) { ret= Xorriso__add_spot(&state, start_lba, i + from_lba - start_lba, prev_quality, 0); if(ret <= 0) goto ex; } start_lba= i + from_lba; prev_quality= quality; } if(!(flag & 2)) { xorriso->pacifier_count+= to_read - skipped_to_read; if(post_read_time - xorriso->last_update_time >= xorriso->pacifier_interval) Xorriso_pacifier_callback(xorriso, "blocks read", xorriso->pacifier_count, xorriso->pacifier_total, "", 8 | 16 | (128 * (job->use_dev == 1))); } } if(prev_quality >= 0) { ret= Xorriso__add_spot(&state, start_lba, block_count + from_lba - start_lba, prev_quality, 0); if(ret <= 0) goto ex; } /* <<< for calibration of quality */ if(total_count > 0) { sprintf(xorriso->info_text, "Xorriso_check_interval: %.1f s / %.f = %f", total_time_diff, (double) total_count, (double) total_time_diff / total_count); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } /* MD5 checksumming : register result */ if(async_md5) { ret= Xorriso__end_slave_md5(&state, 10000, 0); if(ret <= 0) goto ex; } /* >>> ??? allow chain_broken to be a match ? */ if(state.next_tag > 0) { sprintf(xorriso->info_text, "Missing announced MD5 tag: start=%.f pos=%.f", (double) state.md5_start, (double) state.next_tag); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); state.md5_spot_value= Xorriso_read_quality_md5_mismatcH; state.md5_spot_lba= state.next_tag; } if(state.md5_spot_value != Xorriso_read_quality_untesteD) { ret= Xorriso__add_spot(&state, state.md5_start, state.md5_spot_lba - state.md5_start, state.md5_spot_value, 0); if(ret <= 0) goto ex; } ret= 1; ex:; if(async_md5) { Xorriso__end_slave_md5(&state, 10000, 0); sprintf(xorriso->info_text, "async_chunks=%d , chunk_size=%.fs , w_sleeps: %.f , r_sleeps: %.f", state.num_chunks, (double) read_chunk, (double) state.w_sleeps, (double) state.r_sleeps); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); if(state.chunk != NULL) pthread_mutex_destroy(&(state.spot_mutex)); Xorriso_free_meM(state.chunk); Xorriso_free_meM(state.chunk_state); Xorriso_free_meM(state.chunk_fill); Xorriso_free_meM(state.chunk_lba); } Xorriso_free_meM(data); if(state.ctx != NULL) iso_md5_end(&(state.ctx), md5); return(ret); } int Xorriso_check_media(struct XorrisO *xorriso, struct SpotlisT **spotlist, struct CheckmediajoB *job, int flag) { int ret, mode, os_errno, j; int num_sessions, num_tracks, declare_untested= 0; int hret, quality, profile_no; off_t i, blocks, last_track_end= -1, count, track_lba, start_lba= 0; off_t media_blocks= 0, read_chunk= 32, md5_start, track_blocks; off_t read_capacity= -1, end_lba; off_t track_bad_claim= 0; char *toc_info= NULL, profile_name[80], msg[160]; struct burn_drive *drive; struct burn_drive_info *dinfo; enum burn_disc_status s; struct isoburn_toc_disc *isoburn_disc= NULL; struct isoburn_toc_session **isoburn_sessions; struct isoburn_toc_track **iso_burn_tracks; struct burn_toc_entry isoburn_entry; struct stat stbuf; struct burn_multi_caps *caps= NULL; *spotlist= NULL; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to check media readability", 2 * !!job->use_dev); if(ret<=0) goto ex; ret = burn_disc_get_profile(drive, &profile_no, profile_name); if(ret <= 0) profile_no= 0; if(job->min_block_size != 0) read_chunk= job->min_block_size; ret= Spotlist_new(spotlist, 0); if(ret <= 0) {ret= -1; goto ex;} if(job->sector_map_path[0]) { Sectorbitmap_destroy(&(job->sector_map), 0); if(stat(job->sector_map_path, &stbuf) != -1) { ret= Sectorbitmap_from_file(&(job->sector_map), job->sector_map_path, xorriso->info_text, &os_errno, 0); if(ret <= 0) { if(xorriso->info_text[0]) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, os_errno, "FAILURE", 0); goto ex; } } Xorriso_toc_to_string(xorriso, &toc_info, (2 * !!job->use_dev) | (4 * !job->map_with_volid)); } ret= Xorriso_open_job_data_to(xorriso, job, 0); if(ret <= 0) goto ex; Xorriso_pacifier_reset(xorriso, 0); job->start_time= time(NULL); mode= job->mode; if(job->min_lba > 0) { start_lba= job->min_lba; ret= Spotlist_add_item(*spotlist, (off_t) 0, job->min_lba, Xorriso_read_quality_untesteD, 0); if(ret <= 0) goto ex; } s= isoburn_disc_get_status(drive); if(s != BURN_DISC_APPENDABLE && s != BURN_DISC_FULL) { ret= 0; if(s == BURN_DISC_BLANK) { /* check whether medium is overwritable */ ret= burn_disc_get_multi_caps(drive, BURN_WRITE_NONE, &caps, 0); if(ret > 0) if(caps->start_adr == 0) ret= 0; } if(ret <= 0) { no_readable_medium:; Xorriso_msgs_submit(xorriso, 0, "-check_media: No readable medium found", 0, "SORRY", 0); ret= 0; goto ex; } } ret= burn_get_read_capacity_v2(drive, &read_capacity, 0); if(ret <= 0) read_capacity= -1; if(s == BURN_DISC_BLANK && read_capacity <= 0) goto no_readable_medium; if(job->max_lba >= 0) { blocks= job->max_lba + 1 - start_lba; xorriso->pacifier_total= blocks; ret= Xorriso_check_interval(xorriso, *spotlist, job, (int) start_lba, (int) blocks, read_chunk, (int) start_lba, 0); if(ret <= 0) goto ex; } else if(mode == 0) { /* track by track */ if(s == BURN_DISC_BLANK) { no_tracks_found:; Xorriso_msgs_submit(xorriso, 0, "-check_media: No tracks found on medium", 0, "SORRY", 0); ret= 0; goto ex; } isoburn_disc= isoburn_toc_drive_get_disc(drive); if(isoburn_disc == NULL) goto libburn_whole_disc; isoburn_sessions= isoburn_toc_disc_get_sessions(isoburn_disc, &num_sessions); for(i= 0; i < num_sessions; i++) { iso_burn_tracks= isoburn_toc_session_get_tracks(isoburn_sessions[i], &num_tracks); for(j= 0; j < num_tracks; j++) { isoburn_toc_track_get_entry(iso_burn_tracks[j], &isoburn_entry); if(!(isoburn_entry.extensions_valid & 1)) /* should not happen */ continue; track_lba= isoburn_entry.start_lba; track_blocks= isoburn_entry.track_blocks; /* The last track of an appendable BD-R reports more blocks than the read capacity allows. All BD-R track sizes are multiple of 64 kB. */ if (i == num_sessions - 1 && (track_lba + track_blocks > read_capacity && track_lba + track_blocks < read_capacity + 32 && (profile_no == 0x41 || profile_no == 0x40))) track_blocks= read_capacity - track_lba; if(track_lba + track_blocks > read_capacity) { if(track_bad_claim < track_lba + track_blocks) track_bad_claim= track_lba + track_blocks; if(track_lba >= read_capacity) { sprintf(msg, "-check_media: Track %d of session %d begins after end of readable medium area.", j + 1, (int) (i + 1)); Xorriso_msgs_submit(xorriso, 0, msg, 0, "WARNING", 0); continue; } else { if(profile_no >= 0x08 && profile_no <= 0x0a && track_lba + track_blocks == read_capacity + 2 && i == num_sessions - 1 && j == num_tracks - 1) { sprintf(msg, "-check_media: Last CD track exceeds readable area by 2 blocks. Assuming TAO."); Xorriso_msgs_submit(xorriso, 0, msg, 0, "DEBUG", 0); } else { sprintf(msg, "-check_media: Track %d of session %d extends over the end of readable medium area.", j + 1, (int) (i + 1)); Xorriso_msgs_submit(xorriso, 0, msg, 0, "WARNING", 0); } track_blocks= read_capacity - track_lba; } } md5_start= track_lba; if(i == 0 && j == 0) { if(track_lba == 32) { ret= burn_disc_get_multi_caps(drive, BURN_WRITE_NONE, &caps, 0); if(ret > 0) { if(caps->start_adr) { /* block 0 to 31 are the overall mount entry of overwritable */ track_lba= 0; track_blocks+= 32; } } } } if(last_track_end >= 0 && last_track_end < track_lba && last_track_end >= start_lba) { ret= Spotlist_add_item(*spotlist, last_track_end, track_lba - last_track_end, Xorriso_read_quality_off_tracK, 0); if(ret <= 0) goto ex; } last_track_end= track_lba + track_blocks; if(track_lba < start_lba) { track_blocks-= start_lba - track_lba; track_lba= start_lba; } if(track_blocks <= 0) continue; if(declare_untested) { ret= Spotlist_add_item(*spotlist, track_lba, track_blocks, Xorriso_read_quality_untesteD, 0); if(ret <= 0) goto ex; } else { ret= Xorriso_check_interval(xorriso, *spotlist, job, (int) track_lba, track_blocks, read_chunk, md5_start, (i > 0) | (4 * (xorriso->do_md5 & 1))); if(ret <= 0) goto ex; if(ret == 2) declare_untested= 1; } } } if(track_bad_claim > read_capacity) { count= Spotlist_count(*spotlist, 0); Spotlist_get_item(*spotlist, count - 1, &track_lba, &blocks, &quality, 0); if(profile_no >= 0x08 && profile_no <= 0x0a && track_bad_claim - read_capacity == 2 && quality != Xorriso_read_quality_tao_enD) quality= Xorriso_read_quality_tao_enD; else quality= Xorriso_read_quality_unreadablE; ret= Spotlist_add_item(*spotlist, read_capacity, (track_bad_claim - read_capacity), quality, 0); if(ret <= 0) goto ex; } } else if(mode == 1) { /* Image range */ /* Default is the emulated disc capacity. */ if(s == BURN_DISC_BLANK) goto no_tracks_found; isoburn_disc= isoburn_toc_drive_get_disc(drive); if(isoburn_disc == NULL) goto libburn_whole_disc; blocks= media_blocks= isoburn_toc_disc_get_sectors_v2(isoburn_disc); /* If possible, determine the end address of the loaded ISO image. */ track_lba= isoburn_get_attached_start_lba_v2(drive); if(track_lba >= 0) { ret= isoburn_read_iso_head_v2(drive, track_lba, &track_blocks, NULL, 0); if(ret > 0) { blocks= media_blocks= track_lba + track_blocks; } } if(start_lba >= 0) blocks-= start_lba; if(media_blocks <= 0) goto libburn_whole_disc; xorriso->pacifier_total= blocks; ret= Xorriso_check_interval(xorriso, *spotlist, job, (int) start_lba, (int) blocks, read_chunk, (int) start_lba, (4 * (xorriso->do_md5 & 1))); if(ret <= 0) goto ex; } else if(mode == 2) { libburn_whole_disc:; /* single sweep over libburn medium capacity */ ret= burn_get_read_capacity_v2(drive, &blocks, 0); if(ret <= 0) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "No content detected on media"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } blocks-= start_lba; xorriso->pacifier_total= blocks; ret= Xorriso_check_interval(xorriso, *spotlist, job, (int) start_lba, (int) blocks, read_chunk, (int) start_lba, (4 * (xorriso->do_md5 & 1))); if(ret <= 0) goto ex; } Xorriso_pacifier_callback(xorriso, "blocks read", xorriso->pacifier_count, xorriso->pacifier_total, "", 1 | 8 | 16 | 32 | (128 * (job->use_dev == 1))); ret= 1; ex:; if(job->data_to_fd != -1 && strcmp(job->data_to_path, "-") != 0) close(job->data_to_fd); job->data_to_fd= -1; if(read_capacity >= 0) { count= Spotlist_count(*spotlist, 0); end_lba= 0; for(i= 0; i < count; i++) { Spotlist_get_item(*spotlist, i, &start_lba, &blocks, &quality, 0); if(start_lba + blocks > end_lba) end_lba= start_lba + blocks; } if(read_capacity > end_lba) { hret= Spotlist_add_item(*spotlist, end_lba, (read_capacity - end_lba), Xorriso_read_quality_untesteD, 0); if(hret < ret) ret= hret; } } if(ret > 0) ret= Xorriso_update_in_sector_map(xorriso, *spotlist, read_chunk, job, 0); if(ret > 0) { ret= Xorriso_spotlist_to_sectormap(xorriso, *spotlist, read_chunk, &(job->sector_map), 2); if(ret > 0 && job->sector_map_path[0]) { ret= Sectorbitmap_to_file(job->sector_map, job->sector_map_path, toc_info, xorriso->info_text, &os_errno, 0); if(ret <= 0) { if(xorriso->info_text[0]) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, os_errno, "FAILURE", 0); } } } if(toc_info != NULL) free(toc_info); if(ret <= 0) Spotlist_destroy(spotlist, 0); if(caps!=NULL) burn_disc_free_multi_caps(&caps); if(isoburn_disc != NULL) isoburn_toc_disc_free(isoburn_disc); return(ret); } /* @param flag bit0= if not MMC drive print NOTE and return 2 bit1= obtain outdrive, else indrive bit4= do not report failure */ int Xorriso_get_drive_handles(struct XorrisO *xorriso, struct burn_drive_info **dinfo, struct burn_drive **drive, char *attempt, int flag) { int ret; if(flag&2) *dinfo= (struct burn_drive_info *) xorriso->out_drive_handle; else *dinfo= (struct burn_drive_info *) xorriso->in_drive_handle; if(*dinfo==NULL && !(flag & 16)) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "No %s drive acquired %s", (flag&2 ? "output" : "input"), attempt); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } if(*dinfo==NULL) return(0); *drive= (*dinfo)[0].drive; if(flag & 1) { ret= burn_drive_get_drive_role(*drive); if(ret != 1) { sprintf(xorriso->info_text, "Output device is not an MMC drive. Desired operation does not apply."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(2); } } return((*drive)!=NULL); } int Xorriso_pretend_full_disc(struct XorrisO *xorriso, int flag) { int ret; struct burn_drive_info *dinfo; struct burn_drive *drive; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to let libburn pretend having a closed medium", 2); if(ret<=0) return(ret); ret= isoburn_disc_pretend_full_uncond(drive); Xorriso_process_msg_queues(xorriso,0); if(ret <= 0) { sprintf(xorriso->info_text, "Failed to let libburn pretend having a closed medium"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } int Xorriso_scsi_dev_family(struct XorrisO *xorriso, int flag) { burn_preset_device_open(xorriso->drives_exclusive | (xorriso->linux_scsi_dev_family << 2), 0, 0); return(1); } int Xorriso_use_immed_bit(struct XorrisO *xorriso, int flag) { int enable= 1, ret; struct burn_drive_info *dinfo; struct burn_drive *drive; /* It is not an error if no drive is acquired. Xorriso_drive_aquire() will apply use_immed_bit. */ ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to control use of Immed bit", 2 | 16); if(ret<0) return(ret); if(ret == 0) return(1); if(xorriso->use_immed_bit == -1) { enable= 0; } else if(xorriso->use_immed_bit == 1) { enable= 1; } else if(xorriso->use_immed_bit == 0) { /* obtain default value as determined after drive aquiration */ if(xorriso->use_immed_bit_default == 0) return(1); enable= (xorriso->use_immed_bit_default > 0); } burn_drive_set_immed(drive, enable); Xorriso_process_msg_queues(xorriso,0); return(1); } int Xorriso_obtain_indev_readsize(struct XorrisO *xorriso, off_t *blocks, int flag) { int ret; off_t num_data; struct burn_drive_info *dinfo; struct burn_drive *drive; enum burn_disc_status s; *blocks= 0; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to determine readable size", 0); if(ret <= 0) return(0); s= isoburn_disc_get_status(drive); if(s == BURN_DISC_BLANK) return(1); ret= burn_get_read_capacity_v2(drive, &num_data, 0); if(ret <= 0) return(0); *blocks= num_data; return(1); } /* @param flag bit0= as_mkisofs, else cmd */ int Xorriso_feature_to_cmd(struct XorrisO *xorriso, char *name, char *value, int flag) { int i, as_m; double val_num= -123456789; static char *ignored_names[]= {"size", "eltorito", "tree_loaded", "tree_loaded_text", "rr_loaded", "aaip", "relaxed_vol_atts", "rrip_1_10_px_ino", ""}; sscanf(value, "%lf", &val_num); for(i= 0; ignored_names[i][0] != 0; i++) if(strcmp(name, ignored_names[i]) == 0) return(0); as_m= flag & 1; if(strcmp(name, "iso_level") == 0) { if(as_m) { sprintf(xorriso->result_line, "-iso-level %s", value); } else { sprintf(xorriso->result_line, "-compliance iso_9660_level=%s", value); } } else if(strcmp(name, "rockridge") == 0) { if(as_m) { if(val_num > 0.0) sprintf(xorriso->result_line, "-R"); else sprintf(xorriso->result_line, "--norock"); } else { sprintf(xorriso->result_line, "-rockridge %s", val_num > 0.0 ? "on" : "off"); } } else if(strcmp(name, "joliet") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "-J"); } else { sprintf(xorriso->result_line, "-joliet %s", val_num > 0.0 ? "on" : "off"); } } else if(strcmp(name, "iso1999") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "-iso-level 4"); } else { sprintf(xorriso->result_line, "-compliance iso_9660_1999%s", val_num > 0.0 ? "" : "_off"); } } else if(strcmp(name, "untranslated_name_len") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "-untranslated_name_len %s", value); } else { sprintf(xorriso->result_line, "-compliance untranslated_name_len=%s", value); } } else if(strcmp(name, "allow_dir_id_ext") == 0) { if(as_m) { if(val_num > 0.0) return(0); sprintf(xorriso->result_line, "-disallow_dir_id_ext"); } else { sprintf(xorriso->result_line, "-compliance allow_dir_id_ext%s", val_num > 0.0 ? "" : "_off"); } } else if(strcmp(name, "omit_version_numbers") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "-N"); } else { if(val_num <= 0.0) sprintf(xorriso->result_line, "-compliance omit_version_off:only_iso_version_off"); else if(val_num == 2.0) sprintf(xorriso->result_line, "-compliance omit_version_off:only_iso_version"); else sprintf(xorriso->result_line, "-compliance omit_version:only_iso_version_off"); } } else if(strcmp(name, "allow_deep_paths") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "-D"); } else { sprintf(xorriso->result_line, "-compliance deep_paths%s", val_num > 0.0 ? "" : "_off"); } } else if(strcmp(name, "allow_longer_paths") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "-U"); } else { sprintf(xorriso->result_line, "-compliance long_paths%s", val_num > 0.0 ? "" : "_off"); } } else if(strcmp(name, "allow_full_ascii") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "-U"); } else { sprintf(xorriso->result_line, "-compliance full_ascii%s", val_num > 0.0 ? "" : "_off"); } } else if(strcmp(name, "max_37_char_filenames") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "-max-iso9660-filenames"); } else { sprintf(xorriso->result_line, "-compliance long_names%s", val_num > 0.0 ? "" : "_off"); } } else if(strcmp(name, "no_force_dots") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "-d"); } else { sprintf(xorriso->result_line, "-compliance no_force_dots%s:no_j_force_dots%s", ((int) val_num) & 1 ? "" : "_off", ((int) val_num) & 2 ? "" : "_off"); } } else if(strcmp(name, "allow_lowercase") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "-allow-lowercase"); } else { sprintf(xorriso->result_line, "-compliance lowercase%s", val_num > 0.0 ? "" : "_off"); } } else if(strcmp(name, "joliet_longer_paths") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "-joliet-long"); } else { sprintf(xorriso->result_line, "-compliance joliet_long_paths%s", val_num > 0.0 ? "" : "_off"); } } else if(strcmp(name, "joliet_long_names") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "-joliet-long"); } else { sprintf(xorriso->result_line, "-compliance joliet_long_names%s", val_num > 0.0 ? "" : "_off"); } } else if(strcmp(name, "joliet_utf16") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "-joliet-utf16"); } else { sprintf(xorriso->result_line, "-compliance joliet_utf16%s", val_num > 0.0 ? "" : "_off"); } } else if(strcmp(name, "rrip_version_1_10") == 0) { if(as_m) { return(0); } else { sprintf(xorriso->result_line, "-compliance %s_rr", val_num > 0.0 ? "old" : "new"); } } else if(strcmp(name, "aaip_susp_1_10") == 0) { if(as_m) { return(0); } else { sprintf(xorriso->result_line, "-compliance aaip_susp_1_10%s", val_num > 0.0 ? "" : "_off"); } } else if(strcmp(name, "record_md5_session") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "--md5"); } else { sprintf(xorriso->result_line, "-md5 %s", val_num > 0.0 ? "on" : "off"); } } else if(strcmp(name, "record_md5_files") == 0) { if(as_m) { if(val_num <= 0.0) return(0); sprintf(xorriso->result_line, "--md5"); } else { sprintf(xorriso->result_line, "-md5 %s", val_num > 0.0 ? "on" : "off"); } } else { sprintf(xorriso->info_text, "Program error: unexpected feature name '%s'", name); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); return(-1); } return(1); } /* @param flag bit0= do not print xorriso->result_line, but accumulate it */ int Xorriso_assess_written_features(struct XorrisO *xorriso, char *mode, int flag) { int ret, l, ftext_l, replay_count= 0, max_line_size= 2 * SfileadrL; struct burn_drive_info *dinfo; struct burn_drive *drive; enum burn_disc_status s; struct isoburn_read_opts *ropts= NULL; IsoReadImageFeatures *features= NULL; struct isoburn_imgen_opts *imgen_opts= NULL; char *ftext= NULL, *cpt, *npt, *ept, *prev_line= NULL, *cmd_line= NULL; char *result_acc= NULL; static char *tree_loaded_names[3]= {"ISO9660", "Joliet", "ISO9660:1999"}; int tree_loaded_names_max= 2; Xorriso_alloc_meM(prev_line, char, max_line_size); Xorriso_alloc_meM(cmd_line, char, max_line_size); if(flag & 1) { Xorriso_alloc_meM(result_acc, char, 10 * SfileadrL); result_acc[0]= 0; } prev_line[0]= 0; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "when assessing written features", 0); if(ret <= 0) {ret= 0; goto ex;} s= isoburn_disc_get_status(drive); if(s != BURN_DISC_APPENDABLE && s != BURN_DISC_FULL) { Xorriso_msgs_submit(xorriso, 0, "The disc in the input drive offers no readable content", 0, "NOTE", 0); ret= 2; goto ex; } ret= Xorriso_make_read_options(xorriso, drive, &ropts, 1); if(ret <= 0) goto ex; ret = isoburn_assess_written_features(drive, ropts, &features, &imgen_opts, 0); Xorriso_process_msg_queues(xorriso,0); /* <<< Resetting to normal thresholds, after Xorriso_make_read_options */ if(xorriso->img_read_error_mode > 0) Xorriso_set_abort_severity(xorriso, 0); if(ret <= 0) {ret= 0; goto ex;} ret= iso_read_image_features_text(features, 1, &ftext); if(ret < 0) {ret= 0; goto ex;} /* print results, depending on mode */ ftext_l= strlen(ftext); for(cpt= ftext ; cpt - ftext < ftext_l ; cpt+= l + 1) { npt= strchr(cpt, '\n'); if(npt == NULL) l= strlen(cpt); else l= npt - cpt; cpt[l]= 0; ept= strchr(cpt, '='); if(ept == NULL) continue; if(strcmp(mode, "cmd") == 0 || strcmp(mode, "replay") == 0 || strcmp(mode, "as_mkisofs") == 0 ) { *ept= 0; ret= Xorriso_feature_to_cmd(xorriso, cpt, ept + 1, strcmp(mode, "as_mkisofs") == 0); if(ret <= 0) continue; } else { /* "plain" and any other */ strcpy(xorriso->result_line, "Indev feature: "); if(strncmp(cpt, "tree_loaded=", 12) == 0) { sprintf(xorriso->result_line + strlen(xorriso->result_line), "tree_loaded=%d", xorriso->tree_loaded); } else if(strncmp(cpt, "tree_loaded_text=", 17) == 0) { if(xorriso->tree_loaded >= 0 && xorriso->tree_loaded <= tree_loaded_names_max) sprintf(xorriso->result_line + strlen(xorriso->result_line), "tree_loaded_text=%s", tree_loaded_names[xorriso->tree_loaded]); } else if(strncmp(cpt, "rr_loaded=", 10) == 0) { sprintf(xorriso->result_line + strlen(xorriso->result_line), "rr_loaded=%d", xorriso->rr_loaded); } else { strcat(xorriso->result_line, cpt); } } /* Truncate to plausible length */ xorriso->result_line[max_line_size - 1]= 0; if(strcmp(xorriso->result_line, prev_line) == 0) continue; strcpy(prev_line, xorriso->result_line); if(strcmp(mode, "replay") == 0) { /* Perform result_line as command */ strcpy(cmd_line, xorriso->result_line); ret= Xorriso_execute_option(xorriso, cmd_line, (1 << 16)); if(ret <= 0) { /* >>> ??? what to do on error */; } replay_count++; } else { strcat(xorriso->result_line, "\n"); if(flag & 1) { if(strlen(result_acc) + strlen(xorriso->result_line) < 10 * SfileadrL) strcat(result_acc, xorriso->result_line); } else { Xorriso_result(xorriso, 0); } } } if(strcmp(mode, "replay") == 0) { sprintf(xorriso->info_text, "-assess_indev_features replay : Number of performed commands: %d\n", replay_count); Xorriso_info(xorriso, 0); } if(flag & 1) strcpy(xorriso->result_line, result_acc); ret= 1; ex:; Xorriso_process_msg_queues(xorriso,0); if(ropts != NULL) isoburn_ropt_destroy(&ropts, 0); if(features != NULL) iso_read_image_features_destroy(features); if(imgen_opts != NULL) isoburn_igopt_destroy(&imgen_opts, 0); Xorriso_free_meM(ftext); Xorriso_free_meM(result_acc); Xorriso_free_meM(cmd_line); Xorriso_free_meM(prev_line); return(ret); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2010 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of functions which operate on drives and media. */ #ifndef Xorriso_pvt_drive_mgt_includeD #define Xorriso_pvt_drive_mgt_includeD yes int Xorriso_may_burn(struct XorrisO *xorriso, int flag); int Xorriso_toc_line(struct XorrisO *xorriso, int flag); int Xorriso_media_product(struct XorrisO *xorriso, int flag); int Xorriso_check_md5_range(struct XorrisO *xorriso, off_t start_lba, off_t end_lba, char md5[16], int flag); int Xorriso_check_interval(struct XorrisO *xorriso, struct SpotlisT *spotlist, struct CheckmediajoB *job, off_t from_lba, off_t block_count, off_t read_chunk, off_t md5_start, int flag); int Xorriso_get_drive_handles(struct XorrisO *xorriso, struct burn_drive_info **dinfo, struct burn_drive **drive, char *attempt, int flag); int Xorriso_check_for_abort(struct XorrisO *xorriso, char *abort_file_path, double post_read_time, double *last_abort_file_time, int flag); #endif /* ! Xorriso_pvt_drive_mgt_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of emulators for mkisofs and cdrecord. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <fcntl.h> #include <errno.h> #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" int Xorriso_cdrskin_uses_stdout(struct XorrisO *xorriso, int argc, char **argv, int flag) { int i; for(i= 0; i < argc; i++) { if(strcmp(argv[i], "dev=-") == 0 || strcmp(argv[i], "dev=stdio:/dev/fd/1") == 0 || strcmp(argv[i], "-dev=-") == 0 || strcmp(argv[i], "-dev=stdio:/dev/fd/1") == 0) return(1); } return(0); } int Xorriso_cdrskin_help(struct XorrisO *xorriso, int flag) { static char helptext[][80]= { "Usage: xorriso -as cdrecord [options|source_addresses]", "Note: This is not cdrecord. See xorriso -help, xorriso -version, man xorrecord", "Options:", "\t-version\tprint version information and exit emulation", "\t--devices\tprint list of available MMC drives and exit emulation", "\tdev=target\tpseudo-SCSI target to use as CD-Recorder", "\tdrive_scsi_dev_family=sr|scd|sg\t\tselect Linux device type", "\t--drive_not_exclusive\tdo not try to prevent use of busy drive", "\t-v\t\tincrement verbose level by one", "\t-V\t\tincrement SCSI command transport verbose level by one", "\t-checkdrive\tcheck if a driver for the drive is present", "\t-inq\t\tdo an inquiry for the drive", "\tspeed=#\t\tset speed of drive", "\tblank=type\tblank a CD-RW disc (see blank=help)", "\tfs=#\t\tSet fifo size to # (0 to disable, default is 4 MB)", "\t-eject\t\teject the disk after doing the work", "\t-dummy\t\tdo everything with laser turned off", "\t-msinfo\t\tretrieve multi-session info for mkisofs >= 1.10", "\t-toc\t\tretrieve and print TOC/PMA data", "\t-atip\t\tretrieve media state, print \"Is *erasable\"", "\t-multi\t\tgenerate a TOC that allows multi session", "\t--multi_if_possible\tapply -multi if the medium supports it", "\t-waiti\t\twait until input is available before opening SCSI", "\t-tao\t\tWrite disk in TAO mode.", "\t-dao\t\tWrite disk in SAO mode.", "\t-sao\t\tWrite disk in SAO mode.", "\ttsize=#\t\tannounces exact size of source data", "\tpadsize=#\tAmount of padding", "\t-data\t\tSubsequent tracks are CD-ROM data mode 1 (default)", "\t-isosize\tUse iso9660 file system size for next data track", "\t-pad\t\tpadsize=30k", "\t-nopad\t\tDo not pad", "\tminbuf=\t\tKeep drive buffer hungry", "\t-immed\t\tEquivalent to minbuf=75", "\tmodesty_on_drive=\tLike minbuf=, but with more parameters", "\t--grow_overwriteable_iso\temulate multi-session on DVD+RW, BD-RE", "\twrite_start_address=#\t\twrite to byte address on DVD+RW, BD-RE", "\tstream_recording=on|number\ttry to get full speed on DVD-RAM, BD", "\tuse_immed_bit=on|off|default\tcontrol use of Immed bit", "\tdvd_obs=default|32k|64k\t\tbytes per DVD/BD write operation", "\t--obs_pad\t\tpad DVD DAO and stdio to full 32k or 64k", "\t--bdr_obs_exempt\tpossibly exempt BD-R from padding to full 64k", "\tstdio_sync=on|off|end|number\twhether to fsync output to \"stdio:\"", "\t--no_rc\t\tDo not execute xorriso startup files", "\t-help\t\tprint this text to stderr and exit emulation", "Actually this is the integrated ISO RockRidge filesystem manipulator xorriso", "lending its libburn capabilities to a very limited cdrecord emulation. Only", "a single data track can be burnt to blank, appendable or overwritable media.", "A much more elaborate cdrecord emulator is cdrskin from the libburn package.", "", #ifdef Xorriso_GNU_xorrisO "Report bugs to: bug-xorriso@gnu.org , or in private to: scdbackup@gmx.net .", "xorriso home page: <https://www.gnu.org/software/xorriso/>", "General help using GNU software: <https://www.gnu.org/gethelp/>", #else "Report any bugs to bug-xorriso@gnu.org or in private to scdbackup@gmx.net .", #endif "@End_of_helptexT@" }; int i; for(i= 0; strcmp(helptext[i], "@End_of_helptexT@")!=0; i++) { sprintf(xorriso->info_text, "%s\n", helptext[i]); Xorriso_info(xorriso,0); } return(1); } /* micro version of cdrskin */ int Xorriso_cdrskin(struct XorrisO *xorriso, char *whom, int argc, char **argv, int flag) { int ret, i, k, mem_do_close, aq_ret, eject_ret, hflag; off_t msc1, msc2; int do_atip= 0, do_checkdrive= 0, do_eject= 0, do_scanbus= 0; int do_toc= 0, do_verbous= 0, do_version= 0, do_help= 0, do_waiti= 0; int do_multi= 0, do_msinfo= 0, do_grow= 0, do_isosize= 0, do_xa1= 0; int do_auto_close= 0, mem_current_interpreter; double write_start_address= -1.0, tsize= -1.0, mem_auto_close; char *track_source= NULL, *dev_adr= NULL, *cpt; char mem_report_about_text[80], *report_about= "SORRY", blank_mode[80]; char speed[80], *argpt; /* cdrecord 2.01 options which are not scheduled for implementation, yet */ static char ignored_partial_options[][41]= { "timeout=", "debug=", "kdebug=", "kd=", "driver=", "ts=", "pregap=", "defpregap=", "mcn=", "isrc=", "index=", "textfile=", "pktsize=", "cuefile=", "gracetime=", "assert_write_lba=", "fifo_start_at=", "dev_translation=", "fallback_program=", "tao_to_sao_tsize=", "direct_write_amount=", "msifile=", "" }; static char ignored_full_options[][41]= { "-d", "-silent", "-s", "-setdropts", "-prcap", "-reset", "-abort", "-overburn", "-ignsize", "-useinfo", "-fix", "-nofix", "-raw", "-raw96p", "-raw16", "-clone", "-text", "-cdi", "-preemp", "-nopreemp", "-copy", "-nocopy", "-scms", "-shorttrack", "-noshorttrack", "-packet", "-noclose", "-media-info", "-minfo", "-load", "-lock", "-raw96r", "-swab", "-force", "-format", "--adjust_speed_to_drive", "--allow_emulated_drives", "--allow_setuid", "--allow_untested_media", "--any_track", "--demand_a_drive", "--fifo_disable", "--fifo_start_empty", "--fill_up_media", "--list_ignored_options", "--no_rc", "--no_convert_fs_adr", "--prodvd_cli_compatible", "--single_track", "--tell_media_space", "" }; static char blank_help[][80]= { "Blanking options:", "\tall\t\tblank the entire disk", "\tdisc\t\tblank the entire disk", "\tdisk\t\tblank the entire disk", "\tfast\t\tminimally blank the entire disk", "\tminimal\t\tminimally blank the entire disk", "\tas_needed\tblank or format medium to make it ready for (re-)use", "\tdeformat\t\tblank a formatted DVD-RW", "\tdeformat_quickest\tminimally blank a formatted DVD-RW to DAO only", "\tformat_overwrite\tformat a DVD-RW to \"Restricted Overwrite\"", "@End_of_helptexT@" }; mem_current_interpreter= xorriso->current_interpreter; xorriso->current_interpreter= 2; mem_do_close= xorriso->do_close; mem_auto_close= xorriso->auto_close; Xorriso_alloc_meM(track_source, char, SfileadrL); Xorriso_alloc_meM(dev_adr, char, SfileadrL); strcpy(mem_report_about_text, xorriso->report_about_text); track_source[0]= 0; dev_adr[0]= 0; blank_mode[0]= 0; speed[0]= 0; if(xorriso->in_drive_handle != NULL) { ret= Xorriso_option_dev(xorriso, "", 1|32); /* give up indev */ if(ret!=1) goto ex; } /* Assess plan, make settings */ for(i= 0; i<argc; i++) { sprintf(xorriso->info_text, "-as %s: ", whom); Text_shellsafe(argv[i], xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); argpt= argv[i]; if (strncmp(argpt, "--", 2) == 0 && strlen(argpt) > 3) argpt++; for(k=0;ignored_partial_options[k][0]!=0;k++) { if(argpt[0]=='-') if(strncmp(argpt+1,ignored_partial_options[k], strlen(ignored_partial_options[k]))==0) { argpt++; goto no_volunteer; } if(strncmp(argpt,ignored_partial_options[k], strlen(ignored_partial_options[k]))==0) goto no_volunteer; } for(k=0;ignored_full_options[k][0]!=0;k++) if(strcmp(argpt,ignored_full_options[k])==0) goto no_volunteer; if(0) { no_volunteer:; sprintf(xorriso->info_text, "-as %s: Ignored option ", whom); Text_shellsafe(argpt, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); continue; } if(strcmp(argpt, "-atip")==0) { do_atip= 1; } else if(strcmp(argpt, "-audio")==0) { sprintf(xorriso->info_text, "-as %s: Option -audio not supported.", whom); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } else if(strncmp(argpt, "-blank=", 7)==0 || strncmp(argpt, "blank=", 6)==0) { cpt= strchr(argpt, '=')+1; if(strcmp(cpt,"all")==0 || strcmp(cpt,"disc")==0 || strcmp(cpt,"disk")==0) { strcpy(blank_mode, "all"); } else if(strcmp(cpt,"fast")==0 || strcmp(cpt,"minimal")==0) { strcpy(blank_mode, "fast"); } else if(strcmp(cpt,"help")==0) { strcpy(blank_mode, "help"); } else if(strcmp(cpt,"deformat")==0 || strcmp(cpt,"deformat_sequential")==0 || strcmp(cpt,"deformat_quickest")==0 || strcmp(cpt,"deformat_sequential_quickest")==0) { strcpy(blank_mode, cpt); } else if(strcmp(cpt,"format_overwrite")==0) { strcpy(blank_mode, "format_overwrite"); } else if(strcmp(cpt,"as_needed")==0) { strcpy(blank_mode, "as_needed"); } else { sprintf(xorriso->info_text, "-as %s: blank=", whom); Text_shellsafe(cpt, xorriso->info_text, 1); strcat(xorriso->info_text, " not supported. See blank=help ."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } else if(strcmp(argpt, "-checkdrive")==0) { do_checkdrive= 1; } else if(strcmp(argpt, "-dao")==0) { xorriso->do_tao= -1; } else if(strcmp(argpt, "-data")==0) { /* ok */; } else if(strncmp(argpt, "-dev=", 5)==0 || strncmp(argpt, "dev=", 4)==0) { cpt= strchr(argpt, '=')+1; strcpy(dev_adr, cpt); } else if(strcmp(argv[i], "--devices")==0) { /* intentional: argv[i] */ do_scanbus= 2; } else if(strcmp(argv[i], "--drive_not_exclusive") == 0) { /* intentional */ Xorriso_option_drive_access(xorriso, "shared:unrestricted", 0); } else if(strncmp(argpt,"driveropts=", 11)==0 || strncmp(argpt,"-driveropts=", 12)==0) { if(strcmp(argpt+11, "help")==0) { fprintf(stderr,"Driver options:\n"); fprintf(stderr, "burnfree\tPrepare writer to use BURN-Free technology\n"); } } else if(strcmp(argpt, "-dummy")==0) { xorriso->do_dummy= 1; } else if(strncmp(argpt, "-dvd_obs=", 9)==0 || strncmp(argpt, "dvd_obs=", 8)==0) { cpt= strchr(argpt, '=') + 1; Xorriso_option_dvd_obs(xorriso, cpt, 0); } else if(strcmp(argv[i], "--obs_pad") == 0) { /* intentional: argv[i] */ xorriso->do_obs_pad= 1; } else if(strcmp(argv[i], "--bdr_obs_exempt") == 0) { /* intentional: argv[i] */ xorriso->bdr_obs_exempt= 1; } else if(strcmp(argpt, "-eject")==0) { do_eject= 1; } else if(strncmp(argpt, "-fs=", 4)==0 || strncmp(argpt, "fs=", 3)==0) { cpt= strchr(argpt, '=')+1; ret= Xorriso_option_fs(xorriso, cpt, 0); if(ret<=0) goto ex; } else if(strcmp(argv[i], "--grow_overwriteable_iso")==0 || strcmp(argv[i], "--grow_overwritable_iso")==0 || strcmp(argv[i], "--grow_overriteable_iso")==0 ) { /* (A history of typos) */ /* intentional: argv[i] */ do_grow= 1; } else if(strcmp(argpt, "-help")==0) { do_help= 1; } else if(strcmp(argpt, "-isosize")==0) { do_isosize= 1; } else if(strcmp(argpt, "-inq")==0) { do_checkdrive= 2; } else if(strcmp(argpt, "-mode2")==0) { Xorriso_msgs_submit(xorriso, 0, "Defaulting option -mode2 to option -data", 0, "NOTE", 0); } else if(strcmp(argpt, "-msinfo")==0) { do_msinfo= 1; } else if(strcmp(argpt, "-multi")==0) { do_multi= 1; do_auto_close= 0; } else if(strcmp(argv[i], "--multi_if_possible") == 0) { do_multi= 1; do_auto_close= 1; } else if(strcmp(argpt, "-nopad")==0) { xorriso->padding= 0; } else if(strcmp(argv[i], "--no_rc")==0) { /* intentional: argv[i] */ /* already performed in Xorriso_prescan_args */; } else if(strcmp(argpt, "-pad")==0) { xorriso->padding= 15*2048; } else if(strncmp(argpt, "-padsize=", 9)==0 || strncmp(argpt, "padsize=", 8)==0) { cpt= strchr(argpt, '=')+1; ret= Xorriso_option_padding(xorriso, cpt, 0); if(ret<=0) goto ex; } else if(strcmp(argpt, "-sao")==0) { xorriso->do_tao= -1; } else if(strcmp(argpt, "-scanbus")==0) { sprintf(xorriso->info_text, "-as %s: Option -scanbus not supported.", whom); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } else if(strncmp(argpt, "-speed=", 7)==0 || strncmp(argpt, "speed=", 6)==0) { cpt= strchr(argpt, '=')+1; strncpy(speed, cpt, 79); speed[79]= 0; } else if(strncmp(argpt, "-stream_recording=", 18)==0 || strncmp(argpt, "stream_recording=", 17)==0) { cpt= strchr(argpt, '=')+1; Xorriso_option_stream_recording(xorriso, cpt, 0); } else if(strncmp(argpt, "-use_immed_bit=", 15) == 0 || strncmp(argpt, "use_immed_bit=", 14) == 0) { cpt= strchr(argpt, '=') + 1; Xorriso_option_use_immed_bit(xorriso, cpt, 0); } else if(strcmp(argpt, "-immed") == 0) { Xorriso_option_modesty_on_drive(xorriso, "75", 0); } else if(strncmp(argpt, "-minbuf=", 8) == 0 || strncmp(argpt, "minbuf=", 7) == 0 || strncmp(argpt, "-modesty_on_drive=", 18) == 0 || strncmp(argpt, "modesty_on_drive=", 17) == 0) { cpt= strchr(argpt, '=') + 1; Xorriso_option_modesty_on_drive(xorriso, cpt, 0); } else if(strncmp(argpt, "-drive_scsi_dev_family=", 23) == 0 || strncmp(argpt, "drive_scsi_dev_family=", 22) == 0) { cpt= strchr(argpt, '=') + 1; Xorriso_option_scsi_dev_family(xorriso, cpt, 0); } else if(strncmp(argpt, "-stdio_sync=", 12)==0 || strncmp(argpt, "stdio_sync=", 11)==0) { cpt= strchr(argpt, '=') + 1; Xorriso_option_stdio_sync(xorriso, cpt, 0); } else if(strcmp(argpt, "-tao")==0) { xorriso->do_tao= 1; } else if(strcmp(argpt, "-toc")==0 || strcmp(argv[i], "--long_toc")==0) { /* intentional: argpt , argv[i] */ do_toc= 1; } else if(strncmp(argpt, "-tsize=", 7)==0 || strncmp(argpt, "tsize=", 6)==0) { cpt= strchr(argpt, '=')+1; tsize= Scanf_io_size(cpt, 1); if(tsize > 1024.0*1024.0*1024.0*1024.0*1024.0) { sprintf(xorriso->info_text, "-as %s: much too large: %s",whom, argpt); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } else if(strcmp(argv[i], "-V")==0 || strcmp(argpt,"-Verbose")==0) { Xorriso_option_scsi_log(xorriso, "on", 0); } else if(strcmp(argv[i], "-v")==0 || strcmp(argpt,"-verbose")==0) { do_verbous++; } else if(strcmp(argv[i], "-vv")==0) { /* intentional: argv[i] */ do_verbous+= 2; } else if(strcmp(argv[i], "-vvv")==0) { /* intentional: argv[i] */ do_verbous+= 3; } else if(strcmp(argpt, "-version")==0) { do_version= 1; } else if(strcmp(argpt, "-waiti")==0) { do_waiti= 1; } else if(strncmp(argv[i], "write_start_address=", 20)==0) { /* intentional: argv[i] */ write_start_address= Scanf_io_size(argv[i]+20,0); } else if(strcmp(argpt, "-xa")==0) { Xorriso_msgs_submit(xorriso, 0, "Defaulting option -xa to option -data", 0, "NOTE", 0); } else if(strcmp(argpt, "-xa1")==0) { if(do_xa1 == 0) do_xa1= 1; } else if(strcmp(argv[i], "--xa1-ignore")==0) { /* intentional: argv[i] */ do_xa1= -1; } else if(strcmp(argpt, "-xa2")==0) { Xorriso_msgs_submit(xorriso, 0, "Defaulting option -xa2 to option -data", 0, "NOTE", 0); } else if(strcmp(argpt, "-xamix")==0) { Xorriso_msgs_submit(xorriso, 0, "Option -xamix not implemented and data not yet convertible to other modes", 0, "FATAL", 0); ret= 0; goto ex; } else if(argpt[0]=='-' && argpt[1]!=0) { sprintf(xorriso->info_text, "-as %s: Unknown option ", whom); Text_shellsafe(argv[i], xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } else { if(track_source[0]) { sprintf(xorriso->info_text, "-as %s: Surplus track source ", whom); Text_shellsafe(argv[i], xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text, "First and only track source is "); Text_shellsafe(track_source, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 0; goto ex; } if(Sfile_str(track_source, argv[i], 0)<=0) {ret= -1; goto ex;} } } /* Perform actions */ Xorriso_option_report_about(xorriso, "NOTE", 0); if(do_version) { sprintf(xorriso->result_line, "Cdrecord 2.01-Emulation Copyright (C) 2019 see libburnia-project.org xorriso\n"); Xorriso_result(xorriso, 1); Xorriso_option_version(xorriso, 0); } if(do_help) { Xorriso_cdrskin_help(xorriso, 0); } if(strcmp(blank_mode, "help")==0) { for(i= 0; strcmp(blank_help[i], "@End_of_helptexT@")!=0; i++) { sprintf(xorriso->info_text, "%s\n", blank_help[i]); Xorriso_info(xorriso,0); } } if(do_help || strcmp(blank_mode, "help") == 0 || do_version) { ret= 1; goto ex; } if(do_verbous<=0) report_about= "NOTE"; else if(do_verbous<=2) report_about= "UPDATE"; else if(do_verbous==3) report_about= "DEBUG"; else report_about= "ALL"; Xorriso_option_report_about(xorriso, report_about, 0); if(do_scanbus) { if(do_scanbus==1) /* >>> would need -scanbus compatible output and input format */; else Xorriso_option_devices(xorriso, 0); ret= 1; goto ex; } if(!(do_checkdrive || do_atip || do_toc || blank_mode[0] || track_source[0] || do_eject || do_msinfo)) { sprintf(xorriso->info_text, "-as cdrskin: No option specified, which would cause an action."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); ret= 1; goto ex; } if(do_waiti) { sprintf(xorriso->info_text, "xorriso: Option -waiti pauses program until input appears at stdin\n"); Xorriso_info(xorriso,0); sprintf(xorriso->result_line, "Waiting for data on stdin...\n"); Xorriso_result(xorriso, 1); for(ret= 0; ret==0; ) ret= Wait_for_input(0,1000000,0); if(ret<0 || feof(stdin)) { Xorriso_msgs_submit(xorriso, 0, "stdin produces exception rather than data", 0, "NOTE", 0); } sprintf(xorriso->info_text, "xorriso: Option -waiti pausing is done.\n"); } if(dev_adr[0]) { hflag= 2 | 64; /* ts B11201 no more: | 32 */ if(!do_grow) hflag|= 8; /* consider overwritables as blank */ ret= Xorriso_option_dev(xorriso, dev_adr, hflag); if(ret<=0) goto ex; } if(xorriso->out_drive_handle==NULL) { sprintf(xorriso->info_text, "-as %s: No output drive selected", whom); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(do_msinfo) { ret= Xorriso_msinfo(xorriso, &msc1, &msc2, 2 | !!do_grow); if(ret<=0) goto ex; sprintf(xorriso->result_line, "%.f,%.f\n", (double) msc1, (double) msc2); Xorriso_result(xorriso, 1); } if(speed[0]) { ret= Xorriso_option_speed(xorriso, speed, 0); if(ret<=0) goto ex; } if(do_checkdrive) { ret= Xorriso_atip(xorriso, 2-(do_checkdrive==2)); if(ret<=0) goto ex; } if(do_atip) { ret= Xorriso_atip(xorriso, 0); if(ret<=0) goto ex; } if(do_toc) { ret= Xorriso_option_toc(xorriso, 0); if(ret<=0) goto ex; } if(strcmp(blank_mode, "format_overwrite")==0) { ret= Xorriso_option_blank(xorriso, "fast", 1); if(ret<=0) goto ex; } else if(blank_mode[0]) { ret= Xorriso_option_blank(xorriso, blank_mode, 0); if(ret<=0) goto ex; } if(track_source[0]) { xorriso->do_close= !do_multi; xorriso->auto_close= do_auto_close; ret= Xorriso_burn_track(xorriso, (off_t) write_start_address, track_source, (off_t) tsize, (!!do_grow) | ((!!do_isosize) << 1) | ((do_xa1 == 1) << 2)); if(ret == 2) { ret= Xorriso_retry_burn_track(xorriso, (off_t) write_start_address, track_source, (off_t) tsize, (!!do_grow) | ((!!do_isosize) << 1) | ((do_xa1 == 1) << 2)); } aq_ret= Xorriso_reaquire_outdev(xorriso, 2*(ret>0)); if(ret<=0 && ret<aq_ret) goto ex; if(aq_ret<=0) {ret= aq_ret; goto ex;} } ret= 1; ex:; if(do_eject && ret>=0) { eject_ret= Xorriso_option_eject(xorriso, "out", 0); if(eject_ret<ret) ret= eject_ret; } if(ret<=0) { sprintf(xorriso->info_text, "-as %s: Job could not be performed properly.", whom); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } Xorriso_option_report_about(xorriso, mem_report_about_text, 0); xorriso->do_close= mem_do_close; xorriso->auto_close= mem_auto_close; Xorriso_free_meM(dev_adr); Xorriso_free_meM(track_source); xorriso->current_interpreter= mem_current_interpreter; return(ret); } /* This function shall know all options of mkisofs, genisoimage, xorrisofs, ... and the number of arguments which they expect and consume. */ int Xorriso_genisofs_count_args(struct XorrisO *xorriso, int argc, char **argv, int *count, int flag) { int i; char *cmd; static char partial_options[][41]= { "errctl=", "isolinux_mbr=", "--modification-date=", "" }; static char arg0_options[][41]= { "-allow-leading-dots", "-ldots", "-allow-lowercase", "-allow-multidot", "-cache-inodes", "-no-cache-inodes", "-eltorito-alt-boot", "-hard-disk-boot", "-no-emul-boot", "-no-boot", "-boot-info-table", "-check-oldnames", "-d", "-D", "-dvd-video", "-f", "-gui", "-graft-points", "-hide-joliet-trans-tbl", "-hide-rr-moved", "-J", "-joliet-long", "-l", "-L", "-max-iso9660-filenames", "-N", "-nobak", "-no-bak", "-no-limit-pathtables", "-force-rr", "-no-rr", "-no-split-symlink-components", "-no-split-symlink-fields", "-pad", "-no-pad", "-posix-H", "-posix-L", "-posix-P", "-print-size", "-quiet", "-R", "-r", "-relaxed-filenames", "-rrip110", "-rrip112", "-split-output", "-T", "-UDF", "-udf", "-udf-symlinks", "-no-udf-symlinks", "-U", "-no-iso-translate", "-v", "-XA", "-xa", "-z", "-hfs", "-no-hfs", "-apple", "-probe", "-no-desktop", "-mac-name", "-part", "-icon-position", "-chrp-t", "-hfs-unlock", "--cap", "--netatalk", "--double", "--ethershare", "--ushare", "--exchange", "--sgi", "--xinet", "--macbin", "--single", "--dave", "--sfm", "--osx-double", "--osx-hfs", "-debug", "-omit-period", "-disable-deep-relocation", "-joliet", "-full-iso9660-filenames", "-follow-links", "-help", "-transparent-compression", "-omit-version-number", "-rational-rock", "-rock", "-translation-table", "-untranslated-filenames", "-verbose", "-version", "-g", "-h", "-no-mac-files", "-chrp-boot", "--hardlinks", "--acl", "--xattr", "--xattr-any", "--md5", "--for_backup", "--lfa_flags", "--protective-msdos-label", "--boot-catalog-hide", "--no-emul-toc", "--emul-toc", "-disallow_dir_id_ext", "--old-empty", "--old-root-no-md5", "--old-root-devno", "--old-root-no-ino", "--no_rc", "--norock", "-hfsplus", "-fat", "-chrp-boot-part", "-isohybrid-gpt-basdat", "-isohybrid-gpt-hfsplus", "-isohybrid-apm-hfsplus", "--grub2-boot-info", "-joliet-utf16", "-appended_part_as_gpt", "-appended_part_as_apm", "--mbr-force-bootable", "--gpt-iso-bootable", "--gpt-iso-not-ro", "-part_like_isohybrid", "--zisofs-version-2", "--zisofs2-susp-z2", "--zisofs2-susp-zf", "-dvd-audio", "-dvd-hybrid", "-long-rr-time", "-no-long-rr-time", "-short-rr-time", "-ignore-error", "-data-change-warn", "-genisoimage_completion", "" }; static char arg1_options[][41]= { "-abstract", "-A", "-appid", "--application_use", "-biblio", "-b", "-B", "-boot-load-seg", "-boot-load-size", "-C", "-c", "-check-session", "-copyright", "-dir-mode", "-eltorito-id", "-eltorito-selcrit", "-file-mode", "-G", "-gid", "-hide", "-hide-list", "-hidden", "-hidden-list", "-hide-joliet", "-hide-joliet-list", "-hide-hfsplus", "-hide-hfsplus-list", "-hide-udf", "-hide-udf-list", "-input-charset", "-output-charset", "-iso-level", "-jcharset", "-log-file", "-m", "-exclude-list", "-M", "-dev", "-new-dir-mode", "-o", "-p", "-preparer", "-path-list", "-publisher", "-root", "-old-root", "-s", "-sectype", "-sort", "-sparc-boot", "-sparc-label", "-stream-media-size", "-stream-file-name", "-sunx86-boot", "-sunx86-label", "-sysid", "-table-name", "-ucs-level", "-uid", "-V", "-volset", "-volset-size", "-volset-seqno", "-x", "-P", "-map", "-magic", "-hfs-creator", "-hfs-type", "-boot-hfs-file", "-auto", "-cluster-size", "-hide-hfs", "-hide-hfs-list", "-hfs-volid", "-root-info", "-prep-boot", "-input-hfs-charset", "-output-hfs-charset", "-hfs-bless", "-hfs-parms", "-eltorito-boot", "-generic-boot", "-eltorito-catalog", "-cdrecord-params", "-errctl", "-exclude", "-prev-session", "-output", "-use-fileversion", "-volid", "-old-exclude", "-alpha-boot", "-hppa-cmdline", "-hppa-kernel-32", "-hppa-kernel-64", "-hppa-bootloader", "-hppa-ramdisk", "-mips-boot", "-mipsel-boot", "-jigdo-jigdo", "-jigdo-template", "-jigdo-min-file-size", "-jigdo-force-md5", "-jigdo-force-checksum", "-md5-list", "-checksum-list", "-jigdo-exclude", "-jigdo-map", "-jigdo-template-compress", "-jigdo-checksum-algorithm", "-checksum_algorithm_iso", "-checksum_algorithm_template", "--stdio_sync", "--quoted_path_list", "--efi-boot", "--embedded-boot", "-isohybrid-mbr", "-e", "-partition_offset", "-partition_hd_cyl", "-partition_sec_hd", "-partition_cyl_align", "-untranslated_name_len", "-rr_reloc_dir", "-hfsplus-serial-no", "-prep-boot-part", "-efi-boot-part", "-hfsplus-block-size", "-apm-block-size", "--grub2-mbr", "--grub2-sparc-core", "--sort-weight-list", "--sort-weight-patterns", "-hppa-hdrversion", "-file_name_limit", "--set_all_file_dates", "--gpt_disk_guid", "-iso_mbr_part_type", "-eltorito-platform", "-modification-date", "-N", "-omit-version-number", "-new-dir-mode", "-nobak", "-no-bak", "-no-limit-pathtables", "", "", "" }; static char arg2_options[][41]= { "-hfs-bless-by", "-hide_iso_path", "--scdbackup_tag", "--sort-weight", "" }; static char arg3_options[][41]= { "-append_partition", "-hfsplus-file-creator-type", "" }; static char arg4_options[][41]= { "-cut_out", "" }; static char final_options[][41]= { "-find", "" }; cmd= argv[0]; *count= 0; for(i=0; partial_options[i][0]!=0; i++) if(strncmp(partial_options[i], cmd, strlen(partial_options[i]))==0) return(1); for(i=0; arg0_options[i][0]!=0; i++) if(strcmp(arg0_options[i], cmd)==0) return(1); *count= 1; for(i=0; arg1_options[i][0]!=0; i++) if(strcmp(arg1_options[i], cmd)==0) return(1); *count= 2; for(i=0; arg2_options[i][0]!=0; i++) if(strcmp(arg2_options[i], cmd)==0) return(1); *count= 3; for(i=0; arg3_options[i][0]!=0; i++) if(strcmp(arg3_options[i], cmd)==0) return(1); *count= 4; for(i=0; arg4_options[i][0]!=0; i++) if(strcmp(arg4_options[i], cmd)==0) return(1); *count= argc - 1; for(i=0; final_options[i][0]!=0; i++) if(strcmp(final_options[i], cmd)==0) return(1); *count= 0; return(0); } /* @param flag bit0= do not report eventual ignore decision */ int Xorriso_genisofs_ignore(struct XorrisO *xorriso, char *whom, char *argpt, int *i, int flag) { /* mkisofs 2.01 options which are not scheduled for implementation, yet */ static char ignored_arg0_options[][41]= { "-allow-leading-dots", "-ldots", "-allow-multidot", "-cache-inodes", "-check-oldnames", "-L", "-no-bak", "-no-cache-inodes", "-no-split-symlink-components", "-no-split-symlink-fields", "-nobak", "-force-rr", "-T", "-translation-table", "-no-iso-translate", "" }; static char ignored_arg1_options[][41]= { "-check-session", "-hide-hfs", "-hide-hfs-list", "-table-name", "-volset-seqno", "-volset-size", "-sort", "" }; int k; for(k=0;ignored_arg0_options[k][0]!=0;k++) if(strcmp(argpt,ignored_arg0_options[k])==0) goto no_volunteer; for(k=0;ignored_arg1_options[k][0]!=0;k++) if(strcmp(argpt,ignored_arg1_options[k])==0) { (*i)++; goto no_volunteer; } return(0); no_volunteer:; sprintf(xorriso->info_text, "-as %s: Ignored option ", whom); Text_shellsafe(argpt, xorriso->info_text, 1); if(!(flag & 1)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(1); } /* Try to match unrecognized options as start piece of original genisoimage options. But not the cdrecord or xorrisofs options which are not supported by genisoimage. (cdrecord does no option completion.) @param flag bit0= do not issue error messages */ int Xorriso_genisomage_opt_completion(struct XorrisO *xorriso, char *option, const char **found, int flag) { static char genisoimage_options[][41]= { "-abstract", "-A", "-appid", "-allow-leading-dots", "-ldots", "-allow-lowercase", "-allow-multidot", "-biblio", "-cache-inodes", "-no-cache-inodes", "-b", "-eltorito-boot", "-eltorito-alt-boot", "-B", "-sparc-boot", "-G", "-hard-disk-boot", "-no-emul-boot", "-no-boot", "-boot-load-seg", "-boot-load-size", "-boot-info-table", "-C", "-cdrecord-params", "-c", "-eltorito-catalog", "-check-oldnames", "-check-session", "-copyright", "-d", "-omit-period", "-D", "-disable-deep-relocation", "-data-change-warn", "-debug", "-dir-mode", "-dvd-video", "-f", "-follow-links", "-file-mode", "-gid", "-gui", "-graft-points", "-hide", "-hide-list", "-hidden", "-hidden-list", "-hide-joliet", "-hide-joliet-list", "-hide-joliet-trans-tbl", "-hide-rr-moved", "-input-charset", "-output-charset", "-iso-level", "-J", "-joliet-long", "-jcharset", "-l", "-full-iso9660-filenames", "-L", "-log-file", "-m", "-exclude-list", "-max-iso9660-filenames", "-M", "-dev", "-N", "-omit-version-number", "-new-dir-mode", "-nobak", "-no-bak", "-force-rr", "-no-rr", "-no-split-symlink-components", "-no-split-symlink-fields", "-o", "-pad", "-no-pad", "-path-list", "-P", "-publisher", "-p", "-preparer", "-print-size", "-quiet", "-R", "-rock", "-r", "-rational-rock", "-relaxed-filenames", "-root", "-old-root", "-s", "-sectype", "-sort", "-sparc-label", "-split-output", "-stream-media-size", "-stream-file-name", "-sunx86-boot", "-sunx86-label", "-sysid", "-T", "-translation-table", "-transparent-compression", "-table-name", "-ucs-level", "-udf", "-uid", "-use-fileversion", "-U", "-untranslated-filenames", "-no-iso-translate", "-V", "-volset", "-volset-size", "-volset-seqno", "-v", "-verbose", "-x", "-XA", "-xa", "-z", "-hfs", "-apple", "-map", "-magic", "-hfs-creator", "-hfs-type", "-probe", "-no-desktop", "-mac-name", "-boot-hfs-file", "-part", "-auto", "-cluster-size", "-hide-hfs", "-hide-hfs-list", "-hfs-volid", "-icon-position", "-root-info", "-prep-boot", "-chrp-t", "-input-hfs-charset", "-output-hfs-charset", "-hfs-unlock", "-hfs-bless", "-hfs-parms", "--cap", "--netatalk", "--double", "--ethershare", "--ushare", "--exchange", "--sgi", "--xinet", "--macbin", "--single", "--dave", "--sfm", "--osx-double", "--osx-hfs", "-alpha-boot", "-hppa-bootloader", "-hppa-cmdline", "-hppa-kernel-32", "-hppa-kernel-64", "-hppa-ramdisk", "-mips-boot", "-mipsel-boot", "-jigdo-jigdo", "-jigdo-template", "-jigdo-min-file-size", "-jigdo-force-md5", "-jigdo-exclude", "-jigdo-map", "-md5-list", "-jigdo-template-compress", "-chrp-boot", "-checksum_algorithm_template", "-checksum_algorithm_iso", "" }; int i, j, l, count= 0; char *opt, *orig_opt; /* search in genisoimage_options, ignoring leading dashes, count and memorize matches */; *found= ""; for(j= 0; option[j] != 0; j++) if(option[j] != '-') break; opt= option + j; l= strlen(opt); if(l == 0) goto failure; for(i= 0; genisoimage_options[i][0] != 0; i++) { for(j= 0; genisoimage_options[i][j] != 0; j++) if(genisoimage_options[i][j] != '-') break; orig_opt= genisoimage_options[i] + j; if(strncmp(opt, orig_opt, l) == 0) { count++; *found= genisoimage_options[i]; } } if(count == 1) { return(1); } else if(count == 0) { *found= option; if(flag & 1) goto failure; sprintf(xorriso->info_text, "No completion candidate for unrecognized option: %s", option); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } else { *found= option; if(flag & 1) goto failure; sprintf(xorriso->info_text, "Too many candidates for unrecognized option: %s", option); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); sprintf(xorriso->info_text, "List of genisoimage completion candidates:\n"); Xorriso_info(xorriso, 1 | 2); /* List all candidates */ strcpy(xorriso->info_text, " "); for(i= 0; genisoimage_options[i][0] != 0; i++) { for(j= 0; genisoimage_options[i][j] != 0; j++) if(genisoimage_options[i][j] != '-') break; orig_opt= genisoimage_options[i] + j; if(strncmp(opt, orig_opt, l) == 0) { if(strlen(xorriso->info_text) + strlen(genisoimage_options[i] + 3) > 72) { strcat(xorriso->info_text, "\n"); Xorriso_info(xorriso, 1 | 2); strcpy(xorriso->info_text, " "); strcat(xorriso->info_text, genisoimage_options[i]); } else { if(strlen(xorriso->info_text) > 4) strcat(xorriso->info_text, " , "); strcat(xorriso->info_text, genisoimage_options[i]); } } } strcat(xorriso->info_text, "\n"); Xorriso_info(xorriso, 1 | 2); } failure:; *found= option; return(0); } int Xorriso_genisofs_help(struct XorrisO *xorriso, int flag) { static char helptext[][160]= { "Usage: xorriso -as mkisofs [options] file...", "Note: This is not mkisofs. See xorriso -help, xorriso -version, man xorrisofs", "Options:", " -f, -follow-links Follow symbolic links", " -graft-points Allow to use graft points for filenames", " -help Print option help", " -hfsplus Generate HFS+ filesystem", " -hfsplus-file-creator-type CREATOR TYPE iso_rr_path", " Attach creator and type to a File", " -hfs-bless FOLDER_NAME Name of Folder to be blessed", " -hfs-bless-by BLESS_TYPE ISO_RR_PATH", " Bless ISO_RR_PATH by BLESS_TYPE {p,i,s,9,x}", " -hfsplus-serial-no HEXSTRING", " HFS serial number: 16 characters [0-9a-fA-F]", " -hfsplus-block-size NUMBER Set HFS+ block size", " -apm-block-size NUMBER Set Apple Partition Map block size", " -hide GLOBFILE Hide ISO9660/RR file", " -hide-list FILE File with list of ISO9660/RR files to hide", " -hide-joliet GLOBFILE Hide Joliet file", " -hide-joliet-list FILE File with list of Joliet files to hide", " -hide-hfsplus GLOBFILE Hide HFS+ file", " -hide-hfsplus-list FILE File with list of HFS+ files to hide", " -hide_iso_path HIDE_STATE ISO_RR_PATH", " Hide file by its ISO path", " -input-charset CHARSET Local input charset for file name conversion", " -output-charset CHARSET Output charset for file name conversion", " -iso-level LEVEL Set ISO9660 conformance level (1..3) or 4 for ISO9660 version 2", " -disallow_dir_id_ext Do not allow dot in ISO directory names", " -J, -joliet Generate Joliet directory information", " -joliet-long Allow Joliet file names to be 103 Unicode characters", " -joliet-utf16 Encode Joliet names in UTF-16BE rather than UCS-2", " -U, -untranslated-filenames Allow Untranslated filenames (for HPUX & AIX - violates ISO9660).", " -untranslated_name_len LEN Allow up to LEN (1..96) name characters (heavily violates ISO9660).", " -allow-lowercase Allow lower case characters in addition to the current character set (violates ISO9660)", " -relaxed-filenames Allow 7 bit ASCII except lower case characters (violates ISO9660)", " -d, -omit-period Omit trailing periods from filenames (violates ISO9660)", " -l, -full-iso9660-filenames Allow full 31 character filenames for ISO9660 names", " -max-iso9660-filenames Allow 37 character filenames for ISO9660 names (violates ISO9660)", " -N, -omit-version-number Omit version number from ISO9660 filename (violates ISO9660)", " -D, -disable-deep-relocation", " Disable deep directory relocation (violates ISO9660)", " -hide-rr-moved Relocate deep directories to /.rr_moved", " -rr_reloc_dir NAME Set deep directory relocation target in root", " -uid uid Make the owner of all files this uid.", " -gid gid Make the group owner of all files this gid.", " -o FILE, -output FILE Set output file name", " -m GLOBFILE, -exclude GLOBFILE", " Exclude file name", " -x FILE, -old-exclude FILE Exclude file name", " -exclude-list FILE File with list of file names to exclude", " -pad Pad output by 300k (default)", " -no-pad Do not pad output", " -M FILE, -prev-session FILE Set path to previous session to merge", " -C PARAMS, -cdrecord-params PARAMS", " Magic paramters from cdrecord", " -dir-mode mode Make the mode of all directories this mode.", " -file-mode mode Make the mode of all plain files this mode.", " -path-list FILE File with list of pathnames to process", " --quoted_path_list FILE File with list of quoted pathnames to process", " -print-size Print estimated filesystem size and exit", " -quiet Run quietly", " -genisoimage_completion Enable completion of genisoimage options", " -gui Switch behaviour for GUI", " -R, -rock Generate Rock Ridge directory information", " -r, -rational-rock Generate rationalized Rock Ridge directory information", " --norock Disable Rock Ridge. (Strongly discouraged !)", " -file_name_limit LEN Set truncation limit for Rock Ridge names", " --hardlinks Record eventual hard link relations of files", " --acl Record eventual ACLs of files", " --xattr Record eventual user space xattr of files", " --xattr-any Record xattr of any namespace, not only user.", " --lfa_flags Record Linux file attribute flags (chattr)", " --md5 Compute and record MD5 checksums of data files", " --scdbackup_tag PATH NAME With --md5 record a scdbackup checksum tag", " --for_backup Use all options which improve backup fidelity", " -V ID, -volid ID Set Volume ID", " -volset ID Set Volume set ID", " -publisher PUB Set Volume publisher", " -A ID, -appid ID Set Application ID", " -sysid ID Set System ID", " -p PREP, -preparer PREP Set Volume preparer", " -abstract FILE Set Abstract filename", " -biblio FILE Set Bibliographic filename", " -copyright FILE Set Copyright filename", " --application_use CHAR|PATH Set content of Application Use field", " -jigdo-jigdo FILE Produce a jigdo .jigdo file as well as the .iso", " -jigdo-template FILE Produce a jigdo .template file as well as the .iso", " -jigdo-min-file-size SIZE Minimum size for a file to be listed in the jigdo file", " -jigdo-force-checksum PTRN Pattern(s) where files MUST match an externally-supplied checksum", " -jigdo-force-md5 PATTERN Outdated alias of -jigdo-force-checksum", " -jigdo-exclude PATTERN Pattern(s) to exclude from the jigdo file", " -jigdo-map PATTERN1=PATTERN2", " Pattern(s) to map paths (e.g. Debian=/mirror/debian)", " -checksum-list FILE File containing checksums of the files that should be checked", " -md5-list FILE Outdated alias of -checksum-list", " -jigdo-checksum-algorithm ALGORITHM", " Choose algorithm for file matching checksums: md5, sha256", " Expected in the -checksum-list FILE, written into .jigdo file.", " -jigdo-template-compress ALGORITHM", " Choose to use gzip or bzip2 compression for template data; default is gzip", " -checksum_algorithm_iso alg1,alg2,...", " Specify the checksum types desired for the output image (in .jigdo)", " -checksum_algorithm_template alg1,alg2,...", " Specify the checksum types desired for the output jigdo template", " -eltorito-platform Set El Torito platform id for the next boot entry", " -b FILE, -eltorito-boot FILE", " Set El Torito boot image name", " -eltorito-alt-boot Start specifying alternative El Torito boot parameters", " --efi-boot FILE Set El Torito EFI boot image name and type", " -e FILE Set EFI boot image name (more rawly)", " -c FILE, -eltorito-catalog FILE", " Set El Torito boot catalog name", " --boot-catalog-hide Hide boot catalog from ISO9660/RR and Joliet", " -boot-load-size # Set numbers of load sectors", " -hard-disk-boot Boot image is a hard disk image", " -no-emul-boot Boot image is 'no emulation' image", " -boot-info-table Patch boot image with info table", " --grub2-boot-info Patch boot image at byte 2548", " -eltorito-id ID Set El Torito Id String", " -eltorito-selcrit HEXBYTES Set El Torito Selection Criteria", " -isohybrid-gpt-basdat Mark El Torito boot image as Basic Data in GPT", " -isohybrid-gpt-hfsplus Mark El Torito boot image as HFS+ in GPT", " -isohybrid-apm-hfsplus Mark El Torito boot image as HFS+ in APM", " -part_like_isohybrid Mark in MBR, GPT, APM without -isohybrid-mbr", " -iso_mbr_part_type Set type byte or GUID of ISO partition in MBR", " or type GUID if a GPT ISO partition emerges.", " --gpt_disk_guid GUID Set GPT disk GUID or choose automatic GUID", " -G FILE, -generic-boot FILE Set generic boot image name", " --embedded-boot FILE Alias of -G", " --protective-msdos-label Patch System Area by partition table", " --mbr-force-bootable Enforce existence of bootable flag in MBR", " --gpt-iso-bootable Set Legacy BIOS bootable flag in ISO partition", " --gpt-iso-not-ro Do not set Read-only flag in ISO partition", " -partition_offset LBA Make image mountable by first partition, too", " -partition_sec_hd NUMBER Define number of sectors per head", " -partition_hd_cyl NUMBER Define number of heads per cylinder", " -partition_cyl_align MODE Control cylinder alignment: off, on, auto, all", " -mips-boot FILE Set mips boot image name (relative to image root)", " -mipsel-boot FILE Set mipsel boot image name (relative to image root)", " -B FILES, -sparc-boot FILES Set sparc boot image names", " -sparc-label label text Set sparc boot disk label", " -hppa-cmdline CMDLINE Set hppa boot command line", " -hppa-kernel-32 FILE Set hppa 32-bit image name (relative to image root)", " -hppa-kernel-64 FILE Set hppa 64-bit image name (relative to image root)", " -hppa-bootloader FILE Set hppa boot loader file name (relative to image root)", " -hppa-ramdisk FILE Set hppa ramdisk file name (relative to image root)", " -hppa-hdrversion NUMBER Set hppa PALO header version to 4 or 5", " -alpha-boot FILE Set alpha boot image name (relative to image root)", " --grub2-sparc-core FILE Set path of core file for disk label patching", " -efi-boot-part DISKFILE|--efi-boot-image", " Set data source for EFI System Partition", " -chrp-boot-part Mark ISO image size by MBR partition type 0x96", " -chrp-boot Alias of -chrp-boot-part", " -prep-boot-part DISKFILE Set data source for MBR partition type 0x41", " -append_partition NUMBER TYPE FILE", " Append FILE after image. TYPE is hex: 0x.. or", " a GUID to be used if -appended_part_as_gpt.", " -appended_part_as_gpt mark appended partitions in GPT instead of MBR.", " -appended_part_as_apm mark appended partitions in APM.", " -cut_out DISK_PATH BYTE_OFFSET BYTE_COUNT ISO_RR_PATH", " map a byte interval of a regular disk file", " into a regular file in the ISO image", " --modification-date=YYYYMMDDhhmmsscc", " Override date of creation and modification", " --set_all_file_dates TIME Override mtime, atime, ctime in all files", " -isohybrid-mbr FILE Set SYSLINUX mbr/isohdp[fp]x*.bin for isohybrid", " --grub2-mbr FILE Set GRUB2 MBR for boot image address patching", #ifdef Xorriso_with_isohybriD " isolinux_mbr=on|auto|off Control eventual isohybrid MBR generation", #endif " --sort-weight NUMBER FILE Set LBA weight number to file or file tree", " --sort-weight-list DISKFILE Read list of NUMBER FILE pairs for --sort-weight", " --sort-weight-patterns DISKFILE --sort-weight-list with pattern expansion", " --stdio_sync on|off|number Control forced output to disk files", " --no-emul-toc Save 64 kB size on random access output files", " --emul-toc Multi-session history on such output files", " --old-empty Use old style block addresses for empty files", " -z, -transparent-compression", " Enable transparent compression of files", " --zisofs-version-2 Enable processing of zisofs version 2 files", " --zisofs2-susp-z2 Produce Z2 entries for zisofs version 2", " --zisofs2-susp-zf Produce ZF entries for zisofs version 2", " -root DIR Set root directory for all new files and directories", " -old-root DIR Set root directory in previous session that is searched for files", " --old-root-no-md5 Do not record and use MD5 with -old-root", " --old-root-no-ino Do not use disk inode numbers with -old-root", " --old-root-devno Use disk device numbers with -old-root", " -log-file LOG_FILE Re-direct messages to LOG_FILE", " --no_rc Do not execute startup files", " -v, -verbose Verbose", " -version Print the current version", "", #ifdef Xorriso_GNU_xorrisO "Report bugs to: bug-xorriso@gnu.org , or in private to: scdbackup@gmx.net .", "xorriso home page: <https://www.gnu.org/software/xorriso/>", "General help using GNU software: <https://www.gnu.org/gethelp/>", #else "Report any bugs to bug-xorriso@gnu.org or in private to scdbackup@gmx.net .", #endif "@End_of_helptexT@" }; char ra_text[80]; int i; strcpy(ra_text, xorriso->report_about_text); Xorriso_option_report_about(xorriso, "NOTE", 0); for(i= 0; strcmp(helptext[i], "@End_of_helptexT@")!=0; i++) { sprintf(xorriso->info_text, "%s\n", helptext[i]); Xorriso_info(xorriso, 1 | 2); } Xorriso_option_report_about(xorriso, ra_text, 0); return(1); } /* Perform hiding. Cumbersome: The paths and patterns apply to the disk address and not to the Rock Ridge address. Actually even the literal form of the mkisofs pathspec would matter (e.g. "./" versus ""). But xorriso normalizes disk_paths before further processing. Thus the literal form does not matter. @param hide_attrs bit0= hide in ISO/RR bit1= hide in Joliet bit2= hide in HFS+ bit3 to bit5 are reserved for future hidings */ int Xorriso_genisofs_hide(struct XorrisO *xorriso, char *whom, char *pattern, int hide_attrs, int flag) { int zero= 0, ret; char *argv[1]; if((hide_attrs & 63) == 0) return(2); if(strchr(pattern, '/') != NULL) { argv[0]= pattern; ret= Xorriso_option_not_paths(xorriso, 1, argv, &zero, 4 | ((hide_attrs & 63) << 8)); } else { ret= Xorriso_option_not_leaf(xorriso, pattern, hide_attrs & 63); } return(ret); } /* @param flag bit0= quoted list */ int Xorriso_genisofs_hide_list(struct XorrisO *xorriso, char *whom, char *adr, int hide_attrs, int flag) { int ret, linecount= 0, argc= 0, was_failure= 0, i, fret; char **argv= NULL, *id= ""; FILE *fp= NULL; if(adr[0]==0) { if (hide_attrs & 2) id = "joliet-"; else if (hide_attrs & 4) id = "hfsplus-"; sprintf(xorriso->info_text, "Empty file name given with -as %s -hide-%slist", whom, id); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); return(0); } ret= Xorriso_afile_fopen(xorriso, adr, "rb", &fp, 0); if(ret <= 0) return(0); while(1) { ret= Xorriso_read_lines(xorriso, fp, &linecount, &argc, &argv, 4 | (flag & 1) ); if(ret <= 0) goto ex; if(ret == 2) break; for(i= 0; i < argc; i++) { if(argv[i][0] == 0) continue; ret= Xorriso_genisofs_hide(xorriso, whom, argv[i], hide_attrs, 0); if(ret <= 0 || xorriso->request_to_abort) { was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; if(ret > 0) ret= 0; goto ex; } } } ret= 1; ex:; Xorriso_read_lines(xorriso, fp, &linecount, &argc, &argv, 2); if(fp != NULL && fp != stdin) fclose(fp); if(ret<=0) return(ret); return(!was_failure); } /* Strip surplus dash from known single-dash long options */ int Xorriso_genisofs_strip_dash(struct XorrisO *xorriso, char *arg_in, char **arg_out, int flag) { int ret, count; char *argv[1]; *arg_out= arg_in; if(strlen(arg_in) < 4) return(1); if(arg_in[0] != '-' || arg_in[1] != '-' || arg_in[2] == '-') return(1); argv[0]= arg_in + 1; ret= Xorriso_genisofs_count_args(xorriso, 1, argv, &count, 0); if(ret > 0) *arg_out= arg_in + 1; return(1); } /* Interprets a string of single-char options which have no parameters @param flag bit0= check whether string is ok bit1= this is pass 1 bit2= do not issue error messages @return with flag bit0: 0=no , 1=yes, 2= with bit1: non-pass-1 options seen else : 1 = ok , <= 0 indicates error */ int Xorriso_genisofs_fused_options(struct XorrisO *xorriso, char *whom, char *opts, int *option_d, int *iso_level, int *lower_r, char ra_text[80], int flag) { int ret, non_pass1= 0; char *cpt; static char pass1_covered[]= {"fvz"}; static char covered[]= {"dDfJlNRrTUvz"}; if(flag & 1) { for(cpt= opts; *cpt != 0; cpt++) { if(strchr(covered, *cpt) == NULL) {ret= 0; goto ex;} if(flag & 2) if(strchr(pass1_covered, *cpt) == NULL) non_pass1= 1; } ret= 1 + non_pass1; goto ex; } for(cpt= opts; *cpt != 0; cpt++) { if(*cpt == 'd') { if(flag & 2) continue; Xorriso_relax_compliance(xorriso, "no_force_dots", 0); } else if(*cpt == 'D') { if(flag & 2) continue; *option_d= 1; } else if(*cpt == 'f') { if(!(flag & 2)) continue; ret= Xorriso_option_follow(xorriso, "on", 0); if(ret <= 0) goto ex; } else if(*cpt == 'J') { if(flag & 2) continue; xorriso->do_joliet= 1; } else if(*cpt == 'l') { if(flag & 2) continue; if(xorriso->iso_level <= 2) Xorriso_relax_compliance(xorriso, "iso_9660_level=2", 0); if(*iso_level <= 2) *iso_level= 2; } else if(*cpt == 'N') { if(flag & 2) continue; Xorriso_relax_compliance(xorriso, "omit_version", 0); } else if(*cpt == 'R') { if(flag & 2) continue; xorriso->do_rockridge= 1; } else if(*cpt == 'r') { if(flag & 2) continue; xorriso->do_rockridge= 1; *lower_r= 1; } else if(*cpt == 'T') { /* ignored */; } else if(*cpt == 'U') { if(flag & 2) continue; Xorriso_relax_compliance(xorriso, "no_force_dots:long_paths:long_names:omit_version:full_ascii:lowercase", 0); } else if(*cpt == 'v') { if(!(flag & 2)) continue; strcpy(ra_text, "UPDATE"); } else if(*cpt == 'z') { if(!(flag & 2)) continue; Xorriso_option_zisofs(xorriso, "by_magic=on", 0); } else { if(flag & 4) {ret= 0; goto ex;} sprintf(xorriso->info_text, "-as %s: Unsupported option -%c", whom, *cpt); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } ret= 1; ex:; return(ret); } /* @param flag bit0= do not issue error messages @return <=0= error , 1= known option , 2= fused options , 3= completed option 4= completion is not enabled */ int Xorriso_genisofs_eff_opt(struct XorrisO *xorriso, char *whom, char *option, const char **found, int flag) { int ret, count,dummy; char *argv[1], ra_text[80]; *found= option; if(!xorriso->genisoimage_completion) return(4); /* Check whether option is known */ argv[0]= option; ret= Xorriso_genisofs_count_args(xorriso, 1, argv, &count, 0); if(ret > 0) { *found= option; return(1); } /* Xorriso_genisofs_fused_options() shall take over if all letters in the unknown option are single-letter options without parameter. genisoimage interprets known single letter options as fused, eats further arguments if these options expect parameters, and then tries completion with the rest. cdrecord interprets known single letter options as fused only if they do not expect parameters (like -m or -o). No options completion happens. */ ret= Xorriso_genisofs_fused_options(xorriso, whom, option, &dummy, &dummy, &dummy, ra_text, ((flag & 1) << 2) | 1); if(ret > 0) { *found= option; return(2); } /* If not known: try to get it by completion of original mkisofs and genisoimage options */ ret= Xorriso_genisomage_opt_completion(xorriso, option, found, flag & 1); if(ret > 0) return(3); return(0); } int Xorriso_genisofs_add_boot(struct XorrisO *xorriso, int flag) { int ret; if(xorriso->boot_img_size_default && xorriso->boot_image_emul == 0) xorriso->boot_img_full_size= 1; ret= Xorriso_attach_boot_image(xorriso, 0); if(ret <= 0) xorriso->boot_image_bin_path[0]= 0; return(ret); } /* Implementing mkisofs tendency to map single-path pathspecs to / */ int Xorriso_graftable_pathspec(struct XorrisO *xorriso, char *in_pathspec, char *pathspec, int flag) { int ret, l; char *esc_wdx= NULL, *eff_path= NULL, *ept; if((!xorriso->allow_graft_points) || Fileliste__target_source_limit(in_pathspec, '=', &ept, 0) <= 0) { Xorriso_alloc_meM(esc_wdx, char, SfileadrL); Xorriso_alloc_meM(eff_path, char, SfileadrL); strcpy(esc_wdx, xorriso->wdx); if(!xorriso->allow_graft_points) { ret= Fileliste__escape_source_path(esc_wdx, SfileadrL, 0); if(ret <= 0) { Xorriso_msgs_submit(xorriso, 0, "Escaped -cdx directory gets much too long", 0, "FAILURE", 0); ret= 0; goto ex; } } ret= Xorriso_normalize_img_path(xorriso, esc_wdx, in_pathspec, eff_path, 2|4); if(ret <= 0) {ret= 0; goto ex;} ret= Sfile_type(eff_path, 1 | ((xorriso->do_follow_param || xorriso->do_follow_links) << 2)); if(ret == 2) { strcpy(pathspec, "/="); } else { pathspec[0]= '/'; pathspec[1]= 0; ret= Sfile_leafname(eff_path, pathspec + 1, 0); if(ret>0) { if(!xorriso->allow_graft_points) { ret= Fileliste__escape_source_path(pathspec, SfileadrL, 0); if(ret <= 0) { Xorriso_msgs_submit(xorriso, 0, "Escaped leaf name gets much too long", 0, "FAILURE", 0); ret= 0; goto ex; } } strcat(pathspec, "="); } else pathspec[0]= 0; } l= strlen(pathspec); strcat(pathspec, eff_path); if(!xorriso->allow_graft_points) { ret= Fileliste__escape_source_path(pathspec + l, 2 * SfileadrL - l, 0); if(ret <= 0) { Xorriso_msgs_submit(xorriso, 0, "Escaped path gets much too long", 0, "FAILURE", 0); ret= 0; goto ex; } } } else { Sfile_str(pathspec, in_pathspec, 0); } ret= 1; ex:; Xorriso_free_meM(esc_wdx); Xorriso_free_meM(eff_path); return(ret); } int Xorriso_genisofs_path_pecul(struct XorrisO *xorriso, int *was_path, int with_emul_toc, int *allow_dir_id_ext, int *iso_level, int flag) { char *sfe= NULL; int ret; if(*was_path) { ret= 1; goto ex; } *was_path= 1; Xorriso_alloc_meM(sfe, char, 5*SfileadrL); /* Enforce odd mkisofs defaults on first pathspec */ xorriso->pacifier_style= 1; if(xorriso->allow_dir_id_ext_dflt && *allow_dir_id_ext < 0) *allow_dir_id_ext= 1; if(*allow_dir_id_ext == 1) { Xorriso_relax_compliance(xorriso, "allow_dir_id_ext", 0); *allow_dir_id_ext= 2; } if(xorriso->iso_level_is_default && *iso_level < 0) *iso_level= 1; if(*iso_level >= 1 && *iso_level <= 3) { sprintf(sfe, "iso_9660_level=%d", *iso_level); Xorriso_relax_compliance(xorriso, sfe, 0); iso_level= 0; } /* For the sake of compatibility give up emulated multi-session by default */ if(with_emul_toc == 0) xorriso->no_emul_toc|= 1; /* mkisofs records mtime in ECMA-119 and Joliet */ Xorriso_relax_compliance(xorriso, "rec_mtime", 0); /* mkisofs is substantially faster than xorriso by not fsyncing */ if(xorriso->stdio_sync_is_default) Xorriso_option_stdio_sync(xorriso, "off", 0); Xorriso_free_meM(sfe); ret= 1; ex:; return(ret); } int Xorriso_genisofs_platform(struct XorrisO *xorriso, char *id, int flag) { unsigned int u; char re_id[64]; if(strcmp(id, "x86") == 0) return(0); else if(strcmp(id, "PPC") == 0) return(1); else if(strcmp(id, "Mac") == 0) return(2); else if(strcmp(id, "efi") == 0) return(0xef); u= 0x100; if(strncmp(id, "0x", 2) == 0) { sscanf(id + 2, "%x", &u); } else { sscanf(id, "%u", &u); sprintf(re_id, "%u", u); if(strcmp(id, re_id) != 0) goto wrong_id; } if(u <= 0xff) return((int) u); wrong_id:; sprintf(xorriso->info_text, "Unrecognized El Torito Platform Id : '%.16s%s'", id, strlen(id) > 16 ? "..." : ""); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text, "Recognizable are: x86, PPC, Mac, efi, [0...255], [0x00...0xff]"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); return(-1); } /* mini emulation of mkisofs */ int Xorriso_genisofs(struct XorrisO *xorriso, char *whom, int argc, char **argv, int flag) { int ret, i, j, was_path= 0, was_other_option= 0, mem_graft_points, mem; int do_print_size= 0, fd, idx, iso_level= -1, palohdrversion; int was_failure= 0, fret, lower_r= 0, zero= 0; int dir_mode= -1, file_mode= -1, count, partition_number; int allow_dir_id_ext= -1, mem_current_interpreter; int root_seen= 0, do_md5_mem, option_d= 0, arg_count, dummy; mode_t mode_and, mode_or; int with_boot_image= 0, with_cat_path= 0, with_emul_toc= 0; int old_root_md5= 1, old_root_dev= 0, old_root_ino= 1, sort_file_pattern= 0; int *weight_list= NULL, weight_count= 0; int *delay_opt_list= NULL, delay_opt_count= 0; int mkisofs_b_platform_id= 0x00; char *sfe= NULL, *adr= NULL, ra_text[80], *pathspec= NULL, *sort_file= NULL; char *ept, *add_pt, *eff_path= NULL, *indev= NULL, msc[80], *cpt; char *old_root= NULL, *argpt, *hargv[1]; char *boot_path, partno_text[24], *iso_rr_pt, *disk_pt, *rpt, *wpt; char *rm_merge_args[3], *rr_reloc_dir_pt= NULL; char *sort_weight_args[4], *bless_args[6], *sa_path, dummy_text[80]; const char *eff_option; struct stat stbuf; mem_current_interpreter= xorriso->current_interpreter; xorriso->current_interpreter= 1; Xorriso_alloc_meM(sfe, char, 5*SfileadrL); Xorriso_alloc_meM(adr, char, SfileadrL+8); Xorriso_alloc_meM(pathspec, char, 2*SfileadrL); Xorriso_alloc_meM(eff_path, char, SfileadrL); Xorriso_alloc_meM(indev, char, SfileadrL+8); Xorriso_alloc_meM(old_root, char, SfileadrL); Xorriso_alloc_meM(sort_file, char, SfileadrL); for(i= 0; i<argc; i++) { if(argv[i][0] != '-') continue; ret= Xorriso_genisofs_eff_opt(xorriso, whom, argv[i], &eff_option, 0); if(ret <= 0) { was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1 | 2); if(fret < 0) {ret= 0; goto ex;} continue; } else if(strcmp(argv[i], eff_option) != 0) { sprintf(xorriso->info_text, "-as %s option %s completed to: %s", whom, argv[i], eff_option); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } argpt= (char *) eff_option; if(strcmp(eff_option, "-genisoimage_completion") == 0 || strcmp(eff_option, "--genisoimage_completion") == 0) { xorriso->genisoimage_completion= 1; } else if(strcmp(eff_option, "-log-file") == 0 || strcmp(eff_option, "--log-file") == 0) { if(i + 1 >= argc) goto not_enough_args; i+= 1; if(argv[i][0]) { sprintf(xorriso->info_text, "re-directing all messages to %s\n", argv[i]); Xorriso_info(xorriso, 0); } ret= Xorriso_write_to_channel(xorriso, argv[i], 2, 8 | ((argv[i][0] == 0) << 15)); if(ret <= 0) { sprintf(xorriso->info_text, "Cannot open logfile: %s", argv[i]); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno * (ret == 0), "SORRY", 0); was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret < 0) {ret= 0; goto ex;} } if(argv[i][0] == 0) { sprintf(xorriso->info_text, "Revoked stderr message redirection"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } } else { if(argv[i][0] == '-') { if(strcmp(argpt, argv[i]) == 0) { ret= Xorriso_genisofs_fused_options(xorriso, whom, argv[i] + 1, &dummy, &dummy, &dummy, dummy_text, 1); } else { ret= 0; } if(ret <= 0) { hargv[0]= argv[i]; ret= Xorriso_genisofs_count_args(xorriso, 1, hargv, &count, 1); if(ret > 0) i+= count; /* skip eventual arguments of known option */ } } } } strcpy(ra_text, xorriso->report_about_text); weight_list= TSOB_FELD(int, (argc / 3) + 1); if(weight_list == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); {ret= -1; goto ex;} } delay_opt_list= TSOB_FELD(int, argc + 1); if(delay_opt_list == NULL) { cpt= (char *) weight_list; Xorriso_no_malloc_memory(xorriso, &cpt, 0); {ret= -1; goto ex;} } if(xorriso->boot_image_cat_path[0]) with_cat_path= -1; adr[0]= indev[0]= msc[0]= old_root[0]= sort_file[0]= 0; for(i= 0; i<argc; i++) { Xorriso_genisofs_eff_opt(xorriso, whom, argv[i], &eff_option, 1); ret= Xorriso_genisofs_strip_dash(xorriso, (char *) eff_option, &argpt, 0); if(ret <= 0) goto ex; ret= Xorriso_genisofs_ignore(xorriso, whom, argpt, &i, 1); if(ret == 1) continue; if(strcmp(argpt, "-version")==0) { sprintf(xorriso->result_line, "mkisofs 2.01-Emulation Copyright (C) 2024 see libburnia-project.org xorriso\n" ); fd= xorriso->dev_fd_1; if(fd<0) fd= 1; ret= write(fd, xorriso->result_line, strlen(xorriso->result_line)); /* (result of write intentionally ignored) */ fsync(fd); Xorriso_option_version(xorriso, 0); } else if(strcmp(argpt, "-o")==0 || strcmp(argpt, "-output")==0) { if(i+1>=argc) goto not_enough_args; i++; adr[0]= 0; if(strcmp(argv[i],"-")!=0 && strncmp(argv[i], "stdio:", 6)!=0) strcpy(adr, "stdio:"); if(Sfile_str(adr+strlen(adr), argv[i], 0)<=0) {ret= -1; goto ex;} } else if(strcmp(argpt, "-M")==0 || strcmp(argpt, "-dev")==0 || strcmp(argpt, "-prev-session")==0) { if(i+1>=argc) goto not_enough_args; i++; if(strncmp(argv[i], "stdio:", 6)!=0) strcpy(indev, "stdio:"); if(Sfile_str(indev+strlen(indev), argv[i], 0)<=0) {ret= -1; goto ex;} } else if(strcmp(argpt, "-C")==0 || strcmp(argpt, "-cdrecord-params")==0) { if(i+1>=argc) goto not_enough_args; i++; strncpy(msc, argv[i], sizeof(msc)-1); msc[sizeof(msc)-1]= 0; } else if(strcmp(argpt, "-help")==0) { Xorriso_genisofs_help(xorriso, 0); } else if(strcmp(argpt, "-v")==0 || strcmp(argpt, "-verbose")==0) { ret= Xorriso_genisofs_fused_options(xorriso, whom, "v", &option_d, &iso_level, &lower_r, ra_text, 2); if(ret <= 0) goto problem_handler_1; } else if(strcmp(argpt, "-quiet")==0) { strcpy(ra_text, "SORRY"); } else if(strcmp(argpt, "-f")==0 || strcmp(argpt, "-follow-links")==0) { ret= Xorriso_genisofs_fused_options(xorriso, whom, "f", &option_d, &iso_level, &lower_r, ra_text, 2); if(ret <= 0) goto problem_handler_1; } else if(strcmp(argpt, "-iso-level")==0) { if(i+1>=argc) goto not_enough_args; i++; sscanf(argv[i], "%d", &iso_level); if(iso_level < 1 || iso_level > 4) { sprintf(xorriso->info_text, "-as %s: unsupported -iso-level '%s' (use one of: 1,2,3,4)", whom, argv[i]); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto problem_handler_1; } if(iso_level == 4) xorriso->do_iso1999= 1; else { sprintf(sfe, "iso_9660_level=%s", argv[i]); ret= Xorriso_relax_compliance(xorriso, sfe, 0); if(ret <= 0) goto problem_handler_1; } } else if(strcmp(argpt, "-input-charset")==0) { if(i+1>=argc) goto not_enough_args; i++; /* -local_charset */ if(strcmp(argv[i], "default") == 0) ret= Xorriso_option_charset(xorriso, "ISO-8859-1", 4); else ret= Xorriso_option_charset(xorriso, argv[i], 4); if(ret <= 0) goto problem_handler_1; } else if(strcmp(argpt, "-output-charset")==0) { if(i+1>=argc) goto not_enough_args; i++; /* -charset */ if(strcmp(argv[i], "default") == 0) ret= Xorriso_option_charset(xorriso, "ISO-8859-1", 3); else ret= Xorriso_option_charset(xorriso, argv[i], 3); if(ret <= 0) goto problem_handler_1; } else if(strcmp(argpt, "-hide") == 0 || strcmp(argpt, "-hide-list") == 0 || strcmp(argpt, "-hide-joliet") == 0 || strcmp(argpt, "-hide-joliet-list") == 0 || strcmp(argpt, "-hide-hfsplus") == 0 || strcmp(argpt, "-hide-hfsplus-list") == 0) { if(i+1>=argc) goto not_enough_args; i++; if(strcmp(argpt, "-hide") == 0) ret= Xorriso_genisofs_hide(xorriso, whom, argv[i], 1, 0); else if(strcmp(argpt, "-hide-list") == 0) ret= Xorriso_genisofs_hide_list(xorriso, whom, argv[i], 1, 0); else if(strcmp(argpt, "-hide-joliet") == 0) ret= Xorriso_genisofs_hide(xorriso, whom, argv[i], 2, 0); else if(strcmp(argpt, "-hide-joliet-list") == 0) ret= Xorriso_genisofs_hide_list(xorriso, whom, argv[i], 2, 0); else if(strcmp(argpt, "-hide-hfsplus") == 0) ret= Xorriso_genisofs_hide(xorriso, whom, argv[i], 4, 0); else if(strcmp(argpt, "-hide-hfsplus-list") == 0) ret= Xorriso_genisofs_hide_list(xorriso, whom, argv[i], 4, 0); if(ret <= 0) goto problem_handler_1; } else if(strcmp(argpt, "-root") == 0) { if(i+1>=argc) goto not_enough_args; i++; /* Always absolute */ strcpy(eff_path, "/"); if(Sfile_str(eff_path, argv[i], argv[i][0] != '/') <= 0) {ret= -1; goto ex;} strcpy(xorriso->wdi, eff_path); root_seen= 1; } else if(strcmp(argpt, "-old-root") == 0) { if(i+1>=argc) goto not_enough_args; i++; /* Always absolute */ strcpy(old_root, "/"); if(Sfile_str(old_root, argv[i], argv[i][0] != '/') <= 0) {ret= -1; goto ex;} } else if(strcmp(argpt, "--old-root-no-md5")==0) { old_root_md5= 0; } else if(strcmp(argpt, "--old-root-devno")==0) { old_root_dev= 1; } else if(strcmp(argpt, "--old-root-no-ino")==0) { old_root_ino= 0; } else if(strcmp(argpt, "-fat") == 0) { xorriso->do_fat= 1; } else if(strcmp(argpt, "-hfsplus") == 0) { /* Already with -indev */ xorriso->do_hfsplus= 1; } else if(strcmp(argpt, "--hardlinks")==0) { Xorriso_option_hardlinks(xorriso, "on", 0); } else if(strcmp(argpt, "--acl")==0) { Xorriso_option_acl(xorriso, "on", 0); } else if(strcmp(argpt, "--xattr")==0) { Xorriso_option_xattr(xorriso, "on", 0); } else if(strcmp(argpt, "--xattr-any")==0) { Xorriso_option_xattr(xorriso, "any", 0); } else if(strcmp(argpt, "--lfa_flags")==0) { Xorriso_option_lfa_flags(xorriso, "default:on:no_restore", 0); } else if(strcmp(argpt, "--md5")==0) { Xorriso_option_md5(xorriso, "on", 0); } else if(strcmp(argpt, "--scdbackup_tag")==0) { if(i + 2 >= argc) goto not_enough_args; i+= 2; ret= Xorriso_option_scdbackup_tag(xorriso, argv[i-1], argv[i], 0); if(ret <= 0) goto problem_handler_1; } else if(strcmp(argpt, "--for_backup")==0) { Xorriso_option_hardlinks(xorriso, "on", 0); Xorriso_option_acl(xorriso, "on", 0); Xorriso_option_xattr(xorriso, "any", 0); Xorriso_option_md5(xorriso, "on", 0); if(xorriso->lfa_flags_default & 8) Xorriso_option_lfa_flags(xorriso, "default:on:no_restore", 0); } else if(strcmp(argpt, "-z")==0 || strcmp(argpt, "-transparent-compression")==0) { Xorriso_option_zisofs(xorriso, "by_magic=on", 0); } else if(strcmp(argpt, "--zisofs-version-2") == 0) { Xorriso_option_zisofs(xorriso, "by_magic=v2", 0); } else if(strcmp(argpt, "--zisofs2-susp-z2") == 0) { Xorriso_option_zisofs(xorriso, "susp_z2=on", 0); } else if(strcmp(argpt, "--zisofs2-susp-zf") == 0) { Xorriso_option_zisofs(xorriso, "susp_z2=off", 0); } else if(strcmp(argpt, "--stdio_sync")==0) { if(i+1>=argc) goto not_enough_args; i++; Xorriso_option_stdio_sync(xorriso, argv[i], 0); } else if(strcmp(argpt, "-disallow_dir_id_ext")==0) { allow_dir_id_ext= 0; } else if(strcmp(argpt, "--emul-toc")==0) { with_emul_toc= 1; xorriso->no_emul_toc&= ~1; } else if(strcmp(argpt, "--no-emul-toc")==0) { with_emul_toc= 0; xorriso->no_emul_toc|= 1; } else if(strcmp(argpt, "-log-file") == 0) { /* already handled before this loop */; i++; } else if(strcmp(argpt, "-gui") == 0) { xorriso->pacifier_interval= 0.25; } else if(strcmp(argpt, "-file_name_limit") == 0) { if(i+1>=argc) goto not_enough_args; i++; Xorriso_option_file_name_limit(xorriso, argv[i], 0); } else { if(argv[i][0] == '-' && strcmp(argv[i], argpt) == 0) { /* Was not an incomplete genisoimage option */ ret= Xorriso_genisofs_fused_options(xorriso, whom, argv[i] + 1, &option_d, &iso_level, &lower_r, ra_text, 1 | 2); if(ret != 1) was_other_option= 1; } else { ret= 0; was_other_option= 1; } if(ret > 0) { Xorriso_genisofs_fused_options(xorriso, whom, argv[i] + 1, &option_d, &iso_level, &lower_r, ra_text, 2); if(ret <= 0) goto problem_handler_1; } else { hargv[0]= argpt; ret= Xorriso_genisofs_count_args(xorriso, argc - i, hargv, &count, 0); if(ret > 0) i+= count; /* skip eventual arguments of known option */ } } continue; /* regular bottom of loop */ problem_handler_1:; was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; goto ex; } Xorriso_option_report_about(xorriso, ra_text, 0); if(adr[0]) { if(strncmp(adr, "stdio:", 6)==0 && strncmp(adr, "stdio:/dev/fd/", 14)!=0) { ret= Sfile_type(adr+6, 1); if(ret==-1) { /* ok */; } else if(ret==2 || ret==3) { sprintf(xorriso->info_text, "-as %s: Cannot accept %s as target: -o %s", whom, (ret==3 ? "symbolic link" : "directory"), Text_shellsafe(adr+6, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } /* Regard overwritable as blank, truncate regular files on write start */ ret= Xorriso_option_dev(xorriso, adr, 2|8|16); if(ret<=0) goto ex; } if(was_other_option && xorriso->out_drive_handle==NULL) { ret= Xorriso_option_dev(xorriso, "-", 2|4); /* set outdev to stdout */ if(ret<=0) goto ex; } if(msc[0]) { cpt= strchr(msc, ','); if(cpt==NULL) { illegal_c:; sprintf(xorriso->info_text, "-as %s: unusable parameter with option -C: %s", whom, Text_shellsafe(msc, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } else if(cpt==msc || msc[1]==0) goto illegal_c; strncpy(sfe, msc, cpt-msc); sfe[cpt-msc]= 0; if(xorriso->in_drive_handle!=NULL && indev[0]) { /* give up indev before setting the load address */ ret= Xorriso_option_dev(xorriso, "", 1); if(ret<=0) goto ex; } /* growisofs submits msc1+16 to avoid a theoretical bug in mkisofs. Therefore this bug has to be emulated here. Sigh. */ ret= Xorriso_option_load(xorriso, "sbsector", sfe, 1); if(ret<=0) goto ex; ret= Xorriso_option_grow_blindly(xorriso, cpt+1, 0); if(ret<=0) goto ex; } if(old_root[0] || root_seen) { Xorriso_option_md5(xorriso, old_root_md5 ? "on" : "off", 0); Xorriso_option_disk_dev_ino(xorriso, old_root_dev && old_root_ino ? "on" : old_root_ino ? "ino_only" : "off", 0); if(!old_root_ino) Xorriso_option_hardlinks(xorriso, "without_update", 0); } if(indev[0]) { do_md5_mem= xorriso->do_md5; if(xorriso->do_md5 & 1) /* MD5 loading is enabled */ xorriso->do_md5|= 32; /* Do not check tags of superblock,tree,session because growisofs preserves the first sb tag.*/ ret= Xorriso_option_dev(xorriso, indev, 1); xorriso->do_md5= do_md5_mem; if(ret<=0) goto ex; } if(!was_other_option) {ret= 1; goto ex;} if(old_root[0]) { ret= Xorriso_iso_lstat(xorriso, old_root, &stbuf, 0); if(ret >= 0) { if(root_seen) { ret= Xorriso_mkdir(xorriso, xorriso->wdi, 1 | 2); if(ret < 0) {ret= -(ret != -1); goto ex;} } else { strcpy(xorriso->wdi, "/"); } if(strcmp(old_root, xorriso->wdi) != 0) { ret= Xorriso_clone_under(xorriso, old_root, xorriso->wdi, 0); if(ret <= 0) goto ex; } } } xorriso->padding= 300*1024; for(i= 0; i<argc; i++) { sprintf(xorriso->info_text, "-as %s: %s", whom, Text_shellsafe(argv[i], sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); Xorriso_genisofs_eff_opt(xorriso, whom, argv[i], &eff_option, 1); ret= Xorriso_genisofs_strip_dash(xorriso, (char *) eff_option, &argpt, 0); if(ret <= 0) goto ex; ret= Xorriso_genisofs_ignore(xorriso, whom, argpt, &i, 0); if(ret == 1) continue; if(strcmp(argpt, "-version")==0) { /* was already handled in first argument scan */; } else if(strcmp(argpt, "--norock")==0) { xorriso->do_rockridge= 0; lower_r= 0; } else if(strcmp(argpt, "-R")==0 || strcmp(argpt, "-rock")==0) { ret= Xorriso_genisofs_fused_options(xorriso, whom, "R", &option_d, &iso_level, &lower_r, ra_text, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-r")==0 || strcmp(argpt, "-rational-rock")==0){ ret= Xorriso_genisofs_fused_options(xorriso, whom, "r", &option_d, &iso_level, &lower_r, ra_text, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-J")==0 || strcmp(argpt, "-joliet")==0) { ret= Xorriso_genisofs_fused_options(xorriso, whom, "J", &option_d, &iso_level, &lower_r, ra_text, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-joliet-long")==0) { Xorriso_relax_compliance(xorriso, "joliet_long_paths:joliet_long_names", 0); } else if(strcmp(argpt, "-joliet-utf16")==0) { Xorriso_relax_compliance(xorriso, "joliet_utf16", 0); } else if(strcmp(argpt, "-fat") == 0) { /* was already handled in first argument scan */; } else if(strcmp(argpt, "-hfs-bless") == 0 || strcmp(argpt, "-hfs-bless-by") == 0 || strcmp(argpt, "-hfsplus-file-creator-type") == 0) { arg_count= 1; if(strcmp(argpt, "-hfs-bless-by") == 0) arg_count= 2; else if(strcmp(argpt, "-hfsplus-file-creator-type") == 0) arg_count= 3; if(i + arg_count >= argc) goto not_enough_args; /* Memorize command until all pathspecs are processed */ delay_opt_list[delay_opt_count++]= i; i+= arg_count; } else if(strcmp(argpt, "-hfsplus") == 0) { /* was already handled in first argument scan */; } else if(strcmp(argpt, "-hfsplus-serial-no") == 0) { if(i+1>=argc) goto not_enough_args; i++; sprintf(pathspec, "hfsplus_serial=%.80s", argv[i]); ret= Xorriso_option_boot_image(xorriso, "any", pathspec, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-hfsplus-block-size") == 0 || strcmp(argpt, "-apm-block-size") == 0) { if(i+1>=argc) goto not_enough_args; i++; ret= -1; sscanf(argv[i], "%d", &ret); if(argpt[1] == 'h') sprintf(sfe, "hfsplus_block_size=%d", ret); else sprintf(sfe, "apm_block_size=%d", ret); ret= Xorriso_option_boot_image(xorriso, "any", sfe, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-graft-points")==0) { xorriso->allow_graft_points= 3; } else if(strcmp(argpt, "-path-list")==0 || strcmp(argpt, "--quoted_path_list")==0) { if(i+1>=argc) { not_enough_args:; sprintf(xorriso->info_text, "-as %s: Not enough arguments to option %s", whom, argpt); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } i++; xorriso->pacifier_style= 1; ret= Xorriso_option_path_list(xorriso, argv[i], (strcmp(argpt, "--quoted_path_list") == 0) | 2); if(ret<=0) goto problem_handler_2; ret = Xorriso_genisofs_path_pecul(xorriso, &was_path, with_emul_toc, &allow_dir_id_ext, &iso_level, 0); if(ret <= 0) goto ex; } else if(strcmp(argpt, "-f")==0 || strcmp(argpt, "-follow-links")==0) { /* was already handled in first argument scan */; } else if(strcmp(argpt, "-pad")==0) { xorriso->padding= 300*1024; } else if(strcmp(argpt, "-no-pad")==0) { xorriso->padding= 0; } else if(strcmp(argpt, "-print-size")==0) { do_print_size= 1; } else if(strcmp(argpt, "-o")==0 || strcmp(argpt, "-output") == 0) { i++; /* was already handled in first argument scan */; } else if(strcmp(argpt, "-M")==0 || strcmp(argpt, "-dev")==0 || strcmp(argpt, "-prev-session")==0) { i++; /* was already handled in first argument scan */; } else if(strcmp(argpt, "-C")==0 || strcmp(argpt, "-cdrecord-params")==0) { i++; /* was already handled in first argument scan */; } else if(strcmp(argpt, "-help")==0) { /* was already handled in first argument scan */; } else if(strcmp(argpt, "-V")==0 || strcmp(argpt, "-volid")==0 || strcmp(argpt, "-volset")==0 || strcmp(argpt, "-p")==0 || strcmp(argpt, "-preparer")==0 || strcmp(argpt, "-P")==0 || strcmp(argpt, "-publisher")==0 || strcmp(argpt, "-A")==0 || strcmp(argpt, "-appid")==0 || strcmp(argpt, "--application_use")==0 || strcmp(argpt, "-sysid")==0 || strcmp(argpt, "-biblio")==0 || strcmp(argpt, "-copyright")==0 || strcmp(argpt, "-abstract")==0 ) { if(i+1>=argc) goto not_enough_args; i++; ret= 1; if(strcmp(argpt, "-V")==0 || strcmp(argpt, "-volid")==0) ret= Xorriso_option_volid(xorriso, argv[i], 0); else if(strcmp(argpt, "-volset")==0) ret= Xorriso_option_volset_id(xorriso, argv[i], 0); else if(strcmp(argpt, "-p")==0 || strcmp(argpt, "-preparer")==0) ret= Xorriso_option_preparer_id(xorriso, argv[i], 0); else if(strcmp(argpt, "-P")==0 || strcmp(argpt, "-publisher")==0) ret= Xorriso_option_publisher(xorriso, argv[i], 0); else if(strcmp(argpt, "-A")==0 || strcmp(argpt, "-appid")==0) ret= Xorriso_option_application_id(xorriso, argv[i], 0); else if(strcmp(argpt, "-sysid")==0) ret= Xorriso_option_system_id(xorriso, argv[i], 0); else if(strcmp(argpt, "-biblio")==0) ret= Xorriso_option_biblio_file(xorriso, argv[i], 0); else if(strcmp(argpt, "-copyright")==0) ret= Xorriso_option_copyright_file(xorriso, argv[i], 0); else if(strcmp(argpt, "-abstract")==0) ret= Xorriso_option_abstract_file(xorriso, argv[i], 0); else if(strcmp(argpt, "--application_use")==0) ret= Xorriso_option_application_use(xorriso, argv[i], 0); if(ret<=0) goto problem_handler_2; } else if(strcmp(argpt, "-m")==0 || strcmp(argpt, "-exclude")==0 || strcmp(argpt, "-x")==0 || strcmp(argpt, "-old-exclude")==0) { if(i+1>=argc) goto not_enough_args; i++; mem= xorriso->do_disk_pattern; xorriso->do_disk_pattern= 1; if(strchr(argv[i], '/')!=NULL) { idx= i; ret= Xorriso_option_not_paths(xorriso, i+1, argv, &idx, 0); } else ret= Xorriso_option_not_leaf(xorriso, argv[i], 0); xorriso->do_disk_pattern= mem; if(ret<=0) goto problem_handler_2; } else if(strcmp(argpt, "-exclude-list")==0) { if(i+1>=argc) goto not_enough_args; i++; mem= xorriso->do_disk_pattern; xorriso->do_disk_pattern= 1; ret= Xorriso_option_not_list(xorriso, argv[i], 0); xorriso->do_disk_pattern= mem; if(ret<=0) goto problem_handler_2; } else if(strcmp(argpt, "-v")==0 || strcmp(argpt, "-verbose")==0 || strcmp(argpt, "-quiet")==0) { /* was already handled in first argument scan */; } else if(strcmp(argpt, "-iso-level")==0) { i++; /* was already handled in first argument scan */; } else if(strcmp(argpt, "-no-emul-boot")==0 || strcmp(argpt, "-hard-disk-boot")==0 || strcmp(argpt, "-boot-info-table")==0 || strcmp(argpt, "--grub2-boot-info") == 0 || strncmp(argpt, "isolinux_mbr=", 13)==0 || strcmp(argpt, "-eltorito-alt-boot")==0 || strcmp(argpt, "--protective-msdos-label")==0 || strcmp(argpt, "--mbr-force-bootable")==0 || strcmp(argpt, "--gpt-iso-bootable")==0 || strcmp(argpt, "--gpt-iso-not-ro")==0 || strcmp(argpt, "--boot-catalog-hide")==0 || strcmp(argpt, "-isohybrid-gpt-basdat")==0 || strcmp(argpt, "-isohybrid-gpt-hfsplus")==0 || strcmp(argpt, "-isohybrid-apm-hfsplus")==0 || strcmp(argpt, "-part_like_isohybrid")==0) { delay_opt_list[delay_opt_count++]= i; } else if(strcmp(argpt, "-b") == 0 || strcmp(argpt, "-eltorito-boot") == 0 || strcmp(argpt, "-eltorito-platform") == 0 || strcmp(argpt, "--efi-boot") == 0 || strcmp(argpt, "-e") == 0 || strcmp(argpt, "-mips-boot") == 0 || strcmp(argpt, "-mipsel-boot") == 0 || strcmp(argpt, "-c") == 0 || strcmp(argpt, "-eltorito-catalog") == 0 || strcmp(argpt, "-boot-load-size") == 0 || strcmp(argpt, "-eltorito-id") == 0 || strcmp(argpt, "-eltorito-selcrit") == 0 || strcmp(argpt, "--embedded-boot")==0 || strcmp(argpt, "-generic-boot")==0 || strcmp(argpt, "-G") == 0 || strcmp(argpt, "-partition_offset") == 0 || strcmp(argpt, "-partition_hd_cyl") == 0 || strcmp(argpt, "-partition_sec_hd") == 0 || strcmp(argpt, "-partition_cyl_align") == 0 || strcmp(argpt, "-isohybrid-mbr") == 0 || strcmp(argpt, "--grub2-mbr") == 0 || strncmp(argpt, "-hppa-", 6) == 0 || strcmp(argpt, "-alpha-boot") == 0 || strcmp(argpt, "--gpt_disk_guid") == 0 || strcmp(argpt, "-iso_mbr_part_type") == 0) { if(i+1>=argc) goto not_enough_args; delay_opt_list[delay_opt_count++]= i; i++; } else if(strcmp(argpt, "-hide_iso_path") == 0) { if(i + 2 >= argc) goto not_enough_args; delay_opt_list[delay_opt_count++]= i; i+= 2; } else if(strncmp(argpt, "--modification-date=", 20)==0) { ret= Xorriso_option_volume_date(xorriso, "uuid", argpt + 20, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "--set_all_file_dates") == 0) { if(i+1>=argc) goto not_enough_args; i++; ret= Xorriso_option_volume_date(xorriso, "all_file_dates", argv[i], 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-input-charset")==0) { i++; /* was already handled in first argument scan */; } else if(strcmp(argpt, "-output-charset")==0) { i++; /* was already handled in first argument scan */; } else if(strcmp(argpt, "--hardlinks")==0 || strcmp(argpt, "--acl")==0 || strcmp(argpt, "--xattr")==0 || strcmp(argpt, "--xattr-any")==0 || strcmp(argpt, "--lfa_flags")==0 || strcmp(argpt, "--md5")==0 || strcmp(argpt, "--for_backup")==0) { /* was already handled in first argument scan */; } else if(strcmp(argpt, "--scdbackup_tag")==0) { /* was already handled in first argument scan */; i+= 2; } else if(strcmp(argpt, "--sort-weight")==0) { if(i + 2 >= argc) goto not_enough_args; i+= 2; /* memorize for find runs after pathspecs have been added */ weight_list[weight_count++]= i - 2; } else if(strcmp(argpt, "--sort-weight-list") == 0 || strcmp(argpt, "--sort-weight-patterns") == 0) { if(i + 1 >= argc) goto not_enough_args; i++; if(Sfile_str(sort_file, argv[i], 0) <= 0) {ret= -1; goto ex;} sort_file_pattern= (strcmp(argpt, "--sort-weight-patterns") == 0); } else if(strcmp(argpt, "-z")==0 || strcmp(argpt, "-transparent-compression")==0 || strcmp(argpt, "--zisofs-version-2") == 0 || strcmp(argpt, "--zisofs2-susp-z2") == 0 || strcmp(argpt, "--zisofs2-susp-zf") == 0) { /* was already handled in first argument scan */; } else if(strcmp(argpt, "-U") == 0 || strcmp(argpt, "-untranslated-filenames") == 0) { ret= Xorriso_genisofs_fused_options(xorriso, whom, "U", &option_d, &iso_level, &lower_r, ra_text, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-untranslated_name_len") == 0) { if(i+1>=argc) goto not_enough_args; i++; sprintf(sfe, "untranslated_name_len=%s", argv[i]); ret= Xorriso_relax_compliance(xorriso, sfe, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-N") == 0 || strcmp(argpt, "-omit-version-number") == 0) { ret= Xorriso_genisofs_fused_options(xorriso, whom, "N", &option_d, &iso_level, &lower_r, ra_text, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-l") == 0 || strcmp(argpt, "-full-iso9660-filenames") == 0) { ret= Xorriso_genisofs_fused_options(xorriso, whom, "l", &option_d, &iso_level, &lower_r, ra_text, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-max-iso9660-filenames") == 0) { Xorriso_relax_compliance(xorriso, "long_names", 0); } else if(strcmp(argpt, "-d") == 0 || strcmp(argpt, "-omit-period") == 0) { ret= Xorriso_genisofs_fused_options(xorriso, whom, "d", &option_d, &iso_level, &lower_r, ra_text, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-allow-lowercase") == 0) { Xorriso_relax_compliance(xorriso, "lowercase", 0); } else if(strcmp(argpt, "-relaxed-filenames") == 0) { Xorriso_relax_compliance(xorriso, "7bit_ascii", 0); } else if(strcmp(argpt, "-hide") == 0 || strcmp(argpt, "-hide-list") == 0 || strcmp(argpt, "-hide-joliet") == 0 || strcmp(argpt, "-hide-joliet-list") == 0 || strcmp(argpt, "-hide-hfsplus") == 0 || strcmp(argpt, "-hide-hfsplus-list") == 0) { if(i+1>=argc) goto not_enough_args; i++; /* was already handled in first argument scan */; } else if(strcmp(argpt, "-root") == 0 || strcmp(argpt, "-old-root") == 0) { if(i+1>=argc) goto not_enough_args; i++; /* was already handled in first argument scan */; } else if(strcmp(argpt, "--old-root-no-md5")==0 || strcmp(argpt, "--old-root-devno")==0 || strcmp(argpt, "--old-root-no-ino")==0) { /* was already handled in first argument scan */; } else if(strcmp(argpt, "-dir-mode") == 0) { if(i+1>=argc) goto not_enough_args; i++; ret= Xorriso_convert_modstring(xorriso, "-as mkisofs -dir-mode", argv[i], &mode_and, &mode_or, 0); if(ret<=0) goto problem_handler_2; dir_mode= mode_or; } else if(strcmp(argpt, "-file-mode") == 0) { if(i+1>=argc) goto not_enough_args; i++; ret= Xorriso_convert_modstring(xorriso, "-as mkisofs -file-mode", argv[i], &mode_and, &mode_or, 0); if(ret<=0) goto problem_handler_2; file_mode= mode_or; } else if(strcmp(argpt, "-jigdo-jigdo") == 0 || strcmp(argpt, "-jigdo-template") == 0 || strcmp(argpt, "-jigdo-min-file-size") == 0 || strcmp(argpt, "-jigdo-exclude") == 0 || strcmp(argpt, "-jigdo-force-md5") == 0 || strcmp(argpt, "-jigdo-force-checksum") == 0 || strcmp(argpt, "-jigdo-map") == 0 || strcmp(argpt, "-jigdo-checksum-algorithm") == 0 || strcmp(argpt, "-jigdo-template-compress") == 0 || strcmp(argpt, "-checksum_algorithm_iso") == 0 || strcmp(argpt, "-checksum_algorithm_template") == 0 || strcmp(argpt, "-md5-list") == 0 || strcmp(argpt, "-checksum-list") == 0) { i++; ret= Xorriso_option_jigdo(xorriso, argpt, argv[i], 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-chrp-boot-part") == 0 || strcmp(argpt, "-chrp-boot") == 0) { ret= Xorriso_option_boot_image(xorriso, "any", "chrp_boot_part=on", 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-prep-boot-part") == 0) { if(i + 1 >= argc) goto not_enough_args; i++; ret= Sfile_str(xorriso->prep_partition, argv[i], 0); if(ret <= 0) goto ex; } else if(strcmp(argpt, "-efi-boot-part") == 0) { if(i + 1 >= argc) goto not_enough_args; i++; ret= Sfile_str(xorriso->efi_boot_partition, argv[i], 0); if(ret <= 0) goto ex; } else if(strcmp(argpt, "-append_partition") == 0) { if(i + 3 >= argc) goto not_enough_args; i+= 3; ret= Xorriso_option_append_partition(xorriso, argv[i - 2], argv[i - 1], argv[i], 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-appended_part_as_gpt") == 0) { xorriso->appended_as_gpt= 1; } else if(strcmp(argpt, "-appended_part_as_apm") == 0) { xorriso->appended_as_apm= 1; } else if(strcmp(argpt, "-B") == 0 || strcmp(argpt, "-sparc-boot") == 0) { i++; if(strlen(argv[i]) >= SfileadrL) continue; /* Switch system area type to: SUN Disk Label */ strcpy(pathspec, "sparc_label="); strcat(pathspec, xorriso->ascii_disc_label); ret= Xorriso_option_boot_image(xorriso, "any", pathspec, 0); if(ret <= 0) goto problem_handler_2; /* Interpret list of boot partition images or "..." */; cpt= ept= argv[i]; partition_number= 2; while(ept != NULL) { ept= strchr(cpt, ','); if(ept != NULL) { strncpy(pathspec, cpt, ept - cpt); pathspec[ept - cpt]= 0; cpt= ept + 1; } else strcpy(pathspec, cpt); if(strcmp(pathspec, "...") == 0) { for(; partition_number <= 8; partition_number++) { sprintf(partno_text, "%d", partition_number); ret= Xorriso_option_append_partition(xorriso, partno_text, "0x0", ".", 0); if(ret <= 0) goto problem_handler_2; } } else { if(partition_number > 8) { sprintf(xorriso->info_text, "-as %s -sparc-boot %s : Too many boot images", whom, argv[i]); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE",0); goto problem_handler_2; } sprintf(partno_text, "%d", partition_number); ret= Xorriso_option_append_partition(xorriso, partno_text, "0x0", pathspec, 0); if(ret <= 0) goto problem_handler_2; partition_number++; } } } else if(strcmp(argpt, "-sparc-label") == 0) { if(i+1>=argc) goto not_enough_args; i++; sprintf(sfe, "sparc_label=%s", argv[i]); ret= Xorriso_option_boot_image(xorriso, "any", sfe, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "--grub2-sparc-core") == 0) { if(i+1>=argc) goto not_enough_args; i++; sprintf(sfe, "grub2_sparc_core=%s", argv[i]); ret= Xorriso_option_boot_image(xorriso, "any", sfe, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "--stdio_sync")==0) { if(i+1>=argc) goto not_enough_args; i++; /* was already handled in first argument scan */; } else if(strcmp(argpt, "--emul-toc")==0 || strcmp(argpt, "--no-emul-toc")==0) { /* was already handled in first argument scan */; } else if(strcmp(argpt, "--old-empty")==0) { xorriso->do_old_empty= 1; } else if(strcmp(argpt, "-disallow_dir_id_ext")==0) { /* was already handled in first argument scan */; } else if(strcmp(argpt, "--no_rc")==0) { /* was already handled in Xorriso_prescan_args */; } else if(strcmp(argpt, "-D") == 0 || strcmp(argpt, "-disable-deep-relocation") == 0) { ret= Xorriso_genisofs_fused_options(xorriso, whom, "D", &option_d, &iso_level, &lower_r, ra_text, 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-hide-rr-moved") == 0) { rr_reloc_dir_pt= ".rr_moved"; goto rr_reloc_dir; } else if(strcmp(argpt, "-rr_reloc_dir") == 0) { i++; rr_reloc_dir_pt= argv[i]; rr_reloc_dir:; if(rr_reloc_dir_pt[0] == '/') rr_reloc_dir_pt++; if(strchr(rr_reloc_dir_pt, '/') != NULL) { sprintf(xorriso->info_text, "-as %s -rr_reloc_dir %s : May only use directories in root directory", whom, Text_shellsafe(argv[i], sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE",0); } ret= Xorriso_option_rr_reloc_dir(xorriso, rr_reloc_dir_pt, 0); if(ret <= 0) goto problem_handler_2; Xorriso_relax_compliance(xorriso, "deep_paths_off:long_paths_off", 0); } else if(strcmp(argpt, "-log-file") == 0 || strcmp(argpt, "-file_name_limit") == 0) { i+= 1; /* was already handled before this loop */; } else if(strcmp(argpt, "-gui") == 0) { /* was already handled in first argument scan */; } else if(strcmp(argpt, "-uid") == 0) { if(i + 1 >= argc) goto not_enough_args; i++; ret= Xorriso_option_uid(xorriso, argv[i], 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-gid") == 0) { if(i + 1 >= argc) goto not_enough_args; i++; ret= Xorriso_option_gid(xorriso, argv[i], 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(argpt, "-cut_out") == 0) { if(i + 4 >= argc) goto not_enough_args; i+= 4; ret= Xorriso_option_cut_out(xorriso, argv[i - 3], argv[i - 2], argv[i - 1], argv[i], 0); if(ret <= 0) goto problem_handler_2; } else if(strcmp(eff_option, "-genisoimage_completion") == 0) { /* was already handled before this loop */; } else if(argpt[0]=='-') { if(strcmp(argpt, argv[i]) == 0) { /* Was not an incomplete genisoimage option */ ret= Xorriso_genisofs_fused_options(xorriso, whom, argv[i] + 1, &option_d, &iso_level, &lower_r, ra_text, 1); } else { ret= 0; } if(ret == 1) { ret= Xorriso_genisofs_fused_options(xorriso, whom, argv[i] + 1, &option_d, &iso_level, &lower_r, ra_text, 0); if(ret <= 0) goto problem_handler_2; } else { hargv[0]= argpt; ret= Xorriso_genisofs_count_args(xorriso, argc - i, hargv, &count, 1); if(ret > 0) { sprintf(xorriso->info_text, "-as %s: Unsupported option %s", whom, Text_shellsafe(argv[i], sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); i+= count; goto problem_handler_2; } else { sprintf(xorriso->info_text, "-as %s: Unrecognized option %s", whom, Text_shellsafe(argv[i], sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto problem_handler_2; } } } else { ret= Xorriso_graftable_pathspec(xorriso, argv[i], pathspec, 0); if(ret <= 0) goto problem_handler_2; add_pt= pathspec; if(old_root[0]) { /* Split pathspec */ ret= Fileliste__target_source_limit(add_pt, '=', &ept, 0); if(ret > 0) { *ept= 0; iso_rr_pt= add_pt; disk_pt= ept + 1; } else { iso_rr_pt= "/"; disk_pt= add_pt; } /* Unescape iso_rr_pt */ strcpy(eff_path, iso_rr_pt); iso_rr_pt= eff_path; for(wpt= rpt= iso_rr_pt; *rpt != 0; rpt++) { if(*rpt == '\\') { if(*(rpt + 1) == '\\') rpt++; else if(*(rpt + 1) == '=') continue; } *(wpt++) = *rpt; } *wpt= 0; if(root_seen) { ret= Sfile_prepend_path(xorriso->wdi, iso_rr_pt, 0); if(ret<=0) { Xorriso_msgs_submit(xorriso, 0, "Effective path gets much too long", 0, "FAILURE", 0); goto problem_handler_2; } } /* update_merge */ ret= Xorriso_option_update(xorriso, disk_pt, iso_rr_pt, 1 | 8 | 32); } else { mem_graft_points= xorriso->allow_graft_points; xorriso->allow_graft_points= 3; zero= 0; ret= Xorriso_option_add(xorriso, 1, &add_pt, &zero, (was_path << 1) | (root_seen << 2)); xorriso->allow_graft_points= mem_graft_points; } if(ret<=0) goto problem_handler_2; /* Enforce odd mkisofs defaults on first pathspec */ ret = Xorriso_genisofs_path_pecul(xorriso, &was_path, with_emul_toc, &allow_dir_id_ext, &iso_level, 0); if(ret <= 0) goto ex; } continue; /* regular bottom of loop */ problem_handler_2:; was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; goto ex; } if(old_root[0]) { /* Delete all visited nodes which were not found on disk */ if(root_seen) rm_merge_args[0]= xorriso->wdi; else rm_merge_args[0]= "/"; rm_merge_args[1]= "-exec"; rm_merge_args[2]= "rm_merge"; zero= 0; ret= Xorriso_option_find(xorriso, 3, rm_merge_args, &zero, 2); if(ret<=0) goto ex; } if(lower_r) { static char *lower_r_args[3]= {"/", "-exec", "mkisofs_r"}; zero= 0; ret= Xorriso_option_find(xorriso, 3, lower_r_args, &zero, 2); if(ret<=0) goto ex; } if(dir_mode >= 0) { static char *dir_mode_args[6]= {"/", "-type", "d", "-exec", "chmod", ""}; zero= 0; sprintf(sfe, "0%o", (unsigned int) dir_mode); dir_mode_args[5]= sfe; ret= Xorriso_option_find(xorriso, 6, dir_mode_args, &zero, 2); if(ret<=0) goto ex; } if(file_mode >= 0) { static char *file_mode_args[6]= {"/", "-type", "f", "-exec", "chmod", ""}; zero= 0; sprintf(sfe, "0%o", (unsigned int) file_mode); file_mode_args[5]= sfe; ret= Xorriso_option_find(xorriso, 6, file_mode_args, &zero, 2); if(ret<=0) goto ex; } if(sort_file[0]) { ret= Xorriso_apply_sort_file(xorriso, sort_file, sort_file_pattern); if(ret<=0) goto ex; } for(j= 0; j < weight_count; j++) { i= weight_list[j]; /* find argv[i+2] -exec sort_weight argv[i+1] */ zero= 0; sort_weight_args[0]= argv[i + 2]; sort_weight_args[1]= "-exec"; sort_weight_args[2]= "sort_weight"; sort_weight_args[3]= argv[i + 1]; ret= Xorriso_option_find(xorriso, 4, sort_weight_args, &zero, 2); if(ret > 0) continue; /* Problem handler */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; goto ex; } if(option_d) Xorriso_relax_compliance(xorriso, "deep_paths:long_paths", 0); /* After all pathspecs are added: perform delayed options, mostly boot related */ for(j= 0; j < delay_opt_count; j++) { i= delay_opt_list[j]; Xorriso_genisofs_eff_opt(xorriso, whom, argv[i], &eff_option, 1); ret= Xorriso_genisofs_strip_dash(xorriso, (char *) eff_option, &argpt, 0); if(ret <= 0) goto ex; if(strcmp(argpt, "-no-emul-boot")==0) { xorriso->boot_image_emul= 0; xorriso->boot_emul_default= 0; } else if(strcmp(argpt, "-hard-disk-boot")==0) { xorriso->boot_image_emul= 1; xorriso->boot_emul_default= 0; } else if(strcmp(argpt, "-boot-info-table")==0) { xorriso->patch_isolinux_image= (xorriso->patch_isolinux_image & ~2) | 1; } else if(strcmp(argpt, "--grub2-boot-info") == 0) { xorriso->patch_isolinux_image= (xorriso->patch_isolinux_image & ~2) | 512; } else if(strcmp(argpt, "-b") == 0 || strcmp(argpt, "-eltorito-boot") == 0 || strcmp(argpt, "--efi-boot") == 0 || strcmp(argpt, "-e") == 0) { i++; if(strcmp(argpt, "--efi-boot") == 0) { if(xorriso->boot_image_bin_path[0]) { ret= Xorriso_genisofs_add_boot(xorriso, 0); if(ret <= 0) goto problem_handler_boot; } boot_path= xorriso->boot_image_bin_path; xorriso->boot_efi_default= 1; xorriso->boot_image_emul= 0; xorriso->boot_emul_default= 0; } else { boot_path= xorriso->boot_image_bin_path; if(strcmp(argpt, "-e") == 0) xorriso->boot_platform_id= 0xef; else xorriso->boot_platform_id= mkisofs_b_platform_id; xorriso->boot_efi_default= 0; if(xorriso->boot_emul_default) xorriso->boot_image_emul= 2; } boot_path[0]= 0; if(argv[i][0] != '/' && strncmp(argv[i], "--interval:", 11) != 0) strcat(boot_path, "/"); ret= Sfile_str(boot_path + strlen(boot_path), argv[i], 0); if(ret <= 0) goto ex; if(xorriso->boot_efi_default && xorriso->boot_image_bin_path[0]) { ret= Xorriso_genisofs_add_boot(xorriso, 0); if(ret <= 0) goto problem_handler_boot; } xorriso->keep_boot_image= 0; with_boot_image= 1; } else if(strcmp(argpt, "-eltorito-platform") == 0) { if(i + 1>=argc) goto not_enough_args; i++; mem= mkisofs_b_platform_id; mkisofs_b_platform_id= Xorriso_genisofs_platform(xorriso, argv[i], 0); if(mkisofs_b_platform_id < 0) { mkisofs_b_platform_id= 0; goto problem_handler_boot; } if(mkisofs_b_platform_id != mem && xorriso->boot_image_bin_path[0] != 0) { ret= Xorriso_genisofs_add_boot(xorriso, 0); if(ret <= 0) goto problem_handler_boot; } } else if(strcmp(argpt, "-c") == 0 || strcmp(argpt, "-eltorito-catalog") == 0) { if(i+1>=argc) goto not_enough_args; i++; xorriso->boot_image_cat_path[0]= 0; if(argv[i][0] != '/') strcat(xorriso->boot_image_cat_path, "/"); ret= Sfile_str(xorriso->boot_image_cat_path + strlen(xorriso->boot_image_cat_path), argv[i], 0); if(ret <= 0) goto ex; if(with_cat_path == 0) with_cat_path= 1; } else if(strcmp(argpt, "-boot-load-size") == 0) { if(i+1>=argc) goto not_enough_args; i++; if(strcmp(argv[i], "full") == 0) { xorriso->boot_img_full_size= 1; } else { sscanf(argv[i], "%d", &ret); xorriso->boot_image_load_size= ret * 512; } xorriso->boot_img_size_default= 0; } else if(strcmp(argpt, "-eltorito-id") == 0 || strcmp(argpt, "-eltorito-selcrit") == 0) { if(i+1>=argc) goto not_enough_args; i++; if(strcmp(argpt, "-eltorito-id") == 0) sprintf(sfe, "id_string=%s", argv[i]); else sprintf(sfe, "sel_crit=%s", argv[i]); ret= Xorriso_option_boot_image(xorriso, "any", sfe, 0); if(ret <= 0) goto problem_handler_boot; } else if(strncmp(argpt, "isolinux_mbr=", 13)==0) { sprintf(sfe, "isohybrid=%s", argpt + 13); ret= Xorriso_option_boot_image(xorriso, "isolinux", sfe, 0); if(ret <= 0) goto problem_handler_boot; } else if(strcmp(argpt, "-isohybrid-gpt-basdat") == 0) { xorriso->patch_isolinux_image = (xorriso->patch_isolinux_image & ~0x0fc) | (1 << 2); } else if(strcmp(argpt, "-isohybrid-gpt-hfsplus") == 0) { xorriso->patch_isolinux_image = (xorriso->patch_isolinux_image & ~0x0fc) | (2 << 2); } else if(strcmp(argpt, "-isohybrid-apm-hfsplus") == 0) { xorriso->patch_isolinux_image = xorriso->patch_isolinux_image | (1 << 8); } else if(strcmp(argpt, "-part_like_isohybrid") == 0) { xorriso->part_like_isohybrid= 1; } else if(strcmp(argpt, "-iso_mbr_part_type") == 0) { if(i + 1 >= argc) goto not_enough_args; i++; sprintf(sfe, "iso_mbr_part_type=%s", argv[i]); ret= Xorriso_option_boot_image(xorriso, "any", sfe, 0); if(ret <= 0) goto problem_handler_boot; } else if(strcmp(argpt, "--gpt_disk_guid") == 0) { if(i + 1 >= argc) goto not_enough_args; i++; ret= Xorriso_parse_gpt_guid(xorriso, argv[i], 0); if(ret <= 0) goto problem_handler_boot; } else if(strcmp(argpt, "-eltorito-alt-boot")==0) { ret= Xorriso_genisofs_add_boot(xorriso, 0); if(ret <= 0) goto problem_handler_boot; } else if(strcmp(argpt, "--embedded-boot")==0 || strcmp(argpt, "-generic-boot")==0 || strcmp(argpt, "-G") == 0 || strcmp(argpt, "-isohybrid-mbr") == 0 || strcmp(argpt, "--grub2-mbr") == 0) { if(i+1>=argc) goto not_enough_args; i++; if(strcmp(argv[i], ".") == 0) sa_path= ""; else sa_path= argv[i]; ret= Xorriso_set_system_area_path(xorriso, sa_path, 0); if(ret <= 0) goto problem_handler_boot; if(strcmp(argpt, "-isohybrid-mbr")==0) xorriso->system_area_options= (xorriso->system_area_options & ~4001) | 2; else if(strcmp(argpt, "--grub2-mbr") == 0) xorriso->system_area_options= (xorriso->system_area_options & ~2) | 0x4000; } else if(strcmp(argpt, "--protective-msdos-label")==0) { xorriso->system_area_options= (xorriso->system_area_options & ~2) | 1; } else if(strcmp(argpt, "--mbr-force-bootable") == 0) { xorriso->system_area_options= xorriso->system_area_options | (1 << 15); } else if(strcmp(argpt, "--gpt-iso-bootable") == 0) { xorriso->system_area_options= xorriso->system_area_options | (1 << 16); } else if(strcmp(argpt, "--gpt-iso-not-ro") == 0) { xorriso->system_area_options= xorriso->system_area_options | (1 << 17); } else if(strcmp(argpt, "--boot-catalog-hide")==0) { xorriso->boot_image_cat_hidden|= 7; } else if(strcmp(argpt, "-partition_offset") == 0 || strcmp(argpt, "-partition_sec_hd") == 0 || strcmp(argpt, "-partition_hd_cyl") == 0 || strcmp(argpt, "-partition_cyl_align") == 0) { if(i+1>=argc) goto not_enough_args; i++; sprintf(sfe, "%s=%.16s", argpt + 1, argv[i]); ret= Xorriso_option_boot_image(xorriso, "any", sfe, 0); if(ret <= 0) goto problem_handler_boot; } else if(strcmp(argpt, "-mips-boot") == 0 || strcmp(argpt, "-mipsel-boot") == 0) { if(i + 1 >= argc) goto not_enough_args; i++; if(strcmp(argpt, "-mipsel-boot") == 0) strcpy(sfe, "mipsel_path="); else strcpy(sfe, "mips_path="); ret= Sfile_str(sfe, argv[i], 1); if(ret <= 0) goto ex; ret= Xorriso_option_boot_image(xorriso, "any", sfe, 0); if(ret <= 0) goto problem_handler_boot; } else if(strncmp(argpt, "-hppa-", 6) == 0) { if(i + 1 >= argc) goto not_enough_args; i++; sprintf(sfe, "-as mkisofs %s %s", argpt, argv[i]); palohdrversion= (xorriso->system_area_options >> 2) & 0x3f; if(palohdrversion != 4) palohdrversion= 5; ret= Xorriso_coordinate_system_area(xorriso, palohdrversion, 0, sfe, 0); if(ret <= 0) goto ex; ret= Xorriso_set_hppa_boot_parm(xorriso, argv[i], argpt + 6, 0); if(ret <= 0) goto problem_handler_boot; } else if(strcmp(argpt, "-alpha-boot") == 0) { if(i + 1 >= argc) goto not_enough_args; i++; sprintf(sfe, "-as mkisofs %s %s", argpt, argv[i]); ret= Xorriso_coordinate_system_area(xorriso, 6, 0, sfe, 0); if(ret <= 0) goto ex; ret= Xorriso_set_alpha_boot(xorriso, argv[i], 0); } else if(strcmp(argpt, "-hfs-bless") == 0) { static char *bless_arg_data[6]= { "/", "-disk_path", "", "-exec", "set_hfs_bless", "p"}; for(j= 0; j < 6; j++) bless_args[j]= bless_arg_data[j]; bless_args[2]= argv[i + 1]; zero= 0; ret= Xorriso_option_find(xorriso, 6, bless_args, &zero, 2 | 16); if(ret<=0) goto ex; if(ret < 2) { sprintf(xorriso->info_text, "-hfs-bless: Could not find a data file which stems from underneath disk directory "); Text_shellsafe(argv[i + 1], xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); Xorriso_msgs_submit(xorriso, 0, "Consider to use: -hfs-bless-by p ISO_RR_PATH", 0, "HINT", 0); goto problem_handler_boot; } } else if(strcmp(argpt, "-hfs-bless-by") == 0) { ret= Xorriso_hfsplus_bless(xorriso, argv[i + 2], NULL, argv[i + 1], 0); if(ret <= 0) goto problem_handler_boot; } else if(strcmp(argpt, "-hfsplus-file-creator-type") == 0) { ret= Xorriso_hfsplus_file_creator_type(xorriso, argv[i + 3], NULL, argv[i + 1], argv[i + 2], 0); if(ret <= 0) goto problem_handler_boot; } else if(strcmp(argpt, "-hide_iso_path") == 0) { int i_plus; i_plus= i + 2; ret= Xorriso_option_hide(xorriso, argv[i + 1], i + 3, argv, &i_plus, 0); if(ret <= 0) goto problem_handler_boot; } continue; /* regular bottom of loop */ problem_handler_boot:; /* Problem handler */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; goto ex; } if(with_boot_image && with_cat_path == 0 && !(xorriso->boot_image_cat_hidden & 1)) strcpy(xorriso->boot_image_cat_path, "/boot.catalog"); /* The boot catalog has to be hidden separately */ if(xorriso->boot_image_cat_path[0]) { ret= Xorriso_path_is_hidden(xorriso, xorriso->boot_image_cat_path, 0); if(ret > 0) xorriso->boot_image_cat_hidden|= ret; else if(ret < 0) was_failure= 1; } /* Enforce the -boot-load-size default of mkisofs */ if(with_boot_image && xorriso->boot_img_size_default && xorriso->boot_image_emul == 0) xorriso->boot_img_full_size= 1; if(xorriso->no_emul_toc & 1) xorriso->do_padding_by_libisofs= 1; if(do_print_size) { ret= Xorriso_option_print_size(xorriso, 1); goto ex; } ret= !was_failure; ex:; if(was_path && (!do_print_size) && !old_root[0]) Xorriso_pacifier_callback(xorriso, "files added", xorriso->pacifier_count, xorriso->pacifier_total, "", 1); if(do_print_size && Xorriso_change_is_pending(xorriso, 1)) xorriso->volset_change_pending= 2; if(weight_list != NULL) free(weight_list); if(delay_opt_list != NULL) free(delay_opt_list); Xorriso_free_meM(sort_file); Xorriso_free_meM(sfe); Xorriso_free_meM(adr); Xorriso_free_meM(pathspec); Xorriso_free_meM(eff_path); Xorriso_free_meM(indev); Xorriso_free_meM(old_root); xorriso->current_interpreter= mem_current_interpreter; return(ret); } int Xorriso_as_genisofs(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int end_idx, ret, old_idx; old_idx= *idx; end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, 1); (*idx)= end_idx; if(end_idx<=0 || old_idx>=argc) return(1); ret= Xorriso_genisofs(xorriso, "genisofs", end_idx-old_idx, argv+old_idx, 0); return(ret); } int Xorriso_as_cdrskin(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int end_idx, ret, old_idx; old_idx= *idx; end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, 1); (*idx)= end_idx; if(end_idx<=0 || old_idx>=argc) return(1); ret= Xorriso_cdrskin(xorriso, "cdrskin", end_idx-old_idx, argv+old_idx, 0); return(ret); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2010 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of emulators for mkisofs and cdrecord. */ #ifndef Xorriso_pvt_emul_includeD #define Xorriso_pvt_emul_includeD yes /* micro version of cdrskin */ int Xorriso_cdrskin(struct XorrisO *xorriso, char *whom, int argc, char **argv, int flag); int Xorriso_cdrskin_help(struct XorrisO *xorriso, int flag); int Xorriso_cdrskin_uses_stdout(struct XorrisO *xorriso, int argc, char **argv, int flag); int Xorriso_as_cdrskin(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* micro emulation of mkisofs */ int Xorriso_genisofs(struct XorrisO *xorriso, char *whom, int argc, char **argv, int flag); int Xorriso_genisofs_help(struct XorrisO *xorriso, int flag); int Xorriso_as_genisofs(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); #endif /* ! Xorriso_pvt_emul_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2020 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains functions which operate on data filter objects. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include "lib_mgt.h" #include "iso_tree.h" #include "iso_img.h" /* #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" #include "iso_manip.h" #include "sort_cmp.h" */ struct Xorriso_extF { int flag; /* unused yet */ IsoExternalFilterCommand *cmd; }; int Xorriso_extf_destroy(struct XorrisO *xorriso, struct Xorriso_extF **filter, int flag); /* @param flag see struct Xorriso_extF.flag */ int Xorriso_extf_new(struct XorrisO *xorriso, struct Xorriso_extF **filter, char *path, int argc, char **argv, int behavior, char *suffix, char *name, int flag) { int i; struct Xorriso_extF *o= NULL; IsoExternalFilterCommand *cmd; *filter= o= calloc(sizeof(struct Xorriso_extF), 1); if(o == NULL) goto failure; o->flag= flag; o->cmd= NULL; o->cmd= cmd= calloc(sizeof(IsoExternalFilterCommand), 1); if(cmd == NULL) goto failure; cmd->version= 0; cmd->refcount= 0; cmd->name= NULL; cmd->path= NULL; cmd->argv= NULL; cmd->argc= argc + 1; cmd->behavior= behavior; cmd->suffix= NULL; cmd->suffix= strdup(suffix); if(cmd->suffix == NULL) goto failure; cmd->path= strdup(path); if(cmd->path == NULL) goto failure; cmd->argv= calloc(sizeof(char *), argc + 2); if(cmd->argv == NULL) goto failure; for(i= 0; i < argc + 2; i++) cmd->argv[i]= NULL; cmd->argv[0]= strdup(path); if(cmd->argv[0] == NULL) goto failure; for(i= 0; i < argc; i++) { cmd->argv[i + 1]= strdup(argv[i]); if(cmd->argv[i] == NULL) goto failure; } cmd->name= strdup(name); if(cmd->name == NULL) goto failure; return(1); failure:; Xorriso_extf_destroy(xorriso, filter, 0); return(-1); } int Xorriso_extf_destroy(struct XorrisO *xorriso, struct Xorriso_extF **filter, int flag) { int i; IsoExternalFilterCommand *cmd; if(*filter == NULL) return(0); cmd= (*filter)->cmd; if(cmd != NULL) { if(cmd->refcount > 0) return(0); if(cmd->path != NULL) free(cmd->path); if(cmd->suffix != NULL) free(cmd->suffix); if(cmd->argv != NULL) { for(i= 0; i < cmd->argc; i++) if(cmd->argv[i] != NULL) free(cmd->argv[i]); free((char *) cmd->argv); } if(cmd->name != NULL) free(cmd->name); free((char *) cmd); } free((char *) *filter); *filter= NULL; return(1); } int Xorriso_lookup_extf(struct XorrisO *xorriso, char *name, struct Xorriso_lsT **found_lst, int flag) { struct Xorriso_extF *filter; struct Xorriso_lsT *lst; for(lst= xorriso->filters; lst != NULL; lst= Xorriso_lst_get_next(lst, 0)) { filter= (struct Xorriso_extF *) Xorriso_lst_get_text(lst, 0); if(strcmp(filter->cmd->name, name) == 0) { *found_lst= lst; return(1); } } return(0); } int Xorriso_destroy_all_extf(struct XorrisO *xorriso, int flag) { struct Xorriso_extF *filter; struct Xorriso_lsT *lst, *next_lst; for(lst= xorriso->filters; lst != NULL; lst= next_lst) { filter= (struct Xorriso_extF *) Xorriso_lst_get_text(lst, 0); Xorriso_lst_detach_text(lst, 0); next_lst= Xorriso_lst_get_next(lst, 0); Xorriso_lst_destroy(&lst, 0); Xorriso_extf_destroy(xorriso, &filter, 0); } xorriso->filters= NULL; return(1); } /* @param flag bit0= return 2 if renaming is not possible by libisofs (always: if demanded strip suffix is missing or if suffix makes name length > 255) bit1= strip suffix rather than appending it */ int Xorriso_rename_suffix(struct XorrisO *xorriso, IsoNode *node, char *suffix, char *show_path, char new_name[], int flag) { int ret, lo= 0, ls= 0, strip_suffix; char *old_name= NULL, *show_name; IsoImage *volume; strip_suffix= !!(flag & 2); ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret <= 0) goto ex; old_name= strdup((char *) iso_node_get_name(node)); show_name= old_name; if(show_path != NULL) if(show_path[0] != 0) show_name= show_path; lo= strlen(old_name); ls= strlen(suffix); if(strip_suffix) { if(lo <= ls) { /* refuse gracefully */ ret= 2; goto ex; } if(strcmp(old_name + lo - ls, suffix) != 0) { ret= 2; goto ex; } if(lo >= SfileadrL) goto cannot_remove_suffix; strcpy(new_name, old_name); new_name[lo - ls]= 0; ret = iso_image_set_node_name(volume, node, new_name, 1); if (ret < 0) { Xorriso_process_msg_queues(xorriso,0); if (!(flag & 1)) Xorriso_report_iso_error(xorriso, "", ret, "Error when renaming ISO node", 0, "FAILURE", 1); cannot_remove_suffix:; strcpy(xorriso->info_text, "-set_filter: Cannot remove suffix from "); Text_shellsafe(show_name, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, (flag & 1) ? "WARNING" : "FAILURE", 0); ret= 2 * (flag & 1); goto ex; } } else { /* check whether suffix already present */ if(lo >= ls) if(strcmp(old_name + lo - ls, suffix) == 0) { /* refuse gracefully */ ret= 2; goto ex; } if(lo + ls > 255) { cannot_append_suffix:; strcpy(xorriso->info_text, "-set_filter: Cannot append suffix to "); Text_shellsafe(show_name, xorriso->info_text, 1); strcat(xorriso->info_text, ". Left unfiltered."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, (flag & 1) ? "WARNING" : "FAILURE", 0); ret= 2 * (flag & 1); goto ex; } sprintf(new_name, "%s%s", old_name, suffix); ret = iso_image_set_node_name(volume, node, new_name, 1); if (ret < 0) { Xorriso_process_msg_queues(xorriso,0); if (!(flag & 1)) Xorriso_report_iso_error(xorriso, "", ret, "Error when renaming ISO node", 0, "FAILURE", 1); goto cannot_append_suffix; } } ret= 1; ex:; if(old_name != NULL) free(old_name); Xorriso_process_msg_queues(xorriso,0); return(ret); } /* @param flag bit0= return 2 if renaming is not possible bit1= print pacifier messages */ int Xorriso_set_filter(struct XorrisO *xorriso, void *in_node, char *path, char *filter_name, int flag) { int ret, strip_suffix= 0, strip_filter= 0, filter_ret= 0; int explicit_suffix= 0, internal_filter= 0; IsoNode *node; IsoFile *file; struct Xorriso_lsT *found_lst; struct Xorriso_extF *found_filter; IsoExternalFilterCommand *cmd = NULL; char *old_name= NULL, *new_name= NULL, *suffix= ""; IsoStream *stream; IsoImage *volume; Xorriso_alloc_meM(new_name, char, SfileadrL); new_name[0]= 0; node= (IsoNode *) in_node; if(node == NULL) { ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0); if(ret <= 0) goto ex; } if(!LIBISO_ISREG(node)) { strcpy(xorriso->info_text, "-set_filter: Not a regular data file node "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); ret= 0; goto ex; } file= (IsoFile *) node; if(strncmp(filter_name, "--remove-all-filters", 20) == 0) { strip_filter= 1; strip_suffix= 1; if(strlen(filter_name) > 21) { strip_suffix= (filter_name[20] != '+'); suffix= filter_name + 21; explicit_suffix= 1; } } else if(strcmp(filter_name, "--zisofs") == 0) { internal_filter= 1; } else if(strcmp(filter_name, "--zisofs-decode") == 0) { internal_filter= 2; } else if(strcmp(filter_name, "--gzip") == 0) { internal_filter= 3; suffix= ".gz"; strip_suffix= 0; explicit_suffix= 1; } else if(strcmp(filter_name, "--gunzip") == 0 || strcmp(filter_name, "--gzip-decode") == 0) { internal_filter= 4; suffix= ".gz"; strip_suffix= 1; explicit_suffix= 1; } else { ret= Xorriso_lookup_extf(xorriso, filter_name, &found_lst, 0); if(ret < 0) goto ex; if(ret == 0) { strcpy(xorriso->info_text, "-set_filter: Not a registered filter name "); Text_shellsafe(filter_name, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } found_filter= (struct Xorriso_extF *) Xorriso_lst_get_text(found_lst, 0); cmd= found_filter->cmd; suffix= cmd->suffix; strip_suffix= cmd->behavior & 8; } if(suffix[0]) { /* >>> would need full iso_rr_path of node for showing */; old_name= strdup((char *) iso_node_get_name(node)); ret= Xorriso_rename_suffix(xorriso, node, suffix, path, new_name, (flag & 1) | (strip_suffix ? 2 : 0)); if(ret <= 0 || ret == 2) goto ex; } if(strip_filter) { while(1) { if(!explicit_suffix) { stream= iso_file_get_stream(file); if(strncmp(stream->class->type, "gzip", 4) == 0) { suffix= ".gz"; strip_suffix= 1; } else if(strncmp(stream->class->type, "pizg", 4) == 0) { suffix= ".gz"; strip_suffix= 0; } else { ret= iso_stream_get_external_filter(stream, &cmd, 0); if(ret > 0) { suffix= cmd->suffix; strip_suffix= !(cmd->behavior & 8); } } if(suffix[0]) { /* >>> would need the current renaming state of path */; ret= Xorriso_rename_suffix(xorriso, node, suffix, NULL, new_name, (flag & 1) | (strip_suffix << 1)); if(ret <= 0 || ret == 2) goto ex; } } ret= iso_file_remove_filter(file, 0); if(ret != 1) break; } filter_ret= 1; } else if (internal_filter == 1 || internal_filter == 2) { filter_ret = iso_file_add_zisofs_filter(file, 1 | (internal_filter & 2)); if(filter_ret == (int) ISO_ZISOFS_TOO_MANY_PTR) { /* Remove all buffered currently unused block pointers and try again */ ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret < 0) goto ex; if(ret > 0) { Xorriso_msgs_submit(xorriso, 0, "Overflow of zisofs block pointers happened.", 0, "WARNING", 0); Xorriso_msgs_submit(xorriso, 0, "zisofs falling back to mode which needs 3 input read runs.", 0, "WARNING", 0); ret = iso_image_zisofs_discard_bpt(volume, 0); if(ret <= 0) goto ex; filter_ret = iso_file_add_zisofs_filter(file, 1 | (internal_filter & 2)); } } if(filter_ret < 0) { Xorriso_process_msg_queues(xorriso,0); if(!(internal_filter == 2 && filter_ret == (int) ISO_ZISOFS_WRONG_INPUT)) Xorriso_report_iso_error(xorriso, "", filter_ret, "Error when setting filter to ISO node", 0, "FAILURE", 1); } } else if (internal_filter == 3 || internal_filter == 4) { filter_ret = iso_file_add_gzip_filter(file, 1 | ((internal_filter == 4) << 1)); if(filter_ret < 0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", filter_ret, "Error when setting filter to ISO node", 0, "FAILURE", 1); } } else { #ifndef Xorriso_allow_extf_suiD /* This is a final safety precaution before iso_file_add_external_filter() performs fork() and executes the alleged filter program. */ if(getuid() != geteuid()) { sprintf(xorriso->info_text, "-set_filter: UID and EUID differ. Will not run external programs."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); ret= 0; goto ex; } #endif /* ! Xorriso_allow_extf_suiD */ filter_ret = iso_file_add_external_filter(file, cmd, 0); if(filter_ret < 0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", filter_ret, "Error when setting filter to ISO node", 0, "FAILURE", 1); } } if(filter_ret != 1 && new_name[0] && old_name != NULL) { ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; ret = iso_image_set_node_name(volume, node, old_name, 1); if (ret < 0) { Xorriso_process_msg_queues(xorriso,0); if (!(flag & 1)) Xorriso_report_iso_error(xorriso, "", ret, "Error when renaming ISO node", 0, "FAILURE", 1); } } if(flag & 2) { xorriso->pacifier_count++; Xorriso_pacifier_callback(xorriso, "file filters processed", xorriso->pacifier_count, xorriso->pacifier_total, "", 0); } if(filter_ret < 0) { ret= 0; goto ex; } ret= filter_ret; ex:; if(old_name != NULL) free(old_name); Xorriso_free_meM(new_name); Xorriso_process_msg_queues(xorriso,0); return(ret); } int Xorriso_external_filter_banned(struct XorrisO *xorriso, char *purpose, int flag) { int is_banned= 0; #ifndef Xorriso_allow_external_filterS /* To be controlled by: configure --enable-external-filters */ sprintf(xorriso->info_text, "%s : Banned at compile time.", purpose); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text, "This may be changed at compile time by ./configure option --enable-external-filters"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); is_banned= 1; #endif /* ! Xorriso_allow_external_filterS */ #ifndef Xorriso_allow_extf_suiD /* To be controlled by: configure --enable-external-filters-setuid */ if(getuid() != geteuid()) { sprintf(xorriso->info_text, "-set_filter: UID and EUID differ. Will not run external programs."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); sprintf(xorriso->info_text, "This may be changed at compile time by ./configure option --enable-external-filters-setuid"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); is_banned= 1; } #endif /* ! Xorriso_allow_extf_suiD */ if(xorriso->filter_list_closed) { sprintf(xorriso->info_text, "%s : Banned by previous command -close_filter_list", purpose); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); is_banned= 1; } return(is_banned); } /* @param flag bit0= delete filter with the given name */ int Xorriso_external_filter(struct XorrisO *xorriso, char *name, char *options, char *path, int argc, char **argv, int flag) { int ret, delete= 0, behavior= 0, extf_flag= 0, is_banned= 0; char *what, *what_next, *suffix= ""; struct Xorriso_lsT *lst; struct Xorriso_extF *found_filter, *new_filter= NULL; is_banned= Xorriso_external_filter_banned( xorriso, flag & 1 ? "-unregister_filter" : "-external_filter", 0); if(is_banned) return(0); if((!(flag & 1)) && path[0] != '/') { sprintf(xorriso->info_text, "-external_filter : Given command path does not begin by '/' : "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } delete= flag & 1; ret= Xorriso_lookup_extf(xorriso, name, &lst, 0); if(ret < 0) return(ret); if(ret > 0) { if(delete) { found_filter= (struct Xorriso_extF *) Xorriso_lst_get_text(lst, 0); if(found_filter->cmd->refcount > 0) { sprintf(xorriso->info_text, "-external_filter: Cannot remove filter because it is in use by %.f nodes : ", (double) found_filter->cmd->refcount); Text_shellsafe(name, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } Xorriso_lst_detach_text(lst, 0); if(xorriso->filters == lst) xorriso->filters= Xorriso_lst_get_next(lst, 0); Xorriso_lst_destroy(&lst, 0); Xorriso_extf_destroy(xorriso, &found_filter, 0); ret= 1; goto ex; } strcpy(xorriso->info_text, "-external_filter: filter with given name already existing: "); Text_shellsafe(name, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(delete) { strcpy(xorriso->info_text, "-external_filter: filter with given name does not exist: "); Text_shellsafe(name, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } for(what= options; what!=NULL; what= what_next) { what_next= strchr(what, ':'); if(what_next!=NULL) { *what_next= 0; what_next++; } if(strncmp(what, "default", 7) == 0) { suffix= ""; behavior= 0; } else if(strncmp(what, "suffix=", 7) == 0) { suffix= what + 7; } else if(strcmp(what, "remove_suffix") == 0) { behavior|= 8; } else if(strcmp(what, "if_nonempty") == 0) { behavior|= 1; } else if(strcmp(what, "if_reduction") == 0) { behavior|= 2; } else if(strcmp(what, "if_block_reduction") == 0) { behavior|= 4; } else if(strncmp(what, "used=", 5) == 0) { ; /* this is informational output from -status */ } else if(what[0]) { strcpy(xorriso->info_text, "-external_filter: unknown option "); Text_shellsafe(what, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } ret= Xorriso_extf_new(xorriso, &new_filter, path, argc, argv, behavior, suffix, name, extf_flag); if(ret <= 0) { could_not_create:; strcpy(xorriso->info_text, "-external_filter: Could not create filter object"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto ex; } ret= Xorriso_lst_append_binary(&(xorriso->filters), (char *) new_filter,0, 4); if(ret <= 0) goto could_not_create; ret= 1; ex:; if(ret <= 0) { if(new_filter != NULL) Xorriso_extf_destroy(xorriso, &new_filter, 0); } return(ret); } int Xorriso_status_extf(struct XorrisO *xorriso, char *filter, FILE *fp, int flag) /* bit1= do only report to fp */ { int i, maxl= 4 * SfileadrL; struct Xorriso_extF *extf; struct Xorriso_lsT *lst; char *line; line= xorriso->result_line; for(lst= xorriso->filters; lst != NULL; lst= Xorriso_lst_get_next(lst, 0)) { extf= (struct Xorriso_extF *) Xorriso_lst_get_text(lst, 0); strcpy(xorriso->result_line, "-external_filter "); Text_shellsafe(extf->cmd->name, line, 1); if((int) strlen(line) > maxl) continue; strcat(line, " "); if(extf->cmd->suffix[0]) { strcat(line, "suffix="); Text_shellsafe(extf->cmd->suffix, line, 1); if((int) strlen(line) > maxl) continue; strcat(line, ":"); } if(extf->cmd->behavior & 8) strcat(line, "remove_suffix:"); if(extf->cmd->behavior & 1) strcat(line, "if_nonempty:"); if(extf->cmd->behavior & 2) strcat(line, "if_reduction:"); if(extf->cmd->behavior & 4) strcat(line, "if_block_reduction:"); sprintf(line + strlen(line), "used=%.f ", (double) extf->cmd->refcount); if((int) strlen(line) > maxl) continue; Text_shellsafe(extf->cmd->path, line, 1); if((int) strlen(line) > maxl) continue; for(i= 1; i < extf->cmd->argc; i++) { strcat(line, " "); Text_shellsafe(extf->cmd->argv[i], line, 1); if((int) strlen(line) > maxl) break; } if(i < extf->cmd->argc) continue; strcat(line, " --\n"); Xorriso_status_result(xorriso, filter, fp, flag&2); } if(xorriso->filter_list_closed) { strcpy(line, "-close_filter_list\n"); Xorriso_status_result(xorriso, filter, fp, flag&2); } return(1); } /* @param flag bit0= iso_zisofs_set_params bit1= iso_zisofs_ctrl_susp_z2 */ int Xorriso_set_zisofs_params(struct XorrisO *xorriso, int flag) { int ret, i; struct iso_zisofs_ctrl ctrl; if(flag & 2) { iso_zisofs_ctrl_susp_z2(!!xorriso->zisofs_susp_z2); } if(!(flag & 1)) return(1); memset(&ctrl, 0, sizeof(ctrl)); ctrl.version= 1; ret= iso_zisofs_get_params(&ctrl, 0); if(ret < 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when inquiring zisofs parameters before setting some", 0, "FAILURE", 1); return(0); } ctrl.compression_level= xorriso->zlib_level; if(xorriso->zisofs_block_size == (1 << 16)) ctrl.block_size_log2= 16; else if(xorriso->zisofs_block_size == (1 << 17)) ctrl.block_size_log2= 17; else ctrl.block_size_log2= 15; ctrl.v2_enabled= xorriso->zisofs_v2_enabled; ctrl.max_total_blocks= xorriso->zisofs_max_total_blocks; ctrl.max_file_blocks= xorriso->zisofs_max_file_blocks; ctrl.v2_block_size_log2= 17; for(i= 15; i <= 20; i++) if(xorriso->zisofs_v2_block_size == (1 << i)) ctrl.v2_block_size_log2= i; ctrl.block_number_target= xorriso->zisofs_block_number_target; ctrl.bpt_discard_free_ratio= xorriso->zisofs_bpt_discard_free_ratio; ret= iso_zisofs_set_params(&ctrl, 0); Xorriso_process_msg_queues(xorriso,0); if(ret < 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when setting zisofs parameters", 0, "FAILURE", 1); return(0); } return(1); } uint64_t Xorriso_zisofs_current_blocks(struct XorrisO *xorriso, int flag) { int ret; struct iso_zisofs_ctrl ctrl; memset(&ctrl, 0, sizeof(ctrl)); ctrl.version= 1; ret = iso_zisofs_get_params(&ctrl, 0); if(ret != 1) return(0); return(ctrl.current_total_blocks); } int Xorriso_status_zisofs(struct XorrisO *xorriso, char *filter, FILE *fp, int flag) /* bit0= only report non-default settings bit1= only report to fp */ { off_t ziso_count= 0, osiz_count= 0; off_t gzip_count= 0, gunzip_count= 0; uint64_t used_blocks; int always= 0; iso_zisofs_get_refcounts(&ziso_count, &osiz_count, 0); iso_gzip_get_refcounts(&gzip_count, &gunzip_count, 0); if(!(flag & 1)) { always= 1; } else if(filter != NULL) { if(filter[0] != 0) always= 1; } if(always || !( xorriso->zlib_level == xorriso->zlib_level_default && xorriso->zisofs_block_size == xorriso->zisofs_block_size_default && xorriso->zisofs_by_magic == 0)) { sprintf(xorriso->result_line, "-zisofs level=%d:block_size=%dk:by_magic=%s\n", xorriso->zlib_level, xorriso->zisofs_block_size / 1024, xorriso->zisofs_by_magic == 1 ? "on" : xorriso->zisofs_by_magic == 2 ? "v2" : "off"); Xorriso_status_result(xorriso, filter, fp, flag & 2); } if(always || !( xorriso->zisofs_v2_enabled == 0 && xorriso->zisofs_v2_block_size == xorriso->zisofs_v2_block_size_default && xorriso->zisofs_block_number_target == -1)){ sprintf(xorriso->result_line, "-zisofs version_2=%s:block_size_v2=%dk:susp_z2=%s:bpt_target=%.f\n", xorriso->zisofs_v2_enabled ? xorriso->zisofs_v2_enabled == 1 ? "as_needed" : "on" : "off", xorriso->zisofs_v2_block_size / 1024, xorriso->zisofs_susp_z2 ? "on" : "off", (double) xorriso->zisofs_block_number_target); Xorriso_status_result(xorriso, filter, fp, flag & 2); } used_blocks= Xorriso_zisofs_current_blocks(xorriso, 0); if(always || !( xorriso->zisofs_max_total_blocks == xorriso->zisofs_max_total_blocks_default && xorriso->zisofs_max_file_blocks == xorriso->zisofs_max_file_blocks_default && xorriso->zisofs_bpt_discard_free_ratio == xorriso->zisofs_bpt_discard_free_ratio_default)) { sprintf(xorriso->result_line, "-zisofs max_bpt=%.f:max_bpt_f=%.f:bpt_free_ratio=%.3f\n", (double) xorriso->zisofs_max_total_blocks * 8.0, (double) xorriso->zisofs_max_file_blocks * 8.0, xorriso->zisofs_bpt_discard_free_ratio); Xorriso_status_result(xorriso, filter, fp, flag & 2); } if(always || !(used_blocks == 0)) { sprintf(xorriso->result_line, "-zisofs bpt_used=%.f:bpt_ratio_used=%.3f\n", (double) used_blocks * 8.0, ((double) used_blocks) / (double) xorriso->zisofs_max_total_blocks); Xorriso_status_result(xorriso, filter, fp, flag & 2); } if(always || !( ziso_count == 0 && osiz_count == 0 && gzip_count == 0 && gunzip_count == 0)) { sprintf(xorriso->result_line, "-zisofs ziso_used=%.f:osiz_used=%.f:gzip_used=%.f:gunzip_used=%.f\n", (double) ziso_count, (double) osiz_count, (double) gzip_count, (double) gunzip_count); Xorriso_status_result(xorriso, filter, fp, flag & 2); } return(1); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2010 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of functions which operate on data filter objects. */ #ifndef Xorriso_pvt_filters_includeD #define Xorriso_pvt_filters_includeD yes int Xorriso_extf_new(struct XorrisO *xorriso, struct Xorriso_extF **filter, char *path, int argc, char **argv, int behavior, char *suffix, char *name, int flag); int Xorriso_extf_destroy(struct XorrisO *xorriso, struct Xorriso_extF **filter, int flag); int Xorriso_lookup_extf(struct XorrisO *xorriso, char *name, struct Xorriso_lsT **found_lst, int flag); int Xorriso_rename_suffix(struct XorrisO *xorriso, IsoNode *node, char *suffix, char *show_path, char new_name[], int flag); #endif /* ! Xorriso_pvt_filters_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of classes FindjoB, ExprnodE, ExprtesT which perform tree searches in libisofs or in POSIX filesystem */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <dirent.h> #include <errno.h> #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" /* ----------------------- Exprtest ----------------------- */ int Exprtest_new( struct ExprtesT **ftest, struct FindjoB *boss, int flag) { struct ExprtesT *f; *ftest= f= TSOB_FELD(struct ExprtesT,1); if(f==NULL) return(-1); f->boss= boss; f->invert= 0; f->test_type= -1; f->arg1= NULL; f->arg2= NULL; return(1); } int Exprtest_destroy(struct ExprtesT **ftest, int flag) { struct ExprtesT *f; f= *ftest; if(f==NULL) return(0); if(f->test_type == 1 || f->test_type == 13 || f->test_type == 16) { if(f->arg1 != NULL) free(f->arg1); if(f->arg2 != NULL) { regfree(f->arg2); free(f->arg2); } } else if(f->test_type == 9) { /* arg1 is not an allocated value */; } else { if(f->arg1 != NULL) free(f->arg1); if(f->arg2 != NULL) free(f->arg2); } free((char *) f); *ftest= NULL; return(1); } /* ----------------------- Nttpfnode ----------------------- */ int Exprnode_new(struct ExprnodE **fnode, struct FindjoB *job, struct ExprnodE *up, char *origin, int flag) /* bit0= set invert-property bit1= set use_shortcuts */ { struct ExprnodE *n; int ret,i; *fnode= n= TSOB_FELD(struct ExprnodE,1); if(n == NULL) return(-1); for(i= 0; i < (int) sizeof(n->origin); i++) n->origin[i]= 0; strncpy(n->origin, origin, sizeof(n->origin) - 1); n->up= up; n->invert= (flag & 1); n->assoc= 0; n->use_shortcuts= !!(flag & 2); n->left= NULL; n->left_op= -1; n->right= NULL; n->right_op= -1; n->sub= NULL; n->is_if_then_else= 0; n->true_branch= NULL; n->false_branch= NULL; n->test= NULL; n->own_value= -1; n->composed_value= -1; ret= Exprtest_new(&(n->test), job, 0); if(ret<=0){ Exprnode_destroy(fnode, 0); return(-1); } return(1); } int Exprnode_destroy(struct ExprnodE **fnode, int flag) { if(*fnode == NULL) return(0); Exprnode_destroy(&((*fnode)->right),0); Exprnode_destroy(&((*fnode)->sub),0); Exprnode_destroy(&((*fnode)->true_branch),0); Exprnode_destroy(&((*fnode)->false_branch),0); Exprtest_destroy(&((*fnode)->test),0); free((char *) *fnode); *fnode= NULL; return(1); } int Exprnode_set_is_if(struct ExprnodE *fnode, int value, int flag) { fnode->is_if_then_else= value; return(1); } int Exprnode_is_if(struct ExprnodE *fnode, int flag) { return(fnode->is_if_then_else); } int Exprnode_set_branch(struct ExprnodE *fnode, struct ExprnodE *target, int flag) /* bit0= false_branch (else true_branch) */ { struct ExprnodE **branch; if(flag&1) branch= &(fnode->false_branch); else branch= &(fnode->true_branch); Exprnode_destroy(branch,0); (*branch)= target; return(1); } int Exprnode_get_branch(struct ExprnodE *fnode, struct ExprnodE **branch, int flag) /* bit0= false_branch (else true_branch) */ { if(flag&1) (*branch)= fnode->false_branch; else (*branch)= fnode->true_branch; return(1); } int Exprnode_is_defined(struct ExprnodE *fnode, int flag) { struct ExprtesT *ftest; if(fnode==NULL) return(0); if(fnode->sub!=NULL) return(1); ftest= fnode->test; if(ftest==NULL) return(0); if(ftest->test_type>=0) return(1); return(0); } int Exprnode_own_value(struct XorrisO *xorriso, struct ExprnodE *fnode, void *node, char *name, char *path, struct stat *boss_stbuf, struct stat *stbuf, int flag) /* flag: return: (also from Exprtest_match() and Exprnode_tree_value() ) <0 = error 0 = does not match 1 = does match 2 = immediate decision : does not match 3 = immediate decision : does match */ { int ret; if(fnode==NULL) return(1); if(fnode->sub!=NULL) { ret= Exprnode_tree_value(xorriso, fnode->sub, -1, node, name, path, boss_stbuf, stbuf, 0); } else { ret= Exprtest_match(xorriso, fnode->test, node, name, path, boss_stbuf, stbuf, 0); } if(ret<0) return(ret); if(ret>1) return(ret); if(fnode->invert) ret= !ret; return(ret); } int Exprnode_op(int value1, int value2, int op, int flag) { int ret; if(op==0) ret= value1 || value2 ; else ret= value1 && value2 ; return(ret); } int Exprnode_tree_value(struct XorrisO *xorriso, struct ExprnodE *fnode, int left_value, void *node, char *name, char *path, struct stat *boss_stbuf, struct stat *stbuf, int flag) /* bit0-7= testmode: 0=head , 1=filename return: (also from Nntpftest_match() and Nntpfnode_own_value() ) <0 = error 0 = does not match 1 = does match 2 = immediate decision : does not match 3 = immediate decision : does match */ { int value= 1,ret; if(fnode==NULL) return(1); if(!Exprnode_is_defined(fnode,0)) return(1); if(fnode->use_shortcuts && fnode->left!=NULL){ fnode->composed_value= left_value; if(fnode->left_op==0) {/* OR */ if(left_value!=0) goto ex; } else { /* AND */ if(left_value==0) goto ex; } } fnode->composed_value= fnode->own_value= Exprnode_own_value(xorriso, fnode, node, name, path, boss_stbuf, stbuf, 0); if(fnode->own_value < 0 || fnode->own_value > 1) return(fnode->own_value); if(fnode->assoc == 0){ /* left associative */ if(fnode->left != NULL && left_value >= 0) fnode->composed_value= Exprnode_op(left_value, fnode->own_value, fnode->left_op, 0); /* compute right value */ /* is the right value relevant ? */ if(fnode->right!=NULL){ if(fnode->use_shortcuts){ if(fnode->right_op==0) {/* OR */ if(fnode->composed_value!=0) goto ex; } else { /* AND */ if(fnode->composed_value==0) goto ex; } } value= Exprnode_tree_value(xorriso, fnode->right,fnode->composed_value, node, name, path, boss_stbuf, stbuf, 0); if(value<0 || value>1) return(value); fnode->composed_value= value; } }else{ /* right associative */ if(fnode->right!=NULL){ /* is the right value relevant ? */ if(fnode->use_shortcuts){ if(fnode->right_op==0) {/* OR */ if(fnode->composed_value!=0) goto ex; } else { /* AND */ if(fnode->composed_value==0) goto ex; } } value= Exprnode_tree_value(xorriso, fnode->right,fnode->own_value, node, name, path, boss_stbuf, stbuf, 0); if(value<0||value>1) return(value); } else value= fnode->own_value; fnode->composed_value= value; if(fnode->left!=NULL && left_value>=0) fnode->composed_value= Exprnode_op(left_value,fnode->composed_value,fnode->left_op,0); } ex: ret= fnode->composed_value; if(fnode->is_if_then_else) { /* The if-condition is evaluated. Now follow the chosen branch */ struct ExprnodE *branch; if(ret>0) branch= fnode->true_branch; else branch= fnode->false_branch; if(branch!=NULL) { ret= Exprnode_tree_value(xorriso, branch, -1, node, name, path, boss_stbuf, stbuf, 0); if(ret<0) return(ret); if(ret>1) return(ret); } fnode->composed_value= ret; } return(fnode->composed_value); } /* --------------------- Findjob -------------------- */ int Findjob_new(struct FindjoB **o, char *start_path, int flag) { struct FindjoB *m; int ret; m= *o= TSOB_FELD(struct FindjoB,1); if(m==NULL) return(-1); m->start_path= NULL; m->test_tree= NULL; m->cursor= NULL; m->invert= 0; m->use_shortcuts= 1; m->action= 0; /* print */ m->prune= 0; m->use_pattern= 1; m->target= NULL; /* a mere pointer, not managed memory */ m->text_2= NULL; /* a mere pointer, not managed memory */ m->user= 0; m->group= 0; m->type= 0; m->date= 0; m->start_path= strdup(start_path); if(m->start_path==NULL) goto failed; m->found_path= NULL; m->estim_upper_size= 0; m->estim_lower_size= 0; m->subjob= NULL; m->errmsg[0]= 0; m->errn= 0; m->match_count= 0; m->depth= 0; ret= Exprnode_new(&(m->test_tree), m, NULL, "-find", (m->use_shortcuts)<<1); if(ret<=0) goto failed; m->cursor= m->test_tree; return(1); failed:; Findjob_destroy(o, 0); return(-1); } int Findjob_destroy(struct FindjoB **o, int flag) { struct FindjoB *m; m= *o; if(m==NULL) return(0); if(m->test_tree != NULL) Exprnode_destroy(&(m->test_tree), 0); if(m->start_path != NULL) free(m->start_path); if(m->found_path != NULL) free(m->found_path); free((char *) *o); *o= NULL; return(1); } int Findjob_set_start_path(struct FindjoB *o, char *start_path, int flag) { if(o->start_path!=NULL) free(o->start_path); if(start_path!=NULL) { o->start_path= strdup(start_path); if(o->start_path==NULL) return(-1); } else o->start_path= NULL; return(1); } int Findjob_get_start_path(struct FindjoB *o, char **start_path, int flag) { *start_path= o->start_path; return(1); } int Findjob_cursor_complete( struct FindjoB *job, int flag) { int ret; if(job==NULL) return(0); ret= Exprnode_is_defined(job->cursor,0); return(ret); } int Findjob_is_restrictive(struct FindjoB *job, int flag) { if(job == NULL) return(0); if(job->test_tree == NULL) return(0); if(!Exprnode_is_defined(job->test_tree, 0)) return(0); return(1); } int Findjob_new_node(struct FindjoB *job, struct ExprnodE **fnode, char *origin, int flag) /* bit0= open new branch bit1= with bit0 : do not register as sub-node of job->cursor */ { int ret; struct ExprnodE *f; ret= Exprnode_new(fnode,job,NULL,origin, job->invert|((job->use_shortcuts)<<1)); if(ret<=0) return(ret); f= *fnode; if(flag&1) { f->up= job->cursor; if(job->cursor!=NULL && !(flag&2)) { if(job->cursor->sub!=NULL) { /* This would become a memory leak */ job->errn= -2; sprintf(job->errmsg, "Program error while parsing -job : sub branch overwrite"); Exprnode_destroy(fnode, 0); return(0); } else job->cursor->sub= f; } } else { if(job->cursor != NULL) f->up= job->cursor->up; f->left= job->cursor; if(job->cursor!=NULL) job->cursor->right= f; } job->invert= 0; return(1); } /* If an operator is expected : use -and @param flag bit0= prepare for a pseudo-test: if an operator is expected, do nothing and return 2 bit1= use -or rather than -and */ int Findjob_default_and(struct FindjoB *o, int flag) { int ret; if(Findjob_cursor_complete(o, 0)) { if(flag & 1) return(2); if(flag & 2) { ret= Findjob_or(o, 0); } else { ret= Findjob_and(o, 0); } if(ret <= 0) return(ret); } return(1); } int Findjob_open_bracket(struct FindjoB *job, int flag) { int ret; struct ExprnodE *fnode; ret= Findjob_default_and(job, 0); if(ret <= 0) return(ret); ret= Findjob_new_node(job, &fnode, "-sub", 1); if(ret <= 0) return(ret); job->cursor= fnode; return(1); } int Findjob_close_bracket(struct FindjoB *job, int flag) { if(!Findjob_cursor_complete(job, 0)) { job->errn= -3; sprintf(job->errmsg, "Unary operator or expression expected, closing-bracket found"); return(0); } if(job->cursor->up==NULL){ job->errn= -1; sprintf(job->errmsg, "No bracket open when encountering closing bracket."); return(0); } job->cursor= job->cursor->up; return(1); } int Findjob_not(struct FindjoB *job, int flag) { int ret; ret= Findjob_default_and(job, 0); if(ret <= 0) return(ret); job->cursor->invert= !job->cursor->invert; return(1); } int Findjob_and(struct FindjoB *job, int flag) { int ret; struct ExprnodE *fnode; if(!Findjob_cursor_complete(job, 0)) { job->errn= -3; sprintf(job->errmsg, "Unary operator or expression expected, binary operator found"); return(0); } ret= Findjob_new_node(job, &fnode, "-and", 0); if(ret<=0) return(ret); job->cursor->right_op= 1; job->cursor->assoc= 1; /* compute right side first */ fnode->left_op= 1; fnode->assoc= 0; /* compute left side first */ job->cursor= fnode; return(1); } int Findjob_or(struct FindjoB *job, int flag) { int ret; struct ExprnodE *fnode; if(!Findjob_cursor_complete(job, 0)) { job->errn= -3; sprintf(job->errmsg, "Unary operator or expression expected, binary operator found"); return(0); } ret= Findjob_new_node(job, &fnode, "-or", 0); if(ret<=0) return(ret); job->cursor->right= fnode; job->cursor->right_op= 0; /* if existing : compute left side first */ job->cursor->assoc= (job->cursor->left == NULL); fnode->left= job->cursor; fnode->left_op= 0; fnode->assoc= 0; /* no right side yet : compute left side first */ job->cursor= fnode; return(1); } int Findjob_if(struct FindjoB *job, int flag) { int ret; struct ExprnodE *fnode; ret= Findjob_default_and(job, 0); if(ret <= 0) return(ret); ret= Findjob_new_node(job, &fnode, "-if", 1); if(ret<=0) return(ret); Exprnode_set_is_if(fnode,1,0); job->cursor= fnode; return(1); } int Findjob_then(struct FindjoB *job, int flag) { int ret; struct ExprnodE *fnode,*branch= NULL; if(! Findjob_cursor_complete(job,0)) { job->errn= -3; sprintf(job->errmsg, "Unary operator or expression expected, -then-operator found"); return(0); } /* Finding the -if that matches this -then Do not go up one node but look for the leftmost one. If everything is right we are at level of the -if node */ while(job->cursor->left!=NULL) job->cursor= job->cursor->left; Exprnode_get_branch(job->cursor, &branch, 0); if(!Exprnode_is_if(job->cursor, 0) || branch != NULL) { job->errn= -5; sprintf(job->errmsg, "-then-operator found outside its proper range."); return(0); } ret= Findjob_new_node(job, &fnode, "-then", 1|2); if(ret <= 0) return(ret); Exprnode_set_branch(job->cursor, fnode, 0); job->cursor= fnode; return(1); } int Findjob_else(struct FindjoB *job, int flag) { int ret; struct ExprnodE *fnode, *true_branch, *false_branch; if(! Findjob_cursor_complete(job, 0)) { job->errn= -3; sprintf(job->errmsg, "Unary operator or expression expected, -else-operator found"); return(0); } if(job->cursor->up == NULL) goto improper_range; job->cursor= job->cursor->up; Exprnode_get_branch(job->cursor, &true_branch, 0); Exprnode_get_branch(job->cursor, &false_branch, 1); if(!Exprnode_is_if(job->cursor, 0) || true_branch == NULL || false_branch != NULL) { improper_range:; job->errn= -5; sprintf(job->errmsg, "-else-operator found outside its proper range."); return(0); } ret= Findjob_new_node(job, &fnode, "-else", 1 | 2); if(ret <= 0) return(ret); Exprnode_set_branch(job->cursor, fnode, 1); job->cursor= fnode; return(1); } int Findjob_elseif(struct FindjoB *job, int flag) { int ret; struct ExprnodE *true_branch, *false_branch; if(!Findjob_cursor_complete(job, 0)) { job->errn= -3; sprintf(job->errmsg, "Unary operator or expression expected, -elseif-operator found"); return(0); } if(job->cursor->up == NULL) goto improper_range; job->cursor= job->cursor->up; Exprnode_get_branch(job->cursor, &true_branch, 0); Exprnode_get_branch(job->cursor, &false_branch, 1); if(!Exprnode_is_if(job->cursor, 0) || true_branch==NULL || false_branch!=NULL) { improper_range:; job->errn= -5; sprintf(job->errmsg, "-elseif-operator found outside its proper range."); return(0); } job->cursor= job->cursor->up; /* -elseif is equivalent to the three-step sequence : -endif -or -if ( -endif has already been performed by following job->cursor->up ) */ ret= Findjob_or(job, 0); if(ret <= 0) return(0); ret= Findjob_if(job, 0); if(ret <= 0) return(0); return(1); } int Findjob_endif(struct FindjoB *job, int flag) { struct ExprnodE *true_branch; if(!Findjob_cursor_complete(job,0)) { job->errn= -3; sprintf(job->errmsg, "Unary operator or expression expected, -endif found"); return(0); } if(job->cursor->up==NULL) goto improper_range; /* test whether parent node is -if */ job->cursor= job->cursor->up; Exprnode_get_branch(job->cursor, &true_branch, 0); if(!Exprnode_is_if(job->cursor,0) || true_branch == NULL) { improper_range:; job->errn= -5; sprintf(job->errmsg, "-endif-mark found outside its proper range."); return(0); } /* go to grand parent node */ job->cursor= job->cursor->up; return(1); } /* @param flag bit0-1: 0= -name , 1= -wholename , 2= -disk_name , 3= -disk_path */ int Findjob_set_name_expr(struct FindjoB *o, char *name_expr, int flag) { char *regexpr= NULL; regex_t *name_re; struct ExprtesT *t; int ret; regexpr= TSOB_FELD(char, 2*SfileadrL+2); if(regexpr == NULL) {ret= -1; goto ex;} if(strlen(name_expr)>=SfileadrL) {ret= 0; goto ex;}; ret= Findjob_default_and(o, 0); if(ret <= 0) goto ex; t= o->cursor->test; t->test_type= 1; if ((flag & 3) == 1) t->test_type= 13; else if((flag & 3) == 2) t->test_type= 16; else if((flag & 3) == 3) t->test_type= 20; t->arg1= strdup(name_expr); if(t->arg1 == NULL) {ret= -1; goto ex;}; if((flag & 3) == 3) {ret= 1; goto ex;} name_re= (regex_t *) calloc(1, sizeof(regex_t)); if(name_re == NULL) {ret= -1; goto ex;}; Xorriso__bourne_to_reg(name_expr, regexpr, 0); if(regcomp(name_re, regexpr, 0) != 0) { free((char *) name_re); {ret= 0; goto ex;}; } t->arg2= name_re; ret= 1; ex:; Xorriso_free_meM(regexpr); return(ret); } int Findjob_set_file_type(struct FindjoB *o, char file_type, int flag) { static char known[]= {"bcdpf-lsmeX"}; struct ExprtesT *t; int ret; ret= Findjob_default_and(o, 0); if(ret <= 0) return(ret); if(file_type != 0) if(strchr(known, file_type) == NULL) return(0); t= o->cursor->test; t->test_type= 2; t->arg1= calloc(1, 1); if(t->arg1 == NULL) return(-1); *((char *) t->arg1)= file_type; return(1); } /* @param value -1= only without property, 1= only with property @param flag bit0= pseudo-test: if no operator is open, do nothing and return 2 */ int Findjob_set_prop_filter(struct FindjoB *o, int test_type, int value, int flag) { struct ExprtesT *t; int ret; ret= Findjob_default_and(o, flag & 1); if(ret <= 0 || ret == 2) return(ret); t= o->cursor->test; t->test_type= test_type; if(value < 0) t->invert= !t->invert; return(1); } /* @param value -1= only undamaged files, 1= only damaged files */ int Findjob_set_damage_filter(struct FindjoB *o, int value, int flag) { int ret; ret= Findjob_set_prop_filter(o, 3, value, 0); return(ret); } int Findjob_set_num_filter(struct FindjoB *o, int test_type, int num1, int num2, int flag) { struct ExprtesT *t; int ret; ret= Findjob_default_and(o, 0); if(ret <= 0) return(ret); t= o->cursor->test; t->test_type= test_type; t->arg1= calloc(sizeof(int), 1); t->arg2= calloc(sizeof(int), 1); if(t->arg1 == NULL || t->arg2 == NULL) return(-1); *((int *) t->arg1)= num1; *((int *) t->arg2)= num2; return(1); } int Findjob_set_size_filter(struct FindjoB *o, int test_type, off_t num1, int num2, int flag) { struct ExprtesT *t; int ret; ret= Findjob_default_and(o, 0); if(ret <= 0) return(ret); t= o->cursor->test; t->test_type= test_type; t->arg1= calloc(sizeof(off_t), 1); t->arg2= calloc(sizeof(int), 1); if(t->arg1 == NULL || t->arg2 == NULL) return(-1); *((off_t *) t->arg1)= num1; *((int *) t->arg2)= num2; return(1); } int Findjob_set_off_t_filter(struct FindjoB *o, int test_type, off_t num1, off_t num2, int flag) { struct ExprtesT *t; int ret; ret= Findjob_default_and(o, 0); if(ret <= 0) return(ret); t= o->cursor->test; t->test_type= test_type; t->arg1= calloc(sizeof(off_t), 1); t->arg2= calloc(sizeof(off_t), 1); if(t->arg1 == NULL || t->arg2 == NULL) return(-1); *((off_t *) t->arg1)= num1; *((off_t *) t->arg2)= num2; return(1); } int Findjob_set_lba_range(struct FindjoB *o, off_t start_lba, off_t count, int flag) { int ret; off_t end_lba; if(start_lba > 0) end_lba= start_lba + count - 1; else end_lba= start_lba - count + 1; ret= Findjob_set_off_t_filter(o, 4, start_lba, end_lba, 0); return(ret); } int Findjob_set_test_hidden(struct FindjoB *o, int mode, int flag) { struct ExprtesT *t; int ret; ret= Findjob_default_and(o, 0); if(ret <= 0) return(ret); t= o->cursor->test; t->test_type= 17; t->arg1= calloc(sizeof(int), 1); if(t->arg1 == NULL) return(-1); *((int *) t->arg1)= mode; return(1); } /* @param value -1= files without ACL, 1= only files with ACL */ int Findjob_set_acl_filter(struct FindjoB *o, int value, int flag) { int ret; ret= Findjob_set_prop_filter(o, 5, value, 0); return(ret); } /* @param value -1= files without xattr, 1= only files with xattr @param flag bit0=-has_any_xattr rather than -has_xattr */ int Findjob_set_xattr_filter(struct FindjoB *o, int value, int flag) { int ret; ret= Findjob_set_prop_filter(o, (flag & 1 ? 14 : 6), value, 0); return(ret); } /* @param value -1= files without aaip, 1= only files with aaip */ int Findjob_set_aaip_filter(struct FindjoB *o, int value, int flag) { int ret; ret= Findjob_set_prop_filter(o, 7, value, 0); return(ret); } /* @param value -1= files without filter, 1= files with filter */ int Findjob_set_filter_filter(struct FindjoB *o, int value, int flag) { int ret; ret= Findjob_set_prop_filter(o, 8, value, 0); return(ret); } int Findjob_set_crtp_filter(struct FindjoB *o, char *creator, char *hfs_type, int flag) { struct ExprtesT *t; int ret; ret= Findjob_default_and(o, 0); if(ret <= 0) return(ret); t= o->cursor->test; t->test_type= 18; t->arg1= calloc(1, strlen(creator) + 1); t->arg2= calloc(1, strlen(hfs_type) + 1); if(t->arg1 == NULL || t->arg2 == NULL) return(-1); strcpy(t->arg1, creator); strcpy(t->arg2, hfs_type); return(1); } int Findjob_set_bless_filter(struct XorrisO *xorriso, struct FindjoB *o, char *blessing, int flag) { struct ExprtesT *t; int ret; ret= Findjob_default_and(o, 0); if(ret <= 0) return(ret); t= o->cursor->test; t->test_type= 19; t->arg1= calloc(1, sizeof(int)); if(t->arg1 == NULL) return(-1); ret= Xorriso_hfsplus_bless(xorriso, "", NULL, blessing, 4 | 8); if(ret <= 0) return(ret); *((int *) t->arg1)= ret - 1; return(1); } int Findjob_set_wanted_node(struct FindjoB *o, void *wanted_node, int flag) { struct ExprtesT *t; int ret; ret= Findjob_default_and(o, 0); if(ret <= 0) return(ret); t= o->cursor->test; t->test_type= 9; t->arg1= wanted_node; return(1); } int Findjob_set_commit_filter_2(struct FindjoB *o, int flag) { int ret; ret= Findjob_default_and(o, 0); if(ret <= 0) return(ret); o->cursor->test->test_type= 10; return(1); } int Findjob_set_uint64_filter(struct FindjoB *o, int test_type, uint64_t value, int flag) { struct ExprtesT *t; int ret; ret= Findjob_default_and(o, 0); if(ret <= 0) return(ret); t= o->cursor->test; t->test_type= test_type; t->arg1= calloc(sizeof(uint64_t), 1); *((uint64_t *) t->arg1)= value; return(1); } int Findjob_set_arg1(struct FindjoB *o, int test_type, char *arg1, int flag) { struct ExprtesT *t; int ret, hflag= 0; if(test_type == 23) hflag= 2; /* prepend -or rather than -and */ ret= Findjob_default_and(o, hflag); if(ret <= 0) return(ret); t= o->cursor->test; t->test_type= test_type; t->arg1= strdup(arg1); if(t->arg1 == NULL) return(-1); return(1); } /* @param value -1= true, 1= false @param flag bit0= pseudo-test: if no operator is open, do nothing and return 2 */ int Findjob_set_false(struct FindjoB *o, int value, int flag) { int ret; ret= Findjob_set_prop_filter(o, 0, value, flag & 1); return(ret); } int Findjob_set_prune(struct FindjoB *o, int flag) { int ret; ret= Findjob_set_prop_filter(o, 12, 0, 0); return(ret); } int Findjob_set_found_path(struct FindjoB *o, char *path, int flag) { if(o->found_path != NULL) free(o->found_path); if(path != NULL) { o->found_path= strdup(path); if(o->found_path == NULL) return(-1); } else o->found_path= NULL; return(1); } int Findjob_get_found_path(struct FindjoB *o, char **path, int flag) { *path= o->found_path; return(1); } int Findjob_get_last_data_file_block(struct FindjoB *o, uint32_t *lba, int flag) { *lba= o->last_data_file_block; return(1); } int Findjob_get_action(struct FindjoB *o, int flag) { return(o->action); } /* @return <0 error, >=0 see above struct FindjoB.action */ int Findjob_get_action_parms(struct FindjoB *o, char **target, char **text_2, uid_t *user, gid_t *group, mode_t *mode_and, mode_t *mode_or, int *type, time_t *date, struct FindjoB **subjob, uint64_t *lfa_flags, int flag) { *target= o->target; *text_2= o->text_2; *user= o->user; *group= o->group; *mode_and= o->mode_and; *mode_or= o->mode_or; *type= o->type; *date= o->date; *subjob= o->subjob; *lfa_flags= o->lfa_flags; return(o->action); } int Findjob_test_2(struct XorrisO *xorriso, struct FindjoB *o, void *node, char *name, char *path, struct stat *boss_stbuf, struct stat *stbuf, int flag) { int ret; ret= Exprnode_tree_value(xorriso, o->test_tree, -1, node, name, path, boss_stbuf, stbuf, 0); if(ret == 3) ret= 1; else if(ret == 2) ret= 0; return(ret); } int Findjob_set_action_target(struct FindjoB *o, int action, char *target, int flag) { o->action= action; o->target= target; return(1); } int Findjob_set_action_type(struct FindjoB *o, int action, int type, int flag) { o->action= action; o->type= type; return(1); } int Findjob_set_action_text_2(struct FindjoB *o, int action, char *target, char* text_2, int flag) { o->action= action; o->target= target; o->text_2= text_2; return(1); } /* @param flag bit0= recursive */ int Findjob_set_action_chown(struct FindjoB *o, uid_t user,int flag) { int ret; if(flag&1) { o->action= 0; Findjob_destroy(&(o->subjob), 0); ret= Findjob_new(&(o->subjob), "", 0); if(ret<=0) return(-1); Findjob_set_action_chown(o->subjob, user, 0); o->action= 9; } else { o->action= 4; o->user= user; } return(1); } /* @param flag bit0= recursive */ int Findjob_set_action_chgrp(struct FindjoB *o, gid_t group, int flag) { int ret; if(flag&1) { o->action= 0; Findjob_destroy(&(o->subjob), 0); ret= Findjob_new(&(o->subjob), "", 0); if(ret<=0) return(-1); Findjob_set_action_chgrp(o->subjob, group, 0); o->action= 10; } else { o->action= 5; o->group= group; } return(1); } /* @param flag bit0= recursive */ int Findjob_set_action_chmod(struct FindjoB *o, mode_t mode_and, mode_t mode_or, int flag) { int ret; if(flag&1) { o->action= 0; Findjob_destroy(&(o->subjob), 0); ret= Findjob_new(&(o->subjob), "", 0); if(ret<=0) return(-1); Findjob_set_action_chmod(o->subjob, mode_and, mode_or, 0); o->action= 11; } else { o->action= 6; o->mode_and= mode_and; o->mode_or= mode_or; } return(1); } /* @param flag bit0= recursive */ int Findjob_set_action_ad(struct FindjoB *o, int type, time_t date, int flag) { int ret; if(flag&1) { o->action= 0; Findjob_destroy(&(o->subjob), 0); ret= Findjob_new(&(o->subjob), "", 0); if(ret<=0) return(-1); Findjob_set_action_ad(o->subjob, type, date, 0); o->action= 12; } else { o->action= 7; o->type= type; o->date= date; } return(1); } int Findjob_set_action_subjob(struct FindjoB *o, int action, struct FindjoB *subjob, int flag) { o->action= action; Findjob_destroy(&(o->subjob), 0); o->subjob= subjob; return(1); } int Findjob_set_action_found_path(struct FindjoB *o, int flag) { o->action= 23; Findjob_set_found_path(o, NULL, 0); return(1); } int Findjob_set_action_chattr(struct FindjoB *o, int action, uint64_t lfa_flags, int operator, int flag) { o->action= action; o->lfa_flags= lfa_flags; o->type= operator; return(1); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of classes FindjoB, ExprnodE, ExprtesT which perform tree searches in libisofs or in POSIX filesystem. */ #ifndef Xorriso_pvt_findjob_includeD #define Xorriso_pvt_findjob_includeD yes #define Xorriso_findjob_on_expR yes #ifdef Xorriso_findjob_on_expR /* A single Testnode. */ struct ExprtesT { struct FindjoB *boss; int invert; /* 0=normal 1=invert result */ /* 0= -false (with invert : -true) 1= -name char *arg1 (regex_t in *arg2) 2= -type char *arg1 3= -damaged 4= -lba_range off_t *arg1 off_t *arg2 5= -has_acl 6= -has_xattr 7= -has_aaip 8= -has_filter 9= -wanted_node IsoNode *arg1 (for internal use, arg1 not allocated) 10= -pending_data 11= -decision char *arg1 ("yes", "no") 12= -prune 13= -wholename char *arg1 (regex_t in *arg2) 14= -has_any_xattr 15= -has_md5 16= -disk_name char *arg1 (regex_t in *arg2) 17= -hidden int *arg1 (bit0=iso_rr, bit1=joliet) 18= -has_hfs_crtp char *creator char *type 19= -has_hfs_bless int bless_index 20= -disk_path char *arg1 21= -bad_outname int namespace 22= -use_pattern char *arg1 ("on" [or "ls"], "off") 23= -or_use_pattern char *arg1 ("on" [or "ls"], "off") 24= -name_limit_blocker int *arg1 25= -maxdepth int *arg1 26= -mindepth int *arg1 27= -size off_t *arg1 int *arg2 (0=test for equal, -1=smaller, 1=larger, -2=smaller_or_equal , 2=larger_or_equal) 28= -has_lfa_flags uint64_t *arg1 (Linux file attribute flag bits) 29= -has_some_lfa_flags_of uint64_t *arg1 */ int test_type; void *arg1; void *arg2; }; /* A computational node. A tree of these nodes forms the expression. Sequences of AND/OR operations form branches, brackets spawn new branches, NOT inverts node's test resp. subtree result. */ struct ExprnodE { struct ExprnodE *up; char origin[8]; /* Operators */ int invert; /* 0=normal 1=invert own result (subtree or test, but not op) */ int assoc; /* 0= left : compute own value, combine with left value, compute right value, combine with current value 1= right: compute own value, compute right value, combine own and right, combine with left value */ int use_shortcuts; /* 0= evaluate all tests of -and and -or, 1= evaluate only until the combined result is known */ struct ExprnodE *left; int left_op; /* 0=OR , 1=AND */ struct ExprnodE *right; int right_op; /* see left_op */ /* Brackets : a pointer to the first node in a subchain */ struct ExprnodE *sub; int is_if_then_else; struct ExprnodE *true_branch; struct ExprnodE *false_branch; /* elementary test : if sub!=NULL , test is ignored */ struct ExprtesT *test; /* Result */ int own_value; int composed_value; }; struct FindjoB { char *start_path; struct ExprnodE *test_tree; struct ExprnodE *cursor; int invert; /* 0=normal 1=set invert-property for next new test node */ int use_shortcuts; /* 0= echo 1= rm (also rmdir) 2= rm_r >>> 3= mv target 4= chown user 5= chgrp group 6= chmod mode_and mode_or 7= alter_date type date 8= lsdl 9= chown_r user 10= chgrp_r group 11= chmod_r mode_and mode_or 12= alter_date_r type date 13= find 14= compare disk_equivalent_of_start_path 15= in_iso iso_rr_equivalent_of_start_path 16= not_in_iso iso_rr_equiv 17= update disk_equiv 18= add_missing iso_rr_equiv 19= empty_iso_dir iso_rr_equiv 20= is_full_in_iso iso_rr_equiv 21= report_damage 22= report_lba 23= internal: memorize path of last matching node in found_path 24= getfacl 25= setfacl access_acl default_acl 26= getfattr 27= setfattr name value 28= set_filter name 29= show_stream 30= internal: count by xorriso->node_counter 31= internal: register in xorriso->node_array 32= internal: widen_hardlinks disk_equiv: update nodes marked in di_do_widen 33= get_any_xattr 34= get_md5 35= check_md5 36= make_md5 37= mkisofs_r 38= sort_weight number 39= hide on|iso_rr|joliet|off 40= estimate_size 41= update_merge disk_equiv 42= rm_merge 43= clear_merge 44= list_extattr 45= set_hfs_crtp creator type 46= get_hfs_crtp 47= set_hfs_bless blessing 48= get_hfs_bless 49= internal: update creator, type, and blessings from persistent isofs.* 50= print_outname namespace 51= report_sections 52= show_stream_id 53= internal: show_hfs_cmd , controlled by xorriso->show_hfs_cmd* 54= internal: truncate_names 55= internal: unique_trunc_test length test for not uniquely truncatable names, result delivered in XorrisO.find_unique_trunc_result 56= like 54 but tolerating existing truncated names 57= like 55 but tolerating existing truncated names 58= internal: last_data_file_block 59= set_to_mtime 60= lsattrd 61= chattr mode 62= internal: like 27 "setfattr name value" but with permission for all name spaces including "isofs." */ int action; int prune; int use_pattern; /* action specific parameters */ char *target; char *text_2; uid_t user; gid_t group; mode_t mode_and, mode_or; int type; /* see Xorriso_set_time flag, also used as weight and truncate_length */ time_t date; char *found_path; off_t estim_upper_size; off_t estim_lower_size; struct FindjoB *subjob; uint32_t last_data_file_block; uint64_t lfa_flags; /* Errors */ char errmsg[4096]; int errn; /* >0 = UNIX errno -1 = close_bracket: no bracket open -2 = binary operator or closing bracket expected -3 = unexpected binary operator or closing bracket -4 = unsupported command -5 = -then -elseif -else -endif without -if or at wrong place */ /* Counts the test matches */ unsigned long match_count; /* Current depth of tree walking. Relative to start path. */ int depth; }; int Exprnode_destroy(struct ExprnodE **fnode, int flag); int Exprnode_tree_value(struct XorrisO *xorriso, struct ExprnodE *fnode, int left_value, void *node, char *name, char *path, struct stat *boss_stbuf, struct stat *stbuf, int flag); int Findjob_new(struct FindjoB **o, char *start_path, int flag); int Findjob_destroy(struct FindjoB **o, int flag); int Findjob_set_start_path(struct FindjoB *o, char *start_path, int flag); int Findjob_get_start_path(struct FindjoB *o, char **start_path, int flag); int Findjob_set_commit_filter_2(struct FindjoB *o, int flag); int Findjob_set_num_filter(struct FindjoB *o, int test_type, int num1, int num2, int flag); int Findjob_set_size_filter(struct FindjoB *o, int test_type, off_t num1, int num2, int flag); int Findjob_set_off_t_filter(struct FindjoB *o, int test_type, off_t num1, off_t num2, int flag); int Findjob_set_lba_range(struct FindjoB *o, off_t start_lba, off_t count, int flag); int Findjob_set_wanted_node(struct FindjoB *o, void *wanted_node, int flag); /* @param value -1= only undamaged files, 1= only damaged files */ int Findjob_set_damage_filter(struct FindjoB *o, int value, int flag); int Findjob_set_test_hidden(struct FindjoB *o, int mode, int flag); int Findjob_set_crtp_filter(struct FindjoB *o, char *creator, char *hfs_type, int flag); int Findjob_set_bless_filter(struct XorrisO *xorriso, struct FindjoB *o, char *blessing, int flag); int Findjob_set_arg1(struct FindjoB *o, int test_type, char *arg1, int flag); int Findjob_set_uint64_filter(struct FindjoB *o, int test_type, uint64_t value, int flag); int Findjob_open_bracket(struct FindjoB *job, int flag); int Findjob_close_bracket(struct FindjoB *job, int flag); int Findjob_not(struct FindjoB *job, int flag); int Findjob_and(struct FindjoB *job, int flag); int Findjob_or(struct FindjoB *job, int flag); int Findjob_if(struct FindjoB *job, int flag); int Findjob_then(struct FindjoB *job, int flag); int Findjob_else(struct FindjoB *job, int flag); int Findjob_elseif(struct FindjoB *job, int flag); int Findjob_endif(struct FindjoB *job, int flag); int Findjob_test_2(struct XorrisO *xorriso, struct FindjoB *o, void *node, char *name, char *path, struct stat *boss_stbuf, struct stat *stbuf, int flag); int Findjob_set_action_found_path(struct FindjoB *o, int flag); int Findjob_set_action_chattr(struct FindjoB *o, int action, uint64_t lfa_flags, int operator, int flag); /* @param flag bit0= recursive */ int Findjob_set_action_target(struct FindjoB *o, int action, char *target, int flag); /* @param flag bit0= recursive */ int Findjob_set_action_ad(struct FindjoB *o, int type, time_t date, int flag); /* @param flag bit0= recursive */ int Findjob_set_action_chgrp(struct FindjoB *o, gid_t group, int flag); /* @param flag bit0= recursive */ int Findjob_set_action_chmod(struct FindjoB *o, mode_t mode_and, mode_t mode_or, int flag); /* @param flag bit0= recursive */ int Findjob_set_action_chown(struct FindjoB *o, uid_t user,int flag); /* @param flag bit0= -wholename rather than -name */ int Findjob_set_name_expr(struct FindjoB *o, char *name_expr, int flag); int Findjob_set_file_type(struct FindjoB *o, char file_type, int flag); /* @param value -1= files without ACL, 1= only files with ACL */ int Findjob_set_acl_filter(struct FindjoB *o, int value, int flag); /* @param value -1= files without xattr, 1= only files with xattr @param flag bit0=-has_any_xattr rather than -has_xattr */ int Findjob_set_xattr_filter(struct FindjoB *o, int value, int flag); /* @param value -1= files without aaip, 1= only files with aaip */ int Findjob_set_aaip_filter(struct FindjoB *o, int value, int flag); /* @param value -1= files without filter, 1= files with filter */ int Findjob_set_filter_filter(struct FindjoB *o, int value, int flag); /* @param value -1= only without property, 1= only with property @param flag bit0= pseudo-test: if no operator is open, do nothing and return 2 */ int Findjob_set_prop_filter(struct FindjoB *o, int test_type, int value, int flag); /* @param value -1= true, 1= false @param flag bit0= pseudo-test: if no operator is open, do nothing and return 2 */ int Findjob_set_false(struct FindjoB *o, int value, int flag); int Findjob_set_prune(struct FindjoB *o, int flag); int Findjob_set_action_subjob(struct FindjoB *o, int action, struct FindjoB *subjob, int flag); int Findjob_set_action_text_2(struct FindjoB *o, int action, char *target, char* text_2, int flag); int Findjob_set_action_type(struct FindjoB *o, int action, int type, int flag); int Findjob_get_action(struct FindjoB *o, int flag); int Findjob_get_action_parms(struct FindjoB *o, char **target, char **text_2, uid_t *user, gid_t *group, mode_t *mode_and, mode_t *mode_or, int *type, time_t *date, struct FindjoB **subjob, uint64_t *lfa_flags, int flag); int Findjob_set_found_path(struct FindjoB *o, char *path, int flag); int Findjob_get_found_path(struct FindjoB *o, char **path, int flag); int Findjob_get_last_data_file_block(struct FindjoB *o, uint32_t *lba, int flag); #else /* Xorriso_findjob_on_expR */ struct FindjoB; int Findjob_new(struct FindjoB **o, char *start_path, int flag); int Findjob_destroy(struct FindjoB **job, int flag); /* @return 0=no match , 1=match , <0 = error */ int Findjob_test(struct FindjoB *job, char *name, struct stat *boss_stbuf, struct stat *stbuf, int depth, int flag); /* @return <0 error, >=0 see xorriso.c struct FindjoB.action */ int Findjob_get_action(struct FindjoB *o, int flag); /* @return <0 error, >=0 see xorriso.c struct FindjoB.action */ int Findjob_get_action_parms(struct FindjoB *o, char **target, char **text_2, uid_t *user, gid_t *group, mode_t *mode_and, mode_t *mode_or, int *type, time_t *date, struct FindjoB **subjob, int flag); /* @param flag bit0= recursive */ int Findjob_set_action_target(struct FindjoB *o, int action, char *target, int flag); /* @param flag bit0= recursive */ int Findjob_set_action_chgrp(struct FindjoB *o, gid_t group, int flag); /* @param flag bit0= recursive */ int Findjob_set_action_chmod(struct FindjoB *o, mode_t mode_and, mode_t mode_or, int flag); /* @param flag bit0= recursive */ int Findjob_set_action_ad(struct FindjoB *o, int type, time_t date, int flag); int Findjob_set_start_path(struct FindjoB *o, char *start_path, int flag); int Findjob_set_action_found_path(struct FindjoB *o, int flag); int Findjob_get_start_path(struct FindjoB *o, char **start_path, int flag); int Findjob_set_lba_range(struct FindjoB *o, int start_lba, int count, int flag); int Findjob_get_lba_damage_filter(struct FindjoB *o, int *start_lba, int *end_lba, int *damage_filter, int flag); int Findjob_get_commit_filter(struct FindjoB *o, int *commit_filter, int flag); int Findjob_get_acl_filter(struct FindjoB *o, int *acl_filter, int flag); int Findjob_get_xattr_filter(struct FindjoB *o, int *xattr_filter, int flag); int Findjob_get_aaip_filter(struct FindjoB *o, int *aaip_filter, int flag); int Findjob_get_filter_filter(struct FindjoB *o, int *value, int flag); int Findjob_set_wanted_node(struct FindjoB *o, void *wanted_node, int flag); int Findjob_get_wanted_node(struct FindjoB *o, void **wanted_node, int flag); int Findjob_set_found_path(struct FindjoB *o, char *path, int flag); int Findjob_get_found_path(struct FindjoB *o, char **path, int flag); int Findjob_get_last_data_file_block(struct FindjoB *o, uint32_t *lba, int flag); #endif /* ! Xorriso_findjob_on_expR */ #endif /* ! Xorriso_pvt_findjob_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains functions which operate on ISO images and their global properties. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include <sys/wait.h> #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" #include "lib_mgt.h" #include "iso_img.h" #include "iso_tree.h" #include "drive_mgt.h" int Xorriso_set_ignore_aclea(struct XorrisO *xorriso, int flag) { int ret, hflag; IsoImage *volume; ret= Xorriso_get_volume(xorriso, &volume, 1); if(ret<=0) return(ret); hflag= (~xorriso->do_aaip) & 1; if((xorriso->ino_behavior & (1 | 2)) && !(xorriso->do_aaip & (4 | 16))) hflag|= 2; if(xorriso->do_aaip & 1024) hflag|= 8; if(xorriso->do_aaip & 2048) hflag|= 4; if(xorriso->do_aaip & (1 << 15)) hflag|= 32; iso_image_set_ignore_aclea(volume, hflag); return(1); } int Xorriso_update_volid(struct XorrisO *xorriso, int flag) { int gret, sret= 1; gret= Xorriso_get_volid(xorriso, xorriso->loaded_volid, 0); if(gret<=0 || (!xorriso->volid_default) || xorriso->loaded_volid[0]==0) sret= Xorriso_set_volid(xorriso, xorriso->volid, 1); return(gret>0 && sret>0); } int Xorriso_create_empty_iso(struct XorrisO *xorriso, int flag) { int ret; IsoImage *volset; struct isoburn_read_opts *ropts; struct burn_drive_info *dinfo= NULL; struct burn_drive *drive= NULL; if(xorriso->out_drive_handle != NULL) { ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to attach volset to drive", 2); if(ret<=0) return(ret); } if(xorriso->in_volset_handle!=NULL) { iso_image_unref((IsoImage *) xorriso->in_volset_handle); xorriso->in_volset_handle= NULL; Sectorbitmap_destroy(&(xorriso->in_sector_map), 0); Xorriso_destroy_di_array(xorriso, 0); Xorriso_destroy_hln_array(xorriso, 0); xorriso->loaded_volid[0]= 0; xorriso->volset_change_pending= 0; xorriso->boot_count= 0; xorriso->no_volset_present= 0; } ret= isoburn_ropt_new(&ropts, 0); if(ret<=0) return(ret); /* Note: no return before isoburn_ropt_destroy() */ isoburn_ropt_set_extensions(ropts, isoburn_ropt_pretend_blank); isoburn_ropt_set_input_charset(ropts, xorriso->in_charset); isoburn_ropt_set_data_cache(ropts, 1, 1, 0); isoburn_set_read_pacifier(drive, NULL, NULL); isoburn_ropt_set_truncate_mode(ropts, 1, xorriso->file_name_limit); ret= isoburn_read_image(drive, ropts, &volset); Xorriso_process_msg_queues(xorriso,0); isoburn_ropt_destroy(&ropts, 0); if(ret<=0) { sprintf(xorriso->info_text, "Failed to create new empty ISO image object"); Xorriso_report_iso_error(xorriso, "", ret, xorriso->info_text, 0, "FATAL", 0); return(-1); } xorriso->in_volset_handle= (void *) volset; xorriso->in_sector_map= NULL; Xorriso_update_volid(xorriso, 0); xorriso->volset_change_pending= 0; xorriso->boot_count= 0; xorriso->system_area_clear_loaded= (strcmp(xorriso->system_area_disk_path, "/dev/zero") == 0); xorriso->no_volset_present= 0; return(1); } int Xorriso_record_boot_info(struct XorrisO *xorriso, int flag) { int ret; struct burn_drive_info *dinfo; struct burn_drive *drive; IsoImage *image; ElToritoBootImage *bootimg; IsoFile *bootimg_node; IsoBoot *bootcat_node; xorriso->loaded_boot_bin_lba= -1; xorriso->loaded_boot_cat_path[0]= 0; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to record boot LBAs", 0); if(ret<=0) return(0); image= isoburn_get_attached_image(drive); if(image == NULL) return(0); ret= iso_image_get_boot_image(image, &bootimg, &bootimg_node, &bootcat_node); iso_image_unref(image); /* release obtained reference */ if(ret != 1) return(0); if(bootimg_node != NULL) Xorriso__file_start_lba((IsoNode *) bootimg_node, &(xorriso->loaded_boot_bin_lba), 0); if(bootcat_node != NULL) Xorriso_path_from_lba(xorriso, (IsoNode *) bootcat_node, (off_t) 0, xorriso->loaded_boot_cat_path, 0); return(1); } int Xorriso_assert_volid(struct XorrisO *xorriso, off_t msc1, int flag) { int ret; off_t image_blocks; char volid[33]; struct burn_drive_info *dinfo; struct burn_drive *drive; if(xorriso->assert_volid[0] == 0) return(1); ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to perform -assert_volid", 0); if(ret<=0) return(0); ret= isoburn_read_iso_head_v2(drive, msc1, &image_blocks, volid, 1); Xorriso_process_msg_queues(xorriso,0); if(ret <= 0) { sprintf(xorriso->info_text, "-assert_volid: Cannot determine Volume Id at LBA %.f.", (double) msc1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, xorriso->assert_volid_sev, 0); return(0); } ret= Sregex_match(xorriso->assert_volid, volid, 0); if(ret < 0) return(2); if(ret == 0) { strcpy(xorriso->info_text, "-assert_volid: Volume id does not match pattern: "); Text_shellsafe(xorriso->assert_volid, xorriso->info_text, 1); strcat(xorriso->info_text, " <> "); Text_shellsafe(volid, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, xorriso->assert_volid_sev, 0); return(0); } return(ret); } /* @return <0 yes , 0 no , <0 error */ int Xorriso_is_isohybrid(struct XorrisO *xorriso, IsoFile *bootimg_node, int flag) { int ret; unsigned char buf[68]; void *data_stream= NULL; ret= Xorriso_iso_file_open(xorriso, "", (void *) bootimg_node, &data_stream, 1); if(ret <= 0) return(-1); ret= Xorriso_iso_file_read(xorriso, data_stream, (char *) buf, 68, 0); Xorriso_iso_file_close(xorriso, &data_stream, 0); if(ret <= 0) return(0); if(buf[64] == 0xfb && buf[65] == 0xc0 && buf[66] == 0x78 && buf[67] == 0x70) return(1); return(0); } int Xorriso_image_has_md5(struct XorrisO *xorriso, int flag) { int ret; IsoImage *image; uint32_t start_lba, end_lba; char md5[16]; ret= Xorriso_get_volume(xorriso, &image, 0); if(ret<=0) return(ret); ret= iso_image_get_session_md5(image, &start_lba, &end_lba, md5, 0); Xorriso_process_msg_queues(xorriso,0); if(ret <= 0) return(0); return(1); } static const char *un0(const char *text) { if(text == NULL) return(""); return(text); } static int Xorriso_report_pvd_time(struct XorrisO *xorriso, char *head, char *pvd_time, int flag) { char *msg, hr[17]; int at; msg= xorriso->result_line; strncpy(hr, pvd_time, 16); hr[16]= 0; sprintf(msg, "%s %s\n", head, hr); Xorriso_result(xorriso,0); if(pvd_time[16] != 0) { at= abs(pvd_time[16]); sprintf(msg, "%2.2s. Time Zone: %c%-2.2d:%-2.2d\n", head, pvd_time[16] > 0 ? '+' : '-', at / 4, (at - (at / 4) * 4) * 15); Xorriso_result(xorriso,0); } return(1); } int Xorriso_pvd_info(struct XorrisO *xorriso, int flag) { int ret; off_t msc1= -1, msc2, i; IsoImage *image; struct burn_drive_info *dinfo; struct burn_drive *drive; char *msg, block_head[8], *crt, *mdt, *ext, *eft; off_t head_count; msg= xorriso->result_line; ret= Xorriso_get_volume(xorriso, &image, 0); if(ret<=0) return(ret); ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "", 16); if(ret > 0) { ret= Xorriso_msinfo(xorriso, &msc1, &msc2, 1 | 4); if(ret<0) return(ret); Xorriso_toc(xorriso, 128); if(msc1 >= 0) { for(i = msc1 + 16; i < msc1 + 32; i++) { ret= burn_read_data(drive, i * (off_t) 2048, block_head, (off_t) sizeof(block_head), &head_count, 2); if(ret <= 0) { i= msc1 + 32; break; } if(block_head[0] == 1 && strncmp(block_head + 1, "CD001", 5) == 0) break; } if(i < msc1 + 32) { sprintf(msg, "PVD address : %.fs\n", (double) i); Xorriso_result(xorriso,0); } } } sprintf(msg, "Volume Id : %s\n", un0(iso_image_get_volume_id(image))); Xorriso_result(xorriso,0); sprintf(msg, "Volume Set Id: %s\n", xorriso->volset_id); Xorriso_result(xorriso,0); sprintf(msg, "Publisher Id : %s\n", xorriso->publisher); Xorriso_result(xorriso,0); sprintf(msg, "Preparer Id : %s\n", un0(iso_image_get_data_preparer_id(image))); Xorriso_result(xorriso,0); sprintf(msg, "App Id : %s\n", xorriso->application_id); Xorriso_result(xorriso,0); sprintf(msg, "System Id : %s\n", xorriso->system_id); Xorriso_result(xorriso,0); sprintf(msg, "CopyrightFile: %s\n", xorriso->copyright_file); Xorriso_result(xorriso,0); sprintf(msg, "Abstract File: %s\n", xorriso->abstract_file); Xorriso_result(xorriso,0); sprintf(msg, "Biblio File : %s\n", xorriso->biblio_file); Xorriso_result(xorriso,0); ret= iso_image_get_pvd_times(image, &crt, &mdt, &ext, &eft); if(ret != ISO_SUCCESS) { crt= mdt= ext= eft= " "; /* Need 17 bytes. Last byte 0. */ } else { Untimezone_ecma119_17byte(crt, 0); Untimezone_ecma119_17byte(mdt, 0); Untimezone_ecma119_17byte(ext, 0); Untimezone_ecma119_17byte(eft, 0); } Xorriso_report_pvd_time(xorriso, "Creation Time:", crt, 0); Xorriso_report_pvd_time(xorriso, "Modif. Time :", mdt, 0); Xorriso_report_pvd_time(xorriso, "Expir. Time :", ext, 0); Xorriso_report_pvd_time(xorriso, "Eff. Time :", eft, 0); return(1); } /* @param flag bit0= do not mark image as changed */ int Xorriso_set_volid(struct XorrisO *xorriso, char *volid, int flag) { int ret; IsoImage *volume; if(xorriso->in_volset_handle == NULL) return(2); ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) return(ret); if(iso_image_get_volume_id(volume) == NULL || strcmp(iso_image_get_volume_id(volume), volid) != 0) if(!(flag&1)) Xorriso_set_change_pending(xorriso, 1); iso_image_set_volume_id(volume, volid); Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text,"Volume ID: '%s'",iso_image_get_volume_id(volume)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); return(1); } int Xorriso_get_volid(struct XorrisO *xorriso, char volid[33], int flag) { int ret; IsoImage *volume; ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) return(ret); strncpy(volid, iso_image_get_volume_id(volume), 32); volid[32]= 0; return(1); } /* bit0= do only report non-default settings bit1= do only report to fp bit2= is_default bit3= append -boot_image any next bit4= concentrate boot options bit5= override load_size by "full" */ int Xorriso_boot_item_status(struct XorrisO *xorriso, char *cat_path, char *bin_path, int platform_id, int patch_isolinux, int emul, off_t load_size, unsigned char *id_string, unsigned char *selection_crit, char *form, char *filter, FILE *fp, int flag) { int is_default, no_defaults, i, is_default_id= 0, ret; char *line, *bspec= NULL, zeros[28], *partition_entry; off_t file_size; struct stat stbuf; Xorriso_alloc_meM(bspec, char, SfileadrL + 80); no_defaults= flag & 1; line= xorriso->result_line; if(flag & 32) load_size= -1; if((flag & 16) && bin_path[0] != 0) { /* Concentrate boot options. */ memset(zeros, 0, 28); if(memcmp(id_string, zeros, 28) == 0 && memcmp(selection_crit, zeros, 20) == 0) is_default_id= 1; /* -boot_image isolinux dir= ... */ bspec[0]= 0; if(strcmp(form, "isolinux") != 0 && strcmp(form, "any") != 0) ; else if(strcmp(bin_path, "/isolinux.bin") == 0 && strcmp(cat_path, "/boot.cat") == 0) strcpy(bspec, "dir=/"); else if(strcmp(bin_path, "/isolinux/isolinux.bin") == 0 && strcmp(cat_path, "/isolinux/boot.cat") == 0) strcpy(bspec, "dir=/isolinux"); else if(strcmp(xorriso->boot_image_bin_path, "/boot/isolinux/isolinux.bin") == 0 && strcmp(xorriso->boot_image_cat_path, "/boot/isolinux/boot.cat") == 0) strcpy(bspec, "dir=/boot/isolinux"); memset(zeros, 0, 28); if(bspec[0] && platform_id == 0 && (patch_isolinux & 0x3ff) == 1 && load_size == 2048 && is_default_id && emul == 0) { sprintf(line, "-boot_image isolinux %s\n", bspec); Xorriso_status_result(xorriso,filter,fp,flag&2); {ret= 1; goto ex;}; } file_size= 0; if(strncmp(bin_path, "--interval:appended_partition_", 30) == 0) { ret= -1; } else { ret= Xorriso_iso_lstat(xorriso, bin_path, &stbuf, 2 | 4); } if(ret == 0) { file_size= ((stbuf.st_size / (off_t) 512) + !!(stbuf.st_size % (off_t) 512)) * 512; if(flag & 32) load_size= file_size * 512; } if(platform_id == 0xef && (patch_isolinux & 0x3ff) == 0 && load_size / 512 == file_size && is_default_id && emul == 0) { sprintf(line, "-boot_image any efi_path="); Text_shellsafe(bin_path, line, 1); strcat(line, "\n"); Xorriso_status_result(xorriso,filter,fp,flag&2); {ret= 1; goto ex;}; } } is_default= (bin_path[0] == 0) || (flag & 4); sprintf(line, "-boot_image %s bin_path=", form); Text_shellsafe(bin_path, line, 1); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (emul == 0); sprintf(line, "-boot_image %s emul_type=%s\n", form, emul == 2 ? "diskette" : emul == 1 ? "hard_disk" : "no_emulation"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (platform_id == 0 || (flag & 4)); sprintf(line, "-boot_image %s platform_id=0x%-2.2x\n", form, platform_id); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= ((patch_isolinux & 1) == 0 || bin_path[0] == 0 || (flag & 4)); sprintf(line, "-boot_image %s boot_info_table=%s\n", (patch_isolinux & 2) ? "grub" : form, (patch_isolinux & 1) ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= ((patch_isolinux & 512) == 0 || bin_path[0] == 0 || (flag & 4)); sprintf(line, "-boot_image grub grub2_boot_info=%s\n", (patch_isolinux & 512) ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); if(flag & 32) { is_default= 0; sprintf(line, "-boot_image %s load_size=full", form); } else { is_default= (load_size == 2048 || (flag & 4)); sprintf(line, "-boot_image %s load_size=%lu\n", form, (unsigned long) load_size); } if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= 1; if(!(flag & 4)) for(i= 0; i < 20; i++) if(selection_crit[i]) is_default= 0; sprintf(line, "-boot_image %s sel_crit=", form); for(i= 0; i < 20; i++) sprintf(line + strlen(line), "%-2.2X", (unsigned int) selection_crit[i]); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= 1; if(!(flag & 4)) for(i= 0; i < 28; i++) if(id_string[i]) is_default= 0; sprintf(line, "-boot_image %s id_string=", form); for(i= 0; i < 28; i++) sprintf(line + strlen(line), "%-2.2X", (unsigned int) id_string[i]); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= 1; partition_entry= ""; if((patch_isolinux & 0x0fc) == (1 << 2)) partition_entry= "gpt_basdat"; else if((patch_isolinux & 0x0fc) == (2 << 2)) partition_entry= "gpt_hfsplus"; if(partition_entry[0]) { sprintf(line, "-boot_image isolinux partition_entry=%s\n", partition_entry); Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= 0; } if(patch_isolinux & (1 << 8)) { sprintf(line, "-boot_image isolinux partition_entry=apm_hfsplus\n"); Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= 0; } if(is_default && !no_defaults) { sprintf(line, "-boot_image isolinux partition_entry=off\n"); Xorriso_status_result(xorriso, filter, fp, flag & 2); } ret= 1; ex:; Xorriso_free_meM(bspec); return(ret); } int Xorriso_status_hppa(struct XorrisO *xorriso, char *what, char *value, char *filter, FILE *fp, int flag) { char *line; line= xorriso->result_line; if(value == NULL) return(1); sprintf(line, "-boot_image any hppa_%s=", what); Text_shellsafe(value, line, 1); strcat(line, "\n"); Xorriso_status_result(xorriso, filter, fp, flag & 2); return(1); } /* bit0= do only report non-default settings bit1= do only report to fp */ int Xorriso_boot_status_non_mbr(struct XorrisO *xorriso, IsoImage *image, char *filter, FILE *fp, int flag) { int i, num_boots, sa_type; char *paths[15], *line; int ret; char num[4]; char *cmdline, *bootloader, *kernel_32, *kernel_64, *ramdisk; line= xorriso->result_line; sa_type= (xorriso->system_area_options & 0xfc) >> 2; if(sa_type == 3) { sprintf(line, "-boot_image any sparc_label="); Text_shellsafe(xorriso->ascii_disc_label, line, 1); strcat(line, "\n"); Xorriso_status_result(xorriso, filter, fp, flag & 2); sprintf(line, "-boot_image grub grub2_sparc_core="); Text_shellsafe(xorriso->grub2_sparc_core, line, 1); strcat(line, "\n"); Xorriso_status_result(xorriso, filter, fp, flag & 2); return(0); } else if(sa_type == 1 || sa_type == 2) { num_boots= iso_image_get_mips_boot_files(image, paths, 0); Xorriso_process_msg_queues(xorriso, 0); if(num_boots > 0) { if(sa_type == 2) num_boots= 1; for(i= 0; i < num_boots; i++) { sprintf(line, "-boot_image any mips%s_path=", sa_type ==2 ? "el" : ""); Text_shellsafe(paths[i], line, 1); strcat(line, "\n"); Xorriso_status_result(xorriso, filter, fp, flag & 2); } } return(num_boots); } else if(sa_type == 4 || sa_type == 5) { ret= iso_image_get_hppa_palo(image, &cmdline, &bootloader, &kernel_32, &kernel_64, &ramdisk); if(ret == 1) { Xorriso_status_hppa(xorriso, "cmdline", cmdline, filter, fp, 0); Xorriso_status_hppa(xorriso, "bootloader", bootloader, filter, fp, 0); Xorriso_status_hppa(xorriso, "kernel_32", kernel_32, filter, fp, 0); Xorriso_status_hppa(xorriso, "kernel_64", kernel_64, filter, fp, 0); Xorriso_status_hppa(xorriso, "ramdisk", ramdisk, filter, fp, 0); sprintf(num, "%d", sa_type); Xorriso_status_hppa(xorriso, "hdrversion", num, filter, fp, 0); } return(0); } else if(sa_type == 6) { ret= iso_image_get_alpha_boot(image, &bootloader); if (ret == 1 && bootloader != NULL) { sprintf(line, "-boot_image any alpha_boot="); Text_shellsafe(bootloader, line, 1); strcat(line, "\n"); Xorriso_status_result(xorriso, filter, fp, flag & 2); } return(0); } return(0); } /* bit0= do only report non-default settings bit1= do only report to fp */ int Xorriso_append_part_status(struct XorrisO *xorriso, IsoImage *image, char *filter, FILE *fp, int flag) { int i, l, is_default; is_default= (xorriso->appended_as_gpt == 0); sprintf(xorriso->result_line, "-boot_image any appended_part_as=%s\n", xorriso->appended_as_gpt ? "gpt" : "mbr"); if(!(is_default && (flag & 1))) Xorriso_status_result(xorriso, filter, fp, flag & 2); for(i= 0; i < Xorriso_max_appended_partitionS; i++) { if(xorriso->appended_partitions[i] == NULL) continue; sprintf(xorriso->result_line, "-append_partition %d ", i + 1); l= strlen(xorriso->result_line); if(xorriso->appended_part_gpt_flags[i] & 1) { Xorriso__format_guid(xorriso->appended_part_type_guids[i], xorriso->result_line + l, 0); strcpy(xorriso->result_line + l + 32, " "); } else { sprintf(xorriso->result_line + l, "0x%2.2x ", (unsigned int) xorriso->appended_part_types[i]); } Text_shellsafe(xorriso->appended_partitions[i], xorriso->result_line, 1); strcat(xorriso->result_line, "\n"); Xorriso_status_result(xorriso, filter, fp, flag & 2); } return(1); } /* bit0= do only report non-default settings bit1= do only report to fp */ int Xorriso_boot_image_status(struct XorrisO *xorriso, char *filter, FILE *fp, int flag) { int ret, i, num_boots, hflag; int is_default, no_defaults; char *path= NULL, *form= "any", *line, *hpt; struct burn_drive_info *dinfo; struct burn_drive *drive; IsoImage *image= NULL; ElToritoBootImage **boots = NULL; IsoFile **bootnodes = NULL; int platform_id, patch, load_size; enum eltorito_boot_media_type media_type; unsigned char id_string[29], sel_crit[21]; Xorriso_alloc_meM(path, char, SfileadrL); line= xorriso->result_line; no_defaults= flag & 1; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to print boot info", 2 | 16); if(ret<=0) goto no_image; image= isoburn_get_attached_image(drive); Xorriso_process_msg_queues(xorriso,0); if(image == NULL) goto no_image; ret= Xorriso_boot_status_non_mbr(xorriso, image, filter, fp, flag & 3); if(ret < 0) /* == 0 is normal */ {ret= 0; goto ex;} if(xorriso->boot_count == 0 && xorriso->boot_image_bin_path[0] == 0) { no_image:; if(xorriso->patch_isolinux_image & 1) { sprintf(line, "-boot_image %s patch\n", xorriso->patch_isolinux_image & 2 ? "grub" : form); is_default= 0; } else if(xorriso->keep_boot_image) { sprintf(line, "-boot_image %s keep\n", form); is_default= 0; } else { sprintf(line, "-boot_image %s discard\n", form); is_default= 1; } if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); goto report_open_item; } is_default= (xorriso->boot_image_cat_path[0] == 0); sprintf(line,"-boot_image %s cat_path=", form); Text_shellsafe(xorriso->boot_image_cat_path, line, 1); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= !xorriso->boot_image_cat_hidden; hpt= Xorriso__hide_mode_text(xorriso->boot_image_cat_hidden & 63, 0); if(hpt != NULL) sprintf(line, "-boot_image %s cat_hidden=%s\n", form, hpt); Xorriso_free_meM(hpt); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); if(xorriso->boot_count > 0) { /* show attached boot image info */; ret= iso_image_get_all_boot_imgs(image, &num_boots, &boots, &bootnodes, 0); Xorriso_process_msg_queues(xorriso,0); if(ret == 1 && num_boots > 0) { for(i= 0; i < num_boots; i++) { ret= Xorriso_path_from_node(xorriso, (IsoNode *) bootnodes[i], path, 0); if(ret <= 0) continue; platform_id= el_torito_get_boot_platform_id(boots[i]); patch= el_torito_get_isolinux_options(boots[i], 0); el_torito_get_boot_media_type(boots[i], &media_type); load_size= el_torito_get_load_size(boots[i]) * 512; el_torito_get_id_string(boots[i], id_string); el_torito_get_selection_crit(boots[i], sel_crit); if(media_type == ELTORITO_FLOPPY_EMUL) media_type= 2; else if(media_type == ELTORITO_HARD_DISC_EMUL) media_type= 1; else media_type= 0; ret= Xorriso_boot_item_status(xorriso, xorriso->boot_image_cat_path, path, platform_id, patch, media_type, load_size, id_string, sel_crit, "any", filter, fp, 16 | (flag & 3)); if(ret <= 0) continue; sprintf(line,"-boot_image %s next\n", form); Xorriso_status_result(xorriso,filter,fp,flag&2); } } } /* Show pending boot image info */ if(strcmp(xorriso->boot_image_bin_form, "isolinux") == 0 || strcmp(xorriso->boot_image_bin_form, "grub") == 0) form= xorriso->boot_image_bin_form; if(xorriso->boot_count > 0 && xorriso->boot_platform_id == 0 && xorriso->patch_isolinux_image == 0 && xorriso->boot_image_bin_path[0] == 0 && xorriso->boot_image_emul == 0 && xorriso->boot_image_load_size == 4 * 512) { for(i= 0; i < 20; i++) if(xorriso->boot_selection_crit[i]) break; if(i >= 20) for(i= 0; i < 28; i++) if(xorriso->boot_id_string[i]) break; if(i >= 28) {ret= 1; goto ex;} /* Images registered, pending is still default */ } report_open_item:; hflag= 16; if(xorriso->boot_platform_id == 0xef && !xorriso->boot_efi_default) hflag= 0; ret= Xorriso_boot_item_status(xorriso, xorriso->boot_image_cat_path, xorriso->boot_image_bin_path, xorriso->boot_platform_id, xorriso->patch_isolinux_image, xorriso->boot_image_emul, xorriso->boot_image_load_size, xorriso->boot_id_string, xorriso->boot_selection_crit, form, filter, fp, hflag | (flag & 3)); if(ret <= 0) goto ex; ret = Xorriso_append_part_status(xorriso, image, filter, fp, flag & 3); if(ret <= 0) goto ex; ret= 1; ex: if(boots != NULL) free(boots); if(bootnodes != NULL) free(bootnodes); if(image != NULL) iso_image_unref(image); Xorriso_free_meM(path); return(ret); } int Xorriso__append_boot_params(char *line, ElToritoBootImage *bootimg, int flag) { unsigned int platform_id; platform_id= el_torito_get_boot_platform_id(bootimg); if(platform_id != 0) sprintf(line + strlen(line), " , platform_id=0x%-2.2X ", (unsigned int) platform_id); if(el_torito_seems_boot_info_table(bootimg, 0)) sprintf(line + strlen(line), " , boot_info_table=on"); if(el_torito_seems_boot_info_table(bootimg, 1)) sprintf(line + strlen(line), " , grub2_boot_info=on"); return(1); } /* @param flag bit0= no output if no boot record was found bit1= short form bit3= report to info channel (else to result channel) */ int Xorriso_show_boot_info(struct XorrisO *xorriso, int flag) { int ret, bin_path_valid= 0, i, num_boots, sa_count; char *respt, *path= NULL, **sa_report= NULL, *sa_summary= NULL; unsigned char *lb0= NULL; struct burn_drive_info *dinfo; struct burn_drive *drive; IsoImage *image= NULL; ElToritoBootImage *bootimg, **boots = NULL; IsoFile *bootimg_node, **bootnodes = NULL; IsoBoot *bootcat_node; Xorriso_alloc_meM(path, char, SfileadrL); Xorriso_alloc_meM(lb0, unsigned char, 2048); respt= xorriso->result_line; if(xorriso->boot_count > 0) { if(!(flag & 1)) { sprintf(respt, "Boot record : (overridden by -boot_image any next)\n"); Xorriso_toc_line(xorriso, flag & 8); } ret= 1; goto ex; } ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to print boot info", 16); if(ret<=0) goto no_boot; image= isoburn_get_attached_image(drive); if(image == NULL) { ret= 0; no_boot:; if(!(flag & 1)) { sprintf(respt, "Boot record : none\n"); Xorriso_toc_line(xorriso, flag & 8); } goto ex; } ret= iso_image_report_system_area(image, &sa_report, &sa_count, 0); if(ret > 0 && sa_report != NULL) for(i= 0; i < sa_count; i++) if(strncmp(sa_report[i], "System area summary: ", 21) == 0) { Xorriso_alloc_meM(sa_summary, char, strlen(sa_report[i] + 21) + 1); strcpy(sa_summary, sa_report[i] + 21); break; } if(sa_report != NULL) iso_image_report_system_area(image, &sa_report, &sa_count, 1 << 15); Xorriso_process_msg_queues(xorriso,0); /* Using the nodes with extreme care . They might be deleted meanwhile. */ ret= iso_image_get_boot_image(image, &bootimg, &bootimg_node, &bootcat_node); if(ret != 1) { if(sa_summary == NULL) goto no_boot; sprintf(respt, "Boot record : (system area only) , %s\n", sa_summary); Xorriso_toc_line(xorriso, flag & 8); ret= 1; goto ex; } ret= iso_image_get_all_boot_imgs(image, &num_boots, &boots, &bootnodes, 0); Xorriso_process_msg_queues(xorriso,0); if(ret != 1) { num_boots= 0; } else { ret= Xorriso_path_from_node(xorriso, (IsoNode *) bootnodes[0], path, 0); if(ret > 0) bin_path_valid= 1; } sprintf(respt, "Boot record : El Torito"); if(sa_summary != NULL) sprintf(respt + strlen(respt), " , %s", sa_summary); strcat(respt, "\n"); Xorriso_toc_line(xorriso, flag & 8); if(flag & 2) {ret= 1; goto ex;} if(xorriso->loaded_boot_cat_path[0]) { sprintf(respt, "Boot catalog : "); Text_shellsafe(xorriso->loaded_boot_cat_path, respt, 1); strcat(respt, "\n"); } else { sprintf(respt, "Boot catalog : -not-found-at-load-time-\n"); } Xorriso_toc_line(xorriso, flag & 8); if(bin_path_valid) { sprintf(respt, "Boot image : "); Text_shellsafe(path, respt, 1); } else if(xorriso->loaded_boot_bin_lba <= 0) { sprintf(respt, "Boot image : -not-found-at-load-time-"); } else { sprintf(respt, "Boot image : -not-found-any-more-by-lba=%.f", (double) xorriso->loaded_boot_bin_lba); } Xorriso__append_boot_params(respt, bootimg, 0); strcat(respt, "\n"); Xorriso_toc_line(xorriso, flag & 8); if(num_boots > 1) { for(i= 1; i < num_boots; i++) { ret= Xorriso_path_from_node(xorriso, (IsoNode *) bootnodes[i], path, 0); if(ret > 0) { sprintf(respt, "Boot image : "); Text_shellsafe(path, respt, 1); } else sprintf(respt, "Boot image : -not-found-any-more-"); Xorriso__append_boot_params(respt, boots[i], 0); strcat(respt, "\n"); Xorriso_toc_line(xorriso, flag & 8); } } ret= 1; ex:; if(boots != NULL) free(boots); if(bootnodes != NULL) free(bootnodes); if(image != NULL) iso_image_unref(image); /* release obtained reference */ Xorriso_free_meM(path); Xorriso_free_meM(lb0); Xorriso_free_meM(sa_summary); return(ret); } /* @param flag bit0=silently return 0 if no volume/image is present */ int Xorriso_get_volume(struct XorrisO *xorriso, IsoImage **volume, int flag) { *volume= NULL; if(xorriso->in_volset_handle==NULL) { if(flag & 1) return(0); Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text,"No ISO image present."); if(xorriso->indev[0]==0 && xorriso->outdev[0]==0) sprintf(xorriso->info_text+strlen(xorriso->info_text), " No -dev, -indev, or -outdev selected."); else sprintf(xorriso->info_text+strlen(xorriso->info_text), " Possible program error with drive '%s'.", xorriso->indev); if(!xorriso->no_volset_present) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); xorriso->no_volset_present= 1; return(0); } *volume= (IsoImage *) xorriso->in_volset_handle; xorriso->no_volset_present= 0; return(*volume != NULL); } /* @param flag bit0= do not return 1 on volset_change_pending != 1 bit1= issue NOTE if return is 0, indev and outdev point to different drives, and no write run or size run has happened */ int Xorriso_change_is_pending(struct XorrisO *xorriso, int flag) { int ret; if(flag & 1) ret= (xorriso->volset_change_pending == 1); else ret= !!xorriso->volset_change_pending; if((flag & 2) && xorriso->volset_change_pending == 0 && xorriso->commit_attempts <= 0 && xorriso->print_size_attempts <= 0 && xorriso->write_session_counter <= 0 && xorriso->print_size_counter <= 0) { if(xorriso->indev[0] != 0 && xorriso->outdev[0] != 0 && strcmp(xorriso->indev, xorriso->outdev) != 0) { Xorriso_msgs_submit(xorriso, 0, "-indev and -outdev differ. But no pending image modifications.", 0, "NOTE", 0); } } return(ret); } /* @param flag bit0= do not set hln_change_pending */ int Xorriso_set_change_pending(struct XorrisO *xorriso, int flag) { int ret; IsoImage *image; ret= Xorriso_get_volume(xorriso, &image, 1); if(ret <= 0) return ret; /* Do not override mark of -as mkisofs -print-size */ if(xorriso->volset_change_pending != 2) xorriso->volset_change_pending= 1; if(!(flag & 1)) xorriso->hln_change_pending= 1; return(1); } /** @param flag bit0= print mount command to result channel rather than performing it bit1= do not allow prefixes with cmd bit2= interpret unprefixed cmd as shell: */ int Xorriso_mount(struct XorrisO *xorriso, char *dev, int adr_mode, char *adr_value, char *cmd, int flag) { int ret, track, session, params_flag= 0, is_safe= 0, is_extra_drive= 0; int give_up= 0, mount_chardev= 0, status, aquire_flag= 0; off_t lba; char volid[33], *devadr, *mount_command= NULL, *adr_data= NULL, *adr_pt; char *dev_path, *libburn_adr= NULL; char *dpt, *sysname= ""; struct stat stbuf; struct burn_drive_info *dinfo= NULL; struct burn_drive *drive= NULL; Xorriso_alloc_meM(mount_command, char, SfileadrL); Xorriso_alloc_meM(adr_data, char, 163); Xorriso_alloc_meM(libburn_adr, char, BURN_DRIVE_ADR_LEN + SfileadrL); devadr= dev; adr_pt= adr_value; if(strcmp(dev, "indev") == 0) { ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to perform -mount \"indev\"", 0); if(ret<=0) goto ex; dev_path= devadr= xorriso->indev; if(strncmp(dev_path, "stdio:", 6) == 0) dev_path+= 6; else if(strncmp(dev_path, "mmc:", 4) == 0) dev_path+= 4; if(xorriso->in_drive_handle == xorriso->out_drive_handle) give_up= 3; else give_up= 1; } else if(strcmp(dev, "outdev") == 0) { ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to perform -mount \"outdev\"", 2); if(ret<=0) goto ex; dev_path= devadr= xorriso->outdev; if(strncmp(dev_path, "stdio:", 6) == 0) dev_path+= 6; else if(strncmp(dev_path, "mmc:", 4) == 0) dev_path+= 4; if(xorriso->in_drive_handle == xorriso->out_drive_handle) give_up= 3; else give_up= 2; } else { is_extra_drive= 1; dev_path= dev; if(strncmp(dev_path, "stdio:", 6) == 0) dev_path+= 6; else if(strncmp(dev_path, "mmc:", 4) == 0) dev_path+= 4; /* do only accept regular files and block devices */ ret= stat(dev_path, &stbuf); if(ret == -1) { sprintf(xorriso->info_text, "Cannot determine properties of file "); Text_shellsafe(dev_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } ret= System_uname(&sysname, NULL, NULL, NULL, 0); if(ret > 0 && strcmp(sysname, "FreeBSD") == 0) mount_chardev= 1; if(!(S_ISREG(stbuf.st_mode) || (S_ISBLK(stbuf.st_mode) && !mount_chardev) || (S_ISCHR(stbuf.st_mode) && !mount_chardev))) { sprintf(xorriso->info_text, "File object is not suitable as mount device: "); Text_shellsafe(dev_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } /* Acquire drive as direct libburn address or via stdio: prefix */ if(strncmp(dev, "mmc:", 4) == 0) ret= burn_drive_convert_fs_adr(dev + 4, libburn_adr); else ret= burn_drive_convert_fs_adr(dev, libburn_adr); Xorriso_process_msg_queues(xorriso,0); if(ret < 0) {ret= -1; goto ex;} if(ret == 0 && strncmp(dev, "stdio:", 6) != 0 && strncmp(dev, "mmc:", 4) != 0) sprintf(libburn_adr, "stdio:%s", dev); burn_preset_device_open( (xorriso->drives_exclusive && !(xorriso->mount_opts_flag & 1)) | (xorriso->linux_scsi_dev_family << 2), 0, 0); aquire_flag= 1 | 32 | 64; if((xorriso->toc_emulation_flag & 2) && (adr_mode == 3 || adr_mode == 0)) aquire_flag|= 16; /* -rom_toc_scan emul_off with sbsector or auto */ if(xorriso->toc_emulation_flag & 4) aquire_flag|= 128; if(xorriso->toc_emulation_flag & 8) aquire_flag|= 512; ret= isoburn_drive_aquire(&dinfo, libburn_adr, aquire_flag); burn_preset_device_open(1 | (xorriso->linux_scsi_dev_family << 2), 0, 0); Xorriso_process_msg_queues(xorriso,0); if(ret <= 0) {ret= 0; goto ex;} drive= dinfo[0].drive; } ret= Xorriso_prepare_load_search(xorriso, "-mount", adr_mode, adr_pt, adr_data, ¶ms_flag, 0); if(ret <= 0) goto ex; adr_pt= adr_data; ret= isoburn_get_mount_params_v2(drive, adr_mode, adr_pt, &lba, &track, &session, volid, params_flag); Xorriso_process_msg_queues(xorriso,0); if(ret <= 0) goto ex; if(((session <= 0 || track <= 0) && !(aquire_flag & 16)) || ret == 2) { Xorriso_msgs_submit(xorriso, 0, "-mount : Given address does not point to an ISO 9660 session", 0, "FAILURE", 0); ret= 0; goto ex; } if(strstr(devadr, "stdio:") == devadr) devadr+= 6; if(strstr(devadr, "mmc:") == devadr) devadr+= 4; ret= Xorriso_make_mount_cmd(xorriso, cmd, lba, track, session, volid, devadr, mount_command, (flag & (2 | 4)) | ((flag & 4) << 1)); if(ret <= 0) goto ex; if(ret == 2) is_safe= 1; if(is_extra_drive) { isoburn_drive_release(drive, 0); burn_drive_info_free(dinfo); drive= NULL; } else if(give_up > 0 && !((flag & 1) || (xorriso->mount_opts_flag & 1))) { ret= Xorriso_give_up_drive(xorriso, give_up); if(ret <= 0) goto ex; } Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "Volume id : "); Text_shellsafe(volid, xorriso->info_text, 1); strcat(xorriso->info_text, "\n"); Xorriso_info(xorriso, 0); if(flag & 1) { sprintf(xorriso->result_line, "%s\n", mount_command); Xorriso_result(xorriso,0); } else { sprintf(xorriso->info_text, "Mount command: %s\n", mount_command); Xorriso_info(xorriso, 0); if(!is_safe) { Xorriso_msgs_submit(xorriso, 0, "-mount : Will not perform mount command which stems from command template.", 0, "SORRY", 0); sprintf(xorriso->result_line, "%s\n", mount_command); Xorriso_result(xorriso,0); } else { ret= Xorriso_execv(xorriso, mount_command, 0, NULL, "/bin:/sbin", NULL, NULL, NULL, &status, 1); if(WIFEXITED(status) && WEXITSTATUS(status) != 0) { sprintf(xorriso->info_text, "-mount : mount command failed with exit value %d", (int) WEXITSTATUS(status)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } sprintf(xorriso->info_text, "\nMounted session %d of device ", session); Text_shellsafe(dev_path, xorriso->info_text, 1); dpt= strchr(cmd, ':'); if(dpt == NULL) dpt= cmd ; else dpt++; sprintf(xorriso->info_text + strlen(xorriso->info_text), " as directory "); Text_shellsafe(dpt, xorriso->info_text, 1); strcat(xorriso->info_text, "\n"); Xorriso_info(xorriso, 0); } } ret= 1; ex:; if(is_extra_drive && drive != NULL) { isoburn_drive_release(drive, 0); burn_drive_info_free(dinfo); Xorriso_process_msg_queues(xorriso,0); } Xorriso_free_meM(mount_command); Xorriso_free_meM(adr_data); Xorriso_free_meM(libburn_adr); return(ret); } /* @param flag bit0= give up all boot file paths bit1= refuse if already a path is added */ int Xorriso_add_mips_boot_file(struct XorrisO *xorriso, char *path, int flag) { int ret; IsoImage *image; char *paths[15]; ret= Xorriso_get_volume(xorriso, &image, 0); if(ret <= 0) return ret; if(flag & 1) { iso_image_give_up_mips_boot(image, 0); Xorriso_process_msg_queues(xorriso,0); return(1); } if(flag & 2) { ret= iso_image_get_mips_boot_files(image, paths, 0); Xorriso_process_msg_queues(xorriso,0); if(ret < 0) goto report_error; if(ret > 0) { Xorriso_msgs_submit(xorriso, 0, "There is already a boot image file registered.", 0, "FAILURE", 0); return(0); } } ret = iso_image_add_mips_boot_file(image, path, 0); Xorriso_process_msg_queues(xorriso,0); if (ret < 0) { report_error:; Xorriso_report_iso_error(xorriso, "", ret, "Error when adding MIPS boot file", 0, "FAILURE", 1); return(0); } return(1); } /* @param flag bit0= Give up HP-PA boot parameters */ int Xorriso_set_hppa_boot_parm(struct XorrisO *xorriso, char *text, char *what, int flag) { int ret; IsoImage *image; char *par[5]; ret= Xorriso_get_volume(xorriso, &image, 0); if(ret <= 0) return(ret); par[0]= par[1]= par[2]= par[3]= par[4]= NULL; if(flag & 1) { /* Give up HP-PA boot parameters */ iso_image_set_hppa_palo(image, par[0], par[1], par[2], par[3], par[4], 1); return(1); } if(strcmp(what, "cmdline") == 0) { par[0]= text; } else if(strcmp(what, "bootloader") == 0) { par[1]= text; } else if(strcmp(what, "kernel_32") == 0 || strcmp(what, "kernel-32") == 0) { par[2]= text; } else if(strcmp(what, "kernel_64") == 0 || strcmp(what, "kernel-64") == 0) { par[3]= text; } else if(strcmp(what, "ramdisk") == 0) { par[4]= text; } else if(strcmp(what, "hdrversion") == 0) { if(strcmp(text, "4") == 0) { xorriso->system_area_options= (xorriso->system_area_options & ~0xfc) | (4 << 2); } else if(strcmp(text, "5") == 0) { xorriso->system_area_options= (xorriso->system_area_options & ~0xfc) | (5 << 2); } else { strcpy(xorriso->info_text, "Unsupported HP-PA PALO header version "); Text_shellsafe(text, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } else { strcpy(xorriso->info_text, "HP-PA boot parameter name not recognized: hppa_"); Text_shellsafe(what, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= iso_image_set_hppa_palo(image, par[0], par[1], par[2], par[3], par[4], 0); if (ret < 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when adding HP-PA boot parameter", 0, "FAILURE", 1); return(0); } return(1); } /* @param flag bit0= Give up DEC Alpha boot parameters */ int Xorriso_set_alpha_boot(struct XorrisO *xorriso, char *path, int flag) { int ret; IsoImage *image; ret= Xorriso_get_volume(xorriso, &image, 0); if(ret <= 0) return(ret); if(flag & 1) { /* Give up boot parameters */ iso_image_set_alpha_boot(image, NULL, 1); return(1); } ret= iso_image_set_alpha_boot(image, path, 0); if (ret < 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when adding DEC Alpha boot loader", 0, "FAILURE", 1); return(0); } return(1); } /* @param flag bit0= do not set xorriso->system_area_options, just check bit1= only check for grub2_mbr <-> isolinux partition_table */ int Xorriso_coordinate_system_area(struct XorrisO *xorriso, int sa_type, int options, char *cmd, int flag) { int old_type, old_options, new_options; static char *type_names[7] = { "MBR", "MIPS Big Endian Volume Header", "MIPS Little Endian Boot Block", "SUN Disk Label", "HP-PA PALO v4", "HP-PA PALO v5", "DEC Alpha SRM Boot Block"}; static int num_names = 7; old_type= (xorriso->system_area_options & 0xfc) >> 2; old_options= xorriso->system_area_options & 0x3c03; new_options= options & 0x3c03; if(((options & (1 << 14)) && (xorriso->system_area_options & 2)) || ((options & 2) && (xorriso->system_area_options & (1 << 14)))) goto reject; if(flag & 2) return(1); if((old_type != 0 || old_options != 0) && (old_type != sa_type || (old_options != 0 && old_options != new_options))){ reject:; sprintf(xorriso->info_text, "%s : First sector already occupied by %s", cmd, old_type < num_names ? type_names[old_type] : "other boot facility"); if(old_type == 0 && (old_options & 2)) strcat(xorriso->info_text, " for ISOLINUX isohybrid"); else if (old_type == 0 && (xorriso->system_area_options & (1 << 14))) { strcat(xorriso->info_text, " for GRUB2 patching"); if(old_type == 0 && (old_options & 1)) strcat(xorriso->info_text, " with partition table"); } else if(old_type == 0 && (old_options & 1)) strcat(xorriso->info_text, " for partition table"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto hint_revoke; } if(!(flag & 1)) xorriso->system_area_options= (xorriso->system_area_options & ~0x3cff) | ((sa_type << 2) & 0xfc) | (options & 0x3c03); return(1); hint_revoke:; if(old_type == 0) sprintf(xorriso->info_text, "Revokable by -boot_image any discard"); else if(old_type == 1 || old_type == 2) sprintf(xorriso->info_text, "Revokable by -boot_image any mips_discard"); else if(old_type == 3) sprintf(xorriso->info_text, "Revokable by -boot_image any sparc_discard"); if(old_type < 4) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); return(0); } int Xorriso_gpt_crc(struct XorrisO *xorriso, char *path, int flag) { int ret; char *buf = NULL; FILE *fp = NULL; uint32_t crc; Xorriso_alloc_meM(buf, char, 32 * 1024); ret= Xorriso_afile_fopen(xorriso, path, "rb", &fp, 0); if(ret <= 0) goto ex; ret= fread(buf, 1, 32 * 1024, fp); if(ret == 0) { strcpy(xorriso->info_text, "No bytes readable for GPT CRC from "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); ret= 0; goto ex; } crc= iso_crc32_gpt((unsigned char *) buf, ret, 0); sprintf(xorriso->result_line, "0x%8.8x\n", (unsigned int) crc); Xorriso_result(xorriso, 0); ret= 1; ex:; if(fp != NULL && fp != stdin) fclose(fp); Xorriso_free_meM(buf); return(ret); } static int Xorriso_split_report_line(struct XorrisO *xorriso, char *line, int num_limit, char *name, char **contentpt, double *num, int *num_count, char **textpt, int flag) { int i; char *spt, *ept, *cpt; if(strlen(line) < 21) { undigestible: sprintf(xorriso->info_text, "Undigestible report line with -report_* mode cmd: '%s'", line); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } if(line[19] != ':') goto undigestible; strncpy(name, line, 20); name[20]= 0; for(spt= line + 20; *spt == ' '; spt++); *textpt= *contentpt= spt; *num_count= 0; for(i= 0; i < num_limit; i++) { /* Get word */ for(spt= *textpt; *spt == ' '; spt++); if(*spt == 0) { *textpt= spt; break; } for(ept= spt + 1; *ept != ' ' && *ept != 0; ept++); /* Look for decimal number */ if(ept - spt > 16) break; for(cpt= spt; cpt < ept; cpt++) if(*cpt < '0' || *cpt > '9') break; if(cpt != ept) break; sscanf(spt, "%lf", num + *num_count); (*num_count)++; *textpt= ept; } /* Set *textpt to next non-blank */ for(; **textpt == ' '; (*textpt)++); return(1); } int Xorriso_record_cmd_line(struct XorrisO *xorriso, char *buf, char **cmds, int *cmd_count, int flag) { int ret; if(flag & 1) { (*cmd_count)++; ret= 1; goto ex; } Xorriso_alloc_meM(cmds[*cmd_count], char, strlen(buf) + 1); strcpy(cmds[*cmd_count], buf); (*cmd_count)++; ret= 1; ex:; return(ret); } /* @param flag bit0= zeroize MBR partition table bit1= zeroize GPT bit2= zeroize APM bit30= Source imported_iso rather than local_fs */ int Xorriso_add_intvl_adr(struct XorrisO *xorriso, char *buf, uint64_t start_adr, uint64_t end_adr, char *suffix, int flag) { char *path; sprintf(buf + strlen(buf), "--interval:%s:%.f%s-%.f%s:", ((flag & (1 << 30)) ? "imported_iso" : "local_fs"), (double) start_adr, suffix, (double) end_adr, suffix); if(flag & 1) strcat(buf, "zero_mbrpt,"); if(flag & 2) strcat(buf, "zero_gpt,"); if(flag & 4) strcat(buf, "zero_apm,"); if(buf[strlen(buf) - 1] == ',') buf[strlen(buf) - 1] = 0; strcat(buf, ":"); path= xorriso->indev; if(strncmp(path, "stdio:", 6) == 0) path+= 6; Text_shellsafe(path, buf, 1); return(1); } int Xorriso_add_offset_size(struct XorrisO *xorriso, char *buf, off_t byte_offset, off_t byte_size, int flag) { strcat(buf, " "); Sfile_off_t_text(buf + strlen(buf), byte_offset, 0); strcat(buf, " "); Sfile_off_t_text(buf + strlen(buf), byte_size, 0); return(1); } struct elto_img_par { int n, ldsiz, boot_info_table, grub2_boot_info; int do_gpt_basdat, do_gpt_hfsplus, do_apm_hfsplus; unsigned int ld_seg, hdpt, platform_id; unsigned long int lba, extract_size; char pltf[8], b[8], emul[8], boot_image_type[16]; char *path, *id_string, *sel_crit; int path_allocated; }; /* @param ptype 0= unknown, 1= gpt-basdat, 2=gpt-hfsplus, 3=EFI @param flag bit0= isohybrid */ static int Xorriso_register_eltorito_gpt(struct XorrisO *xorriso, struct elto_img_par *et_img, int ptype, int *efi_boot_part, int *first_efi, int flag) { if(flag & 1) { if(ptype == 1 || ptype == 3) et_img->do_gpt_basdat= ptype; else if(ptype == 2) et_img->do_gpt_hfsplus= 1; return(1); } else if(*first_efi && et_img->platform_id == 0xef) { if(*efi_boot_part <= 0) *efi_boot_part= 1; return(1); } if(et_img->platform_id == 0xef) *first_efi= 0; return(0); } /* @param ptype 0= unknown, 1= gpt-basdat, 2=gpt-hfsplus, 3=EFI @param flag bit0= isohybrid */ static int Xorriso_search_eltorito_path(struct XorrisO *xorriso, struct elto_img_par *et_imgs, int elto_count, char *path, int ptype, int *found, int *efi_boot_part, int flag) { int first_efi= 1, et_idx, ret; for(et_idx= 0; et_idx < elto_count; et_idx++) { if(strcmp(et_imgs[et_idx].path, path) != 0) continue; ret= Xorriso_register_eltorito_gpt(xorriso, et_imgs + et_idx, ptype, efi_boot_part, &first_efi, flag); if(ret > 0) break; } *found= et_idx; if(et_idx < elto_count) return(1); return(0); } static int Xorriso_search_eltorito_lba(struct XorrisO *xorriso, struct elto_img_par *et_imgs, int elto_count, unsigned int lba, int *found, int flag) { int et_idx; for(et_idx= 0; et_idx < elto_count; et_idx++) if(et_imgs[et_idx].lba == lba) break; *found= et_idx; if(et_idx < elto_count) return(1); return(0); } int Xorriso_highest_data_block(struct XorrisO *xorriso, uint32_t *high_block, int flag) { int ret; struct FindjoB *job= NULL; struct stat dir_stbuf; *high_block= 0; ret= Findjob_new(&job, "/", 0); if(ret <= 0) { Xorriso_no_findjob(xorriso, "[internal:last_data_file_block]", 0); {ret= -1; goto ex;} } Findjob_set_action_type(job, 58, 0, 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, "/", &dir_stbuf, 0, 0); if(ret <= 0) goto ex; Findjob_get_last_data_file_block(job, high_block, 0); ex:; Findjob_destroy(&job, 0); return(ret); } /* Note: These macros use local variables of Xorriso_scan_report_lines() and Xorriso_make_boot_cut_out(). */ #define Xorriso_record_cmd_linE { \ ret= Xorriso_record_cmd_line(xorriso, buf, cmds, cmd_count, flag & 1); \ buf[0]= 0; \ if(ret <= 0) \ goto ex; \ } #define Xorriso_record_boot_imglinE { \ ret= Xorriso_record_cmd_line(xorriso, buf, boot_imgs, boot_img_count, \ flag & 1); \ buf[0]= 0; \ if(ret <= 0) \ goto ex; \ } /* @param flag bit0= do not record but only count bit1= as_mkisofs bit2= no sorry messages */ int Xorriso_make_boot_cut_out(struct XorrisO *xorriso, unsigned long lba, unsigned long blocks, char **cmds, int *cmd_count, char *buf, int buf_size, char **path, int flag) { int ret= 0, i; char uuid_path[37 + 13]; IsoNode *node; /* Choose a path "hidden_boot_$UUID" which must not exist yet */ for(i = 0; i < 10; i++) { strcpy(uuid_path, "/hidden_boot_"); ret = Xorriso_make_guid(xorriso, uuid_path + 13, 1); if(ret <= 0) goto ex; ret= Xorriso_get_node_by_path(xorriso, uuid_path, NULL, &node, 1); if(ret <= 0) break; /* wait a short time just in case that Xorriso_make_guid() relies on time */ usleep(12345); } if(i >= 10) { if(!(flag & 5)) { sprintf(xorriso->info_text, "Composition of -cut_out command failed because no unused file path can be found"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } ret= 0; goto ex; } /* Issue command or option for cutting out from indev */ strcpy(buf, "-cut_out "); if(strlen(buf) + strlen(xorriso->indev) * 5 + 1 > (unsigned long) buf_size) { buffer_overflow: if(!(flag & 5)) { sprintf(xorriso->info_text, "Composition of -cut_out command failed because it becomes too long"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } ret= 0; goto ex; } Text_shellsafe(xorriso->indev, buf, 1); if(strlen(buf) + 1 + 11 + 1 + 11 + 1 + strlen(uuid_path) + 1 > (unsigned long) buf_size) goto buffer_overflow; sprintf(buf + strlen(buf), " %lus %lus %s", lba, blocks, uuid_path); Xorriso_record_cmd_linE /* Issue command or option for hiding cut out file */ if(flag & 2) { sprintf(buf, "-hide_iso_path on %s", uuid_path); Xorriso_record_cmd_linE } else { sprintf(buf, "-hide on %s --", uuid_path); Xorriso_record_cmd_linE } *path= strdup(uuid_path); if(*path == NULL) {ret= -1; goto ex;} ret= 1; ex:; return(ret); } /* @param flag bit0= do not record but only count bit1= as_mkisofs bit2= no sorry messages */ static int Xorriso_scan_report_lines(struct XorrisO *xorriso, char **et_lines, int et_line_count, char **sa_lines, int sa_line_count, char **cmds, int *cmd_count, char **boot_imgs, int *boot_img_count, int flag) { int ret= 0, i, num_count, mkisofs, line_count, idx, et_idx, isohybrid= 0; int ptype, gpt_idx, j, pad, mbr_idx; int efi_boot_part= 0, full_sparc_part= 0, have_sparc_part= 0, fe_dummy= 1; int appended_as_gpt= 0, have_prep= 0, did_sysarea= 0, cared_for_apm= 0; int cared_for_sparc= 0, have_hfsplus= 0; int have_sysarea= 0, ptable_killer, imported_iso, have_alpha_ldr_path= 0; int have_protective_msdos= 0, part_like_isohybrid= 0, fresh_efi_boot_part; #ifdef Not_any_more_because_padding_is_now_after_partitions int appended_partition= 0; #endif int iso_mbr_part_type= -1, iso_gpt_part_idx= -1, buf_size; unsigned int prev_pltf= 0; unsigned long int sa_options= 0, partno, id_tag, perms, start_cyl; unsigned long int part_status, part_type, mbr_start_block, mbr_num_blocks; unsigned long int partition_offset= 0; uint32_t high_block= 0; off_t indev_blocks; char name[24], *textpt, *contentpt, *buf= NULL, part_type_text[37]; char **lines= NULL; double num[8]; char *cat_path= ""; struct elto_img_par *et_imgs= NULL; int elto_count= 0; uint32_t mbr_parts_end= 0; off_t extract_size; struct FindjoB *job= NULL; struct stat dir_stbuf; IsoImage *image; char *volid, *crt, *mdt, *ext, *eft, uuid[17], *uuid_time; char **app_pseudo_paths= NULL; struct tm tm_erg; int was_force_bootable= 0, have_mbr_force_bootable= 0; uint64_t gpt_bheader_block= 0, start_block, num_blocks; uint64_t img_blocks= 0, iso_part_blocks; char *cpt, *npt, *ftext; int ftext_l, l; unsigned char bin_data[8]; uint64_t gpt_part_flags; int was_gpt_iso_bootable= 0, was_gpt_iso_not_ro= 0, bin_count; struct mbr_par { uint8_t ptype; uint64_t start_block; uint64_t block_count; int appended; int has_path; }; struct mbr_par *mbrpts= NULL; int mbr_count= 0; struct gpt_par { int ptype; /* 0= unknown, 1= gpt-basdat, 2=gpt-hfsplus, 3=EFI */ uint8_t type_guid[16]; int is_gap; int has_path; char *path; uint64_t start_block; uint64_t block_count; }; struct gpt_par *gpts= NULL; int gpt_count= 0; struct apm_par { int ptype; /* bit0= type Apple_HFS , bit1= name HFSPLUS_Hybrid */ char *path; }; struct apm_par *apms= NULL; int apm_count= 0; /* 2 exp 19 blocks = 1 GiB */ #define Xorriso_max_endless_uefi_sizE (1 << 19) mkisofs= !!(flag & 2); imported_iso= (!mkisofs) << 30; *cmd_count= 0; *boot_img_count= 0; line_count= et_line_count + sa_line_count; if(line_count <= 0) {ret= 1; goto ex;} buf_size= 80 + 5 * SfileadrL; Xorriso_alloc_meM(buf, char, buf_size); Xorriso_alloc_meM(lines, char *, line_count); for(i= 0; i < et_line_count; i++) lines[i]= et_lines[i]; for(i= 0; i < sa_line_count; i++) lines[i + et_line_count]= sa_lines[i]; /* Pre-scan to establish context */ for(i= 0; i < line_count; i++) { ret= Xorriso_split_report_line(xorriso, lines[i], 8, name, &contentpt, num, &num_count, &textpt, 0); if(ret <= 0) goto ex; if(strcmp(name, "System area options:") == 0) { sscanf(contentpt, "%lx", &sa_options); } else if(strcmp(name, "System area summary:") == 0) { have_sysarea= 1; } else if(strcmp(name, "El Torito boot img :") == 0) { if(num[0] > elto_count) elto_count= num[0]; } else if(strcmp(name, "PReP boot partition:") == 0) { have_prep= 1; } else if(strcmp(name, "MBR partition :") == 0) { if(num[0] > mbr_count) mbr_count= num[0]; if(strcmp(textpt, "0x80 0x00 0 1") == 0) have_mbr_force_bootable= 1; } else if(strcmp(name, "GPT partition name :") == 0) { if(num[0] > gpt_count) gpt_count= num[0]; } else if(strcmp(name, "APM partition name :") == 0) { if(num[0] > apm_count) apm_count= num[0]; } else if(strcmp(name, "ISO image size/512 :") == 0) { img_blocks= num[0]; } else if(strcmp(name, "Partition offset :") == 0 && (num[0] == 0 || num[0] == 16)) { partition_offset= num[0]; } } ret= Xorriso_highest_data_block(xorriso, &high_block, 0); if(ret < 0) goto ex; if(ret == 0) high_block = img_blocks / 4 - 1; if(elto_count > 0) { Xorriso_alloc_meM(et_imgs, struct elto_img_par, elto_count); for(et_idx= 0; et_idx < elto_count; et_idx++) { et_imgs[et_idx].path= NULL; et_imgs[et_idx].path_allocated= 0; et_imgs[et_idx].ldsiz= -1; } Xorriso_alloc_meM(app_pseudo_paths, char *, elto_count); for(i= 0; i < elto_count; i++) app_pseudo_paths[i]= NULL; for(i= 0; i < elto_count; i++) { Xorriso_alloc_meM(app_pseudo_paths[i], char, 80); app_pseudo_paths[i][0]= 0; } } if(mbr_count > 0) Xorriso_alloc_meM(mbrpts, struct mbr_par, mbr_count); if(gpt_count > 0) { Xorriso_alloc_meM(gpts, struct gpt_par, gpt_count); for(gpt_idx= 0; gpt_idx < gpt_count; gpt_idx++) gpts[gpt_idx].path= NULL; } if(apm_count > 0) { Xorriso_alloc_meM(apms, struct apm_par, apm_count); for(i= 0; i < apm_count; i++) apms[i].path= NULL; } ptable_killer= (mbr_count > 0) | ((gpt_count > 0) << 1) | ((apm_count > 0) << 2); /* Report volume id and GRUB2 modification date */; ret= Xorriso_get_volume(xorriso, &image, 0); if(ret <= 0) goto ex; if(mkisofs) sprintf(buf, "-V "); else sprintf(buf, "-volid "); volid= (char *) un0(iso_image_get_volume_id(image)); Text_shellsafe(volid, buf, 1); Xorriso_record_cmd_linE ret= iso_image_get_pvd_times(image, &crt, &mdt, &ext, &eft); if(ret == ISO_SUCCESS) { uuid_time= crt; /* If Creation Time is bad and Modification Time is ok: use the latter */ ret= Decode_ecma119_format(&tm_erg, crt, 0); if(ret <= 0 || strlen(crt) != 16) { ret= Decode_ecma119_format(&tm_erg, mdt, 0); if(!(ret <= 0 || strlen(mdt) != 16)) uuid_time= mdt; } pad= 0; for(j= 0; j < 16; j++) { if(pad) { uuid[j]= '0'; } else if(uuid_time[j] == 0) { pad= 1; uuid[j]= '0'; } else if(uuid_time[j] < '0' || uuid_time[j] > '9') { uuid[j]= '0'; } else { uuid[j]= uuid_time[j]; } } uuid[16]= 0; ret= Decode_ecma119_format(&tm_erg, uuid, 0); if(!(ret <= 0 || strlen(uuid) != 16)) { if(mkisofs) sprintf(buf, "--modification-date="); else sprintf(buf, "-volume_date uuid "); Text_shellsafe(uuid, buf, 1); Xorriso_record_cmd_linE } } /* First pass: set up objects, record El Torito and info needed in 2nd pass */ for(i= 0; i < line_count; i++) { buf[0]= 0; ret= Xorriso_split_report_line(xorriso, lines[i], 8, name, &contentpt, num, &num_count, &textpt, 0); if(ret <= 0) goto ex; if(strcmp(name, "El Torito cat path :") == 0) { cat_path= textpt; } else if(strcmp(name, "El Torito catalog :") == 0) { strcpy(buf, "eltorito_catalog.img/"); Xorriso_add_offset_size(xorriso, buf, ((off_t) num[0]) * 2048, ((off_t) num[1]) * 2048, 0); Xorriso_record_boot_imglinE } else if(strcmp(name, "El Torito boot img :") == 0) { /* Platform Id, bootability, emulation, load segment, Hard disk emulation partition type, Load size */ idx= num[0] - 1; sscanf(contentpt, "%d %s %s %s %x %x %d %lu", &(et_imgs[idx].n), et_imgs[idx].pltf, et_imgs[idx].b, et_imgs[idx].emul, &(et_imgs[idx].ld_seg), &(et_imgs[idx].hdpt), &(et_imgs[idx].ldsiz), &(et_imgs[idx].lba)); if(strcmp(et_imgs[idx].pltf, "BIOS") == 0) et_imgs[idx].platform_id= 0; else if(strcmp(et_imgs[idx].pltf, "PPC") == 0) et_imgs[idx].platform_id= 1; else if(strcmp(et_imgs[idx].pltf, "Mac") == 0) et_imgs[idx].platform_id= 2; else if(strcmp(et_imgs[idx].pltf, "UEFI") == 0) et_imgs[idx].platform_id= 0xef; else sscanf(et_imgs[idx].pltf, "%x", &(et_imgs[idx].platform_id)); strcpy(et_imgs[idx].boot_image_type, "any"); et_imgs[idx].boot_info_table= 0; et_imgs[idx].grub2_boot_info= 0; et_imgs[idx].path= et_imgs[idx].id_string= et_imgs[idx].sel_crit= ""; et_imgs[idx].do_gpt_basdat= et_imgs[idx].do_gpt_hfsplus= 0; et_imgs[idx].do_apm_hfsplus= 0; et_imgs[idx].extract_size= (et_imgs[idx].ldsiz + 3) / 4; } else if(strcmp(name, "El Torito img path :") == 0) { idx= num[0] - 1; et_imgs[idx].path= textpt; ret= Xorriso_iso_lstat(xorriso, et_imgs[idx].path, &dir_stbuf, 0); if(ret == 0) { extract_size = (dir_stbuf.st_size + (off_t) 2047) / (off_t) 2048; if(extract_size > (off_t) 0xffffffff) { if(!(flag & 5)) { sprintf(xorriso->info_text, "Boot image size exceeds limit of 32-bit block count: "); Text_shellsafe(et_imgs[idx].path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } continue; } if(extract_size > (off_t) et_imgs[idx].extract_size) et_imgs[idx].extract_size= extract_size; } } else if(strcmp(name, "El Torito img blks :") == 0) { idx= num[0] - 1; if(num[1] > et_imgs[idx].extract_size) et_imgs[idx].extract_size= num[1]; } else if(strcmp(name, "El Torito img opts :") == 0) { idx= num[0] - 1; if(strstr(textpt, "boot-info-table") != NULL) et_imgs[idx].boot_info_table= 1; if(strstr(textpt, "isohybrid-suitable") != NULL) strcpy(et_imgs[idx].boot_image_type, "isolinux"); if(strstr(textpt, "grub2-boot-info") != NULL) { strcpy(et_imgs[idx].boot_image_type, "grub"); et_imgs[idx].grub2_boot_info= 1; } } else if(strcmp(name, "El Torito id string:") == 0) { idx= num[0] - 1; et_imgs[idx].id_string= textpt; } else if(strcmp(name, "El Torito sel crit :") == 0) { idx= num[0] - 1; et_imgs[idx].sel_crit= textpt; } else if(strcmp(name, "System area summary:") == 0) { if(strstr(textpt, "protective-msdos-label") != NULL) have_protective_msdos= 1; } else if(strcmp(name, "MBR partition :") == 0) { sscanf(contentpt, "%lu 0x%lx 0x%lx %lu %lu", &partno, &part_status, &part_type, &mbr_start_block, &mbr_num_blocks); idx= partno - 1; mbrpts[idx].ptype= part_type; mbrpts[idx].start_block= mbr_start_block; mbrpts[idx].block_count= mbr_num_blocks; if(mbr_num_blocks > 0 && mbr_start_block + mbr_num_blocks > mbr_parts_end) mbr_parts_end= mbr_start_block + mbr_num_blocks; if(mbr_start_block == partition_offset * 4 && (mbr_start_block + mbr_num_blocks) >= high_block * 4 && iso_mbr_part_type < 0) iso_mbr_part_type = part_type; } else if(strcmp(name, "MBR partition path :") == 0) { idx= num[0] - 1; mbrpts[idx].has_path= 1; } else if(strcmp(name, "GPT lba range :") == 0) { gpt_bheader_block= num[2]; } else if(strcmp(name, "GPT type GUID :") == 0) { idx= num[0] - 1; if(strcmp(textpt, "a2a0d0ebe5b9334487c068b6b72699c7") == 0) gpts[idx].ptype= 1; /* Basic data */ else if(strcmp(textpt, "005346480000aa11aa1100306543ecac") == 0) gpts[idx].ptype= 2; /* HFS+ */ else if(strcmp(textpt, "28732ac11ff8d211ba4b00a0c93ec93b") == 0) gpts[idx].ptype= 3; /* EFI System Partition */ else gpts[idx].ptype= 0; Xorriso_parse_guid(xorriso, textpt, gpts[idx].type_guid, 1); } else if(strcmp(name, "GPT start and size :") == 0) { idx= num[0] - 1; if(num[2] > 0) appended_as_gpt= 1; start_block= gpts[idx].start_block= num[1]; num_blocks= gpts[idx].block_count= num[2]; if(start_block == partition_offset * 4 && (start_block + num_blocks) >= high_block * 4 && iso_gpt_part_idx < 0) iso_gpt_part_idx= idx; } else if(strcmp(name, "GPT partition path :") == 0) { idx= num[0] - 1; gpts[idx].has_path= 1; gpts[idx].path= textpt; } else if(strcmp(name, "GPT partition name :") == 0) { idx= num[0] - 1; if(strstr(contentpt, " 470061007000") != NULL) /* "Gap"... */ gpts[idx].is_gap= 1; } else if(strcmp(name, "APM partition name :") == 0) { idx= num[0] - 1; if(strcmp(textpt, "HFSPLUS_Hybrid") == 0) apms[idx].ptype|= 2; } else if(strcmp(name, "APM partition type :") == 0) { idx= num[0] - 1; if(strcmp(textpt, "Apple_HFS") == 0) apms[idx].ptype|= 1; } else if(strcmp(name, "APM partition path :") == 0) { idx= num[0] - 1; apms[idx].path= textpt; } else if(strcmp(name, "DEC Alpha ldr path :") == 0) { have_alpha_ldr_path= 1; } } if(appended_as_gpt && !have_protective_msdos) { /* Check if really a pure GPT or a nearly pure mbr-force-bootable GPT */ if(mbr_count != 1 && !(mbr_count == 2 && have_mbr_force_bootable)) { appended_as_gpt= 0; } else if(mbrpts[0].ptype != 0xee || mbrpts[0].start_block != 1) { appended_as_gpt= 0; } else if(gpt_bheader_block != mbrpts[0].block_count) { appended_as_gpt= 0; } } iso_part_blocks= img_blocks; for(mbr_idx = 0; mbr_idx < mbr_count; mbr_idx++) { if(mbrpts[mbr_idx].start_block == partition_offset * 4) { iso_part_blocks= mbrpts[mbr_idx].block_count + partition_offset * 4; break; } } /* Second pass: scan for System Area info */ for(i= 0; i < line_count; i++) { buf[0]= 0; ret= Xorriso_split_report_line(xorriso, lines[i], 8, name, &contentpt, num, &num_count, &textpt, 0); if(ret <= 0) goto ex; if(strcmp(name, "System area options:") == 0) { if((sa_options & 0x3c00) == 0x0400) { if(mkisofs) sprintf(buf, "-chrp-boot-part "); else sprintf(buf, "-boot_image any chrp_boot_part=on "); Xorriso_record_cmd_linE buf[0]= 0; ret= Xorriso_assess_written_features(xorriso, mkisofs ? "as_mkisofs" : "cmd", 1); if(ret > 0) { ftext= xorriso->result_line; ftext_l= strlen(ftext); for(cpt= ftext ; cpt - ftext < ftext_l ; cpt+= l + 1) { npt= strchr(cpt, '\n'); if(npt == NULL) l= strlen(cpt); else l= npt - cpt; cpt[l]= 0; /* Only forward relaxations of ISO 9660 defaults, plus no RR */ if(mkisofs) { if(strcmp(cpt, "-iso-level 2") != 0 && strcmp(cpt, "-iso-level 3") != 0 && strcmp(cpt, "--norock") != 0 && strncmp(cpt, "-untranslated_name_len", 22) != 0 && strcmp(cpt, "-N") != 0 && strcmp(cpt, "-D") != 0 && strcmp(cpt, "-U") != 0 && strcmp(cpt, "-max-iso9660-filenames") != 0 && strcmp(cpt, "-d") != 0 && strcmp(cpt, "-allow-lowercase") != 0) continue; } else { /* (Do not forward iso_9660_level because 3 is default and the user possibly had reasons to lower it) */ if(strcmp(cpt, "-rockridge off") != 0 && strncmp(cpt, "-compliance untranslated_name_len=", 34) != 0 && strcmp(cpt, "-compliance omit_version_off:only_iso_version") != 0 && strcmp(cpt, "-compliance omit_version:only_iso_version_off") != 0 && strcmp(cpt, "-compliance deep_paths") != 0 && strcmp(cpt, "-compliance long_paths") != 0 && strcmp(cpt, "-compliance full_ascii") != 0 && strcmp(cpt, "-compliance long_names") != 0 && strncmp(cpt, "-compliance no_force_dots:", 26) != 0 && strcmp(cpt, "-compliance lowercase") != 0) continue; if(strcmp(cpt, "-compliance untranslated_name_len=0") == 0) continue; } if(strncmp(cpt, "-compliance untranslated_name_len=", 34) == 0) { /* Better allow the maximum if it is reported as non-0 */ strcpy(buf, "-compliance untranslated_name_len=96"); } else { strcpy(buf, cpt); } Xorriso_record_cmd_linE } } } } else if(strcmp(name, "System area summary:") == 0) { if(strstr(textpt, "isohybrid") != NULL) { isohybrid= 1; if(mkisofs) sprintf(buf, "-isohybrid-mbr "); else sprintf(buf, "-boot_image isolinux system_area="); Xorriso_add_intvl_adr(xorriso, buf, (uint64_t) 0, (uint64_t) 15, "s", imported_iso | ptable_killer); Xorriso_record_cmd_linE strcpy(buf, "mbr_code_isohybrid.img/"); Xorriso_add_offset_size(xorriso, buf, (off_t) 0, (off_t) 446, 0); Xorriso_record_boot_imglinE did_sysarea= 1; } if(strstr(textpt, "grub2-mbr") != NULL) { if(mkisofs) sprintf(buf, "--grub2-mbr "); else sprintf(buf, "-boot_image grub grub2_mbr="); Xorriso_add_intvl_adr(xorriso, buf, (uint64_t) 0, (uint64_t) 15, "s", imported_iso | ptable_killer); Xorriso_record_cmd_linE strcpy(buf, "mbr_code_grub2.img/"); Xorriso_add_offset_size(xorriso, buf, (off_t) 0, (off_t) 446, 0); Xorriso_record_boot_imglinE did_sysarea= 1; } if(strstr(textpt, "protective-msdos-label") != NULL) { if(mkisofs) sprintf(buf, "--protective-msdos-label"); else sprintf(buf, "-boot_image any partition_table=on"); Xorriso_record_cmd_linE } if(strstr(textpt, "cyl-align-off") != NULL) { if(mkisofs) sprintf(buf, "-partition_cyl_align off"); else sprintf(buf, "-boot_image any partition_cyl_align=off"); } else if(strstr(textpt, "cyl-align-all") != NULL) { if(mkisofs) sprintf(buf, "-partition_cyl_align all"); else sprintf(buf, "-boot_image any partition_cyl_align=all"); } else if(strstr(textpt, "cyl-align-") != NULL) { if(mkisofs) sprintf(buf, "-partition_cyl_align on"); else sprintf(buf, "-boot_image any partition_cyl_align=on"); } else buf[0]= 0; } else if(strcmp(name, "Partition offset :") == 0 && (num[0] == 0 || num[0] == 16)) { if(mkisofs) sprintf(buf, "-partition_offset %.f", num[0]); else sprintf(buf, "-boot_image any partition_offset=%.f", num[0]); } else if(strcmp(name, "MBR heads per cyl :") == 0 && (num[0] > 0 && num[0] <= 255)) { if(mkisofs) sprintf(buf, "-partition_hd_cyl %.f", num[0]); else sprintf(buf, "-boot_image any partition_hd_cyl=%.f", num[0]); } else if(strcmp(name, "MBR secs per head :") == 0 && (num[0] > 0 && num[0] <= 63)) { if(mkisofs) sprintf(buf, "-partition_sec_hd %.f", num[0]); else sprintf(buf, "-boot_image any partition_sec_hd=%.f", num[0]); } else if(strcmp(name, "MBR partition :") == 0) { sscanf(contentpt, "%lu 0x%lx 0x%lx %lu %lu", &partno, &part_status, &part_type, &mbr_start_block, &mbr_num_blocks); if(mbr_num_blocks > 0 && part_type != 0x00 && part_type != 0xee && (iso_part_blocks <= mbr_start_block || (have_protective_msdos && img_blocks == mbr_parts_end && partno > 1))) { if(!appended_as_gpt) { sprintf(buf, "-append_partition %lu 0x%lx ", partno, part_type); Xorriso_add_intvl_adr(xorriso, buf, (uint64_t) mbr_start_block, ((uint64_t) mbr_start_block) + mbr_num_blocks - 1, "d", imported_iso); Xorriso_record_cmd_linE if(partno >= 1 && (int) partno <= mbr_count) mbrpts[partno - 1].appended= 1; #ifdef Not_any_more_because_padding_is_now_after_partitions appended_partition= 1; #endif } if(part_type == 0xef) { sprintf(buf, "mbr_part%lu_efi.img/", partno); Xorriso_add_offset_size(xorriso, buf, ((off_t) mbr_start_block) * 512, ((off_t) mbr_num_blocks) * 512, 0); Xorriso_record_boot_imglinE } } else if(part_type == 0x41 && have_prep) { if(mkisofs) { sprintf(buf, "-prep-boot-part "); } else { sprintf(buf, "-boot_image any prep_boot_part="); } Xorriso_add_intvl_adr(xorriso, buf, (uint64_t) mbr_start_block, ((uint64_t) mbr_start_block) + mbr_num_blocks - 1, "d", imported_iso); Xorriso_record_cmd_linE sprintf(buf, "mbr_part%lu_prep.img/", partno); Xorriso_add_offset_size(xorriso, buf, ((off_t) mbr_start_block) * 512, ((off_t) mbr_num_blocks) * 512, 0); Xorriso_record_boot_imglinE } else if(part_type == 0xef) { sprintf(buf, "mbr_part%lu_efi.img/", partno); Xorriso_add_offset_size(xorriso, buf, ((off_t) mbr_start_block) * 512, ((off_t) mbr_num_blocks) * 512, 0); Xorriso_record_boot_imglinE } if((part_status & 0x80) && !was_force_bootable) { was_force_bootable= 1; if(buf[0]) { Xorriso_record_cmd_linE } if(mkisofs) sprintf(buf, "--mbr-force-bootable"); else sprintf(buf, "-boot_image any mbr_force_bootable=on"); } } else if(strcmp(name, "MBR partition path :") == 0) { idx= num[0] - 1; if(mbrpts[idx].ptype == 0x41) { sprintf(xorriso->info_text, "Cannot make proposal to mark PReP partition by data file: "); Text_shellsafe(textpt, xorriso->info_text, 1); if(!(flag & 5)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); continue; } ptype= 0; if(mbrpts[idx].ptype == 0xef) ptype= 3; ret= Xorriso_search_eltorito_path(xorriso, et_imgs, elto_count, textpt, ptype, &et_idx, &efi_boot_part, !!isohybrid); if(ret <= 0) { sprintf(xorriso->info_text, "Cannot make proposal to mark data file as MBR partition without being an El Torito boot image : "); Text_shellsafe(textpt, xorriso->info_text, 1); if(!(flag & 5)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } else { for(gpt_idx= 0; gpt_idx < gpt_count; gpt_idx++) { if(gpts[gpt_idx].path != NULL) if(strcmp(gpts[gpt_idx].path, textpt) == 0) break; } if(gpt_idx >= gpt_count) { sprintf(xorriso->info_text, "Cannot make proposal to mark data file as MBR partition without being in GPT : "); Text_shellsafe(textpt, xorriso->info_text, 1); if(!(flag & 5)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } } } else if(strcmp(name, "GPT disk GUID :") == 0) { /* >>> ??? need command to set disk GUID */; } else if(strcmp(name, "GPT partition name :") == 0) { /* >>> ??? need command to set partition name for partition number */; } else if(strcmp(name, "GPT partition GUID :") == 0) { /* >>> ??? need command to set partition GUID for partition number */; } else if(strcmp(name, "GPT partition flags:") == 0) { /* >>> check whether 0x100000000000000[15] . Else: complain */; cpt= strstr(contentpt, "0x"); if(cpt != NULL) { ret= Hex_to_bin(cpt + 2, 8, &bin_count, bin_data, 0); if(ret > 0) { /* convert big-endian bin_data to local endianness */ gpt_part_flags= 0; for(gpt_idx= 0; gpt_idx < bin_count; gpt_idx++) gpt_part_flags|= (((uint64_t) bin_data[gpt_idx]) << (8 * (bin_count - 1 - gpt_idx))); if((gpt_part_flags & 4) && !was_gpt_iso_bootable) { was_gpt_iso_bootable= 1; if(buf[0]) { Xorriso_record_cmd_linE } if(mkisofs) sprintf(buf, "--gpt-iso-bootable"); else sprintf(buf, "-boot_image any gpt_iso_bootable=on"); } if(num_count > 0 && num[0] == 1 && (!(gpt_part_flags & (((uint64_t) 1) << 60))) && !was_gpt_iso_not_ro) { was_gpt_iso_not_ro= 1; if(buf[0]) { Xorriso_record_cmd_linE } if(mkisofs) sprintf(buf, "--gpt-iso-not-ro"); else sprintf(buf, "-boot_image any gpt_iso_not_ro=on"); } } } } else if(strcmp(name, "GPT partition path :") == 0) { idx= num[0] - 1; ret= Xorriso_search_eltorito_path(xorriso, et_imgs, elto_count, textpt, gpts[idx].ptype, &et_idx, &efi_boot_part, !!isohybrid); if(ret <= 0) { sprintf(xorriso->info_text, "Cannot make proposal to mark data file as GPT partition : "); Text_shellsafe(textpt, xorriso->info_text, 1); if(!(flag & 5)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } } else if(strcmp(name, "GPT start and size :") == 0) { idx= num[0] - 1; if(gpts[idx].ptype == 3) part_type= 0xef; else part_type= 0xcd; fresh_efi_boot_part= 0; if(high_block * 4 < num[1] && num[2] > 0 && !gpts[idx].is_gap) { for(mbr_idx = 0; mbr_idx < mbr_count; mbr_idx++) { if(mbrpts[mbr_idx].start_block == num[1]) { if(mbrpts[mbr_idx].block_count != num[2] && !(flag & 1)) { sprintf(xorriso->info_text, "GPT partition %d has same start block as MBR partition %d but different block count (%.f <> %.f)", idx + 1, mbr_idx + 1, num[2], (double) mbrpts[mbr_idx].block_count); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } break; } } if(mbr_idx >= mbr_count) { if(appended_as_gpt == 1) { appended_as_gpt= 2; Xorriso__format_guid(gpts[idx].type_guid, part_type_text, 0); } else { sprintf(part_type_text, "0x%lx", part_type); } sprintf(buf, "-append_partition %d %s ", idx + 1, part_type_text); Xorriso_add_intvl_adr(xorriso, buf, (uint64_t) num[1], (uint64_t) (num[1] + num[2] - 1.0), "d", imported_iso); Xorriso_record_cmd_linE #ifdef Not_any_more_because_padding_is_now_after_partitions appended_partition= 1; #endif } } else if(gpts[idx].ptype == 3 && gpts[idx].has_path == 0 && img_blocks >= num[1] + num[2] && !efi_boot_part) { if(mkisofs) sprintf(buf, "-efi-boot-part "); else sprintf(buf, "-boot_image any efi_boot_part="); Xorriso_add_intvl_adr(xorriso, buf, (uint64_t) num[1], (uint64_t) (num[1] + num[2] - 1.0), "d", imported_iso); efi_boot_part= 2; fresh_efi_boot_part= 1; Xorriso_record_cmd_linE } if(!fresh_efi_boot_part) { /* Check for isohybri-ish MBR and GPT mix */ if((mbr_count == 1 || (mbr_count == 2 && have_mbr_force_bootable)) && mbrpts[0].ptype == 0xee && have_protective_msdos) { /* real GPT (+/- mbr_force_bootable) is not -part_like_isohybrid */ ret= 0; } else { ret= Xorriso_search_eltorito_lba(xorriso, et_imgs, elto_count, (unsigned int) (num[1] / 4.0), &et_idx, 0); } if(ret > 0) { if(!(isohybrid || et_imgs[et_idx].do_gpt_basdat || et_imgs[et_idx].do_gpt_hfsplus || part_like_isohybrid)) { if(mkisofs) sprintf(buf, "-part_like_isohybrid"); else sprintf(buf, "-boot_image any part_like_isohybrid=on"); Xorriso_record_cmd_linE part_like_isohybrid= 1; appended_as_gpt= 0; } /* mark el torito for -isohybrid-gpt-... */ Xorriso_register_eltorito_gpt(xorriso, et_imgs + et_idx, gpts[idx].ptype, &efi_boot_part, &fe_dummy, 1); if(efi_boot_part == 1) { /* -isohybrid-gpt- substitutes for -efi-boot-part --efi-boot-image */ efi_boot_part= 2; } } } if(gpts[idx].ptype == 2 && (img_blocks / 2 > num[2] || num[1] >= img_blocks)) { /* Obviously not a HFS+ tree covering the ISO */ sprintf(buf, "gpt_part%d_hfsplus.img/", idx + 1); Xorriso_add_offset_size(xorriso, buf, ((off_t) num[1]) * 512, ((off_t) num[2]) * 512, 0); Xorriso_record_boot_imglinE } else if(gpts[idx].ptype == 3) { sprintf(buf, "gpt_part%d_efi.img/", idx + 1); Xorriso_add_offset_size(xorriso, buf, ((off_t) num[1]) * 512, ((off_t) num[2]) * 512, 0); Xorriso_record_boot_imglinE } } else if(strcmp(name, "APM block size :") == 0) { if(mkisofs) sprintf(buf, "-apm-block-size %.f", num[0]); else sprintf(buf, "-boot_image any apm_block_size=%.f", num[0]); } else if(strcmp(name, "APM partition name :") == 0) { /* >>> ??? need command to set APM partition name for partition number */; } else if(strcmp(name, "APM partition path :") == 0) { idx= num[0] - 1; /* Check El Torito EFI boot images for same path */ for(et_idx= 0; isohybrid && et_idx < elto_count; et_idx++) if(strcmp(et_imgs[et_idx].path, textpt) == 0) { if(apms[idx].ptype == 1) { et_imgs[et_idx].do_apm_hfsplus= 1; cared_for_apm= 1; } break; } } else if(strcmp(name, "APM start and size :") == 0) { idx= num[0] - 1; if(num[1] + num[2] <= img_blocks && apms[idx].ptype == 3 && apms[idx].path == NULL && !have_hfsplus) { /* >>> HFS+ magic number */; /* >>> Read byte 1024 and 1025 after partition start Must be {'H', '+'} (0x482b big endian) */; /* ??? >>> Do this recognition in libisofs ? */ if(mkisofs) sprintf(buf, "-hfsplus"); else sprintf(buf, "-hfsplus on"); Xorriso_record_cmd_linE /* Report commands for blessings and creator-type */ ret= Findjob_new(&job, "/", 0); if(ret <= 0) { Xorriso_no_findjob(xorriso, "xorriso", 0); {ret= -1; goto ex;} } Findjob_set_action_target(job, 53, NULL, 0); xorriso->show_hfs_cmd_count= *cmd_count; xorriso->show_hfs_cmds= cmds; xorriso->show_hfs_cmd_flag= (flag & 1) | ((!!mkisofs) << 1); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, "/", &dir_stbuf, 0, 0); *cmd_count= xorriso->show_hfs_cmd_count; if(ret <= 0) goto ex; have_hfsplus= 1; cared_for_apm= 1; } } else if(strcmp(name, "MIPS-BE boot path :") == 0) { if(mkisofs) sprintf(buf, "-mips-boot "); else sprintf(buf, "-boot_image any mips_path="); Text_shellsafe(textpt, buf, 1); } else if(strcmp(name, "MIPS-LE boot path :") == 0) { if(mkisofs) sprintf(buf, "-mipsel-boot "); else sprintf(buf, "-boot_image any mipsel_path="); Text_shellsafe(textpt, buf, 1); } else if(strcmp(name, "SUN SPARC disklabel:") == 0) { if(mkisofs) sprintf(buf, "-sparc-label "); else sprintf(buf, "-boot_image any sparc_label="); Text_shellsafe(textpt, buf, 1); } else if(strcmp(name, "SPARC GRUB2 path :") == 0) { if(mkisofs) { sprintf(buf, "-B ,"); Xorriso_record_cmd_linE sprintf(buf, "--grub2-sparc-core "); } else sprintf(buf, "-boot_image grub grub2_sparc_core="); Text_shellsafe(textpt, buf, 1); cared_for_sparc= 1; } else if(strcmp(name, "SUN SPARC partition:") == 0) { have_sparc_part= 1; partno= id_tag= perms= num_blocks= 0; start_cyl= 0xffffffff; sscanf(contentpt, "%lu 0x%lx 0x%lx %lu %lu", &partno, &id_tag, &perms, &start_cyl, &mbr_num_blocks); if(partno > 0 && partno < 9 && start_cyl == 0 && mbr_num_blocks >= img_blocks - 600 && mbr_num_blocks <= img_blocks && ((partno == 1 && id_tag == 4) || (partno > 1 && id_tag == 2))) full_sparc_part|= (1 << (partno - 1)); } else if(strcmp(name, "PALO header version:") == 0) { if(mkisofs) sprintf(buf, "-hppa-hdrversion %.f", num[0]); else sprintf(buf, "-boot_image any hppa_hdrversion=%.f", num[0]); } else if(strcmp(name, "HP-PA cmdline :") == 0) { if(mkisofs) sprintf(buf, "-hppa-cmdline "); else sprintf(buf, "-boot_image any hppa_cmdline="); Text_shellsafe(textpt, buf, 1); } else if(strcmp(name, "HP-PA 32-bit kernel:") == 0) { if(mkisofs) sprintf(buf, "-hppa-kernel-32 "); else sprintf(buf, "-boot_image any hppa_kernel_32="); Text_shellsafe(textpt, buf, 1); } else if(strcmp(name, "HP-PA 64-bit kernel:") == 0) { if(mkisofs) sprintf(buf, "-hppa-kernel-64 "); else sprintf(buf, "-boot_image any hppa_kernel_64="); Text_shellsafe(textpt, buf, 1); } else if(strcmp(name, "HP-PA ramdisk :") == 0) { if(mkisofs) sprintf(buf, "-hppa-ramdisk "); else sprintf(buf, "-boot_image any hppa_ramdisk="); Text_shellsafe(textpt, buf, 1); } else if(strcmp(name, "HP-PA bootloader :") == 0) { if(mkisofs) sprintf(buf, "-hppa-bootloader "); else sprintf(buf, "-boot_image any hppa_bootloader="); Text_shellsafe(textpt, buf, 1); } else if(strcmp(name, "DEC Alpha ldr adr :") == 0) { if(!have_alpha_ldr_path) { sprintf(xorriso->info_text, "Cannot enable DEC Alpha boot loader because it is not a data file in the ISO filesystem"); if(!(flag & 5)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } } else if(strcmp(name, "DEC Alpha ldr path :") == 0) { if(mkisofs) sprintf(buf, "-alpha-boot "); else sprintf(buf, "-boot_image any alpha_boot="); Text_shellsafe(textpt, buf, 1); } if(buf[0]) Xorriso_record_cmd_linE } if(appended_as_gpt == 2) { if(mkisofs) sprintf(buf, "-appended_part_as_gpt"); else sprintf(buf, "-boot_image any appended_part_as=gpt"); Xorriso_record_cmd_linE } if(have_sparc_part) { if(full_sparc_part == 255) { if(mkisofs) { sprintf(buf, "-G "); Xorriso_add_intvl_adr(xorriso, buf, (uint64_t) 0, (uint64_t) 15, "s", imported_iso); Xorriso_record_cmd_linE did_sysarea= 1; sprintf(buf, "-B ..."); Xorriso_record_cmd_linE } else { sprintf(buf, "-boot_image any system_area="); Xorriso_add_intvl_adr(xorriso, buf, (uint64_t) 0, (uint64_t) 15, "s", imported_iso); Xorriso_record_cmd_linE did_sysarea= 1; for(i= 2; i <= 8; i++) { sprintf(buf, "-append_partition %d 0x00 .", i); Xorriso_record_cmd_linE } } cared_for_sparc= 1; } else if(!cared_for_sparc) { sprintf(xorriso->info_text, "Cannot enable SUN Disk Label because of non-trivial partition layout"); if(!(flag & 5)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } } if(have_sysarea && !did_sysarea) { /* Zeroize old partition tables from -indev */ if(mkisofs) sprintf(buf, "-G "); else sprintf(buf, "-boot_image any system_area="); Xorriso_add_intvl_adr(xorriso, buf, (uint64_t) 0, (uint64_t) 15, "s", imported_iso | ptable_killer); Xorriso_record_cmd_linE did_sysarea= 1; } if(have_sysarea) { strcpy(buf, "systemarea.img/"); Xorriso_add_offset_size(xorriso, buf, (off_t) 0, (off_t) 16 * 2048, 0); Xorriso_record_boot_imglinE } if(iso_mbr_part_type >= 0) { if(mkisofs) sprintf(buf, "-iso_mbr_part_type 0x%2.2x", (unsigned int) iso_mbr_part_type); else sprintf(buf, "-boot_image any iso_mbr_part_type=0x%2.2x", (unsigned int) iso_mbr_part_type); Xorriso_record_cmd_linE } else if(iso_gpt_part_idx >= 0) { if(mkisofs) sprintf(buf, "-iso_mbr_part_type "); else sprintf(buf, "-boot_image any iso_mbr_part_type="); Xorriso__format_guid(gpts[iso_gpt_part_idx].type_guid, buf + strlen(buf), 0); Xorriso_record_cmd_linE } /* Issue commands related to El Torito */ if(elto_count <= 0) goto after_el_torito; if(efi_boot_part == 1) { if(mkisofs) sprintf(buf, "-efi-boot-part --efi-boot-image"); else sprintf(buf, "-boot_image any efi_boot_part=--efi-boot-image"); Xorriso_record_cmd_linE } if(cat_path[0]) { if(mkisofs) sprintf(buf, "-c "); else sprintf(buf, "-boot_image any cat_path="); Text_shellsafe(cat_path, buf, 1); } else { if(mkisofs) sprintf(buf, "--boot-catalog-hide"); else sprintf(buf, "-boot_image any cat_hidden=on"); } Xorriso_record_cmd_linE for(idx= 0; idx < elto_count; idx++) { if(strcmp(et_imgs[idx].pltf, "UEFI") == 0 && et_imgs[idx].extract_size <= 0) { ret= Xorriso_obtain_indev_readsize(xorriso, &indev_blocks, 0); if(ret > 0) { if(indev_blocks > (off_t) et_imgs[idx].lba && indev_blocks - et_imgs[idx].lba <= Xorriso_max_endless_uefi_sizE) et_imgs[idx].extract_size= indev_blocks - et_imgs[idx].lba; } if(et_imgs[idx].extract_size <= 0) continue; } sprintf(buf, "eltorito_img%d_", idx + 1); for(j= 0; j < 4 && et_imgs[idx].pltf[j] != 0; j++) { buf[strlen(buf) + 1]= 0; buf[strlen(buf)]= tolower(et_imgs[idx].pltf[j]); } strcat(buf, ".img/"); Xorriso_add_offset_size(xorriso, buf, ((off_t) et_imgs[idx].lba) * 2048, ((off_t) et_imgs[idx].extract_size) * 2048, 0); Xorriso_record_boot_imglinE if(et_imgs[idx].ld_seg != 0 && et_imgs[idx].ld_seg != 0x07c0) { if(!(flag & 5)) { sprintf(xorriso->info_text, "Cannot enable EL Torito boot image #%d because its Load Segment is neither 0x0 nor 0x7c0", idx + 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } continue; } if(idx > 0) { if(mkisofs) sprintf(buf, "-eltorito-alt-boot"); else sprintf(buf, "-boot_image any next"); Xorriso_record_cmd_linE } if(et_imgs[idx].path[0] == 0) { /* Check whether appended partition */; for(i= 0; i < mbr_count; i++) if((mbrpts[i].appended || !mbrpts[i].has_path) && mbrpts[i].start_block == ((uint64_t) et_imgs[idx].lba) * 4 && (mbrpts[i].block_count == (uint64_t) et_imgs[idx].ldsiz || et_imgs[idx].ldsiz == 0 || et_imgs[idx].ldsiz == 1)) break; if (i < mbr_count) { if(!mbrpts[i].appended) { mbrpts[i].appended= 1; if(!appended_as_gpt) { sprintf(buf, "-append_partition %lu 0x%lx ", (unsigned long) i + 1, (unsigned long) mbrpts[i].ptype); Xorriso_add_intvl_adr(xorriso, buf, (uint64_t) mbrpts[i].start_block, ((uint64_t) mbrpts[i].start_block) + mbrpts[i].block_count - 1, "d", imported_iso); Xorriso_record_cmd_linE #ifdef Not_any_more_because_padding_is_now_after_partitions appended_partition= 1; #endif buf[0]= 0; } } sprintf(app_pseudo_paths[idx], "--interval:appended_partition_%d_start_%lus_size_%lud:all::", i + 1, (unsigned long) et_imgs[idx].lba, (unsigned long) mbrpts[i].block_count); et_imgs[idx].path= app_pseudo_paths[idx]; } if (et_imgs[idx].path[0] == 0 && efi_boot_part != 2) { for(i= 0; i < gpt_count; i++) { if((have_protective_msdos || appended_as_gpt) && ( gpts[i].start_block == ((uint64_t) et_imgs[idx].lba) * 4 && (gpts[i].block_count == (uint64_t) et_imgs[idx].ldsiz || et_imgs[idx].ldsiz == 0 || et_imgs[idx].ldsiz == 1))) break; } if (i < gpt_count) { sprintf(app_pseudo_paths[idx], "--interval:appended_partition_%d_start_%lus_size_%lud:all::", i + 1, (unsigned long) et_imgs[idx].lba, (unsigned long) gpts[i].block_count); et_imgs[idx].path= app_pseudo_paths[idx]; } } if (et_imgs[idx].path[0] == 0 && et_imgs[idx].extract_size > 0) { ret= Xorriso_make_boot_cut_out(xorriso, et_imgs[idx].lba, et_imgs[idx].extract_size, cmds, cmd_count, buf, buf_size, &(et_imgs[idx].path), flag & 7); if(ret <= 0) { if(!(flag & 5)) { sprintf(xorriso->info_text, "Cannot enable hidden EL Torito boot image #%d by a -cut_out command", idx + 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } buf[0]= 0; continue; } et_imgs[idx].path_allocated= 1; } if (et_imgs[idx].path[0] == 0) { if(!(flag & 5)) { sprintf(xorriso->info_text, "Cannot enable EL Torito boot image #%d because it is not a data file in the ISO filesystem", idx + 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } buf[0]= 0; continue; } } if(et_imgs[idx].platform_id != 0xef) { if(mkisofs) { if(prev_pltf != et_imgs[idx].platform_id) { sprintf(buf, "-eltorito-platform 0x%2.2x", et_imgs[idx].platform_id); Xorriso_record_cmd_linE } prev_pltf= et_imgs[idx].platform_id; sprintf(buf, "-b "); } else { sprintf(buf, "-boot_image %s bin_path=", et_imgs[idx].boot_image_type); } } else { if(mkisofs) sprintf(buf, "-e "); else sprintf(buf, "-boot_image %s efi_path=", et_imgs[idx].boot_image_type); } Text_shellsafe(et_imgs[idx].path, buf, 1); Xorriso_record_cmd_linE if(!mkisofs) { sprintf(buf, "-boot_image any platform_id=0x%2.2x", et_imgs[idx].platform_id); Xorriso_record_cmd_linE } if(strcmp(et_imgs[idx].emul, "none") == 0) { if(mkisofs) sprintf(buf, "-no-emul-boot"); else sprintf(buf, "-boot_image any emul_type=no_emulation"); } else if(strcmp(et_imgs[idx].emul, "hd") == 0) { if(mkisofs) sprintf(buf, "-hard-disk-boot"); else sprintf(buf, "-boot_image any emul_type=hard_disk"); } else { if(mkisofs) buf[0]= 0; else sprintf(buf, "-boot_image any emul_type=diskette"); } if(buf[0]) Xorriso_record_cmd_linE if(et_imgs[idx].ldsiz >= 0) { if(mkisofs) sprintf(buf, "-boot-load-size %d", et_imgs[idx].ldsiz); else sprintf(buf, "-boot_image any load_size=%d", et_imgs[idx].ldsiz * 512); Xorriso_record_cmd_linE } if(et_imgs[idx].boot_info_table) { if(mkisofs) sprintf(buf, "-boot-info-table"); else sprintf(buf, "-boot_image any boot_info_table=on"); Xorriso_record_cmd_linE } if(et_imgs[idx].grub2_boot_info) { if(mkisofs) sprintf(buf, "--grub2-boot-info"); else sprintf(buf, "-boot_image grub grub2_boot_info=on"); Xorriso_record_cmd_linE } if(et_imgs[idx].id_string[0] != 0) { if(mkisofs) sprintf(buf, "-eltorito-id "); else sprintf(buf, "-boot_image any id_string="); Text_shellsafe(et_imgs[idx].id_string, buf, 1); Xorriso_record_cmd_linE } if(et_imgs[idx].sel_crit[0] != 0) { if(mkisofs) sprintf(buf, "-eltorito-selcrit "); else sprintf(buf, "-boot_image any sel_crit="); Text_shellsafe(et_imgs[idx].sel_crit, buf, 1); Xorriso_record_cmd_linE } if(et_imgs[idx].do_gpt_basdat && !(et_imgs[idx].do_gpt_basdat == 3 && strstr(et_imgs[idx].path, "--interval:appended_partition_") == et_imgs[idx].path)) { /* (not with appended EFI partition) */ if(mkisofs) sprintf(buf, "-isohybrid-gpt-basdat"); else sprintf(buf, "-boot_image isolinux partition_entry=gpt_basdat"); Xorriso_record_cmd_linE } if(et_imgs[idx].do_gpt_hfsplus) { if(mkisofs) sprintf(buf, "-isohybrid-gpt-hfsplus"); else sprintf(buf, "-boot_image isolinux partition_entry=gpt_hfsplus"); Xorriso_record_cmd_linE } if(et_imgs[idx].do_apm_hfsplus) { if(mkisofs) sprintf(buf, "-isohybrid-apm-hfsplus"); else sprintf(buf, "-boot_image isolinux partition_entry=apm_hfsplus"); Xorriso_record_cmd_linE } } after_el_torito: if((apm_count > 0 && !cared_for_apm) && !(flag & 5)) { sprintf(xorriso->info_text, "Cannot make proposal to produce APM of loaded image"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } #ifdef Not_any_more_because_padding_is_now_after_partitions if(appended_partition) { if(mkisofs) sprintf(buf, "-no-pad"); else sprintf(buf, "-padding 0"); Xorriso_record_cmd_linE } #endif /* Not_any_more_because_padding_is_now_after_partitions */ ret= 1; ex: xorriso->show_hfs_cmds= NULL; Findjob_destroy(&job, 0); Xorriso_free_meM(apms); Xorriso_free_meM(gpts); Xorriso_free_meM(mbrpts); if(app_pseudo_paths != NULL) { for(i= 0; i < elto_count; i++) if(app_pseudo_paths[i] != NULL) Xorriso_free_meM(app_pseudo_paths[i]); Xorriso_free_meM(app_pseudo_paths); } for(et_idx= 0; et_idx < elto_count; et_idx++) if(et_imgs[et_idx].path_allocated) Xorriso_free_meM(et_imgs[et_idx].path); Xorriso_free_meM(et_imgs); Xorriso_free_meM(lines); Xorriso_free_meM(buf); return(ret); #undef Xorriso_record_cmd_linE #undef Xorriso_record_boot_imglinE #undef Xorriso_max_endless_uefi_sizE } /* @param flag bit0= currently not significant: report is about El Torito rather than System Area bit1= report -as mkisofs options in cmds bit2= no sorry messages bit15= dispose cmds and boot_imgs */ static int Xorriso_report_to_cmd(struct XorrisO *xorriso, char **et_lines, int et_line_count, char **sa_lines, int sa_line_count, char ***cmds, int *cmd_count, char ***boot_imgs, int *boot_img_count, int flag) { int ret= 0, i; if(flag & (1 << 15)) {ret= 1; goto ex;} *cmds= NULL; *cmd_count= 0; /* Count commands */ ret= Xorriso_scan_report_lines(xorriso, et_lines, et_line_count, sa_lines, sa_line_count, *cmds, cmd_count, *boot_imgs, boot_img_count, 1 | (flag & 6)); if(ret <= 0) goto ex; if(*cmd_count <= 0 && *boot_img_count <= 0) {ret= 2; goto ex;} if(*cmd_count > 0) { Xorriso_alloc_meM(*cmds, char *, *cmd_count); for(i= 0; i < *cmd_count; i++) (*cmds)[i]= NULL; } if(*boot_img_count > 0) { Xorriso_alloc_meM(*boot_imgs, char *, *boot_img_count); for(i= 0; i < *boot_img_count; i++) (*boot_imgs)[i]= NULL; } /* Record commands */ ret= Xorriso_scan_report_lines(xorriso, et_lines, et_line_count, sa_lines, sa_line_count, *cmds, cmd_count, *boot_imgs, boot_img_count, flag & 6); if(ret <= 0) goto ex; ret= 1; ex: if(ret <= 0 || (flag & (1 << 15))) { if(*cmds != NULL) { for(i= 0; i < *cmd_count; i++) if((*cmds)[i] != NULL) Xorriso_free_meM((*cmds)[i]); Xorriso_free_meM(*cmds); *cmds= NULL; } if(*boot_imgs != NULL) { for(i= 0; i < *boot_img_count; i++) if((*boot_imgs)[i] != NULL) Xorriso_free_meM((*boot_imgs)[i]); Xorriso_free_meM(*boot_imgs); *boot_imgs= NULL; } } return(ret); } static void Xorriso_report_lines(struct XorrisO *xorriso, char **lines, int line_count) { int i; for(i = 0; i < line_count ; i++) { sprintf(xorriso->result_line, "%s\n", lines[i]); Xorriso_result(xorriso,0); } } /* @param flag bit0= report El Torito rather than System Area bit1= with form "cmd" do not report but rather execute */ int Xorriso_report_system_area(struct XorrisO *xorriso, char *form, int flag) { int ret, line_count, cmd_count= 0, et_line_count= 0, sa_line_count= 0; int do_cmd= 0, as_mkisofs= 0, i, bin_count, boot_img_count= 0; char **lines = NULL, **et_lines= NULL, **sa_lines= NULL, **cmds= NULL; char **boot_imgs= NULL; uint8_t guid[16]; IsoImage *image; if(strcmp(form, "cmd") == 0 || strcmp(form, "as_mkisofs") == 0 || (flag & 2)) do_cmd= 1; if(strcmp(form, "as_mkisofs") == 0) as_mkisofs= 1; if(strcmp(form, "help") == 0) { if(flag & 1) ret= iso_image_report_el_torito(NULL, &et_lines, &et_line_count, 1); else ret= iso_image_report_system_area(NULL, &sa_lines, &sa_line_count, 1); if(ret <= 0) goto ex; sprintf(xorriso->result_line, "------------------------------------------------------------------------------\n"); Xorriso_result(xorriso, 0); if(flag & 1) sprintf(xorriso->result_line, "With -report_el_torito \"plain\":\n"); else sprintf(xorriso->result_line, "With -report_system_area \"plain\":\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "------------------------------------------------------------------------------\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); } else if(strcmp(form, "") == 0 || strcmp(form, "plain") == 0 || do_cmd) { ret= Xorriso_get_volume(xorriso, &image, 0); if(ret <= 0) goto ex; if(do_cmd || (flag & 1)) ret= iso_image_report_el_torito(image, &et_lines, &et_line_count, 0); if(ret < 0) goto ex; if(do_cmd || !(flag & 1)) ret= iso_image_report_system_area(image, &sa_lines, &sa_line_count, 0); if(ret < 0) goto ex; if(do_cmd) { ret= Xorriso_report_to_cmd(xorriso, et_lines, et_line_count, sa_lines, sa_line_count, &cmds, &cmd_count, &boot_imgs, &boot_img_count, (flag & 1) | (as_mkisofs << 1)); if(ret <= 0) goto ex; } } else if(strncmp(form, "gpt_crc_of:", 11) == 0 && !(flag & 1)) { ret = Xorriso_gpt_crc(xorriso, form + 11, 0); goto ex; } else if(strcmp(form, "make_guid") == 0 && !(flag & 1)) { ret= Xorriso_make_guid(xorriso, xorriso->result_line, 0); if(ret < 0) goto ex; strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso,0); goto ex; } else if(strcmp(form, "gpt_disk_guid") == 0 && !(flag & 1)) { ret= Xorriso_get_volume(xorriso, &image, 0); if(ret <= 0) goto ex; ret= iso_image_report_system_area(image, &sa_lines, &sa_line_count, 0); if(ret <= 0) goto ex; for(i= 0; i < sa_line_count; i++) { if(strncmp(sa_lines[i], "GPT disk GUID : ", 26) == 0) { ret= Hex_to_bin(sa_lines[i] + 26, 16, &bin_count, guid, 0); if(ret < 0 || bin_count != 16) break; Xorriso_format_guid(xorriso, guid, xorriso->result_line, 0); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso,0); ret= 1; goto ex; } } ret= 1; goto ex; } else { sprintf(xorriso->info_text, "%s form parameter not recognized: ", flag & 1 ? "-report_el_torito" : "-report_system_area"); Text_shellsafe(form, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(ret < 0) goto ex; if(flag & 1) { lines= et_lines; line_count= et_line_count; } else { lines= sa_lines; line_count= sa_line_count; } if(!do_cmd) { if(lines == NULL || ret == 0) { if(flag & 1) strcpy(xorriso->info_text, "No El Torito information was loaded"); else strcpy(xorriso->info_text, "No System Area was loaded"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 2; goto ex; } if(line_count == 0) { if(flag & 1) strcpy(xorriso->info_text, "No El Torito information available"); else strcpy(xorriso->info_text, "System Area only contains 0-bytes"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 2; goto ex; } } if(flag & 2) { if(cmd_count > 0) { ret= Xorriso_execute_option(xorriso, "-boot_image any discard -boot_image any system_area=/dev/zero", 1 | 16); if(ret <= 0) goto ex; for(i= 0; i < cmd_count; i++) { ret= Xorriso_execute_option(xorriso, cmds[i], 1 | 16); if(ret <= 0) goto ex; } sprintf(xorriso->info_text, "Replayed %d boot related commands", cmd_count); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } else { Xorriso_msgs_submit(xorriso, 0, "No proposals available for boot related commands", 0, "NOTE", 0); ret= 2; goto ex; } } else if(do_cmd) { Xorriso_report_lines(xorriso, cmds, cmd_count); } else { Xorriso_report_lines(xorriso, lines, line_count); } ret= 1; ex:; Xorriso_report_to_cmd(xorriso, NULL, 0, NULL, 0, &cmds, &cmd_count, &boot_imgs, &boot_img_count, 1 << 15); if(et_lines != NULL) iso_image_report_el_torito(NULL, &et_lines, &et_line_count, 1 << 15); if(sa_lines != NULL) iso_image_report_system_area(NULL, &sa_lines, &sa_line_count, 1 << 15); return(ret); } /* @param flag bit15= dispose imgs */ int Xorriso_list_boot_images(struct XorrisO *xorriso, char ***imgs, int *img_count, int flag) { int ret, cmd_count= 0, et_line_count= 0, sa_line_count= 0, boot_img_count= 0; char **et_lines= NULL, **sa_lines= NULL, **cmds= NULL, **boot_imgs= NULL; IsoImage *image; if(flag & (1 << 15)) { boot_imgs= *imgs; boot_img_count= *img_count; Xorriso_report_to_cmd(xorriso, NULL, 0, NULL, 0, &cmds, &cmd_count, &boot_imgs, &boot_img_count, 1 << 15); *imgs= NULL; *img_count= 0; return(1); } *imgs= NULL; *img_count= 0; ret= Xorriso_get_volume(xorriso, &image, 0); if(ret <= 0) goto ex; ret= iso_image_report_el_torito(image, &et_lines, &et_line_count, 0); if(ret < 0) goto ex; ret= iso_image_report_system_area(image, &sa_lines, &sa_line_count, 0); if(ret < 0) goto ex; ret= Xorriso_report_to_cmd(xorriso, et_lines, et_line_count, sa_lines, sa_line_count, &cmds, &cmd_count, &boot_imgs, &boot_img_count, 4); if(ret <= 0) goto ex; *imgs= boot_imgs; *img_count= boot_img_count; boot_imgs= NULL; boot_img_count= 0; ret= 1; ex:; Xorriso_report_to_cmd(xorriso, NULL, 0, NULL, 0, &cmds, &cmd_count, &boot_imgs, &boot_img_count, 1 << 15); if(et_lines != NULL) iso_image_report_el_torito(NULL, &et_lines, &et_line_count, 1 << 15); if(sa_lines != NULL) iso_image_report_system_area(NULL, &sa_lines, &sa_line_count, 1 << 15); return(ret); } int Xorriso_decode_lfa_flags(struct XorrisO *xorriso, char *flags_text, uint64_t *lfa_flags, int flag) { int ret; ret= iso_util_decode_lfa_flags(flags_text, lfa_flags, 0); if(ret < 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when converting chattr attribute letters", 0, "WARNING", 1); return(0); } return(1); } /* @param flags_text will return the resulting text in allocated memory. @param flag bit0= produce lsattr(1) format with '-' and peculiar sequence */ int Xorriso_encode_lfa_flags(struct XorrisO *xorriso, uint64_t lfa_flags, char **flags_text, int flag) { int ret; ret= iso_util_encode_lfa_flags(lfa_flags, flags_text, flag & 1); if(ret < 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when converting chattr attribute bits", 0, "WARNING", 1); return(0); } return(1); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of functions which operate on ISO images and their global properties. */ #ifndef Xorriso_pvt_iso_img_includeD #define Xorriso_pvt_iso_img_includeD yes int Xorriso_update_volid(struct XorrisO *xorriso, int flag); int Xorriso_record_boot_info(struct XorrisO *xorriso, int flag); int Xorriso_assert_volid(struct XorrisO *xorriso, off_t msc1, int flag); int Xorriso_is_isohybrid(struct XorrisO *xorriso, IsoFile *bootimg_node, int flag); int Xorriso_boot_item_status(struct XorrisO *xorriso, char *cat_path, char *bin_path, int platform_id, int patch_isolinux, int emul, off_t load_size, unsigned char *id_string, unsigned char *selection_crit, char *form, char *filter, FILE *fp, int flag); int Xorriso__append_boot_params(char *line, ElToritoBootImage *bootimg, int flag); int Xorriso_get_volume(struct XorrisO *xorriso, IsoImage **volume, int flag); int Xorriso_record_cmd_line(struct XorrisO *xorriso, char *buf, char **cmds, int *cmd_count, int flag); #endif /* ! Xorriso_pvt_iso_img_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains functions which manipulate the libisofs tree model. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" #include "lib_mgt.h" #include "iso_img.h" #include "iso_tree.h" #include "iso_img.h" #include "iso_manip.h" #include "sort_cmp.h" #include "parse_exec.h" #include "write_run.h" #include "disk_ops.h" /* @param flag bit0= give directory x-permission where is r-permission bit1= do not transfer ACL, xattr, file attribute flags bit2= record dev,inode (only if enabled by xorriso) bit3= with bit0: pretend to have indeed a directory bit5= transfer ACL or xattr from eventual link target */ int Xorriso_transfer_properties(struct XorrisO *xorriso, struct stat *stbuf, char *disk_path, IsoNode *node, int flag) { mode_t mode; int ret= 1, max_bit, os_errno; uint64_t lfa_flags; size_t num_attrs= 0, *value_lengths= NULL; char **names= NULL, **values= NULL; mode= stbuf->st_mode; if((!(flag & 2)) && !(xorriso->do_aaip & 1)) /* Will drop ACL. Update mode S_IRWXG by eventual group:: ACL entry */ iso_local_get_perms_wo_acl(disk_path, &mode, flag & 32); if((flag & 1) && ((flag & 8) || S_ISDIR(mode))) { if(mode&S_IRUSR) mode|= S_IXUSR; if(mode&S_IRGRP) mode|= S_IXGRP; if(mode&S_IROTH) mode|= S_IXOTH; } iso_node_set_permissions(node, mode & 07777); iso_node_set_uid(node, stbuf->st_uid); iso_node_set_gid(node, stbuf->st_gid); iso_node_set_atime(node, stbuf->st_atime); iso_node_set_mtime(node, stbuf->st_mtime); iso_node_set_ctime(node, stbuf->st_ctime); if((xorriso->do_aaip & (1 | 4 | 2048)) && !(flag & 2)) { ret= iso_local_get_attrs(disk_path, &num_attrs, &names, &value_lengths, &values, ((xorriso->do_aaip & 1) && !(flag & 2)) | ((!(xorriso->do_aaip & 4)) << 2) | (flag & 32)); if(ret < 0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, disk_path, ret, "Error when obtaining local ACL and xattr", 0, "FAILURE", 1 | 2); ret= 0; goto ex; } /* Preserve namespace isofs, but not ACL or system xattr */ ret= iso_node_set_attrs(node, num_attrs, names, value_lengths, values, 1 | 8 | 16); if(ret < 0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", ret, "Error when setting ACL and xattr to image node", 0, "FAILURE", 1); ret= 0; goto ex; } if(xorriso->do_aaip & 2048) { ret= iso_local_get_lfa_flags(disk_path, &lfa_flags, &max_bit, &os_errno, (flag & 32) | ((!!(xorriso->do_aaip & (1 << 15))) << 7)); if((xorriso->do_aaip & (1 << 15)) && ret >= 0 && lfa_flags == 0) ret= 4; if(ret < 0) { Xorriso_process_msg_queues(xorriso, 0); Xorriso_report_iso_error(xorriso, disk_path, ret, "Error when obtaining file attribute flags", os_errno, "FAILURE", 1 | 2); ret= 0; goto ex; } else if(ret == 1 || ret == 2) { ret= iso_node_set_lfa_flags(node, lfa_flags, 0); if(ret < 0) { Xorriso_process_msg_queues(xorriso, 0); Xorriso_report_iso_error(xorriso, "", ret, "Error when setting file attribute flags to image node", 0, "FAILURE", 1); ret= 0; goto ex; } } } } if((flag & 4) && ((xorriso->do_aaip & 16) || !(xorriso->ino_behavior & 2))) { ret= Xorriso_record_dev_inode(xorriso, disk_path, (dev_t) 0, (ino_t) 0, (void *) node, "", flag & 32); if(ret <= 0) goto ex; } ret= 1; ex:; Xorriso_process_msg_queues(xorriso,0); iso_local_get_attrs(disk_path, &num_attrs, &names, &value_lengths, &values, 1 << 15); /* free memory */ return(ret); } int Xorriso_graft_split(struct XorrisO *xorriso, IsoImage *volume, IsoDir *dir, char *disk_path, char *img_name, char *nominal_source, char *nominal_target, off_t size, IsoNode **node, int flag) { int ret; IsoDir *new_dir= NULL; IsoNode *part_node; int partno, total_parts; off_t offset; char *part_name= NULL; struct stat stbuf; Xorriso_alloc_meM(part_name, char, SfileadrL); ret= iso_image_add_new_dir(volume, dir, img_name, &new_dir); if(ret < 0) goto ex; *node= (IsoNode *) new_dir; if(lstat(disk_path, &stbuf) != -1) Xorriso_transfer_properties(xorriso, &stbuf, disk_path, *node, 1 | 8); if(xorriso->update_flags & 1) { ret= Xorriso_mark_update_merge(xorriso, img_name, node, 1); if(ret <= 0) {ret= 0; goto ex;} } total_parts= size / xorriso->split_size; if(size % xorriso->split_size) total_parts++; for(partno= 1; partno<=total_parts; partno++) { offset = xorriso->split_size * (off_t) (partno-1); Splitpart__compose(part_name, partno, total_parts, offset, xorriso->split_size, size, 0); ret= Xorriso_tree_graft_node(xorriso, volume, new_dir, disk_path, part_name, nominal_source, nominal_target, offset, xorriso->split_size, &part_node, 8); if(ret<=0) goto ex; } sprintf(xorriso->info_text, "Split into %d parts: ", total_parts); Text_shellsafe(nominal_target, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 1; ex:; Xorriso_free_meM(part_name); return(ret); } /* @param flag bit0= ISO_NODE_NAME_NOT_UNIQUE exception mode: Do not issue message. Return existing node into *node. bit1= if name truncation happens: copy truncated into img_name bit3= cut_out_node: offset and size are valid bit8= hide in iso_rr bit9= hide in joliet bit10= hide in hfsplus */ int Xorriso_tree_graft_node(struct XorrisO *xorriso, IsoImage *volume, IsoDir *dir, char *disk_path, char *img_name, char *nominal_source, char *nominal_target, off_t offset, off_t cut_size, IsoNode **node, int flag) { int ret, stbuf_valid= 0; struct stat stbuf; char *namept, *eff_name, *trunc_name= NULL; off_t size= 0; eff_name= img_name; if(lstat(disk_path, &stbuf) != -1) { stbuf_valid= 1; if(S_ISREG(stbuf.st_mode)) size= stbuf.st_size; } if((int) strlen(eff_name) > xorriso->file_name_limit) { Xorriso_alloc_meM(trunc_name, char, SfileadrL); strncpy(trunc_name, eff_name, SfileadrL - 1); trunc_name[SfileadrL - 1]= 0; ret= iso_truncate_leaf_name(1, xorriso->file_name_limit, trunc_name, 0); if(ret < 0) goto ex; strcpy(xorriso->info_text, "File name had to be truncated and MD5 marked: "); Text_shellsafe(eff_name, xorriso->info_text, 1); strcat(xorriso->info_text, " -> "); Text_shellsafe(trunc_name, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); eff_name= trunc_name; if(flag & 2) strcpy(img_name, trunc_name); } if(flag&8) { if(cut_size > xorriso->file_size_limit && xorriso->file_size_limit > 0) { sprintf(xorriso->info_text, "File piece exceeds size limit of %.f bytes: %.f from ", (double) xorriso->file_size_limit, (double) cut_size); Text_shellsafe(disk_path, xorriso->info_text, 1); strcat(xorriso->info_text, "\n"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= iso_tree_add_new_cut_out_node(volume, dir, eff_name, disk_path, offset, cut_size, node); if(ret<0) goto ex; } else { if(xorriso->split_size > 0 && size > xorriso->split_size) { ret= Xorriso_graft_split(xorriso, volume, dir, disk_path, eff_name, nominal_source, nominal_target, size, node, 0); if(ret<=0) goto ex; } else if(size > xorriso->file_size_limit && xorriso->file_size_limit > 0) { sprintf(xorriso->info_text, "File exceeds size limit of %.f bytes: ", (double) xorriso->file_size_limit); Text_shellsafe(disk_path, xorriso->info_text, 1); strcat(xorriso->info_text, "\n"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } else { ret= iso_tree_add_new_node(volume, dir, eff_name, disk_path, node); if(ret<0) goto ex; } } if(flag & (256 | 512 | 1024)) { ret= Xorriso_set_hidden(xorriso, (void *) *node, "", (flag >> 8) & 7, 0); if(ret <= 0) goto ex; } if(stbuf_valid && ((xorriso->do_aaip & 16) || !(xorriso->ino_behavior & 2))) { ret= Xorriso_record_dev_inode(xorriso, disk_path, stbuf.st_dev, stbuf.st_ino, (void *) *node, "", 1); if(ret <= 0) goto ex; } if(xorriso->update_flags & 1) { ret= Xorriso_mark_update_merge(xorriso, eff_name, *node, 1); if(ret <= 0) goto ex; } ex:; if(ret<0) { if(ret == (int) ISO_NODE_NAME_NOT_UNIQUE && (flag & 1)) { iso_image_dir_get_node(volume, dir, eff_name, node, 0); } else { Xorriso_process_msg_queues(xorriso,0); if(ret == (int) ISO_RR_NAME_TOO_LONG || ret == (int) ISO_RR_NAME_RESERVED || ret == (int) ISO_RR_PATH_TOO_LONG) namept= nominal_target; else namept= nominal_source; Xorriso_report_iso_error(xorriso, namept, ret, "Cannot add node to tree", 0, "FAILURE", 1|2); } } else { if(LIBISO_ISREG(*node)) xorriso->pacifier_byte_count+= iso_file_get_size((IsoFile *) *node); ret= 1; } Xorriso_free_meM(trunc_name); return(ret); } /* @param boss_iter Opaque handle to be forwarded to actions in ISO image Set to NULL if calling this function without having a boss iterator object. @param node Pointer to pointer to existing node, *node is set to NULL, if the node gets removed. @param flag bit0= source is directory bit4= return 3 on rejection by exclusion or user bit6= do not delete eventually existing node from di_array bit7= no special handling of split file directories @return 1= no action was needed, 2= target removed, 3= rejected with bit4, <=0 means error */ int Xoriso_handle_collision(struct XorrisO *xorriso, void *boss_iter, IsoNode **node, char *img_path, char *full_img_path, char *disk_path, char *show_path, int flag) { int ret, target_is_dir, target_is_split, source_is_dir; source_is_dir= flag & 1; target_is_dir= LIBISO_ISDIR(*node); target_is_split= 0; if(target_is_dir && !(flag & 128)) target_is_split= Xorriso_is_split(xorriso, "", (void *) *node, 1 | 2); if(!((target_is_dir && !target_is_split) && source_is_dir)) { Xorriso_process_msg_queues(xorriso, 0); /* handle overwrite situation */; if(xorriso->do_overwrite == 1 || (xorriso->do_overwrite == 2 && !(target_is_dir && !target_is_split))) { ret= Xorriso_rmi(xorriso, boss_iter, (off_t) 0, img_path, 1 | 8 | (flag & 64)); if(ret <= 0) return(ret); if(ret == 3) { sprintf(xorriso->info_text, "User revoked adding of: "); Text_shellsafe(show_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(3 * !!(flag & 16)); } *node= NULL; return(2); } if (disk_path[0]) Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0); if(strcmp(full_img_path, img_path) == 0) sprintf(xorriso->info_text, "While grafting '%s' : file object exists and may not be overwritten", img_path); else sprintf(xorriso->info_text, "While grafting '%s' : '%s' exists and may not be overwritten", full_img_path, img_path); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* @param flag bit0= recursion is active bit1= do not report added files bit6= do not delete eventually existing node from di_array bit7= no special handling of split file directories bit8= hide in iso_rr bit9= hide in joliet */ int Xorriso_add_tree(struct XorrisO *xorriso, IsoDir *dir, char *img_dir_path, char *disk_dir_path, struct LinkiteM *link_stack, int flag) { IsoImage *volume; IsoNode *node; int ret, source_is_dir, source_is_link, fret, was_failure= 0; int do_not_dive, hide_attrs; struct DirseQ *dirseq= NULL; char *name, *img_name, *srcpt, *stbuf_src= ""; struct stat stbuf, hstbuf; dev_t dir_dev; struct LinkiteM *own_link_stack = NULL; char *sfe= NULL, *sfe2= NULL; char *disk_path= NULL, *img_path= NULL, *link_target= NULL; #define Xorriso_add_handle_collisioN 1 #define Xorriso_optimistic_add_treE 1 #ifndef Xorriso_optimistic_add_treE #ifndef Xorriso_add_handle_collisioN int target_is_split= 0, target_is_dir; #endif #endif /* Avoiding large local memory objects in order to save stack space */ sfe= malloc(5*SfileadrL); sfe2= malloc(5*SfileadrL); disk_path= malloc(2*SfileadrL); img_path= malloc(2*SfileadrL); link_target= calloc(SfileadrL, 1); if(sfe==NULL || sfe2==NULL || disk_path==NULL || img_path==NULL || link_target==NULL) { Xorriso_no_malloc_memory(xorriso, &sfe, 0); {ret= -1; goto ex;} } own_link_stack= link_stack; ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; stbuf_src= disk_dir_path; if(lstat(disk_dir_path, &stbuf)==-1) goto cannot_open_dir; dir_dev= stbuf.st_dev; if(S_ISLNK(stbuf.st_mode)) { if(!(xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&1)))) {ret= 2; goto ex;} stbuf_src= disk_dir_path; if(stat(disk_dir_path, &stbuf)==-1) goto cannot_open_dir; if(dir_dev != stbuf.st_dev && !(xorriso->do_follow_mount || (xorriso->do_follow_param && !(flag&1)))) {ret= 2; goto ex;} } ret= Dirseq_new(&dirseq, disk_dir_path, 1); if(ret<0) { sprintf(xorriso->info_text,"Failed to create source filesystem iterator"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } if(ret==0) { cannot_open_dir:; Xorriso_msgs_submit(xorriso, 0, disk_dir_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text,"Cannot open as source directory: %s", Text_shellsafe(disk_dir_path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(Sfile_str(disk_path, disk_dir_path,0)<=0) {ret= -1; goto ex;} if(disk_path[0]==0 || disk_path[strlen(disk_path)-1]!='/') strcat(disk_path,"/"); name= disk_path+strlen(disk_path); if(Sfile_str(img_path, img_dir_path, 0)<=0) {ret= -1; goto ex;} if(img_path[0] == 0) strcat(img_path, "/"); else if(img_path[strlen(img_path) - 1] != '/') strcat(img_path, "/"); img_name= img_path+strlen(img_path); while(1) { /* loop over directory content */ stbuf_src= ""; Linkitem_reset_stack(&own_link_stack, link_stack, 0); srcpt= disk_path; Xorriso_process_msg_queues(xorriso,0); ret= Dirseq_next_adr(dirseq,name,0); /* name is a pointer into disk_path */ if(ret==0) break; if(ret<0) { sprintf(xorriso->info_text,"Failed to obtain next directory entry"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } /* Compare exclusions against disk_path resp. name */ ret= Xorriso_path_is_excluded(xorriso, disk_path, 0); /* (is never param) */ if(ret<0) {ret= -1; goto ex;} if(ret>0) continue; /* Check for mkisofs-style hidings */ hide_attrs= (flag >> 8) & 3; if(hide_attrs != 3) { ret= Xorriso_path_is_hidden(xorriso, disk_path, 0); if(ret<0) goto ex; if(ret>=0) hide_attrs|= ret; } strcpy(img_name, name); if(Xorriso_much_too_long(xorriso, strlen(img_path), 0)<=0) {ret= 0; goto was_problem;} if(Xorriso_much_too_long(xorriso, strlen(srcpt), 0)<=0) {ret= 0; goto was_problem;} stbuf_src= srcpt; if(lstat(srcpt, &stbuf)==-1) { cannot_lstat:; Xorriso_msgs_submit(xorriso, 0, srcpt, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "Cannot determine attributes of source file %s", Text_shellsafe(srcpt, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto was_problem; } source_is_dir= 0; source_is_link= S_ISLNK(stbuf.st_mode); if(xorriso->do_follow_links && source_is_link) { /* Xorriso_hop_link checks for wide link loops */ ret= Xorriso_hop_link(xorriso, srcpt, &own_link_stack, &hstbuf, 8); if(ret<0) goto was_problem; if(ret==1) { ret= Xorriso_resolve_link(xorriso, srcpt, link_target, 0); if(ret<=0) goto was_problem; srcpt= link_target; stbuf_src= srcpt; if(lstat(srcpt, &stbuf)==-1) goto cannot_lstat; } else { Xorriso_msgs_submit(xorriso, 0, srcpt, 0, "ERRFILE", 0); if(Xorriso_eval_problem_status(xorriso, 0, 1|2)<0) {ret= 0; goto was_problem;} ret= Xorriso_resolve_link(xorriso, srcpt, link_target, 1); if(ret<=0) goto was_problem; } } else if (S_ISLNK(stbuf.st_mode)) { ret= Xorriso_resolve_link(xorriso, srcpt, link_target, 1); if(ret<=0) goto was_problem; } do_not_dive= 0; if(S_ISDIR(stbuf.st_mode)) { source_is_dir= 1; if(dir_dev != stbuf.st_dev && !xorriso->do_follow_mount) do_not_dive= 1; } #ifdef Xorriso_optimistic_add_treE ret= Xorriso_tree_graft_node(xorriso, volume, dir, srcpt, img_name, "", img_path, (off_t) 0, (off_t) 0, &node, 1 | (hide_attrs << 8)); if(ret == (int) ISO_NODE_NAME_NOT_UNIQUE) { ret= Xoriso_handle_collision(xorriso, NULL, &node, img_path, img_path, srcpt, img_path, (!!source_is_dir) | (flag & (64 | 128))); if(ret <= 0) goto was_problem; if(node == NULL) { ret= Xorriso_tree_graft_node(xorriso, volume, dir, srcpt, img_name, "", img_path, (off_t) 0, (off_t) 0, &node, (hide_attrs << 8)); if(ret <= 0) node= NULL; } } #else /* Xorriso_optimistic_add_treE */ /* does a node exist with this name ? */ node= NULL; if(dir != NULL) { ret= iso_image_get_dir_node(volume, dir, img_name, &node); } else { ret= Xorriso_node_from_path(xorriso, volume, img_path, &node, 1); } if(ret>0) { target_is_dir= LIBISO_ISDIR(node); target_is_split= 0; if(target_is_dir && !(flag & 128)) target_is_split= Xorriso_is_split(xorriso, "", (void *) node, 1 | 2); if(!((target_is_dir && !target_is_split) && source_is_dir)) { Xorriso_process_msg_queues(xorriso,0); /* handle overwrite situation */; if(xorriso->do_overwrite==1 || (xorriso->do_overwrite==2 && !(target_is_dir && !target_is_split))) { ret= Xorriso_rmi(xorriso, NULL, (off_t) 0, img_path, 1 | 8 | (flag & 64)); if(ret<=0) goto was_problem; if(ret==3) { sprintf(xorriso->info_text, "User revoked adding of: %s", Text_shellsafe(img_path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 0; goto was_problem; } node= NULL; } else { Xorriso_msgs_submit(xorriso, 0, srcpt, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "While grafting %s : file object exists and may not be overwritten by %s", Text_shellsafe(img_path,sfe,0), Text_shellsafe(stbuf_src,sfe2,0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto was_problem; } } } if(node==NULL) { ret= Xorriso_tree_graft_node(xorriso, volume, dir, srcpt, img_name, "", img_path, (off_t) 0, (off_t) 0, &node, (hide_attrs << 8)); } #endif /* Xorriso_optimistic_add_treE */ if(node==NULL) { Xorriso_process_msg_queues(xorriso,0); Xorriso_msgs_submit(xorriso, 0, stbuf_src, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "Grafting failed: %s = %s", Text_shellsafe(img_path,sfe,0), Text_shellsafe(stbuf_src,sfe2,0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto was_problem; } xorriso->pacifier_count++; if((xorriso->pacifier_count%100)==0) Xorriso_pacifier_callback(xorriso, "files added", xorriso->pacifier_count, xorriso->pacifier_total, "", 0); Xorriso_set_change_pending(xorriso, 0); if(source_is_dir) { if(do_not_dive) { sprintf(xorriso->info_text, "Did not follow mount point : %s", Text_shellsafe(disk_path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } else { ret= Xorriso_add_tree(xorriso, (IsoDir *) node, img_path, disk_path, own_link_stack, 1 | (flag & (2 | 64 | 128))); } if(ret<=0) goto was_problem; } continue; /* regular bottom of loop */ was_problem:; was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret<0) goto ex; } ret= 1; ex: if(sfe!=NULL) free(sfe); if(sfe2!=NULL) free(sfe2); if(disk_path!=NULL) free(disk_path); if(img_path!=NULL) free(img_path); if(link_target!=NULL) free(link_target); Xorriso_process_msg_queues(xorriso,0); Linkitem_reset_stack(&own_link_stack, link_stack, 0); Dirseq_destroy(&dirseq, 0); if(ret<=0) return(ret); return(!was_failure); } /* @param flag bit0= cut_out mode : base on leaf parent directory bit1= do not check and perform hidings */ int Xorriso_copy_implicit_properties(struct XorrisO *xorriso, IsoDir *dir, char *full_img_path, char *img_path, char *full_disk_path, int flag) { int ret, nfic, nic, nfdc, d, i; char *nfi= NULL, *ni= NULL, *nfd= NULL, *cpt; struct stat stbuf; Xorriso_alloc_meM(nfi, char, SfileadrL); Xorriso_alloc_meM(ni, char, SfileadrL); Xorriso_alloc_meM(nfd, char, SfileadrL); ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, full_img_path, nfi, 1|2); if(ret<=0) goto ex; ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, img_path, ni, 1|2); if(ret<=0) goto ex; ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, full_disk_path, nfd, 1|2|4); if(ret<=0) goto ex; nfic= Sfile_count_components(nfi, 0); nic= Sfile_count_components(ni, 0); nfdc= Sfile_count_components(nfd, 0); d= nfic-(flag&1)-nic; if(d<0) {ret= -1; goto ex;} if(d>nfdc) {ret= 0; goto ex;} for(i= 0; i<d; i++) { cpt= strrchr(nfd, '/'); if(cpt==NULL) {ret= -1; goto ex;} /* should not happen */ *cpt= 0; } if(nfd[0]==0) strcpy(nfd, "/"); if(stat(nfd, &stbuf)==-1) {ret= 0; goto ex;} Xorriso_transfer_properties(xorriso, &stbuf, nfd, (IsoNode *) dir, ((8 | 1) * ((flag&1) && d==0)) | 4 | 32); sprintf(xorriso->info_text, "Copied properties for "); Text_shellsafe(ni, xorriso->info_text, 1); sprintf(xorriso->info_text+strlen(xorriso->info_text), " from "); Text_shellsafe(nfd, xorriso->info_text, 1); if(!((flag&1) && d==0)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); if(!(flag & 2)) { /* Check for mkisofs-style hidings */ ret= Xorriso_path_is_hidden(xorriso, nfd, 0); if(ret<0) goto ex; if(ret>=0) { /* Hide dir */ ret= Xorriso_set_hidden(xorriso, (void *) dir, "", ret, 0); if(ret <= 0) goto ex; } } ret= 1; ex: Xorriso_free_meM(nfi); Xorriso_free_meM(ni); Xorriso_free_meM(nfd); return(ret); } /* @param bit0= copy link target properties rather than link properties bit1= give directory x-permission where is r-permission bit2= record dev,inode (only if enabled by xorriso) */ int Xorriso_copy_properties(struct XorrisO *xorriso, char *disk_path, char *img_path, int flag) { int ret; IsoNode *node; struct stat stbuf; ret= Xorriso_get_node_by_path(xorriso, img_path, NULL, &node, 0); if(ret<=0) return(ret); if(flag & 1) { if(stat(disk_path, &stbuf)==-1) return(0); } else { if(lstat(disk_path, &stbuf)==-1) return(0); } Xorriso_transfer_properties(xorriso, &stbuf, disk_path, node, ((flag & 2) >> 1) | ((flag & 1) << 5) | (flag & 4)); Xorriso_set_change_pending(xorriso, 0); return(1); } int Xorriso_add_symlink(struct XorrisO *xorriso, IsoDir *parent, char *link_target, char *leaf_name, char *nominal_path, int flag) { int ret= 0; IsoSymlink *link= NULL; IsoImage *volume; ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret <= 0) return(ret); ret= iso_image_add_new_symlink(volume, parent, leaf_name, link_target, &link); Xorriso_process_msg_queues(xorriso,0); if(ret < 0) { Xorriso_report_iso_error(xorriso, nominal_path, ret, "Cannot create symbolic link", 0, "FATAL", 1); ret= 0; } return(ret); } /* @param boss_iter Opaque handle to be forwarded to actions in ISO image Set to NULL if calling this function from outside ISO world @param flag bit0= mkdir: graft in as empty directory, not as copy from disk bit1= do not report added files bit2= -follow, -not_*: this is not a command parameter bit3= use offset and cut_size for cut_out_node bit4= return 3 on rejection by exclusion or user bit5= if directory then do not add sub tree bit6= do not delete eventually existing node from di_array bit7= no special handling of split file directories bit8= hide in iso_rr bit9= hide in joliet bit10= ln -s: graft in as symbolic link. Link target is handed over in parameter disk_path. @return <=0 = error , 1 = added simple node , 2 = added directory , 3 = rejected */ int Xorriso_graft_in(struct XorrisO *xorriso, void *boss_iter, char *disk_path, char *img_path, off_t offset, off_t cut_size, int flag) { IsoImage *volume; char *path= NULL, *apt, *npt, *cpt; char *disk_path_pt, *resolved_disk_path= NULL; IsoDir *dir= NULL, *hdir; IsoNode *node; int done= 0, is_dir= 0, l, ret, source_is_dir, resolve_link= 0; int hide_attrs; struct stat stbuf; #define Xorriso_graft_handle_collisioN 1 #define Xorriso_optimistic_graft_iN 1 #ifndef Xorriso_optimistic_graft_iN #ifndef Xorriso_graft_handle_collisioN int target_is_split, target_is_dir; #endif #endif Xorriso_alloc_meM(path, char, SfileadrL); Xorriso_alloc_meM(resolved_disk_path, char, SfileadrL); hide_attrs= (flag >> 8) & 3; if (disk_path == NULL && !(flag & 1)) { Xorriso_msgs_submit(xorriso, 0, "Program error: Xorriso_graft_in(): disk_path == NULL && !(flag & 1)", 0, "ABORT", 0); {ret= -1; goto ex;} } if (disk_path == NULL) { disk_path= ""; } else { ret= Xorriso_path_is_excluded(xorriso, disk_path, !(flag&4)); if(ret<0) goto ex; if(ret>0) {ret= 3*!!(flag&16); goto ex;} /* Check for mkisofs-style hidings */ if(hide_attrs != 3) { ret= Xorriso_path_is_hidden(xorriso, disk_path, 0); if(ret<0) goto ex; if(ret>=0) hide_attrs|= ret; } } for(cpt= img_path; 1; cpt++) { cpt= strstr(cpt,"/."); if(cpt==NULL) break; if(cpt[2]=='.') { if(cpt[3]=='/' || cpt[3]==0) break; } else if(cpt[2]=='/' || cpt[2]==0) break; } if(cpt!=NULL) { if(disk_path[0]) Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "Unsupported relative addressing in iso_rr_path "); Text_shellsafe(img_path, xorriso->info_text, 1); if(disk_path[0]) { strcat(xorriso->info_text, " (disk: "); Text_shellsafe(disk_path, xorriso->info_text, 1); strcat(xorriso->info_text, ")"); } Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); {ret= 0; goto ex;} } ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; strncpy(path, img_path, SfileadrL - 1); path[SfileadrL - 1]= 0; apt= npt= path; if(!(flag & (1 | 1024))) { if(disk_path[0] == 0) { Xorriso_msgs_submit(xorriso, 0, "/", 0, "ERRFILE", 0); sprintf(xorriso->info_text, "Will not graft-in the whole local filesystem by path '/'"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } ret= lstat(disk_path, &stbuf); if(ret!=-1) { if(S_ISDIR(stbuf.st_mode)) is_dir= 1; else if((stbuf.st_mode&S_IFMT)==S_IFLNK && (xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&4)))) { resolve_link= 1; ret= stat(disk_path, &stbuf); if(ret!=-1) { if(S_ISDIR(stbuf.st_mode)) is_dir= 1; } } } if(ret == -1) { Xorriso_process_msg_queues(xorriso,0); Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "Cannot determine attributes of source file "); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); {ret= 0; goto ex;} } if(S_ISDIR(stbuf.st_mode)) { is_dir= 1; } else { l= strlen(img_path); if(l>0) if(img_path[l-1]=='/') l= 0; if(l==0) { Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "Source "); Text_shellsafe(disk_path, xorriso->info_text, 1); strcat(xorriso->info_text, " is not a directory. Target "); Text_shellsafe(img_path, xorriso->info_text, 1); strcat(xorriso->info_text, " would be."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } } } dir= iso_image_get_root(volume); if(dir==NULL) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "While grafting '%s' : no root node available", img_path); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= 0; goto ex;} } for(npt= apt; !done; apt= npt+1) { npt= strchr(apt, '/'); if(npt==NULL) { npt= apt+strlen(apt); done= 1; } else *npt= 0; if(*apt==0) { *apt= '/'; apt++; if(done) goto attach_source; continue; } source_is_dir= (is_dir || (flag&1) || !done); #ifdef Xorriso_optimistic_graft_iN /* Directories of the source path are likely to exist already as directory in the image. That will cause two lookups with optimistic, and only one with pessimistic. So optimism will pay off only with the leaf. I.e. if(done). */ if(source_is_dir) { /* eventually create directory */ ret= iso_image_dir_get_node(volume, dir, apt, &node, 0); if(ret == 1) { ret= Xoriso_handle_collision(xorriso, boss_iter, &node, path, img_path, disk_path, disk_path[0] ? disk_path : img_path, (!!source_is_dir) | (flag & (16 | 64 | 128))); if(ret <= 0 || ret == 3) goto ex; if(ret == 1 && node != NULL) dir= (IsoDir *) node; } else node= NULL; if(node == NULL) { ret= iso_image_add_new_dir(volume, dir, apt, &hdir); if(ret < 0) { Xorriso_process_msg_queues(xorriso,0); if(disk_path[0]) Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0); Xorriso_report_iso_error(xorriso, img_path, ret, "Cannot create directory", 0, "FAILURE", 1); sprintf(xorriso->info_text, "While grafting '%s' : could not insert '%s'", img_path, path); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(xorriso->update_flags & 1) { ret= Xorriso_mark_update_merge(xorriso, path, (IsoNode *) hdir, 1); if(ret <= 0) {ret= 0; goto ex;} } dir= hdir; Xorriso_set_change_pending(xorriso, 0); iso_node_set_ctime((IsoNode *) dir, time(NULL)); iso_node_set_uid((IsoNode *) dir, geteuid()); iso_node_set_gid((IsoNode *) dir, getegid()); if(disk_path[0] && !done) { /* This not only copies disk directory properties but also sets eventual hide_attrs */ Xorriso_copy_implicit_properties(xorriso, dir, img_path, path, disk_path, !!(flag&8)); } } } if(done) { attach_source:; if(flag&1) { /* directory node was created above */; } else if(flag & 1024) { ret= Xorriso_add_symlink(xorriso, dir, disk_path, apt, img_path, 0); if(ret <= 0) goto ex; Xorriso_set_change_pending(xorriso, 0); } else if(is_dir) { Xorriso_transfer_properties(xorriso, &stbuf, disk_path, (IsoNode *) dir, 4 | 32); if(!(flag&32)) { ret= Xorriso_add_tree(xorriso, dir, img_path, disk_path, NULL, flag & (2 | 64 | 128)); if(ret<=0) goto ex; } } else { if(resolve_link) { ret= Xorriso_resolve_link(xorriso, disk_path, resolved_disk_path, 0); if(ret<=0) goto ex; disk_path_pt= resolved_disk_path; } else disk_path_pt= disk_path; ret= Xorriso_tree_graft_node(xorriso, volume, dir, disk_path_pt, apt, disk_path, img_path, offset, cut_size, &node, 1 | (flag & 8) | (hide_attrs << 8)); if(ret == (int) ISO_NODE_NAME_NOT_UNIQUE) { ret= Xoriso_handle_collision(xorriso, boss_iter, &node, img_path, img_path, disk_path, disk_path[0] ? disk_path : img_path, (flag & (16 | 64 | 128))); if(ret <= 0 || ret == 3) goto ex; ret= Xorriso_tree_graft_node(xorriso, volume, dir, disk_path_pt, apt, disk_path, img_path, offset, cut_size, &node, (flag & 8) | (hide_attrs << 8)); } if(ret<=0) { sprintf(xorriso->info_text, "Grafting failed: "); Text_shellsafe(img_path, xorriso->info_text, 1); strcat(xorriso->info_text, " = "); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } Xorriso_set_change_pending(xorriso, 0); /* <<< Why set the name once again ? iso_image_set_node_name(volume, node, apt, 1); */ xorriso->pacifier_count++; if(xorriso->pacifier_count%100 && !(flag&2)) Xorriso_pacifier_callback(xorriso, "files added", xorriso->pacifier_count, xorriso->pacifier_total, "", 0); } } else *npt= '/'; #else /* Xorriso_optimistic_graft_iN */ node= NULL; ret= iso_image_dir_get_node(volume, dir, apt, &node, 0); if(ret == 1) { #ifdef Xorriso_graft_handle_collisioN ret= Xoriso_handle_collision(xorriso, boss_iter, &node, path, img_path, disk_path, disk_path[0] ? disk_path : img_path, (!!source_is_dir) | (flag & (16 | 64 | 128))); if(ret <= 0 || ret == 3) goto ex; if(ret == 2) goto handle_path_node; #else /* Xorriso_graft_handle_collisioN */ target_is_dir= LIBISO_ISDIR(node); target_is_split= 0; if(target_is_dir && !(flag & 128)) target_is_split= Xorriso_is_split(xorriso, "", (void *) node, 1 | 2); if(!((target_is_dir && !target_is_split) && source_is_dir)) { Xorriso_process_msg_queues(xorriso,0); /* handle overwrite situation */; if(xorriso->do_overwrite==1 || (xorriso->do_overwrite==2 && !(target_is_dir && !target_is_split))) { ret= Xorriso_rmi(xorriso, boss_iter, (off_t) 0, path, 1 | 8 | (flag & 64)); if(ret<=0) goto ex; if(ret==3) { sprintf(xorriso->info_text, "User revoked adding of: "); if(disk_path[0]) Text_shellsafe(disk_path, xorriso->info_text, 1); else Text_shellsafe(img_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); {ret= 3*!!(flag&16); goto ex;} } node= NULL; goto handle_path_node; } if (disk_path[0]) Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "While grafting '%s' : '%s' exists and may not be overwritten", img_path, path); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } #endif /* ! Xorriso_graft_handle_collisioN */ dir= (IsoDir *) node; } handle_path_node:; if(node==NULL && source_is_dir) { /* make a directory */ ret= iso_image_add_new_dir(volume, dir, apt, &hdir); if(ret<0) { Xorriso_process_msg_queues(xorriso,0); if(disk_path[0]) Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0); Xorriso_report_iso_error(xorriso, img_path, ret, "Cannot create directory", 0, "FAILURE", 1); sprintf(xorriso->info_text, "While grafting '%s' : could not insert '%s'", img_path, path); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(xorriso->update_flags & 1) { ret= Xorriso_mark_update_merge(xorriso, path, (IsoNode *) hdir, 1); if(ret <= 0) {ret= 0; goto ex;} } dir= hdir; Xorriso_set_change_pending(xorriso, 0); iso_node_set_ctime((IsoNode *) dir, time(NULL)); iso_node_set_uid((IsoNode *) dir, geteuid()); iso_node_set_gid((IsoNode *) dir, getegid()); if(disk_path[0] && !done) /* This not only copies disk directory properties but also sets eventual hide_attrs */ Xorriso_copy_implicit_properties(xorriso, dir, img_path, path, disk_path, !!(flag&8)); } if(done) { attach_source:; if(flag&1) { /* directory node was created above */; } else if(flag & 1024) { ret= Xorriso_add_symlink(xorriso, dir, disk_path, apt, img_path, 0); if(ret <= 0) goto ex; } else if(is_dir) { Xorriso_transfer_properties(xorriso, &stbuf, disk_path, (IsoNode *) dir, 4 | 32); if(!(flag&32)) { ret= Xorriso_add_tree(xorriso, dir, img_path, disk_path, NULL, flag & (2 | 64 | 128)); if(ret<=0) goto ex; } } else { if(resolve_link) { ret= Xorriso_resolve_link(xorriso, disk_path, resolved_disk_path, 0); if(ret<=0) goto ex; disk_path_pt= resolved_disk_path; } else disk_path_pt= disk_path; ret= Xorriso_tree_graft_node(xorriso, volume, dir, disk_path_pt, apt, disk_path, img_path, offset, cut_size, &node, (flag&8) | (hide_attrs << 8)); if(ret<=0) { sprintf(xorriso->info_text, "Grafting failed: "); Text_shellsafe(img_path, xorriso->info_text, 1); strcat(xorriso->info_text, " = "); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } Xorriso_set_change_pending(xorriso, 0); iso_image_set_node_name(volume, node, apt, 1); xorriso->pacifier_count++; if(xorriso->pacifier_count%100 && !(flag&2)) Xorriso_pacifier_callback(xorriso, "files added", xorriso->pacifier_count, xorriso->pacifier_total, "", 0); } } else *npt= '/'; #endif /* ! Xorriso_optimistic_graft_iN */ } Xorriso_process_msg_queues(xorriso,0); ret= 1+!!is_dir; ex:; Xorriso_free_meM(path); Xorriso_free_meM(resolved_disk_path); return(ret); } /* @param flag bit0= -follow: disk_path is not a command parameter */ int Xorriso_cut_out(struct XorrisO *xorriso, char *disk_path, off_t startbyte, off_t bytecount, char *iso_rr_path, int flag) { int ret; char *eff_source= NULL, *eff_dest= NULL; off_t src_size; struct stat stbuf; Xorriso_alloc_meM(eff_source, char, SfileadrL); Xorriso_alloc_meM(eff_dest, char, SfileadrL); ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, disk_path, eff_source, 2 | 4); if(ret<=0) goto ex; ret= Xorriso_path_is_excluded(xorriso, disk_path, !(flag&1)); if(ret!=0) {ret= 0; goto ex;} if(lstat(eff_source, &stbuf)==-1) { Xorriso_msgs_submit(xorriso, 0, eff_source, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "-cut_out: Cannot determine type of "); Text_shellsafe(eff_source, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); {ret= 0; goto ex;} } if((stbuf.st_mode&S_IFMT) == S_IFLNK) { if(!(xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&1)))) goto unsupported_type; if(stat(eff_source, &stbuf)==-1) { Xorriso_msgs_submit(xorriso, 0, eff_source, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "-cut_out: Cannot determine link target type of "); Text_shellsafe(eff_source, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0); {ret= 0; goto ex;} } } if(S_ISREG(stbuf.st_mode)) { src_size= stbuf.st_size; } else if(!(S_ISDIR(stbuf.st_mode) || S_ISLNK(stbuf.st_mode) || S_ISFIFO(stbuf.st_mode) || S_ISSOCK(stbuf.st_mode))) { src_size= startbyte + bytecount; ret= Xorriso_determine_capacity(xorriso, eff_source, &src_size, NULL, 1); if(ret <= 0) goto unsupported_type; if(src_size <= 0) { Xorriso_msgs_submit(xorriso, 0, "-cut_out: Special file with addressable size range of 0 encountered", 0, "FAILURE", 0); goto unsupported_type; } } else { unsupported_type:; Xorriso_msgs_submit(xorriso, 0, eff_source, 0, "ERRFILE", 0); if(S_ISDIR(stbuf.st_mode) || S_ISLNK(stbuf.st_mode) || S_ISFIFO(stbuf.st_mode) || S_ISSOCK(stbuf.st_mode)) { sprintf(xorriso->info_text, "-cut_out: File type (%s) is not suitable for this command: ", Ftypetxt(stbuf.st_mode, 0)); } else { sprintf(xorriso->info_text, "-cut_out: File (%s) does not support or permit random read access: ", Ftypetxt(stbuf.st_mode, 0)); } Text_shellsafe(eff_source, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(src_size < startbyte) { Xorriso_msgs_submit(xorriso, 0, eff_source, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "-cut_out: Byte offset %.f larger than addressable file size %.f : ", (double) startbyte, (double) src_size); Text_shellsafe(eff_source, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, iso_rr_path, eff_dest, 2); if(ret<=0) goto ex; ret= Xorriso_graft_in(xorriso, NULL, eff_source, eff_dest, startbyte, bytecount, 8); ex:; Xorriso_free_meM(eff_source); Xorriso_free_meM(eff_dest); return(ret); } /* @param flag bit0= do not produce info message on success bit1= do not raise protest if directory already exists @return 1=success, 0=was already directory, -1=was other type, -2=other error */ int Xorriso_mkdir(struct XorrisO *xorriso, char *path, int flag) { int ret; char *eff_path= NULL; Xorriso_alloc_meM(eff_path, char, SfileadrL); ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, path, eff_path, 1); if(ret<0) {ret= -2; goto ex;} if(ret>0) { if(ret == 2 && (flag & 2)) {ret= 0; goto ex;} sprintf(xorriso->info_text,"-mkdir: Address already existing "); Text_shellsafe(eff_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, (ret==2 ? "WARNING" : "FAILURE"), 0); {ret= -1 + (ret == 2); goto ex;} } ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, path, eff_path, 2); if(ret<0) {ret= -2; goto ex;} ret= Xorriso_graft_in(xorriso, NULL, NULL, eff_path, (off_t) 0, (off_t) 0, 1); if(ret<=0) {ret= -2; goto ex;} if(!(flag&1)) { sprintf(xorriso->info_text, "Created directory in ISO image: "); Text_shellsafe(eff_path, xorriso->info_text, 1); strcat(xorriso->info_text, "\n"); Xorriso_info(xorriso, 0); } ret= 1; ex:; Xorriso_free_meM(eff_path); return(ret); } /* @param boss_iter If not NULL then this is an iterator suitable for iso_dir_iter_remove() which is then to be used instead of iso_node_remove(). @param flag bit0= remove whole sub tree: rm -r bit1= remove empty directory: rmdir bit2= recursion: do not reassure in mode 2 "tree" bit3= this is for overwriting and not for plain removal bit4= count deleted files in xorriso->pacifier_count bit5= with bit0 only remove directory content, not the directory bit6= do not delete eventually existing node from di_array @return <=0 = error 1 = removed simple node 2 = removed directory or tree 3 = did not remove on user revocation */ int Xorriso_rmi(struct XorrisO *xorriso, void *boss_iter, off_t boss_mem, char *path, int flag) { int ret, is_dir= 0, pl, not_removed= 0, fret; IsoNode *victim_node= NULL, *node; IsoDir *boss_node, *root_dir; IsoDirIter *iter= NULL; IsoImage *volume; char *sub_name, *name; char *sfe= NULL, *sub_path= NULL; off_t mem; IsoNode **node_array= NULL; int node_count= 0, node_idx; /* Avoiding large local memory objects in order to save stack space */ sfe= malloc(5*SfileadrL); sub_path= malloc(2*SfileadrL); if(sfe==NULL || sub_path==NULL) { Xorriso_no_malloc_memory(xorriso, &sfe, 0); {ret= -1; goto ex;} } #ifndef Libisofs_iso_dir_iter_sufficienT /* Ticket 127: A80301 - A80302 I do not not deem IsoDirIter safe for node list manipulations. The parameter boss_iter once was intended to allow such but has now been downgraded to a mere check for eventual programming bugs. */ if(boss_iter!=NULL) { sprintf(xorriso->info_text, "Program error: Xorriso_rmi() was requested to delete iterated node %s", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); ret= -1; goto ex; } #endif /* Libisofs_iso_dir_iter_sufficienT */ ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; if(Xorriso_much_too_long(xorriso, strlen(path), 0)<=0) {ret= 0; goto ex;} ret= Xorriso_node_from_path(xorriso, volume, path, &victim_node, 0); if(ret<=0) goto ex; root_dir= iso_image_get_root(volume); if(((void *) root_dir) == ((void *) victim_node) && !(flag & 1)) { sprintf(xorriso->info_text, "May not delete root directory"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(LIBISO_ISDIR(victim_node)) is_dir= 1; if(!is_dir) { if(flag&2) { /* rmdir */ sprintf(xorriso->info_text, "%s in loaded ISO image is not a directory", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } else { if(flag&1) { /* rm -r */ if((xorriso->do_reassure==1 && !xorriso->request_not_to_ask) || (flag&32) || ((void *) root_dir) == ((void *) victim_node)) { /* Iterate over subordinates and delete them */ mem= boss_mem; ret= Xorriso_findi_iter(xorriso, (IsoDir *) victim_node, &mem, &iter, &node_array, &node_count, &node_idx, &node, 1|2); if(ret<=0) { cannot_create_iter:; Xorriso_cannot_create_iter(xorriso, ret, 0); ret= -1; goto ex; } pl= strlen(path); strcpy(sub_path, path); if(pl==0 || sub_path[pl-1]!='/') { sub_path[pl++]= '/'; sub_path[pl]= 0; } sub_name= sub_path+pl; while(1) { ret= Xorriso_findi_iter(xorriso, (IsoDir *) victim_node, &mem, &iter, &node_array, &node_count, &node_idx, &node, 0); if(ret<0) goto ex; if(ret==0 || xorriso->request_to_abort) break; name= (char *) iso_node_get_name(node); if(Xorriso_much_too_long(xorriso, pl+1+strlen(name), 0)<=0) {ret= 0; goto rm_r_problem_handler;} strcpy(sub_name, name); ret= Xorriso_rmi(xorriso, iter, mem, sub_path, (flag & ( 1 | 2 | 8 | 16 | 64)) | 4); if(ret==3 || ret<=0 || xorriso->request_to_abort) { rm_r_problem_handler:; not_removed= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret<0) goto dir_not_removed; } } if(flag&32) {ret= 2; goto ex;} if(not_removed) { dir_not_removed:; sprintf(xorriso->info_text, "Directory not removed: %s", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); if(ret>0) ret= 3; goto ex; } } } else { if(!(flag&2)) { /* not rmdir */ sprintf(xorriso->info_text, "%s in loaded ISO image is a directory", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } ret= iso_dir_get_children((IsoDir *) victim_node, &iter); Xorriso_process_msg_queues(xorriso,0); if(ret<0) goto cannot_create_iter; if(ret>0) { if(iso_dir_iter_next(iter, &node) == 1) { sprintf(xorriso->info_text, "Directory not empty on attempt to delete: %s", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } } } if(((void *) root_dir) == ((void *) victim_node)) {ret= 2; goto ex;} if(xorriso->request_to_abort) {ret= 3; goto ex;} boss_node= iso_node_get_parent(victim_node); Xorriso_process_msg_queues(xorriso,0); if(boss_node==NULL) { sprintf(xorriso->info_text, "Cannot find parent node of %s in loaded ISO image", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } while((xorriso->do_reassure==1 || (xorriso->do_reassure==2 && !(flag&4))) && !xorriso->request_not_to_ask) { /* ls -ld */ Xorriso_ls_filev(xorriso, xorriso->wdi, 1, &path, (off_t) 0, 1|2|8); if(is_dir) /* du -s */ Xorriso_ls_filev(xorriso, xorriso->wdi, 1, &path, (off_t) 0, 2|4); if(flag&8) sprintf(xorriso->info_text, "File exists. Remove ? n= keep old, y= remove, x= abort, @= stop asking\n"); else sprintf(xorriso->info_text, "Remove above file ? n= keep it, y= remove it, x= abort, @= stop asking\n"); Xorriso_info(xorriso, 4); ret= Xorriso_request_confirmation(xorriso, 1|2|4|16); if(ret<=0) goto ex; if(xorriso->request_to_abort) { sprintf(xorriso->info_text, "Removal operation aborted by user before file: %s", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 3; goto ex; } if(ret==3) continue; if(ret==6) /* yes */ break; if(ret==4) { /* yes, do not ask again */ xorriso->request_not_to_ask= 1; break; } if(ret==1) { /* no */ sprintf(xorriso->info_text, "Kept in existing state: %s", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 3; goto ex; } } if(!(flag & 64)) Xorriso_invalidate_di_item(xorriso, victim_node, 0); #ifdef Libisofs_iso_dir_iter_sufficienT if(boss_iter!=NULL) { ret= iso_dir_iter_remove((IsoDirIter *) boss_iter); if(ret<0) ret= -1; } else ret= iso_node_remove(victim_node); #else /* ! Libisofs_iso_dir_iter_sufficienT */ ret= iso_node_remove(victim_node); #endif /* Libisofs_iso_dir_iter_sufficienT */ Xorriso_process_msg_queues(xorriso,0); if(ret<0) { Xorriso_report_iso_error(xorriso, path, ret, "Cannot remove node", 0, "FATAL", 1); sprintf(xorriso->info_text, "Internal failure to remove %s from loaded ISO image", Text_shellsafe(path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); ret= -1; goto ex; } if(flag&16) xorriso->pacifier_count++; Xorriso_set_change_pending(xorriso, 0); ret= 1+!!is_dir; ex:; if(sfe!=NULL) free(sfe); if(sub_path!=NULL) free(sub_path); Xorriso_findi_iter(xorriso, (IsoDir *) victim_node, &mem, &iter, &node_array, &node_count, &node_idx, &node, (1u<<31)); return(ret); } int Xorriso_overwrite_dest(struct XorrisO *xorriso, void *boss_iter, char *eff_dest, int dest_ret, char *activity, int flag) { int ret; if(dest_ret==2 && xorriso->do_overwrite!=1) { sprintf(xorriso->info_text, "%s: May not overwrite directory: ", activity); Text_shellsafe(eff_dest, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } else if (dest_ret==1 && !xorriso->do_overwrite) { sprintf(xorriso->info_text, "%s: May not overwrite: ", activity); Text_shellsafe(eff_dest, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } else if(dest_ret>0) { ret= Xorriso_rmi(xorriso, boss_iter, (off_t) 0, eff_dest, 1|8); if(ret<=0) return(0); if(ret==3) { sprintf(xorriso->info_text, "%s: User revoked removal of: ", activity); Text_shellsafe(eff_dest, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(0); } } return(1); } /* @param boss_iter Opaque handle to be forwarded to actions in ISO image Set to NULL if calling this function from outside ISO world @param flag bit0= silently ignore attempt of renaming to same path and return 2 */ int Xorriso_rename(struct XorrisO *xorriso, void *boss_iter, char *origin, char *dest, int flag) { int ret, ol, dest_ret; char *eff_dest= NULL, *dir_adr= NULL, *cpt; char *leafname, *eff_origin= NULL, *old_leafname; IsoImage *volume; IsoDir *origin_dir, *dest_dir; IsoNode *node, *iso_node; Xorriso_alloc_meM(eff_dest, char, SfileadrL); Xorriso_alloc_meM(dir_adr, char, SfileadrL); Xorriso_alloc_meM(eff_origin, char, SfileadrL); #ifndef Libisofs_iso_dir_iter_sufficienT /* Ticket 127: A80301 - A80302 I do not not deem IsoDirIter safe for node list manipulations. The parameter boss_iter once was intended to allow such but has now been downgraded to a mere check for eventual programming bugs. */ if(boss_iter!=NULL) { sprintf(xorriso->info_text, "Program error: Xorriso_rename() was requested to delete iterated node "); Text_shellsafe(origin, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } #endif /* Libisofs_iso_dir_iter_sufficienT */ ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, origin, eff_origin, 0); if(ret<=0) goto ex; dest_ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, dest, eff_dest,1); if(dest_ret<0) {ret= dest_ret; goto ex;} if(dest_ret==0) { /* obtain eff_dest address despite it does not exist */ ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, dest, eff_dest, 2); if(ret<=0) goto ex; } /* Prevent that destination is a subordinate of origin (that would be a black hole plopping out of the universe) */ ol= strlen(eff_origin); if(ol==0) { sprintf(xorriso->info_text, "May not rename root directory"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } else if(strcmp(eff_origin, eff_dest)==0) { if(flag & 1) {ret= 2; goto ex;} sprintf(xorriso->info_text, "Ignored attempt to rename "); Text_shellsafe(eff_origin, xorriso->info_text, 1); strcat(xorriso->info_text, " to itself"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); {ret= 0; goto ex;} } else if(strncmp(eff_origin, eff_dest, ol)==0 && (eff_dest[ol]==0 || eff_dest[ol]=='/')) { sprintf(xorriso->info_text, "May not rename "); Text_shellsafe(eff_origin, xorriso->info_text, 1); strcat(xorriso->info_text, " to its own sub address "); Text_shellsafe(eff_dest, xorriso->info_text, 1 | 2); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } /* Check whether destination exists and may be not overwritable */ ret= Xorriso_overwrite_dest(xorriso, boss_iter, eff_dest, dest_ret, "Renaming", 0); if(ret <= 0) goto ex; /* Ensure existence of destination directory */ strcpy(dir_adr, eff_dest); cpt= strrchr(dir_adr, '/'); if(cpt==NULL) cpt= dir_adr+strlen(dir_adr); *cpt= 0; if(dir_adr[0]!=0) { ret= Xorriso_graft_in(xorriso, boss_iter, NULL, dir_adr, (off_t) 0, (off_t) 0, 1); if(ret<=0) goto ex; } /* Move node */ ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; Xorriso_node_from_path(xorriso, volume, dir_adr, &iso_node, 0); dest_dir= (IsoDir *) iso_node; strcpy(dir_adr, eff_origin); cpt= strrchr(dir_adr, '/'); if(cpt==NULL) cpt= dir_adr+strlen(dir_adr); *cpt= 0; Xorriso_node_from_path(xorriso, volume, dir_adr, &iso_node, 0); origin_dir= (IsoDir *) iso_node; Xorriso_node_from_path(xorriso, volume, eff_origin, &node, 0); if(dest_dir==NULL || origin_dir==NULL || node==NULL) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "Internal error on rename: confirmed node turns out as NULL"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } ret= iso_node_take(node); if(ret<0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, eff_dest, 0, "Cannot take", 0, "FATAL",1); sprintf(xorriso->info_text, "Internal error on rename: failed to take node"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } leafname= strrchr(eff_dest, '/'); if(leafname==NULL) leafname= eff_dest; else leafname++; old_leafname= (char *) iso_node_get_name(node); if(strcmp(leafname, old_leafname)!=0) ret= iso_image_set_node_name(volume, node, leafname, 1); else ret= 1; if(ret<0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, eff_dest, ret, "Cannot set name", 0, "FAILURE", 1); ret= iso_dir_add_node(origin_dir, node, 0); Xorriso_process_msg_queues(xorriso,0); if(ret < 0) Xorriso_report_iso_error(xorriso, eff_origin, ret, "Cannot re-instate node at old path", 0, "FAILURE", 1); {ret= -1; goto ex;} } Xorriso_process_msg_queues(xorriso,0); ret= iso_dir_add_node(dest_dir, node, 0); if(ret<0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, eff_dest, 0, "Cannot add", 0, "FATAL", 1); sprintf(xorriso->info_text, "Internal error on rename: failed to insert node"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } Xorriso_set_change_pending(xorriso, 0); ret= 1; ex:; Xorriso_free_meM(eff_dest); Xorriso_free_meM(dir_adr); Xorriso_free_meM(eff_origin); return(ret); } int Xorriso_cannot_clone(struct XorrisO *xorriso, char *eff_origin, char *eff_dest, int iso_error, int flag) { Xorriso_report_iso_error(xorriso, eff_dest, iso_error, "Cannot clone", 0, "FAILURE", 1); sprintf(xorriso->info_text, "Failed to clone "); Text_shellsafe(eff_origin, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } /* @param flag bit0= for iso_tree_clone() : merge directories bit1= do not issue NOTE message */ int Xorriso_clone_tree(struct XorrisO *xorriso, void *boss_iter, char *origin, char *dest, int flag) { int ret, dest_ret, l; char *eff_dest= NULL, *eff_origin= NULL, *dir_adr= NULL; char *leafname; IsoImage *volume; IsoDir *new_parent; IsoNode *origin_node, *dir_node, *new_node; Xorriso_alloc_meM(eff_dest, char, SfileadrL); Xorriso_alloc_meM(eff_origin, char, SfileadrL); Xorriso_alloc_meM(dir_adr, char, SfileadrL); ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret <= 0) goto ex; ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, origin, eff_origin, 0); if(ret<=0) goto ex; ret= Xorriso_node_from_path(xorriso, volume, eff_origin, &origin_node, 0); if(ret <= 0) goto ex; dest_ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, dest, eff_dest,1); if(dest_ret<0) {ret= dest_ret; goto ex;} if(dest_ret > 0) { if(eff_dest[0] == 0) strcpy(eff_dest, "/"); sprintf(xorriso->info_text, "Cloning: Copy address already exists: "); Text_shellsafe(eff_dest, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } else { /* obtain eff_dest address despite it does not exist */ ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, dest, eff_dest, 2); if(ret<=0) goto ex; } /* Obtain parent path and leaf name */ strcpy(dir_adr, eff_dest); for(l= strlen(dir_adr); l > 0; ) { if(dir_adr[l - 1] == '/') dir_adr[--l]= 0; else break; } leafname= strrchr(dir_adr, '/'); if(leafname == NULL) { leafname= dir_adr; if (leafname[0] == 0) { Xorriso_msgs_submit(xorriso, 0, "Empty file name as clone destination", 0, "FAILURE", 0); {ret= 0; goto ex;} } } else { *leafname= 0; leafname++; if(dir_adr[0] != 0) { /* Ensure existence of destination directory */ ret= Xorriso_graft_in(xorriso, boss_iter, NULL, dir_adr, (off_t) 0, (off_t) 0, 1); if(ret <= 0) goto ex; } } ret= Xorriso_node_from_path(xorriso, volume, dir_adr, &dir_node, 0); if(ret <= 0) goto ex; new_parent= (IsoDir *) dir_node; ret = iso_image_tree_clone(volume, origin_node, new_parent, leafname, &new_node, (flag & 1) | 2); Xorriso_process_msg_queues(xorriso,0); if(ret < 0) { Xorriso_cannot_clone(xorriso, eff_origin, eff_dest, ret, 0); {ret= 0; goto ex;} } Xorriso_set_change_pending(xorriso, 0); if(!(flag & 2)) { strcpy(xorriso->info_text, "Cloned in ISO image: "); Text_shellsafe(eff_origin, xorriso->info_text, 1); strcat(xorriso->info_text, " to "); Text_shellsafe(eff_dest, xorriso->info_text, 1 | 2); strcat(xorriso->info_text, "\n"); Xorriso_info(xorriso, 0); } ret= 1; ex:; Xorriso_free_meM(eff_dest); Xorriso_free_meM(eff_origin); Xorriso_free_meM(dir_adr); return(ret); } int Xorriso_clone_under(struct XorrisO *xorriso, char *origin, char *dest, int flag) { int ret, pass; char *eff_dest= NULL, *eff_origin= NULL, *namept; IsoDir *origin_dir, *dest_dir; IsoDirIter *iter= NULL; IsoNode *origin_node, *new_node; IsoImage *volume; Xorriso_alloc_meM(eff_dest, char, SfileadrL); Xorriso_alloc_meM(eff_origin, char, SfileadrL); ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret <= 0) goto ex; ret= Xorriso_dir_from_path(xorriso, "Copy source", origin, &origin_dir, 0); if(ret <= 0) goto ex; ret= Xorriso_dir_from_path(xorriso, "Copy destination", dest, &dest_dir, 0); if(ret <= 0) goto ex; for(pass= 0; pass < 2; pass++) { ret= iso_dir_get_children(origin_dir, &iter); if(ret < 0) { Xorriso_cannot_create_iter(xorriso, ret, 0); {ret= -1; goto ex;} } Xorriso_process_msg_queues(xorriso,0); while(iso_dir_iter_next(iter, &origin_node) == 1) { namept= (char *) iso_node_get_name(origin_node); sprintf(eff_origin, "%s/%s", origin, namept); sprintf(eff_dest, "%s/%s", dest, namept); if(pass == 0) { ret= Xorriso_node_from_path(xorriso, volume, eff_dest, &new_node, 1); if(ret < 0) goto ex; if(ret > 0) { sprintf(xorriso->info_text, "Cloning: Copy address already exists: "); Text_shellsafe(eff_dest, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } else { ret = iso_image_tree_clone(volume, origin_node, dest_dir, namept, &new_node, 1 | 2); Xorriso_process_msg_queues(xorriso,0); if(ret < 0) { Xorriso_cannot_clone(xorriso, eff_origin, eff_dest, ret, 0); ret= 0; goto ex; } } } iso_dir_iter_free(iter); iter= NULL; } Xorriso_set_change_pending(xorriso, 0); ret= 1; ex:; if(iter != NULL) iso_dir_iter_free(iter); Xorriso_free_meM(eff_dest); Xorriso_free_meM(eff_origin); Xorriso_process_msg_queues(xorriso,0); return(ret); } int Xorriso_set_st_mode(struct XorrisO *xorriso, char *in_path, mode_t mode_and, mode_t mode_or, int flag) { mode_t mode= 0; int ret; IsoNode *node; char *path= NULL; Xorriso_alloc_meM(path, char, SfileadrL); ret= Xorriso_get_node_by_path(xorriso, in_path, path, &node, 0); if(ret<=0) goto ex; mode= iso_node_get_permissions(node); mode= (mode & mode_and) | mode_or; iso_node_set_permissions(node, mode); iso_node_set_ctime(node, time(NULL)); sprintf(xorriso->info_text,"Permissions now: %-5.5o ", (unsigned int) (mode & 0xffff)); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); Xorriso_set_change_pending(xorriso, 0); Xorriso_process_msg_queues(xorriso,0); ret= 1; ex:; Xorriso_free_meM(path); return(ret); } int Xorriso_set_uid(struct XorrisO *xorriso, char *in_path, uid_t uid, int flag) { int ret; IsoNode *node; ret= Xorriso_get_node_by_path(xorriso, in_path, NULL, &node, 0); if(ret<=0) return(ret); iso_node_set_uid(node, uid); iso_node_set_ctime(node, time(NULL)); Xorriso_set_change_pending(xorriso, 0); Xorriso_process_msg_queues(xorriso,0); return(1); } int Xorriso_set_gid(struct XorrisO *xorriso, char *in_path, gid_t gid, int flag) { int ret; IsoNode *node; ret= Xorriso_get_node_by_path(xorriso, in_path, NULL, &node, 0); if(ret<=0) return(ret); iso_node_set_gid(node, gid); iso_node_set_ctime(node, time(NULL)); Xorriso_set_change_pending(xorriso, 0); Xorriso_process_msg_queues(xorriso,0); return(1); } /* @parm flag bit0= atime, bit1= ctime, bit2= mtime, bit8=no auto ctime */ int Xorriso_set_time(struct XorrisO *xorriso, char *in_path, time_t t, int flag) { int ret; IsoNode *node; ret= Xorriso_get_node_by_path(xorriso, in_path, NULL, &node, 0); if(ret<=0) return(ret); if(flag&1) iso_node_set_atime(node, t); if(flag&2) iso_node_set_ctime(node, t); if(flag&4) iso_node_set_mtime(node, t); if(!(flag&(2|256))) iso_node_set_ctime(node, time(NULL)); Xorriso_set_change_pending(xorriso, 0); Xorriso_process_msg_queues(xorriso,0); return(1); } /* Apply the effect of mkisofs -r to a single node */ int Xorriso_mkisofs_lower_r(struct XorrisO *xorriso, IsoNode *node, int flag) { mode_t perms; perms= iso_node_get_permissions(node); iso_node_set_uid(node, (uid_t) 0); iso_node_set_gid(node, (gid_t) 0); perms|= S_IRUSR | S_IRGRP | S_IROTH; perms&= ~(S_IWUSR | S_IWGRP | S_IWOTH); if(perms & (S_IXUSR | S_IXGRP | S_IXOTH)) perms|= (S_IXUSR | S_IXGRP | S_IXOTH); perms&= ~(S_ISUID | S_ISGID | S_ISVTX); iso_node_set_permissions(node, perms); return(1); } /* @param node Opaque handle to IsoNode which is to be manipulated instead of path if it is not NULL. @param path is used as address if node is NULL. @param access_text "access" ACL in long text form @param default_text "default" ACL in long text form @param flag bit0= do not warn of root directory if not capable of AAIP @return >0 success , <=0 failure */ int Xorriso_setfacl(struct XorrisO *xorriso, void *in_node, char *path, char *access_text, char *default_text, int flag) { int ret; IsoNode *node; node= (IsoNode *) in_node; if(node == NULL) { ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0); if(ret<=0) goto ex; } ret= iso_node_set_acl_text(node, access_text, default_text, 4); Xorriso_process_msg_queues(xorriso,0); if(ret <= 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when setting ACL to image node", 0, "FAILURE", 1); if(path != NULL && path[0] != 0) { strcpy(xorriso->info_text, "Error with setting ACL of "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } ret= 0; goto ex; } Xorriso_set_change_pending(xorriso, 0); ret= 1; ex:; return(ret); } /* @param in_node Opaque handle to IsoNode which is to be manipulated instead of path if it is not NULL. @param path is used as address if node is NULL. @param num_attrs Number of attributes @param names Array of pointers to 0 terminated name strings @param value_lengths Array of byte lengths for each attribute payload @param values Array of pointers to the attribute payload bytes @param flag bit0= Do not maintain eventual existing ACL of the node bit1= Do not clear the existing attribute list bit2= Delete the attributes with the given names bit3= Allow non-user attributes. bit4= do not warn of root if incapable of AAIP @return >0 success , <=0 failure */ int Xorriso_setfattr(struct XorrisO *xorriso, void *in_node, char *path, size_t in_num_attrs, char **in_names, size_t *in_value_lengths, char **in_values, int flag) { int ret, block_isofs= 0, in_original= 1; size_t i, j, num_attrs; IsoNode *node; char **names, **values; size_t *value_lengths; num_attrs= in_num_attrs; names= in_names; value_lengths= in_value_lengths; values= in_values; node= (IsoNode *) in_node; if(node == NULL) { ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0); if(ret<=0) goto ex; } if((xorriso->do_aaip & 1024) && !(flag & 8)) { flag|= 8; block_isofs= 1; for(i= 0; i < in_num_attrs; i++) { if(strncmp(in_names[i], "isofs.", 6) == 0) { if(in_original) { strcpy(xorriso->info_text, "Attempt to set xattr from namespace \"isofs\" to "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); ret= Xorriso_eval_problem_status(xorriso, 0, 0); if(ret < 0) { ret= 0; goto ex; } /* Switch to copy mode and omit isofs names */ Xorriso_alloc_meM(names, char *, num_attrs); Xorriso_alloc_meM(value_lengths, size_t, num_attrs); Xorriso_alloc_meM(values, char *, num_attrs); in_original= 0; for(j= 0; j < i; j++) { names[j]= in_names[j]; value_lengths[j]= in_value_lengths[j]; values[j]= in_values[j]; } num_attrs= i; } } else if(!in_original) { names[num_attrs]= in_names[i]; value_lengths[num_attrs]= in_value_lengths[i]; values[num_attrs]= in_values[i]; num_attrs++; } } } if(num_attrs <= 0) { ret= 1; goto ex; } ret= iso_node_set_attrs(node, num_attrs, names, value_lengths, values, (flag & (1 | 2 | 4 | 8)) | (block_isofs << 4)); Xorriso_process_msg_queues(xorriso,0); if(ret <= 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when setting ACL and xattr to image node", 0, "FAILURE", 1); if(path != NULL && path[0] != 0) { strcpy(xorriso->info_text, "Error with setting xattr of "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } ret= 0; goto ex; } Xorriso_set_change_pending(xorriso, 0); ret= 1; ex:; Xorriso_process_msg_queues(xorriso, 0); if(!in_original) { Xorriso_free_meM(names); Xorriso_free_meM(value_lengths); Xorriso_free_meM(values); } return(ret); } /* @param flag bit0= use parameters dev,ino rather than disk_path bit1= compare attribute rather than setting it return: 0=dev,ino match, 1=mismatch, 2=no node attribute -1=error bit5= if not bit0: transfer dev,inode from eventual link target bit7= omit dev check mit bit1 */ int Xorriso_record_dev_inode(struct XorrisO *xorriso, char *disk_path, dev_t dev, ino_t ino, void *in_node, char *iso_path, int flag) { size_t l, di_l= 0; int i, ret; dev_t hdev; ino_t hino; char buf[66], *bufpt, *wpt, *di= NULL; static char *name= "isofs.di"; struct stat stbuf; if(!(flag & 1)) { if(flag & 32) { if(stat(disk_path, &stbuf) == -1) return(-1); } else { if(lstat(disk_path, &stbuf) == -1) return(-1); } dev= stbuf.st_dev; ino= stbuf.st_ino; } wpt= buf; hdev= dev; for(i= 0; hdev != 0; i++) hdev= hdev >> 8; l= i; *(wpt++)= l; for(i= 0; i < (int) l; i++) *(wpt++)= dev >> (8 * (l - i - 1)); hino= ino; for(i= 0; hino != 0; i++) hino= hino >> 8; l= i; *(wpt++)= l; for(i= 0; i < (int) l; i++) *(wpt++)= ino >> (8 * (l - i - 1)); l= wpt - buf; bufpt= buf; if(flag & 2) { /* Compare node attribute with bufpt,l */ ret= Xorriso_get_attr_value(xorriso, in_node, iso_path, "isofs.di", &di_l, &di, 0); if(ret < 0) goto ex; if(ret == 0) {ret= 2; goto ex;} if(flag & 128) { if(di_l <= 0) {ret= 1; goto ex;} hino= 0; for(i= di[0] + 2; i < (int) di_l && i - di[0] - 2 < di[(int) di[0] + 1]; i++) hino= (hino << 8) | ((unsigned char *) di)[i]; if(hino != ino) {ret= 1; goto ex;} } else { if(l != di_l) {ret= 1; goto ex;} for(i= 0; i < (int) l; i++) if(di[i] != buf[i]) {ret= 1; goto ex;} } ret= 0; } else { ret= Xorriso_setfattr(xorriso, in_node, iso_path, (size_t) 1, &name, &l, &bufpt, 2 | 8); } ex:; if(di != NULL) free(di); return(ret); } /* @return see Xorriso_update_interpreter() */ int Xorriso_widen_hardlink(struct XorrisO *xorriso, void * boss_iter, IsoNode *node, char *abs_path, char *iso_prefix, char *disk_prefix, int flag) { int ret= 0, idx, low, high, i, do_widen= 0, compare_result= 0; char *disk_path; Xorriso_alloc_meM(disk_path, char, SfileadrL); /* Lookup all di_array instances of node */ if(LIBISO_ISDIR(node)) {ret= 3; goto ex;} ret= Xorriso_search_di_range(xorriso, node, &idx, &low, &high, 2); if(ret <= 0) {ret= 3; goto ex;} /* Check and reset di_do_widen bits */ for(i= low; i <= high; i++) { if(node != xorriso->di_array[i]) /* might be NULL */ continue; if(xorriso->di_do_widen[i / 8] & (1 << (i % 8))) do_widen= 1; xorriso->di_do_widen[i / 8]&= ~(1 << (i % 8)); } if(idx < 0 || !do_widen) {ret= 3; goto ex;} ret= Xorriso_pfx_disk_path(xorriso, abs_path, iso_prefix, disk_prefix, disk_path, 0); if(ret <= 0) goto ex; ret= Sfile_type(disk_path, 1); if(ret < 0) {ret= 3; goto ex;} /* does not exist on disk */ /* >>> compare_result bit17 = is_split */; ret= Xorriso_update_interpreter(xorriso, boss_iter, NULL, compare_result, disk_path, abs_path, 1); if(ret <= 0) goto ex; ex:; Xorriso_free_meM(disk_path); return(ret); } int Xorriso_set_hidden(struct XorrisO *xorriso, void *in_node, char *path, int hide_state, int flag) { int ret, hide_attrs= 0; IsoNode *node; node= (IsoNode *) in_node; if(node == NULL) { ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0); if(ret<=0) return(ret); } if(hide_state) { hide_attrs|= LIBISO_HIDE_BUT_WRITE; if(hide_state & 1) hide_attrs|= LIBISO_HIDE_ON_RR; if(hide_state & 2) hide_attrs|= LIBISO_HIDE_ON_JOLIET; if(hide_state & 4) hide_attrs|= LIBISO_HIDE_ON_HFSPLUS; } iso_node_set_hidden(node, hide_attrs); return(1); } /* @param flag bit0= increase only upper estimation */ int Xorriso_estimate_file_size(struct XorrisO *xorriso, struct FindjoB *job, char *basename, mode_t st_mode, off_t st_size, int flag) { off_t upper, lower, size; lower = 3 * strlen(basename) + 34; /* >>> + minimum RR ? */ upper = 3 * strlen(basename) + 2048; if(S_ISREG(st_mode)) { size= ((st_size + (off_t) 2047) / (off_t) 2048) * (off_t) 2048; lower+= size; upper+= size; } else if(S_ISDIR(st_mode)) { upper+= 4096; } job->estim_upper_size+= upper; if(!(flag & 1)) job->estim_lower_size+= lower; return(1); } /* @param flag bit0= do not compare but print input and back converted name bit1= if node is NULL: this is a directory */ int Xorriso_test_outchar(struct XorrisO *xorriso, void *node_pt, char *in_name, int name_space, int flag) { IsoNode *node; char *result= NULL, *name, *back= NULL; int ret, relax_mem; size_t result_len, back_len, i; struct isoburn_imgen_opts *sopts= NULL; relax_mem= xorriso->relax_compliance; node= (IsoNode *) node_pt; ret= isoburn_igopt_new(&sopts, 0); if(ret<=0) { Xorriso_process_msg_queues(xorriso, 0); ret= -1; goto ex; } if(!(flag & 1)) xorriso->relax_compliance|= isoburn_igopt_omit_version_numbers; ret= Xorriso_make_iso_write_opts(xorriso, NULL, sopts, 0); if(ret <= 0) { ret= -1; goto ex; } if(node == NULL) { if(flag & 2) name_space|= 256; name= in_name; } else { if(iso_node_get_type(node) == LIBISO_DIR) name_space|= 256; name= (char *) iso_node_get_name(node); } if(name == NULL) { ret= 1; goto ex; } name_space|= 512; /* no error messages */ ret= isoburn_conv_name_chars(sopts, name, strlen(name), &result, &result_len, name_space); if(ret <= 0) { Xorriso_process_msg_queues(xorriso, 0); if(flag & 1) goto print_outname; ret= 0; goto ex; } /* Convert back and compare with original */ ret= isoburn_conv_name_chars(sopts, result, result_len, &back, &back_len, name_space | (1 << 15)); if(ret <= 0) { Xorriso_process_msg_queues(xorriso, 0); if(flag & 1) goto print_outname; ret= 0; goto ex; } if(flag & 1) { print_outname:; Text_shellsafe(name, xorriso->result_line, 0); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); if(back == NULL) strcpy(xorriso->result_line, "(file name conversion error)"); else Text_shellsafe(back, xorriso->result_line, 0); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); strcpy(xorriso->result_line, "--\n"); Xorriso_result(xorriso, 0); } else { for(i= 0; i < back_len; i++) if(name[i] != back[i]) {ret= 0; goto ex;} if(name[i] != 0) {ret= 0; goto ex;} } ret= 1; ex:; isoburn_igopt_destroy(&sopts, 0); if(result != NULL) free(result); if(back != NULL) free(back); xorriso->relax_compliance= relax_mem; return(ret); } int Xorriso_set_to_mtime(struct XorrisO *xorriso, char *show_path, IsoNode *node, int flag) { time_t t; t= iso_node_get_mtime(node); iso_node_set_atime(node, t); iso_node_set_ctime(node, t); Xorriso_set_change_pending(xorriso, 0); return(1); } int Xorriso_cannot_create_iter(struct XorrisO *xorriso, int iso_error,int flag) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", iso_error, "Cannot create iter", 0, "FATAL", 1); sprintf(xorriso->info_text, "Cannot create IsoDirIter object"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); return(1); } /* The caller shall make no assumptions about the meaning of iter, node_array, node_count, node_idx ! They are just opaque handles for which this function provides the memory of proper type. @param flag bit0= initialize iteration bit1= action needs full freedom of object manipulation bit2= action needs LBA sorted iteration bit31= end iteration (mandatory !) */ int Xorriso_findi_iter(struct XorrisO *xorriso, IsoDir *dir_node, off_t *mem, IsoDirIter **iter, IsoNode ***node_array, int *node_count, int *node_idx, IsoNode **iterated_node, int flag) { int ret, i; IsoNode *node; off_t new_mem= 0; char mem_text[80], limit_text[80]; if(flag&1) { *node_array= NULL; *node_count= -1; *node_idx= 0; *iter= NULL; ret= iso_dir_get_children(dir_node, iter); if(ret<0) { cannot_iter:; Xorriso_cannot_create_iter(xorriso, ret, 0); return(-1); } if((flag&2)|(flag&4)) { /* copy list of nodes and prepare soft iterator */ *node_count= 0; while(iso_dir_iter_next(*iter, &node) == 1) (*node_count)++; iso_dir_iter_free(*iter); *iter= NULL; new_mem= ((*node_count)+1) * sizeof(IsoNode *); if(new_mem > xorriso->temp_mem_limit) { Sfile_scale((double) new_mem, mem_text, 5,1e4, 0); Sfile_scale((double) xorriso->temp_mem_limit, limit_text, 5,1e4, 0); sprintf(xorriso->info_text, "Stacked directory snapshots exceed -temp_mem_limit (%s > %s)", mem_text, limit_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); *node_count= -1; return(-1); } (*node_array)= (IsoNode **) calloc((*node_count)+1, sizeof(IsoNode *)); if(*node_array == NULL) { sprintf(xorriso->info_text, "Could not allocate inode list of %.f bytes", ((double) (*node_count)+1) * (double) sizeof(IsoNode *)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); *node_count= -1; return(-1); } *mem= new_mem; ret= iso_dir_get_children(dir_node, iter); if(ret<0) goto cannot_iter; while(iso_dir_iter_next(*iter, &node) == 1 && *node_idx < *node_count) { (*node_array)[*node_idx]= node; iso_node_ref(node); (*node_idx)++; } iso_dir_iter_free(*iter); *iter= NULL; *node_count= *node_idx; *node_idx= 0; if((flag&4) && *node_count>1) qsort(*node_array, *node_count, sizeof(IsoNode *), Xorriso__node_lba_cmp); } } if(flag&(1u<<31)) { if(*node_count>=0 && *node_array!=NULL) { for(i= 0; i<*node_count; i++) iso_node_unref((*node_array)[i]); free(*node_array); *node_array= NULL; *node_count= -1; *node_idx= 0; } else { if(*iter!=NULL) iso_dir_iter_free(*iter); *iter= NULL; } } if(flag&(1|(1u<<31))) return(1); if(*node_count>=0) { /* return next node_array element */ if(*node_idx>=*node_count) return(0); *iterated_node= (*node_array)[*node_idx]; (*node_idx)++; } else { ret= iso_dir_iter_next(*iter, iterated_node); return(ret == 1); } return(1); } /* @param flag bit0= not a command parameter (directory iteration or recursion) bit1= do not count deleted files with rm and rm_r @return <=0 error, 1=ok 2=ok, node has been deleted, 3=ok, do not dive into directory (e.g. because it is a split file) 4=ok, end findjob gracefully */ int Xorriso_findi_action(struct XorrisO *xorriso, struct FindjoB *job, IsoDirIter *boss_iter, off_t boss_mem, char *abs_path, char *show_path, IsoNode *node, int depth, int flag) { int ret= 0, type, action= 0, hflag, deleted= 0, no_dive= 0, i, bless_idx; int unbless= 0; uid_t user= 0; gid_t group= 0; time_t date= 0; mode_t mode_or= 0, mode_and= ~1; char *target, *text_2, *iso_prefix, md5[16], *basename, bless_code[17]; char crtp[10]; struct FindjoB *subjob; struct stat dir_stbuf, stbuf; void *xinfo; struct iso_hfsplus_xinfo_data *hfsplus_xinfo; size_t value_length; char *value; uint64_t lfa_flags, chattr_flags; int max_bit; char *lfa_text= NULL; action= Findjob_get_action_parms(job, &target, &text_2, &user, &group, &mode_and, &mode_or, &type, &date, &subjob, &chattr_flags, 0); if(action<0) action= 0; job->match_count++; hflag= 16*!(flag&2); ret= 1; if(action==1) { /* rm (including rmdir) */ ret= Xorriso_fake_stbuf(xorriso, abs_path, &dir_stbuf, &node, 1); if(ret>0) { if(S_ISDIR(dir_stbuf.st_mode)) hflag= 2; ret= Xorriso_rmi(xorriso, boss_iter, boss_mem, abs_path, hflag); deleted= 1; } } else if(action==2) { /* rm_r */ ret= Xorriso_rmi(xorriso, boss_iter, boss_mem, abs_path, 1|hflag); deleted= 1; } else if(action==3) { /* >>> mv target */; } else if(action==4) { /* chown */ ret= Xorriso_set_uid(xorriso, abs_path, user, 0); } else if(action==5) { /* chgrp */ ret= Xorriso_set_gid(xorriso, abs_path, group, 0); } else if(action==6) { /* chmod */ ret= Xorriso_set_st_mode(xorriso, abs_path, mode_and, mode_or, 0); } else if(action==7) { /* alter_date */ ret= Xorriso_set_time(xorriso, abs_path, date, type&7); } else if(action==8) { /* lsdl */ ret= Xorriso_ls_filev(xorriso, "", 1, &abs_path, (off_t) 0, 1|2|8); } else if(action>=9 && action<=13) { /* actions which have own findjobs */ Findjob_set_start_path(subjob, abs_path, 0); ret= Xorriso_findi(xorriso, subjob, boss_iter, boss_mem, NULL, abs_path, &dir_stbuf, depth, 1); } else if(action==14 || action==17 || action == 41) { /* compare , update , update_merge */ Findjob_get_start_path(job, &iso_prefix, 0); ret= Xorriso_find_compare(xorriso, (void *) boss_iter, (void *) node, abs_path, iso_prefix, target, (action == 17 || action == 41) | ((flag&1)<<1) | ((action == 41) << 2)); if(ret==2) deleted= 1; if(ret==3) no_dive= 1; if(ret>=0) ret= 1; } else if(action==16 || action==18) { /* not_in_iso , add_missing */ ; } else if(action == 21) { /* report_damage */ ret= Xorriso_report_damage(xorriso, show_path, node, 0); } else if(action == 22) { ret= Xorriso_report_lba(xorriso, show_path, node, &job->last_data_file_block, 0); } else if(action == 23) { /* internal: memorize path of last matching node */ ret= Findjob_set_found_path(job, show_path, 0); } else if(action == 24) { ret= Xorriso_getfacl(xorriso, (void *) node, show_path, NULL, 0); } else if(action == 25) { if(target == NULL || target[0] || text_2 == NULL || text_2[0]) ret= Xorriso_setfacl(xorriso, (void *) node, show_path, target, text_2,0); } else if(action == 26) { ret= Xorriso_getfattr(xorriso, (void *) node, show_path, NULL, 0); } else if(action == 27 || action == 62) { ret= Xorriso_path_setfattr(xorriso, (void *) node, show_path, target, strlen(text_2), text_2, 8 * (action == 62)); } else if(action == 28) { /* set_filter */ ret= Xorriso_set_filter(xorriso, (void *) node, show_path, target, 1 | 2); } else if(action == 29 || action == 52) { /* show_stream , show_stream_id */ ret= Xorriso_show_stream(xorriso, (void *) node, show_path, (action == 52)); } else if(action == 30) { /* internal: count */ xorriso->node_counter++; } else if(action == 31) { /* internal: register */ if(xorriso->node_counter < xorriso->node_array_size) { xorriso->node_array[xorriso->node_counter++]= (void *) node; iso_node_ref(node); /* In case node gets deleted from tree during the lifetime of xorriso->node_array */ } } else if(action == 32) { /* internal: widen_hardlinks disk_equiv */ Findjob_get_start_path(job, &iso_prefix, 0); ret= Xorriso_widen_hardlink(xorriso, (void *) boss_iter, node, abs_path, iso_prefix, target, 0); if(ret==2) deleted= 1; } else if(action == 33) { /* get_any_xattr */ ret= Xorriso_getfattr(xorriso, (void *) node, show_path, NULL, 8); } else if(action == 34) { /* get_md5 */ ret= Xorriso_get_md5(xorriso, (void *) node, show_path, md5, 0); if(ret >= 0) ret= 1; } else if(action == 35) { /* check_md5 */ ret= Xorriso_check_md5(xorriso, (void *) node, show_path, 2); if(ret == 0) xorriso->find_check_md5_result|= 1; else if(ret < 0) xorriso->find_check_md5_result|= 2; else if(ret == 1) xorriso->find_check_md5_result|= 8; else if(ret == 2) xorriso->find_check_md5_result|= 4; if(ret >= 0) ret= 1; } else if(action == 36) { /* make_md5 */ ret= Xorriso_make_md5(xorriso, (void *) node, show_path, 0); if(ret >= 0) ret= 1; } else if(action == 37) { /* mkisofs_r */ ret= Xorriso_mkisofs_lower_r(xorriso, node, 0); } else if(action == 38) { /* sort_weight */ iso_node_set_sort_weight(node, type); Xorriso_set_change_pending(xorriso, 0); } else if(action == 39) { /* hide */ Xorriso_set_hidden(xorriso, node, NULL, type, 0); } else if(action == 40) { /* estimate_size */ basename= strrchr(abs_path, '/'); if(basename != NULL) basename++; else basename= abs_path; ret= Xorriso_fake_stbuf(xorriso, "", &stbuf, &node, 1); if(ret > 0) ret= Xorriso_estimate_file_size(xorriso, job, basename, stbuf.st_mode, stbuf.st_size, 0); } else if(action == 42) { /* rm_merge */ ret= Xorriso_mark_update_merge(xorriso, show_path, node, 2 | 4); if(ret == 2) { ret= Xorriso_rmi(xorriso, boss_iter, boss_mem, abs_path, 1|hflag); sprintf(xorriso->info_text, "Deleted "); Text_shellsafe(show_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0); deleted= 1; } } else if(action == 43) { /* clear_merge */ ret= Xorriso_mark_update_merge(xorriso, show_path, node, 2 | 4); } else if(action == 44) { /* list_extattr */ ret= Xorriso_list_extattr(xorriso, (void *) node, show_path, show_path, target, 0); } else if(action == 45) { /* set_hfs_crtp */ ret= Xorriso_hfsplus_file_creator_type(xorriso, show_path, (void *) node, target, text_2, 0); } else if(action == 46) { /* get_hfs_crtp */ ret= iso_node_get_xinfo(node, iso_hfsplus_xinfo_func, &xinfo); if(ret < 0) { Xorriso_process_msg_queues(xorriso, 0); ret= 0; } else if(ret == 1) { hfsplus_xinfo= (struct iso_hfsplus_xinfo_data *) xinfo; for(i= 0; i < 4; i++) xorriso->result_line[i]= hfsplus_xinfo->creator_code[i]; xorriso->result_line[4]= ' '; for(i= 0; i < 4; i++) xorriso->result_line[5 + i]= hfsplus_xinfo->type_code[i]; xorriso->result_line[9]= ' '; xorriso->result_line[10]= 0; Text_shellsafe(show_path, xorriso->result_line, 1); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); } ret= 1; } else if(action == 47) { /* set_hfs_bless */ if(strcmp(target, "none") == 0 || strcmp(target, "n") == 0 || strcmp(target, "N") == 0) { ret= Xorriso_get_blessing(xorriso, node, &bless_idx, bless_code, 0); if(ret < 0) return(ret); if(ret == 0) return(1); unbless= 1; } ret= Xorriso_hfsplus_bless(xorriso, show_path, (void *) node, target, 0); /* If successful, end -find run gracefully */ if(ret > 0) { if(unbless) { sprintf(xorriso->info_text, "HFS blessing '%s' revoked from ", bless_code); } else { sprintf(xorriso->info_text, "HFS blessing '%s' issued to ", target); } Text_shellsafe(show_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } if(!unbless) return(4); } else if(action == 48) { /* get_hfs_bless */ ret= Xorriso_get_blessing(xorriso, node, &bless_idx, bless_code, 0); if (ret > 0) { sprintf(xorriso->result_line, "%-16.16s ", bless_code); Text_shellsafe(show_path, xorriso->result_line, 1); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); } else if(ret == 0) ret= 1; } else if(action == 49) { /* internal: update creator, type, and blessings from persistent isofs.* */ ret= Xorriso_get_attr_value(xorriso, node, show_path, "isofs.hx", &value_length, &value, 0); if(ret < 0) return(ret); if(ret > 0) { if(value_length >= 10) { ret= Xorriso_hfsplus_file_creator_type(xorriso, show_path, (void *) node, value + 2, value + 6, 4); } else ret= 1; free(value); if(ret <= 0) return(ret); } ret= Xorriso_get_attr_value(xorriso, node, show_path, "isofs.hb", &value_length, &value, 0); if(ret < 0) return(ret); if(ret > 0) { if(value_length >= 1) { bless_code[0]= value[0]; bless_code[1]= 0; ret= Xorriso_hfsplus_bless(xorriso, show_path, (void *) node, bless_code, 0); } else ret= 1; free(value); if(ret <= 0) return(ret); } ret= 1; } else if(action == 50) { /* print_outname */ ret= Xorriso_test_outchar(xorriso, (void *) node, "", type, 1); if(ret <= 0) return(ret); } else if(action == 51) { /* report_sections */ ret= Xorriso_report_lba(xorriso, show_path, node, &job->last_data_file_block, 1); } else if(action == 53) { /* internal: show_hfs_cmd */ ret= Xorriso_get_blessing(xorriso, node, &bless_idx, bless_code, 0); if (ret > 0) { if(xorriso->show_hfs_cmd_flag & 2) { sprintf(xorriso->result_line, "-hfs-bless-by %s ", bless_code); Text_shellsafe(show_path, xorriso->result_line, 1); } else { sprintf(xorriso->result_line, "-find "); Text_shellsafe(show_path, xorriso->result_line, 1); sprintf(xorriso->result_line + strlen(xorriso->result_line), " -exec set_hfs_bless %s --", bless_code); } ret= Xorriso_record_cmd_line(xorriso, xorriso->result_line, xorriso->show_hfs_cmds, &xorriso->show_hfs_cmd_count, (xorriso->show_hfs_cmd_flag & 1)); if(ret <= 0) return(ret); } ret= iso_node_get_xinfo(node, iso_hfsplus_xinfo_func, &xinfo); if(ret < 0) { Xorriso_process_msg_queues(xorriso, 0); ret= 0; } else if(ret == 1) { hfsplus_xinfo= (struct iso_hfsplus_xinfo_data *) xinfo; for(i= 0; i < 4; i++) crtp[i]= hfsplus_xinfo->creator_code[i]; crtp[4]= ' '; for(i= 0; i < 4; i++) crtp[5 + i]= hfsplus_xinfo->type_code[i]; crtp[9]= 0; if(xorriso->show_hfs_cmd_flag & 2) { sprintf(xorriso->result_line, "-hfsplus-file-creator-type %s ", crtp); Text_shellsafe(show_path, xorriso->result_line, 1); } else { sprintf(xorriso->result_line, "-find "); Text_shellsafe(show_path, xorriso->result_line, 1); sprintf(xorriso->result_line + strlen(xorriso->result_line), " -exec set_hfs_crtp %s --", crtp); } ret= Xorriso_record_cmd_line(xorriso, xorriso->result_line, xorriso->show_hfs_cmds, &xorriso->show_hfs_cmd_count, (xorriso->show_hfs_cmd_flag & 1)); if(ret <= 0) return(ret); } ret= 1; } else if(action == 54 || action == 56) { /* internal: truncate_name */ ret= Xorriso_truncate_uniquely(xorriso, xorriso->file_name_limit, node, abs_path, show_path, 2 * (action == 56)); } else if(action == 55 || action == 57) { /* internal: unique_trunc_test length (in type) */ ret= Xorriso_truncate_uniquely(xorriso, type, node, abs_path, show_path, 1 | (2 * (action == 57))); } else if(action == 58) { /* internal: last_data_file_block */ ret= Xorriso_report_lba(xorriso, show_path, node, &job->last_data_file_block, 2); } else if(action == 59) { /* set_to_mtime */ ret= Xorriso_set_to_mtime(xorriso, show_path, node, 0); } else if(action == 60) { /* lsattrd */ ret= Xorriso_get_lfa_flags(xorriso, node, show_path, &lfa_flags, &max_bit, 0); if(ret >= 0) { lfa_text= NULL; ret= Xorriso_encode_lfa_flags(xorriso, lfa_flags, &lfa_text, 1); if(ret > 0) { sprintf(xorriso->result_line, "%-22s ", lfa_text); Xorriso_esc_filepath(xorriso, show_path, xorriso->result_line, 1); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); } if(lfa_text != NULL) free(lfa_text); ret= 1; } else if(ret == 0) { ret= 1; } } else if(action == 61) { /* chattr */ ret= Xorriso_set_lfa_flags(xorriso, node, show_path, "", chattr_flags, type, 1 | 4); } else { /* includes : 15 in_iso */ Xorriso_esc_filepath(xorriso, show_path, xorriso->result_line, 0); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); ret= 1; } if(ret<=0) return(ret); if(deleted) return(2); if(no_dive) return(3); return(1); } /* flag bit0= perform -disk_path rather than -disk_name bit0= use_pattern */ int Exprtest_match_disk_name(struct XorrisO *xorriso, struct ExprtesT *ftest, IsoNode *node, int flag) { int ret; char *disk_path= NULL, *npt; regmatch_t name_match; char *arg1; void *arg2; Xorriso_alloc_meM(disk_path, char, SfileadrL); ret= Xorriso_retrieve_disk_path(xorriso, node, disk_path, 0); if(ret <= 0) {ret= 0; goto ex;} if(flag & 1) { if(strcmp(disk_path, ftest->arg1) == 0) {ret= 1; goto ex;} {ret= 0; goto ex;} } arg1= (char *) ftest->arg1; arg2= ftest->arg2; npt= strrchr(disk_path, '/'); if(npt != NULL) npt++; else npt= disk_path; if(flag & 2) { ret= ! regexec(arg2, npt, 1, &name_match, 0); } else { ret= (strcmp(arg1, npt) == 0); } ex:; Xorriso_free_meM(disk_path); return(ret); } int Exprtest_match(struct XorrisO *xorriso, struct ExprtesT *ftest, void *node_pt, char *name, char *path, struct stat *boss_stbuf, struct stat *stbuf, int flag) /* return: <0 = error 0 = does not match 1 = does match 2 = immediate decision : does not match 3 = immediate decision : does match */ { int value=0, ret, bless_idx, size_mode, lba_count, i, mask, max_bit, hflag; off_t range_lba, end_lba, *file_end_lbas= NULL, *file_start_lbas= NULL; off_t start_lba; uint64_t lfa_flags, node_flags; void *arg1, *arg2; char ft, *decision, md5[16], bless_code[17], *acl_text= NULL; regmatch_t name_match; off_t damage_start, damage_end, size, *section_sizes= NULL, size_arg; void *xinfo_dummy; IsoNode *node; IsoStream *stream; struct iso_hfsplus_xinfo_data *hfsplus_xinfo; if(ftest == NULL) return(1); node= (IsoNode *) node_pt; arg1= ftest->arg1; arg2= ftest->arg2; if(node == NULL) { switch(ftest->test_type) { case 0: case 1: case 2: case 4: case 5: case 6: case 11: case 12: case 13: case 14: case 16: case 20: case 21: case 22: case 23: case 25: case 26: case 27: case 28: case 29: /* Tests which need no node parameter or can work without it */ break; default: value= 0; goto ex; } } switch(ftest->test_type) { case 0: /* -false */ value= 0; break; case 1: /* -name *arg1 (regex in *arg2) */ test_name:; if (ftest->boss->use_pattern) { ret= regexec(arg2, name, 1, &name_match, 0); value= !ret; } else { value= (strcmp((char *) arg1, name) == 0); } break; case 2: /* -type *arg1 */ value= 1; ft= *((char *) arg1); if(ft!=0) { if(S_ISBLK(stbuf->st_mode)) { if(ft!='b') value= 0; } else if(S_ISCHR(stbuf->st_mode)) { if(ft!='c') value= 0; } else if(S_ISDIR(stbuf->st_mode)) { if(ft=='m') { if(node != NULL) value= 0; else if(boss_stbuf==NULL) value= 0; else if(boss_stbuf->st_dev == stbuf->st_dev) value= 0; } else if(ft!='d') value= 0; } else if(S_ISFIFO(stbuf->st_mode)) { if(ft!='p') value= 0; } else if(S_ISREG(stbuf->st_mode)) { if(ft!='f' && ft!='-') value= 0; } else if(((stbuf->st_mode)&S_IFMT)==S_IFLNK) { if(ft!='l') value= 0; } else if(((stbuf->st_mode)&S_IFMT)==S_IFSOCK) { if(ft!='s') value= 0; } else if((flag & 1) && ((stbuf->st_mode) & S_IFMT) == Xorriso_IFBOOT) { if(ft!='e' || node == NULL) value= 0; } else { if(ft!='X') value= 0; } } break; case 3: /* -damaged */; value= Xorriso_file_eval_damage(xorriso, node, &damage_start, &damage_end, 0); if(value > 0) value= 1; break; case 4: /* -lba_range *arg1 *arg2 */ value= 1; range_lba= *((off_t *) ftest->arg1); end_lba= *((off_t *) ftest->arg2); if(node == NULL) { value= !(range_lba >= 0); goto ex; } ret= Xorriso__start_end_lbas(node, &lba_count, &file_start_lbas, &file_end_lbas, §ion_sizes, &size, 0); if(ret <= 0) { if(ret < 0) Xorriso_process_msg_queues(xorriso, 0); if(range_lba >= 0) value= 0; } else { for(i= 0; i < lba_count; i++) { if(range_lba >= 0) { if(file_end_lbas[i] < range_lba || file_start_lbas[i] > end_lba) value= 0; } else { if(file_end_lbas[i] >= -range_lba && file_start_lbas[i] <= -end_lba) value= 0; } } } break; case 5: /* -has_acl */ if(node == NULL) { value= 0; ret= Xorriso_local_getfacl(xorriso, path, &acl_text, 16); if(acl_text != NULL) Xorriso_local_getfacl(xorriso, "", &acl_text, 1 << 15); if(ret == 1) { value= 1; } else if(S_ISDIR(stbuf->st_mode)) { ret= Xorriso_local_getfacl(xorriso, path, &acl_text, 1 | 16); if(acl_text != NULL) Xorriso_local_getfacl(xorriso, "", &acl_text, 1 << 15); if(ret == 1) value= 1; } } else { ret= Xorriso_getfacl(xorriso, (void *) node, "", NULL, 2); } if(ret <= 0) { value= -1; Xorriso_process_msg_queues(xorriso, 0); goto ex; } value= (ret == 1); break; case 6: /* -has_xattr */ case 14: /* -has_any_xattr */ ret = Xorriso_getfattr(xorriso, (void *) node, path, NULL, (2 * (node == NULL)) | (8 * (ftest->test_type == 14)) | 64 | 128); if(ret < 0) { value= -1; Xorriso_process_msg_queues(xorriso, 0); goto ex; } value= (ret > 0); break; case 7: /* -has_aaip */ ret= iso_node_get_xinfo(node, aaip_xinfo_func, &xinfo_dummy); if(ret < 0) { value= -1; Xorriso_process_msg_queues(xorriso, 0); goto ex; } value= (ret > 0); break; case 8: /* -has_filter */ value= 0; if(LIBISO_ISREG(node)) { stream= iso_file_get_stream((IsoFile *) node); if(iso_stream_get_input_stream(stream, 0) != NULL) value= 1; } break; case 9: /* -wanted_node arg1 (for internal use) */ value= (((IsoNode *) arg1) == node); break; case 10: /* -pending_data */ value= 1; if(!LIBISO_ISREG(node)) { value= 0; } else { ret= Xorriso__file_start_lba(node, &start_lba, 0); if(ret > 0 && start_lba >= 0) value= 0; } break; case 11: /* -decision */ value= 2; decision= (char *) arg1; if(strcmp(decision, "yes") == 0 || strcmp(decision, "true") == 0) value= 3; break; case 12: /* -prune */ value= 1; ftest->boss->prune= 1; break; case 13: /* -wholename *arg1 (regex in *arg2) */ if (ftest->boss->use_pattern) { ret= regexec(arg2, path, 1, &name_match, 0); value= !ret; } else { value= (strcmp(arg1, path) == 0); } break; case 15: /* -has_md5 */ ret= Xorriso_get_md5(xorriso, node, path, md5, 1); value= (ret > 0); break; case 16: /* -disk_name *arg1 (regex in *arg2) */ if(node == NULL) { goto test_name; } else { value= !! Exprtest_match_disk_name(xorriso, ftest, node, 2 * (ftest->boss->use_pattern)); } break; case 17: /* -hidden int *arg1 */ value= 1; ret= iso_node_get_hidden(node); mask= *((int *) arg1) & 3; if((!!(mask & 1)) ^ (!!(ret & LIBISO_HIDE_ON_RR))) value= 0; if((!!(mask & 2)) ^ (!!(ret & LIBISO_HIDE_ON_JOLIET))) value= 0; if((!!(mask & 3)) ^ (!!(ret & LIBISO_HIDE_ON_HFSPLUS))) value= 0; break; case 18: /* -has_hfs_crtp char *creator char *type */ ret= iso_node_get_xinfo(node, iso_hfsplus_xinfo_func, &xinfo_dummy); value= 0; if(ret < 0) { Xorriso_process_msg_queues(xorriso, 0); ret= 0; } else if(ret == 1) { hfsplus_xinfo= (struct iso_hfsplus_xinfo_data *) xinfo_dummy; if((strlen(arg1) == 1 || (strncmp(arg1, (char *) hfsplus_xinfo->creator_code, 4) == 0 && strlen(arg1) == 4)) && (strlen(arg2) == 1 || (strncmp(arg2, (char *) hfsplus_xinfo->type_code, 4) == 0 && strlen(arg2) == 4))) value= 1; } break; case 19: /* -has_hfs_bless int bless_index */ value= 0; ret= Xorriso_get_blessing(xorriso, node, &bless_idx, bless_code, 0); if (ret > 0) { if(*((int *) arg1) == (int) ISO_HFSPLUS_BLESS_MAX || *((int *) arg1) == bless_idx) value= 1; } break; case 20: /* -disk_path */ if(node == NULL) { value= (strcmp(path, ftest->arg1) == 0); } else { value= !! Exprtest_match_disk_name(xorriso, ftest, node, 1 | 2 * (ftest->boss->use_pattern)); } break; case 21: /* -bad_outname */ ret= Xorriso_test_outchar(xorriso, node, name, *((int *) arg1), 2 * !!S_ISDIR(stbuf->st_mode)); if(ret < 0) { value= -1; goto ex; } value= !ret; /* Xorriso_test_outchar() returns 1 for good and 0 for bad */ break; case 22: /* -use_pattern */ ftest->boss->use_pattern= (strcmp(arg1, "off") != 0); value= 1; break; case 23: /* -or_use_pattern */ ftest->boss->use_pattern= (strcmp(arg1, "off") != 0); value= 0; break; case 24: /* -name_limit_blocker */ ret= Xorriso_truncate_uniquely(xorriso, *((int *) arg1), node, path, path, 1 | 4); value= (ret == 0); break; case 25: /* -maxdepth */ value= (ftest->boss->depth <= *((int *) arg1)); break; case 26: /* -mindepth */ value= (ftest->boss->depth >= *((int *) arg1)); break; case 27: /* -size */ size_arg= *((off_t *) arg1); size_mode= *((int *) arg2); if(node == NULL) { size= stbuf->st_size; } else { ret= Xorriso__get_file_size(node, &size, 0); if(ret <= 0) { if(ret < 0) Xorriso_process_msg_queues(xorriso, 0); value= 0; goto ex; } } if(size_mode == 0) { value= (size == size_arg); } else if(size_mode == 1) { value= (size > size_arg); } else if(size_mode == 2) { value= (size >= size_arg); } else if(size_mode == -1) { value= (size < size_arg); } else if(size_mode == -2) { value= (size <= size_arg); } else { value= 0; } break; case 28: /* -has_lfa_flags uint64_t lfa_flags */ case 29: /* -has_some_lfa_flags_of uint64_t *arg */ lfa_flags= *((uint64_t *) ftest->arg1); if(node == NULL) { hflag= 2; if((xorriso->do_aaip & ((1 << 11) | (1 << 15))) == ((1 << 11) | (1 << 15))) hflag|= (1 << 7); ret= Xorriso_get_lfa_flags(xorriso, NULL, path, &node_flags, &max_bit, hflag); } else { ret= Xorriso_get_lfa_flags(xorriso, node, path, &node_flags, &max_bit, 0); } if(ret <= 0) { if(ret < 0) Xorriso_process_msg_queues(xorriso, 0); value= 0; goto ex; } if(ftest->test_type == 28) { value= ((node_flags & lfa_flags) == lfa_flags); } else { value= !!(node_flags & lfa_flags); } break; default: /* >>> complain about unknown test type */; value= -1; } ex:; if(ftest->invert && value<=1 && value>=0) value= !value; if(file_start_lbas != NULL) free((char *) file_start_lbas); if(file_end_lbas != NULL) free((char *) file_end_lbas); if(section_sizes != NULL) free((char *) section_sizes); return(value); } /* @return <0 = error , 0 = no match , 1 = match */ int Xorriso_findi_test(struct XorrisO *xorriso, struct FindjoB *job, IsoNode *node, char *name, char *path, struct stat *boss_stbuf, struct stat *stbuf, int depth, int flag) { int ret; job->prune= 0; ret= Findjob_test_2(xorriso, job, node, name, path, boss_stbuf, stbuf, 1); if(ret <= 0) return(ret); return(1); } int Xorriso_findi_headline(struct XorrisO *xorriso, struct FindjoB *job, int flag) { int action; action= Findjob_get_action(job, 0); if(action == 21) { /* report_damage */ sprintf(xorriso->result_line, "Report layout: %8s , %8s , %8s , %s\n", "at byte", "Range", "Filesize", "ISO image path"); Xorriso_result(xorriso, 0); } else if(action == 22 || action == 51) { /* report_lba, report_sections */ sprintf(xorriso->result_line, "Report layout: %2s , %8s , %8s , %8s , %s\n", "xt", "Startlba", "Blocks", action == 22 ? "Filesize" : "Sectsize", "ISO image path"); Xorriso_result(xorriso, 0); } return(1); } /* @param flag bit0= recursion bit1= do not count deleted files with rm and rm_r bit2= do not dive into split file directories (implicitly given with actions 14=compare and 17=update) @return <=0 error, 1= ok , 2= dir node and path has been deleted 4= end gracefully */ int Xorriso_findi(struct XorrisO *xorriso, struct FindjoB *job, void *boss_iter, off_t boss_mem, void *dir_node_generic, char *dir_path, struct stat *dir_stbuf, int depth, int flag) { int ret, action= 0, hflag, deleted= 0, no_dive= 0; IsoDirIter *iter= NULL; IsoDir *dir_node= NULL; IsoNode *node, *iso_node; IsoImage *volume= NULL; struct stat stbuf; char *name; off_t mem; IsoNode **node_array= NULL; int node_count= 0, node_idx; char *path= NULL, *abs_path= NULL; job->depth= depth; if(xorriso->request_to_abort) {ret= 0; goto ex;} path= malloc(SfileadrL); abs_path= malloc(SfileadrL); if(path==NULL || abs_path==NULL) { Xorriso_no_malloc_memory(xorriso, &path, 0); {ret= -1; goto ex;} } action= Findjob_get_action(job, 0); if(action<0) action= 0; if(!(flag & 1)) { Xorriso_findi_headline(xorriso, job, 0); job->last_data_file_block= 0; } dir_node= (IsoDir *) dir_node_generic; if(dir_node==NULL) { ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) {ret= -1; goto ex;} ret= Xorriso_make_abs_adr(xorriso, xorriso->wdi, dir_path, path, 1|2|4); if(ret<=0) goto ex; ret= Xorriso_node_from_path(xorriso, volume, path, &iso_node, 0); dir_node= (IsoDir *) iso_node; if(ret<=0) {ret= 0; goto ex;} ret= Xorriso_fake_stbuf(xorriso, "", dir_stbuf, &iso_node, 1); if(ret<=0) goto ex; name= strrchr(dir_path, '/'); if(name==NULL) name= dir_path; else name++; ret= Xorriso_findi_test(xorriso, job, iso_node, name, path, NULL, dir_stbuf, depth, 0); if(ret<0) goto ex; if(job->prune) no_dive= 1; if(ret>0) { iso_node_ref(iso_node); /* protect from real disposal */ ret= Xorriso_findi_action(xorriso, job, (IsoDirIter *) boss_iter, boss_mem, path, dir_path, iso_node, depth, flag&(1|2)); deleted= (iso_node_get_parent(iso_node) == NULL); /* still in tree ? */ iso_node_unref(iso_node); /* eventually do real disposal */ if(xorriso->request_to_abort) {ret= 0; goto ex;} if(ret == 4) goto ex; if(ret<=0) goto ex; if(ret==2 || deleted) { /* re-determine dir_node in case it has a new persona */ ret= Xorriso_node_from_path(xorriso, volume, path, &iso_node, 1); if(ret==0) { deleted= 1; {ret= 2; goto ex;} } if(ret<0) {ret= 0; goto ex;} dir_node= (IsoDir *) iso_node; ret= Xorriso_fake_stbuf(xorriso, "", dir_stbuf, &iso_node, 1); if(ret<=0) goto ex; } if(ret==3) no_dive= 1; } } if(no_dive || !LIBISO_ISDIR((IsoNode *) dir_node)) {ret= 1; goto ex;} if(action == 14 || action == 17 || (flag & 4)) if(Xorriso_is_split(xorriso, dir_path, (IsoNode *) dir_node, 1)>0) {ret= 1; goto ex;} mem= boss_mem; hflag= 1; if(action==1 || action==2 || action==3 || action==17 || action == 28 || action == 32 || action == 41 || action == 42) hflag|= 2; /* need freedom to manipulate image */ if(action==14 || action==17 || action == 28 || action == 35 || action == 36 || action == 41) hflag|= 4; /* need LBA sorted iteration for good data reading performance */ ret= Xorriso_findi_iter(xorriso, dir_node, &mem, &iter, &node_array, &node_count, &node_idx, &node, hflag); if(ret<=0) goto ex; job->depth++; while(1) { ret= Xorriso_findi_iter(xorriso, dir_node, &mem, &iter, &node_array, &node_count, &node_idx, &node, 0); if(ret<0) goto ex; if(ret==0) break; name= (char *) iso_node_get_name(node); ret= Xorriso_make_abs_adr(xorriso, dir_path, name, path, 4); if(ret<=0) goto ex; ret= Xorriso_fake_stbuf(xorriso, "", &stbuf, &node, 1); if(ret<0) goto ex; if(ret==0) continue; /* ??? This seems to be redundant with the single test above ??? Should i dive in unconditionally and leave out test and action here ? ??? Then do above test unconditionally ? --- Seems that the current configuration represents the special handling of the find start path with mount points. Dangerous to change. */ ret= Xorriso_findi_test(xorriso, job, node, name, path, dir_stbuf, &stbuf, depth, 0); if(ret<0) goto ex; if(job->prune) no_dive= 1; if(ret>0) { ret= Xorriso_make_abs_adr(xorriso, xorriso->wdi, path, abs_path, 1|2|4); if(ret<=0) goto ex; ret= Xorriso_findi_action(xorriso, job, iter, mem, abs_path, path, node, depth, 1|(flag&2)); if(xorriso->request_to_abort) {ret= 0; goto ex;} if(ret == 4) goto ex; if(ret==2) { /* node has been deleted */ /* re-determine node in case it has a new persona */ if(volume==NULL) { ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) {ret= -1; goto ex;} } ret= Xorriso_node_from_path(xorriso, volume, abs_path, &node, 1); if(ret==0) continue; if(ret<0) {ret= 0; goto ex;} ret= Xorriso_fake_stbuf(xorriso, "", &stbuf, &node, 1); if(ret<0) goto ex; if(ret==0) continue; } no_dive= (ret==3); if(ret<=0) { if(Xorriso_eval_problem_status(xorriso, ret, 1|2)<0) goto ex; } } if(S_ISDIR(stbuf.st_mode) && !no_dive) { ret= Xorriso_findi(xorriso, job, (void *) iter, mem, (void *) node, path, &stbuf, depth+1, flag|1); if(ret<0) goto ex; if(xorriso->request_to_abort) {ret= 0; goto ex;} if(ret == 4) goto ex; } } ret= 1; ex:; job->depth= depth; if(path!=NULL) free(path); if(abs_path!=NULL) free(abs_path); Xorriso_process_msg_queues(xorriso,0); Xorriso_findi_iter(xorriso, dir_node, &mem, &iter, &node_array, &node_count, &node_idx, &node, (1u<<31)); if(ret<=0) return(ret); if(deleted) return(2); return(1); } /* @param flag bit0= do not dive into trees bit1= do not perform job->action on resulting node array bit2= do not free node_array after all actions are done */ int Xorriso_findi_sorted(struct XorrisO *xorriso, struct FindjoB *job, off_t boss_mem, int filec, char **filev, int flag) { int i, ret, find_flag= 0; struct FindjoB array_job, *proxy_job= NULL, *hindmost= NULL, *hmboss= NULL; struct stat dir_stbuf; IsoNode *node; char *abs_path= NULL; off_t mem_needed= 0; array_job.start_path= NULL; Xorriso_alloc_meM(abs_path, char, SfileadrL); if(job->action == 14 || job->action == 17) find_flag|= 4; if(job->action>=9 && job->action<=13) { /* actions which have own findjobs */ /* array_job replaces the hindmost job in the chain */ for(hindmost= job; hindmost->subjob != NULL; hindmost= hindmost->subjob) hmboss= hindmost; if(hmboss == NULL) {ret= -1; goto ex;} memcpy(&array_job, hindmost, sizeof(struct FindjoB)); hmboss->subjob= &array_job; proxy_job= job; } else { memcpy(&array_job, job, sizeof(struct FindjoB)); proxy_job= &array_job; hindmost= job; } array_job.start_path= NULL; /* is owned by the original, not by array_job */ /* Count matching nodes */ Xorriso_destroy_node_array(xorriso, 0); array_job.action= 30; /* internal: count */ for(i= 0; i < filec; i++) { if(flag & 1) { xorriso->node_counter++; continue; } ret= Findjob_set_start_path(proxy_job, filev[i], 0); if(ret <= 0) goto ex; ret= Xorriso_findi(xorriso, proxy_job, NULL, boss_mem, NULL, filev[i], &dir_stbuf, 0, find_flag); if(ret <= 0) goto ex; } if(xorriso->node_counter <= 0) {ret= 1; goto ex;} mem_needed= boss_mem + xorriso->node_counter * sizeof(IsoNode *); if(!(flag &1)) { ret= Xorriso_check_temp_mem_limit(xorriso, mem_needed, 0); if(ret <= 0) { /* Memory curbed : Perform unsorted find jobs */ if(hmboss != NULL) hmboss->subjob= hindmost; for(i= 0; i < filec; i++) { ret= Findjob_set_start_path(job, filev[i], 0); if(ret <= 0) goto ex; ret= Xorriso_findi(xorriso, job, NULL, boss_mem, NULL, filev[i], &dir_stbuf, 0, find_flag); if(ret <= 0) if(Xorriso_eval_problem_status(xorriso, ret, 1|2)<0) goto ex; } {ret= 1; goto ex;} } } /* Copy matching nodes into allocated array */ ret= Xorriso_new_node_array(xorriso, xorriso->temp_mem_limit, 0, 0); if(ret <= 0) goto ex; array_job.action= 31; /* internal: register */ xorriso->node_counter= 0; for(i= 0; i < filec; i++) { if(flag & 1) { ret= Xorriso_get_node_by_path(xorriso, filev[i], NULL, &node, 0); if(ret <= 0) goto ex; if(xorriso->node_counter < xorriso->node_array_size) { xorriso->node_array[xorriso->node_counter++]= (void *) node; iso_node_ref(node); } continue; } ret= Findjob_set_start_path(proxy_job, filev[i], 0); if(ret <= 0) goto ex; ret= Xorriso_findi(xorriso, proxy_job, NULL, mem_needed, NULL, filev[i], &dir_stbuf, 0, find_flag); if(ret <= 0) goto ex; } Xorriso_sort_node_array(xorriso, 0); if(flag & 2) {ret= 1; goto ex;} /* Perform job->action on xorriso->node_array */ /* Headlines of actions report_damage , report_lba */; Xorriso_findi_headline(xorriso, job, 0); for(i= 0; i < xorriso->node_counter; i++) { node= xorriso->node_array[i]; ret= Xorriso_path_from_node(xorriso, node, abs_path, 0); if(ret < 0) goto ex; if(ret == 0) continue; /* node is deleted from tree meanwhile */ ret= Xorriso_findi_action(xorriso, hindmost, NULL, (off_t) 0, abs_path, abs_path, node, 0, 1); if(ret <= 0 || xorriso->request_to_abort) if(Xorriso_eval_problem_status(xorriso, ret, 1|2)<0) goto ex; if(ret == 4) /* end gracefully */ break; } ret= 1; ex:; if(!(flag & (2 | 4))) Xorriso_destroy_node_array(xorriso, 0); if(hmboss != NULL) hmboss->subjob= hindmost; if(array_job.start_path != NULL) free(array_job.start_path); Xorriso_free_meM(abs_path); return(ret); } int Xorriso_all_node_array(struct XorrisO *xorriso, int addon_nodes, int flag) { int ret; struct FindjoB *job= NULL; struct stat dir_stbuf; ret= Findjob_new(&job, "/", 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "xorriso", 0); {ret= -1; goto ex;} } Findjob_set_action_target(job, 30, NULL, 0); Xorriso_destroy_node_array(xorriso, 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, "/", &dir_stbuf, 0, 0); if(ret <= 0) goto ex; ret= Xorriso_new_node_array(xorriso, xorriso->temp_mem_limit, addon_nodes, 0); if(ret <= 0) goto ex; Findjob_set_action_target(job, 31, NULL, 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, "/", &dir_stbuf, 0, 0); if(ret <= 0) goto ex; ret= 1; ex:; Findjob_destroy(&job, 0); return(ret); } int Xorriso_perform_acl_from_list(struct XorrisO *xorriso, char *file_path, char *uid, char *gid, char *acl, int flag) { int ret, zero= 0; uid_t uid_number; gid_t gid_number; /* Set group and owner */ if(gid[0]) { ret= Xorriso_convert_gidstring(xorriso, gid, &gid_number, 0); if(ret<=0) return(ret); ret= Xorriso_set_gid(xorriso, file_path, gid_number, 0); if(ret<=0) return(ret); } if(uid[0]) { ret= Xorriso_convert_uidstring(xorriso, uid, &uid_number, 0); if(ret<=0) return(ret); ret= Xorriso_set_uid(xorriso, file_path, uid_number, 0); if(ret<=0) return(ret); } ret= Xorriso_option_setfacli(xorriso, acl, 1, &file_path, &zero, 0); if(ret <= 0) return(ret); return(1); } /* @param flag bit0= do not perform setfattr but only check input bit3= Allow non-user attributes. */ int Xorriso_path_setfattr(struct XorrisO *xorriso, void *in_node, char *path, char *name, size_t value_length, char *value, int flag) { int ret, hflag; size_t num_attrs= 1; char *name_pt; hflag= 2 | (flag & 8); name_pt= name; if(name[0] == 0) { sprintf(xorriso->info_text, "-setfattr: Empty attribute name is not allowed"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } else if(strcmp(name, "--remove-all") == 0) { if(value[0]) { sprintf(xorriso->info_text, "-setfattr: Value is not empty with pseudo name --remove-all"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } num_attrs= 0; hflag= 0; } else if(name[0] == '-') { name_pt++; hflag|= 4; } else if(name[0] == '=' || name[0] == '+') { name_pt++; } if(flag & 1) return(1); ret= Xorriso_setfattr(xorriso, in_node, path, num_attrs, &name_pt, &value_length, &value, hflag); return(ret); } /* Warning: The text content of lst gets mangled by 0s and unescaping. */ int Xorriso_perform_attr_from_list(struct XorrisO *xorriso, char *path, struct Xorriso_lsT *lst_start, int flag) { int ret, eaten; char *valuept, *ept, *line, **names= NULL, **values= NULL; size_t num_attr= 0, *value_lengths= NULL, v_len; struct Xorriso_lsT *lst; for(lst= lst_start; lst != NULL; lst= Xorriso_lst_get_next(lst, 0)) num_attr++; if(num_attr == 0) { ret= Xorriso_setfattr(xorriso, NULL, path, num_attr, NULL, NULL, NULL, 0); goto ex; } names= calloc(num_attr, sizeof(char *)); if(names == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } value_lengths= calloc(num_attr, sizeof(size_t)); if(value_lengths== NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } values= calloc(num_attr, sizeof(char *)); if(values== NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } num_attr= 0; for(lst= lst_start; lst != NULL; lst= Xorriso_lst_get_next(lst, 0)) { line= Xorriso_lst_get_text(lst, 0); ept= strchr(line, '='); if(ept == NULL) continue; /* Split into name and content */; *ept= 0; valuept= ept + 1; /* Strip quotes from value */ v_len= strlen(valuept); if(v_len < 2 || *valuept != '"' || *(valuept + v_len - 1) != '"') continue; *valuept= 0; *(valuept + v_len - 1)= 0; valuept++; v_len-= 2; /* Unescape backslashes , values eventually with 0-bytes */ ret= Sfile_bsl_interpreter(line, strlen(line), &eaten, 0); if(ret <= 0) continue; ret= Sfile_bsl_interpreter(valuept, (int) v_len, &eaten, 2); if(ret <= 0) continue; names[num_attr]= line; values[num_attr]= valuept; value_lengths[num_attr]= v_len - eaten; num_attr++; } ret= Xorriso_setfattr(xorriso, NULL, path, num_attr, names, value_lengths, values, 0); ex:; if(names != NULL) free(names); if(value_lengths != NULL) free(value_lengths); if(values != NULL) free(values); return(ret); } int Xorriso__mark_update_xinfo(void *data, int flag) { /* data is an int disguised as pointer. It does not point to memory. */ return(1); } int Xorriso__mark_update_cloner(void *old_data, void **new_data, int flag) { *new_data= NULL; if(flag) return(ISO_XINFO_NO_CLONE); if(old_data == NULL) return(0); /* data is an int disguised as pointer. It does not point to memory. */ *new_data= old_data; return(0); } /* @param flag bit0= found on disk bit1= inquire visit-found status: 1=not visited, 2=not found, 3=found bit2= with bit1: delete xinfo before returning status */ int Xorriso_mark_update_merge(struct XorrisO *xorriso, char *path, void *in_node, int flag) { int ret; void *xipt= NULL; IsoNode *node; if(in_node == NULL) { ret= Xorriso_node_from_path(xorriso, NULL, path, &node, 0); if(ret <= 0) return(ret); } else node= (IsoNode *) in_node; ret= iso_node_get_xinfo(node, Xorriso__mark_update_xinfo, &xipt); if(ret < 0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", ret, "Error when looking for update_merge xinfo", 0, "FAILURE", 1); return(0); } if(flag & 2) { /* Inquire status and optionally delete xinfo */ if(ret == 0) return(1); if(flag & 4) { ret= iso_node_remove_xinfo(node, Xorriso__mark_update_xinfo); if(ret < 0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", ret, "Error when removing update_merge xinfo", 0, "FAILURE", 1); return(0); } } if(((char *) &xipt)[0]) return(3); return(2); } /* xipt is a byte value disguised as void pointer */ if(ret == 1) { if(((char *) &xipt)[0]) return(1); if(!(flag & 1)) return(1); } else ((char *) &xipt)[0]= 0; if(flag & 1) ((char *) &xipt)[0]= 1; ret= iso_node_remove_xinfo(node, Xorriso__mark_update_xinfo); if(ret < 0) goto set_error; ret= iso_node_add_xinfo(node, Xorriso__mark_update_xinfo, xipt); if(ret <= 0) { set_error:; Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", ret, "Error when trying to set update_merge xinfo", 0, "FAILURE", 1); return(0); } return(1); } /* flag bit0= in case of error talk of "overwrite" rather than "remove" */ static int Xorriso_remove_hfsplus_crtp(struct XorrisO *xorriso, IsoNode *node, char *path, int flag) { int ret; char *msg, buf[10], *bufpt; size_t l; static char *name= "isofs.hx"; ret= iso_node_remove_xinfo(node, iso_hfsplus_xinfo_func); Xorriso_process_msg_queues(xorriso, 0); if(ret < 0) { if(flag & 1) msg= "Cannot overwrite HFS+ creator and type of ISO node"; else msg= "Cannot remove HFS+ creator and type of ISO node"; Xorriso_report_iso_error(xorriso, path, ret, msg, 0, "FAILURE", 1); return(0); } /* Delete isofs.hx attribute */ bufpt= buf; /* >>> ??? check whether there is isofs.hx attached ? */; ret= Xorriso_setfattr(xorriso, node, path, (size_t) 1, &name, &l, &bufpt, 4 | 8); return(ret); } static int Xorriso_set_hfsplus_crtp(struct XorrisO *xorriso, IsoNode *node, char *path, char *creator, char *hfs_type, int flag) { struct iso_hfsplus_xinfo_data *hfs_data= NULL; char buf[10], *bufpt; size_t l; int ret; static char *name= "isofs.hx"; /* Register as non-persistent xinfo */ hfs_data= iso_hfsplus_xinfo_new(0); if(hfs_data == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); return(-1); } memcpy(hfs_data->creator_code, creator, 4); memcpy(hfs_data->type_code, hfs_type, 4); ret= iso_node_add_xinfo(node, iso_hfsplus_xinfo_func, (void *) hfs_data); Xorriso_process_msg_queues(xorriso, 0); if(ret < 0) { Xorriso_report_iso_error(xorriso, path, ret, "Cannot attach HFS+ creator and type to ISO node", 0, "FAILURE", 1); goto failure; } else if(ret == 0) { strcat(xorriso->info_text, "Program error: iso_node_add_xinfo refuses to attach HFS+ creator and type"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto failure; } /* Register as persistent attribute isofs.hx */ bufpt= buf; l= 10; buf[0]= 1; buf[1]= 0; memcpy(buf + 2, creator, 4); memcpy(buf + 6, hfs_type, 4); ret= Xorriso_setfattr(xorriso, node, path, (size_t) 1, &name, &l, &bufpt, 2 | 8); if(ret <= 0) goto failure; Xorriso_set_change_pending(xorriso, 0); return(1); failure: if(hfs_data != NULL) iso_hfsplus_xinfo_func(hfs_data, 1); return(0); } /* @param flag bit0= only check creator and hfs_type for compliance. bit1= with bit0: check for search rather than for setting bit2= copy 2 times 4 bytes without any check */ int Xorriso_hfsplus_file_creator_type(struct XorrisO *xorriso, char *path, void *in_node, char *creator, char *hfs_type, int flag) { int ret; IsoNode *node; if(in_node == NULL && !(flag & 1)) { ret= Xorriso_node_from_path(xorriso, NULL, path, &node, 0); if(ret <= 0) return(ret); } else node= (IsoNode *) in_node; if(flag & 4) { ; } else if((creator[0] == 0 && hfs_type[0] == 0) || strcmp(creator, "--delete") == 0) { if(flag & 2) { strcpy(xorriso->info_text, "Attempt to use HFS+ file pseudo-creator '--delete' for searching"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); strcpy(xorriso->info_text, "Suitable are strings of length 4 or length 1"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); return(0); } if(flag & 1) return(1); ret= Xorriso_remove_hfsplus_crtp(xorriso, node, path, 0); if(ret < 0) return(ret); return(1); } else if((strlen(creator) != 4 && !(strlen(creator) == 1 && (flag & 3) == 3)) || (strlen(hfs_type) != 4 && !(strlen(hfs_type) == 1 && (flag & 3) == 3))) { if(flag & 2) { strcpy(xorriso->info_text, "HFS+ file creator code or type code for searching are not exactly 1 or 4 characters long"); } else { strcpy(xorriso->info_text, "HFS+ file creator code or type code are not exactly 4 characters long"); } Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(flag & 1) return(1); ret= Xorriso_remove_hfsplus_crtp(xorriso, node, path, 1); if(ret <= 0) return(ret); ret= Xorriso_set_hfsplus_crtp(xorriso, node, path, creator, hfs_type, 0); if(ret <= 0) return(ret); return(1); } /* @param node If node is NULL and path is empty, then the blessing will be revoked from any node which bears it. @param flag Bitfield for control purposes. bit0= Revoke blessing if node != NULL bears it. bit1= Revoke any blessing of the node, regardless of parameter blessing. If node is NULL, then revoke all blessings. bit2= Only check parameter blessing. Return blessing index + 1 instead of issuing the blessing. bit3= With bit2: Allow blessing "any" and map to index ISO_HFSPLUS_BLESS_MAX. Elsewise, blessing "none" is mapped to ISO_HFSPLUS_BLESS_MAX. */ int Xorriso_hfsplus_bless(struct XorrisO *xorriso, char *path, void *in_node, char *blessing, int flag) { int ret, bless_max; IsoNode *node, **blessed_nodes; IsoImage *volume= NULL; enum IsoHfsplusBlessings bless_code = ISO_HFSPLUS_BLESS_MAX; /* = invalid */ char *hb = ""; size_t l= 0; static char *name= "isofs.hb"; if(strcmp(blessing, "ppc_bootdir") == 0 || strcmp(blessing, "p") == 0 || strcmp(blessing, "P") == 0) { bless_code= ISO_HFSPLUS_BLESS_PPC_BOOTDIR; hb= "p"; } else if(strcmp(blessing, "intel_bootfile") == 0 || strcmp(blessing, "i") == 0 || strcmp(blessing, "I") == 0) { bless_code= ISO_HFSPLUS_BLESS_INTEL_BOOTFILE; hb= "i"; } else if(strcmp(blessing, "show_folder") == 0 || strcmp(blessing, "s") == 0 || strcmp(blessing, "S") == 0) { bless_code= ISO_HFSPLUS_BLESS_SHOWFOLDER; hb= "s"; } else if(strcmp(blessing, "os9_folder") == 0 || strcmp(blessing, "9") == 0) { bless_code= ISO_HFSPLUS_BLESS_OS9_FOLDER; hb= "9"; } else if(strcmp(blessing, "osx_folder") == 0 || strcmp(blessing, "x") == 0 || strcmp(blessing, "X") == 0) { bless_code= ISO_HFSPLUS_BLESS_OSX_FOLDER; hb= "x"; } else if((!(flag & 8)) && (strcmp(blessing, "none") == 0 || strcmp(blessing, "n") == 0 || strcmp(blessing, "N") == 0)) { bless_code= ISO_HFSPLUS_BLESS_MAX; flag |= 2; } else if((flag & 8) && (flag & 4) && (strcmp(blessing, "any") == 0 || strcmp(blessing, "a") == 0 || strcmp(blessing, "A") == 0)) { bless_code= ISO_HFSPLUS_BLESS_MAX; } else { sprintf(xorriso->info_text, "Unknown blessing type "); Text_shellsafe(blessing, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(flag & 4) return(1 + bless_code); if(in_node == NULL && path[0]) { ret= Xorriso_node_from_path(xorriso, NULL, path, &node, 0); if(ret <= 0) return(ret); } else node= (IsoNode *) in_node; ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret <= 0) return(ret); if(!(flag & 2)) { /* Remove persistent bless mark from current bearer */ ret= iso_image_hfsplus_get_blessed(volume, &blessed_nodes, &bless_max, 0); Xorriso_process_msg_queues(xorriso, 0); if(ret < 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when trying to bless a file", 0, "FAILURE", 1); return(0); } if((int) bless_code < bless_max) { if(blessed_nodes[(int) bless_code] != NULL) { ret= Xorriso_setfattr(xorriso, blessed_nodes[(int) bless_code], "", (size_t) 1, &name, &l, &hb, 4 | 8); if(ret <= 0) return(ret); } } } /* Bless node */ ret= iso_image_hfsplus_bless(volume, bless_code, node, flag & 3); Xorriso_process_msg_queues(xorriso, 0); if(ret == 0 && path[0]) { if((flag & 3)) { sprintf(xorriso->info_text, "Attempt to revoke blessing of unblessed file"); } else { sprintf(xorriso->info_text, "Multiple blessing to same file or inappropriate file type"); } if(path[0]) { strcat(xorriso->info_text, ": "); Text_shellsafe(path, xorriso->info_text, 1); } Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } else if (ret < 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when trying to bless a file", 0, "FAILURE", 1); return(0); } /* Attach persistent AAIP bless mark to node */ if(!(flag & 3)) { l= 1; ret= Xorriso_setfattr(xorriso, node, path, (size_t) 1, &name, &l, &hb, 2 | 8); if(ret <= 0) return(ret); } Xorriso_set_change_pending(xorriso, 0); return(1); } int Xorriso_get_blessing(struct XorrisO *xorriso, IsoNode *node, int *bless_idx, char bless_code[17], int flag) { IsoNode **blessed_nodes; int bless_max, ret, i; if(xorriso->in_volset_handle == NULL) return(0); ret= iso_image_hfsplus_get_blessed((IsoImage *) xorriso->in_volset_handle, &blessed_nodes, &bless_max, 0); Xorriso_process_msg_queues(xorriso, 0); if(ret < 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when trying to inquire HFS+ file blessings", 0, "FAILURE", 1); return(-1); } for(i= 0; i < bless_max; i++) { if(blessed_nodes[i] == node) { switch (i) { case ISO_HFSPLUS_BLESS_PPC_BOOTDIR: strcpy(bless_code, "ppc_bootdir"); break; case ISO_HFSPLUS_BLESS_INTEL_BOOTFILE: strcpy(bless_code, "intel_bootfile"); break; case ISO_HFSPLUS_BLESS_SHOWFOLDER: strcpy(bless_code, "show_folder"); break; case ISO_HFSPLUS_BLESS_OS9_FOLDER: strcpy(bless_code, "os9_folder"); break; case ISO_HFSPLUS_BLESS_OSX_FOLDER: strcpy(bless_code, "osx_folder"); break; default: strcpy(bless_code, "unknown_blessing"); } *bless_idx= i; return(1); } } return(0); } /* @param flag bit0= use file addresses as search patterns */ int Xorriso_apply_sort_file(struct XorrisO *xorriso, char *path, int flag) { int ret, linecount= 0, filec= 0, zero, i; FILE *fp= NULL; char *sret, *line= NULL, *spt, *tpt, *patterns[1], **filev= NULL; char *sort_weight_args[4]; off_t mem= 0; IsoImage *volume; Xorriso_alloc_meM(line, char, SfileadrL); ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; ret= Xorriso_afile_fopen(xorriso, path, "rb", &fp, 2); if(ret <= 0) {ret= 0; goto ex;} while(1) { sret= Sfile_fgets_n(line, SfileadrL - 1, fp, 0); if(sret == NULL) { if(ferror(fp)) {ret= 0; goto ex;} break; } linecount++; /* Find first space or tab */ spt= strchr(line, ' '); tpt= strchr(line, '\t'); if(spt == NULL || (tpt != NULL && tpt < spt)) spt= tpt; if(spt == NULL) { sprintf(xorriso->info_text, "No space or tab character found in line %d of sort weight file ", linecount); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } *spt= 0; patterns[0]= spt + 1; if(flag & 1) { /* Obtain list of matching files */ ret= Xorriso_expand_pattern(xorriso, 1, patterns, 0, &filec, &filev, &mem, 4); if(ret <= 0) {ret= 0; goto ex;} } else { filec= 1; } /* Apply weight to file or directory tree */ for(i= 0; i < filec; i++) { zero= 0; if(flag & 1) { sort_weight_args[0]= filev[i]; } else { sort_weight_args[0]= patterns[0]; } sort_weight_args[1]= "-exec"; sort_weight_args[2]= "sort_weight"; sort_weight_args[3]= line; ret= Xorriso_option_find(xorriso, 4, sort_weight_args, &zero, 2); if(ret <= 0) {ret= 0; goto ex;} } if(flag & 1) Sfile_destroy_argv(&filec, &filev, 0); } ret= 1; ex: if(fp != NULL) fclose(fp); Xorriso_free_meM(line); Sfile_destroy_argv(&filec, &filev, 0); return(ret); } /* @param flag bit0= tolerate truncated files of old length and mangle collisions */ int Xorriso_set_file_name_limit(struct XorrisO *xorriso, int value, int flag) { int ret; IsoImage *volume= NULL; struct FindjoB *job= NULL; struct stat dir_stbuf; ret= Xorriso_get_volume(xorriso, &volume, 1); if(ret < 0) goto ex; if (ret == 1 && volume != NULL) { /* Check whether there are non-refreshable truncated names */ ret= Findjob_new(&job, "/", 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "xorriso", 0); {ret= -1; goto ex;} } Findjob_set_action_type(job, 55 + 2 * (flag & 1), value, 0); xorriso->find_unique_trunc_result= 2; ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, "/", &dir_stbuf, 0, 0); if(ret < 0) goto ex; xorriso->request_to_abort= 0; if(xorriso->find_unique_trunc_result == 0 && !(flag & 1)) { Xorriso_msgs_submit(xorriso, 0, "-file_name_limit may not be changed because truncated files exist or collisions would occur", 0, "SORRY", 0); ret= 0; goto ex; } xorriso->file_name_limit= value; iso_image_set_truncate_mode(volume, 1, value); /* truncations are necessary */; if(xorriso->find_unique_trunc_result == 1) { Findjob_set_action_type(job, 54 + 2 * (flag & 1), xorriso->file_name_limit, 0); xorriso->find_unique_trunc_result= 2; ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, "/", &dir_stbuf, 0, 0); if(ret < 0) goto ex; if(xorriso->find_unique_trunc_result == 0) { /* >>> Did not work . What to do ? */; } } } xorriso->file_name_limit= value; ret= 1; ex:; Findjob_destroy(&job, 0); return(ret); } /* @param flag bit0= test for uniqueness, do not change name bit1= tolerate existing truncated names and mangle collisions bit2= be silent about non-uniquely truncatables do not set xorriso->request_to_abort */ int Xorriso_truncate_uniquely(struct XorrisO *xorriso, int length, IsoNode *node, char *abs_path, char *show_path, int flag) { int ret, l, i; unsigned int mangleno; char *name, *trunc= NULL, *old_name= NULL; IsoDir *dir; IsoNode *collider; IsoImage *volume= NULL; name= (char *) iso_node_get_name(node); l= strlen(name); /* Check for truncated name */ if(l == xorriso->file_name_limit && l != length && !(flag & 2)) { i= 0; if(name[l - 33] == ':') { for(i= l - 32; i < l; i++) if((name[i] < '0' || name[i] > '9') && (name[i] < 'a' || name[i] > 'f')) break; } if(i == l) { if(!(flag & 4)) { sprintf(xorriso->info_text, "Truncated name of current limit found: "); Text_shellsafe(name, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } xorriso->find_unique_trunc_result= 0; {ret= 0; goto ex;} } } /* Check for need to truncate */ if(l <= length) {ret= 1; goto ex;} if(xorriso->find_unique_trunc_result > 1) xorriso->find_unique_trunc_result= 1; trunc= strdup(name); old_name= strdup(name); if(trunc == NULL || old_name == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } ret= iso_truncate_leaf_name(1, length, trunc, 0); if(ret < 0) { Xorriso_process_msg_queues(xorriso, 0); Xorriso_report_iso_error(xorriso, "", ret, "Error when truncating file name", 0, "SORRY", 1); xorriso->find_unique_trunc_result= 0; {ret= 0; goto ex;} } dir= iso_node_get_parent(node); if(dir != NULL) { /* (intentionally using deprecated call which does not truncate by itself)*/ ret= iso_dir_get_node(dir, trunc, &collider); if(ret == 1) { if((flag & 1) && !(flag & 2)) { if(!(flag & 4)) { sprintf(xorriso->info_text, "Truncated name collides with existing name: "); Text_shellsafe(name, xorriso->info_text, 1); strcat(xorriso->info_text, " -> "); Text_shellsafe(trunc, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } xorriso->find_unique_trunc_result= 0; {ret= 0; goto ex;} } else { /* Mangle */ for(mangleno= 0; mangleno < 0xffffffff; mangleno++) { Sfile_flatten_utf8_heads(trunc, length - 33 - 9, 0); sprintf(trunc + length - 33 - 9, ":%-8.8X", mangleno); trunc [length - 33] = ':'; ret= iso_dir_get_node(dir, trunc, &collider); if(ret == 0) break; } } } } /* If only for testing: done now */ if(flag & 1) {ret= 1; goto ex;} if(xorriso->file_name_limit != length) {ret= -1; goto ex;} /* Programming error */ ret= Xorriso_get_volume(xorriso, &volume, 1); if(ret < 0) {ret= -1; goto ex;} /* Programming error */ /* Set truncated name */ ret= iso_image_set_node_name(volume, node, trunc, 0); if(ret < 0) { Xorriso_process_msg_queues(xorriso, 0); xorriso->find_unique_trunc_result= 0; {ret= 0; goto ex;} } Xorriso_set_change_pending(xorriso, 0); sprintf(xorriso->info_text, "Truncated: "); Text_shellsafe(old_name, xorriso->info_text, 1); strcat(xorriso->info_text, " -> "); Text_shellsafe(trunc, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 1; ex:; if(ret == 0 && (flag & 1) && !(flag & 4)) xorriso->request_to_abort= 1; Xorriso_free_meM(old_name); Xorriso_free_meM(trunc); return(ret); } int Xorriso_decode_chattr_arg(struct XorrisO *xorriso, char *chattr_text, uint64_t *lfa_flags, int *operator, int flag) { int ret; *lfa_flags= 0; if(strcmp(chattr_text, "--remove-lfa-flags") == 0) { *operator= 4; } else if(chattr_text[0] == '=') { *operator= 0; } else if(chattr_text[0] == '+') { *operator= 1; } else if(chattr_text[0] == '-') { *operator= 2; } else if(chattr_text[0] == '.') { *operator= 3; } else { Xorriso_msgs_submit(xorriso, 0, "-chattr argument does not begin by '=', '+', '-', or '.'", 0, "SORRY", 0); return(0); } ret= Xorriso_decode_lfa_flags(xorriso, chattr_text + 1, lfa_flags, 0); if(ret <= 0) return(ret); return(1); } /* @param in_node if not NULL: the node to manipulate @path if in_node is NULL: path to node in any case: path to report with errors @param operator chattr operation mode 0='=', 1='+', 2='-' non-chattr modes: 3='.', 4="--remove-lfa-flags" @param flag bit0= use lfa_flags and operator rather than chattr_text bit1= do not refuse to set lfa_flags of files which are neither directory nor regular file bit2= if not bit1: refuse silently with return 2 bit3= do not change flags but only test if it would be really attempted without bit3. return 1 if yes, <= 0 if not (with SORRY event). */ int Xorriso_set_lfa_flags(struct XorrisO *xorriso, void *in_node, char *path, char *chattr_text, uint64_t lfa_flags, int operator, int flag) { int ret, max_bit; uint64_t set_flags; IsoNode *node; if(in_node != NULL) { node= (IsoNode *) in_node; } else { ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0); if(ret <= 0) return(ret); } if(!(flag & 1)) { ret= Xorriso_decode_chattr_arg(xorriso, chattr_text, &lfa_flags, &operator, 0); if(ret <= 0) return(ret); } if(!(flag & 2)) { if(!(LIBISO_ISREG(node) || LIBISO_ISDIR(node) || operator == 4)) { if(flag & 4) return(2); Xorriso_report_iso_error(xorriso, path, 0, "File type unsuitable for -chattr", 0, "SORRY", 4); return(0); } } if(flag & 8) return(1); if(operator == 4) { ret= Xorriso_path_setfattr(xorriso, (void *) node, path, "-isofs.fa", (size_t) 0, "", 8); if(ret <= 0) return(ret); Xorriso_set_change_pending(xorriso, 0); return(1); } if(operator == 0) { set_flags= lfa_flags; } else { ret= Xorriso_get_lfa_flags(xorriso, node, path, &set_flags, &max_bit, 0); if(ret < 0) return(ret); if(operator == 1) { set_flags|= lfa_flags; } else if(operator == 2) { set_flags&= ~lfa_flags; } else if(operator == 3) { set_flags&= lfa_flags; } } ret= iso_node_set_lfa_flags(node, set_flags, 0); if(ret < 0) { Xorriso_process_msg_queues(xorriso, 0); Xorriso_report_iso_error(xorriso, path, ret, "Error when setting chattr flags of ISO node", 0, "SORRY", 1); return(-1); } Xorriso_set_change_pending(xorriso, 0); return(1); } int Xorriso_remove_all_lfa_flags(struct XorrisO *xorriso, int flag) { int ret; struct FindjoB *job= NULL; struct stat dir_stbuf; ret= Findjob_new(&job, "/", 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "xorriso", 0); return(-1); } Findjob_set_action_text_2(job, 62, "-isofs.fa", "", 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, "/", &dir_stbuf, 0, 0); Findjob_destroy(&job, 0); if(ret <= 0) return(ret); return(1); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of functions which manipulate the libisofs tree model. */ #ifndef Xorriso_pvt_iso_manip_includeD #define Xorriso_pvt_iso_manip_includeD yes int Xorriso_transfer_properties(struct XorrisO *xorriso, struct stat *stbuf, char *disk_path, IsoNode *node, int flag); int Xorriso_graft_split(struct XorrisO *xorriso, IsoImage *volume, IsoDir *dir, char *disk_path, char *img_name, char *nominal_source, char *nominal_target, off_t size, IsoNode **node, int flag); int Xorriso_tree_graft_node(struct XorrisO *xorriso, IsoImage *volume, IsoDir *dir, char *disk_path, char *img_name, char *nominal_source, char *nominal_target, off_t offset, off_t cut_size, IsoNode **node, int flag); int Xorriso_add_tree(struct XorrisO *xorriso, IsoDir *dir, char *img_dir_path, char *disk_dir_path, struct LinkiteM *link_stack, int flag); int Xorriso_copy_implicit_properties(struct XorrisO *xorriso, IsoDir *dir, char *full_img_path, char *img_path, char *full_disk_path, int flag); int Xorriso_mkisofs_lower_r(struct XorrisO *xorriso, IsoNode *node, int flag); int Xorriso_widen_hardlink(struct XorrisO *xorriso, void * boss_iter, IsoNode *node, char *abs_path, char *iso_prefix, char *disk_prefix, int flag); int Xorriso_cannot_create_iter(struct XorrisO *xorriso, int iso_error, int flag); int Xorriso_findi_iter(struct XorrisO *xorriso, IsoDir *dir_node, off_t *mem, IsoDirIter **iter, IsoNode ***node_array, int *node_count, int *node_idx, IsoNode **iterated_node, int flag); int Xorriso_findi_action(struct XorrisO *xorriso, struct FindjoB *job, IsoDirIter *boss_iter, off_t boss_mem, char *abs_path, char *show_path, IsoNode *node, int depth, int flag); int Xorriso_findi_headline(struct XorrisO *xorriso, struct FindjoB *job, int flag); int Xorriso_findi_sorted(struct XorrisO *xorriso, struct FindjoB *job, off_t boss_mem, int filec, char **filev, int flag); int Xorriso_all_node_array(struct XorrisO *xorriso, int addon_nodes, int flag); int Xorriso__mark_update_xinfo(void *data, int flag); int Xorriso__mark_update_cloner(void *old_data, void **new_data, int flag); int Xorriso_get_blessing(struct XorrisO *xorriso, IsoNode *node, int *bless_idx, char bless_code[17], int flag); int Xorriso_truncate_uniquely(struct XorrisO *xorriso, int length, IsoNode *node, char *abs_path, char *show_path, int flag); #endif /* ! Xorriso_pvt_iso_manip_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains functions which access nodes of the libisofs tree model. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include <pwd.h> #include <grp.h> #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" #include "lib_mgt.h" #include "iso_img.h" #include "iso_tree.h" #include "iso_manip.h" #include "sort_cmp.h" /* @param eff_path returns resulting effective path. Must provide at least SfileadrL bytes of storage. @param flag bit0= do not produce problem events (unless faulty path format) bit1= do not use libisofs (work literally or in disk world) bit2= (implies bit1) this is an address in the disk world bit3= return root directory as "/" and not as "" bit4= (with bit2) determine type of disk file eff_path and return 0 if not existing bit5= (with bit4) this is not a parameter bit6= insist in having an ISO image, even with bits1+2 @return -1 = faulty path format, 0 = not found , 1 = found simple node , 2 = found directory */ int Xorriso_normalize_img_path(struct XorrisO *xorriso, char *wd, char *img_path, char eff_path[], int flag) { int ret, is_dir= 0, done= 0; IsoImage *volume; IsoDir *dir= NULL; IsoNode *node= NULL; char *path= NULL, *apt, *npt, *cpt, *link_target= NULL; Xorriso_alloc_meM(path, char, SfileadrL); Xorriso_alloc_meM(link_target, char, SfileadrL); if(flag & 4) flag|= 2; if((flag&64) || !(flag&2)) { ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; } eff_path[0]= 0; if(img_path[0]==0) { if(flag&8) strcpy(eff_path, "/"); {ret= 2; goto ex;} /* root directory */ } apt= npt= path; if(img_path[0]!='/') { strcpy(path, wd); ret= Sfile_add_to_path(path, img_path, 0); if(ret<=0) goto much_too_long; } else if(Sfile_str(path, img_path, 0)<=0) {ret= -1; goto ex;} if(path[0]!='/') { sprintf(xorriso->info_text, "Internal error: Unresolvable relative addressing in iso_rr_path '%s'", img_path); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FATAL", 0); {ret= -1; goto ex;} } else if(path[1]==0) { if(flag&8) strcpy(eff_path, "/"); {ret= 2; goto ex;} /* root directory */ } if(apt[0] == '.') if(apt[1] == 0 || apt[1] == '/') is_dir= 1; for(npt= apt; !done; apt= npt+1) { npt= strchr(apt, '/'); if(npt==NULL) { npt= apt+strlen(apt); done= 1; } else *npt= 0; if(*apt==0) { *apt= '/'; apt++; if(done) break; continue; } if(strcmp(apt,".")==0) continue; if(strcmp(apt,"..")==0) { if(!(flag&2)) { node= (IsoNode *) dir; if(node==NULL) { bonked_root:; sprintf(xorriso->info_text, "Relative addressing in path exceeds root directory: "); Text_shellsafe(img_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= -1; goto ex;} } dir= iso_node_get_parent(node); /* >>> Get absolute path of node as eff_path, to avoid link problems */; } if(flag & 4) { /* Linux-ish local filesystem semantics: If the parent is a symbolic link, do not ascend to its parent, but to the parent of the link target. >>> ??? Are there systems which do it differently ? */ if(Sfile_type(eff_path, 0) == 3) { ret= Xorriso_resolve_link(xorriso, eff_path, link_target, 2); if(ret <= 0) {ret= -1; goto ex;} strcpy(eff_path, link_target); } } /* truncate eff_path */; cpt= strrchr(eff_path, '/'); if(cpt==NULL) /* ??? if not flag&2 then this is a bug */ goto bonked_root; *cpt= 0; is_dir= 1; continue; } is_dir= 0; ret= Sfile_add_to_path(eff_path, apt, 0); if(ret<=0) { much_too_long:; sprintf(xorriso->info_text, "Effective path gets much too long (%d)", (int) (strlen(eff_path)+strlen(apt)+1)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= -1; goto ex;} } if(!(flag&2)) { dir= (IsoDir *) node; ret= Xorriso_node_from_path(xorriso, volume, eff_path, &node, flag&1); if(ret<=0) {ret= 0; goto ex;} if(dir==NULL) /* could be false with "/dir/.." */ dir= iso_node_get_parent(node); is_dir= LIBISO_ISDIR(node); } } if(flag&16) { ret= Sfile_type(eff_path, 1|(4*(xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&32))) )); if(ret<0) {ret= 0; goto ex;} if(ret==2) is_dir= 1; else is_dir= 0; } ret= 1+!!is_dir; ex:; Xorriso_free_meM(path); Xorriso_free_meM(link_target); return(ret); } /* @param flag bit0=do not complain about non existent node */ int Xorriso_get_node_by_path(struct XorrisO *xorriso, char *in_path, char *eff_path, IsoNode **node, int flag) { int ret; char *path= NULL; IsoImage *volume; Xorriso_alloc_meM(path, char, SfileadrL); ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, in_path, path, flag & 1); if(ret<=0) goto ex; if(eff_path!=NULL) strcpy(eff_path, path); ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; ret= Xorriso_node_from_path(xorriso, volume, path, node, flag & 1); if(ret<=0) {ret= 0; goto ex;} ret= 1; ex:; Xorriso_free_meM(path); return(ret); } /* @param flag */ int Xorriso_node_get_dev(struct XorrisO *xorriso, IsoNode *node, char *path, dev_t *dev, int flag) { *dev= iso_special_get_dev((IsoSpecial *) node); return(1); } /* @param flag bit0= *node is already valid bit1= add extra block for size estimation bit2= complain loudely if path is missing in image bit3= stbuf is to be used without eventual ACL bit4= try to obtain a better st_nlink count if hardlinks are enabled bit5= do not look for truncated versions of path component */ int Xorriso_fake_stbuf(struct XorrisO *xorriso, char *path, struct stat *stbuf, IsoNode **node, int flag) { int ret, min_hl, max_hl, node_idx, i; IsoImage *volume; IsoBoot *bootcat; uint32_t lba; char *catcontent = NULL; off_t catsize; dev_t dev_number; memset((char *) stbuf, 0, sizeof(struct stat)); if(!(flag&1)) { ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) return(-1); ret= Xorriso_node_from_path(xorriso, volume, path, node, ((flag >> 4) & 2) | !(flag&4)); if(ret<=0) *node= NULL; } if(*node==NULL) return(0); /* >>> stbuf->st_dev */ /* >>> stbuf->st_ino */ if(flag & 8) stbuf->st_mode= iso_node_get_perms_wo_acl(*node) & 07777; else stbuf->st_mode= iso_node_get_permissions(*node) & 07777; if(LIBISO_ISDIR(*node)) stbuf->st_mode|= S_IFDIR; else if(LIBISO_ISREG(*node)) stbuf->st_mode|= S_IFREG; else if(LIBISO_ISLNK(*node)) stbuf->st_mode|= S_IFLNK; else if(LIBISO_ISCHR(*node)) { stbuf->st_mode|= S_IFCHR; Xorriso_node_get_dev(xorriso, *node, path, &dev_number, 0); stbuf->st_rdev= dev_number; } else if(LIBISO_ISBLK(*node)) { stbuf->st_mode|= S_IFBLK; /* ts B11124: Debian mips and mipsel have sizeof(stbuf.st_rdev) == 4 whereas sizeof(dev_t) is 8. This workaround assumes that the returned numbers fit into 4 bytes. The may stem from the local filesystem or from the ISO image. At least there will be no memory corruption but only an integer rollover. */ Xorriso_node_get_dev(xorriso, *node, path, &dev_number, 0); stbuf->st_rdev= dev_number; } else if(LIBISO_ISFIFO(*node)) stbuf->st_mode|= S_IFIFO; else if(LIBISO_ISSOCK(*node)) stbuf->st_mode|= S_IFSOCK; else if(LIBISO_ISBOOT(*node)) stbuf->st_mode|= Xorriso_IFBOOT; /* >>> With directories this should be : number of subdirs + 2 */ /* >>> ??? How to obtain RR hardlink number for other types ? */ /* This may get overridden farther down */ stbuf->st_nlink= 1; stbuf->st_uid= iso_node_get_uid(*node); stbuf->st_gid= iso_node_get_gid(*node); if(LIBISO_ISREG(*node)) stbuf->st_size= iso_file_get_size((IsoFile *) *node)+ (2048 * !!(flag&2)); else if(LIBISO_ISBOOT(*node)) { ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret <= 0) return(-1); ret= iso_image_get_bootcat(volume, &bootcat, &lba, &catcontent, &catsize); if(catcontent != NULL) free(catcontent); if(ret < 0) { Xorriso_process_msg_queues(xorriso,0); return(-1); } stbuf->st_size= catsize; } else stbuf->st_size= 0; stbuf->st_blksize= 2048; stbuf->st_blocks= stbuf->st_size / (off_t) 2048; if(stbuf->st_blocks * (off_t) 2048 != stbuf->st_size) stbuf->st_blocks++; stbuf->st_atime= iso_node_get_atime(*node); stbuf->st_mtime= iso_node_get_mtime(*node); stbuf->st_ctime= iso_node_get_ctime(*node); if(LIBISO_ISDIR(*node) || (xorriso->ino_behavior & 1) || (!(flag & 16)) || xorriso->hln_array == NULL) return(1); /* Try to obtain a better link count */ ret= Xorriso_search_hardlinks(xorriso, *node, &node_idx, &min_hl, &max_hl, 0); if(ret < 0) return(ret); if(ret > 0 && node_idx >= 0) { for(i= min_hl; i <= max_hl; i++) { if(i == node_idx) continue; /* Check whether node is still valid */ if(iso_node_get_parent(xorriso->hln_array[i]) != NULL) stbuf->st_nlink++; } } return(1); } /* @param flag >>> bit0= follow links (i.e. stat() rather than lstat() bit1= do not return -2 on severe errors bit2= complain loudely if path is missing in image */ int Xorriso_iso_lstat(struct XorrisO *xorriso, char *path, struct stat *stbuf, int flag) { int ret; IsoNode *node; if(flag&1) { /* >>> follow link in ISO image */; } ret= Xorriso_fake_stbuf(xorriso, path, stbuf, &node, flag&4); if(ret>0) return(0); if(ret<0 && !(flag&2)) return(-2); return(-1); } int Xorriso_node_is_valid(struct XorrisO *xorriso, IsoNode *in_node, int flag) { IsoNode *node, *parent; for(node= in_node; 1; node= parent) { parent= (IsoNode *) iso_node_get_parent(node); if(parent == node) break; if(parent == NULL) return(0); /* Node is not in the tree (any more) */ } return(1); } int Xorriso_path_from_node(struct XorrisO *xorriso, IsoNode *in_node, char path[SfileadrL], int flag) { int ret, i, comp_count= 0; IsoNode *node, *parent, **components= NULL; char *wpt, *npt; for(node= in_node; 1; node= parent) { parent= (IsoNode *) iso_node_get_parent(node); if(parent == node) break; if(parent == NULL) return(0); /* Node is not in the tree (any more) */ comp_count++; } if(comp_count == 0) { strcpy(path, "/"); return(1); } components= calloc(comp_count, sizeof(IsoNode *)); if(components == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } i= comp_count; for(node= in_node; 1; node= parent) { parent= (IsoNode *) iso_node_get_parent(node); if(parent == node) break; components[--i]= node; } wpt= path; for(i= 0; i < comp_count; i++) { npt= (char *) iso_node_get_name(components[i]); if((wpt - path) + strlen(npt) + 1 >= SfileadrL) { /* >>> path is getting much too long */; ret= -1; goto ex; } *(wpt++)= '/'; strcpy(wpt, npt); wpt+= strlen(npt); *wpt= 0; } ret= 1; ex:; if(components != NULL) free(components); return(ret); } /* <<< The lookup from node pointer will be done by Xorriso_path_from_node() (Currently it runs a full tree traversal) Parameter node and flag bit0 will vanish then */ /* @param flag bit0= use lba rather than node pointer */ int Xorriso_path_from_lba(struct XorrisO *xorriso, IsoNode *node, off_t lba, char path[SfileadrL], int flag) { int ret; struct FindjoB *job= NULL; struct stat dir_stbuf; char *found_path; path[0]= 0; if((flag & 1) && lba <= 0) return(0); ret= Findjob_new(&job, "/", 0); if(ret <= 0) { Xorriso_no_findjob(xorriso, "path_from_node", 0); return(ret); } if(flag & 1) Findjob_set_lba_range(job, lba, (off_t) 1, 0); else Findjob_set_wanted_node(job, (void *) node, 0); Findjob_set_action_found_path(job, 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, "/", &dir_stbuf, 0, 0); if(ret > 0) { ret= 1; Findjob_get_found_path(job, &found_path, 0); if(found_path == NULL) ret= 0; else if(Sfile_str(path, found_path, 0) <= 0) ret= -1; } Findjob_destroy(&job, 0); return(ret); } /* @param flag bit0= in_node is valid, do not resolve iso_adr bit2= recognize and parse split parts despite xorriso->split_size <= 0 */ int Xorriso_identify_split(struct XorrisO *xorriso, char *iso_adr, void *in_node, struct SplitparT **parts, int *count, struct stat *total_stbuf, int flag) { int ret, i, incomplete= 0, overlapping= 0; int partno, total_parts, first_total_parts= -1; off_t offset, bytes, total_bytes, first_total_bytes= -1, first_bytes= -1; off_t size, covered; IsoImage *volume; IsoDir *dir_node; IsoDirIter *iter= NULL; IsoNode *node; char *name; struct stat stbuf, first_stbuf; *count= 0; *parts= NULL; if(xorriso->split_size <= 0 && !(flag & 4)) return(0); if(flag&1) { node= (IsoNode *) in_node; } else { ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) return(-1); ret= Xorriso_node_from_path(xorriso, volume, iso_adr, &node, 1); if(ret<=0) return(-1); } if(!LIBISO_ISDIR(node)) return(0); dir_node= (IsoDir *) node; ret= iso_dir_get_children(dir_node, &iter); if(ret<0) { cannot_iter:; Xorriso_cannot_create_iter(xorriso, ret, 0); return(-1); } for(i= 0; iso_dir_iter_next(iter, &node) == 1; i++) { name= (char *) iso_node_get_name(node); ret= Splitpart__parse(name, &partno, &total_parts, &offset, &bytes, &total_bytes, 0); if(ret<=0) {ret= 0; goto ex;} if(i==0) { first_total_parts= total_parts; first_bytes= bytes; first_total_bytes= total_bytes; Xorriso_fake_stbuf(xorriso, "", &first_stbuf, &node, 1); size= first_stbuf.st_size; } else { if(first_total_parts!=total_parts || first_total_bytes!=total_bytes || (first_bytes!=bytes && partno!=total_parts)) {ret= 0; goto ex;} Xorriso_fake_stbuf(xorriso, "", &stbuf, &node, 1); if(first_stbuf.st_mode != stbuf.st_mode || first_stbuf.st_uid != stbuf.st_uid || first_stbuf.st_gid != stbuf.st_gid || first_stbuf.st_mtime != stbuf.st_mtime || first_stbuf.st_ctime != stbuf.st_ctime) {ret= 0; goto ex;} size= stbuf.st_size; } /* check for plausible size */ if(!((partno != total_parts && size == bytes) || (partno == total_parts && size <= bytes))) {ret= 0; goto ex;} if(offset != first_bytes * (off_t) (partno - 1)) {ret= 0; goto ex;} (*count)++; } if(*count <= 0 || *count != first_total_parts) {ret= 0; goto ex;} ret= Splitparts_new(parts, (*count)+1, 0); /* (have one end marker item) */ if(ret<=0) return(ret); iso_dir_iter_free(iter); ret= iso_dir_get_children(dir_node, &iter); if(ret<0) goto cannot_iter; for(i= 0; i<*count; i++) { ret= iso_dir_iter_next(iter, &node); if(ret!=1) break; name= (char *) iso_node_get_name(node); ret= Splitpart__parse(name, &partno, &total_parts, &offset, &bytes, &total_bytes, 0); if(ret<=0) {ret= 0; goto ex;} ret= Splitparts_set(*parts, i, name, partno, total_parts, offset, bytes, total_bytes, 0); if(ret<=0) goto ex; } Splitparts_sort(*parts, *count, 0); covered= 0; for(i= 0; i<*count; i++) { Splitparts_get(*parts, i, &name, &partno, &total_parts, &offset, &bytes, &total_bytes, 0); if(offset>covered) incomplete= 1; else if(offset<covered) overlapping= 1; if(offset+bytes > covered) covered= offset+bytes; } if(total_bytes>covered) incomplete= 1; memcpy(total_stbuf, &first_stbuf, sizeof(struct stat)); total_stbuf->st_size= total_bytes; ret= !(overlapping || incomplete); ex:; if(iter!=NULL) iso_dir_iter_free(iter); return(ret); } /* @param flag bit0= node is valid, do not resolve path bit1= insist in complete collection of part files bit2= recognize and parse split parts despite xorriso->split_size <= 0 */ int Xorriso_is_split(struct XorrisO *xorriso, char *path, void *node, int flag) { struct SplitparT *split_parts= NULL; int split_count= 0, ret; struct stat stbuf; ret= Xorriso_identify_split(xorriso, path, node, &split_parts, &split_count, &stbuf, flag & 7); if(split_parts!=NULL) Splitparts_destroy(&split_parts, split_count, 0); return(ret>0); } int Xorriso_report_acl_header(struct XorrisO *xorriso, char *path, uid_t uid, gid_t gid, int flag) { int ret; struct passwd *pwd; struct group *grp; ret= Xorriso_getfname(xorriso, path, 0); if(ret <= 0) goto ex; pwd= getpwuid(uid); if(pwd == NULL) sprintf(xorriso->result_line, "# owner: %.f\n", (double) uid); else sprintf(xorriso->result_line, "# owner: %s\n", pwd->pw_name); Xorriso_result(xorriso, 0); grp= getgrgid(gid); if(grp == NULL) sprintf(xorriso->result_line, "# group: %.f\n", (double) gid); else sprintf(xorriso->result_line, "# group: %s\n", grp->gr_name); Xorriso_result(xorriso, 0); ret= 1; ex: return(ret); } /* @param node Opaque handle to IsoNode which is to be inquired instead of path if it is not NULL. @param path is used as address if node is NULL. @param acl_text if acl_text is not NULL, then *acl_text will be set to the ACL text (without comments) of the file object. In this case it finally has to be freed by the caller. @param flag bit0= do not report to result but only retrieve ACL text bit1= check for existence of true ACL (not fabricated), do not allocate and set acl_text but return 1 or 2 bit2-3: what ALC to retrieve: 0= "access" and "default", mark "default:" 1= "access" only 2= "default" only, do not mark "default:" bit4= get "access" ACL only if not trivial @return 2 ok, no ACL available, eventual *acl_text will be NULL 1 ok, ACL available, eventual *acl_text stems from malloc() <=0 error */ int Xorriso_getfacl(struct XorrisO *xorriso, void *in_node, char *path, char **acl_text, int flag) { int ret, d_ret, result_len= 0, pass, what; IsoNode *node; char *text= NULL, *d_text= NULL, *cpt, *npt; uid_t uid; gid_t gid; what= (flag >> 2) & 3; if(acl_text != NULL) *acl_text= NULL; node= (IsoNode *) in_node; if(node == NULL) { ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0); if(ret<=0) goto ex; } ret= iso_node_get_acl_text(node, &text, &d_text, flag & 16); d_ret= (d_text != NULL); if(ret < 0 || d_ret < 0) { if(path != NULL && path[0] != 0) { strcpy(xorriso->info_text, "Error with obtaining ACL of "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } ret= 0; goto ex; } if(flag & 2) { ret= 1 + (ret != 1 && d_ret == 0); goto ex; } if((ret == 0 || ret == 2) && d_ret == 0) { if(flag & 1) { ret= 1 + (ret == 0); goto ex; } strcpy(xorriso->info_text, "No ACL associated with "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); if(ret == 0) {ret= 2; goto ex;} } if(!(flag & 1)) { uid= iso_node_get_uid(node); gid= iso_node_get_gid(node); ret= Xorriso_report_acl_header(xorriso, path, uid, gid, 0); if(ret <= 0) goto ex; } for(pass= 0; pass < 1 + (acl_text != NULL && !(flag & 2)); pass++) { if(pass) { if(result_len == 0) break; *acl_text= calloc(result_len + 1, 1); if(*acl_text == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } } if(text != NULL && what <= 1) { for(npt= cpt= text; npt != NULL; cpt= npt + 1) { npt= strchr(cpt, '\n'); if(npt != NULL) *npt= 0; if(*cpt == 0) { if(d_text != NULL || pass) { if(npt != NULL) *npt= '\n'; continue; } } else result_len+= strlen(cpt) + 1; if(pass) { sprintf(*acl_text + strlen(*acl_text), "%s\n", cpt); } else if(!(flag & 1)) { if(Sfile_str(xorriso->result_line, cpt, 0) <= 0) goto too_long; strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); } if(npt != NULL) *npt= '\n'; } } if(d_text != NULL && (what == 0 || what == 2)) { for(npt= cpt= d_text; npt != NULL; cpt= npt + 1) { npt= strchr(cpt, '\n'); if(npt != NULL) *npt= 0; if(*cpt != 0) { if(pass) { if(what == 0) sprintf(*acl_text + strlen(*acl_text), "default:%s\n", cpt); else sprintf(*acl_text + strlen(*acl_text), "%s\n", cpt); } else { xorriso->result_line[0]= 0; if(what == 0) if(Sfile_str(xorriso->result_line, "default:", 0) <= 0) goto too_long; if(Sfile_str(xorriso->result_line, cpt, 1) <= 0) goto too_long; result_len+= strlen(cpt) + 9; } } else xorriso->result_line[0]= 0; if(pass== 0 && !(flag & 1)) { strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); } if(npt != NULL) *npt= '\n'; } } } if(result_len == 0) { if(acl_text != NULL) *acl_text= NULL; ret= 2; } else { ret= 1; } ex:; iso_node_get_acl_text(node, &text, &d_text, 1 << 15); return(ret); too_long: Xorriso_msgs_submit(xorriso, 0, "Oversized ACL", 0, "FAILURE", 0); ret= 0; goto ex; } /* @param flag bit0= do not report to result but only retrieve attr text bit1= path is disk_path bit3= do not ignore non-user attributes bit4= ignore isofs. attributes despite bit3 bit5= in case of symbolic link on disk: inquire link target bit6= check for existence of xattr, return 0 or 1 (depends also on bit3) bit7= ignore bit10 of xorriso->do_aaip (i.e. no -xattr any) */ int Xorriso_getfattr(struct XorrisO *xorriso, void *in_node, char *path, char **attr_text, int flag) { int ret= 1, i, bsl_mem, result_len= 0, pass; size_t num_attrs= 0, *value_lengths= NULL; char **names= NULL, **values= NULL, *bsl; if(attr_text != NULL) *attr_text= NULL; if((xorriso->do_aaip & 1024) && !((flag & 8) || (flag & 128))) flag|= 8 | 16; ret= Xorriso_get_attrs(xorriso, in_node, path, &num_attrs, &names, &value_lengths, &values, flag & (2 | 8 | 32)); if(ret <= 0) goto ex; if(flag & 64) { if(flag & 16) { ret= 0; for(i= 0; i < (int) num_attrs; i++) { if(strncmp(names[i], "isofs.", 6) != 0) { ret= 1; break; } } } else { ret= (num_attrs > 0); } goto ex; } if(num_attrs == 0) {ret= 2; goto ex;} if(!(flag & 1)) { ret= Xorriso_getfname(xorriso, path, 0); if(ret <= 0) goto ex; } for(pass= 0; pass < 1 + (attr_text != NULL); pass++) { if(pass) { *attr_text= calloc(result_len + 1, 1); if(*attr_text == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } } for(i= 0; i < (int) num_attrs; i++) { if(flag & 16) if(strncmp(names[i], "isofs.", 6) == 0) continue; if(strlen(names[i]) + value_lengths[i] >= SfileadrL) { sprintf(xorriso->result_line, "# oversized: name %d , value %d bytes\n", (int) strlen(names[i]), (int) value_lengths[i]); } else { ret= Sfile_bsl_encoder(&bsl, names[i], strlen(names[i]), 8); if(ret <= 0) {ret= -1; goto ex;} strcpy(xorriso->result_line, bsl); free(bsl); ret= Sfile_bsl_encoder(&bsl, values[i], value_lengths[i], 8); if(ret <= 0) {ret= -1; goto ex;} sprintf(xorriso->result_line + strlen(xorriso->result_line), "=\"%s\"\n", bsl); free(bsl); } /* temporarily disable -backslash_codes with result output */ result_len+= strlen(xorriso->result_line); if(pass) { strcat(*attr_text, xorriso->result_line); } else if(!(flag & 1)) { bsl_mem= xorriso->bsl_interpretation; xorriso->bsl_interpretation= 0; Xorriso_result(xorriso, 0); xorriso->bsl_interpretation= bsl_mem; } } } if((flag & 16) && attr_text != NULL) if(*attr_text != NULL) if((*attr_text)[0] == 0) { free(*attr_text); *attr_text= NULL; ret= 2; goto ex; } if(!(flag & 1)) { strcpy(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); } ret= 1; ex:; Xorriso_get_attrs(xorriso, in_node, path, &num_attrs, &names, &value_lengths, &values, 1 << 15); return(ret); } /* @param flag bit0= with mode "e" : Use echo -e encoding but do not put out commands and quotation marks. Rather apply double backslash. */ int Xorriso_append_extattr_comp(struct XorrisO *xorriso, char *comp, size_t comp_len, char *mode, int flag) { int ret; size_t line_limit; char *line, *wpt, *bsl = NULL; unsigned char *upt, *uval; line= xorriso->result_line; line_limit= sizeof(xorriso->result_line); uval= (unsigned char *) comp; if(*mode == 'q') { Text_shellsafe(comp, line, 1); } else if(*mode == 'e' || mode[0] == 0) { for(upt= uval; (size_t) (upt - uval) < comp_len; upt++) if(*upt <= 037 || *upt >= 0177) break; if((size_t) (upt - uval) < comp_len || (flag & 1)) { /* Use "$(echo -e '\0xyz')" */; if(!(flag & 1)) strcat(line, "\"$(echo -e '"); wpt= line + strlen(line); for(upt= uval; (size_t) (upt - uval) < comp_len; upt++) { if(wpt - line + 5 + 3 + 1 > (ssize_t) line_limit) /* "\\0xyz" + "')\"" + 0 */ goto too_much; if(*upt <= 037 || *upt >= 0177 || *upt == '\\' || *upt == '\'') { if(flag & 1) *(wpt++)= '\\'; sprintf((char *) wpt, "\\0%-3.3o", *upt); wpt+= strlen(wpt); } else { *(wpt++)= *upt; } } *wpt= 0; if(!(flag & 1)) strcpy(wpt, "')\""); } else { Text_shellsafe(comp, line, 1); } } else if(*mode == 'b') { ret= Sfile_bsl_encoder(&bsl, comp, comp_len, 8); if(ret <= 0) {ret= -1; goto ex;} if(strlen(line) + strlen(bsl) + 1 > line_limit) goto too_much; strcat(line, bsl); free(bsl); bsl= NULL; } else if(*mode == 'r') { if(strlen(line) + strlen(comp) + 1 > line_limit) goto too_much; strcat(line, comp); } ret= 1; ex:; if(bsl != NULL) free(bsl); return(ret); too_much:; Xorriso_msgs_submit(xorriso, 0, "Oversized BSD-style file attribute", 0, "FAILURE", 0); ret= -1; goto ex; } /* @param flag bit1= path is disk_path bit3= do not ignore eventual non-user attributes. bit5= in case of symbolic link on disk: inquire link target */ int Xorriso_list_extattr(struct XorrisO *xorriso, void *in_node, char *path, char *show_path, char *mode, int flag) { int ret= 1, i, bsl_mem; size_t num_attrs= 0, *value_lengths= NULL; char **names= NULL, **values= NULL, *cpt, *space_pt, *name_pt, *path_pt; char *line; unsigned char *upt, *uval; line= xorriso->result_line; ret= Xorriso_get_attrs(xorriso, in_node, path, &num_attrs, &names, &value_lengths, &values, flag & (2 | 8 | 32)); if(ret <= 0) goto ex; if(flag & 64) { ret= (num_attrs > 0); goto ex; } if(num_attrs == 0) {ret= 2; goto ex;} strcpy(line, "n="); path_pt= show_path + (show_path[0] == '/'); if(path_pt[0] == 0) path_pt= "."; ret= Xorriso_append_extattr_comp(xorriso, path_pt, strlen(path_pt), mode, 0); if(ret <= 0) goto ex; strcat(line, "\n"); Xorriso_result(xorriso, 0); for(i= 0; i < (int) num_attrs; i++) { line[0]= 0; uval= (unsigned char *) values[i]; if(strlen(names[i]) + value_lengths[i] >= SfileadrL) { sprintf(line, "echo 'OMITTED: Oversized: name %d bytes, value %d bytes in file '\"$n\" >&2\n", (int) strlen(names[i]), (int) value_lengths[i]); Xorriso_result(xorriso, 0); continue; } /* Form: $c space name value $n */ /* Split namespace from name */ cpt= strchr(names[i], '.'); if(cpt == NULL) { space_pt= "user"; name_pt= names[i]; } else { *cpt= 0; space_pt= names[i]; name_pt= cpt + 1; } /* FreeBSD setextattr cannot set 0-bytes */ for(upt= uval; (size_t) (upt - uval) < value_lengths[i]; upt++) if(*upt == 0 ) break; if((size_t) (upt - uval) < value_lengths[i]) { strcpy(line, "echo 'OMITTED: Value contains 0-bytes : space \"'\""); ret= Xorriso_append_extattr_comp(xorriso, space_pt, strlen(space_pt), "e", 1); if(ret <= 0) goto ex; strcat(line, "\"'\" , name \"'\""); ret= Xorriso_append_extattr_comp(xorriso, name_pt, strlen(name_pt), "e", 1); if(ret <= 0) goto ex; strcat(line, "\"'\" in file '\""); ret= Xorriso_append_extattr_comp(xorriso, path_pt, strlen(path_pt), "e", 1); if(ret <= 0) goto ex; strcat(line, "\" >&2\n"); /* temporarily disable -backslash_codes with result output */ bsl_mem= xorriso->bsl_interpretation; xorriso->bsl_interpretation= 0; Xorriso_result(xorriso, 0); xorriso->bsl_interpretation= bsl_mem; strcpy(line, "# "); } strcat(line, "$c "); ret= Xorriso_append_extattr_comp(xorriso, space_pt, strlen(space_pt), mode, 0); if(ret <= 0) goto ex; strcat(line, " "); ret= Xorriso_append_extattr_comp(xorriso,name_pt, strlen(name_pt), mode, 0); if(ret <= 0) goto ex; strcat(line, " "); ret= Xorriso_append_extattr_comp(xorriso, values[i], value_lengths[i], mode, 0); if(ret <= 0) goto ex; strcat(line, " \"$n\"\n"); /* temporarily disable -backslash_codes with result output */ bsl_mem= xorriso->bsl_interpretation; xorriso->bsl_interpretation= 0; Xorriso_result(xorriso, 0); xorriso->bsl_interpretation= bsl_mem; } strcpy(line, "\n"); Xorriso_result(xorriso, 0); ret= 1; ex:; Xorriso_get_attrs(xorriso, in_node, path, &num_attrs, &names, &value_lengths, &values, 1 << 15); return(ret); } /* @param flag Bitfield for control purposes bit0= get default ACL rather than access ACL bit4= set *text = NULL and return 2 if the ACL matches st_mode permissions. bit5= in case of symbolic link: inquire link target bit15= free text and return 1 @return 1 ok 2 ok, trivial ACL found while bit4 is set, *text is NULL 0 no ACL manipulation adapter available / ACL not supported by fs -1 failure of system ACL service (see errno) -2 attempt to inquire ACL of a symbolic link without bit4 or bit5 resp. with no suitable link target */ int Xorriso_local_getfacl(struct XorrisO *xorriso, char *disk_path, char **text, int flag) { int ret, skip= 0, colons= 0, countdown= 0; char *acl= NULL, *cpt, *wpt; if(flag & (1 << 15)) { if(*text != NULL) free(*text); *text= NULL; return(1); } *text= NULL; ret= iso_local_get_acl_text(disk_path, &acl, flag & (1 | 16 | 32)); Xorriso_process_msg_queues(xorriso,0); if(ret < 0 || ret == 2) return(ret); if(acl == NULL) return(0); *text= strdup(acl); iso_local_get_acl_text(disk_path, &acl, 1 << 15); if(*text == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); return(-1); } /* Garbage collection about trailing remarks after 3 permission chars */ wpt= *text; for(cpt= *text; *cpt; cpt++) { if(skip) { if(*cpt == '\n') skip= 0; else continue; } if(*cpt == ':' && !countdown) { colons++; if(colons == 2) { countdown= 4; colons= 0; } } if(countdown > 0) { countdown--; if(countdown == 0) skip= 1; } *wpt= *cpt; wpt++; } *wpt= 0; return(1); } /* @param flag bit1= path is disk_path bit3= do not ignore eventual non-user attributes. bit5= in case of symbolic link on disk: inquire link target bit15= free memory */ int Xorriso_get_attrs(struct XorrisO *xorriso, void *in_node, char *path, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag) { int ret, i, widx; IsoNode *node; if(flag & (1 << 15)) { if(flag & 2) { iso_local_get_attrs(NULL, num_attrs, names, value_lengths, values, 1 << 15); } else { iso_node_get_attrs(NULL, num_attrs, names, value_lengths, values, 1 << 15); } return(1); } *num_attrs= 0; if(flag & 2) { ret= iso_local_get_attrs(path, num_attrs, names, value_lengths, values, flag & (8 | 32)); if(ret < 0) { strcpy(xorriso->info_text, "Error with reading xattr of disk file "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); } } else { node= (IsoNode *) in_node; if(node == NULL) { ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0); if(ret<=0) goto ex; } ret= iso_node_get_attrs(node, num_attrs, names, value_lengths, values, 0); if(ret < 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when obtaining xattr of ISO node", 0, "FAILURE", 1); goto ex; } if(!(flag & 8)) { /* Filter away any non-userspace xattr */; widx= 0; for(i= 0; i < (int) *num_attrs; i++) { if(strncmp((*names)[i], "user.", 5) != 0) { free((*names)[i]); (*names)[i]= NULL; if((*values)[i] != NULL) { free((*values)[i]); (*values)[i]= NULL; } } else { if(widx != i) { (*names)[widx]= (*names)[i]; (*value_lengths)[widx]= (*value_lengths)[i]; (*values)[widx]= (*values)[i]; (*names)[i]= NULL; (*value_lengths)[i]= 0; (*values)[i]= NULL; } widx++; } } *num_attrs= widx; } } ret= 1; ex:; Xorriso_process_msg_queues(xorriso,0); return(ret); } int Xorriso_get_attr_value(struct XorrisO *xorriso, void *in_node, char *path, char *name, size_t *value_length, char **value, int flag) { int ret; size_t num_attrs= 0, *value_lengths= NULL, i; char **names = NULL, **values= NULL; *value= NULL; *value_length= 0; ret= Xorriso_get_attrs(xorriso, in_node, path, &num_attrs, &names, &value_lengths, &values, 8); if(ret <= 0) goto ex; for(i= 0; i < num_attrs; i++) { if(strcmp(name, names[i]) != 0) continue; *value= calloc(value_lengths[i] + 1, 1); if(*value == NULL) {ret= -1; goto ex;} memcpy(*value, values[i], value_lengths[i]); (*value)[value_lengths[i]]= 0; *value_length= value_lengths[i]; ret= 1; goto ex; } ret= 0; ex: Xorriso_get_attrs(xorriso, in_node, path, &num_attrs, &names, &value_lengths, &values, 1 << 15); return(ret); } int Xorriso_stream_type(struct XorrisO *xorriso, IsoNode *node, IsoStream *stream, char type_text[], int flag) { int ret, stream_type, block_size_log2; off_t lba; uint8_t zisofs_algo[2], algo_num; char text[5]; strncpy(text, stream->class->type, 4); text[4]= 0; if(strcmp(text, "fsrc") == 0) { ret= Xorriso__file_start_lba(node, &lba, 0); if(ret > 0 && lba > 0) strcpy(type_text, "image"); else strcpy(type_text, "disk"); } else if(strcmp(text, "ziso") == 0 || strcmp(text, "osiz") == 0) { if(strcmp(text, "ziso") == 0) strcpy(type_text, "--zisofs"); else strcpy(type_text, "--zisofs-decode"); ret= iso_stream_get_zisofs_par(stream, &stream_type, zisofs_algo, &algo_num, &block_size_log2, 0); if(ret == 1) sprintf(type_text + strlen(type_text), ":%c%c:%dk", zisofs_algo[0], zisofs_algo[1], 1 << (block_size_log2 - 10)); } else if(strcmp(text, "gzip") == 0) { strcpy(type_text, "--gzip"); } else if(strcmp(text, "pizg") == 0) { strcpy(type_text, "--gunzip"); } else if(strcmp(text, "cout") == 0 || strcmp(text, "boot") == 0 || strcmp(text, "user") == 0 || strcmp(text, "extf") == 0) { strcpy(type_text, text); } else { Text_shellsafe(text, type_text, 0); } return(1); } /* @param flag bit0= do not report to result but only retrieve md5 text @return 1= ok, 0= no md5 available, <0= other error */ int Xorriso_get_md5(struct XorrisO *xorriso, void *in_node, char *path, char md5[16], int flag) { int ret= 1, i; char *wpt; IsoImage *image; IsoNode *node; ret= Xorriso_get_volume(xorriso, &image, 0); if(ret <= 0) goto ex; node= (IsoNode *) in_node; if(node == NULL) { ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0); if(ret<=0) goto ex; } if(!LIBISO_ISREG(node)) return(0); ret= iso_file_get_md5(image, (IsoFile *) node, md5, 0); Xorriso_process_msg_queues(xorriso,0); if(ret <= 0) goto ex; if(flag & 1) {ret= 1; goto ex;} wpt= xorriso->result_line; for(i= 0; i < 16; i++) { sprintf(wpt, "%2.2x", ((unsigned char *) md5)[i]); wpt+= 2; } strcpy(wpt, " "); wpt+= 2; Xorriso_getfname(xorriso, path, 1 | 2); ret= 1; ex:; return(ret); } int Xorriso_make_md5(struct XorrisO *xorriso, void *in_node, char *path, int flag) { int ret; off_t size; IsoNode *node; node= (IsoNode *) in_node; if(node == NULL) { ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0); if(ret <= 0) return(ret); } if(!LIBISO_ISREG(node)) return(0); ret= iso_file_make_md5((IsoFile *) node, 0); size= iso_file_get_size((IsoFile *) node); xorriso->pacifier_count+= size; xorriso->pacifier_byte_count+= size; Xorriso_pacifier_callback(xorriso, "content bytes read", xorriso->pacifier_count, 0, "", 8); Xorriso_process_msg_queues(xorriso, 0); if(ret < 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when computing MD5", 0, "FAILURE", 1); return(0); } Xorriso_set_change_pending(xorriso, 1); return(1); } /* @param flag bit0= do not only sum up sizes but also print subdirs bit2= do not report result by Xorriso_result() */ int Xorriso_show_du_subs(struct XorrisO *xorriso, IsoDir *dir_node, char *abs_path, char *rel_path, off_t *size, off_t boss_mem, int flag) { int i, ret, no_sort= 0, filec= 0, l; IsoDirIter *iter= NULL; IsoNode *node, **node_array= NULL; char *name; off_t sub_size, report_size, mem= 0; char *path= NULL, *show_path= NULL, *sfe= NULL; Xorriso_alloc_meM(sfe, char, 5 * SfileadrL); Xorriso_alloc_meM(path, char, SfileadrL); Xorriso_alloc_meM(show_path, char, SfileadrL); *size= 0; ret= iso_dir_get_children(dir_node, &iter); if(ret<0) { cannot_create_iter:; Xorriso_cannot_create_iter(xorriso, ret, 0); {ret= -1; goto ex;} } for(i= 0; iso_dir_iter_next(iter, &node) == 1; ) { sub_size= 0; name= (char *) iso_node_get_name(node); strcpy(show_path, rel_path); if(Sfile_add_to_path(show_path, name, 0)<=0) goto much_too_long; if(LIBISO_ISDIR(node)) { strcpy(path, abs_path); if(Sfile_add_to_path(path, name, 0)<=0) { much_too_long:; Xorriso_much_too_long(xorriso, strlen(path)+strlen(name)+1, 2); {ret= -1; goto ex;} } filec++; l= strlen(rel_path)+1; mem+= l; if(l % sizeof(char *)) mem+= sizeof(char *)-(l % sizeof(char *)); if(flag&1) /* diving and counting is done further below */ continue; ret= Xorriso_show_du_subs(xorriso, (IsoDir *) node, path, show_path, &sub_size, boss_mem, 0); if(ret<0) goto ex; if(ret==0) continue; } if(LIBISO_ISREG(node)) { sub_size+= iso_file_get_size((IsoFile *) node)+2048; /* sub_size+= iso_file_get_size((IsoFile *) node)+strlen(name)+1; */ } if(sub_size>0) (*size)+= sub_size; Xorriso_process_msg_queues(xorriso,0); } if(filec<=0 || !(flag&1)) {ret= 1; goto ex;} /* Reset iteration */ iso_dir_iter_free(iter); iter= NULL; Xorriso_process_msg_queues(xorriso,0); ret= Xorriso_sorted_node_array(xorriso, dir_node, &filec, &node_array, boss_mem, 1|2|4); if(ret<0) goto ex; if(ret==0) { no_sort= 1; ret= iso_dir_get_children(dir_node, &iter); if(ret<0) goto cannot_create_iter; } for(i= 0; (no_sort || i<filec) && !(xorriso->request_to_abort); i++) { if(no_sort) { ret= iso_dir_iter_next(iter, &node); if(ret!=1) break; if(!LIBISO_ISDIR(node)) continue; } else node= node_array[i]; sub_size= 0; name= (char *) iso_node_get_name(node); strcpy(show_path, rel_path); if(Sfile_add_to_path(show_path, name, 0)<=0) goto much_too_long; strcpy(path, abs_path); if(Sfile_add_to_path(path, name, 0)<=0) goto much_too_long; ret= Xorriso_show_du_subs(xorriso, (IsoDir *) node, path, show_path, &sub_size, boss_mem+mem, flag&1); if(ret<0) goto ex; if(LIBISO_ISREG(node)) { sub_size+= iso_file_get_size((IsoFile *) node)+2048; /* sub_size+= iso_tree_node_get_size((IsoFile *) node)+strlen(name)+1; */ } if(sub_size>0) (*size)+= sub_size; report_size= sub_size/1024; if(report_size*1024<sub_size) report_size++; if(!(flag & 4)) { if(xorriso->sh_style_result) sprintf(xorriso->result_line, "%-7.f ",(double) (report_size)); else sprintf(xorriso->result_line, "%7.f ",(double) (report_size)); sprintf(xorriso->result_line+strlen(xorriso->result_line), "%s\n", Xorriso_esc_filepath(xorriso, show_path, sfe, 0)); Xorriso_result(xorriso, 0); } } ret= 1; ex:; Xorriso_free_meM(sfe); Xorriso_free_meM(path); Xorriso_free_meM(show_path); if(iter!=NULL) iso_dir_iter_free(iter); if(node_array!=NULL) free((char *) node_array); Xorriso_process_msg_queues(xorriso,0); return(ret); } int Xorriso_sorted_dir_i(struct XorrisO *xorriso, IsoDir *dir_node, int *filec, char ***filev, off_t boss_mem, int flag) { int i,j,ret; IsoDirIter *iter= NULL; IsoNode *node; char *name; off_t mem; (*filec)= 0; (*filev)= NULL; ret= iso_dir_get_children(dir_node, &iter); if(ret<0) { cannot_iter:; Xorriso_cannot_create_iter(xorriso, ret, 0); {ret= -1; goto ex;} } mem= 0; for(i= 0; iso_dir_iter_next(iter, &node) == 1; ) { name= (char *) iso_node_get_name(node); mem+= sizeof(char *)+strlen(name)+8; (*filec)++; } iso_dir_iter_free(iter); iter= NULL; if(*filec==0) {ret= 1; goto ex;} ret= Xorriso_check_temp_mem_limit(xorriso, mem+boss_mem, 2); if(ret<=0) goto ex; (*filev)= (char **) calloc(*filec, sizeof(char *)); if(*filev==NULL) {ret= -1; goto ex; } ret= iso_dir_get_children(dir_node, &iter); if(ret<0) goto cannot_iter; for(i= 0; i<*filec; i++) { ret= iso_dir_iter_next(iter, &node); if(ret!=1) break; name= (char *) iso_node_get_name(node); (*filev)[i]= strdup(name); if((*filev)[i]==NULL) { for(j= 0; j<i; j++) if((*filev)[j]!=NULL) free((*filev)[j]); free((char *) (*filev)); ret= -1; goto ex; } } Sort_argv(*filec, *filev, 0); ret= 1; ex:; if(iter!=NULL) iso_dir_iter_free(iter); return(ret); } int Xorriso_node_eff_hidden(struct XorrisO *xorriso, IsoNode *node, int flag) { int hidden_state= 0, ret; IsoNode *current, *parent; current= node; for(current= node; hidden_state != 7;) { ret= iso_node_get_hidden(current); if(ret & LIBISO_HIDE_ON_RR) hidden_state|= 1; if(ret & LIBISO_HIDE_ON_JOLIET) hidden_state|= 2; if(ret & LIBISO_HIDE_ON_HFSPLUS) hidden_state|= 4; parent= (IsoNode *) iso_node_get_parent(current); if(parent == current) break; current= parent; } return(hidden_state); } /* @param flag bit0= do not truncate components which contain any of "*?[" */ int Xorriso_truncate_path_comps(struct XorrisO *xorriso, char *path, char *buffer, char **resultpt, int flag) { char *rpt, *basrpt, *wpt, *baswpt, *cpt; int ended, ret, skip; *resultpt= path; /* Check component lengths */ rpt= path; if(*rpt == '/') rpt++; for(ended= 0; !ended;) { basrpt= rpt; rpt= strchr(basrpt, '/'); if(rpt == NULL) { rpt= basrpt + strlen(basrpt); ended= 1; } skip= 0; if(flag & 1) { for(cpt= basrpt; cpt < rpt; cpt++) { if(strchr("*?[", *cpt) != NULL) { skip= 1; break; } } } if((!skip) && rpt - basrpt > xorriso->file_name_limit) { ended= 0; break; } if(*rpt == '/') rpt++; } if(ended) return(1); /* All short enough */ /* Some truncation is needed */ buffer[0]= 0; wpt= buffer; if(path[0] == '/') *(wpt++)= '/'; rpt= path; if(*rpt == '/') rpt++; for(ended= 0; !ended;) { basrpt= rpt; baswpt= wpt; rpt= strchr(basrpt, '/'); if(rpt == NULL) { rpt= basrpt + strlen(basrpt); ended= 1; } skip= 0; for(cpt= basrpt; cpt < rpt; cpt++) { *(wpt++)= *cpt; if((flag & 1) && strchr("*?[", *cpt) != NULL) skip= 1; } *wpt= 0; if((!skip) && rpt - basrpt > xorriso->file_name_limit) { ret= iso_truncate_leaf_name(1, xorriso->file_name_limit, baswpt, 0); Xorriso_process_msg_queues(xorriso, 0); if(ret < 0) return(0); wpt= baswpt + strlen(baswpt); } if(!ended) { *(wpt++)= '/'; rpt++; } } *resultpt= buffer; return(1); } /* @param flag bit0= long format bit1= do not print count of nodes bit2= du format bit3= print directories as themselves (ls -d) bit4= lsattr format bit5= do not look for truncated versions of path component */ int Xorriso_ls_filev(struct XorrisO *xorriso, char *wd, int filec, char **filev, off_t boss_mem, int flag) { int i, ret, was_error= 0, dfilec= 0, pass, passes, hidden_state= 0; int is_ls= 0, is_lsl= 0, max_bit; uint64_t lfa_flags; IsoNode *node; IsoImage *volume; char *path= NULL, *link_target= NULL, *rpt, **dfilev= NULL; char *a_text= NULL, *d_text= NULL, *namept, *lfa_text= NULL; off_t size; struct stat stbuf; Xorriso_alloc_meM(path, char, SfileadrL); Xorriso_alloc_meM(link_target, char, SfileadrL); rpt= xorriso->result_line; ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; Sort_argv(filec, filev, 0); /* Count valid nodes, warn of invalid ones */ for(i= 0; i<filec; i++) { ret= Xorriso_make_abs_adr(xorriso, wd, filev[i], path, 1|2|4); if(ret<=0) { was_error++; continue; } ret= Xorriso_node_from_path(xorriso, volume, path, &node, 1 | ((flag >> 4) & 2)); if(ret<=0) { sprintf(xorriso->info_text, "Not found in ISO image: "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); was_error++; continue; } } is_ls= !(flag & (4 | 16)); is_lsl= is_ls && (flag & 1); if((flag&8) && !(flag & (2 | 4 | 16))) { sprintf(xorriso->info_text, "Valid ISO nodes found: %d\n", filec-was_error); Xorriso_info(xorriso,1); if(filec-was_error<=0) {ret= !was_error; goto ex;} } passes= 1+!(flag&(4|8)); for(pass= 0; pass<passes; pass++) for(i= 0; i<filec && !(xorriso->request_to_abort); i++) { rpt[0]= 0; ret= Xorriso_make_abs_adr(xorriso, wd, filev[i], path, 1|2|4); if(ret<=0) continue; ret= Xorriso_fake_stbuf(xorriso, path, &stbuf, &node, ((flag&4) >> 1) | 16); if(ret<=0) continue; if(LIBISO_ISDIR(node) && !(flag&(4|8))) { if(pass==0) continue; if(filec>1) { strcpy(xorriso->result_line, "\n"); Xorriso_result(xorriso,0); Xorriso_esc_filepath(xorriso, filev[i], xorriso->result_line, 0); strcat(xorriso->result_line, ":\n"); Xorriso_result(xorriso,0); } ret= Xorriso_sorted_dir_i(xorriso, (IsoDir *) node, &dfilec, &dfilev, boss_mem, 0); if(ret<=0) { /* >>> libisofs iterator loop and single item Xorriso_lsx_filev() */; } else { if(flag&1) { sprintf(xorriso->result_line, "total %d\n", dfilec); Xorriso_result(xorriso,0); } Xorriso_ls_filev(xorriso, path, dfilec, dfilev, boss_mem, (flag & (1 | 16)) | 2 | 8); } if(dfilec>0) Sfile_destroy_argv(&dfilec, &dfilev, 0); continue; } else if(pass>0) continue; link_target[0]= 0; if(is_lsl) { /* -ls_l */ iso_node_get_acl_text(node, &a_text, &d_text, 16); hidden_state= Xorriso_node_eff_hidden(xorriso, node, 0); ret= Xorriso_format_ls_l(xorriso, &stbuf, 1 | ((a_text != NULL || d_text != NULL) << 1) | (hidden_state << 2)); iso_node_get_acl_text(node, &a_text, &d_text, 1 << 15); if(ret<=0) continue; if(LIBISO_ISLNK(node)) { if(Sfile_str(link_target, (char *) iso_symlink_get_dest( (IsoSymlink *) node), 0)<=0) link_target[0]= 0; } } else if(flag&4) { /* -du or -dus */ size= stbuf.st_size; if(S_ISDIR(stbuf.st_mode)) { ret= Xorriso_show_du_subs(xorriso, (IsoDir *) node, path, filev[i], &size, boss_mem, flag&1); if(ret<0) {ret= -1; goto ex;} if(ret==0) continue; } if(xorriso->sh_style_result) sprintf(rpt, "%-7.f ",(double) (size/1024)); else sprintf(rpt, "%7.f ",(double) (size/1024)); } else if(flag & 16) { /* -lsattr */ ret= iso_node_get_lfa_flags(node, &lfa_flags, &max_bit, 0); if(ret < 0) { Xorriso_report_iso_error(xorriso, path, ret, "Error when obtaining lsattr flags of ISO node", 0, "WARNING", 1 | 2); {ret= -1; goto ex;} } else if(ret >= 0) { if(ret == 0) lfa_flags= 0; ret= iso_util_encode_lfa_flags(lfa_flags, &lfa_text, 1); if(ret == (int) ISO_LFA_UNKNOWN_BIT) { sprintf(xorriso->info_text, "Cannot convert all flag bits for -lsattr : 0x%lX", (unsigned long) lfa_flags); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } else if(ret < 0) { Xorriso_report_iso_error(xorriso, path, ret, "Error when converting lsattr flags to text", 0, "WARNING", 1 | 2); {ret= -1; goto ex;} } sprintf(rpt, "%-22s ", lfa_text); if(lfa_text != NULL) free(lfa_text); } } Xorriso_truncate_path_comps(xorriso, filev[i], path, &namept, 0); if(link_target[0] && is_lsl) { Xorriso_esc_filepath(xorriso, namept, xorriso->result_line, 1); strcat(xorriso->result_line, " -> "); Xorriso_esc_filepath(xorriso, link_target, xorriso->result_line, 1 | 2); } else { Xorriso_esc_filepath(xorriso, namept, xorriso->result_line, 1); } strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); } ret= !was_error; ex:; Xorriso_free_meM(path); Xorriso_free_meM(link_target); return(ret); } /* @return: <=0 = error, 1= directory tree, 2= path leads to non-directory */ int Xorriso_get_dus(struct XorrisO *xorriso, char *iso_rr_path, off_t *size, off_t boss_mem, int flag) { int ret; IsoNode *node; char *path= NULL; struct stat stbuf; Xorriso_alloc_meM(path, char, SfileadrL); ret= Xorriso_make_abs_adr(xorriso, xorriso->wdi, iso_rr_path, path, 1 | 2 | 4); if(ret <= 0) goto ex; ret= Xorriso_fake_stbuf(xorriso, path, &stbuf, &node, 0); if(ret <= 0) goto ex; if(!S_ISDIR(stbuf.st_mode)) { *size= stbuf.st_size; ret= 2; goto ex; } ret= Xorriso_show_du_subs(xorriso, (IsoDir *) node, path, iso_rr_path, size, boss_mem, 4); if(ret <= 0) goto ex; ret= 1; ex: Xorriso_free_meM(path); return(ret); } /* This function needs less buffer memory than Xorriso_ls_filev() but cannot perform structured pattern matching as done by Xorriso_expand_pattern() for subsequent Xorriso_ls_filev(). @param flag bit0= long format bit1= only check for directory existence bit2= do not apply search pattern but accept any file bit3= just count nodes and return number */ int Xorriso_ls(struct XorrisO *xorriso, int flag) { int ret, i, filec= 0, failed_at, no_sort= 0; IsoNode *node, **node_array= NULL; IsoDir *dir_node; IsoDirIter *iter= NULL; char *link_target= NULL, *npt, *rpt; struct stat stbuf; Xorriso_alloc_meM(link_target, char, SfileadrL); rpt= xorriso->result_line; ret= Xorriso_dir_from_path(xorriso, "Working directory", xorriso->wdi, &dir_node, 0); if(ret <= 0) goto ex; ret= iso_dir_get_children(dir_node, &iter); if(ret<0) { cannot_create_iter:; Xorriso_cannot_create_iter(xorriso, ret, 0); {ret= -1; goto ex;} } Xorriso_process_msg_queues(xorriso,0); for(i= 0; iso_dir_iter_next(iter, &node) == 1; ) { npt= (char *) iso_node_get_name(node); if(!(flag&4)) { ret= Xorriso_regexec(xorriso, npt, &failed_at, 0); if(ret) continue; /* no match */ } filec++; } /* Reset iteration */ iso_dir_iter_free(iter); iter= NULL; Xorriso_process_msg_queues(xorriso,0); if(flag&8) {ret= filec; goto ex;} sprintf(xorriso->info_text, "Valid ISO nodes found: %d\n", filec); Xorriso_info(xorriso,1); ret= Xorriso_sorted_node_array(xorriso, dir_node, &filec, &node_array, 0, flag&4); if(ret<0) goto ex; if(ret==0) { no_sort= 1; ret= iso_dir_get_children(dir_node, &iter); if(ret<0) goto cannot_create_iter; } for(i= 0; i<filec && !(xorriso->request_to_abort); i++) { if(no_sort) { ret= iso_dir_iter_next(iter, &node); if(ret!=1) break; npt= (char *) iso_node_get_name(node); if(!(flag&4)) { ret= Xorriso_regexec(xorriso, npt, &failed_at, 0); if(ret) continue; /* no match */ } } else node= node_array[i]; npt= (char *) iso_node_get_name(node); link_target[0]= 0; if(LIBISO_ISLNK(node)) { if(Sfile_str(link_target, (char *) iso_symlink_get_dest( (IsoSymlink *) node), 0)<=0) link_target[0]= 0; } rpt[0]= 0; if(flag&1) { ret= Xorriso_fake_stbuf(xorriso, "", &stbuf, &node, 1); if(ret<=0) continue; ret= Xorriso_format_ls_l(xorriso, &stbuf, 1); if(ret<=0) continue; } if(link_target[0] && (flag&1)) { Xorriso_esc_filepath(xorriso, npt, xorriso->result_line, 1); strcat(xorriso->result_line, " -> "); Xorriso_esc_filepath(xorriso, link_target, xorriso->result_line, 1 | 2); } else { Xorriso_esc_filepath(xorriso, npt, xorriso->result_line, 1); } strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); } ret= 1; ex:; if(iter!=NULL) iso_dir_iter_free(iter); Xorriso_process_msg_queues(xorriso,0); if(node_array!=NULL) free((char *) node_array); Xorriso_free_meM(link_target); return(ret); } /* @param flag bit0= count results rather than storing them bit1= this is a recursion bit2= prepend wd (automatically done if wd[0]!=0) */ int Xorriso_obtain_pattern_files_i( struct XorrisO *xorriso, char *wd, IsoDir *dir, int *filec, char **filev, int count_limit, off_t *mem, int *dive_count, int flag) { int ret, failed_at; IsoDirIter *iter= NULL; IsoNode *node; char *name; char *adr= NULL; adr= malloc(SfileadrL); if(adr==NULL) { Xorriso_no_malloc_memory(xorriso, &adr, 0); {ret= -1; goto ex;} } if(!(flag&2)) *dive_count= 0; else (*dive_count)++; ret= Xorriso_check_for_root_pattern(xorriso, filec, filev, count_limit, mem, (flag&1)|2); if(ret!=2) goto ex; ret= iso_dir_get_children(dir, &iter); if(ret<0) { Xorriso_cannot_create_iter(xorriso, ret, 0); {ret= -1; goto ex;} } while(iso_dir_iter_next(iter, &node) == 1) { name= (char *) iso_node_get_name(node); ret= Xorriso_make_abs_adr(xorriso, wd, name, adr, flag&4); if(ret<=0) goto ex; ret= Xorriso_regexec(xorriso, adr, &failed_at, 1); if(ret) { /* no match */ if(failed_at <= *dive_count) /* no hope for a match */ continue; if(!LIBISO_ISDIR(node)) { /* >>> How to deal with softlinks ? */ continue; } /* dive deeper */ ret= Xorriso_obtain_pattern_files_i( xorriso, adr, (IsoDir *) node, filec, filev, count_limit, mem, dive_count, flag|2); if(ret<=0) goto ex; } else { ret= Xorriso_register_matched_adr(xorriso, adr, count_limit, filec, filev, mem, (flag&1)|2); if(ret<=0) goto ex; } } ret= 1; ex:; if(adr!=NULL) free(adr); if(flag&2) (*dive_count)--; if(iter != NULL) iso_dir_iter_free(iter); return(ret); } /* @param flag bit0= a match count !=1 is a FAILURE event bit1= with bit0 tolerate 0 matches if pattern is a constant bit2= do not issue debug messages about temporary memory needs bit3= do not add unresolved pattern to filev */ int Xorriso_expand_pattern(struct XorrisO *xorriso, int num_patterns, char **patterns, int extra_filec, int *filec, char ***filev, off_t *mem, int flag) { int ret, count= 0, abs_adr= 0, i, was_count, was_filec; int nonconst_mismatches= 0, dive_count= 0; IsoImage *volume; IsoDir *dir= NULL, *root_dir; IsoNode *iso_node; *filec= 0; *filev= NULL; xorriso->search_mode= 3; xorriso->structured_search= 1; ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) return(ret); root_dir= iso_image_get_root(volume); if(root_dir==NULL) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "While expanding pattern : Cannot obtain root node of ISO image"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); ret= -1; goto ex; } for(i= 0; i<num_patterns; i++) { ret= Xorriso_prepare_expansion_pattern(xorriso, patterns[i], 0); if(ret<=0) return(ret); if(ret==2) abs_adr= 4; if(patterns[i][0]=='/' || abs_adr) { dir= root_dir; abs_adr= 4; } else { /* This is done so late to allow the following: It is not an error if xorriso->wdi does not exist yet, but one may not use it as base for relative address searches. */ ret= Xorriso_node_from_path(xorriso, volume, xorriso->wdi, &iso_node, 1); dir= (IsoDir *) iso_node; if(ret<=0) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "While expanding pattern "); Text_shellsafe(patterns[i], xorriso->info_text, 1); strcat(xorriso->info_text, " : Working directory does not exist in ISO image"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(!LIBISO_ISDIR((IsoNode *) dir)) { sprintf(xorriso->info_text, "Working directory path does not lead to a directory in ISO image"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } /* count the matches */ was_count= count; ret= Xorriso_obtain_pattern_files_i(xorriso, "", dir, &count, NULL, 0, mem, &dive_count, 1 | abs_adr); if(ret<=0) goto ex; if(was_count==count && strcmp(patterns[i],"*")!=0 && (flag&3)!=1 && !(flag & 8)) { count++; Xorriso_eval_nonmatch(xorriso, patterns[i], &nonconst_mismatches, mem, 0); } } ret= Xorriso_check_matchcount(xorriso, count, nonconst_mismatches, num_patterns, patterns, (flag&1)|2); if(ret<=0) goto ex; count+= extra_filec; (*mem)+= extra_filec * sizeof(char *); if(count<=0) {ret= !!(flag & 8); goto ex;} ret= Xorriso_alloc_pattern_mem(xorriso, *mem, count, filev, !!(flag & 4)); if(ret<=0) goto ex; /* now store addresses */ for(i= 0; i<num_patterns; i++) { ret= Xorriso_prepare_expansion_pattern(xorriso, patterns[i], 0); if(ret<=0) return(ret); if(ret==2) abs_adr= 4; was_filec= *filec; ret= Xorriso_obtain_pattern_files_i(xorriso, "", dir, filec, *filev, count, mem, &dive_count, abs_adr); if(ret<=0) goto ex; if(was_filec == *filec && strcmp(patterns[i],"*")!=0 && (flag&3) != 1 && !(flag & 8)) { (*filev)[*filec]= strdup(patterns[i]); if((*filev)[*filec]==NULL) { (*mem)= strlen(patterns[i])+1; Xorriso_no_pattern_memory(xorriso, *mem, 0); ret= -1; goto ex; } (*filec)++; } } ret= 1; ex:; if(ret<=0) { Sfile_destroy_argv(&count, filev, 0); *filec= 0; } return(ret); } /* @return 1= size is valid , 0= not a regular file , -1 = error */ int Xorriso__get_file_size(IsoNode *node, off_t *size, int flag) { *size= 0; if(!LIBISO_ISREG(node)) return(0); *size= iso_file_get_size((IsoFile *) node); return(1); } int Xorriso__start_end_lbas(IsoNode *node, int *lba_count, off_t **start_lbas, off_t **end_lbas, off_t **section_sizes, off_t *size, int flag) { int section_count= 0, ret, i; struct iso_file_section *sections= NULL; *lba_count= 0; *start_lbas= *end_lbas= NULL; *section_sizes= NULL; *size= 0; if(!LIBISO_ISREG(node)) return(0); *size= iso_file_get_size((IsoFile *) node); ret= iso_file_get_old_image_sections((IsoFile *) node, §ion_count, §ions, 0); if(ret < 0) {ret= -1; goto ex;} if(ret != 1 || section_count <= 0) {ret= 0; goto ex;} *start_lbas= calloc(section_count, sizeof(off_t)); *end_lbas= calloc(section_count, sizeof(off_t)); *section_sizes= calloc(section_count, sizeof(off_t)); if(*start_lbas == NULL || *end_lbas == NULL || *section_sizes == NULL) {ret= -1; goto ex;} for(i= 0; i < section_count; i++) { (*start_lbas)[i]= sections[i].block; (*end_lbas)[i]= sections[i].block + sections[i].size / 2048 - 1; if(sections[i].size % 2048) (*end_lbas)[i]++; (*section_sizes)[i]= sections[i].size; } *lba_count= section_count; ret= 1; ex:; if(sections != NULL) free((char *) sections); if(ret <= 0) { if((*start_lbas) != NULL) free((char *) *start_lbas); if((*end_lbas) != NULL) free((char *) *end_lbas); *start_lbas= *end_lbas= NULL; *lba_count= 0; } return(ret); } int Xorriso__file_start_lba(IsoNode *node, off_t *lba, int flag) { int lba_count= 0, i, ret; off_t size, *section_sizes= NULL, *start_lbas= NULL, *end_lbas= NULL; *lba= -1; ret= Xorriso__start_end_lbas(node, &lba_count, &start_lbas, &end_lbas, §ion_sizes, &size, 0); if(ret <= 0) return(ret); for(i= 0; i < lba_count; i++) { if(*lba < 0 || start_lbas[i] < *lba) *lba= start_lbas[i]; } if(start_lbas != NULL) free((char *) start_lbas); if(end_lbas != NULL) free((char *) end_lbas); if(section_sizes != NULL) free((char *) section_sizes); if(*lba < 0) return(0); return(1); } /* flag bit0= examine sub directories rather than data files */ int Xorriso_dir_disk_path(struct XorrisO *xorriso, IsoNode *dir_node, char disk_path[SfileadrL], int flag) { int ret; char *npt; IsoNode *node; IsoDir *dir; IsoDirIter *iter= NULL; dir= (IsoDir *) dir_node; ret= iso_dir_get_children(dir, &iter); if(ret<0) { Xorriso_cannot_create_iter(xorriso, ret, 0); {ret= -1; goto ex;} } while(1) { ret= iso_dir_iter_next(iter, &node); if(ret < 0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when iterating over directory", 0, "FAILURE", 1); ret= -1; goto ex; } if(ret == 0) break; if(LIBISO_ISDIR(node) && (flag & 1)) { ret= Xorriso_dir_disk_path(xorriso, node, disk_path, flag); if(ret < 0) goto ex; if(ret == 0) continue; } else if(LIBISO_ISREG(node) && !(flag & 1)) { ret= Xorriso_retrieve_disk_path(xorriso, node, disk_path, 0); if(ret < 0) goto ex; if(ret == 0) continue; } else continue; /* Use its parent dir as answer */ npt= strrchr(disk_path, '/'); if(npt == NULL || npt == disk_path) strcpy(disk_path, "/"); else *npt= 0; ret= 1; goto ex; } if(!(flag & 1)) ret= Xorriso_dir_disk_path(xorriso, dir_node, disk_path, 1); else ret= 0; ex: if(iter != NULL) iso_dir_iter_free(iter); return(ret); } int Xorriso_retrieve_disk_path(struct XorrisO *xorriso, IsoNode *node, char disk_path[SfileadrL], int flag) { IsoFile *file; IsoStream *stream= NULL, *input_stream; char type_text[80], *source_path = NULL; int ret; if(LIBISO_ISDIR(node)) { ret= Xorriso_dir_disk_path(xorriso, node, disk_path, 0); return(ret); } if(!LIBISO_ISREG(node)) return(0); /* Obtain most fundamental input stream */ file= (IsoFile *) node; input_stream= iso_file_get_stream(file); if(input_stream == NULL) return(0); while(1) { stream= input_stream; input_stream= iso_stream_get_input_stream(stream, 0); if(input_stream == NULL) break; } /* Obtain disk path if applicable */ type_text[0]= 0; Xorriso_stream_type(xorriso, node, stream, type_text, 0); if(strcmp(type_text, "disk") != 0 && strcmp(type_text, "cout") != 0) return(0); /* among othersi rejected: "image" */ source_path= iso_stream_get_source_path(stream, 0); if(source_path == NULL) return(0); if(strlen(source_path) >= SfileadrL) { free(source_path); return(0); } strcpy(disk_path, source_path); free(source_path); return(1); } /* @param flag bit0= show numbers from iso_stream_get_id */ int Xorriso_show_stream(struct XorrisO *xorriso, void *in_node, char *path, int flag) { int ret; IsoNode *node; IsoFile *file; IsoStream *stream= NULL, *input_stream; IsoExternalFilterCommand *cmd; char type_text[80], *source_path= NULL; unsigned int fs_id; dev_t dev_id; ino_t ino_id; node= (IsoNode *) in_node; if(node == NULL) { ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0); if(ret <= 0) goto ex; } if(!LIBISO_ISREG(node)) {ret= 2; goto ex;} file= (IsoFile *) node; input_stream= iso_file_get_stream(file); Text_shellsafe(path, xorriso->result_line, 0); while(1) { stream= input_stream; input_stream= iso_stream_get_input_stream(stream, 0); if(input_stream == NULL) break; strcat(xorriso->result_line, " < "); Xorriso_stream_type(xorriso, node, stream, type_text, 0); strcat(xorriso->result_line, type_text); if(flag & 1) { iso_stream_get_id(stream, &fs_id, &dev_id, &ino_id); sprintf(xorriso->result_line + strlen(xorriso->result_line), "[%u,%lu,%lu]", fs_id, (unsigned long) dev_id, (unsigned long) ino_id); } ret= iso_stream_get_external_filter(stream, &cmd, 0); if(ret < 0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", ret, "Error when inquiring filter command of node", 0, "FAILURE", 1); ret= 0; goto ex; } if(ret > 0) { strcat(xorriso->result_line, ":"); Text_shellsafe(cmd->name, xorriso->result_line, 1); } if(strlen(xorriso->result_line) > SfileadrL) { Xorriso_result(xorriso, 0); xorriso->result_line[0]= 0; } } strcat(xorriso->result_line, " < "); Xorriso_stream_type(xorriso, node, stream, type_text, 0); strcat(xorriso->result_line, type_text); if(flag & 1) { iso_stream_get_id(stream, &fs_id, &dev_id, &ino_id); sprintf(xorriso->result_line + strlen(xorriso->result_line), "[%u,%lu,%lu]", fs_id, (unsigned long) dev_id, (unsigned long) ino_id); } source_path= iso_stream_get_source_path(stream, 0); if(source_path != NULL) { strcat(xorriso->result_line, ":"); Text_shellsafe(source_path, xorriso->result_line, 1); } strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); ret= 1; ex:; if(source_path != NULL) free(source_path); return(ret); } /* @param damage_start Returns first damaged byte address @param damage_end Returns first byte address after last damaged byte @return <0 error, 0=undamaged , 1=damaged */ int Xorriso_file_eval_damage(struct XorrisO *xorriso, IsoNode *node, off_t *damage_start, off_t *damage_end, int flag) { int lba_count= 0, sect, ret; off_t i, sectors, sector_size, *start_lbas= NULL, *end_lbas= NULL; off_t sect_base= 0, size= 0, byte, *section_sizes= NULL; struct SectorbitmaP *map; *damage_start= *damage_end= -1; map= xorriso->in_sector_map; if(map == NULL) return(0); Sectorbitmap_get_layout(map, §ors, §or_size, 0); sector_size/= 2048; ret= Xorriso__start_end_lbas(node, &lba_count, &start_lbas, &end_lbas, §ion_sizes, &size, 0); if(ret <= 0) { Xorriso_process_msg_queues(xorriso, 0); return(ret); } for(sect= 0; sect < lba_count; sect++) { for(i= start_lbas[sect]; i <= end_lbas[sect]; i+= sector_size) { if(Sectorbitmap_is_set(map, i / sector_size, 0) == 0) { byte= ((off_t) 2048) * ((off_t) (i - start_lbas[sect])) + sect_base; if(*damage_start < 0 || byte < *damage_start) *damage_start= byte; if(byte + (off_t) 2048 > *damage_end) *damage_end= byte + (off_t) 2048; } } sect_base+= ((off_t) 2048) * (end_lbas[sect] - start_lbas[sect] + 1); } if(*damage_end > size) *damage_end= size; if(start_lbas != NULL) free((char *) start_lbas); if(end_lbas != NULL) free((char *) end_lbas); if(section_sizes != NULL) free((char *) section_sizes); if(*damage_start < 0) return(0); return(1); } /* @param flag bit0= report_sections : section size rather than total size bit1= last_data_file_block : looking for highest data file block */ int Xorriso_report_lba(struct XorrisO *xorriso, char *show_path, IsoNode *node, uint32_t *last_block, int flag) { int ret, lba_count, i; off_t size, *section_sizes= NULL, *start_lbas= NULL, *end_lbas= NULL; ret= Xorriso__start_end_lbas(node, &lba_count, &start_lbas, &end_lbas, §ion_sizes, &size, 0); if(ret < 0) { Xorriso_process_msg_queues(xorriso, 0); {ret= -1; goto ex;} } if(ret == 0) {ret= 1; goto ex;} /* it is ok to ignore other types */ for(i= 0; i < lba_count; i++) { if(flag & 1) size= section_sizes[i]; if(flag & 2) { if(end_lbas[i] > 0 && end_lbas[i] > (off_t) *last_block) *last_block= end_lbas[i]; } else { sprintf(xorriso->result_line, "File data lba: %2d , %8.f , %8.f , %8.f , ", i, (double) start_lbas[i], (double) (end_lbas[i] + 1 - start_lbas[i]), (double) size); Text_shellsafe(show_path, xorriso->result_line, 1); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); } } ret= 1; ex:; if(start_lbas != NULL) free((char *) start_lbas); if(end_lbas != NULL) free((char *) end_lbas); if(section_sizes != NULL) free((char *) section_sizes); return(ret); } int Xorriso_report_damage(struct XorrisO *xorriso, char *show_path, IsoNode *node, int flag) { int ret; off_t size= 0, damage_start, damage_end; ret= Xorriso_file_eval_damage(xorriso, node, &damage_start, &damage_end, 0); if(ret < 0) return(0); if(LIBISO_ISREG(node)) size= iso_file_get_size((IsoFile *) node); if(ret > 0) { sprintf(xorriso->result_line, "File damaged : %8.f , %8.f , %8.f , ", (double) damage_start, (double) (damage_end - damage_start) , (double) size); } else { sprintf(xorriso->result_line, "File seems ok: %8.f , %8.f , %8.f , ", -1.0, -1.0, (double) size); } Text_shellsafe(show_path, xorriso->result_line, 1); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); return(1); } /* @param flag bit0= do not accept hln_targets[i] != NULL as *node_idx bit1= use *node_idx as found index rather than searching it bit2= with bit1: use xorriso->node_array rather than hln_array */ int Xorriso_search_hardlinks(struct XorrisO *xorriso, IsoNode *node, int *node_idx, int *min_hl, int *max_hl, int flag) { int idx, ret, i, node_count; void *np, **node_array; node_array= xorriso->hln_array; node_count= xorriso->hln_count; *min_hl= *max_hl= -1; np= node; if(flag & 2) { idx= *node_idx; if(flag & 4) { node_array= xorriso->node_array; node_count= xorriso->node_counter; } } else { *node_idx= -1; ret= Xorriso_search_in_hln_array(xorriso, np, &idx, 0); if(ret <= 0) return(ret); } for(i= idx - 1; i >= 0 ; i--) if(Xorriso__findi_sorted_ino_cmp(&(node_array[i]), &np) != 0) break; *min_hl= i + 1; for(i= idx + 1; i < node_count; i++) if(Xorriso__findi_sorted_ino_cmp(&(node_array[i]), &np) != 0) break; *max_hl= i - 1; /* Search for *node_idx */ if(flag & 2) return(1); for(i= *min_hl; i <= *max_hl; i++) if(node_array[i] == np) { if((flag & 1) && xorriso->hln_targets != NULL && !(flag & 4)) if(xorriso->hln_targets[i] != NULL) continue; *node_idx= i; break; } return(1); } /* @param flag bit0=do not complain about non existent node bit1= do not try to find truncated name first */ int Xorriso_node_from_path(struct XorrisO *xorriso, IsoImage *volume, char *path, IsoNode **node, int flag) { int ret; char *path_pt; path_pt= path; if(path[0]==0) path_pt= "/"; if(volume == NULL) { ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret <= 0) return(ret); } *node= NULL; ret= 2; if(!(flag & 2)) ret= iso_image_path_to_node(volume, path_pt, node); if(ret == 2) ret= iso_tree_path_to_node(volume, path_pt, node); Xorriso_process_msg_queues(xorriso,0); if(ret<=0 || (*node)==NULL) { if(!(flag&1)) { sprintf(xorriso->info_text, "Cannot find path "); Text_shellsafe(path_pt, xorriso->info_text, 1); strcat(xorriso->info_text, " in loaded ISO image"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } return(0); } return(1); } /* @param flag bit0=do not complain about non existent node bit1= do not try to find truncated name first */ int Xorriso_dir_from_path(struct XorrisO *xorriso, char *purpose, char *path, IsoDir **dir_node, int flag) { IsoImage *volume; IsoNode *node; int ret, is_dir= 0; ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) return(ret); ret= Xorriso_node_from_path(xorriso, volume, path, &node, flag & 3); if(ret<=0) goto wdi_is_not_a_dir; if(LIBISO_ISDIR(node)) is_dir= 1; if(!is_dir) { wdi_is_not_a_dir:; sprintf(xorriso->info_text, "%s path does not lead to a directory in ISO image", purpose); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } *dir_node= (IsoDir *) node; return(1); } /* @param flag bit0= do not remove leading slash bit1= append flatly to result_line and put out */ int Xorriso_getfname(struct XorrisO *xorriso, char *path, int flag) { int ret, path_offset= 0, bsl_mem; char *bsl_path= NULL; if(path[0] == '/' && !(flag & 1)) path_offset= 1; /* backslash escaped path rather than shellsafe path */ ret= Sfile_bsl_encoder(&bsl_path, path + path_offset, strlen(path + path_offset), 8); if(ret <= 0) return(-1); if(flag & 2) { sprintf(xorriso->result_line + strlen(xorriso->result_line), "%s\n", bsl_path[0] ? bsl_path : "."); } else { sprintf(xorriso->result_line, "# file: %s\n", bsl_path[0] ? bsl_path : "."); } free(bsl_path); bsl_path= NULL; /* temporarily disable -backslash_codes with result output */ bsl_mem= xorriso->bsl_interpretation; xorriso->bsl_interpretation= 0; Xorriso_result(xorriso, 0); xorriso->bsl_interpretation= bsl_mem; return(1); } int Xorriso_is_plain_image_file(struct XorrisO *xorriso, void *in_node, char *path, int flag) { int ret; off_t lba; IsoStream *stream; IsoNode *node; node= (IsoNode *) in_node; if(node == NULL) { ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0); if(ret<=0) return(ret); } ret= Xorriso__file_start_lba(node, &lba, 0); if(ret > 0) { /* Stream source is from loaded image */ stream= iso_file_get_stream((IsoFile *) node); if(stream != NULL) if(iso_stream_get_input_stream(stream, 0) == NULL) return(1); } return(0); } /* Check for existence of a data file at given path. Complain if it is missing. @param prefix is printed directly before purpose, no blank inbetween @param purpose is printed up to first '=' character @param path ISO or disk file path @param flag bit0= check on disk (else in ISO) */ int Xorriso_warn_if_not_exist(struct XorrisO *xorriso, char *prefix, char *purpose, char *path, int flag) { int ret; char *where, *ept; struct stat stbuf; if(flag & 1) { where= "on disk"; ret= Sfile_type(path, 1 | 4 | 16); if(ret == 1 || ret == 6) return(1); } else { where= "in ISO image"; ret= Xorriso_iso_lstat(xorriso, path, &stbuf, 0); if(ret == 0) { if(S_ISREG(stbuf.st_mode) || S_ISBLK(stbuf.st_mode)) return(0); } } /* Not found or not data file or block device */ sprintf(xorriso->info_text, "%s%s", prefix, purpose); ept= strchr(xorriso->info_text + strlen(prefix), '='); if(ept != NULL) *(ept + 1)= 0; if(ret == -1) { /* This is a normal situation with -as mkisofs emulation */ if(xorriso->current_interpreter == 1) return(2); sprintf(xorriso->info_text + strlen(xorriso->info_text), " : path does not yet exist %s : ", where); } else { sprintf(xorriso->info_text + strlen(xorriso->info_text), " : path exists %s but is not a usable file type : ", where); } Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); return(0); } /* Check for existence of a file at given path. Complain if it exists but is not a boot catalog file. @param prefix is printed directly before purpose, no blank inbetween @param purpose is printed up to first '=' character @param path ISO file path */ int Xorriso_warn_if_not_bootcat(struct XorrisO *xorriso, char *prefix, char *purpose, char *path, int flag) { int ret; char *where, *ept; IsoImage *volume; IsoNode *node; where= "in ISO image"; ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret <= 0) return(-1); ret= Xorriso_node_from_path(xorriso, volume, path, &node, 1); if(ret <= 0) return(1); if(LIBISO_ISBOOT(node)) return(2); sprintf(xorriso->info_text, "%s%s", prefix, purpose); ept= strchr(xorriso->info_text + strlen(prefix), '='); if(ept != NULL) *(ept + 1)= 0; sprintf(xorriso->info_text + strlen(xorriso->info_text), " : path exists %s but is currently not a boot catalog file : ", where); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); return(0); } /* @param in_node if not NULL and not flag bit1: omit path resolution @param flag bit1= path is disk_path bit5= in case of symbolic link on disk: inquire link target bit7= Ignore non-settable Linux-like file attribute flags @return >0 = ok , 0 = no lfa_flags at node or disk file, <0 = libisofs error */ int Xorriso_get_lfa_flags(struct XorrisO *xorriso, void *in_node, char *path, uint64_t *lfa_flags, int *max_bit, int flag) { int ret, os_errno; IsoNode *node; *lfa_flags= 0; if(flag & 2) goto from_disk; node= (IsoNode *) in_node; if(node == NULL) { ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0); if(ret <= 0) return(ret); } ret= iso_node_get_lfa_flags(node, lfa_flags, max_bit, 0); if(ret < 0) { Xorriso_process_msg_queues(xorriso, 0); Xorriso_report_iso_error(xorriso, path, ret, "Error when obtaining lsattr flags of ISO node", 0, "WARNING", 1); return(-1); } if(ret == 0) return(0); return(1); from_disk:; ret= iso_local_get_lfa_flags(path, lfa_flags, max_bit, &os_errno, flag & ((1 << 5) | (1 << 7))); if(ret < 0) { Xorriso_process_msg_queues(xorriso, 0); Xorriso_report_iso_error(xorriso, path, ret, "Error when obtaining lsattr flags of disk file", os_errno, "WARNING", 1); return(-1); } if(ret == 1 || ret == 2) return(1); return(0); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of functions which access nodes of the libisofs tree model. */ #ifndef Xorriso_pvt_iso_tree_includeD #define Xorriso_pvt_iso_tree_includeD yes #define LIBISO_ISDIR(node) (iso_node_get_type(node) == LIBISO_DIR) #define LIBISO_ISREG(node) (iso_node_get_type(node) == LIBISO_FILE) #define LIBISO_ISLNK(node) (iso_node_get_type(node) == LIBISO_SYMLINK) #define LIBISO_ISCHR(node) (iso_node_get_type(node) == LIBISO_SPECIAL && \ S_ISCHR(iso_node_get_mode(node))) #define LIBISO_ISBLK(node) (iso_node_get_type(node) == LIBISO_SPECIAL && \ S_ISBLK(iso_node_get_mode(node))) #define LIBISO_ISFIFO(node) (iso_node_get_type(node) == LIBISO_SPECIAL && \ S_ISFIFO(iso_node_get_mode(node))) #define LIBISO_ISSOCK(node) (iso_node_get_type(node) == LIBISO_SPECIAL && \ S_ISSOCK(iso_node_get_mode(node))) #define LIBISO_ISBOOT(node) (iso_node_get_type(node) == LIBISO_BOOT) int Xorriso_node_from_path(struct XorrisO *xorriso, IsoImage *volume, char *path, IsoNode **node, int flag); int Xorriso_get_node_by_path(struct XorrisO *xorriso, char *in_path, char *eff_path, IsoNode **node, int flag); int Xorriso_dir_from_path(struct XorrisO *xorriso, char *purpose, char *path, IsoDir **dir_node, int flag); int Xorriso_node_get_dev(struct XorrisO *xorriso, IsoNode *node, char *path, dev_t *dev, int flag); int Xorriso_fake_stbuf(struct XorrisO *xorriso, char *path, struct stat *stbuf, IsoNode **node, int flag); int Xorriso_node_is_valid(struct XorrisO *xorriso, IsoNode *in_node, int flag); int Xorriso_path_from_node(struct XorrisO *xorriso, IsoNode *in_node, char path[SfileadrL], int flag); int Xorriso_path_from_lba(struct XorrisO *xorriso, IsoNode *node, off_t lba, char path[SfileadrL], int flag); int Xorriso_get_attr_value(struct XorrisO *xorriso, void *in_node, char *path, char *name, size_t *value_length, char **value, int flag); int Xorriso_stream_type(struct XorrisO *xorriso, IsoNode *node, IsoStream *stream, char type_text[], int flag); int Xorriso_show_du_subs(struct XorrisO *xorriso, IsoDir *dir_node, char *abs_path, char *rel_path, off_t *size, off_t boss_mem, int flag); int Xorriso_sorted_dir_i(struct XorrisO *xorriso, IsoDir *dir_node, int *filec, char ***filev, off_t boss_mem, int flag); int Xorriso_obtain_pattern_files_i( struct XorrisO *xorriso, char *wd, IsoDir *dir, int *filec, char **filev, int count_limit, off_t *mem, int *dive_count, int flag); int Xorriso__get_file_size(IsoNode *node, off_t *size, int flag); int Xorriso__start_end_lbas(IsoNode *node, int *lba_count, off_t **start_lbas, off_t **end_lbas, off_t **section_sizes, off_t *size, int flag); int Xorriso__file_start_lba(IsoNode *node, off_t *lba, int flag); int Xorriso_file_eval_damage(struct XorrisO *xorriso, IsoNode *node, off_t *damage_start, off_t *damage_end, int flag); int Xorriso_report_lba(struct XorrisO *xorriso, char *show_path, IsoNode *node, uint32_t *last_block, int flag); int Xorriso_report_damage(struct XorrisO *xorriso, char *show_path, IsoNode *node, int flag); int Xorriso_getfname(struct XorrisO *xorriso, char *path, int flag); int Xorriso_retrieve_disk_path(struct XorrisO *xorriso, IsoNode *node, char disk_path[SfileadrL], int flag); #endif /* ! Xorriso_pvt_iso_tree_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains functions which manage the relation between xorriso and the libraries: libburn, libisofs, libisoburn. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include <pthread.h> #ifdef HAVE_STDINT_H #include <stdint.h> #else #ifdef HAVE_INTTYPES_H #include <inttypes.h> #endif #endif /* for -charset */ #include <iconv.h> #include <langinfo.h> #ifdef Xorriso_standalonE #ifdef Xorriso_with_libjtE #include "../libjte/libjte.h" #endif #else #ifdef Xorriso_with_libjtE #include <libjte/libjte.h> #endif #endif /* ! Xorriso_standalonE */ #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" #include "lib_mgt.h" #include "iso_manip.h" #include "write_run.h" int Xorriso_abort(struct XorrisO *xorriso, int flag) { int ret; ret= burn_abort(4440, burn_abort_pacifier, "xorriso : "); if(ret<=0) { fprintf(stderr, "\nxorriso : ABORT : Cannot cancel burn session and release drive.\n"); return(0); } fprintf(stderr, "xorriso : ABORT : Drive is released and library is shut down now.\n"); fprintf(stderr, "xorriso : ABORT : Program done. Even if you do not see a shell prompt.\n"); fprintf(stderr, "\n"); exit(1); } /* @param flag bit0= asynchronous handling (else catch thread, wait, and exit) bit1= dealing with MMC drive in critical state behavior 2 -> behavior 1 */ int Xorriso_set_signal_handling(struct XorrisO *xorriso, int flag) { char *handler_prefix= NULL; int behavior, mode; behavior= Xorriso__get_signal_behavior(0); if(behavior == 0) return(2); if(behavior == 2 && !(flag & 2)) mode= 1; else if(behavior == 3) mode= 2; else mode= (flag & 1) * 0x30; handler_prefix= calloc(strlen(xorriso->progname)+3+1, 1); if(handler_prefix==NULL) { sprintf(xorriso->info_text, "Cannot allocate memory for setting signal handler"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); return(-1); } mode|= 256; /* Ignore SIGPIPE */ /* <<< */ sprintf(xorriso->info_text, "burn_set_signal_handling(%d)", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); sprintf(handler_prefix, "%s : ", xorriso->progname); burn_set_signal_handling(handler_prefix, NULL, mode); free(handler_prefix); return(1); } int Xorriso_startup_libraries(struct XorrisO *xorriso, int flag) { int ret, major, minor, micro; char *queue_sev, *print_sev, reason[1024]; struct iso_zisofs_ctrl zisofs_ctrl; /* First an ugly compile time check for header version compatibility. If everything matches, then no C code is produced. In case of mismatch, intentionally faulty C code will be inserted. */ /* The minimum requirement of xorriso towards the libisoburn header at compile time is defined in xorriso/xorrisoburn.h xorriso_libisoburn_req_major xorriso_libisoburn_req_minor xorriso_libisoburn_req_micro It gets compared against the version macros in libburn/libburn.h : isoburn_header_version_major isoburn_header_version_minor isoburn_header_version_micro If the header is too old then the following code shall cause failure of cdrskin compilation rather than to allow production of a program with unpredictable bugs or memory corruption. The compiler messages supposed to appear in this case are: error: 'LIBISOBURN_MISCONFIGURATION' undeclared (first use in this function) error: 'INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libisoburn_dot_h_TOO_OLD__SEE_xorrisoburn_dot_c' undeclared (first use in this function) error: 'LIBISOBURN_MISCONFIGURATION_' undeclared (first use in this function) */ /* The indentation is an advise of man gcc to help old compilers ignoring */ #if xorriso_libisoburn_req_major > isoburn_header_version_major #define Isoburn_libisoburn_dot_h_too_olD 1 #endif #if xorriso_libisoburn_req_major == isoburn_header_version_major && xorriso_libisoburn_req_minor > isoburn_header_version_minor #define Isoburn_libisoburn_dot_h_too_olD 1 #endif #if xorriso_libisoburn_req_minor == isoburn_header_version_minor && xorriso_libisoburn_req_micro > isoburn_header_version_micro #define Isoburn_libisoburn_dot_h_too_olD 1 #endif #ifdef Isoburn_libisoburn_dot_h_too_olD LIBISOBURN_MISCONFIGURATION = 0; INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libisoburn_dot_h_TOO_OLD__SEE_xorrisoburn_dot_c = 0; LIBISOBURN_MISCONFIGURATION_ = 0; #endif /* End of ugly compile time test (scroll up for explanation) */ reason[0]= 0; ret= isoburn_initialize(reason, 0); if(ret==0) { sprintf(xorriso->info_text, "Cannot initialize libraries"); if(reason[0]) sprintf(xorriso->info_text+strlen(xorriso->info_text), ". Reason given:\n%s", reason); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); return(0); } ret= isoburn_is_compatible(isoburn_header_version_major, isoburn_header_version_minor, isoburn_header_version_micro, 0); if(ret<=0) { isoburn_version(&major, &minor, µ); sprintf(xorriso->info_text, "libisoburn version too old: %d.%d.%d . Need at least: %d.%d.%d .\n", major, minor, micro, isoburn_header_version_major, isoburn_header_version_minor, isoburn_header_version_micro); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); return(-1); } xorriso->libs_are_started= 1; queue_sev= "ALL"; if(xorriso->library_msg_direct_print) { /* >>> need option for controlling this in XorrisO. See also Xorriso_msgs_submit */; print_sev= xorriso->report_about_text; } else print_sev= "NEVER"; iso_set_msgs_severities(queue_sev, print_sev, "libsofs : "); burn_msgs_set_severities(queue_sev, print_sev, "libburn : "); /* ??? >>> do we want united queues ? */ /* burn_set_messenger(iso_get_messenger()); */ isoburn_set_msgs_submit(Xorriso_msgs_submit_void, (void *) xorriso, (3<<2) | 128 , 0); ret= Xorriso_set_signal_handling(xorriso, 0); if(ret <= 0) return(ret); memset(&zisofs_ctrl, 0, sizeof(zisofs_ctrl)); zisofs_ctrl.version = 1; ret = iso_zisofs_get_params(&zisofs_ctrl, 0); if (ret == 1) { xorriso->zisofs_block_size= xorriso->zisofs_block_size_default= (1 << zisofs_ctrl.block_size_log2); xorriso->zlib_level= xorriso->zlib_level_default= zisofs_ctrl.compression_level; xorriso->zisofs_v2_enabled= zisofs_ctrl.v2_enabled; xorriso->zisofs_max_total_blocks= xorriso->zisofs_max_total_blocks_default= zisofs_ctrl.max_total_blocks; xorriso->zisofs_max_file_blocks= xorriso->zisofs_max_file_blocks_default= zisofs_ctrl.max_file_blocks; xorriso->zisofs_v2_block_size= xorriso->zisofs_v2_block_size_default= 1 << zisofs_ctrl.v2_block_size_log2; xorriso->zisofs_block_number_target= zisofs_ctrl.block_number_target; xorriso->zisofs_bpt_discard_free_ratio= xorriso->zisofs_bpt_discard_free_ratio_default= zisofs_ctrl.bpt_discard_free_ratio; } xorriso->zisofs_susp_z2= xorriso->zisofs_susp_z2_default= iso_zisofs_ctrl_susp_z2(-1); iso_node_xinfo_make_clonable(Xorriso__mark_update_xinfo, Xorriso__mark_update_cloner, 0); /* Second initialization. This time with libs. */ Xorriso_preparer_string(xorriso, xorriso->preparer_id, 0); /* Inquire lfa capabilities of libisofs */ xorriso->lfa_flags_setting&= ~8; xorriso->lfa_flags_default&= ~8; ret= iso_local_attr_support(4); if(ret & 4) { xorriso->lfa_flags_setting|= 8; xorriso->lfa_flags_default|= 8; } Xorriso_process_msg_queues(xorriso,0); if(reason[0]) { sprintf(xorriso->info_text, "%s", reason); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } strcpy(xorriso->info_text, "Using "); strncat(xorriso->info_text, burn_scsi_transport_id(0), 1024); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); return(1); } /* @param flag bit0= global shutdown of libraries */ int Xorriso_detach_libraries(struct XorrisO *xorriso, int flag) { Xorriso_give_up_drive(xorriso, 3); if(xorriso->in_volset_handle!=NULL) { /* standalone image */ iso_image_unref((IsoImage *) xorriso->in_volset_handle); xorriso->in_volset_handle= NULL; Sectorbitmap_destroy(&(xorriso->in_sector_map), 0); Xorriso_destroy_di_array(xorriso, 0); Xorriso_destroy_hln_array(xorriso, 0); xorriso->boot_count= 0; } if(flag&1) { if(xorriso->libs_are_started==0) return(0); isoburn_finish(); #ifdef Xorriso_with_editlinE Xorriso__shutdown_editline(0); #endif } return(1); } /* @param flag bit0= suppress messages below UPDATE bit1= suppress messages below FAILURE */ int Xorriso_set_image_severities(struct XorrisO *xorriso, int flag) { char *queue_sev, *print_sev; if(flag&2) queue_sev= "FAILURE"; else if(flag&1) queue_sev= "UPDATE"; else queue_sev= "ALL"; if(xorriso->library_msg_direct_print) print_sev= xorriso->report_about_text; else print_sev= "NEVER"; iso_set_msgs_severities(queue_sev, print_sev, "libisofs : "); return(1); } /* @param flag bit0=prepare for a burn run */ int Xorriso_set_abort_severity(struct XorrisO *xorriso, int flag) { int ret, abort_on_number; char *sev_text; static int note_number= -1, failure_number= -1; if(note_number==-1) Xorriso__text_to_sev("NOTE", ¬e_number, 0); if(failure_number==-1) Xorriso__text_to_sev("FAILURE", &failure_number, 0); sev_text= xorriso->abort_on_text; ret= Xorriso__text_to_sev(xorriso->abort_on_text, &abort_on_number, 0); if(ret<=0) return(ret); if(abort_on_number<note_number) sev_text= "NOTE"; else if(abort_on_number>failure_number) sev_text= "FAILURE"; ret= iso_set_abort_severity(sev_text); return(ret>=0); } int Xorriso_report_lib_versions(struct XorrisO *xorriso, int flag) { int major, minor, micro; int req_major, req_minor, req_micro; iso_lib_version(&major, &minor, µ); isoburn_libisofs_req(&req_major, &req_minor, &req_micro); sprintf(xorriso->result_line, "libisofs in use : %d.%d.%d (min. %d.%d.%d)\n", major, minor, micro, req_major, req_minor, req_micro); Xorriso_result(xorriso, 0); #ifdef Xorriso_with_libjtE libjte__version(&major, &minor, µ); isoburn_libjte_req(&req_major, &req_minor, &req_micro); sprintf(xorriso->result_line, "libjte in use : %d.%d.%d (min. %d.%d.%d)\n", major, minor, micro, req_major, req_minor, req_micro); Xorriso_result(xorriso, 0); #endif burn_version(&major, &minor, µ); isoburn_libburn_req(&req_major, &req_minor, &req_micro); sprintf(xorriso->result_line, "libburn in use : %d.%d.%d (min. %d.%d.%d)\n", major, minor, micro, req_major, req_minor, req_micro); Xorriso_result(xorriso, 0); strcpy(xorriso->result_line, "libburn OS adapter: "); strncat(xorriso->result_line, burn_scsi_transport_id(0), 1024); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); isoburn_version(&major, &minor, µ); sprintf(xorriso->result_line, "libisoburn in use : %d.%d.%d (min. %d.%d.%d)\n", major, minor, micro, isoburn_header_version_major, isoburn_header_version_minor, isoburn_header_version_micro); Xorriso_result(xorriso, 0); return(1); } int Xorriso__sev_to_text(int severity, char **severity_name, int flag) { int ret; ret= iso_sev_to_text(severity, severity_name); if(ret>0) return(ret); ret= burn_sev_to_text(severity, severity_name, 0); if(ret>0) return(ret); *severity_name= ""; return(0); } int Xorriso__text_to_sev(char *severity_name, int *severity_number, int flag) { int ret= 1; char severity[20]; Xorriso__to_upper(severity_name, severity, (int) sizeof(severity), 0); ret= iso_text_to_sev(severity, severity_number); if(ret>0) return(ret); ret= burn_text_to_sev(severity, severity_number, 0); return(ret); } int Xorriso__severity_cmp(char *sev1, char *sev2) { int s1= 0x7fffffff, s2= 0x7fffffff, ret; char *default_sev= "FATAL"; ret= Xorriso__text_to_sev(sev1, &s1, 0); if(ret <= 0) Xorriso__text_to_sev(default_sev, &s1, 0); ret= Xorriso__text_to_sev(sev2, &s2, 0); if(ret <= 0) Xorriso__text_to_sev(default_sev, &s2, 0); if(s1 < s2) return -1; if(s1 > s2) return(1); return(0); } char *Xorriso__severity_list(int flag) { return(burn_list_sev_texts(0)); } /* @param flag bit0= report libisofs error text bit1= victim is disk_path bit2= do not inquire libisofs, report msg_text and min_severity */ int Xorriso_report_iso_error(struct XorrisO *xorriso, char *victim, int iso_error_code, char msg_text[], int os_errno, char min_severity[], int flag) { int error_code, iso_sev, min_sev, ret, origin= 1; char *sev_text_pt, *msg_text_pt= NULL; char *sfe= NULL; static int sorry_sev= -1; Xorriso_alloc_meM(sfe, char, 6 * SfileadrL); Xorriso_process_msg_queues(xorriso, 0); if(sorry_sev<0) Xorriso__text_to_sev("SORRY", &sorry_sev, 0); if(flag&4) { origin= 0; error_code= 0x00050000; Xorriso__text_to_sev(min_severity, &iso_sev, 0); } else { error_code= iso_error_get_code(iso_error_code); if(error_code < 0x00030000 || error_code >= 0x00040000) error_code= (error_code & 0xffff) | 0x00050000; if(flag&1) msg_text_pt= (char *) iso_error_to_msg(iso_error_code); iso_sev= iso_error_get_severity(iso_error_code); } if(msg_text_pt==NULL) msg_text_pt= msg_text; if(iso_sev >= sorry_sev && (flag & 2) && victim[0]) Xorriso_msgs_submit(xorriso, 0, victim, 0, "ERRFILE", 0); sev_text_pt= min_severity; Xorriso__text_to_sev(min_severity, &min_sev, 0); if(min_sev < iso_sev && !(flag&4)) Xorriso__sev_to_text(iso_sev, &sev_text_pt, 0); strcpy(sfe, msg_text_pt); if(victim[0]) { strcat(sfe, ": "); Text_shellsafe(victim, sfe+strlen(sfe), 0); } ret= Xorriso_msgs_submit(xorriso, error_code, sfe, os_errno, sev_text_pt, origin << 2); ex:; Xorriso_free_meM(sfe); return(ret); } int Xorriso_get_local_charset(struct XorrisO *xorriso, char **name, int flag) { (*name)= iso_get_local_charset(0); return(1); } int Xorriso_set_local_charset(struct XorrisO *xorriso, char *name, int flag) { int ret; char *nl_charset; iconv_t iconv_ret= (iconv_t) -1; nl_charset= nl_langinfo(CODESET); if(name == NULL) name= nl_charset; if(name == NULL) goto cannot; iconv_ret= iconv_open(nl_charset, name); if(iconv_ret == (iconv_t) -1) goto cannot; else iconv_close(iconv_ret); ret= iso_set_local_charset(name, 0); if(ret <= 0) { cannot:; sprintf(xorriso->info_text, "-local_charset: Cannot assume as local character set: "); if(name != NULL) Text_shellsafe(name, xorriso->info_text, 1); else Text_shellsafe("(NULL-pointer)", xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(0); } sprintf(xorriso->info_text, "Local character set is now assumed as: "); Text_shellsafe(name, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(1); } int Xorriso_process_msg_queues(struct XorrisO *xorriso, int flag) { int ret, error_code= 0, os_errno= 0, count= 0, pass, imgid, tunneled; int name_prefix_code; char severity[80], *text= NULL; #ifdef Xorriso_fetch_with_msg_queueS int locked= 0, uret; #endif #ifdef Xorriso_with_libjtE char *msg; #endif if(!xorriso->libs_are_started) { ret= 1; goto ex; } #ifdef Xorriso_fetch_with_msg_queueS Xorriso_alloc_meM(text, char, sizeof(xorriso->info_text)); ret= pthread_mutex_lock(&(xorriso->lib_msg_queue_lock)); if(ret != 0) { Xorriso_msgs_submit(xorriso, 0, "Cannot acquire mutex lock for processing library message queues", ret, "FATAL", 0); } else locked= 1; #else /* Xorriso_fetch_with_msg_queueS */ text= xorriso->info_text; #endif /* ! Xorriso_fetch_with_msg_queueS */ for(pass= 0; pass< 3; pass++) { while(1) { tunneled= 0; if(pass==0) { #ifdef Xorriso_with_libjtE ret= 0; if(xorriso->libjte_handle != NULL) { msg= libjte_get_next_message(xorriso->libjte_handle); if(msg != NULL) { sprintf(text, "%1.4095s", msg); free(msg); strcpy(severity, "NOTE"); error_code= 0; os_errno= 0; ret= 1; } } #else break; #endif /* ! Xorriso_with_libjtE */ } else if(pass==1) ret= iso_obtain_msgs("ALL", &error_code, &imgid, text, severity); else { ret= burn_msgs_obtain("ALL", &error_code, text, &os_errno, severity); if((error_code>=0x00030000 && error_code<0x00040000) || (error_code>=0x00050000 && error_code<0x00060000)) tunneled= -1; /* "libisofs:" */ else if(error_code>=0x00060000 && error_code<0x00070000) tunneled= 1; /* "libisoburn:" */ } if(ret<=0) break; /* <<< tunneled MISHAP from libisoburn through libburn or well known error codes of MISHAP events With libburn-0.4.4 this is not necessary */ if(error_code==0x5ff73 || error_code==0x3ff73 || error_code==0x3feb9 || error_code==0x3feb2) strcpy(severity, "MISHAP"); else if(error_code==0x51001) strcpy(severity, "ERRFILE"); #ifdef Xorriso_with_libjtE if(pass == 0) name_prefix_code= 0; else name_prefix_code= pass + tunneled; #else name_prefix_code= pass + tunneled; #endif /* Xorriso_with_libjtE */ Xorriso_msgs_submit(xorriso, error_code, text, os_errno, severity, name_prefix_code << 2); count++; } } if(xorriso->library_msg_direct_print && count>0) { sprintf(text," (%d library messages repeated by xorriso)\n", count); #ifdef Xorriso_fetch_with_msg_queueS Xorriso_msgs_submit(xorriso, 0, text, 0, "NOTE", 256); #else /* Xorriso_fetch_with_msg_queueS */ Xorriso_info(xorriso, 0); #endif /* Xorriso_fetch_with_msg_queueS */ } ret= 1; ex:; #ifdef Xorriso_fetch_with_msg_queueS if(locked) { uret= pthread_mutex_unlock(&(xorriso->lib_msg_queue_lock)); if(uret != 0) { Xorriso_msgs_submit(xorriso, 0, "Cannot release mutex lock for processing library message queues", uret, "FATAL", 0); ret= -1; } } Xorriso_free_meM(text); #endif /* Xorriso_fetch_with_msg_queueS */ return(ret); } int Xorriso_md5_start(struct XorrisO *xorriso, void **ctx, int flag) { int ret; ret= iso_md5_start(ctx); if(ret == 1) return(1); Xorriso_no_malloc_memory(xorriso, NULL, 0); return(-1); } int Xorriso_md5_compute(struct XorrisO *xorriso, void *ctx, char *data, int datalen, int flag) { iso_md5_compute(ctx, data, datalen); return(1); } int Xorriso_md5_end(struct XorrisO *xorriso, void **ctx, char md5[16], int flag) { int ret; ret= iso_md5_end(ctx, md5); Xorriso_process_msg_queues(xorriso,0); if(ret <= 0) return(0); return(1); } /* @param flag bit0= avoid library calls */ int Xorriso_preparer_string(struct XorrisO *xorriso, char xorriso_id[129], int flag) { int major, minor, micro; xorriso_id[0]= 0; sprintf(xorriso_id, "XORRISO-%d.%d.%d ", Xorriso_header_version_majoR, Xorriso_header_version_minoR, Xorriso_header_version_micrO); if(strlen(xorriso_id) + strlen(Xorriso_timestamP) < 128) strcat(xorriso_id, Xorriso_timestamP); if(flag & 1) return(1); isoburn_version(&major, &minor, µ); if(strlen(xorriso_id) < 100) sprintf(xorriso_id + strlen(xorriso_id), ", LIBISOBURN-%d.%d.%d", major, minor, micro); iso_lib_version(&major, &minor, µ); if(strlen(xorriso_id) < 100) sprintf(xorriso_id + strlen(xorriso_id), ", LIBISOFS-%d.%d.%d", major, minor, micro); burn_version(&major, &minor, µ); if(strlen(xorriso_id) < 100) sprintf(xorriso_id + strlen(xorriso_id), ", LIBBURN-%d.%d.%d", major, minor, micro); return(1); } #ifdef Xorriso_with_libjtE int Xorriso_assert_jte_handle(struct XorrisO *xorriso, int flag) { int ret; if(xorriso->libjte_handle == NULL) { ret= libjte_new(&(xorriso->libjte_handle), 0); if(ret <= 0 || xorriso->libjte_handle == NULL) { sprintf(xorriso->info_text, "-jigdo: Failed to create libjte environment object"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); return(-1); } /* no stderr, no exit() */ libjte_set_error_behavior(xorriso->libjte_handle, 0, 0); } return(1); } #endif /* Xorriso_with_libjtE */ int Xorriso_jigdo_interpreter(struct XorrisO *xorriso, char *aspect, char *arg, int flag) { #ifdef Xorriso_with_libjtE int ret, num; struct libjte_env *jte; char *msg = NULL; if(strcmp(aspect, "clear") == 0) { if(xorriso->libjte_handle != NULL) libjte_destroy(&(xorriso->libjte_handle)); Xorriso_lst_destroy_all(&(xorriso->jigdo_params), 0); Xorriso_lst_destroy_all(&(xorriso->jigdo_values), 0); xorriso->libjte_params_given= 0; return(1); } ret= Xorriso_assert_jte_handle(xorriso, 0); if(ret <= 0) return(ret); jte= xorriso->libjte_handle; if(strcmp(aspect, "verbose") == 0) { if(strcmp(arg, "on") == 0) { libjte_set_verbose(jte, 1); /* Direct libjte messages to stderr, rather than message list */ libjte_set_error_behavior(xorriso->libjte_handle, 1, 0); xorriso->libjte_params_given|= 2; } else if(strcmp(arg, "off") == 0) { libjte_set_verbose(jte, 0); libjte_set_error_behavior(xorriso->libjte_handle, 0, 0); xorriso->libjte_params_given&= ~2; } else goto bad_arg; } else if(strcmp(aspect, "template_path") == 0 || strcmp(aspect, "-jigdo-template") == 0) { ret= libjte_set_template_path(jte, arg); if(ret <= 0) goto jte_failed; xorriso->libjte_params_given|= 4; } else if(strcmp(aspect, "jigdo_path") == 0 || strcmp(aspect, "-jigdo-jigdo") == 0) { ret= libjte_set_jigdo_path(jte, arg); if(ret <= 0) goto jte_failed; xorriso->libjte_params_given|= 8; } else if(strcmp(aspect, "md5_path") == 0 || strcmp(aspect, "-md5-list") == 0 || strcmp(aspect, "checksum_path") == 0 || strcmp(aspect, "-checksum-list") == 0) { ret= libjte_set_checksum_path(jte, arg); if(ret <= 0) goto jte_failed; xorriso->libjte_params_given|= 16; } else if(strcmp(aspect, "min_size") == 0 || strcmp(aspect, "-jigdo-min-file-size") == 0) { num= Scanf_io_size(arg, 0); ret= libjte_set_min_size(jte, num); if(ret <= 0) goto jte_failed; xorriso->libjte_params_given|= 32; } else if(strcmp(aspect, "checksum_iso") == 0 || strcmp(aspect, "-checksum_algorithm_iso") == 0) { ret= libjte_set_checksum_iso(jte, arg); if(ret <= 0) goto jte_failed; xorriso->libjte_params_given|= 64; } else if(strcmp(aspect, "checksum_template") == 0 || strcmp(aspect, "-checksum_algorithm_template") == 0) { ret= libjte_set_checksum_template(jte, arg); if(ret <= 0) goto jte_failed; xorriso->libjte_params_given|= 128; } else if(strcmp(aspect, "compression") == 0 || strcmp(aspect, "-jigdo-template-compress") == 0) { ret= libjte_set_compression(jte, arg); if(ret <= 0) goto jte_failed; xorriso->libjte_params_given|= 256; } else if(strcmp(aspect, "exclude") == 0 || strcmp(aspect, "-jigdo-exclude") == 0) { ret= libjte_add_exclude(jte, arg); if(ret <= 0) goto jte_failed; xorriso->libjte_params_given|= 512; } else if(strcmp(aspect, "demand_md5") == 0 || strcmp(aspect, "-jigdo-force-md5") == 0 || strcmp(aspect, "demand_checksum") == 0 || strcmp(aspect, "-jigdo-force-checksum") == 0) { ret= libjte_add_checksum_demand(jte, arg); if(ret <= 0) goto jte_failed; xorriso->libjte_params_given|= 1024; } else if(strcmp(aspect, "mapping") == 0 || strcmp(aspect, "-jigdo-map") == 0) { ret= libjte_add_mapping(jte, arg); if(ret <= 0) goto jte_failed; xorriso->libjte_params_given|= 2048; } else if(strcmp(aspect, "checksum_algorithm") == 0 || strcmp(aspect, "-jigdo-checksum-algorithm") == 0) { int ck_size; ret= libjte_set_checksum_algorithm(jte, arg, &ck_size); if(ret <= 0) goto jte_failed; xorriso->libjte_params_given|= 4096; } else { sprintf(xorriso->info_text, "-jigdo: unknown aspect '%s'", aspect); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= Xorriso_lst_new(&(xorriso->jigdo_params), aspect, xorriso->jigdo_params, 1); if(ret > 0) ret= Xorriso_lst_new(&(xorriso->jigdo_values), arg, xorriso->jigdo_values, 1); if(ret <= 0) { Xorriso_no_malloc_memory(xorriso, NULL, 0); return(-1); } Xorriso_process_msg_queues(xorriso, 0); return(1); bad_arg: sprintf(xorriso->info_text, "-jigdo %s : unknown argument '%s'", aspect, arg); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); jte_failed: while(1) { msg= libjte_get_next_message(xorriso->libjte_handle); if(msg == NULL) break; sprintf(xorriso->info_text, "%1.4095s", msg); free(msg); msg= NULL; Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } sprintf(xorriso->info_text, "Experienced libjte failure with: -jigdo %s %s", aspect, arg); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); #else /* Xorriso_with_libjtE */ sprintf(xorriso->info_text, "Jigdo Template Extraction was not enabled at compile time"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); #endif /* ! Xorriso_with_libjtE */ } int Xorriso_list_extras_result(struct XorrisO *xorriso, char *mode, char *what, int flag) { if(mode[0] != 0 && strcmp(mode, "all") != 0) { if(strcmp(mode, what) != 0 && (mode[0] != '-' || strcmp(mode + 1, what) != 0)) return(2); } Xorriso_result(xorriso, 0); return(1); } int Xorriso_list_extras(struct XorrisO *xorriso, char *mode, int flag) { int ret; if(strcmp(mode, "codes") == 0) { sprintf(xorriso->result_line, "List of xorriso extra feature codes. Usable with or without dash.\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "Local ACL : -acl\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "Local xattr : -xattr\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "Local chattr : -lfa_flags\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "Jigdo files : -jigdo\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "zisofs : -zisofs\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "Ext. filters : -external_filter\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "DVD obs 64 kB: -dvd_obs\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "Readline : -use_readline\n"); Xorriso_result(xorriso, 0); return(1); } sprintf(xorriso->result_line, "List of xorriso extra features. yes = enabled , no = disabled\n"); Xorriso_list_extras_result(xorriso, mode, "list_extras", 0); ret= iso_local_attr_support(7); sprintf(xorriso->result_line, "Local ACL : %s\n", ret & 1 ? "yes" : "no"); Xorriso_list_extras_result(xorriso, mode, "acl", 0); sprintf(xorriso->result_line, "Local xattr : %s\n", ret & 2 ? "yes" : "no"); Xorriso_list_extras_result(xorriso, mode, "xattr", 0); sprintf(xorriso->result_line, "Local chattr : %s\n", ret & 4 ? "yes" : "no"); Xorriso_list_extras_result(xorriso, mode, "lfa_flags", 0); sprintf(xorriso->result_line, "Jigdo files : %s\n", #ifdef Xorriso_with_libjtE "yes"); #else "no"); #endif Xorriso_list_extras_result(xorriso, mode, "jigdo", 0); ret= iso_file_add_zisofs_filter(NULL, 4); sprintf(xorriso->result_line, "zisofs : %s\n", ret == 2 ? "yes" : "no"); Xorriso_list_extras_result(xorriso, mode, "zisofs", 0); sprintf(xorriso->result_line, "Ext. filters : %s\n", #ifdef Xorriso_allow_external_filterS #ifdef Xorriso_allow_extf_suiD "yes , setuid allowed"); #else "yes , setuid banned"); #endif #else "no"); #endif Xorriso_list_extras_result(xorriso, mode, "external_filter", 0); sprintf(xorriso->result_line, "DVD obs 64 kB: %s\n", #ifdef Xorriso_dvd_obs_default_64K "yes"); #else "no"); #endif Xorriso_list_extras_result(xorriso, mode, "dvd_obs", 0); sprintf(xorriso->result_line, "Readline : %s\n", #ifdef Xorriso_with_editlinE "yes , libedit"); #else #ifdef Xorriso_with_readlinE "yes"); #else "no"); #endif #endif Xorriso_list_extras_result(xorriso, mode, "use_readline", 0); return(1); } /* @param flag bit0= set num_tiles to default value bit1= set tile_blocks to default value */ int Xorriso_set_data_cache(struct XorrisO *xorriso, void *o, int num_tiles, int tile_blocks, int flag) { int ret, tiles, blocks, set_flag; struct isoburn_read_opts *ropts; ropts= (struct isoburn_read_opts *) o; if(flag & (1 | 2)) { isoburn_ropt_get_data_cache(ropts, &tiles, &blocks, &set_flag, 1); if(flag & 1) num_tiles= tiles; if(flag & 2) tile_blocks= blocks; } ret= isoburn_ropt_set_data_cache(ropts, num_tiles, tile_blocks, 0); return(ret); } /* @param flag bit0= issue hex string rather than structured text format */ int Xorriso_format_guid(struct XorrisO *xorriso, uint8_t guid[16], char *line, int flag) { Xorriso__format_guid(guid, line, !(flag & 1)); return(1); } /* @param flag bit0= issue hex string rather than structured text format */ int Xorriso_make_guid(struct XorrisO *xorriso, char *line, int flag) { uint8_t guid[16]; int ret; iso_generate_gpt_guid(guid); ret= Xorriso_format_guid(xorriso, guid, line, flag & 1); return(ret); } int Xorriso_set_libisofs_now(struct XorrisO *xorriso, int flag) { int ret; time_t now; if(xorriso->do_override_now_time) { now= xorriso->now_time_override; ret= iso_nowtime(&now, 1); } else { ret= iso_nowtime(&now, 0); } return(ret); } /* Frontend to isoburn_conv_name_chars() @param flag bit0= File name. Do not omit version number if it would appear. */ int Xorriso_conv_name_chars(struct XorrisO *xorriso, char *name, char **result, size_t *result_len, int name_space, int flag) { int ret, relax_mem; struct isoburn_imgen_opts *sopts= NULL; *result= NULL; *result_len= 0; relax_mem= xorriso->relax_compliance; ret= isoburn_igopt_new(&sopts, 0); if(ret<=0) { Xorriso_process_msg_queues(xorriso, 0); ret= -1; goto ex; } if(!(flag & 1)) xorriso->relax_compliance|= isoburn_igopt_omit_version_numbers; ret= Xorriso_make_iso_write_opts(xorriso, NULL, sopts, 0); if(ret <= 0) { ret= -1; goto ex; } ret= isoburn_conv_name_chars(sopts, name, strlen(name), result, result_len, name_space); if(ret <= 0) { Xorriso_process_msg_queues(xorriso, 0); ret= 0; goto ex; } ret= 1; ex:; isoburn_igopt_destroy(&sopts, 0); xorriso->relax_compliance= relax_mem; return(ret); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2010 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of functions which manage the relation between xorriso and the libraries: libburn, libisofs, and libisoburn. */ #ifndef Xorriso_pvt_x_includeD #define Xorriso_pvt_x_includeD yes #ifndef Xorriso_standalonE /* The library which does the ISO 9660 / RockRidge manipulations */ #include <libisofs/libisofs.h> /* The library which does MMC optical drive operations */ #include <libburn/libburn.h> /* The library which enhances overwritable media with ISO 9660 multi-session capabilities via the method invented by Andy Polyakov for growisofs */ #include <libisoburn/libisoburn.h> /* The official xorriso options API. "No shortcuts" */ #include "xorriso.h" /* The inner description of XorrisO */ #include "xorriso_private.h" /* The inner isofs- and burn-library interface */ #include "xorrisoburn.h" #else /* ! Xorriso_standalonE */ #include "../libisofs/libisofs.h" #include "../libburn/libburn.h" #include "../libisoburn/libisoburn.h" #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" #endif /* Xorriso_standalonE */ int Xorriso_abort(struct XorrisO *xorriso, int flag); /* @param flag bit0= suppress messages below UPDATE bit1= suppress messages below FAILURE */ int Xorriso_set_image_severities(struct XorrisO *xorriso, int flag); int Xorriso__sev_to_text(int severity, char **severity_name, int flag); /* @param flag bit0= report libisofs error text bit1= victim is disk_path bit2= do not inquire libisofs, report msg_text and min_severity */ int Xorriso_report_iso_error(struct XorrisO *xorriso, char *victim, int iso_error_code, char msg_text[], int os_errno, char min_severity[], int flag); int Xorriso_process_msg_queues(struct XorrisO *xorriso, int flag); #endif /* ! Xorriso_pvt_x_includeD */ #!/bin/sh # # Produce man page xorriso/xorriso.1 and info file xorriso/xorriso.info # from base file xorriso/xorriso.texi. # Same for xorriso/xorrisofs.texi and xorriso/xorrecord.texi. ( cd xorriso ; makeinfo --no-split ./xorriso.texi ) ( cd xorriso ; makeinfo --no-split ./xorrisofs.texi ) ( cd xorriso ; makeinfo --no-split ./xorrecord.texi ) ( cd xorriso ; makeinfo --no-split ./xorriso-tcltk.texi ) ( cd xorriso-dd-target ; makeinfo --no-split ./xorriso-dd-target.texi ) ( cd test ; makeinfo --no-split ./merge_debian_isos.texi ) xorriso/make_xorriso_1 -auto xorriso/make_xorriso_1 -auto -xorrisofs xorriso/make_xorriso_1 -auto -xorrecord xorriso/make_xorriso_1 -auto -xorriso-tcltk xorriso/make_xorriso_1 -auto -xorriso-dd-target xorriso/make_xorriso_1 -auto -merge_debian_isos #!/bin/sh # Create version timestamp xorriso/xorriso_timestamp.h # to be executed within ./libisoburn-develop timestamp="$(date -u '+%Y.%m.%d.%H%M%S')" echo "Version timestamp : $timestamp" echo '#define Xorriso_timestamP "'"$timestamp"'"' >xorriso/xorriso_timestamp.h /* ( cd xorriso ; cc -g -Wall -o make_xorriso_1 make_xorriso_1.c ) */ /* Specialized converter from xorriso/xorriso.texi to xorriso/xorriso.1, or from xorriso/xorrisofs.texi to xorriso/xorrisofs.1, or from xorriso/xorrecord.texi to xorriso/xorrecord.1. or from xorriso/xorriso-tcltk.texi to xorriso/xorriso-tcltk.1. or from xorriso/xorriso-dd-target.texi to xorriso/xorriso-dd-target.1. of from test/merge_debian_iso.texi to test/merge_debian_iso.1 The conversion rules are described at the beginning of xorriso/xorriso.texi Copyright 2010 - 2022 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <errno.h> /* The conversion state */ struct Mx1 { char prog[4096]; int count_in; int count_out; int skipping; /* <0 stacked skipping , 0= no , >0 counting down */ }; int Mx1_substitute(struct Mx1 *m, char line_in[256], char line_out[256], int raw, int upto, int flag); int Mx1_init(struct Mx1 *m, char *prog, int flag) { strncpy(m->prog, prog, sizeof(m->prog) - 1); m->prog[sizeof(m->prog) - 1]= 0; m->count_in= 0; m->count_out= 0; m->skipping= 0; return(1); } int Mx1_report_error(struct Mx1 *m, char *text, int flag) { fprintf(stderr, "%s : line %d : %s\n", m->prog, m->count_in, text); return(1); } int Mx1__get_word(char *line, char word[256], char **remainder, int flag) { char *cpt, *start; int l; word[0]= 0; *remainder= NULL; for(cpt= line; *cpt != 0 && isspace(*cpt); cpt++); if(*cpt == 0) return(0); start= cpt; for(cpt= line; *cpt != 0 && ! isspace(*cpt); cpt++); l= cpt - start; if(l > 0) strncpy(word, start, l); word[l]= 0; *remainder= cpt; return(1); } int Mx1_is_wrap(struct Mx1 *m, char wraps[][20], char *start, char **found, int flag) { int i; for(i= 0; wraps[i][0] != 0; i++) if(strncmp(start, wraps[i], strlen(wraps[i])) == 0) break; if(wraps[i][0] != 0) { if(found != NULL) *found= wraps[i]; return(1); } return(0); } int Mx1_is_bold_wrap(struct Mx1 *m, char *start, char **found, int flag) { int ret; static char bold_wraps[][20]= { "@b{", "@dfn{", "@emph{", "@strong{", "@command{", "" }; ret= Mx1_is_wrap(m, bold_wraps, start, found, 0); return(ret); } int Mx1_is_normal_wrap(struct Mx1 *m, char *start, char **found, int flag) { int ret; static char normal_wraps[][20]= { "@var{", "@code{", "@i{", "@abbr{", "@file{", "@option{", "@samp{", "@r{", "" }; ret= Mx1_is_wrap(m, normal_wraps, start, found, 0); return(ret); } int Mx1_is_ignored_wrap(struct Mx1 *m, char *start, char **found, int flag) { int ret; static char ignored_wraps[][20]= { "@ref{", "@xref{", "" }; ret= Mx1_is_wrap(m, ignored_wraps, start, found, 0); return(ret); } int Mx1_is_any_wrap(struct Mx1 *m, char *start, char **found, int flag) { int ret; ret= Mx1_is_bold_wrap(m, start, found, 0); if(ret > 0) return(1); ret= Mx1_is_normal_wrap(m, start, found, 0); if(ret > 0) return(2); ret= Mx1_is_ignored_wrap(m, start, found, 0); if(ret > 0) return(3); return(0); } /* @param flag bit0= recursion bit1= drop content of brackets */ int Mx1_rewrap(struct Mx1 *m, char **read_pt, char **write_pt, char *write_base, char *envelope, char *front, char *back, int flag) { char *rpt, *wpt, *ept, content[256], msg[256]; int l, ret; rpt= *read_pt; wpt= *write_pt; ept= strchr(rpt, '}'); if(ept == NULL) { sprintf(msg, "No closing bracket found for '%s'", envelope); Mx1_report_error(m, msg, 0); return(-1); } /* Mapped {...} content is subject to the rules except {...} mapping. */ l= ept - rpt; if(flag & 2) l= 0; if(l > 0) { ret= Mx1_substitute(m, rpt, content, 0, l, 1); if(ret <= 0) return(ret); l= strlen(content); } if((wpt - write_base) + l + strlen(front) + strlen(back) > 255) { Mx1_report_error(m, "Line length overflow while text substitution", 0); return(-1); } strcpy(wpt, front); wpt+= strlen(front); if(l > 0) memcpy(wpt, content, l); wpt+= l; strcpy(wpt, back); wpt+= strlen(back); (*read_pt)+= ept - rpt; (*write_pt)= wpt; return(1); } /* @param flag bit0= recursion */ int Mx1_substitute(struct Mx1 *m, char line_in[256], char line_out[256], int raw, int upto, int flag) { char *rpt, *wpt, *found; int ret, typ= 0; wpt= line_out; for(rpt= line_in; rpt - line_in < upto && *rpt != 0; rpt++) { if(rpt - line_in < raw) { *(wpt++)= *rpt; continue; } if(*rpt == '@') { typ= 0; if(!(flag & 1)) typ= Mx1_is_any_wrap(m, rpt, &found, 0); if(typ == 1) { /* @b{...}, @command{...}, @dfn{...}, @emph{...}, @strong{...} get mapped to \fB...\fR . */ rpt+= strlen(found); ret= Mx1_rewrap(m, &rpt, &wpt, line_out, found , "\\fB", "\\fR", flag & 1); if(ret <= 0) return(ret); } else if(typ == 2) { /* @abbr{...}, @code{...}, @file{...}, @i{...}, @option{...}, @r{...}, @ref{...}, @samp{...},@var{...}, get mapped to ... . */ rpt+= strlen(found); ret= Mx1_rewrap(m, &rpt, &wpt, line_out, found, "", "", flag & 1); if(ret <= 0) return(ret); } else if(typ == 3) { /* @ref{...}, @xref{...} get mapped to empty text. */ rpt+= strlen(found); ret= Mx1_rewrap(m, &rpt, &wpt, line_out, found , "", "", (flag & 1) | 2); if(ret <= 0) return(ret); } else if(strncmp(rpt, "@email{", 7) == 0 && !(flag & 1)) { /* @email{...} gets mapped to <...> . */ rpt+= 7; ret= Mx1_rewrap(m, &rpt, &wpt, line_out, "@email{", "<", ">", 0); if(ret <= 0) return(ret); } else if(strncmp(rpt, "@minus{}", 8) == 0) { /* @minus{} will become "-". */ if((wpt - line_out) + 1 > 255) goto overflow; *(wpt++)= '-'; rpt+= 7; } else if(strncmp(rpt, "@@", 2) == 0 || strncmp(rpt, "@{", 2) == 0 || strncmp(rpt, "@}", 2) == 0) { /* @@ , @{, @} will get stripped of their first @. */ if((wpt - line_out) + 1 > 255) goto overflow; *(wpt++)= *(rpt + 1); rpt++; } else { if((wpt - line_out) + 1 > 255) goto overflow; *(wpt++)= *(rpt); } } else if(*rpt == '\\') { /* "\" becomes "\\" */ if((wpt - line_out) + 2 > 255) goto overflow; *(wpt++)= '\\'; *(wpt++)= '\\'; } else if((wpt - line_out) + 1 > 255) { overflow:; Mx1_report_error(m, "Line length overflow while text substitution", 0); return(-1); } else *(wpt++)= *rpt; } *wpt= 0; return(1); } /* @return 1= line_out is valid, 0= do not put out line_out, -1 = error */ int Mx1_convert(struct Mx1 *m, char line_in[256], char line_out[256], int flag) { int l, num, keep= 0, ret, raw, i, backslash_count; char word[256], buf[256], *remainder, *wpt; m->count_in++; l= strlen(line_in); if(m->skipping > 0) { m->skipping--; return(0); } /* The first line gets discarded. */ if(m->count_in == 1) return(0); /* Line start "@c man " will become "", the remainder is put out unaltered. */ if(strncmp(line_in, "@c man ", 7) == 0) { strcpy(line_out, line_in + 7); m->count_out++; return(1); } /* Lines "@*" will be converted to ".br" */ if(strcmp(line_in, "@*") == 0) { strcpy(line_out, ".br"); m->count_out++; return(1); } /* @c man-ignore-lines N will discard N following lines. "@c man-ignore-lines begin" discards all following lines up to "@c man-ignore-lines end". */ if(strncmp(line_in, "@c man-ignore-lines ", 20) == 0) { if(strcmp(line_in + 20, "begin") == 0) { m->skipping--; return(0); } else if(strcmp(line_in + 20, "end") == 0) { if(m->skipping < 0) m->skipping++; return(0); } else if(m->skipping == 0) { num= 0; sscanf(line_in + 20, "%d", &num); if(num > 0) { m->skipping= num; return(0); } } Mx1_report_error(m, "Inappropriate use of '@c man-ignore-lines'", 0); return(-1); } /* Line blocks of "@menu" "@end menu" will be discarded. */ if(strcmp(line_in, "@menu") == 0) { m->skipping--; return(0); } if(strcmp(line_in, "@end menu") == 0) { if(m->skipping < 0) m->skipping++; return(0); } if(m->skipping) return(0); /* "@item -word words" becomes "\fB\-word\fR words". */ /* "@item word words" becomes "\fBword\fR words". */ if(strncmp(line_in, "@item ", 6) == 0) { ret= Mx1__get_word(line_in + 6, word, &remainder, 0); if(ret <= 0) { Mx1_report_error(m, "Found no word after @item", 0); return(0); } strcpy(buf, "\\fB"); if(word[0] == '-') { if(l >= 255) { length_overflow:; Mx1_report_error(m, "Line length overflow while converting @item", 0); return(-1); } strcat(buf, "\\"); } /* Substitute option text */ raw= strlen(buf); strcat(buf, word); ret= Mx1_substitute(m, buf, line_out, raw, strlen(buf), 0); if(ret <= 0) return(-1); if(strlen(line_out) + 3 + strlen(remainder) > 255) goto length_overflow; strcpy(buf, line_out); strcat(buf, "\\fR"); raw= strlen(buf); strcat(buf, remainder); /* Substitute arguments text */ ret= Mx1_substitute(m, buf, line_out, raw, strlen(buf), 0); if(ret <= 0) return(-1); m->count_out++; return(1); } /* @strong{... } gets mapped to \fB...\fR . */ /* @command{... } gets mapped to \fB...\fR . */ /* @minus{} will become "-". */ /* Mapped {...} content is subject to the rules except {...} mapping. */ /* @@ , @{, @} will get stripped of their first @. */ /* "\" becomes "\\" */ if(line_in[0] != '@' || Mx1_is_any_wrap(m, line_in, NULL, 0) > 0 || strncmp(line_in, "@minus{}", 8) == 0 || strncmp(line_in, "@@", 2) == 0 || strncmp(line_in, "@{", 2) == 0 || strncmp(line_in, "@}", 2) == 0 ) { keep= 1; ret= Mx1_substitute(m, line_in, line_out, 0, strlen(line_in), 0); if(ret <= 0) return(-1); } /* Other lines which begin by "@" will be discarded. */ if(! keep) { if(line_in[0] == '@') return(0); strcpy(line_out, line_in); } /* "-" which are not preceded by an uneven number of "\" will get prepended one "\". */ l= strlen(line_out); backslash_count= 0; wpt= buf; for(i= 0; i < l; i++) { if(line_out[i] == '\\') backslash_count++; else if(line_out[i] == '-') { if(backslash_count % 2 == 0) *(wpt++)= '\\'; backslash_count= 0; } else backslash_count= 0; *(wpt++)= line_out[i]; } *wpt= 0; strcpy(line_out, buf); m->count_out++; return(1); } int main(int argc, char **argv) { int ret, l, as_filter= 0, i; char line_in[256], line_out[256], *got; static char name_in[1024]= {"xorriso/xorriso.texi"}; static char name_out[1024]= {"xorriso/xorriso.1"}; struct Mx1 m; FILE *fp_in= stdin, *fp_out= stdout; Mx1_init(&m, argv[0], 0); if(argc < 2) { usage:; fprintf(stderr, "usage: %s -auto|-filter [-xorrisofs]\n", argv[0]); fprintf(stderr, " -auto xorriso/xorriso.texi -> xorriso/xorriso.1\n"); fprintf(stderr, " -filter stdin -> stdout\n"); fprintf(stderr, " -xorrisofs process xorriso/xorrisofs.texi\n"); fprintf(stderr, " -xorrecord process xorriso/xorrecord.texi\n"); fprintf(stderr, " -xorriso-tcltk process xorriso/-xorriso-tcltk.texi\n"); fprintf(stderr, " -xorriso-dd-target process xorriso/-xorriso-dd-target.texi\n"); exit(2); } for(i= 1; i < argc; i++) { if(strcmp(argv[i], "-filter") == 0) { as_filter= 1; } else if(strcmp(argv[i], "-auto") == 0) { as_filter= 0; } else if(strcmp(argv[i], "-xorrisofs") == 0) { strcpy(name_in, "xorriso/xorrisofs.texi"); strcpy(name_out, "xorriso/xorrisofs.1"); } else if(strcmp(argv[i], "-xorrecord") == 0) { strcpy(name_in, "xorriso/xorrecord.texi"); strcpy(name_out, "xorriso/xorrecord.1"); } else if(strcmp(argv[i], "-xorriso-tcltk") == 0) { strcpy(name_in, "xorriso/xorriso-tcltk.texi"); strcpy(name_out, "xorriso/xorriso-tcltk.1"); } else if(strcmp(argv[i], "-xorriso-dd-target") == 0) { strcpy(name_in, "xorriso-dd-target/xorriso-dd-target.texi"); strcpy(name_out, "xorriso-dd-target/xorriso-dd-target.1"); } else if(strcmp(argv[i], "-merge_debian_isos") == 0) { strcpy(name_in, "test/merge_debian_isos.texi"); strcpy(name_out, "test/merge_debian_isos.1"); } else { fprintf(stderr, "%s : unknown option %s\n", argv[0], argv[i]); goto usage; } } if(!as_filter) { fp_in= fopen(name_in, "r"); if(fp_in == NULL) { fprintf(stderr, "%s : failed to fopen( %s ,r) : %d %s\n", argv[0], name_in, errno, strerror(errno)); exit(3); } fp_out= fopen(name_out, "w"); if(fp_out == NULL) { fprintf(stderr, "%s : failed to fopen( %s ,w) : %d %s\n", argv[0], name_out, errno, strerror(errno)); exit(4); } } while(1) { got= fgets(line_in, sizeof(line_in), fp_in); if(got == NULL) break; l= strlen(line_in); while(l > 0) { if(line_in[l - 1] == '\r' || line_in[l - 1] == '\n') { line_in[l - 1] = 0; l--; } else break; } ret= Mx1_convert(&m, line_in, line_out, 0); if(ret < 0) exit(1); if(ret == 0) continue; fprintf(fp_out, "%s\n", line_out); } exit(0); } #!/bin/sh # make_xorriso_standalone.sh # Copyright 2008 - 2024 Thomas Schmitt, scdbackup@gmx.net, GPLv2+ # # Not intended for general use in production installations ! # # This is a development tool which expects a special setup of directories. # It creates a new directory tree # xorriso-standalone # for building GNU xorriso. # The ./bootstrap script gets applied and a source tarball is made. # # This development tool is to be executed in a common parent of the directories # nglibisofs-develop jte-develop libburn-develop libisoburn-develop # where tarballs have been unpacked or repository copies have been cloned of # libisofs jte libburn libisoburn # according to instructions on http://libburnia-project.org. # automake and libtool have to be installed on the system. # # Further it is necessary to build a little binary for HTML man page production # (cd libisoburn-develop/xorriso && \ # cc -g -Wall -o unite_html_b_line unite_html_b_line.c ) # # Then run # # ./libisoburn-develop/xorriso/make_xorriso_standalone.sh # # From the emerging xorriso-standalone tarball can be build a binary # xorriso/xorriso # which at runtime does not depend on installed libburnia libraries. # Execute in xorriso-standalone : # # ./configure && make # # By the variable setting create_gnu_xorriso="yes" # the result will become a GNU xorriso tarball under GPLv3+. # Without this setting, the result is technically equivalent but # stays under GPLv2+. In that case the files xorriso/*_gnu_xorriso # are merely informative. # Note that it is not permissible to revert the transition from # GPLv2+ to GPLv3+. (Rather derive a new GPLv2+ from libburnia.) create_gnu_xorriso="yes" current_dir=$(pwd) lone_dir="$current_dir"/"xorriso-standalone" xorriso_rev=1.5.7 # For unstable uploads and patch level 0 of stable releases: xorriso_pl="" # For higher patch levels of stable releases: ## xorriso_pl=".pl01" with_bootstrap_tarball=1 create_dir() { if mkdir "$1" then dummy=dummy else echo "Failed to create : $1" >&2 exit 1 fi } goto_dir() { if cd "$1" then dummy=dummy else echo "Failed to cd $1" >&2 exit 1 fi } copy_files() { if cp "$@" then dummy=dummy else echo "Failed to : cp " "$@" >&2 exit 1 fi } copy_tree() { if cp -a "$@" then dummy=dummy else echo "Failed to : cp -a " "$@" >&2 exit 1 fi } if test -e "$lone_dir" then echo "Already existing : $lone_dir" >&2 exit 1 fi # Top level directory goto_dir "$current_dir"/libisoburn-develop create_dir "$lone_dir" copy_files \ AUTHORS \ CONTRIBUTORS \ COPYRIGHT \ COPYING \ ChangeLog \ INSTALL \ NEWS \ acinclude.m4 \ bootstrap \ version.h.in \ \ "$lone_dir" # Formerly copied but actually generated or copied by ./bootstrap: # aclocal.m4 ltmain.sh # compile config.guess config.sub depcomp install-sh missing copy_files xorriso/xorriso_bootstrap.txt "$lone_dir"/bootstrap copy_files xorriso/configure_ac.txt "$lone_dir"/configure.ac copy_files xorriso/xorriso_makefile_am.txt "$lone_dir"/Makefile.am # libisoburn create_dir "$lone_dir"/libisoburn copy_files \ libisoburn/*.[ch] \ "$lone_dir"/libisoburn copy_files COPYRIGHT "$lone_dir"/libisoburn touch \ xorriso/man_1_xorriso.html \ xorriso/man_1_xorrisofs.html \ xorriso/man_1_xorrecord.html xorriso/convert_man_to_html.sh create_dir "$lone_dir"/xorriso copy_files \ xorriso/xorriso.h \ xorriso/xorriso_private.h \ xorriso/sfile.h \ xorriso/sfile.c \ xorriso/aux_objects.h \ xorriso/aux_objects.c \ xorriso/findjob.h \ xorriso/findjob.c \ xorriso/check_media.h \ xorriso/check_media.c \ xorriso/misc_funct.h \ xorriso/misc_funct.c \ xorriso/text_io.h \ xorriso/text_io.c \ xorriso/match.h \ xorriso/match.c \ xorriso/emulators.h \ xorriso/emulators.c \ xorriso/disk_ops.h \ xorriso/disk_ops.c \ xorriso/cmp_update.h \ xorriso/cmp_update.c \ xorriso/parse_exec.h \ xorriso/parse_exec.c \ xorriso/opts_a_c.c \ xorriso/opts_d_h.c \ xorriso/opts_i_o.c \ xorriso/opts_p_z.c \ \ xorriso/xorrisoburn.h \ xorriso/base_obj.h \ xorriso/base_obj.c \ xorriso/lib_mgt.h \ xorriso/lib_mgt.c \ xorriso/sort_cmp.h \ xorriso/sort_cmp.c \ xorriso/drive_mgt.h \ xorriso/drive_mgt.c \ xorriso/iso_img.h \ xorriso/iso_img.c \ xorriso/iso_tree.h \ xorriso/iso_tree.c \ xorriso/iso_manip.h \ xorriso/iso_manip.c \ xorriso/write_run.h \ xorriso/write_run.c \ xorriso/read_run.h \ xorriso/read_run.c \ xorriso/filters.h \ xorriso/filters.c \ \ xorriso/xorriso_main.c \ xorriso/xorriso_timestamp.h \ \ xorriso/changelog.txt \ xorriso/xorriso_eng.html \ xorriso/make_docs.sh \ xorriso/make_xorriso_standalone.sh \ xorriso/unite_html_b_line.c \ xorriso/convert_man_to_html.sh \ xorriso/man_xorriso_to_html.sh \ xorriso/man_xorrisofs_to_html.sh \ xorriso/man_xorrecord_to_html.sh \ xorriso/xorriso.texi \ xorriso/xorriso.info \ xorriso/xorrisofs.texi \ xorriso/xorrisofs.info \ xorriso/xorrecord.texi \ xorriso/xorrecord.info \ xorriso/xorriso-tcltk.texi \ xorriso/xorriso-tcltk.info \ xorriso/make_xorriso_1.c \ xorriso/xorriso.1 \ xorriso/xorrisofs.1 \ xorriso/xorrecord.1 \ xorriso/xorriso-tcltk.1 \ xorriso/man_1_xorriso.html \ xorriso/man_1_xorrisofs.html \ xorriso/man_1_xorrecord.html \ "$lone_dir"/xorriso copy_files COPYRIGHT "$lone_dir"/xorriso copy_files xorriso/xorriso_buildstamp_none.h \ "$lone_dir"/xorriso/xorriso_buildstamp.h copy_files xorriso/xorriso_buildstamp_none.h \ "$lone_dir"/xorriso/xorriso_buildstamp_none.h create_dir "$lone_dir"/doc copy_files doc/partition_offset.wiki \ doc/startup_file.txt \ doc/qemu_xorriso.wiki \ "$lone_dir"/doc create_dir "$lone_dir"/test copy_files \ test/compare_file.c \ test/merge_debian_isos \ test/merge_debian_isos.texi \ test/merge_debian_isos.info \ test/merge_debian_isos.1 \ "$lone_dir"/test create_dir "$lone_dir"/frontend copy_files \ frontend/frontend_pipes_xorriso.c \ frontend/README-tcltk \ frontend/xorriso-tcltk \ frontend/sh_on_named_pipes.sh \ frontend/xorriso_broker.sh \ frontend/grub-mkrescue-sed.sh \ "$lone_dir"/frontend create_dir "$lone_dir"/xorriso-dd-target copy_files \ xorriso-dd-target/xorriso-dd-target \ xorriso-dd-target/xorriso-dd-target.texi \ xorriso-dd-target/xorriso-dd-target.info \ xorriso-dd-target/xorriso-dd-target.1 \ "$lone_dir"/xorriso-dd-target # releng copy_tree releng "$lone_dir"/releng rm "$lone_dir"/releng/auto_cxx rm -r "$lone_dir"/releng/releng_generated_data create_dir "$lone_dir"/releng/releng_generated_data # nglibisofs create_dir "$lone_dir"/libisofs create_dir "$lone_dir"/libisofs/filters goto_dir "$current_dir"/nglibisofs-develop copy_files libisofs/*.[ch] "$lone_dir"/libisofs copy_files libisofs/filters/*.[ch] "$lone_dir"/libisofs/filters copy_files doc/susp_aaip*.txt "$lone_dir"/doc copy_files doc/zisofs_format.txt "$lone_dir"/doc copy_files doc/zisofs2_format.txt "$lone_dir"/doc copy_files doc/checksums.txt "$lone_dir"/doc copy_files doc/boot_sectors.txt "$lone_dir"/doc copy_files COPYRIGHT "$lone_dir"/libisofs test -e CONTRIBUTORS && cat CONTRIBUTORS >>"$lone_dir"/CONTRIBUTORS # To get a common version.h cat version.h.in >> "$lone_dir"/version.h.in # libjte create_dir "$lone_dir"/libjte goto_dir "$current_dir"/jte-develop copy_files *.[ch] "$lone_dir"/libjte copy_files COPYRIGHT "$lone_dir"/libjte # # Now using libisoburn/releng/jigdo-gen-md5-list because in jigit it is # restricted to Linux and FreeBSD. goto_dir "$current_dir"/libisoburn-develop/releng copy_files jigdo-gen-md5-list jigdo-gen-md5-list.1 "$lone_dir"/libjte # libburn create_dir "$lone_dir"/libburn goto_dir "$current_dir"/libburn-develop copy_files libburn/*.[ch] "$lone_dir"/libburn copy_files COPYRIGHT "$lone_dir"/libburn cat CONTRIBUTORS >>"$lone_dir"/CONTRIBUTORS # Delete a source module of yet unclear ancestry. # The build process will avoid to use it. rm "$lone_dir"/libburn/crc.c # To get a common version.h cat version.h.in >> "$lone_dir"/version.h.in # Decision about legal situation goto_dir "$current_dir"/libisoburn-develop if test "$create_gnu_xorriso" = "yes" then copy_files xorriso/README_gnu_xorriso "$lone_dir"/README copy_files xorriso/COPYRIGHT_gnu_xorriso "$lone_dir"/COPYRIGHT copy_files xorriso/COPYING_gnu_xorriso "$lone_dir"/COPYING copy_files xorriso/AUTHORS_gnu_xorriso "$lone_dir"/AUTHORS # patch xorriso/xorriso.h to be GNU xorriso sed -e's/define Xorriso_libburnia_xorrisO/define Xorriso_GNU_xorrisO/' \ -e's/This may be changed to Xorriso_GNU_xorrisO in order to c/C/' \ <xorriso/xorriso.h >"$lone_dir"/xorriso/xorriso.h else copy_files README "$lone_dir"/README fi # tarball if test "$with_bootstrap_tarball" = 1 then tarball_dir="$current_dir"/xorriso-"$xorriso_rev""$xorriso_pl" mv "$lone_dir" "$tarball_dir" goto_dir "$tarball_dir" ./bootstrap # Remove unneeded temporary data from ./bootstrap rm -r ./autom4te.cache # Repair non-portable shell code output of ./bootstrap ( cd "$compile_dir" || exit 1 sed -e 's/^for ac_header in$/test -z 1 \&\& for ac_header in dummy/' \ < ./configure > ./configure-repaired if test "$?" = 0 then echo "$0: Empty 'for ac_header in' found in configure." >&2 fi mv ./configure-repaired ./configure chmod a+rx,go-w,u+w ./configure ) if test "$create_gnu_xorriso" = "yes" then # ftp-upload@gnu.org rejects Makefile.in with a dangerous chmod on make dist sed -e 's/-perm -777 -exec chmod a+rwx/-perm -755 -exec chmod u+rwx,go+rx/' \ < Makefile.in > new_Makefile.in mv new_Makefile.in Makefile.in fi cd "$current_dir" tar czf ./xorriso-"$xorriso_rev""$xorriso_pl".tar.gz $(basename "$tarball_dir") ls -l $(pwd)/xorriso-"$xorriso_rev""$xorriso_pl".tar.gz mv "$tarball_dir" "$lone_dir" fi echo "Done" echo "HINT: Now build xorriso/xorriso by:" echo " cd '$lone_dir' && ./configure && make" echo #!/bin/sh # # man_xorrecord_to_html.sh - ts A80118 , B10309 , B50730 # # Generates a HTML version of man page xorrecord.1 # # To be executed in the libisoburn toplevel directory (eg. ./libisoburn-0.1.0) # # set -x man_dir=$(pwd)"/xorriso" export MANPATH="$man_dir" manpage="xorrecord" raw_html=$(pwd)/"xorriso/raw_man_1_xorrecord.html" htmlpage=$(pwd)/"xorriso/man_1_xorrecord.html" if test -r "$man_dir"/"$manpage".1 then dummy=dummy else echo "Cannot find readable man page source $1" >&2 exit 1 fi if test -e "$man_dir"/man1 then dummy=dummy else ln -s . "$man_dir"/man1 fi if test "$1" = "-work_as_filter" then # set -x sed \ -e 's/<meta name="generator" content="groff -Thtml, see www.gnu.org">/<meta name="generator" content="groff -Thtml, via man -H, via xorriso\/convert_man_to_html.sh">/' \ -e 's/<meta name="Content-Style" content="text\/css">/<meta name="Content-Style" content="text\/css"><META NAME="description" CONTENT="man page of xorriso"><META NAME="keywords" CONTENT="man xorrecord, manual, xorrecord, CD, DVD, BD, cdrecord, compatible"><META NAME="robots" CONTENT="follow">/' \ -e 's/<title>XORRECORD<\/title>/<title>man 1 xorrecord<\/title>/' \ -e 's/<h1 align=center>XORRECORD<\/h1>/<h1 align=center>man 1 xorrecord<\/h1>/' \ -e 's/<body>/<body BGCOLOR="#F5DEB3" TEXT=#000000 LINK=#0000A0 VLINK=#800000>/' \ -e 's/<b>MMC, Session, Track, Media types: <br> MMC<\/b>/<b>MMC, Session, Track, Media types:<\/b><BR>\ <BR><b>MMC<\/b>/' \ -e 's/<b>Drive preparation and addressing:<\/b>/<b>Drive preparation and addressing:<\/b><BR>/' \ -e 's/<b>Relation to program xorriso: <br> xorrecord<\/b>/<b>Relation to program xorriso:<\/b><BR>\ <BR><b>xorrecord<\/b>/' \ -e 's/<b>Addressing the drive: <br> --devices<\/b>/<b>Addressing the drive: <\/b><BR>\ <BR><b>--devices<\/b>/' \ -e 's/EXAMPLES):<br>/<A HREF="#EXAMPLES">EXAMPLES<\/A>):<br>/' \ -e 's/<b>Inquiring drive and media:<\/b>/\ <BR><b>Inquiring drive and media:<\/b><BR>\ <BR>/' \ -e 's/<b>Settings for the burn run:<\/b>/\ <BR><b>Settings for the burn run:<\/b><BR>/' \ -e 's/<b><br> blank=mode<\/b>/<BR>\ <BR><b>blank=mode<\/b>/' \ -e 's/<b>Program version and verbosity: <br> −version<\/b>/\ <BR><b>Program version and verbosity:<\/b><BR>\ <BR><b>-version<\/b>/' \ -e 's/<b>Options not compatible to cdrecord: <br> --no_rc<\/b>/\ <BR><b>Options not compatible to cdrecord:<\/b><BR>\ <BR><b>--no_rc<\/b>/' \ \ -e 's/<\/body>/<BR><HR><FONT SIZE=-1><CENTER>(HTML generated from '"$manpage"'.1 on '"$(date)"' by '$(basename "$0")' )<\/CENTER><\/FONT><\/body>/' \ -e 's/−/-/g' \ <"$2" >"$htmlpage" set +x chmod u+rw,go+r,go-w "$htmlpage" echo "Emerged file:" ls -lL "$htmlpage" else # export BROWSER='cp "%s" '"$raw_html" export BROWSER=$(pwd)/'xorriso/unite_html_b_line "%s" '"$raw_html" man -H "$manpage" # cp "$raw_html" /tmp/x.html "$0" -work_as_filter "$raw_html" rm "$raw_html" rm "$man_dir"/man1 fi #!/bin/sh # # man_xorriso_to_html.sh - ts A80118 , B10309 , B50730 # # Generates a HTML version of man page xorriso.1 # # To be executed in the libisoburn toplevel directory (eg. ./libisoburn-0.1.0) # # set -x man_dir=$(pwd)"/xorriso" export MANPATH="$man_dir" manpage="xorriso" raw_html=$(pwd)/"xorriso/raw_man_1_xorriso.html" htmlpage=$(pwd)/"xorriso/man_1_xorriso.html" if test -r "$man_dir"/"$manpage".1 then dummy=dummy else echo "Cannot find readable man page source $1" >&2 exit 1 fi if test -e "$man_dir"/man1 then dummy=dummy else ln -s . "$man_dir"/man1 fi if test "$1" = "-work_as_filter" then # set -x sed \ -e 's/<meta name="generator" content="groff -Thtml, see www.gnu.org">/<meta name="generator" content="groff -Thtml, via man -H, via xorriso\/convert_man_to_html.sh">/' \ -e 's/<meta name="Content-Style" content="text\/css">/<meta name="Content-Style" content="text\/css"><META NAME="description" CONTENT="man page of xorriso"><META NAME="keywords" CONTENT="man xorriso, manual, xorriso, CD, CD-RW, CD-R, burning, cdrecord, compatible"><META NAME="robots" CONTENT="follow">/' \ -e 's/<title>XORRISO<\/title>/<title>man 1 xorriso<\/title>/' \ -e 's/<h1 align=center>XORRISO<\/h1>/<h1 align=center>man 1 xorriso<\/h1>/' \ -e 's/<body>/<body BGCOLOR="#F5DEB3" TEXT=#000000 LINK=#0000A0 VLINK=#800000>/' \ -e 's/<b>Overview of features:<\/b>/\ <BR><A NAME="Overview"><\/A><b>Overview of features:<\/b>/' \ -e 's/<b>General information paragraphs:<\/b>/\ <BR><A NAME="General"><\/A><b>General information paragraphs:<\/b>/' \ -e 's/Session model <br>/<A HREF="#Model">Session model<\/A> <br>/' \ -e 's/^Media types and states/<A HREF="#Media">Media types and states<\/A>/' \ -e 's/^Creating, Growing, Modifying, Blind Growing/<A HREF="#Methods">Creating, Growing, Modifying, Blind Growing<\/A>/' \ -e 's/^Libburn drives/<A HREF="#Drives">Libburn drives<\/A>/' \ -e 's/^Rock Ridge, POSIX, X\/Open, El Torito, ACL, xattr/<A HREF="#Extras">Rock Ridge, POSIX, X\/Open, El Torito, ACL, xattr<\/A>/' \ -e 's/^Command processing/<A HREF="#Processing">Command processing<\/A>/' \ -e 's/^Dialog, Readline, Result pager/<A HREF="#Dialog">Dialog, Readline, Result pager<\/A>/' \ -e 's/have a look at section EXAMPLES/have a look at section <A HREF="#EXAMPLES">EXAMPLES<\/A>/' \ -e 's/<b>Session model:<\/b>/\ <BR><A NAME="Model"><\/A><b>Session model:<\/b><BR>/' \ -e 's/<b>Media types and states:<\/b>/\ <BR><A NAME="Media"><\/A><b>Media types and states:<\/b><BR>/' \ -e 's/<b>Creating, Growing, Modifying, Blind Growing:<\/b>/\ <BR><A NAME="Methods"><\/A><b>Creating, Growing, Modifying, Blind Growing:<\/b><BR>/' \ -e 's/<b>Libburn drives:<\/b>/\ <BR><b>Libburn drives:<\/b><A NAME="Drives"><\/A><BR>/' \ -e 's/^-dev /\ \ -dev /' \ -e 's/^-devices /\ \ -devices /' \ -e 's/<b>Rock Ridge, POSIX, X\/Open, El Torito, ACL, xattr: <br> Rock Ridge<\/b>/\ <BR><A NAME="Extras"><\/A><b>Rock Ridge, POSIX, X\/Open, El Torito, ACL, xattr:<\/b><BR>\ <BR><b>Rock Ridge<\/b>/' \ -e 's/<b>Command processing:<\/b>/\ <BR><A NAME="Processing"><\/A><b>Command processing:<\/b><BR>/' \ -e 's/<b>Dialog, Readline, Result pager:<\/b>/<A NAME="Dialog"><\/A><b>Dialog, Readline, Result pager:<\/b><BR>/' \ -e 's/<b><br> Execution order of program arguments:<\/b>/<br>\ <BR><A NAME="ArgSort"><\/A><b>Execution order of program arguments:<\/b><BR>/' \ -e 's/<b>Acquiring source and target drive:<\/b>/\ <BR><A NAME="AqDrive"><\/A><b>Acquiring source and target drive:<\/b><BR>/' \ -e 's/<b>Influencing the behavior of image/\ <BR><A NAME="Loading"><\/A><b>Influencing the behavior of image/' \ -e 's/<b>Inserting files into ISO image:<\/b>/\ <BR><A NAME="Insert"><\/A><b>Inserting files into ISO image:<\/b><BR>/' \ -e 's/<b>Settings for file insertion:<\/b>/\ <BR><A NAME="SetInsert"><\/A><b>Settings for file insertion:<\/b><BR>\ <BR>/' \ -e 's/<b>Tree traversal command -find: <br> \−find<\/b>/\ <BR><A NAME="CmdFind"><\/A><b>Tree traversal command -find:<\/b><BR>\ <BR><b>-find<\/b>/' \ -e 's/<b>File manipulations:<\/b>/\ <BR><A NAME="Manip"><\/A><b>File manipulations:<\/b><BR>/' \ -e 's/^<p><b>−iso_rr_pattern/<p>\ <BR><b>\−iso_rr_pattern/' \ -e 's/EXAMPLES):<br>/<A HREF="#EXAMPLES">EXAMPLES<\/A>):<br>/' \ -e 's/<b>Filters for data file content:<\/b>/\ <BR><A NAME="Filter"><\/A><b>Filters for data file content:<\/b><BR>/' \ -e 's/<b>Writing the result, drive control:<\/b>/\ <BR><A NAME="Writing"><\/A><b>Writing the result, drive control:<\/b><BR>/' \ -e 's/^-find \/ /\ \ -find \/ /' \ -e 's/^$<\/b> ln -s/\ \ $<\/b> ln -s/' \ -e 's/<b>Settings for result writing:<\/b>/\ <BR><A NAME="SetWrite"><\/A><b>Settings for result writing:<\/b><BR>/' \ -e 's/^706k = 706kB/\ \ 706k = 706kB/' \ -e 's/^5540k = 5540kB/\ \ 5540k = 5540kB/' \ -e 's/<b>Bootable ISO images:<\/b>/\ <BR><A NAME="Bootable"><\/A><b>Bootable ISO images:<\/b><BR>/' \ -e 's/<b>Jigdo Template Extraction:<\/b>/\ <BR><A NAME="Jigdo"><\/A><b>Jigdo Template Extraction:<\/b><BR>/' \ -e 's/<b>Character sets:<\/b>/\ <BR><A NAME="Charset"><\/A><b>Character sets:<\/b><BR>/' \ -e 's/<b>Exception processing:<\/b>/\ <BR><A NAME="Exception"><\/A><b>Exception processing:<\/b><BR>/' \ -e 's/<b>Dialog mode control: <br> −dialog<\/b>/\ <BR><A NAME="DialogCtl"><\/A><b>Dialog mode control:<\/b><BR>\ <BR><b>-dialog<\/b>/' \ -e 's/<b>Drive and media related inquiry actions: <br> −devices<\/b>/\ <BR><A NAME="Inquiry"><\/A><b>Drive and media related inquiry actions:<\/b><BR>\ <BR><b>-devices<\/b>/' \ -e 's/<b>Navigation in ISO image and disk filesystem: <br> −cd/\ <BR><A NAME="Navigate"><\/A><b>Navigation in ISO image and disk filesystem:<\/b><BR>\ <BR><b>-cd<\/b>/' \ -e 's/<b>Evaluation of readability and recovery:<\/b>/\ <BR><A NAME="Verify"><\/A><b>Evaluation of readability and recovery:<\/b><BR>/' \ -e 's/<b>osirrox ISO-to-disk restore commands:<\/b>/\ <BR><A NAME="Restore"><\/A><b>osirrox ISO-to-disk restore commands:<\/b><BR>/' \ -e 's/<b>Command compatibility emulations:<\/b>/\ <BR><A NAME="Emulation"><\/A><b>Command compatibility emulations:<\/b><BR>/' \ -e 's/^<p><b>−as</<p>\ <BR><b>\−as</' \ -e 's/<b>Scripting, dialog and program control features:<\/b>/\ <BR><A NAME="Scripting"><\/A><b>Scripting, dialog and program control features:<\/b><BR>\ <BR>/' \ -e 's/<b>Support for frontend programs via stdin and stdout: <br> −pkt_output<\/b>/\ <BR><A NAME="Frontend"><\/A><b>Support for frontend programs via stdin and stdout:<\/b><BR>\ <BR><b>-pkt_output<\/b>/' \ -e 's/disk_path_stdin disk_path_stdout <br>/disk_path_stdin disk_path_stdout/' \ -e 's/xorriso -outdev \/dev\/sr2 \\ -blank fast \\ -pathspecs on/xorriso -outdev \/dev\/sr2 -blank fast -pathspecs on/' \ -e 's/\\ -add \\ \/sounds=\/home\/me\/sounds \\ \/pictures \\ -- \\ -rm_r \\/ -add \/sounds=\/home\/me\/sounds \/pictures -- -rm_r /' \ -e 's/\/sounds\/indecent \\ \’\/pictures\/\*private\*\’ \\/\/sounds\/indecent \’\/pictures\/*private*\’ /' \ -e 's/\/pictures\/confidential \\ -- \\ -add \\/\/pictures\/confidential -- -add/' \ -e 's/xorriso -dev \/dev\/sr2 \\ -rm_r \/sounds -- \\ -mv \\/xorriso -dev \/dev\/sr2 -rm_r \/sounds -- -mv /' \ -e 's/\/pictures\/confidential \\ \/pictures\/restricted \\ -- \\ -chmod/\/pictures\/confidential \/pictures\/restricted -- -chmod/' \ -e 's/go-rwx \/pictures\/restricted -- \\ -pathsspecs on \\ -add \\/go-rwx \/pictures\/restricted -- -pathsspecs on -add /' \ -e 's/\/sounds=\/home\/me\/prepared_for_dvd\/sounds_dummy /\/sounds=\/home\/me\/prepared_for_dvd\/sounds_dummy/' \ -e 's/\/movies=\/home\/me\/prepared_for_dvd\/movies \\ -- \\ -commit/\/movies=\/home\/me\/prepared_for_dvd\/movies -- -commit/' \ -e 's/xorriso -indev \/dev\/sr2 \\ -rm_r \/sounds -- \\/xorriso -indev \/dev\/sr2 -rm_r \/sounds -- /' \ -e 's/-outdev \/dev\/sr0 -blank fast \\ -commit -eject all/-outdev \/dev\/sr0 -blank fast -commit -eject all/' \ -e 's/See section FILES/See section <A HREF="#FILES">FILES<\/A>/' \ -e 's/See section EXAMPLES/See section <A HREF="#EXAMPLES">EXAMPLES<\/A>/' \ -e 's/<\/body>/<BR><HR><FONT SIZE=-1><CENTER>(HTML generated from '"$manpage"'.1 on '"$(date)"' by '$(basename "$0")' )<\/CENTER><\/FONT><\/body>/' \ -e 's/−/-/g' \ <"$2" >"$htmlpage" set +x chmod u+rw,go+r,go-w "$htmlpage" echo "Emerged file:" ls -lL "$htmlpage" else # export BROWSER='cp "%s" '"$raw_html" export BROWSER=$(pwd)/'xorriso/unite_html_b_line "%s" '"$raw_html" man -H "$manpage" # cp "$raw_html" /tmp/x.html "$0" -work_as_filter "$raw_html" rm "$raw_html" rm "$man_dir"/man1 fi #!/bin/sh # # man_xorrisofs_to_html.sh - ts A80118 , B10309 , B50730 # # Generates a HTML version of man page xorrisofs.1 # # To be executed in the libisoburn toplevel directory (eg. ./libisoburn-0.1.0) # # set -x man_dir=$(pwd)"/xorriso" export MANPATH="$man_dir" manpage="xorrisofs" raw_html=$(pwd)/"xorriso/raw_man_1_xorrisofs.html" htmlpage=$(pwd)/"xorriso/man_1_xorrisofs.html" if test -r "$man_dir"/"$manpage".1 then dummy=dummy else echo "Cannot find readable man page source $1" >&2 exit 1 fi if test -e "$man_dir"/man1 then dummy=dummy else ln -s . "$man_dir"/man1 fi if test "$1" = "-work_as_filter" then # set -x sed \ -e 's/<meta name="generator" content="groff -Thtml, see www.gnu.org">/<meta name="generator" content="groff -Thtml, via man -H, via xorriso\/convert_man_to_html.sh">/' \ -e 's/<meta name="Content-Style" content="text\/css">/<meta name="Content-Style" content="text\/css"><META NAME="description" CONTENT="man page of xorriso"><META NAME="keywords" CONTENT="man xorrisofs, manual, xorrisofs, ISO 9660, mkisofs, compatible"><META NAME="robots" CONTENT="follow">/' \ -e 's/<title>XORRISOFS<\/title>/<title>man 1 xorrisofs<\/title>/' \ -e 's/<h1 align=center>XORRISOFS<\/h1>/<h1 align=center>man 1 xorrisofs<\/h1>/' \ -e 's/<body>/<body BGCOLOR="#F5DEB3" TEXT=#000000 LINK=#0000A0 VLINK=#800000>/' \ -e 's/<b>ISO 9660, Rock Ridge, Joliet, HFS+: <br> ISO 9660<\/b>/\ <BR><b>ISO 9660, Rock Ridge, Joliet, HFS+:<\/b><BR>\ <BR><b>ISO 9660<\/b>/' \ -e 's/<b>Inserting files into the ISO image: <br> xorrisofs<\/b>/\ <BR><b>Inserting files into the ISO image:<\/b><BR>\ <BR><b>xorrisofs<\/b>/' \ -e 's/<b>Relation to program xorriso: <br> xorrisofs<\/b>/\ <BR><b>Relation to program xorriso:<\/b><BR>\ <BR><b>xorrisofs<\/b>/' \ -e 's/EXAMPLES):<br>/<A HREF="#EXAMPLES">EXAMPLES<\/A>):<br>/' \ -e 's/<b>Image loading:<\/b>/<b>Image loading:<\/b><BR>/' \ -e 's/<b>Settings for file insertion: <br> \−path-list<\/b>/\ <BR><b>Settings for file insertion:<\/b><BR>\ <BR><b>-path-list<\/b>/' \ -e 's/<b>Settings for image production: <br> −o<\/b>/\ <BR><b>Settings for image production:<\/b><BR>\ <BR><b>-o<\/b>/' \ -e 's/<b>Settings for standards compliance: <br> \−iso-level<\/b>/\ <BR><b>Settings for standards compliance:<\/b><BR>\ <BR><b>-iso-level<\/b>/' \ -e 's/<b>Settings for standards extensions:<\/b>/\ <BR><b>Settings for standards extensions:<\/b><BR>\ <BR>/' \ -e 's/<b>Settings for file hiding: <br> \−hide<\/b>/\ <BR><b>Settings for file hiding:<\/b><BR>\ <BR><b>-hide<\/b>/' \ -e 's/<b>ISO image ID strings:<\/b>/\ <BR><b>ISO image ID strings:<\/b><BR>/' \ -e 's/<b>El Torito Bootable ISO images:<\/b>/\ <BR><b>El Torito Bootable ISO images:<\/b><BR>/' \ -e 's/<b>System Area, MBR, GPT, APM, other boot blocks:<\/b>/\ <BR><b>System Area, MBR, GPT, APM, other boot blocks:<\/b><BR>/' \ -e 's/<b><br> −G<\/b> disk_path/<BR>\ <BR><b>-G<\/b> disk_path/' \ -e 's/<b>Character sets:<\/b>/\ <BR><b>Character sets:<\/b><BR>/' \ -e 's/<b>Jigdo Template Extraction:<\/b>/\ <BR><b>Jigdo Template Extraction:<\/b><BR>/' \ -e 's/<b>Miscellaneous options: <br> −print-size<\/b>/\ <BR><b>Miscellaneous options:<\/b><BR>\ <BR><b>-print-size<\/b>/' \ \ -e 's/<\/body>/<BR><HR><FONT SIZE=-1><CENTER>(HTML generated from '"$manpage"'.1 on '"$(date)"' by '$(basename "$0")' )<\/CENTER><\/FONT><\/body>/' \ -e 's/−/-/g' \ <"$2" >"$htmlpage" set +x chmod u+rw,go+r,go-w "$htmlpage" echo "Emerged file:" ls -lL "$htmlpage" else # export BROWSER='cp "%s" '"$raw_html" export BROWSER=$(pwd)/'xorriso/unite_html_b_line "%s" '"$raw_html" man -H "$manpage" # cp "$raw_html" /tmp/x.html "$0" -work_as_filter "$raw_html" rm "$raw_html" rm "$man_dir"/man1 fi /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2013 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of functions for pattern matching. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <fcntl.h> #include <errno.h> #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" /* @param flag bit0= do not augment relative structured search by xorriso->wdi bit1= return 2 if bonked at start point by .. (caller then aborts or retries without bit0) bit2= eventually prepend wdx rather than wdi @return <=0 error, 1= ok, 2= with bit1: relative pattern exceeds start point */ int Xorriso_prepare_regex(struct XorrisO *xorriso, char *adr, int flag) { int l,ret,i,count,bonked= 0,is_constant,is_still_relative= 0, adr_size; char *cpt,*npt,*adr_part= NULL, *absolute_adr= NULL, *adr_start,*wd; adr_size= 2 * SfileadrL; Xorriso_alloc_meM(adr_part, char, adr_size); Xorriso_alloc_meM(absolute_adr, char, adr_size); if(flag&4) wd= xorriso->wdx; else wd= xorriso->wdi; if(xorriso->search_mode>=2 && xorriso->search_mode<=4) { if(xorriso->search_mode==3 || xorriso->search_mode==4) { l= strlen(adr)+strlen(wd)+1; if(l * 2 + 2 > ((int) sizeof(xorriso->reg_expr)) || l * 2 + 2 > adr_size){ sprintf(xorriso->info_text,"Search pattern too long"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } } Xorriso_destroy_re(xorriso,0); if(xorriso->structured_search && xorriso->search_mode==3) { if(adr[0]!='/') is_still_relative= 1; if(is_still_relative && !(flag&1)) { /* relative expression : prepend working directory */ sprintf(absolute_adr,"%s/%s",wd,adr); adr_start= absolute_adr; xorriso->prepended_wd= 1; is_still_relative= 0; } else adr_start= adr; /* count slashes */; cpt= adr_start; while(*cpt=='/') cpt++; for(i= 0;1;i++) { cpt= strchr(cpt,'/'); if(cpt==NULL) break; while(*cpt=='/') cpt++; } count= i+1; xorriso->re= TSOB_FELD(regex_t,count); if(xorriso->re==NULL) {ret= -1; goto ex;} xorriso->re_constants= TSOB_FELD(char *,count); if(xorriso->re_constants==NULL) {ret= -1; goto ex;} for(i= 0;i<count;i++) xorriso->re_constants[i]= NULL; xorriso->re_count= count; xorriso->re_fill= 0; /* loop over slash chunks*/; cpt= adr_start; xorriso->re_fill= 0; while(*cpt=='/') cpt++; for(i= 0;i<count;i++) { npt= strchr(cpt,'/'); if(npt==NULL) { if((int) strlen(cpt) >= adr_size) {ret= -1; goto ex;} strcpy(adr_part,cpt); } else { if(npt-cpt >= adr_size) {ret= -1; goto ex;} strncpy(adr_part,cpt,npt-cpt); adr_part[npt-cpt]= 0; } if(adr_part[0]==0) goto next_adr_part; if(adr_part[0] == '.' && adr_part[1] == 0) goto next_adr_part; if(adr_part[0]=='.' && adr_part[1]=='.' && adr_part[2]==0) { /* delete previous part */ if(xorriso->re_fill <= 0) { bonked= 1; goto next_adr_part; } if(xorriso->re_constants[xorriso->re_fill-1]!=NULL) { free(xorriso->re_constants[xorriso->re_fill-1]); xorriso->re_constants[xorriso->re_fill-1]= NULL; } else regfree(&(xorriso->re[xorriso->re_fill-1])); (xorriso->re_fill)--; goto next_adr_part; } if(strcmp(adr_part,"*")==0) { adr_part[0]= 0; ret= 2; } else ret= Xorriso__bourne_to_reg(adr_part,xorriso->reg_expr,0); if(ret==2) { if(Sregex_string(&(xorriso->re_constants[xorriso->re_fill]),adr_part,0) <=0) {ret= -1; goto ex;} } else { if(regcomp(&(xorriso->re[xorriso->re_fill]),xorriso->reg_expr,0)!=0) goto cannot_compile; } xorriso->re_fill++; next_adr_part:; if(i==count-1) break; cpt= npt+1; while(*cpt=='/') cpt++; } if(bonked) { if(flag&2) {ret= 2; goto ex;} sprintf(xorriso->info_text, "Your '..' bonked at the %s directory.", is_still_relative ? "working" : "root"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE",0); {ret= 0; goto ex;} } if(xorriso->re_fill == 0 && is_still_relative) { /* "." and its equivalents end up here */ if(Sregex_string(&(xorriso->re_constants[0]), ".", 0) <=0) {ret= -1; goto ex;} xorriso->re_fill= 1; } Xorriso__bourne_to_reg(adr_start,xorriso->reg_expr,0); /* just for show */ } else { is_constant= 0; if(strcmp(adr,"*")==0 || adr[0]==0) { is_constant= 1; } else if(xorriso->search_mode==3 || xorriso->search_mode==4) { ret= Xorriso__bourne_to_reg(adr,xorriso->reg_expr,0); is_constant= (ret==2); } else { if(strlen(adr)>=sizeof(xorriso->reg_expr)) {ret= -1; goto ex;} strcpy(xorriso->reg_expr,adr); } xorriso->re_count= 0; /* tells matcher that this is not structured */ xorriso->re_constants= TSOB_FELD(char *,1); if(xorriso->re_constants==NULL) {ret= -1; goto ex;} xorriso->re_constants[0]= NULL; if(is_constant) { if(strcmp(adr,"*")==0) { if(Sregex_string(&(xorriso->re_constants[0]),"",0)<=0) {ret= -1; goto ex;} } else { if(Sregex_string(&(xorriso->re_constants[0]),adr,0)<=0) {ret= -1; goto ex;} } xorriso->re_fill= 1; } else { xorriso->re= TSOB_FELD(regex_t,1); if(xorriso->re==NULL) {ret= -1; goto ex;} if(regcomp(&(xorriso->re[0]),xorriso->reg_expr,0)!=0) { cannot_compile:; sprintf(xorriso->info_text, "Cannot compile regular expression : %s", xorriso->reg_expr); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE",0); {ret= 0; goto ex;} } } } } ret= 1; ex:; Xorriso_free_meM(adr_part); Xorriso_free_meM(absolute_adr); return(ret); } /* @param flag bit0= do not shortcut last component of to_match bit1= consider match if regex matches parent of path bit2= retry beginning at failed last component @return 0=match , else no match */ int Xorriso_regexec(struct XorrisO *xorriso, char *to_match, int *failed_at, int flag) { int ret,i,re_start= 0,reg_nomatch= -1; char *cpt,*npt, *adr_part= NULL, *mpt; Xorriso_alloc_meM(adr_part, char, SfileadrL); reg_nomatch= REG_NOMATCH; *failed_at= 0; if(!(xorriso->structured_search && xorriso->re_count>0)) { if(xorriso->re_constants!=NULL) if(xorriso->re_constants[0]!=NULL) { if(xorriso->re_constants[0][0]==0) {ret= 0; goto ex;} if(strcmp(xorriso->re_constants[0],to_match)!=0) {ret= reg_nomatch; goto ex;} {ret= 0; goto ex;} } ret= regexec(&(xorriso->re[0]),to_match,1,xorriso->match,0); goto ex; } cpt= to_match; while(*cpt=='/') cpt++; if(flag&4) re_start= xorriso->re_failed_at; if(re_start<0) re_start= 0; for(i= re_start;i<xorriso->re_fill;i++) { *failed_at= i; npt= strchr(cpt,'/'); if(npt==NULL) { if(i<xorriso->re_fill-1 && !(flag&1)) {ret= reg_nomatch; goto ex;} /* this must be the last expression part */ mpt= cpt; } else { strncpy(adr_part,cpt,npt-cpt); adr_part[npt-cpt]= 0; mpt= adr_part; } if(xorriso->re_constants[i]!=NULL) { if(xorriso->re_constants[i][0]!=0) /* empty constant matches anything */ if(strcmp(xorriso->re_constants[i],mpt)!=0) {ret= reg_nomatch; goto ex;} } else { ret= regexec(&(xorriso->re[i]),mpt,1,xorriso->match,0); if(ret!=0) goto ex; } if(npt==NULL) { if(i>=xorriso->re_fill-1) {ret= 0; goto ex;} /* MATCH */ *failed_at= i+1; {ret= reg_nomatch; goto ex;} } cpt= npt+1; while(*cpt=='/') cpt++; } *failed_at= xorriso->re_fill; if(flag & 2) {ret= 0; goto ex;} /* MATCH */ ret= reg_nomatch; ex:; Xorriso_free_meM(adr_part); return(ret); } int Xorriso_is_in_patternlist(struct XorrisO *xorriso, struct Xorriso_lsT *patternlist, char *path, int flag) { int ret, failed_at, i= 0; struct Xorriso_lsT *s; xorriso->search_mode= 3; xorriso->structured_search= 1; for(s= patternlist; s != NULL; s= Xorriso_lst_get_next(s, 0)) { ret= Xorriso_prepare_regex(xorriso, Xorriso_lst_get_text(s, 0), 0); if(ret <= 0) return(-1); /* Match path or parent of path */ ret= Xorriso_regexec(xorriso, path, &failed_at, 2); if(ret == 0) return(i + 1); i++; } return(0); } char *Xorriso_get_pattern(struct XorrisO *xorriso, struct Xorriso_lsT *patternlist, int index, int flag) { int i= 0; struct Xorriso_lsT *s; for(s= patternlist; s != NULL; s= Xorriso_lst_get_next(s, 0)) { if(i == index) return(Xorriso_lst_get_text(s, 0)); i++; } return(NULL); } /* @param flag bit2= this is a disk_pattern @return <=0 failure , 1 pattern ok , 2 pattern needed prepended wd */ int Xorriso_prepare_expansion_pattern(struct XorrisO *xorriso, char *pattern, int flag) { int ret, prepwd= 0; ret= Xorriso_prepare_regex(xorriso, pattern, 1|2|(flag&4)); if(ret==2) { ret= Xorriso_prepare_regex(xorriso, pattern, flag&4); prepwd= 1; } if(ret<=0) { sprintf(xorriso->info_text, "Cannot compile pattern to regular expression: %s", pattern); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1+prepwd); } /* @param flag bit0= count results rather than storing them bit1= unexpected change of number is a FATAL event @return <=0 error , 1 is root (end processing) , 2 is not root (go on processing) */ int Xorriso_check_for_root_pattern(struct XorrisO *xorriso, int *filec, char **filev, int count_limit, off_t *mem, int flag) { if(xorriso->re_fill!=0) return(2); /* This is the empty pattern representing root */ if(flag&1) { (*filec)++; (*mem)+= 8; } else { if(*filec >= count_limit) { sprintf(xorriso->info_text, "Number of matching files changed unexpectedly (> %d)", count_limit); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, (flag&2 ? "FATAL" : "WARNING"), 0); return(flag&2 ? -1 : 0); } filev[*filec]= strdup("/"); if(filev[*filec]==NULL) { Xorriso_no_pattern_memory(xorriso, (off_t) 2, 0); return(-1); } (*filec)++; } return(1); } /* @param flag bit0= count result rather than storing it bit1= unexpected change of number is a FATAL event */ int Xorriso_register_matched_adr(struct XorrisO *xorriso, char *adr, int count_limit, int *filec, char **filev, off_t *mem, int flag) { int l; if(flag&1) { (*filec)++; l= strlen(adr)+1; (*mem)+= sizeof(char *)+l; if(l % sizeof(char *)) (*mem)+= sizeof(char *)-(l % sizeof(char *)); } else { if(*filec >= count_limit) { sprintf(xorriso->info_text, "Number of matching files changed unexpectedly (> %d)", count_limit); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, (flag&2 ? "FATAL" : "WARNING"), 0); return(flag&2 ? -1 : 0); } filev[*filec]= strdup(adr); if(filev[*filec]==NULL) { Xorriso_no_pattern_memory(xorriso, (off_t) (strlen(adr)+1), 0); return(-1); } (*filec)++; } return(1); } /* @param flag bit0= count results rather than storing them bit1= this is a recursion bit2= prepend wd (automatically done if wd[0]!=0) @return <=0 error , 1 ok , 2 could not open directory */ int Xorriso_obtain_pattern_files_x( struct XorrisO *xorriso, char *wd, char *dir_adr, int *filec, char **filev, int count_limit, off_t *mem, int *dive_count, int flag) { int ret, failed_at, follow_mount, follow_links; struct DirseQ *dirseq= NULL; struct stat stbuf; dev_t dir_dev; char *path; char *adr= NULL, *name= NULL, *path_data= NULL; adr= malloc(SfileadrL); name= malloc(SfileadrL); path_data= malloc(SfileadrL); if(adr==NULL || name==NULL || path_data==NULL) { Xorriso_no_malloc_memory(xorriso, &adr, 0); {ret= -1; goto ex;} } follow_mount= (xorriso->do_follow_mount || xorriso->do_follow_pattern); follow_links= (xorriso->do_follow_links || xorriso->do_follow_pattern); if(!(flag&2)) *dive_count= 0; else (*dive_count)++; ret= Xorriso_check_for_root_pattern(xorriso, filec, filev, count_limit, mem, flag&1); if(ret!=2) goto ex; if(lstat(dir_adr, &stbuf)==-1) {ret= 2; goto ex;} dir_dev= stbuf.st_dev; if(S_ISLNK(stbuf.st_mode)) { if(stat(dir_adr, &stbuf)==-1) {ret= 2; goto ex;} if(dir_dev != stbuf.st_dev && !follow_mount) {ret= 2; goto ex;} } ret= Dirseq_new(&dirseq, dir_adr, 1); if(ret<0) { sprintf(xorriso->info_text, "Cannot obtain disk directory iterator"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } if(ret==0) {ret= 2; goto ex;} while(1) { ret= Dirseq_next_adr(dirseq,name,0); if(ret==0) break; if(ret<0) { sprintf(xorriso->info_text,"Failed to obtain next directory entry"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } ret= Xorriso_make_abs_adr(xorriso, wd, name, adr, flag&4); if(ret<=0) goto ex; ret= Xorriso_regexec(xorriso, adr, &failed_at, 1); if(ret>0) { /* no match */ if(failed_at <= *dive_count) /* no hope for a match */ continue; path= adr; if(adr[0]!='/') { path= path_data; ret= Xorriso_make_abs_adr(xorriso, xorriso->wdx, adr, path, 1|4); if(ret<=0) goto ex; } if(follow_links) ret= stat(path,&stbuf); else ret= lstat(path,&stbuf); if(ret==-1) continue; if(!S_ISDIR(stbuf.st_mode)) continue; if(dir_dev != stbuf.st_dev && !follow_mount) continue; /* dive deeper */ ret= Xorriso_obtain_pattern_files_x(xorriso, adr, path, filec, filev, count_limit, mem, dive_count, flag|2); if(ret<=0) goto ex; } else { ret= Xorriso_register_matched_adr(xorriso, adr, count_limit, filec, filev, mem, flag&1); if(ret<0) goto ex; if(ret==0) break; } } ret= 1; ex:; if(adr!=NULL) free(adr); if(name!=NULL) free(name); if(path_data!=NULL) free(path_data); Dirseq_destroy(&dirseq,0); if(flag&2) (*dive_count)--; return(ret); } int Xorriso_eval_nonmatch(struct XorrisO *xorriso, char *pattern, int *nonconst_mismatches, off_t *mem, int flag) { int k,l; /* Is this a constant pattern ? */ for(k= 0; k<xorriso->re_fill; k++) { if(xorriso->re_constants[k]==NULL) break; if(xorriso->re_constants[k][0]==0) break; } if(k<xorriso->re_fill) (*nonconst_mismatches)++; /* it is not */ l= strlen(pattern)+1; (*mem)+= sizeof(char *)+l; if(l % sizeof(char *)) (*mem)+= sizeof(char *)-(l % sizeof(char *)); return(1); } /* @param flag bit0= a match count !=1 is a SORRY event bit1= a match count !=1 is a FAILURE event */ int Xorriso_check_matchcount(struct XorrisO *xorriso, int count, int nonconst_mismatches, int num_patterns, char **patterns, int flag) { if((flag&1) && (count!=1 || nonconst_mismatches)){ if(count-nonconst_mismatches>0) sprintf(xorriso->info_text, "Pattern match with more than one file object"); else sprintf(xorriso->info_text, "No pattern match with any file object"); if(num_patterns==1) sprintf(xorriso->info_text+strlen(xorriso->info_text), ": "); Text_shellsafe(patterns[0], xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, (flag&2 ? "FAILURE" : "SORRY"), 0); return(0); } return(1); } int Xorriso_no_pattern_memory(struct XorrisO *xorriso, off_t mem, int flag) { char mem_text[80]; Sfile_scale((double) mem, mem_text,5,1e4,1); sprintf(xorriso->info_text, "Cannot allocate enough memory (%s) for pattern expansion", mem_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); return(1); } int Xorriso_alloc_pattern_mem(struct XorrisO *xorriso, off_t mem, int count, char ***filev, int flag) { char mem_text[80], limit_text[80]; Sfile_scale((double) mem, mem_text,5,1e4,0); sprintf(xorriso->info_text, "Temporary memory needed for pattern expansion : %s", mem_text); if(!(flag&1)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); if(mem > xorriso->temp_mem_limit) { Sfile_scale((double) xorriso->temp_mem_limit, limit_text,5,1e4,1); sprintf(xorriso->info_text, "List of matching file addresses exceeds -temp_mem_limit (%s > %s)", mem_text, limit_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } (*filev)= (char **) calloc(count, sizeof(char *)); if(*filev==NULL) { Xorriso_no_pattern_memory(xorriso, mem, 0); return(-1); } return(1); } /* @param flag bit0= a match count !=1 is a FAILURE event bit1= with bit0 tolerate 0 matches if pattern is a constant bit3= do not add unresolved pattern to filev */ int Xorriso_expand_disk_pattern(struct XorrisO *xorriso, int num_patterns, char **patterns, int extra_filec, int *filec, char ***filev, off_t *mem, int flag) { int ret, count= 0, abs_adr= 0, i, was_count, was_filec; int nonconst_mismatches= 0, dive_count= 0; char *dir_adr= NULL; Xorriso_alloc_meM(dir_adr, char, SfileadrL); *filec= 0; *filev= NULL; xorriso->search_mode= 3; xorriso->structured_search= 1; for(i= 0; i<num_patterns; i++) { abs_adr= 0; ret= Xorriso_prepare_expansion_pattern(xorriso, patterns[i], 4); if(ret<=0) goto ex; if(ret==2) abs_adr= 4; if(patterns[i][0]=='/' || abs_adr) { strcpy(dir_adr, "/"); abs_adr= 4; } else { strcpy(dir_adr, xorriso->wdx); if(dir_adr[0]==0) strcpy(dir_adr, "/"); ret= Sfile_type(dir_adr, 1|4); if(ret!=2) { Xorriso_msgs_submit(xorriso, 0, dir_adr, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "Address set by -cdx is not a directory: "); Text_shellsafe(dir_adr, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } /* count the matches */ was_count= count; ret= Xorriso_obtain_pattern_files_x(xorriso, "", dir_adr, &count, NULL, 0, mem, &dive_count, 1 | abs_adr); if(ret<=0) goto ex; if(was_count==count && strcmp(patterns[i],"*")!=0 && (flag&3)!=1 && !(flag & 8)) { count++; ret= Xorriso_eval_nonmatch(xorriso, patterns[i], &nonconst_mismatches, mem, 0); if(ret<=0) goto ex; } } ret= Xorriso_check_matchcount(xorriso, count, nonconst_mismatches, num_patterns, patterns, (flag&1)|2); if(ret<=0) goto ex; count+= extra_filec; (*mem)+= extra_filec * sizeof(char *); if(count<=0) {ret= !(flag & 8); goto ex;} ret= Xorriso_alloc_pattern_mem(xorriso, *mem, count, filev, 0); if(ret<=0) goto ex; /* now store addresses */ for(i= 0; i<num_patterns; i++) { abs_adr= 0; ret= Xorriso_prepare_expansion_pattern(xorriso, patterns[i], 4); if(ret<=0) goto ex; if(ret==2) abs_adr= 4; if(patterns[i][0]=='/' || abs_adr) { strcpy(dir_adr, "/"); abs_adr= 4; } else { strcpy(dir_adr, xorriso->wdx); if(dir_adr[0]==0) strcpy(dir_adr, "/"); } was_filec= *filec; ret= Xorriso_obtain_pattern_files_x(xorriso, "", dir_adr, filec, *filev, count, mem, &dive_count, abs_adr); if(ret<=0) goto ex; if(was_filec == *filec && strcmp(patterns[i],"*")!=0 && (flag & 3) != 1 && !(flag & 8)) { (*filev)[*filec]= strdup(patterns[i]); if((*filev)[*filec]==NULL) { (*mem)= strlen(patterns[i])+1; Xorriso_no_pattern_memory(xorriso, *mem, 0); ret= -1; goto ex; } (*filec)++; } } ret= 1; ex:; if(ret<=0) { if(filev!=NULL) Sfile_destroy_argv(&count, filev, 0); *filec= 0; } Xorriso_free_meM(dir_adr); return(ret); } /* @param flag bit0= command without pattern capability bit1= disk_pattern rather than iso_rr_pattern */ int Xorriso_warn_of_wildcards(struct XorrisO *xorriso, char *path, int flag) { static int count_iso= 0, count_disk= 0, max_iso= 3, max_disk= 3; if(strchr(path,'*')!=NULL || strchr(path,'?')!=NULL || strchr(path,'[')!=NULL) { if(flag & 2) { count_disk++; if(count_disk > max_disk) return(1); } else { count_iso++; if(count_iso > max_iso) return(1); } if(flag&1) { sprintf(xorriso->info_text, "Pattern expansion of wildcards \"*?[\" does not apply to this command"); } else { sprintf(xorriso->info_text, "Pattern expansion of wildcards \"*?[\" is disabled by command %s", (flag&2) ? "-disk_pattern or -pathspecs" : "-iso_rr_pattern"); } Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); sprintf(xorriso->info_text,"Pattern seen: "); Text_shellsafe(path, xorriso->info_text, 1); strcat(xorriso->info_text, "\n"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); return(1); } return(0); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2010 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of functions for pattern matching. */ #ifndef Xorriso_pvt_match_includeD #define Xorriso_pvt_match_includeD yes int Xorriso_prepare_regex(struct XorrisO *xorriso, char *adr, int flag); /* @return 0=match , else no match */ int Xorriso_regexec(struct XorrisO *xorriso, char *to_match, int *failed_at, int flag); int Xorriso_is_in_patternlist(struct XorrisO *xorriso, struct Xorriso_lsT *patternlist, char *path, int flag); char *Xorriso_get_pattern(struct XorrisO *xorriso, struct Xorriso_lsT *patternlist, int index, int flag); int Xorriso_prepare_expansion_pattern(struct XorrisO *xorriso, char *pattern, int flag); /* @param flag bit0= count results rather than storing them @return <=0 error , 1 is root (end processing) , 2 is not root (go on processing) */ int Xorriso_check_for_root_pattern(struct XorrisO *xorriso, int *filec, char **filev, int count_limit, off_t *mem, int flag); /* @param flag bit0= count result rather than storing it bit1= unexpected change of number is a FATAL event */ int Xorriso_register_matched_adr(struct XorrisO *xorriso, char *adr, int count_limit, int *filec, char **filev, off_t *mem, int flag); int Xorriso_eval_nonmatch(struct XorrisO *xorriso, char *pattern, int *nonconst_mismatches, off_t *mem, int flag); /* @param flag bit0= a match count !=1 is a SORRY event */ int Xorriso_check_matchcount(struct XorrisO *xorriso, int count, int nonconst_mismatches, int num_patterns, char **patterns, int flag); int Xorriso_no_pattern_memory(struct XorrisO *xorriso, off_t mem, int flag); int Xorriso_alloc_pattern_mem(struct XorrisO *xorriso, off_t mem, int count, char ***filev, int flag); /* @param flag bit0= command without pattern capability bit1= disk_pattern rather than iso_rr_pattern */ int Xorriso_warn_of_wildcards(struct XorrisO *xorriso, char *path, int flag); /* @param flag bit0= a match count !=1 is a FAILURE event bit1= with bit0 tolerate 0 matches if pattern is a constant */ int Xorriso_expand_disk_pattern(struct XorrisO *xorriso, int num_patterns, char **patterns, int extra_filec, int *filec, char ***filev, off_t *mem, int flag); #endif /* ! Xorriso_pvt_match_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the miscellaneous helper functions of xorriso. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <sys/utsname.h> #include "sfile.h" #include "misc_funct.h" /* --------------------------------- misc --------------------------------- */ int Strcmp(const void *pt1, const void *pt2) { return(strcmp(*((char **) pt1), *((char **) pt2))); } int Sort_argv(int argc, char **argv, int flag) { if(argc<=0) return(2); qsort(argv,(size_t) argc,sizeof(char *),Strcmp); return(1); } static int Text_to_argv(char *text, int *argc, char ***argv, int flag) { char *npt, *cpt; int pass; *argv= NULL; *argc= 0; for(pass= 0; pass < 2; pass++) { if(pass) { if(*argc == 0) return(1); (*argv)= calloc(*argc, sizeof(char *)); if(*argv == NULL) { *argc= 0; return(-1); } *argc= 0; } for(npt= cpt= text; npt != NULL; cpt= npt + 1) { npt= strchr(cpt, '\n'); if(pass) { if(npt != NULL) *npt= 0; (*argv)[*argc]= cpt; } (*argc)++; } } return(1); } static int Count_diffs(int argc1, char **argv1, int argc2, char **argv2, int flag) { int count= 0, i1= 0, i2= 0, cmp, end_corr= 0; Sort_argv(argc1, argv1, 0); Sort_argv(argc2, argv2, 0); while(1) { if(i1 >= argc1) { count+= argc2 - i2 - end_corr; break; } if(i2 >= argc2) { count+= argc1 - i1 - end_corr; break; } cmp= strcmp(argv1[i1], argv2[i2]); if(cmp == 0) { end_corr= 0; i1++; i2++; } else if(cmp > 0) { count++; end_corr= 1; i2++; if(i2 < argc2 && i1 < argc1 - 1) if(strcmp(argv1[i1 + 1], argv2[i2]) == 0) { i1++; end_corr= 0; } } else { count++; end_corr= 1; i1++; if(i1 < argc1 && i2 < argc2 - 1) if(strcmp(argv2[i2 + 1], argv1[i1]) == 0) { i2++; end_corr= 0; } } } return(count); } /* @flag bit0= do not initialize *diff_count @return <0 error , 0 = mismatch , 1 = match */ int Compare_text_lines(char *text1, char *text2, int *diff_count, int flag) { int ret, argc1= 0, argc2= 0; char **argv1= NULL, **argv2= NULL, *copy1= NULL, *copy2= NULL; if(!(flag & 1)) *diff_count= 0; if(text1 == NULL && text2 == NULL) return(1); if(text1 != NULL) { copy1= strdup(text1); if(copy1 == NULL) {ret= -1; goto ex;} ret= Text_to_argv(copy1, &argc1, &argv1, 0); if(ret <= 0) {ret= -1; goto ex;} } if(text2 != NULL) { copy2= strdup(text2); if(copy2 == NULL) {ret= -1; goto ex;} ret= Text_to_argv(copy2, &argc2, &argv2, 0); if(ret <= 0) {ret= -1; goto ex;} } ret= Count_diffs(argc1, argv1, argc2, argv2, 1); if(ret < 0) goto ex; *diff_count+= ret; ret= (*diff_count == 0); ex:; if(argv1 != NULL) free(argv1); if(argv2 != NULL) free(argv2); if(copy1 != NULL) free(copy1); if(copy2 != NULL) free(copy2); return ret; } /** Convert a text into a number of type double and multiply it by unit code [kmgtpe] (2^10 to 2^60) or [s] (2048) or [d] (512). (Also accepts capital letters.) @param text Input like "42", "2k", "3.14m" or "-1g" @param flag Bitfield for control purposes: bit0= return -1 rather than 0 on failure @return The derived double value */ double Scanf_io_size(char *text, int flag) /* bit0= default value -1 rather than 0 */ { int c; double ret= 0.0; if(flag&1) ret= -1.0; if(text[0]==0) return(ret); sscanf(text,"%lf",&ret); c= text[strlen(text)-1]; if(c=='k' || c=='K') ret*= 1024.0; if(c=='m' || c=='M') ret*= 1024.0*1024.0; if(c=='g' || c=='G') ret*= 1024.0*1024.0*1024.0; if(c=='t' || c=='T') ret*= 1024.0*1024.0*1024.0*1024.0; if(c=='p' || c=='P') ret*= 1024.0*1024.0*1024.0*1024.0*1024.0; if(c=='e' || c=='E') ret*= 1024.0*1024.0*1024.0*1024.0*1024.0*1024.0; if(c=='s' || c=='S') ret*= 2048.0; if(c=='d' || c=='D') ret*= 512.0; return(ret); } int Decode_date_input_format(struct tm *erg, char *text, int flag) /* MMDDhhmm[[CC]YY][.ss]] */ { int i,l,year; time_t current_time; struct tm *now; current_time= time(0); now= localtime(¤t_time); for(i= 0; i < (int) sizeof(struct tm); i++) ((char *) erg)[i]= ((char *) now)[i]; l= strlen(text); for(i=0;i<l;i++) if(text[i]<'0'||text[i]>'9') break; if(i!=8 && i!=10 && i!=12) return(0); if(text[i]==0) goto decode; if(text[i]!='.' || l!=15) return(0); i++; if(text[i]<'0'||text[i]>'9') return(0); i++; if(text[i]<'0'||text[i]>'9') return(0); decode:; /* MMDDhhmm[[CC]YY][.ss]] */ i= 0; erg->tm_mon= 10*(text[0]-'0')+text[1]-'0'-1; erg->tm_mday= 10*(text[2]-'0')+text[3]-'0'; erg->tm_hour= 10*(text[4]-'0')+text[5]-'0'; erg->tm_min= 10*(text[6]-'0')+text[7]-'0'; erg->tm_sec= 0; if(l==8) return(1); if(l>10){ year= 1000*(text[8]-'0')+100*(text[9]-'0')+10*(text[10]-'0')+(text[11]-'0'); }else{ year= 1900+10*(text[8]-'0')+(text[9]-'0'); if(year<1970) year+= 100; } erg->tm_year= year-1900; if(l<=12) return(1); erg->tm_sec= 10*(text[13]-'0')+text[14]-'0'; return(1); } int Decode_date_weekday(char *text, int flag) { int i; static char days[][4]= {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", ""}; for(i= 0; days[i][0]!=0; i++) if(strncmp(text,days[i],3)==0) return(i); if((strlen(text)==3 || (strlen(text)==4 && text[3]==',')) && isalpha(text[0]) && isalpha(text[1]) && isalpha(text[2])) return(7); return(-1); } int Decode_date_month(char *text, int flag) { int i; static char months[][4]= {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ""}; for(i= 0; months[i][0]!=0; i++) if(strncmp(text,months[i],3)==0) return(i); return(-1); } /* @return -1=not a number, -2=not a day , 1 to 31 day of month */ int Decode_date_mday(char *text, int flag) { int ret, i; for(i= 0; text[i]!=0; i++) if(!isdigit(text[i])) return(-1); if(strlen(text)>2 || text[0]==0) return(-2); sscanf(text, "%d", &ret); if(ret<=0 || ret>31) return(-2); return(ret); } int Decode_date_hms(char *text, struct tm *erg, int flag) { int i, hour= -1, minute= -1, second= 0; for(i= 0; i<9; i+= 3) { if(i==6&&text[i]==0) break; if(!isdigit(text[i])) return(-1); if(!isdigit(text[i+1])) return(-1); if(text[i+2]!=':' && !(text[i+2]==0 && i>=3)) return(-1); if(i==0) sscanf(text+i,"%d",&hour); else if(i==3) sscanf(text+i,"%d",&minute); else sscanf(text+i,"%d",&second); } if(hour<0 || hour>23 || minute<0 || minute>59 || second>59) return(-1); erg->tm_hour= hour; erg->tm_min= minute; erg->tm_sec= second; return(1); } /* @return -1=not a number, -2=not a year , >=0 years AD */ int Decode_date_year(char *text, int flag) { int ret, i; for(i= 0; text[i]!=0; i++) if(!isdigit(text[i])) return(-1); if(strlen(text)!=4) return(-2); sscanf(text, "%d", &ret); if(ret<0 || ret>3000) return(-2); return(ret); } int Decode_date_timezone(char *text, struct tm *erg, int flag) { int i; static char tzs[][5]= {"GMT", "CET", "CEST", "0000", ""}; for(i= 0; tzs[i][0]!=0; i++) if(strcmp(text,tzs[i])==0) { /* ??? >>> what to do with timezone info ? Add to ->tm_hour ? */ return(1); } if(text[0]=='+' || text[0]=='-') { for(i= 1; text[i]!=0; i++) if(!isdigit(text[i])) return(-1); if(i!=5) return(-1); /* ??? >>> what to do with timezone info ? Add to ->tm_hour ? */ return(1); } else { for(i= 0; text[i]!=0; i++) if(text[i]<'A' || text[i]>'Z') return(-1); if(i!=3 && i!=4) return(-1); return(2); } } int Decode_date_output_format(struct tm *erg, char *text, int flag) /* Thu Nov 8 09:07:50 CET 2007 */ /* Sat, 03 Nov 2007 08:58:30 +0100 */ /* Nov 7 23:24 */ { int ret, i, argc= 0, seen_year= 0, seen_month= 0, seen_day= 0, seen_time= 0; char **argv= NULL; struct tm *now; time_t timep; memset(erg, 0, sizeof(*erg)); erg->tm_isdst= -1; ret= Sfile_make_argv("xorriso", text, &argc, &argv, 0); if(ret<=0) goto ex; for(i= 1; i<argc; i++) { if(!seen_month) { ret= Decode_date_month(argv[i], 0); if(ret>=0) { seen_month= 1; erg->tm_mon= ret; continue; } } if(!seen_day) { ret= Decode_date_mday(argv[i], 0); if(ret>0) { seen_day= 1; erg->tm_mday= ret; continue; } if(ret==-2) /* first pure number must be day of month */ {ret= 0; goto ex;} } if(!seen_time) { ret= Decode_date_hms(argv[i], erg, 0); if(ret>0) { seen_time= 1; continue; } } if(!seen_year) { ret= Decode_date_year(argv[i], 0); if(ret>0) { erg->tm_year= ret-1900; seen_year= 1; continue; } } /* ignorants have to stay at the end of the loop */ ret= Decode_date_timezone(argv[i], erg, 0); if(ret>=0) continue; ret= Decode_date_weekday(argv[i], 0); if(ret>=0) continue; /* ignore weekdays */ {ret= 0; goto ex;} /* unrecognizable component */ } if(!(seen_day && seen_month)) {ret= 0; goto ex;} if(!seen_year) { /* then use this year */ timep= time(NULL); now= localtime(&timep); erg->tm_year= now->tm_year; } ret= 1; ex: Sfile_make_argv("", "", &argc, &argv, 2); /* release storage */ return(ret); } int Decode_ecma119_format(struct tm *erg, char *text, int flag) /* YYYYMMDDhhmmsscc[LOC] */ /* 2010040711405800 */ { int i, l, num, utc= 1; struct tm norm_tm; memset(erg, 0, sizeof(*erg)); erg->tm_isdst= -1; l= strlen(text); if(l == 19) { if(strcmp(text + 16, "LOC") != 0) return(0); utc= 0; l= 16; } if(l != 16) return(0); for(i= 0; i < l; i++) if(text[i] < '0' || text[i] > '9') return(0); num= 0; for(i= 0; i < 4; i++) num= num * 10 + text[i] - '0'; if(num < 1970 || num > 3000) return(0); erg->tm_year = num - 1900; erg->tm_mon= 10*(text[4]-'0')+text[5]-'0'-1; if(erg->tm_mon > 12) return(0); erg->tm_mday= 10*(text[6]-'0')+text[7]-'0'; if(erg->tm_mday > 31) return(0); erg->tm_hour= 10*(text[8]-'0')+text[9]-'0'; if(erg->tm_hour > 23) return(0); erg->tm_min= 10*(text[10]-'0')+text[11]-'0'; if(erg->tm_min > 59) return(0); erg->tm_sec= 10*(text[12]-'0')+text[13]-'0'; if(erg->tm_sec > 59) return(0); /* Let mktime(3) compute erg->tm_wday and erg->tm_yday */ memcpy(&norm_tm, erg, sizeof(struct tm)); mktime(&norm_tm); erg->tm_wday= norm_tm.tm_wday; erg->tm_yday= norm_tm.tm_yday; return(1 + !utc); } int Decode_xorriso_timestamp(struct tm *erg, char *code, int flag) /* 2007.11.07.225624 or 2007_11_07_225624 */ { char buf[20]; int year,month,day,hour= 0,minute= 0,second= 0, i, l, mem; memset(erg, 0, sizeof(*erg)); erg->tm_isdst= -1; l= strlen(code); if(l>17 || l<10) return(0); strcpy(buf, code); for(i= 0; buf[i]!=0 && i<4; i++) if(!isdigit(buf[i])) return(0); if(buf[4] != '.' && buf[4] != '_') return(0); buf[4]= 0; sscanf(buf, "%d", &year); if(year<1900 || year>3000) return(0); if(!(isdigit(buf[5]) && isdigit(buf[6]) && (buf[7] == '.' || buf[7] == '_'))) return(0); buf[7]= 0; sscanf(buf+5, "%d", &month); if(month<1 || month>12) return(0); if(!(isdigit(buf[8]) && isdigit(buf[9]) && (buf[10]=='.' || buf[10]=='_' || buf[10]==0))) return(0); buf[10]= 0; sscanf(buf+8, "%d", &day); if(day<1 || day>31) return(0); if(l==10) goto done; if(!(isdigit(buf[11]) && isdigit(buf[12]) && (isdigit(buf[13]) || buf[13]==0))) return(0); mem= buf[13]; buf[13]= 0; sscanf(buf+11, "%d", &hour); buf[13]= mem; if(hour<0 || hour>23) return(0); if(l==13) goto done; if(!(isdigit(buf[13]) && isdigit(buf[14]) && (isdigit(buf[15]) || buf[15]==0))) return(0); mem= buf[15]; buf[15]= 0; sscanf(buf+13, "%d", &minute); buf[15]= mem; if(minute<0 || minute>59) return(0); if(l==15) goto done; if(!(isdigit(buf[15]) && isdigit(buf[16]) && buf[17]==0)) return(0); sscanf(buf+15, "%d", &second); if(second<0 || second>59) return(0); done:; erg->tm_year= year-1900; erg->tm_mon= month-1; erg->tm_mday= day; erg->tm_hour= hour; erg->tm_min= minute; erg->tm_sec= second; return(1); } int Decode_timestring(char *code, time_t *date, int flag) { char scale_chr; double value,seconds; struct tm result_tm; int seconds_valid= 0, ret; *date= 0; if(code[0]=='-' || code[0]=='+' || code[0]=='=' || code[0]=='@'){ if(code[1]==0) return(0); if(!isdigit(code[1])) return(0); value= -1; if(code[0]=='=' || code[0]=='@') { seconds= 0; sscanf(code+1,"%lf",&value); } else { seconds= time(NULL); sscanf(code,"%lf",&value); } scale_chr= code[strlen(code)-1]; if(isalpha(scale_chr)) scale_chr= tolower(scale_chr); if (scale_chr=='s') seconds+= 1.0*value; else if(scale_chr=='h') seconds+= 3600.0*value; else if(scale_chr=='d') seconds+= 86400.0*value; else if(scale_chr=='w') seconds+= 86400.0*7.0*value; else if(scale_chr=='m') seconds+= 86400.0*31.0*value; else if(scale_chr=='y') seconds+= 86400.0*(365.25*value+1.0); else seconds+= 1.0*value; seconds_valid= 1; goto completed; } else if(Sfile_decode_datestr(&result_tm,code,0)>0) { /* YYMMDD[.hhmm[ss]] */ result_tm.tm_isdst= -1; seconds= mktime(&result_tm); seconds_valid= 1; goto completed; } else if(Decode_date_input_format(&result_tm,code,0)>0) { /* MMDDhhmm[[CC]YY][.ss]] */ result_tm.tm_isdst= -1; seconds= mktime(&result_tm); seconds_valid= 1; goto completed; } else if(Decode_xorriso_timestamp(&result_tm, code, 0)>0) { /* 2007.11.07.225624 */ seconds= mktime(&result_tm); seconds_valid= 1; goto completed; } else if(Decode_date_output_format(&result_tm, code, 0)>0) { /* Thu Nov 8 09:07:50 CET 2007 */; /* Sat, 03 Nov 2007 08:58:30 +0100 */; /* Nov 7 23:24 */; seconds= mktime(&result_tm); seconds_valid= 1; goto completed; } else if((ret= Decode_ecma119_format(&result_tm, code, 0)) > 0) { /* YYYYMMDDhhmmsscc[LOC] */ /* 2010040711405800LOC */ seconds= mktime(&result_tm); if(ret == 1) { #ifdef HAVE_TM_GMTOFF seconds+= result_tm.tm_gmtoff; #else if(result_tm.tm_isdst < 0) result_tm.tm_isdst = 0; #ifndef Libburnia_timezonE #define Libburnia_timezonE timezone #endif seconds-= Libburnia_timezonE - result_tm.tm_isdst * 3600; #endif } seconds_valid= 1; goto completed; } return(0); completed:; if(!seconds_valid) return(0); *date= seconds; return(1); } int Decode_ecma119_17byte(time_t *seconds, char *code, int flag) /* YYYYMMDDhhmmssccN (N = 15 minutes Offset from GMT: -48 West to +52 east) */ { int ret; char digits[17]; struct tm erg; *seconds= 0; strncpy(digits, code, 16); digits[16]= 0; ret= Decode_ecma119_format(&erg, digits, 0); if(ret <= 0) return(ret); *seconds= mktime(&erg); *seconds-= (int) code[16] * 60 * 15; /* Convert from local time to GMT */ #ifdef HAVE_TM_GMTOFF *seconds+= erg.tm_gmtoff; #else if(erg.tm_isdst < 0) erg.tm_isdst = 0; #ifndef Libburnia_timezonE #define Libburnia_timezonE timezone #endif *seconds-= Libburnia_timezonE - erg.tm_isdst * 3600; #endif return(1); } int Encode_ecma119_17byte(time_t seconds, char *centi_seconds, char *code, int flag) { struct tm *gmt; gmt= gmtime(&seconds); if(gmt == NULL) return(0); sprintf(code, "%4.4d", 1900 + gmt->tm_year); sprintf(code + 4, "%2.2d", gmt->tm_mon + 1); sprintf(code + 6, "%2.2d", gmt->tm_mday); sprintf(code + 8, "%2.2d", gmt->tm_hour); sprintf(code + 10, "%2.2d", gmt->tm_min); sprintf(code + 12, "%2.2d", gmt->tm_sec); strncpy(code + 14, centi_seconds, 2); code[16]= code[17]= 0; return(1); } /* flag: bit0= format to xorriso timestamp in local time bit1= truncate centiseconds (with !bit0) */ int Untimezone_ecma119_17byte(char *code, int flag) { int ret; time_t seconds; char new_code[18]; if(code[16] == 0 && !(flag & 1)) return(1); ret= Decode_ecma119_17byte(&seconds, code, 0); if(ret <= 0) return(ret); if(flag & 1) { /* xorriso timestamp format in local time */ if(Ftimetxt(seconds, new_code, 1 | 2) == NULL) return(0); } else { /* ECMA-119 17 byte format in GMT */ ret= Encode_ecma119_17byte(seconds, flag & 2 ? "00" : code + 14, new_code, 0); if(ret <= 0) return(ret); } strcpy(code, new_code); return(1); } /* @param flag bit0=with year and seconds bit1-3= form 0= ls -l format 1= timestamp format YYYY.MM.DD.hhmmss 2= Wdy Mon Day hh:mm:ss Year 3= Mon Day hh:mm:ss Year 4= YYMMDD.hhmmss */ char *Ftimetxt(time_t t, char *timetext, int flag) { char *rpt; struct tm tms, *tmpt; static char months[12][4]= { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; static char days[7][4]= {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; int form; form= (flag>>1)&7; tmpt= localtime_r(&t, &tms); rpt= timetext; rpt[0]= 0; if(tmpt==0) sprintf(rpt+strlen(rpt), "%12.f", (double) t); else if (form==1) sprintf(rpt+strlen(rpt), "%4.4d.%2.2d.%2.2d.%2.2d%2.2d%2.2d", 1900+tms.tm_year, tms.tm_mon+1, tms.tm_mday, tms.tm_hour, tms.tm_min, tms.tm_sec); else if (form==2) sprintf(rpt+strlen(rpt), "%s %s %2.2d %2.2d:%2.2d:%2.2d %4.4d", days[tms.tm_wday], months[tms.tm_mon], tms.tm_mday, tms.tm_hour, tms.tm_min, tms.tm_sec, 1900+tms.tm_year); else if (form==3) sprintf(rpt+strlen(rpt), "%s %2.2d %2.2d:%2.2d:%2.2d %4.4d", months[tms.tm_mon], tms.tm_mday, tms.tm_hour, tms.tm_min, tms.tm_sec, 1900+tms.tm_year); else if (form == 4) { if(tms.tm_year>99) sprintf(rpt+strlen(rpt), "%c", 'A' + (tms.tm_year - 100) / 10); else sprintf(rpt+strlen(rpt), "%c", '0' + tms.tm_year / 10); sprintf(rpt+strlen(rpt), "%1.1d%2.2d%2.2d.%2.2d%2.2d%2.2d", tms.tm_year % 10, tms.tm_mon + 1, tms.tm_mday, tms.tm_hour, tms.tm_min, tms.tm_sec); } else if (flag&1) sprintf(rpt+strlen(rpt), "%2d %3s %4.4d %2.2d:%2.2d:%2.2d", tms.tm_mday, months[tms.tm_mon], 1900+tms.tm_year, tms.tm_hour, tms.tm_min, tms.tm_sec); else if(time(NULL)-t < 180*86400 && time(NULL)-t >= 0) sprintf(rpt+strlen(rpt), "%3s %2d %2.2d:%2.2d", months[tms.tm_mon], tms.tm_mday, tms.tm_hour, tms.tm_min); else sprintf(rpt+strlen(rpt), "%3s %2d %4.4d", months[tms.tm_mon], tms.tm_mday, 1900+tms.tm_year); return(timetext); } /* @param flag bit0= single letters */ char *Ftypetxt(mode_t st_mode, int flag) { if(flag&1) goto single_letters; if(S_ISDIR(st_mode)) return("directory"); else if(S_ISREG(st_mode)) return("regular_file"); else if(S_ISLNK(st_mode)) return("symbolic_link"); else if(S_ISBLK(st_mode)) return("block_device"); else if(S_ISCHR(st_mode)) return("char_device"); else if(S_ISFIFO(st_mode)) return("name_pipe"); else if(S_ISSOCK(st_mode)) return("unix_socket"); return("unknown"); single_letters:; if(S_ISDIR(st_mode)) return("d"); else if(S_ISREG(st_mode)) return("-"); else if(S_ISLNK(st_mode)) return("l"); else if(S_ISBLK(st_mode)) return("b"); else if(S_ISCHR(st_mode)) return("c"); else if(S_ISFIFO(st_mode)) return("p"); else if(S_ISSOCK(st_mode)) return("s"); return("?"); } int Wait_for_input(int fd, int microsec, int flag) { struct timeval wt; fd_set rds,wts,exs; int ready; FD_ZERO(&rds); FD_ZERO(&wts); FD_ZERO(&exs); FD_SET(fd,&rds); FD_SET(fd,&exs); wt.tv_sec= microsec/1000000; wt.tv_usec= microsec%1000000; ready= select(fd+1,&rds,&wts,&exs,&wt); if(ready<=0) return(0); if(FD_ISSET(fd,&exs)) return(-1); if(FD_ISSET(fd,&rds)) return(1); return(0); } int System_uname(char **sysname, char **release, char **version, char **machine, int flag) { int ret; static struct utsname uts; static int initialized= 0; if(initialized == 0) { ret= uname(&uts); if(ret != 0) initialized = -1; } if(initialized == -1) return(0); if(sysname != NULL) *sysname= uts.sysname; if(release != NULL) *release= uts.release; if(version != NULL) *version= uts.version; if(machine != NULL) *machine= uts.machine; return(1); } /* ------------------------------------------------------------------------ */ #ifndef Xorriso_sregex_externaL #ifndef Smem_malloC #define Smem_malloC malloc #endif #ifndef Smem_freE #define Smem_freE free #endif int Sregex_string_cut(char **handle, char *text, int len, int flag) /* bit0= append (text!=NULL) */ { int l=0; char *old_handle; if((flag&1)&&*handle!=NULL) l+= strlen(*handle); old_handle= *handle; if(text!=NULL) { l+= len; *handle= TSOB_FELD(char,l+1); if(*handle==NULL) { *handle= old_handle; return(0); } if((flag&1) && old_handle!=NULL) strcpy(*handle,old_handle); else (*handle)[0]= 0; if(len>0) strncat(*handle,text,len); } else { *handle= NULL; } if(old_handle!=NULL) Smem_freE(old_handle); return(1); } int Sregex_string(char **handle, char *text, int flag) /* bit0= append (text!=NULL) */ { int ret,l=0; if(text!=NULL) l= strlen(text); /* #define Sregex_looking_for_contenT 1 */ #ifdef Sregex_looking_for_contenT /* a debugging point if a certain text content has to be caught */ if(text!=NULL) if(strcmp(text,"clear")==0) ret= 0; #endif ret= Sregex_string_cut(handle,text,l,flag&1); return(ret); } /* vars[][0] points to the variable names, vars[][1] to their contents. start marks the begin of variable names. It must be non-empty. esc before start disables this meaning. start and esc may be equal but else they must have disjoint character sets. end marks the end of a variable name. It may be empty but if non-empty it must not appear in vars[][0]. @param flag bit0= Substitute unknown variables by empty text (else copy start,name,end unaltered to result). Parameter end must be non-empty for that. */ int Sregex_resolve_var(char *form, char *vars[][2], int num_vars, char *start, char *end, char *esc, char *result, int result_size, int flag) { int l_e, l_s, l_esc, i, start_equals_esc; char *rpt, *wpt, *spt, *npt, *ept; if(start[0] == 0) /* It is not allowed to have no start marker */ return(-1); l_s= strlen(start); l_e= strlen(end); l_esc= strlen(esc); start_equals_esc= !strcmp(start, esc); rpt= form; wpt= result; wpt[0]= 0; while(1) { /* look for start mark */ spt= strstr(rpt, start); if(spt == NULL) { if((wpt - result) + (int) strlen(rpt) >= result_size) return(0); strcpy(wpt, rpt); wpt+= strlen(wpt); break; } /* copy cleartext part up to next variable */ if((wpt - result) + (spt - rpt) >= result_size) return(0); strncpy(wpt, rpt, spt - rpt); wpt+= spt - rpt; *wpt= 0; rpt= spt; npt= spt + l_s; /* handle eventual escape */ if(start_equals_esc) { if(strncmp(spt + l_s, esc, l_esc) == 0) { /* copy esc and start */ if((wpt - result) + l_esc + l_s >= result_size) return(0); strncpy(wpt, spt, l_esc + l_s); wpt+= l_esc + l_s; rpt+= l_esc + l_s; *wpt= 0; continue; } } else { /* escape would be already copied */ if(l_esc > 0 && spt - form >= l_esc) { if(strncmp(spt - l_esc, esc, l_esc) == 0) { /* copy start */ if((wpt - result) + l_s >= result_size) return(0); strncpy(wpt, spt, l_s); wpt+= l_s; rpt+= l_s; *wpt= 0; continue; } } } /* Memorize eventual end mark for default handling */; ept= NULL; if(l_e > 0) ept= strstr(npt, end); /* Look for defined variable name */ for(i = 0; i < num_vars; i++) { if(strncmp(npt, vars[i][0], strlen(vars[i][0])) == 0 && (l_e == 0 || strncmp(npt + strlen(vars[i][0]), end, l_e) == 0)) break; } if(i < num_vars) { /* substitute found variable */ if((wpt - result) + (int) strlen(vars[i][1]) >= result_size) return(0); strcpy(wpt, vars[i][1]); rpt= npt + strlen(vars[i][0]) + l_e; } else if((flag & 1) && ept != NULL) { /* skip up to end mark */ rpt= ept + l_e; } else if(ept != NULL) { /* copy start,name,end */ if((wpt - result) + (ept - rpt) + l_e >= result_size) return(0); strncpy(wpt, rpt, (ept - rpt) + l_e); rpt= ept + l_e; } else { /* copy start marker only */ if((wpt - result) + l_s >= result_size) return(0); strncpy(wpt, rpt, l_s); rpt= rpt + l_s; } wpt+= strlen(wpt); *wpt= 0; } return(1); } /* @param flag bit0= only test expression whether compilable */ int Sregex_match(char *pattern, char *text, int flag) { int ret; char *re_text= NULL; regex_t re; regmatch_t match[1]; re_text= TSOB_FELD(char, 2 * SfileadrL); if(re_text == NULL) {ret= -1; goto ex;} Xorriso__bourne_to_reg(pattern, re_text, 0); ret= regcomp(&re, re_text, 0); if(ret != 0) {ret= -1; goto ex;} if(flag & 1) { regfree(&re); {ret= 1; goto ex;} } ret= regexec(&re, text, 1, match, 0); regfree(&re); if(ret != 0) {ret= 0; goto ex;} ret= 1; ex:; if(re_text != NULL) free(re_text); return(ret); } #endif /* Xorriso_sregex_externaL */ /* @param flag bit0= append to out_text rather than overwrite it bit1= length limit is 10 * SfileadrL rather than 5 * */ char *Text_shellsafe(char *in_text, char *out_text, int flag) { int l,i,w=0, limit= 5 * SfileadrL; if(flag&1) w= strlen(out_text); if(flag & 2) limit= 10 * SfileadrL; /* enclose everything by hard quotes */ l= strlen(in_text); out_text[w++]= '\''; for(i=0;i<l;i++){ if(in_text[i]=='\''){ if(w + 7 > limit) goto overflow; /* escape hard quote within the text */ out_text[w++]= '\''; out_text[w++]= '"'; out_text[w++]= '\''; out_text[w++]= '"'; out_text[w++]= '\''; } else { if(w + 3 > limit) { overflow:; strcpy(out_text, "'xorriso: TEXT MUCH TOO LONG ... '"); return(out_text); } out_text[w++]= in_text[i]; } } out_text[w++]= '\''; out_text[w++]= 0; return(out_text); } int Hex_to_bin(char *hex, int bin_size, int *bin_count, unsigned char *bin_data, int flag) { int i, l, acc; l= strlen(hex); if(((l % 2) && l < 2 * bin_size) || l == 0) return(-1); /* malformed */ *bin_count= 0; for(i= 0; i < l; i+= 2) { if(*bin_count >= bin_size) return(0); /* overflow */ if(hex[i] >= '0' && hex[i] <= '9') acc= (hex[i] - '0') << 4; else if(hex[i] >= 'A' && hex[i] <= 'F') acc= (hex[i] - 'A' + 10) << 4; else if(hex[i] >= 'a' && hex[i] <= 'f') acc= (hex[i] - 'a' + 10) << 4; else return(-1); if(hex[i + 1] >= '0' && hex[i + 1] <= '9') acc|= (hex[i + 1] - '0'); else if(hex[i + 1] >= 'A' && hex[i + 1] <= 'F') acc|= (hex[i + 1] - 'A' + 10); else if(hex[i + 1] >= 'a' && hex[i + 1] <= 'f') acc|= (hex[i + 1] - 'a' + 10); else return(-1); bin_data[*bin_count]= acc; (*bin_count)++; } return(1); } #ifndef Xorriso_fileliste_externaL /* @param flag bit0= pathspec mode "on" rather than "as_mkisofs" */ int Fileliste__target_source_limit(char *line, char sep, char **limit_pt, int flag) { char *npt; for(npt= line;*npt!=0;npt++) { if(*npt=='\\') { if(*(npt + 1) == '=' || (*(npt + 1) == '\\' && !(flag & 1))) npt++; continue; } if(*npt=='=') break; } if(*npt==0) npt= NULL; (*limit_pt)= npt; return(npt!=NULL); } int Fileliste__escape_source_path(char *line, int size, int flag) { int l, count= 0, i; char *wpt; l= strlen(line); for(i= 0; i < l; i++) if(line[i] == '=' || line[i] == '\\') count++; if(l + count >= size) return(0); wpt= line + l + count; for(i= l; i >= 0; i--) { *(wpt--)= line[i]; if(line[i] == '=' || line[i] == '\\') *(wpt--)= '\\'; } return(1); } int Xorriso__bourne_to_reg(char bourne_expr[], char reg_expr[], int flag) /* reg_expr should be twice as large as bourne_expr ( + 2 to be exact) */ /* return: 2= bourne_expr is surely a constant */ { char *wpt,*lpt; int backslash= 0,is_constant= 1,in_square_brackets= 0; int first_in_square_brackets=0; wpt= reg_expr; lpt= bourne_expr; *(wpt++)= '^'; while(*lpt!=0){ if(first_in_square_brackets>0) first_in_square_brackets--; if(!backslash){ switch(*lpt){ case '?': *(wpt++)= '.'; is_constant= 0; break;case '*': *(wpt++)= '.'; *(wpt++)= '*'; is_constant= 0; break;case '.': *(wpt++)= '\\'; *(wpt++)= '.'; break;case '+': *(wpt++)= '\\'; *(wpt++)= '+'; break;case '[': *(wpt++)= *lpt; first_in_square_brackets= 2; in_square_brackets= 1; is_constant= 0; break;case ']': *(wpt++)= *lpt; in_square_brackets= 0; break;case '!': if(first_in_square_brackets) *(wpt++)= '^'; else if(in_square_brackets) *(wpt++)= '!'; else { *(wpt++)= '\\'; *(wpt++)= '!'; } break;case '^': if(in_square_brackets) { *(wpt++)= '^'; } else { *(wpt++)= '\\'; *(wpt++)= '^'; } break;case '$': *(wpt++)= '\\'; *(wpt++)= '$'; break;case '\\': backslash= 1; *(wpt++)= '\\'; is_constant= 0; break;default: *(wpt++)= *lpt; } } else { backslash= 0; *(wpt++)= *lpt; } lpt++; } *(wpt++)= '$'; *wpt= 0; return(1+(is_constant>0)); } #endif /* ! Xorriso_fileliste_externaL */ int Xorriso__hide_mode(char *mode, int flag) { char *npt, *cpt; int l, value= 0; npt= cpt= mode; for(; npt!=NULL; cpt= npt+1) { npt= strchr(cpt,':'); if(npt==NULL) l= strlen(cpt); else l= npt-cpt; if(l == 0) continue; if(l == 2 && strncmp(cpt, "on", l) == 0) value= 1 | 2 | 4; else if(l == 6 && strncmp(cpt, "iso_rr", l) == 0) value |= 1; else if(l == 6 && strncmp(cpt, "joliet", l) == 0) value |= 2; else if(l == 7 && strncmp(cpt, "hfsplus", l) == 0) value |= 4; else if(l == 3 && strncmp(cpt, "off", l) == 0) value= 0; else return(-1); } return(value); } char *Xorriso__hide_mode_text(int hide_mode, int flag) { char *acc= NULL; acc = calloc(1, 80); if(acc == NULL) return(NULL); acc[0]= 0; if(hide_mode == 0) { strcat(acc, "off:"); } else if(hide_mode == 7) { strcat(acc, "on:"); } else { if(hide_mode & 1) strcat(acc, "iso_rr:"); if(hide_mode & 2) strcat(acc, "joliet:"); if(hide_mode & 4) strcat(acc, "hfsplus:"); } if(acc[0]) acc[strlen(acc) - 1]= 0; /* cut off last colon */ return acc; } /* @return 0=truncated, 1=ok */ int Xorriso__to_upper(char *in, char *out, int out_size, int flag) { int i; for(i= 0; i < out_size - 1 && in[i] != 0; i++) if(isalpha(in[i])) out[i]= toupper(in[i]); else out[i]= in[i]; out[i]= 0; return(in[i] == 0); } /* @return 0=truncated, 1=ok */ int Xorriso__to_lower(char *in, char *out, int out_size, int flag) { int i; for(i= 0; i < out_size - 1 && in[i] != 0; i++) if(isalpha(in[i])) out[i]= tolower(in[i]); else out[i]= in[i]; out[i]= 0; return(in[i] == 0); } /* @param flag bit0= prepend target_prefix even if source_prefix does not get removed */ int Xorriso__exchange_prefix(char *source_prefix, char *target_prefix, char *eff_source, char *eff_target, int flag) { char *source_pt; strcpy(eff_target, target_prefix); source_pt= eff_source; if(source_prefix[0]) { if(strncmp(source_prefix, eff_source, strlen(source_prefix)) != 0) { if(!(flag & 1)) return(0); } else { source_pt+= strlen(source_prefix); } } strcat(eff_target, source_pt); return(1); } /* @param text takes result, must provide at least 37 characters of storage @param flag bit0= structured text format (else hex string) */ int Xorriso__format_guid(uint8_t guid[16], char *text, int flag) { int i; if(flag & 1) { /* Structured text */ text[0]= 0; for(i= 3; i >= 0; i--) sprintf(text + strlen(text), "%-2.2x", (unsigned int) guid[i]); sprintf(text + strlen(text), "-"); for(i= 5; i >= 4; i--) sprintf(text + strlen(text), "%-2.2x", (unsigned int) guid[i]); sprintf(text + strlen(text), "-"); for(i= 7; i >= 6; i--) sprintf(text + strlen(text), "%-2.2x", (unsigned int) guid[i]); sprintf(text + strlen(text), "-"); for(i= 8; i <= 9; i++) sprintf(text + strlen(text), "%-2.2x", (unsigned int) guid[i]); sprintf(text + strlen(text), "-"); for(i= 10; i <= 15; i++) sprintf(text + strlen(text), "%-2.2x", (unsigned int) guid[i]); } else { /* Plain hex string */ for(i= 0; i < 16; i++) sprintf(text + i * 2, "%-2.2x", (unsigned int) guid[i]); } return(1); } int Xorriso__parse_size_param(char *cpt, int key_l, int l, double *num) { char text[16]; *num= 0.0; if(l <= key_l || l >= key_l + 16) return(0); strncpy(text, cpt + key_l, l - key_l); text[l - key_l]= 0; *num= Scanf_io_size(text, 0); return(1); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2020 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of miscellaneous helper functions of xorriso. */ #ifndef Xorriso_pvt_misc_includeD #define Xorriso_pvt_misc_includeD yes #include <regex.h> #ifdef HAVE_STDINT_H #include <stdint.h> #else #ifdef HAVE_INTTYPES_H #include <inttypes.h> #endif #endif char *Text_shellsafe(char *in_text, char *out_text, int flag); int Sort_argv(int argc, char **argv, int flag); /* @param flag bit0= single letters */ char *Ftypetxt(mode_t st_mode, int flag); /* @param flag bit0=with year and seconds bit1=timestamp format YYYY.MM.DD.hhmmss */ char *Ftimetxt(time_t t, char *timetext, int flag); int System_uname(char **sysname, char **release, char **version, char **machine, int flag); /** Convert a text into a number of type double and multiply it by unit code [kmgtpe] (2^10 to 2^60) or [s] (2048). (Also accepts capital letters.) @param text Input like "42", "2k", "3.14m" or "-1g" @param flag Bitfield for control purposes: bit0= return -1 rather than 0 on failure @return The derived double value */ double Scanf_io_size(char *text, int flag); /* @flag bit0= do not initialize *diff_count @return <0 error , 0 = mismatch , 1 = match */ int Compare_text_lines(char *text1, char *text2, int *diff_count, int flag); int Decode_timestring(char *code, time_t *date, int flag); int Decode_ecma119_format(struct tm *erg, char *text, int flag); int Decode_ecma119_17byte(time_t *seconds, char *text, int flag); int Untimezone_ecma119_17byte(char *code, int flag); int Encode_ecma119_17byte(time_t seconds, char *centi_seconds, char *code, int flag); int Wait_for_input(int fd, int microsec, int flag); int Fileliste__target_source_limit(char *line, char sep, char **limit_pt, int flag); int Fileliste__escape_source_path(char *line, int size, int flag); int Hex_to_bin(char *hex, int bin_size, int *bin_count, unsigned char *bin_data, int flag); /* bit0= append (text!=NULL) */ int Sregex_string(char **handle, char *text, int flag); /* @param flag bit0= only test expression whether compilable */ int Sregex_match(char *pattern, char *text, int flag); /* vars[][0] points to the variable names, vars[][1] to their contents. start marks the begin of variable names. It must be non-empty. esc before start disables this meaning. start and esc may be equal but else they must have disjoint character sets. end marks the end of a variable name. It may be empty but if non-empty it must not appear in vars[][0]. @param flag bit0= Substitute unknown variables by empty text (else copy start,name,end unaltered to result). Parameter end must be non-empty for that. */ int Sregex_resolve_var(char *form, char *vars[][2], int num_vars, char *start, char *end, char *esc, char *result, int result_size, int flag); /* reg_expr should be twice as large as bourne_expr ( + 2 to be exact) */ /* return: 2= bourne_expr is surely a constant */ int Xorriso__bourne_to_reg(char bourne_expr[], char reg_expr[], int flag); int Xorriso__hide_mode(char *mode, int flag); char *Xorriso__hide_mode_text(int hide_mode, int flag); /* @return 0=truncated, 1=ok */ int Xorriso__to_upper(char *in, char *out, int out_size, int flag); int Xorriso__to_lower(char *in, char *out, int out_size, int flag); /* @param flag bit0= prepend target_prefix even if source_prefix does not get removed */ int Xorriso__exchange_prefix(char *source_prefix, char *target_prefix, char *eff_source, char *eff_target, int flag); /* @param text takes result, must provide at least 37 characters of storage @param flag bit0= structured text format (else hex string) */ int Xorriso__format_guid(uint8_t guid[16], char *text, int flag); /* @param cpt start of keyword=value string @param key_l length from cpt of keyword= string @param l length from cpt up to end of value string @param num result */ int Xorriso__parse_size_param(char *cpt, int key_l, int l, double *num); #endif /* ! Xorriso_pvt_misc_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of commands -a* to -c* as mentioned in man page or info file derived from xorriso.texi. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> /* for -charset */ #include <iconv.h> #include <langinfo.h> #include <locale.h> #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" /* Option -abort_on */ int Xorriso_option_abort_on(struct XorrisO *xorriso, char *in_severity, int flag) { int ret, sev; char severity[20], *official; Xorriso__to_upper(in_severity, severity, (int) sizeof(severity), 0); ret= Xorriso__text_to_sev(severity, &sev, 0); if(ret<=0) { sprintf(xorriso->info_text, "-abort_on: Not a known severity name : "); Text_shellsafe(in_severity, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(ret); } ret= Xorriso__sev_to_text(sev, &official, 0); if(ret <= 0) official= severity; if(Sfile_str(xorriso->abort_on_text, official, 0) <= 0) return(-1); xorriso->abort_on_severity= sev; xorriso->abort_on_is_default= 0; Xorriso_set_abort_severity(xorriso, 0); return(1); } /* Option -abstract_file */ int Xorriso_option_abstract_file(struct XorrisO *xorriso, char *name, int flag) { if(Xorriso_check_name_len(xorriso, name, (int) sizeof(xorriso->abstract_file), "-abstract_file", 0) <= 0) return(0); strcpy(xorriso->abstract_file, name); Xorriso_set_change_pending(xorriso, 1); return(1); } /* Option -acl "on"|"off" */ int Xorriso_option_acl(struct XorrisO *xorriso, char *mode, int flag) { int ret; if(strcmp(mode, "off")==0) xorriso->do_aaip&= ~3; else if(strcmp(mode, "on")==0) xorriso->do_aaip|= (1 | 2); else { sprintf(xorriso->info_text, "-acl: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= Xorriso_set_ignore_aclea(xorriso, 0); if(ret <= 0) return(ret); return(1); } /* @param flag bit3= unescape \\ */ static void unescape_pathspec_part(char *rpt, int flag) { char *wpt; wpt= rpt; for(; *rpt != 0; rpt++) { if(*rpt == '\\') { if(*(rpt + 1) == '=') continue; if((flag & 8) && *(rpt + 1) == '\\') rpt++; } *(wpt++)= *rpt; } *wpt= 0; } /* Option -add */ /* @param flag bit0=do not report the added item bit1=do not reset pacifier, no final pacifier message bit2= prepend ISO working directory in any case */ int Xorriso_option_add(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int i, end_idx, ret, was_failure= 0, fret, optc= 0, split, as_mkisofs= 0; char *target= NULL, *source= NULL, *ept, *eff_path= NULL; char **optv= NULL; ret= Xorriso_opt_args(xorriso, "-add", argc, argv, *idx, &end_idx, &optc, &optv, ((!!xorriso->allow_graft_points)<<2)|2); if(ret<=0) goto ex; Xorriso_alloc_meM(target, char, SfileadrL); Xorriso_alloc_meM(source, char, SfileadrL); Xorriso_alloc_meM(eff_path, char, SfileadrL); if(xorriso->allow_graft_points & 2) as_mkisofs= 8; if(!(flag&2)) Xorriso_pacifier_reset(xorriso, 0); for(i= 0; i<optc; i++) { if(Sfile_str(target,optv[i],0)<=0) {ret= -1; goto ex;} strcpy(source, optv[i]); split= 0; if(xorriso->allow_graft_points) { ret= Fileliste__target_source_limit(target, '=', &ept, !(xorriso->allow_graft_points & 2)); if(ret>0) { *ept= 0; strcpy(source, ept+1); split= 1; } /* unescape \= */; if(split) { unescape_pathspec_part(target, as_mkisofs); if(as_mkisofs) unescape_pathspec_part(source, as_mkisofs); } else { unescape_pathspec_part(source, as_mkisofs); } } if(split==0) strcpy(target, source); if(flag & 4) { ret= Sfile_prepend_path(xorriso->wdi, target, 0); if(ret<=0) { sprintf(xorriso->info_text, "Effective path gets much too long (%d)", (int) (strlen(xorriso->wdi)+strlen(target)+1)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto problem_handler; } } ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, target, eff_path, 2); if(ret<=0) goto problem_handler; strcpy(target, eff_path); ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, source,eff_path,2|4); if(ret<=0) goto problem_handler; strcpy(source, eff_path); ret= Xorriso_graft_in(xorriso, NULL, source, target, (off_t)0, (off_t)0, 0); if(ret<=0 || xorriso->request_to_abort) goto problem_handler; sprintf(xorriso->info_text, "Added to ISO image: %s '%s'='%s'\n", (ret>1 ? "directory" : "file"), (target[0] ? target : "/"), source); if(!(flag&1)) Xorriso_info(xorriso, 0); continue; /* regular bottom of loop */ problem_handler:; was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; goto ex; } if(!(flag&2)) Xorriso_pacifier_callback(xorriso, "files added", xorriso->pacifier_count, xorriso->pacifier_total, "", 1); ret= 1; ex:; (*idx)= end_idx; Xorriso_free_meM(target); Xorriso_free_meM(source); Xorriso_free_meM(eff_path); Xorriso_opt_args(xorriso, "-add", argc, argv, *idx, &end_idx, &optc, &optv, 256); if(ret<=0) return(ret); return(!was_failure); } /* Option -add_plainly "none"|"unknown" */ int Xorriso_option_add_plainly(struct XorrisO *xorriso, char *mode,int flag) { if(strcmp(mode, "none")==0) xorriso->add_plainly= 0; if(strcmp(mode, "unknown")==0) xorriso->add_plainly= 1; else if(strcmp(mode, "dashed")==0) xorriso->add_plainly= 2; else if(strcmp(mode, "any")==0) xorriso->add_plainly= 3; else { sprintf(xorriso->info_text, "-add_plainly: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -alter_date , -alter_date_r */ /* @param flag bit0=recursive (-alter_date_r) */ int Xorriso_option_alter_date(struct XorrisO *xorriso, char *time_type, char *timestring, int argc, char **argv, int *idx, int flag) { int i, ret, was_failure= 0, t_type= 0, end_idx, fret; time_t t; int optc= 0; char **optv= NULL; struct FindjoB *job= NULL; struct stat dir_stbuf; ret= Xorriso_opt_args(xorriso, "-alter_date", argc, argv, *idx, &end_idx, &optc, &optv, 0); if(ret<=0) goto ex; ret= Xorriso_convert_datestring(xorriso, "-alter_date", time_type, timestring, &t_type, &t, 0); if(ret<=0) goto ex; for(i= 0; i<optc; i++) { if(flag&1) { ret= Findjob_new(&job, optv[i], 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "-alter_date", 0); {ret= -1; goto ex;} } Findjob_set_action_ad(job, t_type, t, 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, optv[i], &dir_stbuf, 0, 0); Findjob_destroy(&job, 0); } else ret= Xorriso_set_time(xorriso, optv[i], t, t_type); if(ret>0 && !xorriso->request_to_abort) continue; /* regular bottom of loop */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; goto ex; } ret= 1; ex:; (*idx)= end_idx; Xorriso_opt_args(xorriso, "-alter_date", argc, argv, *idx, &end_idx, &optc, &optv, 256); Findjob_destroy(&job, 0); if(ret<=0) return(ret); return(!was_failure); } /* Option -append_partition */ int Xorriso_option_append_partition(struct XorrisO *xorriso, char *partno_text, char *type_text, char *image_path, int flag) { int partno = 0, type_code= -1, i, guid_valid= 0, ret, set_changed= 0; int disable= 0; unsigned int unum; char *tpt; uint8_t guid[16]; static char *part_type_names[] = {"FAT12", "FAT16", "Linux", "", NULL}; static int part_type_codes[] = { 0x01, 0x06, 0x83, 0x00}; if(strcmp(partno_text, "all") == 0) { if(strcmp(type_text, "revoke") != 0 && image_path[0] != 0) { sprintf(xorriso->info_text, "-append_partition: Pseudo partition number 'all' works only with type code 'revoke' or empty disk path"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } partno= -1; } else { sscanf(partno_text, "%d", &partno); if(partno < 1 || partno > Xorriso_max_appended_partitionS) { sprintf(xorriso->info_text, "-append_partition: Partition number '%s' is out of range (1...%d)", partno_text, Xorriso_max_appended_partitionS); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } if(strcmp(type_text, "revoke") == 0) { disable= 1; } else { for(i= 0; part_type_names[i] != NULL; i++) if(strcmp(part_type_names[i], type_text) == 0) break; if(part_type_names[i] != NULL) type_code= part_type_codes[i]; if(type_code < 0) { ret= Xorriso_parse_type_guid(xorriso, type_text, guid, &type_code, 0); if(ret > 0) guid_valid= 1; } if(type_code < 0) { tpt= type_text; if(strncmp(tpt, "0x", 2) == 0) tpt+= 2; else goto bad_type; unum= 0xffffffff; sscanf(tpt, "%X", &unum); if(unum > 0xff) { bad_type:; sprintf(xorriso->info_text, "-append_partition: Partition type '%s' is out of range (0x00...0xff or GUID)", type_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } type_code= unum; } disable= (image_path[0] == 0); } if(partno == -1) { /* All partitions */ set_changed= 0; for(i= 0; i < Xorriso_max_appended_partitionS; i++) { /* For now only pseudo-type 'revoke' works for all partitions */ if(xorriso->appended_partitions[i - 1] != NULL) { if(xorriso->appended_partitions[i - 1][0] != 0) set_changed= 1; free(xorriso->appended_partitions[i - 1]); xorriso->appended_partitions[i - 1]= NULL; } } goto work_done; } set_changed= 1; if(xorriso->appended_partitions[partno - 1] != NULL) { if(strcmp(xorriso->appended_partitions[partno - 1], image_path) == 0) set_changed= 0; free(xorriso->appended_partitions[partno - 1]); xorriso->appended_partitions[partno - 1]= NULL; } else { if(disable) set_changed= 0; } if(disable) goto work_done; xorriso->appended_partitions[partno - 1]= strdup(image_path); if(xorriso->appended_partitions[partno - 1] == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); return(-1); } if(xorriso->appended_part_types[partno - 1] != type_code) set_changed= 1; xorriso->appended_part_types[partno - 1]= type_code; if(guid_valid) { if(xorriso->appended_part_gpt_flags[partno - 1] & 1) { if(memcmp(xorriso->appended_part_type_guids[partno - 1], guid, 16) != 0) set_changed= 1; } else { set_changed= 1; } memcpy(xorriso->appended_part_type_guids[partno - 1], guid, 16); xorriso->appended_part_gpt_flags[partno - 1]|= 1; } else { if(xorriso->appended_part_gpt_flags[partno - 1] & 1) set_changed= 1; xorriso->appended_part_gpt_flags[partno - 1]&= ~1; } work_done:; if(set_changed) Xorriso_set_change_pending(xorriso, 1); return(1); } /* Option -application_id */ int Xorriso_option_application_id(struct XorrisO *xorriso, char *name, int flag) { if(Xorriso_check_name_len(xorriso, name, (int) sizeof(xorriso->application_id), "-application_id", 0) <= 0) return(0); if(strcmp(name, "@xorriso@") == 0) Xorriso_preparer_string(xorriso, xorriso->application_id, 0); else strcpy(xorriso->application_id,name); Xorriso_set_change_pending(xorriso, 1); return(1); } /* Command -application_use */ int Xorriso_option_application_use(struct XorrisO *xorriso, char *path, int flag) { if(Sfile_str(xorriso->application_use, path, 0) <= 0) { sprintf(xorriso->info_text, "-application_use: parameter string is much too long (%d)", (int) strlen(path)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -as */ /* @param flag bit0=do not report the added item bit1=do not reset pacifier, no final pacifier message */ int Xorriso_option_as(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int end_idx, ret, idx_count; end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, 1); idx_count= end_idx-(*idx); if(end_idx<=0 || (*idx)>=argc) { if(idx_count<1) sprintf(xorriso->info_text, "-as : Not enough arguments given. Needed: whom do_what %s", xorriso->list_delimiter); else sprintf(xorriso->info_text, "-as %s : Not enough arguments given. Needed: do_what %s", argv[*idx], xorriso->list_delimiter); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(strcmp(argv[*idx], "cdrecord")==0 || strcmp(argv[*idx], "wodim")==0 || strcmp(argv[*idx], "cdrskin")==0 || strcmp(argv[*idx], "xorrecord")==0) { ret= Xorriso_cdrskin(xorriso, argv[*idx], end_idx-(*idx)-1, argv+(*idx)+1, 0); if(ret<=0) goto ex; } else if(strcmp(argv[*idx], "mkisofs")==0 || strcmp(argv[*idx], "genisoimage")==0 || strcmp(argv[*idx], "genisofs")==0 || strcmp(argv[*idx], "xorrisofs")==0) { ret= Xorriso_genisofs(xorriso, argv[*idx], end_idx-(*idx)-1, argv+(*idx)+1, 0); if(ret<=0) goto ex; } else { sprintf(xorriso->info_text, "-as : Not a known emulation personality: '%s'", argv[*idx]); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } ret= 1; ex:; (*idx)= end_idx; return(ret); } /* Option -assert_volid */ int Xorriso_option_assert_volid(struct XorrisO *xorriso, char *pattern, char *severity, int flag) { int ret, sev; char *sev_text= "", off_severity[20]; if(strlen(pattern)>=sizeof(xorriso->assert_volid)) { sprintf(xorriso->info_text, "Name too long with option -application_id (%d > %d)", (int) strlen(pattern), (int) sizeof(xorriso->assert_volid)-1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } if(pattern[0]) { ret= Sregex_match(pattern, "", 1); if(ret <= 0) { sprintf(xorriso->info_text, "-assert_volid: Cannot use given pattern."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } if(severity[0] != 0 || pattern[0] != 0) { if(severity[0] == 0) sev_text= xorriso->abort_on_text; else sev_text= severity; if(strcmp(sev_text, "NEVER") == 0) sev_text= "ABORT"; Xorriso__to_upper(sev_text, off_severity, (int) sizeof(off_severity), 0); sev_text= off_severity; ret= Xorriso__text_to_sev(sev_text, &sev, 0); if(ret<=0) { sprintf(xorriso->info_text, "-assert_volid: Not a known severity name : "); Text_shellsafe(severity, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(ret); } } if(Sfile_str(xorriso->assert_volid, pattern,0) <= 0) return(-1); strcpy(xorriso->assert_volid_sev, sev_text); return(1); } /* Command -assess_indev_features */ int Xorriso_option_assess_indev_features(struct XorrisO *xorriso, char *mode, int flag) { int ret; if(strcmp(mode, "plain") != 0 && strcmp(mode, "cmd") != 0 && strcmp(mode, "as_mkisofs") != 0 && strcmp(mode, "replay") != 0 && mode[0] != 0) { sprintf(xorriso->info_text, "-assess_indev_features: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= Xorriso_assess_written_features(xorriso, mode, 0); return(ret); } /* Option -auto_charset "on"|"off" */ int Xorriso_option_auto_charset(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "off")==0) xorriso->do_aaip&= ~(256 | 512); else if(strcmp(mode, "on")==0) xorriso->do_aaip|= (256 | 512); else { sprintf(xorriso->info_text, "-auto_charset: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -backslash_codes */ int Xorriso_option_backslash_codes(struct XorrisO *xorriso, char *mode, int flag) { char *npt, *cpt; int l, was; was= xorriso->bsl_interpretation; xorriso->bsl_interpretation= 0; npt= cpt= mode; for(; npt!=NULL; cpt= npt+1) { npt= strchr(cpt,':'); if(npt==NULL) l= strlen(cpt); else l= npt-cpt; if(l == 0) continue; if(l == 3 && strncmp(cpt, "off", l)==0) { xorriso->bsl_interpretation= 0; } else if(l == 16 && strncmp(cpt, "in_double_quotes", l)==0) { xorriso->bsl_interpretation= (xorriso->bsl_interpretation & ~3) | 1; } else if(l == 9 && strncmp(cpt, "in_quotes", l)==0) { xorriso->bsl_interpretation= (xorriso->bsl_interpretation & ~3) | 2; } else if(l == 17 && strncmp(cpt, "with_quoted_input", l)==0) { xorriso->bsl_interpretation= (xorriso->bsl_interpretation & ~3) | 3; } else if(l == 22 && strncmp(cpt, "with_program_arguments", l)==0) { xorriso->bsl_interpretation= xorriso->bsl_interpretation | 16; } else if(l == 13 && strncmp(cpt, "encode_output", l)==0) { xorriso->bsl_interpretation= xorriso->bsl_interpretation | 32 | 64; } else if(l == 14 && strncmp(cpt, "encode_results", l)==0) { xorriso->bsl_interpretation= xorriso->bsl_interpretation | 32; } else if(l == 12 && strncmp(cpt, "encode_infos", l)==0) { xorriso->bsl_interpretation= xorriso->bsl_interpretation | 64; } else if(l == 2 && strncmp(cpt, "on", l)==0) { xorriso->bsl_interpretation= 3 | 16 | 32 | 64; } else { if(l<SfileadrL) sprintf(xorriso->info_text, "-backslash_codes: unknown mode '%s'", cpt); else sprintf(xorriso->info_text, "-backslash_codes: oversized mode parameter (%d)", l); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); xorriso->bsl_interpretation= was; return(0); } } return(1); } /* Option -ban_stdio_write */ int Xorriso_option_ban_stdio_write(struct XorrisO *xorriso, int flag) { xorriso->ban_stdio_write= 1; return(1); } /* Option -biblio_file */ int Xorriso_option_biblio_file(struct XorrisO *xorriso, char *name, int flag) { if(Xorriso_check_name_len(xorriso, name, (int) sizeof(xorriso->biblio_file), "-biblio_file", 0) <= 0) return(0); strcpy(xorriso->biblio_file, name); Xorriso_set_change_pending(xorriso, 1); return(1); } /* Option -blank and -format */ /* @param flag bit0= format rather than blank @return <=0 error , 1 success, 2 revoked by -reassure */ int Xorriso_option_blank(struct XorrisO *xorriso, char *in_mode, int flag) { char *cmd= "-blank", *mode; int aq_ret, ret, mode_flag= 0, as_needed= 0, idx, do_force= 0; off_t size= 0; if(flag&1) cmd= "-format"; if(xorriso->out_drive_handle == NULL) { sprintf(xorriso->info_text, "%s: No output drive set by -dev -or -outdev", cmd); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(xorriso->in_drive_handle == xorriso->out_drive_handle) { if(Xorriso_change_is_pending(xorriso, 0)) { sprintf(xorriso->info_text, "%s: Image changes pending. -commit or -rollback first.", cmd); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } ret= Xorriso_reassure(xorriso, cmd, "possibly make unreadable data on outdev", 0); if(ret<=0) return(2); if(strncmp(in_mode, "force:", 6) == 0) { do_force= 1; mode= in_mode + 6; } else mode= in_mode; if(strcmp(mode, "as_needed")==0 || mode[0]==0) as_needed= 1; else if(strcmp(mode, "all")==0 || strcmp(mode, "full")==0) mode_flag= 0; else if((strcmp(mode, "deformat")==0 || strcmp(mode, "deformat_sequential")==0) && !(flag&1)) mode_flag= 2; else if((strcmp(mode, "deformat_quickest")==0 || strcmp(mode, "deformat_sequential_quickest")==0) && !(flag&1)) mode_flag= 3; else if(strcmp(mode, "fast")==0) mode_flag= 1; else if(strncmp(mode, "by_index_", 9)==0 && (flag&1)) { mode_flag= 128; idx= -1; if(strlen(mode)>9) sscanf(mode+9, "%d", &idx); if(idx<0 || idx>255) { unusable_index:; sprintf(xorriso->info_text, "-format: mode '%s' provides unusable index number", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } mode_flag|= (idx<<8); } else if(strncmp(mode, "fast_by_index_", 14)==0 && (flag&1)) { mode_flag= 1 | 128; idx= -1; if(strlen(mode)>14) sscanf(mode+14, "%d", &idx); if(idx<0 || idx>255) goto unusable_index; mode_flag|= (idx<<8); } else if(strncmp(mode, "by_size_", 8) == 0 && (flag & 1)) { size= (off_t) Scanf_io_size(mode + 8, 0); if(size <= 0) { unusable_size:; sprintf(xorriso->info_text, "-format: mode '%s' provides unusable size value", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } mode_flag= 2; } else if(strncmp(mode, "fast_by_size_", 13) == 0 && (flag & 1)) { size= (off_t) Scanf_io_size(mode + 13, 0); if(size <= 0) goto unusable_size; mode_flag= 3; } else if(strcmp(mode, "without_spare") == 0 && (flag & 1)) { mode_flag= 32; } else { sprintf(xorriso->info_text, "%s: Unknown %s mode '%s'", cmd, ((flag&1) ? "-format" : "-blank"), mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(do_force) { ret= Xorriso_pretend_full_disc(xorriso, 0); if(ret <= 0) return(ret); } if(as_needed) ret= Xorriso_blank_as_needed(xorriso, (flag&1)<<2); else if(flag&1) ret= Xorriso_format_media(xorriso, size, mode_flag & 0xffa7); else ret= Xorriso_blank_media(xorriso, mode_flag&3); if(ret==0) return(ret); if(ret <= 0) { /* in case of success, above functions will have re-acquired */ aq_ret= Xorriso_reaquire_outdev(xorriso, 0); /* actually give up drive */ if(ret<aq_ret) return(ret); if(aq_ret<=0) return(aq_ret); } return(1); } /* Option -boot_image */ int Xorriso_option_boot_image(struct XorrisO *xorriso, char *form, char *treatment, int flag) { int was_ok= 1, ret, isolinux_grub= 0, count, bin_count, parm_len; int palohdrversion, type_code, is_change= 0; unsigned int u; char *formpt, *treatpt, *eff_path= NULL, *eqpt, parm[20]; uint8_t sn[8]; double num; Xorriso_alloc_meM(eff_path, char, SfileadrL); formpt= form; if(formpt[0]=='-') formpt++; treatpt= treatment; if(treatpt[0]=='-') treatpt++; if(strcmp(formpt, "isolinux")==0 || strcmp(formpt, "grub") == 0) isolinux_grub= 1; if(strcmp(treatpt, "keep")==0) { if(xorriso->boot_count > 0) { cannot_keep_or_patch:; sprintf(xorriso->info_text, "Loaded boot image has already been replaced. Cannot keep or patch it."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(isolinux_grub) goto treatment_patch; xorriso->keep_boot_image= 1; xorriso->patch_isolinux_image= (xorriso->patch_isolinux_image & ~3) | 0; xorriso->boot_image_bin_path[0]= 0; xorriso->patch_system_area= 0; is_change= 1; } else if(strcmp(treatpt, "patch")==0) { treatment_patch:; if(xorriso->boot_count > 0) goto cannot_keep_or_patch; xorriso->keep_boot_image= 0; xorriso->patch_isolinux_image= (xorriso->patch_isolinux_image & ~3) | 1; xorriso->boot_image_bin_path[0]= 0; if(strcmp(formpt, "grub") == 0) { xorriso->patch_isolinux_image|= 2; xorriso->patch_system_area= 1; } else if(strcmp(formpt, "isolinux") == 0) xorriso->patch_system_area= 2; else xorriso->patch_system_area= 0; is_change= 1; } else if(strcmp(treatpt, "replay")==0) { ret= Xorriso_report_system_area(xorriso, "cmd", 2); if(ret <= 0) goto ex; } else if(strcmp(treatpt, "discard")==0) { xorriso->keep_boot_image= 0; xorriso->patch_isolinux_image= (xorriso->patch_isolinux_image & ~0x3ff) | 0; xorriso->boot_image_bin_path[0]= 0; xorriso->patch_system_area= 0; if((xorriso->system_area_options & 0xfc ) == 0) xorriso->system_area_options= 0; /* Reset eventual type 0 flags */ if(xorriso->boot_count > 0) { ret= Xorriso_attach_boot_image(xorriso, 2); /* dispose boot images */ if(ret <= 0) goto ex; } is_change= 1; } else if(strcmp(treatpt, "next") == 0) { ret= Xorriso_attach_boot_image(xorriso, 0); if(ret <= 0) goto ex; } else if(strcmp(treatpt, "show_status")==0) { sprintf(xorriso->result_line, "------------------------------------\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "Status of loaded boot image :\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "------------------------------------\n"); Xorriso_result(xorriso, 0); Xorriso_show_boot_info(xorriso, 0); sprintf(xorriso->result_line, "------------------------------------\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "Boot image settings for next commit:\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "------------------------------------\n"); Xorriso_result(xorriso, 0); Xorriso_status(xorriso, "-boot_image", NULL, 0); Xorriso_status(xorriso, "-append_partition", NULL, 0); sprintf(xorriso->result_line, "------------------------------------\n"); Xorriso_result(xorriso, 0); } else if(strcmp(treatpt, "cat_path=") == 0) { xorriso->boot_image_cat_path[0] = 0; is_change= 1; } else if(strncmp(treatpt, "cat_path=", 9) == 0) { ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, treatpt + 9, xorriso->boot_image_cat_path, 2); if(ret <= 0) goto ex; Xorriso_warn_if_not_bootcat(xorriso, "-boot_image ", treatment, xorriso->boot_image_cat_path, 0); is_change= 1; } else if(strncmp(treatpt, "cat_hidden=", 11) == 0) { ret= Xorriso__hide_mode(treatpt + 11, 0); if(ret >= 0) { is_change= 1; xorriso->boot_image_cat_hidden= ret; } else { was_ok= 0; } } else if(strncmp(treatpt, "dir=", 4) == 0) { if(strcmp(formpt, "isolinux")==0) { /* ISOLINUX */ /* The three locations mentioned in http://syslinux.zytor.com/iso.php */ if(strcmp(treatpt + 4, "/") == 0) strcpy(xorriso->boot_image_bin_path, "/"); else if(strcmp(treatpt + 4, "isolinux") == 0 || strcmp(treatpt + 4, "/isolinux") == 0) strcpy(xorriso->boot_image_bin_path, "/isolinux/"); else if(strcmp(treatpt + 4, "boot/isolinux") == 0 || strcmp(treatpt + 4, "/boot/isolinux") == 0 || strcmp(treatpt + 4, "boot") == 0 || strcmp(treatpt + 4, "/boot") == 0) strcpy(xorriso->boot_image_bin_path, "/boot/isolinux/"); else { sprintf(xorriso->info_text, "Unrecognized keyword with -boot_image %s %s", form, treatment); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text, "Allowed with dir= are / , /isolinux . /boot/isolinux"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); {ret= 0; goto ex;} } strcpy(xorriso->boot_image_cat_path, xorriso->boot_image_bin_path); strcat(xorriso->boot_image_bin_path, "isolinux.bin"); strcat(xorriso->boot_image_cat_path, "boot.cat"); xorriso->boot_image_load_size= 4 * 512; xorriso->boot_img_size_default= 0; xorriso->keep_boot_image= 0; xorriso->patch_isolinux_image= (xorriso->patch_isolinux_image & ~3) | 1; strcpy(xorriso->boot_image_bin_form, formpt); Xorriso_warn_if_not_exist(xorriso, "Implicit boot image via -boot_image ", treatment, xorriso->boot_image_bin_path, 0); Xorriso_warn_if_not_bootcat(xorriso, "-boot_image ", treatment, xorriso->boot_image_cat_path, 0); is_change= 1; {ret= 1; goto ex;} } else if(strcmp(formpt, "grub") == 0) { /* >>> GRUB */ was_ok= 0; strcpy(xorriso->boot_image_bin_form, formpt); is_change= 1; } else was_ok= 0; } else if(strcmp(treatpt, "bin_path=") == 0) { xorriso->boot_image_bin_path[0] = 0; xorriso->boot_efi_default= 0; is_change= 1; } else if(strncmp(treatpt, "bin_path=", 9) == 0) { if(strncmp(treatpt + 9, "--interval:appended_partition_", 30) == 0) { if(strlen(treatpt + 9) >= sizeof(xorriso->boot_image_bin_path)) { interval_text_long:; sprintf(xorriso->info_text, "-boot_image: --interval text is much too long (%d)", (int) strlen(treatpt + 9)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } strcpy(xorriso->boot_image_bin_path, treatpt + 9); } else { ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, treatpt + 9, xorriso->boot_image_bin_path, 2); if(ret <= 0) goto ex; Xorriso_warn_if_not_exist(xorriso, "-boot_image ", treatment, xorriso->boot_image_bin_path, 0); } xorriso->keep_boot_image= 0; if(isolinux_grub) { xorriso->patch_isolinux_image= (xorriso->patch_isolinux_image & ~3) | 1; if(xorriso->boot_image_bin_path[0]) { xorriso->boot_image_load_size= 4 * 512; xorriso->boot_img_size_default= 0; } strcpy(xorriso->boot_image_bin_form, formpt); } else strcpy(xorriso->boot_image_bin_form, "any"); xorriso->boot_efi_default= 0; is_change= 1; } else if(strcmp(treatpt, "efi_path=") == 0) { xorriso->boot_image_bin_path[0] = 0; xorriso->boot_efi_default= 0; is_change= 1; } else if(strncmp(treatpt, "efi_path=", 9) == 0) { if(strncmp(treatpt + 9, "--interval:appended_partition_", 30) == 0) { if(strlen(treatpt + 9) >= sizeof(xorriso->boot_image_bin_path)) goto interval_text_long; strcpy(xorriso->boot_image_bin_path, treatpt + 9); } else { ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, treatpt + 9, xorriso->boot_image_bin_path, 2); if(ret <= 0) goto ex; Xorriso_warn_if_not_exist(xorriso, "-boot_image ", treatment, xorriso->boot_image_bin_path, 0); } xorriso->keep_boot_image= 0; xorriso->boot_efi_default= 1; is_change= 1; } else if(strncmp(treatpt, "mips_path=", 10) == 0) { sprintf(eff_path, "-boot_image %s mips_path=", formpt); ret= Xorriso_coordinate_system_area(xorriso, 1, 0, eff_path, 0); if(ret <= 0) goto ex; ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, treatpt + 10, eff_path, 2); if(ret <= 0) goto ex; ret= Xorriso_add_mips_boot_file(xorriso, eff_path, 0); if(ret <= 0) goto ex; Xorriso_warn_if_not_exist(xorriso, "-boot_image ", treatment, eff_path, 0); is_change= 1; } else if(strncmp(treatpt, "mipsel_path=", 12) == 0) { sprintf(eff_path, "-boot_image %s mipsel_path=", formpt); ret= Xorriso_coordinate_system_area(xorriso, 2, 0, eff_path, 0); if(ret <= 0) goto ex; ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, treatpt + 12, eff_path, 2); if(ret <= 0) goto ex; ret= Xorriso_add_mips_boot_file(xorriso, eff_path, 2); if(ret <= 0) goto ex; Xorriso_warn_if_not_exist(xorriso, "-boot_image ", treatment, eff_path, 0); is_change= 1; } else if(strcmp(treatpt, "mips_discard") == 0 || strcmp(treatpt, "mipsel_discard") == 0 || strcmp(treatpt, "sparc_discard") == 0 || strcmp(treatpt, "hppa_discard") == 0 || strcmp(treatpt, "alpha_discard") == 0) { xorriso->system_area_options&= ~0xfc; /* system area type 0 */ Xorriso_add_mips_boot_file(xorriso, "", 1); /* give up MIPS boot files */ Xorriso_set_hppa_boot_parm(xorriso, "", "", 1); /* give up HP-PA files */ Xorriso_set_alpha_boot(xorriso, "", 1); /* give up DEC Alpha loader */ is_change= 1; } else if(strncmp(treatpt, "sparc_label=", 12) == 0) { sprintf(eff_path, "-boot_image %s sparc_label=", formpt); ret= Xorriso_coordinate_system_area(xorriso, 3, 0, eff_path, 0); if(ret <= 0) goto ex; strncpy(xorriso->ascii_disc_label, treatpt + 12, Xorriso_disc_label_sizE - 1); xorriso->ascii_disc_label[Xorriso_disc_label_sizE - 1] = 0; is_change= 1; } else if(strncmp(treatpt, "grub2_sparc_core=", 17) == 0) { ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, treatpt + 17, xorriso->grub2_sparc_core, 2); if(ret <= 0) goto ex; Xorriso_warn_if_not_exist(xorriso, "-boot_image ", treatment, xorriso->grub2_sparc_core, 0); is_change= 1; } else if(strncmp(treatpt, "hppa_", 5) == 0) { sprintf(eff_path, "-boot_image %s %s", formpt, treatpt); palohdrversion= (xorriso->system_area_options >> 2) & 0x3f; if(palohdrversion != 4) palohdrversion= 5; ret= Xorriso_coordinate_system_area(xorriso, palohdrversion, 0, eff_path, 0); if(ret <= 0) goto ex; eqpt= strchr(treatpt, '='); if(eqpt == NULL) { sprintf(xorriso->info_text, "No equal sign found in -boot_image %s %s", form, treatment); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } parm_len= (eqpt - treatpt) - 5; if(parm_len > (int) sizeof(parm) - 1) parm_len= sizeof(parm) - 1; strncpy(parm, treatpt + 5, parm_len); parm[parm_len]= 0; ret= Xorriso_set_hppa_boot_parm(xorriso, eqpt + 1, parm, 0); if(ret <= 0) goto ex; /* (needs no file existence check because already done in libisofs) */ is_change= 1; } else if(strncmp(treatpt, "alpha_boot=", 11) == 0) { sprintf(eff_path, "-boot_image %s %s", formpt, treatpt); ret= Xorriso_coordinate_system_area(xorriso, 6, 0, eff_path, 0); if(ret <= 0) goto ex; ret = Xorriso_set_alpha_boot(xorriso, treatpt + 11, 0); if(ret <= 0) goto ex; Xorriso_warn_if_not_exist(xorriso, "-boot_image ", treatment, treatpt + 11, 0); is_change= 1; } else if(strncmp(treatpt, "boot_info_table=", 16)==0) { if(strcmp(treatpt + 16, "off") == 0) xorriso->patch_isolinux_image= (xorriso->patch_isolinux_image & ~3) | 0; else if(strcmp(treatpt + 16, "on") == 0) xorriso->patch_isolinux_image= (xorriso->patch_isolinux_image & ~3) | 1 | (2 * (strcmp(treatpt, "grub") == 0)); else was_ok= 0; if(was_ok) is_change= 1; } else if(strncmp(treatpt, "grub2_boot_info=", 16)==0) { if(strcmp(treatpt + 16, "off") == 0) xorriso->patch_isolinux_image= xorriso->patch_isolinux_image & ~512; else if(strcmp(treatpt + 16, "on") == 0) xorriso->patch_isolinux_image= xorriso->patch_isolinux_image | 512; else was_ok= 0; if(was_ok) is_change= 1; } else if(strncmp(treatpt, "load_size=", 10) == 0) { if(strcmp(treatpt + 10, "full") == 0) { xorriso->boot_img_full_size= 1; } else { num= Scanf_io_size(treatpt + 10, 0); if(num < 512 && isolinux_grub) { sprintf(xorriso->info_text, "-boot_image %s : load_size too small (%s < 512)", formpt, treatpt + 10); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } xorriso->boot_image_load_size= num; } xorriso->boot_img_size_default= 0; is_change= 1; } else if(strncmp(treatpt, "id_string=", 10) == 0) { memset(xorriso->boot_id_string, 0, 29); if(strlen(treatpt + 10) == 56) { ret= Hex_to_bin(treatpt + 10, 28, &count, xorriso->boot_id_string, 0); } else ret= 0; if(ret <= 0) strncpy((char *) xorriso->boot_id_string, treatpt + 10, 28); is_change= 1; } else if(strncmp(treatpt, "sel_crit=", 9) == 0) { memset(xorriso->boot_selection_crit, 0, 21); count= 0; ret= Hex_to_bin(treatpt + 9, 20, &count, xorriso->boot_selection_crit, 0); if(ret <= 0) { sprintf(xorriso->info_text, "-boot_image %s sel_crit= : Wrong form. Need even number of hex digits.", formpt); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } is_change= 1; } else if(strncmp(treatpt, "system_area=", 12) == 0) { if(strcmp(formpt, "isolinux")==0) { ret= Xorriso_coordinate_system_area(xorriso, 0, 2, "-boot_image isolinux system_area=", 0); if(ret <= 0) goto ex; } if(strcmp(treatpt + 12, ".") == 0) { ret= Xorriso_set_system_area_path(xorriso, "", 0); } else { /* (checks also for existence of the disk file) */ ret= Xorriso_set_system_area_path(xorriso, treatpt + 12, 0); } if(ret <= 0) goto ex; xorriso->system_area_options&= ~0x4000; if(strcmp(treatpt + 12, "/dev/zero") == 0) xorriso->system_area_clear_loaded= 1; is_change= 1; } else if(strncmp(treatpt, "partition_table=", 16)==0) { if(strcmp(treatpt + 16, "off") == 0) { xorriso->system_area_options&= ~3; } else if(strcmp(treatpt + 16, "on") == 0) { sprintf(eff_path, "-boot_image %s partition_table=", formpt); if(strcmp(formpt, "isolinux")==0) ret= Xorriso_coordinate_system_area(xorriso, 0, 2, eff_path, 0); else ret= Xorriso_coordinate_system_area(xorriso, 0, 1, eff_path, 0); if(ret <= 0) goto ex; } else was_ok= 0; if(was_ok) is_change= 1; } else if(strncmp(treatpt, "partition_entry=", 16)==0) { if(strcmp(formpt, "isolinux") != 0) { sprintf(xorriso->info_text, "-boot_image %s partition_entry=%s : Wrong type. Need \"isolinux\".", formpt, treatpt + 16); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(strcmp(treatpt + 16, "gpt_basdat") == 0) { xorriso->patch_isolinux_image = (xorriso->patch_isolinux_image & ~0x0fc) | (1 << 2); } else if(strcmp(treatpt + 16, "gpt_hfsplus") == 0) { xorriso->patch_isolinux_image = (xorriso->patch_isolinux_image & ~0x0fc) | (2 << 2); } else if(strcmp(treatpt + 16, "apm_hfsplus") == 0) { xorriso->patch_isolinux_image = xorriso->patch_isolinux_image | (1 << 8); } else if(strcmp(treatpt + 16, "off") == 0) { xorriso->patch_isolinux_image = (xorriso->patch_isolinux_image & ~0x1fc); } else was_ok= 0; if(was_ok) is_change= 1; } else if(strncmp(treatpt, "partition_offset=", 17)==0) { u= 0; sscanf(treatpt + 17, "%u", &u); if(u > 0 && u < 16) { sprintf(xorriso->info_text, "-boot_image %s partition_offset= : Non-zero number too small (<16).", formpt); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } xorriso->partition_offset= u; is_change= 1; } else if(strncmp(treatpt, "appended_part_as=", 17) == 0) { if(strcmp(treatpt + 17, "gpt") == 0) { xorriso->appended_as_gpt = 1; } else if(strcmp(treatpt + 17, "mbr") == 0) { xorriso->appended_as_gpt = 0; xorriso->appended_as_apm = 0; } else if(strcmp(treatpt + 17, "apm") == 0) { xorriso->appended_as_apm = 1; } else was_ok= 0; if(was_ok) is_change= 1; } else if(strncmp(treatpt, "gpt_disk_guid=", 14) == 0) { ret= Xorriso_parse_gpt_guid(xorriso, treatpt + 14, 0); if(ret <= 0) goto ex; is_change= 1; } else if(strncmp(treatpt, "part_like_isohybrid=", 20) == 0) { if(strcmp(treatpt + 20, "on") == 0) xorriso->part_like_isohybrid= 1; else if(strcmp(treatpt + 20, "off") == 0) xorriso->part_like_isohybrid= 0; else was_ok= 0; if(was_ok) is_change= 1; } else if(strncmp(treatpt, "iso_mbr_part_type=", 18) == 0) { ret= 256; xorriso->iso_mbr_part_flag&= ~1; if(strncmp(treatpt + 18, "default", 2) == 0) { ret= -1; } else if(strncmp(treatpt + 18, "0x", 2) == 0) { u= 256; sscanf(treatpt + 20, "%x", &u); ret= u; } else { ret= Xorriso_parse_type_guid(xorriso, treatpt + 18, xorriso->iso_gpt_type_guid, &type_code, 0); if(ret > 0) { ret= type_code; xorriso->iso_mbr_part_flag|= 1; } else { sscanf(treatpt + 18, "%d", &ret); } } if(ret < -1 || ret > 0xff) { sprintf(xorriso->info_text, "-boot_image %s : iso_mbr_part_type='%s' wrong (\"default\", 0 ... 255, 0x00 ... 0xff)", formpt, treatpt + 18); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } xorriso->iso_mbr_part_type= ret; is_change= 1; } else if(strncmp(treatpt, "partition_hd_cyl=", 17)==0) { u= 0; sscanf(treatpt + 17, "%u", &u); if(u > 255) { sprintf(xorriso->info_text, "-boot_image %s partition_hd_cyl= : Number too large (>255).", formpt); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } xorriso->partition_heads_per_cyl= u; is_change= 1; } else if(strncmp(treatpt, "partition_sec_hd=", 17)==0) { u= 0; sscanf(treatpt + 17, "%u", &u); if(u > 63) { sprintf(xorriso->info_text, "-boot_image %s partition_sec_hd= : Number too large (>63).", formpt); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } xorriso->partition_secs_per_head= u; is_change= 1; } else if(strncmp(treatpt, "partition_cyl_align=", 20)==0) { if(strcmp(treatpt + 20, "auto") == 0) xorriso->system_area_options= (xorriso->system_area_options & ~0x300); else if(strcmp(treatpt + 20, "on") == 0) xorriso->system_area_options= (xorriso->system_area_options & ~0x300) | 0x100; else if(strcmp(treatpt + 20, "off") == 0) xorriso->system_area_options= (xorriso->system_area_options & ~0x300) | 0x200; else if(strcmp(treatpt + 20, "all") == 0) xorriso->system_area_options= (xorriso->system_area_options & ~0x300) | 0x300; else { sprintf(xorriso->info_text, "-boot_image %s partition_cyl_align: unknown mode : %s", formpt, treatpt + 20); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); {ret= 0; goto ex;} } is_change= 1; } else if(strncmp(treatpt, "platform_id=", 12)==0) { u= 256; /* intentionally too large */ if(strncmp(treatpt + 12, "0x", 2) == 0) sscanf(treatpt + 14, "%x", &u); else sscanf(treatpt + 12, "%u", &u); if(u > 0xff) { sprintf(xorriso->info_text, "-boot_image %s : platform_id too large (%s > 0xff)", formpt, treatpt + 12); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } xorriso->boot_platform_id= u; is_change= 1; } else if(strncmp(treatpt, "emul_type=", 10)==0) { if(strcmp(treatpt + 10, "none") == 0 || strcmp(treatpt + 10, "no_emulation") == 0) { xorriso->boot_image_emul= 0; xorriso->boot_emul_default= 0; } else if(strcmp(treatpt + 10, "hard_disk") == 0) { xorriso->boot_image_emul= 1; xorriso->boot_emul_default= 0; } else if(strcmp(treatpt + 10, "floppy") == 0 || strcmp(treatpt + 10, "diskette") == 0) { xorriso->boot_image_emul= 2; xorriso->boot_emul_default= 0; } else { sprintf(xorriso->info_text, "-boot_image %s : Unknown media_type : %s", formpt, treatpt + 10); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } is_change= 1; } else if(strncmp(treatpt, "hfsplus_serial=", 15) == 0) { ret= Hex_to_bin(treatpt + 15, 8, &bin_count, (unsigned char *) sn, 0); if(ret <= 0 || bin_count != 8) { sprintf(xorriso->info_text, "boot_image %s : Malformed hfsplus_serial : %s", formpt, treatpt + 15); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); Xorriso_msgs_submit(xorriso, 0, "Expected is a string of 16 hex digits [0-9a-fA-F]", 0, "HINT", 0); ret= 0; goto ex; } else { memcpy(xorriso->hfsp_serial_number, sn, 8); } is_change= 1; } else if(strncmp(treatpt, "hfsplus_block_size=", 19) == 0) { u= 0; sscanf(treatpt + 19, "%u", &u); if(u != 0 && u!= 512 && u != 2048) { sprintf(xorriso->info_text, "boot_image %s : Malformed hfsplus_block_size : %s", formpt, treatpt + 19); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); Xorriso_msgs_submit(xorriso, 0, "Expected are 0, 512, or 2048", 0, "HINT", 0); ret= 0; goto ex; } xorriso->hfsp_block_size= u; is_change= 1; } else if(strncmp(treatpt, "apm_block_size=", 15) == 0) { u= 0; sscanf(treatpt + 15, "%u", &u); if(u != 0 && u!= 512 && u != 2048) { sprintf(xorriso->info_text, "boot_image %s : Malformed apm_block_size : %s", formpt, treatpt + 15); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); Xorriso_msgs_submit(xorriso, 0, "Expected are 0, 512, or 2048", 0, "HINT", 0); ret= 0; goto ex; } xorriso->apm_block_size= u; is_change= 1; } else if(strncmp(treatpt, "efi_boot_part=", 14) == 0) { if(Sfile_str(xorriso->efi_boot_partition, treatpt + 14, 0) <= 0) {ret= -1; goto ex;} Xorriso_warn_if_not_exist(xorriso, "-boot_image ", treatment, treatpt + 14, 1); is_change= 1; } else if(strncmp(treatpt, "prep_boot_part=", 15) == 0) { if(Sfile_str(xorriso->prep_partition, treatpt + 15, 0) <= 0) {ret= -1; goto ex;} Xorriso_warn_if_not_exist(xorriso, "-boot_image ", treatment, treatpt + 15, 1); is_change= 1; } else if(strncmp(treatpt, "chrp_boot_part=", 15) == 0) { if(strcmp(treatpt + 15, "on") == 0) { xorriso->system_area_options= (xorriso->system_area_options & ~0x3cfc) | 0x400; } else if(strcmp(treatpt + 15, "off") == 0) { xorriso->system_area_options= xorriso->system_area_options & ~0x3c00; } else { sprintf(xorriso->info_text, "-boot_image %s chrp_boot_part: unknown mode : %s", formpt, treatpt + 15); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); {ret= 0; goto ex;} } is_change= 1; } else if(strncmp(treatpt, "isohybrid=", 10) == 0 && strcmp(formpt, "isolinux")==0) { #ifdef Xorriso_with_isohybriD if(strcmp(treatpt + 10, "off") == 0) xorriso->boot_image_isohybrid= 0; else if(strcmp(treatpt + 10, "auto") == 0) xorriso->boot_image_isohybrid= 1; else if(strcmp(treatpt + 10, "on") == 0) xorriso->boot_image_isohybrid= 2; else if(strcmp(treatpt + 10, "force") == 0) xorriso->boot_image_isohybrid= 3; else { sprintf(xorriso->info_text, "Unrecognized keyword with -boot_image %s %s", form, treatment); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text, "Allowed with isohybrid= are: off , auto , on , force"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); {ret= 0; goto ex;} } is_change= 1; #else if(strcmp(treatpt + 10, "off") == 0) { xorriso->boot_image_isohybrid= 0; } else { sprintf(xorriso->info_text, "isohybrid MBR generation has been disabled on request of its inventor H. Peter Anvin on 31 Mar 2010"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text, "It has been replaced by -boot_image isolinux system_area=External-File"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); } #endif /* ! Xorriso_with_isohybriD */ } else if(strncmp(treatpt, "grub2_mbr=", 10) == 0) { ret= Xorriso_coordinate_system_area(xorriso, 0, (1 << 14), eff_path, 1 | 2); if(ret <= 0) goto ex; if(strcmp(treatpt + 10, ".") == 0) { ret= Xorriso_set_system_area_path(xorriso, "", 0); } else { /* (checks also for existence of the disk file) */ ret= Xorriso_set_system_area_path(xorriso, treatpt + 10, 0); } if(ret <= 0) goto ex; if(treatpt [10] == 0) xorriso->system_area_options&= ~0x4000; else xorriso->system_area_options|= 0x4000; } else if(strncmp(treatpt, "mbr_force_bootable=", 19) == 0) { if(strcmp(treatpt + 19, "off") == 0) { xorriso->system_area_options&= ~(1 << 15); } else if(strcmp(treatpt + 19, "on") == 0) { xorriso->system_area_options|= (1 << 15); } else { sprintf(xorriso->info_text, "-boot_image %s mbr_force_bootable=: unknown mode : %s", formpt, treatpt + 19); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); {ret= 0; goto ex;} } is_change= 1; } else if(strncmp(treatpt, "gpt_iso_bootable=", 17) == 0) { if(strcmp(treatpt + 17, "off") == 0) { xorriso->system_area_options&= ~(1 << 16); } else if(strcmp(treatpt + 17, "on") == 0) { xorriso->system_area_options|= (1 << 16); } else { sprintf(xorriso->info_text, "-boot_image %s gpt_iso_bootable=: unknown mode : %s", formpt, treatpt + 17); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); {ret= 0; goto ex;} } is_change= 1; } else if(strncmp(treatpt, "gpt_iso_not_ro=", 15) == 0) { if(strcmp(treatpt + 15, "off") == 0) { xorriso->system_area_options&= ~(1 << 17); } else if(strcmp(treatpt + 15, "on") == 0) { xorriso->system_area_options|= (1 << 17); } else { sprintf(xorriso->info_text, "-boot_image %s gpt_iso_not_ro=: unknown mode : %s", formpt, treatpt + 15); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); {ret= 0; goto ex;} } is_change= 1; } else { was_ok= 0; } if(is_change) Xorriso_set_change_pending(xorriso, 1); if(!was_ok) { sprintf(xorriso->info_text, "Unrecognized options with -boot_image: %s %s", form, treatment); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } ret= 1; ex: Xorriso_free_meM(eff_path); return(ret); } /* Option -calm_drive */ int Xorriso_option_calm_drive(struct XorrisO *xorriso, char *which, int flag) { int gu_flag= 0, ret; if(strcmp(which,"in")==0) gu_flag= 1; else if(strcmp(which,"out")==0) gu_flag= 2; else if(strcmp(which,"on")==0) { xorriso->do_calm_drive|= 1; } else if(strcmp(which,"off")==0) { xorriso->do_calm_drive&= ~1; } else if(strcmp(which,"revoke")==0) { gu_flag= 7; } else gu_flag= 3; ret= Xorriso_drive_snooze(xorriso, gu_flag); return(ret); } /* Option -cd alias -cdi */ int Xorriso_option_cdi(struct XorrisO *xorriso, char *iso_rr_path, int flag) { char *path= NULL, *eff_path= NULL, *namept; int ret; Xorriso_alloc_meM(path, char, SfileadrL); Xorriso_alloc_meM(eff_path, char, SfileadrL); if (strlen(iso_rr_path)>sizeof(xorriso->wdi)) { sprintf(xorriso->info_text,"-cdi: iso_rr_path too long (%d > %d)", (int) strlen(iso_rr_path), (int) sizeof(xorriso->wdi)-1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } Xorriso_warn_of_wildcards(xorriso, iso_rr_path, 1); sprintf(xorriso->info_text,"previous working directory:\n"); Xorriso_info(xorriso,0); Xorriso_esc_filepath(xorriso, xorriso->wdi, xorriso->result_line, 0); if(xorriso->sh_style_result == 0 || xorriso->wdi[0] == 0) strcat(xorriso->result_line, "/"); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso,0); if(strcmp(iso_rr_path,"/")==0 || iso_rr_path[0]==0) { strcpy(xorriso->wdi,""); Xorriso_option_pwdi(xorriso, 0); {ret= 1; goto ex;} } else if(iso_rr_path[0]!='/') { strcpy(path, xorriso->wdi); if(Sfile_add_to_path(path,iso_rr_path,0)<=0) {ret= -1; goto ex;} } else { if(Sfile_str(path,iso_rr_path,0)<=0) {ret= -1; goto ex;} } ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, path, eff_path, 1); if(ret<0) goto ex; if(ret==0) { sprintf(xorriso->info_text, "-cdi: not existing yet in ISO image : "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, path, eff_path, 2); if(ret<=0) goto ex; } else if(ret!=2) { sprintf(xorriso->info_text, "-cdi: not a directory : "); Text_shellsafe(eff_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } Xorriso_truncate_path_comps(xorriso, eff_path, path, &namept, 0); strcpy(xorriso->wdi, namept); Xorriso_option_pwdi(xorriso, 0); ret= 1; ex:; Xorriso_free_meM(path); Xorriso_free_meM(eff_path); return(ret); } /* Option -cdx */ int Xorriso_option_cdx(struct XorrisO *xorriso, char *disk_path, int flag) { char *path= NULL, *eff_path= NULL; int ret; Xorriso_alloc_meM(path, char, SfileadrL); Xorriso_alloc_meM(eff_path, char, SfileadrL); if (strlen(disk_path)>sizeof(xorriso->wdx)) { sprintf(xorriso->info_text,"-cdx: disk_path too long (%d > %d)", (int) strlen(disk_path), (int) sizeof(xorriso->wdx)-1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } Xorriso_warn_of_wildcards(xorriso, disk_path, 1|2); sprintf(xorriso->info_text,"previous working directory on hard disk:\n"); Xorriso_info(xorriso,0); Xorriso_esc_filepath(xorriso, xorriso->wdx, xorriso->result_line, 0); if(xorriso->sh_style_result == 0 || xorriso->wdx[0] == 0) strcat(xorriso->result_line, "/"); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso,0); if(strcmp(disk_path,"/")==0) { strcpy(xorriso->wdx,""); Xorriso_option_pwdx(xorriso, 0); {ret= 1; goto ex;} } else if(disk_path[0]!='/') { strcpy(path, xorriso->wdx); if(Sfile_add_to_path(path,disk_path,0)<=0) {ret= -1; goto ex;} } else { if(Sfile_str(path,disk_path,0)<=0) {ret= -1; goto ex;} } ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, path, eff_path, 2|4); if(ret<=0) goto ex; if(eff_path[0]) { ret= Sfile_type(eff_path,1|4|8); if(ret<0) { Xorriso_msgs_submit(xorriso, 0, eff_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text,"-cdx: file not found : "); Text_shellsafe(eff_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(ret!=2) { Xorriso_msgs_submit(xorriso, 0, eff_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "-cdx: not a directory : "); Text_shellsafe(eff_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } } if(Sfile_str(xorriso->wdx,eff_path,0)<=0) {ret= -1; goto ex;} Xorriso_option_pwdx(xorriso, 0); ret= 1; ex:; Xorriso_free_meM(path); Xorriso_free_meM(eff_path); return(ret); } /* Option -changes_pending */ int Xorriso_option_changes_pending(struct XorrisO *xorriso, char *state, int flag) { if(strcmp(state, "no") == 0) xorriso->volset_change_pending= 0; else if(strcmp(state, "yes") == 0) xorriso->volset_change_pending= 1; else if(strcmp(state, "mkisofs_printed") == 0) xorriso->volset_change_pending= 2; else if(strcmp(state, "show_status") == 0) { strcpy(xorriso->result_line, "-changes_pending "); if(xorriso->volset_change_pending == 0) strcat(xorriso->result_line, "no"); else if(xorriso->volset_change_pending == 2) strcat(xorriso->result_line, "mkisofs_printed"); else strcat(xorriso->result_line, "yes"); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso,0); } else { sprintf(xorriso->info_text, "-changes_pending: unknown state code '%s'", state); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -charset */ /* @param flag bit0= set in_charset bit1= set out_charset bit2= set local_charset */ int Xorriso_option_charset(struct XorrisO *xorriso, char *name, int flag) { int ret; char *name_pt= NULL, *local_charset; iconv_t iconv_ret= (iconv_t) -1; if(name != NULL) if(name[0] != 0) name_pt= name; if(flag & 4) { ret= Xorriso_set_local_charset(xorriso, name_pt, 0); if(ret <= 0) return(ret); } if(flag & 1) { if(name_pt != NULL) { Xorriso_get_local_charset(xorriso, &local_charset, 0); iconv_ret= iconv_open(local_charset, name_pt); if(iconv_ret == (iconv_t) -1) { sprintf(xorriso->info_text, "-%scharset: Cannot convert from character set ", flag & 2 ? "" : "in_"); Text_shellsafe(name_pt, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); return(0); } else iconv_close(iconv_ret); } if(Sregex_string(&(xorriso->in_charset), name_pt, 0) <= 0) { Xorriso_no_malloc_memory(xorriso, NULL, 0); return(-1); } } if(flag & 2) { if(name_pt != NULL) { Xorriso_get_local_charset(xorriso, &local_charset, 0); iconv_ret= iconv_open(local_charset, name_pt); if(iconv_ret == (iconv_t) -1) { sprintf(xorriso->info_text, "-%scharset: Cannot convert to charset ", flag & 1 ? "" : "out_"); Text_shellsafe(name_pt, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); return(0); } else iconv_close(iconv_ret); } if(Sregex_string(&(xorriso->out_charset), name_pt, 0) <= 0) { Xorriso_no_malloc_memory(xorriso, NULL, 0); return(-1); } } if(flag & 3) { if(name_pt == NULL) Xorriso_get_local_charset(xorriso, &name_pt, 0); sprintf(xorriso->info_text, "Character set for %sconversion is now: ", (flag & 3) == 1 ? "input " : (flag & 3) == 2 ? "output " : ""); Text_shellsafe(name_pt, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } return(1); } /* Commands -chattr alias -chattri and -chattr_r alias chattr_ri */ /* @param flag bit0=recursive -chattr_r */ int Xorriso_option_chattri(struct XorrisO *xorriso, char *chattr_text, int argc, char **argv, int *idx, int flag) { int i, ret, was_failure= 0, end_idx, fret, operator; int optc= 0; uint64_t lfa_flags; char **optv= NULL; struct FindjoB *job= NULL; struct stat dir_stbuf; ret= Xorriso_opt_args(xorriso, "-chattr", argc, argv, *idx, &end_idx, &optc, &optv, 0); if(ret <= 0) goto ex; ret= Xorriso_decode_chattr_arg(xorriso, chattr_text, &lfa_flags, &operator, 0); if(ret <= 0) goto ex; for(i= 0; i<optc; i++) { if(flag&1) { /* Try out whether this file is suitable for chattr */ ret= Xorriso_set_lfa_flags(xorriso, NULL, optv[i], chattr_text, lfa_flags, operator, 1 | 8); if(ret <= 0 || ret == 2) goto partial_failure; ret= Findjob_new(&job, optv[i], 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "-chattr_r", 0); {ret= -1; goto ex;} } Findjob_set_action_chattr(job, 61, lfa_flags, operator, 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, optv[i], &dir_stbuf, 0, 0); Findjob_destroy(&job, 0); } else { ret= Xorriso_set_lfa_flags(xorriso, NULL, optv[i], "", lfa_flags, operator, 1); } if(ret>0 && !xorriso->request_to_abort) continue; /* regular bottom of loop */ partial_failure:; was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; ret= 0; goto ex; } ret= 1; ex:; (*idx)= end_idx; Xorriso_opt_args(xorriso, "-chattr", argc, argv, *idx, &end_idx, &optc, &optv, 256); Findjob_destroy(&job, 0); if(ret<=0) return(ret); return(!was_failure); } /* Options -check_md5 and -check_md5_r @param flag bit0= issue summary message bit1= do not reset pacifier, no final pacifier message >>> bit2= do not issue pacifier messages at all bit3= recursive: -check_md5_r */ int Xorriso_option_check_md5(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int ret, i, mem_pci, end_idx, fret, sev, do_report= 0; int optc= 0; char **optv= NULL, *cpt, *severity= "ALL", off_severity[20]; struct FindjoB *job= NULL; double mem_lut= 0.0; mem_pci= xorriso->pacifier_interval; ret= Xorriso_opt_args(xorriso, "-check_md5", argc, argv, *idx + 1, &end_idx, &optc, &optv, 128); if(ret<=0) goto ex; /* Interpret argv[*idx] as severity */ if(argc <= *idx) { sprintf(xorriso->info_text, "-check_md5: No event severity given for case of mismatch"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } severity= argv[*idx]; Xorriso__to_upper(severity, off_severity, (int) sizeof(off_severity), 0); severity= off_severity; ret= Xorriso__text_to_sev(severity, &sev, 0); if(ret<=0) { sprintf(xorriso->info_text, "-check_md5: Not a known severity name : "); Text_shellsafe(severity, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto ex; } if(!(flag & (2 | 4))) { Xorriso_pacifier_reset(xorriso, 0); mem_lut= xorriso->last_update_time; } xorriso->pacifier_interval= 5.0; xorriso->find_check_md5_result= 0; if(optc == 0) { ret= Xorriso_check_session_md5(xorriso, severity, 0); do_report= 1; goto ex; } for(i= 0; i < optc; i++) { if(flag & 8) { ret= Findjob_new(&job, optv[i], 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "-check_md5_r", 0); {ret= -1; goto ex;} } Findjob_set_action_target(job, 35, severity, 0); cpt= optv[i]; ret= Xorriso_findi_sorted(xorriso, job, (off_t) 0, 1, &cpt, 0); Findjob_destroy(&job, 0); if(ret > 0) ret= xorriso->find_compare_result; else { ret= -1; xorriso->find_check_md5_result|= 2; } } else { ret= Xorriso_check_md5(xorriso, NULL, optv[i], 4); if(ret < 0) xorriso->find_check_md5_result|= 2; else if(ret == 0) xorriso->find_check_md5_result|= 1; else if(ret == 1) xorriso->find_check_md5_result|= 8; else if(ret == 2) xorriso->find_check_md5_result|= 4; } if(ret>0 && !xorriso->request_to_abort) continue; /* regular bottom of loop */ fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; ret= 0; goto report_outcome; } ret= 1; report_outcome:; do_report= 1; ex:; if(!(flag & (2 | 4))) { xorriso->pacifier_interval= mem_pci; if(mem_lut!=xorriso->last_update_time && !(flag&2)) Xorriso_pacifier_callback(xorriso, "content bytes read", xorriso->pacifier_count, 0, "", 1 | 8 | 32); } if(do_report) { if(optc == 0) { if(ret <= 0) { sprintf(xorriso->result_line, "MD5 MISMATCH WITH DATA OF LOADED SESSION !\n"); Xorriso_result(xorriso,0); if(strcmp(severity, "ALL") != 0) { sprintf(xorriso->info_text, "Event triggered by MD5 comparison mismatch"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, severity, 0); } } else { sprintf(xorriso->result_line, "Ok, session data match recorded md5.\n"); Xorriso_result(xorriso,0); } } else { Xorriso_report_md5_outcome(xorriso, severity, 0); } } (*idx)= end_idx; Xorriso_opt_args(xorriso, "-getfacl", argc, argv, *idx, &end_idx, &optc, &optv, 256); Findjob_destroy(&job, 0); if(ret <= 0) return(ret); return((xorriso->find_check_md5_result & 3) == 0); } /* Option -check_media */ int Xorriso_option_check_media(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int ret, quality, pass, was_md5= 0, was_event= 0; off_t i, count, lba, blocks; int end_idx, old_idx, os_errno, to_info= 0; char quality_name[80], *head_buffer= NULL; struct SpotlisT *spotlist= NULL; struct CheckmediajoB *job= NULL; struct FindjoB *findjob= NULL; struct stat dir_stbuf; old_idx= *idx; end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, 1); (*idx)= end_idx; Xorriso_alloc_meM(head_buffer, char, 64 * 1024); ret= Checkmediajob_new(&job, 0); if(ret <= 0) goto ex; ret= Xorriso_check_media_setup_job(xorriso, job, argv, old_idx, end_idx, 0); if(ret <= 0) goto ex; to_info= (strcmp(job->data_to_path, "-") == 0); if((job->report_mode == 1 || job->report_mode == 2) && job->use_dev == 1) { sprintf(xorriso->info_text, "-check_media: cannot report=*files while use=outdef"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(job->patch_lba0 && job->data_to_path[0] == 0) { sprintf(xorriso->info_text, "-check_media: cannot apply patch_lba0= while data_to= has empty value"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(job->patch_lba0 && to_info) { sprintf(xorriso->info_text, "-check_media: cannot apply patch_lba0= while data_to= is \"-\""); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(job->use_dev == 2) { if(job->sector_map_path[0] == 0) { sprintf(xorriso->info_text, "-check_media: option use=sector_map but sector_map=''"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } ret= Sectorbitmap_from_file(&(job->sector_map), job->sector_map_path, xorriso->info_text, &os_errno, 0); if(ret <= 0) { if(xorriso->info_text[0]) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, os_errno, "FAILURE", 0); goto ex; } ret= Xorriso_sectormap_to_spotlist(xorriso, job, &spotlist, 0); if(ret <= 0) goto ex; Sectorbitmap_destroy(&(xorriso->in_sector_map), 0); ret= Sectorbitmap_clone(job->sector_map, &(xorriso->in_sector_map), 0); if(ret <= 0) goto ex; } else { ret= Xorriso_check_media(xorriso, &spotlist, job, 0); if(ret <= 0) goto ex; } if(job->patch_lba0) { ret= Xorriso_open_job_data_to(xorriso, job, 0); if(ret <= 0) goto ex; if(ret == 1) { ret= Xorriso_update_iso_lba0(xorriso, job->patch_lba0_msc1, (off_t) 0, head_buffer, job, (8 * (job->patch_lba0 == 1)) | 4 | (job->patch_lba0_msc1 < 0)); if(ret <= 0) goto ex; } } if(job->report_mode == 0 || job->report_mode == 2) { /* report blocks */ for(pass= 0; pass < 2; pass++) { if(pass == 0) { sprintf(xorriso->result_line, "Media checks : lba , size , quality\n"); } else { if(!was_md5) break; sprintf(xorriso->result_line, "MD5 checks : lba , size , result\n"); } if(to_info) { strcpy(xorriso->info_text, xorriso->result_line); Xorriso_info(xorriso, 0); } else { Xorriso_result(xorriso, 0); } count= Spotlist_count(spotlist, 0); for(i= 0; i < count; i++) { ret= Spotlist_get_item(spotlist, i, &lba, &blocks, &quality, 0); if(ret <= 0) continue; if(pass == 0) { if(quality == Xorriso_read_quality_md5_mismatcH || quality == Xorriso_read_quality_unreadablE) { was_event= 1; } if(quality == Xorriso_read_quality_md5_matcH || quality == Xorriso_read_quality_md5_mismatcH) { was_md5= 1; continue; } } else if(pass == 1 && !(quality == Xorriso_read_quality_md5_matcH || quality == Xorriso_read_quality_md5_mismatcH)) continue; sprintf(xorriso->result_line, "%s: %10.f , %10.f , %s\n", pass == 0 ? "Media region " : "MD5 tag range", (double) lba, (double) blocks, Spotlist__quality_name(quality, quality_name, xorriso->check_media_bad_limit, 0)); if(to_info) { strcpy(xorriso->info_text, xorriso->result_line); Xorriso_info(xorriso, 0); } else { Xorriso_result(xorriso, 0); } } } } if(job->report_mode == 1 || job->report_mode == 2) { /* report files */ ret= Findjob_new(&findjob, "/", 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "-check_media report=files", 0); {ret= -1; goto ex;} } Findjob_set_damage_filter(findjob, 1, 0); Findjob_set_action_target(findjob, 21, NULL, 0); ret= Xorriso_findi(xorriso, findjob, NULL, (off_t) 0, NULL, "/", &dir_stbuf, 0, 0); Findjob_destroy(&findjob, 0); if(ret <= 0) goto ex; } ret= 1; ex:; if(was_event && strcmp(job->event_severity, "ALL") != 0) { sprintf(xorriso->info_text, "Event triggered by media read error or MD5 comparison mismatch"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, job->event_severity, 0); } Spotlist_destroy(&spotlist, 0); Checkmediajob_destroy(&job, 0); Xorriso_free_meM(head_buffer); return(ret); } /* Option -check_media_defaults */ int Xorriso_option_check_media_defaults(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int ret, old_idx, end_idx; struct CheckmediajoB *job= NULL; old_idx= *idx; end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, 1); (*idx)= end_idx; ret= Checkmediajob_new(&job, 0); if(ret <= 0) goto ex; ret= Xorriso_check_media_setup_job(xorriso, job, argv, old_idx, end_idx, 0); if(ret <= 0) goto ex; Checkmediajob_destroy(&(xorriso->check_media_default), 0); xorriso->check_media_default= job; job= NULL; ret= 1; ex:; Checkmediajob_destroy(&job, 0); return(ret); } /* Option -chgrp alias -chgrpi , chgrp_r alias chgrpi */ /* @param flag bit0=recursive (-chgrp_r) */ int Xorriso_option_chgrpi(struct XorrisO *xorriso, char *gid, int argc, char **argv, int *idx, int flag) { int i, ret, was_failure= 0, end_idx, fret; gid_t gid_number; int optc= 0; char **optv= NULL; struct FindjoB *job= NULL; struct stat dir_stbuf; ret= Xorriso_opt_args(xorriso, "-chgrpi", argc, argv, *idx, &end_idx, &optc, &optv, 0); if(ret<=0) goto ex; ret= Xorriso_convert_gidstring(xorriso, gid, &gid_number, 0); if(ret<=0) goto ex; for(i= 0; i<optc; i++) { if(flag&1) { ret= Findjob_new(&job, optv[i], 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "-chgrp_r", 0); {ret= -1; goto ex;} } Findjob_set_action_chgrp(job, gid_number, 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, optv[i], &dir_stbuf, 0, 0); Findjob_destroy(&job, 0); } else ret= Xorriso_set_gid(xorriso, optv[i], gid_number, 0); if(ret>0 && !xorriso->request_to_abort) continue; /* regular bottom of loop */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; goto ex; } ret= 1; ex:; (*idx)= end_idx; Xorriso_opt_args(xorriso, "-chgrpi", argc, argv, *idx, &end_idx, &optc, &optv, 256); /* clean up */ if(ret<=0) return(ret); Findjob_destroy(&job, 0); return(!was_failure); } /* Option -chmod alias -chmodi , -chmod_r alias chmod_ri */ /* @param flag bit0=recursive (-chmod_r) */ int Xorriso_option_chmodi(struct XorrisO *xorriso, char *mode, int argc, char **argv, int *idx, int flag) { int i, ret, was_failure= 0, end_idx, fret; mode_t mode_and= ~0, mode_or= 0; int optc= 0; char **optv= NULL; struct FindjoB *job= NULL; struct stat dir_stbuf; ret= Xorriso_opt_args(xorriso, "-chmodi", argc, argv, *idx, &end_idx, &optc, &optv, 0); if(ret<=0) goto ex; ret= Xorriso_convert_modstring(xorriso, "-chmodi", mode, &mode_and, &mode_or, 0); if(ret<=0) goto ex; for(i= 0; i<optc; i++) { if(flag&1) { ret= Findjob_new(&job, optv[i], 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "-chmod_r", 0); {ret= -1; goto ex;} } Findjob_set_action_chmod(job, mode_and, mode_or, 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, optv[i], &dir_stbuf, 0, 0); Findjob_destroy(&job, 0); } else { ret= Xorriso_set_st_mode(xorriso, optv[i], mode_and, mode_or, 0); } if(ret>0 && !xorriso->request_to_abort) continue; /* regular bottom of loop */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; ret= 0; goto ex; } ret= 1; ex:; (*idx)= end_idx; Xorriso_opt_args(xorriso, "-chmodi", argc, argv, *idx, &end_idx, &optc, &optv, 256); Findjob_destroy(&job, 0); if(ret<=0) return(ret); return(!was_failure); } /* Option -chown alias -chowni , chown_r alias chown_ri */ /* @param flag bit0=recursive (-chown_r) */ int Xorriso_option_chowni(struct XorrisO *xorriso, char *uid, int argc, char **argv, int *idx, int flag) { int i, ret, was_failure= 0, end_idx, fret; uid_t uid_number; int optc= 0; char **optv= NULL; struct FindjoB *job= NULL; struct stat dir_stbuf; ret= Xorriso_opt_args(xorriso, "-chowni", argc, argv, *idx, &end_idx, &optc, &optv, 0); if(ret<=0) goto ex; ret= Xorriso_convert_uidstring(xorriso, uid, &uid_number, 0); if(ret<=0) goto ex; for(i= 0; i<optc; i++) { if(flag&1) { ret= Findjob_new(&job, optv[i], 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "-chown_r", 0); {ret= -1; goto ex;} } Findjob_set_action_chown(job, uid_number, 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, optv[i], &dir_stbuf, 0, 0); Findjob_destroy(&job, 0); } else ret= Xorriso_set_uid(xorriso, optv[i], uid_number, 0); if(ret>0 && !xorriso->request_to_abort) continue; /* regular bottom of loop */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; ret= 0; goto ex; } ret= 1; ex:; (*idx)= end_idx; Xorriso_opt_args(xorriso, "-chowni", argc, argv, *idx, &end_idx, &optc, &optv, 256); Findjob_destroy(&job, 0); if(ret<=0) return(ret); return(!was_failure); } /* Option -clone */ int Xorriso_option_clone(struct XorrisO *xorriso, char *origin, char *dest, int flag) { int ret; ret= Xorriso_clone_tree(xorriso, NULL, origin, dest, 0); return(ret); } /* Option -close "on"|"off"|"as_needed" */ int Xorriso_option_close(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "off") == 0) { xorriso->do_close= 0; xorriso->auto_close= 0; } else if(strcmp(mode, "as_needed") == 0) { xorriso->do_close= 0; xorriso->auto_close= 1; } else { xorriso->do_close= 1; xorriso->auto_close= 0; } return(1); } /* Option -close_damaged */ int Xorriso_option_close_damaged(struct XorrisO *xorriso, char *mode, int flag) { int ret, force= 0; if(strcmp(mode, "as_needed") == 0 || strcmp(mode, "") == 0) force= 0; else if(strcmp(mode, "force") == 0) force= 1; else { sprintf(xorriso->info_text, "-close_damaged: unknown mode "); Text_shellsafe(mode, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= Xorriso_reassure(xorriso, "-close_damaged", "Close damaged track and session", 0); if(ret <= 0) {ret= 2; goto ex;} ret= Xorriso_close_damaged(xorriso, force); if(ret <= 0) goto ex; ret= 1; ex:; return(ret); } /* Option -close_filter_list */ int Xorriso_option_close_filter_list(struct XorrisO *xorriso, int flag) { xorriso->filter_list_closed= 1; return(1); } /* Option -commit */ /* @param flag bit0= leave indrive and outdrive acquired as they were, i.e. do not acquire outdrive as new in-out-drive bit1= do not perform eventual -reassure @return <=0 error , 1 success, 2 revoked by -reassure , 3 no change pending */ int Xorriso_option_commit(struct XorrisO *xorriso, int flag) { int ret; char eternal_problem_status_text_mem[80]; xorriso->commit_attempts++; if(!Xorriso_change_is_pending(xorriso, 0)) { sprintf(xorriso->info_text, "-commit: No image modifications pending"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); {ret= 3; goto ex;} } if(!(flag&2)) { ret= Xorriso_reassure(xorriso, "-commit", "write the pending image changes to the medium", 0); if(ret<=0) {ret= 2; goto ex;} } Xorriso_process_errfile(xorriso, 0, "burn session start", 0, 1); Xorriso_get_problem_status(xorriso, eternal_problem_status_text_mem, 1); ret= Xorriso_write_session(xorriso, 0); if(ret == 2) { if(Xorriso__severity_cmp("WARNING", eternal_problem_status_text_mem) > 0) strcpy(eternal_problem_status_text_mem, "WARNING"); Xorriso_set_problem_status(xorriso, eternal_problem_status_text_mem, 1); ret= Xorriso_retry_write_session(xorriso, 0); } Xorriso_process_errfile(xorriso, 0, "burn session end", 0, 1); if(ret<=0) goto ex; Xorriso_write_session_log(xorriso, 0); xorriso->volset_change_pending= 0; xorriso->no_volset_present= 0; if(flag&1) {ret= 1; goto ex;} if(Sregex_string(&(xorriso->in_charset), xorriso->out_charset, 0) <= 0) {ret= -1; goto ex;} if(xorriso->grow_blindly_msc2>=0) ret= Xorriso_option_dev(xorriso, "", 3|4); else { xorriso->displacement= 0; xorriso->displacement_sign= 0; ret= Xorriso_reaquire_outdev(xorriso, 3); if(xorriso->in_drive_handle == NULL) xorriso->image_start_mode= 0; /* session setting is invalid by now */ } ex:; return(ret); } /* Option -commit_eject */ /* @return <=0 error , 1 success, 2 revoked by -reassure */ int Xorriso_option_commit_eject(struct XorrisO *xorriso, char *which, int flag) { int ret, eret; ret= Xorriso_option_commit(xorriso, 1); if(ret<=0 || ret==2 || ret==3) return(ret); if(strcmp(which, "none")==0) eret= 1; else eret= Xorriso_option_eject(xorriso, which, 1); ret= Xorriso_option_dev(xorriso, "", 3|4); if(eret<ret) return(eret); return(ret); } /* Options -compare and -compare_r @param flag bit0= issue summary message bit1= do not reset pacifier, no final pacifier message bit2= do not issue pacifier messages at all bit3= recursive: -compare_r */ int Xorriso_option_compare(struct XorrisO *xorriso, char *disk_path, char *iso_path, int flag) { int ret, mem_pci, zero= 0, result, follow_links; double mem_lut= 0.0; char *ipth, *argv[6], *eff_origin= NULL, *eff_dest= NULL; Xorriso_alloc_meM(eff_origin, char, SfileadrL); Xorriso_alloc_meM(eff_dest, char, SfileadrL); ipth= iso_path; if(ipth[0]==0) ipth= disk_path; if(disk_path[0]==0) { sprintf(xorriso->info_text, "-compare: Empty disk_path given"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 1); {ret= 0; goto ex;} } ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, disk_path, eff_origin, 2|4|8); if(ret<=0) goto ex; ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, ipth, eff_dest, 2|8); if(ret<=0) goto ex; if(xorriso->disk_excl_mode&8) ret= Xorriso_path_is_excluded(xorriso, eff_origin, 1); else ret= 0; if(ret!=0) goto report_outcome; if(!(flag&2)) { Xorriso_pacifier_reset(xorriso, 0); mem_lut= xorriso->last_update_time; } mem_pci= xorriso->pacifier_interval; xorriso->pacifier_interval= 5.0; if(flag&8) { xorriso->find_compare_result= 1; argv[0]= eff_dest; argv[1]= "-exec"; argv[2]= "compare"; argv[3]= eff_origin; zero= 0; ret= Xorriso_option_find(xorriso, 4, argv, &zero, 2); /* -findi */ if(ret>0) { argv[0]= eff_origin; argv[1]= "-exec"; argv[2]= "not_in_iso"; argv[3]= eff_dest; zero= 0; ret= Xorriso_option_find(xorriso, 4, argv, &zero, 1|2); /* -findx */ if(ret>0 && !xorriso->do_follow_mount) { argv[0]= eff_origin; argv[1]= "-type"; argv[2]= "m"; argv[3]= "-exec"; argv[4]= "is_full_in_iso"; argv[5]= eff_dest; zero= 0; ret= Xorriso_option_find(xorriso, 6, argv, &zero, 1|2); /* -findx */ } if(ret>0) ret= xorriso->find_compare_result; else ret= -1; } else ret= -1; } else { follow_links= (xorriso->do_follow_links || xorriso->do_follow_param) << 28; ret= Xorriso_compare_2_files(xorriso, eff_origin, eff_dest, "", &result, 2 | follow_links | ((flag&4)<<27) | (1<<30)); } xorriso->pacifier_interval= mem_pci; if(mem_lut!=xorriso->last_update_time && !(flag&2)) Xorriso_pacifier_callback(xorriso, "content bytes read", xorriso->pacifier_count, 0, "", 1 | 8 | 32); report_outcome:; if(ret>0) { sprintf(xorriso->result_line, "Both file objects match as far as expectable.\n"); } else if(ret==0) { sprintf(xorriso->result_line, "Differences detected.\n"); } else { sprintf(xorriso->result_line, "Comparison failed due to error.\n"); } if(flag&1) Xorriso_result(xorriso,0); if(ret<0) goto ex; ret= 1; ex:; Xorriso_free_meM(eff_origin); Xorriso_free_meM(eff_dest); return(ret); } /* Option -compliance */ int Xorriso_option_compliance(struct XorrisO *xorriso, char *mode, int flag) { return(Xorriso_relax_compliance(xorriso, mode, 0)); } /* Command -concat */ int Xorriso_option_concat(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int ret, end_idx, optc= 0, progc= 0, iso_rr_start, prog_end_idx= -1; char **optv= NULL, **progv= NULL, *delimiter_mem= NULL; char *delimiter= NULL; /* Must be done before any goto ex; */ end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, 1); iso_rr_start= *idx + 2; if(xorriso->allow_restore <= 0) { sprintf(xorriso->info_text, "-concat: image content copies are not enabled by option -osirrox"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(end_idx - *idx < 3) { sprintf(xorriso->info_text, "-concat: Not enough arguments. Need mode, target, iso_rr_path."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(strcmp(argv[*idx], "pipe") == 0) { if(end_idx - *idx < 5) { sprintf(xorriso->info_text, "-concat pipe: Not enough arguments. Need delimiter, program path, delimiter, iso_rr_path."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } ret= Xorriso_check_thing_len(xorriso, argv[*idx + 1], sizeof(xorriso->list_delimiter), "-concat", "Delimiter", 0); if(ret <= 0) goto ex; Xorriso_alloc_meM(delimiter_mem, char, strlen(xorriso->list_delimiter) + 1); Xorriso_alloc_meM(delimiter, char, strlen(argv[*idx + 1]) + 1); strcpy(delimiter_mem, xorriso->list_delimiter); strcpy(delimiter, argv[*idx + 1]); strcpy(xorriso->list_delimiter, delimiter); ret= Xorriso_opt_args(xorriso, "-concat pipe", argc , argv, *idx + 2, &prog_end_idx, &progc, &progv, 4 | 128); strcpy(xorriso->list_delimiter, delimiter_mem); if(ret <= 0) goto ex; if(progc <= 0) { sprintf(xorriso->info_text, "-concat pipe: No program path given."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } iso_rr_start= prog_end_idx + 1; } ret= Xorriso_opt_args(xorriso, "-concat", argc , argv, iso_rr_start, &end_idx, &optc, &optv, 128); if(ret <= 0) goto ex; if(optc <= 0) { sprintf(xorriso->info_text, "-concat: No iso_rr_paths given."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); ret= 0; goto ex; } ret= Xorriso_concat(xorriso, argv[*idx], argv[*idx + 1], progc, progv, optc, optv, 0); ex:; if(progv != NULL) { if(delimiter_mem != NULL && delimiter != NULL) strcpy(xorriso->list_delimiter, delimiter); Xorriso_opt_args(xorriso, "-concat", argc, argv, *idx + 2, &prog_end_idx, &progc, &progv, 256); if(delimiter_mem != NULL && delimiter != NULL) strcpy(xorriso->list_delimiter, delimiter_mem); } Xorriso_opt_args(xorriso, "-concat", argc, argv, iso_rr_start, &end_idx, &optc, &optv, 256); Xorriso_free_meM(delimiter); Xorriso_free_meM(delimiter_mem); *idx= end_idx; return(ret); } /* Option -copyright_file */ int Xorriso_option_copyright_file(struct XorrisO *xorriso, char *name, int flag) { if(Xorriso_check_name_len(xorriso, name, (int) sizeof(xorriso->copyright_file), "-copyright_file", 0) <= 0) return(0); strcpy(xorriso->copyright_file, name); Xorriso_set_change_pending(xorriso, 1); return(1); } /* Option -cp_clone */ int Xorriso_option_cp_clone(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int i, end_idx_dummy, ret, is_dir= 0, was_failure= 0, fret, pass; char *eff_origin= NULL, *eff_dest= NULL, *dest_dir= NULL, *leafname= NULL; int optc= 0; char **optv= NULL; struct stat stbuf; Xorriso_alloc_meM(eff_origin, char, SfileadrL); Xorriso_alloc_meM(eff_dest, char, SfileadrL); Xorriso_alloc_meM(dest_dir, char, SfileadrL); Xorriso_alloc_meM(leafname, char, SfileadrL); ret= Xorriso_cpmv_args(xorriso, "-cp_clone", argc, argv, idx, &optc, &optv, eff_dest, 1); if(ret<=0) goto ex; if(ret == 1 && optc > 1) { nondir_exists:; sprintf(xorriso->info_text, "-cp_clone: Copy address already exists and is not a directory: "); Text_shellsafe(eff_dest, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } strcpy(dest_dir, eff_dest); if(optc == 1) { ret= Xorriso_iso_lstat(xorriso, eff_dest, &stbuf, 0); if(ret >= 0) { if(S_ISDIR(stbuf.st_mode))/* target directory exists */ is_dir= 1; else goto nondir_exists; } } else { is_dir= 1; ret= Xorriso_mkdir(xorriso, dest_dir, 1 | 2); if(ret < 0) {ret= -(ret != -1); goto ex;} } /* Pass 0 checks whether the way is clear, pass 1 does the cloning */ for(pass= 0; pass < 2; pass++) { for(i= 0; i<optc; i++) { ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, optv[i], eff_origin, !!pass); if(ret<=0 || xorriso->request_to_abort) goto problem_handler; if(is_dir) { ret= Sfile_leafname(eff_origin, leafname, 0); if(ret<=0) goto problem_handler; strcpy(eff_dest, dest_dir); ret= Sfile_add_to_path(eff_dest, leafname, 0); if(ret<=0) { sprintf(xorriso->info_text, "Effective path gets much too long (%d)", (int) (strlen(eff_dest)+strlen(leafname)+1)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto problem_handler; } } ret= Xorriso_iso_lstat(xorriso, eff_dest, &stbuf, 0); if(pass == 0) { /* It is ok if both are directories */; if(ret >= 0 && S_ISDIR(stbuf.st_mode)) { ret= Xorriso_iso_lstat(xorriso, eff_origin, &stbuf, 0); if (ret >= 0 && S_ISDIR(stbuf.st_mode)) ret= -1; } if(ret >= 0) { sprintf(xorriso->info_text, "Cloning: May not overwrite: "); Text_shellsafe(eff_dest, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto problem_handler; } } else { ret= Xorriso_clone_tree(xorriso, NULL, eff_origin, eff_dest, 1); if(ret <= 0) goto problem_handler; } continue; /* regular bottom of loop */ problem_handler:; was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; goto ex; } } ret= !was_failure; ex:; Xorriso_opt_args(xorriso, "-cp_clone", argc, argv, *idx, &end_idx_dummy, &optc, &optv, 256); Xorriso_free_meM(eff_origin); Xorriso_free_meM(eff_dest); Xorriso_free_meM(dest_dir); Xorriso_free_meM(leafname); return(ret); } /* Option -cpr alias -cpri */ int Xorriso_option_cpri(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int i, ret, is_dir= 0, was_failure= 0, fret, end_idx_dummy; char *eff_origin= NULL, *eff_dest= NULL, *dest_dir= NULL, *leafname= NULL; int optc= 0; char **optv= NULL; Xorriso_alloc_meM(eff_origin, char, SfileadrL); Xorriso_alloc_meM(eff_dest, char, SfileadrL); Xorriso_alloc_meM(dest_dir, char, SfileadrL); Xorriso_alloc_meM(leafname, char, SfileadrL); ret= Xorriso_cpmv_args(xorriso, "-cpri", argc, argv, idx, &optc, &optv, eff_dest, 1|2); if(ret<=0) goto ex; if(ret==2) { is_dir= 1; strcpy(dest_dir, eff_dest); } /* Perform graft-ins */ Xorriso_pacifier_reset(xorriso, 0); for(i= 0; i<optc && !xorriso->request_to_abort; i++) { ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, optv[i], eff_origin, 2|4); if(ret<=0 || xorriso->request_to_abort) goto problem_handler; if(is_dir) { ret= Sfile_leafname(eff_origin, leafname, 0); if(ret<=0) goto problem_handler; strcpy(eff_dest, dest_dir); ret= Sfile_add_to_path(eff_dest, leafname, 0); if(ret<=0) { sprintf(xorriso->info_text, "Effective path gets much too long (%d)", (int) (strlen(eff_dest)+ strlen(leafname)+1)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto problem_handler; } } ret= Xorriso_graft_in(xorriso, NULL, eff_origin, eff_dest, (off_t) 0, (off_t) 0, 0); if(ret<=0 || xorriso->request_to_abort) goto problem_handler; sprintf(xorriso->info_text, "Added to ISO image: %s '%s'='%s'\n", (ret>1 ? "directory" : "file"), (eff_dest[0] ? eff_dest : "/"), eff_origin); if(!(flag&1)) Xorriso_info(xorriso, 0); continue; /* regular bottom of loop */ problem_handler:; was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; goto ex; } Xorriso_pacifier_callback(xorriso, "files added", xorriso->pacifier_count, xorriso->pacifier_total, "", 1); ret= !was_failure; ex:; Xorriso_opt_args(xorriso, "-cpri", argc, argv, *idx, &end_idx_dummy, &optc, &optv, 256); Xorriso_free_meM(eff_origin); Xorriso_free_meM(eff_dest); Xorriso_free_meM(dest_dir); Xorriso_free_meM(leafname); return(ret); } /* Options -cpx , -cpax, -cp_rx , -cp_rax */ /* @param flag bit0= recursive (-cp_rx, -cp_rax) bit1= full property restore (-cpax, -cp_rax) */ int Xorriso_option_cpx(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int i, ret, is_dir= 0, was_failure= 0, fret, end_idx_dummy, problem_count; char *eff_origin= NULL, *eff_dest= NULL, *dest_dir= NULL, *leafname= NULL; char **eff_src_array= NULL, **eff_tgt_array= NULL; int optc= 0; char **optv= NULL; struct stat stbuf; Xorriso_alloc_meM(eff_origin, char, SfileadrL); Xorriso_alloc_meM(eff_dest, char, SfileadrL); Xorriso_alloc_meM(dest_dir, char, SfileadrL); Xorriso_alloc_meM(leafname, char, SfileadrL); ret= Xorriso_cpmv_args(xorriso, "-cp*x", argc, argv, idx, &optc, &optv, eff_dest, 1|4); if(ret<=0) goto ex; if(ret==2) { is_dir= 1; strcpy(dest_dir, eff_dest); } if(xorriso->allow_restore <= 0) { sprintf(xorriso->info_text, "-cpx: image-to-disk copies are not enabled by option -osirrox"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(xorriso->do_restore_sort_lba || !(xorriso->ino_behavior & 4)) { eff_src_array= calloc(optc, sizeof(char *)); eff_tgt_array= calloc(optc, sizeof(char *)); if(eff_src_array == NULL || eff_tgt_array == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } for(i= 0; i < optc; i++) eff_src_array[i]= eff_tgt_array[i]= NULL; } /* Perform copying */ Xorriso_pacifier_reset(xorriso, 0); for(i= 0; i<optc && !xorriso->request_to_abort; i++) { ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, optv[i], eff_origin, 2|8); if(ret<=0 || xorriso->request_to_abort) goto problem_handler; ret= Xorriso_iso_lstat(xorriso, eff_origin, &stbuf, 2|4); if(ret==-1) goto problem_handler; if(S_ISDIR(stbuf.st_mode) && !(flag&1)) { /* only allow directories if they actually represent split data files */ ret= 0; if(xorriso->do_concat_split) ret= Xorriso_is_split(xorriso, eff_origin, NULL, 4); if(ret<0) goto problem_handler; if(ret==0) { sprintf(xorriso->info_text, "-cpx: May not copy directory "); Text_shellsafe(eff_origin, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto problem_handler; } } if(is_dir && strcmp(eff_origin, "/")!=0) { ret= Sfile_leafname(eff_origin, leafname, 0); if(ret<=0) goto problem_handler; strcpy(eff_dest, dest_dir); ret= Sfile_add_to_path(eff_dest, leafname, 0); if(ret<=0) { sprintf(xorriso->info_text, "Effective path gets much too long (%d)", (int) (strlen(eff_dest)+strlen(leafname)+1)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto problem_handler; } } if(eff_src_array != NULL) { eff_src_array[i]= strdup(eff_origin); eff_tgt_array[i]= strdup(eff_dest); if(eff_src_array[i] == NULL || eff_tgt_array[i] == NULL) { Xorriso_no_malloc_memory(xorriso, &(eff_src_array[i]), 0); ret= -1; goto ex; } } else { ret= Xorriso_restore(xorriso, eff_origin, eff_dest, (off_t) 0, (off_t) 0, 16 | ((!(flag&2))<<6)); if(ret<=0 || xorriso->request_to_abort) goto problem_handler; if(ret==3 || (flag&1)) continue; sprintf(xorriso->info_text, "Copied from ISO image to disk: %s '%s' = '%s'\n", (ret>1 ? "directory" : "file"), eff_origin, eff_dest); Xorriso_info(xorriso, 0); } continue; /* regular bottom of loop */ problem_handler:; was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; goto ex; } if(eff_src_array != NULL) { ret= Xorriso_restore_sorted(xorriso, optc, eff_src_array, eff_tgt_array, &problem_count, 0); if(ret <= 0 || problem_count > 0) was_failure= 1; } if(xorriso->pacifier_count>0) Xorriso_pacifier_callback(xorriso, "files restored",xorriso->pacifier_count, xorriso->pacifier_total, "", 1|4); ret= !was_failure; ex:; i= optc; Sfile_destroy_argv(&i, &eff_src_array, 0); i= optc; Sfile_destroy_argv(&i, &eff_tgt_array, 0); Xorriso_opt_args(xorriso, "-cp*x", argc, argv, *idx, &end_idx_dummy, &optc, &optv, 256); Xorriso_free_meM(eff_origin); Xorriso_free_meM(eff_dest); Xorriso_free_meM(dest_dir); Xorriso_free_meM(leafname); return(ret); } /* Option -cut_out */ int Xorriso_option_cut_out(struct XorrisO *xorriso, char *disk_path, char *start, char *count, char *iso_rr_path, int flag) { int ret; double num; off_t startbyte, bytecount; num= Scanf_io_size(start, 0); if(num<0 || num > 1.0e18) { /* 10^18 = 10^3 ^ 6 < 2^10 ^ 6 = 2^60 */ sprintf(xorriso->info_text, "-cut_out: startbyte address negative or much too large (%s)", start); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } startbyte= num; num= Scanf_io_size(count, 0); if(num<=0 || num > 1.0e18) { sprintf(xorriso->info_text, "-cut_out: bytecount zero, negative or much too large (%s)", count); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } bytecount= num; sprintf(xorriso->info_text, "-cut_out from %s , byte %.f to %.f, and graft as %s", disk_path, (double) startbyte, (double) (startbyte+bytecount), iso_rr_path); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); ret= Xorriso_cut_out(xorriso, disk_path, startbyte, bytecount, iso_rr_path, 0); return(ret); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of commands as mentioned in man page or info file derived from xorriso.texi. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" #ifdef Xorriso_with_readlinE #define Xorriso_with_line_editoR #endif #ifdef Xorriso_with_editlinE #define Xorriso_with_line_editoR #endif /* Command -data_cache_size */ int Xorriso_option_data_cache_size(struct XorrisO *xorriso, char *num_tiles, char *tile_blocks, int flag) { int ret, blocks= -1, tiles= -1, to_default= 0; sscanf(num_tiles, "%d", &tiles); sscanf(tile_blocks, "%d", &blocks); if(strcmp(num_tiles, "default") == 0 || num_tiles[0] == 0) to_default|= 1; if(strcmp(tile_blocks, "default") == 0 || tile_blocks[0] == 0) to_default|= 2; ret= Xorriso_set_data_cache(xorriso, NULL, tiles, blocks, to_default); if(ret > 0) { xorriso->cache_num_tiles= tiles; xorriso->cache_tile_blocks= blocks; xorriso->cache_default= to_default; } return(ret); } /* Options -dev , -indev, -outdev */ /** @param flag bit0= use as indev bit1= use as outdev bit2= do not -reassure bit3= regard overwritable media as blank bit4= if the drive is a regular disk file: truncate it to the write start address bit5= do not print toc of acquired drive bit6= do not calm down drive after acquiring it @return <=0 error , 1 success, 2 revoked by -reassure */ int Xorriso_option_dev(struct XorrisO *xorriso, char *in_adr, int flag) { int ret; char *adr; adr= in_adr; if(strcmp(in_adr, "-")==0) adr= "stdio:/dev/fd/1"; if(strncmp(adr, "stdio:", 6)==0) { if(strlen(adr)==6 || strcmp(adr, "stdio:/")==0 || strcmp(adr, "stdio:.")==0 || strcmp(adr, "stdio:..")==0 || strcmp(adr, "stdio:-")==0) { sprintf(xorriso->info_text, "No suitable path given by device address '%s'", adr); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } if(Xorriso_change_is_pending(xorriso, 0) && (flag&1)) { sprintf(xorriso->info_text, "%s: Image changes pending. -commit or -rollback first", (flag&2) ? "-dev" : "-indev"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if((flag&1) && (xorriso->in_drive_handle != NULL || adr[0]) && !(flag&4)) { ret= Xorriso_reassure(xorriso, (flag&2) ? "-dev" : "-indev", "eventually discard the current image", 0); if(ret<=0) return(2); } if(adr[0]==0) { if((flag&1) && xorriso->in_drive_handle != NULL) { if(xorriso->in_drive_handle == xorriso->out_drive_handle) sprintf(xorriso->info_text,"Giving up -dev "); else sprintf(xorriso->info_text,"Giving up -indev "); Text_shellsafe(xorriso->indev, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } if((flag&2) && xorriso->out_drive_handle != NULL && xorriso->in_drive_handle != xorriso->out_drive_handle) { sprintf(xorriso->info_text,"Giving up -outdev "); Text_shellsafe(xorriso->outdev, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } ret= Xorriso_give_up_drive(xorriso, (flag&3)|((flag&32)>>2)); } else ret= Xorriso_aquire_drive(xorriso, adr, NULL, (flag & (3 | 32 | 64)) | (((flag & (8 | 16)) >> 1))); if(ret<=0) return(ret); if(xorriso->in_drive_handle == NULL) xorriso->image_start_mode= 0; /* session setting is invalid by now */ return(1); } /* Option -devices , -device_links */ /* @param flag bit0= perform -device_links rather than -devices @return <=0 error , 1 success, 2 revoked by -reassure */ int Xorriso_option_devices(struct XorrisO *xorriso, int flag) { int ret; if(Xorriso_change_is_pending(xorriso, 0)) { sprintf(xorriso->info_text, "-devices: Image changes pending. -commit or -rollback first"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= Xorriso_reassure(xorriso, "-devices", "eventually discard the current image", 0); if(ret<=0) return(2); xorriso->info_text[0]= 0; if(xorriso->in_drive_handle!=NULL || xorriso->out_drive_handle!=NULL) { if(xorriso->in_drive_handle == xorriso->out_drive_handle) { sprintf(xorriso->info_text, "Gave up -dev "); Text_shellsafe(xorriso->indev, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); }else { if(xorriso->in_drive_handle!=NULL) { sprintf(xorriso->info_text, "Gave up -indev "); Text_shellsafe(xorriso->indev, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } if(xorriso->out_drive_handle!=NULL) { sprintf(xorriso->info_text, "Gave up -outdev "); Text_shellsafe(xorriso->outdev, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } } Xorriso_give_up_drive(xorriso, 3); } ret= Xorriso_show_devices(xorriso, flag & 1); return(ret); } /* Option -dialog "on"|"single_line"|"off" */ int Xorriso_option_dialog(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "on") == 0 || strcmp(mode, "multi_line") == 0) xorriso->dialog= 2; else if(strcmp(mode, "single_line") == 0) xorriso->dialog= 1; else if(strcmp(mode, "off") == 0) xorriso->dialog= 0; else { sprintf(xorriso->info_text, "-dialog: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } return(1); } /* Option -disk_dev_ino "on"|"ino_only"|"off" */ int Xorriso_option_disk_dev_ino(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "on") == 0) xorriso->do_aaip= (xorriso->do_aaip & ~128) | 16 | 32 | 64; else if(strcmp(mode, "ino_only") == 0) xorriso->do_aaip|= 16 | 32 | 64 | 128; else if(strcmp(mode, "off") == 0) xorriso->do_aaip &= ~(16 | 32 | 64 | 128); else { sprintf(xorriso->info_text, "-disk_dev_ino: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } return(1); } /* Option -disk_pattern "on"|"ls"|"off" */ int Xorriso_option_disk_pattern(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "off")==0) xorriso->do_disk_pattern= 0; else if(strcmp(mode, "on")==0) xorriso->do_disk_pattern= 1; else if(strcmp(mode, "ls")==0) xorriso->do_disk_pattern= 2; else { sprintf(xorriso->info_text, "-disk_pattern: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -displacement [-]offset */ int Xorriso_option_displacement(struct XorrisO *xorriso, char *value, int flag) { double num; int displacement_sign= 1, l; char *cpt; cpt= value; if(value[0] == '-') { displacement_sign= -1; cpt++; } else if(value[0] == '+') cpt++; num= Scanf_io_size(cpt, 0); l= strlen(cpt); if(cpt[l - 1] < '0' || cpt[l - 1] > '9') num/= 2048.0; if(num < 0.0 || num > 4294967295.0) { sprintf(xorriso->info_text, "-displacement: too large or too small: '%s'", value); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(num == 0) displacement_sign= 0; xorriso->displacement= num; xorriso->displacement_sign= displacement_sign; return(1); } /* Command -drive_access "exclusive"|"shared":"readonly"|"unrestricted" */ int Xorriso_option_drive_access(struct XorrisO *xorriso, char *mode, int flag) { int l; char *npt, *cpt; npt= cpt= mode; for(cpt= mode; npt != NULL; cpt= npt+1) { npt= strchr(cpt, ':'); if(npt==NULL) l= strlen(cpt); else l= npt - cpt; if(l == 0 && mode[0] != 0) goto unknown_mode; if(strncmp(cpt, "shared", l) == 0 && l == 6) { xorriso->drives_exclusive= 0; } else if(strncmp(cpt, "exclusive", l) == 0 && l == 9) { xorriso->drives_exclusive= 1; } else if(strncmp(cpt, "readonly", l) == 0 && l == 8) { xorriso->drives_access= 0; } else if(strncmp(cpt, "unrestricted", l) == 0 && l == 12) { xorriso->drives_access= 1; } else { unknown_mode:; sprintf(xorriso->info_text, "-drive_access: unknown mode '"); if(l > 0 && l < SfileadrL) strncat(xorriso->info_text, cpt, l); strcat(xorriso->info_text, "'"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } return(1); } /* Option -drive_class */ int Xorriso_option_drive_class(struct XorrisO *xorriso, char *d_class, char *pattern, int flag) { int ret= 1; if(strcmp(d_class, "banned") == 0) { ret= Xorriso_lst_new(&(xorriso->drive_blacklist), pattern, xorriso->drive_blacklist, 1); } else if(strcmp(d_class, "caution") == 0) { ret= Xorriso_lst_new(&(xorriso->drive_greylist), pattern, xorriso->drive_greylist, 1); } else if (strcmp(d_class, "harmless") == 0) { ret= Xorriso_lst_new(&(xorriso->drive_whitelist), pattern, xorriso->drive_whitelist, 1); } else if (strcmp(d_class, "clear_list") == 0) { if(strcmp(pattern, "banned") == 0) Xorriso_lst_destroy_all(&(xorriso->drive_blacklist), 0); else if(strcmp(pattern, "caution") == 0) Xorriso_lst_destroy_all(&(xorriso->drive_greylist), 0); else if(strcmp(pattern, "harmless") == 0) Xorriso_lst_destroy_all(&(xorriso->drive_whitelist), 0); else if(strcmp(pattern, "all") == 0) { Xorriso_lst_destroy_all(&(xorriso->drive_blacklist), 0); Xorriso_lst_destroy_all(&(xorriso->drive_greylist), 0); Xorriso_lst_destroy_all(&(xorriso->drive_whitelist), 0); } else { sprintf(xorriso->info_text, "-drive_class clear : unknown class '%s'", pattern); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= 1; } else { sprintf(xorriso->info_text, "-drive_class: unknown class '%s'", d_class); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(ret); } /* Option -dummy "on"|"off" */ int Xorriso_option_dummy(struct XorrisO *xorriso, char *mode, int flag) { xorriso->do_dummy= !!strcmp(mode, "off"); return(1); } /* Option -dvd_obs "default"|"32k"|"64k" */ int Xorriso_option_dvd_obs(struct XorrisO *xorriso, char *obs, int flag) { double num; if(strcmp(obs, "obs_pad") == 0) { xorriso->do_obs_pad= 1; return(1); } else if(strcmp(obs, "no_obs_pad") == 0) { xorriso->do_obs_pad= 0; return(1); } else if(strcmp(obs, "bdr_obs_exempt")== 0) { xorriso->bdr_obs_exempt= 1; return(1); } else if(strcmp(obs, "no_bdr_obs_exempt")== 0) { xorriso->bdr_obs_exempt= 0; return(1); } else if(strcmp(obs, "default") == 0) { num= 0; } else if(obs[0] >= '0' && obs[0] <= '9') { num = Scanf_io_size(obs,0); } else { sprintf(xorriso->info_text, "-dvd_obs : Unrecognized parameter."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } if(num != 0 && num != 32768 && num != 65536) { sprintf(xorriso->info_text, "-dvd_obs : Bad size. Acceptable are 0, 32k, 64k"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } else xorriso->dvd_obs= num; return(1); } /* Option -early_stdio_test */ int Xorriso_option_early_stdio_test(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "on") == 0) xorriso->early_stdio_test= 2 | 4; else if(strcmp(mode, "off") == 0) xorriso->early_stdio_test= 0; else if(strcmp(mode, "appendable_wo") == 0) xorriso->early_stdio_test= 2 | 4 | 8; else { sprintf(xorriso->info_text, "-early_stdio_test: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } return(1); } /* Command -ecma119_map */ int Xorriso_option_ecma119_map(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "unmapped") == 0) xorriso->ecma119_map= 0; else if(strcmp(mode, "stripped") == 0) xorriso->ecma119_map= 1; else if(strcmp(mode, "uppercase") == 0) xorriso->ecma119_map= 2; else if(strcmp(mode, "lowercase") == 0) xorriso->ecma119_map= 3; else { sprintf(xorriso->info_text, "-ecma119_map: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } return(1); } /* Option -eject */ /* @param flag bit0=do not report toc of eventually remaining drives */ int Xorriso_option_eject(struct XorrisO *xorriso, char *which, int flag) { int gu_flag= 4, ret; if(strncmp(which,"in",2)==0) gu_flag|= 1; else if(strncmp(which,"out",3)==0) gu_flag|= 2; else gu_flag|= 3; if((gu_flag&1) && Xorriso_change_is_pending(xorriso, 0)) { sprintf(xorriso->info_text, "-eject: Image changes pending. -commit or -rollback first"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(flag&1) gu_flag|= 8; ret= Xorriso_give_up_drive(xorriso, gu_flag); return(ret); } /* Options -end , and -rollback_end */ /* @param flag bit0= discard pending changes bit1= do not -reassure @return <=0 error , 1 success, 2 revoked by -reassure */ int Xorriso_option_end(struct XorrisO *xorriso, int flag) { int ret; char *cmd, *which_will; if(flag&1) cmd= "-rollback_end"; else cmd= "-end"; if(Xorriso_change_is_pending(xorriso, 0)) { if((flag & 1) || !Xorriso_change_is_pending(xorriso, 1)) which_will= "end the program discarding image changes"; else which_will= "commit image changes and then end the program"; } else { which_will= "end the program"; } if(!(flag&2)) { ret= Xorriso_reassure(xorriso, cmd, which_will, 0); if(ret<=0) return(2); } if(Xorriso_change_is_pending(xorriso, 0)) { if((flag & 1) || !Xorriso_change_is_pending(xorriso, 1)) { xorriso->volset_change_pending= 0; } else { ret= Xorriso_option_commit(xorriso, 1); xorriso->volset_change_pending= 0; /* no further tries to commit */ if(ret<=0) return(ret); } } ret= Xorriso_give_up_drive(xorriso, 3); if(ret<=0) return(ret); return(1); } /* Option -errfile_log marked|plain path|-|"" */ int Xorriso_option_errfile_log(struct XorrisO *xorriso, char *mode, char *path, int flag) { int ret, mode_word; FILE *fp= NULL; if(path[0]==0 || path[0]=='-') { /* ok */; } else { fp= fopen(path, "a"); if(fp==0) { sprintf(xorriso->info_text, "-errfile_log: Cannot open file "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } mode_word= xorriso->errfile_mode; if(strcmp(mode, "marked")==0) mode_word|= 1; else if(strcmp(mode, "plain")==0) mode_word&= ~1; else { sprintf(xorriso->info_text, "-errfile_log: Unknown mode "); Text_shellsafe(mode, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); if(fp != NULL) fclose(fp); return(0); } Xorriso_process_errfile(xorriso, 0, "log end", 0, 1); if(xorriso->errfile_fp!=NULL) fclose(xorriso->errfile_fp); xorriso->errfile_fp= fp; xorriso->errfile_mode= mode_word; ret= Sfile_str(xorriso->errfile_log, path, 0); if(ret>0) ret= Xorriso_process_errfile(xorriso, 0, "log start", 0, 1); if(ret<=0) return(ret); return(1); } /* Option -error_behavior */ int Xorriso_option_error_behavior(struct XorrisO *xorriso, char *occasion, char *behavior, int flag) { if(strcmp(occasion, "image_loading")==0) { if(strcmp(behavior, "best_effort")==0) xorriso->img_read_error_mode= 0; else if(strcmp(behavior, "failure")==0 || strcmp(behavior, "FAILURE")==0) xorriso->img_read_error_mode= 1; else if(strcmp(behavior, "fatal")==0 || strcmp(behavior, "FATAL")==0) xorriso->img_read_error_mode= 2; else { unknown_behavior:; sprintf(xorriso->info_text, "-error_behavior: with '%s': unknown behavior '%s'", occasion, behavior); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } else if(strcmp(occasion, "file_extraction")==0) { if(strcmp(behavior, "best_effort")==0) xorriso->extract_error_mode= 0; else if(strcmp(behavior, "keep")==0) xorriso->extract_error_mode= 1; else if(strcmp(behavior, "delete")==0) xorriso->extract_error_mode= 2; else goto unknown_behavior; } else { sprintf(xorriso->info_text, "-error_behavior: unknown occasion '%s'", occasion); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -external_filter */ int Xorriso_option_external_filter(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int ret, start_idx, end_idx; start_idx= *idx; end_idx= Xorriso_end_idx(xorriso, argc, argv, start_idx, 1); (*idx)= end_idx; if(end_idx - start_idx < 3) { sprintf(xorriso->info_text, "-external_filter : Not enough parameters given. Needed: name options path %s", xorriso->list_delimiter); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= Xorriso_external_filter(xorriso, argv[start_idx], argv[start_idx + 1], argv[start_idx + 2], end_idx - start_idx - 3, argv + start_idx + 3, 0); return(ret); } /* Options -extract , -extract_single */ /* @param flag bit0=do not report the restored item bit1=do not reset pacifier, no final pacifier message bit2= do not make lba-sorted node array for hardlink detection bit5= -extract_single: eventually do not insert directory tree */ int Xorriso_option_extract(struct XorrisO *xorriso, char *iso_path, char *disk_path, int flag) { int ret, problem_count; char *eff_origin= NULL, *eff_dest= NULL, *ipth, *eopt[1], *edpt[1]; Xorriso_alloc_meM(eff_origin, char, SfileadrL); Xorriso_alloc_meM(eff_dest, char, SfileadrL); if(xorriso->allow_restore <= 0) { sprintf(xorriso->info_text, "-extract: image-to-disk copies are not enabled by option -osirrox"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(!(flag&2)) Xorriso_pacifier_reset(xorriso, 0); ipth= iso_path; if(ipth[0]==0) ipth= disk_path; if(disk_path[0]==0) { sprintf(xorriso->info_text, "-extract: Empty disk_path given"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 1); ret= 0; goto ex; } ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, disk_path, eff_dest, 2|4); if(ret<=0) goto ex; ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, ipth, eff_origin, 2|8); if(ret<=0) goto ex; eopt[0]= eff_origin; edpt[0]= eff_dest; ret= Xorriso_restore_sorted(xorriso, 1, eopt, edpt, &problem_count, (flag & 32 ? 33 : 0)); if(!(flag&2)) Xorriso_pacifier_callback(xorriso, "files restored",xorriso->pacifier_count, xorriso->pacifier_total, "", 1 | 4 | 8 | 32); if(ret <= 0 || problem_count > 0) goto ex; if(!(flag&1)) { sprintf(xorriso->info_text, "Extracted from ISO image: %s '%s'='%s'\n", (ret>1 ? "directory" : "file"), eff_origin, eff_dest); Xorriso_info(xorriso,0); } ret= 1; ex:; if(!(flag & (4 | 32))) Xorriso_destroy_node_array(xorriso, 0); Xorriso_free_meM(eff_origin); Xorriso_free_meM(eff_dest); return(ret); } /* Command -extract_boot_images */ int Xorriso_option_extract_boot_images(struct XorrisO *xorriso, char *disk_dir_path, int flag) { int ret; if(xorriso->allow_restore <= 0) { sprintf(xorriso->info_text, "-extract_boot_images: image-to-disk copies are not enabled by option -osirrox" ); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= Xorriso_extract_boot_images(xorriso, disk_dir_path, 0); return(ret); } /* Option -extract_cut */ int Xorriso_option_extract_cut(struct XorrisO *xorriso, char *iso_rr_path, char *start, char *count, char *disk_path, int flag) { int ret; double num; off_t startbyte, bytecount; num= Scanf_io_size(start, 0); if(num<0 || num > 1.0e18) { /* 10^18 = 10^3 ^ 6 < 2^10 ^ 6 = 2^60 */ sprintf(xorriso->info_text, "-extract_cut: startbyte address negative or much too large (%s)", start); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } startbyte= num; num= Scanf_io_size(count, 0); if(num<=0 || num > 1.0e18) { sprintf(xorriso->info_text, "-extract_cut: bytecount zero, negative or much too large (%s)", count); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } bytecount= num; sprintf(xorriso->info_text, "-extract_cut from %s , byte %.f to %.f, and store as %s", iso_rr_path, (double) startbyte, (double) (startbyte+bytecount), disk_path); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); ret= Xorriso_extract_cut(xorriso, iso_rr_path, disk_path, startbyte, bytecount, 0); return(ret); } /* Command -file_name_limit */ int Xorriso_option_file_name_limit(struct XorrisO *xorriso, char *value, int flag) { int ret, sub_flag= 0; double num; if(value[0] == '+') sub_flag|= 1; num= Scanf_io_size(value + (sub_flag & 1), 0); if(num < 64 || num > 255) { sprintf(xorriso->info_text, "-file_name_limit: Value '%s' out of range [64..255]", value); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } if(num == xorriso->file_name_limit) return(1); ret= Xorriso_set_file_name_limit(xorriso, (int) num, sub_flag); return(ret > 0); } /* Option -file_size_limit */ int Xorriso_option_file_size_limit(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int ret, i, end_idx; off_t new_limit= 0; end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, 1); if(*idx >= end_idx) {ret= 2; goto ex;} if(*idx + 1 == end_idx && strcmp(argv[*idx], "off") == 0) { xorriso->file_size_limit= 0; ret= 1; goto ex; } for(i= *idx; i < end_idx; i++) new_limit+= Scanf_io_size(argv[i], 0); if(new_limit <= 0) { sprintf(xorriso->info_text, "-file_size_limit: values sum up to %.f", (double) new_limit); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } xorriso->file_size_limit= new_limit; ret= 1; ex:; if((xorriso->file_size_limit >= ((off_t) 4) * (off_t) (1024 * 1024 * 1024) || xorriso->file_size_limit == 0) && xorriso->iso_level < 3 && ret > 0) { xorriso->iso_level= 3; xorriso->iso_level_is_default= 0; Xorriso_msgs_submit(xorriso, 0, "-file_size_limit of at least 4 GiB causes ISO level 3", 0, "NOTE", 0); } (*idx)= end_idx; if(ret > 0) { if(xorriso->file_size_limit > 0) sprintf(xorriso->info_text, "-file_size_limit now at %.f\n", (double) xorriso->file_size_limit); else sprintf(xorriso->info_text, "-file_size_limit now off\n"); Xorriso_info(xorriso,0); } return(ret); } static int Xorriso_determine_name_space(struct XorrisO *xorriso, char *space_name, int flag) { if(strcmp(space_name, "rockridge") == 0) return(1); else if(strcmp(space_name, "joliet") == 0) return(2); else if(strcmp(space_name, "ecma119") == 0 || strcmp(space_name, "iso9660") == 0) return(3); else if(strcmp(space_name, "hfsplus") == 0) return(4); sprintf(xorriso->info_text, "-find: Unknown output namespace identifier"); return(0); } static int Xorriso_truncate_const_find_name(struct XorrisO *xorriso, char *expr, char *buffer, char **namept, int flag) { int ret; *namept= expr; ret= Xorriso_truncate_path_comps(xorriso, expr, buffer, namept, 1); if(ret < 0) { sprintf(xorriso->info_text, "-find[ix]: cannot truncate constant -name to -file_name_limit: "); Text_shellsafe(expr, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -find alias -findi, and -findx */ /* @param flag bit0= -findx rather than -findi bit1= do not reset pacifier, no final pacifier message do not reset find_compare_result bit2= do not count deleted files with rm and rm_r bit3= use Xorriso_findi_sorted() rather than Xorriso_findi() (this can also be ordered by test -sort_lba) bit4= return number of matches plus 1 */ int Xorriso_option_find(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int ret, i, end_idx, type= 0, action, deleter= 0, count, operator; off_t start_lba, block_count; uint64_t lfa_flags; int list_extattr_head= 0, bsl_mem, disk_path, name_space, prefix_code; struct FindjoB *job, *first_job= NULL, *new_job; char *start_path, *path= NULL, *cpt, *other_path_start= NULL, *cd_pt; char *access_acl_text= NULL, *default_acl_text= NULL, *list_extattr_mode; char *arg1_pt, *namept, *suffix_pt, *value_pt, size_text[40]; off_t size_value, suffix_factor= 1; struct stat dir_stbuf; uid_t user= 0; gid_t group= 0; time_t date= 0; mode_t mode_or= 0, mode_and= ~1; double mem_lut= 0.0; end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, 1); Xorriso_alloc_meM(path, char, SfileadrL); Xorriso_alloc_meM(other_path_start, char, SfileadrL); start_path= "."; list_extattr_mode= "e"; if(end_idx > *idx && start_path[0]!=0) start_path= argv[*idx]; ret= Findjob_new(&first_job, start_path, 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "-find[ix]", 0); {ret= -1; goto ex;} } job= first_job; if(!(flag&2)) xorriso->find_compare_result= 1; for(i= *idx+1; i<end_idx; i++) { ret= 1; if(strcmp(argv[i], "-name")==0) { if(i+1>=end_idx) { not_enough_arguments:; sprintf(xorriso->info_text, "-find[ix]: not enough parameters with test "); Text_shellsafe(argv[i], xorriso->info_text, 1); goto sorry_ex; } i++; ret= Xorriso_truncate_const_find_name(xorriso, argv[i], path, &namept, 0); if(ret <= 0) goto ex; ret= Findjob_set_name_expr(job, namept, 0); if(ret<=0) { sprintf(xorriso->info_text, "-find[ix]: cannot set -name expression "); Text_shellsafe(argv[i], xorriso->info_text, 1); goto sorry_ex; } } else if(strcmp(argv[i], "-wholename")==0) { if(i+1>=end_idx) goto not_enough_arguments; i++; ret= Xorriso_truncate_const_find_name(xorriso, argv[i], path, &namept, 0); if(ret <= 0) goto ex; ret= Findjob_set_name_expr(job, namept, 1); if(ret<=0) { sprintf(xorriso->info_text, "-find[ix]: cannot set -wholename expression "); Text_shellsafe(argv[i], xorriso->info_text, 1); goto sorry_ex; } } else if(strcmp(argv[i], "-type")==0) { if(i+1>=end_idx) goto not_enough_arguments; i++; ret= Findjob_set_file_type(job, argv[i][0], 0); if(ret<=0) { sprintf(xorriso->info_text, "-find[ix]: unknown -type '%c'",argv[i][0]); goto sorry_ex; } } else if(strcmp(argv[i], "-damaged")==0) { Findjob_set_damage_filter(job, 1, 0); } else if(strcmp(argv[i], "-undamaged")==0) { Findjob_set_damage_filter(job, -1, 0); } else if(strcmp(argv[i], "-lba_range")==0) { if(i+2>=end_idx) goto not_enough_arguments; i+= 2; /* >>> if letter suffix: use Scanf_io_size */ ret= Sfile_text_to_off_t(argv[i - 1], &start_lba, 0); if(ret <= 0) { off_t_overflow:; sprintf(xorriso->info_text, "-find[ix]: -lba_range number too large"); goto sorry_ex; } ret= Sfile_text_to_off_t(argv[i], &block_count, 0); if(ret <= 0) goto off_t_overflow; Findjob_set_lba_range(job, start_lba, block_count, 0); } else if(strcmp(argv[i], "-pending_data")==0) { Findjob_set_commit_filter_2(job, 0); } else if(strcmp(argv[i], "-has_acl")==0) { Findjob_set_acl_filter(job, 1, 0); } else if(strcmp(argv[i], "-has_no_acl")==0) { Findjob_set_acl_filter(job, -1, 0); } else if(strcmp(argv[i], "-has_xattr")==0) { Findjob_set_xattr_filter(job, 1, 0); } else if(strcmp(argv[i], "-has_any_xattr")==0) { Findjob_set_xattr_filter(job, 1, 1); } else if(strcmp(argv[i], "-has_no_xattr")==0) { Findjob_set_xattr_filter(job, -1, 0); } else if(strcmp(argv[i], "-has_aaip")==0) { Findjob_set_aaip_filter(job, 1, 0); } else if(strcmp(argv[i], "-has_no_aaip")==0) { Findjob_set_aaip_filter(job, -1, 0); } else if(strcmp(argv[i], "-has_lfa_flags") == 0 || strcmp(argv[i], "-has_some_lfa_flags_of") == 0) { if(i + 1 >= end_idx) goto not_enough_arguments; i++; ret= Xorriso_decode_lfa_flags(xorriso, argv[i], &lfa_flags, 0); if(ret <= 0) goto sorry_ex; Findjob_set_uint64_filter(job, 28 + (strcmp(argv[i - 1], "-has_some_lfa_flags_of") == 0), lfa_flags, 0); } else if(strcmp(argv[i], "-has_filter")==0) { Findjob_set_filter_filter(job, 1, 0); } else if(strcmp(argv[i], "-has_no_filter")==0) { Findjob_set_filter_filter(job, -1, 0); } else if(strcmp(argv[i], "-has_md5")==0) { Findjob_set_prop_filter(job, 15, 1, 0); } else if(strcmp(argv[i], "-disk_name")==0 || strcmp(argv[i], "-disk_path")==0) { disk_path= (strcmp(argv[i], "-disk_path") == 0); if(i+1>=end_idx) goto not_enough_arguments; i++; arg1_pt= argv[i]; if(disk_path) { ret= Xorriso_make_abs_adr(xorriso, xorriso->wdx, argv[i], path, 1 | 2 | 4 | 8); if(ret<=0) goto ex; arg1_pt= path; } ret= Findjob_set_name_expr(job, arg1_pt, 2 + disk_path); if(ret<=0) { sprintf(xorriso->info_text, "-find[ix]: cannot set %s ", disk_path ? "-disk_path address" : "-disk_name expression"); Text_shellsafe(argv[i], xorriso->info_text, 1); goto sorry_ex; } } else if(strcmp(argv[i], "-hidden")==0) { if(i + 1 >= end_idx) goto not_enough_arguments; i+= 1; type= Xorriso__hide_mode(argv[i], 0); if(type < 0) { sprintf(xorriso->info_text, "-findi: -hidden : unknown hide state "); Text_shellsafe(argv[i], xorriso->info_text, 1); goto sorry_ex; } else { ret= Findjob_set_test_hidden(job, type, 0); if(ret <= 0) { sprintf(xorriso->info_text, "-findi: cannot setup -hidden test"); goto sorry_ex; } } } else if(strcmp(argv[i], "-has_hfs_crtp")==0) { if(i + 2 >= end_idx) goto not_enough_arguments; i+= 2; ret= Xorriso_hfsplus_file_creator_type(xorriso, "", NULL, argv[i - 1], argv[i], 3); if(ret <= 0) {ret= 0; goto ex;} ret= Findjob_set_crtp_filter(job, argv[i - 1], argv[i], 0); if(ret <= 0) { sprintf(xorriso->info_text, "-findi: cannot setup -has_hfs_crtp test"); goto sorry_ex; } } else if(strcmp(argv[i], "-has_hfs_bless")==0) { if(i + 1 >= end_idx) goto not_enough_arguments; i+= 1; ret= Findjob_set_bless_filter(xorriso, job, argv[i], 0); if(ret <= 0) { sprintf(xorriso->info_text, "-findi: cannot setup -has_hfs_bless test"); goto sorry_ex; } } else if(strcmp(argv[i], "-bad_outname")==0) { if(i + 1 >= end_idx) goto not_enough_arguments; i+= 1; name_space= Xorriso_determine_name_space(xorriso, argv[i], 0); if(name_space < 0) { ret= 0; goto sorry_ex; } ret= Findjob_set_num_filter(job, 21, name_space, 0, 0); if(ret <= 0) { sprintf(xorriso->info_text, "-findi: cannot setup -bad_outname test"); goto sorry_ex; } } else if(strcmp(argv[i], "-true") == 0) { ret= Findjob_set_false(job, -1, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-false") == 0) { ret= Findjob_set_false(job, 1, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-decision") == 0) { if(i+1>=end_idx) goto not_enough_arguments; i++; ret= Findjob_set_arg1(job, 11, argv[i], 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-prune") == 0) { ret= Findjob_set_prune(job, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-sub") == 0 || strcmp(argv[i], "(") == 0) { ret= Findjob_open_bracket(job, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-subend") == 0 || strcmp(argv[i], ")") == 0) { ret= Findjob_close_bracket(job, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-not") == 0 || strcmp(argv[i], "!") == 0) { ret= Findjob_not(job, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-and") == 0 || strcmp(argv[i], "-a") == 0) { ret= Findjob_and(job, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-or") == 0 || strcmp(argv[i], "-o") == 0) { ret= Findjob_or(job, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-if") == 0) { ret= Findjob_if(job, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-then") == 0) { ret= Findjob_then(job, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-else") == 0) { ret= Findjob_else(job, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-elseif") == 0) { ret= Findjob_elseif(job, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-endif") == 0) { ret= Findjob_endif(job, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-sort_lba") == 0) { flag|= 8; /* If an operator is open: insert a -true test, else do nothing */ ret= Findjob_set_false(job, -1, 1); if(ret == 2) ret= 1; } else if(strcmp(argv[i], "-use_pattern") == 0 || strcmp(argv[i], "-or_use_pattern") == 0) { if(i + 1 >= end_idx) goto not_enough_arguments; i++; ret= Findjob_set_arg1(job, 22 + (strcmp(argv[i - 1], "-or_use_pattern") == 0), argv[i], 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-name_limit_blocker") == 0) { if(i + 1 >= end_idx) goto not_enough_arguments; i++; sscanf(argv[i], "%d", &count); if(count < 64 || count > 255) { sprintf(xorriso->info_text, "-findi: wrong length with -name_limit_blocker [64...255]"); goto sorry_ex; } ret= Findjob_set_num_filter(job, 24, count, 0, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-maxdepth") == 0 || strcmp(argv[i], "-mindepth") == 0) { if(i + 1 >= end_idx) goto not_enough_arguments; i++; count= -1; sscanf(argv[i], "%d", &count); if(count < 0) { sprintf(xorriso->info_text, "-findi: wrong length with %s [>= 0]", argv[i - 1]); goto sorry_ex; } ret= Findjob_set_num_filter(job, 25 + (strcmp(argv[i - 1], "-mindepth") == 0), count, 0, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-size") == 0) { if(i + 1 >= end_idx) goto not_enough_arguments; i++; if(strlen(argv[i]) >= sizeof(size_text) - 1) { sprintf(xorriso->info_text, "-findi: Number text after -size is much too long"); goto sorry_ex; } strcpy(size_text, argv[i]); value_pt= size_text; prefix_code= 0; if(*value_pt == 0) goto size_value_bad; if(*value_pt == '-') { prefix_code= -1; value_pt++; } else if(*value_pt == '+') { prefix_code= 1; value_pt++; } if(*value_pt == 0) goto size_value_bad; if(*value_pt == '=') { prefix_code*= 2; value_pt++; } if(*value_pt == 0) { size_value_bad:; sprintf(xorriso->info_text, "-findi: Number text after -size is not suitable"); } /* Convert "find -size" suffix to Scanf_io_size suffix */ suffix_pt= value_pt + strlen(value_pt) - 1; if(*suffix_pt == 'b' || *suffix_pt == 'B') { *suffix_pt= 'd'; /* 512 bytes */ } else if(*suffix_pt == 'c' || *suffix_pt == 'C') { *suffix_pt= 0; /* 1 byte */ } else if(*suffix_pt == 'w'|| *suffix_pt == 'W') { *suffix_pt= 0; /* 2 byte */ suffix_factor= 2; } else if(*suffix_pt >= '0' && *suffix_pt <= '9') { suffix_pt++; *suffix_pt= 'd'; /* 512 bytes */ *(suffix_pt + 1)= 0; } size_value= (off_t) Scanf_io_size(value_pt, 1) * (off_t) suffix_factor; if(size_value < 0) goto size_value_bad; ret= Findjob_set_size_filter(job, 27, size_value, prefix_code, 0); if(ret <= 0) goto ex; } else if(strcmp(argv[i], "-exec")==0) { if(i+1>=end_idx) { not_enough_exec_arguments:; sprintf(xorriso->info_text, "-find[ix]: not enough parameters with -exec "); Text_shellsafe(argv[i], xorriso->info_text, 1); goto sorry_ex; } i++; cpt= argv[i]; if(*cpt=='-') cpt++; if(strcmp(cpt, "echo")==0) { Findjob_set_action_target(job, 0, NULL, 0); } else if(strcmp(cpt, "rm")==0) { Findjob_set_action_target(job, 1, NULL, 0); deleter= 1; } else if(strcmp(cpt, "rm_r")==0) { Findjob_set_action_target(job, 2, NULL, 0); deleter= 1; #ifdef NIX /* >>> not implemented yet */; } else if(strcmp(cpt, "mv")==0) { if(i+1>=end_idx) goto not_enough_exec_arguments; i++; Findjob_set_action_target(job, 3, argv[i], 0); #endif } else if(strcmp(cpt, "chown")==0 || strcmp(cpt, "chown_r")==0) { if(i+1>=end_idx) goto not_enough_exec_arguments; i++; ret= Xorriso_convert_uidstring(xorriso, argv[i], &user, 0); if(ret<=0) goto ex; ret= Findjob_set_action_chown(job, user, strlen(cpt)>5); if(ret<=0) { Xorriso_no_findjob(xorriso, "-find -exec chown_r", 0); goto ex; } } else if(strcmp(cpt, "chgrp")==0 || strcmp(cpt, "chgrp_r")==0) { if(i+1>=end_idx) goto not_enough_exec_arguments; i++; ret= Xorriso_convert_gidstring(xorriso, argv[i], &group, 0); if(ret<=0) goto ex; ret= Findjob_set_action_chgrp(job, group, strlen(cpt)>5); if(ret<=0) { Xorriso_no_findjob(xorriso, "-find -exec chgrp_r", 0); goto ex; } } else if(strcmp(cpt, "chmod")==0 || strcmp(cpt, "chmod_r")==0) { if(i+1>=end_idx) goto not_enough_exec_arguments; i++; ret= Xorriso_convert_modstring(xorriso, "-find -exec chmod", argv[i], &mode_and, &mode_or, 0); if(ret<=0) goto ex; ret= Findjob_set_action_chmod(job, mode_and, mode_or, strlen(cpt)>5); if(ret<=0) { Xorriso_no_findjob(xorriso, "-find -exec chmod_r", 0); goto ex; } } else if(strcmp(cpt, "alter_date")==0 || strcmp(cpt, "alter_date_r")==0){ if(i+2>=end_idx) goto not_enough_exec_arguments; i+= 2; ret= Xorriso_convert_datestring(xorriso, "-find -exec alter_date", argv[i-1], argv[i], &type, &date, 0); if(ret<=0) goto ex; ret= Findjob_set_action_ad(job, type, date, strlen(cpt)>10); if(ret<=0) { Xorriso_no_findjob(xorriso, "-find -exec alter_date_r", 0); goto ex; } } else if(strcmp(cpt, "set_to_mtime") == 0) { Findjob_set_action_target(job, 59, NULL, 0); } else if(strcmp(cpt, "lsdl")==0) { Findjob_set_action_target(job, 8, NULL, 0); } else if(strcmp(cpt, "find")==0) { ret= Findjob_new(&new_job, "", 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "-find[ix]", 0); {ret= -1; goto ex;} } Findjob_set_action_subjob(job, 13, new_job, 0); job= new_job; } else if(strcmp(cpt, "compare")==0 || strcmp(cpt, "update")==0 || strcmp(cpt, "widen_hardlinks")==0 || strcmp(cpt, "update_merge")==0) { if(i+1>=end_idx) goto not_enough_exec_arguments; i++; action= 14; if(strcmp(cpt, "update")==0) action= 17; else if(strcmp(cpt, "widen_hardlinks")==0) action= 32; else if(strcmp(cpt, "update_merge") == 0) { action= 41; /* Enter update_merge mode for node adding */ xorriso->update_flags|= 1; } ret= Xorriso_make_abs_adr(xorriso, xorriso->wdx, argv[i], other_path_start, 1|2|4|8); if(ret<=0) goto ex; Findjob_set_action_target(job, action, other_path_start, 0); ret= Xorriso_make_abs_adr(xorriso, xorriso->wdi, start_path, path, 1|2|4); if(ret<=0) goto ex; Findjob_set_start_path(job, path, 0); if(!(flag&2)) { Xorriso_pacifier_reset(xorriso, 0); mem_lut= xorriso->last_update_time; } } else if(strcmp(cpt, "in_iso")==0 || strcmp(cpt, "not_in_iso")==0 || strcmp(cpt, "add_missing")==0 || strcmp(cpt, "empty_iso_dir")==0 || strcmp(cpt, "is_full_in_iso")==0) { if(i+1>=end_idx) goto not_enough_exec_arguments; i++; ret= Xorriso_make_abs_adr(xorriso, xorriso->wdi, argv[i], other_path_start, 1|2|4); if(ret<=0) goto ex; if(strcmp(cpt, "in_iso")==0) action= 15; else if(strcmp(cpt, "add_missing")==0) action= 18; else if(strcmp(cpt, "empty_iso_dir")==0) action= 19; else if(strcmp(cpt, "is_full_in_iso")==0) action= 20; else action= 16; Findjob_set_action_target(job, action, other_path_start, 0); ret= Xorriso_make_abs_adr(xorriso, xorriso->wdx, start_path, path, 1|2|4|8); if(ret<=0) goto ex; Findjob_set_start_path(job, path, 0); } else if(strcmp(cpt, "report_damage")==0) { Findjob_set_action_target(job, 21, NULL, 0); } else if(strcmp(cpt, "report_lba")==0) { Findjob_set_action_target(job, 22, NULL, 0); } else if(strcmp(cpt, "getfacl")==0) { Findjob_set_action_target(job, 24, NULL, 0); } else if(strcmp(cpt, "setfacl")==0) { if(i+1>=end_idx) goto not_enough_exec_arguments; i++; ret= Xorriso_normalize_acl_text(xorriso, argv[i], &access_acl_text, &default_acl_text, 0); if(ret <= 0) goto ex; Findjob_set_action_text_2(job, 25, access_acl_text, default_acl_text, 0); } else if(strcmp(cpt, "getfattr")==0) { Findjob_set_action_target(job, 26, NULL, 0); } else if(strcmp(cpt, "setfattr")==0) { if(i + 2 >= end_idx) goto not_enough_exec_arguments; i+= 2; /* check input */ ret= Xorriso_path_setfattr(xorriso, NULL, "", argv[i - 1], strlen(argv[i]), argv[i], 1); if(ret <= 0) goto ex; Findjob_set_action_text_2(job, 27, argv[i - 1], argv[i], 0); } else if(strcmp(cpt, "set_filter")==0) { if(i + 1 >= end_idx) goto not_enough_exec_arguments; i+= 1; Findjob_set_action_target(job, 28, argv[i], 0); if(!(flag&2)) { Xorriso_pacifier_reset(xorriso, 0); mem_lut= xorriso->last_update_time; } } else if(strcmp(cpt, "show_stream")==0) { Findjob_set_action_target(job, 29, NULL, 0); } else if(strcmp(cpt, "get_any_xattr")==0) { Findjob_set_action_target(job, 33, NULL, 0); } else if(strcmp(cpt, "get_md5")==0) { Findjob_set_action_target(job, 34, NULL, 0); } else if(strcmp(cpt, "check_md5")==0) { if(i + 1 >= end_idx) goto not_enough_exec_arguments; i+= 1; Findjob_set_action_target(job, 35, argv[i], 0); flag|= 8; if(!(flag&2)) { Xorriso_pacifier_reset(xorriso, 0); mem_lut= xorriso->last_update_time; } if(!(flag & 1)) xorriso->find_check_md5_result= 0; } else if(strcmp(cpt, "make_md5")==0) { Findjob_set_action_target(job, 36, NULL, 0); flag|= 8; if(!(flag&2)) { Xorriso_pacifier_reset(xorriso, 0); mem_lut= xorriso->last_update_time; } } else if(strcmp(cpt, "mkisofs_r")==0) { Findjob_set_action_target(job, 37, NULL, 0); } else if(strcmp(cpt, "sort_weight")==0) { if(i + 1 >= end_idx) goto not_enough_exec_arguments; i+= 1; sscanf(argv[i], "%d", &type); Findjob_set_action_type(job, 38, type, 0); } else if(strcmp(cpt, "hide")==0) { if(i+1>=end_idx) goto not_enough_exec_arguments; i++; type= Xorriso__hide_mode(argv[i], 0); if(type < 0) { sprintf(xorriso->info_text, "-find -exec hide: unknown hide state "); Text_shellsafe(argv[i], xorriso->info_text, 1); goto sorry_ex; } Findjob_set_action_type(job, 39, type, 0); } else if(strcmp(cpt, "estimate_size")==0) { Findjob_set_action_target(job, 40, NULL, 0); } else if(strcmp(cpt, "rm_merge")==0) { Findjob_set_action_target(job, 42, NULL, 0); xorriso->update_flags&= ~1; /* End update_merge mode for node adding */ } else if(strcmp(cpt, "clear_merge")==0) { Findjob_set_action_target(job, 43, NULL, 0); xorriso->update_flags&= ~1; /* End update_merge mode for node adding */ } else if(strcmp(cpt, "list_extattr")==0) { if(i+1>=end_idx) goto not_enough_exec_arguments; i++; Findjob_set_action_target(job, 44, argv[i], 0); list_extattr_head= 1; list_extattr_mode= argv[i]; } else if(strcmp(cpt, "set_hfs_crtp")==0) { if(i + 2 >= end_idx) goto not_enough_exec_arguments; i+= 2; /* Check creator and type for compliance */ ret= Xorriso_hfsplus_file_creator_type(xorriso, "", NULL, argv[i - 1], argv[i], 1); if(ret <= 0) goto ex; Findjob_set_action_text_2(job, 45, argv[i - 1], argv[i], 0); } else if(strcmp(cpt, "get_hfs_crtp")==0) { Findjob_set_action_target(job, 46, NULL, 0); } else if(strcmp(cpt, "set_hfs_bless")==0) { if(i+1>=end_idx) goto not_enough_exec_arguments; i++; /* Check type of blessing for compliance */ ret= Xorriso_hfsplus_bless(xorriso, "", NULL, argv[i], 4); if(ret <= 0) goto ex; Findjob_set_action_target(job, 47, argv[i], 0); } else if(strcmp(cpt, "get_hfs_bless")==0) { Findjob_set_action_target(job, 48, NULL, 0); } else if(strcmp(cpt, "print_outname")==0) { if(i+1>=end_idx) goto not_enough_exec_arguments; i++; name_space= Xorriso_determine_name_space(xorriso, argv[i], 0); if(name_space < 0) { ret= 0; goto sorry_ex; } Findjob_set_action_type(job, 50, name_space, 0); } else if(strcmp(cpt, "report_sections")==0) { Findjob_set_action_target(job, 51, NULL, 0); } else if(strcmp(cpt, "show_stream_id") == 0) { Findjob_set_action_target(job, 52, NULL, 0); } else if(strcmp(cpt, "lsattrd") == 0) { Findjob_set_action_target(job, 60, NULL, 0); } else if(strcmp(cpt, "chattr") == 0) { if(i + 1 >= end_idx) goto not_enough_exec_arguments; i++; ret= Xorriso_decode_chattr_arg(xorriso, argv[i], &lfa_flags, &operator, 0); if(ret <= 0) goto sorry_ex; Findjob_set_action_chattr(job, 61, lfa_flags, operator, 0); } else { sprintf(xorriso->info_text, "-find -exec: unknown action "); Text_shellsafe(argv[i], xorriso->info_text, 1); goto sorry_ex; } } else { sprintf(xorriso->info_text, "-find[ix]: unknown option "); Text_shellsafe(argv[i], xorriso->info_text, 1); sorry_ex:; Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } } if(list_extattr_head) { sprintf(xorriso->result_line, "# Output of xorriso %s action list_extattr\n", (flag & 1) ? "-findx" : "-find"); Xorriso_result(xorriso, 0); strcpy(xorriso->result_line, "cd "); if(start_path[0] == '/') strcat(xorriso->result_line, "/"); else { cd_pt= (flag & 1) ? xorriso->wdx : xorriso->wdi; if(cd_pt[0] == 0) cd_pt= "/"; ret= Xorriso_append_extattr_comp(xorriso, cd_pt, strlen(cd_pt), list_extattr_mode, 0); if(ret <= 0) goto ex; } strcat(xorriso->result_line, "\n"); /* temporarily disable -backslash_codes with result output */ bsl_mem= xorriso->bsl_interpretation; xorriso->bsl_interpretation= 0; Xorriso_result(xorriso, 0); xorriso->bsl_interpretation= bsl_mem; sprintf(xorriso->result_line, "c=\"setextattr\"\n\n"); Xorriso_result(xorriso, 0); } if(flag&1) ret= Xorriso_findx(xorriso, first_job, "", start_path, &dir_stbuf, 0, NULL, 0); else if(flag & 8) { cpt= start_path; ret= Xorriso_findi_sorted(xorriso, first_job, (off_t) 0, 1, &cpt, 0); } else ret= Xorriso_findi(xorriso, first_job, NULL, (off_t) 0, NULL, start_path, &dir_stbuf, 0, (flag&4)>>1); ex:; if(deleter && !(flag&2)) Xorriso_pacifier_callback(xorriso, "iso_rr_paths deleted", xorriso->pacifier_count, 0, "", 1|2); else if(first_job != NULL && first_job->action == 28 && !(flag&2)) Xorriso_pacifier_callback(xorriso, "file filters processed", xorriso->pacifier_count, 0, "", 1 | 2); else if(mem_lut!=xorriso->last_update_time && mem_lut!=0.0 && !(flag&2)) Xorriso_pacifier_callback(xorriso, "content bytes read", xorriso->pacifier_count, 0, "", 1 | 8 | 32); if(first_job != NULL && first_job->action == 35 && !(flag & 1)) Xorriso_report_md5_outcome(xorriso, first_job->target, 0); if(first_job != NULL && first_job->action == 40) { sprintf(xorriso->result_line,"Size lower : %lus\n", (unsigned long) (first_job->estim_lower_size / (off_t) 2048)); Xorriso_result(xorriso,0); sprintf(xorriso->result_line,"Size upper : %lus\n", (unsigned long) ((first_job->estim_upper_size / (off_t) 2048) + !!(first_job->estim_upper_size % 2048))); Xorriso_result(xorriso,0); } if(access_acl_text != NULL) free(access_acl_text); if(default_acl_text != NULL) free(default_acl_text); if(ret > 0 && (flag & 16) && first_job != NULL) ret= first_job->match_count + 1; Findjob_destroy(&first_job, 0); Xorriso_free_meM(path); Xorriso_free_meM(other_path_start); (*idx)= end_idx; return(ret); } /* Option -follow */ int Xorriso_option_follow(struct XorrisO *xorriso, char *mode, int flag) { int was_fl, was_fm, was_fpr, was_fpt, was_fc, l; double num; char *cpt, *npt; was_fpt= xorriso->do_follow_pattern; was_fpr= xorriso->do_follow_param; was_fl= xorriso->do_follow_links; was_fc= xorriso->do_follow_concat; was_fm= xorriso->do_follow_mount; xorriso->do_follow_pattern= 0; xorriso->do_follow_param= 0; xorriso->do_follow_links= 0; xorriso->do_follow_concat= 0; xorriso->do_follow_mount= 0; npt= cpt= mode; for(cpt= mode; npt!=NULL; cpt= npt+1) { npt= strchr(cpt,':'); if(npt==NULL) l= strlen(cpt); else l= npt-cpt; if(l==0) goto unknown_mode; if(strncmp(cpt, "off", l)==0) { xorriso->do_follow_pattern= 0; xorriso->do_follow_param= 0; xorriso->do_follow_links= 0; xorriso->do_follow_concat= 0; xorriso->do_follow_mount= 0; } else if(strncmp(cpt, "on", l)==0) { xorriso->do_follow_pattern= 1; xorriso->do_follow_param= 1; xorriso->do_follow_links= 1; xorriso->do_follow_concat= 1; xorriso->do_follow_mount= 1; } else if(strncmp(cpt, "default", l)==0) { xorriso->do_follow_pattern= 1; xorriso->do_follow_param= 0; xorriso->do_follow_links= 0; xorriso->do_follow_concat= 0; xorriso->do_follow_mount= 1; xorriso->follow_link_limit= 100; } else if(strncmp(cpt, "link", l)==0 || strncmp(cpt,"links", l)==0) { xorriso->do_follow_links= 1; } else if(strncmp(cpt, "mount", l)==0) { xorriso->do_follow_mount= 1; } else if(strncmp(cpt,"param", l)==0) { xorriso->do_follow_param= 1; } else if(strncmp(cpt, "pattern", l)==0) { xorriso->do_follow_pattern= 1; } else if(strncmp(cpt, "concat", l)==0) { xorriso->do_follow_concat= 1; } else if(strncmp(cpt, "limit=", 6)==0) { sscanf(cpt+6, "%lf", &num); if(num<=0 || num>1.0e6) { sprintf(xorriso->info_text, "-follow: Value too %s with '%s'", num<=0 ? "small" : "large", cpt+6); goto sorry_ex; } xorriso->follow_link_limit= num; } else { unknown_mode:; if(l<SfileadrL) sprintf(xorriso->info_text, "-follow: unknown mode '%s'", cpt); else sprintf(xorriso->info_text, "-follow: oversized mode parameter (%d)",l); sorry_ex: Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); xorriso->do_follow_pattern= was_fpt; xorriso->do_follow_param= was_fpr; xorriso->do_follow_links= was_fl; xorriso->do_follow_concat= was_fc; xorriso->do_follow_mount= was_fm; return(0); } } return(1); } /* Command -for_backup */ int Xorriso_option_for_backup(struct XorrisO *xorriso, int flag) { Xorriso_option_hardlinks(xorriso, "on", 0); Xorriso_option_acl(xorriso, "on", 0); Xorriso_option_xattr(xorriso, "any", 0); Xorriso_option_md5(xorriso, "on", 0); if(xorriso->lfa_flags_default & 8) Xorriso_option_lfa_flags(xorriso, "default:on:import_only_settable:restore_mask=aAcdDijmPsStTux", 0); return(1); } /* Option -fs */ int Xorriso_option_fs(struct XorrisO *xorriso, char *size, int flag) { double num; num= Scanf_io_size(size, 0); if(num < 64*1024 || num > 1024.0 * 1024.0 * 1024.0) { sprintf(xorriso->info_text, "-fs: wrong size %.f (allowed: %.f - %.f)", num, 64.0 * 1024.0, 1024.0 * 1024.0 * 1024.0); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } xorriso->fs= num / 2048.0; if(xorriso->fs * 2048 < num) xorriso->fs++; return(1); } /* Command -genisoimage_completion */ int Xorriso_option_genisoimage_completion(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "on") == 0) { xorriso->genisoimage_completion= 1; } else if(strcmp(mode, "off") == 0) { xorriso->genisoimage_completion= 0; } else { sprintf(xorriso->info_text, "-genisoimage_completion: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } return(1); } /* Commands -getfacl alias -getfacli, -getfacl_r alias -getfacl_ri -getfattr alias getfattri */ /* @param flag bit0= recursive -getfacl_r bit1= getfattr rather than getfacl bit3= with bit1: do not ignore eventual non-user attributes */ int Xorriso_option_getfacli(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int i, ret, was_failure= 0, end_idx, fret; int optc= 0; char **optv= NULL; struct FindjoB *job= NULL; struct stat dir_stbuf; ret= Xorriso_opt_args(xorriso, "-getfacl", argc, argv, *idx, &end_idx, &optc, &optv, 0); if(ret<=0) goto ex; for(i= 0; i<optc; i++) { if(flag&1) { ret= Findjob_new(&job, optv[i], 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "-getfacl_r", 0); {ret= -1; goto ex;} } if(flag & 2) { Findjob_set_action_target(job, 26, NULL, 0); } else Findjob_set_action_target(job, 24, NULL, 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, optv[i], &dir_stbuf, 0, 0); Findjob_destroy(&job, 0); } else { if(flag & 2) ret= Xorriso_getfattr(xorriso, NULL, optv[i], NULL, flag & 8); else ret= Xorriso_getfacl(xorriso, NULL, optv[i], NULL, 0); } if(ret>0 && !xorriso->request_to_abort) continue; /* regular bottom of loop */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; ret= 0; goto ex; } ret= 1; ex:; (*idx)= end_idx; Xorriso_opt_args(xorriso, "-getfacl", argc, argv, *idx, &end_idx, &optc, &optv, 256); Findjob_destroy(&job, 0); if(ret<=0) return(ret); return(!was_failure); } /* Option -gid */ int Xorriso_option_gid(struct XorrisO *xorriso, char *gid, int flag) { int ret; xorriso->do_global_gid= 0; if(gid[0]==0 || strcmp(gid,"-")==0) return(1); ret= Xorriso_convert_gidstring(xorriso, gid, &(xorriso->global_gid), 0); if(ret>0) xorriso->do_global_gid= 1; return(ret); } /* Option -grow_blindly */ int Xorriso_option_grow_blindly(struct XorrisO *xorriso, char *msc2, int flag) { double num; int l; if(msc2[0]==0 || msc2[0]=='-' || strcmp(msc2, "off")==0) { xorriso->grow_blindly_msc2= -1; return(1); } num= Scanf_io_size(msc2, 0); l= strlen(msc2); if(msc2[l-1]<'0' || msc2[l-1]>'9') num/= 2048.0; xorriso->grow_blindly_msc2= num; return(1); } /* Option -hardlinks "on"|"off" */ int Xorriso_option_hardlinks(struct XorrisO *xorriso, char *mode, int flag) { int ret; char *what_data= NULL, *what, *what_next; Xorriso_alloc_meM(what_data, char, SfileadrL); if(Sfile_str(what_data, mode, 0)<=0) { sprintf(xorriso->info_text, "-hardlinks: mode string is much too long (%d)", (int) strlen(mode)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } for(what= what_data; what != NULL; what= what_next) { what_next= strchr(what, ':'); if(what_next != NULL) { *what_next= 0; what_next++; } if(strcmp(what, "off") == 0) { Xorriso_finish_hl_update(xorriso, 0); xorriso->ino_behavior|= 1 | 2 | 4; xorriso->ino_behavior&= ~8; } else if(strcmp(what, "on") == 0) { xorriso->ino_behavior&= ~(1 | 2 | 4 | 8); } else if(strcmp(what, "without_update") == 0) { Xorriso_finish_hl_update(xorriso, 0); xorriso->ino_behavior&= ~(1 | 2 | 4); xorriso->ino_behavior|= 8; } else if(strcmp(what, "start_update") == 0) { xorriso->ino_behavior&= ~(1 | 2 | 4 | 8); ret= Xorriso_make_di_array(xorriso, 1); if(ret <= 0) goto ex; } else if(strcmp(what, "end_update") == 0) { Xorriso_finish_hl_update(xorriso, 0); } else if(strcmp(what, "perform_update") == 0) { Xorriso_finish_hl_update(xorriso, 0); } else if(strcmp(what, "start_extract") == 0) { xorriso->ino_behavior&= ~(1 | 2 | 4); ret= Xorriso_make_hln_array(xorriso, 1); if(ret <= 0) goto ex; } else if(strcmp(what, "end_extract") == 0) { Xorriso_destroy_hln_array(xorriso, 0); } else if(strcmp(what, "discard_extract") == 0) { Xorriso_destroy_hln_array(xorriso, 0); } else if(strcmp(what, "normal_extract") == 0) { xorriso->ino_behavior&= ~16; } else if(strcmp(what, "cheap_sorted_extract") == 0) { xorriso->ino_behavior|= 16; } else if(strcmp(what, "lsl_count") == 0) { xorriso->ino_behavior&= ~32; } else if(strcmp(what, "no_lsl_count") == 0) { xorriso->ino_behavior|= 32; } else { sprintf(xorriso->info_text, "-hardlinks: unknown mode '%s' in '%s'", what, mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } } /* <<< ts B00613 : This is wrong: it enables new_rr if -hardlinks is off. Documented is that new_rr gets enabled if hardlinks are on. But it never worked that way. A compromise seems to be to disable this totally and to change man xorriso. new_rr still is not recognized by mount on Solaris. if(xorriso->ino_behavior & 2) Xorriso_option_compliance(xorriso, "new_rr", 0); */ ret= 1; ex:; Xorriso_free_meM(what_data); return(ret); } /* Option -help and part of -prog_help */ int Xorriso_option_help(struct XorrisO *xorriso, int flag) { static char text[][80]={ #ifdef Xorriso_no_helP "This binary program does not contain a help text.", "If available, read: man 1 xorriso", #else "This program creates, loads, manipulates and writes ISO 9660 filesystem", "images with Rock Ridge extensions. Write targets can be drives with optical", "media or local filesystem objects.", "The program operations are controlled by a sequence of commands, of which", "the initial ones are given as program arguments or as lines in startup", "files. Further commands may get read from files in batch mode or from", "standard input in dialog mode.", "", " -x Only in effect if given as program argument:", " Execute commands given as program arguments in a sequence", " that most likely makes some sense. Default is to execute", " program arguments exactly in the sequence as given.", "", "Preparation commands:", "Drive addresses are either /dev/... as listed with command -devices or", "disk files, eventually with prefix \"stdio:\" if non-CD-drive in /dev tree.", "E.g. /dev/sr0 , /tmp/pseudo_drive , stdio:/dev/sdc", " -dev address Set input and output drive and load eventual ISO image.", " Set the image expansion method to growing.", " -indev address Set input drive and load eventual ISO image. Use expansion", " methods modifying or blind growing.", " -outdev address", " Set output drive and use modifying or blind growing.", " -drive_class \"harmless\"|\"banned\"|\"risky\"|\"clear_list\" disk_pattern", " Add a drive path pattern to one of the safety lists or make", " those lists empty. Defaulty entry in \"risky\" is \"/dev\".", " -drive_access \"exclusive\"|\"shared\":\"unrestricted\"|\"readonly\"", " Enable or disable device file locking mechanisms.", " Enable or disable status and content changes of drive.", " -scsi_dev_family \"default\"|\"sr\"|\"scd\"|\"sg\"", " Linux specific: Choose device file type.", " -read_speed number[\"k/s\"|\"[x]CD\"|\"[x]DVD\"|\"[x]BD\"]|keyword", " Set the read speed. Default is \"none\" = do not set speed", " before reading. Prefix \"soft_force:\" enables slowdown by", " software.", " -grow_blindly \"off\"|predicted_nwa", " Switch between modifying and blind growing.", " -load \"session\"|\"track\"|\"lba\"|\"sbsector\"|\"volid\"|\"auto\" id", " -load \"at_time\"|\"before\"|\"after\"|\"not_before\"|\"not_after\" time", " Load a particular (outdated) ISO session from a -dev or", " -indev which hosts more than one session.", " -displacement [-]block_address", " When loading ISO tree or reading data files compensate a", " displacement versus the start address for which the image", " was prepared.", " -read_fs \"any\"|\"norock\"|\"nojoliet\"|\"ecma119\"", " Specify which kind of filesystem tree to load if present.", " -rom_toc_scan \"on\"|\"force\"|\"off\"[:\"emul_on\"|\"emul_off\"]", " [:\"emul_wide\"|\"emul_narrow\"]", " Enable scanning for ISO sessions on read-only drives/media", " and on overwritable media with emulated TOC.", " -calm_drive \"in\"|\"out\"|\"all\"|\"on\"|\"off\"", " Reduce drive noise until it gets actually used again.", " -assert_volid pattern severity", " Accept input image only if its volume id matches pattern.", " -charset name Set the character set name to be used for file name", " conversion from and to media.", " -in_charset name", " Like -charset but only for conversion from media.", " -auto_charset \"on\"|\"off\"", " Enable writing and reading of character set name in image.", " -out_charset name", " Like -charset but only for conversion to media.", " -local_charset name", " Override system assumption of the local character set name.", " -hardlinks mode[:mode ...]", " Enable or disable recording and restoring of hard links.", " Modes are \"on\", \"off\", \"perform_update\",", " \"without_update\", \"discard_extract\",", " \"cheap_sorted_extract\", \"normal_extract\"", " -acl \"on\"|\"off\"", " Enable or disable reading and writing of ACLs.", " -xattr \"on\"|\"user\"|\"any\"|\"off\"", " Enable or disable reading and writing of xattr.", " -lfa_flags mode[:mode ...]", " Enable or disable reading and restoring of Linux chattr", " flags.", " -md5 \"on\"|\"all\"|\"off\"", " Enable or disable processing of MD5 checksums.", " -for_backup", " Shortcut for: -hardlinks on -acl on -xattr any -md5 on", " possibly: -lfa_flags default:on:restore_mask=aAcCdDijmPsStTux", " -ecma119_map \"unmapped\"|\"stripped\"|\"uppercase\"|\"lowercase\"", " Choose conversion of file names if neither Rock Ridge", " nor Joliet is present in the loaded ISO session.", " -joliet_map \"unmapped\"|\"stripped\"", " Choose conversion of file names if the Joliet tree is read", " when loading the ISO session.", " -iso_nowtime \"dynamic\"|timestring", " Choose use of current time or a fixed point in time for", " timestamps where libisofs would normally use the current", " (i.e. dynamic) time.", " -disk_dev_ino \"on\"|\"ino_only\"|\"off\"", " Enable or disable recording of disk file dev_t and ino_t", " and their use in file comparison.", " -scdbackup_tag list_path record_name", " Enable production of scdbackup tag with -md5 on", " -ban_stdio_write", " Allow for writing only the usage of optical drives.", " -early_stdio_test \"on\"|\"appendable_wo\"|\"off\"", " Classify stdio drives by effective access permissions.", " -data_cache_size number_of_tiles blocks_per_tile", " Adjust size and granularity of the data read cache.", " -blank [\"force:\"]\"fast\"|\"all\"|\"deformat\"|\"deformat_quickest\"", " Blank medium or invalidate ISO image on medium.", " Prefix \"force:\" overrides medium evaluation.", " -truncate_overwritable entity id adjust", " Activate an older session on overwritable medium. Adjust", " its size to some value not smaller than original old size.", " -close_damaged \"as_needed\"|\"force\"", " Close track and session of damaged medium.", " -format \"as_needed\"|\"full\"|\"fast\"|\"by_index_#\"|\"by_size_#\"", " Format BD-RE, BD-R, DVD-RAM, DVD-RW, DVD+RW.", " -volid volume_id", " Specifies the volume ID text. (32 chars out of [A-Z0-9_])", " -volset_id name", " Specifies the volume set id. (128 chars)", " -publisher name", " Specifies the publisher name. (128 chars)", " -application_id name", " Specifies the application id. (128 chars)", " -system_id name", " Specifies the system id for the System Area. (32 chars)", " -volume_date type timestring", " Specifies volume timestamps. [\"c\",\"m\",\"x\",\"f\",\"uuid\"]", " -copyright_file name", " Specifies the name of the Copyright File. (37 chars)", " -biblio_file name", " Specifies the name of the Bibliographic File. (37 chars)", " -abstract_file name", " Specifies the name of the Abstract File. (37 chars)", " -application_use character|0xXY|disk_path", " Specifies the content of Application Use field. (512 bytes)", " A single character or a hex code gets repeated 512 times.", " Other text gets opened as data file and 512 bytes are read.", " -joliet \"on\"|\"off\"", " Generate Joliet info additional to Rock Ridge info.", " -hfsplus \"on\"|\"off\"", " Generate a HFS+ partition and filesystem within ISO image.", " -rockridge \"on\"|\"off\"", " Opportunity to omit Rock Ridge info. (Do not do it !)", " -jigdo \"clear\"|\"template_path\"|\"jigdo_path\"|\"md5_path\"", " |\"min_size\"|\"checksum_iso\"|\"checksum_template\"", " |\"checksum_path\"|\"demand_checksum\"|\"checksum_algorithm\"", " |\"compression\"|\"exclude\"|\"demand_md5\"|\"mapping\"", " |\"checksum_iso\"|\"checksum_template\"", " value", " Clear Jigdo Template Extraction parameter list or add a", " parameter with its value to that list.", " -compliance rule[:rule...]", " Allow more or less harmless deviations from strict standards", " compliance.", " -rr_reloc_dir name", " Specifies name of relocation directory in root directory,", " to which deep subtrees will get relocated if -compliance", " is set to \"deep_paths_off\".", " -boot_image \"any\"|\"isolinux\"|\"grub\"", " |\"discard\"|\"keep\"|\"patch\"|\"replay\"", " |\"dir=\"|\"bin_path=\"", " |\"cat_path=\"|\"cat_hidden=on|iso_rr|joliet|off\"", " |\"load_size=\"|\"boot_info_table=\"", " |\"grub2_boot_info=\"|\"grub2_mbr=\"|\"partition_offset=\"", " |\"partition_hd_cyl=\"|\"partition_sec_hd=\"", " |\"partition_cyl_align=\"|\"mbr_force_bootable=\"", " |\"system_area=\"|\"partition_table=on|off\"", " |\"partition_entry=\"|\"appended_part_as=\"", " |\"part_like_isohybrid=\"|\"iso_mbr_part_type=\"", " |\"gpt_disk_guid=\"|\"gpt_iso_bootable\"|\"gpt_iso_not_ro=\"", " |\"chrp_boot_part=on|off=\"|\"prep_boot_part=\"", " |\"efi_boot_part=\"|\"efi_boot_part=--efi-boot-image\"", " |\"mips_path=\"|\"mipsel_path=\"|\"mips_discard\"", " |\"sparc_label=\"|\"grub2_sparc_core=\"|\"sparc_discard\"", " |\"hppa_cmdline=\"|\"hppa_bootloader=\"|\"hppa_kernel_32=\"", " |\"hppa_kernel_64=\"|\"hppa_ramdisk=\"|\"hppa_hdrversion=\"", " |\"hppa_discard\"|\"alpha_boot=\"|\"alpha_discard\"", " |\"hfsplus_serial=\"|\"hfsplus_block_size=\"", " |\"apm_block_size=\"|\"show_status\"", " Whether to discard or keep an exiting El Torito boot image,", " or to freshly set up boot equipment. \"replay\" performs", " the commands proposed by -report_system_area \"cmd\".", " ISOLINUX can be made bootable by dir=/ or dir=/isolinux", " or dir=/boot/isolinux. Others, like GRUB, by bin_path=...", " and cat_path=...", " The boot image and its helper files need to be added to the", " ISO image by the usual commands like -map or -add.", " system_area= and partition_table= are for MBR based booting", " from USB stick. The system_area= file needs not to be added.", " chrp_boot_part= and prep_boot_part= are for PowerPC.", " efi_boot_part= is for booting EFI systems from USB stick.", " mips_path= adds Big Endian MIPS boot files. mipsel_path=", " sets one Little Endian MIPS boot file. sparc_label=", " activates SUN Disk Label. hppa_* is for HP PA-RISC via PALO.", " alpha_boot= is for DEC Alpha SRM. MIPS, SUN, HP, and Alpha", " are mutually exclusive and exclusive to production", " of MBR and to booting via EFI from USB stick.", " -append_partition partition_number type_code disk_path", " Append a prepared filesystem image after the end of the", " ISO image. Caution: Will be overwritten by multi-session.", "", " -uid uid User id to be used for the whole multi-session ISO image.", " -gid gid Group id for the same purpose.", "", " -devices Show list of available optical drives and their addresses.", " -device_links Like devices, but showing link paths which are hopefully", " persistent over reboot on modern Linux systems.", "", " -toc Show media specific tables of content (sessions).", " -toc_of \"in\"|\"out\"|\"all\"[\":short\"]", " Show -toc of either input drive or output drive or both.", " -toc_info_type \"volid\"|\"creation_time[_gmt]\"|\"modification_time[_gmt]\"", " Choose what to show in the right column of -toc and -toc_of.", "", " -assess_indev_features \"plain\"|\"cmd\"|\"as_mkisofs\"|\"replay\"", " Inspect the filesystem on -indev for features like ISO level,", " Rock Ridge, or file name relaxations.", "", " -mount_cmd drive entity id path", " Print to result channel a command suitable to mount the", " depicted entity (see -load) at the given directory path.", " -mount_opts \"exclusive\"|\"shared\"", " Set options for -mount and -mount_cmd.", " -session_string drive entity id \"linux:\"path|\"freebsd:\"path|form", " Print foreign OS command or custom line.", "", " -list_formats Show media specific list of format descriptors.", "", " -list_speeds Show media specific list of write speed descriptors.", "", " -list_profiles \"in\"|\"out\"|\"all\"", " Show list of media types supported by indev and/or outdev.", " -print_size Print the foreseeable consumption by next -commit.", "", " -tell_media_space", " Print foreseeable available space on output medium", " -pvd_info Print various id strings of the loaded ISO image.", "", " -report_el_torito \"plain\"|\"help\"|\"cmd\"|\"as_mkisofs\"", " \"plain\" prints information about the El Torito boot catalog", " and boot images of the loaded ISO image.", " \"help\" prints an explanation of the output format.", " \"cmd\" and \"as_mkisofs\" propose commands to reproduce", " the boot equipment reported by -report_el_torito \"plain\"", " and -report_system_area \"plain\"", " -report_system_area \"plain\"|\"help\"|\"gpt_crc_of:\"disk_path", " |\"cmd\"|\"as_mkisofs\"", " \"plain\" prints information about recognized data", " \"help\" prints an explanation of the output format.", " in the System Area of the loaded ISO image: MBR, GPT, ...", " \"gpt_crc_of:\" prints GPT CRC of file disk_path.", " For \"cmd\" and \"as_mkisofs\" see -report_el_torito.", "", "Commands with variable length path list [...] need the list delimiter text", "as end mark if they are followed by another command. By default this", "delimiter is \"--\". In dialog and with commands read from files, the line", "end serves as such a mark. With program arguments this mark can be omitted", "only with the last command in the list of program arguments.", "For brevity the list delimiter is referred as \"--\" throughout this text.", "", " -list_delimiter text Set the list delimiter to be used instead of \"--\"", " It has to be a single word, must not be empty, not longer", " than 80 characters, may not contain quotation marks.", "", "Manipulation commands:", "disk_path is a path to an object in the local filesystem tree.", "iso_rr_path is the Rock Ridge name of a file object in the ISO image.", "pathspec is either a disk_path or (if allowed) a pair: iso_rr_path=disk_path", "Commands marked by [***] have variable length parameter lists and perform", "pattern expansion if enabled by -iso_rr_pattern or -disk_pattern.", "", " -pathspecs \"on\"|\"off\"|\"as_mkisofs\"", " Allow or disallow pathspecs of form iso_rr_path=disk_path", " Only \"off\" allows -disk_pattern expansion.", " -file_name_limit number", " Set truncation size for file names [64 ... 255].", " -file_size_limit value [...]", " Set limit for file content size. One or more numbers to add.", " -add pathspec [...] | disk_path [***]", " Insert the given files or directory trees from", " filesystem into the ISO image. Much like mkisofs.", " -add_plainly \"none\"|\"unknown\"|\"dashed\"|\"any\"", " Whether to add lonely arguments as pathspec or disk_path.", " -path_list disk_path", " Like -add but read the pathspecs from file disk_path.", " -quoted_path_list disk_path", " Like -path_list but with line rules as -dialog \"on\".", "", " -map disk_path iso_rr_path", " Insert disk file object at the given iso_rr_path.", " -map_single disk_path iso_rr_path", " Like -map but with directory do not insert its sub tree.", " -map_l disk_prefix iso_rr_prefix disk_path [***]", " Performs -map with each disk_path.", " -update disk_path iso_rr_path", " Compare both file objects and do what is necessary to make", " iso_rr_path a matching copy of disk_path.", " -update_r disk_path iso_rr_path", " Like -update but affecting all files below directories.", " -update_l disk_prefix iso_rr_prefix disk_path [***]", " Performs -update_r with each disk_path.", " -update_li iso_rr_prefix disk_prefix iso_rr_path [***]", " Performs -update_r with each iso_rr_path.", " -update_lxi disk_prefix iso_rr_prefix disk_or_iso_rr_path [***]", " Performs -update_r with each disk_path or corresponding", " iso_rr_path after exchange of disk_prefix by iso_rr_prefix.", " -cut_out disk_path byte_offset byte_count iso_rr_path", " Map a byte interval of a regular disk file into a regular", " file in the ISO image.", "", " -cpr disk_path [***] iso_rr_path", " Insert the given files or directory trees from filesystem", " into the ISO image, according to the rules of cp -r.", "", " -rm iso_rr_path [***]", " Delete the given files from the ISO image.", " -rm_r iso_rr_path [***]", " Delete the given directory trees from ISO image.", " -move iso_rr_path iso_rr_path", " Rename the single file given by the first iso_rr_path to", " the second iso_rr_path.", " -mv iso_rr_path [***] iso_rr_path", " Like shell command mv rename the given file objects in the", " ISO tree to the last of the iso_rr_path parameters.", " -chown uid iso_rr_path [***]", " Equivalent to chown in the ISO image.", " -chown_r uid iso_rr_path [***]", " Like -chown but affecting all files below directories.", " -chgrp gid iso_rr_path [***]", " Equivalent to chgrp in the ISO image.", " -chgrp_r gid iso_rr_path [***]", " Like -chgrp but affecting all files below directories.", " -chmod mode iso_rr_path [***]", " Equivalent to chmod in the ISO image.", " -chmod_r mode iso_rr_path [***]", " Like -chmod but affecting all files below directories.", " -setfacl acl_text iso_rr_path [***]", " Replace the permissions and eventual ACL of the given files", " in the ISO image by the ACL which is defined by acl_text.", " -setfacl_r acl_text iso_rr_path [***]", " Like -setfacl but affecting all files below directories.", " -setfacl_list disk_path", " Read output of getfacl from file disk_path. Set owner,", " group and ACL of the iso_rr_path given by line \"# file:\".", " -setfattr [-]name value iso_rr_path [***]", " Set xattr pair with the given name to the given value, or", " delete pair if name is prefixed with \"-\" and value is", " an empty text.", " -setfattr_r [-]name value iso_rr_path [***]", " Like -setfattr but affecting all files below directories.", " -setfattr_list disk_path", " Read output of getfattr from file disk_path. Replace the", " xattr of the iso_rr_path given by line \"# file:\".", " -chattr \"+\"|\"-\"|\"=\"|\".\"mode iso_rr_path [***]", " Set or unset Linux chattr flags of the given files.", " -alter_date type timestring iso_rr_path [***]", " Alter the date entries of a file in the ISO image. type is", " one of \"a\", \"m\", \"b\" for:", " access time, modification time, both times.", " -alter_date_r type timestring iso_rr_path [***]", " Like -alter_date but affecting all files below directories.", " -hide on|iso_rr:joliet:hfsplus|off iso_rr_path [***]", " Keep names of files out of directory trees, but store their", " data content in the image.", " -find iso_rr_path [test [op] [test ...]] [-exec action [params]]", " performs an action on files below the given directory in", " the ISO image. Tests:", " -name pattern, -wholename pattern, -disk_name pattern,", " -disk_path pattern, -use_pattern on|off,", " -or_use_pattern on|off, -name_limit_blocker length,", " -type b|c|d|p|f|l|s|e, -pending_data, -hidden,", " -lba_range start count, -damaged, -has_acl, -has_xattr,", " -has_aaip, -has_filter, -has_md5, -has_any_xattr,", " -has_lfa_flags letters, -has_some_lfa_flags_of letters,", " -has_hfs_crtp, -has_hfs_bless, -bad_outname,", " -name_limit_blocker, -maxdepth, -mindepth, -size,", " -prune, -decision yes|no, -true, -false", " Operators: -not, -or, -and, -sub, (, -subend, ),", " -if, -then, -elseif, -else, -endif", " Action may be one of: echo, chown, chown_r, chgrp, chgrp_r,", " chmod, chmod_r, alter_date, alter_date_r, set_to_mtime,", " lsdl, compare, rm, rm_r, compare, update, report_damage,", " report_lba, report_sections,", " getfacl, setfacl, getfattr, setfattr, get_any_xattr,", " list_extattr, get_md5, check_md5, make_md5,", " set_hfs_crtp, get_hfs_crtp, set_hfs_bless, get_hfs_bless,", " set_filter, show_stream, show_stream_id, mkisofs_r,", " hide, print_outname, estimate_size, in_iso, not_in_iso", " add_missing, empty_iso_dir, is_full_in_iso, sort_weight", " update_merge, rm_merge, clear_merge, lsattrd, chattr, find", " params are their parameters except iso_rr_path.", " -mkdir iso_rr_path [...]", " Create empty directories if they do not exist yet.", " -lns target_text iso_rr_path", " Create a symbolic link pointing to target_text", " -rmdir iso_rr_path [***]", " Delete empty directories.", " -clone iso_rr_path_original iso_rr_path_copy", " Create an ISO copy of an ISO file or ISO directory tree.", " -cp_clone iso_rr_path_original [***] iso_rr_path_dest", " Create ISO to ISO copies according to the rules of cp -r.", "", " -- Default list delimiter marking the end of command parameter", " lists. It may be changed by command -list_delimiter.", "", " -not_paths disk_path [***]", " Add the given paths to the list of excluded absolute paths.", " -not_leaf pattern", " Add the given pattern to the list of leafname exclusions.", " -not_list disk_path", " Read lines from disk_path and use as -not_paths (with \"/\")", " or as -not_leaf (without \"/\").", " -quoted_not_list disk_path", " Like -not_list but with line rules as -dialog \"on\".", " -not_mgt \"reset\"|\"on\"|\"off\"|\"param_on\"|\"subtree_on\"|\"ignore_on\"", " Control effect of exclusion lists.", " -follow \"on\"|\"pattern:param:link:concat:mount:limit=#\"|\"default\"|\"off\"", " Follow symbolic links and mount points within disk_path.", " -overwrite \"on\"|\"nondir\"|\"off\"", " Allow or disallow to overwrite existing files in ISO image.", " -split_size number[\"k\"|\"m\"]", " Set the threshold for automatic splitting of regular files.", " -reassure \"on\"|\"tree\"|\"off\"", " If \"on\" then ask the user for \"y\" or \"n\" with any", " file before deleting or overwriting it in the ISO image.", "", "Filter commands:", "External filter processes may produce synthetic file content by reading the", "original content from stdin and writing to stdout whatever they want.", #ifdef Xorriso_allow_external_filterS " -external_filter name option[:option] program_path [arguments] --", " Define an external filter. Options are: suffix=...: ", " remove_suffix:if_nonempty:if_reduction:if_block_reduction.", " -unregister_filter name", " Undefine an external filter.", " -close_filter_list", " Irrevocably ban -external_filter and -unregister_filter.", #else "Sorry: The use of external filters was not enabled at compile time.", " E.g. by ./configure option --enable-external-filters", #endif /* ! Xorriso_allow_external_filterS */ " -set_filter name iso_rr_path [***]", " Apply a defined filter to the given data files.", " Special name \"--remove-all-filters\" revokes filtering.", " Builtin filters are --gzip , --gunzip, --zisofs .", " -set_filter_r name iso_rr_path [***]", " Like -set_filter but affecting all files below directories.", "", "zisofs is a compression format which is recognized by some Linux kernels.", "xorriso supports it by builtin filter \"--zisofs\" which is to be applied by", "the user, and by \"--zisofs-decode\" which is applied automatically when", "compressed content is detected with a file in the ISO image.", " -zisofs option[:options]", " Set global zisofs parameters:", " level=0|...|9 , block_size=32k|64k|128k , by_magic=on|off", " version_2=off|as_needed|on , block_size_v2=32k|...|1024k", " max_bpt=1k...128g , max_bpt_f=1k...128g , bpt_target=num", " bpt_free_ratio=-1|[0.0...1.0] , susp_z2=off|on" "", "Write-to-media commands:", " -rollback Discard the manipulated ISO image and reload it.", "", " -changes_pending \"no\"|\"yes\"|\"mkisofs_printed\"|\"show_status\"", " Override the automatically determined change status of the", " loaded image, or show the current status.", " -commit Perform the write operation if changes are pending.", " Then perform -dev outdrive.", " Hint: To perform a final write operation with no new -dev", " and no new loading of image, execute command -end.", " -commit_eject \"in\"|\"out\"|\"all\"|\"none\"", " Like -commit but rather eject than load image from outdrive.", " Give up any unejected drive afterwards.", " -write_type \"auto\"|\"tao\"|\"sao/dao\"", " Set write type for CD-R[W], DVD-R[W], DVD+R, BD-R.", " -close \"on\"|\"off\"|\"as_needed\"", " If \"on\" then mark the written medium as not appendable.", " -padding number[\"k\"|\"m\"]|\"included\"|\"appended\"", " Append extra bytes to image stream. (Default is 300k)", " -dummy \"on\"|\"off\"", " If \"on\" simulate burning. Refuse if medium cannot simulate.", " -speed number[\"k/s\"|\"m/s\"|\"[x]CD\"|\"[x]DVD\"|\"[x]BD\"]", " Set the burn speed. Default is \"max\" = maximum speed.", " -stream_recording \"on\"|\"off\"", " Try to circumvent slow checkread on DVD-RAM, BD-RE, BD-R.", " -modesty_on_drive [\"on\"|\"off\"|min_percent_number]", " [:\"min_percent=\"number][:\"max_percent=\"number]", " [:\"min_usec=\"number][:\"max_usec\"=number]", " [:\"timeout_sec=\"number]", " Keep drive buffer hungry to ease concurrent burn run.", " -dvd_obs \"default\"|\"32k\"|\"64k\"|\"obs_pad\"|\"bdr_obs_exempt\"", " Set number of bytes per DVD/BD write operation or control", " end alignment padding with DAO DVD-R[W] or BD-R.", " -use_immed_bit \"on\"|\"off\"|\"default\"", " Control whether long running SCSI commands shall run", " asynchronously with progress messages.", " -stdio_sync \"on\"|\"off\"|\"end\"|number", " Set number of bytes after which to force output to stdio", " pseudo drives. \"on\" is the same as 16m.", " -fs number[\"k\"|\"m\"]", " Set the size of the fifo buffer. (Default is 4m)", " -eject \"in\"|\"out\"|\"all\"", " Immediately eject the medium in -indev, -outdev, or both.", "", "Navigation commands:", "", " -cd iso_rr_path Change working directory in the ISO image. iso_rr_paths", " which do not begin with '/' will be inserted beginning at", " the path given with -cd. -ls patterns will eventually", " looked up at this path.", " -cdi disk_path Same as -cd disk_path", " -cdx disk_path Change the current working directory in the local", " filesystem. disk_paths which do not begin with '/'", " will be looked up beginning at the path given with -cdx.", " -lsx patterns will eventually be looked up at this path.", " -pwd tells the current working directory in the ISO image.", " -pwdi same as -pwd.", " -pwdx tells the current working directory in the local filesystem.", "", " -iso_rr_pattern \"on\"|\"ls\"|\"off\"", " Enable or disable pattern expansions for ISO image commands", " marked by [***]. \"ls\" restricts it to -ls and -du.", " -disk_pattern \"on\"|\"ls\"|\"off\"", " Enable or disable pattern expansions for local filesystem", " commands marked by [***]. \"ls\" restricts to -ls*x and -du*x.", "", " -ls pattern [***] lists files of the ISO image which match one of the", " given shell parser patterns. (I.e. wildcards '*' '?').", " Directories are listed by their content.", " -lsd pattern [***] like -ls but listing directories as single items.", " -lsl pattern [***] like -ls but also telling some file attributes.", " -lsdl pattern [***] like -lsd but also telling some file attributes.", "", " -lsx pattern [***] lists files of the local filesystem which match one", " of the patterns. Directories are listed by their content.", " -lsdx pattern [***] like -lsx but listing directories as single items.", " -lslx pattern [***] like -lsx but also telling some file attributes.", " -lsdlx pattern [***] like -lsdx but also telling some file attributes.", " -lsattr pattern [***] lists Linux chattr flags of the given files.", " -lsattrd pattern [***] like -lsattr but listing directories as single items.", " -getfacl pattern [***] list eventual ACLs of the given files.", " -getfacl_r pattern [***] like -getfacl but listing whole file trees.", " -getfattr pattern [***] list eventual xattr of the given files.", " -getfxattr_r pattern [***] like -getfxattr but listing whole file trees.", "", " -du pattern [***] recursively lists sizes of files or directories in the", " ISO image which match one of the shell parser patterns.", " -dux pattern [***] recursively lists sizes of files or directories in the", " local filesystem which match one of the shell parser", " patterns.", " -dus pattern [***] like -du but summing up subdirectories without", " listing them explicitly.", " -dusx pattern [***] like -dux but summing up subdirectories without", " listing them explicitly.", "", " -findx disk_path [-name pattern] [-type t] [-exec action [params]]", " Like -find but operating on local filesystem. Most -exec", " actions are defaulted to action echo. Supported actions are:", " in_iso, not_in_iso, is_full_in_iso, add_missing,", " empty_iso_dir", "", " -compare disk_path iso_rr_path", " compare attributes and in case of regular data files the", " content of filesystem object and ISO object.", " -compare_r disk_path iso_rr_path", " Like -compare but affecting all files below directories.", " -compare_l disk_prefix iso_rr_prefix disk_path [***]", " Performs -compare_r with each disk_path.", "", " -show_stream iso_rr_path [***]", " Show content stream chain of data files in the ISO image.", " -show_stream_r iso_rr_path [***]", " Like -show_stream but affecting all files below directories.", "", "Restore commands which copy file objects from ISO image to disk filesystem:", " -osirrox \"on\"|\"device_files\"|\"off\"|\"blocked\"|\"unblock\"|\"banned\"", " [:\"concat_split_on\"|\"concat_split_off\"]", " [:\"auto_chmod_on\"|\"auto_chmod_off\"]", " [:\"sort_lba_on\"|\"sort_lba_off\"]", " [:\"strict_acl_on\"|\"strict_acl_off\"]", " [:\"check_md5_on\"|\"check_md5_off\"|\"check_md5_force\"]", " [:\"sparse=off\"|\"sparse=\"number]", " By default \"off\" the inverse operation of xorriso from ISO", " image to disk filesystem is disabled. \"on\" allows xorriso", " to create, overwrite, delete files in the disk filesystem.", " \"banned\" is irrevocably \"off\". \"blocked\" can only be", " revoked by \"unblock\". (\"device_files\" is dangerous.)", " -extract iso_rr_path disk_path", " Copy tree under iso_rr_path onto disk address disk_path.", " This avoids the pitfalls of cp -r addressing rules.", " -extract_l iso_rr_prefix disk_prefix iso_rr_path [***]", " Perform -extract with each iso_rr_path.", " -extract_single iso_rr_path disk_path", " Like -extract but with directory do not restore sub tree.", " -extract_cut iso_rr_path byte_offset byte_count disk_path", " Copy a byte interval from iso_rr_path to disk_path.", " This is governed in part by -check_media_defaults.", " -extract_boot_images disk_path", " Copy boot images into files in directory disk_path with", " names which tell the role inside the ISO.", " -cpx iso_rr_path [***] disk_path", " Copy leaf file objects from ISO image to disk filesystem.", " -cpax iso_rr_path [***] disk_path", " Like -cpx but trying to restore timestamps and ownership.", " -cp_rx iso_rr_path [***] disk_path", " Copy directory trees from ISO image to disk filesystem.", " -cp_rax iso_rr_path [***] disk_path", " Like -cp_rx but trying to restore timestamps and ownership.", " -paste_in iso_rr_path disk_path byte_offset byte_count", " Copy ISO file content into a byte interval of a disk file.", " -concat \"append\"|\"overwrite\" target iso_rr_path [***]", " Write content of iso_rr_paths into disk file target. Target", " may be a disk_path or \"-\" for writing to standard output.", " -concat \"pipe\" lim prog [args [...]] lim iso_rr_path [***]", " Start prog with given args and write content of iso_rr_paths", " into its standard input. prog must at least contain one '/'.", " lim may be any word that is not among prog and args.", " -mount drive entity id path", " Like -mount_cmd but actually performing that command if", " not setuid or setgid is active.", "", "Evaluation of readability:", " -check_media [options] --", " Try to read data blocks from the medium and report about the", " outcome. Several options modify the behavior:", " use=indev|outdev , what=track|session ,", " min_lba=blockadr , max_lba=blockadr ,", " abort_file=path , time_limit=seconds , item_limit=number ,", " retry=on|off|default , data_to=filepath ,", " sector_map=filepath , map_with_volid=on|off ,", " patch_lba0=on|off|force|blockadr[:force] ,", " report=blocks|files|blocks_files event=severity ,", " bad_limit=quality , slow_limit=seconds , chunk_size=bytes", " -check_media_defaults [options] --", " Preset options for runs of -check_media and -extract_cut.", "", "Compatibility emulation (option list may be ended by list delimiter --):", " -as mkisofs [-help|-version|-o|-R|-r|-J|-V|-P|-f|-m|-exclude-list|", " -no-pad|-M|-C|-graft-points|-path-list|pathspecs|-z|", " -no-emul-boot|-b|-c|-boot-info-table|-boot-load-size|-G|...]", " Perform some mkisofs gestures, understand pathspecs as mkisofs", " does. Commit happens outside emulation at usual occasions.", " For a list of options see -as mkisofs -help.", " -read_mkisofsrc", " Read and interpret the .mkisofsrc configuration file.", " -genisoimage_completion \"on\"|\"off\"", " Enable completion of genisoimage options during -as mkisofs.", " -as cdrecord [-help|-v|dev=|speed=|blank=|fs=|-eject|-atip|padsize=|-multi]", " path|-", " Perform some cdrecord gestures, eventually write at most one", " data track to blank, appendable or overwritable media.", " -pacifier \"xorriso\"|\"cdrecord\"|\"mkisofs\"", " Choose format of UPDATE pacifier during write operations.", "", "General commands:", " -help Print this text", " -abort_on severity Set the threshold for events to abort the program.", " Useful severities: NEVER, ABORT, FATAL, FAILURE, SORRY, WARNING", " -return_with severity exit_value Set the threshold for events to return", " at program end the given exit_value even if not aborted.", " exit_value may be 0 or 32 to 63.", " -report_about severity Set the threshold for events to be reported.", " Use -abort_on severities or: HINT, NOTE, UPDATE, DEBUG, ALL", " -signal_handling \"on\"|\"off\"|\"sig_dfl\"|\"sig_ign\"", " Handling of signals. Default \"on\" uses libburn handler.", " -error_behavior \"image_loading\"|\"file_extraction\" behavior", " Behavior \"best_effort\" is most endurant but may produce", " results which are correct only on the first glimpse.", " -dialog \"on\"|\"off\"|\"single_line\"", " After all program arguments are processed, enter dialog mode.", " \"single_line\" does not support newline characters within", " open quotation marks and no line continuation by trailing \\.", " -page len width Prompt user after len output lines (0=no prompt).", " width (default 80) can adjust line number computation", " to the output terminal's line width.", #ifdef Xorriso_with_line_editoR " -use_readline \"on\"|\"off\"", " Whether to use libreadline for dialog if available.", " -history text Copy text into libreadline history. This command", " itself is not copied to the history list.", #endif /* Xorriso_with_line_editoR */ " -sh_style_result \"on\"|\"off\"", " If \"on\" do not wrap file addresses in quotation marks with", " -pwd -pwdx -ls -lsd -lsl -lsdl -lsx -lsdx -lslx -lsdlx", " -du -dus -dux -dusx -findx -find", " -backslash_codes \"on\"|\"off\"|", " \"in_double_quotes\"|\"in_quotes\"|\"with_quoted_input\"", " [:\"with_program_arguments\"][:\"encode_output\"]", " Disable or enable interpretation of \\a \\b \\e \\f \\n \\r \\t \\v", " \\\\ \\NNN \\xNN \\cC in input or program arguments.", " -pkt_output \"on\"|\"off\" Direct output to stdout and prefix each line", " by a short header which tells channel id and a mode number.", " Each such output packet is finalized by a newline.", " Channel ids are 'R:' for result lines, 'I:' for notes", " and error messages, 'M:' for -mark texts. Bit 0 of the", " mode number tells whether the newline is also part of the", " packet payload. Example of a info message with newline:", " I:1: enter option text :", " -pkt_output:on is intended for use by frontend programs.", " -msg_op \"start_sieve\"|\"read_sieve\"|\"clear_sieve\"|\"end_sieve\"|", " \"parse\"|\"parse_silently\"|\"parse_bulk\"|\"parse_bulk_silently\"|", " \"compare_sev\"|\"list_sev\" param_text", " Enable, use, or disable message sieve. Or parse lines into", " words. Or compare or list severity names.", " -named_pipes_loop mode[:mode]", " disk_path_stdin disk_path_stdout disk_path_stderr", " Enter an EOF resistant dialog loop at a named pipe as input", " and zero, one or two named pipes as output. \"-\" = no pipe.", " Mode \"cleanup\" removes pipes at loop end. \"keep\" does not.", " Mode \"buffered\" reads all lines from input pipe before it", " opens the output pipes. \"direct\" opens after first input.", " -launch_frontend program [args ...] --", " Start a program, connect its stdin to xorriso stdout and", " stderr, connect its stdout to xorriso stdin.", " Use any given parameters as arguments for the started program.", " -logfile channel fileaddress Copy output of a channel to the given file.", " channel may be 'R','I','M' as with -pkt_output or '.'", " for the consolidated -pkt_output stream.", " -mark text If text is not empty it will get put out each time a command", " is completed.", " -temp_mem_limit number[\"k\"|\"m\"]", " Set the maximum size for pattern expansion. (Default is 16m)", " -prog text Use text as this program's name in subsequent messages", " -prog_help text Use text as this program's name and perform -help", " -status mode|filter Report the current settings of persistent commands.", " Modes:", " short... print only important or altered settings", " long ... print settings even if they have default values", " long_history like long plus -history: lines", " Filters begin with '-' and are compared literally against the", " output lines of -status long_history. A line is put out only", " if its start matches the filter.", " -status_history_max number Maximum number of history lines to be reported", " with -status:long_history", " -options_from_file fileaddress", " Reads lines from the given file and executes them as commands.", " -no_rc Only if used as first program argument, this command", " prevents reading and interpretation of these startup files:", " /etc/default/xorriso , /etc/opt/xorriso/rc", " /etc/xorriso/xorriso.conf , $HOME/.xorrisorc", " -print text", " Print a text to result channel.", " -print_info text", " Print a text to info channel.", " -print_mark text", " Print a text to mark channel.", " -prompt text", " Wait for Enter key or for a line of input at stdin.", " -sleep number", " Do nothing during the given number of seconds.", " -errfile_log mode path|channel", " Log disk paths of files involved in problem events.", " -session_log path", " Set path of a file where a log record gets appended after", " each session. Form: timestamp start_lba size volume-id", " -scsi_log \"on\"|\"off\"", " Enable or disable logging of SCSI commands to stderr.", " # any text Is ignored. In dialog mode the input line will be stored in", " the eventual readline history, nevertheless.", " -list_extras code", " Tell whether certain extra features were enabled at compile", " time. Code \"all\" lists all features and a headline. Other", " codes pick a single feature. \"codes\" lists the known codes.", " -list_arg_sorting", " Print the sorting order of xorriso commands with option -x.", " -version Tell program and version number", " -end End program. Commit eventual pending changes.", " -rollback_end", " End program. Discard pending changes.", "", "", "Command -page causes a user prompt after the given number of result lines.", "Empty input resumes output until the next prompt. Other input may be:", " @ suppresses paging until the current action is done", " @@ suppresses further result output but continues the action", " @@@ aborts the current action", " other aborts the current action and executes input as new command", "", #ifdef Xorriso_GNU_xorrisO "Report bugs to: bug-xorriso@gnu.org , or in private to: scdbackup@gmx.net .", "xorriso home page: <https://www.gnu.org/software/xorriso/>", "General help using GNU software: <https://www.gnu.org/gethelp/>", #else "Report any bugs to bug-xorriso@gnu.org or in private to scdbackup@gmx.net .", #endif "", #endif /* ! Xorriso_no_helP */ "@ENDE_OF_HELPTEXT_(HOPEFULLY_UNIQUELY_SILLY_TEXT)@" }; char *tpt= NULL; int i; Xorriso_restxt(xorriso,"\n"); sprintf(xorriso->result_line,"usage: %s [settings|actions]\n", xorriso->progname); Xorriso_result(xorriso,0); Xorriso_restxt(xorriso,"\n"); for(i=0;1;i++) { tpt= text[i]; if(strcmp(tpt,"@ENDE_OF_HELPTEXT_(HOPEFULLY_UNIQUELY_SILLY_TEXT)@")==0) break; sprintf(xorriso->result_line,"%s\n",tpt); Xorriso_result(xorriso,0); if(xorriso->request_to_abort) return(1); } Xorriso_restxt(xorriso,"\n"); return(1); } /* Option -hfsplus "on"|"off" */ int Xorriso_option_hfsplus(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "off")==0) xorriso->do_hfsplus= 0; else if(strcmp(mode, "on")==0) xorriso->do_hfsplus= 1; else { sprintf(xorriso->info_text, "-hfsplus: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -hide */ int Xorriso_option_hide(struct XorrisO *xorriso, char *hide_state, int argc, char **argv, int *idx, int flag) { int i, ret, end_idx, optc= 0, was_failure= 0, fret, hide_mode; char **optv= NULL; ret= Xorriso_opt_args(xorriso, "-hide", argc, argv, *idx, &end_idx, &optc, &optv, 0); if(ret<=0) goto ex; hide_mode= Xorriso__hide_mode(hide_state, 0); if(hide_mode < 0) { sprintf(xorriso->info_text, "-hide : unknown hide state "); Text_shellsafe(hide_state, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto ex; } for(i= 0; i<optc; i++) { ret= Xorriso_set_hidden(xorriso, NULL, optv[i], hide_mode, 0); if(ret>0 && !xorriso->request_to_abort) continue; /* regular bottom of loop */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; ret= 0; goto ex; } ret= 1; ex:; (*idx)= end_idx; Xorriso_opt_args(xorriso, "-hide", argc, argv, *idx, &end_idx, &optc, &optv, 256); if(ret<=0) return(ret); return(!was_failure); } /* Option -history */ int Xorriso_option_history(struct XorrisO *xorriso, char *line, int flag) { Xorriso_dialog_input(xorriso, line, strlen(line) + 1, 2 | 32); return(1); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of options as mentioned in man page or info file derived from xorriso.texi. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" /* Command -iso_nowtime "dynamic"|timespec */ int Xorriso_option_iso_nowtime(struct XorrisO *xorriso, char *text, int flag) { char *time_type = "m"; int t_type= 0, ret; time_t t; if(strcmp(text, "dynamic") == 0) { xorriso->do_override_now_time= 0; Xorriso_set_libisofs_now(xorriso, 2); Xorriso_msgs_submit(xorriso, 0, "-iso_nowtime: Set to \"dynamic\"", 0, "NOTE", 0); return(1); } ret= Xorriso_convert_datestring(xorriso, "-iso_nowtime", time_type, text, &t_type, &t, 0); if(ret<=0) goto ex; xorriso->do_override_now_time= 1; xorriso->now_time_override= t; Xorriso_set_libisofs_now(xorriso, 1); sprintf(xorriso->info_text, "-iso_nowtime: Set to =%.f", (double) t); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 1; ex:; return(ret); } /* Option -iso_rr_pattern "on"|"ls"|"off" */ int Xorriso_option_iso_rr_pattern(struct XorrisO *xorriso, char *mode,int flag) { if(strcmp(mode, "off")==0) xorriso->do_iso_rr_pattern= 0; else if(strcmp(mode, "on")==0) xorriso->do_iso_rr_pattern= 1; else if(strcmp(mode, "ls")==0) xorriso->do_iso_rr_pattern= 2; else { sprintf(xorriso->info_text, "-iso_rr_pattern: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -jigdo aspect argument */ int Xorriso_option_jigdo(struct XorrisO *xorriso, char *aspect, char *arg, int flag) { int ret; ret= Xorriso_jigdo_interpreter(xorriso, aspect, arg, 0); return(ret); } /* Option -joliet "on"|"off" */ int Xorriso_option_joliet(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "off")==0) xorriso->do_joliet= 0; else if(strcmp(mode, "on")==0) xorriso->do_joliet= 1; else { sprintf(xorriso->info_text, "-joliet: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Command -joliet_map */ int Xorriso_option_joliet_map(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "unmapped") == 0) { xorriso->joliet_map= 0; } else if(strcmp(mode, "stripped") == 0) { xorriso->joliet_map= 1; } else { sprintf(xorriso->info_text, "-joliet_map: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } return(1); } /* Command -launch_frontend */ int Xorriso_option_launch_frontend(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int ret, end_idx; end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, 1); if(xorriso->launch_frontend_banned) { sprintf(xorriso->info_text, "-launch_frontend was already executed in this xorriso run"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); ret= 0; goto ex; } xorriso->launch_frontend_banned= 1; if(end_idx <= *idx) {ret= 1; goto ex;} if(argv[*idx][0] == 0) {ret= 1; goto ex;} xorriso->dialog= 2; ret= Xorriso_launch_frontend(xorriso, end_idx - *idx, argv + *idx, "", "", 0); ex:; (*idx)= end_idx; return(ret); } /* Command -lfa_flags */ int Xorriso_option_lfa_flags(struct XorrisO *xorriso, char *mode, int flag) { int ret, lfa_flags_mem, l, sev, max_sev; char *npt, *cpt, *mask= NULL, severity[20]; uint64_t mask_flags; lfa_flags_mem= xorriso->lfa_flags_setting; npt= cpt= mode; for(; npt!=NULL; cpt= npt+1) { npt= strchr(cpt,':'); if(npt==NULL) l= strlen(cpt); else l= npt-cpt; if(l == 0) continue; if(l == 3 && strncmp(cpt, "off", l) == 0) { xorriso->lfa_flags_setting&= ~1; xorriso->lfa_flags_setting&= ~4; } else if((l == 2 && strncmp(cpt, "on", l) == 0)) { xorriso->lfa_flags_setting|= 1; xorriso->lfa_flags_setting&= ~4; } else if((l == 7 && strncmp(cpt, "auto_on", l) == 0)) { xorriso->lfa_flags_setting|= 4; if(xorriso->lfa_flags_default & 8) xorriso->lfa_flags_setting|= 1; else xorriso->lfa_flags_setting&= ~1; } else if((l == 7 && strncmp(cpt, "restore", l) == 0)) { xorriso->lfa_flags_setting|= (1 << 12); } else if((l == 10 && strncmp(cpt, "no_restore", l) == 0)) { xorriso->lfa_flags_setting&= ~(1 << 12); } else if((l == 4 && strncmp(cpt, "read", l) == 0)) { xorriso->lfa_flags_setting|= (1 << 11); } else if((l == 7 && strncmp(cpt, "no_read", l) == 0)) { xorriso->lfa_flags_setting&= ~(1 << 11); } else if(l == 10 && strncmp(cpt, "restore_su", l) == 0) { xorriso->lfa_flags_setting&= ~2; xorriso->lfa_flags_setting&= ~(1 << 13); } else if(l == 13 && strncmp(cpt, "no_restore_su", l) == 0) { xorriso->lfa_flags_setting&= ~2; xorriso->lfa_flags_setting|= 1 << 13; } else if(l == 15 && strncmp(cpt, "restore_su_auto", l) == 0) { xorriso->lfa_flags_setting|= 2; } else if(l == 18 && strncmp(cpt, "restore_only_known", l) == 0) { xorriso->lfa_flags_setting|= 1 << 14; } else if(l == 15 && strncmp(cpt, "restore_unknown", l) == 0) { xorriso->lfa_flags_setting&= ~(1 << 14); } else if(l == 19 && strncmp(cpt, "import_non_settable", l) == 0) { xorriso->lfa_flags_setting&= ~(1 << 15); } else if(l == 20 && strncmp(cpt, "import_only_settable", l) == 0) { xorriso->lfa_flags_setting|= 1 << 15; } else if(l >= 13 && strncmp(cpt, "restore_mask=", 13) == 0) { if(l == 13) { xorriso->lfa_restore_mask= ~((uint64_t) 0); } else { Xorriso_alloc_meM(mask, char, l - 13 + 1); strncpy(mask, cpt + 13, l - 13); mask[l - 13]= 0; ret= Xorriso_decode_lfa_flags(xorriso, mask, &mask_flags, 0); if(ret > 0) { xorriso->lfa_restore_mask= mask_flags; } else { sprintf(xorriso->info_text, "Cannot decode mask string of -lfa_flags restore_mask='%s'", mask); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } } } else if(l >= 14 && strncmp(cpt, "restore_error=", 14) == 0) { Xorriso__to_upper(cpt + 14, severity, (int) sizeof(severity), 0); if(strcmp(severity, "SILENT") != 0) { ret= Xorriso__text_to_sev(severity, &sev, 0); if(ret<=0) { sprintf(xorriso->info_text, "-lfa_flags restore_error=: Not a known severity name : "); Text_shellsafe(cpt + 14, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(ret); } Xorriso__text_to_sev("FATAL", &max_sev, 0); if(sev > max_sev) { sprintf(xorriso->info_text, "-lfa_flags restore_error=: Severity curbed to maximum value 'FATAL': "); Text_shellsafe(cpt + 14, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); strcpy(severity, "FATAL"); } } Xorriso__to_lower(severity, xorriso->lfa_restore_err_sev, sizeof(xorriso->lfa_restore_err_sev), 0); } else if(l >= 14 && strncmp(cpt, "restore_single", 14) == 0) { xorriso->lfa_flags_setting|= 1 << 16; } else if(l >= 14 && strncmp(cpt, "no_restore_single", 14) == 0) { xorriso->lfa_flags_setting&= ~(1 << 16); } else if(l == 7 && strncmp(cpt, "default", l) == 0) { xorriso->lfa_flags_setting= xorriso->lfa_flags_default; xorriso->lfa_restore_mask= ~((uint64_t) 0); } else { sprintf(xorriso->info_text, "-lfa_flags: unknown or mistyped mode in '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); xorriso->lfa_flags_setting= lfa_flags_mem; {ret= 0; goto ex;} } } if(xorriso->lfa_flags_setting & 2) { if(geteuid() == 0) xorriso->lfa_flags_setting&= ~(1 << 13); else xorriso->lfa_flags_setting|= 1 << 13; } xorriso->do_aaip&= ~(31 << 11); if(xorriso->lfa_flags_setting & 1) xorriso->do_aaip|= xorriso->lfa_flags_setting & (63 << 11); ret= Xorriso_set_ignore_aclea(xorriso, 0); if(ret <= 0) goto ex; ret= 1; ex:; Xorriso_free_meM(mask); return(ret); } /* Option -list_arg_sorting */ int Xorriso_option_list_arg_sorting(struct XorrisO *xorriso, int flag) { int ret; ret= Xorriso_cmd_sorting_rank(xorriso, 0, NULL, 0, 1); return(ret); } /* Option -list_delimiter */ int Xorriso_option_list_delimiter(struct XorrisO *xorriso, char *text, int flag) { int ret, argc; char **argv= NULL; if(text[0] == 0) { sprintf(xorriso->info_text, "-list_delimiter: New delimiter text is empty"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(strlen(text) > 80) { sprintf(xorriso->info_text, "-list_delimiter: New delimiter text is too long"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= Sfile_make_argv(xorriso->progname, text, &argc, &argv, 4); if(ret > 0) { if(argc > 2) { sprintf(xorriso->info_text, "-list_delimiter: New delimiter text contains more than one word"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } Sfile_make_argv(xorriso->progname, text, &argc, &argv, 2); if(argc > 2) return(0); } if(strchr(text, '"') != NULL || strchr(text, '\'') != NULL) { sprintf(xorriso->info_text, "-list_delimiter: New delimiter text contains quotation marks"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } strcpy(xorriso->list_delimiter, text); return(1); } /* Option -list_extras */ int Xorriso_option_list_extras(struct XorrisO *xorriso, char *mode, int flag) { int ret; ret= Xorriso_list_extras(xorriso, mode, 0); return(ret); } /* Option -list_formats */ int Xorriso_option_list_formats(struct XorrisO *xorriso, int flag) { int ret; ret= Xorriso_list_formats(xorriso, 0); return(ret); } /* Option -list_speeds */ int Xorriso_option_list_speeds(struct XorrisO *xorriso, int flag) { int ret; ret= Xorriso_list_speeds(xorriso, 0); return(ret); } /* Option -list_profiles */ int Xorriso_option_list_profiles(struct XorrisO *xorriso, char *which, int flag) { int ret; int mode= 0; if(strncmp(which,"in",2)==0) mode|= 1; else if(strncmp(which,"out",3)==0) mode|= 2; else mode|= 3; if(mode & 1) { ret= Xorriso_toc(xorriso, 1 | 16 | 32); if(ret > 0) Xorriso_list_profiles(xorriso, 0); } if((mode & 2) && xorriso->in_drive_handle != xorriso->out_drive_handle) { ret= Xorriso_toc(xorriso, 1 | 2 | 16 | 32); if(ret > 0) Xorriso_list_profiles(xorriso, 2); } return(1); } /* Command -lns alias -lnsi */ int Xorriso_option_lnsi(struct XorrisO *xorriso, char *target, char *path, int flag) { int ret; char *eff_path= NULL, *buffer= NULL, *namept; Xorriso_alloc_meM(eff_path, char, SfileadrL); Xorriso_alloc_meM(buffer, char, SfileadrL); ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, path, eff_path, 1); if(ret < 0) {ret= 0; goto ex;} if(ret > 0) { sprintf(xorriso->info_text, "-lns: Address already existing: "); Text_shellsafe(eff_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, path, eff_path, 2); if(ret < 0) {ret= 0; goto ex;} ret= Xorriso_truncate_path_comps(xorriso, target, buffer, &namept, 0); if(ret < 0) {ret= 0; goto ex;} ret= Xorriso_graft_in(xorriso, NULL, namept, eff_path, (off_t) 0, (off_t) 0, 1024); if(ret <= 0) {ret= 0; goto ex;} ret= 1; ex:; Xorriso_free_meM(buffer); Xorriso_free_meM(eff_path); return(ret); } /* Option -load session|track|sbsector value */ /* @param flag bit0= with adr_mode sbsector: adr_value is possibly 16 too high @return <=0 error , 1 success, 2 revoked by -reassure */ int Xorriso_option_load(struct XorrisO *xorriso, char *adr_mode, char *adr_value, int flag) { int ret; if(Xorriso_change_is_pending(xorriso, 0)) { sprintf(xorriso->info_text, "-load: Image changes pending. -commit or -rollback first"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= Xorriso_reassure(xorriso, "-load", "loads an alternative image", 0); if(ret<=0) return(2); ret= Xorriso_decode_load_adr(xorriso, "-load", adr_mode, adr_value, &(xorriso->image_start_mode), xorriso->image_start_value, flag & 1); if(ret <= 0) return(ret); xorriso->image_start_mode|= (1<<30); /* enable non-default msc1 processing */ if(strlen(xorriso->indev)>0) { ret= Xorriso_option_rollback(xorriso, 1); /* Load image, no -reassure */ if(ret<=0) return(ret); } return(1); } /* Option -logfile */ int Xorriso_option_logfile(struct XorrisO *xorriso, char *channel, char *fileadr, int flag) { int hflag,channel_no= 0, ret; if(channel[0]==0) { logfile_wrong_form:; sprintf(xorriso->info_text,"Wrong form. Correct would be: -logfile \".\"|\"R\"|\"I\"|\"M\" file_address"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } hflag= 2; if(channel[0]=='R') channel_no= 1; else if(channel[0]=='I') channel_no= 2; else if(channel[0]=='M') channel_no= 3; else if(channel[0]=='.') hflag= 4; else goto logfile_wrong_form; if(strcmp(fileadr,"-")==0 || fileadr[0]==0) hflag|= (1<<15); xorriso->logfile[channel_no][0]= 0; ret= Xorriso_write_to_channel(xorriso, fileadr, channel_no, hflag); if(ret<=0) { sprintf(xorriso->info_text, "Cannot open logfile: %s", fileadr); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } else if(!(hflag&(1<<15))) if(Sfile_str(xorriso->logfile[channel_no], fileadr, 0)<=0) return(-1); return(ret>0); } /* Options -ls alias -lsi and -lsl alias -lsli and -lsd alias -lsdi and -lsdl alias -lsdli and -du alias -dui and -dus alias -dusi and -lsattr alias -lsattri and -lsattrd alias -lsattrdi @param flag bit0= long format (-lsl , -du, not -dus, not -ls) bit1= do not expand patterns but use literally bit2= -du rather than -ls bit3= list directories as themselves (-lsd) bit4= -lsattr rather than -ls */ int Xorriso_option_lsi(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int ret, end_idx, filec= 0, nump, i, star= 1; char **filev= NULL, **patterns= NULL; off_t mem= 0; struct stat stbuf; if(flag & 4) { if(!(flag & 1)) star= 0; } else { if(flag & 8) star= 0; } end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, 1); if(xorriso->do_iso_rr_pattern==0) flag|= 2; nump= end_idx - *idx; if((flag&2) && nump>0 ) { ; } else if(nump <= 0) { if(Xorriso_iso_lstat(xorriso, xorriso->wdi, &stbuf, 0)<0) { sprintf(xorriso->info_text, "Current -cd path does not yet exist in the ISO image"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); {ret= 0; goto ex;} } if(!S_ISDIR(stbuf.st_mode)) { sprintf(xorriso->info_text, "Current -cd meanwhile points to a non-directory in ISO image"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); {ret= 0; goto ex;} } patterns= calloc(1, sizeof(char *)); if(patterns == NULL) { no_memory:; sprintf(xorriso->info_text, "Cannot allocate enough memory for pattern expansion"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } nump= 1; if(star) patterns[0]= "*"; else patterns[0]= "."; flag&= ~2; } else { patterns= calloc(nump, sizeof(char *)); if(patterns==NULL) goto no_memory; for(i= 0; i<nump; i++) { if(argv[i + *idx][0]==0) { if(star) patterns[i]= "*"; else patterns[i]= "."; } else patterns[i]= argv[i + *idx]; } } if((flag & 1) && !(xorriso->ino_behavior & 32)) { ret= Xorriso_make_hln_array(xorriso, 0); /* for stbuf.st_nlink */ if(ret < 0) goto ex; } if(flag&2) { ret= Xorriso_ls_filev(xorriso, xorriso->wdi, nump, argv + (*idx), mem, flag & (1 | 4 | 8 | 16)); } else if(nump==1 && strcmp(patterns[0],"*")==0 && !(flag&4)){ /* save temporary memory by calling simpler function */ ret= Xorriso_ls(xorriso, (flag&1)|4); } else { ret= Xorriso_expand_pattern(xorriso, nump, patterns, 0, &filec, &filev, &mem, 0); if(ret<=0) {ret= 0; goto ex;} ret= Xorriso_ls_filev(xorriso, xorriso->wdi, filec, filev, mem, flag & (1 | 4 | 8 | 16)); } if(ret<=0) {ret= 0; goto ex;} ret= 1; ex:; if(patterns!=NULL) free((char *) patterns); Sfile_destroy_argv(&filec, &filev, 0); (*idx)= end_idx; return(ret); } /* Options -lsx, -lslx, -lsdx , -lsdlx , -dux , -dusx @param flag bit0= long format (-lslx , -dux) bit1= do not expand patterns but use literally bit2= du rather than ls bit3= list directories as themselves (ls -d) */ int Xorriso_option_lsx(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int ret, end_idx, filec= 0, nump, i; char **filev= NULL, **patterns= NULL; off_t mem= 0; end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, 1|2); if(xorriso->do_disk_pattern==0) flag|= 2; nump= end_idx - *idx; if((flag&2) && nump>0) { ; } else if(nump <= 0) { patterns= calloc(1, sizeof(char *)); if(patterns == NULL) { no_memory:; sprintf(xorriso->info_text, "Cannot allocate enough memory for pattern expansion"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } nump= 1; if(flag&8) patterns[0]= "."; else patterns[0]= "*"; flag&= ~2; } else { patterns= calloc(nump, sizeof(char *)); if(patterns==NULL) goto no_memory; for(i= 0; i<nump; i++) { if(argv[i + *idx][0]==0) patterns[i]= "*"; else patterns[i]= argv[i + *idx]; } } if(flag&2) { ret= Xorriso_lsx_filev(xorriso, xorriso->wdx, nump, argv + (*idx), mem, flag&(1|4|8)); #ifdef Not_yeT } else if(nump==1 && strcmp(patterns[0],"*")==0 && !(flag&4)){ /* save temporary memory by calling simpler function */ ret= Xorriso_ls(xorriso, (flag&1)|4); #endif } else { ret= Xorriso_expand_disk_pattern(xorriso, nump, patterns, 0, &filec, &filev, &mem, 0); if(ret<=0) {ret= 0; goto ex;} ret= Xorriso_lsx_filev(xorriso, xorriso->wdx, filec, filev, mem, flag&(1|4|8)); } if(ret<=0) {ret= 0; goto ex;} ret= 1; ex:; if(patterns!=NULL) free((char *) patterns); Sfile_destroy_argv(&filec, &filev, 0); (*idx)= end_idx; return(ret); } /* Option -map , -map_single */ /* @param flag bit0=do not report the added item bit1=do not reset pacifier, no final pacifier message bit5= -map_single: do not insert directory tree */ int Xorriso_option_map(struct XorrisO *xorriso, char *disk_path, char *iso_path, int flag) { int ret; char *eff_origin= NULL, *eff_dest= NULL, *ipth; Xorriso_alloc_meM(eff_origin, char, SfileadrL); Xorriso_alloc_meM(eff_dest, char, SfileadrL); if(!(flag&2)) Xorriso_pacifier_reset(xorriso, 0); ipth= iso_path; if(ipth[0]==0) ipth= disk_path; if(disk_path[0]==0) { sprintf(xorriso->info_text, "-map: Empty disk_path given"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 1); {ret= 0; goto ex;} } ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, disk_path, eff_origin, 2|4); if(ret<=0) goto ex; ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, ipth, eff_dest, 2); if(ret<=0) goto ex; ret= Xorriso_graft_in(xorriso, NULL, eff_origin, eff_dest, (off_t) 0, (off_t) 0, 2|(flag&32)); if(!(flag&2)) Xorriso_pacifier_callback(xorriso, "files added", xorriso->pacifier_count, xorriso->pacifier_total, "", 1); if(ret<=0) goto ex; if(!(flag&1)) { sprintf(xorriso->info_text, "Added to ISO image: %s '%s'='%s'\n", (ret>1 ? "directory" : "file"), (eff_dest[0] ? eff_dest : "/"), eff_origin); Xorriso_info(xorriso,0); } ret= 1; ex:; Xorriso_free_meM(eff_origin); Xorriso_free_meM(eff_dest); return(ret); } /* Command -map_l , -compare_l , -update_l , -extract_l , -update_lxi , -update_li */ /* @param flag bit4= do not establish and dispose xorriso->di_array for update_l bit8-11= mode 0= -map_l 1= -compare_l 2= -update_l 3= -extract_l 4= -update_lxi 5= -update_li */ int Xorriso_option_map_l(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int ret, end_idx, optc= 0, was_failure= 1, i, j, fret, mode, problem_count; int ns_flag= 2|4, nt_flag= 2, opt_args_flag= 2, arg2c= 0, opt2c= 0; int new_opt2c; char *source_prefix= NULL, *target_prefix= NULL, *cmd, **optv= NULL; char *eff_source= NULL, *eff_target= NULL, *s_wd, *t_wd; char **eff_src_array= NULL, **eff_tgt_array= NULL, **opt2v= NULL; char **arg2v= NULL; cmd= "-map_l"; s_wd= xorriso->wdx; t_wd= xorriso->wdi; Xorriso_pacifier_reset(xorriso, 0); mode= (flag>>8) & 15; if(mode==1) cmd= "-compare_l"; else if(mode==2) cmd= "-update_l"; else if(mode == 3 || mode == 5) { if(mode == 5) cmd= "-update_li"; else cmd= "-extract_l"; ns_flag= 2; s_wd= xorriso->wdi; nt_flag= 2|4; t_wd= xorriso->wdx; opt_args_flag= 0; } else if(mode == 4) { cmd= "-update_lxi"; } end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, 1|2); if(end_idx - (*idx) < 3) { sprintf(xorriso->info_text, "%s: Not enough arguments given (%d < 3)", cmd, end_idx - (*idx)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 1); ret= 0; goto ex; } Xorriso_alloc_meM(source_prefix, char, SfileadrL); Xorriso_alloc_meM(target_prefix, char, SfileadrL); Xorriso_alloc_meM(eff_source, char, SfileadrL); Xorriso_alloc_meM(eff_target, char, SfileadrL); ret= Xorriso_normalize_img_path(xorriso, s_wd, argv[*idx], source_prefix, ns_flag | 64); if(ret<=0) goto ex; ret= Xorriso_normalize_img_path(xorriso, t_wd, argv[(*idx)+1], target_prefix, nt_flag); if(ret<=0) goto ex; ret= Xorriso_opt_args(xorriso, cmd, argc, argv, (*idx)+2, &end_idx, &optc, &optv, opt_args_flag); if(ret<=0) goto ex; if(mode == 4) { /* Convert pattern from disk to iso_rr */ arg2c= end_idx - *idx - 2; Xorriso_alloc_meM(arg2v, char *, arg2c); for(i = 0; i < arg2c; i++) arg2v[i]= NULL; arg2c= 0; for(i = (*idx) + 2; i < end_idx; i++) { ret= Xorriso_normalize_img_path(xorriso, s_wd, argv[i], eff_source, ns_flag); if(ret<=0) goto ex; ret= Xorriso__exchange_prefix(source_prefix, target_prefix, eff_source, eff_target, 0); if(ret <= 0) continue; Xorriso_alloc_meM(arg2v[arg2c], char, strlen(eff_target) + 1); strcpy(arg2v[arg2c], eff_target); arg2c++; } /* Expand wildcards in iso_rr, do not include unmatched patterns */ ret= Xorriso_opt_args(xorriso, cmd, arg2c, arg2v, 0, &i, &opt2c, &opt2v, (1 << 10) | (1 << 7)); if(ret<=0) goto ex; /* Convert from iso_rr path to disk path */ new_opt2c= 0; for(i = 0; i < opt2c; i++) { ret= Xorriso__exchange_prefix(target_prefix, source_prefix, opt2v[i], eff_source, 0); free(opt2v[i]); opt2v[i]= NULL; if(ret <= 0) continue; Xorriso_alloc_meM(opt2v[new_opt2c], char, strlen(eff_source) + 1); strcpy(opt2v[new_opt2c], eff_source); new_opt2c++; } opt2c= new_opt2c; /* Merge both results */ if(opt2c > 0) { Sfile_destroy_argv(&arg2c, &arg2v, 0); Xorriso_alloc_meM(arg2v, char *, optc + opt2c); for(i = 0; i < optc + opt2c; i++) arg2v[i]= NULL; arg2c= 0; for(i= 0; i < optc; i++) { ret= Xorriso_normalize_img_path(xorriso, s_wd, optv[i], eff_source, ns_flag); if(ret<=0) goto ex; Xorriso_alloc_meM(arg2v[arg2c], char, strlen(eff_source) + 1); strcpy(arg2v[arg2c], eff_source); arg2c++; } for(i= 0; i < opt2c; i++) { for(j= 0; j < optc; j++) if(strcmp(opt2v[i], arg2v[j]) == 0) break; if(j < optc) continue; arg2v[arg2c++]= opt2v[i]; opt2v[i]= NULL; } Sfile_destroy_argv(&optc, &optv, 0); optv= arg2v; arg2v= NULL; optc= arg2c; arg2c= 0; } } if(mode == 3 && (xorriso->do_restore_sort_lba || !(xorriso->ino_behavior & 4))) { eff_src_array= calloc(optc, sizeof(char *)); eff_tgt_array= calloc(optc, sizeof(char *)); if(eff_src_array == NULL || eff_tgt_array == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } for(i= 0; i < optc; i++) eff_src_array[i]= eff_tgt_array[i]= NULL; } if((mode == 2 || mode == 4) && !((xorriso->ino_behavior & 2) || (flag & 16) || xorriso->di_array != NULL)) { /* Create all-image node array sorted by isofs.di */ ret= Xorriso_make_di_array(xorriso, 0); if(ret <= 0) goto ex; } for(i= 0; i<optc; i++) { ret= Xorriso_normalize_img_path(xorriso, s_wd, optv[i], eff_source, ns_flag); if(ret<=0) goto ex; ret= Xorriso__exchange_prefix(source_prefix, target_prefix, eff_source, eff_target, 0); if(ret == 0) { sprintf(xorriso->info_text, "%s: disk_path ", cmd); Text_shellsafe(eff_source, xorriso->info_text, 1); strcat(xorriso->info_text, " does not begin with disk_prefix "); Text_shellsafe(source_prefix, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 1); } if(ret <= 0) goto ex; if(mode==0) ret= Xorriso_option_map(xorriso, eff_source, eff_target, 2); else if(mode==1) ret= Xorriso_option_compare(xorriso, eff_source, eff_target, 2|8); else if(mode == 2 || mode == 4) ret= Xorriso_option_update(xorriso, eff_source, eff_target, 2 | 8 | 16); else if(mode==3) { if(eff_src_array != NULL) { eff_src_array[i]= strdup(eff_source); eff_tgt_array[i]= strdup(eff_target); if(eff_src_array[i] == NULL || eff_tgt_array[i] == NULL) { Xorriso_no_malloc_memory(xorriso, &(eff_src_array[i]), 0); ret= -1; goto ex; } } else { ret= Xorriso_option_extract(xorriso, eff_source, eff_target, 2 | 4); } } else if(mode == 5) { ret= Xorriso_option_update(xorriso, eff_target, eff_source, 2 | 8 | 16); } if(ret>0 && !xorriso->request_to_abort) continue; /* regular bottom of loop */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1 | 2); if(fret>=0) continue; goto ex; } ret= 1; if(mode == 3 && eff_src_array != NULL) { ret= Xorriso_lst_append_binary(&(xorriso->node_disk_prefixes), target_prefix, strlen(target_prefix) + 1, 0); if(ret <= 0) goto ex; ret= Xorriso_lst_append_binary(&(xorriso->node_img_prefixes), source_prefix, strlen(source_prefix) + 1, 0); if(ret <= 0) goto ex; ret= Xorriso_restore_sorted(xorriso, optc, eff_src_array, eff_tgt_array, &problem_count, 0); if(ret <= 0 || problem_count > 0) was_failure= 1; } if(mode==0) Xorriso_pacifier_callback(xorriso, "files added", xorriso->pacifier_count, xorriso->pacifier_total, "", 1); else if(mode==1 || mode==2 || mode == 4 || mode == 5) Xorriso_pacifier_callback(xorriso, "content bytes read", xorriso->pacifier_count, 0, "", 1 | 8 | 32); else if(mode==3) Xorriso_pacifier_callback(xorriso, "files restored",xorriso->pacifier_count, xorriso->pacifier_total, "", 1|4); ex:; Xorriso_destroy_node_array(xorriso, 0); i= optc; Sfile_destroy_argv(&i, &eff_src_array, 0); i= optc; Sfile_destroy_argv(&i, &eff_tgt_array, 0); Xorriso_free_meM(source_prefix); Xorriso_free_meM(target_prefix); Xorriso_free_meM(eff_source); Xorriso_free_meM(eff_target); (*idx)= end_idx; Xorriso_opt_args(xorriso, cmd, argc, argv, *idx, &end_idx, &optc, &optv, 256); Xorriso_opt_args(xorriso, cmd, argc, argv, *idx, &end_idx, &opt2c, &opt2v, 256); if(arg2c > 0) Sfile_destroy_argv(&arg2c, &arg2v, 0); else if(arg2v != NULL) Xorriso_free_meM(arg2v); if(ret<=0) return(ret); return(!was_failure); } /* Option -mark */ int Xorriso_option_mark(struct XorrisO *xorriso, char *mark, int flag) { if(mark[0]==0) xorriso->mark_text[0]= 0; else strncpy(xorriso->mark_text,mark,sizeof(xorriso->mark_text)-1); xorriso->mark_text[sizeof(xorriso->mark_text)-1]= 0; return(1); } /* Option -md5 "on"|"all"|"off" */ int Xorriso_option_md5(struct XorrisO *xorriso, char *mode, int flag) { char *npt, *cpt; int l; npt= cpt= mode; for(; npt!=NULL; cpt= npt+1) { npt= strchr(cpt,':'); if(npt==NULL) l= strlen(cpt); else l= npt-cpt; if(l == 0) continue; if(l == 3 && strncmp(cpt, "off", l) == 0) xorriso->do_md5&= ~31; else if(l == 2 && strncmp(cpt, "on", l) == 0) xorriso->do_md5= (xorriso->do_md5 & ~31) | 7 | 16; else if(l == 3 && strncmp(cpt, "all", l) == 0) xorriso->do_md5|= 31; else if(l == 18 && strncmp(cpt, "stability_check_on", l) == 0) xorriso->do_md5|= 8; else if(l == 19 && strncmp(cpt, "stability_check_off", l) == 0) xorriso->do_md5&= ~8; else if(l == 13 && strncmp(cpt, "load_check_on", l) == 0) xorriso->do_md5&= ~32; else if(l == 14 && strncmp(cpt, "load_check_off", l) == 0) xorriso->do_md5|= 32; else { sprintf(xorriso->info_text, "-md5: unknown mode "); Text_shellsafe(cpt, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } return(1); } /* Option -mkdir alias -mkdiri */ int Xorriso_option_mkdiri(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int i, end_idx, ret, was_failure= 0, fret; end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, 0); for(i= *idx; i<end_idx; i++) { ret= Xorriso_mkdir(xorriso, argv[i], 0); if(ret>0 && !xorriso->request_to_abort) continue; /* regular bottom of loop */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; goto ex; } ret= 1; ex:; (*idx)= end_idx; if(ret<=0) return(ret); return(!was_failure); } int Xorriso_option_modesty_on_drive(struct XorrisO *xorriso, char *mode, int flag) { char *npt, *cpt, *ppt; int l, num, set_min; npt= cpt= mode; for(; npt!=NULL; cpt= npt+1) { npt= strchr(cpt,':'); if(npt==NULL) l= strlen(cpt); else l= npt-cpt; if(l == 0) continue; if(l == 3 && strncmp(cpt, "off", l) == 0) { xorriso->modesty_on_drive= 0; } else if(l == 1 && strncmp(cpt, "0", l) == 0) { xorriso->modesty_on_drive= 0; } else if(l == 2 && strncmp(cpt, "on", l) == 0) { xorriso->modesty_on_drive= 1; } else if(l == 1 && strncmp(cpt, "1", l) == 0) { xorriso->modesty_on_drive= 1; } else if(l == 2 && strncmp(cpt, "-1", l) == 0) { ; } else if(*cpt >= '1' && *cpt <= '9') { ppt= cpt; set_min= 2; set_size_percent:; sscanf(ppt, "%d", &num); if(num == -1) { ; } else if(num < 25) { bad_percent:; sprintf(xorriso->info_text, "-modesty_on_drive: percentage out of range [25 to 100]"); Text_shellsafe(cpt, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } else if(num > 100) { goto bad_percent; } if(set_min == 2) { xorriso->modesty_on_drive= 1; } if(set_min) xorriso->min_buffer_percent= num; else xorriso->max_buffer_percent= num; } else if(l >= 12 && strncmp(cpt, "min_percent=", 12) == 0) { ppt= cpt + 12; set_min= 1; goto set_size_percent; } else if(l >= 12 && strncmp(cpt, "max_percent=", 12) == 0) { ppt= cpt + 12; set_min= 0; goto set_size_percent; } else if(l >= 9 && strncmp(cpt, "min_usec=", 9) == 0) { ppt= cpt + 9; set_min= 1; set_sec:; num= -1; sscanf(ppt, "%d", &num); if(num < 0) num= 0; if(set_min == 2) xorriso->max_buffer_usec= num; else if(set_min == 1) xorriso->min_buffer_usec= num; else if(set_min == 0) xorriso->max_buffer_percent= num; else xorriso->buffer_timeout_sec= num; } else if(l >= 9 && strncmp(cpt, "max_usec=", 9) == 0) { ppt= cpt + 9; set_min= 2; goto set_sec; } else if(l >= 12 && strncmp(cpt, "timeout_sec=", 12) == 0) { ppt= cpt + 12; set_min= -1; goto set_sec; } else { sprintf(xorriso->info_text, "-modesty_on_drive: unknown mode "); Text_shellsafe(cpt, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } return(1); } /* Options -mount , -mount_cmd , -session_string */ /* @param bit0= -mount_cmd: print mount command to result channel rather than performing it bit1= perform -session_string rather than -mount_cmd */ int Xorriso_option_mount(struct XorrisO *xorriso, char *dev, char *adr_mode, char *adr, char *cmd, int flag) { int ret, entity_code= 0, m_flag; char entity_id[81], *mnt; if(flag & 1) mnt= "-mount_cmd"; else if(flag & 2) mnt= "-session_string"; else { mnt= "-mount"; if(xorriso->allow_restore <= 0) { sprintf(xorriso->info_text, "-mount: image-to-disk features are not enabled by option -osirrox"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(Xorriso_change_is_pending(xorriso, 0)) { sprintf(xorriso->info_text, "%s: Image changes pending. -commit or -rollback first", mnt); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } ret= Xorriso_decode_load_adr(xorriso, mnt, adr_mode, adr, &entity_code, entity_id, 0); if(ret <= 0) return(ret); if(flag & 2) m_flag= 1 | 4; else m_flag= (flag & 1) | 2; ret= Xorriso_mount(xorriso, dev, entity_code, entity_id, cmd, m_flag); return(ret); } /* Option -mount_opts option[:...] */ int Xorriso_option_mount_opts(struct XorrisO *xorriso, char *mode, int flag) { int was, l; char *cpt, *npt; was= xorriso->mount_opts_flag; npt= cpt= mode; for(cpt= mode; npt!=NULL; cpt= npt+1) { npt= strchr(cpt,':'); if(npt==NULL) l= strlen(cpt); else l= npt-cpt; if(l==0) goto unknown_mode; if(strncmp(cpt, "shared", l)==0) { xorriso->mount_opts_flag|= 1; } else if(strncmp(cpt, "exclusive", l)==0) { xorriso->mount_opts_flag&= ~1; } else { unknown_mode:; if(l<SfileadrL) sprintf(xorriso->info_text, "-mount_opts: unknown option '%s'", cpt); else sprintf(xorriso->info_text, "-mount_opts: oversized parameter (%d)",l); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); xorriso->mount_opts_flag= was; return(0); } } return(1); } /* Command -move */ int Xorriso_option_move(struct XorrisO *xorriso, char *origin, char *dest, int flag) { int ret; char *eff_origin= NULL, *eff_dest= NULL; Xorriso_alloc_meM(eff_origin, char, SfileadrL); Xorriso_alloc_meM(eff_dest, char, SfileadrL); ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, origin, eff_origin, 0); if(ret <= 0) {ret= 0; goto ex;} ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, dest, eff_dest, 2); if(ret < 0) {ret= 0; goto ex;} ret= Xorriso_rename(xorriso, NULL, eff_origin, eff_dest, 0); if(ret <= 0) goto ex; ret= 1; ex:; Xorriso_free_meM(eff_origin); Xorriso_free_meM(eff_dest); return(ret); } /* Command -msg_op */ int Xorriso_option_msg_op(struct XorrisO *xorriso, char *what, char *arg, int flag) { int ret, available, argc, pargc, i, pflag, max_words, input_lines, msd_mem; char **argv= NULL, **pargv= NULL, *msg= ""; char *prefix, *separators; msd_mem= xorriso->msg_sieve_disabled; ret= 1; if(strcmp(what, "parse") == 0 || strcmp(what, "parse_silently") == 0 || strcmp(what, "parse_bulk") == 0 || strcmp(what, "parse_bulk_silently") == 0) { ret= Xorriso_parse_line(xorriso, arg, "", "", 5, &argc, &argv, 0); prefix= ""; if(argc > 0) prefix= argv[0]; separators= ""; if(argc > 1) separators= argv[1]; max_words= 0; if(argc > 2) sscanf(argv[2], "%d", &max_words); pflag= 0; if(argc > 3) sscanf(argv[3], "%d", &pflag); input_lines= 1; if(argc > 4) sscanf(argv[4], "%d", &input_lines); if(strcmp(what, "parse") == 0 || strcmp(what, "parse_silently") == 0) { ret= Xorriso_msg_op_parse(xorriso, "", prefix, separators, max_words, pflag, input_lines, (strcmp(what, "parse_silently") == 0)); } else { ret= Xorriso_msg_op_parse_bulk(xorriso, prefix, separators, max_words, pflag, input_lines, (strcmp(what, "parse_bulk_silently") == 0)); } if(ret <= 0) goto ex; xorriso->msg_sieve_disabled= msd_mem; Xorriso__dispose_words(&argc, &argv); } else if(strcmp(what, "start_sieve") == 0) { Xorriso_sieve_dispose(xorriso, 0); ret= Xorriso_sieve_big(xorriso, 0); msg= "Message sieve enabled"; /* >>> } else if(strcmp(what, "add_filter_rule") == 0) { */; } else if(strcmp(what, "clear_sieve") == 0) { ret= Xorriso_sieve_clear_results(xorriso, 0); msg= "Recorded message sieve results disposed"; } else if(strcmp(what, "end_sieve") == 0) { ret= Xorriso_sieve_dispose(xorriso, 0); msg= "Message sieve disabled"; } else if(strcmp(what, "read_sieve") == 0) { ret= Xorriso_sieve_get_result(xorriso, arg, &pargc, &pargv, &available, 0); xorriso->msg_sieve_disabled= 1; sprintf(xorriso->result_line, "%d\n", ret); Xorriso_result(xorriso, 1); if(ret > 0) { sprintf(xorriso->result_line, "%d\n", pargc); Xorriso_result(xorriso, 1); for(i= 0; i < pargc; i++) { ret= Sfile_count_char(pargv[i], '\n') + 1; sprintf(xorriso->result_line, "%d\n", ret); Xorriso_result(xorriso, 1); Sfile_str(xorriso->result_line, pargv[i], 0); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 1); } } else { strcpy(xorriso->result_line, "0\n"); Xorriso_result(xorriso, 1); available= 0; } sprintf(xorriso->result_line, "%d\n", available); Xorriso_result(xorriso, 1); xorriso->msg_sieve_disabled= msd_mem; Xorriso__dispose_words(&pargc, &pargv); ret= 1; } else if(strcmp(what, "show_sieve") == 0) { ret= Xorriso_sieve_get_result(xorriso, "", &pargc, &pargv, &available, 8); xorriso->msg_sieve_disabled= 1; sprintf(xorriso->result_line, "%d\n", ret); Xorriso_result(xorriso, 1); if(ret > 0) { sprintf(xorriso->result_line, "%d\n", pargc); Xorriso_result(xorriso, 1); for(i= 0; i < pargc; i++) { sprintf(xorriso->result_line, "%s\n", pargv[i]); Xorriso_result(xorriso, 1); } } xorriso->msg_sieve_disabled= msd_mem; Xorriso__dispose_words(&pargc, &pargv); } else if(strcmp(what, "compare_sev") == 0) { ret= Xorriso_parse_line(xorriso, arg, "", ",", 2, &argc, &argv, 0); if(argc < 2) { sprintf(xorriso->info_text, "-msg_op cmp_sev: malformed severity pair '%s'", arg); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } else { ret= Xorriso__severity_cmp(argv[0], argv[1]); sprintf(xorriso->result_line, "%d\n", ret); Xorriso_result(xorriso, 1); } Xorriso__dispose_words(&argc, &argv); } else if(strcmp(what, "list_sev") == 0) { sprintf(xorriso->result_line, "%s\n", Xorriso__severity_list(0)); Xorriso_result(xorriso, 1); } else { sprintf(xorriso->info_text, "-msg_op: unknown operation '%s'", what); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); ret= 0; } if(ret > 0 && msg[0]) Xorriso_msgs_submit(xorriso, 0, msg, 0, "NOTE", 0); ex:; xorriso->msg_sieve_disabled= msd_mem; return(ret); } /* Option -mv alias -mvi */ int Xorriso_option_mvi(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int i, end_idx_dummy, ret, is_dir= 0, was_failure= 0, fret; char *eff_origin= NULL, *eff_dest= NULL, *dest_dir= NULL; char *leafname= NULL; int optc= 0; char **optv= NULL; Xorriso_alloc_meM(eff_origin, char, SfileadrL); Xorriso_alloc_meM(eff_dest, char, SfileadrL); Xorriso_alloc_meM(dest_dir, char, SfileadrL); Xorriso_alloc_meM(leafname, char, SfileadrL); ret= Xorriso_cpmv_args(xorriso, "-mvi", argc, argv, idx, &optc, &optv, eff_dest, 0); if(ret<=0) goto ex; if(ret==2) { is_dir= 1; strcpy(dest_dir, eff_dest); } /* Perform movements */ for(i= 0; i<optc; i++) { ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi,optv[i],eff_origin,0); if(ret<=0 || xorriso->request_to_abort) goto problem_handler; if(is_dir) { ret= Sfile_leafname(eff_origin, leafname, 0); if(ret<=0) goto problem_handler; strcpy(eff_dest, dest_dir); ret= Sfile_add_to_path(eff_dest, leafname, 0); if(ret<=0) { sprintf(xorriso->info_text, "Effective path gets much too long (%d)", (int) (strlen(eff_dest)+strlen(leafname)+1)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto problem_handler; } } ret= Xorriso_rename(xorriso, NULL, eff_origin, eff_dest, 0); if(ret<=0 || xorriso->request_to_abort) goto problem_handler; sprintf(xorriso->info_text, "Renamed in ISO image: "); Text_shellsafe(eff_origin, xorriso->info_text, 1); strcat(xorriso->info_text, " to "); Text_shellsafe(eff_dest, xorriso->info_text, 1 | 2); strcat(xorriso->info_text, "\n"); Xorriso_info(xorriso, 0); continue; /* regular bottom of loop */ problem_handler:; was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; goto ex; } ret= !was_failure; ex:; Xorriso_free_meM(eff_origin); Xorriso_free_meM(eff_dest); Xorriso_free_meM(dest_dir); Xorriso_free_meM(leafname); Xorriso_opt_args(xorriso, "-mvi", argc, argv, *idx, &end_idx_dummy, &optc, &optv, 256); return(ret); } /* Option -named_pipe_loop */ int Xorriso_option_named_pipe_loop(struct XorrisO *xorriso, char *mode, char *stdin_pipe, char *stdout_pipe, char *stderr_pipe, int flag) { char *pipe_paths[3], *cpt, *npt; int ret, hflag= 0, l; npt= mode; for(cpt= mode; npt != NULL; cpt= npt + 1) { npt= strchr(cpt, ':'); if(npt==NULL) l= strlen(cpt); else l= npt-cpt; if(l==0) { ; } else if(strncmp(cpt, "-", l) == 0) { ; } else if(strncmp(cpt, "cleanup", l) == 0) { hflag|= 1; } else if(strncmp(cpt, "keep", l) == 0) { hflag&= ~1; } else if(strncmp(cpt, "buffered", l) == 0) { hflag|= 2; } else if(strncmp(cpt, "direct", l) == 0) { hflag&= ~2; } else { sprintf(xorriso->info_text, "-named_pipe_loop: unknown mode in '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } pipe_paths[0]= stdin_pipe; pipe_paths[1]= stdout_pipe; pipe_paths[2]= stderr_pipe; ret= Xorriso_named_pipe_loop(xorriso, pipe_paths, hflag); return(ret); } /* Option -no_rc */ int Xorriso_option_no_rc(struct XorrisO *xorriso, int flag) { xorriso->no_rc= 1; return(1); } /* Option -not_leaf , (-hide_disk_leaf , -as mkisofs -hide) */ /* @param flag bit0-bit5= hide rather than adding to disk_exclusions bit0= add to iso_rr_hidings bit1= add to joliet_hidings bit2= add to hfsplus_hidings */ int Xorriso_option_not_leaf(struct XorrisO *xorriso, char *pattern, int flag) { regex_t re; char *regexpr= NULL; int ret= 0; Xorriso_alloc_meM(regexpr, char, 2 * SfileadrL + 2); if(pattern[0]==0) {ret= 0; goto cannot_add;} Xorriso__bourne_to_reg(pattern, regexpr, 0); if(regcomp(&re, regexpr, 0)!=0) {ret= 0; goto cannot_add;} if(flag & 63) { if(flag & 1) { ret= Exclusions_add_not_leafs(xorriso->iso_rr_hidings, pattern, &re, 0); if(ret<=0) goto cannot_add; } if(flag & 2) { ret= Exclusions_add_not_leafs(xorriso->joliet_hidings, pattern, &re, 0); if(ret<=0) goto cannot_add; } if(flag & 4) { ret= Exclusions_add_not_leafs(xorriso->hfsplus_hidings, pattern, &re, 0); if(ret<=0) goto cannot_add; } } else { ret= Exclusions_add_not_leafs(xorriso->disk_exclusions, pattern, &re, 0); } if(ret<=0) { cannot_add:; sprintf(xorriso->info_text,"Cannot add pattern: %s ", (flag & 3) ? "-hide_disk_leaf" : "-not_leaf"); Text_shellsafe(pattern, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto ex; } ret= 1; ex:; Xorriso_free_meM(regexpr); return(ret); } /* Option -not_list , -quoted_not_list */ /* @param flag bit0= -quoted_not_list */ int Xorriso_option_not_list(struct XorrisO *xorriso, char *adr, int flag) { int ret, linecount= 0, insertcount= 0, null= 0, argc= 0, i; FILE *fp= NULL; char **argv= NULL; Xorriso_pacifier_reset(xorriso, 0); if(adr[0]==0) { sprintf(xorriso->info_text, "Empty file name given with %s", (flag & 1) ? "-quoted_not_list" : "-not_list"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= Xorriso_afile_fopen(xorriso, adr, "rb", &fp, 0); if(ret <= 0) return(0); while(1) { ret= Xorriso_read_lines(xorriso, fp, &linecount, &argc, &argv, 4 | (flag & 1) ); if(ret <= 0) goto ex; if(ret == 2) break; for(i= 0; i < argc; i++) { if(argv[i][0] == 0) continue; if(strchr(argv[i], '/')!=NULL) { null= 0; ret= Xorriso_option_not_paths(xorriso, 1, argv + i, &null, 0); } else ret= Xorriso_option_not_leaf(xorriso, argv[i], 0); if(ret<=0) goto ex; insertcount++; } } ret= 1; ex:; Xorriso_read_lines(xorriso, fp, &linecount, &argc, &argv, 2); if(fp != NULL && fp != stdin) fclose(fp); if(ret<=0) { sprintf(xorriso->info_text, "Aborted reading of file "); Text_shellsafe(adr, xorriso->info_text, 1); sprintf(xorriso->info_text + strlen(xorriso->info_text), " in line number %d", linecount); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } sprintf(xorriso->info_text, "Added %d exclusion list items from file ", insertcount); Text_shellsafe(adr, xorriso->info_text, 1); strcat(xorriso->info_text, "\n"); Xorriso_info(xorriso,0); return(ret); } /* Option -not_mgt */ int Xorriso_option_not_mgt(struct XorrisO *xorriso, char *setting, int flag) { int ret; char *what_data= NULL, *what, *what_next; Xorriso_alloc_meM(what_data, char, SfileadrL); if(Sfile_str(what_data, setting, 0)<=0) { sprintf(xorriso->info_text, "-not_mgt: setting string is much too long (%d)", (int) strlen(setting)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } for(what= what_data; what!=NULL; what= what_next) { what_next= strchr(what, ':'); if(what_next!=NULL) { *what_next= 0; what_next++; } if(strcmp(what, "reset")==0 || strcmp(what, "erase")==0) { if(strcmp(what, "reset")==0) xorriso->disk_excl_mode= 1; Exclusions_destroy(&(xorriso->disk_exclusions), 0); ret= Exclusions_new(&(xorriso->disk_exclusions), 0); if(ret<=0) { Xorriso_no_malloc_memory(xorriso, NULL, 0); goto ex; } } else if(strcmp(what, "on")==0) { xorriso->disk_excl_mode|= 1; } else if(strcmp(what, "off")==0) { xorriso->disk_excl_mode&= ~1; } else if(strcmp(what, "param_on")==0) { xorriso->disk_excl_mode|= 2; } else if(strcmp(what, "param_off")==0) { xorriso->disk_excl_mode&= ~2; } else if(strcmp(what, "subtree_on")==0) { xorriso->disk_excl_mode|= 4; } else if(strcmp(what, "subtree_off")==0) { xorriso->disk_excl_mode&= ~4; } else if(strcmp(what, "ignore_on")==0) { xorriso->disk_excl_mode|= 8; } else if(strcmp(what, "ignore_off")==0) { xorriso->disk_excl_mode&= ~8; } else { sprintf(xorriso->info_text, "-not_mgt: unknown setting '%s'", what); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } } ret= 1; ex:; Xorriso_free_meM(what_data); return(ret); } /* Option -not_paths , (-hide_disk_paths , -as mkisofs -hide) */ /* @param flag bit0= add to iso_rr_hidings rather than disk_exclusions bit1= add to joliet_hidings rather than disk_exclusions bit2= enable disk pattern expansion regardless of -disk_pattern bit8-13= consolidated hide state bits, duplicating bit0-1 bit8= add to iso_rr_hidings bit9= add to joliet_hidings bit10= add to hfsplus_hidings */ int Xorriso_option_not_paths(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int ret, end_idx, num_descr= 0, dummy, optc= 0, i; char **descr= NULL, **optv= NULL, *eff_path= NULL, *hpt= NULL; end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, (xorriso->do_disk_pattern == 1 || (flag & 4)) | 2); if(end_idx<=0) {ret= end_idx; goto ex;} num_descr= end_idx - *idx; if(num_descr<=0) {ret= 1; goto ex;} /* produce absolute patterns */ Xorriso_alloc_meM(eff_path, char, SfileadrL); descr= TSOB_FELD(char *, num_descr); if(descr==NULL) { no_memory:; Xorriso_no_pattern_memory(xorriso, sizeof(char *) * (off_t) num_descr, 0); ret= -1; goto ex; } for(i= 0; i<num_descr; i++) descr[i]= NULL; for(i= 0; i<num_descr; i++) { ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, argv[i+*idx], eff_path, 2|4); if(ret<=0) goto ex; descr[i]= strdup(eff_path); if(descr[i]==NULL) goto no_memory; } ret= Xorriso_opt_args(xorriso, (flag & 0x3f03) ? "-hide_disk_paths" : "-not_paths", num_descr, descr, 0, &dummy, &optc, &optv, 2 | ((flag & 4) << 7)); if(ret<=0) goto ex; if(flag & 0x3f03) { if(flag & 0x0101) { ret= Exclusions_add_not_paths(xorriso->iso_rr_hidings, num_descr, descr, optc, optv, 0); if(ret<=0) { no_hide:; sprintf(xorriso->info_text, "Cannot add path list: -hide_disk_paths "); hpt= Xorriso__hide_mode_text(flag & 0x3f03, 0); if(hpt != NULL) sprintf(xorriso->info_text + strlen(xorriso->info_text), "%s ", hpt); Xorriso_free_meM(hpt); Text_shellsafe(argv[*idx], xorriso->info_text, 1); strcat(xorriso->info_text, num_descr > 1 ? " ... " : " "); strcat(xorriso->info_text, xorriso->list_delimiter); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto ex; } } if(flag & 0x0202) { ret= Exclusions_add_not_paths(xorriso->joliet_hidings, num_descr, descr, optc, optv, 0); if(ret<=0) goto no_hide; } if(flag & 0x0400) { ret= Exclusions_add_not_paths(xorriso->hfsplus_hidings, num_descr, descr, optc, optv, 0); if(ret<=0) goto no_hide; } } else { ret= Exclusions_add_not_paths(xorriso->disk_exclusions, num_descr, descr, optc, optv, 0); if(ret<=0) { sprintf(xorriso->info_text,"Cannot add path list: -not_paths "); Text_shellsafe(argv[*idx], xorriso->info_text, 1); strcat(xorriso->info_text, num_descr > 1 ? " ... " : " "); strcat(xorriso->info_text, xorriso->list_delimiter); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } } ex:; (*idx)= end_idx; Xorriso_opt_args(xorriso, "-not_paths", num_descr, descr, 0, &dummy, &optc, &optv, 256); if(descr!=NULL) { for(i= 0; i<num_descr; i++) if(descr[i]!=NULL) free(descr[i]); free((char *) descr); descr= NULL; } Xorriso_free_meM(eff_path); return(ret); } /* Option -options_from_file */ int Xorriso_option_options_from_file(struct XorrisO *xorriso, char *adr, int flag) /* bit0= called from Xorriso_prescan_args, therefore execute via that same function */ /* return: <=0 error , 1 = success , 3 = end program run */ { int ret,linecount= 0, argc= 0, was_failure= 0, fret; FILE *fp= NULL; char **argv= NULL; int linec= 0; char *line= NULL, **linev= NULL; if(adr[0]==0) { sprintf(xorriso->info_text,"Empty file name given with -options_from_file"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } if(xorriso->is_dialog) { sprintf(xorriso->info_text,"+ performing command lines from file "); Text_shellsafe(adr, xorriso->info_text, 1); strcat(xorriso->info_text, " :\n"); Xorriso_info(xorriso,1); } ret= Xorriso_afile_fopen(xorriso, adr, "rb", &fp, 0); if(ret <= 0) return(0); sprintf(xorriso->info_text, "Command file: "); Text_shellsafe(adr, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); while(1) { ret= Xorriso_read_lines(xorriso, fp, &linecount, &linec, &linev, 1 | 8); if(ret <= 0) goto ex; /* no problem_handler because there is no sense in going on */ if(ret == 2) break; line= linev[0]; if(line[0]==0 || line[0]=='#') continue; if(flag&1) { ret= Sfile_make_argv(xorriso->progname, line, &argc, &argv, 4 | 8 | ((xorriso->bsl_interpretation & 3) << 5)); if(ret<=0) goto problem_handler; ret= Xorriso_prescan_args(xorriso,argc,argv,1); if(ret==0) {ret= 3; goto ex;} if(ret<0) goto problem_handler; } else { if(xorriso->is_dialog) { sprintf(xorriso->info_text,"+ %d: %s\n",linecount,line); Xorriso_info(xorriso,1); } ret= Xorriso_execute_option(xorriso,line,1|(1<<16)); if(ret==3) goto ex; if(ret<=0) goto problem_handler; } continue; /* regular bottom of loop */ problem_handler:; was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1); if(fret>=0) continue; goto ex; } ret= 1; ex:; Sfile_make_argv("", "", &argc, &argv, 2); /* release memory */ Xorriso_read_lines(xorriso, fp, &linecount, &linec, &linev, 2); Xorriso_reset_counters(xorriso,0); if(fp != NULL && fp != stdin) fclose(fp); if(ret<=0) { sprintf(xorriso->info_text, "error triggered by line %d of file:\n ", linecount); Text_shellsafe(adr, xorriso->info_text, 1); strcat(xorriso->info_text, "\n"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 1); } sprintf(xorriso->info_text, "Command file end: "); Text_shellsafe(adr, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); if(ret!=1) return(ret); return(!was_failure); } /* Option -osirrox "on"|"off" */ int Xorriso_option_osirrox(struct XorrisO *xorriso, char *mode, int flag) { int l, allow_restore; char *npt, *cpt; double num= 0.0; allow_restore= xorriso->allow_restore; npt= cpt= mode; for(cpt= mode; npt!=NULL; cpt= npt+1) { npt= strchr(cpt,':'); if(npt==NULL) l= strlen(cpt); else l= npt-cpt; if(l==0 && mode[0]!=0) goto unknown_mode; if(strncmp(cpt, "off", l)==0 && l >= 3) allow_restore= 0; else if(strncmp(cpt, "banned", l)==0 && l >= 5) allow_restore= -1; else if(strncmp(cpt, "blocked", l)==0 && l >= 7) allow_restore= -2; else if(strncmp(cpt, "unblock", l)==0 && l >= 7) { if(xorriso->allow_restore == -2) xorriso->allow_restore= 0; allow_restore= 1; } else if(strncmp(cpt, "device_files", l)==0 && l >= 12) allow_restore= 2; else if((strncmp(cpt, "on", l)==0 && l >= 2) || mode[0]==0) allow_restore= 1; else if(strncmp(cpt, "concat_split_on", l)==0 && l >= 15) xorriso->do_concat_split= 1; else if(strncmp(cpt, "concat_split_off", l)==0 && l >= 16) xorriso->do_concat_split= 0; else if(strncmp(cpt, "auto_chmod_on", l)==0 && l >= 13) xorriso->do_auto_chmod= 1; else if(strncmp(cpt, "auto_chmod_off", l)==0 && l >= 14) xorriso->do_auto_chmod= 0; else if(strncmp(cpt, "sort_lba_on", l)==0 && l >= 11) xorriso->do_restore_sort_lba= 1; else if(strncmp(cpt, "sort_lba_off", l)==0 && l >= 12) xorriso->do_restore_sort_lba= 0; else if(strncmp(cpt, "o_excl_on", l)==0 && l >= 9) xorriso->drives_exclusive= 1; else if(strncmp(cpt, "o_excl_off", l)==0 && l >= 10) xorriso->drives_exclusive= 0; else if(strncmp(cpt, "strict_acl_on", l)==0 && l >= 13) xorriso->do_strict_acl|= 1; else if(strncmp(cpt, "strict_acl_off", l)==0 && l >= 14) xorriso->do_strict_acl&= ~1; else if(strncmp(cpt, "check_md5_on", l) == 0 && l >= 12) { xorriso->do_md5|= 1 << 6; xorriso->do_md5&= ~(2 << 6); } else if(strncmp(cpt, "check_md5_force", l)==0 && l >= 15) { xorriso->do_md5|= 3 << 6; } else if(strncmp(cpt, "check_md5_off", l)==0 && l >= 13) { xorriso->do_md5&= ~(3 << 6); } else if(strncmp(cpt, "sparse=", 7) == 0 && l >= 7) { if(strncmp(cpt + 7, "off", 3) == 0 && l == 10) { num= 0.0; } else { Xorriso__parse_size_param(cpt, 7, l, &num); if(num < 1.0) num= 0.0; if(num > 1.0 * 1024.0 * 1024.0 * 1024.0) { strcpy(xorriso->info_text, "osirrox sparse= too large (allowed: off, 1 to 1g)"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } xorriso->sparse_min_gap= num; } else { unknown_mode:; sprintf(xorriso->info_text, "-osirrox: unknown mode '%s'", cpt); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } if(allow_restore > 0 && xorriso->allow_restore == -1) { sprintf(xorriso->info_text, "-osirrox: was already permanently disabled by setting 'banned'"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(allow_restore > 0 && xorriso->allow_restore == -2) { sprintf(xorriso->info_text, "-osirrox: is currently disabled by setting 'blocked'"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(xorriso->allow_restore != -1) xorriso->allow_restore= allow_restore; sprintf(xorriso->info_text, "Copying of file objects from ISO image to disk filesystem is: %s\n", xorriso->allow_restore > 0 ? "Enabled" : "Disabled"); Xorriso_info(xorriso, 0); return(1); } /* Option -overwrite "on"|"nondir"|"off" */ int Xorriso_option_overwrite(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "off")==0) xorriso->do_overwrite= 0; else if(strcmp(mode, "on")==0) xorriso->do_overwrite= 1; else if(strcmp(mode, "nondir")==0) xorriso->do_overwrite= 2; else { sprintf(xorriso->info_text, "-overwrite: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of options -p* to -z* as mentioned in man page or info file derived from xorriso.texi. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" /* Option -pacifier */ int Xorriso_option_pacifier(struct XorrisO *xorriso, char *style, int flag) { #define Xorriso_pacifier_min_intvL 0.1 #define Xorriso_pacifier_max_intvL 60.0 if(strcmp(style, "xorriso")==0 || strcmp(style, "default")==0) xorriso->pacifier_style= 0; else if(strcmp(style, "mkisofs")==0 || strcmp(style, "genisofs")==0 || strcmp(style, "genisoimage")==0 || strcmp(style, "xorrisofs")==0) xorriso->pacifier_style= 1; else if(strcmp(style, "cdrecord")==0 || strcmp(style, "cdrskin")==0 || strcmp(style, "wodim")==0 || strcmp(style, "xorrecord")==0) xorriso->pacifier_style= 2; else if(strncmp(style, "interval=", 9) == 0) { sscanf(style + 9, "%lf", &(xorriso->pacifier_interval)); if(xorriso->pacifier_interval < Xorriso_pacifier_min_intvL) { sprintf(xorriso->info_text, "-pacifier: interval='%s' is too low. Min: %f", style, Xorriso_pacifier_min_intvL); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); xorriso->pacifier_interval= Xorriso_pacifier_min_intvL; } else if(xorriso->pacifier_interval > Xorriso_pacifier_max_intvL) { sprintf(xorriso->info_text, "-pacifier: interval='%s' is too high. Max: %f", style, Xorriso_pacifier_max_intvL); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); xorriso->pacifier_interval= Xorriso_pacifier_max_intvL; } } else { sprintf(xorriso->info_text, "-pacifier: unknown behavior code '%s'", style); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } return(1); } /* Option -padding */ int Xorriso_option_padding(struct XorrisO *xorriso, char *size, int flag) { double num; if(strcmp(size, "included") == 0) { xorriso->do_padding_by_libisofs= 1; return(1); } else if(strcmp(size, "excluded") == 0 || strcmp(size, "appended") == 0) { xorriso->do_padding_by_libisofs= 0; return(1); } else if(size[0] < '0' || size[0] > '9') { sprintf(xorriso->info_text, "-padding: unrecognized non-numerical mode "); Text_shellsafe(size, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } num= Scanf_io_size(size, 0); if(num < 0 || num > 1024.0 * 1024.0 * 1024.0) { sprintf(xorriso->info_text, "-padding: wrong size %.f (allowed: %.f - %.f)", num, 0.0, 1024.0 * 1024.0 * 1024.0); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } xorriso->padding= num; if(xorriso->padding/2048 != num/2048.0) xorriso->padding++; return(1); } /* Option -page */ int Xorriso_option_page(struct XorrisO *xorriso, int len, int width, int flag) { if(len<0 || width<=0) { sprintf(xorriso->info_text, "Improper numeric value of arguments of -page: %d %d", len, width); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } xorriso->result_page_length= len; xorriso->result_page_width= width; return(1); } /* Option -paste_in */ int Xorriso_option_paste_in(struct XorrisO *xorriso, char *iso_rr_path, char *disk_path, char *start, char *count, int flag) { int ret; double num; off_t startbyte, bytecount; num= Scanf_io_size(start, 0); if(num<0 || num > 1.0e18) { /* 10^18 = 10^3 ^ 6 < 2^10 ^ 6 = 2^60 */ sprintf(xorriso->info_text, "-paste_in: startbyte address negative or much too large (%s)", start); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } startbyte= num; num= Scanf_io_size(count, 0); if(num<=0 || num > 1.0e18) { sprintf(xorriso->info_text, "-paste_in : bytecount zero, negative or much too large (%s)", count); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } bytecount= num; sprintf(xorriso->info_text, "-paste_in from %s to %s, byte %.f to %.f", iso_rr_path, disk_path, (double) startbyte, (double) (startbyte+bytecount)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); Xorriso_pacifier_reset(xorriso, 0); ret= Xorriso_paste_in(xorriso, disk_path, startbyte, bytecount, iso_rr_path, 0); Xorriso_pacifier_callback(xorriso, "files restored",xorriso->pacifier_count, xorriso->pacifier_total, "", 1 | 4 | 8 | 32); return(ret); } /* Option -path_list , -quoted_path_list */ /* @param flag bit0= -quoted_path_list bit1= mkisofs mode: Use / as target for pathspecs without = */ int Xorriso_option_path_list(struct XorrisO *xorriso, char *adr, int flag) { int ret,linecount= 0, insertcount= 0, null= 0, was_failure= 0, fret= 0; int was_ferror= 0, argc= 0, i, allow_graft_points_mem; FILE *fp= NULL; char **argv= NULL, *pathspec= NULL; allow_graft_points_mem= xorriso->allow_graft_points; Xorriso_pacifier_reset(xorriso, 0); if(adr[0]==0) { sprintf(xorriso->info_text,"Empty file name given with %s", flag & 1 ? "-quoted_path_list" : "-path_list"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); return(0); } ret= Xorriso_afile_fopen(xorriso, adr, "rb", &fp, 0); if(ret <= 0) return(0); Xorriso_alloc_meM(pathspec, char, 2 * SfileadrL); while(1) { ret= Xorriso_read_lines(xorriso, fp, &linecount, &argc, &argv, 4 | (flag & 1) ); if(ret <= 0) goto ex; if(ret == 2) break; for(i= 0; i < argc; i++) { if(argv[i][0] == 0) continue; null= 0; if(flag & 2) { ret= Xorriso_graftable_pathspec(xorriso, argv[i], pathspec, 0); if(ret <= 0) goto problem_handler; xorriso->allow_graft_points= 3; ret= Xorriso_option_add(xorriso, 1, &pathspec, &null, 1 | 2); xorriso->allow_graft_points= allow_graft_points_mem; } else { ret= Xorriso_option_add(xorriso, 1, argv + i, &null, 1 | 2); } if(ret<=0 || xorriso->request_to_abort) goto problem_handler; insertcount++; continue; /* regular bottom of loop */ problem_handler:; was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; if(ret > 0) ret= 0; goto ex; } } ret= 1; ex:; xorriso->allow_graft_points= allow_graft_points_mem; Sfile_make_argv("", "", &argc, &argv, 2); Xorriso_free_meM(pathspec); Xorriso_read_lines(xorriso, fp, &linecount, &argc, &argv, 2); if(fp != NULL && fp != stdin) fclose(fp); Xorriso_pacifier_callback(xorriso, "files added", xorriso->pacifier_count, xorriso->pacifier_total, "", 1); if(ret<=0) { sprintf(xorriso->info_text, "Aborted reading of file "); Text_shellsafe(adr, xorriso->info_text, 1); sprintf(xorriso->info_text + strlen(xorriso->info_text), " in line number %d", linecount); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, (fret==-2 ? "NOTE" : "FAILURE"), 0); } else ret= !was_ferror; sprintf(xorriso->info_text, "Added %d items from file ", insertcount); Text_shellsafe(adr, xorriso->info_text, 1); strcat(xorriso->info_text, "\n"); Xorriso_info(xorriso,0); if(ret<=0) return(ret); return(!was_failure); } /* Option -pathspecs */ int Xorriso_option_pathspecs(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "off")==0) xorriso->allow_graft_points= 0; else if(strcmp(mode, "on")==0) xorriso->allow_graft_points= 1; else if(strcmp(mode, "as_mkisofs")==0) xorriso->allow_graft_points= 3; else { sprintf(xorriso->info_text, "-pathspecs: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -pkt_output */ int Xorriso_option_pkt_output(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode,"off")==0) xorriso->packet_output= 0; else xorriso->packet_output= 1; return(1); } /* Option -preparer_id */ int Xorriso_option_preparer_id(struct XorrisO *xorriso, char *name, int flag) { if(Xorriso_check_name_len(xorriso, name, (int) sizeof(xorriso->preparer_id), "-preparer_id", 0) <= 0) return(0); if(strcmp(name, "@xorriso@") == 0) Xorriso_preparer_string(xorriso, xorriso->preparer_id, 0); else strcpy(xorriso->preparer_id, name); Xorriso_set_change_pending(xorriso, 1); return(1); } /* Options -print , -print_info , -print_mark */ /* @param flag bit0-1= channel: 0=result, 1=info, 2=mark */ int Xorriso_option_print(struct XorrisO *xorriso, char *text, int flag) { int maxl, l, mode; l= strlen(text); mode= flag & 3; if(mode == 1) maxl= sizeof(xorriso->info_text); else if(mode == 2) maxl= sizeof(xorriso->mark_text); else maxl= sizeof(xorriso->result_line); if(l > maxl - 2) { sprintf(xorriso->info_text, "Output text too long for -print%s(%d > %d)", mode == 1 ? "_info" : mode == 2 ? "_mark" : "", l, maxl - 2); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); return(0); } if(mode == 1) { sprintf(xorriso->info_text,"%s\n", text); Xorriso_info(xorriso,0); } else if(mode == 2) { strcpy(xorriso->info_text, xorriso->mark_text); strcpy(xorriso->mark_text, text); Xorriso_mark(xorriso,0); strcpy(xorriso->mark_text, xorriso->info_text); } else { sprintf(xorriso->result_line,"%s\n",text); Xorriso_result(xorriso,1); } return(1); } /* Option -print_size @param flag bit0= report in mkisofs compatible form on real stdout (resp. on result channel if xorriso->packet_output) */ int Xorriso_option_print_size(struct XorrisO *xorriso, int flag) { int fd; off_t ret; xorriso->print_size_attempts++; if(!Xorriso_change_is_pending(xorriso, 0)) { sprintf(xorriso->info_text,"-print_size: No image modifications pending"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); if(!(flag & 1)) { sprintf(xorriso->result_line,"Image size : 0s\n"); Xorriso_result(xorriso,0); } return(2); } ret= Xorriso_write_session(xorriso, 1); if(ret<=0) { sprintf(xorriso->info_text,"-print_size: Failed to set up virtual -commit"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } if(flag&1) { sprintf(xorriso->result_line,"%.f\n", (double) ret); if(xorriso->packet_output) { Xorriso_result(xorriso,0); } else { fd= xorriso->dev_fd_1; if(fd<0) fd= 1; ret= write(fd, xorriso->result_line, strlen(xorriso->result_line)); /* (result of write intentionally ignored) */ fsync(fd); } } else { sprintf(xorriso->result_line,"Image size : %.fs\n", (double) ret); Xorriso_result(xorriso,0); } return(1); } /* Option -prog */ int Xorriso_option_prog(struct XorrisO *xorriso, char *name, int flag) { if(strlen(name)>=sizeof(xorriso->progname)) { sprintf(xorriso->info_text, "Name too long with option -prog (%d > %d)", (int) strlen(name), (int) sizeof(xorriso->progname)-1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } if(Sfile_str(xorriso->progname,name,0)<=0) return(-1); return(1); } /* Option -prog_help */ int Xorriso_option_prog_help(struct XorrisO *xorriso, char *name, int flag) { int ret; ret= Xorriso_option_prog(xorriso, name, 0); if(ret<=0) return(ret); ret= Xorriso_option_help(xorriso, 0); return(ret); } /* Option -prompt */ int Xorriso_option_prompt(struct XorrisO *xorriso, char *text, int flag) { int ret; char line[80]; strncpy(xorriso->result_line,text,sizeof(xorriso->result_line)-1); xorriso->result_line[sizeof(xorriso->result_line)-1]= 0; Xorriso_result(xorriso,0); ret= Xorriso_dialog_input(xorriso, line, sizeof(line),1); return(ret); } /* Option -publisher */ int Xorriso_option_publisher(struct XorrisO *xorriso, char *name, int flag) { if(Xorriso_check_name_len(xorriso, name, (int) sizeof(xorriso->publisher), "-publisher", 0) <= 0) return(0); strcpy(xorriso->publisher,name); Xorriso_set_change_pending(xorriso, 1); return(1); } /* Option -pvd_info */ int Xorriso_option_pvd_info(struct XorrisO *xorriso, int flag) { return(Xorriso_pvd_info(xorriso, 0)); } /* Option -pwd alias -pwdi */ int Xorriso_option_pwdi(struct XorrisO *xorriso, int flag) { sprintf(xorriso->info_text,"current working directory in ISO image:\n"); Xorriso_info(xorriso,0); Xorriso_esc_filepath(xorriso, xorriso->wdi, xorriso->result_line, 0); if(xorriso->sh_style_result == 0 || xorriso->wdi[0] == 0) strcat(xorriso->result_line, "/"); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso,0); return(1); } /* Option -pwdx */ int Xorriso_option_pwdx(struct XorrisO *xorriso, int flag) { sprintf(xorriso->info_text,"current working directory on hard disk:\n"); Xorriso_info(xorriso,0); Xorriso_esc_filepath(xorriso, xorriso->wdx, xorriso->result_line, 0); if(xorriso->sh_style_result == 0 || xorriso->wdx[0] == 0) strcat(xorriso->result_line, "/"); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso,0); return(1); } /* Command -read_fs */ int Xorriso_option_read_fs(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "any") == 0) { xorriso->read_fs= 0; } else if(strcmp(mode, "norock") == 0) { xorriso->read_fs= 1; } else if(strcmp(mode, "nojoliet") == 0) { xorriso->read_fs= 2; } else if(strcmp(mode, "ecma119") == 0) { xorriso->read_fs= 3; } else { sprintf(xorriso->info_text, "-read_fs: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } int Xorriso_option_read_mkisofsrc(struct XorrisO *xorriso, int flag) { int ret; ret= Xorriso_read_mkisofsrc(xorriso, 0); return(ret); } /* Option -reassure "on"|"tree"|"off" */ int Xorriso_option_reassure(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "off")==0) xorriso->do_reassure= 0; else if(strcmp(mode, "on")==0) xorriso->do_reassure= 1; else if(strcmp(mode, "tree")==0) xorriso->do_reassure= 2; else { sprintf(xorriso->info_text, "-reassure: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } return(1); } /* Option -report_about */ int Xorriso_option_report_about(struct XorrisO *xorriso, char *in_severity, int flag) { int ret, sev; char severity[20], *official; Xorriso__to_upper(in_severity, severity, (int) sizeof(severity), 0); ret= Xorriso__text_to_sev(severity, &sev, 0); if(ret<=0) { sprintf(xorriso->info_text, "-report_about: Not a known severity name : "); Text_shellsafe(in_severity, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); return(ret); } ret= Xorriso__sev_to_text(sev, &official, 0); if(ret <= 0) official= severity; if(Sfile_str(xorriso->report_about_text, official, 0) <= 0) return(-1); xorriso->report_about_severity= sev; return(1); } /* Command -report_el_torito */ int Xorriso_option_report_el_torito(struct XorrisO *xorriso, char *form, int flag) { int ret; ret= Xorriso_report_system_area(xorriso, form, 1); return(ret); } /* Command -report_system_area */ int Xorriso_option_report_system_area(struct XorrisO *xorriso, char *form, int flag) { int ret; ret= Xorriso_report_system_area(xorriso, form, 0); return(ret); } /* Option -return_with */ int Xorriso_option_return_with(struct XorrisO *xorriso, char *in_severity, int exit_value, int flag) { int ret, sev; char severity[20], *official; Xorriso__to_upper(in_severity, severity, (int) sizeof(severity), 0); ret= Xorriso__text_to_sev(severity, &sev, 0); if(ret<=0) { sprintf(xorriso->info_text, "-return_with: Not a known severity name : "); Text_shellsafe(in_severity, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(ret); } ret= Xorriso__sev_to_text(sev, &official, 0); if(ret <= 0) official= severity; if(exit_value && (exit_value < 32 || exit_value > 63)) { sprintf(xorriso->info_text, "-return_with: Not an allowed exit_value. Use 0, or 32 to 63."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(Sfile_str(xorriso->return_with_text, official, 0) <= 0) return(-1); xorriso->return_with_severity= sev; xorriso->return_with_value= exit_value; return(1); } /* Options -rm alias -rmi , -rm_r alias -rm_ri , -rmdir alias -rmdiri */ /* @param flag bit0=recursive , bit1= remove empty directory: rmdir */ int Xorriso_option_rmi(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int i, ret, end_idx, was_failure= 0, fret; char *path= NULL, *eff_path= NULL; int optc= 0; char **optv= NULL; ret= Xorriso_opt_args(xorriso, "-rm*i", argc, argv, *idx, &end_idx, &optc, &optv, 0); if(ret<=0) goto ex; Xorriso_alloc_meM(path, char, SfileadrL); Xorriso_alloc_meM(eff_path, char, SfileadrL); for(i= 0; i<optc; i++) { if(Sfile_str(path,optv[i],0)<=0) {ret= -1; goto problem_handler;} if(path[0]!='/') { ret= Sfile_prepend_path(xorriso->wdi, path, 0); if(ret<=0) goto problem_handler; } ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, path, eff_path, 1); if(ret<0) goto problem_handler; if(ret==0) { sprintf(xorriso->info_text, "Cannot find path "); Text_shellsafe(path, xorriso->info_text, 1); strcat(xorriso->info_text, " in loaded ISO image for removal"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); goto problem_handler; } strcpy(path, eff_path); ret= Xorriso_rmi(xorriso, NULL, (off_t) 0, path, flag&(1|2)); if(ret<=0 || xorriso->request_to_abort) goto problem_handler; if(ret<3) { sprintf(xorriso->info_text, "Removed from ISO image: %s '%s'\n", ((flag&2) ? "directory" : (ret>1 ? "subtree" : "file")), path); Xorriso_info(xorriso, 0); } continue; /* regular bottom of loop */ problem_handler:; was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; goto ex; } ret= 1; ex:; (*idx)= end_idx; Xorriso_free_meM(path); Xorriso_free_meM(eff_path); Xorriso_opt_args(xorriso, "-rm*i", argc, argv, *idx, &end_idx, &optc, &optv, 256); if(ret<=0) return(ret); return(!was_failure); } /* Option -rockridge "on"|"off" */ int Xorriso_option_rockridge(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "off")==0) xorriso->do_rockridge= 0; else if(strcmp(mode, "on")==0) xorriso->do_rockridge= 1; else { sprintf(xorriso->info_text, "-rockridge: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -rollback */ /* @param flag bit0= do not -reassure @return <=0 error , 1 success, 2 revoked by -reassure */ int Xorriso_option_rollback(struct XorrisO *xorriso, int flag) { int ret; char *indev= NULL, *which_will; Xorriso_alloc_meM(indev, char, SfileadrL); if(Xorriso_change_is_pending(xorriso, 0)) which_will= "revoke the pending image changes"; else which_will= "reload the image"; if(!(flag&1)) { ret= Xorriso_reassure(xorriso, "-rollback", which_will, 0); if(ret<=0) {ret= 2; goto ex;} } if(Sfile_str(indev, xorriso->indev, 0)<=0) {ret= -1; goto ex;} xorriso->volset_change_pending= 0; ret= Xorriso_give_up_drive(xorriso, 1|8); if(ret<=0) goto ex; xorriso->image_start_mode&= ~(1u<<31); /* reactivate eventual -load address */ ret= Xorriso_option_dev(xorriso, indev, 1|4); ex:; Xorriso_free_meM(indev); return(ret); } /* Option -rom_toc_scan */ int Xorriso_option_rom_toc_scan(struct XorrisO *xorriso, char *mode, int flag) { int l; char *cpt, *npt; npt= cpt= mode; for(cpt= mode; npt != NULL; cpt= npt + 1) { npt= strchr(cpt,':'); if(npt==NULL) l= strlen(cpt); else l= npt-cpt; if(l==0) continue; if(strncmp(cpt, "off", l) == 0) xorriso->toc_emulation_flag&= ~5; else if(strncmp(cpt, "on", l) == 0) xorriso->toc_emulation_flag= (xorriso->toc_emulation_flag & ~4) | 1; else if(strncmp(cpt, "force", l) == 0) xorriso->toc_emulation_flag|= 5; else if(strncmp(cpt, "emul_off", l) == 0) xorriso->toc_emulation_flag|= 2; else if(strncmp(cpt, "emul_on", l) == 0) xorriso->toc_emulation_flag&= ~2; else if(strncmp(cpt, "emul_wide", l) == 0) xorriso->toc_emulation_flag|= 8; else if(strncmp(cpt, "emul_narrow", l) == 0) xorriso->toc_emulation_flag&= ~8; else { sprintf(xorriso->info_text, "-rom_toc_scan: unknown mode in '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } return(1); } /* Command -rr_reloc_dir */ int Xorriso_option_rr_reloc_dir(struct XorrisO *xorriso, char *name, int flag) { if(strlen(name) > 255) { sprintf(xorriso->info_text, "Name too long with -rr_reloc_dir. Max. 255 bytes allowed."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(strchr(name, '/') != NULL) { sprintf(xorriso->info_text, "Name given with -rr_reloc_dir contains '/' character"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } strcpy(xorriso->rr_reloc_dir, name); return(1); } /* Option -scdbackup_tag list_path record_name */ int Xorriso_option_scdbackup_tag(struct XorrisO *xorriso, char *listname, char *recname, int flag) { if(strlen(recname) > 80) { sprintf(xorriso->info_text, "Unsuitable record name given with -scdbackup_tag"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } strcpy(xorriso->scdbackup_tag_name, recname); xorriso->scdbackup_tag_time[0]= 0; if(Sfile_str(xorriso->scdbackup_tag_listname, listname, 0) <= 0) return(-1); return(1); } /* Command -scsi_dev_family */ int Xorriso_option_scsi_dev_family(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "default") == 0) xorriso->linux_scsi_dev_family= 0; else if(strcmp(mode, "sr") == 0) xorriso->linux_scsi_dev_family= 1; else if(strcmp(mode, "scd") == 0) xorriso->linux_scsi_dev_family= 2; else if(strcmp(mode, "sg") == 0) xorriso->linux_scsi_dev_family= 4; else { sprintf(xorriso->info_text, "-scsi_dev_family: unknown family '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } Xorriso_scsi_dev_family(xorriso, 0); return(1); } /* Option -scsi_log */ int Xorriso_option_scsi_log(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "on") == 0) xorriso->scsi_log= 1; else if(strcmp(mode, "off") == 0) xorriso->scsi_log= 0; else { sprintf(xorriso->info_text, "-scsi_log: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } Xorriso_scsi_log(xorriso, !!xorriso->scsi_log); return(1); } /* Option -session_log */ int Xorriso_option_session_log(struct XorrisO *xorriso, char *path, int flag) { if(Sfile_str(xorriso->session_logfile, path, 0)<=0) return(-1); return(1); } /* Option -setfacl_list alias -setfacl_listi */ int Xorriso_option_setfacl_listi(struct XorrisO *xorriso, char *path, int flag) { int ret, eaten, line_size; size_t buf_size= 0, buf_add, l, linecount= 0, diff_buf_wpt; char *line= NULL, *buf= NULL, *wpt, *new_buf, limit_text[80]; char *file_path= NULL, *uid= NULL, *gid= NULL; FILE *fp= NULL; line_size= SfileadrL * 4; buf_add= line_size * 4; Xorriso_alloc_meM(line, char, line_size); Xorriso_alloc_meM(file_path, char, SfileadrL); Xorriso_alloc_meM(uid, char, 161); Xorriso_alloc_meM(gid, char, 161); Xorriso_pacifier_reset(xorriso, 0); if(path[0]==0) { sprintf(xorriso->info_text, "Empty file name given with -setfacl_list"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } ret= Xorriso_afile_fopen(xorriso, path, "rb", &fp, 0); if(ret <= 0) {ret= 0; goto ex;} buf_size= buf_add; buf= calloc(buf_size, 1); if(buf == NULL) goto out_of_mem; wpt= buf; *wpt= 0; uid[0]= gid[0]= 0; while(1) { if(Sfile_fgets_n(line, line_size, fp, 0) == NULL) break; linecount++; if(strncmp(line, "# file: ", 8) ==0) { if(wpt != buf && file_path[0]) { /* Commit previous list */ ret= Xorriso_perform_acl_from_list(xorriso, file_path, uid, gid, buf, 0); if(ret<=0) goto ex; wpt= buf; *wpt= 0; file_path[0]= uid[0]= gid[0]= 0; } /* Unescape line and register as file path */ Sfile_bsl_interpreter(line + 8, strlen(line + 8), &eaten, 0); if(strlen(line + 8) >= SfileadrL) { sprintf(xorriso->info_text, "-setfacl_list: Oversized file path"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } strcpy(file_path, line + 8); continue; } else if(strncmp(line, "# owner: ", 9) == 0) { if(strlen(line + 9) > 160) { sprintf(xorriso->info_text, "-setfacl_list: Oversized owner id"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } strcpy(uid, line + 9); continue; } else if(strncmp(line, "# group: ", 9) == 0) { if(strlen(line + 9) > 160) { sprintf(xorriso->info_text, "-setfacl_list: Oversized group id"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } strcpy(gid, line + 9); continue; } else if(line[0] == '#' || line[0] == 0) { continue; } else if(strcmp(line, "@") == 0) { Xorriso_msgs_submit(xorriso, 0, "-setfacl_list input ended by '@'", 0, "NOTE", 0); break; } else if(strcmp(line, "@@@") == 0) { Xorriso_msgs_submit(xorriso, 0, "-setfacl_list aborted by input line '@@@'", 0, "WARNING", 0); ret= 0; goto ex; } /* Register ACL entry */ l= strlen(line); if(wpt + l + 2 - buf > (int) buf_size) { if((int) (buf_size + buf_add) > xorriso->temp_mem_limit) { Sfile_scale((double) xorriso->temp_mem_limit, limit_text,5,1e4,1); sprintf(xorriso->info_text, "-setfacl_list: List entry for a single file exceeds -temp_mem_limit %s", limit_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } diff_buf_wpt= wpt - buf; buf_size+= buf_add; new_buf= realloc(buf, buf_size); if(new_buf == NULL) goto out_of_mem; buf= new_buf; wpt= buf + diff_buf_wpt; } memcpy(wpt, line, l); *(wpt + l)= '\n'; wpt+= l + 1; *wpt= 0; } if(wpt != buf && file_path[0]) { /* Commit last list */ ret= Xorriso_perform_acl_from_list(xorriso, file_path, uid, gid, buf, 0); if(ret<=0) goto ex; } else { sprintf(xorriso->info_text, "-setfacl_list: Unexpected end of file "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } ret= 1; ex:; if(buf != NULL) free(buf); if(fp != NULL && fp != stdin) fclose(fp); if(ret <= 0) { sprintf(xorriso->info_text, "-setfacl_list "); Text_shellsafe(path, xorriso->info_text, 1); sprintf(xorriso->info_text + strlen(xorriso->info_text), " aborted in line %.f\n", (double) linecount); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } Xorriso_free_meM(line); Xorriso_free_meM(file_path); Xorriso_free_meM(uid); Xorriso_free_meM(gid); return(ret); out_of_mem:; Xorriso_no_malloc_memory(xorriso, &buf, 0); ret= -1; goto ex; } /* Options -setfacl alias -setfacli, -setfacl_r alias -setfacl_ri */ /* @param flag bit0=recursive -setfacl_r */ int Xorriso_option_setfacli(struct XorrisO *xorriso, char *acl_text, int argc, char **argv, int *idx, int flag) { int i, ret, was_failure= 0, end_idx, fret; int optc= 0; char **optv= NULL, *access_acl_text= NULL, *default_acl_text= NULL; struct FindjoB *job= NULL; struct stat dir_stbuf; ret= Xorriso_opt_args(xorriso, "-setfacl", argc, argv, *idx, &end_idx, &optc, &optv, 0); if(ret <= 0) goto ex; ret= Xorriso_normalize_acl_text(xorriso, acl_text, &access_acl_text, &default_acl_text, 0); if(access_acl_text != NULL && default_acl_text != NULL) { strcpy(xorriso->info_text, "Access-ACL :\n"); Xorriso_set_info_text(xorriso, access_acl_text, 2000, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); strcpy(xorriso->info_text, "Default-ACL :\n"); Xorriso_set_info_text(xorriso, default_acl_text, 2000, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } else if(access_acl_text == NULL && default_acl_text == NULL) { sprintf(xorriso->info_text, "Will delete Access-ACL and Default-ACL"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } if(ret <= 0) goto ex; for(i= 0; i<optc; i++) { if(flag&1) { ret= Findjob_new(&job, optv[i], 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "-setfacl_r", 0); {ret= -1; goto ex;} } Findjob_set_action_text_2(job, 25, access_acl_text, default_acl_text, 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, optv[i], &dir_stbuf, 0, 0); Findjob_destroy(&job, 0); } else { ret= 1; if(access_acl_text == NULL || access_acl_text[0] || default_acl_text == NULL || default_acl_text[0]) ret= Xorriso_setfacl(xorriso, NULL, optv[i], access_acl_text, default_acl_text, 0); } if(ret>0 && !xorriso->request_to_abort) continue; /* regular bottom of loop */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; ret= 0; goto ex; } ret= 1; ex:; (*idx)= end_idx; Xorriso_opt_args(xorriso, "-setfacl", argc, argv, *idx, &end_idx, &optc, &optv, 256); Findjob_destroy(&job, 0); if(access_acl_text != NULL) free(access_acl_text); if(default_acl_text != NULL) free(default_acl_text); if(ret<=0) return(ret); return(!was_failure); } /* Options -setfattr alias -setfattri, -setfattr_r alias -setfattr_ri */ /* @param flag bit0=recursive -setfattr_r */ int Xorriso_option_setfattri(struct XorrisO *xorriso, char *name, char *value, int argc, char **argv, int *idx, int flag) { int i, ret, was_failure= 0, end_idx, fret; int optc= 0; char **optv= NULL; struct FindjoB *job= NULL; struct stat dir_stbuf; ret= Xorriso_opt_args(xorriso, "-setfattr", argc, argv, *idx, &end_idx, &optc, &optv, 0); if(ret <= 0) goto ex; /* check input */ ret= Xorriso_path_setfattr(xorriso, NULL, "", name, strlen(value), value, 1); if(ret <= 0) goto ex; for(i= 0; i<optc; i++) { if(flag&1) { ret= Findjob_new(&job, optv[i], 0); if(ret<=0) { Xorriso_no_findjob(xorriso, "-setfattr_r", 0); {ret= -1; goto ex;} } Findjob_set_action_text_2(job, 27, name, value, 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, optv[i], &dir_stbuf, 0, 0); Findjob_destroy(&job, 0); } else { ret= 1; ret= Xorriso_path_setfattr(xorriso, NULL, optv[i], name, strlen(value), value, 0); } if(ret>0 && !xorriso->request_to_abort) continue; /* regular bottom of loop */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; ret= 0; goto ex; } ret= 1; ex:; (*idx)= end_idx; Xorriso_opt_args(xorriso, "-setfattr", argc, argv, *idx, &end_idx, &optc, &optv, 256); Findjob_destroy(&job, 0); if(ret<=0) return(ret); return(!was_failure); } /* Option -setfattr_list alias -setfattr_listi */ int Xorriso_option_setfattr_listi(struct XorrisO *xorriso, char *path, int flag) { int ret, eaten, line_size= SfileadrL * 4; size_t linecount= 0, mem_used= 0, num_attr= 0, v_len; char *line= NULL, limit_text[80], *ept, *valuept; char *file_path= NULL; FILE *fp= NULL; struct Xorriso_lsT *lst_curr= NULL, *lst_start= NULL; Xorriso_alloc_meM(line, char, line_size); Xorriso_alloc_meM(file_path, char, SfileadrL); Xorriso_pacifier_reset(xorriso, 0); if(path[0]==0) { sprintf(xorriso->info_text, "Empty file name given with -setfattr_list"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } ret= Xorriso_afile_fopen(xorriso, path, "rb", &fp, 0); if(ret <= 0) {ret= 0; goto ex;} while(1) { if(Sfile_fgets_n(line, line_size, fp, 0) == NULL) break; linecount++; if(strncmp(line, "# file: ", 8) ==0) { if(num_attr > 0 && file_path[0]) { /* Commit previous list */ ret= Xorriso_perform_attr_from_list(xorriso, file_path, lst_start, 0); if(ret<=0) goto ex; num_attr= 0; file_path[0]= 0; Xorriso_lst_destroy_all(&lst_start, 0); lst_curr= NULL; } /* Unescape line and register as file path */ Sfile_bsl_interpreter(line + 8, strlen(line + 8), &eaten, 0); if(strlen(line + 8) >= SfileadrL) { sprintf(xorriso->info_text, "-setfattr_list: Oversized file path"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } strcpy(file_path, line + 8); continue; } else if(line[0] == '#' || line[0] == 0) { continue; } else if(strcmp(line, "@") == 0) { Xorriso_msgs_submit(xorriso, 0, "-setfattr_list input ended by '@'", 0, "NOTE", 0); break; } else if(strcmp(line, "@@@") == 0) { Xorriso_msgs_submit(xorriso, 0, "-setfattr_list aborted by input line '@@@'", 0, "WARNING", 0); ret= 1; goto ex; } mem_used+= strlen(line) + 1; if(mem_used > (size_t) xorriso->temp_mem_limit) { Sfile_scale((double) xorriso->temp_mem_limit, limit_text,5,1e4,1); sprintf(xorriso->info_text, "-setfattr_list: List entry for a single file exceeds -temp_mem_limit %s", limit_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } /* Register attr pair */ ept= strchr(line, '='); if(ept == NULL) { sprintf(xorriso->info_text, "-setfattr_list: "); Text_shellsafe(path, xorriso->info_text, 1); sprintf(xorriso->info_text + strlen(xorriso->info_text), " : Line %.f : No separator '=' found", (double) linecount); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); continue; } valuept= ept + 1; v_len= strlen(valuept); for(ept= valuept + v_len - 1; ept > valuept; ept--) if(isspace(*ept)) *ept= 0; else break; v_len= strlen(valuept); if(v_len < 2 || *valuept != '"' || *(valuept + v_len -1) != '"') { sprintf(xorriso->info_text, "-setfattr_list: "); Text_shellsafe(path, xorriso->info_text, 1); sprintf(xorriso->info_text + strlen(xorriso->info_text), " : Line %.f : Value not enclosed in quotes", (double) linecount); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); continue; } ret= Xorriso_lst_new(&lst_curr, line, lst_curr, 0); if(ret <= 0) goto out_of_mem; if(lst_start == NULL) lst_start= lst_curr; num_attr++; } if(file_path[0]) { /* Commit last list */ ret= Xorriso_perform_attr_from_list(xorriso, file_path, lst_start, 0); if(ret<=0) goto ex; } else { sprintf(xorriso->info_text, "-setfattr_list: Unexpected end of file "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } ret= 1; ex:; if(fp != NULL && fp != stdin) fclose(fp); Xorriso_lst_destroy_all(&lst_start, 0); Xorriso_free_meM(line); Xorriso_free_meM(file_path); if(ret <= 0) { sprintf(xorriso->info_text, "-setfattr_list "); Text_shellsafe(path, xorriso->info_text, 1); sprintf(xorriso->info_text + strlen(xorriso->info_text), " aborted in line %.f\n", (double) linecount); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } return(ret); out_of_mem:; Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } /* Options -set_filter , -set_filter_r , -show_stream , -show_stream_r */ /* @param flag bit0=recursive -set_filter_r bit1= do not reset pacifier, no final pacifier message bit2= -show_stream rather than -set_filter */ int Xorriso_option_set_filter(struct XorrisO *xorriso, char *name, int argc, char **argv, int *idx, int flag) { int i, ret, was_failure= 0, end_idx, fret; int optc= 0; char **optv= NULL; struct FindjoB *job= NULL; struct stat dir_stbuf; char *cmd= "-set_filter"; switch(flag & 5) { case 0: cmd= "-set_filter"; break; case 1: cmd= "-set_filter_r"; break; case 4: cmd= "-show_stream"; break; case 5: cmd= "-show_stream_r"; } ret= Xorriso_opt_args(xorriso, cmd, argc, argv, *idx, &end_idx, &optc, &optv, 0); if(ret <= 0) goto ex; if(!(flag&2)) Xorriso_pacifier_reset(xorriso, 0); for(i= 0; i<optc; i++) { if(flag&1) { ret= Findjob_new(&job, optv[i], 0); if(ret<=0) { Xorriso_no_findjob(xorriso, cmd, 0); {ret= -1; goto ex;} } Findjob_set_action_target(job, ((flag & 4) ? 29 : 28), name, 0); Findjob_set_file_type(job, 'f', 0); ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, optv[i], &dir_stbuf, 0, 0); Findjob_destroy(&job, 0); } else { ret= 1; if(flag & 4) ret= Xorriso_show_stream(xorriso, NULL, optv[i], 0); else ret= Xorriso_set_filter(xorriso, NULL, optv[i], name, 0); } if(ret>0 && !xorriso->request_to_abort) continue; /* regular bottom of loop */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret>=0) continue; ret= 0; goto ex; } if(!(flag&2)) Xorriso_pacifier_callback(xorriso, "file filters processed", xorriso->pacifier_count, 0, "", 1); ret= 1; ex:; (*idx)= end_idx; Xorriso_opt_args(xorriso, cmd, argc, argv, *idx, &end_idx, &optc, &optv, 256); Findjob_destroy(&job, 0); if(ret<=0) return(ret); return(!was_failure); } /* Option -sh_style_result */ int Xorriso_option_sh_style_result(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "off") == 0) { xorriso->sh_style_result= 0; } else if(strcmp(mode, "on") == 0) { xorriso->sh_style_result= 1; } else { sprintf(xorriso->info_text, "-sh_style_result: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -signal_handling */ /* @param flag bit0= prescan mode: do not yet install the eventual handler else: when switching from other modes to "off": activate mode "sig_dfl" */ int Xorriso_option_signal_handling(struct XorrisO *xorriso, char *mode, int flag) { int ret, behavior; if (strcmp(mode, "off") == 0) { behavior= Xorriso__get_signal_behavior(0); if(flag & 1) { behavior= 0; } else if(behavior != 0) { sprintf(xorriso->info_text, "Signal handling mode \"off\" comes too late. Defaulted to \"sig_dfl\"\n"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); behavior= 2; } } else if(strcmp(mode, "libburn") == 0 || strcmp(mode, "on") == 0) { behavior= 1; } else if (strcmp(mode, "sig_dfl") == 0) { behavior= 2; } else if (strcmp(mode, "sig_ign") == 0) { behavior= 3; } else { sprintf(xorriso->info_text, "-signal_handling: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); Xorriso_msgs_submit(xorriso, 0, "Use one of: \"off\",\"on\",\"sig_dfl\",\"sig_ign\"", 0, "HINT", 0); return(0); } Xorriso__preset_signal_behavior(behavior, 0); if(flag & 1) return(1); ret= Xorriso_set_signal_handling(xorriso, 0); return(ret); } /* Option -sleep */ int Xorriso_option_sleep(struct XorrisO *xorriso, char *duration, int flag) { double dur= 0.0, start_time, end_time, todo, granularity= 0.01; unsigned long usleep_time; sscanf(duration, "%lf", &dur); start_time= Sfile_microtime(0); end_time= start_time + dur; Ftimetxt(time(NULL), xorriso->info_text, 6); sprintf(xorriso->info_text + strlen(xorriso->info_text), " : Will sleep for %f seconds", dur); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0); while(1) { todo= end_time - Sfile_microtime(0); if(todo <= 0) usleep_time= 0; else if(todo > granularity) usleep_time= granularity * 1.0e6; else usleep_time= todo * 1.0e6; if(usleep_time == 0) break; usleep(usleep_time); } sprintf(xorriso->info_text, "Slept for %f seconds", Sfile_microtime(0) - start_time); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); return(1); } /* Commands -speed , -read_speed */ /* @param flag bit0= -read_speed rather than -speed */ int Xorriso_option_speed(struct XorrisO *xorriso, char *speed_in, int flag) { int is_cd= 1, unit_found= 0, ret, profile_number, intspeed= 1, for_force= 0; double num= -2.0; char *cpt, profile_name[80], *speed; speed= speed_in; if(strncmp(speed, "soft_corr:", 10) == 0 && (flag & 1)) { sscanf(speed + 10, "%lf", &num); if(num > 1.0e9 || num < 0.0) { sprintf(xorriso->info_text, "-read_speed soft_corr: Value too small or too large (0 to 1e9): '%s'", speed + 10); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } xorriso->read_speed_corr= num; return(1); } if(strncmp(speed, "soft_force:", 11) == 0 && (flag & 1)) { for_force= 1; speed+= 11; } if(speed[0] == 0 || strcmp(speed, "any") == 0 || strcmp(speed, "max") == 0) { intspeed= 0; } else if(strcmp(speed, "min") == 0) { intspeed= -1; } else if(strcmp(speed, "none") == 0) { intspeed= -2; } else { sscanf(speed,"%lf",&num); if(num <= 0) intspeed= num; } if(intspeed <= 0) goto set_speed_and_exit; for(cpt= speed+strlen(speed)-1; cpt>=speed; cpt--) if(isdigit(*cpt) || *cpt=='.') break; cpt++; if(*cpt=='k' || *cpt=='K') { /* is merchand kilobyte, stays merchand kilobyte */ unit_found= 1; } else if(*cpt=='m' || *cpt=='M') { num*= 1000; unit_found= 1; } else if(*cpt=='x' || *cpt=='X') cpt++; if (unit_found) { ; } else if(*cpt=='c' || *cpt=='C') { cd_speed:; num*= 176.4; } else if(*cpt=='d' || *cpt=='D') { dvd_speed:; num*= 1385; } else if(*cpt=='b' || *cpt=='B') { bd_speed:; num*= 4495.625; } else { ret= Xorriso_get_profile(xorriso, &profile_number, profile_name, 2 * !(flag & 1)); is_cd= (ret==2); if(is_cd) goto cd_speed; else if (ret == 3) goto bd_speed; else goto dvd_speed; } if(num> 2.0e9) { sprintf(xorriso->info_text, "%s: Value too large or not recognizable: '%s'", flag & 1 ? "-read_speed" : "-speed", speed); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } intspeed= num; if(intspeed < num) intspeed++; set_speed_and_exit:; if(flag & 1) { if(for_force) xorriso->read_speed_force= intspeed; else xorriso->read_speed= intspeed; } else { xorriso->write_speed= intspeed; } return(1); } /* Option -split_size */ int Xorriso_option_split_size(struct XorrisO *xorriso, char *size, int flag) { double num; num= Scanf_io_size(size, 0); if(num > xorriso->file_size_limit && xorriso->file_size_limit > 0) { sprintf(xorriso->info_text, "-split_size: too large %.f (allowed: %.f)", num, (double) xorriso->file_size_limit); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } else if(num < 0) num= 0.0; xorriso->split_size= num; return(1); } /* Option -status */ int Xorriso_option_status(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode,"short")==0) Xorriso_status(xorriso,NULL,NULL,1); else if(strcmp(mode,"long")==0) Xorriso_status(xorriso,NULL,NULL,0); else if(strcmp(mode,"long_history")==0) Xorriso_status(xorriso,NULL,NULL,8); else if(mode[0]=='-') Xorriso_status(xorriso,mode,NULL,8); else Xorriso_status(xorriso,NULL,NULL,1); return(1); } /* Option -status_history_max */ int Xorriso_option_status_history_max(struct XorrisO *xorriso, int num, int flag) { if(num>=0 && num<1000000) xorriso->status_history_max= num; return(1); } /* Option -stdio_sync "on"|"off"|"end"|size */ int Xorriso_option_stdio_sync(struct XorrisO *xorriso, char *rhythm, int flag) { double num; if(strcmp(rhythm, "default") == 0 || strcmp(rhythm, "on") == 0) { num= 0; } else if(strcmp(rhythm, "off") == 0) { num= -1; } else if(strcmp(rhythm, "end") == 0) { num= 1; } else { num = Scanf_io_size(rhythm, 0) / 2048; if(num < 32 || num > 512 * 1024) { sprintf(xorriso->info_text, "-stdio_sync : Bad size. Use: 64k to 1g, \"on\", \"off\", \"end\""); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } } xorriso->stdio_sync= num; xorriso->stdio_sync_is_default= 0; return(1); } /* Option -stream_recording */ int Xorriso_option_stream_recording(struct XorrisO *xorriso, char *mode, int flag) { double num; if(strcmp(mode,"on")==0 || mode[0]==0) xorriso->do_stream_recording= 32; else if(strcmp(mode,"full")==0) xorriso->do_stream_recording= 1; else if(strcmp(mode,"data")==0) xorriso->do_stream_recording= 2; else if(mode[0] >= '0' && mode[0] <= '9') { num= Scanf_io_size(mode, 0); num/= 2048.0; if(num >= 16 && num <= 0x7FFFFFFF) xorriso->do_stream_recording= num; else xorriso->do_stream_recording= 0; } else xorriso->do_stream_recording= 0; return(1); } /* Option -system_id */ int Xorriso_option_system_id(struct XorrisO *xorriso, char *name, int flag) { if(Xorriso_check_name_len(xorriso, name, (int) sizeof(xorriso->system_id), "-system_id", 0) <= 0) return(0); strcpy(xorriso->system_id, name); Xorriso_set_change_pending(xorriso, 1); return(1); } /* Option -tell_media_space */ int Xorriso_option_tell_media_space(struct XorrisO *xorriso, int flag) { int ret; off_t free_space= 0, media_space= 0; ret= Xorriso_tell_media_space(xorriso, &media_space, &free_space, 0); if(ret<=0) { sprintf(xorriso->info_text, "Cannot -tell_media_space"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } if(free_space<0) { sprintf(xorriso->info_text, "Pending image size larger than free space on medium"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } sprintf(xorriso->result_line, "Media space : %.fs\n", (double) media_space); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "After commit : %.fs\n", (double) free_space); Xorriso_result(xorriso, 0); return(1); } /* Option -temp_mem_limit */ int Xorriso_option_temp_mem_limit(struct XorrisO *xorriso, char *size, int flag) { double num; num= Scanf_io_size(size, 0); if(num < 64.0 * 1024.0 || num > 1024.0 * 1024.0 * 1024.0) { sprintf(xorriso->info_text, "-temp_mem_limit: wrong size %.f (allowed: %.f - %.f)", num, 64.0 * 1024.0, 1024.0 * 1024.0 * 1024.0); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } xorriso->temp_mem_limit= num; return(1); } /* Option -toc */ /* @param flag bit0= short report form as with -dev, no table-of-content */ int Xorriso_option_toc(struct XorrisO *xorriso, int flag) { int ret, in_ret= 1000; if(strcmp(xorriso->indev,xorriso->outdev)==0) ret= Xorriso_toc(xorriso, 0); else { if(xorriso->indev[0]!=0) in_ret= Xorriso_toc(xorriso, 0); if(xorriso->indev[0]!=0 && xorriso->outdev[0]!=0) { strcpy(xorriso->result_line, "-------------: ---------------------------------------------------------------\n"); Xorriso_result(xorriso,0); } ret= 1; if(xorriso->outdev[0]!=0) ret= Xorriso_toc(xorriso, 2 | (flag & 1)); if(in_ret<ret) ret= in_ret; } return(ret); } /* Command -toc_info_type */ int Xorriso_option_toc_info_type(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "volid") == 0 || mode[0] == 0) { xorriso->toc_info_type= 1; } else if(strcmp(mode, "creation_time") == 0 || strcmp(mode, "ctime") == 0 || strcmp(mode, "creation_time_gmt") == 0 || strcmp(mode, "ctime_gmt") == 0) { xorriso->toc_info_type= 3; xorriso->toc_time_form= (strstr(mode, "_gmt") == NULL); } else if(strcmp(mode, "modification_time") == 0 || strcmp(mode, "mtime") == 0 || strcmp(mode, "modification_time_gmt") == 0 || strcmp(mode, "mtime_gmt") == 0) { xorriso->toc_info_type= 4; xorriso->toc_time_form= (strstr(mode, "_gmt") == NULL); } else { sprintf(xorriso->info_text, "-toc_info_type: unknown type '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -toc_of */ int Xorriso_option_toc_of(struct XorrisO *xorriso, char *which, int flag) { int ret= 0, toc_flag= 0; if(strstr(which, ":short") != NULL) toc_flag|= 1; if(strncmp(which, "in", 2) == 0) { if(xorriso->indev[0] == 0) { Xorriso_msgs_submit(xorriso, 0, "-toc_of 'in' : No input drive acquired", 0, "NOTE", 0); return(2); } ret= Xorriso_toc(xorriso, toc_flag | 0); } else if(strncmp(which, "out", 3) == 0) { if(xorriso->outdev[0] == 0) { Xorriso_msgs_submit(xorriso, 0, "-toc_of 'out' : No output drive acquired", 0, "NOTE", 0); return(2); } ret= Xorriso_toc(xorriso, toc_flag | 2); } else if(strncmp(which, "all", 3) == 0) { if(xorriso->indev[0] == 0 && xorriso->outdev[0] == 0) { Xorriso_msgs_submit(xorriso, 0, "-toc_of 'all' : No drive acquired", 0, "NOTE", 0); return(2); } ret= Xorriso_option_toc(xorriso, toc_flag | 0); } else { sprintf(xorriso->info_text, "-toc_of: Unknown drive code "); Text_shellsafe(which, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); ret= 0; } return(ret); } /* Command -truncate_overwritable */ int Xorriso_option_truncate_overwritable(struct XorrisO *xorriso, char *adr_mode, char *adr_value, char *adjust, int flag) { int ret; ret= Xorriso_truncate_overwritable(xorriso, adr_mode, adr_value, adjust, 0); return(ret); } /* Option -uid */ int Xorriso_option_uid(struct XorrisO *xorriso, char *uid, int flag) { int ret; xorriso->do_global_uid= 0; if(uid[0]==0 || strcmp(uid,"-")==0) return(1); ret= Xorriso_convert_uidstring(xorriso, uid, &(xorriso->global_uid), 0); if(ret>0) xorriso->do_global_uid= 1; return(ret); } /* Option -unregister_filter */ int Xorriso_option_unregister_filter(struct XorrisO *xorriso, char *name, int flag) { int ret; ret= Xorriso_external_filter(xorriso, name, "", "", 0, NULL, 1); return(ret); } /* Options -update and -update_r @param flag bit0= issue start and summary message bit1= do not reset pacifier, no final pacifier message bit2= do not issue pacifier messages at all bit3= recursive: -update_r bit4= do not establish and dispose xorriso->di_array bit5= do not delete files which are not found under disk_path, but rather mark visited files and mark files which were found. */ int Xorriso_option_update(struct XorrisO *xorriso, char *disk_path, char *iso_path, int flag) { int ret, mem_pci, zero= 0, result, uret, follow_links, do_register= 0; int not_in_iso= 0, not_on_disk= 0; double mem_lut= 0.0, start_time; char *ipth, *argv[6]; char *eff_origin= NULL, *eff_dest= NULL; struct stat stbuf; Xorriso_alloc_meM(eff_origin, char, SfileadrL); Xorriso_alloc_meM(eff_dest, char, SfileadrL); start_time= Sfile_microtime(0); ipth= iso_path; if(ipth[0]==0) ipth= disk_path; if(disk_path[0]==0) { sprintf(xorriso->info_text, "-update: Empty disk_path given"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 1); {ret= 0; goto ex;} } ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, disk_path, eff_origin, 2|4|8); if(ret<=0) goto ex; ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, ipth, eff_dest, 2|8); if(ret<=0) goto ex; if(!(flag&2)) { Xorriso_pacifier_reset(xorriso, 0); mem_lut= xorriso->last_update_time; } mem_pci= xorriso->pacifier_interval; xorriso->pacifier_interval= 5.0; if(flag&1) { sprintf(xorriso->info_text, "Updating "); Text_shellsafe(eff_origin, xorriso->info_text, 1); strcat(xorriso->info_text, " to "); Text_shellsafe(eff_dest, xorriso->info_text, 1 | 2); strcat(xorriso->info_text, "\n"); Xorriso_info(xorriso,0); } if(xorriso->disk_excl_mode&8) ret= Xorriso_path_is_excluded(xorriso, eff_origin, 1); else ret= 0; if(ret!=0) goto report_outcome; if(!(xorriso->ino_behavior & 2)) { if(!(xorriso->di_array != NULL || (flag & 16))) { /* Create all-image node array sorted by isofs.di */ ret= Xorriso_make_di_array(xorriso, 0); if(ret <= 0) goto ex; } if(xorriso->di_array != NULL) { do_register= 1; if(!(flag & 8)) { /* If directory with -update : do not register di_*_paths */ ret= lstat(eff_origin, &stbuf); if(ret != -1) if(S_ISDIR(stbuf.st_mode)) do_register= 0; } } } if(flag&8) { xorriso->find_compare_result= 1; ret= Xorriso_iso_lstat(xorriso, eff_dest, &stbuf, 0); if(ret >= 0) { argv[0]= eff_dest; argv[1]= "-exec"; if(flag & 32) argv[2]= "update_merge"; else argv[2]= "update"; argv[3]= eff_origin; zero= 0; ret= Xorriso_option_find(xorriso, 4, argv, &zero, 2 | (8 * !((xorriso->do_aaip & 96) == 96))); /* -findi */ } else if(ret==-2) { /* severe error (e.g. lack of image) */ ret= -1; goto report_outcome; } else { not_in_iso= 1; ret= 1; } if(ret>0) { ret= lstat(eff_origin, &stbuf); if(ret != -1) { argv[0]= eff_origin; argv[1]= "-exec"; argv[2]= "add_missing"; argv[3]= eff_dest; zero= 0; ret= Xorriso_option_find(xorriso, 4, argv, &zero, 1|2); /* -findx */ if(ret>0 && (!xorriso->do_follow_mount) && !(flag & 32)) { /* >>> ??? what about mount points with (flag & 32) ? empty_iso_dir shall delete those which already existed and are freshly excluded. (E.g. by mounting at a non-empty directory, or by new follow rules.) This deletion does not match the idea of merging. For determining the foreign files in a directory which is target of a mount point, one would have to enter that mount point directory. Somewhat contrary to do-not-follow. */ argv[0]= eff_origin; argv[1]= "-type"; argv[2]= "m"; argv[3]= "-exec"; argv[4]= "empty_iso_dir"; argv[5]= eff_dest; zero= 0; ret= Xorriso_option_find(xorriso, 6, argv, &zero, 1|2); /* -findx */ } if(ret>0) ret= xorriso->find_compare_result; else ret= -1; } else { ret= xorriso->find_compare_result; not_on_disk= 1; } } else ret= -1; if(not_on_disk && not_in_iso) { sprintf(xorriso->info_text, "Missing on disk and in ISO: disk_path "); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 1); ret= -1; } } else { if(flag & 32) xorriso->update_flags|= 1; /* Enter update_merge mode for node adding */ /* compare ctime too, no filename reporting, eventually silent */ follow_links= (xorriso->do_follow_links || xorriso->do_follow_param) <<28; ret= Xorriso_compare_2_files(xorriso, eff_origin, eff_dest, "", &result, 2 | follow_links | ((flag&4)<<27) | (3<<30)); if(ret == 0 || (ret > 0 && (flag & 32))) { if(ret > 0) result= 0; uret= Xorriso_update_interpreter(xorriso, NULL, NULL, result, eff_origin, eff_dest, (!!(flag & 32)) << 1); if(uret<=0) ret= -1; if(uret==3) ret= -1; } } xorriso->pacifier_interval= mem_pci; if(mem_lut!=xorriso->last_update_time && !(flag & (2 | 4))) Xorriso_pacifier_callback(xorriso, "content bytes read", xorriso->pacifier_count, 0, "", 1 | 8 | 32); report_outcome:; if(ret>0) { sprintf(xorriso->info_text, "No file object needed update."); do_register= 0; } else if(ret==0) { sprintf(xorriso->info_text, "Differences detected and updated."); } else { sprintf(xorriso->info_text, "Not ok. Comparison or update failed due to error."); do_register= 0; } if(do_register) { ret= Xorriso_iso_lstat(xorriso, eff_dest, &stbuf, 0); if(ret < 0) do_register= 0; } if(do_register) { ret= Xorriso_lst_new(&(xorriso->di_disk_paths), eff_origin, xorriso->di_disk_paths, 1); if(ret <= 0) goto ex; ret= Xorriso_lst_new(&(xorriso->di_iso_paths), eff_dest, xorriso->di_iso_paths, 1); if(ret <= 0) goto ex; } sprintf(xorriso->info_text+strlen(xorriso->info_text), " (runtime %.1f s)\n", Sfile_microtime(0)-start_time); if(flag&1) Xorriso_info(xorriso,0); ex:; Xorriso_free_meM(eff_origin); Xorriso_free_meM(eff_dest); if(ret < 0) return(ret); return(1); } /* Command -use_immed_bit */ int Xorriso_option_use_immed_bit(struct XorrisO *xorriso, char *mode, int flag) { int ret; if(strncmp(mode, "default", 7) == 0 || mode[0] == 0) { xorriso->use_immed_bit= 0; } else if(strcmp(mode, "on")==0) { xorriso->use_immed_bit= 1; } else if(strcmp(mode, "off")==0) { xorriso->use_immed_bit= -1; } else { sprintf(xorriso->info_text, "-use_immed_bit: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= Xorriso_use_immed_bit(xorriso, 0); return(ret); } /* Option -use_readline */ int Xorriso_option_use_readline(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode,"off")==0) xorriso->use_stdin= 1; else xorriso->use_stdin= 0; return(1); } /* Option -version */ int Xorriso_option_version(struct XorrisO *xorriso, int flag) { const char *license_text; sprintf(xorriso->result_line, "%sxorriso %d.%d.%d%s\n", #ifdef Xorriso_GNU_xorrisO "GNU ", #else "", #endif /* ! Xorriso_GNU_xorrisO */ Xorriso_header_version_majoR, Xorriso_header_version_minoR, Xorriso_header_version_micrO, Xorriso_program_patch_leveL); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "ISO 9660 Rock Ridge filesystem manipulator and CD/DVD/BD burn program\n"); sprintf(xorriso->result_line+strlen(xorriso->result_line), "Copyright (C) 2023, Thomas Schmitt <scdbackup@gmx.net>, libburnia project.\n"); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "xorriso version : %d.%d.%d%s\n", Xorriso_header_version_majoR, Xorriso_header_version_minoR, Xorriso_header_version_micrO, Xorriso_program_patch_leveL); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "Version timestamp : %s\n",Xorriso_timestamP); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "Build timestamp : %s\n",Xorriso_build_timestamP); Xorriso_result(xorriso, 0); Xorriso_report_lib_versions(xorriso, 0); #ifdef Xorriso_GNU_xorrisO license_text= "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>."; sprintf(xorriso->result_line, "%s\n", license_text); Xorriso_result(xorriso, 0); sprintf(xorriso->result_line, "This is free software: you are free to change and redistribute it.\n"); Xorriso_result(xorriso, 0); #else license_text= "Provided under GNU GPL version 2 or later."; #ifdef Xorriso_with_readlinE { const char *readline_license; readline_license= Xorriso__readline_license(0); if(strcmp(readline_license, "GPLv3+") == 0) license_text= "Provided under GNU GPL version 3 or later, due to libreadline license."; } #endif /* Xorriso_with_readlinE */ sprintf(xorriso->result_line, "%s\n", license_text); Xorriso_result(xorriso, 0); #endif /* ! Xorriso_GNU_xorrisO */ sprintf(xorriso->result_line, "There is NO WARRANTY, to the extent permitted by law.\n"); Xorriso_result(xorriso, 0); return(1); } /* Option -volid */ /* @param flag bit0= do not warn of problematic volid */ int Xorriso_option_volid(struct XorrisO *xorriso, char *volid, int flag) { int warn_shell= 0, warn_ecma= 0, i, ret; char *result= NULL; size_t result_len= 0; static char shell_chars[]= { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-+=:.,~@"}; static char ecma_chars[]= {"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"}; for(i=0; volid[i]!=0; i++) { if(strchr(shell_chars, volid[i])==NULL) warn_shell= 1; if(strchr(ecma_chars, volid[i])==NULL) warn_ecma= 1; } if(i>32) { sprintf(xorriso->info_text, "-volid: Text too long (%d > 32)", i); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(warn_shell && !(flag & 1)) { sprintf(xorriso->info_text, "-volid text problematic as automatic mount point name"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } if(xorriso->do_joliet && strlen(volid)>16 && !(flag & 1)) { ret= Xorriso_conv_name_chars(xorriso, volid, &result, &result_len, 2, 0); if(result != NULL) free(result); /* just want the result_len */ xorriso->info_text[0]= 0; if(ret <= 0) { sprintf(xorriso->info_text, "Cannot determine length of -volid text in Joliet character set"); } else if(result_len > 32) { sprintf(xorriso->info_text, "-volid text is too long for Joliet (%d > 16)", (int) (result_len + 1) / 2); } if(xorriso->info_text[0]) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } if(warn_ecma && !(flag & 1)) { sprintf(xorriso->info_text, "-volid text does not comply to ISO 9660 / ECMA 119 rules"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } strcpy(xorriso->volid, volid); ret= Xorriso_set_volid(xorriso, volid, 0); if(ret<=0) return(ret); xorriso->volid_default= (strcmp(xorriso->volid, "ISOIMAGE")==0 || xorriso->volid[0]==0); return(1); } /* Option -volset_id */ int Xorriso_option_volset_id(struct XorrisO *xorriso, char *name, int flag) { if(Xorriso_check_name_len(xorriso, name, (int) sizeof(xorriso->volset_id), "-volset_id", 0) <= 0) return(0); strcpy(xorriso->volset_id, name); Xorriso_set_change_pending(xorriso, 1); return(1); } /* Option -volume_date */ int Xorriso_option_volume_date(struct XorrisO *xorriso, char *time_type, char *timestring, int flag) { int ret, t_type= 0; time_t t; struct tm erg; if(timestring[0] == 0 || strcmp(timestring, "default") == 0 || strcmp(timestring, "overridden") == 0 ){ t= 0; } else if(strcmp(time_type, "uuid") == 0 || (strcmp(time_type, "all_file_dates") == 0 && strcmp(timestring, "set_to_mtime") == 0)) { t= time(NULL); /* Just to have some that is not 0 */ } else { ret= Xorriso_convert_datestring(xorriso, "-volume_date", "m", timestring, &t_type, &t, 0); if(ret<=0) goto ex; } if(strcmp(time_type, "uuid") == 0) { if(t == 0) { xorriso->vol_uuid[0]= 0; ret= 1; goto ex; } ret= Decode_ecma119_format(&erg, timestring, 0); if(ret <= 0 || strlen(timestring) != 16) { sprintf(xorriso->info_text, "-volume_date uuid : Not an ECMA-119 time string. (16 decimal digits, range 1970... to 2999...)"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } strcpy(xorriso->vol_uuid, timestring); if(erg.tm_year < 138) { sprintf(xorriso->info_text, "Understanding ECMA-119 timestring '%s' as: %s", timestring, asctime(&erg)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } } else if(strcmp(time_type, "all_file_dates") == 0) { if(t == 0) { xorriso->all_file_dates[0]= 0; ret= 1; goto ex; } strncpy(xorriso->all_file_dates, timestring, sizeof(xorriso->all_file_dates) - 1); xorriso->all_file_dates[sizeof(xorriso->all_file_dates) - 1]= 0; } else if(strcmp(time_type, "c") == 0) { xorriso->vol_creation_time= t; } else if(strcmp(time_type, "m") == 0) { xorriso->vol_modification_time= t; } else if(strcmp(time_type, "x") == 0) { xorriso->vol_expiration_time= t; } else if(strcmp(time_type, "f") == 0) { xorriso->vol_effective_time= t; } else { /* >>> unknown time type */; ret= 0; goto ex; } ret= 1; ex:; return(ret); } /* Command -write_type */ int Xorriso_option_write_type(struct XorrisO *xorriso, char *mode, int flag) { if(strcmp(mode, "auto") == 0) xorriso->do_tao = 0; else if(strcmp(mode, "tao") == 0 || strcmp(mode, "TAO") == 0) xorriso->do_tao = 1; else if(strcmp(mode, "sao") == 0 || strcmp(mode, "SAO") == 0 || strcmp(mode, "dao") == 0 || strcmp(mode, "DAO") == 0 || strcmp(mode, "sao/dao") == 0 || strcmp(mode, "SAO/DAO") == 0 ) xorriso->do_tao = -1; else { sprintf(xorriso->info_text, "-write_type: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* Option -xattr "on"|"any"|"off" */ int Xorriso_option_xattr(struct XorrisO *xorriso, char *mode, int flag) { int ret; if(strcmp(mode, "off") == 0) { xorriso->do_aaip&= ~(4 | 8 | 1024); } else if(strcmp(mode, "on") == 0 || strcmp(mode, "user") == 0) { xorriso->do_aaip&= ~1024; xorriso->do_aaip|= (4 | 8); } else if(strcmp(mode, "any") == 0) { xorriso->do_aaip|= (4 | 8 | 1024); } else { sprintf(xorriso->info_text, "-xattr: unknown mode '%s'", mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } ret= Xorriso_set_ignore_aclea(xorriso, 0); if(ret <= 0) return(ret); return(1); } /* Option -zisofs */ int Xorriso_option_zisofs(struct XorrisO *xorriso, char *mode, int flag) { int was_level, was_blocksize, was_v2_enabled, was_blocksize_v2; int was_zisofs_susp_z2; uint64_t was_max_total_blocks, was_max_file_blocks, was_block_number_target; int ret, l, i, sticky, set_isofs_params= 0; double num, was_bpt_discard_free_ratio; char *cpt, *npt; was_level= xorriso->zlib_level; was_blocksize= xorriso->zisofs_block_size; was_v2_enabled= xorriso->zisofs_v2_enabled; was_max_total_blocks= xorriso->zisofs_max_total_blocks; was_max_file_blocks= xorriso->zisofs_max_file_blocks; was_blocksize_v2= xorriso->zisofs_v2_block_size; was_block_number_target= xorriso->zisofs_block_number_target; was_bpt_discard_free_ratio= xorriso->zisofs_bpt_discard_free_ratio; was_zisofs_susp_z2= xorriso->zisofs_susp_z2; npt= cpt= mode; for(cpt= mode; npt!=NULL; cpt= npt+1) { npt= strchr(cpt,':'); if(npt==NULL) l= strlen(cpt); else l= npt-cpt; if(l==0) goto unknown_mode; if(strncmp(cpt, "level=", 6) == 0) { sscanf(cpt + 6, "%lf", &num); if(num < 0 || num > 9) { sprintf(xorriso->info_text, "-zisofs: Wrong zlib compression level '%s' (allowed 0...9)", cpt + 6); goto sorry_ex; } xorriso->zlib_level= num; } else if(strncmp(cpt, "ziso_used=", 10) == 0 || strncmp(cpt, "osiz_used=", 10) == 0 || strncmp(cpt, "gzip_used=", 10) == 0 || strncmp(cpt, "gunzip_used=", 12) == 0 || strncmp(cpt, "bpt_ratio_used=", 15) == 0 || strncmp(cpt, "bpt_used=", 9) == 0) { /* (ignored info from -status) */; } else if(strncmp(cpt, "block_size=", 11)==0) { Xorriso__parse_size_param(cpt, 11, l, &num); if (num != (1 << 15) && num != (1 << 16) && num != (1 << 17)) { sprintf(xorriso->info_text, "-zisofs: Unsupported block size (allowed 32k, 64k, 128k)"); goto sorry_ex; } xorriso->zisofs_block_size= num; } else if(strncmp(cpt, "by_magic=", 9)==0) { if(strncmp(cpt + 9, "on", l - 9) == 0) { xorriso->zisofs_by_magic= 1; } else if(strncmp(cpt + 9, "v2", l - 9) == 0) { xorriso->zisofs_by_magic= 2; } else if(strncmp(cpt + 9, "off", l - 9) == 0) { xorriso->zisofs_by_magic= 0; } else { sprintf(xorriso->info_text, "-zisofs: Unrecognized by_magic mode (allowed: on, off, v2)"); goto sorry_ex; } } else if(strncmp(cpt, "version_2=", 10) == 0) { if(strncmp(cpt + 10, "off", l - 10) == 0) { xorriso->zisofs_v2_enabled= 0; } else if(strncmp(cpt + 10, "as_needed", l - 10) == 0) { xorriso->zisofs_v2_enabled= 1; } else if(strncmp(cpt + 10, "on", l - 10) == 0) { xorriso->zisofs_v2_enabled= 2; } else { sprintf(xorriso->info_text, "-zisofs: Unrecognized version_2 mode (allowed: on, off, as_needed)"); goto sorry_ex; } } else if(strncmp(cpt, "max_bpt=", 8) == 0) { Xorriso__parse_size_param(cpt, 8, l, &num); if(num < 1024.0 || num > 128.0 * 1024.0 * 1024.0 * 1024.0) { sprintf(xorriso->info_text, "-zisofs: Unsupported block pointer pool size (allowed: 1k to 128g)"); goto sorry_ex; } sticky= 0; if(xorriso->zisofs_max_total_blocks == xorriso->zisofs_max_file_blocks) sticky= 1; xorriso->zisofs_max_total_blocks= num / 8.0; if(xorriso->zisofs_max_total_blocks < xorriso->zisofs_max_file_blocks || sticky) xorriso->zisofs_max_file_blocks= xorriso->zisofs_max_total_blocks; } else if(strncmp(cpt, "max_bpt_f=", 10) == 0) { Xorriso__parse_size_param(cpt, 10, l, &num); if(num < 1024.0 || num > 128.0 * 1024.0 * 1024.0 * 1024.0) { sprintf(xorriso->info_text, "-zisofs: Unsupported block pointer list size (allowed: 1k to 128g)"); goto sorry_ex; } xorriso->zisofs_max_file_blocks= num / 8.0; if(xorriso->zisofs_max_file_blocks > xorriso->zisofs_max_total_blocks) xorriso->zisofs_max_total_blocks= xorriso->zisofs_max_file_blocks; } else if(strncmp(cpt, "block_size_v2=", 14) == 0) { Xorriso__parse_size_param(cpt, 14, l, &num); for(i= 15 ; i <= 20; i++) if(num == (1 << i)) break; if(i > 20) { sprintf(xorriso->info_text, "-zisofs: Unsupported block size (allowed 32k, 64k, 128k, ... 1024k)"); goto sorry_ex; } xorriso->zisofs_v2_block_size= num; } else if(strncmp(cpt, "bpt_target=", 11) == 0) { Xorriso__parse_size_param(cpt, 11, l, &num); xorriso->zisofs_block_number_target= num; } else if(strncmp(cpt, "bpt_free_ratio=", 15) == 0) { Xorriso__parse_size_param(cpt, 15, l, &num); /* 0 means to libisofs "do not change" */ if(num == 0.0) num= -1.0; if(num != -1.0 && (num <= 0.0 || num > 1.0)) { sprintf(xorriso->info_text, "-zisofs: Unsupported free blockpointer ratio (allowed -1 or 0.0 to 1.0)"); goto sorry_ex; } xorriso->zisofs_bpt_discard_free_ratio = num; } else if(strncmp(cpt, "susp_z2=", 8) == 0) { if(strncmp(cpt + 8, "off", l - 8) == 0) { xorriso->zisofs_susp_z2= 0; } else if(strncmp(cpt + 8, "on", l - 8) == 0) { xorriso->zisofs_susp_z2= 1; } else { sprintf(xorriso->info_text, "-zisofs: Unrecognized susp_z2 mode (allowed: on, off)"); goto sorry_ex; } Xorriso_set_zisofs_params(xorriso, 2); } else if(strncmp(cpt, "default", l)==0) { xorriso->zlib_level= xorriso->zlib_level_default; xorriso->zisofs_block_size= xorriso->zisofs_block_size_default; xorriso->zisofs_by_magic= 0; xorriso->zisofs_v2_enabled= 0; xorriso->zisofs_max_total_blocks= xorriso->zisofs_max_total_blocks_default; xorriso->zisofs_max_file_blocks= xorriso->zisofs_max_file_blocks_default; xorriso->zisofs_v2_block_size= xorriso->zisofs_v2_block_size_default; xorriso->zisofs_block_number_target= -1; xorriso->zisofs_bpt_discard_free_ratio= xorriso->zisofs_bpt_discard_free_ratio_default; xorriso->zisofs_susp_z2= xorriso->zisofs_susp_z2_default; Xorriso_set_zisofs_params(xorriso, 2); } else { unknown_mode:; if(l<SfileadrL) sprintf(xorriso->info_text, "-zisofs: unknown mode '%s'", cpt); else sprintf(xorriso->info_text, "-zisofs: oversized mode parameter (%d)",l); sorry_ex: Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); xorriso->zlib_level= was_level; xorriso->zisofs_block_size= was_blocksize; xorriso->zisofs_v2_enabled= was_v2_enabled; xorriso->zisofs_max_total_blocks= was_max_total_blocks; xorriso->zisofs_max_file_blocks= was_max_file_blocks; xorriso->zisofs_v2_block_size= was_blocksize_v2; xorriso->zisofs_block_number_target= was_block_number_target; xorriso->zisofs_bpt_discard_free_ratio= was_bpt_discard_free_ratio; xorriso->zisofs_susp_z2 = was_zisofs_susp_z2; Xorriso_set_zisofs_params(xorriso, 2); return(0); } if(strncmp(cpt, "by_magic=", 9) != 0 && strncmp(cpt, "susp_z2=", 8) != 0) set_isofs_params= 1; } ret= 1; if(set_isofs_params) ret= Xorriso_set_zisofs_params(xorriso, 1); return(ret); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of functions which deal with parsing and interpretation of command input. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <fcntl.h> #include <errno.h> #include <pwd.h> #include <grp.h> #include <sys/resource.h> #include <sys/wait.h> #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" #ifdef Xorriso_fetch_with_msg_queueS #include <pthread.h> #endif /* @param flag bit0= do not warn of wildcards bit1= these are disk_paths */ int Xorriso_end_idx(struct XorrisO *xorriso, int argc, char **argv, int idx, int flag) { int i, warned= 0; for(i= idx; i<argc; i++) { if(strcmp(argv[i], xorriso->list_delimiter)==0) break; if(!((flag&1) || warned)) warned= Xorriso_warn_of_wildcards(xorriso, argv[i], flag&2); } return(i); } /* Returns a vector of strings which belong to an open ended arg list. If expansion is enabled, the vector might be allocated, else it is a pointer into the argv input vector. Thus the release of that memory is an expert task to be done by this function only. Use bit8 for that. With bit8 parameter argc MUST be the same value as with the call which might have allocated memory. @param xorriso The environment object @param argc Length of argv @param argv The vector with arguments, eventual list_delimiter ("--") and then eventual unrelated words @param idx Start index in argv of the argument list @param optc Length of the effective possibly expanded option vector @param optv The option vector. Maybe a pointer into argv or maybe an own allocated vector. @param flag bit0= do not warn of wildcards bit1= these are disk_paths bit2= never expand wildcards bit3= do not expand last argument bit4= ignore last argument bit5= demand exactly one match bit6= with bit5 allow 0 matches if pattern is a constant bit7= silently tolerate empty argument list bit8= free the eventually allocated sub_vector bit9= always expand wildcards bit10= do not add unresolved pattern to optv */ int Xorriso_opt_args(struct XorrisO *xorriso, char *cmd, int argc, char **argv, int idx, int *end_idx, int *optc, char ***optv, int flag) { int i, do_expand, nump, was_empty= 0, filec= 0, ret; char **filev= NULL, **patterns= NULL; off_t mem= 0; if(flag&2) do_expand= (xorriso->do_disk_pattern==1 && !(flag&4)) || (flag & 512); else do_expand= (xorriso->do_iso_rr_pattern==1 && !(flag&4)) || (flag & 512); if(flag&256) { if(argv == NULL || *optv < argv || (*optv >= argv + argc && argc > 0)) Sfile_destroy_argv(optc, optv, 0); return(1); } if(idx>=argc) { *end_idx= argc; *optc= 0; *optv= NULL; sprintf(xorriso->info_text, "%s : Not enough arguments given", cmd); if((flag & 128)) return(1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } *end_idx= Xorriso_end_idx(xorriso, argc, argv, idx, ((flag&1) || do_expand) | (flag&2)); if(*end_idx<0) return(*end_idx); if((flag&16) && (*end_idx)>idx) (*end_idx)--; *optc= *end_idx - idx; *optv= NULL; if(*optc<=0 || !do_expand) { copy_args:; if(*optc > 0) { Xorriso_alloc_meM(*optv, char *, *optc); for(i= 0; i < *optc; i++) { Xorriso_alloc_meM((*optv)[i], char, strlen(argv[idx + i]) + 1); strcpy((*optv)[i], argv[idx + i]); } } return(1); } patterns= calloc(*optc, sizeof(char *)); if(patterns==NULL) { no_memory:; sprintf(xorriso->info_text, "%s : Cannot allocate enough memory for pattern expansion", cmd); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } nump= 0; if(flag&8) { was_empty= 1; mem+= strlen(argv[idx + *optc - 1])+1+sizeof(char *); } for(i= 0; i<*optc-!!(flag&8); i++) { if(argv[i + idx][0]==0) { was_empty++; mem+= sizeof(char *); /* as upper limit for size of an empty string */ continue; } patterns[nump++]= argv[i + idx]; } if(nump<=0) { /* Only empty texts. May the caller get happy with them. */ free(patterns); goto copy_args; } if(flag&2) ret= Xorriso_expand_disk_pattern(xorriso, nump, patterns, was_empty, &filec, &filev, &mem, ((flag >> 5) & 3) | ((!!(flag & 1024)) << 3)); else ret= Xorriso_expand_pattern(xorriso, nump, patterns, was_empty, &filec, &filev, &mem, ((flag >> 5) & 3) | ((!!(flag & 1024)) << 3)); if(ret<=0) {ret= 0; goto ex;} for(i= 0; i<was_empty; i++) { if(i==was_empty-1 && (flag&8)) filev[filec++]= strdup(argv[idx + *optc - 1]); else filev[filec++]= strdup(""); if(filev[filec-1]==NULL) goto no_memory; } #ifdef Xorriso_verbous_pattern_expansioN { int l; sprintf(xorriso->info_text, "Pattern expansion yields %d items:", filec); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); l= 0; xorriso->info_text[0]= 0; for(i= 0; i<filec; i++) { l= strlen(xorriso->info_text); if(l>0 && l+1+strlen(filev[i])>60) { Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); xorriso->info_text[0]= 0; l= 0; } sprintf(xorriso->info_text+l, " %s", filev[i]); } l= strlen(xorriso->info_text); if(l>0) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } #endif /* Xorriso_verbous_pattern_expansioN */ ret= 1; ex:; if(patterns!=NULL) free((char *) patterns); if(ret<=0) { Sfile_destroy_argv(&filec, &filev, 0); } else { *optc= filec; *optv= filev; } return(ret); } /* @param flag bit0= get eternal problem status */ int Xorriso_get_problem_status(struct XorrisO *xorriso, char severity[80], int flag) { if(flag & 1) { strcpy(severity, xorriso->eternal_problem_status_text); return(xorriso->eternal_problem_status); } else { strcpy(severity, xorriso->problem_status_text); return(xorriso->problem_status); } } /* @param flag bit0= set eternal problem status to severity, and set problem status to ALL */ int Xorriso_set_problem_status(struct XorrisO *xorriso, char *severity, int flag) { char *sev_text= "ALL"; int sev, ret; #ifdef Xorriso_fetch_with_msg_queueS int locked= 0, uret; static int complaints= 0, complaint_limit= 5; #endif if(severity[0] && strlen(severity) < sizeof(xorriso->problem_status_text)) sev_text= severity; ret= Xorriso__text_to_sev(sev_text, &sev, 0); if(ret<=0) return(0); #ifdef Xorriso_fetch_with_msg_queueS ret= pthread_mutex_lock(&(xorriso->problem_status_lock)); if(ret != 0) { /* Cannot report failure through the failing message output system */ complaints++; if(complaints < complaint_limit) fprintf(stderr, "xorriso : pthread_mutex_lock() for problem_status returns %d\n", ret); } else locked= 1; #endif /* Xorriso_fetch_with_msg_queueS */ if(flag & 1) { strcpy(xorriso->problem_status_text, "ALL"); Xorriso__text_to_sev(xorriso->problem_status_text, &(xorriso->problem_status), 0); } else { xorriso->problem_status= sev; strcpy(xorriso->problem_status_text, sev_text); } if(sev > xorriso->eternal_problem_status || (flag & 1)) { xorriso->eternal_problem_status= sev; strcpy(xorriso->eternal_problem_status_text, sev_text); } #ifdef Xorriso_fetch_with_msg_queueS if(locked) { uret= pthread_mutex_unlock(&(xorriso->problem_status_lock)); if(uret != 0) { /* Cannot report failure through the failing message output system */ complaints++; if(complaints < complaint_limit) fprintf(stderr, "xorriso : pthread_mutex_unlock() for problem_status returns %d\n", uret); } } #endif /* Xorriso_fetch_with_msg_queueS */ return(1); } /** @param flag bit0= do not issue own event messages bit1= take xorriso->request_to_abort as reason for abort @return Gives the advice: 2= pardon was given, go on 1= no problem, go on 0= function failed but xorriso would not abort, go on <0= do abort -1 = due to problem_status -2 = due to xorriso->request_to_abort */ int Xorriso_eval_problem_status(struct XorrisO *xorriso, int ret, int flag) { static int sev= 0; if(sev==0) Xorriso__text_to_sev("SORRY", &sev, 0); if((flag&2) && xorriso->request_to_abort) return(-2); Xorriso_process_msg_queues(xorriso, 0); if(ret>0 && xorriso->problem_status <= 0) return(1); if(xorriso->problem_status < xorriso->abort_on_severity && xorriso->problem_status > 0) { if(xorriso->problem_status >= sev && !(flag&1)) { sprintf(xorriso->info_text, "xorriso : NOTE : Tolerated problem event of severity '%s'\n", xorriso->problem_status_text); Xorriso_info(xorriso, 0);/* submit not as problem event */ } ret= 2; } else if(xorriso->problem_status > 0) { sprintf(xorriso->info_text, "xorriso : aborting : -abort_on '%s' encountered '%s'\n", xorriso->abort_on_text, xorriso->problem_status_text); if(!(flag&1)) Xorriso_info(xorriso, 0);/* submit not as problem event */ ret= -1; } else if(ret>0) ret= 1; else ret= 2; return(ret); } /* @param flag bit0= a non-existing target of multiple sources is a directory bit1= all paths except the last one are disk_paths bit2= the last path is a disk_path @return <=0 is error, 1= leaf file object, 2= directory */ int Xorriso_cpmv_args(struct XorrisO *xorriso, char *cmd, int argc, char **argv, int *idx, int *optc, char ***optv, char eff_dest[SfileadrL], int flag) { int destc= 0, is_dir=0, end_idx, ret, i; char **destv= NULL; end_idx= Xorriso_end_idx(xorriso, argc, argv, *idx, (xorriso->do_iso_rr_pattern==1)|(flag&2)); if(end_idx - *idx < 2) { sprintf(xorriso->info_text, "%s: not enough arguments", cmd); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); {ret= 0; goto ex;} } ret= Xorriso_opt_args(xorriso, cmd, argc, argv, *idx, &end_idx, optc, optv, 1 | (flag&2) | 16); /* ignore last argument */ if(ret<=0) goto ex; /* demand one match, or 0 with a constant */ ret= Xorriso_opt_args(xorriso, cmd, argc, argv, end_idx, &end_idx, &destc, &destv, 1 | ((flag&4)>>1) | 32 | 64); if(ret<=0) goto ex; /* Evaluate target address */ if(flag&4) ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, destv[0], eff_dest, 2|4|16); else ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, destv[0], eff_dest, 1); if(ret<0) {ret= 0; goto ex;} if(ret==2 || ((flag&1) && *optc > 1 && ret==0)) { is_dir= 1; } else if(*optc > 1) { if(flag & 2) for(i= 0; i<*optc; i++) Xorriso_msgs_submit(xorriso, 0, (*optv)[i], 0, "ERRFILE", 0); sprintf(xorriso->info_text, "%s: more than one origin given, destination is a non-directory: ", cmd); Text_shellsafe(destv[0], xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(ret==0) { /* compute complete eff_dest */ ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, destv[0], eff_dest, 2 | (flag&4)); if(ret<0) {ret= 0; goto ex;} } ret= 1+is_dir; ex:; Xorriso_opt_args(xorriso, cmd, argc, argv, *idx, &end_idx, &destc, &destv, 256); (*idx)= end_idx; return(ret); } /* @param flag bit0= with adr_mode sbsector: adr_value is possibly 16 too high */ int Xorriso_decode_load_adr(struct XorrisO *xorriso, char *cmd, char *adr_mode, char *adr_value, int *entity_code, char entity_id[81], int flag) { double num; int l; if(strcmp(adr_mode, "auto")==0) *entity_code= 0; else if(strcmp(adr_mode, "session")==0) *entity_code= 1; else if(strcmp(adr_mode, "track")==0) *entity_code= 2; else if(strcmp(adr_mode, "lba")==0 || strcmp(adr_mode, "sbsector")==0) *entity_code= 3 | ((flag&1) << 16); else if(strcmp(adr_mode, "volid")==0) *entity_code= 4; else if(strcmp(adr_mode, "at_time")==0) *entity_code= 5; else if(strcmp(adr_mode, "not_after")==0) *entity_code= 6; else if(strcmp(adr_mode, "not_before")==0) *entity_code= 7; else if(strcmp(adr_mode, "before")==0) *entity_code= 8; else if(strcmp(adr_mode, "after")==0) *entity_code= 9; else { sprintf(xorriso->info_text, "%s: unknown address mode '%s'", cmd, adr_mode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } l= strlen(adr_value); if(l==0) *entity_code= 0; if(*entity_code>=1 && *entity_code<= 3) { num= Scanf_io_size(adr_value, 0); if(*entity_code==3 && (adr_value[l-1]<'0' || adr_value[l-1]>'9')) num/= 2048.0; sprintf(entity_id, "%.f", num); } else { if(strlen(adr_value)>80) { sprintf(xorriso->info_text, "%s: address value too long (80 < %d)", cmd, (int) strlen(adr_value)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } strcpy(entity_id, adr_value); } return(1); } int Xorriso_prepare_load_search(struct XorrisO *xorriso, char *cmd, int adr_mode, char *adr_value_in, char *adr_value_out, int *params_flag, int flag) { int ret; time_t seconds; *params_flag= 0; if(adr_mode == 4 && strlen(adr_value_in) <= 80) { ret= Xorriso__bourne_to_reg(adr_value_in, adr_value_out, 0); if(ret == 1) { *params_flag|= 4; } else { strcpy(adr_value_out, adr_value_in); } return(1); } if(adr_mode >= 5 && adr_mode <= 9) { ret= Decode_timestring(adr_value_in, &seconds, 0); if(ret <= 0) { sprintf(xorriso->info_text, "%s: Cannot decode timestring '%s'", cmd, adr_value_in); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } sprintf(adr_value_out, "%.f", (double) seconds); if(xorriso->toc_info_type == 3) *params_flag|= 4; return(1); } strcpy(adr_value_out, adr_value_in); return(1); } int Xorriso_check_thing_len(struct XorrisO *xorriso, char *name, int size, char *cmd, char *thing, int flag) { if((int) strlen(name) >= size) { sprintf(xorriso->info_text, "%s too long with option %s (%d > %d)", thing, cmd, (int) strlen(name), size - 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } return(1); } int Xorriso_check_name_len(struct XorrisO *xorriso, char *name, int size, char *cmd, int flag) { return Xorriso_check_thing_len(xorriso, name, size, cmd, "Name", flag); } /* @return <0 error , >=0 number of skipped dashes */ int Xorriso_normalize_command(struct XorrisO *xorriso, char *original_cmd, int argno, char *cmd_data, int sizeof_cmd_data, char **cmd, int flag) { int was_dashed= 0; char *dash_pt; if((int) strlen(original_cmd) >= sizeof_cmd_data) { if(argno>=0) sprintf(xorriso->info_text, "Oversized argument #%d (length %d)\n", argno, (int) strlen(original_cmd)); else sprintf(xorriso->info_text, "Oversized option (length %d)\n", (int) strlen(original_cmd)); return(-1); } strcpy(cmd_data, original_cmd); *cmd= cmd_data; if(strcmp(*cmd, xorriso->list_delimiter)==0) return(1); while((*cmd)[0]=='-') { if((*cmd)[1]==0) break; was_dashed++; (*cmd)++; } for(dash_pt= *cmd; *dash_pt!=0; dash_pt++) if(*dash_pt=='-') *dash_pt= '_'; return(was_dashed); } /* @param flag bit0= do not warn of unknown option @return <=0 error, 1=count is valid, 2=dashed unknown, 3=undashed unknown */ int Xorriso_count_args(struct XorrisO *xorriso, int argc, char **argv, int *count, int flag) { int ret, was_dashed= 0, i, cmd_data_size= 2 * SfileadrL; char *cmd, *cmd_data= NULL; static char arg0_commands[][40]= { "ban_stdio_write","close_filter_list","commit", "device_links","devices","end", "for_backup", "help", "list_arg_sorting","list_formats","list_speeds", "no_rc","print_size","pvd_info","pwd","pwdi","pwdx", "read_mkisofsrc","rollback","rollback_end", "tell_media_space","toc","version", "" }; static char arg1_commands[][40]= { "abort_on","acl","add_plainly","application_id","application_use", "auto_charset","abstract_file","assess_indev_features", "backslash_codes","blank","biblio_file", "calm_drive","cd","cdi","cdx","changes_pending","charset", "close","close_damaged", "commit_eject","compliance","copyright_file", "dev","dialog","disk_dev_ino","disk_pattern","displacement", "drive_access","dummy","dvd_obs","early_stdio_test","ecma119_map","eject", "extract_boot_images", "iso_nowtime","iso_rr_pattern","file_name_limit","follow","format","fs", "genisoimage_completion", "gid", "grow_blindly", "hardlinks", "hfsplus","history","indev","in_charset","joliet","joliet_map", "lfa_flags", "list_delimiter","list_extras","list_profiles","local_charset", "mark","md5","mount_opts","modesty_on_drive", "not_leaf","not_list","not_mgt", "options_from_file","osirrox","outdev","out_charset","overwrite", "pacifier","padding","path_list","pathspecs","pkt_output", "preparer_id","print","print_info","print_mark","prompt", "prog","prog_help","publisher","quoted_not_list","quoted_path_list", "read_fs","read_speed","reassure","report_about", "report_el_torito","report_system_area","rockridge", "rom_toc_scan","rr_reloc_dir","scsi_dev_family","scsi_log", "session_log","setfattr_list","setfattr_listi", "sh_style_result","signal_handling","sleep", "speed","split_size","status","status_history_max", "stdio_sync","stream_recording","system_id","temp_mem_limit", "toc_info_type", "toc_of", "uid","unregister_filter","use_immed_bit","use_readline", "volid","volset_id", "write_type","xattr","zisofs", "" }; static char arg2_commands[][40]= { "assert_volid","boot_image","clone","compare","compare_r","drive_class", "data_cache_size", "errfile_log","error_behavior","extract","extract_single", "jigdo","lns","lnsi","load","logfile", "map","map_single","move","msg_op","page","return_with", "scdbackup_tag","update","update_r","volume_date", "" }; static char arg3_commands[][40]= { "append_partition", "truncate_overwritable", "" }; static char arg4_commands[][40]= { "cut_out","extract_cut","mount","mount_cmd","named_pipe_loop", "paste_in","session_string", "" }; static char argn_commands[][40]= { "add","alter_date","alter_date_r","as", "chattr","chattr_r","chattr_ri","chattri", "check_md5","check_md5_r","check_media","check_media_defaults", "chgrp","chgrpi","chgrp_r","chgrp_ri","chmod","chmodi", "chmod_r","chmod_ri","chown","chowni","chown_r","chown_ri", "compare_l","concat","cp_clone","cp_rax","cp_rx","cpr","cpri","cpax","cpx", "du","dui","dus","dusi","dux","dusx","external_filter","extract_l", "file_size_limit","find","findi","finds","findx", "getfacl","getfacli","getfacl_r","getfacl_ri", "getfattr","getfattri","getfattr_r","getfattr_ri","hide", "launch_frontend","lsattr","lsattri","lsattrd","lsaddrdi", "ls","lsi","lsl","lsli","lsd","lsdi","lsdl","lsdli", "lsx","lslx","lsdx","lsdlx","map_l","mv","mvi","mkdir","mkdiri", "not_paths","rm","rmi","rm_r","rm_ri","rmdir","rmdiri", "update_l","update_li","update_lx","update_lxi", "setfacl","setfacli","setfacl_list","setfacl_listi", "setfacl_r","setfacl_ri","setfattr","setfattri", "setfattr_r","setfattr_ri", "set_filter","set_filter_r","show_stream","show_stream_r", "" }; Xorriso_alloc_meM(cmd_data, char, cmd_data_size); *count= 0; if(argc<=0) {ret= -1; goto ex;} ret= Xorriso_normalize_command(xorriso, argv[0], -1, cmd_data, cmd_data_size, &cmd, 0); if(ret<0) goto ex; was_dashed= (ret>0); if(cmd[0]=='#' || cmd[0]==0 || strcmp(cmd, xorriso->list_delimiter) == 0) { /* ignore: comment line , empty option , orphaned list delimiter */ {ret= 1; goto ex;} } for(i=0; arg0_commands[i][0]!=0; i++) if(strcmp(arg0_commands[i], cmd)==0) {ret= 1; goto ex;} *count= 1; for(i=0; arg1_commands[i][0]!=0; i++) if(strcmp(arg1_commands[i], cmd)==0) {ret= 1; goto ex;} *count= 2; for(i=0; arg2_commands[i][0]!=0; i++) if(strcmp(arg2_commands[i], cmd)==0) {ret= 1; goto ex;} *count= 3; for(i=0; arg3_commands[i][0]!=0; i++) if(strcmp(arg3_commands[i], cmd)==0) {ret= 1; goto ex;} *count= 4; for(i=0; arg4_commands[i][0]!=0; i++) if(strcmp(arg4_commands[i], cmd)==0) {ret= 1; goto ex;} *count= 0; for(i=0; argn_commands[i][0]!=0; i++) if(strcmp(argn_commands[i], cmd)==0) { ret= Xorriso_end_idx(xorriso, argc, argv, 1, 1); if(ret<1) goto ex; *count= ret-1; {ret= 1; goto ex;} } if(!(flag&1)) { sprintf(xorriso->info_text, "Unknown option : '%s'", argv[0]); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } ret= 2 + !was_dashed; ex: Xorriso_free_meM(cmd_data); return(ret); } /* @param flag bit0= list sorting order rather than looking for argv[idx] */ int Xorriso_cmd_sorting_rank(struct XorrisO *xorriso, int argc, char **argv, int idx, int flag) { int ret, i, cmd_data_size= 2 * SfileadrL; char *cmd, *cmd_data= NULL; static char *commands[]= { "* Execution order of program arguments with option -x:", "x", "* Support for frontend programs via stdin and stdout (1):", "prog", "prog_help", "* Exception processing:", "abort_on", "return_with", "report_about", "signal_handling", "error_behavior", "* Scripting, dialog and program control features (1):", "no_rc", "help", "version", "list_extras", "list_arg_sorting", "temp_mem_limit", "backslash_codes", "errfile_log", "session_log", "scsi_log", "options_from_file", "list_delimiter", "print", "print_info", "print_mark", "prompt", "sleep", "sh_style_result", "* Influencing opening of drives:", "drive_access","drive_class","early_stdio_test", "* Drive and media related inquiry actions (1):", "devices", "device_links", "mount_opts", "mount_cmd", "session_string", "* Influencing the behavior of image loading:", "read_speed", "load", "displacement", "read_fs", "assert_volid", "in_charset", "auto_charset", "for_backup", "hardlinks", "acl", "xattr", "md5", "lfa_flags", "ecma119_map", "joliet_map", "disk_dev_ino", "rom_toc_scan", "calm_drive", "ban_stdio_write", "data_cache_size", "scsi_dev_family", "iso_nowtime", "* Character sets:", "charset", "local_charset", "* Acquiring source and target drive:", "dev", "indev", "outdev", "* Drive and media related inquiry actions (2):", "list_profiles", "list_formats", "list_speeds", "toc_info_type", "toc", "toc_of", "pvd_info", "report_system_area", "report_el_torito", "assess_indev_features", "* Settings for file insertion:", "file_name_limit", "file_size_limit", "not_mgt", "not_paths", "not_leaf", "not_list", "quoted_not_list", "follow", "pathspecs", "overwrite", "split_size", "* Navigation in ISO image and disk filesystem (1):", "cd", "cdx", "pwd", "pwdx", "* Inserting files into ISO image:", "disk_pattern", "add_plainly", "mkdir", "lns", "add", "path_list", "quoted_path_list", "map", "map_single", "map_l", "update", "update_r", "update_l", "update_li", "update_lx", "update_lxi", "cut_out", "cpr", "clone", "cp_clone", "* Navigation in ISO image and disk filesystem (2):", "ls", "lsd", "lsl", "lsdl", "lsx", "lsdx", "lslx", "lsdlx", "lsattr", "lsattri", "lsattrd", "lsaddrdi", "getfacl", "getfacl_r", "getfattr", "getfattr_r", "du", "dus", "dux", "dusx", "findx", "compare", "compare_r", "compare_l", "show_stream", "show_stream_r", "* File manipulations:", "iso_rr_pattern", "rm", "rm_r", "rmdir", "move", "mv", "chown", "chown_r", "chgrp", "chgrp_r", "chmod", "chmod_r", "setfacl", "setfacl_r", "setfacl_list", "setfattr", "setfattr_r", "setfattr_list", "chattr", "chattr_r", "alter_date", "alter_date_r", "hide", "* Filters for data file content:", "external_filter", "unregister_filter", "close_filter_list", "set_filter", "set_filter_r", "* Tree traversal command -find:", "find", "* osirrox ISO-to-disk restore options:", "osirrox", "extract", "extract_single", "extract_l", "extract_cut", "extract_boot_images", "cpx", "cpax", "cp_rx", "cp_rax", "paste_in", "concat", "mount", "* Settings for result writing:", "rockridge", "joliet", "hfsplus","compliance", "rr_reloc_dir", "volid", "volset_id", "publisher", "application_id", "system_id", "volume_date", "copyright_file", "abstract_file", "biblio_file", "preparer_id", "application_use", "out_charset", "read_mkisofsrc", "uid", "gid", "zisofs", "speed", "stream_recording", "dvd_obs", "modesty_on_drive", "use_immed_bit", "stdio_sync", "dummy", "fs", "close", "padding", "write_type", "grow_blindly", "pacifier", "scdbackup_tag", "* Bootable ISO images:", "boot_image", "append_partition", "* Jigdo Template Extraction:", "jigdo", "* Command compatibility emulations:", "as", "* Scripting, dialog and program control features (2):", "history", "status_history_max", "status", "* Drive and media related inquiry actions (3):", "print_size", "tell_media_space", "* Writing the result, drive control:", "format", "blank", "truncate_overwritable", "close_damaged", "rollback", "changes_pending", "commit", "commit_eject", "eject", "* Evaluation of readability and recovery:", "check_media_defaults", "check_media", "check_md5", "check_md5_r", "* Support for frontend programs via stdin and stdout (2):", "pkt_output", "logfile", "mark", "msg_op", "* Dialog mode control:", "dialog", "page", "use_readline", "reassure", "* Support for frontend programs via stdin and stdout (3):", "launch_frontend", "named_pipe_loop", "* Scripting, dialog and program control features (3):", "rollback_end", "end", "" }; if(flag & 1) { for(i= 0; commands[i][0] !=0; i++) { if(commands[i][0] == '*') sprintf(xorriso->result_line, "#%s\n", commands[i] + 1); else sprintf(xorriso->result_line, "-%s\n", commands[i]); Xorriso_result(xorriso, 0); } ret= 1; goto ex; } if(argc <= 0) {ret= -1; goto ex;} Xorriso_alloc_meM(cmd_data, char, cmd_data_size); ret= Xorriso_normalize_command(xorriso, argv[idx], -1, cmd_data, cmd_data_size, &cmd, 0); if(ret < 0) goto ex; if(cmd[0] == '#' || cmd[0] == 0 || strcmp(cmd, xorriso->list_delimiter) == 0) { /* Move to end: comment line , empty option , orphaned list delimiter */ ret= 0x7fffffff; goto ex; } for(i= 0; commands[i][0] !=0; i++) { if(commands[i][0] == '*') /* headline in command list */ continue; if(strcmp(commands[i], cmd) != 0) continue; ret= i + 1; goto ex; } ret= 1; ex: Xorriso_free_meM(cmd_data); return(ret); } int Xorriso__cmp_cmd_rank(const void *a, const void *b) { int ra, rb; ra= ((int *) a)[1]; rb= ((int *) b)[1]; if(ra < rb) return(-1); if(ra > rb) return(1); ra= ((int *) a)[2]; rb= ((int *) b)[2]; if(ra < rb) return(-1); if(ra > rb) return(1); return(0); } /* @param flag bit0= print command sequence rather than executing it bit1= these are command line arguments (for xorriso->argument_emulation) */ int Xorriso_exec_args_sorted(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) { int cmd_count= 0, ret, i, arg_count, *idx_rank= NULL, cmd_idx; /* Count commands and allocate index-rank array */ for(i= *idx; i < argc; i++) { ret= Xorriso_count_args(xorriso, argc - i, argv + i, &arg_count, 1); if(ret <= 0) goto ex; if(ret != 1) continue; cmd_count++; i+= arg_count; } if(cmd_count <= 0) {ret= 1; goto ex;} Xorriso_alloc_meM(idx_rank, int, 3 * cmd_count); /* Fill index-rank array and sort */ cmd_count= 0; for(i= *idx; i < argc; i++) { ret= Xorriso_count_args(xorriso, argc - i, argv + i, &arg_count, 1); if(ret <= 0) goto ex; if(ret != 1) continue; idx_rank[3 * cmd_count]= i; ret= Xorriso_cmd_sorting_rank(xorriso, argc, argv, i, 0); if(ret < 0) goto ex; idx_rank[3 * cmd_count + 1]= ret; idx_rank[3 * cmd_count + 2]= cmd_count; cmd_count++; i+= arg_count; } qsort(idx_rank, cmd_count, 3 * sizeof(int), Xorriso__cmp_cmd_rank); /* Execute or print indice from index-rank array */ if(flag & 1) { sprintf(xorriso->result_line, "Automatically determined command sequence:\n"); Xorriso_result(xorriso, 0); xorriso->result_line[0]= 0; } for(i= 0; i < cmd_count; i++) { cmd_idx= idx_rank[3 * i]; if(flag & 1) { if(strlen(xorriso->result_line) + 1 + strlen(argv[cmd_idx]) > 78) { strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); xorriso->result_line[0]= 0; } sprintf(xorriso->result_line + strlen(xorriso->result_line), " %s", argv[cmd_idx]); } else { ret= Xorriso_interpreter(xorriso, argc, argv, &cmd_idx, 4 | (flag & 2)); if(ret <= 0 || ret == 3) goto ex; } } if(flag & 1) { if(strlen(xorriso->result_line) > 0) { strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 0); } } else *idx= argc; ret= 1; ex: Xorriso_free_meM(idx_rank); return(ret); } /* @param flag bit0= recursion bit1= these are command line arguments (for xorriso->argument_emulation) bit2= Only execute the one command argv[*idx] and advance *idx to the next command if successful. Then return. */ int Xorriso_interpreter(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag) /* return: <=0 error , 1 = success , 2 = problem event ignored , 3 = end program run */ { int ret, was_dashed, end_ret, num1, num2, cmd_data_size= 2 * SfileadrL; int mem_idx, arg_count, i; char *cmd, *original_cmd, *cmd_data= NULL, *arg1, *arg2, *arg3, *arg4; Xorriso_alloc_meM(cmd_data, char, cmd_data_size); if(xorriso==NULL) {ret= 0; goto ex;} if(xorriso->is_dialog) { xorriso->result_line_counter= xorriso->result_page_counter= 0; if(xorriso->result_page_length<0) xorriso->result_page_length= -xorriso->result_page_length; } next_command:; if(flag&2) { ret= 1; if(xorriso->argument_emulation==1) ret= Xorriso_as_genisofs(xorriso, argc, argv, idx, 0); else if(xorriso->argument_emulation==2) ret= Xorriso_as_cdrskin(xorriso, argc, argv, idx, 0); if(xorriso->argument_emulation>0) { xorriso->argument_emulation= 0; if(ret<=0) goto eval_any_problems; if((*idx)>=argc) {ret= 1; goto ex;} } xorriso->current_interpreter= 0; if((xorriso->arrange_args || (flag & 8)) && !(flag & (4 | 16))) { ret= Xorriso_exec_args_sorted(xorriso, argc, argv, idx, 0); goto ex; } } xorriso->current_interpreter= 0; ret= Xorriso_count_args(xorriso, argc - *idx, argv + *idx, &arg_count, 1); if((ret == 1 || ret == 2) && strcmp(argv[*idx], xorriso->list_delimiter) != 0) { sprintf(xorriso->info_text, "Command: %s", argv[*idx]); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); for(i= 1; i <= arg_count && *idx + i < argc; i++) { sprintf(xorriso->info_text, "Parameter: %s", argv[*idx + i]); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } if(*idx + arg_count >= argc) { sprintf(xorriso->info_text, "Missing arguments: %d", *idx + arg_count + 1 - argc); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } } xorriso->prepended_wd= 0; xorriso->request_to_abort= xorriso->request_not_to_ask= 0; Xorriso_set_problem_status(xorriso, "", 0); if((*idx)<argc) original_cmd= cmd= argv[*idx]; else original_cmd= cmd= ""; if(xorriso->add_plainly==3 && cmd[0] && !xorriso->is_dialog) { (*idx)++; goto add_plain_argument; } was_dashed= 0; ret= Xorriso_normalize_command(xorriso, original_cmd, -1, cmd_data, cmd_data_size, &cmd, 0); if(ret<0) goto eval_any_problems; was_dashed= ret; (*idx)++; if((*idx)<argc) arg1= argv[(*idx)]; else arg1= ""; if((*idx)+1<argc) arg2= argv[(*idx)+1]; else arg2= ""; if((*idx) + 2 < argc) arg3= argv[(*idx) + 2]; else arg3= ""; ret= 1; if(cmd[0]=='#' || cmd[0]==0) { /* ignore comment line and empty option */; } else if(strcmp(cmd,"abort_on")==0) { (*idx)++; ret= Xorriso_option_abort_on(xorriso, arg1, 0); } else if(strcmp(cmd,"abstract_file")==0) { (*idx)++; Xorriso_option_abstract_file(xorriso, arg1, 0); } else if(strcmp(cmd,"acl")==0) { (*idx)++; ret= Xorriso_option_acl(xorriso, arg1, 0); } else if(strcmp(cmd,"add")==0) { ret= Xorriso_option_add(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"add_plainly")==0) { (*idx)++; ret= Xorriso_option_add_plainly(xorriso, arg1, 0); } else if(strcmp(cmd,"alter_date")==0 || strcmp(cmd,"alter_date_r")==0) { (*idx)+= 2; ret= Xorriso_option_alter_date(xorriso, arg1, arg2, argc, argv, idx, strlen(cmd)>10); } else if(strcmp(cmd,"append_partition")==0) { (*idx)+= 3; ret= Xorriso_option_append_partition(xorriso, arg1, arg2, arg3, 0); } else if(strcmp(cmd,"application_id")==0) { (*idx)++; ret= Xorriso_option_application_id(xorriso, arg1, 0); } else if(strcmp(cmd,"application_use") == 0) { (*idx)++; ret= Xorriso_option_application_use(xorriso, arg1, 0); } else if(strcmp(cmd,"as")==0) { ret= Xorriso_option_as(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"assert_volid")==0) { (*idx)+= 2; ret= Xorriso_option_assert_volid(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"assess_indev_features")==0) { (*idx)++; ret= Xorriso_option_assess_indev_features(xorriso, arg1, 0); } else if(strcmp(cmd,"auto_charset")==0) { (*idx)++; ret= Xorriso_option_auto_charset(xorriso, arg1, 0); } else if(strcmp(cmd,"backslash_codes")==0) { (*idx)++; ret= Xorriso_option_backslash_codes(xorriso, arg1, 0); } else if(strcmp(cmd,"ban_stdio_write")==0) { ret= Xorriso_option_ban_stdio_write(xorriso, 0); } else if(strcmp(cmd,"biblio_file")==0) { (*idx)++; Xorriso_option_biblio_file(xorriso, arg1, 0); } else if(strcmp(cmd,"blank")==0) { (*idx)++; ret= Xorriso_option_blank(xorriso, arg1, 0); } else if(strcmp(cmd,"boot_image")==0) { (*idx)+= 2; ret= Xorriso_option_boot_image(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"calm_drive")==0) { (*idx)++; ret= Xorriso_option_calm_drive(xorriso, arg1, 0); } else if(strcmp(cmd,"cd")==0 || strcmp(cmd,"cdi")==0) { (*idx)++; ret= Xorriso_option_cdi(xorriso, arg1, 0); } else if(strcmp(cmd,"cdx")==0) { (*idx)++; ret= Xorriso_option_cdx(xorriso, arg1, 0); } else if(strcmp(cmd, "changes_pending")==0) { (*idx)++; ret= Xorriso_option_changes_pending(xorriso, arg1, 0); } else if(strcmp(cmd,"charset")==0) { (*idx)++; ret= Xorriso_option_charset(xorriso, arg1, 3); } else if(strcmp(cmd, "chattr") == 0 || strcmp(cmd, "chattri") == 0) { (*idx)++; ret= Xorriso_option_chattri(xorriso, arg1, argc, argv, idx, 0); } else if(strcmp(cmd, "chattr_r") == 0 || strcmp(cmd, "chattr_ri") == 0) { (*idx)++; ret= Xorriso_option_chattri(xorriso, arg1, argc, argv, idx, 1); } else if(strcmp(cmd,"check_md5")==0) { ret= Xorriso_option_check_md5(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"check_md5_r")==0) { ret= Xorriso_option_check_md5(xorriso, argc, argv, idx, 8); } else if(strcmp(cmd,"check_media")==0) { ret= Xorriso_option_check_media(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"check_media_defaults")==0) { ret= Xorriso_option_check_media_defaults(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"chgrp")==0 || strcmp(cmd,"chgrpi")==0) { (*idx)+= 1; ret= Xorriso_option_chgrpi(xorriso, arg1, argc, argv, idx, 0); } else if(strcmp(cmd,"chgrp_r")==0 || strcmp(cmd,"chgrp_ri")==0) { (*idx)+= 1; ret= Xorriso_option_chgrpi(xorriso, arg1, argc, argv, idx, 1); } else if(strcmp(cmd,"chmod")==0 || strcmp(cmd,"chmodi")==0) { (*idx)+= 1; ret= Xorriso_option_chmodi(xorriso, arg1, argc, argv, idx, 0); } else if(strcmp(cmd,"chmod_r")==0 || strcmp(cmd,"chmod_ri")==0) { (*idx)+= 1; ret= Xorriso_option_chmodi(xorriso, arg1, argc, argv, idx, 1); } else if(strcmp(cmd,"chown_r")==0 || strcmp(cmd,"chown_ri")==0) { (*idx)+= 1; ret= Xorriso_option_chowni(xorriso, arg1, argc, argv, idx, 1); } else if(strcmp(cmd,"chown")==0 || strcmp(cmd,"chowni")==0) { (*idx)+= 1; ret= Xorriso_option_chowni(xorriso, arg1, argc, argv, idx, 0); } else if(strcmp(cmd,"clone")==0) { (*idx)+= 2; ret= Xorriso_option_clone(xorriso, arg1, arg2, 1); } else if(strcmp(cmd,"close")==0) { (*idx)++; ret= Xorriso_option_close(xorriso, arg1, 0); } else if(strcmp(cmd,"close_damaged")==0) { (*idx)++; ret= Xorriso_option_close_damaged(xorriso, arg1, 0); } else if(strcmp(cmd,"close_filter_list")==0) { ret= Xorriso_option_close_filter_list(xorriso, 0); } else if(strcmp(cmd,"commit")==0) { ret= Xorriso_option_commit(xorriso, 0); } else if(strcmp(cmd,"commit_eject")==0) { (*idx)++; ret= Xorriso_option_commit_eject(xorriso, arg1, 0); } else if(strcmp(cmd,"compare")==0) { (*idx)+= 2; ret= Xorriso_option_compare(xorriso, arg1, arg2, 1); } else if(strcmp(cmd,"compare_l")==0) { ret= Xorriso_option_map_l(xorriso, argc, argv, idx, 1<<8); } else if(strcmp(cmd,"compare_r")==0) { (*idx)+= 2; ret= Xorriso_option_compare(xorriso, arg1, arg2, 1|8); } else if(strcmp(cmd,"compliance")==0) { (*idx)++; Xorriso_option_compliance(xorriso, arg1, 0); } else if(strcmp(cmd,"concat") == 0) { ret= Xorriso_option_concat(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"copyright_file")==0) { (*idx)++; Xorriso_option_copyright_file(xorriso, arg1, 0); } else if(strcmp(cmd,"cp_clone") == 0) { ret= Xorriso_option_cp_clone(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"cp_rx")==0 || strcmp(cmd,"cp_rax")==0) { ret= Xorriso_option_cpx(xorriso, argc, argv, idx, 1|((strcmp(cmd,"cp_rax")==0)<<1)); } else if(strcmp(cmd,"cpr")==0 || strcmp(cmd,"cpri")==0) { ret= Xorriso_option_cpri(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"cpx")==0 || strcmp(cmd,"cpax")==0) { ret= Xorriso_option_cpx(xorriso, argc, argv, idx, (strcmp(cmd,"cpax")==0)<<1); } else if(strcmp(cmd,"cut_out")==0) { (*idx)+= 4; if((*idx)>argc) { sprintf(xorriso->info_text, "-cut_out: Not enough arguments. Needed are: disk_path start count so_rr_path"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; } else ret= Xorriso_option_cut_out(xorriso, arg1, arg2, argv[(*idx)-2], argv[(*idx)-1], 0); } else if(strcmp(cmd,"data_cache_size")==0) { (*idx)+= 2; ret= Xorriso_option_data_cache_size(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"dev")==0) { (*idx)++; ret= Xorriso_option_dev(xorriso, arg1, 3); } else if(strcmp(cmd,"device_links")==0) { ret= Xorriso_option_devices(xorriso, 1); } else if(strcmp(cmd,"devices")==0) { ret= Xorriso_option_devices(xorriso, 0); } else if(strcmp(cmd,"dialog")==0) { (*idx)++; ret= Xorriso_option_dialog(xorriso, arg1, 0); } else if(strcmp(cmd,"disk_dev_ino")==0) { (*idx)++; ret= Xorriso_option_disk_dev_ino(xorriso, arg1, 0); } else if(strcmp(cmd,"displacement")==0) { (*idx)++; ret= Xorriso_option_displacement(xorriso, arg1, 0); } else if(strcmp(cmd,"disk_pattern")==0) { (*idx)++; ret= Xorriso_option_disk_pattern(xorriso, arg1, 0); } else if(strcmp(cmd,"drive_access")==0) { (*idx)++; ret= Xorriso_option_drive_access(xorriso, arg1, 0); } else if(strcmp(cmd,"drive_class")==0) { (*idx)+= 2; ret= Xorriso_option_drive_class(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"du")==0 || strcmp(cmd,"dui")==0 || strcmp(cmd,"dus")==0 || strcmp(cmd,"dusi")==0) { ret= Xorriso_option_lsi(xorriso, argc, argv, idx, (cmd[2]!='s')|4); } else if(strcmp(cmd,"dummy")==0) { (*idx)++; ret= Xorriso_option_dummy(xorriso, arg1, 0); } else if(strcmp(cmd,"dvd_obs")==0) { (*idx)++; ret= Xorriso_option_dvd_obs(xorriso, arg1, 0); } else if(strcmp(cmd,"dux")==0 || strcmp(cmd,"dusx")==0) { ret= Xorriso_option_lsx(xorriso, argc, argv, idx, (cmd[2]!='s')|4); } else if(strcmp(cmd,"early_stdio_test")==0) { (*idx)++; ret= Xorriso_option_early_stdio_test(xorriso, arg1, 0); } else if(strcmp(cmd,"ecma119_map")==0) { (*idx)++; ret= Xorriso_option_ecma119_map(xorriso, arg1, 0); } else if(strcmp(cmd,"eject")==0) { (*idx)++; ret= Xorriso_option_eject(xorriso, arg1, 0); } else if(strcmp(cmd,"end")==0) { end_ret= Xorriso_option_end(xorriso, 0); ret= Xorriso_eval_problem_status(xorriso, ret, 0); if(ret<0) goto ex; if(end_ret!=2) {ret= 3; goto ex;} } else if(strcmp(cmd,"errfile_log")==0) { (*idx)+= 2; ret= Xorriso_option_errfile_log(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"error_behavior")==0) { (*idx)+= 2; ret= Xorriso_option_error_behavior(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"external_filter")==0) { ret= Xorriso_option_external_filter(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"extract")==0) { (*idx)+= 2; ret= Xorriso_option_extract(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"extract_boot_images")==0) { (*idx)+= 1; if((*idx)>argc) { sprintf(xorriso->info_text, "-extract_boot_images: Empty disk_path cannot be used as target directory"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; } else { ret= Xorriso_option_extract_boot_images(xorriso, arg1, 0); } } else if(strcmp(cmd,"extract_cut")==0) { (*idx)+= 4; if((*idx)>argc) { sprintf(xorriso->info_text, "-extract_cut: Not enough arguments. Needed are: disk_path start count iso_rr_path"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; } else ret= Xorriso_option_extract_cut(xorriso, arg1, arg2, argv[(*idx)-2], argv[(*idx)-1], 0); } else if(strcmp(cmd,"extract_l")==0) { ret= Xorriso_option_map_l(xorriso, argc, argv, idx, 3<<8); } else if(strcmp(cmd,"extract_single")==0) { (*idx)+= 2; ret= Xorriso_option_extract(xorriso, arg1, arg2, 32); } else if(strcmp(cmd,"file_name_limit")==0) { (*idx)++; ret= Xorriso_option_file_name_limit(xorriso, arg1, 0); } else if(strcmp(cmd,"file_size_limit")==0) { ret= Xorriso_option_file_size_limit(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"find")==0 || strcmp(cmd,"findi")==0) { ret= Xorriso_option_find(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"findx")==0) { ret= Xorriso_option_find(xorriso, argc, argv, idx, 1); } else if(strcmp(cmd,"follow")==0) { (*idx)++; ret= Xorriso_option_follow(xorriso, arg1, 0); } else if(strcmp(cmd,"for_backup")==0) { ret= Xorriso_option_for_backup(xorriso, 0); } else if(strcmp(cmd,"format")==0) { (*idx)++; ret= Xorriso_option_blank(xorriso, arg1, 1); } else if(strcmp(cmd,"fs")==0) { (*idx)++; ret= Xorriso_option_fs(xorriso, arg1, 0); } else if(strcmp(cmd, "genisoimage_completion")==0) { (*idx)++; ret= Xorriso_option_genisoimage_completion(xorriso, arg1, 0); } else if(strcmp(cmd,"getfacl")==0 || strcmp(cmd,"getfacli")==0) { ret= Xorriso_option_getfacli(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"getfacl_r")==0 || strcmp(cmd,"getfacl_ri")==0) { ret= Xorriso_option_getfacli(xorriso, argc, argv, idx, 1); } else if(strcmp(cmd,"getfattr")==0 || strcmp(cmd,"getfattri")==0) { ret= Xorriso_option_getfacli(xorriso, argc, argv, idx, 2); } else if(strcmp(cmd,"getfattr_r")==0 || strcmp(cmd,"getfattr_ri")==0) { ret= Xorriso_option_getfacli(xorriso, argc, argv, idx, 1 | 2); } else if(strcmp(cmd,"gid")==0) { (*idx)++; ret= Xorriso_option_gid(xorriso,arg1,0); } else if(strcmp(cmd,"grow_blindly")==0) { (*idx)++; ret= Xorriso_option_grow_blindly(xorriso,arg1,0); } else if(strcmp(cmd,"hardlinks")==0) { (*idx)++; ret= Xorriso_option_hardlinks(xorriso, arg1, 0); } else if(strcmp(cmd,"hfsplus")==0) { (*idx)++; ret= Xorriso_option_hfsplus(xorriso, arg1, 0); } else if(strcmp(cmd,"help")==0) { Xorriso_option_help(xorriso,0); } else if(strcmp(cmd,"hide")==0) { (*idx)+= 1; ret= Xorriso_option_hide(xorriso, arg1, argc, argv, idx, 1); } else if(strcmp(cmd,"history")==0) { /* add to readline history */ (*idx)++; ret= Xorriso_option_history(xorriso, arg1, 0); } else if(strcmp(cmd,"indev")==0) { (*idx)++; ret= Xorriso_option_dev(xorriso, arg1, 1); } else if(strcmp(cmd,"in_charset")==0) { (*idx)++; ret= Xorriso_option_charset(xorriso, arg1, 1); } else if(strcmp(cmd,"iso_nowtime")==0) { (*idx)++; ret= Xorriso_option_iso_nowtime(xorriso, arg1, 0); } else if(strcmp(cmd,"iso_rr_pattern")==0) { (*idx)++; ret= Xorriso_option_iso_rr_pattern(xorriso, arg1, 0); } else if(strcmp(cmd,"jigdo")==0) { (*idx)+= 2; ret= Xorriso_option_jigdo(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"joliet")==0) { (*idx)++; ret= Xorriso_option_joliet(xorriso, arg1, 0); } else if(strcmp(cmd,"joliet_map")==0) { (*idx)++; ret= Xorriso_option_joliet_map(xorriso, arg1, 0); } else if(strcmp(cmd, "launch_frontend") == 0) { ret= Xorriso_option_launch_frontend(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"lfa_flags")==0) { (*idx)++; ret= Xorriso_option_lfa_flags(xorriso, arg1, 0); } else if(strcmp(cmd, "list_arg_sorting") == 0) { ret= Xorriso_option_list_arg_sorting(xorriso, 0); } else if(strcmp(cmd, "list_delimiter") == 0) { (*idx)++; ret= Xorriso_option_list_delimiter(xorriso, arg1, 0); } else if(strcmp(cmd, "list_extras") == 0) { (*idx)++; ret= Xorriso_option_list_extras(xorriso, arg1, 0); } else if(strcmp(cmd,"list_formats")==0) { ret= Xorriso_option_list_formats(xorriso, 0); } else if(strcmp(cmd,"list_profiles")==0) { (*idx)++; ret= Xorriso_option_list_profiles(xorriso, arg1, 0); } else if(strcmp(cmd,"list_speeds")==0) { ret= Xorriso_option_list_speeds(xorriso, 0); } else if(strcmp(cmd, "lns") == 0 || strcmp(cmd, "lnsi") == 0) { (*idx)+= 2; ret= Xorriso_option_lnsi(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"load")==0) { (*idx)+= 2; ret= Xorriso_option_load(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"local_charset")==0) { (*idx)++; ret= Xorriso_option_charset(xorriso, arg1, 4); } else if(strcmp(cmd,"logfile")==0) { (*idx)+= 2; ret= Xorriso_option_logfile(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"lsattr")==0 || strcmp(cmd,"lsattri")==0 || strcmp(cmd,"lsattrd")==0 || strcmp(cmd,"lsattrdi")==0) { ret= Xorriso_option_lsi(xorriso, argc, argv, idx, 16 | (cmd[6] == 'd') << 3); } else if(strcmp(cmd,"ls")==0 || strcmp(cmd,"lsi")==0 || strcmp(cmd,"lsl")==0 || strcmp(cmd,"lsli")==0) { ret= Xorriso_option_lsi(xorriso, argc, argv, idx, (cmd[2]=='l')); } else if(strcmp(cmd,"lsd")==0 || strcmp(cmd,"lsdi")==0 || strcmp(cmd,"lsdl")==0 || strcmp(cmd,"lsdli")==0) { ret= Xorriso_option_lsi(xorriso, argc, argv, idx, (cmd[3]=='l')|8); } else if(strcmp(cmd,"lsdx")==0 || strcmp(cmd,"lsdlx")==0) { ret= Xorriso_option_lsx(xorriso, argc, argv, idx, (cmd[3]=='l')|8); } else if(strcmp(cmd,"lsx")==0 || strcmp(cmd,"lslx")==0) { ret= Xorriso_option_lsx(xorriso, argc, argv, idx, (cmd[2]=='l')); } else if(strcmp(cmd,"map")==0) { (*idx)+= 2; ret= Xorriso_option_map(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"map_l")==0) { ret= Xorriso_option_map_l(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"map_single")==0) { (*idx)+= 2; ret= Xorriso_option_map(xorriso, arg1, arg2, 32); } else if(strcmp(cmd,"mark")==0) { (*idx)++; ret= Xorriso_option_mark(xorriso, arg1, 0); } else if(strcmp(cmd, "md5")==0) { (*idx)++; ret= Xorriso_option_md5(xorriso, arg1, 0); } else if(strcmp(cmd,"mkdir")==0 || strcmp(cmd,"mkdiri")==0) { ret= Xorriso_option_mkdiri(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd, "modesty_on_drive")==0) { (*idx)++; ret= Xorriso_option_modesty_on_drive(xorriso, arg1, 0); } else if(strcmp(cmd, "mount") == 0 || strcmp(cmd, "mount_cmd") == 0) { (*idx)+= 4; if((*idx)>argc) { sprintf(xorriso->info_text, "-%s: Not enough arguments. Needed are: device entity id path", cmd); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; } else ret= Xorriso_option_mount(xorriso, arg1, arg2, argv[(*idx)-2], argv[(*idx)-1], (strcmp(cmd, "mount_cmd") == 0)); } else if(strcmp(cmd, "mount_opts")==0) { (*idx)++; ret= Xorriso_option_mount_opts(xorriso, arg1, 0); } else if(strcmp(cmd, "move")==0) { (*idx)+= 2; ret= Xorriso_option_move(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"msg_op") == 0) { (*idx)+= 2; ret= Xorriso_option_msg_op(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"mv")==0 || strcmp(cmd,"mvi")==0) { ret= Xorriso_option_mvi(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"named_pipe_loop")==0) { if((*idx) + 3 < argc) arg4= argv[(*idx) + 3]; else arg4= ""; (*idx)+= 4; ret= Xorriso_option_named_pipe_loop(xorriso, arg1, arg2, arg3, arg4, 0); if(ret == 3) goto ex; } else if(strcmp(cmd,"no_rc")==0) { ret= Xorriso_option_no_rc(xorriso, 0); } else if(strcmp(cmd,"not_leaf")==0) { (*idx)++; ret= Xorriso_option_not_leaf(xorriso, arg1, 0); } else if(strcmp(cmd,"not_list")==0) { (*idx)++; ret= Xorriso_option_not_list(xorriso, arg1, 0); } else if(strcmp(cmd,"not_mgt")==0) { (*idx)++; ret= Xorriso_option_not_mgt(xorriso, arg1, 0); } else if(strcmp(cmd,"not_paths")==0) { ret= Xorriso_option_not_paths(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"options_from_file")==0) { (*idx)++; ret= Xorriso_option_options_from_file(xorriso,arg1,0); if(ret==3) goto ex; } else if(strcmp(cmd,"osirrox")==0) { (*idx)++; ret= Xorriso_option_osirrox(xorriso,arg1,0); } else if(strcmp(cmd,"outdev")==0) { (*idx)++; ret= Xorriso_option_dev(xorriso, arg1, 2); } else if(strcmp(cmd,"out_charset")==0) { (*idx)++; ret= Xorriso_option_charset(xorriso, arg1, 2); } else if(strcmp(cmd,"overwrite")==0) { (*idx)++; ret= Xorriso_option_overwrite(xorriso,arg1,0); } else if(strcmp(cmd,"pacifier")==0) { (*idx)++; ret= Xorriso_option_pacifier(xorriso, arg1, 0); } else if(strcmp(cmd,"padding")==0) { (*idx)++; ret= Xorriso_option_padding(xorriso, arg1, 0); } else if(strcmp(cmd,"page")==0) { (*idx)+= 2; num1= num2= 0; sscanf(arg1,"%d",&num1); sscanf(arg2,"%d",&num2); if(num1<0) num1= 0; if(arg1[0]==0) num1= 16; if(num2<=0) num2= 80; ret= Xorriso_option_page(xorriso, num1, num2, 0); } else if(strcmp(cmd,"paste_in")==0) { (*idx)+= 4; if((*idx)>argc) { sprintf(xorriso->info_text, "-paste_in: Not enough arguments. Needed are: disk_path start count so_rr_path"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; } else ret= Xorriso_option_paste_in(xorriso, arg1, arg2, argv[(*idx)-2], argv[(*idx)-1], 0); } else if(strcmp(cmd,"path-list")==0 || strcmp(cmd,"path_list")==0) { (*idx)++; ret= Xorriso_option_path_list(xorriso, arg1, 0); } else if(strcmp(cmd,"pathspecs")==0) { (*idx)++; ret= Xorriso_option_pathspecs(xorriso, arg1, 0); } else if(strcmp(cmd,"pkt_output")==0) { (*idx)++; ret= Xorriso_option_pkt_output(xorriso, arg1, 0); } else if(strcmp(cmd,"preparer_id")==0) { (*idx)++; ret= Xorriso_option_preparer_id(xorriso, arg1, 0); } else if(strcmp(cmd,"print")==0) { (*idx)++; ret= Xorriso_option_print(xorriso, arg1, 0); } else if(strcmp(cmd,"print_info")==0) { (*idx)++; ret= Xorriso_option_print(xorriso, arg1, 1); } else if(strcmp(cmd,"print_mark")==0) { (*idx)++; ret= Xorriso_option_print(xorriso, arg1, 2); } else if(strcmp(cmd,"print_size")==0) { Xorriso_option_print_size(xorriso, 0); } else if(strcmp(cmd,"prompt")==0) { (*idx)++; ret= Xorriso_option_prompt(xorriso, arg1, 0); } else if(strcmp(cmd,"prog")==0) { (*idx)++; ret= Xorriso_option_prog(xorriso, arg1, 0); } else if(strcmp(cmd,"publisher")==0) { (*idx)++; Xorriso_option_publisher(xorriso, arg1, 0); } else if(strcmp(cmd,"pvd_info")==0) { Xorriso_option_pvd_info(xorriso, 0); } else if(strcmp(cmd,"pwd")==0 || strcmp(cmd,"pwdi")==0) { Xorriso_option_pwdi(xorriso, 0); } else if(strcmp(cmd,"pwdx")==0) { Xorriso_option_pwdx(xorriso, 0); } else if(strcmp(cmd,"quoted_not_list")==0) { (*idx)++; ret= Xorriso_option_not_list(xorriso, arg1, 1); } else if(strcmp(cmd,"quoted_path_list")==0) { (*idx)++; ret= Xorriso_option_path_list(xorriso, arg1, 1); } else if(strcmp(cmd,"read_fs")==0) { (*idx)++; ret= Xorriso_option_read_fs(xorriso, arg1, 0); } else if(strcmp(cmd,"read_mkisofsrc")==0) { ret= Xorriso_option_read_mkisofsrc(xorriso, 0); } else if(strcmp(cmd,"read_speed")==0) { (*idx)++; ret= Xorriso_option_speed(xorriso, arg1, 1); } else if(strcmp(cmd,"reassure")==0) { (*idx)++; ret= Xorriso_option_reassure(xorriso, arg1, 0); } else if(strcmp(cmd,"report_about")==0) { (*idx)++; ret= Xorriso_option_report_about(xorriso, arg1, 0); } else if(strcmp(cmd,"report_el_torito")==0) { (*idx)++; ret= Xorriso_option_report_el_torito(xorriso, arg1, 0); } else if(strcmp(cmd,"report_system_area")==0) { (*idx)++; ret= Xorriso_option_report_system_area(xorriso, arg1, 0); } else if(strcmp(cmd,"return_with")==0) { (*idx)+= 2; num2= 0; sscanf(arg2,"%d",&num2); ret= Xorriso_option_return_with(xorriso, arg1, num2, 0); } else if(strcmp(cmd,"rm")==0 || strcmp(cmd,"rmi")==0) { ret= Xorriso_option_rmi(xorriso, argc, argv, idx, 0); } else if(strcmp(cmd,"rm_r")==0 || strcmp(cmd,"rm_ri")==0) { ret= Xorriso_option_rmi(xorriso, argc, argv, idx, 1); } else if(strcmp(cmd,"rmdir")==0 || strcmp(cmd,"rmdiri")==0) { ret= Xorriso_option_rmi(xorriso, argc, argv, idx, 2); } else if(strcmp(cmd, "rockridge") == 0) { (*idx)++; ret= Xorriso_option_rockridge(xorriso, arg1, 0); } else if(strcmp(cmd,"rollback")==0) { ret= Xorriso_option_rollback(xorriso, 0); } else if(strcmp(cmd,"rollback_end")==0) { end_ret= Xorriso_option_end(xorriso, 1); ret= Xorriso_eval_problem_status(xorriso, ret, 0); if(ret<0) goto ex; if(end_ret!=2) {ret= 3; goto ex;} } else if(strcmp(cmd,"rom_toc_scan")==0) { (*idx)++; Xorriso_option_rom_toc_scan(xorriso, arg1, 0); } else if(strcmp(cmd,"rr_reloc_dir")==0) { (*idx)++; Xorriso_option_rr_reloc_dir(xorriso, arg1, 0); } else if(strcmp(cmd,"scdbackup_tag")==0) { (*idx)+= 2; ret= Xorriso_option_scdbackup_tag(xorriso, arg1, arg2, 0); } else if(strcmp(cmd, "scsi_dev_family") == 0) { (*idx)++; ret= Xorriso_option_scsi_dev_family(xorriso, arg1, 0); } else if(strcmp(cmd, "scsi_log") == 0) { (*idx)++; ret= Xorriso_option_scsi_log(xorriso, arg1, 0); } else if(strcmp(cmd,"session_log")==0) { (*idx)++; ret= Xorriso_option_session_log(xorriso, arg1, 0); } else if(strcmp(cmd, "session_string") == 0) { (*idx)+= 4; if((*idx)>argc) { sprintf(xorriso->info_text, "-%s: Not enough arguments. Needed are: device entity id format", cmd); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; } else ret= Xorriso_option_mount(xorriso, arg1, arg2, argv[(*idx)-2], argv[(*idx)-1], 2); } else if(strcmp(cmd,"setfacl")==0 || strcmp(cmd,"setfacli")==0) { (*idx)+= 1; ret= Xorriso_option_setfacli(xorriso, arg1, argc, argv, idx, 0); } else if(strcmp(cmd,"setfacl_list")==0 || strcmp(cmd,"setfacl_listi")==0) { (*idx)+= 1; ret= Xorriso_option_setfacl_listi(xorriso, arg1, 0); } else if(strcmp(cmd,"setfacl_r")==0 || strcmp(cmd,"setfacl_ri")==0) { (*idx)+= 1; ret= Xorriso_option_setfacli(xorriso, arg1, argc, argv, idx, 1); } else if(strcmp(cmd,"setfattr")==0 || strcmp(cmd,"setfattri")==0) { (*idx)+= 2; ret= Xorriso_option_setfattri(xorriso, arg1, arg2, argc, argv, idx, 0); } else if(strcmp(cmd,"setfattr_list")==0 || strcmp(cmd,"setfattr_listi")==0) { (*idx)+= 1; ret= Xorriso_option_setfattr_listi(xorriso, arg1, 0); } else if(strcmp(cmd,"setfattr_r")==0 || strcmp(cmd,"setfattr_ri")==0) { (*idx)+= 2; ret= Xorriso_option_setfattri(xorriso, arg1, arg2, argc, argv, idx, 1); } else if(strcmp(cmd,"set_filter")==0 || strcmp(cmd,"set_filter_r")==0) { (*idx)+= 1; ret= Xorriso_option_set_filter(xorriso, arg1, argc, argv, idx, strcmp(cmd,"set_filter_r")==0); } else if(strcmp(cmd,"show_stream")==0 || strcmp(cmd,"show_stream_r")==0) { ret= Xorriso_option_set_filter(xorriso, "", argc, argv, idx, (strcmp(cmd,"show_stream_r")==0) | 2 | 4); } else if(strcmp(cmd,"sh_style_result")==0) { (*idx)++; ret= Xorriso_option_sh_style_result(xorriso, arg1, 0); } else if(strcmp(cmd,"signal_handling")==0) { (*idx)++; ret= Xorriso_option_signal_handling(xorriso, arg1, 0); } else if(strcmp(cmd,"sleep")==0) { (*idx)++; ret= Xorriso_option_sleep(xorriso, arg1, 0); } else if(strcmp(cmd,"speed")==0) { (*idx)++; ret= Xorriso_option_speed(xorriso, arg1, 0); } else if(strcmp(cmd,"split_size")==0) { (*idx)++; ret= Xorriso_option_split_size(xorriso, arg1, 0); } else if(strcmp(cmd,"status")==0) { (*idx)++; ret= Xorriso_option_status(xorriso, arg1, 0); } else if(strcmp(cmd,"status_history_max")==0) { (*idx)++; sscanf(arg1,"%d",&num1); ret= Xorriso_option_status_history_max(xorriso, num1, 0); } else if(strcmp(cmd,"stdio_sync")==0) { (*idx)++; ret= Xorriso_option_stdio_sync(xorriso, arg1, 0); } else if(strcmp(cmd,"stream_recording")==0) { (*idx)++; ret= Xorriso_option_stream_recording(xorriso, arg1, 0); } else if(strcmp(cmd,"system_id")==0) { (*idx)++; ret= Xorriso_option_system_id(xorriso, arg1, 0); } else if(strcmp(cmd,"tell_media_space")==0) { Xorriso_option_tell_media_space(xorriso, 0); } else if(strcmp(cmd,"temp_mem_limit")==0) { (*idx)++; ret= Xorriso_option_temp_mem_limit(xorriso, arg1, 0); } else if(strcmp(cmd,"test")==0) { /* This option does not exist. */ /* install temporary test code here */; if (0) { /* Test setup for for Xorriso_push_outlists() et.al. */ /* Test setup for Xorriso_parse_line */ int stack_handle = -1, line_count= 0; struct Xorriso_lsT *result_list, *info_list; int Xorriso_process_msg_lists(struct XorrisO *xorriso, struct Xorriso_lsT *result_list, struct Xorriso_lsT *info_list, int *line_count, int flag); (*idx)++; if(strcmp(arg1, "push") == 0) { ret= Xorriso_push_outlists(xorriso, &stack_handle, 3); fprintf(stderr, "xorriso -test: push = %d, handle = %d\n", ret, stack_handle); } else if(strcmp(arg1, "pull") == 0) { ret= Xorriso_pull_outlists(xorriso, -1, &result_list, &info_list, 0); fprintf(stderr, "xorriso -test: pull = %d\n", ret); if(ret > 0) { ret= Xorriso_process_msg_lists(xorriso, result_list, info_list, &line_count, 0); fprintf(stderr, "xorriso -test: Xorriso_process_msg_lists() = %d, line_count = %d\n", ret, line_count); } } else if(strcmp(arg1, "fetch") == 0) { ret= Xorriso_fetch_outlists(xorriso, -1, &result_list, &info_list, 0); fprintf(stderr, "xorriso -test: fetch = %d\n", ret); if(ret > 0) { ret= Xorriso_process_msg_lists(xorriso, result_list, info_list, &line_count, 0); fprintf(stderr, "xorriso -test: Xorriso_process_msg_lists() = %d, line_count = %d\n", ret, line_count); } } else if(strcmp(arg1, "peek") == 0) { ret= Xorriso_peek_outlists(xorriso, -1, 0, 0); fprintf(stderr, "xorriso -test: peek = %d\n", ret); } else if(strcmp(arg1, "sleep_peek") == 0) { usleep(1000000); ret= Xorriso_peek_outlists(xorriso, -1, 0, 0); fprintf(stderr, "xorriso -test: sleep_peek = %d\n", ret); } else if(strcmp(arg1, "peek_loop") == 0) { ret= Xorriso_peek_outlists(xorriso, -1, 3, 4); fprintf(stderr, "xorriso -test: peek_loop = %d\n", ret); } else if(strcmp(arg1, "start") == 0) { ret= Xorriso_start_msg_watcher(xorriso, NULL, NULL, NULL, NULL, 0); fprintf(stderr, "xorriso -test: Xorriso_start_msg_watcher() = %d\n", ret); } else if(strcmp(arg1, "stop") == 0) { ret= Xorriso_stop_msg_watcher(xorriso, 0); fprintf(stderr, "xorriso -test: Xorriso_stop_msg_watcher() = %d\n", ret); } else if(strcmp(arg1, "help") == 0) { fprintf(stderr, "-test [mode] [arguments]\n"); fprintf(stderr, " push\n"); fprintf(stderr, " perform Xorriso_push_outlists()\n"); fprintf(stderr, " pull\n"); fprintf(stderr, " perform Xorriso_pull_outlists() and show messages\n"); fprintf(stderr, " fetch\n"); fprintf(stderr, " perform Xorriso_fetch_outlists() and show\n"); fprintf(stderr, " peek\n"); fprintf(stderr, " perform Xorriso_peek_outlists()\n"); fprintf(stderr, " sleep_peek\n"); fprintf(stderr, " sleep 1 s and perform Xorriso_peek_outlists()\n"); fprintf(stderr, " peek_loop\n"); fprintf(stderr, " wait for up to 3s in Xorriso_peek_outlists()\n"); fprintf(stderr, " for return value 0 or -1\n"); fprintf(stderr, " start\n"); fprintf(stderr, " perform Xorriso_start_msg_watcher()\n"); fprintf(stderr, " stop\n"); fprintf(stderr, " perform Xorriso_stop_msg_watcher()\n"); } else { fprintf(stderr, "xorriso -test: unknown mode: %s\n", arg1); } ret= 0; } } else if(strcmp(cmd,"toc")==0) { Xorriso_option_toc(xorriso, 0); } else if(strcmp(cmd,"toc_info_type")==0) { (*idx)++; Xorriso_option_toc_info_type(xorriso, arg1, 0); } else if(strcmp(cmd,"toc_of")==0) { (*idx)++; Xorriso_option_toc_of(xorriso, arg1, 0); } else if(strcmp(cmd,"truncate_overwritable")==0) { (*idx)+= 3; ret= Xorriso_option_truncate_overwritable(xorriso, arg1, arg2, arg3, 0); } else if(strcmp(cmd,"uid")==0) { (*idx)++; ret= Xorriso_option_uid(xorriso,arg1,0); } else if(strcmp(cmd,"unregister_filter")==0) { (*idx)++; ret= Xorriso_option_unregister_filter(xorriso, arg1, 0); } else if(strcmp(cmd,"update")==0) { (*idx)+= 2; ret= Xorriso_option_update(xorriso, arg1, arg2, 1); } else if(strcmp(cmd,"update_l") == 0 || strcmp(cmd,"update_lx") == 0) { ret= Xorriso_option_map_l(xorriso, argc, argv, idx, 2<<8); } else if(strcmp(cmd,"update_li") == 0) { ret= Xorriso_option_map_l(xorriso, argc, argv, idx, 5 << 8); } else if(strcmp(cmd,"update_lxi") == 0) { ret= Xorriso_option_map_l(xorriso, argc, argv, idx, 4 << 8); } else if(strcmp(cmd,"update_r")==0) { (*idx)+= 2; ret= Xorriso_option_update(xorriso, arg1, arg2, 1|8); } else if(strcmp(cmd,"use_immed_bit")==0) { (*idx)++; ret= Xorriso_option_use_immed_bit(xorriso, arg1, 0); } else if(strcmp(cmd,"use_readline")==0) { (*idx)++; ret= Xorriso_option_use_readline(xorriso, arg1, 0); } else if(strcmp(cmd,"version")==0){ ret= Xorriso_option_version(xorriso, 0); } else if(strcmp(cmd,"volset_id")==0) { (*idx)++; ret= Xorriso_option_volset_id(xorriso, arg1, 0); } else if(strcmp(cmd,"volid")==0) { (*idx)++; ret= Xorriso_option_volid(xorriso,arg1,0); } else if(strcmp(cmd,"volume_date")==0) { (*idx)+= 2; ret= Xorriso_option_volume_date(xorriso, arg1, arg2, 0); } else if(strcmp(cmd,"write_type")==0) { (*idx)++; ret= Xorriso_option_write_type(xorriso, arg1, 0); } else if(strcmp(cmd, "x") == 0) { /* only in effect in Xorriso_prescan_args() */; } else if(strcmp(cmd,"xattr")==0) { (*idx)++; ret= Xorriso_option_xattr(xorriso, arg1, 0); } else if(strcmp(cmd,"zisofs")==0) { (*idx)++; ret= Xorriso_option_zisofs(xorriso, arg1, 0); } else if(strcmp(cmd, xorriso->list_delimiter)==0){ /* tis ok */; } else if(was_dashed) { if(xorriso->add_plainly>1) goto add_plain_argument; unknown_option:; sprintf(xorriso->info_text, "Not a known command: '%s'\n", original_cmd); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto eval_any_problems;} } else { if(xorriso->add_plainly<=0) goto unknown_option; add_plain_argument:; mem_idx= *idx; (*idx)--; ret= Xorriso_option_add(xorriso, (*idx)+1, argv, idx, 0); (*idx)= mem_idx; } eval_any_problems: ret= Xorriso_eval_problem_status(xorriso, ret, 0); if(ret<0) goto ex; if(*idx < argc && !(flag & 4)) goto next_command; ex:; fflush(stdout); Xorriso_free_meM(cmd_data); return(ret); } int Xorriso_parse_line(struct XorrisO *xorriso, char *line, char *prefix, char *separators, int max_words, int *argc, char ***argv, int flag) { int ret, bsl_mode; char *to_parse, *progname= ""; if(xorriso == NULL && (flag & (32 | 64))) { ret= -2; goto ex; } *argc= 0; *argv= NULL; to_parse= line; if((flag & 1) || xorriso == NULL) bsl_mode= (flag >> 1) & 3; else bsl_mode= xorriso->bsl_interpretation & 3; if(prefix[0]) { if(strncmp(line, prefix, strlen(prefix)) == 0) { to_parse= line + strlen(prefix); } else { ret= 2; goto ex; } } if(xorriso != NULL) progname= xorriso->progname; ret= Sfile_sep_make_argv(progname, to_parse, separators, max_words, argc, argv, (!(flag & 32)) | 4 | (bsl_mode << 5)); if(ret < 0) { if(xorriso != NULL) Xorriso_msgs_submit(xorriso, 0, "Severe lack of resources during command line parsing", 0, "FATAL", 0); ret= -1; goto ex; } if(ret == 0) { if((flag & 64) && xorriso != NULL) { sprintf(xorriso->info_text, "Incomplete quotation in %s line: %s", (flag & 32) ? "command" : "parsed", to_parse); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } goto ex; } ret= 1; ex:; if(ret <= 0) Sfile_sep_make_argv("", "", "", 0, argc, argv, 2); /* release memory */ return(ret); } void Xorriso__dispose_words(int *argc, char ***argv) { Sfile_make_argv("", "", argc, argv, 2); /* release memory */ } int Xorriso_execute_option(struct XorrisO *xorriso, char *line, int flag) /* bit0-bit15 are forwarded to Xorriso_interpreter bit16= no pageing of info lines bit17= print === bar even if xorriso->found<0 */ { int ret,argc= 0, idx= 1; char **argv= NULL; double tdiff; struct timeval tv; gettimeofday(&tv, NULL); Xorriso_reset_counters(xorriso,0); xorriso->idle_time= 0.0; tdiff= tv.tv_sec+(1.e-6*(double) tv.tv_usec); ret= Xorriso_parse_line(xorriso, line, "", "", 0, &argc, &argv, 32 | 64); if(ret <= 0) goto ex; if(argc<2) {ret= 1; goto ex;} if(argv[1][0]=='#') {ret= 1; goto ex;} ret= Xorriso_interpreter(xorriso, argc, argv, &idx, flag&0xffff); if(ret<0) goto ex; gettimeofday(&tv, NULL); tdiff= tv.tv_sec+(1.e-6*(double) tv.tv_usec)-tdiff-xorriso->idle_time; if(tdiff<0.001) tdiff= 0.001; if(xorriso->error_count>0) { sprintf(xorriso->info_text, "----------------------------- %7.f errors encountered\n", xorriso->error_count); Xorriso_info(xorriso,!(flag&(1<<16))); } /* ??? >>> print elapsed time tdiff ? */; if((flag&(1<<17)) && !xorriso->bar_is_fresh) { sprintf(xorriso->info_text,"============================\n"); Xorriso_info(xorriso,0); xorriso->bar_is_fresh= 1; } Xorriso_reset_counters(xorriso,0); ex:; Sfile_make_argv("", "", &argc, &argv, 2); /* release memory */ return(ret); } int Xorriso_dialog(struct XorrisO *xorriso, int flag) { int ret, line_size= 2 * SfileadrL; char *line= NULL; Xorriso_alloc_meM(line, char, line_size); if(!xorriso->dialog) {ret= 1; goto ex;} if(xorriso->abort_on_is_default) Xorriso_option_abort_on(xorriso, "NEVER", 0); xorriso->is_dialog= 1; while(1) { if(xorriso->pending_option[0]!=0) { Xorriso_mark(xorriso,0); strcpy(line,xorriso->pending_option); xorriso->pending_option[0]= 0; } else { if(!xorriso->bar_is_fresh) { sprintf(xorriso->info_text,"============================\n"); Xorriso_info(xorriso,0); xorriso->bar_is_fresh= 1; } sprintf(xorriso->info_text,"enter option and arguments :\n"); Xorriso_info(xorriso,0); Xorriso_mark(xorriso,0); ret= Xorriso_dialog_input(xorriso,line, line_size, 4); if(ret<=0) break; } sprintf(xorriso->info_text, "==============================================================\n"); Xorriso_info(xorriso,0); ret= Xorriso_execute_option(xorriso,line,1<<17); if(ret<0) goto ex; if(ret==3) goto ex; xorriso->did_something_useful= 1; xorriso->no_volset_present= 0; /* Re-enable "No ISO image present." */ } ret= 1; ex:; xorriso->is_dialog= 0; Xorriso_free_meM(line); return(ret); } /* @return 1=replaced , 2=not replaced , <=0 = error */ int Xorriso_replace_arg_by_bsl(struct XorrisO *xorriso, char **arg, char **argpt, int flag) { int ret, eaten, l; if(!(xorriso->bsl_interpretation & 16)) return(2); l= strlen(*argpt); Xorriso_free_meM(*arg); Xorriso_alloc_meM(*arg, char, l + 1); strcpy(*arg, *argpt); *argpt= *arg; ret= Sfile_bsl_interpreter(*arg, l, &eaten, 0); ex:; return(ret); } int Xorriso_prescan_args(struct XorrisO *xorriso, int argc, char **argv, int flag) /* bit0= do not interpret argv[1] bit1= complain about inknown arguments */ /* return: <0 error 0 end program 1 ok, go on */ { int i, ret, was_dashed, num2, arg_count; int advice, mem_add_plainly, error_seen= 0, mem_bsl; int was_report_about= 0, was_abort_on= 0, was_return_with= 0; int was_signal_handling= 0, was_scsi_log= 0, cmd_data_size= 5 * SfileadrL; char *cmd, *original_cmd, *cmd_data= NULL, *arg1, *arg2; char *arg1_data= NULL, *arg2_data= NULL; char mem_list_delimiter[81]; strcpy(mem_list_delimiter, xorriso->list_delimiter); mem_add_plainly= xorriso->add_plainly; mem_bsl= xorriso->bsl_interpretation; Xorriso_alloc_meM(cmd_data, char, cmd_data_size); for(i=1+(flag&1);i<argc;i++) { original_cmd= cmd= argv[i]; was_dashed= 0; was_dashed= Xorriso_normalize_command(xorriso, original_cmd, i, cmd_data, cmd_data_size, &cmd, 0); if(was_dashed<0) {ret= -1; goto ex;} arg1= ""; if(i + 1 < argc) { arg1= argv[i + 1]; ret= Xorriso_replace_arg_by_bsl(xorriso, &arg1_data, &arg1, 0); if(ret <= 0) goto ex; } arg2= ""; if(i + 2 < argc) { arg2= argv[i + 2]; ret= Xorriso_replace_arg_by_bsl(xorriso, &arg2_data, &arg2, 0); if(ret <= 0) goto ex; } if(i>1) xorriso->did_something_useful= 1; if(i==1 && argc==2) { if(strcmp(cmd,"prog_help")==0) { i++; Xorriso_option_prog_help(xorriso,arg1,0); xorriso->did_something_useful= 1; {ret= 0; goto ex;} } else if(strcmp(cmd,"help")==0) { if(xorriso->argument_emulation == 1) { Xorriso_genisofs_help(xorriso, 0); } else if(xorriso->argument_emulation == 2) { Xorriso_cdrskin_help(xorriso, 0); } else { Xorriso_option_help(xorriso,0); } xorriso->did_something_useful= 1; {ret= 0; goto ex;} } } else if(i==1 && strcmp(cmd,"no_rc")==0) { ret= Xorriso_option_no_rc(xorriso, 0); if(ret<=0) error_seen= 1; } else if(xorriso->argument_emulation == 1) { /* mkisofs emulation */ if(xorriso->dev_fd_1 < 0) goto protect_stdout; {ret= 1; goto ex;} } else if(xorriso->argument_emulation == 2) { /* cdrecord emulation */ if(xorriso->dev_fd_1 < 0) if(Xorriso_cdrskin_uses_stdout(xorriso, argc - 1 - (flag & 1), argv + 1 + (flag & 1), 0)) goto protect_stdout; {ret= 1; goto ex;} } else if((strcmp(cmd,"dev")==0 || strcmp(cmd,"outdev")==0 || strcmp(cmd,"indev")==0) && (strcmp(arg1,"stdio:/dev/fd/1")==0 || strcmp(arg1,"-")==0) && xorriso->dev_fd_1<0) { /* Detach fd 1 from externally perceived stdout and attach it to stderr. Keep dev_fd_1 connected to external stdout. dev_fd_1 is to be used when "stdio:/dev/fd/1" is interpreted as drive address. */ protect_stdout:; ret= Xorriso_protect_stdout(xorriso, 0); if(ret == 1) { sprintf(xorriso->info_text, "Encountered - or stdio:/dev/fd/1 as possible write target."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); sprintf(xorriso->info_text, "Redirecting nearly all text message output to stderr."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); sprintf(xorriso->info_text, "Disabling use of libreadline."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } if(xorriso->argument_emulation >= 1 && xorriso->argument_emulation <=2) {ret= 1; goto ex;} } else if(strcmp(cmd,"abort_on")==0 && was_dashed == 1) { i++; if(!was_abort_on) Xorriso_option_abort_on(xorriso, arg1, 0); was_abort_on= 1; } else if(strcmp(cmd,"report_about")==0 && was_dashed == 1) { i++; if(!was_report_about) Xorriso_option_report_about(xorriso, arg1, 0); was_report_about= 1; } else if(strcmp(cmd,"return_with")==0 && was_dashed == 1) { i+= 2; num2= 0; sscanf(arg2,"%d",&num2); if(!was_return_with) Xorriso_option_return_with(xorriso, arg1, num2, 0); was_return_with= 1; } else if(strcmp(cmd,"as")==0 && was_dashed == 1) { ret= Xorriso_count_args(xorriso, argc - i, argv + i, &arg_count, 1); if(ret == 1) { i+= arg_count; if((strcmp(arg1, "cdrecord")==0 || strcmp(arg1, "wodim")==0 || strcmp(arg1, "cdrskin")==0 || strcmp(arg1, "xorrecord")==0) && xorriso->dev_fd_1 < 0) if(Xorriso_cdrskin_uses_stdout(xorriso, arg_count - 1, argv + i - arg_count + 2, 0)) goto protect_stdout; } if(was_dashed == 1) { if((strcmp(arg1, "mkisofs")==0 || strcmp(arg1, "genisoimage")==0 || strcmp(arg1, "genisofs")==0 || strcmp(arg1, "xorrisofs")==0) && xorriso->dev_fd_1 < 0) goto protect_stdout; } } else if(strcmp(cmd, "list_delimiter") == 0) { /* Needed for interpreting other args. Gets reset after prescan. */ i++; ret= Xorriso_option_list_delimiter(xorriso, arg1, 0); if(ret <= 0) error_seen= 1; } else if(strcmp(cmd, "add_plainly") == 0) { i++; ret= Xorriso_option_add_plainly(xorriso, arg1, 0); if(ret <= 0) error_seen= 1; if(xorriso->add_plainly == 3) { /* All further arguments count as pathspecs */ {ret= 1; goto ex;} } } else if(strcmp(cmd, "scsi_log") == 0 && was_dashed == 1) { i++; if(!was_scsi_log) Xorriso_option_scsi_log(xorriso, arg1, 0); was_scsi_log= 1; } else if(strcmp(cmd, "signal_handling") == 0 && was_dashed == 1) { i++; if(!was_signal_handling) Xorriso_option_signal_handling(xorriso, arg1, 1); /* no install */ was_signal_handling= 1; } else if(strcmp(original_cmd, "-x") == 0) { xorriso->arrange_args= 1; } else if(strcmp(cmd, "backslash_codes") == 0) { i++; ret= Xorriso_option_backslash_codes(xorriso, arg1, 0); if(ret <= 0) error_seen= 1; } else { ret= Xorriso_count_args(xorriso, argc - i, argv + i, &arg_count, 1); if(ret == 1) { i+= arg_count; } else if((flag & 2) && ((was_dashed && xorriso->add_plainly <= 1) || xorriso->add_plainly <= 0)) { sprintf(xorriso->info_text, "Not a known command: '%s'\n", original_cmd); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); error_seen= 1; } } } ret= 1; ex:; strcpy(xorriso->list_delimiter, mem_list_delimiter); xorriso->add_plainly= mem_add_plainly; xorriso->bsl_interpretation= mem_bsl; Xorriso_free_meM(arg1_data); Xorriso_free_meM(arg2_data); Xorriso_free_meM(cmd_data); if(error_seen && ret > 0) { advice= Xorriso_eval_problem_status(xorriso, 0, 0); if(advice < 0) ret= -1; } return(ret); } int Xorriso_read_as_mkisofsrc(struct XorrisO *xorriso, char *path, int flag) { int ret, linecount= 0; FILE *fp= NULL; char *sret, *line= NULL, *cpt, *wpt; Xorriso_alloc_meM(line, char, SfileadrL); ret= Xorriso_afile_fopen(xorriso, path, "rb", &fp, 1 | 2); if(ret <= 0) {ret= 0; goto ex;} while(1) { sret= Sfile_fgets_n(line, SfileadrL - 1, fp, 0); if(sret == NULL) { if(ferror(fp)) {ret= 0; goto ex;} break; } linecount++; /* Interpret line */ if(line[0] == 0 || line[0] == '#') continue; cpt= strchr(line, '='); if(cpt == NULL) { /* >>> ??? complain ? abort reading ? */; continue; } *cpt= 0; /* Names are not case sensitive */ for(wpt= line; wpt < cpt; wpt++) if(*wpt >= 'a' && *wpt <= 'z') *wpt= toupper(*wpt); /* Remove trailing whitespace from name */ for(wpt= cpt - 1; wpt >= line ; wpt--) if(*wpt == ' ' || *wpt == '\t') *wpt= 0; else break; /* Remove trailing whitespace from value */ for(wpt= cpt + 1 + strlen(cpt + 1) - 1; wpt >= cpt; wpt--) if(*wpt == ' ' || *wpt == '\t') *wpt= 0; else break; /* Remove leading whitespace from value */ for(cpt++; *cpt == ' ' || *cpt == '\t'; cpt++); if(strcmp(line, "APPI") == 0) { ret= Xorriso_option_application_id(xorriso, cpt, 0); } else if(strcmp(line, "COPY") == 0) { ret= Xorriso_option_copyright_file(xorriso, cpt, 0); } else if(strcmp(line, "ABST") == 0) { ret= Xorriso_option_abstract_file(xorriso, cpt, 0); } else if(strcmp(line, "BIBL") == 0) { ret= Xorriso_option_biblio_file(xorriso, cpt, 0); } else if(strcmp(line, "PREP") == 0) { /* Not planned to be implemented. Preparer is xorriso. */ ret= 1; } else if(strcmp(line, "PUBL") == 0) { ret= Xorriso_option_publisher(xorriso, cpt, 0); } else if(strcmp(line, "SYSI") == 0) { ret= Xorriso_option_system_id(xorriso, cpt, 0); } else if(strcmp(line, "VOLI") == 0) { ret= Xorriso_option_volid(xorriso, cpt, 1); } else if(strcmp(line, "VOLS") == 0) { ret= Xorriso_option_volset_id(xorriso, cpt, 0); } else if(strcmp(line, "HFS_TYPE") == 0) { /* Not planned to be implemented */ ret= 1; } else if(strcmp(line, "HFS_CREATOR") == 0) { /* Not planned to be implemented */ ret= 1; } else { /* >>> ??? complain ? abort reading ? */; } if(ret <= 0) goto ex; } xorriso->mkisofsrc_done= 1; ret= 1; ex: if(fp != NULL) fclose(fp); Xorriso_free_meM(line); return(ret); } /* ./.mkisofsrc , getenv("MKISOFSRC") , $HOME/.mkisofsrc , $(basename $0)/.mkisofsrc */ int Xorriso_read_mkisofsrc(struct XorrisO *xorriso, int flag) { char *path= NULL, *cpt; int ret; Xorriso_alloc_meM(path, char, SfileadrL); ret= Xorriso_read_as_mkisofsrc(xorriso, "./.mkisofsrc", 0); if(ret > 0) goto ex; cpt= getenv("MKISOFSRC"); if(cpt != NULL) { strncpy(path, cpt, SfileadrL - 1); path[SfileadrL - 1]= 0; ret= Xorriso_read_as_mkisofsrc(xorriso, path, 0); if(ret > 0) goto ex; } cpt= getenv("HOME"); if(cpt != NULL) { strncpy(path, cpt, SfileadrL - 1 - 11); path[SfileadrL - 1 - 11]= 0; strcat(path, "/.mkisofsrc"); ret= Xorriso_read_as_mkisofsrc(xorriso, path, 0); if(ret > 0) goto ex; } strcpy(path, xorriso->progname); cpt= strrchr(path, '/'); if(cpt != NULL) { strcpy(cpt + 1, ".mkisofsrc"); ret= Xorriso_read_as_mkisofsrc(xorriso, path, 0); if(ret > 0) goto ex; } /* no .mkisofsrc file found */ ret= 2; ex:; Xorriso_free_meM(path); return(ret); } /* https://reproducible-builds.org/specs/source-date-epoch/ and reproducible-builds@lists.alioth.debian.org in august 2016 */ int Xorriso_source_date_epoch(struct XorrisO *xorriso, int flag) { int ret; /* num_text must be able to take the sprintf output of "%.f". num_text + 12 must be able to take "%d" with a 64 bit int. */ char *sec_text, num_text[40]; double dsec= -1.0; time_t tsec; sec_text= getenv("SOURCE_DATE_EPOCH"); if(sec_text == NULL) return(2); sscanf(sec_text, "%lf", &dsec); sprintf(num_text, "%.f", dsec); tsec= dsec; if(dsec < 0 || ((double) tsec) != dsec || strcmp(sec_text, num_text) != 0) { malformed:; Xorriso_msgs_submit(xorriso, 0, "Malformed environment variable SOURCE_DATE_EPOCH encountered", 0, "SORRY", 0); Xorriso_msgs_submit(xorriso, 0, "Unset SOURCE_DATE_EPOCH before starting xorriso or see https://reproducible-builds.org/specs/source-date-epoch/", 0, "HINT", 0); return(0); } ret= Encode_ecma119_17byte(tsec, "00", num_text, 0); if(ret <= 0) goto malformed; strcpy(xorriso->vol_uuid, num_text); xorriso->gpt_guid_mode= 2; /* Disk GUID from vol_uuid */ strcpy(xorriso->all_file_dates, "set_to_mtime"); xorriso->do_override_now_time= 1; xorriso->now_time_override= tsec; Xorriso_set_libisofs_now(xorriso, 0); sprintf(xorriso->info_text, "Environment variable SOURCE_DATE_EPOCH encountered with value %s", sec_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); sprintf(xorriso->info_text, "SOURCE_DATE_EPOCH : -volume_date uuid %s", xorriso->vol_uuid); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); sprintf(xorriso->info_text, "SOURCE_DATE_EPOCH : -volume_date all_file_dates %s", xorriso->all_file_dates); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); sprintf(xorriso->info_text, "SOURCE_DATE_EPOCH : -boot_image any gpt_disk_guid=volume_date_uuid"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); sprintf(xorriso->info_text, "SOURCE_DATE_EPOCH : -iso_nowtime =%.f", (double) tsec); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); return(1); } int Xorriso_read_rc(struct XorrisO *xorriso, int flag) { int ret,i,was_failure= 0,fret; /* Interpret environment variable SOURCE_DATE_EPOCH */ ret= Xorriso_source_date_epoch(xorriso, 0); ret= Xorriso_eval_problem_status(xorriso, ret, 0); if(ret < 0) return(0); if(xorriso->no_rc) return(1); i= xorriso->rc_filename_count-1; Sfile_home_adr_s(".xorrisorc", xorriso->rc_filenames[i], sizeof(xorriso->rc_filenames[i]),0); for(i=0;i<xorriso->rc_filename_count;i++) { ret= Sfile_type(xorriso->rc_filenames[i],1|8); if(ret!=1) continue; ret= Xorriso_option_options_from_file(xorriso,xorriso->rc_filenames[i],0); if(ret>1) return(ret); if(ret==1) continue; /* regular bottom of loop */ was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1); if(fret>=0) continue; return(ret); } if(xorriso->argument_emulation == 1 && !xorriso->mkisofsrc_done) { ret= Xorriso_read_mkisofsrc(xorriso, 0); if(ret <= 0) was_failure= 1; } return(!was_failure); } int Xorriso_make_return_value(struct XorrisO *xorriso, int flag) { int exit_value= 0; if(xorriso->eternal_problem_status >= xorriso->return_with_severity) exit_value= xorriso->return_with_value; if(exit_value) { sprintf(xorriso->info_text, "-return_with %s %d triggered by problem severity %s", xorriso->return_with_text, exit_value, xorriso->eternal_problem_status_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } return(exit_value); } int Xorriso_program_arg_bsl(struct XorrisO *xorriso, int argc, char ***argv, int flag) { int i, ret, eaten, bsl_mem, params_to_come= 0, cmd_data_size= 5 * SfileadrL; int next_is_backslash_codes= 0, next_is_list_delimiter= 0; char **new_argv= NULL, *cmd, *cmd_data= NULL; char mem_list_delimiter[81]; strcpy(mem_list_delimiter, xorriso->list_delimiter); bsl_mem= xorriso->bsl_interpretation; if(argc <= 0) return(0); Xorriso_alloc_meM(cmd_data, char, cmd_data_size); new_argv= (char **) Smem_malloC(argc * sizeof(char *)); if(new_argv == NULL) {ret= -1; goto ex;} for(i= 0; i < argc; i++) { new_argv[i]= strdup((*argv)[i]); if(new_argv[i] == NULL) {ret= -1; goto ex;} if(i == 0) continue; if(xorriso->bsl_interpretation & 16) { ret= Sfile_bsl_interpreter(new_argv[i], strlen(new_argv[i]), &eaten, 0); if(ret <= 0) goto ex; } if(params_to_come == 0) { ret= Xorriso_normalize_command(xorriso, new_argv[i], i, cmd_data, cmd_data_size, &cmd, 0); if(ret < 0) goto ex; if(strcmp(cmd, "backslash_codes") == 0) { params_to_come= 1; next_is_backslash_codes= 1; } else if(strcmp(cmd, "list_delimiter") == 0) { params_to_come= 1; next_is_list_delimiter= 1; } else { ret= Xorriso_count_args(xorriso, argc - i, *argv + i, ¶ms_to_come, 1); if(ret <= 0) goto ex; if(ret != 1) params_to_come= 0; } } else { params_to_come--; if(next_is_backslash_codes) { next_is_backslash_codes= 0; ret= Xorriso_option_backslash_codes(xorriso, new_argv[i], 0); if(ret <= 0) goto ex; } else if(next_is_list_delimiter) { next_is_list_delimiter= 0; ret= Xorriso_option_list_delimiter(xorriso, new_argv[i], 0); if(ret <= 0) goto ex; } } } ret= 1; ex:; Xorriso_free_meM(cmd_data); strcpy(xorriso->list_delimiter, mem_list_delimiter); xorriso->bsl_interpretation= bsl_mem; if(ret <= 0) { if(new_argv != NULL) free((char *) new_argv); } else *argv= new_argv; return(ret); } /* @param flag bit0= prepend wd only if name does not begin by '/' bit1= normalize image path bit2= prepend wd (automatically done if wd[0]!=0) bit3= (with bit1) this is an address in the disk world */ int Xorriso_make_abs_adr(struct XorrisO *xorriso, char *wd, char *name, char adr[], int flag) { char *norm_adr= NULL; int ret; Xorriso_alloc_meM(norm_adr, char, SfileadrL); if((wd[0]!=0 || (flag&4)) && !((flag&1) && name[0]=='/')) { if(strlen(wd)+1>=SfileadrL) goto much_too_long; strcpy(adr, wd); if(name[0]) if(Sfile_add_to_path(adr, name, 0)<=0) { much_too_long:; Xorriso_much_too_long(xorriso, (int) (strlen(adr)+strlen(name)+1), 2); {ret= 0; goto ex;} } } else { if(strlen(name)+1>=SfileadrL) goto much_too_long; strcpy(adr, name); } if(flag&2) { ret= Xorriso_normalize_img_path(xorriso, "", adr, norm_adr, 1|2|((flag&8)>>1)); if(ret<=0) goto ex; if(norm_adr[0]==0) strcpy(norm_adr, "/"); strcpy(adr, norm_adr); } ret= 1; ex:; Xorriso_free_meM(norm_adr); return(ret); } /* @param flag bit0= do not complain in case of error, but set info_text */ int Xorriso_convert_datestring(struct XorrisO *xorriso, char *cmd, char *time_type, char *timestring, int *t_type, time_t *t, int flag) { int ret; *t_type= 0; if(strcmp(time_type, "a")==0) (*t_type)|= 1; else if(strcmp(time_type, "a-c")==0) (*t_type)|= 1 | 256; else if(strcmp(time_type, "m")==0) (*t_type)|= 4; else if(strcmp(time_type, "m-c")==0) (*t_type)|= 4 | 256; else if(strcmp(time_type, "b")==0) (*t_type)|= 5; else if(strcmp(time_type, "b-c")==0) (*t_type)|= 5 | 256; else if(strcmp(time_type, "c")==0) (*t_type)|= 2 | 256; else { sprintf(xorriso->info_text, "%s: Unrecognized type '%s'", cmd, time_type); if(!(flag & 1)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } ret= Decode_timestring(timestring, t, 0); if(ret<=0) { sprintf(xorriso->info_text, "%s: Cannot decode timestring '%s'", cmd, timestring); if(!(flag & 1)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); return(0); } sprintf(xorriso->info_text, "Understanding timestring '%s' as: %s", timestring, ctime(t)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); return(1); } /* @param flag bit1= do not report memory usage as DEBUG */ int Xorriso_check_temp_mem_limit(struct XorrisO *xorriso, off_t mem, int flag) { char mem_text[80], limit_text[80]; Sfile_scale((double) mem, mem_text,5,1e4,0); if(!(flag&2)) { sprintf(xorriso->info_text, "Temporary memory needed for result sorting : %s", mem_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } if(mem > xorriso->temp_mem_limit) { Sfile_scale((double) xorriso->temp_mem_limit,limit_text,5,1e4,1); sprintf(xorriso->info_text, "Cannot sort. List of matching files exceeds -temp_mem_limit (%s > %s)", mem_text, limit_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); return(0); } return(1); } int Xorriso_wait_child_end(struct XorrisO *xorriso, pid_t child_pid, int *status, int flag) { int ret; do { /* try to read and print the reply */; ret= waitpid(child_pid, status, WNOHANG); if(ret == -1) { if(errno != EINTR) return(0); } else if(ret == 0) { continue; } else { break; } } while(1); /* >>> interpret *status */; return(1); } int Xorriso_make_argv_with_null(struct XorrisO *xorriso, int in_argc, char **in_argv, int *argc, char ***argv, int flag) { int i, ret= 0; *argv= NULL; Xorriso_alloc_meM(*argv, char *, in_argc + 1); for(i= 0; i < in_argc; i++) { Xorriso_alloc_meM((*argv)[i], char, strlen(in_argv[i]) + 1); strcpy((*argv)[i], in_argv[i]); *argc= i + 1; } (*argv)[in_argc]= NULL; ret= 1; ex:; if(ret <= 0) Sfile_destroy_argv(argc, argv, 0); return(ret); } /* @param flag bit0= use env_path to find the desired program bit1= use in_argv rather than parsing cmd to words bit2= -reserved- bit3= demand absolute cmd path return: <=0 : error 1 : done */ int Xorriso_execv(struct XorrisO *xorriso, char *cmd, int in_argc, char **in_argv, char *env_path, int *stdin_pipe, int *stdout_pipe, pid_t *forked_pid, int *status, int flag) { int ret, argc= 0, has_slash; char **argv= NULL, *pathlist= NULL, *cpt, *npt, *prog= NULL; pid_t child_pid; struct stat stbuf; Xorriso_alloc_meM(prog, char, 5 * SfileadrL); wait3(NULL,WNOHANG,NULL); /* just to remove any old dead child */ if(flag & 2) { ret= Xorriso_make_argv_with_null(xorriso, in_argc, in_argv, &argc, &argv, 0); } else { ret= Sfile_make_argv("", cmd, &argc, &argv, 1|4|128); } if(ret <= 0) goto ex; if(argc < 1) {ret= 0; goto ex;} strcpy(prog, argv[0]); has_slash= (strchr(argv[0], '/') != NULL); if((flag & 8) && !has_slash) { sprintf(xorriso->info_text, "External program path contains no '/': "); Text_shellsafe(argv[0], xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if((flag & 1) && !has_slash) { if(env_path == NULL) env_path= "/bin:/sbin"; else if(env_path[0] == 0) env_path= "/bin:/sbin"; if(Sregex_string(&pathlist, env_path, 0) <= 0) {ret= -1; goto ex;} for(cpt= npt= pathlist; npt != NULL; cpt= npt + 1) { npt= strchr(cpt, ':'); if(npt != NULL) *npt= 0; if(strlen(cpt) + strlen(argv[0]) + 1 >= SfileadrL) {ret= -1; goto ex;} sprintf(prog, "%s/%s", cpt, argv[0]); ret= stat(prog, &stbuf); if(ret != -1) break; prog[0]= 0; } if(prog[0] == 0) { sprintf(xorriso->info_text, "Cannot find external program "); Text_shellsafe(argv[0], xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } child_pid= fork(); if(child_pid==-1) {ret= -1; goto ex;} if(child_pid==0) { /* this is the child process */ sprintf(xorriso->info_text, "Executing external program "); Text_shellsafe(prog, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); Xorriso_destroy(&xorriso, 0); /* reduce memory foot print */ if(stdin_pipe != NULL) { close(0); if(dup2(stdin_pipe[0], 0) == -1) { perror("dup2(,0)"); exit(1); } close(stdin_pipe[1]); /* unused */ } if(stdout_pipe != NULL) { close(1); if(dup2(stdout_pipe[1], 1) == -1) { perror("dup2(,1)"); exit(1); } close(stdout_pipe[0]); /* unused */ } execv(prog, argv); /* should never come back */ fprintf(stderr,"--- execution of shell command failed:\n"); fprintf(stderr," %s\n",cmd); exit(127); } /* this is the original process waiting for child to exit */ if(stdin_pipe != NULL) close(stdin_pipe[0]); /* unused */ if(stdout_pipe != NULL) close(stdout_pipe[1]); /* unused */ if(stdin_pipe != NULL || stdout_pipe != NULL) { /* Pipes need to be fed by the caller who later calls Xorriso_wait_child_end with *forked_pid as child_pid. */ *forked_pid= child_pid; {ret= 1; goto ex;} } ret= Xorriso_wait_child_end(xorriso, child_pid, status, 0); if(ret <= 0) goto ex; ret= 1; ex: Sfile_make_argv("", "", &argc, &argv, 2); Sregex_string(&pathlist, NULL, 0); Xorriso_free_meM(prog); return(ret); } /* @param flag bit0= use env_path to find the desired program bit1= use in_argv rather than parsing cmd to words bit2= "r" rather than "w" bit3= demand absolute cmd path bit4= override any restriction on external filters */ int Xorriso_pipe_open(struct XorrisO *xorriso, char *purpose, char *cmd, int in_argc, char **in_argv, char *env_path, int *fd, pid_t *forked_pid, int flag) { int fp_pipe[2], *stdin_pipe= NULL, *stdout_pipe= NULL, status, ret; *fd= -1; if(!(flag & 16)) { ret= Xorriso_external_filter_banned(xorriso, purpose, 0); if(ret) return(0); } if(pipe(fp_pipe) != 0) { sprintf(xorriso->info_text, "Cannot create pipe(2) object"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FATAL", 0); return(0); } if(flag & 4) { stdout_pipe= fp_pipe; *fd= fp_pipe[0]; } else { stdin_pipe= fp_pipe; *fd= fp_pipe[1]; } ret= Xorriso_execv(xorriso, cmd, in_argc, in_argv, env_path, stdin_pipe, stdout_pipe, forked_pid, &status, flag & 11); return(ret); } /* @param flag bit0= path is a command parameter bit1= prepend xorriso->wdx if path is relative bit2= do not issue note about excluded parameter */ int Xorriso_path_is_excluded(struct XorrisO *xorriso, char *path, int flag) { int ret; char *path_pt, *local_path_mem= NULL; if(!(xorriso->disk_excl_mode&1)) /* exclusion is off */ {ret= 0; goto ex;} if((flag&1) && !(xorriso->disk_excl_mode&2)) /* params are exempted */ {ret= 0; goto ex;} path_pt= path; if((flag & 2) && path[0] != '/' && path[0] != 0) { Xorriso_alloc_meM(local_path_mem, char, strlen(xorriso->wdx) + 1 + strlen(path) + 1); strcpy(local_path_mem, xorriso->wdx); strcat(local_path_mem, "/"); strcat(local_path_mem, path); path_pt= local_path_mem; } ret= Exclusions_match(xorriso->disk_exclusions, path_pt, !!(xorriso->disk_excl_mode&4)); if(ret<0) { sprintf(xorriso->info_text, "Error during disk file exclusion decision"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); } if(ret > 0 && (flag & 1) && !(flag & 4)) { sprintf(xorriso->info_text, "Disk path parameter excluded by %s : ", (ret==1 ? "-not_paths" : "-not_leaf")); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } ex:; Xorriso_free_meM(local_path_mem); return(ret); } int Xorriso_path_is_hidden(struct XorrisO *xorriso, char *path, int flag) { int ret, hide_attrs= 0; ret= Exclusions_match(xorriso->iso_rr_hidings, path, 0); if(ret < 0) { failure:; sprintf(xorriso->info_text, "Error during disk file hiding decision"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); return(-1); } if(ret > 0) hide_attrs|= 1; ret= Exclusions_match(xorriso->joliet_hidings, path, 0); if(ret < 0) goto failure; if(ret > 0) hide_attrs|= 2; ret= Exclusions_match(xorriso->hfsplus_hidings, path, 0); if(ret < 0) goto failure; if(ret > 0) hide_attrs|= 4; return(hide_attrs); } /* Normalize ACL and sort apart "access" ACL from "default" ACL. */ int Xorriso_normalize_acl_text(struct XorrisO *xorriso, char *in_text, char **access_acl_text, char **default_acl_text, int flag) { int ret, access_count= 0, default_count= 0, pass, is_default, line_len; int was_error= 0, line_count= 0, perms; char *acl_text= NULL, *cpt, *npt, *access_wpt= NULL, *default_wpt= NULL; char *dpt= NULL, *ddpt= NULL, **wpt, *ppt; if(in_text[0] == 0 || strcmp(in_text, "clear") == 0 || strcmp(in_text, "--remove-all") == 0) { *access_acl_text= *default_acl_text= NULL; return(1); } else if (strcmp(in_text, "--remove-default") == 0) { /* >>> protect Access-ACL and delete Default-ACL */; /* <<< */ return(0); } acl_text= strdup(in_text); if(acl_text == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); {ret= -1; goto ex;} } /* From comma to newline */ for(cpt= strchr(acl_text, ','); cpt != NULL; cpt= strchr(cpt + 1, ',')) *cpt= '\n'; /* Normalize to long text form and sort apart "access" ACL from "default" ACL */; for(pass= 0; pass < 2; pass++) { line_count= 0; for(cpt= acl_text; cpt != NULL; cpt= npt) { line_count++; npt= strchr(cpt, '\n'); if(npt != NULL) npt++; if(*cpt == '#' || *cpt == '\n' || *cpt == 0) continue; is_default= 0; wpt= &access_wpt; if(*cpt == 'd') { is_default= 1; if(pass == 1) wpt= &default_wpt; cpt= strchr(cpt, ':'); if(cpt == NULL) { was_error= line_count; continue; } cpt++; } line_len= 0; dpt= strchr(cpt, ':'); if(dpt != NULL) ddpt= strchr(dpt + 1, ':'); if(dpt == NULL || ddpt == NULL) { was_error= line_count; continue; } if(*cpt == 'u') { if(pass == 0) { line_len+= 5; line_len+= ddpt - dpt; } else { strcpy(*wpt, "user:"); strncpy(*wpt + 5, dpt + 1, ddpt - dpt); (*wpt)+= 5 + (ddpt - dpt); } } else if(*cpt == 'g') { if(pass == 0) { line_len+= 6 + (ddpt - dpt); } else { strcpy(*wpt, "group:"); strncpy(*wpt + 6, dpt + 1, ddpt - dpt); (*wpt)+= 6 + (ddpt - dpt); } } else if(*cpt == 'o') { if(pass == 0) { if(ddpt - dpt > 1) { was_error= line_count; continue; } line_len+= 6 + (ddpt - dpt); } else { strcpy(*wpt, "other:"); strncpy(*wpt + 6, dpt + 1, ddpt - dpt); (*wpt)+= 6 + (ddpt - dpt); } } else if(*cpt == 'm') { if(pass == 0) { if(ddpt - dpt > 1) { was_error= line_count; continue; } line_len+= 5 + (ddpt - dpt); } else { strcpy(*wpt, "mask:"); strncpy(*wpt + 5, dpt + 1, ddpt - dpt); (*wpt)+= 5 + (ddpt - dpt); } } else { /* Unknown tag type */ was_error= line_count; continue; } /* Examine permissions at ddpt + 1 */; perms= 0; for(ppt= ddpt + 1; *ppt != 0 && *ppt != '\n'; ppt++) { if(*ppt == 'r') perms|= 4; else if(*ppt == 'w') perms|= 2; else if(*ppt == 'x') perms|= 1; else if(*ppt == '-' || *ppt == ' ' || *ppt == '\t') ; else if(*ppt == '#') break; else { was_error= line_count; break; } } if(pass == 0) { line_len+= 4; } else { sprintf(*wpt, "%c%c%c\n", perms & 4 ? 'r' : '-', perms & 2 ? 'w' : '-', perms & 1 ? 'x' : '-'); (*wpt)+= 4; } if(pass == 0) { if(is_default) default_count+= line_len; else access_count+= line_len; } } if(pass == 0) { *access_acl_text= calloc(access_count + 1, 1); *default_acl_text= calloc(default_count + 1, 1); if(*access_acl_text == NULL || *default_acl_text == NULL) { Xorriso_no_malloc_memory(xorriso, access_acl_text, 0); {ret= -1; goto ex;} } access_wpt= *access_acl_text; default_wpt= *default_acl_text; } else { *access_wpt= 0; *default_wpt= 0; } } ret= 1; ex:; if(acl_text != NULL) free(acl_text); if(was_error) { sprintf(xorriso->info_text, "Malformed ACL entries encountered. Last one in line number %d.", was_error); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(ret); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of functions which deal with parsing and interpretation of command input. */ #ifndef Xorriso_pvt_cmd_includeD #define Xorriso_pvt_cmd_includeD yes /* @param flag bit0= do not warn of wildcards bit1= these are disk_paths */ int Xorriso_end_idx(struct XorrisO *xorriso, int argc, char **argv, int idx, int flag); int Xorriso_opt_args(struct XorrisO *xorriso, char *cmd, int argc, char **argv, int idx, int *end_idx, int *optc, char ***optv, int flag); int Xorriso_get_problem_status(struct XorrisO *xorriso, char severity[80], int flag); int Xorriso_set_problem_status(struct XorrisO *xorriso, char *severity, int flag); /** @param flag bit0= do not issue own event messages bit1= take xorriso->request_to_abort as reason for abort @return Gives the advice: 2= pardon was given, go on 1= no problem, go on 0= function failed but xorriso would not abort, go on <0= do abort -1 = due to problem_status -2 = due to xorriso->request_to_abort */ int Xorriso_eval_problem_status(struct XorrisO *xorriso, int ret, int flag); int Xorriso_cpmv_args(struct XorrisO *xorriso, char *cmd, int argc, char **argv, int *idx, int *optc, char ***optv, char eff_dest[SfileadrL], int flag); /* @param flag bit0= with adr_mode sbsector: adr_value is possibly 16 too high */ int Xorriso_decode_load_adr(struct XorrisO *xorriso, char *cmd, char *adr_mode, char *adr_value, int *entity_code, char entity_id[81], int flag); int Xorriso_prepare_load_search(struct XorrisO *xorriso, char *cmd, int adr_mode, char *adr_value_in, char *adr_value_out, int *params_flag, int flag); int Xorriso_check_thing_len(struct XorrisO *xorriso, char *name, int size, char *cmd, char *thing, int flag); int Xorriso_check_name_len(struct XorrisO *xorriso, char *name, int size, char *cmd, int flag); /* @param flag bit0= prepend wd only if name does not begin by '/' bit2= prepend wd (automatically done if wd[0]!=0) */ int Xorriso_make_abs_adr(struct XorrisO *xorriso, char *wd, char *name, char adr[], int flag); /* @param flag bit0= do not complain in case of error, but set info_text */ int Xorriso_convert_datestring(struct XorrisO *xorriso, char *cmd, char *time_type, char *timestring, int *t_type, time_t *t, int flag); int Xorriso_check_temp_mem_limit(struct XorrisO *xorriso, off_t mem, int flag); int Xorriso_execv(struct XorrisO *xorriso, char *cmd, int in_argc, char **in_argv, char *env_path, int *stdin_pipe, int *stdout_pipe, pid_t *forked_pid, int *status, int flag); int Xorriso_pipe_open(struct XorrisO *xorriso, char *purpose, char *cmd, int in_argc, char **in_argv, char *env_path, int *fd, pid_t *forked_pid, int flag); int Xorriso_wait_child_end(struct XorrisO *xorriso, pid_t child_pid, int *status, int flag); int Xorriso_path_is_excluded(struct XorrisO *xorriso, char *path, int flag); int Xorriso_path_is_hidden(struct XorrisO *xorriso, char *path, int flag); /* Normalize ACL and sort apart "access" ACL from "default" ACL. */ int Xorriso_normalize_acl_text(struct XorrisO *xorriso, char *in_text, char **access_acl_text, char **default_acl_text, int flag); int Xorriso_read_mkisofsrc(struct XorrisO *xorriso, int flag); /* @param flag bit0= list sorting order rather than looking for argv[idx] */ int Xorriso_cmd_sorting_rank(struct XorrisO *xorriso, int argc, char **argv, int idx, int flag); #endif /* ! Xorriso_pvt_cmd_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains functions which are needed to read data from ISO image. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include <fcntl.h> #include <utime.h> /* O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #include "lib_mgt.h" #include "drive_mgt.h" #include "iso_img.h" #include "iso_tree.h" #include "iso_manip.h" #include "read_run.h" #include "sort_cmp.h" #include "disk_ops.h" int Xorriso__read_pacifier(IsoImage *image, IsoFileSource *filesource) { struct XorrisO *xorriso; xorriso= (struct XorrisO *) iso_image_get_attached_data(image); if(xorriso==NULL) return(1); Xorriso_process_msg_queues(xorriso,0); xorriso->pacifier_count++; if(xorriso->pacifier_count%10) return(1); Xorriso_pacifier_callback(xorriso, "nodes read", xorriso->pacifier_count, 0, "", 0); return(1); } /* @param flag bit0= open IsoNode *node_pt rather than looking up pathname bit1= dig out the most original stream for reading */ int Xorriso_iso_file_open(struct XorrisO *xorriso, char *pathname, void *node_pt, void **stream, int flag) { int ret; char *eff_path= NULL; IsoNode *node= NULL; IsoFile *filenode= NULL; IsoStream *iso_stream= NULL, *input_stream; Xorriso_alloc_meM(eff_path, char, SfileadrL); *stream= NULL; if(flag&1) { node= (IsoNode *) node_pt; } else { ret= Xorriso_get_node_by_path(xorriso, pathname, eff_path, &node, 0); if(ret<=0) goto ex; } if(!LIBISO_ISREG(node)) { sprintf(xorriso->info_text, "Given path does not lead to a regular data file in the image"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } filenode= (IsoFile *) node; iso_stream= iso_file_get_stream(filenode); if(iso_stream==NULL) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "Could not obtain source stream of file in the image for reading"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(flag & 2) { /* Dig out the most original stream */ while(1) { input_stream= iso_stream_get_input_stream(iso_stream, 0); if(input_stream == NULL) break; iso_stream= input_stream; } } if(!iso_stream_is_repeatable(iso_stream)) { sprintf(xorriso->info_text, "The data production of the file in the image is one-time only"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } ret= iso_stream_open(iso_stream); if(ret<0) { sprintf(xorriso->info_text, "Could not open data file in the image for reading"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } Xorriso_process_msg_queues(xorriso,0); *stream= iso_stream; #ifdef NIX /* <<< */ { unsigned int fs_id; dev_t dev_id; ino_t ino; iso_stream_get_id(iso_stream, &fs_id, &dev_id, &ino); fprintf(stderr, "xorriso_debug: iso_ino= %ld\n", (long int) ino); } #endif ret= 1; ex:; Xorriso_free_meM(eff_path); return(ret); } int Xorriso_iso_file_read(struct XorrisO *xorriso, void *stream, char *buf, int count, int flag) { int ret, rcnt= 0; IsoStream *stream_pt; stream_pt= (IsoStream *) stream; while(rcnt<count) { ret= iso_stream_read(stream_pt, (void *) (buf+rcnt), (size_t) (count-rcnt)); if(ret==0) /* EOF */ break; if(ret<0) { /* error */ Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", ret, "Error on read", 0, "FAILURE", 1 | ((ret == -1)<<2) ); return(-1); } rcnt+= ret; } return(rcnt); } int Xorriso_iso_file_close(struct XorrisO *xorriso, void **stream, int flag) { int ret; if(*stream==NULL) return(0); ret= iso_stream_close(*stream); if(ret==1) *stream= NULL; Xorriso_process_msg_queues(xorriso,0); return(ret); } /* @param flag bit0= in_node is valid, do not resolve img_path bit1= test mode: print DEBUG messages @return <0 = error, 0 = surely not identical regular files , 1 = surely identical 2 = potentially depending on unknown disk file (e.g. -cut_out) */ int Xorriso_restore_is_identical(struct XorrisO *xorriso, void *in_node, char *img_path, char *disk_path, char type_text[5], int flag) { int ret; unsigned int fs_id; dev_t dev_id; ino_t ino_id; IsoStream *stream; IsoImage *volume; IsoNode *node; struct stat stbuf; off_t dummy; memset(type_text, 0, 5); if(!Xorriso_change_is_pending(xorriso, 0)) return(0); if(flag&1) { node= (IsoNode *) in_node; } else { ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) return(-1); ret= Xorriso_node_from_path(xorriso, volume, img_path, &node, 1); if(ret<=0) return(-1); } ret= Xorriso__file_start_lba(node, &dummy, 0); if(ret != 0) { Xorriso_process_msg_queues(xorriso, 0); return(0); } if(!LIBISO_ISREG(node)) return(0); stream= iso_file_get_stream((IsoFile *) node); memcpy(type_text, stream->class->type, 4); iso_stream_get_id(stream, &fs_id, &dev_id, &ino_id); if(flag&2) { sprintf(xorriso->info_text, "%s : fs=%d dev=%.f ino=%.f (%s)", img_path, fs_id, (double) dev_id, (double) ino_id, type_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } ret= stat(disk_path, &stbuf); if(ret==-1) return(0); if(flag&2) { sprintf(xorriso->info_text, "%s : dev=%.f ino=%.f", disk_path, (double) stbuf.st_dev, (double) stbuf.st_ino); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } if(fs_id!=1) return(2); /* >>> obtain underlying dev_t ino_t of type "cout" */; if(strcmp(type_text, "fsrc")!=0) return(2); if(stbuf.st_dev==dev_id && stbuf.st_ino==ino_id) return(1); return(0); } int Xorriso_iso_file_to_fd(struct XorrisO *xorriso, char *path, int fd, int flag) { int ret, rret, wret, to_write, wanted; void *stream= NULL; char *buffer= NULL, *wpt; off_t todo; static int buffer_size= 64 * 1024; Xorriso_alloc_meM(buffer, char, buffer_size); ret= Xorriso_iso_file_open(xorriso, path, NULL, &stream, 0); if(ret <= 0) goto ex; todo= iso_stream_get_size((IsoStream *) stream); while(todo > 0) { if(todo < buffer_size) wanted= todo; else wanted= buffer_size; rret = Xorriso_iso_file_read(xorriso, stream, buffer, wanted, 0); if(rret <= 0) {ret= -1; goto ex;} todo-= rret; wpt= buffer; for(to_write= rret; to_write > 0;) { wret= write(fd, wpt, to_write); if(wret <= 0) { if(wret == 0) sprintf(xorriso->info_text, "Strange behavior of write(2): return == 0 with "); else sprintf(xorriso->info_text, "Write error with "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, wret == 0 ? 0 : errno, "FAILURE",0); ret= 0; goto ex; } to_write-= wret; wpt+= wret; } } ret= 1; ex:; if(stream != NULL) Xorriso_iso_file_close(xorriso, &stream, 0); Xorriso_free_meM(buffer); return(ret); } /* @flag bit0= do not report error in case of error worth of single flag try @return 0=failure, 1=ok, 2=pardoned, 3=pardoned error worth of single flag try */ int Xorriso_report_chattr_outcome(struct XorrisO *xorriso, char *disk_path, uint64_t lfa_flags, uint64_t lfa_mask, int iso_ret, int os_errno, int flag) { int ret, eps_ret, sev, lfa_error= 0; char msg[101], *lfa_text= NULL, severity[20]; if(iso_ret == 1) {ret= 1; goto ex;} if(strcmp(xorriso->lfa_restore_err_sev, "silent") == 0 || strcmp(xorriso->lfa_restore_err_sev, "all") == 0) {ret= 2; goto ex;} Xorriso__to_upper(xorriso->lfa_restore_err_sev, severity, sizeof(severity), 0); ret= iso_util_encode_lfa_flags(lfa_flags & lfa_mask, &lfa_text, 0); if(lfa_text == NULL) lfa_text= strdup("-unknown-attributes-"); if(iso_ret < 0) { if(iso_ret == (int) ISO_LFA_NO_SET_LOCAL) { lfa_error= 1; if(flag & 1) {ret= 3; goto ex;} } strcpy(msg, "Could not set chattr '"); if(lfa_text != NULL) strcat(msg, lfa_text); strcat(msg, "'"); /* Adjust severity to event_pt. Number 0x7f000000 comes from libisofs.h, iso_error_get_severity */ Xorriso__text_to_sev(severity, &sev, 0); iso_ret&= ~0x7f000000; iso_ret|= sev; Xorriso_report_iso_error(xorriso, disk_path, iso_ret, msg, os_errno, severity, 2); ret= 0; goto ex; } else if(iso_ret == 2) { sprintf(xorriso->info_text, "Could not map all of chattr '%s' to local file attributes of ", lfa_text); } else if(iso_ret == 3) { sprintf(xorriso->info_text, "Will not apply chattr '%s' to symbolic link ", lfa_text); } else { sprintf(xorriso->info_text, "Unknown return value %d from iso_local_set_lfa_flags() with chattr '%s' to ", iso_ret, lfa_text); } Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, severity, 0); ret= 0; ex:; if(ret == 0) { eps_ret= Xorriso_eval_problem_status(xorriso, 0, 1); if(eps_ret >= 0) ret= 2 + !!lfa_error; } if(lfa_text != NULL) free(lfa_text); return(ret); } /* @param flag bit0= try single attribute flags if the whole change_mask fails possibly because of inappropriate attributes @return 0=failure 1= ok 2= pardoned 3= pardoned error worth of single flag try 4= with bit0: partial success */ int Xorriso_local_set_lfa_flags(struct XorrisO *xorriso, char *disk_path, uint64_t lfa_flags, int max_bit, uint64_t change_mask, int *os_errno, int flag) { int ret, partial_success= 0, os_errno_mem, i, count; uint64_t single_mask, single_lfa; ret= iso_local_set_lfa_flags(disk_path, lfa_flags, max_bit, change_mask, os_errno, 4); ret= Xorriso_report_chattr_outcome(xorriso, disk_path, lfa_flags, change_mask, ret, *os_errno, flag & 1); if(ret != 3 || !(flag & 1)) return(ret); /* Try with single flag calls */ count= 0; for(i= 0; i <= max_bit && i < 64; i++) if(change_mask & (((uint64_t) 1) << i)) count++; if(count < 2) return(ret); /* was already a single flag call */ os_errno_mem= *os_errno; for(i= 0; i <= max_bit && i < 64; i++) { single_mask= ((uint64_t) 1) << i; if(!(single_mask & change_mask)) continue; single_lfa= lfa_flags & single_mask; *os_errno= 0; ret= iso_local_set_lfa_flags(disk_path, single_lfa, max_bit, single_mask, os_errno, 4); ret= Xorriso_report_chattr_outcome(xorriso, disk_path, single_lfa, single_mask, ret, *os_errno, 0); if(ret <= 0) return(ret); if(ret == 1) partial_success= 1; } *os_errno= os_errno_mem; return(3 + !!partial_success); } uint64_t Xorriso__lfa_bits(char *lfa_text) { int ret; uint64_t lfa_bits; ret= iso_util_decode_lfa_flags(lfa_text, &lfa_bits, 0); if(ret < 0) lfa_bits= 0; return(lfa_bits); } /* @param flag bit0= set i bit1= set a */ int Xorriso_set_local_chattr_ia(struct XorrisO *xorriso, char *disk_path, int flag) { int ret, max_bit= 31, os_errno; uint64_t lfa_flags= 0; static uint64_t lfa_i= 0xffffffff, lfa_a= 0; if(lfa_i == 0xffffffff) { lfa_i= Xorriso__lfa_bits("i"); lfa_a= Xorriso__lfa_bits("a"); } if(flag & 1) lfa_flags|= lfa_i; if(flag & 2) lfa_flags|= lfa_a; if(lfa_flags == 0) return(1); ret= Xorriso_local_set_lfa_flags(xorriso, disk_path, lfa_flags, max_bit, lfa_flags, &os_errno, 0); if(ret <= 0) return(ret); return(1); } /* If present and enabled: restore chattr i and/or a */ int Xorriso_restore_chattr_ia(struct XorrisO *xorriso, IsoNode *node, char *disk_path, int flag) { int ret, max_bit; uint64_t lfa_flags, mask, chattr_flag= 0; static uint64_t lfa_i= 0xffffffff, lfa_a= 0; if(lfa_i == 0xffffffff) { lfa_i= Xorriso__lfa_bits("i"); lfa_a= Xorriso__lfa_bits("a"); } if((xorriso->do_aaip & (1 << 12))) { mask= iso_util_get_effective_lfa_mask(xorriso->lfa_restore_mask, (!!(xorriso->do_aaip & (1 << 13))) | ((!!(xorriso->do_aaip & (1 << 14))) << 1)); if(!(mask & (lfa_i | lfa_a))) return(2); ret= iso_node_get_lfa_flags(node, &lfa_flags, &max_bit, 0); if(ret > 0) { if(lfa_flags & lfa_i) chattr_flag|= 1; if(lfa_flags & lfa_a) chattr_flag|= 2; if(chattr_flag) { ret= Xorriso_set_local_chattr_ia(xorriso, disk_path, chattr_flag); if(ret <= 0) return(ret); return(1); } } } return(2); } int Xorriso_early_chattr_CF(struct XorrisO *xorriso, IsoNode *node, char *disk_path, int *os_errno, int flag) { int ret, max_bit; uint64_t lfa_flags, xorriso_mask, set_mask; static uint64_t lfa_C= 0xffffffff, lfa_F= 0; if(lfa_C == 0xffffffff) { lfa_C= Xorriso__lfa_bits("C"); lfa_F= Xorriso__lfa_bits("F"); } *os_errno= 0; if(!(xorriso->lfa_flags_setting & (1 << 12))) return(2); ret= iso_node_get_lfa_flags(node, &lfa_flags, &max_bit, 0); if(ret <= 0) return(3); xorriso_mask= iso_util_get_effective_lfa_mask(xorriso->lfa_restore_mask, (!!(xorriso->do_aaip & (1 << 13))) | ((!!(xorriso->do_aaip & (1 << 14))) << 1)); set_mask= 0; /* C must be set on empty regular files. But if it would be set for a directory, all its files would get set C, even if their IsoNode does not have it. */ if(!LIBISO_ISDIR(node)) set_mask|= lfa_C; /* F must be set on empty directories. (It is undefined for regular files) */ if(LIBISO_ISDIR(node)) set_mask|= lfa_F; set_mask&= xorriso_mask; if(set_mask == 0) return(4); ret= Xorriso_local_set_lfa_flags(xorriso, disk_path, lfa_flags, max_bit, set_mask, os_errno, !!(xorriso->do_aaip & (1 << 16))); if(ret <= 0) return(ret); return(1); } int Xorriso_restore_timestamps(struct XorrisO *xorriso, char *disk_path, IsoNode *node, int flag) { int ret; struct utimbuf utime_buffer; utime_buffer.actime= iso_node_get_atime(node); utime_buffer.modtime= iso_node_get_mtime(node); ret= utime(disk_path, &utime_buffer); if(ret == -1) { sprintf(xorriso->info_text, "Cannot change atime, mtime of disk file "); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); return(0); } return(1); } /* @param flag bit0= minimal transfer: access permissions only bit1= keep directory open: keep owner, allow rwx for owner and push directory onto xorriso->perm_stack @return 1=ok , 2=ok, lfa "i" and/or "a" pushed to permstack, <=0 error */ int Xorriso_restore_properties(struct XorrisO *xorriso, char *disk_path, IsoNode *node, int flag) { int ret, is_dir= 0, errno_copy= 0, local_attrs_set= 0, i, err_count; int lfa_ia_pushed= 0; mode_t mode; uid_t uid, disk_uid; gid_t gid, disk_gid; struct stat stbuf; size_t num_attrs= 0, *value_lengths= NULL; char **names= NULL, **values= NULL; int *errnos= NULL; uint64_t lfa_flags= 0, mask= 0, push_mask= 0; int max_bit, os_errno; static uint64_t lfa_C= 0xffffffff, lfa_i= 0, lfa_a= 0, lfa_F= 0; if(lfa_C == 0xffffffff) { lfa_C= Xorriso__lfa_bits("C"); lfa_F= Xorriso__lfa_bits("F"); lfa_i= Xorriso__lfa_bits("i"); lfa_a= Xorriso__lfa_bits("a"); } ret= lstat(disk_path, &stbuf); if(ret==-1) { sprintf(xorriso->info_text, "Cannot obtain properties of disk file "); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); {ret= 0; goto ex;} } disk_uid= uid= stbuf.st_uid; disk_gid= stbuf.st_gid; is_dir= S_ISDIR(stbuf.st_mode); mode= iso_node_get_permissions(node); if(xorriso->do_aaip & (2 | 8 | 16)) { ret= iso_node_get_attrs(node, &num_attrs, &names, &value_lengths, &values, (!!(xorriso->do_aaip & 2)) | (!(xorriso->do_aaip & (8 | 16))) << 2); if (ret < 0) { Xorriso_process_msg_queues(xorriso, 0); strcpy(xorriso->info_text, "Error with obtaining ACL and xattr for "); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(num_attrs > 0) { Xorriso_alloc_meM(errnos, int, num_attrs); ret= iso_local_set_attrs_errno(disk_path, num_attrs, names, value_lengths, values, errnos, ((!(xorriso->do_strict_acl & 1)) << 6) | ((!!(xorriso->do_aaip & 1024)) << 3) ); if(ret < 0) { cannot_set_xattr:; errno_copy= errno; if(ret != (int) ISO_AAIP_NO_SET_LOCAL) errno_copy= 0; Xorriso_report_iso_error(xorriso, "", ret, "Error on iso_local_set_attrs", 0, "FAILURE", 1 | ((ret == -1)<<2) ); sprintf(xorriso->info_text, "Disk file "); Text_shellsafe(disk_path, xorriso->info_text, 1); err_count= 0; for(i= 0; (unsigned int) i < num_attrs; i++) { if(errnos[i] == 0) continue; if(err_count >= 3) { strcat(xorriso->info_text, " , and more"); break; } err_count++; errno_copy= 0; /* Detail errno overrides final errno */ if(names[i][0] == 0) sprintf(xorriso->info_text + strlen(xorriso->info_text), " , ACL "); else sprintf(xorriso->info_text + strlen(xorriso->info_text), " , xattr %s ", names[i]); if(errnos[i] < 0) Text_shellsafe("Unknown error", xorriso->info_text, 1); else Text_shellsafe(strerror(errnos[i]), xorriso->info_text, 1); } Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno_copy, "FAILURE",0); {ret= 0; goto ex;} } local_attrs_set= 1; } Xorriso_process_msg_queues(xorriso,0); } if(!(xorriso->do_aaip & 2)) mode= iso_node_get_perms_wo_acl(node); /* Need to assess lfa_flags early because 'i' might need to be pushed to the perm stack */ if(xorriso->do_aaip & (1 << 12)) { ret= iso_node_get_lfa_flags(node, &lfa_flags, &max_bit, 0); if (ret < 0) { Xorriso_process_msg_queues(xorriso, 0); strcpy(xorriso->info_text, "Error with obtaining file attribut flags for "); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } else if(ret > 0) { mask= iso_util_get_effective_lfa_mask(xorriso->lfa_restore_mask, (!!(xorriso->do_aaip & (1 << 13))) | ((!!(xorriso->do_aaip & (1 << 14))) << 1)); /* Do not set 'C' of non-directories or 'F' of directories here. It would be too late. */ if(!is_dir) mask&= ~lfa_C; if(is_dir) mask&= ~lfa_F; push_mask= mask; /* Do not set lfa_flags 'i' or 'a' of a directory here. It would be too early. */ if(is_dir) mask&= ~(lfa_i | lfa_a); } } if(is_dir && (flag&2)) { ret= Xorriso_fake_stbuf(xorriso, "", &stbuf, &node, 1 | ((!!(xorriso->do_aaip & 2)) << 3)); if(ret<=0) {ret= 0; goto ex;} ret= Permstack_push(&(xorriso->perm_stack), disk_path, &stbuf, (!!(lfa_flags & push_mask & lfa_i)) | ((!!(lfa_flags & push_mask & lfa_a)) << 2)); if(ret<=0) { Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0); strcpy(xorriso->info_text, "Cannot memorize permissions for disk directory"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } lfa_ia_pushed= !!(lfa_flags & push_mask & (lfa_i | lfa_a)); mode|= S_IRUSR|S_IWUSR|S_IXUSR; } ret= chmod(disk_path, mode); if(ret==-1) { cannot_set_perm:; sprintf(xorriso->info_text, "Cannot change access permissions of disk file "); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); {ret= 0; goto ex;} } if(flag&1) {ret= 1; goto ex;} ret= Xorriso_restore_timestamps(xorriso, disk_path, node, 0); if(ret <= 0) goto ex; gid= iso_node_get_gid(node); if(!(S_ISDIR(stbuf.st_mode) && (flag&2))) uid= iso_node_get_uid(node); if(uid != disk_uid || gid != disk_gid) { ret= chown(disk_path, uid, gid); /* don't complain if it fails */ /* Check whether xattr are still set and try to set them again if needed. E.g. Linux 3.16 removes security.capability on chown(2). */ if(local_attrs_set && (xorriso->do_aaip & 1024)) { ret= iso_local_set_attrs_errno(disk_path, num_attrs, names, value_lengths, values, errnos, 1 | ((!!(xorriso->do_aaip & 1024)) << 3) | 128); if(ret < 0) goto cannot_set_xattr; } /* Check whether setuid or setgid bits got reset */ ret= lstat(disk_path, &stbuf); if(ret != -1) { if((mode ^ stbuf.st_mode) & (S_ISUID | S_ISGID)) { ret= chmod(disk_path, mode); if(ret==-1) goto cannot_set_perm; } } } if(xorriso->do_aaip & (1 << 12)) { if(mask != 0) { ret= Xorriso_local_set_lfa_flags(xorriso, disk_path, lfa_flags, max_bit, mask, &os_errno, !!(xorriso->do_aaip & (1 << 16))); if(ret <= 0) goto ex; } } ret= 1 + !!lfa_ia_pushed; ex:; iso_node_get_attrs(node, &num_attrs, &names, &value_lengths, &values,1 << 15); if(errnos != NULL) free(errnos); return(ret); } /* @param flag bit1= minimal transfer: access permissions only bit2= keep directory open: keep owner, allow rwx for owner push to xorriso->perm_stack */ int Xorriso_restore_implicit_properties(struct XorrisO *xorriso, char *full_disk_path, char *disk_path, char *full_img_path, int flag) { int ret, nfic, ndc, nfdc, d, i; char *nfi= NULL, *nd= NULL, *nfd= NULL, *cpt; struct stat stbuf; IsoNode *node; Xorriso_alloc_meM(nfi, char, SfileadrL); Xorriso_alloc_meM(nd, char, SfileadrL); Xorriso_alloc_meM(nfd, char, SfileadrL); ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, full_disk_path, nfd, 1|2|4); if(ret<=0) goto ex; ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, disk_path, nd, 1|2); if(ret<=0) goto ex; ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, full_img_path, nfi, 1|2); if(ret<=0) goto ex; nfdc= Sfile_count_components(nfd, 0); ndc= Sfile_count_components(nd, 0); nfic= Sfile_count_components(nfi, 0); d= nfdc-ndc; if(d<0) {ret= -1; goto ex;} if(d>nfic) {ret= 0; goto ex;} for(i= 0; i<d; i++) { cpt= strrchr(nfi, '/'); if(cpt==NULL) {ret= -1; goto ex;} /* should not happen */ *cpt= 0; } if(nfi[0]==0) strcpy(nfi, "/"); ret= Xorriso_fake_stbuf(xorriso, nfi, &stbuf, &node, 0); if(ret<=0) {ret= 0; goto ex;} ret= Xorriso_restore_properties(xorriso, nd, node, ((flag>>1)&3)); if(ret<=0) goto ex; sprintf(xorriso->info_text, "Restored properties for "); Text_shellsafe(nd, xorriso->info_text, 1); strcat(xorriso->info_text, " from "); Text_shellsafe(nfi, xorriso->info_text, 1 | 2); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); ret= 1; ex:; Xorriso_free_meM(nfi); Xorriso_free_meM(nd); Xorriso_free_meM(nfd); return(ret); } /* If defined the position accounting will be done by lseek() and used to * verify the position accounting in struct Xorriso_sparse_statE. * # def ine Xorriso_check_sparsE yes */ struct Xorriso_sparse_statE { int use_lseek; off_t cur_pos; off_t after_last_written; int warnings; }; #ifdef Xorriso_check_sparsE static int Xorriso_sparse_warn(struct XorrisO *xorriso, struct Xorriso_sparse_statE *sparse_state, int occasion, char *msg, int flag) { if(sparse_state->warnings & (1 << occasion)) return(1); sparse_state->warnings|= 1 << occasion; Xorriso_msgs_submit(xorriso, 0, msg, 0, "SORRY", 0); return(1); } #endif /* Xorriso_check_sparsE */ static int Xorriso_sparse_init(struct XorrisO *xorriso, struct Xorriso_sparse_statE **sparse_state, int write_fd, int flag) { struct Xorriso_sparse_statE *o= NULL; off_t cur_pos; struct stat stbuf; int ret; *sparse_state= NULL; /* Check whether sparse writing is disabled */ if(xorriso->sparse_min_gap <= 0) {ret= 0; goto ex;} /* Analyze write_fd */ ret= fstat(write_fd, &stbuf); if(ret == -1) {ret= 0; goto ex;} if(!S_ISREG(stbuf.st_mode)) {ret= 0; goto ex;} cur_pos= lseek(write_fd, (off_t) 0, SEEK_CUR); if(cur_pos < stbuf.st_size) {ret= 0; goto ex;} Xorriso_alloc_meM(o, struct Xorriso_sparse_statE, 1); /* Initialize sparse_state */ o->use_lseek= 1; o->cur_pos= o->after_last_written= cur_pos; o->warnings= 0; ret= 1; ex:; if(ret >= 1) *sparse_state= o; else Xorriso_free_meM(o); return(ret); } static int Xorriso_sparse_zeroize(struct XorrisO *xorriso, struct Xorriso_sparse_statE *sparse_state, int write_fd, off_t start, off_t count, int flag) { int ret, buf_size= 32 * 1024, buf_fill, wret; off_t todo, seek_ret; char *buf= NULL; if(count <= 0) {ret= 2; goto ex;} Xorriso_alloc_meM(buf, char, buf_size); seek_ret= lseek(write_fd, start, SEEK_SET); if(seek_ret == -1) {ret= -1; goto ex;} sparse_state->cur_pos= seek_ret; for(todo= count; todo > 0; ) { if(buf_size < todo) buf_fill= buf_size; else buf_fill= todo; wret= write(write_fd, buf, buf_fill); if(wret <= 0) {ret= wret; goto ex;} todo-= wret; sparse_state->cur_pos+= wret; } ret= 1; ex:; Xorriso_free_meM(buf); return(ret); } /* @param flag bit0= this is the last buffer of the stream */ static int Xorriso_sparse_write(struct XorrisO *xorriso, struct Xorriso_sparse_statE *sparse_state, int write_fd, char *buf, int count, int flag) { int wret, i, ret; off_t cur_pos= -1, seek_ret; static char zero[32]= {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; if(sparse_state == NULL) goto do_write; if(!sparse_state->use_lseek) goto do_write; if(flag & 1) goto do_write; #ifdef Xorriso_check_sparsE cur_pos= lseek(write_fd, (off_t) 0, SEEK_CUR); if(cur_pos == -1) goto do_write; if(cur_pos != sparse_state->cur_pos) { Xorriso_sparse_warn(xorriso, sparse_state, 0, "cur_pos deviation in Xorriso_sparse_write:intro", 0); sparse_state->cur_pos= cur_pos; } #else cur_pos= sparse_state->cur_pos; #endif /* Check for all zeros */ if(count % 32) goto do_write; for(i= 0; i < count; i+= 32) if(memcmp(buf + i, zero, 32)) break; if(i < count) goto do_write; /* Omit write() until next non-zero buffer or end of writing */ #ifdef Xorriso_check_sparsE /* Only for debugging: Do real lseek() instead of write() */ seek_ret= lseek(write_fd, cur_pos + count, SEEK_SET); if(seek_ret == -1) return(-1); #endif sparse_state->cur_pos= cur_pos + count; return(count); do_write: if(sparse_state != NULL) { /* Check whether the gap since after_last_written is too small. If so: fill the whole gap by writing zeros. */ if(sparse_state->after_last_written < cur_pos) { if(xorriso->sparse_min_gap > cur_pos - sparse_state->after_last_written) { ret= Xorriso_sparse_zeroize(xorriso, sparse_state, write_fd, sparse_state->after_last_written, cur_pos - sparse_state->after_last_written, 0); if(ret < 0) return(ret); if(ret == 0) { seek_ret= lseek(write_fd, cur_pos, SEEK_SET); if(seek_ret == -1) return(-1); sparse_state->cur_pos= seek_ret; } } } } if(sparse_state != NULL) { #ifdef Xorriso_check_sparsE cur_pos= lseek(write_fd, (off_t) 0, SEEK_CUR); if(cur_pos != sparse_state->cur_pos) { Xorriso_sparse_warn(xorriso, sparse_state, 1, "cur_pos deviation in Xorriso_sparse_write:do_write", 0); sparse_state->cur_pos= cur_pos; } #else /* lseek() has been delayed until now */ if(sparse_state->after_last_written != sparse_state->cur_pos) { seek_ret= lseek(write_fd, sparse_state->cur_pos, SEEK_SET); if(seek_ret == -1) return(-1); } #endif /* ! Xorriso_check_sparsE */ } wret= write(write_fd, buf, count); if(sparse_state != NULL && wret > 0 && cur_pos >= 0) sparse_state->cur_pos= sparse_state->after_last_written= cur_pos + wret; return(wret); } static int Xorriso_sparse_finish(struct XorrisO *xorriso, struct Xorriso_sparse_statE **sparse_state, int write_fd, int flag) { int ret; off_t cur_pos; struct Xorriso_sparse_statE *o; if(sparse_state == NULL) return(0); o= *sparse_state; if(o == NULL) return(1); if(write_fd == -1) {ret= 1; goto ex;} #ifdef Xorriso_check_sparsE cur_pos= lseek(write_fd, (off_t) 0, SEEK_CUR); if(cur_pos == -1) {ret= -1; goto ex;} if(cur_pos != o->cur_pos) { Xorriso_sparse_warn(xorriso, o, 2, "cur_pos deviation in Xorriso_sparse_finish", 0); o->cur_pos= cur_pos; } #else cur_pos= o->cur_pos; #endif /* ! Xorriso_check_sparsE */ if(o->after_last_written < cur_pos) { /* Check whether the gap since after_last_written is too small. If so: fill the whole gap by writing zeros, else: write a last zero byte. */ if(xorriso->sparse_min_gap > cur_pos - o->after_last_written) ret= Xorriso_sparse_zeroize(xorriso, o, write_fd, o->after_last_written, cur_pos - o->after_last_written, 0); else ret= Xorriso_sparse_zeroize(xorriso, o, write_fd, cur_pos - 1, (off_t) 1, 0); if(ret <= 0) goto ex; } ret= 1; ex:; Xorriso_free_meM(o); *sparse_state= NULL; return(ret); } /* @param flag bit0= Minimal transfer: access permissions only bit1= *_offset and bytes are valid for writing to regular file bit2= This is not a parameter. Do not report if ignored bit3= do not restore properties bit4= issue pacifier messages with long lasting copying bit7= return 4 if restore fails from denied permission do not issue error message @return <0 severe error , 0 failure , 1 success , 2 regularly not installed (disallowed device, UNIX domain socket) 4 with bit7: permission to restore was denied */ int Xorriso_tree_restore_node(struct XorrisO *xorriso, IsoNode *node, char *img_path, off_t img_offset, char *disk_path, off_t disk_offset, off_t bytes, int flag) { int ret= 0, write_fd= -1, wanted, wret, open_flags, l_errno= 0; int target_deleted= 0, buf_size= 32 * 1024, new_empty= 0; char *what= "[unknown filetype]"; char *buf= NULL, type_text[5], *temp_path= NULL, *buf_pt, *reason; char *link_target, *open_path_pt= NULL; off_t todo= 0, size, seek_ret, last_p_count= 0, already_done, read_count= 0; void *data_stream= NULL; mode_t mode; dev_t dev= 0; struct stat stbuf; struct utimbuf utime_buffer; IsoImage *volume; IsoBoot *bootcat; uint32_t lba; char *catcontent = NULL; off_t catsize, iso_node_size, wanted_size, cap; char disk_md5[16], iso_md5[16]; void *ctx= NULL; int use_md5= 0, i, sparse_ret= 3; struct Xorriso_sparse_statE *sparse_state= NULL; Xorriso_alloc_meM(buf, char, buf_size); Xorriso_alloc_meM(temp_path, char, SfileadrL); if(!(flag & 2)) img_offset= bytes= 0; if(LIBISO_ISDIR(node)) { what= "directory"; ret= mkdir(disk_path, 0777); l_errno= errno; if(ret == 0) { ret= Xorriso_early_chattr_CF(xorriso, node, disk_path, &l_errno, 0); if(ret <= 0) goto cannot_restore; } } else if(LIBISO_ISREG(node) || ISO_NODE_IS_BOOTCAT(node)) { if(ISO_NODE_IS_BOOTCAT(node)) { what= "boot catalog"; } else { what= "regular file"; ret= Xorriso_iso_file_open(xorriso, img_path, (void *) node, &data_stream, 1); if(ret<=0) goto ex; if((xorriso->do_md5 & 65) == 65 && !(flag & 2)) { ret= Xorriso_is_plain_image_file(xorriso, (void *) node, img_path, 0); if(ret > 0) { ret= Xorriso_get_md5(xorriso, (void *) node, img_path, iso_md5, 1); if(ret > 0) ret= Xorriso_md5_start(xorriso, &ctx, 0); if(ret > 0) { use_md5= 1; } else if(xorriso->do_md5 & 128) { sprintf(xorriso->info_text, "Cannot obtain any recorded MD5 of file "); Text_shellsafe(img_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); ret= Xorriso_eval_problem_status(xorriso, 0, 1 | 2); if(ret < 0) {ret= 0; goto ex;} } } } } open_path_pt= disk_path; ret= stat(open_path_pt, &stbuf); if(ret == -1) { if(errno == EACCES && (flag & 128)) {ret= 4; goto ex;} new_empty= 1; } if(flag&2) { if(ret != -1) { wanted_size= disk_offset + bytes; iso_node_size= iso_file_get_size((IsoFile *) node); if(wanted_size > disk_offset + iso_node_size) wanted_size= disk_offset + iso_node_size; cap= wanted_size; ret= Xorriso_determine_capacity(xorriso, open_path_pt, &cap, &reason, 3); if(ret <= 0 || (cap <= 0 && !S_ISREG(stbuf.st_mode))) { if(ret > 0) reason= "has addressable range 0"; sprintf(xorriso->info_text, "Restore offset demanded. But target file (%s) ", Ftypetxt(stbuf.st_mode, 0)); Text_shellsafe(disk_path, xorriso->info_text, 1); strcat(xorriso->info_text, reason); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE",0); l_errno= 0; goto cannot_restore; } if(cap < wanted_size && !S_ISREG(stbuf.st_mode)) { /* >>> non-regular file might be too small to take the interval */; } } } else { /* If source and target are the same disk file then do not copy content */ ret= Xorriso_restore_is_identical(xorriso, (void *) node, img_path, disk_path, type_text, 1); if(ret<0) goto ex; if(ret==1) { /* preliminarily emulate touch (might get overridden later) */ utime_buffer.actime= stbuf.st_atime; utime_buffer.modtime= time(0); utime(disk_path,&utime_buffer); goto restore_properties; } if(ret==2) { /* Extract to temporary file and rename only after copying */ ret= Xorriso_make_tmp_path(xorriso, disk_path, temp_path, &write_fd, 128); if(ret <= 0 || ret == 4) goto ex; open_path_pt= temp_path; new_empty= 1; } } if(write_fd==-1) { open_flags= O_WRONLY|O_CREAT; if(disk_offset==0 || !(flag&2)) open_flags|= O_EXCL; write_fd= open(open_path_pt, open_flags | O_BINARY, S_IRUSR | S_IWUSR); l_errno= errno; if(write_fd == -1 && errno == EACCES && (flag & 128)) {ret= 4; goto ex;} if(write_fd==-1) goto cannot_restore; } if(new_empty && open_path_pt != NULL) { ret= Xorriso_early_chattr_CF(xorriso, node, open_path_pt, &l_errno, 0); if(ret <= 0) goto cannot_restore; } if(ISO_NODE_IS_BOOTCAT(node)) { ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; ret= iso_image_get_bootcat(volume, &bootcat, &lba, &catcontent, &catsize); if(ret < 0) goto ex; todo= size= catsize; } else { todo= size= iso_file_get_size((IsoFile *) node); } if(flag&2) { if(bytes<size) todo= size= bytes; seek_ret= lseek(write_fd, disk_offset, SEEK_SET); l_errno= errno; if(seek_ret == -1) { sprintf(xorriso->info_text, "Cannot address byte %.f in filesystem path ", (double) disk_offset); Text_shellsafe(open_path_pt, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0); goto cannot_restore; } } if(ISO_NODE_IS_FILE(node)) Xorriso_sparse_init(xorriso, &sparse_state, write_fd, 0); while(todo>0) { wanted= buf_size; if(wanted>todo) wanted= todo; if(ISO_NODE_IS_BOOTCAT(node)) { ret= todo; buf_pt= catcontent; } else { ret= Xorriso_iso_file_read(xorriso, data_stream, buf, wanted, 0); buf_pt= buf; } if(ret<=0) { if(xorriso->extract_error_mode == 0 && Xorriso_is_plain_image_file(xorriso, node, "", 0)) { close(write_fd); write_fd= -1; already_done= (size - todo) / (off_t) 2048; already_done*= (off_t) 2048; sprintf(xorriso->info_text, "Starting best_effort handling on ISO file "); Text_shellsafe(img_path, xorriso->info_text, 1); sprintf(xorriso->info_text + strlen(xorriso->info_text), " at byte %.f", (double) already_done); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= Xorriso_read_file_data(xorriso, node, img_path, open_path_pt, already_done, already_done, size - already_done, 2); if(ret >= 0) xorriso->pacifier_byte_count+= todo; if(ret > 0) todo= 0; else todo= -1; } if(ret <= 0) { sprintf(xorriso->info_text, "Cannot read all bytes from ISO file "); Text_shellsafe(img_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } break; } read_count+= ret; if(use_md5) Xorriso_md5_compute(xorriso, ctx, buf_pt, ret, 0); if(img_offset > read_count - ret) { /* skip the desired amount of bytes */ if(read_count <= img_offset) continue; buf_pt= buf_pt + (img_offset - (read_count - ret)); ret= read_count - img_offset; } if(sparse_state == NULL) wret= write(write_fd, buf_pt, ret); else wret= Xorriso_sparse_write(xorriso, sparse_state, write_fd, buf_pt, ret, 0); if(wret>=0) { todo-= wret; xorriso->pacifier_byte_count+= wret; if((flag&16) && xorriso->pacifier_byte_count - last_p_count >= 128*1024) { Xorriso_pacifier_callback(xorriso, "files restored", xorriso->pacifier_count, xorriso->pacifier_total, "", 2 | 4 | 8); last_p_count= xorriso->pacifier_byte_count; } } if(wret != ret) { sprintf(xorriso->info_text, "Cannot write all bytes to disk filesystem path "); Text_shellsafe(open_path_pt, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0); break; } } if(use_md5) { ret= Xorriso_md5_end(xorriso, &ctx, disk_md5, 0); if(ret <= 0) { sprintf(xorriso->info_text, "Internal problem with obtaining computed MD5 for extracted data of "); goto bad_md5; } else { for(i= 0; i < 16; i++) if(iso_md5[i] != disk_md5[i]) break; if(i < 16) { sprintf(xorriso->info_text, "MD5 of extracted data does not match recorded MD5 of file "); bad_md5:; Text_shellsafe(img_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); ret= Xorriso_eval_problem_status(xorriso, 0, 1 | 2); if(ret < 0) {ret= 0; goto ex;} } } } if(write_fd != -1) { sparse_ret= Xorriso_sparse_finish(xorriso, &sparse_state, write_fd, 0); close(write_fd); } write_fd= -1; if(todo > 0 && xorriso->extract_error_mode == 2) { unlink(open_path_pt); target_deleted= 1; } if(! ISO_NODE_IS_BOOTCAT(node)) Xorriso_iso_file_close(xorriso, &data_stream, 0); data_stream= NULL; if(temp_path==open_path_pt && !target_deleted) { ret= rename(temp_path, disk_path); if(ret==-1) { sprintf(xorriso->info_text, "Cannot rename temporary path "); Text_shellsafe(temp_path, xorriso->info_text, 1); strcat(xorriso->info_text, " to final disk path "); Text_shellsafe(disk_path, xorriso->info_text, 1 | 2); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0); unlink(temp_path); ret= 0; goto ex; } } ret= -(todo > 0); l_errno= 0; if(sparse_ret <= 0) { strcpy(xorriso->info_text, "Could not finalize sparse extraction of "); Text_shellsafe(disk_path, xorriso->info_text, 1 | 2); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, sparse_ret < 0 ? errno : 0, "FAILURE", 0); } } else if(LIBISO_ISLNK(node)) { what= "symbolic link"; link_target= (char *) iso_symlink_get_dest((IsoSymlink *) node); ret= symlink(link_target, disk_path); l_errno= errno; } else if(LIBISO_ISCHR(node)) { what= "character device"; if(xorriso->allow_restore!=2) { ignored:; if(!(flag&4)) { sprintf(xorriso->info_text, "Ignored file type: %s ", what); Text_shellsafe(img_path, xorriso->info_text, 1); strcat(xorriso->info_text, " = "); Text_shellsafe(disk_path, xorriso->info_text, 1 | 2); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } {ret= 2; goto ex;} } mode= S_IFCHR | 0777; ret= Xorriso_node_get_dev(xorriso, node, img_path, &dev, 0); if(ret<=0) goto ex; if(dev == (dev_t) 1) { probably_damaged:; sprintf(xorriso->info_text, "Most probably damaged device file not restored: mknod "); Text_shellsafe(disk_path, xorriso->info_text, 1); sprintf(xorriso->info_text + strlen(xorriso->info_text), " %s 0 1", LIBISO_ISCHR(node) ? "c" : "b"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } ret= mknod(disk_path, mode, dev); l_errno= errno; } else if(LIBISO_ISBLK(node)) { what= "block device"; if(xorriso->allow_restore!=2) goto ignored; mode= S_IFBLK | 0777; ret= Xorriso_node_get_dev(xorriso, node, img_path, &dev, 0); if(ret<=0) goto ex; if(dev == (dev_t) 1) goto probably_damaged; ret= mknod(disk_path, mode, dev); l_errno= errno; } else if(LIBISO_ISFIFO(node)) { what= "named pipe"; mode= S_IFIFO | 0777; ret= mknod(disk_path, mode, dev); l_errno= errno; } else if(LIBISO_ISSOCK(node)) { what= "unix socket"; /* Restoring a socket file is not possible. One rather needs to restart the service which temporarily created the socket. */ goto ignored; } else { sprintf(xorriso->info_text, "Cannot restore file type '%s'", what); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); ret= 0; goto ex; } if(ret == -1 && l_errno == EACCES && (flag & 128)) {ret= 4; goto ex;} if(ret==-1) { cannot_restore:; sprintf(xorriso->info_text, "Cannot restore %s to disk filesystem: ", what); Text_shellsafe(img_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, l_errno, "FAILURE", 0); ret= 0; goto ex; } restore_properties:; if((flag&8) || LIBISO_ISLNK(node)) ret= 1; else ret= Xorriso_restore_properties(xorriso, disk_path, node, flag&1); if(todo < 0) ret= 0; ex:; if(write_fd != -1) { close(write_fd); if(ret <= 0 && xorriso->extract_error_mode == 2 && open_path_pt != NULL) unlink(open_path_pt); } Xorriso_free_meM(buf); Xorriso_free_meM(temp_path); if(catcontent != NULL) free(catcontent); if(data_stream!=NULL) Xorriso_iso_file_close(xorriso, &data_stream, 0); if(ctx != NULL) Xorriso_md5_end(xorriso, &ctx, disk_md5, 0); if(sparse_state != NULL) Xorriso_sparse_finish(xorriso, &sparse_state, -1, 0); Xorriso_process_msg_queues(xorriso,0); return(ret); } /* Handle overwrite situation in disk filesystem. @param node intended source of overwriting or NULL @param flag bit4= return 3 on rejection by exclusion or user bit6= permission to call Xorriso_make_accessible() */ int Xorriso_restore_overwrite(struct XorrisO *xorriso, IsoNode *node, char *img_path, char *path, char *nominal_path, struct stat *stbuf, int flag) { int ret; char type_text[5]; Xorriso_process_msg_queues(xorriso,0); if(xorriso->do_overwrite==1 || (xorriso->do_overwrite==2 && !S_ISDIR(stbuf->st_mode))) { ret= Xorriso_restore_is_identical(xorriso, (void *) node, img_path, path, type_text, (node!=NULL)); if(ret<0) return(ret); if(ret>0) /* will be handled properly by restore functions */ ret= Xorriso_reassure_restore(xorriso, path, 8); else ret= Xorriso_rmx(xorriso, (off_t) 0, path, 8 | (flag & 64)); if(ret<=0) return(ret); if(ret==3) { sprintf(xorriso->info_text, "User revoked restoring of (ISO) file: "); Text_shellsafe(img_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(3*!!(flag&16)); } return(1); } Xorriso_msgs_submit(xorriso, 0, nominal_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "While restoring "); Text_shellsafe(nominal_path, xorriso->info_text, 1); strcat(xorriso->info_text, " : "); if(strcmp(nominal_path, path) == 0) strcat(xorriso->info_text, "file object"); else Text_shellsafe(path, xorriso->info_text, 1 | 2); strcat(xorriso->info_text, " exists and may not be overwritten"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } /* @return <0 error, bit0= hardlink created bit1= siblings with target NULL found bit2= siblings with non-NULL target found */ int Xorriso_restore_target_hl(struct XorrisO *xorriso, IsoNode *node, char *disk_path, int *node_idx, int flag) { int ret, min_hl, max_hl, i, null_target_sibling= 0, link_sibling= 0; if(xorriso->hln_targets == NULL) return(0); ret= Xorriso_search_hardlinks(xorriso, node, node_idx, &min_hl, &max_hl, 1); if(ret < 0) return(ret); if(ret == 0 || *node_idx < 0 || min_hl == max_hl) return(0); for(i= min_hl; i <= max_hl; i++) { if(xorriso->hln_targets[i] == NULL) { if(i != *node_idx) null_target_sibling= 1; continue; } link_sibling= 1; ret= Xorriso_restore_make_hl(xorriso, xorriso->hln_targets[i], disk_path, !!xorriso->do_auto_chmod); if(ret > 0) return(1); } return((null_target_sibling << 1) | (link_sibling << 2)); } /* @return <0 error, bit0= hardlink created bit2= siblings lower index found */ int Xorriso_restore_prefix_hl(struct XorrisO *xorriso, IsoNode *node, char *disk_path, int node_idx, int flag) { int ret, min_hl, max_hl, i, link_sibling= 0, hflag; char *old_path= NULL, *img_path= NULL; struct Xorriso_lsT *img_prefixes= NULL, *disk_prefixes= NULL; Xorriso_alloc_meM(old_path, char, SfileadrL); Xorriso_alloc_meM(img_path, char, SfileadrL); ret= Xorriso_search_hardlinks(xorriso, node, &node_idx, &min_hl, &max_hl, 2 | 4); if(ret < 0) goto ex; if(ret == 0 || min_hl == max_hl) {ret= 0; goto ex;} for(i= min_hl; i < node_idx; i++) { link_sibling= 1; ret= Xorriso_path_from_node(xorriso, xorriso->node_array[i], img_path, 0); if(ret < 0) goto ex; if(ret == 0) continue; /* Node is deleted from tree (Should not happen here) */ hflag= 1; if(i == min_hl) { hflag= 0; } else if(xorriso->node_array[i] != xorriso->node_array[i - 1]) { hflag= 0; } if(hflag == 0) { img_prefixes= xorriso->node_img_prefixes; disk_prefixes= xorriso->node_disk_prefixes; } ret= Xorriso_make_restore_path(xorriso, &img_prefixes, &disk_prefixes, img_path, old_path, hflag); if(ret <= 0) goto ex; ret= Xorriso_restore_make_hl(xorriso, old_path, disk_path, !!xorriso->do_auto_chmod); if(ret > 0) {ret= 1; goto ex;} } ret= link_sibling << 2; ex:; Xorriso_free_meM(old_path); Xorriso_free_meM(img_path); return(ret); } /* @return <0 = error , 0 = availmem exhausted first time , 1 = ok 2 = availmem exhausted repeated */ int Xorriso_register_node_target(struct XorrisO *xorriso, int node_idx, char *disk_path, int flag) { int l; if(xorriso->node_targets_availmem == 0) return(2); if(xorriso->hln_targets == NULL || node_idx < 0 || node_idx >= xorriso->hln_count) return(0); if(xorriso->hln_targets[node_idx] != NULL) { xorriso->node_targets_availmem+= strlen(xorriso->hln_targets[node_idx]) +1; free(xorriso->hln_targets[node_idx]); } l= strlen(disk_path); if(xorriso->node_targets_availmem <= l + 1) { sprintf(xorriso->info_text, "Hardlink target buffer exceeds -temp_mem_limit. Hardlinks may get divided."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); xorriso->node_targets_availmem= 0; return(0); } xorriso->hln_targets[node_idx]= strdup(disk_path); if(xorriso->hln_targets[node_idx] == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); return(-1); } xorriso->node_targets_availmem-= (l + 1); return(1); } /* @param flag bit0= offset and bytes is valid for writing to regular file bit1= do not report copied files bit2= -follow, -not_*: this is not a command parameter bit3= keep directory open: keep owner, allow rwx for owner bit4= do not look for hardlinks even if enabled bit6= this is a copy action: do not fake times and ownership bit7= return 4 if restore fails from denied permission do not issue error message bit10= do not restore properties @return <=0 = error , 1 = added leaf file object , 2 = added directory , 3= regularly not installed (disallowed device, UNIX domain socket) 4 = with bit7: permission to restore was denied */ int Xorriso_restore_disk_object(struct XorrisO *xorriso, char *img_path, IsoNode *node, char *disk_path, off_t offset, off_t bytes, int flag) { int ret, i, split_count= 0, partno, total_parts, leaf_is_split= 0; int record_hl_path= 0, node_idx, cannot_register= 0, no_props; off_t total_bytes; char *part_name, *part_path= NULL, *img_path_pt= NULL; IsoImage *volume; IsoNode *part_node, *first_part_node= NULL; struct SplitparT *split_parts= NULL; struct stat stbuf; Xorriso_alloc_meM(part_path, char, SfileadrL); no_props= !!(flag & 1024); ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; if(LIBISO_ISDIR(node) && xorriso->do_concat_split) leaf_is_split= Xorriso_identify_split(xorriso, img_path, node, &split_parts, &split_count, &stbuf, 1 | 4); if(leaf_is_split) { /* map all files in directory img_path into regular file disk_path */ for(i=0 ; i<split_count; i++) { Splitparts_get(split_parts, i, &part_name, &partno, &total_parts, &offset, &bytes, &total_bytes, 0); strcpy(part_path, img_path); if(Sfile_add_to_path(part_path, part_name, 0)<=0) { Xorriso_much_too_long(xorriso, strlen(img_path)+strlen(part_name)+1, 2); goto restoring_failed; } ret= Xorriso_node_from_path(xorriso, volume, part_path, &part_node, 0); if(ret<=0) goto restoring_failed; if(i==0) first_part_node= part_node; if(offset+bytes>total_bytes) bytes= total_bytes-offset; ret= Xorriso_tree_restore_node(xorriso, part_node, part_path, (off_t) 0, disk_path, offset, bytes, (!!(flag&64)) | 2 | (flag & (4 | 128)) | 8 | ( 16 * !(flag&2))); if(ret<=0) goto restoring_failed; if(ret == 4) goto ex; if(ret == 2) {ret= 3; goto ex;} } if(first_part_node != NULL && !no_props) { ret= Xorriso_restore_properties(xorriso, disk_path, first_part_node, !!(flag&64)); if(ret <= 0) goto restoring_failed; } goto went_well; } #ifdef Osirrox_not_yeT if(resolve_link) { ret= Xorriso_resolve_link(xorriso, disk_path, resolved_disk_path, 0); if(ret<=0) goto ex; disk_path_pt= resolved_disk_path; } else #endif /* Osirrox_not_yeT */ img_path_pt= img_path; if(!((xorriso->ino_behavior & 4) || (flag & (1 | 16)) || LIBISO_ISDIR(node))){ /* Try to restore as hardlink */ ret= Xorriso_restore_target_hl(xorriso, node, disk_path, &node_idx, !!xorriso->do_auto_chmod); if(ret < 0) { goto ex; } else if(ret & 1) { /* Success, hardlink was created */ goto went_well; } else if(ret & 2) { /* Did not establish hardlink. Hardlink siblings with target NULL found.*/ record_hl_path= 1; } if(ret & 4) { /* Found siblings with non-NULL target, but did not link. */ ret= Xorriso_eval_problem_status(xorriso, 1, 1 | 2); if(ret < 0) {ret= 0; goto ex;} } } ret= Xorriso_tree_restore_node(xorriso, node, img_path_pt, (off_t) 0, disk_path, offset, bytes, (flag&(4 | 8 | 128)) | (!!(flag&64)) | ((flag&1)<<1) | ( 16 * !(flag&2)) | (no_props << 3)); if(ret == 4) goto ex; if(ret == 2) {ret= 3; goto ex;} if(ret > 0 && (flag & 8) && !no_props) ret= Xorriso_restore_properties(xorriso, disk_path, node, 2 | !!(flag&64)); if(ret<=0) { restoring_failed:; sprintf(xorriso->info_text, "Restoring failed: "); Text_shellsafe(img_path, xorriso->info_text, 1); strcat(xorriso->info_text, " = "); Text_shellsafe(disk_path, xorriso->info_text, 1 | 2); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); {ret= 0; goto ex;} } if(record_hl_path) { /* Start of a disk hardlink family */ ret= Xorriso_register_node_target(xorriso, node_idx, disk_path, 0); if(ret < 0) goto ex; if(ret == 0) cannot_register= 1; } went_well:; xorriso->pacifier_count++; if(!(flag&2)) Xorriso_pacifier_callback(xorriso, "files restored", xorriso->pacifier_count, xorriso->pacifier_total, "", 4 | 8); ret= 1; ex:; if(split_parts!=NULL) Splitparts_destroy(&split_parts, split_count, 0); Xorriso_free_meM(part_path); if(ret > 0 && cannot_register) ret= 0; return(ret); } /* @param flag bit0= source is a directory and not to be restored as split file >>> bit6= permission to call Xorriso_make_accessible() @return <=0 error , 1=collision handled , 2=no collision , 3=revoked by user */ int Xorriso_handle_collision(struct XorrisO *xorriso, IsoNode *node, char *img_path, char *disk_path, char *nominal_disk_path, int *stbuf_ret, int flag) { int ret, target_is_dir= 0, target_is_link= 0, stat_ret, made_accessible= 0; struct stat target_stbuf, lt_stbuf; struct PermiteM *perm_stack_mem; perm_stack_mem= xorriso->perm_stack; /* does a disk file exist with this name ? */ *stbuf_ret= lstat(disk_path, &target_stbuf); if(*stbuf_ret==-1) { if((flag & 64) && errno == EACCES) { ret= Xorriso_make_accessible(xorriso, disk_path, 0); if(ret < 0) goto ex; made_accessible= 1; *stbuf_ret= lstat(disk_path, &target_stbuf); } if(*stbuf_ret==-1) {ret= 2; goto ex;} } target_is_link= S_ISLNK(target_stbuf.st_mode); if(target_is_link) { stat_ret= stat(disk_path, <_stbuf); if(stat_ret == -1) { if((flag & 64) && errno == EACCES && !made_accessible) { ret= Xorriso_make_accessible(xorriso, disk_path, 0); if(ret < 0) goto ex; made_accessible= 1; stat_ret= stat(disk_path, <_stbuf); } } if(stat_ret != -1) target_is_dir= S_ISDIR(lt_stbuf.st_mode); } else { target_is_dir= S_ISDIR(target_stbuf.st_mode); } if(target_is_dir && (!target_is_link) && !(flag&1)) { strcpy(xorriso->info_text, "Attempt to replace DISK directory "); Text_shellsafe(nominal_disk_path, xorriso->info_text+strlen(xorriso->info_text), 0); strcat(xorriso->info_text, " by ISO file "); Text_shellsafe(img_path, xorriso->info_text+strlen(xorriso->info_text), 0); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(!(target_is_dir && (flag&1))) { Xorriso_process_msg_queues(xorriso,0); ret= Xorriso_restore_overwrite(xorriso, node, img_path, disk_path, nominal_disk_path, &target_stbuf, 16 | (flag & 64)); if(ret==3) {ret= 3; goto ex;} if(ret<=0) goto ex; *stbuf_ret= -1; /* It might still exist but will be handled properly */ } ret= 1; ex:; if(made_accessible) Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, 0); return(ret); } /* @param flag bit0= recursion is active bit1= do not report restored files bit6= this is a copy action: do not fake times and ownership bit7+8= 0= direct operation 1= create only directories, count nodes in xorriso->node_counter 2= only register non-directory nodes in xorriso->node_array 3= count nodes in xorriso->node_counter, create no directory */ int Xorriso_restore_tree(struct XorrisO *xorriso, IsoDir *dir, char *img_dir_path, char *disk_dir_path, off_t boss_mem, struct LinkiteM *link_stack, int flag) { IsoImage *volume; IsoNode *node; IsoDirIter *iter= NULL; IsoNode **node_array= NULL; int node_count= 0, node_idx; int ret, source_is_dir, fret, was_failure= 0, sret; int do_not_dive, source_is_split= 0, len_dp, len_ip, stbuf_ret, hflag, hret; char *name, *disk_name, *leaf_name, *srcpt, *stbuf_src= ""; struct LinkiteM *own_link_stack; char *sfe= NULL, *sfe2= NULL; char *disk_path= NULL, *img_path= NULL, *link_target= NULL; off_t mem; struct PermiteM *perm_stack_mem; struct stat stbuf, disk_stbuf; int dir_create= 0, node_register= 0, do_node_count= 0, normal_mode= 0; int target_was_no_dir, dir_is_new; struct stat *stack_stbufpt; int lfa_ia_pushed= 0, stack_chattr; perm_stack_mem= xorriso->perm_stack; switch((flag >> 7) & 3) { case 0: normal_mode= 1; break; case 1: dir_create= 1; break; case 2: node_register= 1; break; case 3: do_node_count= 1; } /* Avoiding large local memory objects in order to save stack space */ sfe= malloc(5*SfileadrL); sfe2= malloc(5*SfileadrL); disk_path= malloc(2*SfileadrL); img_path= malloc(2*SfileadrL); link_target= malloc(SfileadrL); if(sfe==NULL || sfe2==NULL || disk_path==NULL || img_path==NULL || link_target==NULL) { Xorriso_no_malloc_memory(xorriso, &sfe, 0); {ret= -1; goto ex;} } own_link_stack= link_stack; ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; stbuf_src= img_dir_path; node= (IsoNode *) dir; ret= Xorriso_fake_stbuf(xorriso, stbuf_src, &stbuf, &node, 1); if(ret<=0) { Xorriso_msgs_submit(xorriso, 0, disk_dir_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text,"Cannot open as (ISO) source directory: %s", Text_shellsafe(img_dir_path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } #ifdef Osirrox_not_yeT dev_t dir_dev; dir_dev= stbuf.st_dev; if(S_ISLNK(stbuf.st_mode)) { if(!(xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&1)))) {ret= 2; goto ex;} stbuf_src= disk_dir_path; if(stat(disk_dir_path, &stbuf)==-1) goto cannot_open_dir; if(dir_dev != stbuf.st_dev && !(xorriso->do_follow_mount || (xorriso->do_follow_param && !(flag&1)))) {ret= 2; goto ex;} } #endif /* Osirrox_not_yeT */ if(!S_ISDIR(stbuf.st_mode)) { Xorriso_msgs_submit(xorriso, 0, disk_dir_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text,"Is not a directory in ISO image: %s", Text_shellsafe(img_dir_path, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } mem= boss_mem; ret= Xorriso_findi_iter(xorriso, dir, &mem, &iter, &node_array, &node_count, &node_idx, &node, 1 | 4 * (normal_mode && (xorriso->ino_behavior & 4))); if(ret<=0) goto ex; if(Sfile_str(img_path, img_dir_path,0)<=0) { much_too_long:; Xorriso_much_too_long(xorriso, SfileadrL, 2); {ret= 0; goto ex;} } if(img_path[0]==0 || img_path[strlen(img_path)-1]!='/') strcat(img_path,"/"); name= img_path+strlen(img_path); if(Sfile_str(disk_path, disk_dir_path, 0)<=0) goto much_too_long; if(disk_path[0]==0 || disk_path[strlen(disk_path)-1]!='/') strcat(disk_path,"/"); disk_name= disk_path+strlen(disk_path); len_dp= strlen(disk_path); len_ip= strlen(img_path); while(1) { /* loop over ISO directory content */ stbuf_src= ""; target_was_no_dir= 0; dir_is_new= 0; #ifdef Osirrox_not_yeT Linkitem_reset_stack(&own_link_stack, link_stack, 0); #endif srcpt= img_path; Xorriso_process_msg_queues(xorriso,0); ret= Xorriso_findi_iter(xorriso, dir, &mem, &iter, &node_array, &node_count, &node_idx, &node, 0); if(ret<0) goto ex; if(ret==0 || xorriso->request_to_abort) break; leaf_name= (char *) iso_node_get_name(node); if(Xorriso_much_too_long(xorriso, len_dp + strlen(leaf_name)+1, 0)<=0) {ret= 0; goto was_problem;} if(Xorriso_much_too_long(xorriso, len_ip + strlen(leaf_name)+1, 0)<=0) {ret= 0; goto was_problem;} /* name and disk_name are pointers into img_path and disk_path */ strcpy(name, leaf_name); strcpy(disk_name, leaf_name); ret= Xorriso_path_is_excluded(xorriso, disk_path, 2); if(ret < 0) goto was_problem; if(ret > 0) continue; stbuf_src= srcpt; ret= Xorriso_fake_stbuf(xorriso, img_path, &stbuf, &node, 1); if(ret<=0) goto was_problem; source_is_dir= 0; #ifdef Osirrox_not_yeT /* ??? Link following in the image would cause severe problems with Xorriso_path_from_node() */ int source_is_link; source_is_link= S_ISLNK(stbuf.st_mode); if(xorriso->do_follow_links && source_is_link) { /* Xorriso_hop_link checks for wide link loops */ ret= Xorriso_hop_link(xorriso, srcpt, &own_link_stack, &hstbuf, 0); if(ret<0) goto was_problem; if(ret==1) { ret= Xorriso_resolve_link(xorriso, srcpt, link_target, 0); if(ret<=0) goto was_problem; srcpt= link_target; stbuf_src= srcpt; if(lstat(srcpt, &stbuf)==-1) goto cannot_lstat; } else { if(Xorriso_eval_problem_status(xorriso, 0, 1|2)<0) {ret= 0; goto was_problem;} } } else if (S_ISLNK(stbuf.st_mode)) { ret= Xorriso_resolve_link(xorriso, srcpt, link_target, 1); if(ret<=0) goto was_problem; } #endif /* Osirrox_not_yeT */ do_not_dive= 0; if(S_ISDIR(stbuf.st_mode)) source_is_dir= 1; source_is_split= 0; if(source_is_dir) source_is_split= Xorriso_is_split(xorriso, img_path, node, 1 | 2 | 4); if(source_is_split) do_not_dive= 1; if(source_is_dir || !(dir_create || do_node_count || node_register)) { ret= Xorriso_handle_collision(xorriso, node, img_path, disk_path, disk_path, &stbuf_ret, (source_is_dir && !source_is_split)); if(ret<=0 || ret==3) goto was_problem; } else { stbuf_ret= -1; } if(stbuf_ret!=-1) { /* (Can only happen with directory) */ Xorriso_auto_chmod(xorriso, disk_path, 0); } else { hflag= 4 | (flag & (2|64)); if(source_is_dir && !do_not_dive) hflag|= 8; /* keep directory open for user */ if((dir_create || do_node_count) && !source_is_dir) { xorriso->node_counter++; } else if(node_register && !source_is_dir) { if(xorriso->node_counter < xorriso->node_array_size) { xorriso->node_array[xorriso->node_counter++]= (void *) node; iso_node_ref(node); } } else if(node_register || do_node_count) { ret= 1; } else { if(source_is_dir) { ret= lstat(disk_path, &disk_stbuf); if(ret == -1) { target_was_no_dir= 1; } else { if(!S_ISDIR(disk_stbuf.st_mode)) target_was_no_dir= 1; } } ret= Xorriso_restore_disk_object(xorriso, img_path, node, disk_path, (off_t) 0, (off_t) 0, hflag); if(ret == 3) /* intentionally not restored */ do_not_dive= 1; if(target_was_no_dir) { sret= lstat(disk_path, &disk_stbuf); if(sret != -1) if(S_ISDIR(disk_stbuf.st_mode)) dir_is_new= 1; } } if(ret<=0) goto was_problem; } ret= 1; if(source_is_dir && !do_not_dive) { ret= Xorriso_restore_tree(xorriso, (IsoDir *) node, img_path, disk_path, mem, own_link_stack, 1 | (flag & (2 | (3 << 7)))); } if(source_is_dir) { /* Restore exact access permissions of directory */ hret= Permstack_peek(&(xorriso->perm_stack), perm_stack_mem, xorriso, disk_path, &stack_stbufpt, &stack_chattr, 0); if(hret == 1 && (stack_chattr & (1 | 4))) lfa_ia_pushed= 1; hret= Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, !!(flag&64)); if(hret<=0 && hret<ret) ret= hret; if(dir_is_new && !(dir_create || lfa_ia_pushed)) { hret= Xorriso_restore_chattr_ia(xorriso, node, disk_path, 0); if(hret <= 0 && hret < ret) ret= hret; } } if(ret <= 0) goto was_problem; continue; /* regular bottom of loop */ was_problem:; was_failure= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret<0) goto ex; Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, !!(flag&64)); } ret= 1; ex: Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, !!(flag&64)); if(sfe!=NULL) free(sfe); if(sfe2!=NULL) free(sfe2); if(disk_path!=NULL) free(disk_path); if(img_path!=NULL) free(img_path); if(link_target!=NULL) free(link_target); Xorriso_findi_iter(xorriso, dir, &mem, &iter, &node_array, &node_count, &node_idx, &node, (1u<<31)); Xorriso_process_msg_queues(xorriso,0); #ifdef Osirrox_not_yeT Linkitem_reset_stack(&own_link_stack, link_stack, 0); #endif if(ret<=0) return(ret); return(!was_failure); } /* @param flag >>> bit0= mkdir: graft in as empty directory, not as copy from iso bit1= do not report copied files bit2= -follow, -not_*: this is not a command parameter bit3= use offset and cut_size for -paste_in bit4= return 3 on rejection by exclusion or user bit5= if directory then do not add sub tree bit6= this is a copy action: do not fake times and ownership bit7+8= operation mode 0= direct operation 1= create only directories, count nodes in xorriso->node_counter 2= only register non-directory nodes in xorriso->node_array 3= count nodes in xorriso->node_counter, create no directory bit9= with operation mode 1 do net register prefixes bit10= with bit3: do not restore properties to leaf file @return <=0 = error , 1 = added leaf file object , 2 = added directory , 3 = rejected */ int Xorriso_restore(struct XorrisO *xorriso, char *img_path, char *disk_path, off_t offset, off_t bytes, int flag) { IsoImage *volume; char *path= NULL, *apt, *npt; IsoNode *node= NULL; int done= 0, is_dir= 0, ret, source_is_dir, stbuf_ret, hret; int dir_create= 0, node_count= 0, node_register= 0, path_size, l_errno= 0; int leaf_is_split= 0, source_is_split= 0, new_dir_made= 0, no_props; struct stat stbuf; struct PermiteM *perm_stack_mem; struct stat *stack_stbufpt; int lfa_ia_pushed= 0, stack_chattr; perm_stack_mem= xorriso->perm_stack; path_size= SfileadrL; Xorriso_alloc_meM(path, char, path_size); switch((flag >> 7) & 3) { case 1: dir_create= 1; break; case 2: node_register= 1; break; case 3: node_count= 1; } no_props= (!!(flag & 8)) * (flag & 1024); if(dir_create && !(flag & (1 << 9))) { ret= Xorriso_lst_append_binary(&(xorriso->node_disk_prefixes), disk_path, strlen(disk_path) + 1, 0); if(ret <= 0) goto ex; ret= Xorriso_lst_append_binary(&(xorriso->node_img_prefixes), img_path, strlen(img_path) + 1, 0); if(ret <= 0) goto ex; } ret= Xorriso_path_is_excluded(xorriso, disk_path, 4 | 2 | !(flag & 4)); if(ret < 0) {ret= 0; goto ex;} if(ret > 0) { if(!(flag & 4)) { strcpy(xorriso->info_text, "Excluded from restoring by -not_path or -not_leaf :"); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } {ret= 3*!!(flag&16); goto ex;} } ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; strncpy(path, disk_path, path_size - 1); path[path_size - 1]= 0; apt= npt= path; if(!(flag&1)) { ret= Xorriso_fake_stbuf(xorriso, img_path, &stbuf, &node, 0); if(ret>0) { if(S_ISDIR(stbuf.st_mode)) is_dir= 1; #ifdef Osirrox_not_yeT /* ??? this would cause severe problems with Xorriso_path_from_node() */ else if((stbuf.st_mode&S_IFMT)==S_IFLNK && (xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&4)))) { resolve_link= 1; ret= Xorriso_iso_lstat(xorriso, img_path, &stbuf, 1|2); if(ret!=-1) { if(S_ISDIR(stbuf.st_mode)) is_dir= 1; } } #endif /* Osirrox_not_yeT */ } else { Xorriso_process_msg_queues(xorriso,0); Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "Cannot determine attributes of (ISO) source file "); Text_shellsafe(img_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } if(is_dir && xorriso->do_concat_split) leaf_is_split= Xorriso_is_split(xorriso, img_path, node, 1 | 2 | 4); } for(npt= apt; !done; apt= npt+1) { npt= strchr(apt, '/'); if(npt==NULL) { npt= apt+strlen(apt); done= 1; } else *npt= 0; if(*apt==0) { *apt= '/'; apt++; if(done) goto attach_source; continue; } source_is_dir= (is_dir || (flag&1) || !done); source_is_split= done && leaf_is_split; stbuf_ret= -1; if((flag&8) && done) { /* ??? move down from Xorriso_paste_in() : check whether target does not exist or both are suitable */; } else if(source_is_dir || !(dir_create || node_count || node_register)) { ret= Xorriso_handle_collision(xorriso, node, img_path, path, disk_path, &stbuf_ret, (source_is_dir && !source_is_split)); if(ret<=0 || ret==3) goto ex; } new_dir_made= 0; if(stbuf_ret==-1 && (source_is_dir && !source_is_split) && !(node_count || node_register)) { /* make a directory */ ret= mkdir(path, 0777); if(ret==-1) { Xorriso_process_msg_queues(xorriso,0); Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "While restoring '%s' : could not insert '%s'", disk_path, path); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0); {ret= 0; goto ex;} } if(done) { if(node != NULL) { ret= Xorriso_early_chattr_CF(xorriso, node, disk_path, &l_errno, 0); if(ret <= 0) goto ex; } } else { /* keep rwx for the owner, delay lfa_flags 'i' if present */ ret= Xorriso_restore_implicit_properties(xorriso, disk_path, path, img_path, 4); if(ret <= 0) goto ex; } new_dir_made= 1; } else if((source_is_dir && !source_is_split)) { if(!(node_count || node_register)) Xorriso_auto_chmod(xorriso, path, 0); } if(done) { attach_source:; if(flag&1) { /* directory was created above */; } else if(is_dir && !source_is_split) { if(!node_register) { if(new_dir_made) { /* keep open and push to Permstack */ ret= Xorriso_restore_properties(xorriso, disk_path, node, 2 | !!(flag&64)); if(ret <= 0) { hret= Xorriso_eval_problem_status(xorriso, ret, 1 | 2); if(hret < 0) goto ex; } } } if(!(flag&32)) { ret= Xorriso_restore_tree(xorriso, (IsoDir *) node, img_path, path, (off_t) 0, NULL, flag & (2 | 64 | (3 << 7))); if(ret <= 0) { hret= Xorriso_eval_problem_status(xorriso, ret, 1 | 2); if(hret < 0) goto ex; } } if(new_dir_made && !(flag&64)) { /* set timestamps which Permstack_pop() will not set */ ret= Xorriso_restore_timestamps(xorriso, disk_path, node, 0); if(ret <= 0) { hret= Xorriso_eval_problem_status(xorriso, ret, 1 | 2); if(hret < 0) goto ex; } } } else { if(dir_create || node_count) { xorriso->node_counter++; } else if(node_register) { if(xorriso->node_counter < xorriso->node_array_size) { xorriso->node_array[xorriso->node_counter++]= (void *) node; iso_node_ref(node); } } else { ret= Xorriso_restore_disk_object(xorriso, img_path, node, path, offset, bytes, (flag & (2|4|64)) | no_props | !!(flag&8)); if(ret <= 0) { hret= Xorriso_eval_problem_status(xorriso, ret, 1 | 2); if(hret < 0) goto ex; } } } } else { *npt= '/'; } } if(new_dir_made && !dir_create) { ret= 1; /* Need to set any properties before possibly setting immutable bit. So pop earlier than normal. */ hret= Permstack_peek(&(xorriso->perm_stack), perm_stack_mem, xorriso, disk_path, &stack_stbufpt, &stack_chattr, 0); if(hret == 1 && (stack_chattr & (1 | 4))) lfa_ia_pushed= 1; hret= Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, 2 | !!(flag&64)); if(hret <= 0 && hret < ret) ret= hret; if(!lfa_ia_pushed) { hret= Xorriso_restore_chattr_ia(xorriso, node, disk_path, 0); if(hret <= 0 && hret < ret) ret= hret; if(ret <= 0) goto ex; } } Xorriso_process_msg_queues(xorriso,0); ret= 1 + (is_dir && !leaf_is_split); ex:; /* restore exact access permissions of stacked paths */ hret= Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, 2 | !!(flag&64)); if(hret<=0 && hret<ret) ret= hret; Xorriso_free_meM(path); return(ret); } int Xorriso_restore_node_array(struct XorrisO *xorriso, int flag) { int i, ret, fret, hflag, stbuf_ret, faulty_family= 0; struct PermiteM *perm_stack_mem; char *img_path= NULL, *disk_path= NULL; IsoNode *node; struct Xorriso_lsT *img_prefixes= NULL, *disk_prefixes= NULL; perm_stack_mem= xorriso->perm_stack; Xorriso_alloc_meM(img_path, char, SfileadrL); Xorriso_alloc_meM(disk_path, char, SfileadrL); Xorriso_sort_node_array(xorriso, 0); disk_path[0]= 0; for(i= 0; i < xorriso->node_counter; i++) { node= (IsoNode *) xorriso->node_array[i]; ret= Xorriso_path_from_node(xorriso, node, img_path, 0); if(ret < 0) goto ex; if(ret == 0) continue; /* Node is deleted from tree (Should not happen here) */ hflag= 1; if(i == 0) { hflag= 0; } else if(node != xorriso->node_array[i - 1]) { hflag= 0; } if(hflag == 0) { img_prefixes= xorriso->node_img_prefixes; disk_prefixes= xorriso->node_disk_prefixes; } ret= Xorriso_make_restore_path(xorriso, &img_prefixes, &disk_prefixes, img_path, disk_path, hflag); if(ret<=0) goto was_problem; /* (Exclusion must already be handled when the node array gets filled, because of no distinction between parameter and tree passenger here.) */ ret= Xorriso_handle_collision(xorriso, node, img_path, disk_path, disk_path, &stbuf_ret, 64); if(ret<=0 || ret==3) goto was_problem; if(xorriso->hln_array != NULL && !(xorriso->ino_behavior & 16)) { /* Eventual lookup of hardlinks will be done in Xorriso_restore_disk_object() */; } else if(i > 0 && !(xorriso->ino_behavior & 4)) { if(Xorriso__findi_sorted_ino_cmp(&(xorriso->node_array[i-1]), &(xorriso->node_array[i])) == 0) { if(faulty_family) { sprintf(xorriso->info_text, "Hardlinking omitted with "); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } else { /* Try to install hardlink to a sibling */ ret= Xorriso_restore_prefix_hl(xorriso, node, disk_path, i, 0); if(ret < 0) { goto was_problem; } else if(ret & 1) { /* Success, hardlink was created */ xorriso->pacifier_count++; continue; } if(ret & 4) { /* Found elder siblings, but did not link. */ ret= Xorriso_eval_problem_status(xorriso, 1, 1 | 2); if(ret < 0) {ret= 0; goto ex;} } } } else faulty_family= 0; } ret= Xorriso_restore_disk_object(xorriso, img_path, node, disk_path, (off_t) 0, (off_t) 0, 4 | (xorriso->ino_behavior & 16) | 128); if(ret<=0) goto was_problem; if(ret == 4) { /* Failed from lack of permission */ ret= Xorriso_make_accessible(xorriso, disk_path, 0); if(ret < 0) goto ex; ret= Xorriso_restore_disk_object(xorriso, img_path, node, disk_path, (off_t) 0, (off_t) 0, 4 | (xorriso->ino_behavior & 16)); if(ret<=0) goto was_problem; Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, 0); } continue; /* regular bottom of loop */ was_problem:; faulty_family= 1; fret= Xorriso_eval_problem_status(xorriso, ret, 1|2); if(fret<0) goto ex; Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, 0); } ret= 1; ex:; Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, 0); Xorriso_free_meM(img_path); Xorriso_free_meM(disk_path); return(ret); } /* @param flag bit0= -follow, -not: disk_path is not a command parameter */ int Xorriso_paste_in(struct XorrisO *xorriso, char *disk_path, off_t startbyte, off_t bytecount, char *iso_rr_path, int flag) { int ret, no_props= 0; off_t wanted_size, cap; char *eff_source= NULL, *eff_dest= NULL, *reason; struct stat stbuf, disk_stbuf; IsoNode *node; Xorriso_alloc_meM(eff_source, char, SfileadrL); Xorriso_alloc_meM(eff_dest, char, SfileadrL); ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, disk_path, eff_dest, 2|4); if(ret<=0) goto ex; ret= Xorriso_path_is_excluded(xorriso, disk_path, 4 | 2 | !(flag & 1)); if(ret > 0 && !(flag & 1)) { strcpy(xorriso->info_text, "Excluded from restoring by -not_path or -not_leaf :"); Text_shellsafe(disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); } if(ret!=0) {ret= 0; goto ex;} ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, iso_rr_path, eff_source, 2); if(ret<=0) goto ex; ret= Xorriso_fake_stbuf(xorriso, eff_source, &stbuf, &node, 4); if(ret<=0) {ret= 0; goto ex;} if(!S_ISREG(stbuf.st_mode)) { Xorriso_msgs_submit(xorriso, 0, eff_dest, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "-paste_in: ISO file "); Text_shellsafe(eff_source, xorriso->info_text, 1); strcat(xorriso->info_text, " is not a data file"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); {ret= 0; goto ex;} } /* >>> ??? flag bit to obtain startbyte and bytecount from file name */; ret= lstat(eff_dest, &disk_stbuf); if(ret != -1) { no_props= 1 << 10; wanted_size= startbyte + bytecount; if(wanted_size > stbuf.st_size) wanted_size= stbuf.st_size; cap= wanted_size; ret= Xorriso_determine_capacity(xorriso, eff_dest, &cap, &reason, 3); if(ret <= 0 || (cap <= 0 && !S_ISREG(disk_stbuf.st_mode))) { Xorriso_msgs_submit(xorriso, 0, eff_dest, 0, "ERRFILE", 0); if(ret > 0) reason= "has addressable range 0"; sprintf(xorriso->info_text, "-paste_in: DISK file (%s) ", Ftypetxt(disk_stbuf.st_mode, 0)); Text_shellsafe(eff_dest, xorriso->info_text, 1); sprintf(xorriso->info_text + strlen(xorriso->info_text), " exists but %s", reason); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(cap < wanted_size && !S_ISREG(disk_stbuf.st_mode)) { /* >>> non-regular file might be too small to take the interval */; } } ret= Xorriso_restore(xorriso, eff_source, eff_dest, startbyte, bytecount, 8 | no_props); ex:; Xorriso_free_meM(eff_source); Xorriso_free_meM(eff_dest); return(ret); } int Xorriso_extract_cut(struct XorrisO *xorriso, char *img_path, char *disk_path, off_t img_offset, off_t bytes, int flag) { int ret, stbuf_ret, read_raw; double mem_lut= 0.0; char *eff_img_path= NULL, *eff_disk_path= NULL; IsoImage *volume; IsoNode *node; Xorriso_alloc_meM(eff_img_path, char, SfileadrL); Xorriso_alloc_meM(eff_disk_path, char, SfileadrL); ret= Xorriso_get_volume(xorriso, &volume, 0); if(ret<=0) goto ex; ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, img_path, eff_img_path, 0); if(ret<=0) goto ex; ret= Xorriso_node_from_path(xorriso, volume, eff_img_path, &node, 0); if(ret<=0) goto ex; ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, disk_path, eff_disk_path, 2 | 4); if(ret<=0) goto ex; ret= Xorriso_path_is_excluded(xorriso, eff_disk_path, 1 | 2 | 4); if(ret < 0) goto ex; if(ret > 0) { strcpy(xorriso->info_text, "Excluded from restoring by -not_path or -not_leaf :"); Text_shellsafe(eff_disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); {ret= 0; goto ex;} } /* If it is a non-filtered stream from the ISO image and img_offset is a multiple of 2048 then use Xorriso_read_file_data() for random access offset. */ if(!LIBISO_ISREG(node)) { Xorriso_msgs_submit(xorriso, 0, eff_disk_path, 0, "ERRFILE", 0); sprintf(xorriso->info_text, "-extract_cut: ISO file "); Text_shellsafe(eff_img_path, xorriso->info_text, 1); strcat(xorriso->info_text, " is not a data file"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } ret= Xorriso_handle_collision(xorriso, node, img_path, eff_disk_path, disk_path, &stbuf_ret, 0); if(ret<=0 || ret==3) {ret= 0; goto ex;} Xorriso_pacifier_reset(xorriso, 0); mem_lut= xorriso->last_update_time; read_raw= 0; if((img_offset % 2048) == 0) { ret= Xorriso_is_plain_image_file(xorriso, node, "", 0); if(ret > 0) read_raw= 1; } if (read_raw) { ret= Xorriso_read_file_data(xorriso, node, eff_img_path, eff_disk_path, img_offset, (off_t) 0, bytes, 0); if(ret<=0) goto ex; } else { ret= Xorriso_tree_restore_node(xorriso, node, eff_img_path, img_offset, eff_disk_path, (off_t) 0, bytes, 2 | 8); if(ret<=0) goto ex; if(ret != 1) {ret= 0; goto ex;} } ret= Xorriso_restore_properties(xorriso, eff_disk_path, node, 0); if(ret<=0) goto ex; if(mem_lut != xorriso->last_update_time) Xorriso_pacifier_callback(xorriso, "blocks read", xorriso->pacifier_count, 0, "", 1 | 8 | 16 | 32); ret= 1; ex:; Xorriso_free_meM(eff_img_path); Xorriso_free_meM(eff_disk_path); return(ret); } /* @param flag bit0= ignore node and img_path, operate on whole medium bit1= for Xorriso_check_interval(): no pacifier messages */ int Xorriso_read_file_data(struct XorrisO *xorriso, IsoNode *node, char *img_path, char *disk_path, off_t img_offset, off_t disk_offset, off_t bytes, int flag) { int ret, i, lba_count= 0, read_chunk= 16; int quality, bad_extract= 0; off_t lba, count, blocks, spot, *start_lbas= NULL, *end_lbas= NULL; int data_to_skip= 0; off_t indev_blocks; off_t size= 0, file_base_bytes= 0, file_processed_bytes= 0, img_adr; off_t new_file_base_bytes, upto_file_bytes, start_byte= 0; off_t *section_sizes = NULL; struct SpotlisT *spotlist= NULL; struct CheckmediajoB *job= NULL; upto_file_bytes= img_offset + bytes; data_to_skip= img_offset % (off_t) 2048; if(flag & 1) { lba_count= 1; Xorriso_alloc_meM(start_lbas, off_t, 1); Xorriso_alloc_meM(end_lbas, off_t, 1); Xorriso_alloc_meM(section_sizes, off_t, 1); start_lbas[0]= 0; ret= Xorriso_obtain_indev_readsize(xorriso, &indev_blocks, 0); if(ret > 0) end_lbas[0]= indev_blocks - 1; else end_lbas[0]= 0x7ffffffffffffffe; size= end_lbas[0] * (off_t) 2048; section_sizes[0]= size; } else { ret= Xorriso__start_end_lbas(node, &lba_count, &start_lbas, &end_lbas, §ion_sizes, &size, 0); if(ret <= 0) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "File object "); Text_shellsafe(img_path, xorriso->info_text, 1); strcat(xorriso->info_text, " is currently not a data file from the loaded image"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto ex; } } if(img_offset + bytes < size && bytes > 0) size= img_offset + bytes; ret= Checkmediajob_new(&job, 0); if(ret <= 0) goto ex; if(xorriso->check_media_default != NULL) Checkmediajob_copy(xorriso->check_media_default, job, 0); job->min_lba= -1; job->max_lba= -1; job->sector_map_path[0]= 0; ret= Spotlist_new(&spotlist, 0); if(ret <= 0) {ret= -1; goto ex;} if(Sfile_str(job->data_to_path, disk_path, 0) <= 0) {ret= -1; goto ex;} ret= Xorriso_open_job_data_to(xorriso, job, 0); if(ret <= 0) goto ex; for(i= 0; i < lba_count && file_base_bytes < upto_file_bytes; i++) { lba= start_lbas[i]; count= end_lbas[i] + 1 - start_lbas[i]; new_file_base_bytes= file_base_bytes + count * (off_t) 2048; /* skip intervals before img_offset */ if(new_file_base_bytes <= img_offset) { file_base_bytes= new_file_base_bytes; continue; } /* Eventually adjust first interval start */ img_adr= lba * (off_t) 2048; if(file_base_bytes < img_offset) { img_adr+= img_offset - file_base_bytes; lba= img_adr / (off_t) 2048; count= end_lbas[i] + 1 - lba; file_base_bytes= img_offset; } /* Omit surplus blocks */ if(new_file_base_bytes > upto_file_bytes) count-= (new_file_base_bytes - upto_file_bytes) / (off_t) 2048; /* Adjust job */ job->data_to_offset= file_processed_bytes - img_adr + disk_offset; job->data_to_limit= size - file_base_bytes; job->data_to_skip= data_to_skip; data_to_skip= 0; file_processed_bytes+= count * (off_t) 2048; ret= Xorriso_check_interval(xorriso, spotlist, job, lba, count, read_chunk, 0, (flag & 2)); if(ret <= 0) goto ex; if (ret == 2) { sprintf(xorriso->info_text, "Attempt aborted to extract data from "); Text_shellsafe(img_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } file_base_bytes= new_file_base_bytes; } /* Use spotlist to evaluate damage */ file_base_bytes= 0; count= Spotlist_count(spotlist, 0); for(spot= 0; spot < count; spot++) { ret= Spotlist_get_item(spotlist, spot, &lba, &blocks, &quality, 0); if(ret <= 0) continue; if(quality < Xorriso_read_quality_valiD) { for(i= 0; i < lba_count; i++) { if(start_lbas[i] <= lba && end_lbas[i] >= lba) { start_byte= (lba - start_lbas[i]) * (off_t) 2048 + file_base_bytes; break; } file_base_bytes+= ((off_t) (end_lbas[i] + 1 - start_lbas[i])) * (off_t) 2048; } if(i < lba_count) { sprintf(xorriso->info_text, "Bad extract : %14.f , %14.f , ", (double) start_byte, ((double) blocks) * 2048.0); Text_shellsafe(disk_path, xorriso->info_text, 1); strcat(xorriso->info_text, "\n"); Xorriso_info(xorriso, 0); bad_extract= 1; } } } ret= !bad_extract; ex:; if(start_lbas != NULL) free((char *) start_lbas); if(end_lbas != NULL) free((char *) end_lbas); if(section_sizes != NULL) free((char *) section_sizes); Spotlist_destroy(&spotlist, 0); Checkmediajob_destroy(&job, 0); return(ret); } int Xorriso_extract_boot_images(struct XorrisO *xorriso, char *disk_dir_path, int flag) { int ret, img_count= 0, i, was_problem= 0; char **imgs= NULL, *eff_path= NULL, *cpt, *eff_namept; struct stat stbuf; off_t byte_offset, byte_size; Xorriso_alloc_meM(eff_path, char, SfileadrL); ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, disk_dir_path, eff_path, 2 | 4); if(ret <= 0) goto ex; if(strlen(eff_path) > SfileadrL - 80) { sprintf(xorriso->info_text, "-extract_boot_images: disk_path is too long (%lu)\n", (unsigned long int) strlen(eff_path)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } ret= stat(eff_path, &stbuf); if(ret == 0) { if(!S_ISDIR(stbuf.st_mode)) { sprintf(xorriso->info_text, "-extract_boot_images: disk_path is not a directory : "); Text_shellsafe(eff_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } else { ret= mkdir(eff_path, 0777); if(ret == -1) { sprintf(xorriso->info_text, "-extract_boot_images: cannot create directory : "); Text_shellsafe(eff_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } } strcat(eff_path, "/"); eff_namept= eff_path + strlen(eff_path); ret= Xorriso_list_boot_images(xorriso, &imgs, &img_count, 0); if(ret <= 0) goto ex; /* Interpret list and create files */ for(i= 0; i < img_count; i++) { ret= Xorriso_eval_problem_status(xorriso, 1, 1 | 2); if(ret < 0) {ret= 0; goto ex;} cpt= strchr(imgs[i], '/'); if(cpt == NULL) continue; *cpt= 0; cpt+= 2; ret= Sfile_text_to_off_t(cpt, &byte_offset, 0); if(ret <= 0) continue; cpt+= ret; if(*cpt == 0) continue; cpt++; ret= Sfile_text_to_off_t(cpt, &byte_size, 0); if(ret <= 0) continue; strcpy(eff_namept, imgs[i]); sprintf(xorriso->info_text, "%s : offset=%.f size=%.f\n", eff_path, (double) byte_offset, (double) byte_size); Xorriso_info(xorriso, 0); ret= stat(eff_path, &stbuf); if(ret != -1) { sprintf(xorriso->info_text, "-extract_boot_images: File already exists on disk: "); Text_shellsafe(eff_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); continue; } ret= Xorriso_read_file_data(xorriso, NULL, NULL, eff_path, byte_offset, (off_t) 0, byte_size, 1); if(ret <= 0) was_problem= 1; } ret= Xorriso_eval_problem_status(xorriso, 1, 1 | 2); if(ret < 0 || was_problem) {ret= 0; goto ex;} ret= 1; ex:; Xorriso_free_meM(eff_path); Xorriso_list_boot_images(xorriso, &imgs, &img_count, 1 << 15); return(ret); } /* @param node Opaque handle to IsoNode which is to be inquired instead of path if it is not NULL. @param path is used as address if node is NULL. @param flag bit0= do not report to result but only indicate outcome by return value bit1= silently ignore nodes without MD5 bit2= do not only report mismatches but also matches @return 3= not a data file 2= no MD5 attached to node 1= ok, MD5 compared and matching 0= not ok, MD5 mismatch <0= other error */ int Xorriso_check_md5(struct XorrisO *xorriso, void *in_node, char *path, int flag) { int ret, wanted, rret, buffer_size= 64 * 1024; IsoImage *image; IsoNode *node; IsoFile *file; char node_md5[16], data_md5[16], *buffer= NULL; void *stream= NULL, *ctx= NULL; off_t todo; Xorriso_alloc_meM(buffer, char, 64 * 1024); node= (IsoNode *) in_node; if(node == NULL) { ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0); if(ret<=0) {ret= -1; goto ex;} } if(!LIBISO_ISREG(node)) { strcpy(xorriso->info_text, "-check_md5: Not a data file: "); Text_shellsafe(path, xorriso->info_text, 1); if(!(flag & 2)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 3; goto ex; } file= (IsoFile *) node; /* obtain MD5 */ ret= Xorriso_get_volume(xorriso, &image, 0); if(ret <= 0) {ret= -1; goto ex;} ret= iso_file_get_md5(image, file, node_md5, 0); Xorriso_process_msg_queues(xorriso,0); if(ret < 0) {ret= -1; goto ex;} if(ret == 0) { strcpy(xorriso->info_text, "-check_md5: No MD5 recorded with file: "); Text_shellsafe(path, xorriso->info_text, 1); if(!(flag & 2)) Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= 2; goto ex; } /* Read file and compute MD5 */; ret= Xorriso_iso_file_open(xorriso, path, (void *) node, &stream, 1 | 2); if(ret <= 0) {ret= -1; goto ex;} ret= iso_md5_start(&ctx); if(ret < 0) goto ex; todo= iso_stream_get_size(stream); while(todo > 0) { if(todo < buffer_size) wanted= todo; else wanted= buffer_size; rret = Xorriso_iso_file_read(xorriso, stream, buffer, wanted, 0); if(rret <= 0) {ret= -1; goto ex;} todo-= rret; ret = iso_md5_compute(ctx, buffer, rret); if(ret < 0) goto ex; xorriso->pacifier_count+= rret; xorriso->pacifier_byte_count+= rret; Xorriso_pacifier_callback(xorriso, "content bytes read", xorriso->pacifier_count, 0, "", 8); ret= Xorriso_check_for_abort( xorriso, xorriso->check_media_default != NULL ? xorriso->check_media_default->abort_file_path : "/var/opt/xorriso/do_abort_check_media", Sfile_microtime(0), &xorriso->last_abort_file_time, 0); if(ret == 1) {ret= -2; goto ex;} } ret= iso_md5_end(&ctx, data_md5); if(ret < 0) goto ex; /* Report outcome */ Xorriso_process_msg_queues(xorriso,0); if(! iso_md5_match(node_md5, data_md5)) { sprintf(xorriso->result_line, "MD5 MISMATCH: "); Text_shellsafe(path, xorriso->result_line, 1); strcat(xorriso->result_line, "\n"); if(!(flag & 1)) Xorriso_result(xorriso,0); ret= 0; } else { sprintf(xorriso->result_line, "md5 match : "); Text_shellsafe(path, xorriso->result_line, 1); strcat(xorriso->result_line, "\n"); if(flag & 4) Xorriso_result(xorriso,0); ret= 1; } ex:; Xorriso_process_msg_queues(xorriso,0); Xorriso_iso_file_close(xorriso, &stream, 0); if(ctx != NULL) iso_md5_end(&ctx, data_md5); Xorriso_free_meM(buffer); if(ret < 0) { if(ret == -2) sprintf(xorriso->result_line, "Aborted at: "); else sprintf(xorriso->result_line, "NOT READABLE: "); Text_shellsafe(path, xorriso->result_line, 1); strcat(xorriso->result_line, "\n"); if(!(flag & 1)) Xorriso_result(xorriso,0); if(ret == -2) xorriso->request_to_abort= 1; } return(ret); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2010 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of functions which */ #ifndef Xorriso_pvt_read_run_includeD #define Xorriso_pvt_read_run_includeD yes int Xorriso__read_pacifier(IsoImage *image, IsoFileSource *filesource); int Xorriso_restore_properties(struct XorrisO *xorriso, char *disk_path, IsoNode *node, int flag); int Xorriso_restore_implicit_properties(struct XorrisO *xorriso, char *full_disk_path, char *disk_path, char *full_img_path, int flag); int Xorriso_tree_restore_node(struct XorrisO *xorriso, IsoNode *node, char *img_path, off_t img_offset, char *disk_path, off_t disk_offset, off_t bytes, int flag); int Xorriso_restore_overwrite(struct XorrisO *xorriso, IsoNode *node, char *img_path, char *path, char *nominal_path, struct stat *stbuf, int flag); int Xorriso_restore_target_hl(struct XorrisO *xorriso, IsoNode *node, char *disk_path, int *node_idx, int flag); int Xorriso_restore_prefix_hl(struct XorrisO *xorriso, IsoNode *node, char *disk_path, int node_idx, int flag); int Xorriso_register_node_target(struct XorrisO *xorriso, int node_idx, char *disk_path, int flag); int Xorriso_restore_disk_object(struct XorrisO *xorriso, char *img_path, IsoNode *node, char *disk_path, off_t offset, off_t bytes, int flag); int Xorriso_handle_collision(struct XorrisO *xorriso, IsoNode *node, char *img_path, char *disk_path, char *nominal_disk_path, int *stbuf_ret, int flag); int Xorriso_restore_tree(struct XorrisO *xorriso, IsoDir *dir, char *img_dir_path, char *disk_dir_path, off_t boss_mem, struct LinkiteM *link_stack, int flag); int Xorriso_read_file_data(struct XorrisO *xorriso, IsoNode *node, char *img_path, char *disk_path, off_t img_offset, off_t disk_offset, off_t bytes, int flag); #endif /* ! Xorriso_pvt_read_run_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2020 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of functions around files and strings. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <pwd.h> #include <grp.h> #include "sfile.h" /* @param flag bit0= do not clip off carriage return at line end */ char *Sfile_fgets_n(char *line, int maxl, FILE *fp, int flag) { int l; char *ret; ret= fgets(line,maxl,fp); if(ret==NULL) return(NULL); l= strlen(line); if(l > 0 && !(flag & 1)) if(line[l-1] == '\r') line[--l]= 0; if(l > 0) if(line[l-1] == '\n') line[--l]= 0; if(l > 0 && !(flag & 1)) if(line[l-1] == '\r') line[--l]= 0; return(ret); } int Sfile_count_char(char *text, char to_count) { int count= 0; char *cpt; for(cpt= text; *cpt != 0; cpt++) if(*cpt == to_count) count++; return count; } int Sfile_count_components(char *path, int flag) /* bit0= do not ignore trailing slash bit1= do not ignore empty components (other than the empty root name) */ { int l,count= 0; char *cpt; l= strlen(path); if(l==0) return(0); count= 1; for(cpt= path+l-1;cpt>=path;cpt--) { if(*cpt=='/') { if(*(cpt+1)==0 && !(flag&1)) continue; if(*(cpt+1)=='/' && !(flag&2)) continue; count++; } } return(count); } int Sfile_component_pointer(char *path, char **sourcept, int idx, int flag) /* bit0= do not ignore trailing slash bit1= do not ignore empty components (other than the empty root name) bit2= accept 0 as '/' */ { int count= 0; char *spt; for(spt= path;*spt!=0 || (flag&4);spt++) { if(count>=idx) { *sourcept= spt; return(1); } if(*spt=='/' || *spt==0) { if(*(spt+1)=='/' && !(flag&2)) continue; if(*(spt+1)==0 && !(flag&1)) continue; count++; } } if((flag&1) && count>=idx) return(1); return(0); } int Sfile_leafname(char *path, char leafname[SfileadrL], int flag) { int count, ret; char *lpt; leafname[0]= 0; count= Sfile_count_components(path, 0); if(count==0) return(0); ret= Sfile_component_pointer(path, &lpt, count-1, 0); if(ret<=0) return(ret); if(Sfile_str(leafname, lpt, 0)<=0) return(0); lpt= strchr(leafname, '/'); if(lpt!=NULL) *lpt= 0; return(1); } int Sfile_add_to_path(char path[SfileadrL], char *addon, int flag) { int l; l= strlen(path); if(l+1>=SfileadrL) return(0); if(l==0) { strcpy(path,"/"); l= 1; } else if(path[l-1]!='/') { path[l++]= '/'; path[l]= 0; } if(l+strlen(addon)>=SfileadrL) return(0); if(addon[0]=='/') strcpy(path+l,addon+1); else strcpy(path+l,addon); return(1); } int Sfile_prepend_path(char *prefix, char path[SfileadrL], int flag) { int l, i, slashes, prefix_len, path_len; l= strlen(prefix); if(l == 0) return(1); /* Do not copy slashes between both parts */ for(prefix_len= l; prefix_len > 0; prefix_len--) if(prefix[prefix_len - 1] != '/') break; if(prefix_len == 0) prefix_len= strlen(prefix) - 1; path_len= strlen(path); for(slashes= 0; slashes < path_len; slashes++) if(path[slashes] != '/') break; l= (strlen(path) - slashes) + prefix_len + 1; if(l>=SfileadrL) { #ifdef Not_yeT /* >>> ??? how to transport messages to xorriso ? */ sprintf(xorriso->info_text, "Combination of wd and relative address too long (%d > %d)", l,SfileadrL-1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); #endif return(-1); } l-= strlen(path); if(l < 0) { for(i= slashes; i <= path_len + 1; i++) path[i+l]= path[i]; } else if(l > 0) { for(i= path_len + 1; i >= slashes; i--) path[i+l]= path[i]; } if(prefix_len > 0) memcpy(path, prefix, prefix_len); path[l - 1 + slashes]= '/'; return(1); } int Sfile_get_dev_fd_no(char *filename, int flag) { int i, fd= -1; if(strncmp(filename, "/dev/fd/", 8) != 0) return(-1); for(i = 8; filename[i]; i++) if(filename[i] < '0' || filename[i] > '9') break; if(i > 8 && filename[i] == 0) sscanf(filename + 8, "%d", &fd); if(fd < 0) fd = -1; return(fd); } int Sfile_type(char *filename, int flag) /* bit0= return -1 if file is missing bit1= return a hardlink with siblings as type 5 bit2= evaluate eventual link target rather than the link object itself bit3= return a socket or a char device as types 7 or 8 rather than 0 bit4= interpret /dev/fd/#fd# as open file descriptor fd */ /* return: 0=unknown 1=regular 2=directory 3=symbolic link 4=named pipe 5=multiple hardlink (with bit1) 6=block device 7=socket (with bit3) 8=character device (with bit3) | 1024 if interpreted as /dev/fd/#fd# | 2048 if interpreted as /dev/fd/#fd# and not fstatable */ { struct stat stbuf; int fd= -1, was_dev_fd= 0, ret; if(flag & 16) fd= Sfile_get_dev_fd_no(filename, 0); if(fd != -1) { was_dev_fd= 1; if(fstat(fd, &stbuf) == -1) return(1024 | 2048); } else if(flag&4) { if(stat(filename,&stbuf)==-1) { if(flag&1) return(-1); else return(0); } } else { if(lstat(filename,&stbuf)==-1) { if(flag&1) return(-1); else return(0); } } if(S_ISREG(stbuf.st_mode)) { if(flag&2) if(stbuf.st_nlink>1) {ret= 5; goto ex;} {ret= 1; goto ex;} } if(S_ISDIR(stbuf.st_mode)) {ret= 2; goto ex;} if((stbuf.st_mode&S_IFMT)==S_IFLNK) {ret= 3; goto ex;} if(S_ISFIFO(stbuf.st_mode)) {ret= 4; goto ex;} if(S_ISBLK(stbuf.st_mode)) {ret= 6; goto ex;} if(flag&8) if((stbuf.st_mode&S_IFMT)==S_IFSOCK) {ret= 7; goto ex;} if(flag&8) if(S_ISCHR(stbuf.st_mode)) {ret= 8; goto ex;} ret = 0; ex:; return(ret | (was_dev_fd << 10)); } char *Sfile_datestr(time_t tim, short int flag) /* bit0=with hours+minutes bit1=with seconds bit8= local time rather than UTC */ { static char zeitcode[80]={"000000"}; char puff[80]; struct tm *azt; if(flag&256) azt = localtime(&tim); else azt = gmtime(&tim); if(azt->tm_year>99) sprintf(zeitcode,"%c%1.1d%2.2d%2.2d", 'A'+(azt->tm_year-100)/10,azt->tm_year%10, azt->tm_mon+1,azt->tm_mday); else sprintf(zeitcode,"%2.2d%2.2d%2.2d", azt->tm_year,azt->tm_mon+1,azt->tm_mday); if(flag&1){ sprintf(puff,".%2.2d%2.2d",azt->tm_hour,azt->tm_min); strcat(zeitcode,puff); } if(flag&2){ sprintf(puff,"%2.2d",azt->tm_sec); strcat(zeitcode,puff); } return(zeitcode); } int Sfile_scale(double value, char *result, int siz, double thresh, int flag) /* bit0= omit 'b' if it would elsewise be appended bit1= make text as short as possible bit2= no fraction (if it would fit at all) */ { char scale_c,scales[7],form[80], *negpt= NULL, *cpt; int i,dec_siz= 0,avail_siz= 1; if(value<0) { value= -value; siz--; result[0]= '-'; negpt= result; result++; } strcpy(scales,"bkmgtp"); scale_c= scales[0]; for(i=1;scales[i]!=0;i++) { if(value<thresh-0.5) break; value/= 1024.0; scale_c= scales[i]; } if(scale_c!='b' && !(flag&4)) { /* is there room for fractional part ? */ avail_siz= siz-1; sprintf(form,"%%.f"); sprintf(result,"%.f",value); if(((int) strlen(result)) <= avail_siz - 2) dec_siz= 1; /* we are very modest */ } if(scale_c=='b' && (flag&1)) { if(flag&2) sprintf(form,"%%.f"); else sprintf(form,"%%%d.f",siz); sprintf(result,form,value); } else { if(flag&2) sprintf(form,"%%.f%%c"); else if(dec_siz>0) sprintf(form,"%%%d.%df%%c",avail_siz,dec_siz); else sprintf(form,"%%%d.f%%c",siz-1); sprintf(result,form,value,scale_c); } if(negpt != NULL) { for(cpt= result; *cpt==' '; cpt++); if(cpt > result) { *negpt= ' '; *(cpt - 1)= '-'; } } return(1); } int Sfile_off_t_text(char text[80], off_t num, int flag) { char *tpt; off_t hnum, scale= 1; int digits= 0, d, i; tpt= text; hnum= num; if(hnum<0) { *(tpt++)= '-'; hnum= -num; } if(hnum<0) { /* it can stay nastily persistent */ strcpy(text, "_overflow_"); return(0); } for(i= 0; i<23; i++) { /* good for up to 70 bit = 10 exp 21.07... */ if(hnum==0) break; hnum/= 10; if(hnum) scale*= 10; } if(i==0) { strcpy(text, "0"); return(1); } if(i==23) { strcpy(text, "_overflow_"); return(0); } digits= i; hnum= num; for(; i>0; i--) { d= hnum/scale; tpt[digits-i]= '0'+d; hnum= hnum%scale; scale/= 10; } tpt[digits]= 0; return(1); } /* @return index number of first not interpreted text byte */ int Sfile_text_to_off_t(char *text, off_t *num, int flag) { int sig= 1, ridx; *num= 0; ridx= 0; if(text[ridx] == '-') { sig= -1; ridx++; } else if(text[ridx] == '+') { ridx++; } for(; text[ridx] != 0; ridx++) { if(text[ridx] < '0' || text[ridx] > '9') break; if(*num > (((off_t) 1) << 59)) return(-1); *num= *num * 10 + text[ridx] - '0'; } *num= *num * sig; return(ridx); } /* Converts backslash codes into single characters: \a BEL 7 , \b BS 8 , \e ESC 27 , \f FF 12 , \n LF 10 , \r CR 13 , \t HT 9 , \v VT 11 , \\ \ 92 \[0-9][0-9][0-9] octal code , \x[0-9a-f][0-9a-f] hex code , \cX control-x (ascii(X)-64) @param upto maximum number of characters to examine for backslash. The scope of a backslash (0 to 3 characters) is not affected. @param eaten returns the difference in length between input and output @param flag bit0= only determine *eaten, do not convert bit1= convert \000 to binary 0 */ int Sfile_bsl_interpreter(char *text, int upto, int *eaten, int flag) { char *rpt, *wpt, num_text[8], wdummy[8]; unsigned int num= 0; *eaten= 0; wpt= text; for(rpt= text; *rpt != 0 && rpt - text < upto; rpt++) { if(flag & 1) wpt= wdummy; if(*rpt == '\\') { rpt++; (*eaten)++; if(*rpt == 'a') { *(wpt++)= 7; } else if(*rpt == 'b') { *(wpt++)= 8; } else if(*rpt == 'e') { *(wpt++)= 27; } else if(*rpt == 'f') { *(wpt++)= 12; } else if(*rpt == 'n') { *(wpt++)= 10; } else if(*rpt == 'r') { *(wpt++)= 13; } else if(*rpt == 't') { *(wpt++)= 9; } else if(*rpt == 'v') { *(wpt++)= 11; } else if(*rpt == '\\') { *(wpt++)= '\\'; } else if(rpt[0] >= '0' && rpt[0] <= '7' && rpt[1] >= '0' && rpt[1] <= '7' && rpt[2] >= '0' && rpt[2] <= '7') { num_text[0]= '0'; num_text[1]= *(rpt + 0); num_text[2]= *(rpt + 1); num_text[3]= *(rpt + 2); num_text[4]= 0; sscanf(num_text, "%o", &num); if((num > 0 || (flag & 2)) && num <= 255) { rpt+= 2; (*eaten)+= 2; *(wpt++)= num; } else goto not_a_code; } else if(rpt[0] == 'x' && ((rpt[1] >= '0' && rpt[1] <= '9') || (rpt[1] >= 'A' && rpt[1] <= 'F') || (rpt[1] >= 'a' && rpt[1] <= 'f')) && ((rpt[2] >= '0' && rpt[2] <= '9') || (rpt[2] >= 'A' && rpt[2] <= 'F') || (rpt[2] >= 'a' && rpt[2] <= 'f')) ) { num_text[0]= *(rpt + 1); num_text[1]= *(rpt + 2); num_text[2]= 0; sscanf(num_text, "%x", &num); if(num > 0 && num <= 255) { rpt+= 2; (*eaten)+= 2; *(wpt++)= num; } else goto not_a_code; } else if(*rpt == 'c') { if(rpt[1] > 64 && rpt[1] < 96) { *(wpt++)= rpt[1] - 64; rpt++; (*eaten)++; } else goto not_a_code; } else { not_a_code:; *(wpt++)= '\\'; rpt--; (*eaten)--; } } else *(wpt++)= *rpt; } *wpt= *rpt; return(1); } /* @param flag bit0= only encode inside quotes bit1= encode < 32 outside quotes except 7, 8, 9, 10, 12, 13 bit2= encode in any case above 126 bit3= encode in any case shellsafe and name-value-safe: <=42 , 59, 60, 61, 62, 63, 92, 94, 96, >=123 */ int Sfile_bsl_encoder(char **result, char *text, size_t text_len, int flag) { signed char *rpt; char *wpt; int count, sq_open= 0, dq_open= 0; count= 0; for(rpt= (signed char *) text; (size_t) (((char *) rpt) - text) < text_len; rpt++) { count++; if(flag & 8) { if(!(*rpt <= 42 || (*rpt >= 59 && *rpt <= 63) || *rpt == 92 || *rpt == 94 || *rpt == 96 || *rpt >= 123)) continue; } else if(*rpt >= 32 && *rpt <= 126 && *rpt != '\\') continue; if(((*rpt >= 7 && *rpt <= 13) || *rpt == 27 || *rpt == '\\') && !(flag & 8)) count++; else count+= 3; } (*result)= wpt= calloc(count + 1, 1); if(wpt == NULL) return(-1); for(rpt= (signed char *) text; (size_t) (((char *) rpt) - text) < text_len; rpt++) { if(*rpt == '\'') sq_open= !(sq_open || dq_open); if(*rpt == '"') dq_open= !(sq_open || dq_open); if(flag & 8) { if(!(*rpt <= 42 || (*rpt >= 59 && *rpt <= 63) || *rpt == 92 || *rpt == 94 || *rpt == 96 || *rpt >= 123)) { *(wpt++)= *rpt; continue; } } else if(*rpt >= 32 && *rpt <= 126 && *rpt != '\\') { *(wpt++)= *rpt; continue; } else if( ((flag & 1) && !(sq_open || dq_open)) && !((flag & 2) && (*rpt >= 1 && * rpt <= 31 && !(*rpt == 7 || *rpt == 8 || *rpt == 9 || *rpt == 10 || *rpt == 12 || *rpt == 13))) && !((flag & 4) && (*rpt > 126 || *rpt < 0)) && !((flag & 6) && *rpt == '\\')) { *(wpt++)= *rpt; continue; } *(wpt++)= '\\'; if(((*rpt >= 7 && *rpt <= 13) || *rpt == 27 || *rpt == '\\') && !(flag&8)) { if(*rpt == 7) *(wpt++)= 'a'; else if(*rpt == 8) *(wpt++)= 'b'; else if(*rpt == 9) *(wpt++)= 't'; else if(*rpt == 10) { *(wpt++)= 'n'; } else if(*rpt == 11) *(wpt++)= 'v'; else if(*rpt == 12) *(wpt++)= 'f'; else if(*rpt == 13) *(wpt++)= 'r'; else if(*rpt == 27) *(wpt++)= 'e'; else if(*rpt == '\\') *(wpt++)= '\\'; } else { sprintf(wpt, "%-3.3o", (unsigned int) *((unsigned char *) rpt)); wpt+= 3; } } *wpt= 0; return(1); } int Sfile_destroy_argv(int *argc, char ***argv, int flag) { int i; if(*argc>0 && *argv!=NULL){ for(i=0;i<*argc;i++){ if((*argv)[i]!=NULL) Smem_freE((*argv)[i]); } Smem_freE((char *) *argv); } *argc= 0; *argv= NULL; return(1); } int Sfile_sep_make_argv(char *progname, char *line, char *separators, int max_words, int *argc, char ***argv, int flag) /* bit0= read progname as first argument from line bit1= just release argument list argv and return bit2= abort with return(0) if incomplete quotes are found bit3= eventually prepend missing '-' to first argument read from line bit4= like bit2 but only check quote completeness, do not allocate memory bit5+6= interpretation of backslashes: 0= no interpretation, leave unchanged 1= only inside double quotes 2= outside single quotes 3= everywhere bit7= append a NULL element to argv */ { int i,pass,maxl=0,l,argzaehl=0,bufl,line_start_argc, bsl_mode, ret= 0, eaten; char *cpt,*start; char *buf= NULL; Sfile_destroy_argv(argc,argv,0); if(flag&2) {ret= 1; goto ex;} if(flag & 16) flag|= 4; bsl_mode= (flag >> 5) & 3; buf= calloc(strlen(line) + SfileadrL, 1); if(buf == NULL) {ret= -1; goto ex;} for(pass=0;pass<2;pass++) { cpt= line-1; if(!(flag&1)){ argzaehl= line_start_argc= 1; if(pass==0) maxl= strlen(progname); else strcpy((*argv)[0],progname); } else { argzaehl= line_start_argc= 0; if(pass==0) maxl= 0; } while(*(++cpt)!=0){ if(*separators) { if(strchr(separators, *cpt) != NULL) continue; } else if(isspace(*cpt)) continue; start= cpt; buf[0]= 0; cpt--; if(max_words > 0 && argzaehl >= max_words && *cpt != 0) { /* take uninterpreted up to the end */ cpt+= strlen(cpt) - 1; } while(*(++cpt)!=0) { if(*separators) { if(strchr(separators, *cpt) != NULL) break; } else if(isspace(*cpt)) break; if(*cpt=='"'){ l= cpt-start; bufl= strlen(buf); if(l>0) { strncpy(buf + bufl, start, l); buf[bufl + l]= 0; if(bsl_mode >= 3) { ret= Sfile_bsl_interpreter(buf + bufl, l, &eaten, 0); if(ret <= 0) goto ex; } } l= strlen(buf); start= cpt+1; while(*(++cpt)!=0) if(*cpt=='"') break; if((flag&4) && *cpt==0) {ret= 0; goto ex;} l= cpt-start; bufl= strlen(buf); if(l>0) { strncpy(buf + bufl, start, l); buf[bufl + l]= 0; if(bsl_mode >= 1) { ret= Sfile_bsl_interpreter(buf + bufl, l, &eaten, 0); if(ret <= 0) goto ex; } } start= cpt+1; }else if(*cpt=='\''){ l= cpt-start; bufl= strlen(buf); if(l>0) { strncpy(buf + bufl, start, l); buf[bufl + l]= 0; if(bsl_mode >= 3) { ret= Sfile_bsl_interpreter(buf + bufl, l, &eaten, 0); if(ret <= 0) goto ex; } } l= strlen(buf); start= cpt+1; while(*(++cpt)!=0) if(*cpt=='\'') break; if((flag&4) && *cpt==0) {ret= 0; goto ex;} l= cpt-start; bufl= strlen(buf); if(l>0) { strncat(buf,start,l);buf[bufl+l]= 0; if(bsl_mode >= 2) { ret= Sfile_bsl_interpreter(buf + bufl, l, &eaten, 0); if(ret <= 0) goto ex; } } start= cpt+1; } if(*cpt==0) break; } l= cpt-start; bufl= strlen(buf); if(l>0) { strncpy(buf + bufl, start, l); buf[bufl + l]= 0; if(bsl_mode >= 3) { ret= Sfile_bsl_interpreter(buf + bufl, l, &eaten, 0); if(ret <= 0) goto ex; } } l= strlen(buf); if(pass==0){ if(argzaehl==line_start_argc && (flag&8)) if(buf[0]!='-' && buf[0]!=0 && buf[0]!='#') l++; if(l>maxl) maxl= l; }else{ strcpy((*argv)[argzaehl],buf); if(argzaehl==line_start_argc && (flag&8)) if(buf[0]!='-' && buf[0]!=0 && buf[0]!='#') sprintf((*argv)[argzaehl],"-%s", buf); } argzaehl++; if(*cpt==0) break; } if(pass==0){ if(flag & 16) {ret= 1; goto ex;} *argc= argzaehl; if(argzaehl>0 || (flag & 128)) { *argv= (char **) Smem_malloC((argzaehl + !!(flag & 128)) * sizeof(char *)); if(*argv==NULL) {ret= -1; goto ex;} } for(i=0;i<*argc;i++) { (*argv)[i]= (char *) Smem_malloC((maxl+1)); if((*argv)[i]==NULL) {ret= -1; goto ex;} } if(flag & 128) (*argv)[*argc]= NULL; } } ret= 1; ex: if(buf != NULL) free(buf); return(ret); } int Sfile_make_argv(char *progname, char *line, int *argc, char ***argv, int flag) { return Sfile_sep_make_argv(progname, line, "", 0, argc, argv, flag); } /* @param flag bit0= append */ int Sfile_str(char *target, char *source, int flag) { int l; l= strlen(source); if(flag&1) l+= strlen(target); if(l>=SfileadrL) { fprintf(stderr, "--- Path string overflow (%d > %d). Malicious input ?\n", l,SfileadrL-1); return(0); } if(flag&1) strcat(target, source); else strcpy(target, source); return(1); } /** Combine environment variable HOME with given filename @param filename Address relative to $HOME @param fileadr Resulting combined address @param fa_size Size of array fileadr @param flag Unused yet @return 1=ok , 0=no HOME variable , -1=result address too long */ int Sfile_home_adr_s(char *filename, char *fileadr, int fa_size, int flag) { char *home; strcpy(fileadr,filename); home= getenv("HOME"); if(home==NULL) return(0); if((int) (strlen(home) + strlen(filename) + 1) >= fa_size) return(-1); strcpy(fileadr,home); if(filename[0]!=0){ strcat(fileadr,"/"); strcat(fileadr,filename); } return(1); } /** Return a double representing seconds and microseconds since 1 Jan 1970 */ double Sfile_microtime(int flag) { struct timeval tv; gettimeofday(&tv, NULL); return((double) (tv.tv_sec+1.0e-6*tv.tv_usec)); } int Sfile_decode_datestr(struct tm *reply, char *text, int flag) /* YYMMDD[.hhmm[ss]] */ { int i,l; time_t current_time; struct tm *now; current_time= time(0); now= localtime(¤t_time); for(i=0; i < (int) sizeof(struct tm); i++) ((char *) reply)[i]= ((char *) now)[i]; if(text[0]<'0'|| (text[0]>'9' && text[0]<'A') || text[0]>'Z') return(0); l= strlen(text); for(i=1;i<l;i++) if(text[i]<'0'||text[i]>'9') break; if(i!=6) return(0); if(text[i]==0) goto decode; if(text[i]!='.' || (l!=11 && l!=13)) return(0); for(i++;i<l;i++) if(text[i]<'0'||text[i]>'9') break; if(i!=l) return(0); decode:; reply->tm_hour= 0; reply->tm_min= 0; reply->tm_sec= 0; i= 0; if(text[0]>='A') reply->tm_year= 100+(text[i]-'A')*10+text[1]-'0'; else reply->tm_year= 10*(text[0]-'0')+text[1]-'0'; reply->tm_mon= 10*(text[2]-'0')+text[3]-'0'-1; reply->tm_mday= 10*(text[4]-'0')+text[5]-'0'; if(l==6) return(1); reply->tm_hour= 10*(text[7]-'0')+text[8]-'0'; reply->tm_min= 10*(text[9]-'0')+text[10]-'0'; if(l==11) return(1); reply->tm_sec= 10*(text[11]-'0')+text[12]-'0'; return(1); } /* From libisofs: Find backward from idx the start byte of a possible UTF-8 character. https://en.wikipedia.org/wiki/UTF-8#Description */ static int find_utf8_start(char *name, int idx, int flag) { unsigned char *uname, uch; int i; uname= (unsigned char *) name; if ((uname[idx] & 0xc0) != 0x80) return idx; /* not an UTF-8 tail byte */ for (i = 0; i < 5 && idx - 1 - i >= 0; i++) { /* up to deprecated 6-byte codes */ uch = uname[idx - 1 - i]; if ((uch & 0xe0) == 0xc0 || (uch & 0xf0) == 0xe0 || (uch & 0xf8) == 0xf0 || (uch & 0xfc) == 0xf8 || (uch & 0xfe) == 0xfc) return (idx - 1 - i); /* UTF-8 start byte found */ if ((uch & 0xc0) != 0x80) return idx; /* not an UTF-8 tail byte, so no UTF-8 */ } return idx; /* no UTF-8 start found */ } int Sfile_flatten_utf8_heads(char *name, int idx, int flag) { int neck; neck = find_utf8_start(name, idx, 0); if(neck >= idx) return(2); for(; neck < idx; neck++) name[neck] = '_'; return(1); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2020 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of functions around files and strings. */ #ifndef Xorriso_pvt_sfile_includeD #define Xorriso_pvt_sfile_includeD yes #define TSOB_FELD(typ, count) (typ *) calloc(1, (count)*sizeof(typ)); #define Smem_malloC malloc #define Smem_freE free #define Xorriso_alloc_meM(pt, typ, count) { \ pt= (typ *) calloc(1, (count) * sizeof(typ)); \ if(pt == NULL) { \ Xorriso_no_malloc_memory(xorriso, NULL, 0); \ ret= -1; \ goto ex; \ } } #define Xorriso_free_meM(pt) { \ if(pt != NULL) \ free((char *) pt); \ pt= NULL; \ } #define SfileadrL 4096 int Sfile_str(char *target, char *source, int flag); double Sfile_microtime(int flag); int Sfile_add_to_path(char path[SfileadrL], char *addon, int flag); int Sfile_scale(double value, char *result, int siz, double thresh, int flag); int Sfile_destroy_argv(int *argc, char ***argv, int flag); int Sfile_count_char(char *text, char to_count); /* bit0= do not ignore trailing slash bit1= do not ignore empty components (other than the empty root name) */ int Sfile_count_components(char *path, int flag); /* @param flag bit0= return -1 if file is missing bit1= return a hardlink with siblings as type 5 bit2= evaluate eventual link target rather than the link object itself bit3= return a socket or a char device as types 7 or 8 rather than 0 @return 0=unknown 1=regular 2=directory 3=symbolic link 4=named pipe 5=multiple hardlink (with bit1) 6=block device 7=socket (with bit3) 8=character device (with bit3) | 1024 if interpreted as /dev/fd/#fd# | 2048 if interpreted as /dev/fd/#fd# and not fstatable */ int Sfile_type(char *filename, int flag); /* @param flag bit0= only encode inside quotes bit1= encode < 32 outside quotes except 7, 8, 9, 10, 12, 13 bit2= encode in any case above 126 bit3= encode in any case shellsafe: <=42 , 59, 60, 62, 63, 92, 94, 96, >=123 */ int Sfile_bsl_encoder(char **result, char *text, size_t text_len, int flag); int Sfile_argv_bsl(int argc, char ***argv, int flag); /* bit0= read progname as first argument from line bit1= just release argument list argv and return bit2= abort with return(0) if incomplete quotes are found bit3= eventually prepend missing '-' to first argument read from line bit4= like bit2 but only check quote completeness, do not allocate memory bit5+6= interpretation of backslashes: 0= no interpretation, leave unchanged 1= only inside double quotes 2= outside single quotes 3= everywhere bit7= append a NULL element to argv */ int Sfile_make_argv(char *progname, char *line, int *argc, char ***argv, int flag); int Sfile_sep_make_argv(char *progname, char *line, char *separators, int max_argc, int *argc, char ***argv, int flag); /* YYMMDD[.hhmm[ss]] */ int Sfile_decode_datestr(struct tm *reply, char *text, int flag); int Sfile_off_t_text(char text[80], off_t num, int flag); int Sfile_text_to_off_t(char *text, off_t *num, int flag); int Sfile_leafname(char *path, char leafname[SfileadrL], int flag); /* @param flag bit0= do not clip of carriage return at line end */ char *Sfile_fgets_n(char *line, int maxl, FILE *fp, int flag); /* bit0=with hours+minutes bit1=with seconds bit8= local time rather than UTC */ char *Sfile_datestr(time_t tim, short int flag); /* Converts backslash codes into single characters: \a BEL 7 , \b BS 8 , \e ESC 27 , \f FF 12 , \n LF 10 , \r CR 13 , \t HT 9 , \v VT 11 , \\ \ 92 \[0-9][0-9][0-9] octal code , \x[0-9a-f][0-9a-f] hex code , \cX control-x (ascii(X)-64) @param upto maximum number of characters to examine for backslash. The scope of a backslash (0 to 3 characters) is not affected. @param eaten returns the difference in length between input and output @param flag bit0= only determine *eaten, do not convert bit1= convert \000 to binary 0 */ int Sfile_bsl_interpreter(char *text, int upto, int *eaten, int flag); int Sfile_prepend_path(char *prefix, char path[SfileadrL], int flag); int Sfile_home_adr_s(char *filename, char *fileadr, int fa_size, int flag); int Sfile_get_dev_fd_no(char *filename, int flag); int Sfile_flatten_utf8_heads(char *name, int idx, int flag); #endif /* ! Xorriso_pvt_sfile_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains functions which sort and compare tree nodes. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include "base_obj.h" #include "lib_mgt.h" #include "sort_cmp.h" #include "iso_tree.h" #include "iso_manip.h" int Xorriso__findi_sorted_ino_cmp(const void *p1, const void *p2) { int ret; IsoNode *n1, *n2; n1= *((IsoNode **) p1); n2= *((IsoNode **) p2); ret= Xorriso__node_lba_cmp(&n1, &n2); if(ret) return (ret > 0 ? 1 : -1); ret= iso_node_cmp_ino(n1, n2, 0); return(ret); } /* Not suitable for qsort() but for cross-array comparisons. p1 and p2 are actually IsoNode *p1, IsoNode *p2 */ int Xorriso__hln_cmp(const void *p1, const void *p2) { int ret; ret= Xorriso__findi_sorted_ino_cmp(&p1, &p2); if(ret) return (ret > 0 ? 1 : -1); if(p1 != p2) return(p1 < p2 ? -1 : 1); return(0); } /* p1 and p2 are actually IsoNode **p1, IsoNode **p2 */ int Xorriso__findi_sorted_cmp(const void *p1, const void *p2) { int ret; ret= Xorriso__findi_sorted_ino_cmp(p1, p2); if(ret) return (ret > 0 ? 1 : -1); if(p1 != p2) return(p1 < p2 ? -1 : 1); return(0); } int Xorriso_sort_node_array(struct XorrisO *xorriso, int flag) { if(xorriso->node_counter <= 0) return(0); qsort(xorriso->node_array, xorriso->node_counter, sizeof(IsoNode *), Xorriso__findi_sorted_cmp); return(1); } int Xorriso__search_node(void *node_array[], int n, int (*cmp)(const void *p1, const void *p2), void *node, int *idx, int flag) { int ret, l, r, p, pos; if(n == 0) return(0); l= 0; r= n + 1; while(1) { p= (r - l) / 2; if(p == 0) break; p+= l; /* NULL elements may indicate invalid nodes.Their first valid right neighbor will serve as proxy. If none exists, then the test pushes leftwards. */ for(pos= p - 1; pos < n; pos++) if(node_array[pos] != NULL) break; if(pos < n) ret= (*cmp)(&(node_array[pos]), &node); else ret= 1; if(ret < 0) l= p; else if(ret > 0) r= p; else { *idx= pos; return(1); } } return(0); } int Xorriso_search_in_hln_array(struct XorrisO *xorriso, void *node, int *idx, int flag) { int ret; if(xorriso->hln_array == NULL || xorriso->hln_count <= 0) return(0); ret= Xorriso__search_node(xorriso->hln_array, xorriso->hln_count, Xorriso__findi_sorted_ino_cmp, node, idx, 0); return ret; } int Xorriso__get_di(IsoNode *node, dev_t *dev, ino_t *ino, int flag) { int ret, i, i_end, imgid, error_code; size_t value_length= 0; char *value= NULL, *msg= NULL, severity[80]; unsigned char *vpt; static char *name= "isofs.di"; #ifdef NIX /* <<< */ Xorriso_get_di_counteR++; #endif /* NIX */ msg= TSOB_FELD(char, ISO_MSGS_MESSAGE_LEN); if(msg == NULL) {ret= -1; goto ex;} *dev= 0; *ino= 0; ret= iso_node_lookup_attr(node, name, &value_length, &value, 0); if(ret <= 0) { /* Drop any pending messages because there is no xorriso to take them */ iso_obtain_msgs("NEVER", &error_code, &imgid, msg, severity); goto ex; } vpt= (unsigned char *) value; if(vpt[0] > sizeof(ino_t)) { /* >>> obviously not the same system that recorded the device number */; } for(i= 1; i <= vpt[0] && i < (int) value_length; i++) *dev= ((*dev) << 8) | vpt[i]; i_end= i + vpt[i] + 1; if(vpt[i] > sizeof(ino_t)) { /* >>> obviously not the same system that recorded the inode number */; } for(i++; i < i_end && i < (int) value_length; i++) *ino= ((*ino) << 8) | vpt[i]; free(value); ret= 1; ex:; if(msg != NULL) free(msg); return(ret); } int Xorriso__di_ino_cmp(const void *p1, const void *p2) { int ret; IsoNode *n1, *n2; dev_t d1, d2; ino_t i1, i2; n1= *((IsoNode **) p1); n2= *((IsoNode **) p2); ret= Xorriso__get_di(n1, &d1, &i1, 0); if(ret <= 0) {d1= 0; i1= 0;} ret= Xorriso__get_di(n2, &d2, &i2, 0); if(ret <= 0) {d2= 0; i2= 0;} if(d1 < d2) return(-1); if(d1 > d2) return(1); if(i1 < i2) return(-1); if(i1 > i2) return(1); if(d1 == 0 && i1 == 0 && n1 != n2) return(n1 < n2 ? -1 : 1); return(0); } int Xorriso__di_cmp(const void *p1, const void *p2) { int ret; IsoNode *n1, *n2; ret= Xorriso__di_ino_cmp(p1, p2); if(ret) return(ret); n1= *((IsoNode **) p1); n2= *((IsoNode **) p2); if(n1 != n2) return(n1 < n2 ? -1 : 1); return(0); } int Xorriso__sort_di(void *node_array[], int count, int flag) { if(count <= 0) return(0); qsort(node_array, count, sizeof(IsoNode *), Xorriso__di_cmp); return(1); } int Xorriso_invalidate_di_item(struct XorrisO *xorriso, IsoNode *node, int flag) { int ret, idx; if(xorriso->di_array == NULL) return(1); ret= Xorriso__search_node(xorriso->di_array, xorriso->di_count, Xorriso__di_cmp, node, &idx, 0); if(ret <= 0) return(ret == 0); if(xorriso->di_array[idx] != NULL) iso_node_unref(xorriso->di_array[idx]); xorriso->di_array[idx]= NULL; return(1); } /* @param flag bit0= return 1 even if matching nodes were found but node is not among them bit1= use Xorriso__di_cmp() rather than Xorriso__di_ino_cmp() */ int Xorriso_search_di_range(struct XorrisO *xorriso, IsoNode *node, int *idx, int *low, int *high, int flag) { int ret, i, found; int (*cmp)(const void *p1, const void *p2)= Xorriso__di_ino_cmp; if(flag & 2) cmp= Xorriso__di_cmp; *high= *low= *idx= -1; ret= Xorriso__search_node(xorriso->di_array, xorriso->di_count, cmp, node, &found, 0); if(ret <= 0) return(0); *low= *high= found; for(i= found + 1; i < xorriso->di_count; i++) if(xorriso->di_array[i] != NULL) { if((*cmp)(&node, &(xorriso->di_array[i])) != 0) break; *high= i; } for(i= found - 1; i >= 0; i--) if(xorriso->di_array[i] != NULL) { if((*cmp)(&node, &(xorriso->di_array[i])) != 0) break; *low= i; } for(i= *low; i <= *high; i++) if(xorriso->di_array[i] == node) { *idx= i; break; } return(*idx >= 0 || (flag & 1)); } int Xorriso__node_lba_cmp(const void *node1, const void *node2) { int ret; off_t lba1= 0, lba2= 0; ret= Xorriso__file_start_lba(*((IsoNode **) node1), &lba1, 0); if(ret!=1) lba1= 0; ret= Xorriso__file_start_lba(*((IsoNode **) node2), &lba2, 0); if(ret!=1) lba2= 0; if(lba1 > lba2) return(1); if(lba1 < lba2) return(-1); return(0); } int Xorriso__node_name_cmp(const void *node1, const void *node2) { char *name1, *name2; name1= (char *) iso_node_get_name(*((IsoNode **) node1)); name2= (char *) iso_node_get_name(*((IsoNode **) node2)); return(strcmp(name1,name2)); } /* @param flag bit0= only accept directory nodes bit1= do not report memory usage as DEBUG bit2= do not apply search pattern but accept any node */ int Xorriso_sorted_node_array(struct XorrisO *xorriso, IsoDir *dir_node, int *nodec, IsoNode ***node_array, off_t boss_mem, int flag) { int i, ret, failed_at; char *npt; IsoDirIter *iter= NULL; IsoNode *node; off_t mem; mem= ((*nodec)+1)*sizeof(IsoNode *); ret= Xorriso_check_temp_mem_limit(xorriso, mem+boss_mem, flag&2); if(ret<=0) return(ret); *node_array= calloc(sizeof(IsoNode *), (*nodec)+1); if(*node_array==NULL) { sprintf(xorriso->info_text, "Cannot allocate memory for %d directory entries", *nodec); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); return(-1); } ret= iso_dir_get_children(dir_node, &iter); if(ret<0) { Xorriso_cannot_create_iter(xorriso, ret, 0); return(-1); } for(i= 0; iso_dir_iter_next(iter, &node) == 1 && i<*nodec; ) { npt= (char *) iso_node_get_name(node); if(!(flag&4)) { ret= Xorriso_regexec(xorriso, npt, &failed_at, 0); if(ret) continue; /* no match */ } if(flag&1) if(!LIBISO_ISDIR(node)) continue; (*node_array)[i++]= node; } iso_dir_iter_free(iter); *nodec= i; if(*nodec<=0) return(1); qsort(*node_array, *nodec, sizeof(IsoNode *), Xorriso__node_name_cmp); return(1); } int Xorriso_remake_hln_array(struct XorrisO *xorriso, int flag) { int ret, addon_nodes= 0, i, old_count, old_pt, new_pt; IsoNode **old_nodes; char **old_targets; /* Count hln_targets of which the node has been deleted meanwhile */ for(i= 0; i < xorriso->hln_count; i++) { if(xorriso->hln_targets[i] == NULL) continue; if(Xorriso_node_is_valid(xorriso, xorriso->hln_array[i], 0)) continue; addon_nodes++; } ret= Xorriso_all_node_array(xorriso, addon_nodes, 0); if(ret <= 0) goto ex; if(addon_nodes > 0) { /* Transfer delete nodes with hln_target to node array */ for(i= 0; i < xorriso->hln_count; i++) { if(xorriso->hln_targets[i] == NULL) continue; if(Xorriso_node_is_valid(xorriso, xorriso->hln_array[i], 0)) continue; if(xorriso->node_counter < xorriso->node_array_size) { xorriso->node_array[xorriso->node_counter++]= xorriso->hln_array[i]; iso_node_ref(xorriso->node_array[xorriso->node_counter - 1]); } } } Xorriso_sort_node_array(xorriso, 0); old_nodes= (IsoNode **) xorriso->hln_array; old_targets= (char **) xorriso->hln_targets; old_count= xorriso->hln_count; xorriso->hln_array= 0; xorriso->hln_targets= NULL; /* Transfer node_array to di_array without unrefering nodes */ xorriso->hln_count= xorriso->node_counter; xorriso->hln_array= xorriso->node_array; xorriso->node_counter= 0; xorriso->node_array_size= 0; xorriso->node_array= NULL; /* Allocate hln_targets */ ret= Xorriso_new_hln_array(xorriso, xorriso->temp_mem_limit, 1); if(ret<=0) goto ex; xorriso->node_targets_availmem= xorriso->temp_mem_limit; if(old_targets != NULL) { /* Transfer targets from old target array */; new_pt= old_pt= 0; while(new_pt < xorriso->hln_count && old_pt < old_count) { ret= Xorriso__hln_cmp(xorriso->hln_array[new_pt], old_nodes[old_pt]); if(ret < 0) { new_pt++; } else if(ret > 0) { old_pt++; } else { xorriso->hln_targets[new_pt]= old_targets[old_pt]; if(old_targets[old_pt] != NULL) xorriso->temp_mem_limit-= strlen(old_targets[old_pt]) + 1; old_targets[old_pt]= NULL; new_pt++; old_pt++; } } for(old_pt= 0; old_pt < old_count; old_pt++) if(old_targets[old_pt] != NULL) /* (should not happen) */ free(old_targets[old_pt]); free((char *) old_targets); } if(old_nodes != NULL) { for(old_pt= 0; old_pt < old_count; old_pt++) if(old_nodes[old_pt] != NULL) iso_node_unref(old_nodes[old_pt]); free((char *) old_nodes); } xorriso->hln_change_pending= 0; ret= 1; ex:; return(ret); } /* @param flag bit0= overwrite existing hln_array (else return 2) */ int Xorriso_make_hln_array(struct XorrisO *xorriso, int flag) { int ret; if(xorriso->hln_array != NULL && !(flag & 1)) { /* If no fresh image manipulations occurred: keep old array */ if(!xorriso->hln_change_pending) return(2); ret= Xorriso_remake_hln_array(xorriso, 0); return(ret); } Xorriso_destroy_hln_array(xorriso, 0); ret= Xorriso_all_node_array(xorriso, 0, 0); if(ret <= 0) goto ex; Xorriso_sort_node_array(xorriso, 0); /* Transfer node_array to di_array without unrefering nodes */ xorriso->hln_count= xorriso->node_counter; xorriso->hln_array= xorriso->node_array; xorriso->node_counter= 0; xorriso->node_array_size= 0; xorriso->node_array= NULL; /* Allocate hln_targets */ ret= Xorriso_new_hln_array(xorriso, xorriso->temp_mem_limit, 1); if(ret<=0) { Xorriso_destroy_hln_array(xorriso, 0); goto ex; } xorriso->node_targets_availmem= xorriso->temp_mem_limit; xorriso->hln_change_pending= 0; ret= 1; ex:; return(ret); } /* @param flag bit0= overwrite existing di_array (else return 2) bit1= make di_array despite xorriso->ino_behavior bit 3 */ int Xorriso_make_di_array(struct XorrisO *xorriso, int flag) { int ret, bytes; #ifdef NIX /* <<< */ unsigned long old_gdic; old_gdic= Xorriso_get_di_counteR; #endif /* NIX */ if((xorriso->ino_behavior & 8 ) && !(flag & 2)) return(2); if(xorriso->di_array != NULL && !(flag & 1)) return(2); Xorriso_finish_hl_update(xorriso, 0); ret= Xorriso_all_node_array(xorriso, 0, 0); if(ret <= 0) goto ex; bytes= xorriso->node_array_size / 8 + 1; xorriso->di_do_widen= calloc(bytes, 1); if(xorriso->di_do_widen == NULL) { Xorriso_no_malloc_memory(xorriso, NULL, 0); ret= -1; goto ex; } /* Transfer node_array to di_array without unrefering nodes */ xorriso->di_count= xorriso->node_counter; xorriso->di_array= xorriso->node_array; xorriso->node_counter= 0; xorriso->node_array_size= 0; xorriso->node_array= NULL; Xorriso__sort_di((void *) xorriso->di_array, xorriso->di_count, 0); ret= 1; ex:; #ifdef NIX /* <<< */ fprintf(stderr, "xorriso_DEBUG: sort_count= %lu\n", Xorriso_get_di_counteR - old_gdic); #endif /* NIX */ return(ret); } /* @param flag bit0= iso_rr_path is freshly added and up to date bit1= do not mark as changed content (implied by bit0 too) bit2= -follow: this is not a command parameter @return -1= severe error 0= not applicable for hard links 1= go on with processing 2= iso_rr_path is fully updated */ int Xorriso_hardlink_update(struct XorrisO *xorriso, int *compare_result, char *disk_path, char *iso_rr_path, int flag) { int ret, hret, idx, low, high, i, do_overwrite= 0, did_fake_di= 0; int follow_links, old_idx= -1; IsoNode *node; struct stat stbuf; dev_t old_dev; ino_t old_ino; if(xorriso->di_array == NULL) return(1); follow_links= xorriso->do_follow_links || (xorriso->do_follow_param && !(flag & 4)); ret= Xorriso_node_from_path(xorriso, NULL, iso_rr_path, &node, 0); if(ret <= 0) return(ret); if(LIBISO_ISDIR(node)) return(1); /* Handle eventual hardlink split : */ /* This is achieved by setting the content change bit. Reason: The node needs to be removed from di_array because its di is not matching its array index any more. So it becomes invisible for the join check of eventual later hardlink siblings. Therefore it must be updated now, even if it has currently no siblings which it leaves or which it joins. */ if(!(flag & (1 | 2))) do_overwrite= 1; Xorriso__get_di(node, &old_dev, &old_ino, 0); ret= Xorriso__search_node(xorriso->di_array, xorriso->di_count, Xorriso__di_cmp, node, &idx, 0); if(ret < 0) {ret= 0; goto ex;} if(ret > 0) old_idx= idx; /* Handle eventual hardlink joining : */ if(follow_links) ret= stat(disk_path, &stbuf); else ret= lstat(disk_path, &stbuf); if(ret==-1) {ret= 0; goto ex;} /* Are there new dev-ino-siblings in the image ? */ /* Fake isofs.di */ if(!(flag & 1)) { ret= Xorriso_record_dev_inode(xorriso, disk_path, stbuf.st_dev, stbuf.st_ino, node, iso_rr_path, 1); if(ret <= 0) {ret= -1; goto ex;} did_fake_di= 1; /* temporarily remove node from di_array so it does not disturb search by its fake di info */; if(old_idx >= 0) xorriso->di_array[old_idx]= NULL; } ret= Xorriso_search_di_range(xorriso, node, &idx, &low, &high, 1); if(did_fake_di) { /* Revoke fake of isofs.di */ hret= Xorriso_record_dev_inode(xorriso, disk_path, old_dev, old_ino, node, iso_rr_path, 1); if(hret <= 0) {ret= -1; goto ex;} if(old_idx >= 0) xorriso->di_array[old_idx]= node; } if(ret == 0) {ret= 1; goto ex;} if(ret < 0) {ret= 0; goto ex;} #ifdef Xorriso_hardlink_update_debuG /* <<< */ if(low < high || idx < 0) { fprintf(stderr, "xorriso_DEBUG: old_idx= %d , low= %d , high= %d , iso= '%s' , disk='%s'\n", old_idx, low, high, iso_rr_path, disk_path); fprintf(stderr, "xorriso_DEBUG: old_dev= %lu , old_ino= %.f , dev= %lu , ino= %.f\n", (unsigned long) old_dev, (double) old_ino, (unsigned long) stbuf.st_dev, (double) stbuf.st_ino); if(idx >= 0 && idx != old_idx) fprintf(stderr, "xorriso_DEBUG: idx= %d , old_idx = %d\n", idx, old_idx); } #endif /* Xorriso_hardlink_update_debuG */ /* Overwrite all valid siblings : */ for(i= low; i <= high; i++) { if(i == idx || xorriso->di_array[i] == NULL) continue; #ifdef Xorriso_hardlink_update_debuG /* <<< */ { ino_t ino; dev_t dev; Xorriso__get_di(xorriso->di_array[i], &dev, &ino, 0); fprintf(stderr, "xorriso_DEBUG: iso_sibling= '%s' , dev= %lu , ino= %.f\n", node_path, (unsigned long) dev, (double) ino); } #endif /* Xorriso_hardlink_update_debuG */ xorriso->di_do_widen[i / 8]|= 1 << (i % 8); } ret= 1; ex:; if(do_overwrite) *compare_result|= (1<<15); if(old_idx >= 0 && (*compare_result & (3 << 21))) { /* The old di info is obsolete */ if(xorriso->di_array[old_idx] != NULL) iso_node_unref(xorriso->di_array[old_idx]); xorriso->di_array[old_idx]= NULL; } return(ret); } /* @param flag bit0= do not destroy di_array */ int Xorriso_finish_hl_update(struct XorrisO *xorriso, int flag) { int ret, zero= 0; char *argv[4]; struct Xorriso_lsT *disk_lst, *iso_lst; if(xorriso->di_array == NULL) {ret= 1; goto ex;} disk_lst= xorriso->di_disk_paths; iso_lst= xorriso->di_iso_paths; while(disk_lst != NULL && iso_lst != NULL) { argv[0]= Xorriso_lst_get_text(iso_lst, 0); argv[1]= "-exec"; argv[2]= "widen_hardlinks"; argv[3]= Xorriso_lst_get_text(disk_lst, 0); zero= 0; ret= Xorriso_option_find(xorriso, 4, argv, &zero, 0); /* -findi */ if(ret < 0) goto ex; disk_lst= Xorriso_lst_get_next(disk_lst, 0); iso_lst= Xorriso_lst_get_next(iso_lst, 0); } ret= 1; ex:; if(!(flag & 1)) Xorriso_destroy_di_array(xorriso, 0); return(ret); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2010 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of functions which sort and compare tree nodes. */ #ifndef Xorriso_pvt_sort_cmp_includeD #define Xorriso_pvt_sort_cmp_includeD yes int Xorriso__findi_sorted_ino_cmp(const void *p1, const void *p2); int Xorriso__hln_cmp(const void *p1, const void *p2); int Xorriso__findi_sorted_cmp(const void *p1, const void *p2); int Xorriso__search_node(void *node_array[], int n, int (*cmp)(const void *p1, const void *p2), void *node, int *idx, int flag); int Xorriso_search_in_hln_array(struct XorrisO *xorriso, void *node, int *idx, int flag); int Xorriso__get_di(IsoNode *node, dev_t *dev, ino_t *ino, int flag); int Xorriso__di_ino_cmp(const void *p1, const void *p2); int Xorriso__di_cmp(const void *p1, const void *p2); int Xorriso__sort_di(void *node_array[], int count, int flag); int Xorriso_invalidate_di_item(struct XorrisO *xorriso, IsoNode *node, int flag); int Xorriso_search_di_range(struct XorrisO *xorriso, IsoNode *node, int *idx, int *low, int *high, int flag); int Xorriso__node_lba_cmp(const void *node1, const void *node2); int Xorriso__node_name_cmp(const void *node1, const void *node2); int Xorriso_sorted_node_array(struct XorrisO *xorriso, IsoDir *dir_node, int *nodec, IsoNode ***node_array, off_t boss_mem, int flag); int Xorriso_remake_hln_array(struct XorrisO *xorriso, int flag); int Xorriso_make_di_array(struct XorrisO *xorriso, int flag); int Xorriso_search_hardlinks(struct XorrisO *xorriso, IsoNode *node, int *node_idx, int *min_hl, int *max_hl, int flag); #endif /* ! Xorriso_pvt_sort_cmp_includeD */ /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the implementation of text i/o functions. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include <signal.h> #include <pthread.h> #include <fcntl.h> /* O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif /* for -charset */ #include <iconv.h> #include <langinfo.h> #include <locale.h> #ifdef Xorriso_with_readlinE #ifdef Xorriso_with_old_readlinE #include <readline.h> #include <history.h> #else /* Xorriso_with_old_readlinE */ #include <readline/readline.h> #include <readline/history.h> #endif /* ! Xorriso_with_old_readlinE */ #define Xorriso_with_line_editoR yes #endif /* Xorriso_with_readlinE */ #ifdef Xorriso_with_editlinE #include <histedit.h> #define Xorriso_with_line_editoR yes #endif /* Xorriso_with_editlinE */ #include "xorriso.h" #include "xorriso_private.h" #include "xorrisoburn.h" int Xorriso_protect_stdout(struct XorrisO *xorriso, int flag) { if(xorriso->dev_fd_1>=0) return(2); xorriso->dev_fd_1= dup(1); close(1); dup2(2,1); return(1); } #ifdef Xorriso_with_editlinE /* These have to be global and shared by all XorrisOs which might be active. */ static EditLine *editline_handle= NULL; static History *editline_history= NULL; static int editline_is_initialized= 0; char *Xorriso__editline_prompt(EditLine *el_handle) { return ""; } void Xorriso_init_editline(struct XorrisO *xorriso, int flag) { HistEvent ev; /* >>> Need mutex */ if(editline_is_initialized != 0) return; editline_is_initialized= -1; /* Invalid */ editline_handle= el_init(xorriso->progname, stdin, stdout, stderr); if(editline_handle == NULL) return; el_set(editline_handle, EL_EDITOR, "emacs"); el_set(editline_handle, EL_PROMPT, &Xorriso__editline_prompt); editline_history= history_init(); if(editline_history == NULL) return; history(editline_history, &ev, H_SETSIZE, 1000); el_set(editline_handle, EL_HIST, history, editline_history); editline_is_initialized= 1; /* Valid now */ return; } int Xorriso__shutdown_editline(int flag) { if(editline_history != NULL) history_end(editline_history); editline_history= NULL; if(editline_handle != NULL) el_end(editline_handle); editline_handle= NULL; editline_is_initialized= 0; return(1); } #endif /* Xorriso_with_editlinE */ #ifdef Xorriso_with_readlinE /* http://lists.gnu.org/archive/html/bug-readline/2014-06/msg00005.html */ const char *Xorriso__readline_license(int flag) { #ifdef RL_VERSION_MAJOR #if RL_VERSION_MAJOR > 5 return("GPLv3+"); #endif #endif return("GPLv2+"); } #endif /* Xorriso_with_readlinE */ #ifdef Xorriso_with_line_editoR char *Xorriso_emul_readline(struct XorrisO *xorriso, int flag) { #ifdef Xorriso_with_editlinE const char *cpt; int count= 0; char *retpt; /* >>> Need mutex */ Xorriso_init_editline(xorriso, 0); if(editline_is_initialized < 0) { /* >>> fallback */; } cpt= el_gets(editline_handle, &count); if(count == -1 || cpt == NULL) return(NULL); retpt= calloc(1, count + 1); if(retpt == NULL) return(NULL); memcpy(retpt, cpt, count); retpt[count]= 0; return(retpt); #else #ifdef Xorriso_with_readlinE char *cpt; cpt= readline(""); return(cpt); #else return(NULL); #endif /* ! Xorriso_with_readlinE */ #endif /* ! Xorriso_with_editlinE */ } void Xorriso_emul_add_history(struct XorrisO *xorriso, char *line, int flag) { #ifdef Xorriso_with_editlinE HistEvent ev; /* >>> Need mutex */ Xorriso_init_editline(xorriso, 0); if(editline_is_initialized < 0) return; history(editline_history, &ev, H_ENTER, line); #else #ifdef Xorriso_with_readlinE add_history(line); #else /* ??? How to raise protest ? */; #endif /* ! Xorriso_with_readlinE */ #endif /* ! Xorriso_with_editlinE */ } /* @param flag bit1= do only report to fp */ int Xorriso_status_history(struct XorrisO *xorriso, char *filter, FILE *fp, int flag) { #ifdef Xorriso_with_editlinE int ret, l; HistEvent ev; int hc, i, was_end= 0; char *str= NULL; /* >>> Need mutex */ Xorriso_init_editline(xorriso, 0); if(editline_is_initialized < 0) {ret= 0; goto ex;} Xorriso_alloc_meM(str, char, SfileadrL); ret= history(editline_history, &ev, H_LAST); for(hc= 0; ret != -1; hc++) { ret= history(editline_history, &ev, H_PREV); was_end = (strcmp(ev.str, "-end") == 0); } if(was_end) hc--; if(hc >= xorriso->status_history_max) i= hc - xorriso->status_history_max; else i= 0; ret= history(editline_history, &ev, H_LAST); for(; i < hc && ret != -1; i++) { /* Eat newline at line end */ strncpy(str, ev.str, SfileadrL - 1); str[SfileadrL - 1]= 0; l= strlen(str); if(l > 0) if(str[l - 1] == '\n') str[l - 1]= 0; sprintf(xorriso->result_line, "-history "); Text_shellsafe(str, xorriso->result_line, 1); strcat(xorriso->result_line, "\n"); Xorriso_status_result(xorriso, filter, fp, flag & 2); ret= history(editline_history, &ev, H_PREV); } ret= 1; ex:; Xorriso_free_meM(str); return(ret); #else #ifdef Xorriso_with_readlinE HIST_ENTRY **hl; int hc, i; hl= history_list(); if(hl != NULL) { for(hc= 0; hl[hc] != NULL; hc++); if(hc > 0) if(strcmp(hl[hc-1]->line, "-end") == 0) hc--; if(hc >= xorriso->status_history_max) i= hc - xorriso->status_history_max; else i= 0; for(; i < hc; i++) { sprintf(xorriso->result_line, "-history "); Text_shellsafe(hl[i]->line, xorriso->result_line, 1); strcat(xorriso->result_line, "\n"); Xorriso_status_result(xorriso, filter, fp, flag & 2); } } return(1); #else /* Xorriso_with_readlinE */ return(0); #endif /* ! Xorriso_with_readlinE */ #endif /* ! Xorriso_with_editlinE */ } #endif /* Xorriso_with_line_editoR */ int Xorriso_dialog_input(struct XorrisO *xorriso, char line[], int linesize, int flag) /* bit0= do not write to history bit1= do not read input (but eventually write to history) bit2= do not write to history line which begin with "-history:" or "-history " bit3= enforce single line dialog mode bit4= do not read from xorriso->buffered_dialog bit5= write to history in any case (if it is enabled at compile time) */ { char **argv= NULL, *linept, *why_append= ""; int ret, argc= 0, base_length= 0, l, append_line; #ifdef Xorriso_with_line_editoR static char last_input[SfileadrL]= {""}; int no_history= 0; char *cpt= NULL; #endif /* Xorriso_with_line_editoR */ double tdiff; struct timeval tv; struct Xorriso_lsT *next_lst; gettimeofday(&tv, NULL); tdiff= tv.tv_sec+(1.e-6*(double) tv.tv_usec); fflush(stdout); linept= line; #ifdef Xorriso_with_line_editoR no_history= (flag & 1) || xorriso->use_stdin; #endif get_single:; if(xorriso->buffered_dialog != NULL && !(flag & (16 | 2))) { /* Consume next buffered line */ next_lst= Xorriso_lst_get_next(xorriso->buffered_dialog, 0); strcpy(line, Xorriso_lst_get_text(xorriso->buffered_dialog, 0)); Xorriso_lst_destroy(&(xorriso->buffered_dialog), 0); xorriso->buffered_dialog= next_lst; goto process_single; } #ifdef Xorriso_with_line_editoR if(xorriso->use_stdin || xorriso->dev_fd_1>=0 || xorriso->tolerate_stdin_eof) { if(flag&2) { if(flag & 32) goto put_into_history; {ret= 1; goto ex;} } if(Sfile_fgets_n(linept,linesize - base_length - 1, stdin, (xorriso->dialog == 2)) == NULL) { if(xorriso->tolerate_stdin_eof) {ret= -2; goto ex;} /* need a very dramatic end */ kill(getpid(),SIGHUP); {ret= -1; goto ex;} } goto process_single; } if(flag&2) { cpt= NULL; } else { cpt= Xorriso_emul_readline(xorriso, 0); if(cpt==NULL) { /* need a very dramatic end */ kill(getpid(),SIGHUP); {ret= -1; goto ex;} } l= strlen(cpt); if(l >= linesize - base_length - 1) { strncpy(linept, cpt, linesize - 1); line[linesize - 1]= 0; sprintf(xorriso->info_text,"Input line too long !"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); goto new_empty; } else strcpy(linept, cpt); } #else /* Xorriso_with_line_editoR */ if(flag&2) {ret= 1; goto ex;} if(Sfile_fgets_n(linept, linesize - base_length - 1, stdin, (xorriso->dialog == 2)) == NULL) { if(xorriso->tolerate_stdin_eof) {ret= -2; goto ex;} /* need a very dramatic end */ kill(getpid(),SIGHUP); {ret= -1; goto ex;} } #endif /* ! Xorriso_with_line_editoR */ process_single:; if(xorriso->dialog == 2 && !(flag & 8)) { append_line= 0; if(linept != line && strcmp(linept, "@@@") == 0) { sprintf(xorriso->info_text, "Incomplete input line cleared by %s", linept); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE",0); new_empty:; line[0]= 0; linept= line; sprintf(xorriso->info_text, "-------------------------------------\n"); Xorriso_info(xorriso,0); sprintf(xorriso->info_text, "Enter new text for empty input line :\n"); Xorriso_info(xorriso,0); goto get_single; } l= strlen(line); ret= Sfile_make_argv("", line, &argc, &argv, 16); if(ret < 0) goto ex; if(ret == 0 && !append_line) { /* append a newline character */ if(l >= linesize - 1) { sprintf(xorriso->info_text,"Input line too long !"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0); goto new_empty; } line[l]= '\n'; line[l + 1]= 0; append_line= 1; why_append= "Quoted newline char"; } if(l > 0 && !append_line) if(line[l - 1] == '\\') { line[l - 1]= 0; append_line= 1; why_append= "Trailing backslash "; } if(append_line) { base_length= strlen(line); linept= line + base_length; sprintf(xorriso->info_text, "---------------------------------------------------------------\n"); Xorriso_info(xorriso,0); sprintf(xorriso->info_text, "%s : Enter rest of line (or @@@ to clear it) :\n", why_append); Xorriso_info(xorriso,0); goto get_single; } } #ifdef Xorriso_with_line_editoR put_into_history:; if((flag & 32) || (line[0]!=0 && strcmp(last_input,line)!=0 && !no_history)) { if(!((flag&4) && (strncmp(line,"-history:",9)==0 || strncmp(line,"-history ",9)==0))) { Xorriso_emul_add_history(xorriso, line, 0); strncpy(last_input,line,sizeof(last_input)-1); last_input[sizeof(last_input)-1]= 0; } } #endif /* Xorriso_with_line_editoR */ ret= 1; ex:; #ifdef Xorriso_with_line_editoR if(cpt!=NULL) free(cpt); #endif gettimeofday(&tv, NULL); xorriso->idle_time+= tv.tv_sec+(1.e-6*(double) tv.tv_usec)-tdiff; return(ret); } int Xorriso_request_confirmation(struct XorrisO *xorriso, int flag) /* bit0= important operation going on: demand confirmation of abort, only abort on @@@ bit1= mark '@' and '@@' by return 4 bit2= accept: i|n= ignore | do not remove , r|y= retry | remove , q|x= abort bit3= @@@ = 'done reading' rather than 'abort' bit4= in non-dialog mode return 6 rather than 1 */ /* return: <=0 error 1= go on | do not remove existing file 2= abort 3= redo request for confirmation 4= see flag bit1 (5= skip volume) 6= retry failed operation | remove existing file */ { int ret; char *line= NULL, *cpt, *previous_line= NULL; char *abort_req_text,*abort_really_text; Xorriso_alloc_meM(line, char, SfileadrL); Xorriso_alloc_meM(previous_line, char, SfileadrL); if(!xorriso->dialog) { if(flag&16) {ret= 6; goto ex;} {ret= 1; goto ex;} } if(flag&8) { abort_req_text= "request to end"; abort_really_text= "done reading"; } else { abort_req_text= "request to abort"; abort_really_text= "abort this command"; } ret= Xorriso_dialog_input(xorriso,line, SfileadrL, 1); xorriso->result_line_counter= 0; xorriso->result_page_counter++; if(ret<=0) if(xorriso->result_page_length>0) xorriso->result_page_length= -xorriso->result_page_length; cpt= line; if(strcmp(cpt,"@@@")==0 || strcmp(cpt,"x")==0 || strcmp(cpt,"X")==0 || strcmp(cpt,"q")==0 || strcmp(cpt,"Q")==0) { if(flag&1) { strcpy(previous_line,cpt); sprintf(xorriso->info_text, "... [%s = %s registered. Really %s ? (y/n) ] ...\n", cpt,abort_req_text,abort_really_text); Xorriso_info(xorriso,0); ret= Xorriso_dialog_input(xorriso,line, SfileadrL, 1); if(ret<=0) goto ex; cpt= line; if(strcmp(cpt,previous_line)==0 || ((*cpt=='Y' || *cpt=='y' || *cpt=='j' || *cpt=='J' || *cpt=='1') && *(cpt+1)==0)) { xorriso->request_to_abort= 1; sprintf(xorriso->info_text, "------- ( %s confirmed )\n",abort_req_text); Xorriso_info(xorriso,0); {ret= 2; goto ex;} } sprintf(xorriso->info_text, "....... ( %s revoked )\n",abort_req_text); Xorriso_info(xorriso,0); {ret= 3; goto ex;} } xorriso->request_to_abort= 1; sprintf(xorriso->info_text, "----------- [%s = request to abort registered. Operation ends ] ------------\n", cpt); Xorriso_info(xorriso,0); {ret= 2; goto ex;} } else if(*cpt=='@') { if(strcmp(cpt,"@@")==0) { goto klammer_affe; } else if(strcmp(cpt,"@")==0) { klammer_affe:; if(xorriso->result_page_length>0) xorriso->result_page_length= -xorriso->result_page_length; if(flag&1) { sprintf(xorriso->info_text, "... [@ = prompt suppression registered. Prompting disabled temporarily ] ...\n"); Xorriso_info(xorriso,0); } } else { Xorriso_dialog_input(xorriso,cpt,strlen(line)+1,2); /* write to history */ sprintf(xorriso->info_text, "--- Unrecognized input beginning with @. Please enter something else.\n"); Xorriso_info(xorriso,0); {ret= 3; goto ex;} } if(flag&2) {ret= 4; goto ex;} if(flag&1) {ret= 3; goto ex;} {ret= 1; goto ex;} } else if(flag&4) { if(strcmp(cpt,"i")==0 || strcmp(cpt,"I")==0 || strcmp(cpt,"n")==0 || strcmp(cpt,"N")==0 || *cpt==0) { {ret= 1; goto ex;} } else if(strcmp(cpt,"r")==0 || strcmp(cpt,"R")==0 || strcmp(cpt,"y")==0 || strcmp(cpt,"Y")==0) { {ret= 6; goto ex;} } else { /* >>> unknown input */ sprintf(xorriso->info_text, "--- Please enter one of : empty line, i,n, r,y, q,x, @, @@@\n"); Xorriso_info(xorriso,0); {ret= 3; goto ex;} } } else if(*cpt!=0 && !(flag&1)) { Xorriso_dialog_input(xorriso,cpt,strlen(line)+1,2); /* write to history */ strcpy(xorriso->pending_option,cpt); xorriso->request_to_abort= 1; sprintf(xorriso->info_text, "-------------- [ Input of option registered. Operation ends ] ---------------\n"); Xorriso_info(xorriso,0); {ret= 2; goto ex;} } else if(*cpt!=0) { Xorriso_dialog_input(xorriso,cpt,strlen(line)+1,2); /* write to history */ sprintf(xorriso->info_text, "--- Please enter one of : empty line, @, @@@\n"); Xorriso_info(xorriso,0); {ret= 3; goto ex;} } ret= 1; ex:; Xorriso_free_meM(line); Xorriso_free_meM(previous_line); return(ret); } /* @param flag bit0= quoted multiline mode bit1= release allocated memory and return 1 bit2= with bit0: warn of empty text arguments bit3= deliver as single quoted text including all whitespace and without any backslash interpretation @return -1=out of memory , 0=line format error , 1=ok, go on , 2=done */ int Xorriso_read_lines(struct XorrisO *xorriso, FILE *fp, int *linecount, int *argc, char ***argv, int flag) { char *line= NULL, *linept, *fgot; int l, base_length, append_line, ret, mem_linecount, i; Sfile_make_argv("", line, argc, argv, 2); if(flag & 2) {ret= 1; goto ex;} Xorriso_alloc_meM(line, char, 5 * SfileadrL + 2); mem_linecount= *linecount; linept= line; base_length= 0; while(1) { fgot= Sfile_fgets_n(linept, SfileadrL - base_length - 1, fp, !!(flag & (1 | 8))); if(fgot == NULL) { if(ferror(fp)) {ret= 0; goto ex;} if(linept != line) { sprintf(xorriso->info_text,"Open quotation mark at end of input"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } {ret= 2; goto ex;} } l= strlen(line); (*linecount)++; append_line= 0; if(flag & 1) { /* check whether the line is incomplete yet */ ret= Sfile_make_argv("", line, argc, argv, 16); if(ret < 0) goto ex; if(ret == 0 && !append_line) { line[l]= '\n'; line[l + 1]= 0; append_line= 1; } if(l > 0 && !append_line) if(line[l - 1] == '\\') { line[l - 1]= 0; append_line= 1; } } if(l >= SfileadrL) { sprintf(xorriso->info_text,"Input line too long !"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(!append_line) break; base_length= strlen(line); linept= line + base_length; } if((flag & 1) && !(flag & 8)) { ret= Sfile_make_argv("", line, argc, argv, 1 | ((xorriso->bsl_interpretation & 3) << 5)); if(ret < 0) goto ex; if(flag & 4) for(i= 0; i < *argc; i++) { if((*argv)[i][0] == 0) { sprintf(xorriso->info_text, "Empty text as quoted argument in "); } else if(strlen((*argv)[i]) >= SfileadrL) { (*argv)[i][SfileadrL - 1]= 0; sprintf(xorriso->info_text, "Input text too long and now truncated in"); } else continue; if(mem_linecount + 1 < *linecount) sprintf(xorriso->info_text + strlen(xorriso->info_text), "lines %d to %d", mem_linecount + 1, *linecount); else sprintf(xorriso->info_text + strlen(xorriso->info_text), "line %d", mem_linecount + 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } } else { (*argv)= Smem_malloC(sizeof(char *)); if(*argv == NULL) {ret= -1; goto ex;} (*argv)[0]= strdup(line); if((*argv)[0] == NULL) { free(*argv); (*argv)= NULL; {ret= -1; goto ex;} } *argc= 1; } ret= 1; ex:; Xorriso_free_meM(line); return(ret); } int Xorriso_predict_linecount(struct XorrisO *xorriso, char *line, int *linecount, int flag) { int width,l; char *spt,*ept; *linecount= 0; spt= line; width= xorriso->result_page_width; while(1) { ept= strchr(spt,'\n'); if(ept==NULL) l= strlen(spt); else l= ept-spt; l+= xorriso->result_open_line_len; if(ept!=NULL && l==0) (*linecount)++; else { (*linecount)+= l/width; if(ept==NULL) { xorriso->result_open_line_len= l%width; break; } (*linecount)+= !!(l%width); } xorriso->result_open_line_len= 0; spt= ept+1; } return(1); } int Xorriso_pager(struct XorrisO *xorriso, char *line, int flag) /* bit1= mark '@' by return 4 */ /* return: <=0 error , 1=go on , 2=abort , 4=see flag bit1*/ { int ret,linecount; char *info_text= NULL; if(xorriso->result_page_length<=0 || xorriso->request_not_to_ask || xorriso->dialog == 0) {ret= 1; goto ex;} Xorriso_predict_linecount(xorriso,line,&linecount,0); if(xorriso->result_line_counter+linecount>xorriso->result_page_length) { ask_for_page:; if(info_text == NULL) Xorriso_alloc_meM(info_text, char, 10*SfileadrL); strcpy(info_text,xorriso->info_text); sprintf(xorriso->info_text,"\n"); Xorriso_info(xorriso,0); sprintf(xorriso->info_text, ".... [Press Enter to continue. @,Enter avoids further stops. @@@ aborts] ....\n"); Xorriso_info(xorriso,0); ret= Xorriso_request_confirmation(xorriso,flag&2); strcpy(xorriso->info_text,info_text); if(ret<=0) goto ex; if(ret==2) {ret= 2; goto ex;} if(ret==3) goto ask_for_page; } xorriso->result_line_counter+= linecount; ret= 1; ex:; Xorriso_free_meM(info_text); return(ret); } /* @param flag bit0= no error message in case of failure */ static int Xorriso_obtain_lock(struct XorrisO *xorriso, pthread_mutex_t *lock_handle, char *purpose, int flag) { int ret; static int complaints= 0, complaint_limit= 5; ret= pthread_mutex_lock(lock_handle); if(ret != 0) { if(flag & 1) return(-1); /* Cannot report failure through the failing message output system */ complaints++; if(complaints <= complaint_limit) fprintf(stderr, "xorriso : pthread_mutex_lock() for %s returns %d\n", purpose, ret); return(-1); } return(1); } /* @param flag bit0= no error message in case of failure */ static int Xorriso_release_lock(struct XorrisO *xorriso, pthread_mutex_t *lock_handle, char *purpose, int flag) { int ret; static int complaints= 0, complaint_limit= 5; ret= pthread_mutex_unlock(lock_handle); if(ret != 0) { if(flag & 1) return(0); /* Cannot report failure through the failing message output system */ complaints++; if(complaints <= complaint_limit) fprintf(stderr, "xorriso : pthread_mutex_unlock() for %s returns %d\n", purpose, ret); return(0); } return(1); } static int Xorriso_lock_outlists(struct XorrisO *xorriso, int flag) { int ret; ret= Xorriso_obtain_lock(xorriso, &(xorriso->result_msglists_lock), "outlists", 0); return(ret); } static int Xorriso_unlock_outlists(struct XorrisO *xorriso, int flag) { int ret; ret= Xorriso_release_lock(xorriso, &(xorriso->result_msglists_lock), "outlists", 0); return(ret); } static int Xorriso_write_to_msglist(struct XorrisO *xorriso, struct Xorriso_lsT **xorriso_msglist, char *text, int flag) { int ret, locked= 0; struct Xorriso_lsT *msglist; ret= Xorriso_lock_outlists(xorriso, 0); if(ret <= 0) goto ex; locked= 1; msglist= *xorriso_msglist; ret= Xorriso_lst_append_binary(&msglist, text, strlen(text) + 1, 0); if(ret <= 0) { ret= -1; goto ex; } if(*xorriso_msglist == NULL) *xorriso_msglist= msglist; ret= 1; ex:; if(locked) Xorriso_unlock_outlists(xorriso, 0); return(ret); } int Xorriso_write_to_channel(struct XorrisO *xorriso, char *in_text, int channel_no, int flag) /* bit0= eventually backslash encode linefeeds bit1= text is the name of the log file for the given channel bit2= text is the name of the consolidated packet log file for all channels bit3= text is the name of the stderr redirection file bit15= with bit1 to bit3: close depicted log file */ { char *rpt, *npt, *text= NULL, *line= NULL; int ret= 1, info_redirected= 0, result_redirected= 0, l; char prefix[16]; FILE *logfile_fp, *pktlog_fp; static int num_channels= 4; static char channel_prefixes[4][4]= {".","R","I","M"}; int Xorriso_sieve_filter_msg(struct XorrisO *xorriso, char *msg, int flag); #ifdef Xorriso_fetch_with_msg_queueS static int complaints= 0, complaint_limit= 5; int locked= 0, uret; #endif text= in_text; /* might change due to backslash encoding */ if(channel_no<0 || channel_no>=num_channels) {ret= -1; goto ex;} #ifdef Xorriso_fetch_with_msg_queueS ret= pthread_mutex_lock(&(xorriso->write_to_channel_lock)); if(ret != 0) { /* Cannot report failure through the failing message output system */ complaints++; if(complaints <= complaint_limit) fprintf(stderr, "xorriso : pthread_mutex_lock() for write_to_channel returns %d\n", ret); /* Intentionally not aborting here */; } else locked= 1; #endif /* Xorriso_fetch_with_msg_queueS */ /* Logfiles */ logfile_fp= xorriso->logfile_fp[channel_no]; pktlog_fp= xorriso->pktlog_fp; if((flag&2) && logfile_fp!=NULL) { fprintf(logfile_fp, "! end ! end ! end ! end ! end ! end ! end ! end xorriso log : %s : %s\n", channel_prefixes[channel_no],Sfile_datestr(time(0),1|2|256)); fclose(logfile_fp); xorriso->logfile_fp[channel_no]= logfile_fp= NULL; } if((flag&4) && pktlog_fp!=NULL) { fprintf(pktlog_fp, "I:1:! end ! end ! end ! end ! end ! end ! end ! end xorriso log : %s : %s\n", channel_prefixes[channel_no],Sfile_datestr(time(0),1|2|256)); fclose(pktlog_fp); xorriso->pktlog_fp= pktlog_fp= NULL; } if((flag & 8) && xorriso->stderr_fp != NULL) { fclose(xorriso->stderr_fp); xorriso->stderr_fp= NULL; } if(flag&(1<<15)) {ret= 1; goto ex;} if((flag&2)) { xorriso->logfile_fp[channel_no]= logfile_fp= fopen(text,"a"); if(logfile_fp==NULL) {ret= 0; goto ex;} fprintf(logfile_fp, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! xorriso log : %s : %s\n", channel_prefixes[channel_no],Sfile_datestr(time(0),1|2|256)); fflush(logfile_fp); } if((flag&4)) { xorriso->pktlog_fp= pktlog_fp= fopen(text,"a"); if(pktlog_fp==NULL) {ret= 0; goto ex;} fprintf(pktlog_fp, "I:1:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! xorriso log : . : %s\n", Sfile_datestr(time(0),1|2|256)); fflush(pktlog_fp); } if(flag & 8) { ret= truncate(text, (off_t) 0); if(ret == -1 && errno != ENOENT) {ret= 0; goto ex;} xorriso->stderr_fp= fopen(text, "a"); if(xorriso->stderr_fp == NULL) {ret= 0; goto ex;} } if(flag & (2 | 4 | 8)) {ret= 1; goto ex;} /* Eventually perform backslash encoding of non-printable characters */ if(((xorriso->bsl_interpretation & 32) && channel_no == 1) || ((xorriso->bsl_interpretation & 64) && channel_no == 2)) { ret= Sfile_bsl_encoder(&text, text, strlen(text), 1 | 2 | 4); if(ret <= 0) {ret= -1; goto ex;} } /* Pick interesting words for the Xorriso_sieve API */ Xorriso_sieve_filter_msg(xorriso, text, (channel_no > 0 ? channel_no - 1 : 0)); /* Eventually perform message redirection */ if(xorriso->msglist_stackfill > 0) { if(xorriso->msglist_flags[xorriso->msglist_stackfill - 1] & 1) result_redirected= 1; if(xorriso->msglist_flags[xorriso->msglist_stackfill - 1] & 2) info_redirected= 1; } /* Non-redirected output */ if(!xorriso->packet_output) { if(channel_no==1 || channel_no==3) { if(result_redirected) { ret= Xorriso_write_to_msglist(xorriso, &(xorriso->result_msglists[xorriso->msglist_stackfill - 1]), text, 0); if(ret <= 0) { ret= -1; goto ex; } } else { printf("%s",text); fflush(stdout); } } if(channel_no==2 || channel_no==3) { if(info_redirected) { ret= Xorriso_write_to_msglist(xorriso, &(xorriso->info_msglists[xorriso->msglist_stackfill - 1]), text, 0); if(ret <= 0) { ret= -1; goto ex; } } else { if(xorriso->stderr_fp != NULL) { fprintf(xorriso->stderr_fp, "%s", text); fflush(xorriso->stderr_fp); } else fprintf(stderr, "%s", text); } } if(logfile_fp!=NULL) { fprintf(logfile_fp,"%s",text); fflush(logfile_fp); } if(pktlog_fp==NULL) {ret= 1; goto ex;} } rpt= text; sprintf(prefix,"%s:x: ",channel_prefixes[channel_no]); while(*rpt!=0) { npt= strchr(rpt,'\n'); if(npt==NULL) prefix[2]= '0'; else prefix[2]= '1'; if(!result_redirected) { if(xorriso->packet_output) { ret= fwrite(prefix,5,1,stdout); if(ret<=0) {ret= 0; goto ex;} } } if(pktlog_fp!=NULL) { ret= fwrite(prefix,5,1,pktlog_fp); if(ret<=0) {ret= 0; goto ex;} } if(npt==NULL) { if(xorriso->packet_output) { if(result_redirected) { l= strlen(rpt); Xorriso_alloc_meM(line, char, 5 + l + 1 + 1); memcpy(line, prefix, 5); memcpy(line + 5, rpt, l); line[5 + l] = '\n'; line[5 + l + 1] = 0; ret= Xorriso_write_to_msglist(xorriso, &(xorriso->result_msglists[xorriso->msglist_stackfill - 1]), line, 0); Xorriso_free_meM(line); line= NULL; if(ret <= 0) { ret= -1; goto ex; } } else { ret= fwrite(rpt,strlen(rpt),1,stdout); if(ret<=0) {ret= 0; goto ex;} ret= fwrite("\n",1,1,stdout); if(ret<=0) {ret= 0; goto ex;} } } if(pktlog_fp!=NULL) { ret= fwrite(rpt,strlen(rpt),1,pktlog_fp); if(ret<=0) {ret= 0; goto ex;} ret= fwrite("\n",1,1,pktlog_fp); if(ret<=0) {ret= 0; goto ex;} } break; } else { if(xorriso->packet_output) { if(result_redirected) { l= npt + 1 - rpt; Xorriso_alloc_meM(line, char, 5 + l + 1); memcpy(line, prefix, 5); memcpy(line + 5, rpt, l); line[5 + l] = 0; ret= Xorriso_write_to_msglist(xorriso, &(xorriso->result_msglists[xorriso->msglist_stackfill - 1]), line, 0); Xorriso_free_meM(line); line= NULL; if(ret <= 0) { ret= -1; goto ex; } } else { ret= fwrite(rpt,npt+1-rpt,1,stdout); if(ret<=0) {ret= 0; goto ex;} } } if(pktlog_fp!=NULL) { ret= fwrite(rpt,npt+1-rpt,1,pktlog_fp); if(ret<=0) {ret= 0; goto ex;} } } rpt= npt+1; } if(xorriso->packet_output) fflush(stdout); if(pktlog_fp!=NULL) fflush(pktlog_fp); ret= 1; ex: if(text != in_text && text != NULL) free(text); Xorriso_free_meM(line); #ifdef Xorriso_fetch_with_msg_queueS if(locked) { uret= pthread_mutex_unlock(&(xorriso->write_to_channel_lock)); if(uret != 0) { /* Cannot report failure through the failing message output system */ complaints++; if(complaints <= complaint_limit) fprintf(stderr, "xorriso : pthread_mutex_unlock() for write_to_channel returns %d\n", uret); } } #endif /* Xorriso_fetch_with_msg_queueS */ return(ret); } int Xorriso_push_outlists(struct XorrisO *xorriso, int *stack_handle, int flag) { int ret, locked= 0; ret= Xorriso_lock_outlists(xorriso, 0); if(ret <= 0) goto ex; locked= 1; if(xorriso->msglist_stackfill + 1 >= Xorriso_max_outlist_stacK) { Xorriso_unlock_outlists(xorriso, 0); locked= 0; Xorriso_msgs_submit(xorriso, 0, "Overflow of message output redirection stack", 0, "FATAL", 0); ret= -1; goto ex; } if((flag & 3) == 0) flag|= 3; xorriso->msglist_stackfill++; xorriso->result_msglists[xorriso->msglist_stackfill - 1]= NULL; xorriso->info_msglists[xorriso->msglist_stackfill - 1]= NULL; xorriso->msglist_flags[xorriso->msglist_stackfill - 1]= flag & 3; *stack_handle= xorriso->msglist_stackfill - 1; ret= 1; ex:; if(locked) Xorriso_unlock_outlists(xorriso, 0); return(ret); return(1); } int Xorriso_fetch_outlists(struct XorrisO *xorriso, int stack_handle, struct Xorriso_lsT **result_list, struct Xorriso_lsT **info_list, int flag) { int ret, locked= 0; #ifdef Xorriso_fetch_with_msg_queueS ret= Xorriso_process_msg_queues(xorriso, 0); if(ret <= 0) goto ex; #endif /* Xorriso_fetch_with_msg_queueS */ if((flag & 3) == 0) flag|= 3; ret= Xorriso_lock_outlists(xorriso, 0); if(ret <= 0) goto ex; locked= 1; if(stack_handle == -1) stack_handle= xorriso->msglist_stackfill - 1; if(stack_handle < 0 || stack_handle >= xorriso->msglist_stackfill) { Xorriso_unlock_outlists(xorriso, 0); locked= 0; Xorriso_msgs_submit(xorriso, 0, "Program error: Wrong message output redirection stack handle", 0, "FATAL", 0); ret= -1; goto ex; } if(flag & 1) { *result_list= xorriso->result_msglists[stack_handle]; xorriso->result_msglists[stack_handle]= NULL; } if(flag & 2) { *info_list= xorriso->info_msglists[stack_handle]; xorriso->info_msglists[stack_handle]= NULL; } ret= 1; ex:; if(locked) Xorriso_unlock_outlists(xorriso, 0); return(ret); } int Xorriso_peek_outlists(struct XorrisO *xorriso, int stack_handle, int timeout, int flag) { int ret, locked= 0, yes= 0; static int u_wait= 19000; time_t start_time; if((flag & 3) == 0) flag|= 3; if(stack_handle == -1) stack_handle= xorriso->msglist_stackfill - 1; start_time= time(NULL); try_again:; ret= Xorriso_obtain_lock(xorriso, &(xorriso->msgw_fetch_lock), "message watcher fetch operation", 0); if(ret <= 0) {yes= -2; goto ex;} locked= 1; yes= 0; if(stack_handle < 0 || stack_handle >= xorriso->msglist_stackfill) {yes= -1; goto ex;} if(flag & 1) yes|= (xorriso->result_msglists[stack_handle] != NULL); if(flag & 2) yes|= (xorriso->info_msglists[stack_handle] != NULL); if(xorriso->msg_watcher_state == 2 && xorriso->msgw_msg_pending) yes|= 2; ret= Xorriso_release_lock(xorriso, &(xorriso->msgw_fetch_lock), "message watcher fetch operation", 0); if(ret <= 0) {yes= -2; goto ex;} locked= 0; if(yes && (flag & 4)) { usleep(u_wait); if(time(NULL) <= start_time + timeout) goto try_again; } ex:; if(locked) { ret= Xorriso_release_lock(xorriso, &(xorriso->msgw_fetch_lock), "message watcher fetch operation", 0); if(ret <= 0 && yes >= 0) yes= -2; } return(yes); } int Xorriso_pull_outlists(struct XorrisO *xorriso, int stack_handle, struct Xorriso_lsT **result_list, struct Xorriso_lsT **info_list, int flag) { int i, ret, locked= 0; ret= Xorriso_lock_outlists(xorriso, 0); if(ret <= 0) goto ex; locked= 1; if(stack_handle == -1) stack_handle= xorriso->msglist_stackfill - 1; if(stack_handle < 0 || stack_handle >= xorriso->msglist_stackfill) { Xorriso_unlock_outlists(xorriso, 0); locked= 0; Xorriso_msgs_submit(xorriso, 0, "Program error: Wrong message output redirection stack handle", 0, "FATAL", 0); ret= -1; goto ex; } /* Concatenate all redirections above stack_handle */ *result_list= NULL; *info_list= NULL; for(i = stack_handle; i < xorriso->msglist_stackfill; i++) { if(*result_list == NULL) *result_list= xorriso->result_msglists[i]; else Xorriso_lst_concat(*result_list, xorriso->result_msglists[i], 0); if(*info_list == NULL) *info_list= xorriso->info_msglists[i]; else Xorriso_lst_concat(*info_list, xorriso->info_msglists[i], 0); } xorriso->msglist_stackfill= stack_handle; ret= 1; ex:; if(locked) Xorriso_unlock_outlists(xorriso, 0); return(ret); } int Xorriso_info_handler_stderr(void *handle, char *text) { struct XorrisO *xorriso; xorriso= (struct XorrisO *) handle; if(xorriso->stderr_fp != NULL) { fprintf(xorriso->stderr_fp, "%s", text); fflush(xorriso->stderr_fp); } else { fprintf(stderr, "%s", text); fflush(stderr); } return(1); } int Xorriso_result_handler_stdout(void *handle, char *text) { printf("%s", text); fflush(stdout); return(1); } int Xorriso_result_handler_pkt(void *handle, char *text) { int nl= -1, ret, l; struct XorrisO *xorriso; xorriso= (struct XorrisO *) handle; if(!xorriso->packet_output) return Xorriso_result_handler_stdout(handle, text); /* Interpret pkt_output */ l= strlen(text); if(l >= 5) { if(strchr("RIM", text[0]) != NULL && text[1] == ':' && strchr("01", text[2]) != NULL && text[3] == ':' && text[4] == ' ') nl= (text[2] == '1'); } if(nl < 0) /* Not pkt_output format */ return Xorriso_result_handler_stdout(handle, text); if(nl == 0) { /* Suppress newline */ if(text[l - 1] == '\n') l--; } if(text[0] == 'R') { ret= fwrite(text + 5, l - 5, 1, stdout); } else { ret= fwrite(text + 5, l - 5, 1, xorriso->stderr_fp != NULL ? xorriso->stderr_fp : stderr); } if(ret <= 0) return(0); return(1); } int Xorriso_process_msg_lists(struct XorrisO *xorriso, struct Xorriso_lsT *result_list, struct Xorriso_lsT *info_list, int *line_count, int flag) { struct Xorriso_lsT *lpt; int ret; int (*handler)(void *handle, char *text); void *handle; handler= xorriso->msgw_result_handler; handle= xorriso->msgw_result_handle; if(handler == NULL) { handler= Xorriso_result_handler_pkt; handle= xorriso; } for(lpt= result_list; lpt != NULL; lpt= lpt->next) { (*line_count)++; ret= (*handler)(handle, Xorriso_lst_get_text(lpt, 0)); if(ret < 0) return(-1); } handler= xorriso->msgw_info_handler; handle= xorriso->msgw_info_handle; if(handler == NULL) { handler= Xorriso_info_handler_stderr; handle= xorriso; } for(lpt= info_list; lpt != NULL; lpt= lpt->next) { (*line_count)++; ret= (*handler)(handle, Xorriso_lst_get_text(lpt, 0)); if(ret < 0) return(-1); } return(1); } static void *Xorriso_msg_watcher(void *state_pt) { struct XorrisO *xorriso; int ret, u_wait= 25000, line_count, sleep_thresh= 20, lock_failure= 0; struct Xorriso_lsT *result_list= NULL, *info_list= NULL; static int debug_sev= 0; xorriso= (struct XorrisO *) state_pt; if(debug_sev == 0) Xorriso__text_to_sev("DEBUG", &debug_sev, 0); xorriso->msg_watcher_state= 2; if(xorriso->msgw_info_handler != NULL && debug_sev < xorriso->report_about_severity && debug_sev < xorriso->abort_on_severity) (*xorriso->msgw_info_handler)(xorriso, "xorriso : DEBUG : Concurrent message watcher started\n"); while(1) { line_count= 0; /* Watch out for end request in xorriso */ if(xorriso->msg_watcher_state == 3) break; ret= Xorriso_obtain_lock(xorriso, &(xorriso->msgw_fetch_lock), "message watcher fetch operation", 1); if(ret <= 0) { lock_failure= 1; break; } xorriso->msgw_msg_pending= 1; ret= Xorriso_fetch_outlists(xorriso, -1, &result_list, &info_list, 3); if(ret > 0) { /* Process fetched lines */ xorriso->msgw_msg_pending= 2; Xorriso_release_lock(xorriso, &(xorriso->msgw_fetch_lock), "message watcher fetch operation", 1); ret= Xorriso_process_msg_lists(xorriso, result_list, info_list, &line_count, 0); xorriso->msgw_msg_pending= 0; Xorriso_lst_destroy_all(&result_list, 0); Xorriso_lst_destroy_all(&info_list, 0); if(ret < 0) break; } else { xorriso->msgw_msg_pending= 0; Xorriso_release_lock(xorriso, &(xorriso->msgw_fetch_lock), "message watcher fetch operation", 1); } xorriso->msgw_msg_pending= 0; if(ret < 0) break; if(line_count < sleep_thresh) usleep(u_wait); } if(xorriso->msgw_info_handler != NULL && debug_sev < xorriso->report_about_severity && debug_sev < xorriso->abort_on_severity && !lock_failure) (*xorriso->msgw_info_handler)(xorriso, "xorriso : DEBUG : Concurrent message watcher ended\n"); xorriso->msg_watcher_state= 0; return(NULL); } int Xorriso_start_msg_watcher(struct XorrisO *xorriso, int (*result_handler)(void *handle, char *text), void *result_handle, int (*info_handler)(void *handle, char *text), void *info_handle, int flag) { int ret, u_wait= 1000, locked= 0, pushed= 0, uret, line_count= 0; struct Xorriso_lsT *result_list= NULL, *info_list= NULL; pthread_attr_t attr; pthread_attr_t *attr_pt = NULL; pthread_t thread; ret= pthread_mutex_lock(&(xorriso->msg_watcher_lock)); if(ret != 0) { Xorriso_msgs_submit(xorriso, 0, "Cannot acquire mutex lock for managing concurrent message watcher", ret, "FATAL", 0); ret= -1; goto ex; } locked= 1; /* Check for running watcher */ if(xorriso->msg_watcher_state > 0) { sprintf(xorriso->info_text, "There is already a concurrent message watcher running"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } ret= Xorriso_push_outlists(xorriso, &(xorriso->msgw_stack_handle), 3); if(ret <= 0) goto ex; pushed= 1; /* Register watcher */ xorriso->msgw_result_handler= result_handler; xorriso->msgw_result_handle= result_handle; xorriso->msgw_info_handler= info_handler; xorriso->msgw_info_handle= info_handle; xorriso->msg_watcher_state= 1; /* Start thread */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); attr_pt= &attr; ret= pthread_create(&thread, attr_pt, Xorriso_msg_watcher, xorriso); if(ret != 0) { sprintf(xorriso->info_text, "Cannot create thread for concurrent message watcher"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } /* Wait until watcher has indicated start */ while(xorriso->msg_watcher_state == 1) { /* >>> have a timeout ? */; usleep(u_wait); } ret= 1; ex:; if(ret <= 0 && pushed) { uret= Xorriso_pull_outlists(xorriso, xorriso->msgw_stack_handle, &result_list, &info_list, 0); if(uret > 0) { xorriso->msgw_result_handler= NULL; xorriso->msgw_info_handler= NULL; Xorriso_process_msg_lists(xorriso, result_list, info_list, &line_count, 0); Xorriso_lst_destroy_all(&result_list, 0); Xorriso_lst_destroy_all(&info_list, 0); } } if(locked) { uret= pthread_mutex_unlock(&(xorriso->msg_watcher_lock)); if(uret != 0) { Xorriso_msgs_submit(xorriso, 0, "Cannot release mutex lock for managing concurrent message watcher", uret, "FATAL", 0); ret= -1; } } return(ret); } /* @param flag bit0= do not complain loudly if no wather is active */ int Xorriso_stop_msg_watcher(struct XorrisO *xorriso, int flag) { int ret, u_wait= 1000, locked= 0, uret, line_count= 0; struct Xorriso_lsT *result_list= NULL, *info_list= NULL; if((flag & 1) && xorriso->msg_watcher_state != 2) /* Roughly tolerate non-running watcher */ {ret= 0; goto ex;} ret= pthread_mutex_lock(&(xorriso->msg_watcher_lock)); if(ret != 0) { Xorriso_msgs_submit(xorriso, 0, "Cannot acquire mutex lock for managing concurrent message watcher", ret, "FATAL", 0); ret= -1; goto ex; } locked= 1; /* Check for running watcher */ if(xorriso->msg_watcher_state != 2) { sprintf(xorriso->info_text, "There is no concurrent message watcher running"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "SORRY", 0); ret= 0; goto ex; } /* Inform watcher of desire to stop it */ xorriso->msg_watcher_state= 3; /* Wait until watcher has indicated its end */ while(xorriso->msg_watcher_state != 0) { /* >>> have a timeout ? */; usleep(u_wait); } ret= Xorriso_obtain_lock(xorriso, &(xorriso->msgw_fetch_lock), "message watcher fetch operation", 1); if(ret <= 0) { Xorriso_msgs_submit(xorriso, 0, "Cannot obtain mutex lock for managing concurrent message watcher", ret, "FATAL", 0); ret= -1; goto ex; } xorriso->msgw_msg_pending= 1; ret= Xorriso_pull_outlists(xorriso, xorriso->msgw_stack_handle, &result_list, &info_list, 0); if(ret > 0) { xorriso->msgw_msg_pending= 2; Xorriso_release_lock(xorriso, &(xorriso->msgw_fetch_lock), "message watcher fetch operation", 1); Xorriso_process_msg_lists(xorriso, result_list, info_list, &line_count, 0); xorriso->msgw_msg_pending= 0; Xorriso_lst_destroy_all(&result_list, 0); Xorriso_lst_destroy_all(&info_list, 0); } else { xorriso->msgw_msg_pending= 0; Xorriso_release_lock(xorriso, &(xorriso->msgw_fetch_lock), "message watcher fetch operation", 1); } xorriso->msgw_result_handler= NULL; xorriso->msgw_info_handler= NULL; ret= 1; ex:; if(locked) { uret= pthread_mutex_unlock(&(xorriso->msg_watcher_lock)); if(uret != 0) { Xorriso_msgs_submit(xorriso, 0, "Cannot release mutex lock for managing concurrent message watcher", uret, "FATAL", 0); ret= -1; } } return(ret); } /* -------------------------- Xorriso_msg_sievE -------------------------- */ struct Xorriso_msg_filteR { char *name; char *prefix; char *separators; int channels; /* What to watch: bit0=result , bit1=info , bit2=mark */ int num_words; int *word_idx; int last_word_line_end; /* Oldest result gets discarded when new surpassed threshold */ int max_results; struct Xorriso_lsT *results; /* Serialized tuples of num_words */ int num_results; int num_delivered; struct Xorriso_lsT *next_to_deliver; struct Xorriso_msg_filteR *prev; struct Xorriso_msg_filteR *next; }; int Xorriso_msg_filter_destroy(struct Xorriso_msg_filteR **o, int flag); int Xorriso_msg_filter_new(struct Xorriso_msg_filteR **o, char *name, struct Xorriso_msg_filteR *prev, struct Xorriso_msg_filteR *next, int flag) { struct Xorriso_msg_filteR *m; m= (*o)= TSOB_FELD(struct Xorriso_msg_filteR, 1); if((*o) == NULL) return(-1); m->name= NULL; m->prefix= NULL; m->separators= NULL; m->channels= 7; m->num_words= 0; m->word_idx= NULL; m->last_word_line_end= flag & 1; m->max_results= 1; m->results= NULL; m->num_results= 0; m->num_delivered= 0; m->next_to_deliver= NULL; m->name= strdup(name); if(m->name == NULL) goto failure; m->prev= prev; if(prev != NULL) prev->next= m; m->next= next; if(next != NULL) next->prev= m; return(1); failure: Xorriso_msg_filter_destroy(o, 0); return(-1); } int Xorriso_msg_filter_destroy(struct Xorriso_msg_filteR **o, int flag) { struct Xorriso_msg_filteR *m; if((*o)==NULL) return(0); m= *o; if(m->name != NULL) free(m->name); if(m->prefix != NULL) free(m->prefix); if(m->separators != NULL) free(m->separators); if(m->word_idx != NULL) free((char *) m->word_idx); if(m->results != NULL) Xorriso_lst_destroy_all(&(m->results), 0); if(m->prev != NULL) m->prev->next= m->next; if(m->next != NULL) m->next->prev= m->prev; free(*o); *o= NULL; return(1); } int Xorriso_msg_filter_set_words(struct Xorriso_msg_filteR *m, int num_words, int *word_idx, int flag) { int i; if(m->word_idx != NULL) free(m->word_idx); m->num_words= 0; if(num_words <= 0) return(1); m->word_idx= TSOB_FELD(int, num_words); if(m->word_idx == NULL) return(-1); for(i= 0; i < num_words; i++) m->word_idx[i]= word_idx[i]; m->num_words= num_words; return(1); } struct Xorriso_msg_sievE { int num_filters; struct Xorriso_msg_filteR *first_filter; }; int Xorriso_msg_sieve_new(struct Xorriso_msg_sievE **o, int flag) { struct Xorriso_msg_sievE *m; m= (*o)= TSOB_FELD(struct Xorriso_msg_sievE, 1); if((*o) == NULL) return(-1); m->num_filters= 0; m->first_filter= NULL; return(1); } int Xorriso_msg_sieve_destroy(struct Xorriso_msg_sievE **o, int flag) { struct Xorriso_msg_sievE *m; struct Xorriso_msg_filteR *f, *next_f= NULL; if((*o) == NULL) return(0); m= *o; for(f= m->first_filter; f != NULL; f= next_f) { next_f= f->next; Xorriso_msg_filter_destroy(&f, 0); } free(*o); *o= NULL; return(1); } /* API */ int Xorriso_sieve_add_filter(struct XorrisO *xorriso, char *name, int channels, char *prefix, char *separators, int num_words, int *word_idx, int max_results, int flag) { int ret; struct Xorriso_msg_sievE *sieve= NULL; struct Xorriso_msg_filteR *filter; if(xorriso->msg_sieve == NULL) { ret= Xorriso_msg_sieve_new(&sieve, 0); if(ret <= 0) goto no_mem; xorriso->msg_sieve= sieve; } else sieve= xorriso->msg_sieve; ret= Xorriso_msg_filter_new(&filter, name, NULL, sieve->first_filter, flag & 1); if(ret <= 0) goto no_mem; sieve->first_filter= filter; ret= Xorriso_msg_filter_set_words(filter, num_words, word_idx, 0); if(ret <= 0) goto no_mem; if(prefix != NULL) filter->prefix= strdup(prefix); if(separators != NULL) filter->separators= strdup(separators); filter->channels= channels; filter->max_results= max_results; (sieve->num_filters)++; return(1); no_mem:; Xorriso_msg_sieve_destroy(&sieve, 0); Xorriso_no_malloc_memory(xorriso, NULL, 0); return(-1); } /* API */ int Xorriso_sieve_dispose(struct XorrisO *xorriso, int flag) { Xorriso_msg_sieve_destroy(&(xorriso->msg_sieve), 0); return(1); } /* API */ int Xorriso_sieve_clear_results(struct XorrisO *xorriso, int flag) { struct Xorriso_msg_filteR *f; if(xorriso->msg_sieve == NULL) return(1); for(f= xorriso->msg_sieve->first_filter; f != NULL; f= f->next) { f->num_results= 0; f->num_delivered= 0; if(f->results != NULL) Xorriso_lst_destroy_all(&(f->results), 0); f->next_to_deliver= NULL; } return(1); } /* API */ /* @param flag bit0= Reset reading to first matching result bit1= Only inquire number of available results. Do not allocate memory. bit2= If *argv is not NULL, then free it before attaching new memory. bit3= Do not read recorded data but rather list all filter names */ int Xorriso_sieve_get_result(struct XorrisO *xorriso, char *name, int *argc, char ***argv, int *available, int flag) { struct Xorriso_msg_filteR *f; struct Xorriso_lsT *lst; int i; if(flag & 4) Xorriso__dispose_words(argc, argv); *argc= 0; *argv= NULL; if(xorriso->msg_sieve == NULL) return(0); if(flag & 8) { if(xorriso->msg_sieve->num_filters <= 0) return(0); *argv= calloc(xorriso->msg_sieve->num_filters, sizeof(char *)); if(*argv == NULL) goto no_mem; *argc= xorriso->msg_sieve->num_filters; for(i= 0; i < *argc; i++) (*argv)[i]= NULL; i= 0; for(f= xorriso->msg_sieve->first_filter; f != NULL; f= f->next) { (*argv)[*argc - i - 1]= strdup(f->name); if((*argv)[*argc - i - 1] == NULL) goto no_mem; i++; } *argc= i; return(1); } for(f= xorriso->msg_sieve->first_filter; f != NULL; f= f->next) { if(strcmp(f->name, name) != 0) continue; *available= f->num_results - f->num_delivered; if(*available <= 0) return(0); if(flag & 2) return(1); if(flag & 1) { f->num_delivered= 0; f->next_to_deliver= NULL; } if(f->next_to_deliver == NULL) { f->next_to_deliver= f->results; for(i= 0; i < f->num_words * f->num_delivered; i++) if(f->next_to_deliver != NULL) f->next_to_deliver= Xorriso_lst_get_next(f->next_to_deliver, 0); } if(f->next_to_deliver == NULL) { /* Should not happen */ goto unexpected_null; } if(f->num_words <= 0) return(1); *argv= calloc(f->num_words, sizeof(char *)); if(*argv == NULL) goto no_mem; *argc= f->num_words; for(i= 0; i < *argc; i++) (*argv)[i]= NULL; lst= f->next_to_deliver; for(i= 0; i < *argc; i++) { if(lst != NULL) { (*argv)[i]= strdup(Xorriso_lst_get_text(lst, 0)); if((*argv)[i] == NULL) goto no_mem; } else { /* should not happen */ unexpected_null:; Xorriso_msgs_submit(xorriso, 0, "Program error: Unexpected NULL pointer in message sieve.", 0, "WARNING", 0); if(*argv != NULL) Xorriso__dispose_words(argc, argv); *available= 0; return(-2); } lst= Xorriso_lst_get_next(lst, 0); } f->next_to_deliver= lst; (f->num_delivered)++; (*available)--; return(1); } return(-2); no_mem: if(*argv != NULL) Xorriso__dispose_words(argc, argv); Xorriso_no_malloc_memory(xorriso, NULL, 0); return(-1); } int Xorriso_sieve_big(struct XorrisO *xorriso, int flag) { struct Xorriso_sieve_big_filteR { char *name; int channels; char *prefix; char *separators; int num_words; int word_idx[6]; int max_results; int flag; }; static struct Xorriso_sieve_big_filteR filters[] = { {"-changes_pending", 3, "-changes_pending", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"? -dev", 3, "? -dev", "", 4, { 0, 1, 3, 4, -1, -1}, 10, 0}, {"?? -dev", 3, "?? -dev", "", 4, { 0, 1, 3, 4, -1, -1}, 90, 0}, {"Abstract File:", 3, "Abstract File: ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"After commit :", 3, "After commit :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"App Id :", 3, "App Id : ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"Biblio File :", 3, "Biblio File : ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"Build timestamp :", 3, "Build timestamp : ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"CopyrightFile:", 3, "CopyrightFile: ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"Creation Time:", 3, "Creation Time: ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"DVD obs 64 kB:", 3, "DVD obs 64 kB:", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"Drive access :", 3, "Drive access : ", ": ", 2, { 0, 1, -1, -1, -1, -1}, 2, 0}, {"Drive current:", 3, "Drive current:", "", 2, { 0, 1, -1, -1, -1, -1}, 2, 0}, {"Drive id :", 3, "Drive id :", "", 1, { 0, -1, -1, -1, -1, -1}, 2, 0}, {"Drive type :", 3, "Drive type :", "", 3, { 1, 3, 5, -1, -1, -1}, 2, 0}, {"Eff. Time :", 3, "Eff. Time : ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"Expir. Time :", 3, "Expir. Time : ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"Ext. filters :", 3, "Ext. filters : ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"File damaged :", 3, "File damaged :", "", 4, { 0, 2, 4, 6, -1, -1}, 10000, 0}, {"File data lba:", 3, "File data lba:", "", 5, { 0, 2, 4, 6, 8, -1}, 10000, 0}, {"Format idx :", 3, "Format idx ", ",: ", 4, { 0, 1, 2, 3, -1, -1}, 100, 1}, {"Format status:", 3, "Format status:", ", ", 2, { 0, 1, -1, -1, -1, -1}, 1, 1}, {"ISO session :", 3, "ISO session :", "", 4, { 0, 2, 4, 6, -1, -1}, 10000, 1}, {"Image size :", 3, "Image size :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"Indev feature:", 3, "Indev feature:", "= ", 2, { 0, 1, -1, -1, -1, -1}, 10000, 1}, {"Jigdo files :", 3, "Jigdo files :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"Local ACL :", 3, "Local ACL :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"Local xattr :", 3, "Local xattr :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"MD5 MISMATCH:", 3, "MD5 MISMATCH:", "", 1, { 0, -1, -1, -1, -1, -1}, 10000, 0}, {"MD5 tag range:", 3, "MD5 tag range:", "", 3, { 0, 2, 4, -1, -1, -1}, 10000, 1}, {"Media blocks :", 3, "Media blocks :", "", 3, { 0, 3, 6, -1, -1, -1}, 2, 0}, {"Media current:", 3, "Media current: ", "", 1, { 0, -1, -1, -1, -1, -1}, 2, 1}, {"Media id :", 3, "Media id :", "", 1, { 0, -1, -1, -1, -1, -1}, 2, 0}, {"Media nwa :", 3, "Media nwa :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"Media product:", 3, "Media product:", "", 2, { 0, 2, -1, -1, -1, -1}, 2, 1}, {"Media region :", 3, "Media region :", "", 3, { 0, 2, 4, -1, -1, -1}, 10000, 1}, {"Media space :", 3, "Media space :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"Media status :", 3, "Media status : ", "", 1, { 0, -1, -1, -1, -1, -1}, 2, 1}, {"Media summary:", 3, "Media summary:", "", 4, { 0, 2, 5, 7, -1, -1}, 2, 0}, {"Modif. Time :", 3, "Modif. Time : ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"Overburnt by :", 3, "Overburnt by :", "", 1, { 0, -1, -1, -1, -1, -1}, 2, 0}, {"PVD address :", 3, "PVD address :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"Preparer Id :", 3, "Preparer Id : ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"Profile :", 3, "Profile :", "", 2, { 0, 1, -1, -1, -1, -1}, 256, 1}, {"Publisher Id :", 3, "Publisher Id : ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"Readline :", 3, "Readline :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"Size lower :", 3, "Size lower :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"Size upper :", 3, "Size upper :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"System Id :", 3, "System Id : ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"Version timestamp :", 3, "Version timestamp :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"Volume Id :", 3, "Volume Id : ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"Volume Set Id:", 3, "Volume Set Id: ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"Volume id :", 3, "Volume id :", "", 1, { 0, -1, -1, -1, -1, -1}, 2, 0}, {"Write speed :", 3, "Write speed :", "", 2, { 0, 2, -1, -1, -1, -1}, 100, 0}, {"Write speed H:", 3, "Write speed H:", "", 2, { 0, 2, -1, -1, -1, -1}, 1, 0}, {"Write speed L:", 3, "Write speed L:", "", 2, { 0, 2, -1, -1, -1, -1}, 1, 0}, {"Write speed h:", 3, "Write speed h:", "", 2, { 0, 2, -1, -1, -1, -1}, 1, 0}, {"Write speed l:", 3, "Write speed l:", "", 2, { 0, 2, -1, -1, -1, -1}, 1, 0}, {"libburn in use :", 3, "libburn in use :", "", 2, { 0, 1, -1, -1, -1, -1}, 1, 1}, {"libburn OS adapter:", 3, "libburn OS adapter: ", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 1}, {"libisoburn in use :", 3, "libisoburn in use :", "", 2, { 0, 1, -1, -1, -1, -1}, 1, 1}, {"libisofs in use :", 3, "libisofs in use :", "", 2, { 0, 1, -1, -1, -1, -1}, 1, 1}, {"libjte in use :", 3, "libjte in use :", "", 2, { 0, 1, -1, -1, -1, -1}, 1, 1}, {"xorriso version :", 3, "xorriso version :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"zisofs :", 3, "zisofs :", "", 1, { 0, -1, -1, -1, -1, -1}, 1, 0}, {"@", 0, "@", "", 0, {-1, -1, -1, -1, -1, -1}, 0, 0} }; struct Xorriso_sieve_big_filteR *f; int ret, i; for(i= 0; ; i++) { f= &(filters[i]); if(strcmp(f->name, "@") == 0) break; ret= Xorriso_sieve_add_filter(xorriso, f->name, f->channels, f->prefix, f->separators, f->num_words, f->word_idx, f->max_results, f->flag); if(ret <= 0) goto failure; } return(1); failure: Xorriso_sieve_dispose(xorriso, 0); return(-1); } /* Check for matching filter and eventually extract words. To be called by Xorriso_result, Xorriso_info, Xorriso_mark, and alike. Thus no own message output is allowed here ! @param flag bit0-1= channel: 0= result channel 1= info channel 2= mark channel */ int Xorriso_sieve_filter_msg(struct XorrisO *xorriso, char *msg, int flag) { int channel, ret, argc= 0, i, max_words, l, widx, skip; char **argv= NULL, *prefix_storage= NULL, *prefix, *cpt, *to_parse= NULL; struct Xorriso_msg_filteR *f; struct Xorriso_lsT *lst, *prev_lst, *next_lst; if(xorriso->msg_sieve == NULL || xorriso->msg_sieve_disabled) return(1); channel= flag & 3; for(f= xorriso->msg_sieve->first_filter; f != NULL; f= f->next) { if(!(f->channels & (1 << channel))) continue; prefix= f->prefix; if(prefix[0] == '?') { skip= 0; for(cpt= prefix; *cpt; cpt++) if(*cpt == '?') skip++; else break; l= strlen(prefix); if(strlen(msg) >= (unsigned int) l) { if(l - skip == 0 || strncmp(prefix + skip, msg + skip, l - skip) == 0) { Xorriso_free_meM(prefix_storage); prefix_storage= NULL; Xorriso_alloc_meM(prefix_storage, char, l + 1); strncpy(prefix_storage, msg, l); prefix_storage[l]= 0; prefix= prefix_storage; } } } if(prefix[0]) if(strncmp(prefix, msg, strlen(prefix)) != 0) continue; if (to_parse != NULL) free(to_parse); to_parse= strdup(msg); if(to_parse == NULL) goto no_mem; l= strlen(to_parse); if(l > 0) if(to_parse[l - 1] == '\n') to_parse[l - 1]= 0; max_words= 0; if(f->last_word_line_end) if(f->num_words > 0) /* Let last word take rest of line */ max_words= f->word_idx[f->num_words - 1]; if(max_words <= 0 && f->last_word_line_end) { /* Copy rest of line as single word because Xorriso_parse_line understands max_words == 0 as unlimited number of words. But here it is desired to get the rest of line already in argv[0]. */ max_words= 0; argv= calloc(1, sizeof(char *)); if(argv == NULL) goto no_mem; argc= 1; argv[0]= strdup(to_parse + strlen(prefix)); if(argv[0] == NULL) goto no_mem; ret= 1; } else { ret= Xorriso_parse_line(xorriso, to_parse, prefix, f->separators, max_words, &argc, &argv, 0); } if(ret < 0) goto ex; if(ret == 0) continue; if(f->last_word_line_end && argc > max_words) { l= strlen(argv[max_words]); if(l > 0) if(argv[max_words][l - 1] == '\n') argv[max_words][l - 1]= 0; } if(f->max_results > 0 && f->num_results >= f->max_results) { /* Dispose surplus results */ for(i= 0; i < f->num_words; i++) { if(f->results != NULL) { next_lst= f->results->next; Xorriso_lst_destroy(&(f->results), 0); f->results= next_lst; } } if(f->num_delivered > 0) (f->num_delivered)--; if(f->num_delivered == 0) f->next_to_deliver= NULL; f->num_results--; } if(f->results == NULL) { prev_lst= NULL; } else { for(prev_lst= f->results; prev_lst->next != NULL; prev_lst= prev_lst->next); } for(i= 0; i < f->num_words; i++) { widx= f->word_idx[i]; if(widx >= argc || widx < 0) ret= Xorriso_lst_new(&lst, "", prev_lst, 0); else if(argv[widx] == NULL) ret= Xorriso_lst_new(&lst, "", prev_lst, 0); else ret= Xorriso_lst_new(&lst, argv[widx], prev_lst, 0); if(ret <= 0) goto no_mem; if(prev_lst == NULL) f->results= lst; prev_lst= lst; } (f->num_results)++; Xorriso__dispose_words(&argc, &argv); } ret= 1; ex: if(to_parse != NULL) free(to_parse); Xorriso_free_meM(prefix_storage); Xorriso__dispose_words(&argc, &argv); return(ret); no_mem:; Xorriso_no_malloc_memory(xorriso, NULL, 1); /* reports to stderr */ ret= -1; goto ex; } /* ^^^^^^^^^^^^^^^^^^^^^^^^^^ Xorriso_msg_sievE ^^^^^^^^^^^^^^^^^^^^^^^^^^ */ int Xorriso_result(struct XorrisO *xorriso, int flag) /* bit0= no considerations or computations or dialog. Just put out. */ { int ret, redirected= 0; if(flag&1) goto put_it_out; if(xorriso->request_to_abort) return(1); if(xorriso->msglist_stackfill > 0) if(xorriso->msglist_flags[xorriso->msglist_stackfill - 1] & 1) redirected= 1; if(xorriso->result_page_length>0 && !redirected) { ret= Xorriso_pager(xorriso,xorriso->result_line,2); if(ret<=0) return(ret); if(ret==2) return(1); if(xorriso->request_to_abort) return(1); } put_it_out:; xorriso->bar_is_fresh= 0; ret= Xorriso_write_to_channel(xorriso, xorriso->result_line, 1,0); return(ret); } int Xorriso_info(struct XorrisO *xorriso, int flag) /* bit0= use pager (as with result) bit1= permission to suppress output bit2= insist in showing output */ { int ret; static int note_sev= 0; if(flag&2) if(xorriso->request_to_abort) return(1); if(note_sev==0) Xorriso__text_to_sev("NOTE", ¬e_sev, 0); if(note_sev<xorriso->report_about_severity && note_sev<xorriso->abort_on_severity && !(flag&4)) return(1); if(flag&1) { ret= Xorriso_pager(xorriso,xorriso->info_text,2); if(ret<=0) return(ret); if(ret==2) return(1); if(flag&2) if(xorriso->request_to_abort) return(1); } xorriso->bar_is_fresh= 0; ret=Xorriso_write_to_channel(xorriso, xorriso->info_text, 2, 0); return(ret); } int Xorriso_mark(struct XorrisO *xorriso, int flag) { int ret= 1,r_ret,i_ret; if(xorriso->mark_text[0]==0) return(1); if(xorriso->packet_output) ret=Xorriso_write_to_channel(xorriso, xorriso->mark_text, 3, 0); else { sprintf(xorriso->result_line,"%s\n",xorriso->mark_text); r_ret= Xorriso_result(xorriso,1); strcpy(xorriso->info_text,xorriso->result_line); i_ret= Xorriso_info(xorriso,0); if(r_ret==0 || i_ret==0) ret= 0; } return(ret); } int Xorriso_restxt(struct XorrisO *xorriso, char *text) { int ret; strncpy(xorriso->result_line,text,sizeof(xorriso->result_line)-1); xorriso->result_line[sizeof(xorriso->result_line)-1]= 0; ret= Xorriso_result(xorriso,0); return(ret); } /* @param flag bit0-7= purpose 0= ERRFILE 1= mark line (only to be put out if enabled) */ int Xorriso_process_errfile(struct XorrisO *xorriso, int error_code, char msg_text[], int os_errno, int flag) { char ttx[41]; int purpose; if(strlen(msg_text)>SfileadrL) return(-1); purpose= flag&255; if(purpose==1 && !(xorriso->errfile_mode&1)) return(2); if(xorriso->errfile_fp!=NULL) { if(purpose==1) fprintf(xorriso->errfile_fp, "----------------- %s %s\n", msg_text, Ftimetxt(time(0), ttx, 1)); else fprintf(xorriso->errfile_fp, "%s\n", msg_text); fflush(xorriso->errfile_fp); return(1); } if(xorriso->errfile_log[0]==0) return(1); if(strcmp(xorriso->errfile_log, "-")==0 || strcmp(xorriso->errfile_log, "-R")==0) { if(purpose==1) sprintf(xorriso->result_line, "----------------- %s %s\n", msg_text, Ftimetxt(time(0), ttx, 1)); else sprintf(xorriso->result_line, "%s\n", msg_text); Xorriso_result(xorriso, 1); return(1); } if(strcmp(xorriso->errfile_log, "-I") == 0 && xorriso->info_text != msg_text) { /* (Beware of stepping on own foot) */ if(purpose==1) sprintf(xorriso->info_text, "ERRFILE_MARK=%s %s\n", msg_text, Ftimetxt(time(0), ttx, 1)); else sprintf(xorriso->info_text, "ERRFILE=%s\n", msg_text); Xorriso_info(xorriso, 0); return(1); } return(2); } #ifdef Xorriso_fetch_with_msg_queueS /* Important: This function must stay thread-safe with all use of xorriso. */ #else /* Note: It is ok to submit xorriso->info_text as msg_text here. */ #endif /* flag: bit0= for Xorriso_info() : use pager (as with result) bit1= for Xorriso_info() : permission to suppress output bit2..5= name prefix 0="xorriso" 1="libisofs" 2="libburn" 3="libisoburn" else: "" bit6= append carriage return rather than line feed (if not os_errno) bit7= perform Xorriso_process_msg_queues() first bit8= do not prepend name prefix and severity */ int Xorriso_msgs_submit(struct XorrisO *xorriso, int error_code, char msg_text[], int os_errno, char severity[], int flag) { int ret, lt, li, sev, i; char *sev_text= "FATAL", prefix[80], *text= NULL; static char pfx_list[20][16]= { "xorriso : ", "libisofs: ", "libburn : ", "libisoburn: ", "", "", "", "", "", "", "", "", "", "", "", "" }; if(flag&128) Xorriso_process_msg_queues(xorriso, 0); if(strcmp(severity, "ERRFILE")==0) Xorriso_process_errfile(xorriso, error_code, msg_text, os_errno, 0); /* Set problem status */ ret= Xorriso__text_to_sev(severity, &sev, 0); if(ret<=0) Xorriso__text_to_sev(sev_text, &sev, 0); else sev_text= severity; if(xorriso->problem_status<sev) Xorriso_set_problem_status(xorriso, sev_text, 0); /* Report problem event */ if(sev<xorriso->report_about_severity && sev<xorriso->abort_on_severity) {ret= 2; goto ex;} lt= strlen(msg_text); if(!(flag & 256)) { sprintf(prefix,"%s%s : ", pfx_list[(flag>>2)&15], sev_text); li= strlen(prefix); } else { prefix[0]= 0; li= 0; } if(lt > ((int) sizeof(xorriso->info_text)) - li - 2) lt= sizeof(xorriso->info_text)-li-2; #ifdef Xorriso_fetch_with_msg_queueS Xorriso_alloc_meM(text, char, sizeof(xorriso->info_text)); #else /* Xorriso_fetch_with_msg_queueS */ text= xorriso->info_text; #endif /* ! Xorriso_fetch_with_msg_queueS */ if(msg_text == text) { if(li > 0) { for(i= lt; i>=0; i--) msg_text[i+li]= msg_text[i]; for(i=0; i<li; i++) msg_text[i]= prefix[i]; } } else { if(li > 0) strcpy(text, prefix); strncpy(text + li, msg_text, lt); } if((flag&64) && os_errno<=0) text[li+lt]= '\r'; else text[li+lt]= '\n'; text[li+lt+1]= 0; if(os_errno>0) sprintf(text + strlen(text) - 1, " : %s\n", strerror(os_errno)); #ifdef Xorriso_fetch_with_msg_queueS Xorriso_write_to_channel(xorriso, text, 2, 0); #else /* Xorriso_fetch_with_msg_queueS */ Xorriso_info(xorriso,4|(flag&3)); #endif /* ! Xorriso_fetch_with_msg_queueS */ ex:; #ifdef Xorriso_fetch_with_msg_queueS Xorriso_free_meM(text); #endif /* ! Xorriso_fetch_with_msg_queueS */ return(ret); } /* To be used with isoburn_set_msgs_submit() */ int Xorriso_msgs_submit_void(void *xorriso, int error_code, char msg_text[], int os_errno, char severity[], int flag) { int ret; ret= Xorriso_msgs_submit((struct XorrisO *) xorriso, error_code, msg_text, os_errno, severity, flag); return(ret); } /** @return -1= abort , 0= no , 1= yes */ int Xorriso_reassure(struct XorrisO *xorriso, char *cmd, char *which_will, int flag) { int ret; if(!xorriso->do_reassure) return(1); sprintf(xorriso->info_text, "Really perform %s which will %s ? (y/n)\n", cmd, which_will); Xorriso_info(xorriso, 4); do { ret= Xorriso_request_confirmation(xorriso, 2|4|16); } while(ret==3); if(ret==6 || ret==4) { sprintf(xorriso->info_text, "%s confirmed", cmd); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(1); } if(ret==2) { sprintf(xorriso->info_text, "%s aborted", cmd); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(-1); } sprintf(xorriso->info_text, "%s revoked", cmd); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(0); } int Xorriso_write_session_log(struct XorrisO *xorriso, int flag) { FILE *fp= NULL; char *sfe= NULL, timetext[40], *rpt, *wpt; int ret; if(xorriso->session_logfile[0]==0) {ret= 2; goto ex;} Xorriso_alloc_meM(sfe, char, 5 * SfileadrL); fp= fopen(xorriso->session_logfile, "a"); if(fp==0) { sprintf(xorriso->info_text, "-session_log: Cannot open file %s", Text_shellsafe(xorriso->session_logfile, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } wpt= sfe; for(rpt= xorriso->volid; *rpt!=0; rpt++) { if(*rpt=='\n') { *(wpt++)= '\\'; *(wpt++)= 'n'; } else *(wpt++)= *rpt; } *wpt= 0; fprintf(fp, "%s %.f %.f %s\n", Ftimetxt(time(0), timetext, 2), (double) xorriso->session_lba, (double) xorriso->session_blocks, sfe); fclose(fp); ret= 1; ex:; Xorriso_free_meM(sfe); return(ret); } int Xorriso_status_filter(struct XorrisO *xorriso, char *filter, char *line, int flag) { if(filter!=NULL) if(filter[0]=='-') if(strncmp(filter, line, strlen(filter))!=0) return(0); return(1); } int Xorriso_status_result(struct XorrisO *xorriso, char *filter, FILE *fp, int flag) /* bit1= do only report to fp */ { int ret; ret= Xorriso_status_filter(xorriso, filter, xorriso->result_line, 0); if(ret <= 0) return(2); if(!(flag&2)) Xorriso_result(xorriso,0); if(fp!=NULL) { ret= fwrite(xorriso->result_line,strlen(xorriso->result_line),1,fp); if(ret<=0) return(ret); } return(1); } /* bit0= do only report non-default settings bit1= do only report to fp */ int Xorriso_boot_status_sysarea(struct XorrisO *xorriso, char *filter, FILE *fp, int flag) { char *line, *form= "any", *spec= "system_area="; int sa_type; line= xorriso->result_line; sa_type= (xorriso->system_area_options & 0xfc) >> 2; if(sa_type != 0) return(2); if (xorriso->system_area_disk_path[0] == 0 && (flag & 1)) return(2); if(xorriso->system_area_options & 1) { form= "grub"; if(xorriso->system_area_options & (1 << 14)) spec= "grub2_mbr="; } else if(xorriso->system_area_options & 2) { form= "isolinux"; } if(xorriso->system_area_options & (1 << 14)) { form= "grub"; spec= "grub2_mbr="; } sprintf(line, "-boot_image %s %s", form, spec); Text_shellsafe(xorriso->system_area_disk_path, line, 1); strcat(line, "\n"); Xorriso_status_result(xorriso, filter, fp, flag & 2); return(1); } static char *Xorriso__speedname(int speed) { static char name[64]; if(speed > 0) { sprintf(name, "%dkB/s", speed); return(name); } else if(speed == 0) { return("max"); } else if(speed == -1) { return("min"); } else if(speed == -2) { return("none"); } sprintf(name, "%d", speed); return(name); } int Xorriso_status(struct XorrisO *xorriso, char *filter, FILE *fp, int flag) /* bit0= do only report non-default settings bit1= do only report to fp bit2= report current -resume status even if bit0 is set, but only if valid bit3= report readline history bit4= report -resume options indirectly as -options_from_file:${resume_state_file}_pos */ { int is_default, no_defaults, i, ret, adr_mode, do_single, behavior; int show_indev= 1, show_outdev= 1, show_dev= 0; int do_drive_access, did_drive_access; int part_table_implicit= 0; char *line, *sfe= NULL, mode[80], *form, *treatment; char *in_pt, *out_pt, *nl_charset, *local_charset, *mode_pt; char *dev_filter= NULL, *xorriso_id= NULL, *lfa_text= NULL; static char channel_prefixes[4][4]= {".","R","I","M"}; static char load_names[][20]= {"auto", "session", "track", "lba", "volid"}; static int max_load_mode= 4; static char scsi_family[8][8]= {"default", "sr", "scd", "st", "sg", "", "", ""}; struct Xorriso_lsT *paths, *leafs, *s, *plst, *vlst; Xorriso_alloc_meM(sfe, char, 5 * SfileadrL + 80); Xorriso_alloc_meM(xorriso_id, char, 129); no_defaults= flag&1; line= xorriso->result_line; if(xorriso->no_rc) { sprintf(line,"-no_rc\n"); Xorriso_status_result(xorriso,filter,fp,flag&2); } is_default= strcmp(xorriso->list_delimiter, "--") == 0; sprintf(line,"-list_delimiter %s\n", xorriso->list_delimiter); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= 0; if(xorriso->dialog == 2) sprintf(line,"-dialog on\n"); else if(xorriso->dialog == 1) sprintf(line,"-dialog single_line\n"); else { sprintf(line,"-dialog off\n"); is_default= 1; } if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->result_page_length==0 && xorriso->result_page_width==80); sprintf(line,"-page %d %d\n", (xorriso->result_page_length>=0?xorriso->result_page_length :-xorriso->result_page_length), xorriso->result_page_width); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->use_stdin==0); sprintf(line,"-use_readline %s\n", (xorriso->use_stdin?"off":"on")); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->sh_style_result == 0); sprintf(line, "-sh_style_result %s\n", xorriso->sh_style_result ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->bsl_interpretation == 0); strcpy(line, "-backslash_codes "); if(xorriso->bsl_interpretation == 0) strcat(line, "off"); else if(xorriso->bsl_interpretation == (3 | 16 | 32 | 64)) strcat(line, "on"); else { if((xorriso->bsl_interpretation & 3) == 1) strcat(line, "in_double_quotes"); else if((xorriso->bsl_interpretation & 3) == 2) strcat(line, "in_quotes"); else if((xorriso->bsl_interpretation & 3) == 3) strcat(line, "with_quoted_input"); if(xorriso->bsl_interpretation & 16) { if(strlen(line) > 17) strcat(line, ":"); strcat(line, "with_program_arguments"); } if((xorriso->bsl_interpretation & (32 | 64)) == (32 | 64)) { if(strlen(line) > 17) strcat(line, ":"); strcat(line, "encode_output"); } else { if(xorriso->bsl_interpretation & 32) { if(strlen(line) > 17) strcat(line, ":"); strcat(line, "encode_results"); } if(xorriso->bsl_interpretation & 64) { if(strlen(line) > 17) strcat(line, ":"); strcat(line, "encode_infos"); } } } strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= !xorriso->packet_output; sprintf(line,"-pkt_output %s\n",(xorriso->packet_output?"on":"off")); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); for(i=0;i<4;i++) { is_default= (xorriso->logfile[i][0] == 0); sprintf(line,"-logfile %s %s\n", channel_prefixes[i],Text_shellsafe(xorriso->logfile[i],sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); } is_default= (xorriso->errfile_log[0]==0); sprintf(line,"-errfile_log %s\n",Text_shellsafe(xorriso->errfile_log,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); if(xorriso->check_media_default == NULL) { is_default= 1; sprintf(line, "-check_media_defaults reset=now %s\n", xorriso->list_delimiter); } else { ret= Xorriso_check_media_list_job(xorriso, xorriso->check_media_default, line, no_defaults); is_default= (ret == 2); strcat(line, "\n"); } if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); behavior= Xorriso__get_signal_behavior(0); is_default= (behavior == 1); treatment= "on"; if(behavior == 0) treatment= "off"; else if(behavior == 2) treatment= "sig_dfl"; else if(behavior == 3) treatment= "sig_ign"; sprintf(line,"-signal_handling %s\n", treatment); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->img_read_error_mode == 1); treatment= "best_effort"; if(xorriso->img_read_error_mode==1) treatment= "failure"; else if(xorriso->img_read_error_mode==2) treatment= "fatal"; sprintf(line,"-error_behavior image_loading %s\n", treatment); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->extract_error_mode == 1); treatment= "keep"; if(xorriso->extract_error_mode == 0) treatment= "best_effort"; else if(xorriso->extract_error_mode == 2) treatment= "delete"; sprintf(line,"-error_behavior file_extraction %s\n", treatment); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->mark_text[0]==0); sprintf(line,"-mark %s\n",Text_shellsafe(xorriso->mark_text,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->temp_mem_limit==16*1024*1024); if((xorriso->temp_mem_limit/1024/1024)*1024*1024==xorriso->temp_mem_limit) sprintf(line,"-temp_mem_limit %dm\n", xorriso->temp_mem_limit/1024/1024); else sprintf(line,"-temp_mem_limit %dk\n", xorriso->temp_mem_limit/1024); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); sprintf(line,"-prog %s\n",Text_shellsafe(xorriso->progname,sfe,0)); Xorriso_status_result(xorriso,filter,fp,flag&2); if(xorriso->ban_stdio_write) { sprintf(line,"-ban_stdio_write\n"); Xorriso_status_result(xorriso,filter,fp,flag&2); } is_default= ((xorriso->early_stdio_test & 14) == 0); sprintf(line, "-early_stdio_test %s\n", xorriso->early_stdio_test & 6 ? xorriso->early_stdio_test & 8 ? "appendable_wo" : "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= ((xorriso->cache_default & 3) == 3); sprintf(line, "-data_cache_size "); if(xorriso->cache_default & 1) sprintf(line + strlen(line), "default "); else sprintf(line + strlen(line), "%d ", xorriso->cache_num_tiles); if(xorriso->cache_default & 2) sprintf(line + strlen(line), "default\n"); else sprintf(line + strlen(line), "%d\n", xorriso->cache_tile_blocks); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->allow_restore==0 && xorriso->do_concat_split==1 && xorriso->do_auto_chmod==0 && xorriso->drives_exclusive == 1); mode_pt= "off"; if(xorriso->allow_restore == -2) mode_pt= "blocked"; else if(xorriso->allow_restore == -1) mode_pt= "banned"; else if(xorriso->allow_restore == 1) mode_pt= "on"; else if(xorriso->allow_restore == 2) mode_pt= "device_files"; if(xorriso->allow_restore == -1) sprintf(line,"-osirrox %s\n", mode_pt); else sprintf(line,"-osirrox %s:%s:%s:%s:%s\n", mode_pt, xorriso->do_concat_split ? "concat_split_on" : "concat_split_off", xorriso->do_auto_chmod ? "auto_chmod_on" : "auto_chmod_off", xorriso->do_restore_sort_lba ? "sort_lba_on" : "sort_lba_off", xorriso->drives_exclusive ? "o_excl_on" : "o_excl_off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default = ((xorriso->do_strict_acl & 1) == 0 && (xorriso->do_md5 & (64 | 128)) == 64 && xorriso->sparse_min_gap == 0); sprintf(line,"-osirrox %s:%s:sparse=", (xorriso->do_strict_acl & 1) ? "strict_acl_on" : "strict_acl_off", (xorriso->do_md5 & 64) ? (xorriso->do_md5 & 128) ? "check_md5_force" : "check_md5_on" : "check_md5_off"); if(xorriso->sparse_min_gap <= 0) strcat(line, "off"); else Sfile_off_t_text(line + strlen(line), xorriso->sparse_min_gap, 0); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= (xorriso->toc_info_type == 1); sprintf(line, "-toc_info_type %s%s\n", xorriso->toc_info_type == 3 ? "creation_time" : xorriso->toc_info_type == 4 ? "modification_time" : "volid", xorriso->toc_time_form == 0 ? "_gmt" : ""); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= (xorriso->mount_opts_flag == 0); sprintf(line,"-mount_opts %s\n", xorriso->mount_opts_flag & 1 ? "shared" : "exclusive"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); Xorriso_boot_image_status(xorriso, filter, fp, flag & 3); Xorriso_boot_status_sysarea(xorriso, filter, fp, flag & 3); is_default= (xorriso->partition_offset == 0); sprintf(line,"-boot_image any partition_offset=%lu\n", (unsigned long int) xorriso->partition_offset); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->partition_secs_per_head == 0); sprintf(line,"-boot_image any partition_sec_hd=%lu\n", (unsigned long int) xorriso->partition_secs_per_head); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->partition_heads_per_cyl == 0); sprintf(line,"-boot_image any partition_hd_cyl=%lu\n", (unsigned long int) xorriso->partition_heads_per_cyl); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); ret= (xorriso->system_area_options & 0x300) >> 8; is_default= (ret == 0); sprintf(line,"-boot_image any partition_cyl_align=%s\n", ret == 0 ? "auto" : ret == 1 ? "on" : ret == 3 ? "all" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); if((xorriso->system_area_disk_path[0] || !part_table_implicit) && (xorriso->partition_offset == 0 || (xorriso->system_area_options & 2))) { is_default= ((xorriso->system_area_options & 3) == 0); sprintf(line,"-boot_image %s partition_table=%s\n", xorriso->system_area_options & 2 ? "isolinux" : "grub", xorriso->system_area_options & 3 ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); } is_default= ((xorriso->system_area_options & (1 << 15)) == 0); sprintf(line, "-boot_image any mbr_force_bootable=%s\n", (xorriso->system_area_options & (1 << 15)) ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= ((xorriso->system_area_options & (1 << 16)) == 0); sprintf(line, "-boot_image any gpt_iso_bootable=%s\n", (xorriso->system_area_options & (1 << 16)) ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= ((xorriso->system_area_options & (1 << 17)) == 0); sprintf(line, "-boot_image any gpt_iso_not_ro=%s\n", (xorriso->system_area_options & (1 << 17)) ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->appended_as_gpt == 0 && xorriso->appended_as_apm == 0); if(is_default) { sprintf(line, "-boot_image any appended_part_as=mbr\n"); if(!no_defaults) Xorriso_status_result(xorriso,filter,fp,flag&2); } else { if(xorriso->appended_as_gpt) { sprintf(line, "-boot_image any appended_part_as=gpt\n"); Xorriso_status_result(xorriso,filter,fp,flag&2); } if(xorriso->appended_as_apm) { sprintf(line, "-boot_image any appended_part_as=apm\n"); Xorriso_status_result(xorriso,filter,fp,flag&2); } } is_default= (xorriso->part_like_isohybrid == 0); sprintf(line, "-boot_image any part_like_isohybrid=%s\n", xorriso->part_like_isohybrid ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->iso_mbr_part_type == -1); sprintf(line, "-boot_image any iso_mbr_part_type="); if(xorriso->iso_mbr_part_flag & 1) { Xorriso__format_guid(xorriso->iso_gpt_type_guid, line + strlen(line), 0); strcat(line, "\n"); } else if(xorriso->iso_mbr_part_type == -1) { sprintf(line + strlen(line), "default\n"); } else { sprintf(line + strlen(line), "0x%-2.2x\n", (unsigned int) xorriso->iso_mbr_part_type); } if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->gpt_guid_mode == 0); sprintf(line, "-boot_image any gpt_disk_guid=%s", xorriso->gpt_guid_mode == 0 ? "random" : xorriso->gpt_guid_mode == 2 ? "volume_date_uuid" : ""); if(xorriso->gpt_guid_mode == 1) for(i= 0; i < 16; i++) sprintf(line + strlen(line), "%-2.2x", (unsigned int) xorriso->gpt_guid[i]); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); ret= ((xorriso->system_area_options & 0x3cfc) == 0x400); is_default= (ret == 0); sprintf(line, "-boot_image any chrp_boot_part=%s\n", ret == 1 ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= (xorriso->prep_partition[0] == 0); sprintf(line,"-boot_image any prep_boot_part=%s\n", Text_shellsafe(xorriso->prep_partition, sfe, 0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= (xorriso->efi_boot_partition[0] == 0); sprintf(line,"-boot_image any efi_boot_part=%s\n", Text_shellsafe(xorriso->efi_boot_partition, sfe, 0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); #ifdef Xorriso_with_isohybriD if(strcmp(form, "isolinux") == 0) { static char modes[4][6]= {"off", "auto", "on", "force"}; is_default= (xorriso->boot_image_isohybrid == 1); sprintf(line,"-boot_image isolinux isohybrid=%s\n", modes[xorriso->boot_image_isohybrid & 3]); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); } #endif /* Xorriso_with_isohybriD */ is_default= 1; for(i= 0; i < 8; i++) if(xorriso->hfsp_serial_number[i]) is_default= 0; sprintf(line, "-boot_image any hfsplus_serial="); for(i= 0; i < 8; i++) sprintf(line + strlen(line), "%-2.2X", (unsigned int) xorriso->hfsp_serial_number[i]); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= (xorriso->hfsp_block_size == 0); sprintf(line, "-boot_image any hfsplus_block_size=%d\n", xorriso->hfsp_block_size); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= (xorriso->apm_block_size == 0); sprintf(line, "-boot_image any apm_block_size=%d\n", xorriso->apm_block_size); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); sprintf(line,"-cd "); if(filter != NULL) if(strncmp(filter, "-cdi", 4) == 0) sprintf(line,"-cdi "); sprintf(line + strlen(line),"%s\n", (xorriso->wdi[0] ? Text_shellsafe(xorriso->wdi,sfe,0) : "'/'")); Xorriso_status_result(xorriso,filter,fp,flag&2); sprintf(line,"-cdx %s\n", (xorriso->wdx[0] ? Text_shellsafe(xorriso->wdx,sfe,0) : "'/'")); Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->split_size==0); strcpy(line,"-split_size "); if(xorriso->split_size % (1024*1024) || xorriso->split_size==0) { Sfile_off_t_text(line+strlen(line), xorriso->split_size, 0); } else { Sfile_off_t_text(line+strlen(line), xorriso->split_size / (1024*1024), 0); strcat(line, "m"); } strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->add_plainly==0); sprintf(line,"-add_plainly %s\n", (xorriso->add_plainly == 1 ? "unknown" : xorriso->add_plainly == 2 ? "dashed" : xorriso->add_plainly == 3 ? "any" : "none")); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); ret= Exclusions_get_descrs(xorriso->disk_exclusions, &paths, &leafs, 0); if(ret>0) { for(; paths!=NULL; paths= paths->next) { sprintf(line, "-not_paths %s %s\n", Text_shellsafe(paths->text, sfe, 0), xorriso->list_delimiter); Xorriso_status_result(xorriso,filter,fp,flag&2); } for(; leafs!=NULL; leafs= leafs->next) { sprintf(line,"-not_leaf %s\n", Text_shellsafe(leafs->text, sfe, 0)); Xorriso_status_result(xorriso,filter,fp,flag&2); } } is_default= (xorriso->file_name_limit == 255); sprintf(line, "-file_name_limit %d\n", xorriso->file_name_limit); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->file_size_limit == Xorriso_default_file_size_limiT); if(xorriso->file_size_limit <= 0) sprintf(line, "-file_size_limit off %s\n", xorriso->list_delimiter); else sprintf(line, "-file_size_limit %.f %s\n", (double) xorriso->file_size_limit, xorriso->list_delimiter); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->disk_excl_mode==1); sprintf(line, "-not_mgt %s:%s:%s:%s\n", (xorriso->disk_excl_mode&1 ? "on" : "off"), (xorriso->disk_excl_mode&2 ? "param_on" : "param_off"), (xorriso->disk_excl_mode&4 ? "subtree_on" : "subtree_off"), (xorriso->disk_excl_mode&8 ? "ignore_on" : "ignore_off")); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->do_override_now_time == 0); if(xorriso->do_override_now_time) sprintf(line, "-iso_nowtime =%.f\n", (double) xorriso->now_time_override); else sprintf(line, "-iso_nowtime dynamic\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= (xorriso->do_iso_rr_pattern==1); sprintf(line,"-iso_rr_pattern %s\n", (xorriso->do_iso_rr_pattern == 1 ? "on" : (xorriso->do_iso_rr_pattern == 2 ? "ls" : "off"))); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->do_disk_pattern==2); sprintf(line,"-disk_pattern %s\n", (xorriso->do_disk_pattern == 1 ? "on" : (xorriso->do_disk_pattern == 2 ? "ls" : "off"))); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= xorriso->volid_default; sprintf(line,"-volid %s\n",Text_shellsafe(xorriso->volid,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); if(is_default && xorriso->loaded_volid[0] && strcmp(xorriso->loaded_volid, xorriso->volid)!=0 && !no_defaults) { sprintf(line,"# loaded image effective -volid %s\n", Text_shellsafe(xorriso->loaded_volid,sfe,0)); Xorriso_status_result(xorriso,filter,fp,flag&2); } Xorriso_preparer_string(xorriso, xorriso_id, 0); is_default= (strcmp(xorriso->preparer_id, xorriso_id) == 0); sprintf(line,"-preparer_id %s\n",Text_shellsafe(xorriso->preparer_id,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->publisher[0]==0); sprintf(line,"-publisher %s\n",Text_shellsafe(xorriso->publisher,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->application_id[0]==0); sprintf(line,"-application_id %s\n", Text_shellsafe(xorriso->application_id,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->system_id[0]==0); sprintf(line,"-system_id %s\n", Text_shellsafe(xorriso->system_id,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->volset_id[0]==0); sprintf(line,"-volset_id %s\n", Text_shellsafe(xorriso->volset_id,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->vol_creation_time == 0); sprintf(line,"-volume_date c %s\n", is_default ? "default" : Ftimetxt(xorriso->vol_creation_time, sfe, 2)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->vol_modification_time == 0); sprintf(line,"-volume_date m %s\n", xorriso->vol_uuid[0] ? "overridden" : is_default ? "default" : Ftimetxt(xorriso->vol_modification_time, sfe, 2)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->vol_expiration_time == 0); sprintf(line,"-volume_date x %s\n", is_default ? "default" : Ftimetxt(xorriso->vol_expiration_time, sfe, 2)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->vol_effective_time == 0); sprintf(line,"-volume_date f %s\n", is_default ? "default" : Ftimetxt(xorriso->vol_effective_time, sfe, 2)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->vol_uuid[0] == 0); sprintf(line,"-volume_date uuid %s\n", Text_shellsafe(xorriso->vol_uuid,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->all_file_dates[0] == 0); sprintf(line,"-volume_date all_file_dates %s\n", Text_shellsafe(xorriso->all_file_dates,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->copyright_file[0] == 0); sprintf(line,"-copyright_file %s\n", Text_shellsafe(xorriso->copyright_file,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->biblio_file[0]==0); sprintf(line,"-biblio_file %s\n",Text_shellsafe(xorriso->biblio_file,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->abstract_file[0]==0); sprintf(line,"-abstract_file %s\n", Text_shellsafe(xorriso->abstract_file,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (strcmp(xorriso->application_use, " ") == 0); sprintf(line, "-application_use %s\n", Text_shellsafe(xorriso->application_use, sfe, 0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->do_joliet==0); sprintf(line,"-joliet %s\n", (xorriso->do_joliet == 1 ? "on" : "off")); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->do_rockridge == 1); sprintf(line, "-rockridge %s\n", (xorriso->do_rockridge == 1 ? "on" : "off")); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->do_hfsplus == 0); sprintf(line,"-hfsplus %s\n", (xorriso->do_hfsplus == 1 ? "on" : "off")); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); Xorriso_lst_get_last(xorriso->jigdo_params, &plst, 0); Xorriso_lst_get_last(xorriso->jigdo_values, &vlst, 0); if(plst == NULL || vlst == NULL) { is_default= 1; sprintf(line,"-jigdo clear 'all'\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); } while(plst != NULL && vlst != NULL) { sprintf(line,"-jigdo %s %s\n", Xorriso_lst_get_text(plst, 0), Text_shellsafe(Xorriso_lst_get_text(vlst, 0), sfe, 0)); Xorriso_status_result(xorriso, filter, fp, flag & 2); plst= Xorriso_lst_get_prev(plst, 0); vlst= Xorriso_lst_get_prev(vlst, 0); } if(xorriso->do_global_uid) { sprintf(line,"-uid %lu\n", (unsigned long) xorriso->global_uid); Xorriso_status_result(xorriso,filter,fp,flag&2); } if(xorriso->do_global_gid) { sprintf(line,"-gid %lu\n", (unsigned long) xorriso->global_gid); Xorriso_status_result(xorriso,filter,fp,flag&2); } Xorriso_status_extf(xorriso, filter, fp, flag & 2); Xorriso_status_zisofs(xorriso, filter, fp, flag & 3); is_default= !xorriso->allow_graft_points; sprintf(line,"-pathspecs %s\n", (xorriso->allow_graft_points & 2) ? "as_mkisofs" : xorriso->allow_graft_points ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->do_follow_pattern && (!xorriso->do_follow_param) && xorriso->do_follow_mount && (!xorriso->do_follow_links) && xorriso->follow_link_limit==100 && (!xorriso->do_follow_concat)); mode[0]= 0; if(xorriso->do_follow_pattern && !(xorriso->do_follow_links && xorriso->do_follow_mount)) strcat(mode,":pattern"); if(xorriso->do_follow_param && !(xorriso->do_follow_links)) strcat(mode,":param"); if(xorriso->do_follow_links) strcat(mode,":link"); if(xorriso->do_follow_concat) strcat(mode,":concat"); if(xorriso->do_follow_mount) strcat(mode,":mount"); if(mode[0]==0) strcpy(mode, ":off"); sprintf(mode+strlen(mode), ":limit=%d", xorriso->follow_link_limit); sprintf(line,"-follow %s\n", mode+1); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->do_overwrite==2); sprintf(line,"-overwrite %s\n",(xorriso->do_overwrite == 1 ? "on" : (xorriso->do_overwrite == 2 ? "nondir" : "off"))); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= !xorriso->do_reassure; sprintf(line,"-reassure %s\n",(xorriso->do_reassure == 1 ? "on" : (xorriso->do_reassure == 2 ? "tree" : "off"))); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->read_speed == -2); sprintf(line,"-read_speed %s\n", Xorriso__speedname(xorriso->read_speed)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->read_speed_force <= 0); sprintf(line,"-read_speed soft_force:%s\n", xorriso->read_speed_force <= 0 ? "0" : Xorriso__speedname(xorriso->read_speed_force)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->read_speed_corr == 250000); sprintf(line,"-read_speed soft_corr:%d\n", xorriso->read_speed_corr); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= !(xorriso->auto_close || xorriso->do_close); sprintf(line,"-close %s\n",xorriso->auto_close ? "as_needed" : xorriso->do_close ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->do_tao == 0); sprintf(line,"-write_type %s\n", xorriso->do_tao == 0 ? "auto" : xorriso->do_tao > 0 ? "tao" : "sao/dao"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= !xorriso->do_dummy; sprintf(line,"-dummy %s\n",(xorriso->do_dummy ? "on" : "off")); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->write_speed==0); sprintf(line,"-speed %s\n", Xorriso__speedname(xorriso->write_speed)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->do_stream_recording==0); strcpy(mode, "off"); if(xorriso->do_stream_recording == 1) strcpy(mode, "full"); if(xorriso->do_stream_recording == 2) strcpy(mode, "data"); else if(xorriso->do_stream_recording == 32) strcpy(mode, "on"); else if(xorriso->do_stream_recording >= 16) sprintf(mode, "%ds", xorriso->do_stream_recording); sprintf(line,"-stream_recording %s\n", mode); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->modesty_on_drive == 0 && xorriso->min_buffer_usec == 5000 && xorriso->max_buffer_usec == 25000 && xorriso->buffer_timeout_sec == 120 && xorriso->min_buffer_percent == 90 && xorriso->max_buffer_percent == 95); if(xorriso->modesty_on_drive == 0) strcpy(mode, "off"); else if(xorriso->modesty_on_drive == 1) strcpy(mode, "on"); else sprintf(mode, "%d", xorriso->modesty_on_drive); sprintf(mode + strlen(mode), ":min_percent=%d", xorriso->min_buffer_percent); sprintf(mode + strlen(mode), ":max_percent=%d", xorriso->max_buffer_percent); if(xorriso->buffer_timeout_sec >= 0) sprintf(mode + strlen(mode), ":timeout_sec=%d", xorriso->buffer_timeout_sec); if(xorriso->min_buffer_usec >= 0) sprintf(mode + strlen(mode), ":min_usec=%d", xorriso->min_buffer_usec); if(xorriso->max_buffer_usec >= 0) sprintf(mode + strlen(mode), ":max_usec=%d", xorriso->max_buffer_usec); sprintf(line,"-modesty_on_drive %s\n", mode); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->dvd_obs == 0); strcpy(mode, "default"); if(xorriso->dvd_obs == 32768 || xorriso->dvd_obs == 65536) sprintf(mode, "%dk", xorriso->dvd_obs / 1024); sprintf(line,"-dvd_obs %s\n", mode); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->do_obs_pad == 0); strcpy(mode, "no_obs_pad"); if(xorriso->do_obs_pad) strcpy(mode, "obs_pad"); sprintf(line,"-dvd_obs %s\n", mode); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= (xorriso->bdr_obs_exempt == 0); strcpy(mode, "no_bdr_obs_exempt"); if(xorriso->bdr_obs_exempt) strcpy(mode, "bdr_obs_exempt"); sprintf(line,"-dvd_obs %s\n", mode); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= (xorriso->use_immed_bit == 0); strcpy(line, "-use_immed_bit "); if(xorriso->use_immed_bit == 0) { strcat(line, "default"); if(xorriso->use_immed_bit_default > 0) strcat(line, "/on"); else if(xorriso->use_immed_bit_default < 0) strcat(line, "/off"); strcat(line, "\n"); } else if(xorriso->use_immed_bit > 0) { strcat(line, "on\n"); } else if(xorriso->use_immed_bit < 0) { strcat(line, "off\n"); } if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->stdio_sync == 0); strcpy(line, "-stdio_sync "); if(xorriso->stdio_sync == -1) strcat(line, "off"); else if(xorriso->stdio_sync == 0) strcat(line, "on"); else if(xorriso->stdio_sync == 1) strcat(line, "end"); else if(xorriso->stdio_sync % 512) { Sfile_off_t_text(line+strlen(line), (off_t) (xorriso->stdio_sync * 2048), 0); } else { Sfile_off_t_text(line+strlen(line), (off_t) (xorriso->stdio_sync / 512), 0); strcat(line, "m"); } strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->fs==4*512); if((xorriso->fs/512)*512==xorriso->fs) sprintf(line,"-fs %dm\n", xorriso->fs/512); else sprintf(line,"-fs %dk\n", xorriso->fs*2); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->padding==300*1024); sprintf(line,"-padding %dk\n", xorriso->padding/1024); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->do_padding_by_libisofs == 0); sprintf(line,"-padding %s\n", xorriso->do_padding_by_libisofs ? "included" : "appended"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (strcmp(xorriso->report_about_text,"UPDATE")==0); sprintf(line,"-report_about %s\n",xorriso->report_about_text); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->scsi_log == 0); sprintf(line,"-scsi_log %s\n", xorriso->scsi_log ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->session_logfile[0]==0); sprintf(line,"-session_log %s\n", Text_shellsafe(xorriso->session_logfile,sfe,0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->pacifier_style==0); sprintf(line,"-pacifier '%s'\n", xorriso->pacifier_style==1 ? "mkisofs" : xorriso->pacifier_style==2 ? "cdrecord" : "xorriso"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->pacifier_interval == 1.0); sprintf(line,"-pacifier interval=%f\n", xorriso->pacifier_interval); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (strcmp(xorriso->return_with_text,"SORRY")==0 && xorriso->return_with_value==32); sprintf(line,"-return_with %s %d\n", xorriso->return_with_text, xorriso->return_with_value); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= 0; sprintf(line,"-abort_on %s\n",xorriso->abort_on_text); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); if(xorriso->status_history_max!=Xorriso_status_history_maX || !no_defaults) { sprintf(line,"-status_history_max %d\n",xorriso->status_history_max); Xorriso_status_result(xorriso,filter,fp,flag&2); } #ifdef Xorriso_with_line_editoR if((flag & 8) && xorriso->status_history_max > 0 && !xorriso->use_stdin) Xorriso_status_history(xorriso, filter, fp, flag & 2); #endif /* Xorriso_with_line_editoR */ is_default= (xorriso->toc_emulation_flag == 0); sprintf(line,"-rom_toc_scan %s:%s:%s\n", xorriso->toc_emulation_flag & 4 ? "force" : xorriso->toc_emulation_flag & 1 ? "on" : "off", xorriso->toc_emulation_flag & 2 ? "emul_off" : "emul_on", xorriso->toc_emulation_flag & 8 ? "emul_wide" : "emul_narrow"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= (xorriso->displacement == 0); sprintf(line, "-displacement %s%lu\n", xorriso->displacement_sign < 0 ? "-" : "", (unsigned long) xorriso->displacement); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); adr_mode= xorriso->image_start_mode & 0xffff; if(adr_mode>=0 && adr_mode<=max_load_mode) { is_default= (adr_mode==0); sprintf(line,"-load %s ", load_names[adr_mode]); if(adr_mode==0) sprintf(line+strlen(line),"''\n"); else if(adr_mode>=1 && adr_mode<=3) sprintf(line+strlen(line),"%s\n", xorriso->image_start_value); else sprintf(line+strlen(line),"%s\n", Text_shellsafe(xorriso->image_start_value, sfe, 0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); } is_default= (xorriso->read_fs == 0); sprintf(line, "-read_fs %s\n", xorriso->read_fs == 0 ? "any" : xorriso->read_fs == 1 ? "norock" : xorriso->read_fs == 2 ? "nojoliet" : "ecma119"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= (xorriso->do_calm_drive & 1); sprintf(line,"-calm_drive %s\n", xorriso->do_calm_drive & 1 ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->grow_blindly_msc2<0); sprintf(sfe, "%.f", (double) xorriso->grow_blindly_msc2); sprintf(line,"-grow_blindly %s\n", xorriso->grow_blindly_msc2 < 0 ? "off" : sfe); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); Xorriso_get_local_charset(xorriso, &local_charset, 0); nl_charset= nl_langinfo(CODESET); is_default= (strcmp(local_charset, nl_charset) == 0); sprintf(line, "-local_charset %s\n", Text_shellsafe(local_charset, sfe, 0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= (xorriso->out_charset == NULL && xorriso->in_charset == NULL); in_pt= ""; if(xorriso->in_charset != NULL) in_pt= xorriso->in_charset; out_pt= ""; if(xorriso->out_charset != NULL) out_pt= xorriso->out_charset; do_single= 0; ret= Xorriso_status_filter(xorriso, filter, "-in_charset", 0); if(ret <= 0) ret= Xorriso_status_filter(xorriso, filter, "-out_charset", 0); if(ret > 0) do_single= 1; if(strcmp(in_pt, out_pt) == 0 && !do_single) { sprintf(line, "-charset %s\n", Text_shellsafe(in_pt, sfe, 0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); } else { sprintf(line, "-in_charset %s\n", Text_shellsafe(in_pt, sfe, 0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); sprintf(line, "-out_charset %s\n", Text_shellsafe(out_pt, sfe, 0)); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); } is_default= ((xorriso->do_aaip & (256 | 512)) == 0); sprintf(line,"-auto_charset %s\n", (xorriso->do_aaip & 256 ? "on" : "off")); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= ((xorriso->ino_behavior & 31) == 7); switch (xorriso->ino_behavior & 15) { case 0: form= "on"; break; case 8: form= "without_update"; break; default: form= "off"; } sprintf(line,"-hardlinks %s:%s:%s\n", form, xorriso->ino_behavior & 32 ? "no_lsl_count" : "lsl_count", xorriso->ino_behavior & 16 ? "cheap_sorted_extract" : "normal_extract"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= ((xorriso->do_aaip & (1 | 4)) == 0); sprintf(line,"-acl %s\n", (xorriso->do_aaip & 1 ? "on" : "off")); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= ((xorriso->do_aaip & (2 | 8)) == 0); sprintf(line,"-xattr %s\n", (xorriso->do_aaip & 4 ? xorriso->do_aaip & 1024 ? "any" : "user" : "off")); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->lfa_flags_setting == xorriso->lfa_flags_default) && (xorriso->lfa_restore_mask == ~((uint64_t) 0)) && strcmp(xorriso->lfa_restore_err_sev, "sorry") == 0; strcpy(line, "-lfa_flags "); if(xorriso->lfa_flags_setting & 1) strcat(line, "on"); else strcat(line, "off"); if(xorriso->lfa_flags_setting & 4) strcat(line, ":auto_on"); if(xorriso->lfa_flags_setting & (1 << 11)) strcat(line, ":read"); else strcat(line, ":no_read"); if(xorriso->lfa_flags_setting & (1 << 12)) strcat(line, ":restore"); else strcat(line, ":no_restore"); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); strcpy(line, "-lfa_flags "); if(xorriso->lfa_flags_setting & (1 << 15)) strcat(line, "import_only_settable"); else strcat(line, "import_non_settable"); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); strcpy(line, "-lfa_flags "); if(xorriso->lfa_flags_setting & (1 << 13)) strcat(line, "no_restore_su"); else strcat(line, "restore_su"); if(xorriso->lfa_flags_setting & 2) strcat(line, ":restore_su_auto"); if(xorriso->lfa_flags_setting & (1 << 14)) strcat(line, ":restore_only_known"); else strcat(line, ":restore_unknown"); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); sprintf(line, "-lfa_flags restore_error=%s", xorriso->lfa_restore_err_sev); if(xorriso->lfa_flags_setting & (1 << 16)) strcat(line, ":restore_single"); else strcat(line, ":no_restore_single"); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); strcpy(line, "-lfa_flags "); if(xorriso->lfa_restore_mask == ~((uint64_t) 0)) { strcat(line, "restore_mask="); } else { ret= Xorriso_encode_lfa_flags(xorriso, xorriso->lfa_restore_mask, &lfa_text, 0); if(ret > 0) { strcat(line, "restore_mask="); if(lfa_text[0] != 0) strcat(line, lfa_text); else strcat(line, "-"); } if(lfa_text != NULL) free(lfa_text); } strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= ((xorriso->do_aaip & (16 | 32 | 64)) == 0); sprintf(line,"-disk_dev_ino %s\n", (xorriso->do_aaip & 16 ? (xorriso->do_aaip & 128 ? "ino_only" : "on" ) : "off")); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= ((xorriso->do_md5 & 31) == 0); sprintf(line, "-md5 "); if(xorriso->do_md5 & 1) { if((xorriso->do_md5 & 8) == 8) { strcat(line, "all"); } else { strcat(line, "on"); if(xorriso->do_md5 & 8) strcat(line, ":stability_check_on"); } if(xorriso->do_md5 & 32) strcat(line, ":load_check_off"); strcat(line, "\n"); } else strcat(line, "off\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->ecma119_map == 1); sprintf(line, "-ecma119_map "); if(xorriso->ecma119_map == 0) strcat(line, "unmapped\n"); else if(xorriso->ecma119_map == 2) strcat(line, "uppercase\n"); else if(xorriso->ecma119_map == 3) strcat(line, "lowercase\n"); else strcat(line, "stripped\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->joliet_map == 1); sprintf(line, "-joliet_map "); if(xorriso->joliet_map == 0) strcat(line, "unmapped\n"); else strcat(line, "stripped\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->scdbackup_tag_name[0] == 0 && xorriso->scdbackup_tag_listname[0] == 0); sprintf(line, "-scdbackup_tag "); Text_shellsafe(xorriso->scdbackup_tag_listname, line, 1); strcat(line, " "); Text_shellsafe(xorriso->scdbackup_tag_name, line, 1); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (Xorriso_get_relax_text(xorriso, sfe, 0) == 2); sprintf(line,"-compliance %s\n", sfe); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->rr_reloc_dir[0] == 0); sprintf(line, "-rr_reloc_dir "); Text_shellsafe(xorriso->rr_reloc_dir, line, 1); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->assert_volid[0] == 0); sprintf(line, "-assert_volid "); Text_shellsafe(xorriso->assert_volid, line, 1); strcat(line, " "); Text_shellsafe(xorriso->assert_volid_sev, line, 1); strcat(line, "\n"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); is_default= (xorriso->genisoimage_completion == 0); sprintf(line, "-genisoimage_completion %s\n", xorriso->genisoimage_completion ? "on" : "off"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= 1; if(xorriso->drive_blacklist != NULL || xorriso->drive_whitelist != NULL || xorriso->drive_greylist == NULL) is_default= 0; if(xorriso->drive_greylist != NULL) { if(strcmp(Xorriso_get_pattern(xorriso, xorriso->drive_greylist, 0, 0), "/dev") != 0) is_default= 0; if(Xorriso_get_pattern(xorriso, xorriso->drive_greylist, 1, 0) != NULL) is_default= 0; } if(!(is_default && no_defaults)) { for(s= xorriso->drive_blacklist; s != NULL; s= Xorriso_lst_get_next(s, 0)) { sprintf(line, "-drive_class 'banned' %s\n", Text_shellsafe(Xorriso_lst_get_text(s, 0), sfe, 0)); Xorriso_status_result(xorriso,filter,fp,flag&2); } for(s= xorriso->drive_greylist; s != NULL; s= Xorriso_lst_get_next(s, 0)) { sprintf(line, "-drive_class 'caution' %s\n", Text_shellsafe(Xorriso_lst_get_text(s, 0), sfe, 0)); Xorriso_status_result(xorriso,filter,fp,flag&2); } for(s= xorriso->drive_whitelist; s != NULL; s= Xorriso_lst_get_next(s, 0)) { sprintf(line, "-drive_class 'harmless' %s\n", Text_shellsafe(Xorriso_lst_get_text(s, 0), sfe, 0)); Xorriso_status_result(xorriso,filter,fp,flag&2); } } is_default= (xorriso->drives_exclusive == 1 && xorriso->drives_access == 1); sprintf(line, "-drive_access %s:%s\n", xorriso->drives_exclusive ? "exclusive" : "shared", xorriso->drives_access == 0 ? "readonly" : "unrestricted"); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso, filter, fp, flag & 2); is_default= (xorriso->linux_scsi_dev_family == 0); sprintf(line, "-scsi_dev_family %s\n", scsi_family[xorriso->linux_scsi_dev_family & 7]); if(!(is_default && no_defaults)) Xorriso_status_result(xorriso,filter,fp,flag&2); do_single= do_drive_access= 0; dev_filter= filter; if(dev_filter != NULL) { show_dev= Xorriso_status_filter(xorriso, filter, "-dev", 0); if(show_dev > 0) dev_filter= NULL; } if(dev_filter != NULL) { show_indev= Xorriso_status_filter(xorriso, filter, "-indev", 0); show_outdev= Xorriso_status_filter(xorriso, filter, "-outdev", 0); if(show_outdev > 0 || show_indev > 0) do_single= 1; } if((xorriso->drives_exclusive != xorriso->indev_is_exclusive || xorriso->drives_access != xorriso->indev_access) && xorriso->indev[0]) { do_single= 1; do_drive_access|= 1; } if((xorriso->drives_exclusive != xorriso->outdev_is_exclusive || xorriso->drives_access != xorriso->outdev_access) && xorriso->outdev[0]) { do_single= 1; do_drive_access|= 2; } if(strcmp(xorriso->indev, xorriso->outdev) == 0 && !do_single) { sprintf(line,"-dev %s\n", Text_shellsafe(xorriso->indev,sfe,0)); Xorriso_status_result(xorriso, dev_filter, fp, flag & 2); } else { did_drive_access= 0; if((do_drive_access & 1) && xorriso->indev[0] && show_indev) { sprintf(line,"-drive_access %s:%s\n", xorriso->indev_is_exclusive ? "exclusive" : "shared", xorriso->indev_access == 0 ? "readonly" : "unrestricted"); Xorriso_status_result(xorriso, NULL, fp, flag & 2); did_drive_access= 1; } sprintf(line,"-indev %s\n", Text_shellsafe(xorriso->indev,sfe,0)); Xorriso_status_result(xorriso, dev_filter, fp, flag & 2); if(did_drive_access) { sprintf(line,"-drive_access %s:%s\n", xorriso->drives_exclusive ? "exclusive" : "shared", xorriso->drives_access == 0 ? "readonly" : "unrestricted"); Xorriso_status_result(xorriso, NULL, fp, flag & 2); } did_drive_access= 0; if((do_drive_access & 2) && xorriso->outdev[0] && show_outdev) { sprintf(line,"-drive_access %s:%s\n", xorriso->outdev_is_exclusive ? "exclusive" : "shared", xorriso->outdev_access == 0 ? "readonly" : "unrestricted"); Xorriso_status_result(xorriso, NULL, fp, flag & 2); did_drive_access= 1; } sprintf(line,"-outdev %s\n", Text_shellsafe(xorriso->outdev,sfe,0)); Xorriso_status_result(xorriso, dev_filter, fp, flag & 2); if(did_drive_access) { sprintf(line,"-drive_access %s:%s\n", xorriso->drives_exclusive ? "exclusive" : "shared", xorriso->drives_access == 0 ? "readonly" : "unrestricted"); Xorriso_status_result(xorriso, NULL, fp, flag & 2); } } ret= 1; ex:; Xorriso_free_meM(sfe); Xorriso_free_meM(xorriso_id); return(ret); } int Xorriso_pacifier_reset(struct XorrisO *xorriso, int flag) { xorriso->start_time= Sfile_microtime(0); xorriso->last_update_time= xorriso->start_time; xorriso->pacifier_count= 0; xorriso->pacifier_prev_count= 0; xorriso->pacifier_total= 0; xorriso->pacifier_byte_count= 0; return(1); } /* This call is to be issued by long running workers in short intervals. It will check whether enough time has elapsed since the last pacifier message and eventually issue an update message. @param what_done A sparse description of the action, preferably in past tense. E.g. "done" , "files added". @param count The number of objects processed so far. Is ignored if <=0. @param todo The number of objects to be done in total. Is ignored if <=0. @param current_object A string telling the object currently processed. Ignored if "". @param flag bit0= report unconditionally, no time check bit1= report count <=0 (no thank you for being patient then) bit2= report xorriso->pacifier_byte_count bit3= report speed bit4= with bit3: count is in blocks, else in bytes bit5= with bit3: report total speed bit6= report with carriage return rather than line feed bit7= with bit5: speed unit for outdev rather than indev */ int Xorriso_pacifier_callback(struct XorrisO *xorriso, char *what_done, off_t count, off_t todo, char *current_object, int flag) { double current_time, since, interval_time, speed, speed_factor; char count_text[80], byte_text[80], profile_name[80], *speed_unit; int ret, profile_number, short_sec; off_t amount; current_time= Sfile_microtime(0); interval_time= current_time - xorriso->last_update_time; if(interval_time < xorriso->pacifier_interval && !(flag&1)) return(1); xorriso->last_update_time= Sfile_microtime(0); since= current_time - xorriso->start_time; if((flag & 1) && since < 1.0 && xorriso->pacifier_interval >= 1.0) since= 1.0; if((flag & 1) && since < 0.1) since= 0.1; byte_text[0]= 0; if(flag&4) { strcat(byte_text, " ("); Sfile_scale((double) xorriso->pacifier_byte_count, byte_text+strlen(byte_text), 7, 1e5, 0); strcat(byte_text, ")"); } short_sec= 0; if(count<=0.0 && !(flag&2)) { if(since < 2) return(2); sprintf(xorriso->info_text, "Thank you for being patient for"); } else if(todo<=0.0) { if(count<10000000) sprintf(count_text, "%7.f", (double) count); else Sfile_scale((double) count, count_text, 7, 1e5, 1); sprintf(xorriso->info_text, "%s %s%s in", count_text, what_done, byte_text); short_sec= (flag & 64); } else { sprintf(xorriso->info_text, "%.f of %.f %s%s in", (double) count, (double) todo, what_done, byte_text); short_sec= (flag & (8 | 64)); } if(xorriso->pacifier_interval < 1.0) { sprintf(xorriso->info_text + strlen(xorriso->info_text), " %.1f", since); } else { sprintf(xorriso->info_text + strlen(xorriso->info_text), " %.f", since); } sprintf(xorriso->info_text + strlen(xorriso->info_text), " %s", short_sec ? "s" : "seconds"); speed= -1.0; if(flag & 4) amount= xorriso->pacifier_byte_count; else amount= count; if((flag & 8)) { if(flag & 32) { if(since > 0) speed= amount / since; } else if(amount >= xorriso->pacifier_prev_count) { if(interval_time > 0) speed= (amount - xorriso->pacifier_prev_count) / interval_time; } } if(speed >= 0.0) { if(flag & 16) speed*= 2048.0; ret= Xorriso_get_profile(xorriso, &profile_number, profile_name, (flag >> 6) & 2); speed_factor= 1385000; speed_unit= "D"; if(ret == 2) { speed_factor= 150.0*1024; speed_unit= "C"; } else if(ret == 3) { speed_factor= 4495625; speed_unit= "B"; } sprintf(xorriso->info_text+strlen(xorriso->info_text), " %s %.1fx%s", (flag & 32 ? "=" : ","), speed / speed_factor, speed_unit); } xorriso->pacifier_prev_count= amount; if(current_object[0]!=0) sprintf(xorriso->info_text+strlen(xorriso->info_text), ", now at %s", current_object); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", (flag&64)); return(1); } int Xorriso_reset_counters(struct XorrisO *xorriso, int flag) { xorriso->error_count= 0; xorriso->insert_count= 0; xorriso->insert_bytes= 0; Xorriso_pacifier_reset(xorriso, 0); return(1); } /* @param flag bit0= to stderr rather than Xorriso_msgs_submit */ int Xorriso_no_malloc_memory(struct XorrisO *xorriso, char **to_free, int flag) { if(to_free!=NULL) if(*to_free!=NULL) { /* Eventual memory sacrifice to get on going */ free(*to_free); *to_free= NULL; } sprintf(xorriso->info_text, "Out of virtual memory"); if(flag & 1) { fprintf(stderr, "%s", xorriso->info_text); /* (No need to first check for problem status worse than ABORT) */ Xorriso_set_problem_status(xorriso, "ABORT", 0); } else Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "ABORT", 0); return(1); } /* @param flag bit0=path is in source filesystem , bit1= unconditionally */ int Xorriso_much_too_long(struct XorrisO *xorriso, int len, int flag) { if(len>=SfileadrL || (flag&2)) { sprintf(xorriso->info_text, "Path given for file in %s is much too long (%d)", ((flag&1) ? "local filesystem" : "ISO image"), len); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } int Xorriso_no_findjob(struct XorrisO *xorriso, char *cmd, int flag) { sprintf(xorriso->info_text, "%s: cannot create find job object", cmd); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); return(1); } int Xorriso_report_md5_outcome(struct XorrisO *xorriso, char *severity, int flag) { int has_md5; has_md5= Xorriso_image_has_md5(xorriso, 0); if(xorriso->find_check_md5_result & 1) { sprintf(xorriso->result_line, "Mismatch detected between file contents and MD5 checksums.\n"); } else if(xorriso->find_check_md5_result & 8) { sprintf(xorriso->result_line, "File contents and their MD5 checksums match.\n"); } else { sprintf(xorriso->result_line, "Not a single file with MD5 checksum was found."); if(has_md5 <= 0) strcat(xorriso->result_line, " (There is no MD5 checksum array loaded.)\n"); else strcat(xorriso->result_line, "\n"); } Xorriso_result(xorriso,0); if(xorriso->find_check_md5_result & 2) { sprintf(xorriso->result_line, "Encountered errors other than non-match during MD5 checking.\n"); Xorriso_result(xorriso,0); } if((xorriso->find_check_md5_result & 4) && has_md5) { sprintf(xorriso->result_line, "There were data files which have no MD5 and thus could not be checked.\n"); Xorriso_result(xorriso,0); } if((xorriso->find_check_md5_result & 3) && strcmp(severity, "ALL") != 0) { sprintf(xorriso->info_text, "Event triggered by MD5 comparison mismatch"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, severity, 0); } return(1); } /* @param flag bit0= do not issue prompt messages on info channel bit1= use line rather than asking at dialog input */ int Xorriso_msg_op_parse(struct XorrisO *xorriso, char *line, char *prefix, char *separators, int max_words, int pflag, int input_lines, int flag) { int ret, i, l, pargc= 0, bsl_mem; char *pline= NULL, **pargv= NULL, *parse_line, *text= NULL, *text_pt; Xorriso_alloc_meM(pline, char, SfileadrL); if(!(flag & 1)) { if(input_lines > 1) sprintf(xorriso->info_text, "-msg_op parse: Enter %d lines of text\n", input_lines); else sprintf(xorriso->info_text, "-msg_op parse: Enter text line\n"); Xorriso_info(xorriso, 0); } if(flag & 2) { parse_line= line; } else { pline[0]= 0; for(i= 0; i < input_lines; i++) { l= strlen(pline); ret= Xorriso_dialog_input(xorriso, pline + l, SfileadrL - l - 1, 8 | 1); if(ret <= 0) goto ex; if(i < input_lines - 1) strcat(pline, "\n"); } parse_line= pline; } ret= Xorriso_parse_line(xorriso, parse_line, prefix, separators, max_words, &pargc, &pargv, pflag); /* Temporarily disable backslash encoding of result channel */ bsl_mem= xorriso->bsl_interpretation; xorriso->bsl_interpretation&= ~32; xorriso->msg_sieve_disabled= 1; sprintf(xorriso->result_line, "%d\n", ret); Xorriso_result(xorriso, 1); if(ret == 1) { sprintf(xorriso->result_line, "%d\n", pargc); Xorriso_result(xorriso, 1); for(i= 0; i < pargc; i++) { text_pt= pargv[i]; if (bsl_mem & 32) { ret= Sfile_bsl_encoder(&text, pargv[i], strlen(pargv[i]), 4); if(ret > 0) text_pt= text; } ret= Sfile_count_char(text_pt, '\n') + 1; sprintf(xorriso->result_line, "%d\n", ret); Xorriso_result(xorriso, 1); Sfile_str(xorriso->result_line, text_pt, 0); strcat(xorriso->result_line, "\n"); Xorriso_result(xorriso, 1); Xorriso_free_meM(text); text= NULL; } } else { sprintf(xorriso->result_line, "0\n"); Xorriso_result(xorriso, 1); } xorriso->bsl_interpretation= bsl_mem; ret= 1; ex:; Xorriso__dispose_words(&pargc, &pargv); Xorriso_free_meM(text); Xorriso_free_meM(pline); return ret; } /* @param flag bit0= do not issue prompt messages on info channel */ int Xorriso_msg_op_parse_bulk(struct XorrisO *xorriso, char *prefix, char *separators, int max_words, int pflag, int bulk_lines, int flag) { int ret, input_lines, i, j, l; char line[80]; struct Xorriso_lsT *input_list= NULL, *input_end= NULL, *new_lst, *lst; char *pline= NULL; if(!(flag & 1)) { sprintf(xorriso->info_text, "Enter %d groups of lines. Each group begins by a line which tells the\n", bulk_lines); Xorriso_info(xorriso, 0); sprintf(xorriso->info_text, "number of following lines in the group. Then come the announced lines\n"); Xorriso_info(xorriso, 0); sprintf(xorriso->info_text, "Do this blindly. No further prompt will appear. Best be a computer.\n"); Xorriso_info(xorriso, 0); } Xorriso_alloc_meM(pline, char, SfileadrL); for(i= 0; i < bulk_lines; i++) { ret= Xorriso_dialog_input(xorriso, line, sizeof(line), 8 | 1); if(ret <= 0) goto ex; input_lines= -1; sscanf(line, "%d", &input_lines); pline[0]= 0; for(j= 0; j < input_lines; j++) { l= strlen(pline); ret= Xorriso_dialog_input(xorriso, pline + l, SfileadrL - l - 1, 8 | 1); if(ret <= 0) goto ex; if(j < input_lines - 1) strcat(pline, "\n"); } ret= Xorriso_lst_new(&new_lst, pline, input_end, 0); if(ret <= 0) goto ex; if(input_list == NULL) input_list= new_lst; input_end= new_lst; } for(lst= input_list; lst != NULL; lst= Xorriso_lst_get_next(lst, 0)) { ret= Xorriso_msg_op_parse(xorriso, Xorriso_lst_get_text(lst, 0), prefix, separators, max_words, pflag, input_lines, 1 | 2); if(ret <= 0) goto ex; } ret= 1; ex:; Xorriso_lst_destroy_all(&input_list, 0); Xorriso_free_meM(pline); return(1); } int Xorriso_launch_frontend(struct XorrisO *xorriso, int argc, char **argv, char *cmd_pipe_adr, char *reply_pipe_adr, int flag) { int command_pipe[2], reply_pipe[2], ret, i, cpid, is_banned= 0; char **exec_argv= NULL, *sfe= NULL, *adrpt; struct stat stbuf; Xorriso_alloc_meM(sfe, char, 5 * SfileadrL); for(i= 0; i < 2; i++) command_pipe[i]= reply_pipe[i]= -1; #ifndef Xorriso_allow_launch_frontenD /* To be controlled by: configure --enable-external-filters */ sprintf(xorriso->info_text, "-launch_frontend : Banned at compile time."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text, "This may be changed at compile time by ./configure option --enable-external-filters"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); is_banned= 1; #endif /* ! Xorriso_allow_launch_frontenD */ #ifndef Xorriso_allow_launch_frontend_suiD /* To be controlled by: configure --enable-external-filters-setuid */ if(getuid() != geteuid()) { sprintf(xorriso->info_text, "-set_filter: UID and EUID differ. Will not run external programs."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); sprintf(xorriso->info_text, "This may be changed at compile time by ./configure option --enable-external-filters-setuid"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); is_banned= 1; } #endif /* ! Xorriso_allow_launch_frontend_suiD */ if(is_banned) {ret= 0; goto ex;} if(argc > 0) { if(strchr(argv[0], '/') == NULL) { sprintf(xorriso->info_text, "-launch_frontend : Command path does not contain a '/'-character"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } /* Add a NULL pointer for execv() */ Xorriso_alloc_meM(exec_argv, char *, argc + 1); for(i= 0; i < argc; i++) exec_argv[i]= argv[i]; exec_argv[argc]= NULL; } else if(cmd_pipe_adr[0] == 0 || reply_pipe_adr[0] == 0) {ret= 0; goto ex;} if(cmd_pipe_adr[0] && reply_pipe_adr[0]) { /* Create named pipes if needed */ for(i= 0; i < 2; i++) { if(i == 0) adrpt= cmd_pipe_adr; else adrpt= reply_pipe_adr; ret= stat(adrpt, &stbuf); if(ret == -1) { ret= mknod(adrpt, S_IFIFO | S_IRWXU | S_IRWXG | S_IRWXO | S_IRWXO, (dev_t) 0); if(ret == -1) { sprintf(xorriso->info_text, "-launch_frontend: Cannot create named pipe %s", Text_shellsafe(adrpt, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } } } } else { ret= pipe(command_pipe); if (ret == -1) { no_pipe_open: sprintf(xorriso->info_text, "-launch_frontend: Failed to create a nameless pipe object"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } ret= pipe(reply_pipe); if (ret == -1) goto no_pipe_open; } if(argc > 0) { cpid = fork(); if (cpid == -1) { sprintf(xorriso->info_text, "-launch_frontend: Failed to create a child process"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } } else cpid= -1; /* Dummy child id */ if (cpid != 0) { /* Parent becomes the xorriso slave */ xorriso->use_stdin= 1; if(cmd_pipe_adr[0] && reply_pipe_adr[0]) { command_pipe[0]= open(cmd_pipe_adr, O_RDONLY | O_BINARY); if(command_pipe[0] == -1) { sprintf(xorriso->info_text, "-launch_frontend: Failed to open named command pipe %s", Text_shellsafe(cmd_pipe_adr, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } reply_pipe[1]= open(reply_pipe_adr, O_WRONLY | O_APPEND | O_BINARY); if(reply_pipe[1] == -1) { sprintf(xorriso->info_text, "-launch_frontend: Failed to open named reply pipe %s", Text_shellsafe(reply_pipe_adr, sfe, 0)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } } else { /* Close unused pipe ends */ close(command_pipe[1]); close(reply_pipe[0]); } close(0); close(1); close(2); ret= dup2(command_pipe[0], 0); if(ret == -1) { no_dup:; sprintf(xorriso->info_text, "-launch_frontend: Failed to connect pipe to xorriso standard i/o channels"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= -1; goto ex; } ret= dup2(reply_pipe[1], 1); if(ret == -1) goto no_dup; ret= dup2(reply_pipe[1], 2); if(ret == -1) goto no_dup; ret= 1; goto ex; } /* Child becomes the frontend program */ /* Close unused pipe ends */; if(cmd_pipe_adr[0] && reply_pipe_adr[0]) { command_pipe[1]= open(cmd_pipe_adr, O_WRONLY | O_APPEND | O_BINARY); if(command_pipe[1] == -1) { fprintf(stderr, "xorriso: -launch_frontend: Failed to open named command pipe '%s'\n", cmd_pipe_adr); perror("xorriso: -launch_frontend"); exit(1); } reply_pipe[0]= open(reply_pipe_adr, O_RDONLY | O_BINARY); if(reply_pipe[0] == -1) { fprintf(stderr, "xorriso: -launch_frontend: Failed to open named reply pipe '%s'\n", reply_pipe_adr); exit(1); } } else { close(command_pipe[0]); close(reply_pipe[1]); } close(0); close(1); ret= dup2(command_pipe[1], 1); if(ret == -1) { perror("xorriso: -launch_frontend: Error on redirecting standard output for frontend"); exit(1); } ret= dup2(reply_pipe[0], 0); if(ret == -1) { perror("xorriso: -launch_frontend: Error on redirecting standard input for frontend"); exit(1); } execv(exec_argv[0], exec_argv); fprintf(stderr, "xorriso: -launch_frontend: Failure to start program '%s'\n", exec_argv[0]); perror("xorriso: -launch_frontend"); exit(1); ex:; Xorriso_free_meM(exec_argv); Xorriso_free_meM(sfe); return(ret); } int Xorriso_open_named_pipe(struct XorrisO *xorriso, char fd_names[3][20], int mem_fds[], char *pipe_paths[], int pipe_fds[], int i, int flag) { if(mem_fds[i] == -1) return(2); pipe_fds[i]= open(pipe_paths[i], (i == 0 ? O_RDONLY : O_WRONLY) | O_BINARY); if(pipe_fds[i] == -1) { sprintf(xorriso->info_text, "-named_pipe_loop: Failed to open %s pipe ", fd_names[i]); Text_shellsafe(pipe_paths[i], xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0); return(0); } close(i); dup2(pipe_fds[i], i); return(1); } /* Usage example via bash: in=/u/test/xorriso_stdin out=/u/test/xorriso_stdout mknod "$in" p mknod "$out" p xorriso -abort_on NEVER \ -named_pipe_loop cleanup:buffered "$in" "$out" - & # Send command and receive result xorriso_cmd() { echo "$@" >/u/test/xorriso_stdin cat </u/test/xorriso_stdout } # Transport safety of filenames by wrapping in quotes and escaping quotes xorriso_esc() { echo -n "'" echo -n "$1" | sed -e "s/'/'"'"'"'"'"'"'/g" echo -n "'" } disk_path=...arbitrary.characters... iso_path=...arbitrary.characters... xorriso_cmd map $(xorriso_esc "$disk_path") $(xorriso_esc "$iso_path") */ /* @param flag bit0= unlink pipes when the loop ends bit1= read all lines from pipe until EOF before executing them */ int Xorriso_named_pipe_loop(struct XorrisO *xorriso, char *pipe_paths[3], int flag) { char *line= NULL; int i, ret, mem_fds[3], pipe_fds[3], first_line, hret, pipes_are_valid= 0; int lst_ret, filling_buffer= 0; off_t mem_used= 0, mem_needed; struct stat stbuf; struct Xorriso_lsT *prev_lst= NULL; static char fd_names[3][20] = { "standard input", "standard output", "standard error" }; char mem_text[80], limit_text[80]; for(i= 0; i < 3; i++ ) mem_fds[i]= pipe_fds[i]= -1; if(xorriso->tolerate_stdin_eof) { sprintf(xorriso->info_text, "Already in -named_pipe_loop. Ignoring further -named_pipe_loop command."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "SORRY", 0); return(0); /* intentionally not goto ex */ } Xorriso_alloc_meM(line, char, SfileadrL); /* Memorize stdin, stdout, and stderr. Close originals. */ for(i= 0; i < 3; i++ ) { if(pipe_paths[i][0] == 0 || strcmp(pipe_paths[i], "-") == 0) continue; if(stat(pipe_paths[i], &stbuf) == -1) { sprintf(xorriso->info_text, "-named_pipe_loop: Cannot determine properties of file "); Text_shellsafe(pipe_paths[i], xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } if(!S_ISFIFO(stbuf.st_mode)) { sprintf(xorriso->info_text, "-named_pipe_loop: File is not a named pipe: "); Text_shellsafe(pipe_paths[i], xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } mem_fds[i]= dup(i); if(mem_fds[i] == -1) { sprintf(xorriso->info_text, "-named_pipe_loop: Cannot duplicate original %s", fd_names[i]); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } } pipes_are_valid= 1; while(1) { /* Open input pipe */ ret= Xorriso_open_named_pipe(xorriso, fd_names, mem_fds, pipe_paths, pipe_fds, 0, 0); if(ret <= 0) goto ex; /* As long as the input connection exists */ xorriso->tolerate_stdin_eof= 1; first_line= 1; if(flag & 2) { filling_buffer= 8 | 16; /* single line, no reading from buffered_dialog */ prev_lst= NULL; mem_used= 0; } while(1) { /* Fetch input line */ if((flag & 2) && xorriso->buffered_dialog == NULL && !filling_buffer) { ret= -2; /* EOF */ } else { ret= Xorriso_dialog_input(xorriso, line, SfileadrL, 1 | filling_buffer); } if((flag & 2) && filling_buffer) { /* Fetch and buffer lines before opening output pipes */ if(ret > 0) { /* Check for excessive memory consumption */; mem_needed= strlen(line) + 8 + sizeof(struct Xorriso_lsT); if(mem_used + mem_needed > xorriso->temp_mem_limit) { Sfile_scale((double) (mem_used + mem_needed), mem_text, 5, 1e4, 0); Sfile_scale((double) xorriso->temp_mem_limit, limit_text, 5, 1e4, 0); sprintf(xorriso->info_text, "-named_pipe_loop: List of buffered input lines exceeds -temp_mem_limit (%s > %s)", mem_text, limit_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= -1; goto ex; } mem_used+= mem_needed; lst_ret= Xorriso_lst_new(&prev_lst, line, prev_lst, 0); if(lst_ret <= 0) { Xorriso_msgs_submit(xorriso, 0, "-named_pipe_loop: Cannot buffer all input lines", 0, "FATAL", 0); ret= -1; goto ex; } else { if(xorriso->buffered_dialog == NULL) xorriso->buffered_dialog= prev_lst; continue; } } filling_buffer= 0; /* start consuming buffer */ continue; } /* Open output pipes late to allow the sender to open them after the first (and usually only) input line was transmitted: echo ... >stdin_pipe res=$(cat <stdout_pipe) This will work independently of the stdin_pipe buffer size. */ if(first_line) { first_line= 0; for(i= 1; i < 3; i++) { hret= Xorriso_open_named_pipe(xorriso, fd_names, mem_fds, pipe_paths, pipe_fds, i, 0); if(hret <= 0 && ret > 0) {ret= hret; goto ex;} } } /* Now evaluate outcome of Xorriso_dialog_input() */ if(ret == -2) /* EOF at stdin */ break; if(ret <= 0) /* Other error */ goto ex; /* Regular end of command */ if(strcmp(line, "end_named_pipe_loop") == 0) {ret= 1; goto ex;} /* Perform xorriso command */ ret= Xorriso_execute_option(xorriso, line, (1 << 16)); if(ret <= 0) goto ex; if(ret == 3) /* e.g. -end command */ goto ex; Xorriso_mark(xorriso,0); } /* Restore stdin, stdout and stderr */ for(i= 0; i < 3; i++) { if(mem_fds[i] != -1) { if(pipe_fds[i] != -1) close(pipe_fds[i]); pipe_fds[i]= -1; close(i); dup2(mem_fds[i], i); } } } ret= 1; ex:; xorriso->tolerate_stdin_eof= 0; if(flag & 2) Xorriso_lst_destroy_all(&(xorriso->buffered_dialog), 0); /* Close any open pipes. Restore stdin, stdout and stderr. */ for(i= 0; i < 3; i++) { if(pipe_fds[i] != -1) { if((flag & 1) && pipes_are_valid) { if(stat(pipe_paths[i], &stbuf) != -1) { if(S_ISFIFO(stbuf.st_mode)) { sprintf(xorriso->info_text, "Removing named pipe "); Text_shellsafe(pipe_paths[i], xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); unlink(pipe_paths[i]); } } } close(pipe_fds[i]); } if(mem_fds[i] != -1) { close(i); dup2(mem_fds[i], i); close(mem_fds[i]); } } Xorriso_free_meM(line); return(ret); } /* @param flag bit0= append to out_text rather than overwrite it bit1= length limit is 10 * SfileadrL rather than 5 * */ char *Xorriso_esc_filepath(struct XorrisO *xorriso, char *in_text, char *out_text, int flag) { int l, w=0, limit= 5 * SfileadrL; char *res; if(xorriso->sh_style_result == 0) { res= Text_shellsafe(in_text, out_text, flag); return(res); } if(flag&1) w= strlen(out_text); if(flag & 2) limit= 10 * SfileadrL; l= strlen(in_text); if(w + l >= limit) { strcpy(out_text, "'xorriso: TEXT MUCH TOO LONG ... "); return(out_text); } strcpy(out_text + w, in_text); return(out_text); } /* @param flag bit=add to existing info_text */ int Xorriso_set_info_text(struct XorrisO *xorriso, char *text, size_t trunc_len, int flag) { size_t offst= 0, maxl; maxl= sizeof(xorriso->info_text) - 1; if(flag & 1) offst= strlen(xorriso->info_text); if(offst >= maxl) return(0); maxl-= offst; if(maxl > trunc_len) maxl= trunc_len; strncpy(xorriso->info_text + offst, text, maxl + 1); if(strlen(text) > maxl) { strcpy(xorriso->info_text + offst + maxl - 12, "#[truncated]"); return(2); } return(1); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2014 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of text i/o functions. */ #ifndef Xorriso_pvt_textio_includeD #define Xorriso_pvt_textio_includeD yes int Xorriso_dialog_input(struct XorrisO *xorriso, char line[], int linesize, int flag); /** @return -1= abort , 0= no , 1= yes */ int Xorriso_reassure(struct XorrisO *xorriso, char *cmd, char *which_will, int flag); int Xorriso_request_confirmation(struct XorrisO *xorriso, int flag); /* @param flag bit0= quoted multiline mode bit1= release allocated memory and return 1 bit2= with bit0: warn of empty text arguments bit3= deliver as single quoted text including all whitespace and without any backslash interpretation @return -1=out of memory , 0=line format error , 1=ok, go on , 2=done */ int Xorriso_read_lines(struct XorrisO *xorriso, FILE *fp, int *linecount, int *argc, char ***argv, int flag); int Xorriso_write_to_channel(struct XorrisO *xorriso, char *in_text, int channel_no, int flag); int Xorriso_result(struct XorrisO *xorriso, int flag); int Xorriso_restxt(struct XorrisO *xorriso, char *text); int Xorriso_info(struct XorrisO *xorriso, int flag); int Xorriso_mark(struct XorrisO *xorriso, int flag); int Xorriso_write_session_log(struct XorrisO *xorriso, int flag); int Xorriso_status_result(struct XorrisO *xorriso, char *filter, FILE *fp, int flag); int Xorriso_status(struct XorrisO *xorriso, char *filter, FILE *fp, int flag); int Xorriso_pacifier_reset(struct XorrisO *xorriso, int flag); /* This call is to be issued by long running workers in short intervals. It will check whether enough time has elapsed since the last pacifier message and eventually issue an update message. @param what_done A sparse description of the action, preferably in past tense. E.g. "done" , "files added". @param count The number of objects processed so far. Is ignored if <=0. @param todo The number of objects to be done in total. Is ignored if <=0. @param current_object A string telling the object currently processed. Ignored if "". @param flag bit0= report unconditionally, no time check */ int Xorriso_pacifier_callback(struct XorrisO *xorriso, char *what_done, off_t count, off_t todo, char *current_object, int flag); int Xorriso_reset_counters(struct XorrisO *xorriso, int flag); int Xorriso_no_malloc_memory(struct XorrisO *xorriso, char **to_free, int flag); int Xorriso_much_too_long(struct XorrisO *xorriso, int len, int flag); int Xorriso_no_findjob(struct XorrisO *xorriso, char *cmd, int flag); int Xorriso_report_md5_outcome(struct XorrisO *xorriso, char *severity, int flag); int Xorriso_protect_stdout(struct XorrisO *xorriso, int flag); int Xorriso_msg_op_parse(struct XorrisO *xorriso, char *line, char *prefix, char *separators, int max_words, int pflag, int input_lines, int flag); int Xorriso_msg_op_parse_bulk(struct XorrisO *xorriso, char *prefix, char *separators, int max_words, int pflag, int bulk_lines, int flag); int Xorriso_launch_frontend(struct XorrisO *xorriso, int argc, char **argv, char *cmd_pipe_adr, char *reply_pipe_adr, int flag); int Xorriso_named_pipe_loop(struct XorrisO *xorriso, char *pipe_paths[3], int flag); char *Xorriso_esc_filepath(struct XorrisO *xorriso, char *in_text, char *out_text, int flag); int Xorriso_set_info_text(struct XorrisO *xorriso, char *text, size_t trunc_len, int flag); #ifdef Xorriso_with_editlinE int Xorriso__shutdown_editline(int flag); #endif #ifdef Xorriso_with_readlinE const char *Xorriso__readline_license(int flag); #endif #endif /* ! Xorriso_pvt_textio_includeD */ /* ( cd xorriso ; cc -g -Wall -o unite_html_b_line unite_html_b_line.c ) */ /* Specialized converter for the output of man -H, which unites lines where the line end is between <b> and </b>. Copyright 2015 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <errno.h> int unite_lines(char *buffer, int *b_open, int *b_state, int flag) { char *cpt; int last_was_nl= 0; for(cpt= buffer; *cpt != 0; cpt++) { if(*b_open) { if(*b_state == 0 && *cpt == '<') { *b_state= 1; } else if(*b_state == 1) { if(*cpt == '/') *b_state= 2; else *b_state= 0; } else if(*b_state == 2) { if(*cpt == 'b' || *cpt == 'B') *b_state= 3; else *b_state= 0; } else if(*b_state == 3) { if(*cpt == '>') *b_open= 0; *b_state= 0; } } else { if(*b_state == 0 && *cpt == '<') { *b_state= 1; } else if(*b_state == 1) { if(*cpt == 'b' || *cpt == 'B') *b_state= 2; else *b_state= 0; } else if(*b_state == 2) { if(*cpt == '>') *b_open= 1; *b_state= 0; } } last_was_nl= (*cpt == '\n'); } if(*b_open && last_was_nl) { /* replace newline */ *(cpt - 1)= ' '; } return(1); } int main(int argc, char **argv) { FILE *fpin, *fpout; char buffer[4096], *respt; int ret, b_open= 0, b_state= 0; if(argc != 3) { fprintf(stderr, "usage: %s input_path output_path\n", argv[0]); return(1); } if(strcmp(argv[1], "-") == 0) { fpin= stdin; } else { fpin= fopen(argv[1], "rb"); if(fpin == 0) { fprintf(stderr, "Error with input file '%s' : %s\n", argv[1], strerror(errno)); return(2); } } if(strcmp(argv[2], "-") == 0) { fpout= stdout; } else { fpout= fopen(argv[2], "wb"); if(fpout == 0) { fprintf(stderr, "Error with output file '%s' : %s\n", argv[2], strerror(errno)); return(3); } } while(1) { respt= fgets(buffer, sizeof(buffer), fpin); if(respt == NULL) break; ret= unite_lines(buffer, &b_open, &b_state, 0); if(ret <= 0) break; ret= fputs(buffer, fpout); if(ret < 0) { fprintf(stderr, "Error writing to output file '%s' : %s\n", argv[2], strerror(errno)); return(4); } } if(fpin != stdin) fclose(fpin); if(fpout != stdout) fclose(stdout); return(0); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains functions which are needed to write sessions. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <ctype.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <errno.h> #include <fcntl.h> #ifdef HAVE_STDINT_H #include <stdint.h> #else #ifdef HAVE_INTTYPES_H #include <inttypes.h> #endif #endif /* O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #ifdef Xorriso_standalonE #ifdef Xorriso_with_libjtE #include "../libjte/libjte.h" #endif #else #ifdef Xorriso_with_libjtE #include <libjte/libjte.h> #endif #endif /* ! Xorriso_standalonE */ #include "xorriso.h" #include "xorriso_private.h" #include "lib_mgt.h" #include "drive_mgt.h" #include "iso_img.h" #include "iso_tree.h" #include "write_run.h" /* @param flag bit0= talk of -as cdrecord -multi rather than of -close */ int Xorriso_check_multi(struct XorrisO *xorriso, struct burn_drive *drive, int flag) { int profile_no= 0, ret; struct burn_multi_caps *caps= NULL; char profile_name[80]; if(xorriso->auto_close) xorriso->do_close= 0; if(!xorriso->do_close) { burn_disc_get_profile(drive, &profile_no, profile_name); if(profile_no == 0x14) { /* DVD-RW sequential */ ret= burn_disc_get_multi_caps(drive, BURN_WRITE_TAO, &caps, 0); if(caps != NULL) burn_disc_free_multi_caps(&caps); if(ret == 0) { if(xorriso->auto_close) { sprintf(xorriso->info_text, "-close \"as_needed\" triggered -close \"on\""); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); xorriso->do_close= 1; } else if(flag & 1) { sprintf(xorriso->info_text, "This DVD-RW media can only be written without option -multi"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text, "Possibly it was blanked by blank=deformat_quickest"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); sprintf(xorriso->info_text, "After writing a session without -multi, apply blank=all"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); return(0); } else { sprintf(xorriso->info_text, "This DVD-RW media can only be written with -close \"on\""); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text, "Possibly it was blanked by -blank \"deformat_quickest\""); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); sprintf(xorriso->info_text, "After writing a session with -close \"on\", apply -blank \"all\""); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); return(0); } } } else if(profile_no == 0x15) { /* DVD-RW DL */ if(xorriso->auto_close) { sprintf(xorriso->info_text, "-close \"as_needed\" triggered -close \"on\""); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); xorriso->do_close= 1; } else if(flag & 1) { sprintf(xorriso->info_text, "DVD-R DL media can only be written without option -multi"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } else { sprintf(xorriso->info_text, "DVD-R DL media can only be written with -close \"on\""); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } } return(1); } int Xorriso_make_write_options( struct XorrisO *xorriso, struct burn_drive *drive, struct burn_write_opts **burn_options, int flag) { int drive_role, stream_mode= 0, ret, profile; char profile_name[80]; enum burn_disc_status s; *burn_options= burn_write_opts_new(drive); if(*burn_options==NULL) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text,"Cannot allocate option set"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } burn_write_opts_set_simulate(*burn_options, !!xorriso->do_dummy); drive_role= burn_drive_get_drive_role(drive); burn_write_opts_set_multi(*burn_options, !(xorriso->do_close || drive_role==0 || drive_role==3)); ret= burn_disc_get_profile(drive, &profile, profile_name); if(ret > 0) { s= isoburn_disc_get_status(drive); if(xorriso->auto_close && xorriso->do_close == 0 && profile == 0x14 && s == BURN_DISC_BLANK) /* Prepare for missing feature 21h despite drive's announcement */ burn_write_opts_set_fail21h_sev(*burn_options, "NOTE"); } if(xorriso->write_speed != -2) burn_drive_set_speed(drive, 0, xorriso->write_speed); burn_drive_set_buffer_waiting(drive, xorriso->modesty_on_drive, xorriso->min_buffer_usec, xorriso->max_buffer_usec, xorriso->buffer_timeout_sec, xorriso->min_buffer_percent, xorriso->max_buffer_percent); if(xorriso->do_stream_recording == 1) stream_mode= 1; else if(xorriso->do_stream_recording == 2) stream_mode= 51200; /* 100 MB */ else if(xorriso->do_stream_recording >= 16) stream_mode= xorriso->do_stream_recording; burn_write_opts_set_stream_recording(*burn_options, stream_mode); #ifdef Xorriso_dvd_obs_default_64K if(xorriso->dvd_obs == 0) burn_write_opts_set_dvd_obs(*burn_options, 64 * 1024); else #endif burn_write_opts_set_dvd_obs(*burn_options, xorriso->dvd_obs); burn_write_opts_set_obs_pad(*burn_options, !!xorriso->do_obs_pad); burn_write_opts_set_bdr_obs_exempt(*burn_options, !!xorriso->bdr_obs_exempt); burn_write_opts_set_stdio_fsync(*burn_options, xorriso->stdio_sync); burn_write_opts_set_underrun_proof(*burn_options, 1); return(1); } /* @param flag bit0= do not write but only prepare and return size in sectors bit1= do not use isoburn wrappers, do not assume libisofs bit2= no_emul_toc : do not pad random access media to full 64 KB */ off_t Xorriso_sanitize_image_size(struct XorrisO *xorriso, struct burn_drive *drive, struct burn_disc *disc, struct burn_write_opts *burn_options, int flag) { int num_sessions= 0, num_tracks= 0, padding= 0, profile; off_t media_space, ret, img_sectors, lba, nwa, multi_emul_blocks= 0; char profile_name[80]; struct burn_session **sessions; struct burn_track **tracks; enum burn_disc_status s; img_sectors= burn_disc_get_sectors_v2(disc); sessions= burn_disc_get_sessions(disc, &num_sessions); if(sessions==NULL || num_sessions < 1) { no_track:; Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text,"Program error : no track in prepared disc"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); {ret= -1; goto ex;} } tracks= burn_session_get_tracks(sessions[0], &num_tracks); if(tracks==NULL || num_tracks < 1) goto no_track; padding= 0; ret= burn_disc_get_profile(drive, &profile, profile_name); padding= xorriso->padding / 2048; if(xorriso->padding > padding * 2048) padding++; if(img_sectors>0 && ret>0 && (profile==0x09 || profile==0x0a)) { /* CD-R , CD-RW */ if(img_sectors + padding < Xorriso_cd_min_track_sizE) { padding= Xorriso_cd_min_track_sizE - img_sectors; sprintf(xorriso->info_text, "Expanded track to minimum size of %d sectors", Xorriso_cd_min_track_sizE); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } } if(xorriso->alignment == 0 && !((xorriso->no_emul_toc & 1) || flag & 4)) { ret= isoburn_needs_emulation(drive); if(ret > 0) { /* Take care that the session is padded up to the future NWA. Else with padding < 32 it could happen that PVDs from older sessions survive and confuse -rom_toc_scan. */ xorriso->alignment= 32; s= isoburn_disc_get_status(drive); if(s == BURN_DISC_BLANK) { /* Count blocks before nwa as part of the image */; ret= isoburn_disc_track_lba_nwa_v2(drive, burn_options, 0, &lba, &nwa); if(ret <= 0) nwa= 0; multi_emul_blocks= nwa; } } } if(!(flag & 2)) { #ifdef Xorriso_with_libjtE /* JTE : no multi-session, no_emul_toc, padding in libisofs */ if(xorriso->libjte_handle != NULL) padding= 0; #endif /* ! Xorriso_with_libjtE */ if(xorriso->do_padding_by_libisofs) padding= 0; } if(xorriso->alignment > 0) { if(img_sectors > 0) { ret= isoburn_disc_track_lba_nwa_v2(drive, burn_options, 0, &lba, &nwa); if(ret <= 0) nwa= 0; lba= (nwa + img_sectors + padding) % xorriso->alignment; if(lba > 0) padding+= xorriso->alignment - lba; } } burn_track_define_data(tracks[0], 0, padding * 2048, 0, BURN_MODE1); Xorriso_process_msg_queues(xorriso,0); if(flag&2) media_space= burn_disc_available_space(drive, burn_options) / (off_t) 2048; else media_space= isoburn_disc_available_space(drive, burn_options) / (off_t) 2048; if(media_space < img_sectors + padding) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "Image size %.fs exceeds free space on media %.fs", (double) (img_sectors + padding), (double) media_space); if(flag & 1) { Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } else { Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } } if(flag&1) { ret= multi_emul_blocks + img_sectors + padding; } else ret= 1; ex:; return(ret); } int Xorriso_auto_format(struct XorrisO *xorriso, int flag) { int ret, profile, status, num_formats; char profile_name[80]; struct burn_drive_info *dinfo; struct burn_drive *drive; off_t size; unsigned dummy; ret= Xorriso_may_burn(xorriso, 0); if(ret <= 0) return(0); ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to autoformat", 2); if(ret<=0) return(0); ret= burn_disc_get_profile(drive, &profile, profile_name); if(ret>0 && (profile==0x12 || profile==0x43)) { /* DVD-RAM or BD-RE */ ret= burn_disc_get_formats(drive, &status, &size, &dummy, &num_formats); if(ret>0 && status==BURN_FORMAT_IS_UNFORMATTED) { sprintf(xorriso->info_text, "Unformatted %s medium detected. Trying -format fast.", profile_name); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= Xorriso_format_media(xorriso, (off_t) 0, 1 | 4); if(ret<=0) { sprintf(xorriso->info_text, "Automatic formatting of %s failed", profile_name); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(ret); } burn_drive_re_assess(drive, 0); } } return(1); } /* @param flag bit0= fail on indev == outdev with "imported_iso" bit1= fail on indev == NULL with "imported_iso" */ int Xorriso_check_intvl_string(struct XorrisO *xorriso, char **part_image, int flag) { char *cpt, *ipt, *orig; orig= *part_image; if(strncmp(*part_image, "--interval:", 11) != 0) return(0); if(strchr(*part_image + 11, ':') == NULL) return(0); (*part_image)+= 11; if(!(flag & 3)) return(1); cpt= strchr(*part_image, ':'); ipt= strstr(*part_image, "imported_iso"); if(ipt == NULL || ipt > cpt) return(1); if((flag & 2) && xorriso->in_drive_handle == NULL) { sprintf(xorriso->info_text, "Interval reader lacks of -indev to read from \"imported_iso\""); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto failure; } if(!(flag & 1)) return(1); if(xorriso->out_drive_handle != xorriso->in_drive_handle) return(1); sprintf(xorriso->info_text, "Interval reader may not read from \"imported_iso\" during write run to same drive"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); failure:; sprintf(xorriso->info_text, "Rejected: "); Text_shellsafe(orig, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(-1); } int Xorriso_set_system_area(struct XorrisO *xorriso, struct burn_drive *in_drive, struct burn_drive *out_drive, IsoImage *img, struct isoburn_imgen_opts *sopts, int flag) { int ret, options, system_area_options; int sa_loaded, read_count, i, read_sum= 0; off_t iso_lba= -1, start_lba, image_blocks; char volid[33]; FILE *fp= NULL; char *buf= NULL, *bufpt= NULL, *intvl; uint8_t *intvl_buf; off_t hd_lba, byte_count; unsigned char *ub; ElToritoBootImage *bootimg; IsoFile *bootimg_node; IsoNode *sparc_core_node; uint32_t offst; enum burn_disc_status state; struct iso_interval_reader *ivr = NULL; if(xorriso->grub2_sparc_core[0]) { ret= Xorriso_node_from_path(xorriso, img, xorriso->grub2_sparc_core, &sparc_core_node, 1); if(ret <= 0) { sprintf(xorriso->info_text, "Cannot find in ISO image: -boot_image grub grub2_sparc_core="); Text_shellsafe(xorriso->grub2_sparc_core, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(!ISO_NODE_IS_FILE(sparc_core_node)) { sprintf(xorriso->info_text, "Not a data file: -boot_image grub grub2_sparc_core="); Text_shellsafe(xorriso->grub2_sparc_core, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } ret = iso_image_set_sparc_core(img, (IsoFile *) sparc_core_node, 0); if(ret < 0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", ret, "Error when setting up -boot_image grub grub2_sparc_core=", 0, "FAILURE", 1); {ret= 0; goto ex;} } } Xorriso_alloc_meM(buf, char, 32768); memset(buf, 0, 32768); system_area_options= xorriso->system_area_options; if(xorriso->system_area_clear_loaded || (in_drive != out_drive && in_drive != NULL)) sa_loaded= 0; else sa_loaded= iso_image_get_system_area(img, buf, &options, 0); if(sa_loaded < 0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", sa_loaded, "Error when inquiring System Area data of ISO 9660 image", 0, "FAILURE", 1); {ret= 0; goto ex;} } bufpt= buf; if(xorriso->system_area_disk_path[0] == 0) { if(xorriso->patch_system_area && xorriso->system_area_options == 0 && sa_loaded > 0) { system_area_options= xorriso->patch_system_area; /* Check whether partition 1 ends at image end */; ub= (unsigned char *) buf; hd_lba= (ub[454] | (ub[455] << 8) | (ub[456] << 16) | (((unsigned int) ub[457]) << 24)) + (ub[458] | (ub[459] << 8) | (ub[460] << 16) | (((unsigned int) ub[461]) << 24)); iso_lba= -1; ret= isoburn_disc_get_msc1_v2(in_drive, &start_lba); if(ret > 0) { ret= isoburn_read_iso_head_v2(in_drive, start_lba, &image_blocks, volid, 1); if(ret > 0) iso_lba= start_lba + image_blocks; } if(iso_lba * (off_t) 4 > hd_lba) { system_area_options= 0; } else if((xorriso->patch_system_area & 1) && iso_lba * (off_t) 4 != hd_lba) { system_area_options= 0; } else if((xorriso->patch_system_area & 2) && iso_lba * (off_t) 4 + (off_t) (63 * 256) < hd_lba) { system_area_options= 0; } else if(xorriso->patch_system_area & 2) { /* isohybrid patching */ /* Check whether bytes 432-345 point to ElTorito LBA */ hd_lba= ub[432] | (ub[433] << 8) | (ub[434] << 16) | (((unsigned int) ub[435]) << 24); ret= iso_image_get_boot_image(img, &bootimg, &bootimg_node, NULL); if(ret != 1) { system_area_options= 0; } else if(bootimg_node != NULL) { Xorriso__file_start_lba((IsoNode *) bootimg_node, &(iso_lba), 0); if(iso_lba * (off_t) 4 != hd_lba) system_area_options= 0; } } if(system_area_options == 0) { Xorriso_msgs_submit(xorriso, 0, "Loaded System Area data are not suitable for MBR patching.", 0, "DEBUG", 0); } } {ret= 1; goto do_set;} } bufpt= buf; if(strcmp(xorriso->system_area_disk_path, "/dev/zero") == 0) { memset(buf, 0, 32768); {ret= 1; goto do_set;} } intvl= xorriso->system_area_disk_path; ret= Xorriso_check_intvl_string(xorriso, &intvl, 2); if(ret < 0) { {ret= 0; goto ex;} } else if(ret > 0) { ret= iso_interval_reader_new(img, intvl, &ivr, &byte_count, 0); Xorriso_process_msg_queues(xorriso, 0); if(ret < 0) { intvl_reader_err:; sprintf(xorriso->info_text, "Error when reading -boot_image system_area="); Text_shellsafe(xorriso->system_area_disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); {ret= 0; goto ex;} } for(i= 0; i < 16; i++) { intvl_buf= (uint8_t *) (buf + 2048 * i); ret= iso_interval_reader_read(ivr, intvl_buf, &read_count, 0); Xorriso_process_msg_queues(xorriso, 0); if(ret == 0) break; if(ret < 0) goto intvl_reader_err; read_sum+= read_count; } ret= read_sum; } else { ret= Xorriso_afile_fopen(xorriso, xorriso->system_area_disk_path, "rb", &fp, 2); if(ret <= 0) {ret= 0; goto ex;} ret= fread(buf, 1, 32768, fp); if(ret < 32768) { if(ferror(fp)) { sprintf(xorriso->info_text, "Error when reading -boot_image system_area="); Text_shellsafe(xorriso->system_area_disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); {ret= 0; goto ex;} } } } do_set:; if(ret > 0 && xorriso->system_area_disk_path[0]) { sprintf(xorriso->info_text, "Copying to System Area: %d bytes from file ", ret); Text_shellsafe(xorriso->system_area_disk_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } ret= isoburn_igopt_set_system_area(sopts, bufpt, system_area_options); if(ret != ISO_SUCCESS) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", ret, "Error when attaching System Area data to ISO 9660 image", 0, "FAILURE", 1); {ret= 0; goto ex;} } offst= xorriso->partition_offset; state= isoburn_disc_get_status(out_drive); if(state == BURN_DISC_APPENDABLE) { ret= isoburn_get_img_partition_offset(out_drive, &offst); if(ret == 1) { sprintf(xorriso->info_text, "Preserving in ISO image: -boot_image any partition_offset=%lu", (unsigned long) offst); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } else offst= xorriso->partition_offset; } ret= isoburn_igopt_set_part_offset(sopts, offst, xorriso->partition_secs_per_head, xorriso->partition_heads_per_cyl); if(ret != ISO_SUCCESS) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", ret, "Error when setting partition offset", 0, "FAILURE", 1); {ret= 0; goto ex;} } ret= 1; ex:; if(fp != NULL && fp != stdin) fclose(fp); iso_interval_reader_destroy(&ivr, 0); Xorriso_free_meM(buf); return(ret); } /* @param flag bit0= do not increment boot_count and do not reset boot parameters bit1= dispose attached boot images */ int Xorriso_attach_boot_image(struct XorrisO *xorriso, int flag) { int ret; char *cpt; struct burn_drive_info *source_dinfo; struct burn_drive *source_drive; IsoImage *image= NULL; IsoNode *node= NULL; ElToritoBootImage *bootimg; enum eltorito_boot_media_type emul_type= ELTORITO_NO_EMUL; char *bin_path; int emul, platform_id; off_t load_size; struct stat stbuf; int hflag= 0, is_interval= 0, cat_tries; if(xorriso->boot_image_bin_path[0] == 0 && !(flag & 2)) { /* >>> no boot image path given : no op */; ret= 2; goto ex; } if(xorriso->in_drive_handle == NULL) hflag= 2; ret= Xorriso_get_drive_handles(xorriso, &source_dinfo, &source_drive, "on attempt to attach boot image", hflag); if(ret<=0) goto ex; image= isoburn_get_attached_image(source_drive); if(image == NULL) { /* (should not happen) */ sprintf(xorriso->info_text, "No ISO image present on attempt to attach boot image"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(flag & 2) { iso_image_remove_boot_image(image); xorriso->boot_count= 0; ret= 1; goto ex; } bin_path= xorriso->boot_image_bin_path; emul= xorriso->boot_image_emul; platform_id= xorriso->boot_platform_id; load_size= xorriso->boot_image_load_size; if(strncmp(bin_path, "--interval:appended_partition_", 30) == 0) { is_interval= 1; if(load_size <= 0) load_size= 512; } if(xorriso->boot_efi_default) { emul= 0; platform_id= 0xef; xorriso->patch_isolinux_image= (xorriso->patch_isolinux_image & ~3) | 0; } if((platform_id == 0xef || load_size < 0) && !is_interval) { ret= Xorriso_iso_lstat(xorriso, bin_path, &stbuf, 2 | 4); if(ret != 0) {ret= 0; goto ex;} load_size= ((stbuf.st_size / (off_t) 512) + !!(stbuf.st_size % (off_t) 512)) * 512; } sprintf(xorriso->info_text, "Adding boot image "); Text_shellsafe(bin_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); if(emul == 0) emul_type= ELTORITO_NO_EMUL; else if(emul == 1) emul_type= ELTORITO_HARD_DISC_EMUL; else if(emul == 2) emul_type= ELTORITO_FLOPPY_EMUL; if (!is_interval) { ret= Xorriso_node_from_path(xorriso, image, bin_path, &node, 1); if(ret <= 0) { sprintf(xorriso->info_text, "Cannot find in ISO image: -boot_image ... bin_path="); Text_shellsafe(bin_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } } if(xorriso->boot_count == 0) { if(xorriso->boot_image_cat_path[0] == 0) { strcpy(xorriso->boot_image_cat_path, bin_path); cpt= strrchr(xorriso->boot_image_cat_path, '/'); if(cpt == NULL) { strcpy(xorriso->boot_image_cat_path, "/"); cpt= xorriso->boot_image_cat_path + 1; } else { cpt++; } strcpy(cpt, "boot.cat"); if(xorriso->boot_image_cat_hidden & 1) { /* Find a name which does not yet exist */ ret= 1; for(cat_tries= 1; ret > 0 && cat_tries < 1000000; cat_tries++) { ret= Xorriso_node_from_path(xorriso, image, xorriso->boot_image_cat_path, &node, 1); if(ret > 0) sprintf(cpt, "real%d_boot.cat", cat_tries); } } } ret= Xorriso_node_from_path(xorriso, image, xorriso->boot_image_cat_path, &node, 1); if(ret > 0) { if(!xorriso->do_overwrite) { sprintf(xorriso->info_text, "May not overwrite existing -boot_image ... cat_path="); Text_shellsafe(xorriso->boot_image_cat_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } ret= Xorriso_rmi(xorriso, NULL, (off_t) 0, xorriso->boot_image_cat_path, 8 | (xorriso->do_overwrite == 1)); if(ret != 1) { sprintf(xorriso->info_text, "Could not remove existing -boot_image cat_path="); Text_shellsafe(xorriso->boot_image_cat_path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } } /* Discard old boot image, set new one */ ret= iso_image_get_boot_image(image, &bootimg, NULL, NULL); if(ret == 1) iso_image_remove_boot_image(image); ret= iso_image_set_boot_image(image, bin_path, emul_type, xorriso->boot_image_cat_path, &bootimg); if(ret > 0) iso_image_set_boot_catalog_weight(image, 1000000000); } else { ret= iso_image_add_boot_image(image, bin_path, emul_type, 0, &bootimg); } if(ret < 0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_report_iso_error(xorriso, "", ret, "Error when attaching El-Torito boot image to ISO 9660 image", 0, "FAILURE", 1); sprintf(xorriso->info_text, "Could not attach El-Torito boot image to ISO 9660 image"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } el_torito_set_boot_platform_id(bootimg, (uint8_t) platform_id); if(load_size / 512 > 65535) { sprintf(xorriso->info_text, "Boot image load size exceeds 65535 blocks of 512 bytes. "); if(platform_id == 0xef) { strcat(xorriso->info_text, "Will record 0 in El Torito to extend ESP to end-of-medium."); load_size= 0; } else { strcat(xorriso->info_text, "Will record 65535 in El Torito."); load_size= 65535 * 512; } Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } if(xorriso->boot_img_full_size) { el_torito_set_full_load(bootimg, 1); } else { /* The function will understand negative short as positive unsigned */ el_torito_set_load_size(bootimg, (short) (load_size / 512)); } el_torito_set_id_string(bootimg, xorriso->boot_id_string); el_torito_set_selection_crit(bootimg, xorriso->boot_selection_crit); ret= Xorriso_set_isolinux_options(xorriso, image, 1); if(!(flag & 1)) { /* Register attachment and reset even in case of error return */ xorriso->boot_count++; xorriso->boot_platform_id= 0; xorriso->patch_isolinux_image= 0; xorriso->boot_image_bin_path[0]= 0; xorriso->boot_image_bin_form[0]= 0; xorriso->boot_image_emul= 0; xorriso->boot_emul_default= 1; xorriso->boot_image_load_size= 4 * 512; xorriso->boot_img_size_default= 1; xorriso->boot_img_full_size= 0; memset(xorriso->boot_id_string, 0, sizeof(xorriso->boot_id_string)); memset(xorriso->boot_selection_crit, 0, sizeof(xorriso->boot_selection_crit)); xorriso->boot_efi_default= 0; } if(ret <= 0) goto ex; ret= 1; ex:; if(image != NULL) iso_image_unref(image); return(ret); } int Xorriso_write_application_use(struct XorrisO *xorriso, IsoImage *image, int flag) { int l, ret, count= 512; unsigned int byte= 0; char *path, data[512]; FILE *fp= NULL; path= xorriso->application_use; l= strlen(path); if(l <= 1) { memset(data, path[0], 512); } else if(l == 4 && path[0] == '0' && path[1] == 'x' && isxdigit(path[2]) && isxdigit(path[3])) { sscanf(path + 2, "%x", &byte); memset(data, (int) byte, 512); } else { /* Read up to 512 bytes from file path */ ret= Xorriso_afile_fopen(xorriso, path, "rb", &fp, 0); if(ret <= 0) {ret= 0; goto ex;} ret= fread(data, 1, 512, fp); if(ret < 512) { if(ferror(fp)) { sprintf(xorriso->info_text, "-application_use: Error while reading file "); Text_shellsafe(path, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } } if(ret < 0) count= 0; else count= ret; } iso_image_set_app_use(image, data, count); ret= 1; ex: if(fp != NULL && fp != stdin) fclose(fp); return(ret); } int Xorriso_retry_write_session(struct XorrisO *xorriso, int flag) { int ret, auto_close_mem, do_close_mem; if(xorriso->do_tao == 1) { Xorriso_msgs_submit(xorriso, 0, "There is no hope for a re-try with -close \"on\" as long as -write_type is \"tao\"", 0, "FAILURE", 0); return(0); } Xorriso_msgs_submit(xorriso, 0, "Re-trying with -close \"on\"", 0, "NOTE", 0); do_close_mem= xorriso->do_close; auto_close_mem= xorriso->auto_close; xorriso->do_close= 1; xorriso->auto_close= 0; ret= Xorriso_write_session(xorriso, 0); xorriso->do_close= do_close_mem; xorriso->auto_close= auto_close_mem; return(ret); } int Xorriso_make_iso_write_opts(struct XorrisO *xorriso, IsoImage *image, struct isoburn_imgen_opts *sopts, int flag) { int ext, i, ret, pad_by_libisofs= 0, is_bootable= 0, relax, intvl_string= 0; int intvl_check= 2; /* 3 forbids "imported_iso" */ char *out_cs, *part_image; IsoNode *root_node; uint32_t padding; relax= xorriso->relax_compliance; if(image != NULL) is_bootable= iso_image_get_boot_image(image, NULL, NULL, NULL); /* xorriso->patch_isolinux_image gets reset in Xorriso_attach_boot_image() So this applies only to -boot_image ... "patch" or "keep". >>> Better would be to analyze and keep the relaxations of the loaded image. */ if((xorriso->patch_isolinux_image & 1) && is_bootable == 1) relax|= isoburn_igopt_allow_full_ascii; out_cs= xorriso->out_charset; if(out_cs == NULL) Xorriso_get_local_charset(xorriso, &out_cs, 0); isoburn_igopt_set_level(sopts, xorriso->iso_level); ext= ((!!xorriso->do_rockridge) * isoburn_igopt_rockridge) | ((!!xorriso->do_joliet) * isoburn_igopt_joliet) | ((!!xorriso->do_hfsplus) * isoburn_igopt_hfsplus) | ((!!xorriso->do_fat) * isoburn_igopt_fat) | ((!!xorriso->do_iso1999) * isoburn_igopt_iso1999) | (( !(xorriso->ino_behavior & 2)) * isoburn_igopt_hardlinks) | (( (!(xorriso->ino_behavior & 2)) || (xorriso->do_aaip & (2 | 8 | 16 | 256 | (1 << 11))) || (xorriso->do_md5 & (2 | 4)) || xorriso->do_hfsplus ) * isoburn_igopt_aaip) | ((!!(xorriso->do_md5 & 2)) * isoburn_igopt_session_md5) | ((!!(xorriso->do_md5 & 4)) * isoburn_igopt_file_md5) | ((!!(xorriso->do_md5 & 8)) * isoburn_igopt_file_stability) | ((!!xorriso->do_old_empty) * isoburn_igopt_old_empty) | ((flag & 1) * isoburn_igopt_will_cancel); if(xorriso->no_emul_toc & 1) ext|= isoburn_igopt_no_emul_toc; isoburn_igopt_set_extensions(sopts, ext); isoburn_igopt_set_relaxed(sopts, relax); ret = isoburn_igopt_set_rr_reloc(sopts, xorriso->rr_reloc_dir, xorriso->rr_reloc_flags); if(ret <= 0) {ret= 0; goto ex;} ret= isoburn_igopt_set_untranslated_name_len(sopts, xorriso->untranslated_name_len); if(ret <= 0) {ret= 0; goto ex;} isoburn_igopt_set_sort_files(sopts, 1); isoburn_igopt_set_over_mode(sopts, 0, 0, (mode_t) 0, (mode_t) 0); isoburn_igopt_set_over_ugid(sopts, 2 * !!xorriso->do_global_uid, 2 * !!xorriso->do_global_gid, (uid_t) xorriso->global_uid, (gid_t) xorriso->global_gid); isoburn_igopt_set_out_charset(sopts, out_cs); isoburn_igopt_set_fifo_size(sopts, xorriso->fs * 2048); Ftimetxt(time(NULL), xorriso->scdbackup_tag_time, 8); isoburn_igopt_set_scdbackup_tag(sopts, xorriso->scdbackup_tag_name, xorriso->scdbackup_tag_time, xorriso->scdbackup_tag_written); if(xorriso->prep_partition[0]) { part_image= xorriso->prep_partition; intvl_string= Xorriso_check_intvl_string(xorriso, &part_image, intvl_check); if(intvl_string < 0) {ret= 0; goto ex;} ret= isoburn_igopt_set_prep_partition(sopts, part_image, intvl_string); if(ret <= 0) {ret= 0; goto ex;} } if(xorriso->efi_boot_partition[0]) { part_image= xorriso->efi_boot_partition; intvl_string= Xorriso_check_intvl_string(xorriso, &part_image, intvl_check); if(intvl_string < 0) {ret= 0; goto ex;} ret= isoburn_igopt_set_efi_bootp(sopts, part_image, intvl_string); if(ret <= 0) {ret= 0; goto ex;} } for(i= 0; i < Xorriso_max_appended_partitionS; i++) { if(xorriso->appended_partitions[i] == NULL) continue; if(xorriso->appended_partitions[i][0] == 0) continue; if(strcmp(xorriso->appended_partitions[i], ".") == 0) part_image= ""; else part_image= xorriso->appended_partitions[i]; intvl_string= Xorriso_check_intvl_string(xorriso, &part_image, intvl_check); if(intvl_string < 0) {ret= 0; goto ex;} isoburn_igopt_set_partition_img(sopts, i + 1, xorriso->appended_part_types[i], part_image); isoburn_igopt_set_part_flag(sopts, i + 1, intvl_string); isoburn_igopt_set_part_type_guid(sopts, i + 1, xorriso->appended_part_type_guids[i], xorriso->appended_part_gpt_flags[i] & 1); } isoburn_igopt_set_appended_as_gpt(sopts, xorriso->appended_as_gpt); isoburn_igopt_set_appended_as_apm(sopts, xorriso->appended_as_apm); isoburn_igopt_set_part_like_isohybrid(sopts, xorriso->part_like_isohybrid); isoburn_igopt_set_iso_mbr_part_type(sopts, xorriso->iso_mbr_part_type); isoburn_igopt_set_iso_type_guid(sopts, xorriso->iso_gpt_type_guid, xorriso->iso_mbr_part_flag & 1); isoburn_igopt_set_gpt_guid(sopts, xorriso->gpt_guid, xorriso->gpt_guid_mode); ret= isoburn_igopt_set_max_ce_entries(sopts, xorriso->max_ce_entries, xorriso->max_ce_entries_flag); if(ret <= 0) {ret= 0; goto ex;} isoburn_igopt_set_disc_label(sopts, xorriso->ascii_disc_label); isoburn_igopt_set_hfsp_serial_number(sopts, xorriso->hfsp_serial_number); isoburn_igopt_set_hfsp_block_size(sopts, xorriso->hfsp_block_size, xorriso->apm_block_size); isoburn_igopt_set_pvd_times(sopts, xorriso->vol_creation_time, xorriso->vol_modification_time, xorriso->vol_expiration_time, xorriso->vol_effective_time, xorriso->vol_uuid); #ifdef Xorriso_with_libjtE if(xorriso->libjte_handle && (xorriso->libjte_params_given & (4 | 8))) { /* >>> Check whether the mandatory parameters are set */; ret= libjte_set_outfile(xorriso->libjte_handle, xorriso->outdev); Xorriso_process_msg_queues(xorriso, 0); if(ret <= 0) goto ex; isoburn_igopt_attach_jte(sopts, xorriso->libjte_handle); pad_by_libisofs= 1; } #endif /* Xorriso_with_libjtE */ if(xorriso->do_padding_by_libisofs || pad_by_libisofs) { /* Padding to be done by libisofs, not by libburn. */ padding= xorriso->padding / 2048; if((uint32_t) xorriso->padding > padding * 2048) padding++; /* fprintf(stderr, "XORRISO_DEBUG: isoburn_igopt_set_tail_blocks(%d)\n", (int) padding); */ isoburn_igopt_set_tail_blocks(sopts, padding); } /* Make final abort check before starting expensive activities */ ret= Xorriso_eval_problem_status(xorriso, 1, 0); if(ret<0) {ret= 0; goto ex;} if(xorriso->zisofs_by_magic && image != NULL) { sprintf(xorriso->info_text, "Checking disk file content for zisofs compression headers."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0); root_node= (IsoNode *) iso_image_get_root(image); ret= iso_node_zf_by_magic(root_node, (xorriso->out_drive_handle == xorriso->in_drive_handle) | 2 | 16 | (xorriso->zisofs_by_magic << 8)); if(ret<0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when examining file content for zisofs headers", 0, "FAILURE", 1); } ret= Xorriso_eval_problem_status(xorriso, 1, 0); if(ret<0) {ret= 0; goto ex;} sprintf(xorriso->info_text, "Check for zisofs compression headers done."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0); } ret = isoburn_igopt_set_write_type(sopts, xorriso->do_tao); if(ret <= 0) goto ex; ret = isoburn_igopt_set_stdio_endsync(sopts, xorriso->stdio_sync >= 0); if(ret <= 0) goto ex; ret= 1; ex:; Xorriso_process_msg_queues(xorriso, 0); return(ret); } int Xorriso_set_all_file_dates(struct XorrisO *xorriso, int flag) { int idx, ret, was_failure= 0; char *hargv[4]; if(xorriso->all_file_dates[0] == 0) return(2); if(strcmp(xorriso->all_file_dates, "set_to_mtime") == 0) { hargv[0]= "/"; hargv[1]= "-exec"; hargv[2]= "set_to_mtime"; hargv[3]= "--"; idx= 0; ret= Xorriso_option_find(xorriso, 4, hargv, &idx, 0); if(ret <= 0) was_failure= 1; } else { hargv[0]= "/"; idx= 0; ret= Xorriso_option_alter_date(xorriso, "b", xorriso->all_file_dates, 1, hargv, &idx, 1); if(ret <= 0) was_failure= 1; idx= 0; ret= Xorriso_option_alter_date(xorriso, "c", xorriso->all_file_dates, 1, hargv, &idx, 1); if(ret <= 0) was_failure= 1; } Xorriso_relax_compliance(xorriso, "always_gmt", 0); return(!was_failure); } /* @return 0= no EFI , 1= EFI but no warning needed , 2= warning was issued */ int Xorriso_warn_efi_boot_dir(struct XorrisO *xorriso, IsoImage *image, int flag) { int ret, num_boots, i, filec= 0; off_t mem= 0; char *patterns[1], **filev= NULL; ElToritoBootImage **boots = NULL; IsoFile **bootnodes = NULL; IsoNode *node; /* Check El Torito images for EFI */ ret= iso_image_get_all_boot_imgs(image, &num_boots, &boots, &bootnodes, 0); Xorriso_process_msg_queues(xorriso,0); if(ret == 1) { for(i= 0; i <num_boots; i++) { if(el_torito_get_boot_platform_id(boots[i]) == 0xef) goto has_efi; } } /* Check appended partitions for EFI */ for(i= 0; i < Xorriso_max_appended_partitionS; i++) { if(xorriso->appended_partitions[i] == NULL) continue; if(xorriso->appended_partitions[i][0] == 0) continue; if(xorriso->appended_part_types[i] == 0xef) goto has_efi; } /* No EFI found */ ret= 0; goto ex; has_efi:; /* Check for /[Ee][Ff][Ii]/[Bb][Oo][Oo][Tt]/ in ISO tree */ patterns[0]= "/[Ee][Ff][Ii]/[Bb][Oo][Oo][Tt]"; ret= Xorriso_expand_pattern(xorriso, 1, patterns, 0, &filec, &filev, &mem, 0); if(ret > 0 && filec > 0) { ret= Xorriso_node_from_path(xorriso, image, filev[0], &node, 1); if(ret > 0) { if(LIBISO_ISDIR(node)) { ret= 1; goto ex; } } } Xorriso_msgs_submit(xorriso, 0, "EFI boot equipment is provided but no directory /EFI/BOOT", 0, "WARNING", 0); Xorriso_msgs_submit(xorriso, 0, "will emerge in the ISO filesystem. A popular method to", 0, "WARNING", 0); Xorriso_msgs_submit(xorriso, 0, "prepare a USB stick on MS-Windows relies on having in the", 0, "WARNING", 0); Xorriso_msgs_submit(xorriso, 0, "ISO filesystem a copy of the EFI System Partition tree.", 0, "WARNING", 0); ret= 2; ex:; Sfile_destroy_argv(&filec, &filev, 0); if(boots != NULL) free(boots); if(bootnodes != NULL) free(bootnodes); return(ret); } /* @param flag bit0= do not write but only prepare and return size in sectors @return <=0 error , 1= success 2= failure with DVD-RW, please call Xorriso_retry_write_session() */ off_t Xorriso_write_session(struct XorrisO *xorriso, int flag) { int ret, i, pacifier_speed= 0, is_bootable= 0; off_t data_lba; int freshly_bootable= 0, hide_attr, signal_mode, role, is_bdr_pow= 0; char *xorriso_id= NULL, *img_id, *sfe= NULL, *out_cs; struct isoburn_imgen_opts *sopts= NULL; struct burn_drive_info *dinfo, *source_dinfo; struct burn_drive *drive, *source_drive; struct burn_disc *disc= NULL; struct burn_write_opts *burn_options= NULL; off_t readcounter= 0,writecounter= 0; int num_sessions= 0, num_tracks= 0; struct burn_session **sessions; struct burn_track **tracks; enum burn_disc_status s; IsoImage *image= NULL; int profile_number; char *profile_name= NULL, *reasons= NULL; IsoBoot *bootcat_node; Xorriso_alloc_meM(sfe, char, 5 * SfileadrL); Xorriso_alloc_meM(xorriso_id, char, 256); Xorriso_alloc_meM(profile_name, char, 80); Xorriso_alloc_meM(reasons, char, BURN_REASONS_LEN); ret= Xorriso_finish_hl_update(xorriso, 0); if(ret <= 0) goto ex; ret= Xorriso_set_all_file_dates(xorriso, 1); if(ret <= 0) goto ex; out_cs= xorriso->out_charset; if(out_cs == NULL) Xorriso_get_local_charset(xorriso, &out_cs, 0); ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to write", 2); if(ret<=0) goto ex; if(!(flag & 1)) { ret= Xorriso_auto_format(xorriso, 0); if(ret <=0 ) {ret= 0; goto ex;} } is_bdr_pow= burn_drive_get_bd_r_pow(drive); if(is_bdr_pow) { sprintf(xorriso->info_text, "May not write to Pseudo Overwrite formatted BD-R medium"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } s= isoburn_disc_get_status(drive); if (xorriso->do_hfsplus && ( (xorriso->grow_blindly_msc2 >= 0 && xorriso->out_drive_handle != xorriso->in_drive_handle) || (xorriso->out_drive_handle == xorriso->in_drive_handle && s != BURN_DISC_BLANK) )) { sprintf(xorriso->info_text, "May not grow ISO image while -hfsplus is enabled"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(xorriso->out_drive_handle == xorriso->in_drive_handle) { if(abs(xorriso->displacement_sign) == 1 && xorriso->displacement != 0 && s != BURN_DISC_BLANK) { sprintf(xorriso->info_text, "May not grow ISO image while -displacement is non-zero"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } source_drive= drive; } else { if(xorriso->in_drive_handle == NULL) { source_drive= drive; } else { ret= Xorriso_get_drive_handles(xorriso, &source_dinfo, &source_drive, "on attempt to get source for write", 0); if(ret<=0) goto ex; } if(s!=BURN_DISC_BLANK) { s= burn_disc_get_status(drive); if(s!=BURN_DISC_BLANK) sprintf(xorriso->info_text, "-indev differs from -outdev and -outdev media is not blank"); else sprintf(xorriso->info_text, "-indev differs from -outdev and -outdev media holds non-zero data"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } } ret= Xorriso_get_profile(xorriso, &profile_number, profile_name, 2); if(ret == 2) pacifier_speed= 1; else if(ret == 3) pacifier_speed= 2; ret= Xorriso_check_multi(xorriso, drive, 0); if(ret<=0) goto ex; ret= isoburn_igopt_new(&sopts, 0); if(ret<=0) { Xorriso_process_msg_queues(xorriso, 0); goto ex; } xorriso->alignment= 0; image= isoburn_get_attached_image(source_drive); if(image != NULL) { iso_image_set_application_id(image, xorriso->application_id); iso_image_set_publisher_id(image, xorriso->publisher); iso_image_set_system_id(image, xorriso->system_id); iso_image_set_volset_id(image, xorriso->volset_id); iso_image_set_copyright_file_id(image, xorriso->copyright_file); iso_image_set_biblio_file_id(image, xorriso->biblio_file); iso_image_set_abstract_file_id(image, xorriso->abstract_file); Xorriso_write_application_use(xorriso, image, 0); Xorriso_process_msg_queues(xorriso,0); } if((xorriso->do_aaip & 256) && out_cs != NULL) { static char *names = "isofs.cs"; size_t value_lengths[1]; value_lengths[0]= strlen(out_cs); ret= Xorriso_setfattr(xorriso, NULL, "/", (size_t) 1, &names, value_lengths, &out_cs, 2 | 8); if(ret<=0) goto ex; } if(iso_image_was_blind_attrs(image, 0)) Xorriso_msgs_submit(xorriso, 0, "Some file xattr namespace could not be explored", 0, "WARNING", 0); if(image!=NULL && 12+strlen(Xorriso_timestamP)<80) { strcpy(xorriso_id, xorriso->preparer_id); img_id= (char *) iso_image_get_data_preparer_id(image); if(img_id!=NULL) { for(i= strlen(img_id)-1; i>=0 && img_id[i]==' '; i--); if(i>0) { sprintf(xorriso->info_text, "Overwrote previous preparer id '%s'", img_id); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } } iso_image_set_data_preparer_id(image, xorriso_id); } ret= Xorriso_set_system_area(xorriso, source_drive, drive, image, sopts, 0); if(ret <= 0) goto ex; /* Activate, adjust or discard boot image */ if(image!=NULL) { if(xorriso->boot_image_bin_path[0]) { ret= Xorriso_attach_boot_image(xorriso, xorriso->boot_count == 0); if(ret <= 0) goto ex; freshly_bootable= 1; } is_bootable= iso_image_get_boot_image(image, NULL, NULL, &bootcat_node); } if(image!=NULL && !(flag&1)) { if(xorriso->boot_count > 0 || freshly_bootable) { /* Eventually rename boot catalog node to changed boot_image_cat_path */ if(is_bootable > 0) { ret= Xorriso_path_from_node(xorriso, (IsoNode *) bootcat_node, sfe, 0); if(ret > 0) { if(strcmp(sfe, xorriso->boot_image_cat_path) != 0) { ret= Xorriso_rename(xorriso, NULL, sfe, xorriso->boot_image_cat_path, 1); if(ret <= 0) goto ex; } } } hide_attr= !!(xorriso->boot_image_cat_hidden); if(xorriso->boot_image_cat_hidden & 1) hide_attr|= LIBISO_HIDE_ON_RR; if(xorriso->boot_image_cat_hidden & 2) hide_attr|= LIBISO_HIDE_ON_JOLIET; if(xorriso->boot_image_cat_hidden & 4) hide_attr|= LIBISO_HIDE_ON_HFSPLUS; iso_image_set_boot_catalog_hidden(image, hide_attr); } else if(xorriso->patch_isolinux_image & 1) { if(is_bootable == 1) { /* will imply isoburn_igopt_allow_full_ascii */ sprintf(xorriso->info_text, "Patching boot info table"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); ret= Xorriso_path_from_lba(xorriso, NULL, (off_t) xorriso->loaded_boot_bin_lba, sfe, 1); if(ret < 0) goto ex; if(ret == 0) { sprintf(xorriso->info_text, "Cannot patch boot image: no file found for its LBA."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text, "Probably the loaded boot image file was deleted in this session."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text, "Use -boot_image \"any\" \"discard\" or set new boot image"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); goto ex; } ret= Xorriso_set_isolinux_options(xorriso, image, 0); if(ret <= 0) goto ex; } else if(!freshly_bootable) { sprintf(xorriso->info_text, "Could not find any boot image for -boot_image patching"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); } } else if(xorriso->keep_boot_image && is_bootable == 1) { /* will imply isoburn_igopt_allow_full_ascii */ sprintf(xorriso->info_text, "Keeping boot image unchanged"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } else if(is_bootable == 1) { iso_image_remove_boot_image(image); sprintf(xorriso->info_text, "Discarded boot image from old session"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } /* hardcoded and regardless whether a catalog will get written */ iso_image_set_boot_catalog_weight(image, 1000000000); } if((xorriso->do_aaip & 16) || !(xorriso->ino_behavior & 2)) { /* Overwrite isofs.st of root node by xorriso->isofs_st_out */ char *name= "isofs.st"; char timestamp[16], *value= timestamp; size_t value_length; sprintf(timestamp, "%.f", (double) xorriso->isofs_st_out); value_length= strlen(timestamp); Xorriso_setfattr(xorriso, NULL, "/", (size_t) 1, &name, &value_length, &value, 2 | 8); } Xorriso_warn_efi_boot_dir(xorriso, image, 0); ret= Xorriso_make_iso_write_opts(xorriso, image, sopts, flag & 1); if(ret <= 0) goto ex; /* >>> omit iso_image_update_sizes if the image was filled up very quickly */; ret= iso_image_update_sizes(image); if(ret < 0) { Xorriso_process_msg_queues(xorriso, 0); if(ret<0) { Xorriso_report_iso_error(xorriso, "", ret, "Error when updating file sizes", 0, "FAILURE", 1); } ret= Xorriso_eval_problem_status(xorriso, 1, 0); if(ret<0) {ret= 0; goto ex;} } Xorriso_set_abort_severity(xorriso, 1); if (xorriso->grow_blindly_msc2 >= 0 && xorriso->out_drive_handle != xorriso->in_drive_handle) { ret= isoburn_prepare_blind_grow_v2(source_drive, &disc, sopts, drive, xorriso->grow_blindly_msc2); if(ret>0) { /* Allow the consumer of output to access the input drive */ source_drive= NULL; ret= Xorriso_give_up_drive(xorriso, 1|8); if(ret<=0) goto ex; } } else if(xorriso->out_drive_handle == xorriso->in_drive_handle || xorriso->in_drive_handle == NULL) { ret= isoburn_prepare_disc(source_drive, &disc, sopts); } else { ret= isoburn_prepare_new_image(source_drive, &disc, sopts, drive); } if(ret <= 0) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text,"Failed to prepare session write run"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } ret= Xorriso_make_write_options(xorriso, drive, &burn_options, 0); if(ret<=0) goto cancel_iso; isoburn_igopt_get_effective_lba_v2(sopts, &(xorriso->session_lba)); if(xorriso->do_stream_recording == 2) { ret= isoburn_igopt_get_data_start_v2(sopts, &data_lba); if(ret > 0 && data_lba >= 16 && data_lba < (int) 0x7fffffff) burn_write_opts_set_stream_recording(burn_options, (int) data_lba); } ret= Xorriso_sanitize_image_size(xorriso, drive, disc, burn_options, flag&1); if(ret<=0 || (flag&1)) { Xorriso_process_msg_queues(xorriso,0); if(flag&1) /* set queue severity to FAILURE */ Xorriso_set_image_severities(xorriso, 2); if(flag&1) /* reset queue severity */ Xorriso_set_image_severities(xorriso, 0); if(flag & 1) xorriso->print_size_counter++; goto cancel_iso; } ret= Xorriso_may_burn(xorriso, 0); if(ret <= 0) goto cancel_iso; role= burn_drive_get_drive_role(drive); /* Important: do not return until burn_is_aborting() was checked */ signal_mode= 1; if(role == 1) signal_mode|= 2; Xorriso_set_signal_handling(xorriso, signal_mode); /* De-activate eventual target file truncation in dummy mode */ ret= isoburn_set_truncate(drive, (!xorriso->do_dummy) | 2 | 4); if(ret < 0) goto cancel_iso; xorriso->run_state= 1; /* Indicate that burning has started */ isoburn_disc_write(burn_options, disc); burn_write_opts_free(burn_options); burn_options= NULL; ret= Xorriso_pacifier_loop(xorriso, drive, pacifier_speed << 4); if(burn_is_aborting(0)) Xorriso_abort(xorriso, 0); /* Never comes back */ Xorriso_set_signal_handling(xorriso, 0); if(ret<=0) goto ex; if(!isoburn_drive_wrote_well(drive)) { isoburn_cancel_prepared_write(source_drive, drive, 0); Xorriso_process_msg_queues(xorriso,0); if(xorriso->auto_close && xorriso->do_close == 0) { if(burn_drive_was_feat21_failure(drive)) { sprintf(xorriso->info_text, "libburn indicates failure with writing DVD-RW to appendable state."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); /* Urge caller to call Xorriso_retry_write_session() */ ret= 2; goto ex; } } sprintf(xorriso->info_text, "libburn indicates failure with writing."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } Xorriso_process_msg_queues(xorriso,0); sessions= burn_disc_get_sessions(disc, &num_sessions); if(num_sessions>0) { tracks= burn_session_get_tracks(sessions[0], &num_tracks); if(tracks!=NULL && num_tracks>0) { burn_track_get_counters(tracks[0],&readcounter,&writecounter); xorriso->session_blocks= writecounter / 2048.0; sprintf(xorriso->info_text, "ISO image produced: %.f sectors\nWritten to medium : %.f sectors at LBA %.f\n", ((double) readcounter) / 2048.0, (double) xorriso->session_blocks, (double) xorriso->session_lba); Xorriso_info(xorriso, 0); } } ret= isoburn_activate_session(drive); Xorriso_process_msg_queues(xorriso,0); if(ret<=0) { sprintf(xorriso->info_text, "Could not write new set of volume descriptors"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); goto ex; } /* Done early to free any reference to the libisofs resources via disc */ if(disc!=NULL) burn_disc_free(disc); disc= NULL; /* To wait for the end of the libisofs threads and their messages. */ isoburn_sync_after_write(source_drive, drive, 0); Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "Writing to %s completed successfully.\n\n", Text_shellsafe(xorriso->outdev,sfe,0)); Xorriso_info(xorriso, 0); xorriso->write_session_counter++; ret= 1; ex:; xorriso->run_state= 0; /* Indicate that burning has ended */ Xorriso_set_abort_severity(xorriso, 0); if(ret<=0) { /* >>> ??? revive discarded boot image */; /* suppress automatic -commit at program end */ xorriso->volset_change_pending= 3; } if(disc!=NULL) burn_disc_free(disc); if(image != NULL) iso_image_unref(image); isoburn_igopt_destroy(&sopts, 0); if(burn_options != NULL) burn_write_opts_free(burn_options); Xorriso_process_msg_queues(xorriso,0); Xorriso_append_scdbackup_record(xorriso, 0); Xorriso_free_meM(sfe); Xorriso_free_meM(xorriso_id); Xorriso_free_meM(profile_name); Xorriso_free_meM(reasons); return(ret); cancel_iso:; isoburn_cancel_prepared_write(source_drive, drive, 0); goto ex; } int Xorriso_check_burn_abort(struct XorrisO *xorriso, int flag) { int ret; struct burn_drive_info *dinfo; struct burn_drive *drive; if(burn_is_aborting(0)) return(2); if(xorriso->run_state!=1) return(0); ret= Xorriso_eval_problem_status(xorriso, 1, 1); if(ret>=0) return(0); sprintf(xorriso->info_text, "-abort_on '%s' encountered '%s' during image writing", xorriso->abort_on_text, xorriso->problem_status_text); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, xorriso->problem_status_text, 0); ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to abort burn run", 2); if(ret<=0) return(0); burn_drive_cancel(drive); sprintf(xorriso->info_text, "libburn has now been urged to cancel its operation"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(1); } /* This loop watches burn runs until they end. It issues pacifying update messages to the user. @param flag bit0-3 = emulation mode 0= xorriso 1= mkisofs 2= cdrecord bit4= report speed in CD units bit5= report speed in BD units */ int Xorriso_pacifier_loop(struct XorrisO *xorriso, struct burn_drive *drive, int flag) { int ret, size, free_bytes, i, aborting= 0, emul, buffer_fill= 50; int iso_wait_counter= 0, iso_cancel_limit= 5; struct burn_progress_v2 progress; off_t last_sector; char *status_text, date_text[80], *speed_unit, mem_text[8]; enum burn_drive_status drive_status; double start_time, current_time, last_time, base_time= 0.0, base_count= 0.0; double next_base_time= 0.0, next_base_count= 0.0, first_base_time= 0.0; double first_base_count= 0.0, norm= 0.0, now_time, fract_offset= 0.0; double measured_speed, speed_factor= 1385000, quot, speed_min_time; double tdiff, now_fract; time_t time_prediction; IsoImage *image= NULL; image= isoburn_get_attached_image(drive); start_time= Sfile_microtime(0); while(burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) usleep(100002); emul= flag&15; if(emul==0) emul= xorriso->pacifier_style; fract_offset= 1.0 / 3.0 * (double) emul - ((int) (1.0 / 3.0 * (double) emul)); speed_min_time= 0.2 * xorriso->pacifier_interval; speed_unit= "D"; if(flag&16) { speed_factor= 150.0*1024; speed_unit= "C"; } else if(flag & 32) { speed_factor= 4495625; speed_unit= "B"; } progress.sector= 0; current_time= Sfile_microtime(0); measured_speed= 0.0; while(1) { last_time= current_time; last_sector= progress.sector; drive_status= burn_drive_get_status_v2(drive, &progress); if(drive_status == BURN_DRIVE_IDLE) { /* To avoid a race condition between burn_source and libisofs writer thread: Wait for ISO generator thread to have finished. After some seconds kick it by isoburn_cancel_prepared_write() which waits for the write thread to end. */ if(image == NULL) break; if(!iso_image_generator_is_running(image)) break; iso_wait_counter++; if(iso_wait_counter > iso_cancel_limit) { isoburn_cancel_prepared_write(drive, NULL, 0); break; } } current_time= Sfile_microtime(0); if(drive_status == BURN_DRIVE_WRITING && progress.sectors > 0) { if(current_time-last_time > speed_min_time) measured_speed= (progress.sector - last_sector) * 2048.0 / (current_time - last_time); buffer_fill= 50; if(progress.buffer_capacity>0) buffer_fill= (double) (progress.buffer_capacity - progress.buffer_available) * 100.0 / (double) progress.buffer_capacity; if(emul==2) { if(progress.sector<=progress.sectors) sprintf(xorriso->info_text, "%4.f of %4.f MB written", (double) (progress.sector / 512), (double) ( progress.sectors / 512)); else sprintf(xorriso->info_text, "%4.f MB written", (double) (progress.sector / 512)); if(xorriso->pacifier_fifo!=NULL) ret= burn_fifo_inquire_status(xorriso->pacifier_fifo, &size, &free_bytes, &status_text); else ret= isoburn_get_fifo_status(drive, &size, &free_bytes, &status_text); if(ret>0 ) sprintf(xorriso->info_text+strlen(xorriso->info_text), " (fifo %2d%%)", (int) (100.0-100.0*((double) free_bytes)/(double) size)); sprintf(xorriso->info_text+strlen(xorriso->info_text), " [buf %3d%%]", buffer_fill); if(current_time-last_time > speed_min_time) sprintf(xorriso->info_text+strlen(xorriso->info_text), " %4.1fx.", measured_speed/speed_factor); } else if(emul == 1 && progress.sectors > 0 && progress.sector <= progress.sectors) { /* "37.87% done, estimate finish Tue Jul 15 18:55:07 2008" */ quot= ((double) progress.sector) / ((double) progress.sectors); sprintf(xorriso->info_text, " %2.2f%% done", quot*100.0); if(current_time - start_time >= 2 && quot > 0.0 && (quot >= 0.02 || progress.sector >= 5*1024)) { if(base_time == 0.0 && progress.sector >= 16*1024) { first_base_time= base_time= next_base_time= current_time; first_base_count= next_base_count= progress.sector; } else if(next_base_time > 0 && current_time - next_base_time >= 10) { base_time= next_base_time; base_count= next_base_count; next_base_time= current_time; next_base_count= progress.sector; } if(first_base_time > 0 && current_time - first_base_time >= 10 && progress.sectors > first_base_count && progress.sector > first_base_count) { norm= (1.0 - quot); if(norm < 0.0001) norm= 0.0001; quot= ((double) progress.sector - first_base_count) / ((double) progress.sectors - first_base_count); time_prediction= norm * (1.0 - quot) / quot * (current_time - first_base_time); } else { time_prediction= (1.0 - quot) / quot * (current_time - start_time); norm= 1.0; } if(base_time > 0 && current_time - base_time >= 10 && progress.sectors > base_count) { quot= ((double) progress.sector - base_count) / ((double) progress.sectors - base_count); time_prediction+= (1.0 - quot) / quot * (current_time - base_time); norm+= 1.0; } time_prediction/= norm; if(time_prediction < 30*86400 && time_prediction > 0) { time_prediction+= current_time + 1; Ftimetxt(time_prediction, date_text, 4); sprintf(xorriso->info_text+strlen(xorriso->info_text), ", estimate finish %s", date_text); } } } else { if(progress.sector<=progress.sectors) { if(progress.sectors <= 0) strcpy(mem_text, " 99.9"); else sprintf(mem_text, "%5.1f", 100.0 * ((double) progress.sector) / ((double) progress.sectors)); mem_text[5]= 0; sprintf(xorriso->info_text, "Writing: %10.fs %s%% ", (double) progress.sector, mem_text); } else { Sfile_scale(2048.0 * (double) progress.sector, mem_text, 5, 1e4, 1); sprintf(xorriso->info_text, "Writing: %10.fs %s ", (double) progress.sector, mem_text); } ret= isoburn_get_fifo_status(drive, &size, &free_bytes, &status_text); if(ret>0 ) sprintf(xorriso->info_text+strlen(xorriso->info_text), " fifo %3d%% buf %3d%%", (int) (100.0-100.0*((double) free_bytes)/(double) size), buffer_fill); if(current_time - last_time > speed_min_time) sprintf(xorriso->info_text+strlen(xorriso->info_text), " %5.1fx%s ", measured_speed/speed_factor, speed_unit); } } else if(drive_status == BURN_DRIVE_CLOSING_SESSION || drive_status == BURN_DRIVE_CLOSING_TRACK) sprintf(xorriso->info_text, "Closing track/session. Working since %.f seconds", current_time-start_time); else if(drive_status == BURN_DRIVE_FORMATTING) sprintf(xorriso->info_text, "Formatting. Working since %.f seconds", current_time-start_time); else sprintf(xorriso->info_text, "Thank you for being patient. Working since %.f seconds.", current_time-start_time); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0); for(i= 0; i < 20; i++) { /* 10 usleeps more than supposed to be needed */ Xorriso_process_msg_queues(xorriso, 0); if(aborting<=0) aborting= Xorriso_check_burn_abort(xorriso, 0); usleep((unsigned long) (100000.0 * xorriso->pacifier_interval)); now_time= Sfile_microtime(0); tdiff= ((off_t)(now_time / xorriso->pacifier_interval)) - (off_t)(current_time / xorriso->pacifier_interval); now_fract= (now_time / xorriso->pacifier_interval - (off_t)(now_time / xorriso->pacifier_interval)); if(tdiff < 1.0) continue; if(fract_offset <= 0.0) /* "xorriso" pacifier shall not wait for slot */ break; if((now_fract >= fract_offset && now_fract < fract_offset + 0.3) || tdiff >= 2.0) break; } } iso_image_unref(image); return(1); } /* @param flag bit0= fast bit1= deformat bit2= do not re-aquire drive @return 0=failure, did not touch medium , -1=failure, altered medium 1=success, altered medium , 2=success, did not touch medium */ int Xorriso_blank_media(struct XorrisO *xorriso, int flag) { int ret, do_deformat= 0, signal_mode, using_immed; struct burn_drive_info *dinfo; struct burn_drive *drive; enum burn_disc_status disc_state; struct burn_progress_v2 p; double percent = 1.0; int current_profile; char current_profile_name[80]; time_t start_time; char mode_names[4][80]= {"all", "fast", "deformat", "deformat_quickest"}; char progress_text[40]; ret= Xorriso_may_burn(xorriso, 0); if(ret <= 0) return(0); ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to -blank", 2); if(ret<=0) return(0); burn_disc_get_profile(drive, ¤t_profile, current_profile_name); disc_state = isoburn_disc_get_status(drive); if(current_profile == 0x13) { /* overwritable DVD-RW */ /* Depending on flag bit1 formatted DVD-RW will get blanked to sequential state or pseudo blanked by invalidating an eventual ISO image. */ if(flag&2) do_deformat= 1; } else if(current_profile == 0x14) { /* sequential DVD-RW */ if((flag&1) && !(flag&2)) { sprintf(xorriso->info_text, "-blank: DVD-RW present. Mode 'fast' defaulted to mode 'all'."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); sprintf(xorriso->info_text, "Mode 'deformat_quickest' produces single-session-only media."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); flag&= ~1; } } if(disc_state == BURN_DISC_BLANK) { if(!do_deformat) { sprintf(xorriso->info_text, "Blank medium detected. Will leave it untouched"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return 2; } } else if(disc_state==BURN_DISC_FULL || disc_state==BURN_DISC_APPENDABLE) { ; } else if(disc_state == BURN_DISC_EMPTY) { sprintf(xorriso->info_text,"No media detected in drive"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return 0; } else { sprintf(xorriso->info_text, "Unsuitable drive and media state"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return 0; } if(!isoburn_disc_erasable(drive)) { sprintf(xorriso->info_text, "Media is not of erasable type"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return 0; } if(xorriso->do_dummy) { sprintf(xorriso->info_text, "-dummy mode prevents blanking of medium in mode '%s'.", mode_names[flag&3]); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(1); } using_immed= burn_drive_get_immed(drive); sprintf(xorriso->info_text, "Beginning to blank medium in mode '%s'.\n", mode_names[flag&3]); Xorriso_info(xorriso,0); /* Important: do not return until burn_is_aborting() was checked */ signal_mode= 1; ret= burn_drive_get_drive_role(drive); if(ret == 1) signal_mode|= 2; Xorriso_set_signal_handling(xorriso, signal_mode); if(do_deformat) burn_disc_erase(drive, (flag&1)); else isoburn_disc_erase(drive, (flag&1)); start_time= time(0); usleep(1000000); if(!using_immed) sprintf(progress_text, "synchronously since"); while (burn_drive_get_status_v2(drive, &p) != BURN_DRIVE_IDLE) { Xorriso_process_msg_queues(xorriso,0); if(p.sectors>0 && p.sector>=0) /* display 1 to 99 percent */ percent = 1.0 + ((double) p.sector+1.0) / ((double) p.sectors) * 98.0; if(using_immed) sprintf(progress_text, "%.1f%% done in", percent); sprintf(xorriso->info_text, "Blanking ( %s %d seconds )", progress_text, (int) (time(0) - start_time)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0); usleep(1000000); } Xorriso_process_msg_queues(xorriso,0); if(burn_is_aborting(0)) Xorriso_abort(xorriso, 0); /* Never comes back */ Xorriso_set_signal_handling(xorriso, 0); if(burn_drive_wrote_well(drive)) { sprintf(xorriso->info_text, "Blanking done\n"); Xorriso_info(xorriso,0); } else { sprintf(xorriso->info_text, "Blanking failed."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } if(!(flag & 4)) { ret= Xorriso_reaquire_outdev(xorriso, 2 + (xorriso->in_drive_handle == xorriso->out_drive_handle)); if(ret <= 0) return(-1); } return(1); } /* @param flag bit0= try to achieve faster formatting bit1= use parameter size (else use default size) bit2= do not re-aquire drive bit5= try to disable Defect Management bit7= by_index mode: bit8 to bit15 contain the index of the format to use. @return 0=failure, did not touch medium , -1=failure, altered medium 1=success, altered medium , 2=success, did not touch medium */ int Xorriso_format_media(struct XorrisO *xorriso, off_t in_size, int flag) { int ret, mode_flag= 0, index, status, num_formats, signal_mode, using_immed; unsigned dummy; struct burn_drive_info *dinfo; struct burn_drive *drive; struct burn_progress_v2 p; double percent = 1.0; int current_profile; char current_profile_name[80], progress_text[40]; off_t size= 0; time_t start_time; enum burn_disc_status disc_state; ret= Xorriso_may_burn(xorriso, 0); if(ret <= 0) return(0); ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to -format", 2); if(ret<=0) return(0); if(flag & 2) { mode_flag= 0; /* format to given size */ } else { mode_flag= 4; /* format to full size */ } if(flag & 32) mode_flag|= 32; /* try to disable Defect Management */ burn_disc_get_profile(drive, ¤t_profile, current_profile_name); if(flag&128) { /* by_index */ index= (flag>>8) & 0xff; ret= burn_disc_get_formats(drive, &status, &size, &dummy, &num_formats); if(ret<=0) num_formats= 0; if(ret<=0 || index<0 || index>=num_formats) { if(num_formats>0) sprintf(xorriso->info_text, "-format by_index_%d: format descriptors range from index 0 to %d", index, num_formats-1); else sprintf(xorriso->info_text, "-format by_index_%d: no format descriptors available", index); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } mode_flag|= (flag & 0xff80); if(flag&1) mode_flag|= (1<<6); } else if(current_profile == 0x12) { /* DVD+RAM */ if(!(flag & 2)) mode_flag= 6; /* format to default payload size */ if(flag&1) mode_flag|= (1<<6); } else if(current_profile == 0x13) { /* DVD-RW */ if(flag&1) { sprintf(xorriso->info_text, "Detected formatted DVD-RW. Thus omitting desired fast format run."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(2); } } else if(current_profile == 0x14) { /* DVD-RW sequential */ if(flag & 1) { size= 128*1024*1024; mode_flag= 1; /* format to size, then write size of zeros */ } else mode_flag= 4; } else if(current_profile == 0x1a) { /* DVD+RW */ if(flag&1) { sprintf(xorriso->info_text, "Detected DVD+RW. Thus omitting desired fast format run."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(2); } } else if(current_profile == 0x41) { /* BD-R SRM */ if(!(flag & 2)) mode_flag= 6; /* format to default payload size */ if(flag&1) mode_flag|= (1<<6); } else if(current_profile == 0x43) { /* BD-RE */ if(!(flag & (2 | 32))) mode_flag= 6; /* format to default payload size */ if(flag&1) mode_flag|= (1<<6); } else { sprintf(xorriso->info_text, "-format: Unsuitable media detected."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); sprintf(xorriso->info_text,"Media current: %s (%4.4xh)", current_profile_name, current_profile); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(0); } if(!(flag & 1)) mode_flag|= 16; /* enable re-formatting */ if(xorriso->do_dummy) { sprintf(xorriso->info_text, "-dummy mode prevents formatting of medium."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(1); } using_immed= burn_drive_get_immed(drive); sprintf(xorriso->info_text, "Beginning to format medium.\n"); Xorriso_info(xorriso, 0); if(flag & 2) size= in_size; /* Important: do not return until burn_is_aborting() was checked */ signal_mode= 1; ret= burn_drive_get_drive_role(drive); if(ret == 1) signal_mode|= 2; Xorriso_set_signal_handling(xorriso, signal_mode); burn_disc_format(drive, size, mode_flag); start_time= time(0); usleep(1000000); if(!using_immed) sprintf(progress_text, "synchronously since"); while (burn_drive_get_status_v2(drive, &p) != BURN_DRIVE_IDLE) { Xorriso_process_msg_queues(xorriso,0); if(p.sectors>0 && p.sector>=0) /* display 1 to 99 percent */ percent = 1.0 + ((double) p.sector+1.0) / ((double) p.sectors) * 98.0; if(using_immed) sprintf(progress_text, "%.1f%% done in", percent); sprintf(xorriso->info_text, "Formatting ( %s %d seconds )", progress_text, (int) (time(0) - start_time)); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0); usleep(1000000); } Xorriso_process_msg_queues(xorriso,0); if(burn_is_aborting(0)) Xorriso_abort(xorriso, 0); /* Never comes back */ Xorriso_set_signal_handling(xorriso, 0); if(burn_drive_wrote_well(drive)) { sprintf(xorriso->info_text, "Formatting done\n"); Xorriso_info(xorriso,0); } else { sprintf(xorriso->info_text, "libburn indicates failure with formatting."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(-1); } if(!(flag & 4)) { ret= Xorriso_reaquire_outdev(xorriso, 2 + (xorriso->in_drive_handle == xorriso->out_drive_handle)); if(ret <= 0) return(-1); } disc_state = isoburn_disc_get_status(drive); if(disc_state==BURN_DISC_FULL && !(flag&1)) { /* Blank because full format certification pattern might be non-zero */ ret= Xorriso_blank_media(xorriso, 1); if(ret <= 0) return(0); } return(1); } /* @param flag bit2= formatting rather than blanking @return 0=failure, did not touch medium , -1=failure, altered medium 1=success, altered medium , 2=success, did not touch medium */ int Xorriso_blank_as_needed(struct XorrisO *xorriso, int flag) { int ret, is_formatted= -1, status, num_formats, did_work= 0; struct burn_drive_info *dinfo; struct burn_drive *drive; enum burn_disc_status disc_state; unsigned dummy; int current_profile; char current_profile_name[80]; off_t size; ret= Xorriso_may_burn(xorriso, 0); if(ret <= 0) return(0); ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to blank or format", 2); if(ret<=0) return(0); burn_disc_get_profile(drive, ¤t_profile, current_profile_name); ret= burn_disc_get_formats(drive, &status, &size, &dummy, &num_formats); if(ret>0) { if(status==BURN_FORMAT_IS_FORMATTED) is_formatted= 1; else if(status == BURN_FORMAT_IS_UNFORMATTED) is_formatted= 0; } if(current_profile == 0x12 || current_profile == 0x43) { /* DVD+RAM , BD-RE */ if(is_formatted<0) { sprintf(xorriso->info_text, "-blank or -format: Unclear formatting status of %s", current_profile_name); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } if(!is_formatted) { ret= Xorriso_format_media(xorriso, (off_t) 0, (current_profile == 0x43)); if(ret <= 0) return(ret); did_work= (ret == 1); } } else if(current_profile == 0x14 && (flag&4)) { /* DVD-RW sequential */ ret= Xorriso_format_media(xorriso, (off_t) 0, 0); if(ret <= 0) return(ret); did_work= (ret == 1); } else if(current_profile == 0x41) { /* BD-R SRM */ if((flag & 4) && !is_formatted) { ret= Xorriso_format_media(xorriso, (off_t) 0, 1); if(ret <= 0) return(ret); did_work= (ret == 1); } } disc_state = isoburn_disc_get_status(drive); if(disc_state != BURN_DISC_BLANK && !(flag&4)) { ret= Xorriso_blank_media(xorriso, 1); return(ret); } if(did_work) return(1); sprintf(xorriso->info_text, "%s as_needed: no need for action detected", (flag&4) ? "-format" : "-blank"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); return(2); } int Xorriso_retry_burn_track(struct XorrisO *xorriso, off_t write_start_address, char *track_source, off_t tsize, int flag) { int ret, auto_close_mem, do_close_mem; if(xorriso->do_tao == 1) { Xorriso_msgs_submit(xorriso, 0, "There is no hope for a re-try with -close \"on\" as long as -write_type is \"tao\"", 0, "FAILURE", 0); return(0); } Xorriso_msgs_submit(xorriso, 0, "Re-trying with -close \"on\"", 0, "NOTE", 0); do_close_mem= xorriso->do_close; auto_close_mem= xorriso->auto_close; xorriso->do_close= 1; xorriso->auto_close= 0; ret= Xorriso_burn_track(xorriso, write_start_address, track_source, tsize, flag); xorriso->do_close= do_close_mem; xorriso->auto_close= auto_close_mem; return(ret); } /* @param write_start_address is valid if >=0 @param tsize is valid if >0 @param flag bit0= grow_overwriteable_iso bit1= do_isosize bit2= do_xa1 conversion @return <=0 error , 1= success 2= failure with DVD-RW, please call Xorriso_retry_burn_track() */ int Xorriso_burn_track(struct XorrisO *xorriso, off_t write_start_address, char *track_source, off_t tsize, int flag) { int ret, fd, profile_number, is_cd= 0, do_isosize, is_bd= 0, signal_mode; off_t dummy, nwa= -1, isosize= -1; struct burn_drive_info *dinfo; struct burn_drive *drive; struct burn_write_opts *burn_options= NULL; struct burn_disc *disc= NULL; struct burn_session *session= NULL; struct burn_track *track= NULL; struct stat stbuf; off_t fixed_size= 0; struct burn_source *data_src= NULL, *fifo_src= NULL; enum burn_disc_status disc_state; char *reasons= NULL, *profile_name= NULL; char *head_buffer= NULL; Xorriso_alloc_meM(reasons, char, BURN_REASONS_LEN); Xorriso_alloc_meM(profile_name, char, 80); Xorriso_alloc_meM(head_buffer, char, 64 * 1024); ret= Xorriso_may_burn(xorriso, 0); if(ret <= 0) {ret= 0; goto ex;} ret= Xorriso_auto_format(xorriso, 0); if(ret <=0 ) {ret= 0; goto ex;} do_isosize= !!(flag&2); ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to burn track", 2); if(ret<=0) {ret= 0; goto ex;} ret= Xorriso_check_multi(xorriso, drive, 1); if(ret<=0) goto ex; ret= Xorriso_make_write_options(xorriso, drive, &burn_options, 0); if(ret<=0) goto ex; disc= burn_disc_create(); session= burn_session_create(); ret= burn_disc_add_session(disc,session,BURN_POS_END); if(ret==0) { sprintf(xorriso->info_text, "Cannot add session object to disc object."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); goto ex; } track= burn_track_create(); if(track_source[0] == '-' && track_source[1] == 0) { fd= 0; } else { if(xorriso->fs >= 64) fd= burn_os_open_track_src(track_source, O_RDONLY, 0); else fd= open(track_source, O_RDONLY | O_BINARY); if(fd>=0) if(fstat(fd,&stbuf)!=-1) if((stbuf.st_mode&S_IFMT)==S_IFREG) fixed_size= stbuf.st_size; } if(fd>=0) data_src= burn_fd_source_new(fd, -1, fixed_size); if(data_src==NULL) { sprintf(xorriso->info_text, "Could not open data source "); Text_shellsafe(track_source, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); ret= 0; goto ex; } if((do_isosize || xorriso->fs != 0) && xorriso->fs < 64) xorriso->fs= 64; if(xorriso->fs > 0) { fifo_src= burn_fifo_source_new(data_src, 2048 + 8 * !!(flag & 4), xorriso->fs, 1); if(fifo_src == NULL) { sprintf(xorriso->info_text, "Could not create fifo object of %.f MB", ((double) xorriso->fs) / 1024.0 / 1024.0); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); ret= 0; goto ex; } } xorriso->pacifier_fifo= fifo_src; if(burn_track_set_source(track, fifo_src == NULL ? data_src : fifo_src) != BURN_SOURCE_OK) { sprintf(xorriso->info_text, "Cannot attach source object to track object"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); ret= 0; goto ex; } burn_track_set_cdxa_conv(track, !!(flag & 4)); burn_session_add_track(session, track, BURN_POS_END); burn_source_free(data_src); if(flag&1) /* consider overwriteables with ISO as appendable */ disc_state= isoburn_disc_get_status(drive); else /* handle overwriteables as always blank */ disc_state= burn_disc_get_status(drive); if(disc_state == BURN_DISC_BLANK || disc_state == BURN_DISC_APPENDABLE) { /* ok */; } else { if(disc_state == BURN_DISC_FULL) { sprintf(xorriso->info_text, "Closed media with data detected. Need blank or appendable media."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); if(burn_disc_erasable(drive)) { sprintf(xorriso->info_text, "Try -blank as_needed\n"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0); } } else if(disc_state == BURN_DISC_EMPTY) { sprintf(xorriso->info_text, "No media detected in drive"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } else { sprintf(xorriso->info_text, "Cannot recognize state of drive and media"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } ret= 0; goto ex; } if(isoburn_needs_emulation(drive)) burn_write_opts_set_multi(burn_options, 0); if(tsize > 0) { fixed_size= tsize; burn_track_set_size(track, fixed_size); } if(do_isosize) { ret= burn_fifo_peek_data(xorriso->pacifier_fifo, head_buffer, 64*1024, 0); if(ret<=0) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "Cannot obtain first 64 kB from input stream."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } /* read isosize from head_buffer, not from medium */ ret= isoburn_read_iso_head_v2(drive, 0, &isosize, head_buffer, (1 << 13)); if(ret<=0) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "Option -isosize given but data stream seems not to be ISO 9660"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } sprintf(xorriso->info_text, "Size of ISO 9660 image: %.fs", (double) isosize); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); fixed_size= isosize * (off_t) 2048; burn_track_set_size(track, fixed_size); } ret= Xorriso_get_profile(xorriso, &profile_number, profile_name, 2); is_cd= (ret==2); is_bd= (ret == 3); if(isoburn_needs_emulation(drive)) { if(flag&1) { ret= isoburn_disc_track_lba_nwa_v2(drive, burn_options, 0, &dummy, &nwa); Xorriso_process_msg_queues(xorriso,0); if(ret<=0) { sprintf(xorriso->info_text, "Cannot obtain next writeable address of emulated multi-session media\n"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(nwa == 32 && disc_state != BURN_DISC_APPENDABLE) nwa= 0; /* No automatic toc emulation. Formatter might not be aware. */ } else { nwa= 0; if (disc_state == BURN_DISC_APPENDABLE) { ret= isoburn_disc_track_lba_nwa_v2(drive, burn_options, 0, &dummy, &nwa); Xorriso_process_msg_queues(xorriso,0); if(ret<=0) { sprintf(xorriso->info_text, "Cannot obtain next writeable address of emulated appendable media\n"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } } burn_write_opts_set_start_byte(burn_options, nwa * (off_t) 2048); } if(write_start_address>=0) { nwa= write_start_address / (off_t) 2048; if(nwa * (off_t) 2048 < write_start_address ) nwa++; burn_write_opts_set_start_byte(burn_options, nwa * (off_t) 2048); } if(xorriso->do_tao) { if (xorriso->do_tao > 0) burn_write_opts_set_write_type(burn_options, BURN_WRITE_TAO, BURN_BLOCK_MODE1); else burn_write_opts_set_write_type(burn_options, BURN_WRITE_SAO, BURN_BLOCK_SAO); ret = burn_precheck_write(burn_options, disc, reasons, 0); if(ret<=0) { sprintf(xorriso->info_text, "Cannot set write type %s for this medium.\n", xorriso->do_tao > 0 ? "TAO" : "SAO"); sprintf(xorriso->info_text+strlen(xorriso->info_text), "Reasons given:\n%s", reasons); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } sprintf(xorriso->info_text, "Explicitly chosen write type: %s", xorriso->do_tao > 0 ? "TAO" : "SAO"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0); } else { if(burn_write_opts_auto_write_type(burn_options, disc, reasons, 0) == BURN_WRITE_NONE) { sprintf(xorriso->info_text, "Failed to find a suitable write mode with this media.\n"); sprintf(xorriso->info_text+strlen(xorriso->info_text), "Reasons given:\n%s", reasons); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } ret= Xorriso_sanitize_image_size(xorriso, drive, disc, burn_options, 2 | 4); if(ret<=0) goto ex; sprintf(xorriso->info_text, "Beginning to write data track.\n"); Xorriso_info(xorriso,0); /* Important: do not return until burn_is_aborting() was checked */ signal_mode= 1; ret= burn_drive_get_drive_role(drive); if(ret == 1) signal_mode|= 2; Xorriso_set_signal_handling(xorriso, signal_mode); xorriso->run_state= 1; /* Indicate that burning has started */ burn_disc_write(burn_options, disc); ret= Xorriso_pacifier_loop(xorriso, drive, 2 | (is_cd << 4) | (is_bd << 5)); if(burn_is_aborting(0)) Xorriso_abort(xorriso, 0); /* Never comes back */ Xorriso_set_signal_handling(xorriso, 0); if(ret<=0) goto ex; if(!burn_drive_wrote_well(drive)) { Xorriso_process_msg_queues(xorriso,0); if(xorriso->auto_close && xorriso->do_close == 0) { if(burn_drive_was_feat21_failure(drive)) { sprintf(xorriso->info_text, "libburn indicates failure with writing DVD-RW to appendable state."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); /* Urge caller to call Xorriso_retry_burn_rack() */ ret= 2; goto ex; } } sprintf(xorriso->info_text, "libburn indicates failure with writing."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(flag & 1) { ret= Xorriso_update_iso_lba0(xorriso, nwa, isosize, head_buffer, NULL, flag & 2); if(ret <= 0) goto ex; } sprintf(xorriso->info_text, "Writing to "); Text_shellsafe(xorriso->outdev, xorriso->info_text, 1); strcat(xorriso->info_text, " completed successfully.\n\n"); Xorriso_info(xorriso, 0); ret= 1; ex:; Xorriso_process_msg_queues(xorriso,0); if(disc!=NULL) burn_disc_free(disc); if(session != NULL) burn_session_free(session); if(track != NULL) burn_track_free(track); if(burn_options != NULL) burn_write_opts_free(burn_options); if(xorriso->pacifier_fifo!=NULL) burn_source_free(xorriso->pacifier_fifo); xorriso->pacifier_fifo= NULL; xorriso->run_state= 0; /* Indicate that burning has ended */ Xorriso_free_meM(reasons); Xorriso_free_meM(profile_name); Xorriso_free_meM(head_buffer); return(ret); } int Xorriso_relax_compliance(struct XorrisO *xorriso, char *mode, int flag) { char *npt, *cpt; int l, was, value, ret, endl; struct isoburn_imgen_opts *opts= NULL; char *msg= NULL; char submode[41], *endpt; off_t limit; was= xorriso->relax_compliance; npt= cpt= mode; for(; npt!=NULL; cpt= npt+1) { npt= strchr(cpt,':'); if(npt==NULL) l= strlen(cpt); else l= npt-cpt; if(l == 0) continue; if((l == 6 && strncmp(cpt, "strict", l) == 0) || (l == 5 && strncmp(cpt, "clear", l) == 0)) { xorriso->relax_compliance= 0; } else if(l == 7 && strncmp(cpt, "default", l) == 0) { xorriso->relax_compliance= Xorriso_relax_compliance_defaulT; } else if((l == 18 && strncmp(cpt, "untranslated_names", l) == 0) || (l == 21 && strncmp(cpt, "untranslated_names_on", l) == 0) ) { xorriso->untranslated_name_len = -1; } else if((l == 22 && strncmp(cpt, "untranslated_names_off", l) == 0)) { xorriso->untranslated_name_len = 0; } else if((l >= 22 && strncmp(cpt, "untranslated_name_len=", 22) == 0)) { value= -1; sscanf(cpt + 22, "%d", &value); /* Let libisoburn check the value */ ret= isoburn_igopt_new(&opts, 0); if(ret != 1) return(-1); ret= isoburn_igopt_set_untranslated_name_len(opts, value); isoburn_igopt_destroy(&opts, 0); if(ret <= 0) { /* Not a tasty value */ xorriso->relax_compliance= was; return(0); } xorriso->untranslated_name_len = value; } else if((l == 16 && strncmp(cpt, "allow_dir_id_ext", l) == 0) || (l == 19 && strncmp(cpt, "allow_dir_id_ext_on", l) == 0) ) { xorriso->relax_compliance|= isoburn_igopt_allow_dir_id_ext; xorriso->allow_dir_id_ext_dflt= 0; } else if((l == 20 && strncmp(cpt, "allow_dir_id_ext_off", l) == 0)) { xorriso->relax_compliance&= ~isoburn_igopt_allow_dir_id_ext; xorriso->allow_dir_id_ext_dflt= 0; } else if((l == 12 && strncmp(cpt, "omit_version", l) == 0) || (l == 15 && strncmp(cpt, "omit_version_on", l) == 0) ) { xorriso->relax_compliance|= isoburn_igopt_omit_version_numbers; } else if((l == 16 && strncmp(cpt, "omit_version_off", l) == 0)) { xorriso->relax_compliance&= ~isoburn_igopt_omit_version_numbers; } else if((l == 16 && strncmp(cpt, "only_iso_version", l) == 0) || (l == 19 && strncmp(cpt, "only_iso_version_on", l) == 0) ) { xorriso->relax_compliance|= isoburn_igopt_only_iso_versions; } else if((l == 20 && strncmp(cpt, "only_iso_version_off", l) == 0)) { xorriso->relax_compliance&= ~isoburn_igopt_only_iso_versions; } else if((l == 10 && strncmp(cpt, "deep_paths", l) == 0) || (l == 13 && strncmp(cpt, "deep_paths_on", l) == 0)) { xorriso->relax_compliance|= isoburn_igopt_allow_deep_paths; } else if(l == 14 && strncmp(cpt, "deep_paths_off", l) == 0) { xorriso->relax_compliance&= ~isoburn_igopt_allow_deep_paths; } else if((l == 10 && strncmp(cpt, "long_paths", l) == 0) || (l == 13 && strncmp(cpt, "long_paths_on", l) == 0) ) { xorriso->relax_compliance|= isoburn_igopt_allow_longer_paths; } else if(l == 14 && strncmp(cpt, "long_paths_off", l) == 0) { xorriso->relax_compliance&= ~isoburn_igopt_allow_longer_paths; } else if((l == 10 && strncmp(cpt, "long_names", l) == 0) || (l == 13 && strncmp(cpt, "long_names_on", l) == 0)) { xorriso->relax_compliance|= isoburn_igopt_max_37_char_filenames; } else if(l == 14 && strncmp(cpt, "long_names_off", l) == 0) { xorriso->relax_compliance&= ~isoburn_igopt_max_37_char_filenames; } else if((l == 13 && strncmp(cpt, "no_force_dots", l) == 0) || (l == 16 && strncmp(cpt, "no_force_dots_on", l) == 0)) { xorriso->relax_compliance|= isoburn_igopt_no_force_dots; } else if(l == 17 && strncmp(cpt, "no_force_dots_off", l) == 0) { xorriso->relax_compliance&= ~isoburn_igopt_no_force_dots; } else if((l == 15 && strncmp(cpt, "no_j_force_dots", l) == 0) || (l == 18 && strncmp(cpt, "no_j_force_dots_on", l) == 0)) { xorriso->relax_compliance|= isoburn_igopt_no_j_force_dots; } else if(l == 19 && strncmp(cpt, "no_j_force_dots_off", l) == 0) { xorriso->relax_compliance&= ~isoburn_igopt_no_j_force_dots; } else if((l == 9 && strncmp(cpt, "lowercase", l) == 0) || (l == 12 && strncmp(cpt, "lowercase_on", l) == 0)) { xorriso->relax_compliance|= isoburn_igopt_allow_lowercase; } else if(l == 13 && strncmp(cpt, "lowercase_off", l) == 0) { xorriso->relax_compliance&= ~isoburn_igopt_allow_lowercase; } else if((l == 10 && strncmp(cpt, "full_ascii", l) == 0) || (l == 13 && strncmp(cpt, "full_ascii_on", l) == 0)) { xorriso->relax_compliance|= isoburn_igopt_allow_full_ascii; } else if(l == 14 && strncmp(cpt, "full_ascii_off", l) == 0) { xorriso->relax_compliance&= ~isoburn_igopt_allow_full_ascii; } else if((l == 10 && strncmp(cpt, "7bit_ascii", l) == 0) || (l == 13 && strncmp(cpt, "7bit_ascii_on", l) == 0)) { xorriso->relax_compliance|= isoburn_igopt_allow_7bit_ascii; } else if(l == 14 && strncmp(cpt, "7bit_ascii_off", l) == 0) { xorriso->relax_compliance&= ~isoburn_igopt_allow_7bit_ascii; } else if((l == 17 && strncmp(cpt, "joliet_long_paths", l) == 0) || (l == 20 && strncmp(cpt, "joliet_long_paths_on", l) == 0)) { xorriso->relax_compliance|= isoburn_igopt_joliet_longer_paths; } else if(l == 21 && strncmp(cpt, "joliet_long_paths_off", l) == 0) { xorriso->relax_compliance&= ~isoburn_igopt_joliet_longer_paths; } else if((l == 17 && strncmp(cpt, "joliet_long_names", l) == 0) || (l == 20 && strncmp(cpt, "joliet_long_names_on", l) == 0)) { xorriso->relax_compliance|= isoburn_igopt_joliet_long_names; } else if(l == 21 && strncmp(cpt, "joliet_long_names_off", l) == 0) { xorriso->relax_compliance&= ~isoburn_igopt_joliet_long_names; } else if((l == 12 && strncmp(cpt, "joliet_utf16", l) == 0) || (l == 15 && strncmp(cpt, "joliet_utf16_on", l) == 0)) { xorriso->relax_compliance|= isoburn_igopt_joliet_utf16; } else if(l == 16 && strncmp(cpt, "joliet_utf16_off", l) == 0) { xorriso->relax_compliance&= ~isoburn_igopt_joliet_utf16; } else if((l == 10 && strncmp(cpt, "always_gmt", l) == 0) || (l == 13 && strncmp(cpt, "always_gmt_on", l) == 0)) { xorriso->relax_compliance|= isoburn_igopt_always_gmt; } else if(l == 14 && strncmp(cpt, "always_gmt_off", l) == 0) { xorriso->relax_compliance&= ~isoburn_igopt_always_gmt; } else if((l == 9 && strncmp(cpt, "rec_mtime", l) == 0) || (l == 12 && strncmp(cpt, "rec_mtime_on", l) == 0)) { xorriso->relax_compliance|= (isoburn_igopt_dir_rec_mtime | isoburn_igopt_joliet_rec_mtime | isoburn_igopt_iso1999_rec_mtime); } else if(l == 13 && strncmp(cpt, "rec_mtime_off", l) == 0) { xorriso->relax_compliance&= ~(isoburn_igopt_dir_rec_mtime | isoburn_igopt_joliet_rec_mtime | isoburn_igopt_iso1999_rec_mtime); } else if((l == 6 && strncmp(cpt, "old_rr", l) == 0) || (l == 9 && strncmp(cpt, "old_rr_on", l) == 0) || (l == 10 && strncmp(cpt, "new_rr_off", l) == 0)) { xorriso->relax_compliance|= isoburn_igopt_rrip_version_1_10 | isoburn_igopt_aaip_susp_1_10; } else if((l == 10 && strncmp(cpt, "old_rr_off", l) == 0) || (l == 9 && strncmp(cpt, "new_rr_on", l) == 0) || (l == 6 && strncmp(cpt, "new_rr", l) == 0)) { xorriso->relax_compliance&= ~(isoburn_igopt_rrip_version_1_10 | isoburn_igopt_aaip_susp_1_10); } else if((l == 14 && strncmp(cpt, "aaip_susp_1_10", l) == 0) || (l == 17 && strncmp(cpt, "aaip_susp_1_10_on", l) == 0) || (l == 18 && strncmp(cpt, "aaip_susp_1_12_off", l) == 0)) { xorriso->relax_compliance|= isoburn_igopt_aaip_susp_1_10; } else if((l == 18 && strncmp(cpt, "aaip_susp_1_10_off", l) == 0) || (l == 17 && strncmp(cpt, "aaip_susp_1_12_on", l) == 0) || (l == 14 && strncmp(cpt, "aaip_susp_1_12", l) == 0)) { xorriso->relax_compliance&= ~isoburn_igopt_aaip_susp_1_10; } else if((l == 11 && strncmp(cpt, "no_emul_toc", l) == 0) || (l == 14 && strncmp(cpt, "no_emul_toc_on", l) == 0)) { xorriso->no_emul_toc|= 1; } else if((l == 15 && strncmp(cpt, "no_emul_toc_off", l) == 0) || (l == 8 && strncmp(cpt, "emul_toc", l) == 0)) { xorriso->no_emul_toc&= ~1; } else if((l == 13 && strncmp(cpt, "iso_9660_1999", l) == 0) || (l == 16 && strncmp(cpt, "iso_9660_1999_on", l) == 0)) { xorriso->do_iso1999= 1; } else if(l == 17 && strncmp(cpt, "iso_9660_1999_off", l) == 0) { xorriso->do_iso1999= 0; } else if((l >= 15 && strncmp(cpt, "iso_9660_level=", 15) == 0)) { value= 0; sscanf(cpt + 15, "%d", &value); if(value == 1 || value == 2) { limit= ((off_t) 4) * ((off_t) 1024*1024*1024) - ((off_t) 1); xorriso->iso_level= value; xorriso->iso_level_is_default= 0; if(xorriso->file_size_limit > limit) xorriso->file_size_limit= limit; } else if(value == 3) { xorriso->iso_level= value; xorriso->iso_level_is_default= 0; if(xorriso->file_size_limit < Xorriso_default_file_size_limiT) xorriso->file_size_limit= Xorriso_default_file_size_limiT; } else { Xorriso_alloc_meM(msg, char, 160); sprintf(msg, "-compliance iso_9660_level=%d : Only 1, 2, or 3 are permissible", value); Xorriso_msgs_submit(xorriso, 0, msg, 0, "FAILURE", 0); Xorriso_free_meM(msg); msg= NULL; xorriso->relax_compliance= was; return(0); } } else if((l == 8 && strncmp(cpt, "iso_9660", l) == 0) || (l == 11 && strncmp(cpt, "iso_9660_on", l) == 0)) { /* may have a meaning in future */; } else if(l == 12 && strncmp(cpt, "iso_9660_off", l) == 0) { /* may have a meaning in future */; Xorriso_msgs_submit(xorriso, 0, "-compliance -iso_9660_off : Cannot do anything else but ISO 9660", 0, "FAILURE", 0); xorriso->relax_compliance= was; return(0); } else if((l == 9 && strncmp(cpt, "old_empty", l) == 0) || (l == 12 && strncmp(cpt, "old_empty_on", l) == 0)) { xorriso->do_old_empty= 1; } else if(l == 13 && strncmp(cpt, "old_empty_off", l) == 0) { xorriso->do_old_empty= 0; } else if(l >= 15 && strncmp(cpt, "max_ce_entries=", 15) == 0) { value= 0; sscanf(cpt + 15, "%d", &value); if(value < 1 || value > 100000) { if(msg == NULL) Xorriso_alloc_meM(msg, char, 160); sprintf(msg, "-compliance max_ce_entries=%d : Permissible is 1 to 100000", value); Xorriso_msgs_submit(xorriso, 0, msg, 0, "FAILURE", 0); xorriso->relax_compliance= was; ret= 0; goto ex; } else { xorriso->max_ce_entries= value; } } else if(l >= 12 && strncmp(cpt, "max_ce_drop=", 12) == 0) { endl= sizeof(submode) - 1; endpt= strchr(cpt + 12, ':'); if(endpt != NULL) if(endl > endpt - (cpt + 12)) endl= endpt - (cpt + 12); strncpy(submode, cpt + 12, endl); submode[endl]= 0; if(strcmp(submode, "off") == 0) { xorriso->max_ce_entries_flag&= ~15; } else if(strcmp(submode, "xattr") == 0) { xorriso->max_ce_entries_flag= (xorriso->max_ce_entries_flag & ~15) | 1; } else if(strcmp(submode, "xattr_acl") == 0) { xorriso->max_ce_entries_flag= (xorriso->max_ce_entries_flag & ~15) | 2; } else { sprintf(xorriso->info_text, "-compliance: unknown mode in max_ce_drop='%s'", submode); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } else { if(l<SfileadrL) sprintf(xorriso->info_text, "-compliance: unknown rule '%s'", cpt); else sprintf(xorriso->info_text, "-compliance: oversized rule parameter (%d)", l); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); xorriso->relax_compliance= was; return(0); } } return(1); ex:; Xorriso_free_meM(msg); return(ret); } /* @return 1=ok 2=ok, is default setting */ int Xorriso_get_relax_text(struct XorrisO *xorriso, char mode[1024], int flag) { int r, drop; r= xorriso->relax_compliance; if(r == 0) { strcpy(mode, "strict"); return(1); } strcpy(mode, "clear"); sprintf(mode + strlen(mode), ":iso_9660_level=%d", xorriso->iso_level); if(r & isoburn_igopt_allow_dir_id_ext) strcat(mode, ":allow_dir_id_ext"); if(r & isoburn_igopt_omit_version_numbers) strcat(mode, ":omit_version"); if(r & isoburn_igopt_only_iso_versions) strcat(mode, ":only_iso_version"); if(r & isoburn_igopt_allow_deep_paths) strcat(mode, ":deep_paths"); if(r & isoburn_igopt_allow_longer_paths) strcat(mode, ":long_paths"); if(r & isoburn_igopt_max_37_char_filenames) strcat(mode, ":long_names"); if(r & isoburn_igopt_no_force_dots) strcat(mode, ":no_force_dots"); if(r & isoburn_igopt_no_j_force_dots) strcat(mode, ":no_j_force_dots"); if(r & isoburn_igopt_allow_lowercase) strcat(mode, ":lowercase"); if(r & isoburn_igopt_allow_full_ascii) strcat(mode, ":full_ascii"); else if(r & isoburn_igopt_allow_7bit_ascii) strcat(mode, ":7bit_ascii"); if(r & isoburn_igopt_joliet_longer_paths) strcat(mode, ":joliet_long_paths"); if(r & isoburn_igopt_joliet_long_names) strcat(mode, ":joliet_long_names"); if(r & isoburn_igopt_joliet_utf16) strcat(mode, ":joliet_utf16"); if(r & isoburn_igopt_always_gmt) strcat(mode, ":always_gmt"); if(r & isoburn_igopt_dir_rec_mtime) strcat(mode, ":rec_mtime"); if(r & isoburn_igopt_rrip_version_1_10) { strcat(mode, ":old_rr"); if(!(r & isoburn_igopt_aaip_susp_1_10)) strcat(mode, ":aaip_susp_1_10_off"); } else { strcat(mode, ":new_rr"); if(r & isoburn_igopt_aaip_susp_1_10) strcat(mode, ":aaip_susp_1_10"); } if(xorriso->no_emul_toc & 1) strcat(mode, ":no_emul_toc"); if(xorriso->untranslated_name_len != 0) sprintf(mode + strlen(mode), ":untranslated_name_len=%d", xorriso->untranslated_name_len); if(xorriso->do_iso1999) sprintf(mode + strlen(mode), ":iso_9660_1999"); if(xorriso->do_old_empty) sprintf(mode + strlen(mode), ":old_empty"); sprintf(mode + strlen(mode), ":max_ce_entries=%u", xorriso->max_ce_entries); drop= xorriso->max_ce_entries_flag & 15; sprintf(mode + strlen(mode), ":max_ce_drop=%s", drop == 0 ? "off" : drop == 1 ? "xattr" : "xattr_acl"); return(1 + (r == Xorriso_relax_compliance_defaulT && !(xorriso->no_emul_toc & 1) && xorriso->untranslated_name_len == 0 && !xorriso->do_iso1999 && xorriso->iso_level == 3 && xorriso->max_ce_entries == 31 && drop == 2)); } /* @param flag bit0= operating on newly attached boot image */ int Xorriso_set_isolinux_options(struct XorrisO *xorriso, IsoImage *image, int flag) { int make_isohybrid_mbr= 0, ret, patch_table= 0, num_boots, i; ElToritoBootImage *bootimg, **boots = NULL; IsoFile *bootimg_node, **bootnodes = NULL; ret= iso_image_get_boot_image(image, &bootimg, &bootimg_node, NULL); Xorriso_process_msg_queues(xorriso,0); if(ret != 1) { sprintf(xorriso->info_text, "Programming error: No boot image available in Xorriso_set_isolinux_options()"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0); ret= -1; goto ex; } ret= iso_image_get_all_boot_imgs(image, &num_boots, &boots, &bootnodes, 0); Xorriso_process_msg_queues(xorriso,0); if(ret != 1) { Xorriso_report_iso_error(xorriso, "", ret, "Cannot inquire boot images", 0, "FATAL", 1); ret= -1; goto ex; } /* bit0 : 1=boot-info-table , bit2-7 : 1=EFI , 2=HFS+ , bit8 : 1=APM */ patch_table = xorriso->patch_isolinux_image & 0x3fd; if((flag & 1) && num_boots > 1) { ret= el_torito_set_isolinux_options(boots[num_boots - 1], patch_table, 0); ret= (ret == 1); goto ex; } /* Handle patching of first attached boot image or of imported boot images */ for(i= 0; i < num_boots; i++) { patch_table = xorriso->patch_isolinux_image & 0x3fd; if(patch_table && !(flag & 1)) { if(!el_torito_seems_boot_info_table(boots[i], 0)) patch_table&= ~1; else if((xorriso->patch_isolinux_image & 2) && el_torito_get_boot_platform_id(boots[i]) == 0xef) patch_table&= ~1; } if(i > 0 || xorriso->boot_image_isohybrid == 0) { ret= el_torito_set_isolinux_options(boots[i], patch_table, 0); if(ret != 1) {ret= 0; goto ex;} continue; } /* <<< From here on only with first boot image and deprecated builtin isohybrid MBR */ if(xorriso->boot_image_isohybrid == 3) { make_isohybrid_mbr= 1; } else { ret= Xorriso_is_isohybrid(xorriso, bootimg_node, 0); if(ret < 0) {ret= 0; goto ex;} if(ret > 0) make_isohybrid_mbr= 1; } if(xorriso->boot_image_isohybrid == 2 && !make_isohybrid_mbr) { sprintf(xorriso->info_text, "Isohybrid signature is demanded but not found in boot image file."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } if(make_isohybrid_mbr) { sprintf(xorriso->info_text, "Will write isohybrid MBR."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } ret= el_torito_set_isolinux_options(bootimg, patch_table | (make_isohybrid_mbr << 1),0); if(ret != 1) {ret= 0; goto ex;} } ex: Xorriso_process_msg_queues(xorriso,0); if(boots != NULL) free(boots); if(bootnodes != NULL) free(bootnodes); return(ret); } int Xorriso_overwrite_iso_head(struct XorrisO *xorriso, struct burn_drive *drive, char *head_buffer, off_t lba, int flag) { int ret; off_t to_write; to_write= 64 * 1024; burn_drive_reset_simulate(drive, xorriso->do_dummy); ret= burn_random_access_write(drive, lba * (off_t) 2048, head_buffer, to_write, 1); if(ret <= 0) { Xorriso_process_msg_queues(xorriso, 0); sprintf(xorriso->info_text, "Cannot write new ISO image head to LBA %.f", (double) lba); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } return(1); } /* @param flag bit0= insist on tag_type 4 (relocated superblock tag) bit1= accept tag with ISO_MD5_AREA_CORRUPTED */ int Xorriso_find_sb_checksum(struct XorrisO *xorriso, char *head_buffer, int *vd_end, int flag) { int i, tag_type, ret; uint32_t pos, range_start, range_size, next_tag; char md5[16]; *vd_end= 0; /* Look for volume descriptor end */ for(i= 16; i < 32; i++) if(((unsigned char *) head_buffer)[i * 2048] == 0xff && strncmp(head_buffer + i * 2048 + 1, "CD001", 5) == 0) break; /* Check whether the next one is a libisofs checksum tag */ if(i < 32) { *vd_end= i; i++; ret= iso_util_decode_md5_tag(head_buffer + i * 2048, &tag_type, &pos, &range_start, &range_size, &next_tag, md5, 0); if(((unsigned int) ret) != ISO_MD5_AREA_CORRUPTED || !(flag & 2)) if(ret <= 0) return(ret); if((flag & 1) && tag_type != 4) return(0); /* No other tag type is supposed to occur before type 4 */ } return(i + 1); } /* @param field_head Example: " md5=" */ int Xorriso__set_iso_check_tag_md5(char *tag_data, char *field_head, void **ctx, int *field_end, int flag) { char md5_bin[16], m32, *cpt; int i; iso_md5_end(ctx, md5_bin); cpt= strstr(tag_data, field_head); if(cpt == NULL) return(0); cpt+= strlen(field_head); m32= cpt[32]; for(i= 0; i < 16; i++) sprintf(cpt + 2 * i, "%2.2x", ((unsigned char *) md5_bin)[i]); cpt[32]= m32; *field_end= (cpt - tag_data) + 32; return(1); } int Xorriso_verify_sb_tag(struct XorrisO *xorriso, char *head_buffer, int checksum_block, int flag) { int tag_type, ret; uint32_t pos, range_start, range_size, next_tag; char md5_rec[16], md5_comp[16]; void *ctx= NULL; /* Obtain checksum */ iso_util_decode_md5_tag(head_buffer + checksum_block * 2048, &tag_type, &pos, &range_start, &range_size, &next_tag, md5_rec, 0); /* Verify checksum */ ret= iso_md5_start(&ctx); if(ret <= 0) { Xorriso_process_msg_queues(xorriso,0); Xorriso_no_malloc_memory(xorriso, NULL, 0); return(0); } ret= iso_md5_compute(ctx, head_buffer, checksum_block * 2048); iso_md5_end(&ctx, md5_comp); if(ret <= 0) { Xorriso_process_msg_queues(xorriso,0); return(0); } if(iso_md5_match(md5_rec, md5_comp)) return(1); Xorriso_msgs_submit(xorriso, 0, "Superblock data do not match superblock checksum tag", 0, "WARNING", 0); return(0); } int Xorriso_refresh_sb_tag(struct XorrisO *xorriso, char *head_buffer, int checksum_block, int flag) { int ret, field_end; char md5_bin[16]; void *ctx= NULL; /* Recompute checksum and update found checksum tag */; ret= iso_md5_start(&ctx); if(ret <= 0) { no_md5_ctx:; Xorriso_process_msg_queues(xorriso,0); Xorriso_no_malloc_memory(xorriso, NULL, 0); return(0); } ret= iso_md5_compute(ctx, head_buffer, checksum_block * 2048); if(ret <= 0) { md5_comp_failed:; iso_md5_end(&ctx, md5_bin); return(0); } Xorriso__set_iso_check_tag_md5(head_buffer + checksum_block * 2048, " md5=", &ctx, &field_end, 0); if(ret <= 0) return(2); ret= iso_md5_start(&ctx); if(ret <= 0) goto no_md5_ctx; ret= iso_md5_compute(ctx, head_buffer + checksum_block * 2048, field_end); if(ret <= 0) goto md5_comp_failed; Xorriso__set_iso_check_tag_md5(head_buffer + checksum_block * 2048, " self=", &ctx, &field_end, 0); return(1); } /* @param flag bit0= obtain iso_lba from indev bit1= head_buffer already contains a valid head bit2= issue message about success bit3= check whether source blocks are banned by in_sector_map bit4= refresh relocated sb checksum tag bit5= bit1 for Xorriso_find_sb_checksum: accept tag with ISO_MD5_AREA_CORRUPTED */ int Xorriso_update_iso_lba0(struct XorrisO *xorriso, off_t iso_lba, off_t isosize, char *head_buffer, struct CheckmediajoB *job, int flag) { int ret, i, checksum_block= -1, vd_end; uint32_t full_size; char *headpt; struct burn_drive_info *dinfo; struct burn_drive *drive = NULL; off_t seek_ret, to_write; ret= Xorriso_may_burn(xorriso, 0); if(ret <= 0) return(0); if(flag & 1) { ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to learn current session lba", 0); if(ret<=0) return(0); ret= isoburn_disc_get_msc1_v2(drive, &iso_lba); if(ret<=0) return(0); drive= NULL; /* indev will not be used furtherly */ } if(job == NULL) { ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to update at lba 0 to 31", 2); if(ret<=0) return(0); } if(iso_lba < 32) return(2); if(!(flag & 2)) { /* head_buffer was not filled yet. Read it from output media. */ if(drive != NULL) if(burn_drive_get_drive_role(drive) == 5) /* write-only */ return(2); if(job != NULL && job->data_to_fd >= 0) { if((flag & 8) && job->sector_map != NULL) { ret= Sectorbitmap_bytes_are_set(job->sector_map, iso_lba * (off_t) 2048, (iso_lba + 32) * ((off_t) 2048) - (off_t) 1, 0); if(ret <= 0) { sprintf(xorriso->info_text, "ISO image head at lba %.f is marked as invalid blocks in file copy", (double) iso_lba); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0); return(0); } } seek_ret= lseek(job->data_to_fd, ((off_t) 2048) * iso_lba, SEEK_SET); if(seek_ret == -1) ret= 0; else ret= read(job->data_to_fd, head_buffer, 64 * 1024); if(ret < 64 * 1024) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "Cannot read ISO image head from file copy"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0); return(0); } ret= isoburn_read_iso_head_v2(NULL, (off_t) 0, &isosize, head_buffer, 1 << 13); if(ret<=0) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "Alleged session start does not look like ISO 9660."); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0); return(0); } } else { ret= 0; if(drive != NULL) ret= isoburn_read_iso_head_v2(drive, iso_lba, &isosize, head_buffer, 2); if(ret<=0) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "Cannot read freshly written ISO image head"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } } } /* patch ISO header */ if(iso_lba + isosize > (off_t) 0xffffffff) { sprintf(xorriso->info_text, "The total ISO filesystem size would exceed 8 TiB"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); return(0); } full_size= iso_lba + isosize; headpt= head_buffer + 32*1024; for(i=0;i<4;i++) headpt[87-i]= headpt[80+i]= (full_size >> (8*i)) & 0xff; /* >>> What about Joliet et.al. ? */; if(flag & 16) { /* Find relocated sb checksum tag */ ret= Xorriso_find_sb_checksum(xorriso, head_buffer, &vd_end, 1 | ((flag >> 4) & 2)); if(ret > 0) { /* If it is recognizable then it matched in Xorriso_adjust_relocated_sb */ checksum_block= ret - 1; ret= Xorriso_refresh_sb_tag(xorriso, head_buffer, checksum_block, 0); if(ret <= 0) return(0); } } if(job != NULL) { /* This is a check_media superblock relocation: Invalidate eventual libisofs checksum tags. Write only up to PVD end plus eventual invalidated tag. */ to_write= 2048 * 32; ret= Xorriso_find_sb_checksum(xorriso, head_buffer, &i, ((flag >> 4) & 2)); if(ret > 0) { if(!(flag & 16)) /* invalidate */ memset(head_buffer + (ret - 1) * 2048, 0, 8); to_write= 2048 * ret; } else if(i > 0) { to_write= 2048 * (i + 1); } seek_ret= lseek(job->data_to_fd, (off_t) 0, SEEK_SET); if(seek_ret == -1) ret= 0; else ret= write(job->data_to_fd, head_buffer, to_write); if(ret < to_write) { Xorriso_process_msg_queues(xorriso,0); sprintf(xorriso->info_text, "Cannot write ISO image head to file copy"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0); return(0); } } else { /* This is a regular superblock relocation. Write full 64 kB. */ ret= Xorriso_overwrite_iso_head(xorriso, drive, head_buffer, (off_t) 0, 0); if(ret <= 0) return(ret); } if(flag & 4) { sprintf(xorriso->info_text, "Overwrote LBA 0 to 31 by 64 KiB from LBA %.f", (double) iso_lba); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); } return(1); } /* @return 1= ok, 0= no match, -1= MD5 computation error, -2= MD5 clone or start error */ int Xorriso_migrate_checksum_tag(struct XorrisO *xorriso, char *buffer, int buf_base, int start, int checksum_block, char md5_rec[16], void *ctx_unch, void *ctx_chng, int flag) { int ret, to_compute; char *headpt, md5_clone[16]; void *ctx_clone= NULL; int field_end; /* Checksum both up to before checksum tag */ headpt= buffer + start * 2048; to_compute= (checksum_block - start) * 2048; if(to_compute > 0) { ret= iso_md5_compute(ctx_unch, headpt, to_compute); if(ret <= 0) {ret= -1; goto ex;} ret= iso_md5_compute(ctx_chng, headpt, to_compute); if(ret <= 0) {ret= -1; goto ex;} } /* Verify with unchanged checksum */ ret= iso_md5_clone(ctx_unch, &ctx_clone); if(ret <= 0) {ret= -2; goto ex;} iso_md5_end(&ctx_clone, md5_clone); if(!iso_md5_match(md5_rec, md5_clone)) {ret= 0; goto ex;} /* Compute unchanged rest of block range */ headpt= buffer + checksum_block * 2048; to_compute= 2048; ret= iso_md5_compute(ctx_unch, headpt, to_compute); if(ret <= 0) {ret= -1; goto ex;} /* Replace checksum in tag by changed checksum */ ret= iso_md5_clone(ctx_chng, &ctx_clone); if(ret <= 0) {ret= -2; goto ex;} Xorriso__set_iso_check_tag_md5(headpt, " md5=", &ctx_clone, &field_end, 0); /* Recompute and write self= checksum */ ret= iso_md5_start(&ctx_clone); if(ret <= 0) {ret= -2; goto ex;} ret= iso_md5_compute(ctx_clone, headpt, field_end); if(ret <= 0) {ret= -1; goto ex;} Xorriso__set_iso_check_tag_md5(headpt, " self=", &ctx_clone, &field_end, 0); /* Add rest of head_buffer to changed checksum */ ret= iso_md5_compute(ctx_chng, headpt, to_compute); if(ret <= 0) {ret= -1; goto ex;} ret= 1; ex:; if(ctx_clone != NULL) iso_md5_end(&ctx_clone, md5_clone); return(ret); } /* Verify and re-compute tree and session checksum tag */ int Xorriso_refresh_ts_tags(struct XorrisO *xorriso, struct burn_drive *drive, void *ctx_unch, void *ctx_chng, off_t iso_lba, off_t session_size, int checksum_block, int flag) { int i, ret, tag_type, look_for_tag, check_start, look_from_block, was_change; off_t read_pos, to_read, data_count; uint32_t pos, range_start, range_size, next_tag; char md5_rec[16]; char *buf= NULL; look_for_tag= 3; /* tree tag */ look_from_block= checksum_block + 1; /* first buffer is already partly done */ Xorriso_alloc_meM(buf, char, 32 * 2048); for(read_pos= iso_lba; read_pos < iso_lba + session_size; read_pos+= 32) { was_change= 0; to_read= 32; if(read_pos + to_read > iso_lba + session_size) to_read= iso_lba + session_size - read_pos; ret= burn_read_data(drive, read_pos * (off_t) 2048, buf, to_read * (off_t) 2048, &data_count, 0); if(ret <= 0) {ret= 0; goto ex;} check_start= look_from_block; for(i= look_from_block; i < to_read; i++) { /* Watch out for tag */ ret= iso_util_decode_md5_tag(buf + i * 2048, &tag_type, &pos, &range_start, &range_size, &next_tag, md5_rec, look_for_tag); if(ret < 0 ) { ret= 0; goto ex; } else if(ret == 1) { if(tag_type != look_for_tag) { sprintf(xorriso->info_text, "Encountered checksum tag type %d while looking for %d", tag_type, look_for_tag); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "MISHAP", 0); ret= 2; goto ex; } /* Checksum up to before tag, verify, if match replace checksum and write */ ret= Xorriso_migrate_checksum_tag(xorriso, buf, read_pos, check_start, i, md5_rec, ctx_unch, ctx_chng, 0); if(ret == -2) goto ex; if(ret < 0) {ret= 0; goto ex;} if(ret == 0) { sprintf(xorriso->info_text, "Checksum tag MD5 mismatch in old session state"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "MISHAP", 0); ret= 2; goto ex; } was_change= 1; if(look_for_tag == 3) { look_for_tag= 1; /* session tag */ } else { look_for_tag= -1; break; } check_start= i + 1; } } look_from_block= 0; /* all following buffer need processing from start */ if(was_change) { ret= burn_random_access_write(drive, (off_t) read_pos * (off_t) 2048, buf, to_read * (off_t) 2048, 1); if(ret <= 0) { Xorriso_process_msg_queues(xorriso, 0); sprintf(xorriso->info_text, "Cannot write new checksum tag data to LBA %d", (int) read_pos); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } if(look_for_tag < 0) {ret= 1; goto ex;} /* Checksum what was not checksummed yet */ if(to_read - check_start > 0) { ret= iso_md5_compute(ctx_unch, buf + 2048 * check_start, (to_read - check_start) * 2048); if(ret <= 0) {ret= 0; goto ex;} ret= iso_md5_compute(ctx_chng, buf + 2048 * check_start, (to_read - check_start) * 2048); if(ret <= 0) {ret= 0; goto ex;} } } ret= 1; ex:; Xorriso_free_meM(buf); return(ret); } int Xorriso_adjust_session_size(struct XorrisO *xorriso, struct burn_drive *drive, char *head_buffer, off_t iso_lba, off_t iso_size, int checksum_block, off_t session_size, int flag) { int i, ret, tag_type; uint32_t pos, range_start, range_size, next_tag; char *headpt, md5_unch[16], md5_chng[16], md5_clone[16], md5_rec[16]; void *ctx_unch= NULL, *ctx_chng= NULL, *ctx_clone= NULL; if(checksum_block > 0) { /* Obtain recorded superblock MD5 */ ret= iso_util_decode_md5_tag(head_buffer + checksum_block * 2048, &tag_type, &pos, &range_start, &range_size, &next_tag, md5_rec, 0); if(ret <= 0 || tag_type != 2) { sprintf(xorriso->info_text, "Encountered checksum tag type %d while looking for 2", tag_type); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "MISHAP", 0); checksum_block= 0; } } if(checksum_block > 0) { /* Create md5 context for unchanged state */ ret= iso_md5_start(&ctx_unch); if(ret <= 0) { no_ctx:; Xorriso_process_msg_queues(xorriso, 0); Xorriso_no_malloc_memory(xorriso, NULL, 0); goto ex; } /* Checksum up to before PVD */ ret= iso_md5_compute(ctx_unch, head_buffer, 32768); if(ret <= 0) goto ex; /* Before the first change: obtain md5 object for changed state */ ret= iso_md5_clone(ctx_unch, &ctx_chng); if(ret <= 0) goto no_ctx; /* Add PVD to unchanged checksum */ ret= iso_md5_compute(ctx_unch, head_buffer + 32768, 2048); if(ret <= 0) goto ex; } /* Update session PVD at iso_lba+16 to iso_size */ headpt= head_buffer + 32 * 1024; if(iso_size > (off_t) 0xffffffff) { sprintf(xorriso->info_text, "The total ISO filesystem size exceeds 8 TiB"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } for(i= 0; i < 4; i++) headpt[87 - i]= headpt[80 + i]= (iso_size >> (8 * i)) & 0xff; if(checksum_block > 0) { /* Add changed PVD to changed checksum */ ret= iso_md5_compute(ctx_chng, head_buffer + 32768, 2048); if(ret <= 0) goto ex; ret= Xorriso_migrate_checksum_tag(xorriso, head_buffer, (int) iso_lba, 17, checksum_block, md5_rec, ctx_unch, ctx_chng, 0); if(ret == -2) goto no_ctx; if(ret < 0) {ret= 0; goto ex;} if(ret == 0) { sprintf(xorriso->info_text, "Superblock MD5 mismatch in old session state"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "MISHAP", 0); checksum_block= 0; } } ret= Xorriso_overwrite_iso_head(xorriso, drive, head_buffer, iso_lba, 0); if(ret <= 0) goto ex; if(checksum_block > 0) { /* Verify and re-compute existing checksum tree and session tag */ ret= Xorriso_refresh_ts_tags(xorriso, drive, ctx_unch, ctx_chng, iso_lba, session_size, checksum_block, 0); if(ret == -2) goto no_ctx; if(ret <= 0) goto ex; } ret= 1; ex:; Xorriso_process_msg_queues(xorriso, 0); if(ctx_unch != NULL) iso_md5_end(&ctx_unch, md5_unch); if(ctx_chng != NULL) iso_md5_end(&ctx_chng, md5_chng); if(ctx_clone != NULL) iso_md5_end(&ctx_clone, md5_clone); return(ret); } /* Read relocated superblock and patch in the VDs of the session superblock */ int Xorriso_adjust_relocated_sb(struct XorrisO *xorriso, struct burn_drive *drive, char *head_buffer, char **sb_buffer, int flag) { int ret, i, vd_end, checksum_block= -1; off_t old_size; char *buffer, *checksum= NULL; *sb_buffer= NULL; Xorriso_alloc_meM(*sb_buffer, char, 32 * 2048); buffer= *sb_buffer; Xorriso_alloc_meM(checksum, char, 2048); ret= isoburn_read_iso_head_v2(drive, 0, &old_size, buffer, 2); if(ret <= 0) goto ex; ret= Xorriso_find_sb_checksum(xorriso, buffer, &vd_end, 0); if(ret < 0) goto ex; if(ret > 0) { checksum_block= ret - 1; memcpy(checksum, buffer + checksum_block * 2048, 2048); ret= Xorriso_verify_sb_tag(xorriso, buffer, checksum_block, 0); if(ret <= 0) { checksum_block= -1; memset(checksum, 0, 8); } } for(i= 16; i < 32; i++) { memcpy(buffer + i * 2048, head_buffer + i * 2048, 2048); if(((unsigned char *) head_buffer)[i * 2048] == 0xff && strncmp(head_buffer + i * 2048 + 1, "CD001", 5) == 0) { i++; break; } } if(checksum_block >= 0 && i < 32) memcpy(buffer + i * 2048, checksum, 2048); ret= 1; ex: if(ret <= 0) Xorriso_free_meM(*sb_buffer); Xorriso_free_meM(checksum); return(ret); } int Xorriso_truncate_overwritable(struct XorrisO *xorriso, char *adr_mode, char *adr_value, char *adjust, int flag) { int ret, iso_session, iso_track, image_start_mode= 0; int was_indev= 0, checksum_block= 0, vd_end, headless_mode= 0, i; int params_flag; off_t iso_lba= 0, iso_size= 0, old_size, new_size, blocks, readable_blocks; char image_start_value[81], *head_buffer= NULL, iso_volid[33]; char *sb_buffer= NULL, *checksum_pt, *adr_data= NULL; struct burn_drive_info *dinfo; struct burn_drive *drive = NULL, *in_drive = NULL; struct burn_multi_caps *caps= NULL; Xorriso_alloc_meM(head_buffer, char, 32 * 2048); Xorriso_alloc_meM(adr_data, char, 163); if(Xorriso_change_is_pending(xorriso, 0)) { sprintf(xorriso->info_text, "-truncate_overwritable: Image changes pending. -commit or -rollback first"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } ret= Xorriso_may_burn(xorriso, 0); if(ret <= 0) goto ex; ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to activate an older session", 2); if(ret <= 0) goto ex; /* Is it overwritable ? */ ret= burn_disc_get_multi_caps(drive, BURN_WRITE_NONE, &caps, 0); if(ret > 0) { if(caps->start_adr == 0) ret= 0; } if(ret <= 0) { sprintf(xorriso->info_text, "-truncate_overwritable: Loaded medium is not random-access overwritable"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto ex; } ret= Xorriso_reassure(xorriso, "-truncate_overwritable", "activates an older session and destroys newer ones", 0); if(ret <= 0) {ret= 2; goto ex;} /* Learn old size */ ret= isoburn_read_iso_head_v2(drive, 0, &old_size, iso_volid, 0); if(ret <= 0) { sprintf(xorriso->info_text, "-truncate_overwritable: Cannot read ISO 9660 Volume Descriptor from LBA 0"); if((strcmp(adr_mode, "lba") == 0 || strcmp(adr_mode, "sbsector") == 0) && strcmp(adjust, "new") == 0) { Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0); ret= burn_get_read_capacity_v2(drive, &old_size, 0); if(ret <= 0) goto ex; headless_mode= 1; } else { Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); goto ex; } } if(headless_mode) { iso_lba= Scanf_io_size(adr_value, 0); ret= isoburn_read_iso_head_v2(drive, iso_lba, &new_size, head_buffer, 2 | (1 << 12)); if(ret <= 0) goto not_iso_9660; } else { /* Check for PVD at image_start_value and learn new size */ ret= Xorriso_decode_load_adr(xorriso, "-truncate_overwritable", adr_mode, adr_value, &image_start_mode, image_start_value, 0); if(ret <= 0) goto ex; ret= Xorriso_prepare_load_search(xorriso, "-truncate_overwritable", image_start_mode, image_start_value, adr_data, ¶ms_flag, 0); if(ret <= 0) goto ex; ret= isoburn_get_mount_params_v2(drive, image_start_mode, adr_data, &iso_lba, &iso_track, &iso_session, iso_volid, params_flag); if(ret <= 0) goto ex; if(ret != 1) { not_iso_9660:; sprintf(xorriso->info_text, "-truncate_overwritable: Given address does not lead to ISO 9660 Volume Descriptor"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } if(iso_lba >= old_size) { sprintf(xorriso->info_text, "-truncate_overwritable: Given address is larger than current ISO size"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } ret= isoburn_read_iso_head_v2(drive, iso_lba, &new_size, head_buffer, 2); if(ret <= 0) goto ex; } ret= Xorriso_find_sb_checksum(xorriso, head_buffer, &vd_end, 0); if(ret > 0) checksum_block= ret - 1; /* Default is "new" */ iso_size= new_size; if(strcmp(adjust, "old") == 0) { /* ISO size before truncation */ iso_size= old_size - iso_lba; } else if(adjust[0] == '+') { /* Add-on size to new */ blocks= Scanf_io_size(adjust + 1, 0) / 2048; if(blocks < 0) goto wrong_adjust; iso_size+= blocks; } else if(adjust[0] >= '0' && adjust[0] <= '9') { /* Add-on size to new */ blocks= Scanf_io_size(adjust, 0) / 2048; if(blocks < iso_lba + iso_size) { wrong_adjust:; sprintf(xorriso->info_text, "-truncate_overwritable: Given total filesystem size is smaller than new session size"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } iso_size= blocks - iso_lba; } ret= burn_get_read_capacity_v2(drive, &readable_blocks, 0); Xorriso_process_msg_queues(xorriso, 0); if(ret > 0) { if(iso_lba + iso_size > readable_blocks) { sprintf(xorriso->info_text, "-truncate_overwritable: Given total filesystem size is larger than formatted medium size"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } } /* Give up possible input drive */ ret= Xorriso_get_drive_handles(xorriso, &dinfo, &in_drive, "", 16); if(ret < 0) goto ex; if(ret == 1) { ret= Xorriso_give_up_drive(xorriso, 1); if(ret<=0) goto ex; was_indev= 1; } if(iso_size != new_size) { ret=Xorriso_adjust_session_size(xorriso, drive, head_buffer, iso_lba, iso_size, checksum_block, new_size, 0); if(ret <= 0) goto ex; } /* Load first 64 kB and transfer VDs from head_buffer */ if(headless_mode) { Xorriso_alloc_meM(sb_buffer, char, 32 * 2048); memcpy(sb_buffer, head_buffer, 32 * 2048); for(i= 17; i < 32; i++) if(strncmp(sb_buffer + i * 2048, "libisofs_sb_checksum_tag_v1", 27) == 0) break; if(i < 32) { /* Convert superblock tag to provisory relocated superblock tag */ checksum_pt= sb_buffer + i * 2048; memset(checksum_pt, 0, 2048); sprintf(checksum_pt, "libisofs_rlsb32_checksum_tag_v1 pos=%d range_start=0 range_size=%d", i, i); sprintf(checksum_pt + strlen(checksum_pt), " session_start=%.f md5=0123456789abcdef0123456789abcdef self=0123456789abcdef0123456789abcdef\n", (double) iso_lba); } } else { ret= Xorriso_adjust_relocated_sb(xorriso, drive, head_buffer, &sb_buffer, 0); if(ret <= 0) goto ex; } /* Patch the size and write back */ ret= Xorriso_update_iso_lba0(xorriso, iso_lba, iso_size, sb_buffer, NULL, 2 | 16 | ((!!headless_mode) << 5)); if(ret <= 0) goto ex; ret= Xorriso_reaquire_outdev(xorriso, 2 + was_indev); if(ret <= 0) goto ex; ret= 1; ex: if(caps!=NULL) burn_disc_free_multi_caps(&caps); Xorriso_free_meM(adr_data); Xorriso_free_meM(head_buffer); Xorriso_free_meM(sb_buffer); Xorriso_process_msg_queues(xorriso,0); return(ret); } int Xorriso_set_system_area_path(struct XorrisO *xorriso, char *path, int flag) { int ret; char *eff_src= NULL, *intvl; struct iso_interval_reader *ivr= NULL; off_t byte_count; IsoImage *img= NULL; struct burn_drive_info *source_dinfo; struct burn_drive *source_drive; if(path[0] == 0) { xorriso->system_area_disk_path[0]= 0; {ret= 1; goto ex;} } Xorriso_alloc_meM(eff_src, char, SfileadrL); intvl = path; ret = Xorriso_check_intvl_string(xorriso, &intvl, 0); if(ret > 0) { /* Check for syntactical correctness */ if(xorriso->in_drive_handle != NULL) { ret= Xorriso_get_drive_handles(xorriso, &source_dinfo, &source_drive, "on attempt to verify interval reader string", 0); if(ret<=0) goto ex; img= isoburn_get_attached_image(source_drive); } ret= iso_interval_reader_new(img, intvl, &ivr, &byte_count, 1); Xorriso_process_msg_queues(xorriso, 0); if(ret < 0) { sprintf(xorriso->info_text, "Given path for system area is not accepted by interval reader"); Text_shellsafe(eff_src, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } iso_interval_reader_destroy(&ivr, 0); ret= Sfile_str(xorriso->system_area_disk_path, path, 0); if(ret <= 0) {ret= -1; goto ex;} ret= 1; goto ex; } ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, path, eff_src, 2|4|16); if(ret < 0) goto ex; if(ret == 0) { sprintf(xorriso->info_text, "Given path does not exist on disk: -boot_image system_area="); Text_shellsafe(eff_src, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } if(ret == 2) { sprintf(xorriso->info_text, "Given path leads to a directory: -boot_image system_area="); Text_shellsafe(eff_src, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); {ret= 0; goto ex;} } ret= Sfile_str(xorriso->system_area_disk_path, eff_src, 0); if(ret <= 0) {ret= -1; goto ex;} ret= 1; ex: Xorriso_free_meM(eff_src); if(img != NULL) iso_image_unref(img); return(ret); } /* @param flag bit0=force burn_disc_close_damaged() */ int Xorriso_close_damaged(struct XorrisO *xorriso, int flag) { int ret; struct burn_drive_info *dinfo; struct burn_drive *drive; struct burn_write_opts *burn_options= NULL; if(Xorriso_change_is_pending(xorriso, 0)) { sprintf(xorriso->info_text, "Image changes pending. -commit or -rollback first"); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); ret= 0; goto ex; } ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "on attempt to close damaged session", 2); if(ret<=0) goto ex; ret= Xorriso_check_multi(xorriso, drive, 0); if(ret<=0) goto ex; ret= Xorriso_make_write_options(xorriso, drive, &burn_options, 0); if(ret <= 0) goto ex; ret= burn_disc_close_damaged(burn_options, flag & 1); Xorriso_process_msg_queues(xorriso, 0); Xorriso_option_dev(xorriso, "", 3 | 4); /* Give up drives */ if(ret <= 0) goto ex; ret= 1; ex:; Xorriso_process_msg_queues(xorriso, 0); if(burn_options != NULL) burn_write_opts_free(burn_options); return(ret); } /* @param flag bit0= no error message */ int Xorriso_parse_guid(struct XorrisO *xorriso, char *text, uint8_t guid[16], int flag) { int bin_count= 0, ret; uint8_t u[16], tr; /* Try RFC 4122 : big endian XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX Translate to UEFI: first three components to little-endian */ if(strlen(text) == 36) { if(text[8] == '-' && text[13] == '-' && text[18] == '-' && text[23] == '-'){ ret= Hex_to_bin(text, 4, &bin_count, u, 0); if(ret < 0 || bin_count != 4) goto malformed; tr= u[0]; u[0]= u[3]; u[3]= tr; tr= u[1]; u[1]= u[2]; u[2]= tr; ret= Hex_to_bin(text + 9, 2, &bin_count, u + 4, 0); if(ret < 0 || bin_count != 2) goto malformed; tr= u[4]; u[4]= u[5]; u[5]= tr; ret= Hex_to_bin(text + 14, 2, &bin_count, u + 6, 0); if(ret < 0 || bin_count != 2) goto malformed; tr= u[6]; u[6]= u[7]; u[7]= tr; ret= Hex_to_bin(text + 19, 2, &bin_count, u + 8, 0); if(ret < 0 || bin_count != 2) goto malformed; ret= Hex_to_bin(text + 24, 6, &bin_count, u + 10, 0); if(ret < 0 || bin_count != 6) goto malformed; memcpy(guid, u, 16); return(1); } } if(strlen(text) == 32) { ret= Hex_to_bin(text, 16, &bin_count, u, 0); if(ret < 0 || bin_count != 16) goto malformed; memcpy(guid, u, 16); return(1); } malformed:; if(!(flag & 1)) { sprintf(xorriso->info_text, "Malformed GUID string: "); Text_shellsafe(text, xorriso->info_text, 1); Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0); } return(0); } int Xorriso_parse_gpt_guid(struct XorrisO *xorriso, char *text, int flag) { int ret; if(strcmp(text, "random") == 0) { xorriso->gpt_guid_mode= 0; return(1); } if(strcmp(text, "modification-date") == 0 || strcmp(text, "volume_date_uuid") == 0) { xorriso->gpt_guid_mode= 2; return(1); } ret= Xorriso_parse_guid(xorriso, text, xorriso->gpt_guid, 0); if(ret <= 0) return(ret); xorriso->gpt_guid_mode= 1; return(1); } /* @return Tells the recognition status 0= not recognized as GUID 1= non-EFI type GUID 2= EFI type GUID */ int Xorriso_parse_type_guid(struct XorrisO *xorriso, char *text, uint8_t guid[16], int *mbr_type, int flag) { int j, ret; static uint8_t efi_sys_uuid[16] = { 0x28, 0x73, 0x2a, 0xc1, 0x1f, 0xf8, 0xd2, 0x11, 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b }; ret= Xorriso_parse_guid(xorriso, text, guid, 1); if(ret > 0) { for(j= 0; j < 16; j++) if(guid[j] != efi_sys_uuid[j]) break; if(j >= 16) { *mbr_type= 0xef; return(2); } else { *mbr_type= 0x83; return(1); } } return(0); } /* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains declarations of functions which are needed to write sessions. */ #ifndef Xorriso_pvt_write_run_includeD #define Xorriso_pvt_write_run_includeD yes /* CD specs say one shall not write tracks < 600 kiB */ #define Xorriso_cd_min_track_sizE 300 /* Default setting for -compliance */ #define Xorriso_relax_compliance_defaulT \ (isoburn_igopt_allow_deep_paths | isoburn_igopt_allow_longer_paths | \ isoburn_igopt_always_gmt | isoburn_igopt_dir_rec_mtime | \ isoburn_igopt_joliet_rec_mtime | isoburn_igopt_iso1999_rec_mtime | \ isoburn_igopt_rrip_version_1_10 | isoburn_igopt_aaip_susp_1_10 | \ isoburn_igopt_only_iso_versions | isoburn_igopt_no_j_force_dots) int Xorriso_make_write_options( struct XorrisO *xorriso, struct burn_drive *drive, struct burn_write_opts **burn_options, int flag); off_t Xorriso_sanitize_image_size(struct XorrisO *xorriso, struct burn_drive *drive, struct burn_disc *disc, struct burn_write_opts *burn_options, int flag); int Xorriso_auto_format(struct XorrisO *xorriso, int flag); int Xorriso_set_system_area(struct XorrisO *xorriso, struct burn_drive *in_drive, struct burn_drive *out_drive, IsoImage *img, struct isoburn_imgen_opts *sopts, int flag); int Xorriso_check_burn_abort(struct XorrisO *xorriso, int flag); int Xorriso_pacifier_loop(struct XorrisO *xorriso, struct burn_drive *drive, int flag); int Xorriso_set_isolinux_options(struct XorrisO *xorriso, IsoImage *image, int flag); int Xorriso_make_iso_write_opts(struct XorrisO *xorriso, IsoImage *image, struct isoburn_imgen_opts *sopts, int flag); #endif /* ! Xorriso_pvt_write_run_includeD */ .\" Hey, EMACS: -*- nroff -*- .\" .\" IMPORTANT NOTE: .\" .\" The original of this file is kept in xorriso/xorrecord.texi .\" This here was generated by program xorriso/make_xorriso_1 .\" .\" .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH XORRECORD 1 "Version 1.5.7, Apr 19, 2024" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp <n> insert n+1 empty lines .\" for manpage-specific macros, see man(7) .nh .SH NAME xorrecord \- Emulation of CD/DVD/BD program cdrecord by program xorriso .SH SYNOPSIS .B xorrecord [ options ] dev=device [track_source] .br .SH DESCRIPTION .PP \fBxorrecord\fR writes preformatted data to CD, DVD, and BD media. .br .PP It understands some options of program cdrecord from cdrtools by Joerg Schilling. Its implementation is part of program xorriso which shares no source code with cdrtools, but rather makes use of libburn for communicating with the drive. .br Another, more complete cdrecord emulator is program \fBcdrskin\fR which uses the same burn functions as \fBxorrecord\fR, but is able to burn audio CDs and to handle CD\-TEXT. .SS \fBMMC, Session, Track, Media types:\fR .br \fBMMC\fR is a standard out of the SCSI family which defines the interaction between computers and optical drives. Since more than a decade all CD, DVD, or BD recorders obey this standard regardless by what bus cabling they are attached to the computer. libburn relies on this standard compliance and on the capability of the operating system to perform SCSI transactions over the particular bus cabling. .br A \fBSession\fR is a data region on an optical disc which usually gets written in a single sweep. It contains at least one \fBTrack\fR which is a contiguous string of readable blocks. \fBxorrecord\fR produces a single session with a single data track which consists of blocks with 2048 bytes each. It chooses the write mode automatically according to media type, medium state, and option \-multi. .br On CD media there are other track types, like audio, and particular write modes like TAO and SAO. CD and DVD\- media can put more than one track into a session. Some of these features can be addressed by program \fBcdrskin\fR. .br MMC describes several recordable \fBmedia types\fR which roughly form two families. .br \fBSequentially recordable media\fR are CD\-R, CD\-RW, DVD\-R, DVD\-R DL, DVD\-RW, DVD+R, DVD+R DL, BD\-R. Except DVD\-R DL they can store more than one session if there is still unwritten space and if the previous session was written with option \fB\-multi\fR. CD\-RW and DVD\-RW can be blanked in order to be re\-usable from scratch. .br \fBOverwritable media\fR are DVD\-RAM, DVD+RW, formatted DVD\-RW, BD\-RE. They offer a single session with a single track for random access writing. There is no need to blank overwritable media before re\-use. .br DVD\-RW media are sold in sequentially recordable state but can be formatted once to become overwritable. See options \fBblank=format_overwrite\fR and \fBblank=deformat\fR. .br If ISO 9660 filesystems are to be stored on overwritable media, then it is possible to emulate multiple sessions, by using option \fB\-\-grow_overwriteable_iso\fR. In this case, the need for blanking before re\-use is emulated too. .SS .B Drive preparation and addressing: .PP The drives, CD, DVD, or BD burners, are accessed via file addresses which are specific to libburn and the operating system. Those addresses get listed by a run of \fBxorrecord \-\-devices\fR or \fBxorriso \-device_links\fR. .br On GNU/Linux, FreeBSD, and NetBSD, the user needs rw\-permission for the device file. On Solaris, the user needs r\-permission and privilege "sys_devices", which is usually gained by running \fBxorrecord\fR via command pfexec. .br These permissions or privileges are needed already for listing a drive. So it might be necessary to get the overview as superuser or via pfexec. .br \fBxorrecord\fR does not perform cdrecord option \-scanbus and does not accept the addresses of form Bus,Target,Lun which are told by \-scanbus. If support for these addresses is necessary, consider to use program cdrskin. .br It is possible to let \fBxorrecord\fR work on emulated drives. Their addresses begin by prefix "stdio:" followed by a file address. The emulated media behavior depends on the file type. See man xorriso for details. .br If standard output is chosen as emulated drive, then all program result texts, which usually appear on standard output, will get redirected to standard error. .SS \fBRelation to program xorriso:\fR .br \fBxorrecord\fR is actually a command mode of program \fBxorriso\fR, which gets entered either by xorriso command "\-as cdrecord" or by starting the program by one of the names "xorrecord", "cdrecord", "wodim", or "cdrskin". .br This command mode can be left by argument "\-\-" which leads to generic xorriso command mode. See \fBman xorriso\fR for its description. Other than in xorriso command mode, the sequence of the cdrecord emulation options does not matter. All pending actions get performed in a fixed sequence before the program run ends or before cdrecord emulation ends. .SS .br .SH OPTIONS .br .PP .TP .B Addressing the drive: .TP \fB--devices\fR Print the list of accessible CD, DVD, or BD drives to standard output. Drives might be inaccessible if the user lacks of permissions to use them or if the drive is in use by another program. .br Each accessible drive is shown by a line like: .br 0 \-dev '/dev/sr0' rwrw\-\- : 'TSSTcorp' 'CDDVDW SH\-S203B' .br The libburn address of this drive is '/dev/sr0'. 'TSSTcorp' is the name of the vendor (in this case: Toshiba Samsung Storage Technologies Corporation), 'CDDVDW SH\-S203B' is the model name (in this case: a DVD burner). .br Afterwards end emulation without performing any further drive operation. .TP \fBdev=drive_address\fR Set the libburn address of the drive to be used. .br E.g. on GNU/Linux: dev=/dev/sr0 .br E.g. on FreeBSD: dev=/dev/cd0 .br E.g. on NetBSD: dev=/dev/rcd0d .br E.g. on Solaris: dev=/dev/rdsk/c2t2d0s2 .br See also above "Drive preparation and addressing". .br The medium in the drive should not be mounted or be otherwise in use. .br This option will only get into effect if a track source, a blank= option, or a drive inquiry option is given. Else it will lead to a SORRY event and normally cause a non\-zero exit value. .TP .B Inquiring drive and media: .TP \fB\-inq\fR Print to standard output: vendor, model name, and firmware revision of the drive. .TP \fB\-checkdrive\fR Print unconditionally that the drive supports burnfree, SAO, and TAO. Also print the output of option \-inq. .TP \fB\-atip\fR Print the output of \-checkdrive, the most capable profile of the medium in the drive, the list of profiles which are supported by the drive, whether it is erasable (i.e. can be blanked), the media manufacturer, and the medium product name. .br Profiles are usage models, which are often tied to a particular media type (e.g. CD\-RW), but may also apply to a family of media. E.g. profile CD\-ROM applies to all CD media which contain data. .TP \fB\-toc\fR Print a table of content of the medium in the drive. The output is not compatible to cdrecord option \-toc, but rather the one of \fBxorriso\fR command \-toc. It lists the address, vendor, model name, and firmware revision of the drive. .br About the medium it tells product name and manufacturer, whether there is already content written, and if so, whether the medium is closed or appendable. Appendable media can take another session. The amount of readable and writable data is told. If there are sessions, then their start block address and size is reported. If a session contains an ISO 9660 filesystem, then its Volume Id is reported. If the medium is writable, then the next writable block address is reported. .br If not option \fB\-\-grow_overwriteable_iso\fR is given or no ISO 9660 file system is present on the medium, then overwritable media are reported as being blank. This is due to the fact that they can be written from scratch without further preparation, and that MMC does not distinguish between data written by the most previous burn run and older data which have not been overwritten by that burn run. Consequently, these media are reported with 0 readable blocks, although all their writable blocks normally are readable, too. .TP \fB\-msinfo\fR Print the argument text for option \-C of programs mkisofs, genisoimage, or xorrisofs. It consists of two numbers separated by a comma. .br The first number tells the first block of the first track of the last recorded session. This is also the address used by default when operating systems mount a medium with e.g. ISO 9660 filesystem. .br The second number tells the next writable address, where \fBxorrecord\fR will begin to write the next session. .br This option is only valid for written, appendable media. In all other cases it will yield no output text but will abort the program with non\-zero exit value. .TP .B Settings for the burn run: .PP A burn run requires exactly one track source address argument, which tells from where to read the data which shall be put into the upcoming session. The medium state must be either blank or appendable. .br Track source may be "\-" for standard input or the address of a readable file of any type except directories. Nearly all media types accept a track source with unpredictable byte count, like standard input or named pipes. Nevertheless, DVD\-R DL and DVD\-RW blanked by mode deformat_quickest demand exact in\-advance reservation of the track size, so that they either need to be read from a source of predictable length, or need to be accompanied by option \fBtsize=\fR or by option \fB\-isosize\fR. .br Several options expect a size value as argument. A number with a trailing letter "b" or without a trailing letter is a plain byte count. Other trailing letters cause multiplication of the given number by a scaling factor: .br "k" or "K" = 1024 , "m" or "M" = 1024k , "g" or "G" = 1024m , "s" or "S" = 2048 .br E.g. tsize=234567s means a size of 234567 * 2048 = 480393216 bytes. .TP \fBblank=mode\fR Blank a CD\-RW or DVD\-RW to make it re\-usable from scratch. Format a DVD\-RW, DVD+RW, DVD\-RAM, BD\-R, or BD\-RE if not yet formatted. .br This operation normally makes any recorded data on the medium unreadable. It is combinable with burning in the same run of \fBxorrecord\fR, or it may be performed without a track source, leaving the medium empty. .br The mode given with blank= selects the particular behavior: .RS .TP .br as_needed .br Try to make the media ready for writing from scratch. If it needs formatting, then format it. If it is not blank, then try to apply blank=fast. It is a reason to abort if the medium cannot assume thoroughly writeable state, e.g. if it is a non\-blank write\-once. .br This leaves unformatted DVD\-RW in unformatted blank state. To format DVD\-RW use blank=format_overwrite. Blank unformatted BD\-R stay unformatted. .br (Note: blank=as_needed is not an original cdrecord option.) .TP .br all .br Blank an entire CD\-RW or an unformatted DVD\-RW. .TP .br fast .br Minimally blank an entire CD\-RW or blank an unformatted DVD\-RW. .TP .br deformat .br Like blank=all but with the additional ability to blank overwritable DVD\-RW. This will destroy their formatting and make them sequentially recordable. .br (Note: blank=deformat is not an original cdrecord options) .TP .br deformat_quickest .br Like blank=deformat but blanking DVD\-RW only minimally. This is faster than full blanking but yields media incapable of writing tracks of unpredictable size. Multi\-session will not be possible either. .br (Note: blank=deformat_quickest is not an original cdrecord option.) .TP .br format_overwrite .br Format a DVD\-RW to "Restricted Overwrite". The user should bring some patience. .br Format unformatted DVD+RW, BD\-RE or blank BD\-R to their default size. It is not mandatory to do this with DVD+RW and BD\-RE media, because they will get formatted automatically on the first write attempt. .br BD\-R media may be written in unformatted state. This keeps disabled the replacement of bad blocks and enables full nominal write speed. Once BD\-R media are written, they cannot be formatted any more. .br For re\-formatting already formatted media or for formatting with non\-default size, use program \fBxorriso\fR with command \fB\-format\fR. .br (Note: blank=format_overwrite is not an original cdrecord options) .TP .br help .br Print a short overview of blank modes to standard error output. .br Afterwards end emulation without performing any drive operation. .RE .TP \fB\-multi\fR This option keeps CD, unformatted DVD\-R[W], DVD+R, or BD\-R appendable after the current session has been written. Without it the disc gets closed and may not be written any more \- unless it is a \-RW and gets blanked, which causes loss of its content. .br This option cannot be applied to DVD\-R DL or to DVD\-RW which were blanked by mode "deformat_quickest". Option \-\-multi_if_possible may automatically recognize and handle this situation. .br In order to have all filesystem content accessible, the eventual ISO\-9660 filesystem of a follow\-up session needs to be prepared in a special way by the filesystem formatter program. mkisofs, genisoimage, and xorrisofs expect particular info about the situation which can be retrieved by \fBxorrecord\fR option \-msinfo. .br With overwritable DVD or BD media, \-multi cannot mark the end of the session. So when adding a new session, this end has to be determined from the payload. Currently only ISO\-9660 filesystems can be used that way. See option \fB\-\-grow_overwriteable_iso\fR. .TP \fB\-dummy\fR Try to perform the drive operations without actually affecting the inserted media. There is no warranty that this will work with a particular combination of drive and media. Blanking is prevented reliably, though. To avoid inadverted real burning, \-dummy refuses burn runs on anything but CD\-R[W], DVD\-R[W], or emulated stdio\-drives. .TP \fB\-waiti\fR Wait until input data is available at stdin or EOF occurs at stdin. Only then begin to access any drives. .br One should use this if xorrisofs is working at the end of a pipe where the feeder process reads from the drive before it starts writing its output into xorrisofs. Example: .br xorrisofs ... \-C 0,12800 \-M /dev/sr0 ... | \\ .br xorrecord dev=/dev/sr0 ... \-waiti \- .br This option works even if standard input is not the track source. If no process is piping in, then the Enter key of your terminal will act as trigger for \fBxorrecord\fR. Note that this input line will not be consumed by cdrskin if standard input is not the track source. It will end up as shell command, usually. .TP \fBtsize=size\fR Announce the exact size of the track source. This is necessary with DVD\-R DL media and with quickest blanked DVD\-RW, if the size cannot be determined in advance from the track source. E.g. if it is standard input or a named pipe. .br If the track source does not deliver the predicted amount of bytes, the remainder of the track is padded with zeros. This is not considered an error. If on the other hand the track source delivers more than the announced bytes then the track on media gets truncated to the predicted size and xorrecord exits with non\-zero value. .TP \fB\-isosize\fR Try to obtain the track size from the content of the track source. This works only if the track source bears an ISO 9660 filesystem. Any other track source content will cause the burn run to abort. .br If the track source is not a regular file or block device, then this option will work only if the program's fifo size is at least 64k. See option fs=. .TP \fBpadsize=size\fR Add the given amount of trailing zeros to the upcoming track. This feature can be disabled by size 0. Default is 300 kB in order to work around a problem with GNU/Linux which often fails to read the last few blocks of a CD track which was written in write mode TAO. TAO is used by \fBxorrecord\fR if the track size cannot be predicted or if the CD medium is not blank but appendable. .TP \fB\-nopad\fR The same as padsize=0. .TP \fB\-pad\fR The same as padsize=15s. This was once sufficient with older GNU/Linux kernels. Meanwhile one should at least use padsize=128k, if not padsize=300k. .TP \fB\-data\fR Explicitly announce that the track source shall be recorded as data track, and not as audio track. This option has no effect with \fBxorrecord\fR, because there is no support for other track formats anyway. .TP \fB\-tao\fR Explicitly demand that write type TAO shall be used for CD, or Incremental for DVD\-R. Normally the program will choose the write type according to the given medium state, option \-multi, and track source. Demanding it explicitly prevents the start of a write run, if it is not appropriate to the situation. .TP \fB\-sao\fR Explicitly demand that write type SAO shall be used for CD, or DAO for DVD\-R. This might prevent the write run, if it is not appropriate to the situation. .TP \fB\-dao\fR Alias of \-sao. .TP \fBfs=size\fR Set the size of the program fifo buffer to the given value rather than the default of 4m. .br The fifo buffers a temporary surplus of track source data in order to provide the drive with a steady stream during times of temporary lack of track source supply. .br Other than cdrecord, xorrecord enables drive buffer underrun protection by default and does not wait with writing until the fifo is full for a first time. On very old CD drives and slow computers, this might cause aborted burn runs. In this case, consider to use program \fBcdrskin\fR for CD burning. DVD and BD drives tolerate buffer underrun without problems. .br The larger the fifo, the longer periods of poor source supply can be compensated. But a large fifo can deprive the operating system of read cache for better filesystem performance. .TP \fBspeed=value\fR Set the write speed. Default is 0 = maximum speed. Speed can be given in media type dependent x\-speed numbers or as a desired throughput per second in MMC compliant kB (= 1000) or MB (= 1000 kB). Media x\-speed factor can be set explicitly by appending "c" for CD, "d" for DVD, "b" for BD. "x" is optional. .br Example speeds: .br 706k = 706kB/s = 4c = 4xCD .br 5540k = 5540kB/s = 4d = 4xDVD .br If there is no hint about the speed unit attached, then the medium in the drive will decide. Default unit is CD, 1x = 176,400 raw bytes/second. With DVD, 1x = 1,385,000 bytes/second. With BD, 1x = 4,495,625 bytes/second. .br MMC drives usually activate their own idea of speed and take the speed value given by the burn program only as a hint for their own decision. .TP \fBminbuf=percentage\fR Equivalent to: .br modesty_on_drive=<percentage> .TP \fB\-immed\fR Equivalent to: .br modesty_on_drive=75 .br In cdrecord, this also controls use of the Immed bit. But xorriso uses Immed where possible and appropriate, unless it is disabled by option use_immed_bit=off . .TP \fB\-eject\fR Eject the drive tray after alll other work is done. .TP .B Program version and verbosity: .TP \fB\-version\fR Print to standard output a line beginning by .br "Cdrecord 2.01\-Emulation Copyright" .br and further lines which report the version of xorriso and its supporting libraries. They also state the license under which the program is provided, and disclaim any warranty, to the extent permitted by law. .br Afterwards end emulation without performing any drive operation. .TP \fB\-v\fR Increase program verbosity by one level. There are four verbosity levels from nearly silent to debugging verbosity. The both highest levels can be enabled by repeated \-v or by \-vv or by \-vvv. .TP \fB\-V\fR Log SCSI commands and drive replies to standard error. This might be of interest if \fBxorrecord\fR and a particular drive or medium do not cooperate as expected, or if you just want to know how libburn interacts with the drive. To understand this extremely verbose log, one needs to read SCSI specs SPC, SBC, and MMC. .br Please do not add such a log to a bug report on the first hand, unless you want to point out a particular deviation from said specs, or if you get asked for this log by a maintainer of \fBxorrecord\fR who feels in charge for your bug report. .TP \fB\-help\fR Print a sparse list of program options to standard error and declare not to be cdrecord. .br Afterwards end emulation without performing any drive operation. .TP .B Options not compatible to cdrecord: .TP \fB--no_rc\fR Only if used as first command line argument this option prevents reading and interpretation of startup files. See section FILES below. .TP \fB--drive_not_exclusive\fR This option disables the use of device file locking mechanisms when acquiring the drive. On GNU/Linux the locking is done by open(O_EXCL), on FreeBSD by flock(LOCK_EX). .br Be aware that it can cause problems if you use a drive which is mounted, or opened by some other process, or guarded by /dev/pktcdvd*. Make sure that other users of the drive do not cause drive activities while a xorrecord burn run is going on. .TP \fBdrive_scsi_dev_family=sr|scd|sg|default\fR GNU/Linux specific: .br By default, cdrskin tries to map Linux drive addresses to /dev/sr* before they get opened for operating the drive. This coordinates well with other use cases of optical drives, like mount(8). But since year 2010 all /dev/sr* share a global lock which allows only one drive to process an SCSI command while all others have to wait for its completion. This yields awful throughput if more than one drive is writing or reading simultaneously. .br The global lock is not applied to device files /dev/sg* and also not with the system calls read(2), write(2). But ioctl(SG_IO) is affected, which is needed to perform the SCSI commands for optical burning. .br So for simultaneous burn runs on modern GNU/Linux it is advisable to use drive_scsi_dev_family="sg". The drive addresses may then well be given as /dev/sr* but will nevertheless get used as /dev/sg*. .TP \fB--grow_overwriteable_iso\fR Enable emulation of multi\-session writing on overwritable media which contain an ISO 9660 filesystem. This emulation is learned from growisofs \-M but adapted to the usage model of .br xorrecord \-msinfo .br xorrisofs \-C \-M | xorrecord \-waiti \-multi \- .br for sequential media. .br \-\-grow_overwriteable_iso does not hamper the use of true multi\-session media. I.e. it is possible to use the same \fBxorrecord\fR options with both kinds of media and to achieve similar results if ISO 9660 filesystem images are to be written. This option implies option \-isosize and therefore demands that the track source is a ISO 9660 filesystem image. .br With overwritable media and no option blank=fast|all present it expands an eventual ISO 9660 filesystem on media. It is assumed that this image's inner size description points to the end of the valuable data. Overwritable media with a recognizable ISO 9660 size will be regarded as appendable rather than as blank. I.e. options \-msinfo and \-toc will work. \-toc will always show a single session with its size increasing with every added ISO 9660 image. .TP \fB--multi_if_possible\fR Apply option \-multi if the medium is suitable. Not suitable are DVD\-R DL and DVD\-RW, which were blanked with mode "deformat_quickest". .br Not all drives correctly recognize such fast\-blanked DVD\-RW which need "on". If there is well founded suspicion that a burn run failed due to \-multi, then this causes a re\-try without \-multi. .TP \fBstream_recording="on"|"off"|number\fR Mode "on" requests that compliance to the desired speed setting is preferred over management of write errors. With DVD\-RAM and BD this can bring effective write speed near to the nominal write speed of the media. But it will also disable the automatic use of replacement blocks if write errors occur. It might as well be disliked or ignored by the drive. .br If a number is given, then error management stays enabled for all byte addresses below that number. Any number below 16s is the same as "off". .TP \fBdvd_obs="default"|"32k"|"64k"\fR Set the number of bytes to be transmitted with each write operation to DVD or BD media. With most write types, tracks get padded up to the next multiple of this write size (see option \-\-obs_pad). A number of 64 KB may improve throughput with bus systems which show latency problems. The default depends on media type, option stream_recording=, and on compile time options. .TP \fB\--obs_pad\fR Pad the data of the last write operation of a DVD\-R[W] DAO session, or BD\-R session, or stdio: pseudo\-drive session up to the full size of an output chunk. This padding has to be applied automatically to the other DVD and BD media types, where it causes e.g. ISO images to have trailing unclaimed blocks. Whether it is applied automatically to BD\-R depends on option \-\-bdr_obs_exempt. .br Use this option if there is the suspicion that DVD\-R[W] DAO or BD\-R sessions abort with your kernel and/or DVD drive, if their size is not a multiple of 16 blocks. .br This option may also get enabled at compile time of libburn. .TP \fB\--bdr_obs_exempt\fR Exempt BD\-R media from automatic unconditional transaction end padding, provided that this padding is not requested by \-\-obs_pad and that no stream_recording is requested. .br This is a new feature introduced with version 1.5.6. It might become default in later versions. .TP \fBmodesty_on_drive=parameter[:parameters]\fR Control whether the drive buffer shall be kept from getting completely filled. Parameter "on" (or "1") keeps the program from trying to write to the burner drive while its buffer is in danger to be filled over a given limit. If this filling is exceeded then the program will wait until the filling reaches a given low percentage value. .br This can ease the load on operating system and drive controller and thus help with achieving better input bandwidth if disk and burner are not on independent controllers (like hda and hdb). It may also help with simultaneous burns on different burners with Linux kernels like 3.16, if one has reason not to fix the problem by drive_scsi_dev_family="sg". On the other hand it increases the risk of buffer underflow and thus reduced write speed. .br Some burners are not suitable because they report buffer fill with granularity too coarse in size or time, or expect their buffer to be filled to the top before they go to full speed. .br Parameters "off" or "0" disable this feature. .br The threshold for beginning to wait is given by parameter "max_percent=". Parameter "min_percent=" defines the threshold for resuming transmission. Percentages are permissible in the range of 25 to 100. Numbers in this range without a prepended name are interpreted as "on:min_percent=". .br E.g.: modesty_on_drive=75 .br The optimal values depend on the buffer behavior of the drive. .br Parameter "timeout_sec=" defines after which time of unsuccessful waiting the modesty shall be disabled because it does not work. .br Parameter "min_usec=" defines the initial sleeping period in microseconds. If the drive buffer appears to be too full for sending more data, the program will wait the given time and inquire the buffer fill state again. If repeated inquiry shows not enough free space, the sleep time will slowly be increased to what parameter "max_usec=" defines. .br Parameters, which are not mentioned with a modesty_on_drive= option, stay unchanged. Default is: .br modesty_on_drive=off:min_percent=90:max_percent=95: timeout_sec=120:min_usec=5000:max_usec=25000 .TP \fBuse_immed_bit="on"|"off"|"default"\fR Control whether several long lasting SCSI commands shall be executed with the Immed bit, which makes the commands end early while the drive operation is still going on. xorriso then inquires progress indication until the drive reports to be ready again. If this feature is turned off, then blanking and formatting will show no progress indication. .br It may depend on the operating system whether \-use_immed_bit is set to "off" by default. .TP \fBwrite_start_address=byte_offset\fR Set the byte address on overwritable media where to start writing the track. With DVD+RW, DVD\-RAM or BD\-RE, byte_offset must be aligned to 2 kiB blocks, but better is 32 kiB on DVD and 64 kiB on BD. With formatted DVD\-RW 32 kiB alignment is mandatory. .br Other media are not suitable for this option. .TP \fBstdio_sync="on"|"off"|number\fR Set the number of bytes after which to force output to emulated stdio: drives. This forcing keeps the memory from being clogged with lots of pending data for slow devices. Default "on" is the same as "16m". Forced output can be disabled by "off". .SH EXAMPLES .SS .B Overview of examples: Get an overview of drives and their addresses .br Get info about a particular drive or loaded media .br Prepare CD-RW or DVD-RW for re-use, BD-R for bad block handling .br Format DVD-RW to avoid need for blanking before re-use .br De-format DVD-RW to make it capable of multi-session again .br Write a single ISO 9660 filesystem image .br Write multiple ISO 9660 sessions .br Write ISO 9660 session on-the-fly .br Write compressed afio archive on-the-fly .br .SS .B Get an overview of drives and their addresses: $ xorrecord \-\-devices .SS .B Get info about a particular drive and loaded media: $ xorrecord dev=/dev/sr0 \-atip \-toc \-\-grow_overwriteable_iso .SS .B Prepare CD-RW or DVD-RW for re-use: $ xorrecord \-v dev=/dev/sr0 blank=as_needed \-eject .SS .B Format DVD-RW to avoid need for blanking before re-use: $ xorrecord \-v dev=/dev/sr0 blank=format_overwrite \-eject .br This command may also be used to format BD\-R media before first use, in order to enable handling of write errors. Several hundred MB of spare blocks will be reserved and write runs on such media will perform with less than half nominal speed. .SS .B De-format DVD-RW to make it capable of multi-session again: $ xorrecord \-v dev=/dev/sr0 blank=deformat .SS .B Write a single ISO 9660 filesystem image: $ xorrecord \-v dev=/dev/sr0 speed=12 fs=8m \\ blank=as_needed \-eject padsize=300k my_image.iso .SS .B Write multiple ISO 9660 sessions: This is possible with all media except minimally blanked DVD\-RW and DVD\-R DL, which cannot do multi\-session. .br The first session is written like in the previous example, except that option \-multi is used. It will contain the files of hard disk directory ./tree1 under the ISO 9660 directory /dir1: .br $ xorrisofs \-o image_1.iso \-J \-graft\-points /dir1=./tree1 .br $ xorrecord \-v dev=/dev/sr0 speed=12 fs=8m \\ .br \-multi \-\-grow_overwriteable_iso \\ .br blank=as_needed \-eject padsize=300k image_1.iso .br For the second session xorrisofs needs to know the \-msinfo numbers of the medium. Further it will read data from the medium by using the system's read\-only CD\-ROM driver. .br Many systems do not take notice of xorrecord's write activities. It is necessary to force their attention by ejecting and reloading the drive tray. Therefore above run uses option \-eject. .br Get the \-msinfo numbers (and properly reload the tray if it has a motor) by: .br $ m=$(xorrecord dev=/dev/sr0 \-msinfo) .br Offer a victim to any problem caused by obtrusive demons after tray loading: .br $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 .br Use the numbers with xorrisofs to add ./tree2 to the image as /dir2: .br $ xorrisofs \-M /dev/sr0 \-C $m \-o image_2.iso \\ .br \-J \-graft\-points /dir2=./tree2 .br Now burn the new session onto the same medium. This time without blanking: .br $ xorrecord \-v dev=/dev/sr0 speed=12 fs=8m \\ .br \-multi \-\-grow_overwriteable_iso \\ .br \-eject padsize=300k image_2.iso .br Operating systems which mount this medium will read the superblock of the second session and show both directories /dir1 and /dir2. .SS .B Write ISO 9660 session on-the-fly: It is possible to combine the run of \fBxorrisofs\fR and \fBxorrecord\fR in a pipeline without storing the ISO 9660 image as file on hard disk. .br The piped run is more vulnerable to the problem that some systems have not enough patience with automatic tray loading and that demons may interfere with a first CD\-ROM driver read attempt from a freshly loaded medium. It is advised to load the tray manually or via a separate run of xorriso with a subsequent run of dd. .br Again, xorriso has the patience and dd is a dispensable victim for demons. .br $ m=$(xorrecord dev=/dev/sr0 \-msinfo) .br $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 .br $ xorrisofs \-M /dev/sr0 \-C $m \\ .br \-J \-graft\-points /dir2=./tree2 \\ .br | xorrecord \-v dev=/dev/sr0 speed=12 fs=8m \\ .br \-waiti \-multi \-\-grow_overwriteable_iso \\ .br \-eject padsize=300k \- .br This is also the main use case of program \fBxorriso\fR itself, where the run would need no system workarounds and simply look like: .br $ xorriso \-dev /dev/sr0 \-joliet on \-speed 12 \-fs 8m \\ .br \-map ./tree2 /dir2 \-commit_eject all .SS .B Write compressed afio archive on-the-fly: This is possible with all media except minimally blanked DVD\-RW and DVD\-R DL. Since the compressed output stream is of very variable speed, a larger fifo is advised. Nevertheless, this example is not suitable for very old CD drives which have no underrun protection and thus would abort the burn run on temporary data shortage. .br $ find . | afio \-oZ \- | \\ .br xorrecord \-v dev=/dev/sr0 speed=12 fs=64m \\ .br \-multi padsize=300k \- .br afio archives do not contain references to absolute data block addresses. So they need no special precautions for multi\-session. One may get the session start addresses by option \-toc, and then use dd option skip= to begin reading at one of those addresses. E.g. for listing its content: .br $ dd if=/dev/sr0 bs=2048 skip=64046 | afio \-tvZ \- .br afio will know when the end of the archive is reached. .SH FILES .SS .B Startup files: .br If not \-\-no_rc is given as the first argument then \fBxorrecord\fR attempts on startup to read and execute lines from the following files: .br /etc/default/xorriso .br /etc/opt/xorriso/rc .br /etc/xorriso/xorriso.conf .br $HOME/.xorrisorc .br The files are read in the sequence given here, but none of them is required to exist. The lines are not interpreted as \fBxorrecord\fR options but as generic \fBxorriso\fR commands. See man xorriso. .SH SEE ALSO .TP For generic xorriso command mode .BR xorriso(1) .TP Formatting track sources for xorrecord: .BR xorrisofs(1), .BR mkisofs(8), .BR genisoimage(8), .BR afio(1), .BR star(1) .TP Other programs which burn sessions to optical media .BR growisofs(1), .BR cdrecord(1), .BR wodim(1), .BR cdrskin(1) .SH BUGS To report bugs, request help, or suggest enhancements for \fBxorriso\fR, please send electronic mail to the public list <bug\-xorriso@gnu.org>. If more privacy is desired, mail to <scdbackup@gmx.net>. .br Please describe what you expect \fBxorriso\fR to do, the program arguments or dialog commands by which you tried to achieve it, the messages of \fBxorriso\fR, and the undesirable outcome of your program run. .br Expect to get asked more questions before solutions can be proposed. .SH AUTHOR Thomas Schmitt <scdbackup@gmx.net> .br for libburnia\-project.org .SH COPYRIGHT Copyright (c) 2011 \- 2024 Thomas Schmitt .br Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of xorriso. If you make use of the license to derive modified versions of xorriso then you are entitled to modify this text under that same license. .SH CREDITS \fBxorriso\fR is in part based on work by Vreixo Formoso who provides libisofs together with Mario Danic who also leads the libburnia team. Thanks to Andy Polyakov who invented emulated growing, to Derek Foreman and Ben Jansens who once founded libburn. .br Compliments towards Joerg Schilling whose cdrtools served me for ten years. This is xorrecord.info, produced by makeinfo version 5.2 from xorrecord.texi. xorrecord - Emulation of CD/DVD/BD program cdrecord by program xorriso Copyright (C) 2011 - 2023 Thomas Schmitt Permission is granted to distribute this text freely. INFO-DIR-SECTION Archiving START-INFO-DIR-ENTRY * Xorrecord: (xorrecord). Emulates CD/DVD/BD program cdrecord END-INFO-DIR-ENTRY  File: xorrecord.info, Node: Top, Next: Overview, Up: (dir) xorrecord 1.5.7 *************** xorrecord - Emulation of CD/DVD/BD program cdrecord by program xorriso * Menu: * Overview:: Overview * Standards:: MMC, Session, Track, Media types * Drive:: Drive preparation and addressing * Xorriso:: Relation to program xorriso * Options:: Options * Examples:: Examples * Files:: Files * Seealso:: See also * Bugreport:: Reporting bugs * Legal:: Author, Copyright, Credits * CommandIdx:: Alphabetic Command List * ConceptIdx:: Alphabetic List of Concepts and Objects  File: xorrecord.info, Node: Overview, Next: Standards, Prev: Top, Up: Top 1 Overview ********** 'xorrecord' writes preformatted data to CD, DVD, and BD media. It understands some options of program cdrecord from cdrtools by Joerg Schilling. Its implementation is part of program xorriso which shares no source code with cdrtools, but rather makes use of libburn for communicating with the drive. Another, more complete cdrecord emulator is program *cdrskin* which uses the same burn functions as *xorrecord*, but is able to burn audio CDs and to handle CD-TEXT.  File: xorrecord.info, Node: Standards, Next: Drive, Prev: Overview, Up: Top 2 MMC, Session, Track, Media types ********************************** *MMC* is a standard out of the SCSI family which defines the interaction between computers and optical drives. Since more than a decade all CD, DVD, or BD recorders obey this standard regardless by what bus cabling they are attached to the computer. libburn relies on this standard compliance and on the capability of the operating system to perform SCSI transactions over the particular bus cabling. A *Session* is a data region on an optical disc which usually gets written in a single sweep. It contains at least one *Track* which is a contiguous string of readable blocks. 'xorrecord' produces a single session with a single data track which consists of blocks with 2048 bytes each. It chooses the write mode automatically according to media type, medium state, and option -multi. On CD media there are other track types, like audio, and particular write modes like TAO and SAO. CD and DVD- media can put more than one track into a session. Some of these features can be addressed by program *cdrskin*. MMC describes several recordable *media types* which roughly form two families. *Sequentially recordable media* are CD-R, CD-RW, DVD-R, DVD-R DL, DVD-RW, DVD+R, DVD+R DL, BD-R. Except DVD-R DL they can store more than one session if there is still unwritten space and if the previous session was written with option *-multi*. CD-RW and DVD-RW can be blanked in order to be re-usable from scratch. *Overwritable media* are DVD-RAM, DVD+RW, formatted DVD-RW, BD-RE. They offer a single session with a single track for random access writing. There is no need to blank overwritable media before re-use. DVD-RW media are sold in sequentially recordable state but can be formatted once to become overwritable. See options *blank=format_overwrite* and *blank=deformat*. If ISO 9660 filesystems are to be stored on overwritable media, then it is possible to emulate multiple sessions, by using option *-grow_overwriteable_iso*. In this case, the need for blanking before re-use is emulated too.  File: xorrecord.info, Node: Drive, Next: Xorriso, Prev: Standards, Up: Top 3 Drive preparation and addressing ********************************** The drives, CD, DVD, or BD burners, are accessed via file addresses which are specific to libburn and the operating system. Those addresses get listed by a run of 'xorrecord --devices' or 'xorriso -device_links'. On GNU/Linux, FreeBSD, and NetBSD, the user needs rw-permission for the device file. On Solaris, the user needs r-permission and privilege "sys_devices", which is usually gained by running 'xorrecord' via command pfexec. These permissions or privileges are needed already for listing a drive. So it might be necessary to get the overview as superuser or via pfexec. 'xorrecord' does not perform cdrecord option -scanbus and does not accept the addresses of form Bus,Target,Lun which are told by -scanbus. If support for these addresses is necessary, consider to use program cdrskin. It is possible to let 'xorrecord' work on emulated drives. Their addresses begin by prefix "stdio:" followed by a file address. The emulated media behavior depends on the file type. See man xorriso for details. If standard output is chosen as emulated drive, then all program result texts, which usually appear on standard output, will get redirected to standard error.  File: xorrecord.info, Node: Xorriso, Next: Options, Prev: Drive, Up: Top 4 Relation to program xorriso ***************************** 'xorrecord' is actually a command mode of program *xorriso*, which gets entered either by xorriso command "-as cdrecord" or by starting the program by one of the names "xorrecord", "cdrecord", "wodim", or "cdrskin". This command mode can be left by argument "--" which leads to generic xorriso command mode. See *man xorriso* for its description. Other than in xorriso command mode, the sequence of the cdrecord emulation options does not matter. All pending actions get performed in a fixed sequence before the program run ends or before cdrecord emulation ends.  File: xorrecord.info, Node: Options, Next: Examples, Prev: Xorriso, Up: Top 5 Options ********* * Menu: * DriveAddr:: Drive addressing * Inquire:: Inquiring drive and media * SetBurn:: Settings for the burn run * Verbose:: Program version and verbosity * NonCdrecord:: Options not compatible to cdrecord  File: xorrecord.info, Node: DriveAddr, Next: Inquire, Prev: Options, Up: Options 5.1 Addressing the drive ======================== --devices Print the list of accessible CD, DVD, or BD drives to standard output. Drives might be inaccessible if the user lacks of permissions to use them or if the drive is in use by another program. Each accessible drive is shown by a line like: 0 -dev '/dev/sr0' rwrw- : 'TSSTcorp' 'CDDVDW SH-S203B' The libburn address of this drive is '/dev/sr0'. 'TSSTcorp' is the name of the vendor (in this case: Toshiba Samsung Storage Technologies Corporation), 'CDDVDW SH-S203B' is the model name (in this case: a DVD burner). Afterwards end emulation without performing any further drive operation. dev=drive_address Set the libburn address of the drive to be used. E.g. on GNU/Linux: dev=/dev/sr0 E.g. on FreeBSD: dev=/dev/cd0 E.g. on NetBSD: dev=/dev/rcd0d E.g. on Solaris: dev=/dev/rdsk/c2t2d0s2 See also above "Drive preparation and addressing". The medium in the drive should not be mounted or be otherwise in use. This option will only get into effect if a track source, a blank= option, or a drive inquiry option is given. Else it will lead to a SORRY event and normally cause a non-zero exit value.  File: xorrecord.info, Node: Inquire, Next: SetBurn, Prev: DriveAddr, Up: Options 5.2 Inquiring drive and media ============================= -inq Print to standard output: vendor, model name, and firmware revision of the drive. -checkdrive Print unconditionally that the drive supports burnfree, SAO, and TAO. Also print the output of option -inq. -atip Print the output of -checkdrive, the most capable profile of the medium in the drive, the list of profiles which are supported by the drive, whether it is erasable (i.e. can be blanked), the media manufacturer, and the medium product name. Profiles are usage models, which are often tied to a particular media type (e.g. CD-RW), but may also apply to a family of media. E.g. profile CD-ROM applies to all CD media which contain data. -toc Print a table of content of the medium in the drive. The output is not compatible to cdrecord option -toc, but rather the one of 'xorriso' command -toc. It lists the address, vendor, model name, and firmware revision of the drive. About the medium it tells product name and manufacturer, whether there is already content written, and if so, whether the medium is closed or appendable. Appendable media can take another session. The amount of readable and writable data is told. If there are sessions, then their start block address and size is reported. If a session contains an ISO 9660 filesystem, then its Volume Id is reported. If the medium is writable, then the next writable block address is reported. If not option *-grow_overwriteable_iso* is given or no ISO 9660 file system is present on the medium, then overwritable media are reported as being blank. This is due to the fact that they can be written from scratch without further preparation, and that MMC does not distinguish between data written by the most previous burn run and older data which have not been overwritten by that burn run. Consequently, these media are reported with 0 readable blocks, although all their writable blocks normally are readable, too. -msinfo Print the argument text for option -C of programs mkisofs, genisoimage, or xorrisofs. It consists of two numbers separated by a comma. The first number tells the first block of the first track of the last recorded session. This is also the address used by default when operating systems mount a medium with e.g. ISO 9660 filesystem. The second number tells the next writable address, where 'xorrecord' will begin to write the next session. This option is only valid for written, appendable media. In all other cases it will yield no output text but will abort the program with non-zero exit value.  File: xorrecord.info, Node: SetBurn, Next: Verbose, Prev: Inquire, Up: Options 5.3 Settings for the burn run ============================= A burn run requires exactly one track source address argument, which tells from where to read the data which shall be put into the upcoming session. The medium state must be either blank or appendable. Track source may be "-" for standard input or the address of a readable file of any type except directories. Nearly all media types accept a track source with unpredictable byte count, like standard input or named pipes. Nevertheless, DVD-R DL and DVD-RW blanked by mode deformat_quickest demand exact in-advance reservation of the track size, so that they either need to be read from a source of predictable length, or need to be accompanied by option *tsize=* or by option *-isosize*. Several options expect a size value as argument. A number with a trailing letter "b" or without a trailing letter is a plain byte count. Other trailing letters cause multiplication of the given number by a scaling factor: "k" or "K" = 1024 , "m" or "M" = 1024k , "g" or "G" = 1024m , "s" or "S" = 2048 E.g. tsize=234567s means a size of 234567 * 2048 = 480393216 bytes. blank=mode Blank a CD-RW or DVD-RW to make it re-usable from scratch. Format a DVD-RW, DVD+RW, DVD-RAM, BD-R, or BD-RE if not yet formatted. This operation normally makes any recorded data on the medium unreadable. It is combinable with burning in the same run of 'xorrecord', or it may be performed without a track source, leaving the medium empty. The mode given with blank= selects the particular behavior: as_needed Try to make the media ready for writing from scratch. If it needs formatting, then format it. If it is not blank, then try to apply blank=fast. It is a reason to abort if the medium cannot assume thoroughly writeable state, e.g. if it is a non-blank write-once. This leaves unformatted DVD-RW in unformatted blank state. To format DVD-RW use blank=format_overwrite. Blank unformatted BD-R stay unformatted. (Note: blank=as_needed is not an original cdrecord option.) all Blank an entire CD-RW or an unformatted DVD-RW. fast Minimally blank an entire CD-RW or blank an unformatted DVD-RW. deformat Like blank=all but with the additional ability to blank overwritable DVD-RW. This will destroy their formatting and make them sequentially recordable. (Note: blank=deformat is not an original cdrecord options) deformat_quickest Like blank=deformat but blanking DVD-RW only minimally. This is faster than full blanking but yields media incapable of writing tracks of unpredictable size. Multi-session will not be possible either. (Note: blank=deformat_quickest is not an original cdrecord option.) format_overwrite Format a DVD-RW to "Restricted Overwrite". The user should bring some patience. Format unformatted DVD+RW, BD-RE or blank BD-R to their default size. It is not mandatory to do this with DVD+RW and BD-RE media, because they will get formatted automatically on the first write attempt. BD-R media may be written in unformatted state. This keeps disabled the replacement of bad blocks and enables full nominal write speed. Once BD-R media are written, they cannot be formatted any more. For re-formatting already formatted media or for formatting with non-default size, use program *xorriso* with command *-format*. (Note: blank=format_overwrite is not an original cdrecord options) help Print a short overview of blank modes to standard error output. Afterwards end emulation without performing any drive operation. -multi This option keeps CD, unformatted DVD-R[W], DVD+R, or BD-R appendable after the current session has been written. Without it the disc gets closed and may not be written any more - unless it is a -RW and gets blanked, which causes loss of its content. This option cannot be applied to DVD-R DL or to DVD-RW which were blanked by mode "deformat_quickest". Option -multi_if_possible may automatically recognize and handle this situation. In order to have all filesystem content accessible, the eventual ISO-9660 filesystem of a follow-up session needs to be prepared in a special way by the filesystem formatter program. mkisofs, genisoimage, and xorrisofs expect particular info about the situation which can be retrieved by 'xorrecord' option -msinfo. With overwritable DVD or BD media, -multi cannot mark the end of the session. So when adding a new session, this end has to be determined from the payload. Currently only ISO-9660 filesystems can be used that way. See option *-grow_overwriteable_iso*. -dummy Try to perform the drive operations without actually affecting the inserted media. There is no warranty that this will work with a particular combination of drive and media. Blanking is prevented reliably, though. To avoid inadverted real burning, -dummy refuses burn runs on anything but CD-R[W], DVD-R[W], or emulated stdio-drives. -waiti Wait until input data is available at stdin or EOF occurs at stdin. Only then begin to access any drives. One should use this if xorrisofs is working at the end of a pipe where the feeder process reads from the drive before it starts writing its output into xorrisofs. Example: xorrisofs ... -C 0,12800 -M /dev/sr0 ... | \ xorrecord dev=/dev/sr0 ... -waiti - This option works even if standard input is not the track source. If no process is piping in, then the Enter key of your terminal will act as trigger for 'xorrecord'. Note that this input line will not be consumed by cdrskin if standard input is not the track source. It will end up as shell command, usually. tsize=size Announce the exact size of the track source. This is necessary with DVD-R DL media and with quickest blanked DVD-RW, if the size cannot be determined in advance from the track source. E.g. if it is standard input or a named pipe. If the track source does not deliver the predicted amount of bytes, the remainder of the track is padded with zeros. This is not considered an error. If on the other hand the track source delivers more than the announced bytes then the track on media gets truncated to the predicted size and xorrecord exits with non-zero value. -isosize Try to obtain the track size from the content of the track source. This works only if the track source bears an ISO 9660 filesystem. Any other track source content will cause the burn run to abort. If the track source is not a regular file or block device, then this option will work only if the program's fifo size is at least 64k. See option fs=. padsize=size Add the given amount of trailing zeros to the upcoming track. This feature can be disabled by size 0. Default is 300 kB in order to work around a problem with GNU/Linux which often fails to read the last few blocks of a CD track which was written in write mode TAO. TAO is used by 'xorrecord' if the track size cannot be predicted or if the CD medium is not blank but appendable. -nopad The same as padsize=0. -pad The same as padsize=15s. This was once sufficient with older GNU/Linux kernels. Meanwhile one should at least use padsize=128k, if not padsize=300k. -data Explicitly announce that the track source shall be recorded as data track, and not as audio track. This option has no effect with 'xorrecord', because there is no support for other track formats anyway. -tao Explicitly demand that write type TAO shall be used for CD, or Incremental for DVD-R. Normally the program will choose the write type according to the given medium state, option -multi, and track source. Demanding it explicitly prevents the start of a write run, if it is not appropriate to the situation. -sao Explicitly demand that write type SAO shall be used for CD, or DAO for DVD-R. This might prevent the write run, if it is not appropriate to the situation. -dao Alias of -sao. fs=size Set the size of the program fifo buffer to the given value rather than the default of 4m. The fifo buffers a temporary surplus of track source data in order to provide the drive with a steady stream during times of temporary lack of track source supply. Other than cdrecord, xorrecord enables drive buffer underrun protection by default and does not wait with writing until the fifo is full for a first time. On very old CD drives and slow computers, this might cause aborted burn runs. In this case, consider to use program *cdrskin* for CD burning. DVD and BD drives tolerate buffer underrun without problems. The larger the fifo, the longer periods of poor source supply can be compensated. But a large fifo can deprive the operating system of read cache for better filesystem performance. speed=value Set the write speed. Default is 0 = maximum speed. Speed can be given in media type dependent x-speed numbers or as a desired throughput per second in MMC compliant kB (= 1000) or MB (= 1000 kB). Media x-speed factor can be set explicitly by appending "c" for CD, "d" for DVD, "b" for BD. "x" is optional. Example speeds: 706k = 706kB/s = 4c = 4xCD 5540k = 5540kB/s = 4d = 4xDVD If there is no hint about the speed unit attached, then the medium in the drive will decide. Default unit is CD, 1x = 176,400 raw bytes/second. With DVD, 1x = 1,385,000 bytes/second. With BD, 1x = 4,495,625 bytes/second. MMC drives usually activate their own idea of speed and take the speed value given by the burn program only as a hint for their own decision. minbuf=percentage Equivalent to: modesty_on_drive=<percentage> -immed Equivalent to: modesty_on_drive=75 In cdrecord, this also controls use of the Immed bit. But xorriso uses Immed where possible and appropriate, unless it is disabled by option use_immed_bit=off . -eject Eject the drive tray after alll other work is done.  File: xorrecord.info, Node: Verbose, Next: NonCdrecord, Prev: SetBurn, Up: Options 5.4 Program version and verbosity ================================= -version Print to standard output a line beginning by "Cdrecord 2.01-Emulation Copyright" and further lines which report the version of xorriso and its supporting libraries. They also state the license under which the program is provided, and disclaim any warranty, to the extent permitted by law. Afterwards end emulation without performing any drive operation. -v Increase program verbosity by one level. There are four verbosity levels from nearly silent to debugging verbosity. The both highest levels can be enabled by repeated -v or by -vv or by -vvv. -V Log SCSI commands and drive replies to standard error. This might be of interest if *xorrecord* and a particular drive or medium do not cooperate as expected, or if you just want to know how libburn interacts with the drive. To understand this extremely verbose log, one needs to read SCSI specs SPC, SBC, and MMC. Please do not add such a log to a bug report on the first hand, unless you want to point out a particular deviation from said specs, or if you get asked for this log by a maintainer of 'xorrecord' who feels in charge for your bug report. -help Print a sparse list of program options to standard error and declare not to be cdrecord. Afterwards end emulation without performing any drive operation.  File: xorrecord.info, Node: NonCdrecord, Next: ExDevices, Prev: Verbose, Up: Options 5.5 Options not compatible to cdrecord ====================================== --no_rc Only if used as first command line argument this option prevents reading and interpretation of startup files. See section FILES below. --drive_not_exclusive This option disables the use of device file locking mechanisms when acquiring the drive. On GNU/Linux the locking is done by open(O_EXCL), on FreeBSD by flock(LOCK_EX). Be aware that it can cause problems if you use a drive which is mounted, or opened by some other process, or guarded by /dev/pktcdvd*. Make sure that other users of the drive do not cause drive activities while a xorrecord burn run is going on. drive_scsi_dev_family=sr|scd|sg|default GNU/Linux specific: By default, cdrskin tries to map Linux drive addresses to /dev/sr* before they get opened for operating the drive. This coordinates well with other use cases of optical drives, like mount(8). But since year 2010 all /dev/sr* share a global lock which allows only one drive to process an SCSI command while all others have to wait for its completion. This yields awful throughput if more than one drive is writing or reading simultaneously. The global lock is not applied to device files /dev/sg* and also not with the system calls read(2), write(2). But ioctl(SG_IO) is affected, which is needed to perform the SCSI commands for optical burning. So for simultaneous burn runs on modern GNU/Linux it is advisable to use drive_scsi_dev_family="sg". The drive addresses may then well be given as /dev/sr* but will nevertheless get used as /dev/sg*. --grow_overwriteable_iso Enable emulation of multi-session writing on overwritable media which contain an ISO 9660 filesystem. This emulation is learned from growisofs -M but adapted to the usage model of xorrecord -msinfo xorrisofs -C -M | xorrecord -waiti -multi - for sequential media. -grow_overwriteable_iso does not hamper the use of true multi-session media. I.e. it is possible to use the same 'xorrecord' options with both kinds of media and to achieve similar results if ISO 9660 filesystem images are to be written. This option implies option -isosize and therefore demands that the track source is a ISO 9660 filesystem image. With overwritable media and no option blank=fast|all present it expands an eventual ISO 9660 filesystem on media. It is assumed that this image's inner size description points to the end of the valuable data. Overwritable media with a recognizable ISO 9660 size will be regarded as appendable rather than as blank. I.e. options -msinfo and -toc will work. -toc will always show a single session with its size increasing with every added ISO 9660 image. --multi_if_possible Apply option -multi if the medium is suitable. Not suitable are DVD-R DL and DVD-RW, which were blanked with mode "deformat_quickest". Not all drives correctly recognize such fast-blanked DVD-RW which need "on". If there is well founded suspicion that a burn run failed due to -multi, then this causes a re-try without -multi. stream_recording="on"|"off"|number Mode "on" requests that compliance to the desired speed setting is preferred over management of write errors. With DVD-RAM and BD this can bring effective write speed near to the nominal write speed of the media. But it will also disable the automatic use of replacement blocks if write errors occur. It might as well be disliked or ignored by the drive. If a number is given, then error management stays enabled for all byte addresses below that number. Any number below 16s is the same as "off". dvd_obs="default"|"32k"|"64k" Set the number of bytes to be transmitted with each write operation to DVD or BD media. With most write types, tracks get padded up to the next multiple of this write size (see option -obs_pad). A number of 64 KB may improve throughput with bus systems which show latency problems. The default depends on media type, option stream_recording=, and on compile time options. -obs_pad Pad the data of the last write operation of a DVD-R[W] DAO session, or BD-R session, or stdio: pseudo-drive session up to the full size of an output chunk. This padding has to be applied automatically to the other DVD and BD media types, where it causes e.g. ISO images to have trailing unclaimed blocks. Whether it is applied automatically to BD-R depends on option --bdr_obs_exempt. .br Use this option if there is the suspicion that DVD-R[W] DAO or BD-R sessions abort with your kernel and/or DVD drive, if their size is not a multiple of 16 blocks. .br This option may also get enabled at compile time of libburn. -bdr_obs_exempt Exempt BD-R media from automatic unconditional transaction end padding, provided that this padding is not requested by -obs_pad and that no stream_recording is requested. This is a new feature introduced with version 1.5.6. It might become default in later versions. modesty_on_drive=parameter[:parameters] Control whether the drive buffer shall be kept from getting completely filled. Parameter "on" (or "1") keeps the program from trying to write to the burner drive while its buffer is in danger to be filled over a given limit. If this filling is exceeded then the program will wait until the filling reaches a given low percentage value. This can ease the load on operating system and drive controller and thus help with achieving better input bandwidth if disk and burner are not on independent controllers (like hda and hdb). It may also help with simultaneous burns on different burners with Linux kernels like 3.16, if one has reason not to fix the problem by drive_scsi_dev_family="sg". On the other hand it increases the risk of buffer underflow and thus reduced write speed. Some burners are not suitable because they report buffer fill with granularity too coarse in size or time, or expect their buffer to be filled to the top before they go to full speed. Parameters "off" or "0" disable this feature. The threshold for beginning to wait is given by parameter "max_percent=". Parameter "min_percent=" defines the threshold for resuming transmission. Percentages are permissible in the range of 25 to 100. Numbers in this range without a prepended name are interpreted as "on:min_percent=". E.g.: modesty_on_drive=75 The optimal values depend on the buffer behavior of the drive. Parameter "timeout_sec=" defines after which time of unsuccessful waiting the modesty shall be disabled because it does not work. Parameter "min_usec=" defines the initial sleeping period in microseconds. If the drive buffer appears to be too full for sending more data, the program will wait the given time and inquire the buffer fill state again. If repeated inquiry shows not enough free space, the sleep time will slowly be increased to what parameter "max_usec=" defines. Parameters, which are not mentioned with a modesty_on_drive= option, stay unchanged. Default is: modesty_on_drive=off:min_percent=90:max_percent=95: timeout_sec=120:min_usec=5000:max_usec=25000 use_immed_bit="on"|"off"|"default" Control whether several long lasting SCSI commands shall be executed with the Immed bit, which makes the commands end early while the drive operation is still going on. xorriso then inquires progress indication until the drive reports to be ready again. If this feature is turned off, then blanking and formatting will show no progress indication. It may depend on the operating system whether -use_immed_bit is set to "off" by default. write_start_address=byte_offset Set the byte address on overwritable media where to start writing the track. With DVD+RW, DVD-RAM or BD-RE, byte_offset must be aligned to 2 kiB blocks, but better is 32 kiB on DVD and 64 kiB on BD. With formatted DVD-RW 32 kiB alignment is mandatory. Other media are not suitable for this option. stdio_sync="on"|"off"|number Set the number of bytes after which to force output to emulated stdio: drives. This forcing keeps the memory from being clogged with lots of pending data for slow devices. Default "on" is the same as "16m". Forced output can be disabled by "off".  File: xorrecord.info, Node: Examples, Next: Files, Prev: Options, Up: Top 6 Examples ********** * Menu: * ExDevices:: Get an overview of drives and their addresses * ExMedium:: Get info about a particular drive or loaded media * ExBlank:: Prepare CD-RW or DVD-RW for re-use * ExFormat:: Format DVD-RW to avoid need for blanking before re-use * ExDeformat:: De-format DVD-RW to make it capable of multi-session again * ExIsoSingle:: Write a single ISO 9660 filesystem image * ExIsoMulti:: Write multiple ISO 9660 sessions * ExIsoFly:: Write ISO 9660 session on-the-fly * ExAfio:: Write compressed afio archive on-the-fly  File: xorrecord.info, Node: ExDevices, Next: ExMedium, Prev: NonCdrecord, Up: Examples 6.1 Get an overview of drives and their addresses ================================================= $ xorrecord -devices  File: xorrecord.info, Node: ExMedium, Next: ExBlank, Prev: ExDevices, Up: Examples 6.2 Get info about a particular drive and loaded media ====================================================== $ xorrecord dev=/dev/sr0 -atip -toc -grow_overwriteable_iso  File: xorrecord.info, Node: ExBlank, Next: ExFormat, Prev: ExMedium, Up: Examples 6.3 Prepare CD-RW or DVD-RW for re-use ====================================== $ xorrecord -v dev=/dev/sr0 blank=as_needed -eject  File: xorrecord.info, Node: ExFormat, Next: ExDeformat, Prev: ExBlank, Up: Examples 6.4 Format DVD-RW to avoid need for blanking before re-use ========================================================== $ xorrecord -v dev=/dev/sr0 blank=format_overwrite -eject This command may also be used to format BD-R media before first use, in order to enable handling of write errors. Several hundred MB of spare blocks will be reserved and write runs on such media will perform with less than half nominal speed.  File: xorrecord.info, Node: ExDeformat, Next: ExIsoSingle, Prev: ExFormat, Up: Examples 6.5 De-format DVD-RW to make it capable of multi-session again ============================================================== $ xorrecord -v dev=/dev/sr0 blank=deformat  File: xorrecord.info, Node: ExIsoSingle, Next: ExIsoMulti, Prev: ExDeformat, Up: Examples 6.6 Write a single ISO 9660 filesystem image ============================================ $ xorrecord -v dev=/dev/sr0 speed=12 fs=8m \ blank=as_needed -eject padsize=300k my_image.iso  File: xorrecord.info, Node: ExIsoMulti, Next: ExIsoFly, Prev: ExIsoSingle, Up: Examples 6.7 Write multiple ISO 9660 sessions ==================================== This is possible with all media except minimally blanked DVD-RW and DVD-R DL, which cannot do multi-session. The first session is written like in the previous example, except that option -multi is used. It will contain the files of hard disk directory ./tree1 under the ISO 9660 directory /dir1: $ xorrisofs -o image_1.iso -J -graft-points /dir1=./tree1 $ xorrecord -v dev=/dev/sr0 speed=12 fs=8m \ -multi -grow_overwriteable_iso \ blank=as_needed -eject padsize=300k image_1.iso For the second session xorrisofs needs to know the -msinfo numbers of the medium. Further it will read data from the medium by using the system's read-only CD-ROM driver. Many systems do not take notice of xorrecord's write activities. It is necessary to force their attention by ejecting and reloading the drive tray. Therefore above run uses option -eject. Get the -msinfo numbers (and properly reload the tray if it has a motor) by: $ m=$(xorrecord dev=/dev/sr0 -msinfo) Offer a victim to any problem caused by obtrusive demons after tray loading: $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 Use the numbers with xorrisofs to add ./tree2 to the image as /dir2: $ xorrisofs -M /dev/sr0 -C $m -o image_2.iso \ -J -graft-points /dir2=./tree2 Now burn the new session onto the same medium. This time without blanking: $ xorrecord -v dev=/dev/sr0 speed=12 fs=8m \ -multi -grow_overwriteable_iso \ -eject padsize=300k image_2.iso Operating systems which mount this medium will read the superblock of the second session and show both directories /dir1 and /dir2.  File: xorrecord.info, Node: ExIsoFly, Next: ExAfio, Prev: ExIsoMulti, Up: Examples 6.8 Write ISO 9660 session on-the-fly ===================================== It is possible to combine the run of *xorrisofs* and 'xorrecord' in a pipeline without storing the ISO 9660 image as file on hard disk. The piped run is more vulnerable to the problem that some systems have not enough patience with automatic tray loading and that demons may interfere with a first CD-ROM driver read attempt from a freshly loaded medium. It is advised to load the tray manually or via a separate run of xorriso with a subsequent run of dd. Again, xorriso has the patience and dd is a dispensable victim for demons. $ m=$(xorrecord dev=/dev/sr0 -msinfo) $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 $ xorrisofs -M /dev/sr0 -C $m \ -J -graft-points /dir2=./tree2 \ | xorrecord -v dev=/dev/sr0 speed=12 fs=8m \ -waiti -multi -grow_overwriteable_iso \ -eject padsize=300k - This is also the main use case of program *xorriso* itself, where the run would need no system workarounds and simply look like: $ xorriso -dev /dev/sr0 -joliet on -speed 12 -fs 8m \ -map ./tree2 /dir2 -commit_eject all  File: xorrecord.info, Node: ExAfio, Prev: ExIsoFly, Up: Examples 6.9 Write compressed afio archive on-the-fly ============================================ This is possible with all media except minimally blanked DVD-RW and DVD-R DL. Since the compressed output stream is of very variable speed, a larger fifo is advised. Nevertheless, this example is not suitable for very old CD drives which have no underrun protection and thus would abort the burn run on temporary data shortage. $ find . | afio -oZ - | \ xorrecord -v dev=/dev/sr0 speed=12 fs=64m \ -multi padsize=300k - afio archives do not contain references to absolute data block addresses. So they need no special precautions for multi-session. One may get the session start addresses by option -toc, and then use dd option skip= to begin reading at one of those addresses. E.g. for listing its content: $ dd if=/dev/sr0 bs=2048 skip=64046 | afio -tvZ - afio will know when the end of the archive is reached.  File: xorrecord.info, Node: Files, Next: Seealso, Prev: Examples, Up: Top 7 Files ******* 7.1 Startup Files ================= If not -no_rc is given as the first argument then 'xorrecord' attempts on startup to read and execute lines from the following files: /etc/default/xorriso /etc/opt/xorriso/rc /etc/xorriso/xorriso.conf $HOME/.xorrisorc The files are read in the sequence given here, but none of them is required to exist. The lines are not interpreted as 'xorrecord' options but as generic *xorriso* commands. See man xorriso.  File: xorrecord.info, Node: Seealso, Next: Bugreport, Prev: Files, Up: Top 8 See also ********** For generic xorriso command mode xorriso(1) Formatting track sources for xorrecord xorrisofs(1), mkisofs(8), genisoimage(1), afio(1), star(1) Other programs which burn sessions to optical media growisofs(1), cdrecord(1), wodim(1), cdrskin(1)  File: xorrecord.info, Node: Bugreport, Next: Legal, Prev: Seealso, Up: Top 9 Reporting bugs **************** To report bugs, request help, or suggest enhancements for 'xorriso', please send electronic mail to the public list <bug-xorriso@gnu.org>. If more privacy is desired, mail to <scdbackup@gmx.net>. Please describe what you expect 'xorriso' to do, the program arguments or dialog commands by which you tried to achieve it, the messages of 'xorriso', and the undesirable outcome of your program run. Expect to get asked more questions before solutions can be proposed.  File: xorrecord.info, Node: Legal, Next: CommandIdx, Prev: Bugreport, Up: Top 10 Author, Copyright, Credits ***************************** 10.1 Author =========== Thomas Schmitt <scdbackup@gmx.net> for libburnia-project.org 10.2 Copyright ============== Copyright (c) 2011 - 2024 Thomas Schmitt Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of xorriso. If you make use of the license to derive modified versions of xorriso then you are entitled to modify this text under that same license. 10.3 Credits ============ 'xorriso' is in part based on work by Vreixo Formoso who provides libisofs together with Mario Danic who also leads the libburnia team. Thanks to Andy Polyakov who invented emulated growing, to Derek Foreman and Ben Jansens who once founded libburn. Compliments towards Joerg Schilling whose cdrtools served me for ten years.  File: xorrecord.info, Node: CommandIdx, Next: ConceptIdx, Prev: Legal, Up: Top 11 Alphabetic Command List ************************** �[index�] * Menu: * --bdr_obs_exempt write transaction end padding: NonCdrecord. (line 92) * --devices get list of drives: DriveAddr. (line 8) * --drive_not_exclusive use drive even if busy: NonCdrecord. (line 12) * --grow_overwriteable_iso emulate ISO 9660 multi-session: NonCdrecord. (line 37) * --multi_if_possible apply -multi if medium is suitable: NonCdrecord. (line 57) * --no_rc do not execute xorriso startup files: NonCdrecord. (line 8) * --obs_pad write transaction end padding: NonCdrecord. (line 81) * -atip inquire medium state: Inquire. (line 14) * -checkdrive inquire drive CD capabilities: Inquire. (line 11) * -dao explicitly set write type SAO/DAO: SetBurn. (line 172) * -data explicitly announce a data track: SetBurn. (line 157) * -dummy control write simulation: SetBurn. (line 106) * -eject finally eject drive tray: SetBurn. (line 214) * -help print sparse overview of options: Verbose. (line 30) * -immed keep drive buffer hungry: SetBurn. (line 208) * -inq inquire drive identifiers: Inquire. (line 8) * -isosize obtain track size from ISO 9660 superblock: SetBurn. (line 137) * -modesty_on_drive keep drive buffer hungry: NonCdrecord. (line 98) * -msinfo retrieve multi-session info: Inquire. (line 43) * -multi keep media appendable after burn run: SetBurn. (line 89) * -nopad disable adding of bytes to end of track: SetBurn. (line 151) * -pad add 15 blocks to end of track: SetBurn. (line 153) * -sao explicitly set write type SAO/DAO: SetBurn. (line 168) * -tao explicitly set write type TAO: SetBurn. (line 162) * -toc inquire medium content: Inquire. (line 22) * -v increase program verbosity: Verbose. (line 16) * -V log SCSI command transactions to stderr: Verbose. (line 20) * -version report emulation and xorriso version: Verbose. (line 8) * -waiti access drive only after stdin delivers data: SetBurn. (line 113) * blank= make media re-usabable or format media: SetBurn. (line 28) * dev= address the drive to be used: DriveAddr. (line 21) * drive_scsi_dev_family= Linux device type to be used: NonCdrecord. (line 20) * dvd_obs= set write transaction payload size: NonCdrecord. (line 74) * fs= set program fifo size: SetBurn. (line 174) * minbuf= keep drive buffer hungry: SetBurn. (line 205) * padsize= add bytes to end of track: SetBurn. (line 144) * speed= set write speed: SetBurn. (line 189) * stdio_sync= control stdio buffer: NonCdrecord. (line 150) * stream_recording= try to get full speed on DVD-RAM, BD: NonCdrecord. (line 64) * tsize= set a fixed track size: SetBurn. (line 126) * use_immed_bit= control use of Immed bit: NonCdrecord. (line 135) * write_start_address= set block address for write start: NonCdrecord. (line 144)  File: xorrecord.info, Node: ConceptIdx, Prev: CommandIdx, Up: Top 12 Alphabetic List of Concepts and Objects ****************************************** �[index�] * Menu: * Accessing drive, wait for stdin, -waiti: SetBurn. (line 113) * Blank, format, Immed bit, use_immed_bit=: NonCdrecord. (line 135) * Bugs, reporting: Bugreport. (line 6) * Data track, announce, -data: SetBurn. (line 157) * Defect management, control, stream_recording=: NonCdrecord. (line 64) * Drive, address, dev=: DriveAddr. (line 21) * Drive, get list of, --devices: DriveAddr. (line 8) * Drive, inquire CD capabilities, -checkdrive: Inquire. (line 11) * Drive, inquire identifiers, -inq: Inquire. (line 8) * Drive, Linux device type, drive_scsi_dev_family: NonCdrecord. (line 20) * Drive, use even if busy, --drive_not_exclusive: NonCdrecord. (line 12) * Eject, the tray, -eject: SetBurn. (line 214) * Examples: Examples. (line 6) * Fifo, set size, fs=: SetBurn. (line 174) * Full speed, on DVD-RAM and BD, stream_recording=: NonCdrecord. (line 64) * Media types, _definiton: Standards. (line 23) * Media, blank, blank=: SetBurn. (line 28) * Media, format, blank=: SetBurn. (line 28) * Media, keep appendable, --multi_if_possible: NonCdrecord. (line 57) * Media, keep appendable, -multi: SetBurn. (line 89) * Media, make re-usable, blank=: SetBurn. (line 28) * medium content, inquire, -toc: Inquire. (line 22) * medium state, inquire, -atip: Inquire. (line 14) * MMC, _definiton: Standards. (line 6) * multi-session info, retrieve, -msinfo: Inquire. (line 43) * Multi-session, emulate ISO 9660, --grow_overwriteable_iso: NonCdrecord. (line 37) * Options, overview, -help: Verbose. (line 30) * Overwritable media, _definiton: Standards. (line 30) * Padding, at end of track, padsize=: SetBurn. (line 144) * Padding, disable, -nopad: SetBurn. (line 151) * Padding, insufficient old, -pad: SetBurn. (line 153) * Problems, reporting: Bugreport. (line 6) * SCSI commands, log, -V: Verbose. (line 20) * Sequentially recordable media, _definiton: Standards. (line 25) * Session, _definiton: Standards. (line 12) * Speed, set for writing, speed=: SetBurn. (line 189) * Startup files, do not execute, --no_rc: NonCdrecord. (line 8) * Track size, obtain from ISO 9660, -isosize: SetBurn. (line 137) * Track size, set fixed, tsize=: SetBurn. (line 126) * Track, _definiton: Standards. (line 13) * Transaction end padding, BD-R, -bdr_obs_exempt: NonCdrecord. (line 92) * Transaction end padding, enforce, -obs_pad: NonCdrecord. (line 81) * Transaction size, set, dvd_obs=: NonCdrecord. (line 74) * Tray, eject, -eject: SetBurn. (line 214) * Verbosity, increase, -v: Verbose. (line 16) * Verbosity, SCSI commands, -V: Verbose. (line 20) * Version, report, -version: Verbose. (line 8) * Write simulation , control, -dummy: SetBurn. (line 106) * Write start address, set, write_start_address=: NonCdrecord. (line 144) * Write type, SAO/DAO, -dao: SetBurn. (line 172) * Write type, SAO/DAO, -sao: SetBurn. (line 168) * Write type, TAO, -tao: SetBurn. (line 162) * Write, buffer syncing, stdio_sync=: NonCdrecord. (line 150) * Write, drive buffer, -immed: SetBurn. (line 208) * Write, drive buffer, minbuf=: SetBurn. (line 205) * Write, drive buffer, modesty_on_drive=: NonCdrecord. (line 98) * xorriso, mkisofs emulation: Xorriso. (line 6) * xorriso, options: Options. (line 6)  Tag Table: Node: Top395 Node: Overview1103 Node: Standards1683 Node: Drive3845 Node: Xorriso5176 Node: Options5885 Node: DriveAddr6253 Node: Inquire7618 Node: SetBurn10487 Node: Verbose21111 Node: NonCdrecord22661 Node: Examples31496 Node: ExDevices32157 Node: ExMedium32375 Node: ExBlank32638 Node: ExFormat32859 Node: ExDeformat33377 Node: ExIsoSingle33644 Node: ExIsoMulti33928 Node: ExIsoFly35672 Node: ExAfio36864 Node: Files37862 Node: Seealso38418 Node: Bugreport38781 Node: Legal39372 Node: CommandIdx40301 Node: ConceptIdx44073  End Tag Table \input texinfo @c -*-texinfo-*- @c %**start of header @setfilename xorrecord.info @settitle GNU xorrecord 1.5.7 @c %**end of header @c @c man-ignore-lines begin @dircategory Archiving @direntry * Xorrecord: (xorrecord). Emulates CD/DVD/BD program cdrecord @end direntry @c man-ignore-lines end @c @c Notes about embedded man page: @c This texinfo code contains the necessary info to produce a man page @c which resembles much the version of xorriso.1 from which this code @c was originally derived in march 2010. @c One can produce the man page by applying the following rules: @c The first line gets discarded. @c Line start "@c man " will become "", the remainder is put out unaltered. @c Lines "@*" will be converted to ".br" @c "@c man-ignore-lines N" will discard N following lines. @c "@c man-ignore-lines begin" discards all following lines @c up to "@c man-ignore-lines end". @c Line blocks of "@menu" "@end menu" will be discarded. @c "@item word words" becomes "\fBword\fR words". @c @b{...}, @command{...}, @dfn{...}, @emph{...}, @strong{...} @c get mapped to \fB...\fR . @c @abbr{...}, @code{...}, @file{...}, @i{...}, @option{...}, @r{...}, @c @ref{...}, @samp{...},@var{...}, get mapped to ... . @c @ref{...}, @xref{...} get mapped to empty text. @c @email{...} gets mapped to <...> . @c Mapped {...} content is subject to the rules except {...} mapping. @c @minus{} will become "-". @c @@ , @{, @} will get stripped of their first @. @c Other lines which begin by "@" will be discarded. @c In lines not stemming from "@c man", "\" becomes "\\" @c "-" which are not preceded by an uneven number of "\" will get @c prepended one "\". @c @c @c man .\" Hey, EMACS: -*- nroff -*- @c man .\" @c man .\" IMPORTANT NOTE: @c man .\" @c man .\" The original of this file is kept in xorriso/xorrecord.texi @c man .\" This here was generated by program xorriso/make_xorriso_1 @c man .\" @c man .\" @c man .\" First parameter, NAME, should be all caps @c man .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection @c man .\" other parameters are allowed: see man(7), man(1) @c man .TH XORRECORD 1 "Version 1.5.7, Apr 19, 2024" @c man .\" Please adjust this date whenever revising the manpage. @c man .\" @c man .\" Some roff macros, for reference: @c man .\" .nh disable hyphenation @c man .\" .hy enable hyphenation @c man .\" .ad l left justify @c man .\" .ad b justify to both left and right margins @c man .\" .nf disable filling @c man .\" .fi enable filling @c man .\" .br insert line break @c man .\" .sp <n> insert n+1 empty lines @c man .\" for manpage-specific macros, see man(7) @c man .nh @c man-ignore-lines begin @copying xorrecord - Emulation of CD/DVD/BD program cdrecord by program xorriso Copyright @copyright{} 2011 - 2023 Thomas Schmitt @quotation Permission is granted to distribute this text freely. @end quotation @end copying @c man-ignore-lines end @titlepage @title Manual of GNU xorriso personality xorrecord 1.5.7 @author Thomas Schmitt @page @vskip 0pt plus 1filll @insertcopying @end titlepage @contents @ifnottex @node Top @top xorrecord 1.5.7 @c man-ignore-lines 1 @c man .SH NAME xorrecord - Emulation of CD/DVD/BD program cdrecord by program xorriso @end ifnottex @menu * Overview:: Overview * Standards:: MMC, Session, Track, Media types * Drive:: Drive preparation and addressing * Xorriso:: Relation to program xorriso * Options:: Options * Examples:: Examples * Files:: Files * Seealso:: See also * Bugreport:: Reporting bugs * Legal:: Author, Copyright, Credits * CommandIdx:: Alphabetic Command List * ConceptIdx:: Alphabetic List of Concepts and Objects @end menu @node Overview, Standards, Top, Top @chapter Overview @c man .SH SYNOPSIS @c man .B xorrecord @c man [ options ] dev=device [track_source] @c man .br @c man .SH DESCRIPTION @c man .PP @command{xorrecord} writes preformatted data to CD, DVD, and BD media. @* @sp 1 @c man .PP It understands some options of program cdrecord from cdrtools by Joerg Schilling. Its implementation is part of program xorriso which shares no source code with cdrtools, but rather makes use of libburn for communicating with the drive. @* @sp 1 Another, more complete cdrecord emulator is program @strong{cdrskin} which uses the same burn functions as @strong{xorrecord}, but is able to burn audio CDs and to handle CD-TEXT. @c man .SS @node Standards, Drive, Overview, Top @chapter MMC, Session, Track, Media types @c man \fBMMC, Session, Track, Media types:\fR @c man .br @cindex MMC, _definiton @strong{MMC} is a standard out of the SCSI family which defines the interaction between computers and optical drives. Since more than a decade all CD, DVD, or BD recorders obey this standard regardless by what bus cabling they are attached to the computer. libburn relies on this standard compliance and on the capability of the operating system to perform SCSI transactions over the particular bus cabling. @* @cindex Session, _definiton A @strong{Session} is a data region on an optical disc which usually gets written in a single sweep. It contains at least one @cindex Track, _definiton @strong{Track} which is a contiguous string of readable blocks. @command{xorrecord} produces a single session with a single data track which consists of blocks with 2048 bytes each. It chooses the write mode automatically according to media type, medium state, and option -multi. @* On CD media there are other track types, like audio, and particular write modes like TAO and SAO. CD and DVD- media can put more than one track into a session. Some of these features can be addressed by program @strong{cdrskin}. @* @sp 1 @cindex Media types, _definiton MMC describes several recordable @strong{media types} which roughly form two families. @* @cindex Sequentially recordable media, _definiton @strong{Sequentially recordable media} are CD-R, CD-RW, DVD-R, DVD-R DL, DVD-RW, DVD+R, DVD+R DL, BD-R. Except DVD-R DL they can store more than one session if there is still unwritten space and if the previous session was written with option @strong{-multi}. CD-RW and DVD-RW can be blanked in order to be re-usable from scratch. @* @cindex Overwritable media, _definiton @strong{Overwritable media} are DVD-RAM, DVD+RW, formatted DVD-RW, BD-RE. They offer a single session with a single track for random access writing. There is no need to blank overwritable media before re-use. @* DVD-RW media are sold in sequentially recordable state but can be formatted once to become overwritable. See options @strong{blank=format_overwrite} and @strong{blank=deformat}. @* If ISO 9660 filesystems are to be stored on overwritable media, then it is possible to emulate multiple sessions, by using option @strong{--grow_overwriteable_iso}. In this case, the need for blanking before re-use is emulated too. @c man .SS @sp 1 @c man .B Drive preparation and addressing: @node Drive, Xorriso, Standards, Top @chapter Drive preparation and addressing @c man .PP The drives, CD, DVD, or BD burners, are accessed via file addresses which are specific to libburn and the operating system. Those addresses get listed by a run of @command{xorrecord --devices} or @command{xorriso -device_links}. @* On GNU/Linux, FreeBSD, and NetBSD, the user needs rw-permission for the device file. On Solaris, the user needs r-permission and privilege "sys_devices", which is usually gained by running @command{xorrecord} via command pfexec. @* These permissions or privileges are needed already for listing a drive. So it might be necessary to get the overview as superuser or via pfexec. @* @command{xorrecord} does not perform cdrecord option -scanbus and does not accept the addresses of form Bus,Target,Lun which are told by -scanbus. If support for these addresses is necessary, consider to use program cdrskin. @* @sp 1 It is possible to let @command{xorrecord} work on emulated drives. Their addresses begin by prefix "stdio:" followed by a file address. The emulated media behavior depends on the file type. See man xorriso for details. @* If standard output is chosen as emulated drive, then all program result texts, which usually appear on standard output, will get redirected to standard error. @c man .SS @node Xorriso, Options, Drive, Top @chapter Relation to program xorriso @c man \fBRelation to program xorriso:\fR @c man .br @cindex xorriso, mkisofs emulation @command{xorrecord} is actually a command mode of program @strong{xorriso}, which gets entered either by xorriso command "-as cdrecord" or by starting the program by one of the names "xorrecord", "cdrecord", "wodim", or "cdrskin". @* This command mode can be left by argument "@minus{}@minus{}" which leads to generic xorriso command mode. See @strong{man xorriso} for its description. Other than in xorriso command mode, the sequence of the cdrecord emulation options does not matter. All pending actions get performed in a fixed sequence before the program run ends or before cdrecord emulation ends. @c man .SS @node Options, Examples, Xorriso, Top @chapter Options @cindex xorriso, options @c man .br @c man .SH OPTIONS @c man .br @menu * DriveAddr:: Drive addressing * Inquire:: Inquiring drive and media * SetBurn:: Settings for the burn run * Verbose:: Program version and verbosity * NonCdrecord:: Options not compatible to cdrecord @end menu @c man .PP @c man .TP @c man .B Addressing the drive: @node DriveAddr, Inquire, Options, Options @section Addressing the drive @table @asis @sp 1 @c man .TP @item @minus{}@minus{}devices @kindex @minus{}@minus{}devices get list of drives @cindex Drive, get list of, @minus{}@minus{}devices Print the list of accessible CD, DVD, or BD drives to standard output. Drives might be inaccessible if the user lacks of permissions to use them or if the drive is in use by another program. @* Each accessible drive is shown by a line like: @* 0 -dev '/dev/sr0' rwrw-- : 'TSSTcorp' 'CDDVDW SH-S203B' @* The libburn address of this drive is '/dev/sr0'. 'TSSTcorp' is the name of the vendor (in this case: Toshiba Samsung Storage Technologies Corporation), 'CDDVDW SH-S203B' is the model name (in this case: a DVD burner). @* Afterwards end emulation without performing any further drive operation. @c man .TP @item dev=drive_address @kindex dev= address the drive to be used @cindex Drive, address, dev= Set the libburn address of the drive to be used. @* E.g. on GNU/Linux: dev=/dev/sr0 @* E.g. on FreeBSD: dev=/dev/cd0 @* E.g. on NetBSD: dev=/dev/rcd0d @* E.g. on Solaris: dev=/dev/rdsk/c2t2d0s2 @* See also above "Drive preparation and addressing". @* The medium in the drive should not be mounted or be otherwise in use. @* This option will only get into effect if a track source, a blank= option, or a drive inquiry option is given. Else it will lead to a SORRY event and normally cause a non-zero exit value. @end table @c man .TP @c man .B Inquiring drive and media: @node Inquire, SetBurn, DriveAddr, Options @section Inquiring drive and media @table @asis @sp 1 @c man .TP @item -inq @kindex -inq inquire drive identifiers @cindex Drive, inquire identifiers, -inq Print to standard output: vendor, model name, and firmware revision of the drive. @c man .TP @item -checkdrive @kindex -checkdrive inquire drive CD capabilities @cindex Drive, inquire CD capabilities, -checkdrive Print unconditionally that the drive supports burnfree, SAO, and TAO. Also print the output of option -inq. @c man .TP @item -atip @kindex -atip inquire medium state @cindex medium state, inquire, -atip Print the output of -checkdrive, the most capable profile of the medium in the drive, the list of profiles which are supported by the drive, whether it is erasable (i.e. can be blanked), the media manufacturer, and the medium product name. @* Profiles are usage models, which are often tied to a particular media type (e.g. CD-RW), but may also apply to a family of media. E.g. profile CD-ROM applies to all CD media which contain data. @c man .TP @item -toc @kindex -toc inquire medium content @cindex medium content, inquire, -toc Print a table of content of the medium in the drive. The output is not compatible to cdrecord option -toc, but rather the one of @command{xorriso} command -toc. It lists the address, vendor, model name, and firmware revision of the drive. @* About the medium it tells product name and manufacturer, whether there is already content written, and if so, whether the medium is closed or appendable. Appendable media can take another session. The amount of readable and writable data is told. If there are sessions, then their start block address and size is reported. If a session contains an ISO 9660 filesystem, then its Volume Id is reported. If the medium is writable, then the next writable block address is reported. @* If not option @strong{--grow_overwriteable_iso} is given or no ISO 9660 file system is present on the medium, then overwritable media are reported as being blank. This is due to the fact that they can be written from scratch without further preparation, and that MMC does not distinguish between data written by the most previous burn run and older data which have not been overwritten by that burn run. Consequently, these media are reported with 0 readable blocks, although all their writable blocks normally are readable, too. @c man .TP @item -msinfo @kindex -msinfo retrieve multi-session info @cindex multi-session info, retrieve, -msinfo Print the argument text for option -C of programs mkisofs, genisoimage, or xorrisofs. It consists of two numbers separated by a comma. @* The first number tells the first block of the first track of the last recorded session. This is also the address used by default when operating systems mount a medium with e.g. ISO 9660 filesystem. @* The second number tells the next writable address, where @command{xorrecord} will begin to write the next session. @* This option is only valid for written, appendable media. In all other cases it will yield no output text but will abort the program with non-zero exit value. @end table @c man .TP @c man .B Settings for the burn run: @node SetBurn, Verbose, Inquire, Options @section Settings for the burn run @table @asis @c man .PP A burn run requires exactly one track source address argument, which tells from where to read the data which shall be put into the upcoming session. The medium state must be either blank or appendable. @* Track source may be "-" for standard input or the address of a readable file of any type except directories. Nearly all media types accept a track source with unpredictable byte count, like standard input or named pipes. Nevertheless, DVD-R DL and DVD-RW blanked by mode deformat_quickest demand exact in-advance reservation of the track size, so that they either need to be read from a source of predictable length, or need to be accompanied by option @strong{tsize=} or by option @strong{-isosize}. @* Several options expect a size value as argument. A number with a trailing letter "b" or without a trailing letter is a plain byte count. Other trailing letters cause multiplication of the given number by a scaling factor: @* "k" or "K" = 1024 , "m" or "M" = 1024k , "g" or "G" = 1024m , "s" or "S" = 2048 @* E.g. tsize=234567s means a size of 234567 * 2048 = 480393216 bytes. @sp 1 @c man .TP @item blank=mode @kindex blank= make media re-usabable or format media @cindex Media, blank, blank= @cindex Media, make re-usable, blank= @cindex Media, format, blank= Blank a CD-RW or DVD-RW to make it re-usable from scratch. Format a DVD-RW, DVD+RW, DVD-RAM, BD-R, or BD-RE if not yet formatted. @* This operation normally makes any recorded data on the medium unreadable. It is combinable with burning in the same run of @command{xorrecord}, or it may be performed without a track source, leaving the medium empty. @* The mode given with blank= selects the particular behavior: @c man .RS @c man .TP @* @sp 1 as_needed @* Try to make the media ready for writing from scratch. If it needs formatting, then format it. If it is not blank, then try to apply blank=fast. It is a reason to abort if the medium cannot assume thoroughly writeable state, e.g. if it is a non-blank write-once. @* This leaves unformatted DVD-RW in unformatted blank state. To format DVD-RW use blank=format_overwrite. Blank unformatted BD-R stay unformatted. @* (Note: blank=as_needed is not an original cdrecord option.) @c man .TP @* @sp 1 all @* Blank an entire CD-RW or an unformatted DVD-RW. @c man .TP @sp 1 @* fast @* Minimally blank an entire CD-RW or blank an unformatted DVD-RW. @c man .TP @sp 1 @* deformat @* Like blank=all but with the additional ability to blank overwritable DVD-RW. This will destroy their formatting and make them sequentially recordable. @* (Note: blank=deformat is not an original cdrecord options) @c man .TP @sp 1 @* deformat_quickest @* Like blank=deformat but blanking DVD-RW only minimally. This is faster than full blanking but yields media incapable of writing tracks of unpredictable size. Multi-session will not be possible either. @* (Note: blank=deformat_quickest is not an original cdrecord option.) @c man .TP @sp 1 @* format_overwrite @* Format a DVD-RW to "Restricted Overwrite". The user should bring some patience. @* Format unformatted DVD+RW, BD-RE or blank BD-R to their default size. It is not mandatory to do this with DVD+RW and BD-RE media, because they will get formatted automatically on the first write attempt. @* BD-R media may be written in unformatted state. This keeps disabled the replacement of bad blocks and enables full nominal write speed. Once BD-R media are written, they cannot be formatted any more. @* For re-formatting already formatted media or for formatting with non-default size, use program @strong{xorriso} with command @strong{-format}. @* (Note: blank=format_overwrite is not an original cdrecord options) @c man .TP @sp 1 @* help @* Print a short overview of blank modes to standard error output. @* Afterwards end emulation without performing any drive operation. @c man .RE @c man .TP @item -multi @kindex -multi keep media appendable after burn run @cindex Media, keep appendable, -multi This option keeps CD, unformatted DVD-R[W], DVD+R, or BD-R appendable after the current session has been written. Without it the disc gets closed and may not be written any more - unless it is a -RW and gets blanked, which causes loss of its content. @* This option cannot be applied to DVD-R DL or to DVD-RW which were blanked by mode "deformat_quickest". Option --multi_if_possible may automatically recognize and handle this situation. @* In order to have all filesystem content accessible, the eventual ISO-9660 filesystem of a follow-up session needs to be prepared in a special way by the filesystem formatter program. mkisofs, genisoimage, and xorrisofs expect particular info about the situation which can be retrieved by @command{xorrecord} option -msinfo. @* With overwritable DVD or BD media, -multi cannot mark the end of the session. So when adding a new session, this end has to be determined from the payload. Currently only ISO-9660 filesystems can be used that way. See option @strong{--grow_overwriteable_iso}. @c man .TP @item -dummy @kindex -dummy control write simulation @cindex Write simulation , control, -dummy Try to perform the drive operations without actually affecting the inserted media. There is no warranty that this will work with a particular combination of drive and media. Blanking is prevented reliably, though. To avoid inadverted real burning, -dummy refuses burn runs on anything but CD-R[W], DVD-R[W], or emulated stdio-drives. @c man .TP @item -waiti @kindex -waiti access drive only after stdin delivers data @cindex Accessing drive, wait for stdin, -waiti Wait until input data is available at stdin or EOF occurs at stdin. Only then begin to access any drives. @* One should use this if xorrisofs is working at the end of a pipe where the feeder process reads from the drive before it starts writing its output into xorrisofs. Example: @* xorrisofs ... -C 0,12800 -M /dev/sr0 ... | \ @* xorrecord dev=/dev/sr0 ... -waiti - @* This option works even if standard input is not the track source. If no process is piping in, then the Enter key of your terminal will act as trigger for @command{xorrecord}. Note that this input line will not be consumed by cdrskin if standard input is not the track source. It will end up as shell command, usually. @c man .TP @item tsize=size @kindex tsize= set a fixed track size @cindex Track size, set fixed, tsize= Announce the exact size of the track source. This is necessary with DVD-R DL media and with quickest blanked DVD-RW, if the size cannot be determined in advance from the track source. E.g. if it is standard input or a named pipe. @* If the track source does not deliver the predicted amount of bytes, the remainder of the track is padded with zeros. This is not considered an error. If on the other hand the track source delivers more than the announced bytes then the track on media gets truncated to the predicted size and xorrecord exits with non-zero value. @c man .TP @item -isosize @kindex -isosize obtain track size from ISO 9660 superblock @cindex Track size, obtain from ISO 9660, -isosize Try to obtain the track size from the content of the track source. This works only if the track source bears an ISO 9660 filesystem. Any other track source content will cause the burn run to abort. @* If the track source is not a regular file or block device, then this option will work only if the program's fifo size is at least 64k. See option fs=. @c man .TP @item padsize=size @kindex padsize= add bytes to end of track @cindex Padding, at end of track, padsize= Add the given amount of trailing zeros to the upcoming track. This feature can be disabled by size 0. Default is 300 kB in order to work around a problem with GNU/Linux which often fails to read the last few blocks of a CD track which was written in write mode TAO. TAO is used by @command{xorrecord} if the track size cannot be predicted or if the CD medium is not blank but appendable. @c man .TP @item -nopad @kindex -nopad disable adding of bytes to end of track @cindex Padding, disable, -nopad The same as padsize=0. @c man .TP @item -pad @kindex -pad add 15 blocks to end of track @cindex Padding, insufficient old, -pad The same as padsize=15s. This was once sufficient with older GNU/Linux kernels. Meanwhile one should at least use padsize=128k, if not padsize=300k. @c man .TP @item -data @kindex -data explicitly announce a data track @cindex Data track, announce, -data Explicitly announce that the track source shall be recorded as data track, and not as audio track. This option has no effect with @command{xorrecord}, because there is no support for other track formats anyway. @c man .TP @item -tao @kindex -tao explicitly set write type TAO @cindex Write type, TAO, -tao Explicitly demand that write type TAO shall be used for CD, or Incremental for DVD-R. Normally the program will choose the write type according to the given medium state, option -multi, and track source. Demanding it explicitly prevents the start of a write run, if it is not appropriate to the situation. @c man .TP @item -sao @kindex -sao explicitly set write type SAO/DAO @cindex Write type, SAO/DAO, -sao Explicitly demand that write type SAO shall be used for CD, or DAO for DVD-R. This might prevent the write run, if it is not appropriate to the situation. @c man .TP @item -dao @kindex -dao explicitly set write type SAO/DAO @cindex Write type, SAO/DAO, -dao Alias of -sao. @c man .TP @item fs=size @kindex fs= set program fifo size @cindex Fifo, set size, fs= Set the size of the program fifo buffer to the given value rather than the default of 4m. @* The fifo buffers a temporary surplus of track source data in order to provide the drive with a steady stream during times of temporary lack of track source supply. @* Other than cdrecord, xorrecord enables drive buffer underrun protection by default and does not wait with writing until the fifo is full for a first time. On very old CD drives and slow computers, this might cause aborted burn runs. In this case, consider to use program @strong{cdrskin} for CD burning. DVD and BD drives tolerate buffer underrun without problems. @* The larger the fifo, the longer periods of poor source supply can be compensated. But a large fifo can deprive the operating system of read cache for better filesystem performance. @c man .TP @item speed=value @kindex speed= set write speed @cindex Speed, set for writing, speed= Set the write speed. Default is 0 = maximum speed. Speed can be given in media type dependent x-speed numbers or as a desired throughput per second in MMC compliant kB (= 1000) or MB (= 1000 kB). Media x-speed factor can be set explicitly by appending "c" for CD, "d" for DVD, "b" for BD. "x" is optional. @* Example speeds: @* 706k = 706kB/s = 4c = 4xCD @* 5540k = 5540kB/s = 4d = 4xDVD @* If there is no hint about the speed unit attached, then the medium in the drive will decide. Default unit is CD, 1x = 176,400 raw bytes/second. With DVD, 1x = 1,385,000 bytes/second. With BD, 1x = 4,495,625 bytes/second. @* MMC drives usually activate their own idea of speed and take the speed value given by the burn program only as a hint for their own decision. @c man .TP @item minbuf=percentage @kindex minbuf= keep drive buffer hungry @cindex Write, drive buffer, minbuf= Equivalent to: @* modesty_on_drive=<percentage> @c man .TP @item -immed @kindex -immed keep drive buffer hungry @cindex Write, drive buffer, -immed Equivalent to: @* modesty_on_drive=75 @* In cdrecord, this also controls use of the Immed bit. But xorriso uses Immed where possible and appropriate, unless it is disabled by option use_immed_bit=off . @c man .TP @item -eject @kindex -eject finally eject drive tray @cindex Eject, the tray, -eject @cindex Tray, eject, -eject Eject the drive tray after alll other work is done. @end table @c man .TP @c man .B Program version and verbosity: @node Verbose, NonCdrecord, SetBurn, Options @section Program version and verbosity @table @asis @sp 1 @c man .TP @item -version @kindex -version report emulation and xorriso version @cindex Version, report, -version Print to standard output a line beginning by @* "Cdrecord 2.01-Emulation Copyright" @* and further lines which report the version of xorriso and its supporting libraries. They also state the license under which the program is provided, and disclaim any warranty, to the extent permitted by law. @* Afterwards end emulation without performing any drive operation. @c man .TP @item -v @kindex -v increase program verbosity @cindex Verbosity, increase, -v Increase program verbosity by one level. There are four verbosity levels from nearly silent to debugging verbosity. The both highest levels can be enabled by repeated -v or by -vv or by -vvv. @c man .TP @item -V @kindex -V log SCSI command transactions to stderr @cindex Verbosity, SCSI commands, -V @cindex SCSI commands, log, -V Log SCSI commands and drive replies to standard error. This might be of interest if @strong{xorrecord} and a particular drive or medium do not cooperate as expected, or if you just want to know how libburn interacts with the drive. To understand this extremely verbose log, one needs to read SCSI specs SPC, SBC, and MMC. @* Please do not add such a log to a bug report on the first hand, unless you want to point out a particular deviation from said specs, or if you get asked for this log by a maintainer of @command{xorrecord} who feels in charge for your bug report. @c man .TP @item -help @kindex -help print sparse overview of options @cindex Options, overview, -help Print a sparse list of program options to standard error and declare not to be cdrecord. @* Afterwards end emulation without performing any drive operation. @end table @c man .TP @c man .B Options not compatible to cdrecord: @node NonCdrecord, ExDevices, Verbose, Options @section Options not compatible to cdrecord @table @asis @sp 1 @c man .TP @item @minus{}@minus{}no_rc @kindex @minus{}@minus{}no_rc do not execute xorriso startup files @cindex Startup files, do not execute, @minus{}@minus{}no_rc Only if used as first command line argument this option prevents reading and interpretation of startup files. See section FILES below. @c man .TP @item @minus{}@minus{}drive_not_exclusive @kindex @minus{}@minus{}drive_not_exclusive use drive even if busy @cindex Drive, use even if busy, @minus{}@minus{}drive_not_exclusive This option disables the use of device file locking mechanisms when acquiring the drive. On GNU/Linux the locking is done by open(O_EXCL), on FreeBSD by flock(LOCK_EX). @* Be aware that it can cause problems if you use a drive which is mounted, or opened by some other process, or guarded by /dev/pktcdvd*. Make sure that other users of the drive do not cause drive activities while a xorrecord burn run is going on. @c man .TP @item drive_scsi_dev_family=sr|scd|sg|default @kindex drive_scsi_dev_family= Linux device type to be used @cindex Drive, Linux device type, drive_scsi_dev_family GNU/Linux specific: @* By default, cdrskin tries to map Linux drive addresses to /dev/sr* before they get opened for operating the drive. This coordinates well with other use cases of optical drives, like mount(8). But since year 2010 all /dev/sr* share a global lock which allows only one drive to process an SCSI command while all others have to wait for its completion. This yields awful throughput if more than one drive is writing or reading simultaneously. @* The global lock is not applied to device files /dev/sg* and also not with the system calls read(2), write(2). But ioctl(SG_IO) is affected, which is needed to perform the SCSI commands for optical burning. @* So for simultaneous burn runs on modern GNU/Linux it is advisable to use drive_scsi_dev_family="sg". The drive addresses may then well be given as /dev/sr* but will nevertheless get used as /dev/sg*. @c man .TP @item @minus{}@minus{}grow_overwriteable_iso @kindex @minus{}@minus{}grow_overwriteable_iso emulate ISO 9660 multi-session @cindex Multi-session, emulate ISO 9660, @minus{}@minus{}grow_overwriteable_iso Enable emulation of multi-session writing on overwritable media which contain an ISO 9660 filesystem. This emulation is learned from growisofs -M but adapted to the usage model of @* xorrecord -msinfo @* xorrisofs -C -M | xorrecord -waiti -multi - @* for sequential media. @* --grow_overwriteable_iso does not hamper the use of true multi-session media. I.e. it is possible to use the same @command{xorrecord} options with both kinds of media and to achieve similar results if ISO 9660 filesystem images are to be written. This option implies option -isosize and therefore demands that the track source is a ISO 9660 filesystem image. @* With overwritable media and no option blank=fast|all present it expands an eventual ISO 9660 filesystem on media. It is assumed that this image's inner size description points to the end of the valuable data. Overwritable media with a recognizable ISO 9660 size will be regarded as appendable rather than as blank. I.e. options -msinfo and -toc will work. -toc will always show a single session with its size increasing with every added ISO 9660 image. @c man .TP @item @minus{}@minus{}multi_if_possible @kindex @minus{}@minus{}multi_if_possible apply -multi if medium is suitable @cindex Media, keep appendable, @minus{}@minus{}multi_if_possible Apply option -multi if the medium is suitable. Not suitable are DVD-R DL and DVD-RW, which were blanked with mode "deformat_quickest". @* Not all drives correctly recognize such fast-blanked DVD-RW which need "on". If there is well founded suspicion that a burn run failed due to -multi, then this causes a re-try without -multi. @c man .TP @item stream_recording="on"|"off"|number @kindex stream_recording= try to get full speed on DVD-RAM, BD @cindex Full speed, on DVD-RAM and BD, stream_recording= @cindex Defect management, control, stream_recording= Mode "on" requests that compliance to the desired speed setting is preferred over management of write errors. With DVD-RAM and BD this can bring effective write speed near to the nominal write speed of the media. But it will also disable the automatic use of replacement blocks if write errors occur. It might as well be disliked or ignored by the drive. @* If a number is given, then error management stays enabled for all byte addresses below that number. Any number below 16s is the same as "off". @c man .TP @item dvd_obs="default"|"32k"|"64k" @kindex dvd_obs= set write transaction payload size @cindex Transaction size, set, dvd_obs= Set the number of bytes to be transmitted with each write operation to DVD or BD media. With most write types, tracks get padded up to the next multiple of this write size (see option --obs_pad). A number of 64 KB may improve throughput with bus systems which show latency problems. The default depends on media type, option stream_recording=, and on compile time options. @c man .TP @item --obs_pad @kindex --obs_pad write transaction end padding @cindex Transaction end padding, enforce, --obs_pad Pad the data of the last write operation of a DVD-R[W] DAO session, or BD-R session, or stdio: pseudo-drive session up to the full size of an output chunk. This padding has to be applied automatically to the other DVD and BD media types, where it causes e.g. ISO images to have trailing unclaimed blocks. Whether it is applied automatically to BD-R depends on option @minus{}@minus{}bdr_obs_exempt. .br Use this option if there is the suspicion that DVD-R[W] DAO or BD-R sessions abort with your kernel and/or DVD drive, if their size is not a multiple of 16 blocks. .br This option may also get enabled at compile time of libburn. @c man .TP @item --bdr_obs_exempt @kindex --bdr_obs_exempt write transaction end padding @cindex Transaction end padding, BD-R, --bdr_obs_exempt Exempt BD-R media from automatic unconditional transaction end padding, provided that this padding is not requested by --obs_pad and that no stream_recording is requested. @* This is a new feature introduced with version 1.5.6. It might become default in later versions. @c man .TP @item modesty_on_drive=parameter[:parameters] @kindex -modesty_on_drive keep drive buffer hungry @cindex Write, drive buffer, modesty_on_drive= Control whether the drive buffer shall be kept from getting completely filled. Parameter "on" (or "1") keeps the program from trying to write to the burner drive while its buffer is in danger to be filled over a given limit. If this filling is exceeded then the program will wait until the filling reaches a given low percentage value. @* This can ease the load on operating system and drive controller and thus help with achieving better input bandwidth if disk and burner are not on independent controllers (like hda and hdb). It may also help with simultaneous burns on different burners with Linux kernels like 3.16, if one has reason not to fix the problem by drive_scsi_dev_family="sg". On the other hand it increases the risk of buffer underflow and thus reduced write speed. @* Some burners are not suitable because they report buffer fill with granularity too coarse in size or time, or expect their buffer to be filled to the top before they go to full speed. @* Parameters "off" or "0" disable this feature. @* The threshold for beginning to wait is given by parameter "max_percent=". Parameter "min_percent=" defines the threshold for resuming transmission. Percentages are permissible in the range of 25 to 100. Numbers in this range without a prepended name are interpreted as "on:min_percent=". @* E.g.: modesty_on_drive=75 @* The optimal values depend on the buffer behavior of the drive. @* Parameter "timeout_sec=" defines after which time of unsuccessful waiting the modesty shall be disabled because it does not work. @* Parameter "min_usec=" defines the initial sleeping period in microseconds. If the drive buffer appears to be too full for sending more data, the program will wait the given time and inquire the buffer fill state again. If repeated inquiry shows not enough free space, the sleep time will slowly be increased to what parameter "max_usec=" defines. @* Parameters, which are not mentioned with a modesty_on_drive= option, stay unchanged. Default is: @* modesty_on_drive=off:min_percent=90:max_percent=95: timeout_sec=120:min_usec=5000:max_usec=25000 @c man .TP @item use_immed_bit="on"|"off"|"default" @kindex use_immed_bit= control use of Immed bit @cindex Blank, format, Immed bit, use_immed_bit= Control whether several long lasting SCSI commands shall be executed with the Immed bit, which makes the commands end early while the drive operation is still going on. xorriso then inquires progress indication until the drive reports to be ready again. If this feature is turned off, then blanking and formatting will show no progress indication. @* It may depend on the operating system whether -use_immed_bit is set to "off" by default. @c man .TP @item write_start_address=byte_offset @kindex write_start_address= set block address for write start @cindex Write start address, set, write_start_address= Set the byte address on overwritable media where to start writing the track. With DVD+RW, DVD-RAM or BD-RE, byte_offset must be aligned to 2 kiB blocks, but better is 32 kiB on DVD and 64 kiB on BD. With formatted DVD-RW 32 kiB alignment is mandatory. @* Other media are not suitable for this option. @c man .TP @item stdio_sync="on"|"off"|number @kindex stdio_sync= control stdio buffer @cindex Write, buffer syncing, stdio_sync= Set the number of bytes after which to force output to emulated stdio: drives. This forcing keeps the memory from being clogged with lots of pending data for slow devices. Default "on" is the same as "16m". Forced output can be disabled by "off". @end table @node Examples, Files, Options, Top @chapter Examples @c man .SH EXAMPLES @c man .SS @c man .B Overview of examples: @c man Get an overview of drives and their addresses @c man .br @c man Get info about a particular drive or loaded media @c man .br @c man Prepare CD-RW or DVD-RW for re-use, BD-R for bad block handling @c man .br @c man Format DVD-RW to avoid need for blanking before re-use @c man .br @c man De-format DVD-RW to make it capable of multi-session again @c man .br @c man Write a single ISO 9660 filesystem image @c man .br @c man Write multiple ISO 9660 sessions @c man .br @c man Write ISO 9660 session on-the-fly @c man .br @c man Write compressed afio archive on-the-fly @c man .br @cindex Examples @menu * ExDevices:: Get an overview of drives and their addresses * ExMedium:: Get info about a particular drive or loaded media * ExBlank:: Prepare CD-RW or DVD-RW for re-use * ExFormat:: Format DVD-RW to avoid need for blanking before re-use * ExDeformat:: De-format DVD-RW to make it capable of multi-session again * ExIsoSingle:: Write a single ISO 9660 filesystem image * ExIsoMulti:: Write multiple ISO 9660 sessions * ExIsoFly:: Write ISO 9660 session on-the-fly * ExAfio:: Write compressed afio archive on-the-fly @end menu @c man .SS @c man .B Get an overview of drives and their addresses: @node ExDevices, ExMedium, NonCdrecord, Examples @section Get an overview of drives and their addresses @sp 1 $ xorrecord --devices @c man .SS @c man .B Get info about a particular drive and loaded media: @node ExMedium, ExBlank, ExDevices, Examples @section Get info about a particular drive and loaded media @sp 1 $ xorrecord dev=/dev/sr0 -atip -toc --grow_overwriteable_iso @c man .SS @c man .B Prepare CD-RW or DVD-RW for re-use: @node ExBlank, ExFormat, ExMedium, Examples @section Prepare CD-RW or DVD-RW for re-use @sp 1 $ xorrecord -v dev=/dev/sr0 blank=as_needed -eject @c man .SS @c man .B Format DVD-RW to avoid need for blanking before re-use: @node ExFormat, ExDeformat, ExBlank, Examples @section Format DVD-RW to avoid need for blanking before re-use @sp 1 $ xorrecord -v dev=/dev/sr0 blank=format_overwrite -eject @* @sp 1 This command may also be used to format BD-R media before first use, in order to enable handling of write errors. Several hundred MB of spare blocks will be reserved and write runs on such media will perform with less than half nominal speed. @c man .SS @c man .B De-format DVD-RW to make it capable of multi-session again: @node ExDeformat, ExIsoSingle, ExFormat, Examples @section De-format DVD-RW to make it capable of multi-session again @sp 1 $ xorrecord -v dev=/dev/sr0 blank=deformat @c man .SS @c man .B Write a single ISO 9660 filesystem image: @node ExIsoSingle, ExIsoMulti, ExDeformat, Examples @section Write a single ISO 9660 filesystem image @sp 1 $ xorrecord -v dev=/dev/sr0 speed=12 fs=8m \ blank=as_needed -eject padsize=300k my_image.iso @c man .SS @c man .B Write multiple ISO 9660 sessions: @node ExIsoMulti, ExIsoFly, ExIsoSingle, Examples @section Write multiple ISO 9660 sessions This is possible with all media except minimally blanked DVD-RW and DVD-R DL, which cannot do multi-session. @* The first session is written like in the previous example, except that option -multi is used. It will contain the files of hard disk directory ./tree1 under the ISO 9660 directory /dir1: @* @sp 1 $ xorrisofs -o image_1.iso -J -graft-points /dir1=./tree1 @* $ xorrecord -v dev=/dev/sr0 speed=12 fs=8m \ @* -multi --grow_overwriteable_iso \ @* blank=as_needed -eject padsize=300k image_1.iso @* @sp 1 For the second session xorrisofs needs to know the -msinfo numbers of the medium. Further it will read data from the medium by using the system's read-only CD-ROM driver. @* Many systems do not take notice of xorrecord's write activities. It is necessary to force their attention by ejecting and reloading the drive tray. Therefore above run uses option -eject. @* Get the -msinfo numbers (and properly reload the tray if it has a motor) by: @* $ m=$(xorrecord dev=/dev/sr0 -msinfo) @* @sp 1 Offer a victim to any problem caused by obtrusive demons after tray loading: @* $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 @* @sp 1 Use the numbers with xorrisofs to add ./tree2 to the image as /dir2: @* @sp 1 $ xorrisofs -M /dev/sr0 -C $m -o image_2.iso \ @* -J -graft-points /dir2=./tree2 @* @sp 1 Now burn the new session onto the same medium. This time without blanking: @* @sp 1 $ xorrecord -v dev=/dev/sr0 speed=12 fs=8m \ @* -multi --grow_overwriteable_iso \ @* -eject padsize=300k image_2.iso @* @sp 1 Operating systems which mount this medium will read the superblock of the second session and show both directories /dir1 and /dir2. @c man .SS @c man .B Write ISO 9660 session on-the-fly: @node ExIsoFly, ExAfio, ExIsoMulti, Examples @section Write ISO 9660 session on-the-fly It is possible to combine the run of @strong{xorrisofs} and @command{xorrecord} in a pipeline without storing the ISO 9660 image as file on hard disk. @* The piped run is more vulnerable to the problem that some systems have not enough patience with automatic tray loading and that demons may interfere with a first CD-ROM driver read attempt from a freshly loaded medium. It is advised to load the tray manually or via a separate run of xorriso with a subsequent run of dd. @* Again, xorriso has the patience and dd is a dispensable victim for demons. @* @sp 1 $ m=$(xorrecord dev=/dev/sr0 -msinfo) @* @sp 1 $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 @* @sp 1 $ xorrisofs -M /dev/sr0 -C $m \ @* -J -graft-points /dir2=./tree2 \ @* | xorrecord -v dev=/dev/sr0 speed=12 fs=8m \ @* -waiti -multi --grow_overwriteable_iso \ @* -eject padsize=300k - @* @sp 1 This is also the main use case of program @strong{xorriso} itself, where the run would need no system workarounds and simply look like: @* @sp 1 $ xorriso -dev /dev/sr0 -joliet on -speed 12 -fs 8m \ @* -map ./tree2 /dir2 -commit_eject all @c man .SS @c man .B Write compressed afio archive on-the-fly: @node ExAfio, , ExIsoFly, Examples @section Write compressed afio archive on-the-fly This is possible with all media except minimally blanked DVD-RW and DVD-R DL. Since the compressed output stream is of very variable speed, a larger fifo is advised. Nevertheless, this example is not suitable for very old CD drives which have no underrun protection and thus would abort the burn run on temporary data shortage. @* @sp 1 $ find . | afio -oZ - | \ @* xorrecord -v dev=/dev/sr0 speed=12 fs=64m \ @* -multi padsize=300k - @* @sp 1 afio archives do not contain references to absolute data block addresses. So they need no special precautions for multi-session. One may get the session start addresses by option -toc, and then use dd option skip= to begin reading at one of those addresses. E.g. for listing its content: @* @sp 1 $ dd if=/dev/sr0 bs=2048 skip=64046 | afio -tvZ - @* @sp 1 afio will know when the end of the archive is reached. @c man .SH FILES @node Files, Seealso, Examples, Top @chapter Files @c man .SS @c man .B Startup files: @section Startup Files @* If not --no_rc is given as the first argument then @command{xorrecord} attempts on startup to read and execute lines from the following files: @* @sp 1 /etc/default/xorriso @* /etc/opt/xorriso/rc @* /etc/xorriso/xorriso.conf @* $HOME/.xorrisorc @* @sp 1 The files are read in the sequence given here, but none of them is required to exist. The lines are not interpreted as @command{xorrecord} options but as generic @strong{xorriso} commands. See man xorriso. @c man .SH SEE ALSO @c man .TP @c man For generic xorriso command mode @c man .BR xorriso(1) @c man .TP @c man Formatting track sources for xorrecord: @c man .BR xorrisofs(1), @c man .BR mkisofs(8), @c man .BR genisoimage(8), @c man .BR afio(1), @c man .BR star(1) @c man .TP @c man Other programs which burn sessions to optical media @c man .BR growisofs(1), @c man .BR cdrecord(1), @c man .BR wodim(1), @c man .BR cdrskin(1) @c man-ignore-lines begin @node Seealso, Bugreport, Files, Top @chapter See also @table @asis @item For generic xorriso command mode xorriso(1) @item Formatting track sources for xorrecord xorrisofs(1), mkisofs(8), genisoimage(1), afio(1), star(1) @item Other programs which burn sessions to optical media growisofs(1), cdrecord(1), wodim(1), cdrskin(1) @end table @c man-ignore-lines end @c man .SH BUGS @node Bugreport, Legal, Seealso, Top @chapter Reporting bugs @cindex Bugs, reporting @cindex Problems, reporting To report bugs, request help, or suggest enhancements for @command{xorriso}, please send electronic mail to the public list @email{bug-xorriso@@gnu.org}. If more privacy is desired, mail to @email{scdbackup@@gmx.net}. @* @sp 1 Please describe what you expect @command{xorriso} to do, the program arguments or dialog commands by which you tried to achieve it, the messages of @command{xorriso}, and the undesirable outcome of your program run. @* @sp 1 Expect to get asked more questions before solutions can be proposed. @c man .SH AUTHOR @node Legal, CommandIdx, Bugreport, Top @chapter Author, Copyright, Credits @section Author Thomas Schmitt <scdbackup@@gmx.net> @* for libburnia-project.org @c man .SH COPYRIGHT @section Copyright Copyright (c) 2011 - 2024 Thomas Schmitt @* Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of xorriso. If you make use of the license to derive modified versions of xorriso then you are entitled to modify this text under that same license. @c man .SH CREDITS @section Credits @command{xorriso} is in part based on work by Vreixo Formoso who provides libisofs together with Mario Danic who also leads the libburnia team. Thanks to Andy Polyakov who invented emulated growing, to Derek Foreman and Ben Jansens who once founded libburn. @* Compliments towards Joerg Schilling whose cdrtools served me for ten years. @c man-ignore-lines begin @node CommandIdx, ConceptIdx, Legal, Top @chapter Alphabetic Command List @printindex ky @node ConceptIdx,, CommandIdx, Top @chapter Alphabetic List of Concepts and Objects @printindex cp @c man-ignore-lines end @bye .\" Hey, EMACS: -*- nroff -*- .\" .\" IMPORTANT NOTE: .\" .\" The original of this file is kept in xorriso/xorriso-tcltk.texi .\" This here was generated by program xorriso/make_xorriso_1 .\" .\" .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH XORRISO-TCLTK 1 "Version 1.5.7, Jun 07, 2023" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp <n> insert n+1 empty lines .\" for manpage-specific macros, see man(7) .nh .SH NAME xorriso\-tcltk \- Educational GUI frontend for xorriso .SH SYNOPSIS .B xorriso-tcltk [ options ] .br .SH DESCRIPTION .PP \fBxorriso\-tcltk\fR demonstrates xorriso use cases by a collection of GUI components. .br .PP It creates a window with buttons, editable text fields, and list boxes. For exploration simply start \fBxorriso\-tcltk\fR without any options. .br .PP Click on the "Help" button at the upper right edge to get an overview help text in a separate window. It explains the three main parts of the GUI window and it gives examples for a few main use cases of xorriso. .br .PP Click by the rightmost mouse button on any button, list box, or text field, to get a specific help text in another window. .br .PP \fBxorriso\fR is a program which copies file objects from POSIX compliant filesystems into Rock Ridge enhanced ISO 9660 filesystems and performs session\-wise manipulation of such filesystems. It can load the management information of existing ISO images and it writes the session results to optical media or to filesystem objects. .br Vice versa \fBxorriso\fR is able to copy file objects out of ISO 9660 filesystems. .SS .br .SH OPTIONS .br .PP Normally, no program options are needed when \fBxorriso\-tcltk\fR gets started. Some of the options are for demonstration of program development. This man page lists only those options which may be helpful for end users. .PP .TP \fB--help\fR Print a help text with the complete list of start options and exit. .TP \fB--no_extract\fR Do not allow extraction of files from ISO filesystem to hard disk. This is not revokable during the program run. .TP \fB--geometry\fR {+|-}X{+|-}Y Set the position of the main window on the screen. +0X is the left edge, \-0X is the right edge, +0Y is the upper edge, \-0Y is the lower edge. .TP \fB--click_to_focus\fR Chooses that input fields and list boxes get the keyboard focus only when being clicked by the mouse. This is the default. .TP \fB--auto_focus\fR Chooses that the keyboard focus is where the mouse pointer is. .SH EXAMPLES Just run \fBxorriso\-tcltk\fR in a shell terminal without any further arguments .SH FILES .SS .B Startup files: .br When starting xorriso, its normal startup files get read and their text lines get executed as commands. See section FILES in the man page of xorriso or chapter Files in the info document of xorriso. .SH SEE ALSO .TP .BR xorriso(1) .SH BUGS To report bugs, request help, or suggest enhancements for \fBxorriso\fR or \fBxorriso\-tcltk\fR, please send electronic mail to the public list <bug\-xorriso@gnu.org>. If more privacy is desired, mail to <scdbackup@gmx.net>. .br Please describe what you expect the program to do, the program arguments, GUI components, or dialog commands by which you tried to achieve it, the messages of \fBxorriso\fR, and the undesirable outcome of your program run. .br Expect to get asked more questions before solutions can be proposed. .SH AUTHOR Thomas Schmitt <scdbackup@gmx.net> .br for libburnia\-project.org .SH COPYRIGHT Copyright (c) 2011 \- 2023 Thomas Schmitt .br Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of xorriso\-tcltk. If you make use of the license to derive modified versions of xorriso\-tcltk then you are entitled to modify this text under that same license. .SH CREDITS \fBxorriso\fR is in part based on work by Vreixo Formoso who provides libisofs together with Mario Danic who also leads the libburnia team. Thanks to Andy Polyakov who invented emulated growing, to Derek Foreman and Ben Jansens who once founded libburn. .br Compliments towards Joerg Schilling whose cdrtools served me for ten years. This is xorriso-tcltk.info, produced by makeinfo version 5.2 from xorriso-tcltk.texi. xorriso-tcltk - Educational GUI frontend for xorriso Copyright (C) 2011 - 2023 Thomas Schmitt Permission is granted to distribute this text freely. INFO-DIR-SECTION Archiving START-INFO-DIR-ENTRY * Xorriso-tcltk: (xorriso-tcltk). Educational GUI frontend for xorriso END-INFO-DIR-ENTRY  File: xorriso-tcltk.info, Node: Top, Next: Overview, Up: (dir) xorriso-tcltk 1.5.7 ******************* xorriso-tcltk - Educational GUI frontend for xorriso * Menu: * Overview:: Overview * Options:: Options * Examples:: Examples * Files:: Files * Seealso:: See also * Bugreport:: Reporting bugs * Legal:: Author, Copyright, Credits * CommandIdx:: Alphabetic Command List * ConceptIdx:: Alphabetic List of Concepts and Objects  File: xorriso-tcltk.info, Node: Overview, Next: Options, Prev: Top, Up: Top 1 Overview ********** 'xorriso-tcltk' demonstrates xorriso use cases by a collection of GUI components. It creates a window with buttons, editable text fields, and list boxes. For exploration simply start 'xorriso-tcltk' without any options. Click on the "Help" button at the upper right edge to get an overview help text in a separate window. It explains the three main parts of the GUI window and it gives examples for a few main use cases of xorriso. Click by the rightmost mouse button on any button, list box, or text field, to get a specific help text in another window. 'xorriso' is a program which copies file objects from POSIX compliant filesystems into Rock Ridge enhanced ISO 9660 filesystems and performs session-wise manipulation of such filesystems. It can load the management information of existing ISO images and it writes the session results to optical media or to filesystem objects. Vice versa 'xorriso' is able to copy file objects out of ISO 9660 filesystems.  File: xorriso-tcltk.info, Node: Options, Next: Examples, Prev: Overview, Up: Top 2 Options ********* Normally, no program options are needed when 'xorriso-tcltk' gets started. Some of the options are for demonstration of program development. This man page lists only those options which may be helpful for end users. --help Print a help text with the complete list of start options and exit. --no_extract Do not allow extraction of files from ISO filesystem to hard disk. This is not revokable during the program run. --geometry {+|-}X{+|-}Y Set the position of the main window on the screen. +0X is the left edge, -0X is the right edge, +0Y is the upper edge, -0Y is the lower edge. --click_to_focus Chooses that input fields and list boxes get the keyboard focus only when being clicked by the mouse. This is the default. --auto_focus Chooses that the keyboard focus is where the mouse pointer is.  File: xorriso-tcltk.info, Node: Examples, Next: Files, Prev: Options, Up: Top 3 Examples ********** Just run 'xorriso-tcltk' in a shell terminal without any further arguments  File: xorriso-tcltk.info, Node: Files, Next: Seealso, Prev: Examples, Up: Top 4 Files ******* 4.1 Startup Files ================= When starting xorriso, its normal startup files get read and their text lines get executed as commands. See section FILES in the man page of xorriso or chapter Files in the info document of xorriso.  File: xorriso-tcltk.info, Node: Seealso, Next: Bugreport, Prev: Files, Up: Top 5 See also ********** info xorriso  File: xorriso-tcltk.info, Node: Bugreport, Next: Legal, Prev: Seealso, Up: Top 6 Reporting bugs **************** To report bugs, request help, or suggest enhancements for 'xorriso' or 'xorriso-tcltk', please send electronic mail to the public list <bug-xorriso@gnu.org>. If more privacy is desired, mail to <scdbackup@gmx.net>. Please describe what you expect the program to do, the program arguments, GUI components, or dialog commands by which you tried to achieve it, the messages of 'xorriso', and the undesirable outcome of your program run. Expect to get asked more questions before solutions can be proposed.  File: xorriso-tcltk.info, Node: Legal, Next: CommandIdx, Prev: Bugreport, Up: Top 7 Author, Copyright, Credits **************************** 7.1 Author ========== Thomas Schmitt <scdbackup@gmx.net> for libburnia-project.org 7.2 Copyright ============= Copyright (c) 2011 - 2023 Thomas Schmitt Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of xorriso-tcltk. If you make use of the license to derive modified versions of xorriso-tcltk then you are entitled to modify this text under that same license. 7.3 Credits =========== 'xorriso' is in part based on work by Vreixo Formoso who provides libisofs together with Mario Danic who also leads the libburnia team. Thanks to Andy Polyakov who invented emulated growing, to Derek Foreman and Ben Jansens who once founded libburn. Compliments towards Joerg Schilling whose cdrtools served me for ten years.  File: xorriso-tcltk.info, Node: CommandIdx, Next: ConceptIdx, Prev: Legal, Up: Top 8 Alphabetic Options List ************************* �[index�] * Menu: * --auto_focus text input where mouse is: Options. (line 23) * --click_to_focus click needed before text input: Options. (line 20) * --geometry set position of main window: Options. (line 16) * --help print help text to stderr: Options. (line 11) * --no_extract ban extraction of files to disk: Options. (line 13)  File: xorriso-tcltk.info, Node: ConceptIdx, Prev: CommandIdx, Up: Top 9 Alphabetic List of Concepts and Objects ***************************************** �[index�] * Menu: * Bugs, reporting: Bugreport. (line 6) * Extraction of files, ban, --no_extract: Options. (line 13) * Help text, print to stderr, --help: Options. (line 11) * Keyboard focus, change by click, --click_to_focus: Options. (line 20) * Keyboard focus, where mouse is, --auto_focus: Options. (line 23) * Main window, position, --geometry: Options. (line 16) * Problems, reporting: Bugreport. (line 6) * xorriso, options: Options. (line 6)  Tag Table: Node: Top386 Node: Overview922 Node: Options2009 Node: Examples2967 Node: Files3151 Node: Seealso3492 Node: Bugreport3615 Node: Legal4249 Node: CommandIdx5186 Node: ConceptIdx5716  End Tag Table \input texinfo @c -*-texinfo-*- @c %**start of header @setfilename xorriso-tcltk.info @settitle GNU xorriso-tcltk 1.5.7 @c %**end of header @c @c man-ignore-lines begin @dircategory Archiving @direntry * Xorriso-tcltk: (xorriso-tcltk). Educational GUI frontend for xorriso @end direntry @c man-ignore-lines end @c @c Notes about embedded man page: @c This texinfo code contains the necessary info to produce a man page @c which resembles much the version of xorriso.1 from which this code @c was originally derived in march 2010. @c One can produce the man page by applying the following rules: @c The first line gets discarded. @c Line start "@c man " will become "", the remainder is put out unaltered. @c Lines "@*" will be converted to ".br" @c "@c man-ignore-lines N" will discard N following lines. @c "@c man-ignore-lines begin" discards all following lines @c up to "@c man-ignore-lines end". @c Line blocks of "@menu" "@end menu" will be discarded. @c "@item word words" becomes "\fBword\fR words". @c @b{...}, @command{...}, @dfn{...}, @emph{...}, @strong{...} @c get mapped to \fB...\fR . @c @abbr{...}, @code{...}, @file{...}, @i{...}, @option{...}, @r{...}, @c @ref{...}, @samp{...},@var{...}, get mapped to ... . @c @ref{...}, @xref{...} get mapped to empty text. @c @email{...} gets mapped to <...> . @c Mapped {...} content is subject to the rules except {...} mapping. @c @minus{} will become "-". @c @@ , @{, @} will get stripped of their first @. @c Other lines which begin by "@" will be discarded. @c In lines not stemming from "@c man", "\" becomes "\\" @c "-" which are not preceded by an uneven number of "\" will get @c prepended one "\". @c @c @c man .\" Hey, EMACS: -*- nroff -*- @c man .\" @c man .\" IMPORTANT NOTE: @c man .\" @c man .\" The original of this file is kept in xorriso/xorriso-tcltk.texi @c man .\" This here was generated by program xorriso/make_xorriso_1 @c man .\" @c man .\" @c man .\" First parameter, NAME, should be all caps @c man .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection @c man .\" other parameters are allowed: see man(7), man(1) @c man .TH XORRISO-TCLTK 1 "Version 1.5.7, Jun 07, 2023" @c man .\" Please adjust this date whenever revising the manpage. @c man .\" @c man .\" Some roff macros, for reference: @c man .\" .nh disable hyphenation @c man .\" .hy enable hyphenation @c man .\" .ad l left justify @c man .\" .ad b justify to both left and right margins @c man .\" .nf disable filling @c man .\" .fi enable filling @c man .\" .br insert line break @c man .\" .sp <n> insert n+1 empty lines @c man .\" for manpage-specific macros, see man(7) @c man .nh @c man-ignore-lines begin @copying xorriso-tcltk - Educational GUI frontend for xorriso Copyright @copyright{} 2011 - 2023 Thomas Schmitt @quotation Permission is granted to distribute this text freely. @end quotation @end copying @c man-ignore-lines end @titlepage @title Manual of GNU xorriso frontend xorriso-tcltk 1.5.7 @author Thomas Schmitt @page @vskip 0pt plus 1filll @insertcopying @end titlepage @contents @ifnottex @node Top @top xorriso-tcltk 1.5.7 @c man-ignore-lines 1 @c man .SH NAME xorriso-tcltk - Educational GUI frontend for xorriso @end ifnottex @menu * Overview:: Overview * Options:: Options * Examples:: Examples * Files:: Files * Seealso:: See also * Bugreport:: Reporting bugs * Legal:: Author, Copyright, Credits * CommandIdx:: Alphabetic Command List * ConceptIdx:: Alphabetic List of Concepts and Objects @end menu @node Overview, Options, Top, Top @chapter Overview @c man .SH SYNOPSIS @c man .B xorriso-tcltk @c man [ options ] @c man .br @c man .SH DESCRIPTION @c man .PP @command{xorriso-tcltk} demonstrates xorriso use cases by a collection of GUI components. @* @sp 1 @c man .PP It creates a window with buttons, editable text fields, and list boxes. For exploration simply start @command{xorriso-tcltk} without any options. @* @sp 1 @c man .PP Click on the "Help" button at the upper right edge to get an overview help text in a separate window. It explains the three main parts of the GUI window and it gives examples for a few main use cases of xorriso. @* @sp 1 @c man .PP Click by the rightmost mouse button on any button, list box, or text field, to get a specific help text in another window. @* @sp 1 @c man .PP @command{xorriso} is a program which copies file objects from POSIX compliant filesystems into Rock Ridge enhanced ISO 9660 filesystems and performs session-wise manipulation of such filesystems. It can load the management information of existing ISO images and it writes the session results to optical media or to filesystem objects. @* Vice versa @command{xorriso} is able to copy file objects out of ISO 9660 filesystems. @c man .SS @node Options, Examples, Overview, Top @chapter Options @cindex xorriso, options @c man .br @c man .SH OPTIONS @c man .br @c man .PP Normally, no program options are needed when @command{xorriso-tcltk} gets started. Some of the options are for demonstration of program development. This man page lists only those options which may be helpful for end users. @c man .PP @c man .TP @table @asis @item @minus{}@minus{}help @kindex @minus{}@minus{}help print help text to stderr @cindex Help text, print to stderr, @minus{}@minus{}help Print a help text with the complete list of start options and exit. @c man .TP @item @minus{}@minus{}no_extract @kindex @minus{}@minus{}no_extract ban extraction of files to disk @cindex Extraction of files, ban, @minus{}@minus{}no_extract Do not allow extraction of files from ISO filesystem to hard disk. This is not revokable during the program run. @c man .TP @item @minus{}@minus{}geometry @{+|-@}X@{+|-@}Y @kindex @minus{}@minus{}geometry set position of main window @cindex Main window, position, @minus{}@minus{}geometry Set the position of the main window on the screen. +0X is the left edge, -0X is the right edge, +0Y is the upper edge, -0Y is the lower edge. @c man .TP @item @minus{}@minus{}click_to_focus @kindex @minus{}@minus{}click_to_focus click needed before text input @cindex Keyboard focus, change by click, @minus{}@minus{}click_to_focus Chooses that input fields and list boxes get the keyboard focus only when being clicked by the mouse. This is the default. @c man .TP @item @minus{}@minus{}auto_focus @kindex @minus{}@minus{}auto_focus text input where mouse is @cindex Keyboard focus, where mouse is, @minus{}@minus{}auto_focus Chooses that the keyboard focus is where the mouse pointer is. @end table @node Examples, Files, Options, Top @chapter Examples @c man .SH EXAMPLES Just run @command{xorriso-tcltk} in a shell terminal without any further arguments @c man .SH FILES @node Files, Seealso, Examples, Top @chapter Files @c man .SS @c man .B Startup files: @section Startup Files @* When starting xorriso, its normal startup files get read and their text lines get executed as commands. See section FILES in the man page of xorriso or chapter Files in the info document of xorriso. @c man .SH SEE ALSO @c man .TP @c man .BR xorriso(1) @c man-ignore-lines begin @node Seealso, Bugreport, Files, Top @chapter See also info xorriso @c man-ignore-lines end @c man .SH BUGS @node Bugreport, Legal, Seealso, Top @chapter Reporting bugs @cindex Bugs, reporting @cindex Problems, reporting To report bugs, request help, or suggest enhancements for @command{xorriso} or @command{xorriso-tcltk}, please send electronic mail to the public list @email{bug-xorriso@@gnu.org}. If more privacy is desired, mail to @email{scdbackup@@gmx.net}. @* @sp 1 Please describe what you expect the program to do, the program arguments, GUI components, or dialog commands by which you tried to achieve it, the messages of @command{xorriso}, and the undesirable outcome of your program run. @* @sp 1 Expect to get asked more questions before solutions can be proposed. @c man .SH AUTHOR @node Legal, CommandIdx, Bugreport, Top @chapter Author, Copyright, Credits @section Author Thomas Schmitt <scdbackup@@gmx.net> @* for libburnia-project.org @c man .SH COPYRIGHT @section Copyright Copyright (c) 2011 - 2023 Thomas Schmitt @* Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of xorriso-tcltk. If you make use of the license to derive modified versions of xorriso-tcltk then you are entitled to modify this text under that same license. @c man .SH CREDITS @section Credits @command{xorriso} is in part based on work by Vreixo Formoso who provides libisofs together with Mario Danic who also leads the libburnia team. Thanks to Andy Polyakov who invented emulated growing, to Derek Foreman and Ben Jansens who once founded libburn. @* Compliments towards Joerg Schilling whose cdrtools served me for ten years. @c man-ignore-lines begin @node CommandIdx, ConceptIdx, Legal, Top @chapter Alphabetic Options List @printindex ky @node ConceptIdx,, CommandIdx, Top @chapter Alphabetic List of Concepts and Objects @printindex cp @c man-ignore-lines end @bye .\" Hey, EMACS: -*- nroff -*- .\" .\" IMPORTANT NOTE: .\" .\" The original of this file is kept in xorriso/xorriso.texi .\" This here was generated by program xorriso/make_xorriso_1 .\" .\" .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH XORRISO 1 "Version 1.5.7, Sep 07, 2024" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp <n> insert n+1 empty lines .\" for manpage-specific macros, see man(7) .nh .SH NAME xorriso \- creates, loads, manipulates and writes ISO 9660 filesystem images with Rock Ridge extensions. .SH SYNOPSIS .B xorriso .RI [ settings | actions ] .br .SH DESCRIPTION .PP \fBxorriso\fR is a program which copies file objects from POSIX compliant filesystems into Rock Ridge enhanced ISO 9660 filesystems and performs session\-wise manipulation of such filesystems. It can load the management information of existing ISO images and it writes the session results to optical media or to filesystem objects. .br Vice versa \fBxorriso\fR is able to copy file objects out of ISO 9660 filesystems. .PP A special property of \fBxorriso\fR is that it needs neither an external ISO 9660 formatter program nor an external burn program for CD, DVD or BD but rather incorporates the libraries of libburnia\-project.org . .SS .B Overview of features: .br Operates on an existing ISO image or creates a new one. .br Copies files from disk filesystem into the ISO image. .br Copies files from ISO image to disk filesystem (see osirrox). .br Renames or deletes file objects in the ISO image. .br Changes file properties in the ISO image. .br Updates ISO subtrees incrementally to match given disk subtrees. .br Writes result either as completely new image or as add\-on session to optical media or filesystem objects. .br Can activate ISOLINUX and GRUB boot images via El Torito and MBR. .br Can perform multi\-session tasks as emulation of mkisofs and cdrecord. .br Can record and restore hard links and ACL. .br Content may get zisofs compressed or filtered by external processes. .br Can issue commands to mount older sessions on GNU/Linux or FreeBSD. .br Can check media for damages and copy readable blocks to disk. .br Can attach MD5 checksums to each data file and the whole session. .br Scans for optical drives, blanks re\-usable optical media. .br Reads its instructions from command line arguments, dialog, and files. .br Provides navigation commands for interactive ISO image manipulation. .br Adjustable thresholds for abort, exit value, and problem reporting. .br .sp 1 Note that \fBxorriso\fR does not write audio CDs and that it does not produce UDF filesystems which are specified for official video DVD or BD. .SS .B General information paragraphs: .br Session model .br Media types and states .br Creating, Growing, Modifying, Blind Growing .br Libburn drives .br Rock Ridge, POSIX, X/Open, El Torito, ACL, xattr .br Command processing .br Dialog, Readline, Result pager .sp 1 Maybe you first want to have a look at section EXAMPLES near the end of this text before reading the next few hundred lines of background information. .SS \fBSession model:\fR .br Unlike other filesystems, \fBISO 9660\fR (aka \fBECMA\-119\fR) is not intended for read\-write operation but rather for being generated in a single sweep and being written to media as a \fBsession\fR. .br The data content of the session is called filesystem \fBimage\fR. .PP The written image in its session can then be mounted by the operating system for being used read\-only. GNU/Linux is able to mount ISO images from block devices, which may represent optical media, other media or via a loop device even from regular disk files. FreeBSD mounts ISO images from devices that represent arbitrary media or from regular disk files. .PP This session usage model has been extended on CD media by the concept of \fBmulti\-session\fR , which adds information to the CD and gives the mount programs of the operating systems the addresses of the entry points of each session. The mount programs recognize block devices which represent CD media and will by default mount the image in the last session. .br This session usually contains an updated directory tree for the whole medium which governs the data contents in all recorded sessions. So in the view of the mount program all sessions of a particular medium together form a single filesystem image. .br Adding a session to an existing ISO image is in this text referred as \fBgrowing\fR. .br The multi\-session model of the MMC standard does not apply to all media types. But program growisofs by Andy Polyakov showed how to extend this functionality to overwritable media or disk files which carry valid ISO 9660 filesystems. .PP \fBxorriso\fR provides growing as well as an own method named \fBmodifying\fR which produces a completely new ISO image from the old one and the modifications. See paragraph Creating, Growing, Modifying, Blind Growing below. .PP \fBxorriso\fR adopts the concept of multi\-session by loading an image directory tree if present, by offering to manipulate it by several actions, and by writing the new image to the target medium. .br The first session of a \fBxorriso\fR run begins by the definition of the input drive with the ISO image or by the definition of an output drive. The session ends by command \-commit which triggers writing. A \-commit is done automatically when the program ends regularly. .PP After \-commit a new session begins with the freshly written one as input. A new input drive can only be chosen as long as the loaded ISO image was not altered. Pending alteration can be revoked by command \-rollback. .PP Writing a session to the target is supposed to be very expensive in terms of time and of consumed space on appendable or write\-once media. Therefore all intended manipulations of a particular ISO image should be done in a single session. But in principle it is possible to store intermediate states and to continue with image manipulations. .SS .B Media types and states: There are two families of media in the MMC standard: .br \fBMulti\-session media\fR are CD\-R, CD\-RW, DVD\-R, DVD+R, DVD+R/DL, BD\-R, and unformatted DVD\-RW. These media provide a table of content which describes their existing sessions. See command \fB\-toc\fR. .br Similar to multi\-session media are DVD\-R DL and minimally blanked DVD\-RW. They record only a single session of which the size must be known in advance. \fBxorriso\fR will write onto them only if command \-close is set to "on". .br \fBOverwritable media\fR are DVD\-RAM, DVD+RW, BD\-RE, and formatted DVD\-RW. They offer random write access but do not provide information about their session history. If they contain one or more ISO 9660 sessions and if the first session was written by \fBxorriso\fR, then a table of content can be emulated. Else only a single overall session will be visible. .br DVD\-RW media can be formatted by \-format "full". They can be made unformatted by \-blank "deformat". .br Regular files and block devices are handled as overwritable media. Pipes and other writeable file types are handled as blank multi\-session media. .br The program growisofs formats by default BD\-R to be pseudo\-overwritable (POW). xorriso will classify them as .br Media current: is unsuitable , is POW formatted .br and will refuse to write to them or to obtain multi\-session information from them. .PP These media can assume several states in which they offer different capabilities. .br \fBBlank\fR media can be written from scratch. They contain no ISO image suitable for \fBxorriso\fR. .br Blank is the state of newly purchased optical media. With used CD\-RW and DVD\-RW it can be achieved by action \-blank "as_needed". Overwritable media are considered blank if they are new or if they have been marked as blank by \fBxorriso\fR. Action \-blank "as_needed" can be used to do this marking on overwritable media, or to apply mandatory formatting to new media if necessary. .br \fBAppendable\fR media accept further sessions. Either they are MMC multi\-session media in appendable state, or they are overwritable media which contain an ISO image suitable for \fBxorriso\fR. .br Appendable is the state after writing a session with command \-close off. .br \fBClosed\fR media cannot be written. They may contain an ISO image suitable for \fBxorriso\fR. .br Closed is the state of DVD\-ROM media and of multi\-session media which were written with command \-close on. If the drive is read\-only hardware then it will probably show any media as closed CD\-ROM or DVD\-ROM. .br Overwritable media assume this state in such read\-only drives or if they contain unrecognizable data in the first 32 data blocks. .br Read\-only drives may or may not show session histories of multi\-session media. Often only the first and the last session are visible. Sometimes not even that. Command \-rom_toc_scan might or might not help in such cases. .SS .B Creating, Growing, Modifying, Blind Growing: .br A new empty ISO image gets \fBcreated\fR if there is no input drive with a valid ISO 9660 image when the first time an output drive is defined. This is achieved by command \-dev on blank media or by command \-outdev on media in any state. .br The new empty image can be populated with directories and files. Before it can be written, the medium in the output drive must get into blank state if it was not blank already. .PP If there is a input drive with a valid ISO image, then this image gets loaded as foundation for manipulations and extension. The constellation of input and output drive determines which write method will be used. They have quite different capabilities and constraints. .PP The method of \fBgrowing\fR adds new data to the existing data on the medium. These data comprise of new file content and they override the existing ISO 9660 + Rock Ridge directory tree. It is possible to hide files from previous sessions but they still exist on the medium and with many types of optical media it is quite easy to recover them by mounting older sessions. .br Growing is achieved by command \-dev. .PP The write method of \fBmodifying\fR produces compact filesystem images with no outdated files or directory trees. Modifying can write its images to target media which are completely unsuitable for multi\-session operations. E.g. DVD\-RW which were treated with \-blank deformat_quickest, DVD\-R DL, named pipes, character devices, sockets. On the other hand modified sessions cannot be written to appendable media but to blank media only. .br So for this method one needs either two optical drives or has to work with filesystem objects as source and/or target medium. .br Modifying takes place if input drive and output drive are not the same and if command \-grow_blindly is set to its default "off". This is achieved by commands \-indev and \-outdev. .PP If command \-grow_blindly is set to a non\-negative number and if \-indev and \-outdev are both set to different drives, then \fBblind growing\fR is performed. It produces an add\-on session which is ready for being written to the given block address. This is the usage model of .br mkisofs \-M $indev \-C $msc1,$msc2 \-o $outdev .br which gives much room for wrong parameter combinations and should thus only be employed if a strict distinction between ISO formatter \fBxorriso\fR and the burn program is desired. \-C $msc1,$msc2 is equivalent to: .br \-load sbsector $msc1 \-grow_blindly $msc2 .SS .B Libburn drives: .br Input drive, i.e. source of an existing or empty ISO image, can be any random access readable libburn drive: optical media with readable data, blank optical media, regular files, block devices. .br Output drive, i.e. target for writing, can be any libburn drive. Some drive types do not support the method of growing but only the methods of modifying and blind growing. They all are suitable for newly created images. .PP All drive file objects have to offer rw\-permission to the user of \fBxorriso\fR. Even those which will not be usable for reading an ISO image. .br With any type of drive object, the data are considered to be organized in blocks of 2 KiB. Access happens in terms of Logical Block Address (\fBLBA\fR) which gives the number of a particular data block. .PP MMC compliant (i.e. optical) drives on GNU/Linux usually get addressed by the path of their block device or of their generic character device. E.g. .br \-dev /dev/sr0 .br \-dev /dev/hdc .br \-dev /dev/sg2 .br By default xorriso will try to map the given address to /dev/hd* and /dev/sr*. The command \-scsi_dev_family can redirect the mapping from sr to scd or sg. The latter does not suffer from the concurrency problems which plagued /dev/sr of Linux kernels since version 3 up to 5.5. But it does not yield the same addresses which are used by mount(8) or by open(2) for read(2). .br On FreeBSD the device files have names like .br \-dev /dev/cd0 .br On NetBSD: .br \-dev /dev/rcd0d .br On OpenSolaris: .br \-dev /dev/rdsk/c4t0d0s2 .br Get a list of accessible drives by command .br \-device_links .br It might be necessary to do this as \fBsuperuser\fR in order to see all drives and to then allow rw\-access for the intended users. Consider to bundle the authorized users in a group like old "floppy". .PP Filesystem objects of nearly any type can be addressed by prefix "stdio:" and their path in the filesystem. E.g.: .br \-dev stdio:/dev/sdc .br The default setting of \-drive_class allows the user to address files outside the /dev tree without that prefix. E.g.: .br \-dev /tmp/pseudo_drive .br If path leads to a regular file or to a block device then the emulated drive is random access readable and can be used for the method of growing if it already contains a valid ISO 9660 image. Any other file type is not readable via "stdio:" and can only be used as target for the method of modifying or blind growing. Non\-existing paths in existing directories are handled as empty regular files. .PP A very special kind of pseudo drive are open file descriptors. They are depicted by "stdio:/dev/fd/" and descriptor number (see man 2 open). .br Addresses "\-" or "stdio:/dev/fd/1" depict standard output, which normally is the output channel for result texts. To prevent a fatal intermingling of ISO image and text messages, all result texts get redirected to stderr if \-*dev "\-" or "stdio:/dev/fd/1" is among the start arguments of the program. .br Standard output is currently suitable for creating one session per program run without dialog. Use in other situations is discouraged and several restrictions apply: .br It is not allowed to use standard output as pseudo drive if it was not among the start arguments. Do not try to fool this ban via backdoor addresses to stdout. .br If stdout is used as drive, then \-use_readline is permanently disabled. Use of backdoors can cause severe memory and/or tty corruption. .PP Be aware that especially the superuser can write into any accessible file or device by using its path with the "stdio:" prefix. By default any address in the /dev tree without prefix "stdio:" will work only if it leads to a MMC drive. .br One may use command \fB\-ban_stdio_write\fR to surely prevent this risk and to restrict drive usage to MMC drives. .br One may prepend "mmc:" to a path to surely disallow any automatic "stdio:". .br By command \-drive_class one may ban certain paths or allow access without prefix "stdio:" to other paths. .SS .B Rock Ridge, POSIX, X/Open, El Torito, ACL, xattr: .br \fBRock Ridge\fR is the name of a set of additional information which enhance an ISO 9660 filesystem so that it can represent a POSIX compliant filesystem with ownership, access permissions, symbolic links, and other attributes. .br This is what \fBxorriso\fR uses for a decent representation of the disk files within the ISO image. \fBxorriso\fR produces Rock Ridge information by default. It is strongly discouraged to disable this feature. .PP \fBxorriso\fR is not named "porriso" because POSIX only guarantees 14 characters of filename length. It is the X/Open System Interface standard XSI which demands a file name length of up to 255 characters and paths of up to 1024 characters. Rock Ridge fulfills this demand. .PP An \fBEl Torito\fR boot record points the BIOS bootstrapping facility to one or more boot images, which are binary program files stored in the ISO image. The content of the boot image files is not in the scope of El Torito. .br Most bootable GNU/Linux CDs are equipped with ISOLINUX or GRUB boot images. \fBxorriso\fR is able to create or maintain an El Torito object which makes such an image bootable. For details see command \-boot_image. .br It is possible to make ISO images bootable from USB stick or other hard\-disk\-like media. Several options install a \fBMBR\fR (Master Boot Record), It may get adjusted according to the needs of the intended boot firmware and the involved boot loaders, e.g. GRUB2 or ISOLINUX. A MBR contains boot code and a partition table. The new MBR of a follow\-up session can get in effect only on overwritable media. .br MBR is read by PC\-BIOS when booting from USB stick or hard disk, and by PowerPC CHRP or PReP when booting. An MBR partition with type 0xee indicates the presence of GPT. .br Emulation \-as mkisofs supports the example options out of the ISOLINUX wiki, the options used in GRUB script grub\-mkrescue, and the example in the FreeBSD AvgLiveCD wiki. .br A \fBGPT\fR (GUID Partition Table) marks partitions in a more modern way. It is read by EFI when booting from USB stick or hard disk, and may be used for finding and mounting a HFS+ partition inside the ISO image. .br An \fBAPM\fR (Apple Partition Map) marks the HFS+ partition. It is read by Macs for booting and for mounting. .br MBR, GPT and APM are combinable. APM occupies the first 8 bytes of MBR boot code. All three do not hamper El Torito booting from CDROM. .br There is support for further facilities: MIPS Big Endian (SGI), MIPS Little Endian (DEC), SUN SPARC, HP\-PA. Those are mutually not combinable and also not combinable with MBR, GPT, or APM. .br .PP \fBACL\fR are an advanced way of controlling access permissions to file objects. Neither ISO 9660 nor Rock Ridge specify a way to record ACLs. So libisofs has introduced a standard conformant extension named AAIP for that purpose. It uses this extension if enabled by command \fB\-acl\fR. .br AAIP enhanced images are supposed to be mountable normally, but one cannot expect that the mounted filesystem will show and respect the ACLs. For now, only \fBxorriso\fR is able to retrieve those ACLs. It can bring them into effect when files get restored to an ACL enabled file system or it can print them in a format suitable for tool setfacl. .br Files with ACL show as group permissions the setting of entry "mask::" if that entry exists. Nevertheless the non\-listed group members get handled according to entry "group::". When removing ACL from a file, \fBxorriso\fR brings "group::" into effect. .br Recording and restoring of ACLs from and to local files works currently only on GNU/Linux and FreeBSD. .PP \fBxattr\fR (aka EA, or extattr) are pairs of name and value which can be attached to file objects. AAIP is able to represent them and \fBxorriso\fR can record and restore them. .br But be aware that pairs with names of non\-user namespaces are not necessarily portable between operating systems and not even between filesystems. Only those which begin with "user.", like "user.x" or "user.whatever", can unconditionally be expected to be appropriate on other machines and disks. Processing of other xattr may need administrator privileges. .br Name has to be a 0 terminated string. Value may be any array of bytes which does not exceed the size of 4095 bytes. xattr processing happens only if it is enabled by command \fB\-xattr\fR. .br As with ACL, currently only \fBxorriso\fR is able to retrieve xattr from AAIP enhanced images, to restore them to xattr capable file systems, or to print them. .br Recording and restoring of xattr from and to local files works currently only on GNU/Linux and FreeBSD, where they are known as extattr. .PP \fBLinux file attributes\fR are binary flags which can be set by program chattr(1) and listed by program lsattr(1). See their man pages and the definitions FS_*_FL in Linux header file <linux/fs.h>. Not all defined flags get reported by lsattr and accepted by chattr, but their number grew over the years. .br \fBxorriso\fR records the flags of disk files if enabled by command \fB\-lfa_flags\fR. Its command \-lsattr lists 22 flags the same way as the program lsattr does. They can be set by xorriso command \-chattr and can be enabled by \-lfa_flags for restoring when their files get restored to disk. .SS .B Command processing: .br Commands are either actions which happen immediately or settings which influence following actions. So their sequence does matter, unless they are given as program arguments and command \fB\-x\fR is among them. .br The list of all current settings can be inquired by command \-status "long". Command \-status "short" lists a handful of fundamental settings and all settings which are not default at program start. .PP Commands consist of a command word, followed by zero or more parameter words. If the list of parameter words is of variable length (indicated by "[...]" or "[***]") then it must be terminated by either the \fBlist delimiter\fR, occur at the end of the argument list, or occur at the end of an input line. .PP At program start the list delimiter is the string "\-\-". This may be changed with the \-list_delimiter command in order to allow "\-\-" as parameter in a variable length list. However, it is advised to reset the delimiter to "\-\-" immediately afterwards. .br For brevity the list delimiter is referred as "\-\-" throughout this text. .br The list delimiter is silently ignored if it appears after the parameters of a command with a fixed list length. It is handled as normal text if it appears among the parameters of such a command. .PP \fBPattern expansion\fR converts a list of pattern words into a list of existing file addresses. Unmatched pattern words will appear unaltered in that result list. .br Pattern matching supports the usual shell parser wildcards '*' '?' '[xyz]' and respects '/' as the path separator, which may only be matched literally. .br Pattern expansion is a property of some particular commands and not a general feature. It is controlled by commands \-iso_rr_pattern and \-disk_pattern. Commands which use pattern expansion all have variable parameter lists which are specified in this text by "[***]" rather than "[...]". .br Some other commands perform pattern matching unconditionally. .PP Command and parameter words are either read from the program arguments, where one argument is one word, or from quoted input lines where words are recognized similar to the quotation rules of a shell parser. .br \fBxorriso\fR is not a shell, although it might appear so at first glimpse. Be aware that the interaction of quotation marks and pattern symbols like "*" differs from the usual shell parsers. In \fBxorriso\fR, a quotation mark does not make a pattern symbol literal. .PP \fBQuoted input\fR converts whitespace\-separated text into words. The double quotation mark " and the single quotation mark ' can be used to enclose whitespace and make it part of words (e.g. of file names). Each mark type can enclose the marks of the other type. A trailing backslash \\ outside quotations or an open quotation cause the next input line to be appended. .br Quoted input accepts any 8\-bit character except NUL (0) as the content of the quotes. Nevertheless it can be cumbersome for the user to produce those characters directly. Therefore quoted input and program arguments offer optional \fBBackslash Interpretation\fR which can represent all 8\-bit characters except NUL (0) via backslash codes as in $'...' of bash. .br This is not enabled by default. See command \-backslash_codes. .PP When the program starts then it first looks for argument \-no_rc. If this is not present then it looks for its startup files and reads their content as command input lines. Then it interprets the program arguments as commands and parameters. Finally it enters dialog mode if command \-dialog "on" has been executed by this point. .PP The program ends either by command \-end, or by the end of program arguments if dialog mode has not been enabled at that point, or by a problem event which triggers the threshold of command \-abort_on. .SS .B Dialog, Readline, Result pager: .br Dialog mode prompts for a quoted input line, parses it into words, and performs them as commands with their parameters. It provides assisting services to make dialog more comfortable. .PP Readline is an enhancement for the input line. You may already know it from the bash shell. Whether it is available in \fBxorriso\fR depends on the availability of package readline\-dev at the time when \fBxorriso\fR was built from its sourcecode. .br Readline lets the user move the cursor over the text in the line by help of the Left and the Right arrow keys. Text may be inserted at the cursor position. The Delete key removes the character under the cursor. Up and Down arrow keys navigate through the history of previous input lines. .br See man readline for more info about libreadline. .PP Command \-page activates a built\-in result text pager which may be convenient in dialog mode. After an action has output the given number of terminal lines, the pager prompts the user for a line of input. .br An empty line lets \fBxorriso\fR resume work until the next page is output. .br The single character "@" disables paging for the current action. .br "@@@", "x", "q", "X", or "Q" request that the current action aborts and suppress further result output. .br Any other line input will be interpreted as new dialog line. The current action is requested to abort. Afterwards, the input line is executed. .PP Some actions apply paging to their info output, too. .br The request to abort may or may not be obeyed by the current action. All actions try to abort as soon as possible. .br .SH OPTIONS .br All command words are shown with a leading dash although this dash is not mandatory for the command to be recognized. Nevertheless within command \-as the dashes of the emulated commands are mandatory. .br Normally any number of leading dashes is ignored with command words and inner dashes are interpreted as underscores. .TP .B Execution order of program arguments: .PP By default the program arguments of a xorriso run are interpreted as a sequence of commands which get performed exactly in the given order. This requires the user to write commands for desired settings before the commands which shall be influenced by those settings. .br Many other programs support program arguments in an arbitrary ordering and perform settings and actions in a sequence at their own discretion. xorriso provides an option to enable such a behavior at the cost of loss of expressivity. .TP \fB\-x\fR Enable automatic sorting of program arguments into a sequence that (most likely) is sensible. This command may be given at any position among the commands which are handed over as program arguments. .br Note: It works only if it is given as program argument and with a single dash (i.e. "\-x"). It will not work in startup files, nor with \-options_from_file, nor in dialog mode, nor as "x" and finally not as "\-\-x". It affects only the commands given as program arguments. .TP \fB\-list_arg_sorting\fR List all xorriso commands in the order which applies if command \-x is in effect. .br This list may also be helpful without \-x for a user who ponders over the sequence in which to put commands. Deviations from the listed sorting order may well make sense, though. .PP .TP .B Acquiring source and target drive: .PP The effect of acquiring a drive may depend on several commands in the next paragraph "Influencing the behavior of image loading". If desired, their enabling commands have to be performed before the commands which acquire the drive. .TP \fB\-dev\fR address Set input and output drive to the same address and load an ISO image if it is present. If there is no ISO image then create a blank one. Set the image expansion method to growing. .br This is only allowed as long as no changes are pending in the currently loaded ISO image. If changes are pending, then one has to perform \-commit or \-rollback first. .br Special address string "\-" means standard output, to which several restrictions apply. See above paragraph "Libburn drives". .br An empty address string "" gives up the current device without acquiring a new one. .TP \fB\-indev\fR address Set input drive and load an ISO image if present. If the new input drive differs from \-outdev then switch from growing to modifying or to blind growing. It depends on the setting of \-grow_blindly which of both gets activated. The same rules and restrictions apply as with \-dev. .TP \fB\-outdev\fR address Set output drive and if it differs from the input drive then switch from growing to modifying or to blind growing. Unlike \-dev and \-indev this action does not load a new ISO image. So it can be performed even if there are pending changes. .br \-outdev can be performed without previous \-dev or \-indev. In that case an empty ISO image with no changes pending is created. It can either be populated by help of \-map, \-add et.al. or it can be discarded silently if \-dev or \-indev are performed afterwards. .br Special address string "\-" means standard output, to which several restrictions apply. See above paragraph "Libburn drives". .br An empty address string "" gives up the current output drive without acquiring a new one. No writing is possible without an output drive. .TP \fB\-drive_class\fR "harmless"|"banned"|"caution"|"clear_list" disk_pattern Add a drive path pattern to one of the safety lists or make those lists empty. There are three lists defined which get tested in the following sequence: .br If a drive address path matches the "harmless" list then the drive will be accepted. If it is not a MMC device then the prefix "stdio:" will be prepended automatically. This list is empty by default. .br Else if the path matches the "banned" list then the drive will not be accepted by \fBxorriso\fR but rather lead to a FAILURE event. This list is empty by default. .br Else if the path matches the "caution" list and if it is not a MMC device, then its address must have the prefix "stdio:" or it will be rejected. This list has by default one entry: "/dev". .br If a drive path matches no list then it is considered "harmless". By default these are all paths which do not begin with directory "/dev". .br A path matches a list if one of its parent paths or itself matches a list entry. Address prefix "stdio:" or "mmc:" will be ignored when testing for matches. .br By pseudo\-class "clear_list" and pseudo\-patterns "banned", "caution", "harmless", or "all", the lists may be made empty. .br E.g.: \-drive_class clear_list banned .br One will normally define the \-drive_class lists in one of the \fBxorriso\fR Startup Files. .br Note: This is not a security feature but rather a bumper for the superuser to prevent inadverted mishaps. For reliably blocking access to a device file you have to deny its rw\-permissions in the filesystem. .TP \fB\-drive_access\fR "exclusive"|"shared":"unrestricted"|"readonly" Control whether device file locking mechanisms shall be used when acquiring a drive, and whether status or content of the medium in the drive may be altered. Useful and most harmless are the setting "shared:readonly" and the default setting "exclusive:unrestricted". .br "exclusive" enables tests and locks when acquiring the drive. It depends on the operating system which locking mechanisms get applied, if any. On GNU/Linux it is open(O_EXCL). On FreeBSD it is flock(LOCK_EX). .br "shared" disables the use of these mechanisms to become able to acquire drives which are mounted, or opened by some process, or guarded by /dev/pktcdvd*. .br "unrestricted" enables all technically appropriate operations on an acquired drive. "shared:unrestricted" risks to get own burn runs spoiled by other processes or to vice versa spoil activities of such processes. So use "exclusive:unrestricted" unless you know for sure that "shared" is safe. .br "readonly" disables operations which might surprise a co\-user of the drive. For \-outdev these are formatting, blanking, writing, ejecting. For \-indev this is ejecting. Be aware that even reading and drive status inquiries can disturb an ongoing burn run on CD\-R[W] and DVD\-R[W]. .TP \fB\-scsi_dev_family\fR "default"|"sr"|"scd"|"sg" GNU/Linux specific: .br By default, xorriso tries to map Linux drive addresses to /dev/sr* before they get opened for operating the drive. This coordinates well with other use cases of optical drives, like mount(8). But since year 2010 all /dev/sr* share a global lock which allows only one drive to process an SCSI command while all others have to wait for its completion. This yields awful throughput if more than one drive is writing or reading simultaneously. The global lock is not applied to device files /dev/sg* and also not if the xorriso drive address is prepended by "stdio:". .br So for simultaneous burn runs on modern GNU/Linux it is advisable to perform \-scsi_dev_family "sg" before any \-dev, \-indev, or \-outdev. The drive addresses may then well be given as /dev/sr* but will nevertheless get used as the matching /dev/sg*. .br If you decide so, consider to put the command into a global startup file like /etc/opt/xorriso/rc. .TP \fB\-grow_blindly\fR "off"|predicted_nwa If predicted_nwa is a non\-negative number then perform blind growing rather than modifying if \-indev and \-outdev are set to different drives. "off" or "\-1" switch to modifying, which is the default. .br predicted_nwa is the block address where the add\-on session of blind growing will finally end up. It is the responsibility of the user to ensure this final position and the presence of the older sessions. Else the overall ISO image will not be mountable or will produce read errors when accessing file content. \fBxorriso\fR will write the session to the address as obtained from examining \-outdev and not necessarily to predicted_nwa. .br During a run of blind growing, the input drive is given up before output begins. The output drive is given up when writing is done. .TP .B Influencing the behavior of image loading: .PP The following commands should normally be performed before loading an image by acquiring an input drive. In rare cases it is desirable to activate them only after image loading. .TP \fB\-read_speed\fR code|number[k|m|c|d|b] Set the speed for reading. Default is "none", which avoids to send a speed setting command to the drive before reading begins. .br Further special speed codes are: .br "max" (or "0") selects maximum speed as announced by the drive. .br "min" (or "\-1") selects minimum speed as announced by the drive. .br Speed can be given in media dependent numbers or as a desired throughput per second in MMC compliant kB (= 1000) or MB (= 1000 kB). Media x\-speed factor can be set explicitly by "c" for CD, "d" for DVD, "b" for BD, "x" is optional. .br Example speeds: .br 706k = 706kB/s = 4c = 4xCD .br 5540k = 5540kB/s = 4d = 4xDVD .br If there is no hint about the speed unit attached, then the medium in the \-indev will decide. Default unit is CD = 176.4k. .br Depending on the drive, the reported read speeds can be deceivingly low or high. Therefore "min" cannot become higher than 1x speed of the involved medium type. Read speed "max" cannot become lower than 52xCD, 24xDVD, or 20xBD, depending on the medium type. .br MMC drives usually activate their own idea of speed and take the speed value given by the burn program only as hint for their own decision. Friendly drives adjust their constant angular velocity so that the desired speed is reached at the outer rim of the medium. But often there is only the choice between very slow and very loud. .br Sometimes no speed setting is obeyed at all, but speed is adjusted to the demand frequency of the reading program. So xorriso offers to set an additional software enforced limit by prefix "soft_force:". The program will take care not to read faster than the soft_force speed. This may be combined with setting the drive speed to a higher value. Setting "soft_force:0" disables this feature. .br "soft_force:" tries to correct in subsequent waiting periods lost or surplus time of up to 0.25 seconds. This smoothens the overall data stream but also enables short times of higher speed to compensate short times of low speed. Prefix "soft_corr:" sets this hindsight span by giving a number of microseconds. Not more than 1 billion = 1000 seconds. Very short times can cause speed deviations, because systematic inaccuracies of the waiting function cannot be compensated. .br Examples (combinable): .br \-read_speed 6xBD .br \-read_speed soft_force:4xBD \-read_speed soft_corr:100000 .TP \fB\-load\fR entity id Load a particular (possibly outdated) ISO session from \-dev or \-indev. Usually all available sessions are shown with command \-toc. .br entity depicts the kind of addressing. id depicts the particular address. The following entities are defined: .br "auto" with any id addresses the last session in \-toc. This is the default. .br "session" with id being a number as of a line "ISO session", column "Idx". .br "track" with id being a number as of a line "ISO track", column "Idx". .br "lba" or "sbsector" with a number as of a line "ISO ...", column "sbsector". .br "volid" with a search pattern for a text as in the column "Volume Id" of a \-toc line "ISO ...". .br "at_time" with a time string as described with command \-alter_date chooses the last session or track where the modification timestamp matches the given time within the same second. .br "before" with a time string chooses the last session or track of which the timestamp is older than the given time. But it does not match an entity with exactly the given time. .br "not_after" is like "before" but also matches an entity with exactly the given time. .br "after" with a time string chooses the first session or track of which the timestamp is younger than the given time. But it does not match an entity with exactly the given time. .br "not_before" is like "after" but also matches an entity with exactly the given time. .br Comparison of time entities is done with an accuracy of one second. I.e. the centiseconds of ISO 9660 timestamps are ignored. If \-toc_info_type is set to "creation_time", then the comparison is done against the creation timestamp of track or session rather than the modification timestamp. The output of \-pvd_info shows both timestamps as "Creation Time:" and "Modif. Time :". .br The time comparisons pick first and last matching sessions. If the sequence of timestamps on a drive is not chronologically ascending, the picks might not be the best choice. In this case look at the output of \-toc_info_type "mtime" \-toc and choose the desired entity by "session", "track", or "sbsector". .br Addressing a non\-existing entity or one which does not represent an ISO image will either abandon \-indev or at least lead to a blank image. .br If an input drive is set at the moment when \-load is executed, then the addressed ISO image is loaded immediately. Else, the setting will be pending until the next \-dev or \-indev. After the image has been loaded once, the setting is valid for \-rollback until next \-dev or \-indev, where it will be reset to "auto". .TP \fB\-displacement\fR [-]lba Compensate a displacement of the image versus the start address for which the image was prepared. This affects only loading of ISO images and reading of their files. The multi\-session method of growing is not allowed as long as \-displacement is non\-zero. I.e. \-indev and \-outdev must be different. The displacement gets reset to 0 before the drive gets re\-acquired after writing. .br Examples: .br If a track of a CD starts at block 123456 and gets copied to a disk file where it begins at block 0, then this copy can be loaded with \-displacement \-123456 .br If an ISO image was written onto a partition with offset of 640000 blocks of 512 bytes, then it can be loaded from the base device by \-load sbsector 160000 \-displacement 160000 .br (If the partition start address is not divisible by 4, then you will have to employ a loop device instead.) .br In both cases, the ISO sessions should be self contained, i.e. not add\-on sessions to an ISO image outside their track or partition. .TP \fB\-read_fs\fR "any"|"norock"|"nojoliet"|"ecma119" Specify which kind of filesystem tree to load if present. If the wish cannot be fulfilled, then ECMA\-119 names are loaded and converted according to \-ecma119_map. .br "any" first tries to read Rock Ridge. If not present, Joliet is tried. .br "norock" does not try Rock Ridge. .br "nojoliet" does not try Joliet. .br "ecma119" tries neither Rock Ridge nor Joliet. .TP \fB\-assert_volid\fR pattern severity Refuse to load ISO images with volume IDs which do not match the given search pattern. When refusing an image, give up the input drive and issue an event of the given severity (like FAILURE, see \-abort_on). An empty search pattern accepts any image. .br This command does not hamper the creation of an empty image from blank input media and does not discard an already loaded image. .TP \fB\-in_charset\fR character_set_name Set the character set from which to convert file names when loading an image. See paragraph "Character sets" for more explanations. When loading the written image after \-commit the setting of \-out_charset will be copied to \-in_charset. .TP \fB\-auto_charset\fR "on"|"off" Enable or disable recording and interpretation of the output character set name in an xattr attribute of the image root directory. If enabled and if a recorded character set name is found, then this name will be used as name of the input character set when reading an image. .br Note that the default output charset is the local character set of the terminal where \fBxorriso\fR runs. Before attributing this local character set to the produced ISO image, check whether the terminal properly displays all intended filenames, especially exotic national characters. .TP \fB\-hardlinks\fR mode[:mode...] Enable or disable loading and recording of hardlink relations. .br In default mode "off", iso_rr files lose their inode numbers at image load time. Each iso_rr file object which has no inode number at image generation time will get a new unique inode number if \-compliance is set to new_rr. .br Mode "on" preserves inode numbers from the loaded image if such numbers were recorded. When committing a session it searches for families of iso_rr files which stem from the same disk file, have identical content filtering and have identical properties. The family members all get the same inode number. Whether these numbers are respected at mount time depends on the operating system. .br Command \-lsl displays hardlink counts if "lsl_count" is enabled. This can slow down the command substantially after changes to the ISO image have been made. Therefore the default is "no_lsl_count". .br Commands \-update and \-update_r track splits and fusions of hard links in filesystems which have stable device and inode numbers. This can cause automatic last minute changes before the session gets written. Command \-hardlinks "perform_update" may be used to do these changes earlier, e.g. if you need to apply filters to all updated files. .br Mode "without_update" avoids hardlink processing during update commands. Use this if your filesystem situation does not allow \-disk_dev_ino "on". .br \fBxorriso\fR commands which extract files from an ISO image try to hardlink files with identical inode number. The normal scope of this operation is from image load to image load. One may give up the accumulated hard link addresses by \-hardlinks "discard_extract". .br A large number of hardlink families may exhaust \-temp_mem_limit if not \-osirrox "sort_lba_on" and \-hardlinks "cheap_sorted_extract" are both in effect. This restricts hard linking to other files restored by the same single extract command. \-hardlinks "normal_extract" re\-enables wide and expensive hardlink accumulation. .br .TP \fB\-acl\fR "on"|"off" Enable or disable processing of ACLs. If enabled, then \fBxorriso\fR will obtain ACLs from disk file objects, store ACLs in the ISO image using the libisofs specific AAIP format, load AAIP data from ISO images, test ACL during file comparison, and restore ACLs to disk files when extracting them from ISO images. See also commands \-getfacl, \-setfacl. .TP \fB\-xattr\fR "on"|"user"|"any"|"off" Enable or disable processing of xattr attributes. If enabled, then \fBxorriso\fR will handle xattr similar to ACL. See also commands \-getfattr, \-setfattr and above paragraph about xattr. .br Modes "on" and "user" read and write only attributes from namespace "user". .br Mode "any" processes attributes of all namespaces. This might need administrator privileges, even if the owner of the disk file tries to read or write the attributes. .br Note that it is not possible to set xattr of namespace "isofs." by xorriso xattr manipulation commands. .TP \fB\-lfa_flags\fR mode[:mode...] Enable, disable, or influence processing of Linux file attributes as described in man 1 chattr. .br Mode "on" enables actual processing of the attributes. .br Mode "off" disables it. .br Mode "auto_on" enables it only if the underlying libisofs was compiled with support for Linux file attributes, which is typically not the case on non\-Linux systems. Enabled processing without lisofs support would cause failure events when reading disk files. .br The other modes define the behavior in case of "on": .br Mode "read" enables obtaining of these attributes from disk files, storing them in the emerging ISO 9660 filesystem as AAIP data, importing them when an ISO filesystem gets loaded and bears such stored attributes, and comparing them during comparisons between files on disk and in ISO. .br Mode "no_read" disables reading of the attributes from disk files and importing them when an ISO filesystem gets loaded. If other settings like \-acl "on" or \-xattr "on" require storing of AAIP data, then file attributes might still get stored in the emerging ISO. To surely exclude any file attributes, run before \-commit : .br \-chattr_r \-\-remove\-lfa\-flags / \-\- .br Mode "import_non_settable" enables obtaining of non\-settable attributes from disk and attaching of attributes to files in the emerging ISO image even if all attribute flags are zero. .br Mode "import_only_settable" causes non\-settable attributes ("eEhINVZ") to be ignored when file objects get read from disk. If the remaining attribute flags are all zero, then no attribute information gets attached to the file in the ISO. This saves storage and eases finding of non\-trivial attributes in the ISO image. .br Mode "restore" enables restoring of attributes when their file gets restored and comparing them during comparisons between files on disk and in ISO. Several modes below modify the behavior during restoring of attributes. .br Mode "no_restore" disables restoring of attributes. .br Mode "restore_su" enables restoring of the attributes "aij" which are only changeable by the bearer of superuser capabilities. "no_restore_su" disables restoring of these attributes. "restore_su_auto" enables it only if the effective user id is 0. .br The attribute "i" (for "immutable") gets restored to directories only if they have been created freshly by the same file restoring xorriso command. A directory which already existed on disk will not be made immutable. .br Mode "restore_only_known" restricts restoring to the known settable attribute flags "aAcCdDFijmPsStTux". "restore_unknown" enables the attempt to restore unknown flags or even those which are known to be unchangeable, if they are not disabled by other modes. .br Mode "restore_mask=..." enables particular attributes for restoring. All others will not be restored. The list of desired attribute letters follows the '=' character. An empty list enables all attributes, if they are not disabled by other modes. The single character "\-" bans all attributes from restoring, like "off" does. Example: .br \-lfa_flags restore_mask=SdCiaj .br Mode "restore_error=" sets the behavior for the event that restoring of attribute flags to the local filesystem fails. Available are the keyword "silent" and the severities from "all" to "fatal". "silent" and severity "all" suppress any error messages and abort considerations caused by restore attemps of attribute flags. Else the error message is issued with the given severity. Then it depends on the setting of command \-abort_on whether restoring goes on or gets aborted. .br Mode "restore_single" tries to set the attribute flags of a file one\-by\-one if the attempt fails to set them all at once. This happens only if the "restore_error=" severity and the setting of command \-abort_on do not demand to abort the program run. Note that some flags in some situations get ignored silently by some kernels. .br Mode "no_restore_single" disables this attempt to get at least some of the attribute flags into effect. .br Mode "default" reinstates the default settings: .br \-lfa_flags off:read:restore:restore_su_auto:restore_only_known .br \-lfa_flags restore_mask=:restore_error=sorry:restore_single .br Use "default:on" to get default settings with enabled processing. .TP \fB\-md5\fR "on"|"all"|"off"|"load_check_off" Enable or disable processing of MD5 checksums for the overall session and for each single data file. If enabled then images with checksum tags get loaded only if the tags of superblock and directory tree match properly. The MD5 checksums of data files and whole session get loaded from the image if there are any. .br With commands \-compare and \-update the recorded MD5 of a file will be used to avoid content reading from the image. Only the disk file content will be read and compared with that MD5. This can save much time if \-disk_dev_ino "on" is not suitable. .br Commands which copy whole data files from ISO to hard disk will verify the copied data stream by the recorded MD5, if \-osirrox "check_md5_on" is set. .br At image generation time they are computed for each file which gets its data written into the new session. The checksums of files which have their data in older sessions get copied into the new session. Superblock, tree and whole session get a checksum tag each. .br Mode "all" will additionally check during image generation whether the checksum of a data file changed between the time when its reading began and the time when it ended. This implies reading every file twice. .br Mode "load_check_off" together with "on" or "all" will load recorded MD5 sums but not test the recorded checksum tags of superblock and directory tree. This is necessary if growisofs was used as burn program, because it does not overwrite the superblock checksum tag of the first session. Therefore load_check_off is in effect when \fBxorriso\fR \-as mkisofs option \-M is performed. .br The test can be re\-enabled by mode "load_check_on". .br Checksums can be exploited via commands \-check_md5, \-check_md5_r, via find actions get_md5, check_md5, and via \-check_media. .TP \fB\-for_backup\fR Enable all extra features which help to produce or to restore backups with highest fidelity of file properties. Currently this is a shortcut for: .br \-hardlinks on \-acl on \-xattr any \-md5 on .br and possibly: .br \-lfa_flags default:on:import_only_settable .br \-lfa_flags restore_mask=aAcdDijmPsStTux .br If you restore a backup with xattr from non\-user namespaces, then make sure that the target operating system and filesystem know what these attributes mean. Possibly you will need administrator privileges to record or restore such attributes. At recording time, xorriso will try to tolerate missing privileges and just record what is readable. But at restore time, missing privileges or preconditions will cause failure events. .br Command \-xattr "user" after command \-for_backup will exclude non\-user attributes from being recorded or restored. .br The command \-lfa_flags is executed by \-for_backup only if the underlying libisofs was compiled with support for Linux file attributes, which is typically not the case on non\-Linux systems. .br If \-lfa_flags is executed by \-for_backup then the restore mask enables all known settable attributes, except "C" and "F" which have special constraints which xorriso cannot yet detect at restore time. Command \-lfa_flags "restore_mask=" after \-for_backup will enable all known settable attributes. .TP \fB\-ecma119_map\fR "stripped"|"unmapped"|"lowercase"|"uppercase" Choose the conversion of file names when a session gets loaded, if they stem neither from a Rock Ridge name nor from a Joliet name. .br Mode "stripped" is the default. It shows the names as found in the ISO but removes trailing ";1" or ".;1" if present. .br Mode "unmapped" shows names as found without removing characters. Warning: Multi\-session converts "xyz;1" to "xyz_1" and maybe adds new ";1". .br Mode "lowercase" is like "stripped" but also maps uppercase letters to lowercase letters. This is compatible to default GNU/Linux mount behavior. .br Mode "uppercase" is like "stripped" but maps lowercase letters to uppercase, if any occur despite the prescriptions of ECMA\-119. .TP \fB\-joliet_map\fR "stripped"|"unmapped" Choose the conversion of file names when a session gets loaded from a Joliet tree. .br Mode "stripped" is the default. It removes trailing ";1" or ".;1" if present. .br Mode "unmapped" shows names as found without removing characters. Warning: Multi\-session converts "xyz;1" to "xyz_1" and maybe adds new ";1". .TP \fB\-iso_nowtime\fR "dynamic"|timestring Choose whether to use the current time ("dynamic") or a fixed time point for timestamps of ISO 9660 nodes without a disk source file and as default for superblock timestamps. .br If a timestring is given, then it is used for such timestamps. For the formats of timestrings see command \fB\-alter_date\fR. .TP \fB\-disk_dev_ino\fR "on"|"ino_only"|"off" Enable or disable processing of recorded file identification numbers (dev_t and ino_t). If enabled they are stored as xattr and can substantially accelerate file comparison. The root node gets a global start timestamp. If during comparison a file with younger timestamps is found in the ISO image, then it is suspected to have inconsistent content. .br If device numbers and inode numbers of the disk filesystems are persistent and if no irregular alterations of timestamps or system clock happen, then potential content changes can be detected without reading that content. File content change is assumed if any of mtime, ctime, device number or inode number have changed. .br Mode "ino_only" replaces the precondition that device numbers are stable by the precondition that mount points in the compared tree always lead to the same filesystems. Use this if mode "on" always sees all files changed. .br The speed advantage appears only if the loaded session was produced with \-disk_dev_ino "on" too. .br Note that \-disk_dev_ino "off" is totally in effect only if \-hardlinks is "off", too. .TP \fB\-file_name_limit\fR [+]number Set the maximum permissible length for file names in the range of 64 to 255. Path components which are longer than the given number will get truncated and have their last 33 bytes overwritten by a colon ':' and the hex representation of the MD5 of the first 4095 bytes of the whole oversized name. Potential incomplete UTF\-8 characters will get their leading bytes replaced by '_'. .br iso_rr_paths with the long components will still be able to access the file paths with truncated components. .br If \-file_name_limit is executed while an ISO tree is present, the file names in the ISO tree get checked for existing truncated file names of the current limit and for name collisions between newly truncated files and existing files. In both cases, the setting will be refused with a SORRY event. .br One may lift this ban by prepending the character "+" to the argument of \-file_name_limit. Truncated filenames may then get truncated again, invalidating their MD5 part. Colliding truncated names are made unique, consuming at least 9 more bytes of the remaining name part. .br If writing of xattr is enabled, then the length will be stored in "isofs.nt" of the root directory. If reading of xattr is enabled and "isofs.nt" is found, then the found length will get into effect if it is smaller than the current setting of \-file_name_limit. .br File name patterns will only work if they match the truncated name. This might change in future. .br Files with truncated names get deleted and re\-added unconditionally during \-update and \-update_r. This might change in future. .br Linux kernels up to at least 4.1 misrepresent names of length 254 and 255. If you expect such names in or under disk_paths and plan to mount the ISO by such Linux kernels, consider to set \-file_name_limit 253. Else just avoid names longer than 253 characters. .TP \fB\-rom_toc_scan\fR "on"|"force"|"off"[:"emul_off"][:"emul_wide"] Read\-only drives do not tell the actual media type but show any media as ROM (e.g. as DVD\-ROM). The session history of MMC multi\-session media might be truncated to first and last session or even be completely false. (The emulated history of overwritable media is not affected by this.) .br To have in case of failure a chance of getting the session history and especially the address of the last session, there is a scan for ISO 9660 filesystem headers which might help but also might yield worse results than the drive's table of content. At its end it can cause read attempts to invalid addresses and thus ugly drive behavior. Setting "on" enables that scan for alleged read\-only media. .br Some operating systems are not able to mount the most recent session of multi\-session DVD or BD. If on such a system \fBxorriso\fR has no own MMC capabilities then it may still find that session from a scanned table of content. Setting "force" handles any media like a ROM medium with setting "on". .br On the other hand the emulation of session history on overwritable media can hamper reading of partly damaged media. Setting "off:emul_off" disables the elsewise trustworthy table\-of\-content scan for those media. .br The table\-of\-content scan on overwritable media normally searches only up to the end of the session that is pointed to by the superblock at block 0. Setting "on:emul_wide" lets the scan continue up to the end of the medium. This may be useful after copying a medium with \-check_media patch_lba0=on when not the last session was loaded. .TP \fB\-calm_drive\fR "in"|"out"|"all"|"revoke"|"on"|"off" Reduce drive noise until it is actually used again. Some drives stay alert for substantial time after they have been used for reading. This reduces the startup time for the next drive operation but can be loud and waste energy if no i/o with the drive is expected to happen soon. .br Modes "in", "out", "all" immediately calm down \-indev, \-outdev, or both, respectively. Mode "revoke" immediately alerts both. Mode "on" causes \-calm_drive to be performed automatically after each \-dev, \-indev, and \-outdev. Mode "off" disables this. .TP \fB\-ban_stdio_write\fR Allow for writing only the usage of MMC optical drives. Disallow to write the result into files of nearly arbitrary type. Once set, this command cannot be revoked. .TP \fB\-early_stdio_test\fR "on"|"appendable_wo"|"off" If enabled by "on" then regular files and block devices get tested for effective access permissions. This implies to try opening those files for writing, which otherwise will happen only later and only if actual writing is desired. .br The test result is used for classifying the pseudo drives as overwritable, read\-only, write\-only, or uselessly empty. This may lead to earlier detection of severe problems, and may avoid some less severe error events. .br Mode "appendable_wo" is like "on" with the additional property that non\-empty write\-only files are regarded as appendable rather than blank. .TP \fB\-data_cache_size\fR number_of_tiles blocks_per_tile Set the size and granularity of the data cache which is used when ISO images are loaded and when file content is read from ISO images. The cache consists of several tiles, which each consists of several blocks. A larger cache reduces the need for tiles being read multiple times. Larger tiles might additionally improve the data throughput from the drive, but can be wasteful if the data are scattered over the medium. .br Larger cache sizes help best with image loading from MMC drives. They are an inferior alternative to \-osirrox option "sort_lba_on". .br blocks_per_tile must be a power of 2. E.g. 16, 32, or 64. The overall cache size must not exceed 1 GiB. The default values can be restored by parameter "default" instead of one or both of the numbers. Currently the default is 32 tiles of 32 blocks = 2 MiB. .TP .B Inserting files into ISO image: .PP The following commands expect file addresses of two kinds: .br \fBdisk_path\fR is a path to an object in the local filesystem tree. .br \fBiso_rr_path\fR is the Rock Ridge name of a file object in the ISO image. If no Rock Ridge information is recorded in the loaded ISO image, then you will see ISO 9660 names which are of limited length and character set. If no Rock Ridge information shall be stored in an emerging ISO image, then their names will get mapped to such restricted ISO 9660 (aka ECMA\-119) names. .PP Note that in the ISO image you are as powerful as the superuser. Access permissions of the existing files in the image do not apply to your write operations. They are intended to be in effect with the read\-only mounted image. .PP If the iso_rr_path of a newly inserted file leads to an existing file object in the ISO image, then the following collision handling happens: .br If both objects are directories then they get merged by recursively inserting the subobjects from filesystem into ISO image. If other file types collide then the setting of command \fB\-overwrite\fR decides. .br Renaming of files has similar collision handling, but directories can only be replaced, not merged. Note that if the target directory exists, then \-mv inserts the source objects into this directory rather than attempting to replace it. Command \-move, on the other hand, would attempt to replace it. .PP The commands in this section alter the ISO image and not the local filesystem. .TP \fB\-disk_pattern\fR "on"|"ls"|"off" Set the pattern expansion mode for the disk_path parameters of several commands which support this feature. .br Setting "off" disables this feature for all commands which are marked in this man page by "disk_path [***]" or "disk_pattern [***]". .br Setting "on" enables it for all those commands. .br Setting "ls" enables it only for those which are marked by "disk_pattern [***]". .br Default is "ls". .TP \fB\-add\fR pathspec [...] | disk_path [***] Insert the given files or directory trees from filesystem into the ISO image. .br If \-pathspecs is set to "on" or "as_mkisofs" then pattern expansion is always disabled and character '=' has a special meaning. It separates the ISO image path from the disk path: .br iso_rr_path=disk_path .br Character '=' in the iso_rr_path must be escaped by '\\' (i.e. as "\\="). .br With \-pathspecs "on", the character '\\' must not be escaped. The character '=' in the disk_path must not be escaped. .br With \-pathspecs "as_mkisofs", all characters '\\' must be escaped in both, iso_rr_path and disk_path. The character '=' may or may not be escaped in the disk_path. .br If iso_rr_path does not begin with '/' then \-cd is prepended. If disk_path does not begin with '/' then \-cdx is prepended. .br If no '=' is given then the word is used as both, iso_rr_path and disk path. If in this case the word does not begin with '/' then \-cdx is prepended to the disk_path and \-cd is prepended to the iso_rr_path. .br If \-pathspecs is set to "off" then \-disk_pattern expansion applies, if enabled. The resulting words are used as both, iso_rr_path and disk path. Relative path words get prepended the setting of \-cdx to disk_path and the setting of \-cd to iso_rr_path. .TP \fB\-add_plainly\fR mode If set to mode "unknown" then any command word that does not begin with "\-" and is not recognized as known command will be subject to a virtual \-add command. I.e. it will be used as pathspec or as disk_path and added to the image. If enabled, \-disk_pattern expansion applies to disk_paths. .br Mode "dashed" is similar to "unknown" but also adds unrecognized command words even if they begin with "\-". .br Mode "any" announces that all further words are to be added as pathspecs or disk_paths. This does not work in dialog mode. .br Mode "none" is the default. It prevents any words from being understood as files to add, if they are not parameters to appropriate commands. .TP \fB\-path_list\fR disk_path Like \-add but read the parameter words from file disk_path or standard input if disk_path is "\-". The list must contain exactly one pathspec or disk_path pattern per line. .TP \fB\-quoted_path_list\fR disk_path Like \-path_list but with quoted input reading rules. Lines get split into parameter words for \-add. Whitespace outside quotes is discarded. .TP \fB\-map\fR disk_path iso_rr_path Insert file object disk_path into the ISO image as iso_rr_path. If disk_path is a directory then its whole sub tree is inserted into the ISO image. .TP \fB\-map_single\fR disk_path iso_rr_path Like \-map, but if disk_path is a directory then its sub tree is not inserted. .TP \fB\-map_l\fR disk_prefix iso_rr_prefix disk_path [***] Perform \-map with each of the disk_path parameters. iso_rr_path will be composed from disk_path by replacing disk_prefix by iso_rr_prefix. .TP \fB\-update\fR disk_path iso_rr_path Compare file object disk_path with file object iso_rr_path. If they do not match, then perform the necessary image manipulations to make iso_rr_path a matching copy of disk_path. By default this comparison will imply lengthy content reading before a decision is made. Commands \-disk_dev_ino or \-md5 may accelerate comparison if they were already in effect when the loaded session was recorded. .br If disk_path is a directory and iso_rr_path does not exist yet, then the whole subtree will be inserted. Else only directory attributes will be updated. .TP \fB\-update_r\fR disk_path iso_rr_path Like \-update but working recursively. I.e. all file objects below both addresses get compared whether they have counterparts below the other address and whether both counterparts match. If there is a mismatch then the necessary update manipulation is done. .br Note that the comparison result may depend on command \-follow. Its setting should always be the same as with the first adding of disk_path as iso_rr_path. .br If iso_rr_path does not exist yet, then it gets added. If disk_path does not exist, then iso_rr_path gets deleted. .TP \fB\-update_l\fR disk_prefix iso_rr_prefix disk_path [***] Perform \-update_r with each of the disk_path parameters. iso_rr_path will be composed from disk_path by replacing disk_prefix by iso_rr_prefix. .TP \fB\-update_li\fR iso_rr_prefix disk_prefix iso_rr_path [***] Perform \-update_r with each of the iso_rr_path parameters. disk_path will be composed from iso_rr_path by replacing iso_rr_prefix by disk_prefix. .TP \fB\-update_lxi\fR disk_prefix iso_rr_prefix disk_path [***] Perform \-update_r with each of the disk_path parameters and with iso_rr_paths in the ISO filesystem which are derived from the disk_path parameters after exchanging disk_prefix by iso_rr_prefix. So, other than \-update_l, this detects missing matches of disk_path and deletes the corresponding iso_rr_path. .br Note that relative disk_paths and disk_path patterns are interpreted as sub paths of the current disk working directory \-cdx. The corresponding iso_rr_paths are derived by exchanging disk_prefix by iso_rr_prefix before pattern expansion happens. The current \-cdi directory has no influence. .TP \fB\-cut_out\fR disk_path byte_offset byte_count iso_rr_path Map a byte interval of a regular disk file or of a device file into a regular file in the ISO image. The file depicted by disk_path has to support random read access. A symbolic link will only work if enabled by \-follow "link" or "param". .br Cutting out a byte interval may be necessary if the disk file is larger than a single medium, or if it exceeds the traditional limit of 2 GiB \- 1 for old operating systems, or the limit of 4 GiB \- 1 for newer ones. Contemporary Linux kernels are able to read properly files >= 4 GiB \- 1. .br A clumsy remedy for such limits is to backup file pieces and to concatenate them at restore time. A well tested chopping size is 2047m. It is permissible to request a higher byte_count than available. The resulting file will be truncated to the correct size of a final piece. To request a byte_offset higher than available yields no file in the ISO image but a SORRY event. E.g: .br \-cut_out /my/disk/file 0 2047m \\ .br /file/part_1_of_3_at_0_with_2047m_of_5753194821 \\ .br \-cut_out /my/disk/file 2047m 2047m \\ .br /file/part_2_of_3_at_2047m_with_2047m_of_5753194821 \\ .br \-cut_out /my/disk/file 4094m 2047m \\ .br /file/part_3_of_3_at_4094m_with_2047m_of_5753194821 .br If the directory /file does no yet exist, then its permissions are not taken from directory /my/disk but rather from /my/disk/file with additional x\-permission for those who have r\-permission. .br While command \-split_size is set larger than 0, and if all pieces of a file reside in the same ISO directory with no other files, and if the names look like above, then their ISO directory will be recognized and handled like a regular file. This affects commands \-compare*, \-update*, and overwrite situations. .br See command \-split_size for details. .br Another use case is copying the content of a device file as interval or as a whole into the emerging ISO filesystem. The fact that the byte_count is allowed to be unreasonably high enables copying of a whole device: .br \-cut_out /dev/sdd3 0 1000g /content_of_sdd3 .TP \fB\-cpr\fR disk_path [***] iso_rr_path Insert the given files or directory trees from filesystem into the ISO image. .br The rules for generating the ISO addresses are similar as with shell command cp \-r. Nevertheless, directories of the iso_rr_path are created if necessary. Especially a not yet existing iso_rr_path will be handled as directory if multiple disk_paths are present. The leafnames of the multiple disk_paths will be grafted under that directory as would be done with an existing directory. .br If a single disk_path is present then a non\-existing iso_rr_path will get the same type as the disk_path. .br If a disk_path does not begin with '/' then \-cdx is prepended. If the iso_rr_path does not begin with '/' then \-cd is prepended. .TP \fB\-mkdir\fR iso_rr_path [...] Create empty directories if they do not exist yet. Existence as directory generates a WARNING event, existence as other file causes a FAILURE event. .TP \fB\-lns\fR target_text iso_rr_path Create a symbolic link with address iso_rr_path which points to target_text. iso_rr_path may not exist yet. .br Hint: Command \-clone produces the ISO equivalent of a hard link. .TP \fB\-clone\fR iso_rr_path_original iso_rr_path_copy Create a copy of the ISO file object iso_rr_path_original with the new address iso_rr_path_copy. If the original is a directory then copy all files and directories underneath. If iso_rr_path_original is a boot catalog file, then it gets not copied but is silently ignored. .br The copied ISO file objects have the same attributes. Copied data files refer to the same content source as their originals. The copies may then be manipulated independendly of their originals. .br This command will refuse execution if the address iso_rr_path_copy already exists in the ISO tree. .TP \fB\-cp_clone\fR iso_rr_path_original [***] iso_rr_path_dest Create copies of one or more ISO file objects as with command \-clone. In case of collision merge directories with existing ones, but do not overwrite existing ISO file objects. .br The rules for generating the copy addresses are the same as with command \-cpr (see above) or shell command cp \-r. Other than with \-cpr, relative iso_rr_path_original will get prepended the \-cd path and not the \-cdx path. Consider to \-mkdir iso_rr_path_dest before \-cp_clone so the copy address does not depend on the number of iso_rr_path_original parameters. .TP .B Settings for file insertion: .TP \fB\-file_size_limit\fR value [value [...]] -- Set the maximum permissible size for a single data file. The values get summed up for the actual limit. If the only value is "off" then the file size is not limited by \fBxorriso\fR. Default is a limit of 100 extents, 4g \-2k each: .br \-file_size_limit 400g \-200k \-\- .br When mounting ISO 9660 filesystems, old operating systems can handle only files up to 2g \-1 \-\-. Newer ones are good up to 4g \-1 \-\-. You need quite a new Linux kernel to read correctly the final bytes of a file >= 4g if its size is not aligned to 2048 byte blocks. .br \fBxorriso\fR's own data read capabilities are not affected by operating system size limits. Such limits apply to mounting only. Nevertheless, the target filesystem of an \-extract must be able to take the file size. .TP \fB\-not_mgt\fR code[:code[...]] Control the behavior of the exclusion lists. .br Exclusion processing happens before disk_paths get mapped to the ISO image, before disk files get compared with image files, and before image files get extracted to disk files. .br The absolute disk paths involved in such an action are matched against the \-not_paths list. The leafnames of disk paths are matched against the patterns in the \-not_leaf list. If a match is detected then the disk path will not be regarded as an existing file and not be added to the ISO image. .br Several codes are defined. The _on/_off settings persist until they are revoked by their_off/_on counterparts. .br "erase" empties the lists which were accumulated by \-not_paths and \-not_leaf. .br "reset" is like "erase" but also re\-installs default behavior. .br "off" disables exclusion processing temporarily without invalidating the lists and settings. .br "on" re\-enables exclusion processing. .br "param_off" applies exclusion processing only to paths below disk_path parameter of commands. I.e. explicitly given disk_paths are exempted from exclusion processing. .br "param_on" applies exclusion processing to command parameters as well as to files below such parameters. .br "subtree_off" with "param_on" excludes parameter paths only if they match a \-not_paths item exactly. .br "subtree_on" additionally excludes parameter paths which lead to a file address below any \-not_paths item. .br "ignore_off" treats excluded disk files as if they were missing. I.e. they get reported with \-compare and deleted from the image with \-update. .br "ignore_on" keeps excluded files out of \-compare or \-update activities. .TP \fB\-not_paths\fR disk_path [***] Add the given paths to the list of excluded absolute disk paths. If a given path is relative, then the current \-cdx is prepended to form an absolute path. Pattern matching, if enabled, happens at definition time and not when exclusion checks are made. .br Keep in mind that there may be alternative paths to the same disk file. The exclusion tests are done literally, so that they do not keep files from getting into the ISO filesystem by other paths. Accordingly an exclusion does not prevent a disk file from being overwritten by file extraction via an alternative not excluded path. So the exclusions need to be coordinated with the actual disk_path parameters given with commands. .br (Do not forget to end the list of disk_paths by "\-\-") .TP \fB\-not_leaf\fR pattern Add a single shell parser style pattern to the list of exclusions for disk leafnames. These patterns are evaluated when the exclusion checks are made. .TP \fB\-not_list\fR disk_path Read lines from disk_path and use each of them either as \-not_paths parameter, if they contain a / character, or as \-not_leaf pattern. .TP \fB\-quoted_not_list\fR disk_path Like \-not_list but with quoted input reading rules. Each word is handled as one parameter for \-not_paths or \-not_leaf. .TP \fB\-follow\fR occasion[:occasion[...]] Enable or disable resolution of symbolic links and mountpoints under disk_paths. This applies to actions \-add, \-cut_out, \-du*x, \-ls*x, \-findx, \-concat, and to \-disk_pattern expansion. .br There are three main kinds of follow decisison to be made: .br \fBlink\fR is the hop from a symbolic link to its target file object for the purpose of reading. I.e. not for command \-concat. If enabled then symbolic links are handled as their target file objects, else symbolic links are handled as themselves. .br \fBmount\fR is the hop from one filesystem to another subordinate filesystem. If enabled then mountpoint directories are handled as any other directory, else mountpoints are handled as empty directories if they are encountered in directory tree traversals. .br \fBconcat\fR is the hop from a symbolic link to its target file object for the purpose of writing. I.e. for command \-concat. This is a security risk ! .br Less general than above occasions: .br \fBpattern\fR is mount and link hopping, but only during \-disk_pattern expansion. .br \fBparam\fR is link hopping for parameter words (after eventual pattern expansion). If enabled then \-ls*x will show the link targets rather than the links themselves. \-du*x, \-findx, and \-add will process the link targets but not follow links in an eventual directory tree below the targets (unless "link" is enabled). \-cut_out will process link targets. .br Occasions can be combined in a colon separated list. All occasions mentioned in the list will then lead to a positive follow decision. .br \fBoff\fR prevents any positive follow decision. Use it if no other occasion applies. .br Shortcuts: .br \fBdefault\fR is equivalent to "pattern:mount:limit=100". .br \fBon\fR always decides positive. Equivalent to "link:mount:concat". .br Not an occasion but an optional setting is: .br \fBlimit=\fR<number> which sets the maximum number of link hops. A link hop consists of a sequence of symbolic links and a final target of different type. Nevertheless those hops can loop. Example: .br $ ln \-s .. uploop .br Link hopping has a built\-in loop detection which stops hopping at the first repetition of a link target. Then the repeated link is handled as itself and not as its target. Regrettably one can construct link networks which cause exponential workload before their loops get detected. The number given with "limit=" can curb this workload at the risk of truncating an intentional sequence of link hops. .TP \fB\-pathspecs\fR "on"|"off"|"as_mkisofs" Control parameter interpretation with \fBxorriso\fR actions \-add and \-path_list. .br Mode "as_mkisofs" enables pathspecs of the form .br \fBiso_rr_path=disk_path\fR .br like with program mkisofs \-graft\-points. .br All characters '\\' must be escaped in both, iso_rr_path and disk_path. The character '=' must be escaped in the iso_rr_path and may or may not be escaped in the disk_path. This mode temporarily disables \-disk_pattern expansion for command \-add. .br Mode "on" does nearly the same. But '=' must only be escaped in the iso_rr_path and '\\' must not be escaped at all. This has the disadvantage that one cannot express an iso_rr_path which ends by '\\'. .br Mode "off" disables pathspecs of the form target=source and re\-enables \-disk_pattern expansion. .TP \fB\-overwrite\fR "on"|"nondir"|"off" Allow or disallow overwriting of existing files in the ISO image or in the local filesystem by files with the same name. .br With setting "off", name collisions with at least one non\-directory file cause FAILURE events. Collisions of two directories lead to merging of their file lists. .br With setting "nondir", only directories are protected by such events, other existing file types get treated with \-rm before the new file gets added. Setting "on" enables automatic \-rm_r. I.e. a non\-directory can replace an existing directory and all its subordinates. .br If restoring of files to the disk filesystem is enabled by \-osirrox, then the overwrite rule applies to the target file objects on disk as well, but "on" is downgraded to "nondir". .TP \fB\-split_size\fR number["k"|"m"] Set the threshold for automatic splitting of regular files. Such splitting maps a large disk file onto a ISO directory with several part files in it. This is necessary if the size of the disk file exceeds \-file_size_limit. Older operating systems can handle files in mounted ISO 9660 filesystems only if they are smaller than 2 GiB or in other cases 4 GiB. .br Default is 0 which will exclude files larger than \-file_size_limit by a FAILURE event. A well tested \-split_size is 2047m. Sizes above \-file_size_limit are not permissible. .br The newly created ISO directory inherits its permissions from the data file with additional x\-permission for those who have r\-permission. .br While command \-split_size is set larger than 0 such a directory with split file pieces will be recognized and handled like a regular file by commands \-compare* , \-update*, and in overwrite situations. There are \-osirrox parameters "concat_split_on" and "concat_split_off" which control the handling when files get restored to disk. .br In order to be recognizable, the names of the part files have to describe the splitting by 5 numbers: .br part_number,total_parts,byte_offset,byte_count,disk_file_size .br which are embedded in the following text form: .br part_#_of_#_at_#_with_#_of_# .br Scaling characters like "m" or "k" are taken into respect. All digits are interpreted as decimal, even if leading zeros are present. .br E.g: /file/part_1_of_3_at_0_with_2047m_of_5753194821 .br No other files are allowed in the directory. All parts have to be present and their numbers have to be plausible. E.g. byte_count must be valid as \-cut_out parameter and their contents may not overlap. .TP .B File manipulations: .PP The following commands manipulate files in the ISO image, regardless whether they stem from the loaded image or were newly inserted. .PP .TP \fB\-iso_rr_pattern\fR "on"|"ls"|"off" Set the pattern expansion mode for the iso_rr_path parameters of several commands which support this feature. .br Setting "off" disables pattern expansion for all commands which are marked in this man page by "iso_rr_path [***]" or "iso_rr_pattern [***]". .br Setting "on" enables it for all those commands. .br Setting "ls" enables it only for those which are marked by "iso_rr_pattern [***]". .br Default is "on". .TP \fB\-rm\fR iso_rr_path [***] Delete the given files from the ISO image. .br Note: This does not free any space on the \-indev medium, even if the deletion is committed to that same medium. .br The image size will shrink if the image is written to a different medium in modification mode. .TP \fB\-rm_r\fR iso_rr_path [***] Delete the given files or directory trees from the ISO image. See also the note with command \-rm. .TP \fB\-rmdir\fR iso_rr_path [***] Delete empty directories. .TP \fB\-move\fR iso_rr_path iso_rr_path Rename the file given by the first (origin) iso_rr_path to the second (destination) iso_rr_path. Deviate from rules of shell command mv by not moving the origin file underneath an existing destination directory. The origin file will rather replace such a directory, if this is allowed by command \-overwrite. .TP \fB\-mv\fR iso_rr_path [***] iso_rr_path Rename the given file objects in the ISO tree to the last parameter in the list. Use the same rules as with shell command mv. .br If pattern expansion is enabled and if the last parameter contains wildcard characters then it must match exactly one existing file address, or else the command fails with a FAILURE event. .TP \fB\-chown\fR uid iso_rr_path [***] Set ownership of file objects in the ISO image. uid may either be a decimal number or the name of a user known to the operating system. .TP \fB\-chown_r\fR uid iso_rr_path [***] Like \-chown but affecting all files below eventual directories. .TP \fB\-chgrp\fR gid iso_rr_path [***] Set group attribute of file objects in the ISO image. gid may either be a decimal number or the name of a group known to the operating system. .TP \fB\-chgrp_r\fR gid iso_rr_path [***] Like \-chgrp but affecting all files below eventual directories. .TP \fB\-chmod\fR mode iso_rr_path [***] Equivalent to shell command chmod in the ISO image. mode is either an octal number beginning with "0" or a comma separated list of statements of the form [ugoa]*[+\-=][rwxst]* . .br Like: go\-rwx,u+rwx . .br \fBPersonalities\fR: u=user, g=group, o=others, a=all .br \fBOperators\fR: + adds given permissions, \- revokes given permissions, = revokes all old permissions and then adds the given ones. .br \fBPermissions\fR: r=read, w=write, x=execute|inspect, s=setuid|setgid, t=sticky bit .br For octal numbers see man 2 stat. .TP \fB\-chmod_r\fR mode iso_rr_path [***] Like \-chmod but affecting all files below eventual directories. .TP \fB\-setfacl\fR acl_text iso_rr_path [***] Attach the given ACL to the given iso_rr_paths. If the files already have ACLs, then those get deleted before the new ones get into effect. If acl_text is empty, or contains the text "clear" or the text "\-\-remove\-all", then the existing ACLs will be removed and no new ones will be attached. Any other content of acl_text will be interpreted as a list of ACL entries. It may be in the long multi\-line format as put out by \-getfacl but may also be abbreviated as follows: .br ACL entries are separated by comma or newline. If an entry is empty text or begins with "#" then it will be ignored. A valid entry has to begin by a letter out of {ugom} for "user", "group", "other", "mask". It has to contain two colons ":". A non\-empty text between those ":" gives a user id or group id. After the second ":" there may be letters out of {rwx\- #}. The first three give read, write, or execute permission. Letters "\-", " " and TAB are ignored. "#" causes the rest of the entry to be ignored. Letter "X" or any other letters are not supported. Examples: .br g:toolies:rw,u:lisa:rw,u:1001:rw,u::wr,g::r,o::r,m::rw .br group:toolies:rw\-,user::rw\-,group::r\-\-,other::r\-\-,mask::rw\- .br A valid entry may be prefixed by "d", some following characters and ":". This indicates that the entry goes to the "default" ACL rather than to the "access" ACL. Example: .br u::rwx,g::rx,o::,d:u::rwx,d:g::rx,d:o::,d:u:lisa:rwx,d:m::rwx .TP \fB\-setfacl_r\fR acl_text iso_rr_path [***] Like \-setfacl but affecting all files below given directories. .TP \fB\-setfacl_list\fR disk_path Read the output of \-getfacl_r or shell command getfacl \-R and apply it to the iso_rr_paths as given in lines beginning with "# file:". This will change ownership, group and ACL of the given files. If disk_path is "\-" then lines are read from standard input. Line "@" ends the list, "@@@" aborts without changing the pending iso_rr_path. .br Since \-getfacl and getfacl \-R strip leading "/" from file paths, the setting of \-cd does always matter. .TP \fB\-setfattr\fR [-]name value iso_rr_path [***] Attach the given xattr pair of name and value to the given iso_rr_paths. If the given name is prefixed by "\-", then the pair with that name gets removed from the xattr list. If name is "\-\-remove\-all" then all user namespace xattr of the given iso_rr_paths get deleted. In case of deletion, value must be an empty text. .br Which names are permissible depends on the setting of command \-xattr. "on" or "user" restricts them to namespace "user". I.e. a name has to look like "user.x" or "user.whatever". .br \-xattr setting "any" enables names from all namespaces except "isofs". .br Values and names undergo the normal input processing of \fBxorriso\fR. See also command \-backslash_codes. Other than with command \-setfattr_list, the byte value 0 cannot be expressed via \-setfattr. .TP \fB\-setfattr_r\fR [-]name value iso_rr_path [***] Like \-setfattr but affecting all files below given directories. .TP \fB\-setfattr_list\fR disk_path Read the output format of \-getfattr_r or shell command getfattr \-Rd and apply it to the iso_rr_paths as given in lines beginning with "# file:". All previously existing xattr of the acceptable namespaces will be deleted before the new xattr get attached. The set of acceptable names depends on the setting of command \-xattr. .br If disk_path is "\-" then lines are read from standard input. .br Since \-getfattr and getfattr \-Rd strip leading "/" from file paths, the setting of \-cd does always matter. .br Empty input lines and lines which begin by "#" will be ignored (except "# file:"). Line "@" ends the list, "@@@" aborts without changing the pending iso_rr_path. Other input lines must have the form .br name="value" .br The separator "=" is not allowed in names. Value may contain any kind of bytes. It must be in quotes. Trailing whitespace after the end quote will be ignored. Non\-printables bytes and quotes must be represented as \\XYZ by their octal 8\-bit code XYZ. Use code \\000 for 0\-bytes. .TP \fB\-chattr\fR mode iso_rr_path [***] Set or unset Linux file attributes like program chattr(1) would do to disk files. Applying this command to files which are neither directory nor regular data file will yield a SORRY event, unless the mode is "\-\-remove\-lfa\-flags". .br The first letter of the mode string determines what to do. The other letters are symbolic attribute flag letters out of the set "aAcCdDeEFhiIjNmPsStTuVxZ" as described in man 1 chattr. There is no restriction which attributes can be set or unset. But at restore time, unusual or unsuitable attributes may cause problems. .br First letter '+' causes the given attribute flags to be set. All other attributes stay as they are. .br First letter '\-' causes the given attribute flags to be unset. All other attributes stay as they are. (Note that '\-' is also accepted as symbolic attribute letter which has no effect.) .br A special case is the mode string "\-\-remove\-lfa\-flags" which causes the Linux file attribute information to be removed from the file. The \-find test \-has_lfa_flags "\-" will then not match the file any more. .br First letter '.' causes all attribute flags except the given ones to be unset. The given ones stay as they are. This is not a feature of program chattr(1). .br First letter '=' causes the given attribute flags to be set. All other get unset. Mode "=\-" leads to all attribute flags being unset, but \-find test \-has_lfa_flags "\-" will match the file afterwards. .br Example: \-chattr +sDu /my/file /my/other_file \-\- .TP \fB\-chattr_r\fR mode iso_rr_path [***] Like \-chattr but affecting also all suitable files below the given directories. Except with mode "\-\-remove\-lfa\-flags", the given iso_rr_path parameters need to be directories or regular data files or else a SORRY event will happen. Files below the given directories will be skipped silently if their type is not suitable for \-chattr. .TP \fB\-alter_date\fR type timestring iso_rr_path [***] Alter the date entries of files in the ISO image. type may be one of the following: .br "a" sets access time, updates ctime. .br "m" sets modification time, updates ctime. .br "b" sets access time and modification time, updates ctime. .br "a\-c", "m\-c", and "b\-c" set the times without updating ctime. .br "c" sets the ctime. .br timestring may be in the following formats (see also section EXAMPLES): .br As expected by program date: MMDDhhmm[[CC]YY][.ss]] .br As produced by program date: .br [Day] MMM DD hh:mm:ss [TZON] YYYY .br Relative times counted from current clock time: .br +|\-Number["s"|"h"|"d"|"w"|"m"|"y"] .br where "s" means seconds, "h" hours, "d" days, "w" weeks, "m"=30d, "y"=365.25d plus 1d added to multiplication result. .br Absolute seconds counted from Jan 1 1970 00:00 GMT: .br =Number .br \fBxorriso\fR's own timestamps: .br YYYY.MM.DD[.hh[mm[ss]]] .br scdbackup timestamps: .br YYMMDD[.hhmm[ss]] .br where "A0" is year 2000, "B0" is 2010, etc. .br ECMA\-119 volume timestamps: .br YYYYMMDDhhmmsscc .br These are normally given as GMT. The suffix "LOC" causes local timezone conversion. E.g. 2013010720574700, 2013010720574700LOC. The last two digits cc (centiseconds) will be ignored, but must be present in order to make the format recognizable. .br Example: .br \-alter_date m\-c 2013.11.27.103951 /file1 /file2 \-\- .br This command does not persistently apply to the boot catalog, which gets fresh timestamps at \-commit time. Command \-volume_date "uuid" can set this time value. .TP \fB\-alter_date_r\fR type timestring iso_rr_path [***] Like \-alter_date but affecting all files below eventual directories. .TP \fB\-hide\fR hide_state iso_rr_path [***] Prevent the names of the given files from showing up in the directory trees of ISO 9660 and/or Joliet and/or HFS+ when the image gets written. The data content of such hidden files will be included in the resulting image, even if they do not show up in any directory. But you will need own means to find nameless data in the image. .br Warning: Data which are hidden from the ISO 9660 tree will not be copied by the write method of modifying. .br Possible values of hide_state are: "iso_rr" for hiding from ISO 9660 tree, "joliet" for Joliet tree, "hfsplus" for HFS+, "on" for them all. "off" means visibility in all directory trees. .br These values may be combined. E.g.: joliet:hfsplus .br This command does not apply to the boot catalog. Rather use: \-boot_image "any" "cat_hidden=on" .TP .B Tree traversal command -find: .PP .TP \fB\-find\fR iso_rr_path [test [op] [test ...]] [-exec action [params]] -- A restricted substitute for shell command find in the ISO image. It performs an action on matching file objects at or below iso_rr_path. .br If not used as last command in the line then the parameter list needs to get terminated by "\-\-". .br Tests are optional. If they are omitted then action is applied to all file objects. If tests are given then they form together an expression. The action is applied only if the expression matches the file object. Default expression operator between tests is \-and, i.e. the expression matches only if all its tests match. .br Available tests are: .br \fB\-name\fR pattern : Matches if pattern matches the file leaf name. If the pattern does not contain any of the characters "*?[", then it will be truncated according to \-file_name_limit and thus match the truncated name in the ISO filesystem. .br \fB\-wholename\fR pattern : Matches if pattern matches the file path as it would be printed by action "echo". Character '/' can be matched by wildcards. If pattern pieces between '/' do not contain any of the characters "*?[", they will be truncated according to \-file_name_limit. .br \fB\-disk_name\fR pattern : Like \-name but testing the leaf name of the file source on disk. Can match only data files which do not stem from the loaded image, or for directories above such data files. With directories the result can change between \-find runs if their content stems from multiple sources. .br \fB\-disk_path\fR disk_path : Matches if the given disk_path is equal to the path of the file source on disk. The same restrictions apply as with \-disk_name. .br \fB\-type\fR type_letter : Matches files of the given type: "block", "char", "dir", "pipe", "file", "link", "socket", "eltorito", and "Xotic" which matches what is not matched by the other types. .br Only the first letter is interpreted. E.g.: \-find / \-type d .br \fB\-size\fR [+-][=]number[cwbdksmg] : Matches files with matching relation to the given size number. .br The prefix defines the desired relation: .br No prefix or prefix "=" means: File must have exactly the given size. .br Prefix "+" means: File must be larger than given size. .br Prefix "+=" means: File must be larger than or equal to given size limit. .br Prefix "\-" means: File must be smaller than given size limit. .br Prefix "\-=" means: File must be smaller than or equal to given size limit. .br Suffixes are peculiar to stay compatible with program "find": .br No suffix means blocks of 512 bytes, "c" means single bytes, "w" means 2 bytes, "b" means 512 bytes. The suffixes "k", "M", and "G" mean 1024, 1024k, and 1024M respectively. As usual with xorriso, the suffixes "d" and "s" mean 512 and 2048 and all suffixes are recognized as both, uppercase and lowercase letters. .br E.g. match files of 4 GiB or larger: .br \-size +=4g .br \fB\-maxdepth\fR number : Matches only files which are at most at the given depth level relative to the iso_rr_path where \-find starts. That path itself is at depth 0, its directory children are at 1, their directory children at 2, and so on. .br \fB\-mindepth\fR number : Matches only files which are at least at the given depth level. .br \fB\-damaged\fR : Matches files which use data blocks marked as damaged by a previous run of \-check_media. The damage info vanishes when a new ISO image gets loaded. .br Note that a MD5 session mismatch marks all files of the session as damaged. If finer distinction is desired, perform \-md5 off before \-check_media. .br \fB\-pending_data\fR : Matches files which get their content from outside the loaded ISO image. .br \fB\-lba_range\fR start_lba block_count : Matches files which use data blocks within the range of start_lba and start_lba+block_count\-1. .br \fB\-has_acl\fR : Matches files which have a non\-trivial ACL. .br \fB\-has_xattr\fR : Matches files which have xattr name\-value pairs from user namespace. .br \fB\-has_aaip\fR : Matches files which have ACL or any xattr. .br \fB\-has_lfa_flags\fR flag_letters : Matches files which have Linux file attributes attached and have all flags set which correspond to the characters in the string flag_letters. The characters may be zero or more out of the set "aAcCdDeEFhiIjNmPsStTuVxZ". The character '\-' will be ignored. The flag_letters string "\-" matches any attribute set, but not a file with no Linux file attributes attached. E.g. look for files with both flags 'i' (immutable) and 'd' (no dump) set: .br \-has_lfa_flags di .br \fB\-has_some_lfa_flags_of\fR flag_letters : .br Similar to \-has_lfa_flags but matching files which have at least one of the flags set which correspond to the characters in the string flag_letters. The flag_letters string "\-" never matches any file. E.g. look for files which have 'i' or 'a' or both of them set: .br \-has_some_lfa_flags_of ia .br \fB\-has_any_xattr\fR : Matches files which have any xattr other than ACL. .br \fB\-has_md5\fR : Matches data files which have MD5 checksums. .br \fB\-has_hfs_crtp\fR creator type : Matches files which have the given HFS+ creator and type attached. These are codes of 4 characters which get stored if \-hfsplus is enabled. Use a single dash '\-' as wildcard that matches any such code. E.g:. .br \-has_hfs_crtp YYDN TEXT .br \-has_hfs_crtp \- \- .br \fB\-has_hfs_bless\fR blessing : Matches files which bear the given HFS+ blessing. It may be one of : "ppc_bootdir", "intel_bootfile", "show_folder", "os9_folder", "osx_folder", "any". See also action set_hfs_bless. .br \fB\-has_filter\fR : Matches files which are filtered by \-set_filter. .br \fB\-hidden\fR hide_state : Matches files which are hidden in "iso_rr" tree, in "joliet" tree, in "hfsplus" tree, in all trees ("on"), or not hidden in any tree ("off"). .br Those which are hidden in some tree match \-not \-hidden "off". .br \fB\-bad_outname\fR namespace : Matches files with names which change when converted forth and back between the local character set and one of the namespaces "rockridge", "joliet", "ecma119", "hfsplus". .br All applicable \-compliance rules are taken into respect. Rule "omit_version" is always enabled, because else namespaces "joliet" and "ecma119" would cause changes with every non\-directory name. Consider to also enable rules "no_force_dots" and "no_j_force_dots". .br The namespaces use different character sets and apply further restrictions to name length, permissible characters, and mandatory name components. "rockridge" uses the character set defined by \-out_charset, "joliet" uses UCS\-2BE, "ecma119" uses ASCII, "hfsplus" uses UTF\-16BE. .br \fB\-name_limit_blocker\fR length : Matches file names which would prevent command \-file_name_limit with the given length. The command itself reports only the first problem file. .br \fB\-prune\fR : If this test is reached and the tested file is a directory then \-find will not dive into that directory. This test itself does always match. .br \fB\-use_pattern\fR "on"|"off" : This pseudo test controls the interpretation of wildcards with tests \-name, \-wholename, and \-disk_name. Default is "on". If interpretation is disabled by "off", then the parameters of \-name, \-wholename, and \-disk_name have to match literally rather than as search pattern. This test itself does always match. .br \fB\-or_use_pattern\fR "on"|"off" : Like \-use_pattern, but automatically appending the test by \-or rather than by \-and. Further the test itself does never match. So a subsequent test \-or will cause its other operand to be performed. .br \fB\-decision\fR "yes"|"no" : If this test is reached then the evaluation ends immediately and action is performed if the decision is "yes" or "true". See operator \-if. .br \fB\-true\fR and \fB\-false\fR : Always match or match not, respectively. Evaluation goes on. .br \fB\-sort_lba\fR : Always match. This causes \-find to perform its action in a sequence sorted by the ISO image block addresses of the files. It may improve throughput with actions which read data from optical drives. Action will always get the absolute path as parameter. .br Available operators are: .br \fB\-not\fR : Matches if the next test or sub expression does not match. Several tests do this specifically: .br \-undamaged, \-lba_range with negative start_lba, \-has_no_acl, \-has_no_xattr, \-has_no_aaip, \-has_no_filter . .br \fB\-and\fR : Matches if both neighboring tests or expressions match. .br \fB\-or\fR : Matches if at least one of both neighboring tests or expressions matches. .br \fB\-sub\fR ... \fB\-subend\fR or \fB(\fR ... \fB)\fR : Enclose a sub expression which gets evaluated first before it is processed by neighboring operators. Normal precedence is: \-not, \-or , \-and. .br \fB\-if\fR ... \fB\-then\fR\ ... \fB\-elseif\fR ... \fB\-then\fR ... \fB\-else\fR ... \fB\-endif\fR : Enclose one or more sub expressions. If the \-if expression matches, then the \-then expression is evaluated as the result of the whole expression up to \-endif. Else the next \-elseif expression is evaluated and if it matches, its \-then expression. Finally in case of no match, the \-else expression is evaluated. There may be more than one \-elseif. Neither \-else nor \-elseif are mandatory. If \-else is missing and would be hit, then the result is a non\-match. .br \-if\-expressions are the main use case for above test \-decision. Default action is \fBecho\fR, i.e. to print the address of the found file. Other actions are certain \fBxorriso\fR commands which get performed on the found files. These commands may have specific parameters. See also their particular descriptions. .br \fBchown\fR and \fBchown_r\fR change the ownership and get the user id as parameter. E.g.: \-exec chown thomas \-\- .br \fBchgrp\fR and \fBchgrp_r\fR change the group attribute and get the group id as parameter. E.g.: \-exec chgrp_r staff \-\- .br \fBchmod\fR and \fBchmod_r\fR change access permissions and get a mode string as parameter. E.g.: \-exec chmod a\-w,a+r \-\- .br \fBalter_date\fR and \fBalter_date_r\fR change the timestamps. They get a type character and a timestring as parameters. .br E.g.: \-exec alter_date "m" "Dec 30 19:34:12 2007" \-\- .br \fBset_to_mtime\fR sets the ctime and atime to the value found in mtime. .br \fBlsdl\fR prints file information like shell command ls \-dl. .br \fBcompare\fR performs command \-compare with the found file address as iso_rr_path and the corresponding file address below its parameter disk_path_start. For this the iso_rr_path of the \-find command gets replaced by the disk_path_start. .br E.g.: \-find /thomas \-exec compare /home/thomas \-\- .br \fBupdate\fR performs command \-update with the found file address as iso_rr_path. The corresponding file address is determined like with above action "compare". .br \fBupdate_merge\fR is like update but does not delete the found file if it is missing on disk. It may be run several times and records with all visited files whether their counterpart on disk has already been seen by one of the update_merge runs. Finally, a \-find run with action "rm_merge" may remove all files that saw no counterpart on disk. .br Up to the next "rm_merge" or "clear_merge" all newly inserted files will get marked as having a disk counterpart. .br \fBrm\fR removes the found iso_rr_path from the image if it is not a directory with files in it. I.e. this "rm" includes "rmdir". .br \fBrm_r\fR removes the found iso_rr_path from the image, including whole directory trees. .br \fBrm_merge\fR removes the found iso_rr_path if it was visited by one or more previous actions "update_merge" and saw no counterpart on disk in any of them. The marking from the update actions is removed in any case. .br \fBclear_merge\fR removes an eventual marking from action "update_merge". .br \fBreport_damage\fR classifies files whether they hit a data block that is marked as damaged. The result is printed together with the address of the first damaged byte, the maximum span of damages, file size, and the path of the file. .br \fBreport_lba\fR prints files which are associated to image data blocks. It tells the logical block address, the block number, the byte size, and the path of each file. There may be reported more than one line per file if the file has more than one section. In this case each line has a different extent number in column "xt". .br \fBreport_sections\fR like report_lba but telling the byte sizes of the particular sections rather than the overall byte size of the file. .br \fBgetfacl\fR prints access permissions in ACL text form to the result channel. .br \fBsetfacl\fR attaches ACLs after removing existing ones. The new ACL is given in text form as defined with command \-setfacl. .br E.g.: \-exec setfacl u:lisa:rw,u::rw,g::r,o::\-,m::rw \-\- .br \fBgetfattr\fR prints xattr name\-value pairs to the result channel. The choice of namespaces depends on the setting of command \-xattr: "off", "on", or "user" restricts it to the namespace "user", "any" only omits namespace "isofs". .br \fBget_any_xattr\fR prints xattr name\-value pairs from any namespace except ACL to the result channel. This is mostly for debugging of namespace "isofs". .br \fBlist_extattr\fR mode prints a script to the result channel, which would use FreeBSD command setextattr to set the file's xattr name\-value pairs of user namespace. Parameter mode controls the form of the output of names and values. Default mode "e" prints harmless characters in shell quotation marks, but represents texts with octal 001 to 037 and 0177 to 0377 by an embedded echo \-e command. Mode "q" prints any characters in shell quotation marks. This might not be terminal\-safe but should work in script files. Mode "r" uses no quotation marks. Not safe. Mode "b" prints backslash encoding. Not suitable for shell parsing. .br E.g.: \-exec list_extattr e \-\- .br Command \-backslash_codes does not affect the output. .br \fBlsattrd\fR shows the Linux file attribute flags like command \-lsattrd does. .br \fBchattr\fR applies \-chattr with the given mode. Other than command \-chattr this silently skips any file which are not \-type "dir" or "file". .br E.g.: \-exec chattr +sDu \-\- .br \fBget_md5\fR prints the MD5 sum, if recorded, together with file path. .br \fBcheck_md5\fR compares the MD5 sum, if recorded, with the file content and reports if mismatch. .br E.g.: \-find / \-not \-pending_data \-exec check_md5 FAILURE \-\- .br \fBmake_md5\fR equips a data file with an MD5 sum of its content. Useful to upgrade the files in the loaded image to full MD5 coverage by the next commit with \-md5 "on". .br E.g.: \-find / \-type f \-not \-has_md5 \-exec make_md5 \-\- .br \fBsetfattr\fR sets or deletes xattr name value pairs. .br E.g.: \-find / \-has_xattr \-exec setfattr \-\-remove\-all '' \-\- .br \fBset_hfs_crtp\fR adds, changes, or removes HFS+ creator and type attributes. .br E.g.: \-exec set_hfs_crtp YYDN TEXT .br E.g.: \-find /my/dir \-prune \-exec set_hfs_crtp \-\-delete \- .br \fBget_hfs_crtp\fR prints the HFS+ creator and type attributes together with the iso_rr_path, if the file has such attributes at all. .br E.g.: \-exec get_hfs_crtp .br \fBset_hfs_bless\fR applies or removes HFS+ blessings. They are roles which can be attributed to up to four directories and a data file: .br "ppc_bootdir", "intel_bootfile", "show_folder", "os9_folder", "osx_folder". .br They may be abbreviated as "p", "i", "s", "9", and "x". .br Each such role can be attributed to at most one file object. "intel_bootfile" is the one that would apply to a data file. All others apply to directories. The \-find run will end as soon as the first blessing is issued. The previous bearer of the blessing will lose it then. No file object can bear more than one blessing. .br E.g.: \-find /my/blessed/directory \-exec set_hfs_bless p .br Further there is blessing "none" or "n" which revokes any blessing from the found files. This \-find run will not stop when the first match is reached. .br E.g.: \-find / \-has_hfs_bless any \-exec set_hfs_bless none .br \fBget_hfs_bless\fR prints the HFS+ blessing role and the iso_rr_path, if the file is blessed at all. .br E.g.: \-exec get_hfs_bless .br \fBset_filter\fR applies or removes filters. .br E.g.: \-exec set_filter \-\-zisofs \-\- .br \fBmkisofs_r\fR applies the rules of mkisofs \-r to the file object: .br user id and group id become 0, all r\-permissions get granted, all w denied. If there is any x\-permission, then all three x get granted. s\- and t\-bits get removed. .br \fBsort_weight\fR attributes a LBA weight number to regular files. .br The number may range from \-2147483648 to 2147483647. The higher it is, the lower will be the block address of the file data in the emerging ISO image. Currently the boot catalog has a hardcoded weight of 1 billion. Normally it should occupy the block with the lowest possible address. .br Data files which are loaded by \-indev or \-dev get a weight between 1 and 2 exp 28 = 268,435,456, depending on their block address. This shall keep them roughly in the same order if the write method of modifying is applied. .br Data files which are added by other commands get an initial weight of 0. Boot image files have a default weight of 2. .br E.g.: \-exec sort_weight 3 \-\- .br \fBshow_stream\fR shows the content stream chain of a data file. .br \fBshow_stream_id\fR is like show_stream, but also prints between stream type and first ":" in square brackets libisofs id numbers: [fs_id,dev_id,ino_id]. .br \fBhide\fR brings the file into one of the hide states "on", "iso_rr", "joliet", "hfsplus", "off". They may be combined. E.g.: joliet:hfsplus .br E.g.: .br \-find / \-disk_name *_secret \-exec hide on .br \fBprint_outname\fR prints in the first line the filename as registered by the program model, and in the second line the filename after conversion forth and back between local character set and one of the namespaces "rockridge", "joliet", "ecma119", or "hfsplus". The third output line is "\-\-" . .br The name conversion does not take into respect the possibility of name collisions in the target namespace. Such collisions are most likely in "joliet" and "ecma119", where they get resolved by automatic file name changes. .br E.g.: .br \-find / \-bad_outname joliet \-exec print_outname joliet .br \fBestimate_size\fR prints a lower and an upper estimation of the number of blocks which the found files together will occupy in the emerging ISO image. This does not account for the superblock, for the directories in the \-find path, or for image padding. .br \fBfind\fR performs another run of \-find on the matching file address. It accepts the same params as \-find, except iso_rr_path. .br E.g.: .br \-find / \-name '???' \-type d \-exec find \-name '[abc]*' \-exec chmod a\-w,a+r \-\- .TP .B Filters for data file content: .PP \fBFilters\fR may be installed between data files in the ISO image and their content source outside the image. They may also be used vice versa between data content in the image and target files on disk. .br Built\-in filters are "\-\-zisofs" and "\-\-zisofs\-decode". The former is to be applied via \-set_filter, the latter is automatically applied if zisofs compressed content is detected with a file when loading the ISO image. .br Another built\-in filter pair is "\-\-gzip" and "\-\-gunzip" with suffix ".gz". They behave about like external gzip and gunzip but avoid forking a process for each single file. So they are much faster if there are many small files. .PP .TP \fB\-external_filter\fR name option[:option] program_path [arguments] -- Register a content filter by associating a name with a program path, program arguments, and some behavioral options. Once registered it can be applied to multiple data files in the ISO image, regardless whether their content resides in the loaded ISO image or in the local filesystem. External filter processes may produce synthetic file content by reading the original content from stdin and writing to stdout whatever they want. They must deliver the same output on the same input in repeated runs. .br Options are: .br "default" means that no other option is intended. .br "suffix=..." sets a file name suffix. If it is not empty then it will be appended to the file name or removed from it. .br "remove_suffix" will remove a file name suffix rather than appending it. .br "if_nonempty" will leave 0\-sized files unfiltered. .br "if_reduction" will try filtering and revoke it if the content size does not shrink. .br "if_block_reduction" will revoke if the number of 2 kB blocks does not shrink. .br "used=..." is ignored. Command \-status shows it with the number of files which currently have the filter applied. .br Examples: .br \-external_filter bzip2 suffix=.bz2:if_block_reduction \\ .br /usr/bin/bzip2 \-\- .br \-external_filter bunzip2 suffix=.bz2:remove_suffix \\ .br /usr/bin/bunzip2 \-\- .TP \fB\-unregister_filter\fR name Remove an \-external_filter registration. This is only possible if the filter is not applied to any file in the ISO image. .TP \fB\-close_filter_list\fR Irrevocably ban commands \-concat "pipe", \-external_filter, and \-unregister_filter, but not \-set_filter. Use this to prevent external filtering in general or when all intended filters are registered and \-concat mode "pipe" shall be disallowed. External filters may also be banned totally at compile time of \fBxorriso\fR. By default they are banned if \fBxorriso\fR runs under setuid permission. .TP \fB\-set_filter\fR name iso_rr_path [***] Apply an \-external_filter or a built\-in filter to the given data files in the ISO image. If the filter suffix is not empty , then it will be applied to the file name. Renaming only happens if the filter really gets attached and is not revoked by its options. By default files which already bear the suffix will not get filtered. The others will get the suffix appended to their names. If the filter has option "remove_suffix", then the filter will only be applied if the suffix is present and can be removed. Name oversize or collision caused by suffix change will prevent filtering. .br With most filter types this command will immediately run the filter once for each file in order to determine the output size. Content reading operations like \-extract , \-compare and image generation will perform further filter runs and deliver filtered content. .br At image generation time the filter output must still be the same as the output from the first run. Filtering for image generation does not happen with files from the loaded ISO image if the write method of growing is in effect (i.e \-indev and \-outdev are identical). .br The reserved filter name "\-\-remove\-all\-filters" revokes filtering. This will revoke suffix renamings as well. Use "\-\-remove\-all\-filters+" to prevent any suffix renaming. .br Attaching or detaching filters will not alter the state of \-changes_pending. If the filter manipulations shall be the only changes in a write run, then explicitly execute \-changes_pending "yes". .TP \fB\-set_filter_r\fR name iso_rr_path [***] Like \-set_filter but affecting all data files below eventual directories. .TP .B Writing the result, drive control: .PP (see also paragraph about settings below) .TP \fB\-rollback\fR Discard the manipulated ISO image and reload it from \-indev. (Use \-rollback_end if immediate program end is desired.) .TP \fB\-changes_pending\fR "no"|"yes"|"mkisofs_printed"|"show_status" Write runs are performed only if a change of the image has been made since the image was loaded or created blank. Vice versa the program will start a write run for pending changes when it ends normally (i.e. not by abort and not by command \-rollback_end). .br The command \-changes_pending can be used to override the automatically determined state. This is mainly useful for setting state "yes" despite no real changes were made. The sequence \-changes_pending "no" \-end is equivalent to the command \-rollback_end. State "mkisofs_printed" is caused by emulation command \-as mkisofs if option \-print\-size is present. .br The pseudo\-state "show_status" can be used to print the current state to result channel. .br Image loading or manipulations which happen after this command will again update automatically the change status of the image. .TP \fB\-commit\fR Perform the write operation. Afterwards, if \-outdev is readable, make it the new \-dev and load the image from there. Switch to growing mode. (A subsequent \-outdev will activate modification mode or blind growing.) \-commit is performed automatically at end of program if there are uncommitted manipulations pending. .br So, to perform a final write operation with no new \-dev and no new loading of image, rather execute command \-end. If you want to go on without image loading, execute \-commit_eject "none". To eject after write without image loading, use \-commit_eject "all". .br To suppress a final write, execute \-rollback_end. .br Writing can last quite a while. It is not unnormal with several types of media that there is no progress visible for the first few minutes or that the drive gnaws on the medium for a few minutes after all data have been transmitted. \fBxorriso\fR and the drives are in a client\-server relationship. The drives have much freedom about what to do with the media. Some combinations of drives and media simply do not work, despite the promises by their vendors. If writing fails then try other media or another drive. The reason for such failure is hardly ever in the code of the various burn programs but you may well try some of those listed below under SEE ALSO. .TP \fB\-eject\fR "in"|"out"|"all" Eject the medium in \-indev, \-outdev, or both drives, respectively. Note: It is not possible yet to effectively eject disk files. .TP \fB\-commit_eject\fR "in"|"out"|"all"|"none" Combined \-commit and \-eject. When writing has finished do not make \-outdev the new \-dev, and load no ISO image. Rather eject \-indev and/or \-outdev. Give up any non\-ejected drive. .TP \fB\-blank\fR mode Make media ready for writing from scratch (if not \-dummy is activated). .br This affects only the \-outdev not the \-indev. If both drives are the same and if the ISO image was altered then this command leads to a FAILURE event. Defined modes are: as_needed, fast, all, deformat, deformat_quickest .br "as_needed" cares for used CD\-RW, DVD\-RW and for used overwritable media by applying \-blank "fast". It applies \-format "full" to yet unformatted DVD\-RAM and BD\-RE. Other media in blank state are gracefully ignored. Media which cannot be made ready for writing from scratch cause a FAILURE event. .br "fast" makes CD\-RW and unformatted DVD\-RW re\-usable, or invalidates overwritable ISO images. "all" might work more thoroughly and need more time. .br "deformat" converts overwritable DVD\-RW into unformatted ones. .br "deformat_quickest" is a faster way to deformat or blank DVD\-RW but produces media which are only suitable for a single session. Some drives announce this state by not offering feature 21h, but some drives offer it anyway. If feature 21h is missing, then \fBxorriso\fR will refuse to write on DVD\-RW if not command \-close is set to "on". .br The progress reports issued by some drives while blanking are quite unrealistic. Do not conclude success or failure from the reported percentages. Blanking was successful if no SORRY event or worse occurred. .br Mode may be prepended by "force:" in order to override the evaluation of the medium state by libburn. E.g. "force:fast". Blanking will nevertheless only succeed if the drive is willing to do it. .br .TP \fB\-format\fR mode Convert unformatted DVD\-RW into overwritable ones, "de\-ice" DVD+RW, format newly purchased BD\-RE or BD\-R, re\-format DVD\-RAM or BD\-RE. .br Defined modes are: .br as_needed, full, fast, by_index_<num>, fast_by_index_<num>, by_size_<num>, fast_by_size_<num>, without_spare .br "as_needed" formats yet unformatted DVD\-RW, DVD\-RAM, BD\-RE, or blank unformatted BD\-R. Other media are left untouched. .br "full" (re\-)formats DVD\-RW, DVD+RW, DVD\-RAM, BD\-RE, or blank unformatted BD\-R. .br "fast" does the same as "full" but tries to be quicker. .br "by_index_" selects a format out of the descriptor list issued by command \-list_formats. The index number from that list is to be appended to the mode word. E.g: "by_index_3". .br "fast_by_index_" does the same as "by_index_" but tries to be quicker. .br "by_size_" selects a format out of the descriptor list which provides at least the given size. That size is to be appended to the mode word. E.g: "by_size_4100m". This applies to media with Defect Management. On BD\-RE it will not choose format 0x31, which offers no Defect Management. .br "fast_by_size_" does the same as "by_size_" but tries to be quicker. .br "without_spare" selects the largest format out of the descriptor list which provides no Spare Area for Defect Management. On BD\-RE this will be format 0x31. .br The formatting action has no effect on media if \-dummy is activated. .br Formatting is normally needed only once during the lifetime of a medium, if ever. But it is a reason for re\-formatting if: .br DVD\-RW was deformatted by \-blank, .br DVD+RW has read failures (re\-format before next write), .br DVD\-RAM or BD\-RE shall change their amount of defect reserve. .br BD\-R may be written unformatted or may be formatted before first use. Formatting activates Defect Management which tries to catch and repair bad spots on media during the write process at the expense of half speed even with flawless media. .br The progress reports issued by some drives while formatting are quite unrealistic. Do not conclude success or failure from the reported percentages. Formatting was successful if no SORRY event or worse occurred. Be patient with apparently frozen progress. .TP \fB\-list_formats\fR Put out a list of format descriptors as reported by the output drive for the current medium. The list gives the index number after "Format idx", a MMC format code, the announced size in blocks (like "2236704s") and the same size in MiB. .br MMC format codes are manifold. Most important are: "00h" general formatting, "01h" increases reserve space for DVD\-RAM, "26h" for DVD+RW, "30h" for BD\-RE with reserve space, "31h" for BD\-RE without reserve space, "32h" for BD\-R. .br Smaller format size with DVD\-RAM, BD\-RE, or BD\-R means more reserve space. .TP \fB\-list_speeds\fR Put out a list of speed values as reported by the drives with the loaded media. The list tells read speeds of the input drive and of the output drive. Further it tells write speeds of the output drive. .br The list of write speeds does not necessarily mean that the medium is writable or that these speeds are actually achievable. Especially the lists reported with empty drive or with ROM media obviously advertise speeds for other media. .br It is not mandatory to use speed values out of the listed range. The drive is supposed to choose a safe speed that is as near to the desired speed as possible. .br At the end of the list, "Write speed L" and "Write speed H" are the best guesses for lower and upper write speed limit. "Write speed l" and "Write speed h" may appear only with CD and eventually override the list of other speed offers. .br Only if the drive reports contradicting speed information there will appear "Write speed 0", which tells the outcome of speed selection by command \-speed 0, if it deviates from "Write speed H". .br "Read speed L" and "Read speed H" tell the minimum and maximum read speeds, as reported by the drive. They would be chosen by \-read_speed "min" or "max" if they undercut or surpass the built\-in limits. These are "1x", "52xCD", "24xDVD", "20xBD". .TP \fB\-list_profiles\fR "in"|"out"|"all" Put out a list of media types supported by \-indev, \-outdev, or both, respectively. The currently recognized type is marked by text "(current)". .TP \fB\-truncate_overwritable\fR entity id adjust On overwritable medium copy the volume descriptors of an existing session to the overall descriptors at LBA 0 ff. This makes all sessions \fBinaccessible\fR which are younger than the activated one. A reason to do this would be read errors in the younger sessions and the wish to re\-write or skip them. .br This operation is only allowed if no changes to the loaded filesystem are pending. If an \-indev is acquired then it is released before the write operation begins and re\-acquired only in case of success. .br The parameters "entity" and "id" have the same meaning as with command \-load. They choose the existing ISO session which shall become the youngest accessible session. Available entity names are "session", "track", "lba", "sbsector", "volid". "auto" makes few sense. id is a number or search text as appropriate for the given entity. .br Parameter "adjust" controls the claimed size of the activated session. Text "new" means the size of the newly activated session as it was before this command. I.e. the space of the then inaccessible younger sessions will be re\-used when appending more sessions. .br "old" means the size up to the end of the previously youngest session. I.e. "old" will not free the space of the then inaccessible younger sessions for re\-use. .br A number preceded by "+" gives the number of bytes to be added to "new". A number without "+" gives the overall number of bytes. In any case the result may not be smaller than "new". Numbers may have a unit suffix: "d"=512, "k"=1024, "s"=2048, "m"=1024k, "g"=1024m. .br Normally the volume descriptors at block 16 ff. have to be readable. Only with entity "lba" or "sbsector" and adjust mode "new" it is possible to address a session if block 16 ff. yields no valid volume descriptors. .br Examples: .br Activate session 4 and enable overwriting of the blocks of younger sessions: .br \-truncate_overwritable session 4 new .br Activate session 4 and claim the blocks of younger sessions as useless part of session 4: .br \-truncate_overwritable session 4 old .br Let session 4 claim additional 500 MiB as useless data: .br \-truncate_overwritable session 4 +500m .TP \fB\-close_damaged\fR "as_needed"|"force" Try to close the upcoming track and session if the drive reported the medium as damaged. This may apply to CD\-R, CD\-RW, DVD\-R, DVD\-RW, DVD+R, DVD+R DL, or BD\-R media. It is indicated by warning messages when the drive gets acquired, and by a remark "but next track is damaged" with the line "Media status :" of command \-toc. .br The setting of command \-close determines whether the medium stays appendable. .br Mode "as_needed" gracefully refuses on media which are not reported as damaged. Mode "force" attempts the close operation even with media which appear undamaged. .br No image changes are allowed to be pending before this command is performed. After closing was attempted, both drives are given up. .TP .B Settings for result writing: .PP Rock Ridge info will be generated by default. ACLs will be written according to the setting of command \-acl. .TP \fB\-joliet\fR "on"|"off" If enabled by "on", generate Joliet tree additional to ISO 9660 + Rock Ridge tree. .TP \fB\-hfsplus\fR "on"|"off" If enabled by "on", generate a HFS+ filesystem inside the ISO 9660 image and mark it by Apple Partition Map (APM) entries in the System Area, the first 32 KiB of the image. .br This may collide with data submitted by \-boot_image system_area=. The first 8 bytes of the System Area get overwritten by { 0x45, 0x52, 0x08 0x00, 0xeb, 0x02, 0xff, 0xff } which can be executed as x86 machine code without negative effects. So if an MBR gets combined with this feature, then its first 8 bytes should contain no essential commands. .br The next blocks of 2 KiB in the System Area will be occupied by APM entries. The first one covers the part of the ISO image before the HFS+ filesystem metadata. The second one marks the range from HFS+ metadata to the end of file content data. If more ISO image data follow, then a third partition entry gets produced. Other features of xorriso might cause the need for more APM entries. .br The HFS+ filesystem is not suitable for add\-on sessions produced by the multi\-session method of growing. An existing ISO image may nevertheless be the base for a new image produced by the method of modifying. If \-hfsplus is enabled when \-indev or \-dev gets executed, then AAIP attributes get loaded from the input image and checked for information about HFS creator, filetype, or blessing. If found, then they get enabled as settings for the next image production. Therefore it is advisable to perform \-hfsplus "on" before \-indev or \-dev. .br Information about HFS creator, type, and blessings gets stored by xorriso if \-hfsplus is enabled at \-commit time. It is stored as copy outside the HFS+ partition, but rather along with the Rock Ridge information. xorriso does not read any information from the HFS+ meta data. .br Be aware that HFS+ is case\-insensitive although it can record file names with upper\-case and lower\-case letters. Therefore, file names from the iso_rr name tree may collide in the HFS+ name tree. In this case they get changed by adding underscore characters and counting numbers. In case of very long names, it might be necessary to map them to "MANGLED_...". .br WARNING: .br The HFS+ implementation in libisofs has a limit of 125,829,120 bytes for the size of the overall directory tree. This suffices for about 300,000 files of normal name length. If the limit gets exceeded, a FAILURE event will be issued and the ISO production will not happen. .TP \fB\-rockridge\fR "on"|"off" Mode "off" disables production of Rock Ridge information for the ISO 9660 file objects. The multi\-session capabilities of xorriso depend much on the naming fidelity of Rock Ridge. So it is strongly discouraged to deviate from default setting "on". .TP \fB\-compliance\fR rule[:rule...] Adjust the compliance to specifications of ISO 9660/ECMA\-119 and its contemporary extensions. In some cases it is worth to deviate a bit in order to circumvent bugs of the intended reader system or to get unofficial extra features. .br There are several adjustable rules which have a keyword each. If they are mentioned with this command then their rule gets added to the relaxation list. This list can be erased by rules "strict" or "clear". It can be reset to its start setting by "default". All of the following relaxation rules can be revoked individually by appending "_off". Like "deep_paths_off". .br Rule keywords are: .br "iso_9660_level="number chooses level 1 with ECMA\-119 names of the form 8.3 and \-file_size_limit <= 4g \- 1, or level 2 with ECMA\-119 names up to length 32 and the same \-file_size_limit, or level 3 with ECMA\-119 names up to length 32 and \-file_size_limit >= 400g \-200k. If necessary \-file_size_limit gets adjusted. .br "allow_dir_id_ext" allows ECMA\-119 names of directories to have a name extension as with other file types. It does not force dots and it omits the version number, though. This is a bad tradition of mkisofs which violates ECMA\-119. Especially ISO level 1 only allows 8 characters in a directory name and not 8.3. .br "omit_version" does not add versions (";1") to ECMA\-119 and Joliet file names. .br "only_iso_version" does not add versions (";1") to Joliet file names. .br "deep_paths" allows ECMA\-119 file paths deeper than 8 levels. .br "long_paths" allows ECMA\-119 file paths longer than 255 characters. .br "long_names" allows up to 37 characters with ECMA\-119 file names. .br "no_force_dots" does not add a dot to ECMA\-119 file names which have none. .br "no_j_force_dots" does not add a dot to Joliet file names which have none. .br "lowercase" allows lowercase characters in ECMA\-119 file names. .br "7bit_ascii" allows nearly all 7\-bit characters in ECMA\-119 file names. Not allowed are 0x0 and '/'. If not "lowercase" is enabled, then lowercase letters get converted to uppercase. .br "full_ascii" allows all 8\-bit characters except 0x0 and '/' in ECMA\-119 file names. .br "untranslated_names" might be dangerous for inadverted reader programs which rely on the restriction to at most 37 characters in ECMA\-119 file names. This rule allows ECMA\-119 file names up to 96 characters with no character conversion. If a file name has more characters, then image production will fail deliberately. .br "untranslated_name_len="number enables untranslated_names with a smaller limit for the length of file names. 0 disables this feature, \-1 chooses maximum length limit, numbers larger than 0 give the desired length limit. .br "joliet_long_names" allows Joliet leaf names up to 103 characters rather than 64. .br "joliet_long_paths" allows Joliet paths longer than 240 characters. .br "joliet_utf16" encodes Joliet names in UTF\-16BE rather than UCS\-2. The difference is with characters which are not present in UCS\-2 and get encoded in UTF\-16 by 2 words of 16 bit each. Both words then stem from a reserved subset of UCS\-2. .br "always_gmt" stores timestamps in GMT representation with timezone 0. .br "rec_mtime" records with non\-RockRidge directory entries the disk file's mtime and not the creation time of the image. This applies to the ECMA\-119 tree (plain ISO 9660), to Joliet, and to ISO 9660:1999. "rec_mtime" is default. If disabled, it gets automatically re\-enabled by \-as mkisofs emulation when a pathspec is encountered. .br "new_rr" uses Rock Ridge version 1.12 (suitable for GNU/Linux but not for older FreeBSD or for Solaris). This implies "aaip_susp_1_10_off" which may be changed by subsequent "aaip_susp_1_10". .br Default is "old_rr" which uses Rock Ridge version 1.10. This implies also "aaip_susp_1_10" which may be changed by subsequent "aaip_susp_1_10_off". .br "aaip_susp_1_10" allows AAIP to be written as unofficial extension of RRIP rather than as official extension under SUSP\-1.12. .br "no_emul_toc" saves 64 kB with the first session on overwritable media but makes the image incapable of displaying its session history. .br "iso_9660_1999" causes the production of an additional directory tree compliant to ISO 9660:1999. It can record long filenames for readers which do not understand Rock Ridge. .br "old_empty" uses the old way of of giving block addresses in the range of [0,31] to files with no own data content. The new way is to have a dedicated block to which all such files will point. .br "max_ce_entries="number sets the maximum number of SUSP CE entries and thus continuation areas. Each continuation area can hold at most 2048 bytes of SUSP data (Rock Ridge or AAIP). The first area can be smaller. There might be some waste at the end of each area. When the maximum number is exceeded during ISO filesystem production then either xattr and ACL get dropped from the affected file or an error gets reported and image production is prevented. .br Linux silently ignores a file when encountering its 32th CE entry. (Workaround is to mount the filesystem with option "norock".) So the default setting is 31. Minimum is 1, maximum is 100000. If a limit higher than 31 is chosen and 31 gets surpassed, then a warning message gets reported. .br "max_ce_drop="mode sets the behavior when the limit of max_ce_entries= is surpassed. Mode "off" causes an error message and prevents image production. Mode "xattr" and "xattr_acl" report a warning, delete from the affected file all xattr of namespaces other than "isofs", and then try again. If this still surpasses the limit, then mode "xattr_acl" deletes all ACL from the file and retries. If this still surpasses the limit, then an error message gets reported and image production is prevented. .br Default setting is .br "clear:iso_9660_level=3:only_iso_version:deep_paths:long_paths: .br no_j_force_dots:always_gmt:rec_mtime:old_rr:max_ce_entries=31: .br max_ce_drop=xattr_acl" .br Note: The term "ECMA\-119 name" means the plain ISO 9660 names and attributes which get visible if the reader ignores Rock Ridge. .TP \fB\-rr_reloc_dir\fR name Specify the name of the relocation directory in which deep directory subtrees shall be placed if \-compliance is set to "deep_paths_off" or "long_paths_off". A deep directory is one that has a chain of 8 parent directories (including root) above itself, or one that contains a file with an ECMA\-119 path of more than 255 characters. .br The overall directory tree will appear originally deep when interpreted as Rock Ridge tree. It will appear as re\-arranged if only ECMA\-119 information is considered. .br The default relocation directory is the root directory. By giving a non\-empty name with \-rr_reloc_dir, a directory in the root directory may get this role. If that directory does not already exist at \-commit time, then it will get created and marked for Rock Ridge as relocation artefact. At least on GNU/Linux it will not be displayed in mounted Rock Ridge images. .br The name must not contain a '/' character and must not be longer than 255 bytes. .TP \fB\-volid\fR text Specify the volume ID, which most operating systems will consider to be the volume name of the image or medium. .br \fBxorriso\fR accepts any text up to 32 characters, but according to rarely obeyed specs stricter rules apply: .br ECMA\-119 demands ASCII characters out of [A\-Z0\-9_]. Like: .br "IMAGE_23" .br Joliet allows 16 UCS\-2 characters. Like: .br "Windows name" .br Be aware that the volume id might get used automatically as the name of the mount point when the medium is inserted into a playful computer system. .br If an ISO image gets loaded while the volume ID is set to default "ISOIMAGE" or to "", then the volume ID of the loaded image will become the effective volume id for the next write run. But as soon as command \-volid is performed afterwards, this pending ID is overridden by the new setting. .br Consider this when setting \-volid "ISOIMAGE" before executing \-dev, \-indev, or \-rollback. If you insist in \-volid "ISOIMAGE", set it again after those commands. .TP \fB\-volset_id\fR text Set the volume set ID string to be written with the next \-commit. Permissible are up to 128 characters. This setting gets overridden by image loading. .TP \fB\-publisher\fR text Set the publisher ID string to be written with the next \-commit. This may identify the person or organisation who specified what shall be recorded. Permissible are up to 128 characters. This setting gets overridden by image loading. .TP \fB\-application_id\fR text Set the application ID string to be written with the next \-commit. This may identify the specification of how the data are recorded. Permissible are up to 128 characters. This setting gets overridden by image loading. .br The special text "@xorriso@" gets converted to the ID string of \fBxorriso\fR which is normally written as \-preparer_id. It is a wrong tradition to write the program ID as \-application_id. .TP \fB\-system_id\fR text Set the system ID string to be written with the next \-commit. This may identify the system which can recognize and act upon the content of the System Area in image blocks 0 to 15. Permissible are up to 32 characters. This setting gets overridden by image loading. .TP \fB\-volume_date\fR type timestring Set one of the four overall timestamps for subsequent image writing. Available types are: .br "c" time when the volume was created. .br "m" time when volume was last modified. .br "x" time when the information in the volume expires. .br "f" time since when the volume is effectively valid. .br "all_file_dates" sets mtime, atime, and ctime of all files and directories to the given time. If the timestring is "set_to_mtime", then the atime and ctime of each file and directory get set to the value found in their mtime. .br These actions stay delayed until actual ISO production begins. Up to then they can be revoked by "all_file_dates" with empty timestring or timestring "default". .br The timestamps of the El Torito boot catalog file get refreshed when the ISO is produced. They can be influenced by "uuid". .br "uuid" sets a timestring that overrides "c" and "m" times literally and sets the time of the El Torito boot catalog. It must consist of 16 decimal digits which form YYYYMMDDhhmmsscc, with YYYY between 1970 and 2999. Time zone is GMT. It is supposed to match this GRUB line: .br search \-\-fs\-uuid \-\-set YYYY\-MM\-DD\-hh\-mm\-ss\-cc .br E.g. 2010040711405800 is 7 Apr 2010 11:40:58 (+0 centiseconds). .br Timestrings for the other types may be given as with command \-alter_date. Some of them are prone to timezone computations. The timestrings "default" or "overridden" cause default settings: "c" and "m" will show the current time of image creation. "x" and "f" will be marked as insignificant. "uuid" will be deactivated. .br At \-commit time, some timestamps get set to the maximum value of effectively written volume creation and modification time: El Torito boot catalog, HFS+ superblock, ECMA\-119 file modification time if \-compliance "no_rec_mtime". The isohybrid MBR id is computed from "uuid" if given, else from the effective volume modification date. .TP \fB\-copyright_file\fR text Set the copyright file name to be written with the next \-commit. This should be the ISO 9660 path of a file in the image which contains a copyright statement. Permissible are up to 37 characters. This setting gets overridden by image loading. .TP \fB\-abstract_file\fR text Set the abstract file name to be written with the next \-commit. This should be the ISO 9660 path of a file in the image which contains an abstract statement about the image content. Permissible are up to 37 characters. This setting gets overridden by image loading. .TP \fB\-biblio_file\fR text Set the biblio file name to be written with the next \-commit. This should be the ISO 9660 path of a file in the image which contains bibliographic records. Permissible are up to 37 characters. This setting gets overridden by image loading. .TP \fB\-preparer_id\fR text Set the preparer ID string to be written with the next \-commit. This may identify the person or other entity which controls the preparation of the data which shall be recorded. Normally this should be the ID of \fBxorriso\fR and not of the person or program which operates \fBxorriso\fR. Please avoid to change it. Permissible are up to 128 characters. .br The special text "@xorriso@" gets converted to the ID string of \fBxorriso\fR which is default at program startup. .br Unlike other ID strings, this setting is not influenced by image loading. .TP \fB\-application_use\fR character|0xXY|disk_path Specify the content of the Application Use field which can take at most 512 bytes. .br If the parameter of this command is empty, then the field is filled with 512 0\-bytes. If it is a single character, then it gets repeated 512 times. If it begins by "0x" followed by two hex digits [0\-9a\-fA\-F], then the digits are read as byte value which gets repeated 512 times. .br Any other parameter text is used as disk_path to open a data file and to read up to 512 bytes from it. If the file is smaller than 512 bytes, then the remaining bytes in the field get set to binary 0. .br This setting is not influenced by image loading. .TP \fB\-out_charset\fR character_set_name Set the character set to which file names get converted when writing an image. See paragraph "Character sets" for more explanations. When loading the written image after \-commit the setting of \-out_charset will be copied to \-in_charset. .TP \fB\-uid\fR uid User id to be used for all files when the new ISO tree gets written to media. .TP \fB\-gid\fR gid Group id to be used for all files when the new ISO tree gets written to media. .TP \fB\-zisofs\fR parameter[:parameters] Set global parameters for zisofs compression. This data format is recognized and transparently uncompressed by some Linux kernels. It is to be applied via command \-set_filter with built\-in filter "\-\-zisofs". .br Note: This command is only permitted while no \-\-zisofs filters are applied to any files. .br Parameters are: .br "level="[0\-9] zlib compression: 0=none, 1=fast,..., 9=slow .br "block_size="32k|64k|128k sets the size of version 1 compression blocks. .br "by_magic=on" enables an expensive test at image generation time which checks files from disk whether they already are zisofs compressed, e.g. by program mkzftree. "by_magic=v2" enables processing of already zisofs2 compressed files additionally to those of zisofs version 1. "by_magic=off" disables both. .br "version_2="off|as_needed|on controls compression by experimental version zisofs2 which can encode files of size 4 GiB or larger. The Linux kernel (as of 5.9) does not yet know this format and will complain like .br isofs: Unknown ZF compression algorithm: PZ .br The files will then appear in their compressed form with zisofs2 header, block pointer list, and compressed data. .br zisofs2 is recognized by xorriso in files from loaded images and gets equipped with \-\-zisofs\-decode filters, unless restrictions on the number of block pointers prevent this. .br Mode "off" restricts compression to files smaller than 4 GiB uncompressed size. Mode "as_needed" uses zisofs2 for larger files. Mode "on" uses zisofs2 for all zisofs compressed files. .br "susp_z2="off|on controls production of SUSP entries "Z2" instead of "ZF" with zisofs2 compressed files. Unaware Linux kernels are supposed to silently ignore "Z2" entries. .br "block_size_v2="32k|64k|128k|256k|512k|1m sets the size of compression blocks for zisofs2. .br "bpt_target="\-1|>0 sets a number of block pointers per file, which is considered low enough to justify a reduction of block size. If this number is larger than 0, then block sizes smaller than the settings of block_size= or block_size_v2= are tried whether they yield not more block pointers than the given number. If so, the smallest suitable block size is applied. .br The inavoidable final block pointer counts. E.g. a file of 55 KiB has 3 block pointers if block size is 32k, and 2 block pointers with block size 64k. .br bpt_target=\-1 disables this automatic block size adjustment. .br "max_bpt="1k...128g sets the limit for the overall allocated block pointer memory. Block pointers occupy virtual memory while a file gets uncompressed and while a file, which shall be compressed, waits for ISO filesystem creation. .br One pointer occupies 8 bytes of memory and governs block_size or block_size_v2 uncompressed bytes. I.e. with block size 128k, 1m of block pointer memory suffices for at most 16g of uncompressed file size. Each file consumes one end block pointer, independently of the file size. Partially filled end blocks may further reduce the effective payload. .br In case of overflow of the max_bpt limit while adding compression filters the program tries to go on by discarding all buffered block pointers of previously added \-\-zisofs filters. From then on all newly added filters will discard their block pointers immediately after being added. Discarded block pointers cause an additional read and compression run of the input file during the production of the ISO filesystem. .br "max_bpt_f="1k...128g sets the limit for the memory size of the block pointer list of a single file. max_bpt_f is never larger than max_bpt. If either is set to violate this rule, the other gets set to the same value. If both values are the same before a change by max_bpt= or max_bpt_f=, then both limits stick together unless the limit is decreased by max_bpt_f=. .br "bpt_free_ratio="\-1|0.0...1.0 sets a threshold for switching to block pointer discarding during compression. If less than the given fraction of the max_bpt_f= memory is free, then block pointers of compression filters get discarded immediately after being added. Value \-1 disables this feature. .br "default" is the same as "level=6:block_size=32k:by_magic=off: version_2=off:block_size_v2=128k:susp_z2=off:max_bpt=256m:max_bpt_f=256m: bpt_free_ratio=\-1". .TP \fB\-speed\fR code|number[k|m|c|d|b] Set the burn speed. Default is "max" (or "0") = maximum speed as announced by the drive. Further special speed codes are: .br "min" (or "\-1") selects minimum speed as announced by the drive. .br "none" avoids to send a speed setting command to the drive before burning begins. .br Speed can be given in media dependent numbers or as a desired throughput per second in MMC compliant kB (= 1000) or MB (= 1000 kB). Media x\-speed factor can be set explicitly by "c" for CD, "d" for DVD, "b" for BD, "x" is optional. .br Example speeds: .br 706k = 706kB/s = 4c = 4xCD .br 5540k = 5540kB/s = 4d = 4xDVD .br If there is no hint about the speed unit attached, then the medium in the \-outdev will decide. Default unit is CD = 176.4k. .br MMC drives usually activate their own idea of speed and take the speed value given by the burn program only as upper limit for their own decision. .TP \fB\-stream_recording\fR "on"|"off"|"full"|"data"|number Setting "on" tries to circumvent the management of defects on DVD\-RAM, BD\-RE, or BD\-R. Defect management keeps partly damaged media usable. But it reduces write speed to half nominal speed even if the medium is in perfect shape. For the case of flawless media, one may use \-stream_recording "on" to get full speed. .br "full" tries full speed with all write operations, whereas "on" does this only above byte address 32s. One may give a number of at least 16s in order to set an own address limit. .br "data" causes full speed to start when superblock and directory entries are written and writing of file content blocks begins. .TP \fB\-dvd_obs\fR "default"|"32k"|"64k"|"obs_pad"|"bdr_obs_exempt" GNU/Linux specific: Set the number of bytes to be transmitted with each write operation to DVD or BD media. A number of 64 KB may improve throughput with bus systems which show latency problems. The default depends on media type, on command \-stream_recording , and on compile time options. .br On all systems: "obs_pad" pads the data of the last write operation of a DVD\-R[W] DAO session or BD\-R session up to the full size of an output chunk. This padding has to be applied automatically to the other DVD and BD media types, where it causes e.g. ISO images to have trailing unclaimed blocks. Whether it is applied automatically to BD\-R depends on "bdr_obs_exempt". "obs_pad" can be disabled by "no_obs_pad". .br "bdr_obs_exempt" exempts BD\-R media from automatic unconditional transaction end padding, provided that this padding is not requested by "obs_pad" and that no stream_recording is requested. "bdr_obs_exempt" can be disabled by "no_obs_exempt". .br This is a new feature introduced with version 1.5.6. It might become default in later versions. .TP \fB\-modesty_on_drive\fR parameter[:parameters] Control whether the drive buffer shall be kept from getting completely filled. Parameter "on" (or "1") keeps the program from trying to write to the burner drive while its buffer is in danger to be filled over a given limit. If this limit is exceeded then the program will wait until the filling reaches a given low percentage value. .br This can ease the load on operating system and drive controller and thus help with achieving better input bandwidth if disk and burner are not on independent controllers (like hda and hdb). It may also help with throughput problems of simultaneous burns on different burners with Linux kernels like 3.16, if one has reason not to fix the problem by \-scsi_dev_family "sg". On the other hand it increases the risk of buffer underflow and thus reduced write speed. .br Some burners are not suitable because they report buffer fill with granularity too coarse in size or time, or expect their buffer to be filled to the top before they go to full speed. .br Parameters "off" or "0" disable this feature. .br The threshold for beginning to wait is given by parameter "max_percent=". Parameter "min_percent=" defines the threshold for resuming transmission. Percentages are permissible in the range of 25 to 100. Numbers in this range without a prepended name are interpreted as "on:min_percent=". .br E.g.: \-modesty_on_drive 75 .br The optimal values depend on the buffer behavior of the drive. .br Parameter "timeout_sec=" defines after which time of unsuccessful waiting the modesty shall be disabled because it does not work. .br Parameter "min_usec=" defines the initial sleeping period in microseconds. If the drive buffer appears to be too full for sending more data, the program will wait the given time and inquire the buffer fill state again. If repeated inquiry shows not enough free space, the sleep time will slowly be increased to what parameter "max_usec=" defines. .br Parameters, which are not mentioned with a \-modesty_on_drive command, stay unchanged. Default is: .br \-modesty_on_drive off:min_percent=90:max_percent=95: timeout_sec=120:min_usec=5000:max_usec=25000 .TP \fB\-use_immed_bit\fR "on"|"off"|"default" Control whether several long lasting SCSI commands shall be executed with the Immed bit, which makes the commands end early while the drive operation is still going on. xorriso then inquires progress indication until the drive reports to be ready again. If this feature is turned off, then blanking and formatting will show no progress indication. .br It may depend on the operating system whether \-use_immed_bit is set to "off" by default. Command \-status will tell by appending "/on" or "/off" if a drive has already been acquired and \-use_immed_bit is currently set to "default". Command \-use_immed_bit tolerates and ignores such appended text. .TP \fB\-stdio_sync\fR "on"|"off"|"end"|number Set the number of bytes after which to force output to stdio: pseudo drives. This forcing keeps the memory from being clogged with lots of pending data for slow devices. Default "on" is the same as "16m". Forced output can be disabled by "off", or be delayed by "end" until all data are produced. If a number is chosen, then it must be at least 64k. .TP \fB\-dummy\fR "on"|"off" If "on" then simulate burning or refuse with FAILURE event if no simulation is possible, do neither blank nor format. .TP \fB\-fs\fR number["k"|"m"] Set the size of the fifo buffer which smoothens the data stream from ISO image generation to media burning. Default is 4 MiB, minimum 64 kiB, maximum 1 GiB. The number may be followed by letter "k" or "m" which means unit is kiB (= 1024) or MiB (= 1024 kiB). .TP \fB\-close\fR "on"|"off"|"as_needed" If \-close is set to "on" then mark the written medium as not appendable any more. This will have no effect on overwritable media types. Setting "on" is the contrary of cdrecord option \-multi, and is one aspect of growisofs option \-dvd\-compat. .br If set to "off" then keep the medium writable for an appended session. .br If set to "as_needed" then use "on" only if "off" is predicted to fail with the given medium and its state. .br Not all drives correctly recognize fast\-blanked DVD\-RW which need "on". If there is well founded suspicion that a burn run failed due to \-close "off", then \-close "as_needed" causes a re\-try with "on". .br Note that emulation command \-as "cdrecord" temporarily overrides the current setting of \-close by its own default \-close "on" if its option \-multi is missing. .TP \fB\-write_type\fR "auto"|"tao"|"sao/dao" Set the write type for the next burn run. "auto" will select SAO with blank CD media, DAO with blank DVD\-R[W] if \-close is "on", and elsewise CD TAO or the equivalent write type of the particular DVD/BD media. Choosing TAO or SAO/DAO explicitly might cause the burn run to fail if the desired write type is not possible with the given media state. .TP \fB\-padding\fR number["k"|"m"]|"included"|"appended" Append the given number of extra bytes to the image stream. This is a traditional remedy for a traditional bug in block device read drivers. Needed only for CD recordings in TAO mode. Since one can hardly predict on what media an image might end up, \fBxorriso\fR adds the traditional 300k of padding by default to all images. .br For images which will never get to a CD it is safe to use \-padding 0 . .br Normally padding is not written as part of the ISO image but appended after the image end. This is \-padding mode "appended". .br Emulation command \-as "mkisofs" and command \-jigdo cause padding to be written as part of the image. The same effect is achieved by \-padding mode "included". .TP .B Bootable ISO images: .PP Contrary to published specifications many BIOSes will load an El Torito record from the first session on media and not from the last one, which gets mounted by default. This makes no problems with overwritable media, because they appear to inadverted readers as one single session. .br But with multi\-session media CD\-R[W], DVD\-R[W], DVD+R, it implies that the whole bootable system has to reside already in the first session and that the last session still has to bear all files which the booted system expects after mounting the ISO image. .br If a boot image from ISOLINUX or GRUB is known to be present on media then it is advised to patch it when a follow\-up session gets written. But one should not rely on the capability to influence the bootability of the existing sessions, unless one can assume overwritable media. .br Normally the boot images are data files inside the ISO filesystem. By special path "\-\-interval:appended_partition_NNN:all::" it is possible to refer to an appended partition. The number NNN gives the partition number as used with the corresponding command \-append_partition. E.g.: .br \-append_partition 2 0xef /tmp/efi.img \-boot_image any efi_path=\-\-interval:appended_partition_2:all:: .br There are booting mechanisms which do not use an El Torito record but rather start at the first bytes of the image: PC\-BIOS MBR or EFI GPT for hard\-disk\-like devices, APM partition entries for Macs which expect HFS+ boot images, MIPS Volume Header for old SGI computers, DEC Boot Block for old MIPS DECstation, SUN Disk Label for SPARC machines, HP\-PA boot sector for HP PA\-RISC machines, DEC Alpha SRM boot sector for old DEC Alpha machines. .PP Several of the following commands expect disk paths as input but also accept description strings for the libisofs interval reader, which is able to cut out data from disk files or \-indev and to zeroize parts of the content: command \-append_partition, boot specs system_area=, grub2_mbr=, prep_boot_part=, efi_boot_part=. .br The description string consists of the following components, separated by colon ':' .br "\-\-interval:"Flags":"Interval":"Zeroizers":"Source .br The component "\-\-interval" states that this is not a plain disk path but rather an interval reader description string. The component Flags modifies the further interpretation: .br "local_fs" demands to read from a file depicted by the path in Source. .br "imported_iso" demands to read from the \-indev. This works only if \-outdev is not the same as \-indev. The Source component is ignored. .br "appended_partition_NNN" with a decimal number NNN works only for \-boot_image bootspecs which announce El Torito boot image paths: bin_path=, efi_path=. The number gives the partition number as used with the corresponding command \-append_partition. .br The component Interval consists of two byte address numbers separated by a "\-" character. E.g. "0\-429" means to read bytes 0 to 429. .br The component Zeroizers consists of zero or more comma separated strings. They define which part of the read data to zeroize. Byte number 0 means the byte read from the Interval start address. Each string may be one of: .br "zero_mbrpt" demands to zeroize the MBR partition table if bytes 510 and 511 bear the MBR signature 0x55 0xaa. .br "zero_gpt" demands to check for a GPT header in bytes 512 to 1023, to zeroize it and its partition table blocks. .br "zero_apm" demands to check for an APM block 0 and to zeroize its partition table blocks. .br Start_byte"\-"End_byte demands to zeroize the read\-in bytes beginning with number Start_byte and ending after End_byte. .br The component Source is the file path with flag "local_fs", and ignored with flag "imported_iso". .br Byte numbers may be scaled by a suffix out of {k,m,g,t,s,d} meaning multiplication by {1024, 1024k, 1024m, 1024g, 2048, 512}. A scaled value end number depicts the last byte of the scaled range. .br E.g. "0d\-0d" is "0\-511". .br Examples: .br "local_fs:0\-32767:zero_mbrpt,zero_gpt,440\-443:/tmp/template.iso" .br "imported_iso:45056d\-47103d::" .br .TP \fB\-boot_image\fR "any"|"isolinux"|"grub" .br "discard"|"keep"|"patch"|"replay"|"show_status"| bootspec|"next" .br Define the equipment of the emerging filesystem with boot entry points. .br With systems which boot via BIOS or EFI this is a set of El Torito boot images, possibly MBR boot code, and possibly partition tables of type MBR, GPT, or APM. Such file sets get produced by boot loader systems like ISOLINUX or GRUB. .br Each \-boot_image command has two parameters: type and setting. More than one \-boot_image command may be used to define the handling of one or more boot images. Sequence matters. .br Types \fBisolinux\fR and \fBgrub\fR care for known peculiarities. Type \fBany\fR makes no assumptions about the origin of the boot images. .br When loading an ISO filesystem, system area and El Torito boot images get loaded, too. The default behavior is not to write loaded El Torito boot images and to write the loaded system area content without alterations. .br \fBdiscard\fR gives up the El Torito boot catalog and its boot images. regardless whether loaded from an ISO filesystem or defined by commands. Any BIOS or EFI related boot options get revoked. Nevertheless, loaded system area data and the possibly defined appended partitions stay valid. If desired, they have to be erased by .br \-boot_image any system_area=/dev/zero .br \-append_partition all revoke \- .br \fBkeep\fR keeps or copies El Torito boot images unaltered and writes a new catalog. .br \fBpatch\fR applies patching to existing El Torito boot images if they seem to bear a boot info table. .br A boot info table needs to be patched when the boot image gets newly introduced into the ISO image or if an existing image gets relocated. This is automatically done if type "isolinux" or "grub" is given, but not with "any". .br If patching is enabled, then boot images from previous sessions will be checked whether they seem to bear a boot info table. If not, then they stay unpatched. This check is not infallible. So if you do know that the images need no patching, use "any" "keep". "grub" "patch" will not patch EFI images (platform_id=0xef). .br \fBreplay\fR is a more modern version of "patch", which not only cares for existing El Torito boot equipment but also for the recognizable boot provisions in the System Area. It discards any existing \-boot_image setting including the system area and executes the commands proposed by command \-report_el_torito "cmd". .br Special care has to be taken with manipulations of files in the emerging ISO filesystem which are mentioned by \-report_el_torito "cmd". Their paths are memorized at ISO image load time. In general \-boot_image "any" "replay" should be applied after all file manipulations are done. All file paths from the \-report_el_torito commands must then still lead to data files which are suitable for their respective commands. .br The effects of file path changes after \-boot_image "any" "replay" can be surprising. E.g. removing or replacing a boot image with boot info table for legacy BIOS leads to a hidden boot image with the content as it was at "replay" time. Doing the same with a boot image for EFI leads to an error at \-commit time or to the new file content becomming the EFI boot image. .br Out of historical reasons \fBreplay\fR does not revoke all possibly made \-append_partition settings but only overwrites those for which the loaded ISO image provides candidates. .br \fBshow_status\fR will print what is known about the loaded boot images and their designated fate. .br Examples: .br Drop El Torito: .br \-boot_image any discard .br Drop El Torito, system area, appended partitions: .br \-boot_image any discard .br \-boot_image any system_area=/dev/zero .br \-append_partition all revoke \- .br Maintain recognizable stuff after revoking possibly made \-append_partition settings to surely get only the partitions from the loaded ISO: .br \-append_partition all revoke \- .br \-boot_image any replay .br Re\-adjust El Torito only for GRUB: .br \-boot_image grub patch .br Re\-adjust El Torito only for ISOLINUX: .br \-boot_image isolinux patch .br A \fBbootspec\fR is a word of the form name=value. It is used to describe the parameters of a boot feature. The names "dir", "bin_path", "efi_path" lead to El Torito bootable images. Name "system_area" activates a given file as MBR or other disk header. .br On all media types this is possible within the first session. In further sessions an existing boot image can get replaced by a new one, but depending on the media type this may have few effect at boot time. See above. .br El Torito boot images have to be added to the ISO image by normal means (image loading, \-map, \-add, ...). In case of ISOLINUX the files should reside either in ISO image directory /isolinux or in /boot/isolinux . In that case it suffices to use as bootspec the text "\fBdir=/isolinux\fR" or "dir=/boot/isolinux". E.g.: .br \-boot_image isolinux dir=/boot/isolinux .br which bundles these individual settings: .br \-boot_image isolinux bin_path=/boot/isolinux/isolinux.bin .br \-boot_image isolinux cat_path=/boot/isolinux/boot.cat .br \-boot_image isolinux load_size=2048 .br \-boot_image any boot_info_table=on .br An El Torito boot catalog file gets inserted into the ISO image with address \fBcat_path=\fR with the first \-boot_image "any" "next" or at \-commit time. It is subject to normal \-overwrite and \-reassure processing if there is already a file with the same name. The catalog lists the boot images and is read by the boot facility to choose one of the boot images. But it is not necessary that it appears in the directory tree at all. One may hide it in all trees by \fBcat_hidden=on\fR. Other possible values are "iso_rr", "joliet", "hfsplus", and the default "off". The timestamps of the boot catalog file are refreshed at commit time. Command \-volume_date "uuid" can be used to set their value. .br \fBbin_path=\fR depicts an El Torito boot image file, a binary program which is to be started by the hardware boot facility (e.g. the BIOS) at boot time. .br \fBefi_path=\fR depicts an El Torito boot image file that is ready for EFI booting. This is normally a FAT filesystem image not larger than 65535 blocks of 512 bytes (= 32 MiB \- 512). Its load_size is determined automatically, no boot info table gets written, no boot medium gets emulated, platform_id is 0xef. .br \fBemul_type=\fR can be one of "no_emulation", "hard_disk", "diskette". It controls the boot medium emulation code of a boot image. The default "no_emulation" is suitable for ISOLINUX, GRUB, FreeBSD cdboot. .br \fBload_size=\fR is a value which depends on the boot image. Default is 2048 which matches the expectations of most boot images. The special value "full" means the full size of the boot image file rounded up to a multiple of 2048 bytes. Maximum is 33,552,384 bytes. .br \fBboot_info_table=on\fR causes address patching to bytes 8 to 63 of the boot image which is given by "any" "bin_path=". "boot_info_table=off" disables this patching. .br \fBgrub2_boot_info=on\fR causes address patching to byte 2548 of the boot image which is given by "any" "bin_path=". The address is written as 64 bit little\-endian number. It is the 2KB block address of the boot image content, multiplied by 4, and then incremented by 5. "grub2_boot_info=off" disables this patching. .br \fBplatform_id=\fR defines by a hexadecimal or decimal number the Platform ID of the boot image. "0x00" is 80x86 PC\-BIOS, "0x01" is PowerPC, "0x02" is Mac, "0xef" is EFI (decimal "239"). .br \fBid_string=\fRtext|56_hexdigits defines the ID string of the boot catalog section where the boot image will be listed. If the value consists of 56 characters [0\-9A\-Fa\-f] then it is converted into 28 bytes, else the first 28 characters become the ID string. The ID string of the first boot image becomes the overall catalog ID. It is limited to 24 characters. Other id_strings become section IDs. .br \fBsel_crit=\fRhexdigits defines the Selection Criteria of the boot image. Up to 20 bytes get read from the given characters [0\-9A\-Fa\-f]. They get attributed to the boot image entry in the catalog. .br \fBnext\fR ends the definition of a boot image and starts a new one. Any following \-bootimage bootspecs will affect the new image. The first "next" discards loaded boot images and their catalog. .br \fBsystem_area=\fRdisk_path copies at most 32768 bytes from the given disk file to the very start of the ISO image. This System Area is reserved for system dependent boot software, e.g. an MBR which can be used to boot from USB stick or hard disk. .br Other than an El Torito boot image, the file disk_path needs not to be added to the ISO image. .br In multi\-session situations the existing System Area is preserved by default. In in this case, the special disk_path "." prevents reading of a disk file but nevertheless causes adjustments in the loaded system area data. Such adjustments may get ordered by \-boot_image commands. .br Special "system_area=/dev/zero" causes 32k of 0\-bytes. Use this to e.g. discard partition tables which were loaded with the ISO image. .br This setting is the default with the write method of Modifying, when \-indev and \-outdev are both used and not the same drive. If you indeed need to copy the unchanged system area from \-indev to \-outdev, use "system_area=\-\-interval:imported_iso:0s\-15s::" , which was the default in older versions of xorriso. .br \fB\-boot_image isolinux system_area=\fR implies "partition_table=on". In this case, the disk path should lead to one of the SYSLINUX files isohdp[fp]x*.bin or to a file which was derived from one of those files. E.g. to the first 512 bytes from an ISOLINUX isohybrid ISO image. .br El Torito boot images (dir=, bin_path=, efi_path=) may then be augmented by \fBisolinux partition_entry=gpt_basdat\fR or \fBisolinux partition_entry=gpt_hfsplus\fR, and by \fBisolinux partition_entry=apm_hfsplus\fR. The boot image will then be mentioned in an invalid GPT as Basic Data or GPT HFS+ partition, and in a valid APM as HFS+ partition. The first three GPT partitions will also be marked by MBR partitions. The MBR partition of type 0xEF is what actually is used by EFI firmware for booting from USB stick. .br \fB\-boot_image any gpt_disk_guid=\fRvalue controls whether an emerging GPT shall get a randomly generated disk GUID or whether the GUID is supplied by the user. Value "random" is default. Value "volume_date_uuid" produces a low quality GUID from the value set by \-volume_date "uuid". .br A string of 32 hex digits, or a RFC 4122 compliant GUID string may be used to set the disk GUID directly. UEFI prescribes the first three components of a RFC 4122 GUID string to be byte\-swapped in the binary representation: .br E.g. gpt_disk_guid=2303cd2a\-73c7\-424a\-a298\-25632da7f446 equals gpt_disk_guid=2acd0323c7734a42a29825632da7f446 .br The partition GUIDs get generated by minimally varying the disk GUID. .br \fB\-boot_image any part_like_isohybrid=on\fR enables \-boot_image isolinux partition_entry= even if no \-boot_image isolinux system_area= is given. No MBR partition of type 0xee emerges, even if GPT gets produced. Gaps between GPT and APM partitions will not be filled by more partitions. Appended partitions get mentioned in APM if other APM partitions emerge. .br \fB\-boot_image any iso_mbr_part_type=\fRnumber sets the partition type of the MBR partition which represents the ISO or at least protects it. .br Number may be 0x00 to 0xff. The text "default" re\-enables the default types of the various occasions to create an ISO MBR partition. This is without effect if no such partition emerges by other settings or if the partition type is prescribed mandatorily like 0xee for GPT protective MBR or 0x96 for CHRP. .br If instead a type_guid is given by a 32\-digit hex string like a2a0d0ebe5b9334487c068b6b72699c7 or by a structured text like EBD0A0A2\-B9E5\-4433\-87C0\-68B6B72699C7, then it will be used as partition type if the ISO filesystem appears as partition in GPT. In MBR, C12A7328\-F81F\-11D2\-BA4B\-00A0C93EC93B will be mapped to 0xef. Any other GUID will be mapped to 0x83. .br \fBgrub2_mbr=\fRdisk_path works like "any" system_area= with additional patching for modern GRUB MBRs. The content start address of the first boot image is converted to a count of 512 byte blocks, and an offset of 4 is added. The result is written as 64 bit little\-endian number to byte address 0x1b0. .br This feature can be revoked either by grub2_mbr= with empty disk path, or by submitting a disk_path via system_area=. .br \fBpartition_table=on\fR causes a simple partition table to be written into bytes 446 to 511 of the System Area. .br With type "isolinux" it shows a partition that begins at byte 0 and it causes the LBA of the first boot image to be written into the MBR. For the first session this works only if also "system_area=" and "bin_path=" or "dir=" is given. .br With types "any" and "grub" it shows a single partition which starts at byte 512 and ends where the ISO image ends. This works with or without system_area= or boot image. .br Bootspecs chrp_boot_part=, prep_boot_part=, and efi_boot_part= overwrite this entry in the MBR partition table. .br If types "isolinux" or "grub" are set to "patch", then "partition_table=on" is activated without new boot image. In this case the existing System Area gets checked whether it bears addresses and sizes as if it had been processed by "partition_table=on". If so, then those parameters get updated when the new System Area is written. .br \fBappended_part_as=gpt\fR marks partitions from \-append_partition in GPT rather than in MBR. In this case the MBR shows a single partition of type 0xee which covers the whole output data. The number of appendable partitions with GPT is 8 rather than 4 with MBR. .br \fBappended_part_as=mbr\fR is the default. Appended partitions get marked in GPT only if GPT is produced because of other settings. If given explicitly, this clears setting "gpt" and "apm". Nevertheless "apm" may be added to "mbr". .br \fBappended_part_as=apm\fR marks partitions from \-append_partition in APM additionally to "mbr" or "gpt". The partition number in APM will not be influenced by \-append_partition parameter partition_number. .br By default, appended partitions get marked in APM only if APM is produced because of other options together with part_like_isohybrid="on". .br \fBchrp_boot_part=on\fR causes a single partition in MBR which covers the whole ISO image and has type 0x96. This is not compatible with any other feature that produces MBR partition entries. It makes GPT unrecognizable. .br \fBprep_boot_part=\fRdisk_path inserts the content of a data file into the image and marks it by an MBR partition of type 0x41. The parts of the ISO image before and after this partition will be covered by further MBR partitions. The data file is supposed to contain ELF executable code. .br \fBefi_boot_part=\fRdisk_path inserts the content of a data file into the image and marks it by a GPT partition. If not chrp_boot_part=on, then the first partition in MBR will have type 0xee to announce the presence of GPT. The data file is supposed to contain a FAT filesystem. .br Instead of a disk_path, the word \-\-efi\-boot\-image may be given. It exposes in GPT the content of the first El Torito EFI boot image as EFI system partition. EFI boot images are introduced by bootspec efi_path=. The affected EFI boot image cannot show up in HFS+ because it is stored outside the HFS+ partition. .br \fBpartition_offset=\fR2kb_block_adr causes a partition table with a single partition that begins at the given block address. This is counted in 2048 byte blocks, not in 512 byte blocks. If the block address is non\-zero then it must be at least 16. A non\-zero partition offset causes two superblocks to be generated and two sets of directory trees. The image is then mountable from its absolute start as well as from the partition start. .br The offset value of an ISO image gets preserved when a new session is added. So the value defined here is only in effect if a new ISO image gets written. .br \fBpartition_hd_cyl=\fRnumber gives the number of heads per cylinder for the partition table. 0 chooses a default value. Maximum is 255. .br \fBpartition_sec_hd=\fRnumber gives the number of sectors per head for the partition table. 0 chooses a default value. Maximum is 63. .br The product partition_sec_hd * partition_hd_cyl * 512 is the cylinder size. It should be divisible by 2048 in order to make exact alignment possible. With appended partitions and "appended_part_as=gpt" there is no limit for the number of cylinders. Else there may be at most 1024 of them. If the cylinder size is too small to stay below the limit, then appropriate values of partition_hd_cyl are chosen with partition_sec_hd 32 or 63. If the image is larger than 8,422,686,720 bytes, then the cylinder size constraints cannot be fulfilled for MBR. .br \fBpartition_cyl_align=\fRmode controls image size alignment to an integer number of cylinders. It is prescribed by isohybrid specs and it seems to please program fdisk. Cylinder size must be divisible by 2048. Images larger than 8,323,596,288 bytes cannot be aligned in MBR partition table. .br Mode "auto" is default. Alignment by padding happens only with "isolinux" "partition_table=on". .br Mode "on" causes alignment by padding with "partition_table=on" for any type. Mode "all" is like "on" but also pads up partitions from \-append_partition to an aligned size. .br Mode "off" disables alignment for any type. .br \fBmbr_force_bootable=\fRmode enforces an MBR partition with "bootable/active" flag if options like partition_table= or grub2_mbr= indicate production of a bootable MBR. These options normally cause the flag to be set if there is an MBR partition of type other than 0xee or 0xef. If no such partition exists, then no bootflag is set, unless mbr_force_bootable="on" forces creation of a dummy partition of type 0x00 which covers only the first block of the ISO image. .br If no bootable MBR is indicated and a partition gets created by \-append_partition, then mbr_force_bootable="on" causes a bootflag like it would do with a bootable MBR. .br \fBgpt_iso_bootable=\fRon causes bit 2 of the GPT partition flags to be set for the ISO 9660 partition if such a GPT partition emerges. This bit is specified as "Legacy BIOS bootable" but its true significance is unclear. Some GPT\-aware BIOS might want to see it in some partition. Mode "off" revokes this setting. .br \fBgpt_iso_not_ro=\fRon causes bit 60 of the GPT partition flags to be not set for the ISO 9660 partition if such a GPT partition emerges. This bit is specified as "Read\-only" and thus appropriate. But it is unusual in GPT disk partitions. Mode "off" revokes this setting and causes the read\-only bit to be set. .br \fBmips_path=\fRiso_rr_path declares a data file in the image to be a MIPS Big Endian boot file and causes production of a MIPS Big Endian Volume Header. This is mutually exclusive with production of other boot blocks like MBR. It will overwrite the first 512 bytes of any data provided by system_area=. Up to 15 boot files can be declared by mips_path=. .br \fBmipsel_path=\fRiso_rr_path declares a data file in the image to be the MIPS Little Endian boot file. This is mutually exclusive with other boot blocks. It will overwrite the first 512 bytes of any data provided by system_area=. Only a single boot file can be declared by mipsel_path=. .br \fBsparc_label=\fRtext causes the production of a SUN Disk Label with the given text as ASCII label. Partitions 2 to 8 may be occupied by appended images. Partition 1 will always be the ISO image. See command \-append_partition. The first 512 bytes of any data provided by system_area= will be overwritten. .br \fBgrub2_sparc_core=\fRiso_rr_path causes the content address and size of the given file to be written after the SUN Disk Label. Both numbers are counted in bytes. The address is written as 64 bit big\-endian number to byte 0x228. The size is written as 32 bit big\-endian number to byte 0x230. .br \fBhppa_cmdline=\fRtext sets the PALO command line for HP\-PA. Up to 1023 characters are permitted by default. With hppa_hdrversion=4 the limit is 127. .br Note that the first five hppa_ bootspecs are mandatory, if any of the hppa_ bootspecs is used. Only hppa_hdrversion= is allowed to be missing. .br \fBhppa_bootloader=\fRiso_rr_path designates the given path as HP\-PA bootloader file. .br \fBhppa_kernel_32=\fRiso_rr_path designates the given path as HP\-PA 32 bit kernel file. .br \fBhppa_kernel_64=\fRiso_rr_path designates the given path as HP\-PA 64 bit kernel file. .br \fBhppa_ramdisk=\fRiso_rr_path designates the given path as HP\-PA RAM disk file. .br \fBhppa_hdrversion=\fRnumber chooses between PALO header version 5 (default) and version 4. For the appropriate value see in PALO source code: PALOHDRVERSION. .br \fBalpha_boot=\fRiso_rr_path declares a data file in the image to be the DEC Alpha SRM Secondary Bootstrap Loader and causes production of a boot sector which points to it. This is mutually exclusive with production of other boot blocks like MBR. .br \fBmips_discard\fR, \fBmipsel_discard\fR, \fBsparc_discard\fR, \fBhppa_discard\fR, \fBalpha_discard\fR revoke any boot file declarations made for mips/mipsel, sparc, hppa, or alpha, respectively. This removes the ban on production of other boot blocks. .br \fBhfsplus_serial=\fRhexstring sets a string of 16 digits "0" to "9" and letters "a" to "f", which will be used as unique serial number of an emerging HFS+ filesystem. .br \fBhfsplus_block_size=\fRnumber sets the allocation block size to be used when producing HFS+ filesystems. Permissible are 512, 2048, or 0. The latter lets the program decide. .br \fBapm_block_size=\fRnumber sets the block size to be used when describing partitions by an Apple Partition Map. Permissible are 512, 2048, or 0. The latter lets the program decide. .br Note that size 512 is not compatible with production of GPT, and that size 2048 will not be mountable \-t hfsplus at least by older Linux kernels. .br .TP \fB\-append_partition\fR partition_number type_code disk_path Cause a prepared filesystem image to be appended to the ISO image and to be described by a partition table entry in a boot block at the start of the emerging ISO image. The partition entry will bear the size of the submitted file rounded up to the next multiple of 2048 bytes or to the next multiple of the cylinder size. .br Beware of subsequent multi\-session runs. The appended partition will get overwritten. .br Partitions may be appended with partition table types MBR, GPT, and SUN Disk Label. Additionally to MBR and GPT it is possible to have them marked in APM. .br With \fBMBR\fR: .br partition_number may be 1 to 4. Number 1 will put the whole ISO image into the unclaimed space before partition 1. So together with most \fBxorriso\fR MBR features, number 2 would be the most natural choice. .br The type_code may be "FAT12", "FAT16", "Linux", or a hexadecimal number between 0x00 and 0xff. Not all those numbers will yield usable results. For a list of MBR partition type codes search the Internet for "Partition Types" or run fdisk command "L". .br type_code may also be a type GUID as plain hex string like a2a0d0ebe5b9334487c068b6b72699c7 or as structured text like EBD0A0A2\-B9E5\-4433\-87C0\-68B6B72699C7. It will be used if the partition is mentioned in GPT. In MBR, C12A7328\-F81F\-11D2\-BA4B\-00A0C93EC93B will be mapped to 0xef. Any other GUID will be mapped to 0x83. In APM, 48465300\-0000\-11AA\-AA11\-00306543ECAC will be mapped to partition type "Apple_HFS", any other to "Data". .br If some other command causes the production of GPT, then the appended partitions will be mentioned there too. .br \fBGPT\fR can be forced by .br \-boot_image "any" "appended_part_as=gpt" .br partition_number may be 1 to 8. But other than with MBR partitions it is not guaranteed that the resulting GPT partition will have this number. .br More important than the desired partition number will be that the resulting ISO filesystem is covered gaplessly with GPT table and its partitions and that the partitions in the table are sorted by block address. If partition_number is higher than the number of preceding partitions, then the appropriate number of empty partition entries is inserted to achieve the desired partition_number. If the number of preceding partitions is too high, then a NOTE message informs about the inability to achieve partition_number and about the actually assigned number. .br The type_code may be the same as described with MBR. Given GUIDs are used unchanged. Given MBR partition types get translated. 0xef becomes C12A7328\-F81F\-11D2\-BA4B\-00A0C93EC93B, others become EBD0A0A2\-B9E5\-4433\-87C0\-68B6B72699C7. .br \fBSUN Disk Label\fR is chosen by \-boot_image any sparc_label=. .br partition_number may be 2 to 8. Number 1 will always be the ISO image. Partition start addresses are aligned to 320 KiB. The type_code does not matter. Submit 0x0. .br disk_path "." causes the partition to become a copy of the next lower valid one. .br With MBR, GPT, and SUN alike: .br The disk_path must provide the necessary data bytes at commit time. .br Issueing \-append_partition with a partition number that was already used in a previous \-append_partition command does not cause an error but silently overrides the previous setting. .br The pseudo type_code "revoke" or an empty disk_path prevent the partition from being appended. The pseudo partition number "all" may be used in this case to revoke all previous \-append_partition settings. .TP .B Jigdo Template Extraction: .PP From man genisoimage: "Jigdo is a tool to help in the distribution of large files like CD and DVD images; see http://atterer.net/jigdo/ for more details. Debian CDs and DVD ISO images are published on the web in jigdo format to allow end users to download them more efficiently." .br \fBxorriso\fR can produce a .jigdo and a .template file together with a single\-session ISO image. The .jigdo file contains checksums and symbolic file addresses. The .template file contains the compressed ISO image with reference tags instead of the content bytes of the listed files. .br Input for this process are the normal arguments for a \fBxorriso\fR session on a blank \-outdev, and a checksum file which lists those data files which may be listed in the .jigdo file and externally referenced in the .template file. Each designated file is represented in the checksum file by a single text line: .br Checksum as hex digits, 2 blanks, size as 12 decimal digits or blanks, 2 blanks, symbolic file address .br The kind of checksum is chosen by \-jigdo "checksum_algorithm" with values "md5" (32 hex digits) or "sha256" (64 hex digits). It will also be used for the file address lines in the .jigdo file. The default is "md5". .br The file address in a checksum file line has to bear the same basename as the disk_path of the file which it shall match. The directory path of the file address is decisive for To=From mapping, not for file recognition. After To=From mapping, the file address gets written into the .jigdo file. Jigdo restore tools will convert these addresses into really reachable data source addresses from which they can read. .br If the list of jigdo parameters is not empty, then \fBxorriso\fR will refuse to write to non\-blank targets, it will disable multi\-session emulation, and padding will be counted as part of the ISO image. .br .TP \fB\-jigdo\fR parameter_name value Clear Jigdo Template Extraction parameter list or add a parameter to that list. The alias names are the corresponding genisoimage options. They are accepted as parameter names as well. Especially they are recognized by the \-as mkisofs emulation command. .br Parameter \fBclear\fR with any value empties the whole list. No .jigdo and .template file will be produced. .br \fBchecksum_algorithm\fR chooses the checksum algorithm which shall be used for the data file entries in the .jigdo file and is expected in the checksum file. Permissible are "md5" or "sha256". Default is "md5". .br Alias: \-jigdo\-checksum\-algorithm .br \fBtemplate_path\fR sets the disk_path for the .template file with the holed and compressed ISO image copy. .br Alias: \-jigdo\-template .br \fBjigdo_path\fR sets the disk_path for the .jigdo file with the checksums and download addresses for filling the holes in .template. .br Alias: \-jigdo\-jigdo .br \fBchecksum_path\fR sets the disk_path where to find the checksum file with symbolic file addresses and checksums according to \fBchecksum_algorithm\fR. .br Alias: md5_path .br Alias: \-checksum\-list .br Alias: \-md5\-list .br \fBmin_size\fR sets the minimum size for a data file to be listed in the .jigdo file and being a hole in the .template file. .br Alias: \-jigdo\-min\-file\-size .br \fBexclude\fR adds a regular expression pattern which will get compared with the absolute disk_path of any data file. A match causes the file to stay in .template in any case. .br Alias: \-jigdo\-exclude .br \fBdemand_checksum\fR adds a regular expression pattern which will get compared with the absolute disk_path of any data file that was not found in the checksum list file as of "checksum_path". A match causes a MISHAP event. .br Alias: demand_md5 .br Alias: \-jigdo\-force\-checksum .br Alias: \-jigdo\-force\-md5 .br \fBmapping\fR adds a string pair of the form To=From to the parameter list. If a data file gets listed in the .jigdo file, then it is referred by the file address from its line in the checksum file. This file address gets checked whether it begins with the From string. If so, then this string will be replaced by the To string and a ':' character, before it goes into the .jigdo file. The From string should end by a '/' character. .br Alias: \-jigdo\-map .br \fBcompression\fR chooses one of "bzip2" or "gzip" for the compression of the template file. The jigdo file is put out uncompressed. .br Alias: \-jigdo\-template\-compress .br \fBchecksum_iso\fR chooses one or more of "md5", "sha1", "sha256", "sha512" for the auxiliary "# Image Hex" checksums in the jigdo file. The value may e.g. look like "md5,sha1,sha512". Value "all" chooses all available algorithms. Note that MD5 stays always enabled. .br Alias: \-checksum_algorithm_iso .br \fBchecksum_template\fR is like checksum_iso but for "# Template Hex". .br Alias: \-checksum_algorithm_template .TP .B Character sets: .PP File names are strings of non\-zero bytes with 8 bit each. Unfortunately the same byte string may appear as different peculiar national characters on differently nationalized terminals. The meanings of byte codes are defined in \fBcharacter sets\fR which have names. Shell command iconv \-l lists them. .br The file names on hard disk are assumed to be encoded by the \fBlocal character set\fR which is also used for the communication with the user. Byte codes 32 to 126 of the local character set must match the US\-ASCII characters of the same code. ISO\-8859 and UTF\-8 fulfill this demand. .br By default, \fBxorriso\fR uses the character set as told by shell command "locale" with argument "charmap". This may be influenced by environment variables LC_ALL, LC_CTYPE, or LANG and should match the expectations of the terminal. In some situations it may be necessary to set it by command \-local_charset. .br Local character sets should not matter as long as only english alphanumeric characters are used for file names or as long as all writers and readers of the media use the same local character set. Outside these constraints it may be necessary to let \fBxorriso\fR convert byte codes from and to other character sets. .br The Rock Ridge file names in ISO filesystems are assumed to be encoded by the \fBinput character set\fR. The Rock Ridge file names which get written with ISO filesystems will be encoded by the \fBoutput character set\fR. .br The sets can be defined independently by commands \-in_charset and \-out_charset. Normally one will have both identical, if ever. Other than the local character set, these two character sets may deviate from US\-ASCII. .br The output character sets for Joliet and HFS+ are not influenced by these commands. Joliet uses output character set UCS\-2 or UTF\-16. HFS+ uses UTF\-16. .br The default output charset is the local character set of the terminal where \fBxorriso\fR runs. So by default no conversion happens between local filesystem names and emerging Rock Ridge names in the image. The situation stays ambiguous and the reader has to riddle what character set was used. .br By command \-auto_charset it is possible to attribute the output charset name to the image. This makes the situation unambiguous. But if your terminal character set does not match the character set of the local file names, then this attribute can become plainly wrong and cause problems at read time. To prevent this it is necessary to check whether the terminal properly displays all intended filenames. Check especially the exotic national characters. .br To enforce recording of a particular character set name without any conversion at image generation time, set \-charset and \-local_charset to the desired name, and enable \-backslash_codes to avoid evil character display on your terminal. .TP \fB\-charset\fR character_set_name Set the character set from which to convert file names when loading an image and to which to convert when writing an image. .TP \fB\-local_charset\fR character_set_name Override the system assumption of the local character set name. If this appears necessary, one should consider to set \-backslash_codes to "on" in order to avoid dangerous binary codes being sent to the terminal. .TP .B Exception processing: .PP Since the tasks of \fBxorriso\fR are manifold and prone to external influence, there may arise the need for \fBxorriso\fR to report and handle problem events. .br Those events get classified when they are detected by one of the software modules and forwarded to reporting and evaluation modules which decide about reactions. Event classes are sorted by severity: .br "NEVER" The upper end of the severity spectrum. .br "ABORT" The program is being aborted and on its way to end. .br "FATAL" The main purpose of the run failed or an important resource failed unexpectedly. .br "FAILURE" An important part of the job could not be performed. .br "MISHAP" A FAILURE which can be tolerated during ISO image generation. .br "SORRY" A less important part of the job could not be performed. .br "WARNING" A situation is suspicious of being not intended by the user. .br "HINT" A proposal to the user how to achieve better results. .br "NOTE" A harmless information about noteworthy circumstances. .br "UPDATE" A pacifier message during long running operations. .br "DEBUG" A message which would only interest the program developers. .br "ERRFILE" A filename for the \-errfile_log if it is enabled. .br "ALL" The lower end of the severity spectrum. .TP \fB\-abort_on\fR severity Set the severity threshold for events to abort the program. .br Useful: "NEVER", "ABORT", "FATAL", "FAILURE" , "MISHAP", "SORRY" .br It may become necessary to abort the program anyway, despite the setting by this command. Expect not many "ABORT" events to be ignorable. .br A special property of this command is that it works preemptive if given as program start argument. I.e. the first \-abort_on setting among the start arguments is in effect already when the first operations of \fBxorriso\fR begin. Only "\-abort_on" with dash "\-" is recognized that way. .TP \fB\-return_with\fR severity exit_value Set the threshold and exit_value to be returned at program end if no abort has happened. This is to allow \fBxorriso\fR to go on after problems but to get a failure indicating exit value from the program, nevertheless. Useful is a value lower than the \-abort_on threshold, down to "WARNING". .br exit_value may be either 0 (indicating success to the starter of the program) or a number between 32 and 63. Some other exit_values are used by \fBxorriso\fR if it decides to abort the program run: .br 1=abort due to external signal .br 2=no program arguments given .br 3=creation of \fBxorriso\fR main object failed .br 4=failure to start libburnia\-project.org libraries .br 5=program abort during argument processing .br 6=program abort during dialog processing .TP \fB\-report_about\fR severity Set the threshold for events to be reported. .br Useful: "SORRY", "WARNING", "HINT", "NOTE", "UPDATE", "DEBUG", "ALL" .br Regardless what is set by \-report_about, messages get always reported if they reach the severity threshold of \-abort_on . .br Event messages are sent to the info channel "I" which is usually stderr but may be influenced by command \-pkt_output. Info messages which belong to no event get attributed severity "NOTE". .br A special property of this command is that the first \-report_about setting among the start arguments is in effect already when the first operations of \fBxorriso\fR begin. Only "\-report_about" with dash "\-" is recognized that way. .TP \fB\-signal_handling\fR mode Control the installation of a signal handler which shall react on external signals (e.g. from program "kill" or from keys Ctrl+C) or on signals caused by severe program errors. .br Mode "on" is the default. It uses the signal handler of libburn which produces ugly messages but puts much effort in releasing optical drives before \fBxorriso\fR ends. .br Mode "off" as first \-signal_handling among the start arguments prevents all own signal precautions of \fBxorriso\fR. Inherited signal handler settings stay as they are. .br It works like "sig_dfl" if given after other signal handling was already established at program start. .br Mode "sig_dfl" uses the system provided default handling of signals, which is normally a sudden abort of the program. To prevent stuck drives, the libburn handler is used during burning, blanking, and formatting on MMC drives. .br Mode "sig_ign" tries to ignore as many signal types as possible. This imposes the risk that \fBxorriso\fR refuses to end until externally kill \-9 if performed. kill \-9 then imposes the risk that the drive is left in unusable state and needs poweroff to be reset. So during burning, blanking, and formatting wait for at least their normal run time before killing externally. .br A special property of this command is that the first \-signal_handling setting among the start arguments is in effect already when the first operations of \fBxorriso\fR begin. Only "\-signal_handling" with dash "\-" is recognized that way. .TP \fB\-error_behavior\fR occasion behavior Control the program behavior at problem event occasions. For now this applies to occasions "image_loading" which is given while an image tree is read from the input device, and to "file_extraction" which is given with osirrox commands like \-extract. .br With "image_loading" there are three behaviors available: .br "best_effort" goes on with reading after events with severity below FAILURE if the threshold of command \-abort_on allows this. .br "failure" aborts image tree reading on first event of at least SORRY. It issues an own FAILURE event. This is the default. .br "fatal" acts like "failure" but issues the own event as FATAL. .br With occasion "file_extraction" there are three behaviors: .br "keep" maintains incompletely extracted files on disk. This is the default. .br "delete" removes files which encountered errors during content extraction. .br "best_effort" starts a revovery attempt by means of \-extract_cut if the file content stems from the loaded ISO image and is not filtered. .TP .B Dialog mode control: .TP \fB\-dialog\fR "on"|"off"|"single_line" Enable or disable to enter dialog mode after all program arguments are processed. In dialog mode input lines get prompted via readline or from stdin. .br If no \-abort_on severity was set when dialog starts, then "NEVER" is set to avoid abort in most cases of wrong input or other problems. Before dialog begins, the default is "FAILURE" which e.g. aborts on unknown commands. .br Mode "on" supports input of newline characters within quotation marks and line continuation by trailing backslash outside quotation marks. Mode "single_line" does not. .TP \fB\-page\fR length width Describe terminal to the text pager. See also above, paragraph Result pager. .br If parameter length is nonzero then the user gets prompted after that number of terminal lines. Zero length disables paging. .br Parameter width is the number of characters per terminal line. It is used to compute the number of terminal lines which get occupied by an output line. A usual terminal width is 80. .TP \fB\-use_readline\fR "on"|"off" If "on" then use readline for dialog. Else use plain stdin. .br See also above, paragraph Dialog, Readline, Result pager. .TP \fB\-reassure\fR "on"|"tree"|"off" If "on" then ask the user for "y" or "n": .br before deleting or overwriting any file in the ISO image, .br before overwriting any disk file during restore operations, .br before rolling back pending image changes, .br before committing image changes to media, .br before changing the input drive, .br before blanking or formatting media, .br before ending the program. .br With setting "tree" the reassuring prompt will appear for an eventual directory only once and not for each file in its whole subtree. .br Setting "off" silently kills any kind of image file object and performs above irrevocable actions. .br To really produce user prompts, command \-dialog needs to be set to "on". Note that the prompt does not appear in situations where file removal is forbidden by command \-overwrite. \-reassure only imposes an additional curb for removing existing file objects. .br Be aware that file objects get deleted from the ISO image immediately after confirmation. They are gone even if the running command gets aborted and its desired effect gets revoked. In case of severe mess\-up, consider to use \-rollback to revoke the whole session. .TP .B Drive and media related inquiry actions: .TP \fB\-devices\fR Show list of available MMC drives with the addresses of their libburn standard device files. .br This is only possible when no ISO image changes are pending. After this command was executed, there is no drive current and no image loaded. .br In order to be visible, a device has to offer rw\-permissions with its libburn standard device file. Thus it might be only the \fBsuperuser\fR who is able to see all drives. .br Drives which are occupied by other processes get not shown. .TP \fB\-device_links\fR Like \-devices, but presenting the drives with addresses of symbolic links which point to the actual device files. .br Modern GNU/Linux systems may shuffle drive addresses from boot to boot. The udev daemon is supposed to create links which always point to the same drive, regardless of its system address. The command \-device_links shows the addresses of such links if they begin by "/dev/dvd" or "/dev/cd". Precedence is: "dvdrw", "cdrw", "dvd", "cdrom", "cd". .TP \fB\-toc\fR .br Show media specific tables of content. This is the session history of the medium, not the ISO image directory tree. .br In case of overwritable media holding a valid ISO image, it may happen that only a single session gets shown. But if the first session on the overwritable media was written by \fBxorriso\fR then a complete session history can be emulated. .br A drive which is incapable of writing may show any media as CD\-ROM or DVD\-ROM with only one or two sessions on it. The last of these sessions is supposed to be the most recent real session then. .br Some read\-only drives and media show no usable session history at all. Command \-rom_toc_scan might help. .br If input device and output device are both acquired and not the same, then both tables\-of\-content get shown. .TP \fB\-toc_of\fR "in"|"out"|"all"[":short"] Like command \-toc but explicitly choosing which drive's table\-of\-content to show. "in" shows \-indev or \-dev, "out" shows \-outdev or \-dev, "all" shows the same as \-toc. .br If ":short" is appended to the drive choosing word, then only a short summary of drive state and medium content is printed. .br As further difference to \-toc, this command does not emit FAILURE events if the desired drive is not acquired. .TP \fB\-toc_info_type\fR typetext Choose which information to show in the rightmost column of \-toc and \-toc_of. .br Type "volid" is the default. It shows the Volume Ids of the listed ISO sessions. .br Type "creation_time" or "ctime" chooses the Creation Times. .br Type "modification_time" or "mtime" chooses the Modification Times. .br Appending "_gmt" to a time type text causes the time information to be shown in ECMA\-119 format YYYYMMDDhhmmsscc in timezone GMT. Else it is shown as timestamps YYYY.MM.DD.hhmmss in the local timezone of the system. .TP \fB\-assess_indev_features\fR "plain"|"cmd"|"as_mkisofs"|"replay" Inspect the filesystem on \-indev for the presence of Rock Ridge, Joliet, or ISO 9660:1999, and for traces of other write options which seem to have been used when the filesystem was created. .br Note that this command does not detect and report a possibly present HFS+ tree. .br Mode "cmd" lists xorriso commands which would activate the detected settings. .br Mode "as_mkisofs" lists options of the \-as mkisofs emulation, which would activate those of the detected settings which are not default. .br Mode "replay" performs the commands which get listed by mode "cmd". .br Mode "plain" lists after a "Indev feature: " header name\-value pairs as delivered by libisofs function iso_read_image_feature_named(). See libisofs.h. The other modes derive their output from this list. I.e. the sequence of commands from "cmd" follows the sequence of "plain". .br Not leading to "cmd" lines are: .br "size=" tells the number of 2048 byte blocks of the filesystem. .br "eltorito=1" tells that El Torito boot equipment was detected. .br "tree_loaded=" tells which tree was loaded by \-indev: .br 0 = ISO 9660 , 1 = Joliet , 2 = ISO 9660:1999 .br "tree_loaded_text=" tells the same by name: "ISO9660", "Joliet", "ISO9660:1999" .br "rr_loaded=1" tells that Rock Ridge information was loaded with the tree. .br "aaip=1" tells that AAIP information was detected (ACL, xattr, MD5, ...). .br "relaxed_vol_atts=1" tells that the volume attributes like \-volid or \-preparer_id bear characters outside the restricted character sets which are specified for them by ECMA\-119. .br "rrip_1_10_px_ino=1" tells that with Rock Ridge 1.10 a PX entry was found which looks like from Rock Ridge 1.12. .TP \fB\-mount_cmd\fR drive entity id path Emit an appropriate command line for mounting the ISO session indicated by drive, entity and id. The result will be different on GNU/Linux and on FreeBSD or NetBSD. .br drive can be "indev" or "outdev" to indicate already acquired drives, or it can be the path of a not yet acquired drive. Prefix "stdio:" for non\-MMC drives is not mandatory. .br See command \fB\-load\fR for the meaning of entity and id. .br Entities are: "auto", "session", "track", "lba", "sbsector", "volid", "at_time", "before", "not_after", "after", and "not_before". .br Each is to be used with its appropriate kind of id string: "auto", session number, track number, block number, search expression for volume id, or time string. .br path will be used as mount point and must already exist as a directory on disk. .br The command gets printed to the result channel. See command \-mount for direct execution of this command. .TP \fB\-mount_opts\fR option[:option...] Set options which influence \-mount and \-mount_cmd. Currently there is only option "exclusive" which is default and its counterpart "shared". The latter causes \fBxorriso\fR not to give up the affected drive with command \-mount. On GNU/Linux it adds mount option "loop" which may enable mounting of several sessions of the same block device at the same time. One should not write to a mounted optical medium, of course. Take care to umount all sessions before ejecting. .TP \fB\-session_string\fR drive entity id format Print to the result channel a text which gets composed according to format and the parameters of the addressed session. .br Formats "linux:"path or "freebsd:"path produce the output of \-mount_cmd for the given operating systems. .br In other texts \fBxorriso\fR will substitute the following parameter names. An optional prefix "string:" will be removed. .br "%device%" will be substituted by the mountable device path of the drive address. .br "%sbsector%" will be substituted by the session start sector. .br "%track%", "%session%", "%volid%" will be substituted by track number, session number, or volume id of the depicted session. .TP \fB\-print_size\fR Print the foreseeable consumption of 2048 byte blocks by next \-commit. This can last a while as a \-commit gets prepared and only in last moment is revoked by this command. The result depends on several settings and also on the kind of output device. If no \-jigdo options are set and not command \-as "mkisofs" was used, then \-padding (300 kB by default) is not counted as part of the image size. .br If an El Torito boot image file is already depicted, then command \-print_size automatically executes \-boot_image "any" "next". This means that the properties of that boot image cannot be edited by subsequent commands. .TP \fB\-tell_media_space\fR Print available space on the output medium and the free space after subtracting already foreseeable consumption by next \-commit. .br Note that the title of the prediction "After commit :" is misleading. It is rather the space that may still be filled in this session without making the next \-commit fail from medium overflow. .br The free space after the next \-commit might be smaller by several MB. This depends on medium type, number of recorded sessions, and drive habits. .TP \fB\-pvd_info\fR Print various ID strings and timestamps which can be found in loaded ISO images. Some of the IDs may be changed by commands like \-volid or \-publisher. For these IDs \-pvd_info reports what would be written with the next \-commit. .br The timestamps get shown in ECMA\-119 format YYYYMMDDhhmmsscc and timezone GMT. They do not get automatically propagated from loaded image to newly written image. The ones for new images may be set by command \-volume_date. See there for the meaning of the particular timestamps. .TP \fB\-report_el_torito\fR mode .br With mode \fBplain\fR print a report about the information found in the El Torito boot catalog of the loaded ISO image. .br With mode \fBhelp\fR print a text which explains the meaning of the lines put out by "plain". .br Mode \fBcmd\fR tries to print the \fBxorriso\fR commands which are necessary to produce the found boot equipment: disk identifiers, El Torito boot images, and System Area. Disk identifiers are strings which the booting operating system might use to find the ISO filesystem from where it comes. Currently known is the use of volume id and modification date. .br The intended use case is modification of the filesystem by having \-indev and \-outdev pointing to different images or drives. The result might be insufficient, if the found equipment cannot be produced by xorriso. Various SORRY events may arise in this case, but it is not guaranteed that xorriso recognizes all its insufficiencies. .br Mode \fBas_mkisofs\fR tries to print the \fBxorriso \-as mkisofs\fR options, which are necessary to produce the found equipment. The intended use case is to use the mounted filesystem as input tree together with the printed options. .br If CHRP equipment is detected, then modes \fBcmd\fR and \fBas_mkisofs\fR issue some of the relaxation commands or options which get detected by command \fB\-assess_indev_features\fR. This happens because CHRP firmware reads file paths from file /ppc/bootinfo.txt and tries to find them case\-insensitively in the ECMA\-119 tree without using Rock Ridge. If such a path has actually forbidden properties, like the name "powerpc\-ieee1275", then the relaxations are needed to bring it unmangled into the ECMA\-119 tree. .br It is important to keep in mind that the file paths shown in the report lines and commands were registered directly after image loading. Possible filesystem manipulations which later remove these paths or replace their file content will not influence the report lines or commands. .TP \fB\-report_system_area\fR mode With mode \fBplain\fR print a report about the information found in the System Area of the loaded ISO image. The report consists of zero to many lines with a header text, a colon, and information text. .br With mode \fBhelp\fR print a text which explains the meaning of the lines put out by "plain". You probably will have to look for more documentation which explains the technical details of the mentioned boot facilities. .br Modes \fBcmd\fR and \fBas_mkisofs\fR work like with command \-report_el_torito. See above. .br It is important to keep in mind that the file paths shown in the report lines and commands were registered directly after image loading. Possible filesystem manipulations which later remove these paths or replace their file content will not influence the report lines or commands. .br With mode \fBgpt_disk_guid\fR print the GPT disk GUID of the loaded ISO in RFC 4122 text format to result channel. It is not considered an error if no GPT is present. In this case nothing is printed to result channel. .br With mode \fBgpt_crc_of:\fRdisk_path read up to 32 KiB from the disk file with the path given after the colon. Compute the GPT compliant CRC number and print it to the result channel. The number is shown like "0x690fd979". The special disk_path "\-" causes reading from standard input. .br With mode \fBmake_guid\fR print a pseudo\-random GUID in RFC 4122 text format to result channel. .TP .B Navigation in ISO image and disk filesystem: .TP \fB\-cd\fR iso_rr_path Change the current working directory in the ISO image. This is prepended to iso_rr_paths which do not begin with '/'. .br It is possible to set the working directory to a path which does not exist yet in the ISO image. The necessary parent directories will be created when the first file object is inserted into that virtual directory. Use \-mkdir if you want to enforce the existence of the directory already at first insertion. .TP \fB\-cdx\fR disk_path Change the current working directory in the local filesystem. To be prepended to disk_paths which do not begin with '/'. .TP \fB\-pwd\fR .br Tell the current working directory in the ISO image. .TP \fB\-pwdx\fR .br Tell the current working directory in the local filesystem. .TP \fB\-ls\fR iso_rr_pattern [***] List files in the ISO image which match shell patterns (i.e. with wildcards '*' '?' '[a\-z]'). If a pattern does not begin with '/' then it is compared with addresses relative to \-cd. .br Directories are listed by their content rather than as single file item. .br Pattern expansion may be disabled by command \-iso_rr_pattern. .TP \fB\-lsd\fR iso_rr_pattern [***] Like \-ls but listing directories as themselves and not by their content. This resembles shell command ls \-d. .TP \fB\-lsl\fR iso_rr_pattern [***] Like \-ls but also list some of the file attributes. The output format resembles shell command ls \-ln. .br File type 'e' indicates the El Torito boot catalog. .br If the file has non\-trivial ACL, then a '+' is appended to the permission info. If the file is hidden, then 'I' for "iso_rr", 'J' for "joliet", 'A' for "hfsplus", 'H' for multiple hiding gets appended. Together with ACL it is 'i', 'j', 'a', 'h'. .TP \fB\-lsdl\fR iso_rr_pattern [***] Like \-lsd but also list some of the file attributes. The output format resembles shell command ls \-dln. .TP \fB\-lsx\fR disk_pattern [***] List files in the local filesystem which match shell patterns. Patterns which do not begin with '/' are used relative to \-cdx. .br Directories are listed by their content rather than as single file item. .br Pattern expansion may be disabled by command \-disk_pattern. .TP \fB\-lsdx\fR disk_pattern [***] Like \-lsx but listing directories as themselves and not by their content. This resembles shell command ls \-d. .TP \fB\-lslx\fR disk_pattern [***] Like \-lsx but also listing some of the file attributes. Output format resembles shell command ls \-ln. .TP \fB\-lsdlx\fR disk_pattern [***] Like \-lsdx but also listing some of the file attributes. Output format resembles shell command ls \-dln. .TP \fB\-getfacl\fR iso_rr_pattern [***] Print the access permissions of the given files in the ISO image using the format of shell command getfacl. If a file has no ACL then it gets fabricated from the \-chmod settings. A file may have a real ACL if it was introduced into the ISO image while command \-acl was set to "on". .TP \fB\-getfacl_r\fR iso_rr_pattern [***] Like \-gefacl but listing recursively the whole file trees underneath eventual directories. .TP \fB\-getfattr\fR iso_rr_pattern [***] Print the xattr of the given files in the ISO image. If a file has no such xattr then noting is printed for it. The choice of namespaces depends on the setting of command \-xattr: "on" or "user" restricts it to namespace "user", "any" only omits namespace "isofs". .TP \fB\-getfattr_r\fR iso_rr_pattern [***] Like \-gefattr but listing recursively the whole file trees underneath of directories. .TP \fB\-lsattr\fR iso_rr_pattern [***] Print the Linux file attributes of the given files like program lsattr(1) would do with disk files. The meaning of the shown flag letters are described in man 1 chattr with the exception of '\-', which is shown as placeholder for an unset flag. .br The given files will get a line printed even if they have no Linux file attributes attached. In this case all flags will be shown as '\-'. .TP \fB\-du\fR iso_rr_pattern [***] Recursively list size of directories and files in the ISO image which match one of the patterns. similar to shell command du \-k. .TP \fB\-dus\fR iso_rr_pattern [***] List size of directories and files in the ISO image which match one of the patterns. Similar to shell command du \-sk. .TP \fB\-dux\fR disk_pattern [***] Recursively list size of directories and files in the local filesystem which match one of the patterns. Similar to shell command du \-k. .TP \fB\-dusx\fR disk_pattern [***] List size of directories and files in the local filesystem which match one of the patterns. Similar to shell command du \-sk. .TP \fB\-findx\fR disk_path [test [op] [test ...]] [-exec action [params]] -- Like \-find but operating on local filesystem and not on the ISO image. The \-findx command is subject to the settings of \-follow. .br \-findx accepts the same tests as \-find, but only the following ones work like described with \-find: .br \-bad_outname, \-decision, \-disk_name, \-disk_path, \-has_acl, \-has_any_xattr, \-has_lfa_flags, \-has_some_lfa_flags_of, \-has_xattr, \-lba_range, \-maxdepth, \-mindepth, \-name, \-or_use_pattern, \-prune, \-size, \-true, \-type, \-use_pattern, \-wholename .br The others get defaulted to \-false, because they are not applicable to disk files. .br Test \-type accepts the same parameters as with \-find. Additionally it recognizes type "mountpoint" (or "m") which matches subdirectories which reside on a different device than their parent. This type never matches the disk_path given as start address for \-findx. .br Test \-lba_range matches only if its parameter start_lba is 0. .br Tests \-has_lfa_flags and \-has_some_lfa_flags_of ignore non\-settable file attribute flags if \-lfa_flags is set to on:import_only_settable. .br \-findx accepts the \-exec actions as does \-find. But except the following few actions it will always perform action "echo". .br \fBin_iso\fR reports the path if its counterpart exists in the ISO image. For this the disk_path of the \-findx command gets replaced by the iso_rr_path given as parameter. .br E.g.: \-findx /home/thomas \-exec in_iso /thomas_on_cd \-\- .br \fBnot_in_iso\fR reports the path if its counterpart does not exist in the ISO image. The report format is the same as with command \-compare. .br \fBadd_missing\fR iso_rr_path_start adds the counterpart if it does not yet exist in the ISO image and marks it for "rm_merge" as non\-removable. .br E.g.: \-findx /home/thomas \-exec add_missing /thomas_on_cd \-\- .br \fBis_full_in_iso\fR reports if the counterpart in the ISO image contains files. To be used with \-type "m" to report mount points. .br \fBempty_iso_dir\fR deletes all files from the counterpart in the ISO image. To be used with \-type "m" to truncate mount points. .br \fBprint_outname\fR prints in the first line the filename as found on disk, and in the second line the filename after conversion forth and back between local character set and one of the namespaces "rockridge", "joliet", "ecma119", or "hfsplus". The third output line is "\-\-" . .br The name conversion does not take into respect the possibility of name collisions in the target namespace. Such collisions are most likely in "joliet" and "ecma119", where they get resolved by automatic file name changes. .br \fBestimate_size\fR prints a lower and an upper estimation of the number of blocks which the found files together will occupy in the emerging ISO image. This does not account for the superblock, for the directories in the \-findx path, or for image padding. .br \fBgetfacl\fR prints access permissions in ACL text form to the result channel. .br \fBgetfattr\fR prints xattr name\-value pairs to the result channel. The choice of namespaces depends on the setting of command \-xattr: "off", "on", or "user" restricts it to the namespace "user", "any" causes all namespaces to be shown. .br \fBget_any_xattr\fR prints xattr name\-value pairs to the result channel. All namespaces are shown regardless of the setting of command \-xattr. .br \fBlist_extattr\fR mode prints a script to the result channel, which would use FreeBSD command setextattr to set the file's xattr name\-value pairs of user namespace. See \-find for a description of parameter mode. .br E.g. \-exec list_extattr e \-\- .br \fBlsattrd\fR prints the Linux file attribute flags like command \-lsattrd does. This shows non\-settable flags, too, even if they are to be ignored by the setting of command \-lfa_flags. .TP \fB\-compare\fR disk_path iso_rr_path Compare attributes and eventual data file content of a fileobject in the local filesystem with a file object in the ISO image. The iso_rr_path may well point to an image file object which is not yet committed, i.e. of which the data content still resides in the local filesystem. Such data content is prone to externally caused changes. .br If iso_rr_path is empty then disk_path is used as path in the ISO image too. .br Differing attributes are reported in detail, differing content is summarized. Both to the result channel. In case of no differences no result lines are emitted. .TP \fB\-compare_r\fR disk_path iso_rr_path Like \-compare but working recursively. I.e. all file objects below both addresses get compared whether they have counterparts below the other address and whether both counterparts match. .TP \fB\-compare_l\fR disk_prefix iso_rr_prefix disk_path [***] Perform \-compare_r with each of the disk_path parameters. iso_rr_path will be composed from disk_path by replacing disk_prefix by iso_rr_prefix. .TP \fB\-show_stream\fR iso_rr_path [***] Display the content stream chain of data files in the ISO image. The chain consists of the iso_rr_name and one or more streams, separated by " < " marks. A stream description consists of one or more texts, separated by ":" characters. The first text tells the stream type, the following ones, if ever, describe its individual properties. Frequently used types are: .br disk:'disk_path' for local filesystem objects. .br image:'iso_rr_path' for ISO image file objects. .br cout:'disk_path offset count' for \-cut_out files. .br extf:'filter_name' for external filters. .br \-\-zisofs:algorithm:block_size for zisofs compression filters. .br \-\-zisofs\-decode:algorithm:block_size for zisofs uncompression filters. .br \-\-gzip for internal gzip compression filters. .br \-\-gunzip for internal gzip uncompression filters. .br Example: .br '/abc/xyz.gz' < extf:'gzip' < disk:'/home/me/x' .TP \fB\-show_stream_r\fR iso_rr_path [***] Like \-show_stream but working recursively. .TP .B Evaluation of readability and recovery: .PP It is not uncommon that optical media produce read errors. The reasons may be various and get obscured by error correction which is performed by the drives and based on extra data on the media. If a drive returns data then one can quite trust that they are valid. But at some degree of read problems the correction will fail and the drive is supposed to indicate error. .br \fBxorriso\fR can scan a medium for readable data blocks, classify them according to their read speed, save them to a file, and keep track of successfully saved blocks for further tries on the same medium. .br By command \-md5 checksums may get recorded with data files and whole sessions. These checksums are reachable only via indev and a loaded image. They work independently of the media type and can detect transmission errors. .TP \fB\-check_media\fR [option [option ...]] -- Try to read data blocks from the indev drive, optionally copy them to a disk file, and finally report about the encountered quality. Several options may be used to modify the default behavior. .br The parameters given with this command override the default settings which may have been changed by command \-check_media_defaults. See there for a description of available options. .br The result list tells intervals of 2 KiB blocks with start address, number of blocks and quality. Qualities which begin with "+" are supposed to be valid readable data. Qualities with "\-" are unreadable or corrupted data. "0" indicates qualities which are not covered by the check run or are regularly allowed to be unreadable (e.g. gaps between tracks). .br Alternatively it is possible to report damaged files rather than blocks. .br If \-md5 is "on" then the default mode what=tracks looks out for libisofs checksum tags for the ISO session data and checks them against the checksums computed from the data stream. .TP \fB\-check_media_defaults\fR [option [option ...]] -- Preset options for runs of \-check_media, \-extract_cut and best_effort file extraction. Options given with \-check_media will override the preset options. \-extract_cut will override some options automatically. .br An option consists of a keyword, a "=" character, and a value. Options may override each other. So their sequence matters. .br The default setting at program start is: .br use=indev what=tracks min_lba=\-1 max_lba=\-1 retry=default .br time_limit=28800 item_limit=100000 data_to='' event=ALL .br abort_file=/var/opt/xorriso/do_abort_check_media .br sector_map='' map_with_volid=off patch_lba0=off report=blocks .br bad_limit=invalid slow_limit=1.0 chunk_size=0s async_chunks=0 .br Option "reset=now" restores these startup defaults. .br Non\-default options are: .br \fBreport="files"\fR lists the files which use damaged blocks (not with use=outdev). The format is like with find \-exec report_damage. Note that a MD5 session mismatch marks all files of the session as damaged. If finer distinction is desired, perform \-md5 off before \-check_media. .br \fBreport="blocks_files"\fR first lists damaged blocks and then affected files. .br \fBuse="outdev"\fR reads from the output drive instead of the input drive. This avoids loading the ISO image tree from media. .br \fBuse="sector_map"\fR does not read any media but loads the file given by option sector_map= and processes this virtual outcome. .br \fBwhat="disc"\fR scans the payload range of a medium without respecting track gaps. .br \fBwhat="image"\fR similar to "disc", but restricts scanning to the range of the ISO 9660 image, if present. .br \fBmin_lba=limit\fR omits all blocks with addresses lower than limit. .br \fBmax_lba=limit\fR switches to what=disc and omits all blocks above limit. .br \fBchunk_size=size\fR sets the number of bytes to be read in one low\-level read operation. This gets rounded down to full blocks of 2048 bytes. 0 means automatic size. .br \fBretry="on"\fR forces read retries with minimal senseful chunk size when the normal read chunk produces a read error. This size is 1s with CD and stdio files, 16s with DVD (1 ECC Block), and 32s with BD (1 Cluster). By default, retries are only enabled with CD media. "retry=off" forbits retries for all media types. .br \fBabort_file=disk_path\fR gives the path of the file which may abort a scan run. Abort happens if the file exists and its mtime is not older than the start time of the run. Use shell command "touch" to trigger this. Other than an aborted program run, this will report the tested and untested blocks and go on with running \fBxorriso\fR. .br \fBtime_limit=seconds\fR gives the number of seconds after which the scan shall be aborted. This is useful for unattended scanning of media which may else overwork the drive in its effort to squeeze out some readable blocks. Abort may be delayed by the drive gnawing on the last single read operation. Value \-1 means unlimited time. .br \fBitem_limit=number\fR gives the number of report list items after which to abort. Value \-1 means unlimited item number. .br \fBdata_to=disk_path\fR copies the valid blocks to the given file, which must support random access writing, unless disk_path is "\-" which means standard output. .br In the latter case, patch_lba0= settings other than "off" yield failure. Further the usual result messages of \-check_media get redirected to the info channel. But beware of result messages from other commands. Beware of \-*dev "\-" which redirect standard output to standard error. Keep the run simple: .br xorriso \-indev /dev/sr0 \-check_media data_to=\- \-\- | md5sum .br xorriso \-outdev /dev/sr0 \-check_media data_to=\- use=outdev \\ what=disc min_lba=0 max_lba=999999 \-\- | sha256sum .br \fBevent=severity\fR sets the given severity for a problem event which shall be issued at the end of a check run if data blocks were unreadable or failed to match recorded MD5 checksums. Severity "ALL" disables this event. .br \fBsector_map=disk_path\fR tries to read the file given by disk_path as sector bitmap and to store such a map file after the scan run. The bitmap tells which blocks have been read successfully in previous runs. It is the persistent memory for several scans on the same medium, even with intermediate eject, in order to collect readable blocks whenever the drive is lucky enough to produce them. The stored file contains a human readable TOC of tracks and their start block addresses, followed by binary bitmap data. .br By default, untested blocks are not considered bad, but rather as intentionally unread. If you expect time_limit= or item_limit= to abort the run, then consider to use bad_limit="untested". .br \fBmap_with_volid="on"\fR examines tracks whether they are ISO images and prints their volume IDs into the human readable TOC of sector_map=. .br \fBpatch_lba0="on"\fR transfers within the data_to= file a copy of the currently loaded session head to the start of that file and patches it to be valid at that position. This makes the loaded session the last valid session of the image file when it gets mounted or loaded as stdio: drive. New sessions will be appended after this last session and will overwrite any sessions which have followed it. .br \fBpatch_lba0="force"\fR performs patch_lba0="on" even if \fBxorriso\fR believes that the copied data are not valid. .br patch_lba0= may also bear a number. If it is 32 or higher it is taken as start address of the session to be copied. In this case it is not necessary to have an \-indev and a loaded image. ":force" may be appended after the number. .br \fBbad_limit=threshold\fR sets the highest quality which shall be considered as damage. Choose one of "good", "md5_match", "slow", "partial", "valid", "untested", "md5_mismatch", "invalid", "tao_end", "off_track", "unreadable". .br "valid" and "invalid" are qualities imported from a sector_map file. "tao_end" and "off_track" are intentionally not readable, but not bad either. "partial" are blocks retrieved from a partially readable chunk. They are supposed to be ok but stem from a suspicious neighborhood. .br "md5_match" and "md5_mismatch" regions overlap with regions of other quality. The former is a strong confirmation for quality, the latter only tells that one or more blocks of the region must be wrong. .br By default bad_limit is set higher than md5_mismatch, so that mismatches are classified as quality class "0" rather than "\-". This means that the sectors of a MD5 mismatch range are recorded in the sector_map as successfully read, if the drive handed them out at all. Set "bad_limit=md5_mismatch" to let the sector_map record the whole mismatching range as yet not retrieved. .br \fBslow_limit=threshold\fR sets the time threshold for a single read chunk to be considered slow. This may be a fractional number like 0.1 or 1.5. .br \fBasync_chunks=number\fR enables asynchronous MD5 processing if number is 2 or larger. In this case the given number of read chunks is allocated as fifo buffer. On very fast MMC drives try: chunk_size=64s async_chunks=16. .TP \fB\-check_md5\fR severity iso_rr_path [***] Compare the data content of the given files in the loaded image with their recorded MD5 checksums, if there are any. In case of any mismatch an event of the given severity is issued. It may then be handled by appropriate settings of commands \-abort_on or \-return_with which both can cause non\-zero exit values of the program run. Severity ALL suppresses that event. .br This command reports match and mismatch of data files to the result channel. Non\-data files cause NOTE events. There will also be UPDATE events from data reading. .br If no iso_rr_path is given then the whole loaded session is compared with its MD5 sum. Be aware that this covers only one session and not the whole image if there are older sessions. .TP \fB\-check_md5_r\fR severity iso_rr_path [***] Like \-check_md5 but checking all data files underneath the given paths. Only mismatching data files will be reported. .TP .B osirrox ISO-to-disk restore commands: .PP Normally \fBxorriso\fR only writes to disk files which were given as stdio: pseudo\-drives or as log files. But its alter ego osirrox is able to extract file objects from ISO images and to create, overwrite, or delete file objects on disk. .br Disk file exclusions by \-not_mgt, \-not_leaf, \-not_paths apply. The exclusion tests are made with the paths and names for the disk files. If exclusion of paths or names in the ISO image is desired, then use image manipulation commands like \-rm or \-find ... \-exec rm before extraction, and end the program by \-rollback_end . .br Excluded disk_path parameters of extraction commands cause SORRY events. Implicitly given paths in trees under disk_path parameters are excluded silently. .br If disk file objects already exist then the settings of \-overwrite and \-reassure apply. But \-overwrite "on" only triggers the behavior of \-overwrite "nondir". I.e. directories cannot be deleted. .br Access permissions of files in the ISO image do not restrict restoring. The directory permissions on disk have to allow rwx. .TP \fB\-osirrox\fR setting[:option:...] Setting \fBoff\fR disables disk filesystem manipulations. This is the default unless the program was started with leafname \fBosirrox\fR. Elsewise the capability to restore files can be enabled explicitly by \-osirrox \fBon\fR. It can be irrevocably disabled by \-osirrox \fBbanned\fR. .br The setting \fBblocked\fR is like \fBoff\fR. But it can only be revoked by setting \fBunblock\fR, which elsewise is like \fBon\fR. This can be used to curb command scripts which might use \fBon\fR undesiredly. .br To enable restoring of special files by \fBdevice_files\fR is potentially dangerous. The meaning of the number st_rdev (see man 2 stat) depends much on the operating system. Best is to restore device files only to the same system from where they were copied. If not enabled, device files in the ISO image are ignored during restore operations. .br Due to a bug of previous versions, device files from previous sessions might have been altered to major=0, minor=1. So this combination does not get restored. .br Option \fBconcat_split_on\fR is default. It enables restoring of split file directories as data files if the directory contains a complete collection of \-cut_out part files. With option \fBconcat_split_off\fR such directories are handled like any other ISO image directory. .br Option \fBauto_chmod_off\fR is default. If \fBauto_chmod_on\fR is set then access restrictions for disk directories get circumvented if those directories are owned by the effective user who runs \fBxorriso\fR. This happens by temporarily granting rwx permission to the owner. .br Option \fBsort_lba_on\fR may improve read performance with optical drives. It can restore large numbers of hard links without exhausting \-temp_mem_limit. It does not preserve directory mtime and it needs \-osirrox option auto_chmod_on in order to extract directories which offer no write permission. Default is \fBsort_lba_off\fR. .br Option \fBo_excl_on\fR is the default unless the program was started with leafname "osirrox". On GNU/Linux it tries to avoid using drives which are mounted or in use by other libburn programs. Option \fBo_excl_off\fR on GNU/Linux enables access to such drives by the equivalent of \-drive_access "shared:readonly". I.e. drives which get acquired while \fBo_excl_off\fR will refuse to get blanked, formatted, written, or ejected. But be aware that even harmless inquiries can spoil ongoing burns of CD\-R[W] and DVD\-R[W]. .br Option \fBstrict_acl_off\fR is default. It tolerates on FreeBSD the presence of directory "default" ACLs in the ISO image. With \fBstrict_acl_on\fR these GNU/Linux ACLs cause on FreeBSD a FAILURE event during restore with \-acl "on". .br Option \fBcheck_md5_off\fR disables MD5 checking during copy to disk. The default option \fBcheck_md5_on\fR enables it if \-md5 is "on". If a data file with recorded MD5 is copied as a whole to the disk filesystem, then the MD5 of the copied content gets computed and compared with the recorded MD5. A mismatch causes an error message of severity SORRY. Option \fBcheck_md5_force\fR causes an error message if \-md5 is "on" but no MD5 is recorded for the data file. .br Option \fBsparse=\fR controls production of sparse files during extraction of files from the ISO filesystem. Default is \fBsparse=off\fR. .br A positive number like in \fBsparse=1m\fR sets the minimum requirement for the length of a sequence of 0\-bytes which shall be represented by a gap. This saves disk space if the disk filesystem supports sparse files. A gap gets created by help of lseek(2) if a sequence of read buffers, which contain only 0\-bytes, bears at least the minimum amount of bytes. Expect read buffers to be in the size range of 32k or 64k. .br Command \-paste_in creates gaps only if the writing begins at or after the end of the existing disk file. So the sequence of \-paste_in commands matters. Command \-concat does not create sparse files. .TP \fB\-extract\fR iso_rr_path disk_path Copy the file objects at and underneath iso_rr_path to their corresponding addresses at and underneath disk_path. This is the inverse of \-map or \-update_r. .br If iso_rr_path is a directory and disk_path is an existing directory then both trees will be merged. Directory attributes get extracted only if the disk directory is newly created by the copy operation. Disk files get removed only if they are to be replaced by file objects from the ISO image. .br As many attributes as possible are copied together with restored file objects. .TP \fB\-extract_single\fR iso_rr_path disk_path Like \-extract, but if iso_rr_path is a directory then its sub tree gets not restored. .TP \fB\-extract_l\fR iso_rr_prefix disk_prefix iso_rr_path [***] Perform \-extract with each of the iso_rr_path parameters. disk_path will be composed from iso_rr_path by replacing iso_rr_prefix by disk_prefix. .TP \fB\-extract_cut\fR iso_rr_path byte_offset byte_count disk_path Copy a byte interval from a data file out of an ISO image into a newly created disk file. The main purpose for this is to offer a way of handling large files if they are not supported by mount \-t iso9660 or if the target disk filesystem cannot store large files. .br If the data bytes of iso_rr_path are stored in the loaded ISO image, and no filter is applied, and byte_offset is a multiple of 2048, then a special run of \-check_media is performed. It may be quicker and more rugged than the general reading method. .TP \fB\-cpx\fR iso_rr_path [***] disk_path Copy single leaf file objects from the ISO image to the address given by disk_path. If more then one iso_rr_path is given then disk_path must be a directory or non\-existent. In the latter case it gets created and the extracted files get installed in it with the same leafnames. .br Missing directory components in disk_path will get created, if possible. .br Directories are allowed as iso_rr_path only with \-osirrox "concat_split_on" and only if they actually represent a complete collection of \-cut_out split file parts. .TP \fB\-cpax\fR iso_rr_path [***] disk_path Like \-cpx but restoring mtime, atime as in ISO image and trying to set ownership and group as in ISO image. .TP \fB\-cp_rx\fR iso_rr_path [***] disk_path Like \-cpx but also extracting whole directory trees from the ISO image. .br The resulting disk paths are determined as with shell command cp \-r : If disk_path is an existing directory then the trees will be inserted or merged underneath this directory and will keep their leaf names. The ISO directory "/" has no leaf name and thus gets mapped directly to disk_path. .TP \fB\-cp_rax\fR iso_rr_path [***] disk_path Like \-cp_rx but restoring mtime, atime as in ISO image and trying to set ownership and group as in ISO image. .TP \fB\-paste_in\fR iso_rr_path disk_path byte_offset byte_count Read the content of a ISO data file and write it into a data file or device file on disk beginning at the byte_offset. Write at most byte_count bytes. The file depicted by disk_path has to support random write access. .br This is the inverse of command \-cut_out. .TP \fB\-concat\fR mode [target | lim prog [args [...]] lim] iso_rr_path [***] Copy the data content of one or more data files of the ISO image into a disk file object, into a file descriptor, or start a program and copy the data into its standard input. The latter is subject to the security restrictions for external filters. .br Modes \fBoverwrite\fR and \fBappend\fR write into the target which is given by the second parameter. This may be the path to a disk file object, or "\-" which means standard output, or a text of the form /dev/fd/number, where number is an open file descriptor (e.g. standard error is /dev/fd/2). An existing target file is not removed before writing begins. If it is not able to take content data, then this command fails. Mode overwrite truncates regular data files to 0 size before writing into them. Example: .br \-concat append /home/me/accumulated_text /my/iso/text \-\- .br Mode \fBpipe\fR expects as second parameter a delimiter word which shall mark the end of the program argument list. The third argument is the disk_path to the program. It must contain at least one '/'. $PATH is not applied. Further parameters up to the announced delimiter word are used as arguments with the program start. Example: .br \-iso_rr_pattern on \\ .br \-concat pipe + /usr/bin/wc + "/my/iso/files*" \-\- .br The further parameters in all modes are the iso_rr_paths of data files. Their content gets concatenated in the copy. .TP \fB\-extract_boot_images\fR disk_path Copy boot equipment to disk, which is not necessarily represented as data files in the ISO filesystem. The data get written into various files in a disk directory, which may already exist or of which the parent must exist so that it can get created. .br Files may be missing if their corresponding information is not present in the ISO filesystem. Existing files do not get overwritten but rather cause a failure event. .br The same data may appear in different files. E.g. the El Torito boot image for EFI is often the same data as the EFI partition in MBR or GPT. .br File "eltorito_catalog.img" contains the El Torito Boot Catalog. .br Files "eltorito_img*_*.img" contain El Torito Boot images. The first "*" gives the image number, the second "*" gives the type: "bios", "mac", "ppc", "uefi", or a hex number. .br File "mbr_code_isohybrid.img" contains the ISOLINUX MBR template. .br File "mbr_code_grub2.img" contains the GRUB2 MBR template. .br File "systemarea.img" contains the whole 32 KiB of System Area if not all zero. .br Files "mbr_part*_efi.img" contain EFI partition images from the MBR partition table. The "*" text part gives the partition number. .br Files "mbr_part*_prep.img" contain PReP partition images. .br Files "gpt_part*_efi.img" contain EFI partition images from GPT. .br Files "gpt_part*_hfsplus.img" contain HFS+ partition images from GPT. To avoid extracting the whole HFS+ aspect of hybrid ISO filesystems, the partition image is extracted only if it has less than half of the size of the ISO filesystem or if the partition is outside the ISO filesystem. .TP \fB\-mount\fR drive entity id path Produce the same line as \-mount_cmd and then execute it as external program run after giving up the depicted drive. See also \-mount_opts. This demands \-osirrox to be enabled and normally will succeed only for the superuser. For safety reasons the mount program is only executed if it is reachable as /bin/mount or /sbin/mount. .TP .B Command compatibility emulations: .PP Writing of ISO 9660 on CD is traditionally done by program mkisofs as ISO 9660 image producer and cdrecord as burn program. \fBxorriso\fR does not strive for their comprehensive emulation. Nevertheless it is ready to perform some of its core tasks under control of commands which in said programs trigger comparable actions. .TP \fB\-as\fR personality option [options] -- .br Perform the variable length option list as sparse emulation of the program depicted by the personality word. .br Personality "\fBmkisofs\fR" accepts the options listed with: .br \-as mkisofs \-help \-\- .br Among them: \-R (always on), \-r, \-J, \-o, \-M, \-C, \-dir\-mode, \-file\-mode, \-path\-list, \-m, \-exclude\-list, \-f, \-print\-size, \-pad, \-no\-pad, \-V, \-v, \-version, \-graft\-points, \-z, \-no\-emul\-boot, \-b, \-c, \-boot\-info\-table, \-boot\-load\-size, \-input\-charset, \-G, \-output\-charset, \-U, \-hide, \-hide\-joliet, \-hide\-list, \-hide\-joliet\-list, file paths and pathspecs. A lot of options are not supported and lead to failure of the mkisofs emulation. Some are ignored, but better do not rely on this tolerance. .br The supported options are documented in detail in xorrisofs.info and in man xorrisofs. The description here is focused on the effect of mkisofs emulation in the context of a \fBxorriso\fR run. .br Other than with the "cdrecord" personality there is no automatic \-commit at the end of a "mkisofs" option list. Verbosity settings \-v (= "UPDATE") and \-quiet (= "SORRY") persist. The output file persists until things happen like \-commit, \-rollback, \-dev, or end of \fBxorriso\fR. .br Options which affect all file objects in the ISO image, like \-r or \-dir\-mode, will be applied only to files which are present in the ISO image when the command \-as ends. If you use several \-as mkisofs commands in the same run, then consider to put such options into the last \-as command. .br If files are added to the image, then \-pacifier gets set to "mkisofs" and \-stdio_sync is defaulted to "off" if no such setting was made yet. .br \-graft\-points is equivalent to \-pathspecs on. Note that pathspecs without "=" are interpreted differently than with \fBxorriso\fR command \-add. Directories get merged with the root directory of the ISO image, other filetypes get mapped into that root directory. .br If pathspecs are given and if no output file was chosen before or during the "mkisofs" option list, then standard output (\-outdev "\-") will get into effect. If \-o points to a regular file, then it will be truncated to 0 bytes when finally writing begins. This truncation does not happen if the drive is chosen by \fBxorriso\fR commands before \-as mkisofs or after its list delimiter. Directories and symbolic links are no valid \-o targets. .br Writing to stdout is possible only if \-as "mkisofs" was among the start arguments or if other start arguments pointed the output drive to standard output. .br \-print\-size inhibits automatic image production at program end. This ban is lifted only if the pending image changes get discarded. .br Padding is counted as part of the ISO image if not option \-\-emul\-toc is given. .br If no \-iso\-level is given, then level 1 is chosen when the first file or directory is added to the image. At the same occasion directory names get allowed to violate the standard by \-compliance option allow_dir_id_ext. This may be avoided by option \-disallow_dir_id_ext. .br Option \-root is supported. Option \-old\-root is implemented by \fBxorriso\fR commands \-mkdir, \-cp_clone, \-find update_merge, and \-find rm_merge. \-root and \-old\-root set command \-disk_dev_ino to "ino_only" and \-md5 to "on", by default. \-disk_dev_ino can be set to "off" by \-\-old\-root\-no\-ino or to "on" by \-\-old\-root\-devno . \-md5 can be set to "off" by \-\-old\-root\-no\-md5 . .br Not original mkisofs options are \-\-quoted_path_list , \-\-hardlinks , \-\-acl , \-\-xattr , \-\-md5 , \-\-stdio_sync . They work like the \fBxorriso\fR commands with the same name and hardcoded parameter "on", e.g. \-acl "on". Explicit parameters are expected by \-\-stdio_sync and \-\-scdbackup_tag. .br The capability to preserve multi\-session history on overwritable media gets disabled by default. It can be enabled by using \-\-emul\-toc with the first session. See \-compliance no_emul_toc. .br \-\-sort\-weight gets as parameters a number and an iso_rr_path. The number becomes the LBA sorting weight of regular file iso_rr_path or of all regular files underneath directory iso_rr_path. (See \-find \-exec sort_weight). .br Adopted from grub\-mkisofs are \-\-protective\-msdos\-label (see \-boot_image grub partition_table=on) and \-\-modification\-date=YYYYMMDDhhmmsscc (see \-volume_date uuid). For EFI bootable GRUB boot images use \-\-efi\-boot. It performs \-boot_image grub efi_path= surrounded by two \-boot_image "any" "next". Alternative option \-e from Fedora genisoimage sets bin_path and platform_id for EFI, but performs no "next". .br For MBR bootable ISOLINUX images there is \-isohybrid\-mbr FILE, where FILE is one of the Syslinux files mbr/isohdp[fp]x*.bin . Use this instead of \-G to apply the effect of \-boot_image isolinux partition_table=on. .br \-\-boot\-catalog\-hide is \-boot_image any cat_hidden=on. .br \-mips\-boot is the same as \-boot_image any mips_path= . .br \-mipsel\-boot leads to mipsel_path= . .br \-partition_offset number is \-boot_image any partition_offset=number. .br Command \-append_partition is supported. .br \-untranslated_name_len number is \-compliance untranslated_name_len=number. .br \-\-old\-empty is \-compliance old_empty. .br The options of genisoimage Jigdo Template Extraction are recognized and performed via \fBxorriso\fR command \-jigdo. See the "Alias:" names there for the meaning of the genisoimage options. .br Personalities "\fBxorrisofs\fR", "\fBgenisoimage\fR", and "\fBgenisofs\fR" are aliases for "mkisofs". .br If \fBxorriso\fR is started with one of the leafnames "xorrisofs", "genisofs", "mkisofs", or "genisoimage", then it performs \-read_mkisofsrc and prepends \-as "genisofs" to the program arguments. I.e. all arguments will be interpreted mkisofs style until "\-\-" is encountered. From then on, arguments are interpreted as \fBxorriso\fR commands. .br \-\-no_rc as first argument of such a program start prevents interpretation of startup files. See section FILES below. .br Personality "\fBcdrecord\fR" accepts the options listed with: .br \-as cdrecord \-help \-\- .br Among them: \-v, dev=, speed=, blank=, fs=, \-eject, \-atip, padsize=, tsize=, \-isosize, \-multi, \-msinfo, \-\-grow_overwriteable_iso, write_start_address=, track source file path or "\-" for standard input as track source. .br It ignores most other options of cdrecord and cdrskin but refuses on \-audio, \-scanbus, and on blanking modes unknown to \fBxorriso\fR. .br The scope is only a single data track per session to be written to blank, overwritable, or appendable media. The medium gets closed if closing is applicable and not option \-multi is present. .br If an input drive was acquired, then it is given up. This is only allowed if no image changes are pending. .br dev= must be given as \fBxorriso\fR device address. Addresses like 0,0,0 or ATA:1,1,0 are not supported. .br If a track source is given, then an automatic \-commit happens at the end of the "cdrecord" option list. .br \-\-grow_overwriteable_iso enables emulation of multi\-session on overwritable media. To enable emulation of a TOC, the first session needs \-C 0,32 with \-as mkisofs (but no \-M) and \-\-grow_overwriteable_iso write_start_address=32s with \-as cdrecord. .br A much more elaborate libburn based cdrecord emulator is the program cdrskin. .br Personalites "\fBxorrecord\fR", "\fBwodim\fR", and "\fBcdrskin\fR" are aliases for "cdrecord". .br If \fBxorriso\fR is started with one of the leafnames "xorrecord", "cdrskin", "cdrecord", or "wodim", then it automatically prepends \-as "cdrskin" to the program arguments. I.e. all arguments will be interpreted cdrecord style until "\-\-" is encountered. From then on, arguments are interpreted as \fBxorriso\fR commands. .br \-\-no_rc as first argument of such a program start prevents interpretation of \fBxorriso\fR startup files. See section FILES below. .TP \fB\-read_mkisofsrc\fR Try one by one to open for reading: ./.mkisofsrc , $MKISOFSRC , $HOME/.mkisofsrc , $(dirname $0)/.mkisofsrc .br On success interpret the file content as of man mkisofs CONFIGURATION, and end this command. Do not try further files. The last address is used only if start argument 0 has a non\-trivial dirname. .br The reader currently interprets the following NAME=VALUE pairs: APPI (\-application_id) , PUBL (\-publisher) , SYSI (\-system_id) , VOLI (\-volid) , VOLS (\-volset_id) .br Any other lines will be silently ignored. .TP \fB\-genisoimage_completion\fR "on"|"off" Enable or disable the completion of genisoimage options during \-as mkisofs emulation. .br If enabled by "on", then unrecognized option arguments which begin by a dash '\-' get compared against the known genisoimage options, like program genisoimage does unconditionally (and undocumentedly). If the given argument matches the beginning of exactly one genisoimage option, then it gets replaced by that option. Option arguments which consist entirely of a leading dash and letters out of "dDfJlNRrTUvz" are not matched but rather interpreted as usual, i.e. as multiple options with leading dash and each single letter. If no genisoimage option is found or more than one are found, then a SORRY message is issued and the argument stays as is. .br If disabled by "off", no completion of options happens. Like with enabled completion, option arguments which consist entirely of letters out of "dDfJlNRrTUvz" are not matched but rather interpreted as multiple arguments with leading dash and each single letter. .TP \fB\-pacifier\fR behavior_code Control behavior of UPDATE pacifiers during write operations. The following behavior codes are defined: .br "xorriso" is the default format: .br Writing: sector XXXXX of YYYYYY [fifo active, nn% fill] .br "cdrecord" looks like: .br X of Y MB written (fifo nn%) [buf mmm%] .br "mkisofs" .br nn% done, estimate finish Tue Jul 15 20:13:28 2008 .br The frequency of the messages can be adjusted by .br "interval=number" .br where number gives the seconds between two messages. Permissible settings are 0.1 to 60.0. .TP \fB\-scdbackup_tag\fR list_path record_name Set the parameter "name" for a scdbackup checksum record. It will be appended in an scdbackup checksum tag to the \-md5 session tag if the image starts at LBA 0. This is the case if it gets written as first session onto a sequential medium, or piped into a program, named pipe or character device. .br If list_path is not empty then the record will also be appended to the data file given by this path. .br Program scdbackup_verify will recognize and verify tag and file record. .br An empty record_name disables this feature. .TP .B Scripting, dialog and program control features: .TP \fB\-no_rc\fR .br Only if used as first program argument this command prevents reading and interpretation of startup files. See section FILES below. .TP \fB\-options_from_file\fR fileaddress Read quoted input from fileaddress and execute it like dialog lines. Empty lines and lines which begin by # are ignored. Normally one line should hold one \fBxorriso\fR command and all its parameters. Nevertheless lines may be concatenated by a trailing backslash. .br See also section "Command processing", paragraph "Quoted input". .TP \fB\-help\fR .br Print helptext. .TP \fB\-version\fR Print program name and version, component versions, license. .TP \fB\-list_extras\fR code Tell whether certain extra features were enabled at compile time. Code "all" lists all features and a headline. Other codes pick a single feature. Code "codes" lists them. They share names with related commands (see also there): .br "acl" tells whether xorriso has an adapter for local filesystems ACLs. .br "xattr" tells whether xorriso has an adapter for local filesystems EA. .br "lfa_flags" tells whether xorriso has an adapter for local Linux file attributes (see man 1 chattr). .br "jigdo" tells whether production of Jigdo files is possible. .br "zisofs" tells whether zisofs and built\-in gzip filters are enabled. .br "external_filter" tells whether external filter processes are allowed and whether they are allowed if real user id and effective user id differ. .br "dvd_obs" tells whether 64 kB output to DVD media is default. .br "use_readline" tells whether readline may be enabled in dialog mode. .br .TP \fB\-history\fR textline Copy textline into libreadline history. .TP \fB\-status\fR mode|filter Print the current settings of \fBxorriso\fR. Modes: .br short... print only important or altered settings .br long ... print all settings including defaults .br long_history like long plus history lines .br Filters begin with '\-' and are compared literally against the output lines of \-status:long_history. A line is put out only if its start matches the filter text. No wildcards. .TP \fB\-status_history_max\fR number Set maximum number of history lines to be reported with \-status "long_history". .TP \fB\-list_delimiter\fR word Set the list delimiter to be used instead of "\-\-". It has to be a single word, must not be empty, not longer than 80 characters, and must not contain quotation marks. .br For brevity the list delimiter is referred as "\-\-" throughout this text. .TP \fB\-sh_style_result\fR "on"|"off" Make the result output of some filesystem inspection commands look more like the output of equivalent shell commands. The most important effect is to prevent the wrapping of file addresses into quotation marks with commands \-pwd \-pwdx \-ls \-lsd \-lsl \-lsdl \-lsx \-lsdx \-lslx \-lsdlx \-du \-dus \-dux \-dusx \-findx \-find .br This will make ambiguous the representation of file names which contain newline characters. On the other hand it should facilitate integration of xorriso into shell scripts which already use the corresponding shell commands. .TP \fB\-backslash_codes\fR "on"|"off"|mode[:mode] Enable or disable the interpretation of symbolic representations of special characters with quoted input, or with program arguments, or with program text output. If enabled the following translations apply: .br \\a=bell(007) \\b=backspace(010) \\e=Escape(033) \\f=formfeed(014) .br \\n=linefeed(012) \\r=carriage_return(015) \\t=tab(011) .br \\v=vtab(013) \\\\=backslash(134) \\[0\-7][0\-7][0\-7]=octal_code .br \\x[0\-9a\-f][0\-9a\-f]=hex_code \\cC=control\-C .br Translations can occur with quoted input in 3 modes: .br "in_double_quotes" translates only inside " quotation. .br "in_quotes" translates inside " and ' quotation. .br "with_quoted_input" translates inside and outside quotes. .br With the start program arguments there is mode: .br "with_program_arguments" translates program arguments. .br .br Mode "encode_output" encodes output characters. It combines "encode_results" with "encode_infos". Inside single or double quotation marks encoding applies to 8\-bit characters octal 001 to 037 , 177 to 377 and to backslash(134). Outside quotation marks some harmless ASCII control characters stay unencoded: bell(007), backspace(010), tab(011), linefeed(012), formfeed(014), carriage_return(015). .br Mode "off" is default and disables any translation. Mode "on" is "with_quoted_input:with_program_arguments:encode_output". .TP \fB\-temp_mem_limit\fR number["k"|"m"] Set the maximum size of temporary memory to be used for image dependent buffering. Currently this applies to pattern expansion, LBA sorting, restoring of hard links. .br Default is 16m = 16 MiB, minimum 64k = 64 kiB, maximum 1024m = 1 GiB. .TP \fB\-print\fR text Print a text line to the result channel which is by default stdout. .TP \fB\-print_info\fR text Print a text line to the info channel which is by default stderr. .TP \fB\-print_mark\fR text Print a text line to the mark channel which is by default directed to both, result and info channel. An empty text will cause no output at all. .TP \fB\-prompt\fR text Show text at beginning of output line and wait for the user to hit the Enter key or to send a line via stdin. .TP \fB\-sleep\fR seconds Wait for the given number of seconds before performing the next command. Expect coarse granularity no better than 1/100 seconds. .TP \fB\-errfile_log\fR mode path|channel .br If problem events are related to input files from the filesystem, then their disk_paths can be logged to a file or to output channels R or I. .br Mode can either be "plain" or "marked". The latter causes marker lines which give the time of log start, burn session start, burn session end, log end or program end. In mode "plain", only the file paths are logged. .br If path is "\-" or "\-R" then the log is directed to the result channel. Path "\-I" directs it to the info message channel. Any text that does not begin with "\-" is used as path for a file to append the log lines. .br Problematic files can be recorded multiple times during one program run. If the program run aborts then the list might not be complete because some input files might not have been processed at all. .br The errfile paths are transported as messages of very low severity "ERRFILE". This transport becomes visible with \-report_about "ALL". .TP \fB\-session_log\fR path If path is not empty it gives the address of a plain text file where a log record gets appended after each session. This log can be used to determine the start_lba of a session for mount options \-o sbsector= (on GNU/Linux) or \-s (on FreeBSD) from date or volume ID. .br Record format is: timestamp start_lba size volume\-id .br The first three items are single words, the rest of the line is the volume ID. .TP \fB\-scsi_log\fR "on"|"off" Mode "on" enables very verbose logging of SCSI commands and drive replies. Logging messages get printed to stderr, not to any of the \fBxorriso\fR output channels. .br A special property of this command is that the first \-scsi_log setting among the start arguments is in effect already when the first operations of \fBxorriso\fR begin. Only "\-scsi_log" with dash "\-" is recognized that way. .TP \fB\-end\fR .br End program after writing pending changes. .TP \fB\-rollback_end\fR Discard pending changes. End program immediately. .TP \fB#\fR any text Only in dialog or file execution mode, and only as first non\-whitespace in line: Do not execute the line but store it in readline history. .TP .B Support for frontend programs via stdin and stdout: .TP \fB\-pkt_output\fR "on"|"off" Consolidate text output on stdout and classify each line by a channel indicator: .br 'R:' for result lines, .br 'I:' for notes and error messages, .br 'M:' for \-mark texts. .br Next is a decimal number of which only bit 0 has a meaning for now. 0 means no newline at end of payload, 1 means that the newline character at the end of the output line belongs to the payload. After another colon and a blank follows the payload text. .br Example: .br I:1: enter option and parameters : .TP \fB\-logfile\fR channel fileaddress Copy output of a channel to the given file. Channel may be one of: "." for all channels, "I" for info messages, "R" for result lines, "M" for \-mark texts. .TP \fB\-mark\fR text If text is not empty it will get put out on "M" channel each time \fBxorriso\fR is ready for the next dialog line or before \fBxorriso\fR performs a command that was entered to the pager prompt. .TP \fB\-msg_op\fR opcode parameter_text This command shall facilitate extraction of particular information from the message output of other commands. It gives access to the C API function Xorriso_parse_line() and to the message sieve that is provided by the C API. Please refer to their descriptions in file xorriso.h. Further it helps to interpret the severity codes of info messages. .br Intended users are frontend programs which operate xorriso in dialog mode. .br The result output of this command is not caught by the message sieve. .br The following opcodes are defined: .br \fBstart_sieve\fR .br Install the message sieve as of Xorriso_sieve_big() and start watching program messages. The parameter_text has no meaning. .br \fBshow_sieve\fR .br Show a list of filter rule names. The parameter_text has no meaning. The list begins by a line with the return value of Xorriso_sieve_get_result() with flag bit3. If this value is larger than 0, then the next line tells the number of names. The following lines show one name each. .br \fBread_sieve\fR .br Use the parameter_text as name of a filter rule and inquire its next recorded result. See Xorriso_sieve_big() for a list of names and reply strings. .br The recorded strings are put out on result channel. They get wrapped into lines which tell their structure. The first line tells the return value of Xorriso_sieve_get_result(). The next line tells the number of strings. Each string begins by a line that tells the number of lines of the string. Then follow these lines. They are to be concatenated with a newline character between each of them. Finally the number of still available recorded results of the given name is put out. .br \fBclear_sieve\fR .br Dispose all recorded strings and continue watching program messages. The parameter_text has no meaning. .br \fBend_sieve\fR .br Dispose the sieve with its filter rules and stop watching program messages. The parameter_text has no meaning. .br \fBparse\fR .br Read a text from dialog input and submit it to Xorriso_parse_line(). The parameter_text word shall consist of several words separated by blanks. It will be necessary to use both kinds of quotation marks. .br E.g. "'ISO session :' '' 0 0 1" .br The five parameter words are: prefix, separators, max_words, flag, number_of_input_lines. The former four are handed over to Xorriso_parse_line(). The number of input lines minus one tells xorriso how many newline characters are part of the input text. .br The announced number of text lines will be read from dialog input, concatenated with a newline character between each of them, and submitted to Xorriso_parse_line() as parameter line. Note that newlines outside of quotation marks are interpreted as separators if the separators parameter is empty. .br The parsed strings are put out on result channel. They get wrapped into lines which tell their structure. The first line tells the return value of Xorriso_parse_line(). The next line tells the number of strings. Each string begins by a line that tells the number of lines of the string. Then follow these lines. They are to be concatenated with a newline character between each of them. .br If \-backslash_codes "encode_output" is enabled, then the strings undergo encoding as if they were enclosed in quotes. Escpecially each string will be put out as a single result line. .br \fBparse_bulk\fR .br Like "parse", but with the fifth parameter word being number_of_input_texts rather than number_of_input_lines. Each input text has to be preceded by a line that tells number_of_input_lines as with "parse". Then come the announced number of text lines. .br All input texts will be read before printing of result lines begins. This consumes memory in xorriso. So the number_of_input_texts should not be extremely high. On the other hand, large transactions of command, input texts, and results are desirable if connection latency is an issue. .br \fBparse_silently\fR .br Like "parse" but not issuing a prompting message. Confusing to humans. .br \fBparse_bulk_silently\fR .br Like "parse_bulk" but not issuing a prompting message. Confusing to humans. .br \fBcompare_sev\fR .br The parameter_text should contain two comma separated severity texts as issued by this program. Like "SORRY,UPDATE". See also paragraph "Exception processing". .br These two severity texts get compared and a number gets printed to the result channel. This number is 0 if both severities are equal. It is \-1 if the first severity is lower than the second one. It is 1 is the first severity is higher than the second one. .br Above example "SORRY,UPDATE" will yield 1. .br \fBlist_sev\fR .br Print to the result channel a blank separated list of all severity names. Sorted from low to high severity. .TP \fB\-named_pipe_loop\fR mode[:mode] disk_path_stdin disk_path_stdout disk_path_stderr Temporarily replace standard input, standard output and standard error by named pipes. Enter dialog mode without readline. .br Defined modes are: .br "cleanup" removes the submitted pipe files when the loop ends. .br "keep" does not delete them. This is the default. .br "buffered" reads all lines from the input pipe until EOF before it opens the output pipes and processes the input lines. .br "direct" opens the output pipes after the first input line was read. Each line is executed directly after it is read. This is the default. .br The other three parameters must either be disk paths to existing named pipes, or be "\-" to leave the according standard i/o channel unreplaced. .br xorriso will open the stdin pipe, read and execute dialog lines from it until the sender closes the pipe. The output pipes get opened depending on mode "buffered" or "direct". After all lines are executed, xorriso will close its side of the pipes and enter a new cycle of opening, reading and executing. .br If an input line consists only of the word "end_named_pipe_loop" then \-named_pipe_loop will end and further xorriso commands may be executed from other sources. .TP \fB\-launch_frontend\fR program [arguments ...] -- Start the program that is given as first parameter. Submit the other parameters as program arguments. Enable xorriso dialog mode. .br Two nameless pipe objects are created. xorriso standard input gets connected to the standard output of the started program. xorriso standard output and standard error get connected to the standard input of that program. .br xorriso will abort when the started program ends or if it cannot be started at all. In both cases it will return a non\-zero exit value. The exit value will be zero if the frontend sends \-end or \-rollback_end before ending itself. .br This command may be totaly banned at compile time. It is banned by default if xorriso runs under setuid permissions. .br The program name will not be searched in the $PATH directories. To make this clear, it must contain at least one /\-character. Best is an absolute path. .br Example: .br xorriso \-launch_frontend "$(which xorriso\-tcltk)" \-stdio \-\- .br The frontend program should first send via its standard output: .br \-mark 0 \-pkt_output on \-msg_op start_sieve \- \-reassure off .br It should be ready to decode \-pkt_output and to react on \-mark messages. Best is to increment the \-mark number after each sent command sequence and then to wait for the new number to show up in a mark message: .br ...some...commands... \-mark <incremented_number> .br Further are advised: .br \-report_about UPDATE \-abort_on NEVER .br \-iso_rr_pattern off \-disk_pattern off .br A check of the xorriso version should be done, in order to make sure that all desired features are present. .br Command \-launch_frontend will only work once per xorriso run. If no command parameters are submitted or if program is an empty text, then no program will be started but nevertheless \-launch_frontend will be irrevocably disabled. .TP \fB\-prog\fR text Use text as name of this program in subsequent messages .TP \fB\-prog_help\fR text Use text as name of this program and perform \-help. .br .SH EXAMPLES .SS .B Overview of examples: As superuser learn about available drives .br Blank medium and compose a new ISO image as batch run .br A dialog session doing about the same .br Manipulate an existing ISO image on the same medium .br Copy modified ISO image from one medium to another .br Bring a prepared ISOLINUX tree onto medium and make it bootable .br Change existing file name tree from ISO-8859-1 to UTF-8 .br Operate on storage facilities other than optical drives .br Burn an existing ISO image file to medium .br Perform multi-session runs as of cdrtools traditions .br Let xorriso work underneath growisofs .br Adjust thresholds for verbosity, exit value and program abort .br Examples of input timestrings .br Incremental backup of a few directory trees .br Restore directory trees from a particular ISO session to disk .br Try to retrieve blocks from a damaged medium .SS .B As superuser learn about available drives On Linux, FreeBSD or NetBSD consider to give rw\-permissions to those users or groups which shall be able to use the drives with \fBxorriso\fR. On Solaris use pfexec. Consider to restrict privileges of \fBxorriso\fR to "base,sys_devices" and to give r\-permission to user or group. .br $ xorriso \-device_links .br 1 \-dev '/dev/cdrom1' rwrw\-\- : 'TSSTcorp' 'DVD\-ROM SH\-D162C .br 1 \-dev '/dev/cdrw' rwrw\-\- : 'TSSTcorp' 'CDDVDW SH\-S223B' .br 2 \-dev '/dev/cdrw3' rwrw\-\- : 'HL\-DT\-ST' 'BDDVDRW_GGC\-H20L' .SS .B Blank medium and compose a new ISO image as batch run Acquire drive /dev/sr2, make medium ready for writing a new image, fill the image with the files from hard disk directories /home/me/sounds and /home/me/pictures. .br Because no \-dialog "on" is given, the program will then end by writing the session to the medium. .br $ xorriso \-outdev /dev/sr2 \\ .br \-blank as_needed \\ .br \-map /home/me/sounds /sounds \\ .br \-map /home/me/pictures /pictures .br .br The ISO image may be shaped in a more elaborate way like the following: Omit some unwanted stuff by removing it from the image directory tree. Reintroduce some wanted stuff. .br $ cd /home/me .br $ xorriso \-outdev /dev/sr2 \\ .br \-blank as_needed \\ .br \-map /home/me/sounds /sounds \\ .br \-map /home/me/pictures /pictures \\ .br \-rm_r \\ .br /sounds/indecent \\ .br '/pictures/*private*' \\ .br /pictures/confidential \\ .br \-\- \\ .br \-cd / \\ .br \-add pictures/confidential/work* \-\- .br Note that '/pictures/*private*' is a pattern for iso_rr_paths while pictures/confidential/work* gets expanded by the shell with addresses from the hard disk. Commands \-add and \-map have different parameter rules but finally the same effect: they put files into the image. .SS .B A dialog session doing about the same .br Some settings are already given as start argument. The other activities are done as dialog input. The pager gets set to 20 lines of 80 characters. .br The drive is acquired by command \-dev rather than \-outdev in order to see the message about its current content. By command \-blank this content is made ready for being overwritten and the loaded ISO image is made empty. .br In order to be able to eject the medium, the session needs to be committed explicitly. .br .B $ xorriso -dialog on -page 20 80 -disk_pattern on .br enter option and arguments : .br .B \-dev /dev/sr2 .br enter option and arguments : .br .B \-blank as_needed .br enter option and arguments : .br .B \-map /home/me/sounds /sounds -map /home/me/pictures /pictures .br enter option and arguments : .br .B \-rm_r /sounds/indecent /pictures/*private* /pictures/confidential .br enter option and arguments : .br .B \-cdx /home/me/pictures -cd /pictures .br enter option and arguments : .br .B \-add confidential/office confidential/factory .br enter option and arguments : .br .B \-du / .br enter option and arguments : .br .B \-commit_eject all -end .br .br .br .br .br .br .br .br .br .SS .B Manipulate an existing ISO image on the same medium Load image from drive. Remove (i.e. hide) directory /sounds and its subordinates. Rename directory /pictures/confidential to /pictures/restricted. Change access permissions of directory /pictures/restricted. Add new directory trees /sounds and /movies. Burn to the same medium, check whether the tree can be loaded, and eject. .br $ xorriso \-dev /dev/sr2 \\ .br \-rm_r /sounds \-\- \\ .br \-mv \\ .br /pictures/confidential \\ .br /pictures/restricted \\ .br \-\- \\ .br \-chmod go\-rwx /pictures/restricted \-\- \\ .br \-map /home/me/prepared_for_dvd/sounds_dummy /sounds \\ .br \-map /home/me/prepared_for_dvd/movies /movies \\ .br \-commit \-eject all .SS .B Copy modified ISO image from one medium to another Load image from input drive. Do the same manipulations as in the previous example. Acquire output drive and blank it. Burn the modified image as first and only session to the output drive. .br $ xorriso \-indev /dev/sr2 \\ .br \-rm_r /sounds \-\- \\ .br ... .br \-outdev /dev/sr0 \-blank as_needed \\ .br \-commit \-eject all .SS .B Bring a prepared ISOLINUX tree onto medium and make it bootable The user has already created a suitable file tree on disk and copied the ISOLINUX files into subdirectory ./boot/isolinux of that tree. Now \fBxorriso\fR can burn an El Torito bootable medium: .br $ xorriso \-outdev /dev/sr0 \-blank as_needed \\ .br \-map /home/me/ISOLINUX_prepared_tree / \\ .br \-boot_image isolinux dir=/boot/isolinux .SS .B Change existing file name tree from ISO-8859-1 to UTF-8 This example assumes that the existing ISO image was written with character set ISO\-8859\-1 but that the readers expected UTF\-8. Now a new session gets added with converted file names. Command \-changes_pending "yes" enables writing despite the lack of any manipulation command. .br In order to avoid any weaknesses of the local character set, this command pretends that it uses already the final target set UTF\-8. Therefore strange file names may appear in messages, which will be made terminal\-safe by command \-backslash_codes. .br $ xorriso \-in_charset ISO\-8859\-1 \-local_charset UTF\-8 \\ .br \-out_charset UTF\-8 \-backslash_codes on \-dev /dev/sr0 \\ .br \-changes_pending yes \-commit \-eject all .SS .B Operate on storage facilities other than optical drives Full read\-write operation is possible with regular files and block devices: .br $ xorriso \-dev /tmp/regular_file ... .br Paths underneath /dev normally need prefix "stdio:" .br $ xorriso \-dev stdio:/dev/sdb ... .br If /dev/sdb is to be used frequently and /dev/sda is the system disk, then consider to place the following lines in a \fBxorriso\fR Startup File. They allow you to use /dev/sdb without prefix and protect disk /dev/sda from \fBxorriso\fR: .br \-drive_class banned /dev/sda* .br \-drive_class harmless /dev/sdb .br Other writeable file types are supported write\-only: .br $ xorriso \-outdev /tmp/named_pipe ... .br Among the write\-only drives is standard output: .br $ xorriso \-outdev \- \\ .br ... .br | gzip >image.iso.gz .SS .B Burn an existing ISO image file to medium Actually this works with any kind of data, not only ISO images: .br $ xorriso \-as cdrecord \-v dev=/dev/sr0 blank=as_needed image.iso .SS .B Perform multi-session runs as of cdrtools traditions Between both processes there can be performed arbitrary transportation or filtering. .br The first session is written like this: .br $ xorriso \-as mkisofs prepared_for_iso/tree1 | \\ .br xorriso \-as cdrecord \-v dev=/dev/sr0 blank=fast \-multi \-eject \- .br Follow\-up sessions are written like this (the run of dd is only to give demons a chance to spoil it): .br $ m=$(xorriso \-as cdrecord dev=/dev/sr0 \-msinfo) .br $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 .br $ xorriso \-as mkisofs \-M /dev/sr0 \-C $m prepared_for_iso/tree2 | \\ .br xorriso \-as cdrecord \-v dev=/dev/sr0 \-waiti \-multi \-eject \- .br Always eject the drive tray between sessions. .br The run of xorriso \-as mkisofs will read old sessions via the CD\-ROM driver of /dev/sr0. This driver might not be aware of the changed content as long as the medium is not loaded again. In this case the previous session would not be properly assessed by xorriso and the new session would contain only the newly added files. .br Some systems have not enough patience with automatic tray loading and some demons may interfere with a first CD\-ROM driver read attempt from a freshly loaded medium. .br When loading the tray manually, wait 10 seconds after the drive has stopped blinking. .br A safe automatic way seems to be a separate run of xorriso for loading the tray with proper waiting, and a subsequent run of dd which shall offer itself to any problems caused by demons assessing the changed drive status. If this does not help, insert a run of "sleep 10" between xorriso and dd. .br This example works for multi\-session media only. Add cdrskin option \-\-grow_overwriteable_iso to all \-as cdrecord runs in order to enable multi\-session emulation on overwritable media. .SS .B Let xorriso work underneath growisofs growisofs expects an ISO formatter program which understands options \-C and \-M. If \fBxorriso\fR gets started by name "xorrisofs" then it is suitable for that. .br $ export MKISOFS="xorrisofs" .br $ growisofs \-Z /dev/dvd /some/files .br $ growisofs \-M /dev/dvd /more/files .br If no "xorrisofs" is available on your system, then you will have to create a link pointing to the \fBxorriso\fR binary and tell growisofs to use it. E.g. by: .br $ ln \-s $(which xorriso) "$HOME/xorrisofs" .br $ export MKISOFS="$HOME/xorrisofs" .br One may quit mkisofs emulation by argument "\-\-" and make use of all \fBxorriso\fR commands. growisofs dislikes options which start with "\-o" but \-outdev must be set to "\-". So use "outdev" instead: .br $ growisofs \-Z /dev/dvd \-\- outdev \- \-update_r /my/files /files .br $ growisofs \-M /dev/dvd \-\- outdev \- \-update_r /my/files /files .br growisofs has excellent burn capabilities with DVD and BD. It does not emulate session history on overwritable media, though. .SS .B Adjust thresholds for verbosity, exit value and program abort Be quite verbose, exit 32 if severity "FAILURE" was encountered, do not abort prematurely but forcibly go on until the end of commands. .br $ xorriso ... \\ .br \-report_about UPDATE \\ .br \-return_with FAILURE 32 \\ .br \-abort_on NEVER \\ .br ... .SS .B Examples of input timestrings .br As printed by program date: .B 'Thu Nov 8 14:51:13 CET 2007' .br The same without ignored parts: .B 'Nov 8 14:51:13 2007' .br The same as expected by date: .B 110814512007.13 .br Four weeks in the future: .B +4w .br The current time: .B +0 .br Three hours ago: .B \-3h .br Seconds since Jan 1 1970: .B =1194531416 .br .br .br .br .br .br .br .SS .B Incremental backup of a few directory trees This changes the directory trees /projects and /personal_mail in the ISO image so that they become exact copies of their disk counterparts. ISO file objects get created, deleted or get their attributes adjusted accordingly. .br ACL, xattr, hard links and MD5 checksums will be recorded. Accelerated comparison is enabled at the expense of potentially larger backup size. Only media with the expected volume ID or blank media are accepted. Files with names matching *.o or *.swp get excluded explicitly. .br When done with writing the new session gets checked by its recorded MD5. .br $ xorriso \\ .br \-abort_on FATAL \\ .br \-for_backup \-disk_dev_ino on \\ .br \-assert_volid 'PROJECTS_MAIL_*' FATAL \\ .br \-dev /dev/sr0 \\ .br \-volid PROJECTS_MAIL_"$(date '+%Y_%m_%d_%H%M%S')" \\ .br \-not_leaf '*.o' \-not_leaf '*.swp' \\ .br \-update_r /home/thomas/projects /projects \\ .br \-update_r /home/thomas/personal_mail /personal_mail \\ .br \-commit \-toc \-check_md5 FAILURE \-\- \-eject all .br To be used several times on the same medium, whenever an update of the two disk trees to the medium is desired. Begin with a blank medium and update it until the run fails gracefully due to lack of remaining space on the old one. .br This makes sense if the full backup leaves substantial remaining capacity on media and if the expected changes are much smaller than the full backup. To apply zisofs compression to those data files which get newly copied from the local filesystem, insert these commands immediately before \-commit : .br \-hardlinks perform_update \\ .br \-find / \-type f \-pending_data \-exec set_filter \-\-zisofs \-\- \\ .br Commands \-disk_dev_ino and \-for_backup depend on stable device and inode numbers on disk. Without them, an update run may use \-md5 "on" to match recorded MD5 sums against the current file content on hard disk. This is usually much faster than the default which compares both contents directly. .br With \fBmount\fR option \fB\-o "sbsector="\fR on GNU/Linux or \fB\-s\fR on FreeBSD or NetBSD it is possible to access the session trees which represent the older backup versions. With CD media, GNU/Linux mount accepts session numbers directly by its option "session=". .br Multi\-session media and most overwritable media written by \fBxorriso\fR can tell the sbsectors of their sessions by \fBxorriso\fR command \-toc. Used after \-commit the following command prints the matching mount command for the newly written session (here for mount point /mnt): .br \-mount_cmd "indev" "auto" "auto" /mnt .br Commands \-mount_cmd and \-mount are also able to produce the mount commands for older sessions in the table\-of\-content. E.g. as superuser: .br # osirrox \-mount /dev/sr0 "volid" '*2008_12_05*' /mnt .br .sp 1 Above example produces a result similar to \-root / \-old\-root / with mkisofs. For getting the session trees accumulated in the new sessions, let all \-update commands use a common parent directory and clone it after updating is done: .br \-update_r /home/thomas/projects /current/projects \\ .br \-update_r /home/thomas/personal_mail /current/personal_mail \\ .br \-clone /current /"$(date '+%Y_%m_%d_%H%M%S')" \\ .br The cloned tree will have a name like /2011_02_12_155700. .br .sp 1 Sessions on multi\-session media are separated by several MB of unused blocks. So with small sessions the payload capacity can become substantially lower than the overall media capacity. If the remaining space on a medium does not suffice for the next gap, the drive is supposed to close the medium automatically. .br .sp 1 \fBBetter do not use your youngest backup for \-update_r\fR. Have at least two media which you use alternatingly. So only older backups get endangered by the new write operation, while the newest backup is stored safely on a different medium. .br Always have a blank medium ready to perform a full backup in case the update attempt fails due to insufficient remaining capacity. This failure will not spoil the old medium, of course. .SS .B Restore directory trees from a particular ISO session to disk This is an alternative to mounting the medium and using normal file operations. .br First check which backup sessions are on the medium: .br $ xorriso \-outdev /dev/sr0 \-toc .br Then enable restoring of ACL, xattr and hard links. Load the desired session and copy the file trees to disk. Avoid to create /home/thomas/restored without rwx\-permission. .br $ xorriso \-for_backup \\ .br \-load volid 'PROJECTS_MAIL_2008_06_19*' \\ .br \-indev /dev/sr0 \\ .br \-osirrox on:auto_chmod_on \\ .br \-chmod u+rwx / \-\- \\ .br \-extract /projects /home/thomas/restored/projects \\ .br \-extract /personal_mail /home/thomas/restored/personal_mail \\ .br \-rollback_end .br The final command \-rollback_end prevents an error message about the altered image being discarded. .SS .B Try to retrieve blocks from a damaged medium .br $ xorriso \-abort_on NEVER \-indev /dev/sr0 \\ .br \-check_media time_limit=1800 report=blocks_files \\ .br data_to="$HOME"/dvd_copy sector_map="$HOME"/dvd_copy.map \-\- .br This can be repeated several times, if necessary with \-eject or with other \-indev drives. See the human readable part of "$HOME"/dvd_copy.map for addresses which can be used on "$HOME"/dvd_copy with mount option \-o sbsector= or \-s. .SH FILES .SS .B Program alias names: .br Normal installation of \fBxorriso\fR creates three links or copies which by their program name pre\-select certain settings: .br \fBxorrisofs\fR starts \fBxorriso\fR with \-as mkisofs emulation. .br \fBxorrecord\fR starts \fBxorriso\fR with \-as cdrecord emulation. .br \fBosirrox\fR starts with \-osirrox "on:o_excl_off" which allows further commands to copy files from ISO image to disk and to apply command \-mount to one or more of the existing ISO sessions. .SS .B Startup files: .br If not \-no_rc is given as the first argument then \fBxorriso\fR attempts on startup to read and execute lines from the following files: .br /etc/default/xorriso .br /etc/opt/xorriso/rc .br /etc/xorriso/xorriso.conf .br $HOME/.xorrisorc .br The files are read in the sequence given above, but none of them is required to exist. The line format is described with command \-options_from_file. .br If mkisofs emulation was enabled by program name "xorrisofs", "mkisofs", "genisoimage", or "genisofs", then afterwards \-read_mkisofsrc is performed, which reads .mkisofsrc files. See there. .SS .B Runtime control files: .br The default setting of \-check_media abort_file= is: .br /var/opt/xorriso/do_abort_check_media .br .SS .SH ENVIRONMENT The following environment variables influence the program behavior: .br HOME is used to find startup files of xorriso and mkisofs. .br SOURCE_DATE_EPOCH belongs to the specs of reproducible\-builds.org. It is supposed to be either undefined or to contain a decimal number which tells the seconds since january 1st 1970. If it contains a number, then it is used as time value to set the default of \-volume date "uuid", sets \-boot_image "any" "gpt_disk_guid=" to "volume_date_uuid", \-volume_date "all_file_dates" to "set_to_mtime", and \-iso_nowtime to "=$SOURCE_DATE_EPOCH". .br Startup files and program options can override the effect of SOURCE_DATE_EPOCH. .br .SS .SH SEE ALSO .TP For the mkisofs emulation of xorriso .BR xorrisofs(1) .TP For the cdrecord emulation of xorriso .BR xorrecord(1) .TP For mounting xorriso generated ISO 9660 images (-t iso9660) .BR mount(8) .TP Libreadline, a comfortable input line facility .BR readline(3) .TP Other programs which produce ISO 9660 images .BR mkisofs(8), .BR genisoimage(1) .TP Other programs which burn sessions to optical media .BR growisofs(1), .BR cdrecord(1), .BR wodim(1), .BR cdrskin(1) .TP ACL, xattr, Linux file attributes .BR getfacl(1), .BR setfacl(1), .BR getfattr(1), .BR setfattr(1), .BR lsattr(1), .BR chattr(1) .TP MD5 checksums .BR md5sum(1) .TP On FreeBSD the commands for xattr and MD5 differ .BR getextattr(8), .BR setextattr(8), .BR md5(1) .SH BUGS To report bugs, request help, or suggest enhancements for \fBxorriso\fR, please send electronic mail to the public list <bug\-xorriso@gnu.org>. If more privacy is desired, mail to <scdbackup@gmx.net>. .br Please describe what you expect \fBxorriso\fR to do, the program arguments or dialog commands by which you tried to achieve it, the messages of \fBxorriso\fR, and the undesirable outcome of your program run. .br Expect to get asked more questions before solutions can be proposed. .SH AUTHOR Thomas Schmitt <scdbackup@gmx.net> .br for libburnia\-project.org .SH COPYRIGHT Copyright (c) 2007 \- 2024 Thomas Schmitt .br Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of \fBxorriso\fR. If you make use of the license to derive modified versions of \fBxorriso\fR then you are entitled to modify this text under that same license. .SH CREDITS \fBxorriso\fR is in part based on work by Vreixo Formoso who provides libisofs together with Mario Danic who also leads the libburnia team. Vladimir Serbinenko contributed the HFS+ filesystem code and related knowledge. Thanks to Andy Polyakov who invented emulated growing, to Derek Foreman and Ben Jansens who once founded libburn. .br Compliments towards Joerg Schilling whose cdrtools served me for ten years. /* xorriso - libisoburn higher level API which creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, <scdbackup@gmx.net> Provided under GPL version 2 or later. This file contains the public API of xorriso which covers all of its operations. An example of its usage is xorriso_main.c which checks version compatibility, creates a xorriso object, initializes the libraries, and runs the command interpreters of the API to constitute the command line oriented batch and dialog tool xorriso. Alternatively to command interpreters it is possible to run all options of xorriso directly via the calls of the "Command API". The "Problem Status and Message API" shall then be used to obtain the text output of the options. Mandatory calls are: Xorriso_new(), Xorriso_startup_libraries(), Xorriso_destroy() This architecture is fully public since version 0.5.8. From then on, new features get marked by @since major.minor.micro The option calls may have older "since" marks which then tell when the corresponding command was introduced in the command interpreter. Please note that struct XorrisO and its API calls are _not_ thread-safe in general. It is not permissible to run two API calls on the same XorrisO object concurrently. The only exception is Xorriso_fetch_outlists() in order to learn about the ongoing text output of other API calls. There is a lower level of API which consists of libisofs.h, libburn.h and libisoburn.h. One should not mix those calls with the ones of xorriso.h . */ /* Important: If you add a public API function then add its name to file libisoburn/libisoburn.ver */ #ifndef Xorriso_includeD #define Xorriso_includeD yes #ifdef __cplusplus extern "C" { #endif /** Opaque handle of the xorriso runtime context */ struct XorrisO; /* This may be changed to Xorriso_GNU_xorrisO in order to create GNU xorriso under GPLv3+ derived from above GPLv2+. */ #define Xorriso_libburnia_xorrisO yes /* --------------------- Fundamental Management ------------------- */ /** These three release version numbers tell the revision of this header file and of the API which it describes. They shall be memorized by applications at build time. @since 0.5.8 */ #define Xorriso_header_version_majoR 1 #define Xorriso_header_version_minoR 5 #define Xorriso_header_version_micrO 7 /** If needed: Something like ".pl01" to indicate a bug fix. Normally empty. @since 0.5.8 */ #define Xorriso_program_patch_leveL "" /** Obtain the three release version numbers of the library. These are the numbers encountered by the application when linking with libisoburn, i.e. possibly not before run time. Better do not base the fundamental compatibility decision of an application on these numbers. For a reliable check use Xorriso__is_compatible(). @since 0.5.8 @param major The maturity version (0 for now, as we are still learning) @param minor The development goal version. @param micro The development step version. This has an additional meaning: Pare numbers indicate a version with frozen API. I.e. you can rely on the same set of features to be present in all published releases with that major.minor.micro combination. Features of a pare release will stay available and ABI compatible as long as the SONAME of libisoburn stays "1". Currently there are no plans to ever change the SONAME. Odd numbers indicate that API upgrades are in progress. I.e. new features might be already present or they might be still missing. Newly introduced features may be changed incompatibly or even be revoked before release of a pare version. So micro revisions {1,3,5,7,9} should never be used for dynamic linking unless the proper library match can be guaranteed by external circumstances. @return 1 success, <=0 might in future become an error indication */ void Xorriso__version(int *major, int *minor, int *micro); /** Check whether all features of header file xorriso.h from the given major.minor.micro revision triple can be delivered by the library version which is performing this call. if (! Xorriso__is_compatible(Xorriso_header_version_majoR, Xorriso_header_version_minoR, Xorriso_header_version_micrO, 0)) ...refuse to start the program with this dynamic library version... @since 0.5.8 @param major obtained at build time @param minor obtained at build time @param micro obtained at build time @param flag Bitfield for control purposes. Unused yet. Submit 0. @return 1= library can work for caller 0= library is not usable in some aspects. Caller must restrict itself to an earlier API version or must not use this library at all. */ int Xorriso__is_compatible(int major, int minor, int micro, int flag); /* Get the patch level text (e.g. "" or ".pl01") of the program code. @param flag unused yet, submit 0 @return readonly character string */ char *Xorriso__get_patch_level_text(int flag); /* Choose how Xorriso_startup_libraries() and the XorrisO object shall prepare for eventual signals. @param behavior Default is behavior 1. 0= no own signal handling. The main application has to do that. Do not start burn runs without any handling ! 1= use libburn signal handler. Most time with action 0. During writing, formatting, blanking: 0x30. Only usable with a single xorriso object. 2= Enable system default reaction on all signals @since 1.0.9 3= Try to ignore nearly all signals @since 1.0.9 @param flag unused yet, submit 0 @return <= 0 is error, >0 is success */ int Xorriso__preset_signal_behavior(int behavior, int flag); /* Mandatory call: Create a new xorriso object and tell it the program name to be used with messages and for decision of special behavior. @param xorriso returns the newly created XorrisO object @param progname typically argv[0] of main(). Some leafnames of the progname path have special meaning and trigger special behavior: "osirrox" allows image-to-disk copying: -osirrox "on" "xorrisofs" activates -as "mkisofs" emulation from start "genisofs" alias of "xorrisofs" "mkisofs" alias of "xorrisofs" "genisoimage" alias of "xorrisofs" "xorrecord" activates -as "cdrecord" emulation from start "cdrecord" alias of "xorrecord" "wodim" alias of "xorrecord" "cdrskin" alias of "xorrecord" @param flag unused yet, submit 0 @return >0 success , <=0 failure, no object created */ int Xorriso_new(struct XorrisO ** xorriso, char *progname, int flag); /* Note: Between Xorriso_new() and the next call Xorriso_startup_libraries() there may be called the special command interpreter Xorriso_prescan_args(). The other command interpreters may be used only after Xorriso_startup_libraries(). The same restriction applies to the calls of the Command API further below. */ /* Mandatory call: It has to be made before calling any function listed below this point. Only exception is the special command interpreter Xorriso_prescan_args(). Make global library initializations. This must be done with the first xorriso object that gets created and with the first xorriso object that gets created after Xorriso_destroy(,1). @param xorriso The context object. @param flag unused yet, submit 0 @return <=0 error , >0 success */ int Xorriso_startup_libraries(struct XorrisO *xorriso, int flag); /* Note: After library startup, you may run Command Interpreters or call functions from the Command API. Wenn all desired activities are done, you may check whether there are uncommitted changes pending, compute an exit value, destroy the XorrisO object, and exit your program. */ /* Inquire whether option -commit would make sense. @param xorriso The context object to inquire. @param flag bit0= do not return 1 if -as mkisofs -print-size was performed on the current image. @since 0.6.6 bit1= issue NOTE if return is 0, indev and outdev point to different drives, and no write run has happened @since 1.5.8 @return 0= -commit would have nothing to do 1= a new image session would emerge at -commit */ int Xorriso_change_is_pending(struct XorrisO *xorriso, int flag); /* Compute the exit value from the recorded maximum event severity. @param xorriso The context object to inquire. @param flag unused yet, submit 0 @return The computed exit value */ int Xorriso_make_return_value(struct XorrisO *xorriso, int flag); /* Mandatory call: Destroy xorriso object when it is no longer needed. @param xorriso The context object to destroy. *xorriso will become NULL. @param flag bit0= Perform global library shutdown. Use only with last xorriso object to be destroyed. @return <=0 error, >0 success */ int Xorriso_destroy(struct XorrisO **xorriso, int flag); /* --------------------- Command Interpreters ------------------- */ /* This special interpreter may be called between Xorriso_new() and Xorriso_startup_libraries(). It interprets certain commands which shall get into effect before the libraries get initialized: -abort_on , -report_about , -return_with , -scsi_log , -signal_handling This is the only occasion where command -x has an effect: -x Some commands get executed only if they are the only command in argv: -prog_help , -help The following is recognized only if it is the first of all arguments: -no_rc Some get examined for the need to redirect stdout messages: -dev , -outdev , -indev , -as Commands -backslash_codes , -list_delimiter , -add_plainly get into effect during this call. But their setting at begin of the call gets restored before the call returns. @param xorriso The context object in which to perform the commands. @param argc Number of arguments. @param argv The arguments. argv[0] contains the program name. argv[1] to argv[argc-1] contain commands and parameters. @param idx Argument cursor. When this function is called, *idx must be at least 1, argv[*idx] must be a command. *idx will iterate over commands and parameters until this function aborts or until argc is reached. @param flag bit0= do not interpret argv[1] bit1= produce FAILURE events on unknown commands @since 1.1.0 @return <0 error 0 end program 1 ok, go on */ int Xorriso_prescan_args(struct XorrisO *xorriso, int argc, char **argv, int flag); /* Read and interpret commands from eventual startup files as listed in man xorriso. @param xorriso The context object in which to perform the commands. @param flag unused yet, submit 0 @return <=0 = error 1 = success 3 = end program run (e.g. because command -end was encountered) */ int Xorriso_read_rc(struct XorrisO *xorriso, int flag); /* Check whether program arguments shall be backslash decoded. If so, then replace *argv by a new argument vector. The old one will not be freed by this call. If it is dynamic memory then you need to keep a copy of the pointer and free it yourself after this call. @since 1.3.2: This call internally interprets the commands -backslash_codes and -list_delimiter if it encounters them among the arguments. The decoding of backslashes can thus be enabled and disabled by the arguments themselves. The state of the xorriso object in respect to these commands gets preserved at the start of the call and restored when the call ends. (*argv)[0] never gets decoded. The old *argv will always be replaced by a new one. @param xorriso The context object @param argc Number of arguments. @param argv The arguments. (*argv)[0] contains the program name. (*argv)[1] to (*argv)[argc-1] contain commands and parameters If argv after the call differs from argv before the call, then one should dispose it later by: Xorriso__dispose_words(argc, argv); @param flag unused yet, submit 0 @return <= 0 error , > 0 success */ int Xorriso_program_arg_bsl(struct XorrisO *xorriso, int argc, char ***argv, int flag); /* Interpret argv as xorriso command options and their parameters. (An alternative is to call functions of the options API directly and to perform own error status evaluation. See below: Command API.) After the first command and its parameters there may be more commands and parameters. All parameters must be given in the same call as their command. @since 1.2.2: Commands may get arranged in a sequence that is most likely to make sense. E.g. image loading settings before drive aquiration, then commands for adding files, then settings for writing, then writing. This feature may be enabled by command "-x" in Xorriso_prescan_args() or by parameter flag of this call. @param xorriso The context object in which to perform the commands. @param argc Number of arguments. @param argv The arguments. argv[0] contains the program name. argv[1] to argv[argc-1] contain commands and parameters. @param idx Argument cursor. When this function is called, *idx must be at least 1, argv[*idx] must be a command. *idx will iterate over commands and parameters until this function aborts, or until argc is reached, or only once if flag bit2 is set. @param flag bit0= reserved. Indicates recursion. Submit 0. bit1= Indicates that these are the main() program start arguments. This enables their use with emulations which where set with Xorriso_new(), or argument arranging. bit2= Only execute the one command argv[*idx] and advance *idx to the next command if successful. Then return. This prevents any argument arranging. @since 1.2.2 bit3= With bit1 and not bit2: Enable argument arranging as with Xorriso_prescan_args() and command "-x". @since 1.2.2 bit4= With bit1: Surely disable argument arranging. @since 1.2.2 @return <=0 = error 1 = success 2 = problem event ignored 3 = end program run (e.g. because command -end was encountered) */ int Xorriso_interpreter(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Parse a command line into words and use them as argv for a call of Xorriso_interpreter(). Put out some info lines about the outcome. @param xorriso The context object in which to perform the commands. @param line A text of one or more words according to man xorriso paragraph "Command processing" up to and including "Backslash Interpretation". @param flag bit0 to bit15 are forwarded to Xorriso_interpreter() bit16= no pageing of info lines bit17= print === bar even if xorriso->found<0 @return see return of Xorriso_interpreter() */ int Xorriso_execute_option(struct XorrisO *xorriso, char *line, int flag); /* Parse a text line into words. This parsing obeys the same rules as command line parsing but allows to skip a prefix, to use a user provided set of separator characters, and to restrict the number of parsed words. If parameter xorriso is NULL, then this call is safe for usage by a concurrent thread while a xorriso API call is being executed. @since 1.2.6 @param xorriso The context object which provides settings for parsing and output channels for error messages. May be NULL in order to allow concurrent execution e.g. by a callback function of Xorriso_start_msg_watcher(). If xorriso is NULL then: flag bit1-bit4 are in effect even if bit0 is not set. flag bit5 and bit6 may not be set. @param line A text of one or more words according to man xorriso paragraph "Command processing" up to and including "Backslash Interpretation". @param prefix If not empty then the line will only be parsed if it begins by the prefix text. Parsing will then begin after the end of the prefix. If the prefix does not match, then 0 will be returned in *argc, argv will be NULL, and the return value will be 2. @param separators If not empty this overrides the default list of word separating characters. Default set is the one of isspace(3). @param max_words If not 0: Maximum number of words to parse. If there remains line text after the last parsed word and its following separators, then this remainder is copied unparsed into a final result word. In this case *argc will be larger than max_words by one. Note that trailing separators are considered to be followed by an empty word. @param argc Will return the number of allocated and filled word strings. @param argv Will return the array of word strings. Do not forget to dispose the allocated memory by a call to Xorriso__dispose_words(). @param flag Bitfield for control purposes bit0= Override setting of -backslash_codes. bit1-4= With bit0: backslash behavior 0= off 1= in_double_quotes 2= in_quotes 3= with_quoted_input bit5= Prepend the program name as (*argv)[0], so that *argv is suitable for Xorriso_interpreter() and other calls which expect this. Not allowed if xorriso is NULL. bit6= Issue failure message in case of return 0 Not allowed if xorriso is NULL. @return <=0 means error and invalidity of *argv: 0 = Input format error. E.g. bad quotation mark. -1 = Lack of resources. E.g. memory. -2 = Improper combination of call parameters. >0 means success but not necessarily a valid result: 1 = Result in argc and argv is valid (but may be empty by argc == 0, argv == NULL). 2 = Line did not match prefix. Result is invalid and empty. */ int Xorriso_parse_line(struct XorrisO *xorriso, char *line, char *prefix, char *separators, int max_words, int *argc, char ***argv, int flag); /* Dispose a list of strings as allocated by Xorriso_parse_line() or Xorriso_program_arg_bsl(), or Xorriso_sieve_get_result(). @since 1.2.6 @param argc A pointer to the number of allocated and filled word strings. *argc will be set to 0 by this call. @param argv A pointer to the array of word strings. *argv will be set to NULL by this call. */ void Xorriso__dispose_words(int *argc, char ***argv); /* Enter xorriso command line dialog mode, using libreadline if configured at build time and not disabled at run time. This call returns immediately if not option -dialog "on" was performed before. @param xorriso The context object in which to perform the commands. @param flag unused yet, submit 0 @return <=0 error, 1= dialog mode ended normally , 3= dialog mode ended normally,interpreter asks to end program */ /* @since 0.1.0 */ int Xorriso_dialog(struct XorrisO *xorriso, int flag); /* --------------------- Problem Status and Message API ------------------- */ /** Submit a problem message to the xorriso problem reporting and handling system. This will eventually increase problem status rank, which may at certain stages in the program be pardoned and reset to 0. The pardon is governed by Xorriso_option_abort_on() and by the anger of the affected program part. If no pardon has been given, then the problem status reaches the caller of option functions. Problem status should be inquired by Xorriso_eval_problem_status() and be reset before next option execution by Xorriso_set_problem_status(). The problem status itself does not cause the failure of option functions. But in case of failures for other reasons, a remnant overly severe problem status can cause overly harsh program reactions. @param xorriso The environment handle @param error_code The unique error code of your message. Submit 0 if you do not have reserved error codes within the libburnia project. @param msg_text Not more than 8196 characters of message text. A final newline character gets appended automatically. @param os_errno Eventual errno related to the message. Submit 0 if the message is not related to a operating system error. @param severity One of "ABORT", "FATAL", "FAILURE", "MISHAP", "SORRY", "WARNING", "HINT", "NOTE", "UPDATE", "DEBUG". Defaults to "FATAL". @param flag Bitfield for control purposes bit0= use pager (as with result) bit1= permission to suppress output @return 1 if message was delivered, <=0 if failure */ int Xorriso_msgs_submit(struct XorrisO *xorriso, int error_code, char msg_text[], int os_errno, char severity[], int flag); /** Alternative call interface of Xorriso_msgs_submit with void* instead of struct XorrisO* */ int Xorriso_msgs_submit_void(void *xorriso, int error_code, char msg_text[], int os_errno, char severity[], int flag); /** Evaluate an advise whether to abort or whether to go on with option processing. This should be called after any option function was processed. It updates the problem status by processing the library message queues and then it uses this status and the submitted return value of the option function to evaluate the situation. @param xorriso The environment handle @param ret The return value of the previously called option function @param flag bit0= do not issue own event messages bit1= take xorriso->request_to_abort as reason for abort @return Gives the advice: 2= pardon was given, go on 1= no problem, go on 0= function failed but xorriso would not abort, go on <0= do abort -1 = due to xorriso->problem_status or due to ret<0 -2 = due to xorriso->request_to_abort */ int Xorriso_eval_problem_status(struct XorrisO *xorriso, int ret, int flag); /** Set the current problem status of the xorriso handle. @param xorriso The environment handle @param severity A severity text. Empty text resets to "No Problem". @param flag Unused yet. Submit 0. @return <=0 failure (e.g. wrong severity text), 1 success. */ int Xorriso_set_problem_status(struct XorrisO *xorriso, char *severity, int flag); /* The next three functions are part of Xorriso_eval_problem_status(). You may use them to build an own advisor function. */ /** Compare two severity texts for their severeness. Unknown severity texts get defaulted to "FATAL". @since 1.2.6 @param sev1 First severity text to compare @param sev2 Second severity text to compare @return -1 sev1 is less severe than sev2 0 sev1 is equally severe to sev2 1 sev1 is more severe than sev2 */ int Xorriso__severity_cmp(char *sev1, char *sev2); /** Return a blank separated list of severity names. Sorted from low to high severity. @since 1.2.6 @param flag Bitfield for control purposes (unused yet, submit 0) @return A constant string with the severity names */ char *Xorriso__severity_list(int flag); /** Obtain the current problem status of the xorriso handle. @param xorriso The environment handle @param severity The severity text matching the current problem status @param flag Unused yet. Submit 0. @return The severity rank number. 0= no problem occurred. */ int Xorriso_get_problem_status(struct XorrisO *xorriso, char severity[80], int flag); /** Forward any pending messages from the library message queues to the xorriso message system which puts out on info channel. This registers the severity of the library events like the severity of a message submitted via Xorriso_msgs_submit(). xorriso sets the message queues of the libraries to queuing "ALL". Many inner functions of xorriso call Xorriso_process_msg_queues() on their own because they expect library output pending. Nevertheless, a loop of xorriso option calls should either call Xorriso_eval_problem_status() or Xorriso_process_msg_queues() with each cycle. @param xorriso The environment handle @param flag Unused yet. Submit 0. @return 1 on success, <=0 if failure */ int Xorriso_process_msg_queues(struct XorrisO *xorriso, int flag); /** Write a message for option -errfile_log. @param xorriso The environment handle @param error_code The unique error code of your message. Submit 0 if you do not have reserved error codes within the libburnia project. @param msg_text Not more than 8196 characters of message text. @param os_errno Eventual errno related to the message. Submit 0 if the message is not related to a operating system error. @param flag bit0-7= meaning of msg_text ( 0= ERRFILE path , for internal use mainly ) 1= mark line text (only to be put out if enabled) @return <=0 error , >0 success */ int Xorriso_process_errfile(struct XorrisO *xorriso, int error_code, char msg_text[], int os_errno, int flag); /* Message output evaluation xorriso is basically a dialog software which reacts on commands by side effects and by messages. The side effects manipulate the state of the ISO image model and of drives. This state can be inquired by commands which emit messages. There are several approaches how a program that uses xorriso via this API can receive and use the message output of xorriso. - The message sieve may be programmed to pick certain information snippets out of the visible message stream. This covers all messages on the result channel and those info channel messages which get not suppressed by command -report_about. All important info messages have severity NOTE or higher. Much of the message interpretation is supposed to happen by the sieve filter rules which describe the interesting message lines and the positions of the interesting message parts. The call Xorriso_sieve_big() installs a sieve that looks out for most model state messages which xorriso can emit. After a few commands the user will ask the sieve for certain text pieces that might have been caught. - The outlist stack may be used to catch messages in linked lists rather than putting them out on the message channels. All interpretation of the messages has to be done by the user of the xorriso API. Function Xorriso_parse_line() is intended to help with splitting up messages into words. The outlist stack is handy for catching the results of information commands with large uniform output or no well recognizable message prefix. Like -lsl, -getfacl, -status, -find ... -exec get_md5. One should push the stack before the command, pull it afterwards, examine the text list by Xorriso_lst_get_*(), and finally dispose the list. - The message watcher is a separate program thread which uses the outlist stack to catch the messages and to call user provided handler functions. These functions can use Xorriso_parse_line() too, if they submit the xorriso parameter as NULL. They may not use the struct XorrisO object in any way. Synchronization between watcher and emitters of commands can be achieved by Xorriso_peek_outlists(). The main motivation for the message watcher is to inspect and display messages of long lasting xorriso commands while they are still executing. E.g. of -commit, -blank, -format. One would normally start it before such a command and stop it afterwards. But of course, the watcher can stay activated all the time and process all message output via its handler calls. The message sieve does not interfere with outlists and message watcher. The message watcher will only see messages which are not caught by outlists which were enabled after the watcher thread was started. */ /* The programmable message sieve picks words out of the program messages of xorriso. The sieve is a collection of filter rules. Each one is defined by a call of Xorriso_sieve_add_filter(). The sieve watches the given output channels for messages which begin by the given text prefixes of the filters. Matching lines get split into words by Xorriso_parse_line() using the given separators. The words described by the filter's word index array get recorded by the filter and can be inquired by Xorriso_sieve_get_result() after one or more xorriso commands have been performed. The recorded results may be disposed by Xorriso_sieve_clear_results without giving up the sieve. The whole sieve may be disposed by Xorriso_sieve_dispose(). Default at library start is an inactive sieve without any filter rules. */ /** Add a filter rule to the message sieve. Start watching output messages, if this is not already enabled. @since 1.2.6 @param xorriso The environment handle @param name The filter name by which its recorded results shall be inquired via Xorriso_sieve_get_result() @param channels Which of the output channels the filter shall watch bit0= result channel bit1= info channel bit2= mark channel @param prefix The line start to watch for. Will also be handed over to Xorriso_parse_line(). Empty text matches all lines. If the prefix begins by '?' characters, then these match any character at the beginning of a message. The prefix of the filter rule will then be adapted to really match the line, before it gets handed over to Xorriso_parse_line(). @param separators List of separator characters for Xorriso_parse_line() @param num_words Number of word indice in word_idx @param word_idx Array with the argv indice to be picked from the the result of Xorriso_parse_line(). Must at least contain num_words elements. @param max_results If not 0, then the maximum number of line results that shall be recorded by the filter. When this number is exceeded, then results of older lines get discarded when new results get recorded. @param flag Bitfield for control purposes bit0= Last result word shall contain the remainder of the message line @return <=0 error , >0 success */ int Xorriso_sieve_add_filter(struct XorrisO *xorriso, char *name, int channels, char *prefix, char *separators, int num_words, int *word_idx, int max_results, int flag); /** Inquire recorded results from a particular filter rule. @param xorriso The environment handle @param name The filter name as given by Xorriso_sieve_add_filter() @param argc Will return the number of allocated and filled word strings. @param argv Will return the array of word strings. Do not forget to dispose the allocated memory by a call to Xorriso__dispose_words(). @param available Will return the number of results which are still available for further calls of Xorriso_sieve_get_result() with the given name. @param flag Bitfield for control purposes: bit0= Reset reading to first matching result. bit1= Only inquire number of available results. Do not allocate memory. bit2= If *argv is not NULL, then free it before attaching new memory. bit3= Do not read recorded data but rather list all filter names. @return <0 error: -1 = memory shortage -2 = no filter rule found 0 No more data available for the given name With bit3: No filter rules installed. >0 argc and argv are valid */ int Xorriso_sieve_get_result(struct XorrisO *xorriso, char *name, int *argc, char ***argv, int *available, int flag); /** Dispose all recorded results. Keep filter rules. Continue watching and recording. @since 1.2.6 @param xorriso The environment handle @param flag Unused yet. Submit 0. @return <=0 error , >0 success */ int Xorriso_sieve_clear_results(struct XorrisO *xorriso, int flag); /** Dispose all filter rules. End watching and recording. This is the default state at library startup. @since 1.2.6 @param xorriso The environment handle @param flag Unused yet. Submit 0. @return <=0 error , >0 success */ int Xorriso_sieve_dispose(struct XorrisO *xorriso, int flag); /** Install a large sieve with filters for about any interesting message of xorriso. The filter rule names are mostly the same as the prefixes they search for. If you do not find the message prefix of your desire, then you may add a filter rule by Xorriso_sieve_add_filter(). If you do not want all these filter any more, call Xorriso_sieve_dispose(). You should obtain your recorded data often and then call Xorriso_sieve_clear_results(). It is nevertheless ok to perform several different xorriso information commands and to then obtain results from the sieve. The installed filters in particular: Name Recorded values, returned by Xorriso_sieve_get_result() ------------------------------------------------------------------------ "-changes_pending" up to 1 result from -changes_pending show_status argv[0]= "yes" or "no" "? -dev" up to 10 results from -devices or -device_links (records drives with single digit index number) argv[0]= drive address argv[1]= permissions argv[2]= drive vendor argv[3]= product id "?? -dev" up to 90 results from -devices or -device_links (records drives with double digit index number) argv[0]= drive address argv[1]= permissions argv[2]= drive vendor argv[3]= product id "Abstract File:" up to 1 result from -pvd_info argv[0]= file name (Note: prefix is "Abstract File: ") "After commit :" up to 1 result from -tell_media_space argv[0]= number of blocks with "s" appended "App Id :" up to 1 result from -pvd_info argv[0]= id (Note: prefix is "App Id : ") "Biblio File :" up to 1 result from -pvd_info argv[0]= file name (Note: prefix is "Biblio File : ") "Build timestamp :" up to 1 result from -version argv[0]= timestamp (Note: prefix is "Build timestamp : ") "CopyrightFile:" up to 1 result from -pvd_info argv[0]= file name (Note: prefix is "CopyrightFile: ") "Creation Time:" up to 1 result from -pvd_info argv[0]= YYYYMMDDhhmmsscc (Note: prefix is "Creation Time: ") "DVD obs 64 kB:" up to 1 result from -list_extras argv[0]= "yes" or "no" "Drive access: " up to 2 result from -dev, -indev, -toc argv[0]= "exclusive", "shared" argv[1]= "readonly","restricted" "Drive current:" up to 2 results from -dev, -indev, -toc, others argv[0]= command ("-dev", "-outdev", "-indev") argv[1]= drive address "Drive type :" up to 2 results from -toc argv[0]= vendor argv[1]= product argv[2]= revision "Eff. Time :" up to 1 result from -pvd_info argv[0]= YYYYMMDDhhmmsscc (Note: prefix is "EffectiveTime: ") "Expir. Time :" up to 1 result from -pvd_info argv[0]= YYYYMMDDhhmmsscc (Note: prefix is "Expir. Time : ") "Ext. filters :" up to 1 result from -list_extras argv[0]= "yes" or "no" , possibly more info (Note: prefix is "Ext. filters : ") "File damaged :" up to 10000 results from -find ... -exec report_damage argv[0]= damage start byte in file argv[1]= damage range size in file argv[2]= file size argv[3]= path in ISO image "File data lba:" up to 10000 results from -find ... -exec report_lba argv[0]= extent number (all extents of same path together are the content of one file) argv[1]= start block number of extent argv[2]= number of blocks of extent argv[3]= overall file content size in all extents argv[4]= path in ISO image "Format idx :" up to 100 results from -list_formats argv[0]= index argv[1]= MMC code argv[2]= number of blocks with "s" appended argv[3]= roughly the size in MiB (Note: prefix is "Format idx ") "Format status:" up to 1 result from -list_formats argv[0]= status argv[1]= capacity "ISO session :" up to 10000 results from -toc argv[0]= Idx argv[1]= sbsector argv[2]= Size argv[3]= Volume Id "Image size :" up to 1 result from -print_size argv[0]= number of blocks with "s" appended "Indev feature:" up to 10000 results from -assess_indev_features plain argv[0]= name argv[1]= value "Jigdo files :" up to 1 result from -list_extras argv[0]= "yes" or "no" "Local ACL :" up to 1 result from -list_extras argv[0]= "yes" or "no" "Local xattr :" up to 1 result from -list_extras argv[0]= "yes" or "no" "MD5 MISMATCH:" up to 10000 results from -check_md5* argv[0]= path of mismatching file "MD5 tag range:" up to 10000 results from -check_media argv[0]= lba argv[1]= size in blocks argv[2]= result text (starting with "+", "-", or "0") "Media blocks :" up to 2 results from -toc argv[0]= readable argv[1]= writable argv[2]= overall "Media current:" up to 2 results from -dev, -indev, -toc, others argv[0]= media type / MMC profile name (Note: prefix is "Media current: " which eats extra blank) "Media nwa :" up to 1 result from -toc argv[0]= next writable address "Media product:" up to 2 results from -toc argv[0]= product id argv[1]= manufacturer "Media region :" up to 10000 results from -check_media argv[0]= lba argv[1]= size in blocks argv[2]= quality text (starting with "+", "-", or "0") "Media space :" up to 1 result from -tell_media_space argv[0]= number of blocks with "s" appended "Media status :" up to 2 results from -dev, -indev, -toc, others argv[0]= status description (Note: prefix is "Media status : ") "Media summary:" up to 2 results from -dev, -indev, -toc, others argv[0]= sessions argv[1]= data blocks (full count) argv[2]= data (with unit letter k,m,g) argv[3]= free (with unit letter k,m,g) "Modif. Time :" up to 1 result from -pvd_info argv[0]= YYYYMMDDhhmmsscc (Note: prefix is "Modif. Time : ") "Overburnt by :" up to 2 results from -toc argv[0]= number of readable blocks beyond ATIP leadout (Note: only reported with overburnt CD-RW) "PVD address :" up to 1 result from -pvd_info argv[0]= block address with "s" appended "Preparer Id :" up to 1 result from -pvd_info argv[0]= id (Note: prefix is "Preparer Id : ") "Profile :" up to 256 results from -list_profiles argv[0]= MMC code argv[1]= profile name in round brackets possibly appended: " (current)" "Publisher Id :" up to 1 result from -pvd_info argv[0]= id (Note: prefix is "Publisher Id : ") "Readline :" up to 1 result from -list_extras argv[0]= "yes" or "no" "Size lower :" up to 1 result from -findx ... -exec estimate_size argv[0]= size with appended "s" "Size upper :" up to 1 result from -findx ... -exec estimate_size argv[0]= size with appended "s" "System Id :" up to 1 result from -pvd_info argv[0]= id (Note: prefix is "System Id : ") "Version timestamp :" up to 1 result from -version argv[0]= timestamp "Volume Id :" up to 1 result from -pvd_info argv[0]= id (Note: Not output from -dev or -toc but from -pvd_info) "Volume Set Id:" up to 1 result from -pvd_info argv[0]= id (Note: prefix is "Volume Set Id: ") "Volume id :" up to 2 results from -dev, -indev, -toc, others argv[0]= volume id (Note: Not output from -pvd_info but from -dev or -toc) "Write speed :" up to 100 results from -list_speeds argv[0]= kilobytes per second argv[1]= speed factor "Write speed H:" up to 1 result from -list_speeds see "Write speed :" "Write speed L:" up to 1 result from -list_speeds see "Write speed :" "Write speed h:" up to 1 result from -list_speeds see "Write speed :" "Write speed l:" up to 1 result from -list_speeds see "Write speed :" "libburn in use :" up to 1 result from -version argv[0]= version text argv[1]= minimum version requirement "libburn OS adapter:" up to 1 result from -version argv[0]= adapter description (Note: prefix is "libburn OS adapter: ") "libisoburn in use :" up to 1 result from -version argv[0]= version text argv[1]= minimum version requirement "libisofs in use :" up to 1 result from -version argv[0]= version text argv[1]= minimum version requirement "libjte in use :" up to 1 result from -version argv[0]= version text argv[1]= minimum version requirement "xorriso version :" up to 1 result from -version argv[0]= version text "zisofs :" up to 1 result from -list_extras argv[0]= "yes" or "no" ------------------------------------------------------------------------ @since 1.2.6 @param xorriso The environment handle @param flag Unused yet. Submit 0. @return <=0 error , >0 success */ int Xorriso_sieve_big(struct XorrisO *xorriso, int flag); /* The outlist stack allows to redirect the info and result messages from their normal channels into a pair of string lists which can at some later time be retrieved by the application. These redirection caches can be stacked to allow stacked applications. xorriso itself uses them for internal purposes. The call Xorriso_start_msg_watcher() starts a concurrent thread which uses outlists to collect messages and to hand them over by calling application provided functions. */ /* A list item able of forming double chained lists */ struct Xorriso_lsT; /** Maximum number of stacked redirections */ #define Xorriso_max_outlist_stacK 32 /** Enable a new redirection of info and/or result channel. The normal message output and eventual older redirections will not see new messages until the redirection is ended by a call to Xorriso_pull_outlists() with the stack_handle value returned by this call. If Xorriso_option_pkt_output() is set to "on", then it will consolidate output in the result_list of Xorriso_fetch_outlists() and Xorriso_pull_outlists(). @param xorriso The environment handle @param stack_handle returns an id number which is unique as long as its redirection is stacked. Do not interpret it and do not use it after its redirection was pulled from the stack. @param flag Bitfield for control purposes bit0= redirect result channel bit1= redirect info channel If bit0 and bit1 are 0, both channels get redirected. @return 1 on success, <=0 if failure */ int Xorriso_push_outlists(struct XorrisO *xorriso, int *stack_handle, int flag); /** Obtain the currently collected text messages of redirected info and result channel. The messages are handed out as two lists. Both lists have to be disposed via Xorriso_lst_destroy_all() when they are no longer needed. The message lists are either NULL or represented by their first Xorriso_lsT item. This call is safe for being used by a concurrent thread while a xorriso API call is being executed on the same struct XorrisO. In such a situation, it should not be used with high frequency in order not to hamper the ongoing xorriso operation by blocking its message output facility. A hundred times per second should be enough. @since 1.2.6 @param xorriso The environment handle @param stack_handle An id number returned by Xorriso_push_outlists() and not yet revoked by Xorriso_pull_outlists(). Submit -1 to address the most recent valid id. @param result_list Result and mark messages (usually directed to stdout) @param info_list Info and mark messages (usually directed to stderr) @param flag Bitfield for control purposes bit0= fetch result channel bit1= fetch info channel If bit0 and bit1 are 0, both channels get fetched. @return 1 on success, <=0 if failure */ int Xorriso_fetch_outlists(struct XorrisO *xorriso, int stack_handle, struct Xorriso_lsT **result_list, struct Xorriso_lsT **info_list, int flag); /** Inquire whether messages are pending in redirected result and info channel. This may be used to determine whether a concurrent message watcher already has obtained all pending messages. @since 1.2.6 @param xorriso The environment handle @param stack_handle An id number returned by Xorriso_push_outlists() and not yet revoked by Xorriso_pull_outlists(). Submit -1 to address the most recent valid id. @param timeout Number of seconds after which to return despite flag bit 2 @param flag Bitfield for control purposes bit0= fetch result channel bit1= fetch info channel bit2= wait and retry until return is 0 or -1 If bit0 and bit1 are 0, both channels get fetched. @return If > 0: bit0= messages are pending in outlists bit1= message watcher is processing fetched messages Else: 0= no messages are pending anywhere -1= inappropriate stack_handle -2= locking failed */ int Xorriso_peek_outlists(struct XorrisO *xorriso, int stack_handle, int timeout, int flag); /** Disable the redirection given by stack_handle. If it was the current receiver of messages then switch output to the next older redirection, or to the normal channels if no redirections are stacked any more. The messages collected by the disabled redirection are handed out as two lists. Both lists have to be disposed via Xorriso_lst_destroy_all() when they are no longer needed. The message lists are either NULL or represented by their first Xorriso_lsT item. @param xorriso The environment handle @param stack_handle An id number returned by Xorriso_push_outlists() and not yet revoked by Xorriso_pull_outlists(). This handle is invalid after the call. Submit -1 to address the most recent valid id. @param result_list Result and mark messages (usually directed to stdout) @param info_list Info and mark messages (usually directed to stderr) @param flag unused yet, submit 0 @return 1 on success, <=0 if failure */ int Xorriso_pull_outlists(struct XorrisO *xorriso, int stack_handle, struct Xorriso_lsT **result_list, struct Xorriso_lsT **info_list, int flag); /** Redirect output by Xorriso_push_outlists() and start a thread which fetches this output and performs a call of a given function with each message that is obtained. @since 1.2.6 @param xorriso The environment handle @param result_handler Pointer to the function which shall be called with each result message. A NULL pointer causes output to be directed to stdout or to be interpreted as -pkt_output format if this is enabled by Xorriso_option_pkt_output(). The function should return 1. A return value of -1 urges not to call again with further lines. @param result_handle The first argument of (*result_handler)(). It shall point to a memory object that knows all necessary external parameters for running (*result_handler)(). Submit NULL if result_handler is NULL. @param info_handler Pointer to the function which shall be called with each info message. A NULL pointer causes output to be directed to stderr or to -as mkisofs -log-file. The function should return 1. A return value of -1 urges not to call again with further lines. @param info_handle The first argument of (*info_handler)(). It shall point to a memory object that knows all necessary external parameters for running (*info_handler)(). Submit NULL if info_handler is NULL. @param flag unused yet, submit 0 @return 1 on success, <=0 if failure (e.g. there is already a watcher active) */ int Xorriso_start_msg_watcher(struct XorrisO *xorriso, int (*result_handler)(void *handle, char *text), void *result_handle, int (*info_handler)(void *handle, char *text), void *info_handle, int flag); /** Revoke output redirection by Xorriso_start_msg_watcher() and end the watcher thread. If text messages are delivered when Xorriso_pull_outlists() is called, then they get put out through the active handler functions. @since 1.2.6 @param xorriso The environment handle @param flag Bitfield for control purposes: bit0= do not issue SORRY message if no message watcher is active @return 1 on success, <=0 if failure */ int Xorriso_stop_msg_watcher(struct XorrisO *xorriso, int flag); /** Obtain the text message from the current list item. @param entry The current list item @param flag unused yet, submit 0 @return Pointer to the text content of the list item. This pointer does not have to be freed. */ char *Xorriso_lst_get_text(struct Xorriso_lsT *entry, int flag); /** Obtain the address of the next item in the chain of messages. An iteration over the output of Xorriso_pull_outlists() starts at the returned result_list or info_list and ends when this function returns NULL. @param entry The current list item @param flag unused yet, submit 0 @return Pointer to the next list item or NULL if end of list. This pointer does not have to be freed. */ struct Xorriso_lsT *Xorriso_lst_get_next(struct Xorriso_lsT *entry, int flag); /** Obtain the address of the previous item in the chain of messages. @param entry The current list item @param flag unused yet, submit 0 @return Pointer to the previous list item or NULL if start of list. This pointer does not have to be freed. */ struct Xorriso_lsT *Xorriso_lst_get_prev(struct Xorriso_lsT *entry, int flag); /** Destroy all list items which are directly or indirectly connected to the given link item. All pointers obtained by Xorriso_lst_get_text() become invalid by this. Apply this to each of the two list handles obtained by Xorriso_pull_outlists() when the lists are no longer needed. @param lstring *lstring will be freed and set to NULL. It is not dangerous to submit a pointer to a NULL-pointer. @param flag unused yet, submit 0 @return -1= lstring was NULL (i.e. wrong use of this call), 0= *lstring was already NULL, 1= item actually disposed */ int Xorriso_lst_destroy_all(struct Xorriso_lsT **lstring, int flag); /* ---------------------------- Command API ------------------------ */ /* See man 1 xorriso for explanation of the particular commands */ /* Before each call to a command function, there should happen: Xorriso_set_problem_status() with empty severity text. After each call to a command function, there should happen: Xorriso_eval_problem_status() One should follow its eventual advice to abort. Commands with a varying number of arguments get them passed like Xorriso_interpreter(). E.g.: int Xorriso_option_add(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); The command functions will begin to read the arguments at position *idx and will see the list end either at the next argv which contains the -list_delimiter text or at argv[argc-1]. After the call, *idx will be the index of the first not yet interpreted argv. Do not set any flag bits which are not described by "@param flag". I.e. if flag is not mentioned, then submit 0. Yet undefined flag bits might get a meaning in future. Unset bits will then produce the traditional behavior, whereas set bits might bring surprises to inadverted callers. The API is available @since 0.5.8 Earlier "since" marks refer to availability in the command interpreter. */ /* Command -abort_on */ /* @since 0.1.0 */ int Xorriso_option_abort_on(struct XorrisO *xorriso, char *severity, int flag); /* Command -abstract_file */ /* @since 0.6.0 */ int Xorriso_option_abstract_file(struct XorrisO *xorriso, char *name, int flag); /* Command -acl "on"|"off" */ /* @since 0.3.4 */ int Xorriso_option_acl(struct XorrisO *xorriso, char *mode, int flag); /* Command -add */ /* @param flag bit0=do not report the added item bit1=do not reset pacifier, no final pacifier message */ /* @since 0.1.0 */ int Xorriso_option_add(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -add_plainly "on"|"off" */ /* @since 0.1.0 */ int Xorriso_option_add_plainly(struct XorrisO *xorriso, char *mode, int flag); /* Command -alter_date, -alter_date_r */ /* @param flag bit0=recursive (-alter_date_r) */ /* @since 0.1.0 */ int Xorriso_option_alter_date(struct XorrisO *xorriso, char *time_type, char *timestring, int argc, char **argv, int *idx, int flag); /* Command -append_partition */ /* @since 0.6.4 */ int Xorriso_option_append_partition(struct XorrisO *xorriso, char *partno_text, char *type_text, char *image_path, int flag); /* Command -application_id */ /* @since 0.3.0 */ int Xorriso_option_application_id(struct XorrisO *xorriso, char *name, int flag); /* Command -application_use */ /* @since 1.3.2 */ int Xorriso_option_application_use(struct XorrisO *xorriso, char *path, int flag); /* Command -as */ /* @param flag bit0=do not report the added item bit1=do not reset pacifier, no final pacifier message */ /* @since 0.1.2 */ int Xorriso_option_as(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -assert_volid */ /* @since 0.3.2 */ int Xorriso_option_assert_volid(struct XorrisO *xorriso, char *pattern, char *severity, int flag); /* Command -assess_indev_features */ /* @since 1.5.6 */ int Xorriso_option_assess_indev_features(struct XorrisO *xorriso, char *mode, int flag); /* Command -auto_charset "on"|"off" */ /* @since 0.3.8 */ int Xorriso_option_auto_charset(struct XorrisO *xorriso, char *mode, int flag); /* Command -backslash_codes */ /* @since 0.3.0 */ int Xorriso_option_backslash_codes(struct XorrisO *xorriso, char *mode, int flag); /* Command -ban_stdio_write */ /* @since 0.1.0 */ int Xorriso_option_ban_stdio_write(struct XorrisO *xorriso, int flag); /* Command -biblio_file */ /* @since 0.6.0 */ int Xorriso_option_biblio_file(struct XorrisO *xorriso, char *name, int flag); /* Command -blank and -format */ /* @param flag bit0= format rather than blank @return <=0 error , 1 success, 2 revoked by -reassure */ /* @since 0.1.0 */ int Xorriso_option_blank(struct XorrisO *xorriso, char *mode, int flag); /* Command -boot_image */ /* @since 0.1.0 */ int Xorriso_option_boot_image(struct XorrisO *xorriso, char *form, char *treatment, int flag); /* Command -calm_drive */ /* @since 0.4.2 */ int Xorriso_option_calm_drive(struct XorrisO *xorriso, char *which, int flag); /* Command -cd alias -cdi */ /* @since 0.1.0 */ int Xorriso_option_cdi(struct XorrisO *xorriso, char *iso_rr_path, int flag); /* Command -cdx */ /* @since 0.1.0 */ int Xorriso_option_cdx(struct XorrisO *xorriso, char *disk_path, int flag); /* Command -changes_pending */ /* @since 1.2.2 */ int Xorriso_option_changes_pending(struct XorrisO *xorriso, char *state, int flag); /* Commands -charset, -in_charset, -out_charset, -local_charset */ /* @param flag bit0= set in_charset bit1= set out_charset bit2= set local_charset */ /* @since 0.3.0 */ int Xorriso_option_charset(struct XorrisO *xorriso, char *name, int flag); /* Commands -chattr alias -chattri and -chattr_r alias -chattr_ri */ /* @param flag bit0=recursive -chattr_r */ /* @since 1.5.8 */ int Xorriso_option_chattri(struct XorrisO *xorriso, char *chattr_text, int argc, char **argv, int *idx, int flag); /* Command -check_md5 and -check_md5_r @param flag bit0= issue summary message bit1= do not reset pacifier, no final pacifier message bit2= do not issue pacifier messages at all bit3= recursive: -check_md5_r */ /* @since 0.4.2 */ int Xorriso_option_check_md5(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -check_media */ /* @since 0.2.4 */ int Xorriso_option_check_media(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -check_media_defaults */ /* @since 0.2.6 */ int Xorriso_option_check_media_defaults(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -chgrp alias -chgrpi , chgrp_r alias chgrp_ri */ /* @param flag bit0=recursive (-chgrp_r) */ /* @since 0.1.0 */ int Xorriso_option_chgrpi(struct XorrisO *xorriso, char *gid, int argc, char **argv, int *idx, int flag); /* Command -chmod alias -chmodi , -chmod_r alias chmod_ri */ /* @param flag bit0=recursive (-chmod_r) */ /* @since 0.1.0 */ int Xorriso_option_chmodi(struct XorrisO *xorriso, char *mode, int argc, char **argv, int *idx, int flag); /* Command -chown alias -chowni , chown_r alias chown_ri */ /* @param flag bit0=recursive (-chown_r) */ /* @since 0.1.0 */ int Xorriso_option_chowni(struct XorrisO *xorriso, char *uid, int argc, char **argv, int *idx, int flag); /* Command -clone */ /* @since 1.0.2 */ int Xorriso_option_clone(struct XorrisO *xorriso, char *origin, char *dest, int flag); /* Command -close "on"|"off"| @since 1.3.4 "as_needed" */ /* @since 0.1.0 */ int Xorriso_option_close(struct XorrisO *xorriso, char *mode, int flag); /* Command -close_damaged */ /* @since 1.1.0 */ int Xorriso_option_close_damaged(struct XorrisO *xorriso, char *mode, int flag); /* Command -close_filter_list */ /* @since 0.3.8 */ int Xorriso_option_close_filter_list(struct XorrisO *xorriso, int flag); /* Command -commit */ /* @param flag bit0= leave indrive and outdrive acquired as they were, i.e. do not acquire outdrive as new in-out-drive bit1= do not perform eventual -reassure @return <=0 error , 1 success, 2 revoked by -reassure */ /* @since 0.1.0 */ int Xorriso_option_commit(struct XorrisO *xorriso, int flag); /* Command -commit_eject */ /* @return <=0 error , 1 success, 2 revoked by -reassure */ /* @since 0.1.0 */ int Xorriso_option_commit_eject(struct XorrisO *xorriso, char *which, int flag); /* Command -compare and -compare_r @param flag bit0= issue summary message bit1= do not reset pacifier, no final pacifier message bit2= do not issue pacifier messages at all bit3= recursive: -compare_r */ /* @since 0.1.2 */ int Xorriso_option_compare(struct XorrisO *xorriso, char *disk_path, char *iso_path, int flag); /* Command -compliance */ /* @since 0.3.0 */ int Xorriso_option_compliance(struct XorrisO *xorriso, char *mode, int flag); /* Command -concat */ /* @since 1.3.8 */ int Xorriso_option_concat(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -copyright_file */ /* @since 0.6.0 */ int Xorriso_option_copyright_file(struct XorrisO *xorriso, char *name, int flag); /* Command -cp_clone */ /* @since 1.0.2 */ int Xorriso_option_cp_clone(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -cpr alias -cpri */ /* @since 0.1.0 */ int Xorriso_option_cpri( struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -cpx , -cpax, -cp_rx , -cp_rax */ /* @param flag bit0= recursive (-cp_rx, -cp_rax) @since 0.2.0 bit1= full property restore (-cpax, -cp_rax) @since 0.2.0 */ /* @since 0.1.8 */ int Xorriso_option_cpx(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -cut_out */ /* @since 0.1.2 */ int Xorriso_option_cut_out(struct XorrisO *xorriso, char *disk_path, char *start, char *count, char *iso_rr_path, int flag); /* Command -dev , -indev, -outdev */ /* @param flag bit0=use as indev , bit1= use as outdev @return <=0 error , 1 success, 2 revoked by -reassure */ /* @since 0.1.0 */ int Xorriso_option_dev(struct XorrisO *xorriso, char *adr, int flag); /* Command -data_cache_size */ /* @since 1.2.2 */ int Xorriso_option_data_cache_size(struct XorrisO *xorriso, char *num_tiles, char *tile_blocks, int flag); /* Command -devices */ /* @param flag bit0= perform -device_links rather than -devices @since 1.1.4 @return <=0 error , 1 success, 2 revoked by -reassure */ /* @since 0.1.0 */ int Xorriso_option_devices(struct XorrisO *xorriso, int flag); /* Command -dialog "on"|"off" */ /* @since 0.2.8 */ /* (since 0.1.0 there was -dialog and -dialog_reset without arg) */ int Xorriso_option_dialog(struct XorrisO *xorriso, char *mode, int flag); /* Command -disk_dev_ino "on"|"off" */ /* @since 0.3.4 */ int Xorriso_option_disk_dev_ino(struct XorrisO *xorriso, char *mode, int flag); /* Command -disk_pattern "on"|"ls"|"off" */ /* @since 0.1.0 */ int Xorriso_option_disk_pattern(struct XorrisO *xorriso, char *mode, int flag); /* Command -displacement [-]offset */ /* @since 0.6.6 */ int Xorriso_option_displacement(struct XorrisO *xorriso, char *value, int flag); /* Command -drive_access "exclusive"|"shared":"readonly"|"unrestricted" */ /* @since 1.5.2 */ int Xorriso_option_drive_access(struct XorrisO *xorriso, char *mode, int flag); /* Command -drive_class */ /* @since 0.3.2 */ int Xorriso_option_drive_class(struct XorrisO *xorriso, char *d_class, char *pattern, int flag); /* Command -dummy "on"|"off" */ /* @since 0.1.0 */ int Xorriso_option_dummy(struct XorrisO *xorriso, char *mode, int flag); /* Command -dvd_obs "default"|"32k"|"64k" */ /* @since 0.4.8 */ int Xorriso_option_dvd_obs(struct XorrisO *xorriso, char *obs, int flag); /* Command -early_stdio_test */ /* @since 1.0.6 */ int Xorriso_option_early_stdio_test(struct XorrisO *xorriso, char *mode, int flag); /* Command -ecma119_map */ /* @since 1.4.2 */ int Xorriso_option_ecma119_map(struct XorrisO *xorriso, char *mode, int flag); /* Command -eject */ /* @param flag bit0=do not report toc of eventually remaining drives */ /* @since 0.1.0 */ int Xorriso_option_eject(struct XorrisO *xorriso, char *which, int flag); /* Command -end , and -rollback_end */ /* @param flag bit0= discard pending changes @return <=0 error , 1 success, 2 revoked by -reassure */ /* @since 0.1.0 */ int Xorriso_option_end(struct XorrisO *xorriso, int flag); /* Command -errfile_log marked|plain path|-|"" */ /* @since 0.1.2 */ int Xorriso_option_errfile_log(struct XorrisO *xorriso, char *mode, char *path, int flag); /* Command -error_behavior */ /* @since 0.1.6 */ int Xorriso_option_error_behavior(struct XorrisO *xorriso, char *occasion, char *behavior, int flag); /* Command -external_filter */ /* @since 0.3.8 */ int Xorriso_option_external_filter(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -extract , -extract_single */ /* @param flag bit0=do not report the restored item bit1=do not reset pacifier, no final pacifier message bit5= -extract_single: do not insert directory tree */ /* @since 0.2.0 */ int Xorriso_option_extract(struct XorrisO *xorriso, char *disk_path, char *iso_path, int flag); /* Command -extract_boot_images */ /* @sice 1.5.4 */ int Xorriso_option_extract_boot_images(struct XorrisO *xorriso, char *disk_dir_path, int flag); /* Command -extract_cut */ /* @since 0.2.6 */ int Xorriso_option_extract_cut(struct XorrisO *xorriso, char *iso_rr_path, char *start, char *count, char *disk_path, int flag); /* Command -file_name_limit */ /* @since 1.4.2 */ int Xorriso_option_file_name_limit(struct XorrisO *xorriso, char *value, int flag); /* Command -file_size_limit */ /* @since 0.2.6 */ int Xorriso_option_file_size_limit(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -find alias -findi, and -findx */ /* @param flag bit0= -findx rather than -findi bit1= do not reset pacifier, no final pacifier message do not reset find_compare_result */ /* @since 0.1.0 */ int Xorriso_option_find(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -follow */ /* @since 0.1.0 */ int Xorriso_option_follow(struct XorrisO *xorriso, char *mode, int flag); /* Command -for_backup is a shortcut for Xorriso_option_hardlinks(xorriso, "on", 0); Xorriso_option_acl(xorriso, "on", 0); Xorriso_option_xattr(xorriso, "any", 0); Xorriso_option_md5(xorriso, "on", 0); Xorriso_option_lfa_flags(xorriso, "default:on:restore_mask=aAcCdDijmPsStTux", 0); */ /* @since 0.4.0 */ /* xattr "any" @since 1.5.0 lfa_flags "default:on:restore_mask=aAcCdDijmPsStTux" @since 1.5.8 */ /* Command -for_backup */ /* (available as command since 0.4.0, but not as API call) */ /* @since 1.5.8 */ int Xorriso_option_for_backup(struct XorrisO *xorriso, int flag); /* Command -fs */ /* @since 0.1.0 */ int Xorriso_option_fs(struct XorrisO *xorriso, char *size, int flag); /* Command -genisoimage_completion */ /* @since 1.5.8 */ int Xorriso_option_genisoimage_completion(struct XorrisO *xorriso, char *mode, int flag); /* Commands -getfacl alias -getfacli, -getfacl_r alias -getfacl_ri -getfattr alias getfattri */ /* @param flag bit0=recursive -getfacl_r bit1= getfattr rather than getfacl bit3= with bit1: do not ignore eventual non-user attributes */ /* @since 0.3.4 */ int Xorriso_option_getfacli(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -gid */ /* @since 0.1.0 */ int Xorriso_option_gid(struct XorrisO *xorriso, char *gid, int flag); /* Command -grow_blindly */ /* @since 0.2.2 */ int Xorriso_option_grow_blindly(struct XorrisO *xorriso, char *msc2, int flag); /* Command -hardlinks "on"|"off" */ /* @since 0.4.0 */ int Xorriso_option_hardlinks(struct XorrisO *xorriso, char *mode, int flag); /* Command -help and part of -prog_help */ /* @since 0.1.0 */ int Xorriso_option_help(struct XorrisO *xorriso, int flag); /* Option -hfsplus "on"|"off" */ /* @since 1.2.4 */ int Xorriso_option_hfsplus(struct XorrisO *xorriso, char *mode, int flag); /* Command -hide */ /* @since 0.6.0 */ int Xorriso_option_hide(struct XorrisO *xorriso, char *hide_state, int argc, char **argv, int *idx, int flag); /* Command -history */ /* @since 0.1.0 */ int Xorriso_option_history(struct XorrisO *xorriso, char *line, int flag); /* Command -iso_nowtime "dynamic"|timespec */ /* @since 1.5.2 */ int Xorriso_option_iso_nowtime(struct XorrisO *xorriso, char *text, int flag); /* Command -iso_rr_pattern "on"|"ls"|"off" */ /* @since 0.1.0 */ int Xorriso_option_iso_rr_pattern(struct XorrisO *xorriso, char *mode, int flag); /* Command -jigdo aspect argument */ /* @since 0.6.4 */ int Xorriso_option_jigdo(struct XorrisO *xorriso, char *aspect, char *arg, int flag); /* Command -joliet "on"|"off" */ /* @since 0.1.0 */ int Xorriso_option_joliet(struct XorrisO *xorriso, char *mode, int flag); /* Command -joliet_map "unmapped" | "stripped" */ int Xorriso_option_joliet_map(struct XorrisO *xorriso, char *mode, int flag); /* Command -launch_frontend */ /* @since 1.2.6 */ int Xorriso_option_launch_frontend(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -lfa_flags */ /* @sice 1.5.8 */ int Xorriso_option_lfa_flags(struct XorrisO *xorriso, char *mode, int flag); /* Command -list_arg_sorting */ /* @since 1.2.2 */ int Xorriso_option_list_arg_sorting(struct XorrisO *xorriso, int flag); /* Command -list_delimiter */ /* @since 0.2.6 */ int Xorriso_option_list_delimiter(struct XorrisO *xorriso, char *text, int flag); /* Command -list_extras */ /* @since 1.1.6 */ int Xorriso_option_list_extras(struct XorrisO *xorriso, char *mode, int flag); /* Command -list_formats */ /* @since 0.1.6 */ int Xorriso_option_list_formats(struct XorrisO *xorriso, int flag); /* Command -list_profiles */ /* @since 0.4.2 */ int Xorriso_option_list_profiles(struct XorrisO *xorriso, char *which, int flag); /* Command -list_speeds */ /* @since 1.1.2 */ int Xorriso_option_list_speeds(struct XorrisO *xorriso, int flag); /* Command -lns alias -lnsi */ /* @since 1.2.6 */ int Xorriso_option_lnsi(struct XorrisO *xorriso, char *target, char *path, int flag); /* Command -load session|track|sbsector value */ /* @param flag bit0= with adr_mode sbsector: adr_value is possibly 16 too high @return <=0 error , 1 success, 2 revoked by -reassure */ /* @since 0.1.6 */ int Xorriso_option_load(struct XorrisO *xorriso, char *adr_mode, char *adr_value, int flag); /* Command -logfile */ /* @since 0.1.0 */ int Xorriso_option_logfile(struct XorrisO *xorriso, char *channel, char *fileadr, int flag); /* Command -ls alias -lsi and -lsl alias -lsli and -lsd alias -lsdi and -lsdl alias -lsdli and -du alias -dui and -dus alias -dusi and -lsattr alias -lsattr and -lsattrd alias -lsattrdi @param flag bit0= long format (-lsl , -du, not -dus, not -ls) bit1= do not expand patterns but use literally bit2= -du rather than -ls bit3= list directories as themselves (-lsd) bit4= -lsattr rather than -ls @since 1.5.8 */ /* @since 0.1.0 */ int Xorriso_option_lsi(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -lsx, -lslx, -lsdx , -lsdlx , -dux , -dusx @param flag bit0= long format (-lslx , -dux) bit1= do not expand patterns but use literally bit2= du rather than ls bit3= list directories as themselves (ls -d) */ /* @since 0.1.0 */ int Xorriso_option_lsx(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Commandis -map , -map_single */ /* @param flag bit0=do not report the added item bit1=do not reset pacifier, no final pacifier message bit5= -map_single: do not insert directory tree */ /* @since 0.1.6 */ int Xorriso_option_map(struct XorrisO *xorriso, char *disk_path, char *iso_path, int flag); /* Command -map_l , -compare_l , -update_l , -extract_l */ /* @param flag bit8-11= mode 0= -map_l 1= -compare_l 2= -update_l 3= -extract_l 4= -update_lxi @since 1.4.8 5= -update_li @since 1.4.8 */ /* @since 0.2.0 */ int Xorriso_option_map_l(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -mark */ /* @since 0.1.0 */ int Xorriso_option_mark(struct XorrisO *xorriso, char *mark, int flag); /* Command -md5 */ /* @since 0.4.2 */ int Xorriso_option_md5(struct XorrisO *xorriso, char *mode, int flag); /* Command -mkdir alias -mkdiri */ /* @since 0.1.0 */ int Xorriso_option_mkdiri(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -modesty_on_drive */ /* @since 1.4.2 */ int Xorriso_option_modesty_on_drive(struct XorrisO *xorriso, char *mode, int flag); /* Command -mount , -mount_cmd , -session_string */ /* @param bit0= -mount_cmd: print mount command to result channel rather than performing it bit1= perform -session_string rather than -mount_cmd */ /* @since 0.3.2 */ int Xorriso_option_mount(struct XorrisO *xorriso, char *dev, char *adr_mode, char *adr, char *cmd, int flag); /* Command -mount_opts option[:...] */ /* @since 0.4.4 */ int Xorriso_option_mount_opts(struct XorrisO *xorriso, char *mode, int flag); /* Command -move */ /* @since 1.2.8 */ int Xorriso_option_move(struct XorrisO *xorriso, char *origin, char *dest, int flag); /* Command -msg_op */ /* @since 1.2.6 */ int Xorriso_option_msg_op(struct XorrisO *xorriso, char *what, char *arg, int flag); /* Command -mv alias -mvi */ /* @since 0.1.0 */ int Xorriso_option_mvi(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Option -named_pipe_loop */ /* @since 1.3.2 */ int Xorriso_option_named_pipe_loop(struct XorrisO *xorriso, char *mode, char *stdin_pipe, char *stdout_pipe, char *stderr_pipe, int flag); /* Command -no_rc */ /* @since 0.1.0 */ int Xorriso_option_no_rc(struct XorrisO *xorriso, int flag); /* Command -not_leaf , -as mkisofs -hide without '/' */ /* @param flag bit0= add to iso_rr hide list rather than to disk exclusions @since 0.6.0 bit1= add to joliet hide list rather than disk exclusions @since 0.6.0 bit2= add to HFS+ hide list rather than disk exclusions @since 1.2.4 */ /* @since 0.1.6 */ int Xorriso_option_not_leaf(struct XorrisO *xorriso, char *pattern, int flag); /* Command -not_list , -quoted_not_list */ /* @param flag bit0= -quoted_not_list @since 0.3.0 */ /* @since 0.1.6 */ int Xorriso_option_not_list(struct XorrisO *xorriso, char *adr, int flag); /* Command -not_mgt */ /* @since 0.1.6 */ int Xorriso_option_not_mgt(struct XorrisO *xorriso, char *setting, int flag); /* Command -not_paths , -as mkisofs -hide with '/' */ /* @param flag bit0= add to iso_rr hide list rather than to disk exclusions @since 0.6.0 bit1= add to joliet hide list rather than disk exclusions @since 0.6.0 bit2= enable disk pattern expansion regardless of -disk_pattern bit8-13= consolidated hide state bits, duplicating bit0-1 @since 1.2.4 bit8= add to iso_rr_hidings, same as bit0 bit9= add to joliet_hidings, same as bit1 bit10= add to hfsplus_hidings */ /* @since 0.1.6 */ int Xorriso_option_not_paths(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -options_from_file */ /* @return <=0 error , 1 = success , 3 = request to end program run */ /* @since 0.1.0 */ int Xorriso_option_options_from_file(struct XorrisO *xorriso, char *adr, int flag); /* Command -osirrox "on"|"off" */ /* @since 0.1.8 */ int Xorriso_option_osirrox(struct XorrisO *xorriso, char *mode, int flag); /* Command -overwrite "on"|"nondir"|"off" */ /* @since 0.1.0 */ int Xorriso_option_overwrite(struct XorrisO *xorriso, char *mode, int flag); /* Command -pacifier */ /* @since 0.2.2 */ int Xorriso_option_pacifier(struct XorrisO *xorriso, char *style, int flag); /* Command -padding */ /* @since 0.1.0 */ int Xorriso_option_padding(struct XorrisO *xorriso, char *size, int flag); /* Command -page */ /* @since 0.1.0 */ int Xorriso_option_page(struct XorrisO *xorriso, int len, int width, int flag); /* Command -paste_in */ /* @since 0.2.0 */ int Xorriso_option_paste_in(struct XorrisO *xorriso, char *iso_rr_path, char *disk_path, char *start, char *count, int flag); /* Command -path_list , -quoted_path_list */ /* @param flag bit0= -quoted_path_list @since 0.3.0 */ /* @since 0.1.0 */ int Xorriso_option_path_list(struct XorrisO *xorriso, char *adr, int flag); /* Command -pathspecs */ /* @since 0.1.0 */ int Xorriso_option_pathspecs(struct XorrisO *xorriso, char *mode, int flag); /* Command -pkt_output */ /* Note: If output is redirected by Xorriso_push_outlists() then mode "on" consolidates output in the result output list, not on stdout. */ /* @since 0.1.0 */ int Xorriso_option_pkt_output(struct XorrisO *xorriso, char *mode, int flag); /* Command -preparer_id */ /* @since 0.6.2 */ int Xorriso_option_preparer_id(struct XorrisO *xorriso, char *name, int flag); /* Command -print, -print_info , -print_mark */ /* @param flag bit0-1= output channel: 0= result channel 1= info channel @since 1.0.6 2= mark channel @since 1.0.6 */ /* @since 1.0.6 */ int Xorriso_option_print(struct XorrisO *xorriso, char *text, int flag); /* Command -print_size @param flag bit0= report in mkisofs compatible form on real stdout */ /* @since 0.1.0 */ int Xorriso_option_print_size(struct XorrisO *xorriso, int flag); /* Command -prog */ /* @since 0.1.0 */ int Xorriso_option_prog(struct XorrisO *xorriso, char *name, int flag); /* Command -prompt */ /* @since 0.1.0 */ int Xorriso_option_prompt(struct XorrisO *xorriso, char *text, int flag); /* Command -prog_help */ /* @since 0.1.0 */ int Xorriso_option_prog_help(struct XorrisO *xorriso, char *name, int flag); /* Command -publisher */ /* @since 0.1.2 */ int Xorriso_option_publisher(struct XorrisO *xorriso, char *name, int flag); /* Command -pvd_info */ /* @since 0.4.4 */ int Xorriso_option_pvd_info(struct XorrisO *xorriso, int flag); /* Command -pwd alias -pwdi */ /* @since 0.1.0 */ int Xorriso_option_pwdi(struct XorrisO *xorriso, int flag); /* Command -pwdx */ /* @since 0.1.0 */ int Xorriso_option_pwdx(struct XorrisO *xorriso, int flag); /* Command -read_fs */ /* @since 1.4.2 */ int Xorriso_option_read_fs(struct XorrisO *xorriso, char *mode, int flag); /* Command -read_mkisofsrc */ /* @since 0.6.0 */ int Xorriso_option_read_mkisofsrc(struct XorrisO *xorriso, int flag); /* Command -reassure "on"|"tree"|"off" */ /* @since 0.1.0 */ int Xorriso_option_reassure(struct XorrisO *xorriso, char *mode, int flag); /* Command -report_about */ /* @since 0.1.0 */ int Xorriso_option_report_about(struct XorrisO *xorriso, char *severity, int flag); /* Command -report_el_torito */ /* @since 1.3.8 */ int Xorriso_option_report_el_torito(struct XorrisO *xorriso, char *form, int flag); /* Command -report_system_area */ /* @since 1.3.8 */ int Xorriso_option_report_system_area(struct XorrisO *xorriso, char *form, int flag); /* Command -return_with */ /* @since 0.1.0 */ int Xorriso_option_return_with(struct XorrisO *xorriso, char *severity, int exit_value, int flag); /* Command -rm alias -rmi , -rm_r alias -rm_ri , -rmdir alias -rmdiri */ /* @param flag bit0=recursive bit2= remove empty directory: rmdiri */ /* @since 0.1.0 */ int Xorriso_option_rmi(struct XorrisO *xorriso, int argc, char **argv, int *idx, int flag); /* Command -rockridge "on"|"off" */ /* @since 1.2.4 */ int Xorriso_option_rockridge(struct XorrisO *xorriso, char *mode, int flag); /* Command -rollback */ /* @param flag bit0= do not -reassure @return <=0 error , 1 success, 2 revoked by -reassure */ /* @since 0.1.0 */ int Xorriso_option_rollback(struct XorrisO *xorriso, int flag); /* Command -rom_toc_scan */ /* @since 0.1.6 */ int Xorriso_option_rom_toc_scan(struct XorrisO *xorriso, char *mode, int flag); /* Command -rr_reloc_dir */ /* @since 1.2.2 */ int Xorriso_option_rr_reloc_dir(struct XorrisO *xorriso, char *name, int flag); /* Command -scdbackup_tag */ /* @since 0.4.4 */ int Xorriso_option_scdbackup_tag(struct XorrisO *xorriso, char *list_path, char *record_name, int flag); /* Command -scsi_dev_family */ /* @since 1.4.4 */ int Xorriso_option_scsi_dev_family(struct XorrisO *xorriso, char *mode, int flag); /* Command -scsi_log */ /* @since 0.5.0 */ int Xorriso_option_scsi_log(struct XorrisO *xorriso, char *mode, int flag); /* Command -session_log */ /* @since 0.1.4 */ int Xorriso_option_session_log(struct XorrisO *xorriso, char *path, int flag); /* Command -setfacl_list alias -setfacl_listi */ /* @since 0.3.4 */ int Xorriso_option_setfacl_listi(struct XorrisO *xorriso, char *disk_path, int flag); /* Command -setfacl alias -setfacli , -setfacl_r alias -setfacl_ri */ /* @param flag bit0=recursive -setfacl_r */ /* @since 0.3.4 */ int Xorriso_option_setfacli(struct XorrisO *xorriso, char *acl_text, int argc, char **argv, int *idx, int flag); /* Command -setfattr alias -setfattri, -setfattr_r alias -setfattr_ri */ /* @param flag bit0=recursive -setfattr_r */ /* @since 0.3.4 */ int Xorriso_option_setfattri(struct XorrisO *xorriso, char *name, char *value, int argc, char **argv, int *idx, int flag); /* Command -setfattr_list alias -setfattr_listi */ /* @since 0.3.4 */ int Xorriso_option_setfattr_listi(struct XorrisO *xorriso, char *path, int flag); /* Command -set_filter , -set_filter_r , -show_stream , -show_stream_r */ /* @param flag bit0=recursive -set_filter_r bit1= do not reset pacifier, no final pacifier message bit2= -show_stream rather than -set_filter */ /* @since 0.3.8 */ int Xorriso_option_set_filter(struct XorrisO *xorriso, char *name, int argc, char **argv, int *idx, int flag); /* Option -sh_style_result */ /* @since 1.3.2 */ int Xorriso_option_sh_style_result(struct XorrisO *xorriso, char *mode, int flag); /* Command -signal_handling */ /* @param flag bit0= do not yet install the eventual handler @since 1.1.0 */ int Xorriso_option_signal_handling(struct XorrisO *xorriso, char *mode, int flag); /* Command -sleep */ /* @since 1.1.8 */ int Xorriso_option_sleep(struct XorrisO *xorriso, char *duration, int flag); /* Command -speed , -read_speed */ /* @param flag bit0= @since 1.3.4 -read_speed rather than -speed */ /* @since 0.1.0 */ int Xorriso_option_speed(struct XorrisO *xorriso, char *speed, int flag); /* Command -split_size */ /* @since 0.1.4 */ int Xorriso_option_split_size(struct XorrisO *xorriso, char *s, int flag); /* Command -status */ /* @since 0.1.0 */ int Xorriso_option_status(struct XorrisO *xorriso, char *mode, int flag); /* Command -status_history_max */ /* @since 0.1.0 */ int Xorriso_option_status_history_max(struct XorrisO *xorriso, int num1, int flag); /* Command -stdio_sync "on"|"off"|"end"|size */ /* @since 0.4.6 */ int Xorriso_option_stdio_sync(struct XorrisO *xorriso, char *rhythm, int flag); /* Command -stream_recording */ /* @since 0.1.8 */ int Xorriso_option_stream_recording(struct XorrisO *xorriso, char *mode, int flag); /* Command -system_id */ /* @since 0.4.4 */ int Xorriso_option_system_id(struct XorrisO *xorriso, char *name, int flag); /* Command -tell_media_space */ /* @since 0.1.0 */ int Xorriso_option_tell_media_space(struct XorrisO *xorriso, int flag); /* Command -temp_mem_limit */ /* @since 0.1.0 */ int Xorriso_option_temp_mem_limit(struct XorrisO *xorriso, char *size, int flag); /* Command -toc */ /* @param flag bit0= short report form as with -dev, no table-of-content */ /* @since 0.1.0 */ int Xorriso_option_toc(struct XorrisO *xorriso, int flag); /* Command -toc_info_type */ /* @since 1.5.8 */ int Xorriso_option_toc_info_type(struct XorrisO *xorriso, char *mode, int flag); /* Command -toc_of */ /* @since 1.2.6 */ int Xorriso_option_toc_of(struct XorrisO *xorriso, char *which, int flag); /* Command -truncate_overwritable */ /* @since 1.5.4 */ int Xorriso_option_truncate_overwritable(struct XorrisO *xorriso, char *adr_mode, char *adr_value, char *adjust, int flag); /* Command -uid */ /* @since 0.1.0 */ int Xorriso_option_uid(struct XorrisO *xorriso, char *uid, int flag); /* Command -unregister_filter */ /* @since 0.3.8 */ int Xorriso_option_unregister_filter(struct XorrisO *xorriso, char *name, int flag); /* Command -update and -update_r @param flag bit0= issue summary message bit1= do not reset pacifier, no final pacifier message bit2= do not issue pacifier messages at all bit3= recursive: -update_r */ /* @since 0.1.2 */ int Xorriso_option_update(struct XorrisO *xorriso, char *disk_path, char *iso_path, int flag); /* Command -use_readline */ /* @since 0.1.0 */ int Xorriso_option_use_readline(struct XorrisO *xorriso, char *mode, int flag); /* Command -use_immed_bit */ /* @since 1.4.6 */ int Xorriso_option_use_immed_bit(struct XorrisO *xorriso, char *mode, int flag); /* Command -version */ /* @since 0.1.0 */ int Xorriso_option_version(struct XorrisO *xorriso, int flag); /* Command -volid */ /* @param flag bit0= do not warn of problematic volid */ int Xorriso_option_volid(struct XorrisO *xorriso, char *volid, int flag); /* Command -volset_id */ /* @since 0.4.4 */ int Xorriso_option_volset_id(struct XorrisO *xorriso, char *name, int flag); /* Command -volume_date */ /* @since 0.5.4 */ int Xorriso_option_volume_date(struct XorrisO *xorriso, char *time_type, char *timestring, int flag); /* Command -write_type */ /* @since 1.2.4 */ int Xorriso_option_write_type(struct XorrisO *xorriso, char *mode, int flag); /* There is no Xorriso_option_x() because -x has an effect only in Xorriso_prescan_args(). Use the flag bits of Xorriso_interpreter() if you want to impose command sorting on your own. @since 1.2.2 */ /* Command -xattr "on"|"off" */ /* @since 0.3.4 */ int Xorriso_option_xattr(struct XorrisO *xorriso, char *mode, int flag); /* Command -zisofs */ /* @since 0.3.8 */ int Xorriso_option_zisofs(struct XorrisO *xorriso, char *mode, int flag); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* Xorriso_includeD */ This is xorriso.info, produced by makeinfo version 5.2 from xorriso.texi. xorriso - creates, loads, manipulates and writes ISO 9660 filesystem images with Rock Ridge extensions. Copyright (C) 2007 - 2024 Thomas Schmitt Permission is granted to distribute this text freely. INFO-DIR-SECTION Archiving START-INFO-DIR-ENTRY * Xorriso: (xorriso). Burns ISO 9660 on CD, DVD, BD. END-INFO-DIR-ENTRY  File: xorriso.info, Node: Top, Next: Overview, Up: (dir) GNU xorriso 1.5.7 ***************** xorriso - creates, loads, manipulates and writes ISO 9660 filesystem images with Rock Ridge extensions. * Menu: * Overview:: Overview * Model:: Session model * Media:: Media types and states * Methods:: Creating, Growing, Modifying, Blind Growing * Drives:: Libburn drives * Extras:: Rock Ridge, POSIX, X/Open, El Torito, ACL, xattr * Processing:: Command processing * Dialog:: Dialog, Readline, Result pager * Commands:: Reference of commands * Examples:: Examples * Files:: Files * Environ:: Environment * Seealso:: See also * Bugreport:: Reporting bugs * Legal:: Author, Copyright, Credits * CommandIdx:: Alphabetic Command List * ConceptIdx:: Alphabetic List of Concepts and Objects  File: xorriso.info, Node: Overview, Next: Model, Prev: Top, Up: Top 1 Overview ********** 'xorriso' is a program which copies file objects from POSIX compliant filesystems into Rock Ridge enhanced ISO 9660 filesystems and performs session-wise manipulation of such filesystems. It can load the management information of existing ISO images and it writes the session results to optical media or to filesystem objects. Vice versa 'xorriso' is able to copy file objects out of ISO 9660 filesystems. A special property of 'xorriso' is that it needs neither an external ISO 9660 formatter program nor an external burn program for CD, DVD or BD but rather incorporates the libraries of libburnia-project.org . 1.1 Features ============ Operates on an existing ISO image or creates a new one. Copies files from disk filesystem into the ISO image. Copies files from ISO image to disk filesystem (see osirrox). Renames or deletes file objects in the ISO image. Changes file properties in the ISO image. Updates ISO subtrees incrementally to match given disk subtrees. Writes result either as completely new image or as add-on session to optical media or filesystem objects. Can activate ISOLINUX and GRUB boot images via El Torito and MBR. Can perform multi-session tasks as emulation of mkisofs and cdrecord. Can record and restore hard links and ACL. Content may get zisofs compressed or filtered by external processes. Can issue commands to mount older sessions on GNU/Linux or FreeBSD. Can check media for damages and copy readable blocks to disk. Can attach MD5 checksums to each data file and the whole session. Scans for optical drives, blanks re-usable optical media. Reads its instructions from command line arguments, dialog, and files. Provides navigation commands for interactive ISO image manipulation. Adjustable thresholds for abort, exit value, and problem reporting. Note that 'xorriso' does not write audio CDs and that it does not produce UDF filesystems which are specified for official video DVD or BD.  File: xorriso.info, Node: Model, Next: Media, Prev: Overview, Up: Top 2 Session model *************** Unlike other filesystems, *ISO 9660* (aka *ECMA-119*) is not intended for read-write operation but rather for being generated in a single sweep and being written to media as a *session*. The data content of the session is called filesystem *image*. The written image in its session can then be mounted by the operating system for being used read-only. GNU/Linux is able to mount ISO images from block devices, which may represent optical media, other media or via a loop device even from regular disk files. FreeBSD mounts ISO images from devices that represent arbitrary media or from regular disk files. This session usage model has been extended on CD media by the concept of *multi-session* , which adds information to the CD and gives the mount programs of the operating systems the addresses of the entry points of each session. The mount programs recognize block devices which represent CD media and will by default mount the image in the last session. This session usually contains an updated directory tree for the whole medium which governs the data contents in all recorded sessions. So in the view of the mount program all sessions of a particular medium together form a single filesystem image. Adding a session to an existing ISO image is in this text referred as *growing*. The multi-session model of the MMC standard does not apply to all media types. But program growisofs by Andy Polyakov showed how to extend this functionality to overwritable media or disk files which carry valid ISO 9660 filesystems. 'xorriso' provides growing as well as an own method named *modifying* which produces a completely new ISO image from the old one and the modifications. See paragraph Creating, Growing, Modifying, Blind Growing below. 'xorriso' adopts the concept of multi-session by loading an image directory tree if present, by offering to manipulate it by several actions, and by writing the new image to the target medium. The first session of a 'xorriso' run begins by the definition of the input drive with the ISO image or by the definition of an output drive. The session ends by command -commit which triggers writing. A -commit is done automatically when the program ends regularly. After -commit a new session begins with the freshly written one as input. A new input drive can only be chosen as long as the loaded ISO image was not altered. Pending alteration can be revoked by command -rollback. Writing a session to the target is supposed to be very expensive in terms of time and of consumed space on appendable or write-once media. Therefore all intended manipulations of a particular ISO image should be done in a single session. But in principle it is possible to store intermediate states and to continue with image manipulations.  File: xorriso.info, Node: Media, Next: Methods, Prev: Model, Up: Top 3 Media types and states ************************ There are two families of media in the MMC standard: *Multi-session media* are CD-R, CD-RW, DVD-R, DVD+R, DVD+R/DL, BD-R, and unformatted DVD-RW. These media provide a table of content which describes their existing sessions. See command *-toc*. Similar to multi-session media are DVD-R DL and minimally blanked DVD-RW. They record only a single session of which the size must be known in advance. 'xorriso' will write onto them only if command -close is set to "on". *Overwritable media* are DVD-RAM, DVD+RW, BD-RE, and formatted DVD-RW. They offer random write access but do not provide information about their session history. If they contain one or more ISO 9660 sessions and if the first session was written by 'xorriso', then a table of content can be emulated. Else only a single overall session will be visible. DVD-RW media can be formatted by -format "full". They can be made unformatted by -blank "deformat". Regular files and block devices are handled as overwritable media. Pipes and other writeable file types are handled as blank multi-session media. The program growisofs formats by default BD-R to be pseudo-overwritable (POW). xorriso will classify them as Media current: is unsuitable , is POW formatted and will refuse to write to them or to obtain multi-session information from them. These media can assume several states in which they offer different capabilities. *Blank* media can be written from scratch. They contain no ISO image suitable for 'xorriso'. Blank is the state of newly purchased optical media. With used CD-RW and DVD-RW it can be achieved by action -blank "as_needed". Overwritable media are considered blank if they are new or if they have been marked as blank by 'xorriso'. Action -blank "as_needed" can be used to do this marking on overwritable media, or to apply mandatory formatting to new media if necessary. *Appendable* media accept further sessions. Either they are MMC multi-session media in appendable state, or they are overwritable media which contain an ISO image suitable for 'xorriso'. Appendable is the state after writing a session with command -close off. *Closed* media cannot be written. They may contain an ISO image suitable for 'xorriso'. Closed is the state of DVD-ROM media and of multi-session media which were written with command -close on. If the drive is read-only hardware then it will probably show any media as closed CD-ROM or DVD-ROM. Overwritable media assume this state in such read-only drives or if they contain unrecognizable data in the first 32 data blocks. Read-only drives may or may not show session histories of multi-session media. Often only the first and the last session are visible. Sometimes not even that. Command -rom_toc_scan might or might not help in such cases.  File: xorriso.info, Node: Methods, Next: Drives, Prev: Media, Up: Top 4 Creating, Growing, Modifying, Blind Growing: ********************************************** A new empty ISO image gets *created* if there is no input drive with a valid ISO 9660 image when the first time an output drive is defined. This is achieved by command -dev on blank media or by command -outdev on media in any state. The new empty image can be populated with directories and files. Before it can be written, the medium in the output drive must get into blank state if it was not blank already. If there is a input drive with a valid ISO image, then this image gets loaded as foundation for manipulations and extension. The constellation of input and output drive determines which write method will be used. They have quite different capabilities and constraints. The method of *growing* adds new data to the existing data on the medium. These data comprise of new file content and they override the existing ISO 9660 + Rock Ridge directory tree. It is possible to hide files from previous sessions but they still exist on the medium and with many types of optical media it is quite easy to recover them by mounting older sessions. Growing is achieved by command -dev. The write method of *modifying* produces compact filesystem images with no outdated files or directory trees. Modifying can write its images to target media which are completely unsuitable for multi-session operations. E.g. DVD-RW which were treated with -blank deformat_quickest, DVD-R DL, named pipes, character devices, sockets. On the other hand modified sessions cannot be written to appendable media but to blank media only. So for this method one needs either two optical drives or has to work with filesystem objects as source and/or target medium. Modifying takes place if input drive and output drive are not the same and if command -grow_blindly is set to its default "off". This is achieved by commands -indev and -outdev. If command -grow_blindly is set to a non-negative number and if -indev and -outdev are both set to different drives, then *blind growing* is performed. It produces an add-on session which is ready for being written to the given block address. This is the usage model of mkisofs -M $indev -C $msc1,$msc2 -o $outdev which gives much room for wrong parameter combinations and should thus only be employed if a strict distinction between ISO formatter 'xorriso' and the burn program is desired. -C $msc1,$msc2 is equivalent to: -load sbsector $msc1 -grow_blindly $msc2  File: xorriso.info, Node: Drives, Next: Extras, Prev: Methods, Up: Top 5 Libburn drives **************** Input drive, i.e. source of an existing or empty ISO image, can be any random access readable libburn drive: optical media with readable data, blank optical media, regular files, block devices. Output drive, i.e. target for writing, can be any libburn drive. Some drive types do not support the method of growing but only the methods of modifying and blind growing. They all are suitable for newly created images. All drive file objects have to offer rw-permission to the user of 'xorriso'. Even those which will not be usable for reading an ISO image. With any type of drive object, the data are considered to be organized in blocks of 2 KiB. Access happens in terms of Logical Block Address (*LBA*) which gives the number of a particular data block. MMC compliant (i.e. optical) drives on GNU/Linux usually get addressed by the path of their block device or of their generic character device. E.g. -dev /dev/sr0 -dev /dev/hdc -dev /dev/sg2 By default xorriso will try to map the given address to /dev/hd* and /dev/sr*. The command -scsi_dev_family can redirect the mapping from sr to scd or sg. The latter does not suffer from the concurrency problems which plagued /dev/sr of Linux kernels since version 3 up to 5.5. But it does not yield the same addresses which are used by mount(8) or by open(2) for read(2). On FreeBSD the device files have names like -dev /dev/cd0 On NetBSD: -dev /dev/rcd0d On OpenSolaris: -dev /dev/rdsk/c4t0d0s2 Get a list of accessible drives by command -device_links It might be necessary to do this as *superuser* in order to see all drives and to then allow rw-access for the intended users. Consider to bundle the authorized users in a group like old "floppy". Filesystem objects of nearly any type can be addressed by prefix "stdio:" and their path in the filesystem. E.g.: -dev stdio:/dev/sdc The default setting of -drive_class allows the user to address files outside the /dev tree without that prefix. E.g.: -dev /tmp/pseudo_drive If path leads to a regular file or to a block device then the emulated drive is random access readable and can be used for the method of growing if it already contains a valid ISO 9660 image. Any other file type is not readable via "stdio:" and can only be used as target for the method of modifying or blind growing. Non-existing paths in existing directories are handled as empty regular files. A very special kind of pseudo drive are open file descriptors. They are depicted by "stdio:/dev/fd/" and descriptor number (see man 2 open). Addresses "-" or "stdio:/dev/fd/1" depict standard output, which normally is the output channel for result texts. To prevent a fatal intermingling of ISO image and text messages, all result texts get redirected to stderr if -*dev "-" or "stdio:/dev/fd/1" is among the start arguments of the program. Standard output is currently suitable for creating one session per program run without dialog. Use in other situations is discouraged and several restrictions apply: It is not allowed to use standard output as pseudo drive if it was not among the start arguments. Do not try to fool this ban via backdoor addresses to stdout. If stdout is used as drive, then -use_readline is permanently disabled. Use of backdoors can cause severe memory and/or tty corruption. Be aware that especially the superuser can write into any accessible file or device by using its path with the "stdio:" prefix. By default any address in the /dev tree without prefix "stdio:" will work only if it leads to a MMC drive. One may use command *-ban_stdio_write* to surely prevent this risk and to restrict drive usage to MMC drives. One may prepend "mmc:" to a path to surely disallow any automatic "stdio:". By command -drive_class one may ban certain paths or allow access without prefix "stdio:" to other paths.  File: xorriso.info, Node: Extras, Next: Processing, Prev: Drives, Up: Top 6 Rock Ridge, POSIX, X/Open, El Torito, ACL, xattr ************************************************** *Rock Ridge* is the name of a set of additional information which enhance an ISO 9660 filesystem so that it can represent a POSIX compliant filesystem with ownership, access permissions, symbolic links, and other attributes. This is what 'xorriso' uses for a decent representation of the disk files within the ISO image. 'xorriso' produces Rock Ridge information by default. It is strongly discouraged to disable this feature. 'xorriso' is not named "porriso" because POSIX only guarantees 14 characters of filename length. It is the X/Open System Interface standard XSI which demands a file name length of up to 255 characters and paths of up to 1024 characters. Rock Ridge fulfills this demand. An *El Torito* boot record points the BIOS bootstrapping facility to one or more boot images, which are binary program files stored in the ISO image. The content of the boot image files is not in the scope of El Torito. Most bootable GNU/Linux CDs are equipped with ISOLINUX or GRUB boot images. 'xorriso' is able to create or maintain an El Torito object which makes such an image bootable. For details see command -boot_image. It is possible to make ISO images bootable from USB stick or other hard-disk-like media. Several options install a *MBR* (Master Boot Record), It may get adjusted according to the needs of the intended boot firmware and the involved boot loaders, e.g. GRUB2 or ISOLINUX. A MBR contains boot code and a partition table. The new MBR of a follow-up session can get in effect only on overwritable media. MBR is read by PC-BIOS when booting from USB stick or hard disk, and by PowerPC CHRP or PReP when booting. An MBR partition with type 0xee indicates the presence of GPT. Emulation -as mkisofs supports the example options out of the ISOLINUX wiki, the options used in GRUB script grub-mkrescue, and the example in the FreeBSD AvgLiveCD wiki. A *GPT* (GUID Partition Table) marks partitions in a more modern way. It is read by EFI when booting from USB stick or hard disk, and may be used for finding and mounting a HFS+ partition inside the ISO image. An *APM* (Apple Partition Map) marks the HFS+ partition. It is read by Macs for booting and for mounting. MBR, GPT and APM are combinable. APM occupies the first 8 bytes of MBR boot code. All three do not hamper El Torito booting from CDROM. There is support for further facilities: MIPS Big Endian (SGI), MIPS Little Endian (DEC), SUN SPARC, HP-PA. Those are mutually not combinable and also not combinable with MBR, GPT, or APM. *ACL* are an advanced way of controlling access permissions to file objects. Neither ISO 9660 nor Rock Ridge specify a way to record ACLs. So libisofs has introduced a standard conformant extension named AAIP for that purpose. It uses this extension if enabled by command *-acl*. AAIP enhanced images are supposed to be mountable normally, but one cannot expect that the mounted filesystem will show and respect the ACLs. For now, only 'xorriso' is able to retrieve those ACLs. It can bring them into effect when files get restored to an ACL enabled file system or it can print them in a format suitable for tool setfacl. Files with ACL show as group permissions the setting of entry "mask::" if that entry exists. Nevertheless the non-listed group members get handled according to entry "group::". When removing ACL from a file, 'xorriso' brings "group::" into effect. Recording and restoring of ACLs from and to local files works currently only on GNU/Linux and FreeBSD. *xattr* (aka EA, or extattr) are pairs of name and value which can be attached to file objects. AAIP is able to represent them and 'xorriso' can record and restore them. But be aware that pairs with names of non-user namespaces are not necessarily portable between operating systems and not even between filesystems. Only those which begin with "user.", like "user.x" or "user.whatever", can unconditionally be expected to be appropriate on other machines and disks. Processing of other xattr may need administrator privileges. Name has to be a 0 terminated string. Value may be any array of bytes which does not exceed the size of 4095 bytes. xattr processing happens only if it is enabled by command *-xattr*. As with ACL, currently only 'xorriso' is able to retrieve xattr from AAIP enhanced images, to restore them to xattr capable file systems, or to print them. Recording and restoring of xattr from and to local files works currently only on GNU/Linux and FreeBSD, where they are known as extattr. *Linux file attributes* are binary flags which can be set by program chattr(1) and listed by program lsattr(1). See their man pages and the definitions FS_*_FL in Linux header file <linux/fs.h>. Not all defined flags get reported by lsattr and accepted by chattr, but their number grew over the years. 'xorriso' records the flags of disk files if enabled by command *-lfa_flags*. Its command -lsattr lists 22 flags the same way as the program lsattr does. They can be set by xorriso command -chattr and can be enabled by -lfa_flags for restoring when their files get restored to disk.  File: xorriso.info, Node: Processing, Next: Dialog, Prev: Extras, Up: Top 7 Command processing ******************** Commands are either actions which happen immediately or settings which influence following actions. So their sequence does matter, unless they are given as program arguments and command *-x* is among them. The list of all current settings can be inquired by command -status "long". Command -status "short" lists a handful of fundamental settings and all settings which are not default at program start. Commands consist of a command word, followed by zero or more parameter words. If the list of parameter words is of variable length (indicated by "[...]" or "[***]") then it must be terminated by either the *list delimiter*, occur at the end of the argument list, or occur at the end of an input line. At program start the list delimiter is the string "--". This may be changed with the -list_delimiter command in order to allow "--" as parameter in a variable length list. However, it is advised to reset the delimiter to "--" immediately afterwards. For brevity the list delimiter is referred as "--" throughout this text. The list delimiter is silently ignored if it appears after the parameters of a command with a fixed list length. It is handled as normal text if it appears among the parameters of such a command. *Pattern expansion* converts a list of pattern words into a list of existing file addresses. Unmatched pattern words will appear unaltered in that result list. Pattern matching supports the usual shell parser wildcards '*' '?' '[xyz]' and respects '/' as the path separator, which may only be matched literally. Pattern expansion is a property of some particular commands and not a general feature. It is controlled by commands -iso_rr_pattern and -disk_pattern. Commands which use pattern expansion all have variable parameter lists which are specified in this text by "[***]" rather than "[...]". Some other commands perform pattern matching unconditionally. Command and parameter words are either read from the program arguments, where one argument is one word, or from quoted input lines where words are recognized similar to the quotation rules of a shell parser. 'xorriso' is not a shell, although it might appear so at first glimpse. Be aware that the interaction of quotation marks and pattern symbols like "*" differs from the usual shell parsers. In 'xorriso', a quotation mark does not make a pattern symbol literal. *Quoted input* converts whitespace-separated text into words. The double quotation mark " and the single quotation mark ' can be used to enclose whitespace and make it part of words (e.g. of file names). Each mark type can enclose the marks of the other type. A trailing backslash \ outside quotations or an open quotation cause the next input line to be appended. Quoted input accepts any 8-bit character except NUL (0) as the content of the quotes. Nevertheless it can be cumbersome for the user to produce those characters directly. Therefore quoted input and program arguments offer optional *Backslash Interpretation* which can represent all 8-bit characters except NUL (0) via backslash codes as in $'...' of bash. This is not enabled by default. See command -backslash_codes. When the program starts then it first looks for argument -no_rc. If this is not present then it looks for its startup files and reads their content as command input lines. Then it interprets the program arguments as commands and parameters. Finally it enters dialog mode if command -dialog "on" has been executed by this point. The program ends either by command -end, or by the end of program arguments if dialog mode has not been enabled at that point, or by a problem event which triggers the threshold of command -abort_on.  File: xorriso.info, Node: Dialog, Next: Commands, Prev: Processing, Up: Top 8 Dialog, Readline, Result pager ******************************** Dialog mode prompts for a quoted input line, parses it into words, and performs them as commands with their parameters. It provides assisting services to make dialog more comfortable. Readline is an enhancement for the input line. You may already know it from the bash shell. Whether it is available in 'xorriso' depends on the availability of package readline-dev at the time when 'xorriso' was built from its sourcecode. Readline lets the user move the cursor over the text in the line by help of the Left and the Right arrow keys. Text may be inserted at the cursor position. The Delete key removes the character under the cursor. Up and Down arrow keys navigate through the history of previous input lines. See info readline for more info about libreadline. Command -page activates a built-in result text pager which may be convenient in dialog mode. After an action has output the given number of terminal lines, the pager prompts the user for a line of input. An empty line lets 'xorriso' resume work until the next page is output. The single character "@" disables paging for the current action. "@@@", "x", "q", "X", or "Q" request that the current action aborts and suppress further result output. Any other line input will be interpreted as new dialog line. The current action is requested to abort. Afterwards, the input line is executed. Some actions apply paging to their info output, too. The request to abort may or may not be obeyed by the current action. All actions try to abort as soon as possible.  File: xorriso.info, Node: Commands, Next: Examples, Prev: Dialog, Up: Top 9 Commands ********** All command words are shown with a leading dash although this dash is not mandatory for the command to be recognized. Nevertheless within command -as the dashes of the emulated commands are mandatory. Normally any number of leading dashes is ignored with command words and inner dashes are interpreted as underscores. * Menu: * ArgSort:: Execution order of program arguments * AqDrive:: Acquiring source and target drive * Loading:: Influencing the behavior of image loading * Insert:: Inserting files into ISO image * SetInsert:: Settings for file insertion * Manip:: File manipulations * CmdFind:: Tree traversal command -find * Filter:: Filters for data file content * Writing:: Writing the result, drive control * SetWrite:: Settings for result writing * Bootable:: Bootable ISO images * Jigdo:: Jigdo Template Extraction * Charset:: Character sets * Exception:: Exception processing * DialogCtl:: Dialog mode control * Inquiry:: Drive and media related inquiry actions * Navigate:: Navigation in ISO image and disk filesystem * Verify:: Evaluation of readability and recovery * Restore:: osirrox ISO-to-disk restore commands * Emulation:: Command compatibility emulations (cdrtools) * Scripting:: Scripting, dialog and program control features * Frontend:: Support for frontend programs via stdin and stdout  File: xorriso.info, Node: ArgSort, Next: AqDrive, Prev: Commands, Up: Commands 9.1 Execution order of program arguments ======================================== By default the program arguments of a xorriso run are interpreted as a sequence of commands which get performed exactly in the given order. This requires the user to write commands for desired settings before the commands which shall be influenced by those settings. Many other programs support program arguments in an arbitrary ordering and perform settings and actions in a sequence at their own discretion. xorriso provides an option to enable such a behavior at the cost of loss of expressivity. -x Enable automatic sorting of program arguments into a sequence that (most likely) is sensible. This command may be given at any position among the commands which are handed over as program arguments. Note: It works only if it is given as program argument and with a single dash (i.e. "-x"). It will not work in startup files, nor with -options_from_file, nor in dialog mode, nor as "x" and finally not as "--x". It affects only the commands given as program arguments. -list_arg_sorting List all xorriso commands in the order which applies if command -x is in effect. This list may also be helpful without -x for a user who ponders over the sequence in which to put commands. Deviations from the listed sorting order may well make sense, though.  File: xorriso.info, Node: AqDrive, Next: Loading, Prev: ArgSort, Up: Commands 9.2 Acquiring source and target drive ===================================== The effect of acquiring a drive may depend on several commands in the next paragraph "Influencing the behavior of image loading". If desired, their enabling commands have to be performed before the commands which acquire the drive. -dev address Set input and output drive to the same address and load an ISO image if it is present. If there is no ISO image then create a blank one. Set the image expansion method to growing. This is only allowed as long as no changes are pending in the currently loaded ISO image. If changes are pending, then one has to perform -commit or -rollback first. Special address string "-" means standard output, to which several restrictions apply. See above paragraph "Libburn drives". An empty address string "" gives up the current device without acquiring a new one. -indev address Set input drive and load an ISO image if present. If the new input drive differs from -outdev then switch from growing to modifying or to blind growing. It depends on the setting of -grow_blindly which of both gets activated. The same rules and restrictions apply as with -dev. -outdev address Set output drive and if it differs from the input drive then switch from growing to modifying or to blind growing. Unlike -dev and -indev this action does not load a new ISO image. So it can be performed even if there are pending changes. -outdev can be performed without previous -dev or -indev. In that case an empty ISO image with no changes pending is created. It can either be populated by help of -map, -add et.al. or it can be discarded silently if -dev or -indev are performed afterwards. Special address string "-" means standard output, to which several restrictions apply. See above paragraph "Libburn drives". An empty address string "" gives up the current output drive without acquiring a new one. No writing is possible without an output drive. -drive_class "harmless"|"banned"|"caution"|"clear_list" disk_pattern Add a drive path pattern to one of the safety lists or make those lists empty. There are three lists defined which get tested in the following sequence: If a drive address path matches the "harmless" list then the drive will be accepted. If it is not a MMC device then the prefix "stdio:" will be prepended automatically. This list is empty by default. Else if the path matches the "banned" list then the drive will not be accepted by 'xorriso' but rather lead to a FAILURE event. This list is empty by default. Else if the path matches the "caution" list and if it is not a MMC device, then its address must have the prefix "stdio:" or it will be rejected. This list has by default one entry: "/dev". If a drive path matches no list then it is considered "harmless". By default these are all paths which do not begin with directory "/dev". A path matches a list if one of its parent paths or itself matches a list entry. Address prefix "stdio:" or "mmc:" will be ignored when testing for matches. By pseudo-class "clear_list" and pseudo-patterns "banned", "caution", "harmless", or "all", the lists may be made empty. E.g.: -drive_class clear_list banned One will normally define the -drive_class lists in one of the 'xorriso' Startup Files. Note: This is not a security feature but rather a bumper for the superuser to prevent inadverted mishaps. For reliably blocking access to a device file you have to deny its rw-permissions in the filesystem. -drive_access "exclusive"|"shared":"unrestricted"|"readonly" Control whether device file locking mechanisms shall be used when acquiring a drive, and whether status or content of the medium in the drive may be altered. Useful and most harmless are the setting "shared:readonly" and the default setting "exclusive:unrestricted". "exclusive" enables tests and locks when acquiring the drive. It depends on the operating system which locking mechanisms get applied, if any. On GNU/Linux it is open(O_EXCL). On FreeBSD it is flock(LOCK_EX). "shared" disables the use of these mechanisms to become able to acquire drives which are mounted, or opened by some process, or guarded by /dev/pktcdvd*. "unrestricted" enables all technically appropriate operations on an acquired drive. "shared:unrestricted" risks to get own burn runs spoiled by other processes or to vice versa spoil activities of such processes. So use "exclusive:unrestricted" unless you know for sure that "shared" is safe. "readonly" disables operations which might surprise a co-user of the drive. For -outdev these are formatting, blanking, writing, ejecting. For -indev this is ejecting. Be aware that even reading and drive status inquiries can disturb an ongoing burn run on CD-R[W] and DVD-R[W]. -scsi_dev_family "default"|"sr"|"scd"|"sg" GNU/Linux specific: By default, xorriso tries to map Linux drive addresses to /dev/sr* before they get opened for operating the drive. This coordinates well with other use cases of optical drives, like mount(8). But since year 2010 all /dev/sr* share a global lock which allows only one drive to process an SCSI command while all others have to wait for its completion. This yields awful throughput if more than one drive is writing or reading simultaneously. The global lock is not applied to device files /dev/sg* and also not if the xorriso drive address is prepended by "stdio:". So for simultaneous burn runs on modern GNU/Linux it is advisable to perform -scsi_dev_family "sg" before any -dev, -indev, or -outdev. The drive addresses may then well be given as /dev/sr* but will nevertheless get used as the matching /dev/sg*. If you decide so, consider to put the command into a global startup file like /etc/opt/xorriso/rc. -grow_blindly "off"|predicted_nwa If predicted_nwa is a non-negative number then perform blind growing rather than modifying if -indev and -outdev are set to different drives. "off" or "-1" switch to modifying, which is the default. predicted_nwa is the block address where the add-on session of blind growing will finally end up. It is the responsibility of the user to ensure this final position and the presence of the older sessions. Else the overall ISO image will not be mountable or will produce read errors when accessing file content. 'xorriso' will write the session to the address as obtained from examining -outdev and not necessarily to predicted_nwa. During a run of blind growing, the input drive is given up before output begins. The output drive is given up when writing is done.  File: xorriso.info, Node: Loading, Next: Insert, Prev: AqDrive, Up: Commands 9.3 Influencing the behavior of image loading ============================================= The following commands should normally be performed before loading an image by acquiring an input drive. In rare cases it is desirable to activate them only after image loading. -read_speed code|number[k|m|c|d|b] Set the speed for reading. Default is "none", which avoids to send a speed setting command to the drive before reading begins. Further special speed codes are: "max" (or "0") selects maximum speed as announced by the drive. "min" (or "-1") selects minimum speed as announced by the drive. Speed can be given in media dependent numbers or as a desired throughput per second in MMC compliant kB (= 1000) or MB (= 1000 kB). Media x-speed factor can be set explicitly by "c" for CD, "d" for DVD, "b" for BD, "x" is optional. Example speeds: 706k = 706kB/s = 4c = 4xCD 5540k = 5540kB/s = 4d = 4xDVD If there is no hint about the speed unit attached, then the medium in the -indev will decide. Default unit is CD = 176.4k. Depending on the drive, the reported read speeds can be deceivingly low or high. Therefore "min" cannot become higher than 1x speed of the involved medium type. Read speed "max" cannot become lower than 52xCD, 24xDVD, or 20xBD, depending on the medium type. MMC drives usually activate their own idea of speed and take the speed value given by the burn program only as hint for their own decision. Friendly drives adjust their constant angular velocity so that the desired speed is reached at the outer rim of the medium. But often there is only the choice between very slow and very loud. Sometimes no speed setting is obeyed at all, but speed is adjusted to the demand frequency of the reading program. So xorriso offers to set an additional software enforced limit by prefix "soft_force:". The program will take care not to read faster than the soft_force speed. This may be combined with setting the drive speed to a higher value. Setting "soft_force:0" disables this feature. "soft_force:" tries to correct in subsequent waiting periods lost or surplus time of up to 0.25 seconds. This smoothens the overall data stream but also enables short times of higher speed to compensate short times of low speed. Prefix "soft_corr:" sets this hindsight span by giving a number of microseconds. Not more than 1 billion = 1000 seconds. Very short times can cause speed deviations, because systematic inaccuracies of the waiting function cannot be compensated. Examples (combinable): -read_speed 6xBD -read_speed soft_force:4xBD -read_speed soft_corr:100000 -load entity id Load a particular (possibly outdated) ISO session from -dev or -indev. Usually all available sessions are shown with command -toc. entity depicts the kind of addressing. id depicts the particular address. The following entities are defined: "auto" with any id addresses the last session in -toc. This is the default. "session" with id being a number as of a line "ISO session", column "Idx". "track" with id being a number as of a line "ISO track", column "Idx". "lba" or "sbsector" with a number as of a line "ISO ...", column "sbsector". "volid" with a search pattern for a text as in the column "Volume Id" of a -toc line "ISO ...". "at_time" with a time string as described with command -alter_date chooses the last session or track where the modification timestamp matches the given time within the same second. "before" with a time string chooses the last session or track of which the timestamp is older than the given time. But it does not match an entity with exactly the given time. "not_after" is like "before" but also matches an entity with exactly the given time. "after" with a time string chooses the first session or track of which the timestamp is younger than the given time. But it does not match an entity with exactly the given time. "not_before" is like "after" but also matches an entity with exactly the given time. Comparison of time entities is done with an accuracy of one second. I.e. the centiseconds of ISO 9660 timestamps are ignored. If -toc_info_type is set to "creation_time", then the comparison is done against the creation timestamp of track or session rather than the modification timestamp. The output of -pvd_info shows both timestamps as "Creation Time:" and "Modif. Time :". The time comparisons pick first and last matching sessions. If the sequence of timestamps on a drive is not chronologically ascending, the picks might not be the best choice. In this case look at the output of -toc_info_type "mtime" -toc and choose the desired entity by "session", "track", or "sbsector". Addressing a non-existing entity or one which does not represent an ISO image will either abandon -indev or at least lead to a blank image. If an input drive is set at the moment when -load is executed, then the addressed ISO image is loaded immediately. Else, the setting will be pending until the next -dev or -indev. After the image has been loaded once, the setting is valid for -rollback until next -dev or -indev, where it will be reset to "auto". -displacement [-]lba Compensate a displacement of the image versus the start address for which the image was prepared. This affects only loading of ISO images and reading of their files. The multi-session method of growing is not allowed as long as -displacement is non-zero. I.e. -indev and -outdev must be different. The displacement gets reset to 0 before the drive gets re-acquired after writing. Examples: If a track of a CD starts at block 123456 and gets copied to a disk file where it begins at block 0, then this copy can be loaded with -displacement -123456 If an ISO image was written onto a partition with offset of 640000 blocks of 512 bytes, then it can be loaded from the base device by -load sbsector 160000 -displacement 160000 (If the partition start address is not divisible by 4, then you will have to employ a loop device instead.) In both cases, the ISO sessions should be self contained, i.e. not add-on sessions to an ISO image outside their track or partition. -read_fs "any"|"norock"|"nojoliet"|"ecma119" Specify which kind of filesystem tree to load if present. If the wish cannot be fulfilled, then ECMA-119 names are loaded and converted according to -ecma119_map. "any" first tries to read Rock Ridge. If not present, Joliet is tried. "norock" does not try Rock Ridge. "nojoliet" does not try Joliet. "ecma119" tries neither Rock Ridge nor Joliet. -assert_volid pattern severity Refuse to load ISO images with volume IDs which do not match the given search pattern. When refusing an image, give up the input drive and issue an event of the given severity (like FAILURE, see -abort_on). An empty search pattern accepts any image. This command does not hamper the creation of an empty image from blank input media and does not discard an already loaded image. -in_charset character_set_name Set the character set from which to convert file names when loading an image. See paragraph "Character sets" for more explanations. When loading the written image after -commit the setting of -out_charset will be copied to -in_charset. -auto_charset "on"|"off" Enable or disable recording and interpretation of the output character set name in an xattr attribute of the image root directory. If enabled and if a recorded character set name is found, then this name will be used as name of the input character set when reading an image. Note that the default output charset is the local character set of the terminal where 'xorriso' runs. Before attributing this local character set to the produced ISO image, check whether the terminal properly displays all intended filenames, especially exotic national characters. -hardlinks mode[:mode...] Enable or disable loading and recording of hardlink relations. In default mode "off", iso_rr files lose their inode numbers at image load time. Each iso_rr file object which has no inode number at image generation time will get a new unique inode number if -compliance is set to new_rr. Mode "on" preserves inode numbers from the loaded image if such numbers were recorded. When committing a session it searches for families of iso_rr files which stem from the same disk file, have identical content filtering and have identical properties. The family members all get the same inode number. Whether these numbers are respected at mount time depends on the operating system. Command -lsl displays hardlink counts if "lsl_count" is enabled. This can slow down the command substantially after changes to the ISO image have been made. Therefore the default is "no_lsl_count". Commands -update and -update_r track splits and fusions of hard links in filesystems which have stable device and inode numbers. This can cause automatic last minute changes before the session gets written. Command -hardlinks "perform_update" may be used to do these changes earlier, e.g. if you need to apply filters to all updated files. Mode "without_update" avoids hardlink processing during update commands. Use this if your filesystem situation does not allow -disk_dev_ino "on". 'xorriso' commands which extract files from an ISO image try to hardlink files with identical inode number. The normal scope of this operation is from image load to image load. One may give up the accumulated hard link addresses by -hardlinks "discard_extract". A large number of hardlink families may exhaust -temp_mem_limit if not -osirrox "sort_lba_on" and -hardlinks "cheap_sorted_extract" are both in effect. This restricts hard linking to other files restored by the same single extract command. -hardlinks "normal_extract" re-enables wide and expensive hardlink accumulation. -acl "on"|"off" Enable or disable processing of ACLs. If enabled, then 'xorriso' will obtain ACLs from disk file objects, store ACLs in the ISO image using the libisofs specific AAIP format, load AAIP data from ISO images, test ACL during file comparison, and restore ACLs to disk files when extracting them from ISO images. See also commands -getfacl, -setfacl. -xattr "on"|"user"|"any"|"off" Enable or disable processing of xattr attributes. If enabled, then 'xorriso' will handle xattr similar to ACL. See also commands -getfattr, -setfattr and above paragraph about xattr. Modes "on" and "user" read and write only attributes from namespace "user". Mode "any" processes attributes of all namespaces. This might need administrator privileges, even if the owner of the disk file tries to read or write the attributes. Note that it is not possible to set xattr of namespace "isofs." by xorriso xattr manipulation commands. -lfa_flags mode[:mode...] Enable, disable, or influence processing of Linux file attributes as described in man 1 chattr. Mode "on" enables actual processing of the attributes. Mode "off" disables it. Mode "auto_on" enables it only if the underlying libisofs was compiled with support for Linux file attributes, which is typically not the case on non-Linux systems. Enabled processing without lisofs support would cause failure events when reading disk files. The other modes define the behavior in case of "on": Mode "read" enables obtaining of these attributes from disk files, storing them in the emerging ISO 9660 filesystem as AAIP data, importing them when an ISO filesystem gets loaded and bears such stored attributes, and comparing them during comparisons between files on disk and in ISO. Mode "no_read" disables reading of the attributes from disk files and importing them when an ISO filesystem gets loaded. If other settings like -acl "on" or -xattr "on" require storing of AAIP data, then file attributes might still get stored in the emerging ISO. To surely exclude any file attributes, run before -commit : -chattr_r -remove-lfa-flags / - Mode "import_non_settable" enables obtaining of non-settable attributes from disk and attaching of attributes to files in the emerging ISO image even if all attribute flags are zero. Mode "import_only_settable" causes non-settable attributes ("eEhINVZ") to be ignored when file objects get read from disk. If the remaining attribute flags are all zero, then no attribute information gets attached to the file in the ISO. This saves storage and eases finding of non-trivial attributes in the ISO image. Mode "restore" enables restoring of attributes when their file gets restored and comparing them during comparisons between files on disk and in ISO. Several modes below modify the behavior during restoring of attributes. Mode "no_restore" disables restoring of attributes. Mode "restore_su" enables restoring of the attributes "aij" which are only changeable by the bearer of superuser capabilities. "no_restore_su" disables restoring of these attributes. "restore_su_auto" enables it only if the effective user id is 0. The attribute "i" (for "immutable") gets restored to directories only if they have been created freshly by the same file restoring xorriso command. A directory which already existed on disk will not be made immutable. Mode "restore_only_known" restricts restoring to the known settable attribute flags "aAcCdDFijmPsStTux". "restore_unknown" enables the attempt to restore unknown flags or even those which are known to be unchangeable, if they are not disabled by other modes. Mode "restore_mask=..." enables particular attributes for restoring. All others will not be restored. The list of desired attribute letters follows the '=' character. An empty list enables all attributes, if they are not disabled by other modes. The single character "-" bans all attributes from restoring, like "off" does. Example: -lfa_flags restore_mask=SdCiaj Mode "restore_error=" sets the behavior for the event that restoring of attribute flags to the local filesystem fails. Available are the keyword "silent" and the severities from "all" to "fatal". "silent" and severity "all" suppress any error messages and abort considerations caused by restore attemps of attribute flags. Else the error message is issued with the given severity. Then it depends on the setting of command -abort_on whether restoring goes on or gets aborted. Mode "restore_single" tries to set the attribute flags of a file one-by-one if the attempt fails to set them all at once. This happens only if the "restore_error=" severity and the setting of command -abort_on do not demand to abort the program run. Note that some flags in some situations get ignored silently by some kernels. Mode "no_restore_single" disables this attempt to get at least some of the attribute flags into effect. Mode "default" reinstates the default settings: -lfa_flags off:read:restore:restore_su_auto:restore_only_known -lfa_flags restore_mask=:restore_error=sorry:restore_single Use "default:on" to get default settings with enabled processing. -md5 "on"|"all"|"off"|"load_check_off" Enable or disable processing of MD5 checksums for the overall session and for each single data file. If enabled then images with checksum tags get loaded only if the tags of superblock and directory tree match properly. The MD5 checksums of data files and whole session get loaded from the image if there are any. With commands -compare and -update the recorded MD5 of a file will be used to avoid content reading from the image. Only the disk file content will be read and compared with that MD5. This can save much time if -disk_dev_ino "on" is not suitable. Commands which copy whole data files from ISO to hard disk will verify the copied data stream by the recorded MD5, if -osirrox "check_md5_on" is set. At image generation time they are computed for each file which gets its data written into the new session. The checksums of files which have their data in older sessions get copied into the new session. Superblock, tree and whole session get a checksum tag each. Mode "all" will additionally check during image generation whether the checksum of a data file changed between the time when its reading began and the time when it ended. This implies reading every file twice. Mode "load_check_off" together with "on" or "all" will load recorded MD5 sums but not test the recorded checksum tags of superblock and directory tree. This is necessary if growisofs was used as burn program, because it does not overwrite the superblock checksum tag of the first session. Therefore load_check_off is in effect when 'xorriso' -as mkisofs option -M is performed. The test can be re-enabled by mode "load_check_on". Checksums can be exploited via commands -check_md5, -check_md5_r, via find actions get_md5, check_md5, and via -check_media. -for_backup Enable all extra features which help to produce or to restore backups with highest fidelity of file properties. Currently this is a shortcut for: -hardlinks on -acl on -xattr any -md5 on and possibly: -lfa_flags default:on:import_only_settable -lfa_flags restore_mask=aAcdDijmPsStTux If you restore a backup with xattr from non-user namespaces, then make sure that the target operating system and filesystem know what these attributes mean. Possibly you will need administrator privileges to record or restore such attributes. At recording time, xorriso will try to tolerate missing privileges and just record what is readable. But at restore time, missing privileges or preconditions will cause failure events. Command -xattr "user" after command -for_backup will exclude non-user attributes from being recorded or restored. The command -lfa_flags is executed by -for_backup only if the underlying libisofs was compiled with support for Linux file attributes, which is typically not the case on non-Linux systems. If -lfa_flags is executed by -for_backup then the restore mask enables all known settable attributes, except "C" and "F" which have special constraints which xorriso cannot yet detect at restore time. Command -lfa_flags "restore_mask=" after -for_backup will enable all known settable attributes. -ecma119_map "stripped"|"unmapped"|"lowercase"|"uppercase" Choose the conversion of file names when a session gets loaded, if they stem neither from a Rock Ridge name nor from a Joliet name. Mode "stripped" is the default. It shows the names as found in the ISO but removes trailing ";1" or ".;1" if present. Mode "unmapped" shows names as found without removing characters. Warning: Multi-session converts "xyz;1" to "xyz_1" and maybe adds new ";1". Mode "lowercase" is like "stripped" but also maps uppercase letters to lowercase letters. This is compatible to default GNU/Linux mount behavior. Mode "uppercase" is like "stripped" but maps lowercase letters to uppercase, if any occur despite the prescriptions of ECMA-119. -joliet_map "stripped"|"unmapped" Choose the conversion of file names when a session gets loaded from a Joliet tree. Mode "stripped" is the default. It removes trailing ";1" or ".;1" if present. Mode "unmapped" shows names as found without removing characters. Warning: Multi-session converts "xyz;1" to "xyz_1" and maybe adds new ";1". -iso_nowtime "dynamic"|timestring Choose whether to use the current time ("dynamic") or a fixed time point for timestamps of ISO 9660 nodes without a disk source file and as default for superblock timestamps. If a timestring is given, then it is used for such timestamps. For the formats of timestrings see command *-alter_date*. -disk_dev_ino "on"|"ino_only"|"off" Enable or disable processing of recorded file identification numbers (dev_t and ino_t). If enabled they are stored as xattr and can substantially accelerate file comparison. The root node gets a global start timestamp. If during comparison a file with younger timestamps is found in the ISO image, then it is suspected to have inconsistent content. If device numbers and inode numbers of the disk filesystems are persistent and if no irregular alterations of timestamps or system clock happen, then potential content changes can be detected without reading that content. File content change is assumed if any of mtime, ctime, device number or inode number have changed. Mode "ino_only" replaces the precondition that device numbers are stable by the precondition that mount points in the compared tree always lead to the same filesystems. Use this if mode "on" always sees all files changed. The speed advantage appears only if the loaded session was produced with -disk_dev_ino "on" too. Note that -disk_dev_ino "off" is totally in effect only if -hardlinks is "off", too. -file_name_limit [+]number Set the maximum permissible length for file names in the range of 64 to 255. Path components which are longer than the given number will get truncated and have their last 33 bytes overwritten by a colon ':' and the hex representation of the MD5 of the first 4095 bytes of the whole oversized name. Potential incomplete UTF-8 characters will get their leading bytes replaced by '_'. iso_rr_paths with the long components will still be able to access the file paths with truncated components. If -file_name_limit is executed while an ISO tree is present, the file names in the ISO tree get checked for existing truncated file names of the current limit and for name collisions between newly truncated files and existing files. In both cases, the setting will be refused with a SORRY event. One may lift this ban by prepending the character "+" to the argument of -file_name_limit. Truncated filenames may then get truncated again, invalidating their MD5 part. Colliding truncated names are made unique, consuming at least 9 more bytes of the remaining name part. If writing of xattr is enabled, then the length will be stored in "isofs.nt" of the root directory. If reading of xattr is enabled and "isofs.nt" is found, then the found length will get into effect if it is smaller than the current setting of -file_name_limit. File name patterns will only work if they match the truncated name. This might change in future. Files with truncated names get deleted and re-added unconditionally during -update and -update_r. This might change in future. Linux kernels up to at least 4.1 misrepresent names of length 254 and 255. If you expect such names in or under disk_paths and plan to mount the ISO by such Linux kernels, consider to set -file_name_limit 253. Else just avoid names longer than 253 characters. -rom_toc_scan "on"|"force"|"off"[:"emul_off"][:"emul_wide"] Read-only drives do not tell the actual media type but show any media as ROM (e.g. as DVD-ROM). The session history of MMC multi-session media might be truncated to first and last session or even be completely false. (The emulated history of overwritable media is not affected by this.) To have in case of failure a chance of getting the session history and especially the address of the last session, there is a scan for ISO 9660 filesystem headers which might help but also might yield worse results than the drive's table of content. At its end it can cause read attempts to invalid addresses and thus ugly drive behavior. Setting "on" enables that scan for alleged read-only media. Some operating systems are not able to mount the most recent session of multi-session DVD or BD. If on such a system 'xorriso' has no own MMC capabilities then it may still find that session from a scanned table of content. Setting "force" handles any media like a ROM medium with setting "on". On the other hand the emulation of session history on overwritable media can hamper reading of partly damaged media. Setting "off:emul_off" disables the elsewise trustworthy table-of-content scan for those media. The table-of-content scan on overwritable media normally searches only up to the end of the session that is pointed to by the superblock at block 0. Setting "on:emul_wide" lets the scan continue up to the end of the medium. This may be useful after copying a medium with -check_media patch_lba0=on when not the last session was loaded. -calm_drive "in"|"out"|"all"|"revoke"|"on"|"off" Reduce drive noise until it is actually used again. Some drives stay alert for substantial time after they have been used for reading. This reduces the startup time for the next drive operation but can be loud and waste energy if no i/o with the drive is expected to happen soon. Modes "in", "out", "all" immediately calm down -indev, -outdev, or both, respectively. Mode "revoke" immediately alerts both. Mode "on" causes -calm_drive to be performed automatically after each -dev, -indev, and -outdev. Mode "off" disables this. -ban_stdio_write Allow for writing only the usage of MMC optical drives. Disallow to write the result into files of nearly arbitrary type. Once set, this command cannot be revoked. -early_stdio_test "on"|"appendable_wo"|"off" If enabled by "on" then regular files and block devices get tested for effective access permissions. This implies to try opening those files for writing, which otherwise will happen only later and only if actual writing is desired. The test result is used for classifying the pseudo drives as overwritable, read-only, write-only, or uselessly empty. This may lead to earlier detection of severe problems, and may avoid some less severe error events. Mode "appendable_wo" is like "on" with the additional property that non-empty write-only files are regarded as appendable rather than blank. -data_cache_size number_of_tiles blocks_per_tile Set the size and granularity of the data cache which is used when ISO images are loaded and when file content is read from ISO images. The cache consists of several tiles, which each consists of several blocks. A larger cache reduces the need for tiles being read multiple times. Larger tiles might additionally improve the data throughput from the drive, but can be wasteful if the data are scattered over the medium. Larger cache sizes help best with image loading from MMC drives. They are an inferior alternative to -osirrox option "sort_lba_on". blocks_per_tile must be a power of 2. E.g. 16, 32, or 64. The overall cache size must not exceed 1 GiB. The default values can be restored by parameter "default" instead of one or both of the numbers. Currently the default is 32 tiles of 32 blocks = 2 MiB.  File: xorriso.info, Node: Insert, Next: SetInsert, Prev: Loading, Up: Commands 9.4 Inserting files into ISO image ================================== The following commands expect file addresses of two kinds: *disk_path* is a path to an object in the local filesystem tree. *iso_rr_path* is the Rock Ridge name of a file object in the ISO image. If no Rock Ridge information is recorded in the loaded ISO image, then you will see ISO 9660 names which are of limited length and character set. If no Rock Ridge information shall be stored in an emerging ISO image, then their names will get mapped to such restricted ISO 9660 (aka ECMA-119) names. Note that in the ISO image you are as powerful as the superuser. Access permissions of the existing files in the image do not apply to your write operations. They are intended to be in effect with the read-only mounted image. If the iso_rr_path of a newly inserted file leads to an existing file object in the ISO image, then the following collision handling happens: If both objects are directories then they get merged by recursively inserting the subobjects from filesystem into ISO image. If other file types collide then the setting of command *-overwrite* decides. Renaming of files has similar collision handling, but directories can only be replaced, not merged. Note that if the target directory exists, then -mv inserts the source objects into this directory rather than attempting to replace it. Command -move, on the other hand, would attempt to replace it. The commands in this section alter the ISO image and not the local filesystem. -disk_pattern "on"|"ls"|"off" Set the pattern expansion mode for the disk_path parameters of several commands which support this feature. Setting "off" disables this feature for all commands which are marked in this man page by "disk_path [***]" or "disk_pattern [***]". Setting "on" enables it for all those commands. Setting "ls" enables it only for those which are marked by "disk_pattern [***]". Default is "ls". -add pathspec [...] | disk_path [***] Insert the given files or directory trees from filesystem into the ISO image. If -pathspecs is set to "on" or "as_mkisofs" then pattern expansion is always disabled and character '=' has a special meaning. It separates the ISO image path from the disk path: iso_rr_path=disk_path Character '=' in the iso_rr_path must be escaped by '\' (i.e. as "\="). With -pathspecs "on", the character '\' must not be escaped. The character '=' in the disk_path must not be escaped. With -pathspecs "as_mkisofs", all characters '\' must be escaped in both, iso_rr_path and disk_path. The character '=' may or may not be escaped in the disk_path. If iso_rr_path does not begin with '/' then -cd is prepended. If disk_path does not begin with '/' then -cdx is prepended. If no '=' is given then the word is used as both, iso_rr_path and disk path. If in this case the word does not begin with '/' then -cdx is prepended to the disk_path and -cd is prepended to the iso_rr_path. If -pathspecs is set to "off" then -disk_pattern expansion applies, if enabled. The resulting words are used as both, iso_rr_path and disk path. Relative path words get prepended the setting of -cdx to disk_path and the setting of -cd to iso_rr_path. -add_plainly mode If set to mode "unknown" then any command word that does not begin with "-" and is not recognized as known command will be subject to a virtual -add command. I.e. it will be used as pathspec or as disk_path and added to the image. If enabled, -disk_pattern expansion applies to disk_paths. Mode "dashed" is similar to "unknown" but also adds unrecognized command words even if they begin with "-". Mode "any" announces that all further words are to be added as pathspecs or disk_paths. This does not work in dialog mode. Mode "none" is the default. It prevents any words from being understood as files to add, if they are not parameters to appropriate commands. -path_list disk_path Like -add but read the parameter words from file disk_path or standard input if disk_path is "-". The list must contain exactly one pathspec or disk_path pattern per line. -quoted_path_list disk_path Like -path_list but with quoted input reading rules. Lines get split into parameter words for -add. Whitespace outside quotes is discarded. -map disk_path iso_rr_path Insert file object disk_path into the ISO image as iso_rr_path. If disk_path is a directory then its whole sub tree is inserted into the ISO image. -map_single disk_path iso_rr_path Like -map, but if disk_path is a directory then its sub tree is not inserted. -map_l disk_prefix iso_rr_prefix disk_path [***] Perform -map with each of the disk_path parameters. iso_rr_path will be composed from disk_path by replacing disk_prefix by iso_rr_prefix. -update disk_path iso_rr_path Compare file object disk_path with file object iso_rr_path. If they do not match, then perform the necessary image manipulations to make iso_rr_path a matching copy of disk_path. By default this comparison will imply lengthy content reading before a decision is made. Commands -disk_dev_ino or -md5 may accelerate comparison if they were already in effect when the loaded session was recorded. If disk_path is a directory and iso_rr_path does not exist yet, then the whole subtree will be inserted. Else only directory attributes will be updated. -update_r disk_path iso_rr_path Like -update but working recursively. I.e. all file objects below both addresses get compared whether they have counterparts below the other address and whether both counterparts match. If there is a mismatch then the necessary update manipulation is done. Note that the comparison result may depend on command -follow. Its setting should always be the same as with the first adding of disk_path as iso_rr_path. If iso_rr_path does not exist yet, then it gets added. If disk_path does not exist, then iso_rr_path gets deleted. -update_l disk_prefix iso_rr_prefix disk_path [***] Perform -update_r with each of the disk_path parameters. iso_rr_path will be composed from disk_path by replacing disk_prefix by iso_rr_prefix. -update_li iso_rr_prefix disk_prefix iso_rr_path [***] Perform -update_r with each of the iso_rr_path parameters. disk_path will be composed from iso_rr_path by replacing iso_rr_prefix by disk_prefix. -update_lxi disk_prefix iso_rr_prefix disk_path [***] Perform -update_r with each of the disk_path parameters and with iso_rr_paths in the ISO filesystem which are derived from the disk_path parameters after exchanging disk_prefix by iso_rr_prefix. So, other than -update_l, this detects missing matches of disk_path and deletes the corresponding iso_rr_path. Note that relative disk_paths and disk_path patterns are interpreted as sub paths of the current disk working directory -cdx. The corresponding iso_rr_paths are derived by exchanging disk_prefix by iso_rr_prefix before pattern expansion happens. The current -cdi directory has no influence. -cut_out disk_path byte_offset byte_count iso_rr_path Map a byte interval of a regular disk file or of a device file into a regular file in the ISO image. The file depicted by disk_path has to support random read access. A symbolic link will only work if enabled by -follow "link" or "param". Cutting out a byte interval may be necessary if the disk file is larger than a single medium, or if it exceeds the traditional limit of 2 GiB - 1 for old operating systems, or the limit of 4 GiB - 1 for newer ones. Contemporary Linux kernels are able to read properly files >= 4 GiB - 1. A clumsy remedy for such limits is to backup file pieces and to concatenate them at restore time. A well tested chopping size is 2047m. It is permissible to request a higher byte_count than available. The resulting file will be truncated to the correct size of a final piece. To request a byte_offset higher than available yields no file in the ISO image but a SORRY event. E.g: -cut_out /my/disk/file 0 2047m \ /file/part_1_of_3_at_0_with_2047m_of_5753194821 \ -cut_out /my/disk/file 2047m 2047m \ /file/part_2_of_3_at_2047m_with_2047m_of_5753194821 \ -cut_out /my/disk/file 4094m 2047m \ /file/part_3_of_3_at_4094m_with_2047m_of_5753194821 If the directory /file does no yet exist, then its permissions are not taken from directory /my/disk but rather from /my/disk/file with additional x-permission for those who have r-permission. While command -split_size is set larger than 0, and if all pieces of a file reside in the same ISO directory with no other files, and if the names look like above, then their ISO directory will be recognized and handled like a regular file. This affects commands -compare*, -update*, and overwrite situations. See command -split_size for details. Another use case is copying the content of a device file as interval or as a whole into the emerging ISO filesystem. The fact that the byte_count is allowed to be unreasonably high enables copying of a whole device: -cut_out /dev/sdd3 0 1000g /content_of_sdd3 -cpr disk_path [***] iso_rr_path Insert the given files or directory trees from filesystem into the ISO image. The rules for generating the ISO addresses are similar as with shell command cp -r. Nevertheless, directories of the iso_rr_path are created if necessary. Especially a not yet existing iso_rr_path will be handled as directory if multiple disk_paths are present. The leafnames of the multiple disk_paths will be grafted under that directory as would be done with an existing directory. If a single disk_path is present then a non-existing iso_rr_path will get the same type as the disk_path. If a disk_path does not begin with '/' then -cdx is prepended. If the iso_rr_path does not begin with '/' then -cd is prepended. -mkdir iso_rr_path [...] Create empty directories if they do not exist yet. Existence as directory generates a WARNING event, existence as other file causes a FAILURE event. -lns target_text iso_rr_path Create a symbolic link with address iso_rr_path which points to target_text. iso_rr_path may not exist yet. Hint: Command -clone produces the ISO equivalent of a hard link. -clone iso_rr_path_original iso_rr_path_copy Create a copy of the ISO file object iso_rr_path_original with the new address iso_rr_path_copy. If the original is a directory then copy all files and directories underneath. If iso_rr_path_original is a boot catalog file, then it gets not copied but is silently ignored. The copied ISO file objects have the same attributes. Copied data files refer to the same content source as their originals. The copies may then be manipulated independendly of their originals. This command will refuse execution if the address iso_rr_path_copy already exists in the ISO tree. -cp_clone iso_rr_path_original [***] iso_rr_path_dest Create copies of one or more ISO file objects as with command -clone. In case of collision merge directories with existing ones, but do not overwrite existing ISO file objects. The rules for generating the copy addresses are the same as with command -cpr (see above) or shell command cp -r. Other than with -cpr, relative iso_rr_path_original will get prepended the -cd path and not the -cdx path. Consider to -mkdir iso_rr_path_dest before -cp_clone so the copy address does not depend on the number of iso_rr_path_original parameters.  File: xorriso.info, Node: SetInsert, Next: Manip, Prev: Insert, Up: Commands 9.5 Settings for file insertion =============================== -file_size_limit value [value [...]] -- Set the maximum permissible size for a single data file. The values get summed up for the actual limit. If the only value is "off" then the file size is not limited by 'xorriso'. Default is a limit of 100 extents, 4g -2k each: -file_size_limit 400g -200k -- When mounting ISO 9660 filesystems, old operating systems can handle only files up to 2g -1 --. Newer ones are good up to 4g -1 --. You need quite a new Linux kernel to read correctly the final bytes of a file >= 4g if its size is not aligned to 2048 byte blocks. 'xorriso''s own data read capabilities are not affected by operating system size limits. Such limits apply to mounting only. Nevertheless, the target filesystem of an -extract must be able to take the file size. -not_mgt code[:code[...]] Control the behavior of the exclusion lists. Exclusion processing happens before disk_paths get mapped to the ISO image, before disk files get compared with image files, and before image files get extracted to disk files. The absolute disk paths involved in such an action are matched against the -not_paths list. The leafnames of disk paths are matched against the patterns in the -not_leaf list. If a match is detected then the disk path will not be regarded as an existing file and not be added to the ISO image. Several codes are defined. The _on/_off settings persist until they are revoked by their_off/_on counterparts. "erase" empties the lists which were accumulated by -not_paths and -not_leaf. "reset" is like "erase" but also re-installs default behavior. "off" disables exclusion processing temporarily without invalidating the lists and settings. "on" re-enables exclusion processing. "param_off" applies exclusion processing only to paths below disk_path parameter of commands. I.e. explicitly given disk_paths are exempted from exclusion processing. "param_on" applies exclusion processing to command parameters as well as to files below such parameters. "subtree_off" with "param_on" excludes parameter paths only if they match a -not_paths item exactly. "subtree_on" additionally excludes parameter paths which lead to a file address below any -not_paths item. "ignore_off" treats excluded disk files as if they were missing. I.e. they get reported with -compare and deleted from the image with -update. "ignore_on" keeps excluded files out of -compare or -update activities. -not_paths disk_path [***] Add the given paths to the list of excluded absolute disk paths. If a given path is relative, then the current -cdx is prepended to form an absolute path. Pattern matching, if enabled, happens at definition time and not when exclusion checks are made. Keep in mind that there may be alternative paths to the same disk file. The exclusion tests are done literally, so that they do not keep files from getting into the ISO filesystem by other paths. Accordingly an exclusion does not prevent a disk file from being overwritten by file extraction via an alternative not excluded path. So the exclusions need to be coordinated with the actual disk_path parameters given with commands. (Do not forget to end the list of disk_paths by "--") -not_leaf pattern Add a single shell parser style pattern to the list of exclusions for disk leafnames. These patterns are evaluated when the exclusion checks are made. -not_list disk_path Read lines from disk_path and use each of them either as -not_paths parameter, if they contain a / character, or as -not_leaf pattern. -quoted_not_list disk_path Like -not_list but with quoted input reading rules. Each word is handled as one parameter for -not_paths or -not_leaf. -follow occasion[:occasion[...]] Enable or disable resolution of symbolic links and mountpoints under disk_paths. This applies to actions -add, -cut_out, -du*x, -ls*x, -findx, -concat, and to -disk_pattern expansion. There are three main kinds of follow decisison to be made: *link* is the hop from a symbolic link to its target file object for the purpose of reading. I.e. not for command -concat. If enabled then symbolic links are handled as their target file objects, else symbolic links are handled as themselves. *mount* is the hop from one filesystem to another subordinate filesystem. If enabled then mountpoint directories are handled as any other directory, else mountpoints are handled as empty directories if they are encountered in directory tree traversals. *concat* is the hop from a symbolic link to its target file object for the purpose of writing. I.e. for command -concat. This is a security risk ! Less general than above occasions: *pattern* is mount and link hopping, but only during -disk_pattern expansion. *param* is link hopping for parameter words (after eventual pattern expansion). If enabled then -ls*x will show the link targets rather than the links themselves. -du*x, -findx, and -add will process the link targets but not follow links in an eventual directory tree below the targets (unless "link" is enabled). -cut_out will process link targets. Occasions can be combined in a colon separated list. All occasions mentioned in the list will then lead to a positive follow decision. *off* prevents any positive follow decision. Use it if no other occasion applies. Shortcuts: *default* is equivalent to "pattern:mount:limit=100". *on* always decides positive. Equivalent to "link:mount:concat". Not an occasion but an optional setting is: *limit=*<number> which sets the maximum number of link hops. A link hop consists of a sequence of symbolic links and a final target of different type. Nevertheless those hops can loop. Example: $ ln -s .. uploop Link hopping has a built-in loop detection which stops hopping at the first repetition of a link target. Then the repeated link is handled as itself and not as its target. Regrettably one can construct link networks which cause exponential workload before their loops get detected. The number given with "limit=" can curb this workload at the risk of truncating an intentional sequence of link hops. -pathspecs "on"|"off"|"as_mkisofs" Control parameter interpretation with 'xorriso' actions -add and -path_list. Mode "as_mkisofs" enables pathspecs of the form *iso_rr_path=disk_path* like with program mkisofs -graft-points. All characters '\' must be escaped in both, iso_rr_path and disk_path. The character '=' must be escaped in the iso_rr_path and may or may not be escaped in the disk_path. This mode temporarily disables -disk_pattern expansion for command -add. Mode "on" does nearly the same. But '=' must only be escaped in the iso_rr_path and '\' must not be escaped at all. This has the disadvantage that one cannot express an iso_rr_path which ends by '\'. Mode "off" disables pathspecs of the form target=source and re-enables -disk_pattern expansion. -overwrite "on"|"nondir"|"off" Allow or disallow overwriting of existing files in the ISO image or in the local filesystem by files with the same name. With setting "off", name collisions with at least one non-directory file cause FAILURE events. Collisions of two directories lead to merging of their file lists. With setting "nondir", only directories are protected by such events, other existing file types get treated with -rm before the new file gets added. Setting "on" enables automatic -rm_r. I.e. a non-directory can replace an existing directory and all its subordinates. If restoring of files to the disk filesystem is enabled by -osirrox, then the overwrite rule applies to the target file objects on disk as well, but "on" is downgraded to "nondir". -split_size number["k"|"m"] Set the threshold for automatic splitting of regular files. Such splitting maps a large disk file onto a ISO directory with several part files in it. This is necessary if the size of the disk file exceeds -file_size_limit. Older operating systems can handle files in mounted ISO 9660 filesystems only if they are smaller than 2 GiB or in other cases 4 GiB. Default is 0 which will exclude files larger than -file_size_limit by a FAILURE event. A well tested -split_size is 2047m. Sizes above -file_size_limit are not permissible. The newly created ISO directory inherits its permissions from the data file with additional x-permission for those who have r-permission. While command -split_size is set larger than 0 such a directory with split file pieces will be recognized and handled like a regular file by commands -compare* , -update*, and in overwrite situations. There are -osirrox parameters "concat_split_on" and "concat_split_off" which control the handling when files get restored to disk. In order to be recognizable, the names of the part files have to describe the splitting by 5 numbers: part_number,total_parts,byte_offset,byte_count,disk_file_size which are embedded in the following text form: part_#_of_#_at_#_with_#_of_# Scaling characters like "m" or "k" are taken into respect. All digits are interpreted as decimal, even if leading zeros are present. E.g: /file/part_1_of_3_at_0_with_2047m_of_5753194821 No other files are allowed in the directory. All parts have to be present and their numbers have to be plausible. E.g. byte_count must be valid as -cut_out parameter and their contents may not overlap.  File: xorriso.info, Node: Manip, Next: CmdFind, Prev: SetInsert, Up: Commands 9.6 File manipulations ====================== The following commands manipulate files in the ISO image, regardless whether they stem from the loaded image or were newly inserted. -iso_rr_pattern "on"|"ls"|"off" Set the pattern expansion mode for the iso_rr_path parameters of several commands which support this feature. Setting "off" disables pattern expansion for all commands which are marked in this man page by "iso_rr_path [***]" or "iso_rr_pattern [***]". Setting "on" enables it for all those commands. Setting "ls" enables it only for those which are marked by "iso_rr_pattern [***]". Default is "on". -rm iso_rr_path [***] Delete the given files from the ISO image. Note: This does not free any space on the -indev medium, even if the deletion is committed to that same medium. The image size will shrink if the image is written to a different medium in modification mode. -rm_r iso_rr_path [***] Delete the given files or directory trees from the ISO image. See also the note with command -rm. -rmdir iso_rr_path [***] Delete empty directories. -move iso_rr_path iso_rr_path Rename the file given by the first (origin) iso_rr_path to the second (destination) iso_rr_path. Deviate from rules of shell command mv by not moving the origin file underneath an existing destination directory. The origin file will rather replace such a directory, if this is allowed by command -overwrite. -mv iso_rr_path [***] iso_rr_path Rename the given file objects in the ISO tree to the last parameter in the list. Use the same rules as with shell command mv. If pattern expansion is enabled and if the last parameter contains wildcard characters then it must match exactly one existing file address, or else the command fails with a FAILURE event. -chown uid iso_rr_path [***] Set ownership of file objects in the ISO image. uid may either be a decimal number or the name of a user known to the operating system. -chown_r uid iso_rr_path [***] Like -chown but affecting all files below eventual directories. -chgrp gid iso_rr_path [***] Set group attribute of file objects in the ISO image. gid may either be a decimal number or the name of a group known to the operating system. -chgrp_r gid iso_rr_path [***] Like -chgrp but affecting all files below eventual directories. -chmod mode iso_rr_path [***] Equivalent to shell command chmod in the ISO image. mode is either an octal number beginning with "0" or a comma separated list of statements of the form [ugoa]*[+-=][rwxst]* . Like: go-rwx,u+rwx . *Personalities*: u=user, g=group, o=others, a=all *Operators*: + adds given permissions, - revokes given permissions, = revokes all old permissions and then adds the given ones. *Permissions*: r=read, w=write, x=execute|inspect, s=setuid|setgid, t=sticky bit For octal numbers see man 2 stat. -chmod_r mode iso_rr_path [***] Like -chmod but affecting all files below eventual directories. -setfacl acl_text iso_rr_path [***] Attach the given ACL to the given iso_rr_paths. If the files already have ACLs, then those get deleted before the new ones get into effect. If acl_text is empty, or contains the text "clear" or the text "--remove-all", then the existing ACLs will be removed and no new ones will be attached. Any other content of acl_text will be interpreted as a list of ACL entries. It may be in the long multi-line format as put out by -getfacl but may also be abbreviated as follows: ACL entries are separated by comma or newline. If an entry is empty text or begins with "#" then it will be ignored. A valid entry has to begin by a letter out of {ugom} for "user", "group", "other", "mask". It has to contain two colons ":". A non-empty text between those ":" gives a user id or group id. After the second ":" there may be letters out of {rwx- #}. The first three give read, write, or execute permission. Letters "-", " " and TAB are ignored. "#" causes the rest of the entry to be ignored. Letter "X" or any other letters are not supported. Examples: g:toolies:rw,u:lisa:rw,u:1001:rw,u::wr,g::r,o::r,m::rw group:toolies:rw-,user::rw-,group::r--,other::r--,mask::rw- A valid entry may be prefixed by "d", some following characters and ":". This indicates that the entry goes to the "default" ACL rather than to the "access" ACL. Example: u::rwx,g::rx,o::,d:u::rwx,d:g::rx,d:o::,d:u:lisa:rwx,d:m::rwx -setfacl_r acl_text iso_rr_path [***] Like -setfacl but affecting all files below given directories. -setfacl_list disk_path Read the output of -getfacl_r or shell command getfacl -R and apply it to the iso_rr_paths as given in lines beginning with "# file:". This will change ownership, group and ACL of the given files. If disk_path is "-" then lines are read from standard input. Line "@" ends the list, "@@@" aborts without changing the pending iso_rr_path. Since -getfacl and getfacl -R strip leading "/" from file paths, the setting of -cd does always matter. -setfattr [-]name value iso_rr_path [***] Attach the given xattr pair of name and value to the given iso_rr_paths. If the given name is prefixed by "-", then the pair with that name gets removed from the xattr list. If name is "--remove-all" then all user namespace xattr of the given iso_rr_paths get deleted. In case of deletion, value must be an empty text. Which names are permissible depends on the setting of command -xattr. "on" or "user" restricts them to namespace "user". I.e. a name has to look like "user.x" or "user.whatever". -xattr setting "any" enables names from all namespaces except "isofs". Values and names undergo the normal input processing of 'xorriso'. See also command -backslash_codes. Other than with command -setfattr_list, the byte value 0 cannot be expressed via -setfattr. -setfattr_r [-]name value iso_rr_path [***] Like -setfattr but affecting all files below given directories. -setfattr_list disk_path Read the output format of -getfattr_r or shell command getfattr -Rd and apply it to the iso_rr_paths as given in lines beginning with "# file:". All previously existing xattr of the acceptable namespaces will be deleted before the new xattr get attached. The set of acceptable names depends on the setting of command -xattr. If disk_path is "-" then lines are read from standard input. Since -getfattr and getfattr -Rd strip leading "/" from file paths, the setting of -cd does always matter. Empty input lines and lines which begin by "#" will be ignored (except "# file:"). Line "@" ends the list, "@@@" aborts without changing the pending iso_rr_path. Other input lines must have the form name="value" The separator "=" is not allowed in names. Value may contain any kind of bytes. It must be in quotes. Trailing whitespace after the end quote will be ignored. Non-printables bytes and quotes must be represented as \XYZ by their octal 8-bit code XYZ. Use code \000 for 0-bytes. -chattr mode iso_rr_path [***] Set or unset Linux file attributes like program chattr(1) would do to disk files. Applying this command to files which are neither directory nor regular data file will yield a SORRY event, unless the mode is "-remove-lfa-flags". The first letter of the mode string determines what to do. The other letters are symbolic attribute flag letters out of the set "aAcCdDeEFhiIjNmPsStTuVxZ" as described in man 1 chattr. There is no restriction which attributes can be set or unset. But at restore time, unusual or unsuitable attributes may cause problems. First letter '+' causes the given attribute flags to be set. All other attributes stay as they are. First letter '-' causes the given attribute flags to be unset. All other attributes stay as they are. (Note that '-' is also accepted as symbolic attribute letter which has no effect.) A special case is the mode string "-remove-lfa-flags" which causes the Linux file attribute information to be removed from the file. The -find test -has_lfa_flags "-" will then not match the file any more. First letter '.' causes all attribute flags except the given ones to be unset. The given ones stay as they are. This is not a feature of program chattr(1). First letter '=' causes the given attribute flags to be set. All other get unset. Mode "=-" leads to all attribute flags being unset, but -find test -has_lfa_flags "-" will match the file afterwards. Example: -chattr +sDu /my/file /my/other_file - -chattr_r mode iso_rr_path [***] Like -chattr but affecting also all suitable files below the given directories. Except with mode "-remove-lfa-flags", the given iso_rr_path parameters need to be directories or regular data files or else a SORRY event will happen. Files below the given directories will be skipped silently if their type is not suitable for -chattr. -alter_date type timestring iso_rr_path [***] Alter the date entries of files in the ISO image. type may be one of the following: "a" sets access time, updates ctime. "m" sets modification time, updates ctime. "b" sets access time and modification time, updates ctime. "a-c", "m-c", and "b-c" set the times without updating ctime. "c" sets the ctime. timestring may be in the following formats (see also section EXAMPLES): As expected by program date: MMDDhhmm[[CC]YY][.ss]] As produced by program date: [Day] MMM DD hh:mm:ss [TZON] YYYY Relative times counted from current clock time: +|-Number["s"|"h"|"d"|"w"|"m"|"y"] where "s" means seconds, "h" hours, "d" days, "w" weeks, "m"=30d, "y"=365.25d plus 1d added to multiplication result. Absolute seconds counted from Jan 1 1970 00:00 GMT: =Number 'xorriso''s own timestamps: YYYY.MM.DD[.hh[mm[ss]]] scdbackup timestamps: YYMMDD[.hhmm[ss]] where "A0" is year 2000, "B0" is 2010, etc. ECMA-119 volume timestamps: YYYYMMDDhhmmsscc These are normally given as GMT. The suffix "LOC" causes local timezone conversion. E.g. 2013010720574700, 2013010720574700LOC. The last two digits cc (centiseconds) will be ignored, but must be present in order to make the format recognizable. Example: -alter_date m-c 2013.11.27.103951 /file1 /file2 - This command does not persistently apply to the boot catalog, which gets fresh timestamps at -commit time. Command -volume_date "uuid" can set this time value. -alter_date_r type timestring iso_rr_path [***] Like -alter_date but affecting all files below eventual directories. -hide hide_state iso_rr_path [***] Prevent the names of the given files from showing up in the directory trees of ISO 9660 and/or Joliet and/or HFS+ when the image gets written. The data content of such hidden files will be included in the resulting image, even if they do not show up in any directory. But you will need own means to find nameless data in the image. Warning: Data which are hidden from the ISO 9660 tree will not be copied by the write method of modifying. Possible values of hide_state are: "iso_rr" for hiding from ISO 9660 tree, "joliet" for Joliet tree, "hfsplus" for HFS+, "on" for them all. "off" means visibility in all directory trees. These values may be combined. E.g.: joliet:hfsplus This command does not apply to the boot catalog. Rather use: -boot_image "any" "cat_hidden=on"  File: xorriso.info, Node: CmdFind, Next: Filter, Prev: Manip, Up: Commands 9.7 Tree traversal command -find ================================ -find iso_rr_path [test [op] [test ...]] [-exec action [params]] -- A restricted substitute for shell command find in the ISO image. It performs an action on matching file objects at or below iso_rr_path. If not used as last command in the line then the parameter list needs to get terminated by "--". Tests are optional. If they are omitted then action is applied to all file objects. If tests are given then they form together an expression. The action is applied only if the expression matches the file object. Default expression operator between tests is -and, i.e. the expression matches only if all its tests match. Available tests are: -name pattern : Matches if pattern matches the file leaf name. If the pattern does not contain any of the characters "*?[", then it will be truncated according to -file_name_limit and thus match the truncated name in the ISO filesystem. -wholename pattern : Matches if pattern matches the file path as it would be printed by action "echo". Character '/' can be matched by wildcards. If pattern pieces between '/' do not contain any of the characters "*?[", they will be truncated according to -file_name_limit. -disk_name pattern : Like -name but testing the leaf name of the file source on disk. Can match only data files which do not stem from the loaded image, or for directories above such data files. With directories the result can change between -find runs if their content stems from multiple sources. -disk_path disk_path : Matches if the given disk_path is equal to the path of the file source on disk. The same restrictions apply as with -disk_name. -type type_letter : Matches files of the given type: "block", "char", "dir", "pipe", "file", "link", "socket", "eltorito", and "Xotic" which matches what is not matched by the other types. Only the first letter is interpreted. E.g.: -find / -type d -size [+-][=]number[cwbdksmg] : Matches files with matching relation to the given size number. The prefix defines the desired relation: No prefix or prefix "=" means: File must have exactly the given size. Prefix "+" means: File must be larger than given size. Prefix "+=" means: File must be larger than or equal to given size limit. Prefix "-" means: File must be smaller than given size limit. Prefix "-=" means: File must be smaller than or equal to given size limit. Suffixes are peculiar to stay compatible with program "find": No suffix means blocks of 512 bytes, "c" means single bytes, "w" means 2 bytes, "b" means 512 bytes. The suffixes "k", "M", and "G" mean 1024, 1024k, and 1024M respectively. As usual with xorriso, the suffixes "d" and "s" mean 512 and 2048 and all suffixes are recognized as both, uppercase and lowercase letters. E.g. match files of 4 GiB or larger: -size +=4g -maxdepth number : Matches only files which are at most at the given depth level relative to the iso_rr_path where -find starts. That path itself is at depth 0, its directory children are at 1, their directory children at 2, and so on. -mindepth number : Matches only files which are at least at the given depth level. -damaged : Matches files which use data blocks marked as damaged by a previous run of -check_media. The damage info vanishes when a new ISO image gets loaded. Note that a MD5 session mismatch marks all files of the session as damaged. If finer distinction is desired, perform -md5 off before -check_media. -pending_data : Matches files which get their content from outside the loaded ISO image. -lba_range start_lba block_count : Matches files which use data blocks within the range of start_lba and start_lba+block_count-1. -has_acl : Matches files which have a non-trivial ACL. -has_xattr : Matches files which have xattr name-value pairs from user namespace. -has_aaip : Matches files which have ACL or any xattr. -has_lfa_flags flag_letters : Matches files which have Linux file attributes attached and have all flags set which correspond to the characters in the string flag_letters. The characters may be zero or more out of the set "aAcCdDeEFhiIjNmPsStTuVxZ". The character '-' will be ignored. The flag_letters string "-" matches any attribute set, but not a file with no Linux file attributes attached. E.g. look for files with both flags 'i' (immutable) and 'd' (no dump) set: -has_lfa_flags di -has_some_lfa_flags_of flag_letters : Similar to -has_lfa_flags but matching files which have at least one of the flags set which correspond to the characters in the string flag_letters. The flag_letters string "-" never matches any file. E.g. look for files which have 'i' or 'a' or both of them set: -has_some_lfa_flags_of ia -has_any_xattr : Matches files which have any xattr other than ACL. -has_md5 : Matches data files which have MD5 checksums. -has_hfs_crtp creator type : Matches files which have the given HFS+ creator and type attached. These are codes of 4 characters which get stored if -hfsplus is enabled. Use a single dash '-' as wildcard that matches any such code. E.g:. -has_hfs_crtp YYDN TEXT -has_hfs_crtp - - -has_hfs_bless blessing : Matches files which bear the given HFS+ blessing. It may be one of : "ppc_bootdir", "intel_bootfile", "show_folder", "os9_folder", "osx_folder", "any". See also action set_hfs_bless. -has_filter : Matches files which are filtered by -set_filter. -hidden hide_state : Matches files which are hidden in "iso_rr" tree, in "joliet" tree, in "hfsplus" tree, in all trees ("on"), or not hidden in any tree ("off"). Those which are hidden in some tree match -not -hidden "off". -bad_outname namespace : Matches files with names which change when converted forth and back between the local character set and one of the namespaces "rockridge", "joliet", "ecma119", "hfsplus". All applicable -compliance rules are taken into respect. Rule "omit_version" is always enabled, because else namespaces "joliet" and "ecma119" would cause changes with every non-directory name. Consider to also enable rules "no_force_dots" and "no_j_force_dots". The namespaces use different character sets and apply further restrictions to name length, permissible characters, and mandatory name components. "rockridge" uses the character set defined by -out_charset, "joliet" uses UCS-2BE, "ecma119" uses ASCII, "hfsplus" uses UTF-16BE. -name_limit_blocker length : Matches file names which would prevent command -file_name_limit with the given length. The command itself reports only the first problem file. -prune : If this test is reached and the tested file is a directory then -find will not dive into that directory. This test itself does always match. -use_pattern "on"|"off" : This pseudo test controls the interpretation of wildcards with tests -name, -wholename, and -disk_name. Default is "on". If interpretation is disabled by "off", then the parameters of -name, -wholename, and -disk_name have to match literally rather than as search pattern. This test itself does always match. -or_use_pattern "on"|"off" : Like -use_pattern, but automatically appending the test by -or rather than by -and. Further the test itself does never match. So a subsequent test -or will cause its other operand to be performed. -decision "yes"|"no" : If this test is reached then the evaluation ends immediately and action is performed if the decision is "yes" or "true". See operator -if. -true and -false : Always match or match not, respectively. Evaluation goes on. -sort_lba : Always match. This causes -find to perform its action in a sequence sorted by the ISO image block addresses of the files. It may improve throughput with actions which read data from optical drives. Action will always get the absolute path as parameter. Available operators are: -not : Matches if the next test or sub expression does not match. Several tests do this specifically: -undamaged, -lba_range with negative start_lba, -has_no_acl, -has_no_xattr, -has_no_aaip, -has_no_filter . -and : Matches if both neighboring tests or expressions match. -or : Matches if at least one of both neighboring tests or expressions matches. -sub ... -subend or ( ... ) : Enclose a sub expression which gets evaluated first before it is processed by neighboring operators. Normal precedence is: -not, -or , -and. -if ... -then ... -elseif ... -then ... -else ... -endif : Enclose one or more sub expressions. If the -if expression matches, then the -then expression is evaluated as the result of the whole expression up to -endif. Else the next -elseif expression is evaluated and if it matches, its -then expression. Finally in case of no match, the -else expression is evaluated. There may be more than one -elseif. Neither -else nor -elseif are mandatory. If -else is missing and would be hit, then the result is a non-match. -if-expressions are the main use case for above test -decision. Default action is *echo*, i.e. to print the address of the found file. Other actions are certain 'xorriso' commands which get performed on the found files. These commands may have specific parameters. See also their particular descriptions. chown and chown_r change the ownership and get the user id as parameter. E.g.: -exec chown thomas -- chgrp and chgrp_r change the group attribute and get the group id as parameter. E.g.: -exec chgrp_r staff -- chmod and chmod_r change access permissions and get a mode string as parameter. E.g.: -exec chmod a-w,a+r -- alter_date and alter_date_r change the timestamps. They get a type character and a timestring as parameters. E.g.: -exec alter_date "m" "Dec 30 19:34:12 2007" -- set_to_mtime sets the ctime and atime to the value found in mtime. lsdl prints file information like shell command ls -dl. compare performs command -compare with the found file address as iso_rr_path and the corresponding file address below its parameter disk_path_start. For this the iso_rr_path of the -find command gets replaced by the disk_path_start. E.g.: -find /thomas -exec compare /home/thomas -- update performs command -update with the found file address as iso_rr_path. The corresponding file address is determined like with above action "compare". update_merge is like update but does not delete the found file if it is missing on disk. It may be run several times and records with all visited files whether their counterpart on disk has already been seen by one of the update_merge runs. Finally, a -find run with action "rm_merge" may remove all files that saw no counterpart on disk. Up to the next "rm_merge" or "clear_merge" all newly inserted files will get marked as having a disk counterpart. rm removes the found iso_rr_path from the image if it is not a directory with files in it. I.e. this "rm" includes "rmdir". rm_r removes the found iso_rr_path from the image, including whole directory trees. rm_merge removes the found iso_rr_path if it was visited by one or more previous actions "update_merge" and saw no counterpart on disk in any of them. The marking from the update actions is removed in any case. clear_merge removes an eventual marking from action "update_merge". report_damage classifies files whether they hit a data block that is marked as damaged. The result is printed together with the address of the first damaged byte, the maximum span of damages, file size, and the path of the file. report_lba prints files which are associated to image data blocks. It tells the logical block address, the block number, the byte size, and the path of each file. There may be reported more than one line per file if the file has more than one section. In this case each line has a different extent number in column "xt". report_sections like report_lba but telling the byte sizes of the particular sections rather than the overall byte size of the file. getfacl prints access permissions in ACL text form to the result channel. setfacl attaches ACLs after removing existing ones. The new ACL is given in text form as defined with command -setfacl. E.g.: -exec setfacl u:lisa:rw,u::rw,g::r,o::-,m::rw -- getfattr prints xattr name-value pairs to the result channel. The choice of namespaces depends on the setting of command -xattr: "off", "on", or "user" restricts it to the namespace "user", "any" only omits namespace "isofs". get_any_xattr prints xattr name-value pairs from any namespace except ACL to the result channel. This is mostly for debugging of namespace "isofs". list_extattr mode prints a script to the result channel, which would use FreeBSD command setextattr to set the file's xattr name-value pairs of user namespace. Parameter mode controls the form of the output of names and values. Default mode "e" prints harmless characters in shell quotation marks, but represents texts with octal 001 to 037 and 0177 to 0377 by an embedded echo -e command. Mode "q" prints any characters in shell quotation marks. This might not be terminal-safe but should work in script files. Mode "r" uses no quotation marks. Not safe. Mode "b" prints backslash encoding. Not suitable for shell parsing. E.g.: -exec list_extattr e - Command -backslash_codes does not affect the output. lsattrd shows the Linux file attribute flags like command -lsattrd does. chattr applies -chattr with the given mode. Other than command -chattr this silently skips any file which are not -type "dir" or "file". E.g.: -exec chattr +sDu - get_md5 prints the MD5 sum, if recorded, together with file path. check_md5 compares the MD5 sum, if recorded, with the file content and reports if mismatch. E.g.: -find / -not -pending_data -exec check_md5 FAILURE -- make_md5 equips a data file with an MD5 sum of its content. Useful to upgrade the files in the loaded image to full MD5 coverage by the next commit with -md5 "on". E.g.: -find / -type f -not -has_md5 -exec make_md5 -- setfattr sets or deletes xattr name value pairs. E.g.: -find / -has_xattr -exec setfattr --remove-all " -- set_hfs_crtp adds, changes, or removes HFS+ creator and type attributes. E.g.: -exec set_hfs_crtp YYDN TEXT E.g.: -find /my/dir -prune -exec set_hfs_crtp -delete - get_hfs_crtp prints the HFS+ creator and type attributes together with the iso_rr_path, if the file has such attributes at all. E.g.: -exec get_hfs_crtp set_hfs_bless applies or removes HFS+ blessings. They are roles which can be attributed to up to four directories and a data file: "ppc_bootdir", "intel_bootfile", "show_folder", "os9_folder", "osx_folder". They may be abbreviated as "p", "i", "s", "9", and "x". Each such role can be attributed to at most one file object. "intel_bootfile" is the one that would apply to a data file. All others apply to directories. The -find run will end as soon as the first blessing is issued. The previous bearer of the blessing will lose it then. No file object can bear more than one blessing. E.g.: -find /my/blessed/directory -exec set_hfs_bless p Further there is blessing "none" or "n" which revokes any blessing from the found files. This -find run will not stop when the first match is reached. E.g.: -find / -has_hfs_bless any -exec set_hfs_bless none get_hfs_bless prints the HFS+ blessing role and the iso_rr_path, if the file is blessed at all. E.g.: -exec get_hfs_bless set_filter applies or removes filters. E.g.: -exec set_filter --zisofs -- mkisofs_r applies the rules of mkisofs -r to the file object: user id and group id become 0, all r-permissions get granted, all w denied. If there is any x-permission, then all three x get granted. s- and t-bits get removed. sort_weight attributes a LBA weight number to regular files. The number may range from -2147483648 to 2147483647. The higher it is, the lower will be the block address of the file data in the emerging ISO image. Currently the boot catalog has a hardcoded weight of 1 billion. Normally it should occupy the block with the lowest possible address. Data files which are loaded by -indev or -dev get a weight between 1 and 2 exp 28 = 268,435,456, depending on their block address. This shall keep them roughly in the same order if the write method of modifying is applied. Data files which are added by other commands get an initial weight of 0. Boot image files have a default weight of 2. E.g.: -exec sort_weight 3 -- show_stream shows the content stream chain of a data file. show_stream_id is like show_stream, but also prints between stream type and first ":" in square brackets libisofs id numbers: [fs_id,dev_id,ino_id]. hide brings the file into one of the hide states "on", "iso_rr", "joliet", "hfsplus", "off". They may be combined. E.g.: joliet:hfsplus E.g.: -find / -disk_name *_secret -exec hide on print_outname prints in the first line the filename as registered by the program model, and in the second line the filename after conversion forth and back between local character set and one of the namespaces "rockridge", "joliet", "ecma119", or "hfsplus". The third output line is "-" . The name conversion does not take into respect the possibility of name collisions in the target namespace. Such collisions are most likely in "joliet" and "ecma119", where they get resolved by automatic file name changes. E.g.: -find / -bad_outname joliet -exec print_outname joliet estimate_size prints a lower and an upper estimation of the number of blocks which the found files together will occupy in the emerging ISO image. This does not account for the superblock, for the directories in the -find path, or for image padding. find performs another run of -find on the matching file address. It accepts the same params as -find, except iso_rr_path. E.g.: -find / -name '???' -type d -exec find -name '[abc]*' -exec chmod a-w,a+r --  File: xorriso.info, Node: Filter, Next: Writing, Prev: CmdFind, Up: Commands 9.8 Filters for data file content ================================= *Filters* may be installed between data files in the ISO image and their content source outside the image. They may also be used vice versa between data content in the image and target files on disk. Built-in filters are "--zisofs" and "--zisofs-decode". The former is to be applied via -set_filter, the latter is automatically applied if zisofs compressed content is detected with a file when loading the ISO image. Another built-in filter pair is "--gzip" and "--gunzip" with suffix ".gz". They behave about like external gzip and gunzip but avoid forking a process for each single file. So they are much faster if there are many small files. -external_filter name option[:option] program_path [arguments] -- Register a content filter by associating a name with a program path, program arguments, and some behavioral options. Once registered it can be applied to multiple data files in the ISO image, regardless whether their content resides in the loaded ISO image or in the local filesystem. External filter processes may produce synthetic file content by reading the original content from stdin and writing to stdout whatever they want. They must deliver the same output on the same input in repeated runs. Options are: "default" means that no other option is intended. "suffix=..." sets a file name suffix. If it is not empty then it will be appended to the file name or removed from it. "remove_suffix" will remove a file name suffix rather than appending it. "if_nonempty" will leave 0-sized files unfiltered. "if_reduction" will try filtering and revoke it if the content size does not shrink. "if_block_reduction" will revoke if the number of 2 kB blocks does not shrink. "used=..." is ignored. Command -status shows it with the number of files which currently have the filter applied. Examples: -external_filter bzip2 suffix=.bz2:if_block_reduction \ /usr/bin/bzip2 -- -external_filter bunzip2 suffix=.bz2:remove_suffix \ /usr/bin/bunzip2 -- -unregister_filter name Remove an -external_filter registration. This is only possible if the filter is not applied to any file in the ISO image. -close_filter_list Irrevocably ban commands -concat "pipe", -external_filter, and -unregister_filter, but not -set_filter. Use this to prevent external filtering in general or when all intended filters are registered and -concat mode "pipe" shall be disallowed. External filters may also be banned totally at compile time of 'xorriso'. By default they are banned if 'xorriso' runs under setuid permission. -set_filter name iso_rr_path [***] Apply an -external_filter or a built-in filter to the given data files in the ISO image. If the filter suffix is not empty , then it will be applied to the file name. Renaming only happens if the filter really gets attached and is not revoked by its options. By default files which already bear the suffix will not get filtered. The others will get the suffix appended to their names. If the filter has option "remove_suffix", then the filter will only be applied if the suffix is present and can be removed. Name oversize or collision caused by suffix change will prevent filtering. With most filter types this command will immediately run the filter once for each file in order to determine the output size. Content reading operations like -extract , -compare and image generation will perform further filter runs and deliver filtered content. At image generation time the filter output must still be the same as the output from the first run. Filtering for image generation does not happen with files from the loaded ISO image if the write method of growing is in effect (i.e -indev and -outdev are identical). The reserved filter name "--remove-all-filters" revokes filtering. This will revoke suffix renamings as well. Use "--remove-all-filters+" to prevent any suffix renaming. Attaching or detaching filters will not alter the state of -changes_pending. If the filter manipulations shall be the only changes in a write run, then explicitly execute -changes_pending "yes". -set_filter_r name iso_rr_path [***] Like -set_filter but affecting all data files below eventual directories.  File: xorriso.info, Node: Writing, Next: SetWrite, Prev: Filter, Up: Commands 9.9 Writing the result, drive control ===================================== (see also paragraph about settings below) -rollback Discard the manipulated ISO image and reload it from -indev. (Use -rollback_end if immediate program end is desired.) -changes_pending "no"|"yes"|"mkisofs_printed"|"show_status" Write runs are performed only if a change of the image has been made since the image was loaded or created blank. Vice versa the program will start a write run for pending changes when it ends normally (i.e. not by abort and not by command -rollback_end). The command -changes_pending can be used to override the automatically determined state. This is mainly useful for setting state "yes" despite no real changes were made. The sequence -changes_pending "no" -end is equivalent to the command -rollback_end. State "mkisofs_printed" is caused by emulation command -as mkisofs if option -print-size is present. The pseudo-state "show_status" can be used to print the current state to result channel. Image loading or manipulations which happen after this command will again update automatically the change status of the image. -commit Perform the write operation. Afterwards, if -outdev is readable, make it the new -dev and load the image from there. Switch to growing mode. (A subsequent -outdev will activate modification mode or blind growing.) -commit is performed automatically at end of program if there are uncommitted manipulations pending. So, to perform a final write operation with no new -dev and no new loading of image, rather execute command -end. If you want to go on without image loading, execute -commit_eject "none". To eject after write without image loading, use -commit_eject "all". To suppress a final write, execute -rollback_end. Writing can last quite a while. It is not unnormal with several types of media that there is no progress visible for the first few minutes or that the drive gnaws on the medium for a few minutes after all data have been transmitted. 'xorriso' and the drives are in a client-server relationship. The drives have much freedom about what to do with the media. Some combinations of drives and media simply do not work, despite the promises by their vendors. If writing fails then try other media or another drive. The reason for such failure is hardly ever in the code of the various burn programs but you may well try some of those listed below under SEE ALSO. -eject "in"|"out"|"all" Eject the medium in -indev, -outdev, or both drives, respectively. Note: It is not possible yet to effectively eject disk files. -commit_eject "in"|"out"|"all"|"none" Combined -commit and -eject. When writing has finished do not make -outdev the new -dev, and load no ISO image. Rather eject -indev and/or -outdev. Give up any non-ejected drive. -blank mode Make media ready for writing from scratch (if not -dummy is activated). This affects only the -outdev not the -indev. If both drives are the same and if the ISO image was altered then this command leads to a FAILURE event. Defined modes are: as_needed, fast, all, deformat, deformat_quickest "as_needed" cares for used CD-RW, DVD-RW and for used overwritable media by applying -blank "fast". It applies -format "full" to yet unformatted DVD-RAM and BD-RE. Other media in blank state are gracefully ignored. Media which cannot be made ready for writing from scratch cause a FAILURE event. "fast" makes CD-RW and unformatted DVD-RW re-usable, or invalidates overwritable ISO images. "all" might work more thoroughly and need more time. "deformat" converts overwritable DVD-RW into unformatted ones. "deformat_quickest" is a faster way to deformat or blank DVD-RW but produces media which are only suitable for a single session. Some drives announce this state by not offering feature 21h, but some drives offer it anyway. If feature 21h is missing, then 'xorriso' will refuse to write on DVD-RW if not command -close is set to "on". The progress reports issued by some drives while blanking are quite unrealistic. Do not conclude success or failure from the reported percentages. Blanking was successful if no SORRY event or worse occurred. Mode may be prepended by "force:" in order to override the evaluation of the medium state by libburn. E.g. "force:fast". Blanking will nevertheless only succeed if the drive is willing to do it. -format mode Convert unformatted DVD-RW into overwritable ones, "de-ice" DVD+RW, format newly purchased BD-RE or BD-R, re-format DVD-RAM or BD-RE. Defined modes are: as_needed, full, fast, by_index_<num>, fast_by_index_<num>, by_size_<num>, fast_by_size_<num>, without_spare "as_needed" formats yet unformatted DVD-RW, DVD-RAM, BD-RE, or blank unformatted BD-R. Other media are left untouched. "full" (re-)formats DVD-RW, DVD+RW, DVD-RAM, BD-RE, or blank unformatted BD-R. "fast" does the same as "full" but tries to be quicker. "by_index_" selects a format out of the descriptor list issued by command -list_formats. The index number from that list is to be appended to the mode word. E.g: "by_index_3". "fast_by_index_" does the same as "by_index_" but tries to be quicker. "by_size_" selects a format out of the descriptor list which provides at least the given size. That size is to be appended to the mode word. E.g: "by_size_4100m". This applies to media with Defect Management. On BD-RE it will not choose format 0x31, which offers no Defect Management. "fast_by_size_" does the same as "by_size_" but tries to be quicker. "without_spare" selects the largest format out of the descriptor list which provides no Spare Area for Defect Management. On BD-RE this will be format 0x31. The formatting action has no effect on media if -dummy is activated. Formatting is normally needed only once during the lifetime of a medium, if ever. But it is a reason for re-formatting if: DVD-RW was deformatted by -blank, DVD+RW has read failures (re-format before next write), DVD-RAM or BD-RE shall change their amount of defect reserve. BD-R may be written unformatted or may be formatted before first use. Formatting activates Defect Management which tries to catch and repair bad spots on media during the write process at the expense of half speed even with flawless media. The progress reports issued by some drives while formatting are quite unrealistic. Do not conclude success or failure from the reported percentages. Formatting was successful if no SORRY event or worse occurred. Be patient with apparently frozen progress. -list_formats Put out a list of format descriptors as reported by the output drive for the current medium. The list gives the index number after "Format idx", a MMC format code, the announced size in blocks (like "2236704s") and the same size in MiB. MMC format codes are manifold. Most important are: "00h" general formatting, "01h" increases reserve space for DVD-RAM, "26h" for DVD+RW, "30h" for BD-RE with reserve space, "31h" for BD-RE without reserve space, "32h" for BD-R. Smaller format size with DVD-RAM, BD-RE, or BD-R means more reserve space. -list_speeds Put out a list of speed values as reported by the drives with the loaded media. The list tells read speeds of the input drive and of the output drive. Further it tells write speeds of the output drive. The list of write speeds does not necessarily mean that the medium is writable or that these speeds are actually achievable. Especially the lists reported with empty drive or with ROM media obviously advertise speeds for other media. It is not mandatory to use speed values out of the listed range. The drive is supposed to choose a safe speed that is as near to the desired speed as possible. At the end of the list, "Write speed L" and "Write speed H" are the best guesses for lower and upper write speed limit. "Write speed l" and "Write speed h" may appear only with CD and eventually override the list of other speed offers. Only if the drive reports contradicting speed information there will appear "Write speed 0", which tells the outcome of speed selection by command -speed 0, if it deviates from "Write speed H". "Read speed L" and "Read speed H" tell the minimum and maximum read speeds, as reported by the drive. They would be chosen by -read_speed "min" or "max" if they undercut or surpass the built-in limits. These are "1x", "52xCD", "24xDVD", "20xBD". -list_profiles "in"|"out"|"all" Put out a list of media types supported by -indev, -outdev, or both, respectively. The currently recognized type is marked by text "(current)". -truncate_overwritable entity id adjust On overwritable medium copy the volume descriptors of an existing session to the overall descriptors at LBA 0 ff. This makes all sessions *inaccessible* which are younger than the activated one. A reason to do this would be read errors in the younger sessions and the wish to re-write or skip them. This operation is only allowed if no changes to the loaded filesystem are pending. If an -indev is acquired then it is released before the write operation begins and re-acquired only in case of success. The parameters "entity" and "id" have the same meaning as with command -load. They choose the existing ISO session which shall become the youngest accessible session. Available entity names are "session", "track", "lba", "sbsector", "volid". "auto" makes few sense. id is a number or search text as appropriate for the given entity. Parameter "adjust" controls the claimed size of the activated session. Text "new" means the size of the newly activated session as it was before this command. I.e. the space of the then inaccessible younger sessions will be re-used when appending more sessions. "old" means the size up to the end of the previously youngest session. I.e. "old" will not free the space of the then inaccessible younger sessions for re-use. A number preceded by "+" gives the number of bytes to be added to "new". A number without "+" gives the overall number of bytes. In any case the result may not be smaller than "new". Numbers may have a unit suffix: "d"=512, "k"=1024, "s"=2048, "m"=1024k, "g"=1024m. Normally the volume descriptors at block 16 ff. have to be readable. Only with entity "lba" or "sbsector" and adjust mode "new" it is possible to address a session if block 16 ff. yields no valid volume descriptors. Examples: Activate session 4 and enable overwriting of the blocks of younger sessions: -truncate_overwritable session 4 new Activate session 4 and claim the blocks of younger sessions as useless part of session 4: -truncate_overwritable session 4 old Let session 4 claim additional 500 MiB as useless data: -truncate_overwritable session 4 +500m -close_damaged "as_needed"|"force" Try to close the upcoming track and session if the drive reported the medium as damaged. This may apply to CD-R, CD-RW, DVD-R, DVD-RW, DVD+R, DVD+R DL, or BD-R media. It is indicated by warning messages when the drive gets acquired, and by a remark "but next track is damaged" with the line "Media status :" of command -toc. The setting of command -close determines whether the medium stays appendable. Mode "as_needed" gracefully refuses on media which are not reported as damaged. Mode "force" attempts the close operation even with media which appear undamaged. No image changes are allowed to be pending before this command is performed. After closing was attempted, both drives are given up.  File: xorriso.info, Node: SetWrite, Next: Bootable, Prev: Writing, Up: Commands 9.10 Settings for result writing ================================ Rock Ridge info will be generated by default. ACLs will be written according to the setting of command -acl. -joliet "on"|"off" If enabled by "on", generate Joliet tree additional to ISO 9660 + Rock Ridge tree. -hfsplus "on"|"off" If enabled by "on", generate a HFS+ filesystem inside the ISO 9660 image and mark it by Apple Partition Map (APM) entries in the System Area, the first 32 KiB of the image. This may collide with data submitted by -boot_image system_area=. The first 8 bytes of the System Area get overwritten by { 0x45, 0x52, 0x08 0x00, 0xeb, 0x02, 0xff, 0xff } which can be executed as x86 machine code without negative effects. So if an MBR gets combined with this feature, then its first 8 bytes should contain no essential commands. The next blocks of 2 KiB in the System Area will be occupied by APM entries. The first one covers the part of the ISO image before the HFS+ filesystem metadata. The second one marks the range from HFS+ metadata to the end of file content data. If more ISO image data follow, then a third partition entry gets produced. Other features of xorriso might cause the need for more APM entries. The HFS+ filesystem is not suitable for add-on sessions produced by the multi-session method of growing. An existing ISO image may nevertheless be the base for a new image produced by the method of modifying. If -hfsplus is enabled when -indev or -dev gets executed, then AAIP attributes get loaded from the input image and checked for information about HFS creator, filetype, or blessing. If found, then they get enabled as settings for the next image production. Therefore it is advisable to perform -hfsplus "on" before -indev or -dev. Information about HFS creator, type, and blessings gets stored by xorriso if -hfsplus is enabled at -commit time. It is stored as copy outside the HFS+ partition, but rather along with the Rock Ridge information. xorriso does not read any information from the HFS+ meta data. Be aware that HFS+ is case-insensitive although it can record file names with upper-case and lower-case letters. Therefore, file names from the iso_rr name tree may collide in the HFS+ name tree. In this case they get changed by adding underscore characters and counting numbers. In case of very long names, it might be necessary to map them to "MANGLED_...". WARNING: The HFS+ implementation in libisofs has a limit of 125,829,120 bytes for the size of the overall directory tree. This suffices for about 300,000 files of normal name length. If the limit gets exceeded, a FAILURE event will be issued and the ISO production will not happen. -rockridge "on"|"off" Mode "off" disables production of Rock Ridge information for the ISO 9660 file objects. The multi-session capabilities of xorriso depend much on the naming fidelity of Rock Ridge. So it is strongly discouraged to deviate from default setting "on". -compliance rule[:rule...] Adjust the compliance to specifications of ISO 9660/ECMA-119 and its contemporary extensions. In some cases it is worth to deviate a bit in order to circumvent bugs of the intended reader system or to get unofficial extra features. There are several adjustable rules which have a keyword each. If they are mentioned with this command then their rule gets added to the relaxation list. This list can be erased by rules "strict" or "clear". It can be reset to its start setting by "default". All of the following relaxation rules can be revoked individually by appending "_off". Like "deep_paths_off". Rule keywords are: "iso_9660_level="number chooses level 1 with ECMA-119 names of the form 8.3 and -file_size_limit <= 4g - 1, or level 2 with ECMA-119 names up to length 32 and the same -file_size_limit, or level 3 with ECMA-119 names up to length 32 and -file_size_limit >= 400g -200k. If necessary -file_size_limit gets adjusted. "allow_dir_id_ext" allows ECMA-119 names of directories to have a name extension as with other file types. It does not force dots and it omits the version number, though. This is a bad tradition of mkisofs which violates ECMA-119. Especially ISO level 1 only allows 8 characters in a directory name and not 8.3. "omit_version" does not add versions (";1") to ECMA-119 and Joliet file names. "only_iso_version" does not add versions (";1") to Joliet file names. "deep_paths" allows ECMA-119 file paths deeper than 8 levels. "long_paths" allows ECMA-119 file paths longer than 255 characters. "long_names" allows up to 37 characters with ECMA-119 file names. "no_force_dots" does not add a dot to ECMA-119 file names which have none. "no_j_force_dots" does not add a dot to Joliet file names which have none. "lowercase" allows lowercase characters in ECMA-119 file names. "7bit_ascii" allows nearly all 7-bit characters in ECMA-119 file names. Not allowed are 0x0 and '/'. If not "lowercase" is enabled, then lowercase letters get converted to uppercase. "full_ascii" allows all 8-bit characters except 0x0 and '/' in ECMA-119 file names. "untranslated_names" might be dangerous for inadverted reader programs which rely on the restriction to at most 37 characters in ECMA-119 file names. This rule allows ECMA-119 file names up to 96 characters with no character conversion. If a file name has more characters, then image production will fail deliberately. "untranslated_name_len="number enables untranslated_names with a smaller limit for the length of file names. 0 disables this feature, -1 chooses maximum length limit, numbers larger than 0 give the desired length limit. "joliet_long_names" allows Joliet leaf names up to 103 characters rather than 64. "joliet_long_paths" allows Joliet paths longer than 240 characters. "joliet_utf16" encodes Joliet names in UTF-16BE rather than UCS-2. The difference is with characters which are not present in UCS-2 and get encoded in UTF-16 by 2 words of 16 bit each. Both words then stem from a reserved subset of UCS-2. "always_gmt" stores timestamps in GMT representation with timezone 0. "rec_mtime" records with non-RockRidge directory entries the disk file's mtime and not the creation time of the image. This applies to the ECMA-119 tree (plain ISO 9660), to Joliet, and to ISO 9660:1999. "rec_mtime" is default. If disabled, it gets automatically re-enabled by -as mkisofs emulation when a pathspec is encountered. "new_rr" uses Rock Ridge version 1.12 (suitable for GNU/Linux but not for older FreeBSD or for Solaris). This implies "aaip_susp_1_10_off" which may be changed by subsequent "aaip_susp_1_10". Default is "old_rr" which uses Rock Ridge version 1.10. This implies also "aaip_susp_1_10" which may be changed by subsequent "aaip_susp_1_10_off". "aaip_susp_1_10" allows AAIP to be written as unofficial extension of RRIP rather than as official extension under SUSP-1.12. "no_emul_toc" saves 64 kB with the first session on overwritable media but makes the image incapable of displaying its session history. "iso_9660_1999" causes the production of an additional directory tree compliant to ISO 9660:1999. It can record long filenames for readers which do not understand Rock Ridge. "old_empty" uses the old way of of giving block addresses in the range of [0,31] to files with no own data content. The new way is to have a dedicated block to which all such files will point. "max_ce_entries="number sets the maximum number of SUSP CE entries and thus continuation areas. Each continuation area can hold at most 2048 bytes of SUSP data (Rock Ridge or AAIP). The first area can be smaller. There might be some waste at the end of each area. When the maximum number is exceeded during ISO filesystem production then either xattr and ACL get dropped from the affected file or an error gets reported and image production is prevented. Linux silently ignores a file when encountering its 32th CE entry. (Workaround is to mount the filesystem with option "norock".) So the default setting is 31. Minimum is 1, maximum is 100000. If a limit higher than 31 is chosen and 31 gets surpassed, then a warning message gets reported. "max_ce_drop="mode sets the behavior when the limit of max_ce_entries= is surpassed. Mode "off" causes an error message and prevents image production. Mode "xattr" and "xattr_acl" report a warning, delete from the affected file all xattr of namespaces other than "isofs", and then try again. If this still surpasses the limit, then mode "xattr_acl" deletes all ACL from the file and retries. If this still surpasses the limit, then an error message gets reported and image production is prevented. Default setting is "clear:iso_9660_level=3:only_iso_version:deep_paths:long_paths: no_j_force_dots:always_gmt:rec_mtime:old_rr:max_ce_entries=31: max_ce_drop=xattr_acl" Note: The term "ECMA-119 name" means the plain ISO 9660 names and attributes which get visible if the reader ignores Rock Ridge. -rr_reloc_dir name Specify the name of the relocation directory in which deep directory subtrees shall be placed if -compliance is set to "deep_paths_off" or "long_paths_off". A deep directory is one that has a chain of 8 parent directories (including root) above itself, or one that contains a file with an ECMA-119 path of more than 255 characters. The overall directory tree will appear originally deep when interpreted as Rock Ridge tree. It will appear as re-arranged if only ECMA-119 information is considered. The default relocation directory is the root directory. By giving a non-empty name with -rr_reloc_dir, a directory in the root directory may get this role. If that directory does not already exist at -commit time, then it will get created and marked for Rock Ridge as relocation artefact. At least on GNU/Linux it will not be displayed in mounted Rock Ridge images. The name must not contain a '/' character and must not be longer than 255 bytes. -volid text Specify the volume ID, which most operating systems will consider to be the volume name of the image or medium. 'xorriso' accepts any text up to 32 characters, but according to rarely obeyed specs stricter rules apply: ECMA-119 demands ASCII characters out of [A-Z0-9_]. Like: "IMAGE_23" Joliet allows 16 UCS-2 characters. Like: "Windows name" Be aware that the volume id might get used automatically as the name of the mount point when the medium is inserted into a playful computer system. If an ISO image gets loaded while the volume ID is set to default "ISOIMAGE" or to "", then the volume ID of the loaded image will become the effective volume id for the next write run. But as soon as command -volid is performed afterwards, this pending ID is overridden by the new setting. Consider this when setting -volid "ISOIMAGE" before executing -dev, -indev, or -rollback. If you insist in -volid "ISOIMAGE", set it again after those commands. -volset_id text Set the volume set ID string to be written with the next -commit. Permissible are up to 128 characters. This setting gets overridden by image loading. -publisher text Set the publisher ID string to be written with the next -commit. This may identify the person or organisation who specified what shall be recorded. Permissible are up to 128 characters. This setting gets overridden by image loading. -application_id text Set the application ID string to be written with the next -commit. This may identify the specification of how the data are recorded. Permissible are up to 128 characters. This setting gets overridden by image loading. The special text "@xorriso@" gets converted to the ID string of 'xorriso' which is normally written as -preparer_id. It is a wrong tradition to write the program ID as -application_id. -system_id text Set the system ID string to be written with the next -commit. This may identify the system which can recognize and act upon the content of the System Area in image blocks 0 to 15. Permissible are up to 32 characters. This setting gets overridden by image loading. -volume_date type timestring Set one of the four overall timestamps for subsequent image writing. Available types are: "c" time when the volume was created. "m" time when volume was last modified. "x" time when the information in the volume expires. "f" time since when the volume is effectively valid. "all_file_dates" sets mtime, atime, and ctime of all files and directories to the given time. If the timestring is "set_to_mtime", then the atime and ctime of each file and directory get set to the value found in their mtime. These actions stay delayed until actual ISO production begins. Up to then they can be revoked by "all_file_dates" with empty timestring or timestring "default". The timestamps of the El Torito boot catalog file get refreshed when the ISO is produced. They can be influenced by "uuid". "uuid" sets a timestring that overrides "c" and "m" times literally and sets the time of the El Torito boot catalog. It must consist of 16 decimal digits which form YYYYMMDDhhmmsscc, with YYYY between 1970 and 2999. Time zone is GMT. It is supposed to match this GRUB line: search --fs-uuid --set YYYY-MM-DD-hh-mm-ss-cc E.g. 2010040711405800 is 7 Apr 2010 11:40:58 (+0 centiseconds). Timestrings for the other types may be given as with command -alter_date. Some of them are prone to timezone computations. The timestrings "default" or "overridden" cause default settings: "c" and "m" will show the current time of image creation. "x" and "f" will be marked as insignificant. "uuid" will be deactivated. At -commit time, some timestamps get set to the maximum value of effectively written volume creation and modification time: El Torito boot catalog, HFS+ superblock, ECMA-119 file modification time if -compliance "no_rec_mtime". The isohybrid MBR id is computed from "uuid" if given, else from the effective volume modification date. -copyright_file text Set the copyright file name to be written with the next -commit. This should be the ISO 9660 path of a file in the image which contains a copyright statement. Permissible are up to 37 characters. This setting gets overridden by image loading. -abstract_file text Set the abstract file name to be written with the next -commit. This should be the ISO 9660 path of a file in the image which contains an abstract statement about the image content. Permissible are up to 37 characters. This setting gets overridden by image loading. -biblio_file text Set the biblio file name to be written with the next -commit. This should be the ISO 9660 path of a file in the image which contains bibliographic records. Permissible are up to 37 characters. This setting gets overridden by image loading. -preparer_id text Set the preparer ID string to be written with the next -commit. This may identify the person or other entity which controls the preparation of the data which shall be recorded. Normally this should be the ID of 'xorriso' and not of the person or program which operates 'xorriso'. Please avoid to change it. Permissible are up to 128 characters. The special text "@xorriso@" gets converted to the ID string of 'xorriso' which is default at program startup. Unlike other ID strings, this setting is not influenced by image loading. -application_use character|0xXY|disk_path Specify the content of the Application Use field which can take at most 512 bytes. If the parameter of this command is empty, then the field is filled with 512 0-bytes. If it is a single character, then it gets repeated 512 times. If it begins by "0x" followed by two hex digits [0-9a-fA-F], then the digits are read as byte value which gets repeated 512 times. Any other parameter text is used as disk_path to open a data file and to read up to 512 bytes from it. If the file is smaller than 512 bytes, then the remaining bytes in the field get set to binary 0. This setting is not influenced by image loading. -out_charset character_set_name Set the character set to which file names get converted when writing an image. See paragraph "Character sets" for more explanations. When loading the written image after -commit the setting of -out_charset will be copied to -in_charset. -uid uid User id to be used for all files when the new ISO tree gets written to media. -gid gid Group id to be used for all files when the new ISO tree gets written to media. -zisofs parameter[:parameters] Set global parameters for zisofs compression. This data format is recognized and transparently uncompressed by some Linux kernels. It is to be applied via command -set_filter with built-in filter "--zisofs". Note: This command is only permitted while no -zisofs filters are applied to any files. Parameters are: "level="[0-9] zlib compression: 0=none, 1=fast,..., 9=slow "block_size="32k|64k|128k sets the size of version 1 compression blocks. "by_magic=on" enables an expensive test at image generation time which checks files from disk whether they already are zisofs compressed, e.g. by program mkzftree. "by_magic=v2" enables processing of already zisofs2 compressed files additionally to those of zisofs version 1. "by_magic=off" disables both. "version_2="off|as_needed|on controls compression by experimental version zisofs2 which can encode files of size 4 GiB or larger. The Linux kernel (as of 5.9) does not yet know this format and will complain like isofs: Unknown ZF compression algorithm: PZ The files will then appear in their compressed form with zisofs2 header, block pointer list, and compressed data. zisofs2 is recognized by xorriso in files from loaded images and gets equipped with -zisofs-decode filters, unless restrictions on the number of block pointers prevent this. Mode "off" restricts compression to files smaller than 4 GiB uncompressed size. Mode "as_needed" uses zisofs2 for larger files. Mode "on" uses zisofs2 for all zisofs compressed files. "susp_z2="off|on controls production of SUSP entries "Z2" instead of "ZF" with zisofs2 compressed files. Unaware Linux kernels are supposed to silently ignore "Z2" entries. "block_size_v2="32k|64k|128k|256k|512k|1m sets the size of compression blocks for zisofs2. "bpt_target="-1|>0 sets a number of block pointers per file, which is considered low enough to justify a reduction of block size. If this number is larger than 0, then block sizes smaller than the settings of block_size= or block_size_v2= are tried whether they yield not more block pointers than the given number. If so, the smallest suitable block size is applied. The inavoidable final block pointer counts. E.g. a file of 55 KiB has 3 block pointers if block size is 32k, and 2 block pointers with block size 64k. bpt_target=-1 disables this automatic block size adjustment. "max_bpt="1k...128g sets the limit for the overall allocated block pointer memory. Block pointers occupy virtual memory while a file gets uncompressed and while a file, which shall be compressed, waits for ISO filesystem creation. One pointer occupies 8 bytes of memory and governs block_size or block_size_v2 uncompressed bytes. I.e. with block size 128k, 1m of block pointer memory suffices for at most 16g of uncompressed file size. Each file consumes one end block pointer, independently of the file size. Partially filled end blocks may further reduce the effective payload. In case of overflow of the max_bpt limit while adding compression filters the program tries to go on by discarding all buffered block pointers of previously added -zisofs filters. From then on all newly added filters will discard their block pointers immediately after being added. Discarded block pointers cause an additional read and compression run of the input file during the production of the ISO filesystem. "max_bpt_f="1k...128g sets the limit for the memory size of the block pointer list of a single file. max_bpt_f is never larger than max_bpt. If either is set to violate this rule, the other gets set to the same value. If both values are the same before a change by max_bpt= or max_bpt_f=, then both limits stick together unless the limit is decreased by max_bpt_f=. "bpt_free_ratio="-1|0.0...1.0 sets a threshold for switching to block pointer discarding during compression. If less than the given fraction of the max_bpt_f= memory is free, then block pointers of compression filters get discarded immediately after being added. Value -1 disables this feature. "default" is the same as "level=6:block_size=32k:by_magic=off: version_2=off:block_size_v2=128k:susp_z2=off:max_bpt=256m:max_bpt_f=256m: bpt_free_ratio=-1". -speed code|number[k|m|c|d|b] Set the burn speed. Default is "max" (or "0") = maximum speed as announced by the drive. Further special speed codes are: "min" (or "-1") selects minimum speed as announced by the drive. "none" avoids to send a speed setting command to the drive before burning begins. Speed can be given in media dependent numbers or as a desired throughput per second in MMC compliant kB (= 1000) or MB (= 1000 kB). Media x-speed factor can be set explicitly by "c" for CD, "d" for DVD, "b" for BD, "x" is optional. Example speeds: 706k = 706kB/s = 4c = 4xCD 5540k = 5540kB/s = 4d = 4xDVD If there is no hint about the speed unit attached, then the medium in the -outdev will decide. Default unit is CD = 176.4k. MMC drives usually activate their own idea of speed and take the speed value given by the burn program only as upper limit for their own decision. -stream_recording "on"|"off"|"full"|"data"|number Setting "on" tries to circumvent the management of defects on DVD-RAM, BD-RE, or BD-R. Defect management keeps partly damaged media usable. But it reduces write speed to half nominal speed even if the medium is in perfect shape. For the case of flawless media, one may use -stream_recording "on" to get full speed. "full" tries full speed with all write operations, whereas "on" does this only above byte address 32s. One may give a number of at least 16s in order to set an own address limit. "data" causes full speed to start when superblock and directory entries are written and writing of file content blocks begins. -dvd_obs "default"|"32k"|"64k"|"obs_pad"|"bdr_obs_exempt" GNU/Linux specific: Set the number of bytes to be transmitted with each write operation to DVD or BD media. A number of 64 KB may improve throughput with bus systems which show latency problems. The default depends on media type, on command -stream_recording , and on compile time options. On all systems: "obs_pad" pads the data of the last write operation of a DVD-R[W] DAO session or BD-R session up to the full size of an output chunk. This padding has to be applied automatically to the other DVD and BD media types, where it causes e.g. ISO images to have trailing unclaimed blocks. Whether it is applied automatically to BD-R depends on "bdr_obs_exempt". "obs_pad" can be disabled by "no_obs_pad". "bdr_obs_exempt" exempts BD-R media from automatic unconditional transaction end padding, provided that this padding is not requested by "obs_pad" and that no stream_recording is requested. "bdr_obs_exempt" can be disabled by "no_obs_exempt". This is a new feature introduced with version 1.5.6. It might become default in later versions. -modesty_on_drive parameter[:parameters] Control whether the drive buffer shall be kept from getting completely filled. Parameter "on" (or "1") keeps the program from trying to write to the burner drive while its buffer is in danger to be filled over a given limit. If this limit is exceeded then the program will wait until the filling reaches a given low percentage value. This can ease the load on operating system and drive controller and thus help with achieving better input bandwidth if disk and burner are not on independent controllers (like hda and hdb). It may also help with throughput problems of simultaneous burns on different burners with Linux kernels like 3.16, if one has reason not to fix the problem by -scsi_dev_family "sg". On the other hand it increases the risk of buffer underflow and thus reduced write speed. Some burners are not suitable because they report buffer fill with granularity too coarse in size or time, or expect their buffer to be filled to the top before they go to full speed. Parameters "off" or "0" disable this feature. The threshold for beginning to wait is given by parameter "max_percent=". Parameter "min_percent=" defines the threshold for resuming transmission. Percentages are permissible in the range of 25 to 100. Numbers in this range without a prepended name are interpreted as "on:min_percent=". E.g.: -modesty_on_drive 75 The optimal values depend on the buffer behavior of the drive. Parameter "timeout_sec=" defines after which time of unsuccessful waiting the modesty shall be disabled because it does not work. Parameter "min_usec=" defines the initial sleeping period in microseconds. If the drive buffer appears to be too full for sending more data, the program will wait the given time and inquire the buffer fill state again. If repeated inquiry shows not enough free space, the sleep time will slowly be increased to what parameter "max_usec=" defines. Parameters, which are not mentioned with a -modesty_on_drive command, stay unchanged. Default is: -modesty_on_drive off:min_percent=90:max_percent=95: timeout_sec=120:min_usec=5000:max_usec=25000 -use_immed_bit "on"|"off"|"default" Control whether several long lasting SCSI commands shall be executed with the Immed bit, which makes the commands end early while the drive operation is still going on. xorriso then inquires progress indication until the drive reports to be ready again. If this feature is turned off, then blanking and formatting will show no progress indication. It may depend on the operating system whether -use_immed_bit is set to "off" by default. Command -status will tell by appending "/on" or "/off" if a drive has already been acquired and -use_immed_bit is currently set to "default". Command -use_immed_bit tolerates and ignores such appended text. -stdio_sync "on"|"off"|"end"|number Set the number of bytes after which to force output to stdio: pseudo drives. This forcing keeps the memory from being clogged with lots of pending data for slow devices. Default "on" is the same as "16m". Forced output can be disabled by "off", or be delayed by "end" until all data are produced. If a number is chosen, then it must be at least 64k. -dummy "on"|"off" If "on" then simulate burning or refuse with FAILURE event if no simulation is possible, do neither blank nor format. -fs number["k"|"m"] Set the size of the fifo buffer which smoothens the data stream from ISO image generation to media burning. Default is 4 MiB, minimum 64 kiB, maximum 1 GiB. The number may be followed by letter "k" or "m" which means unit is kiB (= 1024) or MiB (= 1024 kiB). -close "on"|"off"|"as_needed" If -close is set to "on" then mark the written medium as not appendable any more. This will have no effect on overwritable media types. Setting "on" is the contrary of cdrecord option -multi, and is one aspect of growisofs option -dvd-compat. If set to "off" then keep the medium writable for an appended session. If set to "as_needed" then use "on" only if "off" is predicted to fail with the given medium and its state. Not all drives correctly recognize fast-blanked DVD-RW which need "on". If there is well founded suspicion that a burn run failed due to -close "off", then -close "as_needed" causes a re-try with "on". Note that emulation command -as "cdrecord" temporarily overrides the current setting of -close by its own default -close "on" if its option -multi is missing. -write_type "auto"|"tao"|"sao/dao" Set the write type for the next burn run. "auto" will select SAO with blank CD media, DAO with blank DVD-R[W] if -close is "on", and elsewise CD TAO or the equivalent write type of the particular DVD/BD media. Choosing TAO or SAO/DAO explicitly might cause the burn run to fail if the desired write type is not possible with the given media state. -padding number["k"|"m"]|"included"|"appended" Append the given number of extra bytes to the image stream. This is a traditional remedy for a traditional bug in block device read drivers. Needed only for CD recordings in TAO mode. Since one can hardly predict on what media an image might end up, 'xorriso' adds the traditional 300k of padding by default to all images. For images which will never get to a CD it is safe to use -padding 0 . Normally padding is not written as part of the ISO image but appended after the image end. This is -padding mode "appended". Emulation command -as "mkisofs" and command -jigdo cause padding to be written as part of the image. The same effect is achieved by -padding mode "included".  File: xorriso.info, Node: Bootable, Next: Jigdo, Prev: SetWrite, Up: Commands 9.11 Bootable ISO images ======================== Contrary to published specifications many BIOSes will load an El Torito record from the first session on media and not from the last one, which gets mounted by default. This makes no problems with overwritable media, because they appear to inadverted readers as one single session. But with multi-session media CD-R[W], DVD-R[W], DVD+R, it implies that the whole bootable system has to reside already in the first session and that the last session still has to bear all files which the booted system expects after mounting the ISO image. If a boot image from ISOLINUX or GRUB is known to be present on media then it is advised to patch it when a follow-up session gets written. But one should not rely on the capability to influence the bootability of the existing sessions, unless one can assume overwritable media. Normally the boot images are data files inside the ISO filesystem. By special path "-interval:appended_partition_NNN:all::" it is possible to refer to an appended partition. The number NNN gives the partition number as used with the corresponding command -append_partition. E.g.: -append_partition 2 0xef /tmp/efi.img -boot_image any efi_path=-interval:appended_partition_2:all:: There are booting mechanisms which do not use an El Torito record but rather start at the first bytes of the image: PC-BIOS MBR or EFI GPT for hard-disk-like devices, APM partition entries for Macs which expect HFS+ boot images, MIPS Volume Header for old SGI computers, DEC Boot Block for old MIPS DECstation, SUN Disk Label for SPARC machines, HP-PA boot sector for HP PA-RISC machines, DEC Alpha SRM boot sector for old DEC Alpha machines. Several of the following commands expect disk paths as input but also accept description strings for the libisofs interval reader, which is able to cut out data from disk files or -indev and to zeroize parts of the content: command -append_partition, boot specs system_area=, grub2_mbr=, prep_boot_part=, efi_boot_part=. The description string consists of the following components, separated by colon ':' "--interval:"Flags":"Interval":"Zeroizers":"Source The component "--interval" states that this is not a plain disk path but rather an interval reader description string. The component Flags modifies the further interpretation: "local_fs" demands to read from a file depicted by the path in Source. "imported_iso" demands to read from the -indev. This works only if -outdev is not the same as -indev. The Source component is ignored. "appended_partition_NNN" with a decimal number NNN works only for -boot_image bootspecs which announce El Torito boot image paths: bin_path=, efi_path=. The number gives the partition number as used with the corresponding command -append_partition. The component Interval consists of two byte address numbers separated by a "-" character. E.g. "0-429" means to read bytes 0 to 429. The component Zeroizers consists of zero or more comma separated strings. They define which part of the read data to zeroize. Byte number 0 means the byte read from the Interval start address. Each string may be one of: "zero_mbrpt" demands to zeroize the MBR partition table if bytes 510 and 511 bear the MBR signature 0x55 0xaa. "zero_gpt" demands to check for a GPT header in bytes 512 to 1023, to zeroize it and its partition table blocks. "zero_apm" demands to check for an APM block 0 and to zeroize its partition table blocks. Start_byte"-"End_byte demands to zeroize the read-in bytes beginning with number Start_byte and ending after End_byte. The component Source is the file path with flag "local_fs", and ignored with flag "imported_iso". Byte numbers may be scaled by a suffix out of {k,m,g,t,s,d} meaning multiplication by {1024, 1024k, 1024m, 1024g, 2048, 512}. A scaled value end number depicts the last byte of the scaled range. E.g. "0d-0d" is "0-511". Examples: "local_fs:0-32767:zero_mbrpt,zero_gpt,440-443:/tmp/template.iso" "imported_iso:45056d-47103d::" -boot_image "any"|"isolinux"|"grub" "discard"|"keep"|"patch"|"replay"|"show_status"| bootspec|"next" Define the equipment of the emerging filesystem with boot entry points. With systems which boot via BIOS or EFI this is a set of El Torito boot images, possibly MBR boot code, and possibly partition tables of type MBR, GPT, or APM. Such file sets get produced by boot loader systems like ISOLINUX or GRUB. Each -boot_image command has two parameters: type and setting. More than one -boot_image command may be used to define the handling of one or more boot images. Sequence matters. Types *isolinux* and *grub* care for known peculiarities. Type *any* makes no assumptions about the origin of the boot images. When loading an ISO filesystem, system area and El Torito boot images get loaded, too. The default behavior is not to write loaded El Torito boot images and to write the loaded system area content without alterations. *discard* gives up the El Torito boot catalog and its boot images. regardless whether loaded from an ISO filesystem or defined by commands. Any BIOS or EFI related boot options get revoked. Nevertheless, loaded system area data and the possibly defined appended partitions stay valid. If desired, they have to be erased by -boot_image any system_area=/dev/zero -append_partition all revoke - *keep* keeps or copies El Torito boot images unaltered and writes a new catalog. *patch* applies patching to existing El Torito boot images if they seem to bear a boot info table. A boot info table needs to be patched when the boot image gets newly introduced into the ISO image or if an existing image gets relocated. This is automatically done if type "isolinux" or "grub" is given, but not with "any". If patching is enabled, then boot images from previous sessions will be checked whether they seem to bear a boot info table. If not, then they stay unpatched. This check is not infallible. So if you do know that the images need no patching, use "any" "keep". "grub" "patch" will not patch EFI images (platform_id=0xef). *replay* is a more modern version of "patch", which not only cares for existing El Torito boot equipment but also for the recognizable boot provisions in the System Area. It discards any existing -boot_image setting including the system area and executes the commands proposed by command -report_el_torito "cmd". Special care has to be taken with manipulations of files in the emerging ISO filesystem which are mentioned by -report_el_torito "cmd". Their paths are memorized at ISO image load time. In general -boot_image "any" "replay" should be applied after all file manipulations are done. All file paths from the -report_el_torito commands must then still lead to data files which are suitable for their respective commands. The effects of file path changes after -boot_image "any" "replay" can be surprising. E.g. removing or replacing a boot image with boot info table for legacy BIOS leads to a hidden boot image with the content as it was at "replay" time. Doing the same with a boot image for EFI leads to an error at -commit time or to the new file content becomming the EFI boot image. Out of historical reasons *replay* does not revoke all possibly made -append_partition settings but only overwrites those for which the loaded ISO image provides candidates. *show_status* will print what is known about the loaded boot images and their designated fate. Examples: Drop El Torito: -boot_image any discard Drop El Torito, system area, appended partitions: -boot_image any discard -boot_image any system_area=/dev/zero -append_partition all revoke - Maintain recognizable stuff after revoking possibly made -append_partition settings to surely get only the partitions from the loaded ISO: -append_partition all revoke - -boot_image any replay Re-adjust El Torito only for GRUB: -boot_image grub patch Re-adjust El Torito only for ISOLINUX: -boot_image isolinux patch A *bootspec* is a word of the form name=value. It is used to describe the parameters of a boot feature. The names "dir", "bin_path", "efi_path" lead to El Torito bootable images. Name "system_area" activates a given file as MBR or other disk header. On all media types this is possible within the first session. In further sessions an existing boot image can get replaced by a new one, but depending on the media type this may have few effect at boot time. See above. El Torito boot images have to be added to the ISO image by normal means (image loading, -map, -add, ...). In case of ISOLINUX the files should reside either in ISO image directory /isolinux or in /boot/isolinux . In that case it suffices to use as bootspec the text "*dir=/isolinux*" or "dir=/boot/isolinux". E.g.: -boot_image isolinux dir=/boot/isolinux which bundles these individual settings: -boot_image isolinux bin_path=/boot/isolinux/isolinux.bin -boot_image isolinux cat_path=/boot/isolinux/boot.cat -boot_image isolinux load_size=2048 -boot_image any boot_info_table=on An El Torito boot catalog file gets inserted into the ISO image with address *cat_path=* with the first -boot_image "any" "next" or at -commit time. It is subject to normal -overwrite and -reassure processing if there is already a file with the same name. The catalog lists the boot images and is read by the boot facility to choose one of the boot images. But it is not necessary that it appears in the directory tree at all. One may hide it in all trees by *cat_hidden=on*. Other possible values are "iso_rr", "joliet", "hfsplus", and the default "off". The timestamps of the boot catalog file are refreshed at commit time. Command -volume_date "uuid" can be used to set their value. *bin_path=* depicts an El Torito boot image file, a binary program which is to be started by the hardware boot facility (e.g. the BIOS) at boot time. *efi_path=* depicts an El Torito boot image file that is ready for EFI booting. This is normally a FAT filesystem image not larger than 65535 blocks of 512 bytes (= 32 MiB - 512). Its load_size is determined automatically, no boot info table gets written, no boot medium gets emulated, platform_id is 0xef. *emul_type=* can be one of "no_emulation", "hard_disk", "diskette". It controls the boot medium emulation code of a boot image. The default "no_emulation" is suitable for ISOLINUX, GRUB, FreeBSD cdboot. *load_size=* is a value which depends on the boot image. Default is 2048 which matches the expectations of most boot images. The special value "full" means the full size of the boot image file rounded up to a multiple of 2048 bytes. Maximum is 33,552,384 bytes. *boot_info_table=on* causes address patching to bytes 8 to 63 of the boot image which is given by "any" "bin_path=". "boot_info_table=off" disables this patching. *grub2_boot_info=on* causes address patching to byte 2548 of the boot image which is given by "any" "bin_path=". The address is written as 64 bit little-endian number. It is the 2KB block address of the boot image content, multiplied by 4, and then incremented by 5. "grub2_boot_info=off" disables this patching. *platform_id=* defines by a hexadecimal or decimal number the Platform ID of the boot image. "0x00" is 80x86 PC-BIOS, "0x01" is PowerPC, "0x02" is Mac, "0xef" is EFI (decimal "239"). *id_string=*text|56_hexdigits defines the ID string of the boot catalog section where the boot image will be listed. If the value consists of 56 characters [0-9A-Fa-f] then it is converted into 28 bytes, else the first 28 characters become the ID string. The ID string of the first boot image becomes the overall catalog ID. It is limited to 24 characters. Other id_strings become section IDs. *sel_crit=*hexdigits defines the Selection Criteria of the boot image. Up to 20 bytes get read from the given characters [0-9A-Fa-f]. They get attributed to the boot image entry in the catalog. *next* ends the definition of a boot image and starts a new one. Any following -bootimage bootspecs will affect the new image. The first "next" discards loaded boot images and their catalog. *system_area=*disk_path copies at most 32768 bytes from the given disk file to the very start of the ISO image. This System Area is reserved for system dependent boot software, e.g. an MBR which can be used to boot from USB stick or hard disk. Other than an El Torito boot image, the file disk_path needs not to be added to the ISO image. In multi-session situations the existing System Area is preserved by default. In in this case, the special disk_path "." prevents reading of a disk file but nevertheless causes adjustments in the loaded system area data. Such adjustments may get ordered by -boot_image commands. Special "system_area=/dev/zero" causes 32k of 0-bytes. Use this to e.g. discard partition tables which were loaded with the ISO image. This setting is the default with the write method of Modifying, when -indev and -outdev are both used and not the same drive. If you indeed need to copy the unchanged system area from -indev to -outdev, use "system_area=-interval:imported_iso:0s-15s::" , which was the default in older versions of xorriso. *-boot_image isolinux system_area=* implies "partition_table=on". In this case, the disk path should lead to one of the SYSLINUX files isohdp[fp]x*.bin or to a file which was derived from one of those files. E.g. to the first 512 bytes from an ISOLINUX isohybrid ISO image. El Torito boot images (dir=, bin_path=, efi_path=) may then be augmented by *isolinux partition_entry=gpt_basdat* or *isolinux partition_entry=gpt_hfsplus*, and by *isolinux partition_entry=apm_hfsplus*. The boot image will then be mentioned in an invalid GPT as Basic Data or GPT HFS+ partition, and in a valid APM as HFS+ partition. The first three GPT partitions will also be marked by MBR partitions. The MBR partition of type 0xEF is what actually is used by EFI firmware for booting from USB stick. *-boot_image any gpt_disk_guid=*value controls whether an emerging GPT shall get a randomly generated disk GUID or whether the GUID is supplied by the user. Value "random" is default. Value "volume_date_uuid" produces a low quality GUID from the value set by -volume_date "uuid". A string of 32 hex digits, or a RFC 4122 compliant GUID string may be used to set the disk GUID directly. UEFI prescribes the first three components of a RFC 4122 GUID string to be byte-swapped in the binary representation: E.g. gpt_disk_guid=2303cd2a-73c7-424a-a298-25632da7f446 equals gpt_disk_guid=2acd0323c7734a42a29825632da7f446 The partition GUIDs get generated by minimally varying the disk GUID. *-boot_image any part_like_isohybrid=on* enables -boot_image isolinux partition_entry= even if no -boot_image isolinux system_area= is given. No MBR partition of type 0xee emerges, even if GPT gets produced. Gaps between GPT and APM partitions will not be filled by more partitions. Appended partitions get mentioned in APM if other APM partitions emerge. *-boot_image any iso_mbr_part_type=*number sets the partition type of the MBR partition which represents the ISO or at least protects it. Number may be 0x00 to 0xff. The text "default" re-enables the default types of the various occasions to create an ISO MBR partition. This is without effect if no such partition emerges by other settings or if the partition type is prescribed mandatorily like 0xee for GPT protective MBR or 0x96 for CHRP. If instead a type_guid is given by a 32-digit hex string like a2a0d0ebe5b9334487c068b6b72699c7 or by a structured text like EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, then it will be used as partition type if the ISO filesystem appears as partition in GPT. In MBR, C12A7328-F81F-11D2-BA4B-00A0C93EC93B will be mapped to 0xef. Any other GUID will be mapped to 0x83. *grub2_mbr=*disk_path works like "any" system_area= with additional patching for modern GRUB MBRs. The content start address of the first boot image is converted to a count of 512 byte blocks, and an offset of 4 is added. The result is written as 64 bit little-endian number to byte address 0x1b0. This feature can be revoked either by grub2_mbr= with empty disk path, or by submitting a disk_path via system_area=. *partition_table=on* causes a simple partition table to be written into bytes 446 to 511 of the System Area. With type "isolinux" it shows a partition that begins at byte 0 and it causes the LBA of the first boot image to be written into the MBR. For the first session this works only if also "system_area=" and "bin_path=" or "dir=" is given. With types "any" and "grub" it shows a single partition which starts at byte 512 and ends where the ISO image ends. This works with or without system_area= or boot image. Bootspecs chrp_boot_part=, prep_boot_part=, and efi_boot_part= overwrite this entry in the MBR partition table. If types "isolinux" or "grub" are set to "patch", then "partition_table=on" is activated without new boot image. In this case the existing System Area gets checked whether it bears addresses and sizes as if it had been processed by "partition_table=on". If so, then those parameters get updated when the new System Area is written. *appended_part_as=gpt* marks partitions from -append_partition in GPT rather than in MBR. In this case the MBR shows a single partition of type 0xee which covers the whole output data. The number of appendable partitions with GPT is 8 rather than 4 with MBR. *appended_part_as=mbr* is the default. Appended partitions get marked in GPT only if GPT is produced because of other settings. If given explicitly, this clears setting "gpt" and "apm". Nevertheless "apm" may be added to "mbr". *appended_part_as=apm* marks partitions from -append_partition in APM additionally to "mbr" or "gpt". The partition number in APM will not be influenced by -append_partition parameter partition_number. By default, appended partitions get marked in APM only if APM is produced because of other options together with part_like_isohybrid="on". *chrp_boot_part=on* causes a single partition in MBR which covers the whole ISO image and has type 0x96. This is not compatible with any other feature that produces MBR partition entries. It makes GPT unrecognizable. *prep_boot_part=*disk_path inserts the content of a data file into the image and marks it by an MBR partition of type 0x41. The parts of the ISO image before and after this partition will be covered by further MBR partitions. The data file is supposed to contain ELF executable code. *efi_boot_part=*disk_path inserts the content of a data file into the image and marks it by a GPT partition. If not chrp_boot_part=on, then the first partition in MBR will have type 0xee to announce the presence of GPT. The data file is supposed to contain a FAT filesystem. Instead of a disk_path, the word --efi-boot-image may be given. It exposes in GPT the content of the first El Torito EFI boot image as EFI system partition. EFI boot images are introduced by bootspec efi_path=. The affected EFI boot image cannot show up in HFS+ because it is stored outside the HFS+ partition. *partition_offset=*2kb_block_adr causes a partition table with a single partition that begins at the given block address. This is counted in 2048 byte blocks, not in 512 byte blocks. If the block address is non-zero then it must be at least 16. A non-zero partition offset causes two superblocks to be generated and two sets of directory trees. The image is then mountable from its absolute start as well as from the partition start. The offset value of an ISO image gets preserved when a new session is added. So the value defined here is only in effect if a new ISO image gets written. *partition_hd_cyl=*number gives the number of heads per cylinder for the partition table. 0 chooses a default value. Maximum is 255. *partition_sec_hd=*number gives the number of sectors per head for the partition table. 0 chooses a default value. Maximum is 63. The product partition_sec_hd * partition_hd_cyl * 512 is the cylinder size. It should be divisible by 2048 in order to make exact alignment possible. With appended partitions and "appended_part_as=gpt" there is no limit for the number of cylinders. Else there may be at most 1024 of them. If the cylinder size is too small to stay below the limit, then appropriate values of partition_hd_cyl are chosen with partition_sec_hd 32 or 63. If the image is larger than 8,422,686,720 bytes, then the cylinder size constraints cannot be fulfilled for MBR. *partition_cyl_align=*mode controls image size alignment to an integer number of cylinders. It is prescribed by isohybrid specs and it seems to please program fdisk. Cylinder size must be divisible by 2048. Images larger than 8,323,596,288 bytes cannot be aligned in MBR partition table. Mode "auto" is default. Alignment by padding happens only with "isolinux" "partition_table=on". Mode "on" causes alignment by padding with "partition_table=on" for any type. Mode "all" is like "on" but also pads up partitions from -append_partition to an aligned size. Mode "off" disables alignment for any type. *mbr_force_bootable=*mode enforces an MBR partition with "bootable/active" flag if options like partition_table= or grub2_mbr= indicate production of a bootable MBR. These options normally cause the flag to be set if there is an MBR partition of type other than 0xee or 0xef. If no such partition exists, then no bootflag is set, unless mbr_force_bootable="on" forces creation of a dummy partition of type 0x00 which covers only the first block of the ISO image. If no bootable MBR is indicated and a partition gets created by -append_partition, then mbr_force_bootable="on" causes a bootflag like it would do with a bootable MBR. *gpt_iso_bootable=*on causes bit 2 of the GPT partition flags to be set for the ISO 9660 partition if such a GPT partition emerges. This bit is specified as "Legacy BIOS bootable" but its true significance is unclear. Some GPT-aware BIOS might want to see it in some partition. Mode "off" revokes this setting. *gpt_iso_not_ro=*on causes bit 60 of the GPT partition flags to be not set for the ISO 9660 partition if such a GPT partition emerges. This bit is specified as "Read-only" and thus appropriate. But it is unusual in GPT disk partitions. Mode "off" revokes this setting and causes the read-only bit to be set. *mips_path=*iso_rr_path declares a data file in the image to be a MIPS Big Endian boot file and causes production of a MIPS Big Endian Volume Header. This is mutually exclusive with production of other boot blocks like MBR. It will overwrite the first 512 bytes of any data provided by system_area=. Up to 15 boot files can be declared by mips_path=. *mipsel_path=*iso_rr_path declares a data file in the image to be the MIPS Little Endian boot file. This is mutually exclusive with other boot blocks. It will overwrite the first 512 bytes of any data provided by system_area=. Only a single boot file can be declared by mipsel_path=. *sparc_label=*text causes the production of a SUN Disk Label with the given text as ASCII label. Partitions 2 to 8 may be occupied by appended images. Partition 1 will always be the ISO image. See command -append_partition. The first 512 bytes of any data provided by system_area= will be overwritten. *grub2_sparc_core=*iso_rr_path causes the content address and size of the given file to be written after the SUN Disk Label. Both numbers are counted in bytes. The address is written as 64 bit big-endian number to byte 0x228. The size is written as 32 bit big-endian number to byte 0x230. *hppa_cmdline=*text sets the PALO command line for HP-PA. Up to 1023 characters are permitted by default. With hppa_hdrversion=4 the limit is 127. Note that the first five hppa_ bootspecs are mandatory, if any of the hppa_ bootspecs is used. Only hppa_hdrversion= is allowed to be missing. *hppa_bootloader=*iso_rr_path designates the given path as HP-PA bootloader file. *hppa_kernel_32=*iso_rr_path designates the given path as HP-PA 32 bit kernel file. *hppa_kernel_64=*iso_rr_path designates the given path as HP-PA 64 bit kernel file. *hppa_ramdisk=*iso_rr_path designates the given path as HP-PA RAM disk file. *hppa_hdrversion=*number chooses between PALO header version 5 (default) and version 4. For the appropriate value see in PALO source code: PALOHDRVERSION. *alpha_boot=*iso_rr_path declares a data file in the image to be the DEC Alpha SRM Secondary Bootstrap Loader and causes production of a boot sector which points to it. This is mutually exclusive with production of other boot blocks like MBR. *mips_discard*, *mipsel_discard*, *sparc_discard*, *hppa_discard*, *alpha_discard* revoke any boot file declarations made for mips/mipsel, sparc, hppa, or alpha, respectively. This removes the ban on production of other boot blocks. *hfsplus_serial=*hexstring sets a string of 16 digits "0" to "9" and letters "a" to "f", which will be used as unique serial number of an emerging HFS+ filesystem. *hfsplus_block_size=*number sets the allocation block size to be used when producing HFS+ filesystems. Permissible are 512, 2048, or 0. The latter lets the program decide. *apm_block_size=*number sets the block size to be used when describing partitions by an Apple Partition Map. Permissible are 512, 2048, or 0. The latter lets the program decide. Note that size 512 is not compatible with production of GPT, and that size 2048 will not be mountable -t hfsplus at least by older Linux kernels. -append_partition partition_number type_code disk_path Cause a prepared filesystem image to be appended to the ISO image and to be described by a partition table entry in a boot block at the start of the emerging ISO image. The partition entry will bear the size of the submitted file rounded up to the next multiple of 2048 bytes or to the next multiple of the cylinder size. Beware of subsequent multi-session runs. The appended partition will get overwritten. Partitions may be appended with partition table types MBR, GPT, and SUN Disk Label. Additionally to MBR and GPT it is possible to have them marked in APM. With *MBR*: partition_number may be 1 to 4. Number 1 will put the whole ISO image into the unclaimed space before partition 1. So together with most 'xorriso' MBR features, number 2 would be the most natural choice. The type_code may be "FAT12", "FAT16", "Linux", or a hexadecimal number between 0x00 and 0xff. Not all those numbers will yield usable results. For a list of MBR partition type codes search the Internet for "Partition Types" or run fdisk command "L". type_code may also be a type GUID as plain hex string like a2a0d0ebe5b9334487c068b6b72699c7 or as structured text like EBD0A0A2-B9E5-4433-87C0-68B6B72699C7. It will be used if the partition is mentioned in GPT. In MBR, C12A7328-F81F-11D2-BA4B-00A0C93EC93B will be mapped to 0xef. Any other GUID will be mapped to 0x83. In APM, 48465300-0000-11AA-AA11-00306543ECAC will be mapped to partition type "Apple_HFS", any other to "Data". If some other command causes the production of GPT, then the appended partitions will be mentioned there too. *GPT* can be forced by -boot_image "any" "appended_part_as=gpt" partition_number may be 1 to 8. But other than with MBR partitions it is not guaranteed that the resulting GPT partition will have this number. More important than the desired partition number will be that the resulting ISO filesystem is covered gaplessly with GPT table and its partitions and that the partitions in the table are sorted by block address. If partition_number is higher than the number of preceding partitions, then the appropriate number of empty partition entries is inserted to achieve the desired partition_number. If the number of preceding partitions is too high, then a NOTE message informs about the inability to achieve partition_number and about the actually assigned number. The type_code may be the same as described with MBR. Given GUIDs are used unchanged. Given MBR partition types get translated. 0xef becomes C12A7328-F81F-11D2-BA4B-00A0C93EC93B, others become EBD0A0A2-B9E5-4433-87C0-68B6B72699C7. *SUN Disk Label* is chosen by -boot_image any sparc_label=. partition_number may be 2 to 8. Number 1 will always be the ISO image. Partition start addresses are aligned to 320 KiB. The type_code does not matter. Submit 0x0. disk_path "." causes the partition to become a copy of the next lower valid one. With MBR, GPT, and SUN alike: The disk_path must provide the necessary data bytes at commit time. Issueing -append_partition with a partition number that was already used in a previous -append_partition command does not cause an error but silently overrides the previous setting. The pseudo type_code "revoke" or an empty disk_path prevent the partition from being appended. The pseudo partition number "all" may be used in this case to revoke all previous -append_partition settings.  File: xorriso.info, Node: Jigdo, Next: Charset, Prev: Bootable, Up: Commands 9.12 Jigdo Template Extraction ============================== From man genisoimage: "Jigdo is a tool to help in the distribution of large files like CD and DVD images; see http://atterer.net/jigdo/ for more details. Debian CDs and DVD ISO images are published on the web in jigdo format to allow end users to download them more efficiently." 'xorriso' can produce a .jigdo and a .template file together with a single-session ISO image. The .jigdo file contains checksums and symbolic file addresses. The .template file contains the compressed ISO image with reference tags instead of the content bytes of the listed files. Input for this process are the normal arguments for a 'xorriso' session on a blank -outdev, and a checksum file which lists those data files which may be listed in the .jigdo file and externally referenced in the .template file. Each designated file is represented in the checksum file by a single text line: Checksum as hex digits, 2 blanks, size as 12 decimal digits or blanks, 2 blanks, symbolic file address The kind of checksum is chosen by -jigdo "checksum_algorithm" with values "md5" (32 hex digits) or "sha256" (64 hex digits). It will also be used for the file address lines in the .jigdo file. The default is "md5". The file address in a checksum file line has to bear the same basename as the disk_path of the file which it shall match. The directory path of the file address is decisive for To=From mapping, not for file recognition. After To=From mapping, the file address gets written into the .jigdo file. Jigdo restore tools will convert these addresses into really reachable data source addresses from which they can read. If the list of jigdo parameters is not empty, then 'xorriso' will refuse to write to non-blank targets, it will disable multi-session emulation, and padding will be counted as part of the ISO image. -jigdo parameter_name value Clear Jigdo Template Extraction parameter list or add a parameter to that list. The alias names are the corresponding genisoimage options. They are accepted as parameter names as well. Especially they are recognized by the -as mkisofs emulation command. Parameter *clear* with any value empties the whole list. No .jigdo and .template file will be produced. *checksum_algorithm* chooses the checksum algorithm which shall be used for the data file entries in the .jigdo file and is expected in the checksum file. Permissible are "md5" or "sha256". Default is "md5". Alias: -jigdo-checksum-algorithm *template_path* sets the disk_path for the .template file with the holed and compressed ISO image copy. Alias: -jigdo-template *jigdo_path* sets the disk_path for the .jigdo file with the checksums and download addresses for filling the holes in .template. Alias: -jigdo-jigdo *checksum_path* sets the disk_path where to find the checksum file with symbolic file addresses and checksums according to *checksum_algorithm*. Alias: md5_path Alias: -checksum-list Alias: -md5-list *min_size* sets the minimum size for a data file to be listed in the .jigdo file and being a hole in the .template file. Alias: -jigdo-min-file-size *exclude* adds a regular expression pattern which will get compared with the absolute disk_path of any data file. A match causes the file to stay in .template in any case. Alias: -jigdo-exclude *demand_checksum* adds a regular expression pattern which will get compared with the absolute disk_path of any data file that was not found in the checksum list file as of "checksum_path". A match causes a MISHAP event. Alias: demand_md5 Alias: -jigdo-force-checksum Alias: -jigdo-force-md5 *mapping* adds a string pair of the form To=From to the parameter list. If a data file gets listed in the .jigdo file, then it is referred by the file address from its line in the checksum file. This file address gets checked whether it begins with the From string. If so, then this string will be replaced by the To string and a ':' character, before it goes into the .jigdo file. The From string should end by a '/' character. Alias: -jigdo-map *compression* chooses one of "bzip2" or "gzip" for the compression of the template file. The jigdo file is put out uncompressed. Alias: -jigdo-template-compress *checksum_iso* chooses one or more of "md5", "sha1", "sha256", "sha512" for the auxiliary "# Image Hex" checksums in the jigdo file. The value may e.g. look like "md5,sha1,sha512". Value "all" chooses all available algorithms. Note that MD5 stays always enabled. Alias: -checksum_algorithm_iso *checksum_template* is like checksum_iso but for "# Template Hex". Alias: -checksum_algorithm_template  File: xorriso.info, Node: Charset, Next: Exception, Prev: Jigdo, Up: Commands 9.13 Character sets =================== File names are strings of non-zero bytes with 8 bit each. Unfortunately the same byte string may appear as different peculiar national characters on differently nationalized terminals. The meanings of byte codes are defined in *character sets* which have names. Shell command iconv -l lists them. The file names on hard disk are assumed to be encoded by the *local character set* which is also used for the communication with the user. Byte codes 32 to 126 of the local character set must match the US-ASCII characters of the same code. ISO-8859 and UTF-8 fulfill this demand. By default, 'xorriso' uses the character set as told by shell command "locale" with argument "charmap". This may be influenced by environment variables LC_ALL, LC_CTYPE, or LANG and should match the expectations of the terminal. In some situations it may be necessary to set it by command -local_charset. Local character sets should not matter as long as only english alphanumeric characters are used for file names or as long as all writers and readers of the media use the same local character set. Outside these constraints it may be necessary to let 'xorriso' convert byte codes from and to other character sets. The Rock Ridge file names in ISO filesystems are assumed to be encoded by the *input character set*. The Rock Ridge file names which get written with ISO filesystems will be encoded by the *output character set*. The sets can be defined independently by commands -in_charset and -out_charset. Normally one will have both identical, if ever. Other than the local character set, these two character sets may deviate from US-ASCII. The output character sets for Joliet and HFS+ are not influenced by these commands. Joliet uses output character set UCS-2 or UTF-16. HFS+ uses UTF-16. The default output charset is the local character set of the terminal where 'xorriso' runs. So by default no conversion happens between local filesystem names and emerging Rock Ridge names in the image. The situation stays ambiguous and the reader has to riddle what character set was used. By command -auto_charset it is possible to attribute the output charset name to the image. This makes the situation unambiguous. But if your terminal character set does not match the character set of the local file names, then this attribute can become plainly wrong and cause problems at read time. To prevent this it is necessary to check whether the terminal properly displays all intended filenames. Check especially the exotic national characters. To enforce recording of a particular character set name without any conversion at image generation time, set -charset and -local_charset to the desired name, and enable -backslash_codes to avoid evil character display on your terminal. -charset character_set_name Set the character set from which to convert file names when loading an image and to which to convert when writing an image. -local_charset character_set_name Override the system assumption of the local character set name. If this appears necessary, one should consider to set -backslash_codes to "on" in order to avoid dangerous binary codes being sent to the terminal.  File: xorriso.info, Node: Exception, Next: DialogCtl, Prev: Charset, Up: Commands 9.14 Exception processing ========================= Since the tasks of 'xorriso' are manifold and prone to external influence, there may arise the need for 'xorriso' to report and handle problem events. Those events get classified when they are detected by one of the software modules and forwarded to reporting and evaluation modules which decide about reactions. Event classes are sorted by severity: "NEVER" The upper end of the severity spectrum. "ABORT" The program is being aborted and on its way to end. "FATAL" The main purpose of the run failed or an important resource failed unexpectedly. "FAILURE" An important part of the job could not be performed. "MISHAP" A FAILURE which can be tolerated during ISO image generation. "SORRY" A less important part of the job could not be performed. "WARNING" A situation is suspicious of being not intended by the user. "HINT" A proposal to the user how to achieve better results. "NOTE" A harmless information about noteworthy circumstances. "UPDATE" A pacifier message during long running operations. "DEBUG" A message which would only interest the program developers. "ERRFILE" A filename for the -errfile_log if it is enabled. "ALL" The lower end of the severity spectrum. -abort_on severity Set the severity threshold for events to abort the program. Useful: "NEVER", "ABORT", "FATAL", "FAILURE" , "MISHAP", "SORRY" It may become necessary to abort the program anyway, despite the setting by this command. Expect not many "ABORT" events to be ignorable. A special property of this command is that it works preemptive if given as program start argument. I.e. the first -abort_on setting among the start arguments is in effect already when the first operations of 'xorriso' begin. Only "-abort_on" with dash "-" is recognized that way. -return_with severity exit_value Set the threshold and exit_value to be returned at program end if no abort has happened. This is to allow 'xorriso' to go on after problems but to get a failure indicating exit value from the program, nevertheless. Useful is a value lower than the -abort_on threshold, down to "WARNING". exit_value may be either 0 (indicating success to the starter of the program) or a number between 32 and 63. Some other exit_values are used by 'xorriso' if it decides to abort the program run: 1=abort due to external signal 2=no program arguments given 3=creation of 'xorriso' main object failed 4=failure to start libburnia-project.org libraries 5=program abort during argument processing 6=program abort during dialog processing -report_about severity Set the threshold for events to be reported. Useful: "SORRY", "WARNING", "HINT", "NOTE", "UPDATE", "DEBUG", "ALL" Regardless what is set by -report_about, messages get always reported if they reach the severity threshold of -abort_on . Event messages are sent to the info channel "I" which is usually stderr but may be influenced by command -pkt_output. Info messages which belong to no event get attributed severity "NOTE". A special property of this command is that the first -report_about setting among the start arguments is in effect already when the first operations of 'xorriso' begin. Only "-report_about" with dash "-" is recognized that way. -signal_handling mode Control the installation of a signal handler which shall react on external signals (e.g. from program "kill" or from keys Ctrl+C) or on signals caused by severe program errors. Mode "on" is the default. It uses the signal handler of libburn which produces ugly messages but puts much effort in releasing optical drives before 'xorriso' ends. Mode "off" as first -signal_handling among the start arguments prevents all own signal precautions of 'xorriso'. Inherited signal handler settings stay as they are. It works like "sig_dfl" if given after other signal handling was already established at program start. Mode "sig_dfl" uses the system provided default handling of signals, which is normally a sudden abort of the program. To prevent stuck drives, the libburn handler is used during burning, blanking, and formatting on MMC drives. Mode "sig_ign" tries to ignore as many signal types as possible. This imposes the risk that 'xorriso' refuses to end until externally kill -9 if performed. kill -9 then imposes the risk that the drive is left in unusable state and needs poweroff to be reset. So during burning, blanking, and formatting wait for at least their normal run time before killing externally. A special property of this command is that the first -signal_handling setting among the start arguments is in effect already when the first operations of 'xorriso' begin. Only "-signal_handling" with dash "-" is recognized that way. -error_behavior occasion behavior Control the program behavior at problem event occasions. For now this applies to occasions "image_loading" which is given while an image tree is read from the input device, and to "file_extraction" which is given with osirrox commands like -extract. With "image_loading" there are three behaviors available: "best_effort" goes on with reading after events with severity below FAILURE if the threshold of command -abort_on allows this. "failure" aborts image tree reading on first event of at least SORRY. It issues an own FAILURE event. This is the default. "fatal" acts like "failure" but issues the own event as FATAL. With occasion "file_extraction" there are three behaviors: "keep" maintains incompletely extracted files on disk. This is the default. "delete" removes files which encountered errors during content extraction. "best_effort" starts a revovery attempt by means of -extract_cut if the file content stems from the loaded ISO image and is not filtered.  File: xorriso.info, Node: DialogCtl, Next: Inquiry, Prev: Exception, Up: Commands 9.15 Dialog mode control ======================== -dialog "on"|"off"|"single_line" Enable or disable to enter dialog mode after all program arguments are processed. In dialog mode input lines get prompted via readline or from stdin. If no -abort_on severity was set when dialog starts, then "NEVER" is set to avoid abort in most cases of wrong input or other problems. Before dialog begins, the default is "FAILURE" which e.g. aborts on unknown commands. Mode "on" supports input of newline characters within quotation marks and line continuation by trailing backslash outside quotation marks. Mode "single_line" does not. -page length width Describe terminal to the text pager. See also above, paragraph Result pager. If parameter length is nonzero then the user gets prompted after that number of terminal lines. Zero length disables paging. Parameter width is the number of characters per terminal line. It is used to compute the number of terminal lines which get occupied by an output line. A usual terminal width is 80. -use_readline "on"|"off" If "on" then use readline for dialog. Else use plain stdin. See also above, paragraph Dialog, Readline, Result pager. -reassure "on"|"tree"|"off" If "on" then ask the user for "y" or "n": before deleting or overwriting any file in the ISO image, before overwriting any disk file during restore operations, before rolling back pending image changes, before committing image changes to media, before changing the input drive, before blanking or formatting media, before ending the program. With setting "tree" the reassuring prompt will appear for an eventual directory only once and not for each file in its whole subtree. Setting "off" silently kills any kind of image file object and performs above irrevocable actions. To really produce user prompts, command -dialog needs to be set to "on". Note that the prompt does not appear in situations where file removal is forbidden by command -overwrite. -reassure only imposes an additional curb for removing existing file objects. Be aware that file objects get deleted from the ISO image immediately after confirmation. They are gone even if the running command gets aborted and its desired effect gets revoked. In case of severe mess-up, consider to use -rollback to revoke the whole session.  File: xorriso.info, Node: Inquiry, Next: Navigate, Prev: DialogCtl, Up: Commands 9.16 Drive and media related inquiry actions ============================================ -devices Show list of available MMC drives with the addresses of their libburn standard device files. This is only possible when no ISO image changes are pending. After this command was executed, there is no drive current and no image loaded. In order to be visible, a device has to offer rw-permissions with its libburn standard device file. Thus it might be only the *superuser* who is able to see all drives. Drives which are occupied by other processes get not shown. -device_links Like -devices, but presenting the drives with addresses of symbolic links which point to the actual device files. Modern GNU/Linux systems may shuffle drive addresses from boot to boot. The udev daemon is supposed to create links which always point to the same drive, regardless of its system address. The command -device_links shows the addresses of such links if they begin by "/dev/dvd" or "/dev/cd". Precedence is: "dvdrw", "cdrw", "dvd", "cdrom", "cd". -toc Show media specific tables of content. This is the session history of the medium, not the ISO image directory tree. In case of overwritable media holding a valid ISO image, it may happen that only a single session gets shown. But if the first session on the overwritable media was written by 'xorriso' then a complete session history can be emulated. A drive which is incapable of writing may show any media as CD-ROM or DVD-ROM with only one or two sessions on it. The last of these sessions is supposed to be the most recent real session then. Some read-only drives and media show no usable session history at all. Command -rom_toc_scan might help. If input device and output device are both acquired and not the same, then both tables-of-content get shown. -toc_of "in"|"out"|"all"[":short"] Like command -toc but explicitly choosing which drive's table-of-content to show. "in" shows -indev or -dev, "out" shows -outdev or -dev, "all" shows the same as -toc. If ":short" is appended to the drive choosing word, then only a short summary of drive state and medium content is printed. As further difference to -toc, this command does not emit FAILURE events if the desired drive is not acquired. -toc_info_type typetext Choose which information to show in the rightmost column of -toc and -toc_of. Type "volid" is the default. It shows the Volume Ids of the listed ISO sessions. Type "creation_time" or "ctime" chooses the Creation Times. Type "modification_time" or "mtime" chooses the Modification Times. Appending "_gmt" to a time type text causes the time information to be shown in ECMA-119 format YYYYMMDDhhmmsscc in timezone GMT. Else it is shown as timestamps YYYY.MM.DD.hhmmss in the local timezone of the system. -assess_indev_features "plain"|"cmd"|"as_mkisofs"|"replay" Inspect the filesystem on -indev for the presence of Rock Ridge, Joliet, or ISO 9660:1999, and for traces of other write options which seem to have been used when the filesystem was created. Note that this command does not detect and report a possibly present HFS+ tree. Mode "cmd" lists xorriso commands which would activate the detected settings. Mode "as_mkisofs" lists options of the -as mkisofs emulation, which would activate those of the detected settings which are not default. Mode "replay" performs the commands which get listed by mode "cmd". Mode "plain" lists after a "Indev feature: " header name-value pairs as delivered by libisofs function iso_read_image_feature_named(). See libisofs.h. The other modes derive their output from this list. I.e. the sequence of commands from "cmd" follows the sequence of "plain". Not leading to "cmd" lines are: "size=" tells the number of 2048 byte blocks of the filesystem. "eltorito=1" tells that El Torito boot equipment was detected. "tree_loaded=" tells which tree was loaded by -indev: 0 = ISO 9660 , 1 = Joliet , 2 = ISO 9660:1999 "tree_loaded_text=" tells the same by name: "ISO9660", "Joliet", "ISO9660:1999" "rr_loaded=1" tells that Rock Ridge information was loaded with the tree. "aaip=1" tells that AAIP information was detected (ACL, xattr, MD5, ...). "relaxed_vol_atts=1" tells that the volume attributes like -volid or -preparer_id bear characters outside the restricted character sets which are specified for them by ECMA-119. "rrip_1_10_px_ino=1" tells that with Rock Ridge 1.10 a PX entry was found which looks like from Rock Ridge 1.12. -mount_cmd drive entity id path Emit an appropriate command line for mounting the ISO session indicated by drive, entity and id. The result will be different on GNU/Linux and on FreeBSD or NetBSD. drive can be "indev" or "outdev" to indicate already acquired drives, or it can be the path of a not yet acquired drive. Prefix "stdio:" for non-MMC drives is not mandatory. See command '-load' for the meaning of entity and id. Entities are: "auto", "session", "track", "lba", "sbsector", "volid", "at_time", "before", "not_after", "after", and "not_before". Each is to be used with its appropriate kind of id string: "auto", session number, track number, block number, search expression for volume id, or time string. path will be used as mount point and must already exist as a directory on disk. The command gets printed to the result channel. See command -mount for direct execution of this command. -mount_opts option[:option...] Set options which influence -mount and -mount_cmd. Currently there is only option "exclusive" which is default and its counterpart "shared". The latter causes 'xorriso' not to give up the affected drive with command -mount. On GNU/Linux it adds mount option "loop" which may enable mounting of several sessions of the same block device at the same time. One should not write to a mounted optical medium, of course. Take care to umount all sessions before ejecting. -session_string drive entity id format Print to the result channel a text which gets composed according to format and the parameters of the addressed session. Formats "linux:"path or "freebsd:"path produce the output of -mount_cmd for the given operating systems. In other texts 'xorriso' will substitute the following parameter names. An optional prefix "string:" will be removed. "%device%" will be substituted by the mountable device path of the drive address. "%sbsector%" will be substituted by the session start sector. "%track%", "%session%", "%volid%" will be substituted by track number, session number, or volume id of the depicted session. -print_size Print the foreseeable consumption of 2048 byte blocks by next -commit. This can last a while as a -commit gets prepared and only in last moment is revoked by this command. The result depends on several settings and also on the kind of output device. If no -jigdo options are set and not command -as "mkisofs" was used, then -padding (300 kB by default) is not counted as part of the image size. If an El Torito boot image file is already depicted, then command -print_size automatically executes -boot_image "any" "next". This means that the properties of that boot image cannot be edited by subsequent commands. -tell_media_space Print available space on the output medium and the free space after subtracting already foreseeable consumption by next -commit. Note that the title of the prediction "After commit :" is misleading. It is rather the space that may still be filled in this session without making the next -commit fail from medium overflow. The free space after the next -commit might be smaller by several MB. This depends on medium type, number of recorded sessions, and drive habits. -pvd_info Print various ID strings and timestamps which can be found in loaded ISO images. Some of the IDs may be changed by commands like -volid or -publisher. For these IDs -pvd_info reports what would be written with the next -commit. The timestamps get shown in ECMA-119 format YYYYMMDDhhmmsscc and timezone GMT. They do not get automatically propagated from loaded image to newly written image. The ones for new images may be set by command -volume_date. See there for the meaning of the particular timestamps. -report_el_torito mode With mode *plain* print a report about the information found in the El Torito boot catalog of the loaded ISO image. With mode *help* print a text which explains the meaning of the lines put out by "plain". Mode *cmd* tries to print the *xorriso* commands which are necessary to produce the found boot equipment: disk identifiers, El Torito boot images, and System Area. Disk identifiers are strings which the booting operating system might use to find the ISO filesystem from where it comes. Currently known is the use of volume id and modification date. The intended use case is modification of the filesystem by having -indev and -outdev pointing to different images or drives. The result might be insufficient, if the found equipment cannot be produced by xorriso. Various SORRY events may arise in this case, but it is not guaranteed that xorriso recognizes all its insufficiencies. Mode *as_mkisofs* tries to print the *xorriso -as mkisofs* options, which are necessary to produce the found equipment. The intended use case is to use the mounted filesystem as input tree together with the printed options. If CHRP equipment is detected, then modes *cmd* and *as_mkisofs* issue some of the relaxation commands or options which get detected by command *-assess_indev_features*. This happens because CHRP firmware reads file paths from file /ppc/bootinfo.txt and tries to find them case-insensitively in the ECMA-119 tree without using Rock Ridge. If such a path has actually forbidden properties, like the name "powerpc-ieee1275", then the relaxations are needed to bring it unmangled into the ECMA-119 tree. It is important to keep in mind that the file paths shown in the report lines and commands were registered directly after image loading. Possible filesystem manipulations which later remove these paths or replace their file content will not influence the report lines or commands. -report_system_area mode With mode *plain* print a report about the information found in the System Area of the loaded ISO image. The report consists of zero to many lines with a header text, a colon, and information text. With mode *help* print a text which explains the meaning of the lines put out by "plain". You probably will have to look for more documentation which explains the technical details of the mentioned boot facilities. Modes *cmd* and *as_mkisofs* work like with command -report_el_torito. See above. It is important to keep in mind that the file paths shown in the report lines and commands were registered directly after image loading. Possible filesystem manipulations which later remove these paths or replace their file content will not influence the report lines or commands. With mode *gpt_disk_guid* print the GPT disk GUID of the loaded ISO in RFC 4122 text format to result channel. It is not considered an error if no GPT is present. In this case nothing is printed to result channel. With mode *gpt_crc_of:*disk_path read up to 32 KiB from the disk file with the path given after the colon. Compute the GPT compliant CRC number and print it to the result channel. The number is shown like "0x690fd979". The special disk_path "-" causes reading from standard input. With mode *make_guid* print a pseudo-random GUID in RFC 4122 text format to result channel.  File: xorriso.info, Node: Navigate, Next: Verify, Prev: Inquiry, Up: Commands 9.17 Navigation in ISO image and disk filesystem ================================================ -cd iso_rr_path Change the current working directory in the ISO image. This is prepended to iso_rr_paths which do not begin with '/'. It is possible to set the working directory to a path which does not exist yet in the ISO image. The necessary parent directories will be created when the first file object is inserted into that virtual directory. Use -mkdir if you want to enforce the existence of the directory already at first insertion. -cdx disk_path Change the current working directory in the local filesystem. To be prepended to disk_paths which do not begin with '/'. -pwd Tell the current working directory in the ISO image. -pwdx Tell the current working directory in the local filesystem. -ls iso_rr_pattern [***] List files in the ISO image which match shell patterns (i.e. with wildcards '*' '?' '[a-z]'). If a pattern does not begin with '/' then it is compared with addresses relative to -cd. Directories are listed by their content rather than as single file item. Pattern expansion may be disabled by command -iso_rr_pattern. -lsd iso_rr_pattern [***] Like -ls but listing directories as themselves and not by their content. This resembles shell command ls -d. -lsl iso_rr_pattern [***] Like -ls but also list some of the file attributes. The output format resembles shell command ls -ln. File type 'e' indicates the El Torito boot catalog. If the file has non-trivial ACL, then a '+' is appended to the permission info. If the file is hidden, then 'I' for "iso_rr", 'J' for "joliet", 'A' for "hfsplus", 'H' for multiple hiding gets appended. Together with ACL it is 'i', 'j', 'a', 'h'. -lsdl iso_rr_pattern [***] Like -lsd but also list some of the file attributes. The output format resembles shell command ls -dln. -lsx disk_pattern [***] List files in the local filesystem which match shell patterns. Patterns which do not begin with '/' are used relative to -cdx. Directories are listed by their content rather than as single file item. Pattern expansion may be disabled by command -disk_pattern. -lsdx disk_pattern [***] Like -lsx but listing directories as themselves and not by their content. This resembles shell command ls -d. -lslx disk_pattern [***] Like -lsx but also listing some of the file attributes. Output format resembles shell command ls -ln. -lsdlx disk_pattern [***] Like -lsdx but also listing some of the file attributes. Output format resembles shell command ls -dln. -getfacl iso_rr_pattern [***] Print the access permissions of the given files in the ISO image using the format of shell command getfacl. If a file has no ACL then it gets fabricated from the -chmod settings. A file may have a real ACL if it was introduced into the ISO image while command -acl was set to "on". -getfacl_r iso_rr_pattern [***] Like -gefacl but listing recursively the whole file trees underneath eventual directories. -getfattr iso_rr_pattern [***] Print the xattr of the given files in the ISO image. If a file has no such xattr then noting is printed for it. The choice of namespaces depends on the setting of command -xattr: "on" or "user" restricts it to namespace "user", "any" only omits namespace "isofs". -getfattr_r iso_rr_pattern [***] Like -gefattr but listing recursively the whole file trees underneath of directories. -lsattr iso_rr_pattern [***] Print the Linux file attributes of the given files like program lsattr(1) would do with disk files. The meaning of the shown flag letters are described in man 1 chattr with the exception of '-', which is shown as placeholder for an unset flag. The given files will get a line printed even if they have no Linux file attributes attached. In this case all flags will be shown as '-'. -du iso_rr_pattern [***] Recursively list size of directories and files in the ISO image which match one of the patterns. similar to shell command du -k. -dus iso_rr_pattern [***] List size of directories and files in the ISO image which match one of the patterns. Similar to shell command du -sk. -dux disk_pattern [***] Recursively list size of directories and files in the local filesystem which match one of the patterns. Similar to shell command du -k. -dusx disk_pattern [***] List size of directories and files in the local filesystem which match one of the patterns. Similar to shell command du -sk. -findx disk_path [test [op] [test ...]] [-exec action [params]] -- Like -find but operating on local filesystem and not on the ISO image. The -findx command is subject to the settings of -follow. -findx accepts the same tests as -find, but only the following ones work like described with -find: -bad_outname, -decision, -disk_name, -disk_path, -has_acl, -has_any_xattr, -has_lfa_flags, -has_some_lfa_flags_of, -has_xattr, -lba_range, -maxdepth, -mindepth, -name, -or_use_pattern, -prune, -size, -true, -type, -use_pattern, -wholename The others get defaulted to -false, because they are not applicable to disk files. Test -type accepts the same parameters as with -find. Additionally it recognizes type "mountpoint" (or "m") which matches subdirectories which reside on a different device than their parent. This type never matches the disk_path given as start address for -findx. Test -lba_range matches only if its parameter start_lba is 0. Tests -has_lfa_flags and -has_some_lfa_flags_of ignore non-settable file attribute flags if -lfa_flags is set to on:import_only_settable. -findx accepts the -exec actions as does -find. But except the following few actions it will always perform action "echo". in_iso reports the path if its counterpart exists in the ISO image. For this the disk_path of the -findx command gets replaced by the iso_rr_path given as parameter. E.g.: -findx /home/thomas -exec in_iso /thomas_on_cd -- not_in_iso reports the path if its counterpart does not exist in the ISO image. The report format is the same as with command -compare. add_missing iso_rr_path_start adds the counterpart if it does not yet exist in the ISO image and marks it for "rm_merge" as non-removable. E.g.: -findx /home/thomas -exec add_missing /thomas_on_cd -- is_full_in_iso reports if the counterpart in the ISO image contains files. To be used with -type "m" to report mount points. empty_iso_dir deletes all files from the counterpart in the ISO image. To be used with -type "m" to truncate mount points. print_outname prints in the first line the filename as found on disk, and in the second line the filename after conversion forth and back between local character set and one of the namespaces "rockridge", "joliet", "ecma119", or "hfsplus". The third output line is "-" . The name conversion does not take into respect the possibility of name collisions in the target namespace. Such collisions are most likely in "joliet" and "ecma119", where they get resolved by automatic file name changes. estimate_size prints a lower and an upper estimation of the number of blocks which the found files together will occupy in the emerging ISO image. This does not account for the superblock, for the directories in the -findx path, or for image padding. getfacl prints access permissions in ACL text form to the result channel. getfattr prints xattr name-value pairs to the result channel. The choice of namespaces depends on the setting of command -xattr: "off", "on", or "user" restricts it to the namespace "user", "any" causes all namespaces to be shown. get_any_xattr prints xattr name-value pairs to the result channel. All namespaces are shown regardless of the setting of command -xattr. list_extattr mode prints a script to the result channel, which would use FreeBSD command setextattr to set the file's xattr name-value pairs of user namespace. See -find for a description of parameter mode. E.g. -exec list_extattr e - lsattrd prints the Linux file attribute flags like command -lsattrd does. This shows non-settable flags, too, even if they are to be ignored by the setting of command -lfa_flags. -compare disk_path iso_rr_path Compare attributes and eventual data file content of a fileobject in the local filesystem with a file object in the ISO image. The iso_rr_path may well point to an image file object which is not yet committed, i.e. of which the data content still resides in the local filesystem. Such data content is prone to externally caused changes. If iso_rr_path is empty then disk_path is used as path in the ISO image too. Differing attributes are reported in detail, differing content is summarized. Both to the result channel. In case of no differences no result lines are emitted. -compare_r disk_path iso_rr_path Like -compare but working recursively. I.e. all file objects below both addresses get compared whether they have counterparts below the other address and whether both counterparts match. -compare_l disk_prefix iso_rr_prefix disk_path [***] Perform -compare_r with each of the disk_path parameters. iso_rr_path will be composed from disk_path by replacing disk_prefix by iso_rr_prefix. -show_stream iso_rr_path [***] Display the content stream chain of data files in the ISO image. The chain consists of the iso_rr_name and one or more streams, separated by " < " marks. A stream description consists of one or more texts, separated by ":" characters. The first text tells the stream type, the following ones, if ever, describe its individual properties. Frequently used types are: disk:'disk_path' for local filesystem objects. image:'iso_rr_path' for ISO image file objects. cout:'disk_path offset count' for -cut_out files. extf:'filter_name' for external filters. -zisofs:algorithm:block_size for zisofs compression filters. -zisofs-decode:algorithm:block_size for zisofs uncompression filters. -gzip for internal gzip compression filters. -gunzip for internal gzip uncompression filters. Example: '/abc/xyz.gz' < extf:'gzip' < disk:'/home/me/x' -show_stream_r iso_rr_path [***] Like -show_stream but working recursively.  File: xorriso.info, Node: Verify, Next: Restore, Prev: Navigate, Up: Commands 9.18 Evaluation of readability and recovery =========================================== It is not uncommon that optical media produce read errors. The reasons may be various and get obscured by error correction which is performed by the drives and based on extra data on the media. If a drive returns data then one can quite trust that they are valid. But at some degree of read problems the correction will fail and the drive is supposed to indicate error. 'xorriso' can scan a medium for readable data blocks, classify them according to their read speed, save them to a file, and keep track of successfully saved blocks for further tries on the same medium. By command -md5 checksums may get recorded with data files and whole sessions. These checksums are reachable only via indev and a loaded image. They work independently of the media type and can detect transmission errors. -check_media [option [option ...]] -- Try to read data blocks from the indev drive, optionally copy them to a disk file, and finally report about the encountered quality. Several options may be used to modify the default behavior. The parameters given with this command override the default settings which may have been changed by command -check_media_defaults. See there for a description of available options. The result list tells intervals of 2 KiB blocks with start address, number of blocks and quality. Qualities which begin with "+" are supposed to be valid readable data. Qualities with "-" are unreadable or corrupted data. "0" indicates qualities which are not covered by the check run or are regularly allowed to be unreadable (e.g. gaps between tracks). Alternatively it is possible to report damaged files rather than blocks. If -md5 is "on" then the default mode what=tracks looks out for libisofs checksum tags for the ISO session data and checks them against the checksums computed from the data stream. -check_media_defaults [option [option ...]] -- Preset options for runs of -check_media, -extract_cut and best_effort file extraction. Options given with -check_media will override the preset options. -extract_cut will override some options automatically. An option consists of a keyword, a "=" character, and a value. Options may override each other. So their sequence matters. The default setting at program start is: use=indev what=tracks min_lba=-1 max_lba=-1 retry=default time_limit=28800 item_limit=100000 data_to=" event=ALL abort_file=/var/opt/xorriso/do_abort_check_media sector_map=" map_with_volid=off patch_lba0=off report=blocks bad_limit=invalid slow_limit=1.0 chunk_size=0s async_chunks=0 Option "reset=now" restores these startup defaults. Non-default options are: report="files" lists the files which use damaged blocks (not with use=outdev). The format is like with find -exec report_damage. Note that a MD5 session mismatch marks all files of the session as damaged. If finer distinction is desired, perform -md5 off before -check_media. report="blocks_files" first lists damaged blocks and then affected files. use="outdev" reads from the output drive instead of the input drive. This avoids loading the ISO image tree from media. use="sector_map" does not read any media but loads the file given by option sector_map= and processes this virtual outcome. what="disc" scans the payload range of a medium without respecting track gaps. what="image" similar to "disc", but restricts scanning to the range of the ISO 9660 image, if present. min_lba=limit omits all blocks with addresses lower than limit. max_lba=limit switches to what=disc and omits all blocks above limit. chunk_size=size sets the number of bytes to be read in one low-level read operation. This gets rounded down to full blocks of 2048 bytes. 0 means automatic size. retry="on" forces read retries with minimal senseful chunk size when the normal read chunk produces a read error. This size is 1s with CD and stdio files, 16s with DVD (1 ECC Block), and 32s with BD (1 Cluster). By default, retries are only enabled with CD media. "retry=off" forbits retries for all media types. abort_file=disk_path gives the path of the file which may abort a scan run. Abort happens if the file exists and its mtime is not older than the start time of the run. Use shell command "touch" to trigger this. Other than an aborted program run, this will report the tested and untested blocks and go on with running 'xorriso'. time_limit=seconds gives the number of seconds after which the scan shall be aborted. This is useful for unattended scanning of media which may else overwork the drive in its effort to squeeze out some readable blocks. Abort may be delayed by the drive gnawing on the last single read operation. Value -1 means unlimited time. item_limit=number gives the number of report list items after which to abort. Value -1 means unlimited item number. data_to=disk_path copies the valid blocks to the given file, which must support random access writing, unless disk_path is "-" which means standard output. In the latter case, patch_lba0= settings other than "off" yield failure. Further the usual result messages of -check_media get redirected to the info channel. But beware of result messages from other commands. Beware of -*dev "-" which redirect standard output to standard error. Keep the run simple: xorriso -indev /dev/sr0 -check_media data_to=- - | md5sum xorriso -outdev /dev/sr0 -check_media data_to=- use=outdev \ what=disc min_lba=0 max_lba=999999 - | sha256sum event=severity sets the given severity for a problem event which shall be issued at the end of a check run if data blocks were unreadable or failed to match recorded MD5 checksums. Severity "ALL" disables this event. sector_map=disk_path tries to read the file given by disk_path as sector bitmap and to store such a map file after the scan run. The bitmap tells which blocks have been read successfully in previous runs. It is the persistent memory for several scans on the same medium, even with intermediate eject, in order to collect readable blocks whenever the drive is lucky enough to produce them. The stored file contains a human readable TOC of tracks and their start block addresses, followed by binary bitmap data. By default, untested blocks are not considered bad, but rather as intentionally unread. If you expect time_limit= or item_limit= to abort the run, then consider to use bad_limit="untested". map_with_volid="on" examines tracks whether they are ISO images and prints their volume IDs into the human readable TOC of sector_map=. patch_lba0="on" transfers within the data_to= file a copy of the currently loaded session head to the start of that file and patches it to be valid at that position. This makes the loaded session the last valid session of the image file when it gets mounted or loaded as stdio: drive. New sessions will be appended after this last session and will overwrite any sessions which have followed it. patch_lba0="force" performs patch_lba0="on" even if 'xorriso' believes that the copied data are not valid. patch_lba0= may also bear a number. If it is 32 or higher it is taken as start address of the session to be copied. In this case it is not necessary to have an -indev and a loaded image. ":force" may be appended after the number. bad_limit=threshold sets the highest quality which shall be considered as damage. Choose one of "good", "md5_match", "slow", "partial", "valid", "untested", "md5_mismatch", "invalid", "tao_end", "off_track", "unreadable". "valid" and "invalid" are qualities imported from a sector_map file. "tao_end" and "off_track" are intentionally not readable, but not bad either. "partial" are blocks retrieved from a partially readable chunk. They are supposed to be ok but stem from a suspicious neighborhood. "md5_match" and "md5_mismatch" regions overlap with regions of other quality. The former is a strong confirmation for quality, the latter only tells that one or more blocks of the region must be wrong. By default bad_limit is set higher than md5_mismatch, so that mismatches are classified as quality class "0" rather than "-". This means that the sectors of a MD5 mismatch range are recorded in the sector_map as successfully read, if the drive handed them out at all. Set "bad_limit=md5_mismatch" to let the sector_map record the whole mismatching range as yet not retrieved. slow_limit=threshold sets the time threshold for a single read chunk to be considered slow. This may be a fractional number like 0.1 or 1.5. async_chunks=number enables asynchronous MD5 processing if number is 2 or larger. In this case the given number of read chunks is allocated as fifo buffer. On very fast MMC drives try: chunk_size=64s async_chunks=16. -check_md5 severity iso_rr_path [***] Compare the data content of the given files in the loaded image with their recorded MD5 checksums, if there are any. In case of any mismatch an event of the given severity is issued. It may then be handled by appropriate settings of commands -abort_on or -return_with which both can cause non-zero exit values of the program run. Severity ALL suppresses that event. This command reports match and mismatch of data files to the result channel. Non-data files cause NOTE events. There will also be UPDATE events from data reading. If no iso_rr_path is given then the whole loaded session is compared with its MD5 sum. Be aware that this covers only one session and not the whole image if there are older sessions. -check_md5_r severity iso_rr_path [***] Like -check_md5 but checking all data files underneath the given paths. Only mismatching data files will be reported.  File: xorriso.info, Node: Restore, Next: Emulation, Prev: Verify, Up: Commands 9.19 osirrox ISO-to-disk restore commands ========================================= Normally 'xorriso' only writes to disk files which were given as stdio: pseudo-drives or as log files. But its alter ego osirrox is able to extract file objects from ISO images and to create, overwrite, or delete file objects on disk. Disk file exclusions by -not_mgt, -not_leaf, -not_paths apply. The exclusion tests are made with the paths and names for the disk files. If exclusion of paths or names in the ISO image is desired, then use image manipulation commands like -rm or -find ... -exec rm before extraction, and end the program by -rollback_end . Excluded disk_path parameters of extraction commands cause SORRY events. Implicitly given paths in trees under disk_path parameters are excluded silently. If disk file objects already exist then the settings of -overwrite and -reassure apply. But -overwrite "on" only triggers the behavior of -overwrite "nondir". I.e. directories cannot be deleted. Access permissions of files in the ISO image do not restrict restoring. The directory permissions on disk have to allow rwx. -osirrox setting[:option:...] Setting *off* disables disk filesystem manipulations. This is the default unless the program was started with leafname *osirrox*. Elsewise the capability to restore files can be enabled explicitly by -osirrox *on*. It can be irrevocably disabled by -osirrox *banned*. The setting *blocked* is like *off*. But it can only be revoked by setting *unblock*, which elsewise is like *on*. This can be used to curb command scripts which might use *on* undesiredly. To enable restoring of special files by *device_files* is potentially dangerous. The meaning of the number st_rdev (see man 2 stat) depends much on the operating system. Best is to restore device files only to the same system from where they were copied. If not enabled, device files in the ISO image are ignored during restore operations. Due to a bug of previous versions, device files from previous sessions might have been altered to major=0, minor=1. So this combination does not get restored. Option *concat_split_on* is default. It enables restoring of split file directories as data files if the directory contains a complete collection of -cut_out part files. With option *concat_split_off* such directories are handled like any other ISO image directory. Option *auto_chmod_off* is default. If *auto_chmod_on* is set then access restrictions for disk directories get circumvented if those directories are owned by the effective user who runs 'xorriso'. This happens by temporarily granting rwx permission to the owner. Option *sort_lba_on* may improve read performance with optical drives. It can restore large numbers of hard links without exhausting -temp_mem_limit. It does not preserve directory mtime and it needs -osirrox option auto_chmod_on in order to extract directories which offer no write permission. Default is *sort_lba_off*. Option *o_excl_on* is the default unless the program was started with leafname "osirrox". On GNU/Linux it tries to avoid using drives which are mounted or in use by other libburn programs. Option *o_excl_off* on GNU/Linux enables access to such drives by the equivalent of -drive_access "shared:readonly". I.e. drives which get acquired while *o_excl_off* will refuse to get blanked, formatted, written, or ejected. But be aware that even harmless inquiries can spoil ongoing burns of CD-R[W] and DVD-R[W]. Option *strict_acl_off* is default. It tolerates on FreeBSD the presence of directory "default" ACLs in the ISO image. With *strict_acl_on* these GNU/Linux ACLs cause on FreeBSD a FAILURE event during restore with -acl "on". Option *check_md5_off* disables MD5 checking during copy to disk. The default option *check_md5_on* enables it if -md5 is "on". If a data file with recorded MD5 is copied as a whole to the disk filesystem, then the MD5 of the copied content gets computed and compared with the recorded MD5. A mismatch causes an error message of severity SORRY. Option *check_md5_force* causes an error message if -md5 is "on" but no MD5 is recorded for the data file. Option *sparse=* controls production of sparse files during extraction of files from the ISO filesystem. Default is *sparse=off*. A positive number like in *sparse=1m* sets the minimum requirement for the length of a sequence of 0-bytes which shall be represented by a gap. This saves disk space if the disk filesystem supports sparse files. A gap gets created by help of lseek(2) if a sequence of read buffers, which contain only 0-bytes, bears at least the minimum amount of bytes. Expect read buffers to be in the size range of 32k or 64k. Command -paste_in creates gaps only if the writing begins at or after the end of the existing disk file. So the sequence of -paste_in commands matters. Command -concat does not create sparse files. -extract iso_rr_path disk_path Copy the file objects at and underneath iso_rr_path to their corresponding addresses at and underneath disk_path. This is the inverse of -map or -update_r. If iso_rr_path is a directory and disk_path is an existing directory then both trees will be merged. Directory attributes get extracted only if the disk directory is newly created by the copy operation. Disk files get removed only if they are to be replaced by file objects from the ISO image. As many attributes as possible are copied together with restored file objects. -extract_single iso_rr_path disk_path Like -extract, but if iso_rr_path is a directory then its sub tree gets not restored. -extract_l iso_rr_prefix disk_prefix iso_rr_path [***] Perform -extract with each of the iso_rr_path parameters. disk_path will be composed from iso_rr_path by replacing iso_rr_prefix by disk_prefix. -extract_cut iso_rr_path byte_offset byte_count disk_path Copy a byte interval from a data file out of an ISO image into a newly created disk file. The main purpose for this is to offer a way of handling large files if they are not supported by mount -t iso9660 or if the target disk filesystem cannot store large files. If the data bytes of iso_rr_path are stored in the loaded ISO image, and no filter is applied, and byte_offset is a multiple of 2048, then a special run of -check_media is performed. It may be quicker and more rugged than the general reading method. -cpx iso_rr_path [***] disk_path Copy single leaf file objects from the ISO image to the address given by disk_path. If more then one iso_rr_path is given then disk_path must be a directory or non-existent. In the latter case it gets created and the extracted files get installed in it with the same leafnames. Missing directory components in disk_path will get created, if possible. Directories are allowed as iso_rr_path only with -osirrox "concat_split_on" and only if they actually represent a complete collection of -cut_out split file parts. -cpax iso_rr_path [***] disk_path Like -cpx but restoring mtime, atime as in ISO image and trying to set ownership and group as in ISO image. -cp_rx iso_rr_path [***] disk_path Like -cpx but also extracting whole directory trees from the ISO image. The resulting disk paths are determined as with shell command cp -r : If disk_path is an existing directory then the trees will be inserted or merged underneath this directory and will keep their leaf names. The ISO directory "/" has no leaf name and thus gets mapped directly to disk_path. -cp_rax iso_rr_path [***] disk_path Like -cp_rx but restoring mtime, atime as in ISO image and trying to set ownership and group as in ISO image. -paste_in iso_rr_path disk_path byte_offset byte_count Read the content of a ISO data file and write it into a data file or device file on disk beginning at the byte_offset. Write at most byte_count bytes. The file depicted by disk_path has to support random write access. This is the inverse of command -cut_out. -concat mode [target | lim prog [args [...]] lim] iso_rr_path [***] Copy the data content of one or more data files of the ISO image into a disk file object, into a file descriptor, or start a program and copy the data into its standard input. The latter is subject to the security restrictions for external filters. Modes *overwrite* and *append* write into the target which is given by the second parameter. This may be the path to a disk file object, or "-" which means standard output, or a text of the form /dev/fd/number, where number is an open file descriptor (e.g. standard error is /dev/fd/2). An existing target file is not removed before writing begins. If it is not able to take content data, then this command fails. Mode overwrite truncates regular data files to 0 size before writing into them. Example: -concat append /home/me/accumulated_text /my/iso/text - Mode *pipe* expects as second parameter a delimiter word which shall mark the end of the program argument list. The third argument is the disk_path to the program. It must contain at least one '/'. $PATH is not applied. Further parameters up to the announced delimiter word are used as arguments with the program start. Example: -iso_rr_pattern on \ -concat pipe + /usr/bin/wc + "/my/iso/files*" - The further parameters in all modes are the iso_rr_paths of data files. Their content gets concatenated in the copy. -extract_boot_images disk_path Copy boot equipment to disk, which is not necessarily represented as data files in the ISO filesystem. The data get written into various files in a disk directory, which may already exist or of which the parent must exist so that it can get created. Files may be missing if their corresponding information is not present in the ISO filesystem. Existing files do not get overwritten but rather cause a failure event. The same data may appear in different files. E.g. the El Torito boot image for EFI is often the same data as the EFI partition in MBR or GPT. File "eltorito_catalog.img" contains the El Torito Boot Catalog. Files "eltorito_img*_*.img" contain El Torito Boot images. The first "*" gives the image number, the second "*" gives the type: "bios", "mac", "ppc", "uefi", or a hex number. File "mbr_code_isohybrid.img" contains the ISOLINUX MBR template. File "mbr_code_grub2.img" contains the GRUB2 MBR template. File "systemarea.img" contains the whole 32 KiB of System Area if not all zero. Files "mbr_part*_efi.img" contain EFI partition images from the MBR partition table. The "*" text part gives the partition number. Files "mbr_part*_prep.img" contain PReP partition images. Files "gpt_part*_efi.img" contain EFI partition images from GPT. Files "gpt_part*_hfsplus.img" contain HFS+ partition images from GPT. To avoid extracting the whole HFS+ aspect of hybrid ISO filesystems, the partition image is extracted only if it has less than half of the size of the ISO filesystem or if the partition is outside the ISO filesystem. -mount drive entity id path Produce the same line as -mount_cmd and then execute it as external program run after giving up the depicted drive. See also -mount_opts. This demands -osirrox to be enabled and normally will succeed only for the superuser. For safety reasons the mount program is only executed if it is reachable as /bin/mount or /sbin/mount.  File: xorriso.info, Node: Emulation, Next: Scripting, Prev: Restore, Up: Commands 9.20 Command compatibility emulations (cdrtools) ================================================ Writing of ISO 9660 on CD is traditionally done by program mkisofs as ISO 9660 image producer and cdrecord as burn program. 'xorriso' does not strive for their comprehensive emulation. Nevertheless it is ready to perform some of its core tasks under control of commands which in said programs trigger comparable actions. -as personality option [options] -- Perform the variable length option list as sparse emulation of the program depicted by the personality word. Personality "*mkisofs*" accepts the options listed with: -as mkisofs -help -- Among them: -R (always on), -r, -J, -o, -M, -C, -dir-mode, -file-mode, -path-list, -m, -exclude-list, -f, -print-size, -pad, -no-pad, -V, -v, -version, -graft-points, -z, -no-emul-boot, -b, -c, -boot-info-table, -boot-load-size, -input-charset, -G, -output-charset, -U, -hide, -hide-joliet, -hide-list, -hide-joliet-list, file paths and pathspecs. A lot of options are not supported and lead to failure of the mkisofs emulation. Some are ignored, but better do not rely on this tolerance. The supported options are documented in detail in xorrisofs.info and in man xorrisofs. The description here is focused on the effect of mkisofs emulation in the context of a 'xorriso' run. Other than with the "cdrecord" personality there is no automatic -commit at the end of a "mkisofs" option list. Verbosity settings -v (= "UPDATE") and -quiet (= "SORRY") persist. The output file persists until things happen like -commit, -rollback, -dev, or end of 'xorriso'. Options which affect all file objects in the ISO image, like -r or -dir-mode, will be applied only to files which are present in the ISO image when the command -as ends. If you use several -as mkisofs commands in the same run, then consider to put such options into the last -as command. If files are added to the image, then -pacifier gets set to "mkisofs" and -stdio_sync is defaulted to "off" if no such setting was made yet. -graft-points is equivalent to -pathspecs on. Note that pathspecs without "=" are interpreted differently than with 'xorriso' command -add. Directories get merged with the root directory of the ISO image, other filetypes get mapped into that root directory. If pathspecs are given and if no output file was chosen before or during the "mkisofs" option list, then standard output (-outdev "-") will get into effect. If -o points to a regular file, then it will be truncated to 0 bytes when finally writing begins. This truncation does not happen if the drive is chosen by 'xorriso' commands before -as mkisofs or after its list delimiter. Directories and symbolic links are no valid -o targets. Writing to stdout is possible only if -as "mkisofs" was among the start arguments or if other start arguments pointed the output drive to standard output. -print-size inhibits automatic image production at program end. This ban is lifted only if the pending image changes get discarded. Padding is counted as part of the ISO image if not option -emul-toc is given. If no -iso-level is given, then level 1 is chosen when the first file or directory is added to the image. At the same occasion directory names get allowed to violate the standard by -compliance option allow_dir_id_ext. This may be avoided by option -disallow_dir_id_ext. Option -root is supported. Option -old-root is implemented by 'xorriso' commands -mkdir, -cp_clone, -find update_merge, and -find rm_merge. -root and -old-root set command -disk_dev_ino to "ino_only" and -md5 to "on", by default. -disk_dev_ino can be set to "off" by --old-root-no-ino or to "on" by --old-root-devno . -md5 can be set to "off" by --old-root-no-md5 . Not original mkisofs options are --quoted_path_list , --hardlinks , --acl , --xattr , --md5 , --stdio_sync . They work like the 'xorriso' commands with the same name and hardcoded parameter "on", e.g. -acl "on". Explicit parameters are expected by --stdio_sync and --scdbackup_tag. The capability to preserve multi-session history on overwritable media gets disabled by default. It can be enabled by using --emul-toc with the first session. See -compliance no_emul_toc. --sort-weight gets as parameters a number and an iso_rr_path. The number becomes the LBA sorting weight of regular file iso_rr_path or of all regular files underneath directory iso_rr_path. (See -find -exec sort_weight). Adopted from grub-mkisofs are --protective-msdos-label (see -boot_image grub partition_table=on) and --modification-date=YYYYMMDDhhmmsscc (see -volume_date uuid). For EFI bootable GRUB boot images use --efi-boot. It performs -boot_image grub efi_path= surrounded by two -boot_image "any" "next". Alternative option -e from Fedora genisoimage sets bin_path and platform_id for EFI, but performs no "next". For MBR bootable ISOLINUX images there is -isohybrid-mbr FILE, where FILE is one of the Syslinux files mbr/isohdp[fp]x*.bin . Use this instead of -G to apply the effect of -boot_image isolinux partition_table=on. --boot-catalog-hide is -boot_image any cat_hidden=on. -mips-boot is the same as -boot_image any mips_path= . -mipsel-boot leads to mipsel_path= . -partition_offset number is -boot_image any partition_offset=number. Command -append_partition is supported. -untranslated_name_len number is -compliance untranslated_name_len=number. --old-empty is -compliance old_empty. The options of genisoimage Jigdo Template Extraction are recognized and performed via 'xorriso' command -jigdo. See the "Alias:" names there for the meaning of the genisoimage options. Personalities "*xorrisofs*", "*genisoimage*", and "*genisofs*" are aliases for "mkisofs". If 'xorriso' is started with one of the leafnames "xorrisofs", "genisofs", "mkisofs", or "genisoimage", then it performs -read_mkisofsrc and prepends -as "genisofs" to the program arguments. I.e. all arguments will be interpreted mkisofs style until "--" is encountered. From then on, arguments are interpreted as 'xorriso' commands. --no_rc as first argument of such a program start prevents interpretation of startup files. See section FILES below. Personality "*cdrecord*" accepts the options listed with: -as cdrecord -help -- Among them: -v, dev=, speed=, blank=, fs=, -eject, -atip, padsize=, tsize=, -isosize, -multi, -msinfo, --grow_overwriteable_iso, write_start_address=, track source file path or "-" for standard input as track source. It ignores most other options of cdrecord and cdrskin but refuses on -audio, -scanbus, and on blanking modes unknown to 'xorriso'. The scope is only a single data track per session to be written to blank, overwritable, or appendable media. The medium gets closed if closing is applicable and not option -multi is present. If an input drive was acquired, then it is given up. This is only allowed if no image changes are pending. dev= must be given as 'xorriso' device address. Addresses like 0,0,0 or ATA:1,1,0 are not supported. If a track source is given, then an automatic -commit happens at the end of the "cdrecord" option list. --grow_overwriteable_iso enables emulation of multi-session on overwritable media. To enable emulation of a TOC, the first session needs -C 0,32 with -as mkisofs (but no -M) and --grow_overwriteable_iso write_start_address=32s with -as cdrecord. A much more elaborate libburn based cdrecord emulator is the program cdrskin. Personalites "*xorrecord*", "*wodim*", and "*cdrskin*" are aliases for "cdrecord". If 'xorriso' is started with one of the leafnames "xorrecord", "cdrskin", "cdrecord", or "wodim", then it automatically prepends -as "cdrskin" to the program arguments. I.e. all arguments will be interpreted cdrecord style until "--" is encountered. From then on, arguments are interpreted as 'xorriso' commands. --no_rc as first argument of such a program start prevents interpretation of 'xorriso' startup files. See section FILES below. -read_mkisofsrc Try one by one to open for reading: ./.mkisofsrc , $MKISOFSRC , $HOME/.mkisofsrc , $(dirname $0)/.mkisofsrc On success interpret the file content as of man mkisofs CONFIGURATION, and end this command. Do not try further files. The last address is used only if start argument 0 has a non-trivial dirname. The reader currently interprets the following NAME=VALUE pairs: APPI (-application_id) , PUBL (-publisher) , SYSI (-system_id) , VOLI (-volid) , VOLS (-volset_id) Any other lines will be silently ignored. -genisoimage_completion "on"|"off" Enable or disable the completion of genisoimage options during -as mkisofs emulation. If enabled by "on", then unrecognized option arguments which begin by a dash '-' get compared against the known genisoimage options, like program genisoimage does unconditionally (and undocumentedly). If the given argument matches the beginning of exactly one genisoimage option, then it gets replaced by that option. Option arguments which consist entirely of a leading dash and letters out of "dDfJlNRrTUvz" are not matched but rather interpreted as usual, i.e. as multiple options with leading dash and each single letter. If no genisoimage option is found or more than one are found, then a SORRY message is issued and the argument stays as is. If disabled by "off", no completion of options happens. Like with enabled completion, option arguments which consist entirely of letters out of "dDfJlNRrTUvz" are not matched but rather interpreted as multiple arguments with leading dash and each single letter. -pacifier behavior_code Control behavior of UPDATE pacifiers during write operations. The following behavior codes are defined: "xorriso" is the default format: Writing: sector XXXXX of YYYYYY [fifo active, nn% fill] "cdrecord" looks like: X of Y MB written (fifo nn%) [buf mmm%] "mkisofs" nn% done, estimate finish Tue Jul 15 20:13:28 2008 The frequency of the messages can be adjusted by "interval=number" where number gives the seconds between two messages. Permissible settings are 0.1 to 60.0. -scdbackup_tag list_path record_name Set the parameter "name" for a scdbackup checksum record. It will be appended in an scdbackup checksum tag to the -md5 session tag if the image starts at LBA 0. This is the case if it gets written as first session onto a sequential medium, or piped into a program, named pipe or character device. If list_path is not empty then the record will also be appended to the data file given by this path. Program scdbackup_verify will recognize and verify tag and file record. An empty record_name disables this feature.  File: xorriso.info, Node: Scripting, Next: Frontend, Prev: Emulation, Up: Commands 9.21 Scripting, dialog and program control features =================================================== -no_rc Only if used as first program argument this command prevents reading and interpretation of startup files. See section FILES below. -options_from_file fileaddress Read quoted input from fileaddress and execute it like dialog lines. Empty lines and lines which begin by # are ignored. Normally one line should hold one 'xorriso' command and all its parameters. Nevertheless lines may be concatenated by a trailing backslash. See also section "Command processing", paragraph "Quoted input". -help Print helptext. -version Print program name and version, component versions, license. -list_extras code Tell whether certain extra features were enabled at compile time. Code "all" lists all features and a headline. Other codes pick a single feature. Code "codes" lists them. They share names with related commands (see also there): "acl" tells whether xorriso has an adapter for local filesystems ACLs. "xattr" tells whether xorriso has an adapter for local filesystems EA. "lfa_flags" tells whether xorriso has an adapter for local Linux file attributes (see man 1 chattr). "jigdo" tells whether production of Jigdo files is possible. "zisofs" tells whether zisofs and built-in gzip filters are enabled. "external_filter" tells whether external filter processes are allowed and whether they are allowed if real user id and effective user id differ. "dvd_obs" tells whether 64 kB output to DVD media is default. "use_readline" tells whether readline may be enabled in dialog mode. -history textline Copy textline into libreadline history. -status mode|filter Print the current settings of 'xorriso'. Modes: short... print only important or altered settings long ... print all settings including defaults long_history like long plus history lines Filters begin with '-' and are compared literally against the output lines of -status:long_history. A line is put out only if its start matches the filter text. No wildcards. -status_history_max number Set maximum number of history lines to be reported with -status "long_history". -list_delimiter word Set the list delimiter to be used instead of "--". It has to be a single word, must not be empty, not longer than 80 characters, and must not contain quotation marks. For brevity the list delimiter is referred as "--" throughout this text. -sh_style_result "on"|"off" Make the result output of some filesystem inspection commands look more like the output of equivalent shell commands. The most important effect is to prevent the wrapping of file addresses into quotation marks with commands -pwd -pwdx -ls -lsd -lsl -lsdl -lsx -lsdx -lslx -lsdlx -du -dus -dux -dusx -findx -find This will make ambiguous the representation of file names which contain newline characters. On the other hand it should facilitate integration of xorriso into shell scripts which already use the corresponding shell commands. -backslash_codes "on"|"off"|mode[:mode] Enable or disable the interpretation of symbolic representations of special characters with quoted input, or with program arguments, or with program text output. If enabled the following translations apply: \a=bell(007) \b=backspace(010) \e=Escape(033) \f=formfeed(014) \n=linefeed(012) \r=carriage_return(015) \t=tab(011) \v=vtab(013) \\=backslash(134) \[0-7][0-7][0-7]=octal_code \x[0-9a-f][0-9a-f]=hex_code \cC=control-C Translations can occur with quoted input in 3 modes: "in_double_quotes" translates only inside " quotation. "in_quotes" translates inside " and ' quotation. "with_quoted_input" translates inside and outside quotes. With the start program arguments there is mode: "with_program_arguments" translates program arguments. Mode "encode_output" encodes output characters. It combines "encode_results" with "encode_infos". Inside single or double quotation marks encoding applies to 8-bit characters octal 001 to 037 , 177 to 377 and to backslash(134). Outside quotation marks some harmless ASCII control characters stay unencoded: bell(007), backspace(010), tab(011), linefeed(012), formfeed(014), carriage_return(015). Mode "off" is default and disables any translation. Mode "on" is "with_quoted_input:with_program_arguments:encode_output". -temp_mem_limit number["k"|"m"] Set the maximum size of temporary memory to be used for image dependent buffering. Currently this applies to pattern expansion, LBA sorting, restoring of hard links. Default is 16m = 16 MiB, minimum 64k = 64 kiB, maximum 1024m = 1 GiB. -print text Print a text line to the result channel which is by default stdout. -print_info text Print a text line to the info channel which is by default stderr. -print_mark text Print a text line to the mark channel which is by default directed to both, result and info channel. An empty text will cause no output at all. -prompt text Show text at beginning of output line and wait for the user to hit the Enter key or to send a line via stdin. -sleep seconds Wait for the given number of seconds before performing the next command. Expect coarse granularity no better than 1/100 seconds. -errfile_log mode path|channel If problem events are related to input files from the filesystem, then their disk_paths can be logged to a file or to output channels R or I. Mode can either be "plain" or "marked". The latter causes marker lines which give the time of log start, burn session start, burn session end, log end or program end. In mode "plain", only the file paths are logged. If path is "-" or "-R" then the log is directed to the result channel. Path "-I" directs it to the info message channel. Any text that does not begin with "-" is used as path for a file to append the log lines. Problematic files can be recorded multiple times during one program run. If the program run aborts then the list might not be complete because some input files might not have been processed at all. The errfile paths are transported as messages of very low severity "ERRFILE". This transport becomes visible with -report_about "ALL". -session_log path If path is not empty it gives the address of a plain text file where a log record gets appended after each session. This log can be used to determine the start_lba of a session for mount options -o sbsector= (on GNU/Linux) or -s (on FreeBSD) from date or volume ID. Record format is: timestamp start_lba size volume-id The first three items are single words, the rest of the line is the volume ID. -scsi_log "on"|"off" Mode "on" enables very verbose logging of SCSI commands and drive replies. Logging messages get printed to stderr, not to any of the 'xorriso' output channels. A special property of this command is that the first -scsi_log setting among the start arguments is in effect already when the first operations of 'xorriso' begin. Only "-scsi_log" with dash "-" is recognized that way. -end End program after writing pending changes. -rollback_end Discard pending changes. End program immediately. # any text Only in dialog or file execution mode, and only as first non-whitespace in line: Do not execute the line but store it in readline history.  File: xorriso.info, Node: Frontend, Next: ExDevices, Prev: Scripting, Up: Commands 9.22 Support for frontend programs via stdin and stdout ======================================================= -pkt_output "on"|"off" Consolidate text output on stdout and classify each line by a channel indicator: 'R:' for result lines, 'I:' for notes and error messages, 'M:' for -mark texts. Next is a decimal number of which only bit 0 has a meaning for now. 0 means no newline at end of payload, 1 means that the newline character at the end of the output line belongs to the payload. After another colon and a blank follows the payload text. Example: I:1: enter option and parameters : -logfile channel fileaddress Copy output of a channel to the given file. Channel may be one of: "." for all channels, "I" for info messages, "R" for result lines, "M" for -mark texts. -mark text If text is not empty it will get put out on "M" channel each time 'xorriso' is ready for the next dialog line or before 'xorriso' performs a command that was entered to the pager prompt. -msg_op opcode parameter_text This command shall facilitate extraction of particular information from the message output of other commands. It gives access to the C API function Xorriso_parse_line() and to the message sieve that is provided by the C API. Please refer to their descriptions in file xorriso.h. Further it helps to interpret the severity codes of info messages. Intended users are frontend programs which operate xorriso in dialog mode. The result output of this command is not caught by the message sieve. The following opcodes are defined: *start_sieve* Install the message sieve as of Xorriso_sieve_big() and start watching program messages. The parameter_text has no meaning. *show_sieve* Show a list of filter rule names. The parameter_text has no meaning. The list begins by a line with the return value of Xorriso_sieve_get_result() with flag bit3. If this value is larger than 0, then the next line tells the number of names. The following lines show one name each. *read_sieve* Use the parameter_text as name of a filter rule and inquire its next recorded result. See Xorriso_sieve_big() for a list of names and reply strings. The recorded strings are put out on result channel. They get wrapped into lines which tell their structure. The first line tells the return value of Xorriso_sieve_get_result(). The next line tells the number of strings. Each string begins by a line that tells the number of lines of the string. Then follow these lines. They are to be concatenated with a newline character between each of them. Finally the number of still available recorded results of the given name is put out. *clear_sieve* Dispose all recorded strings and continue watching program messages. The parameter_text has no meaning. *end_sieve* Dispose the sieve with its filter rules and stop watching program messages. The parameter_text has no meaning. *parse* Read a text from dialog input and submit it to Xorriso_parse_line(). The parameter_text word shall consist of several words separated by blanks. It will be necessary to use both kinds of quotation marks. E.g. "'ISO session :' " 0 0 1" The five parameter words are: prefix, separators, max_words, flag, number_of_input_lines. The former four are handed over to Xorriso_parse_line(). The number of input lines minus one tells xorriso how many newline characters are part of the input text. The announced number of text lines will be read from dialog input, concatenated with a newline character between each of them, and submitted to Xorriso_parse_line() as parameter line. Note that newlines outside of quotation marks are interpreted as separators if the separators parameter is empty. The parsed strings are put out on result channel. They get wrapped into lines which tell their structure. The first line tells the return value of Xorriso_parse_line(). The next line tells the number of strings. Each string begins by a line that tells the number of lines of the string. Then follow these lines. They are to be concatenated with a newline character between each of them. If -backslash_codes "encode_output" is enabled, then the strings undergo encoding as if they were enclosed in quotes. Escpecially each string will be put out as a single result line. *parse_bulk* Like "parse", but with the fifth parameter word being number_of_input_texts rather than number_of_input_lines. Each input text has to be preceded by a line that tells number_of_input_lines as with "parse". Then come the announced number of text lines. All input texts will be read before printing of result lines begins. This consumes memory in xorriso. So the number_of_input_texts should not be extremely high. On the other hand, large transactions of command, input texts, and results are desirable if connection latency is an issue. *parse_silently* Like "parse" but not issuing a prompting message. Confusing to humans. *parse_bulk_silently* Like "parse_bulk" but not issuing a prompting message. Confusing to humans. *compare_sev* The parameter_text should contain two comma separated severity texts as issued by this program. Like "SORRY,UPDATE". See also paragraph "Exception processing". These two severity texts get compared and a number gets printed to the result channel. This number is 0 if both severities are equal. It is -1 if the first severity is lower than the second one. It is 1 is the first severity is higher than the second one. Above example "SORRY,UPDATE" will yield 1. *list_sev* Print to the result channel a blank separated list of all severity names. Sorted from low to high severity. -named_pipe_loop mode[:mode] disk_path_stdin disk_path_stdout disk_path_stderr Temporarily replace standard input, standard output and standard error by named pipes. Enter dialog mode without readline. Defined modes are: "cleanup" removes the submitted pipe files when the loop ends. "keep" does not delete them. This is the default. "buffered" reads all lines from the input pipe until EOF before it opens the output pipes and processes the input lines. "direct" opens the output pipes after the first input line was read. Each line is executed directly after it is read. This is the default. The other three parameters must either be disk paths to existing named pipes, or be "-" to leave the according standard i/o channel unreplaced. xorriso will open the stdin pipe, read and execute dialog lines from it until the sender closes the pipe. The output pipes get opened depending on mode "buffered" or "direct". After all lines are executed, xorriso will close its side of the pipes and enter a new cycle of opening, reading and executing. If an input line consists only of the word "end_named_pipe_loop" then -named_pipe_loop will end and further xorriso commands may be executed from other sources. -launch_frontend program [arguments ...] -- Start the program that is given as first parameter. Submit the other parameters as program arguments. Enable xorriso dialog mode. Two nameless pipe objects are created. xorriso standard input gets connected to the standard output of the started program. xorriso standard output and standard error get connected to the standard input of that program. xorriso will abort when the started program ends or if it cannot be started at all. In both cases it will return a non-zero exit value. The exit value will be zero if the frontend sends -end or -rollback_end before ending itself. This command may be totaly banned at compile time. It is banned by default if xorriso runs under setuid permissions. The program name will not be searched in the $PATH directories. To make this clear, it must contain at least one /-character. Best is an absolute path. Example: xorriso -launch_frontend "$(which xorriso-tcltk)" -stdio - The frontend program should first send via its standard output: -mark 0 -pkt_output on -msg_op start_sieve - -reassure off It should be ready to decode -pkt_output and to react on -mark messages. Best is to increment the -mark number after each sent command sequence and then to wait for the new number to show up in a mark message: ...some...commands... -mark <incremented_number> Further are advised: -report_about UPDATE -abort_on NEVER -iso_rr_pattern off -disk_pattern off A check of the xorriso version should be done, in order to make sure that all desired features are present. Command -launch_frontend will only work once per xorriso run. If no command parameters are submitted or if program is an empty text, then no program will be started but nevertheless -launch_frontend will be irrevocably disabled. -prog text Use text as name of this program in subsequent messages -prog_help text Use text as name of this program and perform -help.  File: xorriso.info, Node: Examples, Next: Files, Prev: Commands, Up: Top 10 Examples *********** * Menu: * ExDevices:: As superuser learn about available drives * ExCreate:: Blank medium and compose a new ISO image as batch run * ExDialog:: A dialog session doing about the same * ExGrowing:: Manipulate an existing ISO image on the same medium * ExModifying:: Copy modified ISO image from one medium to another * ExBootable:: Bring a prepared ISOLINUX tree onto medium and make it bootable * ExCharset:: Change existing file name tree from ISO-8859-1 to UTF-8 * ExPseudo:: Operate on storage facilities other than optical drives * ExCdrecord:: Burn an existing ISO image file to medium * ExMkisofs:: Perform multi-session runs as of cdrtools traditions * ExGrowisofs:: Let 'xorriso' work underneath growisofs * ExException:: Adjust thresholds for verbosity, exit value and program abort * ExTime:: Examples of input timestrings * ExIncBackup:: Incremental backup of a few directory trees * ExRestore:: Restore directory trees from a particular ISO session to disk * ExRecovery:: Try to retrieve blocks from a damaged medium  File: xorriso.info, Node: ExDevices, Next: ExCreate, Prev: Frontend, Up: Examples 10.1 As superuser learn about available drives ============================================== On Linux, FreeBSD or NetBSD consider to give rw-permissions to those users or groups which shall be able to use the drives with 'xorriso'. On Solaris use pfexec. Consider to restrict privileges of 'xorriso' to "base,sys_devices" and to give r-permission to user or group. $ xorriso -device_links 1 -dev '/dev/cdrom1' rwrw-- : 'TSSTcorp' 'DVD-ROM SH-D162C 1 -dev '/dev/cdrw' rwrw-- : 'TSSTcorp' 'CDDVDW SH-S223B' 2 -dev '/dev/cdrw3' rwrw-- : 'HL-DT-ST' 'BDDVDRW_GGC-H20L'  File: xorriso.info, Node: ExCreate, Next: ExDialog, Prev: ExDevices, Up: Examples 10.2 Blank medium and compose a new ISO image as batch run ========================================================== Acquire drive /dev/sr2, make medium ready for writing a new image, fill the image with the files from hard disk directories /home/me/sounds and /home/me/pictures. Because no -dialog "on" is given, the program will then end by writing the session to the medium. $ xorriso -outdev /dev/sr2 \ -blank as_needed \ -map /home/me/sounds /sounds \ -map /home/me/pictures /pictures The ISO image may be shaped in a more elaborate way like the following: Omit some unwanted stuff by removing it from the image directory tree. Reintroduce some wanted stuff. $ cd /home/me $ xorriso -outdev /dev/sr2 \ -blank as_needed \ -map /home/me/sounds /sounds \ -map /home/me/pictures /pictures \ -rm_r \ /sounds/indecent \ '/pictures/*private*' \ /pictures/confidential \ -- \ -cd / \ -add pictures/confidential/work* -- Note that '/pictures/*private*' is a pattern for iso_rr_paths while pictures/confidential/work* gets expanded by the shell with addresses from the hard disk. Commands -add and -map have different parameter rules but finally the same effect: they put files into the image.  File: xorriso.info, Node: ExDialog, Next: ExGrowing, Prev: ExCreate, Up: Examples 10.3 A dialog session doing about the same as the previous example ================================================================== Some settings are already given as start argument. The other activities are done as dialog input. The pager gets set to 20 lines of 80 characters. The drive is acquired by command -dev rather than -outdev in order to see the message about its current content. By command -blank this content is made ready for being overwritten and the loaded ISO image is made empty. In order to be able to eject the medium, the session needs to be committed explicitly. $ xorriso -dialog on -page 20 80 -disk_pattern on enter option and arguments : -dev /dev/sr2 enter option and arguments : -blank as_needed enter option and arguments : -map /home/me/sounds /sounds -map /home/me/pictures /pictures enter option and arguments : -rm_r /sounds/indecent /pictures/*private* /pictures/confidential enter option and arguments : -cdx /home/me/pictures -cd /pictures enter option and arguments : -add confidential/office confidential/factory enter option and arguments : -du / enter option and arguments : -commit_eject all -end  File: xorriso.info, Node: ExGrowing, Next: ExModifying, Prev: ExDialog, Up: Examples 10.4 Manipulate an existing ISO image on the same medium ======================================================== Load image from drive. Remove (i.e. hide) directory /sounds and its subordinates. Rename directory /pictures/confidential to /pictures/restricted. Change access permissions of directory /pictures/restricted. Add new directory trees /sounds and /movies. Burn to the same medium, check whether the tree can be loaded, and eject. $ xorriso -dev /dev/sr2 \ -rm_r /sounds -- \ -mv \ /pictures/confidential \ /pictures/restricted \ -- \ -chmod go-rwx /pictures/restricted -- \ -map /home/me/prepared_for_dvd/sounds_dummy /sounds \ -map /home/me/prepared_for_dvd/movies /movies \ -commit -eject all  File: xorriso.info, Node: ExModifying, Next: ExBootable, Prev: ExGrowing, Up: Examples 10.5 Copy modified ISO image from one medium to another ======================================================= Load image from input drive. Do the same manipulations as in the previous example. Acquire output drive and blank it. Burn the modified image as first and only session to the output drive. $ xorriso -indev /dev/sr2 \ -rm_r /sounds -- \ ... -outdev /dev/sr0 -blank as_needed \ -commit -eject all  File: xorriso.info, Node: ExBootable, Next: ExCharset, Prev: ExModifying, Up: Examples 10.6 Bring a prepared ISOLINUX tree onto medium and make it bootable ==================================================================== The user has already created a suitable file tree on disk and copied the ISOLINUX files into subdirectory ./boot/isolinux of that tree. Now 'xorriso' can burn an El Torito bootable medium: $ xorriso -outdev /dev/sr0 -blank as_needed \ -map /home/me/ISOLINUX_prepared_tree / \ -boot_image isolinux dir=/boot/isolinux  File: xorriso.info, Node: ExCharset, Next: ExPseudo, Prev: ExBootable, Up: Examples 10.7 Change existing file name tree from ISO-8859-1 to UTF-8 ============================================================ This example assumes that the existing ISO image was written with character set ISO-8859-1 but that the readers expected UTF-8. Now a new session gets added with converted file names. Command -changes_pending "yes" enables writing despite the lack of any manipulation command. In order to avoid any weaknesses of the local character set, this command pretends that it uses already the final target set UTF-8. Therefore strange file names may appear in messages, which will be made terminal-safe by command -backslash_codes. $ xorriso -in_charset ISO-8859-1 -local_charset UTF-8 \ -out_charset UTF-8 -backslash_codes on -dev /dev/sr0 \ -changes_pending yes -commit -eject all  File: xorriso.info, Node: ExPseudo, Next: ExCdrecord, Prev: ExCharset, Up: Examples 10.8 Operate on storage facilities other than optical drives ============================================================ Full read-write operation is possible with regular files and block devices: $ xorriso -dev /tmp/regular_file ... Paths underneath /dev normally need prefix "stdio:" $ xorriso -dev stdio:/dev/sdb ... If /dev/sdb is to be used frequently and /dev/sda is the system disk, then consider to place the following lines in a 'xorriso' Startup File. They allow you to use /dev/sdb without prefix and protect disk /dev/sda from 'xorriso': -drive_class banned /dev/sda* -drive_class harmless /dev/sdb Other writeable file types are supported write-only: $ xorriso -outdev /tmp/named_pipe ... Among the write-only drives is standard output: $ xorriso -outdev - \ ... | gzip >image.iso.gz  File: xorriso.info, Node: ExCdrecord, Next: ExMkisofs, Prev: ExPseudo, Up: Examples 10.9 Burn an existing ISO image file to medium ============================================== Actually this works with any kind of data, not only ISO images: $ xorriso -as cdrecord -v dev=/dev/sr0 blank=as_needed image.iso  File: xorriso.info, Node: ExMkisofs, Next: ExGrowisofs, Prev: ExCdrecord, Up: Examples 10.10 Perform multi-session runs as of cdrtools traditions ========================================================== Between both processes there can be performed arbitrary transportation or filtering. The first session is written like this: $ xorriso -as mkisofs prepared_for_iso/tree1 | \ xorriso -as cdrecord -v dev=/dev/sr0 blank=fast -multi -eject - Follow-up sessions are written like this (the run of dd is only to give demons a chance to spoil it): $ m=$(xorriso -as cdrecord dev=/dev/sr0 -msinfo) $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 $ xorriso -as mkisofs -M /dev/sr0 -C $m prepared_for_iso/tree2 | \ xorriso -as cdrecord -v dev=/dev/sr0 -waiti -multi -eject - Always eject the drive tray between sessions. The run of xorriso -as mkisofs will read old sessions via the CD-ROM driver of /dev/sr0. This driver might not be aware of the changed content as long as the medium is not loaded again. In this case the previous session would not be properly assessed by xorriso and the new session would contain only the newly added files. Some systems have not enough patience with automatic tray loading and some demons may interfere with a first CD-ROM driver read attempt from a freshly loaded medium. When loading the tray manually, wait 10 seconds after the drive has stopped blinking. A safe automatic way seems to be a separate run of xorriso for loading the tray with proper waiting, and a subsequent run of dd which shall offer itself to any problems caused by demons assessing the changed drive status. If this does not help, insert a run of "sleep 10" between xorriso and dd. This example works for multi-session media only. Add cdrskin option --grow_overwriteable_iso to all -as cdrecord runs in order to enable multi-session emulation on overwritable media.  File: xorriso.info, Node: ExGrowisofs, Next: ExException, Prev: ExMkisofs, Up: Examples 10.11 Let 'xorriso' work underneath growisofs ============================================= growisofs expects an ISO formatter program which understands options -C and -M. If 'xorriso' gets started by name "xorrisofs" then it is suitable for that. $ export MKISOFS="xorrisofs" $ growisofs -Z /dev/dvd /some/files $ growisofs -M /dev/dvd /more/files If no "xorrisofs" is available on your system, then you will have to create a link pointing to the 'xorriso' binary and tell growisofs to use it. E.g. by: $ ln -s $(which xorriso) "$HOME/xorrisofs" $ export MKISOFS="$HOME/xorrisofs" One may quit mkisofs emulation by argument "--" and make use of all 'xorriso' commands. growisofs dislikes options which start with "-o" but -outdev must be set to "-". So use "outdev" instead: $ growisofs -Z /dev/dvd -- outdev - -update_r /my/files /files $ growisofs -M /dev/dvd -- outdev - -update_r /my/files /files growisofs has excellent burn capabilities with DVD and BD. It does not emulate session history on overwritable media, though.  File: xorriso.info, Node: ExException, Next: ExTime, Prev: ExGrowisofs, Up: Examples 10.12 Adjust thresholds for verbosity, exit value and program abort =================================================================== Be quite verbose, exit 32 if severity "FAILURE" was encountered, do not abort prematurely but forcibly go on until the end of commands. $ xorriso ... \ -report_about UPDATE \ -return_with FAILURE 32 \ -abort_on NEVER \ ...  File: xorriso.info, Node: ExTime, Next: ExIncBackup, Prev: ExException, Up: Examples 10.13 Examples of input timestrings =================================== As printed by program date: 'Thu Nov 8 14:51:13 CET 2007' The same without ignored parts: 'Nov 8 14:51:13 2007' The same as expected by date: 110814512007.13 Four weeks in the future: +4w The current time: +0 Three hours ago: -3h Seconds since Jan 1 1970: =1194531416  File: xorriso.info, Node: ExIncBackup, Next: ExRestore, Prev: ExTime, Up: Examples 10.14 Incremental backup of a few directory trees ================================================= This changes the directory trees /projects and /personal_mail in the ISO image so that they become exact copies of their disk counterparts. ISO file objects get created, deleted or get their attributes adjusted accordingly. ACL, xattr, hard links and MD5 checksums will be recorded. Accelerated comparison is enabled at the expense of potentially larger backup size. Only media with the expected volume ID or blank media are accepted. Files with names matching *.o or *.swp get excluded explicitly. When done with writing the new session gets checked by its recorded MD5. $ xorriso \ -abort_on FATAL \ -for_backup -disk_dev_ino on \ -assert_volid 'PROJECTS_MAIL_*' FATAL \ -dev /dev/sr0 \ -volid PROJECTS_MAIL_"$(date '+%Y_%m_%d_%H%M%S')" \ -not_leaf '*.o' -not_leaf '*.swp' \ -update_r /home/thomas/projects /projects \ -update_r /home/thomas/personal_mail /personal_mail \ -commit -toc -check_md5 FAILURE -- -eject all To be used several times on the same medium, whenever an update of the two disk trees to the medium is desired. Begin with a blank medium and update it until the run fails gracefully due to lack of remaining space on the old one. This makes sense if the full backup leaves substantial remaining capacity on media and if the expected changes are much smaller than the full backup. To apply zisofs compression to those data files which get newly copied from the local filesystem, insert these commands immediately before -commit : -hardlinks perform_update \ -find / -type f -pending_data -exec set_filter --zisofs -- \ Commands -disk_dev_ino and -for_backup depend on stable device and inode numbers on disk. Without them, an update run may use -md5 "on" to match recorded MD5 sums against the current file content on hard disk. This is usually much faster than the default which compares both contents directly. With *mount* option *-o "sbsector="* on GNU/Linux or *-s* on FreeBSD or NetBSD it is possible to access the session trees which represent the older backup versions. With CD media, GNU/Linux mount accepts session numbers directly by its option "session=". Multi-session media and most overwritable media written by 'xorriso' can tell the sbsectors of their sessions by 'xorriso' command -toc. Used after -commit the following command prints the matching mount command for the newly written session (here for mount point /mnt): -mount_cmd "indev" "auto" "auto" /mnt Commands -mount_cmd and -mount are also able to produce the mount commands for older sessions in the table-of-content. E.g. as superuser: # osirrox -mount /dev/sr0 "volid" '*2008_12_05*' /mnt Above example produces a result similar to -root / -old-root / with mkisofs. For getting the session trees accumulated in the new sessions, let all -update commands use a common parent directory and clone it after updating is done: -update_r /home/thomas/projects /current/projects \ -update_r /home/thomas/personal_mail /current/personal_mail \ -clone /current /"$(date '+%Y_%m_%d_%H%M%S')" \ The cloned tree will have a name like /2011_02_12_155700. Sessions on multi-session media are separated by several MB of unused blocks. So with small sessions the payload capacity can become substantially lower than the overall media capacity. If the remaining space on a medium does not suffice for the next gap, the drive is supposed to close the medium automatically. *Better do not use your youngest backup for -update_r*. Have at least two media which you use alternatingly. So only older backups get endangered by the new write operation, while the newest backup is stored safely on a different medium. Always have a blank medium ready to perform a full backup in case the update attempt fails due to insufficient remaining capacity. This failure will not spoil the old medium, of course.  File: xorriso.info, Node: ExRestore, Next: ExRecovery, Prev: ExIncBackup, Up: Examples 10.15 Restore directory trees from a particular ISO session to disk =================================================================== This is an alternative to mounting the medium and using normal file operations. First check which backup sessions are on the medium: $ xorriso -outdev /dev/sr0 -toc Then enable restoring of ACL, xattr and hard links. Load the desired session and copy the file trees to disk. Avoid to create /home/thomas/restored without rwx-permission. $ xorriso -for_backup \ -load volid 'PROJECTS_MAIL_2008_06_19*' \ -indev /dev/sr0 \ -osirrox on:auto_chmod_on \ -chmod u+rwx / -- \ -extract /projects /home/thomas/restored/projects \ -extract /personal_mail /home/thomas/restored/personal_mail \ -rollback_end The final command -rollback_end prevents an error message about the altered image being discarded.  File: xorriso.info, Node: ExRecovery, Prev: ExRestore, Up: Examples 10.16 Try to retrieve blocks from a damaged medium ================================================== $ xorriso -abort_on NEVER -indev /dev/sr0 \ -check_media time_limit=1800 report=blocks_files \ data_to="$HOME"/dvd_copy sector_map="$HOME"/dvd_copy.map -- This can be repeated several times, if necessary with -eject or with other -indev drives. See the human readable part of "$HOME"/dvd_copy.map for addresses which can be used on "$HOME"/dvd_copy with mount option -o sbsector= or -s.  File: xorriso.info, Node: Files, Next: Environ, Prev: Examples, Up: Top 11 Files ******** 11.1 Program Alias Names ======================== Normal installation of 'xorriso' creates three links or copies which by their program name pre-select certain settings: *xorrisofs* starts 'xorriso' with -as mkisofs emulation. *xorrecord* starts 'xorriso' with -as cdrecord emulation. *osirrox* starts with -osirrox "on:o_excl_off" which allows further commands to copy files from ISO image to disk and to apply command -mount to one or more of the existing ISO sessions. 11.2 Startup Files ================== If not -no_rc is given as the first argument then 'xorriso' attempts on startup to read and execute lines from the following files: /etc/default/xorriso /etc/opt/xorriso/rc /etc/xorriso/xorriso.conf $HOME/.xorrisorc The files are read in the sequence given above, but none of them is required to exist. The line format is described with command -options_from_file. If mkisofs emulation was enabled by program name "xorrisofs", "mkisofs", "genisoimage", or "genisofs", then afterwards -read_mkisofsrc is performed, which reads .mkisofsrc files. See there. 11.3 Runtime control files ========================== The default setting of -check_media abort_file= is: /var/opt/xorriso/do_abort_check_media  File: xorriso.info, Node: Environ, Next: Seealso, Prev: Files, Up: Top 12 Environ ********** The following environment variables influence the program behavior: HOME is used to find startup files of xorriso and mkisofs. SOURCE_DATE_EPOCH belongs to the specs of reproducible-builds.org. It is supposed to be either undefined or to contain a decimal number which tells the seconds since january 1st 1970. If it contains a number, then it is used as time value to set the default of -volume date "uuid", sets -boot_image "any" "gpt_disk_guid=" to "volume_date_uuid", -volume_date "all_file_dates" to "set_to_mtime", and -iso_nowtime to "=$SOURCE_DATE_EPOCH". Startup files and program options can override the effect of SOURCE_DATE_EPOCH.  File: xorriso.info, Node: Seealso, Next: Bugreport, Prev: Environ, Up: Top 13 See also *********** For the mkisofs emulation of 'xorriso' xorrisofs(1) For the cdrecord emulation of 'xorriso' xorrecord(1) For mounting 'xorriso' generated ISO 9660 images (-t iso9660) mount(8) Libreadline, a comfortable input line facility readline(3) Other programs which produce ISO 9660 images mkisofs(8), genisoimage(1) Other programs which burn sessions to optical media growisofs(1), cdrecord(1), wodim(1), cdrskin(1) ACL, xattr, Linux file attributes getfacl(1), setfacl(1), getfattr(1), setfattr(1), lsattr(1), chattr(1) MD5 checksums md5sum(1) On FreeBSD some commands differ: getextattr(8), setextattr(8), md5(1)  File: xorriso.info, Node: Bugreport, Next: Legal, Prev: Seealso, Up: Top 14 Reporting bugs ***************** To report bugs, request help, or suggest enhancements for 'xorriso', please send electronic mail to the public list <bug-xorriso@gnu.org>. If more privacy is desired, mail to <scdbackup@gmx.net>. Please describe what you expect 'xorriso' to do, the program arguments or dialog commands by which you tried to achieve it, the messages of 'xorriso', and the undesirable outcome of your program run. Expect to get asked more questions before solutions can be proposed.  File: xorriso.info, Node: Legal, Next: CommandIdx, Prev: Bugreport, Up: Top 15 Author, Copyright, Credits ***************************** 15.1 Author =========== Thomas Schmitt <scdbackup@gmx.net> for libburnia-project.org 15.2 Copyright ============== Copyright (c) 2007 - 2024 Thomas Schmitt Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of 'xorriso'. If you make use of the license to derive modified versions of 'xorriso' then you are entitled to modify this text under that same license. 15.3 Credits ============ 'xorriso' is in part based on work by Vreixo Formoso who provides libisofs together with Mario Danic who also leads the libburnia team. Vladimir Serbinenko contributed the HFS+ filesystem code and related knowledge. Thanks to Andy Polyakov who invented emulated growing, to Derek Foreman and Ben Jansens who once founded libburn. Compliments towards Joerg Schilling whose cdrtools served me for ten years.  File: xorriso.info, Node: CommandIdx, Next: ConceptIdx, Prev: Legal, Up: Top 16 Alphabetic Command List ************************** �[index�] * Menu: * # starts a comment line: Scripting. (line 158) * -abort_on controls abort on error: Exception. (line 28) * -abstract_file sets abstract file name: SetWrite. (line 271) * -acl controls handling of ACLs: Loading. (line 189) * -add inserts one or more paths: Insert. (line 44) * -add_plainly inserts one or more paths: Insert. (line 68) * -alter_date sets timestamps in ISO image: Manip. (line 173) * -alter_date_r sets timestamps in ISO image: Manip. (line 208) * -append_partition adds arbitrary file after image end: Bootable. (line 470) * -application_id sets application id: SetWrite. (line 218) * -application_use sets application use field: SetWrite. (line 293) * -as emulates mkisofs or cdrecord: Emulation. (line 13) * -assert_volid rejects undesired images: Loading. (line 129) * -assess_indev_features shows filesystem features: Inquiry. (line 61) * -auto_charset learns character set from image: Loading. (line 141) * -backslash_codes enables backslash conversion: Scripting. (line 73) * -ban_stdio_write demands real drive: Loading. (line 454) * -biblio_file sets biblio file name: SetWrite. (line 277) * -blank erases media: Writing. (line 57) * -boot_image controls bootability: Bootable. (line 75) * -calm_drive reduces drive activity: Loading. (line 444) * -cd sets working directory in ISO: Navigate. (line 7) * -cdx sets working directory on disk: Navigate. (line 15) * -changes_pending overrides change status: Writing. (line 12) * -charset sets input/output character set: Charset. (line 54) * -chattr sets Linux file attributes in ISO image: Manip. (line 139) * -chattr_r sets Linux file attributes in ISO image: Manip. (line 166) * -check_md5 verifies file checksum: Verify. (line 184) * -check_md5_r verifies file tree checksums: Verify. (line 198) * -check_media reads media block by block: Verify. (line 21) * -check_media_defaults sets -check_media options: Verify. (line 40) * -chgrp sets group in ISO image: Manip. (line 49) * -chgrp_r sets group in ISO image: Manip. (line 53) * -chmod sets permissions in ISO image: Manip. (line 55) * -chmod_r sets permissions in ISO image: Manip. (line 66) * -chown sets ownership in ISO image: Manip. (line 43) * -chown_r sets ownership in ISO image: Manip. (line 47) * -clone copies ISO directory tree: Insert. (line 196) * -close controls media closing: SetWrite. (line 505) * -close_damaged closes damaged track and session: Writing. (line 209) * -close_filter_list bans filter registration: Filter. (line 50) * -commit writes pending ISO image: Writing. (line 27) * -commit_eject writes and ejects: Writing. (line 53) * -compare reports ISO/disk differences: Navigate. (line 178) * -compare_l reports ISO/disk differences: Navigate. (line 194) * -compare_r reports ISO/disk differences: Navigate. (line 190) * -compliance controls standard compliance: SetWrite. (line 62) * -concat copies ISO file content: Restore. (line 148) * -copyright_file sets copyright file name: SetWrite. (line 266) * -cpax copies files to disk: Restore. (line 128) * -cpr inserts like with cp -r: Insert. (line 175) * -cpx copies files to disk: Restore. (line 117) * -cp_clone copies ISO directory tree: Insert. (line 207) * -cp_rx copies file trees to disk: Restore. (line 131) * -cp_rx copies file trees to disk <1>: Restore. (line 139) * -cut_out inserts piece of data file or device: Insert. (line 139) * -data_cache_size adjusts read cache size: Loading. (line 470) * -dev acquires one drive for input and output: AqDrive. (line 12) * -devices gets list of drives: Inquiry. (line 7) * -device_links gets list of drives: Inquiry. (line 17) * -dialog enables dialog mode: DialogCtl. (line 7) * -disk_dev_ino fast incremental backup: Loading. (line 364) * -disk_pattern controls pattern expansion: Insert. (line 34) * -displacement compensate altered image start address: Loading. (line 102) * -drive_access control device file locking: AqDrive. (line 72) * -drive_class controls drive accessability: AqDrive. (line 43) * -du show directory size in ISO image: Navigate. (line 86) * -dummy controls write simulation: SetWrite. (line 497) * -dus show directory size in ISO image: Navigate. (line 89) * -dusx show directory size on disk: Navigate. (line 96) * -dux show directory size on disk: Navigate. (line 92) * -dvd_obs set write block size and end alignment: SetWrite. (line 421) * -early_stdio_test classifies stdio drives: Loading. (line 458) * -ecma119_map names w/o Rock Ridge, Joliet: Loading. (line 337) * -eject ejects drive tray: Writing. (line 50) * -end writes pending session and ends program: Scripting. (line 153) * -errfile_log logs problematic disk files: Scripting. (line 118) * -error_behavior controls error workarounds: Exception. (line 93) * -external_filter registers data filter: Filter. (line 20) * -external_filter unregisters data filter: Filter. (line 47) * -extract copies file tree to disk: Restore. (line 90) * -extract_boot_images copies boot equipment to disk: Restore. (line 176) * -extract_cut copies file piece to disk: Restore. (line 108) * -extract_l copies files to disk: Restore. (line 104) * -extract_single copies file to disk: Restore. (line 101) * -file_name_limit curbs length of file names: Loading. (line 384) * -file_size_limit limits data file size: SetInsert. (line 7) * -find traverses and alters ISO tree: CmdFind. (line 7) * -findx traverses disk tree: Navigate. (line 99) * -follow softlinks and mount points: SetInsert. (line 77) * -format formats media: Writing. (line 87) * -for_backup acl,xattr,hardlinks,md5,lfa_flags: Loading. (line 312) * -fs sets size of fifo: SetWrite. (line 500) * -genisoimage_completion completion of genisoimage options: Emulation. (line 166) * -getfacl shows ACL in ISO image: Navigate. (line 60) * -getfacl_r shows ACL in ISO image: Navigate. (line 66) * -getfattr shows xattr in ISO image: Navigate. (line 69) * -getfattr_r shows xattr in ISO image: Navigate. (line 75) * -gid sets global ownership: SetWrite. (line 314) * -grow_blindly overrides next writeable address: AqDrive. (line 112) * -hardlinks controls handling of hard links: Loading. (line 152) * -help prints help text: Scripting. (line 19) * -hfsplus enables production of HFS+ partition: SetWrite. (line 14) * -hide excludes file names from directory trees: Manip. (line 211) * -history brings text into readline history: Scripting. (line 44) * -indev acquires a drive for input: AqDrive. (line 23) * -in_charset sets input character set: Loading. (line 136) * -iso_nowtime fixed "now" time for ISO 9660 objects: Loading. (line 358) * -iso_rr_pattern controls pattern expansion: Manip. (line 10) * -jigdo clears JTE or or adds parameter to JTE: Jigdo. (line 37) * -joliet enables production of Joliet tree: SetWrite. (line 10) * -joliet_map Joliet names: Loading. (line 350) * -launch_frontend starts frontend program at pipes: Frontend. (line 141) * -lfa_flags controls handling of Linux file attributes: Loading. (line 207) * -list_arg_sorting prints sorting order of -x: ArgSort. (line 26) * -list_delimiter replaces '--': Scripting. (line 57) * -list_extras lists compile time extra features: Scripting. (line 24) * -list_formats lists available formats: Writing. (line 128) * -list_profiles lists supported media: Writing. (line 163) * -list_speeds lists available write speeds: Writing. (line 139) * -lns creates ISO symbolic link: Insert. (line 192) * -load addresses a particular session as input: Loading. (line 54) * -local_charset sets terminal character set: Charset. (line 57) * -logfile logs output channels to file: Frontend. (line 19) * -ls lists files in ISO image: Navigate. (line 24) * -lsattr shows Linux file attributes in ISO image: Navigate. (line 78) * -lsd lists files in ISO image: Navigate. (line 31) * -lsdl lists files in ISO image: Navigate. (line 42) * -lsdlx lists files on disk: Navigate. (line 57) * -lsdx lists files on disk: Navigate. (line 51) * -lsl lists files in ISO image: Navigate. (line 34) * -lslx lists files on disk: Navigate. (line 54) * -lsx lists files on disk: Navigate. (line 45) * -map inserts path: Insert. (line 89) * -map_l inserts paths from disk file: Insert. (line 96) * -map_single inserts path: Insert. (line 93) * -mark sets synchronizing message: Frontend. (line 23) * -md5 controls handling of MD5 sums: Loading. (line 281) * -mkdir creates ISO directory: Insert. (line 188) * -modesty_on_drive keep drive buffer hungry: SetWrite. (line 440) * -mount issues mount command for ISO session: Restore. (line 204) * -mount_cmd composes mount command line: Inquiry. (line 95) * -mount_cmd controls mount command: Inquiry. (line 113) * -msg_op perform operations on program messages: Frontend. (line 27) * -mv renames files in ISO image: Manip. (line 37) * -mv renames single file in ISO image: Manip. (line 31) * -named_pipe_loop enters EOF resistant dialog: Frontend. (line 119) * -not_leaf sets exclusion pattern: SetInsert. (line 67) * -not_list sets exclusions from disk file: SetInsert. (line 71) * -not_mgt controls file exclusion: SetInsert. (line 22) * -not_paths sets absolute exclusion paths: SetInsert. (line 54) * -no_rc disables startup files: Scripting. (line 7) * -options_from_file reads commands from file: Scripting. (line 12) * -osirrox enables ISO-to-disk copying: Restore. (line 25) * -outdev acquires a drive for output: AqDrive. (line 29) * -out_charset sets output character set: SetWrite. (line 306) * -overwrite enables overwriting in ISO: SetInsert. (line 140) * -pacifier controls pacifier text form: Emulation. (line 184) * -padding sets amount or mode of image padding: SetWrite. (line 528) * -page set terminal geometry: DialogCtl. (line 18) * -paste_in copies file into disk file: Restore. (line 142) * -pathspecs sets meaning of = with -add: SetInsert. (line 124) * -path_list inserts paths from disk file: Insert. (line 81) * -pkt_output consolidates text output: Frontend. (line 7) * -preparer_id sets preparer id: SetWrite. (line 282) * -print prints result text line: Scripting. (line 104) * -print_info prints message text line: Scripting. (line 106) * -print_mark prints synchronizing text line: Scripting. (line 108) * -print_size predicts image size: Inquiry. (line 134) * -prog sets program name: Frontend. (line 176) * -prog_help prints help text: Frontend. (line 178) * -prompt prompts for enter key: Scripting. (line 112) * -publisher sets publisher id: SetWrite. (line 213) * -pvd_info shows image id strings: Inquiry. (line 156) * -pwd tells working directory in ISO: Navigate. (line 19) * -pwdx tells working directory on disk: Navigate. (line 21) * -quoted_not_list sets exclusions: SetInsert. (line 74) * -quoted_path_list inserts paths from disk file: Insert. (line 85) * -read_fs filesystem type for image loading: Loading. (line 120) * -read_mkisofsrc searches and reads .mkisofsrc file: Emulation. (line 155) * -read_speed set read speed: Loading. (line 11) * -reassure enables confirmation question: DialogCtl. (line 29) * -report_about controls verbosity: Exception. (line 54) * -report_el_torito shows Boot Catalog: Inquiry. (line 166) * -report_system_area shows MBR, GPT, and alike: Inquiry. (line 201) * -return_with controls exit value: Exception. (line 39) * -rm deletes files from ISO image: Manip. (line 20) * -rmdir deletes ISO directory: Manip. (line 29) * -rm_r deletes trees from ISO image: Manip. (line 26) * -rockridge disables production of Rock Ridge info: SetWrite. (line 57) * -rollback discards pending changes: Writing. (line 9) * -rollback_end ends program without writing: Scripting. (line 156) * -rom_toc_scan searches for sessions: Loading. (line 416) * -rr_reloc_dir sets name of relocation directory: SetWrite. (line 171) * -scdbackup_tag enables scdbackup checksum tag: Emulation. (line 197) * -scsi_dev_family choose Linux device file type: AqDrive. (line 95) * -scsi_log reports SCSI commands: Scripting. (line 145) * -session_log logs written sessions: Scripting. (line 136) * -session_string composes session info line: Inquiry. (line 122) * -setfacl sets ACL in ISO image: Manip. (line 68) * -setfacl_list sets ACL in ISO image: Manip. (line 94) * -setfacl_r sets ACL in ISO image: Manip. (line 92) * -setfattr sets xattr in ISO image: Manip. (line 103) * -setfattr_list sets xattr in ISO image: Manip. (line 120) * -setfattr_r sets xattr in ISO image: Manip. (line 118) * -set_filter applies filter to file: Filter. (line 58) * -set_filter_r applies filter to file tree: Filter. (line 84) * -show_stream shows data source and filters: Navigate. (line 198) * -show_stream_r shows data source and filters: Navigate. (line 216) * -sh_style_result makes results look more like shell: Scripting. (line 63) * -signal_handling controls handling of system signals: Exception. (line 67) * -sleep waits for a given time span: Scripting. (line 115) * -speed set write speed: SetWrite. (line 392) * -split_size enables large file splitting: SetInsert. (line 154) * -status shows current settings: Scripting. (line 46) * -status_history_max curbs -status history: Scripting. (line 54) * -stdio_sync controls stdio buffer: SetWrite. (line 490) * -stream_recording controls defect management: SetWrite. (line 410) * -system_id sets system id: SetWrite. (line 226) * -tell_media_space reports free space: Inquiry. (line 146) * -temp_mem_limit curbs memory consumption: Scripting. (line 98) * -toc shows list of sessions: Inquiry. (line 27) * -toc_info_type shows list of sessions: Inquiry. (line 49) * -toc_of shows list of sessions: Inquiry. (line 41) * -truncate_overwritable activates older session: Writing. (line 167) * -uid sets global ownership: SetWrite. (line 311) * -update inserts path if different: Insert. (line 100) * -update_l inserts paths if different: Insert. (line 120) * -update_l inserts paths if different <1>: Insert. (line 128) * -update_li inserts paths if different: Insert. (line 124) * -update_r inserts paths if different: Insert. (line 110) * -use_immed_bit controls use of Immed bit: SetWrite. (line 478) * -use_readline enables readline for dialog: DialogCtl. (line 26) * -version prints help text: Scripting. (line 22) * -volid sets volume id: SetWrite. (line 189) * -volset_id sets volume set id: SetWrite. (line 209) * -volume_date sets volume timestamp: SetWrite. (line 232) * -write_type chooses TAO or SAO/DAO: SetWrite. (line 521) * -x enables automatic execution order of arguments: ArgSort. (line 16) * -xattr controls handling of xattr (EA): Loading. (line 196) * -zisofs controls zisofs production: SetWrite. (line 317)  File: xorriso.info, Node: ConceptIdx, Prev: CommandIdx, Up: Top 17 Alphabetic List of Concepts and Objects ****************************************** �[index�] * Menu: * ACL, control handling, -acl: Loading. (line 189) * ACL, set in ISO image, -setfacl: Manip. (line 68) * ACL, set in ISO image, -setfacl_list: Manip. (line 94) * ACL, set in ISO image, -setfacl_r: Manip. (line 92) * ACL, show in ISO image, -getfacl: Navigate. (line 60) * ACL, show in ISO image, -getfacl_r: Navigate. (line 66) * ACL, _definition: Extras. (line 50) * APM block size: Bootable. (line 461) * APM, _definition: Extras. (line 42) * Appendable media, _definition: Media. (line 43) * Appended Filesystem Image, -append_partition: Bootable. (line 470) * Appended partition, in APM: Bootable. (line 326) * Appended partition, in MBR or GPT: Bootable. (line 317) * Appended partitions, GPT: Bootable. (line 499) * Appended partitions, MBR: Bootable. (line 480) * Automatic execution order, of arguments, -x: ArgSort. (line 16) * Backslash Interpretation, _definition: Processing. (line 57) * Backup, enable fast incremental, -disk_dev_ino: Loading. (line 364) * Backup, enable features, -for_backup: Loading. (line 312) * Backup, scdbackup checksum tag, -scdbackup: Emulation. (line 197) * Blank media, _definition: Media. (line 34) * Blank, format, Immed bit, -use_immed_bit: SetWrite. (line 478) * Blind growing, _definition: Methods. (line 41) * Bootability, control, -boot_image: Bootable. (line 75) * Bugs, reporting: Bugreport. (line 6) * cdrecord, Emulation: Emulation. (line 120) * Character Set, for input, -in_charset: Loading. (line 136) * Character Set, for input/output, -charset: Charset. (line 54) * Character Set, for output, -out_charset: SetWrite. (line 306) * Character set, learn from image, -auto_charset: Loading. (line 141) * Character Set, of terminal, -local_charset: Charset. (line 57) * Character Set, _definition: Charset. (line 6) * CHRP partition, _definition: Bootable. (line 333) * Closed media, _definition: Media. (line 49) * Comment, #: Scripting. (line 158) * Control, signal handling, -signal_handling: Exception. (line 67) * Create, new ISO image, _definition: Methods. (line 7) * Cylinder alignment, _definition: Bootable. (line 377) * Cylinder size, _definition: Bootable. (line 362) * Damaged track and session, close, -close_damaged: Writing. (line 209) * DEC Alpha SRM boot sector, production: Bootable. (line 447) * Delete, from ISO image, -rm: Manip. (line 20) * Delete, from ISO image, -rm_r: Manip. (line 26) * Delete, ISO directory, -rmdir: Manip. (line 29) * Device file locking, -drive_access: AqDrive. (line 72) * Dialog, bring text into history, -history: Scripting. (line 44) * Dialog, confirmation question, -reassure: DialogCtl. (line 29) * Dialog, enable dialog mode, -dialog: DialogCtl. (line 7) * Dialog, EOF resistant, -named_pipe_loop: Frontend. (line 119) * Dialog, line editing, -use_readline: DialogCtl. (line 26) * Dialog, terminal geometry, -page: DialogCtl. (line 18) * Directories, copy, -cp_clone: Insert. (line 207) * Directory, copy, -clone: Insert. (line 196) * Directory, create, -mkdir: Insert. (line 188) * Directory, delete, -rmdir: Manip. (line 29) * disk_path, _definition: Insert. (line 6) * Drive, accessability, -drive_class: AqDrive. (line 43) * Drive, classify stdio, -early_stdio_test: Loading. (line 458) * Drive, demand real MMC, -ban_stdio_write: Loading. (line 454) * Drive, eject tray, -eject: Writing. (line 50) * Drive, for input and output, -dev: AqDrive. (line 12) * Drive, for input, -indev: AqDrive. (line 23) * Drive, for output, -outdev: AqDrive. (line 29) * Drive, get drive list, -devices: Inquiry. (line 7) * Drive, get drive list, -device_links: Inquiry. (line 17) * Drive, list supported media, -list_profiles: Writing. (line 163) * Drive, reduce activity, -calm_drive: Loading. (line 444) * Drive, report SCSI commands, -scsi_log: Scripting. (line 145) * Drive, write and eject, -commit_eject: Writing. (line 53) * Drive, _definition: Drives. (line 6) * EA, _definition: Extras. (line 66) * ECMA-119, _definition: Model. (line 6) * EFI system partition, _definition: Bootable. (line 342) * El Torito, _definition: Extras. (line 19) * Emulation, -as: Emulation. (line 13) * Emulation, .mkisofsrc, -read_mkisofsrc: Emulation. (line 155) * Emulation, cdrecord, -as: Emulation. (line 120) * Emulation, mkisofs, -as: Emulation. (line 17) * Emulation, options completion, -genisoimage_completion: Emulation. (line 166) * Emulation, pacifier form, -pacifier: Emulation. (line 184) * Examples: Examples. (line 6) * extattr, _definition: Extras. (line 66) * File content, copy, -concat: Restore. (line 148) * File names, curb length, -file_name_limit: Loading. (line 384) * File names, if Joliet is loaded: Loading. (line 350) * File names, if neither Rock Ridge nor Joliet: Loading. (line 337) * Filesytem features, show, -assess_indev_features: Inquiry. (line 61) * Filter, apply to file tree, -set_filter_r: Filter. (line 84) * Filter, apply to file, -set_filter: Filter. (line 58) * Filter, ban registration, -close_filter_list: Filter. (line 50) * Filter, register, -external_filter: Filter. (line 20) * Filter, show chain, -show_stream: Navigate. (line 198) * Filter, show chains of tree, -show_stream_r: Navigate. (line 216) * Filter, unregister, -unregister_filter: Filter. (line 47) * Filter, zisofs parameters, -zisofs: SetWrite. (line 317) * Filter, _definition: Filter. (line 6) * Frontend program, start at pipes, -launch_frontend: Frontend. (line 141) * GPT Legacy BIOS bootable flag, set for ISO: Bootable. (line 399) * GPT read-only flag, do not set for ISO: Bootable. (line 404) * GPT, control GUID, -boot_image gpt_disk_guid=: Bootable. (line 260) * GPT, _definition: Extras. (line 39) * Group, global in ISO image, -gid: SetWrite. (line 314) * Group, in ISO image, -chgrp: Manip. (line 49) * Group, in ISO image, -chgrp_r: Manip. (line 53) * Growing, _definition: Methods. (line 20) * Hard links, control handling, -hardlinks: Loading. (line 152) * HFS+ allocation block size: Bootable. (line 458) * HFS+ serial number: Bootable. (line 455) * hidden, set in ISO image, -hide: Manip. (line 211) * HP-PA boot sector, production: Bootable. (line 430) * Image reading, cache size, -data_cache_size: Loading. (line 470) * Image, demand volume ID, -assert_volid: Loading. (line 129) * Image, discard pending changes, -rollback: Writing. (line 9) * Image, filesystem to load, -read_fs: Loading. (line 120) * Image, override change status, -changes_pending: Writing. (line 12) * Image, set abstract file name, -abstract_file: SetWrite. (line 271) * Image, set application id, -application_id: SetWrite. (line 218) * Image, set application iuse field, -application_use: SetWrite. (line 293) * Image, set biblio file name, -biblio_file: SetWrite. (line 277) * Image, set copyright file name, -copyright_file: SetWrite. (line 266) * Image, set preparer id, -preparer_id: SetWrite. (line 282) * Image, set publisher id, -publisher: SetWrite. (line 213) * Image, set system id, -system_id: SetWrite. (line 226) * Image, set volume id, -volid: SetWrite. (line 189) * Image, set volume set id, -volset_id: SetWrite. (line 209) * Image, set volume timestamp, -volume_date: SetWrite. (line 232) * Image, show Boot Catalog: Inquiry. (line 166) * Image, show id strings, -pvd_info: Inquiry. (line 156) * Image, show MBR, GPT, and alike, -pvd_info: Inquiry. (line 201) * Image, _definition: Model. (line 9) * Input Character Set, _definition: Charset. (line 25) * Insert, enable overwriting, -overwrite: SetInsert. (line 140) * Insert, file exclusion absolute, -not_paths: SetInsert. (line 54) * Insert, file exclusion from file, -not_list: SetInsert. (line 71) * Insert, file exclusion pattern, -not_leaf: SetInsert. (line 67) * Insert, file exclusion, -not_mgt: SetInsert. (line 22) * Insert, file exclusion, -quoted_not_list: SetInsert. (line 74) * Insert, if different, -update: Insert. (line 100) * Insert, if different, -update_l: Insert. (line 120) * Insert, if different, -update_li: Insert. (line 124) * Insert, if different, -update_lxi: Insert. (line 128) * Insert, if different, -update_r: Insert. (line 110) * Insert, large file splitting, -split_size: SetInsert. (line 154) * Insert, limit data file size, -file_size_limit: SetInsert. (line 7) * Insert, links or mount points, -follow: SetInsert. (line 77) * Insert, meaning of = with -add, -pathspecs: SetInsert. (line 124) * Insert, non-dashed arguments, -add_plainly: Insert. (line 68) * Insert, path, -map: Insert. (line 89) * Insert, path, -map_single: Insert. (line 93) * Insert, paths from disk file, -map_l: Insert. (line 96) * Insert, paths from disk file, -path_list: Insert. (line 81) * Insert, paths from disk file, -quoted_path_list: Insert. (line 85) * Insert, paths, -cpr: Insert. (line 175) * Insert, pathspecs, -add: Insert. (line 44) * Insert, piece of data file or device, -cut_out: Insert. (line 139) * Interval reader for system area and partitions: Bootable. (line 32) * ISO 9660, _definition: Model. (line 6) * iso_rr_path, _definition: Insert. (line 7) * Jigdo Template Extraction, -jigdo: Jigdo. (line 37) * Jigdo Template Extraction, _definition: Jigdo. (line 6) * LBA, _definition: Drives. (line 17) * libisofs, fixed "now" time: Loading. (line 358) * Linux device type, -scsi_dev_family: AqDrive. (line 95) * Linux file attributes, control handling, -lfa_flags: Loading. (line 207) * Linux file attributes, set in ISO image, -chattr: Manip. (line 139) * Linux file attributes, set in ISO image, -chattr_r: Manip. (line 166) * Linux file attributes, show in ISO image, -lsattr: Navigate. (line 78) * Linux file attributes, _definition: Extras. (line 84) * List delimiter, _definition: Processing. (line 13) * Local Character Set, _definition: Charset. (line 11) * MBR bootable/active flag, enforce: Bootable. (line 388) * MBR, set, -boot_image system_area=: Bootable. (line 227) * MBR, _definition: Extras. (line 27) * MD5, control handling, -md5: Loading. (line 281) * Media, erase, -blank: Writing. (line 57) * Media, format, -format: Writing. (line 87) * Media, list formats, -list_formats: Writing. (line 128) * Media, list write speeds, -list_speeds: Writing. (line 139) * MIPS boot file, activation: Bootable. (line 409) * mkisofs, Emulation: Emulation. (line 17) * Modifying, _definition: Methods. (line 28) * Multi-session media, _definition: Media. (line 7) * Multi-session, _definition: Model. (line 18) * Navigate, directory size in ISO image, -du: Navigate. (line 86) * Navigate, directory size in ISO image, -dus: Navigate. (line 89) * Navigate, directory size in on disk, -dusx: Navigate. (line 96) * Navigate, directory size in on disk, -dux: Navigate. (line 92) * Navigate, list disk files, -lsdlx: Navigate. (line 57) * Navigate, list disk files, -lsdx: Navigate. (line 51) * Navigate, list disk files, -lslx: Navigate. (line 54) * Navigate, list disk files, -lsx: Navigate. (line 45) * Navigate, list ISO files, -ls: Navigate. (line 24) * Navigate, list ISO files, -lsd: Navigate. (line 31) * Navigate, list ISO files, -lsdl: Navigate. (line 42) * Navigate, list ISO files, -lsl: Navigate. (line 34) * Navigate, set disk working directory, -cdx: Navigate. (line 15) * Navigate, set ISO working directory, -cd: Navigate. (line 7) * Navigate, tell disk working directory, -pwdx: Navigate. (line 21) * Navigate, tell ISO working directory, -pwd: Navigate. (line 19) * Next writeable address, -grow_blindly: AqDrive. (line 112) * Older session, activate, -truncate_overwritable: Writing. (line 167) * Output Character Set, _definition: Charset. (line 26) * Overwritable media, _definition: Media. (line 14) * Ownership, global in ISO image, -uid: SetWrite. (line 311) * Ownership, in ISO image, -chown: Manip. (line 43) * Ownership, in ISO image, -chown_r: Manip. (line 47) * Partition offset, _definition: Bootable. (line 352) * Partition table, _definition: Bootable. (line 300) * Pathspec, _definition: SetInsert. (line 126) * Pattern expansion, for disk paths, -disk_pattern: Insert. (line 34) * Pattern expansion, for ISO paths, -iso_rr_pattern: Manip. (line 10) * Pattern expansion, _definition: Processing. (line 29) * Permissions, in ISO image, -chmod: Manip. (line 55) * Permissions, in ISO image, -chmod_r: Manip. (line 66) * PReP partition, _definition: Bootable. (line 337) * Problems, reporting: Bugreport. (line 6) * Process, consolidate text output, -pkt_output: Frontend. (line 7) * Process, control abort on error, -abort_on: Exception. (line 28) * Process, control exit value, -return_with: Exception. (line 39) * Process, control verbosity, -report_about: Exception. (line 54) * Process, disable startup files, -no_rc: Scripting. (line 7) * Process, end program and write, -end: Scripting. (line 153) * Process, end program, no writing, -rollback_end: Scripting. (line 156) * Process, error workarounds, -error_behavior: Exception. (line 93) * Process, log output channels to file, -logfile: Frontend. (line 19) * Process, read command file, -options_from_file: Scripting. (line 12) * Process, set synchronizing message, -mark: Frontend. (line 23) * Program messages, perform operations, -msg_op: Frontend. (line 27) * Program, backslash conversion, -backslash_codes: Scripting. (line 73) * Program, curb memory, -temp_mem_limit: Scripting. (line 98) * Program, end and write, -end: Scripting. (line 153) * Program, end without writing, -rollback_end: Scripting. (line 156) * Program, list extra features, -list_extras: Scripting. (line 24) * Program, print help text, -help: Scripting. (line 19) * Program, print help text, -prog_help: Frontend. (line 178) * Program, print message text line, -print_info: Scripting. (line 106) * Program, print result text line, -print: Scripting. (line 104) * Program, print synchronizing text line, -print_mark: Scripting. (line 108) * Program, print version, -version: Scripting. (line 22) * Program, prompt for enter key, -prompt: Scripting. (line 112) * Program, replace --, -list_delimiter: Scripting. (line 57) * Program, set name, -prog: Frontend. (line 176) * Program, show current settings, -status: Scripting. (line 46) * Program, status history, -status_history_max: Scripting. (line 54) * Program, wait a time span, -sleep: Scripting. (line 115) * Quoted input, _definition: Processing. (line 51) * Read, set speed, -read_speed: Loading. (line 11) * Recovery, retrieve blocks, -check_media: Verify. (line 21) * Relocation directory, set name, -rr_reloc_dir: SetWrite. (line 171) * Rename, in ISO image, -move: Manip. (line 31) * Rename, in ISO image, -mv: Manip. (line 37) * Restore, copy boot equipment to disk, -extract_boot_images: Restore. (line 176) * Restore, copy file into disk file, -paste_in: Restore. (line 142) * Restore, copy file piece to disk, -extract_cut: Restore. (line 108) * Restore, copy file to disk, -extract_single: Restore. (line 101) * Restore, copy file tree to disk, -extract: Restore. (line 90) * Restore, copy file trees to disk, -cp_rx: Restore. (line 131) * Restore, copy file trees to disk, -cp_rx <1>: Restore. (line 139) * Restore, copy files to disk, -cpax: Restore. (line 128) * Restore, copy files to disk, -cpx: Restore. (line 117) * Restore, copy files to disk, -extract_l: Restore. (line 104) * Restore, enable ISO-to-disk, -osirrox: Restore. (line 25) * Result layout, more shell-like, -sh_style_result: Scripting. (line 63) * Rock Ridge, _definition: Extras. (line 6) * Session, altered start address, -displacement: Loading. (line 102) * Session, info string, -session_string: Inquiry. (line 122) * Session, issue mount command, -mount: Restore. (line 204) * Session, log when written, -session_log: Scripting. (line 136) * Session, mount command line, -mount_cmd: Inquiry. (line 95) * Session, mount parameters, -mount_opts: Inquiry. (line 113) * Session, select as input, -load: Loading. (line 54) * Session, _definition: Model. (line 6) * Sorting order, for -x, -list_arg_sorting: ArgSort. (line 26) * SUN Disk Label, production: Bootable. (line 420) * SUN SPARC boot images, activation: Bootable. (line 517) * Symbolic link, create, -lns: Insert. (line 192) * System area, _definition: Bootable. (line 227) * Table-of-content, choose info to show, -toc_info_type: Inquiry. (line 49) * Table-of-content, search sessions, -rom_toc_scan: Loading. (line 416) * Table-of-content, show parts of, -toc_of: Inquiry. (line 41) * Table-of-content, show, -toc: Inquiry. (line 27) * Timestamps, set in ISO image, -alter_date: Manip. (line 173) * Timestamps, set in ISO image, -alter_date_r: Manip. (line 208) * Tree, disk, traverse, -findx: Navigate. (line 99) * Tree, ISO, traverse and alter, -find: CmdFind. (line 7) * Unsuitable media states, _definition: Media. (line 25) * UTF-16, for Joliet paths, -compliance: SetWrite. (line 114) * Verify, check blocks, -check_media: Verify. (line 21) * Verify, compare ISO and disk file, -compare: Navigate. (line 178) * Verify, compare ISO and disk tree, -compare_r: Navigate. (line 190) * Verify, compare ISO and disk, -compare_l: Navigate. (line 194) * Verify, file checksum, -check_md5: Verify. (line 184) * Verify, file tree checksums, -check_md5_r: Verify. (line 198) * Verify, preset -check_media, -check_media_defaults: Verify. (line 40) * Write, block size and end alignment, -dvd_obs: SetWrite. (line 421) * Write, bootability, -boot_image: Bootable. (line 75) * Write, buffer syncing, -stdio_sync: SetWrite. (line 490) * Write, close media, -close: SetWrite. (line 505) * Write, compliance to specs, -compliance: SetWrite. (line 62) * Write, defect management, -stream_recording: SetWrite. (line 410) * Write, disable Rock Ridge, -rockridge: SetWrite. (line 57) * Write, drive buffer, -modesty_on_drive: SetWrite. (line 440) * Write, enable HFS+, -hfsplus: SetWrite. (line 14) * Write, enable Joliet, -joliet: SetWrite. (line 10) * Write, fifo size, -fs: SetWrite. (line 500) * Write, free space, -tell_media_space: Inquiry. (line 146) * Write, log problematic disk files, -errfile_log: Scripting. (line 118) * Write, log written sessions, -session_log: Scripting. (line 136) * Write, padding image, -padding: SetWrite. (line 528) * Write, pending ISO image, -commit: Writing. (line 27) * Write, predict image size, -print_size: Inquiry. (line 134) * Write, set speed, -speed: SetWrite. (line 392) * Write, simulation, -dummy: SetWrite. (line 497) * Write, TAO or SAO/DAO, -write_type: SetWrite. (line 521) * xattr, control handling, -xattr: Loading. (line 196) * xattr, set in ISO image, -setfattr: Manip. (line 103) * xattr, set in ISO image, -setfattr_list: Manip. (line 120) * xattr, set in ISO image, -setfattr_r: Manip. (line 118) * xattr, show in ISO image, -getfattr: Navigate. (line 69) * xattr, show in ISO image, -getfattr_r: Navigate. (line 75) * xattr, _definition: Extras. (line 66)  Tag Table: Node: Top415 Node: Overview1405 Node: Model3441 Node: Media6344 Node: Methods9265 Node: Drives11851 Node: Extras15803 Node: Processing21108 Node: Dialog24945 Node: Commands26634 Node: ArgSort28311 Node: AqDrive29805 Node: Loading36962 Node: Insert65464 Node: SetInsert77659 Node: Manip87895 Node: CmdFind100015 Node: Filter121337 Node: Writing125959 Node: SetWrite138453 Node: Bootable169728 Node: Jigdo201010 Node: Charset206013 Node: Exception209342 Node: DialogCtl215531 Node: Inquiry218133 Node: Navigate230698 Node: Verify241915 Node: Restore253064 Node: Emulation265271 Node: Scripting276847 Node: Frontend284741 Node: Examples294367 Node: ExDevices295545 Node: ExCreate296206 Node: ExDialog297506 Node: ExGrowing298777 Node: ExModifying299586 Node: ExBootable300096 Node: ExCharset300651 Node: ExPseudo301547 Node: ExCdrecord302474 Node: ExMkisofs302794 Node: ExGrowisofs304691 Node: ExException305844 Node: ExTime306302 Node: ExIncBackup306760 Node: ExRestore310786 Node: ExRecovery311732 Node: Files312304 Node: Environ313638 Node: Seealso314386 Node: Bugreport315150 Node: Legal315741 Node: CommandIdx316753 Node: ConceptIdx335227  End Tag Table \input texinfo @c -*-texinfo-*- @c %**start of header @setfilename xorriso.info @settitle GNU xorriso 1.5.7 @c %**end of header @c @c man-ignore-lines begin @dircategory Archiving @direntry * Xorriso: (xorriso). Burns ISO 9660 on CD, DVD, BD. @end direntry @c man-ignore-lines end @c @c Notes about embedded man page: @c This texinfo code contains the necessary info to produce a man page @c which resembles much the version of xorriso.1 from which this code @c was originally derived in march 2010. @c One can produce the man page by applying the following rules: @c The first line gets discarded. @c Line start "@c man " will become "", the remainder is put out unaltered. @c Lines "@*" will be converted to ".br" @c "@c man-ignore-lines N" will discard N following lines. @c "@c man-ignore-lines begin" discards all following lines @c up to "@c man-ignore-lines end". @c Line blocks of "@menu" "@end menu" will be discarded. @c "@item word words" becomes "\fBword\fR words". @c @b{...}, @command{...}, @dfn{...}, @emph{...}, @strong{...} @c get mapped to \fB...\fR . @c @abbr{...}, @code{...}, @file{...}, @i{...}, @option{...}, @r{...}, @c @ref{...}, @samp{...},@var{...}, get mapped to ... . @c @ref{...}, @xref{...} get mapped to empty text. @c @email{...} gets mapped to <...> . @c Mapped {...} content is subject to the rules except {...} mapping. @c @minus{} will become "-". @c @@ , @{, @} will get stripped of their first @. @c Other lines which begin by "@" will be discarded. @c In lines not stemming from "@c man", "\" becomes "\\" @c "-" which are not preceded by an uneven number of "\" will get @c prepended one "\". @c @c @c man .\" Hey, EMACS: -*- nroff -*- @c man .\" @c man .\" IMPORTANT NOTE: @c man .\" @c man .\" The original of this file is kept in xorriso/xorriso.texi @c man .\" This here was generated by program xorriso/make_xorriso_1 @c man .\" @c man .\" @c man .\" First parameter, NAME, should be all caps @c man .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection @c man .\" other parameters are allowed: see man(7), man(1) @c man .TH XORRISO 1 "Version 1.5.7, Sep 07, 2024" @c man .\" Please adjust this date whenever revising the manpage. @c man .\" @c man .\" Some roff macros, for reference: @c man .\" .nh disable hyphenation @c man .\" .hy enable hyphenation @c man .\" .ad l left justify @c man .\" .ad b justify to both left and right margins @c man .\" .nf disable filling @c man .\" .fi enable filling @c man .\" .br insert line break @c man .\" .sp <n> insert n+1 empty lines @c man .\" for manpage-specific macros, see man(7) @c man .nh @c man-ignore-lines begin @copying xorriso - creates, loads, manipulates and writes ISO 9660 filesystem images with Rock Ridge extensions. Copyright @copyright{} 2007 - 2024 Thomas Schmitt @quotation Permission is granted to distribute this text freely. @end quotation @end copying @c man-ignore-lines end @titlepage @title Manual of GNU xorriso 1.5.7 @author Thomas Schmitt @page @vskip 0pt plus 1filll @insertcopying @end titlepage @contents @ifnottex @node Top @top GNU xorriso 1.5.7 @c man-ignore-lines 1 @c man .SH NAME xorriso - creates, loads, manipulates and writes ISO 9660 filesystem images with Rock Ridge extensions. @end ifnottex @menu * Overview:: Overview * Model:: Session model * Media:: Media types and states * Methods:: Creating, Growing, Modifying, Blind Growing * Drives:: Libburn drives * Extras:: Rock Ridge, POSIX, X/Open, El Torito, ACL, xattr * Processing:: Command processing * Dialog:: Dialog, Readline, Result pager * Commands:: Reference of commands * Examples:: Examples * Files:: Files * Environ:: Environment * Seealso:: See also * Bugreport:: Reporting bugs * Legal:: Author, Copyright, Credits * CommandIdx:: Alphabetic Command List * ConceptIdx:: Alphabetic List of Concepts and Objects @end menu @node Overview, Model, Top, Top @chapter Overview @c man .SH SYNOPSIS @c man .B xorriso @c man .RI [ settings | actions ] @c man .br @c man .SH DESCRIPTION @c man .PP @command{xorriso} is a program which copies file objects from POSIX compliant filesystems into Rock Ridge enhanced ISO 9660 filesystems and performs session-wise manipulation of such filesystems. It can load the management information of existing ISO images and it writes the session results to optical media or to filesystem objects. @* Vice versa @command{xorriso} is able to copy file objects out of ISO 9660 filesystems. @c man .PP @sp 1 A special property of @command{xorriso} is that it needs neither an external ISO 9660 formatter program nor an external burn program for CD, DVD or BD but rather incorporates the libraries of libburnia-project.org . @c man .SS @section Features @c man .B Overview of features: @* Operates on an existing ISO image or creates a new one. @* Copies files from disk filesystem into the ISO image. @* Copies files from ISO image to disk filesystem (see osirrox). @* Renames or deletes file objects in the ISO image. @* Changes file properties in the ISO image. @* Updates ISO subtrees incrementally to match given disk subtrees. @* Writes result either as completely new image or as add-on session to optical media or filesystem objects. @* Can activate ISOLINUX and GRUB boot images via El Torito and MBR. @* Can perform multi-session tasks as emulation of mkisofs and cdrecord. @* Can record and restore hard links and ACL. @* Content may get zisofs compressed or filtered by external processes. @* Can issue commands to mount older sessions on GNU/Linux or FreeBSD. @* Can check media for damages and copy readable blocks to disk. @* Can attach MD5 checksums to each data file and the whole session. @* Scans for optical drives, blanks re-usable optical media. @* Reads its instructions from command line arguments, dialog, and files. @* Provides navigation commands for interactive ISO image manipulation. @* Adjustable thresholds for abort, exit value, and problem reporting. @* @sp 1 @c man .sp 1 Note that @command{xorriso} does not write audio CDs and that it does not produce UDF filesystems which are specified for official video DVD or BD. @c man .SS @c man .B General information paragraphs: @c man .br @c man Session model @c man .br @c man Media types and states @c man .br @c man Creating, Growing, Modifying, Blind Growing @c man .br @c man Libburn drives @c man .br @c man Rock Ridge, POSIX, X/Open, El Torito, ACL, xattr @c man .br @c man Command processing @c man .br @c man Dialog, Readline, Result pager @c man .sp 1 @c man Maybe you first want to have a look at section EXAMPLES near the end of @c man this text before reading the next few hundred lines of background information. @c man .SS @node Model, Media, Overview, Top @chapter Session model @c man \fBSession model:\fR @c man .br @cindex Session, _definition @cindex ISO 9660, _definition @cindex ECMA-119, _definition Unlike other filesystems, @strong{ISO 9660} (aka @strong{ECMA-119}) is not intended for read-write operation but rather for being generated in a single sweep and being written to media as a @strong{session}. @* @cindex Image, _definition The data content of the session is called filesystem @strong{image}. @c man .PP @sp 1 The written image in its session can then be mounted by the operating system for being used read-only. GNU/Linux is able to mount ISO images from block devices, which may represent optical media, other media or via a loop device even from regular disk files. FreeBSD mounts ISO images from devices that represent arbitrary media or from regular disk files. @c man .PP @sp 1 @cindex Multi-session, _definition This session usage model has been extended on CD media by the concept of @strong{multi-session} , which adds information to the CD and gives the mount programs of the operating systems the addresses of the entry points of each session. The mount programs recognize block devices which represent CD media and will by default mount the image in the last session. @* This session usually contains an updated directory tree for the whole medium which governs the data contents in all recorded sessions. So in the view of the mount program all sessions of a particular medium together form a single filesystem image. @* Adding a session to an existing ISO image is in this text referred as @strong{growing}. @* The multi-session model of the MMC standard does not apply to all media types. But program growisofs by Andy Polyakov showed how to extend this functionality to overwritable media or disk files which carry valid ISO 9660 filesystems. @c man .PP @sp 1 @command{xorriso} provides growing as well as an own method named @strong{modifying} which produces a completely new ISO image from the old one and the modifications. See paragraph Creating, Growing, Modifying, Blind Growing below. @c man .PP @sp 1 @command{xorriso} adopts the concept of multi-session by loading an image directory tree if present, by offering to manipulate it by several actions, and by writing the new image to the target medium. @c man .br The first session of a @command{xorriso} run begins by the definition of the input drive with the ISO image or by the definition of an output drive. The session ends by command -commit which triggers writing. A -commit is done automatically when the program ends regularly. @c man .PP @sp 1 After -commit a new session begins with the freshly written one as input. A new input drive can only be chosen as long as the loaded ISO image was not altered. Pending alteration can be revoked by command -rollback. @c man .PP @sp 1 Writing a session to the target is supposed to be very expensive in terms of time and of consumed space on appendable or write-once media. Therefore all intended manipulations of a particular ISO image should be done in a single session. But in principle it is possible to store intermediate states and to continue with image manipulations. @c man .SS @node Media, Methods, Model, Top @chapter Media types and states @c man .B Media types and states: There are two families of media in the MMC standard: @* @cindex Multi-session media, _definition @strong{Multi-session media} are CD-R, CD-RW, DVD-R, DVD+R, DVD+R/DL, BD-R, and unformatted DVD-RW. These media provide a table of content which describes their existing sessions. See command @strong{-toc}. @* Similar to multi-session media are DVD-R DL and minimally blanked DVD-RW. They record only a single session of which the size must be known in advance. @command{xorriso} will write onto them only if command -close is set to "on". @* @cindex Overwritable media, _definition @strong{Overwritable media} are DVD-RAM, DVD+RW, BD-RE, and formatted DVD-RW. They offer random write access but do not provide information about their session history. If they contain one or more ISO 9660 sessions and if the first session was written by @command{xorriso}, then a table of content can be emulated. Else only a single overall session will be visible. @* DVD-RW media can be formatted by -format "full". They can be made unformatted by -blank "deformat". @* Regular files and block devices are handled as overwritable media. Pipes and other writeable file types are handled as blank multi-session media. @* @cindex Unsuitable media states, _definition The program growisofs formats by default BD-R to be pseudo-overwritable (POW). xorriso will classify them as @* Media current: is unsuitable , is POW formatted @* and will refuse to write to them or to obtain multi-session information from them. @c man .PP @sp 1 These media can assume several states in which they offer different capabilities. @* @sp 1 @cindex Blank media, _definition @strong{Blank} media can be written from scratch. They contain no ISO image suitable for @command{xorriso}. @* Blank is the state of newly purchased optical media. With used CD-RW and DVD-RW it can be achieved by action -blank "as_needed". Overwritable media are considered blank if they are new or if they have been marked as blank by @command{xorriso}. Action -blank "as_needed" can be used to do this marking on overwritable media, or to apply mandatory formatting to new media if necessary. @* @sp 1 @cindex Appendable media, _definition @strong{Appendable} media accept further sessions. Either they are MMC multi-session media in appendable state, or they are overwritable media which contain an ISO image suitable for @command{xorriso}. @* Appendable is the state after writing a session with command -close off. @* @sp 1 @cindex Closed media, _definition @strong{Closed} media cannot be written. They may contain an ISO image suitable for @command{xorriso}. @* Closed is the state of DVD-ROM media and of multi-session media which were written with command -close on. If the drive is read-only hardware then it will probably show any media as closed CD-ROM or DVD-ROM. @* Overwritable media assume this state in such read-only drives or if they contain unrecognizable data in the first 32 data blocks. @* Read-only drives may or may not show session histories of multi-session media. Often only the first and the last session are visible. Sometimes not even that. Command -rom_toc_scan might or might not help in such cases. @c man .SS @node Methods, Drives, Media, Top @chapter Creating, Growing, Modifying, Blind Growing: @c man .B Creating, Growing, Modifying, Blind Growing: @* @cindex Create, new ISO image, _definition A new empty ISO image gets @strong{created} if there is no input drive with a valid ISO 9660 image when the first time an output drive is defined. This is achieved by command -dev on blank media or by command -outdev on media in any state. @* The new empty image can be populated with directories and files. Before it can be written, the medium in the output drive must get into blank state if it was not blank already. @c man .PP @sp 1 If there is a input drive with a valid ISO image, then this image gets loaded as foundation for manipulations and extension. The constellation of input and output drive determines which write method will be used. They have quite different capabilities and constraints. @c man .PP @sp 1 @cindex Growing, _definition The method of @strong{growing} adds new data to the existing data on the medium. These data comprise of new file content and they override the existing ISO 9660 + Rock Ridge directory tree. It is possible to hide files from previous sessions but they still exist on the medium and with many types of optical media it is quite easy to recover them by mounting older sessions. @* Growing is achieved by command -dev. @c man .PP @sp 1 @cindex Modifying, _definition The write method of @strong{modifying} produces compact filesystem images with no outdated files or directory trees. Modifying can write its images to target media which are completely unsuitable for multi-session operations. E.g. DVD-RW which were treated with -blank deformat_quickest, DVD-R DL, named pipes, character devices, sockets. On the other hand modified sessions cannot be written to appendable media but to blank media only. @* So for this method one needs either two optical drives or has to work with filesystem objects as source and/or target medium. @* Modifying takes place if input drive and output drive are not the same and if command -grow_blindly is set to its default "off". This is achieved by commands -indev and -outdev. @c man .PP @sp 1 @cindex Blind growing, _definition If command -grow_blindly is set to a non-negative number and if -indev and -outdev are both set to different drives, then @strong{blind growing} is performed. It produces an add-on session which is ready for being written to the given block address. This is the usage model of @* mkisofs -M $indev -C $msc1,$msc2 -o $outdev @* which gives much room for wrong parameter combinations and should thus only be employed if a strict distinction between ISO formatter @command{xorriso} and the burn program is desired. -C $msc1,$msc2 is equivalent to: @* -load sbsector $msc1 -grow_blindly $msc2 @c man .SS @node Drives, Extras, Methods, Top @chapter Libburn drives @c man .B Libburn drives: @c man .br @cindex Drive, _definition Input drive, i.e. source of an existing or empty ISO image, can be any random access readable libburn drive: optical media with readable data, blank optical media, regular files, block devices. @* Output drive, i.e. target for writing, can be any libburn drive. Some drive types do not support the method of growing but only the methods of modifying and blind growing. They all are suitable for newly created images. @c man .PP @sp 1 All drive file objects have to offer rw-permission to the user of @command{xorriso}. Even those which will not be usable for reading an ISO image. @* @cindex LBA, _definition With any type of drive object, the data are considered to be organized in blocks of 2 KiB. Access happens in terms of Logical Block Address (@strong{LBA}) which gives the number of a particular data block. @c man .PP @sp 1 MMC compliant (i.e. optical) drives on GNU/Linux usually get addressed by the path of their block device or of their generic character device. E.g. @* -dev /dev/sr0 @* -dev /dev/hdc @* -dev /dev/sg2 @* By default xorriso will try to map the given address to /dev/hd* and /dev/sr*. The command -scsi_dev_family can redirect the mapping from sr to scd or sg. The latter does not suffer from the concurrency problems which plagued /dev/sr of Linux kernels since version 3 up to 5.5. But it does not yield the same addresses which are used by mount(8) or by open(2) for read(2). @* On FreeBSD the device files have names like @* -dev /dev/cd0 @* On NetBSD: @* -dev /dev/rcd0d @* On OpenSolaris: @* -dev /dev/rdsk/c4t0d0s2 @* Get a list of accessible drives by command @* -device_links @* It might be necessary to do this as @strong{superuser} in order to see all drives and to then allow rw-access for the intended users. Consider to bundle the authorized users in a group like old "floppy". @c man .PP @sp 1 Filesystem objects of nearly any type can be addressed by prefix "stdio:" and their path in the filesystem. E.g.: @* -dev stdio:/dev/sdc @* The default setting of -drive_class allows the user to address files outside the /dev tree without that prefix. E.g.: @* -dev /tmp/pseudo_drive @* If path leads to a regular file or to a block device then the emulated drive is random access readable and can be used for the method of growing if it already contains a valid ISO 9660 image. Any other file type is not readable via "stdio:" and can only be used as target for the method of modifying or blind growing. Non-existing paths in existing directories are handled as empty regular files. @c man .PP @sp 1 A very special kind of pseudo drive are open file descriptors. They are depicted by "stdio:/dev/fd/" and descriptor number (see man 2 open). @* Addresses "-" or "stdio:/dev/fd/1" depict standard output, which normally is the output channel for result texts. To prevent a fatal intermingling of ISO image and text messages, all result texts get redirected to stderr if -*dev "-" or "stdio:/dev/fd/1" is among the start arguments of the program. @* Standard output is currently suitable for creating one session per program run without dialog. Use in other situations is discouraged and several restrictions apply: @* It is not allowed to use standard output as pseudo drive if it was not among the start arguments. Do not try to fool this ban via backdoor addresses to stdout. @* If stdout is used as drive, then -use_readline is permanently disabled. Use of backdoors can cause severe memory and/or tty corruption. @c man .PP @sp 1 Be aware that especially the superuser can write into any accessible file or device by using its path with the "stdio:" prefix. By default any address in the /dev tree without prefix "stdio:" will work only if it leads to a MMC drive. @* One may use command @strong{-ban_stdio_write} to surely prevent this risk and to restrict drive usage to MMC drives. @* One may prepend "mmc:" to a path to surely disallow any automatic "stdio:". @c man .br By command -drive_class one may ban certain paths or allow access without prefix "stdio:" to other paths. @c man .SS @node Extras, Processing, Drives, Top @chapter Rock Ridge, POSIX, X/Open, El Torito, ACL, xattr @c man .B Rock Ridge, POSIX, X/Open, El Torito, ACL, xattr: @c man .br @cindex Rock Ridge, _definition @strong{Rock Ridge} is the name of a set of additional information which enhance an ISO 9660 filesystem so that it can represent a POSIX compliant filesystem with ownership, access permissions, symbolic links, and other attributes. @* This is what @command{xorriso} uses for a decent representation of the disk files within the ISO image. @command{xorriso} produces Rock Ridge information by default. It is strongly discouraged to disable this feature. @c man .PP @sp 1 @command{xorriso} is not named "porriso" because POSIX only guarantees 14 characters of filename length. It is the X/Open System Interface standard XSI which demands a file name length of up to 255 characters and paths of up to 1024 characters. Rock Ridge fulfills this demand. @c man .PP @sp 1 @cindex El Torito, _definition An @strong{El Torito} boot record points the BIOS bootstrapping facility to one or more boot images, which are binary program files stored in the ISO image. The content of the boot image files is not in the scope of El Torito. @* Most bootable GNU/Linux CDs are equipped with ISOLINUX or GRUB boot images. @command{xorriso} is able to create or maintain an El Torito object which makes such an image bootable. For details see command -boot_image. @* @cindex MBR, _definition It is possible to make ISO images bootable from USB stick or other hard-disk-like media. Several options install a @strong{MBR} (Master Boot Record), It may get adjusted according to the needs of the intended boot firmware and the involved boot loaders, e.g. GRUB2 or ISOLINUX. A MBR contains boot code and a partition table. The new MBR of a follow-up session can get in effect only on overwritable media. @* MBR is read by PC-BIOS when booting from USB stick or hard disk, and by PowerPC CHRP or PReP when booting. An MBR partition with type 0xee indicates the presence of GPT. @* Emulation -as mkisofs supports the example options out of the ISOLINUX wiki, the options used in GRUB script grub-mkrescue, and the example in the FreeBSD AvgLiveCD wiki. @* @cindex GPT, _definition A @strong{GPT} (GUID Partition Table) marks partitions in a more modern way. It is read by EFI when booting from USB stick or hard disk, and may be used for finding and mounting a HFS+ partition inside the ISO image. @* @cindex APM, _definition An @strong{APM} (Apple Partition Map) marks the HFS+ partition. It is read by Macs for booting and for mounting. @* MBR, GPT and APM are combinable. APM occupies the first 8 bytes of MBR boot code. All three do not hamper El Torito booting from CDROM. @* There is support for further facilities: MIPS Big Endian (SGI), MIPS Little Endian (DEC), SUN SPARC, HP-PA. Those are mutually not combinable and also not combinable with MBR, GPT, or APM. @* @c man .PP @sp 1 @cindex ACL, _definition @strong{ACL} are an advanced way of controlling access permissions to file objects. Neither ISO 9660 nor Rock Ridge specify a way to record ACLs. So libisofs has introduced a standard conformant extension named AAIP for that purpose. It uses this extension if enabled by command @strong{-acl}. @* AAIP enhanced images are supposed to be mountable normally, but one cannot expect that the mounted filesystem will show and respect the ACLs. For now, only @command{xorriso} is able to retrieve those ACLs. It can bring them into effect when files get restored to an ACL enabled file system or it can print them in a format suitable for tool setfacl. @* Files with ACL show as group permissions the setting of entry "mask::" if that entry exists. Nevertheless the non-listed group members get handled according to entry "group::". When removing ACL from a file, @command{xorriso} brings "group::" into effect. @* Recording and restoring of ACLs from and to local files works currently only on GNU/Linux and FreeBSD. @c man .PP @sp 1 @cindex xattr, _definition @cindex EA, _definition @cindex extattr, _definition @strong{xattr} (aka EA, or extattr) are pairs of name and value which can be attached to file objects. AAIP is able to represent them and @command{xorriso} can record and restore them. @* But be aware that pairs with names of non-user namespaces are not necessarily portable between operating systems and not even between filesystems. Only those which begin with "user.", like "user.x" or "user.whatever", can unconditionally be expected to be appropriate on other machines and disks. Processing of other xattr may need administrator privileges. @* Name has to be a 0 terminated string. Value may be any array of bytes which does not exceed the size of 4095 bytes. xattr processing happens only if it is enabled by command @strong{-xattr}. @* As with ACL, currently only @command{xorriso} is able to retrieve xattr from AAIP enhanced images, to restore them to xattr capable file systems, or to print them. @* Recording and restoring of xattr from and to local files works currently only on GNU/Linux and FreeBSD, where they are known as extattr. @c man .PP @sp 1 @cindex Linux file attributes, _definition @strong{Linux file attributes} are binary flags which can be set by program chattr(1) and listed by program lsattr(1). See their man pages and the definitions FS_*_FL in Linux header file <linux/fs.h>. Not all defined flags get reported by lsattr and accepted by chattr, but their number grew over the years. @* @command{xorriso} records the flags of disk files if enabled by command @strong{-lfa_flags}. Its command -lsattr lists 22 flags the same way as the program lsattr does. They can be set by xorriso command -chattr and can be enabled by -lfa_flags for restoring when their files get restored to disk. @c man .SS @node Processing, Dialog, Extras, Top @chapter Command processing @c man .B Command processing: @c man .br Commands are either actions which happen immediately or settings which influence following actions. So their sequence does matter, unless they are given as program arguments and command @strong{-x} is among them. @* The list of all current settings can be inquired by command -status "long". Command -status "short" lists a handful of fundamental settings and all settings which are not default at program start. @c man .PP @sp 1 @cindex List delimiter, _definition Commands consist of a command word, followed by zero or more parameter words. If the list of parameter words is of variable length (indicated by "[...]" or "[***]") then it must be terminated by either the @strong{list delimiter}, occur at the end of the argument list, or occur at the end of an input line. @c man .PP @sp 1 At program start the list delimiter is the string "@minus{}@minus{}". This may be changed with the -list_delimiter command in order to allow "@minus{}@minus{}" as parameter in a variable length list. However, it is advised to reset the delimiter to "@minus{}@minus{}" immediately afterwards. @* For brevity the list delimiter is referred as "@minus{}@minus{}" throughout this text. @* The list delimiter is silently ignored if it appears after the parameters of a command with a fixed list length. It is handled as normal text if it appears among the parameters of such a command. @c man .PP @sp 1 @cindex Pattern expansion, _definition @strong{Pattern expansion} converts a list of pattern words into a list of existing file addresses. Unmatched pattern words will appear unaltered in that result list. @* Pattern matching supports the usual shell parser wildcards '*' '?' '[xyz]' and respects '/' as the path separator, which may only be matched literally. @* Pattern expansion is a property of some particular commands and not a general feature. It is controlled by commands -iso_rr_pattern and -disk_pattern. Commands which use pattern expansion all have variable parameter lists which are specified in this text by "[***]" rather than "[...]". @* Some other commands perform pattern matching unconditionally. @c man .PP @sp 1 Command and parameter words are either read from the program arguments, where one argument is one word, or from quoted input lines where words are recognized similar to the quotation rules of a shell parser. @* @command{xorriso} is not a shell, although it might appear so at first glimpse. Be aware that the interaction of quotation marks and pattern symbols like "*" differs from the usual shell parsers. In @command{xorriso}, a quotation mark does not make a pattern symbol literal. @c man .PP @sp 1 @cindex Quoted input, _definition @strong{Quoted input} converts whitespace-separated text into words. The double quotation mark " and the single quotation mark ' can be used to enclose whitespace and make it part of words (e.g. of file names). Each mark type can enclose the marks of the other type. A trailing backslash \ outside quotations or an open quotation cause the next input line to be appended. @* @cindex Backslash Interpretation, _definition Quoted input accepts any 8-bit character except NUL (0) as the content of the quotes. Nevertheless it can be cumbersome for the user to produce those characters directly. Therefore quoted input and program arguments offer optional @strong{Backslash Interpretation} which can represent all 8-bit characters except NUL (0) via backslash codes as in $'...' of bash. @* This is not enabled by default. See command -backslash_codes. @c man .PP @sp 1 When the program starts then it first looks for argument -no_rc. If this is not present then it looks for its startup files and reads their content as command input lines. Then it interprets the program arguments as commands and parameters. Finally it enters dialog mode if command -dialog "on" has been executed by this point. @c man .PP @sp 1 The program ends either by command -end, or by the end of program arguments if dialog mode has not been enabled at that point, or by a problem event which triggers the threshold of command -abort_on. @c man .SS @node Dialog, Commands, Processing, Top @chapter Dialog, Readline, Result pager @c man .B Dialog, Readline, Result pager: @c man .br Dialog mode prompts for a quoted input line, parses it into words, and performs them as commands with their parameters. It provides assisting services to make dialog more comfortable. @c man .PP @sp 1 Readline is an enhancement for the input line. You may already know it from the bash shell. Whether it is available in @command{xorriso} depends on the availability of package readline-dev at the time when @command{xorriso} was built from its sourcecode. @* Readline lets the user move the cursor over the text in the line by help of the Left and the Right arrow keys. Text may be inserted at the cursor position. The Delete key removes the character under the cursor. Up and Down arrow keys navigate through the history of previous input lines. @* @c man-ignore-lines 1 See info readline @c man See man readline for more info about libreadline. @c man .PP @sp 1 Command -page activates a built-in result text pager which may be convenient in dialog mode. After an action has output the given number of terminal lines, the pager prompts the user for a line of input. @* An empty line lets @command{xorriso} resume work until the next page is output. @* The single character "@@" disables paging for the current action. @* "@@@@@@", "x", "q", "X", or "Q" request that the current action aborts and suppress further result output. @* Any other line input will be interpreted as new dialog line. The current action is requested to abort. Afterwards, the input line is executed. @c man .PP @sp 1 Some actions apply paging to their info output, too. @* The request to abort may or may not be obeyed by the current action. All actions try to abort as soon as possible. @node Commands, Examples, Dialog, Top @chapter Commands @c man .br @c man .SH OPTIONS @c man .br All command words are shown with a leading dash although this dash is not mandatory for the command to be recognized. Nevertheless within command -as the dashes of the emulated commands are mandatory. @* Normally any number of leading dashes is ignored with command words and inner dashes are interpreted as underscores. @menu * ArgSort:: Execution order of program arguments * AqDrive:: Acquiring source and target drive * Loading:: Influencing the behavior of image loading * Insert:: Inserting files into ISO image * SetInsert:: Settings for file insertion * Manip:: File manipulations * CmdFind:: Tree traversal command -find * Filter:: Filters for data file content * Writing:: Writing the result, drive control * SetWrite:: Settings for result writing * Bootable:: Bootable ISO images * Jigdo:: Jigdo Template Extraction * Charset:: Character sets * Exception:: Exception processing * DialogCtl:: Dialog mode control * Inquiry:: Drive and media related inquiry actions * Navigate:: Navigation in ISO image and disk filesystem * Verify:: Evaluation of readability and recovery * Restore:: osirrox ISO-to-disk restore commands * Emulation:: Command compatibility emulations (cdrtools) * Scripting:: Scripting, dialog and program control features * Frontend:: Support for frontend programs via stdin and stdout @end menu @c man .TP @node ArgSort, AqDrive, Commands, Commands @section Execution order of program arguments @c man .B Execution order of program arguments: @c man .PP By default the program arguments of a xorriso run are interpreted as a sequence of commands which get performed exactly in the given order. This requires the user to write commands for desired settings before the commands which shall be influenced by those settings. @* Many other programs support program arguments in an arbitrary ordering and perform settings and actions in a sequence at their own discretion. xorriso provides an option to enable such a behavior at the cost of loss of expressivity. @table @asis @sp 1 @c man .TP @item -x @kindex -x enables automatic execution order of arguments @cindex Automatic execution order, of arguments, -x Enable automatic sorting of program arguments into a sequence that (most likely) is sensible. This command may be given at any position among the commands which are handed over as program arguments. @* Note: It works only if it is given as program argument and with a single dash (i.e. "-x"). It will not work in startup files, nor with -options_from_file, nor in dialog mode, nor as "x" and finally not as "@minus{}@minus{}x". It affects only the commands given as program arguments. @c man .TP @item -list_arg_sorting @kindex -list_arg_sorting prints sorting order of -x @cindex Sorting order, for -x, -list_arg_sorting List all xorriso commands in the order which applies if command -x is in effect. @* This list may also be helpful without -x for a user who ponders over the sequence in which to put commands. Deviations from the listed sorting order may well make sense, though. @end table @c man .PP @c man .TP @node AqDrive, Loading, ArgSort, Commands @section Acquiring source and target drive @c man .B Acquiring source and target drive: @c man .PP The effect of acquiring a drive may depend on several commands in the next paragraph "Influencing the behavior of image loading". If desired, their enabling commands have to be performed before the commands which acquire the drive. @table @asis @sp 1 @c man .TP @item -dev address @kindex -dev acquires one drive for input and output @cindex Drive, for input and output, -dev Set input and output drive to the same address and load an ISO image if it is present. If there is no ISO image then create a blank one. Set the image expansion method to growing. @* This is only allowed as long as no changes are pending in the currently loaded ISO image. If changes are pending, then one has to perform -commit or -rollback first. @* Special address string "-" means standard output, to which several restrictions apply. See above paragraph "Libburn drives". @* An empty address string "" gives up the current device without acquiring a new one. @c man .TP @item -indev address @kindex -indev acquires a drive for input @cindex Drive, for input, -indev Set input drive and load an ISO image if present. If the new input drive differs from -outdev then switch from growing to modifying or to blind growing. It depends on the setting of -grow_blindly which of both gets activated. The same rules and restrictions apply as with -dev. @c man .TP @item -outdev address @kindex -outdev acquires a drive for output @cindex Drive, for output, -outdev Set output drive and if it differs from the input drive then switch from growing to modifying or to blind growing. Unlike -dev and -indev this action does not load a new ISO image. So it can be performed even if there are pending changes. @* -outdev can be performed without previous -dev or -indev. In that case an empty ISO image with no changes pending is created. It can either be populated by help of -map, -add et.al. or it can be discarded silently if -dev or -indev are performed afterwards. @* Special address string "-" means standard output, to which several restrictions apply. See above paragraph "Libburn drives". @* An empty address string "" gives up the current output drive without acquiring a new one. No writing is possible without an output drive. @c man .TP @item -drive_class "harmless"|"banned"|"caution"|"clear_list" disk_pattern @kindex -drive_class controls drive accessability @cindex Drive, accessability, -drive_class Add a drive path pattern to one of the safety lists or make those lists empty. There are three lists defined which get tested in the following sequence: @* If a drive address path matches the "harmless" list then the drive will be accepted. If it is not a MMC device then the prefix "stdio:" will be prepended automatically. This list is empty by default. @* Else if the path matches the "banned" list then the drive will not be accepted by @command{xorriso} but rather lead to a FAILURE event. This list is empty by default. @* Else if the path matches the "caution" list and if it is not a MMC device, then its address must have the prefix "stdio:" or it will be rejected. This list has by default one entry: "/dev". @* If a drive path matches no list then it is considered "harmless". By default these are all paths which do not begin with directory "/dev". @* A path matches a list if one of its parent paths or itself matches a list entry. Address prefix "stdio:" or "mmc:" will be ignored when testing for matches. @* By pseudo-class "clear_list" and pseudo-patterns "banned", "caution", "harmless", or "all", the lists may be made empty. @* E.g.: -drive_class clear_list banned @* One will normally define the -drive_class lists in one of the @command{xorriso} Startup Files. @* Note: This is not a security feature but rather a bumper for the superuser to prevent inadverted mishaps. For reliably blocking access to a device file you have to deny its rw-permissions in the filesystem. @c man .TP @item -drive_access "exclusive"|"shared":"unrestricted"|"readonly" @kindex -drive_access control device file locking @cindex Device file locking, -drive_access Control whether device file locking mechanisms shall be used when acquiring a drive, and whether status or content of the medium in the drive may be altered. Useful and most harmless are the setting "shared:readonly" and the default setting "exclusive:unrestricted". @* "exclusive" enables tests and locks when acquiring the drive. It depends on the operating system which locking mechanisms get applied, if any. On GNU/Linux it is open(O_EXCL). On FreeBSD it is flock(LOCK_EX). @* "shared" disables the use of these mechanisms to become able to acquire drives which are mounted, or opened by some process, or guarded by /dev/pktcdvd*. @* "unrestricted" enables all technically appropriate operations on an acquired drive. "shared:unrestricted" risks to get own burn runs spoiled by other processes or to vice versa spoil activities of such processes. So use "exclusive:unrestricted" unless you know for sure that "shared" is safe. @* "readonly" disables operations which might surprise a co-user of the drive. For -outdev these are formatting, blanking, writing, ejecting. For -indev this is ejecting. Be aware that even reading and drive status inquiries can disturb an ongoing burn run on CD-R[W] and DVD-R[W]. @c man .TP @item -scsi_dev_family "default"|"sr"|"scd"|"sg" @kindex -scsi_dev_family choose Linux device file type @cindex Linux device type, -scsi_dev_family GNU/Linux specific: @* By default, xorriso tries to map Linux drive addresses to /dev/sr* before they get opened for operating the drive. This coordinates well with other use cases of optical drives, like mount(8). But since year 2010 all /dev/sr* share a global lock which allows only one drive to process an SCSI command while all others have to wait for its completion. This yields awful throughput if more than one drive is writing or reading simultaneously. The global lock is not applied to device files /dev/sg* and also not if the xorriso drive address is prepended by "stdio:". @* So for simultaneous burn runs on modern GNU/Linux it is advisable to perform -scsi_dev_family "sg" before any -dev, -indev, or -outdev. The drive addresses may then well be given as /dev/sr* but will nevertheless get used as the matching /dev/sg*. @* If you decide so, consider to put the command into a global startup file like /etc/opt/xorriso/rc. @c man .TP @item -grow_blindly "off"|predicted_nwa @kindex -grow_blindly overrides next writeable address @cindex Next writeable address, -grow_blindly If predicted_nwa is a non-negative number then perform blind growing rather than modifying if -indev and -outdev are set to different drives. "off" or "-1" switch to modifying, which is the default. @* predicted_nwa is the block address where the add-on session of blind growing will finally end up. It is the responsibility of the user to ensure this final position and the presence of the older sessions. Else the overall ISO image will not be mountable or will produce read errors when accessing file content. @command{xorriso} will write the session to the address as obtained from examining -outdev and not necessarily to predicted_nwa. @* During a run of blind growing, the input drive is given up before output begins. The output drive is given up when writing is done. @end table @c man .TP @c man .B Influencing the behavior of image loading: @node Loading, Insert, AqDrive, Commands @section Influencing the behavior of image loading @c man .PP The following commands should normally be performed before loading an image by acquiring an input drive. In rare cases it is desirable to activate them only after image loading. @table @asis @sp 1 @c man .TP @item -read_speed code|number[k|m|c|d|b] @kindex -read_speed set read speed @cindex Read, set speed, -read_speed Set the speed for reading. Default is "none", which avoids to send a speed setting command to the drive before reading begins. @* Further special speed codes are: @* "max" (or "0") selects maximum speed as announced by the drive. @* "min" (or "-1") selects minimum speed as announced by the drive. @* Speed can be given in media dependent numbers or as a desired throughput per second in MMC compliant kB (= 1000) or MB (= 1000 kB). Media x-speed factor can be set explicitly by "c" for CD, "d" for DVD, "b" for BD, "x" is optional. @* Example speeds: @* 706k = 706kB/s = 4c = 4xCD @* 5540k = 5540kB/s = 4d = 4xDVD @* If there is no hint about the speed unit attached, then the medium in the -indev will decide. Default unit is CD = 176.4k. @* Depending on the drive, the reported read speeds can be deceivingly low or high. Therefore "min" cannot become higher than 1x speed of the involved medium type. Read speed "max" cannot become lower than 52xCD, 24xDVD, or 20xBD, depending on the medium type. @* MMC drives usually activate their own idea of speed and take the speed value given by the burn program only as hint for their own decision. Friendly drives adjust their constant angular velocity so that the desired speed is reached at the outer rim of the medium. But often there is only the choice between very slow and very loud. @* Sometimes no speed setting is obeyed at all, but speed is adjusted to the demand frequency of the reading program. So xorriso offers to set an additional software enforced limit by prefix "soft_force:". The program will take care not to read faster than the soft_force speed. This may be combined with setting the drive speed to a higher value. Setting "soft_force:0" disables this feature. @* "soft_force:" tries to correct in subsequent waiting periods lost or surplus time of up to 0.25 seconds. This smoothens the overall data stream but also enables short times of higher speed to compensate short times of low speed. Prefix "soft_corr:" sets this hindsight span by giving a number of microseconds. Not more than 1 billion = 1000 seconds. Very short times can cause speed deviations, because systematic inaccuracies of the waiting function cannot be compensated. @* Examples (combinable): @* -read_speed 6xBD @* -read_speed soft_force:4xBD -read_speed soft_corr:100000 @c man .TP @item -load entity id @kindex -load addresses a particular session as input @cindex Session, select as input, -load Load a particular (possibly outdated) ISO session from -dev or -indev. Usually all available sessions are shown with command -toc. @* entity depicts the kind of addressing. id depicts the particular address. The following entities are defined: @* "auto" with any id addresses the last session in -toc. This is the default. @* "session" with id being a number as of a line "ISO session", column "Idx". @* "track" with id being a number as of a line "ISO track", column "Idx". @* "lba" or "sbsector" with a number as of a line "ISO ...", column "sbsector". @* "volid" with a search pattern for a text as in the column "Volume Id" of a -toc line "ISO ...". @* "at_time" with a time string as described with command -alter_date chooses the last session or track where the modification timestamp matches the given time within the same second. @* "before" with a time string chooses the last session or track of which the timestamp is older than the given time. But it does not match an entity with exactly the given time. @* "not_after" is like "before" but also matches an entity with exactly the given time. @* "after" with a time string chooses the first session or track of which the timestamp is younger than the given time. But it does not match an entity with exactly the given time. @* "not_before" is like "after" but also matches an entity with exactly the given time. @* Comparison of time entities is done with an accuracy of one second. I.e. the centiseconds of ISO 9660 timestamps are ignored. If -toc_info_type is set to "creation_time", then the comparison is done against the creation timestamp of track or session rather than the modification timestamp. The output of -pvd_info shows both timestamps as "Creation Time:" and "Modif. Time :". @* The time comparisons pick first and last matching sessions. If the sequence of timestamps on a drive is not chronologically ascending, the picks might not be the best choice. In this case look at the output of -toc_info_type "mtime" -toc and choose the desired entity by "session", "track", or "sbsector". @* Addressing a non-existing entity or one which does not represent an ISO image will either abandon -indev or at least lead to a blank image. @* If an input drive is set at the moment when -load is executed, then the addressed ISO image is loaded immediately. Else, the setting will be pending until the next -dev or -indev. After the image has been loaded once, the setting is valid for -rollback until next -dev or -indev, where it will be reset to "auto". @c man .TP @item -displacement [-]lba @kindex -displacement compensate altered image start address @cindex Session, altered start address, -displacement Compensate a displacement of the image versus the start address for which the image was prepared. This affects only loading of ISO images and reading of their files. The multi-session method of growing is not allowed as long as -displacement is non-zero. I.e. -indev and -outdev must be different. The displacement gets reset to 0 before the drive gets re-acquired after writing. @* Examples: @* If a track of a CD starts at block 123456 and gets copied to a disk file where it begins at block 0, then this copy can be loaded with -displacement -123456 @* If an ISO image was written onto a partition with offset of 640000 blocks of 512 bytes, then it can be loaded from the base device by -load sbsector 160000 -displacement 160000 @* (If the partition start address is not divisible by 4, then you will have to employ a loop device instead.) @* In both cases, the ISO sessions should be self contained, i.e. not add-on sessions to an ISO image outside their track or partition. @c man .TP @item -read_fs "any"|"norock"|"nojoliet"|"ecma119" @kindex -read_fs filesystem type for image loading @cindex Image, filesystem to load, -read_fs Specify which kind of filesystem tree to load if present. If the wish cannot be fulfilled, then ECMA-119 names are loaded and converted according to -ecma119_map. @* "any" first tries to read Rock Ridge. If not present, Joliet is tried. @* "norock" does not try Rock Ridge. @* "nojoliet" does not try Joliet. @* "ecma119" tries neither Rock Ridge nor Joliet. @c man .TP @item -assert_volid pattern severity @kindex -assert_volid rejects undesired images @cindex Image, demand volume ID, -assert_volid Refuse to load ISO images with volume IDs which do not match the given search pattern. When refusing an image, give up the input drive and issue an event of the given severity (like FAILURE, see -abort_on). An empty search pattern accepts any image. @* This command does not hamper the creation of an empty image from blank input media and does not discard an already loaded image. @c man .TP @item -in_charset character_set_name @kindex -in_charset sets input character set @cindex Character Set, for input, -in_charset Set the character set from which to convert file names when loading an image. See paragraph "Character sets" for more explanations. When loading the written image after -commit the setting of -out_charset will be copied to -in_charset. @c man .TP @item -auto_charset "on"|"off" @kindex -auto_charset learns character set from image @cindex Character set, learn from image, -auto_charset Enable or disable recording and interpretation of the output character set name in an xattr attribute of the image root directory. If enabled and if a recorded character set name is found, then this name will be used as name of the input character set when reading an image. @* Note that the default output charset is the local character set of the terminal where @command{xorriso} runs. Before attributing this local character set to the produced ISO image, check whether the terminal properly displays all intended filenames, especially exotic national characters. @c man .TP @item -hardlinks mode[:mode...] @kindex -hardlinks controls handling of hard links @cindex Hard links, control handling, -hardlinks Enable or disable loading and recording of hardlink relations. @* In default mode "off", iso_rr files lose their inode numbers at image load time. Each iso_rr file object which has no inode number at image generation time will get a new unique inode number if -compliance is set to new_rr. @* Mode "on" preserves inode numbers from the loaded image if such numbers were recorded. When committing a session it searches for families of iso_rr files which stem from the same disk file, have identical content filtering and have identical properties. The family members all get the same inode number. Whether these numbers are respected at mount time depends on the operating system. @* Command -lsl displays hardlink counts if "lsl_count" is enabled. This can slow down the command substantially after changes to the ISO image have been made. Therefore the default is "no_lsl_count". @* Commands -update and -update_r track splits and fusions of hard links in filesystems which have stable device and inode numbers. This can cause automatic last minute changes before the session gets written. Command -hardlinks "perform_update" may be used to do these changes earlier, e.g. if you need to apply filters to all updated files. @* Mode "without_update" avoids hardlink processing during update commands. Use this if your filesystem situation does not allow -disk_dev_ino "on". @* @command{xorriso} commands which extract files from an ISO image try to hardlink files with identical inode number. The normal scope of this operation is from image load to image load. One may give up the accumulated hard link addresses by -hardlinks "discard_extract". @* A large number of hardlink families may exhaust -temp_mem_limit if not -osirrox "sort_lba_on" and -hardlinks "cheap_sorted_extract" are both in effect. This restricts hard linking to other files restored by the same single extract command. -hardlinks "normal_extract" re-enables wide and expensive hardlink accumulation. @* @c man .TP @item -acl "on"|"off" @kindex -acl controls handling of ACLs @cindex ACL, control handling, -acl Enable or disable processing of ACLs. If enabled, then @command{xorriso} will obtain ACLs from disk file objects, store ACLs in the ISO image using the libisofs specific AAIP format, load AAIP data from ISO images, test ACL during file comparison, and restore ACLs to disk files when extracting them from ISO images. See also commands -getfacl, -setfacl. @c man .TP @item -xattr "on"|"user"|"any"|"off" @kindex -xattr controls handling of xattr (EA) @cindex xattr, control handling, -xattr Enable or disable processing of xattr attributes. If enabled, then @command{xorriso} will handle xattr similar to ACL. See also commands -getfattr, -setfattr and above paragraph about xattr. @* Modes "on" and "user" read and write only attributes from namespace "user". @* Mode "any" processes attributes of all namespaces. This might need administrator privileges, even if the owner of the disk file tries to read or write the attributes. @* Note that it is not possible to set xattr of namespace "isofs." by xorriso xattr manipulation commands. @c man .TP @item -lfa_flags mode[:mode...] @kindex -lfa_flags controls handling of Linux file attributes @cindex Linux file attributes, control handling, -lfa_flags Enable, disable, or influence processing of Linux file attributes as described in man 1 chattr. @* Mode "on" enables actual processing of the attributes. @* Mode "off" disables it. @* Mode "auto_on" enables it only if the underlying libisofs was compiled with support for Linux file attributes, which is typically not the case on non-Linux systems. Enabled processing without lisofs support would cause failure events when reading disk files. @* The other modes define the behavior in case of "on": @* Mode "read" enables obtaining of these attributes from disk files, storing them in the emerging ISO 9660 filesystem as AAIP data, importing them when an ISO filesystem gets loaded and bears such stored attributes, and comparing them during comparisons between files on disk and in ISO. @* Mode "no_read" disables reading of the attributes from disk files and importing them when an ISO filesystem gets loaded. If other settings like -acl "on" or -xattr "on" require storing of AAIP data, then file attributes might still get stored in the emerging ISO. To surely exclude any file attributes, run before -commit : @* -chattr_r --remove-lfa-flags / -- @* Mode "import_non_settable" enables obtaining of non-settable attributes from disk and attaching of attributes to files in the emerging ISO image even if all attribute flags are zero. @* Mode "import_only_settable" causes non-settable attributes ("eEhINVZ") to be ignored when file objects get read from disk. If the remaining attribute flags are all zero, then no attribute information gets attached to the file in the ISO. This saves storage and eases finding of non-trivial attributes in the ISO image. @* Mode "restore" enables restoring of attributes when their file gets restored and comparing them during comparisons between files on disk and in ISO. Several modes below modify the behavior during restoring of attributes. @* Mode "no_restore" disables restoring of attributes. @* Mode "restore_su" enables restoring of the attributes "aij" which are only changeable by the bearer of superuser capabilities. "no_restore_su" disables restoring of these attributes. "restore_su_auto" enables it only if the effective user id is 0. @* The attribute "i" (for "immutable") gets restored to directories only if they have been created freshly by the same file restoring xorriso command. A directory which already existed on disk will not be made immutable. @* Mode "restore_only_known" restricts restoring to the known settable attribute flags "aAcCdDFijmPsStTux". "restore_unknown" enables the attempt to restore unknown flags or even those which are known to be unchangeable, if they are not disabled by other modes. @* Mode "restore_mask=..." enables particular attributes for restoring. All others will not be restored. The list of desired attribute letters follows the '=' character. An empty list enables all attributes, if they are not disabled by other modes. The single character "-" bans all attributes from restoring, like "off" does. Example: @* -lfa_flags restore_mask=SdCiaj @* Mode "restore_error=" sets the behavior for the event that restoring of attribute flags to the local filesystem fails. Available are the keyword "silent" and the severities from "all" to "fatal". "silent" and severity "all" suppress any error messages and abort considerations caused by restore attemps of attribute flags. Else the error message is issued with the given severity. Then it depends on the setting of command -abort_on whether restoring goes on or gets aborted. @* Mode "restore_single" tries to set the attribute flags of a file one-by-one if the attempt fails to set them all at once. This happens only if the "restore_error=" severity and the setting of command -abort_on do not demand to abort the program run. Note that some flags in some situations get ignored silently by some kernels. @* Mode "no_restore_single" disables this attempt to get at least some of the attribute flags into effect. @* Mode "default" reinstates the default settings: @* -lfa_flags off:read:restore:restore_su_auto:restore_only_known @* -lfa_flags restore_mask=:restore_error=sorry:restore_single @* Use "default:on" to get default settings with enabled processing. @c man .TP @item -md5 "on"|"all"|"off"|"load_check_off" @kindex -md5 controls handling of MD5 sums @cindex MD5, control handling, -md5 Enable or disable processing of MD5 checksums for the overall session and for each single data file. If enabled then images with checksum tags get loaded only if the tags of superblock and directory tree match properly. The MD5 checksums of data files and whole session get loaded from the image if there are any. @* With commands -compare and -update the recorded MD5 of a file will be used to avoid content reading from the image. Only the disk file content will be read and compared with that MD5. This can save much time if -disk_dev_ino "on" is not suitable. @* Commands which copy whole data files from ISO to hard disk will verify the copied data stream by the recorded MD5, if -osirrox "check_md5_on" is set. @* At image generation time they are computed for each file which gets its data written into the new session. The checksums of files which have their data in older sessions get copied into the new session. Superblock, tree and whole session get a checksum tag each. @* Mode "all" will additionally check during image generation whether the checksum of a data file changed between the time when its reading began and the time when it ended. This implies reading every file twice. @* Mode "load_check_off" together with "on" or "all" will load recorded MD5 sums but not test the recorded checksum tags of superblock and directory tree. This is necessary if growisofs was used as burn program, because it does not overwrite the superblock checksum tag of the first session. Therefore load_check_off is in effect when @command{xorriso} -as mkisofs option -M is performed. @* The test can be re-enabled by mode "load_check_on". @* Checksums can be exploited via commands -check_md5, -check_md5_r, via find actions get_md5, check_md5, and via -check_media. @c man .TP @item -for_backup @kindex -for_backup acl,xattr,hardlinks,md5,lfa_flags @cindex Backup, enable features, -for_backup Enable all extra features which help to produce or to restore backups with highest fidelity of file properties. Currently this is a shortcut for: @* -hardlinks on -acl on -xattr any -md5 on @* and possibly: @* -lfa_flags default:on:import_only_settable @* -lfa_flags restore_mask=aAcdDijmPsStTux @* If you restore a backup with xattr from non-user namespaces, then make sure that the target operating system and filesystem know what these attributes mean. Possibly you will need administrator privileges to record or restore such attributes. At recording time, xorriso will try to tolerate missing privileges and just record what is readable. But at restore time, missing privileges or preconditions will cause failure events. @* Command -xattr "user" after command -for_backup will exclude non-user attributes from being recorded or restored. @* The command -lfa_flags is executed by -for_backup only if the underlying libisofs was compiled with support for Linux file attributes, which is typically not the case on non-Linux systems. @* If -lfa_flags is executed by -for_backup then the restore mask enables all known settable attributes, except "C" and "F" which have special constraints which xorriso cannot yet detect at restore time. Command -lfa_flags "restore_mask=" after -for_backup will enable all known settable attributes. @c man .TP @item -ecma119_map "stripped"|"unmapped"|"lowercase"|"uppercase" @kindex -ecma119_map names w/o Rock Ridge, Joliet @cindex File names, if neither Rock Ridge nor Joliet Choose the conversion of file names when a session gets loaded, if they stem neither from a Rock Ridge name nor from a Joliet name. @* Mode "stripped" is the default. It shows the names as found in the ISO but removes trailing ";1" or ".;1" if present. @* Mode "unmapped" shows names as found without removing characters. Warning: Multi-session converts "xyz;1" to "xyz_1" and maybe adds new ";1". @* Mode "lowercase" is like "stripped" but also maps uppercase letters to lowercase letters. This is compatible to default GNU/Linux mount behavior. @* Mode "uppercase" is like "stripped" but maps lowercase letters to uppercase, if any occur despite the prescriptions of ECMA-119. @c man .TP @item -joliet_map "stripped"|"unmapped" @kindex -joliet_map Joliet names @cindex File names, if Joliet is loaded Choose the conversion of file names when a session gets loaded from a Joliet tree. @* Mode "stripped" is the default. It removes trailing ";1" or ".;1" if present. @* Mode "unmapped" shows names as found without removing characters. Warning: Multi-session converts "xyz;1" to "xyz_1" and maybe adds new ";1". @c man .TP @item -iso_nowtime "dynamic"|timestring @kindex -iso_nowtime fixed "now" time for ISO 9660 objects @cindex libisofs, fixed "now" time Choose whether to use the current time ("dynamic") or a fixed time point for timestamps of ISO 9660 nodes without a disk source file and as default for superblock timestamps. @* If a timestring is given, then it is used for such timestamps. For the formats of timestrings see command @strong{-alter_date}. @c man .TP @item -disk_dev_ino "on"|"ino_only"|"off" @kindex -disk_dev_ino fast incremental backup @cindex Backup, enable fast incremental, -disk_dev_ino Enable or disable processing of recorded file identification numbers (dev_t and ino_t). If enabled they are stored as xattr and can substantially accelerate file comparison. The root node gets a global start timestamp. If during comparison a file with younger timestamps is found in the ISO image, then it is suspected to have inconsistent content. @* If device numbers and inode numbers of the disk filesystems are persistent and if no irregular alterations of timestamps or system clock happen, then potential content changes can be detected without reading that content. File content change is assumed if any of mtime, ctime, device number or inode number have changed. @* Mode "ino_only" replaces the precondition that device numbers are stable by the precondition that mount points in the compared tree always lead to the same filesystems. Use this if mode "on" always sees all files changed. @* The speed advantage appears only if the loaded session was produced with -disk_dev_ino "on" too. @* Note that -disk_dev_ino "off" is totally in effect only if -hardlinks is "off", too. @c man .TP @item -file_name_limit [+]number @kindex -file_name_limit curbs length of file names @cindex File names, curb length, -file_name_limit Set the maximum permissible length for file names in the range of 64 to 255. Path components which are longer than the given number will get truncated and have their last 33 bytes overwritten by a colon ':' and the hex representation of the MD5 of the first 4095 bytes of the whole oversized name. Potential incomplete UTF-8 characters will get their leading bytes replaced by '_'. @* iso_rr_paths with the long components will still be able to access the file paths with truncated components. @* If -file_name_limit is executed while an ISO tree is present, the file names in the ISO tree get checked for existing truncated file names of the current limit and for name collisions between newly truncated files and existing files. In both cases, the setting will be refused with a SORRY event. @* One may lift this ban by prepending the character "+" to the argument of -file_name_limit. Truncated filenames may then get truncated again, invalidating their MD5 part. Colliding truncated names are made unique, consuming at least 9 more bytes of the remaining name part. @* If writing of xattr is enabled, then the length will be stored in "isofs.nt" of the root directory. If reading of xattr is enabled and "isofs.nt" is found, then the found length will get into effect if it is smaller than the current setting of -file_name_limit. @* File name patterns will only work if they match the truncated name. This might change in future. @* Files with truncated names get deleted and re-added unconditionally during -update and -update_r. This might change in future. @* Linux kernels up to at least 4.1 misrepresent names of length 254 and 255. If you expect such names in or under disk_paths and plan to mount the ISO by such Linux kernels, consider to set -file_name_limit 253. Else just avoid names longer than 253 characters. @c man .TP @item -rom_toc_scan "on"|"force"|"off"[:"emul_off"][:"emul_wide"] @kindex -rom_toc_scan searches for sessions @cindex Table-of-content, search sessions, -rom_toc_scan Read-only drives do not tell the actual media type but show any media as ROM (e.g. as DVD-ROM). The session history of MMC multi-session media might be truncated to first and last session or even be completely false. (The emulated history of overwritable media is not affected by this.) @* To have in case of failure a chance of getting the session history and especially the address of the last session, there is a scan for ISO 9660 filesystem headers which might help but also might yield worse results than the drive's table of content. At its end it can cause read attempts to invalid addresses and thus ugly drive behavior. Setting "on" enables that scan for alleged read-only media. @* Some operating systems are not able to mount the most recent session of multi-session DVD or BD. If on such a system @command{xorriso} has no own MMC capabilities then it may still find that session from a scanned table of content. Setting "force" handles any media like a ROM medium with setting "on". @* On the other hand the emulation of session history on overwritable media can hamper reading of partly damaged media. Setting "off:emul_off" disables the elsewise trustworthy table-of-content scan for those media. @* The table-of-content scan on overwritable media normally searches only up to the end of the session that is pointed to by the superblock at block 0. Setting "on:emul_wide" lets the scan continue up to the end of the medium. This may be useful after copying a medium with -check_media patch_lba0=on when not the last session was loaded. @c man .TP @item -calm_drive "in"|"out"|"all"|"revoke"|"on"|"off" @kindex -calm_drive reduces drive activity @cindex Drive, reduce activity, -calm_drive Reduce drive noise until it is actually used again. Some drives stay alert for substantial time after they have been used for reading. This reduces the startup time for the next drive operation but can be loud and waste energy if no i/o with the drive is expected to happen soon. @* Modes "in", "out", "all" immediately calm down -indev, -outdev, or both, respectively. Mode "revoke" immediately alerts both. Mode "on" causes -calm_drive to be performed automatically after each -dev, -indev, and -outdev. Mode "off" disables this. @c man .TP @item -ban_stdio_write @kindex -ban_stdio_write demands real drive @cindex Drive, demand real MMC, -ban_stdio_write Allow for writing only the usage of MMC optical drives. Disallow to write the result into files of nearly arbitrary type. Once set, this command cannot be revoked. @c man .TP @item -early_stdio_test "on"|"appendable_wo"|"off" @kindex -early_stdio_test classifies stdio drives @cindex Drive, classify stdio, -early_stdio_test If enabled by "on" then regular files and block devices get tested for effective access permissions. This implies to try opening those files for writing, which otherwise will happen only later and only if actual writing is desired. @* The test result is used for classifying the pseudo drives as overwritable, read-only, write-only, or uselessly empty. This may lead to earlier detection of severe problems, and may avoid some less severe error events. @* Mode "appendable_wo" is like "on" with the additional property that non-empty write-only files are regarded as appendable rather than blank. @c man .TP @item -data_cache_size number_of_tiles blocks_per_tile @kindex -data_cache_size adjusts read cache size @cindex Image reading, cache size, -data_cache_size Set the size and granularity of the data cache which is used when ISO images are loaded and when file content is read from ISO images. The cache consists of several tiles, which each consists of several blocks. A larger cache reduces the need for tiles being read multiple times. Larger tiles might additionally improve the data throughput from the drive, but can be wasteful if the data are scattered over the medium. @* Larger cache sizes help best with image loading from MMC drives. They are an inferior alternative to -osirrox option "sort_lba_on". @* blocks_per_tile must be a power of 2. E.g. 16, 32, or 64. The overall cache size must not exceed 1 GiB. The default values can be restored by parameter "default" instead of one or both of the numbers. Currently the default is 32 tiles of 32 blocks = 2 MiB. @end table @c man .TP @c man .B Inserting files into ISO image: @node Insert, SetInsert, Loading, Commands @section Inserting files into ISO image @c man .PP The following commands expect file addresses of two kinds: @c man .br @cindex disk_path, _definition @strong{disk_path} is a path to an object in the local filesystem tree. @c man .br @cindex iso_rr_path, _definition @strong{iso_rr_path} is the Rock Ridge name of a file object in the ISO image. If no Rock Ridge information is recorded in the loaded ISO image, then you will see ISO 9660 names which are of limited length and character set. If no Rock Ridge information shall be stored in an emerging ISO image, then their names will get mapped to such restricted ISO 9660 (aka ECMA-119) names. @c man .PP @sp 1 Note that in the ISO image you are as powerful as the superuser. Access permissions of the existing files in the image do not apply to your write operations. They are intended to be in effect with the read-only mounted image. @c man .PP @sp 1 If the iso_rr_path of a newly inserted file leads to an existing file object in the ISO image, then the following collision handling happens: @* If both objects are directories then they get merged by recursively inserting the subobjects from filesystem into ISO image. If other file types collide then the setting of command @strong{-overwrite} decides. @* Renaming of files has similar collision handling, but directories can only be replaced, not merged. Note that if the target directory exists, then -mv inserts the source objects into this directory rather than attempting to replace it. Command -move, on the other hand, would attempt to replace it. @c man .PP @sp 1 The commands in this section alter the ISO image and not the local filesystem. @table @asis @sp 1 @c man .TP @item -disk_pattern "on"|"ls"|"off" @kindex -disk_pattern controls pattern expansion @cindex Pattern expansion, for disk paths, -disk_pattern Set the pattern expansion mode for the disk_path parameters of several commands which support this feature. @* Setting "off" disables this feature for all commands which are marked in this man page by "disk_path [***]" or "disk_pattern [***]". @* Setting "on" enables it for all those commands. @* Setting "ls" enables it only for those which are marked by "disk_pattern [***]". @* Default is "ls". @c man .TP @item -add pathspec [...] | disk_path [***] @kindex -add inserts one or more paths @cindex Insert, pathspecs, -add Insert the given files or directory trees from filesystem into the ISO image. @* If -pathspecs is set to "on" or "as_mkisofs" then pattern expansion is always disabled and character '=' has a special meaning. It separates the ISO image path from the disk path: @* iso_rr_path=disk_path @* Character '=' in the iso_rr_path must be escaped by '\' (i.e. as "\="). @* With -pathspecs "on", the character '\' must not be escaped. The character '=' in the disk_path must not be escaped. @* With -pathspecs "as_mkisofs", all characters '\' must be escaped in both, iso_rr_path and disk_path. The character '=' may or may not be escaped in the disk_path. @* If iso_rr_path does not begin with '/' then -cd is prepended. If disk_path does not begin with '/' then -cdx is prepended. @* If no '=' is given then the word is used as both, iso_rr_path and disk path. If in this case the word does not begin with '/' then -cdx is prepended to the disk_path and -cd is prepended to the iso_rr_path. @* If -pathspecs is set to "off" then -disk_pattern expansion applies, if enabled. The resulting words are used as both, iso_rr_path and disk path. Relative path words get prepended the setting of -cdx to disk_path and the setting of -cd to iso_rr_path. @c man .TP @item -add_plainly mode @kindex -add_plainly inserts one or more paths @cindex Insert, non-dashed arguments, -add_plainly If set to mode "unknown" then any command word that does not begin with "-" and is not recognized as known command will be subject to a virtual -add command. I.e. it will be used as pathspec or as disk_path and added to the image. If enabled, -disk_pattern expansion applies to disk_paths. @* Mode "dashed" is similar to "unknown" but also adds unrecognized command words even if they begin with "-". @* Mode "any" announces that all further words are to be added as pathspecs or disk_paths. This does not work in dialog mode. @* Mode "none" is the default. It prevents any words from being understood as files to add, if they are not parameters to appropriate commands. @c man .TP @item -path_list disk_path @kindex -path_list inserts paths from disk file @cindex Insert, paths from disk file, -path_list Like -add but read the parameter words from file disk_path or standard input if disk_path is "-". The list must contain exactly one pathspec or disk_path pattern per line. @c man .TP @item -quoted_path_list disk_path @kindex -quoted_path_list inserts paths from disk file @cindex Insert, paths from disk file, -quoted_path_list Like -path_list but with quoted input reading rules. Lines get split into parameter words for -add. Whitespace outside quotes is discarded. @c man .TP @item -map disk_path iso_rr_path @kindex -map inserts path @cindex Insert, path, -map Insert file object disk_path into the ISO image as iso_rr_path. If disk_path is a directory then its whole sub tree is inserted into the ISO image. @c man .TP @item -map_single disk_path iso_rr_path @kindex -map_single inserts path @cindex Insert, path, -map_single Like -map, but if disk_path is a directory then its sub tree is not inserted. @c man .TP @item -map_l disk_prefix iso_rr_prefix disk_path [***] @kindex -map_l inserts paths from disk file @cindex Insert, paths from disk file, -map_l Perform -map with each of the disk_path parameters. iso_rr_path will be composed from disk_path by replacing disk_prefix by iso_rr_prefix. @c man .TP @item -update disk_path iso_rr_path @kindex -update inserts path if different @cindex Insert, if different, -update Compare file object disk_path with file object iso_rr_path. If they do not match, then perform the necessary image manipulations to make iso_rr_path a matching copy of disk_path. By default this comparison will imply lengthy content reading before a decision is made. Commands -disk_dev_ino or -md5 may accelerate comparison if they were already in effect when the loaded session was recorded. @* If disk_path is a directory and iso_rr_path does not exist yet, then the whole subtree will be inserted. Else only directory attributes will be updated. @c man .TP @item -update_r disk_path iso_rr_path @kindex -update_r inserts paths if different @cindex Insert, if different, -update_r Like -update but working recursively. I.e. all file objects below both addresses get compared whether they have counterparts below the other address and whether both counterparts match. If there is a mismatch then the necessary update manipulation is done. @* Note that the comparison result may depend on command -follow. Its setting should always be the same as with the first adding of disk_path as iso_rr_path. @* If iso_rr_path does not exist yet, then it gets added. If disk_path does not exist, then iso_rr_path gets deleted. @c man .TP @item -update_l disk_prefix iso_rr_prefix disk_path [***] @kindex -update_l inserts paths if different @cindex Insert, if different, -update_l Perform -update_r with each of the disk_path parameters. iso_rr_path will be composed from disk_path by replacing disk_prefix by iso_rr_prefix. @c man .TP @item -update_li iso_rr_prefix disk_prefix iso_rr_path [***] @kindex -update_li inserts paths if different @cindex Insert, if different, -update_li Perform -update_r with each of the iso_rr_path parameters. disk_path will be composed from iso_rr_path by replacing iso_rr_prefix by disk_prefix. @c man .TP @item -update_lxi disk_prefix iso_rr_prefix disk_path [***] @kindex -update_l inserts paths if different @cindex Insert, if different, -update_lxi Perform -update_r with each of the disk_path parameters and with iso_rr_paths in the ISO filesystem which are derived from the disk_path parameters after exchanging disk_prefix by iso_rr_prefix. So, other than -update_l, this detects missing matches of disk_path and deletes the corresponding iso_rr_path. @* Note that relative disk_paths and disk_path patterns are interpreted as sub paths of the current disk working directory -cdx. The corresponding iso_rr_paths are derived by exchanging disk_prefix by iso_rr_prefix before pattern expansion happens. The current -cdi directory has no influence. @c man .TP @item -cut_out disk_path byte_offset byte_count iso_rr_path @kindex -cut_out inserts piece of data file or device @cindex Insert, piece of data file or device, -cut_out Map a byte interval of a regular disk file or of a device file into a regular file in the ISO image. The file depicted by disk_path has to support random read access. A symbolic link will only work if enabled by -follow "link" or "param". @* Cutting out a byte interval may be necessary if the disk file is larger than a single medium, or if it exceeds the traditional limit of 2 GiB - 1 for old operating systems, or the limit of 4 GiB - 1 for newer ones. Contemporary Linux kernels are able to read properly files >= 4 GiB - 1. @* A clumsy remedy for such limits is to backup file pieces and to concatenate them at restore time. A well tested chopping size is 2047m. It is permissible to request a higher byte_count than available. The resulting file will be truncated to the correct size of a final piece. To request a byte_offset higher than available yields no file in the ISO image but a SORRY event. E.g: @* -cut_out /my/disk/file 0 2047m \ @* /file/part_1_of_3_at_0_with_2047m_of_5753194821 \ @* -cut_out /my/disk/file 2047m 2047m \ @* /file/part_2_of_3_at_2047m_with_2047m_of_5753194821 \ @* -cut_out /my/disk/file 4094m 2047m \ @* /file/part_3_of_3_at_4094m_with_2047m_of_5753194821 @* If the directory /file does no yet exist, then its permissions are not taken from directory /my/disk but rather from /my/disk/file with additional x-permission for those who have r-permission. @* While command -split_size is set larger than 0, and if all pieces of a file reside in the same ISO directory with no other files, and if the names look like above, then their ISO directory will be recognized and handled like a regular file. This affects commands -compare*, -update*, and overwrite situations. @* See command -split_size for details. @* Another use case is copying the content of a device file as interval or as a whole into the emerging ISO filesystem. The fact that the byte_count is allowed to be unreasonably high enables copying of a whole device: @* -cut_out /dev/sdd3 0 1000g /content_of_sdd3 @c man .TP @item -cpr disk_path [***] iso_rr_path @kindex -cpr inserts like with cp -r @cindex Insert, paths, -cpr Insert the given files or directory trees from filesystem into the ISO image. @* The rules for generating the ISO addresses are similar as with shell command cp -r. Nevertheless, directories of the iso_rr_path are created if necessary. Especially a not yet existing iso_rr_path will be handled as directory if multiple disk_paths are present. The leafnames of the multiple disk_paths will be grafted under that directory as would be done with an existing directory. @* If a single disk_path is present then a non-existing iso_rr_path will get the same type as the disk_path. @* If a disk_path does not begin with '/' then -cdx is prepended. If the iso_rr_path does not begin with '/' then -cd is prepended. @c man .TP @item -mkdir iso_rr_path [...] @kindex -mkdir creates ISO directory @cindex Directory, create, -mkdir Create empty directories if they do not exist yet. Existence as directory generates a WARNING event, existence as other file causes a FAILURE event. @c man .TP @item -lns target_text iso_rr_path @kindex -lns creates ISO symbolic link @cindex Symbolic link, create, -lns Create a symbolic link with address iso_rr_path which points to target_text. iso_rr_path may not exist yet. @* Hint: Command -clone produces the ISO equivalent of a hard link. @c man .TP @item -clone iso_rr_path_original iso_rr_path_copy @kindex -clone copies ISO directory tree @cindex Directory, copy, -clone Create a copy of the ISO file object iso_rr_path_original with the new address iso_rr_path_copy. If the original is a directory then copy all files and directories underneath. If iso_rr_path_original is a boot catalog file, then it gets not copied but is silently ignored. @* The copied ISO file objects have the same attributes. Copied data files refer to the same content source as their originals. The copies may then be manipulated independendly of their originals. @* This command will refuse execution if the address iso_rr_path_copy already exists in the ISO tree. @c man .TP @item -cp_clone iso_rr_path_original [***] iso_rr_path_dest @kindex -cp_clone copies ISO directory tree @cindex Directories, copy, -cp_clone Create copies of one or more ISO file objects as with command -clone. In case of collision merge directories with existing ones, but do not overwrite existing ISO file objects. @* The rules for generating the copy addresses are the same as with command -cpr (see above) or shell command cp -r. Other than with -cpr, relative iso_rr_path_original will get prepended the -cd path and not the -cdx path. Consider to -mkdir iso_rr_path_dest before -cp_clone so the copy address does not depend on the number of iso_rr_path_original parameters. @end table @c man .TP @c man .B Settings for file insertion: @node SetInsert, Manip, Insert, Commands @section Settings for file insertion @c man .TP @table @asis @item -file_size_limit value [value [...]] @minus{}@minus{} @kindex -file_size_limit limits data file size @cindex Insert, limit data file size, -file_size_limit Set the maximum permissible size for a single data file. The values get summed up for the actual limit. If the only value is "off" then the file size is not limited by @command{xorriso}. Default is a limit of 100 extents, 4g -2k each: @* -file_size_limit 400g -200k @minus{}@minus{} @* When mounting ISO 9660 filesystems, old operating systems can handle only files up to 2g -1 @minus{}@minus{}. Newer ones are good up to 4g -1 @minus{}@minus{}. You need quite a new Linux kernel to read correctly the final bytes of a file >= 4g if its size is not aligned to 2048 byte blocks. @* @command{xorriso}'s own data read capabilities are not affected by operating system size limits. Such limits apply to mounting only. Nevertheless, the target filesystem of an -extract must be able to take the file size. @c man .TP @item -not_mgt code[:code[...]] @kindex -not_mgt controls file exclusion @cindex Insert, file exclusion, -not_mgt Control the behavior of the exclusion lists. @* Exclusion processing happens before disk_paths get mapped to the ISO image, before disk files get compared with image files, and before image files get extracted to disk files. @* The absolute disk paths involved in such an action are matched against the -not_paths list. The leafnames of disk paths are matched against the patterns in the -not_leaf list. If a match is detected then the disk path will not be regarded as an existing file and not be added to the ISO image. @* Several codes are defined. The _on/_off settings persist until they are revoked by their_off/_on counterparts. @* "erase" empties the lists which were accumulated by -not_paths and -not_leaf. @* "reset" is like "erase" but also re-installs default behavior. @* "off" disables exclusion processing temporarily without invalidating the lists and settings. @* "on" re-enables exclusion processing. @* "param_off" applies exclusion processing only to paths below disk_path parameter of commands. I.e. explicitly given disk_paths are exempted from exclusion processing. @* "param_on" applies exclusion processing to command parameters as well as to files below such parameters. @* "subtree_off" with "param_on" excludes parameter paths only if they match a -not_paths item exactly. @* "subtree_on" additionally excludes parameter paths which lead to a file address below any -not_paths item. @* "ignore_off" treats excluded disk files as if they were missing. I.e. they get reported with -compare and deleted from the image with -update. @* "ignore_on" keeps excluded files out of -compare or -update activities. @c man .TP @item -not_paths disk_path [***] @kindex -not_paths sets absolute exclusion paths @cindex Insert, file exclusion absolute, -not_paths Add the given paths to the list of excluded absolute disk paths. If a given path is relative, then the current -cdx is prepended to form an absolute path. Pattern matching, if enabled, happens at definition time and not when exclusion checks are made. @* Keep in mind that there may be alternative paths to the same disk file. The exclusion tests are done literally, so that they do not keep files from getting into the ISO filesystem by other paths. Accordingly an exclusion does not prevent a disk file from being overwritten by file extraction via an alternative not excluded path. So the exclusions need to be coordinated with the actual disk_path parameters given with commands. @* (Do not forget to end the list of disk_paths by "@minus{}@minus{}") @c man .TP @item -not_leaf pattern @kindex -not_leaf sets exclusion pattern @cindex Insert, file exclusion pattern, -not_leaf Add a single shell parser style pattern to the list of exclusions for disk leafnames. These patterns are evaluated when the exclusion checks are made. @c man .TP @item -not_list disk_path @kindex -not_list sets exclusions from disk file @cindex Insert, file exclusion from file, -not_list Read lines from disk_path and use each of them either as -not_paths parameter, if they contain a / character, or as -not_leaf pattern. @c man .TP @item -quoted_not_list disk_path @kindex -quoted_not_list sets exclusions @cindex Insert, file exclusion, -quoted_not_list Like -not_list but with quoted input reading rules. Each word is handled as one parameter for -not_paths or -not_leaf. @c man .TP @item -follow occasion[:occasion[...]] @kindex -follow softlinks and mount points @cindex Insert, links or mount points, -follow Enable or disable resolution of symbolic links and mountpoints under disk_paths. This applies to actions -add, -cut_out, -du*x, -ls*x, -findx, -concat, and to -disk_pattern expansion. @* There are three main kinds of follow decisison to be made: @* @strong{link} is the hop from a symbolic link to its target file object for the purpose of reading. I.e. not for command -concat. If enabled then symbolic links are handled as their target file objects, else symbolic links are handled as themselves. @* @strong{mount} is the hop from one filesystem to another subordinate filesystem. If enabled then mountpoint directories are handled as any other directory, else mountpoints are handled as empty directories if they are encountered in directory tree traversals. @* @strong{concat} is the hop from a symbolic link to its target file object for the purpose of writing. I.e. for command -concat. This is a security risk ! @* Less general than above occasions: @* @strong{pattern} is mount and link hopping, but only during -disk_pattern expansion. @* @strong{param} is link hopping for parameter words (after eventual pattern expansion). If enabled then -ls*x will show the link targets rather than the links themselves. -du*x, -findx, and -add will process the link targets but not follow links in an eventual directory tree below the targets (unless "link" is enabled). -cut_out will process link targets. @* Occasions can be combined in a colon separated list. All occasions mentioned in the list will then lead to a positive follow decision. @* @strong{off} prevents any positive follow decision. Use it if no other occasion applies. @* Shortcuts: @* @strong{default} is equivalent to "pattern:mount:limit=100". @* @strong{on} always decides positive. Equivalent to "link:mount:concat". @* @sp 1 Not an occasion but an optional setting is: @* @strong{limit=}<number> which sets the maximum number of link hops. A link hop consists of a sequence of symbolic links and a final target of different type. Nevertheless those hops can loop. Example: @* $ ln -s .. uploop @* Link hopping has a built-in loop detection which stops hopping at the first repetition of a link target. Then the repeated link is handled as itself and not as its target. Regrettably one can construct link networks which cause exponential workload before their loops get detected. The number given with "limit=" can curb this workload at the risk of truncating an intentional sequence of link hops. @c man .TP @item -pathspecs "on"|"off"|"as_mkisofs" @kindex -pathspecs sets meaning of = with -add @cindex Insert, meaning of = with -add, -pathspecs Control parameter interpretation with @command{xorriso} actions -add and -path_list. @* @cindex Pathspec, _definition Mode "as_mkisofs" enables pathspecs of the form @* @strong{iso_rr_path=disk_path} @* like with program mkisofs -graft-points. @* All characters '\' must be escaped in both, iso_rr_path and disk_path. The character '=' must be escaped in the iso_rr_path and may or may not be escaped in the disk_path. This mode temporarily disables -disk_pattern expansion for command -add. @* Mode "on" does nearly the same. But '=' must only be escaped in the iso_rr_path and '\' must not be escaped at all. This has the disadvantage that one cannot express an iso_rr_path which ends by '\'. @* Mode "off" disables pathspecs of the form target=source and re-enables -disk_pattern expansion. @c man .TP @item -overwrite "on"|"nondir"|"off" @kindex -overwrite enables overwriting in ISO @cindex Insert, enable overwriting, -overwrite Allow or disallow overwriting of existing files in the ISO image or in the local filesystem by files with the same name. @* With setting "off", name collisions with at least one non-directory file cause FAILURE events. Collisions of two directories lead to merging of their file lists. @* With setting "nondir", only directories are protected by such events, other existing file types get treated with -rm before the new file gets added. Setting "on" enables automatic -rm_r. I.e. a non-directory can replace an existing directory and all its subordinates. @* If restoring of files to the disk filesystem is enabled by -osirrox, then the overwrite rule applies to the target file objects on disk as well, but "on" is downgraded to "nondir". @c man .TP @item -split_size number["k"|"m"] @kindex -split_size enables large file splitting @cindex Insert, large file splitting, -split_size Set the threshold for automatic splitting of regular files. Such splitting maps a large disk file onto a ISO directory with several part files in it. This is necessary if the size of the disk file exceeds -file_size_limit. Older operating systems can handle files in mounted ISO 9660 filesystems only if they are smaller than 2 GiB or in other cases 4 GiB. @* Default is 0 which will exclude files larger than -file_size_limit by a FAILURE event. A well tested -split_size is 2047m. Sizes above -file_size_limit are not permissible. @* The newly created ISO directory inherits its permissions from the data file with additional x-permission for those who have r-permission. @* While command -split_size is set larger than 0 such a directory with split file pieces will be recognized and handled like a regular file by commands -compare* , -update*, and in overwrite situations. There are -osirrox parameters "concat_split_on" and "concat_split_off" which control the handling when files get restored to disk. @* In order to be recognizable, the names of the part files have to describe the splitting by 5 numbers: @* part_number,total_parts,byte_offset,byte_count,disk_file_size @* which are embedded in the following text form: @* part_#_of_#_at_#_with_#_of_# @* Scaling characters like "m" or "k" are taken into respect. All digits are interpreted as decimal, even if leading zeros are present. @* E.g: /file/part_1_of_3_at_0_with_2047m_of_5753194821 @* No other files are allowed in the directory. All parts have to be present and their numbers have to be plausible. E.g. byte_count must be valid as -cut_out parameter and their contents may not overlap. @end table @c man .TP @c man .B File manipulations: @node Manip, CmdFind, SetInsert, Commands @section File manipulations @c man .PP The following commands manipulate files in the ISO image, regardless whether they stem from the loaded image or were newly inserted. @c man .PP @table @asis @sp 1 @c man .TP @item -iso_rr_pattern "on"|"ls"|"off" @kindex -iso_rr_pattern controls pattern expansion @cindex Pattern expansion, for ISO paths, -iso_rr_pattern Set the pattern expansion mode for the iso_rr_path parameters of several commands which support this feature. @* Setting "off" disables pattern expansion for all commands which are marked in this man page by "iso_rr_path [***]" or "iso_rr_pattern [***]". @* Setting "on" enables it for all those commands. @* Setting "ls" enables it only for those which are marked by "iso_rr_pattern [***]". @* Default is "on". @c man .TP @item -rm iso_rr_path [***] @kindex -rm deletes files from ISO image @cindex Delete, from ISO image, -rm Delete the given files from the ISO image. @* Note: This does not free any space on the -indev medium, even if the deletion is committed to that same medium. @* The image size will shrink if the image is written to a different medium in modification mode. @c man .TP @item -rm_r iso_rr_path [***] @kindex -rm_r deletes trees from ISO image @cindex Delete, from ISO image, -rm_r Delete the given files or directory trees from the ISO image. See also the note with command -rm. @c man .TP @item -rmdir iso_rr_path [***] @kindex -rmdir deletes ISO directory @cindex Delete, ISO directory, -rmdir @cindex Directory, delete, -rmdir Delete empty directories. @c man .TP @item -move iso_rr_path iso_rr_path @kindex -mv renames single file in ISO image @cindex Rename, in ISO image, -move Rename the file given by the first (origin) iso_rr_path to the second (destination) iso_rr_path. Deviate from rules of shell command mv by not moving the origin file underneath an existing destination directory. The origin file will rather replace such a directory, if this is allowed by command -overwrite. @c man .TP @item -mv iso_rr_path [***] iso_rr_path @kindex -mv renames files in ISO image @cindex Rename, in ISO image, -mv Rename the given file objects in the ISO tree to the last parameter in the list. Use the same rules as with shell command mv. @* If pattern expansion is enabled and if the last parameter contains wildcard characters then it must match exactly one existing file address, or else the command fails with a FAILURE event. @c man .TP @item -chown uid iso_rr_path [***] @kindex -chown sets ownership in ISO image @cindex Ownership, in ISO image, -chown Set ownership of file objects in the ISO image. uid may either be a decimal number or the name of a user known to the operating system. @c man .TP @item -chown_r uid iso_rr_path [***] @kindex -chown_r sets ownership in ISO image @cindex Ownership, in ISO image, -chown_r Like -chown but affecting all files below eventual directories. @c man .TP @item -chgrp gid iso_rr_path [***] @kindex -chgrp sets group in ISO image @cindex Group, in ISO image, -chgrp Set group attribute of file objects in the ISO image. gid may either be a decimal number or the name of a group known to the operating system. @c man .TP @item -chgrp_r gid iso_rr_path [***] @kindex -chgrp_r sets group in ISO image @cindex Group, in ISO image, -chgrp_r Like -chgrp but affecting all files below eventual directories. @c man .TP @item -chmod mode iso_rr_path [***] @kindex -chmod sets permissions in ISO image @cindex Permissions, in ISO image, -chmod Equivalent to shell command chmod in the ISO image. mode is either an octal number beginning with "0" or a comma separated list of statements of the form [ugoa]*[+-=][rwxst]* . @* Like: go-rwx,u+rwx . @* @strong{Personalities}: u=user, g=group, o=others, a=all @* @strong{Operators}: + adds given permissions, - revokes given permissions, = revokes all old permissions and then adds the given ones. @* @strong{Permissions}: r=read, w=write, x=execute|inspect, s=setuid|setgid, t=sticky bit @* For octal numbers see man 2 stat. @c man .TP @item -chmod_r mode iso_rr_path [***] @kindex -chmod_r sets permissions in ISO image @cindex Permissions, in ISO image, -chmod_r Like -chmod but affecting all files below eventual directories. @c man .TP @item -setfacl acl_text iso_rr_path [***] @kindex -setfacl sets ACL in ISO image @cindex ACL, set in ISO image, -setfacl Attach the given ACL to the given iso_rr_paths. If the files already have ACLs, then those get deleted before the new ones get into effect. If acl_text is empty, or contains the text "clear" or the text "@minus{}@minus{}remove-all", then the existing ACLs will be removed and no new ones will be attached. Any other content of acl_text will be interpreted as a list of ACL entries. It may be in the long multi-line format as put out by -getfacl but may also be abbreviated as follows: @* ACL entries are separated by comma or newline. If an entry is empty text or begins with "#" then it will be ignored. A valid entry has to begin by a letter out of @{ugom@} for "user", "group", "other", "mask". It has to contain two colons ":". A non-empty text between those ":" gives a user id or group id. After the second ":" there may be letters out of @{rwx- #@}. The first three give read, write, or execute permission. Letters "-", " " and TAB are ignored. "#" causes the rest of the entry to be ignored. Letter "X" or any other letters are not supported. Examples: @* g:toolies:rw,u:lisa:rw,u:1001:rw,u::wr,g::r,o::r,m::rw @* group:toolies:rw@minus{},user::rw@minus{},group::r@minus{}@minus{},other::r@minus{}@minus{},mask::rw@minus{} @* A valid entry may be prefixed by "d", some following characters and ":". This indicates that the entry goes to the "default" ACL rather than to the "access" ACL. Example: @* u::rwx,g::rx,o::,d:u::rwx,d:g::rx,d:o::,d:u:lisa:rwx,d:m::rwx @c man .TP @item -setfacl_r acl_text iso_rr_path [***] @kindex -setfacl_r sets ACL in ISO image @cindex ACL, set in ISO image, -setfacl_r Like -setfacl but affecting all files below given directories. @c man .TP @item -setfacl_list disk_path @kindex -setfacl_list sets ACL in ISO image @cindex ACL, set in ISO image, -setfacl_list Read the output of -getfacl_r or shell command getfacl -R and apply it to the iso_rr_paths as given in lines beginning with "# file:". This will change ownership, group and ACL of the given files. If disk_path is "-" then lines are read from standard input. Line "@@" ends the list, "@@@@@@" aborts without changing the pending iso_rr_path. @* Since -getfacl and getfacl -R strip leading "/" from file paths, the setting of -cd does always matter. @c man .TP @item -setfattr [-]name value iso_rr_path [***] @kindex -setfattr sets xattr in ISO image @cindex xattr, set in ISO image, -setfattr Attach the given xattr pair of name and value to the given iso_rr_paths. If the given name is prefixed by "-", then the pair with that name gets removed from the xattr list. If name is "@minus{}@minus{}remove@minus{}all" then all user namespace xattr of the given iso_rr_paths get deleted. In case of deletion, value must be an empty text. @* Which names are permissible depends on the setting of command -xattr. "on" or "user" restricts them to namespace "user". I.e. a name has to look like "user.x" or "user.whatever". @* -xattr setting "any" enables names from all namespaces except "isofs". @* Values and names undergo the normal input processing of @command{xorriso}. See also command -backslash_codes. Other than with command -setfattr_list, the byte value 0 cannot be expressed via -setfattr. @c man .TP @item -setfattr_r [-]name value iso_rr_path [***] @kindex -setfattr_r sets xattr in ISO image @cindex xattr, set in ISO image, -setfattr_r Like -setfattr but affecting all files below given directories. @c man .TP @item -setfattr_list disk_path @kindex -setfattr_list sets xattr in ISO image @cindex xattr, set in ISO image, -setfattr_list Read the output format of -getfattr_r or shell command getfattr -Rd and apply it to the iso_rr_paths as given in lines beginning with "# file:". All previously existing xattr of the acceptable namespaces will be deleted before the new xattr get attached. The set of acceptable names depends on the setting of command -xattr. @* If disk_path is "-" then lines are read from standard input. @* Since -getfattr and getfattr -Rd strip leading "/" from file paths, the setting of -cd does always matter. @* Empty input lines and lines which begin by "#" will be ignored (except "# file:"). Line "@@" ends the list, "@@@@@@" aborts without changing the pending iso_rr_path. Other input lines must have the form @* name="value" @* The separator "=" is not allowed in names. Value may contain any kind of bytes. It must be in quotes. Trailing whitespace after the end quote will be ignored. Non-printables bytes and quotes must be represented as \XYZ by their octal 8-bit code XYZ. Use code \000 for 0-bytes. @c man .TP @item -chattr mode iso_rr_path [***] @kindex -chattr sets Linux file attributes in ISO image @cindex Linux file attributes, set in ISO image, -chattr Set or unset Linux file attributes like program chattr(1) would do to disk files. Applying this command to files which are neither directory nor regular data file will yield a SORRY event, unless the mode is "--remove-lfa-flags". @* The first letter of the mode string determines what to do. The other letters are symbolic attribute flag letters out of the set "aAcCdDeEFhiIjNmPsStTuVxZ" as described in man 1 chattr. There is no restriction which attributes can be set or unset. But at restore time, unusual or unsuitable attributes may cause problems. @* First letter '+' causes the given attribute flags to be set. All other attributes stay as they are. @* First letter '-' causes the given attribute flags to be unset. All other attributes stay as they are. (Note that '-' is also accepted as symbolic attribute letter which has no effect.) @* A special case is the mode string "--remove-lfa-flags" which causes the Linux file attribute information to be removed from the file. The -find test -has_lfa_flags "-" will then not match the file any more. @* First letter '.' causes all attribute flags except the given ones to be unset. The given ones stay as they are. This is not a feature of program chattr(1). @* First letter '=' causes the given attribute flags to be set. All other get unset. Mode "=-" leads to all attribute flags being unset, but -find test -has_lfa_flags "-" will match the file afterwards. @* Example: -chattr +sDu /my/file /my/other_file -- @c man .TP @item -chattr_r mode iso_rr_path [***] @kindex -chattr_r sets Linux file attributes in ISO image @cindex Linux file attributes, set in ISO image, -chattr_r Like -chattr but affecting also all suitable files below the given directories. Except with mode "--remove-lfa-flags", the given iso_rr_path parameters need to be directories or regular data files or else a SORRY event will happen. Files below the given directories will be skipped silently if their type is not suitable for -chattr. @c man .TP @item -alter_date type timestring iso_rr_path [***] @kindex -alter_date sets timestamps in ISO image @cindex Timestamps, set in ISO image, -alter_date Alter the date entries of files in the ISO image. type may be one of the following: @* "a" sets access time, updates ctime. @* "m" sets modification time, updates ctime. @* "b" sets access time and modification time, updates ctime. @* "a-c", "m-c", and "b-c" set the times without updating ctime. @* "c" sets the ctime. @* timestring may be in the following formats (see also section EXAMPLES): @* As expected by program date: MMDDhhmm[[CC]YY][.ss]] @* As produced by program date: @* [Day] MMM DD hh:mm:ss [TZON] YYYY @* Relative times counted from current clock time: @* +|-Number["s"|"h"|"d"|"w"|"m"|"y"] @* where "s" means seconds, "h" hours, "d" days, "w" weeks, "m"=30d, "y"=365.25d plus 1d added to multiplication result. @* Absolute seconds counted from Jan 1 1970 00:00 GMT: @* =Number @* @command{xorriso}'s own timestamps: @* YYYY.MM.DD[.hh[mm[ss]]] @* scdbackup timestamps: @* YYMMDD[.hhmm[ss]] @* where "A0" is year 2000, "B0" is 2010, etc. @* ECMA-119 volume timestamps: @* YYYYMMDDhhmmsscc @* These are normally given as GMT. The suffix "LOC" causes local timezone conversion. E.g. 2013010720574700, 2013010720574700LOC. The last two digits cc (centiseconds) will be ignored, but must be present in order to make the format recognizable. @* Example: @* -alter_date m-c 2013.11.27.103951 /file1 /file2 -- @* This command does not persistently apply to the boot catalog, which gets fresh timestamps at -commit time. Command -volume_date "uuid" can set this time value. @c man .TP @item -alter_date_r type timestring iso_rr_path [***] @kindex -alter_date_r sets timestamps in ISO image @cindex Timestamps, set in ISO image, -alter_date_r Like -alter_date but affecting all files below eventual directories. @c man .TP @item -hide hide_state iso_rr_path [***] @kindex -hide excludes file names from directory trees @cindex hidden, set in ISO image, -hide Prevent the names of the given files from showing up in the directory trees of ISO 9660 and/or Joliet and/or HFS+ when the image gets written. The data content of such hidden files will be included in the resulting image, even if they do not show up in any directory. But you will need own means to find nameless data in the image. @* Warning: Data which are hidden from the ISO 9660 tree will not be copied by the write method of modifying. @* Possible values of hide_state are: "iso_rr" for hiding from ISO 9660 tree, "joliet" for Joliet tree, "hfsplus" for HFS+, "on" for them all. "off" means visibility in all directory trees. @* These values may be combined. E.g.: joliet:hfsplus @* This command does not apply to the boot catalog. Rather use: -boot_image "any" "cat_hidden=on" @end table @c man .TP @c man .B Tree traversal command -find: @node CmdFind, Filter, Manip, Commands @section Tree traversal command -find @c man .PP @table @asis @c man .TP @item -find iso_rr_path [test [op] [test ...]] [-exec action [params]] @minus{}@minus{} @kindex -find traverses and alters ISO tree @cindex Tree, ISO, traverse and alter, -find A restricted substitute for shell command find in the ISO image. It performs an action on matching file objects at or below iso_rr_path. @* If not used as last command in the line then the parameter list needs to get terminated by "@minus{}@minus{}". @* Tests are optional. If they are omitted then action is applied to all file objects. If tests are given then they form together an expression. The action is applied only if the expression matches the file object. Default expression operator between tests is -and, i.e. the expression matches only if all its tests match. @* Available tests are: @* @table @asis @sp 1 @item -name pattern : Matches if pattern matches the file leaf name. If the pattern does not contain any of the characters "*?[", then it will be truncated according to -file_name_limit and thus match the truncated name in the ISO filesystem. @* @item -wholename pattern : Matches if pattern matches the file path as it would be printed by action "echo". Character '/' can be matched by wildcards. If pattern pieces between '/' do not contain any of the characters "*?[", they will be truncated according to -file_name_limit. @* @item -disk_name pattern : Like -name but testing the leaf name of the file source on disk. Can match only data files which do not stem from the loaded image, or for directories above such data files. With directories the result can change between -find runs if their content stems from multiple sources. @* @item -disk_path disk_path : Matches if the given disk_path is equal to the path of the file source on disk. The same restrictions apply as with -disk_name. @* @item -type type_letter : Matches files of the given type: "block", "char", "dir", "pipe", "file", "link", "socket", "eltorito", and "Xotic" which matches what is not matched by the other types. @* Only the first letter is interpreted. E.g.: -find / -type d @* @item -size [+-][=]number[cwbdksmg] : Matches files with matching relation to the given size number. @* The prefix defines the desired relation: @* No prefix or prefix "=" means: File must have exactly the given size. @* Prefix "+" means: File must be larger than given size. @* Prefix "+=" means: File must be larger than or equal to given size limit. @* Prefix "-" means: File must be smaller than given size limit. @* Prefix "-=" means: File must be smaller than or equal to given size limit. @* Suffixes are peculiar to stay compatible with program "find": @* No suffix means blocks of 512 bytes, "c" means single bytes, "w" means 2 bytes, "b" means 512 bytes. The suffixes "k", "M", and "G" mean 1024, 1024k, and 1024M respectively. As usual with xorriso, the suffixes "d" and "s" mean 512 and 2048 and all suffixes are recognized as both, uppercase and lowercase letters. @* E.g. match files of 4 GiB or larger: @* -size +=4g @* @item -maxdepth number : Matches only files which are at most at the given depth level relative to the iso_rr_path where -find starts. That path itself is at depth 0, its directory children are at 1, their directory children at 2, and so on. @* @item -mindepth number : Matches only files which are at least at the given depth level. @* @item -damaged : Matches files which use data blocks marked as damaged by a previous run of -check_media. The damage info vanishes when a new ISO image gets loaded. @* Note that a MD5 session mismatch marks all files of the session as damaged. If finer distinction is desired, perform -md5 off before -check_media. @* @item -pending_data : Matches files which get their content from outside the loaded ISO image. @* @item -lba_range start_lba block_count : Matches files which use data blocks within the range of start_lba and start_lba+block_count-1. @* @item -has_acl : Matches files which have a non-trivial ACL. @* @item -has_xattr : Matches files which have xattr name-value pairs from user namespace. @* @item -has_aaip : Matches files which have ACL or any xattr. @* @item -has_lfa_flags flag_letters : Matches files which have Linux file attributes attached and have all flags set which correspond to the characters in the string flag_letters. The characters may be zero or more out of the set "aAcCdDeEFhiIjNmPsStTuVxZ". The character '-' will be ignored. The flag_letters string "-" matches any attribute set, but not a file with no Linux file attributes attached. E.g. look for files with both flags 'i' (immutable) and 'd' (no dump) set: @* -has_lfa_flags di @* @item -has_some_lfa_flags_of flag_letters : @* Similar to -has_lfa_flags but matching files which have at least one of the flags set which correspond to the characters in the string flag_letters. The flag_letters string "-" never matches any file. E.g. look for files which have 'i' or 'a' or both of them set: @* -has_some_lfa_flags_of ia @* @item -has_any_xattr : Matches files which have any xattr other than ACL. @* @item -has_md5 : Matches data files which have MD5 checksums. @* @item -has_hfs_crtp creator type : Matches files which have the given HFS+ creator and type attached. These are codes of 4 characters which get stored if -hfsplus is enabled. Use a single dash '-' as wildcard that matches any such code. E.g:. @* -has_hfs_crtp YYDN TEXT @* -has_hfs_crtp - - @* @item -has_hfs_bless blessing : Matches files which bear the given HFS+ blessing. It may be one of : "ppc_bootdir", "intel_bootfile", "show_folder", "os9_folder", "osx_folder", "any". See also action set_hfs_bless. @* @item -has_filter : Matches files which are filtered by -set_filter. @* @item -hidden hide_state : Matches files which are hidden in "iso_rr" tree, in "joliet" tree, in "hfsplus" tree, in all trees ("on"), or not hidden in any tree ("off"). @* Those which are hidden in some tree match -not -hidden "off". @* @item -bad_outname namespace : Matches files with names which change when converted forth and back between the local character set and one of the namespaces "rockridge", "joliet", "ecma119", "hfsplus". @* All applicable -compliance rules are taken into respect. Rule "omit_version" is always enabled, because else namespaces "joliet" and "ecma119" would cause changes with every non-directory name. Consider to also enable rules "no_force_dots" and "no_j_force_dots". @* The namespaces use different character sets and apply further restrictions to name length, permissible characters, and mandatory name components. "rockridge" uses the character set defined by -out_charset, "joliet" uses UCS-2BE, "ecma119" uses ASCII, "hfsplus" uses UTF-16BE. @* @item -name_limit_blocker length : Matches file names which would prevent command -file_name_limit with the given length. The command itself reports only the first problem file. @* @item -prune : If this test is reached and the tested file is a directory then -find will not dive into that directory. This test itself does always match. @* @item -use_pattern "on"|"off" : This pseudo test controls the interpretation of wildcards with tests -name, -wholename, and -disk_name. Default is "on". If interpretation is disabled by "off", then the parameters of -name, -wholename, and -disk_name have to match literally rather than as search pattern. This test itself does always match. @* @item -or_use_pattern "on"|"off" : Like -use_pattern, but automatically appending the test by -or rather than by -and. Further the test itself does never match. So a subsequent test -or will cause its other operand to be performed. @* @item -decision "yes"|"no" : If this test is reached then the evaluation ends immediately and action is performed if the decision is "yes" or "true". See operator -if. @* @c man \fB\-true\fR and \fB\-false\fR : @c man-ignore-lines 1 @item -true and -false : Always match or match not, respectively. Evaluation goes on. @* @item -sort_lba : Always match. This causes -find to perform its action in a sequence sorted by the ISO image block addresses of the files. It may improve throughput with actions which read data from optical drives. Action will always get the absolute path as parameter. @* Available operators are: @* @item -not : Matches if the next test or sub expression does not match. Several tests do this specifically: @* -undamaged, -lba_range with negative start_lba, -has_no_acl, -has_no_xattr, -has_no_aaip, -has_no_filter . @* @item -and : Matches if both neighboring tests or expressions match. @* @item -or : Matches if at least one of both neighboring tests or expressions matches. @* @c man \fB\-sub\fR ... \fB\-subend\fR or \fB(\fR ... \fB)\fR : @c man-ignore-lines 1 @item -sub ... -subend or ( ... ) : Enclose a sub expression which gets evaluated first before it is processed by neighboring operators. Normal precedence is: -not, -or , -and. @* @c man \fB\-if\fR ... \fB\-then\fR\ ... \fB\-elseif\fR ... \fB\-then\fR ... @c man \fB\-else\fR ... \fB\-endif\fR : @c man-ignore-lines 1 @item -if ... -then ... -elseif ... -then ... -else ... -endif : Enclose one or more sub expressions. If the -if expression matches, then the -then expression is evaluated as the result of the whole expression up to -endif. Else the next -elseif expression is evaluated and if it matches, its -then expression. Finally in case of no match, the -else expression is evaluated. There may be more than one -elseif. Neither -else nor -elseif are mandatory. If -else is missing and would be hit, then the result is a non-match. @* -if-expressions are the main use case for above test -decision. @end table @sp 1 Default action is @strong{echo}, i.e. to print the address of the found file. Other actions are certain @command{xorriso} commands which get performed on the found files. These commands may have specific parameters. See also their particular descriptions. @c man .br @table @asis @sp 1 @c man \fBchown\fR and \fBchown_r\fR @c man-ignore-lines 1 @item chown and chown_r change the ownership and get the user id as parameter. E.g.: -exec chown thomas @minus{}@minus{} @* @c man \fBchgrp\fR and \fBchgrp_r\fR @c man-ignore-lines 1 @item chgrp and chgrp_r change the group attribute and get the group id as parameter. E.g.: -exec chgrp_r staff @minus{}@minus{} @* @c man \fBchmod\fR and \fBchmod_r\fR @c man-ignore-lines 1 @item chmod and chmod_r change access permissions and get a mode string as parameter. E.g.: -exec chmod a-w,a+r @minus{}@minus{} @* @c man \fBalter_date\fR and \fBalter_date_r\fR @c man-ignore-lines 1 @item alter_date and alter_date_r change the timestamps. They get a type character and a timestring as parameters. @* E.g.: -exec alter_date "m" "Dec 30 19:34:12 2007" @minus{}@minus{} @* @c man \fBset_to_mtime\fR @c man-ignore-lines 1 @item set_to_mtime sets the ctime and atime to the value found in mtime. @* @item lsdl prints file information like shell command ls -dl. @* @item compare performs command -compare with the found file address as iso_rr_path and the corresponding file address below its parameter disk_path_start. For this the iso_rr_path of the -find command gets replaced by the disk_path_start. @* E.g.: -find /thomas -exec compare /home/thomas @minus{}@minus{} @* @item update performs command -update with the found file address as iso_rr_path. The corresponding file address is determined like with above action "compare". @* @item update_merge is like update but does not delete the found file if it is missing on disk. It may be run several times and records with all visited files whether their counterpart on disk has already been seen by one of the update_merge runs. Finally, a -find run with action "rm_merge" may remove all files that saw no counterpart on disk. @* Up to the next "rm_merge" or "clear_merge" all newly inserted files will get marked as having a disk counterpart. @* @item rm removes the found iso_rr_path from the image if it is not a directory with files in it. I.e. this "rm" includes "rmdir". @* @item rm_r removes the found iso_rr_path from the image, including whole directory trees. @* @item rm_merge removes the found iso_rr_path if it was visited by one or more previous actions "update_merge" and saw no counterpart on disk in any of them. The marking from the update actions is removed in any case. @* @item clear_merge removes an eventual marking from action "update_merge". @* @item report_damage classifies files whether they hit a data block that is marked as damaged. The result is printed together with the address of the first damaged byte, the maximum span of damages, file size, and the path of the file. @* @item report_lba prints files which are associated to image data blocks. It tells the logical block address, the block number, the byte size, and the path of each file. There may be reported more than one line per file if the file has more than one section. In this case each line has a different extent number in column "xt". @* @item report_sections like report_lba but telling the byte sizes of the particular sections rather than the overall byte size of the file. @* @item getfacl prints access permissions in ACL text form to the result channel. @* @item setfacl attaches ACLs after removing existing ones. The new ACL is given in text form as defined with command -setfacl. @* E.g.: -exec setfacl u:lisa:rw,u::rw,g::r,o::@minus{},m::rw @minus{}@minus{} @* @item getfattr prints xattr name-value pairs to the result channel. The choice of namespaces depends on the setting of command -xattr: "off", "on", or "user" restricts it to the namespace "user", "any" only omits namespace "isofs". @* @item get_any_xattr prints xattr name-value pairs from any namespace except ACL to the result channel. This is mostly for debugging of namespace "isofs". @* @item list_extattr mode prints a script to the result channel, which would use FreeBSD command setextattr to set the file's xattr name-value pairs of user namespace. Parameter mode controls the form of the output of names and values. Default mode "e" prints harmless characters in shell quotation marks, but represents texts with octal 001 to 037 and 0177 to 0377 by an embedded echo -e command. Mode "q" prints any characters in shell quotation marks. This might not be terminal-safe but should work in script files. Mode "r" uses no quotation marks. Not safe. Mode "b" prints backslash encoding. Not suitable for shell parsing. @* E.g.: -exec list_extattr e -- @* Command -backslash_codes does not affect the output. @* @item lsattrd shows the Linux file attribute flags like command -lsattrd does. @* @item chattr applies -chattr with the given mode. Other than command -chattr this silently skips any file which are not -type "dir" or "file". @* E.g.: -exec chattr +sDu -- @* @item get_md5 prints the MD5 sum, if recorded, together with file path. @* @item check_md5 compares the MD5 sum, if recorded, with the file content and reports if mismatch. @* E.g.: -find / -not -pending_data -exec check_md5 FAILURE @minus{}@minus{} @* @item make_md5 equips a data file with an MD5 sum of its content. Useful to upgrade the files in the loaded image to full MD5 coverage by the next commit with -md5 "on". @* E.g.: -find / -type f -not -has_md5 -exec make_md5 @minus{}@minus{} @* @item setfattr sets or deletes xattr name value pairs. @* E.g.: -find / -has_xattr -exec setfattr @minus{}@minus{}remove-all '' @minus{}@minus{} @* @item set_hfs_crtp adds, changes, or removes HFS+ creator and type attributes. @* E.g.: -exec set_hfs_crtp YYDN TEXT @* E.g.: -find /my/dir -prune -exec set_hfs_crtp --delete - @* @item get_hfs_crtp prints the HFS+ creator and type attributes together with the iso_rr_path, if the file has such attributes at all. @* E.g.: -exec get_hfs_crtp @* @item set_hfs_bless applies or removes HFS+ blessings. They are roles which can be attributed to up to four directories and a data file: @* "ppc_bootdir", "intel_bootfile", "show_folder", "os9_folder", "osx_folder". @* They may be abbreviated as "p", "i", "s", "9", and "x". @* Each such role can be attributed to at most one file object. "intel_bootfile" is the one that would apply to a data file. All others apply to directories. The -find run will end as soon as the first blessing is issued. The previous bearer of the blessing will lose it then. No file object can bear more than one blessing. @* E.g.: -find /my/blessed/directory -exec set_hfs_bless p @* Further there is blessing "none" or "n" which revokes any blessing from the found files. This -find run will not stop when the first match is reached. @* E.g.: -find / -has_hfs_bless any -exec set_hfs_bless none @* @item get_hfs_bless prints the HFS+ blessing role and the iso_rr_path, if the file is blessed at all. @* E.g.: -exec get_hfs_bless @* @item set_filter applies or removes filters. @* E.g.: -exec set_filter @minus{}@minus{}zisofs @minus{}@minus{} @* @item mkisofs_r applies the rules of mkisofs -r to the file object: @* user id and group id become 0, all r-permissions get granted, all w denied. If there is any x-permission, then all three x get granted. s- and t-bits get removed. @* @item sort_weight attributes a LBA weight number to regular files. @* The number may range from -2147483648 to 2147483647. The higher it is, the lower will be the block address of the file data in the emerging ISO image. Currently the boot catalog has a hardcoded weight of 1 billion. Normally it should occupy the block with the lowest possible address. @* Data files which are loaded by -indev or -dev get a weight between 1 and 2 exp 28 = 268,435,456, depending on their block address. This shall keep them roughly in the same order if the write method of modifying is applied. @* Data files which are added by other commands get an initial weight of 0. Boot image files have a default weight of 2. @* E.g.: -exec sort_weight 3 @minus{}@minus{} @* @item show_stream shows the content stream chain of a data file. @* @item show_stream_id is like show_stream, but also prints between stream type and first ":" in square brackets libisofs id numbers: [fs_id,dev_id,ino_id]. @* @item hide brings the file into one of the hide states "on", "iso_rr", "joliet", "hfsplus", "off". They may be combined. E.g.: joliet:hfsplus @* E.g.: @* -find / -disk_name *_secret -exec hide on @* @item print_outname prints in the first line the filename as registered by the program model, and in the second line the filename after conversion forth and back between local character set and one of the namespaces "rockridge", "joliet", "ecma119", or "hfsplus". The third output line is "--" . @* The name conversion does not take into respect the possibility of name collisions in the target namespace. Such collisions are most likely in "joliet" and "ecma119", where they get resolved by automatic file name changes. @* E.g.: @* -find / -bad_outname joliet -exec print_outname joliet @* @item estimate_size prints a lower and an upper estimation of the number of blocks which the found files together will occupy in the emerging ISO image. This does not account for the superblock, for the directories in the -find path, or for image padding. @* @item find performs another run of -find on the matching file address. It accepts the same params as -find, except iso_rr_path. @* E.g.: @* -find / -name '???' -type d -exec find -name '[abc]*' -exec chmod a-w,a+r @minus{}@minus{} @end table @end table @c man .TP @c man .B Filters for data file content: @c man .PP @node Filter, Writing, CmdFind, Commands @section Filters for data file content @cindex Filter, _definition @strong{Filters} may be installed between data files in the ISO image and their content source outside the image. They may also be used vice versa between data content in the image and target files on disk. @* @sp 1 Built-in filters are "@minus{}@minus{}zisofs" and "@minus{}@minus{}zisofs-decode". The former is to be applied via -set_filter, the latter is automatically applied if zisofs compressed content is detected with a file when loading the ISO image. @* Another built-in filter pair is "@minus{}@minus{}gzip" and "@minus{}@minus{}gunzip" with suffix ".gz". They behave about like external gzip and gunzip but avoid forking a process for each single file. So they are much faster if there are many small files. @c man .PP @table @asis @sp 1 @c man .TP @item -external_filter name option[:option] program_path [arguments] @minus{}@minus{} @kindex -external_filter registers data filter @cindex Filter, register, -external_filter Register a content filter by associating a name with a program path, program arguments, and some behavioral options. Once registered it can be applied to multiple data files in the ISO image, regardless whether their content resides in the loaded ISO image or in the local filesystem. External filter processes may produce synthetic file content by reading the original content from stdin and writing to stdout whatever they want. They must deliver the same output on the same input in repeated runs. @* Options are: @* "default" means that no other option is intended. @* "suffix=..." sets a file name suffix. If it is not empty then it will be appended to the file name or removed from it. @* "remove_suffix" will remove a file name suffix rather than appending it. @* "if_nonempty" will leave 0-sized files unfiltered. @* "if_reduction" will try filtering and revoke it if the content size does not shrink. @* "if_block_reduction" will revoke if the number of 2 kB blocks does not shrink. @* "used=..." is ignored. Command -status shows it with the number of files which currently have the filter applied. @* Examples: @* -external_filter bzip2 suffix=.bz2:if_block_reduction \ @* /usr/bin/bzip2 @minus{}@minus{} @* -external_filter bunzip2 suffix=.bz2:remove_suffix \ @* /usr/bin/bunzip2 @minus{}@minus{} @c man .TP @item -unregister_filter name @kindex -external_filter unregisters data filter @cindex Filter, unregister, -unregister_filter Remove an -external_filter registration. This is only possible if the filter is not applied to any file in the ISO image. @c man .TP @item -close_filter_list @kindex -close_filter_list bans filter registration @cindex Filter, ban registration, -close_filter_list Irrevocably ban commands -concat "pipe", -external_filter, and -unregister_filter, but not -set_filter. Use this to prevent external filtering in general or when all intended filters are registered and -concat mode "pipe" shall be disallowed. External filters may also be banned totally at compile time of @command{xorriso}. By default they are banned if @command{xorriso} runs under setuid permission. @c man .TP @item -set_filter name iso_rr_path [***] @kindex -set_filter applies filter to file @cindex Filter, apply to file, -set_filter Apply an -external_filter or a built-in filter to the given data files in the ISO image. If the filter suffix is not empty , then it will be applied to the file name. Renaming only happens if the filter really gets attached and is not revoked by its options. By default files which already bear the suffix will not get filtered. The others will get the suffix appended to their names. If the filter has option "remove_suffix", then the filter will only be applied if the suffix is present and can be removed. Name oversize or collision caused by suffix change will prevent filtering. @* With most filter types this command will immediately run the filter once for each file in order to determine the output size. Content reading operations like -extract , -compare and image generation will perform further filter runs and deliver filtered content. @* At image generation time the filter output must still be the same as the output from the first run. Filtering for image generation does not happen with files from the loaded ISO image if the write method of growing is in effect (i.e -indev and -outdev are identical). @* The reserved filter name "@minus{}@minus{}remove-all-filters" revokes filtering. This will revoke suffix renamings as well. Use "@minus{}@minus{}remove-all-filters+" to prevent any suffix renaming. @* Attaching or detaching filters will not alter the state of -changes_pending. If the filter manipulations shall be the only changes in a write run, then explicitly execute -changes_pending "yes". @c man .TP @item -set_filter_r name iso_rr_path [***] @kindex -set_filter_r applies filter to file tree @cindex Filter, apply to file tree, -set_filter_r Like -set_filter but affecting all data files below eventual directories. @end table @c man .TP @c man .B Writing the result, drive control: @node Writing, SetWrite, Filter, Commands @section Writing the result, drive control @c man .PP (see also paragraph about settings below) @table @asis @sp 1 @c man .TP @item -rollback @kindex -rollback discards pending changes @cindex Image, discard pending changes, -rollback Discard the manipulated ISO image and reload it from -indev. (Use -rollback_end if immediate program end is desired.) @c man .TP @item -changes_pending "no"|"yes"|"mkisofs_printed"|"show_status" @kindex -changes_pending overrides change status @cindex Image, override change status, -changes_pending Write runs are performed only if a change of the image has been made since the image was loaded or created blank. Vice versa the program will start a write run for pending changes when it ends normally (i.e. not by abort and not by command -rollback_end). @* The command -changes_pending can be used to override the automatically determined state. This is mainly useful for setting state "yes" despite no real changes were made. The sequence -changes_pending "no" -end is equivalent to the command -rollback_end. State "mkisofs_printed" is caused by emulation command -as mkisofs if option -print-size is present. @* The pseudo-state "show_status" can be used to print the current state to result channel. @* Image loading or manipulations which happen after this command will again update automatically the change status of the image. @c man .TP @item -commit @kindex -commit writes pending ISO image @cindex Write, pending ISO image, -commit Perform the write operation. Afterwards, if -outdev is readable, make it the new -dev and load the image from there. Switch to growing mode. (A subsequent -outdev will activate modification mode or blind growing.) -commit is performed automatically at end of program if there are uncommitted manipulations pending. @* So, to perform a final write operation with no new -dev and no new loading of image, rather execute command -end. If you want to go on without image loading, execute -commit_eject "none". To eject after write without image loading, use -commit_eject "all". @* To suppress a final write, execute -rollback_end. @* Writing can last quite a while. It is not unnormal with several types of media that there is no progress visible for the first few minutes or that the drive gnaws on the medium for a few minutes after all data have been transmitted. @command{xorriso} and the drives are in a client-server relationship. The drives have much freedom about what to do with the media. Some combinations of drives and media simply do not work, despite the promises by their vendors. If writing fails then try other media or another drive. The reason for such failure is hardly ever in the code of the various burn programs but you may well try some of those listed below under SEE ALSO. @c man .TP @item -eject "in"|"out"|"all" @kindex -eject ejects drive tray @cindex Drive, eject tray, -eject Eject the medium in -indev, -outdev, or both drives, respectively. Note: It is not possible yet to effectively eject disk files. @c man .TP @item -commit_eject "in"|"out"|"all"|"none" @kindex -commit_eject writes and ejects @cindex Drive, write and eject, -commit_eject Combined -commit and -eject. When writing has finished do not make -outdev the new -dev, and load no ISO image. Rather eject -indev and/or -outdev. Give up any non-ejected drive. @c man .TP @item -blank mode @kindex -blank erases media @cindex Media, erase, -blank Make media ready for writing from scratch (if not -dummy is activated). @* This affects only the -outdev not the -indev. If both drives are the same and if the ISO image was altered then this command leads to a FAILURE event. Defined modes are: as_needed, fast, all, deformat, deformat_quickest @* "as_needed" cares for used CD-RW, DVD-RW and for used overwritable media by applying -blank "fast". It applies -format "full" to yet unformatted DVD-RAM and BD-RE. Other media in blank state are gracefully ignored. Media which cannot be made ready for writing from scratch cause a FAILURE event. @* "fast" makes CD-RW and unformatted DVD-RW re-usable, or invalidates overwritable ISO images. "all" might work more thoroughly and need more time. @* "deformat" converts overwritable DVD-RW into unformatted ones. @* "deformat_quickest" is a faster way to deformat or blank DVD-RW but produces media which are only suitable for a single session. Some drives announce this state by not offering feature 21h, but some drives offer it anyway. If feature 21h is missing, then @command{xorriso} will refuse to write on DVD-RW if not command -close is set to "on". @* The progress reports issued by some drives while blanking are quite unrealistic. Do not conclude success or failure from the reported percentages. Blanking was successful if no SORRY event or worse occurred. @* Mode may be prepended by "force:" in order to override the evaluation of the medium state by libburn. E.g. "force:fast". Blanking will nevertheless only succeed if the drive is willing to do it. @* @c man .TP @item -format mode @kindex -format formats media @cindex Media, format, -format Convert unformatted DVD-RW into overwritable ones, "de-ice" DVD+RW, format newly purchased BD-RE or BD-R, re-format DVD-RAM or BD-RE. @* Defined modes are: @* as_needed, full, fast, by_index_<num>, fast_by_index_<num>, by_size_<num>, fast_by_size_<num>, without_spare @* "as_needed" formats yet unformatted DVD-RW, DVD-RAM, BD-RE, or blank unformatted BD-R. Other media are left untouched. @* "full" (re-)formats DVD-RW, DVD+RW, DVD-RAM, BD-RE, or blank unformatted BD-R. @* "fast" does the same as "full" but tries to be quicker. @* "by_index_" selects a format out of the descriptor list issued by command -list_formats. The index number from that list is to be appended to the mode word. E.g: "by_index_3". @* "fast_by_index_" does the same as "by_index_" but tries to be quicker. @* "by_size_" selects a format out of the descriptor list which provides at least the given size. That size is to be appended to the mode word. E.g: "by_size_4100m". This applies to media with Defect Management. On BD-RE it will not choose format 0x31, which offers no Defect Management. @* "fast_by_size_" does the same as "by_size_" but tries to be quicker. @* "without_spare" selects the largest format out of the descriptor list which provides no Spare Area for Defect Management. On BD-RE this will be format 0x31. @* The formatting action has no effect on media if -dummy is activated. @* Formatting is normally needed only once during the lifetime of a medium, if ever. But it is a reason for re-formatting if: @* DVD-RW was deformatted by -blank, @* DVD+RW has read failures (re-format before next write), @* DVD-RAM or BD-RE shall change their amount of defect reserve. @* BD-R may be written unformatted or may be formatted before first use. Formatting activates Defect Management which tries to catch and repair bad spots on media during the write process at the expense of half speed even with flawless media. @* The progress reports issued by some drives while formatting are quite unrealistic. Do not conclude success or failure from the reported percentages. Formatting was successful if no SORRY event or worse occurred. Be patient with apparently frozen progress. @c man .TP @item -list_formats @kindex -list_formats lists available formats @cindex Media, list formats, -list_formats Put out a list of format descriptors as reported by the output drive for the current medium. The list gives the index number after "Format idx", a MMC format code, the announced size in blocks (like "2236704s") and the same size in MiB. @* MMC format codes are manifold. Most important are: "00h" general formatting, "01h" increases reserve space for DVD-RAM, "26h" for DVD+RW, "30h" for BD-RE with reserve space, "31h" for BD-RE without reserve space, "32h" for BD-R. @* Smaller format size with DVD-RAM, BD-RE, or BD-R means more reserve space. @c man .TP @item -list_speeds @kindex -list_speeds lists available write speeds @cindex Media, list write speeds, -list_speeds Put out a list of speed values as reported by the drives with the loaded media. The list tells read speeds of the input drive and of the output drive. Further it tells write speeds of the output drive. @* The list of write speeds does not necessarily mean that the medium is writable or that these speeds are actually achievable. Especially the lists reported with empty drive or with ROM media obviously advertise speeds for other media. @* It is not mandatory to use speed values out of the listed range. The drive is supposed to choose a safe speed that is as near to the desired speed as possible. @* At the end of the list, "Write speed L" and "Write speed H" are the best guesses for lower and upper write speed limit. "Write speed l" and "Write speed h" may appear only with CD and eventually override the list of other speed offers. @* Only if the drive reports contradicting speed information there will appear "Write speed 0", which tells the outcome of speed selection by command -speed 0, if it deviates from "Write speed H". @* "Read speed L" and "Read speed H" tell the minimum and maximum read speeds, as reported by the drive. They would be chosen by -read_speed "min" or "max" if they undercut or surpass the built-in limits. These are "1x", "52xCD", "24xDVD", "20xBD". @c man .TP @item -list_profiles "in"|"out"|"all" @kindex -list_profiles lists supported media @cindex Drive, list supported media, -list_profiles Put out a list of media types supported by -indev, -outdev, or both, respectively. The currently recognized type is marked by text "(current)". @c man .TP @item -truncate_overwritable entity id adjust @kindex -truncate_overwritable activates older session @cindex Older session, activate, -truncate_overwritable On overwritable medium copy the volume descriptors of an existing session to the overall descriptors at LBA 0 ff. This makes all sessions @strong{inaccessible} which are younger than the activated one. A reason to do this would be read errors in the younger sessions and the wish to re-write or skip them. @* This operation is only allowed if no changes to the loaded filesystem are pending. If an -indev is acquired then it is released before the write operation begins and re-acquired only in case of success. @* The parameters "entity" and "id" have the same meaning as with command -load. They choose the existing ISO session which shall become the youngest accessible session. Available entity names are "session", "track", "lba", "sbsector", "volid". "auto" makes few sense. id is a number or search text as appropriate for the given entity. @* Parameter "adjust" controls the claimed size of the activated session. Text "new" means the size of the newly activated session as it was before this command. I.e. the space of the then inaccessible younger sessions will be re-used when appending more sessions. @* "old" means the size up to the end of the previously youngest session. I.e. "old" will not free the space of the then inaccessible younger sessions for re-use. @* A number preceded by "+" gives the number of bytes to be added to "new". A number without "+" gives the overall number of bytes. In any case the result may not be smaller than "new". Numbers may have a unit suffix: "d"=512, "k"=1024, "s"=2048, "m"=1024k, "g"=1024m. @* Normally the volume descriptors at block 16 ff. have to be readable. Only with entity "lba" or "sbsector" and adjust mode "new" it is possible to address a session if block 16 ff. yields no valid volume descriptors. @* Examples: @* Activate session 4 and enable overwriting of the blocks of younger sessions: @* -truncate_overwritable session 4 new @* Activate session 4 and claim the blocks of younger sessions as useless part of session 4: @* -truncate_overwritable session 4 old @* Let session 4 claim additional 500 MiB as useless data: @* -truncate_overwritable session 4 +500m @c man .TP @item -close_damaged "as_needed"|"force" @kindex -close_damaged closes damaged track and session @cindex Damaged track and session, close, -close_damaged Try to close the upcoming track and session if the drive reported the medium as damaged. This may apply to CD-R, CD-RW, DVD-R, DVD-RW, DVD+R, DVD+R DL, or BD-R media. It is indicated by warning messages when the drive gets acquired, and by a remark "but next track is damaged" with the line "Media status :" of command -toc. @* The setting of command -close determines whether the medium stays appendable. @* Mode "as_needed" gracefully refuses on media which are not reported as damaged. Mode "force" attempts the close operation even with media which appear undamaged. @* No image changes are allowed to be pending before this command is performed. After closing was attempted, both drives are given up. @end table @c man .TP @c man .B Settings for result writing: @node SetWrite, Bootable, Writing, Commands @section Settings for result writing @c man .PP Rock Ridge info will be generated by default. ACLs will be written according to the setting of command -acl. @table @asis @sp 1 @c man .TP @item -joliet "on"|"off" @kindex -joliet enables production of Joliet tree @cindex Write, enable Joliet, -joliet If enabled by "on", generate Joliet tree additional to ISO 9660 + Rock Ridge tree. @sp 1 @c man .TP @item -hfsplus "on"|"off" @kindex -hfsplus enables production of HFS+ partition @cindex Write, enable HFS+, -hfsplus If enabled by "on", generate a HFS+ filesystem inside the ISO 9660 image and mark it by Apple Partition Map (APM) entries in the System Area, the first 32 KiB of the image. @* This may collide with data submitted by -boot_image system_area=. The first 8 bytes of the System Area get overwritten by @{ 0x45, 0x52, 0x08 0x00, 0xeb, 0x02, 0xff, 0xff @} which can be executed as x86 machine code without negative effects. So if an MBR gets combined with this feature, then its first 8 bytes should contain no essential commands. @* The next blocks of 2 KiB in the System Area will be occupied by APM entries. The first one covers the part of the ISO image before the HFS+ filesystem metadata. The second one marks the range from HFS+ metadata to the end of file content data. If more ISO image data follow, then a third partition entry gets produced. Other features of xorriso might cause the need for more APM entries. @* The HFS+ filesystem is not suitable for add-on sessions produced by the multi-session method of growing. An existing ISO image may nevertheless be the base for a new image produced by the method of modifying. If -hfsplus is enabled when -indev or -dev gets executed, then AAIP attributes get loaded from the input image and checked for information about HFS creator, filetype, or blessing. If found, then they get enabled as settings for the next image production. Therefore it is advisable to perform -hfsplus "on" before -indev or -dev. @* Information about HFS creator, type, and blessings gets stored by xorriso if -hfsplus is enabled at -commit time. It is stored as copy outside the HFS+ partition, but rather along with the Rock Ridge information. xorriso does not read any information from the HFS+ meta data. @* Be aware that HFS+ is case-insensitive although it can record file names with upper-case and lower-case letters. Therefore, file names from the iso_rr name tree may collide in the HFS+ name tree. In this case they get changed by adding underscore characters and counting numbers. In case of very long names, it might be necessary to map them to "MANGLED_...". @* WARNING: @* The HFS+ implementation in libisofs has a limit of 125,829,120 bytes for the size of the overall directory tree. This suffices for about 300,000 files of normal name length. If the limit gets exceeded, a FAILURE event will be issued and the ISO production will not happen. @sp 1 @c man .TP @item -rockridge "on"|"off" @kindex -rockridge disables production of Rock Ridge info @cindex Write, disable Rock Ridge, -rockridge Mode "off" disables production of Rock Ridge information for the ISO 9660 file objects. The multi-session capabilities of xorriso depend much on the naming fidelity of Rock Ridge. So it is strongly discouraged to deviate from default setting "on". @c man .TP @item -compliance rule[:rule...] @kindex -compliance controls standard compliance @cindex Write, compliance to specs, -compliance Adjust the compliance to specifications of ISO 9660/ECMA-119 and its contemporary extensions. In some cases it is worth to deviate a bit in order to circumvent bugs of the intended reader system or to get unofficial extra features. @* There are several adjustable rules which have a keyword each. If they are mentioned with this command then their rule gets added to the relaxation list. This list can be erased by rules "strict" or "clear". It can be reset to its start setting by "default". All of the following relaxation rules can be revoked individually by appending "_off". Like "deep_paths_off". @* Rule keywords are: @* "iso_9660_level="number chooses level 1 with ECMA-119 names of the form 8.3 and -file_size_limit <= 4g - 1, or level 2 with ECMA-119 names up to length 32 and the same -file_size_limit, or level 3 with ECMA-119 names up to length 32 and -file_size_limit >= 400g -200k. If necessary -file_size_limit gets adjusted. @* "allow_dir_id_ext" allows ECMA-119 names of directories to have a name extension as with other file types. It does not force dots and it omits the version number, though. This is a bad tradition of mkisofs which violates ECMA-119. Especially ISO level 1 only allows 8 characters in a directory name and not 8.3. @* "omit_version" does not add versions (";1") to ECMA-119 and Joliet file names. @* "only_iso_version" does not add versions (";1") to Joliet file names. @* "deep_paths" allows ECMA-119 file paths deeper than 8 levels. @* "long_paths" allows ECMA-119 file paths longer than 255 characters. @* "long_names" allows up to 37 characters with ECMA-119 file names. @* "no_force_dots" does not add a dot to ECMA-119 file names which have none. @* "no_j_force_dots" does not add a dot to Joliet file names which have none. @* "lowercase" allows lowercase characters in ECMA-119 file names. @* "7bit_ascii" allows nearly all 7-bit characters in ECMA-119 file names. Not allowed are 0x0 and '/'. If not "lowercase" is enabled, then lowercase letters get converted to uppercase. @* "full_ascii" allows all 8-bit characters except 0x0 and '/' in ECMA-119 file names. @* "untranslated_names" might be dangerous for inadverted reader programs which rely on the restriction to at most 37 characters in ECMA-119 file names. This rule allows ECMA-119 file names up to 96 characters with no character conversion. If a file name has more characters, then image production will fail deliberately. @* "untranslated_name_len="number enables untranslated_names with a smaller limit for the length of file names. 0 disables this feature, -1 chooses maximum length limit, numbers larger than 0 give the desired length limit. @* "joliet_long_names" allows Joliet leaf names up to 103 characters rather than 64. @* "joliet_long_paths" allows Joliet paths longer than 240 characters. @* @cindex UTF-16, for Joliet paths, -compliance "joliet_utf16" encodes Joliet names in UTF-16BE rather than UCS-2. The difference is with characters which are not present in UCS-2 and get encoded in UTF-16 by 2 words of 16 bit each. Both words then stem from a reserved subset of UCS-2. @* "always_gmt" stores timestamps in GMT representation with timezone 0. @* "rec_mtime" records with non-RockRidge directory entries the disk file's mtime and not the creation time of the image. This applies to the ECMA-119 tree (plain ISO 9660), to Joliet, and to ISO 9660:1999. "rec_mtime" is default. If disabled, it gets automatically re-enabled by -as mkisofs emulation when a pathspec is encountered. @* "new_rr" uses Rock Ridge version 1.12 (suitable for GNU/Linux but not for older FreeBSD or for Solaris). This implies "aaip_susp_1_10_off" which may be changed by subsequent "aaip_susp_1_10". @* Default is "old_rr" which uses Rock Ridge version 1.10. This implies also "aaip_susp_1_10" which may be changed by subsequent "aaip_susp_1_10_off". @* "aaip_susp_1_10" allows AAIP to be written as unofficial extension of RRIP rather than as official extension under SUSP-1.12. @* "no_emul_toc" saves 64 kB with the first session on overwritable media but makes the image incapable of displaying its session history. @* "iso_9660_1999" causes the production of an additional directory tree compliant to ISO 9660:1999. It can record long filenames for readers which do not understand Rock Ridge. @* "old_empty" uses the old way of of giving block addresses in the range of [0,31] to files with no own data content. The new way is to have a dedicated block to which all such files will point. @* "max_ce_entries="number sets the maximum number of SUSP CE entries and thus continuation areas. Each continuation area can hold at most 2048 bytes of SUSP data (Rock Ridge or AAIP). The first area can be smaller. There might be some waste at the end of each area. When the maximum number is exceeded during ISO filesystem production then either xattr and ACL get dropped from the affected file or an error gets reported and image production is prevented. @* Linux silently ignores a file when encountering its 32th CE entry. (Workaround is to mount the filesystem with option "norock".) So the default setting is 31. Minimum is 1, maximum is 100000. If a limit higher than 31 is chosen and 31 gets surpassed, then a warning message gets reported. @* "max_ce_drop="mode sets the behavior when the limit of max_ce_entries= is surpassed. Mode "off" causes an error message and prevents image production. Mode "xattr" and "xattr_acl" report a warning, delete from the affected file all xattr of namespaces other than "isofs", and then try again. If this still surpasses the limit, then mode "xattr_acl" deletes all ACL from the file and retries. If this still surpasses the limit, then an error message gets reported and image production is prevented. @* Default setting is @* "clear:iso_9660_level=3:only_iso_version:deep_paths:long_paths: @* no_j_force_dots:always_gmt:rec_mtime:old_rr:max_ce_entries=31: @* max_ce_drop=xattr_acl" @* Note: The term "ECMA-119 name" means the plain ISO 9660 names and attributes which get visible if the reader ignores Rock Ridge. @c man .TP @item -rr_reloc_dir name @kindex -rr_reloc_dir sets name of relocation directory @cindex Relocation directory, set name, -rr_reloc_dir Specify the name of the relocation directory in which deep directory subtrees shall be placed if -compliance is set to "deep_paths_off" or "long_paths_off". A deep directory is one that has a chain of 8 parent directories (including root) above itself, or one that contains a file with an ECMA-119 path of more than 255 characters. @* The overall directory tree will appear originally deep when interpreted as Rock Ridge tree. It will appear as re-arranged if only ECMA-119 information is considered. @* The default relocation directory is the root directory. By giving a non-empty name with -rr_reloc_dir, a directory in the root directory may get this role. If that directory does not already exist at -commit time, then it will get created and marked for Rock Ridge as relocation artefact. At least on GNU/Linux it will not be displayed in mounted Rock Ridge images. @* The name must not contain a '/' character and must not be longer than 255 bytes. @c man .TP @item -volid text @kindex -volid sets volume id @cindex Image, set volume id, -volid Specify the volume ID, which most operating systems will consider to be the volume name of the image or medium. @* @command{xorriso} accepts any text up to 32 characters, but according to rarely obeyed specs stricter rules apply: @* ECMA-119 demands ASCII characters out of [A-Z0-9_]. Like: @* "IMAGE_23" @* Joliet allows 16 UCS-2 characters. Like: @* "Windows name" @* Be aware that the volume id might get used automatically as the name of the mount point when the medium is inserted into a playful computer system. @* If an ISO image gets loaded while the volume ID is set to default "ISOIMAGE" or to "", then the volume ID of the loaded image will become the effective volume id for the next write run. But as soon as command -volid is performed afterwards, this pending ID is overridden by the new setting. @* Consider this when setting -volid "ISOIMAGE" before executing -dev, -indev, or -rollback. If you insist in -volid "ISOIMAGE", set it again after those commands. @c man .TP @item -volset_id text @kindex -volset_id sets volume set id @cindex Image, set volume set id, -volset_id Set the volume set ID string to be written with the next -commit. Permissible are up to 128 characters. This setting gets overridden by image loading. @c man .TP @item -publisher text @kindex -publisher sets publisher id @cindex Image, set publisher id, -publisher Set the publisher ID string to be written with the next -commit. This may identify the person or organisation who specified what shall be recorded. Permissible are up to 128 characters. This setting gets overridden by image loading. @c man .TP @item -application_id text @kindex -application_id sets application id @cindex Image, set application id, -application_id Set the application ID string to be written with the next -commit. This may identify the specification of how the data are recorded. Permissible are up to 128 characters. This setting gets overridden by image loading. @* The special text "@@xorriso@@" gets converted to the ID string of @command{xorriso} which is normally written as -preparer_id. It is a wrong tradition to write the program ID as -application_id. @c man .TP @item -system_id text @kindex -system_id sets system id @cindex Image, set system id, -system_id Set the system ID string to be written with the next -commit. This may identify the system which can recognize and act upon the content of the System Area in image blocks 0 to 15. Permissible are up to 32 characters. This setting gets overridden by image loading. @c man .TP @item -volume_date type timestring @kindex -volume_date sets volume timestamp @cindex Image, set volume timestamp, -volume_date Set one of the four overall timestamps for subsequent image writing. Available types are: @* "c" time when the volume was created. @* "m" time when volume was last modified. @* "x" time when the information in the volume expires. @* "f" time since when the volume is effectively valid. @* "all_file_dates" sets mtime, atime, and ctime of all files and directories to the given time. If the timestring is "set_to_mtime", then the atime and ctime of each file and directory get set to the value found in their mtime. @* These actions stay delayed until actual ISO production begins. Up to then they can be revoked by "all_file_dates" with empty timestring or timestring "default". @* The timestamps of the El Torito boot catalog file get refreshed when the ISO is produced. They can be influenced by "uuid". @* "uuid" sets a timestring that overrides "c" and "m" times literally and sets the time of the El Torito boot catalog. It must consist of 16 decimal digits which form YYYYMMDDhhmmsscc, with YYYY between 1970 and 2999. Time zone is GMT. It is supposed to match this GRUB line: @* search @minus{}@minus{}fs-uuid @minus{}@minus{}set YYYY-MM-DD-hh-mm-ss-cc @* E.g. 2010040711405800 is 7 Apr 2010 11:40:58 (+0 centiseconds). @* Timestrings for the other types may be given as with command -alter_date. Some of them are prone to timezone computations. The timestrings "default" or "overridden" cause default settings: "c" and "m" will show the current time of image creation. "x" and "f" will be marked as insignificant. "uuid" will be deactivated. @* At -commit time, some timestamps get set to the maximum value of effectively written volume creation and modification time: El Torito boot catalog, HFS+ superblock, ECMA-119 file modification time if -compliance "no_rec_mtime". The isohybrid MBR id is computed from "uuid" if given, else from the effective volume modification date. @c man .TP @item -copyright_file text @kindex -copyright_file sets copyright file name @cindex Image, set copyright file name, -copyright_file Set the copyright file name to be written with the next -commit. This should be the ISO 9660 path of a file in the image which contains a copyright statement. Permissible are up to 37 characters. This setting gets overridden by image loading. @c man .TP @item -abstract_file text @kindex -abstract_file sets abstract file name @cindex Image, set abstract file name, -abstract_file Set the abstract file name to be written with the next -commit. This should be the ISO 9660 path of a file in the image which contains an abstract statement about the image content. Permissible are up to 37 characters. This setting gets overridden by image loading. @c man .TP @item -biblio_file text @kindex -biblio_file sets biblio file name @cindex Image, set biblio file name, -biblio_file Set the biblio file name to be written with the next -commit. This should be the ISO 9660 path of a file in the image which contains bibliographic records. Permissible are up to 37 characters. This setting gets overridden by image loading. @c man .TP @item -preparer_id text @kindex -preparer_id sets preparer id @cindex Image, set preparer id, -preparer_id Set the preparer ID string to be written with the next -commit. This may identify the person or other entity which controls the preparation of the data which shall be recorded. Normally this should be the ID of @command{xorriso} and not of the person or program which operates @command{xorriso}. Please avoid to change it. Permissible are up to 128 characters. @* The special text "@@xorriso@@" gets converted to the ID string of @command{xorriso} which is default at program startup. @* Unlike other ID strings, this setting is not influenced by image loading. @c man .TP @item -application_use character|0xXY|disk_path @kindex -application_use sets application use field @cindex Image, set application iuse field, -application_use Specify the content of the Application Use field which can take at most 512 bytes. @* If the parameter of this command is empty, then the field is filled with 512 0-bytes. If it is a single character, then it gets repeated 512 times. If it begins by "0x" followed by two hex digits [0-9a-fA-F], then the digits are read as byte value which gets repeated 512 times. @* Any other parameter text is used as disk_path to open a data file and to read up to 512 bytes from it. If the file is smaller than 512 bytes, then the remaining bytes in the field get set to binary 0. @* This setting is not influenced by image loading. @c man .TP @item -out_charset character_set_name @kindex -out_charset sets output character set @cindex Character Set, for output, -out_charset Set the character set to which file names get converted when writing an image. See paragraph "Character sets" for more explanations. When loading the written image after -commit the setting of -out_charset will be copied to -in_charset. @c man .TP @item -uid uid @kindex -uid sets global ownership @cindex Ownership, global in ISO image, -uid User id to be used for all files when the new ISO tree gets written to media. @c man .TP @item -gid gid @kindex -gid sets global ownership @cindex Group, global in ISO image, -gid Group id to be used for all files when the new ISO tree gets written to media. @c man .TP @item -zisofs parameter[:parameters] @kindex -zisofs controls zisofs production @cindex Filter, zisofs parameters, -zisofs Set global parameters for zisofs compression. This data format is recognized and transparently uncompressed by some Linux kernels. It is to be applied via command -set_filter with built-in filter "@minus{}@minus{}zisofs". @* Note: This command is only permitted while no --zisofs filters are applied to any files. @* Parameters are: @* "level="[0-9] zlib compression: 0=none, 1=fast,..., 9=slow @* "block_size="32k|64k|128k sets the size of version 1 compression blocks. @* "by_magic=on" enables an expensive test at image generation time which checks files from disk whether they already are zisofs compressed, e.g. by program mkzftree. "by_magic=v2" enables processing of already zisofs2 compressed files additionally to those of zisofs version 1. "by_magic=off" disables both. @* "version_2="off|as_needed|on controls compression by experimental version zisofs2 which can encode files of size 4 GiB or larger. The Linux kernel (as of 5.9) does not yet know this format and will complain like @* isofs: Unknown ZF compression algorithm: PZ @* The files will then appear in their compressed form with zisofs2 header, block pointer list, and compressed data. @* zisofs2 is recognized by xorriso in files from loaded images and gets equipped with --zisofs-decode filters, unless restrictions on the number of block pointers prevent this. @* Mode "off" restricts compression to files smaller than 4 GiB uncompressed size. Mode "as_needed" uses zisofs2 for larger files. Mode "on" uses zisofs2 for all zisofs compressed files. @* "susp_z2="off|on controls production of SUSP entries "Z2" instead of "ZF" with zisofs2 compressed files. Unaware Linux kernels are supposed to silently ignore "Z2" entries. @* "block_size_v2="32k|64k|128k|256k|512k|1m sets the size of compression blocks for zisofs2. @* "bpt_target="-1|>0 sets a number of block pointers per file, which is considered low enough to justify a reduction of block size. If this number is larger than 0, then block sizes smaller than the settings of block_size= or block_size_v2= are tried whether they yield not more block pointers than the given number. If so, the smallest suitable block size is applied. @* The inavoidable final block pointer counts. E.g. a file of 55 KiB has 3 block pointers if block size is 32k, and 2 block pointers with block size 64k. @* bpt_target=-1 disables this automatic block size adjustment. @* "max_bpt="1k...128g sets the limit for the overall allocated block pointer memory. Block pointers occupy virtual memory while a file gets uncompressed and while a file, which shall be compressed, waits for ISO filesystem creation. @* One pointer occupies 8 bytes of memory and governs block_size or block_size_v2 uncompressed bytes. I.e. with block size 128k, 1m of block pointer memory suffices for at most 16g of uncompressed file size. Each file consumes one end block pointer, independently of the file size. Partially filled end blocks may further reduce the effective payload. @* In case of overflow of the max_bpt limit while adding compression filters the program tries to go on by discarding all buffered block pointers of previously added --zisofs filters. From then on all newly added filters will discard their block pointers immediately after being added. Discarded block pointers cause an additional read and compression run of the input file during the production of the ISO filesystem. @* "max_bpt_f="1k...128g sets the limit for the memory size of the block pointer list of a single file. max_bpt_f is never larger than max_bpt. If either is set to violate this rule, the other gets set to the same value. If both values are the same before a change by max_bpt= or max_bpt_f=, then both limits stick together unless the limit is decreased by max_bpt_f=. @* "bpt_free_ratio="-1|0.0...1.0 sets a threshold for switching to block pointer discarding during compression. If less than the given fraction of the max_bpt_f= memory is free, then block pointers of compression filters get discarded immediately after being added. Value -1 disables this feature. @* "default" is the same as "level=6:block_size=32k:by_magic=off: version_2=off:block_size_v2=128k:susp_z2=off:max_bpt=256m:max_bpt_f=256m: bpt_free_ratio=-1". @c man .TP @item -speed code|number[k|m|c|d|b] @kindex -speed set write speed @cindex Write, set speed, -speed Set the burn speed. Default is "max" (or "0") = maximum speed as announced by the drive. Further special speed codes are: @* "min" (or "-1") selects minimum speed as announced by the drive. @* "none" avoids to send a speed setting command to the drive before burning begins. @* Speed can be given in media dependent numbers or as a desired throughput per second in MMC compliant kB (= 1000) or MB (= 1000 kB). Media x-speed factor can be set explicitly by "c" for CD, "d" for DVD, "b" for BD, "x" is optional. @* Example speeds: @* 706k = 706kB/s = 4c = 4xCD @* 5540k = 5540kB/s = 4d = 4xDVD @* If there is no hint about the speed unit attached, then the medium in the -outdev will decide. Default unit is CD = 176.4k. @* MMC drives usually activate their own idea of speed and take the speed value given by the burn program only as upper limit for their own decision. @c man .TP @item -stream_recording "on"|"off"|"full"|"data"|number @kindex -stream_recording controls defect management @cindex Write, defect management, -stream_recording Setting "on" tries to circumvent the management of defects on DVD-RAM, BD-RE, or BD-R. Defect management keeps partly damaged media usable. But it reduces write speed to half nominal speed even if the medium is in perfect shape. For the case of flawless media, one may use -stream_recording "on" to get full speed. @* "full" tries full speed with all write operations, whereas "on" does this only above byte address 32s. One may give a number of at least 16s in order to set an own address limit. @* "data" causes full speed to start when superblock and directory entries are written and writing of file content blocks begins. @c man .TP @item -dvd_obs "default"|"32k"|"64k"|"obs_pad"|"bdr_obs_exempt" @kindex -dvd_obs set write block size and end alignment @cindex Write, block size and end alignment, -dvd_obs GNU/Linux specific: Set the number of bytes to be transmitted with each write operation to DVD or BD media. A number of 64 KB may improve throughput with bus systems which show latency problems. The default depends on media type, on command -stream_recording , and on compile time options. @* On all systems: "obs_pad" pads the data of the last write operation of a DVD-R[W] DAO session or BD-R session up to the full size of an output chunk. This padding has to be applied automatically to the other DVD and BD media types, where it causes e.g. ISO images to have trailing unclaimed blocks. Whether it is applied automatically to BD-R depends on "bdr_obs_exempt". "obs_pad" can be disabled by "no_obs_pad". @* "bdr_obs_exempt" exempts BD-R media from automatic unconditional transaction end padding, provided that this padding is not requested by "obs_pad" and that no stream_recording is requested. "bdr_obs_exempt" can be disabled by "no_obs_exempt". @* This is a new feature introduced with version 1.5.6. It might become default in later versions. @c man .TP @item -modesty_on_drive parameter[:parameters] @kindex -modesty_on_drive keep drive buffer hungry @cindex Write, drive buffer, -modesty_on_drive Control whether the drive buffer shall be kept from getting completely filled. Parameter "on" (or "1") keeps the program from trying to write to the burner drive while its buffer is in danger to be filled over a given limit. If this limit is exceeded then the program will wait until the filling reaches a given low percentage value. @* This can ease the load on operating system and drive controller and thus help with achieving better input bandwidth if disk and burner are not on independent controllers (like hda and hdb). It may also help with throughput problems of simultaneous burns on different burners with Linux kernels like 3.16, if one has reason not to fix the problem by -scsi_dev_family "sg". On the other hand it increases the risk of buffer underflow and thus reduced write speed. @* Some burners are not suitable because they report buffer fill with granularity too coarse in size or time, or expect their buffer to be filled to the top before they go to full speed. @* Parameters "off" or "0" disable this feature. @* The threshold for beginning to wait is given by parameter "max_percent=". Parameter "min_percent=" defines the threshold for resuming transmission. Percentages are permissible in the range of 25 to 100. Numbers in this range without a prepended name are interpreted as "on:min_percent=". @* E.g.: -modesty_on_drive 75 @* The optimal values depend on the buffer behavior of the drive. @* Parameter "timeout_sec=" defines after which time of unsuccessful waiting the modesty shall be disabled because it does not work. @* Parameter "min_usec=" defines the initial sleeping period in microseconds. If the drive buffer appears to be too full for sending more data, the program will wait the given time and inquire the buffer fill state again. If repeated inquiry shows not enough free space, the sleep time will slowly be increased to what parameter "max_usec=" defines. @* Parameters, which are not mentioned with a -modesty_on_drive command, stay unchanged. Default is: @* -modesty_on_drive off:min_percent=90:max_percent=95: timeout_sec=120:min_usec=5000:max_usec=25000 @c man .TP @item -use_immed_bit "on"|"off"|"default" @kindex -use_immed_bit controls use of Immed bit @cindex Blank, format, Immed bit, -use_immed_bit Control whether several long lasting SCSI commands shall be executed with the Immed bit, which makes the commands end early while the drive operation is still going on. xorriso then inquires progress indication until the drive reports to be ready again. If this feature is turned off, then blanking and formatting will show no progress indication. @* It may depend on the operating system whether -use_immed_bit is set to "off" by default. Command -status will tell by appending "/on" or "/off" if a drive has already been acquired and -use_immed_bit is currently set to "default". Command -use_immed_bit tolerates and ignores such appended text. @c man .TP @item -stdio_sync "on"|"off"|"end"|number @kindex -stdio_sync controls stdio buffer @cindex Write, buffer syncing, -stdio_sync Set the number of bytes after which to force output to stdio: pseudo drives. This forcing keeps the memory from being clogged with lots of pending data for slow devices. Default "on" is the same as "16m". Forced output can be disabled by "off", or be delayed by "end" until all data are produced. If a number is chosen, then it must be at least 64k. @c man .TP @item -dummy "on"|"off" @kindex -dummy controls write simulation @cindex Write, simulation, -dummy If "on" then simulate burning or refuse with FAILURE event if no simulation is possible, do neither blank nor format. @c man .TP @item -fs number["k"|"m"] @kindex -fs sets size of fifo @cindex Write, fifo size, -fs Set the size of the fifo buffer which smoothens the data stream from ISO image generation to media burning. Default is 4 MiB, minimum 64 kiB, maximum 1 GiB. The number may be followed by letter "k" or "m" which means unit is kiB (= 1024) or MiB (= 1024 kiB). @c man .TP @item -close "on"|"off"|"as_needed" @kindex -close controls media closing @cindex Write, close media, -close If -close is set to "on" then mark the written medium as not appendable any more. This will have no effect on overwritable media types. Setting "on" is the contrary of cdrecord option -multi, and is one aspect of growisofs option -dvd-compat. @* If set to "off" then keep the medium writable for an appended session. @* If set to "as_needed" then use "on" only if "off" is predicted to fail with the given medium and its state. @* Not all drives correctly recognize fast-blanked DVD-RW which need "on". If there is well founded suspicion that a burn run failed due to -close "off", then -close "as_needed" causes a re-try with "on". @* Note that emulation command -as "cdrecord" temporarily overrides the current setting of -close by its own default -close "on" if its option -multi is missing. @c man .TP @item -write_type "auto"|"tao"|"sao/dao" @kindex -write_type chooses TAO or SAO/DAO @cindex Write, TAO or SAO/DAO, -write_type Set the write type for the next burn run. "auto" will select SAO with blank CD media, DAO with blank DVD-R[W] if -close is "on", and elsewise CD TAO or the equivalent write type of the particular DVD/BD media. Choosing TAO or SAO/DAO explicitly might cause the burn run to fail if the desired write type is not possible with the given media state. @c man .TP @item -padding number["k"|"m"]|"included"|"appended" @kindex -padding sets amount or mode of image padding @cindex Write, padding image, -padding Append the given number of extra bytes to the image stream. This is a traditional remedy for a traditional bug in block device read drivers. Needed only for CD recordings in TAO mode. Since one can hardly predict on what media an image might end up, @command{xorriso} adds the traditional 300k of padding by default to all images. @* For images which will never get to a CD it is safe to use -padding 0 . @* Normally padding is not written as part of the ISO image but appended after the image end. This is -padding mode "appended". @* Emulation command -as "mkisofs" and command -jigdo cause padding to be written as part of the image. The same effect is achieved by -padding mode "included". @end table @c man .TP @c man .B Bootable ISO images: @node Bootable, Jigdo, SetWrite, Commands @section Bootable ISO images @c man .PP Contrary to published specifications many BIOSes will load an El Torito record from the first session on media and not from the last one, which gets mounted by default. This makes no problems with overwritable media, because they appear to inadverted readers as one single session. @* But with multi-session media CD-R[W], DVD-R[W], DVD+R, it implies that the whole bootable system has to reside already in the first session and that the last session still has to bear all files which the booted system expects after mounting the ISO image. @* If a boot image from ISOLINUX or GRUB is known to be present on media then it is advised to patch it when a follow-up session gets written. But one should not rely on the capability to influence the bootability of the existing sessions, unless one can assume overwritable media. @* Normally the boot images are data files inside the ISO filesystem. By special path "--interval:appended_partition_NNN:all::" it is possible to refer to an appended partition. The number NNN gives the partition number as used with the corresponding command -append_partition. E.g.: @* -append_partition 2 0xef /tmp/efi.img @* -boot_image any efi_path=--interval:appended_partition_2:all:: @* There are booting mechanisms which do not use an El Torito record but rather start at the first bytes of the image: PC-BIOS MBR or EFI GPT for hard-disk-like devices, APM partition entries for Macs which expect HFS+ boot images, MIPS Volume Header for old SGI computers, DEC Boot Block for old MIPS DECstation, SUN Disk Label for SPARC machines, HP-PA boot sector for HP PA-RISC machines, DEC Alpha SRM boot sector for old DEC Alpha machines. @c man .PP @sp 1 @cindex Interval reader for system area and partitions Several of the following commands expect disk paths as input but also accept description strings for the libisofs interval reader, which is able to cut out data from disk files or -indev and to zeroize parts of the content: command -append_partition, boot specs system_area=, grub2_mbr=, prep_boot_part=, efi_boot_part=. @* The description string consists of the following components, separated by colon ':' @* "@minus{}@minus{}interval:"Flags":"Interval":"Zeroizers":"Source @* The component "@minus{}@minus{}interval" states that this is not a plain disk path but rather an interval reader description string. @* The component Flags modifies the further interpretation: @* "local_fs" demands to read from a file depicted by the path in Source. @* "imported_iso" demands to read from the -indev. This works only if -outdev is not the same as -indev. The Source component is ignored. @* "appended_partition_NNN" with a decimal number NNN works only for -boot_image bootspecs which announce El Torito boot image paths: bin_path=, efi_path=. The number gives the partition number as used with the corresponding command -append_partition. @* The component Interval consists of two byte address numbers separated by a "-" character. E.g. "0-429" means to read bytes 0 to 429. @* The component Zeroizers consists of zero or more comma separated strings. They define which part of the read data to zeroize. Byte number 0 means the byte read from the Interval start address. Each string may be one of: @* "zero_mbrpt" demands to zeroize the MBR partition table if bytes 510 and 511 bear the MBR signature 0x55 0xaa. @* "zero_gpt" demands to check for a GPT header in bytes 512 to 1023, to zeroize it and its partition table blocks. @* "zero_apm" demands to check for an APM block 0 and to zeroize its partition table blocks. @* Start_byte"-"End_byte demands to zeroize the read-in bytes beginning with number Start_byte and ending after End_byte. @* The component Source is the file path with flag "local_fs", and ignored with flag "imported_iso". @* Byte numbers may be scaled by a suffix out of @{k,m,g,t,s,d@} meaning multiplication by @{1024, 1024k, 1024m, 1024g, 2048, 512@}. A scaled value end number depicts the last byte of the scaled range. @* E.g. "0d-0d" is "0-511". @* Examples: @* "local_fs:0-32767:zero_mbrpt,zero_gpt,440-443:/tmp/template.iso" @* "imported_iso:45056d-47103d::" @* @table @asis @sp 1 @c man .TP @item -boot_image "any"|"isolinux"|"grub" @kindex -boot_image controls bootability @cindex Write, bootability, -boot_image @cindex Bootability, control, -boot_image @* "discard"|"keep"|"patch"|"replay"|"show_status"| bootspec|"next" @* @sp 1 Define the equipment of the emerging filesystem with boot entry points. @* With systems which boot via BIOS or EFI this is a set of El Torito boot images, possibly MBR boot code, and possibly partition tables of type MBR, GPT, or APM. Such file sets get produced by boot loader systems like ISOLINUX or GRUB. @* @sp 1 Each -boot_image command has two parameters: type and setting. More than one -boot_image command may be used to define the handling of one or more boot images. Sequence matters. @* Types @strong{isolinux} and @strong{grub} care for known peculiarities. Type @strong{any} makes no assumptions about the origin of the boot images. @* @sp 1 When loading an ISO filesystem, system area and El Torito boot images get loaded, too. The default behavior is not to write loaded El Torito boot images and to write the loaded system area content without alterations. @* @strong{discard} gives up the El Torito boot catalog and its boot images. regardless whether loaded from an ISO filesystem or defined by commands. Any BIOS or EFI related boot options get revoked. Nevertheless, loaded system area data and the possibly defined appended partitions stay valid. If desired, they have to be erased by @* -boot_image any system_area=/dev/zero @* -append_partition all revoke - @* @strong{keep} keeps or copies El Torito boot images unaltered and writes a new catalog. @* @strong{patch} applies patching to existing El Torito boot images if they seem to bear a boot info table. @* A boot info table needs to be patched when the boot image gets newly introduced into the ISO image or if an existing image gets relocated. This is automatically done if type "isolinux" or "grub" is given, but not with "any". @* If patching is enabled, then boot images from previous sessions will be checked whether they seem to bear a boot info table. If not, then they stay unpatched. This check is not infallible. So if you do know that the images need no patching, use "any" "keep". "grub" "patch" will not patch EFI images (platform_id=0xef). @* @strong{replay} is a more modern version of "patch", which not only cares for existing El Torito boot equipment but also for the recognizable boot provisions in the System Area. It discards any existing -boot_image setting including the system area and executes the commands proposed by command -report_el_torito "cmd". @* Special care has to be taken with manipulations of files in the emerging ISO filesystem which are mentioned by -report_el_torito "cmd". Their paths are memorized at ISO image load time. In general -boot_image "any" "replay" should be applied after all file manipulations are done. All file paths from the -report_el_torito commands must then still lead to data files which are suitable for their respective commands. @* The effects of file path changes after -boot_image "any" "replay" can be surprising. E.g. removing or replacing a boot image with boot info table for legacy BIOS leads to a hidden boot image with the content as it was at "replay" time. Doing the same with a boot image for EFI leads to an error at -commit time or to the new file content becomming the EFI boot image. @* Out of historical reasons @strong{replay} does not revoke all possibly made -append_partition settings but only overwrites those for which the loaded ISO image provides candidates. @* @strong{show_status} will print what is known about the loaded boot images and their designated fate. @* Examples: @* Drop El Torito: @* -boot_image any discard @* Drop El Torito, system area, appended partitions: @* -boot_image any discard @* -boot_image any system_area=/dev/zero @* -append_partition all revoke - @* Maintain recognizable stuff after revoking possibly made -append_partition settings to surely get only the partitions from the loaded ISO: @* -append_partition all revoke - @* -boot_image any replay @* Re-adjust El Torito only for GRUB: @* -boot_image grub patch @* Re-adjust El Torito only for ISOLINUX: @* -boot_image isolinux patch @* @sp 1 A @strong{bootspec} is a word of the form name=value. It is used to describe the parameters of a boot feature. The names "dir", "bin_path", "efi_path" lead to El Torito bootable images. Name "system_area" activates a given file as MBR or other disk header. @* On all media types this is possible within the first session. In further sessions an existing boot image can get replaced by a new one, but depending on the media type this may have few effect at boot time. See above. @* El Torito boot images have to be added to the ISO image by normal means (image loading, -map, -add, ...). In case of ISOLINUX the files should reside either in ISO image directory /isolinux or in /boot/isolinux . In that case it suffices to use as bootspec the text "@strong{dir=/isolinux}" or "dir=/boot/isolinux". E.g.: @* -boot_image isolinux dir=/boot/isolinux @* which bundles these individual settings: @* -boot_image isolinux bin_path=/boot/isolinux/isolinux.bin @* -boot_image isolinux cat_path=/boot/isolinux/boot.cat @* -boot_image isolinux load_size=2048 @* -boot_image any boot_info_table=on @* An El Torito boot catalog file gets inserted into the ISO image with address @strong{cat_path=} with the first -boot_image "any" "next" or at -commit time. It is subject to normal -overwrite and -reassure processing if there is already a file with the same name. The catalog lists the boot images and is read by the boot facility to choose one of the boot images. But it is not necessary that it appears in the directory tree at all. One may hide it in all trees by @strong{cat_hidden=on}. Other possible values are "iso_rr", "joliet", "hfsplus", and the default "off". The timestamps of the boot catalog file are refreshed at commit time. Command -volume_date "uuid" can be used to set their value. @* @strong{bin_path=} depicts an El Torito boot image file, a binary program which is to be started by the hardware boot facility (e.g. the BIOS) at boot time. @* @strong{efi_path=} depicts an El Torito boot image file that is ready for EFI booting. This is normally a FAT filesystem image not larger than 65535 blocks of 512 bytes (= 32 MiB - 512). Its load_size is determined automatically, no boot info table gets written, no boot medium gets emulated, platform_id is 0xef. @* @strong{emul_type=} can be one of "no_emulation", "hard_disk", "diskette". It controls the boot medium emulation code of a boot image. The default "no_emulation" is suitable for ISOLINUX, GRUB, FreeBSD cdboot. @* @strong{load_size=} is a value which depends on the boot image. Default is 2048 which matches the expectations of most boot images. The special value "full" means the full size of the boot image file rounded up to a multiple of 2048 bytes. Maximum is 33,552,384 bytes. @* @strong{boot_info_table=on} causes address patching to bytes 8 to 63 of the boot image which is given by "any" "bin_path=". "boot_info_table=off" disables this patching. @* @strong{grub2_boot_info=on} causes address patching to byte 2548 of the boot image which is given by "any" "bin_path=". The address is written as 64 bit little-endian number. It is the 2KB block address of the boot image content, multiplied by 4, and then incremented by 5. "grub2_boot_info=off" disables this patching. @* @strong{platform_id=} defines by a hexadecimal or decimal number the Platform ID of the boot image. "0x00" is 80x86 PC-BIOS, "0x01" is PowerPC, "0x02" is Mac, "0xef" is EFI (decimal "239"). @* @strong{id_string=}text|56_hexdigits defines the ID string of the boot catalog section where the boot image will be listed. If the value consists of 56 characters [0-9A-Fa-f] then it is converted into 28 bytes, else the first 28 characters become the ID string. The ID string of the first boot image becomes the overall catalog ID. It is limited to 24 characters. Other id_strings become section IDs. @* @strong{sel_crit=}hexdigits defines the Selection Criteria of the boot image. Up to 20 bytes get read from the given characters [0-9A-Fa-f]. They get attributed to the boot image entry in the catalog. @* @strong{next} ends the definition of a boot image and starts a new one. Any following -bootimage bootspecs will affect the new image. The first "next" discards loaded boot images and their catalog. @* @cindex System area, _definition @cindex MBR, set, -boot_image system_area= @strong{system_area=}disk_path copies at most 32768 bytes from the given disk file to the very start of the ISO image. This System Area is reserved for system dependent boot software, e.g. an MBR which can be used to boot from USB stick or hard disk. @* Other than an El Torito boot image, the file disk_path needs not to be added to the ISO image. @* In multi-session situations the existing System Area is preserved by default. In in this case, the special disk_path "." prevents reading of a disk file but nevertheless causes adjustments in the loaded system area data. Such adjustments may get ordered by -boot_image commands. @* Special "system_area=/dev/zero" causes 32k of 0-bytes. Use this to e.g. discard partition tables which were loaded with the ISO image. @* This setting is the default with the write method of Modifying, when -indev and -outdev are both used and not the same drive. If you indeed need to copy the unchanged system area from -indev to -outdev, use "system_area=--interval:imported_iso:0s-15s::" , which was the default in older versions of xorriso. @* @strong{-boot_image isolinux system_area=} implies "partition_table=on". In this case, the disk path should lead to one of the SYSLINUX files isohdp[fp]x*.bin or to a file which was derived from one of those files. E.g. to the first 512 bytes from an ISOLINUX isohybrid ISO image. @* El Torito boot images (dir=, bin_path=, efi_path=) may then be augmented by @strong{isolinux partition_entry=gpt_basdat} or @strong{isolinux partition_entry=gpt_hfsplus}, and by @strong{isolinux partition_entry=apm_hfsplus}. The boot image will then be mentioned in an invalid GPT as Basic Data or GPT HFS+ partition, and in a valid APM as HFS+ partition. The first three GPT partitions will also be marked by MBR partitions. The MBR partition of type 0xEF is what actually is used by EFI firmware for booting from USB stick. @* @cindex GPT, control GUID, -boot_image gpt_disk_guid= @strong{-boot_image any gpt_disk_guid=}value controls whether an emerging GPT shall get a randomly generated disk GUID or whether the GUID is supplied by the user. Value "random" is default. Value "volume_date_uuid" produces a low quality GUID from the value set by -volume_date "uuid". @* A string of 32 hex digits, or a RFC 4122 compliant GUID string may be used to set the disk GUID directly. UEFI prescribes the first three components of a RFC 4122 GUID string to be byte-swapped in the binary representation: @* E.g. gpt_disk_guid=2303cd2a-73c7-424a-a298-25632da7f446 equals gpt_disk_guid=2acd0323c7734a42a29825632da7f446 @* The partition GUIDs get generated by minimally varying the disk GUID. @* @strong{-boot_image any part_like_isohybrid=on} enables -boot_image isolinux partition_entry= even if no -boot_image isolinux system_area= is given. No MBR partition of type 0xee emerges, even if GPT gets produced. Gaps between GPT and APM partitions will not be filled by more partitions. Appended partitions get mentioned in APM if other APM partitions emerge. @* @strong{-boot_image any iso_mbr_part_type=}number sets the partition type of the MBR partition which represents the ISO or at least protects it. @* Number may be 0x00 to 0xff. The text "default" re-enables the default types of the various occasions to create an ISO MBR partition. This is without effect if no such partition emerges by other settings or if the partition type is prescribed mandatorily like 0xee for GPT protective MBR or 0x96 for CHRP. @* If instead a type_guid is given by a 32-digit hex string like a2a0d0ebe5b9334487c068b6b72699c7 or by a structured text like EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, then it will be used as partition type if the ISO filesystem appears as partition in GPT. In MBR, C12A7328-F81F-11D2-BA4B-00A0C93EC93B will be mapped to 0xef. Any other GUID will be mapped to 0x83. @* @strong{grub2_mbr=}disk_path works like "any" system_area= with additional patching for modern GRUB MBRs. The content start address of the first boot image is converted to a count of 512 byte blocks, and an offset of 4 is added. The result is written as 64 bit little-endian number to byte address 0x1b0. @* This feature can be revoked either by grub2_mbr= with empty disk path, or by submitting a disk_path via system_area=. @* @cindex Partition table, _definition @strong{partition_table=on} causes a simple partition table to be written into bytes 446 to 511 of the System Area. @* With type "isolinux" it shows a partition that begins at byte 0 and it causes the LBA of the first boot image to be written into the MBR. For the first session this works only if also "system_area=" and "bin_path=" or "dir=" is given. @* With types "any" and "grub" it shows a single partition which starts at byte 512 and ends where the ISO image ends. This works with or without system_area= or boot image. @* Bootspecs chrp_boot_part=, prep_boot_part=, and efi_boot_part= overwrite this entry in the MBR partition table. @* If types "isolinux" or "grub" are set to "patch", then "partition_table=on" is activated without new boot image. In this case the existing System Area gets checked whether it bears addresses and sizes as if it had been processed by "partition_table=on". If so, then those parameters get updated when the new System Area is written. @* @cindex Appended partition, in MBR or GPT @strong{appended_part_as=gpt} marks partitions from -append_partition in GPT rather than in MBR. In this case the MBR shows a single partition of type 0xee which covers the whole output data. The number of appendable partitions with GPT is 8 rather than 4 with MBR. @* @strong{appended_part_as=mbr} is the default. Appended partitions get marked in GPT only if GPT is produced because of other settings. If given explicitly, this clears setting "gpt" and "apm". Nevertheless "apm" may be added to "mbr". @* @cindex Appended partition, in APM @strong{appended_part_as=apm} marks partitions from -append_partition in APM additionally to "mbr" or "gpt". The partition number in APM will not be influenced by -append_partition parameter partition_number. @* By default, appended partitions get marked in APM only if APM is produced because of other options together with part_like_isohybrid="on". @* @cindex CHRP partition, _definition @strong{chrp_boot_part=on} causes a single partition in MBR which covers the whole ISO image and has type 0x96. This is not compatible with any other feature that produces MBR partition entries. It makes GPT unrecognizable. @* @cindex PReP partition, _definition @strong{prep_boot_part=}disk_path inserts the content of a data file into the image and marks it by an MBR partition of type 0x41. The parts of the ISO image before and after this partition will be covered by further MBR partitions. The data file is supposed to contain ELF executable code. @* @cindex EFI system partition, _definition @strong{efi_boot_part=}disk_path inserts the content of a data file into the image and marks it by a GPT partition. If not chrp_boot_part=on, then the first partition in MBR will have type 0xee to announce the presence of GPT. The data file is supposed to contain a FAT filesystem. @* Instead of a disk_path, the word @minus{}@minus{}efi-boot-image may be given. It exposes in GPT the content of the first El Torito EFI boot image as EFI system partition. EFI boot images are introduced by bootspec efi_path=. The affected EFI boot image cannot show up in HFS+ because it is stored outside the HFS+ partition. @* @cindex Partition offset, _definition @strong{partition_offset=}2kb_block_adr causes a partition table with a single partition that begins at the given block address. This is counted in 2048 byte blocks, not in 512 byte blocks. If the block address is non-zero then it must be at least 16. A non-zero partition offset causes two superblocks to be generated and two sets of directory trees. The image is then mountable from its absolute start as well as from the partition start. @* The offset value of an ISO image gets preserved when a new session is added. So the value defined here is only in effect if a new ISO image gets written. @* @cindex Cylinder size, _definition @strong{partition_hd_cyl=}number gives the number of heads per cylinder for the partition table. 0 chooses a default value. Maximum is 255. @* @strong{partition_sec_hd=}number gives the number of sectors per head for the partition table. 0 chooses a default value. Maximum is 63. @* The product partition_sec_hd * partition_hd_cyl * 512 is the cylinder size. It should be divisible by 2048 in order to make exact alignment possible. With appended partitions and "appended_part_as=gpt" there is no limit for the number of cylinders. Else there may be at most 1024 of them. If the cylinder size is too small to stay below the limit, then appropriate values of partition_hd_cyl are chosen with partition_sec_hd 32 or 63. If the image is larger than 8,422,686,720 bytes, then the cylinder size constraints cannot be fulfilled for MBR. @* @cindex Cylinder alignment, _definition @strong{partition_cyl_align=}mode controls image size alignment to an integer number of cylinders. It is prescribed by isohybrid specs and it seems to please program fdisk. Cylinder size must be divisible by 2048. Images larger than 8,323,596,288 bytes cannot be aligned in MBR partition table. @* Mode "auto" is default. Alignment by padding happens only with "isolinux" "partition_table=on". @* Mode "on" causes alignment by padding with "partition_table=on" for any type. Mode "all" is like "on" but also pads up partitions from -append_partition to an aligned size. @* Mode "off" disables alignment for any type. @* @cindex MBR bootable/active flag, enforce @strong{mbr_force_bootable=}mode enforces an MBR partition with "bootable/active" flag if options like partition_table= or grub2_mbr= indicate production of a bootable MBR. These options normally cause the flag to be set if there is an MBR partition of type other than 0xee or 0xef. If no such partition exists, then no bootflag is set, unless mbr_force_bootable="on" forces creation of a dummy partition of type 0x00 which covers only the first block of the ISO image. @* If no bootable MBR is indicated and a partition gets created by -append_partition, then mbr_force_bootable="on" causes a bootflag like it would do with a bootable MBR. @* @cindex GPT Legacy BIOS bootable flag, set for ISO @strong{gpt_iso_bootable=}on causes bit 2 of the GPT partition flags to be set for the ISO 9660 partition if such a GPT partition emerges. This bit is specified as "Legacy BIOS bootable" but its true significance is unclear. Some GPT-aware BIOS might want to see it in some partition. Mode "off" revokes this setting. @* @cindex GPT read-only flag, do not set for ISO @strong{gpt_iso_not_ro=}on causes bit 60 of the GPT partition flags to be not set for the ISO 9660 partition if such a GPT partition emerges. This bit is specified as "Read-only" and thus appropriate. But it is unusual in GPT disk partitions. Mode "off" revokes this setting and causes the read-only bit to be set. @* @cindex MIPS boot file, activation @strong{mips_path=}iso_rr_path declares a data file in the image to be a MIPS Big Endian boot file and causes production of a MIPS Big Endian Volume Header. This is mutually exclusive with production of other boot blocks like MBR. It will overwrite the first 512 bytes of any data provided by system_area=. Up to 15 boot files can be declared by mips_path=. @* @strong{mipsel_path=}iso_rr_path declares a data file in the image to be the MIPS Little Endian boot file. This is mutually exclusive with other boot blocks. It will overwrite the first 512 bytes of any data provided by system_area=. Only a single boot file can be declared by mipsel_path=. @* @cindex SUN Disk Label, production @strong{sparc_label=}text causes the production of a SUN Disk Label with the given text as ASCII label. Partitions 2 to 8 may be occupied by appended images. Partition 1 will always be the ISO image. See command -append_partition. The first 512 bytes of any data provided by system_area= will be overwritten. @* @strong{grub2_sparc_core=}iso_rr_path causes the content address and size of the given file to be written after the SUN Disk Label. Both numbers are counted in bytes. The address is written as 64 bit big-endian number to byte 0x228. The size is written as 32 bit big-endian number to byte 0x230. @* @cindex HP-PA boot sector, production @strong{hppa_cmdline=}text sets the PALO command line for HP-PA. Up to 1023 characters are permitted by default. With hppa_hdrversion=4 the limit is 127. @* Note that the first five hppa_ bootspecs are mandatory, if any of the hppa_ bootspecs is used. Only hppa_hdrversion= is allowed to be missing. @* @strong{hppa_bootloader=}iso_rr_path designates the given path as HP-PA bootloader file. @* @strong{hppa_kernel_32=}iso_rr_path designates the given path as HP-PA 32 bit kernel file. @* @strong{hppa_kernel_64=}iso_rr_path designates the given path as HP-PA 64 bit kernel file. @* @strong{hppa_ramdisk=}iso_rr_path designates the given path as HP-PA RAM disk file. @* @strong{hppa_hdrversion=}number chooses between PALO header version 5 (default) and version 4. For the appropriate value see in PALO source code: PALOHDRVERSION. @* @cindex DEC Alpha SRM boot sector, production @strong{alpha_boot=}iso_rr_path declares a data file in the image to be the DEC Alpha SRM Secondary Bootstrap Loader and causes production of a boot sector which points to it. This is mutually exclusive with production of other boot blocks like MBR. @* @strong{mips_discard}, @strong{mipsel_discard}, @strong{sparc_discard}, @strong{hppa_discard}, @strong{alpha_discard} revoke any boot file declarations made for mips/mipsel, sparc, hppa, or alpha, respectively. This removes the ban on production of other boot blocks. @* @cindex HFS+ serial number @strong{hfsplus_serial=}hexstring sets a string of 16 digits "0" to "9" and letters "a" to "f", which will be used as unique serial number of an emerging HFS+ filesystem. @* @cindex HFS+ allocation block size @strong{hfsplus_block_size=}number sets the allocation block size to be used when producing HFS+ filesystems. Permissible are 512, 2048, or 0. The latter lets the program decide. @* @cindex APM block size @strong{apm_block_size=}number sets the block size to be used when describing partitions by an Apple Partition Map. Permissible are 512, 2048, or 0. The latter lets the program decide. @* Note that size 512 is not compatible with production of GPT, and that size 2048 will not be mountable -t hfsplus at least by older Linux kernels. @end table @* @table @asis @sp 1 @c man .TP @item -append_partition partition_number type_code disk_path @kindex -append_partition adds arbitrary file after image end @cindex Appended Filesystem Image, -append_partition Cause a prepared filesystem image to be appended to the ISO image and to be described by a partition table entry in a boot block at the start of the emerging ISO image. The partition entry will bear the size of the submitted file rounded up to the next multiple of 2048 bytes or to the next multiple of the cylinder size. @* Beware of subsequent multi-session runs. The appended partition will get overwritten. @* Partitions may be appended with partition table types MBR, GPT, and SUN Disk Label. Additionally to MBR and GPT it is possible to have them marked in APM. @* @cindex Appended partitions, MBR With @strong{MBR}: @* partition_number may be 1 to 4. Number 1 will put the whole ISO image into the unclaimed space before partition 1. So together with most @command{xorriso} MBR features, number 2 would be the most natural choice. @* The type_code may be "FAT12", "FAT16", "Linux", or a hexadecimal number between 0x00 and 0xff. Not all those numbers will yield usable results. For a list of MBR partition type codes search the Internet for "Partition Types" or run fdisk command "L". @* type_code may also be a type GUID as plain hex string like a2a0d0ebe5b9334487c068b6b72699c7 or as structured text like EBD0A0A2-B9E5-4433-87C0-68B6B72699C7. It will be used if the partition is mentioned in GPT. In MBR, C12A7328-F81F-11D2-BA4B-00A0C93EC93B will be mapped to 0xef. Any other GUID will be mapped to 0x83. In APM, 48465300-0000-11AA-AA11-00306543ECAC will be mapped to partition type "Apple_HFS", any other to "Data". @* If some other command causes the production of GPT, then the appended partitions will be mentioned there too. @* @cindex Appended partitions, GPT @strong{GPT} can be forced by @* -boot_image "any" "appended_part_as=gpt" @* partition_number may be 1 to 8. But other than with MBR partitions it is not guaranteed that the resulting GPT partition will have this number. @* More important than the desired partition number will be that the resulting ISO filesystem is covered gaplessly with GPT table and its partitions and that the partitions in the table are sorted by block address. If partition_number is higher than the number of preceding partitions, then the appropriate number of empty partition entries is inserted to achieve the desired partition_number. If the number of preceding partitions is too high, then a NOTE message informs about the inability to achieve partition_number and about the actually assigned number. @* The type_code may be the same as described with MBR. Given GUIDs are used unchanged. Given MBR partition types get translated. 0xef becomes C12A7328-F81F-11D2-BA4B-00A0C93EC93B, others become EBD0A0A2-B9E5-4433-87C0-68B6B72699C7. @* @cindex SUN SPARC boot images, activation @strong{SUN Disk Label} is chosen by -boot_image any sparc_label=. @* partition_number may be 2 to 8. Number 1 will always be the ISO image. Partition start addresses are aligned to 320 KiB. The type_code does not matter. Submit 0x0. @* disk_path "." causes the partition to become a copy of the next lower valid one. @* With MBR, GPT, and SUN alike: @* The disk_path must provide the necessary data bytes at commit time. @* Issueing -append_partition with a partition number that was already used in a previous -append_partition command does not cause an error but silently overrides the previous setting. @* The pseudo type_code "revoke" or an empty disk_path prevent the partition from being appended. The pseudo partition number "all" may be used in this case to revoke all previous -append_partition settings. @end table @c man .TP @c man .B Jigdo Template Extraction: @node Jigdo, Charset, Bootable, Commands @section Jigdo Template Extraction @c man .PP @cindex Jigdo Template Extraction, _definition From man genisoimage: "Jigdo is a tool to help in the distribution of large files like CD and DVD images; see http://atterer.net/jigdo/ for more details. Debian CDs and DVD ISO images are published on the web in jigdo format to allow end users to download them more efficiently." @* @command{xorriso} can produce a .jigdo and a .template file together with a single-session ISO image. The .jigdo file contains checksums and symbolic file addresses. The .template file contains the compressed ISO image with reference tags instead of the content bytes of the listed files. @* Input for this process are the normal arguments for a @command{xorriso} session on a blank -outdev, and a checksum file which lists those data files which may be listed in the .jigdo file and externally referenced in the .template file. Each designated file is represented in the checksum file by a single text line: @* Checksum as hex digits, 2 blanks, size as 12 decimal digits or blanks, 2 blanks, symbolic file address @* The kind of checksum is chosen by -jigdo "checksum_algorithm" with values "md5" (32 hex digits) or "sha256" (64 hex digits). It will also be used for the file address lines in the .jigdo file. The default is "md5". @* The file address in a checksum file line has to bear the same basename as the disk_path of the file which it shall match. The directory path of the file address is decisive for To=From mapping, not for file recognition. After To=From mapping, the file address gets written into the .jigdo file. Jigdo restore tools will convert these addresses into really reachable data source addresses from which they can read. @* If the list of jigdo parameters is not empty, then @command{xorriso} will refuse to write to non-blank targets, it will disable multi-session emulation, and padding will be counted as part of the ISO image. @* @table @asis @sp 1 @c man .TP @item -jigdo parameter_name value @kindex -jigdo clears JTE or or adds parameter to JTE @cindex Jigdo Template Extraction, -jigdo Clear Jigdo Template Extraction parameter list or add a parameter to that list. The alias names are the corresponding genisoimage options. They are accepted as parameter names as well. Especially they are recognized by the -as mkisofs emulation command. @* Parameter @strong{clear} with any value empties the whole list. No .jigdo and .template file will be produced. @* @strong{checksum_algorithm} chooses the checksum algorithm which shall be used for the data file entries in the .jigdo file and is expected in the checksum file. Permissible are "md5" or "sha256". Default is "md5". @* Alias: -jigdo-checksum-algorithm @* @strong{template_path} sets the disk_path for the .template file with the holed and compressed ISO image copy. @* Alias: -jigdo-template @* @strong{jigdo_path} sets the disk_path for the .jigdo file with the checksums and download addresses for filling the holes in .template. @* Alias: -jigdo-jigdo @* @strong{checksum_path} sets the disk_path where to find the checksum file with symbolic file addresses and checksums according to @strong{checksum_algorithm}. @* Alias: md5_path @* Alias: -checksum-list @* Alias: -md5-list @* @strong{min_size} sets the minimum size for a data file to be listed in the .jigdo file and being a hole in the .template file. @* Alias: -jigdo-min-file-size @* @strong{exclude} adds a regular expression pattern which will get compared with the absolute disk_path of any data file. A match causes the file to stay in .template in any case. @* Alias: -jigdo-exclude @* @strong{demand_checksum} adds a regular expression pattern which will get compared with the absolute disk_path of any data file that was not found in the checksum list file as of "checksum_path". A match causes a MISHAP event. @* Alias: demand_md5 @* Alias: -jigdo-force-checksum @* Alias: -jigdo-force-md5 @* @strong{mapping} adds a string pair of the form To=From to the parameter list. If a data file gets listed in the .jigdo file, then it is referred by the file address from its line in the checksum file. This file address gets checked whether it begins with the From string. If so, then this string will be replaced by the To string and a ':' character, before it goes into the .jigdo file. The From string should end by a '/' character. @* Alias: -jigdo-map @* @strong{compression} chooses one of "bzip2" or "gzip" for the compression of the template file. The jigdo file is put out uncompressed. @* Alias: -jigdo-template-compress @* @strong{checksum_iso} chooses one or more of "md5", "sha1", "sha256", "sha512" for the auxiliary "# Image Hex" checksums in the jigdo file. The value may e.g. look like "md5,sha1,sha512". Value "all" chooses all available algorithms. Note that MD5 stays always enabled. @* Alias: -checksum_algorithm_iso @* @strong{checksum_template} is like checksum_iso but for "# Template Hex". @* Alias: -checksum_algorithm_template @end table @c man .TP @c man .B Character sets: @node Charset, Exception, Jigdo, Commands @section Character sets @c man .PP @cindex Character Set, _definition File names are strings of non-zero bytes with 8 bit each. Unfortunately the same byte string may appear as different peculiar national characters on differently nationalized terminals. The meanings of byte codes are defined in @strong{character sets} which have names. Shell command iconv -l lists them. @* @cindex Local Character Set, _definition The file names on hard disk are assumed to be encoded by the @strong{local character set} which is also used for the communication with the user. Byte codes 32 to 126 of the local character set must match the US-ASCII characters of the same code. ISO-8859 and UTF-8 fulfill this demand. @* By default, @command{xorriso} uses the character set as told by shell command "locale" with argument "charmap". This may be influenced by environment variables LC_ALL, LC_CTYPE, or LANG and should match the expectations of the terminal. In some situations it may be necessary to set it by command -local_charset. @* Local character sets should not matter as long as only english alphanumeric characters are used for file names or as long as all writers and readers of the media use the same local character set. Outside these constraints it may be necessary to let @command{xorriso} convert byte codes from and to other character sets. @* @cindex Input Character Set, _definition The Rock Ridge file names in ISO filesystems are assumed to be encoded by the @strong{input character set}. @cindex Output Character Set, _definition The Rock Ridge file names which get written with ISO filesystems will be encoded by the @strong{output character set}. @* The sets can be defined independently by commands -in_charset and -out_charset. Normally one will have both identical, if ever. Other than the local character set, these two character sets may deviate from US-ASCII. @* The output character sets for Joliet and HFS+ are not influenced by these commands. Joliet uses output character set UCS-2 or UTF-16. HFS+ uses UTF-16. @* The default output charset is the local character set of the terminal where @command{xorriso} runs. So by default no conversion happens between local filesystem names and emerging Rock Ridge names in the image. The situation stays ambiguous and the reader has to riddle what character set was used. @* By command -auto_charset it is possible to attribute the output charset name to the image. This makes the situation unambiguous. But if your terminal character set does not match the character set of the local file names, then this attribute can become plainly wrong and cause problems at read time. To prevent this it is necessary to check whether the terminal properly displays all intended filenames. Check especially the exotic national characters. @* To enforce recording of a particular character set name without any conversion at image generation time, set -charset and -local_charset to the desired name, and enable -backslash_codes to avoid evil character display on your terminal. @table @asis @sp 1 @c man .TP @item -charset character_set_name @kindex -charset sets input/output character set @cindex Character Set, for input/output, -charset Set the character set from which to convert file names when loading an image and to which to convert when writing an image. @c man .TP @item -local_charset character_set_name @kindex -local_charset sets terminal character set @cindex Character Set, of terminal, -local_charset Override the system assumption of the local character set name. If this appears necessary, one should consider to set -backslash_codes to "on" in order to avoid dangerous binary codes being sent to the terminal. @end table @c man .TP @c man .B Exception processing: @node Exception, DialogCtl, Charset, Commands @section Exception processing @c man .PP Since the tasks of @command{xorriso} are manifold and prone to external influence, there may arise the need for @command{xorriso} to report and handle problem events. @* Those events get classified when they are detected by one of the software modules and forwarded to reporting and evaluation modules which decide about reactions. Event classes are sorted by severity: @* "NEVER" The upper end of the severity spectrum. @* "ABORT" The program is being aborted and on its way to end. @* "FATAL" The main purpose of the run failed or an important resource failed unexpectedly. @* "FAILURE" An important part of the job could not be performed. @* "MISHAP" A FAILURE which can be tolerated during ISO image generation. @* "SORRY" A less important part of the job could not be performed. @* "WARNING" A situation is suspicious of being not intended by the user. @* "HINT" A proposal to the user how to achieve better results. @* "NOTE" A harmless information about noteworthy circumstances. @* "UPDATE" A pacifier message during long running operations. @* "DEBUG" A message which would only interest the program developers. @* "ERRFILE" A filename for the -errfile_log if it is enabled. @* "ALL" The lower end of the severity spectrum. @table @asis @sp 1 @c man .TP @item -abort_on severity @kindex -abort_on controls abort on error @cindex Process, control abort on error, -abort_on Set the severity threshold for events to abort the program. @* Useful: "NEVER", "ABORT", "FATAL", "FAILURE" , "MISHAP", "SORRY" @* It may become necessary to abort the program anyway, despite the setting by this command. Expect not many "ABORT" events to be ignorable. @* A special property of this command is that it works preemptive if given as program start argument. I.e. the first -abort_on setting among the start arguments is in effect already when the first operations of @command{xorriso} begin. Only "-abort_on" with dash "-" is recognized that way. @c man .TP @item -return_with severity exit_value @kindex -return_with controls exit value @cindex Process, control exit value, -return_with Set the threshold and exit_value to be returned at program end if no abort has happened. This is to allow @command{xorriso} to go on after problems but to get a failure indicating exit value from the program, nevertheless. Useful is a value lower than the -abort_on threshold, down to "WARNING". @* exit_value may be either 0 (indicating success to the starter of the program) or a number between 32 and 63. Some other exit_values are used by @command{xorriso} if it decides to abort the program run: @* 1=abort due to external signal @* 2=no program arguments given @* 3=creation of @command{xorriso} main object failed @* 4=failure to start libburnia-project.org libraries @* 5=program abort during argument processing @* 6=program abort during dialog processing @c man .TP @item -report_about severity @kindex -report_about controls verbosity @cindex Process, control verbosity, -report_about Set the threshold for events to be reported. @* Useful: "SORRY", "WARNING", "HINT", "NOTE", "UPDATE", "DEBUG", "ALL" @* Regardless what is set by -report_about, messages get always reported if they reach the severity threshold of -abort_on . @* Event messages are sent to the info channel "I" which is usually stderr but may be influenced by command -pkt_output. Info messages which belong to no event get attributed severity "NOTE". @* A special property of this command is that the first -report_about setting among the start arguments is in effect already when the first operations of @command{xorriso} begin. Only "-report_about" with dash "-" is recognized that way. @c man .TP @item -signal_handling mode @kindex -signal_handling controls handling of system signals @cindex Control, signal handling, -signal_handling Control the installation of a signal handler which shall react on external signals (e.g. from program "kill" or from keys Ctrl+C) or on signals caused by severe program errors. @* Mode "on" is the default. It uses the signal handler of libburn which produces ugly messages but puts much effort in releasing optical drives before @command{xorriso} ends. @* Mode "off" as first -signal_handling among the start arguments prevents all own signal precautions of @command{xorriso}. Inherited signal handler settings stay as they are. @* It works like "sig_dfl" if given after other signal handling was already established at program start. @* Mode "sig_dfl" uses the system provided default handling of signals, which is normally a sudden abort of the program. To prevent stuck drives, the libburn handler is used during burning, blanking, and formatting on MMC drives. @* Mode "sig_ign" tries to ignore as many signal types as possible. This imposes the risk that @command{xorriso} refuses to end until externally kill -9 if performed. kill -9 then imposes the risk that the drive is left in unusable state and needs poweroff to be reset. So during burning, blanking, and formatting wait for at least their normal run time before killing externally. @* A special property of this command is that the first -signal_handling setting among the start arguments is in effect already when the first operations of @command{xorriso} begin. Only "-signal_handling" with dash "-" is recognized that way. @c man .TP @item -error_behavior occasion behavior @kindex -error_behavior controls error workarounds @cindex Process, error workarounds, -error_behavior Control the program behavior at problem event occasions. For now this applies to occasions "image_loading" which is given while an image tree is read from the input device, and to "file_extraction" which is given with osirrox commands like -extract. @* With "image_loading" there are three behaviors available: @* "best_effort" goes on with reading after events with severity below FAILURE if the threshold of command -abort_on allows this. @* "failure" aborts image tree reading on first event of at least SORRY. It issues an own FAILURE event. This is the default. @* "fatal" acts like "failure" but issues the own event as FATAL. @* With occasion "file_extraction" there are three behaviors: @* "keep" maintains incompletely extracted files on disk. This is the default. @* "delete" removes files which encountered errors during content extraction. @* "best_effort" starts a revovery attempt by means of -extract_cut if the file content stems from the loaded ISO image and is not filtered. @end table @c man .TP @c man .B Dialog mode control: @node DialogCtl, Inquiry, Exception, Commands @section Dialog mode control @table @asis @c man .TP @item -dialog "on"|"off"|"single_line" @kindex -dialog enables dialog mode @cindex Dialog, enable dialog mode, -dialog Enable or disable to enter dialog mode after all program arguments are processed. In dialog mode input lines get prompted via readline or from stdin. @* If no -abort_on severity was set when dialog starts, then "NEVER" is set to avoid abort in most cases of wrong input or other problems. Before dialog begins, the default is "FAILURE" which e.g. aborts on unknown commands. @* Mode "on" supports input of newline characters within quotation marks and line continuation by trailing backslash outside quotation marks. Mode "single_line" does not. @c man .TP @item -page length width @kindex -page set terminal geometry @cindex Dialog, terminal geometry, -page Describe terminal to the text pager. See also above, paragraph Result pager. @* If parameter length is nonzero then the user gets prompted after that number of terminal lines. Zero length disables paging. @* Parameter width is the number of characters per terminal line. It is used to compute the number of terminal lines which get occupied by an output line. A usual terminal width is 80. @c man .TP @item -use_readline "on"|"off" @kindex -use_readline enables readline for dialog @cindex Dialog, line editing, -use_readline If "on" then use readline for dialog. Else use plain stdin. @* See also above, paragraph Dialog, Readline, Result pager. @c man .TP @item -reassure "on"|"tree"|"off" @kindex -reassure enables confirmation question @cindex Dialog, confirmation question, -reassure If "on" then ask the user for "y" or "n": @* before deleting or overwriting any file in the ISO image, @* before overwriting any disk file during restore operations, @* before rolling back pending image changes, @* before committing image changes to media, @* before changing the input drive, @* before blanking or formatting media, @* before ending the program. @* With setting "tree" the reassuring prompt will appear for an eventual directory only once and not for each file in its whole subtree. @* Setting "off" silently kills any kind of image file object and performs above irrevocable actions. @* To really produce user prompts, command -dialog needs to be set to "on". Note that the prompt does not appear in situations where file removal is forbidden by command -overwrite. -reassure only imposes an additional curb for removing existing file objects. @* Be aware that file objects get deleted from the ISO image immediately after confirmation. They are gone even if the running command gets aborted and its desired effect gets revoked. In case of severe mess-up, consider to use -rollback to revoke the whole session. @end table @c man .TP @c man .B Drive and media related inquiry actions: @node Inquiry, Navigate, DialogCtl, Commands @section Drive and media related inquiry actions @table @asis @c man .TP @item -devices @kindex -devices gets list of drives @cindex Drive, get drive list, -devices Show list of available MMC drives with the addresses of their libburn standard device files. @* This is only possible when no ISO image changes are pending. After this command was executed, there is no drive current and no image loaded. @* In order to be visible, a device has to offer rw-permissions with its libburn standard device file. Thus it might be only the @strong{superuser} who is able to see all drives. @* Drives which are occupied by other processes get not shown. @c man .TP @item -device_links @kindex -device_links gets list of drives @cindex Drive, get drive list, -device_links Like -devices, but presenting the drives with addresses of symbolic links which point to the actual device files. @* Modern GNU/Linux systems may shuffle drive addresses from boot to boot. The udev daemon is supposed to create links which always point to the same drive, regardless of its system address. The command -device_links shows the addresses of such links if they begin by "/dev/dvd" or "/dev/cd". Precedence is: "dvdrw", "cdrw", "dvd", "cdrom", "cd". @c man .TP @item -toc @* @kindex -toc shows list of sessions @cindex Table-of-content, show, -toc Show media specific tables of content. This is the session history of the medium, not the ISO image directory tree. @* In case of overwritable media holding a valid ISO image, it may happen that only a single session gets shown. But if the first session on the overwritable media was written by @command{xorriso} then a complete session history can be emulated. @* A drive which is incapable of writing may show any media as CD-ROM or DVD-ROM with only one or two sessions on it. The last of these sessions is supposed to be the most recent real session then. @* Some read-only drives and media show no usable session history at all. Command -rom_toc_scan might help. @* If input device and output device are both acquired and not the same, then both tables-of-content get shown. @c man .TP @item -toc_of "in"|"out"|"all"[":short"] @kindex -toc_of shows list of sessions @cindex Table-of-content, show parts of, -toc_of Like command -toc but explicitly choosing which drive's table-of-content to show. "in" shows -indev or -dev, "out" shows -outdev or -dev, "all" shows the same as -toc. @* If ":short" is appended to the drive choosing word, then only a short summary of drive state and medium content is printed. @* As further difference to -toc, this command does not emit FAILURE events if the desired drive is not acquired. @c man .TP @item -toc_info_type typetext @kindex -toc_info_type shows list of sessions @cindex Table-of-content, choose info to show, -toc_info_type Choose which information to show in the rightmost column of -toc and -toc_of. @* Type "volid" is the default. It shows the Volume Ids of the listed ISO sessions. @* Type "creation_time" or "ctime" chooses the Creation Times. @* Type "modification_time" or "mtime" chooses the Modification Times. @* Appending "_gmt" to a time type text causes the time information to be shown in ECMA-119 format YYYYMMDDhhmmsscc in timezone GMT. Else it is shown as timestamps YYYY.MM.DD.hhmmss in the local timezone of the system. @c man .TP @item -assess_indev_features "plain"|"cmd"|"as_mkisofs"|"replay" @kindex -assess_indev_features shows filesystem features @cindex Filesytem features, show, -assess_indev_features Inspect the filesystem on -indev for the presence of Rock Ridge, Joliet, or ISO 9660:1999, and for traces of other write options which seem to have been used when the filesystem was created. @* Note that this command does not detect and report a possibly present HFS+ tree. @* Mode "cmd" lists xorriso commands which would activate the detected settings. @* Mode "as_mkisofs" lists options of the -as mkisofs emulation, which would activate those of the detected settings which are not default. @* Mode "replay" performs the commands which get listed by mode "cmd". @* Mode "plain" lists after a "Indev feature: " header name-value pairs as delivered by libisofs function iso_read_image_feature_named(). See libisofs.h. The other modes derive their output from this list. I.e. the sequence of commands from "cmd" follows the sequence of "plain". @* Not leading to "cmd" lines are: @* "size=" tells the number of 2048 byte blocks of the filesystem. @* "eltorito=1" tells that El Torito boot equipment was detected. @* "tree_loaded=" tells which tree was loaded by -indev: @* 0 = ISO 9660 , 1 = Joliet , 2 = ISO 9660:1999 @* "tree_loaded_text=" tells the same by name: "ISO9660", "Joliet", "ISO9660:1999" @* "rr_loaded=1" tells that Rock Ridge information was loaded with the tree. @* "aaip=1" tells that AAIP information was detected (ACL, xattr, MD5, ...). @* "relaxed_vol_atts=1" tells that the volume attributes like -volid or -preparer_id bear characters outside the restricted character sets which are specified for them by ECMA-119. @* "rrip_1_10_px_ino=1" tells that with Rock Ridge 1.10 a PX entry was found which looks like from Rock Ridge 1.12. @c man .TP @item -mount_cmd drive entity id path @kindex -mount_cmd composes mount command line @cindex Session, mount command line, -mount_cmd Emit an appropriate command line for mounting the ISO session indicated by drive, entity and id. The result will be different on GNU/Linux and on FreeBSD or NetBSD. @* drive can be "indev" or "outdev" to indicate already acquired drives, or it can be the path of a not yet acquired drive. Prefix "stdio:" for non-MMC drives is not mandatory. @* See command @command{-load} for the meaning of entity and id. @* Entities are: "auto", "session", "track", "lba", "sbsector", "volid", "at_time", "before", "not_after", "after", and "not_before". @* Each is to be used with its appropriate kind of id string: "auto", session number, track number, block number, search expression for volume id, or time string. @* path will be used as mount point and must already exist as a directory on disk. @* The command gets printed to the result channel. See command -mount for direct execution of this command. @c man .TP @item -mount_opts option[:option...] @kindex -mount_cmd controls mount command @cindex Session, mount parameters, -mount_opts Set options which influence -mount and -mount_cmd. Currently there is only option "exclusive" which is default and its counterpart "shared". The latter causes @command{xorriso} not to give up the affected drive with command -mount. On GNU/Linux it adds mount option "loop" which may enable mounting of several sessions of the same block device at the same time. One should not write to a mounted optical medium, of course. Take care to umount all sessions before ejecting. @c man .TP @item -session_string drive entity id format @kindex -session_string composes session info line @cindex Session, info string, -session_string Print to the result channel a text which gets composed according to format and the parameters of the addressed session. @* Formats "linux:"path or "freebsd:"path produce the output of -mount_cmd for the given operating systems. @* In other texts @command{xorriso} will substitute the following parameter names. An optional prefix "string:" will be removed. @* "%device%" will be substituted by the mountable device path of the drive address. @* "%sbsector%" will be substituted by the session start sector. @* "%track%", "%session%", "%volid%" will be substituted by track number, session number, or volume id of the depicted session. @c man .TP @item -print_size @kindex -print_size predicts image size @cindex Write, predict image size, -print_size Print the foreseeable consumption of 2048 byte blocks by next -commit. This can last a while as a -commit gets prepared and only in last moment is revoked by this command. The result depends on several settings and also on the kind of output device. If no -jigdo options are set and not command -as "mkisofs" was used, then -padding (300 kB by default) is not counted as part of the image size. @* If an El Torito boot image file is already depicted, then command -print_size automatically executes -boot_image "any" "next". This means that the properties of that boot image cannot be edited by subsequent commands. @c man .TP @item -tell_media_space @kindex -tell_media_space reports free space @cindex Write, free space, -tell_media_space Print available space on the output medium and the free space after subtracting already foreseeable consumption by next -commit. @* Note that the title of the prediction "After commit :" is misleading. It is rather the space that may still be filled in this session without making the next -commit fail from medium overflow. @* The free space after the next -commit might be smaller by several MB. This depends on medium type, number of recorded sessions, and drive habits. @c man .TP @item -pvd_info @kindex -pvd_info shows image id strings @cindex Image, show id strings, -pvd_info Print various ID strings and timestamps which can be found in loaded ISO images. Some of the IDs may be changed by commands like -volid or -publisher. For these IDs -pvd_info reports what would be written with the next -commit. @* The timestamps get shown in ECMA-119 format YYYYMMDDhhmmsscc and timezone GMT. They do not get automatically propagated from loaded image to newly written image. The ones for new images may be set by command -volume_date. See there for the meaning of the particular timestamps. @c man .TP @item -report_el_torito mode @kindex -report_el_torito shows Boot Catalog @cindex Image, show Boot Catalog @* With mode @strong{plain} print a report about the information found in the El Torito boot catalog of the loaded ISO image. @* With mode @strong{help} print a text which explains the meaning of the lines put out by "plain". @* Mode @strong{cmd} tries to print the @strong{xorriso} commands which are necessary to produce the found boot equipment: disk identifiers, El Torito boot images, and System Area. Disk identifiers are strings which the booting operating system might use to find the ISO filesystem from where it comes. Currently known is the use of volume id and modification date. @* The intended use case is modification of the filesystem by having -indev and -outdev pointing to different images or drives. The result might be insufficient, if the found equipment cannot be produced by xorriso. Various SORRY events may arise in this case, but it is not guaranteed that xorriso recognizes all its insufficiencies. @* Mode @strong{as_mkisofs} tries to print the @strong{xorriso -as mkisofs} options, which are necessary to produce the found equipment. The intended use case is to use the mounted filesystem as input tree together with the printed options. @* If CHRP equipment is detected, then modes @strong{cmd} and @strong{as_mkisofs} issue some of the relaxation commands or options which get detected by command @strong{-assess_indev_features}. This happens because CHRP firmware reads file paths from file /ppc/bootinfo.txt and tries to find them case-insensitively in the ECMA-119 tree without using Rock Ridge. If such a path has actually forbidden properties, like the name "powerpc-ieee1275", then the relaxations are needed to bring it unmangled into the ECMA-119 tree. @* It is important to keep in mind that the file paths shown in the report lines and commands were registered directly after image loading. Possible filesystem manipulations which later remove these paths or replace their file content will not influence the report lines or commands. @c man .TP @item -report_system_area mode @kindex -report_system_area shows MBR, GPT, and alike @cindex Image, show MBR, GPT, and alike, -pvd_info With mode @strong{plain} print a report about the information found in the System Area of the loaded ISO image. The report consists of zero to many lines with a header text, a colon, and information text. @* With mode @strong{help} print a text which explains the meaning of the lines put out by "plain". You probably will have to look for more documentation which explains the technical details of the mentioned boot facilities. @* Modes @strong{cmd} and @strong{as_mkisofs} work like with command -report_el_torito. See above. @* It is important to keep in mind that the file paths shown in the report lines and commands were registered directly after image loading. Possible filesystem manipulations which later remove these paths or replace their file content will not influence the report lines or commands. @* With mode @strong{gpt_disk_guid} print the GPT disk GUID of the loaded ISO in RFC 4122 text format to result channel. It is not considered an error if no GPT is present. In this case nothing is printed to result channel. @* With mode @strong{gpt_crc_of:}disk_path read up to 32 KiB from the disk file with the path given after the colon. Compute the GPT compliant CRC number and print it to the result channel. The number is shown like "0x690fd979". The special disk_path "-" causes reading from standard input. @* With mode @strong{make_guid} print a pseudo-random GUID in RFC 4122 text format to result channel. @end table @c man .TP @c man .B Navigation in ISO image and disk filesystem: @node Navigate, Verify, Inquiry, Commands @section Navigation in ISO image and disk filesystem @table @asis @c man .TP @item -cd iso_rr_path @kindex -cd sets working directory in ISO @cindex Navigate, set ISO working directory, -cd Change the current working directory in the ISO image. This is prepended to iso_rr_paths which do not begin with '/'. @* It is possible to set the working directory to a path which does not exist yet in the ISO image. The necessary parent directories will be created when the first file object is inserted into that virtual directory. Use -mkdir if you want to enforce the existence of the directory already at first insertion. @c man .TP @item -cdx disk_path @kindex -cdx sets working directory on disk @cindex Navigate, set disk working directory, -cdx Change the current working directory in the local filesystem. To be prepended to disk_paths which do not begin with '/'. @c man .TP @item -pwd @* @kindex -pwd tells working directory in ISO @cindex Navigate, tell ISO working directory, -pwd Tell the current working directory in the ISO image. @c man .TP @item -pwdx @kindex -pwdx tells working directory on disk @cindex Navigate, tell disk working directory, -pwdx @* Tell the current working directory in the local filesystem. @c man .TP @item -ls iso_rr_pattern [***] @kindex -ls lists files in ISO image @cindex Navigate, list ISO files, -ls List files in the ISO image which match shell patterns (i.e. with wildcards '*' '?' '[a-z]'). If a pattern does not begin with '/' then it is compared with addresses relative to -cd. @* Directories are listed by their content rather than as single file item. @* Pattern expansion may be disabled by command -iso_rr_pattern. @c man .TP @item -lsd iso_rr_pattern [***] @kindex -lsd lists files in ISO image @cindex Navigate, list ISO files, -lsd Like -ls but listing directories as themselves and not by their content. This resembles shell command ls -d. @c man .TP @item -lsl iso_rr_pattern [***] @kindex -lsl lists files in ISO image @cindex Navigate, list ISO files, -lsl Like -ls but also list some of the file attributes. The output format resembles shell command ls -ln. @* File type 'e' indicates the El Torito boot catalog. @* If the file has non-trivial ACL, then a '+' is appended to the permission info. If the file is hidden, then 'I' for "iso_rr", 'J' for "joliet", 'A' for "hfsplus", 'H' for multiple hiding gets appended. Together with ACL it is 'i', 'j', 'a', 'h'. @c man .TP @item -lsdl iso_rr_pattern [***] @kindex -lsdl lists files in ISO image @cindex Navigate, list ISO files, -lsdl Like -lsd but also list some of the file attributes. The output format resembles shell command ls -dln. @c man .TP @item -lsx disk_pattern [***] @kindex -lsx lists files on disk @cindex Navigate, list disk files, -lsx List files in the local filesystem which match shell patterns. Patterns which do not begin with '/' are used relative to -cdx. @* Directories are listed by their content rather than as single file item. @* Pattern expansion may be disabled by command -disk_pattern. @c man .TP @item -lsdx disk_pattern [***] @kindex -lsdx lists files on disk @cindex Navigate, list disk files, -lsdx Like -lsx but listing directories as themselves and not by their content. This resembles shell command ls -d. @c man .TP @item -lslx disk_pattern [***] @kindex -lslx lists files on disk @cindex Navigate, list disk files, -lslx Like -lsx but also listing some of the file attributes. Output format resembles shell command ls -ln. @c man .TP @item -lsdlx disk_pattern [***] @kindex -lsdlx lists files on disk @cindex Navigate, list disk files, -lsdlx Like -lsdx but also listing some of the file attributes. Output format resembles shell command ls -dln. @c man .TP @item -getfacl iso_rr_pattern [***] @kindex -getfacl shows ACL in ISO image @cindex ACL, show in ISO image, -getfacl Print the access permissions of the given files in the ISO image using the format of shell command getfacl. If a file has no ACL then it gets fabricated from the -chmod settings. A file may have a real ACL if it was introduced into the ISO image while command -acl was set to "on". @c man .TP @item -getfacl_r iso_rr_pattern [***] @kindex -getfacl_r shows ACL in ISO image @cindex ACL, show in ISO image, -getfacl_r Like -gefacl but listing recursively the whole file trees underneath eventual directories. @c man .TP @item -getfattr iso_rr_pattern [***] @kindex -getfattr shows xattr in ISO image @cindex xattr, show in ISO image, -getfattr Print the xattr of the given files in the ISO image. If a file has no such xattr then noting is printed for it. The choice of namespaces depends on the setting of command -xattr: "on" or "user" restricts it to namespace "user", "any" only omits namespace "isofs". @c man .TP @item -getfattr_r iso_rr_pattern [***] @kindex -getfattr_r shows xattr in ISO image @cindex xattr, show in ISO image, -getfattr_r Like -gefattr but listing recursively the whole file trees underneath of directories. @c man .TP @item -lsattr iso_rr_pattern [***] @kindex -lsattr shows Linux file attributes in ISO image @cindex Linux file attributes, show in ISO image, -lsattr Print the Linux file attributes of the given files like program lsattr(1) would do with disk files. The meaning of the shown flag letters are described in man 1 chattr with the exception of '-', which is shown as placeholder for an unset flag. @* The given files will get a line printed even if they have no Linux file attributes attached. In this case all flags will be shown as '-'. @c man .TP @item -du iso_rr_pattern [***] @kindex -du show directory size in ISO image @cindex Navigate, directory size in ISO image, -du Recursively list size of directories and files in the ISO image which match one of the patterns. similar to shell command du -k. @c man .TP @item -dus iso_rr_pattern [***] @kindex -dus show directory size in ISO image @cindex Navigate, directory size in ISO image, -dus List size of directories and files in the ISO image which match one of the patterns. Similar to shell command du -sk. @c man .TP @item -dux disk_pattern [***] @kindex -dux show directory size on disk @cindex Navigate, directory size in on disk, -dux Recursively list size of directories and files in the local filesystem which match one of the patterns. Similar to shell command du -k. @c man .TP @item -dusx disk_pattern [***] @kindex -dusx show directory size on disk @cindex Navigate, directory size in on disk, -dusx List size of directories and files in the local filesystem which match one of the patterns. Similar to shell command du -sk. @c man .TP @item -findx disk_path [test [op] [test ...]] [-exec action [params]] @minus{}@minus{} @kindex -findx traverses disk tree @cindex Tree, disk, traverse, -findx Like -find but operating on local filesystem and not on the ISO image. The -findx command is subject to the settings of -follow. @* -findx accepts the same tests as -find, but only the following ones work like described with -find: @* -bad_outname, -decision, -disk_name, -disk_path, -has_acl, -has_any_xattr, -has_lfa_flags, -has_some_lfa_flags_of, -has_xattr, -lba_range, -maxdepth, -mindepth, -name, -or_use_pattern, -prune, -size, -true, -type, -use_pattern, -wholename @* The others get defaulted to -false, because they are not applicable to disk files. @* Test -type accepts the same parameters as with -find. Additionally it recognizes type "mountpoint" (or "m") which matches subdirectories which reside on a different device than their parent. This type never matches the disk_path given as start address for -findx. @* Test -lba_range matches only if its parameter start_lba is 0. @* Tests -has_lfa_flags and -has_some_lfa_flags_of ignore non-settable file attribute flags if -lfa_flags is set to on:import_only_settable. @* -findx accepts the -exec actions as does -find. But except the following few actions it will always perform action "echo". @* @table @asis @sp 1 @item in_iso reports the path if its counterpart exists in the ISO image. For this the disk_path of the -findx command gets replaced by the iso_rr_path given as parameter. @* E.g.: -findx /home/thomas -exec in_iso /thomas_on_cd @minus{}@minus{} @* @item not_in_iso reports the path if its counterpart does not exist in the ISO image. The report format is the same as with command -compare. @* @item add_missing iso_rr_path_start adds the counterpart if it does not yet exist in the ISO image and marks it for "rm_merge" as non-removable. @* E.g.: -findx /home/thomas -exec add_missing /thomas_on_cd @minus{}@minus{} @* @item is_full_in_iso reports if the counterpart in the ISO image contains files. To be used with -type "m" to report mount points. @* @item empty_iso_dir deletes all files from the counterpart in the ISO image. To be used with -type "m" to truncate mount points. @* @item print_outname prints in the first line the filename as found on disk, and in the second line the filename after conversion forth and back between local character set and one of the namespaces "rockridge", "joliet", "ecma119", or "hfsplus". The third output line is "--" . @* The name conversion does not take into respect the possibility of name collisions in the target namespace. Such collisions are most likely in "joliet" and "ecma119", where they get resolved by automatic file name changes. @* @item estimate_size prints a lower and an upper estimation of the number of blocks which the found files together will occupy in the emerging ISO image. This does not account for the superblock, for the directories in the -findx path, or for image padding. @* @item getfacl prints access permissions in ACL text form to the result channel. @* @item getfattr prints xattr name-value pairs to the result channel. The choice of namespaces depends on the setting of command -xattr: "off", "on", or "user" restricts it to the namespace "user", "any" causes all namespaces to be shown. @* @item get_any_xattr prints xattr name-value pairs to the result channel. All namespaces are shown regardless of the setting of command -xattr. @* @item list_extattr mode prints a script to the result channel, which would use FreeBSD command setextattr to set the file's xattr name-value pairs of user namespace. See -find for a description of parameter mode. @* E.g. -exec list_extattr e -- @* @item lsattrd prints the Linux file attribute flags like command -lsattrd does. This shows non-settable flags, too, even if they are to be ignored by the setting of command -lfa_flags. @end table @c man .TP @item -compare disk_path iso_rr_path @kindex -compare reports ISO/disk differences @cindex Verify, compare ISO and disk file, -compare Compare attributes and eventual data file content of a fileobject in the local filesystem with a file object in the ISO image. The iso_rr_path may well point to an image file object which is not yet committed, i.e. of which the data content still resides in the local filesystem. Such data content is prone to externally caused changes. @* If iso_rr_path is empty then disk_path is used as path in the ISO image too. @* Differing attributes are reported in detail, differing content is summarized. Both to the result channel. In case of no differences no result lines are emitted. @c man .TP @item -compare_r disk_path iso_rr_path @kindex -compare_r reports ISO/disk differences @cindex Verify, compare ISO and disk tree, -compare_r Like -compare but working recursively. I.e. all file objects below both addresses get compared whether they have counterparts below the other address and whether both counterparts match. @c man .TP @item -compare_l disk_prefix iso_rr_prefix disk_path [***] @kindex -compare_l reports ISO/disk differences @cindex Verify, compare ISO and disk, -compare_l Perform -compare_r with each of the disk_path parameters. iso_rr_path will be composed from disk_path by replacing disk_prefix by iso_rr_prefix. @c man .TP @item -show_stream iso_rr_path [***] @kindex -show_stream shows data source and filters @cindex Filter, show chain, -show_stream Display the content stream chain of data files in the ISO image. The chain consists of the iso_rr_name and one or more streams, separated by " < " marks. A stream description consists of one or more texts, separated by ":" characters. The first text tells the stream type, the following ones, if ever, describe its individual properties. Frequently used types are: @* disk:'disk_path' for local filesystem objects. @* image:'iso_rr_path' for ISO image file objects. @* cout:'disk_path offset count' for -cut_out files. @* extf:'filter_name' for external filters. @* --zisofs:algorithm:block_size for zisofs compression filters. @* --zisofs-decode:algorithm:block_size for zisofs uncompression filters. @* --gzip for internal gzip compression filters. @* --gunzip for internal gzip uncompression filters. @* Example: @* '/abc/xyz.gz' < extf:'gzip' < disk:'/home/me/x' @c man .TP @item -show_stream_r iso_rr_path [***] @kindex -show_stream_r shows data source and filters @cindex Filter, show chains of tree, -show_stream_r Like -show_stream but working recursively. @end table @c man .TP @c man .B Evaluation of readability and recovery: @node Verify, Restore, Navigate, Commands @section Evaluation of readability and recovery @c man .PP It is not uncommon that optical media produce read errors. The reasons may be various and get obscured by error correction which is performed by the drives and based on extra data on the media. If a drive returns data then one can quite trust that they are valid. But at some degree of read problems the correction will fail and the drive is supposed to indicate error. @* @command{xorriso} can scan a medium for readable data blocks, classify them according to their read speed, save them to a file, and keep track of successfully saved blocks for further tries on the same medium. @* By command -md5 checksums may get recorded with data files and whole sessions. These checksums are reachable only via indev and a loaded image. They work independently of the media type and can detect transmission errors. @table @asis @sp 1 @c man .TP @item -check_media [option [option ...]] @minus{}@minus{} @kindex -check_media reads media block by block @cindex Verify, check blocks, -check_media @cindex Recovery, retrieve blocks, -check_media Try to read data blocks from the indev drive, optionally copy them to a disk file, and finally report about the encountered quality. Several options may be used to modify the default behavior. @* The parameters given with this command override the default settings which may have been changed by command -check_media_defaults. See there for a description of available options. @* The result list tells intervals of 2 KiB blocks with start address, number of blocks and quality. Qualities which begin with "+" are supposed to be valid readable data. Qualities with "-" are unreadable or corrupted data. "0" indicates qualities which are not covered by the check run or are regularly allowed to be unreadable (e.g. gaps between tracks). @* Alternatively it is possible to report damaged files rather than blocks. @* If -md5 is "on" then the default mode what=tracks looks out for libisofs checksum tags for the ISO session data and checks them against the checksums computed from the data stream. @c man .TP @item -check_media_defaults [option [option ...]] @minus{}@minus{} @kindex -check_media_defaults sets -check_media options @cindex Verify, preset -check_media, -check_media_defaults Preset options for runs of -check_media, -extract_cut and best_effort file extraction. Options given with -check_media will override the preset options. -extract_cut will override some options automatically. @* An option consists of a keyword, a "=" character, and a value. Options may override each other. So their sequence matters. @* The default setting at program start is: @* use=indev what=tracks min_lba=-1 max_lba=-1 retry=default @* time_limit=28800 item_limit=100000 data_to='' event=ALL @* abort_file=/var/opt/xorriso/do_abort_check_media @* sector_map='' map_with_volid=off patch_lba0=off report=blocks @* bad_limit=invalid slow_limit=1.0 chunk_size=0s async_chunks=0 @* Option "reset=now" restores these startup defaults. @* Non-default options are: @* @table @asis @sp 1 @item report="files" lists the files which use damaged blocks (not with use=outdev). The format is like with find -exec report_damage. Note that a MD5 session mismatch marks all files of the session as damaged. If finer distinction is desired, perform -md5 off before -check_media. @* @item report="blocks_files" first lists damaged blocks and then affected files. @* @item use="outdev" reads from the output drive instead of the input drive. This avoids loading the ISO image tree from media. @* @item use="sector_map" does not read any media but loads the file given by option sector_map= and processes this virtual outcome. @* @item what="disc" scans the payload range of a medium without respecting track gaps. @* @item what="image" similar to "disc", but restricts scanning to the range of the ISO 9660 image, if present. @* @item min_lba=limit omits all blocks with addresses lower than limit. @* @item max_lba=limit switches to what=disc and omits all blocks above limit. @* @item chunk_size=size sets the number of bytes to be read in one low-level read operation. This gets rounded down to full blocks of 2048 bytes. 0 means automatic size. @* @item retry="on" forces read retries with minimal senseful chunk size when the normal read chunk produces a read error. This size is 1s with CD and stdio files, 16s with DVD (1 ECC Block), and 32s with BD (1 Cluster). By default, retries are only enabled with CD media. "retry=off" forbits retries for all media types. @* @item abort_file=disk_path gives the path of the file which may abort a scan run. Abort happens if the file exists and its mtime is not older than the start time of the run. Use shell command "touch" to trigger this. Other than an aborted program run, this will report the tested and untested blocks and go on with running @command{xorriso}. @* @item time_limit=seconds gives the number of seconds after which the scan shall be aborted. This is useful for unattended scanning of media which may else overwork the drive in its effort to squeeze out some readable blocks. Abort may be delayed by the drive gnawing on the last single read operation. Value -1 means unlimited time. @* @item item_limit=number gives the number of report list items after which to abort. Value -1 means unlimited item number. @* @item data_to=disk_path copies the valid blocks to the given file, which must support random access writing, unless disk_path is "-" which means standard output. @* In the latter case, patch_lba0= settings other than "off" yield failure. Further the usual result messages of -check_media get redirected to the info channel. But beware of result messages from other commands. Beware of -*dev "-" which redirect standard output to standard error. Keep the run simple: @* xorriso -indev /dev/sr0 -check_media data_to=- -- | md5sum @* xorriso -outdev /dev/sr0 -check_media data_to=- use=outdev \ what=disc min_lba=0 max_lba=999999 -- | sha256sum @* @item event=severity sets the given severity for a problem event which shall be issued at the end of a check run if data blocks were unreadable or failed to match recorded MD5 checksums. Severity "ALL" disables this event. @* @item sector_map=disk_path tries to read the file given by disk_path as sector bitmap and to store such a map file after the scan run. The bitmap tells which blocks have been read successfully in previous runs. It is the persistent memory for several scans on the same medium, even with intermediate eject, in order to collect readable blocks whenever the drive is lucky enough to produce them. The stored file contains a human readable TOC of tracks and their start block addresses, followed by binary bitmap data. @* By default, untested blocks are not considered bad, but rather as intentionally unread. If you expect time_limit= or item_limit= to abort the run, then consider to use bad_limit="untested". @* @item map_with_volid="on" examines tracks whether they are ISO images and prints their volume IDs into the human readable TOC of sector_map=. @* @item patch_lba0="on" transfers within the data_to= file a copy of the currently loaded session head to the start of that file and patches it to be valid at that position. This makes the loaded session the last valid session of the image file when it gets mounted or loaded as stdio: drive. New sessions will be appended after this last session and will overwrite any sessions which have followed it. @* @item patch_lba0="force" performs patch_lba0="on" even if @command{xorriso} believes that the copied data are not valid. @* patch_lba0= may also bear a number. If it is 32 or higher it is taken as start address of the session to be copied. In this case it is not necessary to have an -indev and a loaded image. ":force" may be appended after the number. @* @item bad_limit=threshold sets the highest quality which shall be considered as damage. Choose one of "good", "md5_match", "slow", "partial", "valid", "untested", "md5_mismatch", "invalid", "tao_end", "off_track", "unreadable". @* "valid" and "invalid" are qualities imported from a sector_map file. "tao_end" and "off_track" are intentionally not readable, but not bad either. "partial" are blocks retrieved from a partially readable chunk. They are supposed to be ok but stem from a suspicious neighborhood. @* "md5_match" and "md5_mismatch" regions overlap with regions of other quality. The former is a strong confirmation for quality, the latter only tells that one or more blocks of the region must be wrong. @* By default bad_limit is set higher than md5_mismatch, so that mismatches are classified as quality class "0" rather than "-". This means that the sectors of a MD5 mismatch range are recorded in the sector_map as successfully read, if the drive handed them out at all. Set "bad_limit=md5_mismatch" to let the sector_map record the whole mismatching range as yet not retrieved. @* @item slow_limit=threshold sets the time threshold for a single read chunk to be considered slow. This may be a fractional number like 0.1 or 1.5. @* @item async_chunks=number enables asynchronous MD5 processing if number is 2 or larger. In this case the given number of read chunks is allocated as fifo buffer. On very fast MMC drives try: chunk_size=64s async_chunks=16. @end table @c man .TP @kindex -check_md5 verifies file checksum @cindex Verify, file checksum, -check_md5 @item -check_md5 severity iso_rr_path [***] Compare the data content of the given files in the loaded image with their recorded MD5 checksums, if there are any. In case of any mismatch an event of the given severity is issued. It may then be handled by appropriate settings of commands -abort_on or -return_with which both can cause non-zero exit values of the program run. Severity ALL suppresses that event. @* This command reports match and mismatch of data files to the result channel. Non-data files cause NOTE events. There will also be UPDATE events from data reading. @* If no iso_rr_path is given then the whole loaded session is compared with its MD5 sum. Be aware that this covers only one session and not the whole image if there are older sessions. @c man .TP @item -check_md5_r severity iso_rr_path [***] @kindex -check_md5_r verifies file tree checksums @cindex Verify, file tree checksums, -check_md5_r Like -check_md5 but checking all data files underneath the given paths. Only mismatching data files will be reported. @end table @c man .TP @c man .B osirrox ISO-to-disk restore commands: @node Restore, Emulation, Verify, Commands @section osirrox ISO-to-disk restore commands @c man .PP Normally @command{xorriso} only writes to disk files which were given as stdio: pseudo-drives or as log files. But its alter ego osirrox is able to extract file objects from ISO images and to create, overwrite, or delete file objects on disk. @* Disk file exclusions by -not_mgt, -not_leaf, -not_paths apply. The exclusion tests are made with the paths and names for the disk files. If exclusion of paths or names in the ISO image is desired, then use image manipulation commands like -rm or -find ... -exec rm before extraction, and end the program by -rollback_end . @* Excluded disk_path parameters of extraction commands cause SORRY events. Implicitly given paths in trees under disk_path parameters are excluded silently. @* If disk file objects already exist then the settings of -overwrite and -reassure apply. But -overwrite "on" only triggers the behavior of -overwrite "nondir". I.e. directories cannot be deleted. @* Access permissions of files in the ISO image do not restrict restoring. The directory permissions on disk have to allow rwx. @table @asis @sp 1 @c man .TP @item -osirrox setting[:option:...] @kindex -osirrox enables ISO-to-disk copying @cindex Restore, enable ISO-to-disk, -osirrox Setting @strong{off} disables disk filesystem manipulations. This is the default unless the program was started with leafname @strong{osirrox}. Elsewise the capability to restore files can be enabled explicitly by -osirrox @strong{on}. It can be irrevocably disabled by -osirrox @strong{banned}. @* The setting @strong{blocked} is like @strong{off}. But it can only be revoked by setting @strong{unblock}, which elsewise is like @strong{on}. This can be used to curb command scripts which might use @strong{on} undesiredly. @* To enable restoring of special files by @strong{device_files} is potentially dangerous. The meaning of the number st_rdev (see man 2 stat) depends much on the operating system. Best is to restore device files only to the same system from where they were copied. If not enabled, device files in the ISO image are ignored during restore operations. @* Due to a bug of previous versions, device files from previous sessions might have been altered to major=0, minor=1. So this combination does not get restored. @* Option @strong{concat_split_on} is default. It enables restoring of split file directories as data files if the directory contains a complete collection of -cut_out part files. With option @strong{concat_split_off} such directories are handled like any other ISO image directory. @* Option @strong{auto_chmod_off} is default. If @strong{auto_chmod_on} is set then access restrictions for disk directories get circumvented if those directories are owned by the effective user who runs @command{xorriso}. This happens by temporarily granting rwx permission to the owner. @* Option @strong{sort_lba_on} may improve read performance with optical drives. It can restore large numbers of hard links without exhausting -temp_mem_limit. It does not preserve directory mtime and it needs -osirrox option auto_chmod_on in order to extract directories which offer no write permission. Default is @strong{sort_lba_off}. @* Option @strong{o_excl_on} is the default unless the program was started with leafname "osirrox". On GNU/Linux it tries to avoid using drives which are mounted or in use by other libburn programs. Option @strong{o_excl_off} on GNU/Linux enables access to such drives by the equivalent of -drive_access "shared:readonly". I.e. drives which get acquired while @strong{o_excl_off} will refuse to get blanked, formatted, written, or ejected. But be aware that even harmless inquiries can spoil ongoing burns of CD-R[W] and DVD-R[W]. @* Option @strong{strict_acl_off} is default. It tolerates on FreeBSD the presence of directory "default" ACLs in the ISO image. With @strong{strict_acl_on} these GNU/Linux ACLs cause on FreeBSD a FAILURE event during restore with -acl "on". @* Option @strong{check_md5_off} disables MD5 checking during copy to disk. The default option @strong{check_md5_on} enables it if -md5 is "on". If a data file with recorded MD5 is copied as a whole to the disk filesystem, then the MD5 of the copied content gets computed and compared with the recorded MD5. A mismatch causes an error message of severity SORRY. Option @strong{check_md5_force} causes an error message if -md5 is "on" but no MD5 is recorded for the data file. @* Option @strong{sparse=} controls production of sparse files during extraction of files from the ISO filesystem. Default is @strong{sparse=off}. @* A positive number like in @strong{sparse=1m} sets the minimum requirement for the length of a sequence of 0-bytes which shall be represented by a gap. This saves disk space if the disk filesystem supports sparse files. A gap gets created by help of lseek(2) if a sequence of read buffers, which contain only 0-bytes, bears at least the minimum amount of bytes. Expect read buffers to be in the size range of 32k or 64k. @* Command -paste_in creates gaps only if the writing begins at or after the end of the existing disk file. So the sequence of -paste_in commands matters. Command -concat does not create sparse files. @c man .TP @item -extract iso_rr_path disk_path @kindex -extract copies file tree to disk @cindex Restore, copy file tree to disk, -extract Copy the file objects at and underneath iso_rr_path to their corresponding addresses at and underneath disk_path. This is the inverse of -map or -update_r. @* If iso_rr_path is a directory and disk_path is an existing directory then both trees will be merged. Directory attributes get extracted only if the disk directory is newly created by the copy operation. Disk files get removed only if they are to be replaced by file objects from the ISO image. @* As many attributes as possible are copied together with restored file objects. @c man .TP @item -extract_single iso_rr_path disk_path @kindex -extract_single copies file to disk @cindex Restore, copy file to disk, -extract_single Like -extract, but if iso_rr_path is a directory then its sub tree gets not restored. @c man .TP @item -extract_l iso_rr_prefix disk_prefix iso_rr_path [***] @kindex -extract_l copies files to disk @cindex Restore, copy files to disk, -extract_l Perform -extract with each of the iso_rr_path parameters. disk_path will be composed from iso_rr_path by replacing iso_rr_prefix by disk_prefix. @c man .TP @item -extract_cut iso_rr_path byte_offset byte_count disk_path @kindex -extract_cut copies file piece to disk @cindex Restore, copy file piece to disk, -extract_cut Copy a byte interval from a data file out of an ISO image into a newly created disk file. The main purpose for this is to offer a way of handling large files if they are not supported by mount -t iso9660 or if the target disk filesystem cannot store large files. @* If the data bytes of iso_rr_path are stored in the loaded ISO image, and no filter is applied, and byte_offset is a multiple of 2048, then a special run of -check_media is performed. It may be quicker and more rugged than the general reading method. @c man .TP @item -cpx iso_rr_path [***] disk_path @kindex -cpx copies files to disk @cindex Restore, copy files to disk, -cpx Copy single leaf file objects from the ISO image to the address given by disk_path. If more then one iso_rr_path is given then disk_path must be a directory or non-existent. In the latter case it gets created and the extracted files get installed in it with the same leafnames. @* Missing directory components in disk_path will get created, if possible. @* Directories are allowed as iso_rr_path only with -osirrox "concat_split_on" and only if they actually represent a complete collection of -cut_out split file parts. @c man .TP @item -cpax iso_rr_path [***] disk_path @kindex -cpax copies files to disk @cindex Restore, copy files to disk, -cpax Like -cpx but restoring mtime, atime as in ISO image and trying to set ownership and group as in ISO image. @c man .TP @item -cp_rx iso_rr_path [***] disk_path @kindex -cp_rx copies file trees to disk @cindex Restore, copy file trees to disk, -cp_rx Like -cpx but also extracting whole directory trees from the ISO image. @* The resulting disk paths are determined as with shell command cp -r : If disk_path is an existing directory then the trees will be inserted or merged underneath this directory and will keep their leaf names. The ISO directory "/" has no leaf name and thus gets mapped directly to disk_path. @c man .TP @item -cp_rax iso_rr_path [***] disk_path @kindex -cp_rx copies file trees to disk @cindex Restore, copy file trees to disk, -cp_rx Like -cp_rx but restoring mtime, atime as in ISO image and trying to set ownership and group as in ISO image. @c man .TP @item -paste_in iso_rr_path disk_path byte_offset byte_count @kindex -paste_in copies file into disk file @cindex Restore, copy file into disk file, -paste_in Read the content of a ISO data file and write it into a data file or device file on disk beginning at the byte_offset. Write at most byte_count bytes. The file depicted by disk_path has to support random write access. @* This is the inverse of command -cut_out. @c man .TP @item -concat mode [target | lim prog [args [...]] lim] iso_rr_path [***] @kindex -concat copies ISO file content @cindex File content, copy, -concat Copy the data content of one or more data files of the ISO image into a disk file object, into a file descriptor, or start a program and copy the data into its standard input. The latter is subject to the security restrictions for external filters. @* Modes @strong{overwrite} and @strong{append} write into the target which is given by the second parameter. This may be the path to a disk file object, or "-" which means standard output, or a text of the form /dev/fd/number, where number is an open file descriptor (e.g. standard error is /dev/fd/2). An existing target file is not removed before writing begins. If it is not able to take content data, then this command fails. Mode overwrite truncates regular data files to 0 size before writing into them. Example: @* @sp 1 -concat append /home/me/accumulated_text /my/iso/text -- @* @sp 1 Mode @strong{pipe} expects as second parameter a delimiter word which shall mark the end of the program argument list. The third argument is the disk_path to the program. It must contain at least one '/'. $PATH is not applied. Further parameters up to the announced delimiter word are used as arguments with the program start. Example: @* @sp 1 -iso_rr_pattern on \ @* -concat pipe + /usr/bin/wc + "/my/iso/files*" -- @* @sp 1 The further parameters in all modes are the iso_rr_paths of data files. Their content gets concatenated in the copy. @c man .TP @item -extract_boot_images disk_path @kindex -extract_boot_images copies boot equipment to disk @cindex Restore, copy boot equipment to disk, -extract_boot_images Copy boot equipment to disk, which is not necessarily represented as data files in the ISO filesystem. The data get written into various files in a disk directory, which may already exist or of which the parent must exist so that it can get created. @* Files may be missing if their corresponding information is not present in the ISO filesystem. Existing files do not get overwritten but rather cause a failure event. @* The same data may appear in different files. E.g. the El Torito boot image for EFI is often the same data as the EFI partition in MBR or GPT. @* File "eltorito_catalog.img" contains the El Torito Boot Catalog. @* Files "eltorito_img*_*.img" contain El Torito Boot images. The first "*" gives the image number, the second "*" gives the type: "bios", "mac", "ppc", "uefi", or a hex number. @* File "mbr_code_isohybrid.img" contains the ISOLINUX MBR template. @* File "mbr_code_grub2.img" contains the GRUB2 MBR template. @* File "systemarea.img" contains the whole 32 KiB of System Area if not all zero. @* Files "mbr_part*_efi.img" contain EFI partition images from the MBR partition table. The "*" text part gives the partition number. @* Files "mbr_part*_prep.img" contain PReP partition images. @* Files "gpt_part*_efi.img" contain EFI partition images from GPT. @* Files "gpt_part*_hfsplus.img" contain HFS+ partition images from GPT. To avoid extracting the whole HFS+ aspect of hybrid ISO filesystems, the partition image is extracted only if it has less than half of the size of the ISO filesystem or if the partition is outside the ISO filesystem. @c man .TP @item -mount drive entity id path @kindex -mount issues mount command for ISO session @cindex Session, issue mount command, -mount Produce the same line as -mount_cmd and then execute it as external program run after giving up the depicted drive. See also -mount_opts. This demands -osirrox to be enabled and normally will succeed only for the superuser. For safety reasons the mount program is only executed if it is reachable as /bin/mount or /sbin/mount. @end table @c man .TP @c man .B Command compatibility emulations: @node Emulation, Scripting, Restore, Commands @section Command compatibility emulations (cdrtools) @c man .PP Writing of ISO 9660 on CD is traditionally done by program mkisofs as ISO 9660 image producer and cdrecord as burn program. @command{xorriso} does not strive for their comprehensive emulation. Nevertheless it is ready to perform some of its core tasks under control of commands which in said programs trigger comparable actions. @table @asis @sp 1 @c man .TP @item -as personality option [options] @minus{}@minus{} @kindex -as emulates mkisofs or cdrecord @cindex Emulation, -as @* Perform the variable length option list as sparse emulation of the program depicted by the personality word. @* @sp 1 @cindex Emulation, mkisofs, -as @cindex mkisofs, Emulation Personality "@strong{mkisofs}" accepts the options listed with: @* -as mkisofs -help @minus{}@minus{} @* Among them: -R (always on), -r, -J, -o, -M, -C, -dir-mode, -file-mode, -path-list, -m, -exclude-list, -f, -print-size, -pad, -no-pad, -V, -v, -version, -graft-points, -z, -no-emul-boot, -b, -c, -boot-info-table, -boot-load-size, -input-charset, -G, -output-charset, -U, -hide, -hide-joliet, -hide-list, -hide-joliet-list, file paths and pathspecs. A lot of options are not supported and lead to failure of the mkisofs emulation. Some are ignored, but better do not rely on this tolerance. @* The supported options are documented in detail in xorrisofs.info and in man xorrisofs. The description here is focused on the effect of mkisofs emulation in the context of a @command{xorriso} run. @* Other than with the "cdrecord" personality there is no automatic -commit at the end of a "mkisofs" option list. Verbosity settings -v (= "UPDATE") and -quiet (= "SORRY") persist. The output file persists until things happen like -commit, -rollback, -dev, or end of @command{xorriso}. @* Options which affect all file objects in the ISO image, like -r or -dir-mode, will be applied only to files which are present in the ISO image when the command -as ends. If you use several -as mkisofs commands in the same run, then consider to put such options into the last -as command. @* If files are added to the image, then -pacifier gets set to "mkisofs" and -stdio_sync is defaulted to "off" if no such setting was made yet. @* -graft-points is equivalent to -pathspecs on. Note that pathspecs without "=" are interpreted differently than with @command{xorriso} command -add. Directories get merged with the root directory of the ISO image, other filetypes get mapped into that root directory. @* If pathspecs are given and if no output file was chosen before or during the "mkisofs" option list, then standard output (-outdev "-") will get into effect. If -o points to a regular file, then it will be truncated to 0 bytes when finally writing begins. This truncation does not happen if the drive is chosen by @command{xorriso} commands before -as mkisofs or after its list delimiter. Directories and symbolic links are no valid -o targets. @* Writing to stdout is possible only if -as "mkisofs" was among the start arguments or if other start arguments pointed the output drive to standard output. @* -print-size inhibits automatic image production at program end. This ban is lifted only if the pending image changes get discarded. @* Padding is counted as part of the ISO image if not option --emul-toc is given. @* If no -iso-level is given, then level 1 is chosen when the first file or directory is added to the image. At the same occasion directory names get allowed to violate the standard by -compliance option allow_dir_id_ext. This may be avoided by option -disallow_dir_id_ext. @* Option -root is supported. Option -old-root is implemented by @command{xorriso} commands -mkdir, -cp_clone, -find update_merge, and -find rm_merge. -root and -old-root set command -disk_dev_ino to "ino_only" and -md5 to "on", by default. @minus{}disk_dev_ino can be set to "off" by @minus{}@minus{}old-root-no-ino or to "on" by @minus{}@minus{}old-root-devno . @minus{}md5 can be set to "off" by @minus{}@minus{}old-root-no-md5 . @* Not original mkisofs options are @minus{}@minus{}quoted_path_list , @minus{}@minus{}hardlinks , @minus{}@minus{}acl , @minus{}@minus{}xattr , @minus{}@minus{}md5 , @minus{}@minus{}stdio_sync . They work like the @command{xorriso} commands with the same name and hardcoded parameter "on", e.g. -acl "on". Explicit parameters are expected by @minus{}@minus{}stdio_sync and @minus{}@minus{}scdbackup_tag. @* The capability to preserve multi-session history on overwritable media gets disabled by default. It can be enabled by using @minus{}@minus{}emul-toc with the first session. See -compliance no_emul_toc. @* @minus{}@minus{}sort-weight gets as parameters a number and an iso_rr_path. The number becomes the LBA sorting weight of regular file iso_rr_path or of all regular files underneath directory iso_rr_path. (See -find -exec sort_weight). @* Adopted from grub-mkisofs are @minus{}@minus{}protective-msdos-label (see -boot_image grub partition_table=on) and @minus{}@minus{}modification-date=YYYYMMDDhhmmsscc (see -volume_date uuid). For EFI bootable GRUB boot images use @minus{}@minus{}efi-boot. It performs @minus{}boot_image grub efi_path= surrounded by two @minus{}boot_image "any" "next". Alternative option @minus{}e from Fedora genisoimage sets bin_path and platform_id for EFI, but performs no "next". @* For MBR bootable ISOLINUX images there is -isohybrid-mbr FILE, where FILE is one of the Syslinux files mbr/isohdp[fp]x*.bin . Use this instead of -G to apply the effect of -boot_image isolinux partition_table=on. @* @minus{}@minus{}boot-catalog-hide is -boot_image any cat_hidden=on. @* @minus{}mips-boot is the same as -boot_image any mips_path= . @* @minus{}mipsel-boot leads to mipsel_path= . @* @minus{}partition_offset number is @minus{}boot_image any partition_offset=number. @* Command @minus{}append_partition is supported. @* @minus{}untranslated_name_len number is @minus{}compliance untranslated_name_len=number. @* @minus{}@minus{}old-empty is -compliance old_empty. @* The options of genisoimage Jigdo Template Extraction are recognized and performed via @command{xorriso} command -jigdo. See the "Alias:" names there for the meaning of the genisoimage options. @* @sp 1 Personalities "@strong{xorrisofs}", "@strong{genisoimage}", and "@strong{genisofs}" are aliases for "mkisofs". @* If @command{xorriso} is started with one of the leafnames "xorrisofs", "genisofs", "mkisofs", or "genisoimage", then it performs -read_mkisofsrc and prepends -as "genisofs" to the program arguments. I.e. all arguments will be interpreted mkisofs style until "@minus{}@minus{}" is encountered. From then on, arguments are interpreted as @command{xorriso} commands. @* @minus{}@minus{}no_rc as first argument of such a program start prevents interpretation of startup files. See section FILES below. @* @sp 1 @cindex Emulation, cdrecord, -as @cindex cdrecord, Emulation Personality "@strong{cdrecord}" accepts the options listed with: @* -as cdrecord -help @minus{}@minus{} @* Among them: -v, dev=, speed=, blank=, fs=, -eject, -atip, padsize=, tsize=, -isosize, -multi, -msinfo, @minus{}@minus{}grow_overwriteable_iso, write_start_address=, track source file path or "-" for standard input as track source. @* It ignores most other options of cdrecord and cdrskin but refuses on -audio, -scanbus, and on blanking modes unknown to @command{xorriso}. @* The scope is only a single data track per session to be written to blank, overwritable, or appendable media. The medium gets closed if closing is applicable and not option -multi is present. @* If an input drive was acquired, then it is given up. This is only allowed if no image changes are pending. @* dev= must be given as @command{xorriso} device address. Addresses like 0,0,0 or ATA:1,1,0 are not supported. @* If a track source is given, then an automatic -commit happens at the end of the "cdrecord" option list. @* @minus{}@minus{}grow_overwriteable_iso enables emulation of multi-session on overwritable media. To enable emulation of a TOC, the first session needs -C 0,32 with -as mkisofs (but no -M) and @minus{}@minus{}grow_overwriteable_iso write_start_address=32s with -as cdrecord. @* A much more elaborate libburn based cdrecord emulator is the program cdrskin. @* Personalites "@strong{xorrecord}", "@strong{wodim}", and "@strong{cdrskin}" are aliases for "cdrecord". @* If @command{xorriso} is started with one of the leafnames "xorrecord", "cdrskin", "cdrecord", or "wodim", then it automatically prepends -as "cdrskin" to the program arguments. I.e. all arguments will be interpreted cdrecord style until "@minus{}@minus{}" is encountered. From then on, arguments are interpreted as @command{xorriso} commands. @* @minus{}@minus{}no_rc as first argument of such a program start prevents interpretation of @command{xorriso} startup files. See section FILES below. @c man .TP @item -read_mkisofsrc @kindex -read_mkisofsrc searches and reads .mkisofsrc file @cindex Emulation, .mkisofsrc, -read_mkisofsrc Try one by one to open for reading: ./.mkisofsrc , $MKISOFSRC , $HOME/.mkisofsrc , $(dirname $0)/.mkisofsrc @* On success interpret the file content as of man mkisofs CONFIGURATION, and end this command. Do not try further files. The last address is used only if start argument 0 has a non-trivial dirname. @* The reader currently interprets the following NAME=VALUE pairs: APPI (-application_id) , PUBL (-publisher) , SYSI (-system_id) , VOLI (-volid) , VOLS (-volset_id) @* Any other lines will be silently ignored. @c man .TP @item -genisoimage_completion "on"|"off" @kindex -genisoimage_completion completion of genisoimage options @cindex Emulation, options completion, -genisoimage_completion Enable or disable the completion of genisoimage options during -as mkisofs emulation. @* If enabled by "on", then unrecognized option arguments which begin by a dash '-' get compared against the known genisoimage options, like program genisoimage does unconditionally (and undocumentedly). If the given argument matches the beginning of exactly one genisoimage option, then it gets replaced by that option. Option arguments which consist entirely of a leading dash and letters out of "dDfJlNRrTUvz" are not matched but rather interpreted as usual, i.e. as multiple options with leading dash and each single letter. If no genisoimage option is found or more than one are found, then a SORRY message is issued and the argument stays as is. @* If disabled by "off", no completion of options happens. Like with enabled completion, option arguments which consist entirely of letters out of "dDfJlNRrTUvz" are not matched but rather interpreted as multiple arguments with leading dash and each single letter. @c man .TP @item -pacifier behavior_code @kindex -pacifier controls pacifier text form @cindex Emulation, pacifier form, -pacifier Control behavior of UPDATE pacifiers during write operations. The following behavior codes are defined: @* "xorriso" is the default format: @* Writing: sector XXXXX of YYYYYY [fifo active, nn% fill] @* "cdrecord" looks like: @* X of Y MB written (fifo nn%) [buf mmm%] @* "mkisofs" @* nn% done, estimate finish Tue Jul 15 20:13:28 2008 @* The frequency of the messages can be adjusted by @* "interval=number" @* where number gives the seconds between two messages. Permissible settings are 0.1 to 60.0. @c man .TP @item -scdbackup_tag list_path record_name @kindex -scdbackup_tag enables scdbackup checksum tag @cindex Backup, scdbackup checksum tag, -scdbackup Set the parameter "name" for a scdbackup checksum record. It will be appended in an scdbackup checksum tag to the -md5 session tag if the image starts at LBA 0. This is the case if it gets written as first session onto a sequential medium, or piped into a program, named pipe or character device. @* If list_path is not empty then the record will also be appended to the data file given by this path. @* Program scdbackup_verify will recognize and verify tag and file record. @* An empty record_name disables this feature. @end table @c man .TP @c man .B Scripting, dialog and program control features: @node Scripting, Frontend, Emulation, Commands @section Scripting, dialog and program control features @table @asis @c man .TP @item -no_rc @kindex -no_rc disables startup files @cindex Process, disable startup files, -no_rc @* Only if used as first program argument this command prevents reading and interpretation of startup files. See section FILES below. @c man .TP @item -options_from_file fileaddress @kindex -options_from_file reads commands from file @cindex Process, read command file, -options_from_file Read quoted input from fileaddress and execute it like dialog lines. Empty lines and lines which begin by # are ignored. Normally one line should hold one @command{xorriso} command and all its parameters. Nevertheless lines may be concatenated by a trailing backslash. @* See also section "Command processing", paragraph "Quoted input". @c man .TP @item -help @kindex -help prints help text @cindex Program, print help text, -help @* Print helptext. @c man .TP @item -version @kindex -version prints help text @cindex Program, print version, -version Print program name and version, component versions, license. @c man .TP @item -list_extras code @kindex -list_extras lists compile time extra features @cindex Program, list extra features, -list_extras Tell whether certain extra features were enabled at compile time. Code "all" lists all features and a headline. Other codes pick a single feature. Code "codes" lists them. They share names with related commands (see also there): @* "acl" tells whether xorriso has an adapter for local filesystems ACLs. @* "xattr" tells whether xorriso has an adapter for local filesystems EA. @* "lfa_flags" tells whether xorriso has an adapter for local Linux file attributes (see man 1 chattr). @* "jigdo" tells whether production of Jigdo files is possible. @* "zisofs" tells whether zisofs and built-in gzip filters are enabled. @* "external_filter" tells whether external filter processes are allowed and whether they are allowed if real user id and effective user id differ. @* "dvd_obs" tells whether 64 kB output to DVD media is default. @* "use_readline" tells whether readline may be enabled in dialog mode. @* @c man .TP @item -history textline @kindex -history brings text into readline history @cindex Dialog, bring text into history, -history Copy textline into libreadline history. @c man .TP @item -status mode|filter @kindex -status shows current settings @cindex Program, show current settings, -status Print the current settings of @command{xorriso}. Modes: @* short... print only important or altered settings @* long ... print all settings including defaults @* long_history like long plus history lines @* Filters begin with '-' and are compared literally against the output lines of -status:long_history. A line is put out only if its start matches the filter text. No wildcards. @c man .TP @item -status_history_max number @kindex -status_history_max curbs -status history @cindex Program, status history, -status_history_max Set maximum number of history lines to be reported with -status "long_history". @c man .TP @item -list_delimiter word @kindex -list_delimiter replaces '@minus{}@minus{}' @cindex Program, replace @minus{}@minus{}, -list_delimiter Set the list delimiter to be used instead of "@minus{}@minus{}". It has to be a single word, must not be empty, not longer than 80 characters, and must not contain quotation marks. @* For brevity the list delimiter is referred as "@minus{}@minus{}" throughout this text. @c man .TP @item -sh_style_result "on"|"off" @kindex -sh_style_result makes results look more like shell @cindex Result layout, more shell-like, -sh_style_result Make the result output of some filesystem inspection commands look more like the output of equivalent shell commands. The most important effect is to prevent the wrapping of file addresses into quotation marks with commands -pwd -pwdx -ls -lsd -lsl -lsdl -lsx -lsdx -lslx -lsdlx -du -dus -dux -dusx -findx -find @* This will make ambiguous the representation of file names which contain newline characters. On the other hand it should facilitate integration of xorriso into shell scripts which already use the corresponding shell commands. @c man .TP @item -backslash_codes "on"|"off"|mode[:mode] @kindex -backslash_codes enables backslash conversion @cindex Program, backslash conversion, -backslash_codes Enable or disable the interpretation of symbolic representations of special characters with quoted input, or with program arguments, or with program text output. If enabled the following translations apply: @* \a=bell(007) \b=backspace(010) \e=Escape(033) \f=formfeed(014) @* \n=linefeed(012) \r=carriage_return(015) \t=tab(011) @* \v=vtab(013) \\=backslash(134) \[0-7][0-7][0-7]=octal_code @* \x[0-9a-f][0-9a-f]=hex_code \cC=control-C @* Translations can occur with quoted input in 3 modes: @* "in_double_quotes" translates only inside " quotation. @* "in_quotes" translates inside " and ' quotation. @* "with_quoted_input" translates inside and outside quotes. @* With the start program arguments there is mode: @* "with_program_arguments" translates program arguments. @* @* Mode "encode_output" encodes output characters. It combines "encode_results" with "encode_infos". Inside single or double quotation marks encoding applies to 8-bit characters octal 001 to 037 , 177 to 377 and to backslash(134). Outside quotation marks some harmless ASCII control characters stay unencoded: bell(007), backspace(010), tab(011), linefeed(012), formfeed(014), carriage_return(015). @* Mode "off" is default and disables any translation. Mode "on" is "with_quoted_input:with_program_arguments:encode_output". @c man .TP @item -temp_mem_limit number["k"|"m"] @kindex -temp_mem_limit curbs memory consumption @cindex Program, curb memory, -temp_mem_limit Set the maximum size of temporary memory to be used for image dependent buffering. Currently this applies to pattern expansion, LBA sorting, restoring of hard links. @* Default is 16m = 16 MiB, minimum 64k = 64 kiB, maximum 1024m = 1 GiB. @c man .TP @item -print text @kindex -print prints result text line @cindex Program, print result text line, -print Print a text line to the result channel which is by default stdout. @c man .TP @item -print_info text @kindex -print_info prints message text line @cindex Program, print message text line, -print_info Print a text line to the info channel which is by default stderr. @c man .TP @item -print_mark text @kindex -print_mark prints synchronizing text line @cindex Program, print synchronizing text line, -print_mark Print a text line to the mark channel which is by default directed to both, result and info channel. An empty text will cause no output at all. @c man .TP @item -prompt text @kindex -prompt prompts for enter key @cindex Program, prompt for enter key, -prompt Show text at beginning of output line and wait for the user to hit the Enter key or to send a line via stdin. @c man .TP @item -sleep seconds @kindex -sleep waits for a given time span @cindex Program, wait a time span, -sleep Wait for the given number of seconds before performing the next command. Expect coarse granularity no better than 1/100 seconds. @c man .TP @item -errfile_log mode path|channel @kindex -errfile_log logs problematic disk files @cindex Write, log problematic disk files, -errfile_log @* If problem events are related to input files from the filesystem, then their disk_paths can be logged to a file or to output channels R or I. @* Mode can either be "plain" or "marked". The latter causes marker lines which give the time of log start, burn session start, burn session end, log end or program end. In mode "plain", only the file paths are logged. @* If path is "-" or "-R" then the log is directed to the result channel. Path "-I" directs it to the info message channel. Any text that does not begin with "-" is used as path for a file to append the log lines. @* Problematic files can be recorded multiple times during one program run. If the program run aborts then the list might not be complete because some input files might not have been processed at all. @* The errfile paths are transported as messages of very low severity "ERRFILE". This transport becomes visible with -report_about "ALL". @c man .TP @item -session_log path @kindex -session_log logs written sessions @cindex Write, log written sessions, -session_log @cindex Session, log when written, -session_log If path is not empty it gives the address of a plain text file where a log record gets appended after each session. This log can be used to determine the start_lba of a session for mount options -o sbsector= (on GNU/Linux) or -s (on FreeBSD) from date or volume ID. @* Record format is: timestamp start_lba size volume-id @* The first three items are single words, the rest of the line is the volume ID. @c man .TP @item -scsi_log "on"|"off" @kindex -scsi_log reports SCSI commands @cindex Drive, report SCSI commands, -scsi_log Mode "on" enables very verbose logging of SCSI commands and drive replies. Logging messages get printed to stderr, not to any of the @command{xorriso} output channels. @* A special property of this command is that the first -scsi_log setting among the start arguments is in effect already when the first operations of @command{xorriso} begin. Only "-scsi_log" with dash "-" is recognized that way. @c man .TP @item -end @kindex -end writes pending session and ends program @cindex Process, end program and write, -end @cindex Program, end and write, -end @* End program after writing pending changes. @c man .TP @item -rollback_end @kindex -rollback_end ends program without writing @cindex Program, end without writing, -rollback_end @cindex Process, end program, no writing, -rollback_end Discard pending changes. End program immediately. @c man .TP @item # any text @kindex # starts a comment line @cindex Comment, # Only in dialog or file execution mode, and only as first non-whitespace in line: Do not execute the line but store it in readline history. @end table @c man .TP @c man .B Support for frontend programs via stdin and stdout: @node Frontend, ExDevices, Scripting, Commands @section Support for frontend programs via stdin and stdout @table @asis @c man .TP @item -pkt_output "on"|"off" @kindex -pkt_output consolidates text output @cindex Process, consolidate text output, -pkt_output Consolidate text output on stdout and classify each line by a channel indicator: @* 'R:' for result lines, @* 'I:' for notes and error messages, @* 'M:' for -mark texts. @* Next is a decimal number of which only bit 0 has a meaning for now. 0 means no newline at end of payload, 1 means that the newline character at the end of the output line belongs to the payload. After another colon and a blank follows the payload text. @* Example: @* I:1: enter option and parameters : @c man .TP @item -logfile channel fileaddress @kindex -logfile logs output channels to file @cindex Process, log output channels to file, -logfile Copy output of a channel to the given file. Channel may be one of: "." for all channels, "I" for info messages, "R" for result lines, "M" for -mark texts. @c man .TP @item -mark text @kindex -mark sets synchronizing message @cindex Process, set synchronizing message, -mark If text is not empty it will get put out on "M" channel each time @command{xorriso} is ready for the next dialog line or before @command{xorriso} performs a command that was entered to the pager prompt. @c man .TP @item -msg_op opcode parameter_text @kindex -msg_op perform operations on program messages @cindex Program messages, perform operations, -msg_op This command shall facilitate extraction of particular information from the message output of other commands. It gives access to the C API function Xorriso_parse_line() and to the message sieve that is provided by the C API. Please refer to their descriptions in file xorriso.h. Further it helps to interpret the severity codes of info messages. @* Intended users are frontend programs which operate xorriso in dialog mode. @* The result output of this command is not caught by the message sieve. @* The following opcodes are defined: @* @strong{start_sieve} @* Install the message sieve as of Xorriso_sieve_big() and start watching program messages. The parameter_text has no meaning. @* @strong{show_sieve} @* Show a list of filter rule names. The parameter_text has no meaning. The list begins by a line with the return value of Xorriso_sieve_get_result() with flag bit3. If this value is larger than 0, then the next line tells the number of names. The following lines show one name each. @* @strong{read_sieve} @* Use the parameter_text as name of a filter rule and inquire its next recorded result. See Xorriso_sieve_big() for a list of names and reply strings. @* The recorded strings are put out on result channel. They get wrapped into lines which tell their structure. The first line tells the return value of Xorriso_sieve_get_result(). The next line tells the number of strings. Each string begins by a line that tells the number of lines of the string. Then follow these lines. They are to be concatenated with a newline character between each of them. Finally the number of still available recorded results of the given name is put out. @* @strong{clear_sieve} @* Dispose all recorded strings and continue watching program messages. The parameter_text has no meaning. @* @strong{end_sieve} @* Dispose the sieve with its filter rules and stop watching program messages. The parameter_text has no meaning. @* @strong{parse} @* Read a text from dialog input and submit it to Xorriso_parse_line(). The parameter_text word shall consist of several words separated by blanks. It will be necessary to use both kinds of quotation marks. @* E.g. "'ISO session :' '' 0 0 1" @* The five parameter words are: prefix, separators, max_words, flag, number_of_input_lines. The former four are handed over to Xorriso_parse_line(). The number of input lines minus one tells xorriso how many newline characters are part of the input text. @* The announced number of text lines will be read from dialog input, concatenated with a newline character between each of them, and submitted to Xorriso_parse_line() as parameter line. Note that newlines outside of quotation marks are interpreted as separators if the separators parameter is empty. @* The parsed strings are put out on result channel. They get wrapped into lines which tell their structure. The first line tells the return value of Xorriso_parse_line(). The next line tells the number of strings. Each string begins by a line that tells the number of lines of the string. Then follow these lines. They are to be concatenated with a newline character between each of them. @* If -backslash_codes "encode_output" is enabled, then the strings undergo encoding as if they were enclosed in quotes. Escpecially each string will be put out as a single result line. @* @strong{parse_bulk} @* Like "parse", but with the fifth parameter word being number_of_input_texts rather than number_of_input_lines. Each input text has to be preceded by a line that tells number_of_input_lines as with "parse". Then come the announced number of text lines. @* All input texts will be read before printing of result lines begins. This consumes memory in xorriso. So the number_of_input_texts should not be extremely high. On the other hand, large transactions of command, input texts, and results are desirable if connection latency is an issue. @* @strong{parse_silently} @* Like "parse" but not issuing a prompting message. Confusing to humans. @* @strong{parse_bulk_silently} @* Like "parse_bulk" but not issuing a prompting message. Confusing to humans. @* @strong{compare_sev} @* The parameter_text should contain two comma separated severity texts as issued by this program. Like "SORRY,UPDATE". See also paragraph "Exception processing". @* These two severity texts get compared and a number gets printed to the result channel. This number is 0 if both severities are equal. It is -1 if the first severity is lower than the second one. It is 1 is the first severity is higher than the second one. @* Above example "SORRY,UPDATE" will yield 1. @* @strong{list_sev} @* Print to the result channel a blank separated list of all severity names. Sorted from low to high severity. @c man .TP @item -named_pipe_loop mode[:mode] disk_path_stdin disk_path_stdout disk_path_stderr @kindex -named_pipe_loop enters EOF resistant dialog @cindex Dialog, EOF resistant, -named_pipe_loop Temporarily replace standard input, standard output and standard error by named pipes. Enter dialog mode without readline. @* Defined modes are: @* "cleanup" removes the submitted pipe files when the loop ends. @* "keep" does not delete them. This is the default. @* "buffered" reads all lines from the input pipe until EOF before it opens the output pipes and processes the input lines. @* "direct" opens the output pipes after the first input line was read. Each line is executed directly after it is read. This is the default. @* The other three parameters must either be disk paths to existing named pipes, or be "-" to leave the according standard i/o channel unreplaced. @* xorriso will open the stdin pipe, read and execute dialog lines from it until the sender closes the pipe. The output pipes get opened depending on mode "buffered" or "direct". After all lines are executed, xorriso will close its side of the pipes and enter a new cycle of opening, reading and executing. @* If an input line consists only of the word "end_named_pipe_loop" then -named_pipe_loop will end and further xorriso commands may be executed from other sources. @c man .TP @item -launch_frontend program [arguments ...] @minus{}@minus{} @kindex -launch_frontend starts frontend program at pipes @cindex Frontend program, start at pipes, -launch_frontend Start the program that is given as first parameter. Submit the other parameters as program arguments. Enable xorriso dialog mode. @* Two nameless pipe objects are created. xorriso standard input gets connected to the standard output of the started program. xorriso standard output and standard error get connected to the standard input of that program. @* xorriso will abort when the started program ends or if it cannot be started at all. In both cases it will return a non-zero exit value. The exit value will be zero if the frontend sends -end or -rollback_end before ending itself. @* This command may be totaly banned at compile time. It is banned by default if xorriso runs under setuid permissions. @* The program name will not be searched in the $PATH directories. To make this clear, it must contain at least one /-character. Best is an absolute path. @* Example: @* xorriso -launch_frontend "$(which xorriso-tcltk)" -stdio -- @* The frontend program should first send via its standard output: @* -mark 0 -pkt_output on -msg_op start_sieve - -reassure off @* It should be ready to decode -pkt_output and to react on -mark messages. Best is to increment the -mark number after each sent command sequence and then to wait for the new number to show up in a mark message: @* ...some...commands... -mark <incremented_number> @* Further are advised: @* -report_about UPDATE -abort_on NEVER @* -iso_rr_pattern off -disk_pattern off @* A check of the xorriso version should be done, in order to make sure that all desired features are present. @* Command -launch_frontend will only work once per xorriso run. If no command parameters are submitted or if program is an empty text, then no program will be started but nevertheless -launch_frontend will be irrevocably disabled. @c man .TP @item -prog text @kindex -prog sets program name @cindex Program, set name, -prog Use text as name of this program in subsequent messages @c man .TP @item -prog_help text @kindex -prog_help prints help text @cindex Program, print help text, -prog_help Use text as name of this program and perform -help. @end table @c man .br @node Examples, Files, Commands, Top @chapter Examples @c man .SH EXAMPLES @c man .SS @c man .B Overview of examples: @c man As superuser learn about available drives @c man .br @c man Blank medium and compose a new ISO image as batch run @c man .br @c man A dialog session doing about the same @c man .br @c man Manipulate an existing ISO image on the same medium @c man .br @c man Copy modified ISO image from one medium to another @c man .br @c man Bring a prepared ISOLINUX tree onto medium and make it bootable @c man .br @c man Change existing file name tree from ISO-8859-1 to UTF-8 @c man .br @c man Operate on storage facilities other than optical drives @c man .br @c man Burn an existing ISO image file to medium @c man .br @c man Perform multi-session runs as of cdrtools traditions @c man .br @c man Let xorriso work underneath growisofs @c man .br @c man Adjust thresholds for verbosity, exit value and program abort @c man .br @c man Examples of input timestrings @c man .br @c man Incremental backup of a few directory trees @c man .br @c man Restore directory trees from a particular ISO session to disk @c man .br @c man Try to retrieve blocks from a damaged medium @cindex Examples @menu * ExDevices:: As superuser learn about available drives * ExCreate:: Blank medium and compose a new ISO image as batch run * ExDialog:: A dialog session doing about the same * ExGrowing:: Manipulate an existing ISO image on the same medium * ExModifying:: Copy modified ISO image from one medium to another * ExBootable:: Bring a prepared ISOLINUX tree onto medium and make it bootable * ExCharset:: Change existing file name tree from ISO-8859-1 to UTF-8 * ExPseudo:: Operate on storage facilities other than optical drives * ExCdrecord:: Burn an existing ISO image file to medium * ExMkisofs:: Perform multi-session runs as of cdrtools traditions * ExGrowisofs:: Let @command{xorriso} work underneath growisofs * ExException:: Adjust thresholds for verbosity, exit value and program abort * ExTime:: Examples of input timestrings * ExIncBackup:: Incremental backup of a few directory trees * ExRestore:: Restore directory trees from a particular ISO session to disk * ExRecovery:: Try to retrieve blocks from a damaged medium @end menu @c man .SS @c man .B As superuser learn about available drives @node ExDevices, ExCreate, Frontend, Examples @section As superuser learn about available drives On Linux, FreeBSD or NetBSD consider to give rw-permissions to those users or groups which shall be able to use the drives with @command{xorriso}. On Solaris use pfexec. Consider to restrict privileges of @command{xorriso} to "base,sys_devices" and to give r-permission to user or group. @* @sp 1 $ xorriso -device_links @* 1 -dev '/dev/cdrom1' rwrw@minus{}@minus{} : 'TSSTcorp' 'DVD-ROM SH-D162C @* 1 -dev '/dev/cdrw' rwrw@minus{}@minus{} : 'TSSTcorp' 'CDDVDW SH-S223B' @* 2 -dev '/dev/cdrw3' rwrw@minus{}@minus{} : 'HL-DT-ST' 'BDDVDRW_GGC-H20L' @c man .SS @c man .B Blank medium and compose a new ISO image as batch run @node ExCreate, ExDialog, ExDevices, Examples @section Blank medium and compose a new ISO image as batch run Acquire drive /dev/sr2, make medium ready for writing a new image, fill the image with the files from hard disk directories /home/me/sounds and /home/me/pictures. @* Because no -dialog "on" is given, the program will then end by writing the session to the medium. @* @sp 1 $ xorriso -outdev /dev/sr2 \ @* -blank as_needed \ @* -map /home/me/sounds /sounds \ @* -map /home/me/pictures /pictures @* @sp 1 @* The ISO image may be shaped in a more elaborate way like the following: Omit some unwanted stuff by removing it from the image directory tree. Reintroduce some wanted stuff. @* @sp 1 $ cd /home/me @* $ xorriso -outdev /dev/sr2 \ @* -blank as_needed \ @* -map /home/me/sounds /sounds \ @* -map /home/me/pictures /pictures \ @* -rm_r \ @* /sounds/indecent \ @* '/pictures/*private*' \ @* /pictures/confidential \ @* @minus{}@minus{} \ @* -cd / \ @* -add pictures/confidential/work* @minus{}@minus{} @* @sp 1 Note that '/pictures/*private*' is a pattern for iso_rr_paths while pictures/confidential/work* gets expanded by the shell with addresses from the hard disk. Commands -add and -map have different parameter rules but finally the same effect: they put files into the image. @c man .SS @c man .B A dialog session doing about the same @c man .br @node ExDialog, ExGrowing, ExCreate, Examples @section A dialog session doing about the same as the previous example Some settings are already given as start argument. The other activities are done as dialog input. The pager gets set to 20 lines of 80 characters. @* The drive is acquired by command -dev rather than -outdev in order to see the message about its current content. By command -blank this content is made ready for being overwritten and the loaded ISO image is made empty. @* In order to be able to eject the medium, the session needs to be committed explicitly. @* @c man .B $ xorriso -dialog on -page 20 80 -disk_pattern on @c man .br @c man enter option and arguments : @c man .br @c man .B \-dev /dev/sr2 @c man .br @c man enter option and arguments : @c man .br @c man .B \-blank as_needed @c man .br @c man enter option and arguments : @c man .br @c man .B \-map /home/me/sounds /sounds -map /home/me/pictures /pictures @c man .br @c man enter option and arguments : @c man .br @c man .B \-rm_r /sounds/indecent /pictures/*private* /pictures/confidential @c man .br @c man enter option and arguments : @c man .br @c man .B \-cdx /home/me/pictures -cd /pictures @c man .br @c man enter option and arguments : @c man .br @c man .B \-add confidential/office confidential/factory @c man .br @c man enter option and arguments : @c man .br @c man .B \-du / @c man .br @c man enter option and arguments : @c man .br @c man .B \-commit_eject all -end @c man .br @sp 1 @c man-ignore-lines begin $ xorriso -dialog on -page 20 80 -disk_pattern on @sp 1 enter option and arguments : @* -dev /dev/sr2 @sp 1 enter option and arguments : @* -blank as_needed @sp 1 enter option and arguments : @* -map /home/me/sounds /sounds -map /home/me/pictures /pictures @sp 1 enter option and arguments : @* -rm_r /sounds/indecent /pictures/*private* /pictures/confidential @sp 1 enter option and arguments : @* -cdx /home/me/pictures -cd /pictures @sp 1 enter option and arguments : @* -add confidential/office confidential/factory @sp 1 enter option and arguments : @* -du / @sp 1 enter option and arguments : @* -commit_eject all -end @c man-ignore-lines end @c man .SS @c man .B Manipulate an existing ISO image on the same medium @node ExGrowing, ExModifying, ExDialog, Examples @section Manipulate an existing ISO image on the same medium Load image from drive. Remove (i.e. hide) directory /sounds and its subordinates. Rename directory /pictures/confidential to /pictures/restricted. Change access permissions of directory /pictures/restricted. Add new directory trees /sounds and /movies. Burn to the same medium, check whether the tree can be loaded, and eject. @* @sp 1 $ xorriso -dev /dev/sr2 \ @* -rm_r /sounds @minus{}@minus{} \ @* -mv \ @* /pictures/confidential \ @* /pictures/restricted \ @* @minus{}@minus{} \ @* -chmod go-rwx /pictures/restricted @minus{}@minus{} \ @* -map /home/me/prepared_for_dvd/sounds_dummy /sounds \ @* -map /home/me/prepared_for_dvd/movies /movies \ @* -commit -eject all @c man .SS @c man .B Copy modified ISO image from one medium to another @node ExModifying, ExBootable, ExGrowing, Examples @section Copy modified ISO image from one medium to another Load image from input drive. Do the same manipulations as in the previous example. Acquire output drive and blank it. Burn the modified image as first and only session to the output drive. @* @sp 1 $ xorriso -indev /dev/sr2 \ @* -rm_r /sounds @minus{}@minus{} \ @* ... @* -outdev /dev/sr0 -blank as_needed \ @* -commit -eject all @c man .SS @c man .B Bring a prepared ISOLINUX tree onto medium and make it bootable @node ExBootable, ExCharset, ExModifying, Examples @section Bring a prepared ISOLINUX tree onto medium and make it bootable The user has already created a suitable file tree on disk and copied the ISOLINUX files into subdirectory ./boot/isolinux of that tree. Now @command{xorriso} can burn an El Torito bootable medium: @* @sp 1 $ xorriso -outdev /dev/sr0 -blank as_needed \ @* -map /home/me/ISOLINUX_prepared_tree / \ @* -boot_image isolinux dir=/boot/isolinux @c man .SS @c man .B Change existing file name tree from ISO-8859-1 to UTF-8 @node ExCharset, ExPseudo, ExBootable, Examples @section Change existing file name tree from ISO-8859-1 to UTF-8 This example assumes that the existing ISO image was written with character set ISO-8859-1 but that the readers expected UTF-8. Now a new session gets added with converted file names. Command -changes_pending "yes" enables writing despite the lack of any manipulation command. @* In order to avoid any weaknesses of the local character set, this command pretends that it uses already the final target set UTF-8. Therefore strange file names may appear in messages, which will be made terminal-safe by command -backslash_codes. @* @sp 1 $ xorriso -in_charset ISO-8859-1 -local_charset UTF-8 \ @* -out_charset UTF-8 -backslash_codes on -dev /dev/sr0 \ @* -changes_pending yes -commit -eject all @c man .SS @c man .B Operate on storage facilities other than optical drives @node ExPseudo, ExCdrecord, ExCharset, Examples @section Operate on storage facilities other than optical drives Full read-write operation is possible with regular files and block devices: @* @sp 1 $ xorriso -dev /tmp/regular_file ... @* @sp 1 Paths underneath /dev normally need prefix "stdio:" @* @sp 1 $ xorriso -dev stdio:/dev/sdb ... @* @sp 1 If /dev/sdb is to be used frequently and /dev/sda is the system disk, then consider to place the following lines in a @command{xorriso} Startup File. They allow you to use /dev/sdb without prefix and protect disk /dev/sda from @command{xorriso}: @* @sp 1 -drive_class banned /dev/sda* @* -drive_class harmless /dev/sdb @* @sp 1 Other writeable file types are supported write-only: @* @sp 1 $ xorriso -outdev /tmp/named_pipe ... @* @sp 1 Among the write-only drives is standard output: @* @sp 1 $ xorriso -outdev - \ @* ... @* | gzip >image.iso.gz @c man .SS @c man .B Burn an existing ISO image file to medium @node ExCdrecord, ExMkisofs, ExPseudo, Examples @section Burn an existing ISO image file to medium Actually this works with any kind of data, not only ISO images: @* @sp 1 $ xorriso -as cdrecord -v dev=/dev/sr0 blank=as_needed image.iso @c man .SS @c man .B Perform multi-session runs as of cdrtools traditions @node ExMkisofs, ExGrowisofs, ExCdrecord, Examples @section Perform multi-session runs as of cdrtools traditions Between both processes there can be performed arbitrary transportation or filtering. @* The first session is written like this: @* @sp 1 $ xorriso -as mkisofs prepared_for_iso/tree1 | \ @* xorriso -as cdrecord -v dev=/dev/sr0 blank=fast -multi -eject - @* @sp 1 Follow-up sessions are written like this (the run of dd is only to give demons a chance to spoil it): @* @sp 1 $ m=$(xorriso -as cdrecord dev=/dev/sr0 -msinfo) @* $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 @* $ xorriso -as mkisofs -M /dev/sr0 -C $m prepared_for_iso/tree2 | \ @* xorriso -as cdrecord -v dev=/dev/sr0 -waiti -multi -eject - @* @sp 1 Always eject the drive tray between sessions. @* The run of xorriso -as mkisofs will read old sessions via the CD-ROM driver of /dev/sr0. This driver might not be aware of the changed content as long as the medium is not loaded again. In this case the previous session would not be properly assessed by xorriso and the new session would contain only the newly added files. @* Some systems have not enough patience with automatic tray loading and some demons may interfere with a first CD-ROM driver read attempt from a freshly loaded medium. @* When loading the tray manually, wait 10 seconds after the drive has stopped blinking. @* A safe automatic way seems to be a separate run of xorriso for loading the tray with proper waiting, and a subsequent run of dd which shall offer itself to any problems caused by demons assessing the changed drive status. If this does not help, insert a run of "sleep 10" between xorriso and dd. @* @sp 1 This example works for multi-session media only. Add cdrskin option @minus{}@minus{}grow_overwriteable_iso to all -as cdrecord runs in order to enable multi-session emulation on overwritable media. @c man .SS @c man .B Let xorriso work underneath growisofs @node ExGrowisofs, ExException, ExMkisofs, Examples @section Let @command{xorriso} work underneath growisofs growisofs expects an ISO formatter program which understands options -C and -M. If @command{xorriso} gets started by name "xorrisofs" then it is suitable for that. @* @sp 1 $ export MKISOFS="xorrisofs" @* $ growisofs -Z /dev/dvd /some/files @* $ growisofs -M /dev/dvd /more/files @* @sp 1 If no "xorrisofs" is available on your system, then you will have to create a link pointing to the @command{xorriso} binary and tell growisofs to use it. E.g. by: @* @sp 1 $ ln -s $(which xorriso) "$HOME/xorrisofs" @* $ export MKISOFS="$HOME/xorrisofs" @* @sp 1 One may quit mkisofs emulation by argument "@minus{}@minus{}" and make use of all @command{xorriso} commands. growisofs dislikes options which start with "-o" but -outdev must be set to "-". So use "outdev" instead: @* @sp 1 $ growisofs -Z /dev/dvd @minus{}@minus{} outdev - -update_r /my/files /files @* $ growisofs -M /dev/dvd @minus{}@minus{} outdev - -update_r /my/files /files @* @sp 1 growisofs has excellent burn capabilities with DVD and BD. It does not emulate session history on overwritable media, though. @c man .SS @c man .B Adjust thresholds for verbosity, exit value and program abort @node ExException, ExTime, ExGrowisofs, Examples @section Adjust thresholds for verbosity, exit value and program abort Be quite verbose, exit 32 if severity "FAILURE" was encountered, do not abort prematurely but forcibly go on until the end of commands. @* @sp 1 $ xorriso ... \ @* -report_about UPDATE \ @* -return_with FAILURE 32 \ @* -abort_on NEVER \ @* ... @c man .SS @c man .B Examples of input timestrings @node ExTime, ExIncBackup, ExException, Examples @section Examples of input timestrings @c man .br @c man As printed by program date: @c man .B 'Thu Nov 8 14:51:13 CET 2007' @c man .br @c man The same without ignored parts: @c man .B 'Nov 8 14:51:13 2007' @c man .br @c man The same as expected by date: @c man .B 110814512007.13 @c man .br @c man Four weeks in the future: @c man .B +4w @c man .br @c man The current time: @c man .B +0 @c man .br @c man Three hours ago: @c man .B \-3h @c man .br @c man Seconds since Jan 1 1970: @c man .B =1194531416 @c man-ignore-lines begin As printed by program date: @* 'Thu Nov 8 14:51:13 CET 2007' @sp 1 The same without ignored parts: @* 'Nov 8 14:51:13 2007' @sp 1 The same as expected by date: @* 110814512007.13 @sp 1 Four weeks in the future: @* +4w @sp 1 The current time: @* +0 @sp 1 Three hours ago: @* -3h @sp 1 Seconds since Jan 1 1970: @* =1194531416 @c man-ignore-lines end @c man .SS @c man .B Incremental backup of a few directory trees @node ExIncBackup, ExRestore, ExTime, Examples @section Incremental backup of a few directory trees This changes the directory trees /projects and /personal_mail in the ISO image so that they become exact copies of their disk counterparts. ISO file objects get created, deleted or get their attributes adjusted accordingly. @* ACL, xattr, hard links and MD5 checksums will be recorded. Accelerated comparison is enabled at the expense of potentially larger backup size. Only media with the expected volume ID or blank media are accepted. Files with names matching *.o or *.swp get excluded explicitly. @* When done with writing the new session gets checked by its recorded MD5. @* @sp 1 $ xorriso \ @* -abort_on FATAL \ @* -for_backup -disk_dev_ino on \ @* -assert_volid 'PROJECTS_MAIL_*' FATAL \ @* -dev /dev/sr0 \ @* -volid PROJECTS_MAIL_"$(date '+%Y_%m_%d_%H%M%S')" \ @* -not_leaf '*.o' -not_leaf '*.swp' \ @* -update_r /home/thomas/projects /projects \ @* -update_r /home/thomas/personal_mail /personal_mail \ @* -commit -toc -check_md5 FAILURE @minus{}@minus{} -eject all @* @sp 1 To be used several times on the same medium, whenever an update of the two disk trees to the medium is desired. Begin with a blank medium and update it until the run fails gracefully due to lack of remaining space on the old one. @* This makes sense if the full backup leaves substantial remaining capacity on media and if the expected changes are much smaller than the full backup. To apply zisofs compression to those data files which get newly copied from the local filesystem, insert these commands immediately before -commit : @* @sp 1 -hardlinks perform_update \ @* -find / -type f -pending_data -exec set_filter @minus{}@minus{}zisofs @minus{}@minus{} \ @* @sp 1 Commands -disk_dev_ino and -for_backup depend on stable device and inode numbers on disk. Without them, an update run may use -md5 "on" to match recorded MD5 sums against the current file content on hard disk. This is usually much faster than the default which compares both contents directly. @* With @strong{mount} option @strong{-o "sbsector="} on GNU/Linux or @strong{-s} on FreeBSD or NetBSD it is possible to access the session trees which represent the older backup versions. With CD media, GNU/Linux mount accepts session numbers directly by its option "session=". @* Multi-session media and most overwritable media written by @command{xorriso} can tell the sbsectors of their sessions by @command{xorriso} command -toc. Used after -commit the following command prints the matching mount command for the newly written session (here for mount point /mnt): @* @sp 1 -mount_cmd "indev" "auto" "auto" /mnt @* @sp 1 Commands -mount_cmd and -mount are also able to produce the mount commands for older sessions in the table-of-content. E.g. as superuser: @* @sp 1 # osirrox -mount /dev/sr0 "volid" '*2008_12_05*' /mnt @* @sp 1 @c man .sp 1 Above example produces a result similar to -root / -old-root / with mkisofs. For getting the session trees accumulated in the new sessions, let all -update commands use a common parent directory and clone it after updating is done: @* -update_r /home/thomas/projects /current/projects \ @* -update_r /home/thomas/personal_mail /current/personal_mail \ @* -clone /current /"$(date '+%Y_%m_%d_%H%M%S')" \ @* The cloned tree will have a name like /2011_02_12_155700. @* @sp 1 @c man .sp 1 Sessions on multi-session media are separated by several MB of unused blocks. So with small sessions the payload capacity can become substantially lower than the overall media capacity. If the remaining space on a medium does not suffice for the next gap, the drive is supposed to close the medium automatically. @* @sp 1 @c man .sp 1 @strong{Better do not use your youngest backup for -update_r}. Have at least two media which you use alternatingly. So only older backups get endangered by the new write operation, while the newest backup is stored safely on a different medium. @* Always have a blank medium ready to perform a full backup in case the update attempt fails due to insufficient remaining capacity. This failure will not spoil the old medium, of course. @c man .SS @c man .B Restore directory trees from a particular ISO session to disk @node ExRestore, ExRecovery, ExIncBackup, Examples @section Restore directory trees from a particular ISO session to disk This is an alternative to mounting the medium and using normal file operations. @* First check which backup sessions are on the medium: @* @sp 1 $ xorriso -outdev /dev/sr0 -toc @* @sp 1 Then enable restoring of ACL, xattr and hard links. Load the desired session and copy the file trees to disk. Avoid to create /home/thomas/restored without rwx-permission. @* @sp 1 $ xorriso -for_backup \ @* -load volid 'PROJECTS_MAIL_2008_06_19*' \ @* -indev /dev/sr0 \ @* -osirrox on:auto_chmod_on \ @* -chmod u+rwx / @minus{}@minus{} \ @* -extract /projects /home/thomas/restored/projects \ @* -extract /personal_mail /home/thomas/restored/personal_mail \ @* -rollback_end @* @sp 1 The final command -rollback_end prevents an error message about the altered image being discarded. @c man .SS @c man .B Try to retrieve blocks from a damaged medium @node ExRecovery,, ExRestore, Examples @section Try to retrieve blocks from a damaged medium @* @sp 1 $ xorriso -abort_on NEVER -indev /dev/sr0 \ @* -check_media time_limit=1800 report=blocks_files \ @* data_to="$HOME"/dvd_copy sector_map="$HOME"/dvd_copy.map @minus{}@minus{} @* @sp 1 This can be repeated several times, if necessary with -eject or with other -indev drives. See the human readable part of "$HOME"/dvd_copy.map for addresses which can be used on "$HOME"/dvd_copy with mount option -o sbsector= or -s. @c man .SH FILES @node Files, Environ, Examples, Top @chapter Files @c man .SS @c man .B Program alias names: @* @section Program Alias Names Normal installation of @command{xorriso} creates three links or copies which by their program name pre-select certain settings: @* @sp 1 @strong{xorrisofs} starts @command{xorriso} with -as mkisofs emulation. @* @strong{xorrecord} starts @command{xorriso} with -as cdrecord emulation. @* @strong{osirrox} starts with -osirrox "on:o_excl_off" which allows further commands to copy files from ISO image to disk and to apply command -mount to one or more of the existing ISO sessions. @c man .SS @c man .B Startup files: @section Startup Files @* If not -no_rc is given as the first argument then @command{xorriso} attempts on startup to read and execute lines from the following files: @* @sp 1 /etc/default/xorriso @* /etc/opt/xorriso/rc @* /etc/xorriso/xorriso.conf @* $HOME/.xorrisorc @* @sp 1 The files are read in the sequence given above, but none of them is required to exist. The line format is described with command -options_from_file. @* If mkisofs emulation was enabled by program name "xorrisofs", "mkisofs", "genisoimage", or "genisofs", then afterwards -read_mkisofsrc is performed, which reads .mkisofsrc files. See there. @c man .SS @c man .B Runtime control files: @section Runtime control files @* The default setting of -check_media abort_file= is: @* @sp 1 /var/opt/xorriso/do_abort_check_media @* @sp 1 @c man .SS @c man .SH ENVIRONMENT @node Environ, Seealso, Files, Top @chapter Environ The following environment variables influence the program behavior: @* HOME is used to find startup files of xorriso and mkisofs. @* SOURCE_DATE_EPOCH belongs to the specs of reproducible-builds.org. It is supposed to be either undefined or to contain a decimal number which tells the seconds since january 1st 1970. If it contains a number, then it is used as time value to set the default of -volume date "uuid", sets -boot_image "any" "gpt_disk_guid=" to "volume_date_uuid", -volume_date "all_file_dates" to "set_to_mtime", and -iso_nowtime to "=$SOURCE_DATE_EPOCH". @* Startup files and program options can override the effect of SOURCE_DATE_EPOCH. @* @sp 1 @c man .SS @c man .SH SEE ALSO @c man .TP @c man For the mkisofs emulation of xorriso @c man .BR xorrisofs(1) @c man .TP @c man For the cdrecord emulation of xorriso @c man .BR xorrecord(1) @c man .TP @c man For mounting xorriso generated ISO 9660 images (-t iso9660) @c man .BR mount(8) @c man .TP @c man Libreadline, a comfortable input line facility @c man .BR readline(3) @c man .TP @c man Other programs which produce ISO 9660 images @c man .BR mkisofs(8), @c man .BR genisoimage(1) @c man .TP @c man Other programs which burn sessions to optical media @c man .BR growisofs(1), @c man .BR cdrecord(1), @c man .BR wodim(1), @c man .BR cdrskin(1) @c man .TP @c man ACL, xattr, Linux file attributes @c man .BR getfacl(1), @c man .BR setfacl(1), @c man .BR getfattr(1), @c man .BR setfattr(1), @c man .BR lsattr(1), @c man .BR chattr(1) @c man .TP @c man MD5 checksums @c man .BR md5sum(1) @c man .TP @c man On FreeBSD the commands for xattr and MD5 differ @c man .BR getextattr(8), @c man .BR setextattr(8), @c man .BR md5(1) @c man-ignore-lines begin @node Seealso, Bugreport, Environ, Top @chapter See also @table @asis @item For the mkisofs emulation of @command{xorriso} xorrisofs(1) @item For the cdrecord emulation of @command{xorriso} xorrecord(1) @item For mounting @command{xorriso} generated ISO 9660 images (-t iso9660) mount(8) @item Libreadline, a comfortable input line facility readline(3) @item Other programs which produce ISO 9660 images mkisofs(8), genisoimage(1) @item Other programs which burn sessions to optical media growisofs(1), cdrecord(1), wodim(1), cdrskin(1) @item ACL, xattr, Linux file attributes getfacl(1), setfacl(1), getfattr(1), setfattr(1), lsattr(1), chattr(1) @item MD5 checksums md5sum(1) @item On FreeBSD some commands differ: getextattr(8), setextattr(8), md5(1) @end table @c man-ignore-lines end @c man .SH BUGS @node Bugreport, Legal, Seealso, Top @chapter Reporting bugs @cindex Bugs, reporting @cindex Problems, reporting To report bugs, request help, or suggest enhancements for @command{xorriso}, please send electronic mail to the public list @email{bug-xorriso@@gnu.org}. If more privacy is desired, mail to @email{scdbackup@@gmx.net}. @* @sp 1 Please describe what you expect @command{xorriso} to do, the program arguments or dialog commands by which you tried to achieve it, the messages of @command{xorriso}, and the undesirable outcome of your program run. @* @sp 1 Expect to get asked more questions before solutions can be proposed. @c man .SH AUTHOR @node Legal, CommandIdx, Bugreport, Top @chapter Author, Copyright, Credits @section Author Thomas Schmitt <scdbackup@@gmx.net> @* for libburnia-project.org @c man .SH COPYRIGHT @section Copyright Copyright (c) 2007 - 2024 Thomas Schmitt @* Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of @command{xorriso}. If you make use of the license to derive modified versions of @command{xorriso} then you are entitled to modify this text under that same license. @c man .SH CREDITS @section Credits @command{xorriso} is in part based on work by Vreixo Formoso who provides libisofs together with Mario Danic who also leads the libburnia team. Vladimir Serbinenko contributed the HFS+ filesystem code and related knowledge. Thanks to Andy Polyakov who invented emulated growing, to Derek Foreman and Ben Jansens who once founded libburn. @* Compliments towards Joerg Schilling whose cdrtools served me for ten years. @c man-ignore-lines begin @node CommandIdx, ConceptIdx, Legal, Top @chapter Alphabetic Command List @printindex ky @node ConceptIdx,, CommandIdx, Top @chapter Alphabetic List of Concepts and Objects @printindex cp @c man-ignore-lines end @bye #!/bin/sh -x aclocal -I . libtoolize --copy --force autoconf autoheader automake --foreign --add-missing --copy --include-deps #ifndef Xorriso_build_timestamP #define Xorriso_build_timestamP "-none-given-" #endif #ifndef Xorriso_build_timestamP #define Xorriso_build_timestamP "-none-given-" #endif <HTML> <HEAD> <META NAME="description" CONTENT="GNU xorriso, creates, loads, manipulates and writes ISO 9660 filesystem images with Rock Ridge extensions"> <META NAME="keywords" CONTENT="xorriso, libburn, libburnia, burn, CD, DVD, BD, ISO, ISO 9660, RockRidge, Rock Ridge, GNU/Linux, Linux, FreeBSD, Solaris, NetBSD, recording, burning, CD-R, CD-RW, DVD-R, DVD-RW, DVD+RW, DVD+R, DVD+R DL, BD-RE, BD-R, scdbackup"> <META NAME="robots" CONTENT="follow"> <TITLE>GNU xorriso - GNU Project - Free Software Foundation

GNU xorriso

ISO 9660 Rock Ridge Filesystem Manipulator
for GNU/Linux, FreeBSD, Solaris, NetBSD

Purpose:

xorriso copies file objects from POSIX compliant filesystems into Rock Ridge enhanced ISO 9660 filesystems and allows session-wise manipulation of such filesystems. It can load the management information of existing ISO images and it writes the session results to optical media or to filesystem objects.
Vice versa xorriso is able to copy file objects out of ISO 9660 filesystems.


Direct hop to download links ->

Hardware requirements:

About any CD, DVD, or BD recorder produced in the recent ten years.
libburn supports recorders which are compliant to standards MMC-1 for CD and MMC-5 for DVD or BD.
GNU/Linux, FreeBSD, Solaris, NetBSD provide access to drives connected via SCSI, PATA (aka IDE, ATA), USB, or SATA.
xorriso also operates on ISO images in data files or block devices. Images or add-on sessions may be written to about any kind of file object.

Software requirements :

GNU/Linux with kernel 2.4 or higher, libc, libpthread :
With kernel 2.4 a PATA/IDE drive has to be under ide-scsi emulation.
Since kernel 2.6 ide-scsi is not needed.
or FreeBSD, libc, libpthread, libcam, libiconv :
PATA/IDE drives need atapicam running.
SATA drives need atapicam running or need to be driven by ahci.
or Solaris, libc, libpthread, libvolmgt :
Tested on kernel 5.11, hopefully suitable for older ones too.
or NetBSD, libc, libpthread :
Tested on 6.1.2 and 6.1.3, hopefully suitable for older ones too.
or some other X/Open system, libc, libpthread :
There will be no direct operation of optical drives, but only POSIX i/o with objects of the local filesystem.
Might work with DVD-RAM, DVD+RW, BD-RE but rather not with CD, DVD-R, DVD+R, BD-R.

Optional supporting software:

libreadline and libreadline-dev
make dialog more convenient.
libacl and libacl-devel
allow on GNU/Linux to get and set ACLs.
zlib and zlib-devel
allow zisofs, gzip compression, and Jigdo file production.
libbz2 and libbz2-devel
allow bzip2 compression of Jigdo template files.
Tcl, Tk >= 8.4, Tcl/Tk package BWidget
enable the operation of GUI frontend script xorriso-tcltk.

This program has been tested on GNU/Linux, FreeBSD, Solaris, NetBSD systems.
For ports to other usable systems contact us.


Special features:

  • ISO 9660 formatter and burner for CD, DVD, BD are fixely integrated.
  • Operates on an existing ISO image or creates a new one.
  • Copies files from filesystem into the ISO image and vice versa.
  • Changes file properties, renames or deletes file objects in the ISO image.
  • Updates ISO subtrees incrementally to match given disk subtrees.
  • Can record and restore hard link relations, ACL, and xattr.
  • Can attach MD5 checksums to each data file and the whole session.
  • File content may get zisofs or gzip compressed or filtered by external processes.
  • Can activate ISOLINUX and GRUB boot equipment by El Torito boot record, MBR code for BIOS, or EFI System Partition.
  • Writes result as completely new image or as add-on session to optical media or filesystem objects.
  • Can perform multi-session tasks as emulation of mkisofs and cdrecord.
  • Can issue commands to mount older sessions on GNU/Linux or FreeBSD.
  • Can check media for damages and copy readable blocks to disk.
  • Scans for optical drives, blanks re-useable optical media, formats media.
  • Suitable for: CD-R, CD-RW, DVD-R, DVD-R DL, DVD-RW, DVD+R, DVD+R DL, DVD+RW, DVD-RAM, BD-R, BD-RE.
  • Reads its instructions from command line arguments, dialog, and batch files.
  • Provides navigation commands for interactive ISO image manipulation.

Note that xorriso does not write audio CDs and that it does not produce UDF filesystems which are specified for official video DVD or BD.

GUI Frontend:

xorriso itself is entirely controlled by text commands. In dialog mode, xorriso reads them as text lines from standard input. Together with some helpful xorriso commands, it is quite easy for frontend programs to operate a separate xorriso process.
Other than the usual batch programs, xorriso will take care of maintaining the emerging ISO image model. Its state can be inquired by the frontend at any time. The frontend is supposed to care for the display of the inquired xorriso state and to send xorriso commands to manipulate the ISO image model.
As a proof of concept, there is a Tcl/Tk script which can be launched by shell command xorriso-tcltk
Click on this image to see a screenshot:
To screenshot of frontend script xorriso-tcltk
See also file frontend/README-tcltk.
The script xorriso-tcltk is part of the tarball and gets installed by make install. If a xorriso distro package does not install it, you may get it directly from libburnia git: libisoburn/frontend/xorriso-tcltk You will probably have to give it x-permission after download. Some browsers insist in adding ".htm" to the file name.
Further you need xorriso >= 1.5.6, Tcl, Tk >= 8.4, Tcl/Tk package "BWidget".

Command Examples:

Get an overview of drives and their addresses
# xorriso -devices
...
0 -dev '/dev/sr0' rwrw-- : 'TSSTcorp' 'CDDVDW SH-S203B'
1 -dev '/dev/scd1' rwrw-- : 'PHILIPS ' 'SPD3300L'
2 -dev '/dev/hda' rwrw-- : 'HL-DT-ST' 'DVD-ROM GDR8162B'
...
Being superuser avoids permission problems with /dev/srN resp. /dev/hdX .
Ordinary users should then get granted access to the /dev files as listed by option --devices. GNU/Linux, FreeBSD, NetBSD demand rw-permission. On Solaris it is r-permission and privileges "basic,sys_devices".
 
Options are either performed as program arguments or as dialog input. Some options have a parameter list of variable length. This list has to be terminated by word '--' or by the end of the input line. Option -add may accept pathspecs of form target=source as known from program mkisofs.

Get info about a particular drive and loaded media:
$ xorriso -indev /dev/sr0 -du / -- -toc 2>&1 | less
Make re-usable media writable again, delete any ISO 9660 image, prepare yet unused BD-RE:
$ xorriso -outdev /dev/sr0 -blank as_needed -eject all

Write some directories into a new or existing ISO 9660 image:
$ xorriso -dev /dev/sr0 -add /home/me/sounds /home/me/pictures
Have a look at the result:
$ xorriso -indev /dev/sr0 -du / -- -toc 2>&1 | less

Create new ISO-9660 filesystem image, compose content, adjust permissions to make it publicly read-only, write it to media and immediately eject media without previously reloading the written image.
$ cd /home/me
$ xorriso -outdev /dev/sr0 -blank as_needed \
  -map /home/me/sounds /sounds \
  -map /home/me/pictures /pictures \
  -rm_r /sounds/indecent '/pictures/*private*' -- \
  -cd / \
  -add pictures/private/horses* -- \
  -chmod_r a+r,a-w / -- \
  -find / -type d -exec chmod a+x -- \
  -volid SOUNDS_PICS_2008_01_16 \
  -commit_eject all

Load the previous session from media, remove (i.e. hide) directory /sounds, rename /pictures/private/horses, add new directory trees /sounds and /movies, disallow any access for group and others. Finally write as additional session to media and eject:
$ xorriso -dev /dev/sr0 \
  -rm_r /sounds -- \
  -mv /pictures/private/horses /horse_show -- \
  -map /home/me/prepared_for_dvd/sounds_dummy /sounds \
  -map /home/me/prepared_for_dvd/movies /movies \
  -chmod_r go-rwx / -- \
  -volid SOUNDS_PICS_2008_01_17 \
  -commit_eject all

Merge the various sessions from old readable media into a single session on new writeable media, cleaning out all invalidated files and session overhead. Touch / in order to mark the image as worth to be written.
Important: -indev and -outdev have to be different drives.
$ xorriso -indev /dev/dvd \
  -alter_date a +0 / -- \
  -outdev /dev/sr0 -blank fast \
  -commit_eject all

Dialog mode accepts one or more options per line. An option and all its arguments have to be given in one single line. Backslash may be used to mark a line as incomplete so it gets continued by the next input line.
Command -end stops the program run. It will write pending changes to media, if that has not already been done by a previous -commit.
$ xorriso -dialog on
enter option and arguments :
-dev /dev/sr0
enter option and arguments :
-map /home/me/prepared_for_dvd/sounds_dummy /sounds
enter option and arguments :
-map /home/me/prepared_for_dvd/movies \
Trailing backslash : Enter rest of line (or @@@ to clear it) :
/movies
Available navigation commands: -cd, -ls, -du, -find
enter option and arguments :
-commit
... perform further commands and finally do:
enter option and arguments :
-end

The following command performs incremental backup. It can be run on blank media to create a copy of the mentioned disk directory trees, and it can be run on appendable media to perform a minimal set of change operations which update the old ISO copies to match the new disk trees. Older states can be retrieved by help of mount options like "sbsector=" or by help of xorriso option -mount.
Eventual ACL, xattr and hardlink relations will be recorded. MD5 checksums will be computed and recorded. Data comparison will be avoided by accelerator option -disk_dev_ino. After writing, the new session will be checked by its recorded MD5.
Only blank media or media with volume id "PROJECTS_MAIL_..." will be accepted. Files with names ending by ".o" or ".swp" are excluded by options -not_leaf.
$ xorriso -for_backup -disk_dev_ino on \
   -assert_volid 'PROJECTS_MAIL_*' FATAL \
   -dev /dev/sr0 \
   -volid PROJECTS_MAIL_"$(date '+%Y_%m_%d_%H%M%S')" \
   -not_leaf '*.o' -not_leaf '*.swp' \
   -update_r /home/thomas/projects /projects \
   -update_r /home/thomas/personal_mail /personal_mail \
   -commit -toc -check_md5 FAILURE -- -eject all
To apply zisofs compression to those data files which get newly copied from the local filesystem, perform immediately before -commit :
   -hardlinks perform_update \
   -find / -type f -pending_data -exec set_filter --zisofs -- \

Operating systems usually mount the most recent session on media. xorriso can issue the appropriate mount commands for older sessions. First get an overview of the sessions on the media:
$ xorriso -outdev /dev/sr0 -toc
TOC layout   : Idx ,  sbsector ,       Size , Volume Id
ISO session  :   1 ,         0 ,    104719s , PROJECTS_MAIL_2008_08_10_231435
ISO session  :   2 ,    106928 ,      6785s , PROJECTS_MAIL_2008_08_14_184548
...
ISO session  :  76 ,    820384 ,     11035s , PROJECTS_MAIL_2009_01_04_191150
Then become superuser and let xorriso mount the session of August 14, 2008 to directory /mnt:
#  xorriso -osirrox on -mount /dev/sr0 volid '*_2008_08_14_*' /mnt
To be later unmounted by: umount /mnt

For creating bootable ISO images with GRUB2, use its script grub-mkrescue which knows how to operate xorriso.

After the user has already created a suitable file tree on disk and copied the ISOLINUX files into subdirectory ./boot/isolinux of that tree, xorriso can burn an El Torito bootable media:
$ xorriso -outdev /dev/sr0 -blank as_needed \
   -map /home/me/ISOLINUX_prepared_tree / \
   -boot_image isolinux dir=/boot/isolinux
An MBR may be added if the file isolinux.bin is modern enough for "isohybrid" (syslinux version 3.72). It enables booting from hard disk or USB stick:
   -boot_image isolinux system_area=mbr/isohdpfx.bin \
   -boot_image isolinux partition_table=on \

ISO images may not only be stored on optical media but also in regular disk files or block devices for full multi-session operation.
$ xorriso -dev /tmp/regular_file ...other.options...
A default setting for safety reasons requires that files below /dev/ need prefix "stdio:" if they do not lead to MMC burner devices. Be cautious not to overwrite your hard disk instead of your USB stick:
$ xorriso -dev stdio:/dev/sdb ...other.options...
Other file types are suitable only for writing but not for reading:
$ xorriso -outdev /tmp/named_pipe ...other.options...
In batch mode it is possible to operate xorriso in a pipeline with an external consumer of the generated ISO image. Any message output will be redirected to stderr in this case. Examples for consumers are cdrecord or growisofs on operating systems where xorriso cannot operate the burner drive directly, or a ssh pipe to another system which has the desired drive and a suitable burn program.
$ xorriso -outdev - ...other.options... | consumer

Let xorriso serve underneath growisofs via its alias name "xorrisofs" which enables mkisofs emulation:
$ export MKISOFS="xorrisofs"
$ growisofs -Z /dev/dvd /some/files
$ growisofs -M /dev/dvd /more/files
One may switch from mkisofs emulation to xorriso's own command mode:
$ growisofs -M /dev/dvd -- outdev - -update_r /my/files /files

If for any reason the reading operating system mishandles the ISO image or some files in it, one may enable reverse operation of xorriso and copy files or trees to disk:
$ xorriso -acl on -xattr on \
   -indev /dev/sr0 \
   -osirrox on \
   -cpx '/pictures/private/horses*/*buttercup*' \
       /home/her/buttercup_dir -- \
   -extract /sounds /home/her/sounds_from_me
Consider to enter dialog mode and use commands like -cd , -du , -lsl , -find.

Get overview of the options:
$ xorriso -help
Read the detailed manual page:
$ man xorriso


Download as source code (see README):

xorriso-1.5.6.pl02.tar.gz (2780 KiB).
(Released 14 Jun 2023)
xorriso-1.5.6.pl02.tar.gz.sig
(detached GPG signature for verification by either
wget https://ftp.gnu.org/gnu/gnu-keyring.gpg
gpg --with-fingerprint --keyring ./gnu-keyring.gpg --verify xorriso-1.5.6.pl02.tar.gz.sig
or
gpg --keyserver keyserver.ubuntu.com --recv-keys ABC0A854
gpg --with-fingerprint --verify xorriso-1.5.6.pl02.tar.gz.sig
Both should confirm
gpg: Good signature from "Thomas Schmitt <scdbackup@gmx.net>"
Primary key fingerprint: 44BC 9FD0 D688 EB00 7C4D D029 E9CB DFC0 ABC0 A854 )
Also on mirrors of ftp://ftp.gnu.org/gnu/ as xorriso/xorriso-1.5.6.pl02.tar.gz
Documentation:
README about installation and drive setup
xorriso -help gives an overview of options
xorriso -as mkisofs -help supported options of mkisofs emulation
xorriso -as cdrecord -help supported options of cdrecord emulation
man xorriso is the manual page
man xorrisofs describes the mkisofs emulation
man xorrecord describes the cdrecord emulation
Contact:
GNU xorriso support mailing list, bug-xorriso@gnu.org
Thomas Schmitt, scdbackup@gmx.net
License:
GPL version 3 or later.
 


Bug fixes towards previous stable version xorriso-1.5.4.pl02:

  • False -status failure with -boot_image --interval:appended_partition
  • -no_rc prevented pre-scanning of arguments for stdio output and others.
  • -not_leaf and -not_paths were not applied to -extract and alike
  • -report_system_area cmd misperceived -part_like_isohybrid with -isohybrid-gpt-basdat
  • -report_system_area cmd misperceived combination of isohybrid and appended partition in GPT
  • -as mkisofs option -part_like_isohybrid did not cause a MBR partition table if the partitions are data files in the ISO rather than appended
  • Split file directories (-split_size) were created with wrong permissions
  • libisofs did not mark clones of imported files as imported. This could cause that original and clone occupy data storage in the newly written session. Thanks to Ivan Shmakov.
  • Partition offset was preserved from -indev rather than from -outdev
  • libisofs could misrepresent Rock Ridge information if many symbolic links or AAIP data were recorded in a directory
  • Data files named /boot.catalog or ./boot.cat could be left out of the emerging ISO if the boot catalog was set to be hidden
  • -toc reported wrong track LBA with overwritable media with unrecognized content (pseudo-closed)
  • -find test -has_xattr matched "isofs." attributes in -xattr mode "any"

Bug fixes towards deprecated version xorriso-1.5.6:

  • On non-GNU/Linux systems ssize_t was not defined in libisofs file rockridge.h . Report and fix proposal by Rui Chen.

Enhancements towards previous stable version xorriso-1.5.4.pl02:

  • New -boot_image settings gpt_iso_bootable= and gpt_iso_not_ro=
  • New -as mkisofs options --gpt-iso-bootable and --gpt-iso-not-ro
  • New -as cdrecord option --obs_pad. Automatic no_emul_toc with -as cdrecord.
  • New parameters "obs_pad" and "bdr_obs_exempt" for -dvd_obs
  • New -as cdrecord option --bdr_obs_exempt
  • New command -assess_indev_features
  • New -find test -size
  • New -compliance rules max_ce_entries=, max_ce_drop=
  • Allowed lseekable device files with -cut_out. Proof-of-concept by Ivan Shmakov on bugs.debian.org.


Software copies included in GNU xorriso:

GNU xorriso is feature-wise equivalent to the dynamic compilation of libburnia libraries, libjte, and libburnia program xorriso. It restricts itself to a technical form where the legal commitments of the libburnia project and the legal intentions of FSF match completely.
 
libburn-1.5.6
reads and writes data from and to CD, DVD, BD.
(founded by Derek Foreman and Ben Jansens, developed and maintained since August 2006 by Thomas Schmitt from team of libburnia-project.org)
libisofs-1.5.6
operates on ISO 9660 filesystem images.
(By Vreixo Formoso, Mario Danic and Thomas Schmitt from team of libburnia-project.org. HFS+ code by Vladimir Serbinenko.)
libisoburn-1.5.6
coordinates libburn and libisofs, emulates multi-session where needed, and hosts the original source code of program xorriso.
It provides the complete functionality of xorriso via a C language API.
(By Vreixo Formoso and Thomas Schmitt from team of libburnia-project.org
The helper script xorriso-dd-target is developed in cooperation with Nio Wiklund alias sudodus.)
libjte-2.0.0
produces jigdo and template file together with the emerging ISO image.
(By Free Software Foundation, Steve McIntyre, George Danchev, Thomas Schmitt)
 
The source code of this software is independent of cdrecord and mkisofs.


Development snapshot, version 1.5.7 :

Bug fixes towards stable version 1.5.6:
  • On non-GNU/Linux systems ssize_t was not defined in libisofs file rockridge.h . Report and fix proposal by Rui Chen.
  • -boot_image and -append_partition were not perceived as image manipulation which makes production of an ISO image worthwhile. Thanks Cameron Seader.
  • -outdev holding an ISO filesystem could get attached wrong start LBA
  • Command -load "volid" did not work with constant search text
  • Command -truncate_overwritable on ISO image without MD5 caused double free of memory
  • Command -boot_image "any" "replay" failed after the legacy BIOS boot image file was replaced by -map. Thanks Brian C. Lane.
  • Command -boot_image system_area=/dev/zero preserved system area of loaded ISO
  • Size assessment of ISO images smaller than 32 KiB yielded random values
Enhancements towards stable version 1.5.6:
  • New -append_partition pseudo partition_number "all" and pseudo type_code "revoke"
  • New -as mkisofs options -cut_out and -hide_iso_path
  • Improved handling of hidden boot images in -boot_image cmd/as_mkisofs/replay
  • The maximum number of appended GPT partitions was increased from 4 to 8
  • New command -toc_info_type
  • New entities "at_time", "before", "after", "not_after", "not_before" for commands -load, -mount, -mount_cmd, -session_string, -truncate_overwritable
 
README 1.5.7
xorriso-1.5.7 -help
xorriso-1.5.7 -as mkisofs -help
xorriso-1.5.7 -as cdrecord -help
man xorriso (as of 1.5.7)
man xorrisofs (as of 1.5.7)
man xorrecord (as of 1.5.7)
 
If you want to distribute development versions of xorriso, then use this tarball which produces static linking between xorriso and the libburnia libraries.
Source (./bootstrap is already applied, build tested, installation see README)
xorriso-1.5.7.tar.gz (2780 KiB).
A dynamically linked development version of xorriso can be obtained from repositories of libburnia-project.org. xorriso is part of libisoburn/trunk and will get built by its "make".
Be warned that the libraries in git are development versions with possibly unstable API/ABI enhancements. Do not distribute development versions for dynamic linking. Only release versions are safe for that.
Download: git clone https://dev.lovelyhq.com/libburnia/libburn.git
Install: cd libburn ; ./bootstrap ; ./configure --prefix /usr ; make ; make install
Download: git clone https://dev.lovelyhq.com/libburnia/libisofs.git
Install: cd libisofs ; ./bootstrap ; ./configure --prefix /usr ; make ; make install
Download: git clone https://dev.lovelyhq.com/libburnia/libisoburn.git
Install: cd libisoburn ; ./bootstrap ; ./configure --prefix /usr ; make ; make install
Build of git versions needs autotools of at least version 1.7 installed. But after the run of ./bootstrap, only vanilla tools like make and gcc are needed.
GNU xorriso contains a copy of libjte-2.0.0 from package jigit which produces jigdo and template file together with the emerging ISO image. (By Free Software Foundation, Steve McIntyre, George Danchev, Thomas Schmitt)
Important: If desired, libjte has to be already installed when libisofs and libisoburn get built.
Download: wget https://www.einval.com/~steve/software/JTE/download/jigit-1.22.tar.xz
Install: unxz <jigit-1.22.tar.xz | tar xf - ; cd jigit-1.22 ; make ; make install
 


Many thanks to Derek Foreman and Ben Jansens for starting libburn.
Very special thanks to Andy Polyakov whose dvd+rw-tools provide the libburnia project with invaluable examples on how to deal with DVD media and how to emulate multi-session on overwritable media.


Dedicated to the GNU Operating System
Enjoying free hosting by
sourceforge.net
www.webframe.org


Links to related free software projects of Thomas Schmitt:
cdrskin, a cdrecord emulator
scdbackup, multi volume CD/DVD/BD backup
(a second source of above)

Legal statement: This website does not serve any commercial purpose.

Copyright 2008 - 2024 Thomas Schmitt.
This text is freely distributable. It shall only be modified in sync with the factual properties of xorriso and its public storage locations. If you make use of the license to derive modified versions of xorriso then you are entitled to modify this text under that same license.
Contact for issues of this web page or the described program:
Thomas Schmitt, scdbackup@gmx.net
GNU xorriso support mailing list, bug-xorriso@gnu.org
/* xorriso - Command line oriented batch and dialog tool which creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2023 Thomas Schmitt, Initial code of this program was derived from program src/askme.c out of scdbackup-0.8.8, Copyright 2007 Thomas Schmitt, BSD-License. Provided under GPL version 2 or later, with the announcement that this might get changed in future. I would prefer BSD or LGPL as soon as the license situation of the library code allows that. (This announcement affects only future releases of xorriso and it will always be possible to derive a GPLv2+ from the future license.) There is a derived package "GNU xorriso" under GPLv3+ which combines the libburnia libraries and program xorriso to a statically linked binary. Overview of xorriso architecture: libburn provides the ability to read and write data. libisofs interprets and manipulates ISO 9660 directory trees. It generates the output stream which is handed over to libburn. libisoburn by its lower level API encapsulates the connectivity issues between libburn and libisofs. This API also enables multi-session emulation on overwritable media and random access file objects. xorriso is the higher level API of libisoburn which allows to operate all three libraries by a unified set of commands. exposes the public functions. Among these functions are direct equivalents of the xorriso interpreter commands. There are also functions for fundamental management and for handling event messages. This file xorriso_main.c runs the xorriso API as batch and dialog program. One should not mix the use of the xorriso API with the use of the lower level APIs of libburn, libisofs, libisoburn. -------------------------------------------------------------------------- The following overview is relevant for development but not for usage of xorriso. An application programmer should read xorriso.h and man xorriso (or info xorriso), rather than diving into its source code. For examples see the functions main() and check_compatibility() below. -------------------------------------------------------------------------- The xorriso source is divided in two groups: A set of source modules interacts with the lower level library APIs: base_obj.[ch] fundamental operations of the XorrisO object lib_mgt.[ch] manages the relation between xorriso and the libraries drive_mgt.[ch] operates on drives and media iso_img.[ch] operates on ISO images and their global properties iso_tree.[ch] access nodes of the libisofs tree model iso_manip.[ch] manipulates the libisofs tree model sort_cmp.[ch] sorts and compare tree nodes write_run.[ch] functions to write sessions read_run.[ch] functions to read data from ISO image filters.[ch] operates on data filter objects xorrisoburn.h declarations needed by the non-library modules Another set is independent of the lower level APIs: parse_exec.c deals with parsing and interpretation of command input sfile.c functions around files and strings aux_objects.c various helper classes misc_funct.c miscellaneous helper functions findjob.c performs tree searches in libisofs or in POSIX filesystem check_media.c perform verifying runs on media or images text_io.c text i/o functions match.c functions for pattern matching emulators.c emulators for mkisofs and cdrecord disk_ops.c actions on onjects of disk filesystems cmp_update.c compare or update files between disk filesystem and ISO filesystem opts_a_c.c commands -a* to -c* opts_d_h.c commands -d* to -h* opts_i_o.c commands -i* to -o* opts_p_z.c commands -p* to -z* xorriso_private.h contains the definition of struct Xorriso and for convenience includes the .h files of the non-library group. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include #include #include #include #include #include /* xorriso_main.c includes the internal copy of the API definition */ /* The official xorriso options API is defined in */ #include "xorriso.h" /* The minimum version of libisoburn xorriso API to be used with this version of xorriso. */ #define Xorriso_req_majoR 1 #define Xorriso_req_minoR 5 #define Xorriso_req_micrO 7 static void yell_xorriso() { fprintf(stderr, "%sxorriso %d.%d.%d%s : RockRidge filesystem manipulator, libburnia project.\n\n", #ifdef Xorriso_GNU_xorrisO "GNU ", #else "", #endif Xorriso_header_version_majoR, Xorriso_header_version_minoR, Xorriso_header_version_micrO, Xorriso_program_patch_leveL); } /* Check whether build configuration and runtime linking are consistent. */ static void check_compatibility() { int lib_major, lib_minor, lib_micro; /* First an ugly compile time check for header version compatibility. If everything matches, then no C code is produced. In case of mismatch, intentionally faulty C code will be inserted. */ /* The minimum requirement of xorriso towards the libisoburn header at compile time is defined above Xorriso_req_majoR Xorriso_req_minoR Xorriso_req_micrO It gets compared against the version macros in xorriso.h : Xorriso_header_version_majoR Xorriso_header_version_minoR Xorriso_header_version_micrO If the header is too old then the following code shall cause failure of cdrskin compilation rather than to allow production of a program with unpredictable bugs or memory corruption. The compiler messages supposed to appear in this case are: error: 'XORRISO_MISCONFIGURATION' undeclared (first use in this function) error: 'INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_xorriso_dot_h_TOO_OLD__SEE_xorriso_main_dot_c' undeclared (first use in this function) error: 'XORRISO_MISCONFIGURATION_' undeclared (first use in this function) */ /* The indentation is an advise of man gcc to help old compilers ignoring */ #if Xorriso_req_majoR > Xorriso_header_version_majoR #define Xorriso_dot_h_too_olD 1 #endif #if Xorriso_req_majoR == Xorriso_header_version_majoR && Xorriso_req_minoR > Xorriso_header_version_minoR #define Xorriso_dot_h_too_olD 1 #endif #if Xorriso_req_minoR == Xorriso_header_version_minoR && Xorriso_req_micrO > Xorriso_header_version_micrO #define Xorriso_dot_h_too_olD 1 #endif #ifdef Xorriso_dot_h_too_olD XORRISO_MISCONFIGURATION = 0; INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_xorriso_dot_h_TOO_OLD__SEE_xorriso_main_dot_c = 0; XORRISO_MISCONFIGURATION_ = 0; #endif /* End of ugly compile time test (scroll up for explanation) */ /* Needed are at least 44 bits in signed type off_t . This is a popular mistake in configuration or compilation. */ if(sizeof(off_t) < 6) { yell_xorriso(); fprintf(stderr, "xorriso : FATAL : Compile time misconfiguration. sizeof(off_t) too small.\n\n"); exit(4); } /* Check whether the linked xorriso code is young enough. */ if(! Xorriso__is_compatible(Xorriso_header_version_majoR, Xorriso_header_version_minoR, Xorriso_header_version_micrO, 0)) { yell_xorriso(); Xorriso__version(&lib_major, &lib_minor, &lib_micro); fprintf(stderr, "xorriso : FATAL : libisoburn/xorriso runtime version mismatch. Found %d.%d.%d, need %d.%d.%d\n\n", lib_major, lib_minor, lib_micro, Xorriso_header_version_majoR, Xorriso_header_version_minoR, Xorriso_header_version_micrO); exit(4); } } int main(int argc, char **argv) { int ret, i; struct XorrisO *xorriso= NULL; char **orig_argv= NULL; check_compatibility(); /* might exit() */ if(argc < 2) { yell_xorriso(); fprintf(stderr,"usage : %s [commands]\n", argv[0]); fprintf(stderr, " More is told by command -help\n"); exit(2); } setlocale(LC_CTYPE, ""); ret= Xorriso_new(&xorriso, argv[0], 0); if(ret <= 0) { fprintf(stderr,"Creation of XorrisO object failed. (not enough memory ?)\n"); exit(3); } /* The prescan of arguments performs actions which have to happen before the normal processing of startup files and arguments. Among them are -help and -prog_help which end the program without yelling its name and version. */ ret= Xorriso_prescan_args(xorriso,argc,argv,0); if(ret == 0) goto end_successfully; /* Put out program name and version to stderr only if not done already now */ yell_xorriso(); if(ret < 0) exit(5); /* After having yelled xorriso, prescan again for unknown arguments */ ret= Xorriso_prescan_args(xorriso, argc, argv, 2); if(ret < 0) exit(5); /* The following command interpreters are allowed only after this initialization. */ ret= Xorriso_startup_libraries(xorriso, 0); if(ret <= 0) {ret= 4; goto emergency_exit;} Xorriso_process_msg_queues(xorriso, 0); /* Interpret startup files */ ret= Xorriso_read_rc(xorriso, 0); if(ret == 3) goto end_successfully; if(ret <= 0) {ret= 5; goto emergency_exit;} /* Interpret program arguments */ orig_argv= argv; ret= Xorriso_program_arg_bsl(xorriso, argc, &argv, 0); if(ret <= 0) {ret= 5; goto emergency_exit;} i= 1; ret= Xorriso_interpreter(xorriso, argc, argv, &i, 2); if(ret == 3) goto end_successfully; if(ret <= 0) {ret= 5; goto emergency_exit;} /* Enter dialog mode if it has been activated meanwhile */ ret= Xorriso_dialog(xorriso, 0); if(ret <= 0) {ret= 6; goto emergency_exit;} end_successfully:; /* normal shutdown, including eventual -commit */ Xorriso_stop_msg_watcher(xorriso, 1); Xorriso_process_msg_queues(xorriso, 0); if(Xorriso_change_is_pending(xorriso, 1 | 2)) Xorriso_option_end(xorriso, 2); Xorriso_process_msg_queues(xorriso, 0); ret= Xorriso_make_return_value(xorriso, 0); Xorriso_process_errfile(xorriso, 0, "xorriso end", 0, 1); Xorriso_destroy(&xorriso, 1); if(orig_argv != argv && orig_argv != NULL) { for(i= 0; i < argc; i++) if(argv[i] != NULL) free(argv[i]); free(argv); } exit(ret); emergency_exit:; if(xorriso != NULL) { /* minimal shutdown */ Xorriso_process_msg_queues(xorriso, 0); Xorriso_destroy(&xorriso, 1); } exit(ret); } # Copyright (c) 2007 - 2019 Thomas Schmitt # Provided under GPL version 2 or later. # ts A90315 : LIBBURNIA_PKGCONFDIR is defined OS specific in acinclude.m4 # was: pkgconfigdir=$(libdir)/pkgconfig pkgconfigdir=$(LIBBURNIA_PKGCONFDIR) libincludedir= lib_LTLIBRARIES = ACLOCAL_AMFLAGS = -I ./ ## ========================================================================= ## libinclude_HEADERS = ## ========================================================================= ## bin_PROGRAMS = \ xorriso/xorriso xorriso_xorriso_CPPFLAGS = -I./libburn -I./libisofs -I./libisoburn -I./xorriso # ts B00518 : The configuration macros are now transmitted via config.h # xorriso_xorriso_CFLAGS = -DXorriso_standalonE \ # $(READLINE_DEF) $(LIBACL_DEF) $(XATTR_DEF) \ # $(EXTF_DEF) $(EXTF_SUID_DEF) $(ZLIB_DEF) \ # $(LIBCDIO_DEF) \ # $(XORRISO_DVD_OBS_64K) $(LIBBURN_O_DIRECT_DEF) xorriso_xorriso_CFLAGS = xorriso_xorriso_LDADD = $(THREAD_LIBS) $(LIBBURN_ARCH_LIBS) xorriso_xorriso_SOURCES = \ \ xorriso/xorriso.h \ xorriso/xorriso_private.h \ xorriso/xorriso_main.c \ xorriso/sfile.h \ xorriso/sfile.c \ xorriso/aux_objects.h \ xorriso/aux_objects.c \ xorriso/findjob.h \ xorriso/findjob.c \ xorriso/check_media.h \ xorriso/check_media.c \ xorriso/misc_funct.h \ xorriso/misc_funct.c \ xorriso/text_io.h \ xorriso/text_io.c \ xorriso/match.h \ xorriso/match.c \ xorriso/emulators.h \ xorriso/emulators.c \ xorriso/disk_ops.h \ xorriso/disk_ops.c \ xorriso/cmp_update.h \ xorriso/cmp_update.c \ xorriso/parse_exec.h \ xorriso/parse_exec.c \ xorriso/opts_a_c.c \ xorriso/opts_d_h.c \ xorriso/opts_i_o.c \ xorriso/opts_p_z.c \ \ xorriso/xorrisoburn.h \ xorriso/base_obj.h \ xorriso/base_obj.c \ xorriso/lib_mgt.h \ xorriso/lib_mgt.c \ xorriso/sort_cmp.h \ xorriso/sort_cmp.c \ xorriso/drive_mgt.h \ xorriso/drive_mgt.c \ xorriso/iso_img.h \ xorriso/iso_img.c \ xorriso/iso_tree.h \ xorriso/iso_tree.c \ xorriso/iso_manip.h \ xorriso/iso_manip.c \ xorriso/write_run.h \ xorriso/write_run.c \ xorriso/read_run.h \ xorriso/read_run.c \ xorriso/filters.h \ xorriso/filters.c \ \ xorriso/xorriso_timestamp.h \ xorriso/xorriso_buildstamp.h \ \ libisoburn/libisoburn.h \ libisoburn/isoburn.h \ libisoburn/isoburn.c \ libisoburn/isofs_wrap.c \ libisoburn/burn_wrap.c \ libisoburn/data_source.c \ \ libisofs/libisofs.h \ libisofs/builder.h \ libisofs/builder.c \ libisofs/node.h \ libisofs/node.c \ libisofs/tree.h \ libisofs/tree.c \ libisofs/image.h \ libisofs/image.c \ libisofs/iso1999.h \ libisofs/iso1999.c \ libisofs/fsource.h \ libisofs/fsource.c \ libisofs/fs_local.c \ libisofs/fs_image.c \ libisofs/messages.h \ libisofs/messages.c \ libisofs/libiso_msgs.h \ libisofs/libiso_msgs.c \ libisofs/stream.h \ libisofs/stream.c \ libisofs/util.h \ libisofs/util.c \ libisofs/util_rbtree.c \ libisofs/util_htable.c \ libisofs/filesrc.h \ libisofs/filesrc.c \ libisofs/ecma119.h \ libisofs/ecma119.c \ libisofs/ecma119_tree.h \ libisofs/ecma119_tree.c \ libisofs/writer.h \ libisofs/buffer.h \ libisofs/buffer.c \ libisofs/rockridge.h \ libisofs/rockridge.c \ libisofs/rockridge_read.c \ libisofs/joliet.h \ libisofs/joliet.c \ libisofs/hfsplus.h \ libisofs/hfsplus.c \ libisofs/hfsplus_decompose.c \ libisofs/hfsplus_classes.c \ libisofs/hfsplus_case.c \ libisofs/eltorito.h \ libisofs/eltorito.c \ libisofs/data_source.c \ libisofs/find.c \ libisofs/filter.h \ libisofs/filter.c \ libisofs/filters/external.c \ libisofs/filters/zisofs.c \ libisofs/filters/gzip.c \ libisofs/system_area.h \ libisofs/system_area.c \ libisofs/make_isohybrid_mbr.c \ libisofs/aaip_0_2.h \ libisofs/aaip_0_2.c \ libisofs/md5.h \ libisofs/md5.c \ \ libjte/libjte.h \ libjte/libjte_private.h \ libjte/libjte.c \ libjte/jte.h \ libjte/jte.c \ libjte/checksum.h \ libjte/checksum.c \ libjte/endian.c \ libjte/endianconv.h \ libjte/md5.h \ libjte/md5.c \ libjte/rsync.h \ libjte/rsync.c \ libjte/sha1.h \ libjte/sha1.c \ libjte/sha256.h \ libjte/sha256.c \ libjte/sha512.h \ libjte/sha512.c \ \ libburn/async.c \ libburn/async.h \ libburn/back_hacks.h \ libburn/cdtext.c \ libburn/cleanup.c \ libburn/cleanup.h \ libburn/crc.h \ libburn/debug.c \ libburn/debug.h \ libburn/drive.c \ libburn/drive.h \ libburn/ecma130ab.c \ libburn/ecma130ab.h \ libburn/error.h \ libburn/file.c \ libburn/file.h \ libburn/init.c \ libburn/init.h \ libburn/libburn.h \ libburn/libdax_audioxtr.h \ libburn/libdax_audioxtr.c \ libburn/libdax_msgs.h \ libburn/libdax_msgs.c \ libburn/mmc.c \ libburn/mmc.h \ libburn/null.c \ libburn/null.h \ libburn/options.c \ libburn/options.h \ libburn/os.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 # Disabled because unneeded and of unclear ancestry # libburn/crc.c noinst_PROGRAMS = \ test/compare_file \ xorriso/make_xorriso_1 # A program to compare two trees of files in mounted filesystems # To compare tree /media/dvd and /original/dir : # find /media/dvd -exec test/compare_file '{}' /media/dvd /original/dir ';' # test_compare_file_CPPFLAGS = -I $(top_builddir)/xorriso test_compare_file_CFLAGS = test_compare_file_LDADD = test_compare_file_SOURCES = test/compare_file.c # Specialized converter from xorriso/xorriso.texi to xorriso/xorriso.1 # xorriso_make_xorriso_1_CPPFLAGS = -I $(top_builddir)/xorriso xorriso_make_xorriso_1_CFLAGS = xorriso_make_xorriso_1_LDADD = xorriso_make_xorriso_1_SOURCES = xorriso/make_xorriso_1.c # A Proof-of-concept for frontends, and xorriso-dd-target if on Linux kernel bin_SCRIPTS = \ frontend/xorriso-tcltk \ $(XORRISO_DD_TARGET) EXTRA_SCRIPTS = xorriso-dd-target/xorriso-dd-target # Install symbolic links to the xorriso binary # install-exec-hook: if test -e "$(DESTDIR)$(bindir)"/xorrisofs ; then rm "$(DESTDIR)$(bindir)"/xorrisofs ; else echo ; fi ln -s xorriso "$(DESTDIR)$(bindir)"/xorrisofs if test -e "$(DESTDIR)$(bindir)"/osirrox ; then rm "$(DESTDIR)$(bindir)"/osirrox ; else echo ; fi ln -s xorriso "$(DESTDIR)$(bindir)"/osirrox if test -e "$(DESTDIR)$(bindir)"/xorrecord ; then rm "$(DESTDIR)$(bindir)"/xorrecord ; else echo ; fi ln -s xorriso "$(DESTDIR)$(bindir)"/xorrecord # Trying to create a build timestamp file semi-manually: make buildstamped # buildstamp: date -u '+#define Xorriso_build_timestamP "%Y.%m.%d.%H%M%S"' >xorriso/xorriso_buildstamp.h cat xorriso/xorriso_buildstamp.h # For now make buildstamped has to be performed manually. buildstamped: buildstamp make # "make clean" shall remove a few stubborn .libs directories # which George Danchev reported Dec 03 2011. # Learned from: http://www.gnu.org/software/automake/manual/automake.html#Clean clean-local: -rm -rf xorriso/.libs test/.libs # Will be executed by "make check" check-local: xorriso/xorriso -no_rc -version -list_extras all ## ========================================================================= ## # Indent source files indent_files = 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 ## ========================================================================= ## # Extra things man_MANS = \ xorriso/xorriso.1 \ xorriso/xorrisofs.1 \ xorriso/xorrecord.1 \ xorriso/xorriso-tcltk.1 \ $(XORRISO_DD_TARGET_MAN) EXTRA_MANS = xorriso-dd-target/xorriso-dd-target.1 info_TEXINFOS = \ xorriso/xorriso.texi \ xorriso/xorrisofs.texi \ xorriso/xorrecord.texi \ xorriso/xorriso-tcltk.texi \ xorriso-dd-target/xorriso-dd-target.texi # xorriso-dd-target.texi is hardcoded for now because of # Makefile.am: error: texinfo file '@XORRISO_DD_TARGET_TEXI@' has # unrecognized extension # $(XORRISO_DD_TARGET_TEXI) # # EXTRA_TEXINFOS = xorriso-dd-target/xorriso-dd-target.texi EXTRA_DIST = \ version.h.in \ README \ AUTHORS \ CONTRIBUTORS \ COPYRIGHT \ COPYING \ INSTALL \ xorriso/changelog.txt \ xorriso/xorriso_buildstamp_none.h \ xorriso/make_docs.sh \ xorriso/make_xorriso_standalone.sh \ xorriso/unite_html_b_line.c \ xorriso/convert_man_to_html.sh \ xorriso/man_xorriso_to_html.sh \ xorriso/man_xorrisofs_to_html.sh \ xorriso/man_xorrecord_to_html.sh \ $(man_MANS) \ doc/susp_aaip_2_0.txt \ doc/susp_aaip_isofs_names.txt \ doc/zisofs_format.txt \ doc/zisofs2_format.txt \ doc/startup_file.txt \ frontend/frontend_pipes_xorriso.c \ frontend/README-tcltk \ frontend/xorriso-tcltk \ frontend/sh_on_named_pipes.sh \ frontend/xorriso_broker.sh \ frontend/grub-mkrescue-sed.sh \ xorriso-dd-target/xorriso-dd-target \ test/merge_debian_isos \ libisofs/aaip-os-dummy.c \ libisofs/aaip-os-linux.c \ libisofs/aaip-os-freebsd.c \ libburn/os-dummy.h \ libburn/os-freebsd.h \ libburn/os-libcdio.h \ libburn/os-linux.h \ libburn/os-solaris.h \ libburn/sg-dummy.c \ libburn/sg-freebsd.c \ libburn/sg-libcdio.c \ libburn/sg-linux.c \ libburn/sg-solaris.c prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: xorriso Description: ISO 9660 filesystem image manipulator Version: @VERSION@ Requires: Libs: -L${libdir} -lpthread Cflags: /* Command line oriented batch and dialog tool which creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, Provided under GPL version 2 or later. This file contains inner declarations of xorriso. The public interface is in xorriso.h */ /* For now, #ifdef Xorriso_is_xorriso_selF has no meaning. But it is already now to be set only by the xorriso.c module. */ #ifndef Xorriso_private_includeD #define Xorriso_private_includeD yes /* <<< Disable this to disable pthread_mutex locking on message and result output. */ #define Xorriso_fetch_with_msg_queueS yes /* for uint32_t */ #ifdef HAVE_STDINT_H #include #else #ifdef HAVE_INTTYPES_H #include #endif #endif /* for pthread_mutex_t on OpenBSD */ #include /** The source code release timestamp */ #include "xorriso_timestamp.h" #ifndef Xorriso_timestamP #define Xorriso_timestamP "-none-given-" #endif /** The binary build timestamp is to be set externally by the compiler or by a macro definition in xorriso_buildstamp.h. */ #include "xorriso_buildstamp.h" #ifndef Xorriso_build_timestamP #define Xorriso_build_timestamP "-none-given-" #endif #include "sfile.h" #include "misc_funct.h" struct ExclusionS; /* List of -not_* conditions */ struct PermiteM; /* Stack of temporarily altered access permissions */ struct CheckmediajoB; /* Parameters for Xorriso_check_media() */ struct SectorbitmaP; /* Distiniction between valid and invalid sectors */ struct FindjoB; /* Program and status of a find run */ /* maximum number of history lines to be reported with -status:long_history */ #define Xorriso_status_history_maX 100 /** The list of startup file names */ #define Xorriso_rc_nuM 4 /* Default setting for the size limit of single data files: 100 extents with 4 GB - 2 kB each = 400 GB - 200 kB */ #define Xorriso_default_file_size_limiT \ (((off_t) 400) * ((off_t) 1024*1024*1024) - (off_t) 204800) /* Maximum number of appended partitions. Effectively usable number depends on system area type. */ #define Xorriso_max_appended_partitionS 8 /* Maximum length of a disc label text plus 1. */ #define Xorriso_disc_label_sizE 129 struct XorrisO { /* the global context of xorriso */ int libs_are_started; /* source */ char progname[SfileadrL]; char initial_wdx[SfileadrL]; int no_rc; /* Command line argument emulations by program name: 0=xorriso mode 1=mkisofs mode 2=cdrecord mode */ int argument_emulation; /* Whether to try completion of unrecognized options to known genisoimage options. 0=no, 1=yes. */ int genisoimage_completion; /* Under which interpreter the current activities are running: 0=xorriso mode : Xorriso_interpreter() 1=mkisofs mode : Xorriso_genisofs() 2=cdrecord mode : Xorriso_cdrskin() */ int current_interpreter; /** List of startupfiles */ char rc_filenames[Xorriso_rc_nuM][SfileadrL]; int rc_filename_count; int arrange_args; /* Whether .mkisofsrc has already been read */ int mkisofsrc_done; char wdi[SfileadrL]; char wdx[SfileadrL]; int did_something_useful; int add_plainly; off_t split_size; char list_delimiter[81]; /* >>> put libisofs aspects here <<< */ int ino_behavior; /* bit0= at image load time: Do not load PX inode numbers but generate new unique ones for all loaded IsoNode. bit1= at image generation time: Do not consolidate suitable nodes to hardlinks. bit2= at restore-to-disk time: Do not consolidate suitable nodes to hardlinks. bit3= with update: Do not try to detect hardlink splits and joinings. bit4= with extract: Do not create or use hln arrays if sort_lba_on bit5= with command -lsl Do not create hln array for hard link count */ int iso_level; int iso_level_is_default; int do_joliet; int do_hfsplus; int do_fat; int do_rockridge; int do_iso1999; int ecma119_map; /* 0=unmapped , 1=stripped , 2=uppercase , 3=lowercase */ int joliet_map; /* 0=unmapped , 1=stripped */ int do_aaip; /* bit0= ACL in bit1= ACL out bit2= EA in bit3= EA out bit4= record dev,inode per node, isofs_st_out in root bit5= check dev,inode,isofs_st_in bit6= omit content check if bit5 check is conclusive bit7= omit dev check with bit5 bit8= store output charset in xattr "isofs.cs" bit9= set input charset from xattr "isofs.cs" bit10= if bit2: import from local filesystem all xattr namespaces, not only "user." if bit3: export to local filesystem all xattr namespaces, not only "user." During node manipulations: enable manipulations of all xattr namespaces, not only "user." (but usually not "isofs.") bit11= read lfa_flags (chattr) from local file objects and when loading ISO image, write them into the ISO filesystem bit12= restore lfa_flags when restoring file to disk bit13= do not restore known superuser lfa_flags bit14= restore only known lfa_flags bit15= ignore non-settable lfa_flags when importing files from disk and do not record "isofs.fa" if the other flags are all zero bit16= try to restore lfa_flags one-by-one if the whole set fails possibly because of inappropriate attributes */ int lfa_flags_setting; /* Current settings of command -lfa_flags bit0= on bit1= auto_su bit2= auto_on (i.e. only if libisofs has been compiled with lfa enabled) bit3= libisofs indeed has lfa enabled (to be set only at program start) bit11-16= at the end of the command these bits get put into .do_aaip if bit0 is on. Else the lfa bits of do_aaip will be set to 0. */ int lfa_flags_default; /* Default for lfa_flags_setting */ uint64_t lfa_restore_mask; /* At most the flag bits which are set here will be restored when a file gets restored to disk */ char lfa_restore_err_sev[20]; /* Severity to use with error message due to lfa restore problems. "silent" and "all" suppress error messages. */ int do_md5; /* bit0= read MD5 array bit1= write session MD5 bit2= write MD5 for each data file bit3= make file content stability check by double reading bit4= use recorded MD5 as proxy of ISO file bit5= with bit0: do not check tags of superblock,tree,session bit6= during extraction verify file content by recorded MD5 bit7= with bit6: take lack of MD5 as error */ int no_emul_toc; /* bit0= On overwritables: write first session to LBA 0 rather than 32. */ int do_old_empty; /* See -compliance old_empty own data content: range [0,31]. The new way is to have a dedicated block to which all such files will point. */ char scdbackup_tag_name[81]; char scdbackup_tag_time[19]; char scdbackup_tag_written[512]; char scdbackup_tag_listname[SfileadrL]; int relax_compliance; /* opaque bitfield to be set by xorrisoburn */ int allow_dir_id_ext_dflt; /* -compliance allow_dir_id_ext still on default */ char rr_reloc_dir[256]; int rr_reloc_flags; int untranslated_name_len; int do_follow_pattern; int do_follow_param; int do_follow_links; int follow_link_limit; int resolve_link_rec_count; int resolve_link_rec_limit; int do_follow_concat; int do_follow_mount; int do_global_uid; uid_t global_uid; int do_global_gid; gid_t global_gid; int do_global_mode; mode_t global_dir_mode; mode_t global_file_mode; int do_tao; /* 1= Use TAO or Incremental -1= Use SAO or DAO 0= let libburn choose */ struct Xorriso_lsT *filters; int filter_list_closed; int zlib_level; int zlib_level_default; int zisofs_block_size; int zisofs_block_size_default; int zisofs_by_magic; int zisofs_v2_enabled; /* 0=no, 1=as_needed, 2=force */ uint64_t zisofs_max_total_blocks; uint64_t zisofs_max_total_blocks_default; uint64_t zisofs_max_file_blocks; uint64_t zisofs_max_file_blocks_default; int zisofs_v2_block_size; int zisofs_v2_block_size_default; int64_t zisofs_block_number_target; double zisofs_bpt_discard_free_ratio; double zisofs_bpt_discard_free_ratio_default; int zisofs_susp_z2; int zisofs_susp_z2_default; int do_overwrite; /* 0=off, 1=on, 2=nondir */ int do_reassure; /* 0=off, 1=on, 2=tree */ uint32_t isofs_size; /* Size of loaded ISO filesystem */ int isofs_has_what; /* bit0= hasRR bit1= hasJoliet bit2= hasIso1999 bit3= hasElTorito */ int tree_loaded; /* 0= ISO 9660 , 1 = Joliet , 2 = ISO 9660:1999 */ int rr_loaded; /* 1= Rock Ridge information was used, 0= Not */ char volid[33]; int volid_default; char loaded_volid[33]; char assert_volid[SfileadrL]; char assert_volid_sev[80]; char preparer_id[129]; char publisher[129]; char application_id[129]; char system_id[33]; char volset_id[129]; char copyright_file[38]; char biblio_file[38]; char abstract_file[38]; char application_use[SfileadrL]; char session_logfile[SfileadrL]; off_t session_lba; off_t session_blocks; /* >>> put libburn/isoburn aspects here */ struct Xorriso_lsT *drive_blacklist; struct Xorriso_lsT *drive_greylist; struct Xorriso_lsT *drive_whitelist; int toc_emulation_flag; /* bit0= bit3 for isoburn_drive_aquire() scan -ROM profiles for ISO sessions bit1= bit4 for isoburn_drive_aquire() do not emulate TOC on overwritable media bit2= bit7 for isoburn_drive_aquire() pretend any media to be -ROM bit3= bit9 for isoburn_drive_aquire() Ignore enclosing session at LBA 0 */ int image_start_mode; /* From what address to load the ISO image bit0-15= addressing mode 0= automatic lba as deduced from media 1= value is session number 2= value is track number 3= value is lba 4= value is volid bit16= with mode 3 : value is possibly 16 too high. Let isoburn_set_msc1() adjust it. bit30= interference with normal msc1 processing is enabled. Without this bit, isoburn_set_msc1() will not be called. bit31= image loading has happened, setting is kept for rollback only. Always apply as 0=auto. */ char image_start_value[81]; /* value according image_start_mode */ uint32_t displacement; int displacement_sign; int read_fs; /* bit0=norock , bit1=nojoliet */ int drives_exclusive; /* burn_preset_device_open() param exclusive */ int drives_access; /* 0=readonly , 1=unrestricted */ int linux_scsi_dev_family; /* 0= default, 1= sr, 2= scd, (3= st), 4= sg */ int early_stdio_test; /* For burn_allow_drive_role_4(): bit1= Test whether a stdio drive can be opened for read-write resp. read-only resp. write only. bit2= Classify files which cannot be opened at all as role 0 : useless dummy. bit3= Classify non-empty role 5 drives as BURN_DISC_APPENDABLE with NWA after the end of the file. It is nevertheless possible to change this address by call burn_write_opts_set_start_byte(). */ int cache_num_tiles; /* -data_cache_size */ int cache_tile_blocks; int cache_default; /* bit0= cache_num_tiles, bit1= cache_tile_blocks */ int do_calm_drive; /* bit0= calm down drive after acquiring it */ char indev[SfileadrL]; void *in_drive_handle; /* interpreted only by libburnia oriented modules */ void *in_volset_handle; /* interpreted only by libburnia oriented modules */ char *in_charset; /* The charset to interpret the filename bytes */ int indev_is_exclusive; int indev_access; /* see drives_access */ char indev_off_adr[SfileadrL]; /* Result of burn_drive_convert_fs_adr(indev) when indev gets acquired. */ time_t isofs_st_out; /* A time point at least 1 second before image composition began. To be stored with image as xattr "isofs.st". */ time_t isofs_st_in; /* That time point as read from "isofs.st" of the loaded image. */ int volset_change_pending; /* whether -commit would make sense 0= no change pending 1= change pending 2= change pending, but -as misofs -print-size was performed on the changed image model 3= change pending, but the attempt to write it failed */ int commit_attempts; /* For Xorriso_change_is_pending bit1 */ int print_size_attempts; /* For Xorriso_change_is_pending bit1 */ int write_session_counter; /* For Xorriso_change_is_pending bit1 */ int print_size_counter; /* For Xorriso_change_is_pending bit1 */ int no_volset_present; /* set to 1 on first failure */ struct CheckmediajoB *check_media_default; int check_media_bad_limit; /* values defined as Xorriso_read_quality_* */ struct SectorbitmaP *in_sector_map; /* eventual sector validity bitmap */ char outdev[SfileadrL]; void *out_drive_handle; /* interpreted only by xorrisoburn.c */ char *out_charset; /* The charset to produce the filename bytes for */ int dev_fd_1; /* The fd which substitutes for /dev/fd/1 and is connected to externaly perveived stdout. */ int outdev_is_exclusive; int outdev_access; /* see drives_access */ char outdev_off_adr[SfileadrL]; /* Result of burn_drive_convert_fs_adr(outdev) when outdev gets acquired. */ off_t grow_blindly_msc2; /* if >= 0 this causes growing from drive to drive. The value is used as block address offset for image generation. Like in: mkisofs -C msc1,msc2 */ int ban_stdio_write; int do_dummy; int do_close; int auto_close; /* Whether to let do_close depend on media state */ int write_speed; /* Write speed in libburn units : 1000 bytes/second , 0 = Max, -1 = Min, -2= do not set */ int read_speed; /* Read speed. See above */ int read_speed_force; /* >0 : use burn_nominal_slowdown() in Xorriso_check_interval() */ int read_speed_corr; /* parameter max_corr for burn_nominal_slowdown */ int fs; /* fifo size in 2048 byte chunks : at most 1 GB */ int padding; /* number of bytes to add after ISO 9660 image */ int do_padding_by_libisofs; /* 0= by libburn , 1= by libisofs */ int alignment; /* if > 0 : output size alignment in 2048 byte blocks. This is always done by libburn, i.e. attached outside the image. Eventual inner alignment of the image end happens first. */ int do_stream_recording; /* 0=no, 1=yes, 2=for data, not for dir >=16 means yes with number as start LBA */ int dvd_obs; /* DVD write chunk size: 0, 32k or 64k */ int do_obs_pad; /* 1= pad up end on all media types */ int bdr_obs_exempt; /* 1= do not unconditionally apply do_obs_pad=1 to BD-R if not stream recording */ int modesty_on_drive; /* "enable" of burn_drive_set_buffer_waiting() 0= disable , 1= enable waiting , (-1 = do not change setting) */ int min_buffer_usec; /* The other parameters for this function */ int max_buffer_usec; int buffer_timeout_sec; int min_buffer_percent; int max_buffer_percent; int use_immed_bit; /* 1= on , 0= default , -1 = off */ int use_immed_bit_default; /* 1= on , 0= yet undefined , -1 = off */ int stdio_sync; /* stdio fsync interval: -1, 0, >=32 */ int stdio_sync_is_default; /* 1= is still default , 0= has been set */ int keep_boot_image; char boot_image_cat_path[SfileadrL]; int boot_image_cat_hidden; /* bit0= hidden in ISO/RR , bit1= in Joliet , bit2= in HFS+ */ int boot_count; /* number of already attached boot images */ char boot_image_bin_path[SfileadrL]; char boot_image_bin_form[16]; int boot_platform_id; int patch_isolinux_image; /* bit0= boot-info-table , bit1= not with EFI bit2-7= Mentioning in isohybrid GPT 1=EFI, 2=HFS+ bit8= Mention in isohybrid Apple Partition Map bit9= GRUB2 boot provisions (patch at byte 1012) */ int boot_image_emul; /* 0=no emulation 1=emulation as hard disk 2=emulation as floppy */ int boot_emul_default; /* 1= boot_image_emul is still default */ off_t boot_image_load_size; int boot_img_size_default; /* 1= boot_image_load_size is still default */ int boot_img_full_size; /* 1= override boot_image_load_size by image size */ unsigned char boot_id_string[29]; unsigned char boot_selection_crit[21]; int boot_image_isohybrid; /* 0=off , deprecated: 1=auto , 2=on , 3=force */ int boot_efi_default; /* 0= no effect , 1= apply --efi-boot parameters when attaching to img */ char system_area_disk_path[SfileadrL]; int system_area_clear_loaded; int system_area_options; /* bit0= "GRUB protective msdos label" (a simple partition table) bit1= isohybrid boot image pointer and partition table bit2-7= System area type 0= with bit0 or bit1: MBR else: unspecified type 1= MIPS Big Endian Volume Header 2= MIPS Little Endian Boot Block 3= SUN Disk Label for SUN SPARC 4= HP-PA PALO boot sector header version 4 5= HP-PA PALO boot sector header version 5 bit8-9= Only with System area type 0 Cylinder alignment mode 0 = auto (align if bit1) 1 = always align 2 = never align 3 = align external partitions bit10-13= System area sub type With type 0 = MBR: Gets overridden by bit0 and bit1. 0 = no particular sub type 1 = CHRP: A single MBR partition of type 0x96 covers the ISO image. Not compatible with any other feature which needs to have own MBR partition entries. 2 = generic MBR bit14= Only with System area type 0 GRUB2 boot provisions: Patch system area at byte 92 to 99 with 512-block address + 1 of the first boot image file. Little-endian 8-byte. Should be combined with options bit0. bit15= Only with MBR: Enforce MBR "bootable/active" flag. In worst case by dummy partition of type 0x00 which occupies block 0. */ int patch_system_area; /* Bits as of system_area_options. to be applied to the loaded system area of the image, if no system_area_disk_path and no system_area_options are set. */ /* The number of unclaimed 2K blocks before start of partition 1 as of the MBR in system area. If not 0 this will cause double volume descriptor sets and double tree. */ uint32_t partition_offset; /* Partition table parameter: 1 to 63, 0= disabled/default */ int partition_secs_per_head; /* 1 to 255, 0= disabled/default */ int partition_heads_per_cyl; /* Disk file paths of content of PreP partition and EFI system partition */ char prep_partition[SfileadrL]; char efi_boot_partition[SfileadrL]; /* Path and type of image files to be appended as MBR partitions */ char *appended_partitions[Xorriso_max_appended_partitionS]; uint8_t appended_part_types[Xorriso_max_appended_partitionS]; uint8_t appended_part_type_guids[Xorriso_max_appended_partitionS][16]; /* Flags in case that appended partitions show up in GPT: bit0= appended_part_type_guids is valid */ uint8_t appended_part_gpt_flags[Xorriso_max_appended_partitionS]; /* If 1: With appended partitions: create protective MBR and mark by GPT */ int appended_as_gpt; /* If 1: With appended partitions: mark by APM */ int appended_as_apm; /* If 1: Apply isohybrid gestures to non-isohybrid situations */ int part_like_isohybrid; /* MBR partition type of ISO filesystem partition or protective partition if not real GPT or CHRP. */ int iso_mbr_part_type; uint8_t iso_gpt_type_guid[16]; int iso_mbr_part_flag; /* Flags in case that the partition table is GPT: bit0= iso_gpt_type_guid is valid */ /* See libisoburn.h isoburn_igopt_set_gpt_guid() */ uint8_t gpt_guid[16]; int gpt_guid_mode; /* See libisoburn.h isoburn_igopt_set_max_ce_entries() */ uint32_t max_ce_entries; uint32_t max_ce_entries_flag; /* Eventual name of the non-ISO aspect of the image. E.g. SUN ASCII label. */ char ascii_disc_label[Xorriso_disc_label_sizE]; /* A data file of which the position and size shall be written after a SUN Disk Label. */ char grub2_sparc_core[SfileadrL]; /* HFS+ image serial number. 00...00 means that it shall be generated by libisofs. */ uint8_t hfsp_serial_number[8]; /* Allocation block size of HFS+ and APM : 0= auto , 512, or 2048 */ int hfsp_block_size; int apm_block_size; /* User settable PVD time stamps */ time_t vol_creation_time; time_t vol_modification_time; time_t vol_expiration_time; time_t vol_effective_time; /* To eventually override vol_modification_time by unconverted string and timezone 0 */ char vol_uuid[17]; /* To flatly set all file timestamps to the same value Special value: "set_to_mtime", see -find action "set_to_mtime" */ char all_file_dates[80]; /* Timestamp for ISO files which have no origin file on disk and thus normally get time(NULL) as timestamps */ int do_override_now_time; time_t now_time_override; #ifdef Xorriso_with_libjtE /* Parameters and state of Jigdo Template Export environment */ struct libjte_env *libjte_handle; #endif /* List of -jigdo parameters since the most recent -jigdo clear */ struct Xorriso_lsT *jigdo_params; struct Xorriso_lsT *jigdo_values; int libjte_params_given; /* bits: 0= outfile , 1= verbosity , 2= template_path 3= jigdo_path , 4= checksum_path 5= min_size , 6= checksum_iso 7= checksum_template , 8= compression 9= exclude , 10= demand_checksum 11= mapping , 12= checksum_algorithm */ /* LBA of boot image after image loading */ off_t loaded_boot_bin_lba; /* Path of the catalog node after image loading */ char loaded_boot_cat_path[SfileadrL]; /* XORRISO options */ int allow_graft_points; /* if not zero: yes special options: bit1= unescape '\\' too, continue unescaping after first real '=' */ int allow_restore; /* -2=disallowed until special mode "unblock" -1=permanently disallowed 0=disallowed, 1=allowed, 2=device files allowed */ int do_concat_split; /* 1= restore complete split file directories as regular files */ int do_auto_chmod; /* 1= eventually temporarily open access permissions of self-owned directories during restore */ int do_restore_sort_lba; /* 1= restore via node_array rather than via tree traversal. Better read performance, no directory mtime restore, needs do_auto_chmod */ int do_strict_acl; /* bit0= do not tolerate inappropriate presence or absence of directory "default" ACL */ int mount_opts_flag; /* bit0= "shared" = not "exclusive" Try to emit non-exclusive mount command. Do not give up drives. Linux: use loop device even on block devices in order to circumvent the ban to mount a device twice (with different sbsector=) FreeBSD: ? */ int dialog; /* 0=off , 1=single-line , 2=multi-line */ struct Xorriso_lsT *buffered_dialog; /* If not NULL : read by dialog */ int bsl_interpretation; /* whether to run input through Sfile_bsl_interpreter(): bit0-1= dialog and quoted file reading 0= no interpretation, leave unchanged 1= only inside double quotes 2= outside single quotes 3= everywhere bit2-3= reserved as future expansion of bit0-1 bit4= interpretation within program start arguments bit5= perform backslash encoding with results bit6= perform backslash encoding with info texts */ int sh_style_result; /* Whether not to wrap into quotation marks the file addresses reported by: pwd pwdx ls lsd lsl lsdl lsx lsdx lslx lsdlx du dus dux dusx findx find and to make du* numbers left adjusted, and not to append "/" to pwd* */ /* Pattern matching facility. It still carries legacy from scdbackup/askme.c but is fully functional for xorriso. */ int search_mode; /* 0= start text 1= fgrep , 2= regular expression 3= (eventually structured) shell parser expression 4= shell parser expression for leaf name */ int structured_search; /* 0= flat text search 1= '/' is a significant separator that cannot be matched by wildcards ( 2= like 1 : but report only occurrence in tree, no payload, no location ) ( 3= like 2 : but report first content level of matching directories ) 4= actually not structured but unique find mode (with search_mode 4) */ int do_iso_rr_pattern; /* 0=off, 1=on, 2=ls */ int do_disk_pattern; /* 0=off, 1=on, 2=ls */ int temp_mem_limit; off_t file_size_limit; int file_name_limit; struct ExclusionS *disk_exclusions; int disk_excl_mode; /* bit0= on (else off) bit1= parameter too (else recursion only) bit2= whole subtree banned (else only exact path) bit3= when comparing ignore excluded files rather than to treat them as truly missing on disk */ struct ExclusionS *iso_rr_hidings; struct ExclusionS *joliet_hidings; struct ExclusionS *hfsplus_hidings; int use_stdin; /* use raw stdin even if readline support is compiled */ int tolerate_stdin_eof; /* Do not abort on EOF in Xorriso_dialog_input but rather return -2. */ int result_page_length; int result_page_width; char mark_text[SfileadrL]; /* ( stdout+stderr, M: ) */ int packet_output; char logfile[4][SfileadrL]; FILE *logfile_fp[4]; FILE *pktlog_fp; FILE *stderr_fp; struct Xorriso_lsT *result_msglists[Xorriso_max_outlist_stacK]; struct Xorriso_lsT *info_msglists[Xorriso_max_outlist_stacK]; int msglist_flags[Xorriso_max_outlist_stacK]; /* bit0= result is redirected bit1= info is redirected */ int msglist_stackfill; int lib_msg_queue_lock_ini; int result_msglists_lock_ini; pthread_mutex_t lib_msg_queue_lock; pthread_mutex_t result_msglists_lock; int write_to_channel_lock_ini; pthread_mutex_t write_to_channel_lock; int msg_watcher_lock_ini; pthread_mutex_t msg_watcher_lock; int msg_watcher_state; /* 0= inactive 1= registered 2= started 3= request to end */ int (*msgw_result_handler)(void *handle, char *text); void *msgw_result_handle; int (*msgw_info_handler)(void *handle, char *text); void *msgw_info_handle; int msgw_stack_handle; int msgw_msg_pending; /* 0=no, 1=fetching(i.e. maybe) , 2=yes */ int msgw_fetch_lock_ini; pthread_mutex_t msgw_fetch_lock; struct Xorriso_msg_sievE *msg_sieve; int msg_sieve_disabled; int status_history_max; /* for -status long_history */ /* 0= no logging of SCSI commands, 1= to stderr */ int scsi_log; char report_about_text[20]; int report_about_severity; int library_msg_direct_print; char abort_on_text[20]; int abort_on_severity; /* A severity rank number as threshold */ int abort_on_is_default; /* will be set to 0 by first -abort_on */ int problem_status; /* Severity rank number. 0= no abort condition present */ char problem_status_text[20]; int problem_status_lock_ini; pthread_mutex_t problem_status_lock; char errfile_log[SfileadrL]; /* for -errfile_log */ int errfile_mode; /* bit0= marked */ FILE *errfile_fp; int img_read_error_mode; /* 0=best_effort , 1=failure , 2=fatal */ int extract_error_mode; /* 0=best_effort , 1=keep , 2=delete */ char return_with_text[20]; int return_with_severity; int return_with_value; int eternal_problem_status; char eternal_problem_status_text[20]; /* temporary search facilities */ regex_t *re; regmatch_t match[1]; char **re_constants; int re_count; int re_fill; char reg_expr[2*SfileadrL]; /* run state */ int run_state; /* 0=preparing , 1=writing image */ int is_dialog; int bar_is_fresh; char pending_option[SfileadrL]; /* eventual option entered at page prompt */ int request_to_abort; /* abort a single operation like -ls, not the program */ int request_not_to_ask; /* suppress reassure and pager */ double idle_time; int re_failed_at; /* mismatch position with structured_search */ int prepended_wd; double insert_count; double insert_bytes; double error_count; /* double will not roll over */ int launch_frontend_banned; /* pacifiers */ int pacifier_style; /* 0= xorriso, 1=mkisofs 2=cdrecord */ double pacifier_interval; double start_time; double last_update_time; /* optional global counters for brain reduced callback functions */ off_t pacifier_count; off_t pacifier_total; off_t pacifier_byte_count; /* auxiliary counter for data bytes */ off_t pacifier_prev_count; /* internal counter for speed measurement */ void *pacifier_fifo; int find_compare_result; /* 1=everything matches , 0=mismatch , -1=error */ int find_check_md5_result; /* bit0= seen mismatch bit1= seen error bit2= seen data file without MD5 bit3= seen match */ int find_unique_trunc_result; /* 0= some names are not uniquely truncatable 1= all names are uniquely truncatable 2= no names need truncation */ double last_abort_file_time; /* most recent check for aborting -check_md5 */ /* Tree node collection and LBA sorting facility */ int node_counter; int node_array_size; void **node_array; struct Xorriso_lsT *node_disk_prefixes; struct Xorriso_lsT *node_img_prefixes; /* Hardlink matching at restore time memorizes hardlink target paths. Array of nodes sorted by LBA. */ int hln_count; void **hln_array; void **hln_targets; int hln_change_pending; /* whether a change was made since hln creation */ /* >>> this should count all temp_mem and thus change its name */ off_t node_targets_availmem; /* Hardlink matching at update time: Array of all nodes in the tree, sorted by disk dev,ino. Bitmap of nodes which possibly got new hardlink siblings. List of involved disk-iso path pairs. */ int di_count; void **di_array; char *di_do_widen; struct Xorriso_lsT *di_disk_paths; struct Xorriso_lsT *di_iso_paths; struct PermiteM *perm_stack; /* Temporarily altered dir access permissions */ /* bit0= update_merge active: mark all newly added nodes as visited+found */ int update_flags; /* For find jobs show_hfs_cmd , show_hfs_as_mkisofs */ /* bit0= do not record but only count bit1= as_mkisofs mode */ int show_hfs_cmd_flag; int show_hfs_cmd_count; char **show_hfs_cmds; /* Extraction to sparse files */ off_t sparse_min_gap; /* result (stdout, R: ) */ char result_line[10*SfileadrL]; int result_line_counter; int result_page_counter; int result_open_line_len; /* info (stderr, I:) */ char info_text[10*SfileadrL]; /* info return mode for isoburn_read_iso_head_v2() with -toc: 1= volume id 3= creation time 4= modification type */ int toc_info_type; /* How to present toc_info_type 2 and 3: 0= in ECMA-119 format YYYYMMDDhhmmsscc rectified to GMT 1= in xorriso format YYY.MM.DD.hhmmss in local time */ int toc_time_form; }; #include "base_obj.h" #include "aux_objects.h" #include "findjob.h" #include "check_media.h" #include "misc_funct.h" #include "text_io.h" #include "match.h" #include "emulators.h" #include "disk_ops.h" #include "cmp_update.h" #include "parse_exec.h" #endif /* Xorriso_private_includeD */ #define Xorriso_timestamP "2024.09.14.174800" /* Adapter to libisoburn, libisofs and libburn for xorriso, a command line oriented batch and dialog tool which creates, loads, manipulates and burns ISO 9660 filesystem images. Copyright 2007-2024 Thomas Schmitt, Provided under GPL version 2 or later. This file contains the inner isofs- and burn-library interface of xorriso. */ #ifndef Xorrisoburn_includeD #define Xorrisoburn_includeD yes /* The minimum version of libisoburn to be used with this version of xorriso */ #define xorriso_libisoburn_req_major 1 #define xorriso_libisoburn_req_minor 5 #define xorriso_libisoburn_req_micro 7 struct SpotlisT; /* List of intervals with different read qualities */ struct CheckmediajoB; /* Parameters for Xorriso_check_media() */ struct Xorriso_msg_sievE; /* Fishes for info particles in reply messages */ int Xorriso_startup_libraries(struct XorrisO *xorriso, int flag); /* @param flag bit0= global shutdown of libraries */ int Xorriso_detach_libraries(struct XorrisO *xorriso, int flag); int Xorriso_create_empty_iso(struct XorrisO *xorriso, int flag); /* @param flag bit0=acquire as isoburn input drive bit1=acquire as libburn output drive (as isoburn drive if bit0) @return <=0 failure , 1=success , 2=neither readable or writeable */ int Xorriso_aquire_drive(struct XorrisO *xorriso, char *adr, char *show_adr, int flag); int Xorriso_give_up_drive(struct XorrisO *xorriso, int flag); off_t Xorriso_write_session(struct XorrisO *xorriso, int flag); int Xorriso_retry_write_session(struct XorrisO *xorriso, int flag); /* @param boss_iter Opaque handle to be forwarded to actions in ISO image Set to NULL if calling this function from outside ISO world @param flag bit0= mkdir: graft in as empty directory, not as copy from disk bit1= do not report added files @return <=0 = error , 1 = added simple node , 2 = added directory */ int Xorriso_graft_in(struct XorrisO *xorriso, void *boss_iter, char *disk_path, char *img_path, off_t offset, off_t cut_size, int flag); int Xorriso__text_to_sev(char *severity_name, int *severity_number,int flag); int Xorriso__sev_to_text(int severity, char **severity_name, int flag); /* @param flag bit0=report about output drive bit1=short report form bit2=do not try to read ISO heads bit3=report to info channel (else to result channel) */ int Xorriso_toc(struct XorrisO *xorriso, int flag); /* @param flag bit0= no output if no boot record was found bit3= report to info channel (else to result channel) */ int Xorriso_show_boot_info(struct XorrisO *xorriso, int flag); int Xorriso_show_devices(struct XorrisO *xorriso, int flag); int Xorriso_tell_media_space(struct XorrisO *xorriso, off_t *media_space, off_t *free_space, int flag); /* @param flag bit0=fast , bit1=deformat @return 0=failure, did not touch medium , -1=failure, altered medium 1=success, altered medium , 2=success, did not touch medium */ int Xorriso_blank_media(struct XorrisO *xorriso, int flag); /* @param flag bit0= try to achieve faster formatting bit1= use parameter size (else use default size) bit2= do not re-aquire drive bit7= by_index mode: bit8 to bit15 contain the index of the format to use. @return 0=failure, did not touch medium , -1=failure, altered medium 1=success, altered medium , 2=success, did not touch medium */ int Xorriso_format_media(struct XorrisO *xorriso, off_t size, int flag); /* @return <=0 error, 1 success */ int Xorriso_list_formats(struct XorrisO *xorriso, int flag); /* @return <=0 error, 1 success */ int Xorriso_list_speeds(struct XorrisO *xorriso, int flag); /* @param flag bit1= obtain outdrive, else indrive @return <=0 error, 1 success */ int Xorriso_list_profiles(struct XorrisO *xorriso, int flag); /* @param flag bit2= formatting rather than blanking @return 0=failure, did not touch medium , -1=failure, altered medium 1=success, altered medium , 2=success, did not touch medium */ int Xorriso_blank_as_needed(struct XorrisO *xorriso, int flag); /* @param boss_iter Opaque internal handle. Use NULL outside xorrisoburn.c : If not NULL then this is an iterator suitable for iso_dir_iter_remove() which is then to be used instead of iso_node_remove(). @param flag bit0= remove whole sub tree: rm -r bit1= remove empty directory: rmdir bit2= recursion: do not reassure in mode 2 "tree" bit3= this is for overwriting and not for plain removal bit4= count deleted files in xorriso->pacifier_count bit5= with bit0 only remove directory content, not the directory bit6= do not delete eventually existing node from di_array @return <=0 = error 1 = removed simple node 2 = removed directory or tree 3 = did not remove on user revocation */ int Xorriso_rmi(struct XorrisO *xorriso, void *boss_iter, off_t boss_mem, char *path, int flag); /* @param flag bit0= long format bit1= do not print count of nodes bit2= du format bit3= print directories as themselves (ls -d) */ int Xorriso_ls_filev(struct XorrisO *xorriso, char *wd, int filec, char **filev, off_t boss_mem, int flag); /* This function needs less buffer memory than Xorriso_ls_filev() but cannot perform structured pattern matching. @param flag bit0= long format bit1= only check for directory existence bit2= do not apply search pattern but accept any file bit3= just count nodes and return number */ int Xorriso_ls(struct XorrisO *xorriso, int flag); /* @return: <=0 = error, 1= directory tree, 2= path leads to non-directory */ int Xorriso_get_dus(struct XorrisO *xorriso, char *iso_rr_path, off_t *size, off_t boss_mem, int flag); /* @param wd Path to prepend in case img_path is not absolute @param img_path Absolute or relative path to be normalized @param eff_path returns resulting effective path. Must provide at least SfileadrL bytes of storage. @param flag bit0= do not produce problem events (unless faulty path format) bit1= work purely literally, do not use libisofs bit2= (with bit1) this is an address in the disk world @return -1 = faulty path format, 0 = not found , 1 = found simple node , 2 = found directory */ int Xorriso_normalize_img_path(struct XorrisO *xorriso, char *wd, char *img_path, char eff_path[], int flag); /* @param boss_iter Opaque handle to be forwarded to actions in ISO image Set to NULL if calling this function from outside ISO world @param flag bit0= silently ignore attempt of renaming to same path and return 2 */ int Xorriso_rename(struct XorrisO *xorriso, void *boss_iter, char *origin, char *dest, int flag); /* @param flag bit0= do not produce info message on success @return 1=success, 0=was already directory, -1=was other type, -2=bad path */ int Xorriso_mkdir(struct XorrisO *xorriso, char *img_path, int flag); /* @param flag bit0= a match count !=1 is a SORRY event */ int Xorriso_expand_pattern(struct XorrisO *xorriso, int num_patterns, char **patterns, int extra_filec, int *filec, char ***filev, off_t *mem, int flag); int Xorriso_set_st_mode(struct XorrisO *xorriso, char *path, mode_t mode_and, mode_t mode_or, int flag); int Xorriso_set_uid(struct XorrisO *xorriso, char *in_path, uid_t uid, int flag); int Xorriso_set_gid(struct XorrisO *xorriso, char *in_path, gid_t gid, int flag); /* @parm flag bit0= atime, bit1= ctime, bit2= mtime, bit8=no auto ctime */ int Xorriso_set_time(struct XorrisO *xorriso, char *in_path, time_t t, int flag); /* @param flag bit0= recursion bit1= do not count deleted files with rm and rm_r */ int Xorriso_findi(struct XorrisO *xorriso, struct FindjoB *job, void *boss_iter, off_t boss_mem, void *dir_node_generic, char *dir_path, struct stat *dir_stbuf, int depth, int flag); /* @param flag bit0= do not dive into trees bit1= do not perform job->action on resulting node array */ int Xorriso_findi_sorted(struct XorrisO *xorriso, struct FindjoB *job, off_t boss_mem, int filec, char **filev, int flag); /* @param flag bit0= do not mark image as changed */ int Xorriso_set_volid(struct XorrisO *xorriso, char *volid, int flag); int Xorriso_get_volid(struct XorrisO *xorriso, char volid[33], int flag); int Xorriso_set_abort_severity(struct XorrisO *xorriso, int flag); int Xorriso_report_lib_versions(struct XorrisO *xorriso, int flag); /* @return 0= stbuf content is valid , -1 = path not found , -2 = severe error occurred */ int Xorriso_iso_lstat(struct XorrisO *xorriso, char *path, struct stat *stbuf, int flag); /* @param flag bit0= -inq bit1= -checkdrive */ int Xorriso_atip(struct XorrisO *xorriso, int flag); /* @param write_start_address is valid if >=0 @param tsize is valid if >0 @param flag bit0= grow_overwriteable_iso bit1= do_isosize */ int Xorriso_burn_track(struct XorrisO *xorriso, off_t write_start_address, char *track_source, off_t tsize, int flag); int Xorriso_retry_burn_track(struct XorrisO *xorriso, off_t write_start_address, char *track_source, off_t tsize, int flag); /* @param flag bit1= outdev rather than indev @return <=0 = failure , 1= ok , 2= ok, is CD profile */ int Xorriso_get_profile(struct XorrisO *xorriso, int *profile_number, char profile_name[80], int flag); /* @param flag bit0= open IsoNode *node_pt rather than looking up pathname bit1= dig out the most original stream for reading */ int Xorriso_iso_file_open(struct XorrisO *xorriso, char *pathname, void *node_pt, void **stream, int flag); int Xorriso_iso_file_read(struct XorrisO *xorriso, void *stream, char *buf, int count, int flag); int Xorriso_iso_file_close(struct XorrisO *xorriso, void **stream, int flag); int Xorriso_iso_file_to_fd(struct XorrisO *xorriso, char *path, int fd, int flag); /* @param bit0= copy link target properties rather than link properties */ int Xorriso_copy_properties(struct XorrisO *xorriso, char *disk_path, char *img_path, int flag); int Xorriso_cut_out(struct XorrisO *xorriso, char *disk_path, off_t startbyte, off_t bytecount, char *iso_rr_path, int flag); int Xorriso_paste_in(struct XorrisO *xorriso, char *disk_path, off_t startbyte, off_t bytecount, char *iso_rr_path, int flag); struct SplitparT; /* @param flag bit0= in_node is valid, do not resolve iso_adr */ int Xorriso_identify_split(struct XorrisO *xorriso, char *iso_adr, void *in_node, struct SplitparT **parts, int *count, struct stat *total_stbuf, int flag); /* @param flag bit0= node is valid, do not resolve path bit1= insist in complete collection of part files */ int Xorriso_is_split(struct XorrisO *xorriso, char *path, void *node, int flag); /* @param flag >>> bit0= mkdir: graft in as empty directory, not as copy from iso bit1= do not report copied files bit2= -follow, -not_*: this is not a command parameter bit3= use offset and cut_size for -paste_in bit4= return 3 on rejection by exclusion or user bit5= if directory then do not add sub tree bit6= this is a copy action: do not fake times and ownership @return <=0 = error , 1 = added leaf file object , 2 = added directory , 3 = rejected */ int Xorriso_restore(struct XorrisO *xorriso, char *img_path, char *disk_path, off_t offset, off_t cut_size, int flag); /* @param flag bit0= in_node is valid, do not resolve img_path */ int Xorriso_restore_is_identical(struct XorrisO *xorriso, void *in_node, char *img_path, char *disk_path, char type_text[5], int flag); /* Return the official libburn address of an address string. This may fail if the string does not constitute a valid drive address. @param official_adr must offer SfileadrL bytes of reply buffer @return 1 = success , 0 = failure , -1 = severe error */ int Xorriso_libburn_adr(struct XorrisO *xorriso, char *address_string, char official_adr[], int flag); /* @param flag bit1= obtain info from outdev */ int Xorriso_msinfo(struct XorrisO *xorriso, off_t *msc1, off_t *msc2, int flag); /* @param flag bit0= obtain iso_lba from indev bit1= head_buffer already contains a valid head bit2= issue message about success bit3= check whether source blocks are banned by in_sector_map */ int Xorriso_update_iso_lba0(struct XorrisO *xorriso, off_t iso_lba, off_t isosize, char *head_buffer, struct CheckmediajoB *job, int flag); int Xorriso_truncate_overwritable(struct XorrisO *xorriso, char *adr_mode, char *adr_value, char *adjust, int flag); int Xorriso_get_local_charset(struct XorrisO *xorriso, char **name, int flag); int Xorriso_set_local_charset(struct XorrisO *xorriso, char *name, int flag); int Xorriso_destroy_node_array(struct XorrisO *xorriso, int flag); int Xorriso_destroy_hln_array(struct XorrisO *xorriso, int flag); int Xorriso_destroy_di_array(struct XorrisO *xorriso, int flag); int Xorriso_new_node_array(struct XorrisO *xorriso, off_t mem_limit, int addon_nodes, int flag); int Xorriso_sort_node_array(struct XorrisO *xorriso, int flag); int Xorriso_new_hln_array(struct XorrisO *xorriso, off_t mem_limit, int flag); /* @param flag bit0= allocate xorriso->node_targets too */ int Xorriso_restore_node_array(struct XorrisO *xorriso, int flag); int Xorriso_check_md5(struct XorrisO *xorriso, void *in_node, char *path, int flag); int Xorriso_check_session_md5(struct XorrisO *xorriso, char *severity, int flag); int Xorriso_image_has_md5(struct XorrisO *xorriso, int flag); int Xorriso_check_media(struct XorrisO *xorriso, struct SpotlisT **spotlist, struct CheckmediajoB *job, int flag); int Xorriso_extract_cut(struct XorrisO *xorriso, char *img_path, char *disk_path, off_t img_offset, off_t bytes, int flag); int Xorriso_extract_boot_images(struct XorrisO *xorriso, char *disk_dir_path, int flag); int Xorriso_relax_compliance(struct XorrisO *xorriso, char *mode, int flag); /* @return 1=ok 2=ok, is default setting */ int Xorriso_get_relax_text(struct XorrisO *xorriso, char mode[1024], int flag); /** @param flag bit0= print mount command to result channel rather than performing it */ int Xorriso_mount(struct XorrisO *xorriso, char *dev, int adr_mode, char *adr_value, char *cmd, int flag); int Xorriso_auto_driveadr(struct XorrisO *xorriso, char *adr, char *result, int flag); /* @param node Opaque handle to IsoNode which is to be inquired instead of path if it is not NULL. @param path is used as address if node is NULL. @param acl_text if acl_text is not NULL, then *acl_text will be set to the ACL text (without comments) of the file object. In this case it finally has to be freed by the caller. @param flag bit0= do not report to result but only retrieve ACL text bit1= just check for existence of ACL, do not allocate and set acl_text but return 1 or 2 @return 2 ok, no ACL available, eventual *acl_text will be NULL 1 ok, ACL available, eventual *acl_text stems from malloc() <=0 error */ int Xorriso_getfacl(struct XorrisO *xorriso, void *node, char *path, char **acl_text, int flag); int Xorriso_report_acl_header(struct XorrisO *xorriso, char *path, uid_t uid, gid_t gid, int flag); int Xorriso_getfattr(struct XorrisO *xorriso, void *in_node, char *path, char **attr_text, int flag); int Xorriso_list_extattr(struct XorrisO *xorriso, void *in_node, char *path, char *show_path, char *mode, int flag); int Xorriso_append_extattr_comp(struct XorrisO *xorriso, char *comp, size_t comp_len, char *mode, int flag); /* Calls iso_image_set_ignore_aclea() according to xorriso->do_aaip */ int Xorriso_set_ignore_aclea(struct XorrisO *xorriso, int flag); /* @param node Opaque handle to IsoNode which is to be manipulated instead of path if it is not NULL. @param path is used as address if node is NULL. @param access_text "access" ACL in long text form @param default_text "default" ACL in long text form @param flag Unused yet, submit 0 @return >0 success , <=0 failure */ int Xorriso_setfacl(struct XorrisO *xorriso, void *in_node, char *path, char *access_text, char *default_text, int flag); int Xorriso_get_attrs(struct XorrisO *xorriso, void *in_node, char *path, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag); int Xorriso_setfattr(struct XorrisO *xorriso, void *in_node, char *path, size_t num_attrs, char **names, size_t *value_lengths, char **values, int flag); int Xorriso_perform_attr_from_list(struct XorrisO *xorriso, char *path, struct Xorriso_lsT *lst_start, int flag); int Xorriso_path_setfattr(struct XorrisO *xorriso, void *in_node, char *path, char *name, size_t value_length, char *value, int flag); int Xorriso_perform_acl_from_list(struct XorrisO *xorriso, char *file_path, char *uid, char *gid, char *acl, int flag); int Xorriso_record_dev_inode(struct XorrisO *xorriso, char *disk_path, dev_t dev, ino_t ino, void *in_node, char *iso_path, int flag); int Xorriso_local_getfacl(struct XorrisO *xorriso, char *disk_path, char **text, int flag); int Xorriso_set_filter(struct XorrisO *xorriso, void *in_node, char *path, char *filter_name, int flag); /* @param flag bit0= delete filter with the given name */ int Xorriso_external_filter(struct XorrisO *xorriso, char *name, char *options, char *path, int argc, char **argv, int flag); int Xorriso_status_extf(struct XorrisO *xorriso, char *filter, FILE *fp, int flag); int Xorriso_destroy_all_extf(struct XorrisO *xorriso, int flag); int Xorriso_show_stream(struct XorrisO *xorriso, void *in_node, char *path, int flag); int Xorriso_set_zisofs_params(struct XorrisO *xorriso, int flag); int Xorriso_status_zisofs(struct XorrisO *xorriso, char *filter, FILE *fp, int flag); /* @param flag bit0= overwrite existing di_array (else return 2) */ int Xorriso_make_di_array(struct XorrisO *xorriso, int flag); /* @param flag bit0= overwrite existing hln_array (else return 2) */ int Xorriso_make_hln_array(struct XorrisO *xorriso, int flag); /* @param flag bit2= -follow: this is not a command parameter @return -1= severe error 0= not applicable for hard links 1= go on with processing 2= iso_rr_path is fully updated */ int Xorriso_hardlink_update(struct XorrisO *xorriso, int *compare_result, char *disk_path, char *iso_rr_path, int flag); int Xorriso_finish_hl_update(struct XorrisO *xorriso, int flag); int Xorriso_get_md5(struct XorrisO *xorriso, void *in_node, char *path, char md5[16], int flag); int Xorriso_make_md5(struct XorrisO *xorriso, void *in_node, char *path, int flag); int Xorriso_md5_start(struct XorrisO *xorriso, void **ctx, int flag); int Xorriso_md5_compute(struct XorrisO *xorriso, void *ctx, char *data, int datalen, int flag); int Xorriso_md5_end(struct XorrisO *xorriso, void **ctx, char md5[16], int flag); /* @param flag bit0=input drive bit1=output drive */ int Xorriso_drive_snooze(struct XorrisO *xorriso, int flag); int Xorriso_is_plain_image_file(struct XorrisO *xorriso, void *in_node, char *path, int flag); int Xorriso_pvd_info(struct XorrisO *xorriso, int flag); /* @param flag bit0= do not set hln_change_pending */ int Xorriso_set_change_pending(struct XorrisO *xorriso, int flag); /* @param flag bit0= enable SCSI command logging to stderr */ int Xorriso_scsi_log(struct XorrisO *xorriso, int flag); /* flag bit0= do not increment boot_count and do not reset boot parameters bit1= dispose attached boot images */ int Xorriso_attach_boot_image(struct XorrisO *xorriso, int flag); /* bit0= do only report non-default settings bit1= do only report to fp */ int Xorriso_boot_image_status(struct XorrisO *xorriso, char *filter, FILE *fp, int flag); int Xorriso_add_mips_boot_file(struct XorrisO *xorriso, char *path, int flag); int Xorriso_set_hppa_boot_parm(struct XorrisO *xorriso, char *text, char *what, int flag); int Xorriso_set_alpha_boot(struct XorrisO *xorriso, char *path, int flag); int Xorriso_coordinate_system_area(struct XorrisO *xorriso, int sa_type, int options, char *cmd, int flag); /* A pseudo file type for El-Torito bootsectors as in man 2 stat : For now take the highest possible value. */ #define Xorriso_IFBOOT S_IFMT int Exprtest_match(struct XorrisO *xorriso, struct ExprtesT *ftest, void *node_pt, char *name, char *path, struct stat *boss_stbuf, struct stat *stbuf, int flag); int Xorriso_toc_to_string(struct XorrisO *xorriso, char **toc_text, int flag); int Xorriso_reaquire_outdev(struct XorrisO *xorriso, int flag); int Xorriso_set_system_area_path(struct XorrisO *xorriso, char *path, int flag); int Xorriso_set_hidden(struct XorrisO *xorriso, void *in_node, char *path, int hide_state, int flag); /* @param flag bit0= avoid library calls */ int Xorriso_preparer_string(struct XorrisO *xorriso, char xorriso_id[129], int flag); int Xorriso_jigdo_interpreter(struct XorrisO *xorriso, char *aspect, char *arg, int flag); int Xorriso_estimate_file_size(struct XorrisO *xorriso, struct FindjoB *job, char *basename, mode_t st_mode, off_t st_size, int flag); int Xorriso_test_outchar(struct XorrisO *xorriso, void *node_pt, char *in_name, int name_space, int flag); int Xorriso_clone_tree(struct XorrisO *xorriso, void *boss_iter, char *origin, char *dest, int flag); int Xorriso_clone_under(struct XorrisO *xorriso, char *origin, char *dest, int flag); int Xorriso_mark_update_merge(struct XorrisO *xorriso, char *path, void *node, int flag); /* @param flag bit0= asynchronous handling (else catch thread, wait, and exit) */ int Xorriso_set_signal_handling(struct XorrisO *xorriso, int flag); /* @param flag bit0=force burn_disc_close_damaged() */ int Xorriso_close_damaged(struct XorrisO *xorriso, int flag); int Xorriso_list_extras(struct XorrisO *xorriso, char *mode, int flag); int Xorriso_set_data_cache(struct XorrisO *xorriso, void *ropts, int num_tiles, int tile_blocks, int flag); int Xorriso_hfsplus_file_creator_type(struct XorrisO *xorriso, char *path, void *in_node, char *creator, char *hfs_type, int flag); int Xorriso_hfsplus_bless(struct XorrisO *xorriso, char *path, void *in_node, char *blessing, int flag); int Xorriso_pretend_full_disc(struct XorrisO *xorriso, int flag); int Xorriso_scsi_dev_family(struct XorrisO *xorriso, int flag); int Xorriso_use_immed_bit(struct XorrisO *xorriso, int flag); int Xorriso_apply_sort_file(struct XorrisO *xorriso, char *path, int flag); int Xorriso_report_system_area(struct XorrisO *xorriso, char *form, int flag); int Xorriso_list_boot_images(struct XorrisO *xorriso, char ***imgs, int *img_count, int flag); int Xorriso_external_filter_banned(struct XorrisO *xorriso, char *purpose, int flag); int Xorriso_set_file_name_limit(struct XorrisO *xorriso, int value, int flag); int Xorriso_truncate_path_comps(struct XorrisO *xorriso, char *path, char *buffer, char **resultpt, int flag); int Xorriso_graftable_pathspec(struct XorrisO *xorriso, char *in_pathspec, char *pathspec, int flag); int Xorriso_parse_guid(struct XorrisO *xorriso, char *text, uint8_t guid[16], int flag); int Xorriso_parse_gpt_guid(struct XorrisO *xorriso, char *text, int flag); int Xorriso_parse_type_guid(struct XorrisO *xorriso, char *text, uint8_t guid[16], int *mbr_type, int flag); int Xorriso_format_guid(struct XorrisO *xorriso, uint8_t guid[16], char *line, int flag); int Xorriso_make_guid(struct XorrisO *xorriso, char *line, int flag); int Xorriso_set_libisofs_now(struct XorrisO *xorriso, int flag); int Xorriso_obtain_indev_readsize(struct XorrisO *xorriso, off_t *blocks, int flag); int Xorriso_assess_written_features(struct XorrisO *xorriso, char *mode, int flag); /* Frontend to isoburn_conv_name_chars() */ int Xorriso_conv_name_chars(struct XorrisO *xorriso, char *name, char **result, size_t *result_len, int name_space, int flag); int Xorriso_warn_if_not_exist(struct XorrisO *xorriso, char *prefix, char *purpose, char *path, int flag); int Xorriso_warn_if_not_bootcat(struct XorrisO *xorriso, char *prefix, char *purpose, char *path, int flag); int Xorriso_decode_lfa_flags(struct XorrisO *xorriso, char *flags_text, uint64_t *lfa_flags, int flag); int Xorriso_encode_lfa_flags(struct XorrisO *xorriso, uint64_t lfa_flags, char **flags_text, int flag); int Xorriso_decode_chattr_arg(struct XorrisO *xorriso, char *lfa_text, uint64_t *lfa_flags, int *operator, int flag); int Xorriso_get_lfa_flags(struct XorrisO *xorriso, void *node, char *path, uint64_t *lfa_flags, int *max_bit, int flag); int Xorriso_set_lfa_flags(struct XorrisO *xorriso, void *in_node, char *path, char *lfa_text, uint64_t lfa_flags, int operator, int flag); int Xorriso_remove_all_lfa_flags(struct XorrisO *xorriso, int flag); int Xorriso_set_local_chattr_ia(struct XorrisO *xorriso, char *disk_path, int flag); #endif /* Xorrisoburn_includeD */ .\" Hey, EMACS: -*- nroff -*- .\" .\" IMPORTANT NOTE: .\" .\" The original of this file is kept in xorriso/xorrisofs.texi .\" This here was generated by program xorriso/make_xorriso_1 .\" .\" .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH XORRISOFS 1 "Version 1.5.7, Sep 05, 2024" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .nh .SH NAME xorrisofs \- Emulation of ISO 9660 program mkisofs by program xorriso .SH SYNOPSIS .B xorrisofs [ options ] [-o filename ] pathspec [pathspecs ...] .br .SH DESCRIPTION .PP \fBxorrisofs\fR produces Rock Ridge enhanced ISO 9660 filesystems and add\-on sessions to such filesystems. Optionally it can produce Joliet directory trees too. .br .PP \fBxorrisofs\fR understands options of program mkisofs from cdrtools by Joerg Schilling. Its implementation is part of program xorriso which shares no source code with cdrtools. .SS \fBISO 9660, Rock Ridge, Joliet, HFS+:\fR .br \fBISO 9660\fR (aka \fBECMA\-119\fR) is a read\-only filesystem that is mainly used for optical media CD, DVD, BD, but may also reside on other storage devices like disk files, USB sticks or disk partitions. It is widely readable by many operating systems and by boot facilities of personal computers. .br ISO 9660 describes directories and data files by very restricted filenames with no distinction of upper case and lower case. Its metadata do not comply to fundamental POSIX specifications. .br \fBRock Ridge\fR is the name of a set of additional information which enhance an ISO 9660 filesystem so that it can represent a POSIX compliant filesystem with ownership, access permissions, symbolic links, and other attributes. Rock Ridge allows filenames of up to 255 bytes and paths of up to 1024 bytes. .br xorrisofs produces Rock Ridge information by default. It is strongly discouraged to disable this feature. .br \fBJoliet\fR is the name of an additional directory tree which provides filenames up to 64 characters encoded as UTF\-16. A Joliet tree is mainly interesting for reading the ISO image by operating systems of Microsoft Corporation. Production of this directory tree may be enabled by option \-J. .br \fBISO 9660:1999\fR is the name of an additional directory tree which provides longer filenames. It allows single file names to have up to 207 characters. It might be of use with some older computer system boot facilities which read neither Rock Ridge nor Joliet but need longer filenames nevertheless. Production of this directory tree may be enabled by option \-iso\-level 4. .br \fBHFS+\fR is the name of a filesystem which is normally used for writing and reading on hard disks and similar devices. It is possible to embed a HFS+ partition into the emerging ISO 9660 image and to mark it by Apple Partition Map entries. This interferes with options which copy data into the first 32 KiB of the ISO image, like \-G or \-isohybrid\-mbr. See option \-hfsplus. .br The main purpose for having an embedded HFS+ partition is booting of certain models of Apple computers. .SS .B Inserting files into the ISO image: .PP \fBxorrisofs\fR deals with two kinds of file addresses: .br \fBdisk_path\fR is a path to an object in the local filesystem tree. .br \fBiso_rr_path\fR is the Rock Ridge address of a file object in the ISO image. If no Rock Ridge information shall be stored in an emerging ISO, then the names will get mapped to ISO 9660 names of limited length and character set. .br .PP A program argument is handled as a \fBpathspec\fR, if it is not recognized as original mkisofs option or additional \fBxorrisofs\fR option. A pathspec depicts an input file object by a disk_path. If option \-graft\-points is not present, then the behavior depends on the file type of disk_path. Directories get merged with the /\-directory of the ISO image. Files of other types get copied into the /\-directory. .br If \-graft\-points is present then each pathspec gets split at the first occurrence of the =\-character. The part before the = is taken as \fBtarget\fR, i.e. the iso_rr_path for the file object in the ISO image. The part after the first = is taken as \fBsource\fR, i.e. the disk_path of the input object. .br It is possible to make =\-characters part of the iso_rr_path by preceding them with a \\\-character. The same must be done for \\\-characters which shall be part of the iso_rr_path. .br .PP If the source part of the pathspec leads to a directory, then all files underneath this directory get inserted into the image, too. It is possible to exclude particular files from being inserted by help of option \-m. .br In case that target already exists, the following rules apply: Directories and other files may overwrite existing non\-directories. Directories get merged with existing directories. Non\-directories may not overwrite existing directories. .SS \fBRelation to program xorriso:\fR .br \fBxorrisofs\fR is actually a command mode of program \fBxorriso\fR, which gets entered either by xorriso command "\-as mkisofs" or by starting the program by one of the names "xorrisofs", "mkisofs", "genisoimage", or "genisofs". .br This command mode can be left by argument "\-\-" which leads to generic xorriso command mode. See \fBman xorriso\fR for its description. .br .PP xorriso performs image reading and writing by help of libburn, which is mainly intended for optical drives, but also operates on all POSIX file types except directories. .br The program messages call any image file a "drive". File types which are not supported for reading are reported as "blank". The reported free media space may be quite fictional. .br Nevertheless \fBxorrisofs\fR does not operate directly on optical drives, but rather forces libburn to regard them as general device files. So for writing of sequential optical media (CD, DVD\-R, DVD+R, BD\-R) one will have to use a burn program. E.g the cdrecord emulation of xorriso. See EXAMPLES. .SS .br .SH OPTIONS .br .PP .TP .B Image loading: .PP The following options control loading of an existing ISO image for the purpose of preparing a suitable add\-on session. If they are missing then a new image is composed from scratch. .TP \fB\-M\fR disk_path Set the path from which to load the existing ISO image directory tree on which to base the upcoming directory tree as add\-on session. The path must lead to a random\-access readable file object. On GNU/Linux: regular data files or block device files. .br A special kind of pseudo disk_path has the form "/dev/fd/"number. It depicts the open file descriptor with the given number, regardless whether the operating system supports this feature by file nodes in /dev/fd or not. E.g. /dev/fd/3 is file descriptor 3 which was opened by the program that later started xorriso. .TP \fB\-prev-session\fR disk_path Alias of \-M. .TP \fB\-dev\fR disk_path Alias of \-M. .TP \fB\-C\fR last_session_start,next_writeable_address Set the 2 KiB block address last_session_start from where to read the ISO image out of the file given by option \-M. .br Separated by a comma, set the next_writeable_address to which the add\-on session will finally be written. Decisive is actually the block address which the intended readers will have to use as superblock address on the intended medium. .br Both values can be inquired from optical media by help of burn programs and cdrecord option \-msinfo. xorriso itself can obtain it in its cdrecord emulation. .br values=$(xorriso \-as cdrecord dev=/dev/... \-msinfo) .br echo $values .br Option \-C may be used without option \-M to create an ISO image from scratch and prepare it for being finally written to a block address other than 0. Parameter last_session_start must then be set to 0. .TP \fB\-cdrecord-params\fR last_session_start,next_writeable_address Alias of \-C. .TP .B Settings for file insertion: .TP \fB\-path-list\fR disk_path Read pathspecs line\-by\-line from disk_file and insert the depicted file objects into the ISO image. If disk_path is "\-" then read the pathspecs from standard input. .TP \fB--quoted_path_list\fR disk_path Like option \-path\-list but reading quoted words rather than plain lines. Whitespace outside of quotes will be discarded. On the other hand it is possible to represent pathspecs which contain newline characters. .br The double quotation mark " and the single quotation mark ' can be used to enclose whitespace and make it part of pathspecs. Each mark type can enclose the marks of the other type. A trailing backslash \\ outside quotations or an open quotation cause the next input line to be appended. .TP \fB\-cut_out\fR disk_path byte_offset byte_count iso_rr_path Map a byte interval of a regular disk file or of a device file into a regular file in the ISO image. The file depicted by disk_path has to support random read access. .br byte_offset and byte_count may be plain numbers counting bytes, or numbers with appended letter "d", "s", "k", "m", "g" to count disk blocks (512 bytes), disc sectors (2048 bytes), KiB (1024 bytes), MiB (1024 KiB), or GiB (1024 MiB). .br E.g: .br \-cut_out bootable.iso 562s 18s /formerly_hidden_boot_image .TP \fB\-f\fR .br Resolve symbolic links on disk rather than storing them as symbolic links in the ISO image. .TP \fB\-follow-links\fR Alias of \-f. .TP \fB\-graft-points\fR Enable interpretation of input file pathspecs as combination of iso_rr_path and disk_path, separated by a =\-character. .TP \fB\-m\fR disk_pattern Exclude files from being inserted into the image. Silently ignored are those files of which the disk_path matches the given shell parser pattern. If no /\-character is part of the pattern, then it gets matched against the leaf name of the disk file. .br It is possible to give more than one \-m option. .TP \fB\-exclude\fR Alias of \-m. .TP \fB\-x\fR .br Alias of \-m. .TP \fB\-old-exclude\fR Alias of \-m. .TP \fB\-exclude-list\fR disk_path Perform \-m using each line out of file disk_path as argument disk_pattern. .TP \fB\-z\fR .br Enable recognition and proper processing of zisofs compressed files as produced by program mkzftree. These files will get equipped with the necessary meta data so that a Linux kernel will recognize them and deliver their content in uncompressed form. .TP \fB\-transparent-compression\fR Alias of \-z. .TP \fB\--zisofs-version-2\fR .br Enable the recognition and proper processing of experimental zisofs version 2 compressed files. The Linux kernel (as of 5.9) does not yet know this format and will complain like .br isofs: Unknown ZF compression algorithm: PZ .br This complaint can be prevented by option \-\-zisofs2\-susp\-z2 . .br The files will be shown by unaware kernels as they were submitted to xorriso, i.e. with zisofs2 header, block pointer list, and compressed data. .br \-\-zisofs\-version\-2 also enables \-z. .TP \fB\--zisofs2-susp-z2\fR Enable the production of SUSP entries "Z2" instead of "ZF" with zisofs2 compressed files. Unaware Linux kernels silently ignore "Z2" entries. .TP \fB\--zisofs2-susp-zf\fR Enable the production of SUSP entries "ZF" instead of "Z2" with zisofs2 compressed files. Unaware Linux kernels complain about zisofs2 "ZF" by "Unknown ZF compression algorithm" and thus leave a mark in the system log. .TP \fB\-root\fR iso_rr_path Insert all files under the given iso_rr_path. If option \-graft\-points is given, then iso_rr_path is prepended to each target part of a pathspec. .br The default for \-root is "/". .TP \fB\-old-root\fR iso_rr_path Enable incremental insertion of files into the loaded image. The effective target and source addresses of given pathspecs get compared whether the target already exists in the ISO image and is still identical to the source on disk. Metadata in the ISO image will get adjusted, if they differ from those on disk. New files and files with changed content will get newly added. Target files which do not exist in any of the according pathspec sources will get removed from the ISO directory tree. .br If the effective setting of \-root differs from the iso_rr_path given with \-old\-root, then the files underneath the \-old\-root directory get cloned underneath the \-root directory. Cloning happens before file comparison. .TP \fB--old-root-no-ino\fR Disable recording and use of disk inode numbers. If no disk inode numbers are recorded, then option \-old\-root will have to read disk file content and compare it with the MD5 checksum that is recorded in the ISO image. .br With recorded disk inode numbers and with credible ctime and mtime, it is possible to detect potential changes in the content without actually reading it. A loophole remains if multiple different filesystems may get mounted at the same directory, like it is habit with /mnt. In this case one has to use option \-\-old\-root\-devno or disable the inode number shortcut by \-\-old\-root\-no\-ino. .TP \fB--old-root-devno\fR Enable comparison of recorded device numbers together with recorded inode numbers. This works only with good old stable device numbers which get out of fashion, regrettably. If the hard disk has a different device number after each reboot, then this comparison will see all files as changed and thus prevent any incremental size saving. .TP \fB--old-root-no-md5\fR Disable recording and use of MD5 checksums for data file content. If neither checksums and nor disk inode numbers are recorded, then option \-old\-root will have to read ISO image file content when comparing it with disk file content. .TP .B Settings for image production: .TP \fB\-o\fR disk_path Set the output file address for the emerging ISO image. If the address exists as regular file, it will be truncated to length 0 when image production begins. It may not already exist as directory. If it does not exist yet then its parent directory must exist and a regular file will get created. .br A special kind of pseudo disk_path has the form "/dev/fd/"number. It depicts the open file descriptor with the given number, regardless whether the operating system supports this feature by file nodes in /dev/fd or not. E.g. /dev/fd/4 is file descriptor 4 which was opened by the program that later started xorriso. .br Default is standard output (/dev/fd/1) which may also be set by disk_path "\-". .TP \fB\-output\fR disk_path Alias of \-o. .TP \fB--stdio_sync\fR "on"|"off"|"end"|number Set the number of bytes after which to force output to disk in order to keep the memory from being clogged with lots of pending data for slow devices. "on" is the same as "16m". Forced output can be disabled by "off", or be delayed by "end" until all data are produced. If a number is chosen, then it must be at least 64k. .br The default with xorriso mkisofs emulation is \-\-stdio_sync "off". .br xorriso uses an inner fifo buffer with default size 4 MiB. So forcing the operating system i/o cache to disk does not necessarily block the simultaneous production of more image content. .TP \fB--emul-toc\fR Write a second superblock with the first session into random\-access files. If further sessions get appended and the first superblock gets updated, then the second superblock will not be overwritten. So it is still possible to mount the first session and to find the start blocks of the further sessions. .br The price is 64 KiB extra space consumption. If \-partition_offset is non\-zero, then it is 128 KiB plus twice the partition offset. .TP \fB--no-emul-toc\fR Do not write a second superblock with the first session into random\-access files. .br This is the default. .TP \fB--sort-weight\fR weight_number iso_rr_path Attribute a LBA weight number to regular files. If iso_rr_path leads to a directory then all regular files underneath will get the weight_number. .br The weight_number may range from \-2147483648 to 2147483647. The higher it is, the lower will be the block address of the file data in the emerging ISO image. Currently the El Torito boot catalog has a hardcoded weight of 1 billion. Normally it should occupy the block with the lowest possible address. Data files get added or loaded with initial weight 0. Boot image files have a default weight of 2. .TP \fB--sort-weight-list\fR disk_path Read pairs of weight number and iso_rr_path from a file of the local filesystem. Apply each pair like with \-\-sort\-weight. .br Only the last \-\-sort\-weight\-list or \-\-sort\-weight\-patterns of a xorrisofs run gets into effect. .br The weight number is read from the start of the line. The iso_rr_path part of an input line begins immediately after the first blank or tab character of the line. .br Notes for the case that this feature is used within a sequence of generic xorriso commands (not an issue with a pure mkisofs emulation run): .br The addressed files must already be in the ISO image model when you execute .br \-as mkisofs \-\-sort\-weight\-list disk_path \-\- .br Several such commands may be used to apply more than one weight file. .br Data files which are loaded by \-indev or \-dev get a weight between 1 and 2 exp 28 = 268,435,456, depending on their block address. This shall keep them roughly in the same order if the write method of modifying is applied. .TP \fB--sort-weight-patterns\fR disk_path Like \-\-sort\-weight\-list , but expanding the iso_rr_paths as shell parser patterns and applying \-\-sort\-weight to each matching file. .TP \fB\-uid\fR number|name Use the given number or locally existing user name as owner id of all files and directories in the emerging filesystem. Empty name or name "\-" revoke this feature. .TP \fB\-gid\fR number|name Use the given number or locally existing group name as group id of all files and directories in the emerging filesystem. Empty name or name "\-" revoke this feature. .TP \fB\-dir-mode\fR mode Set the access permissions for all directories in the image to the given mode which is either an octal number beginning with "0" or a comma separated list of statements of the form [ugoa]*[+\-=][rwxst]* . E.g. ug=rx,a\-rwx .TP \fB\-file-mode\fR mode Like \-dir\-mode but for all regular data files in the image. .TP \fB\-pad\fR .br Add 300 KiB to the end of the produced ISO image. This circumvents possible read errors from ISO images which have been written to CD media in TAO mode. The additional bytes are claimed as part of the ISO image if not \-\-emul\-toc is given. .br Option \-pad is the default. .TP \fB\-no-pad\fR Disable padding of 300 KiB to the end of the produced ISO image. This is safe if the image is not meant to be written on CD or if it gets written to CD as only track in write mode SAO. .TP \fB--old-empty\fR Use the old way of of giving block addresses in the range of [0,31] to files with no own data content. The new way is to have a dedicated block to which all such files will point. .TP .B Settings for standards compliance: .TP \fB\-iso-level\fR number Specify the ISO 9660 version which defines the limitations of file naming and data file size. The naming restrictions do not apply to the Rock Ridge names but only to the low\-level ISO 9660 names. There are three conformance levels: .br Level 1 allows ISO names of the form 8.3 and file size up to 4 GiB \- 1. .br Level 2 allows ISO names with up to 32 characters and file size up to 4 GiB \- 1. .br Level 3 allows ISO names with up to 32 characters and file size of up to 400 GiB \- 200 KiB. (This size limitation is set by the xorriso implementation and not by ISO 9660 which would allow nearly 8 TiB.) .br Pseudo\-level 4 enables production of an additional ISO 9660:1999 directory tree. .TP \fB\-disallow_dir_id_ext\fR Do not follow a bad habit of mkisofs which allows dots in the ISO names of directories. On the other hand, some bootable GNU/Linux images depend on this bad habit. .TP \fB\-U\fR .br This option allows ISO file names without dot and up to 37 characters, ISO file paths longer than 255 characters, and all ASCII characters in file names. Further it omits the semicolon and the version numbers at the end of ISO names. .br This all violates ISO 9660 specs. .TP \fB\-untranslated-filenames\fR Alias of \-U. .TP \fB\-untranslated_name_len\fR number Allow ISO file names up to the given number of characters without any character conversion. The maximum number is 96. If a file name has more characters, then image production will fail deliberately. .br This violates ISO 9660 specs. .TP \fB\-allow-lowercase\fR Allow lowercase character in ISO file names. .br This violates ISO 9660 specs. .TP \fB\-relaxed-filenames\fR Allow nearly all 7\-bit characters in ISO file names. Not allowed are 0x0 and '/'. If not option \-allow\-lowercase is given, then lowercase letters get converted to uppercase. .br This violates ISO 9660 specs. .TP \fB\-d\fR .br Do not add trailing dot to ISO file names without dot. .br This violates ISO 9660 specs. .TP \fB\-omit-period\fR Alias of \-d. .TP \fB\-l\fR .br Allow up to 31 characters in ISO file names. .TP \fB\-full-iso9660-filenames\fR Alias of \-l. .TP \fB\-max-iso9660-filenames\fR Allow up to 37 characters in ISO file names. .br This violates ISO 9660 specs. .TP \fB\-N\fR .br Omit the semicolon and the version numbers at the end of ISO names. .br This violates ISO 9660 specs. .TP \fB\-omit-version-number\fR Alias of \-N. .TP .B Settings for standards extensions: .TP \fB\-R\fR .br With mkisofs this option enables Rock Ridge extensions. \fBxorrisofs\fR produces them by default. It is strongly discouraged to disable them by option \-\-norock. .TP \fB\-rock\fR .br Alias of \-R. .TP \fB\-r\fR .br Enable Rock Ridge and set user and group id of all files in the ISO image to 0. Grant r\-permissions to all. Deny all w\-permissions. If any x\-permission is set, grant x\-permission to all. Remove s\-bit and t\-bit. .br These attribute changes stay delayed until mkisofs emulation ends. Within the same \-as mkisofs emulation command they can be revoked by a subsequent option \-\-norock. For compatibility reasons, option \-R does not revoke the changes ordered by \-r. .TP \fB\-rational-rock\fR Alias of \-r. .TP \fB--norock\fR .br This option disables the production of Rock Ridge extensions for the ISO 9660 file objects. The multi\-session capabilities of \fBxorrisofs\fR depend much on the naming fidelity of Rock Ridge. So it is strongly discouraged to disable it by this option, except for the special use case to revoke the effect of \-r by: \-\-norock \-R .TP \fB--set_all_file_dates\fR timestring Set mtime, atime, and ctime of all files and directories to the given time. .br Valid timestring formats are: 'Nov 8 14:51:13 CET 2007', 110814512007.13, 2007110814511300. See also \-\-modification\-date= and man xorriso, Examples of input timestrings. .br If the timestring is "set_to_mtime", then the atime and ctime of each file and directory get set to the value found in their mtime. .br These actions stay delayed until actual ISO production begins. Up to then they can be revoked by \-\-set_all_file_dates with empty timestring or timestring "default". .br The timestamps of the El Torito boot catalog file get refreshed when the ISO is produced. They can be influenced by \-\-modification\-date=. .TP \fB\-file_name_limit\fR number Set the maximum permissible length for file names in the range of 64 to 255. Path components which are longer than the given number will get truncated and have their last 33 bytes overwritten by a colon ':' and the hex representation of the MD5 of the first 4095 bytes of the whole oversized name. Potential incomplete UTF\-8 characters will get their leading bytes replaced by '_'. .br Linux kernels up to at least 4.1 misrepresent names of length 254 and 255. If you expect such names in or under disk_paths and plan to mount the ISO by such Linux kernels, consider to set \-file_name_limit 253. .TP \fB\-D\fR The standard ECMA\-119 demands that no path in the image shall have more than 8 name components or 255 characters. Therefore it would be necessary to move deeper directory trees to a higher directory. Rock Ridge offers an opportunity to let these relocated directories appear at their original deep position, but this feature might not be implemented properly by operating systems which mount the image. .br Option \-D disables this deep directory relocation, and thus violates ISO 9660 specs. .br xorrisofs has \-D set by default. If given explicitly then it overrides the options \-rr_reloc_dir and \-hide\-rr\-moved. .TP \fB\-disable-deep-relocation\fR Alias of \-D. .TP \fB\-rr_reloc_dir\fR name Enable the relocation of deep directories and thus avoid ECMA\-119 file paths of more than 8 name components or 255 characters. Directories which lead to such file paths will get moved to a directory in the root directory of the image. Its name gets set by this option. It is permissible to use the root directory itself. .br The overall directory tree will appear originally deep when interpreted as Rock Ridge tree. It will appear as re\-arranged if only ECMA\-119 information is considered. .br If the given relocation target directory does not already exist when image production begins, then it will get created and marked for Rock Ridge as relocation artefact. At least on GNU/Linux it will not be displayed in mounted Rock Ridge images. .br The name must not contain a '/' character after its first character and it must not be longer than 255 bytes. .br This option has no effect if option \-D is present. .TP \fB\-hide-rr-moved\fR Alias of \-rr_reloc_dir "/.rr_moved" .TP \fB--for_backup\fR Enable all options which improve backup fidelity: .br \-\-acl, \-\-xattr\-any, \-\-md5, \-\-hardlinks, and possibly \-\-lfa_flags. .br If you later restore a backup with xattr from non\-user namespaces, then make sure that the target operating system and filesystem know what these attributes mean. Possibly you will need administrator privileges to record or restore such attributes. At recording time, xorriso will try to tolerate missing privileges and just record what is readable. .br Option \-\-xattr after option \-\-for_backup excludes non\-user attributes from being recorded. .br Option \-\-for_backup enables \-\-lfa_flags only if the underlying libisofs was compiled with support for Linux file attributes, which is typically not the case on non\-Linux systems. .TP \fB--acl\fR .br Enable recording and loading of ACLs from GNU/Linux or FreeBSD (see man getfacl, man acl). They will not be in effect with mounted ISO images. But xorriso can restore them on the same systems when extracting files from the ISO image. .TP \fB--xattr\fR .br Enable recording and loading of GNU/Linux or FreeBSD extended attributes in user namespace (see man getfattr and man attr, man getextattr and man 9 extattr, respectively). They will not be in effect with mounted ISO images. But xorriso can restore them on the same systems when extracting files from the ISO image. .TP \fB--xattr-any\fR .br Enable recording and loading of GNU/Linux or FreeBSD extended attributes in all namespaces. This might need administrator privileges, even if the owner of the disk file tries to read the attributes. .TP \fB--lfa_flags\fR .br Enable recording and loading of Linux file attributes as described in man 1 chattr. .br Disable restoring of such attributes just in case that the mkisofs emulation gets ended and files get restored to disk. If restoring of the attributes is desired in this case, execute xorriso command \-lfa_flags "restore" with possibly appended mode texts like ":restore_mask=aAcdDijmPsStTux". .TP \fB--md5\fR .br Enable recording of MD5 checksums for the overall ISO image and for each single data file in the image. xorriso can check the content of an ISO image with these sums and raise alert on mismatch. See man xorriso, options \-check_media, check_md5_r. xorriso can print recorded MD5 checksums. E.g. by: .br \-find / \-exec get_md5 .TP \fB--hardlinks\fR Enable loading and recording of hardlink relations. Search for families of iso_rr files which stem from the same disk file, have identical content filtering and have identical properties. The members of each family get the same inode number in the ISO image. .br Whether these numbers are respected at mount time depends on the operating system. xorriso can create hardlink families when extracting files from the ISO image. .TP \fB--scdbackup_tag\fR disk_path record_name Append a scdbackup checksum record to the image. This works only if the parameter next_writeable_address of option \-C is 0 and \-\-md5 is enabled. If disk_path is not an empty string, then append a scdbackup checksum record to the end of this file. record_name is a word that gets part of tag and record. .br Program scdbackup_verify will recognize and verify tag and file record. .br An empty record_name disables this feature. .TP \fB\-J\fR .br Enable the production of an additional Joliet directory tree along with the ISO 9660 Rock Ridge tree. .TP \fB\-joliet\fR Alias of \-J. .TP \fB\-joliet-long\fR Allow 103 characters in Joliet file names rather than 64 as is prescribed by the specification. Allow Joliet paths longer than the prescribed limit of 240 characters. .br Oversized names get truncated. Without this option, oversized paths get excluded from the Joliet tree. .TP \fB\-joliet-utf16\fR Encode Joliet file names in UTF\-16BE rather than UCS\-2. The difference is with characters which are not present in UCS\-2 and get encoded in UTF\-16 by 2 words of 16 bit each. Both words then stem from a reserved subset of UCS\-2. .TP \fB\-hfsplus\fR Enable the production of an additional HFS+ filesystem inside the ISO 9660 image and mark it by Apple Partition Map (APM) entries in the System Area, the first 32 KiB of the image. .br This may collide with options like \-G or \-isohybrid\-mbr which submit user data for inclusion in the same address range. The first 8 bytes of the System Area get overwritten by { 0x45, 0x52, 0x08 0x00, 0xeb, 0x02, 0xff, 0xff } which can be executed as x86 machine code without negative effects. So if an MBR gets combined with this feature, then its first 8 bytes should contain no essential commands. .br The next blocks of 2 KiB in the System Area will be occupied by APM entries. The first one covers the part of the ISO image before the HFS+ filesystem metadata. The second one marks the range from HFS+ metadata to the end of file content data. If more ISO image data follow, then a third partition entry gets produced. Other features of xorriso might cause the need for more APM entries. .br Be aware that HFS+ is case\-insensitive although it can record file names with upper\-case and lower\-case letters. Therefore, file names from the iso_rr name tree may collide in the HFS+ name tree. In this case they get changed by adding underscore characters and counting numbers. In case of very long names, it might be necessary to map them to "MANGLED_...". .br WARNING: .br The HFS+ implementation in libisofs has a limit of 125,829,120 bytes for the size of the overall directory tree. This suffices for about 300,000 files of normal name length. If the limit gets exceeded, a FAILURE event will be issued and the ISO production will not happen. .TP \fB\-hfsplus-serial-no\fR Set a string of 16 digits "0" to "9" and letters "a" to "f", which will be used as unique serial number of an emerging HFS+ filesystem. .TP \fB\-hfsplus-block-size\fR number Set the allocation block size to be used when producing HFS+ filesystems. Permissible are 512, 2048, or 0. The latter lets the program decide. .TP \fB\-apm-block-size\fR number Set the block size to be used when describing partitions by an Apple Partition Map. Permissible are 512, 2048, or 0. The latter lets the program decide. .br Note that size 512 is not compatible with production of GPT, and that size 2048 will not be mountable \-t hfsplus at least by older Linux kernels. .TP \fB\-hfsplus-file-creator-type\fR creator type iso_rr_path Set the HFS+ creator and type attributes of a file in the emerging image. These are two codes of 4 characters each. .TP \fB\-hfs-bless-by\fR blessing iso_rr_path Issue a HFS+ blessing. They are roles which can be attributed to up to four directories and a data file: .br "ppc_bootdir", "intel_bootfile", "show_folder", "os9_folder", "osx_folder". .br They may be abbreviated as "p", "i", "s", "9", and "x". .br Each such role can be attributed to at most one file object. "intel_bootfile" is the one that would apply to a data file. All others apply to directories. No file object can bear more than one blessing. .TP \fB\-hfs-bless\fR disk_path Issue HFS+ blessing "ppc_bootdir" to the directory which stems from the directory disk_path in the local filesystem tree. .br This works only if there is at least one data file underneath the directory. disk_path can become ambiguous if files from different local filesystem sub\-trees are put into the same sub\-tree of the ISO image. Consider to use \-hfs\-bless\-by "p" for unambiguous addressing via iso_rr_path. .TP .B Settings for file hiding: .TP \fB\-hide\fR disk_path_pattern Make files invisible in the directory tree of ISO 9660 and Rock Ridge, if their disk_path matches the given shell parser pattern. The data content of such hidden files will be included in the resulting image, even if they do not show up in any directory. But you will need own means to find nameless data in the image. .br This command does not apply to the boot catalog. .TP \fB\-hide-list\fR disk_path Perform \-hide using each line out of file disk_path as argument disk_path_pattern. .TP \fB\-hide-joliet\fR disk_path_pattern Like option \-hide but making files invisible in the directory tree of Joliet, if their disk_path matches the given shell parser pattern. .TP \fB\-hide-joliet-list\fR disk_path Perform \-hide\-joliet using each line out of file disk_path as argument disk_path_pattern. .TP \fB\-hide-hfsplus\fR disk_path_pattern Like option \-hide but making files invisible in the directory tree of HFS+, if their disk_path matches the given shell parser pattern. .TP \fB\-hide-hfsplus-list\fR disk_path Perform \-hide\-hfsplus using each line out of file disk_path as argument disk_path_pattern. .TP \fB\-hide_iso_path\fR hide_state iso_rr_path Prevent the name of the given file from showing up in the directory trees of ISO 9660 and/or Joliet and/or HFS+ when the image gets written. Other than the above hide options, this one takes the path of a file in the emerging ISO filesystem, not the path of a file on hard disk. .br Possible values of hide_state are: "iso_rr" for hiding from ISO 9660 tree, "joliet" for Joliet tree, "hfsplus" for HFS+, "on" for them all. "off" means visibility in all directory trees. .br These values may be combined. E.g.: joliet:hfsplus .br This command does not apply to the boot catalog. Rather use: \-\-boot\-catalog\-hide .TP .B ISO image ID strings: .PP The following strings and file addresses get stored in the Primary Volume Descriptor of the ISO9660 image. The file addresses are ISO 9660 paths. These files should have iso_rr_paths which consist only of the characters [A\-Z0\-9_] and exactly one dot which separates at most 8 characters from at most 3 characters. .TP \fB\-V\fR text Set the Volume Id of the ISO image. xorriso accepts any text up to 32 characters, but according to rarely obeyed specs stricter rules apply: .br Conformant are ASCII characters out of [A\-Z0\-9_]. Like: "IMAGE_23" .br Joliet allows 16 UCS\-2 characters. Like: "Windows name" .br Be aware that the volume id might get used automatically as name of the mount point when the medium is inserted into a playful computer system. .TP \fB\-volid\fR text Alias of \-V. .TP \fB\-volset\fR text Set the Volume Set Id of the ISO image. Permissible are up to 128 characters. .TP \fB\-P\fR text Set the Publisher Id of the ISO image. This may identify the person or organisation who specified what shall be recorded. Permissible are up to 128 characters. .TP \fB\-publisher\fR text Alias of \-P. .TP \fB\-A\fR text Set the Application Id of the ISO image. This may identify the specification of how the data are recorded. Permissible are up to 128 characters. .br The special text "@xorriso@" gets converted to the id string of xorriso which is normally written as Preparer Id. It is a wrong tradition to write the program id as Application Id. .TP \fB\-appid\fR text Alias of \-A. .TP \fB\-sysid\fR text Set the System Id of the ISO image. This may identify the system which can recognize and act upon the content of the System Area in image blocks 0 to 15. Permissible are up to 32 characters. .TP \fB\-p\fR text Set the Preparer Id of the ISO image. This may identify the person or other entity which controls the preparation of the data which shall be recorded. Normally this should be the id of xorriso and not of the person or program which operates xorriso. Please avoid to change it. Permissible are up to 128 characters. .br The special text "@xorriso@" gets converted to the id string of xorriso which is default at program startup. .TP \fB\-preparer\fR text Alias of \-p. .TP \fB\-abstract\fR iso_path Set the address of the Abstract File of the ISO image. This should be the ISO 9660 path of a file in the image which contains an abstract statement about the image content. Permissible are up to 37 characters. .TP \fB\-biblio\fR iso_path Set the address of the Biblio File of the ISO image. This should be the ISO 9660 path of a file in the image which contains bibliographic records. Permissible are up to 37 characters. .TP \fB\-copyright\fR iso_path Set the address of the Copyright File of the ISO image. This should be the ISO 9660 path of a file in the image which contains a copyright statement. Permissible are up to 37 characters. .TP \fB--modification-date=YYYYMMDDhhmmsscc\fR Set a timestring that overrides ISO image creation and modification timestamps literally. It must consist of 16 decimal digits which form YYYYMMDDhhmmsscc, with YYYY between 1970 and 2999. Time zone is GMT. It is supposed to match this GRUB line: .br search \-\-fs\-uuid \-\-set YYYY\-MM\-DD\-hh\-mm\-ss\-cc .br E.g. 2010040711405800 is 7 Apr 2010 11:40:58 (+0 centiseconds). .br Among the influenced timestamps are: isohybrid MBR id, El Torito boot catalog file, HFS+ superblock. .TP \fB--application_use\fR character|0xXY|disk_path Specify the content of the Application Use field which can take at most 512 bytes. .br If the parameter of this command is empty, then the field is filled with 512 0\-bytes. If it is a single character, then it gets repeated 512 times. If it begins by "0x" followed by two hex digits [0\-9a\-fA\-F], then the digits are read as byte value which gets repeated 512 times. .br Any other parameter text is used as disk_path to open a data file and to read up to 512 bytes from it. If the file is smaller than 512 bytes, then the remaining bytes in the field get set to binary 0. .TP .B El Torito Bootable ISO images: .PP The precondition for a bootable ISO image is to have in the ISO image the files of a boot loader. The boot facilities of computers get directed to such files, which usually execute further program files from the ISO image. \fBxorrisofs\fR can produce several kinds of boot block or boot record, which become part of the ISO image, and get interpreted by the according boot facility. .br .PP An \fBEl Torito\fR boot record points the bootstrapping facility to a boot catalog with one or more boot images, which are binary program files stored in the ISO image. The content of the boot image files is not in the scope of El Torito. .br xorriso composes the boot catalog according to the boot image files given and structured by options \-b, \-e, \-eltorito\-alt\-boot, and \-\-efi\-boot. Often it contains only one entry. .br Normally the boot images are data files inside the ISO filesystem. By special path "\-\-interval:appended_partition_NNN:all::" it is possible to refer to an appended partition. The number NNN gives the partition number as used with the corresponding option \-append_partition. E.g.: .br \-append_partition 2 0xef /tmp/efi.img .br \-e \-\-interval:appended_partition_2:all:: .br El Torito gets interpreted by boot facilities PC\-BIOS and EFI. Most bootable GNU/Linux CDs are equipped with ISOLINUX or GRUB boot images for PC\-BIOS. .br \fBxorrisofs\fR supports the example options out of the ISOLINUX wiki, the options used in GRUB script grub\-mkrescue, and the example in the FreeBSD AvgLiveCD wiki. .br .PP For CD booting via boot facilities other than PC\-BIOS and EFI, and for booting from USB sticks or hard disks, see the next section about the System Area. .br .TP \fB\-b\fR iso_rr_path Specify the boot image file which shall be mentioned in the current entry of the El Torito boot catalog. It will be marked as suitable for PC\-BIOS. .br With boot images from ISOLINUX and GRUB this option should be accompanied by options \-c , \-no\-emul\-boot , \-boot\-load\-size 4 , \-boot\-info\-table. .TP \fB\-eltorito-boot\fR iso_rr_path Alias of \-b. .TP \fB\-eltorito-alt-boot\fR Finalize the current El Torito boot catalog entry and begin a new one. A boot image file and all its necessary options shall be specified before option \-eltorito\-alt\-boot. All further El Torito boot options apply to the new catalog entry. Up to 32 catalog entries are possible. .TP \fB\-e\fR iso_rr_path Specify the boot image file which shall be mentioned in the current entry of the El Torito boot catalog. It will be marked as suitable for EFI. .br Option \-e should be followed by option \-no\-emul\-boot and no other El Torito options before an eventual \-eltorito\-alt\-boot. .TP \fB--efi-boot\fR iso_rr_path Perform \-eltorito\-alt\-boot, option \-e with the given iso_rr_path, \-no\-emul\-boot, and again \-eltorito\-alt\-boot. This gesture is used for achieving EFI\-bootability of the GRUB2 rescue CD. .TP \fB\-eltorito-platform\fR "x86"|"PPC"|"Mac"|"efi"|0xnn|nnn Set the Platform Id number for the next option \-b or \-eltorito\-boot. The number may be chosen by a platform name or by a number between 0 and 255 (0x00 and 0xFF). "x86" = 0 is for PC\-BIOS, "PPC" = 1 for some PowerPC systems, "Mac" = 2 for some MacIntosh systems, "efi" = 0xEF for EFI on modern PCs with x86 compatible CPUs or others. .br If the new platform id differs from the previous one, \-eltorito\-alt\-boot gets performed. .TP \fB\-boot-load-size\fR number|"full" Set the number of 512\-byte blocks to be loaded at boot time from the boot image in the current catalog entry. .br Non\-emulating BIOS bootimages usually need a load size of 4. Nevertheless the default setting of mkisofs is to use the full size of the boot image rounded up to a multiple of 4 512\-byte blocks. This default may be explicitly enforced by the word "full" instead of a number. .br EFI boot images usually get set the number of blocks occupied by the boot image file. .br El Torito cannot represent load sizes higher than 65535. .TP \fB\-hard-disk-boot\fR Mark the boot image in the current catalog entry as emulated hard disk. (Not suitable for any known boot loader.) .TP \fB\-no-emul-boot\fR Mark the boot image in the current catalog entry as not emulating floppy or hard disk. (This is to be used with all known boot loaders.) .br If neither \-hard\-disk\-boot nor \-no\-emul\-boot is given, then the boot image will be marked as emulating a floppy. (Not suitable for any known boot loader.) .TP \fB\-eltorito-id\fR text|56_hexdigits Define the ID string of the boot catalog section where the boot image will be listed. If the value consists of 56 characters [0\-9A\-Fa\-f] then it is converted into 28 bytes, else the first 28 characters become the ID string. The ID string of the first boot image becomes the overall catalog ID. It is limited to 24 characters. Other id_strings become section IDs. .TP \fB\-eltorito-selcrit\fR hexdigits Define the Selection Criteria of the boot image. Up to 20 bytes get read from the given characters [0\-9A\-Fa\-f]. They get attributed to the boot image entry in the catalog. .TP \fB\-boot-info-table\fR Overwrite bytes 8 to 63 in the current boot image. The information will be supplied by xorriso in the course of image production: Block address of the Primary Volume Descriptor, block address of the boot image file, size of the boot image file. .TP \fB--grub2-boot-info\fR Overwrite bytes 2548 to 2555 in the current boot image by the address of that boot image. The address is written as 64 bit little\-endian number. It is the 2KB block address of the boot image content, multiplied by 4, and then incremented by 5. .TP \fB\-c\fR iso_rr_path Set the address of the El Torito boot catalog file within the image. This file address is not significant for the booting PC\-BIOS or EFI, but it may later be read by other programs in order to learn about the available boot images. .TP \fB\-eltorito-catalog\fR iso_rr_path Alias of \-c. .TP \fB--boot-catalog-hide\fR Prevent the El Torito boot catalog from appearing as file in the directory trees of the image. .TP .B System Area, MBR, GPT, APM, other boot blocks: .PP The first 16 blocks of an ISO image are the System Area. It is reserved for system dependent boot software. This may be the boot facilities and partition tables of various hardware architectures. .br A \fBMBR\fR (Master Boot Record) contains boot code and a partition table. It is read by PC\-BIOS when booting from USB stick or hard disk, and by PowerPC CHRP or PReP when booting. An MBR partition with type 0xee indicates the presence of GPT. .br A \fBGPT\fR (GUID Partition Table) marks partitions in a more modern way. It is read by EFI when booting from USB stick or hard disk, and may be used for finding and mounting a HFS+ partition inside the ISO image. .br An \fBAPM\fR (Apple Partition Map) marks the HFS+ partition. It is read by Macs for booting and for mounting. .br MBR, GPT and APM are combinable. APM occupies the first 8 bytes of MBR boot code. All three do not hamper El Torito booting from CDROM. .br \fBxorrisofs\fR supports further boot facilities: MIPS Big Endian (SGI), MIPS Little Endian (DEC), SUN SPARC, HP\-PA, DEC Alpha. Those are mutually not combinable and also not combinable with MBR, GPT, or APM. .PP Several of the following options expect disk paths as input but also accept description strings for the libisofs interval reader, which is able to cut out data from disk files or \-indev and to zeroize parts of the content: \-G, \-generic\-boot, \-\-embedded\-boot, \-\-grub2\-mbr, \-isohybrid\-mbr, \-efi\-boot\-part, \-prep\-boot\-part, \-B, \-sparc\-boot, \-append_partition. .br The description string consists of the following components, separated by colon ':' .br "\-\-interval:"Flags":"Interval":"Zeroizers":"Source .br The component "\-\-interval" states that this is not a plain disk path but rather a interval reader description string. .br The component Flags modifies the further interpretation: .br "local_fs" demands to read from a file depicted by the path in Source. .br "imported_iso" demands to read from the \-indev. This works only if \-outdev is not the same as \-indev. The Source component is ignored. .br "appended_partition_NNN" with a decimal number NNN works only for options which announce El Torito boot image paths: \-b, \-e, \-\-efi\-boot. The number gives the partition number as used with the corresponding option \-append_partition. .br The component Interval consists of two byte address numbers separated by a "\-" character. E.g. "0\-429" means to read bytes 0 to 429. .br The component Zeroizers consists of zero or more comma separated strings. They define which part of the read data to zeroize. Byte number 0 means the byte read from the Interval start address. Each string may be one of: .br "zero_mbrpt" demands to zeroize the MBR partition table if bytes 510 and 511 bear the MBR signature 0x55 0xaa. .br "zero_gpt" demands to check for a GPT header in bytes 512 to 1023, to zeroize it and its partition table blocks. .br "zero_apm" demands to check for an APM block 0 and to zeroize its partition table blocks. .br Start_byte"\-"End_byte demands to zeroize the read\-in bytes beginning with number Start_byte and ending after End_byte. .br The component Source is the file path with flag "local_fs", and ignored with flag "imported_iso". .br Byte numbers may be scaled by a suffix out of {k,m,g,t,s,d} meaning multiplication by {1024, 1024k, 1024m, 1024g, 2048, 512}. A scaled value end number depicts the last byte of the scaled range. .br E.g. "0d\-0d" is "0\-511". .br Examples: .br "local_fs:0\-32767:zero_mbrpt,zero_gpt,440\-443:/tmp/template.iso" .br "imported_iso:45056d\-47103d::" .br .TP \fB\-G\fR disk_path Copy at most 32768 bytes from the given disk file to the very start of the ISO image. .br Other than a El Torito boot image, the file disk_path needs not to be added to the ISO image. It will not show up as file in the directory trees. .br In multi\-session situations, the special disk_path "." prevents reading of a disk file but nevertheless causes the adjustments in the existing MBR, which were ordered by other options. .TP \fB\-generic-boot\fR disk_path Alias of \-G. .TP \fB--embedded-boot\fR disk_path Alias of \-G. .TP \fB--grub2-mbr\fR disk_path Install disk_path in the System Area and treat it as modern GRUB2 MBR. The content start address of the first boot image is converted to a count of 512 byte blocks, and an offset of 4 is added. The result is written as 64 bit little\-endian number to byte address 0x1b0. .TP \fB\-isohybrid-mbr\fR disk_path Install disk_path as ISOLINUX isohybrid MBR which makes the boot image given by option \-b bootable from USB sticks and hard disks via PC\-BIOS. This preparation is normally done by ISOLINUX program isohybrid on the already produced ISO image. .br The disk path should lead to one of the Syslinux files isohdp[fp]x*.bin . The MBR gets patched according to isohybrid needs. The first partition describes the range of the ISO image. Its start is at block 0 by default, but may be set to 64 disk blocks by option \-partition_offset 16. .br For the meaning of special disk_path "." see option \-G. .TP \fB\-isohybrid-gpt-basdat\fR Mark the current El Torito boot image (see options \-b and \-e) in an actually invalid GPT as partition of type Basic Data. This works only with \-isohybrid\-mbr and has the same impact on the system area as \-efi\-boot\-part. It cannot be combined with \-efi\-boot\-part or \-hfsplus. .br The first three boot images which are marked by GPT will also show up as partition entries in MBR. The MBR partition of type 0xEF is what actually is used by EFI firmware for booting from USB stick. The MBR partition for PC\-BIOS gets type 0x00 rather than 0x17 in this case. Often the further MBR entries are the ones which actually get used by EFI. .TP \fB\-isohybrid-gpt-hfsplus\fR Mark the current El Torito boot image (see options \-b and \-e) in GPT as partition of type HFS+. Impact and restrictions are like with \-isohybrid\-gpt\-basdat. .TP \fB\-isohybrid-apm-hfsplus\fR Mark the current El Torito boot image (see options \-b and \-e) in Apple Partition Map as partition of type HFS+. This works only with \-isohybrid\-mbr and has a similar impact on the system area as \-hfsplus. It cannot be combined with \-efi\-boot\-part or \-hfsplus. .br The ISOLINUX isohybrid MBR file must begin by a known pattern of 32 bytes of x86 machine code which essentially does nothing. It will get overwritten by 32 bytes of APM header mock\-up. .TP \fB\-part_like_isohybrid\fR Control whether \-isohybrid\-gpt\-basdat, \-isohybrid\-gpt\-hfsplus, and \-isohybrid\-apm\-hfsplus apply even if not \-isohybrid\-mbr is present. No MBR partition of type 0xee emerges, even if GPT gets produced. Gaps between GPT and APM partitions will not be filled by more partitions. Appended partitions get mentioned in APM if other APM partitions emerge. .TP \fB\-iso_mbr_part_type\fR "default"|number|type_guid Set the partition type of the MBR or GPT partition which represents the ISO or at least protects it. .br Number may be 0x00 to 0xff. The text "default" re\-enables the default types of the various occasions to create an ISO MBR partition. This is without effect if no such partition emerges by other settings or if the partition type is prescribed mandatorily like 0xee for GPT protective MBR or 0x96 for CHRP. .br If instead a type_guid is given by a 32\-digit hex string like a2a0d0ebe5b9334487c068b6b72699c7 or by a structured text like EBD0A0A2\-B9E5\-4433\-87C0\-68B6B72699C7, then it will be used as partition type if the ISO filesystem appears as partition in GPT. In MBR, C12A7328\-F81F\-11D2\-BA4B\-00A0C93EC93B will be mapped to 0xef. Any other GUID will be mapped to 0x83. .TP \fB--protective-msdos-label\fR Patch the System Area by a simple PC\-DOS partition table where partition 1 claims the range of the ISO image but leaves the first block unclaimed. This is mutally exclusive to option \-isohybrid\-mbr. .TP \fB--mbr-force-bootable\fR Enforce an MBR partition with "bootable/active" flag if options like \-\-protective\-msdos\-label or \-\-grub2\-mbr are given. These options normally cause the flag to be set if there is an MBR partition of type other than 0xee or 0xef. If no such partition exists, then no bootflag is set, unless \-\-mbr\-force\-bootable forces creation of a dummy partition of type 0x00 which covers only the first block of the ISO image. .br If no bootable MBR is indicated by other options and a partition gets created by \-append_partition, then \-\-mbr\-force\-bootable causes a bootflag like it would do with e.g. \-\-protective\-msdos\-label. .TP \fB--gpt-iso-bootable\fR Set bit 2 of the GPT partition flags for the ISO 9660 partition if such a GPT partition emerges. This bit is specified as "Legacy BIOS bootable" but its true significance is unclear. Some GPT\-aware BIOS might want to see it in some partition. .TP \fB--gpt-iso-not-ro\fR Do not set bit 60 of the GPT partition flags for the ISO 9660 partition if such a GPT partition emerges. This bit is specified as "Read\-only" and thus appropriate. But it is unusual in GPT disk partitions. .TP \fB\-partition_offset\fR 2kb_block_adr Cause a partition table with a single partition that begins at the given block address. This is counted in 2048 byte blocks, not in 512 byte blocks. If the block address is non\-zero then it must be at least 16. Values larger than 16 are hardly of use. A non\-zero partition offset causes two superblocks to be generated and two sets of directory trees. The image is then mountable from its absolute start as well as from the partition start. .br The offset value of an ISO image gets preserved when a new session is added to a loaded image. So the value defined here is only in effect if a new ISO image gets written. .TP \fB\-partition_hd_cyl\fR number Set the number of heads per cylinder for the MBR partition table. 0 chooses a default value. Maximum is 255. .TP \fB\-partition_sec_hd\fR number Set the number of sectors per head for the MBR partition table. 0 chooses a default value. Maximum is 63. .br The product partition_sec_hd * partition_hd_cyl * 512 is the cylinder size. It should be divisible by 2048 in order to make exact alignment possible. With appended partitions and \-appended_part_as_gpt there is no limit for the number of cylinders. Else there may be at most 1024 of them. If the cylinder size is too small to stay below the limit, then appropriate values of partition_hd_cyl are chosen with partition_sec_hd 32 or 63. If the image is larger than 8,422,686,720 bytes, then the cylinder size constraints cannot be fulfilled for MBR. They seem not overly important anyway. Flat block addresses in partition tables are good for 1 TiB. .TP \fB\-partition_cyl_align\fR mode Control image size alignment to an integer number of cylinders. It is prescribed by isohybrid specs and it seems to please program fdisk. Cylinder size must be divisible by 2048. Images larger than 8,323,596,288 bytes cannot be aligned in MBR partition table. .br Mode "auto" is default. Alignment by padding happens only if option \-isohybrid\-mbr is given. .br Mode "on" causes alignment by padding with option \-\-protective\-msdos\-label too. Mode "all" is like "on" but also pads up partitions from \-append_partition to an aligned size. .br Mode "off" disables alignment unconditionally. .TP \fB\-append_partition\fR partition_number type_code disk_path Cause a prepared filesystem image to be appended to the ISO image and to be described by a partition table entry in a boot block at the start of the emerging ISO image. The partition entry will bear the size of the submitted file rounded up to the next multiple of 2048 bytes or to the next multiple of the cylinder size. .br Beware of subsequent multi\-session runs. The appended partition will get overwritten. .br partition_number may be 1 to 4. Number 1 will put the whole ISO image into the unclaimed space before partition 1. So together with most xorriso MBR or GPT features, number 2 would be the most natural choice. .br The type_code may be "FAT12", "FAT16", "Linux", or a hexadecimal number between 0x00 and 0xff. Not all those numbers will yield usable results. For a list of codes search the Internet for "Partition Types" or run fdisk command "L". If the partition appears in GPT then type_code 0xef is mapped to the EFI System Partition Type GUID. All others get mapped to Basic Data Type GUID. .br type_code may also be a type GUID as plain hex string like a2a0d0ebe5b9334487c068b6b72699c7 or as structured text like EBD0A0A2\-B9E5\-4433\-87C0\-68B6B72699C7. It will be used if the partition is mentioned in GPT. In MBR, C12A7328\-F81F\-11D2\-BA4B\-00A0C93EC93B will be mapped to 0xef. Any other GUID will be mapped to 0x83. In APM, 48465300\-0000\-11AA\-AA11\-00306543ECAC will be mapped to partition type "Apple_HFS", any other to "Data". .br If some other command causes the production of GPT, then the appended partitions will be mentioned there too, even if not \-appended_part_as_gpt is given. .TP \fB\-appended_part_as_gpt\fR Marks partitions from \-append_partition in GPT rather than in MBR. In this case the MBR shows a single partition of type 0xee which covers the whole output data. .br By default, appended partitions get marked in GPT only if GPT is produced because of other options. .br This option raises the maximum number of appended partitions from 4 to 8. But it is not guaranteed that the resulting GPT partition will have the given partition_number. Other GPT partitions may emerge. The final sorting by start block address may put one of them in the partition entry with the desired number, so that the appended partition will get a higher number. .br Given MBR partition types get translated. 0xef becomes C12A7328\-F81F\-11D2\-BA4B\-00A0C93EC93B, others become EBD0A0A2\-B9E5\-4433\-87C0\-68B6B72699C7. .TP \fB\-appended_part_as_apm\fR Marks partitions from \-append_partition in Apple Partition Map, too. The partition number in APM will not be influenced by \-append_partition parameter partition_number. .br By default, appended partitions get marked in APM only if APM is produced because of other options and \-part_like_isohybrid is enabled. .TP \fB\-efi-boot-part\fR disk_path Copy a file from disk into the emerging ISO image and mark it by a GPT entry as EFI System Partition. EFI boot firmware is supposed to use a FAT filesystem image in such a partition for booting from USB stick or hard disk. .br Instead of a disk_path, the word \-\-efi\-boot\-image may be given. It exposes in GPT the content of the first El Torito EFI boot image as EFI system partition. EFI boot images are introduced by options \-e or \-\-efi\-boot. The affected EFI boot image cannot show up in HFS+ because it is stored outside the HFS+ partition. .TP \fB--gpt_disk_guid\fR value Control whether an emerging GPT shall get a randomly generated disk GUID or whether the GUID is supplied by the user. Value "random" is default. Value "modification\-date" produces a low quality GUID from the value set by option \-\-modification\-date=. .br A string of 32 hex digits, or a RFC 4122 compliant GUID string may be used to set the disk GUID directly. UEFI prescribes the first three components of a RFC 4122 GUID string to be byte\-swapped in the binary representation: .br E.g. \-\-gpt_disk_guid 2303cd2a\-73c7\-424a\-a298\-25632da7f446 equals \-\-gpt_disk_guid 2acd0323c7734a42a29825632da7f446 .br The partition GUIDs get generated by minimally varying the disk GUID. .TP \fB\-chrp-boot-part\fR Mark the block range of the whole emerging ISO image as MBR partition of type 0x96. This is not compatible with any other feature that produces MBR partition entries. It makes GPT unrecognizable. .br CHRP is often used in conjunction with HFS. It is not yet tested whether HFS+ filesystems produced with option \-hfsplus would boot on any CHRP capable machine which does not boot pure ISO 9660 as well. .TP \fB\-chrp-boot\fR Alias of \-chrp\-boot\-part. .TP \fB\-prep-boot-part\fR disk_path Copy a file from disk into the emerging ISO image and mark it by a MBR partition entry of type 0x41. PReP boot firmware is supposed to read the content of the partition as single ELF executable file. This option is compatible with other MBR partitions and with GPT. .TP \fB\-mips-boot\fR iso_rr_path Declare a data file in the image to be a MIPS Big Endian boot file and cause production of a MIPS Big Endian Volume Header. This is mutually exclusive with production of other boot blocks like MBR. It will overwrite the first 512 bytes of any data provided by \-G. Up to 15 boot files can be declared by multiple \-mips\-boot options. .TP \fB\-mipsel-boot\fR iso_rr_path Declare a data file in the image to be the MIPS Little Endian boot file. This is mutually exclusive with other boot blocks. It will overwrite the first 512 bytes of any data provided by \-G. Only a single boot file can be declared by \-mipsel\-boot. .TP \fB\-B\fR disk_path[,disk_path ...] Cause one or more data files on disk to be written after the end of the ISO image. A SUN Disk Label will be written into the first 512 bytes of the ISO image which lists this image as partition 1 and the given disk_paths as partition 2 up to 8. .br The disk files should contain suitable boot images for SUN SPARC systems. .br The pseudo disk_path "..." causes that all empty partition entries become copies of the last non\-empty entry. If no other disk_path is given before "..." then all partitions describe the ISO image. In this case, the boot loader code has to be imported by option \-G. .TP \fB\-sparc-boot\fR disk_path[,disk_path ...] Alias of \-B. .TP \fB\-sparc-label\fR text Set the ASCII label text of a SUN Disk Label. .TP \fB--grub2-sparc-core\fR iso_rr_path Cause the content address and size of the given data file in the image to be written after the SUN Disk Label. Both numbers are counted in bytes. The address is written as 64 bit big\-endian number to byte 0x228. The size is written as 32 bit big\-endian number to byte 0x230. .TP \fB\-hppa-cmdline\fR text Set the PALO command line for HP\-PA. Up to 1023 characters are permitted by default. With \-hppa\-hdrversion 4 the limit is 127. .br Note that the first five \-hppa options are mandatory, if any of the \-hppa options is given. Only option \-hppa\-hdrversion is allowed to be missing. .TP \fB\-hppa-bootloader\fR iso_rr_path Designate the given path as HP\-PA bootloader file. .TP \fB\-hppa-kernel-32\fR iso_rr_path Designate the given path as HP\-PA 32 bit kernel file. .TP \fB\-hppa-kernel-64\fR iso_rr_path Designate the given path as HP\-PA 64 bit kernel file. .TP \fB\-hppa-ramdisk\fR iso_rr_path Designate the given path as HP\-PA RAM disk file. .TP \fB\-hppa-hdrversion\fR number Choose between PALO header version 5 (default) and version 4. For the appropriate value see in PALO source code: PALOHDRVERSION. .TP \fB\-alpha-boot\fR iso_rr_path Declare a data file in the image to be the DEC Alpha SRM Secondary Bootstrap Loader and cause production of a boot sector which points to it. This is mutually exclusive with production of other boot blocks like MBR. .TP .B Character sets: .PP Character sets should not matter as long as only english alphanumeric characters are used for file names or as long as all writers and readers of the medium use the same character set. Outside these constraints it may be necessary to let xorriso convert byte codes. .br A conversion from input character set to the output character set is performed when an ISO image gets written. Vice versa there is a conversion from output character set to the input character set when an ISO image gets loaded. The sets can be defined by options \-input\-charset and \-output\-charset, if needed. .br .TP \fB\-input-charset\fR character_set_name Set the character set from which to convert disk file names when inserting them into the ISO image. .TP \fB\-output-charset\fR character_set_name Set the character set from which to convert names of loaded ISO images and to which to convert names when writing ISO images. .TP .B Jigdo Template Extraction: .PP From man genisoimage: "Jigdo is a tool to help in the distribution of large files like CD and DVD images; see http://atterer.net/jigdo/ for more details. Debian CDs and DVD ISO images are published on the web in jigdo format to allow end users to download them more efficiently." .br If the use of libjte was enabled at compile time of xorriso, then \fBxorrisofs\fR can produce a .jigdo and a .template file together with a single\-session ISO image. If not, then Jigdo options will cause a FAILURE event, which normally leads to program abort. .br One may determine the ability for Jigdo by: .br $ xorrisofs \-version 2>&1 | grep '^libjte' && echo YES .br .PP The .jigdo file contains checksums and symbolic file addresses. The .template file contains the compressed ISO image with reference tags instead of the content bytes of the listed files. .br Input for this process are the normal arguments for a \fBxorrisofs\fR session with no image loaded, and a checksum file which lists those data files which may be listed in the .jigdo file and externally referenced in the .template file. Each designated file is represented in the checksum file by a single text line: .br Checksum as hex digits, 2 blanks, size as 12 decimal digits or blanks, 2 blanks, symbolic file address .br The kind of checksum is chosen by \-jigdo "checksum_algorithm" with values "md5" (32 hex digits) or "sha256" (64 hex digits). It will also be used for the file address lines in the .jigdo file. The default is "md5". .br The file address in a checksum file line has to bear the same basename as the disk_path of the file which it shall match. The directory path of the file address is decisive for To=From mapping, not for file recognition. After To=From mapping, the file address gets written into the .jigdo file. Jigdo restore tools will convert these addresses into really reachable data source addresses from which they can read. .br If the list of jigdo parameters is not empty, then padding will be counted as part of the ISO image. .br .TP \fB\-jigdo-checksum-algorithm\fR "md5"|"sha256" Set the checksum algorithm which shall be used for the data file entries in the .jigdo file and is expected in the checksum file. Default is "md5". .TP \fB\-jigdo-jigdo\fR disk_path Set the disk_path for the .jigdo file with the checksums and download addresses for filling the holes in .template. .TP \fB\-jigdo-template\fR disk_path Set the disk_path for the .template file with the holed and compressed ISO image copy. .TP \fB\-jigdo-min-file-size\fR size Set the minimum size for a data file to be listed in the .jigdo file and being a hole in the .template file. size may be a plain number counting bytes, or a number with appended letter "k", "m", "g" to count KiB (1024 bytes), MiB (1024 KiB), or GiB (1024 MiB). .TP \fB\-jigdo-force-checksum\fR disk_path_pattern adds a regular expression pattern which will get compared with the absolute disk_path of any data file that was not found in the checksum file. A match causes a MISHAP event, which normally does not abort the program run but finally causes a non\-zero exit value of the program. .TP \fB\-jigdo-force-md5\fR disk_path_pattern Outdated alias of \-jigdo\-force\-checksum. .TP \fB\-jigdo-exclude\fR disk_path_pattern Add a regular expression pattern which will get compared with the absolute disk_path of any data file. A match causes the file to stay in .template in any case. .TP \fB\-jigdo-map\fR To=From Add a string pair of the form To=From to the parameter list. If a data file gets listed in the .jigdo file, then it is referred by the file address from its line in the checksum file. This file address gets checked whether it begins with the From string. If so, then this string will be replaced by the To string and a ':' character, before it goes into the .jigdo file. The From string should end by a '/' character. .TP \fB\-checksum-list\fR disk_path Set the disk_path where to find the checksum file file with symbolic file addresses and checksums according to \-jigdo\-checksum\-algorithm. .TP \fB\-md5-list\fR disk_path Outdated alias of \-checksum\-list. .TP \fB\-jigdo-template-compress\fR "gzip"|"bzip2" Choose one of "bzip2" or "gzip" for the compression of the template file. The jigdo file is put out uncompressed. .TP \fB\-checksum_algorithm_iso\fR list_of_names Choose one or more of "md5", "sha1", "sha256", "sha512" for the auxiliary "# Image Hex" checksums in the .jigdo file. The list_of_names may e.g. look like "md5,sha1,sha512". Value "all" chooses all available algorithms. Note that MD5 stays always enabled. .TP \fB\-checksum_algorithm_template\fR list_of_names Choose the algorithms for the "# Template Hex" checksums in the .jigdo file. The rules for list_of_names are the same as with \-checksum_algorithm_iso. .TP .B Miscellaneous options: .TP \fB\-genisoimage_completion\fR Match unrecognized option arguments which begin by a dash '\-' against the known genisoimage options, like program genisoimage does unconditionally (and undocumentedly). If the given argument matches the beginning of exactly one genisoimage option, then it gets replaced by that option. Options which are genuine to mkisofs or xorriso's mkisofs emulation are not matched that way. Option arguments which consist entirely of a leading dash and letters out of "dDfJlNRrTUvz" are not matched but rather interpreted as usual, i.e. as multiple options with leading dash and each single letter. If no genisoimage option is found or more than one are found, then a SORRY message is issued and the argument stays as is. .br To enable this mode by default, write the xorriso command .br \-genisoimage_completion on .br into one of the xorriso start files. See section FILES. .TP \fB\-print-size\fR Print to stdandard output the foreseeable number of 2048 byte blocks in the emerging ISO image. Do not produce this image. .br The result depends on several settings. .br If option \-\-emul\-toc is given, then padding (see \-pad) is not counted as part of the image size. In this case either use \-no\-pad or add 150 (= 300 KiB) to the resulting number. .br If mkisofs emulation ends after option \-print\-size, then the properties of the most recently specified boot image file cannot be edited by subsequent xorriso commands. .TP \fB--no_rc\fR Only if used as first argument this option prevents reading and interpretation of startup files. See section FILES below. .TP \fB\-help\fR .br List supported options to stderr. Original mkisofs options bear their original mkisofs description texts. .TP \fB\-quiet\fR .br Suppress most messages of the program run, except those which indicate problems or errors. .TP \fB\-gui\fR .br Increase the frequency of pacifier messages while writing an ISO image. .TP \fB\-log-file\fR disk_path .br Truncate file disk_path to 0 size and redirect to it all messages which would normally appear on stderr. \-log\-file with empty text as disk_path re\-enables output to stderr. .TP \fB\-v\fR .br Enable the output of informational program messages. .TP \fB\-verbose\fR Alias of \-v. .TP \fB\-version\fR Print to standard output a text that begins with .br "mkisofs 2.01\-Emulation Copyright (C)" .br and to standard error the version information of xorriso. .br .SH EXAMPLES .SS .B Overview of examples: A simple image production run .br Set ISO image paths by -graft-points .br Perform multi-session runs .br Let xorrisofs work underneath growisofs .br Incremental backup of a few directory trees .br Incremental backup with accumulated trees .br Create bootable images for PC-BIOS and EFI .br .SS .B A simple image production run A prepared file tree in directory ./for_iso gets copied into the root directory of the ISO image. File permissions get set to read\-only for everybody. Joliet attributes for Microsoft systems get added. The resulting image gets written as data file ./image.iso on disk. .br $ xorrisofs \-r \-J \-o ./image.iso ./for_iso .SS .B Set ISO image paths by -graft-points Without option \-graft\-points each given disk file is copied into the root directory of the ISO image, maintaining its name. If a directory is given, then its files and sub\-directories are copied into the root directory, maintaining their names. .br $ xorrisofs ... /home/me/datafile /tmp/directory .br yields in the ISO image root directory: .br /datafile .br /file_1_from_directory .br ... .br /file_N_from_directory .br .sp 1 With option \-graft\-points it is possible to put files and directories to arbitrary paths in the ISO image. .br $ xorrisofs ... \-graft\-points /home/me/datafile /dir=/tmp/directory .br yields in the ISO image root directory: .br /datafile .br /dir .br Eventually needed parent directories in the image will be created automatically: .br /datafiles/file1=/home/me/datafile .br yields in the ISO image: .br /datafiles/file1 .br The attributes of directory /datafiles get copied from /home/me on disk. .br .sp 1 Normally one should avoid = and \\ characters in the ISO part of a pathspec. But if it must be, one may escape them: .br /with_\\=_and_\\\\/file=/tmp/directory/file .br yields in the ISO image: .br /with_=_and_\\/file .SS .B Perform multi-session runs This example works for multi\-session media only: CD\-R[W], DVD\-R[W], DVD+R, BD\-R. Add cdrskin option \-\-grow_overwriteable_iso to all \-as cdrecord runs in order to enable multi\-session emulation on overwritable media. .br The first session is written like this: .br $ xorrisofs \-graft\-points \\ .br /tree1=prepared_for_iso/tree1 \\ .br | xorriso \-as cdrecord \-v dev=/dev/sr0 blank=fast \-multi \-eject \- .br Follow\-up sessions are written like this (the run of dd is only to give demons a chance to spoil it): .br $ m=$(xorriso \-as cdrecord dev=/dev/sr0 \-msinfo) .br $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 .br $ xorrisofs \-M /dev/sr0 \-C $m \-graft\-points \\ .br /tree2=prepared_for_iso/tree2 \\ .br | xorriso \-as cdrecord \-v dev=/dev/sr0 \-waiti \-multi \-eject \- .br Always eject the drive tray between sessions. .br The run of xorriso \-as mkisofs will read old sessions via the CD\-ROM driver of /dev/sr0. This driver might not be aware of the changed content as long as the medium is not loaded again. In this case the previous session would not be properly assessed by xorriso and the new session would contain only the newly added files. .br Some systems have not enough patience with automatic tray loading and some demons may interfere with a first CD\-ROM driver read attempt from a freshly loaded medium. .br When loading the tray manually, wait 10 seconds after the drive has stopped blinking. .br A safe automatic way seems to be a separate run of xorriso for loading the tray with proper waiting, and a subsequent run of dd which shall offer itself to any problems caused by demons assessing the changed drive status. If this does not help, insert a run of "sleep 10" between xorriso and dd. .SS .B Let xorrisofs work underneath growisofs growisofs expects an ISO formatter program which understands options \-C and \-M. A variable is defined to override the hardcoded default name. .br $ export MKISOFS="xorrisofs" .br $ growisofs \-Z /dev/dvd /some/files .br $ growisofs \-M /dev/dvd /more/files .br If no "xorrisofs" is available on your system, then you will have to create a link pointing to the xorriso binary and tell growisofs to use it. E.g. by: .br $ ln \-s $(which xorriso) "$HOME/xorrisofs" .br $ export MKISOFS="$HOME/xorrisofs" .br One may quit mkisofs emulation by argument "\-\-" and make use of all xorriso commands. growisofs dislikes options which start with "\-o" but \-outdev must be set to "\-". So use "outdev" instead: .br $ growisofs \-Z /dev/dvd \-\-for_backup \-\- \\ .br outdev \- \-update_r /my/files /files .br $ growisofs \-M /dev/dvd \-\-for_backup \-\- \\ .br outdev \- \-update_r /my/files /files .br Note that \-\-for_backup is given in the mkisofs emulation. To preserve the recorded extra data it must already be in effect, when the emulation loads the image. .SS .B Incremental backup of a few directory trees This changes the directory trees /open_source_project and /personal_mail in the ISO image so that they become exact copies of their disk counterparts. ISO file objects get created, deleted or get their attributes adjusted accordingly. .br ACL, xattr, hard links and MD5 checksums will be recorded. It is expected that inode numbers in the disk filesystem are persistent over cycles of mounting and booting. Files with names matching *.o or *.swp get excluded explicitly. .br .sp 1 To be used several times on the same medium, whenever an update of the two disk trees to the medium is desired. Begin with a blank medium and update it until he run fails gracefully due to lack of remaining space on the old one. .br Always eject the drive tray between sessions. A run of dd shall give demons a chance to spoil the first read on freshly loaded media. .br $ msinfo=$(xorriso \-as cdrecord dev=/dev/sr0 \-msinfo) .br $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 .br $ load_opts= .br $ test \-n "$msinfo" && load_opts="\-M /dev/sr0 \-C $msinfo" .br $ xorrisofs $load_opts \-o \- \-\-for_backup \-m '*.o' \-m '*.swp' \\ .br \-V PROJ_MAIL_"$(date '+%Y_%m_%d_%H%M%S')" \-graft\-points \\ .br \-old\-root / \\ .br /projects=/home/thomas/projects \\ .br /personal_mail=/home/thomas/personal_mail \\ .br | xorriso \-as cdrecord dev=/dev/sr0 \-v \-multi \-waiti \-eject \- .br .sp 1 This makes sense if the full backup leaves substantial remaining capacity on media and if the expected changes are much smaller than the full backup. .br .sp 1 \fBBetter do not use your youngest backup for \-old\-root\fR. Have at least two media which you use alternatingly. So only older backups get endangered by the new write operation, while the newest backup is stored safely on a different medium. .br Always have a blank medium ready to perform a full backup in case the update attempt fails due to insufficient remaining capacity. This failure will not spoil the old medium, of course. .br .sp 1 If inode numbers on disk are not persistent, then use option \-\-old\-root\-no\-ino . In this case an update run will compare recorded MD5 sums against the current file content on hard disk. .br .sp 1 With \fBmount\fR option \fB\-o "sbsector="\fR on GNU/Linux or \fB\-s\fR on FreeBSD or NetBSD it is possible to access the session trees which represent the older backup versions. With CD media, GNU/Linux mount accepts session numbers directly by its option "session=". .br Multi\-session media and most overwritable media written by xorriso can tell the sbsectors of their sessions by xorriso option \-toc: .br $ xorriso \-dev /dev/sr0 \-toc .br xorriso can print the matching mount command for a session number: .br $ xorriso \-mount_cmd /dev/sr0 session 12 /mnt .br or for a volume id that matches a search expression: .br $ xorriso \-mount_cmd /dev/sr0 volid '*2008_12_05*' /mnt .br Both yield on standard output something like: .br mount \-t iso9660 \-o nodev,noexec,nosuid,ro,sbsector=1460256 '/dev/sr0' '/mnt' .br The superuser may let xorriso execute the mount command directly: .br # osirrox \-mount /dev/sr0 "volid" '*2008_12_05*' /mnt .SS .B Incremental backup with accumulated trees Solaris does not offer the option to mount older sessions. In order to keep them accessible, one may map all files to a file tree under a session directory and accumulate those directories from session to session. The \-root tree is cloned from the \-old\-root tree before it gets compared with the appropriate trees on disk. .br This demands to know the previously used session directory name. .br With the first session: .br $ xorrisofs \-root /session1 \\ .br \-o \- \-\-for_backup \-m '*.o' \-m '*.swp' \\ .br \-V PROJ_MAIL_"$(date '+%Y_%m_%d_%H%M%S')" \-graft\-points \\ .br /projects=/home/thomas/projects \\ .br /personal_mail=/home/thomas/personal_mail \\ .br | xorriso \-as cdrecord dev=/dev/sr0 \-v blank=as_needed \\ .br \-multi \-waiti \-eject \- .br .sp 1 With the second session, option \-old\-root refers to /session1 and the new \-root is /session2. .br Always eject the drive tray between sessions. A run of dd shall give demons a chance to spoil the first read on freshly loaded media. .br $ msinfo=$(xorriso \-as cdrecord dev=/dev/sr0 \-msinfo) .br $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 .br $ load_opts= .br $ test \-n "$msinfo" && load_opts="\-M /dev/sr0 \-C $msinfo" .br $ xorrisofs $load_opts \-root /session2 \-old\-root /session1 \\ .br \-o \- \-\-for_backup \-m '*.o' \-m '*.swp' \\ .br \-V PROJ_MAIL_"$(date '+%Y_%m_%d_%H%M%S')" \-graft\-points \\ .br /projects=/home/thomas/projects \\ .br /personal_mail=/home/thomas/personal_mail \\ .br | xorriso \-as cdrecord dev=/dev/sr0 \-v \-multi \-waiti \-eject \- .br With the third session, option \-old\-root refers to /session2. The new \-root is /session3. And so on. .SS .B Create bootable images for PC-BIOS and EFI The SYSLINUX/ISOLINUX boot loader suite is popular for booting PC\-BIOS. The ISOLINUX wiki prescribes to create on disk a directory ./CD_root and to copy all desired files underneath that directory. Especially file isolinux.bin shall be copied to ./CD_root/isolinux/isolinux.bin . This is the boot image file. .br The prescribed mkisofs options can be used unchanged with \fBxorrisofs\fR: .br $ xorrisofs \-o output.iso \\ .br \-b isolinux/isolinux.bin \-c isolinux/boot.cat \\ .br \-no\-emul\-boot \-boot\-load\-size 4 \-boot\-info\-table \\ .br ./CD_root .br Put it on CD by a burn program. E.g.: .br $ xorriso \-as cdrecord \-v dev=/dev/sr0 blank=as_needed output.iso .br .sp 1 The image from above example will boot from CD, DVD or BD, but not from USB stick or other hard\-disk\-like devices. This can be done by help of an isohybrid MBR. Syslinux provides matching template files as isohdp[fp]x*.bin . E.g. /usr/lib/syslinux/isohdpfx.bin . .br If a few hundred KB of size do not matter, then option \-partition_offset can be used to create a partition table where partition 1 starts not at block 0. This facilitates later manipulations of the USB stick by tools for partitioning and formatting. .br The image from the following example will be prepared for booting via MBR and its first partition will start at hard disk block 64. .br It will also boot from optical media. .br $ xorrisofs \-o output.iso \\ .br \-b isolinux/isolinux.bin \-c isolinux/boot.cat \\ .br \-no\-emul\-boot \-boot\-load\-size 4 \-boot\-info\-table \\ .br \-isohybrid\-mbr /usr/lib/syslinux/isohdpfx.bin \\ .br \-partition_offset 16 \\ .br ./CD_root .br Become superuser and copy the image to the unpartitioned base device file of the USB stick. On GNU/Linux this is e.g. /dev/sdb, not /dev/sdb1. .br CAUTION: This will overwrite any partitioning on the USB stick and make remaining data unaccessible. .br So first make sure you got the correct address of the intended device. E.g. by reading 100 MiB data from it and watching it blinking: .br # dd bs=2K if=/dev/sdb count=50K >/dev/null .br Now copy the image onto it .br # dd bs=2K if=output.iso of=/dev/sdb .br .sp 1 Now for EFI: .br The boot image file has to be the image of an EFI System Partition, i.e. a FAT filesystem with directory /EFI/BOOT and boot files with EFI prescribed names: BOOTIA32.EFI for 32 bit x86, BOOTx64.EFI for 64 bit AMD/x86 (in UEFI\-2.4 there is indeed a lower case "x"), BOOTAA64.EFI for 64 bit ARM. The software in the FAT filesystem should be able to find and inspect the ISO filesystem for boot loader configuration and start of operating system. GRUB2 program grub\-mkimage can produce such a FAT filesystem with suitable content, which then uses further GRUB2 software from the ISO filesystem. .br EFI boot equipment may be combined with above ISOLINUX isohybrid for PC\-BIOS in a not really UEFI\-2.4 compliant way, which obviously works well. It yields MBR and GPT partition tables, both with nested partitions. Assumed the EFI System Partition image is ready as ./CD_root/boot/grub/efi.img, add the following options before the directory address ./CD_root: .br \-eltorito\-alt\-boot \-e 'boot/grub/efi.img' \-no\-emul\-boot \\ .br \-isohybrid\-gpt\-basdat \\ .br More compliant with UEFI\-2.4 is to decide for either MBR or GPT and to append a copy of the EFI System Partition in order to avoid overlap of ISO partition and EFI partition. Here for MBR: .br \-eltorito\-alt\-boot \-e 'boot/grub/efi.img' \-no\-emul\-boot \\ \-append_partition 2 0xef ./CD_root/boot/grub/efi.img \\ .br The resulting ISOs are supposed to boot from optical media and USB stick. One may omit option \-eltorito\-alt\-boot if no option \-b is used to make the ISO bootable via PC\-BIOS. .br .sp 1 For ISOs with pure GRUB2 boot equipment consider to use GRUB2 tool grub\-mkrescue as frontend to xorrisofs. .br .sp 1 If you have a bootable ISO filesystem and want to know its equipment plus a proposal how to reproduce it, try: .br $ xorriso \-hfsplus on \-indev IMAGE.iso \\ \-report_el_torito plain \-report_system_area plain \\ \-print "" \-print "======= Proposal for xorrisofs options:" \\ \-report_el_torito as_mkisofs .br .br .SH FILES .SS .B Startup files: .br If not \-\-no_rc is given as the first argument then \fBxorrisofs\fR attempts on startup to read and execute lines from the following files: .br /etc/default/xorriso .br /etc/opt/xorriso/rc .br /etc/xorriso/xorriso.conf .br $HOME/.xorrisorc .br The files are read in the sequence given here, but none of them is required to exist. The lines are not interpreted as \fBxorrisofs\fR options but as generic xorriso commands. See man xorriso. .PP After the xorriso startup files, the program tries one by one to open for reading: .br ./.mkisofsrc .br $MKISOFSRC .br $HOME/.mkisofsrc .br $(dirname $0)/.mkisofsrc .br On success it interprets the file content and does not try further files. The last address is used only if start argument 0 has a non\-trivial dirname. .br The reader currently interprets the following NAME=VALUE pairs: .br APPI default for \-A .br PUBL default for \-publisher .br SYSI default for \-sysid .br VOLI default for \-V .br VOLS default for \-volset .br Any other lines will be silently ignored. .br .SH ENVIRONMENT The following environment variables influence the program behavior: .br HOME is used to find xorriso and mkisofs startup files. .br MKISOFSRC may be used to point the program to a mkisofs startup file. .br SOURCE_DATE_EPOCH belongs to the specs of reproducible\-builds.org. It is supposed to be either undefined or to contain a decimal number which tells the seconds since january 1st 1970. If it contains a number, then it is used as time value to set the default of \-\-modification\-date=. \-\-gpt_disk_guid defaults to "modification\-date". The default of \-\-set_all_file_dates is then "set_to_mtime". Further the "now" time for ISO nodes without disk source is then set to the SOURCE_DATE_EPOCH value. .br Startup files and program options can override the effect of SOURCE_DATE_EPOCH. .SS .SH SEE ALSO .TP For generic xorriso command mode .BR xorriso(1) .TP For the cdrecord emulation of xorriso .BR xorrecord(1) .TP For mounting xorriso generated ISO 9660 images (-t iso9660) .BR mount(8) .TP Other programs which produce ISO 9660 images .BR mkisofs(8), .BR genisoimage(8) .TP Programs which burn sessions to optical media .BR growisofs(1), .BR cdrecord(1), .BR wodim(1), .BR cdrskin(1), .BR xorriso(1) .TP ACL, xattr, Linux file attributes .BR getfacl(1), .BR setfacl(1), .BR getfattr(1), .BR setfattr(1), .BR lsattr(1), .BR chattr(1) .TP MD5 checksums .BR md5sum(1) .TP On FreeBSD the commands for xattr and MD5 differ .BR getextattr(8), .BR setextattr(8), .BR md5(1) .SH BUGS To report bugs, request help, or suggest enhancements for \fBxorriso\fR, please send electronic mail to the public list . If more privacy is desired, mail to . .br Please describe what you expect \fBxorriso\fR to do, the program arguments or dialog commands by which you tried to achieve it, the messages of \fBxorriso\fR, and the undesirable outcome of your program run. .br Expect to get asked more questions before solutions can be proposed. .SH AUTHOR Thomas Schmitt .br for libburnia\-project.org .SH COPYRIGHT Copyright (c) 2011 \- 2024 Thomas Schmitt .br Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of xorriso. If you make use of the license to derive modified versions of xorriso then you are entitled to modify this text under that same license. .SH CREDITS \fBxorrisofs\fR is in part based on work by Vreixo Formoso who provides libisofs together with Mario Danic who also leads the libburnia team. Vladimir Serbinenko contributed the HFS+ filesystem code and related knowledge. .br Compliments towards Joerg Schilling whose cdrtools served me for ten years. This is xorrisofs.info, produced by makeinfo version 5.2 from xorrisofs.texi. xorrisofs - Emulation of ISO 9660 program mkisofs by program xorriso Copyright (C) 2011 - 2024 Thomas Schmitt Permission is granted to distribute this text freely. INFO-DIR-SECTION Archiving START-INFO-DIR-ENTRY * Xorrisofs: (xorrisofs). Emulates ISO 9660 program mkisofs END-INFO-DIR-ENTRY  File: xorrisofs.info, Node: Top, Next: Overview, Up: (dir) xorrisofs 1.5.7 *************** xorrisofs - Emulation of ISO 9660 program mkisofs by program xorriso * Menu: * Overview:: Overview * Standards:: ISO 9660, Rock Ridge, Joliet * Insert:: Inserting files into the ISO image * Xorriso:: Relation to program xorriso * Options:: Options * Examples:: Examples * Files:: Files * Environ:: Environment * Seealso:: See also * Bugreport:: Reporting bugs * Legal:: Author, Copyright, Credits * CommandIdx:: Alphabetic Command List * ConceptIdx:: Alphabetic List of Concepts and Objects  File: xorrisofs.info, Node: Overview, Next: Standards, Prev: Top, Up: Top 1 Overview ********** 'xorrisofs' produces Rock Ridge enhanced ISO 9660 filesystems and add-on sessions to such filesystems. Optionally it can produce Joliet directory trees too. 'xorrisofs' understands options of program mkisofs from cdrtools by Joerg Schilling. Its implementation is part of program xorriso which shares no source code with cdrtools.  File: xorrisofs.info, Node: Standards, Next: Insert, Prev: Overview, Up: Top 2 ISO 9660, Rock Ridge, Joliet, HFS+ ************************************ *ISO 9660* (aka *ECMA-119*) is a read-only filesystem that is mainly used for optical media CD, DVD, BD, but may also reside on other storage devices like disk files, USB sticks or disk partitions. It is widely readable by many operating systems and by boot facilities of personal computers. ISO 9660 describes directories and data files by very restricted filenames with no distinction of upper case and lower case. Its metadata do not comply to fundamental POSIX specifications. *Rock Ridge* is the name of a set of additional information which enhance an ISO 9660 filesystem so that it can represent a POSIX compliant filesystem with ownership, access permissions, symbolic links, and other attributes. Rock Ridge allows filenames of up to 255 bytes and paths of up to 1024 bytes. xorrisofs produces Rock Ridge information by default. It is strongly discouraged to disable this feature. *Joliet* is the name of an additional directory tree which provides filenames up to 64 characters encoded as UTF-16. A Joliet tree is mainly interesting for reading the ISO image by operating systems of Microsoft Corporation. Production of this directory tree may be enabled by option -J. *ISO 9660:1999* is the name of an additional directory tree which provides longer filenames. It allows single file names to have up to 207 characters. It might be of use with some older computer system boot facilities which read neither Rock Ridge nor Joliet but need longer filenames nevertheless. Production of this directory tree may be enabled by option -iso-level 4. *HFS+* is the name of a filesystem which is normally used for writing and reading on hard disks and similar devices. It is possible to embed a HFS+ partition into the emerging ISO 9660 image and to mark it by Apple Partition Map entries. This interferes with options which copy data into the first 32 KiB of the ISO image, like -G or -isohybrid-mbr. See option -hfsplus. The main purpose for having an embedded HFS+ partition is booting of certain models of Apple computers.  File: xorrisofs.info, Node: Insert, Next: Xorriso, Prev: Standards, Up: Top 3 Inserting files into the ISO image ************************************ 'xorrisofs' deals with two kinds of file addresses: *disk_path* is a path to an object in the local filesystem tree. *iso_rr_path* is the Rock Ridge address of a file object in the ISO image. If no Rock Ridge information shall be stored in an emerging ISO, then the names will get mapped to ISO 9660 names of limited length and character set. A program argument is handled as a *pathspec*, if it is not recognized as original mkisofs option or additional 'xorrisofs' option. A pathspec depicts an input file object by a disk_path. If option -graft-points is not present, then the behavior depends on the file type of disk_path. Directories get merged with the /-directory of the ISO image. Files of other types get copied into the /-directory. If -graft-points is present then each pathspec gets split at the first occurrence of the =-character. The part before the = is taken as *target*, i.e. the iso_rr_path for the file object in the ISO image. The part after the first = is taken as *source*, i.e. the disk_path of the input object. It is possible to make =-characters part of the iso_rr_path by preceding them with a \-character. The same must be done for \-characters which shall be part of the iso_rr_path. If the source part of the pathspec leads to a directory, then all files underneath this directory get inserted into the image, too. It is possible to exclude particular files from being inserted by help of option -m. In case that target already exists, the following rules apply: Directories and other files may overwrite existing non-directories. Directories get merged with existing directories. Non-directories may not overwrite existing directories.  File: xorrisofs.info, Node: Xorriso, Next: Options, Prev: Insert, Up: Top 4 Relation to program xorriso ***************************** 'xorrisofs' is actually a command mode of program *xorriso*, which gets entered either by xorriso command "-as mkisofs" or by starting the program by one of the names "xorrisofs", "mkisofs", "genisoimage", or "genisofs". This command mode can be left by argument "--" which leads to generic xorriso command mode. See *man xorriso* for its description. xorriso performs image reading and writing by help of libburn, which is mainly intended for optical drives, but also operates on all POSIX file types except directories. The program messages call any image file a "drive". File types which are not supported for reading are reported as "blank". The reported free media space may be quite fictional. Nevertheless 'xorrisofs' does not operate directly on optical drives, but rather forces libburn to regard them as general device files. So for writing of sequential optical media (CD, DVD-R, DVD+R, BD-R) one will have to use a burn program. E.g the cdrecord emulation of xorriso. See EXAMPLES.  File: xorrisofs.info, Node: Options, Next: Examples, Prev: Xorriso, Up: Top 5 Options ********* * Menu: * Loading:: Image loading * SetInsert:: Settings for file insertion * SetProduct:: Settings for image production * SetCompl:: Settings for standards compliance * SetExtras:: Settings for standards extensions * SetHide:: Settings for file hiding * ImageId:: ISO image ID strings * Bootable:: El Torito Bootable ISO images * SystemArea:: System Area, MBR, GPT, APM, other boot blocks * Charset:: Character sets * Jigdo:: Jigdo Template Extraction * Miscellaneous:: Miscellaneous options  File: xorrisofs.info, Node: Loading, Next: SetInsert, Prev: Options, Up: Options 5.1 Influencing the behavior of image loading ============================================= The following options control loading of an existing ISO image for the purpose of preparing a suitable add-on session. If they are missing then a new image is composed from scratch. -M disk_path Set the path from which to load the existing ISO image directory tree on which to base the upcoming directory tree as add-on session. The path must lead to a random-access readable file object. On GNU/Linux: regular data files or block device files. A special kind of pseudo disk_path has the form "/dev/fd/"number. It depicts the open file descriptor with the given number, regardless whether the operating system supports this feature by file nodes in /dev/fd or not. E.g. /dev/fd/3 is file descriptor 3 which was opened by the program that later started xorriso. -prev-session disk_path Alias of -M. -dev disk_path Alias of -M. -C last_session_start,next_writeable_address Set the 2 KiB block address last_session_start from where to read the ISO image out of the file given by option -M. Separated by a comma, set the next_writeable_address to which the add-on session will finally be written. Decisive is actually the block address which the intended readers will have to use as superblock address on the intended medium. Both values can be inquired from optical media by help of burn programs and cdrecord option -msinfo. xorriso itself can obtain it in its cdrecord emulation. values=$(xorriso -as cdrecord dev=/dev/... -msinfo) echo $values Option -C may be used without option -M to create an ISO image from scratch and prepare it for being finally written to a block address other than 0. Parameter last_session_start must then be set to 0. -cdrecord-params last_session_start,next_writeable_address Alias of -C.  File: xorrisofs.info, Node: SetInsert, Next: SetProduct, Prev: Loading, Up: Options 5.2 Settings for file insertion =============================== -path-list disk_path Read pathspecs line-by-line from disk_file and insert the depicted file objects into the ISO image. If disk_path is "-" then read the pathspecs from standard input. --quoted_path_list disk_path Like option -path-list but reading quoted words rather than plain lines. Whitespace outside of quotes will be discarded. On the other hand it is possible to represent pathspecs which contain newline characters. The double quotation mark " and the single quotation mark ' can be used to enclose whitespace and make it part of pathspecs. Each mark type can enclose the marks of the other type. A trailing backslash \ outside quotations or an open quotation cause the next input line to be appended. -cut_out disk_path byte_offset byte_count iso_rr_path Map a byte interval of a regular disk file or of a device file into a regular file in the ISO image. The file depicted by disk_path has to support random read access. byte_offset and byte_count may be plain numbers counting bytes, or numbers with appended letter "d", "s", "k", "m", "g" to count disk blocks (512 bytes), disc sectors (2048 bytes), KiB (1024 bytes), MiB (1024 KiB), or GiB (1024 MiB). E.g: -cut_out bootable.iso 562s 18s /formerly_hidden_boot_image -f Resolve symbolic links on disk rather than storing them as symbolic links in the ISO image. -follow-links Alias of -f. -graft-points Enable interpretation of input file pathspecs as combination of iso_rr_path and disk_path, separated by a =-character. -m disk_pattern Exclude files from being inserted into the image. Silently ignored are those files of which the disk_path matches the given shell parser pattern. If no /-character is part of the pattern, then it gets matched against the leaf name of the disk file. It is possible to give more than one -m option. -exclude Alias of -m. -x Alias of -m. -old-exclude Alias of -m. -exclude-list disk_path Perform -m using each line out of file disk_path as argument disk_pattern. -z Enable recognition and proper processing of zisofs compressed files as produced by program mkzftree. These files will get equipped with the necessary meta data so that a Linux kernel will recognize them and deliver their content in uncompressed form. -transparent-compression Alias of -z. -zisofs-version-2 Enable the recognition and proper processing of experimental zisofs version 2 compressed files. The Linux kernel (as of 5.9) does not yet know this format and will complain like isofs: Unknown ZF compression algorithm: PZ This complaint can be prevented by option -zisofs2-susp-z2 . The files will be shown by unaware kernels as they were submitted to xorriso, i.e. with zisofs2 header, block pointer list, and compressed data. -zisofs-version-2 also enables -z. -zisofs2-susp-z2 Enable the production of SUSP entries "Z2" instead of "ZF" with zisofs2 compressed files. Unaware Linux kernels silently ignore "Z2" entries. -zisofs2-susp-zf Enable the production of SUSP entries "ZF" instead of "Z2" with zisofs2 compressed files. Unaware Linux kernels complain about zisofs2 "ZF" by "Unknown ZF compression algorithm" and thus leave a mark in the system log. -root iso_rr_path Insert all files under the given iso_rr_path. If option -graft-points is given, then iso_rr_path is prepended to each target part of a pathspec. The default for -root is "/". -old-root iso_rr_path Enable incremental insertion of files into the loaded image. The effective target and source addresses of given pathspecs get compared whether the target already exists in the ISO image and is still identical to the source on disk. Metadata in the ISO image will get adjusted, if they differ from those on disk. New files and files with changed content will get newly added. Target files which do not exist in any of the according pathspec sources will get removed from the ISO directory tree. If the effective setting of -root differs from the iso_rr_path given with -old-root, then the files underneath the -old-root directory get cloned underneath the -root directory. Cloning happens before file comparison. --old-root-no-ino Disable recording and use of disk inode numbers. If no disk inode numbers are recorded, then option -old-root will have to read disk file content and compare it with the MD5 checksum that is recorded in the ISO image. With recorded disk inode numbers and with credible ctime and mtime, it is possible to detect potential changes in the content without actually reading it. A loophole remains if multiple different filesystems may get mounted at the same directory, like it is habit with /mnt. In this case one has to use option --old-root-devno or disable the inode number shortcut by --old-root-no-ino. --old-root-devno Enable comparison of recorded device numbers together with recorded inode numbers. This works only with good old stable device numbers which get out of fashion, regrettably. If the hard disk has a different device number after each reboot, then this comparison will see all files as changed and thus prevent any incremental size saving. --old-root-no-md5 Disable recording and use of MD5 checksums for data file content. If neither checksums and nor disk inode numbers are recorded, then option -old-root will have to read ISO image file content when comparing it with disk file content.  File: xorrisofs.info, Node: SetProduct, Next: SetCompl, Prev: SetInsert, Up: Options 5.3 Settings for image production ================================= -o disk_path Set the output file address for the emerging ISO image. If the address exists as regular file, it will be truncated to length 0 when image production begins. It may not already exist as directory. If it does not exist yet then its parent directory must exist and a regular file will get created. A special kind of pseudo disk_path has the form "/dev/fd/"number. It depicts the open file descriptor with the given number, regardless whether the operating system supports this feature by file nodes in /dev/fd or not. E.g. /dev/fd/4 is file descriptor 4 which was opened by the program that later started xorriso. Default is standard output (/dev/fd/1) which may also be set by disk_path "-". -output disk_path Alias of -o. --stdio_sync "on"|"off"|"end"|number Set the number of bytes after which to force output to disk in order to keep the memory from being clogged with lots of pending data for slow devices. "on" is the same as "16m". Forced output can be disabled by "off", or be delayed by "end" until all data are produced. If a number is chosen, then it must be at least 64k. The default with xorriso mkisofs emulation is --stdio_sync "off". xorriso uses an inner fifo buffer with default size 4 MiB. So forcing the operating system i/o cache to disk does not necessarily block the simultaneous production of more image content. --emul-toc Write a second superblock with the first session into random-access files. If further sessions get appended and the first superblock gets updated, then the second superblock will not be overwritten. So it is still possible to mount the first session and to find the start blocks of the further sessions. The price is 64 KiB extra space consumption. If -partition_offset is non-zero, then it is 128 KiB plus twice the partition offset. --no-emul-toc Do not write a second superblock with the first session into random-access files. This is the default. --sort-weight weight_number iso_rr_path Attribute a LBA weight number to regular files. If iso_rr_path leads to a directory then all regular files underneath will get the weight_number. The weight_number may range from -2147483648 to 2147483647. The higher it is, the lower will be the block address of the file data in the emerging ISO image. Currently the El Torito boot catalog has a hardcoded weight of 1 billion. Normally it should occupy the block with the lowest possible address. Data files get added or loaded with initial weight 0. Boot image files have a default weight of 2. --sort-weight-list disk_path Read pairs of weight number and iso_rr_path from a file of the local filesystem. Apply each pair like with --sort-weight. Only the last --sort-weight-list or --sort-weight-patterns of a xorrisofs run gets into effect. The weight number is read from the start of the line. The iso_rr_path part of an input line begins immediately after the first blank or tab character of the line. Notes for the case that this feature is used within a sequence of generic xorriso commands (not an issue with a pure mkisofs emulation run): The addressed files must already be in the ISO image model when you execute -as mkisofs --sort-weight-list disk_path -- Several such commands may be used to apply more than one weight file. Data files which are loaded by -indev or -dev get a weight between 1 and 2 exp 28 = 268,435,456, depending on their block address. This shall keep them roughly in the same order if the write method of modifying is applied. --sort-weight-patterns disk_path Like --sort-weight-list , but expanding the iso_rr_paths as shell parser patterns and applying --sort-weight to each matching file. -uid number|name Use the given number or locally existing user name as owner id of all files and directories in the emerging filesystem. Empty name or name "-" revoke this feature. -gid number|name Use the given number or locally existing group name as group id of all files and directories in the emerging filesystem. Empty name or name "-" revoke this feature. -dir-mode mode Set the access permissions for all directories in the image to the given mode which is either an octal number beginning with "0" or a comma separated list of statements of the form [ugoa]*[+-=][rwxst]* . E.g. ug=rx,a-rwx -file-mode mode Like -dir-mode but for all regular data files in the image. -pad Add 300 KiB to the end of the produced ISO image. This circumvents possible read errors from ISO images which have been written to CD media in TAO mode. The additional bytes are claimed as part of the ISO image if not -emul-toc is given. Option -pad is the default. -no-pad Disable padding of 300 KiB to the end of the produced ISO image. This is safe if the image is not meant to be written on CD or if it gets written to CD as only track in write mode SAO. --old-empty Use the old way of of giving block addresses in the range of [0,31] to files with no own data content. The new way is to have a dedicated block to which all such files will point.  File: xorrisofs.info, Node: SetCompl, Next: SetExtras, Prev: SetProduct, Up: Options 5.4 Settings for standards compliance ===================================== -iso-level number Specify the ISO 9660 version which defines the limitations of file naming and data file size. The naming restrictions do not apply to the Rock Ridge names but only to the low-level ISO 9660 names. There are three conformance levels: Level 1 allows ISO names of the form 8.3 and file size up to 4 GiB - 1. Level 2 allows ISO names with up to 32 characters and file size up to 4 GiB - 1. Level 3 allows ISO names with up to 32 characters and file size of up to 400 GiB - 200 KiB. (This size limitation is set by the xorriso implementation and not by ISO 9660 which would allow nearly 8 TiB.) Pseudo-level 4 enables production of an additional ISO 9660:1999 directory tree. -disallow_dir_id_ext Do not follow a bad habit of mkisofs which allows dots in the ISO names of directories. On the other hand, some bootable GNU/Linux images depend on this bad habit. -U This option allows ISO file names without dot and up to 37 characters, ISO file paths longer than 255 characters, and all ASCII characters in file names. Further it omits the semicolon and the version numbers at the end of ISO names. This all violates ISO 9660 specs. -untranslated-filenames Alias of -U. -untranslated_name_len number Allow ISO file names up to the given number of characters without any character conversion. The maximum number is 96. If a file name has more characters, then image production will fail deliberately. This violates ISO 9660 specs. -allow-lowercase Allow lowercase character in ISO file names. This violates ISO 9660 specs. -relaxed-filenames Allow nearly all 7-bit characters in ISO file names. Not allowed are 0x0 and '/'. If not option -allow-lowercase is given, then lowercase letters get converted to uppercase. This violates ISO 9660 specs. -d Do not add trailing dot to ISO file names without dot. This violates ISO 9660 specs. -omit-period Alias of -d. -l Allow up to 31 characters in ISO file names. -full-iso9660-filenames Alias of -l. -max-iso9660-filenames Allow up to 37 characters in ISO file names. This violates ISO 9660 specs. -N Omit the semicolon and the version numbers at the end of ISO names. This violates ISO 9660 specs. -omit-version-number Alias of -N.  File: xorrisofs.info, Node: SetExtras, Next: SetHide, Prev: SetCompl, Up: Options 5.5 Settings for standards extensions ===================================== -R With mkisofs this option enables Rock Ridge extensions. 'xorrisofs' produces them by default. It is strongly discouraged to disable them by option --norock. -rock Alias of -R. -r Enable Rock Ridge and set user and group id of all files in the ISO image to 0. Grant r-permissions to all. Deny all w-permissions. If any x-permission is set, grant x-permission to all. Remove s-bit and t-bit. These attribute changes stay delayed until mkisofs emulation ends. Within the same -as mkisofs emulation command they can be revoked by a subsequent option --norock. For compatibility reasons, option -R does not revoke the changes ordered by -r. -rational-rock Alias of -r. --norock This option disables the production of Rock Ridge extensions for the ISO 9660 file objects. The multi-session capabilities of 'xorrisofs' depend much on the naming fidelity of Rock Ridge. So it is strongly discouraged to disable it by this option, except for the special use case to revoke the effect of -r by: --norock -R --set_all_file_dates timestring Set mtime, atime, and ctime of all files and directories to the given time. Valid timestring formats are: 'Nov 8 14:51:13 CET 2007', 110814512007.13, 2007110814511300. See also --modification-date= and man xorriso, Examples of input timestrings. If the timestring is "set_to_mtime", then the atime and ctime of each file and directory get set to the value found in their mtime. These actions stay delayed until actual ISO production begins. Up to then they can be revoked by --set_all_file_dates with empty timestring or timestring "default". The timestamps of the El Torito boot catalog file get refreshed when the ISO is produced. They can be influenced by --modification-date=. -file_name_limit number Set the maximum permissible length for file names in the range of 64 to 255. Path components which are longer than the given number will get truncated and have their last 33 bytes overwritten by a colon ':' and the hex representation of the MD5 of the first 4095 bytes of the whole oversized name. Potential incomplete UTF-8 characters will get their leading bytes replaced by '_'. Linux kernels up to at least 4.1 misrepresent names of length 254 and 255. If you expect such names in or under disk_paths and plan to mount the ISO by such Linux kernels, consider to set -file_name_limit 253. -D The standard ECMA-119 demands that no path in the image shall have more than 8 name components or 255 characters. Therefore it would be necessary to move deeper directory trees to a higher directory. Rock Ridge offers an opportunity to let these relocated directories appear at their original deep position, but this feature might not be implemented properly by operating systems which mount the image. Option -D disables this deep directory relocation, and thus violates ISO 9660 specs. xorrisofs has -D set by default. If given explicitly then it overrides the options -rr_reloc_dir and -hide-rr-moved. -disable-deep-relocation Alias of -D. -rr_reloc_dir name Enable the relocation of deep directories and thus avoid ECMA-119 file paths of more than 8 name components or 255 characters. Directories which lead to such file paths will get moved to a directory in the root directory of the image. Its name gets set by this option. It is permissible to use the root directory itself. The overall directory tree will appear originally deep when interpreted as Rock Ridge tree. It will appear as re-arranged if only ECMA-119 information is considered. If the given relocation target directory does not already exist when image production begins, then it will get created and marked for Rock Ridge as relocation artefact. At least on GNU/Linux it will not be displayed in mounted Rock Ridge images. The name must not contain a '/' character after its first character and it must not be longer than 255 bytes. This option has no effect if option -D is present. -hide-rr-moved Alias of -rr_reloc_dir "/.rr_moved" --for_backup Enable all options which improve backup fidelity: --acl, --xattr-any, --md5, --hardlinks, and possibly --lfa_flags. If you later restore a backup with xattr from non-user namespaces, then make sure that the target operating system and filesystem know what these attributes mean. Possibly you will need administrator privileges to record or restore such attributes. At recording time, xorriso will try to tolerate missing privileges and just record what is readable. Option --xattr after option --for_backup excludes non-user attributes from being recorded. Option --for_backup enables --lfa_flags only if the underlying libisofs was compiled with support for Linux file attributes, which is typically not the case on non-Linux systems. --acl Enable recording and loading of ACLs from GNU/Linux or FreeBSD (see man getfacl, man acl). They will not be in effect with mounted ISO images. But xorriso can restore them on the same systems when extracting files from the ISO image. --xattr Enable recording and loading of GNU/Linux or FreeBSD extended attributes in user namespace (see man getfattr and man attr, man getextattr and man 9 extattr, respectively). They will not be in effect with mounted ISO images. But xorriso can restore them on the same systems when extracting files from the ISO image. --xattr-any Enable recording and loading of GNU/Linux or FreeBSD extended attributes in all namespaces. This might need administrator privileges, even if the owner of the disk file tries to read the attributes. --lfa_flags Enable recording and loading of Linux file attributes as described in man 1 chattr. Disable restoring of such attributes just in case that the mkisofs emulation gets ended and files get restored to disk. If restoring of the attributes is desired in this case, execute xorriso command -lfa_flags "restore" with possibly appended mode texts like ":restore_mask=aAcdDijmPsStTux". --md5 Enable recording of MD5 checksums for the overall ISO image and for each single data file in the image. xorriso can check the content of an ISO image with these sums and raise alert on mismatch. See man xorriso, options -check_media, check_md5_r. xorriso can print recorded MD5 checksums. E.g. by: -find / -exec get_md5 --hardlinks Enable loading and recording of hardlink relations. Search for families of iso_rr files which stem from the same disk file, have identical content filtering and have identical properties. The members of each family get the same inode number in the ISO image. Whether these numbers are respected at mount time depends on the operating system. xorriso can create hardlink families when extracting files from the ISO image. --scdbackup_tag disk_path record_name Append a scdbackup checksum record to the image. This works only if the parameter next_writeable_address of option -C is 0 and -md5 is enabled. If disk_path is not an empty string, then append a scdbackup checksum record to the end of this file. record_name is a word that gets part of tag and record. Program scdbackup_verify will recognize and verify tag and file record. An empty record_name disables this feature. -J Enable the production of an additional Joliet directory tree along with the ISO 9660 Rock Ridge tree. -joliet Alias of -J. -joliet-long Allow 103 characters in Joliet file names rather than 64 as is prescribed by the specification. Allow Joliet paths longer than the prescribed limit of 240 characters. Oversized names get truncated. Without this option, oversized paths get excluded from the Joliet tree. -joliet-utf16 Encode Joliet file names in UTF-16BE rather than UCS-2. The difference is with characters which are not present in UCS-2 and get encoded in UTF-16 by 2 words of 16 bit each. Both words then stem from a reserved subset of UCS-2. -hfsplus Enable the production of an additional HFS+ filesystem inside the ISO 9660 image and mark it by Apple Partition Map (APM) entries in the System Area, the first 32 KiB of the image. This may collide with options like -G or -isohybrid-mbr which submit user data for inclusion in the same address range. The first 8 bytes of the System Area get overwritten by { 0x45, 0x52, 0x08 0x00, 0xeb, 0x02, 0xff, 0xff } which can be executed as x86 machine code without negative effects. So if an MBR gets combined with this feature, then its first 8 bytes should contain no essential commands. The next blocks of 2 KiB in the System Area will be occupied by APM entries. The first one covers the part of the ISO image before the HFS+ filesystem metadata. The second one marks the range from HFS+ metadata to the end of file content data. If more ISO image data follow, then a third partition entry gets produced. Other features of xorriso might cause the need for more APM entries. Be aware that HFS+ is case-insensitive although it can record file names with upper-case and lower-case letters. Therefore, file names from the iso_rr name tree may collide in the HFS+ name tree. In this case they get changed by adding underscore characters and counting numbers. In case of very long names, it might be necessary to map them to "MANGLED_...". WARNING: The HFS+ implementation in libisofs has a limit of 125,829,120 bytes for the size of the overall directory tree. This suffices for about 300,000 files of normal name length. If the limit gets exceeded, a FAILURE event will be issued and the ISO production will not happen. -hfsplus-serial-no Set a string of 16 digits "0" to "9" and letters "a" to "f", which will be used as unique serial number of an emerging HFS+ filesystem. -hfsplus-block-size number Set the allocation block size to be used when producing HFS+ filesystems. Permissible are 512, 2048, or 0. The latter lets the program decide. -apm-block-size number Set the block size to be used when describing partitions by an Apple Partition Map. Permissible are 512, 2048, or 0. The latter lets the program decide. Note that size 512 is not compatible with production of GPT, and that size 2048 will not be mountable -t hfsplus at least by older Linux kernels. -hfsplus-file-creator-type creator type iso_rr_path Set the HFS+ creator and type attributes of a file in the emerging image. These are two codes of 4 characters each. -hfs-bless-by blessing iso_rr_path Issue a HFS+ blessing. They are roles which can be attributed to up to four directories and a data file: "ppc_bootdir", "intel_bootfile", "show_folder", "os9_folder", "osx_folder". They may be abbreviated as "p", "i", "s", "9", and "x". Each such role can be attributed to at most one file object. "intel_bootfile" is the one that would apply to a data file. All others apply to directories. No file object can bear more than one blessing. -hfs-bless disk_path Issue HFS+ blessing "ppc_bootdir" to the directory which stems from the directory disk_path in the local filesystem tree. This works only if there is at least one data file underneath the directory. disk_path can become ambiguous if files from different local filesystem sub-trees are put into the same sub-tree of the ISO image. Consider to use -hfs-bless-by "p" for unambiguous addressing via iso_rr_path.  File: xorrisofs.info, Node: SetHide, Next: ImageId, Prev: SetExtras, Up: Options 5.6 Settings for file hiding ============================ -hide disk_path_pattern Make files invisible in the directory tree of ISO 9660 and Rock Ridge, if their disk_path matches the given shell parser pattern. The data content of such hidden files will be included in the resulting image, even if they do not show up in any directory. But you will need own means to find nameless data in the image. This command does not apply to the boot catalog. -hide-list disk_path Perform -hide using each line out of file disk_path as argument disk_path_pattern. -hide-joliet disk_path_pattern Like option -hide but making files invisible in the directory tree of Joliet, if their disk_path matches the given shell parser pattern. -hide-joliet-list disk_path Perform -hide-joliet using each line out of file disk_path as argument disk_path_pattern. -hide-hfsplus disk_path_pattern Like option -hide but making files invisible in the directory tree of HFS+, if their disk_path matches the given shell parser pattern. -hide-hfsplus-list disk_path Perform -hide-hfsplus using each line out of file disk_path as argument disk_path_pattern. -hide_iso_path hide_state iso_rr_path Prevent the name of the given file from showing up in the directory trees of ISO 9660 and/or Joliet and/or HFS+ when the image gets written. Other than the above hide options, this one takes the path of a file in the emerging ISO filesystem, not the path of a file on hard disk. Possible values of hide_state are: "iso_rr" for hiding from ISO 9660 tree, "joliet" for Joliet tree, "hfsplus" for HFS+, "on" for them all. "off" means visibility in all directory trees. These values may be combined. E.g.: joliet:hfsplus This command does not apply to the boot catalog. Rather use: --boot-catalog-hide  File: xorrisofs.info, Node: ImageId, Next: Bootable, Prev: SetHide, Up: Options 5.7 ISO image ID strings ======================== The following strings and file addresses get stored in the Primary Volume Descriptor of the ISO9660 image. The file addresses are ISO 9660 paths. These files should have iso_rr_paths which consist only of the characters [A-Z0-9_] and exactly one dot which separates at most 8 characters from at most 3 characters. -V text Set the Volume Id of the ISO image. xorriso accepts any text up to 32 characters, but according to rarely obeyed specs stricter rules apply: Conformant are ASCII characters out of [A-Z0-9_]. Like: "IMAGE_23" Joliet allows 16 UCS-2 characters. Like: "Windows name" Be aware that the volume id might get used automatically as name of the mount point when the medium is inserted into a playful computer system. -volid text Alias of -V. -volset text Set the Volume Set Id of the ISO image. Permissible are up to 128 characters. -P text Set the Publisher Id of the ISO image. This may identify the person or organisation who specified what shall be recorded. Permissible are up to 128 characters. -publisher text Alias of -P. -A text Set the Application Id of the ISO image. This may identify the specification of how the data are recorded. Permissible are up to 128 characters. The special text "@xorriso@" gets converted to the id string of xorriso which is normally written as Preparer Id. It is a wrong tradition to write the program id as Application Id. -appid text Alias of -A. -sysid text Set the System Id of the ISO image. This may identify the system which can recognize and act upon the content of the System Area in image blocks 0 to 15. Permissible are up to 32 characters. -p text Set the Preparer Id of the ISO image. This may identify the person or other entity which controls the preparation of the data which shall be recorded. Normally this should be the id of xorriso and not of the person or program which operates xorriso. Please avoid to change it. Permissible are up to 128 characters. The special text "@xorriso@" gets converted to the id string of xorriso which is default at program startup. -preparer text Alias of -p. -abstract iso_path Set the address of the Abstract File of the ISO image. This should be the ISO 9660 path of a file in the image which contains an abstract statement about the image content. Permissible are up to 37 characters. -biblio iso_path Set the address of the Biblio File of the ISO image. This should be the ISO 9660 path of a file in the image which contains bibliographic records. Permissible are up to 37 characters. -copyright iso_path Set the address of the Copyright File of the ISO image. This should be the ISO 9660 path of a file in the image which contains a copyright statement. Permissible are up to 37 characters. --modification-date=YYYYMMDDhhmmsscc Set a timestring that overrides ISO image creation and modification timestamps literally. It must consist of 16 decimal digits which form YYYYMMDDhhmmsscc, with YYYY between 1970 and 2999. Time zone is GMT. It is supposed to match this GRUB line: search --fs-uuid --set YYYY-MM-DD-hh-mm-ss-cc E.g. 2010040711405800 is 7 Apr 2010 11:40:58 (+0 centiseconds). Among the influenced timestamps are: isohybrid MBR id, El Torito boot catalog file, HFS+ superblock. --application_use character|0xXY|disk_path Specify the content of the Application Use field which can take at most 512 bytes. If the parameter of this command is empty, then the field is filled with 512 0-bytes. If it is a single character, then it gets repeated 512 times. If it begins by "0x" followed by two hex digits [0-9a-fA-F], then the digits are read as byte value which gets repeated 512 times. Any other parameter text is used as disk_path to open a data file and to read up to 512 bytes from it. If the file is smaller than 512 bytes, then the remaining bytes in the field get set to binary 0.  File: xorrisofs.info, Node: Bootable, Next: SystemArea, Prev: ImageId, Up: Options 5.8 El Torito Bootable ISO images ================================= The precondition for a bootable ISO image is to have in the ISO image the files of a boot loader. The boot facilities of computers get directed to such files, which usually execute further program files from the ISO image. 'xorrisofs' can produce several kinds of boot block or boot record, which become part of the ISO image, and get interpreted by the according boot facility. An *El Torito* boot record points the bootstrapping facility to a boot catalog with one or more boot images, which are binary program files stored in the ISO image. The content of the boot image files is not in the scope of El Torito. xorriso composes the boot catalog according to the boot image files given and structured by options -b, -e, -eltorito-alt-boot, and --efi-boot. Often it contains only one entry. Normally the boot images are data files inside the ISO filesystem. By special path "-interval:appended_partition_NNN:all::" it is possible to refer to an appended partition. The number NNN gives the partition number as used with the corresponding option -append_partition. E.g.: -append_partition 2 0xef /tmp/efi.img -e -interval:appended_partition_2:all:: El Torito gets interpreted by boot facilities PC-BIOS and EFI. Most bootable GNU/Linux CDs are equipped with ISOLINUX or GRUB boot images for PC-BIOS. 'xorrisofs' supports the example options out of the ISOLINUX wiki, the options used in GRUB script grub-mkrescue, and the example in the FreeBSD AvgLiveCD wiki. For CD booting via boot facilities other than PC-BIOS and EFI, and for booting from USB sticks or hard disks, see the next section about the System Area. -b iso_rr_path Specify the boot image file which shall be mentioned in the current entry of the El Torito boot catalog. It will be marked as suitable for PC-BIOS. With boot images from ISOLINUX and GRUB this option should be accompanied by options -c , -no-emul-boot , -boot-load-size 4 , -boot-info-table. -eltorito-boot iso_rr_path Alias of -b. -eltorito-alt-boot Finalize the current El Torito boot catalog entry and begin a new one. A boot image file and all its necessary options shall be specified before option -eltorito-alt-boot. All further El Torito boot options apply to the new catalog entry. Up to 32 catalog entries are possible. -e iso_rr_path Specify the boot image file which shall be mentioned in the current entry of the El Torito boot catalog. It will be marked as suitable for EFI. Option -e should be followed by option -no-emul-boot and no other El Torito options before an eventual -eltorito-alt-boot. --efi-boot iso_rr_path Perform -eltorito-alt-boot, option -e with the given iso_rr_path, -no-emul-boot, and again -eltorito-alt-boot. This gesture is used for achieving EFI-bootability of the GRUB2 rescue CD. -eltorito-platform "x86"|"PPC"|"Mac"|"efi"|0xnn|nnn Set the Platform Id number for the next option -b or -eltorito-boot. The number may be chosen by a platform name or by a number between 0 and 255 (0x00 and 0xFF). "x86" = 0 is for PC-BIOS, "PPC" = 1 for some PowerPC systems, "Mac" = 2 for some MacIntosh systems, "efi" = 0xEF for EFI on modern PCs with x86 compatible CPUs or others. If the new platform id differs from the previous one, -eltorito-alt-boot gets performed. -boot-load-size number|"full" Set the number of 512-byte blocks to be loaded at boot time from the boot image in the current catalog entry. Non-emulating BIOS bootimages usually need a load size of 4. Nevertheless the default setting of mkisofs is to use the full size of the boot image rounded up to a multiple of 4 512-byte blocks. This default may be explicitly enforced by the word "full" instead of a number. EFI boot images usually get set the number of blocks occupied by the boot image file. El Torito cannot represent load sizes higher than 65535. -hard-disk-boot Mark the boot image in the current catalog entry as emulated hard disk. (Not suitable for any known boot loader.) -no-emul-boot Mark the boot image in the current catalog entry as not emulating floppy or hard disk. (This is to be used with all known boot loaders.) If neither -hard-disk-boot nor -no-emul-boot is given, then the boot image will be marked as emulating a floppy. (Not suitable for any known boot loader.) -eltorito-id text|56_hexdigits Define the ID string of the boot catalog section where the boot image will be listed. If the value consists of 56 characters [0-9A-Fa-f] then it is converted into 28 bytes, else the first 28 characters become the ID string. The ID string of the first boot image becomes the overall catalog ID. It is limited to 24 characters. Other id_strings become section IDs. -eltorito-selcrit hexdigits Define the Selection Criteria of the boot image. Up to 20 bytes get read from the given characters [0-9A-Fa-f]. They get attributed to the boot image entry in the catalog. -boot-info-table Overwrite bytes 8 to 63 in the current boot image. The information will be supplied by xorriso in the course of image production: Block address of the Primary Volume Descriptor, block address of the boot image file, size of the boot image file. --grub2-boot-info Overwrite bytes 2548 to 2555 in the current boot image by the address of that boot image. The address is written as 64 bit little-endian number. It is the 2KB block address of the boot image content, multiplied by 4, and then incremented by 5. -c iso_rr_path Set the address of the El Torito boot catalog file within the image. This file address is not significant for the booting PC-BIOS or EFI, but it may later be read by other programs in order to learn about the available boot images. -eltorito-catalog iso_rr_path Alias of -c. --boot-catalog-hide Prevent the El Torito boot catalog from appearing as file in the directory trees of the image.  File: xorrisofs.info, Node: SystemArea, Next: Charset, Prev: Bootable, Up: Options 5.9 System Area, MBR, GPT, APM, other boot blocks ================================================= The first 16 blocks of an ISO image are the System Area. It is reserved for system dependent boot software. This may be the boot facilities and partition tables of various hardware architectures. A *MBR* (Master Boot Record) contains boot code and a partition table. It is read by PC-BIOS when booting from USB stick or hard disk, and by PowerPC CHRP or PReP when booting. An MBR partition with type 0xee indicates the presence of GPT. A *GPT* (GUID Partition Table) marks partitions in a more modern way. It is read by EFI when booting from USB stick or hard disk, and may be used for finding and mounting a HFS+ partition inside the ISO image. An *APM* (Apple Partition Map) marks the HFS+ partition. It is read by Macs for booting and for mounting. MBR, GPT and APM are combinable. APM occupies the first 8 bytes of MBR boot code. All three do not hamper El Torito booting from CDROM. 'xorrisofs' supports further boot facilities: MIPS Big Endian (SGI), MIPS Little Endian (DEC), SUN SPARC, HP-PA, DEC Alpha. Those are mutually not combinable and also not combinable with MBR, GPT, or APM. Several of the following options expect disk paths as input but also accept description strings for the libisofs interval reader, which is able to cut out data from disk files or -indev and to zeroize parts of the content: -G, -generic-boot, --embedded-boot, --grub2-mbr, -isohybrid-mbr, -efi-boot-part, -prep-boot-part, -B, -sparc-boot, -append_partition. The description string consists of the following components, separated by colon ':' "--interval:"Flags":"Interval":"Zeroizers":"Source The component "--interval" states that this is not a plain disk path but rather a interval reader description string. The component Flags modifies the further interpretation: "local_fs" demands to read from a file depicted by the path in Source. "imported_iso" demands to read from the -indev. This works only if -outdev is not the same as -indev. The Source component is ignored. "appended_partition_NNN" with a decimal number NNN works only for options which announce El Torito boot image paths: -b, -e, -efi-boot. The number gives the partition number as used with the corresponding option -append_partition. The component Interval consists of two byte address numbers separated by a "-" character. E.g. "0-429" means to read bytes 0 to 429. The component Zeroizers consists of zero or more comma separated strings. They define which part of the read data to zeroize. Byte number 0 means the byte read from the Interval start address. Each string may be one of: "zero_mbrpt" demands to zeroize the MBR partition table if bytes 510 and 511 bear the MBR signature 0x55 0xaa. "zero_gpt" demands to check for a GPT header in bytes 512 to 1023, to zeroize it and its partition table blocks. "zero_apm" demands to check for an APM block 0 and to zeroize its partition table blocks. Start_byte"-"End_byte demands to zeroize the read-in bytes beginning with number Start_byte and ending after End_byte. The component Source is the file path with flag "local_fs", and ignored with flag "imported_iso". Byte numbers may be scaled by a suffix out of {k,m,g,t,s,d} meaning multiplication by {1024, 1024k, 1024m, 1024g, 2048, 512}. A scaled value end number depicts the last byte of the scaled range. E.g. "0d-0d" is "0-511". Examples: "local_fs:0-32767:zero_mbrpt,zero_gpt,440-443:/tmp/template.iso" "imported_iso:45056d-47103d::" -G disk_path Copy at most 32768 bytes from the given disk file to the very start of the ISO image. Other than a El Torito boot image, the file disk_path needs not to be added to the ISO image. It will not show up as file in the directory trees. In multi-session situations, the special disk_path "." prevents reading of a disk file but nevertheless causes the adjustments in the existing MBR, which were ordered by other options. -generic-boot disk_path Alias of -G. --embedded-boot disk_path Alias of -G. --grub2-mbr disk_path Install disk_path in the System Area and treat it as modern GRUB2 MBR. The content start address of the first boot image is converted to a count of 512 byte blocks, and an offset of 4 is added. The result is written as 64 bit little-endian number to byte address 0x1b0. -isohybrid-mbr disk_path Install disk_path as ISOLINUX isohybrid MBR which makes the boot image given by option -b bootable from USB sticks and hard disks via PC-BIOS. This preparation is normally done by ISOLINUX program isohybrid on the already produced ISO image. The disk path should lead to one of the Syslinux files isohdp[fp]x*.bin . The MBR gets patched according to isohybrid needs. The first partition describes the range of the ISO image. Its start is at block 0 by default, but may be set to 64 disk blocks by option -partition_offset 16. For the meaning of special disk_path "." see option -G. -isohybrid-gpt-basdat Mark the current El Torito boot image (see options -b and -e) in an actually invalid GPT as partition of type Basic Data. This works only with -isohybrid-mbr and has the same impact on the system area as -efi-boot-part. It cannot be combined with -efi-boot-part or -hfsplus. The first three boot images which are marked by GPT will also show up as partition entries in MBR. The MBR partition of type 0xEF is what actually is used by EFI firmware for booting from USB stick. The MBR partition for PC-BIOS gets type 0x00 rather than 0x17 in this case. Often the further MBR entries are the ones which actually get used by EFI. -isohybrid-gpt-hfsplus Mark the current El Torito boot image (see options -b and -e) in GPT as partition of type HFS+. Impact and restrictions are like with -isohybrid-gpt-basdat. -isohybrid-apm-hfsplus Mark the current El Torito boot image (see options -b and -e) in Apple Partition Map as partition of type HFS+. This works only with -isohybrid-mbr and has a similar impact on the system area as -hfsplus. It cannot be combined with -efi-boot-part or -hfsplus. The ISOLINUX isohybrid MBR file must begin by a known pattern of 32 bytes of x86 machine code which essentially does nothing. It will get overwritten by 32 bytes of APM header mock-up. -part_like_isohybrid Control whether -isohybrid-gpt-basdat, -isohybrid-gpt-hfsplus, and -isohybrid-apm-hfsplus apply even if not -isohybrid-mbr is present. No MBR partition of type 0xee emerges, even if GPT gets produced. Gaps between GPT and APM partitions will not be filled by more partitions. Appended partitions get mentioned in APM if other APM partitions emerge. -iso_mbr_part_type "default"|number|type_guid Set the partition type of the MBR or GPT partition which represents the ISO or at least protects it. Number may be 0x00 to 0xff. The text "default" re-enables the default types of the various occasions to create an ISO MBR partition. This is without effect if no such partition emerges by other settings or if the partition type is prescribed mandatorily like 0xee for GPT protective MBR or 0x96 for CHRP. If instead a type_guid is given by a 32-digit hex string like a2a0d0ebe5b9334487c068b6b72699c7 or by a structured text like EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, then it will be used as partition type if the ISO filesystem appears as partition in GPT. In MBR, C12A7328-F81F-11D2-BA4B-00A0C93EC93B will be mapped to 0xef. Any other GUID will be mapped to 0x83. --protective-msdos-label Patch the System Area by a simple PC-DOS partition table where partition 1 claims the range of the ISO image but leaves the first block unclaimed. This is mutally exclusive to option -isohybrid-mbr. --mbr-force-bootable Enforce an MBR partition with "bootable/active" flag if options like --protective-msdos-label or --grub2-mbr are given. These options normally cause the flag to be set if there is an MBR partition of type other than 0xee or 0xef. If no such partition exists, then no bootflag is set, unless --mbr-force-bootable forces creation of a dummy partition of type 0x00 which covers only the first block of the ISO image. If no bootable MBR is indicated by other options and a partition gets created by -append_partition, then --mbr-force-bootable causes a bootflag like it would do with e.g. --protective-msdos-label. --gpt-iso-bootable Set bit 2 of the GPT partition flags for the ISO 9660 partition if such a GPT partition emerges. This bit is specified as "Legacy BIOS bootable" but its true significance is unclear. Some GPT-aware BIOS might want to see it in some partition. --gpt-iso-not-ro Do not set bit 60 of the GPT partition flags for the ISO 9660 partition if such a GPT partition emerges. This bit is specified as "Read-only" and thus appropriate. But it is unusual in GPT disk partitions. -partition_offset 2kb_block_adr Cause a partition table with a single partition that begins at the given block address. This is counted in 2048 byte blocks, not in 512 byte blocks. If the block address is non-zero then it must be at least 16. Values larger than 16 are hardly of use. A non-zero partition offset causes two superblocks to be generated and two sets of directory trees. The image is then mountable from its absolute start as well as from the partition start. The offset value of an ISO image gets preserved when a new session is added to a loaded image. So the value defined here is only in effect if a new ISO image gets written. -partition_hd_cyl number Set the number of heads per cylinder for the MBR partition table. 0 chooses a default value. Maximum is 255. -partition_sec_hd number Set the number of sectors per head for the MBR partition table. 0 chooses a default value. Maximum is 63. The product partition_sec_hd * partition_hd_cyl * 512 is the cylinder size. It should be divisible by 2048 in order to make exact alignment possible. With appended partitions and -appended_part_as_gpt there is no limit for the number of cylinders. Else there may be at most 1024 of them. If the cylinder size is too small to stay below the limit, then appropriate values of partition_hd_cyl are chosen with partition_sec_hd 32 or 63. If the image is larger than 8,422,686,720 bytes, then the cylinder size constraints cannot be fulfilled for MBR. They seem not overly important anyway. Flat block addresses in partition tables are good for 1 TiB. -partition_cyl_align mode Control image size alignment to an integer number of cylinders. It is prescribed by isohybrid specs and it seems to please program fdisk. Cylinder size must be divisible by 2048. Images larger than 8,323,596,288 bytes cannot be aligned in MBR partition table. Mode "auto" is default. Alignment by padding happens only if option -isohybrid-mbr is given. Mode "on" causes alignment by padding with option --protective-msdos-label too. Mode "all" is like "on" but also pads up partitions from -append_partition to an aligned size. Mode "off" disables alignment unconditionally. -append_partition partition_number type_code disk_path Cause a prepared filesystem image to be appended to the ISO image and to be described by a partition table entry in a boot block at the start of the emerging ISO image. The partition entry will bear the size of the submitted file rounded up to the next multiple of 2048 bytes or to the next multiple of the cylinder size. Beware of subsequent multi-session runs. The appended partition will get overwritten. partition_number may be 1 to 4. Number 1 will put the whole ISO image into the unclaimed space before partition 1. So together with most xorriso MBR or GPT features, number 2 would be the most natural choice. The type_code may be "FAT12", "FAT16", "Linux", or a hexadecimal number between 0x00 and 0xff. Not all those numbers will yield usable results. For a list of codes search the Internet for "Partition Types" or run fdisk command "L". If the partition appears in GPT then type_code 0xef is mapped to the EFI System Partition Type GUID. All others get mapped to Basic Data Type GUID. type_code may also be a type GUID as plain hex string like a2a0d0ebe5b9334487c068b6b72699c7 or as structured text like EBD0A0A2-B9E5-4433-87C0-68B6B72699C7. It will be used if the partition is mentioned in GPT. In MBR, C12A7328-F81F-11D2-BA4B-00A0C93EC93B will be mapped to 0xef. Any other GUID will be mapped to 0x83. In APM, 48465300-0000-11AA-AA11-00306543ECAC will be mapped to partition type "Apple_HFS", any other to "Data". If some other command causes the production of GPT, then the appended partitions will be mentioned there too, even if not -appended_part_as_gpt is given. -appended_part_as_gpt Marks partitions from -append_partition in GPT rather than in MBR. In this case the MBR shows a single partition of type 0xee which covers the whole output data. By default, appended partitions get marked in GPT only if GPT is produced because of other options. This option raises the maximum number of appended partitions from 4 to 8. But it is not guaranteed that the resulting GPT partition will have the given partition_number. Other GPT partitions may emerge. The final sorting by start block address may put one of them in the partition entry with the desired number, so that the appended partition will get a higher number. Given MBR partition types get translated. 0xef becomes C12A7328-F81F-11D2-BA4B-00A0C93EC93B, others become EBD0A0A2-B9E5-4433-87C0-68B6B72699C7. -appended_part_as_apm Marks partitions from -append_partition in Apple Partition Map, too. The partition number in APM will not be influenced by -append_partition parameter partition_number. By default, appended partitions get marked in APM only if APM is produced because of other options and -part_like_isohybrid is enabled. -efi-boot-part disk_path Copy a file from disk into the emerging ISO image and mark it by a GPT entry as EFI System Partition. EFI boot firmware is supposed to use a FAT filesystem image in such a partition for booting from USB stick or hard disk. Instead of a disk_path, the word --efi-boot-image may be given. It exposes in GPT the content of the first El Torito EFI boot image as EFI system partition. EFI boot images are introduced by options -e or --efi-boot. The affected EFI boot image cannot show up in HFS+ because it is stored outside the HFS+ partition. --gpt_disk_guid value Control whether an emerging GPT shall get a randomly generated disk GUID or whether the GUID is supplied by the user. Value "random" is default. Value "modification-date" produces a low quality GUID from the value set by option --modification-date=. A string of 32 hex digits, or a RFC 4122 compliant GUID string may be used to set the disk GUID directly. UEFI prescribes the first three components of a RFC 4122 GUID string to be byte-swapped in the binary representation: E.g. --gpt_disk_guid 2303cd2a-73c7-424a-a298-25632da7f446 equals --gpt_disk_guid 2acd0323c7734a42a29825632da7f446 The partition GUIDs get generated by minimally varying the disk GUID. -chrp-boot-part Mark the block range of the whole emerging ISO image as MBR partition of type 0x96. This is not compatible with any other feature that produces MBR partition entries. It makes GPT unrecognizable. CHRP is often used in conjunction with HFS. It is not yet tested whether HFS+ filesystems produced with option -hfsplus would boot on any CHRP capable machine which does not boot pure ISO 9660 as well. -chrp-boot Alias of -chrp-boot-part. -prep-boot-part disk_path Copy a file from disk into the emerging ISO image and mark it by a MBR partition entry of type 0x41. PReP boot firmware is supposed to read the content of the partition as single ELF executable file. This option is compatible with other MBR partitions and with GPT. -mips-boot iso_rr_path Declare a data file in the image to be a MIPS Big Endian boot file and cause production of a MIPS Big Endian Volume Header. This is mutually exclusive with production of other boot blocks like MBR. It will overwrite the first 512 bytes of any data provided by -G. Up to 15 boot files can be declared by multiple -mips-boot options. -mipsel-boot iso_rr_path Declare a data file in the image to be the MIPS Little Endian boot file. This is mutually exclusive with other boot blocks. It will overwrite the first 512 bytes of any data provided by -G. Only a single boot file can be declared by -mipsel-boot. -B disk_path[,disk_path ...] Cause one or more data files on disk to be written after the end of the ISO image. A SUN Disk Label will be written into the first 512 bytes of the ISO image which lists this image as partition 1 and the given disk_paths as partition 2 up to 8. The disk files should contain suitable boot images for SUN SPARC systems. The pseudo disk_path "..." causes that all empty partition entries become copies of the last non-empty entry. If no other disk_path is given before "..." then all partitions describe the ISO image. In this case, the boot loader code has to be imported by option -G. -sparc-boot disk_path[,disk_path ...] Alias of -B. -sparc-label text Set the ASCII label text of a SUN Disk Label. --grub2-sparc-core iso_rr_path Cause the content address and size of the given data file in the image to be written after the SUN Disk Label. Both numbers are counted in bytes. The address is written as 64 bit big-endian number to byte 0x228. The size is written as 32 bit big-endian number to byte 0x230. -hppa-cmdline text Set the PALO command line for HP-PA. Up to 1023 characters are permitted by default. With -hppa-hdrversion 4 the limit is 127. Note that the first five -hppa options are mandatory, if any of the -hppa options is given. Only option -hppa-hdrversion is allowed to be missing. -hppa-bootloader iso_rr_path Designate the given path as HP-PA bootloader file. -hppa-kernel-32 iso_rr_path Designate the given path as HP-PA 32 bit kernel file. -hppa-kernel-64 iso_rr_path Designate the given path as HP-PA 64 bit kernel file. -hppa-ramdisk iso_rr_path Designate the given path as HP-PA RAM disk file. -hppa-hdrversion number Choose between PALO header version 5 (default) and version 4. For the appropriate value see in PALO source code: PALOHDRVERSION. -alpha-boot iso_rr_path Declare a data file in the image to be the DEC Alpha SRM Secondary Bootstrap Loader and cause production of a boot sector which points to it. This is mutually exclusive with production of other boot blocks like MBR.  File: xorrisofs.info, Node: Charset, Next: Jigdo, Prev: SystemArea, Up: Options 5.10 Character sets =================== Character sets should not matter as long as only english alphanumeric characters are used for file names or as long as all writers and readers of the medium use the same character set. Outside these constraints it may be necessary to let xorriso convert byte codes. A conversion from input character set to the output character set is performed when an ISO image gets written. Vice versa there is a conversion from output character set to the input character set when an ISO image gets loaded. The sets can be defined by options -input-charset and -output-charset, if needed. -input-charset character_set_name Set the character set from which to convert disk file names when inserting them into the ISO image. -output-charset character_set_name Set the character set from which to convert names of loaded ISO images and to which to convert names when writing ISO images.  File: xorrisofs.info, Node: Jigdo, Next: Miscellaneous, Prev: Charset, Up: Options 5.11 Jigdo Template Extraction ============================== From man genisoimage: "Jigdo is a tool to help in the distribution of large files like CD and DVD images; see http://atterer.net/jigdo/ for more details. Debian CDs and DVD ISO images are published on the web in jigdo format to allow end users to download them more efficiently." If the use of libjte was enabled at compile time of xorriso, then 'xorrisofs' can produce a .jigdo and a .template file together with a single-session ISO image. If not, then Jigdo options will cause a FAILURE event, which normally leads to program abort. One may determine the ability for Jigdo by: $ xorrisofs -version 2>&1 | grep '^libjte' && echo YES The .jigdo file contains checksums and symbolic file addresses. The .template file contains the compressed ISO image with reference tags instead of the content bytes of the listed files. Input for this process are the normal arguments for a 'xorrisofs' session with no image loaded, and a checksum file which lists those data files which may be listed in the .jigdo file and externally referenced in the .template file. Each designated file is represented in the checksum file by a single text line: Checksum as hex digits, 2 blanks, size as 12 decimal digits or blanks, 2 blanks, symbolic file address The kind of checksum is chosen by -jigdo "checksum_algorithm" with values "md5" (32 hex digits) or "sha256" (64 hex digits). It will also be used for the file address lines in the .jigdo file. The default is "md5". The file address in a checksum file line has to bear the same basename as the disk_path of the file which it shall match. The directory path of the file address is decisive for To=From mapping, not for file recognition. After To=From mapping, the file address gets written into the .jigdo file. Jigdo restore tools will convert these addresses into really reachable data source addresses from which they can read. If the list of jigdo parameters is not empty, then padding will be counted as part of the ISO image. -jigdo-checksum-algorithm "md5"|"sha256" Set the checksum algorithm which shall be used for the data file entries in the .jigdo file and is expected in the checksum file. Default is "md5". -jigdo-jigdo disk_path Set the disk_path for the .jigdo file with the checksums and download addresses for filling the holes in .template. -jigdo-template disk_path Set the disk_path for the .template file with the holed and compressed ISO image copy. -jigdo-min-file-size size Set the minimum size for a data file to be listed in the .jigdo file and being a hole in the .template file. size may be a plain number counting bytes, or a number with appended letter "k", "m", "g" to count KiB (1024 bytes), MiB (1024 KiB), or GiB (1024 MiB). -jigdo-force-checksum disk_path_pattern adds a regular expression pattern which will get compared with the absolute disk_path of any data file that was not found in the checksum file. A match causes a MISHAP event, which normally does not abort the program run but finally causes a non-zero exit value of the program. -jigdo-force-md5 disk_path_pattern Outdated alias of -jigdo-force-checksum. -jigdo-exclude disk_path_pattern Add a regular expression pattern which will get compared with the absolute disk_path of any data file. A match causes the file to stay in .template in any case. -jigdo-map To=From Add a string pair of the form To=From to the parameter list. If a data file gets listed in the .jigdo file, then it is referred by the file address from its line in the checksum file. This file address gets checked whether it begins with the From string. If so, then this string will be replaced by the To string and a ':' character, before it goes into the .jigdo file. The From string should end by a '/' character. -checksum-list disk_path Set the disk_path where to find the checksum file file with symbolic file addresses and checksums according to -jigdo-checksum-algorithm. -md5-list disk_path Outdated alias of -checksum-list. -jigdo-template-compress "gzip"|"bzip2" Choose one of "bzip2" or "gzip" for the compression of the template file. The jigdo file is put out uncompressed. -checksum_algorithm_iso list_of_names Choose one or more of "md5", "sha1", "sha256", "sha512" for the auxiliary "# Image Hex" checksums in the .jigdo file. The list_of_names may e.g. look like "md5,sha1,sha512". Value "all" chooses all available algorithms. Note that MD5 stays always enabled. -checksum_algorithm_template list_of_names Choose the algorithms for the "# Template Hex" checksums in the .jigdo file. The rules for list_of_names are the same as with -checksum_algorithm_iso.  File: xorrisofs.info, Node: Miscellaneous, Next: ExSimple, Prev: Jigdo, Up: Options 5.12 Miscellaneous options ========================== -genisoimage_completion Match unrecognized option arguments which begin by a dash '-' against the known genisoimage options, like program genisoimage does unconditionally (and undocumentedly). If the given argument matches the beginning of exactly one genisoimage option, then it gets replaced by that option. Options which are genuine to mkisofs or xorriso's mkisofs emulation are not matched that way. Option arguments which consist entirely of a leading dash and letters out of "dDfJlNRrTUvz" are not matched but rather interpreted as usual, i.e. as multiple options with leading dash and each single letter. If no genisoimage option is found or more than one are found, then a SORRY message is issued and the argument stays as is. To enable this mode by default, write the xorriso command -genisoimage_completion on into one of the xorriso start files. See section FILES. -print-size Print to stdandard output the foreseeable number of 2048 byte blocks in the emerging ISO image. Do not produce this image. The result depends on several settings. If option -emul-toc is given, then padding (see -pad) is not counted as part of the image size. In this case either use -no-pad or add 150 (= 300 KiB) to the resulting number. If mkisofs emulation ends after option -print-size, then the properties of the most recently specified boot image file cannot be edited by subsequent xorriso commands. --no_rc Only if used as first argument this option prevents reading and interpretation of startup files. See section FILES below. -help List supported options to stderr. Original mkisofs options bear their original mkisofs description texts. -quiet Suppress most messages of the program run, except those which indicate problems or errors. -gui Increase the frequency of pacifier messages while writing an ISO image. -log-file disk_path Truncate file disk_path to 0 size and redirect to it all messages which would normally appear on stderr. -log-file with empty text as disk_path re-enables output to stderr. -v Enable the output of informational program messages. -verbose Alias of -v. -version Print to standard output a text that begins with "mkisofs 2.01-Emulation Copyright (C)" and to standard error the version information of xorriso.  File: xorrisofs.info, Node: Examples, Next: Files, Prev: Options, Up: Top 6 Examples ********** * Menu: * ExSimple:: A simple image production run * ExGraft:: Set ISO image paths by -graft-points * ExMkisofs:: Perform multi-session runs * ExGrowisofs:: Let xorriso work underneath growisofs * ExIncBackup:: Incremental backup of a few directory trees * ExIncBckAcc:: Incremental backup with accumulated trees * ExBootable:: Create bootable images for PC-BIOS and EFI  File: xorrisofs.info, Node: ExSimple, Next: ExGraft, Prev: Miscellaneous, Up: Examples 6.1 A simple image production run ================================= A prepared file tree in directory ./for_iso gets copied into the root directory of the ISO image. File permissions get set to read-only for everybody. Joliet attributes for Microsoft systems get added. The resulting image gets written as data file ./image.iso on disk. $ xorrisofs -r -J -o ./image.iso ./for_iso  File: xorrisofs.info, Node: ExGraft, Next: ExMkisofs, Prev: ExSimple, Up: Examples 6.2 Set ISO image paths by -graft-points ======================================== Without option -graft-points each given disk file is copied into the root directory of the ISO image, maintaining its name. If a directory is given, then its files and sub-directories are copied into the root directory, maintaining their names. $ xorrisofs ... /home/me/datafile /tmp/directory yields in the ISO image root directory: /datafile /file_1_from_directory ... /file_N_from_directory With option -graft-points it is possible to put files and directories to arbitrary paths in the ISO image. $ xorrisofs ... -graft-points /home/me/datafile /dir=/tmp/directory yields in the ISO image root directory: /datafile /dir Eventually needed parent directories in the image will be created automatically: /datafiles/file1=/home/me/datafile yields in the ISO image: /datafiles/file1 The attributes of directory /datafiles get copied from /home/me on disk. Normally one should avoid = and \ characters in the ISO part of a pathspec. But if it must be, one may escape them: /with_\=_and_\\/file=/tmp/directory/file yields in the ISO image: /with_=_and_\/file  File: xorrisofs.info, Node: ExMkisofs, Next: ExGrowisofs, Prev: ExGraft, Up: Examples 6.3 Perform multi-session runs ============================== This example works for multi-session media only: CD-R[W], DVD-R[W], DVD+R, BD-R. Add cdrskin option --grow_overwriteable_iso to all -as cdrecord runs in order to enable multi-session emulation on overwritable media. The first session is written like this: $ xorrisofs -graft-points \ /tree1=prepared_for_iso/tree1 \ | xorriso -as cdrecord -v dev=/dev/sr0 blank=fast -multi -eject - Follow-up sessions are written like this (the run of dd is only to give demons a chance to spoil it): $ m=$(xorriso -as cdrecord dev=/dev/sr0 -msinfo) $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 $ xorrisofs -M /dev/sr0 -C $m -graft-points \ /tree2=prepared_for_iso/tree2 \ | xorriso -as cdrecord -v dev=/dev/sr0 -waiti -multi -eject - Always eject the drive tray between sessions. The run of xorriso -as mkisofs will read old sessions via the CD-ROM driver of /dev/sr0. This driver might not be aware of the changed content as long as the medium is not loaded again. In this case the previous session would not be properly assessed by xorriso and the new session would contain only the newly added files. Some systems have not enough patience with automatic tray loading and some demons may interfere with a first CD-ROM driver read attempt from a freshly loaded medium. When loading the tray manually, wait 10 seconds after the drive has stopped blinking. A safe automatic way seems to be a separate run of xorriso for loading the tray with proper waiting, and a subsequent run of dd which shall offer itself to any problems caused by demons assessing the changed drive status. If this does not help, insert a run of "sleep 10" between xorriso and dd.  File: xorrisofs.info, Node: ExGrowisofs, Next: ExIncBackup, Prev: ExMkisofs, Up: Examples 6.4 Let xorriso work underneath growisofs ========================================= growisofs expects an ISO formatter program which understands options -C and -M. A variable is defined to override the hardcoded default name. $ export MKISOFS="xorrisofs" $ growisofs -Z /dev/dvd /some/files $ growisofs -M /dev/dvd /more/files If no "xorrisofs" is available on your system, then you will have to create a link pointing to the xorriso binary and tell growisofs to use it. E.g. by: $ ln -s $(which xorriso) "$HOME/xorrisofs" $ export MKISOFS="$HOME/xorrisofs" One may quit mkisofs emulation by argument "--" and make use of all xorriso commands. growisofs dislikes options which start with "-o" but -outdev must be set to "-". So use "outdev" instead: $ growisofs -Z /dev/dvd --for_backup -- \ outdev - -update_r /my/files /files $ growisofs -M /dev/dvd --for_backup -- \ outdev - -update_r /my/files /files Note that --for_backup is given in the mkisofs emulation. To preserve the recorded extra data it must already be in effect, when the emulation loads the image.  File: xorrisofs.info, Node: ExIncBackup, Next: ExIncBckAcc, Prev: ExGrowisofs, Up: Examples 6.5 Incremental backup of a few directory trees =============================================== This changes the directory trees /open_source_project and /personal_mail in the ISO image so that they become exact copies of their disk counterparts. ISO file objects get created, deleted or get their attributes adjusted accordingly. ACL, xattr, hard links and MD5 checksums will be recorded. It is expected that inode numbers in the disk filesystem are persistent over cycles of mounting and booting. Files with names matching *.o or *.swp get excluded explicitly. To be used several times on the same medium, whenever an update of the two disk trees to the medium is desired. Begin with a blank medium and update it until he run fails gracefully due to lack of remaining space on the old one. Always eject the drive tray between sessions. A run of dd shall give demons a chance to spoil the first read on freshly loaded media. $ msinfo=$(xorriso -as cdrecord dev=/dev/sr0 -msinfo) $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 $ load_opts= $ test -n "$msinfo" && load_opts="-M /dev/sr0 -C $msinfo" $ xorrisofs $load_opts -o - --for_backup -m '*.o' -m '*.swp' \ -V PROJ_MAIL_"$(date '+%Y_%m_%d_%H%M%S')" -graft-points \ -old-root / \ /projects=/home/thomas/projects \ /personal_mail=/home/thomas/personal_mail \ | xorriso -as cdrecord dev=/dev/sr0 -v -multi -waiti -eject - This makes sense if the full backup leaves substantial remaining capacity on media and if the expected changes are much smaller than the full backup. *Better do not use your youngest backup for -old-root*. Have at least two media which you use alternatingly. So only older backups get endangered by the new write operation, while the newest backup is stored safely on a different medium. Always have a blank medium ready to perform a full backup in case the update attempt fails due to insufficient remaining capacity. This failure will not spoil the old medium, of course. If inode numbers on disk are not persistent, then use option --old-root-no-ino . In this case an update run will compare recorded MD5 sums against the current file content on hard disk. With *mount* option *-o "sbsector="* on GNU/Linux or *-s* on FreeBSD or NetBSD it is possible to access the session trees which represent the older backup versions. With CD media, GNU/Linux mount accepts session numbers directly by its option "session=". Multi-session media and most overwritable media written by xorriso can tell the sbsectors of their sessions by xorriso option -toc: $ xorriso -dev /dev/sr0 -toc xorriso can print the matching mount command for a session number: $ xorriso -mount_cmd /dev/sr0 session 12 /mnt or for a volume id that matches a search expression: $ xorriso -mount_cmd /dev/sr0 volid '*2008_12_05*' /mnt Both yield on standard output something like: mount -t iso9660 -o nodev,noexec,nosuid,ro,sbsector=1460256 '/dev/sr0' '/mnt' The superuser may let xorriso execute the mount command directly: # osirrox -mount /dev/sr0 "volid" '*2008_12_05*' /mnt  File: xorrisofs.info, Node: ExIncBckAcc, Next: ExBootable, Prev: ExIncBackup, Up: Examples 6.6 Incremental backup with accumulated trees ============================================= Solaris does not offer the option to mount older sessions. In order to keep them accessible, one may map all files to a file tree under a session directory and accumulate those directories from session to session. The -root tree is cloned from the -old-root tree before it gets compared with the appropriate trees on disk. This demands to know the previously used session directory name. With the first session: $ xorrisofs -root /session1 \ -o - --for_backup -m '*.o' -m '*.swp' \ -V PROJ_MAIL_"$(date '+%Y_%m_%d_%H%M%S')" -graft-points \ /projects=/home/thomas/projects \ /personal_mail=/home/thomas/personal_mail \ | xorriso -as cdrecord dev=/dev/sr0 -v blank=as_needed \ -multi -waiti -eject - With the second session, option -old-root refers to /session1 and the new -root is /session2. Always eject the drive tray between sessions. A run of dd shall give demons a chance to spoil the first read on freshly loaded media. $ msinfo=$(xorriso -as cdrecord dev=/dev/sr0 -msinfo) $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 $ load_opts= $ test -n "$msinfo" && load_opts="-M /dev/sr0 -C $msinfo" $ xorrisofs $load_opts -root /session2 -old-root /session1 \ -o - --for_backup -m '*.o' -m '*.swp' \ -V PROJ_MAIL_"$(date '+%Y_%m_%d_%H%M%S')" -graft-points \ /projects=/home/thomas/projects \ /personal_mail=/home/thomas/personal_mail \ | xorriso -as cdrecord dev=/dev/sr0 -v -multi -waiti -eject - With the third session, option -old-root refers to /session2. The new -root is /session3. And so on.  File: xorrisofs.info, Node: ExBootable, Prev: ExIncBckAcc, Up: Examples 6.7 Create bootable images for PC-BIOS and EFI ============================================== The SYSLINUX/ISOLINUX boot loader suite is popular for booting PC-BIOS. The ISOLINUX wiki prescribes to create on disk a directory ./CD_root and to copy all desired files underneath that directory. Especially file isolinux.bin shall be copied to ./CD_root/isolinux/isolinux.bin . This is the boot image file. The prescribed mkisofs options can be used unchanged with 'xorrisofs': $ xorrisofs -o output.iso \ -b isolinux/isolinux.bin -c isolinux/boot.cat \ -no-emul-boot -boot-load-size 4 -boot-info-table \ ./CD_root Put it on CD by a burn program. E.g.: $ xorriso -as cdrecord -v dev=/dev/sr0 blank=as_needed output.iso The image from above example will boot from CD, DVD or BD, but not from USB stick or other hard-disk-like devices. This can be done by help of an isohybrid MBR. Syslinux provides matching template files as isohdp[fp]x*.bin . E.g. /usr/lib/syslinux/isohdpfx.bin . If a few hundred KB of size do not matter, then option -partition_offset can be used to create a partition table where partition 1 starts not at block 0. This facilitates later manipulations of the USB stick by tools for partitioning and formatting. The image from the following example will be prepared for booting via MBR and its first partition will start at hard disk block 64. It will also boot from optical media. $ xorrisofs -o output.iso \ -b isolinux/isolinux.bin -c isolinux/boot.cat \ -no-emul-boot -boot-load-size 4 -boot-info-table \ -isohybrid-mbr /usr/lib/syslinux/isohdpfx.bin \ -partition_offset 16 \ ./CD_root Become superuser and copy the image to the unpartitioned base device file of the USB stick. On GNU/Linux this is e.g. /dev/sdb, not /dev/sdb1. CAUTION: This will overwrite any partitioning on the USB stick and make remaining data unaccessible. So first make sure you got the correct address of the intended device. E.g. by reading 100 MiB data from it and watching it blinking: # dd bs=2K if=/dev/sdb count=50K >/dev/null Now copy the image onto it # dd bs=2K if=output.iso of=/dev/sdb Now for EFI: The boot image file has to be the image of an EFI System Partition, i.e. a FAT filesystem with directory /EFI/BOOT and boot files with EFI prescribed names: BOOTIA32.EFI for 32 bit x86, BOOTx64.EFI for 64 bit AMD/x86 (in UEFI-2.4 there is indeed a lower case "x"), BOOTAA64.EFI for 64 bit ARM. The software in the FAT filesystem should be able to find and inspect the ISO filesystem for boot loader configuration and start of operating system. GRUB2 program grub-mkimage can produce such a FAT filesystem with suitable content, which then uses further GRUB2 software from the ISO filesystem. EFI boot equipment may be combined with above ISOLINUX isohybrid for PC-BIOS in a not really UEFI-2.4 compliant way, which obviously works well. It yields MBR and GPT partition tables, both with nested partitions. Assumed the EFI System Partition image is ready as ./CD_root/boot/grub/efi.img, add the following options before the directory address ./CD_root: -eltorito-alt-boot -e 'boot/grub/efi.img' -no-emul-boot \ -isohybrid-gpt-basdat \ More compliant with UEFI-2.4 is to decide for either MBR or GPT and to append a copy of the EFI System Partition in order to avoid overlap of ISO partition and EFI partition. Here for MBR: -eltorito-alt-boot -e 'boot/grub/efi.img' -no-emul-boot \ -append_partition 2 0xef ./CD_root/boot/grub/efi.img \ The resulting ISOs are supposed to boot from optical media and USB stick. One may omit option -eltorito-alt-boot if no option -b is used to make the ISO bootable via PC-BIOS. For ISOs with pure GRUB2 boot equipment consider to use GRUB2 tool grub-mkrescue as frontend to xorrisofs. If you have a bootable ISO filesystem and want to know its equipment plus a proposal how to reproduce it, try: $ xorriso -hfsplus on -indev IMAGE.iso \ -report_el_torito plain -report_system_area plain \ -print "" -print "======= Proposal for xorrisofs options:" \ -report_el_torito as_mkisofs  File: xorrisofs.info, Node: Files, Next: Environ, Prev: Examples, Up: Top 7 Files ******* 7.1 Startup Files ================= If not -no_rc is given as the first argument then 'xorrisofs' attempts on startup to read and execute lines from the following files: /etc/default/xorriso /etc/opt/xorriso/rc /etc/xorriso/xorriso.conf $HOME/.xorrisorc The files are read in the sequence given here, but none of them is required to exist. The lines are not interpreted as 'xorrisofs' options but as generic xorriso commands. See man xorriso. After the xorriso startup files, the program tries one by one to open for reading: ./.mkisofsrc $MKISOFSRC $HOME/.mkisofsrc $(dirname $0)/.mkisofsrc On success it interprets the file content and does not try further files. The last address is used only if start argument 0 has a non-trivial dirname. The reader currently interprets the following NAME=VALUE pairs: APPI default for -A PUBL default for -publisher SYSI default for -sysid VOLI default for -V VOLS default for -volset Any other lines will be silently ignored.  File: xorrisofs.info, Node: Environ, Next: Seealso, Prev: Files, Up: Top 8 Environ ********* The following environment variables influence the program behavior: HOME is used to find xorriso and mkisofs startup files. MKISOFSRC may be used to point the program to a mkisofs startup file. SOURCE_DATE_EPOCH belongs to the specs of reproducible-builds.org. It is supposed to be either undefined or to contain a decimal number which tells the seconds since january 1st 1970. If it contains a number, then it is used as time value to set the default of --modification-date=. --gpt_disk_guid defaults to "modification-date". The default of --set_all_file_dates is then "set_to_mtime". Further the "now" time for ISO nodes without disk source is then set to the SOURCE_DATE_EPOCH value. Startup files and program options can override the effect of SOURCE_DATE_EPOCH.  File: xorrisofs.info, Node: Seealso, Next: Bugreport, Prev: Environ, Up: Top 9 See also ********** For generic 'xorriso' command mode xorriso(1) For the cdrecord emulation of 'xorriso' xorrecord(1) For mounting xorriso generated ISO 9660 images (-t iso9660) mount(8) Other programs which produce ISO 9660 images mkisofs(8), genisoimage(1) Programs which burn sessions to optical media growisofs(1), cdrecord(1), wodim(1), cdrskin(1), xorriso(1) ACL and xattr getfacl(1), setfacl(1), getfattr(1), setfattr(1) lsattr(1), chattr(1) MD5 checksums md5sum(1) On FreeBSD some commands differ: getextattr(8), setextattr(8), md5(1)  File: xorrisofs.info, Node: Bugreport, Next: Legal, Prev: Seealso, Up: Top 10 Reporting bugs ***************** To report bugs, request help, or suggest enhancements for 'xorriso', please send electronic mail to the public list . If more privacy is desired, mail to . Please describe what you expect 'xorriso' to do, the program arguments or dialog commands by which you tried to achieve it, the messages of 'xorriso', and the undesirable outcome of your program run. Expect to get asked more questions before solutions can be proposed.  File: xorrisofs.info, Node: Legal, Next: CommandIdx, Prev: Bugreport, Up: Top 11 Author, Copyright, Credits ***************************** 11.1 Author =========== Thomas Schmitt for libburnia-project.org 11.2 Copyright ============== Copyright (c) 2011 - 2024 Thomas Schmitt Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of xorriso. If you make use of the license to derive modified versions of xorriso then you are entitled to modify this text under that same license. 11.3 Credits ============ 'xorrisofs' is in part based on work by Vreixo Formoso who provides libisofs together with Mario Danic who also leads the libburnia team. Vladimir Serbinenko contributed the HFS+ filesystem code and related knowledge. Compliments towards Joerg Schilling whose cdrtools served me for ten years.  File: xorrisofs.info, Node: CommandIdx, Next: ConceptIdx, Prev: Legal, Up: Top 12 Alphabetic Command List ************************** [index] * Menu: * --acl Recording of ACLs: SetExtras. (line 106) * --application_use set Application Use field: ImageId. (line 79) * --boot-catalog-hide Hide El Torito boot catalog: Bootable. (line 121) * --efi-boot El Torito EFI boot image: Bootable. (line 59) * --embedded-boot Fill System Area e.g. by MBR: SystemArea. (line 79) * --emul-toc enable table-of-content emulation: SetProduct. (line 33) * --for_backup Enable backup fidelity: SetExtras. (line 92) * --gpt-iso-bootable Set Legacy BIOS bootable flag: SystemArea. (line 159) * --gpt-iso-not-ro Do not set Read-only flag: SystemArea. (line 164) * --gpt_disk_guid GPT GUID: SystemArea. (line 270) * --grub2-boot-info Patch El Torito boot image: Bootable. (line 109) * --grub2-mbr Install modern GRUB2 MBR: SystemArea. (line 81) * --grub2-sparc-core SUN SPARC core file: SystemArea. (line 325) * --hardlinks Recording of hardlink relations: SetExtras. (line 142) * --lfa_flags Recording of Linux file attributes: SetExtras. (line 125) * --mbr-force-bootable Enforce MBR bootable/active flag: SystemArea. (line 148) * --md5 Recording of MD5 checksums: SetExtras. (line 134) * --modification-date set ISO image timestamps: ImageId. (line 70) * --no-emul-toc no table-of-content emulation: SetProduct. (line 41) * --norock disable Rock Ridge production: SetExtras. (line 28) * --no_rc do not execute startup files: Miscellaneous. (line 33) * --old-empty old block addresses for empty files: SetProduct. (line 105) * --old-root-devno enable disk idevno with -old-root: SetInsert. (line 114) * --old-root-no-ino disable disk ino with -old-root: SetInsert. (line 103) * --old-root-no-md5 disable MD5 with -old-root: SetInsert. (line 121) * --protective-msdos-label Patch System Area partition table: SystemArea. (line 143) * --quoted_path_list read pathspecs from disk file: SetInsert. (line 12) * --scdbackup_tag Recording of MD5 checksum: SetExtras. (line 150) * --set_all_file_dates set all file timestamps: SetExtras. (line 35) * --sort-weight set block address sorting weight: SetProduct. (line 45) * --sort-weight-list set block address sorting weight: SetProduct. (line 56) * --sort-weight-patterns set block address sorting weight: SetProduct. (line 76) * --stdio_sync control forced output to disk files: SetProduct. (line 23) * --xattr Recording of any xattr: SetExtras. (line 119) * --xattr Recording of user xattr: SetExtras. (line 112) * --zisofs-version-2 enable recognition of zisofs2 files: SetInsert. (line 65) * --zisofs2-susp-z2 produce Z2 for version 2 instead of ZF: SetInsert. (line 76) * --zisofs2-susp-zf produce ZF for version 2 instead of Z2: SetInsert. (line 80) * -A set Application Id: ImageId. (line 34) * -abstract set Abstract File path: ImageId. (line 57) * -allow-lowercase lowercase in ISO file names: SetCompl. (line 42) * -alpha-boot DEC Alpha SRM bootloader: SystemArea. (line 348) * -appended_part_as_apm Appended partitions in APM: SystemArea. (line 253) * -appended_part_as_gpt Appended partitions in GPT: SystemArea. (line 238) * -append_partition Append MBR or GPT partition after image: SystemArea. (line 208) * -appid set Application Id: ImageId. (line 41) * -b El Torito PC-BIOS boot image: Bootable. (line 38) * -B SUN SPARC boot images: SystemArea. (line 310) * -biblio set Biblio File path: ImageId. (line 62) * -boot-info-table Patch El Torito boot image: Bootable. (line 104) * -boot-load-size El Torito boot image load size: Bootable. (line 72) * -c El Torito boot catalog name: Bootable. (line 114) * -C set load address and write address offset: Loading. (line 25) * -cdrecord-params set load address and write address offset: Loading. (line 41) * -checksum-list set path of input checksum file: Jigdo. (line 77) * -checksum_algorithm_iso choose .jigdo checksums: Jigdo. (line 86) * -checksum_algorithm_template choose .template checksums: Jigdo. (line 92) * -chrp-boot CHRP partition: SystemArea. (line 292) * -chrp-boot-part CHRP partition: SystemArea. (line 283) * -copyright set Copyright File path: ImageId. (line 66) * -cut_out insert piece of data file or device: SetInsert. (line 22) * -D allow deep directory hierarchies: SetExtras. (line 60) * -d omit trailing dot in ISO file names: SetCompl. (line 50) * -dev set path for loading existing ISO image: Loading. (line 23) * -dir-mode permissions for all directories: SetProduct. (line 87) * -disable-deep-relocation allow deep directory hierarchies: SetExtras. (line 72) * -disallow_dir_id_ext enforce ISO level 1 directory names: SetCompl. (line 23) * -e El Torito EFI boot image: Bootable. (line 53) * -efi-boot-part EFI boot partition: SystemArea. (line 260) * -eltorito-alt-boot begin next boot catalog entry: Bootable. (line 47) * -eltorito-boot El Torito PC-BIOS boot image: Bootable. (line 45) * -eltorito-catalog El Torito boot catalog name: Bootable. (line 119) * -eltorito-id El Torito boot section id string: Bootable. (line 93) * -eltorito-platform El Torito Platform Id: Bootable. (line 63) * -eltorito-selcrit El Torito boot selection criteria: Bootable. (line 100) * -exclude exclude disk files from inserting: SetInsert. (line 47) * -exclude-list exclude disk files from inserting: SetInsert. (line 54) * -f follow symbolic links on disk: SetInsert. (line 32) * -file-mode permissions for all data files: SetProduct. (line 92) * -file_name_limit curbs length of file names: SetExtras. (line 49) * -follow-links follow symbolic links on disk: SetInsert. (line 36) * -full-iso9660-filenames allow 31 characters in ISO file names: SetCompl. (line 59) * -G Fill System Area e.g. by MBR: SystemArea. (line 68) * -generic-boot Fill System Area e.g. by MBR: SystemArea. (line 77) * -genisoimage_completion completion of genisoimage options: Miscellaneous. (line 8) * -gid group assignment for all files: SetProduct. (line 83) * -graft-points enable target=source pathspecs: SetInsert. (line 38) * -gui increase frequency of pacifier messages: Miscellaneous. (line 44) * -hard-disk-boot El Torito boot image emulation: Bootable. (line 83) * -help list supported options: Miscellaneous. (line 36) * -hfs-bless HFS+ blessing ppc_bootdir: SetExtras. (line 233) * -hfs-bless-by HFS+ blessing: SetExtras. (line 223) * -hfsplus enable production of HFS+ partition: SetExtras. (line 176) * -hfsplus-block-size set APM block size: SetExtras. (line 213) * -hfsplus-block-size set HFS+ allocation block size: SetExtras. (line 209) * -hfsplus-file-creator-type HFS+ creator-type attribute: SetExtras. (line 220) * -hfsplus-serial-no set HFS+ serial number: SetExtras. (line 205) * -hide keep matching files invisible in ISO tree: SetHide. (line 8) * -hide-hfsplus keep matching files invisible in HFS+ tree: SetHide. (line 25) * -hide-hfsplus-list keep matching files invisible in HFS+ tree: SetHide. (line 28) * -hide-joliet keep matching files invisible in Joliet tree: SetHide. (line 18) * -hide-joliet-list keep matching files invisible in Joliet tree: SetHide. (line 22) * -hide-list keep matching files invisible in ISO tree: SetHide. (line 15) * -hide-rr-moved set deep directory relocation target: SetExtras. (line 90) * -hide_iso_path keep a file invisible in ISO tree: SetHide. (line 31) * -hppa-bootloader HP-PA bootloader file: SystemArea. (line 337) * -hppa-cmdline HP-PA PALO command line: SystemArea. (line 331) * -hppa-hdrversion HP-PA PALO header version: SystemArea. (line 345) * -hppa-kernel_32 HP-PA kernel_32 file: SystemArea. (line 339) * -hppa-kernel_64 HP-PA kernel_64 file: SystemArea. (line 341) * -hppa-ramdisk HP-PA ramdisk file: SystemArea. (line 343) * -input-charset set character set of disk file names: Charset. (line 17) * -iso-level define ISO 9660 limitations: SetCompl. (line 7) * -iso-level define ISO 9660 limitations <1>: SetCompl. (line 8) * -isohybrid-apm-hfsplus Mark boot image in APM: SystemArea. (line 114) * -isohybrid-gpt-basdat Mark boot image in GPT: SystemArea. (line 98) * -isohybrid-gpt-hfsplus Mark boot image in GPT: SystemArea. (line 110) * -isohybrid-mbr Install ISOLINUX isohybrid MBR: SystemArea. (line 87) * -iso_mbr_part_type Set type of ISO MBR partition: SystemArea. (line 129) * -J enable production of Joliet directory tree: SetExtras. (line 159) * -jigdo-checksum-algorithm set data file checksum algorithm: Jigdo. (line 42) * -jigdo-exclude add exclusion pattern for checksum file: Jigdo. (line 65) * -jigdo-force-checksum add check pattern for checksum file: Jigdo. (line 57) * -jigdo-force-md5 add check pattern for checksum file: Jigdo. (line 63) * -jigdo-jigdo set name of .jigdo file: Jigdo. (line 46) * -jigdo-map add address translation for .jigdo: Jigdo. (line 69) * -jigdo-min-file-size set minimum extract size: Jigdo. (line 52) * -jigdo-template set name of .template file: Jigdo. (line 49) * -jigdo-template-compress choose compression algorithm: Jigdo. (line 83) * -joliet enable production of Joliet directory tree: SetExtras. (line 163) * -joliet-long allow longer Joliet names: SetExtras. (line 165) * -joliet-utf16 use UTF-16 with Joliet names: SetExtras. (line 171) * -l allow 31 characters in ISO file names: SetCompl. (line 56) * -log-file redirect stderr messages: Miscellaneous. (line 48) * -m exclude disk files from inserting: SetInsert. (line 41) * -M set path for loading existing ISO image: Loading. (line 11) * -max-iso9660-filenames allow 37 characters in ISO file names: SetCompl. (line 61) * -md5-list set path of input checksum file: Jigdo. (line 81) * -mips-boot MIPS Big Endian boot image: SystemArea. (line 299) * -mipsel-boot MIPS Little Endian boot image: SystemArea. (line 305) * -N omit version number in ISO file names: SetCompl. (line 64) * -no-emul-boot El Torito boot image emulation: Bootable. (line 86) * -no-pad do not add zeros to ISO tree: SetProduct. (line 101) * -o set output file address: SetProduct. (line 8) * -old-exclude exclude disk files from inserting: SetInsert. (line 52) * -old-root enable incremental insertion: SetInsert. (line 90) * -omit-period omit trailing dot in ISO file names: SetCompl. (line 54) * -omit-version-number omit version number in ISO file names: SetCompl. (line 69) * -output set output file address: SetProduct. (line 21) * -output-charset set character set of ISO file names: Charset. (line 21) * -p set Preparer Id: ImageId. (line 47) * -P set Publisher Id: ImageId. (line 28) * -pad add 300 KiB of zeros to ISO tree: SetProduct. (line 94) * -partition_cyl_align Image size alignment: SystemArea. (line 197) * -partition_hd_cyl MBR heads per cylinder: SystemArea. (line 180) * -partition_offset Make mountable by partition 1: SystemArea. (line 169) * -partition_sec_hd MBR sectors per head: SystemArea. (line 183) * -part_like_isohybrid Mark partitions like with isohybrid: SystemArea. (line 122) * -path-list read pathspecs from disk file: SetInsert. (line 8) * -prep-boot-part PReP partition: SystemArea. (line 294) * -preparer set Preparer Id: ImageId. (line 55) * -prev-session set path for loading existing ISO image: Loading. (line 21) * -print-size predict ISO image size: Miscellaneous. (line 23) * -publisher set Publisher Id: ImageId. (line 32) * -quiet suppress most messages: Miscellaneous. (line 40) * -R Rock Ridge (is enabled by default): SetExtras. (line 8) * -r Rock Ridge with altered owner and permission: SetExtras. (line 16) * -rational-rock Rock Ridge with altered owner and permission: SetExtras. (line 26) * -relaxed-filenames 7-bit special characters in ISO file names: SetCompl. (line 45) * -rock Rock Ridge (is enabled by default): SetExtras. (line 13) * -root redirect ISO root directory: SetInsert. (line 85) * -rr_reloc_dir set deep directory relocation target: SetExtras. (line 74) * -sparc-boot SUN SPARC boot images: SystemArea. (line 321) * -sparc-label SUN Disk Label text: SystemArea. (line 323) * -sysid set System Id: ImageId. (line 43) * -transparent-compression enable recognition of zisofs files: SetInsert. (line 63) * -U very relaxed filename rules: SetCompl. (line 27) * -uid ownership for all files: SetProduct. (line 79) * -untranslated-filenames very relaxed filename rules: SetCompl. (line 34) * -untranslated_name_len untranslated file names: SetCompl. (line 36) * -v enable verbose messages: Miscellaneous. (line 53) * -V set Volume Id: ImageId. (line 13) * -verbose enable verbose messages: Miscellaneous. (line 56) * -version report program version: Miscellaneous. (line 58) * -volid set Volume Id: ImageId. (line 23) * -volset set Volume Set Id: ImageId. (line 25) * -x exclude disk files from inserting: SetInsert. (line 49) * -z enable recognition of zisofs files: SetInsert. (line 57)  File: xorrisofs.info, Node: ConceptIdx, Prev: CommandIdx, Up: Top 13 Alphabetic List of Concepts and Objects ****************************************** [index] * Menu: * Abstract File, set path, -abstract: ImageId. (line 57) * ACL, record and load, --acl: SetExtras. (line 106) * APM, mark appended partitions, -appended_part_as_apm: SystemArea. (line 253) * APM, _definition: SystemArea. (line 16) * Application Id, set, -A, -appid: ImageId. (line 34) * Backup, enable fidelity, --for_backup: SetExtras. (line 92) * Biblio File, set path, -biblio: ImageId. (line 62) * Block address, set sorting weight, --sort-weight: SetProduct. (line 45) * Block address, set sorting weight, --sort-weight-list: SetProduct. (line 56) * Block address, set sorting weight, --sort-weight-patterns: SetProduct. (line 76) * Bootability, boot catalog hidden, --boot-catalog-hide: Bootable. (line 121) * Bootability, boot catalog name, -c, -eltorito-catalog: Bootable. (line 114) * Bootability, boot image emulation, -hard-disk-boot: Bootable. (line 83) * Bootability, boot image load size, -boot-load-size: Bootable. (line 72) * Bootability, boot image patching, --grub2-boot-info: Bootable. (line 109) * Bootability, boot image patching, -boot-info-table: Bootable. (line 104) * Bootability, bootable MBR partition, --mbr-force-bootable: SystemArea. (line 148) * Bootability, control, --grub2-sparc-core: SystemArea. (line 325) * Bootability, control, --efi-boot: Bootable. (line 59) * Bootability, control, -alpha-boot: SystemArea. (line 348) * Bootability, control, -b, -eltorito-boot: Bootable. (line 38) * Bootability, control, -B, -sparc-boot: SystemArea. (line 310) * Bootability, control, -e: Bootable. (line 53) * Bootability, control, -eltorito-platform: Bootable. (line 63) * Bootability, control, -hppa-bootloader: SystemArea. (line 337) * Bootability, control, -hppa-cmdline: SystemArea. (line 331) * Bootability, control, -hppa-hdrversion: SystemArea. (line 345) * Bootability, control, -hppa-kernel_32: SystemArea. (line 339) * Bootability, control, -hppa-kernel_64: SystemArea. (line 341) * Bootability, control, -hppa-ramdisk: SystemArea. (line 343) * Bootability, control, -mips-boot: SystemArea. (line 299) * Bootability, control, -mipsel-boot: SystemArea. (line 305) * Bootability, El Torito section id string, -eltorito-id: Bootable. (line 93) * Bootability, El Torito selection criteria, -eltorito-selcrit: Bootable. (line 100) * Bootability, fill System Area e.g. by MBR, -G, --embedded-boot, -generic-boot: SystemArea. (line 68) * Bootability, for CHRP, -chrp-boot-part: SystemArea. (line 283) * Bootability, for EFI, -efi-boot-part: SystemArea. (line 260) * Bootability, for PReP, -prep-boot-part: SystemArea. (line 294) * Bootability, GPT Legacy BIOS bootable, --gpt-iso-bootable: SystemArea. (line 159) * Bootability, GPT Read-only flag, --gpt-iso-not-ro: SystemArea. (line 164) * Bootability, install ISOLINUX isohybrid MBR, -isohybrid-mbr: SystemArea. (line 87) * Bootability, install modern GRUB2 MBR, --grub2-mbr: SystemArea. (line 81) * Bootability, mark boot image in APM, -isohybrid-apm-hfsplus: SystemArea. (line 114) * Bootability, mark boot image in GPT, -isohybrid-gpt-basdat: SystemArea. (line 98) * Bootability, mark boot image in GPT, -isohybrid-gpt-hfsplus: SystemArea. (line 110) * Bootability, next entry, -eltorito-alt-boot: Bootable. (line 47) * Bootability, no boot image emulation, -no-emul-boot: Bootable. (line 86) * Bootability, partitions like with isohybrid, -part_like_isohybrid: SystemArea. (line 122) * Bootability, patch System Area partition table, --protective-msdos-label: SystemArea. (line 143) * Bootability, SUN Disk Label text, -sparc-label: SystemArea. (line 323) * Bootability, type of ISO MBR partition, -iso_mbr_part_type: SystemArea. (line 129) * Bugs, reporting: Bugreport. (line 6) * Character Set, for disk file names, -input-charset: Charset. (line 17) * Character Set, for ISO file names, -output-charset: Charset. (line 21) * Character sets, _definition: Charset. (line 6) * Copyright File, set path, -copyright: ImageId. (line 66) * Deep directories, allow, -D, -disable-deep-relocation: SetExtras. (line 60) * Deep directories, relocation target, -hide-rr-moved: SetExtras. (line 90) * Deep directories, relocation target, -rr_reloc_dir: SetExtras. (line 74) * Disk files, exclude, -exclude-list: SetInsert. (line 54) * Disk files, exclude, -m, -exclude, -x, -old-exclude: SetInsert. (line 41) * Disk GUID, for GPT, --gpt_disk_guid: SystemArea. (line 270) * disk_path, _definition: Insert. (line 7) * ECMA-119, _definition: Standards. (line 6) * El Torito, _definition: Bootable. (line 13) * Examples: Examples. (line 6) * File names, curb length, -file_name_limit: SetExtras. (line 49) * File timestamps, set all, --set_all_file_dates: SetExtras. (line 35) * Forced output, control, --stdio_sync: SetProduct. (line 23) * GPT, mark appended partitions, -appended_part_as_gpt: SystemArea. (line 238) * GPT, _definition: SystemArea. (line 13) * Group, for all files, -gid: SetProduct. (line 83) * HFS+, enables production: SetExtras. (line 176) * HFS+, issue blessing ppc_bootdir, -hfs-bless: SetExtras. (line 233) * HFS+, issue blessing, -hfs-bless-by: SetExtras. (line 223) * HFS+, set allocation block size: SetExtras. (line 209) * HFS+, set APM block size: SetExtras. (line 213) * HFS+, set creator and type of file, -hfsplus-file-creator-type: SetExtras. (line 220) * HFS+, set serial number: SetExtras. (line 205) * HFS+, _definition: Standards. (line 32) * Hiding, by ISO RR path, -hide_iso_path: SetHide. (line 31) * Hiding, from HFS+, -hide-hfsplus: SetHide. (line 25) * Hiding, from HFS+, -hide-hfsplus-list: SetHide. (line 28) * Hiding, from ISO and Rock Ridge, -hide: SetHide. (line 8) * Hiding, from ISO and Rock Ridge, -hide-list: SetHide. (line 15) * Hiding, from Joliet, -hide-joliet: SetHide. (line 18) * Hiding, from Joliet, -hide-joliet-list: SetHide. (line 22) * Image size, alignment, -partition_cyl_align: SystemArea. (line 197) * Incremental insertion, disable disk ino, --old-root-no-ino: SetInsert. (line 103) * Incremental insertion, disable MD5, --old-root-no-md5: SetInsert. (line 121) * Incremental insertion, enable disk devno, --old-root-devno: SetInsert. (line 114) * Incremental insertion, enable, -old-root: SetInsert. (line 90) * Insert, piece of data file or device, -cut_out: SetInsert. (line 22) * Interval reader for system area and partitions: SystemArea. (line 24) * ISO 9660, _definition: Standards. (line 6) * ISO 9660:1999, _definition: Standards. (line 26) * ISO file names, 7-bit special characters, -relaxed-filenames: SetCompl. (line 45) * ISO file names, allow 31 characters, -l, -full-iso9660-filenames: SetCompl. (line 56) * ISO file names, allow 37 characters, -max-iso9660-filenames: SetCompl. (line 61) * ISO file names, allow lowercase, -allow-lowercase: SetCompl. (line 42) * ISO file names, omit trailing dot, -d, -omit-period: SetCompl. (line 50) * ISO file names, omit version number, -N, -omit-version-number: SetCompl. (line 64) * ISO file names, untranslated, -untranslated_name_len: SetCompl. (line 36) * ISO file names, very relaxed rules, -U, -untranslated-filenames: SetCompl. (line 27) * ISO image size, predict, -print-size: Miscellaneous. (line 23) * ISO image, set Application Use field, --application_use: ImageId. (line 79) * ISO image, set timestamps, --modification-date=: ImageId. (line 70) * ISO level 1, enforce directory names, -disallow_dir_id_ext: SetCompl. (line 23) * ISO level, specify, -iso-level: SetCompl. (line 7) * ISO level, specify, -iso-level <1>: SetCompl. (line 8) * ISO root directory, redirect, -root: SetInsert. (line 85) * iso_rr_path, _definition: Insert. (line 8) * Jigdo Template Extraction, -checksum-list: Jigdo. (line 77) * Jigdo Template Extraction, -checksum_algorithm_iso: Jigdo. (line 86) * Jigdo Template Extraction, -checksum_algorithm_template: Jigdo. (line 92) * Jigdo Template Extraction, -jigdo-checksum-algorithm: Jigdo. (line 42) * Jigdo Template Extraction, -jigdo-exclude: Jigdo. (line 65) * Jigdo Template Extraction, -jigdo-force-checksum: Jigdo. (line 57) * Jigdo Template Extraction, -jigdo-force-md5: Jigdo. (line 63) * Jigdo Template Extraction, -jigdo-jigdo: Jigdo. (line 46) * Jigdo Template Extraction, -jigdo-map: Jigdo. (line 69) * Jigdo Template Extraction, -jigdo-min-file-size: Jigdo. (line 52) * Jigdo Template Extraction, -jigdo-template: Jigdo. (line 49) * Jigdo Template Extraction, -jigdo-template-compress: Jigdo. (line 83) * Jigdo Template Extraction, -md5-list: Jigdo. (line 81) * Jigdo Template Extraction, _definition: Jigdo. (line 6) * Joliet, allows longer names, -joliet-long: SetExtras. (line 165) * Joliet, enable, -J, -joliet: SetExtras. (line 159) * Joliet, _definition: Standards. (line 21) * Links, follow on disk, -f, -follow-links: SetInsert. (line 32) * Links, record and load hard links, --hardlinks: SetExtras. (line 142) * Linux file attributes, record and load, --lfa_flags: SetExtras. (line 125) * MBR, GPT, append partition, -append_partition: SystemArea. (line 208) * MBR, sectors per head, -partition_sec_hd: SystemArea. (line 180) * MBR, sectors per head, -partition_sec_hd <1>: SystemArea. (line 183) * MBR, _definition: SystemArea. (line 9) * MD5, record and load, --md5: SetExtras. (line 134) * Message output, increase frequency, -gui: Miscellaneous. (line 44) * Message output, redirect stderr, -log-file: Miscellaneous. (line 48) * Message output, suppress, -quiet: Miscellaneous. (line 40) * Mountability, by non-trivial partition 1, -partition_offset: SystemArea. (line 169) * Options completion, -genisoimage_completion: Miscellaneous. (line 8) * Options, list, -help: Miscellaneous. (line 36) * Output file, set address, -o, -output: SetProduct. (line 8) * Ownership, for all files, -uid: SetProduct. (line 79) * Padding, 300 KiB, -pad: SetProduct. (line 94) * Padding, disable, --old-empty: SetProduct. (line 105) * Padding, disable, -no-pad: SetProduct. (line 101) * pathspec, enable target=source, -graft-points: SetInsert. (line 38) * pathspec, read list of, --quoted_path_list: SetInsert. (line 12) * pathspec, read list of, -path-list: SetInsert. (line 8) * pathspec, _definition: Insert. (line 11) * Permissions, for all data files, -file-mode: SetProduct. (line 92) * Permissions, for all directories, -dir-mode: SetProduct. (line 87) * Preparer Id, set, -p: ImageId. (line 47) * Problems, reporting: Bugreport. (line 6) * Program version, report, -version: Miscellaneous. (line 58) * Publisher Id, set, -P, -publisher: ImageId. (line 28) * Rock Ridge, (enabled by default), -R, -rock: SetExtras. (line 8) * Rock Ridge, altered owner and permission, -r, -rational-rock: SetExtras. (line 16) * Rock Ridge, disable production, --norock: SetExtras. (line 28) * Rock Ridge, _definition: Standards. (line 14) * scdbackup, record checksum tag, --scdbackup_tag: SetExtras. (line 150) * Session, select path, -M, -prev-session, -dev: Loading. (line 11) * Session, set load and write address, -C, -cdrecord-params: Loading. (line 25) * Startup files, suppress, --no_rc: Miscellaneous. (line 33) * System Area, _definition: SystemArea. (line 6) * System Id, set, -sysid: ImageId. (line 43) * Table-of-content, emulation off, --no-emul-toc: SetProduct. (line 41) * Table-of-content, emulation, --emul-toc: SetProduct. (line 33) * UTF-16, for Joliet paths, -joliet-utf16: SetExtras. (line 171) * Verbosity, high, -v, -verbose: Miscellaneous. (line 53) * Volume Id, set, -V, -volid: ImageId. (line 13) * Volume Set Id, set, -volset: ImageId. (line 25) * xattr, record and load, --xattr: SetExtras. (line 112) * xattr, record and load, --xattr-any: SetExtras. (line 119) * xorriso, mkisofs emulation: Xorriso. (line 6) * xorriso, options: Options. (line 6) * Z2 instead of ZF for version 2, -zisofs2-susp-z2: SetInsert. (line 76) * ZF instead of Z2 for version 2, -zisofs2-susp-zf: SetInsert. (line 80) * zisofs file, enable recognition, -z, -transparent-compression: SetInsert. (line 57) * zisofs2 file, enable recognition, -zisofs-version-2: SetInsert. (line 65)  Tag Table: Node: Top391 Node: Overview1131 Node: Standards1573 Node: Insert3770 Node: Xorriso5616 Node: Options6762 Node: Loading7489 Node: SetInsert9530 Node: SetProduct15465 Node: SetCompl21037 Node: SetExtras23629 Node: SetHide35886 Node: ImageId37885 Node: Bootable42167 Node: SystemArea48453 Node: Charset68181 Node: Jigdo69206 Node: Miscellaneous74176 Node: Examples76772 Node: ExSimple77266 Node: ExGraft77749 Node: ExMkisofs79049 Node: ExGrowisofs80857 Node: ExIncBackup82047 Node: ExIncBckAcc85223 Node: ExBootable86928 Node: Files91110 Node: Environ92205 Node: Seealso93078 Node: Bugreport93755 Node: Legal94348 Node: CommandIdx95245 Node: ConceptIdx112605  End Tag Table \input texinfo @c -*-texinfo-*- @c %**start of header @setfilename xorrisofs.info @settitle GNU xorrisofs 1.5.7 @c %**end of header @c @c man-ignore-lines begin @dircategory Archiving @direntry * Xorrisofs: (xorrisofs). Emulates ISO 9660 program mkisofs @end direntry @c man-ignore-lines end @c @c Notes about embedded man page: @c This texinfo code contains the necessary info to produce a man page @c which resembles much the version of xorriso.1 from which this code @c was originally derived in march 2010. @c One can produce the man page by applying the following rules: @c The first line gets discarded. @c Line start "@c man " will become "", the remainder is put out unaltered. @c Lines "@*" will be converted to ".br" @c "@c man-ignore-lines N" will discard N following lines. @c "@c man-ignore-lines begin" discards all following lines @c up to "@c man-ignore-lines end". @c Line blocks of "@menu" "@end menu" will be discarded. @c "@item word words" becomes "\fBword\fR words". @c @b{...}, @command{...}, @dfn{...}, @emph{...}, @strong{...} @c get mapped to \fB...\fR . @c @abbr{...}, @code{...}, @file{...}, @i{...}, @option{...}, @r{...}, @c @ref{...}, @samp{...},@var{...}, get mapped to ... . @c @ref{...}, @xref{...} get mapped to empty text. @c @email{...} gets mapped to <...> . @c Mapped {...} content is subject to the rules except {...} mapping. @c @minus{} will become "-". @c @@ , @{, @} will get stripped of their first @. @c Other lines which begin by "@" will be discarded. @c In lines not stemming from "@c man", "\" becomes "\\" @c "-" which are not preceded by an uneven number of "\" will get @c prepended one "\". @c @c @c man .\" Hey, EMACS: -*- nroff -*- @c man .\" @c man .\" IMPORTANT NOTE: @c man .\" @c man .\" The original of this file is kept in xorriso/xorrisofs.texi @c man .\" This here was generated by program xorriso/make_xorriso_1 @c man .\" @c man .\" @c man .\" First parameter, NAME, should be all caps @c man .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection @c man .\" other parameters are allowed: see man(7), man(1) @c man .TH XORRISOFS 1 "Version 1.5.7, Sep 05, 2024" @c man .\" Please adjust this date whenever revising the manpage. @c man .\" @c man .\" Some roff macros, for reference: @c man .\" .nh disable hyphenation @c man .\" .hy enable hyphenation @c man .\" .ad l left justify @c man .\" .ad b justify to both left and right margins @c man .\" .nf disable filling @c man .\" .fi enable filling @c man .\" .br insert line break @c man .\" .sp insert n+1 empty lines @c man .\" for manpage-specific macros, see man(7) @c man .nh @c man-ignore-lines begin @copying xorrisofs - Emulation of ISO 9660 program mkisofs by program xorriso Copyright @copyright{} 2011 - 2024 Thomas Schmitt @quotation Permission is granted to distribute this text freely. @end quotation @end copying @c man-ignore-lines end @titlepage @title Manual of GNU xorriso personality xorrisofs 1.5.7 @author Thomas Schmitt @page @vskip 0pt plus 1filll @insertcopying @end titlepage @contents @ifnottex @node Top @top xorrisofs 1.5.7 @c man-ignore-lines 1 @c man .SH NAME xorrisofs - Emulation of ISO 9660 program mkisofs by program xorriso @end ifnottex @menu * Overview:: Overview * Standards:: ISO 9660, Rock Ridge, Joliet * Insert:: Inserting files into the ISO image * Xorriso:: Relation to program xorriso * Options:: Options * Examples:: Examples * Files:: Files * Environ:: Environment * Seealso:: See also * Bugreport:: Reporting bugs * Legal:: Author, Copyright, Credits * CommandIdx:: Alphabetic Command List * ConceptIdx:: Alphabetic List of Concepts and Objects @end menu @node Overview, Standards, Top, Top @chapter Overview @c man .SH SYNOPSIS @c man .B xorrisofs @c man [ options ] [-o filename ] pathspec [pathspecs ...] @c man .br @c man .SH DESCRIPTION @c man .PP @command{xorrisofs} produces Rock Ridge enhanced ISO 9660 filesystems and add-on sessions to such filesystems. Optionally it can produce Joliet directory trees too. @* @sp 1 @c man .PP @command{xorrisofs} understands options of program mkisofs from cdrtools by Joerg Schilling. Its implementation is part of program xorriso which shares no source code with cdrtools. @c man .SS @node Standards, Insert, Overview, Top @chapter ISO 9660, Rock Ridge, Joliet, HFS+ @c man \fBISO 9660, Rock Ridge, Joliet, HFS+:\fR @c man .br @cindex ISO 9660, _definition @cindex ECMA-119, _definition @strong{ISO 9660} (aka @strong{ECMA-119}) is a read-only filesystem that is mainly used for optical media CD, DVD, BD, but may also reside on other storage devices like disk files, USB sticks or disk partitions. It is widely readable by many operating systems and by boot facilities of personal computers. @* ISO 9660 describes directories and data files by very restricted filenames with no distinction of upper case and lower case. Its metadata do not comply to fundamental POSIX specifications. @* @cindex Rock Ridge, _definition @strong{Rock Ridge} is the name of a set of additional information which enhance an ISO 9660 filesystem so that it can represent a POSIX compliant filesystem with ownership, access permissions, symbolic links, and other attributes. Rock Ridge allows filenames of up to 255 bytes and paths of up to 1024 bytes. @* xorrisofs produces Rock Ridge information by default. It is strongly discouraged to disable this feature. @* @cindex Joliet, _definition @strong{Joliet} is the name of an additional directory tree which provides filenames up to 64 characters encoded as UTF-16. A Joliet tree is mainly interesting for reading the ISO image by operating systems of Microsoft Corporation. Production of this directory tree may be enabled by option -J. @* @cindex ISO 9660:1999, _definition @strong{ISO 9660:1999} is the name of an additional directory tree which provides longer filenames. It allows single file names to have up to 207 characters. It might be of use with some older computer system boot facilities which read neither Rock Ridge nor Joliet but need longer filenames nevertheless. Production of this directory tree may be enabled by option -iso-level 4. @* @cindex HFS+, _definition @strong{HFS+} is the name of a filesystem which is normally used for writing and reading on hard disks and similar devices. It is possible to embed a HFS+ partition into the emerging ISO 9660 image and to mark it by Apple Partition Map entries. This interferes with options which copy data into the first 32 KiB of the ISO image, like -G or -isohybrid-mbr. See option -hfsplus. @* The main purpose for having an embedded HFS+ partition is booting of certain models of Apple computers. @c man .SS @sp 1 @c man .B Inserting files into the ISO image: @node Insert, Xorriso, Standards, Top @chapter Inserting files into the ISO image @c man .PP @command{xorrisofs} deals with two kinds of file addresses: @* @cindex disk_path, _definition @strong{disk_path} is a path to an object in the local filesystem tree. @* @cindex iso_rr_path, _definition @strong{iso_rr_path} is the Rock Ridge address of a file object in the ISO image. If no Rock Ridge information shall be stored in an emerging ISO, then the names will get mapped to ISO 9660 names of limited length and character set. @cindex pathspec, _definition @* @sp 1 @c man .PP A program argument is handled as a @strong{pathspec}, if it is not recognized as original mkisofs option or additional @command{xorrisofs} option. A pathspec depicts an input file object by a disk_path. If option -graft-points is not present, then the behavior depends on the file type of disk_path. Directories get merged with the /-directory of the ISO image. Files of other types get copied into the /-directory. @* If -graft-points is present then each pathspec gets split at the first occurrence of the =-character. The part before the = is taken as @strong{target}, i.e. the iso_rr_path for the file object in the ISO image. The part after the first = is taken as @strong{source}, i.e. the disk_path of the input object. @* It is possible to make =-characters part of the iso_rr_path by preceding them with a \-character. The same must be done for \-characters which shall be part of the iso_rr_path. @* @sp 1 @c man .PP If the source part of the pathspec leads to a directory, then all files underneath this directory get inserted into the image, too. It is possible to exclude particular files from being inserted by help of option -m. @* In case that target already exists, the following rules apply: Directories and other files may overwrite existing non-directories. Directories get merged with existing directories. Non-directories may not overwrite existing directories. @c man .SS @node Xorriso, Options, Insert, Top @chapter Relation to program xorriso @c man \fBRelation to program xorriso:\fR @c man .br @cindex xorriso, mkisofs emulation @command{xorrisofs} is actually a command mode of program @strong{xorriso}, which gets entered either by xorriso command "-as mkisofs" or by starting the program by one of the names "xorrisofs", "mkisofs", "genisoimage", or "genisofs". @* This command mode can be left by argument "@minus{}@minus{}" which leads to generic xorriso command mode. See @strong{man xorriso} for its description. @* @sp 1 @c man .PP xorriso performs image reading and writing by help of libburn, which is mainly intended for optical drives, but also operates on all POSIX file types except directories. @* The program messages call any image file a "drive". File types which are not supported for reading are reported as "blank". The reported free media space may be quite fictional. @* Nevertheless @command{xorrisofs} does not operate directly on optical drives, but rather forces libburn to regard them as general device files. So for writing of sequential optical media (CD, DVD-R, DVD+R, BD-R) one will have to use a burn program. E.g the cdrecord emulation of xorriso. See EXAMPLES. @c man .SS @node Options, Examples, Xorriso, Top @chapter Options @cindex xorriso, options @c man .br @c man .SH OPTIONS @c man .br @menu * Loading:: Image loading * SetInsert:: Settings for file insertion * SetProduct:: Settings for image production * SetCompl:: Settings for standards compliance * SetExtras:: Settings for standards extensions * SetHide:: Settings for file hiding * ImageId:: ISO image ID strings * Bootable:: El Torito Bootable ISO images * SystemArea:: System Area, MBR, GPT, APM, other boot blocks * Charset:: Character sets * Jigdo:: Jigdo Template Extraction * Miscellaneous:: Miscellaneous options @end menu @c man .PP @c man .TP @c man .B Image loading: @node Loading, SetInsert, Options, Options @section Influencing the behavior of image loading @c man .PP The following options control loading of an existing ISO image for the purpose of preparing a suitable add-on session. If they are missing then a new image is composed from scratch. @table @asis @sp 1 @c man .TP @item -M disk_path @kindex -M set path for loading existing ISO image @cindex Session, select path, -M, -prev-session, -dev Set the path from which to load the existing ISO image directory tree on which to base the upcoming directory tree as add-on session. The path must lead to a random-access readable file object. On GNU/Linux: regular data files or block device files. @* A special kind of pseudo disk_path has the form "/dev/fd/"number. It depicts the open file descriptor with the given number, regardless whether the operating system supports this feature by file nodes in /dev/fd or not. E.g. /dev/fd/3 is file descriptor 3 which was opened by the program that later started xorriso. @c man .TP @item -prev-session disk_path @kindex -prev-session set path for loading existing ISO image Alias of -M. @c man .TP @item -dev disk_path @kindex -dev set path for loading existing ISO image Alias of -M. @c man .TP @item -C last_session_start,next_writeable_address @kindex -C set load address and write address offset @cindex Session, set load and write address, -C, -cdrecord-params Set the 2 KiB block address last_session_start from where to read the ISO image out of the file given by option -M. @* Separated by a comma, set the next_writeable_address to which the add-on session will finally be written. Decisive is actually the block address which the intended readers will have to use as superblock address on the intended medium. @* Both values can be inquired from optical media by help of burn programs and cdrecord option -msinfo. xorriso itself can obtain it in its cdrecord emulation. @* values=$(xorriso -as cdrecord dev=/dev/... -msinfo) @* echo $values @* @sp 1 Option -C may be used without option -M to create an ISO image from scratch and prepare it for being finally written to a block address other than 0. Parameter last_session_start must then be set to 0. @c man .TP @item -cdrecord-params last_session_start,next_writeable_address @kindex -cdrecord-params set load address and write address offset Alias of -C. @end table @c man .TP @c man .B Settings for file insertion: @node SetInsert, SetProduct, Loading, Options @section Settings for file insertion @table @asis @sp 1 @c man .TP @item -path-list disk_path @kindex -path-list read pathspecs from disk file @cindex pathspec, read list of, -path-list Read pathspecs line-by-line from disk_file and insert the depicted file objects into the ISO image. If disk_path is "-" then read the pathspecs from standard input. @c man .TP @item @minus{}@minus{}quoted_path_list disk_path @kindex @minus{}@minus{}quoted_path_list read pathspecs from disk file @cindex pathspec, read list of, @minus{}@minus{}quoted_path_list Like option -path-list but reading quoted words rather than plain lines. Whitespace outside of quotes will be discarded. On the other hand it is possible to represent pathspecs which contain newline characters. @* The double quotation mark " and the single quotation mark ' can be used to enclose whitespace and make it part of pathspecs. Each mark type can enclose the marks of the other type. A trailing backslash \ outside quotations or an open quotation cause the next input line to be appended. @c man .TP @item -cut_out disk_path byte_offset byte_count iso_rr_path @kindex -cut_out insert piece of data file or device @cindex Insert, piece of data file or device, -cut_out Map a byte interval of a regular disk file or of a device file into a regular file in the ISO image. The file depicted by disk_path has to support random read access. @* byte_offset and byte_count may be plain numbers counting bytes, or numbers with appended letter "d", "s", "k", "m", "g" to count disk blocks (512 bytes), disc sectors (2048 bytes), KiB (1024 bytes), MiB (1024 KiB), or GiB (1024 MiB). @* E.g: @* -cut_out bootable.iso 562s 18s /formerly_hidden_boot_image @c man .TP @item -f @kindex -f follow symbolic links on disk @cindex Links, follow on disk, -f, -follow-links @* Resolve symbolic links on disk rather than storing them as symbolic links in the ISO image. @c man .TP @item -follow-links @kindex -follow-links follow symbolic links on disk Alias of -f. @c man .TP @item -graft-points @kindex -graft-points enable target=source pathspecs @cindex pathspec, enable target=source, -graft-points Enable interpretation of input file pathspecs as combination of iso_rr_path and disk_path, separated by a =-character. @c man .TP @item -m disk_pattern @kindex -m exclude disk files from inserting @cindex Disk files, exclude, -m, -exclude, -x, -old-exclude Exclude files from being inserted into the image. Silently ignored are those files of which the disk_path matches the given shell parser pattern. If no /-character is part of the pattern, then it gets matched against the leaf name of the disk file. @* It is possible to give more than one -m option. @c man .TP @item -exclude @kindex -exclude exclude disk files from inserting Alias of -m. @c man .TP @item -x @kindex -x exclude disk files from inserting @* Alias of -m. @c man .TP @item -old-exclude @kindex -old-exclude exclude disk files from inserting Alias of -m. @c man .TP @item -exclude-list disk_path @kindex -exclude-list exclude disk files from inserting @cindex Disk files, exclude, -exclude-list Perform -m using each line out of file disk_path as argument disk_pattern. @c man .TP @item -z @kindex -z enable recognition of zisofs files @cindex zisofs file, enable recognition, -z, -transparent-compression @* Enable recognition and proper processing of zisofs compressed files as produced by program mkzftree. These files will get equipped with the necessary meta data so that a Linux kernel will recognize them and deliver their content in uncompressed form. @c man .TP @item -transparent-compression @kindex -transparent-compression enable recognition of zisofs files Alias of -z. @c man .TP @item --zisofs-version-2 @kindex --zisofs-version-2 enable recognition of zisofs2 files @cindex zisofs2 file, enable recognition, --zisofs-version-2 @* Enable the recognition and proper processing of experimental zisofs version 2 compressed files. The Linux kernel (as of 5.9) does not yet know this format and will complain like @* isofs: Unknown ZF compression algorithm: PZ @* This complaint can be prevented by option --zisofs2-susp-z2 . @* The files will be shown by unaware kernels as they were submitted to xorriso, i.e. with zisofs2 header, block pointer list, and compressed data. @* --zisofs-version-2 also enables -z. @c man .TP @item --zisofs2-susp-z2 @kindex --zisofs2-susp-z2 produce Z2 for version 2 instead of ZF @cindex Z2 instead of ZF for version 2, --zisofs2-susp-z2 Enable the production of SUSP entries "Z2" instead of "ZF" with zisofs2 compressed files. Unaware Linux kernels silently ignore "Z2" entries. @c man .TP @item --zisofs2-susp-zf @kindex --zisofs2-susp-zf produce ZF for version 2 instead of Z2 @cindex ZF instead of Z2 for version 2, --zisofs2-susp-zf Enable the production of SUSP entries "ZF" instead of "Z2" with zisofs2 compressed files. Unaware Linux kernels complain about zisofs2 "ZF" by "Unknown ZF compression algorithm" and thus leave a mark in the system log. @c man .TP @item -root iso_rr_path @kindex -root redirect ISO root directory @cindex ISO root directory, redirect, -root Insert all files under the given iso_rr_path. If option -graft-points is given, then iso_rr_path is prepended to each target part of a pathspec. @* The default for -root is "/". @c man .TP @item -old-root iso_rr_path @kindex -old-root enable incremental insertion @cindex Incremental insertion, enable, -old-root Enable incremental insertion of files into the loaded image. The effective target and source addresses of given pathspecs get compared whether the target already exists in the ISO image and is still identical to the source on disk. Metadata in the ISO image will get adjusted, if they differ from those on disk. New files and files with changed content will get newly added. Target files which do not exist in any of the according pathspec sources will get removed from the ISO directory tree. @* If the effective setting of -root differs from the iso_rr_path given with -old-root, then the files underneath the -old-root directory get cloned underneath the -root directory. Cloning happens before file comparison. @c man .TP @item @minus{}@minus{}old-root-no-ino @kindex @minus{}@minus{}old-root-no-ino disable disk ino with -old-root @cindex Incremental insertion, disable disk ino, @minus{}@minus{}old-root-no-ino Disable recording and use of disk inode numbers. If no disk inode numbers are recorded, then option -old-root will have to read disk file content and compare it with the MD5 checksum that is recorded in the ISO image. @* With recorded disk inode numbers and with credible ctime and mtime, it is possible to detect potential changes in the content without actually reading it. A loophole remains if multiple different filesystems may get mounted at the same directory, like it is habit with /mnt. In this case one has to use option @minus{}@minus{}old-root-devno or disable the inode number shortcut by @minus{}@minus{}old-root-no-ino. @c man .TP @item @minus{}@minus{}old-root-devno @kindex @minus{}@minus{}old-root-devno enable disk idevno with -old-root @cindex Incremental insertion, enable disk devno, @minus{}@minus{}old-root-devno Enable comparison of recorded device numbers together with recorded inode numbers. This works only with good old stable device numbers which get out of fashion, regrettably. If the hard disk has a different device number after each reboot, then this comparison will see all files as changed and thus prevent any incremental size saving. @c man .TP @item @minus{}@minus{}old-root-no-md5 @kindex @minus{}@minus{}old-root-no-md5 disable MD5 with -old-root @cindex Incremental insertion, disable MD5, @minus{}@minus{}old-root-no-md5 Disable recording and use of MD5 checksums for data file content. If neither checksums and nor disk inode numbers are recorded, then option -old-root will have to read ISO image file content when comparing it with disk file content. @end table @c man .TP @c man .B Settings for image production: @node SetProduct, SetCompl, SetInsert, Options @section Settings for image production @table @asis @sp 1 @c man .TP @item -o disk_path @kindex -o set output file address @cindex Output file, set address, -o, -output Set the output file address for the emerging ISO image. If the address exists as regular file, it will be truncated to length 0 when image production begins. It may not already exist as directory. If it does not exist yet then its parent directory must exist and a regular file will get created. @* A special kind of pseudo disk_path has the form "/dev/fd/"number. It depicts the open file descriptor with the given number, regardless whether the operating system supports this feature by file nodes in /dev/fd or not. E.g. /dev/fd/4 is file descriptor 4 which was opened by the program that later started xorriso. @* Default is standard output (/dev/fd/1) which may also be set by disk_path "-". @c man .TP @item -output disk_path @kindex -output set output file address Alias of -o. @c man .TP @item @minus{}@minus{}stdio_sync "on"|"off"|"end"|number @kindex @minus{}@minus{}stdio_sync control forced output to disk files @cindex Forced output, control, @minus{}@minus{}stdio_sync Set the number of bytes after which to force output to disk in order to keep the memory from being clogged with lots of pending data for slow devices. "on" is the same as "16m". Forced output can be disabled by "off", or be delayed by "end" until all data are produced. If a number is chosen, then it must be at least 64k. @* The default with xorriso mkisofs emulation is @minus{}@minus{}stdio_sync "off". @* xorriso uses an inner fifo buffer with default size 4 MiB. So forcing the operating system i/o cache to disk does not necessarily block the simultaneous production of more image content. @c man .TP @item @minus{}@minus{}emul-toc @kindex @minus{}@minus{}emul-toc enable table-of-content emulation @cindex Table-of-content, emulation, @minus{}@minus{}emul-toc Write a second superblock with the first session into random-access files. If further sessions get appended and the first superblock gets updated, then the second superblock will not be overwritten. So it is still possible to mount the first session and to find the start blocks of the further sessions. @* The price is 64 KiB extra space consumption. If -partition_offset is non-zero, then it is 128 KiB plus twice the partition offset. @c man .TP @item @minus{}@minus{}no-emul-toc @kindex @minus{}@minus{}no-emul-toc no table-of-content emulation @cindex Table-of-content, emulation off, @minus{}@minus{}no-emul-toc Do not write a second superblock with the first session into random-access files. @* This is the default. @c man .TP @item @minus{}@minus{}sort-weight weight_number iso_rr_path @kindex @minus{}@minus{}sort-weight set block address sorting weight @cindex Block address, set sorting weight, @minus{}@minus{}sort-weight Attribute a LBA weight number to regular files. If iso_rr_path leads to a directory then all regular files underneath will get the weight_number. @* The weight_number may range from -2147483648 to 2147483647. The higher it is, the lower will be the block address of the file data in the emerging ISO image. Currently the El Torito boot catalog has a hardcoded weight of 1 billion. Normally it should occupy the block with the lowest possible address. Data files get added or loaded with initial weight 0. Boot image files have a default weight of 2. @c man .TP @item @minus{}@minus{}sort-weight-list disk_path @kindex @minus{}@minus{}sort-weight-list set block address sorting weight @cindex Block address, set sorting weight, @minus{}@minus{}sort-weight-list Read pairs of weight number and iso_rr_path from a file of the local filesystem. Apply each pair like with @minus{}@minus{}sort-weight. @* Only the last @minus{}@minus{}sort-weight-list or @minus{}@minus{}sort-weight-patterns of a xorrisofs run gets into effect. @* The weight number is read from the start of the line. The iso_rr_path part of an input line begins immediately after the first blank or tab character of the line. @* Notes for the case that this feature is used within a sequence of generic xorriso commands (not an issue with a pure mkisofs emulation run): @* The addressed files must already be in the ISO image model when you execute @* -as mkisofs @minus{}@minus{}sort-weight-list disk_path @minus{}@minus{} @* Several such commands may be used to apply more than one weight file. @* Data files which are loaded by -indev or -dev get a weight between 1 and 2 exp 28 = 268,435,456, depending on their block address. This shall keep them roughly in the same order if the write method of modifying is applied. @c man .TP @item @minus{}@minus{}sort-weight-patterns disk_path @kindex @minus{}@minus{}sort-weight-patterns set block address sorting weight @cindex Block address, set sorting weight, @minus{}@minus{}sort-weight-patterns Like @minus{}@minus{}sort-weight-list , but expanding the iso_rr_paths as shell parser patterns and applying @minus{}@minus{}sort-weight to each matching file. @c man .TP @item -uid number|name @kindex -uid ownership for all files @cindex Ownership, for all files, -uid Use the given number or locally existing user name as owner id of all files and directories in the emerging filesystem. Empty name or name "-" revoke this feature. @c man .TP @item -gid number|name @kindex -gid group assignment for all files @cindex Group, for all files, -gid Use the given number or locally existing group name as group id of all files and directories in the emerging filesystem. Empty name or name "-" revoke this feature. @c man .TP @item -dir-mode mode @kindex -dir-mode permissions for all directories @cindex Permissions, for all directories, -dir-mode Set the access permissions for all directories in the image to the given mode which is either an octal number beginning with "0" or a comma separated list of statements of the form [ugoa]*[+-=][rwxst]* . E.g. ug=rx,a-rwx @c man .TP @item -file-mode mode @kindex -file-mode permissions for all data files @cindex Permissions, for all data files, -file-mode Like -dir-mode but for all regular data files in the image. @c man .TP @item -pad @kindex -pad add 300 KiB of zeros to ISO tree @cindex Padding, 300 KiB, -pad @* Add 300 KiB to the end of the produced ISO image. This circumvents possible read errors from ISO images which have been written to CD media in TAO mode. The additional bytes are claimed as part of the ISO image if not --emul-toc is given. @* Option -pad is the default. @c man .TP @item -no-pad @kindex -no-pad do not add zeros to ISO tree @cindex Padding, disable, -no-pad Disable padding of 300 KiB to the end of the produced ISO image. This is safe if the image is not meant to be written on CD or if it gets written to CD as only track in write mode SAO. @c man .TP @item @minus{}@minus{}old-empty @kindex @minus{}@minus{}old-empty old block addresses for empty files @cindex Padding, disable, @minus{}@minus{}old-empty Use the old way of of giving block addresses in the range of [0,31] to files with no own data content. The new way is to have a dedicated block to which all such files will point. @end table @c man .TP @c man .B Settings for standards compliance: @node SetCompl, SetExtras, SetProduct, Options @section Settings for standards compliance @table @asis @sp 1 @kindex -iso-level define ISO 9660 limitations @cindex ISO level, specify, -iso-level @c man .TP @item -iso-level number @kindex -iso-level define ISO 9660 limitations @cindex ISO level, specify, -iso-level Specify the ISO 9660 version which defines the limitations of file naming and data file size. The naming restrictions do not apply to the Rock Ridge names but only to the low-level ISO 9660 names. There are three conformance levels: @* Level 1 allows ISO names of the form 8.3 and file size up to 4 GiB - 1. @* Level 2 allows ISO names with up to 32 characters and file size up to 4 GiB - 1. @* Level 3 allows ISO names with up to 32 characters and file size of up to 400 GiB - 200 KiB. (This size limitation is set by the xorriso implementation and not by ISO 9660 which would allow nearly 8 TiB.) @* Pseudo-level 4 enables production of an additional ISO 9660:1999 directory tree. @c man .TP @item -disallow_dir_id_ext @kindex -disallow_dir_id_ext enforce ISO level 1 directory names @cindex ISO level 1, enforce directory names, -disallow_dir_id_ext Do not follow a bad habit of mkisofs which allows dots in the ISO names of directories. On the other hand, some bootable GNU/Linux images depend on this bad habit. @c man .TP @item -U @kindex -U very relaxed filename rules @cindex ISO file names, very relaxed rules, -U, -untranslated-filenames @* This option allows ISO file names without dot and up to 37 characters, ISO file paths longer than 255 characters, and all ASCII characters in file names. Further it omits the semicolon and the version numbers at the end of ISO names. @* This all violates ISO 9660 specs. @c man .TP @item -untranslated-filenames @kindex -untranslated-filenames very relaxed filename rules Alias of -U. @c man .TP @item -untranslated_name_len number @kindex -untranslated_name_len untranslated file names @cindex ISO file names, untranslated, -untranslated_name_len Allow ISO file names up to the given number of characters without any character conversion. The maximum number is 96. If a file name has more characters, then image production will fail deliberately. @* This violates ISO 9660 specs. @c man .TP @item -allow-lowercase @kindex -allow-lowercase lowercase in ISO file names @cindex ISO file names, allow lowercase, -allow-lowercase Allow lowercase character in ISO file names. @* This violates ISO 9660 specs. @c man .TP @item -relaxed-filenames @kindex -relaxed-filenames 7-bit special characters in ISO file names @cindex ISO file names, 7-bit special characters, -relaxed-filenames Allow nearly all 7-bit characters in ISO file names. Not allowed are 0x0 and '/'. If not option -allow-lowercase is given, then lowercase letters get converted to uppercase. @* This violates ISO 9660 specs. @c man .TP @item -d @kindex -d omit trailing dot in ISO file names @cindex ISO file names, omit trailing dot, -d, -omit-period @* Do not add trailing dot to ISO file names without dot. @* This violates ISO 9660 specs. @c man .TP @item -omit-period @kindex -omit-period omit trailing dot in ISO file names Alias of -d. @c man .TP @item -l @kindex -l allow 31 characters in ISO file names @cindex ISO file names, allow 31 characters, -l, -full-iso9660-filenames @* Allow up to 31 characters in ISO file names. @c man .TP @item -full-iso9660-filenames @kindex -full-iso9660-filenames allow 31 characters in ISO file names Alias of -l. @c man .TP @item -max-iso9660-filenames @kindex -max-iso9660-filenames allow 37 characters in ISO file names @cindex ISO file names, allow 37 characters, -max-iso9660-filenames Allow up to 37 characters in ISO file names. @* This violates ISO 9660 specs. @c man .TP @item -N @kindex -N omit version number in ISO file names @cindex ISO file names, omit version number, -N, -omit-version-number @* Omit the semicolon and the version numbers at the end of ISO names. @* This violates ISO 9660 specs. @c man .TP @item -omit-version-number @kindex -omit-version-number omit version number in ISO file names Alias of -N. @end table @c man .TP @c man .B Settings for standards extensions: @node SetExtras, SetHide, SetCompl, Options @section Settings for standards extensions @table @asis @sp 1 @c man .TP @item -R @kindex -R Rock Ridge (is enabled by default) @cindex Rock Ridge, (enabled by default), -R, -rock @* With mkisofs this option enables Rock Ridge extensions. @command{xorrisofs} produces them by default. It is strongly discouraged to disable them by option @minus{}@minus{}norock. @c man .TP @item -rock @kindex -rock Rock Ridge (is enabled by default) @* Alias of -R. @c man .TP @item -r @kindex -r Rock Ridge with altered owner and permission @cindex Rock Ridge, altered owner and permission, -r, -rational-rock @* Enable Rock Ridge and set user and group id of all files in the ISO image to 0. Grant r-permissions to all. Deny all w-permissions. If any x-permission is set, grant x-permission to all. Remove s-bit and t-bit. @* These attribute changes stay delayed until mkisofs emulation ends. Within the same -as mkisofs emulation command they can be revoked by a subsequent option @minus{}@minus{}norock. For compatibility reasons, option -R does not revoke the changes ordered by -r. @c man .TP @item -rational-rock @kindex -rational-rock Rock Ridge with altered owner and permission Alias of -r. @c man .TP @item @minus{}@minus{}norock @kindex @minus{}@minus{}norock disable Rock Ridge production @cindex Rock Ridge, disable production, @minus{}@minus{}norock @* This option disables the production of Rock Ridge extensions for the ISO 9660 file objects. The multi-session capabilities of @command{xorrisofs} depend much on the naming fidelity of Rock Ridge. So it is strongly discouraged to disable it by this option, except for the special use case to revoke the effect of -r by: @minus{}@minus{}norock -R @c man .TP @item @minus{}@minus{}set_all_file_dates timestring @kindex @minus{}@minus{}set_all_file_dates set all file timestamps @cindex File timestamps, set all, @minus{}@minus{}set_all_file_dates Set mtime, atime, and ctime of all files and directories to the given time. @* Valid timestring formats are: 'Nov 8 14:51:13 CET 2007', 110814512007.13, 2007110814511300. See also @minus{}@minus{}modification-date= and man xorriso, Examples of input timestrings. @* If the timestring is "set_to_mtime", then the atime and ctime of each file and directory get set to the value found in their mtime. @* These actions stay delayed until actual ISO production begins. Up to then they can be revoked by @minus{}@minus{}set_all_file_dates with empty timestring or timestring "default". @* The timestamps of the El Torito boot catalog file get refreshed when the ISO is produced. They can be influenced by @minus{}@minus{}modification-date=. @c man .TP @item -file_name_limit number @kindex -file_name_limit curbs length of file names @cindex File names, curb length, -file_name_limit Set the maximum permissible length for file names in the range of 64 to 255. Path components which are longer than the given number will get truncated and have their last 33 bytes overwritten by a colon ':' and the hex representation of the MD5 of the first 4095 bytes of the whole oversized name. Potential incomplete UTF-8 characters will get their leading bytes replaced by '_'. @* Linux kernels up to at least 4.1 misrepresent names of length 254 and 255. If you expect such names in or under disk_paths and plan to mount the ISO by such Linux kernels, consider to set -file_name_limit 253. @c man .TP @item -D @kindex -D allow deep directory hierarchies @cindex Deep directories, allow, -D, -disable-deep-relocation The standard ECMA-119 demands that no path in the image shall have more than 8 name components or 255 characters. Therefore it would be necessary to move deeper directory trees to a higher directory. Rock Ridge offers an opportunity to let these relocated directories appear at their original deep position, but this feature might not be implemented properly by operating systems which mount the image. @* Option -D disables this deep directory relocation, and thus violates ISO 9660 specs. @* xorrisofs has -D set by default. If given explicitly then it overrides the options -rr_reloc_dir and -hide-rr-moved. @c man .TP @item -disable-deep-relocation @kindex -disable-deep-relocation allow deep directory hierarchies Alias of -D. @c man .TP @item -rr_reloc_dir name @kindex -rr_reloc_dir set deep directory relocation target @cindex Deep directories, relocation target, -rr_reloc_dir Enable the relocation of deep directories and thus avoid ECMA-119 file paths of more than 8 name components or 255 characters. Directories which lead to such file paths will get moved to a directory in the root directory of the image. Its name gets set by this option. It is permissible to use the root directory itself. @* The overall directory tree will appear originally deep when interpreted as Rock Ridge tree. It will appear as re-arranged if only ECMA-119 information is considered. @* If the given relocation target directory does not already exist when image production begins, then it will get created and marked for Rock Ridge as relocation artefact. At least on GNU/Linux it will not be displayed in mounted Rock Ridge images. @* The name must not contain a '/' character after its first character and it must not be longer than 255 bytes. @* This option has no effect if option -D is present. @c man .TP @item -hide-rr-moved @kindex -hide-rr-moved set deep directory relocation target @cindex Deep directories, relocation target, -hide-rr-moved Alias of -rr_reloc_dir "/.rr_moved" @c man .TP @item @minus{}@minus{}for_backup @kindex @minus{}@minus{}for_backup Enable backup fidelity @cindex Backup, enable fidelity, @minus{}@minus{}for_backup Enable all options which improve backup fidelity: @* @minus{}@minus{}acl, @minus{}@minus{}xattr-any, @minus{}@minus{}md5, @minus{}@minus{}hardlinks, and possibly @minus{}@minus{}lfa_flags. @* If you later restore a backup with xattr from non-user namespaces, then make sure that the target operating system and filesystem know what these attributes mean. Possibly you will need administrator privileges to record or restore such attributes. At recording time, xorriso will try to tolerate missing privileges and just record what is readable. @* Option @minus{}@minus{}xattr after option @minus{}@minus{}for_backup excludes non-user attributes from being recorded. @* Option @minus{}@minus{}for_backup enables @minus{}@minus{}lfa_flags only if the underlying libisofs was compiled with support for Linux file attributes, which is typically not the case on non-Linux systems. @c man .TP @item @minus{}@minus{}acl @kindex @minus{}@minus{}acl Recording of ACLs @cindex ACL, record and load, @minus{}@minus{}acl @* Enable recording and loading of ACLs from GNU/Linux or FreeBSD (see man getfacl, man acl). They will not be in effect with mounted ISO images. But xorriso can restore them on the same systems when extracting files from the ISO image. @c man .TP @item @minus{}@minus{}xattr @kindex @minus{}@minus{}xattr Recording of user xattr @cindex xattr, record and load, @minus{}@minus{}xattr @* Enable recording and loading of GNU/Linux or FreeBSD extended attributes in user namespace (see man getfattr and man attr, man getextattr and man 9 extattr, respectively). They will not be in effect with mounted ISO images. But xorriso can restore them on the same systems when extracting files from the ISO image. @c man .TP @item @minus{}@minus{}xattr-any @kindex @minus{}@minus{}xattr Recording of any xattr @cindex xattr, record and load, @minus{}@minus{}xattr-any @* Enable recording and loading of GNU/Linux or FreeBSD extended attributes in all namespaces. This might need administrator privileges, even if the owner of the disk file tries to read the attributes. @c man .TP @item @minus{}@minus{}lfa_flags @kindex @minus{}@minus{}lfa_flags Recording of Linux file attributes @cindex Linux file attributes, record and load, @minus{}@minus{}lfa_flags @* Enable recording and loading of Linux file attributes as described in man 1 chattr. @* Disable restoring of such attributes just in case that the mkisofs emulation gets ended and files get restored to disk. If restoring of the attributes is desired in this case, execute xorriso command -lfa_flags "restore" with possibly appended mode texts like ":restore_mask=aAcdDijmPsStTux". @c man .TP @item @minus{}@minus{}md5 @kindex @minus{}@minus{}md5 Recording of MD5 checksums @cindex MD5, record and load, @minus{}@minus{}md5 @* Enable recording of MD5 checksums for the overall ISO image and for each single data file in the image. xorriso can check the content of an ISO image with these sums and raise alert on mismatch. See man xorriso, options -check_media, check_md5_r. xorriso can print recorded MD5 checksums. E.g. by: @* -find / -exec get_md5 @c man .TP @item @minus{}@minus{}hardlinks @kindex @minus{}@minus{}hardlinks Recording of hardlink relations @cindex Links, record and load hard links, @minus{}@minus{}hardlinks Enable loading and recording of hardlink relations. Search for families of iso_rr files which stem from the same disk file, have identical content filtering and have identical properties. The members of each family get the same inode number in the ISO image. @* Whether these numbers are respected at mount time depends on the operating system. xorriso can create hardlink families when extracting files from the ISO image. @c man .TP @item @minus{}@minus{}scdbackup_tag disk_path record_name @kindex @minus{}@minus{}scdbackup_tag Recording of MD5 checksum @cindex scdbackup, record checksum tag, @minus{}@minus{}scdbackup_tag Append a scdbackup checksum record to the image. This works only if the parameter next_writeable_address of option -C is 0 and --md5 is enabled. If disk_path is not an empty string, then append a scdbackup checksum record to the end of this file. record_name is a word that gets part of tag and record. @* Program scdbackup_verify will recognize and verify tag and file record. @* An empty record_name disables this feature. @c man .TP @item -J @kindex -J enable production of Joliet directory tree @cindex Joliet, enable, -J, -joliet @* Enable the production of an additional Joliet directory tree along with the ISO 9660 Rock Ridge tree. @c man .TP @item -joliet @kindex -joliet enable production of Joliet directory tree Alias of -J. @c man .TP @item -joliet-long @kindex -joliet-long allow longer Joliet names @cindex Joliet, allows longer names, -joliet-long Allow 103 characters in Joliet file names rather than 64 as is prescribed by the specification. Allow Joliet paths longer than the prescribed limit of 240 characters. @* Oversized names get truncated. Without this option, oversized paths get excluded from the Joliet tree. @c man .TP @item -joliet-utf16 @kindex -joliet-utf16 use UTF-16 with Joliet names @cindex UTF-16, for Joliet paths, -joliet-utf16 Encode Joliet file names in UTF-16BE rather than UCS-2. The difference is with characters which are not present in UCS-2 and get encoded in UTF-16 by 2 words of 16 bit each. Both words then stem from a reserved subset of UCS-2. @c man .TP @item -hfsplus @kindex -hfsplus enable production of HFS+ partition @cindex HFS+, enables production Enable the production of an additional HFS+ filesystem inside the ISO 9660 image and mark it by Apple Partition Map (APM) entries in the System Area, the first 32 KiB of the image. @* This may collide with options like -G or -isohybrid-mbr which submit user data for inclusion in the same address range. The first 8 bytes of the System Area get overwritten by @{ 0x45, 0x52, 0x08 0x00, 0xeb, 0x02, 0xff, 0xff @} which can be executed as x86 machine code without negative effects. So if an MBR gets combined with this feature, then its first 8 bytes should contain no essential commands. @* The next blocks of 2 KiB in the System Area will be occupied by APM entries. The first one covers the part of the ISO image before the HFS+ filesystem metadata. The second one marks the range from HFS+ metadata to the end of file content data. If more ISO image data follow, then a third partition entry gets produced. Other features of xorriso might cause the need for more APM entries. @* Be aware that HFS+ is case-insensitive although it can record file names with upper-case and lower-case letters. Therefore, file names from the iso_rr name tree may collide in the HFS+ name tree. In this case they get changed by adding underscore characters and counting numbers. In case of very long names, it might be necessary to map them to "MANGLED_...". @* WARNING: @* The HFS+ implementation in libisofs has a limit of 125,829,120 bytes for the size of the overall directory tree. This suffices for about 300,000 files of normal name length. If the limit gets exceeded, a FAILURE event will be issued and the ISO production will not happen. @c man .TP @item -hfsplus-serial-no @kindex -hfsplus-serial-no set HFS+ serial number @cindex HFS+, set serial number Set a string of 16 digits "0" to "9" and letters "a" to "f", which will be used as unique serial number of an emerging HFS+ filesystem. @c man .TP @item -hfsplus-block-size number @kindex -hfsplus-block-size set HFS+ allocation block size @cindex HFS+, set allocation block size Set the allocation block size to be used when producing HFS+ filesystems. Permissible are 512, 2048, or 0. The latter lets the program decide. @c man .TP @item -apm-block-size number @kindex -hfsplus-block-size set APM block size @cindex HFS+, set APM block size Set the block size to be used when describing partitions by an Apple Partition Map. Permissible are 512, 2048, or 0. The latter lets the program decide. @* Note that size 512 is not compatible with production of GPT, and that size 2048 will not be mountable -t hfsplus at least by older Linux kernels. @c man .TP @item -hfsplus-file-creator-type creator type iso_rr_path @kindex -hfsplus-file-creator-type HFS+ creator-type attribute @cindex HFS+, set creator and type of file, -hfsplus-file-creator-type Set the HFS+ creator and type attributes of a file in the emerging image. These are two codes of 4 characters each. @c man .TP @item -hfs-bless-by blessing iso_rr_path @kindex -hfs-bless-by HFS+ blessing @cindex HFS+, issue blessing, -hfs-bless-by Issue a HFS+ blessing. They are roles which can be attributed to up to four directories and a data file: @* "ppc_bootdir", "intel_bootfile", "show_folder", "os9_folder", "osx_folder". @* They may be abbreviated as "p", "i", "s", "9", and "x". @* Each such role can be attributed to at most one file object. "intel_bootfile" is the one that would apply to a data file. All others apply to directories. No file object can bear more than one blessing. @c man .TP @item -hfs-bless disk_path @kindex -hfs-bless HFS+ blessing ppc_bootdir @cindex HFS+, issue blessing ppc_bootdir, -hfs-bless Issue HFS+ blessing "ppc_bootdir" to the directory which stems from the directory disk_path in the local filesystem tree. @* This works only if there is at least one data file underneath the directory. disk_path can become ambiguous if files from different local filesystem sub-trees are put into the same sub-tree of the ISO image. Consider to use -hfs-bless-by "p" for unambiguous addressing via iso_rr_path. @end table @c man .TP @c man .B Settings for file hiding: @node SetHide, ImageId, SetExtras, Options @section Settings for file hiding @table @asis @sp 1 @c man .TP @item -hide disk_path_pattern @kindex -hide keep matching files invisible in ISO tree @cindex Hiding, from ISO and Rock Ridge, -hide Make files invisible in the directory tree of ISO 9660 and Rock Ridge, if their disk_path matches the given shell parser pattern. The data content of such hidden files will be included in the resulting image, even if they do not show up in any directory. But you will need own means to find nameless data in the image. @* This command does not apply to the boot catalog. @c man .TP @item -hide-list disk_path @kindex -hide-list keep matching files invisible in ISO tree @cindex Hiding, from ISO and Rock Ridge, -hide-list Perform -hide using each line out of file disk_path as argument disk_path_pattern. @c man .TP @item -hide-joliet disk_path_pattern @kindex -hide-joliet keep matching files invisible in Joliet tree @cindex Hiding, from Joliet, -hide-joliet Like option -hide but making files invisible in the directory tree of Joliet, if their disk_path matches the given shell parser pattern. @c man .TP @item -hide-joliet-list disk_path @kindex -hide-joliet-list keep matching files invisible in Joliet tree @cindex Hiding, from Joliet, -hide-joliet-list Perform -hide-joliet using each line out of file disk_path as argument disk_path_pattern. @c man .TP @item -hide-hfsplus disk_path_pattern @kindex -hide-hfsplus keep matching files invisible in HFS+ tree @cindex Hiding, from HFS+, -hide-hfsplus Like option -hide but making files invisible in the directory tree of HFS+, if their disk_path matches the given shell parser pattern. @c man .TP @item -hide-hfsplus-list disk_path @kindex -hide-hfsplus-list keep matching files invisible in HFS+ tree @cindex Hiding, from HFS+, -hide-hfsplus-list Perform -hide-hfsplus using each line out of file disk_path as argument disk_path_pattern. @c man .TP @item -hide_iso_path hide_state iso_rr_path @kindex -hide_iso_path keep a file invisible in ISO tree @cindex Hiding, by ISO RR path, -hide_iso_path Prevent the name of the given file from showing up in the directory trees of ISO 9660 and/or Joliet and/or HFS+ when the image gets written. Other than the above hide options, this one takes the path of a file in the emerging ISO filesystem, not the path of a file on hard disk. @* Possible values of hide_state are: "iso_rr" for hiding from ISO 9660 tree, "joliet" for Joliet tree, "hfsplus" for HFS+, "on" for them all. "off" means visibility in all directory trees. @* These values may be combined. E.g.: joliet:hfsplus @* This command does not apply to the boot catalog. Rather use: @minus{}@minus{}boot-catalog-hide @end table @c man .TP @c man .B ISO image ID strings: @node ImageId, Bootable, SetHide, Options @section ISO image ID strings @c man .PP The following strings and file addresses get stored in the Primary Volume Descriptor of the ISO9660 image. The file addresses are ISO 9660 paths. These files should have iso_rr_paths which consist only of the characters [A-Z0-9_] and exactly one dot which separates at most 8 characters from at most 3 characters. @table @asis @sp 1 @c man .TP @item -V text @kindex -V set Volume Id @cindex Volume Id, set, -V, -volid Set the Volume Id of the ISO image. xorriso accepts any text up to 32 characters, but according to rarely obeyed specs stricter rules apply: @* Conformant are ASCII characters out of [A-Z0-9_]. Like: "IMAGE_23" @* Joliet allows 16 UCS-2 characters. Like: "Windows name" @* Be aware that the volume id might get used automatically as name of the mount point when the medium is inserted into a playful computer system. @c man .TP @item -volid text @kindex -volid set Volume Id Alias of -V. @c man .TP @item -volset text @kindex -volset set Volume Set Id @cindex Volume Set Id, set, -volset Set the Volume Set Id of the ISO image. Permissible are up to 128 characters. @c man .TP @item -P text @kindex -P set Publisher Id @cindex Publisher Id, set, -P, -publisher Set the Publisher Id of the ISO image. This may identify the person or organisation who specified what shall be recorded. Permissible are up to 128 characters. @c man .TP @item -publisher text @kindex -publisher set Publisher Id Alias of -P. @c man .TP @item -A text @kindex -A set Application Id @cindex Application Id, set, -A, -appid Set the Application Id of the ISO image. This may identify the specification of how the data are recorded. Permissible are up to 128 characters. @* The special text "@@xorriso@@" gets converted to the id string of xorriso which is normally written as Preparer Id. It is a wrong tradition to write the program id as Application Id. @c man .TP @item -appid text @kindex -appid set Application Id Alias of -A. @c man .TP @item -sysid text @kindex -sysid set System Id @cindex System Id, set, -sysid Set the System Id of the ISO image. This may identify the system which can recognize and act upon the content of the System Area in image blocks 0 to 15. Permissible are up to 32 characters. @c man .TP @item -p text @kindex -p set Preparer Id @cindex Preparer Id, set, -p Set the Preparer Id of the ISO image. This may identify the person or other entity which controls the preparation of the data which shall be recorded. Normally this should be the id of xorriso and not of the person or program which operates xorriso. Please avoid to change it. Permissible are up to 128 characters. @* The special text "@@xorriso@@" gets converted to the id string of xorriso which is default at program startup. @c man .TP @item -preparer text @kindex -preparer set Preparer Id Alias of -p. @c man .TP @item -abstract iso_path @kindex -abstract set Abstract File path @cindex Abstract File, set path, -abstract Set the address of the Abstract File of the ISO image. This should be the ISO 9660 path of a file in the image which contains an abstract statement about the image content. Permissible are up to 37 characters. @c man .TP @item -biblio iso_path @kindex -biblio set Biblio File path @cindex Biblio File, set path, -biblio Set the address of the Biblio File of the ISO image. This should be the ISO 9660 path of a file in the image which contains bibliographic records. Permissible are up to 37 characters. @c man .TP @item -copyright iso_path @kindex -copyright set Copyright File path @cindex Copyright File, set path, -copyright Set the address of the Copyright File of the ISO image. This should be the ISO 9660 path of a file in the image which contains a copyright statement. Permissible are up to 37 characters. @c man .TP @item @minus{}@minus{}modification-date=YYYYMMDDhhmmsscc @kindex @minus{}@minus{}modification-date set ISO image timestamps @cindex ISO image, set timestamps, @minus{}@minus{}modification-date= Set a timestring that overrides ISO image creation and modification timestamps literally. It must consist of 16 decimal digits which form YYYYMMDDhhmmsscc, with YYYY between 1970 and 2999. Time zone is GMT. It is supposed to match this GRUB line: @* search @minus{}@minus{}fs-uuid @minus{}@minus{}set YYYY-MM-DD-hh-mm-ss-cc @* E.g. 2010040711405800 is 7 Apr 2010 11:40:58 (+0 centiseconds). @* Among the influenced timestamps are: isohybrid MBR id, El Torito boot catalog file, HFS+ superblock. @c man .TP @item @minus{}@minus{}application_use character|0xXY|disk_path @kindex @minus{}@minus{}application_use set Application Use field @cindex ISO image, set Application Use field, @minus{}@minus{}application_use Specify the content of the Application Use field which can take at most 512 bytes. @* If the parameter of this command is empty, then the field is filled with 512 0-bytes. If it is a single character, then it gets repeated 512 times. If it begins by "0x" followed by two hex digits [0-9a-fA-F], then the digits are read as byte value which gets repeated 512 times. @* Any other parameter text is used as disk_path to open a data file and to read up to 512 bytes from it. If the file is smaller than 512 bytes, then the remaining bytes in the field get set to binary 0. @end table @c man .TP @c man .B El Torito Bootable ISO images: @node Bootable, SystemArea, ImageId, Options @section El Torito Bootable ISO images @c man .PP The precondition for a bootable ISO image is to have in the ISO image the files of a boot loader. The boot facilities of computers get directed to such files, which usually execute further program files from the ISO image. @command{xorrisofs} can produce several kinds of boot block or boot record, which become part of the ISO image, and get interpreted by the according boot facility. @* @c man .PP @sp 1 @cindex El Torito, _definition An @strong{El Torito} boot record points the bootstrapping facility to a boot catalog with one or more boot images, which are binary program files stored in the ISO image. The content of the boot image files is not in the scope of El Torito. @* xorriso composes the boot catalog according to the boot image files given and structured by options -b, -e, -eltorito-alt-boot, and @minus{}@minus{}efi-boot. Often it contains only one entry. @* Normally the boot images are data files inside the ISO filesystem. By special path "--interval:appended_partition_NNN:all::" it is possible to refer to an appended partition. The number NNN gives the partition number as used with the corresponding option -append_partition. E.g.: @* -append_partition 2 0xef /tmp/efi.img @* -e --interval:appended_partition_2:all:: @* El Torito gets interpreted by boot facilities PC-BIOS and EFI. Most bootable GNU/Linux CDs are equipped with ISOLINUX or GRUB boot images for PC-BIOS. @* @command{xorrisofs} supports the example options out of the ISOLINUX wiki, the options used in GRUB script grub-mkrescue, and the example in the FreeBSD AvgLiveCD wiki. @* @c man .PP @sp 1 For CD booting via boot facilities other than PC-BIOS and EFI, and for booting from USB sticks or hard disks, see the next section about the System Area. @* @table @asis @sp 1 @c man .TP @item -b iso_rr_path @kindex -b El Torito PC-BIOS boot image @cindex Bootability, control, -b, -eltorito-boot Specify the boot image file which shall be mentioned in the current entry of the El Torito boot catalog. It will be marked as suitable for PC-BIOS. @* With boot images from ISOLINUX and GRUB this option should be accompanied by options -c , -no-emul-boot , -boot-load-size 4 , -boot-info-table. @c man .TP @item -eltorito-boot iso_rr_path @kindex -eltorito-boot El Torito PC-BIOS boot image Alias of -b. @c man .TP @item -eltorito-alt-boot @kindex -eltorito-alt-boot begin next boot catalog entry @cindex Bootability, next entry, -eltorito-alt-boot Finalize the current El Torito boot catalog entry and begin a new one. A boot image file and all its necessary options shall be specified before option -eltorito-alt-boot. All further El Torito boot options apply to the new catalog entry. Up to 32 catalog entries are possible. @c man .TP @item -e iso_rr_path @kindex -e El Torito EFI boot image @cindex Bootability, control, -e Specify the boot image file which shall be mentioned in the current entry of the El Torito boot catalog. It will be marked as suitable for EFI. @* Option -e should be followed by option -no-emul-boot and no other El Torito options before an eventual -eltorito-alt-boot. @c man .TP @item @minus{}@minus{}efi-boot iso_rr_path @kindex @minus{}@minus{}efi-boot El Torito EFI boot image @cindex Bootability, control, @minus{}@minus{}efi-boot Perform -eltorito-alt-boot, option -e with the given iso_rr_path, -no-emul-boot, and again -eltorito-alt-boot. This gesture is used for achieving EFI-bootability of the GRUB2 rescue CD. @c man .TP @item -eltorito-platform "x86"|"PPC"|"Mac"|"efi"|0xnn|nnn @kindex -eltorito-platform El Torito Platform Id @cindex Bootability, control, -eltorito-platform Set the Platform Id number for the next option -b or -eltorito-boot. The number may be chosen by a platform name or by a number between 0 and 255 (0x00 and 0xFF). "x86" = 0 is for PC-BIOS, "PPC" = 1 for some PowerPC systems, "Mac" = 2 for some MacIntosh systems, "efi" = 0xEF for EFI on modern PCs with x86 compatible CPUs or others. @* If the new platform id differs from the previous one, -eltorito-alt-boot gets performed. @c man .TP @item -boot-load-size number|"full" @kindex -boot-load-size El Torito boot image load size @cindex Bootability, boot image load size, -boot-load-size Set the number of 512-byte blocks to be loaded at boot time from the boot image in the current catalog entry. @* Non-emulating BIOS bootimages usually need a load size of 4. Nevertheless the default setting of mkisofs is to use the full size of the boot image rounded up to a multiple of 4 512-byte blocks. This default may be explicitly enforced by the word "full" instead of a number. @* EFI boot images usually get set the number of blocks occupied by the boot image file. @* El Torito cannot represent load sizes higher than 65535. @c man .TP @item -hard-disk-boot @kindex -hard-disk-boot El Torito boot image emulation @cindex Bootability, boot image emulation, -hard-disk-boot Mark the boot image in the current catalog entry as emulated hard disk. (Not suitable for any known boot loader.) @c man .TP @item -no-emul-boot @kindex -no-emul-boot El Torito boot image emulation @cindex Bootability, no boot image emulation, -no-emul-boot Mark the boot image in the current catalog entry as not emulating floppy or hard disk. (This is to be used with all known boot loaders.) @* If neither -hard-disk-boot nor -no-emul-boot is given, then the boot image will be marked as emulating a floppy. (Not suitable for any known boot loader.) @c man .TP @item -eltorito-id text|56_hexdigits @kindex -eltorito-id El Torito boot section id string @cindex Bootability, El Torito section id string, -eltorito-id Define the ID string of the boot catalog section where the boot image will be listed. If the value consists of 56 characters [0-9A-Fa-f] then it is converted into 28 bytes, else the first 28 characters become the ID string. The ID string of the first boot image becomes the overall catalog ID. It is limited to 24 characters. Other id_strings become section IDs. @c man .TP @item -eltorito-selcrit hexdigits @kindex -eltorito-selcrit El Torito boot selection criteria @cindex Bootability, El Torito selection criteria, -eltorito-selcrit Define the Selection Criteria of the boot image. Up to 20 bytes get read from the given characters [0-9A-Fa-f]. They get attributed to the boot image entry in the catalog. @c man .TP @item -boot-info-table @kindex -boot-info-table Patch El Torito boot image @cindex Bootability, boot image patching, -boot-info-table Overwrite bytes 8 to 63 in the current boot image. The information will be supplied by xorriso in the course of image production: Block address of the Primary Volume Descriptor, block address of the boot image file, size of the boot image file. @c man .TP @item @minus{}@minus{}grub2-boot-info @kindex @minus{}@minus{}grub2-boot-info Patch El Torito boot image @cindex Bootability, boot image patching, @minus{}@minus{}grub2-boot-info Overwrite bytes 2548 to 2555 in the current boot image by the address of that boot image. The address is written as 64 bit little-endian number. It is the 2KB block address of the boot image content, multiplied by 4, and then incremented by 5. @c man .TP @item -c iso_rr_path @kindex -c El Torito boot catalog name @cindex Bootability, boot catalog name, -c, -eltorito-catalog Set the address of the El Torito boot catalog file within the image. This file address is not significant for the booting PC-BIOS or EFI, but it may later be read by other programs in order to learn about the available boot images. @c man .TP @item -eltorito-catalog iso_rr_path @kindex -eltorito-catalog El Torito boot catalog name Alias of -c. @c man .TP @item @minus{}@minus{}boot-catalog-hide @kindex @minus{}@minus{}boot-catalog-hide Hide El Torito boot catalog @cindex Bootability, boot catalog hidden, @minus{}@minus{}boot-catalog-hide Prevent the El Torito boot catalog from appearing as file in the directory trees of the image. @end table @c man .TP @c man .B System Area, MBR, GPT, APM, other boot blocks: @node SystemArea, Charset, Bootable, Options @section System Area, MBR, GPT, APM, other boot blocks @c man .PP @cindex System Area, _definition The first 16 blocks of an ISO image are the System Area. It is reserved for system dependent boot software. This may be the boot facilities and partition tables of various hardware architectures. @* @cindex MBR, _definition A @strong{MBR} (Master Boot Record) contains boot code and a partition table. It is read by PC-BIOS when booting from USB stick or hard disk, and by PowerPC CHRP or PReP when booting. An MBR partition with type 0xee indicates the presence of GPT. @* @cindex GPT, _definition A @strong{GPT} (GUID Partition Table) marks partitions in a more modern way. It is read by EFI when booting from USB stick or hard disk, and may be used for finding and mounting a HFS+ partition inside the ISO image. @* @cindex APM, _definition An @strong{APM} (Apple Partition Map) marks the HFS+ partition. It is read by Macs for booting and for mounting. @* MBR, GPT and APM are combinable. APM occupies the first 8 bytes of MBR boot code. All three do not hamper El Torito booting from CDROM. @* @command{xorrisofs} supports further boot facilities: MIPS Big Endian (SGI), MIPS Little Endian (DEC), SUN SPARC, HP-PA, DEC Alpha. Those are mutually not combinable and also not combinable with MBR, GPT, or APM. @c man .PP @sp 1 @cindex Interval reader for system area and partitions Several of the following options expect disk paths as input but also accept description strings for the libisofs interval reader, which is able to cut out data from disk files or -indev and to zeroize parts of the content: -G, -generic-boot, @minus{}@minus{}embedded-boot, @minus{}@minus{}grub2-mbr, -isohybrid-mbr, -efi-boot-part, -prep-boot-part, -B, -sparc-boot, -append_partition. @* The description string consists of the following components, separated by colon ':' @* "@minus{}@minus{}interval:"Flags":"Interval":"Zeroizers":"Source @* The component "@minus{}@minus{}interval" states that this is not a plain disk path but rather a interval reader description string. @* The component Flags modifies the further interpretation: @* "local_fs" demands to read from a file depicted by the path in Source. @* "imported_iso" demands to read from the -indev. This works only if -outdev is not the same as -indev. The Source component is ignored. @* "appended_partition_NNN" with a decimal number NNN works only for options which announce El Torito boot image paths: -b, -e, --efi-boot. The number gives the partition number as used with the corresponding option -append_partition. @* The component Interval consists of two byte address numbers separated by a "-" character. E.g. "0-429" means to read bytes 0 to 429. @* The component Zeroizers consists of zero or more comma separated strings. They define which part of the read data to zeroize. Byte number 0 means the byte read from the Interval start address. Each string may be one of: @* "zero_mbrpt" demands to zeroize the MBR partition table if bytes 510 and 511 bear the MBR signature 0x55 0xaa. @* "zero_gpt" demands to check for a GPT header in bytes 512 to 1023, to zeroize it and its partition table blocks. @* "zero_apm" demands to check for an APM block 0 and to zeroize its partition table blocks. @* Start_byte"-"End_byte demands to zeroize the read-in bytes beginning with number Start_byte and ending after End_byte. @* The component Source is the file path with flag "local_fs", and ignored with flag "imported_iso". @* Byte numbers may be scaled by a suffix out of @{k,m,g,t,s,d@} meaning multiplication by @{1024, 1024k, 1024m, 1024g, 2048, 512@}. A scaled value end number depicts the last byte of the scaled range. @* E.g. "0d-0d" is "0-511". @* Examples: @* "local_fs:0-32767:zero_mbrpt,zero_gpt,440-443:/tmp/template.iso" @* "imported_iso:45056d-47103d::" @* @table @asis @sp 1 @c man .TP @item -G disk_path @kindex -G Fill System Area e.g. by MBR @cindex Bootability, fill System Area e.g. by MBR, -G, @minus{}@minus{}embedded-boot, -generic-boot Copy at most 32768 bytes from the given disk file to the very start of the ISO image. @* Other than a El Torito boot image, the file disk_path needs not to be added to the ISO image. It will not show up as file in the directory trees. @* In multi-session situations, the special disk_path "." prevents reading of a disk file but nevertheless causes the adjustments in the existing MBR, which were ordered by other options. @c man .TP @item -generic-boot disk_path @kindex -generic-boot Fill System Area e.g. by MBR Alias of -G. @c man .TP @item @minus{}@minus{}embedded-boot disk_path @kindex @minus{}@minus{}embedded-boot Fill System Area e.g. by MBR Alias of -G. @c man .TP @item @minus{}@minus{}grub2-mbr disk_path @kindex @minus{}@minus{}grub2-mbr Install modern GRUB2 MBR @cindex Bootability, install modern GRUB2 MBR, @minus{}@minus{}grub2-mbr Install disk_path in the System Area and treat it as modern GRUB2 MBR. The content start address of the first boot image is converted to a count of 512 byte blocks, and an offset of 4 is added. The result is written as 64 bit little-endian number to byte address 0x1b0. @c man .TP @item -isohybrid-mbr disk_path @kindex -isohybrid-mbr Install ISOLINUX isohybrid MBR @cindex Bootability, install ISOLINUX isohybrid MBR, -isohybrid-mbr Install disk_path as ISOLINUX isohybrid MBR which makes the boot image given by option -b bootable from USB sticks and hard disks via PC-BIOS. This preparation is normally done by ISOLINUX program isohybrid on the already produced ISO image. @* The disk path should lead to one of the Syslinux files isohdp[fp]x*.bin . The MBR gets patched according to isohybrid needs. The first partition describes the range of the ISO image. Its start is at block 0 by default, but may be set to 64 disk blocks by option -partition_offset 16. @* For the meaning of special disk_path "." see option -G. @c man .TP @item -isohybrid-gpt-basdat @kindex -isohybrid-gpt-basdat Mark boot image in GPT @cindex Bootability, mark boot image in GPT, -isohybrid-gpt-basdat Mark the current El Torito boot image (see options -b and -e) in an actually invalid GPT as partition of type Basic Data. This works only with -isohybrid-mbr and has the same impact on the system area as -efi-boot-part. It cannot be combined with -efi-boot-part or -hfsplus. @* The first three boot images which are marked by GPT will also show up as partition entries in MBR. The MBR partition of type 0xEF is what actually is used by EFI firmware for booting from USB stick. The MBR partition for PC-BIOS gets type 0x00 rather than 0x17 in this case. Often the further MBR entries are the ones which actually get used by EFI. @c man .TP @item -isohybrid-gpt-hfsplus @kindex -isohybrid-gpt-hfsplus Mark boot image in GPT @cindex Bootability, mark boot image in GPT, -isohybrid-gpt-hfsplus Mark the current El Torito boot image (see options -b and -e) in GPT as partition of type HFS+. Impact and restrictions are like with -isohybrid-gpt-basdat. @c man .TP @item -isohybrid-apm-hfsplus @kindex -isohybrid-apm-hfsplus Mark boot image in APM @cindex Bootability, mark boot image in APM, -isohybrid-apm-hfsplus Mark the current El Torito boot image (see options -b and -e) in Apple Partition Map as partition of type HFS+. This works only with -isohybrid-mbr and has a similar impact on the system area as -hfsplus. It cannot be combined with -efi-boot-part or -hfsplus. @* The ISOLINUX isohybrid MBR file must begin by a known pattern of 32 bytes of x86 machine code which essentially does nothing. It will get overwritten by 32 bytes of APM header mock-up. @c man .TP @item -part_like_isohybrid @kindex -part_like_isohybrid Mark partitions like with isohybrid @cindex Bootability, partitions like with isohybrid, -part_like_isohybrid Control whether -isohybrid-gpt-basdat, -isohybrid-gpt-hfsplus, and -isohybrid-apm-hfsplus apply even if not -isohybrid-mbr is present. No MBR partition of type 0xee emerges, even if GPT gets produced. Gaps between GPT and APM partitions will not be filled by more partitions. Appended partitions get mentioned in APM if other APM partitions emerge. @c man .TP @item -iso_mbr_part_type "default"|number|type_guid @kindex -iso_mbr_part_type Set type of ISO MBR partition @cindex Bootability, type of ISO MBR partition, -iso_mbr_part_type Set the partition type of the MBR or GPT partition which represents the ISO or at least protects it. @* Number may be 0x00 to 0xff. The text "default" re-enables the default types of the various occasions to create an ISO MBR partition. This is without effect if no such partition emerges by other settings or if the partition type is prescribed mandatorily like 0xee for GPT protective MBR or 0x96 for CHRP. @* If instead a type_guid is given by a 32-digit hex string like a2a0d0ebe5b9334487c068b6b72699c7 or by a structured text like EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, then it will be used as partition type if the ISO filesystem appears as partition in GPT. In MBR, C12A7328-F81F-11D2-BA4B-00A0C93EC93B will be mapped to 0xef. Any other GUID will be mapped to 0x83. @c man .TP @item @minus{}@minus{}protective-msdos-label @kindex @minus{}@minus{}protective-msdos-label Patch System Area partition table @cindex Bootability, patch System Area partition table, @minus{}@minus{}protective-msdos-label Patch the System Area by a simple PC-DOS partition table where partition 1 claims the range of the ISO image but leaves the first block unclaimed. This is mutally exclusive to option -isohybrid-mbr. @c man .TP @item @minus{}@minus{}mbr-force-bootable @kindex @minus{}@minus{}mbr-force-bootable Enforce MBR bootable/active flag @cindex Bootability, bootable MBR partition, @minus{}@minus{}mbr-force-bootable Enforce an MBR partition with "bootable/active" flag if options like @minus{}@minus{}protective-msdos-label or @minus{}@minus{}grub2-mbr are given. These options normally cause the flag to be set if there is an MBR partition of type other than 0xee or 0xef. If no such partition exists, then no bootflag is set, unless @minus{}@minus{}mbr-force-bootable forces creation of a dummy partition of type 0x00 which covers only the first block of the ISO image. @* If no bootable MBR is indicated by other options and a partition gets created by -append_partition, then @minus{}@minus{}mbr-force-bootable causes a bootflag like it would do with e.g. @minus{}@minus{}protective-msdos-label. @c man .TP @item @minus{}@minus{}gpt-iso-bootable @kindex @minus{}@minus{}gpt-iso-bootable Set Legacy BIOS bootable flag @cindex Bootability, GPT Legacy BIOS bootable, @minus{}@minus{}gpt-iso-bootable Set bit 2 of the GPT partition flags for the ISO 9660 partition if such a GPT partition emerges. This bit is specified as "Legacy BIOS bootable" but its true significance is unclear. Some GPT-aware BIOS might want to see it in some partition. @c man .TP @item @minus{}@minus{}gpt-iso-not-ro @kindex @minus{}@minus{}gpt-iso-not-ro Do not set Read-only flag @cindex Bootability, GPT Read-only flag, @minus{}@minus{}gpt-iso-not-ro Do not set bit 60 of the GPT partition flags for the ISO 9660 partition if such a GPT partition emerges. This bit is specified as "Read-only" and thus appropriate. But it is unusual in GPT disk partitions. @c man .TP @item -partition_offset 2kb_block_adr @kindex -partition_offset Make mountable by partition 1 @cindex Mountability, by non-trivial partition 1, -partition_offset Cause a partition table with a single partition that begins at the given block address. This is counted in 2048 byte blocks, not in 512 byte blocks. If the block address is non-zero then it must be at least 16. Values larger than 16 are hardly of use. A non-zero partition offset causes two superblocks to be generated and two sets of directory trees. The image is then mountable from its absolute start as well as from the partition start. @* The offset value of an ISO image gets preserved when a new session is added to a loaded image. So the value defined here is only in effect if a new ISO image gets written. @c man .TP @item -partition_hd_cyl number @kindex -partition_hd_cyl MBR heads per cylinder @cindex MBR, sectors per head, -partition_sec_hd Set the number of heads per cylinder for the MBR partition table. 0 chooses a default value. Maximum is 255. @c man .TP @item -partition_sec_hd number @kindex -partition_sec_hd MBR sectors per head @cindex MBR, sectors per head, -partition_sec_hd Set the number of sectors per head for the MBR partition table. 0 chooses a default value. Maximum is 63. @* The product partition_sec_hd * partition_hd_cyl * 512 is the cylinder size. It should be divisible by 2048 in order to make exact alignment possible. With appended partitions and -appended_part_as_gpt there is no limit for the number of cylinders. Else there may be at most 1024 of them. If the cylinder size is too small to stay below the limit, then appropriate values of partition_hd_cyl are chosen with partition_sec_hd 32 or 63. If the image is larger than 8,422,686,720 bytes, then the cylinder size constraints cannot be fulfilled for MBR. They seem not overly important anyway. Flat block addresses in partition tables are good for 1 TiB. @c man .TP @item -partition_cyl_align mode @kindex -partition_cyl_align Image size alignment @cindex Image size, alignment, -partition_cyl_align Control image size alignment to an integer number of cylinders. It is prescribed by isohybrid specs and it seems to please program fdisk. Cylinder size must be divisible by 2048. Images larger than 8,323,596,288 bytes cannot be aligned in MBR partition table. @* Mode "auto" is default. Alignment by padding happens only if option -isohybrid-mbr is given. @* Mode "on" causes alignment by padding with option @minus{}@minus{}protective-msdos-label too. Mode "all" is like "on" but also pads up partitions from -append_partition to an aligned size. @* Mode "off" disables alignment unconditionally. @c man .TP @item -append_partition partition_number type_code disk_path @kindex -append_partition Append MBR or GPT partition after image @cindex MBR, GPT, append partition, -append_partition Cause a prepared filesystem image to be appended to the ISO image and to be described by a partition table entry in a boot block at the start of the emerging ISO image. The partition entry will bear the size of the submitted file rounded up to the next multiple of 2048 bytes or to the next multiple of the cylinder size. @* Beware of subsequent multi-session runs. The appended partition will get overwritten. @* partition_number may be 1 to 4. Number 1 will put the whole ISO image into the unclaimed space before partition 1. So together with most xorriso MBR or GPT features, number 2 would be the most natural choice. @* The type_code may be "FAT12", "FAT16", "Linux", or a hexadecimal number between 0x00 and 0xff. Not all those numbers will yield usable results. For a list of codes search the Internet for "Partition Types" or run fdisk command "L". If the partition appears in GPT then type_code 0xef is mapped to the EFI System Partition Type GUID. All others get mapped to Basic Data Type GUID. @* type_code may also be a type GUID as plain hex string like a2a0d0ebe5b9334487c068b6b72699c7 or as structured text like EBD0A0A2-B9E5-4433-87C0-68B6B72699C7. It will be used if the partition is mentioned in GPT. In MBR, C12A7328-F81F-11D2-BA4B-00A0C93EC93B will be mapped to 0xef. Any other GUID will be mapped to 0x83. In APM, 48465300-0000-11AA-AA11-00306543ECAC will be mapped to partition type "Apple_HFS", any other to "Data". @* If some other command causes the production of GPT, then the appended partitions will be mentioned there too, even if not -appended_part_as_gpt is given. @c man .TP @item -appended_part_as_gpt @kindex -appended_part_as_gpt Appended partitions in GPT @cindex GPT, mark appended partitions, -appended_part_as_gpt Marks partitions from -append_partition in GPT rather than in MBR. In this case the MBR shows a single partition of type 0xee which covers the whole output data. @* By default, appended partitions get marked in GPT only if GPT is produced because of other options. @* This option raises the maximum number of appended partitions from 4 to 8. But it is not guaranteed that the resulting GPT partition will have the given partition_number. Other GPT partitions may emerge. The final sorting by start block address may put one of them in the partition entry with the desired number, so that the appended partition will get a higher number. @* Given MBR partition types get translated. 0xef becomes C12A7328-F81F-11D2-BA4B-00A0C93EC93B, others become EBD0A0A2-B9E5-4433-87C0-68B6B72699C7. @c man .TP @item -appended_part_as_apm @kindex -appended_part_as_apm Appended partitions in APM @cindex APM, mark appended partitions, -appended_part_as_apm Marks partitions from -append_partition in Apple Partition Map, too. The partition number in APM will not be influenced by -append_partition parameter partition_number. @* By default, appended partitions get marked in APM only if APM is produced because of other options and -part_like_isohybrid is enabled. @c man .TP @item -efi-boot-part disk_path @kindex -efi-boot-part EFI boot partition @cindex Bootability, for EFI, -efi-boot-part Copy a file from disk into the emerging ISO image and mark it by a GPT entry as EFI System Partition. EFI boot firmware is supposed to use a FAT filesystem image in such a partition for booting from USB stick or hard disk. @* Instead of a disk_path, the word @minus{}@minus{}efi-boot-image may be given. It exposes in GPT the content of the first El Torito EFI boot image as EFI system partition. EFI boot images are introduced by options -e or @minus{}@minus{}efi-boot. The affected EFI boot image cannot show up in HFS+ because it is stored outside the HFS+ partition. @c man .TP @item @minus{}@minus{}gpt_disk_guid value @kindex @minus{}@minus{}gpt_disk_guid GPT GUID @cindex Disk GUID, for GPT, @minus{}@minus{}gpt_disk_guid Control whether an emerging GPT shall get a randomly generated disk GUID or whether the GUID is supplied by the user. Value "random" is default. Value "modification-date" produces a low quality GUID from the value set by option @minus{}@minus{}modification-date=. @* A string of 32 hex digits, or a RFC 4122 compliant GUID string may be used to set the disk GUID directly. UEFI prescribes the first three components of a RFC 4122 GUID string to be byte-swapped in the binary representation: @* E.g. @minus{}@minus{}gpt_disk_guid 2303cd2a-73c7-424a-a298-25632da7f446 equals @minus{}@minus{}gpt_disk_guid 2acd0323c7734a42a29825632da7f446 @* The partition GUIDs get generated by minimally varying the disk GUID. @c man .TP @item -chrp-boot-part @kindex -chrp-boot-part CHRP partition @cindex Bootability, for CHRP, -chrp-boot-part Mark the block range of the whole emerging ISO image as MBR partition of type 0x96. This is not compatible with any other feature that produces MBR partition entries. It makes GPT unrecognizable. @* CHRP is often used in conjunction with HFS. It is not yet tested whether HFS+ filesystems produced with option -hfsplus would boot on any CHRP capable machine which does not boot pure ISO 9660 as well. @c man .TP @item -chrp-boot @kindex -chrp-boot CHRP partition Alias of -chrp-boot-part. @c man .TP @item -prep-boot-part disk_path @kindex -prep-boot-part PReP partition @cindex Bootability, for PReP, -prep-boot-part Copy a file from disk into the emerging ISO image and mark it by a MBR partition entry of type 0x41. PReP boot firmware is supposed to read the content of the partition as single ELF executable file. This option is compatible with other MBR partitions and with GPT. @c man .TP @item -mips-boot iso_rr_path @kindex -mips-boot MIPS Big Endian boot image @cindex Bootability, control, -mips-boot Declare a data file in the image to be a MIPS Big Endian boot file and cause production of a MIPS Big Endian Volume Header. This is mutually exclusive with production of other boot blocks like MBR. It will overwrite the first 512 bytes of any data provided by -G. Up to 15 boot files can be declared by multiple -mips-boot options. @c man .TP @item -mipsel-boot iso_rr_path @kindex -mipsel-boot MIPS Little Endian boot image @cindex Bootability, control, -mipsel-boot Declare a data file in the image to be the MIPS Little Endian boot file. This is mutually exclusive with other boot blocks. It will overwrite the first 512 bytes of any data provided by -G. Only a single boot file can be declared by -mipsel-boot. @c man .TP @item -B disk_path[,disk_path ...] @kindex -B SUN SPARC boot images @cindex Bootability, control, -B, -sparc-boot Cause one or more data files on disk to be written after the end of the ISO image. A SUN Disk Label will be written into the first 512 bytes of the ISO image which lists this image as partition 1 and the given disk_paths as partition 2 up to 8. @* The disk files should contain suitable boot images for SUN SPARC systems. @* The pseudo disk_path "..." causes that all empty partition entries become copies of the last non-empty entry. If no other disk_path is given before "..." then all partitions describe the ISO image. In this case, the boot loader code has to be imported by option -G. @c man .TP @item -sparc-boot disk_path[,disk_path ...] @kindex -sparc-boot SUN SPARC boot images Alias of -B. @c man .TP @item -sparc-label text @kindex -sparc-label SUN Disk Label text @cindex Bootability, SUN Disk Label text, -sparc-label Set the ASCII label text of a SUN Disk Label. @c man .TP @item @minus{}@minus{}grub2-sparc-core iso_rr_path @kindex @minus{}@minus{}grub2-sparc-core SUN SPARC core file @cindex Bootability, control, @minus{}@minus{}grub2-sparc-core Cause the content address and size of the given data file in the image to be written after the SUN Disk Label. Both numbers are counted in bytes. The address is written as 64 bit big-endian number to byte 0x228. The size is written as 32 bit big-endian number to byte 0x230. @c man .TP @item -hppa-cmdline text @kindex -hppa-cmdline HP-PA PALO command line @cindex Bootability, control, -hppa-cmdline Set the PALO command line for HP-PA. Up to 1023 characters are permitted by default. With -hppa-hdrversion 4 the limit is 127. @* Note that the first five -hppa options are mandatory, if any of the -hppa options is given. Only option -hppa-hdrversion is allowed to be missing. @c man .TP @item -hppa-bootloader iso_rr_path @kindex -hppa-bootloader HP-PA bootloader file @cindex Bootability, control, -hppa-bootloader Designate the given path as HP-PA bootloader file. @c man .TP @item -hppa-kernel-32 iso_rr_path @kindex -hppa-kernel_32 HP-PA kernel_32 file @cindex Bootability, control, -hppa-kernel_32 Designate the given path as HP-PA 32 bit kernel file. @c man .TP @item -hppa-kernel-64 iso_rr_path @kindex -hppa-kernel_64 HP-PA kernel_64 file @cindex Bootability, control, -hppa-kernel_64 Designate the given path as HP-PA 64 bit kernel file. @c man .TP @item -hppa-ramdisk iso_rr_path @kindex -hppa-ramdisk HP-PA ramdisk file @cindex Bootability, control, -hppa-ramdisk Designate the given path as HP-PA RAM disk file. @c man .TP @item -hppa-hdrversion number @kindex -hppa-hdrversion HP-PA PALO header version @cindex Bootability, control, -hppa-hdrversion Choose between PALO header version 5 (default) and version 4. For the appropriate value see in PALO source code: PALOHDRVERSION. @c man .TP @item -alpha-boot iso_rr_path @kindex -alpha-boot DEC Alpha SRM bootloader @cindex Bootability, control, -alpha-boot Declare a data file in the image to be the DEC Alpha SRM Secondary Bootstrap Loader and cause production of a boot sector which points to it. This is mutually exclusive with production of other boot blocks like MBR. @end table @c man .TP @c man .B Character sets: @node Charset, Jigdo, SystemArea, Options @section Character sets @c man .PP @cindex Character sets, _definition Character sets should not matter as long as only english alphanumeric characters are used for file names or as long as all writers and readers of the medium use the same character set. Outside these constraints it may be necessary to let xorriso convert byte codes. @* A conversion from input character set to the output character set is performed when an ISO image gets written. Vice versa there is a conversion from output character set to the input character set when an ISO image gets loaded. The sets can be defined by options -input-charset and -output-charset, if needed. @* @table @asis @sp 1 @c man .TP @item -input-charset character_set_name @kindex -input-charset set character set of disk file names @cindex Character Set, for disk file names, -input-charset Set the character set from which to convert disk file names when inserting them into the ISO image. @sp 1 @c man .TP @item -output-charset character_set_name @kindex -output-charset set character set of ISO file names @cindex Character Set, for ISO file names, -output-charset Set the character set from which to convert names of loaded ISO images and to which to convert names when writing ISO images. @end table @c man .TP @c man .B Jigdo Template Extraction: @node Jigdo, Miscellaneous, Charset, Options @section Jigdo Template Extraction @c man .PP @cindex Jigdo Template Extraction, _definition From man genisoimage: "Jigdo is a tool to help in the distribution of large files like CD and DVD images; see http://atterer.net/jigdo/ for more details. Debian CDs and DVD ISO images are published on the web in jigdo format to allow end users to download them more efficiently." @* If the use of libjte was enabled at compile time of xorriso, then @command{xorrisofs} can produce a .jigdo and a .template file together with a single-session ISO image. If not, then Jigdo options will cause a FAILURE event, which normally leads to program abort. @* One may determine the ability for Jigdo by: @* @sp 1 $ xorrisofs -version 2>&1 | grep '^libjte' && echo YES @* @sp 1 @c man .PP The .jigdo file contains checksums and symbolic file addresses. The .template file contains the compressed ISO image with reference tags instead of the content bytes of the listed files. @* Input for this process are the normal arguments for a @command{xorrisofs} session with no image loaded, and a checksum file which lists those data files which may be listed in the .jigdo file and externally referenced in the .template file. Each designated file is represented in the checksum file by a single text line: @* Checksum as hex digits, 2 blanks, size as 12 decimal digits or blanks, 2 blanks, symbolic file address @* The kind of checksum is chosen by -jigdo "checksum_algorithm" with values "md5" (32 hex digits) or "sha256" (64 hex digits). It will also be used for the file address lines in the .jigdo file. The default is "md5". @* The file address in a checksum file line has to bear the same basename as the disk_path of the file which it shall match. The directory path of the file address is decisive for To=From mapping, not for file recognition. After To=From mapping, the file address gets written into the .jigdo file. Jigdo restore tools will convert these addresses into really reachable data source addresses from which they can read. @* If the list of jigdo parameters is not empty, then padding will be counted as part of the ISO image. @* @table @asis @sp 1 @c man .TP @item -jigdo-checksum-algorithm "md5"|"sha256" @kindex -jigdo-checksum-algorithm set data file checksum algorithm @cindex Jigdo Template Extraction, -jigdo-checksum-algorithm Set the checksum algorithm which shall be used for the data file entries in the .jigdo file and is expected in the checksum file. Default is "md5". @c man .TP @item -jigdo-jigdo disk_path @kindex -jigdo-jigdo set name of .jigdo file @cindex Jigdo Template Extraction, -jigdo-jigdo Set the disk_path for the .jigdo file with the checksums and download addresses for filling the holes in .template. @c man .TP @item -jigdo-template disk_path @kindex -jigdo-template set name of .template file @cindex Jigdo Template Extraction, -jigdo-template Set the disk_path for the .template file with the holed and compressed ISO image copy. @c man .TP @item -jigdo-min-file-size size @kindex -jigdo-min-file-size set minimum extract size @cindex Jigdo Template Extraction, -jigdo-min-file-size Set the minimum size for a data file to be listed in the .jigdo file and being a hole in the .template file. size may be a plain number counting bytes, or a number with appended letter "k", "m", "g" to count KiB (1024 bytes), MiB (1024 KiB), or GiB (1024 MiB). @c man .TP @item -jigdo-force-checksum disk_path_pattern @kindex -jigdo-force-checksum add check pattern for checksum file @cindex Jigdo Template Extraction, -jigdo-force-checksum adds a regular expression pattern which will get compared with the absolute disk_path of any data file that was not found in the checksum file. A match causes a MISHAP event, which normally does not abort the program run but finally causes a non-zero exit value of the program. @c man .TP @item -jigdo-force-md5 disk_path_pattern @kindex -jigdo-force-md5 add check pattern for checksum file @cindex Jigdo Template Extraction, -jigdo-force-md5 Outdated alias of -jigdo-force-checksum. @c man .TP @item -jigdo-exclude disk_path_pattern @kindex -jigdo-exclude add exclusion pattern for checksum file @cindex Jigdo Template Extraction, -jigdo-exclude Add a regular expression pattern which will get compared with the absolute disk_path of any data file. A match causes the file to stay in .template in any case. @c man .TP @item -jigdo-map To=From @kindex -jigdo-map add address translation for .jigdo @cindex Jigdo Template Extraction, -jigdo-map Add a string pair of the form To=From to the parameter list. If a data file gets listed in the .jigdo file, then it is referred by the file address from its line in the checksum file. This file address gets checked whether it begins with the From string. If so, then this string will be replaced by the To string and a ':' character, before it goes into the .jigdo file. The From string should end by a '/' character. @c man .TP @item -checksum-list disk_path @kindex -checksum-list set path of input checksum file @cindex Jigdo Template Extraction, -checksum-list Set the disk_path where to find the checksum file file with symbolic file addresses and checksums according to -jigdo-checksum-algorithm. @c man .TP @item -md5-list disk_path @kindex -md5-list set path of input checksum file @cindex Jigdo Template Extraction, -md5-list Outdated alias of -checksum-list. @c man .TP @item -jigdo-template-compress "gzip"|"bzip2" @kindex -jigdo-template-compress choose compression algorithm @cindex Jigdo Template Extraction, -jigdo-template-compress Choose one of "bzip2" or "gzip" for the compression of the template file. The jigdo file is put out uncompressed. @c man .TP @item -checksum_algorithm_iso list_of_names @kindex -checksum_algorithm_iso choose .jigdo checksums @cindex Jigdo Template Extraction, -checksum_algorithm_iso Choose one or more of "md5", "sha1", "sha256", "sha512" for the auxiliary "# Image Hex" checksums in the .jigdo file. The list_of_names may e.g. look like "md5,sha1,sha512". Value "all" chooses all available algorithms. Note that MD5 stays always enabled. @c man .TP @item -checksum_algorithm_template list_of_names @kindex -checksum_algorithm_template choose .template checksums @cindex Jigdo Template Extraction, -checksum_algorithm_template Choose the algorithms for the "# Template Hex" checksums in the .jigdo file. The rules for list_of_names are the same as with -checksum_algorithm_iso. @end table @c man .TP @c man .B Miscellaneous options: @node Miscellaneous, ExSimple, Jigdo, Options @section Miscellaneous options @table @asis @sp 1 @c man .TP @item -genisoimage_completion @kindex -genisoimage_completion completion of genisoimage options @cindex Options completion, -genisoimage_completion Match unrecognized option arguments which begin by a dash '-' against the known genisoimage options, like program genisoimage does unconditionally (and undocumentedly). If the given argument matches the beginning of exactly one genisoimage option, then it gets replaced by that option. Options which are genuine to mkisofs or xorriso's mkisofs emulation are not matched that way. Option arguments which consist entirely of a leading dash and letters out of "dDfJlNRrTUvz" are not matched but rather interpreted as usual, i.e. as multiple options with leading dash and each single letter. If no genisoimage option is found or more than one are found, then a SORRY message is issued and the argument stays as is. @* To enable this mode by default, write the xorriso command @* -genisoimage_completion on @* into one of the xorriso start files. See section FILES. @c man .TP @item -print-size @kindex -print-size predict ISO image size @cindex ISO image size, predict, -print-size Print to stdandard output the foreseeable number of 2048 byte blocks in the emerging ISO image. Do not produce this image. @* The result depends on several settings. @* If option --emul-toc is given, then padding (see -pad) is not counted as part of the image size. In this case either use -no-pad or add 150 (= 300 KiB) to the resulting number. @* If mkisofs emulation ends after option -print-size, then the properties of the most recently specified boot image file cannot be edited by subsequent xorriso commands. @c man .TP @item @minus{}@minus{}no_rc @kindex @minus{}@minus{}no_rc do not execute startup files @cindex Startup files, suppress, @minus{}@minus{}no_rc Only if used as first argument this option prevents reading and interpretation of startup files. See section FILES below. @c man .TP @item -help @kindex -help list supported options @cindex Options, list, -help @* List supported options to stderr. Original mkisofs options bear their original mkisofs description texts. @c man .TP @item -quiet @kindex -quiet suppress most messages @cindex Message output, suppress, -quiet @* Suppress most messages of the program run, except those which indicate problems or errors. @c man .TP @item -gui @kindex -gui increase frequency of pacifier messages @cindex Message output, increase frequency, -gui @* Increase the frequency of pacifier messages while writing an ISO image. @c man .TP @item -log-file disk_path @kindex -log-file redirect stderr messages @cindex Message output, redirect stderr, -log-file @* Truncate file disk_path to 0 size and redirect to it all messages which would normally appear on stderr. -log-file with empty text as disk_path re-enables output to stderr. @c man .TP @item -v @kindex -v enable verbose messages @cindex Verbosity, high, -v, -verbose @* Enable the output of informational program messages. @c man .TP @item -verbose @kindex -verbose enable verbose messages Alias of -v. @c man .TP @item -version @kindex -version report program version @cindex Program version, report, -version Print to standard output a text that begins with @* "mkisofs 2.01-Emulation Copyright (C)" @* and to standard error the version information of xorriso. @end table @c man .br @node Examples, Files, Options, Top @chapter Examples @c man .SH EXAMPLES @c man .SS @c man .B Overview of examples: @c man A simple image production run @c man .br @c man Set ISO image paths by -graft-points @c man .br @c man Perform multi-session runs @c man .br @c man Let xorrisofs work underneath growisofs @c man .br @c man Incremental backup of a few directory trees @c man .br @c man Incremental backup with accumulated trees @c man .br @c man Create bootable images for PC-BIOS and EFI @c man .br @cindex Examples @menu * ExSimple:: A simple image production run * ExGraft:: Set ISO image paths by -graft-points * ExMkisofs:: Perform multi-session runs * ExGrowisofs:: Let xorriso work underneath growisofs * ExIncBackup:: Incremental backup of a few directory trees * ExIncBckAcc:: Incremental backup with accumulated trees * ExBootable:: Create bootable images for PC-BIOS and EFI @end menu @c man .SS @c man .B A simple image production run @node ExSimple, ExGraft, Miscellaneous, Examples @section A simple image production run A prepared file tree in directory ./for_iso gets copied into the root directory of the ISO image. File permissions get set to read-only for everybody. Joliet attributes for Microsoft systems get added. The resulting image gets written as data file ./image.iso on disk. @* @sp 1 $ xorrisofs -r -J -o ./image.iso ./for_iso @c man .SS @c man .B Set ISO image paths by -graft-points @node ExGraft, ExMkisofs, ExSimple, Examples @section Set ISO image paths by -graft-points Without option -graft-points each given disk file is copied into the root directory of the ISO image, maintaining its name. If a directory is given, then its files and sub-directories are copied into the root directory, maintaining their names. @* @sp 1 $ xorrisofs ... /home/me/datafile /tmp/directory @* @sp 1 yields in the ISO image root directory: @* @sp 1 /datafile @* /file_1_from_directory @* ... @* /file_N_from_directory @* @sp 1 @c man .sp 1 With option -graft-points it is possible to put files and directories to arbitrary paths in the ISO image. @* @sp 1 $ xorrisofs ... -graft-points /home/me/datafile /dir=/tmp/directory @* @sp 1 yields in the ISO image root directory: @* @sp 1 /datafile @* /dir @* @sp 1 Eventually needed parent directories in the image will be created automatically: @* @sp 1 /datafiles/file1=/home/me/datafile @* @sp 1 yields in the ISO image: @sp 1 @* /datafiles/file1 @* @sp 1 The attributes of directory /datafiles get copied from /home/me on disk. @* @sp 1 @c man .sp 1 Normally one should avoid = and \ characters in the ISO part of a pathspec. But if it must be, one may escape them: @sp 1 @* /with_\=_and_\\/file=/tmp/directory/file @* @sp 1 yields in the ISO image: @* @sp 1 /with_=_and_\/file @c man .SS @c man .B Perform multi-session runs @node ExMkisofs, ExGrowisofs, ExGraft, Examples @section Perform multi-session runs This example works for multi-session media only: CD-R[W], DVD-R[W], DVD+R, BD-R. Add cdrskin option @minus{}@minus{}grow_overwriteable_iso to all -as cdrecord runs in order to enable multi-session emulation on overwritable media. @* The first session is written like this: @* @sp 1 $ xorrisofs -graft-points \ @* /tree1=prepared_for_iso/tree1 \ @* | xorriso -as cdrecord -v dev=/dev/sr0 blank=fast -multi -eject - @* @sp 1 Follow-up sessions are written like this (the run of dd is only to give demons a chance to spoil it): @* @sp 1 $ m=$(xorriso -as cdrecord dev=/dev/sr0 -msinfo) @* $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 @* $ xorrisofs -M /dev/sr0 -C $m -graft-points \ @* /tree2=prepared_for_iso/tree2 \ @* | xorriso -as cdrecord -v dev=/dev/sr0 -waiti -multi -eject - @* @sp 1 Always eject the drive tray between sessions. @* The run of xorriso -as mkisofs will read old sessions via the CD-ROM driver of /dev/sr0. This driver might not be aware of the changed content as long as the medium is not loaded again. In this case the previous session would not be properly assessed by xorriso and the new session would contain only the newly added files. @* Some systems have not enough patience with automatic tray loading and some demons may interfere with a first CD-ROM driver read attempt from a freshly loaded medium. @* When loading the tray manually, wait 10 seconds after the drive has stopped blinking. @* A safe automatic way seems to be a separate run of xorriso for loading the tray with proper waiting, and a subsequent run of dd which shall offer itself to any problems caused by demons assessing the changed drive status. If this does not help, insert a run of "sleep 10" between xorriso and dd. @c man .SS @c man .B Let xorrisofs work underneath growisofs @node ExGrowisofs, ExIncBackup, ExMkisofs, Examples @section Let xorriso work underneath growisofs growisofs expects an ISO formatter program which understands options -C and -M. A variable is defined to override the hardcoded default name. @* @sp 1 $ export MKISOFS="xorrisofs" @* $ growisofs -Z /dev/dvd /some/files @* $ growisofs -M /dev/dvd /more/files @* @sp 1 If no "xorrisofs" is available on your system, then you will have to create a link pointing to the xorriso binary and tell growisofs to use it. E.g. by: @* @sp 1 $ ln -s $(which xorriso) "$HOME/xorrisofs" @* $ export MKISOFS="$HOME/xorrisofs" @* @sp 1 One may quit mkisofs emulation by argument "@minus{}@minus{}" and make use of all xorriso commands. growisofs dislikes options which start with "-o" but -outdev must be set to "-". So use "outdev" instead: @* @sp 1 $ growisofs -Z /dev/dvd @minus{}@minus{}for_backup @minus{}@minus{} \ @* outdev - -update_r /my/files /files @* $ growisofs -M /dev/dvd @minus{}@minus{}for_backup @minus{}@minus{} \ @* outdev - -update_r /my/files /files @* Note that @minus{}@minus{}for_backup is given in the mkisofs emulation. To preserve the recorded extra data it must already be in effect, when the emulation loads the image. @c man .SS @c man .B Incremental backup of a few directory trees @node ExIncBackup, ExIncBckAcc, ExGrowisofs, Examples @section Incremental backup of a few directory trees This changes the directory trees /open_source_project and /personal_mail in the ISO image so that they become exact copies of their disk counterparts. ISO file objects get created, deleted or get their attributes adjusted accordingly. @* ACL, xattr, hard links and MD5 checksums will be recorded. It is expected that inode numbers in the disk filesystem are persistent over cycles of mounting and booting. Files with names matching *.o or *.swp get excluded explicitly. @* @sp 1 @c man .sp 1 To be used several times on the same medium, whenever an update of the two disk trees to the medium is desired. Begin with a blank medium and update it until he run fails gracefully due to lack of remaining space on the old one. @* Always eject the drive tray between sessions. A run of dd shall give demons a chance to spoil the first read on freshly loaded media. @* @sp 1 $ msinfo=$(xorriso -as cdrecord dev=/dev/sr0 -msinfo) @* $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 @* $ load_opts= @* $ test -n "$msinfo" && load_opts="-M /dev/sr0 -C $msinfo" @* $ xorrisofs $load_opts -o - @minus{}@minus{}for_backup -m '*.o' -m '*.swp' \ @* -V PROJ_MAIL_"$(date '+%Y_%m_%d_%H%M%S')" -graft-points \ @* -old-root / \ @* /projects=/home/thomas/projects \ @* /personal_mail=/home/thomas/personal_mail \ @* | xorriso -as cdrecord dev=/dev/sr0 -v -multi -waiti -eject - @* @sp 1 @c man .sp 1 This makes sense if the full backup leaves substantial remaining capacity on media and if the expected changes are much smaller than the full backup. @* @sp 1 @c man .sp 1 @strong{Better do not use your youngest backup for -old-root}. Have at least two media which you use alternatingly. So only older backups get endangered by the new write operation, while the newest backup is stored safely on a different medium. @* Always have a blank medium ready to perform a full backup in case the update attempt fails due to insufficient remaining capacity. This failure will not spoil the old medium, of course. @* @sp 1 @c man .sp 1 If inode numbers on disk are not persistent, then use option @minus{}@minus{}old-root-no-ino . In this case an update run will compare recorded MD5 sums against the current file content on hard disk. @* @sp 1 @c man .sp 1 With @strong{mount} option @strong{-o "sbsector="} on GNU/Linux or @strong{-s} on FreeBSD or NetBSD it is possible to access the session trees which represent the older backup versions. With CD media, GNU/Linux mount accepts session numbers directly by its option "session=". @* Multi-session media and most overwritable media written by xorriso can tell the sbsectors of their sessions by xorriso option -toc: @* @sp 1 $ xorriso -dev /dev/sr0 -toc @* @sp 1 xorriso can print the matching mount command for a session number: @* @sp 1 $ xorriso -mount_cmd /dev/sr0 session 12 /mnt @* @sp 1 or for a volume id that matches a search expression: @* @sp 1 $ xorriso -mount_cmd /dev/sr0 volid '*2008_12_05*' /mnt @* @sp 1 Both yield on standard output something like: @* mount -t iso9660 -o nodev,noexec,nosuid,ro,sbsector=1460256 '/dev/sr0' '/mnt' @* @sp 1 The superuser may let xorriso execute the mount command directly: @* @sp 1 # osirrox -mount /dev/sr0 "volid" '*2008_12_05*' /mnt @c man .SS @c man .B Incremental backup with accumulated trees @node ExIncBckAcc, ExBootable, ExIncBackup, Examples @section Incremental backup with accumulated trees Solaris does not offer the option to mount older sessions. In order to keep them accessible, one may map all files to a file tree under a session directory and accumulate those directories from session to session. The -root tree is cloned from the -old-root tree before it gets compared with the appropriate trees on disk. @* This demands to know the previously used session directory name. @* With the first session: @* @sp 1 $ xorrisofs -root /session1 \ @* -o - @minus{}@minus{}for_backup -m '*.o' -m '*.swp' \ @* -V PROJ_MAIL_"$(date '+%Y_%m_%d_%H%M%S')" -graft-points \ @* /projects=/home/thomas/projects \ @* /personal_mail=/home/thomas/personal_mail \ @* | xorriso -as cdrecord dev=/dev/sr0 -v blank=as_needed \ @* -multi -waiti -eject - @* @sp 1 @c man .sp 1 With the second session, option -old-root refers to /session1 and the new -root is /session2. @* Always eject the drive tray between sessions. A run of dd shall give demons a chance to spoil the first read on freshly loaded media. @* @sp 1 $ msinfo=$(xorriso -as cdrecord dev=/dev/sr0 -msinfo) @* $ dd if=/dev/sr0 count=1 >/dev/null 2>&1 @* $ load_opts= @* $ test -n "$msinfo" && load_opts="-M /dev/sr0 -C $msinfo" @* $ xorrisofs $load_opts -root /session2 -old-root /session1 \ @* -o - @minus{}@minus{}for_backup -m '*.o' -m '*.swp' \ @* -V PROJ_MAIL_"$(date '+%Y_%m_%d_%H%M%S')" -graft-points \ @* /projects=/home/thomas/projects \ @* /personal_mail=/home/thomas/personal_mail \ @* | xorriso -as cdrecord dev=/dev/sr0 -v -multi -waiti -eject - @* @sp 1 With the third session, option -old-root refers to /session2. The new -root is /session3. And so on. @c man .SS @c man .B Create bootable images for PC-BIOS and EFI @node ExBootable, , ExIncBckAcc, Examples @section Create bootable images for PC-BIOS and EFI The SYSLINUX/ISOLINUX boot loader suite is popular for booting PC-BIOS. The ISOLINUX wiki prescribes to create on disk a directory ./CD_root and to copy all desired files underneath that directory. Especially file isolinux.bin shall be copied to ./CD_root/isolinux/isolinux.bin . This is the boot image file. @* The prescribed mkisofs options can be used unchanged with @command{xorrisofs}: @* @sp 1 $ xorrisofs -o output.iso \ @* -b isolinux/isolinux.bin -c isolinux/boot.cat \ @* -no-emul-boot -boot-load-size 4 -boot-info-table \ @* ./CD_root @* @sp 1 Put it on CD by a burn program. E.g.: @* @sp 1 $ xorriso -as cdrecord -v dev=/dev/sr0 blank=as_needed output.iso @* @sp 1 @c man .sp 1 The image from above example will boot from CD, DVD or BD, but not from USB stick or other hard-disk-like devices. This can be done by help of an isohybrid MBR. Syslinux provides matching template files as isohdp[fp]x*.bin . E.g. /usr/lib/syslinux/isohdpfx.bin . @* If a few hundred KB of size do not matter, then option -partition_offset can be used to create a partition table where partition 1 starts not at block 0. This facilitates later manipulations of the USB stick by tools for partitioning and formatting. @* The image from the following example will be prepared for booting via MBR and its first partition will start at hard disk block 64. @* It will also boot from optical media. @* @sp 1 $ xorrisofs -o output.iso \ @* -b isolinux/isolinux.bin -c isolinux/boot.cat \ @* -no-emul-boot -boot-load-size 4 -boot-info-table \ @* -isohybrid-mbr /usr/lib/syslinux/isohdpfx.bin \ @* -partition_offset 16 \ @* ./CD_root @* @sp 1 Become superuser and copy the image to the unpartitioned base device file of the USB stick. On GNU/Linux this is e.g. /dev/sdb, not /dev/sdb1. @* CAUTION: This will overwrite any partitioning on the USB stick and make remaining data unaccessible. @* So first make sure you got the correct address of the intended device. E.g. by reading 100 MiB data from it and watching it blinking: @* @sp 1 # dd bs=2K if=/dev/sdb count=50K >/dev/null @* @sp 1 Now copy the image onto it @* @sp 1 # dd bs=2K if=output.iso of=/dev/sdb @* @sp 1 @c man .sp 1 Now for EFI: @* The boot image file has to be the image of an EFI System Partition, i.e. a FAT filesystem with directory /EFI/BOOT and boot files with EFI prescribed names: BOOTIA32.EFI for 32 bit x86, BOOTx64.EFI for 64 bit AMD/x86 (in UEFI-2.4 there is indeed a lower case "x"), BOOTAA64.EFI for 64 bit ARM. The software in the FAT filesystem should be able to find and inspect the ISO filesystem for boot loader configuration and start of operating system. GRUB2 program grub-mkimage can produce such a FAT filesystem with suitable content, which then uses further GRUB2 software from the ISO filesystem. @* EFI boot equipment may be combined with above ISOLINUX isohybrid for PC-BIOS in a not really UEFI-2.4 compliant way, which obviously works well. It yields MBR and GPT partition tables, both with nested partitions. Assumed the EFI System Partition image is ready as ./CD_root/boot/grub/efi.img, add the following options before the directory address ./CD_root: @* @sp 1 -eltorito-alt-boot -e 'boot/grub/efi.img' -no-emul-boot \ @* -isohybrid-gpt-basdat \ @* @sp 1 More compliant with UEFI-2.4 is to decide for either MBR or GPT and to append a copy of the EFI System Partition in order to avoid overlap of ISO partition and EFI partition. Here for MBR: @* @sp 1 -eltorito-alt-boot -e 'boot/grub/efi.img' -no-emul-boot \ -append_partition 2 0xef ./CD_root/boot/grub/efi.img \ @* @sp 1 The resulting ISOs are supposed to boot from optical media and USB stick. One may omit option -eltorito-alt-boot if no option -b is used to make the ISO bootable via PC-BIOS. @* @sp 1 @c man .sp 1 For ISOs with pure GRUB2 boot equipment consider to use GRUB2 tool grub-mkrescue as frontend to xorrisofs. @* @sp 1 @c man .sp 1 If you have a bootable ISO filesystem and want to know its equipment plus a proposal how to reproduce it, try: @* @sp 1 $ xorriso -hfsplus on -indev IMAGE.iso \ -report_el_torito plain -report_system_area plain \ -print "" -print "======= Proposal for xorrisofs options:" \ -report_el_torito as_mkisofs @* @sp 1 @* @sp 1 @c man .SH FILES @node Files, Environ, Examples, Top @chapter Files @c man .SS @c man .B Startup files: @section Startup Files @* If not --no_rc is given as the first argument then @command{xorrisofs} attempts on startup to read and execute lines from the following files: @* @sp 1 /etc/default/xorriso @* /etc/opt/xorriso/rc @* /etc/xorriso/xorriso.conf @* $HOME/.xorrisorc @* @sp 1 The files are read in the sequence given here, but none of them is required to exist. The lines are not interpreted as @command{xorrisofs} options but as generic xorriso commands. See man xorriso. @c man .PP After the xorriso startup files, the program tries one by one to open for reading: @* @sp 1 ./.mkisofsrc @* $MKISOFSRC @* $HOME/.mkisofsrc @* $(dirname $0)/.mkisofsrc @* @sp 1 On success it interprets the file content and does not try further files. The last address is used only if start argument 0 has a non-trivial dirname. @* The reader currently interprets the following NAME=VALUE pairs: @* @sp 1 APPI default for -A @* PUBL default for -publisher @* SYSI default for -sysid @* VOLI default for -V @* VOLS default for -volset @* @sp 1 Any other lines will be silently ignored. @* @sp 1 @c man .SH ENVIRONMENT @node Environ, Seealso, Files, Top @chapter Environ The following environment variables influence the program behavior: @* HOME is used to find xorriso and mkisofs startup files. @* MKISOFSRC may be used to point the program to a mkisofs startup file. @* SOURCE_DATE_EPOCH belongs to the specs of reproducible-builds.org. It is supposed to be either undefined or to contain a decimal number which tells the seconds since january 1st 1970. If it contains a number, then it is used as time value to set the default of @minus{}@minus{}modification-date=. @minus{}@minus{}gpt_disk_guid defaults to "modification-date". The default of @minus{}@minus{}set_all_file_dates is then "set_to_mtime". Further the "now" time for ISO nodes without disk source is then set to the SOURCE_DATE_EPOCH value. @* Startup files and program options can override the effect of SOURCE_DATE_EPOCH. @c man .SS @c man .SH SEE ALSO @c man .TP @c man For generic xorriso command mode @c man .BR xorriso(1) @c man .TP @c man For the cdrecord emulation of xorriso @c man .BR xorrecord(1) @c man .TP @c man For mounting xorriso generated ISO 9660 images (-t iso9660) @c man .BR mount(8) @c man .TP @c man Other programs which produce ISO 9660 images @c man .BR mkisofs(8), @c man .BR genisoimage(8) @c man .TP @c man Programs which burn sessions to optical media @c man .BR growisofs(1), @c man .BR cdrecord(1), @c man .BR wodim(1), @c man .BR cdrskin(1), @c man .BR xorriso(1) @c man .TP @c man ACL, xattr, Linux file attributes @c man .BR getfacl(1), @c man .BR setfacl(1), @c man .BR getfattr(1), @c man .BR setfattr(1), @c man .BR lsattr(1), @c man .BR chattr(1) @c man .TP @c man MD5 checksums @c man .BR md5sum(1) @c man .TP @c man On FreeBSD the commands for xattr and MD5 differ @c man .BR getextattr(8), @c man .BR setextattr(8), @c man .BR md5(1) @c man-ignore-lines begin @node Seealso, Bugreport, Environ, Top @chapter See also @table @asis @item For generic @command{xorriso} command mode xorriso(1) @item For the cdrecord emulation of @command{xorriso} xorrecord(1) @item For mounting xorriso generated ISO 9660 images (-t iso9660) mount(8) @item Other programs which produce ISO 9660 images mkisofs(8), genisoimage(1) @item Programs which burn sessions to optical media growisofs(1), cdrecord(1), wodim(1), cdrskin(1), xorriso(1) @item ACL and xattr getfacl(1), setfacl(1), getfattr(1), setfattr(1) lsattr(1), chattr(1) @item MD5 checksums md5sum(1) @item On FreeBSD some commands differ: getextattr(8), setextattr(8), md5(1) @end table @c man-ignore-lines end @c man .SH BUGS @node Bugreport, Legal, Seealso, Top @chapter Reporting bugs @cindex Bugs, reporting @cindex Problems, reporting To report bugs, request help, or suggest enhancements for @command{xorriso}, please send electronic mail to the public list @email{bug-xorriso@@gnu.org}. If more privacy is desired, mail to @email{scdbackup@@gmx.net}. @* @sp 1 Please describe what you expect @command{xorriso} to do, the program arguments or dialog commands by which you tried to achieve it, the messages of @command{xorriso}, and the undesirable outcome of your program run. @* @sp 1 Expect to get asked more questions before solutions can be proposed. @c man .SH AUTHOR @node Legal, CommandIdx, Bugreport, Top @chapter Author, Copyright, Credits @section Author Thomas Schmitt @* for libburnia-project.org @c man .SH COPYRIGHT @section Copyright Copyright (c) 2011 - 2024 Thomas Schmitt @* Permission is granted to distribute this text freely. It shall only be modified in sync with the technical properties of xorriso. If you make use of the license to derive modified versions of xorriso then you are entitled to modify this text under that same license. @c man .SH CREDITS @section Credits @command{xorrisofs} is in part based on work by Vreixo Formoso who provides libisofs together with Mario Danic who also leads the libburnia team. Vladimir Serbinenko contributed the HFS+ filesystem code and related knowledge. @* Compliments towards Joerg Schilling whose cdrtools served me for ten years. @c man-ignore-lines begin @node CommandIdx, ConceptIdx, Legal, Top @chapter Alphabetic Command List @printindex ky @node ConceptIdx,, CommandIdx, Top @chapter Alphabetic List of Concepts and Objects @printindex cp @c man-ignore-lines end @bye ./aclocal.m4 ./autom4te.cache ./compile ./config.guess ./config.sub ./configure ./depcomp ./install-sh ./ltmain.sh ./missing ./Makefile.in ./config.log ./config.status ./libtool Makefile ./libisofs-5.pc ./version.h .deps .dirstamp .libs test/iso *.lo *.la ./test/lsl ./test/cat test/test demo/lsl demo/cat demo/tree demo/ecma119tree demo/iso demo/catbuffer demo/isoread demo/isocat demo/isomodify demo/isoms demo/isogrow doc/html doc/doxygen.conf libisofs-1.pc demo/find make check false true make check false true true make check false true true libisofs org.eclipse.cdt.managedbuilder.core.genmakebuilder org.eclipse.cdt.make.core.cleanBuildTarget clean org.eclipse.cdt.make.core.enableCleanBuild true org.eclipse.cdt.make.core.append_environment true ?name? org.eclipse.cdt.make.core.stopOnError false org.eclipse.cdt.make.core.buildCommand make org.eclipse.cdt.make.core.contents org.eclipse.cdt.make.core.activeConfigSettings org.eclipse.cdt.make.core.buildLocation org.eclipse.cdt.make.core.environment org.eclipse.cdt.make.core.useDefaultBuildCmd true org.eclipse.cdt.make.core.enableAutoBuild true org.eclipse.cdt.make.core.enableFullBuild true org.eclipse.cdt.make.core.buildArguments -k org.eclipse.cdt.make.core.fullBuildTarget all ?children? ?name?=outputEntries\|?children?=?name?=entry\\\\\\\\\\\\\\\|\\\\\\\|\|| org.eclipse.cdt.make.core.autoBuildTarget all org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder org.eclipse.cdt.managedbuilder.core.ScannerConfigNature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.core.cnature #Sat Sep 06 00:52:28 CEST 2008 eclipse.preferences.version=1 indexer/filesToParseUpFront= indexer/indexAllFiles=true indexer/indexerId=org.eclipse.cdt.core.fastIndexer indexer/skipReferences=false indexer/skipTypeReferences=false indexerId=org.eclipse.cdt.core.fastIndexer Vreixo Formoso Mario Danic Vladimir Serbinenko Thomas Schmitt 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 Vreixo Formoso , Mario Danic , Vladimir Serbinenko Thomas Schmitt Copyright (C) 2007-2018 Vreixo Formoso, Mario Danic, Vladimir Serbinenko, Thomas Schmitt This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later as published by the Free Software Foundation. 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 git clone git@dev.lovelyhq.com:libburnia/libisofs.git (to become libisofs-1.5.8 or higher) =============================================================================== - no novelties yet - libisofs-1.5.6.pl01.tar.gz Fri Jun 09 2023 =============================================================================== * Bug fix: On non-GNU/Linux systems ssize_t was not defined in rockridge.h . Report and fix proposal by Rui Chen libisofs-1.5.6.tar.gz Wed Jun 07 2023 =============================================================================== * Bug fix: iso_write_opts_set_part_like_isohybrid() did not cause a MBR partition table if the partitions are data files in the ISO rather than appended * Bug fix: The lseek methods of IsoFileSource for local filesystem and loaded ISO returned libisofs error codes as positive off_t numbers * Bug fix: Freshly cloned data files from imported image were not marked as imported. Thanks to Ivan Shmakov. (Closes: #1022851) * Bug fix: Size of further CE area was calculated wrong if its CE entry ended exactly at a block boundary * New iso_write_opts_set_system_area() option bits 16: GPT "Legacy BIOS bootable" and 17: GPT writable * New API calls iso_assess_written_features(), iso_read_image_feature_named(), iso_read_image_features_text() * Allowed lseekable device files with iso_tree_add_new_cut_out_node(). Proof-of-concept by Ivan Shmakov. * New API call iso_write_opts_set_max_ce_entries() libisofs-1.5.4.tar.gz Sat Jan 30 2021 =============================================================================== * Bug fix: Large amounts of AAIP data or many long file names could cause with zisofs an unreadable filesystem after the warning "Calculated and written ECMA-119 tree end differ" * Bug fix: Big-Endian MIPS Volume Header boot file size was rounded up to full 2048. Thanks René Rebe. * Bug fix: El Torito production failed if no catalog path is given and the first boot image path contains no slash * Bug fix: zisofs production was wrong on big-endian machines * Bug fix: Apple Partition Map entries wrote uninitialized data * Bug fix: Appended APM partitions without HFS+ production had start and size 1 * Switched to usage of libjte-2.0.0 * Implemented production and reading of zisofs2 for files larger than 4 GiB - 1. * New struct iso_zisofs_ctrl version 2 * New API call iso_stream_get_zisofs_par() * New API call iso_stream_zisofs_discard_bpt() * New API call iso_image_zisofs_discard_bpt() * New flag bits 8 to 15 in API call iso_node_zf_by_magic() * New API call iso_zisofs_ctrl_susp_z2() * New API call iso_read_opts_set_joliet_map(), new default joliet_map=stripped * New API calls iso_read_image_features_tree_loaded() and iso_read_image_features_rr_loaded() libisofs-1.5.2.tar.gz Sat Oct 26 2019 =============================================================================== * New API calls iso_write_opts_set_part_type_guid(), iso_write_opts_set_iso_type_guid() * New API call iso_nowtime() * New flag bit2 of iso_node_set_acl_text() to be verbous about failures * Made libisofs ready for building out-of-source. Thanks Ross Burton. * Bug fix: Appended GPT partitions were not covered by the protective MBR partition * Bug fix: Multi-session emulation spoiled GPT production. "GPT partitions ... overlap". Regression towards 1.4.8 * Bug fix: Appending partitions 5 to 8 caused damaged ISO filesystems if not for SUN disk label * Bug fix: SIGSEGV happened if options bit 14 of iso_write_opts_set_system_area() is set and no El Torito boot image is defined libisofs-1.5.0.tar.gz Sat Sep 15 2018 =============================================================================== * New API call iso_image_get_ignore_aclea(), new iso_image_set_ignore_aclea() and iso_file_source_get_aa_string() flag bit3 to import all xattr namespaces * New API calls iso_image_was_blind_attrs(), iso_local_set_attrs_errno(). * New flag bit7 with iso_local_set_attrs() to avoid unnecessary write attempts. * New return value 2 of IsoFileSource.get_aa_string() and iso_local_get_attrs(). * Now putting user defined padding after appended partitions. * Bug fix: Add-on sessions with partition offset claimed too many blocks as size. Regression of version 1.4.8. * Bug fix: Long Joliet names without dot were mangled with one character too many. Long Joliet names with leading dot were mangled one char too short. * Bug fix: Reading beyond array end for HFS+ production caused SIGSEGV with FreeBSD 11 CLANG -O2. Thanks ASX of GhostBSD. libisofs-1.4.8.tar.gz Tue Sep 12 2017 =============================================================================== * Bug fix: iso_read_opts_set_no_rockridge() did not prevent reading of root SUSP. * Bug fix: Non-SUSP data in System Use Area prevented image loading if Rock Ridge was enabled. Thanks to Jonathan Dowland. * Bug fix: Protective MBR for GPT could emerge with boot flag set. * Bug fix: Appended partitions of size >= 4 GiB led to abort with error message "FATAL : ISO overwrite". Thanks to Sven Haardiek. * Bug fix: Bit 15 of iso_write_opts_set_system_area did not work with generic MBR. * Bug fix: Keeping and patching of loaded boot images failed. Regression by version 1.4.4. * Bug fix: Program crashes by intentionally wrong ISO image input. Found by American Fuzzy Lop and Jakub Wilk. Debian bug reports: 872372, 872475, 872545, 872590, 872761. * New API calls el_torito_set_full_load(), el_torito_get_full_load(). * New API call iso_write_opts_set_iso_mbr_part_type(). libisofs-1.4.6.tar.gz Fri Sep 16 2016 =============================================================================== * Bug fix: SIGSEGV by NULL when a data file was larger than ISO level allows. * Bug fix: Interpretation of 17 digit timestamps was wrong. * New API calls iso_generate_gpt_guid() and iso_write_opts_set_gpt_guid(). * Made several pseudo-random ids reproducible by overriding volume modification time. libisofs-1.4.4.tar.gz Fri Jul 01 2016 =============================================================================== * Bug fix: HFS+ production could cause MBR partition of type 0xEE without GPT. * Bug fix: Protective MBR was not produced if no System Area data are given. * Bug fix: Protective MBR was not recognized if partition is appended. * Bug fix: The HFS+ filesystem was not marked in GPT of GRUB2 hybrid layout. * Bug fix: HFS+ directories could announce more children than they actually have. * Bug fix: At image loading time GRUB2 MBR was not recognized if the partition table is not the protective one as described by UEFI. * Bug fix: Oversized text in ISO_SYSAREA_REPORT_DOC_ALPHA. Thanks to Etienne Bergeron. * New pseudo path for El Torito boot images: --interval:appened_partition_N:all:: * New bit15 with options of iso_write_opts_set_system_area() to enforce MBR bootable/active flag. * New API calls iso_write_opts_set_appended_as_apm(), iso_write_opts_set_part_like_isohybrid(). * Introduced image size tolerance of 300 kB in order to recognize SUN Disk Label that was generated by genisoimage -B "...". * Added "extern C" to libisofs.h * Removed option --silent from libtool runs. libisofs-1.4.2.tar.gz Sat Nov 28 2015 =============================================================================== * Bug fix: zisofs compression caused SIGSEGV (by reading) with files larger than 524160 KiB. * Bug fix: iso_node_get_name() of root node returned NULL pointer rather than an empty string * Bug fix: Names read from Joliet tree where stripped of trailing ";1" * Now sorting the data file content extents by ECMA-119 tree, rather than by the red-black tree which shall consolidate files with identical source object. * New API call iso_read_opts_set_ecma119_map(). * New AAIP variable isofs.nt records name truncation parameters. * Rectified handling of oversized filenames by new API calls: iso_image_set_truncate_mode, iso_image_get_truncate_mode, iso_truncate_leaf_name, iso_image_set_node_name, iso_image_tree_clone, iso_image_add_new_dir, iso_image_add_new_file, iso_image_add_new_special, iso_image_add_new_symlink, iso_image_dir_get_node, iso_image_path_to_node * Result of a Coverity audit: 50+ code changes, but no easy-to-trigger bugs libisofs-1.4.0.tar.gz Sun May 17 2015 =============================================================================== * Bug fix: iso_image_report_system_area() caused SIGSEGV by NULL if no valid ISO 9660 image was loeaded. Thanks to OmegaPhil. * Bug fix: A SIGSEGV could happen when loading a faulty ISO filesystem. Debian bug 774152. Thanks to Jakub Wilk. * Bug fix: Rock Ridge Continuation Area could be produced crossing a block boundary. This is heavily disliked by the Linux kernel and spoils the representation of directories which contain many symbolic links. * Bug fix: If iso_write_opts_set_hardlinks() enabled automatic inode numbers, then they did not get into effect with nodes were zisofs decoder filters got attached during the image load process. * Bug fix: The header indicator of the last El Torito catalog section header was set to 0x90 rather than 0x91 if more than one boot image is in that section. * Bug fix: Only 128 bytes of an emerging GPT header block were zeroized. * Bug fix: iso_image_report_system_area() did not show GPT partitions of size 0. * Bug fix: A zero sized GPT partition was marked after the last appended GPT partition. * Bug fix: GPT production did not yield proper results with appended sessions or with TOC emulation enabled. * Increased default weight of El Torito boot catalog to 1 billion. * Improved handling of cylinder alignment if the resulting image size is not divisible by 2048. Old behavior was to not align. New is to pad up by a few blocks of 512 bytes. * New API call iso_write_opts_set_appended_as_gpt() and marking of appended partitions in GPT if GPT emerges for other reasons. * New system area type 6 = DEC Alpha SRM boot sector. New API calls iso_image_set_alpha_boot(), iso_image_get_alpha_boot(). Thanks to Helge Deller. * New API object iso_interval_reader. Enabling flag bits for older API calls iso_write_opts_set_prep_img(), iso_write_opts_set_efi_bootp(), and iso_write_opts_set_partition_img(). libisofs-1.3.8.tar.gz Sat Jun 28 2014 =============================================================================== * Bug fix: Prevent allocation of empty hash tables. Thanks Richard Nolde. * Bug fix: Prevent allocation of empty directory children lists. Thanks Richard Nolde. * Bug fix: The GUIDs of main GPT and backup GPT differed if more than one System Area was written into the ISO image. * New API calls iso_image_report_el_torito() and iso_image_report_system_area() * New API call iso_crc32_gpt() libisofs-1.3.6.tar.gz Tue Mar 04 2014 =============================================================================== * Bug fix: Division by zero if HFS+ was combined with TOC emulation for overwritable media. * New API call iso_write_opts_set_joliet_utf16() and ability to read Joliet names as UTF-16BE * New API call iso_conv_name_chars() libisofs-1.3.4.tar.gz Thu Dec 12 2013 =============================================================================== * Giving sort weight 2 as default to El Torito boot images * Encoding HFS+ names in UTF-16 rather than UCS-2. libisofs-1.3.2.tar.gz Wed Aug 07 2013 =============================================================================== * Bug fix: iso_finish() left an invalid global pointer, which a subsequent call of iso_init() would try to dereference. * The sort weight of data files loaded from ISO image is now 2 exp 28 to 1 rather than 2 exp 31 - 1 to - 2 exp 31 libisofs-1.3.0.tar.gz Fri May 17 2013 =============================================================================== * Bug fix: GPT header CRC was computed from all 512 bytes rather than from 92. * Bug fix: Unspecified Expiration Time and Effective Time of ISO volume was represented by 0-bytes rather than ASCII '0' digits. * Bug fix: Reserved and unused fields of APM entries were not zeroed. * Bug fix: The protective MBR partition for GPT started at block 0 instead of 1. * New option bits with el_torito_set_isolinux_options() and iso_write_opts_set_system_area() to control GRUB2 patching of boot image and MBR. * New API calls iso_image_set_sparc_core() and iso_image_get_sparc_core(). libisofs-1.2.8.tar.gz Mon Mar 18 2013 =============================================================================== * Bug fix: Image size prediction altered the pointers to MD5 of data files which stem from a previous session. * Bug fix: Reading damaged Rock Ridge data could cause SIGSEGV by NULL. * New API call iso_image_get_pvd_times(). libisofs-1.2.6.tar.gz Tue Jan 08 2013 =============================================================================== * Bug fix: Appended partitions did not obey cylinder alignment * Bug fix: Non-zero partition offset zeroized the MBR copies of GPT partition table entries * Bug fix: Isohybrid production without any boot image led to SIGSEGV by NULL * Bug fix: Prevented a memory fault when reading damaged Rock Ridge information libisofs-1.2.4.tar.gz Fri Jul 20 2012 =============================================================================== * Bug fix: Memory corruption when reading bootable image that was truncated before the storage location of the boot catalog * Bug fix: Symbol iso_fs_global_id was missing in libisofs.ver. * Bug fix: Volume descriptors of Joliet and ISO 9660:1999 beared non-zero Effective Date, involuntarily restricting the early end of their lifetime. * Bug fix: File Structure Version field of ISO 9660:1999 Enhanced Volume Descriptor was 1 instead of 2. * Bug fix: The separator dot of Joliet names was byte swapped on big-endian machines. * Bug fix: Joliet name comparison was done as signed bytes and thus produced a peculiar sorting order. * Bug fix: Partition cylinder alignment worked only if both, El Torito and application provided system area data were present. * New API function iso_write_opts_set_hfsplus * New API functions iso_hfsplus_xinfo_func(), iso_hfsplus_xinfo_new(), and new struct iso_hfsplus_xinfo_data. * New API call iso_write_opts_set_hfsp_serial_number() * New API calls iso_image_hfsplus_bless and iso_image_hfsplus_get_blessed(), and new public enum IsoHfsplusBlessings. * New API calls so_write_opts_set_prep_img(), iso_write_opts_set_efi_bootp() * New API call iso_write_opts_set_hfsp_block_size() * New API call iso_tree_resolve_symlink() * New system area sub type CHRP with iso_write_opts_set_system_area() * New option bits 2 to 8 for GPT and APM with el_torito_set_isolinux_options() * New flag bit with iso_node_set_attrs() to protect namespace "isofs" * New IsoHideNodeFlag value LIBISO_HIDE_ON_HFSPLUS libisofs-1.2.2.tar.gz Mon Apr 02 2012 =============================================================================== * New API call iso_write_opts_set_rr_reloc() * Bug fix: Directory name mapping to ISO level 1 was too liberal if iso_write_opts_set_allow_dir_id_ext() was enabled * New API call iso_write_opts_set_allow_7bit_ascii() * Improved standards compliance for ISO level 1 names with partly relaxed constraints. libisofs-1.2.0.tar.gz Sat Jan 28 2012 =============================================================================== * Extended influence of iso_write_opts_set_dir_rec_mtime() to Joliet and ISO 9660:1999. libisofs-1.1.6.tar.gz Tue Sep 27 2011 =============================================================================== * Bug fix: On Solaris: False out-of-memory errors when writing images. * Bug fix: On FreeBSD: No ACLs were recorded. * Bug fix: ACL entries of groups and of user id 0 were not properly recorded and cannot be restored. * Bug fix: On FreeBSD: The function for restoring ACLs and xattr returned error, even if no xattr were to be restored. * New API call iso_local_attr_support() * Enabled recording and restoring of extattr on FreeBSD. libisofs-1.1.4.tar.gz Mon Aug 08 2011 =============================================================================== * Bug fix: The function for restoring ACLs and xattr returned error on systems other than Linux and FreeBSD, even if nothing was to be restored. libisofs-1.1.2.tar.gz Fri Jul 08 2011 =============================================================================== * New API call iso_image_get_bootcat() libisofs-1.1.0.tar.gz Sat Jun 18 2011 =============================================================================== * Bug fix: Padding as of iso_write_opts_set_tail_blocks() was added only after cylinder alignment as of iso_write_opts_set_system_area() and thus spoiled this alignment. libisofs-1.0.8.tar.gz Thu May 12 2011 =============================================================================== * Bug fix: iso_write_opts_set_system_area() with system area types 1=MIPS Big Endian and 2=MIPS Little Endian caused SIGSEGV. * Bug fix: SIGSEGV if the path given by iso_image_add_mips_boot_file() does not exist in the image at image production time. * Bug fix: While loading an ISO image: Several reads to malloc memory occured with byte index -1. (Found by Valgrind after years of operation without visible problems.) * Bug fix: Closed a memory leak of 32 kB per loaded ISO image. libisofs-1.0.6.tar.gz Sat Apr 09 2011 =============================================================================== * New API call iso_write_opts_set_joliet_long_names() * New error codes for oversized file addresses libisofs-1.0.4.tar.gz Thu Mar 10 2011 =============================================================================== * Bug fix: Compilation failed if --disable-zlib was configured * Bug fix: isohybrid image size was not aligned to cylinder boundary. * New no_md5 value 2 for API call iso_read_opts_set_no_md5() * New option bits 8 and 9 with iso_write_opts_set_system_area() libisofs-1.0.2.tar.gz Tue Feb 23 2011 =============================================================================== * Bug fix: iso_write_opts_set_aaip(opts, 1) could cause fatal miscalculation of the root directory size. This eventually truncated directory tree and spoiled all data file content. * Bug fix: Volume Descriptor Set Terminator contained non-zero bytes in the reserved field (ECMA-119 8.3.4). The bytes stem from the previously written Volume Descriptor. * New API calls iso_tree_clone(), iso_stream_clone. * New IsoFileSourceIface version 2 with method clone_src(). * New IsoStreamIface version 4 with method clone_stream(). * New public function prototype iso_node_xinfo_cloner. * New API calls iso_node_xinfo_make_clonable(), iso_node_xinfo_get_cloner(). * New public iso_node_xinfo_cloner instance aaip_xinfo_cloner(). * New API calls iso_node_get_next_xinfo(), iso_node_remove_all_xinfo(). * New API call iso_node_remove_tree(). * New API call iso_write_opts_set_old_empty(). libisofs-1.0.0.tar.gz Mon Jan 17 2011 =============================================================================== * Bug fix: ECMA-119 directory names were truncated to 8 characters if lowercase characters or full ASCII are allowed. * New API call iso_write_opts_set_untranslated_name_len() * New API call iso_write_opts_set_allow_dir_id_ext() * New API call iso_memory_stream_new(). (Was formely a private call.) libisofs-0.6.40.tar.gz Fri Dec 10 2010 =============================================================================== * New API call iso_write_opts_set_disc_label(), new system area type 3 = SUN Disk Label for booting SUN SPARC systems. * New API call iso_write_opts_set_will_cancel() avoids start of write thread and is to be used to inquire the future image size. * New error reply code ISO_DISPLACE_ROLLOVER for external data sources with address displacement. libisofs-0.6.38.tar.gz Sat Oct 23 2010 =============================================================================== * New API calls iso_write_opts_attach_jte() and iso_write_opts_detach_jte() allow to use libjte for jigdo production. * New API call iso_write_opts_set_tail_blocks() for tail padding inside ISO image. * New API call iso_image_generator_is_running() to learn when the write thread is done. * New API calls iso_image_add_mips_boot_file(), iso_image_get_mips_boot_files(), iso_image_give_up_mips_boot(). * New API call iso_write_opts_set_partition_img() for appending e.g. a small empty FAT12 filesystem which may be used on USB stick. libisofs-0.6.36.tar.gz Wed Sep 15 2010 =============================================================================== * New API function iso_write_opts_set_part_offset() controls creation of an MBR with a first partiton table entry that bears non-zero start address. A second set of volume descriptors and directory tree+tables gets created which can be used to mount the image at the partition start. * Hiding all non-API symbols from the linker by use of --version-script * Automatic C++ detection in libisofs.h by using macro __cplusplus * Corrected several memory leaks and potential NULL pointer evaluations in case of memory shortage. * Now with history of release notes in ./ChangeLog file. libisofs-0.6.34.tar.gz Tue Jun 29 2010 =============================================================================== * New API call iso_image_set_boot_catalog_hidden() * New API call iso_node_get_hidden() * New IsoHideNodeFlag bit LIBISO_HIDE_BUT_WRITE * New error code ISO_BOOT_NO_CATALOG * Opportunity to reduce compile line length by including "config.h" libisofs-0.6.32.tar.gz Mon May 03 2010 =============================================================================== * New API call iso_image_set_boot_catalog_weight() * New API call iso_image_add_boot_image() * New API calls el_torito_set_boot_platform_id(), el_torito_set_id_string(), el_torito_set_selection_crit() * New API calls iso_image_get_all_boot_imgs(), el_torito_get_boot_platform_id(), el_torito_get_load_seg(), el_torito_get_load_size(), el_torito_get_bootable(), el_torito_get_id_string(), el_torito_get_selection_crit(), el_torito_get_isolinux_options(), el_torito_get_boot_media_type() * New API call el_torito_seems_boot_info_table() libisofs-0.6.30.tar.gz Sat Apr 17 2010 =============================================================================== * New API call iso_write_opts_set_system_area() acts like mkisofs option -G. * New API call iso_write_opts_set_pvd_times(). * Now able to produce a bootable System Area from an ISOLINUX mbr/isohdp [fp]x*.bin file and an ISOLINUX El Torito bootable image (isolinux.bin). * Now able to produce the same Joliet names as mkisofs. * New API calls iso_read_opts_load_system_area() and iso_image_get_system_area() for multi-session handling of MBRs. libisofs-0.6.28.tar.gz Wed Feb 10 2010 =============================================================================== * Bug fix: Random checksum index could sneak in via boot catalog node and cause a SIGSEGV. * Improved compilability out of the box on FreeBSD. libisofs-0.6.26.tar.gz Wed Jan 20 2010 =============================================================================== * Bug fix: Invalid old checksum tags were preserved with iso_write_opts_set_overwrite_buf(), if the new session produced no checksums. * The checksum buffer for the emerging image gets now marked as invalid if image generation is canceled. * More graceful reaction on filesystems where ACL are not enabled but nevertheless requested by the application. * Adaptions to problems reported by Debian buildd. libisofs-0.6.24.tar.gz Thu Oct 08 2009 =============================================================================== * Bug fix: Short Rock Ridge names got stripped of trailing blanks when loaded and written again to a follow-up session. Long names could lose inner blanks. * Bug fix: Avoided to return NULL or single blanks as content of id strings by API calls iso_image_get_volset_id() ... iso_image_get_biblio_file_id(). * New API call iso_write_opts_set_scdbackup_tag(). libisofs-0.6.22.tar.gz Tue Aug 25 2009 =============================================================================== * New API call iso_write_opts_set_record_md5() for writing MD5 sums. * New API call iso_read_opts_set_no_md5() for importing MD5 sums. * New API calls iso_image_get_session_md5() and iso_file_get_md5(). * New API calls iso_md5_start(), iso_md5_compute(), iso_md5_clone(), iso_md5_end(), iso_md5_match() for own MD5 computations. * New API call iso_util_decode_md5_tag() to recognize and parse checksum tags. * New API call iso_file_make_md5() to equip old file nodes with MD5. * Improvements with ./configure and its help text. libisofs-0.6.20.tar.gz Sun May 30 2009 =============================================================================== * Optional automatic detection and recording of hard link relations between files. * Support for restoring hard link relations by the app. libisofs-0.6.18.tar.gz Fri Apr 17 2009 =============================================================================== * Opportunity to set the input charset automatically from an eventual xattr "isofs.cs" of the image root node. * New general filter API to inquire and remove filters. * Specialized APIs for installing filters which are based on external processes or based on zlib. * New API call to inquire the original source path of a data file in an emerging image. libisofs-0.6.16.tar.gz Wed Mar 11 =============================================================================== * Bug fix: The ".." directory record pointed to the same data block as the "." entry. * Bug fix: The use of iso_write_opts_set_rrip_version_1_10() caused a wrong size announcement in the CE entry which points to the ER signature of the image root. * New API call iso_write_opts_get_data_start() inquires the start address of the data section of an emerging ISO image. * ISO image generation does not absolutely depend on the availability of character set "WCHAR_T" with iconv_open(3) any more. libisofs-0.6.14.tar.gz Sat Feb 28 2009 =============================================================================== * New API calls iso_image_set_ignore_aclea(), iso_read_opts_set_no_aaip() control import of ACL and xattr. * New API calls iso_write_opts_set_aaip(), iso_write_opts_set_aaip_susp_1_10() control output of ACL and xattr into generated ISO image. * New API call iso_file_source_get_aa_string(), new function member get_aa_string() in IsoFileSource_Iface allow to access opaquely encoded ACL and xattr. New function handle aaip_xinfo_func attaches aa_strings to IsoNode objects. * New API calls iso_node_get_acl_text(), iso_node_set_acl_text(), iso_node_get_perms_wo_acl() allow inquiry and manipulation of ACLs in IsoNode objects. * New API calls iso_node_get_attrs(), iso_node_set_attrs() allow inquiry and manipulation of xattr in IsoNode objects. libisofs-0.6.12.tar.gz Wed Nov 26 2008 =============================================================================== * New API calls iso_set_local_charset() and iso_get_local_charset() * New API calls iso_write_opts_set_rrip_version_1_10() and iso_write_opts_set_dir_rec_mtime() * New API call el_torito_set_isolinux_options() allows to patch ISOLINUX boot images and to generate a isohybrid MBR on the fly. Such an MBR makes the ISO image bootable from disk-like hardware, e.g. from USB stick. The ISOLINUX boot image has to be of syslinux 3.72 or later to allow MBR generation. * Old API call el_torito_patch_isolinux_image() is deprecated now. libisofs-0.6.10.pl01.tar.gz Wed Nov 19 2008 =============================================================================== * Bug fix: If images generated by mkisofs were loaded then files of size 0 could share their size information with files that contain data. Ticket #144. * Bug fix: ISOLINUX boot images were patched suitable for El Torito but not for an eventual MBR added by SYSLINUX script isohybrid. libisofs 0.6.10 Mon Oct 6 2008: =============================================================================== * Bug fix: Patching of existing ISOLINUX boot images led to a SIGSEGV. * Bug fix: Adding a new ISOLINUX boot image or patching of an existing one caused a read operation although writing had already begun. libisofs-0.6.8.tar.gz Thu Sep 18 2008 =============================================================================== * Support for very large data files in the ISO 9660 image (Level 3, multi-extent) * Bug fix: it was assumed that isolinux images were always a multiple of 4 bytes * New API call iso_image_update_sizes() to refresh recorded file sizes immediately before image generation begins libisofs-0.6.6.tar.gz Sun Jun 1 2008 =============================================================================== * Bug fix: major,minor numbers of device files were not read properly from existing images * Bug fix: iso_tree_path_to_node() returned 1 if a directory path component was a non-directory file * New API call iso_special_get_dev() retrieves major, minor numbers of device files libisofs-0.6.4.tar.gz Sun Apr 27 2008 =============================================================================== * Extended information: iso_node_add_xinfo() * New node iteration: iso_dir_find_children() * Custom image file content via iso_tree_add_new_file() * Missing feature added to map a disk file to an arbitrary image file path via iso_tree_add_new_node() * Obtain image path of a node object via iso_tree_get_node_path() * Various bugfixes libisofs-0.6.2.1.tar.gz Thu Feb 14 2008 =============================================================================== * FIX: missing buffer.h preventing build from succeeding Libisofs 0.6.2 =============================================================================== * Initial release of new generation libisofs * Completely new API * Long term commitment to ABI libisofs.so.6 Installation Instructions ************************* Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== Briefly, the shell commands `./configure; make; make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf bug. Until the bug is fixed you can use this workaround: CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. # Copyright (c) 2007 Vreixo Formoso # Copyright (c) 2009 - 2024 Thomas Schmitt # Provided under the terms of the GNU General Public License version 2 or later. # ts A90315 : LIBBURNIA_PKGCONFDIR is defined OS specific in acinclude.m4 # was: pkgconfigdir=$(libdir)/pkgconfig pkgconfigdir=$(LIBBURNIA_PKGCONFDIR) libincludedir=$(includedir)/libisofs lib_LTLIBRARIES = libisofs/libisofs.la ACLOCAL_AMFLAGS = -I ./ # Enable this if the source includes generated files like version.h # AM_CPPFLAGS = -I $(top_builddir)/libisofs ## ========================================================================= ## # Build libraries libisofs_libisofs_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) $(LIBLDFLAGS) # Enabling system adapters for ACL and EA. # ts A90409: Enabling use of zlib. # ts B00927: Enabling use of libjte (Jigdo Template Extraction) # ts C40713: Enabling system adapter for Linux chattr(1) flags libisofs_libisofs_la_CFLAGS = $(LIBACL_DEF) $(XATTR_DEF) $(LFA_DEF) \ $(ZLIB_DEF) $(LIBJTE_DEF) # ts A90114 : added aaip_0_2.* libisofs_libisofs_la_SOURCES = \ libisofs/builder.h \ libisofs/builder.c \ libisofs/node.h \ libisofs/node.c \ libisofs/tree.h \ libisofs/tree.c \ libisofs/find.c \ libisofs/image.h \ libisofs/image.c \ libisofs/fsource.h \ libisofs/fsource.c \ libisofs/fs_local.c \ libisofs/fs_image.c \ libisofs/messages.h \ libisofs/messages.c \ libisofs/libiso_msgs.h \ libisofs/libiso_msgs.c \ libisofs/stream.h \ libisofs/stream.c \ libisofs/filter.h \ libisofs/filter.c \ libisofs/filters/external.c \ libisofs/filters/zisofs.c \ libisofs/filters/gzip.c \ libisofs/util.h \ libisofs/util.c \ libisofs/util_rbtree.c \ libisofs/util_htable.c \ libisofs/filesrc.h \ libisofs/filesrc.c \ libisofs/ecma119.h \ libisofs/ecma119.c \ libisofs/ecma119_tree.h \ libisofs/ecma119_tree.c \ libisofs/writer.h \ libisofs/buffer.h \ libisofs/buffer.c \ libisofs/rockridge.h \ libisofs/rockridge.c \ libisofs/rockridge_read.c \ libisofs/joliet.h \ libisofs/joliet.c \ libisofs/hfsplus.h \ libisofs/hfsplus.c \ libisofs/hfsplus_decompose.c \ libisofs/hfsplus_classes.c \ libisofs/hfsplus_case.c \ libisofs/eltorito.h \ libisofs/eltorito.c \ libisofs/system_area.h \ libisofs/system_area.c \ libisofs/make_isohybrid_mbr.c \ libisofs/iso1999.h \ libisofs/iso1999.c \ libisofs/data_source.c \ libisofs/aaip_0_2.h \ libisofs/aaip_0_2.c \ libisofs/md5.h \ libisofs/md5.c libisofs_libisofs_la_LIBADD= \ $(THREAD_LIBS) libinclude_HEADERS = \ libisofs/libisofs.h install-exec-hook: $(LIBBURNIA_LDCONFIG_CMD) "$(DESTDIR)$(libdir)" || echo 'NOTE: Explicit dynamic library configuration failed. If needed, configure manually for:' "$(DESTDIR)$(libdir)" ## ========================================================================= ## ## Build demo applications noinst_PROGRAMS = \ demo/demo # demo/tree \ # demo/find \ # demo/iso \ # demo/isoread \ # demo/isocat \ # demo/isomodify \ # demo/isoms # demo/ecma119tree \ # demo/lsl \ # demo/cat \ # demo/catbuffer \ # demo/isogrow # ts A90807 # Consolidated demo code for having less linker messages with a make run. demo_demo_CPPFLAGS = -I $(top_srcdir)/libisofs demo_demo_LDADD = $(libisofs_libisofs_la_OBJECTS) $(libisofs_libisofs_la_LIBADD) demo_demo_SOURCES = demo/demo.c # ts A90806 # This includes fsource.h and thus is no API demo # demo_lsl_CPPFLAGS = -Ilibisofs # demo_lsl_LDADD = $(libisofs_libisofs_la_OBJECTS) $(libisofs_libisofs_la_LIBADD) # demo_lsl_SOURCES = demo/lsl.c # ts A90806 # This includes fsource.h and thus is no API demo # demo_cat_CPPFLAGS = -Ilibisofs # demo_cat_LDADD = $(libisofs_libisofs_la_OBJECTS) $(libisofs_libisofs_la_LIBADD) # demo_cat_SOURCES = demo/cat.c # ts A90806 # This inlcudes buffer.h and thus is no API demo # demo_catbuffer_CPPFLAGS = -Ilibisofs # demo_catbuffer_LDADD = $(libisofs_libisofs_la_OBJECTS) \ # $(libisofs_libisofs_la_LIBADD) # demo_catbuffer_SOURCES = demo/cat_buffer.c # ts A90807 # Consolidated in demo/demo # demo_tree_CPPFLAGS = -Ilibisofs # demo_tree_LDADD = $(libisofs_libisofs_la_OBJECTS) \ # $(libisofs_libisofs_la_LIBADD) # demo_tree_SOURCES = demo/tree.c # ts A90807 # Consolidated in demo/demo # demo_find_CPPFLAGS = -Ilibisofs # demo_find_LDADD = $(libisofs_libisofs_la_OBJECTS) \ # $(libisofs_libisofs_la_LIBADD) # demo_find_SOURCES = demo/find.c # ts A90806 # This inlcudes lots of internal .h files and thus is no API demo # demo_ecma119tree_CPPFLAGS = -Ilibisofs # demo_ecma119tree_LDADD = $(libisofs_libisofs_la_OBJECTS) \ # $(libisofs_libisofs_la_LIBADD) # demo_ecma119tree_SOURCES = demo/ecma119_tree.c # ts A90807 # Consolidated in demo/demo # demo_iso_CPPFLAGS = -Ilibisofs # demo_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(libisofs_libisofs_la_LIBADD) # demo_iso_SOURCES = demo/iso.c # ts A90807 # Consolidated in demo/demo # demo_isoread_CPPFLAGS = -Ilibisofs # demo_isoread_LDADD = $(libisofs_libisofs_la_OBJECTS) \ # $(libisofs_libisofs_la_LIBADD) # demo_isoread_SOURCES = demo/iso_read.c # ts A90807 # Consolidated in demo/demo # demo_isocat_CPPFLAGS = -Ilibisofs # demo_isocat_LDADD = $(libisofs_libisofs_la_OBJECTS) \ # $(libisofs_libisofs_la_LIBADD) # demo_isocat_SOURCES = demo/iso_cat.c # ts A90807 # Consolidated in demo/demo # demo_isomodify_CPPFLAGS = -Ilibisofs # demo_isomodify_LDADD = $(libisofs_libisofs_la_OBJECTS) \ # $(libisofs_libisofs_la_LIBADD) # demo_isomodify_SOURCES = demo/iso_modify.c # ts A90807 # Consolidated in demo/demo # demo_isoms_CPPFLAGS = -Ilibisofs # demo_isoms_LDADD = $(libisofs_libisofs_la_OBJECTS) \ # $(libisofs_libisofs_la_LIBADD) # demo_isoms_SOURCES = demo/iso_ms.c # demo_isogrow_CPPFLAGS = -Ilibisofs -Ilibburn # demo_isogrow_LDADD = $(libisofs_libisofs_la_OBJECTS) \ # $(libisofs_libisofs_la_LIBADD) \ # -lburn # demo_isogrow_SOURCES = demo/iso_grow.c ## ts A90428 , ticket 147, The test code does not use the API and is totally ## outdated in its creation of mocked objects. ## A volunteer is needed to rewrite it using the API. # ## Build unit test # check_PROGRAMS = \ # test/test # # test_test_CPPFLAGS = -Ilibisofs # test_test_LDADD = $(libisofs_libisofs_la_OBJECTS) \ # $(libisofs_libisofs_la_LIBADD) -lcunit # test_test_LDFLAGS = -L.. -lm # # test_test_SOURCES = \ # test/test.h \ # test/test.c \ # test/test_node.c \ # test/test_image.c \ # test/test_tree.c \ # test/test_util.c \ # test/test_rockridge.c \ # test/test_stream.c \ # test/mocked_fsrc.h \ # test/mocked_fsrc.c # "make clean" shall remove a few stubborn .libs directories # which George Danchev reported Dec 03 2011. # Learned from: http://www.gnu.org/software/automake/manual/automake.html#Clean clean-local: -rm -rf demo/.libs ## ========================================================================= ## ## Build documentation (You need Doxygen for this to work) docdir = $(DESTDIR)$(prefix)/share/doc/$(PACKAGE)-$(VERSION) doc: doc/html doc/html: doc/doxygen.conf $(RM) -r doc/html; \ doxygen doc/doxygen.conf; install-data-local: if [ -d doc/html ]; then \ $(mkinstalldirs) $(docdir)/html; \ $(INSTALL_DATA) doc/html/* $(docdir)/html; \ fi uninstall-local: rm -rf $(docdir) ## ========================================================================= ## # Extra things nodist_pkgconfig_DATA = \ libisofs-1.pc # ts A80114 : added aaip-os* EXTRA_DIST = \ bootstrap \ libisofs-1.pc.in \ version.h.in \ doc/doxygen.conf.in \ doc/Tutorial \ README \ AUTHORS \ COPYRIGHT \ COPYING \ NEWS \ INSTALL \ TODO \ ChangeLog \ Roadmap \ doc/susp_aaip_2_0.txt \ doc/susp_aaip_isofs_names.txt \ doc/zisofs_format.txt \ doc/zisofs2_format.txt \ doc/checksums.txt \ doc/boot_sectors.txt \ libisofs/libisofs.ver \ libisofs/aaip-os-dummy.c \ libisofs/aaip-os-linux.c \ libisofs/aaip-os-freebsd.c == Sun Apr 27 2008 == Libisofs v0.6.4 =============== - Extended information: iso_node_add_xinfo() - New node iteration: iso_dir_find_children() - Custom image file content via iso_tree_add_new_file() - Missing feature added to map a disk file to an arbitrary image file path via iso_tree_add_new_node() - Obtain image path of a node object via iso_tree_get_node_path() - Various bugfixes == Fri Feb 22 2008 == Libisofs v0.6.2.1 ================= - FIX: missing buffer.h in tarball == Thu Feb 14 2008 == Libisofs v0.6.2 ================ - Initial release ------------------------------------------------------------------------------ libisofs ------------------------------------------------------------------------------ Released under GNU General Public License version 2 or later. See COPYING file for details. Copyright (C) 2008 - 2023 Vreixo Formoso, Mario Danic, Vladimir Serbinenko, Thomas Schmitt libisofs is part of the libburnia project (libburnia-project.org) ------------------------------------------------------------------------------ Download, Build and Installation libisofs code is maintained in a git repository at dev.lovelyhq.com (https://dev.lovelyhq.com/libburnia/libisofs). You can download it with: $ git clone https://dev.lovelyhq.com/libburnia/libisofs.git Our build system is based on autotools. For preparing the build you will need autotools of at least version 1.7. If you have downloaded the code from the repository, first of all you need to execute ./bootstrap in the toplevel directory ./libisofs, in order to execute autotools. Alternatively you may unpack a release tarball for which you do not need autotools installed. For the most recent release of libisofs see: https://dev.lovelyhq.com/libburnia/web/wiki/Releases To build libisofs go into its toplevel directory and execute ./configure --prefix=/usr make To make the libraries accessible for running and developing applications make install On GNU/Linux it will try to run program ldconfig with the library installation directory as only argument. Failure to do so will not abort installation. One may disable ldconfig by ./configure option --disable-ldconfig-at-install . By use of a version script, the libisofs.so library exposes no other function names but those of the API definitions in . If -Wl,--version-script=... makes problems with the local compiler, then disable this encapsulation feature by ./configure --disable-versioned-libs make clean ; make The ./configure script checks for the availability of supporting libraries. If found, they will become mandatory for the emerging libisofs.so and all applications which use it. This dependency can be avoided by configure options --disable-libacl avoid use of ACL functions like acl_to_text() --disable-xattr avoid use of xattr functions like listxattr() --disable-zlib avoid use of zlib functions like compress2() --disable-libjte avoid use of libjte functions See INSTALL file for general options of ./configure. ------------------------------------------------------------------------------ libisofs is a library to create an ISO-9660 filesystem, supports extensions like RockRidge or Joliet, and introduces an own extension AAIP. It is a full featured ISO-9660 editor which composes and changes the directory tree of an ISO image. This tree and its newly imported data file contents get then written as independent single-session image or as add-on session for the image from where the tree was originally loaded. Features: --------- - Image creation - Creates ISO-9660 images from local files. - Support for RockRidge and Joliet extensions. - Support for ISO-9660:1999 (version 2). - Support for El-Torito bootable images. Tested are: PC-BIOS and EFI. - Support for multi-extent data files up to 400 GB (level 3). - Full-featured edition of the image files, including: addition of new files, removing of existent files, moving files, renaming files, change file attributes (permissions, timestamps...) - Optional recording per file of non-ISO 9660 features: ACL, xattr, content MD5, hard link relations. They do not hamper image readability by operating systems but can be retrieved only via libisofs. - Optional zisofs compression, gzip compression, external filter processes. - Several options to relax ISO-9660 constraints. - Special options for images intended for distribution (suitable default modes for files, hiding of real timestamps...). - Image reading - Image tree and data heap can be verified by stream reading and eventually recorded MD5 tags. - Directory tree and file attributes of ISO 9660 session get loaded into memory for editing or for extraction into local filesystem. - File content can be read by applications. - Automatic zisofs decompression. - Optional application of gzip decompression or external filter processes. - Eventually recorded MD5 of data file can be obtained, MD5 of data stream can be computed and compared. - Helper functions for restoring ACL and/or xattr to the local filesystem. - Multisession - Support for growing an existing image on multi-session media. - Support for "emulated multisession" on overwriteable media such as DVD+RW, USB sticks, regular files. - Support for blindly prepared add-on sessions (mkisofs style -M -C) suitable for pipes which lead to an external burn program. - Image modification - Creates a completely new image from files out of another image and eventual editing operations. Suitable for any target medium. - Others - Handling of different input and output charset. - Good integration with libburn for image burning. - Reliable, good handling of different kind of errors. Requirements: ------------- - iconv() functions for character set conversion must be available. Either implicitely as in Linux or by a separate library like libiconv on FreeBSD. Know bugs: ---------- Multisession and image growing can lead to undesired results in several cases: a) Images with unsupported features, such as: - UDF. - HSF/HFS+ or other Mac extensions. - ECMA-119 Extended attributes. - ... In all these cases, the resulting new image (or new session) could lack some features of the original image. Nevertheless, the ECMA-119 System Area with an eventual Master Boot Record gets preserved by default. In some cases libisofs will issue warning messages, or even refuse to grow or modify the image. Others remain undetected. Images created with libisofs do not have this problems. b) Bootable El-Torito images may have problems, that result in a new image that is not bootable, or that boots from an outdated session. In some cases it might be necessary to add boot info again in a new first session. - There is no safe way to modify hidden boot images, as the size of the boot image can't be figured out. c) Generated images could have different ECMA-119 low level names, due to different way to mangle names, to new files added that force old files to be renamed, to different relaxed contraints... This only affect the ISO-9660 info, not the RR names, so it shouldn't be a problem in most cases. If your app. relies on low level ISO-9660 names, you will need to ensure all node names are valid ISO names (maybe together with some relaxed contraints), otherwise libisofs might arbitrarily change the names. ------------------------------------------------------------------------------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------------ Clarification in my name and in the name of Mario Danic, upcoming copyright holders on toplevel of libburnia. To be fully in effect after the remaining other copyrighted code has been replaced by ours and by copyright-free contributions of our friends. Note: In the particular case of libisofs there is no foreign copyright involved. As of 2010 foreign copyright is only in component libburn. ------------------------------------------------------------------------------ We will not raise any legal protest to dynamic linking of our libraries with applications that are not under GPL, as long as they fulfill the condition of offering the library source code used, whether altered or unaltered, under the GPLv2+, along with the application. Nevertheless, the safest legal position is not to link libburn with non-GPL compatible programs. We ask you politely to use our work in open source spirit and with the due reference to the entire open source community. If there should really arise the case where above clarification does not suffice to fulfill a clear and neat request in open source spirit that would otherwise be declined for mere formal reasons, only in that case we will duely consider to issue a special license covering only that special case. It is the open source idea of responsible freedom which will be decisive and you will have to prove that you exhausted all own means to qualify for GPL. We are firmly committed to allow GPLv2+ now and with future releases. Signed: Mario Danic, Thomas Schmitt Agreement joined later by: Vreixo Formoso Public contact: >>>>>>>>>> RELEASE 0.6.1 (development) >>>>>>>>>>>>>>>>>>>>> - Review error severities OK - Prepare API for stability and compatibility check - Documentation >>>>>>>>>> RELEASE 0.6.2 (stable) >>>>>>>>>>>>>>>>>>>>>>>>>> - Intensive testing and bug fixing >>>>>>>>>> RELEASE 0.6.3 (development) >>>>>>>>>>>>>>>>>>>>> - Improves to public tree -> Expose node extended info. Always compile it. (little memory cost) -> Review builder / tree / node relation -> Optimize storage of children in node? -> Inode object? - Expose Builder and Streams - Implement filters: compression, encryption... - Consider some kind of plugin system for Builders, Filesystems and Filters. - ECMA-119, Joliet, and ISO-9660:1999 writers can share most of the code. Create a new writer as a generalization of these. - Update Java bindings >>>>>>>>>>> ...... >>>>>>>>>>> RELEASE 1.0.0 (stable) >>>>>>>>>>>>>>>>>>>>>>>>>> - UDF - HFS FEATURES ======== TODO ==== #00001 (node.h) -> consider adding new timestamps to IsoTreeNode #00004 (libisofs.h) -> Add a get_mime_type() function. #00005 (node.c) -> optimize iso_dir_iter_take. #00006 (libisofs.h) -> define more replace values when adding a node to a dir #00008 (data_dource.c) -> guard against partial reads #00009 (ecma119_tree.c/h) -> add true support for harlinks and inode numbers #00010 (buffer.c) -> optimize ring buffer #00011 (ecma119.c) -> guard against bad path table usage with more than 65535 dirs #00012 (fs_image.c) -> support follow symlinks on imafe filesystem #00013 (fs_image.c) -> check for unsupported flags when reading a dir record #00014 (fs_image.c) -> more sanity checks to ensure dir record info is valid #00015 (fs_image.c) -> take care of CD-ROM XA discs when reading SP entry #00016 (fs_image.c) -> handle non RR ER entries #00017 (fs_image.c) -> take advantage of other atts of PVD #00018 (fs_image.c) -> check if there are more entries in the boot catalog #00019 (fs_image.c) -> set IsoImage attribs from Joliet SVD? #00020 (fs_image.c) -> handle RR info in Joliet tree #00021 (fs_image.c) -> handle RR info in ISO 9660:1999 tree #00022 (joliet.c) -> support relaxed constraints in joliet filenames #00024 (libisofs.h) -> option to convert names to lower case for iso reading #00025 (libisofs.h) -> support for merging old image files #00026 (libisofs.h) -> add support for "hidden" bootable images. #00027 (iso1999.h) -> Follow ISO 9660:1999 specs when sorting files FIXME ===== dnl Copyright (c) 2009 - 2019 Thomas Schmitt dnl Provided under the terms of the GNU General Public License version 2 or later. AC_DEFUN([LIBBURNIA_SET_FLAGS], [ case $target_os in freebsd* | netbsd*) LDFLAGS="$LDFLAGS -L/usr/local/lib" CPPFLAGS="$CPPFLAGS -I/usr/local/include" ;; esac ]) AC_DEFUN([TARGET_SHIZZLE], [ ARCH="" LIBBURNIA_PKGCONFDIR="$libdir"/pkgconfig AC_MSG_CHECKING([target operating system]) LIBBURNIA_SUPP_ACL=none LIBBURNIA_SUPP_FATTR=none LIBBURNIA_LDCONFIG_CMD="echo 'No ldconfig run performed. If needed, configure manually for:'" case $target in *-*-linux*) ARCH=linux LIBBURN_ARCH_LIBS= LIBBURNIA_SUPP_ACL=libacl LIBBURNIA_SUPP_FATTR=xattr LIBBURNIA_LDCONFIG_CMD=ldconfig ;; *-*-freebsd*) ARCH=freebsd LIBBURNIA_SUPP_ACL=libacl LIBBURNIA_SUPP_FATTR=extattr LIBBURN_ARCH_LIBS=-lcam # This may later be overridden by configure --enable-libdir-pkgconfig LIBBURNIA_PKGCONFDIR=$(echo "$libdir" | sed 's/\/lib$/\/libdata/')/pkgconfig ;; *) ARCH= LIBBURN_ARCH_LIBS= # AC_ERROR([You are attempting to compile for an unsupported platform]) ;; esac AC_MSG_RESULT([$ARCH]) ]) dnl LIBBURNIA_CHECK_ICONV is by Thomas Schmitt, libburnia project dnl It is based on gestures from: dnl iconv.m4 serial AM7 (gettext-0.18) dnl Copyright (C) 2000-2002, 2007-2009 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. dnl AC_DEFUN([LIBBURNIA_CHECK_ICONV], [ dnl Check whether it is allowed to link with -liconv AC_MSG_CHECKING([for iconv() in separate -liconv ]) libburnia_liconv="no" libburnia_save_LIBS="$LIBS" LIBS="$LIBS -liconv" AC_TRY_LINK([#include #include ], [iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);], [libburnia_liconv="yes"], [LIBS="$libburnia_save_LIBS"] ) AC_MSG_RESULT([$libburnia_liconv]) if test x"$libburnia_save_LIBS" = x"$LIBS" then dnl GNU iconv has no function iconv() but libiconv() and a macro iconv() dnl It is not tested whether this is detected by above macro. AC_CHECK_LIB(iconv, libiconv, , ) fi dnl Check for iconv(..., const char **inbuf, ...) AC_MSG_CHECKING([for const qualifier with iconv() ]) AC_TRY_COMPILE([ #include #include size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); ], [], [libburnia_iconv_const=""], [libburnia_iconv_const="const"] ) AC_DEFINE_UNQUOTED([ICONV_CONST], [$libburnia_iconv_const]) test -z "$libburnia_iconv_const" && libburnia_iconv_const="no" AC_MSG_RESULT([$libburnia_iconv_const]) ]) dnl LIBBURNIA_ASSERT_ICONV is by Thomas Schmitt, libburnia project dnl AC_DEFUN([LIBBURNIA_ASSERT_ICONV], [ if test x$LIBISOFS_ASSUME_ICONV = x then dnl Check for the essential gestures of libisofs/util.c AC_MSG_CHECKING([for iconv() to be accessible now ]) AC_TRY_LINK([ #include #include #include #include #include #include #include #include #include #include #include ], [iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd); ], [iconv_test="yes"], [iconv_test="no"] ) AC_MSG_RESULT([$iconv_test]) if test x$iconv_test = xno then echo >&2 echo "Cannot get function iconv() to work. Configuration aborted." >&2 echo "Check whether your system needs a separate libiconv installed." >&2 echo "If it is installed but not found, try something like" >&2 echo ' export LDFLAGS="$LDFLAGS -L/usr/local/lib"' >&2 echo ' export CPPFLAGS="$CPPFLAGS -I/usr/local/include"' >&2 echo ' export LIBS="$LIBS -liconv"' >&2 echo "You may override this test by exporting variable" >&2 echo " LIBISOFS_ASSUME_ICONV=yes" >&2 echo >&2 (exit 1); exit 1; fi fi ]) dnl LIBISOFS_ASSERT_VERS_LIBS is by Thomas Schmitt, libburnia project dnl It tests whether -Wl,--version-script=... works with the compiler AC_DEFUN([LIBISOFS_ASSERT_VERS_LIBS], [ libburnia_save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -Wl,--version-script=$srcdir/libisofs/libisofs.ver" AC_TRY_LINK([#include ], [printf("Hello\n");], [vers_libs_test="yes"], [vers_libs_test="no"]) if test x$vers_libs_test = xyes then LIBLDFLAGS="-Wl,--version-script=$srcdir/libisofs/libisofs.ver" fi LDFLAGS="$libburnia_save_LDFLAGS" AC_SUBST(LIBLDFLAGS) ]) dnl LIBBURNIA_SET_PKGCONFIG determines the install directory for the *.pc file. dnl Important: Must be performed _after_ TARGET_SHIZZLE dnl AC_DEFUN([LIBBURNIA_SET_PKGCONFIG], [ ### for testing --enable-libdir-pkgconfig on Linux ### LIBBURNIA_PKGCONFDIR="$libdir"data/pkgconfig if test "x$LIBBURNIA_PKGCONFDIR" = "x$libdir"/pkgconfig then dummy=dummy else AC_ARG_ENABLE(libdir-pkgconfig, [ --enable-libdir-pkgconfig Install to $libdir/pkgconfig on any OS, default=no], , enable_libdir_pkgconfig="no") AC_MSG_CHECKING([for --enable-libdir-pkgconfig]) if test "x$enable_libdir_pkgconfig" = xyes then LIBBURNIA_PKGCONFDIR="$libdir"/pkgconfig fi AC_MSG_RESULT([$enable_libdir_pkgconfig]) fi libburnia_pkgconfig_override="no" AC_ARG_ENABLE(pkgconfig-path, [ --enable-pkgconfig-path=DIR Absolute path of directory for libisofs-*.pc], libburnia_pkgconfig_override="yes" , enable_pkgconfig_path="none") AC_MSG_CHECKING([for overridden pkgconfig directory path]) if test "x$enable_pkgconfig_path" = xno then libburnia_pkgconfig_override="no" fi if test "x$enable_pkgconfig_path" = x -o "x$enable_pkgconfig_path" = xyes then libburnia_pkgconfig_override="invalid argument" fi if test "x$libburnia_pkgconfig_override" = xyes then LIBBURNIA_PKGCONFDIR="$enable_pkgconfig_path" AC_MSG_RESULT([$LIBBURNIA_PKGCONFDIR]) else AC_MSG_RESULT([$libburnia_pkgconfig_override]) fi AC_SUBST(LIBBURNIA_PKGCONFDIR) dnl For debugging only ### AC_MSG_RESULT([LIBBURNIA_PKGCONFDIR = $LIBBURNIA_PKGCONFDIR]) ]) dnl LIBBURNIA_TRY_TIMEZONE is by Thomas Schmitt, libburnia project dnl It tests whether the global variable exists and is suitable for dnl integer arithmetics. AC_DEFUN([LIBBURNIA_TRY_TIMEZONE], [ echo -n "checking for timezone variable ... " AC_TRY_LINK([ #include ], [long int i; i = 1 - timezone; ], [LIBBURNIA_TIMEZONE="timezone"], [LIBBURNIA_TIMEZONE="0"] ) echo "$LIBBURNIA_TIMEZONE" ]) #!/bin/sh -x aclocal -I . libtoolize --copy --force autoconf automake --foreign --add-missing --copy --include-deps AC_INIT([libisofs], [1.5.7], [http://libburnia-project.org]) AC_PREREQ([2.50]) dnl AC_CONFIG_HEADER([config.h]) AC_CANONICAL_HOST AC_CANONICAL_TARGET LIBBURNIA_SET_FLAGS AM_INIT_AUTOMAKE([subdir-objects]) AC_CONFIG_MACRO_DIR([./]) dnl dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match dnl dnl CURRENT and AGE describe the binary compatibility interval of a dnl dynamic library. dnl See also http://www.gnu.org/software/libtool/manual.html#Interfaces dnl dnl The name of the library will be libisofs.so.$CURRENT-$AGE.$AGE.$REV dnl In the terminology of this file: dnl CURRENT = LT_CURRENT dnl REV = LT_REVISION dnl AGE = LT_AGE dnl dnl LT_CURRENT, LT_REVISION and LT_AGE get set directly now. dnl dnl SONAME of the emerging library is LT_CURRENT - LT_AGE. dnl The linker will do no finer checks. If SONAME matches, then the couple dnl starts. dnl dnl Therefore a run time check is provided by libisofs function dnl iso_lib_version(). It returns the major, minor and micro revision of the dnl library. This means LIBISOFS_*_VERSION kept its second job which does not dnl comply to the usual ways of configure.ac . I.e. now *officially* this is dnl the source code release version as announced to the public. It has no dnl connection to SONAME or libtool version numbering. dnl It rather feeds the API function iso_lib_version(). dnl dnl If LIBISOFS_*_VERSION changes, be sure to change AC_INIT above to match. dnl LIBISOFS_MAJOR_VERSION=1 LIBISOFS_MINOR_VERSION=5 LIBISOFS_MICRO_VERSION=7 LIBISOFS_VERSION=$LIBISOFS_MAJOR_VERSION.$LIBISOFS_MINOR_VERSION.$LIBISOFS_MICRO_VERSION AC_SUBST(LIBISOFS_MAJOR_VERSION) AC_SUBST(LIBISOFS_MINOR_VERSION) AC_SUBST(LIBISOFS_MICRO_VERSION) AC_SUBST(LIBISOFS_VERSION) dnl Libtool versioning LT_RELEASE=$LIBISOFS_MAJOR_VERSION.$LIBISOFS_MINOR_VERSION dnl 2023.05.07 development jump has not yet happened dnl SONAME = 98 - 92 = 6 . Library name = libisofs.6.92.0 LT_CURRENT=98 LT_AGE=92 LT_REVISION=0 LT_CURRENT_MINUS_AGE=`expr $LT_CURRENT - $LT_AGE` AC_SUBST(LT_RELEASE) AC_SUBST(LT_CURRENT) AC_SUBST(LT_REVISION) AC_SUBST(LT_AGE) AC_SUBST(LT_CURRENT_MINUS_AGE) AC_PREFIX_DEFAULT([/usr/local]) test "$prefix" = "NONE" && prefix=$ac_default_prefix dnl ts B90405 : Disabled on advise of Ross Burton dnl AM_MAINTAINER_MODE AM_PROG_CC_C_O AC_C_CONST AC_C_INLINE dnl Large file support AC_SYS_LARGEFILE AC_FUNC_FSEEKO AC_CHECK_FUNC([fseeko]) if test ! $ac_cv_func_fseeko; then AC_MSG_ERROR([Libisofs requires largefile support.]) fi dnl If iconv(3) is in an extra lib, then it gets added to variable LIBS. dnl If not, then no -liconv will be added. LIBBURNIA_CHECK_ICONV dnl To abort configuration if iconv() still cannot be compiled LIBBURNIA_ASSERT_ICONV AC_PROG_LIBTOOL AC_SUBST(LIBTOOL_DEPS) # LIBTOOL="$LIBTOOL --silent" AC_PROG_INSTALL AC_CHECK_HEADERS() dnl Use GNU extensions if available AC_DEFINE(_GNU_SOURCE, 1) dnl Check for tm_gmtoff field in struct tm AC_CHECK_MEMBER([struct tm.tm_gmtoff], [AC_DEFINE(HAVE_TM_GMTOFF, 1, [Define this if tm structure includes a tm_gmtoff entry.])], , [#include ]) dnl Check if non standard timegm() function is available AC_CHECK_DECL([timegm], [AC_DEFINE(HAVE_TIMEGM, 1, [Define this if timegm function is available])], , [#include ]) dnl Whether timezone is an integer variable AH_TEMPLATE([Libburnia_timezonE], [Either timezone or 0]) LIBBURNIA_TRY_TIMEZONE if test x$LIBBURNIA_TIMEZONE = xtimezone then AC_DEFINE([Libburnia_timezonE], [timezone]) else AC_DEFINE([Libburnia_timezonE], [0]) fi dnl Check if non standard eaccess() function is available AC_CHECK_DECL([eaccess], [AC_DEFINE(HAVE_EACCESS, 1, [Define this if eaccess function is available])], , [#include ]) THREAD_LIBS=-lpthread AC_SUBST(THREAD_LIBS) TARGET_SHIZZLE AC_SUBST(ARCH) AC_SUBST(LIBBURN_ARCH_LIBS) dnl 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="-O3 $CFLAGS" CFLAGS="-fexpensive-optimizations $CFLAGS" fi CFLAGS="-DNDEBUG $CFLAGS" else if test x$GCC = xyes; then CFLAGS="-g -pedantic -Wall -Wextra -Wno-unused-parameter -Wno-char-subscripts $CFLAGS" fi CFLAGS="-DDEBUG $CFLAGS" fi dnl Verbose debug to make libisofs issue more debug messages AC_ARG_ENABLE(verbose-debug, [ --enable-verbose-debug Enable verbose debug messages, default=no], AC_DEFINE(LIBISOFS_VERBOSE_DEBUG, 1)) dnl Determine target directory for libisofs-*.pc dnl Important: Must be performed _after_ TARGET_SHIZZLE dnl LIBBURNIA_SET_PKGCONFIG dnl Add compiler-specific flags AC_ARG_ENABLE(libacl, [ --enable-libacl Enable use of ACL functions by libisofs, default=yes], , enable_libacl=yes) LIBACL_DEF= has_acl_h_but_no_func=0 if test x$LIBBURNIA_SUPP_ACL = xlibacl then if test x$enable_libacl = xyes; then dnl Check whether there is libacl-devel and libacl-runtime. dnl If not, erase this macro which would enable use of acl_to_text and others LIBACL_DEF="-DLibisofs_with_aaip_acL" dnl The empty yes case obviously causes -lacl to be linked AC_CHECK_HEADER(sys/acl.h, AC_CHECK_LIB(acl, acl_to_text, , has_acl_h_but_no_libacl=1 ), LIBACL_DEF= ) if test "$has_acl_h_but_no_libacl" = 1 then AC_CHECK_LIB(c, acl_to_text, X= , LIBACL_DEF= ) fi fi fi if test x$LIBACL_DEF = x-DLibisofs_with_aaip_acL then if test x$has_acl_h_but_no_libacl = x1 then echo "enabled local processing of ACL" else echo "enabled libacl, local processing of ACL" fi else echo "disabled local processing of ACL" fi AC_SUBST(LIBACL_DEF) dnl ts A90123 - B80508 AC_ARG_ENABLE(xattr, [ --enable-xattr Enable use of extended file attributes by libisofs, default=yes], , enable_xattr=yes) AC_ARG_ENABLE(xattr-h-pref-attr, [ --enable-xattr-h-pref-attr Prefer include file attr/xattr.h over sys/xattr.h, default=no], , enable_xattr_h_pref_attr=no) XATTR_DEF= XATTR_ADDON_DEF= if test x"$LIBBURNIA_SUPP_FATTR" = xxattr then if test "x$enable_xattr" = xyes; then dnl Check whether there is the header for Linux xattr. dnl If not, erase this macro which would enable use of listxattr and others XATTR_A_DEF= XATTR_S_DEF= if test x"$enable_xattr_h_pref_attr" = xyes then echo "prefering include file attr/xattr.h over sys/attr.h" XATTR_A_DEF=1 AC_CHECK_HEADER(attr/xattr.h, AC_CHECK_LIB(c, listxattr, X= , XATTR_A_DEF= ), XATTR_A_DEF= ) if test x"$XATTR_A_DEF" = x1 then XATTR_DEF="-DLibisofs_with_aaip_xattR" else XATTR_S_DEF=1 AC_CHECK_HEADER(sys/xattr.h, AC_CHECK_LIB(c, listxattr, X= , XATTR_S_DEF= ), XATTR_S_DEF= ) if test x"$XATTR_S_DEF" = x1 then XATTR_DEF="-DLibisofs_with_aaip_xattR" XATTR_ADDON_DEF="-DLibisofs_with_sys_xattR" fi fi else XATTR_S_DEF=1 AC_CHECK_HEADER(sys/xattr.h, AC_CHECK_LIB(c, listxattr, X= , XATTR_S_DEF= ), XATTR_S_DEF= ) if test x"$XATTR_S_DEF" = x1 then XATTR_DEF="-DLibisofs_with_aaip_xattR" XATTR_ADDON_DEF="-DLibisofs_with_sys_xattR" else XATTR_A_DEF=1 AC_CHECK_HEADER(attr/xattr.h, AC_CHECK_LIB(c, listxattr, X= , XATTR_A_DEF= ), XATTR_A_DEF= ) if test x"$XATTR_A_DEF" = x1 then XATTR_DEF="-DLibisofs_with_aaip_xattR" fi fi fi if test x"$XATTR_S_DEF" = x1 then echo "decided to include file sys/attr.h" elif test x"$XATTR_A_DEF" = x1 then echo "decided to include file attr/xattr.h" fi fi elif test x"$LIBBURNIA_SUPP_FATTR" = xextattr then if test "x$enable_xattr" = xyes; then XATTR_DEF="-DLibisofs_with_freebsd_extattR" AC_CHECK_HEADER(sys/extattr.h, AC_CHECK_LIB(c, extattr_list_file, X=, XATTR_DEF= ), XATTR_DEF= ) fi fi if test x$XATTR_DEF = x-DLibisofs_with_aaip_xattR then echo "enabled xattr, local processing of extended file attributes Linux style" elif test x$XATTR_DEF = x-DLibisofs_with_freebsd_extattR then echo "enabled extattr, local processing of extended file attributes FreeBSD style" else echo "disabled local processing of extended file attributes" fi XATTR_DEF="$XATTR_DEF $XATTR_ADDON_DEF" AC_SUBST(XATTR_DEF) dnl ts C40713 LFA_DEF= AC_ARG_ENABLE(lfa-flags, [ --enable-lfa-flags Enable processing of Linux chattr(1) flags, default=yes], , enable_lfa_flags=yes) if test x"$enable_lfa_flags" = xyes; then AC_CHECK_HEADER(linux/fs.h, LFA_DEF="-DLibisofs_with_aaip_lfa_flagS", LFA_DEF=) fi if test x"$LFA_DEF" = x; then echo "disabled Linux chattr(1) flags" else echo "enabled Linux chattr(1) flags" fi AC_SUBST(LFA_DEF) dnl ts A90409 AC_ARG_ENABLE(zlib, [ --enable-zlib Enable use of zlib by libisofs, default=yes], , enable_zlib=yes) if test "x$enable_zlib" = xyes; then dnl Check whether there is the header for zlib. dnl If not, erase this macro which would enable use of compress2() and others. dnl Linking fails on SuSE 9.0 because zlib has compress2() but lacks dnl compressBound(). So compressBound is the more modern thing to test. dnl The empty parameter after "compressBound" causes -lz. ZLIB_DEF="-DLibisofs_with_zliB" AC_CHECK_HEADER(zlib.h, AC_CHECK_LIB(z, compressBound, , ZLIB_DEF= ), ZLIB_DEF= ) else ZLIB_DEF= fi AC_SUBST(ZLIB_DEF) dnl ts B00927 AC_ARG_ENABLE(libjte, [ --enable-libjte Enable use of libjte >= 2.0 by libisofs, default=yes], , enable_libjte=yes) if test "x$enable_libjte" = xyes; then LIBJTE_DEF="-DLibisofs_with_libjtE" AC_CHECK_HEADER(libjte/libjte.h, AC_CHECK_LIB(jte, libjte_set_checksum_algorithm, , LIBJTE_DEF= ), LIBJTE_DEF= ) else LIBJTE_DEF= fi AC_SUBST(LIBJTE_DEF) # Library versioning normally serves a complex purpose. # Since libisofs obeys strict ABI backward compatibility, it needs only the # simple feature to declare function names "global:" or "local:". Only the # global ones are visible to applications at library load time. AC_ARG_ENABLE(versioned-libs, [ --enable-versioned-libs Enable strict symbol encapsulation , default=yes], , enable_versioned_libs=yes) if test x$enable_versioned_libs = xyes; then vers_libs_test=no LIBISOFS_ASSERT_VERS_LIBS if test x$vers_libs_test = xno then echo "disabled strict symbol encapsulation (test failed)" else echo "enabled strict symbol encapsulation" fi else echo "disabled strict symbol encapsulation" fi AC_ARG_ENABLE(ldconfig-at-install, [ --enable-ldconfig-at-install On GNU/Linux run ldconfig, default=yes], , ldconfig_at_install=yes) if test x$ldconfig_at_install = xyes; then dummy=dummy else LIBBURNIA_LDCONFIG_CMD="echo 'NOTE: ldconfig is disabled. If needed, configure manually for:'" echo "disabled run of ldconfig during installation on GNU/Linux" fi AC_SUBST(LIBBURNIA_LDCONFIG_CMD) AC_CONFIG_FILES([ Makefile doc/doxygen.conf version.h libisofs-1.pc ]) AC_OUTPUT /* * Copyright (c) 2007 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #include "libisofs.h" #include "fsource.h" #include #include /* * Little test program to test filesystem implementations. * Outputs file contents to stdout! */ int main(int argc, char **argv) { int res; IsoFilesystem *fs; IsoFileSource *file; struct stat info; if (argc != 2) { fprintf(stderr, "Usage: cat /path/to/file\n"); return 1; } /* create filesystem object */ res = iso_local_filesystem_new(&fs); if (res < 0) { fprintf(stderr, "Can't get local fs object, err = %d\n", res); return 1; } res = fs->get_by_path(fs, argv[1], &file); if (res < 0) { fprintf(stderr, "Can't get file, err = %d\n", res); return 1; } res = iso_file_source_lstat(file, &info); if (res < 0) { fprintf(stderr, "Can't stat file, err = %d\n", res); return 1; } if (S_ISDIR(info.st_mode)) { fprintf(stderr, "Path refers to a directory!!\n"); return 1; } else { char buf[1024]; res = iso_file_source_open(file); if (res < 0) { fprintf(stderr, "Can't open file, err = %d\n", res); return 1; } while ((res = iso_file_source_read(file, buf, 1024)) > 0) { fwrite(buf, 1, res, stdout); } if (res < 0) { fprintf(stderr, "Error reading, err = %d\n", res); return 1; } iso_file_source_close(file); } iso_file_source_unref(file); iso_filesystem_unref(fs); return 0; } /* * Copyright (c) 2007 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #include "libisofs.h" #include "buffer.h" #include #include #include #include #include #include #include /* * Little test program that reads a file and outputs it to stdout, using * the libisofs ring buffer as intermediate memory */ struct th_data { IsoRingBuffer *rbuf; char *path; }; #define WRITE_CHUNK 2048 #define READ_CHUNK 2048 static void *write_function(void *arg) { ssize_t bytes; int res; unsigned char tmp[WRITE_CHUNK]; struct th_data *data = (struct th_data *) arg; int fd = open(data->path, O_RDONLY); if (fd < 0) { fprintf(stderr, "Writer thread error: Can't open file"); iso_ring_buffer_writer_close(data->rbuf, 1); pthread_exit(NULL); } res = 1; while ( (bytes = read(fd, tmp, WRITE_CHUNK)) > 0) { res = iso_ring_buffer_write(data->rbuf, tmp, bytes); if (res <= 0) { break; } /* To test premature reader exit >>>>>>>>>>> iso_ring_buffer_writer_close(data->rbuf); pthread_exit(NULL); <<<<<<<<<<<<<<<<<<<<<<<<< */ // if (rand() > 2000000000) { // fprintf(stderr, "Writer sleeping\n"); // sleep(1); // } } fprintf(stderr, "Writer finish: %d\n", res); close(fd); iso_ring_buffer_writer_close(data->rbuf, 0); pthread_exit(NULL); } static void *read_function(void *arg) { unsigned char tmp[READ_CHUNK]; int res = 1; struct th_data *data = (struct th_data *) arg; while ( (res = iso_ring_buffer_read(data->rbuf, tmp, READ_CHUNK)) > 0) { write(1, tmp, READ_CHUNK); /* To test premature reader exit >>>>>>>>>>> iso_ring_buffer_reader_close(data->rbuf); pthread_exit(NULL); <<<<<<<<<<<<<<<<<<<<<<<<< */ // if (rand() > 2000000000) { // fprintf(stderr, "Reader sleeping\n"); // sleep(1); // } } fprintf(stderr, "Reader finish: %d\n", res); iso_ring_buffer_reader_close(data->rbuf, 0); pthread_exit(NULL); } int main(int argc, char **argv) { int res; struct th_data data; pthread_t reader; pthread_t writer; if (argc != 2) { fprintf(stderr, "Usage: catbuffer /path/to/file\n"); return 1; } res = iso_ring_buffer_new(1024, &data.rbuf); if (res < 0) { fprintf(stderr, "Can't create buffer\n"); return 1; } data.path = argv[1]; res = pthread_create(&writer, NULL, write_function, (void *) &data); res = pthread_create(&reader, NULL, read_function, (void *) &data); pthread_join(writer, NULL); pthread_join(reader, NULL); fprintf(stderr, "Buffer was %d times full and %d times empty.\n", iso_ring_buffer_get_times_full(data.rbuf), iso_ring_buffer_get_times_empty(data.rbuf)); free(data.rbuf); return 0; } /* * Copyright (c) 2007 - 2016 Vreixo Formoso, Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ static char helptext[][80] = { "", "This is a collection of libisofs gestures which formerly were distinct", "programs. The first argument chooses the gesture:", " -tree absolute_directory_path", " Import a directory and print the resulting iso tree.", " -find absolute_directory_path", " Import a directory, find matching nodes and print the", " resulting iso tree.", " -iso [options] directory output_file", " Create an iso image from a local directory. For options see", " output of -iso -h", " -iso_read image_file", " Output the contents of an iso image.", " -iso_cat image_file path_in_image", " Extract a file from a given ISO image and put out its content", " to stdout. The file is addressed by path_in_image. The ISO", " image does not get loaded but rather the lookups are done", " directly in the image file.", " -iso_modify image_file absolute_directory_path output_file", " Load an iso image, add a directory, and write complete image.", " -iso_ms image_lba nwa image_file directory_path output_file", " Load an iso image, add a directory, and write as add-on", " session which shall be appended to the old image.", " image_lba gives the block address of the start of the most", " recent session in the image_file. nwa gives the block address", " where the add-on session will be appended to the image.", "@" }; #define LIBISOFS_WITHOUT_LIBBURN yes #include "libisofs.h" #include #include #include #include #include #include #include #include #include #include #include #ifndef PATH_MAX #define PATH_MAX Libisofs_default_path_maX #endif /* ----------------------------- utilities -------------------------- */ void demo_report_iso_err(int err, char *occasion) { char *severity; fprintf(stderr, "%s : err = 0x%X", occasion, (unsigned int) err); if (err < 0) { iso_sev_to_text(iso_error_get_severity(err), &severity); fprintf(stderr, " -> %s '%s'", severity, iso_error_to_msg(err)); } fprintf(stderr, "\n"); } /* ------------------------- from demo/tree.c ----------------------- */ static void print_permissions(mode_t mode) { char perm[10]; /* TODO suid, sticky... */ perm[9] = '\0'; perm[8] = mode & S_IXOTH ? 'x' : '-'; perm[7] = mode & S_IWOTH ? 'w' : '-'; perm[6] = mode & S_IROTH ? 'r' : '-'; perm[5] = mode & S_IXGRP ? 'x' : '-'; perm[4] = mode & S_IWGRP ? 'w' : '-'; perm[3] = mode & S_IRGRP ? 'r' : '-'; perm[2] = mode & S_IXUSR ? 'x' : '-'; perm[1] = mode & S_IWUSR ? 'w' : '-'; perm[0] = mode & S_IRUSR ? 'r' : '-'; printf("[%s]",perm); } static void tree_print_dir(IsoDir *dir, int level) { int i; IsoDirIter *iter; IsoNode *node; char *sp; sp = calloc(1, level * 2 + 1); for (i = 0; i < level * 2; i += 2) { sp[i] = '|'; sp[i+1] = ' '; } if (level > 0) sp[level * 2 - 1] = '-'; sp[level * 2] = '\0'; iso_dir_get_children(dir, &iter); while (iso_dir_iter_next(iter, &node) == 1) { if (ISO_NODE_IS_DIR(node)) { printf("%s+[D] ", sp); print_permissions(iso_node_get_permissions(node)); printf(" %s\n", iso_node_get_name(node)); tree_print_dir(ISO_DIR(node), level+1); } else if (ISO_NODE_IS_FILE(node)) { printf("%s-[F] ", sp); print_permissions(iso_node_get_permissions(node)); printf(" %s\n", iso_node_get_name(node) ); } else if (ISO_NODE_IS_SYMLINK(node)) { printf("%s-[L] ", sp); print_permissions(iso_node_get_permissions(node)); printf(" %s -> %s \n", iso_node_get_name(node), iso_symlink_get_dest(ISO_SYMLINK(node)) ); } else { printf("%s-[C] ", sp); print_permissions(iso_node_get_permissions(node)); printf(" %s\n", iso_node_get_name(node) ); } } iso_dir_iter_free(iter); free(sp); } int gesture_tree(int argc, char **argv) { int result; IsoImage *image; if (argc != 2) { need_abs_path:; fprintf (stderr, "You need to specify a valid absolute path\n"); return 1; } if (argv[1][0] != '/') goto need_abs_path; iso_init(); iso_set_msgs_severities("NEVER", "ALL", ""); result = iso_image_new("volume_id", &image); if (result < 0) { printf ("Error creating image\n"); return 1; } result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[1]); if (result < 0) { printf ("Error adding directory %d\n", result); return 1; } printf("================= IMAGE =================\n"); tree_print_dir(iso_image_get_root(image), 0); printf("\n\n"); iso_image_unref(image); iso_finish(); return 0; } /* ------------------------- from demo/find.c ----------------------- */ static void find_print_dir(IsoDir *dir) { IsoDirIter *iter; IsoNode *node; IsoFindCondition *cond, *c1, *c2; c1 = iso_new_find_conditions_name("*a*"); c2 = iso_new_find_conditions_mode(S_IFREG); cond = iso_new_find_conditions_and(c1, c2); iso_dir_find_children(dir, cond, &iter); while (iso_dir_iter_next(iter, &node) == 1) { char *path = iso_tree_get_node_path(node); printf(" %s\n", path); free(path); } iso_dir_iter_free(iter); } int gesture_find(int argc, char **argv) { int result; IsoImage *image; if (argc != 2) { need_abs_path:; fprintf (stderr, "You need to specify a valid absolute path\n"); return 1; } if (argv[1][0] != '/') goto need_abs_path; iso_init(); iso_set_msgs_severities("NEVER", "ALL", ""); result = iso_image_new("volume_id", &image); if (result < 0) { printf ("Error creating image\n"); return 1; } result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[1]); if (result < 0) { printf ("Error adding directory %d\n", result); return 1; } find_print_dir(iso_image_get_root(image)); iso_image_unref(image); iso_finish(); return 0; } /* ------------------------- from demo/iso.c ----------------------- */ static const char * const optstring = "JRIL:b:hV:"; extern char *optarg; extern int optind; void iso_usage(char **argv) { printf("%s [OPTIONS] DIRECTORY OUTPUT\n", argv[0]); } void iso_help() { printf( "Options:\n" " -J Add Joliet support\n" " -R Add Rock Ridge support\n" " -I Add ISO 9660:1999 support\n" " -V label Volume Label\n" " -L Set the ISO level (1 or 2)\n" " -b file Specifies a boot image to add to image\n" " -h Print this message\n" ); } int iso_callback(IsoFileSource *src) { char *path = iso_file_source_get_path(src); printf("CALLBACK: %s\n", path); free(path); return 1; } int gesture_iso(int argc, char **argv) { int result; int c; IsoImage *image; struct burn_source *burn_src; unsigned char buf[2048]; FILE *fp = NULL; IsoWriteOpts *opts; char *volid = "VOLID"; char *boot_img = NULL; int rr = 0, j = 0, iso1999 = 0, level = 1; while ((c = getopt(argc, argv, optstring)) != -1) { switch(c) { case 'h': iso_usage(argv); iso_help(); goto ex; break; case 'J': j = 1; break; case 'R': rr = 1; break; case 'I': iso1999 = 1; break; case 'L': level = atoi(optarg); break; case 'b': boot_img = optarg; break; case 'V': volid = optarg; break; case '?': iso_usage(argv); goto ex; break; } } if (argc < 2) { printf ("Please pass directory from which to build ISO\n"); iso_usage(argv); goto ex; } if (argc < 3) { printf ("Please supply output file\n"); iso_usage(argv); goto ex; } fp = fopen(argv[optind+1], "w"); if (fp == NULL) { err(1, "error opening output file"); goto ex; } result = iso_init(); if (result < 0) { printf ("Can't initialize libisofs\n"); goto ex; } iso_set_msgs_severities("NEVER", "ALL", ""); result = iso_image_new(volid, &image); if (result < 0) { printf ("Error creating image\n"); goto ex; } iso_tree_set_follow_symlinks(image, 0); iso_tree_set_ignore_hidden(image, 0); iso_tree_set_ignore_special(image, 0); iso_set_abort_severity("SORRY"); /*iso_tree_set_report_callback(image, callback);*/ result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[optind]); if (result < 0) { printf ("Error adding directory %d\n", result); goto ex; } if (boot_img) { /* adds El-Torito boot info. Tunned for isolinux */ ElToritoBootImage *bootimg; result = iso_image_set_boot_image(image, boot_img, ELTORITO_NO_EMUL, "/isolinux/boot.cat", &bootimg); if (result < 0) { printf ("Error adding boot image %d\n", result); goto ex; } el_torito_set_load_size(bootimg, 4); el_torito_patch_isolinux_image(bootimg); } result = iso_write_opts_new(&opts, 0); if (result < 0) { printf ("Cannot create write opts, error %d\n", result); goto ex; } iso_write_opts_set_iso_level(opts, level); iso_write_opts_set_rockridge(opts, rr); iso_write_opts_set_joliet(opts, j); iso_write_opts_set_iso1999(opts, iso1999); result = iso_image_create_burn_source(image, opts, &burn_src); if (result < 0) { printf ("Cannot create image, error %d\n", result); goto ex; } iso_write_opts_free(opts); while (burn_src->read_xt(burn_src, buf, 2048) == 2048) { result = fwrite(buf, 1, 2048, fp); if (result < 2048) { printf ("Cannot write block. errno= %d\n", errno); goto ex; } } fclose(fp); burn_src->free_data(burn_src); free(burn_src); iso_image_unref(image); iso_finish(); return 0; ex:; if (fp != NULL) fclose(fp); return 1; } /* ------------------------- from demo/iso_read.c ----------------------- */ static void iso_read_print_type(mode_t mode) { switch(mode & S_IFMT) { case S_IFSOCK: printf("[S] "); break; case S_IFLNK: printf("[L] "); break; case S_IFREG: printf("[R] "); break; case S_IFBLK: printf("[B] "); break; case S_IFDIR: printf("[D] "); break; case S_IFIFO: printf("[F] "); break; } } static void iso_read_print_file_src(IsoFileSource *file) { struct stat info; char *name; iso_file_source_lstat(file, &info); iso_read_print_type(info.st_mode); print_permissions(info.st_mode); printf(" %10.f ", (double) info.st_size); /* printf(" {%ld,%ld} ", (long)info.st_dev, (long)info.st_ino); */ name = iso_file_source_get_name(file); printf(" %s", name); free(name); if (S_ISLNK(info.st_mode)) { char buf[PATH_MAX]; iso_file_source_readlink(file, buf, PATH_MAX); printf(" -> %s\n", buf); } printf("\n"); } static void iso_read_print_dir(IsoFileSource *dir, int level) { int ret, i; IsoFileSource *file; struct stat info; char *sp; sp = calloc(1, level * 2 + 1); for (i = 0; i < level * 2; i += 2) { sp[i] = '|'; sp[i+1] = ' '; } if (level > 0) sp[level * 2 - 1] = '-'; sp[level * 2] = '\0'; ret = iso_file_source_open(dir); if (ret < 0) { printf ("Can't open dir %d\n", ret); } while ((ret = iso_file_source_readdir(dir, &file)) == 1) { printf("%s", sp); iso_read_print_file_src(file); ret = iso_file_source_lstat(file, &info); if (ret < 0) { break; } if (S_ISDIR(info.st_mode)) { iso_read_print_dir(file, level + 1); } iso_file_source_unref(file); } iso_file_source_close(dir); if (ret < 0) { printf ("Can't print dir\n"); } free(sp); } int gesture_iso_read(int argc, char **argv) { int result, initialized = 0, return_val = 1; IsoImageFilesystem *fs = NULL; IsoDataSource *src = NULL; IsoFileSource *root = NULL; IsoReadOpts *ropts = NULL; if (argc != 2) { printf ("You need to specify a valid path\n"); goto ex; } result = iso_init(); if (result < 0) { demo_report_iso_err(result, "Cannot init libisofs"); goto ex; } initialized = 1; iso_set_msgs_severities("NEVER", "ALL", ""); result = iso_data_source_new_from_file(argv[1], &src); if (result < 0) { demo_report_iso_err(result, "Error creating data source"); goto ex; } result = iso_read_opts_new(&ropts, 0); if (result < 0) { demo_report_iso_err(result, "Error creating read options"); goto ex; } result = iso_image_filesystem_new(src, ropts, 1, &fs); if (result < 0) { demo_report_iso_err(result, "Error creating filesystem"); goto ex; } iso_read_opts_free(ropts); ropts = NULL; printf("\nVOLUME INFORMATION\n"); printf("==================\n\n"); printf("Vol. id: %s\n", iso_image_fs_get_volume_id(fs)); printf("Publisher: %s\n", iso_image_fs_get_publisher_id(fs)); printf("Data preparer: %s\n", iso_image_fs_get_data_preparer_id(fs)); printf("System: %s\n", iso_image_fs_get_system_id(fs)); printf("Application: %s\n", iso_image_fs_get_application_id(fs)); printf("Copyright: %s\n", iso_image_fs_get_copyright_file_id(fs)); printf("Abstract: %s\n", iso_image_fs_get_abstract_file_id(fs)); printf("Biblio: %s\n", iso_image_fs_get_biblio_file_id(fs)); printf("\nDIRECTORY TREE\n"); printf("==============\n"); result = fs->get_root(fs, &root); if (result < 0) { demo_report_iso_err(result, "Cannot get root object"); goto ex; } /* iso_read_print_file_src(root); */ iso_read_print_dir(root, 0); return_val = 0; ex:; if (root != NULL) iso_file_source_unref(root); if (ropts != NULL) iso_read_opts_free(ropts); if (fs != NULL) { fs->close(fs); iso_filesystem_unref((IsoFilesystem*)fs); } if (src != NULL) iso_data_source_unref(src); if (initialized) iso_finish(); return return_val; } /* ------------------------- from demo/iso_cat.c ----------------------- */ int gesture_iso_cat(int argc, char **argv) { int res, write_ret, ret; IsoFilesystem *fs = NULL; IsoFileSource *file = NULL; struct stat info; IsoDataSource *src = NULL; IsoReadOpts *opts = NULL; if (argc != 3) { fprintf(stderr, "Usage: -iso_cat /path/to/image /path/to/file\n"); return 1; } res = iso_init(); if (res < 0) { demo_report_iso_err(res, "Cannot init libisofs"); return 1; } /* Important Note: From here on memory objects get created which need to be freed in the end. Therefore in case of problems no direct return, but rather a hop to label "ex:", where cleanup happens. */ res = iso_data_source_new_from_file(argv[1], &src); if (res < 0) { demo_report_iso_err(res, "Error creating data source object"); ret = 1; goto ex; } res = iso_read_opts_new(&opts, 0); if (res < 0) { demo_report_iso_err(res, "Error creating read options object"); ret = 1; goto ex; } res = iso_image_filesystem_new(src, opts, 1, &fs); if (res < 0) { demo_report_iso_err(res, "Error creating filesystem object"); ret = 1; goto ex; } iso_read_opts_free(opts); opts = NULL; res = fs->get_by_path(fs, argv[2], &file); if (res < 0) { demo_report_iso_err(res, "Cannot get file object with given path"); ret = 1; goto ex; } res = iso_file_source_lstat(file, &info); if (res < 0) { demo_report_iso_err(res, "Cannot inquire type of file object with given path"); ret = 1; goto ex; } if (S_ISDIR(info.st_mode)) { fprintf(stderr, "Path refers to a directory!!\n"); ret = 1; goto ex; } else { char buf[1024]; res = iso_file_source_open(file); if (res < 0) { demo_report_iso_err(res, "Cannot open file object with given path"); ret = 1; goto ex; } while ((res = iso_file_source_read(file, buf, 1024)) > 0) { write_ret = fwrite(buf, 1, res, stdout); if (write_ret < res) { printf ("Cannot write block to stdout. errno= %d\n", errno); iso_file_source_close(file); ret = 1; goto ex; } } iso_file_source_close(file); if (res < 0) { demo_report_iso_err(res, "Error while reading data content"); fprintf(stderr, "Error reading, err = 0x%X\n", (unsigned int) res); ret = 1; goto ex; } } ret = 0; ex:; if (file != NULL) iso_file_source_unref(file); if (fs != NULL) iso_filesystem_unref(fs); if (opts != NULL) iso_read_opts_free(opts); if (src != NULL) iso_data_source_unref(src); iso_finish(); return ret; } /* ------------------------- from demo/iso_modify.c ----------------------- */ void iso_modify_usage(char **argv) { printf("%s IMAGE DIRECTORY OUTPUT\n", argv[0]); } int gesture_iso_modify(int argc, char **argv) { int result, return_val = 1, initialized = 0; IsoImage *image = NULL; IsoDataSource *src = NULL; struct burn_source *burn_src = NULL; unsigned char buf[2048]; FILE *fp = NULL; IsoWriteOpts *opts = NULL; IsoReadOpts *ropts = NULL; if (argc < 4) { iso_modify_usage(argv); goto ex; } fp = fopen(argv[3], "w"); if (fp == NULL) { err(1, "error opening output file"); goto ex; } result = iso_init(); if (result < 0) { demo_report_iso_err(result, "Cannot init libisofs"); goto ex; } initialized = 1; iso_set_msgs_severities("NEVER", "ALL", ""); /* create the data source to accesss previous image */ result = iso_data_source_new_from_file(argv[1], &src); if (result < 0) { demo_report_iso_err(result, "Error creating data source"); goto ex; } /* create the image context */ result = iso_image_new("volume_id", &image); if (result < 0) { demo_report_iso_err(result, "Error creating image"); goto ex; } iso_tree_set_follow_symlinks(image, 0); iso_tree_set_ignore_hidden(image, 0); /* import previous image */ result = iso_read_opts_new(&ropts, 0); if (result < 0) { demo_report_iso_err(result, "Error creating read options"); goto ex; } result = iso_image_import(image, src, ropts, NULL); if (result < 0) { demo_report_iso_err(result, "Error importing previous session"); goto ex; } /* (One could of course keep them alive until cleanup) */ iso_read_opts_free(ropts); ropts = NULL; iso_data_source_unref(src); src = NULL; /* add new dir */ result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[2]); if (result < 0) { demo_report_iso_err(result, "Error adding directory"); goto ex; } /* Generate a new image with both previous and added contents. Profile 1 means Rock Ridge and ISO level 3. */ result = iso_write_opts_new(&opts, 1); if (result < 0) { demo_report_iso_err(result, "Cannot create write opts"); goto ex; } /* Prefer specs violation over relocation deep directories */ iso_write_opts_set_allow_deep_paths(opts, 1); /* For MS-Windows readers : iso_write_opts_set_joliet(opts, 1); */ result = iso_image_create_burn_source(image, opts, &burn_src); if (result < 0) { demo_report_iso_err(result, "Cannot create image object"); goto ex; } iso_write_opts_free(opts); opts = NULL; while (burn_src->read_xt(burn_src, buf, 2048) == 2048) { result = fwrite(buf, 1, 2048, fp); if (result < 2048) { fprintf (stderr, "Cannot write block. errno= %d\n", errno); goto ex; } } return_val = 0; ex: if (fp != NULL) fclose(fp); if (opts != NULL) iso_write_opts_free(opts); if (burn_src != NULL) { burn_src->free_data(burn_src); free(burn_src); } if (image != NULL) iso_image_unref(image); if (ropts != NULL) iso_read_opts_free(ropts); if (src != NULL) iso_data_source_unref(src); if (initialized) iso_finish(); return return_val; } /* ------------------------- from demo/iso_ms.c ----------------------- */ void iso_ms_usage(char **argv) { printf("%s LSS NWA DISC DIRECTORY OUTPUT\n", argv[0]); } int gesture_iso_ms(int argc, char **argv) { int result, return_val = 1, initialized = 0; IsoImage *image = NULL; IsoDataSource *src = NULL; struct burn_source *burn_src = NULL; unsigned char buf[2048]; FILE *fp = NULL; IsoWriteOpts *opts = NULL; IsoReadOpts *ropts = NULL; uint32_t ms_block; if (argc < 6) { iso_ms_usage(argv); goto ex; } if (strcmp(argv[3], argv[5]) == 0) { fprintf(stderr, "image_file and output_file must not be the same file.\n"); goto ex; } fp = fopen(argv[5], "w"); if (!fp) { err(1, "error opening output file"); goto ex; } result = iso_init(); if (result < 0) { demo_report_iso_err(result, "Cannot init libisofs"); goto ex; } initialized = 1; iso_set_msgs_severities("NEVER", "ALL", ""); /* create the data source to accesss previous image */ result = iso_data_source_new_from_file(argv[3], &src); if (result < 0) { demo_report_iso_err(result, "Error creating data source"); goto ex; } /* create the image context */ result = iso_image_new("volume_id", &image); if (result < 0) { demo_report_iso_err(result, "Error creating image"); goto ex; } iso_tree_set_follow_symlinks(image, 0); iso_tree_set_ignore_hidden(image, 0); /* import previous image */ result = iso_read_opts_new(&ropts, 0); if (result < 0) { fprintf(stderr, "Error creating read options\n"); goto ex; } iso_read_opts_set_start_block(ropts, atoi(argv[1])); result = iso_image_import(image, src, ropts, NULL); iso_read_opts_free(ropts); ropts = NULL; iso_data_source_unref(src); src = NULL; if (result < 0) { demo_report_iso_err(result, "Error importing previous session"); goto ex; } /* add new dir */ result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[4]); if (result < 0) { demo_report_iso_err(result, "Error adding directory"); goto ex; } /* generate a multisession image with new contents */ result = iso_write_opts_new(&opts, 1); if (result < 0) { demo_report_iso_err(result, "Cannot create write opts"); goto ex; } /* round up to 32kb aligment = 16 block */ ms_block = atoi(argv[2]); iso_write_opts_set_ms_block(opts, ms_block); iso_write_opts_set_appendable(opts, 1); result = iso_image_create_burn_source(image, opts, &burn_src); if (result < 0) { printf ("Cannot create image, error %d\n", result); goto ex; } iso_write_opts_free(opts); opts = NULL; while (burn_src->read_xt(burn_src, buf, 2048) == 2048) { result = fwrite(buf, 1, 2048, fp); if (result < 2048) { printf ("Cannot write block. errno= %d\n", errno); goto ex; } } return_val = 0; ex:; if (burn_src != NULL) { burn_src->free_data(burn_src); free(burn_src); } if (opts != NULL) iso_write_opts_free(opts); if (image) iso_image_unref(image); if (ropts != NULL) iso_read_opts_free(ropts); if (src != NULL) iso_data_source_unref(src); if (initialized) iso_finish(); if (fp != NULL) fclose(fp); return return_val; } /* ------------------------- switcher ----------------------- */ int main(int argc, char **argv) { char *gesture; int i; if (argc < 2) { usage:; fprintf(stderr, "usage: %s gesture [gesture_options]\n", argv[0]); for (i = 0; helptext[i][0] != '@'; i++) fprintf(stderr, "%s\n", helptext[i]); exit(1); } for (gesture = argv[1]; *gesture == '-'; gesture++); if (strcmp(gesture, "tree") == 0) { gesture_tree(argc - 1, &(argv[1])); } else if(strcmp(gesture, "find") == 0) { gesture_find(argc - 1, &(argv[1])); } else if(strcmp(gesture, "iso") == 0) { gesture_iso(argc - 1, &(argv[1])); } else if(strcmp(gesture, "iso_read") == 0) { gesture_iso_read(argc - 1, &(argv[1])); } else if(strcmp(gesture, "iso_cat") == 0) { gesture_iso_cat(argc - 1, &(argv[1])); } else if(strcmp(gesture, "iso_modify") == 0) { gesture_iso_modify(argc - 1, &(argv[1])); } else if(strcmp(gesture, "iso_ms") == 0) { gesture_iso_ms(argc - 1, &(argv[1])); } else { goto usage; } exit(0); } /* * Little program that imports a directory to iso image, generates the * ecma119 low level tree and prints it. * Note that this is not an API example, but a little program for test * purposes. */ #include "libisofs.h" #include "ecma119.h" #include "ecma119_tree.h" #include "util.h" #include "filesrc.h" #include "node.h" #include #include #include #include #include #include static void print_permissions(mode_t mode) { char perm[10]; //TODO suid, sticky... perm[9] = '\0'; perm[8] = mode & S_IXOTH ? 'x' : '-'; perm[7] = mode & S_IWOTH ? 'w' : '-'; perm[6] = mode & S_IROTH ? 'r' : '-'; perm[5] = mode & S_IXGRP ? 'x' : '-'; perm[4] = mode & S_IWGRP ? 'w' : '-'; perm[3] = mode & S_IRGRP ? 'r' : '-'; perm[2] = mode & S_IXUSR ? 'x' : '-'; perm[1] = mode & S_IWUSR ? 'w' : '-'; perm[0] = mode & S_IRUSR ? 'r' : '-'; printf("[%s]",perm); } static void print_dir(Ecma119Node *dir, int level) { int i; char *sp = alloca(level * 2 + 1); for (i = 0; i < level * 2; i += 2) { sp[i] = '|'; sp[i+1] = ' '; } sp[level * 2-1] = '-'; sp[level * 2] = '\0'; for (i = 0; i < dir->info.dir->nchildren; i++) { Ecma119Node *child = dir->info.dir->children[i]; if (child->type == ECMA119_DIR) { printf("%s+[D] ", sp); print_permissions(iso_node_get_permissions(child->node)); printf(" %s\n", child->iso_name); print_dir(child, level+1); } else if (child->type == ECMA119_FILE) { printf("%s-[F] ", sp); print_permissions(iso_node_get_permissions(child->node)); printf(" %s {%p}\n", child->iso_name, (void*)child->info.file); } else if (child->type == ECMA119_SYMLINK) { printf("%s-[L] ", sp); print_permissions(iso_node_get_permissions(child->node)); printf(" %s -> %s\n", child->iso_name, ((IsoSymlink*)child->node)->dest); } else if (child->type == ECMA119_SPECIAL) { printf("%s-[S] ", sp); print_permissions(iso_node_get_permissions(child->node)); printf(" %s\n", child->iso_name); } else if (child->type == ECMA119_PLACEHOLDER) { printf("%s-[RD] ", sp); print_permissions(iso_node_get_permissions(child->node)); printf(" %s\n", child->iso_name); } else { printf("%s-[????] ", sp); } } } int main(int argc, char **argv) { int result; IsoImage *image; Ecma119Image *ecma119; if (argc != 2) { printf ("You need to specify a valid path\n"); return 1; } iso_init(); iso_set_msgs_severities("NEVER", "ALL", ""); result = iso_image_new("volume_id", &image); if (result < 0) { printf ("Error creating image\n"); return 1; } result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[1]); if (result < 0) { printf ("Error adding directory %d\n", result); return 1; } ecma119 = calloc(1, sizeof(Ecma119Image)); iso_rbtree_new(iso_file_src_cmp, &(ecma119->files)); ecma119->iso_level = 1; ecma119->rockridge = 1; ecma119->image = image; ecma119->input_charset = strdup("UTF-8"); /* create low level tree */ result = ecma119_tree_create(ecma119); if (result < 0) { printf ("Error creating ecma-119 tree: %d\n", result); return 1; } printf("================= ECMA-119 TREE =================\n"); print_dir(ecma119->root, 0); printf("\n\n"); ecma119_node_free(ecma119->root); iso_rbtree_destroy(ecma119->files, iso_file_src_free); free(ecma119->input_charset); free(ecma119); iso_image_unref(image); iso_finish(); return 0; } /* * Little program that import a directory, find matching nodes and prints the * resulting iso tree. */ #include "libisofs.h" #include #include #include #include #include #include static void print_dir(IsoDir *dir) { IsoDirIter *iter; IsoNode *node; IsoFindCondition *cond, *c1, *c2; c1 = iso_new_find_conditions_name("*a*"); c2 = iso_new_find_conditions_mode(S_IFREG); cond = iso_new_find_conditions_and(c1, c2); iso_dir_find_children(dir, cond, &iter); while (iso_dir_iter_next(iter, &node) == 1) { char *path = iso_tree_get_node_path(node); printf(" %s\n", path); free(path); } iso_dir_iter_free(iter); } int main(int argc, char **argv) { int result; IsoImage *image; if (argc != 2) { printf ("You need to specify a valid path\n"); return 1; } iso_init(); iso_set_msgs_severities("NEVER", "ALL", ""); result = iso_image_new("volume_id", &image); if (result < 0) { printf ("Error creating image\n"); return 1; } result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[1]); if (result < 0) { printf ("Error adding directory %d\n", result); return 1; } print_dir(iso_image_get_root(image)); iso_image_unref(image); iso_finish(); return 0; } /* * Little program to show how to create an iso image from a local * directory. */ #define LIBISOFS_WITHOUT_LIBBURN yes #include "libisofs.h" #include #include #include #include #include #include #include #include #include const char * const optstring = "JRIL:b:hV:"; extern char *optarg; extern int optind; void usage(char **argv) { printf("%s [OPTIONS] DIRECTORY OUTPUT\n", argv[0]); } void help() { printf( "Options:\n" " -J Add Joliet support\n" " -R Add Rock Ridge support\n" " -I Add ISO 9660:1999 support\n" " -V label Volume Label\n" " -L Set the ISO level (1 or 2)\n" " -b file Specifies a boot image to add to image\n" " -h Print this message\n" ); } int callback(IsoFileSource *src) { char *path = iso_file_source_get_path(src); printf("CALLBACK: %s\n", path); free(path); return 1; } int main(int argc, char **argv) { int result; int c; IsoImage *image; struct burn_source *burn_src; unsigned char buf[2048]; FILE *fd; IsoWriteOpts *opts; char *volid = "VOLID"; char *boot_img = NULL; int rr = 0, j = 0, iso1999 = 0, level = 1; while ((c = getopt(argc, argv, optstring)) != -1) { switch(c) { case 'h': usage(argv); help(); exit(0); break; case 'J': j = 1; break; case 'R': rr = 1; break; case 'I': iso1999 = 1; break; case 'L': level = atoi(optarg); break; case 'b': boot_img = optarg; break; case 'V': volid = optarg; break; case '?': usage(argv); exit(1); break; } } if (argc < 2) { printf ("Please pass directory from which to build ISO\n"); usage(argv); return 1; } if (argc < 3) { printf ("Please supply output file\n"); usage(argv); return 1; } fd = fopen(argv[optind+1], "w"); if (!fd) { err(1, "error opening output file"); } result = iso_init(); if (result < 0) { printf ("Can't initialize libisofs\n"); return 1; } iso_set_msgs_severities("NEVER", "ALL", ""); result = iso_image_new(volid, &image); if (result < 0) { printf ("Error creating image\n"); return 1; } iso_tree_set_follow_symlinks(image, 0); iso_tree_set_ignore_hidden(image, 0); iso_tree_set_ignore_special(image, 0); iso_set_abort_severity("SORRY"); /*iso_tree_set_report_callback(image, callback);*/ result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[optind]); if (result < 0) { printf ("Error adding directory %d\n", result); return 1; } if (boot_img) { /* adds El-Torito boot info. Tunned for isolinux */ ElToritoBootImage *bootimg; result = iso_image_set_boot_image(image, boot_img, ELTORITO_NO_EMUL, "/isolinux/boot.cat", &bootimg); if (result < 0) { printf ("Error adding boot image %d\n", result); return 1; } el_torito_set_load_size(bootimg, 4); el_torito_patch_isolinux_image(bootimg); } result = iso_write_opts_new(&opts, 0); if (result < 0) { printf ("Cant create write opts, error %d\n", result); return 1; } iso_write_opts_set_iso_level(opts, level); iso_write_opts_set_rockridge(opts, rr); iso_write_opts_set_joliet(opts, j); iso_write_opts_set_iso1999(opts, iso1999); result = iso_image_create_burn_source(image, opts, &burn_src); if (result < 0) { printf ("Cant create image, error %d\n", result); return 1; } iso_write_opts_free(opts); while (burn_src->read_xt(burn_src, buf, 2048) == 2048) { fwrite(buf, 1, 2048, fd); } fclose(fd); burn_src->free_data(burn_src); free(burn_src); iso_image_unref(image); iso_finish(); return 0; } /* * Copyright (c) 2007 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #include #include #include "libisofs.h" /* * Little test program that extracts a file form a given ISO image. * Outputs file contents to stdout! */ int main(int argc, char **argv) { int res; IsoFilesystem *fs; IsoFileSource *file; struct stat info; IsoDataSource *src; IsoReadOpts *opts; if (argc != 3) { fprintf(stderr, "Usage: isocat /path/to/image /path/to/file\n"); return 1; } res = iso_init(); if (res < 0) { fprintf(stderr, "Can't init libisofs\n"); return 1; } res = iso_data_source_new_from_file(argv[1], &src); if (res < 0) { fprintf(stderr, "Error creating data source\n"); return 1; } res = iso_read_opts_new(&opts, 0); if (res < 0) { fprintf(stderr, "Error creating read options\n"); return 1; } res = iso_image_filesystem_new(src, opts, 1, &fs); if (res < 0) { fprintf(stderr, "Error creating filesystem\n"); return 1; } iso_read_opts_free(opts); res = fs->get_by_path(fs, argv[2], &file); if (res < 0) { fprintf(stderr, "Can't get file, err = %d\n", res); return 1; } res = iso_file_source_lstat(file, &info); if (res < 0) { fprintf(stderr, "Can't stat file, err = %d\n", res); return 1; } if (S_ISDIR(info.st_mode)) { fprintf(stderr, "Path refers to a directory!!\n"); return 1; } else { char buf[1024]; res = iso_file_source_open(file); if (res < 0) { fprintf(stderr, "Can't open file, err = %d\n", res); return 1; } while ((res = iso_file_source_read(file, buf, 1024)) > 0) { fwrite(buf, 1, res, stdout); } if (res < 0) { fprintf(stderr, "Error reading, err = %d\n", res); return 1; } iso_file_source_close(file); } iso_file_source_unref(file); iso_filesystem_unref(fs); iso_data_source_unref(src); iso_finish(); return 0; } /* * Very simple program to show how to grow an iso image. */ #include "libisofs.h" #include "libburn/libburn.h" #include #include #include #include #include #include #include #include static IsoDataSource *libburn_data_source_new(struct burn_drive *d); void usage(char **argv) { printf("%s DISC DIRECTORY\n", argv[0]); } int main(int argc, char **argv) { int result; IsoImage *image; IsoDataSource *src; struct burn_source *burn_src; struct burn_drive_info *drives; struct burn_drive *drive; unsigned char buf[32 * 2048]; IsoWriteOpts *opts; int ret = 0; IsoReadImageFeatures *features; uint32_t ms_block; IsoReadOpts *ropts; if (argc < 3) { usage(argv); return 1; } iso_init(); iso_set_msgs_severities("NEVER", "ALL", ""); /* create the image context */ result = iso_image_new("volume_id", &image); if (result < 0) { printf ("Error creating image\n"); return 1; } iso_tree_set_follow_symlinks(image, 0); iso_tree_set_ignore_hidden(image, 0); if (!burn_initialize()) { err(1, "Can't init libburn"); } burn_msgs_set_severities("NEVER", "SORRY", "libburner : "); if (burn_drive_scan_and_grab(&drives, argv[1], 0) != 1) { err(1, "Can't open device. Are you sure it is a valid drive?\n"); } drive = drives[0].drive; #ifdef ISO_GROW_CHECK_MEDIA { /* some check before going on */ enum burn_disc_status state; int pno; char name[80]; state = burn_disc_get_status(drive); burn_disc_get_profile(drive, &pno, name); /* * my drives report BURN_DISC_BLANK on a DVD+RW with data. * is that correct? */ if ( (pno != 0x1a) /*|| (state != BURN_DISC_FULL)*/ ) { printf("You need to insert a DVD+RW with some data.\n"); printf("Profile: %x, state: %d.\n", pno, state); ret = 1; goto exit_cleanup; } } #endif /* create the data source to accesss previous image */ src = libburn_data_source_new(drive); if (src == NULL) { printf("Can't create data source.\n"); ret = 1; goto exit_cleanup; } /* import previous image */ ret = iso_read_opts_new(&ropts, 0); if (ret < 0) { fprintf(stderr, "Error creating read options\n"); return 1; } result = iso_image_import(image, src, ropts, &features); iso_data_source_unref(src); if (result < 0) { printf ("Error importing previous session %d\n", result); return 1; } iso_read_opts_free(ropts); iso_tree_set_replace_mode(image, ISO_REPLACE_IF_NEWER); /* add new dir */ result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[2]); if (result < 0) { printf ("Error adding directory %d\n", result); return 1; } /* generate a multisession image with new contents */ result = iso_write_opts_new(&opts, 1); if (result < 0) { printf("Cant create write opts, error %d\n", result); return 1; } /* round up to 32kb aligment = 16 block */ ms_block = ((iso_read_image_features_get_size(features) + 15) / 16 ) * 16; iso_write_opts_set_ms_block(opts, ms_block); iso_write_opts_set_appendable(opts, 1); iso_write_opts_set_overwrite_buf(opts, buf); iso_read_image_features_destroy(features); result = iso_image_create_burn_source(image, opts, &burn_src); if (result < 0) { printf("Cant create image, error %d\n", result); return 1; } iso_write_opts_free(opts); /* a. write the new image */ printf("Adding new data...\n"); { struct burn_disc *target_disc; struct burn_session *session; struct burn_write_opts *burn_options; struct burn_track *track; struct burn_progress progress; char reasons[BURN_REASONS_LEN]; target_disc = burn_disc_create(); session = burn_session_create(); burn_disc_add_session(target_disc, session, BURN_POS_END); track = burn_track_create(); burn_track_set_source(track, burn_src); burn_session_add_track(session, track, BURN_POS_END); burn_options = burn_write_opts_new(drive); burn_drive_set_speed(drive, 0, 0); burn_write_opts_set_underrun_proof(burn_options, 1); /* mmm, check for 32K alignment? */ burn_write_opts_set_start_byte(burn_options, ms_block * 2048); if (burn_write_opts_auto_write_type(burn_options, target_disc, reasons, 0) == BURN_WRITE_NONE) { printf("Failed to find a suitable write mode:\n%s\n", reasons); ret = 1; goto exit_cleanup; } /* ok, write the new track */ burn_disc_write(burn_options, target_disc); burn_write_opts_free(burn_options); while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) usleep(1002); while (burn_drive_get_status(drive, &progress) != BURN_DRIVE_IDLE) { printf("Writing: sector %d of %d\n", progress.sector, progress.sectors); sleep(1); } } /* b. write the new vol desc */ printf("Writing the new vol desc...\n"); ret = burn_random_access_write(drive, 0, (char*)buf, 32*2048, 0); if (ret != 1) { printf("Ups, new vol desc write failed\n"); } iso_image_unref(image); exit_cleanup:; burn_drive_release(drives[0].drive, 0); burn_finish(); iso_finish(); exit(ret); } static int libburn_ds_read_block(IsoDataSource *src, uint32_t lba, uint8_t *buffer) { struct burn_drive *d; off_t data_count; d = (struct burn_drive*)src->data; if ( burn_read_data(d, (off_t) lba * (off_t) 2048, (char*)buffer, 2048, &data_count, 0) < 0 ) { return -1; /* error */ } return 1; } static int libburn_ds_open(IsoDataSource *src) { /* nothing to do, device is always opened */ return 1; } static int libburn_ds_close(IsoDataSource *src) { /* nothing to do, device is always opened */ return 1; } static void libburn_ds_free_data(IsoDataSource *src) { /* nothing to do */ } static IsoDataSource * libburn_data_source_new(struct burn_drive *d) { IsoDataSource *ret; ret = malloc(sizeof(IsoDataSource)); ret->version = 0; ret->refcount = 1; ret->read_block = libburn_ds_read_block; ret->open = libburn_ds_open; ret->close = libburn_ds_close; ret->free_data = libburn_ds_free_data; ret->data = d; return ret; } /* * Little program to show how to modify an iso image. */ #define LIBISOFS_WITHOUT_LIBBURN yes #include "libisofs.h" #include #include #include #include #include #include #include #include void usage(char **argv) { printf("%s [OPTIONS] IMAGE DIRECTORY OUTPUT\n", argv[0]); } int main(int argc, char **argv) { int result; IsoImage *image; IsoDataSource *src; struct burn_source *burn_src; unsigned char buf[2048]; FILE *fd; IsoWriteOpts *opts; IsoReadOpts *ropts; if (argc < 4) { usage(argv); return 1; } fd = fopen(argv[3], "w"); if (!fd) { err(1, "error opening output file"); } iso_init(); iso_set_msgs_severities("NEVER", "ALL", ""); /* create the data source to accesss previous image */ result = iso_data_source_new_from_file(argv[1], &src); if (result < 0) { printf ("Error creating data source\n"); return 1; } /* create the image context */ result = iso_image_new("volume_id", &image); if (result < 0) { printf ("Error creating image\n"); return 1; } iso_tree_set_follow_symlinks(image, 0); iso_tree_set_ignore_hidden(image, 0); /* import previous image */ result = iso_read_opts_new(&ropts, 0); if (result < 0) { fprintf(stderr, "Error creating read options\n"); return 1; } result = iso_image_import(image, src, ropts, NULL); iso_read_opts_free(ropts); iso_data_source_unref(src); if (result < 0) { printf ("Error importing previous session %d\n", result); return 1; } /* add new dir */ result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[2]); if (result < 0) { printf ("Error adding directory %d\n", result); return 1; } /* generate a new image with both previous and added contents */ result = iso_write_opts_new(&opts, 1); if (result < 0) { printf("Cant create write opts, error %d\n", result); return 1; } /* for isolinux: iso_write_opts_set_allow_full_ascii(opts, 1); */ result = iso_image_create_burn_source(image, opts, &burn_src); if (result < 0) { printf ("Cant create image, error %d\n", result); return 1; } iso_write_opts_free(opts); while (burn_src->read_xt(burn_src, buf, 2048) == 2048) { fwrite(buf, 1, 2048, fd); } fclose(fd); burn_src->free_data(burn_src); free(burn_src); iso_image_unref(image); iso_finish(); return 0; } /* * Little program to show how to create a multisession iso image. */ #define LIBISOFS_WITHOUT_LIBBURN yes #include "libisofs.h" #include #include #include #include #include #include #include #include void usage(char **argv) { printf("%s LSS NWA DISC DIRECTORY OUTPUT\n", argv[0]); } int main(int argc, char **argv) { int result; IsoImage *image; IsoDataSource *src; struct burn_source *burn_src; unsigned char buf[2048]; FILE *fd; IsoWriteOpts *opts; IsoReadOpts *ropts; uint32_t ms_block; if (argc < 6) { usage(argv); return 1; } fd = fopen(argv[5], "w"); if (!fd) { err(1, "error opening output file"); } iso_init(); iso_set_msgs_severities("NEVER", "ALL", ""); /* create the data source to accesss previous image */ result = iso_data_source_new_from_file(argv[3], &src); if (result < 0) { printf ("Error creating data source\n"); return 1; } /* create the image context */ result = iso_image_new("volume_id", &image); if (result < 0) { printf ("Error creating image\n"); return 1; } iso_tree_set_follow_symlinks(image, 0); iso_tree_set_ignore_hidden(image, 0); /* import previous image */ result = iso_read_opts_new(&ropts, 0); if (result < 0) { fprintf(stderr, "Error creating read options\n"); return 1; } iso_read_opts_set_start_block(ropts, atoi(argv[1])); result = iso_image_import(image, src, ropts, NULL); iso_read_opts_free(ropts); iso_data_source_unref(src); if (result < 0) { printf ("Error importing previous session %d\n", result); return 1; } /* add new dir */ result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[4]); if (result < 0) { printf ("Error adding directory %d\n", result); return 1; } /* generate a multisession image with new contents */ result = iso_write_opts_new(&opts, 1); if (result < 0) { printf("Cant create write opts, error %d\n", result); return 1; } /* round up to 32kb aligment = 16 block */ ms_block = atoi(argv[2]); iso_write_opts_set_ms_block(opts, ms_block); iso_write_opts_set_appendable(opts, 1); result = iso_image_create_burn_source(image, opts, &burn_src); if (result < 0) { printf ("Cant create image, error %d\n", result); return 1; } iso_write_opts_free(opts); while (burn_src->read_xt(burn_src, buf, 2048) == 2048) { fwrite(buf, 1, 2048, fd); } fclose(fd); burn_src->free_data(burn_src); free(burn_src); iso_image_unref(image); iso_finish(); return 0; } /* * Little program to output the contents of an iso image. */ #include #include #include #include #include "libisofs.h" #ifndef PATH_MAX #define PATH_MAX Libisofs_default_path_maX #endif static void print_permissions(mode_t mode) { char perm[10]; /* TODO suid, sticky... */ perm[9] = '\0'; perm[8] = mode & S_IXOTH ? 'x' : '-'; perm[7] = mode & S_IWOTH ? 'w' : '-'; perm[6] = mode & S_IROTH ? 'r' : '-'; perm[5] = mode & S_IXGRP ? 'x' : '-'; perm[4] = mode & S_IWGRP ? 'w' : '-'; perm[3] = mode & S_IRGRP ? 'r' : '-'; perm[2] = mode & S_IXUSR ? 'x' : '-'; perm[1] = mode & S_IWUSR ? 'w' : '-'; perm[0] = mode & S_IRUSR ? 'r' : '-'; printf(" %s ",perm); } static void print_type(mode_t mode) { switch(mode & S_IFMT) { case S_IFSOCK: printf("[S] "); break; case S_IFLNK: printf("[L] "); break; case S_IFREG: printf("[R] "); break; case S_IFBLK: printf("[B] "); break; case S_IFDIR: printf("[D] "); break; case S_IFIFO: printf("[F] "); break; } } static void print_file_src(IsoFileSource *file) { struct stat info; char *name; iso_file_source_lstat(file, &info); print_type(info.st_mode); print_permissions(info.st_mode); printf(" %10.f ", (double) info.st_size); /* printf(" {%ld,%ld} ", (long)info.st_dev, (long)info.st_ino); */ name = iso_file_source_get_name(file); printf(" %s", name); free(name); if (S_ISLNK(info.st_mode)) { char buf[PATH_MAX]; iso_file_source_readlink(file, buf, PATH_MAX); printf(" -> %s\n", buf); } printf("\n"); } static void print_dir(IsoFileSource *dir, int level) { int ret, i; IsoFileSource *file; struct stat info; char *sp = alloca(level * 2 + 1); for (i = 0; i < level * 2; i += 2) { sp[i] = '|'; sp[i+1] = ' '; } sp[level * 2-1] = '-'; sp[level * 2] = '\0'; ret = iso_file_source_open(dir); if (ret < 0) { printf ("Can't open dir %d\n", ret); } while ((ret = iso_file_source_readdir(dir, &file)) == 1) { printf("%s", sp); print_file_src(file); ret = iso_file_source_lstat(file, &info); if (ret < 0) { break; } if (S_ISDIR(info.st_mode)) { print_dir(file, level + 1); } iso_file_source_unref(file); } iso_file_source_close(dir); if (ret < 0) { printf ("Can't print dir\n"); } } int main(int argc, char **argv) { int result; IsoImageFilesystem *fs; IsoDataSource *src; IsoFileSource *root; IsoReadOpts *ropts; if (argc != 2) { printf ("You need to specify a valid path\n"); return 1; } iso_init(); iso_set_msgs_severities("NEVER", "ALL", ""); result = iso_data_source_new_from_file(argv[1], &src); if (result < 0) { printf ("Error creating data source\n"); return 1; } result = iso_read_opts_new(&ropts, 0); if (result < 0) { fprintf(stderr, "Error creating read options\n"); return 1; } result = iso_image_filesystem_new(src, ropts, 1, &fs); iso_read_opts_free(ropts); if (result < 0) { printf ("Error creating filesystem\n"); return 1; } printf("\nVOLUME INFORMATION\n"); printf("==================\n\n"); printf("Vol. id: %s\n", iso_image_fs_get_volume_id(fs)); printf("Publisher: %s\n", iso_image_fs_get_publisher_id(fs)); printf("Data preparer: %s\n", iso_image_fs_get_data_preparer_id(fs)); printf("System: %s\n", iso_image_fs_get_system_id(fs)); printf("Application: %s\n", iso_image_fs_get_application_id(fs)); printf("Copyright: %s\n", iso_image_fs_get_copyright_file_id(fs)); printf("Abstract: %s\n", iso_image_fs_get_abstract_file_id(fs)); printf("Biblio: %s\n", iso_image_fs_get_biblio_file_id(fs)); printf("\nDIRECTORY TREE\n"); printf("==============\n"); result = fs->get_root(fs, &root); if (result < 0) { printf ("Can't get root %d\n", result); return 1; } /* print_file_src(root); */ print_dir(root, 0); iso_file_source_unref(root); fs->close(fs); iso_filesystem_unref((IsoFilesystem*)fs); iso_data_source_unref(src); iso_finish(); return 0; } /* * Copyright (c) 2007 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #include "libisofs.h" #include "fsource.h" #include #include #include #ifndef PATH_MAX #define PATH_MAX Libisofs_default_path_maX #endif /* * Little test program to test filesystem implementations. * */ static void print_permissions(mode_t mode) { char perm[10]; //TODO suid, sticky... perm[9] = '\0'; perm[8] = mode & S_IXOTH ? 'x' : '-'; perm[7] = mode & S_IWOTH ? 'w' : '-'; perm[6] = mode & S_IROTH ? 'r' : '-'; perm[5] = mode & S_IXGRP ? 'x' : '-'; perm[4] = mode & S_IWGRP ? 'w' : '-'; perm[3] = mode & S_IRGRP ? 'r' : '-'; perm[2] = mode & S_IXUSR ? 'x' : '-'; perm[1] = mode & S_IWUSR ? 'w' : '-'; perm[0] = mode & S_IRUSR ? 'r' : '-'; printf(" %s ",perm); } static void print_type(mode_t mode) { switch(mode & S_IFMT) { case S_IFSOCK: printf("[S] "); break; case S_IFLNK: printf("[L] "); break; case S_IFREG: printf("[R] "); break; case S_IFBLK: printf("[B] "); break; case S_IFDIR: printf("[D] "); break; case S_IFIFO: printf("[F] "); break; } } static void print_file_src(IsoFileSource *file) { struct stat info; char *name; iso_file_source_lstat(file, &info); print_type(info.st_mode); print_permissions(info.st_mode); printf(" {%ld,%ld} ", (long)info.st_dev, (long)info.st_ino); name = iso_file_source_get_name(file); printf(" %s", name); free(name); if (S_ISLNK(info.st_mode)) { char buf[PATH_MAX]; iso_file_source_readlink(file, buf, PATH_MAX); printf(" -> %s\n", buf); } printf("\n"); } int main(int argc, char **argv) { int res; IsoFilesystem *fs; IsoFileSource *dir; IsoFileSource *file; struct stat info; if (argc != 2) { fprintf(stderr, "Usage: lsl /path/to/file\n"); return 1; } /* create filesystem object */ res = iso_local_filesystem_new(&fs); if (res < 0) { fprintf(stderr, "Can't get local fs object, err = %d\n", res); return 1; } res = fs->get_by_path(fs, argv[1], &dir); if (res < 0) { fprintf(stderr, "Can't get file, err = %d\n", res); return 1; } res = iso_file_source_lstat(dir, &info); if (res < 0) { fprintf(stderr, "Can't stat file, err = %d\n", res); return 1; } if (S_ISDIR(info.st_mode)) { res = iso_file_source_open(dir); if (res < 0) { fprintf(stderr, "Can't open file, err = %d\n", res); return 1; } while (iso_file_source_readdir(dir, &file) == 1) { print_file_src(file); iso_file_source_unref(file); } res = iso_file_source_close(dir); if (res < 0) { fprintf(stderr, "Can't close file, err = %d\n", res); return 1; } } else { print_file_src(dir); } iso_file_source_unref(dir); iso_filesystem_unref(fs); return 0; } /* * Little program that import a directory and prints the resulting iso tree. */ #include "libisofs.h" #include #include #include #include #include #include static void print_permissions(mode_t mode) { char perm[10]; /* TODO suid, sticky... */ perm[9] = '\0'; perm[8] = mode & S_IXOTH ? 'x' : '-'; perm[7] = mode & S_IWOTH ? 'w' : '-'; perm[6] = mode & S_IROTH ? 'r' : '-'; perm[5] = mode & S_IXGRP ? 'x' : '-'; perm[4] = mode & S_IWGRP ? 'w' : '-'; perm[3] = mode & S_IRGRP ? 'r' : '-'; perm[2] = mode & S_IXUSR ? 'x' : '-'; perm[1] = mode & S_IWUSR ? 'w' : '-'; perm[0] = mode & S_IRUSR ? 'r' : '-'; printf("[%s]",perm); } static void print_dir(IsoDir *dir, int level) { int i; IsoDirIter *iter; IsoNode *node; char *sp = alloca(level * 2 + 1); for (i = 0; i < level * 2; i += 2) { sp[i] = '|'; sp[i+1] = ' '; } sp[level * 2-1] = '-'; sp[level * 2] = '\0'; iso_dir_get_children(dir, &iter); while (iso_dir_iter_next(iter, &node) == 1) { if (ISO_NODE_IS_DIR(node)) { printf("%s+[D] ", sp); print_permissions(iso_node_get_permissions(node)); printf(" %s\n", iso_node_get_name(node)); print_dir(ISO_DIR(node), level+1); } else if (ISO_NODE_IS_FILE(node)) { printf("%s-[F] ", sp); print_permissions(iso_node_get_permissions(node)); printf(" %s\n", iso_node_get_name(node) ); } else if (ISO_NODE_IS_SYMLINK(node)) { printf("%s-[L] ", sp); print_permissions(iso_node_get_permissions(node)); printf(" %s -> %s \n", iso_node_get_name(node), iso_symlink_get_dest(ISO_SYMLINK(node)) ); } else { printf("%s-[C] ", sp); print_permissions(iso_node_get_permissions(node)); printf(" %s\n", iso_node_get_name(node) ); } } iso_dir_iter_free(iter); } int main(int argc, char **argv) { int result; IsoImage *image; if (argc != 2) { printf ("You need to specify a valid path\n"); return 1; } iso_init(); iso_set_msgs_severities("NEVER", "ALL", ""); result = iso_image_new("volume_id", &image); if (result < 0) { printf ("Error creating image\n"); return 1; } result = iso_tree_add_dir_rec(image, iso_image_get_root(image), argv[1]); if (result < 0) { printf ("Error adding directory %d\n", result); return 1; } printf("================= IMAGE =================\n"); print_dir(iso_image_get_root(image), 0); printf("\n\n"); iso_image_unref(image); iso_finish(); return 0; } =============================================================================== LIBISOFS DEVELOPMENT TUTORIAL =============================================================================== Creation date: 2008-Jan-27 Author: Vreixo Formoso _______________________________________________________________________________ This is a little tutorial of how to use libisofs library for application development. Contents: --------- 1. Introduction 1.1 Library initialization 1.2 Image context 1.3 Error reporting 2. Creating an image 2.1 Image tree manipulation 2.2 Set the write options 2.3 Obtaining a burn_source 3. Image growing and modification 3.1 Growing vs Modification 3.2 Image import 3.3 Generating a new image 4. Bootable images 5. Advanced features ------------------------------------------------------------------------------- 1. Introduction ------------------------------------------------------------------------------- [TODO some lines about refcounts] ------------------------------------------------------------------------------- 1.1. Library initialization Before any usage of the library, you have to call iso_init() in the same way, when you have finished using the library, you should call iso_finish() to free all resources reserved by the library. ------------------------------------------------------------------------------- 1.2. Image context Libisofs is image-oriented, the core of libisofs usage is the IsoImage object. Thus, the first you need to do is to get your own IsoImage object: IsoImage *my_image; iso_image_new("NEW DISC", &my_image); An IsoImage is a context for image creation. It holds the files that will be added to image, other related information and several options to customize the behavior of libisofs when working with such Image. i.e., an IsoImage is a context for libisofs operations. As such, you can work with several image contexts at a time. ------------------------------------------------------------------------------- 1.3. Error reporting In libisofs error reporting is done in two ways: with the return value of the functions and with the message queue. Error codes are negative numbers, defined in "libisofs.h" header. An error code is associated with a given severity, either "DEBUG", "UPDATE", "NOTE", "HINT", "WARNING", "SORRY", "FAILURE" and "FATAL". For the meaning of each severity take a look at private header "libiso_msgs.h". Errors reported by function return value are always "FAILURE" or "FATAL". Other kind of errors are only reported with the message queue. You can get the severity of any error message with iso_error_get_severity() function. First of all, most libisofs functions return an integer. If such integer is a negative number, it means the function has returned an error. The error code and its severity is encoded in the return value (take a look at error codes in libisofs.h header). Additionally, libisofs reports most of its errors in a message queue. Error messages on that queue can be printed directly to stderr or programmatically retrieved. First of all, you should set the severity threshold over which an error is printed or enqueued, with function: iso_set_msgs_severities() Errors enqueued can be retrieved with function: iso_obtain_msgs() Together with the code error, a text message and its severity, this function also returns the image id. This is an identifier that uniquely identifies a given image context. You can get the identifier of each IsoImage with the iso_image_get_msg_id() and that way distinguish what image has issued the message. ------------------------------------------------------------------------------- 2. Creating an Image ------------------------------------------------------------------------------- An image is built from a set of files that you want to store together in an ISO-9660 volume. We call the "iso tree" to the file hierarchy that will be written to image. The image context, IsoImage, holds that tree, together with configuration options and other properties of the image, that provide info about the volume (such as the identifier, author, etc...). All configuration options and volume properties are set by its corresponding setters (iso_image_set_volset_id(), iso_image_set_publisher_id()...) To create an image, you have to follow the following steps: * Obtain the image context. See "1.2 Image context" for details of how to obtain the IsoImage. * Set the desired properties * Prepare the iso tree with the files you want to add to image. See "2.1 Image tree manipulation" for details * Select the options for image generation. See "2.2 Set the write options" * Get the burn_source used to actually write the image. ------------------------------------------------------------------------------- 2.1 Image tree manipulation libisofs maintains in memory a file tree (usually called the iso tree), that represents the files and directories that will be written later to image. You are allowed to make whatever changes you want to that tree, just like you do to any "real" filesystem, before actually write it to image. Unlike other ISO-9660 mastering tools, you have full control over the file hierarchy that will be written to image, via the libisofs API. You can add new files, create any file in image, change its name, attributes, etc The iso tree behaves just like any other POSIX filesystem. The root of the iso tree is created automatically when the IsoImage is allocated, and you can't replace it. To get a reference to it you can use the function: iso_image_get_root() * Iso tree objects Each file in the image or iso tree is represented by an IsoNode instance. In the same way a POSIX filesystem has several file types (regular files, directories, symlinks...), the IsoNode has several subtypes: IsoNode | --------------------------------- | | | | IsoDir IsoFile IsoSymlink IsoSpecial where - IsoDir represents a directory - IsoFile represents a regular file - IsoSymlink represents a symbolic linke - IsoSpecial represents any other POSIX file, i.e. block and character devices, FIFOs, sockets. You can obtain the concrete type of an IsoNode with the iso_node_get_type() function. Many libisofs functions take or return an IsoNode. Many others, however, require an specific type. You can safety cast any subtype to an IsoNode object. In the same way, after ensuring you are dealing with the correct subtype, you can downcast a given IsoNode to the specific subtype. IsoDir *dir; IsoNode *node; node = (IsoNode*) dir; if (iso_node_get_type(node) == LIBISO_DIR) { dir = (IsoDir*) node; ... } or with the provided macros: IsoDir *dir; IsoNode *node; node = ISO_NODE(dir); if (ISO_NODE_IS_DIR(node)) { dir = ISO_DIR(node); ... } * Adding files to the image Files can be added to the image or iso tree either as new files or as files from the filesystem. In the first case, files are created directly on the image. They do not correspond to any file in the filesystem. Provided functions are: - iso_tree_add_new_dir() - iso_tree_add_new_symlink() - iso_tree_add_new_special() On the other side, you can add local files to the image, either with the iso_tree_add_node() or with iso_tree_add_dir_rec(). The first is intended to add a single file, while the last can be used to add, recursively, a full directory (see below for details). It is important to note that libisofs doesn't store any kind of link between the IsoNode and the filesystem file it was created from. The above functions just initialize a newly created IsoNode with the attributes of a given file in the filesystem. After that, you can move the original file, change its attributes or even delete it. The IsoNode in the image tree remains with the original attributes. One exception to this rule are the contents of a regular file. Libisofs does not make any copy of those contents until they're actually written to image. Thus, you shouldn't modify, move or delete regular files after adding them to the IsoImage. * Recursive directory addition. One common use case is to add a local directory to the image. While this can be done with iso_tree_add_node(), handling the addition of directory children in the application, libisofs provides a function suitable for this case: iso_tree_add_dir_rec() that takes care of adding all files inside a directory, recursing on directory children. By default, this function adds all children. However, it is usual that you don't want really this. For example, you may want to exclude some kind of files (backup files, application sockets,...). Libisofs provides several functions to customize the behavior of that function: - iso_tree_set_follow_symlinks() - iso_tree_set_ignore_hidden() - iso_tree_set_ignore_special() - iso_tree_add_exclude() * Operations on iso tree [TODO briefly explain how to add node, change attributes, ...] * Replace mode [TODO] ------------------------------------------------------------------------------- 2.2 Set the write options Once you have prepared the iso tree, it is time to select the options for the image writing. These options affect the characteristics of the filesystem to create in the image, but also can control how libisofs generates the image. First of all you have to get an instance of IsoWriteOpts, with the function iso_write_opts_new() The several options available can be classified in: - Extensions to add to the ISO-9660 image: iso_write_opts_set_rockridge() iso_write_opts_set_joliet() iso_write_opts_set_iso1999() RockRidge is highly recommended, in fact you should use it in all image. Joliet is needed if you want to use your images in Windows system. Nowadays, ISO-9660:1999 is no much useful, so in most cases you don't want such extension. - ISO-9660 options: iso_write_opts_set_iso_level() iso_write_opts_set_omit_version_numbers() iso_write_opts_set_allow_deep_paths() iso_write_opts_set_allow_longer_paths() iso_write_opts_set_max_37_char_filenames() iso_write_opts_set_no_force_dots() iso_write_opts_set_allow_lowercase() iso_write_opts_set_allow_full_ascii() These control the options for the ISO-9660 filesystem. In most cases you won't care about them, as it is the RockRidge or Joliet extensions what determine the properties of the files once the image is mounted. - File attributes options iso_write_opts_set_replace_mode() iso_write_opts_set_default_dir_mode() iso_write_opts_set_default_file_mode() iso_write_opts_set_default_uid() iso_write_opts_set_default_gid() iso_write_opts_set_replace_timestamps() iso_write_opts_set_default_timestamp() iso_write_opts_set_always_gmt() They allow to set default attributes for files in image, despite of the real attributes of the file on the local filesystem. ------------------------------------------------------------------------------- 2.3 Obtaining a burn_source Finally, you get the burn_source used to write the image with the function: iso_image_create_burn_source() The returned burn_source is suitable for using with libburn, to directly burn the image to a disc. Alternatively, you can use burn_source read() to get the image contents (for example, to write them to a file, pipe...). Before creating the burn_source, libisofs computes the size of the image, so the get_size() function of the burn_source always returns the final image size. It also starts a writing thread. All the operations needed to generate the image are done by this thread, including read the original files contents. The image is writing to a FIFO buffer, from which the burn_source will read. The size of the buffer can be set in advanced with a property of the IsoWriteOpts struct: iso_write_opts_set_fifo_size() You can get the state of the buffer in any moment, with the function: iso_ring_buffer_get_status() You can also cancel the writer thread at any time, with the cancel() function of the burn_source. ------------------------------------------------------------------------------- 3. Image growing and modification ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- 3.1 Growing vs Modification Libisofs is not restricted only to create new images. It can also be used to modify existing images. It supports two kind of image modifications, that we have called image growing and image modification: Image modification consists in generating a new image, based on the contents of an existing image. In this mode, libisofs takes an image, the users modifies its contents (adding new files, removing files, changing their names...), and finally libisofs generates a completely new image. On the other side, image growing is similar, with the difference that the new image is dependent on the other, i.e., it refers to files of the other image. Thus, it can't be mounted without the old image. The purpose of this kind of images is to increment or add files to a multisession disc. The new image only contains the new files. Old files are just references to the old image blocks. The advantage of the growing approach is that the generated image is smaller, as only the new files are written. This mode is suitable when you just want to add some files to a very big image, or when dealing with write-once media, such as CD-R. Both the time and space needed for the modification is much less than with normal image modify. The main problem of growing is that the new image needs to be recorded together with the old image, in order to be mountable. The total size of the image (old + new) is bigger (even much bigger) than a completely new image. So, if you plan to distribute an image on Internet, or burn it to a disc, generate a completely new image is usually a better alternative. To be able to mount a grown image, the OS needs to now you have appended new data to the original image. In multisession media (such as CD-R), the new data is appended as a new session, so the OS can identify this and mount the image propertly. However, when dealing with non-multisession media (such as DVD+RW) or plain .iso files, the new data is just appended at the end of the old image, and the OS has no way to know that the appended data is in fact a "new session". The method introduced by Andy Polyakov in growisofs can be used in those cases. It consists in overwrite the volume descriptors of the old image with a new ones that refer to the newly appended contents. ------------------------------------------------------------------------------- 3.2 Image import The first thing you need to do in order to modify or grow an image is to import it, with the function: iso_image_import() It takes several arguments. First, the image context, an IsoImage previously obtained with iso_image_new(). In most cases you will want to use an empty image. However, if you have already added files to the image, they will be removed and replaced with the contents of the image being imported. The second parameter is an IsoDataSource instance. It abstracts the original image, and it is used by libisofs to access its contents. You are free to implement your own data source to access image contents. However, libisofs has a simple implementation suitable for reading images on the local filesystem, that can be used for import both .iso files and inserted media, via the block device and POSIX functions. You can get it with iso_data_source_new_from_file() The third parameter of iso_image_import() is a pointer to an IsoReadOpts struct. It holds the options for image reading. You get it with: iso_read_opts_new() and after calling iso_image_import() you should free it with iso_read_opts_free() Some options are related to select what extensions to read. Default options are suitable for most users. iso_read_opts_set_no_rockridge() iso_read_opts_set_no_joliet() iso_read_opts_set_no_iso1999() iso_read_opts_set_preferjoliet() If RockRidge extensions are not present, many files attributes can't be obtained. In those cases libisofs uses default values. You have options to configure what default values to use. iso_read_opts_set_default_uid() iso_read_opts_set_default_gid() iso_read_opts_set_default_permissions() If the original image has been created in another system with a different charset, you may want to use: iso_read_opts_set_input_charset() to specify the encoding of the file names on image. Finally, to import multisession images, you should tell libisofs that it must read the last session. For that, you must set the block where the last session starts: iso_read_opts_set_start_block() The last parameter for iso_image_import(), optional, is a pointer that will be filled with a library-allocated IsoReadImageFeatures, that lets you access some information about the image: size, extensions used,... [TODO: explain that iso_image_import uses dir rec options] ------------------------------------------------------------------------------- 3.3 Generating a new image After importing the image, the old image tree gets loaded. You are free to make any changes to it: add new files, remove files, change names or attributes... Refer to "2.1 Image tree manipulation" for details. When it is ready, you can now create the new image. The process is the same as explained in "2.2 Set the write options" and "2.3 Obtaining a burn_source". However, there are some write options that should be taken into account. First of all, you must select whether you want to grow or modify the image (read "3.1 Growing vs Modification" for details). You must call iso_write_opts_set_appendable() An appendable image leads to image growing, and a non-appendable image leads to a completelly new image (modification). An appendable image will be appended after the old image (in a new session, for example). Thus, in those cases, the first block of the image is not 0. You should set the correct lba of the first block with: iso_write_opts_set_ms_block() That is usually the "Next Writable Address" on a multisession media, and a value slightly greater than the old image size on .iso files or DVD+RW media. You can obtain the old image size with the iso_read_image_features_get_size() function. In this last case (i.e., on a non multisession media), you will need to overwrite the volume descriptors of the old image with the new ones. To do this you need: - Allocate a buffer of at least 64 KiBs. - Initialize it with the first 64 KiBs of the original image. - Pass the buffer to libisofs with the iso_write_opts_set_overwrite_buf() option. - After appending the new image, you have to overwrite the first 64 KiBs of the original image with the new content of the buffer. ------------------------------------------------------------------------------- 4. Bootable images ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- 5. Advanced features ------------------------------------------------------------------------------- = libisofs = libisofs is a library to create an ISO-9660 filesystem, and supports extensions like RockRidge and Joliet. It is also a full featured ISO-9660 editor, allowing you to modify an ISO image or multisession disc, including file addition/removal, change of file names and attributes, and similar. The old libisofs.so.5 has been declarated deprecated and frozen, leaving it unmaintained. A full refactoring of the design has been done during the last months, and the next generation libisofs.so.6 of the library will be released in the following days. == Source Code == The code is maintained in a [http://bazaar-vcs.org/ Bazaar] repository at Launchpad (https://launchpad.net/libisofs/). You can download it with: {{{ $ bzr branch lp:libisofs }}} To report any bug or suggest enchantments, [http://libburnia-project.org/register register] yourself and submit a new ticket. Bug and enchantments reports for nglibisofs can be found at http://libburnia-project.org/report/9. == Usage tutorial == Coming soon... For now check [http://codebrowse.launchpad.net/~mario-danic/libisofs/mainline/annotate/metalpain2002%40yahoo.es-20080201154704-xqyzc57vki97iv3y?file_id=tutorial-20080127170757-cwmomu7oz9eh7fcz-1 "doc/Tutorial"] in the source tree. === Applications === Comming soon: [http://libburnia-project.org/browser/libisoburn/trunk libisoburn]: emulates ISO 9660 multi-session on overwriteable media, coordinates libisofs and libburn. [http://libburnia-project.org/browser/libisoburn/trunk/xorriso/xorriso_eng.html?format=raw xorriso]: creates, loads, manipulates and writes ISO 9660 filesystem images with Rock Ridge extensions. Collection of Boot Sector Formats for ISO 9660 Images by Thomas Schmitt - scdbackup@gmx.net Libburnia project - bug-xorriso@gnu.org pkg-libburnia-devel@lists.alioth.debian.org This information is collected from various sources. Some is backed by specifications, some is just rumor which happens to work (maybe not even that). Content EL Torito CD booting, for PC-BIOS x86, PowerPC, (old) Mac, EFI. Boot Info Table and GRUB2 Boot Info Master Boot Record (MBR), for PC-BIOS x86 from (pseudo-) hard disk Apple Partition Map (APM), for more modern Mac GUID Partition Table (GPT), for EFI from (pseudo-) hard disk MIPS Volume Header, for MIPS Big Endian, e.g. SGI Indigo2. DEC Boot Block, for MIPS Little Endian , e.g. DECstation. SUN Disk Label and boot images, for SUN SPARC GRUB2 SUN SPARC Core File Address PowerPC Reference Platform (PReP), for IBM PowerPC Common Hardware Reference Platform (CHRP), for IBM PowerPC HP-PA via PALO header version 4 HP-PA via PALO header version 5 DEC Alpha SRM boot sector, for Alpha architecture Combinations of boot mechanisms: - SYSLINUX isohybrid MBR - SYSLINUX isohybrid for MBR, UEFI and x86-Mac - GRUB2 grub-mkrescue MBR >>> Mac and/or PowerPC bootable GRUB2 image with HFS+/FAT, APM, EFI GPT partition, PreP MBR partition, mountable FAT partition ------------------------------------------------------------------------------ EL Torito CD booting for PC-BIOS x86, PowerPC, (old) Mac, EFI Sources: El Torito, Bootable CD-ROM Format Specification, Version 1.0, 1995 which refers to ECMA-119, the standard for ISO 9660 filesystems. libisofs/eltorito.[ch] by Vreixo Formoso. http://www.uefi.org/sites/default/files/resources/2_4_Errata_B.pdf ECMA-119 prescribes that the first 32 kB of an ISO 9660 image are System Area with arbitrary content. This prescription is obeyed by PC-BIOS systems only if the ISO 9660 image is presented on CD, DVD or BD media. In this case the El Torito Boot record is the starting point of booting. After the System Area, an ISO 9660 image usually has three distinct block intervals for: - Volume descriptors (Primary Volume Descriptor, Boot Record, Joliet, ...) - Directory trees, tables, boot catalog, embedded partitions and filesystems. - Data file content, including content of El Torito boot images. The Boot Record is an ECMA-119 Volume Descriptor which is located at 2 kB block number 17 (decimal), if present at all. Its content points to the location of the Boot Catalog. The format is described in part by ECMA-119 8.2 "Boot Record" and further specified by El Torito figure 7. Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 0 | 0 | Volume Descriptor Type. 0= Boot record 1 - 5 | "CD001" | Standard Identifier 6 - 6 | 1 | Volume Descriptor Version 7 - 38 | el_torito | Boot System Identifier 39 - 70 | 0 | Boot Identifier | | 71 -2047 | ========== | Boot System Use | | 71 - 74 | cataloglba | The 2 kB block number of the Boot Catalog | | as little-endian 32 bit number. | | 75 -2047 | 0 | Unused ---------- | ---------- | ---------------------------------------------------- el_torito is the constant string "EL TORITO SPECIFICATION" padded by 9 zeros. cataloglba has to be provided by the file system generator. The Boot Catalog lists the available boot images which may be prepared for multiple system architectures, called "platforms". It consists of one or more 2 kB blocks. The content is a sequence of fixed format entries, 32 bytes each. The entries are grouped in sections, which assign the entries to a particular system architecture. The booting system will then choose an entry from an appropriate section. Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 31 | ========== | Validation Entry | | begins the first section, specifies an architecture 32 - 63 | ========== | Initial/Default Entry | | points to a boot image for given architecture ---------- | ---------- | ---------------------------------------------------- Optional: ---------- | ---------- | ---------------------------------------------------- 64 - 95 | ========== | Section Header entry | | begins new section, specifies an architecture 96 - 127 | ========== | Section Entry | | points to a boot image for given architecture ... | .......... | Optional more Section Entries ... | .......... | Optional more Section Headers and their Section | | Entries ---------- | ---------- | ---------------------------------------------------- An architecture is referred by a Platform Id number. Defined by El Torito are: 0 = "80x86" which is used for standard PCs with Intel x86 or compatible CPU 1 = "PowerPC" (possibly for IBM machines with PowerPC CPU) 2 = "Mac" (possibly for Apple computers with MC68000 or PowerPC CPU) UEFI 2.4 specifies in 12.3.2.1 "ISO-9660 and El Torito": 0xef = EFI, a competitor and successor to PC-BIOS, further in use with Intel ia64 Itanium and newer Apple machines. Words and numbers are represented as little-endian. Validation Entry: Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 0 | 1 | Header Id | | 1 - 1 | platform_id| Platform Id. One of: 0, 1, 2, 0xef. See above. | | 2 - 3 | 0 | Reserved 4 - 27 | manuf_dev | ID string identifies the manufacturer/developer | | (no non-zero examples known yet) | | 28 - 29 | checksum | Checksum Word for the Validation Entry. | | The sum of all words in the entry has to be 0. | | 30 - 30 | 0x55 | 31 - 31 | 0xaa | ---------- | ---------- | ---------------------------------------------------- Initial/Default Entry: Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 0 | boot_indct | Boot Indicator: 0x88 = bootable, 0x00 = not bootable | | 1 - 1 | boot_media | Boot Media Type (i.e. media emulated by boot image): | | 0= no emulation , 1= 1.2 MB diskette, 2=1.44 MB, | | 3= 2.88 MB , 4= hard disk | | (About everybody uses 0 = no emulation) | | 2 - 3 | load_seg | Load Segment. (meaning unclear) | | "If this value is 0 the system will use the | | traditional segment of 7C0." | | libisofs default is 0 | | 4 - 4 | sys_type | System Type. | | "Must be a copy of byte 5 from the partition table | | found in the boot image." | | libisofs reads the start the boot image as MBR | | if boot_media == 4. This emulated MBR has a | | partition table from where a byte gets copied. | | Else this byte is 0. | | 5 - 5 | 0 | Unused | | 6 - 7 | sec_count | Sector Count. Sector size 512: | | "the number of virtual/emulated sectors the system | | will store at Load Segment during the initial boot | | procedure." | | libisofs stores 1 for emulated boot_media and a | | user defined value for boot_media == 0. Often: 4. | | 8 - 11 | load_rba | Load RBA. The 2 kB block address where the boot | | image file content is located in the ISO 9660 image. | | 12 - 31 | 0 | Unused ---------- | ---------- | ---------------------------------------------------- Section Header Entry: Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 0 | head_ind | Header Indicator: 0x90 = more headers follow | | 0x91 = final header, last section | | 1 - 1 | platform_id| Platform Id. One of: 0, 1, 2, 0xef. See above. | | 2 - 3 | num_entries| Number of entries to follow in this section | | 4 - 31 | | ID string identifies the manufacturer/developer ---------- | ---------- | ---------------------------------------------------- Section Entry: Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 0 | boot_indct | Boot Indicator: 0x88 = bootable, 0x00 = not bootable | | 1 - 1 | boot_media | Boot Media Type (i.e. media emulated by boot image): | | Bit 0 to 3 govern emulation | | 0= no emulation , 1= 1.2 MB diskette, 2=1.44 MB, | | 3= 2.88 MB , 4= hard disk | | (About everybody uses 0 = no emulation) | | Bit 4 is reserved and must be 0 | | Bit 5 "Continuation entry follows" (meaning unclear) | | Might be the indicator for Extension Entries, | | which are not described here. | | Bit 6 "Image contains an ATAPI driver" | | Bit 7 "Image contains SCSI drivers" | | 2 - 3 | load_seg | Load Segment. (meaning unclear) | | See above Initial/Default Entry | | libisofs default is 0. 4 - 4 | sys_type | System Type. | | See above Initial/Default Entry | | 0 if not emulation == 4. 5 - 5 | 0 | Unused | | 6 - 7 | sec_count | Sector Count. Sector size 512. | | See above Initial/Default Entry | | libisofs stores 1 for emulated boot_media and a | | user defined value for boot_media == 0. Often: 4. | | 8 - 11 | load_rba | Load RBA. The 2 kB block address where the boot | | image file content is located in the ISO 9660 image. | | 12 - 31 | sel_crit | "Vendor unique selection criteria." ---------- | ---------- | ---------------------------------------------------- ------------------------------------------------------------------------------ Boot Info Table and GRUB2 Boot Info Sources: man mkisofs by Joerg Schilling Mail conversations with Vladimir Serbinenko. The boot image file content is mostly opaque to the ISO 9660 image generator. Nevertheless there is a tradition named "Boot Info Table" which prescribes to write information into byte fields of the boot image file content. Recent versions of GRUB2 expect a similar patching which has no name yet. For now let's call it "GRUB2 Boot Info" There are no general means known how a producer of ISO 9660 images could detect the need for Boot Info Table production. It rather needs a hint from the user who has to know whether the boot image expects a Boot Info Table. The Boot Info Table begins at byte 8 of the boot image content. Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 8 - 11 | pvd_lba | Block address of the Primary Volume Descriptor. | | This is the session start LBA + 16. | | 12 - 15 | file_lba | Block address of the start of the boot image file | | content. Block size is 2048. | | 16 - 19 | file_len | Number of bytes in boot image file content. | | 20 - 23 | checksum | The sum of all 32-bit words of the file content | | from byte 64 to file end. | | 24 - 63 | 0 | Reserved ---------- | ---------- | ---------------------------------------------------- All numbers are stored little-endian. GRUB2 Boot Info represents a particular block address inside the boot image. It may well be combined with Boot Info Table. See GRUB2 script grub-mkrescue use of xorrisofs options -boot-info-table and --grub2-boot-info. Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 2548 -2555 | grub2_adr | Block address of the start of the boot image file | | content plus 5. Block size is 512. | | 64 bit Little-endian. ---------- | ---------- | ---------------------------------------------------- ------------------------------------------------------------------------------ Master Boot Record (MBR) for PC-BIOS x86 from (pseudo-) hard disk Sources: http://en.wikipedia.org/wiki/Master_boot_record https://en.wikipedia.org/wiki/INT_13H Mailing list conversations with H. Peter Anvin and Vladimir Serbinenko. Mail conversations with Natalia Portillo. The candidates for MBR booting will normally use El Torito rather than MBR if the ISO image is presented on CD, DVD, or BD media. The MBR comes into effect if the image is on a media that is interpreted by the BIOS as some kind of hard disk. Usually real hard disks, floppy disks, USB sticks, memory cards. An important part of an MBR is the DOS style partition table. It describes up to four primary partitions. There are two formats used for block address: Cylinder/Head/Sector (C/H/S) and Logical Block Address (LBA). Both are based on units of 512 bytes. So MBR_LBA = ISO_LBA * 4. Contemporary x86 BIOS normally supports LBA addressing directly. If INT 0x13 AH 0x41 returns with CX bit0 set, then INT 0x13 AH 0x42 may be used for reading. (Sometimes even if the bit is not set to indicate the capability.) For C/H/S, the sector address is broken up into whole cylinders, remaining heads, and remaining sectors + 1. The nomenclature seems to stem from antique drum storage. There are two parameters, sectors_per_head and heads_per_cylinder which are not stored in the MBR. So at ISO production time it is more or less arbitrary how to convert a LBA into a C/H/S address and vice versa. At boot time the x86 BIOS decides about the two parameters. The boot loader may inquire these values by INT 0x13 AH 0x08 and use them to convert LBA to C/H/S for the read operation INT 0x13 AH 0x02. So the C/H/S values in an ISO's partition table are quite fictional and of few impact on boot loaders. More important seems to align partition ends to a consistent cylinder size, because some partition editors deduce their idea of disk geometry from there and raise protest if they deem it inconsistent. For maximum range of C/H/S addresses one may use sectors_per_head = 63 , heads_per_cylinder = 255. But that is not divisible by 4 and imposes alignment problems with ISO 9660 filesystems. So (32,64) for images up to 1 GiB or (63,252) for larger images are better. Words are composed little-endian style. Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 439 | = opaque = | Code Area filled with bytes for some boot system, | | typically machine code. | | 440 - 443 | disk_sgntr | Disc signature: An individual disk id. Some software | | might use it to recognize the same storage medium | | at different device addresses. | | (The Code Area might extend up to this field.) | | 444 - 445 | 0 | "usually nulls" | | (The Code Area might extend up to this field.) | | 446 - 461 | ========== | Partition Table Entry for partition 1 | | 446 - 446 | status | For some generic MBRs this marks the one partition | | from which the MBR should load and run more code. | | 0x80 = bootflag/active , 0x00 = noboot/inactive | | Some BIOSes ignore MBRs with no bootflag in any of | | their partition table entries. | | 447 - 449 | ========== | C/H/S address of partition start 447 - 447 | start_head | Heads part of start address. 448 - 448 | start_c_s | Bits 0 to 5 : Sectors part of start address. | | Bits 6 to 7 : Bits 8 to 9 of cylinders part. 449 - 449 | start_cyl | Lower 8 bits of cylinders part of start address | | 450 - 450 | part_type | Partition type indicates the purpose or kind of | | filesystem in the partition. | | 451 - 453 | ========== | C/H/S address of last absolute sector in partition 451 - 451 | end_head | Heads part of end address. 452 - 452 | end_c_s | Bits 0 to 5 : Sectors part of end address. | Values: 1 to 63, not 0. | | Bits 6 to 7 : Bits 8 to 9 of cylinders part. 453 - 453 | end_cyl | Lower 8 bits of cylinders part of end address | | 454 - 457 | start_lba | LBA of first absolute sector in partition. | | Block size is 512. Counting starts at 0. | | 458 - 461 | num_blocks | Number of sectors in partition. | | 462 - 477 | ========== | Partition Table Entry for partition 2 | part_entr2 | 16 bytes. Format as with partition 1. | | All 0 means that partition is unused/undefined. | | 478 - 493 | ========== | Partition Table Entry for partition 3 | part_entr3 | 16 bytes. See above. | | 494 - 509 | ========== | Partition Table Entry for partition 4 | part_entr4 | 16 bytes. See above. | | 510 - 510 | 0x55 | MBR signature 511 - 511 | 0xaa | MBR signature | | ---------- | ---------- | ---------------------------------------------------- By tradition the MBR itself and possibly more blocks are not claimed by any partition. But starting the first partition at a non-zero block address causes on Linux a partition device file (e.g. /dev/sdb1) which cannot be used to mount the ISO filesystem. libisofs is able to produce a second set of trees and meta data which is suitable for being mounted at start block 16 (ISO) which is block 64 in MBR. See for call iso_write_opts_set_part_offset() and http://libburnia-project.org/wiki/PartitionOffset for examples with program xorriso. ------------------------------------------------------------------------------ Apple Partition Map (APM) for Apple Macs introduced since 2000 and more computer-like than iPad from CD and often from (pseudo-) hard disk Sources: http://mjg59.dreamwidth.org/11285.html http://opensource.apple.com/source/IOStorageFamily/IOStorageFamily-116/IOApplePartitionScheme.h (typedef struct Block0) http://www.informit.com/articles/article.aspx?p=376123&seqNum=3 syslinux-4.05/utils/isohybrid.c Mail conversations with Vladimir Serbinenko. Mail conversations with Natalia Portillo of DiskImageChef, who quoted "Inside Macintosh" Volumes IV and V. APM has an adjustable block size. Because the ISO images shall always work on optical media, and in order to make room for the header block of an additional GPT, only block size 2048 is considered here. The role of APM in the boot process is to guide the firmware to a HFS+ filesystem. Block0 (aka Driver Descriptor Map) of an APM begins at byte 0 of the medium. Thus it collides with MBR and other boot sector formats. By lucky coincidence it is possible to compose a mock-up of a Block0 which is acceptable to firmware which expects APM, and is also harmless x86 machine code with no negative side effects. So it is possible to combine APM with an especially prepared MBR. Block0 is optional. But in the context of bootable hybrid ISOs it is not only needed to announce block size 2048, but also it is much better suited for staging as harmless x86 machine code than is an APM partition entry. Usually there is no Device Partition Map block (signature "TS"), although it is demanded by the book "Inside Macintosh". It would sit where GPT has its header block. So DPM is not described here. The layout of a Block0 of an APM is: Byte Range | Value | Meaning (all numbers are stored big endian) ---------- | ---------- | ---------------------------------------------------- 0 - 1 | sig | Signature 0x45 = 'E' , 0x52 = 'R' 2 - 3 | block_size | 0x0800 = 2048 4 - 7 | block_count| Number of blocks covered by APM | | Often some x86-harmless dummy. E.g. 0x9090 = 37008 | | or 0xeb02ffff = 3,942,842,367 8 - 9 | dev_type | Device type: The id of the Mac driver which is in | | charge of the storage device. 10 - 11 | dev_id | Device id: Address in an i/o bus system. 12 - 15 | drv_data | Driver data: Not used. 16 - 17 | drv_count | Driver count: Count of entries in drv_map. 18 - 505 | drv_map | Driver descriptor table: | | Up to 61 entries of 8 bytes each. | | They contain the 32 bit 512-byte LBA of the driver's | | storage location, its 16 bit size in 512-byte blocks, | | and value 0x0001. 506 - 511 | reserved | ---------- | ---------- | ---------------------------------------------------- The SYSLINUX program isohybrid.c overwrites the first 32 bytes of this layout by its dummy values. It uses the small block_count 0x00009090 and sets all bytes up to 31 to 0. The libisofs HFS+ extension by Vladimir Serbinenko overwrites only the first 8 bytes. It uses the large block_count 0xeb02ffff. Block0 and the following APM entries each occupy 1 block of the announced size. The first APM entry describes the range from its own start to the end of the last APM entry. Each of the other APM entries describes a partition. The layout of an Apple partition map entry is: Byte Range | Value | Meaning (all numbers are stored big endian) ---------- | ---------- | ---------------------------------------------------- 0 - 1 | sig | Signature 0x50 = 'P' , 0x4d = 'M' 2 - 3 | reserved | 4 - 7 | map_entries| Number of partition entries. | | All entries show the same number. 8 - 11 | start_block| "physical block start of partition" 12 - 15 | block_count| "physical block count of partition" 16 - 47 | name | Partition name 48 - 79 | type | Type string 80 - 83 | lb_start | Logical block start = 0 84 - 87 | lb_count | Logical block count (same as block_count) 88 - 91 | flags | Status flags | | bit0= entry is valid | | bit1= partition is allocated | | bit2= partition is in use | | bit3= partition contains valid boot information | | bit4= partition is readable | | bit5= partition is writable | | bit7= boot code is position independent | | bit30= automatic mount (legacy Mac) 92 - 95 | boot_block | Logical start block number of boot code = 0 96 - 99 | boot_bytes | Number of bytes in boot code = 0 100 - 119 | | More boot code stuff = 0 120 - 135 | processor | "processor type" = 0 136 - 511 | reserved | ---------- | ---------- | ---------------------------------------------------- For the first APM entry (byte 0x0800), the following values apply: map_entries = number of APM entries, including itself. E.g. 4. start_block = 1 block_count = map_entries name = "Apple" type = "Apple_partition_map" flags = 3 libisofs uses APM to mark a HFS+ filesystem partition within an ISO 9660 image. Usually the APM has 3 more entries after the first entry: Entry 2 (byte 0x1000) describes the block interval from ISO image start to the start of the HFS+ filesystem meta data. start_block = 16 block_count = start_of_hfs - 16 name = "Gap0" type = "ISO9660_data" flags = 0x13 Entry 3 (byte 0x1800) describes the interval from the start of the HFS+ meta data to the end of the HFS+ data at the end of its partition. This includes all content blocks of the data files in the ISO image. start_block = start_of_hfs block_count = end_of_hfs - start_of_hfs name = "HFSPLUS_Hybrid" type = "Apple_HFS" flags = 0x40000013 Entry 4 (byte 0x2000) describes the interval from the end of the HFS+ partition to the end of the ISO image. It is possible that this interval is empty. In this case, no fourth APM entry will be written. start_block = end_of_hfs block_count = end_of_iso - end_of_hfs name = "Gap1" type = "ISO9660_data" flags = 0x13 >>> Open questions: >>> What HFS+ blessings are needed for booting ? >>> What files need what HFS creator and type settings ? ------------------------------------------------------------------------------ GUID Partition Table (GPT) for alternative mountability paths and for EFI booting of some Apple Macs from (pseudo-) hard disk Sources: http://mjg59.dreamwidth.org/11285.html http://mjg59.fedorapeople.org/Fedora-LiveCD.iso http://en.wikipedia.org/wiki/GUID_Partition_Table http://en.wikipedia.org/wiki/GUID http://www.uefi.org/sites/default/files/resources/2_4_Errata_B.pdf GPT is the partition map format of EFI, a successor of PC-BIOS. Block size is always 512. GPT consists of a header block at block address 1 and a partition table near the start of the medium. This is called the primary GPT. There is a backup copy of header and table near the end of the medium. GPT is particularly designed to co-exist with MBR. Officially only with a Protective MBR which covers the whole medium (except the MBR itself) by a single partition of type 0xee. Inofficially often with filesystem partitions marked in both, GPT and MBR. In the latter case the booting firmware may or may not prefer GPT over the MBR partition table. GPT can co-exist with APM if APM block size is at least 1024. In this case, the primary partition table will begin after the last APM entry block. The header block format is: Byte Range | Value | Meaning (little endian numbers, LBA unit is 512 byte) ---------- | ---------- | ---------------------------------------------------- 0 - 7 | sig | Signature "EFI PART" (with no trailing zero) 8 - 11 | revision | Revision = {0x00, 0x00, 0x01, 0x00} meaning "1.0" 12 - 15 | head_size | Header size = 0x5c = 92 16 - 19 | head_crc | CRC-32 of this header while head_crc is 0 20 - 23 | reserved | = 0 24 - 31 | curr_lba | Location of this header block = 1 32 - 39 | backup_lba | Location of header backup block. See below. 40 - 47 | first_lba | First usable LBA for partitions 48 - 55 | last_lba | Last usable LBA for partitions 56 - 71 | guid | Disk GUID, Random 72 - 79 | part_start | Partition entries start | | Normally this is 2. But to co-exist with APM, it | might become some other number up to 62. 80 - 83 | entry_count| Number of partition entries 84 - 87 | entry_size | Size of a partition entry = 0x80 = 128 88 - 91 | p_arr_crc | CRC-32 of the partition array 92 - 511 | reserved | Must be 0 ---------- | ---------- | ---------------------------------------------------- The CRC-32 algorithm can be characterized as follows: The generating polynomial has the bit representation 0x104c11db7. The seed value for a bit shifting division algorithm is 0x46af6449. It is chosen so that the CRC of 0 bytes of input is 0x00000000. The least significant bits of input bytes get processed first. I.e. bit0 of the last input byte gets mapped to x exp (7 + 32), bit7 of this byte gets mapped to x exp (0 + 32). The resulting division residue gets bitwise mirrored. E.g. bit0 becomes bit31, bit1 becomes bit30, and so on. Further it gets exored with 0xffffffff. A GUID consists of a 32-bit integer, two 16-bit integers, and an array of 8 bytes. The integers are to be stored big-endian. A globally registered class of GUID are the partition type GUIDs: Basic data partition: a2 a0 d0 eb , e5 b9 , 33 44 , 87 c0 68 b6 b7 26 99 c7 HFS+ partition : 00 53 46 48 , 00 00 , aa 11 , aa 11 00 30 65 43 ec ac EFI System partition: 28 73 2a c1 , 1f f8 , d2 11 , ba 4b 00 a0 c9 3e c9 3b Note that the wikipedia list shows the first 32-bit word and the next two 16-bit words in little-endian interpretation. The partition table is an array of entries. Each has a size of 128 bytes. A partition table entry looks like: Byte Range | Value | Meaning (numbers are stored little endian) ---------- | ---------- | ---------------------------------------------------- 0 - 15 | type_guid | Partition type GUID 16 - 31 | part_guid | Unique partition GUID, Random 32 - 39 | start_lba | First LBA 40 - 47 | end_lba | Last LBA (inclusive) 48 - 55 | flags | Attribute flags | | bit0= "System Partition" Do not alter. | | bit2= Legacy BIOS bootable (MBR partition type 0x80) | | bit60= read-only 56 - 127 | name | Characters encoded as UTF-16LE. Padded by 0-bytes. ---------- | ---------- | ---------------------------------------------------- About header field "Location of header backup block": Near to the end of the image, after any data blocks which might be of interest for the filesystems covered by GPT partitions, there is a backup of partition table and header block. The header block is supposed to mark the end of the usable medium. But libisofs may have the need to add more data. The partition table is stored directly before the header block. So it will normally not begin at a 2 KiB block start. The content of the backup partition table is the same as the one of the primary table. The backup header differs from the primary header by Byte Range | Value | Meaning (little endian numbers, LBA unit is 512 byte) ---------- | ---------- | ---------------------------------------------------- 16 - 19 | head_crc | CRC-32 of this header while head_crc is 0. | | Is recomputed after the following changes. 24 - 31 | curr_lba | Location of this header block. | | Shows own block address. 32 - 39 | backup_lba | Location of header backup block. | | Points to primary header block = 1 72 - 79 | part_start | Partition entries start. | | Points to start of backup partition table. ---------- | ---------- | ---------------------------------------------------- An EFI System partition usually contains the same data blocks as the El Torito boot image for EFI. It is used for booting some Macs from (pseudo-) hard disk. ------------------------------------------------------------------------------ MIPS Volume Header for MIPS Big Endian, e.g. SGI Indigo2 Sources: cdrkit-1.1.10/genisoimage/boot-mips.c by Steve McIntyre which refers to genisovh by Florian Lohoff and Thiemo Seufer who seem to have learned parameter settings from IRIX CD media There are traces in the web which relate this to specs by MIPS Computer Systems, Inc. , 1985 Silicon Graphics Computer Systems, Inc. , 2000 Mail conversations with Natalia Portillo. The first 512 bytes of the media constitute the Volume Header. Words are composed big-endian style. Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 3 | 0x0be5a941 | Magic number 4 - 5 | 0 | Root partition number 6 - 7 | 0 | Swap partition number 8 - 23 | 0 | One of the boot_name items from the Volume Directory | | may be put here to choose for booting the entry with | | that name. | | (Obviously it may be empty if only one non-zero entry | | exists in the Volume Directory.) | | 24 - 71 | ========== | Device Parameters | | 24 - 24 | 0 | Spiral addressing skew (unclear what this means) 25 - 25 | 0 | Words of 0 before header 26 - 26 | 0 | Words of 0 between hdr and data 27 - 27 | 0 | Spare sectors per cylinder 28 - 29 | num_cyl_l | Number of usable cylinder, lower two bytes | | ((iso_size + BYTES_PER_SECTOR - 1) / | | (SECTORS_PER_TRACK * BYTES_PER_SECTOR)) & 0xffff 30 - 31 | 0 | Starting head of volume 0 32 - 33 | 1 | Number of tracks per cylinder 34 - 34 | 0 | Depth of CTQ queue (unclear what this means) 35 - 35 | num_cyl_h | Number of usable cylinders, high byte | | ((iso_size + BYTES_PER_SECTOR - 1) / | | (SECTORS_PER_TRACK * BYTES_PER_SECTOR)) >> 16 36 - 37 | 0 | unused 38 - 39 | 32 | SECTORS_PER_TRACK 40 - 41 | 512 | BYTES_PER_SECTOR 42 - 43 | 0 | Sector interleave (unclear what this means) 44 - 47 | 0x00000034 | Controller characteristics composed from | | DP_RESEEK 0x00000020 /* recalibrate as last resort */ | | DP_IGNOREERRORS 0x00000010 | | /* transfer data regardless of errors */ | | DP_TRKFWD 0x00000004 | | /* forward to replacement track */ 48 - 51 | 0 | Bytes/sec for kernel stats 52 - 55 | 0 | Max num retries on data error 56 - 59 | 0 | ms per word to xfer, for iostat 60 - 71 | 0 | 6 parameter words for xylogics controllers | | 72 - 311 | ========== | Volume Directory with 15 entries of 16 bytes each | | 72 - 87 | ========== | Volume Directory Entry 1 72 - 79 | boot_name | Boot file basename, eventually padded by 0 to length 8 80 - 83 | boot_block | ISO 9660 LBA of boot file * 4, i.e. in blocks of 512 84 - 87 | boot_bytes | File length in bytes | | 88 - 311 | see above | Volume Directory Entries 2 to 15 | | 312 - 504 | ========== | Partition Table with 16 entries of 12 bytes each | | 312 - 407 | 0 | Unused partition entries 1 to 8 | | 408 - 419 | ========== | Partition Table Entry 9 for Volume Header 408 - 411 | part_blks | Number of 512 byte blocks in partition | |(iso_size + (BYTES_PER_SECTOR - 1)) / BYTES_PER_SECTOR 412 - 415 | 0 | Start block of partition 416 - 419 | 0 | PTYPE_VOLHDR = Partition is volume header | | 420 - 431 | 0 | Unused partition entry 10 | | 432 - 443 | ========== | Partition Table Entry 11 for Volume 432 - 435 | part_blks | Number of 512 byte blocks in partition | |(iso_size + (BYTES_PER_SECTOR - 1)) / BYTES_PER_SECTOR 436 - 439 | 0 | Start block of partition 440 - 443 | 6 | PTYPE_VOLUME = Partition is entire volume | | 444 - 503 | 0 | Unused partition entries 12 to 16 | | 504 - 507 | head_chk | Volume header checksum | | The two's complement of bytes 0 to 503 read as big | | endian unsigned 32 bit: sum(words) + head_chk == 0 | | 508 - 511 | 0 | Volume header end padding | | up to 2048 | 0 | ISO 9660 Block end padding ---------- | ---------- | ---------------------------------------------------- ------------------------------------------------------------------------------ DEC Boot Block for MIPS Little Endian , e.g. DECstation Sources: cdrkit-1.1.10/genisoimage/boot-mipsel.c by Steve McIntyre which refers to delo by Florian Lohoff and Thiemo Seufer cdrkit-1.1.10/include/glibc_elf.h by Steve McIntyre which is based on from GNUC C Library by Free Software Foundation, Inc. There seems to be only one boot file possible. Some information needs to be read out of the ELF headers of this boot file. Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 7 | 0 | Padding | | 8 - 11 | 0x0002757a | Magic number | | 12 - 15 | 1 | Mode /* 0: Single extent, 1: Multi extent boot */ | | 16 - 19 | load_adr | Load address /* Load below kernel */ | | Stems from ELF header of boot file. | | See below Elf32_Phdr field p_vaddr. | | 20 - 23 | exec_adr | Execution address /* And exec there */ | | Stems from ELF header of boot file. | | See below Elf32_Ehdr field e_entry. | | 24 - 31 | ========== | Boot Map Entry 1 | | 24 - 27 | seg_size | Segment size in file. Blocks of 512 bytes. | | Stems from ELF header of boot file. | | (Elf32_Phdr field p_filesz + 511) / 512; | | 28 - 31 | seg_start | Segment file offset. Blocks 512 bytes. | | ISO 9660 LBA of boot file * 4 plus offset which | | stems from ELF header of boot file: | | (Elf32_Phdr field p_offset + 511) / 512; | | 32 - 431 | ========== | Boot Map Entries 2 to 51 | 0 | | | ---------- | ---------- | ---------------------------------------------------- Elf32_Ehdr gets loaded from boot file byte address 0: Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 23 | | ( Magic number, file information ) | | 24 - 27 | e_entry | /* Entry point virtual address */ | = exec_adr | Needed for exec_adr | | 28 - 31 | e_phoff | /* Program header table file offset */ | | Byte address of Elf32_Phdr | | Elf32_Phdr gets loaded from boot file byte_address Elf32_Ehdr.e_phoff : Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 3 | | ( Segment type ) | | 4 - 7 | p_offset | /* Segment file offset */ |-> seg_start| Needed for seg_start | | 8 - 11 | p_vaddr | /* Segment virtual address */ | =load_adr | Needed for load_adr | | 12 - 15 | | (Segment physical address) | | 16 - 19 | p_filesz | /* Segment size in file */ |-> seg_size | Needed for seg_size | | ---------- | ---------- | ---------------------------------------------------- ------------------------------------------------------------------------------ SUN Disk Label and boot images for SUN SPARC Sources: cdrtools-2.01.01a77/mkisofs/sunlabel.h cdrtools-2.01.01a77/mkisofs/mkisofs.8 by Joerg Schilling The Disk Label is written to the first 512 bytes of the image. It can mark 8 partitions (slices ) of which the first contains the ISO image. The other 7 may contain boot images. Words are composed big-endian style. Block size is 512. Boot images are provided externally. mkisofs arranges them after the end of the ISO image so that each starts at a cylinder boundary (320 kB). There is a mechanism in mkisofs which fills unused partitions by copies of their predecessor in the partition table: "If the special filename ... is used, the actual and all following boot partitions are mapped to the previous partition. If mkisofs is called with -G image -B ... all boot partitions are mapped to the partition that contains the ISO9660 filesystem." Disk Label components: Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 127 | label | ASCII Label | | "CD-ROM Disc with Sun sparc boot created by ..." | | mkisofs option -sparc-label | | 128 - 263 | ========== | /* vtoc inclusions from AT&T SVr4 */ | | 128 - 131 | 1 | Layout version 132 - 139 | 0 | /* volume name */ 140 - 141 | 8 | Number of partitions | | 142 - 173 | ========== | 8 partition entries of 4 bytes | | 142 - 145 | ========== | Entry for partition 1 142 - 143 | 4 | ID tag of partition: 4 = User partition 144 - 145 | 0x10 | Permissions: 0x10 = read-only | | 146 - 149 | ========== | Entry for partition 2 146 - 147 | id_tag2 | ID tag of partition: | | 0 = unused | | 2 = Root partition with boot image 148 - 149 | perm2 | Permissions: | | 0 = unused | | 0x10 = read-only (if used) | | 150 - 173 | ========== | Entries for partition 3 to 8. | | See above: Entry for partition 2 | | 174 - 175 | 0 | Padding | | 176 - 187 | 0 | /* info for mboot */ | | 188 - 191 | 0x600ddeee | /* to verify vtoc sanity */ | | 192 - 231 | 0 | Reserved | | 232 - 263 | 0 | 8 Timestamps of yet unknown format | | 264 - 419 | 0 | Padding | | 420 - 443 | ========== | Disk properties | | 420 - 421 | 350 | Rotations per minute 422 - 423 | 2048 | Number of physical cylinders (fixely 640 MB) 424 - 425 | 0 | /* alternates per cylinder */ 426 - 429 | 0 | /* obsolete */ 430 - 431 | 1 | /* interleave factor */ 432 - 433 | 2048 | Number of data cylinders (fixely 640 MB) 434 - 435 | 0 | /* # of alternate cylinders */ 436 - 437 | 1 | Number of heads per cylinder (i.e. 1 cyl = 320 kB) 438 - 439 | 640 | Number of sectors per head (i.e. 1 head = 320 kB) 440 - 443 | 0 | /* obsolete */ | | 444 - 507 | ========== | Partition table | | 444 - 451 | ========== | Partition table entry #1 | | 444 - 447 | start_cyl | Start cylinder | | 448 - 451 | num_blocks | Number of 512-byte blocks in partition | | 452 - 507 | ========== | Partition table entries #2 to #8 | ... | See above Partition table entry #1 | | 508 - 509 | 0xdabe | Magic Number | | 510 - 511 | checksum | The result of exoring 2-byte words 0 to 254 | | ---------- | ---------- | ---------------------------------------------------- ------------------------------------------------------------------------------ GRUB2 SUN SPARC Core File Address Sources: Mail conversations with Vladimir Serbinenko. GRUB2 lets libisofs write after the disk label block the address and size of a data file in the ISO image. E.g. of /boot/grub/sparc64-ieee1275/core.img. This is combined with a SUN Disk Label which exposes only the single partition describing the overall ISO filesystem size. Byte Range | Value | Meaning ------------ | ---------- | -------------------------------------------------- 512 - 551 | opaque | Code and data provided by GRUB2 | | 552 - 559 | offset | Start byte number of the file. 64-bit big-endian. | | 560 - 563 | size | Number of bytes in the file. 32-bit big-endian. | | 564 - 32767 | opaque | Code and data provided by GRUB2 | | ------------ | ---------- | -------------------------------------------------- ------------------------------------------------------------------------------ PowerPC Reference Platform (PReP) for IBM PowerPC Sources: Mail conversations with Vladimir Serbinenko. PReP boots via a MBR partition containing only raw ELF and having type 0x41. ------------------------------------------------------------------------------ Common Hardware Reference Platform (CHRP) for IBM PowerPC Sources: Mail conversations with Vladimir Serbinenko. http://stuff.mit.edu/afs/sipb/contrib/doc/specs/protocol/chrp/chrp1_7a.pdf https://www.ibm.com/developerworks/community/wikis/home?lang=en#!/wiki/W51a7ffcf4dfd_4b40_9d82_446ebc23c550/page/PowerLinux%20Boot%20howto CHRP is marked by an MBR partition entry of type 0x96 spanning the whole ISO 9660 image. The specs in chrp1_7a.pdf promise that CHRP also recognizes ISO 9660 file systems on unpartitioned disks. (See 11.1.1. Media Layout Format) The firmware looks up a file /ppc/bootinfo.txt which in SGML-ish tag contains firmware commands. E.g. to execute the binary /boot/grub/powerpc.elf as first stage of GRUB2: boot &device;:\boot\grub\powerpc.elf Vladimir Serbinenko stated: PReP boot may be preferable. At least it can co-exist with other partitions in the ISO image [without causing overlapping between partitions]. ------------------------------------------------------------------------------ HP-PA via PALO header version 4 for HP PA-RISC Sources: cdrkit-1.1.10/genisoimage/boot-hppa.c by Steve McIntyre who states "Heavily inspired by palo" This format is expected by PALO versions before 1.92. Their source code defines PALOHDRVERSION as 4. The format also serves as fallback for newer versions, which expect header version 5, if a 0-byte is found at byte position 1024. There are five parameters which get encoded into the first 248 bytes of the System Area: cmdline, bootloader, 32-bit kernel, 64-bit kernel, and ramdisk. They are all mandatory. While cmdline is simply a string of at most 127 characters, the other four point to data files inside the ISO image. All numbers are recorded big endian. Boot sector components: Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 1 | 0x8000 | Magic | | 2 - 6 | "PALO" | Zero terminated string | | 7 - 7 | 4 | Version | | 8 - 11 | kern32_adr | Byte address of the "HPPA 32-bit kernel" file | | genisoimage option -hppa-kernel-32 12 - 15 | kern32_len | Byte count of the "HPPA 32-bit kernel" file | | 16 - 19 | ramdsk_adr | Byte address of the "HPPA ramdisk" file | | genisoimage option -hppa-ramdisk 20 - 23 | ramdsk_len | Byte count of the "HPPA ramdisk" file | | 24 - 151 | cmdline | "Command line" | | genisoimage option -hppa-cmdline | | 232 - 235 | kern64_adr | Byte address of the "HPPA 64-bit kernel" file | | genisoimage option -hppa-kernel-64 236 - 239 | kern64_len | Byte count of the "HPPA 64-bit kernel" file | | 240 - 243 | bootld_adr | Byte address of the "HPPA bootloader" file | | genisoimage option -hppa-bootloader 244 - 247 | bootld_len | Byte count of the "HPPA bootloader" file | | ---------- | ---------- | ---------------------------------------------------- ------------------------------------------------------------------------------ HP-PA via PALO header version 5 for HP PA-RISC Sources: Public mail conversations with Helge Deller, beginning with https://lists.debian.org/debian-hppa/2014/01/msg00016.html http://git.kernel.org/cgit/linux/kernel/git/deller/palo.git/tree/lib/ (especially struct firstblock in common.h and struct partition in part.h) This format is expected by PALO versions 1.92 or higher. They fall back to header version 4 if a 0-byte is found at byte position 1024. Their source code defines PALOHDRVERSION as 5. There are five parameters which get encoded into the first 2048 bytes of the System Area: cmdline, bootloader, 32-bit kernel, 64-bit kernel, and ramdisk. They are all mandatory. While cmdline is simply a string of at most 1023 characters, the other four point to data files inside the ISO image. Several fields of the firstblock shall be hardcoded to 0, on advise of Helge Deller. Their description is shown in round brackets. All numbers are recorded big endian. Except flags, all 4-byte integers are signed. Boot sector components: Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 1 | 0x8000 | Magic | | 2 - 6 | "PALO" | Zero terminated string | | 7 - 7 | 5 | Version | | 8 - 11 | kern32_adr | Byte address of the 32-bit kernel file | | 12 - 15 | kern32_len | Byte count of the 32-bit kernel file | | 16 - 19 | ramdsk_adr | Byte address of the ramdisk file | | 20 - 23 | ramdsk_len | Byte count of the ramdisk file | | 24 - 141 | 0 | All 0s. Old command line of version 4. | | | | 220 - 223 | 0 | (Length of uncompressed 32-bit kernel) | | 224 - 227 | 0 | (Length of uncompressed 64-bit kernel) | | 228 - 231 | 0 | (flags) | | 232 - 235 | kern64_adr | Byte address of the 64-bit kernel file | | 236 - 239 | kern64_len | Byte count of the 64-bit kernel file | | 240 - 243 | ipl_adr | Byte address of the bootloader file | | 244 - 247 | ipl_len | Byte count of the bootloader file | | 248 - 251 | 0 | (ipl_entry: offset to first command in bootloader) | | 446 - 511 | 0 | (MBR partition table and signature) | | 1024 -2047 | cmdline | Zero terminated command line of up to | | 1023 characters | | ---------- | ---------- | ---------------------------------------------------- ------------------------------------------------------------------------------ DEC Alpha SRM boot sector for Alpha architecture Sources: http://www.tldp.org/HOWTO/text/SRM-HOWTO SRM Firmware Howto - Rich Payne, and David Huggins-Daines cdrkit-1.1.10/genisoimage/boot-alpha.c by Steve McIntyre who states "Heavily inspired by isomarkboot by David Mosberger in 1996" mail conversations with Helge Deller The SRM firmware expects a Secondary Bootstrap Loader program, which usually is a data file of the ISO filesystem. This loader is announced by size and block address in the first 512 bytes of the System Area. SRM accepts the boot sector and executes the loader if the checksum matches. All numbers are recorded as unsigned 64 bit little endian. Boot sector components: Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - ? | boot_string| genisoimage writes | | "Linux/Alpha aboot for ISO filesystem." | | with terminating zero byte. | | ? - 479 | 0 | Unused / undefined. | | 480 - 487 | length | Size of boot loader file in units of 512 bytes. | | 488 - 495 | address | LBA of the boot loader file in units of 512 bytes. | | 496 - 503 | flag | "Always 0" | | 504 - 511 | checksum | Sum of 64 bit words 0 to 63 (bytes 0 to 503). | | ---------- | ---------- | ---------------------------------------------------- ------------------------------------------------------------------------------ Combinations of boot mechanisms ------------------------------------------------------------------------------ SYSLINUX Isohybrid MBR Sources: syslinux-3.72/utils/isohybrid , a perl script by H. Peter Anvin = hpa. Mailing list conversations with hpa. An isohybrid MBR directs the booting BIOS to an ISOLINUX boot image which is also the target of an El Torito boot catalog entry. For that purpose one has to take an MBR template and has to set a few bytes to values which sufficiently describe the ISO image and the boot image file. Words are composed little-endian style. Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 431 | = opaque = | Syslinux machine code provided by MBR template | | 432 - 439 | hd_bootlba | Address of the ISOLINUX boot image file in the | | ISO image. Counted in 512 byte blocks. | | 440 - 443 | mbr_id | Random number 444 - 445 | 0 | Padding | | 446 - 509 | ========== | Partition table | | 446 - 461 | part_entry | Partition table entry 1 describing ISO image size | | rounded up to the next full MiB. The partition starts | | at LBA 0. (I.e. contrary to tradition.) | | See above for partition table entry format. | | 462 - 509 | 0 | Unused partition entries 2 to 4 510 - 511 | 0xaa55 | MBR signature ---------- | ---------- | ---------------------------------------------------- The ISO image file gets padded up to the next full MiB. hpa about MBR templates and partition table filesystem types: "[MBR templates] are available in the Syslinux build tree under the names: mbr/isohdp[fp]x*.bin The default probably should be mbr/isohdppx.bin, but it's ultimately up to the user. [...] Note: the filesystem type is largely arbitrary, in theory it can be any value other than 0x00, 0x05, 0x0f, 0x85, 0xee, or 0xef. 0x17 ("Windows IFS Hidden") seems safeish, some people believe 0x83 (Linux) is better. " >>> SYSLINUX isohybrid for MBR, UEFI and x86-Mac Sources: http://mjg59.dreamwidth.org/11285.html http://mjg59.fedorapeople.org/Fedora-LiveCD.iso http://opensource.apple.com/source/IOStorageFamily/IOStorageFamily-116/IOApplePartitionScheme.h (typedef struct Block0) http://www.informit.com/articles/article.aspx?p=376123&seqNum=3 http://en.wikipedia.org/wiki/GUID_Partition_Table http://en.wikipedia.org/wiki/GUID syslinux-4.05/utils/isohybrid.c >>> Motivation: What systems will use the additional data ? amd64 UEFI ? This is a very condensed format which exposes a lot of entry points for boot firmware. It disobeys some of the prescriptions in the previous chapter. Byte Range | Value | Meaning -------------- | ---------- | ------------------------------------------------- 0 - 511 | mbr | Isohybrid MBR pointing to x86 boot image file, | | with three entries in its partition map. | | It shares its first 32 bytes with apm_head. 0 - 31 | apm_head | Mock-up of the start of the first block of | | an Apple Partition Map (APM). 446 - 461 | mbr_entry1 | Partition table entry 1 describing the size of | | the ISO image plus the backup GPT, padded up to | | the next full MiB. 462 - 477 | mbr_entry2 | Entry 2 describing the EFI VFAT boot image. 478 - 493 | mbr_entry3 | Entry 3 describing the HFS+ boot image. | | 512 - 1023 | gpt_head | GPT header describing the GPT partition array. 1024 - 2047 | unused | 2048 - 4095 | apm_entry1 | APM entry 1 describing APM entries 1 to 3. 4096 - 6143 | apm_entry2 | APM entry 2 describing the EFI VFAT boot image. 6144 - 8195 | apm_entry3 | APM entry 3 describing the HFS+ boot image. 8192 - 8319 | gpt_entry1 | GPT partition entry 1 for the ISO image size. 8320 - 8447 | gpt_entry2 | GPT partition entry 2 for EFI VFAT boot image, 8448 - 8575 | gpt_entry3 | GPT partition entry 3 for the HFS+ boot image. 8576 - 24575 | gtp_empty | Empty GPT partition entries 4 to 128. 24576 - 32767 | unused | 32768 - 34815 | iso_pvd | ISO 9660 superblock 34816 - 36863 | el_torito | EL Torito boot block pointing to a boot catalog | | with entries for the MBR boot image (platform id | | 0x00), and for the two other boot images | | (platform id 0xef) -------------- | ---------- | ------------------------------------------------- For the purpose of booting, there may be two EFI boot image files in the ISO image. A VFAT image and a HFS+ image. The content of both is not in the scope of this document. These boot images get announced by EL Torito boot catalog entries with Platform Id 0xef. Newer SYSLINUX MBR templates begin by 32 bytes of machine code which are intentionally non-essential: 33 ed 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 They may be overwritten by other bytes which must not produce errors or undesirable side effects when executed as x86 machine code. The following 32 bytes from block 0 of an Apple Partition Map (APM) are such harmless code. They stem from Fedora-LiveCD.iso by Matthew Garrett: 45 52 08 00 00 00 90 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 They do not depend on any properties of the ISO image or the information that is described in the following text. The layout of Block0 is constant: Byte Range | Value | Meaning (all numbers are stored big endian) ---------- | ---------- | ---------------------------------------------------- 0 - 1 | sig | Signature 0x45 = 'E' , 0x52 = 'R' 2 - 3 | block_size | 0x0800 = 2048 4 - 7 | block_count| 0x9090 = 37008 8 - 9 | dev_type | obscure: "device type" = 0 10 - 11 | dev_id | obscure: "device id" = 0 12 - 15 | drv_data | obscure: "driver data" = 0 16 - 17 | drv_count | obscure: "driver descriptor count" = 0 18 - 81 | drv_map | obscure: "driver descriptor table" | | with 8 entries of 16 bytes each | | first 14 bytes are 0 ---------- | ---------- | ---------------------------------------------------- The block_count of 0x9090 is pure fantasy. It shall only mark the disc as not empty. The data block ranges of the two EFI boot images get described by MBR partition entries 2 and 3, which thus claim blocks inside of partition 1. This partition nesting is acceptable to some EFI implementations only if the type of partition 1 is 0x00. The MBR partition entry number 1 is 80 00 01 00 00 3f a0 89 00 00 00 00 00 50 14 00 It marks the partition as bootable, starting at block 0, with a size that is the smallest full MiB not smaller than the ISO image size + 18 KiB. (The 18 KiB are needed for the GPT backup.) The ISO image has a size of 332362 blocks of 2K = 1329448 * 512 = 649.14 MiB. The partition size is 0x145000 = 1331200 * 512 = 650 MiB. Start C/H/S = 0/0/1, type is 0x0 ("Empty"), end C/H/S is 649/63/32. Partition 2: 00 fe ff ff ef fe ff ff a4 00 00 00 70 04 00 00 Start block is 0xa4 = 164 * 512 = 41 * 2048. The VFAT image file. Partition size is 0x0470 = 1136 * 512. The size of that file. Start C/H/S = 1023/254/63, type 0xef (fdisk says: "EFI (FAT-12/16/"), end C/H/S = 1023/254/63. Partition 3: 00 fe ff ff 00 fe ff ff 44 05 00 00 c0 08 00 00 Start block is 0x0544 = 1348 * 512 = 337 * 2048. The HFS+ image file. Partition size is 0x08c0 = 2240 * 512. The size of that file. Start C/H/S = 1023/254/63, type 0x00 ("Empty"), end C/H/S = 1023/254/63. The second 512-block in the ISO image is the GPT header. 45 46 49 20 50 41 52 54 00 00 01 00 5c 00 00 00 E F I P A R T 13 db 71 5d 00 00 00 00 01 00 00 00 00 00 00 00 fe 4f 14 00 00 00 00 00 30 00 00 00 00 00 00 00 de 4f 14 00 00 00 00 00 73 23 c8 79 19 e6 97 4d 95 17 69 30 c5 38 e2 99 10 00 00 00 00 00 00 00 80 00 00 00 80 00 00 00 5b 6b 8a 65 Byte Range | Value | Meaning (little endian numbers, LBA unit is 512 byte) ---------- | ---------- | ---------------------------------------------------- 12 - 15 | head_size | Header size = 0x5c = 92 24 - 31 | curr_lba | Location of this header block = 0x1 32 - 39 | backup_lba | Location of header backup block = 0x144ffe = 1331198 | | This is 1 KiB before the end of MBR partition 1 | | (but should be 512 bytes). | | (Potential isohybrid.c bug #1: | | Backup GPT is dislocated by 512 bytes.) 40 - 47 | first_lba | First usable LBA for partitions = 0x30 = 48 48 - 55 | last_lba | Last usable LBA for partitions = 0x144fde = 1331166 56 - 71 | guid | Disk GUID | | Random, produced by uuid_generate(), | | 32,16,16 byte swapped 72 - 79 | part_start | Partition entries start LBA = 0x10 = 16 = byte 0x2000 | | (This is unusual. It leaves room for the Apple | | partition map entries.) 80 - 83 | entry_count| Number of partition entries 0x80 = 128 84 - 87 | entry_size | Size of a partition entry = 0x80 = 128 ---------- | ---------- | ---------------------------------------------------- Because the block size was announced as 2048, the first Apple partition map entry is located at byte 0x800 = 2048. It describes the partition map itself: 50 4d 00 00 00 00 00 03 00 00 00 01 00 00 00 10 P M 41 70 70 6c 65 00 00 00 00 00 00 00 00 00 00 00 A p p l e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 70 70 6c 65 5f 70 61 72 74 69 74 69 6f 6e 5f A p p l e _ p a r t i t i o n _ 6d 61 70 00 00 00 00 00 00 00 00 00 00 00 00 00 m a p 00 00 00 00 00 00 00 0a 00 00 00 03 00 00 00 00 Byte Range | Value | Meaning (all numbers are stored big endian) ---------- | ---------- | ---------------------------------------------------- 4 - 7 | map_entries| "number of partition entries" = 3 8 - 11 | start_block| "physical block start of partition" = 1 12 - 15 | block_count| "physical block count of partition" = 16 | | (Potential isohybrid.c bug #2: | | The value of 16 claims the ISO 9660 PVD as part of | | the partition table. It should be 4 instead.) 16 - 47 | name | Partition name = "Apple" 48 - 79 | type | Type string = "Apple_partition_map" 80 - 83 | lb_start | Logical block start = 0 84 - 87 | lb_count | Logical block count = 10 88 - 91 | flags | Status flags = 3 (valid, allocated) 92 - 95 | boot_block | Logical start block number of boot code = 0 96 - 99 | boot_bytes | Number of bytes in boot code = 0 100 - 119 | | More boot code stuff = 0 120 - 135 | processor | "processor type" = 0 136 - 511 | reserved | ---------- | ---------- | ---------------------------------------------------- (Potential isohybrid.c bug #2: Apple partition map entries bear the block count for blocks of 512 bytes whereas Apple Block0 announces blocks of 2048 bytes.) The next Apple partition map entry is at byte 0x1000 = 4096: 50 4d 00 00 00 00 00 03 00 00 00 29 00 00 04 70 45 46 49 00 00 00 00 00 00 00 00 00 00 00 00 00 E F I 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 70 70 6c 65 5f 48 46 53 00 00 00 00 00 00 00 A p p l e _ H F S 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 70 00 00 00 33 00 00 00 00 3 partitions, start block is 0x29 = 41, block count is 0x0470 = 1136 (should be 284), name is "EFI", type is "Apple_HFS" (although this is a FAT image), logical block start = 0, lb_count = 1136 (should be 284), flags = 0x33 : valid, allocated, readable, writable. This points to file /isolinux/efiboot.img in the ISO image. (Potential isohybrid.c bug #2: Apple partition map entries bear the block count for blocks of 512 bytes whereas Apple Block0 announces blocks of 2048 bytes.) At byte 0x1800 = 6144, there is Apple partition map entry 3: 50 4d 00 00 00 00 00 03 00 00 01 51 00 00 08 c0 45 46 49 00 00 00 00 00 00 00 00 00 00 00 00 00 E F I 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 70 70 6c 65 5f 48 46 53 00 00 00 00 00 00 00 A p p l e _ H F S 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 c0 00 00 00 33 00 00 00 00 3 partitions, start block is 0x0151 = 337 (LBA of /isolinux/macboot.img), block count = 0x08c0 = 2240 (should be 560), name is "EFI", type is "Apple_HFS", logical block start = 0, lb_count = 2240 (should be 560), flags = 0x33 : valid, allocated, readable, writable. (Potential isohybrid.c bug #2: Apple partition map entries bear the block count for blocks of 512 bytes whereas Apple Block0 announces blocks of 2048 bytes.) At byte 0x2000 = 8192 begins the GPT partition array. It ends at byte 0x4000 = 16384. a2 a0 d0 eb e5 b9 33 44 87 c0 68 b6 b7 26 99 c7 a1 87 a1 ba 4d 2c 27 45 ae 05 cf ab a6 fa 87 c1 00 00 00 00 00 00 00 00 28 49 14 00 00 00 00 00 00 00 00 00 00 00 00 00 49 53 4f 48 79 62 72 69 I S O H y b r i 64 20 49 53 4f 00 49 53 4f 48 79 62 72 69 64 00 d I S O I S O H y b r i d 41 70 70 6c 00 00 00 00 00 00 00 00 00 00 00 00 A p p l 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Byte Range | Value | Meaning (numbers are stored little endian) ---------- | ---------- | ---------------------------------------------------- 0 - 15 | type_guid | Partition type GUID = Basic data partition | | Wikipedia: "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7" | | (Note: The first three words are shown byte swapped) 16 - 31 | part_guid | Unique partition GUID | | Random, produced by uuid_generate() | | 32,16,16 byte swapped 32 - 39 | start_lba | First LBA = 0 40 - 47 | end_lba | Last LBA (inclusive) = 0x144828 = 1329448 | | This is the ISO image size in blocks of 512. | | (Potential isohybrid.c bug #3: | | The end_lba in the first GPT entry should be 1 less | | than the count of 512 byte blocks of the ISO image.) 48 - 55 | flags | Attribute flags = 0 56 - 127 | name | Wikipedia says UTF-16LE. | | (Potential isohybrid.c bug #4: | | The name in Fedora-LiveCD.iso is 8 bit and result | | of faulty memory operation on a text constant.) ---------- | ---------- | ---------------------------------------------------- Next entry is at 0x2800 = 10240: a2 a0 d0 eb e5 b9 33 44 87 c0 68 b6 b7 26 99 c7 c8 de c8 1f fb f0 51 40 8c 8a d2 f6 b1 46 16 dc a4 00 00 00 00 00 00 00 13 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 49 53 4f 48 79 62 72 69 I S O H y b r i 64 00 41 70 70 6c 65 00 41 70 70 6c 00 00 00 00 d A p p l e A p p l 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Partition type GUID : "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7" = Basic data Start at block 0xa4 = 164 * 512 = 41 * 2048. The VFAT image file. Last block is 0x0513 = 1299 = 164 + 1135. This end is correct. (Potential isohybrid.c bug #4: Wrong character set and incidential bytes in GPT partition name.) (Potential isohybrid.c bug #5: The EFI System Partition should have type GUID : "C12A7328-F81F-11D2-BA4B-00A0C93EC93B") Next entry at byte 0x02100 = 8448: 00 53 46 48 00 00 aa 11 aa 11 00 30 65 43 ec ac c8 de c8 1f fb f0 51 40 8c 8a d2 f6 b1 46 16 dc 44 05 00 00 00 00 00 00 03 0e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 49 53 4f 48 79 62 72 69 I S O H y b r i 64 00 41 70 70 6c 65 00 41 70 70 6c 00 00 00 00 d A p p l e A p p l 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Partition type GUID : "48465300-0000-11AA-AA11-00306543ECAC" = HFS+ Start block is 0x0544 = 1348 * 512 = 337 * 2048. The HFS+ image file. Last block is 0x0e03 = 3587 = 1348 + 2239. Correct. (Potential isohybrid.c bug #4: Wrong character set and incidential bytes in GPT partition name.) The rest of the System Area is 0 up to the Primary Volume Descriptor at block 16. The ISO image file gets padded up to full MiB with sufficient room for the GPT backup which is stored near the very end of the image file. There is need for at least 16.5 KiB, which effectively occupy 18 KiB. The backup partition array is stored 17 KiB before the end of MBR partition 1, which is also the end of the image file. (Potential isohybrid.c bug #1: Wikipedia suggests "LBA -33" counted from end. This would be 16.5 KiB before end.) The GPT header is stored 1 KiB before the end of MBR partition 1. (Potential isohybrid.c bug #1: Wikipedia suggests "LBA -1" counted from end. This would be 512 bytes.) 45 46 49 20 50 41 52 54 00 00 01 00 5c 00 00 00 E F I P A R T f6 61 10 1c 00 00 00 00 fe 4f 14 00 00 00 00 00 01 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 de 4f 14 00 00 00 00 00 73 23 c8 79 19 e6 97 4d 95 17 69 30 c5 38 e2 99 de 4f 14 00 00 00 00 00 80 00 00 00 80 00 00 00 5b 6b 8a 65 It differs by its own CRC and by some of its parameters: Own address is 0x144ffe. The backup lba is 0x01 = Primary GPT header. Partition entries start is 0x144fde (Potential isohybrid.c bug #1: This overlaps with the last usable LBA. The backup GPT must move up by 512 bytes.) ------------------------------------------------------------------------------ GRUB2 grub-mkrescue MBR Sources: Mailing list conversations with Vladimir Serbinenko. The MBR file that is used with older versions of GRUB2 script grub-mkrescue needs only a partition table entry which describes the image size. Newer versions get patched by the block address of the content of the first El Torito boot image. See grub-mkrescue use of xorrisofs option --grub2-mbr. Byte Range | Value | Meaning ---------- | ---------- | ---------------------------------------------------- 0 - 431 | = opaque = | GRUB2 machine code provided by MBR template | | 432 - 439 | bootimg_adr| With newer versions of grub-mkrescue: | | Block address of the start of the boot image file | | content plus 4. Block size is 512. | | 64 bit Little-endian. | | 440 - 445 | = opaque = | provided by MBR template | | 446 - 509 | ========== | Partition table | | 446 - 461 | part_entry | Partition table entry 1 describing ISO image size | | Peculiar is the start offset of 1 block. | | This prevents mounting of the partition. | | See above for partition table entry format. | | 462 - 509 | 0 | Unused partition entries 2 to 4 510 - 511 | 0xaa55 | MBR signature ---------- | ---------- | ---------------------------------------------------- Vladimir Serbinenko about the partition table entry: "Currently we use first and not last entry. You need to: 1) Zero-fill 446-510 2) Put 0x55, 0xAA into 510-512 3) Put 0x80 (for bootable partition), 0, 2, 0 (C/H/S of the start), 0xcd (partition type), [3 bytes of C/H/S end], 0x01, 0x00, 0x00, 0x00 (LBA start in little endian), [LBA end in little endian] at 446-462 " ------------------------------------------------------------------------------ >>> Mac and/or PowerPC bootable GRUB2 image with HFS+/FAT, APM, EFI GPT partition, PreP MBR partition, mountable FAT partition >>> ? With PC-BIOS MBR x86 Code ? This storage layout was mainly defined by Vladimir Serbinenko. It relies much on the embedded HFS+/FAT filesystem for which he provided the code to libisofs. Start Blocks (2 KiB): 0 System Area 16 Volume Descriptors TREE ISO-RR tree, Joliet tree, other trees and meta data, except HFS+/FAT EFI EFI boot image partition (optional) PREP Prep image partition (optional) HFAT HFS+/FAT metadata (optional) DATA Data file content (including El Torito boot images) HFSB HFS superblock backup (if HFS+/FAT metadata) TAIL Further tails and paddings (optional) GPTB GPT backup (if GPT in System Area) END End of ISO image System Area may contain simultaneously: MBR (x86 boot code must leave room for 8 bytes mock-up of APM Block0) APM GPT MBR Partitions: 0xee from 0 to PREP-1, protective partition, announcing presence of GPT 0x41 from PREP to HFAT-1, PreP partition 0x0c from HFAT to END-1, FAT partition, bootable bit on 0x00 Empty partition GPT Partitions: The primary GPT itself covers the System Area. Basic Data from 16 to EFI-1, protects first part of ISO image EFI System from EFI to PREP-1, offers EFI image for booting Basic Data from PREP to HFAT-1, protects PreP partition HFS+ from HFAT to TAIL-1, offers HFS+ for mounting Basic Data from TAIL to GPTB-1, protects rest of ISO image (if there is) APM Partitions: The range from end of APM to end of System Area stays unprotected. (The primary GPT might sit there.) Apple_partition_map from 1 to 3 or 4, covers the APM itself ISO9660_data from 16 to HFAT-1, covers first part of ISO image Apple_HFS from HFAT to GPTB-1, offers HFS+ for boot and mount ISO9660_data from GPTB to END-1, covers rest of ISO image (might be omitted if empty) El Torito: Boot image for 80x86 PC-BIOS Boot image for EFI (usually the same file as the partition EFI to PREP-1). If optional components are not present, then their addresses coincide with the address of the next component that is present. E.g. HFAT == DATA if no HFS+/FAT filesystem is present. If no FAT filesystem is present within HFS+/FAT, then the type of the last partition is 0xcd. If neither EFI, nor PreP, nor FAT within HFS+/FAT, are present, then there is only one partition. It has type 0xee, if GPT is present in the System Area. It has type 0xcd and offset 1*512, if no GPT is present. Involved -as mkisofs options: -hfsplus -fat -efi-boot-part DISKFILE -prep-boot-part DISKFILE >>> What boots by what ? ------------------------------------------------------------------------------ Description of libisofs MD5 checksumming by Thomas Schmitt - mailto:scdbackup@gmx.net Libburnia project - mailto:libburn-hackers@pykix.org 26 Aug 2009 MD5 is a 128 bit message digest with a very low probability to be the same for any pair of differing data files. It is described in RFC 1321. and can be computed e.g. by program md5sum. libisofs can equip its images with MD5 checksums for superblock, directory tree, the whole session, and for each single data file. See libisofs.h, iso_write_opts_set_record_md5(). The data file checksums get loaded together with the directory tree if this is enabled by iso_read_opts_set_no_md5(). Loaded checksums can be inquired by iso_image_get_session_md5() and iso_file_get_md5(). Stream recognizable checksum tags occupy exactly one block each. They can be detected by submitting a block to iso_util_decode_md5_tag(). libisofs has own MD5 computation functions: iso_md5_start(), iso_md5_compute(), iso_md5_clone(), iso_md5_end(), iso_md5_match() Representation in the Image There may be several stream recognizable checksum tags and a compact array of MD5 items at the end of the session. The latter allows to quickly load many file checksums from media with slow random access. The Checksum Array Location and layout of the checksum array is recorded as AAIP attribute "isofs.ca" of the root node. See doc/susp_aaip_2_0.txt for a general description of AAIP and doc/susp_aaip_isofs_names.txt for the layout of "isofs.ca". The single data files hold an index to their MD5 checksum in individual AAIP attributes "isofs.cx". Index I means: array base address + 16 * I. If there are N checksummed data files then the array consists of N + 2 entries with 16 bytes each. Entry number 0 holds a session checksum which covers the range from the session start block up to (but not including) the start block of the checksum area. This range is described by attribute "isofs.ca" of the root node. Entries 1 to N hold the checksums of individual data files. Entry number N + 1 holds the MD5 checksum of entries 0 to N. The Checksum Tags Because the inquiry of AAIP attributes demands loading of the image tree, there are also checksum tags which can be detected on the fly when reading and checksumming the session from its start point as learned from a media table-of-content. The superblock checksum tag is written after the ECMA-119 volume descriptors. The tree checksum tag is written after the ECMA-119 directory entries. The session checksum tag is written after all payload including the checksum array. (Then follows eventual padding.) The tags are single lines of printable text at the very beginning of a block of 2048 bytes. They have the following format: Tag_id pos=# range_start=# range_size=# [session_start|next=#] md5=# self=#\n Tag_id distinguishes the following tag types "libisofs_rlsb32_checksum_tag_v1" Relocated 64 kB superblock tag "libisofs_sb_checksum_tag_v1" Superblock tag "libisofs_tree_checksum_tag_v1" Directory tree tag "libisofs_checksum_tag_v1" Session tag A relocated superblock may appear at LBA 0 of an image which was produced for being stored in a disk file or on overwritable media (e.g. DVD+RW, BD-RE). Typically there is a first session recorded with a superblock at LBA 32 and the next session may follow shortly after its session tag. (Typically at the next block address which is divisible by 32.) Normally no session starts after the address given by parameter session_start=. Session oriented media like CD-R[W], DVD+R, BD-R will have no relocated superblock but rather bear a table-of-content on media level (to be inquired by MMC commands). Example: A relocated superblock which points to the last session. Then the first session which starts at Logical Block Address 32. The following sessions have the same structure as the first one. LBA 0: <... ECMA-119 System Area and Volume Descriptors ...> LBA 18: libisofs_rlsb32_checksum_tag_v1 pos=18 range_start=0 range_size=18 session_start=311936 md5=6fd252d5b1db52b3c5193447081820e4 self=526f7a3c7fefce09754275c6b924b6d9 <... padding up to LBA 32 ...> LBA 32: <... First Session: ECMA-119 System Area and Volume Descriptors ...> libisofs_sb_checksum_tag_v1 pos=50 range_start=32 range_size=18 md5=17471035f1360a69eedbd1d0c67a6aa2 self=52d602210883eeababfc9cd287e28682 <... ECMA-119 Directory Entries (the tree of file names) ...> LBA 334: libisofs_tree_checksum_tag_v1 pos=334 range_start=32 range_size=302 md5=41acd50285339be5318decce39834a45 self=fe100c338c8f9a494a5432b5bfe6bf3c <... Data file payload and checksum array ...> LBA 81554: libisofs_checksum_tag_v1 pos=81554 range_start=32 range_size=81522 md5=8adb404bdf7f5c0a078873bb129ee5b9 self=57c2c2192822b658240d62cbc88270cb <... more sessions ...> LBA 311936: <... Last Session: ECMA-119 System Area and Volume Descriptors ...> LBA 311954: libisofs_sb_checksum_tag_v1 pos=311954 range_start=311936 range_size=18 next=312286 md5=7f1586e02ac962432dc859a4ae166027 self=2c5fce263cd0ca6984699060f6253e62 <... Last Session: tree, tree checksum tag, data payload, session tag ...> There are several tag parameters. Addresses are given as decimal numbers, MD5 checksums as strings of 32 hex digits. pos= gives the block address where the tag supposes itself to be stored. If this does not match the block address where the tag is found then this either indicates that the tag is payload of the image or that the image has been relocated. (The latter makes the image unusable.) range_start= The block address where the session is supposed to start. If this does not match the session start on media then the volume descriptors of the image have been relocated. (This can happen with overwritable media. If checksumming started at LBA 0 and finds range_start=32, then one has to restart checksumming at LBA 32. See libburn/doc/cookbook.txt "ISO 9660 multi-session emulation on overwritable media" for background information.) range_size= The number of blocks beginning at range_start which are covered by the checksum of the tag. Only with superblock tag and tree tag: next= The block address where the next tag is supposed to be found. This is to avoid the small possibility that a checksum tag with matching position is part of a directory entry or data file. The superblock tag is quite uniquely placed directly after the ECMA-119 Volume Descriptor Set Terminator where no such cleartext is supposed to reside by accident. Only with relocated 64 kB superblock tag: session_start= The start block address (System Area) of the session to which the relocated superblock points. md5= The checksum payload of the tag as lower case hex digits. self= The MD5 checksum of the tag itself up to and including the last hex digit of parameter "md5=". The newline character at the end is mandatory. After that newline there may follow more lines. Their meaning is not necessarily described in this document. One such line type is the scdbackup checksum tag, an ancestor of libisofs tags which is suitable only for single session images which begin at LBA 0. It bears a checksum record which by its MD5 covers all bytes from LBA 0 up to the newline character preceding the scdbackup tag. See scdbackup/README appendix VERIFY for details. ------------------------------------------------------------------------------- Usage at Read Time Checking Before Image Tree Loading In order to check for a trustworthy loadable image tree, read the first 32 blocks from to the session start and look in block 16 to 32 for a superblock checksum tag by iso_util_decode_md5_tag(block, &tag_type, &pos, &range_start, &range_size, &next_tag, md5, 0); If a tag of type 2 or 4 appears and has plausible parameters, then check whether its MD5 matches the MD5 of the data blocks which were read before. With tag type 2: Keep the original MD5 context of the data blocks and clone one for obtaining the MD5 bytes. If the MD5s match, then compute the checksum block and all following ones into the kept MD5 context and go on with reading and computing for the tree checksum tag. This will be found at block address next_tag, verified and parsed by: iso_util_decode_md5_tag(block, &tag_type, &pos, &range_start, &range_size, &next_tag, md5, 3); Again, if the parameters match the reading state, the MD5 must match the MD5 computed from the data blocks which were before. If so, then the tree is ok and safe to be loaded by iso_image_import(). With tag type 4: End the MD5 context and start a new context for the session which you will read next. Then look for the actual session by starting to read at the address given by parameter session_start= which is returned by iso_util_decode_md5_tag() as next_tag. Go on by looking for tag type 2 and follow above prescription. Checking the Data Part of the Session In order to check the trustworthiness of a whole session, continue reading and checksumming after the tree was verified. Read and checksum the blocks. When reaching block address next_tag (from the tree tag) submit this block to iso_util_decode_md5_tag(block, &tag_type, &pos, &range_start, &range_size, &next_tag, md5, 1); If this returns 1, then check whether the returned parameters pos, range_start, and range_size match the state of block reading, and whether the returned bytes in parameter md5 match the MD5 computed from the data blocks which were read before the tag block. Checking All Sessions If the media is sequentially recordable, obtain a table of content and check the first track of each session as prescribed above in Checking Before Image Tree Loading and in Checking the Data Part of the Session. With disk files or overwritable media, look for a relocated superblock tag but do not hop to address next_tag (given by session_start=). Instead look at LBA 32 for the first session and check it as prescribed above. After reaching its end, round up the read address to the next multiple of 32 and check whether it is smaller than session_start= from the super block. If so, expect another session to start there. Checking Single Files in a Loaded Image An image may consist of many sessions wherein many data blocks may not belong to files in the directory tree of the most recent session. Checking this tree and all its data files can ensure that all actually valid data in the image are trustworthy. This will leave out the trees of the older sessions and the obsolete data blocks of overwritten or deleted files. Once the image has been loaded, you can obtain MD5 sums from IsoNode objects which fulfill iso_node_get_type(node) == LIBISO_FILE The recorded checksum can be obtained by iso_file_get_md5(image, (IsoFile *) node, md5, 0); For accessing the file data in the loaded image use iso_file_get_stream((IsoFile *) node); to get the data stream of the object. The checksums cover the data content as it was actually written into the ISO image stream, not necessarily as it was on hard disk before or afterwards. This implies that content filtered files bear the MD5 of the filtered data and not of the original files on disk. When checkreading, one has to avoid any reverse filtering. Dig out the stream which directly reads image data by calling iso_stream_get_input_stream() until it returns NULL and use iso_stream_get_size() rather than iso_file_get_size(). Now you may call iso_stream_open(), iso_stream_read(), iso_stream_close() for reading file content from the loaded image. Session Check in a Loaded Image iso_image_get_session_md5() gives start LBA and session payload size as of "isofs.ca" and the session checksum as of the checksum array. For reading you may use the IsoDataSource object which you submitted to iso_image_import() when reading the image. If this source is associated to a libburn drive, then libburn function burn_read_data() can read directly from it. ------------------------------------------------------------------------------- scdbackup Checksum Tags The session checksum tag does not occupy its whole block. So there is room to store a scdbackup stream checksum tag, which is an ancestor format of the tags described here. This feature allows scdbackup to omit its own checksum filter if using xorriso as ISO 9660 formatter program. Such a tag makes only sense if the session begins at LBA 0. See scdbackup-*/README, appendix VERIFY for a specification. Example of a scdbackup checksum tag: scdbackup_checksum_tag_v0.1 2456606865 61 2_2 B00109.143415 2456606865 485bbef110870c45754d7adcc844a72c c2355d5ea3c94d792ff5893dfe0d6d7b The tag is located at byte position 2456606865, contains 61 bytes of scdbackup checksum record (the next four words): Name of the backup volume is "2_2". Written in year B0 = 2010 (A9 = 2009, B1 = 2011), January (01), 9th (09), 14:34:15 local time. The size of the volume is 2456606865 bytes, which have a MD5 sum of 485bbef110870c45754d7adcc844a72c. The checksum of "2_2 B00109.143415 2456606865 485bbef110870c45754d7adcc844a72c" is c2355d5ea3c94d792ff5893dfe0d6d7b. ------------------------------------------------------------------------------- This text is under Copyright (c) 2009 - 2010 Thomas Schmitt It shall only be modified in sync with libisofs and other software which makes use of libisofs checksums. Please mail change requests to mailing list or to the copyright holder in private. Only if you cannot reach the copyright holder for at least one month it is permissible to modify this text under the same license as the affected copy of libisofs. If you do so, you commit yourself to taking reasonable effort to stay in sync with the other interested users of this text. FEATURES ======== Contents: 2.0 Operations on image tree 2.1 ECMA-119 2.2 Rock Ridge 2.3 Joliet 2.4 El-Torito 2.5 UDF 2.6 HFS/HFS+ 2.7 Others =============================================================================== 2.0 Operations on image tree ----------------------------- Basic: - We HAVE TO Support addition of directories - From filesystem - From filesystem recursively - New on image - We HAVE TO support addition of files - From local filesystem - From previous/ms images - We HAVE TO support addition of other POSIX file types - From filesystem - New on image - Types: symlinks, block/char devices, fifos, sockets... - We HAVE TO support modification of file names on image - We HAVE TO support modification of POSIX attributes: - Uid/Gid - Permissions (we DON'T HAVE TO support full mode modification, as we don't want a dir to be changed to a reg file!!) - Timestamps - We HAVE TO support deletion of nodes. - We HAVE TO support iteration of directory tree. - We WANT TO support direct getting (without iteration) of the number of nodes in a directory. Extras: - We WANT TO support on-the-fly modification of file contents, to allow things like compression and encryption. Notes: many operations will need RR extensions to be actually reflected on the image. =============================================================================== 2.1 ECMA-119 ------------ Support for ECMA-119 (ISO-9660) specification. 2.1.1 Creation -------------- We HAVE TO support creation of new images. General: - We HAVE TO support single volume images - We DON'T NEED TO support multiple volume images. It seems multiple volume images are not used. - We HAVE TO support bootable volumes (see 2.4 in this doc) Conformance: - We HAVE TO support Level 1 restrictions (ECMA-119 10.1) - We HAVE TO support Level 2 restrictions (ECMA-119 10.2) Single Section files have a theoric size limit of 4GB (data length is a 32-bit number, see ECMA-119 9.1.4). However I think I have read that only files up to 2GB are supported. - We MAY support full Level 3 (ECMA-119 10.3) Multiple file sections are useful to support files higher than level 2 limit. However, it seems it's a feature not supported in most O.S. nowadays, so it's not very useful. - We DON'T WANT TO support files recording in interleaved mode (ECMA-119 6.4.3) It seems a feature that it's not used. - We DON'T WANT TO support associated files (ECMA-119 6.5.4) What is that? Is it used? - We DON'T WANT TO support Volume Partitions (ECMA-119 8.6) What is that? Is it used? - We DON'T WANT TO support extended attribute records (ECMA-119 9.5) It seems an unused feature. RR is a better alternative. - We DON'T NEED TO support file versions other than 1. Restrictions: - We HAVE TO provide a way to relax iso restrictions related to filenames, allowing: - Higher filename length, up to 37 chars (ECMA-119 7.5.1/7.6.3) - Omit version number (ECMA-119 7.5.1) - Directory hierarchy deeper than 8 levels / 255 path length (ECMA-119 6.8.2.1) - More characters in filenames, not only d-characters 2.2.2 Reading ------------- General - We HAVE TO support the reading of iso images - We DON'T NEED TO support reading of features we don't support in creation (see 2.2.1 above) - We HAVE TO support reading arbitray file contents inside image 2.2.3 Modification/growing -------------------------- General - We HAVE TO support creation of new images from the contents of an existing image - We HAVE TO support multissession images - We HAVE TO support growing of images =============================================================================== 2.2 Rock Ridge -------------- - We HAVE TO support ALL Rock Ridge features, with these exceptions: - We DON'T NEED TO support SF System User Entry (RRIP 4.1.7), used to encode sparse files. - We MIGHT support BACKUP timestamp (RRIP 4.1.6) - We HAVE TO support any charset in RR filenames, and not only POSIX portable filename character set (RRIP 3.4.1). Namely, UTF-8 SHOULD BE the default for RR filenames. - We MIGHT support Linux specific ZF extension, to allow transparent compression. =============================================================================== 2.3 Joliet ---------- - We HAVE TO support ALL Joliet features, with these exceptions: - We DON'T KNOW what to do with UCS-2 conformance level 1 and 2 (scape sequences '%\@' and '%\C'). What's this??????? - We DON'T KNOW what to do with CD-XA extensions. =============================================================================== 2.4 El-Torito ------------- - We HAVE TO El-Torito standard with a single boot image. - We MAY support multiple boot images and boot entry selection. - El Torito standard is not very clear about how to do that. - We HAVE TO support both emulation and not emulation mode. - We HAVE TO support 80x86 platform. We MAY support Power PC and Mac platforms. - We HAVE TO provide improved support for isolinux boot images, namely patching features. - We HAVE TO support El-Torito in ms images. =============================================================================== 2.5 UDF ------- =============================================================================== 2.6 HFS/HFS+ ------------ =============================================================================== 2.7 Others ---------- - We HAVE TO support sorting of file contents on image - We HAVE TO support inode caching to prevent the same file to be written several times into the image - We DON'T NEED TO support TRANS.TBL files - We DON'T NEED TO support padding of images - Padding should be part of the burning process USE CASES FOR NG LIBISOFS ========================= 3.1 General Operations ====================== 3.1.1 Creation of a new image ----------------------------- Desc: Creation of a new ISO image from files on the local filesystem Phases: - User creates a new image context - User get the root (empty) of the image - User adds files to the image root (see 3.2.) - User sets the options for the the new image (extension to use...) - User gets a burn_source to write the image. - The burn_source can be used by libburn to write the new image. 3.1.2 Image growing (multisession) ---------------------------------- Desc: An existing image can be grown with new files. New content is added incrementally. Suitable for multisession. Growing support for overwritteable media. Phases: - Uses reads an existing image to get the image context. - User get the root of the image - User modifies the image tree (see 3.2.) - User sets the options for the the new image (extension to use...) A required option will be the nwa for the image. Optionally it can pass a pointer to a 64K buffer, that will be filled with suitable volume descriptors to be used with overwrieable media. - User gets a burn_source to write the image. - The burn_source can be used by libburn to write an image that should be appended to the previous image. 3.1.3 Image modification ------------------------ Desc: Creation of a new image from the contents of a previous image. Phases: - Uses reads an existing image to get the image context. - User get the root of the image - User modifies the image tree (see 3.2.) - User sets the options for the the new image (extension to use...) - User gets a burn_source to write the image. - The burn_source can be used by libburn to write the image to another device or file. 3.2 Tree operations =================== 3.2.1 Addition of contents -------------------------- All addition operations take a parent dir. The functions check that the node name is unique among all children. Image context options determine what to do if a file with such name already exist. 3.2.1.1 Directories -------------------- - Creation of new directories in image, given a parent dir. and a name. Attributes are initialized to default values - Addition of a dir from the filesystem, given a parent. Dir contents are not added. Name and other attributes taken from the dir on filesystem - Recursive addition of a dir, given a parent. Directory contents are recursivelly added to image. 3.2.1.2 Regular files ---------------------- - Addition of a local filesystem file. Name, attributes and contents to be written taken from the filesystem file. - Addition of files from the previous image. Files are automatically added to the tree when the image is read. Name and attrbs taken from previous image. When the image has no RR extensions, unavailable atts are initialized to default values. The contents are only written to img if we choose image modification. - Addition of filtered files. Name and atts taken from the local filesystem. A filter (see 3.3) is applied to the file contents before written to image. - Addition of splitted files. Like local filesystem files, but the file is splitted in several files on a given size. Suitable for big (> 2GB) files. Name of the splitted files automatically generated. 3.2.1.3 Symbolic links ---------------------- Simbolic links are only written to image if RR extensions are enabled. - Addition of a simbolic link from local filesystem. Name, atts and dest of a path are taken from the simbolic link. - Addition of new link on image to a path. Name and dest specified, the destination is specified as a path. Attributes initialized to default values. - Addition of new link on image to another node. Name and dest specified, the dest is set to a node previously added to image. When written, the destination path is computed as the relative path from the link to the destination node. Attributes initialized to default values. 3.2.1.4 Special files (block devices, fifos...) ----------------------------------------------- Special files are only written to image if RR extensions are enabled. - Addition of special files from filesystem. - Creation of new special files on image. 3.2.2 Modification of contents ------------------------------ 3.2.2.1 Deletion of nodes ------------------------- - Any node can be deleted. When a dir is remove, all its contents are also removed. 3.2.2.2 Move ------------ - Any node can be move to another dir.. 3.2.2.3 Rename -------------- - You can change the name of any node 3.2.2.4 Change of POSIX attributes ---------------------------------- - Following POSIX atts can be changed: owner (uid/gid), permissions, timestamps. 3.2.3 Bootable information -------------------------- - Addition of a boot image to a volume. - In most cases, the catalog and the boot image itself is added to the iso tree. - Alternatively, user can select to add a hidden images, i.e., images that don't appear in the iso tree. - Modification of boot image attributes: - bootable flag - load segment - load size - Automatic patching of isolinux images. User needs to set whether to apply this. - Reading of El-Torito info from multisession images. Modification of its attributes. - Removing of El-Torito images 3.2.4 Other operations ---------------------- 3.2.4.1 Set file sort weight ----------------------------- - Any file can have a sort weight, that will determine the order in which the files are written to image 3.2.4.2 Hidding of nodes ------------------------ - Files can be hidden in the RR or Joliet tree 3.3 Filters =========== [TODO] Support for: - compression filter - encryption filter - external process filter Index ===== 1. Overview 2. Features 3. Design 4. Implementation ‰PNG  IHDRÞÛƒµÏ{b9IDATxÚíÝO®Û¸º¨}O)X½ã~:g 7´Ê¸CØ…›þm'ÝH;»£Z™Å]¨Ô<òù¯DI$Eɲ-[¿ÄÞ.E¢(Jzù˜‹&W«åñ€±h7Ü6Â,í‚Ýu€vÄõÔ\ˆhG\/j.Ä´#® æ³ºà¿.¸ýªZÞnj:jNÍÔœšÔœš@Í©9@Í©9€šSs€šSs¨95¨95PsjPsj5§æ5§æjNÍjNÍ æÔ æÔ@Í©9j5§æ5§æjNÍPsñ@Í©9@Í©9€šSsÔœš¨95¨95Psj€šßDÍWî…` jNÍjNÍû¢ó?ÿ–$éÖ‰ƒšSs€šSsj.IsQóú¨95¨95§æ’4S5gç æÔ æÔœšKÒ\ÔœƒšSs€šSsj.IsQsvjNÍj>ˆÝfµÙ%ÿ“šK’t•š³sÌLÍ£ÍÜ~cÈzû>hÿW¶ž‡<›Y j‘Ï;Ss€šSsI’rjÎÎñ jll¾]<éÓŸµèœl»3¢æäCÇ¡5«êóÙ¹«CòTñ’UccYÏ믶I×g?µµs·A™/®Ö³½7Þ.ä`c¤n§¾ºèÝi\Q£`ñÛß§è’#;—^uìÖ÷WBìŒÑ£2÷=Wü×ÿ öéÞÊH9'½ä¼ùö½*Þás·ÒZ·²Ê¡[ÿÿü{÷iuɶóøMuûÔœk8ÌHÍ…–šOÛk¾—­†±+Èùýˇóû3~úó¼OµÃÏ‹P6O?*ccmc¾”ág-¬‘<û¾«ì¯î$…ûc#ÅhÔOãªÛÕ2èì±j/©ÖI÷‡üÇÿùß1î­ç‹øîkïíão—ÏÑ/ñ[¯ÿʰŽÞÛk>ü&–©9;×p æ",5Ÿ“šŽèUó_æÍïØÇùaûó{ÿ†vqç¨cwi¼ã63¢qx¾$üût9Y?‘j)®‡h¶…r0é‹(ŸÿâñßÙ‹Šf{èk6f‹Ý+c—0t@KùM,Vsv®á@ÍEXj>5/wš Ô<üáit HgGÁQAï·ºDž±%çü§Tó¾³÷¨yi…ì>]ÌµÝ žº¨N¶½5™¨´žz«hª›HÍ5^XÍ—Ä©ù²Ô|P_c¿šݾõöšgû§“½æéB¦FVôw¸–]xtŒÍ05ïVKßÙû{ÍË*ädä­.êäEEïÎ5ï¿•ÓªyñM¤æÔ\„¥æóWóºWu°šÇŒ5oÈS=8¸ùóÄsn e¬O9ª_ÍcC¢Ãµ Õ<9Öüx¢óïã_]rcÍG¨yy…œ¤üKgvy=SóÔ­ŒÖIz¬ùD7‘š5§æÔ|îjžÑ7^å0eGñ -S¶ÏXM-òöy[YZóí£‚ÂÇå¬:<-”‘’Ä*§Î¿;CK½q/‘é¿*Ä&é;{OOsa…¾E4·³O=ÐÒ¹•õ¼ì‘¡íéZ&¹‰Ô¨95§æO£æÒÒð©K–³J(5jNÍ©95—î¹”Ï1KÔ@͇³Û¬6;jNÍ©¹$Qs æcÏù¾=/”wë£aŸ·¶´vh´Z­·ïÇ£ZûÄŽ¢æÔœšK5¿[`@ÍŸAÍOÝÛçNîN_÷Q²º])öy‡z×ݦÚá`àÓ'jœôÚj9èÍjü °UÈöñSsj.IÔœš æƒ½YžÔ\’¨95§æ^^Í»rÜ«æ»æLvá©éüQcì<¨–ÎÈ™úTÑb¤®®õE¢3ö&ÈöGý—ðŸ×ÿõÿû é9žô²ãqk¼ÿŸšÏïg‹.4xñv‰šƒšXH¯y=ð$ÝkÞÕèÐUS²›ê_Ÿ¶×<Ÿ[‘šÇGì4ìVÊÄ'?;1DÍÛ󳌚¡¥;ö¦›íEÂÄ;ûô 顿CÕüÒ?Ý«æÕP“Bó¬æa·÷Ð^ó”šw®ÎD©95j>75ï ú\~h¯uЍyv¬ùÑ’Ï? Ì¨y-Ó‰±æƒÔ<8Wc¬y§01#¯">ÖœšKÔüÑí)jNÍsj zX-«—†š÷ÌÐR‹òÞ‰##CjÓ=L¨Ò7CË -Á$0Í.ðnaº}êÕU4gh fn1 E¢æÔ5Ÿ»šgF¹¼v‹BÍ%‰škt©9€çSó…@Í_¯±lÜ_Z&IÔœšÐk®×œšßún†[ôšK5§æ¨ù³ªù2#ìS4Ï]íý%ŠšK5j>k5_rçǬšçAýßã{Z&IÔ¨ù Õüúj¡æ#j>%â÷¹^N&IÔ¨ù¼|tªj¡æù/?·ëÿ¦æ’DÍ€š¿x§æƒÌ;%â³¾¿´L’¨9PsjþŒjþbs׬\ æÔ\„©ªžîìÒ&‘ü 8>ö*A£«á@ÍE؇uGoë›gB†–£/VÓ5ºÔ\„½‰ygD<[©9°XA×èRsÔ\„Åõjž@Àš‹°Ô˜û ²œŸ^¨95§æÔœšSsj |_t¢5§æÔœšótõÔœšSsjˆjNÍ©9€ØKDÐ=* æÔ\„¥æ á@ÍEXj ñN5j€šSs–š" ÜnÔ\„¥æ¯o£æ¨95a©9 °Àý@ÍEXj ,Ȭ–‡g5§æÔœšsyéBC]`Ìñ  æÔ\³AÍ1Çõ5×lPséwpQÃ<¨9jNÍ5Ô˜uÌYŽ¿dSâû@Í©¹KÍ׌9¯…¨9jNÍEXj5j®yFDŽë' 5ïîð;Å’î5Oõ¯órx÷©9Ps ¦È±±æ 1ËÞïs¯Òk^QïÅËAÍ©9Ps ‡Ø -oM©6­·ÛÍeûac}lä(€šSs æ@Í©¹›€šSsxX¼¥æÔ5§æpWu;QŠ{óКb¨5â+õóŒÆQÙdf/-,!5@Í©¹È`ÞjÞôQjžÙóðsçõº5Áhdz»_š?5ÀjNÍ©9€RóPГjü”ù='â±-ˆ¢]à…ùW%:.œŠ4¶>ÀysÛà©9jNÍEv³Sóô(—Ý&tÚè,ü}²”è5?,UóÖ º«`{'§ã¿×ß(4%¨ùmÔ09}ÂqóL?úns–ãØXóHŸz^ÍC3Omï¡NÍPóIÕ\<Ïíð\/øjž1Ýì—³œÇ»Àöš§ŠÔúA*5@Í©9<¡š§CqcåÚ±½æ•œO2Ö<¥æÑ²QsÔœšÀ“¨y_(>uE_ìvÔXóÚê÷DÇš×Ã]NŸN?ÛLyKá#cÍ©9jNÍà‰Ô¼,7gh‰ôj«ùÙ¸»ƒOZÇO[òåÕL,­ZºcاæF$ÔœšÀ+¨›Õ@©9jNÍ`¦V÷ÚSÐPsÔœš‹ì¼ûs¹^j€šSs‘€wŸš»‰¨9DvÞ}M jNÍ5ϼûÔ¨94ϼûšÔœš5§æ@ÍÔ\S€šSs æÔÜM@Í¬Žš æÔX5wPsˆì¼ûÔ5§æ";ï>5wPsˆì¼ûšÔœškžx÷©9PsñTó À»OÍPsjÔœš5O€šSsÔœš5§æn"jNÍ€ÕQs7 æÔ¨95wPsj.²ðîSs75×<ðîSs75§æ";ï>5j®y°°wihJPsjNÍÌ¿îHÒ…;€šSs æÔ¨9€škJPsjÝ@eÔœš@ÍPsj5@ÍEv€€š?™šï6›Ý5G¯Z‡_—ŸÈÔ5_†š·MºkÖ3‰e0*S‘€š¿†š¿oׇ%âÖÛmàÅç«ó–½2‡ÿ:ôñSõïëí{;ãjs+“ã—}ªã"çÔ€˜Ôüñj¾Ûœ­ø(Ég+ÞÅ»ëÌÁ…;Ò^e|Ê:ÐñßÁ§}êãæšIbPó‡«y-áï7v{±ScQZ*5ëA™5/tjNÍo%Psj~'5_òÄÔðV5ŸXÍa>@ÍAÍ€šÏBÍ©Æ$_cØ|RsÀ[©2—ÐÀ/ÜÑIÍ_-r®ví5 ÄÍèbÿù·$=WšÄΩùë¨ù•ծݢæ„8j.Iרùõ¯*55Ÿ¶ÚŸ·æ©9jNÍ%é±j~åÛJÍ_3LSsj€šSsIzˆš_óÂRsœšSs æ’4¥šÖjNÍ©95¼• æ’4±š‹Ô\sHÍ©9à­5—¤éÕ|Œ’QsÍáC%o·YmvK’€ g?¥æ æ æ’4·É¯œïœškÇIÞûv}zØNb}4ìó¶Ó–Ö̓V«õöýxTkŸØQ¯.×,EÍAÍAÍ%iþÊ^ÞLSsÍa¡äº·Ïܾî£du»Rìóõ®»MµÃÁÀ§OÕ8ér$ PÖ§UsK¸á©×ÏØ©¹$Í\Í [jj.‚—H^K‘»®ÜÚ²wìJãbÍ*zÔ;ív«»àë‰ Õ\ô”žwý<})jþíãjõq÷ë{ò‰úõç&òíïxÈ៲ǖ¤*“¹å™¤x×§qŸÃQ½5?“ ̪yIcMÍEð^5ïÊq¯šïš‘1<°µ±•mê¨";_æÏ@«{we$5—žzý<}jþóóúíúP¿º¢6­šO~ìTfy7C O4¡5ïõ[j.‚è5¯ž¤{Í»]bI÷š§ú×õš¸kÃF±Ssé©×ÏØ__ÍÏâU¬_)5ßûýé'O_¾^¶ݾë±ÜvŸ.Fß:}·l#9DŽžôWö_ƒë/Ÿë=/—s¾¢úÙ¬|H\rüº‚êjÿu"»s¾nOGU…yûü©ŠD…_rV½éÛDÍ©ùs©ùæ 1ËÞïs¯Òk^QïµÐ±æÓݵ~Yo| ‰ÒÓ®Ÿ'°¿¼šï>}Øþ2Œ$®æÿ;(Úù_ß¿|Ø|û~ÖOv³ÚïP¹æQ»¦heɱѓŽé\Ç­ËP·º¢æ…çOÔ7n$R]é^óáu{:ªÚ¾?°[¬š¬Þäm¢æÔüÕ<Òž—Rku{hJµi½Ýn.Ûëc#Gi·¦Só¼¯sDéy×ÏÃk«ymZ×÷š·…ïëöí¬¼ñÌà :qËŽ´ç_÷³£G¢åIfµ:×gáîüÇ×mscu…j>®z h¡æ/ªæº”^GÍõšK/°~^YÍ#¿ì,p¬R5oeÞ«ƒÔùUë5¿ŸšK5ÇUjžÛ6@ÍÇ-°ª·7“/Rópuˆøbò­é¢?ïé]Ü´:*úíÂï9Ó®¨0ô·7±"Zwü¶«U$Õh6ƒ'Òk’™×¨¹À¾l5ï>¾VÍ‹–hþÚ²-}Töó/ “F–9>Ö|¼š‡Ã3.Ÿk«ùu+*ÄòOŒ5O×^úvßvµ jNÍ©¹v‹š_5ÑØÜf¾¾f95âº)õ3Сñáš%’K=¬Ž³þg;ËëYAš3´äHÏs_R ˜Cf[}è.4qõ":ùwghÉ×^säzëÀ¬çP·ì=K™;,·ÖÏð™Ñú+!5·W§¢òˆ^of61jŽ÷šK’Õ@©95§æËSóä<\ñ‰´¢ó‚õä–P¬?Ã!ëíEþ–š™-œÅ,_ ©™¼¢•îk‰_oz61¨¹$QsjNÍ©ùÂzÍ ÿ¨—š¬ô^Á„b½cÍKÔ¼÷o‘½sôüJ);ýYQSõ`Ð95§æ”N¢æÔœšSsj^2Wù\W…³z Êðnj^Tììôg;ø©955—¨95§æÔœš·´ôÍÃ5̤ fõš¡š»oú3jNÍAÍ%jNÍAÍ©ùujžš‡+⸩yÁzrKX–avj°æö¢±æá,­¹½² _÷LÖ[ÔÔõRsj¾ìGÍ%jNÍ©95§æ­ù³ópgD‰vE'çËä֜ۤy`o†áÙ»Sƒuç+™¡¥šŒ%üÁe¾:ó«ôÌ8–*jôz©95§æãæ$ù+S^¼§»ºsÀ â¤Ù`©9¨95×Gu¿¹Ø%j.ÄÝ_ͧ˜dúÉN«+äÍr¾¦¥-GÍA͵[ÔœškÌö¥«yë/`ÑÅÚªÊüŬ³(A¸@f{H[l‰ƒKþ‡1i—²5~ Ò]0!þ'ÇäôÚ 8„jž¿ºÄj±¿&–hJ¸þò¹ou§Ø_Ë/V4£æ"85§æÔ\¢æBÜó÷š§×èûÖY” ø\ÿkf‰ƒÓçýÆ·¼]>‡;DLè. ð÷î½+¡ÆV“hËt~‰†Ãh½ó—“Ãu 'ÆM”¼çbE3j.‚SsjNÍ%jŽçW󴆓,Eò‰-JPý2ûËÞ¶cãF¢?ìn.›„*]€25Ï^ÝС&ñ%‚¨Ê׬w±¢5§æÔœšSs‰šã•Õ<hQØè¢õPæ|J…ë< SótÌ›¸ºh™{¦m-[¢a¤š÷]¬hFÍ©95§æÔ\¢æxq5¯U;ÚëÄC¯ù§`ÐEù:ƒ{Í ×NÎjqòê:eîé5/[¢a|¯ù¢ÒCÍAÍ©95—¨9^UÍcC#†ªytQ‚`$t½àÀu:ýÄÑ:ïûÕ<¾*Bv(yd5‰Ì(—haªJ˜r¬¹hFÍ©95§æÔ\¢æx5—(D‘’Âö¢©Z¬óÐ,FwÁ„θö¢1'ÝUz¯.ºšDvz¤0—³Ô3´ Zƒ¹übE3jNÍ©95ïæÌ j¾Ø÷ô/öcÇýwŒåM«BÍ©95×nýžÏ]û ˜;5§æw¿„åösSsjNͧ¹j}ŠÔÔ\`§æ’DÍ©95Ÿw3†Ví½†¸APsjNÍ%‰šSsjN͉ jNÍ%jNÍ©95§æðÐÆ«‚ÏIÔœšSs5¯+ ðvà.yr(DÍ©y›Ýy܇O›ÝyÓé5jŽ1.ÞûxPs‰šSó¨˜w-œš5Çàg Ú;NÍ%jNÍK¹LçÙê,Õü}»÷¡æ@ÍQ‹øè€šKÔœšw -<¢æ{1?š_O:5@ÍUæ¹x*7`iëPóÑj¾7óõöý÷<Ýœš5ïe ³lÁ* ÔüJ5ßm_‡¨¹ö^6ÔÜSÄï æÝŽL€š?»š×ãY^ ‚Ssj@¨IîáQŽ”ƒšSó߯šSsí%¼J¨yl¹+ÂÒßtj>ZÍ7µТ½€gÕÖŽc¼ÔüeÔ|A²HÍ©9àíX”ˆ¿ö­¡ã æ÷Ps¯5@ÍAÄI9¨ùƒÕÜ›FÍPspSO¨ùƒÕœ”Ss„ %•¼X)÷˜šßýlÔ5Gª2—6RÅsjþ0G¬æu{¨9€‹«PóÇ«9î 5×`κäP#Ì‚š?^Í â„L»N´n$â™x²Ø±+b,¨ùƒÕÜXsjNÍ,J:KˆPPóGª¹Z¨95ðòRŽnͨPóùª¹(F"Õ*àí ›¨ùŒÔ\ˆ×LsžKÄÅÛòêRÀSª9€šÏÖÅÝn@ÍÜ‹ˆ«=€šSsàÁì6«ÍnÀöË?Øü¨wËî_wÜgìáé‚35Š«xà½@e’r€šÔ¼@ŽûÜú²ÇÈÃ{ö,ϸ»JÎÇ&ŸÑkõ‘Ôx•öò}»¾üñw½}=öÄf×PÚÔö¦_viq¸uÖ ‡ó¿=¼åàU6ÁU‘sÌÇ#g«’O縤 æ(_¦Vw›Ê\º{×ýÇjóQq{¶Çå¸ý¹Þ¸Ïçü)Ø1þ±øðöwƒPã»™Dü™¸nr€šƒš¿|­VÞxÉö¡jÞ2ün§øˆÃSgçæxŒˆ?oœyÉI9@ÍAÍ_»V«‘Ý1${¾FÍÃ3FO:âðŒš§r¦tqUAÇjNÍ©¹Z½’`K¿‚O¥æa7v¯@ÞÊ*ìÝ×kîí¸ÕéÄ: @Í©¹HŠ ,6°ô–¯ú¶Tó^7sx3§ lÌ\Ì™î,ºoïvCÕ3@Í¥Êùe&“mmÛõ¼-Í™XRÛ‡©ùïÆ¨”®8¼sH½Gã(fN͇Š8G$å5§æÀ3Ú}¦½4£‘LJs¦Ï‹Ps"þb_®Ps×kþ}.‘n«»%Šø¸š¹gñ|¨95€%êûp„ûÞ¡–¸8@Í©9^¡‰U À¸Wf±¯ÏªÉÃC>r€šSs©V…ú¨ªXÅjPs µ ñY¨y¾º&¬I· æD+0´»5×k®W­:Ž#û¡&A$ æÔœDxA›LÄœAj>ºÝTŸ5§æ°”/ó£;\Yc¡š_Ùnæ¿G æÔ^ÁïÜ,°’§m7OM§j¨95€ÔDj~ÿÆñšé45§æ@Ä1‹vSÓ Psjí(ÅÇ]CJëg£Ô æÔœDB­â žIÏg)»ÍQw7?Ÿ6»ó¦Ó‡üqÇ}†~>h\Héþl”šÔœš“H¨UÌñiô@*uÁ!ýù\ö~xú h¨95'‘P«xV¡'ð}»>]PÐë|ÞvÚÒÚ¡yÐjµÞ¾WÝ×Á>±£ÚšÜ9o[Ÿc™œÿ}äá)9§æ5§æ$jO#âOôÈ *jGUÊ{ÔíJoÏ;Ô»î6ÕûŒ$‰•?yÄ­£™Ä?ž*5¨95'‘æëâˈ9]QmmÙûm¥áù$­îêüQ%nÍ$eÙ…‡'ËCÍjNÍ`..¾Ôî€^5¯Æ´F†„Û£Ò=jšG3)WóLÉ©9j33×Wýù拪}$Ô¼»ÃïpKº×¼ä'¡½ÝÞù_yŽ8œš æ0a]ÈT*W5olˆî~ŸËX‘ÆXózˆz™›3æpcÍPsàâ³%6CKÇfÛÃBªMëívsÙ~ØØ˜ åº-éSç~ÝÙs¸ZPsàñ9ˆ8MwxúøŒFo^sÔ\A­‚‹{¡¦Öür~Õj Ô æÔ\›µŠ¶‹{|Ù[»©é¨95'‘jU­.TÊ1¹”«^jPsj.âC­¢GUÅÞŽU •FÍjNÍE|À ÂïsViT5¨95°\W~«ÁõPs€šSsK1Hõ0‡P¯×üí¦¦ æÔÀsXç›U¨Ï8ºÊ¤æ5§æ^ÍoHÞüÕ<*è*“šÔœšwzÈq;w/žÑ&ÝDj€š‹øP«Ds õn+5¨95ñ¡V¹8„zj€š‹×$R­"áâj˜š«LjPsñZćZ}X5vzÇw›Õf7݉~üñŸÿ9e~ê©9j‰Wy&ûFªdÔ¼×ÚÛ;œÿ{bÛ‡POÍPsO+âC"CàÑûg6?êÿ¸üëûv}ùçõöýww‡fNä\¨§æ¨¹x ,Ñůȣ²è®N‡[v›£_=rHú? ÔSsÔ\¼^ÔÅ'Ͳ’èÚ¸ûü:jó­¹¹POÍgñ½x.¨9€§ÑˆÛĬ–^'F¨ÿìBÍ©95ŸñeþóoIz®t;§æÀ½òWºð»\{Ç¡ããUÂ>u½æÔœšSsIº‰šßºÝ§æ">ÔêÄÅæÕ¼`|Žï`¬¹POÍ©¹$M æ7mú©¹ˆµšñ\]ØÞšrå´­3Ëz»Ý\öjí`†¡žšSsIºZÍo×úSsjuV"~Í'æB=5§æ’tšßH¨¹ˆÔêÃoÄBD<ŠÕ@…zjNÍ%i5¿EƒNÍE|,ʼnÃržõÔœšKÒõj>¹\QsàÞR~g3^r9¨95&5ÿùy]ÇÈ»_ßãzôëÏMd²éãþ‡J8çteÉŸôÂ{×x‡ùΩ9po)¿µ(sqPsjþ|j~ðòâÕuµçUóûªðºjžQö©Þ>jÜ[Ê'þzMÄAÍUæó«ùîÓ‡íÏï׊ÝiË¥÷}ýåëeû×í[¶3¾»CÝ7nif~8ªYìý¿¾}þÿw,·êØãÆo“ýý%§ÎTE+çîn‘úiž±[€q5œ¨öݧË\[ß«²s*u¯=s-7PóI^@jLàÙ„zj>^ÍZöqwñªÍ·ïW¨ùjõöù}¿1è†ÿòáœçþŸþìfµ÷¿ÖI÷[NÞ¹?ö|H4óf†§|"§ íëï/=u´*ò]ˉ|gŒå3¤†S+¡?jwOÑË»Õ#—vc5¿þ¤æÀ æë=F§8ôš«Ì¨ùQø.æTéÔUZj[ ú¶£ÆYšO_æu—yçt­S{ˆ_zÎÛWóNΩ+-?c±îÝ8zçäó®ÕÛ‡™ð5¤æ">nø0g½ÜËÕ<¨¹Ê|u5?öšÿ $¬ê`ž@Í[?T̓žÝó‡èéRüÕŸ¦RónÎ÷PóÔ%÷ÕC±{‰X­Rsñšš«½îþ½j®ƒÔ¿—;ÖüvjÞôþxoëx5¿|Ξ.å…Õ˜ Õ<ó³ÚöšwÏUPƒ*­Ü­»µJÍÅkj®öRíågk¨95 5?èTwŒò$j^añXó¸çö ~9]¹š<õjžîw~ƒŽ´°Î«üNñÆ?ÍLL™©ö  SLqx̳1k七š‹×Ô\{ Ps,Wͯ_µqÚ£î©×w8×­OÑãß}§ž°xÇ æ×o£Ö¥æâ55×^Þ¯5o/ sÒ£ÃÜÕ­å‡:«u¥ê²hÑêíó{f±žDŸnµäMc)œøjDU ó«ÿľoT…\}üñ÷¿6å !uÏ­“KÕk/²€Q쨠T‰UKWí¡JÖ~ú•­Éüá½Wš¹5ÕŸ½++•ßwj.^Ssíå"yß®Ã?nnv‘}v›óöݦ½ç®ùÒõö½:¤E•s럢g„P/ÔÜLÍ£þtrë`q™z!÷½DvW\?Um¯ä¬XÍ÷ùWÒ_@¾:oXÂÂÑ2™éžÚp™úèyãe«v¨j¯yŠÄeW]íæ9\Í“w0~xß•f/¤þ§ ó}}žnñþ_ÃLJï;5¯©¹ör™^^ Æ¡š·vonÙÿ×Yλ{Ö_ȸP/ÔÌLÍ»Fûuûöaû³`¥úº?µÀ£ù÷ž·Wdsj~è‹- Ý+ñåuÒîºî^Q§Tƒ†|”Œ5Ï”vÜ•æoMõu¢ÿvßwj.^C{¹<=ZÍs{6ÕB½P3k5oýd°WÍ˺®S¶?ï5jˆl·[w š'ê$#ÊGŽ›çÐ^óÌw¥½·æÛÇËxžâ?b”?oÔÐ^.…C7v¯-—ªù>³K^Q5gæB½Pó,j~éÏþþ/èþÚkžR´Øy¯Tózôv~–˜Î©Sè OoK»×<]øøx•XžƒÕ<}Þ¢^óh©zJ²û´$Æ©yßóFÍíåÒô<ëÌ¥c̓<¢Ê ;é!Ô 5wPóØøèHåÜü}—cÍ9ŸÕ—4òp`w|Ìñ5ïœ:®æÁ—Šž±æLe»ä–8E®&“CÉ;y^3Ö|¨š'Ží¿5ûÒ¾}XgÇš¸ïÔ\¼†örÑäz´‹zÍ;£Î£½æùñ0ê…š»¨ùy†žÎæj&䘊è4#gku ÆçiÎÔÑ=o¡š'O]å{ûÚKfhiî©“`ššmåšÍSÄ®¨SªöÝéä9t†–Ô,ûilôØÞ[s°ðü -å÷š‹×Ð^.ÞÍS–\8 å}»§a‰Ž5oé?5ê…š©ùˤCOmbòÉj ÔWÞPóGšùu½æg9%ú³QõBÍ­®®âUÅŒÌü-Qsjj>óörµ<Ê|¼d~ñòZΊÿÿÌk.ÔSó;kwsË‚zÍ%jNÍAÍŸTÍÕ^qÿ9o¦æ*svj>ð‹75—¨955§æž=Üå& õ/¦æƒú¿Ç_&¥“¨95ñU5÷ìáJcê_Lͯìÿ¦æ’DÍjþ|µçG±/,èBýœÕüÖýßÔ\’¨9@Í©9f$èBýLBÍCú¿©¹$Qs€šSsÌHÐ…úû„šTÿ÷3Öç x-¨9ðd I7“EmQ Ù"ÔOÕn–×ík|ïý x!¨9ð ‰^s½æzÍ…ú’þïe®nFæ@Í©ùr;c@Í©9¦’K¡~ò/0Ô æÔœšƒšSsŒ‘r¡¾û-åÊb.SÍPsjŽGÖ*5ÇkHùÒB}W»'—©9@Í©9‰5§æÿоL¨ïj÷ý'"¤æ5§æZV¸ÔË õwèÿ¦æ¨9@Í©9¨ù,ú¿©9jPsjŽe©ytÂõeV¦—  æÔ æì¡ÝfµÙyž‡‡ú'íÿ¦æ¨9@Í©ùä¼o×'<‰õѰÏÛN[Z;4Z­ÖÛ÷ãQ­}bG-[ÍS Ž Ô æÔ¸¡dSóysêÞ>wrwúº’}ÔíJ±Ï;Ô»î6Õœ>yT㤯úT«Ðïƒë§R¨9@Í©ùâ$Ô|ajÞRä®+·¶ì»Òð¨XG³Šõ¬vÞ}žóýßB=5@ÍAÍ©95êå%j^TiN ·wÕœA»×­›ÿ[a¾çð@·©yO55@ÍE|<µšçº”Œ5Ϭ0Ÿ5ûóA“©9^¡žš æ æÔ<£æ½½æ©µâ÷¬N–s½æê©9jjNͧQóèZñj~–scÍ!ÔSsÔÔ|A÷¢¥àÑñ'£Õ<ãæ=j~ ³^›¡B=5@ÍEªy8·xTÍGÌÐ’Z+¾OÍÏs»˜×B=5@Í…µ¾/p½Võ< õÔ5ž¸õ}™Ù9؃çY¨§æƒ. xŠù¦¨9°ÐÖ÷®—š{ž…zj> þù·$Ý?=ÜΩ9ðɦæ`“jNÍ¥ªùcÛhj.⃚SsõÔœšKR­æl¦©¹ˆjNÍ!ÔSsj.I 5TKMÍE|Psj¡žšSsIj«ùCkj.⃚SsõÔœšKRDÍïß^Ssî5‡POÍ©¹$ÅÕüÎM65¨95‡POÍ©¹$Õ“'>p¾sjPsj¡žšSsIêQöû¼_Ô æÔB=5§æ’Ô¯æwxŨ9@Í©9„zjNÍ%©HÍoý–Qsà1’MÍÁ&= ÔœšKϨæ7}Ѩ¹ˆjNÍ!ÔSsj.IÔ\¼¦æÔœšƒšƒšSs‰šSsÔœšC¨§æÔ\’¨¹xMÍÕ*5›ô0Pó25ÿõç¦=ÁôÇݯïI‹úõuûV¼óÐt(̤–ŸñΧþöqúÚ£æÔÔœšSsPsjþÜjÞUÆOæ½|óíû”’î9¡ÍvЩ¯,çý¿PsjøšDÍ!ÔSó'Só^eLíPw¥W=ÐÍ>õs'q§Ÿ¸µç)ÿŸŸ×ÇMë/_ãùw v9ä|Tí¿ÿ•>Åé_ƒíU9ß>¿wµþø¹aªxc.9Q{Ͳ¾]ö‰Mêž:•gës¤0Kë½Ô\¼¨95÷< õÔüz5ÿò¡¿Güàm¶?¿Ç¬:ÝË;­Û{^´øp¢óöHþ­*“®ŽJ ¿“ì³ r»¨ð¾`oÿx»|îž%vêDžÑëj¦õHôÜ j.^Ôœš{ž…zj>šœ¯v²ìxŒc¿im·ûÿ¼ÈzT^óÃ`r]uÜvò4v<ó=!5ÖüpÒt>í«ë^þ¨KÎsxŠlmtOÝ ©Jèä?è^Psñ æÞnϳPOÍÇ©y·´ÿ‡Œ';oÿ4%y‰ýjË?’IºcÔ¼XaSÅqɃ4:7¾¨yê)Õ¼à^PsñÚQjNͽB=5¿JÍË»Ì#ýÓ—æÞQé½0’½æý“Æ´5tŒšu†õš§‹7è’§RóÖ©Ç«yçÒzï5¯õÔªU÷‚š{ „zj~¥šî2{Í£ƒÔ[¦Û㩱1ÁçžAða -ÒüSc¬ùñØóÏ#Ÿ+ÞP5/^®æÉ<£×Õ.Lr¬95¯©¹ZtÔÒ æÞ¡žšTópq¾Û»;n$p¸UdV–öˆ”ôŒ"ÙîáhþÑ9IÞ>o?]ö©²M¢ÛaVå\þ}dž™w‹—¿ä¼^ΦRpêSwóŒ^W|pyûÀž{AÍÅkj®V{ùë^´‚Ë£ðذI5Ôk.Iæ5¯E|Psj¡žšSsI¢æâ55§æÔÔ\e¾´š‡Þ"[è DÍÅk@«õÔüú’g´»4+:(Qsñ ÔSóVþ¹¾í`ËÄ•@%j.^„ú×Vó û¶o[ tP¢æâ5@¨ÆÊ|Hß65—¨95ž¾Å“³Û¬6»Æõ·­Ì¼v›¡E’¨¹xM"¡VŸK£‡î\ràè̽Ë õå}ÛåUJÍ%‰š‹×$ÏZ«¯ó ìêåä.Êû¾]‡[ª=bN\í»Z­·ïáÎ?Ú­ú¼ÿé_ƒíÕ¾Ç\¢Šÿ#R’VQSWDÍ_M»o]™Ô\’¨¹xM"Õ*5ˆ˜·ýuo»çMõ?¦úªw›Ê¤’Ü Òú5ïãöP¶#'~RTjþ`Ïžªo›šSs‰šSsPsµúªÏ@-ÔÁ–JŸ îœܒʼòí^5µ{EÔ|Ö}Ûó¬Lj.IÔœš“HµJÍÂyüÇEgëá í±(ÙÃ;Pó¼âwºáÛgï^5Ÿ²oû‰ž|jNÍ%jNÍ_$ž“º¯¹$ëOv]¯y=Ìe`¯yZ¿Ÿ³÷|òPÿ’MÍ©¹DÍ©9€%ˆlLx[öÛÞkŽ3éŽ9éÚvÐÇÝ?~:öô[ψÇçÜüEÕ<%Ù§P¿í¦æÔ\¢æÔÀëŸÍ$)Žs‰™n0³Ê¶ú9g¸sý9;p%W½‹Ç6KÒ)ê³ÎÏRÒ·¨[¨§æ%Ï0¨¹x óÂACJ„zj~Sþf5^§ux`dF²¯O"ÔSsjjNÍÁ±Ôê]}OwïžÂ&Ý,jNÍAÍ©¹ˆ…Öjai[ìÜeõÔÀ="€x-⃚g¤œšC¨§æ¨¹øB"Õê½K›ùYº» ¡žš æ æju|©€{"ÔSsÔÀd¯ž^sõÔ5¯Y¼zGWŸê©9jàÞjtõ ¡žš æÙºSsõÔ5_ðÊíèÓµîÔB=5@ÍÅ—¥H¤ZÕºƒšÃË €š‹ø æZwõ^^Ô\¼&‘jUë6éaóò æ">¨¹ÖB=5@ÍÅk©Vµî`“B½—5´îZwõ^^Ô\¼´îð¼ õ^^ÔкkÝ!Ô{yPsñкÃó&Ô{yPs`†í¨ÖÞ¡ÞË €šÃ -Ô\롞š æâ5‰T«Zwx „z//jjN͵îê©9j.^“Hµªu‡·@¨÷ò æ æÔ\ëjNÍPsñкÃó&Ô{yPsZwPs//j.^Z÷!']ž7¡žš/öõ‡0NÍ­ûÜÕÜMñ¼y[«æ¿ÿù·$=WºƒSsà5}‹š{TîY~¡žšSsi!j~ë`NÍE|Psjîp Ñ«ê©95—–£æ7çÔ\Ä5§æ÷»)¯ߺ‚.ÔSsj.-JÍoׄQsÔœšSók]¨§æÔ\Zššß¨£æ">¨95§æ× ºPOÍ©¹´@5¿ECFÍE|¼¬šåôÞu§ˆ²eÚ-¯]ÏB=5§æÒ¢Ô|r  æÀ"øk ¾+ê5×kNÍïªæ¿þÜÔ_ô>l~¿«rÎþq÷ëûð£*އWùä3üõuû~±~ê'Mãêy~“'Þt¾sjPsjNÍÇK¹POÍ'Pó£ªn¾]ŒíÛÇ{Ûêx5O•û§æÅ>ÊYbÉ“Ÿtòw…²OõöQs€šSsj>^Ê…zj>…šïU)è)ÿùyýöù}œ9í½ôb®¿|­=ì²ý¼ñ˜vŸÎ{n¾%\­›[Þð¢½æuy¢C½î}v8Ÿú´¥™CþJ_lNõ½Ê9öM ™awK÷JG©°ž»¹ÅKؼ‘}¢ùôu©±k¾V¯!5° ¡æ3”r¡žšO æg=:Úù5=šûc+§?ØXeº«Õi{µñŸ¿ùPéãÑ;'æ6\Í÷':›ñþ?ýyÉ­9h§¡òAÿÉ!s¥§Ï‡¯7ÿx»|î+ü¾Nµ±?×ù]•Q¤’z>no}yˆ”'z›W/Uþ¨‰zÐ'ŒiÔà—ªŽš_{¿„zj~­š‡ã¶»jxÍP‡¨‡ºW9îõ³è%cÍÃu»Ò+—ÍÈb*‡ø•†{–•?{ôò© ž3ßpúïc«J;¥*:ªóW…ëÔüÊך‹øP«ªŽš õÔüÁj¾û\Õå9ÒȺœ’³ŒšwsÚkÞÈ¡“ÉþJOvžSóD…Wz5U¤›«y¬T¥w?6쇚‹×LH­.­´ÔœM õÔªH¹zžª×¼ì2£Åˆû¡æâ5R«ÔœšSs!eyjÞö0úg á(޾ÅáøæäXónn׌5ï¦F¯yrˆH$‡!Wš¶íºÎ Æš*RI=k•ìú*¥ê9Šš‹×LH­*-5T|ÛmV›POÍç¦æ­ÑQ//ôÎj¶·ÏÛ£þøû_‰ëÉÅ“3‡trKvîfzÁƒ)J.å‰o©¦ŒŒ™t˜C{^‘þ+Í\ÝZ†©9—Ë3´t?7§Úl—ª÷¨àÁ3 E¼fBj•šSóBÞ·ç9ÉNb}4ìó¶Ó–Ö̓V«õöýxTkŸØQB=5ˆšKÒ“¯JͼœšŸÕ±WwAŸS ªý½Â«N᣷TóÓ…œ/§sUÇ8êvuaçê]w›j‡ÃuGê§sTã¤B=5§æ’DÍÅk`± Rn„™=3ÿTû験ÞSÍ[ŠÜ-dkËþÒ* ^Q4«èQ#í\¨§æÔ\¢æÔÀË(Hg¬EÝ3ÞuÊŒª¶Ô3•Cô¤ãF‰\¼dvÜ5Þÿ_ߺrÜ«æ»æ”aá«l5¦ŽlçB=5§æ5§æ^µ×<ï…Ejãq°ê`HTUG޹¨í¡'ú?Ö—ÏÍrŽí5¯ ’î5ïVWxúÔW—Tÿº^sjNÍ%‰š‹×XF;Š5>‰v§Æš§Æx;½kÿÎ|4J$Ü3yÔ¤cÍb–”¤ñ-¢¾øÎQÆšSsj.IÔ\¼&‘jUÕEt3ó›Ð’^óÔh–µfÔ|ô(‘dßÿÄ3´$¿¯t­·ÛM8Ö¦>6r”POÍ®æç9øÆO2]< úðSd¼ÌgÌ!x«“¨¹xM"ÕªÒNªæ™Þó5ŽÖÈìÿ{¢Q"7RóW¶I1„šO`Ì#t|¼š'ŽÊýS°R¦DÍ©9¨¹Z]žšgݼÑkž2d”ȵjþìï¦POͧQóÖB3'Ù=,Ó¸ê,Ó4ìËb7ý½ì[2•ùyíÉøò7ÃWmg’²öÂ…u"õÐ]£'[l‰š‹×´L­*íjÞžŸeÔ -Ñ?"Qª®%òã:5¯N@ÍAÍcnÚX4±~ûÅG÷òúöñ·Ëçøêô±ÌƒëÅÞ÷º_°d}TÍ#™NÑ^~Èrôízh}ñè)¶DÍÅkZ¦V•Ö™o«&Ô¯§æÝ'¼±e蘓¤¶^”·ý¹WÍ»¹}ݾõæÐ7Ö<•É©c»2ìLyfcüëA_±%j.^Ô|PaV/M+¾eö¡æ˜§šwÔŒvOÚk^¢æYÛ¦æ6ÿÍ!ÕkžÏäÛdzO©æ}Å–¨¹x @¯y¦‘š õóQóž¾íÛ]æÕüëö-ëµEjžÍ¤Ü“jÞÉ¿·Ø5¯PóAjþêÛn–HöL¾ ŽQóòÁÙ955‰ ­æùLª^óäåKxþYgüW¤¹±æ5§æ¨¹^s¡þ•Y8€äësœš—Ni’r}˜Ë%ÛÝ~ß«´t2ùñ÷¿6ÑÑ&ÝË©7î==czl>–\±%j.^ƒ_BÕ¥â5_x¨ÏKöiã AÛV•$j.^3!¨UU7RÍÍÐòJ¡¾|É€ßGŽ­Lj.IÔ\¼fBjÕä‰Èññš?ißöížaj.IÔ\¼&‘ æªî1jþU1óP?èÇ‘³ø}$5—$jNÍI$¨¹ª{”š/í-¸¾*žB²©95—¨955§æO©æO4ñ5§æ’DÍPsjþLÏUF²_é'°ÔœšK5à{5ØU” Éß\¡žšSs‰šSsÔœšÇ={v õÔ|ÚïxÀ @ÍPsjž¬Ò;ÿ8R¨§æWòðBPs€_ªºWVóÂ$¬O¡žšSs€šƒ ©UU÷”jÞUêgŸøO¨§æÔ æ`Bjõ¹Kûbdù¸m“' õÔ55W«ÔüV"¾ðø&ÔSsÔÔ\­Ró'{T¨¹POÍPsPsPsj.ÔSsÔ\¼¨95§æB=5@Íøž@Í©95@ÍÅkÔœš õÔ5@Í©95§æ¨¹x í(T5ê©9j&¤VU5§æÔ5¯™ZUZ$5ê©9j&¤V©95§æ*S}Ô\¼&‘j•š{ ©¹POÍPs0!PsjNÍU&5¨¹x Psj.¾ õÔ5à{5§æÞ jPsñÀtñaiPs¡žš ææÎ_w¤¥2M õÔ5ÿƒšSs¡žš æ ‘j•šSs¡žš æâ5‰T«J«T©POÍPs0µê€POÍPsñš–©U¥…·@¨§æ¨9h< æÔ5¯jŽ»Ífwü¿ã\çÇϱ§æ^RjPsñÀ“{ïªPlË÷¼Eù²gLÑ„zj€š‹×Ê­öLå­ïÛu¸¥Ú#&¶õá«í=.|Îì´¥•sól«Õzû^Kô%³|gxàÜrv.í¹ä\¨§æ¨9€¥‰y[V÷ªÜ1Ù”ÓÖÛ/q)Üó¨ÆÝNæ\íppôðËÀñó~ãú?֗ϱb§l½³óS¹¹POÍPsà)ÛQŒ¥k»¾Ère²)¥­O¨yxT4ç¨8ï?„{Õúùgrs¡žš æ ‘jui´ÆŠ#TZCS²‡¨y4çÖöîéú: Îl¢æB 5¨¹x-âã¥kõžº‹¹u’ÐßøáûJØõˆr4ç°o;ÚIšwÎ@Í…jPsñZÄ55t8ª$xnüIOÎLÊÕ<5Ö<+ÿÔ\H¡æ5¯E|P󙙟¥±µ1Î%ÚcÝ9¼Þ3"‰œÏó³l7?ªyâ窑²™¡EH¡æ5¯E|xpóoýÎm^s/)5¨¹x hõÙü]ì<ïÝVõ’Rs€š‹×:ïc0Ÿ ÔSsÔÀã¤ü%ÕÜ— ¡žšÏ-ÈK€š˜¦½¤æê©ùô—ùÏ¿%i9©°=¥æÏëïÄrË ÔSsj.IWªyÉ NÍE|¼f­ú»!fø‡Z¡žšSsiájÞûŽSs/«æª¼[¡žšSsIšDÍó¯95ñAÍû/AÍ!ÔSsj.ITóÌ›NÍE|PóÈþÔB=5§æ’t;5O½ìÔ\ć{lÝ©9„zjNÍ%éFj}ß©9 uïiÝ©9„zj~5ÿöñÔ°ùö½Çr~ý¹©ÿª÷q÷ëûqËñC¹*U‡Œ86šÏࣗɰqTóðÑÊx̳Q烮¨|ç+ë¹$«•|ÒÉ{GRs@ëþÄ­;¨¹Ê|5ß{ù§?OŸwŸúì|©ºFÍÃCÆ«yÆ¿û2œXs?¬ß‚Üæ-¸·UóG\Î馿€Ö}¹jîïB½—w^jþëë6Tߟ×oŸß3zÔõ§–gïs8vF®¿|­ó¼ôPž76ùñ?ûºy~ØþüžîºÎžñpQé^íÞK8{"“Ôáçbœò‰ÛÝXxùjÔùâ)ÀþëÓåO¥íf{ù I|ÏîÍÊœt`aR™Ä/6qù·Póz(©x hÝ©9„z/ïŒÔ¼·ç²ÇkW«“Ùÿ¼Øö~cåúáÆæ‡Voýû—¡f{Í;g<~Î-øƒÀP5Ofò+¡•ÁWšè±‘—¬ê 2ß×É©öÿfRÕÌÑt^rþþFoVö¤å…I휪ØèåßNÍOï/5^Óó¨95§æÔüiÔ¼3 eÕ¯æµó#ºÂÞú^}Œž¥ÎsÿeãÒãž,Fß%d2ɘ}ým§sl|cðátùùÊ 3|Éuw~ÿ/ †ž´ÿJ;º³7Vó=Ô\Ä5§æê½¼VóÆÏ@ƒ~ÓqZâjÕoó»Ou‹öûPóÖï5 Æ4Ç¿3$2éWóرÉõ8“C'qš'¾$ ºäËÎqAïý™ï¸Âô~½¡æâ5#Q«Zw·ŒMª=jqôüà¡jvÓfzÍ«S·ºWGöš_3&§/“¢^ó®wF7¶†pì/|œš¿äŸñý—1âÉ›UªæýWJÍÅkF¢Vµîðõ^Þ>5êÚµjü 3=Ö¼ò¥ä»AlðFt˜ø—̤WÍÇF6¶<Œêù°ÎŽ5åLJw_¥æ±›•>é Â¤vNýö÷þcÍ©¹ˆjNÍ!Ô{y­æÝ CçÈëÐRM òöy{t²ÿ+Úóz°UvÌt=ü¦gt{5ÈÈ-™L Ôíc™k\;‡èþ#`ô”$qÆ\_f®®ŠïÎÐöŽW™ö!iµ:‹xaEQsZwÌð¡ê½¼wTójLÍg!vùÒ–s®Te—9XÍûr¾“šyHbOÈYÐK.‡šкãÏUôÑê½¼wTó¯Û·¬%å8èÍõšö°ÞXÍ{K{•š_æ˜^óëþŽ1Y¯yñCÒ=E=‚¨àr¨9­û+½‚.Ô{y'UóØP„às=ü7×Ùü°¯"|É9=d<’yÒSƒAÍ߀V9$ÆšNí’-mð9~ƬM&.3¬ùãçó ‹ÕjNÍ ¨9@ͧ”òÌKšQvjPsjN"AÍj>±”_ßnªO€šSs©V©9ày›@Ê©9j,[•v›£lvªÔ|5CÍjNÍe²óJÊÃÏ5šJ£æ5§æÀkxù‰Íàó®Òô÷íú¸i½}ç‹PÕÔ5pk;öšMý¤äAµÞt¾¨ª©9jNÍ¡}"5å.|Ô5§æŒD­Rs·  æ¨9‰Z”–šÔ5§æŒD­ÎQÍ«|š¹ æ¨95'‘jõaj~úñg0C 5‡˜CÍPsñE3©V•Þ•FÍj>k5âªÏ›J£æ5°šŸ–Bq·€š«4jPó‡©9)ÀUµJ£æ5pˆ!åø¢ªViÔ æ1'ĸeð|Rs€šÏBÍ@ Ô æzÍuâ)kÕ3 ð\ü55Pó‰ÕÜXsF¢V•5¨ùŒÔÜ -´L­*-¼ æ5Ÿ‘šWÅwͤZõ ÀÛÔüñj.ÄT ž7 æ3Rs5jNÍðE¨j• €šãiØmV›ÝMvnðãÿüÏ‘‡GU«LÔœšÏÔŸöòæt•ÚKŠxnPóEZ÷™JKß·ëpKµGÜ[ÿ°ÿ§j¯õö½þÇVV >oÞü8ýçåØãNçÿ¸ør·¨í-¡\w‹ž:(fó 3‘sí%Å<·¨ùżí¢{w=oªÿ1§¬G5o p6«–_Þï½þãõåsÛ¶»eÈmÙ:¤Î)i9|'«ÑjÞ??KÑ -Ýϳœ¡5jŽ6_2ýY.inóšƒ/BU«LÔãÍö©±¨¦Z½©j• €šSs`^íeõ7wâÀs €šSs‘³Ps‚îE€ç5§æ";f¤æ%‚îP ðÜ æÔ\dW«wRó¼ { æ¨€›" BwPóöþzÍ5“ju’¯²ž ˆ9@Í©9ðH5ýê©OPs æÔ˜Fͯ|õÔ'¨9Psj\«æÓ¾z/ÿöñEU­2oÑ;øm54ÕÓ¿zÔªZe–éŸKÒs¥™Û95^S>¨9_„ç–šKRTÍçÜPs‘Ôœšž[j.-KÍçÛ|Ss‘Ôœšž[j.-MÍgúZQs‘Ô|ij€šKÔ|ž85'‘Xœš·¦?¯8½wÑ)ÒS[Ü}ˆ9Ô\’žZÍ穨¹f‹SóÞWoÄÛוõAŠïk€·Ô\’¥æ³ VÔ D¨ù/ín_˜%5ÇP5ÿòaµú¸ûõ½±ýÛÇÓ˵ùö=§M¿þÜD&¡>ævø§N¶¯—r™™ ¿} Ì|¾sjP¢'Só‡|¸Ï×€×Yj¾,5?ŠõúËç¶Zí½üíóûÁº¾nßúì ¥cK»O¶?/ÿùóóúÓŸãÕ|øñÑ]ùzÙ~Ðýºs½Ð /½øõQÝ|gìdÞÝÿR¶ºx©2gòIRžù%ÃÆw¤|-æøÜ½)ÁÍíæSÿäã¿ÿµY¥®ôô¯Áöê¾ÔÅne;Ã’Ë,Pó‡·€ÔЊSsÜêkÀ³¬Œí¹½¡šçÿs˜š_D-pÄ÷/ÎÝð{™ëJTÍceˆäž±SÂݧf÷ÿ~çjϪx‰2÷äÓ=dHæÕ¡pdQ-•åß*í>ÛÆ÷d>±oG­êmÜU]Èê»\Á­ì¹Ìb5l#HÍ×ô™¢æ¯"šCoñ ‡ä3?\ÈðZ*Ï/ÊÑnì•z0ÚÛ/WÔ«æƒ.³OÍØRs jNÍákÀ|  æ 3ëü`´d@ËåÀ³ GóªæL¦VóÂÌÇÕÒÂ×Ó’¦(«„R5ï½´hÓBÍ©9#Q«ÔÜ-ƒ¯Oü×€Œ5Ïtø b=è"–ONÍ;û‡=ñzÍ»ùôuóSÕÂZRø“‘‡]ÔãòÉ)xó°^ó«~JÍ©9#Q«ÔÀ (Tóégh >×Ë“ù=ïüÁ„šGóÉvðÇÆˆ_„²w˜uO>Q-ͼÊ0>Ö<_KEcÄRþ%ÕeÍ'g’sÒîã~\[5ïÜʦÇ÷\&5§æ$R­Rsx;T‹ ÈÍJˆ×eÂ`"‘²‘Êýß{-ª±¸ÈÐäÚùô”°3H0©ÈöSsÖ‘ÌõFghÉL)Ó›ùåêZ³XöÔÒÂ¥¿ÓežÈçpÔacx!WºSèDoe˜aÉeRsj®™T«ÔÞ¼òýµºälSáØ¡iÔü'O±J(5§æ5‡çÔ\šp}ŸñÝÆÅ§Ht™Ssj€šƒšƒšK5§æ5ç‹PÕ‹®Lj.QóE«¹Ps¾¨ªU&5—$jþ`5·È3´—Ôœ/B¨¡æ’Dͬ検‘¨UjîE§æ£ZÏ-EÙ³´g—ë~CyÌ39aù._¢æ7õRÎHÔ*5÷" Ró‚zÆmI† ñq“õ[lÞnjNÍŸNÍOø(#Q«ÔÀÜ£_vÓ¶‹D6׊îŸÚ¹»ÐOáBEíõ,c™¤?£Zô¾»Ngcu`5ÿwlU Vªår6ß J+Qó;«ù¸¥†m±Å–Ó–Õp¨9t`*5ß[æÉ§+GÖ ­‰|ßûeµŠ{®×<²s½û·ÔBñýjžÌ$~ø¥­¥àƒc#/YÕÿd©œãçêÈîS-â=—,Qs½æšI®úºØ«iLFhÉdR æ©cÛcN¿ÊÏÐRÏÄÒœ¡%É5 šÚK¸eðÜ>‡šK5§æ`$jn<·Ô\’¨95ÙÕªÒª€šK5§æ`$jÕ3€šK5§æ$Ôð¼QsI¢æÔšIµê€ç Ô\’¨95¨àys)DÍ©9€šƒšK5§æø"TµÊ< x%¨95@qT5^¤2ÿ^jNÍ¡½„[Ps€šSs‘jÕ-<·Ô æÔ\dW«J«Ï-j.²ƒš€%©ùèŸÇº=´L­zàíj~že”fÔð¼5§æU‚ç ¨95PsXˆš‹×ø¢ª†Ê@Í@üQÕ*5b ½„[xnPs¨vµ · ž[Ô\ˆÙÕªÒªÀs `öj‘]­*- æ$Ôð¼5‡fR­*-¼j€*ÁóÔ@ͳTsñ€ø£ª¡2PsªZe æB ´—pËÏ-jÕ®Vá–Ás €š 1"»ZUZ•xnÌRÍ!²«U¥Ô æ¨9@ÍPs`öüÿ—‚(çgXIEND®B`‚ fs:Filesystem 160.0 73.0 file:FileSource 192.0 209.0 274.0 202.0 User 34.86475730998367 0.0 b:TNBuilder ftn:FileTN fs:FileStream d:DirTreeNode 66.86475730998367 80.0 539.756828460011 126.0 651.0 0.0 683.0 305.0 571.756828460011 328.0 306.0 351.0 331.97135964975513 374.0 363.97135964975513 457.0 418.8259109283281 480.0 363.97135964975513 563.0 1. User wants to add a file to a dir in the iso node 143.89406091532933 16.868736840587744 2. It creates the source filesystem and the custom builder 317.51829970572646 74.92004824517142 570.819415201306 142.7048538003265 0.0 0.0 570.819415201306 142.7048538003265 218.81410916050066 114.16388304026121 0.0 0.0 218.81410916050066 114.16388304026121 3. It gets the file from the filesystem and add it to parent dir 379.1320632384976 217.4323774110454 327.03195662574825 218.46075295682857 0.0 0.0 327.03195662574825 218.46075295682857 4. The dir delegates in the builder. 5. The builder stat's the source file. In this example it's a reg. file 767.038589176755 206.92203801047344 694.4969551615891 312.7614712457156 0.0 0.0 694.4969551615891 312.7614712457156 314.9148790283507 359.23720542189034 0.0 0.0 314.9148790283507 359.23720542189034 6. The conversion is not needed, so the builder just creates a FileTreeNode 762.2817607167442 335.3564064307673 522.2869299335649 399.9594286575042 0.0 0.0 522.2869299335649 399.9594286575042 7. Sets the attributes from source 774.1738318667714 413.8440760209469 8 ...and a FileStream to read contents from the FileSource 762.2817607167442 478.0612602310938 534.9181953038541 453.1845675071054 0.0 0.0 534.9181953038541 453.1845675071054 482.368075796364 524.8261757327898 0.0 0.0 482.368075796364 524.8261757327898 9. Finally, the FileTreeNode is added to the parent dir, and returned to the user 757.5249322567332 556.5489298212734 689.7401267015781 614.8200784564067 0.0 0.0 689.7401267015781 614.8200784564067 363.97135964975513 656.0 10. The user can change any attribute on the FileTreeNode 735.3910524340093 659.0235200658623 373.3523804664971 666.0945878777277 0.0 0.0 373.3523804664971 666.0945878777277 «create» «create» file add_file(file,b) create_file(file) lstat() S_IFREG «create» set attributes ftn «create» set stream (fs) ftn «create» get(path) set_permission() get_root() get_from_path(char *) «interface» Filesystem 159.04005306497305 489.4913761627291 MountedFilesytem 56.38849919058573 630.9884605487425 IsoImage 258.8562868808994 766.3563832139356 lstat() read() close() open() readdir() «interface» SourceFile 481.55979910778467 464.84194569982117 TarFile 176.58261638364775 701.0593878047844 read() size() open() close() «interface» Stream 779.894860994415 340.36024540554786 FdStream 907.9433913981195 505.6600343909271 FileStream 646.2536512193697 514.5953286599063 TransformStream 774.6238447615127 513.9203093177954 create_file() create_symlink() create_dir() «interface» TreeNodeBuilder 469.51180397870456 119.92057094444797 TreeNode 777.5164467644091 137.7586776694888 File 776.3272396494064 235.11044131455145 Dir 899.7797731623193 242.40651557378732 Symlink 658.5957352641371 237.4888555445569 «interface» FileBuilder 68.74900622278733 236.29964842955417 «interface» DirBuilder 190.04813195306485 236.2996484295542 «interface» SymlinkBuilder 304.21201499332614 236.29964842955417 POSIX inspired interface to files on different filesystems. open/close act as a opendir/closedir if the file is a dir, I think we don't need different function to open a dir. 154.8805850420814 333.9382491299707 "Sources" for file contents 587.0127806828101 358.755499461917 CutOutStream 845.6997102991108 605.2834046956852 FilterStream 721.2489168102784 605.2834046956852 «interface» Filter 715.5920625607861 705.6925676241749 Used for arbitray streams, not related to filesystem high-level idea. Also used for files like fifos, that can't be added directly as regulat files via de Builder, because its size is unknown. The need to be added as new_files on image 906.5108934811542 328.0975464705584 Create the user-specified TreeNode from the user-specified source. If the source type differs the TreeNode type the use wants to create, it makes the needed conversion, if possible. Each builder implementation can do different conversions. 654.7808793787427 20.610173055266337 Together with the SourceFile encapsulates the access to a given filesystem and abstracts it to a POSIX interface. 20.610173055266422 403.050865276332 The TreeNodeBuilder can be created with the combination of different interfaces for each factory method 149.90663761154804 57.982756057296896 MountedSrc 373.3523804664971 634.9818895055197 TarSrc 479.4183976444791 695.7930726875627 IsoSrc 578.4133470105959 773.574818618083 {{create}} ‰PNG  IHDR—/àU¦)€IDATxÚìÝ=’ãHºæûXÌ,à–Ý+Τ֔'•\ÂL*%eØYB—ÔÐ4ëR¯MÉ}Ä+©oï¢ÂºÏ>òòpÀ?àA sË$€Ãñú×ûÐî/?Àx°4VXeÃ\IÅýø¯ÿaɨ8AX™Š;ÕV€ŠTœ ¬EÅr â'+Sq„¨8@Å ÂÊT!**.Rq¿}Æûüþ¯¿ÝÁmý×/¯÷ºõ½ódöÏoÿüóˆg µ?s™1q¾þö·‰4êä³®›ûk+`±l#ä@Å@Å…>ô|Þíd_ÿITÜ墢páŒ6ÜGõÓæSÛ•TÜUó}Í,©Ù9PqPqûðþµ;亿ÿøyÓ ’üë×í§hçtžÍ÷_ƒA¡ÆuN]ÕA:ÆÞñÛé´| 7½]̧$~ÆÒHQ7ÍÁO?´:'•ÔÞÁÃå‡Á·(΢ÅvÙtäõ·1*®2 ÆùúKÆÔÕ‰œXš#©„í¿/ºçsúÅx–vïì#*D*.7@qô€‰òñý§“—¼s žýîœæ„½s ’äU% v¾cÛèi(¥¿'Ãê§/6) R•Lj_$'|NF²;Ø¨ŽƒR¥â†ò1kœ}TퟂÈwi8¦g÷×ÁDN,çxú7:i³Ã%»ròéó·OçÏi•xY[É@Å@ÅEÇ÷ã?mÿ‘Ÿ™–'ÕWeõؤ£+]픊§fŠ]"ªžeŠIÍ ŽåT\:’ààØ•5ùXNy£ÕëS~yHô0Â+°Eª¸ßÿ];**nPÅõÖψgÖ%'f®ád_C0Ë.ÏÈgŒ‡¤†“:ZÅU¦s²ŠËÅ_¼éoŸÏÓeË)¯6Ë¥*®Xfæ-`KUq„¨8žXŕߋëŒáÄ.r0N•‹+ºÅUC%“bh_ÛËÇ3jì%3et ©ñÁá­¡t^:W‘ÈèÂ÷¯?mÿ1MÅ]ƒ·PqCÉ[°Š#ä@Åð¼*î°zG0.£ÚׇºËž.순6¶ÄU¹ß+ÇpZ£"£â²éŸ¦â‚%ß.+ˆäú÷â2‘Œ/n(kŒ³í§Mñ½¸\"+r0«Ä2ïÅRq—0* âX¢ŠKî—ò‰›_⥠?ý¼m÷îÖ‰«’ûä=òN²ª7Xôò­[ا꽸(ªîR‡½ÅQIM­Q9úyÛH&­Q9˜ÆÙë¨ò•ùD–r°¬ÄrkTVª¸Y Pq,QÅ v'ŒÅTT'T¨8@Å GÅŠ€ŠŠ¨8¨8A¨8Pq€Š*ŽŠ·ÒÅENû|~ûçŸG¬8²ðåIR˜M­¥V͘³ç m—ºø **îùÖŠ\‹ŠË¥³f“4‹|–·Î›7 ·Í*TO¯ârh»xf÷êþþ×yϵÕñª`»ðĶÑɽ¤Û˃o™-ª“‘;;gwÍN8úIuÑ?¸;óxÕîòà‘“æêØ¡ŸæLªf‹;ßú­(¸Ý}6ˆ¶wíîÂ?ýû¿*½û&ry¨ÀT•íòÛå9BÅŠ«¸ý¸AW;íŽü´ýÇߪdFÇÁ£ê^Õ¹iÅ-ÊNy:’à`Î#/§sঙ'*[oö•e#,çaÃ;ÓUJýWÇ=T*—U\MÁ‹-6oŽPq âÀ¥*.˜'vò\Ûic™yŒ¹sÒQ%ÝôŠ[TiŒštæÔQ&— ›Œe®¥â*nwLJí^òþõ,oŽƒT£ŠGa€q„Š«/xÅfÏ*T¸TÅC3%l?˜P5 1Ú¨ cqÉ–±cqÕþ}}:Ç ›8Í©T]w,®îvwyØ^ 'ñv­U<æQqcÊö?¢wùæÍ*T¨SqGïù¸úBFÅ…/öÌ â‚%_|꾯5ñ½¸L$é÷âRœyU,2Wd‡Ò«bó¨¸Ôô¼!#,åaÃô7¿w_H«.ù<꽸é*n¦¡â@Å€a×úÍ;741,t=›_F¼EÕ]į·8Jÿ™5*ËNy1’Ì•ÉGŽ—mÌš+ê_Xk½š×÷ë%Ž7Â6—þ³ˆzé© úâ½ï—\£²l«áÜÉÔˆÙr„ŠÆ©¸‡Ü.LXQ—mT<¨8Pq€Š¿ øÐ ¢**Ž+,T@Å@Å ‚@ÅŠTœ T¨8¨8®° PqÀ U\°~zÀekKâLï𶺵O¬´±ôeQ®;‹Ït*TÏ­â®á¹–Ü|êíµuw'þTÜÝíyƒûΛr* âX«Šël¾|ÜÎ8±¹sâ`sar°äÖÒÁ†È-¹“wŒ¶¨~ûû.‘ýÄ{ùoûƒû½˜£Í£ë¢mwp>ï@½ùþëp Ø$¦êw:¯¿¿O@ðùø,;ØMÂâÔÆÖŽÓÛò;“SÙ;+ L/Ù‰ûmÙùíŸ~-mnÞ–ýøðùvíXqÎŒ­¹šíÈS ËÙ“Šî¤â:âçãûO'ß7Ø£9qðUû§ òZ;úÊ»¿†‘4é çN''ï˜LóîªpçîÂ?ýû¿íÓß\UNsIƒðV+ŽŒ!9]G¿Oc–½­^Z»ÄO“5MÂ’©­Zìþ5™SaèÙ¼ªÀd“ÝÞwÀ¶Éçžë¤ÙÚ«ßÏß>?7Ò³KÀàãÜcÔŽŠ‡ªžx6T|Pq‰#áÐPë='ŽNpÒÛNF2y.ÍÍUgˆ&5«sT´ƒ ®á0’Ó•=CñœÇ3÷zéëI9ôµSN̤Fiõà—˜ád]5ZÅ…±HÏv§]ûã·ÉòŸ·'*ËêéáÑ×þ"ä@Å¥œòÞ:(ÁàOâ`;!íÚüÔ@ §ÇìC¾¼D³ï*â h•`ùÍíAúv^ì/´XJCÚžÓÖ¨,0Q²O눌)Q ãtc1ºX6cw™ÊTŠö¤â@ÅŠ„Û«8BÚö•…pUCv?L¸íH×*ú)*T¨8A(©8BÚöUmoZëR˶úl•T¨8èéa¤Š#ä mЍ8=½ ,}§ûÈAÛ.PqzzAxÀ1:@Û.PqzzAXt¯©M€¶]* âÖÓÓËé—xY9ÛE/@ì]3ýýén‚‡¶ ª^Ù›âWJszÚ‹¼é55 xd·„öaƦû1ÖäH,‹åG¾RG–èúçËèiÅf±=5*ó÷ôu{ Ý©Ÿûió)Ü€h^WÜ”¦>B*n.U6ö™Tœ^Ö¶ÏWM¨¸Gk½çWqWóæMPqO¤âÎ;«n¾ÿz>ÞÝ´²íÄvü©,O|°¹0¹YPf÷ÒfÏÎ>³¹È{ŸË3ûÃVÝ(|ü޹¢ó› mËç²£gá`ŸÓ‚Uûצöx̬^"ã¼ÈÜ7m´6%ç%Å÷ñŸ?ýüÇaÏÖÃέ¹Ô¦,£×Ķí½ú8­=uÕ¨ú>¡q(õ…ööØ: KÓ|í›”¿·ÕM£šî †n=¤4ú Zm«žêûzIô:²8æ çÑÉwdÝ͘®íö=5] ¬JÅ[Ì`kÎï?f†Û°ŽŠíp$Oâà9ªöOA仦ùØèïþFÒô‡¶;ŸòÞX\|°Ø7Œ¼Q·ÂîÚþ”ÑæÌÆò™ìè[¸É‘@÷fY"ƒ™•Md›°\'Ö„æ„}ž¾´ù{ŠGª`½&ŒÅMkÏÇ\5ª¾OkU²H&»û€Çx†Ûêâ,•Ú[_4ý/hÕƒGÈÜzhBM玩þ¢ÛÁ ÷õå¹îqQînFum7ï©©8€Š[ÛX\¿‰ ~Ñ,ÌLìN’ñ¤ŽmëÀ Y÷7×áÈÏdÇú2}ÃèUÛj°›¯y=lŸŒº,›–YÉÊâ\lçNzßÙ=u®§ŽÐãgËè5ñ4*n|{^Õ¨ú>¹q¨í§†’ÝÄ µÕ‡ášô‹Ùõ·žë%®áì%µ.ñþbd_Ÿ~/®®¨œ÷;˜ÔÛôÔT@Å­_Å 5ֵ Oö`;\VIvNÅ”w®77õFÕ+v¼§ñ vuÕY6-³ª9d‡ÒƒÄÿRqÀ¼*nLã0ÜÄU×÷K‡I*®;Îß^l«Ï§õ'ƒ,MÅÅI­L|¶§«^­¤<–8lÞÉ]Û {j* âb,nÒ ¹ŸÓú/< ´àï_ÚþcšŠŠ|¶¾aø)ž:üuÜX\ø‹c8Wî€ãTfV9‘vȘ¥? ÷5šìJÅóŒÅU{çMܘú>¹q(7Vµíä¶:ž7¾@×Kê@â“ýÅȾ~ ³â>¢.«º¶ÛöÔT@Å­^Å…SÉ/Qq™x{îgÂü´)¾N»OO‚Ï­QÙÎØ<ô§WŸ³]ÂÄU½–®ðQ¯âºë£Äï«dB*C™•MdÛÝ&ï›3Z'^doßXHÿœšíCÅAÛž¬¤ÓÚó1Wªï“‡ê÷âÒmBø¶í¸¶:Õºõô÷ââ—·3}ßH—ê/Æöõ§QQînFum7ï©©8€Š[½Š —{š>£2Oÿ`JQ¼”רlžê/t–м¡7uþx¤<—oÔ /¿E+eëqm½Îa³Ši'ñe5Y–\4l0³¢Dž×s+•¼Ñòe/å‰67¢â€\Û^QMJÃÈ«FÕ÷©Cõ•É6¡7°3ØV}D¼¼Ví­‡ÚÊ'4+=–רL'5“ø\ñ¯IYn¿¸dQènÆvm7î©©8èb˜`¡*Nž2è5¡m®ƯûŒ›æ ú#PqÐÓ ‚^Úvaõ;zSqú#€Šƒž^ôšÐ¶ ‚ ?¨8=½ è5m» ú#PqÐÓ ‚^ж ‚þ â §½&´í‚ è*î{úû¾K]¹óéB^?-|<~; ðšHòžýd#,û.zM§³!i°OèGrïÔ8yͦ¨ÇƒÉ‡*§*޳ßÛ%÷ O[£ÙH´Ý`ôp—ÃÎݧ ÃÏÑΤɴ¥öKM¦¿¼‚%–)ßë¿éßË…lÁH•¥ÌfåÅmóVÒkBÛþ_Ù-ž§´½…¡íp3’i|®Þ¤G©Š›¯šx’­}2U+ê&Fuè8wWuvTß[õÓÏüó¯ÃiîuÓ²8×Pq÷4cqç.<بôãûO§^y×J~ý%Ù!µÝöùȱmÝ]{ºäÔâÜ7}:ÕQÿîx{apB/y»ƒÏщ*ñ[f6UÑ#W?k픑)ž3œ¼zSÔ¤gÌ]J°ÿÝ·ç4Œ)ŸuÞzM<‡ŠÕöÆ W®A(G{Ë&kR“^ù–Z:ž¡Ö~ò… é&ê;ÁdŽ·q5iŽtõè|/ö#T@Å=«Škæ3 ÍÓ›Ðèó$ 1¼çcÜLÅÍò©§¨9gf—¨&=¹äåT\¦,ű¨8`NwyÛ[×ø öµ ¸I“žnš&Å39‹í&&uèÍ0ãéCMšã.`Zú* âžx,®üÞytÂtþBY7þwÿ±¸ÉÝsÊȹÁÉË“W5Wí|ô~L-”œrYJN[¢â€û«¸êÆg°ï¨j|®Ú¤'Û¥º;ÞYÅݦ›˜¥C¯.É.à’,ÎL¥â*îQ{úÔ„Éä™P=¾æ‡ÛàuóÄ{qEí×é5"USß‹ M1ñ…‡|W7pNNPqíUÃééGåBù½¸ *nd^è5AÅ4 –¹/¼W¯â¦5Y7iÒ£ù{É;ŽˆgÔ{q‹î&.ìÐ狎Ԥ9«â&d1PqOÔÓŸVvè<šµ.]Òjð÷àx™©Á¾-XZmû5\óªzµ«òšÎ嫲¦è/>6èôœºK霾MF¾7]5žÞ×d.¤V¤LEûòR3£rl^è5AÅÕÅ5ë4f–¨m|¦]6>WoÒ£v)ºãa}ÈúxF®Q¹änâ½§Šk»’n0!‹Ë‘Pq÷°*NìÏ£×ÄÓ¶í¶ý@ÅQq‚ ×¨8AôG â@Å ‚^ж ‚þ âôô‚ ×ÔkBÛ.‚þ âôô‚ ×´í‚ è@ÅAO/zM`¸m°^ôG âp+WóNüiçÕ¾:?ù½ PqÀýø}‘„5@-¨8Ü[ÅMP2K?5;QqT@ÅÑo$@Ŋì*.µ5ófØ——Í÷_Ógv÷ŠmOËmÐ<ça£ÕºÍFOQý_ÿOO)õöíî$¾ß ôa»Átâ¡*ž1³¯k³½igXŠžRÅQn*×Rqß:IšÚùúËI¢|úù£•^'5²“(­øi”ÌñÌà´ì Vmœm<»¿…Ó.‘mÚš›F—ïNëˆÉ“Ö:D¸»é§Ïß>?Ÿï’~üžŒŒž1‘°îÝrŽŠ£â€'íeŽ0Pq¸ŽŠÛD¯’“›ƒñ_gæfNˆ3wÉ^›Åa:¶ðé¢'M>þ¨4$£2£’Šž³sPq¸‰Škfæ¦2vç^¢â.‰3/üÞ¿žµS3˜6.¶èñ'ª¸øa©8*x–nÅàPq¸¥Šûuû)V_ûWÅNó ;cM¨¸ ã, ßÅ[o(¬6¶äãSqT¨¸Åª¸ðŰäDÄÊw؆U\}œ­˼×~ýö=ˆ«ÑcƒŸ¹*ù^\ø,Þ‹£â€ÇîGŒ¼‡{ª¸peÅVxœ×u|ùôó¶$É5*ëgTÖÄy:mhÊîöRê%ˆ«SqùÇœ]­QÙ>‹5*©8à¡;‡»«¸Õ‡ßRq‚@ÅóuÄPq âæÝ¤;5'T@¿¨8*N¨8€rPq âŠH8*N¨8€rPqTœ PqÀãé7@ÅQq‚@Å‹ï â¨8A â€Ut$€Š£âŠ â@Å ÌÑà|PqÕ©XŠ“e7ãq~¶€Š{0~Ç¡£ûËq38åx ;|PqTî¯âè@*ŽŠcçQNö¨8*Ž„ûŠ£â¨ *ŽrPq8îPmçU(|å@ÅÀ㩸÷×ýPÍëûìI9F|¥È©¸bÚˆ7\î(_7àÊ…j‚Ò»š8¤âè7ÜÑK^fTx²òÊ­Óçívs›íÇpL-Pe§SÚ#çxöÿùÒY“ÿõ­IGÛ8Žc¦âTPqxæò¹“M:ÛlÿH¨¸Ãßbꤤúk‡ï›í{|Nö’®ú;?|;¦&óþ”ÃÙÍ*®“ u@Åä%º4²©ÕO‰±¸âçðëIs}”ΉEdoØ-™ëÄœH2<†F¢â0T ºi„rK*´ÞLÈ!×Ì”Ì^2ó‚Þµ»‹Š³a7€Šh$<_Ag,.CTqÇKv7,Å%b6,E‘…¸OY~/.þÜh°¾&«Vqéñ´X˜E1?õ{qßTðøÊ8Ä,kT&ÆßÚù½ÅLªgTžWNÙlÊñ$Ž,ÓÎÆâ âÀۦ⠬ÉÎ×PqFÞTðŒ1/P¡e„•ª8y â”[,]ÅùÙ@Å«ñ†ùmP´ž\ÅÑo*àjså!ëW âd€ŠVé?Æ-@ÅQq£TœÁ7X¼yŒÊ÷`ÿ—¬æ¿³{ýîH}„ÑniÜò»°SyýM¯¥â€Á€¨A¨8àé”þOžªÆ©¸nT×Pqͦߣ’}gÇ …–™­*p=w>Ò­5›|‡§ÅÏceí YGŵCiÑÔ\µé ³\üåÉëv-ŸpÓx qŒŠ(¶¨8ÀµU\{ìð}³}?œÒÁƒfëÄת¸æÄÓ•ýÛw‡×2ñŸ¿t"­âÆßt`¸.Vqß@™°@ÅÛUÜø^zA…¹ZÅÅ#`¡rÊl•ÑYl…£y :]r<~ü’]ñMcQ5VÅM¸éÀ”Kcq Ln`+µ  â€¥ô©};<ŠëɧÜÁžLjŽL›™¼iO(þ˜o¦åÀM"9z™J´ÉT@ÅT÷Uq]ÅT:¾wÎÚlÆâÊÃeqü3ªµ 7å_B›LÅ â`*®3ñGé`wEÊî+gƒïÅõÞ?ËÄß]£²óbÜþK7’á÷âÆÝ” *ŽŠ@ÅAW ¬BÅýèÌ5L­9ÙŸQž1´FåyUÈÞº(™ø3ûÅ#Ùl·UkT޾)]€ŠÃòú†»t¥÷ê¿ù |Dð/¡†ªe¨8èGÝÊ6ÿ’ 5T-@ÅAÿÍu€Œæ_B …ZPq€þ›ë Œ 5T-@ÅA÷ (äüKþ%ÔPµ }' œó/¡†B-¨8@çÍ{xªb†Ûð`þåa/†p½½®À~‡pGÀÞŽ…:*xßËlõÞÿu”ZsÕfÓù[ò¦j@Å&è·:9*ˬË×Tq½UB7Û÷ƒª*áG82?W#®Çâ×>¡â ï¦âÔ2€ŠÓpTœòüd–L½×9ÒR»sÎs;ïŵ¯ÓÕɸê÷âréó^´uTœZPqzOGÅÉñç³djÊH8õçF6‡6Ûíëùøþ`Ga]6£2kkTB[µ  âô â”g–¼>ñ«tÓ#²_ÔP¨e§';/ýÁ•g– yûöåË‚%ÿÚ:µ §'XôCQqì,ãÀ¿„ª– âôkz¢ ©z˜¬¹åƒðlX’ 5j@Åé @Å)Ï, þ%ÔPµ §'`çŠóeòÌ’üK@ UË*Ü,\öLPqŠÏFË0é- ò§%¸ÆZ)üK¨¡T*îy{‚å\=y{³ âÙ,Þ’÷Zø¿ò¾ÙÓ®´oÿÚ:*÷Ô*N Y€¯iÉԮߧcÇ#½ºí÷ýþö‚kÎI]•”Pç ßöWôN~ÿöòïžOk6OíÞýp¾ÿ!y?ú›…gÏ¨ãø—ÐÖQq¨8=ÁãØÙÚ¡Êó¢ò+’.tÖ8G5v:¡=õýµ9¡•^˜RW%5\ço½ï»Hþô—ÿ÷µ‘l»ï›oß6çÏIñÖ{„ø´ÝÿÁÓuî8«Œã_B[GÅ âž±'XKúG¥S÷̳Y˜%cá+©Fô$UNòµ´Á«ú0qÝá·Žêê}.¨¸&ÖÎÁíî~„‹Ò6§Œã_B[GÅ âž®'XQâ©8Eâ¡U\;å±;=2<žÔNÉ«bzÓ#ƒa¾ÄûjYyV§â:3,©8hë¨8TœžàySNÅÝæÑx6×±d;ó1£dâ~tÇÐrcqcäP"¾fTnFwŠ÷¥;ÜGÅAHÅ âôìÌ켦œJ½ןä)›`fcçųV“q™•ý¶ ÞÁ›UÅõRê½8,°†¢µ  âxÕ`çE›‚¯fÉÔ•‘hëÏlW°xdG ͨ V¨|ï%(ùZÝL*îG°L¥5*±`~_ÇF#,Ò Di¨8.õ=¼FO½>Ít÷Ü–Œ_¥ËŸxÃçì*ŽŠ@ÅñÕžKÂQqÊ3K^AUuV ¹oß¾|™[7Rqx ¶KrJ @ÅñÕ¤–Š[b>Rq,¹:;ó/ñ`-†ÖÇWcgÖ`g%–ŠVÖ\h@Pq|µéti·1;³$Pq¨8¾šDz*NŠƵÚTܳûjÑT>…nOiaÉ!2»D\c1* âPq|5v–Êó³Yòò¥ú«b¸ÒÆTÐ+Àe˜Çë}|;?a^ÌþÈÊóÕ,™Úõûtìx¤wB÷¢vóìÞ9©«2ªì|^fÿî01WÛ¤›Š´À¨8mî@©?зQq«ÎšH TU°aÛNCNhO}mNØK¬Ä4ÈÔU v§51n«¸Nb:‘-[ÆQqФ÷8mîãÍÄзÝ&£Ùù:–Œ•PïHOgŲ)ù2ÛàUñiɨz×–¿Rq€ž§ÍÕèÛØ™Šk'<ö¦G†ÇcÅ•»*§©8@ €Šƒ6wav–ÊóR-ÙÎ|ÌH£ø„á4ÊüX\¾¢â-0*Ënsµ|®gîüf|v>Ä•,™z/®s %ÈvçœgBvÞ‹k§GNq™÷â:Ñx/Ѓ â´¹:*ŽŠ“)©5*#­ÕŸÙÚl·¯Á’]‘54£2¼{~ÊŒì³F% @ÅisµþžnÑöaçõ—ØøUº‰±Ø/ÐÓ â´¹`g*Nެ‰·o_¾,[ÂQqÐn§Íeg°3K®ÏÎT´@Å­¯Í}¼­á&ØYÏLJX %q¨8å‹J *wözŸ§1¢ânSXòüŽ› ¤ù}‡ÁΠâ°ÇÎZäíÀ’T-;³3@Åi ÀÎT·ÂÙ‰T´ ìÌΧ-Ðy^v†Ü‡²vfg€Š{â¶à  Â#kŽ-È}(ì vîE·ÏÙúPq·)!Œ©äÊ;³3@Åi @ÅQqôt`gv¨8m;³ÿuŠ%{Ð2°3;Tœ¶`Î=s»£Í¥â –AÙ`g°3@Å­©-xr §ÍegÈ}(ì v¨¸Ù*ù«çÚvÖ¯±<ƒgeìÌÎwÏúŸûŒØÙå,½”ò 9eƒÙ â¨ mÍíTœÒxyá¡âè‰Ø™*€«* MÙÎì3Wù¹MyÀë;³3@Å=¸ŠÓÊPì ž ” v;TÜü­@ø/Ø™Á³²vfg€Šãõ²sÚÎl>ow¥°¤\ÐÓÕ}PqÊ÷Ýmõûv¾—Yrù¥J¬\ÐÓõe âômÚ\vfI= §ÓÓ±3;ëË@Åi ´¹Tô| â §cgv¨8m;SqJ;”X¹ §;ëË@ÅéÛ´¹ìÌ’z>úrAOÇÎì¬/§-ÐæRqÐóicåôtìÌΧ-`g*Ni‡+ôt`g}¨8}›6—YRÏèéôtìÌÎú2PqÚm.=¨8è騙*N[ÀÎTœÒ%V.èéÀÎú2Pqú6m.;³¤ž~€\ÐÓ±3;ëË@Åi ´¹Tô|ÚX¹=;³3@Åi Ø™ŠSÚ¡ÄÊ=ØY_*NߦÍeg–Ôóz:=;³³¾ TÜÌmÁûë.¢—×÷ÙÓwŒøJ‘¯¯Íeç -ùÔó¼^ê‚¡/7¯Ÿ;ÊǽšÏýÈmîƒÛùú–\½õ|ôäuÁÎЗŠëÅž>o·›ÃÐÄfûñ#ªœÝÓ)í‘s<ûÿ¿|y y}ëDÒq™ƒ?Ç1?F›ËÎ3ŽÅ=¬õ|ôäuÁÎЗŠË8ÃÓ»Ùþ‘pŽ?ú¨'µ?^qø¾Ù¾Ççd/é:Õ§ã‡oÇÔäbÞŸr8»ù°’6—/´äSPÏG?@.Pì }¨¸ˆÆmÝÒÄGñsøõäÊ~”Ή}óÞhF<àщ9‘äå·¹ì|¡Š{VêùèÈê‚¡/—sLÇ:ÄIÇ·7ÁlÈ9n& e/Œyú+L·msÙùB÷¼Ôó¼^ê‚¡/1ÏG<41è/Ùݰ<Ä‘ˆù‰ÇâÍÎ7‹[Ÿõ|¯—º`gèË@Å¥~Ý(þܸ¶}W·Ú9NSÄþnó¿÷tvžû½¸4 ž~€\ .Øú2Pq•ÎrŰF;ͬ·FDõDµó‚›M9žÄ‘‡hsÙyþ5*Ï€z>ú×Ì…pÂôåæ…‰ƒçS?úíh­àzÃRqÓ­ý~Ãò©/ÇÃxª6÷¡ìÌ’z>*÷Ë…Þúîë…ëX¯—Š›`*îzÖ>~½çü}¨8PqTô|ÚX¹0A0t¥]3‘»³±dw_“Öë˜ ÐÝÐ2J@{/*®ðöròÕå;ëºGQq½)³ïÿ·R¨8Gű¤ž¸_ *²žŒkÞ¬ ôÜY,4Âa÷·Žè+«¸â†–Á¨¸z—Ú”Gq‰µÊð}Aõe â@ÅQqr âBç4z™öðíýµvœí²Ï»WñŽ×ÿ^\½ï9ðiTÜ'²êË@ÅŠ£âä´±r¡á£;Õ1Ü%$¹îѼŸ+']Ç=~ô±¸¤äãQ\¬âÆ–õe âxTÇ’z>*‹È…x+‘×÷Îr'WTqÛíµ<äGWqwžIùx*nð½8}¨¸Åôm—/éÛ‹apBCMä·h,nÛæN^Ìw­æ[Å-s5ðÕäŽžŠ“ é*Ù›/.W¹«‡;ÞëïLŸ¯£Gþ½¸Ô TÜdk¾Rq âªâ.YÒ7ê„fXdùQUÜ„Å|×jÞYUÜbW_Mîèù¨8¹0ò¢XT]YÅ머þ ©•X;÷k$*n}*nÔ’¾³w`ÍîÊ7½`ÅÛ»ª¸ÚÅ|ßVkÞYUÜbW_Mîèù@ÅMQwËÚ“ì©Ô;C_*nV—ZÒ78¡³FÔhG¶ýÒÖÈßô¢.öÎ*®v1ßµšwV·ØÕÀW“;z>Pqã5܈8ê‚ÙxpwÉ’¾a —L> ½ìÊ›^ÔÃ.[ÅEC4k3ï¼*îÇBW_MîèùÖ£ ¨ v†¾ TÜuñcä’¾ýQ„`ÚÙŒtAg®YÅ /æ»VóήâݶœÕÀW“;z>úA.ðzÙ™©8àiU\òEk¿ð=xÓuª¸ÚÅ|×jÞ머 [ |5¹£ç£ä¯—Ù™ŠžVÅ%—ô–éë¼´ÿÒ}gøÕ Þ{?7]¥Š«_Ìw­æUÅ-v5ðÕ䎞~ ¼^vfg*øñÄkTƯ'å·Ìj§¿m«Þ;Ÿ^^pð¦«Sqó]«ygUq ] ü/¬&wô| âx½ìÌÎTðH*N[ Í½§/µä£¬®ç´Àz:v†¾ T´¹Ï âg5p=ý¹ §cgèË@ÅA›û *z>m¬\€žŽÙ â´ìLÅ)íP÷傺ÏÎì¬/§oÓæ²3§ç£ ôtìÌÎú2PqÚm.=¨8è騙*N[ÀÎTœÒPqê>;³³¾ Tœ¾M›ËÎTœž~€\ÐÓ±3;ëË@Åi ´¹Tô|ÚX¹=;³3@Åi Ø™ŠSÚ¡îËuŸÙY_*NߦÍeg*NÏG?@.è騙õe â´Ú\*z>PqÐÓ±3;Tœ¶€©8¥ âÔ}vfg}¨8}›6—©8=ý¹ §cgvÖ—ŠÓhs©8èù´±rz:vfg€ŠÓ°3§´CÝ— ê>;³³¾ Tœ¾M›ËÎTœž~€\ÐÓ±3;ëË@Åi ´¹Tô| â §cgvBÅ¡žKÚ\ÌbgÆY~iôtz:°³¾ TÜíøÕ°óìÌ>Ë/í𫹞NOw_B?ž•g€ŠÓ°3'@ÅQqê>ÇÎú2Pqú6}¨8==žŽŠSžõe âT @Å â@Š⃎\ë&ÂM[d"  â¨8A.ôxZ7AnÝ"rÀ“ ¹¥½+ËÏ„Õù Ü­› wh‘ïëÀÑÇÏ„Uû :r­› 7Z5x9ûÈQqÀ};ýgÛF²ãçüë—×—Ïïÿú[úëØ»ü·Ï_ó„›^ø,Â4³/!¶{ø :r*N„eýÞFÅTw‘/~m}0þ»'à!•XùÚ±1ߥäÌû¾œÆ”ŠáŽ-òï¿ÿNÅ7L¨áÎ*î4€Œ¡ýë×í§—xTíýëé¼×ß"·{a/’ä‘Ïïÿøy“¯;ß³ùþk|þÛ?ÿ¼ÿÚ¤öÓÏA´‡¿·‹ïÕ‹ÿ5~„Ÿ¶ÿø[4´ØÜ½Òh¹‡:ÇüúÛßCí?G÷V­ç[œâ-gD:†ÌS„E¥÷8‰,¾NÉY’Ï #_æoT-mEêÔ¥àx[LÃêýùíï»òÝmâFaÆ_fÿqeöŸC®:¹bLN­ÓòÞ±]hèD_oo´Zéú-ò]„ðD=,îm«*—jí?¾ÿtrxv½Ã×_šƒN88å.Äîœãù» O‘}­F}õ®mŽ7úªwþñë9=ûÓÚh£g‰¯âߥ0ô÷ÂL{D…ø“F+7µâ)š„¥§ùZ%gI>ƒ¦mÙ3 ŸgÂÏ;H¸ŽÒ cØ×ɦըI¸õ¯Ï2n~Ëd/sà~¸cXÀwÏ»›´È„@Å=½Š‹\šÐuéœø3ƒGòse/;¿ÿu—¤îi•iÚa‹»æØ2ùø“FË>TxfÒÔ‘“9Ø«–P餳~ðq¸딜%ù š¶e«¸àsïwšÓO CE°ùÕ¡ÿ»N®ôÇ£á½ÁôîøûKÜèTŽì—µË «7ï\õß¿ôÎç¤#I§|ô°{bFA²Å‰./Ïd˜"ò’Îñ¤å£I ý ÃF.Î(gß|V: A'æ¥üå?òsQ 6Ë<“Eª8~-(,NÅÁ©¥íÎNz©T³¨¸Î}kT\mYÄñï»æ³ÀÆŽâKËäT\ÊhƒUúM »…ŠÊúáǹNÉ¡â0b,.7þ~“˜9Ù~ýüÆSRqÑhx®éiÚ—ä0÷à˜ø¨A튄e†ÝS"37¯ ÖÉ™”OvjËJ32•y`ŠH<Ͼ`ùŠŽgØÈƒç NÒ˜ÃJñ¼”?ýû¿•† Éžež Pqë±Õ•Z¤q«›´-íyŒkÀÝŸUÅ…P¥±¸pTgÌX\2þö×ùâÌ©d”õPeÅ’ñ(®<Wð çj%‡ŠÃPëö’éJ—³ÃHBîäÊ%}Ò•­8ˆ?øûÓ,ƒÚÃMCŰû`«‘žÀP7'¡¦ª—Ÿ"k¥ü¯_õ-f¿OY~¸M¯1òÐ9£ñÉVŠæ¥¼ýó¯Å¹(¹dÏ4Ï„Š¨8*®»Š[çç¶æë”fheË\~Á{q…YŽç6?÷Ë]øK_ö½¸Œ ‘Œ¿éŒ¾.½ÕIOÖEImð¡®¡â²–OÉÔÚ§ˆ?i“‹JNÉbß‹£âÖ0WùþR3,vt^cµÐ(Tý$“įRqjWÉ•‘·œ,Q˜“p70ÿ!¡&ª¸©“@j<8ÿáWk¥Ä¼”¹(uÉž6Ï„Š¨8*®·wga¶Dk¶«Í[©iðC/e”_@Èɒ൑í×î‚[aü‰5{K…eº¼(þF²¾dNd™BüI£•*Ûÿvï;ªûνlÒ¤|è}õþS$—3 mØyJÉé%o‘kTRqkSq¹÷â†dÏè÷↙2wÙ vÕXܘ[¤â£H’Ñ^q,.¯äo`·Vq5F:g†±¸:+忥¤€¬Nö´_(­nPqTœ•*Wi^À€O)\Öå TÜ“ª¸ô•½Êß‹«Y£rŠŠëþ´SX]÷ÒAí²Š+Ž›¼<˜ÌPz/.«âª‡Ýëßøªn4¦ˆTü0ñ½¸ióòÙ7¿•rCÐõó.¨8€Šëð~èû^ßgOß{;Úñ¾[Qqw]5z©k‰¯}«q*wVqÉýâRËÞŠ÷‹‹×A©K©ÁôfÇÜ•ƒ#ûõÓ!r>tyܼ<Ü—ˆO­˜Hù´a÷Š!©Ô#¤¶ \£²<]$iù:1?`äÁsòÙ7»•:óRÊsQF%{Â<3*g‹› ô®&©8A âpk·‚ߊü#˜—²îýâ¨8Pq(´Óçíö´…Êö£3¦¨²Ó)í‘s<ûÿ¿|é¼”ûúÖ‰¤£í‚?Ç1Sq‚@ÅŠ£âóR¨8à™UÜN65êl³ý#¡â?Š©“’ꬾo¶ïñ9ÙKºêïtüð혚\ÌûSg7¨8A â@Å ‚@ÅÏ£âÙÔê§ÄX\ñsøõ¤¹>Jç†þ:J1s"ÉTœ Pq âAx$PqÅa¸@#PnI…Ö› 9¤âš™’ÙKcžþ®ÝíTÜéíô`±ûåÏô©ÜjÚô¥YT ¯aðQûß}Ù¹®h~GÅ ‚@ÅTܵÇââ1´Aw¼dwÃòX\"æ5ÅMCKxdBlKPq7|Aæ°®ØK»¯Õ’UÜ:ߢâ¨8AÌ¨ÔøƒŠx/.þÜh°¾&«Vqéñ´X˜E1¯ç½¸ÞêÊÙm`“[EŸVáîmñºú/ÿœ³åË-ŽÝ]vøó~ÁásòKCw×è>i•T²7ßîÿ¤q›Ú€7ØOËX¡â­À¢âO=Ö?ѳcZœ5³êgŒùîåᆠ â¥}Q¶º¿Š ‡ž:»ãcaïyÜ&÷06Õ9§?ÀGüÚÿV”ŠéV²º @ÅQq3ÅåÛó° ?ö É×ÆÒ¿ Žw寡â†Çâ nñ´‰*.U0ê5ünHó{kÀ~ÀðÏý ÓY3Feõã©°‡Û©¸šÂ40òž/úÑ „ô òK²'¼k4ê­8“gëÍhÞìD…bspƒì˜–°‰*®h–‰Tê.¿´T\`%*î†í$*ŽŠ«ýý®¬âŽM÷÷è7¾ŽThÛöD´3ª¸à§Øî{q#U\þñ{+@̺¬7.~TÜêT\8Åý%êz‹š¼t^f‹ÎÙO¶/G;›ŠkÐ_£r¤ŠK¤3³ZéqV¿ì¯Ü˜¸cðšÉökw²ÂÏ ÐúÓÿøŸÿ«}å5*Ûv‘'¼‚°$ŸÒ|¿_±à'‡¹Çâꇉ{ѵ•sÊÙƒ#ò…ûÖ\žüA®~À•Ì;AÜ&;î¬âKÈój®T*¨8*N¸²rãHÅ-NÅÍ’“íïv=ÑS%<ÜîDš¶%z'Ù·zsÁ'Ìè¾Ø3îE¬êìÁùÔ}S?ä”ô³ ̸’yÓÃ÷åy¤7ÉŽÑ §âR3+'Æ\8¯f„Š«*­DÅQqÂxwËB8þFív“·Ï¦ânQ [õÜgqãêµ@Š{T7´ù`•…Ú¿}î¯Î7jQÄúìÄn`íˆ|óS4p_uy°dbfî\òŽ70orø¾ n–c6jí&ãò†¥2a^M6…–Š1VšOÔQqTÜÓª¸›•Ñwéí§½ûšÜçŠSŠCMë6‡×Ø®É+,`Ã:Ù±âP¹ªÕMxBQÅÝFȼER­;l÷6ïÞÿ<ÒŒàõvôîíÌ^xì;]zÿq?*N¨8,OÅEó÷„{ª8Ù¡´Pq¼@*î&Bnlü¡¼Ê«»óYq¶Ù¾ôXOžDZ(ÿßÝ·»UoŠŠã‚°–ÙŒJí䪸kÈ ‘ƒh=Ù6øõ¤æ>²ã{5Ÿ©8A¨8Þ‰ T',©Ìßx¹©1ŸGÊN’¬FÅEÚ/\'…ŠK¼«,^É4‡ŽœsÎÎ}²¯ßã÷–wuÓq[ Ï÷8wÍ8*Žw"GÅ +£»wi/¨¬Þ×üLÊ 4—ßQiçxÔ>´cô+ €úh¯!þõëö^ë˜EP-_ÅÝ%ã¨8*N*ŽŠÓN>I]X@imæC…Õékïŵ¾èJ½×ÖfIŵqj5æôšÏ»Ü,5<°^qÛþÂfSœTÌù„Ëbç—Œ.§­³8v»fu³\öñ`ykìøqz—ÿצotPqÄO×3i°¼sû&Á†ÉGhuØp¼s÷Î ï[Ì‹‹ü *n*îÂÁÜ¥«¸¡‘ý;ª¸§_{nãL ˜ôSqTœpíº°Æ‘ç§Vqûafè,<˜Ù4¯â¶;¡ÕDŠb¥âÒ'ÓVãË•3Í1Lÿ(¯cÿŒùÜþµ4¸jìñÁ“ûƒ“És»¿&oTçî´YSºj󢺨Pq£âjFáÓƒÅÑ_{S âB¼¿0ø|, ƒ£çÙñýµÇìdžÆÁç…ÇÓSó+¢;Ö ÙÇ•›8‘›2˜ï³ÆåT.I¹I&×TOØ(§§8‹cúD 3*©8Š£ân­â~é.ÓïeNp!’NÏ’‰ùBWNÛX×IäxW¾|ºŠÊú4ôRù.¹“k“‹dèFùÌj7þÍíŽ)ÆTÜӌťFá C癩ñ@ð~†ÀK;[ ùP=Ïqg&LÙ®Ê ¸_m>Ýfæ~ Ù—sjlÓŸ1Qͬ€)9!C$ŸÒ·hN.gbMzR³8.™hAÅQqGÅÝc,.ÿ;Z¹?Š>|„Ãqå˜/Qq…´ëʃ±šá±¸èq/§âÂA­p,®üŒ0| €áCã×óŽì׸_o¾ÿöÐpüà9£&NÔ¶ò÷¤ 95yfHmó]g®qœhAÅQqGÅÝ^Å…?ä]¬âN®öÙù)Å<ù½¸q*.åPuîΊ¾—¾¼ìw¥"? ‹¦Ä?³>cæ7åÞÏýÉãÉ»äN>'ßÑÞ¨˜q¿â{n .a*ŽŠ›¦â¢©‰øãÿ6nÂMU\nüú£ðÙÉ…;Ï¡âFM¥(ÙN.¨TÜZÚÉ~ÕÌ¿}¹õšCÉô̸.Ѽ{jQqX‰Š g¿Ç{õŒÊÞ’'—ºó”®*÷BA~ì¥S©‹Zð–Áök¸ ZÝ•Ñåï’ KFÞ{å!óŠÊK©Ñë>BÐPg֢̮]ùRqrÊ&ùW'ºWo6Ãa [Zcgò"_Œ©8*®ü×üp\îk3“mÌ „[ÅUŒƒ_c¾ÿKÌ`lCç\KÅÕÍ XâXÜø~ʳ¨¸Gj'ÕôöÏ¿^ø²øuUܼ[`ÝlUž÷ڢ⨸'KŸ:Ò"¬/d^5²_7v¾Î#ϼwüÍ })ø``ô¼8P^ZwÜÈ~âªdÂn1 Ÿ{/.Ÿ‰ç$gS”N>œsú­¨”¼ÊY£s*ʬéïÅõæ—2Wýì…I-¨¸¥ª¸ãª¶ÍªHåÙŠŸ?"غj¯$Ïéù(¬K4¼ŒPþ÷þú=µÊ¿Ó׬c”8gÒâL#·E¢â¨¸GÛÆ†ÂyŠ¼Î ÄQqT܈QøQ3ëâe*s:¡rô<;P^çHUŽì§®J&ìê£ðQÊSs?††ìK³)jÆ‘^™X3TX1+`BNõ’:yÊfaÉî µæœÅ1y¢ÅMzb3*gðÕu~5(. ”|bÂÖU£*¬©3aO­ò«õëZÀ©òõ*ŽŠãô ÷à*Nü`iE*nT;yÔçÉ­{Fý•˜ 0~ëªÉs¡G©¸Q;_]4¡zÒâLã·Eºª~@=Tœ Pqк GÅݰl¦‹Ï©âÆo]u#7rÝ©‹^‹´8ÓÈm‘ŒÅ‹Š£âA âžOÅMX°gp!¢ [WÝn,npצ¹Æâ.Xœ©z™*ŽŠŠ£âA âž¯ –nʼV\h𽸠UÜàÛz“ß‹«Xwjüë‚í«†-ÎDÅQq×ös&Oë¸öN“c÷æmKÕVx5Ka?ä›%<àÅi â¨8A¨¸Gi's›&—Û)/ ”[×qìÖUÃëe–©âƬ;5~£î2K£gª7GÅ]Õ;%Z"B¦­Ÿ”ÛSîƒ<ä‹k~›ƒŠ£âA0£òE;)²¯×ì®$©ÎQ*ÎO€‚P_žDÈ‘¬X2v´w¢±¸ÅeUÜYB4›J7ݹ¼<óàôýòï?½v ß}h¼´ÝÈD2^Å%"ÙÝâõ·³òŒ÷+sUî`£@Rg` (¶L"=Íñ`õái2¥û ÉäŽO|Æøîå°jË—JN¹`¤T\â§ŽÔQq‚p‘Š{*Ÿ€÷*Tw»±¸ÔÁíΓî U_~$;ðúõä@ï?ÿ–9ÿ’•mJ‚‘œÁ¹v嫎ºEåÜ¿}üu’Œ$—¼šã£ž1{÷—“dª•[©JÎX—}À|:©8A¸žŠã€>§âðÜ*îýõåõ=øþöíË—Î*n.Oó'ßÿ^UÅõVC©Tq©«²CÑ5»Š«~þÒ/ÅäºKÍ3&ïLh¤q–Ï”œÑ*.cÆB:©8A¸ªŠ{6!GµÊP@iÏ©¸Ó—¾²£âæ‹Û}ø‡ãê/ÇR¾¦¦´Í?W7p4xUúà5T\8ðŽÅ•$PªRkÉ㓟1u÷Î+sõ£ ‰ÇÌ–œ)cqy3VÎæ¥âa^÷ãɦVòûùµÀúKûAi}lË%×éËK¨Àšc//›íGxñùÔ@±¥?Rqew\§ä¸êðŠ;½StryÁ–xûë°†DûVÒKÊïŸWÅ…ïAÕ;ý™«’é}ù÷âZaVõ^\Iò½¸ìƒ„«€Ê9•¼Üñž1ÖBõ*.÷˜¹’sÉ{qTœ ,GÅ‘ƒ¬Ö¦â:Êl§×Nº«U`ï¯Í {9w:ºûØ>Ä]·~w»NÊaç…WÅŽõOùÿþœhŠ×xìzÑ+w»©*.\_ñ¥~ ,sUâ`»æa~ýƽ­&­Q™Y(ò%{—Ó”×í×pÇòZ”Ùµ+Ç?cçî‡íÚš«þV©Ÿ3 È”œñ*.0ÍÒŒJA¸›ŠãÀÊÆâ~„ƒn¡4‹Xs0<3<Þ»jÝ2nqûÅ Âw‡{™°IÀµö‹“%‚0j§'ßGÎìJ¢âÞ» 4 —ÕGÅ '5FGÈa%~-ðÌcq±è ¦Q«5*ŽŠ*NV^¯xK â€U©¸¤ŒÛsž<ºðJïÅQq‚°$×ßÐà:3>§îV~Qü£n:-…Á[˜7š)[Ø£ãÚF¾îCÝ$åsߨ©W&PqÀŠTÜÎìÉ`î¼Båö5^ÍÒ•Tœ ,s,nvOzÂr®7¾é…)œºûµtÈ£ª¸ Ÿëšåðz=ß*z}’€Šž¹´Û/ŽŠ„«¸ôú­§¨Í÷ŸS ‰öÓ †‰öÛ#/ü5ËêúÙ§3{7 7ˆ>úùý£áJ£Å›&­>…ƒê"6Za5ÒðIÖÈØ'¹âí¿~y-\˜ELZ¾hó9íi½õj›=:Ãa+×îEž0fêÑ2KåÎËT!€ŠËðöíË—•K¸¥«¸õþB-÷UqÉß¿žÔþ`¼·ÝoçmÎãÝ<ŽÎ}°Õ]檮 ö@lóÒ&ììë'EBâ¦ùQšºö·íëo%‘0ZVºtž4Üë°¿ñbÒªy7ìAËg¶q<Ö¤¹9íxm¸Ûæ`j“)ÏFÞFRe“ys™Š*î¡me,NKÅ¥î_·Ÿ’£s3Ùꯊ%ÓÁÃÞ늯'oû¤1êoZ3×®œÂä V£=ÇšrOš°Î>CïÅ &»>¿*RîYâI§õwÉŽ1Æ)/Nm%—©8ðk¥Š»PÅå&+å¦MÅÎÀyêÍÙ÷ën ÏªœAF*Ï¡âòÃJ9uQŒ*\•NaüïõT\*…Iïš•£›SÅÕÙ'm„b²ëók`žm&UñðWÓÈVÙ§œ°Ô£ÅMù5r™Š+vÕ|&~- ´Sq—™¬T7mêÔY>ïߦùüíÓùsa:Oý 2AxV з°dîk4§nþ±¸:Ÿ¾^°eU\t¯Jû¤ÇâÊ˺TçW:U‘$T¤Éé‘é”#/?ZÕ¼Ù r™Š#äøµ€ÒNÅMSqõRÖ¿ 'ΔçŸAF-Ïñ^ÜÉÏNÿªÑ‰0UI’¯oU¾ÆzIo>yIzü7½4…屸ìOM‡FðÓÃ\¥÷âF©¸ŠdÄ|N¼.-ÿB–üe«sfÞÙÈÛ—K–Uq3å2~-ð ¥}úÊ’±¢É:UÜÐÏôÃÓy.ƒ#k_£òt°3Ãøàî÷ëÃ^h ÔÒþUãVªHyç7íÊ‘),L,6Ú.ÁùÉŠ©YÚÃöÉ ˆe“ˆÆº°Qª‚µ(·ûHŽ«ƒ¶óÚ»o ¾¼ôæ:–ME¾?ÞdS6åÑ]æÊe*ŽT !ꉿõï.ð€*nx:Ï spa‘*nB8/7"<{xÖ T\¡ç¦å,Hŵ›|åW£ÃÞ»Ã28k6ûäÚíô}W¿ˆ÷D—šÎS?ƒL_Åå *ŽŠƒ9™,±´Çš+¥Â‚c; wú˜Sn$ãî¿Få\3*“Óyêg ÂÅ ‚zEÅŠ–^Ú÷k]͉°Ý)›íGâK8j^ò82îYö‹3ƒL âA â(y¬ª´ŸfNffDÆ_ãi–TÜ Uœd'wIGN?PqÀýK{;&j°Î0ÜùH¬Ð¨¸•Å 'G?x —TcÃ2Î{qTœ Pq«\{ãôíy‘ýÁ=žÄ€Ë4ÂÕREÅÀT\…ÊŠ5*Ã?$¦aZ£’Š„§Wqk?åe”Ï8×Pq—?Ú„r—LM gPŽñ§*íö‹£âaa*®Ù¯ùå´•ÇÁµ¶{®9®ô¯ýšÝ8;\1¶øj2©ü”¤vIÙƒþöÏ¿n?7i¢úôóûã©uoÏ7êߥ—Âô¸fyû{»“IíOùàœý(ß ý7=Ǽßü䜼ôF(±õòÛ,Âûú[Fääb –úýH>~ïÑ:i8* ãäÊØ9U‰K&B*îâ~œ â€•–ö·o_¾<„£âaÍ*nç³6ÞvjŸÄЭïi·MÜùÐñŽ5Ý¿&v„ìœJj7‘'©ÖnïØÜ¢Idð8™G8+ä]²ÛMF»IæÍÒ3æî’?ýû¿•M—Mj£|>}þö©QA©dÇÖË<ìî^|zÿš‘F¹Ø#7«ú–KE˜†ú9¸ÉLy·™É…Š¿PÚ©8*N–5£²àï&ŽüÚŽnµîLºÞvtdPÕLrKOkÜÝ+¯¯Â·qñ#TïA9x—‚Y ø"9Œ„ʯ™¤†£­—~–_;ã–Ób ³#Q*êòº²@V–±7¦Rqà×J;WˆÀåU\¼ŸF•Šû¥ûNpBZëU¦ùu Ë[TŒ’5Ã,ûÉG¸TÅÕš¥6ŒÆÖÎçÄV•I­·ÞŦǖIæÑ E«®@Ö”1*ާ@Û‚[©¸ßqa6ái‰T\8ŽÑ‰GÞ†Žô_šêþµ~vbi¨-%ªUÜùsþ1«’18Wg–£xë |õ'(†ÃMW°Þd—-ċҜ~´x¯¦@¦RU.cTg €†Tð(*.ð¼'¿w‘Škýþá÷âRIM&2ñ^Ü9ÍᚉG¸PÅÕ›å¨ß¾î?iGê´Æ‘Ôl²#ëe® ó:û^\:¶àäæ½¸ò£%DcEì)ÆZ]2¶Rqà×J{ÀÚ×;¡â€Õª¸p}¿O?o—½nEÊf%×h>a0œÒ=’Ø{­zyÀdRSÓüzkT¦EBö.RqUfi%e(f¢sBãÔ'57ˆY¯Ýï.õ¦ÙÀ•¹Øâå"˧¶²@vbÈ—±0þ®='B*ŽÆ`a@i?ð{èËf¬«<<ÁªAñ@ÐÃoŸ_–ŸHŠÓW1/°îÒÞlâ݈­ƒðjo¶íÉÛMêäóáö܇Ø\_¦é·ý¸ókŸ,1µ© ~§»Ûó•öÄ:J¯½;‰­ƒ€ëÊ´Fõ§„'§¢h•Ûjeœ¾L» *N¨8ÀÒ|ßP`5Ÿ{ª«ùºSiÍ`[òäÁ¨8Tœ Pqœ0¸Š{ïîëCÅ âŠ!ÇžÀ¢U\;¡rj T´Ø âŠÓ1ËJ{ö½¸Ôâ$)—TqÞ‹ƒ Wq%Cº+¹·kÊǃE꫌ ûûÏ›ÞbÉå=†¶Zž²ä*;¨8€O\¥´gÖ¨lV£ì¬QΪ,̨´F%4×XŠ öqN~nôØAÂe7§Þ]òéçpÏë±+4ŽÕx³kBA âtil¬¾´Ï#»ìÍ5îªâözì¼cÊ÷_KB(øüþ5—ûúËà6Ùß:Þ¢ù½ÝñÃ9mû“ãí˜[“‡Wý÷/½ó“OZ·¿¹ Pqz5T\·o_¾¬VÂQqÀšUÜNö4ãcáðZn,î¸Ku¬Ö·ÄÌÉöä½üû Ê•TÜËËñ´&aݨvRð¤$›í³Ã«b šzÒ¾MF+TøÑçm¥/Ö©âç†ïÅ%ÕQbÈî0–•;¹<˱7׋¡—³\LžP¸Wîü\´D‹@Å_ (íT],HÅuÖ/)ŽÅÆG~û|r±ôúžã¡â~énêR¡ââ'M?H-Ñ"Pq‹jÂôsœ@i§â]<µŠ ×#©lÍÄÈø½¸!=6ú½¸±¸¡ÔF#„ý'#IF+Tœ®ŽÅ¥Šƒr‹Å¨¸`¹ÿÁ÷âúk¢DkTö–øïÅÕ¬Q9¬âÚ«ÚتT\úIKïÅ þðh¥}Î5%­nàN*.ÜöíÓÏۯѻañ~qÉÉ™‡ûÅÅë  ª¸ÓM;k¥Œ˜Q™|ÒÔŠ”Ñ Gœxzg§wUq‚ Pq«nÑ9%×lïÜÅ»·!x¸íws°C_ Úõ×üÝ âŠÓ>«MÊ_.ü‘Äjì>m¶'•Öë+²Ý_ã?vÎ[­ŒÓ—éÂ@Å ,×3È}Ý'ŽVs¤7(wüºVĵ^îÚË8} *N¨8, uÓRqPò/Rq?š)•Á¨[€Š*„ÜMÜÙøð*®Y­âÎ×µ£s±B£âPq‚@ÅáŠ>S0žºØ|/.Vq2Î{q˜æ‘kAÅ  z´<<å£Ö¨L¬PÙ9šZÞÄ•˜åW6€Š*~æì8 á¿çxìTœ Pq ä®k¦ÿxvÞ¾}ù²Z GÅTœ PqàÏQq€ZOŸzoà"ô|ÐÈ^ŽuP_懠ªä„_~p1š•9vºg® žŠ…tÍ‹©ú2h÷@Åz>,QÈé¡Ù Tí¨8Lï3AÏ=4[Š[‚„kʼ¾L£Ì â ±T õîŽÁƒÄS58ÚTx“ÀEµãJD½£â€Bƒ ‰P*'äôÓT@ÅA¾ƒŠ]5x‹Ž¬¤â4쀺Pq8µ2X> *fõΓG¨µ‚Š‹Ù[¬÷~¤‡û^f›TIÅAËTœ¶ò«.¡dš},î#5r”Šû˜m&%-PqÚzÈ#¬¶l¼¿6SŠéü5ó^\û­û&\_ÅïÅõVïâ’1PqÚ+È…çÍÙ *Kk®¶¼xjÑ=y„§®¿ùíâÒÕ¥z¹-oì¿n»Feò>*N{¹ðŒ¹ OAÅa¥íÂ¥wݽ§nœG¼Oè×Õ#e â*Ëi?Æo;k/„ëö’ ÷°úò¥»4Â[w±„Ì: ¹}®xŸP©8(“€º*ÏÜÊìdS£Î6Û?R ÓíÿþQÚi*¿UysªF£…ïÞ”÷¹ÚŸr8»ùÀû/Š´ ¨8dånTÑìÇóÊ)›M9ž™w©â}‚—@Å)“€ºPqÐÊð>õW=´ 2 â4à}BýU-ƒ2T´õ¼O¨¿P”I¹€Õä‚-ï^6Pz¤O‘ §•õW=‚2 ¨  â •ïê¯z¤LOYÔ PqXf{¬á^Êÿ­´»7xŸÐß«G€–AÞTî¯â "íÚ*n•*‘÷ ý½zhd@ÅaQ*®0×lõ\Ònÿ}Ê;~ßl?Α?Fg†#…[œÓp>}ßé¬&jÞ'tùTÜýŸõ°³<‚ @Åño¤â_6Û÷ƒ‚ ucøþ¾ûè¹ò½Ê·8|m$Þ.Ò®Zä}‚Š£âäþ3ØŠå;ƒŠƒúÜ{/.VnÉÏ'©õƪ©@ƽ¿6HÙWq…[”ÓÆû„þ˜Š“ûä‘vTžHÅUŽÅ%%_gždçÐiâczîåùhRÅU•¼Oè©8¹O!@i7@ÅŠK(¥p¦d:¢æ¯‡S_ß»".ufò½»Qª’Šƒþ˜Š“ûO”ûì,ØTøãÞ‹+¾‰Î<˸ïå3ûQ¦nAÅAýå»È}¹ÏÎòˆAÅ0IÅýèLyŒ§YfÞ‹/ Ïl§Yö31£ê/ßEîË}v–Gì Pqü€›Qš}©Í…ú *NîSò< Pq¼À%j¸GqÚ\ð]Ô#¹O!È#y¤%mŸ6ê¯z$÷å>;Ë#v¨¸õÖg,m.ø.|¹O!È#y¤%mŸ6ê¯z$÷å>;Ë#v¨8~´¹PÕ#¹O!@i AÅ ´¹PÕ#¹O!È#y¤%mŸ6ê¯z$÷å>;Ë#v¨8~´¹¸a%µðÏ,«­´ÔzSòHñ(@Åq¡ÍÅ¢Ë@²ªª¿Ö£œaµÞZQv–Gì *ühsq-§þN®G«ÖorŸBGòˆG*N}–MÚ\¬UËuëïûëËëû|7zûöåËœñ-¥=€~ÓzSòHñ(@ũϲI›‹µj¹j7(ðú'œ¾Ï, a1«›hEÙY±3T?·ksÃuzŽ©#O~¤¯ÄvO¼¾µ_ÎýØnÎÞl?~Ä'tcz ÷ëµh)y$¨8PqT´¹XŸ&I©¸Xy…GÞ_Òí,ç—ä¿<‚ÅŒÅiEÙY±3T?Ú\ÜA¤êo£·Zq6$Œ¯wòƒÈ8ïÅiE)y¨8*Ú\ÜS¿UÜf’df^d;áòåùTœ5*µ¢ì,Ø™G*ŽŠƒ6wÐoC*î@zÂd8R÷Œcqö‹Óв³X=šÚz‡ói{ ™†å§íÁ"§ø…°_Ûæ$U›ç`q™xW—¨ï‰Ú×Uþy â¨8hsñ,õ÷!÷‹£â~T‰´ò G6\ç´Rû=žB¸Á#_éKÌĨ¸áJñ¶Îò죂 „6OTß¾}ùò(ŽŠrX cqÍÈCÁ[M®—søÜŽñöâé]Õ)>^xì8]:}ÜãV*®óÔoÅܞ̅y¤%‡GòNë¤,pI„ÒZ{ï¯É?͵<_wS®ê(µ¹˜Zç\YríkœPqËë\rÒE½ÄžÒ\×qOãçM]Ò¿Qòa;IÞ¦´Ò%ÖæblýíÝtÅìËVö~I]¯Ž£âx®‚<TÜÃzïg•qpåNÞZ»xëÁÕÅüÄ@»ì¾¿íoÑÆvöwG7ß¾mΟ³w9\’`{ouR{y&=ÝäGë’—änÔ•”}Óv7S¯óˆµ¹¨®¿qÑmKx±Ô·.þcç¼ÕÊ8*ŽçJ!È#ð(@Å=ªjŒÆYÛ몳.iŽRqá™É?5fü¹ ârÑÆsé+F^^J{\Îåo Í¦©n?m.îî»Ø/ŽŠ£ ¨8PqX¬p=¶hÍõþz³§q+m.–໼}ûòeµŽŠSS(y¨8^ ´¹PÕ#¹O!@i AÅá~õËG› ¾ ßEë}—V”éä;ƒŠÃÒù}ýÚ°‘z0”RPqTÜC¶Þ«hEõƒòH]~€ÞK› *ŽŠÓzSúA*ŽG*Ïí„í£ 9¥÷RqV7ÑzkEõƒòH]\×£5F*nFì4èPqÀ-ÜYž¯Ø'·ö~}v¨ÿhOîm~>ù|¸=׮߀~è½€kûÄ:J¯½;‰­ƒ€ëÊ´Fõ§„'§¢h•Ûjeý PqXz×¥Ãó•üP`5Ÿ{ª«ùºSiÍ`[òäÁ¨8@?€Šô^ÀíT\3É2œTIÅúA âp«®« Š‹Îi'TNŠôƒ¨8ÀE*.ÿ^\jq’”ŒKª8ïÅën€Š€%ûj™5*›Õ(;kT†³* 3*­Q Pq¨8@ïÜ®´Ï#»ìèPq€Þ X“ŠÛñöíË—ÕJ8*Z â ÷”v*Ð2 â½ ´Sq€–|5PqÐ2|5*Ð2 â½°ˆÒž[%·xŸe®ƒBÅAËTô^ÀÓª¸Š‹—·'-PqÐ{K+íáßG Õìö(ªæXwð`¿ðŽKï~ï¸àýÁ©8L.9XŠ"@ÅŠUÅu”ÙN^tT«¨Þ_›öêëtt÷±9|ˆ¥|JÅdÀ%Šî(‘°û:‹2\yzPÍã7f©Î£Ž]{F®‰d‰ùBÅŠãCðMå>_ T”öKT\B¹½¥š!æÈùÚýÿ_¾d#¯ / eFmïöá…çA¥Ó¥#Æ—n܇æ¥R­Ùû1t¾GòVÊô»å *ŽÁ7•û|5PqPÚgVqïYù°Ù¾ôŽŠ8} ÝøüM;—d¢ýÑ•azß}±»UåèÔ}úÐÂxW¥Ùãj.LH½Eå *ŽÁ7_ j ”öiïÅPÁד{ÿ‘Uz™ñº¯a´Ó ÷¾†|Ñ>ƒŠã#òMÁWƒš¥ýcqƒ °á<"“Vz5Ñv×ã â.VqKËí3¨8>"ßTîóÕð8*NþbÁ*î#5µ.ç©×Ë’üŒ½ÚXûXܸ÷â Ãhõ!_¨8Pq|¾©Ü竊ƒÒ~ ×±)õ¢S÷ÚæoÇÃéeûi(F»z¯óñþ*°ýñîi‘=ŠkTVD²Ä|¡â@Åñ!ø¦rŸ¯*Jû5T\8}®·NÆàù]÷?ˆ'¹dbâ­­Ó¡Íf3¸¯Ý²U\ïùb´7Õ¶löÒ~q‘¼-/_¨8Pq|¾©Ü竊ƒÒ®äèCå@ÅAÛ¾ššh´ rAû PqÐöA US-í3¨8=¶OîóÕð8^‚ü…’£• § ÒöÉ}¾ššBÅAË}¨ö â _— ¡ähäPqÐAñä/”}¨\¨8m¶|55EM–Tœö â í_MM´ Z¹ }¨8è x òJŽ>T.Tœ6èéÚ¾ ³OîóÕx òZ-ƒ\Ð>¬âÞ_w§¾l¶ѱ—×÷{šî˜ˆcN x}KœpßÏÓöOÄW[[%ýØnšêÙM*(9Tœ\¨¸ë¨¸Ž7˜84³Ã7MÅ®QÅÝNöÚ¾Yô›èqUÜr+éÛIÂ5âm÷u΄QqÐ2€Š£â*.öÃ6›Màºí°ãÀ™kÇÂúÇb•uú¼=ý2tìÂÁ´&ŠæÇûÐá;ŸÙICÒ¹,ȼTÌáPAìwîͰý£6çÛ5‰ÝÅr:kÄ(DÓöͨßð *nÑ•ô-+ÿšrúkp›Z¥ÇK€–TPq±›uR#GWnïn·¡Ovôãö_‚Ó†Ä@Øtœ·žƒ¹Ù¾‡ç'¼Ç)*.s"†0u‡§þ¨Ng0Òx¹»ë{ytDÛÇW[{% Ðøë½@~N˜oÉK€–TPq)±uýŽr&~Ý%pþN>ØÀÏüÅÏá×0ÎPÞ‹«¼QœÚ®<Ùûkó‡štV>c­ŠÃs2NÅ-½’&Ù’ƒä£'[šQ *TPqIñìm½v¾MrþF9ˆc…Y/å¥q†¾C̨ŒÂ×xfOç°ŠÓñÕÖ_I»Õ,!#L~锊ƒ–TPqi±ñ¾NCSWÿ™?ù«ü,*®ô{o‚×áÔ×÷ÎZ ³§s¸í›ý½8=ÐCª¸eWÒšŒÅJ'*n^;‚¯Ü´ßºÒ¨,®ÚɗȺS¸&:‹Å×ÓºÏp”q;ú« ”Ó9³Š³F%_mÕ•ô-ž’œ‹{¢÷âÔA(9Tœ\øÿÛ»Ûó´y( ÀLç: Ó°K¹²IÙ$M0Ù–Èö}ÿxß„šJ:Öy°âæIq}éñ}ÿÅS§¬T󸦱ÿ…4³ Ÿ<åß}äøÚ°ž×Æuþ]ý¿ç$).ÜhÿЫ­¯HÓ—¨ïQ òƒg@Šs úÌ›WxM~ì3}z5t 82 Å9>ƒG_†[*ÄÙôjl¶K0¿X9öP³RœccŸÙ׫©)Gì¡ŽÏ ÅaB¯¦K0¿X9Ž fAŠ);æH—`~±rì¡f¤8Ç Ç¾7?Øü-z5•ÛXíä›.űø,€‡Þt#éwV)°1_ňk°L{˜…ò©Y¤8=¢ÞÔìKq¬;Å™_¤8)NŠ)N¡75ûRœJ‘ââ¤8)NŠ))]¾g~Ázââ0GRœùÅñ @ŠsÌÕ›²µÕ®BU 8^Hq޹zS†ßY¥ŽWRëìM½»‰ßY¥˜_°ž¤8¤8†9’âÌ/R€瘫7•âtEHq`=Hq޹zS³¯+R)æ¬g)½©C…ªpŒâsõ¦zS)•ŽWRœc®ÞÔ»›øUŠùë@ŠCŠÓa˜#)î…ß¹ÖúÖ-É[p¼âsõ¦f_W¤R̯ZÀHqHq˜#)Îü+s Å9æÚ½gÃnW» U)¨s Å9æêMu~g•‚úŬ<ÅQ>WTî9űx¥X“`=”•âb_ÏŽ»ç¯õ¿T¥X“`=HqHq¨P)N +s Åéõ¦v\))NBÀì-ÅP¿˜#)t€ú5GRØqý{±&Ázâ°ãú÷‚5 Ö3€‡PƒÆÊHq`Ç5h¬0GR“îmäÓa 7YƒêWŠâ°·+³€ÙßËX™Áò'eø[)]‘±2 ˜}cEY«¥ïk)]‘±2 ˜}c… Å¡+*l¬¾îŒjP šÁµW÷ )¤Ô ‡ Å¡ÿØSiÔ RÓÍNü_)¤Ò,`ö¥83(ÅHqè?¤8Ô ëIq”¿lŠý°A)¤‡Tƒ ÅHq:Ht¨A5h_Lä°`)ý‡‡TƒfÐh+)Î.Â,¤YPƒHqÖ³±â°‹Hq¨A5h†±¤8ì":H)5(Åa=[Ï€‡׎kÔ º^ëÙzâ°ãÚqQƒjÐ  ëâ°ãÚqí¸¨A5h†=â°ãÚqÍ‚Dгž­g);®‡TƒfÐhXÏ€‡׎+Å¡u½XÏÖ3 Åaǵãš5ˆ®×z¶ž¤8ì¸v\Ô 4ƒFÃz¤8ì¸v\;.jP šA£aO¤8ì¸v\³ ‘â¬gë@ŠÃŽ;ëŽûá0š5ˆg=[ÏRËî¸çã!éx~óÇo7Ö½ðŽ[ÿ~úŠœý+ñrªÂT§‹®G3ëâØÅŽû^ÜzúSE¤¸Qò›¾‡Yfÿ’©#\o?ßÎ]qº^ëYŠâ($Å5NÜÿà~·ßÿ·N»%ªu‡pÆ`Ú.3ì¸#æ7X(Å=­Ä?Ò5Õ¨Ösû±fªAWTn}=OöTÝc±zVâ°ã¾±ß’WœÊZ=anŠ»~SÎלr_ª—õ§¸ç•xÿ¦u-e«Zë˜ézK)Îzþ,é-s‡Hql(Å [k½˜§{Ÿä×·4w™r¬` ‹ö»C¯8MdKž Ÿ©-–âv³§$–åévÑE}”·‰ë2º§—»/·Nœ›ë¬üI®øâ)ŽU¥¸ø†R\湸dä›6Åù¤Ö6ûïUbó~·§HrªUгž__¡!U§©ƒüïŸ7ž4H>éк.#çBÖéÄéåÎ#ÿÞåzïð…Hql<Å…·Lxz..3ÅÍw*`º×ÅéÕàì).§ŸVß·sqŒ°žClzä§§_ lñu™O: ï)‰GNüÊÖ3 űé×ÜGHqí·Ò›­ƒô•¬9Å=­Äó1U}sº-öº8>\ÏKñ…÷â…Yg¤‡ùýÓÑÖ3 ű¢÷¸:¦ª’Ÿ÷rŠûžë-ò¦û¼8Ôàì).£Ó—x(ïQi4㜋KžÞ\’g¤»“xdçâ)ýǚƪoÇ5Œ¨ÁekÐ ®{4ž¿.®ïrúG‚J]—1œâÒçÓºÁ¬óÈ^Hqè?tfA "ÅYÏíT—qþíqº¸ï£ Ÿ]èÑ9#Ý÷ˆSœp–â)ý‡‡TƒfÐhXÏRœ)5¨ëÅz¶ž);®×,`öu½Ö3Ö3 Åaǵã¢Õ 4Ö3€gÇÅŽ‹TƒfÐhØS);®×,`ö¥8ë)â°ãJq¨A5h×8Ñ{ø’@ŸúÌzë@гã)5¨ëeÑ7Ò6›è¬g@ŠC)Å¡Õ ›Iq‰äö·qÖ.Ü?|òw磽ÿ_tÞ³ž)¤5¨Íà–S\ò\Üõûêt¾F¹ÛÍ÷°Wü;{ Å¡ÿ°ã¢Õ \oŠkdËLqáÛ[š»ôÆBëâÐAê ]Q‰Ù×õšÁ1SÜ{çâž&@ëâ°ã+)5¨ë5ƒ¥¸K|%åàZÏ€‡R)Åaöu½,âêØv»t2ñ½ ÅÙq‘âPƒº^JJqÑ{R¶ßÌÄ•Rœ;.jP šA£aO¤8ì¸v\PƒRœõŒ=â°ãnaÇ5 j)Îz¶ž¤8ì¸RjP šA£a=Rv\;®‡Ôõb=[Ï€‡׎kÔ º^ëÙzâ°ãÚqQƒjÐ ›íIDAT ëâ°ãÚqí¸¨A5h†=âX|!Ÿ+*Qƒ›¬Aõk4¤8@Šc­¾È¦ïA n©Õ¯Ñâ)¤R߃ÜKŠÃÑLФ8tRj)NŠ“â¤8[Œ†Hq€Üb4¤8@Š} ~† žP¿FCФ8@ßàh&ÅRè{ͤ8)¢]“| êWýš£éHq€¹†A0VX“Æj>ÕÐçRèÖ¤5)ÅIqR:ŒÖ¤±’â¤8@ŠÍfÆJ‡úU¿Hq  Ô‚úU¿Hq  Ô¢~Q¿Hqè•.õ«~â@¨ õ«~â@¨ Dý¢~âÐ+] êWý Å.PêWý Å.PˆúEý Å¡ 4Vº@Ô¯ú@Š] .Ô¯ú@Š] .õ‹ú@ŠCh¬t¨_õ €º@] ¨_õ €º@] êõ €‡.ÐXéQ¿ê)tº@P¿ê)tº@Ô/ê)] ±Ò¢~Õ/Rèu ~Õ/Rèu¨_Ô/Rº@cÕ×FÔ¯€+êÏÇŸG9Ïóý`A]àáÊBb£)îí"½œªÃ]uºHqHq ÅÐÊoHqƒ.„·Ÿo—/s)@Š]`Ÿ?5õR·Õßß¿¾ßï·¼=TèÑì+ÃÏTUãO¯2Nº@ù¦¸ÜêîË~q™7îz˜þ )@Š]`_Ÿ—îÞêÞï÷ÖëŸßcY÷¯ß…Nñç~­çô­d§aLÜ^Îáïa¬jº@v™â^¨îøÙ˜Î£µRáL×[JqRè‡û¼Vcv‰‚Tãúħc|cœó’?ØŽ£¤8(Çì).³º“'ÙZå|í))))îƒ>¯qÍÕý¦WÓZŠK\Ð5üƒc^¬å\[¬ß‰ª»ù‰'n¾g~e¬ Å.°¿ÏûŽ#T|ºìósqá´[þÇl½.޽§¸ª;ùÎÅ ÅAÁ)®ÕÏe¿..óëÆßغ5Üž|I݈] ,Ç>S\FuŸ­÷$JŸ‹óº8¤8("ÅÅ×16š³Ü÷¨ú:<ÊíÍ([éý .So7Ò•>/ާ¸Wª;ýqqgö¼G%RÔÎhâ볺@Aõ[þXIqRèË=›?åÅYº@Ô¯€º@] ¨_õ €º@] êõ €‡.] êWý Å.PêWý Å.PˆúEý Å¡ DˆúU¿Hq  Ô‚úU¿Hq  Ô¢~Q¿HqèÑ¢~Õ/Rèu ~Õ/Rèu¨_Ô/Rº@t¨_õ €º@] ¨_õ €º@] êõ €‡.] êWý Å.PêWý Å.PˆúEý Å¡ DˆúU¿Hq  Ô‚úU¿Hq  Ô¢~Q¿HqèÑ¢~Õ/Rèu ~Õ/Rèu¨_Ô/Rîlȧ Dýª_¤8(ÈÙ¬Ô¯ú@Š] .Ô¯ú@Š] .õ‹úâ ÀŠü6ü$<ÔžIEND®B`‚ «interface» BurnSource libburn 600.0 50.0 612.4370214406964 81.3237865499184 Ecma119Source 604.1172144213822 242.59825146055505 WriterState 861.1034292438676 244.31119796826698 FilesWriterSt 984.2531292100068 359.95094883087904 VolDescWriterSt 717.2457054234224 357.4185959653686 DirInfoWriterSt 854.6043620021998 355.85097462036043 Ecma119Image 392.3294860655768 240.39714472372754 The context data for image burn sources, contains references to the tree, creation options... 261.45257180386454 85.80450046553075 Ecma119Node 291.8219414851778 612.806815288254 init() write_voldesc() write_dir_info() «interface» ImageWriter 401.9520048709197 344.8700633507891 JolietNode 409.0872475609359 614.8200784564067 FileRegistry 718.2810974616434 459.0339463910502 Ecma119Writer 273.51763645062584 489.95333138112096 JolietWriter 404.3304191009253 485.1965029211101 ElToritoWriter 512.5482665661723 485.19650292111 size : off_t block : uint32_t IsoFile 720.659511691649 568.4410009713001 «interface» Stream 909.7434429770816 580.3330721213274 ImageWriter goal is to encapsulate the output of image blocks related to one "specification", i.e. we will have an implementation for ECMA-119/RR, other for Joliet... Note that having different implementations for things that need to be written in the same block has no utility, i.e. RR and ECMA-119 must share its Writer implementation. Note also that while this provides considerable encapsulation the provided abstraction is really partial: In the relation with WriterState the encapsulation is quite good, each concrete state know what method to call here (notice that this interface is also quite coupled with state). However, with respect to Ecma119Image the abstration only refers to implementation, as the Ecma119Image needs to know about the ImageWriter implementations. This can't be avoided, as Ecma119Image has to be responsible of the instantation in the correct order and following the user needs. 2.3784142300054896 160.54296052536733 The files are registered into the file registry, that will take care about the written of content. 286.59891471565567 708.7674405416217 Each state will invoque property method in each of the ImageWriters. Some writers can return without outputting anything. It is possible that when dealing with UDF or other specification we would need new states and methods in ImageWriter 765.8493820617523 132.001989765302 * * ‰PNG  IHDRGÁ5}X&€IDATxÚì½K²ÛH®É-ɬ‡uÏ¬Îøj¢tß{&‰ÖKÈ´ÖŠf•óÒ8ïPÇrT•»-UûPïx¸{x€ ßga™à?âuøðÄö =Ì1<¹ª!ϨT y@Õ P5¨€çQ5 Pƒ¨@Õ jP5¢jÞ‡ã»ü#A;Žª@ÕlBÕÕ ªU°iUÿ¸ü§ãõtÏÔŽ‹±O¯Í©ÇïÑ?ƒKQ5¨€uTÍaÒ))Òÿ6Ž‹ª&Q>Ã?m3§ÏU€ªð«šPuŒòÅs\‹D<U€ª¸…ª©:Žª@ÕlBÕ„3_Â>í8ªU°-UL IæÕÈǧ3T ª`ª&Xë,Q#ÂñQð\´ }5¨€[«š=åU€ª@Õ jP5¨T ªU€ª@Õ jž[ÕÀm ±ª`-Uƒ°3 jð¶;ªo; jð¶·lgV8T ªU€ª@Õ jU€ªT  jP5¨T ªU€ª(ªšÔ€ªT ÀU &ZÊ’¨@Õ jP5¨T ªP5¨˜¡[P5€ª@Õ jP5¨T  jP5€ªT ªU€ª@Õ jP5âî¾=ªU€ª¸Î³ùïÿC Ö[6¨T  jP5¡ZÕlJ- jP5€ª@Õ„9ªf;‚UƒªT ª†@ ÌT5Ñ ¨T  jP5a¾ªÙ‚l@Õ jU€ª!W©š»+T ªP5¨p­ª¹¯x@Õ jUðˆªæçïGaƒï?ÿlÿÔþx ÿrÙâ|û8êŽÙØr¢|VÝÚ>6뫚÷ö‘r|_<ëïã³j…ÈQ5€ªØ·ª1\´Û»ÂóRt^eŸV•ôîDªf{F{\U³’òYM,¡jUðªæïϯí×Ñׯ Çÿ8½¬žŠü„©G(<G.œ“ÅÓœÆðáßþë·ôª8¼¿ _w¿ÒU“NNH UU:E+v?ú>ŸKöþãi~w'‹ÙÌòúõóѰU^¹CZ‡—Ïç0ŸÉï8óßÿùMnBÏ•Ã&fZ푼ñHEËSTÅÉB­1¯ ¹‰ÌLv^ï#ªšP~ô¿O§Þ6§ó¯°Ï%P)çþ”éÈOóÿOŸ¢^®ã÷(’Hëèç1£jUðtªfð€9qþú¡w»/NÛÛüòáHçê]®í/#ó '4üžþªS¿œ3º˜­¼RÝz1iñÝV…Òivîýæö÷%•—_^†ßF>›ä>œþî+âଔ.­ñø%¶)Ÿ¢ÒH”Ð ì> Å&¥´¤Æ#­¦e™Öh·Gcûjîâò.¯j.2bT+¯§‚ªiÿÞ‰‹^Y¤/í¿_Oïù9ê%±ê·ÿêr£ÅܜҞ=þ@ÕªàñUMî†þqzé½jÙoóÆS<¨&Ôø©_/2@êpH?Ò1s«FR*c± –ª è³jS¨«+eŒÄ“y­°qgH¤æÕ¸Üx¤¢å)úG‘ÍΛÑfÔ;ÅÊä½¼Þ…UÍ(#&=!ôÕ˜¿Ãöäl“‹ª¤[&﹉b²ŒªT Àó©šduõTžPó9¼$WW^‹\s¯}e¼JÕ˜‘ó9»Rf+ ËÔÁع%UR´|´Þڪƨ‹Rõ‰™|Uk† %#*–däXIÕŒ#ËÔKŠ1_5WU¨€]«š ¯@ưT_:UæøŒ³ªU5áGt5éƒ5¼í6ª¦œO¥}A‹÷ÕŒA»U¡òÆcM·VkÔëÂ6‘’Iúj²I5‰º°UMwÉ%A»¯Fˆ™¾@Õ jâ)×ÎdýÈÉù–&WL©$.»’“h^Íx~äˆKãÁæÏ«¹^ÕÈÙ %Ï«QUM<ñ}Êg›D?¥~!U£Ú¤”–=¯f†ªYª5ªmæéUÍ/ϼšü÷¨IRâV5rK.T²˜™W¨TÍÏ?“…Åê„–Y+bõËFé ek õŽf|•”“x ´`5°Ó¨vÂHĬ^ ½t׌@³1$­fWJ²šù‹%gÏ«™†fMú!·‰'-eų´hyŠ–ÁjY]ô«Ã9†>î4Må8úg¦ñcÉâîhÃJ¯¯v<‘¥,‰ªT ÀöT awaìr!8:m¶WÕ<ÑSU¨T aöÖïV-l—‰ªAÕ jU€ª!¨@ÕªUC P5¨T ª†@@Õ jP5¨T €ªAÕ jUðôª¦_Ùö±f¨ïs¢ùžJú<~FU¬™|8\³£eÌ´êórq¢jU€ªÙ¾oZ•=ÿ¶’{÷ÔQ2¨š[¨šqOËE6„I¶Í¼üsÉMfP5€ªx\UÓù ÍŽã.‡ñƅ®…âΆf$é9ýþîJlÂVŒ¿ÄM$ÕMý(“¢ù²äŠA)¸aqãHgC)ú‚ˆù´·¡Ìí`<ßÜóåó9o-óÒÕêU³YUSõ»ùÿñûðÏSß5Ó)™l·Í,½øï®´”í8ÇN¡9 U¨€ ªšÁ%mœ¿~èwC¿8—Ý>ñ×wåO$Ã9Á¶‰ïoéæëÅ Ø—;ÿøÏ!ß§2÷³³, ðÄ e¾aRƒ8m;ž0Dé•ì£Ø!É•|ÕX±²f¤û]=¦jÚŸC‹­4R•Ñ\Õé‹)&aìÙtÊœ´ºË£8ûhÞ§¤Q5€ªØ»ªIz>œþŽ]ÒÈO-àŠD¹P½¶éHÄOyP™èO3àæfތоåIBŒ¡hpû`±ÈiÆJ×®kTÍæTMÞÝáé·±þ”uªD¢&–5δΙvOˆDªP5£j~g㈔¼‘hªFº68.ø» «%.U£ÜŒ°NÕìSaðmª»®Q5›S5TˆEõª&Ð!UêÅ™V.ÉætÖ jU°qUóÇéÅvjK'¸" ?Ìç}5ºß/ŽZ¾¯Æ-<\/EhŸìMbÐ{﫱ëU³IUsµÒ§Ó„ï«9Ïy†ªT À>TM8mC9§xBùœxº…:¯¦BÕ ƒ²\ój¤\Áo+F zæ –çÕxl;d)š "äS™ßRXN3žÍ«Y"]TÍŽTM¼tÙ(;âÙ-eUÓ,!0ëŽ ÿTæÕ8ÓæÕ$«­¡jUðhª¦s@­fŽ ç¤?éÐ,éÚa¤ˆ(šU³k %'ë^ušO ZÁݳÖ@+&¬EvzŽ«%•Ö"³W».­`®gpUºy]WõÅ¡jn­j¤½e¦ác§ê¾}»i 4gZIv <¨@ÕìTÕî #‚}iž]Õ<ûSU¨TÍs‡ÇШT ªU¨T €ªAÕ jP5¨UƒªAÕªUC P5¨T  jP5UƒªAÕªUC  jP5¨T ª†@@Õ jP5€ªxž÷1ÜT ª†'?ä jfòÜT ªævµOO)°ùž|Ï£˜‡5ªæ1 ](@Õ jP5¨a¿ªÆó4æa €ªAتUƒª!›V5åÇ5Oax` #ªUƒª!CÕØÏdÖ€ªÚ ­UC v jŒÇ2k@Õ jP5ÿç×ÏßéÂKßþér¼škÝ'ß8ŒyËì¥f›ýŽ•8;•oën+EÕhOfÖ€ªAÕ j®óg\[¼dqÕV5„›iƒ0þeUÍ–ísE†e “KžÂð´+ªUSöºþþü:ôݼ~ýc8ùÓKПÓ];œ9&|¨n.ùþÏoǤ;(Ieê8Ýß8ÅÈ_¼üéÃéï1žà÷ËçMZ5}5yYÂøG›ô1 Yzë¿I)F¿õBgŽv{ù|cès8ÆŸ˜K¹VL4Šð_>ÕôŸ—#qÆš3SÄ©¿M9i~«MÅÑròi¶uV¢ 3$ã/ÕÚ…bæuÔŒ+;÷±áa ¨ ‘ЄP5úžÑ!þ{òq/Îåä¶Ž>\wæß™+,Fž8úy*ñ%ç¯ú/>ôÛï‰Ûwùkç\^~ŒŽf°všX–8Ñ®ø—ÿN‰s2¸à–ª± Õçd<>žæpH(͉v­˜haä¢ÃÒ¾{{r£ô>~y~eL­!´œ Ûú+1k3BÆiíj,ܹ„ªV€ U~k^MéC¸!Qäî”Ü5Ô;(R%ô–ˆ^œËÖwlзÞIíÑ9ª&+KÒvi§UÅé™%¢å_Ž\»VJÔˆ°-ìY«Ù°ë&ýíK®˜ù¢mý•¨õïù£ò¶vG,¤jÂç ï{@Õ¸ØÎFL‰HØÅŽC;©¹~^O¯mOZOÅ«¸òÿ.¤jâî ¨ È¥%´“}2rIU#%jD؈ÃÁûÏ;”\úÄL®BÕX™¬W5Šñií·U5ã#U¨T ªÆÑÇ}5¯W5Ó¼±+&O%9¡à¦½4oÙx§ëTÍ/M,ÍW5¥B­ÕWã–Oáèµ|r‘KÕ”’»›ª3¶Pk¿¹ªéž*¨@ÕìFÕ$»Ñ¡jÖW5Á°¢ÂLƒªF¼$ž΂0—ÍfJt™4×!'íhs?BËÄójÚ²ôóγ9'ªª ºÄy)ö¼šüZ1QA0„c½Z=óU›ùcê59iôW<ŸÇkÛyª¦˜±ëZûÍæÕ jŸ`—-ĹÇö6s¾×ýj‚u´N“›.­ åð39ˆMÌÛ§ž¬u°'ä¨s<®U5Ò `Y~&ËÄëtõ'_.OÆw•F «ræ·ËšÅk å‰æÆ–oå\ÖQãS5rÇø­ܶ¥jôŒ]ÝÚAÕ\ãµ là1Ts+:TÍLUCØÚÆ/7ÞAÈîË;j÷¨Àm@Õ jP5¨TRG U€ªY*Ÿ;Ê?ª†@  jP5¨T @@ÕìS·¿DÕ jT ž+ÿh‹y‹Rç1äU©µyƒQm§ËÒÆ—RE«‰:¬—šK½ Ì6Ûß‚åQ5 øâ°¬ªwvïFs5^]¼‘cIÞ+¹›ƒ?×x¿¼ ¿ÇÆ¡bAüïo½¯Ùnfo&Úýî\ÕoýÀ³¾'̼¶ß¼­vaÑ¿ ½ä 7iÔ]Qü†YJµÜègZzÉÆJÏ«Ri?©ä † *SeCšŠÞhÝÖå“¡R³Šè¢¯G€Æ|WU“{iÁ×n{B¸ocúÛ¸ê¿;ÑþwþßÌ›”V)Úôš¡e]Wƒ¨ üf)Zøâ[·nt#-†ª@Ë­_jÛSWµAiÊJ1cÙPLÔc½ªDýQ58‚°U3¼Ñ†H¹]ÃhOq”‘”hÔKõØøü{)ÚÜ ÍGˆÙáÛÇCž‡:ÙcáÙZîêR«ÅQ•–ª)elU㱪U®j‚¾”yŸê£¨§¹àª‰¦_Üߤ‘KV¯…{Í+q Õ \oá»—:Á_•…¾óœETÇz¨„ -yÿªÆëÏú¸VÕiÅ3ºøåy5Ù$“±?!ümú÷Óì!ÚëUÍØWãŸ}QeáaÍ·pý·—Z˜W“We­ªQ3– Øë˜©jÊÖsÜJÍŠÙKg[¡jU³²ªéý¼‚Wzqé–6.´õòùô–®|­¦%ÎÑobSf¤¤‰êÑêâdå ¿r¯”UmaáÂõK]\-«Ê~9²U#<ÌyŸ±Kyç÷Õ¸¬W¼ òš³—Y~uU`€Ó€ªy"U³0Lì&ª£íyó€ªAÕÜpÄCy€ª!µÏ‹ÇóŸp`}5UC –U5¦P5€ª!P5áUÍ#‰T ÐŒUC  j“ª)T ª†@  jÂîU ¾ªU³Ú:XÀ½ñZż—ŠGÎËØªÅY/ò1ßpçMT @XFÕ<†;…G´a¸JÕÌvÝ<;ŠÜQɬ¥;ù¾ Ô¬?ÃóìyØ”ªÙP_ÍÍòÔl`îÃ:ìstù=lI»­k½ØlX©ŒÏ`ºGQ5¹ !ȪƱqd»Áb¶‡c°ãÙq¡Éø£uÚ½8ÿ'awÎ) —LWåQ5¿ãkÿù¿ÿ¯<Iê×ç-~û§e×܃ѤÚi¹Íÿ[Ù 2*9’”±P›Ù.œZûÑ,–T–˜gÁ[~¿|þÑmô©WGÚ†ó3ã0îzüV*¬ÖØiÉûS5­íz%Ó6¾´—߯žSwt…¯Iz×¼ùª¢Ýåkrë†}8¨Ø»ª¹8'Crþú¡wHÄ—W¾Ïú轌žŒëBñ„Á™DÈû[ïÑ6éÑÿ¯’¢š¼¯6\bû×ÿïÿMË’¤~eÞæ ô*»’Í‹5¨½ë“2Ž1{¡—\bž<ØÜkÍÛFnO¹²„ò^ÒšŠs˜Š6i¡¬:„ì)gŠ òÆŒÍÈÿ‚­e1UKÕ¦<ƒŽœn†\¨i LÒya=ßzKõ4Ê»NÓKʾ Fó{^Ì›¤ ZûEAþêcn°åÙù™ZÎÅïÚ‡ í±Rü´pý× #†Û~ECØxž#øC(ó‡R5~b|§›waáªËËÑýYÐŽ*3½û|YZ)oò ? 0ëd9‡qTÂ%cc風†>“¢sXüž[4È «9:x˽4U ›e¯œàªblvþl-‹ªšÁ·kn€_^†ß†ÐT˜,j»ß½¾õ÷XPO’÷—¿ F…Þ!!o²¦wjèŠ/ Ó_…3¹ìÙÉëSý Éó4w˜åÚ¯ÅnüàxŽà jP5ë«Ç`‡é«Ÿù±2p‡Oì÷ëTMÍ@ŒªIùù÷ôyªFŒ*9¢ª¥ "1u­ª±+Kˆ-ÿïŸV_Ÿ¯%øì)ÖÈÿ‚­eQUŠ3§P“˜¦óœõ¤Ú¢$F…zãÚ1Ïø¢P%—ý-Ãÿà°>]¸Sñ×H•æÅ°Þû†à~ŽªUsOU¾Ó‹W…Ÿ•ÿþs»ªf>Så…ë™É£’P…½b_{·bß‘ÃýH{iÞì³Röæ«163ÿ[U5× Í²Î {i¢wc5Ũ§‘]©¡g|Qp5,ŸÈ._nªãC…Ëæ¾©»ÏgÅ€ªAÕàªQ5ꈀÎûïÆH‹ãièz £3Ôy5áTò‚“Ó:]_³|ÎS5ż©ª&sTds{DŒWÕÄûy5j‹ö+K2È0_ œ‹n¹mböZ"þ#Ï«q6¶[ËíTÿ«€.j]êÓê¨Y”¬nôªOCÏø¢àúÜâôæ³3=97ò\x–k|‘¯õ1 jP5ø…°ª‘ç¾ö/ž\2ŸV˜)*­Ç¥\˜GâøÞª®L=½MÓ’ÅWaëP>tžU#äÍÓÚA a~/¶l.— ܘüˆ4W1à¸\ØA­‹RwY\YÍqÑ ‚©MÉ$fÏ5‚qœØ¯&Öhl×·ä{¨I¨) LµÝzÁù’úÔ•}AŒê«®•çÕØ<樚)ÛójJz@˜W#ç\þž!©ñ«’÷Óµ_/Š1Üø+UϦj¶¿ÒéØ{pM°—œ½oÞX°ôI­e¹h¢ÐT˜,jõ·›Ø”9º²·Ä¨¾|Vy 4ûƒGí…$ÛÅ5Ðü";_1LËyþ=CÌsþU©òÓµ_/Š1Üø+U@ëEÕÜÝÃÎG€_Ûrßï–ͪæ±ÃÒ­ÅT5{Ôë–7î|,€ªTÍŽT @؆7²¨ª¹‹^€¼áÓcT  jP5a‹}5Uƒk@ÓEÕT @@Õઆ@  jªUשçR¨öUêþlá¶·0¿ÍAÚÂJEʺMO2Ý¿ié“ ãGÕT #Ð`V_MÕæèž ÓþÛ\vm?uãîþ5Ù{¤Yµ¨š%UÍ ”ã=ïQÎ¥êu¥Ì/ívZRŸ“Õòs÷¯2kU¨š«šf³„p[¶à÷Ëçí+©}1eû. ;tÛ©ÍQ5ÿì¥VTJÒ‘}6Ä¥$ÏŽ"ä[GØ©$ÛV&{YF{*d1çÛËekk;[Œû7t¥‹©m°a×£Ù0Î?ÿ”Kä½VÉŒ¹Eø¸F²]fÔÌUcü)ÊnÔ±r(Ÿ_(´)v$·”Täâ¦>ƒM?…˜å]Å­Ýí„óÇŒ‰û§Ç×Z”êÑjJ‰œ×:j'-Ëxí =ë©SýàRIØÅ__ÕØš^lı"oÊ3\5µ¹DCÇ’.ÞS—¼c¢¹ú·U¸ò è75Ë~¥Ð¾dx>ÌÓDz¦7Gßú¿IØþ÷ÅÔÝÿ|À¯2y ¨Úí³@¶³kœæ·Þ-ëèªmʯU5‡CÑõr ¡âùò Á'vW*Í;Z–%ié|1ÛYu~Aö—½XVÃJä½¶>‡aržVW°|éàõIÅ¿ª)¨FµÝ ÷^Ó©ôñËËð{ã–bΖ=–äüÀ-YÇ+ІT"ïµ·T5>Ëv^Fñ7¬jJwx…†öH^ýQ¡j²±RùdžBÒ¦À-y9}¼€ª)Yþ©¾ÊX-)þ”ª¦_= 18ˆ¾²ôÐð†ÃÑø¢t©Y­bLu~¡Ó8‹ùª&ð%Äš•æàg¢›5åÊ"­›%4 ¥DÎk­ÚéÇÞ§vË^Ôcש£:®OÂ(þ>UM…†.IÞ°VôÕ¨ŸÎIwMr[zº‰Ôž(»È‹êãeújêûÇý*C@Õ jžQÕ¤/Éûo<3÷Ûdš*ΉÍçÎ]­46ìê¨BÆY¾WÐáëgÀÆŽV³1ùYõy²Ù¶ÁÌÞÓ舫Åt,;Vèõ |Q±DÎkå©àÝÁËišéX ÌS§åêX" ÓtÛœWS ™iè@ Êój¬›3˜2~弚îNH„GÕØ_2ŠŸ ®ÒÇS$sçÕ¸v…ªy¬¯2T ªæ U á 69!<ª7RZmU£iè!­h 4[ò&kmF»ùÖ@:ÿí¿~K?6”nQëK†ýÙ`¶>?±\³š[Ä?ãWÇϧjð‹ª! j¥jîÆ.ϯ-FCÕª†@  jÜ~°{n:UCYP5¨@Õ„M÷ÕT  ÃQ5UC P5¨@Õ¨@x^Uƒ›4WTͳ–ž­óBü7 nÞ.zÔ/ƒtùïüÁuT @@Õà&Ð\÷§j\»ÇÜÃÍ]0ÝEv;XÜ74ì´{D»¼j·D¸GSp÷©j6[IùÊ¿{Y÷ú,m³R®ÏU¿4s}$éæeÒrÏÑšÑê áæHóŠ#Ç ²¤¬Þ휔(ÜuG:ŽªY͹ÁSxvUlñýŸßÒÆ º7‹°s@¶A§ð¢Iâüït³„ð6î”`í¾w†Nï ag…l£…~y~ç;n‹9T’·ü–óS,¯YFõL£P¢ ›,õŸå­Þî˜îw7í[ÿjîßÚ‰¡x¡ŠUÛykÓW“ :ôlΘŒM ~÷^šyíV{ûjréŸ~\ Ò üËÿü{¡˜5Tª?6$Œæ·ñ1 êÃŒ˜íü3ŒýEʳͥfù|÷G¥• ²a8ùøô~¼}Ì^Zû\Ž\=Ø79ÞGûgªa)µ¢éB¥‘oI´³Z üUMóºŒ^%u‘ÞÅtBgÕwÛ(žÀuQ½ 0bfòƒ¥½Ô«òc—·˜¨|¦Y(ùªàlúÿ÷O× ºhU¦T,m•Á9“š?LÊ~êÚÓã±®­–IQÄÇèâB1k>¨ÌüØpù{òå ®õcÀœ3’¡ü_¤‚ßÂ'¿Yû[1xD…ýâùöq&+ôÚf‘uÑRªFø«ühÖÔNøU U€/ŽV&~pŒžäù8çü üåN~Å”ãtçré“ÃÁ“\YÕèù1Ê;SÕ˜Ù«³¡XAa/MÔcãS5%«6¤jt÷KîÍÄ·ØY,Æc][?¯¦,â“î ¯(æŠwŽú1`¡3u_¤êV?y•*¨z|Œ]úå¬O&k çÙK¸üüÝu:…iÅ%’Æ1\äP5xŠ€ª+UÍ44Cü$Ÿ©ôÝy¿ñã\VÕH9\\ÕXå]AÕTØP­ Ôó|SúëU³UU£H[ËáÓzñÌx<=€óTM.â㮡x„•^LçÇ€™·eQÕÌþÄ⻪ú‹ÔUMe_ÍÏ?Ï_?žþþýØ œÍ&«d½v…ÆàW5ÑjA÷WÒñw|C•㨜EÀ§¡ÞNÕHŸÅÂÁ^U£Å)Œ#×çÕº5„júìAÅüÜNÕH6 ²'Ï«Öû Ï7:ë|õŽªÙp_MEÅèz·OY+ÏP5¢ˆOEÔŸåìù?¬¥jæ}bq_U÷EjcªÆœWÓ Éûòò!ê÷‹f»kªTÔoc ‡×߆¨œE¨V5Á'­é….EÌ}=~|¼V͸,X¶È˜òq*‹óû8—X[k®fÖÊs–±üu8ìåÇ.ïJ#ÐÄzr’¬å#?™ÑÝĦ ÞIµëU³ñy5î{Õ¿ÇkËñh×^¥jdŸ¯|UÈ^Í•¹ª&~Äójø0cªøEª<¯Æ­jŠ1T¬–­}œ«»ä‘”M *÷ŠÌP5É'«ŸQ¿Mz¡¶6šs€ªUWöÕ) Ϫj2ÿ¬Bƒ¦mähʹxí•#ÐD?#{þ*³ûjÆ•ÇÄ5ÐìŠð˜± åû"åYÍ­j 1\»_2~,_ ¯(J3Aµïs‡l¾M4IFœ.ýW<Žª` jÆ”lÂ~5„Ü—†pï0.¢?ÿ³ª_P5ªUƒª!ܳ¢¯H¶vòتh¥¨€ª!O÷Á_ •¢jª†@  jðà>÷;ìT @@ÕDü»UC P5ô(ªàÙTÍR3•ûxgEeçÁ³#@@Õ jUó„¯$@Õ¸UÄ"RáÊHœª†ÇT .# j¸àiZÂAÙ·(ÙIPØ)rÚI°ÙÐPÚE±?³¸ß¢ïZYe)»7F§ÅÙ˯"¨^“@ûàv€‡Q5ïo½V9ýpxû]VÝnÁV€—“߆Íݧ«†3Í~%EåÚør+·ÁiJö4ª†×$ÀÎY× ¨q°™6m:áÓ˰WºgZK9Âú™3ùÁÙÙ#¨x>U3èÊÆªmBÕTf@  j6ÿòÆ‹«š?N/æì—û÷ÕÔd@  jP5Ü °cU£ÎTÉFpÅ¿§‰+µª¦8“gÙy5Q$C¡ª†—%Ð8¸à‘T¼>X³½ŒÙoÓ Œ9#ÐìuÌ\+M;Ö@˳ç)ý9ª€Æ °e/–{TU³ýð÷çWmy4€ªÁq@ÞªfÓ¡ï™a䀪ةª!¨à5ª†@  jð– À¨€ªAÕÐ2îíÂr#ª†@  jpf À]ªjÚéøÑÖ.+­n|}´×İl¡Äµ¤›e£‡U¤Ãß7ZSá~¶Ýr¹ò°HÕˆ[Õ†¨ T ðp†«š¯/Éf2¾­cª\1ÿ…Ú™Ûñ¼óœÜfÓ›=Æ\›ÐfµÁí÷5ZööAÕÀ£«šï~}û½¼å¥°æ§—ƒüñ[¸¶M¨=ôúõi/ša³Ìæ ¸%h’ÕB —,Å+Aÿýù_ÿÇÿü_v6†‚¼~ý¬9…ã¶žÇoQ_Í÷~óþVMå¡‹Ç>Çg™±t}wDÛ7\ÕwÇÅ'Æ]€¢JW2cD[.¦R»ö=»¯æU9–NPžy ¥ªñ7検mÌ™ÛëoT _©_ÕüüóüõCäø¶.ÑÅïü§Ë_쉿OW]œ³Q©›7ºqÅ_mt¬Ãƒj_/†83—R|ùߎ¡0K"iÎé…PSR)õËñÑ•låMœUý³º`¢0žs\– egû»Ùºôã——áwxÂhœ(¹>f53F´óŠP¬ýrSt›HêÇ(Ä9£1×¶1n¯¿}P5x@›à€gP5“‡d{êéGý¡W¤8Ó 8œFL¢j@ÎC±Æ ÿüãŸßÌB]ηcŽûò¬ª%•L”$Q<Ço™v bñ»’!³TMÉ,U“Çt­ô?føÇÆ\… U#™HˆÍ<çJIP§jÜ™úˆê‹`×þ¼ZËGÇmBÕ¬ÛªÛUÃëh“Üð,ª¦$SÕWãžù]ü²>¯¯FŒaú}ǹ€ª‘L$ôÕøá-T;3ZG„¿ÅÚ¿¦Ö‚¡_›V5Wå¶æöAÕðúౠϣjÚñZ^ “¦±XÓl UÕHCÔÄ¡MÑÄ€xºÁKÎbò¦Îä–¦mtÑæÕŒ'ÌŸW£¯RU8G,ì|UôªÙójj: æ¡Xûþ)^3t‚cÚX}cžÛÆÔÜ.wû jà‰TMâr‰K*5«6E®áÁXviö»iÇQ5<úÙqˆVG«îÚó#«šx[Æn«õxÓÉp×Bé`°ýâ´=¥ðEÙÜÙ3ÉO°·à¹”úð§p“õà÷Ëçÿü”ñã÷öŸíûØÂßC&³´4CiŸóµ3“âWÙ?ÝŸ1>aÜ[sŒ'6f³Ïãú´ç£Vöþ˜y´éÆ‘Y‚f‡¼PbÙËíÙÑê‚¢¥MB髉ZÑ^Í¿ÒU¢ÊÅR½[;ŠJHˉ.T ïc<9 Õq/ОiÏW5ƒ·ú÷ä–¿~èß‹9ì¨}Ç÷7ÝÑ v ïο\xÈ÷"ìò3ö+SØ&ÚÔDó#,£æÿÅ™ÒR ¥¨ß™•ö/ôŒãé]ùöw#ð>~y~W$$UG±B/'ឯuˆvP ¥»éŠ=­.k öAé´¤Ôá­áT5…êÐc˜ÒÒŽ£jxôó>¦ÕÑê€{öüèª&wÝ‚®ÿAÏ âà«ôKó%~=ui¿öƳ|ë=ÂÞËô|ÕŽ~‹%u缪Œ~ûg_ô#Ù æ$ŒÇW§®êð¹þq<§‹¢ÐFCU˜×Wöëk¤^Õt}ƒgÏØBµZÍêÐçÕH½RJ§"ª†÷1ïcZ­¸hÏO£j’ ÇöÁÒp—«T™%áÂü¿3TM©¤««šRaƒLcɲÝë¯O¨hº U“ )ôÚ$Ë^^ö¨šFH²ÄîlQ³Qªw)†¨Hï;BÕðèç}L«£Õ÷íùÙTÍðI>ýìöz¯R5açOØWS˜öÒ¼…ƒÇjûjÜ¥X«¯Æ7b—ô`¸d›/!µ:l×?‹vøÓYë®QãÉ •—}Kª¦3IfE_ͬu5 ‹-UÃû˜÷1­ŽVÜ ´çgQ5áø~qТ%¾ýYšW3yx®y5áž|^ÚIäÓ´„ƒâh‰i-¬j¤F%û›ª&ˆSœ‚2/!­:fÏ«ÑVåR%DV¨ªÆÕêÚ„úÉúbó0TM"ü>œ¾ÖÍŠ±o1W ãà7í8ª†G?ïcZ­¸hÏO¨j•ÍöÁiÕ&i ´IoÌZMYlÍ·’•2E!]Kú§µ ª Óª´>Tir‚ƒ•ÊNoÓjoÅ^²RBRu\³š¸ š–=¹PYÙ+F²)}ý —\IÍÃ[N»^EizŒ>ZÏjx¶.jåâ°Ò]ÜUÃûOhuÜ ´gÚó†U @X6„3jÄ¥}T ïc<9 Õq/ОiϨá9‚ÑIˆªáÊûOŽVÜ ´g@ÕT ð>¦ÕÑê€{ö ¨€ªá}Ìû˜VG«îÚ3ª†@  jx§ò>Æ“Z÷íP5UÃ;•÷1ž­¸hϰUãÚýpÈ—è–¬íw –Ä-môi9íø¥÷__Þ¤÷Š9ÜG²YÈk°’¸ÿÉ"9¿>’mÎΔ5P5¼ñä€Vǽ@{¦=?•ª)íW“H ÀE7ñlI$~Œý"_¿~N3 –ßM·Œ¼—S{÷·&‰É,­üë«à›¹¿ä}=ø›¥^Lè±V?CÕð>Æ“Z÷О÷ jÚ/ñ½çÚùdÃñPQŒ[Ên*Μ,Ú“oÁ2¸o3MòÇéåräjanÄÙEkî¿o@Ùäè‰êËRÜ82Ú2R{Ù~‹¹Ù Ô]ïz)_ÈŒ²Ûé¨'»äš ïßz1ÓŒ3Ðm÷)ÄŸç*Ϲ¶1eÑòÁgaÛM%i#Úr†CÙãs’ JN°7Õï5T ð>Æ“£Õ÷íܪæâTNá¸c`Ø‹l#8º¹Z¿J]ÎàeŽ‚Rz¼Ý)óT5š®=ÞÈž_^†ß²·ªìïß^4{±‚2õ«ä})böŠZ±‹ªŠö­~}E$PŠ/ä*˹’É¢åS1£È§¶*'mD[̰Võr v¾XoŠVü¨à}Œ'G«îÚ3Ô©¿ïN´ðΫQý(†Þ)|Ëúd’©¢+½Ë·n Õ 0CÕde¯P5¥ Jf°”UM–]Õt Ðu1‡¿M]1Å/äj1U¯y`Ì«ñW¨ÿ*m0›Xéåy5¨Þ©w~?aÐêhu4x*Uópª&\ëåóémZÒêÝZWÊX­j¿m¬xOèkÝ5Ú(,uXÑÁXþËéñ|hFÐe1Oþ±±°uÕ¢jbeËÓÔæ˜1«‚â*"”¦FÇ]\Ø4W öÕŒ+‰k ÙIëZÂ{UX̬‚šã‘Kk ýtÏ>zjUóž=^ŽïË?!»Tò˜ÅãçÓ´þÝé\ˆa‹ïã.ï8y;orÁÁàèö[ÝÓ6¿»9’µ+« klc3k[éØH/à•Ä 5oTÍT °¯ðˆ»Á°_ÍÎß²Îeî¼jǃ µ¿†÷í>TÍ“;”5ªfãM.ñ2/ÿÜPÛó´:´Í}µM­ñ£»¢WC“»ü­Ðújï)­y{âYþþEÕ jT ªf]Uø€ÇïãiÃÑæ}Ø¿zãï‹òçwÿ‡óðH(k²ã333^óù ӗ˺·õXƒ8‘׫ší4¹¦ë6¥:ý]jŸi[íÏ‹ù>«íù[mò^Ú¦ÚòA«IÄ j¢¶4Þ ße¹ž7ªðòOŸ„ö–Ç“ß_δP5¨€ªÙŒªi_Sá°„áev9q|…]þž|òû%}h¼ÒÅTÏÉÌ‹püõôÞ¬y5oxÞÇWªš­5¹PLåI™}‰J[•‹9§í­n^7Ü@ÛTÛk…ãÌLè‹FŽj•~£}æ#ÔäòN•$Ÿ¥¾D!9£˜mo†J9ÀÍù5GÖt-ah—Ó¤š¤-ÍLlT¢Qús¬»Æ“ªUC P5[é«™×…O@éb†ß•^c13¥êõ“×é«Y¨¯fƒM.N`f;4œ=!×|8×rQëAŠ™±Ožÿ>žíÇ j6ÜäŒî¾ûjæ´=ç¼Úä]ôÌìþ´dˆY6[ůjÄF5£¦ø½êቪ٪i—šö]{*öRñ¯šO{ÑyI×ì[zŸÙöÛ™…¿^·_öõn„ëʸ·y5NQ±ˆ‹)-u:çy”Dr4YshÆè‰Ð¿Ä\a^ͽšÜû1izÃ?•y5ӿ⦕&—Ï«™Ñöœk á™ÝEÏÌ·2=L^CT5i#’U|ãÈÍ[{êÖ¦…ªy@UóáõEÙ{d -qM ó¶,œ‘âRFØÏ‹Ï’ÃgS5Ë•kËûÕÈ RÙßöúkúõŲõyÔÕu”ãóVP3Æ?,­e2Ÿù>f¿šGhrú~ÒháXž“= (oiµmýj¶¬g®P•‰æMºAÌÑŒSsÕ•>t3lÞI<âS´œªæUM»¡a·Ó¹¼Ëd¾ye¾/§â9[%ú]Õåý%m …m+…­ûÈóµÃ>ý&Û>tÛ\&‘ËÛDêðdضFRMÁnѾ“}œRåû™þýù_ÿÇÿü_Ò¾“ÑΪb¹ŒŠ—h›N浖ĬìŒYÈan ­ùi–t¶êBÒA¹ò3ýY²Í"¶F»ñ{öÍ‹°!Uó¬,0nBþjþÌU@«»™µçU°‘OE jNÕüüóüõC¾Ãýś윛Ë_ì‰ÝÍ骋û5ê¢0òÑEk¼¥Øµ’¾" )Z]ƒ Fn¤ÇvIbJë0¥;¹ƒyäY„e 3ìÕÖ];Æ?¦Æ©0ÎØå„/ÿñ[˜ÿÑmŃ^®ÒWÿËåÑPFµýèFìV™ÃX¨È!°ä¨çýuZLzL+·‰'K†ÁmUSlüå[[(ªæ~_—ØP;¯AF ^ p/ОPÕL.‘Ü#¡uS ýÅ.‚Ö|ÆV§ôg¡øOÖò6x´ëùÖ»’½Ztóžy6¬aŒ#ÒŠ&'÷J=QyOŽ]®ªËkÇ,検C½ª»ÄS[§¥¤+”ªY‰ÎÄßøgDÕ<ôûø™ÇÿÐêð{öü¸ª¦ýHœ‘_öãój¤ÑA¢Ï¥ϘC;{š#áü(.ÎÁ(ZàZUôèS/4†s‡’ùcþåY+NUS1¯¦FÕTåPbcˆçîój”PHZɼ7Kùdž¼%÷‹äÍÒlüþ)s¨šçS5ÏV ´:¼@à^ =?ºªéÝ_sE²ÆûÖ§’G +;Z—ôû4©c-3y~ÆhIêš–¥Øó¬;£>¢¬Ð¥[À9ª-³F¾T±r—m@µtã"]ñ cy¹ìV­&!Žy~ãRkÍO^|Ï]§vÒörjZ–äÊÕn¥OÕnüž5ÐP5Ïú>~6aC«Ã îÚóéƒí´H r× ¨š‡£j/U´gT Ç—€ªáúïã'©Z^ p/ОQ5áªæyTÍ3Ô­/¸hϨ€ªáúÈïcT à¢j€öŒª!¨Þ©»?|¥Ðêð{öŒª!¨Þ©¼ñä€Vǽ@{†Íªš-ÌZÖòP•7ÿÉ÷*ò•é®mǦw0K13ór»^·ßQ5¼ŸµjhuxÀ½@{~8USÚ¤b—ëD­áM^oœ­cvKU³`Ì‹ê^ªfoëÚ¡jžó}ü¨µC«Ã îÚóc©šd'¾x«Ç~zq'ÁÜ9 ¶ÿ‹6ˆìcK6[ìþy‰vØrØCý_ÿÇÿü_ÒfˆÑö‹J~ä“Ó=Ú{úÒi™74ì÷IÔ ÕÉcŽó)Ç`W„èçJÊ{wŽ6ÏNÖÌ2äóûÕ‹{zÚfÔ"ïÿË'Í,¥\Yz¶lö¶mâˆy˜ÝP5°æûø!+ˆV‡Ü ´çÇR5ÂGëÁ%ý{rdÏ_?ô[›_ÜÖ·ß%÷î0Ï cTGç]"ìω#¼œðå?~;éŽÜûÛää‰ùÑNŽò9ff,]’ùËñ)ó¹/J‹¹ÍÆ´%¼Þ? ˜E©ˆê¾”¬ Ó©f‰~$e ­]hšÍõ›ü¶såêbòðÊf_´­³8W¶T ð>¦ÕÑê€{öŒª‘‡âLYУ"º•ÉAÍá.F~q×^>ÿøç·£Ø“cçG;¹v„UÏôÝÝ£I*Æniî»ó œJ–O«jÜU™ÿhkç¬eɨë²Íý…U­ÅF»L³›¥»8W¶T ¬ÿ>~°j¢Õá÷íù UÍ8Ææ4OÕ_ýûÅËÅüx¼êhlãÂáä^3Øže1æ•TMžÏBÕHUid>êC¼ö¼ïÂaF·ÍÝ®«jf4{³YÚÅAÕÀÞÇTS´:¼@à^ =?¡ªº/œS¢k?É÷¿õ^¡w­¬¢s‚n(•°“'‹¼4^H9‹j%U“ä³ÐŸà7K–“NÌ䳡¬6`F^èÄ+]¸z_MU³7›e±8¨ØÉûøa*‹V‡Ü ´ç‡T5ÒH›¸;Eã4}„Ž}_Éý& „áñ¼y^M´“å’&s<Â)ÝI/Dt¦2$IŒYžW#Ġ̣XAÕˆ¦SÍ’ûÓ­žùªO2‘Û€¹aυתšë›½Ç¶vqj¨à}L«£Õ÷íùÙUM¿ˆVÁ—+-´¥Ìמ*»˜ã¢Ré²fB~ô“ã’ö™<½K“‹wIã…&WU4”³¼(–jj}í¬òÜŒ,ŸæÚÁ‚é4³d%mõgÖQc´;r»ºs5GÕ,ÕìmÛšÅiŽ/ÕP5Àû˜VG«îÚ3ªæÎJvÄÕÀhÏP5¼©ÖhuxÀ½@{FÕàÑ>kHæ ÑP5¼SŸû}¼ëŠ£Õá÷íUC P5¼SyÿêJâÉ^ ª†ö ¨€ªùu€[Áû˜VG«îÚ3ª†@  jVšÿµò×ÛîXÜ%ÚõKñ/Ø[«ƒÇ¾ºGÊÆŸ±´T @@Õ jí}¼waƒÞÀ T íùÑUs§”ôª`—Æv©ß~ÅáËïpÁß+g™ë8Ë ï²ÂìR\YÞ5̵…*X5{hc¨šDÒìZج4ˆOðQ5‹HšñÁB{†¥ûj*·Ë˜vŸl÷èè·×ÔžáÕ…;µWÈ­ª$6±CÈíüæ½”ñŽùDÕ0ž8pßGx£,ûŒ”TMÚ¯ü~ùü£Ýô°Ýú0ÛXpØo±W,q8ý0nŽ~|ëÅL0º¼ßW1M"ꊉ¥Eá¿|*îï9îÙeRØÙ0KB‹M+²ƒv‚?ª$ÿãµÁfŽÂN—ƶ’áAÛ8ž! n=©˜hܶrÚ8Uϼ«(Uðúõ³U(}‹X!‡®.Äà̼*ó3·ªfƒ’ac{$W>@x¤€[ÕÄ dt§""º¹KÝ8aÊ®í%ÓI¦oýÀ³¾'¼\ó¹;94ööt=<É…¡{W¢3}IX}5B‘ 1KD5õn©Ëù?L EqæOW5Eã¿/›š‡Y!õRÿÃØöñàdU•ØDÛ7’¶yë…RÊ9Ôý~¹Æsk‹5^¼­P5¨šý˜;ªn­jªu¿7kèW ˆåòªnk<ÿoæéÊI=H†‹Ü}íÎ’¦OŠIxF M±c(•Âéý—M—Çó§âÏX±1qJEûø­«ÄË WJË¡gF™ßÚûñˆª1Öú佂+=:x¤€[ÕxHµª {i¢ŸªÇ)â’~!5æÃ¡<°Ê„f«O f)Êzô€Tœ^B¸÷ÊŒÍP5yÆŒ™$sTͬJœwÐ;qKÊO±*Q5¨ÜST \«jò^š·lhS­ªñÄYèHq¯½¦Æ¹øÅ$ªûjŠ_î~vvf1ÿ¢Aìádbʪ}5yÆV5³*qEU#Ö¸hí¼ÆQ5;–4ž±° UÓMéW-“\^ihî~u³tºxÂߦ/;ö™æ'UMÔWf2\Á—Dš\¡È…Š¥ˆ§¯dCÈÄü=ùL’¼¤ý”ô\˜Æ)Ï«YBÕÄ¥ŽçÕø2拓1‰yójäV×xfm¹ÆQ5;~éò^á +l@Õ¤•äÍ7jÇ÷©~rd‡xšk•ùbñÚSϪIže«o½|>þ¨D®—JE.ÄP.…¾–W–ÿn½¸q™¬xBѸNW2ïbv©çÁ6Žg ´ŸÎÙ#YÆ”M‡¢ÆÊ™wWÁ‡™k ‰94U‡ÑÚâh¨T Â`¾ª!<Ø6š”N”ÙÆvIµQ=pE jê… /„ ðÜT U³Zé´>=jU³¨°á¥€ªT @@ÕìXÕðFÁGž€ª!¨š ^'¸)ÀãP5UƒªÁS@ÕT Þ9ª†)øwžü½…Ùç³óp³Ìÿ4WU^?ïosã_*oh'¨T Öž€ªYÎ?+oñ^ÿŽTͲY]dƒ”›iŒ»ªštÇÌ»(™{µT o\ <"`7ª&ßRÛ{qزۅ]¸6:MÛm0‰9K]Ø ³?¡Ý‰2>yÜ•²ËUmüéöŽÝ®öZaãÈ›˜Ãmãƒßã™}ÔÓ~Ä{kNiË•a°Rã'›ršµà,o¶Ñd´u¦v‰‘s­ù%ewªšbãLϔڧØüºn¼­B²­ÖÔuäo'¨Þ¸Øx>Àc«šüSôåHç6¿~\ºa?û`ÂôÚð´öÈ%†þ„Ä;4N°?<'°Ç´Æ\Í‹ÿò×1*½°‰Y.?¦#‡é¯“Ó9äA=MLËQ®ÌýmÿÔ(¥_^†ßÃù…Zp—7)]+o ='b$Vó³Ë®ª!ÿJ©KíSËžßJJK~£U4þëÛ ª†7.6žðȪ¦(„Oκ‹™ »)䨤²ù™p2{]êâož.þhÔ»â¶À°c}ã°¾õŽ©0`É8­8ÊÈvèûÂ2æ/Ö‚¿Æƒ¨<#Ðü¥s<ó§X,µ»yÔ[Éy³\zU;AÕðÆ@ÕÌU5áæîâ˜ée‡øà“¹–D}üã óTMÿ#ÿ¯èqJ§Yž«^®jÏ»X ³j|%Uc”½BÕ”Jím멚«S¯j'¨TÍf_H–P5œ^f«óZ1ò4~é1ˆlžµ¯Æµ´À9ì®ñ[ ï~yÓ2‰§©†5ËUçykᎪ&oBfÙëújJ"ÊÝ<–P5bI¯L½¦ jP5à ¬j*æÕ8¦ dÕ4s@1%žPV5Ò€+ñ„büÉj°Ñ +Li§éO³#ÊzYÚiª[\(W•Š(ÔBMyÃ×çÕLùœ3¯F.{¥ªQK]jŸÖ¼Ÿ•ÛZójÔÆiÖQU;AÕÜíû~<ß1 ðL€µUMÅhÂoi ´Ÿ©FJÇØ4n}4¡¼ÿhwÕL‹)6?¹oÜšÓl'ž†çÙŽÆV5éMdüÜŸ œ?6¿ÆDÿnÞ#wÐT¨T ª¶ªjDÿ¬ó½ç)v¾íïÓÑiz$íÉç¯z?2Ú¸]s©?ŽÇÇ%gTˆÖÐ]FAòäÌk/9é\ðKÄÿ—ã£þþv8”TËb¥"…\©)¦8£éOS— %•ºÉÀaÊÌ 7D#d-§J]g˜±Ø]c¦›ÛÙ*¸˜‡8]U–ïújîêñ„ÿMà–ÇÎT5†ŸÝ|cV¿jÿ,wAߡӚZЄ‡¡.ÄhåkÍ‚ØÉå×ÇP%ã†*Z¬®3ÁW5j‘óúR •tyµîx㦿õ®y¯¾ì,Y–Ô_nŠ]ÃØrôü[/sê¨)Ý#¨T o_à~TÍU¸w‚$¨P5‰èP5Ñ%šª‘¢µ¦p()&—\[öÅK²¡¬jjж¼ª©¯¯è`þß¹ªÆ6¾»âÆÀ¼Ú¾ˤZÁ…<„=9ñØ9³Í£jîø Öû^À@3j;ïMÕLÓNJój }5n×?ùB}ÞÎûj*}S¹ brúµ·P5Å¢åÖX¶¯¦¦¾’qqagÅ[6¼­VÕXÆ/UœfÆÅUUp;56GÕ j?ZTMì*™ÃrS5Ó€¥i΀KÕ×:çÕ\¥j¤äÌk‹ój¬ÆójÚ„úé㹪1-6s^˜bÐqaÏ«)JšÜ?Í9ˆª£~TXT/áåbŕ̸ô¼«àzU†“p\÷ÈÓ¨šÔÀk€ð\ß3×ÓÙ{0®yU9-L%^>Ë5¢)X¡ëô.%¬gå[¥M/ˆ”\»Ü–~mq ´)«ñraý…·[²žm±k )Ž«‡‰ 6ˆ©k…²FýIÆn~ÊÈÀÉø¡•ÄvR2ã$Ï~:JQ\Í.¸Q•ÅÓœsÏQÕðÔÆV´  u€CÕ{ë›}åçÆÁ³_Í6ƒØÓˆªÁ£°À OcgT *U3”}§’浆ªáýÀÓ ;£j¾ªf÷g[Ñæ4*jP5xiááUÍ{;ëéø¾x¶Þ§I^ï[z¸?¡ªÁûü¡X²\eÆb rf!g7#~»ÅúËn;Ö<  ØÂ=·@n1æÕ¨ójòߣ&I5Š[ÕÈý-¹PÉbf^ ~,¥jz÷(ÿïRªF’ ÁqyºHÚ#áQ‹¨šJYbýV–¨š4 0z$ª*%φꎛk$”UM)†bf”œ ÍCëèsó&ª&èz®<7ˆZ‰ö-?d 4ÿÌ4~,YÀ=mX‰àõÕŽG8²¬­P58¦@ãxVU“÷Ò¼eÃ`®í«Ñ¿Ü‹C†´¾…h¢ËÍT™–Úq¡O°Á·úJ³¡žéë]±Ò²cÐ2N8É")(3c¾§ªéë+ìf©ºAÒ¾÷R„Ê :ö«yž‡;ª†–Àý Ïcçl ´¶C`¸0½Ïzm0Í (¨šà¿8¯¦0×%s³ãËÍ«)v\„k0\­jÂH*Tœ±8ò¼UÕ@E Z¥ójäæ¡ŒpScÎWfÈƶy¯Ê‡}ýèif‚é´y5¨@ÕÐx¸y;«ª&uÑ$÷±Q;õß¶¿0?s8äCŒÆÅ¬Âå°òËÅU¡Æ û~ø×@“ö«×@³û‚u½NoB©••Ùdæ²s³‘¬%-Y4£Þ1D™i7 Ö@“ 2yób”˜‡5ÁÜk Ù3vœWeRä *mÏ S8A´ªUüó{”´|ù»ÏV¨ül¬©jUs“9ýÛ\`;¹š—mMêEóîoS>>¬¶<-4ü B¬Q„ý˜Åð˜Wí½¹BÕ´ÿÅÂEÎ$£Æ~ Ý,S,Âñ×Ó{{°;ÄÖȘcxe—F MwE¹ãO/GÕàjcjÀŸ¦xnªæÆNÕÍ’+&´M%3_Õ¬Y–V5%ûÛXǃE§=LŸGÕT•ô!TÍvûjJZ"»0Ô=â…½ú8O'w‡?]~L“j’䌜ˆñWRCÕàmcç§µ³qïS ÔÂ#YRW5òö‚ö>•Á&€á÷øáÂÈ… ¶G< Ûf;i£5®vðl¶ÏK1îï)žàÙ¨1Œ¿¸L˜V²S¤$ Òœäiå¥öxÆâþ¡¶yÅÌRIv„¼$üóaWûþÆ£ec(N¤£<–qÚS‰jÜóøMÚáÔ®Pÿ=ÕÇÙ®­f;i-— ëG7'¿ã}lo¯jì½YQÕŒÝ8Þ ÓacýÇcйóz:¥Îª12ƒªT àOS X²¦¯æâÀu.T3¬(ßC0rLתq¡>~y~‡'Œ1Œïƒä.Iô^lôW3ZãªÎ‹2¸•£“§0ü, Æo|z·Ó*÷Õi ¥v詟$ò‰ÍZË3¥ªIÄŒ³ñHÙxë½öpÌ›Ã2^{jGQÑÊ›Ê õßS—D#ñ/d;jÌ^k“é¯eí}Û¾škžæ«©š±[Ær&0â1bÖÌ›‚ªãGÕàscaÀŸ¦°ä®v¹ÚªUÀý‹? ÔÂó©ÏŽW5áŒíö)݈ӎ¢ôYo'ù6æ»çÉÝ«".bøg;i¢jðŠP5øåXð§©X]ÕŒ{eŽŽ{çx;!ž£-ÿ ›T›NæŽfö5}H½O:Ú÷бèÉF~ ñO#æE â6s”=ý0nå~|ëÅL0Ê|Wd©ày´ñ…²#^,šVAQ](IÛ+dø0”=>'±³XÑö&§y3KçÑšªI¢7ú”›Yfí6I“hTÍí(Ü\fËGÕª×Óþ4µÀ»LU5kô«Æ%;×sì‘6=|;ŒŽ~‚Øýâí«¼º`SËFBŒ=$y¯ÈÏd÷öw3*ìã——á÷xB^´¦ý¸²¦v¢Ýïn(Ú·~àYßf^/¸mrá종E®—¨m1ÃQ‚z”› {•öcÆ?¿¯F¶ÿÔMW¼M²ìè®”o®BËGÕªï»þ4µÀ»LU5F7Hô YwU¯R5ù…Ál{jDó#<ÓyÕPO¢ýïü¿¹‰DkHÑþ´¿hÅÔÕ¤íhKö×Få$Ö׌@“í£Çok;çUƒ0£›«ÔòQ5€ªT àOS ¼Ë,U“ïÚîrÙWR5Éüo[Õ˜Q‹VL4ꥉzlÜ&òÍb¯-ZYÕ”’öÛÁ›Ê«\…GÇ-¨jìøUaì³gªq·|T  jUøÓÔï2UÕ„­Åúwè«ñMCwE%­"Ñf$Ò0£&üíî«©Y†kIUSJÚo‡r¥ûí\£j¦™QWÌ«)6K1þÜþ2ºTxs•Z>ªP5¸é˜ ð§©Þeºª |¬dÂ@8Oº0¯¦¡Ÿë,é"19Å)œf\«j¤¢S&äy5qhÏ ÏÏ\[cð˜íbª&¨ {^_Õø¯Ò³EMH¨hLûªq^\F[Õh·€|sZ>ªP5xêØ ð§©Þeªª ƒzù|z›Vôz‰Ê×°š^|¸d)ÉëNáûÛB#в¢…C}¢5дDvðM›Ø”™?Õ‚§Ñ.ØW3®Ê%®f'í7¾e[ÓÎjEÖÐK†cÉ2òÚy5YüÅ5дÛÄX¦\¼’5Ð⬖ªT <šª9Ÿš×Øëé‹ļ·Oïãûô;¤;~%I®ÚTƈåì…¹šb¿Ê¦QøõýiÛ¤F1}˜¢×ÎÜŸñ—«Áøß…_UÒ]Úséýjž$ŸÉwv»ç a¿UƒªXüþÍeMAÔXN^÷Ï«SÁ­ë´!•Ëß þ^­S˜ØàòÏjviU3äžÜºJ´;ã/­jŒDçäjöDÕÌÛ~·ÂUC@ÕªæÞÖ¦q.pÿ¦²æâýDÿȾ웟®ÓãgãàœðSroÿ<ˆ'‰bð‡3šÿOÔ¿GŸÛÃOâÉ‘ðòOŸ‡OŒçöª&9 ?¶@ø§S_ì®B“Žˆ(FÑ6{1þʪÆhð¾æ½C{¢jªUƒ°Ù›ªI¾¢¦ûCs¼uІÃö€œÜ=½©ßŒÛ´‚‰@k5žßq¼hìI5ÃÝò$f#r;³.‘-ôÕE+Z -ÏÙ2¾\Å{3þ½T·yïО¨€ªÁƒ<ŒÕ„56;Q5‘Ã#ŠšØçõ«šñw~yâtE®w|¤ûœ=D5 GjãâÄlÈ=Eï{R5E »ò*Þ™ñ—V5IfŠÆô7ïÝØUC P5xôÕÀUMä*ÅÓšë¼dQÿdV0D'˜^º\ý¡ãqøªÝÆ{:*Å‹Ž áª¦ÝVš³xOUS–”3êËî…ØñïÔWãoÞû³'ª†@  jð §:¢›ý¨šÄëú5¿¯F˜-;Rñ˜éLé ]9{$lÿµ¨Ùî¡jŠój–ï«Ù™ñï¤jüÍ{ö¼“ªqîž~û‰ïé6í«, nwS>>¬ä;­#|—ÿÏ“èJEX¤Í‹¡o!5«W£j ô&ÀÛFØìBÕd^Xì@yçÕÈÓ6”¼¢HÓy5¿²Õ¢úŠ]I¢»9Å&e#.Ek>¿änªfÆØ¤Š¾5¥Šwfü;©šŠæ½;{n¬¯fªfMÿ¯UMÉv7Öñq—Ìp{Ç] Œ»WñÖVŸ»²ŒžhQ5pÅ›Ÿa³iU£|Êõ­yMÙÿð’èììÌDé$_©Mï”­ô%ú÷ÚY<·R5ÓH‹©š_ê6{2þ½TMMóÞ™=¯S5ãfއvß÷fÿÁxOôË /Ÿ4{êû'¿…®X¶›¡q²±±L˜ÃdóPc§N{kÅx?ÄFl Øž²ë^¸ü·s¤šVÝ_Oîrpt³Þß&ër¼w̓8µ“ÕÏØykwšOúI"¯±=³¿¼ ¿ÇTò¤¯T5ê>÷ÕÙxëÅg8æM4xZä.6;þ m#Ot<l~Z‘[íü<ŸIrqG™Ô†%ƒhu­•%¾¬B©ÙðôÕdYu$G_ oT þ4P O jäÉAwÍÔQ“uшçkñO®›ÞÐ|ŸŽ$Š5¯ÆpUÃìeYµ•’0RìXKŸÿ볊%ÛàU#š*,,Åã1ݘóªÜöŠÍ)«¦Œ,ÔãTËr]ÔŽ@ó'‡ªáM`¼ °-SàOµðPª&ÒißKÿÃvÅ,?²¤.<®˜”Ca˜–­7lYbý>è±ÜS,¦fCLjíX³¡Öˆ¹FBU1®V5³r«‰Õ<Ÿ×«#Îbäóª`¾ª)%÷hªæýh ɵþúý˧OuëÙ<ø›mƒ7ð§Zx,U~–Ü&ŸG¨ºzñà+ÛÇU%’ÃiBˆo^Mžׇp=éyÇó¡S󲡞YÎW[Ì¢…]ª&ì ûjܹk0|åãkqªe¹² ®é«¹Ñ»ê«IåMÿo[=¥ªAÞàÐcüi FÕ^Zì¿¶³5ác2@8eEžW£ÌoqÍ«QrX§jäy5ÅázÒ3TMI…ª‘³1GžWSçRϰðäÜ»æÕŒ‘ˆój|ó£¬ó#$WžWS«j¤²ˆój”P9¯F`V“܃öÕ´?’m¾Úƒß§Z‚µø…Ÿ¨´  jð§Zx$U.÷ôòùô– ìI&Àèk M 1¥ËšËì“ 1åðû´ ›6>JÚ¯F\ÍîpS uW¸Šõ[Õ43ÉZÒ‚Á‹B®ÖÂý%³Ö@SVÃs®e—ž˜7ênWuó¬¦ë’»Èò²üüÝ[ÅlhvÖX!¹‡U5ƒši´M u2ñ¢ÿU¸õ¨…GR5 bŸÁiº5¿z-e×W£ÿúø²fÙ:¢ßÏž²ãOµ€ªÁ½Ånñ…UC@Õ<§ªAÛàÜSpüi P5UƒªyUƒ¼T þ4P ¨€ª¹—ªV`^ ÚŸ"ãOµ€ª!„ªšn5Ö@¼|Ê‹H-P ¨@Øë~5)ìW³Rr¼§qô),þ4P ›Q5œZ}e–æ]þTSÌo_X-Å~‰d%3?«~߬¤O¿Á®UÍ…ï_>}zhIóëN}5¼ªæ? ÔÂ6TÍF4Ã]\F{Æ–"Q5þVq¯ÚÙW AÕð&¸ë4ºnhØÜ¿øÓÔµpWU“8ŽÃÞ…ÝÞçÑþŒÑÎŒÁÞêßûKòxò=“«’RWRÚ¯0ϧ¿DâNˆÚÉÉÆ‚ã›PÈsgÆéŸÂö YÌ‚å=»:†I{5FÛ¤ÊùO¯·*Õx¶cf²1«žgeoJ£I4ùöŠmòü~ùü£3µ£~“Lk!ÅmOgWª6¦jÐ6ŠûšZ ¶£j†ØÇ 1ŠÎhàu·G—˜ñ¨W ÎßÅ-îÜÇóׇn£I1~UÕøÎÔ£½¤Ûï.qC¥./9ìOŽXyÎÿ©¤’Ƭ÷”Slÿ4:â­ØÐ ˜]åÁYãzŠZ‰”R¤Í#?mL¨9r˜þ:f+“ïo½ j¯]¡…ˆ [ªÊºŠ@ÕÀVU l¸ñ§©ja ª&w¿šïÇ.ߺÏÕWÙýqz¾£Gžý;ÇPyŠŸþSJÅŽ¹Öo“(Ð9ÔªÜr¤ëì¯ÿO»hŒV4:ð­—½&,gò†-ĺ­®¨T lUÕÐoCó¦àOS ÔÂfTM0§wÂ=ÞáÞëÖW^uUæ³ÜÛ0WÕH©,¬j”:“–G»ÕÖøjª¦ÿ‘ÿ÷ÏÅÚÒ"-¤p[UVª¦À Ö;Ó’X,éÚ¶ÙWûÙøÓÔµ° U3ͦÈO >9W¸ó®ºW_==;af_Mž73æÅT˜´îLÏ«»µUMÞKóöÑÃvÛâ¼­*+U³ y³®ª™±õÆG Ñuó„–¡Æñ§©jaãª&*NÔ¶ÝÇâUÂHk®Å2ªF>üžf;(Á=¯ÆrñÅT¤y5Á˜®ªãØäy5.Uã®q=ÅÅçÕŒ“ï§y)Ÿô 2©Ï«¹®…Œ1ô‹(·UmE<©ª9Ÿ^_Oçáç!øýzúѹÿÍñ¦þ¿·ÿlÿÛ¯Xñ>E’IRù•ÄŸ\ ŽàÏáq)¹ñTã´Y;‡îb^ Úæ©ÌB]ãOS ÔÂfTM00fr¹¯1[Êéåóé-XàK^ôI½ª9>žàYmUæJ?y\ÈKYäʽšÙq!¤’ÇÚгÚÏ?¥|¦+’¥IÛδ¿ÆõK#ñjÖ@“»DÌ9*RlÂh‹µ.‰K–ÌÛª¶"ž²¯f·?Z­0é‘Q D¢àrrÿC+Äêþuùï”tE¨„Ú|Ldzs…„óC©`òÉV @Ø jð©já‰U @èºM¤5îÛ6¨„ÆÏ?ö¾þ{òKÕ$êÃÑ]v©1'Ý;âñð`’pAÔøeÍîT ý6Ïà`üijZ@Õ7ØdÓìf!ln^ÍØq’üשjÞãUT©v A†ª1’“ëëCÌžEÕð²xkP¹øÓÔµ€ª!¨áMöÒµ‘]Z_sŠŠ!™üªÆLnê³ÉO{.UC×Í{?T(þ4µ@- jªFz„ÓVÂß–ª™†‡ùeM8u'ŒEœW“õeój,Uü´ÍÃ:@¸¶? ÔÂÃXUC feçDAhódâEĤÅÊ­É¡_…5ÐÆEÌ’5Ðòä„Оj 4xZþ‚›@-P ~·Æ-‰ª!Oµ_g°™SçÜ”ÇÛ¯†~T àOS ¨šý¨š­EK P5ûåû—OŸjtÖ£öÕ½Ûb6»V¿_O?:÷¿ßÍòø½ýçwaëÌl¿K»7%ÚU3þ1¤õné–ðÌ.ÇânžY–Ø…m€ªYDÕ„+›ìƒÓÂhÁhÁpµ^ó°~€ª¹î™;ÊšN&Œ¿'µ0(mØØåäþ¦VôSo®j´lUdTÊÒåéßó3|²Uƒ/ðtŸ±pÑÂþF  ¢¤ñó½¯ÿžüßR5‰ZÑ´BxšUQgˆgÚø½²Uó<ý6ôG jÂ~çÕtþ}þ_§ªy—<ѤB1*T =àÙˆª!¨š¹ÏÜ´—æ8:üξ<@ÕÐu€ªAÕTÍzÏÜ`šKôÛ’"Óp²²F™W³”ªa^ Ú¿xz jÂÓ­ìœèmžL¼ˆX¬J#Ð~K¥ék -¤jX7:" x¢jûÕ\I8ÙfÞ¾4×Ç  #ö«A lP5U³_¾ùô©F%¡jžüƒ%õ¨@@Õ<ÂÃU³À ’&|ÐÙÕC Ž{©Øx¡mðQ€'Æ}Šþã/€JîõðÚHñ»×@(!¨#^ùäx\ jUƒªAÕÐoƒ§<+P5€ª¹¡¤ßÔï~Ø„ª€yÞ-žîs6€½Ô;íP5àrqi äx”¡jP5€O€×<P5w}úã5Ò0h€ªAÕ j瀪`ýG¿ &‚°à¾ jv/oȪyÀ UÀ›¶ÑZh0À» UÀ›h3¨,à]†ªàM›Qwo?@Õªvß„6ðÏRL€¼@ÕÀ2ÂæŽÚYðÌT o€GhT EృªàM°¼Æ¸qCØð.CÕð& Dx ÙÏT o ±‘y*àÉnL°#÷6.ïâ©à©c+T ìÛ·ÆSÇV@«CÕð&ÚÞÚ]7 FÎRe«¿°ð.CÕð„šÜ|*G–=‚A<éŽP¿¨àEÕ>v¸[k\$ZîT ð.CÕ>vð6È5Úäõqr§ jP5€>„}Wõ‹ª@ÕN3Ì1év&,Q¿¨à Œª|ìŒ%a¶õ‹ªžÀ¨À‡ÀÎX¶bäy‘P¿¨à Œª|Dì °X‹½ËÔîT ð.CÕÞ6vØwëåNAÕ jV±ùÍ– ~Q5¨@ÕÀæŒ_¥ˆ¨_T ðFÕ>"vÆ’°ÅZð êU´ûrrUSÛWã+ÔölxU³^_Íý-Œª¹¥ª¹=¨T ª¦JÕhójœ]‹¨šlÊN2]G^«@ôÓ¨¤Ù@[S5Õój¤BíÀ†÷P5ÛºŠŸÅp £jP5¨T À3«aCy 4Ó±®y}}M.ÔÆÒØÇ¥$ɱ,ù‹áÐ8¡t›gÒµZ^¨¼à›³áÕªFÞ¯æª5Ðj‹Ÿ+¦ûYUsƒ§ë` T ªV±ó’ªF\PKÒž—³$ìZÒp/ jP5°q_|ü(ÿ”šUƒªží¨à͇/Ž%UóP5¼ù_K¢j@6évZ ÷ªUøâXU;–4Ü ¨T à‹cI, <ÛUÀ›ðű$ª†§(¹T o>|qÀ’¨¡<Û€›€7¾8–TÍ“>?¹P5¼•_K¢j€g; jxó¾8–DÕðØ$“€ªà͇/XUÃ3“g;ªxóá‹cI@Õ<ã“{UÀKðű$ªx¶ª€7à‹cIT ÏIò ¨Þ|øâ€%Q5[0ªP5¼ù_K¢jxB’s@ÕðæÃ,‰ªží¨à͇/Ž%Uã7ÎÞÛ÷ªUøâXUƒ¤áÙ¨Þ|€/Ž%Q5À³P5¼ùðÅK¢j€g;ªxóá‹cI@Õ¸¬ñ`‡{U€ª|q,‰ªAÒðlT o>ÀÇ’¨àÙ¨€›½ùà6ؾ8`ÉMYן¢ª`¯üõ„¾Ú6yŒZØ‚Ÿ¡=oÁ’øý”P5¨T ªK¢jpú)  jP5¨쌪AÕªU¼Ë;½}/ïñIÒ±D´}5cÔëêT OQl…ªž}Øà™UÜ»ÒÉ‘æhû÷×Óy†ªé/}ãBÕðÝœ­ž¹åÐPxOv†­·¢hH·U5“pIDM,k’y5¹’GÂß”§(ªU¼'°3ÀSHš_w6* [#Åzé°ú 4T OQZªxO`g  =·ªùJî ÝWãT5çÕGžá›ò¥å j€÷v@Õ¤‡qb‹Ì«é"Ysè¾)OQZªxO`g å j¢qb‘Yb ´0vF q/ jP5À{;èmæ!› ÷¾)-‡–ƒªÞØ€{ß”–´T ðžÀÎÜ ø¦´ZªxOvš Ä7¥åÐrP5À{;›P?DÝ4ÑyêUÃ#Uƒª€¥ŸËà;oÜÎk«QI\#k®›W3s5*Þ<¯P5¨€çx—ÃÒ¶ÂÎØ öÓêÒÐk Ù³«ýk U©šîjÖ@»ó7AnZªUØ {b%'¡ ºç¦œ¨æÕÐrP5¼ËQ5€­0&†Â7}=3·åLË3î1€–ƒªà]W=‘±3o>àIÈúHzfVËIö·'deËÐrP5À»UÛhoXŒ;ô6Í£²åˆê%[8œ6Í›{x’6ó `ã…CgPéýû…P5À»Uƒ¡Î€´7îPîÐm¶œPnèjg8++¯§÷VŸ$š¨-ñj—:è’TÒEDËAÕðDÆV¨Þ|ÜÑØ [q‡^Ór¤åòDU“ÿ³W7ç_š"òü¦å jx"c+T o>xŠ'¡¶žrí®—Ëî’éM›ôk©UÔ¸C—x¶=)½Dñ¨š¬îÂuP5¨ÀÛFÕ j¸‘aªf^ZÊñžVιCºõ Õ‘üó¬<»Ô"}5¨ÀBÕ jÅb4°åUÍø}|òþ“ý1óNdJwp~sz.#¸ª‰×Ͻr^ͯک5Qô¯ª‘ˆ'ÖsÕàåfäÙŽªà‰Ì;óæƒ½ªš` 4ABüª^í—¸ Zq!²nÝ^ªé®øÿÛ»—ÜÆ<À>RÌÚKïûÚxç3L€>‚9@¯g–1¼Ì-b¤ç=(²HV‘EJ¢XÔ÷¡Ð0ÜY*>ôÿ™¯Îuäî%{’œ¿{ Ù·Û·K5`llœ}óÙZí}|¼M»ÙÕ´§sÚBm•öíR Ø#K5øæ›9&V!{B[¨5Çš#ÕöÈRo>Û)FØjͱæH5`l¬¤ß|lw­#Ÿ-Ծݾ]ª{d©ß|VCm µæXs¤ÀYª1ζMŒ¶-ÔšcÍ‘j{d©Æ7ØBm¡ÖkŽTöÈöÈÆÙ7ö„¶PkÖ©ì‘¥|óµ?»UÅžÐjͱæH5ÀUöÈçOg=—mºÓ\úSŽþ¾~bwølîþ³¯Ôßµ¥šœ~†¯™ú¹–Fß|Ê#ƒ¯6µæXs¤`±Ts‹$ÓOMá\’Ï·n:tüéœkúý”jfLpéaô͇ÚTmjͱæH5À=SM;Þþ¨_vþí!lTÇTêã)­Ã>½™Çjz"ªÉ‡3Ž”ãͼ#iÿ®——ÖÿÕ‡ƒ’úJRMìs «é|®~t\z}ó©Š,µ©5Çš#ÕwK5Çrµ©|›ºvÿº”Ýÿÿéçð…M­›Ê*SMóó9SU/oÊñÓ {‡w‚÷·»T½ê£~ÛJSMâs%SMìsõÕ,:Œ¾ù:VU¤6U›Zs¬9R p»TÓù‹þW¯Pý’v8« ¿2(œ«®©¦Õ³º{ÖãaäŠö¹]¼¯.Õ¤>×è²H½øÃè›O=dY¨M7ö‡ 2I5À]RͤX’N5ÁuþsSM´ÞÞÚÕyX|ï[ÿ&§oýŸG‚Ts•î·Œ! ÿIDATÏO5‹ £š ©FªÙ¤O²I5ÀSͼc5a¡|Iª‰ö"¼üãtpàåeæ1¯œS¦ xÍ$ÒH5RTƒT,™j"§Åï6XUŸßSÝ*+xcę̂áßÞ€¸ýä•øÍ»ÎgÄu.ï|ºµž–ú\C×5E>WsZàiq,:ŒRÝŽå"Õ€Tø./ò»|þ8_åð‡š ì m¡ Õ€ïrî‘jêÔi6Z3ÙÐ, [(H5€ïòÇL5ÆùsÇÒ·'´…‚Tø.—jŒ³M ‹É R à»\ªQ3-Ô R ø.GªQ3Yâ^ëð„xj@ªQmg©ÆâÞÏñÔj@ù¥Ú6ÎR RTH5 ÕH5ÆYÍ„T#Õ€T¨¶¥©fùn[ÊH5 ÕH5”šj,_¤PI5”j@ª©Fª¡ÈTc± Õ€²Lª¡àTãB¤j¤ N5(R (ΤÊN5 Õ€T#ÕPdª±j@ª‘j(8Õ¸©¤©†‚SH€TRTcœËN5 Õ€T#ÕçRSe€TRjÛ8—šjœu€TRjÛ8Ÿj,¤jTÛÆ¹àTR H5ªmã\dª±°j ¼ ’|—¤g‘©Ý'1:ûòj{åvUýij©fÕ¶T³ÚTR H5qÜfÛ©fKéq—;8 ÕµcRon¤©(t'µÅ?ó;vR ðpÁfc1àRÓj6¶]ñË3H5R °Õ}–óšÖ½t R €êYà©@ðqjÓËtÞ© àx°T#› ÕPpžijÔÙe¶A@ªl© ð ¦Ï Õ Õß+/Á‹K" R €`SpHij¤€Köw57¤€ èk«Ñ]ùR @Ù•ºØR €ˆR À'Š5„Šž'k Õ6ˆA@ªjî²C¼Óa gÁ€TpÍâþ.õý}C…Óðj(8Õˆ4H5’FÙ©¤€› ç Q ©i ¤€¢’@ÑÓ¿×¼@ªXQª¹i4@ªX(Û”›j'¤ Ž" R ÝPPNij€+”¿Wg ÕÀr©æŸÿýWÓ4íºM°j@ªÑ4­øTóÓ ¤j4M+=Õ6R H5š¦Ÿj;Aj4M+>Õ6€TH5š¦Ÿj@ª¤MÓŠO5‚ Õ·O5?{‹”µ|—\7W=ñþk^3'ÞÙÙsl¤X6Õ IW,¶ÊM5 —•3¦9é-á‹7³|¯2÷y#sɬ‡SÍè”KÛ¦¤@ªîšjþ|}9þõåûïçßÿþþÒéN$v´§U2ÆÞ^ýù?6Í};÷¡éF«o§Y¤{•ÿúó/_¾¿Fztòx˜¢ýöþ³;ù±«¦õöc°Àí/‘΀÷ÁèÒYfù.¶ ZÓÿå_‘Uú×÷?£½Ê™Îz˜êCçÏs9¼%øùùõ¯þ®ø¦×žrjhä<—áñ¾àòí+/Õ|~~Ú÷R p—Tóôôüúµÿ塾©þ÷ëû¯o?εÔî·¬¿y¿Œ¼}ø¯Îûÿ=õ¡*¿ê žûv|YF¯Æ_ÿ±«JÃýÿ>e•žÍoŸk|¦û_Öuó1Þ §šÞiwc?…ÓÔa|é,²|—\íéïG£šxo¨'Lr=Œ¬ºõŒŽýo–È9 eœÖéÏÀ¬÷ã–¿!ôÆä:ÛWvªl©¸KªéׯÁß¡sª±n{ûñÏÀ­JkêI;“z•zýá—Ã=­ý fÎ48zð3ãXM:QŒŸF5úâ[,ßED{úÇÃ#_9ç•ÍÆÔ4÷ó=–þ‡±«b@'f¤šô¬ß÷©)È“ÇÿZÛ×”T#ØR °†TÓ¹»ÀÔª7ñöóïãÙ¦õ®hÕ5¥W©×ge† “±ÿšÜÉÈkî›jn³|]³ûÎåøðA†œa]»Sëÿ;7ÕÄg}ÔJ5ÇÿZÛ—T Õ@i©&ø;z~u˜ùöà<¨ÎeÝú5ò·ä)—VG_?ZLw:Ù=V“s\"§x½û±š›-ß%Dïe§0Ó9&6#Õ䬇½Â£4»Þù–Î@šõWx¸f|ü{/¸Êö%ÕH5PXª ϰŸQõ¿=žj‚+¼Ó×3LèUúõõ%‘Ë9âÅtS(G&8q¦y×ÕDǶéFÆu5±S‰–Y¾ ,ˆÈj|Ì3ßÇ®™äz˜KÇ[4×¢<¥“C2Õ„ýÜ‚KkFÆ?z]ÍåÛ—T Õ@q©&¼g×ä3”¢oÎo‰WNõ=¯ž_ßwí'¥´ËµI'ÞD^îIäÖ[ÑN:6ü¹rfZß]*ïhý%vcàh>/¶|—\±é£QÆe!£#Ó[«‹õ‡/€IE¦Ñ>á”G7à6hCã½ÚåÛ—T ÕÀZR»Ú[Û‚¯¨‰Ôy^TH5€T³ÌÃ/úk´¶Æ1|~š&ÕH5 Õhš¦I5R H5š¦I5R Õhš&ÕH5 Õhš¦I5R l+Õ,s]õ­ç²ðÕáÕr]®iR €TÛH5©) ? ~xs£™Þb²ó¦éN_šT ÕÀ#¦šÃC<*§'ÓW•qðx¾¯ºJî?ç±—ŽOñ‹Ý™·óÞÓÏs¯f}P`çQñͣЃŸŸ_ÿ:==°š`õ0Á°3‘Žõç•êUüîÃGCæ=÷05†áL#¯ÉèêèS85Mªj`ƒ©f_ס¥~Fá©8Ÿ]xþùcWú‡G¶Ÿ~™ñ¼ùôa“sd ¸ŸrõØøðù‰Á£â›<5ijúúãD;ÓîXd^‰^už³>4½W³y{o¦óºêX&ÕH5ðp©&9:•ñá(A:\”júo ŽÆD'rŽX‡´°«êû*9t»=–j¢ó=]möçÍ|{ü—Ù]•j4©@ªGL5ý§¹OŠWN5ag¢'¡^ÙÿwFª‰Ík¥©&»«R&ÕH5ðp©æxµFujS«2>¸Ã±š‘¢¼{”f÷-~XÖ±š¼Ð2?Õôf1ÿXÍe=Ñ4©@ªí¦š Àt®«©‹ãñëjŽS¨.gå¢èìù¡¹€dðÊÍE&O½TI5áL›ŽEæ•qôÛ\WÍ'Ó»šyM“j¤Ølªi´r¸îü}×Ü=¬¹Z7*¤~¹/¦ƒ:ûðöX¡_ÿ>>vé3Ð"Õ|â:œŸ‰Î´;ÖWÎm ‡o7ûhýŸçu5:š&ÕH5°åT£iš&ÕH5 Õhš¦I5R H5š¦I5R Õhš&ÕH5 Õhš¦I5R H5š¦iR ÕR¦iR €TH5š¦I5R l6Õ,Iª¤à†>d¯ H5€TH5R ÕR €T0àÿ{V–uˆÉIEND®B`‚ Volume 479.2699858975891 226.94112549695433 block : uint32_t ElToritoCatalog 472.58578643762684 344.73506473629425 bootable : bool type : enum partition_type : enum load_seg : uint16 load_size : uint16 patch_isolinux : bool block: uint32_t BootImage 470.4142135623731 487.3919189857866 In a future we can support several boot images 251.63542622468316 429.69343417595167 img : boolean BootNode 193.07106781186545 334.49242404917493 TreeNode iso_tree 180.0 40.0 193.0 69.0 The img field is an implementation detail, used to distinguish between the catalog node and the image node. This is needed when the image is written. 57.81118318204312 584.0458146424488 The support for growing or modify El-Torito images is really hard to implement. The reason: when the image is hidden, we don't know its size, so the best we can do is just refer to the old image. When modify, all we can do may be wrong. 748.978906441031 574.8973495522459 The block in both Catalog and BootImage is needed for multissession images 629.3242465083424 441.1316647878586 File 188.09040379562163 172.5340546095176 CatalogStream 851.105100475371 283.5127233261827 FileStream 743.4055403867466 284.4253525880894 TransformStream 958.5987801403015 279.8322618091961 «interface» Stream 847.6728065449973 157.05765855361264 IsoLinuxPatch 968.73629022557 384.6660889654818 Generates the content of the catalog on-the-fly 517.6021638285529 107.48023074035522 To apply the needed patch to isolinux images 923.4814562296309 509.1168824543143 1 image 0..1 boot_cat 0..1 node 0..1 node 1 ‰PNG  IHDRQgЈ[z`äIDATxÚíÝM®ã¶Â ao)èi£fíyMîº&5ÊYà P;ø t¦ dœ;¬ƒŒ¾Î.îÁMöQí?I”DR”,Û’ü¼ —Ž,Qÿ^S"w?ðvëÄÀê»â’àaÎ#Îçá<â pÎ#Îçá·Çù wœœà<œçÇûÛîíóœg£Îs;²&Î΃ ÎËñã•ÚaêM)«éìp>ÚÇaßl¹þ£}„èFÎ΃;:Od¸æ¸iø¸:JROÂ/žèòJn® öŠnä<à<¸«óô½¦#3™¡žÔnÇcÖž¨¿‘ó€óà¾Îó£~P-xÖíFçiž–k?ÿÖßÈyÀypwç9ÓŒ÷Ì2ÎÓÿVt#ççÁ£§ü}žä³j3JççÁ-Ιµ­`Þ¶z¯ÜDýG7rpÌîµG~.ÝÁ³&i¾8°çycõ­ë›Á¥ú+½ƒ4¿¾÷Æ::_/XÑ5]uIäK¾m«¯è±>xƒ:;DT¡wÆê,ûo_ßJ®:“dž“kR:$õý¿‡ê§üO‡?ç<8çá<œ«ržv7®>¾ýtÿ9ŠÇ—_šÞ᧯W‡¹v ö¼ü³þ\±ýƒzâ eu•G¬ŽÊ×ãª%ðøÝÜUE~à[ç-ç™ãÂoÐБøŸ6^s×icìZJSûn 3ÎÓI¢ã¡BS·à<q”Ãy°fçÉŒT:Tï}hpÏTÿ²u´èAN?Ò'Ÿ»K=”XþѬøq7–§Õ˜ÇÕÆ>ÛvË…Þ üÉhd+Oí;e€ü¡ŽîJ{ûëêvœg ÎòÇ2¸D8lfçÁê§ÕõìMlì¤f÷ŒÌ”=Zl…j{Ä|†»¼Cs3Ìæ<£ÒêÎ3þÂoP~‡iö2.µËR¸è[¥‡ªÇ©úÃDêvœ‡óp΃•8OØ«ë:Oð#ý@'uhÏ굊«·$‡JÒ?ÃGŸq*ú™?ÿ ~o‡éã<ã;ô÷ç{á%7(½Ã½gú8OÙÄ©C]T'/â<8ç¹·ðô-‚ó7:OW'‚_ó.ÄP×p`Ï΋C­·Aš®dä ÃÎ{r)±‰ïóD»æÙÈ|ë†÷yæ¾ðÁ”Ü!ÿ>Ïõ,#ßç)wž[ÓaHŸÎ¶ó-ò.ºçÙšó,-¶«‹¹v‹vžìú<Ç.iÑÓDù=;3°}úzøRívÚ˜?È•xϵþzyÄ&ÏÛ˜Vío Ï`ö×£.|ðõvh-eŸ·íºqô¼m…ÓFßžCsUŸm-¢©êvœ‡óÜ=ªëмvKwA¸wøóë>3mÚ—…Å\Ý€ópÎÃyÀy„—ý9Vy ñgÕí8çyDɯ´]éºýü¯KlRG>írÞ»þÀyÀy •â< A‘Ô)jÉhl#2Γýþój(¹}ú:ÑÒéú´Ž‰2çç&Šó€6E:'ºòQŒðœ¨ÏtžIržú™µäW<ý¡QsU'žÂ8ö‰ó°ü¶{žqžþøÌ ó\¾rœÈ–;µÝ2'8çç,Ú|´Ý“Ûn¶ÎÃyÀy@C³ó‘¤Ún¶Σ)ç€õ×q{JJÌ )¦íçá<Woð\4ʯÜv»­œœç¾ieœ´)Oé¬w6JŸ±É¸™¶ÛÝçJç’Ãy`emŠf(Ÿ2Ñ•yF&ZÔm‚õ?;K‹žþÙRuÖí¬~±Hº~õùcJ™¶{¥yO‘áO(#iªöj©Ìþð~¶—Nƒ|Uš°É>ÿ«¶¤ã©:ÃKÚnÎÃyðªÎ“QÎ/Ô¦èÆ•$È-k’¿5v$gðŸW÷ùH6Ó%ŸµÝœ‡ó€ó T ?bÃëêMЦl3 óÂsC:WéU`Jœ§gJálœG)çQrftžÜðºz6Ô¦¼x.sùs8OÞI¢¿6FŸi;:“qÎΣä”Ö¹c(Н«7`SmÊkvéF½×4zƒºé¼´¨×v^¸é*Jì}œv‹Ìy88’Sà<…ã<‰áuõ&l´Myµ^ÝØë>©åyší™„Z sd’¶Ý~¿œ‚ˆóppÎ3Ây>žüLç½±»¥˜ ¾µÝî>ççi¤çé“üsxp›²íîiî´Ýî>ççií›dS½ ÛoS¶º€Ï-¥×ËyÀy ä¨7@›²èTº1¡¤3çç’£Þ€ ¶)Ûh°Lm§ív÷9”õ&hSrÇ_o³5cäµÝœœJŽz6Û¦¬´Ùš7ÚÚnÎÎ%G½ Ú”¥‰éì´Ýî>ç’£ÞmÊ"\b- ¢íæ<à½`ZùeAnL<LutçÁRб‚ íçÜþÄH*¿ÒJ:ƒó€óÚ'΃ÕäöiÃ;… ”óàîeXÙ†ö‰ó@nnÜ”b(eà<à<€ö‰ó`¹¹Ý@ºN:ƒó`A ’e ¡}’VœGn_r”4Iê:é ÎåŸ9ܾñøÐutçò ÈÏœGn_DL=utçò ÈÏœÛÌ퉭‚6œÊ3 ?s¹ý xdh´)àç”gÈÏœ‡óÈí›(V >d-p(ÏŸÁyäö‰§˜÷,»ŠÎ?m™°E"/÷¬kRÎåŸ9Ö”ÛSv”PhSÀy <KËχýñûÃG{[{C›÷·ÓïËoïœ+ÉíÑâHIé ÎåX\~îKÏ€òp¬*·?¸ xJ‹³ÉçÜ´ÝÒœÊ3äçûIÏQiZÿ¸Ò8Nè" ?s,(·/¼¶üº–:D]÷`ç‘àœúˆ€üÌy°ˆÜÎ(DwÚÃy ÈÏœOÎí:dê:pp¨7!?ƒól6·Ëö/XŸ¸éÚÃy Näg΃§uΔÐ{'²ÌF{äÎ}D@~æ<Û•Ppphù™ó`î>™ª®ÃôG²p¨7ù™óàÑ™œó<¥,%98ôù™óàA9\neíq÷¬=„ó@Ÿ9žÐÛ–ÛÕuà<àëþr=u) ÎÃy 7(eØòå2÷üŸÏyZþžÛáz%A|¡—Þ×ëÄ9zÞÌå/1Ãs@o Pʰ{·ç9ÿcx?wÂZ}üN‡¬sØj¯VÏ®uœ†Ö JðźÓxº'¾»¨ŸÛyŠâÖh@Ëe2y8“Èó_þ3<çôÆ¥ #îÚÂoÜCÞçé÷È£Ÿ£ÝÆ~ŸoðŸí>þ€qzËè±ÎS·Ô>aÿ=L°ŸÛ7ÏE$øüã<¥‰Öÿܹ®èød?‘3é–7ÿexÎèJ¶sË0Îd(èS–t%ÓÇi?¡´çˆó-ו¿ÞE$øgDÜZ;'ž,¼®•¥?çá<Д2lí~-Ày>bæ ~ëGöG÷N'òØ…\Ú8Oædƒq.Ù§dbìkWJð»9Ϩ¸uÄ"sæçYZ†ç<€Þ ”aàN­èf-á}žÞkïo·O"ÏeýH½\Ñ~ƧýüÏ’œ§ÿùû[ìa¿–Ãô¯} U3ïóÔ½ùï Kð»9Ϩ¸µ—™üû<¥Îó}‘žózc€R†íܦeÌÛÖ{™!µZI³=>ÛX䥈êõý~p¥ Ç:O'¾‘ähâÜN“pž‡ÔuµK¤j¯Å%øŸm·NŠdS ?o[™ó,3Ãs@o PÊ¿Ak¼G÷qžÍ“zêÏ#ÿP¡””Ÿ9 ”2lãîpž×ë©cÓ—#’’œ‡ó@{@){¥û²ê[ÃyôÔ¥$8çÞ ”aË7…óè©KIpν1@)CüvlãŽp=u) ÎÃy 7(eØò½àÎãLá>õ†ë K¢NãOkꃡsû·ÞçÉDEÁyôÔ¥$8çÑ—VÀk8Ïæ¹°@¢Ã/í½“£µçmËMÑ–ÞxÚ6fÞ¶`š¸C=…ÂÀAF'¯Z‚óè©KIpΣ¯ÄK)òý«¥ÿ¢Žº”óè©CJêAq(íJ,8ÏvœçùI* 9žº”„çÙ°ótž`™ëÉøþã&£^5n¿* Ä±RþOªB9ôÔ¥¤»Ày°ç9ÛÅõáùi¦Ñ±”¾´p€ó,.¥çÑS—’Ѓâ=µ¿uàý¥ç8Òþpxã</GqÑzT3üÐç«ñ†§¾ý9Ð{- ü0ç1¼£M88ö‰ó ô·Þß‚^ãÑzJû~?9ÿ©zx/8zÝU.ÎzyÌoþµYq›ótÒ¿¿qg(>Hû}¹¡,[rØu;*QÂÝ_QÚJsÎ5Ô³ú[u)iÉ;Oå:ÁÐQ=ž'?·çuhærè­ïŠ59Oì5¶jÏó o‰!£áÒ‚Òwjn¢¿Oä‰WûªÍ§YKéÞΣ>”†p÷98JZ=ÑyúyG7†Ê“ÚNzÖç<‰é ;ïeÖ¼Ê!›ZA‰ýÓû N´ØhzŽóxžM›wÉ »äÅšÁy6R0ráç:Oêìs®ÍŠÛœ':T’wžò…­R‡-< ª¥öŸ¶T×å#ž…»“óè1HL¸ûÒœGÉÁ¸þÖ½Þç¹ÉyFÄwvž ã<)£(wžÂ#´ifOï?°ÏØÏv?‘jS¥ œGÉÁ¤þÖèyÛ¦9Oü}žÎ®‘÷y°>ç¹ß8OF8ꡘÁÃfö)‰ùÓœGí§M”2p%7ô·ê׳[s¬E¼¥µwòÁ³Ôw£ó¶Ë»¶æm›mmV<Üy ߯éËF“SïÕTù¥ÙðþÖQ‘Ìa£ûŒ{ŸgÔZVs9ám Ü}é Σäà¾ý-¼¶óÄ×ç¹iÞ¶è?CùM!±ž%_'V»þ¥sØÞ>ßGÎÛv™uî‘ΣÒÓ¦ÀÝ—Îàþvgç1¼£,ÀÝ—Î༠¼vßÛ^ÿÂÛú}7úF~cxäúÝ‚øä¹Á«ÞoïÕöâß”“ý-¶-ÐZœGiU¥ œç ^YÞÿüó¾~}¹~:\\°|à%œ?¹5ƒrjæåÞÆÆyšo‡8×NÓ|'˜1jÔôѱÎÓ¥×%ç@ ´|çq_”¸ûÒœyç '• ?÷׸õ°Ygçú8–Ý<ÙX/b?—3-¶qçð, ÇðŽÞÜ}é ΃çI-s£Eô—¦™ì<©emÂíwqã<Ð=+KœÇíPàîKgp<ÝyÂjnç韷ódÜÌÎã}h–ì<ʦ²w_:ƒó`Fçi™Çè÷yÂegyŸ'êT­÷yætó¶A ´@çq”@)çÁÌÎÓ,À~˼mS´ýš·-|Ž-ð¤]³jaøù§Þ¨ö<ð\çQ •@)çÁKNô³Wêo©€ <± ^ß-PàîKgpÌ^rúó¼´ó@9j—Žô ç¸R6cá*?Zkvà;Ï/õý2XGH&Ôƒ»/Áy äp¼¸óL;Wb{g.’GÅ<,ƒlG›w_:ƒó@Éáî0ếÝ3*‘)pŽ“!í‡7Îíâ ´©F~”@~æ<œ«wž÷·«ñ\­§T:2~rþÓy$h·ÛG¯†“Ú_ ¶†Ç¬¾Ï‚´‹Ð¦* €ü¬~æ<@¿ZÉÿ3­.å#-yç©\':ªÇ“Âá¤ãçZŠÎ_»þ¡‘/c?ÚE,¬M5ò£,ò3çá\Ù^ö¼½Ï÷žÐœþ½?ü;¾skðçü·pKümäïÉøpÎ΃õwú í‘y8Pæ<ñ‘‹Žœ¶žÿ^¹B¡óœ÷o‘9ãåóùð­‰fÛ^™z)ùó;‹­GãÃy88°Eí‘8ºþ.òsÆRÚC ¡„ÒS:ÎSü9ej"Ð~Ÿ§;6“¶¾Ö Ûçá<à"°içi=Û¬ÞW +³8Ouþör19‰<ƒ×Ù³ÿEÎÃy Õ„œö2Ú#Yä4W %7é<Á¦jãÇy"=ѳÔfdœ‡ó@«  1í‘&ó&¬ÂlÏy:/îÜö>OÄ1êôåªz‹'û–Nû,á_»CD‰÷y8çÁÝê­&îÚï”åÎ#A¸ë‚rúcÀR"¯Î¤çmk­ó}ä{;ûÃ!œ·­{ìî¼m½Xïöû}­æ·ÖCqã<œœØöH Îy O‚´â<œœ-΃—Ȩ² ”JÎ#58Ô2xBß]:(ƒr¬œÈÏœ‡ó@-@ä?r ?sε ô•AÈÏrÔ„à<œ³T.jhŸ¤$Öè?rÔ„œGjpph–$)nÕž%ß9jBÎ#588îÒ”Zz·I”ù™óp¨e(ƒ´_ÎägÎÃy –ì‰Ìä@~æ<œj¼r—N„b"ç@MÎÃy0C墖´ôX£ÿÈ9Pr©ÁyÀy°‚N”A…HÎägÎÃyÀyÀv´ôP¬ä¨ Áy8843n^ìþÊ9PR8Ôà<˜§–ÙëDKÞèy3§œÎÃy¤çÁlÎóãï ºº´GKÿ‚æ#çò3çá" ?sÎÎSæ<—~ÝŸnÜû5Ò¿¬þÙáôõ]3ÈöVÏŸ¿ÿçŸùzq¸lùõði;:ýé§ÃŸ¿W >úú‘ÿns„öý ,I–ëèVÁ¥Õ{^bXïÐßÞOÒndþç?vé«ëD©±aêÕ7÷Ó×ÿçÿtÓ¤SR©užÂK‹g¡]ɰá¥ÐÒ/¼j°|”ýçÂy8fîo¥§ê˜ž:µ±.lÝmMíÐïë÷ûý;´:Çß~zûí÷k'þË/Îîñ¯—>÷ñCÝÿ7f¾{ ï_v×2X’,™K«£QG)µý½æêÂ;¾Ô÷ØöHj´“å’‘Ýú'§R4e‚K;´äõó,°‘à<É:G·”“òÞ]+®?–J( A«Áypoç‰ É´g™¦;OøÃ0=cÕ>õ׿\ûèW“ünþË“å<~Òu§áK³}pçØøU;J±Ôè ”]yú»¥*¿Oän–]ZÉ©cγ¨v‚ópÎ#¬qž˜ûÕ]œ‡óp¬ÇyšÇ¢›Íî<áé2gìÿ÷÷âïþ>pc’åªOwžx”"©މ>Dw‹d íqžâKã<œ‡óÂ󿯼Sõõ8çÁì}¬»8O8zð¸qž.ow„çKýDÖÐwû;D/°|ø+ú ×³œ§¥DjtE1¶[ä¦dS©õÅà•!ã<œGà<ÂJÖPƒóà•'è½ÏsÞùúê|}ŠðÑ©è­Ã6¯—dgÇn^AÙ5Ït ~7ò>Oÿ':OôÒ‚á¦ÎÛ2ýí%ï·t¤"¥tj„¯BÅwËœ´ä}žp~ˆ)ïód/ópÎ#÷YM%΃Wužp6°O__zŒÅŸL;öYƒí§#„giï;ïû—ôói)Ñ*ýnoÞ¶Þž§›TÁ+-¤.­žñ¬3A{fÞ¶0az棔JÁD‹Ÿ4J©yÛ2S´e6F/ópÎ#÷_÷ùuê156çÁ6K¯6uAkéX³hŽvšópÎ#³;ÏëTejlÎÎ#pΣÏyºód Í„ñ°’5ã’Ê“ õù‹Ãõs3v:¼zÕ5ž?f7ðHó¯kù'-Øù‘ëöpppAàž%´”¸óDâ9¥ÁÉ«yöȉwçÔœ“ÙIt¿ó쎱ÈÄ¿‰uÿþýËЂiùé+‹’±7[æiÙåÏ?ª>×;ô/!qáù°àÍ1™<µ1š’£vž\ǪÊÀyÀyópž9ÆyºÝÖ_ŸÂ îKÖŠýØ?¸ôÓyËáØÇ Ïèiý³QÊíšV%GÎ?Â4øÔß@«u•?&7xKÞç©bÚZ^­½ÕÓ6záåù0\Á¬ð¼èÁ7NÞy+u¬J›ó€óŒtïÐ œGó¹\çéÌy0(…úÄ(SçQ´yœg\g}¢ó´*µ„qb‡QKH—ŸtÔbÊÁƂћ6&Ÿ¼„â|Ø‚nB&OnLe­â9J›óàuŠ®qóh>×3Î38Âþ˜]8ÎÓ;lõ§ÔPÏ´å›ççI½ÏŽ-ÄœÝašó Ÿt¢ó̦7ƒÎ3âJòa{‡¢•»ÓoL9Ϙ9J›óàE瑯{fÞìÎÃyb=ÔæE…Ì á›ú7¾Ïs©©úÚ3ÎybQJ¾ˆÒz*)ï<éùª­WkbOvxŸçšDÉ÷y²Çì¿Ní¢÷yªôó>Oÿá®Aç‰]Bâ òáTçI<º1•’£væ<à<ØŽçLuž{¾îé!:ópžñÎÎ=•ì+÷O¹eÞ¶¨ZŒ}¶-úO4&õÔÌ%ΓZŸ'˜&îP÷nÃIŸ{;|ÿÏ? §GKÎÛ6|Ìþ¯`™yÛj:o¡LIÆÒgۢ閸ðá|Ø~̬ôÙ¶ôÁ#Ó)9jgÎ΃wž{¾î™³S8ÏK9Y ei± †ª^+¼ì…splÝyîüºgæÍNAà<œGXŽóôkû¹/{áœG½ÍyÀyf~Ý3ú”³ pÎ#,sœGPÇrp¬±ÜNuž™^÷ä<çá<çÔ±œœKtž_÷Ì¿Ù)œ‡óœGPÇœå}¬ç¬ÏãuOA{Ìy8çÔ±çÁÖœÇëž‚ö˜óŒužëˆñ£jŒÁE0|êrç1Å¿ Ž87Î#ÚcÎ3Xç<¾}‚×8 pU7çÁ¶ ­6UÐk8Ÿã<ýÕ$ã[>¿Ÿ&;éµ—û<½Ø™?9ÿÊe¹ÌÄ<ûýõC#‹œ¶×0m X¥¿˜_µu¥»ý·_“ã<ƒ~æÊåPa´[ï[^‹ pppAÐk8oçyÿrí Ÿ&~¼¼xéÍúúñ×iÏ_?vñ?}þùSõy`Ű‚¿fG¢š¹(nÐÄsè‹Á–+­.!í}=|éF²5o[ɵ«Žó¨½98Ày´š7;ð”õyþüº¿ . çQ{sp€óh59ÏFœ§?«88¶$9ú[çá<Æyó€ó€óXŠó|ö»ÝÛ{üïoÉ?qÎ#œœœÀÒç¨4»ýá°v±á<œGàAwü\ÿùÞÖ£ÏyΣÎÅP §všÎçþwëÝ´7æ¿Îy8 xŸœËî`q`£ÎÓ·—þçšÌ·ÂÝîþxÛÀšá<à<à<†Ë`A‘œÅy‚Ú¾þ<«Hå û—·ìl{ 4pèoqàùÅ0õùÎÎ<ðÖzŸ'P¢þû<œg ÎÓù¹à<ëSó€ó¸Ýy3ól[}°óm»ö“l±ƒ?NôÇ:Ûç¡=œPV%½þ‡Eð´'Ú0= Ðâ<€² pž<ýY °è,T#5€ó '9Kๅ1ü/0¹2¨Áy΃dyXl· œœ@¼ÿªb”í¨½ÎÎeà<Ø~--ÛúQàW{Æ’®~U+µ†›‚³ÕßÙ·}ìúý§hÕ‹8ÏÓ+Oµ7 NççFenç9¬-äb§?mÏŒóTÞrüW­*ǯ]>w¦k55G‰lßÞë¨pžÙ.m9u¦Úà<à_·~úúŠJbû÷ÿ>§}ŠÓöŸþ‰ÆåPÕ©¯ç=íöëáS:¶£¾•8Ôû—j™çßB³s'>ýé§jÿ.tΘº´ÔMÌßÖ0'ÔÑË|·?ÎOÔZÿ î{xÇÇ;OØÖ¨¡Õ„‹-;ÏæžV°(ç9öÚ/Öo?í¾üùJÝS¯;¯—>q½ó±Ó|ùœÛ~úâñ\o¿5g?ž±Õwïö˜+I:Íǯ\P¼ßõ/ûVjc¥³Ï !ºs7>щ§js„NZ¥.-us‡okúYµÈwãÎÓ‹Oj¤®ºð&§:OÝܨ‘l µšÐÏÃÚçÕlGYÀÝgÔãd©Žrùöcç¸3(T™æ8¿>…ãBÙ~þ[ƒ'ïœIðü>ù{1x§S/~ÒÓhRO®††wR±MŸ÷¤pß‚»ƒó\ZÕ"8ôó°5çyñÌ ,àÉÎÓšá6ç9õ}+OèÔ ;Og2†Bç‰}+¹1ÿâ 'Ÿªsž &Wó¹óœÇ‚&>ÕÆyÀy Ÿ‡;Ïkì( Xó„ã·óÔªÓ$1ÎSüþ[ñ©øÙ¹ô€ÙTíŸñ~ÎÓ8IÌÜ2Îó%ö8"ççlŒåc΃u;OÑ‹•œtÞçé½­‘Ýöæ:|ûÏ Æ´¾^ô>OßyF¼Ïužs®0t/¼{QœZM@Ž]Å=šÝy ì( x°ó ÏÛL;v8÷ì¿ÿçŸáœ]ÝyÛRÛ«ƒŸzÒ©ÇœNç1÷l[ú[‘ÍliÝ©ØFíG®5o[/UOß­/?5o[þ)ÄÖ×Ëæm ±kl¤|Þ¶ø°Òî|ï2oçVr,¶à‡fìëÁR§}£þÚ(;º1òtÕ‘äçέ&Ðwž]K=ªÏÇ?\$â¤#¹¡6¼¿ÕâÑì‘:EóÅØ¶æüºÇÀŽÚœG8´š@ÊyÂNí m“Ùnü;òà)Ž[jU%!÷‹üúœjopAà<Ðj%ÎSn)mxÀNíÐÙþÞ^‡{²óÜù59µ7á<^¦8^¤ÔjbåξsãPIçQ²z‡ü)‚‡ÒÆ1oä×ä<ª΃¥8 pp` ÎÓž`Ê+1á“iÕçÎû<‘SÄÞçí<·G~MΣ¶á×;|úúqq•zãßÿzÿr]TçãÛOµÌ?_M©^fÔCtçVc7vÌa ,`…ÎS- ÚýÜs•æ[¿>Õc;áÆÞ×Ï#?Í‘ ph59ví÷èçQ°*çéÙKäó®õ|ZtÏÖnÁ“lý‡åó@« ȱë½GÆy”lÍyšü·ÂÁŸ~ž‹΃ǵŽZMèçó( à-œjopAà<àÏ Ò“Œí2¹çÚ Ê9À–à<ÐjârìÉka8Fû=™z¢µÖ¼mácjù¡›Èß žm«w<žÕ8ÔÞX(‚ó ×:j5±©~^ç«÷pÎpphµšØP?¯ó¨çá<œà<à<Ð:j5¡ŸΣ,À6*FI­&äXpe8´š€ Σ,çVcÁy”à-œGí œZMàÎS?1}VíÎ?]7§¢F~Ô“eÑ/Þ~QœGí œknµšØ¢ó„c-fœ§ùW=“‰ÿ´™¢_ä<œ88°bç †A"/à¤Þ`ù‘xv+þ@Wâ}žBçéL­°?ÚoÚDâ?¯ôäc»LnÀy8pp`¡ÎÓÌHÖ’‹`2´Ãy¢€ïýŽûiŸ^_?º15o[‰4¬¬£UÑø·S7§vï‹“¹œÕ8çέ&°dçYPìú/ýÈÚÊ6WïákDÊy"®rüns¾êõiâ™ýÈUÞyv½Kz/pªs+?zÊpçY“óüõË[d§Ïïý~þÓùú€¿}n®ëé‘YcÂÖq#_˜ª‘<öùûþ™L„Lž¼_òn)·Çœçuº‚ú»š7yÛvž¨?DÅ TžÙgÛúˆut©>`þÔ“'ó׌ó´<®³êÃîQg²TUÐr'Óç[]/0áÍug‘¶å©ݳäë7޸׾ïÑZ÷ªbæMÀk:Ïû[÷Uþ¾!d,åúõÀ B uëÁÎ:ا<éçéLœjµ¥Õ8ÏŸ_¯X~ûµÚþëáSö§÷ëÏÿ±ñ¢ÎpA½ç§¯áýí­ŸÿÛG»Æðþ#: ÿVæ8ýÞùõz÷ß¾¶"îß9æé+?þ<ç´[ðùÓ××CC)Üœ19Rp í$}û­‰íésê€çðþ¥ªì~kݸó(M5bóßÇ“öÏX]òìÎ3tí—¤0†ÉûžÈÊ'WuˆïZ´°t È=çª~Áu{4*^Ðyš”6XJs˜ŽNdÔå‰Îsü÷§ä[ýÑ!Rôl穬ãϦ_ûñí§kçïØuûò˸VÏr×|½>Tjû¹ÿ}é_#ÐÚ¹ãKCã<ÃÇi‡÷/×îûiÿ0òÁþýc?4[vÍ_O:‰ÐKáÈ»Qs ×.ûù8'éúüó§êssÆÞƒK¨ä§m°AªÿÚø@û‹³;ψl0xߓΓM®ãõ¹†îZ¤°<{ôiûÎÀŠË›vžÄû<õƒ]í­ía rçžñïó4QÞ•:Oß`Þ#ó¶U¢^¦ó”Üq£COçéö#ƒŒx_öôÃvØÎóüõ{îu‘ÔöÁ3—VrœîåŒ9oýùØ>÷qO>ðåÚß½ºÁÀEÅÎ8x9¹KF]ºŸóшÕD¿u¿Jšg÷ýœâ·ÅFøsqžLÕÁäŠ+šOb…¥_@î<Û~Órg†A°âò¶'>­Y½í<¥Ú.t“áyÛ"³¶µ¶öN=rÞ¶ú‹G)ç¹|#úä^üŸ2ƒÛ#ç0è[/º³óÄú¯‰'¯®»u;ÏÈóv÷éÿ÷÷5›ó_~ù莢Tûþèß°c$rÔÓœgè[­8çÓ0viý‡åî<ÖÎ#V\Þ¤óÌHÿÍŸñë—UÚïótßõ¼+r~U½ycWÖQŽž±è}ž©Î“ˆø–Nþ}žê!ÀŸßÊ^ñšÿ}ž”ó4ùaÌû<ƒŠNP‘»k¹ÂÂy8 V\^•ó`}Î3Ù‹8Og:¯]nZ­þ´`Çžâ/Ñ)˺/ ÷·g&ì #pR‹¡îuÉqbWÔ·-?;\$Ë^¤‰ž±pÞ¶©Î“ˆ=çXwÞ¶h"œûú)ç³>Ï´y󢃾‡Qó¶å“+˜Wðð¥;w_ç®u K´€pÎ#‚—9çYšu¤hyòù½‚5®¥SMK°å3Þ¸ðëŠbûªwó‚`ÅeÎÎó*£CœgLG"ÐþkÕÞ¿ìr‡m¨+©:Ή4™rù—êL\KìJR5ù­N´;É¿ØHúSõ’§»–9~;=S÷%7"6Íybߊæêá¼ÄyVæ<á@MÊ dâO”uÆ‹úŽQΜçøo΃1Î3<&ôæSõyA~¬`Ãèú7ׄåÛèÂj¼÷›i·Û@?Åy¦uÿnËíI'ô4úYèܽlÕaÉÕ•wù&'š^ÁŸmkÊy`ÏùaâðwšîçúPÙ1ÁòDÿ:Ó|‡õì¸ø[/¿ýõüµŒKÕì¹³òoH–Ä}Œm>b§wßãÑ(ÈKœg¹Î“xŸ§~ž«½µ= 4Åy¢Î1UzZqß•:Oß`bÞõß5âb“Ÿm‹<„P γ&ç‰ÏfVo;Oä¼ Æ æmëÎÚV0o[ø‡áq˜Þ¹ŽS8ÎsùFô¾x ¤”«Ãëç>"?‡Õä¥õöÌγª&~ç)îþÍñFù6ç¹îÐÿïï·&ì8çJ4½‚Åó”õ2ïå<±LvžæéÒèÐD¯ø=´BÌ^é´ôIưøbgvžÁØf#6Ùy:÷}0œg5Î3#ý7Ày^Ïyr­dÿwý‘-içÅ¡£êtFf Gì ûËrž6ñ³óŒ™÷¨ÅíôÃaŸx*x2­–žòSsã<+ƒí;ϯ‡ÿ}^ùg!WºÞ¦óöÀ×þ¼¤{%牯ú40t½¾R³ÂWt™°ÖRS±E$«åº.«í^ ð§ö*¥ƒ«P§Ë¹“±‚U±>êËl/ë,Çv¼ÿsZ"ºY<8ø|9Bç»ÑÖgOE¯“øC©”¼Gýê•Ñ.ÇéßåÎÿk`ÒèÍê§s$òñ ÉT%WšŠv,…㧘1å;ù³Êc§EÖªã7 ®õcžJÒÁDàZ9ÇZê~kRÚÊ 6Ó±QyÇ`¸Cm(ó±ç9F ¾SÅ­sþêîÝð},¸„|‡j̘É@çvžÀ”¾eþÞ˜Á8OÖyók(q 9Ig·Ã¨i}·pÏ‘§Ë†ú¤á¥EÏ^åà“Q|¹f¯«]Ä¿;tÀQOXå[>B:Ø®äÅ2³/dðŠF]iy›:ÅR>\\¯û9ó­hþ“œç:ÒÒh :ý-'iL ö>ÏÎSþ>OÔ1š¯ö¯h¤‚´“Åû<á<ªÊ2ÍzyËRÒšÜÞLwž5Õ1Õ¡*U¶A?»Ë­a‡âãg®n¸ýÍßÇlç­NÛÆÓFõnëÎÒõè[\þíŒóÜì<›š*-ÁØ÷ˆ¾ïäC§+½„Þpjähýÿf¾[rÀ±/1ì;ü²J:&ù&Ô¿“ïû¨+¼®ÁSÜ)åKÊÔˆ˜%çéH=[8¶ÌZv¨ßè?mÌÎÛ6Ây"³¶˜·-ù¤Üñæž·m`жæmãO8¬yŸ'~#Kǎ냷ßç¹¼uwyUkçi¿»ßæ2•‰Ïïœ5OO¿^D¾›<`±’%vŽžkà}žðÕ·üC #Þçéݬ‚+Í*ù¤õ`3”R§˜3åGtb1O$éàíæ<tpÎsŸªlçIWõ#Z™±Sxì w¨’ïóä§- Ñ9 >õ&yj3W7ð0ä Îy|nZS8ô.ñÜ€â¾e¾«<_ã&:oéG¶rw*ð’Îí,€Â¾e¾«P4“-s4ÎÎ#çyÊ dÝ)‡æ,<ÅS^euÒ±1¼ÓM;ìò“×ûŽœ'€ºó4ëçÆê!““†^Ó6†jÃÀK¡óìz§Œ;Ìñïñ³çÁœ§¼¾Çž³„ëLÄ<ãŒ8´né²ZÌpYÒÓülÕœÎÇÏŸ¾þ;\”bQ*D¦q¿9ž%¦ë,Ñy2ß]]—t-Î3vyµ—šìåõäÅžm«¥§¯"©ÍŸGCž²qž3^ÇjgT΃m9Ï2ãð°ïÞ©‘å< Íʤgy¸¬(zúüå—ÅÅù®}ZÎ3§ó\ñ¬ÑM®ú;¹~S½þQbµ¯©¿ÍD£]ø,ÿƒÄàn‘+E>qE£.?¾sü7ŒàsýFITS½‰£R UÞª>UFÕñ›EµªƒGß^j¶ì¤™{:" ¯k•žÿ{=Tø9¥|òrž¥;Ï4 {ó4Ï×õNXâ3œs;O¿2ì×íeÕc¢ÑÌ.Yئÿ]°|jðõHï\ØNÈ"¡ùö"²~wÑâï³´Ú­jšïŸ<«Ýo‡o?]Žvüðöå¬:õÆòdÌ¥%XÞ>­ÕÓøïÞ"B©¥]S‰ÉyævžÓ ÊÇt x:ÞË:·íª5Åê,XÝìëΧYëxÉdqçéE£ðNØ-r¥Ñȧ6–_~jçhy®3wPžË¢šØ3ý‹Qi $’·º“W|þùSõ¹Þárð¿ÿÕÎK×â}¾Ò 'MÞÓÒ4,i§%/çYœó„ÖP8ÎÓ<×±ˆ¢¡ž¹œ'ãlœužæ'öºBî×íÕcªŒv0¦ü°¯ØË}ï´Éæ ¸½ÈöyJ/r«=EpØÁþÉsÚýþ“„§!ó›¿~Ê<žåš3gî#e/pT8–÷:¥)übäÖÇ ç™wœ§¤_þ¬žÚ>ù œ|4ÎîÞª‚ã…³l·ÁÈnœ¼sÇæÏ…çTªß0‚ò\Õ¡AðÔå·¯u®pÏÂoËI³÷´0 KRlZòržÅ9OðBÏíïóü(y¥§uÈ]©óÄÚ~¡çý­h˜©ð­#΃Rç™ð#c¼O5šÅÍV¾þ/ê´ôä­äù®áæ`h‡ÂÇÕîÝj';TCý“§´ûñDëÿwö>RöÇõ‡cßúó*lßUã±aùI‹îéP–^Åøäå< |¶­Yئ/?FÏÛvýÆþ¿þ=8yÚQ< Çy.ß?÷£ôÞÎÙ‘œðhœ÷pžÂº='“ªKÔÿc*öë×K§¤9(›Šé.Î3¶Ívÿ*x¶í1í~tƒëOk´çá}¤QýáÈõ¨×ðW&19Ï2œ'úYÞ÷ÊÜàÁÇ· wËG>¾qÌå—Ô)ýщ/±1ôòMÿÅ膇R?ª ÿFRvÒü=-LÃÒqžñÉËy–åÿü©ú\ŸºŸNœÃS7Ç ”¬>çS#—’a Tר½®D*½¹êÓù°œGÐ+r¯ ççAîÙ¶ªO2/×®üÕ Â±‹è_ùÇÀê¿þÕ·É’ŒûbgñDzØÖ£XÑ=;[Nq»Df(5r)Ù9fìºâ«³{èNà<%•[ø?¼$œœç¹ö¤ûÿ Ÿ¿J=ЕR‹öWŠÔ%¦£¾˜ûܽ„zð$2b“‹LIj¤Rršó ¥€ p΀ó€ó`ÀyúãÕçÖ C|$§·Ãùå“ë`ÓÔe^ç‰Æ§VÎøRüõžpœg@9Ò)ÉyÎÃypÎÃyð<çùq~_¿yS%˜ yƒ%ÓËÿ-1@ë}žŽ{xœ‘ïóä'ŸúiºoŸÞJ½Ï“ ©”ì]uâ}žÈµÔ)ã}óŒsà<à<œ'm­¡z:µÄÌi½©Ì‚Ïg{ù~™µ,?XÑgü¼mùgÛzñ¹l?+Do§?o[ÏUv#â“~ÿ'5o[úÙ¼fÞ6>çá<88Æ9Ïk†ÄLtË Áˆ“ p΀ó€ó€ó .<äYb<³#K‚Ày8ÎÎÎ#çÎÎÃyAà<œ88çópx@_ ˇó(GÔÏœfÀšKV…‚r@ýÌyè«A› åç}5hS•#êg΋åÿ㩯¥—ÊIEND®B`‚ volume_id : char* publisher_id : char* data_preparer_id : char* system_id : char* application_id : char* copyright_file_id : char* abstract_file_id : char* biblio_file_id : char* Volume 1160.4799402311673 240.649943764645 sort_weight : int block : uint32_t File 687.5479565719912 269.2931470368318 name : char * attribs : struct stat hidden : enum TreeNode 706.83671056434 108.4726745515399 add(XXX) remove(Node) children() Directory 986.1687535943008 267.29314703683184 dest : char* Symlink 571.9364350336367 273.31078127658077 Special 813.0651280884073 272.20749521231266 name : char* <<static>>new(id) <<static>>read(src, opts) create() grow() Image 1149.1980515339465 455.5218613006981 In addition to the dest as a path, it could be a good idea to have a ref to tree node. That way we can compute the dest on creation time, and thus links to files on image are also valid after moving or renaming those files 322.02220861890066 362.2044136147912 Image is a context for the creation of images. Its "static" methods, new() and read() are used to create a new image context, either from scratch or from an existing image (for example, a ms disc). The methods create() and grow() return an BurnSource suitable for libburn. create() writes a full image, grow() only add to the image the new files, thus it is suitable for a new session 1212.7956394939486 697.0920982847697 Ecma119Source 1423.5617211564486 483.61244144432396 «interface» BurnSource Libburn 1420.0 280.0 1431.4906533445824 311.35760744838467 Class diagram for the public tree. Note that getters and setters are not shown, to improve readability. Note also that not all the attributes will have public getters or/and setters. El-Torito related information is shown in another diagram. We don't show the several functions in Dir to manage the tree. 290.59037712396525 9.859316379054544 «interface» DataSource 1192.781692587207 608.8954677283948 «interface» Filters filters 260.0 710.0 265.45434264405947 743.9994422711634 TransformStream 486.9335577265969 640.636302316303 CutOutStream 555.9916340674516 750.220757440409 get_size() read() open() close() is_repeatable() «interface» Stream 688.5487814157467 437.25152600545294 FdStream 680.6673668471356 637.245696021424 FileStream 828.9404615480411 642.40096597045 FilteredStream 428.449880813367 747.5389646099015 «interface» SourceFile 1000.6667341519202 639.0812755928229 For files, we need to know whethe they come from a previous session. That's the purpose of the block field 818.829652614022 414.36457377531684 1 volume {create} 0..1 * children 1 root 1 parent 1 1 src ‰PNG  IHDR#/¦ÆRª€IDATxÚìÝK®ãÈ‚ im)Ð5mŸ¥Æ“ØAuûÄG~Öp/à;HéÓBù8rèB Õ±‹8¸‘û8¥Ei%J¢¤ï!ç!F3Í~Ùƒ««±Ü›Üœ+6Âö-¼ÿúO›Íf³Ýk#ÛLÛf³Ùl3›vó4Ó¶Ùl6Û\¦M¶˜¶Íf³Ùf6m² À´m6›Í6³i“m€'5í¿ âþºýû÷¿ÿùvüpVføU¯R¿5·YLnp/¹K,*=÷qX½ýøýê)³ÂsÏt~áÛüäŠÕšl<—iï=3jƒÞÆ´½ ž»Ó^Búïÿeý)¿|¹I‘9/Á/¿ßgô“•Û%$Â#˜6Ùx*ÓÞ~î÷@Æ Ó?¿¬OýÝëoߣnðÓañžþ¶»Ð‘·©>ØäUþþ¾ùtÜó¥wJsðaO|âñ˜S|öwwèh]­¢;=í_õÄ#ÞÓ¿bòÜÞa‰[X$¹MºO_ÞcÿtYn)ÚÇ=©ËwßÑ.ÀÏÿÌxÝàêƒÔÎ_¨2Áå!¾D/̨€ÅqBøù¯ŒG8NÒòW#W€ã[K~‰²ãS–y;ù2¡áWi¬ðÒ¡6Ì]Ä~ÙüÙ>‘‚ÏŸ¾üµO®t¾œý–/ïÙxRÓ®Üݾ‰™¥<Öeôþí—¶az°…~8É«ìlZº»Ó{ÍëP2ÓÑ <üÓ¯_?µN>Ò{¿‹Û1žû+]qpÅRßàé°àB»p½ß5ý[ùØc»¿=&kÚÁÁ­Í¦L;ýJ9Ú§œŠ#™_•­Ð¶®<ñ>¦Æ­¢ršiçû|ÓNExô.rÿøõÔXiÚS’«ô¹ny¿ú,«ºVþr…¯ÆÙ%¿tSÁµ–v;gçò9ßÍA:L 3þïMM›l<¸iÎÓ{‡âÆe<„²b„vÊ SW©í×­;ñn}ÚÕ^Zuïa¯f¡O»xÝ‘>ídOéY‹–5ó'õiW'W6Á‹åaÜè’)Pè®^M0ýÕ8»äM¯¹½°Û9;—§~7‡éPfÜ›ýy|Ôý5L›l<¶i– ü­0»Æ«3ÓCŸOÏÓN]¥=«?O{àщg›ö¤¹ …¡éÁçnJjþX½U庋BnÖŽŠæŸ_:O;;|7á‘»(ˆÙ~žü/cSm»âW‘\ÅÏ–‡¨„÷‹ezžvöºÅOSÓ 8ý%*æÒn§6—Ó·_.<“M;æi5µn&y¹cœi0í´i¾O;Xìzóy°dqbjfLi»Úpfíñè*áPÞáÚãa7½3M;»öx®Iݾ#-¡v½èì@Ùv¹æpݵ&&QŸpbqé)k¢-p½*ÜEõ ÿPbÓëE÷ß3WJ®šO¤ä«ì‚øôÖOÆ! ¡áü`éôWcBN}‰2#·Ãk-èvŠe¬bäÂHáÉFrR˜ÃFÜ™S¹™6À™öò·ð­QÏ´=ôÛŒŸi{Öf»AÉaÚLû‘L;¹Ó¶)`¶çýþ2m¦m³Ùl6ó´˜6Ó¶Ùl6¦ À´™¶Íf³1m0m›Íf³1m¦ýâËw•opô½Vs½øç°RWïJ×Nù¹Â_l ©ÏšÛßÂÕÞ5[|j^·À¯íÒ–i¼i[ûúrÓ.p^ò^á»þ”yÁï5äð’FßX¾„8×KãŒßˆ©ÁžwG÷ú àLûªWô>m¦=4íî-Ga«1ÿÒ£AËûǯÇC÷½¯§›žØþ«O_ÞÃVéþ ´§K ®ø÷÷ͧ_6þÞ{]í§/ýë?6ŸÆ"Ö ö{âøæ€=ëoßÛÓ·ŸW§ ÍñÁ囊úþëí­ýüßÑKw:Ý»ÓøvÚ7<[öaŠÅ™ø½”V…[;ÜÞ`M©HD¦Íˆ ‘W 0Âè]WÞ`ú׊[˜šªA°aIˆ.qükâk•(`§ÏÃsÑ‹³²»—SÙÛG#ø¼ÿ®ýcBŸöx^_ð®HŠªtëUÌÇÓ_×ß¾dO)äl2üøé”˦ ð$¦uÈìœóØb~ÿö˪ռ´iŸš‰ûvó¯_?>‡´!ìZ–ÇÏÇýmS8yÅöàÓ;{ßýµqøþ_{²›8~wL{ÀŸ÷îŽl%á Ü§øÇnª5áÂhÛ y7ÒÆ9¼n.»»è‡3L±~&ާUúÖ²NU[*^¸b!+ƒl½ëKnpü.LÕÂ%â¯U¡€å2Hø|_k[üöé¹êÒöôAe¢ÕåõÙßèô$™u_Ìèk8ضŸ›É’Ïú|ŸvæàÌú´^´G‡DöZ®§~ªáçTÓ|tï˜~‡[Óý]¢0L4 aäFúnãw=¸Ê˜`ÿÝï$¯lm‡§OI÷ÎåºìŠ÷>2#wß#—">ièõ¤Ä,‡V¡š³M9-“g¤j9eê Ø¤L³rÐðÀ½y~nœ°±Ð ­ü-;ó=¥$Ô|1Ów:q4ë³9;vð©Ó»`ûLàÅM»l&˜v¿Ã­?·8ä¸lêøÞþšøÔ\iÚAYº/y;?~Mw3–²£˜V£·–œp¡i&漦]¸Ü™¦=KªN5í‹3}•‰[Žÿûû?O c2Ç7ºÞ´'ÞÈxÙ‹¢Zuïù¢^Ȧ À´¯gÚæÐ×T³$Ròø°+i<ž¹ƒÃð‰}Ú­`Ç3W‹·sðóI}Úå´ªH‡š!ÐÓ:„+®8—i—/wþ-\–ªµ¦],`çeúŸÑí(î°7ûs44úŒDK¦Ã ßèÑ’PùÅ,^·>ëK}ÚÕKÇeò…iœ8>¼ÓþàöÈÞ<íøàÁMÕÎÓ8À/›oÅíøvön›±Ü]¥"­’·–1íªR‘ œL„ÞS?7dŠÖè]×Þ`V‹·p~ª'Y–?°Ã¹Íj[‰,E/ctÇu¹ºIÅ«9~žÈ|ËÎúFç ɤ¨ûbŽÏÓnþšž§-0ÅŒ`Ú¯hÚM#û¬µÇGû´ÛUˆ ó“sWŒ<ª]!|U÷¡ÄñÁêÄ›Ïñ*åýµÇ£ƒ›5¢ÓKsçÍ$LÞ¦?6ã7õÃÁHõ¯RJ«Â­õÇÊvÂP.áÕs#™òý8WIÎÞuõ æNéßšªÃ{L^¢°†vbBD0ºa4Ó“YYJêüãI’épÞ7:~€$“"—nç¬=Þ$Zoíñìä>#Òs[ŒxjÓ¾ý‹©_yû1Ö¡m³Ùîþ ß±wÛi0m¦}F²ghÛl¶{=@Rk¹1m¦½$Ó¶Ùl6Ó`Ú6›ÍfcÚL›iÛl6ÓÓ¶Ùl6Ó`Úó,èe¥4›ÍfcÚL»÷æ›ðŶ76íü‹Žm6›i`I¦Ý½Û¦ï±;¯î¿íæýÛ/ëoß;ë>½§ÙÙ;kÕù_ÿ¹ýÜùö#cÈ?~¾\çïï›OÁž ’?ÿõ·òÁÉ[ˆ/a³ÙlL€i_ôw|´âH¯>ÿ³sïA÷õÞfOCÇw´ÝÛl ÃÇýíξŸ”;ÝTõ¿7ÞÅ*ݧ98¸}à6›i0í™vR_“^š:ž<«ÛøyÎx=Ò*'­¾~ÍÁÉKØl6Ó`Ú÷5íÞÐñÞ˜óp€÷XP¹¾åSh ÷/†_spò6›ÍÆ´˜ö=M{Ø5tôi×™v7÷»í /2öiWHï]B›Þf³1m¦}ÓÎÎÓŽ:„áãáë¾zó´&¼ ¿íFNÏÓNip7õ:ùޱþûÆ23m›ÍÆ´pÓή=êè_{CÇÃÕÂ?}Ù|n—Ïõ9׆‡ÂÜ®XÞElÑÔçøàD_zÿÖH³ÙlL€i_Ë´+¶íç`è¸Íf³Ù˜6ӾȴCÇm6›ÍÆ´˜öE¦m³Ùl6¦ À´™¶Íf³1m0m›ÍfcÚL€iÛl6›i0íä¶Ú·dõÞם޶Щ~]ÖõÞ­5oÈçÞFV¼Dóæ³[½<ìÂ[>ûôÛ¼ í.¯a»ÆEÛ0½XÎÆ´^É´§+å5ï¼§^îŒè=±.0Wº«š6´1m¦}Žiw=Þ}uùóË:è-?ü7>,ß=ÞäÐ=ø}ó)u¡ãçæÈ=ëoßS±ŠNo^Þ÷ö£˜,½øä.Q•Û¦|wÅßÛSöŸW,ßÈP —;œ^ÈÁS4Òé?øk.äaôv‰|z ûþààó§/o¿&z£7'TáöiXsk™ò–Éá+裲T(¢ý«¬¿}IÝûaOîNËÉÆ´𸦽³‹cëÿýÛ/«ÏÿìĦ}wf$mâÄXàƒwzïk,b'Ý…N!·Gîµ$q¹Ä釭ó4©lÚ§ød.Q—>}úõë§V¥ên$ø»§iý:Ð; ü×Ì 72í±¿‘9L;¸PÚ·‡ø¿s›ö5ªü-¨qà‘™áIwÊ´™6–kÚËíÓ®ë¡Íöi×Èü½M{üFæ3ín¶s>ßË#ØóÃÚ{½ÙŸ͌پÀ´¯”PÉÞæšïÈh™?/“î”i3m<‰iN‚Í y­š§Ý»V7o¶<˜¶'‡Ýå§·£škçiHÑ„¤(™yñFÆçißдs™xX©«›ä\î?Ó´s U?¡=5â:ø\*o©´J—¥rÎgõçigfŒÝ)ÓfÚ¸¯iOyŸöyk'õrï]SÖ¬>;†¶]+ûÓ—Më9áåâÓ{ëK×­=>Þe]¹Ïå©Y{¼VeW«Üèñø¯ãcÝ ·9â´gŽªW†G#™MÒ|YF.U–Fsö·áÚãáU¦Ý©÷x3mÜÅ´m6ÛÒ¶ÓJr6¦ ¦m³ÙÎÝâ¥Î¤ ÓÓ¶Ùl6Ó`Ú6›ÍfcÚL›iÛl6ÓÓ¶Ùl6Óx ÓÞ¿Ž(|CïéMN»ÏŸ¾¼O]–éŒ÷ Ýæ”…~ƒx6ošïfOú‡¯ ëÞˆþŠo®ò².¦ €‡1íË‚»>HÚúÛ÷æóï:G›¿oþ¿ƒÒO°«¹ßC›ö\‘_H" ¢±/Ÿl¿‚š^^2ù9ÓÀ]LûýÛ/G»Þ}xûÜv»óÐé=òæ¡«yû1ƒþ‰ÇFÿ¡ç¼Súƒiÿõ¯¼ÞoÔôÓžþš '¾â°»>øv×ÏÊ>–ƒHƧ„ñi_›üiwïÿ1 ¹‡ÃÎS|‚t‹ *y#I«¸»Ÿƒ¼ø{zž&S#v¿Ü¹KlJœŽj>ᜢ½ÿíét|ó;ÔiÐG"â[ˆ yýíË[®¤»ôÏ-™¥Ú2[ñ•·1m¦=bÚ½=ðGc¡mG÷^¿ÛïT/wçär7­óÝçVà;Z­Ž–»oîÇ¢˜W X)£pâ+†?"´rF8Ós˜|$R‘OœÒ?÷˜È‰ÃÂ8ÿÙ~n£4Ú§5ítÒu>™§6OËý¨c‘Ëúþ%i•?=ù„”ž~×øôë×Oío§ ¥s!uûñ‘û4¿³JãôruyÉŒÒ*yîèWÞÆ´˜v…i7mñø¿ýnØ´ÚõûicÝ9nÚû¶qLîÜ ÉAö&yê®Ze• Ö¤Ã ’©SÝìM‡v|XÞTËÇT™vqçÔÎ&W”Èå[¨ Ÿ6í±´ª†½ßfD9»§ßÂ>MÎ(W——Ì| G‹¨‘çL€i_`Úaov¯g;Z’jÌ…®aÚÁ ØFÞ&{`êG„3º‚ éŽdâ”°›}ÿ!yXidxþ˜å˜vœs™öŸaɜݴÇ.çÂGNú~ÍB®ˆÖuÓ`Úã¦wù~n‡1q5ë-]É´»¹¯“ÏÞÚäžÃ:åè"™9%1j`ô÷‹ ‹ø*}ÚQ.7íAjÌdÚýQë·5íÑ\8óÈêï×¼!ä¾ÝõEÝÆ´˜vÙ´Ó˜Û‰ÙaWd7i3³…ƒ±+æi—M»¸²tÙ´sqk\…‹«gzYS#fƒÏ£é»eî”pÒxú°ÄïáÒnõ¦}8«Yàjì÷ˆ¿&Èä<״õÇkM»+Qó´G¤(æBùÈ ‘Ó󴳞|YÉ,Ž„Ož[[ÔmL€i—M»·4wô.¥viñU~õ„µÇ fµ¿taýäâèñÜâÒIùL¯>¢¥tˆ#Y8%ŠÆð°äšÛG>}Ù|nW¯Ì| õè`ïh¹él‚Täi&5Î2íÌHæÓ”¨šµÇ˵œ Å#Ãdé­=>úýº°dæB(ÑñicÚL{Ü´m6ÛͶÓB}6Ó`Ú6›íÜ-^#MšØ˜6Ó¶Ùl6Ó`ÚLÛf³Ù˜6˜¶Íf³1m¦ ðp¦ X&Là!M;äÀ"Q0mÓ`ÚL˜6ÓL¦ ÓL¦ Ó€i¦ Ó€i¦ Ó€i¦ Ó€iÀS?Fq+6À´àUL["Hg¦ P:0m`€Ò€iÄ\éüÇ ©˜60m0mÀ´L›i0m`ÚL€iÓÓLÀ´™6Ӧʹ˜60m0mÀ´€iƒi¦ XžioßVIÞ¶—…Ù?·§>Àc”.‰ÓLp7Ó¾†ßÆA1m¦ L»ÝÓuQ¿oÖqw¼óÔO¾Þ¼§L»ëFïÙt{ÖzÝû[ò¢L0mÀ£šv·ïðïõf{0ßÒ΃W÷ÂëLû½=°9ó}xùƬ»3ã‹2m¦ kÚƒžêÝ?CEÎíì”ùp~kÚÁ¾Þ)ÇýÇ„×M†Ï´˜6öE¯×#}Ú9Ó~¿Å¨q¦ ˜60í{˜vo„÷Gig¥ñþ¼ëÑyÚƒÙáÉ‹2m¦ Ï`Ú½ÁÜ©µÄ‡£ÇÃ#ÆÖ?-1>XK-wQ¦ ˜6`ᦠ¦ ˜60m0mÀ´L›i0m`ÚL€iÓÓLÀ´™6Ӧʹ˜60m0mÀ´€iƒi0m`ÚL€iÓfÚL˜6˜6`ڦʹ˜6ÜÃqêL{û¶zÛÖæ^òàvg.¨I—L0Ý´%ÂmÒyÌ´ß7ëÝQëõ×õáÿ›÷3M{ô¯L0m`Ú/aÚïÈ[öÑÆw½Ö§}§³Ò}Ú»œÎìМ~Ü3?w À´mÚsõÁÞ³/·Î´·ÉÞym:žÑ ôù¨¿{vwøî­$ÎièÙswáîÔä%Ó¼ºiŸ10û.¦=5Â=±`·BÑþÈOäøx|Œaç0mÀdÓÎ ndž(ŸF$ô«ùG bñPäJ®#Uo8tï‚ÅÙOlÚƒ ûC˜6L0i§F#·zÚ¢|’±}WèׯëÓç`Är4ù<¡œ«´L Ì~ÓŽ#?¯i'‡iÀ´—švY«BI GÇ£Ž“C‘Ïíi±Z¨ Îò>íÔo5¦=m:¦ `Úð mñí[+Æ{«ŒGƒ>‡"šöª'¹Íçóú¢ã³jâÐû‘‹ÉصFg‚i³ 0mÓ€l‹'ç]F’çæi·ªÝééâ³jâðQœ7žÓãòµFgši!0mÓ€i‹wã§Wó˜vÔ½šêÛƒ³®gÚåk&ÎdÓæB`Ú¦ Oß;u/éÓ'gwÃÇ©g|4{îOQFõ8}­ÑÄ9Ç´é˜6€iÀs·ÅIîÙfk΃Ï9Óî¯"6PÑL»‡ãŸŽk—æi‡¿Ô_«œ8gš6#Ó0mxê¶x°fø¦]Ùl¿3èÍ%Þ_{<:ž_õ;7#::+‡öøý¦×î¢þZ‰s®i“"0mÓmqLNç²iË øv˜6h‹cfÓ–ðí0mÐÇ̦-GàÛ `Ú -Ž™M[¦À·À´@[3›¶|o7€i€¶8f6mYßnÓ€çh‹'_µUxÿÖ¤ýL{ši3%0mÓ€g5íLûê¦M–À´LžÂ´ß7ëÕžõæ=6çÝ?޼mköŸÂêö$/Q)ê•¡…!´ŸN±<ìjþq›_Î6m¾¦ `Úðè¦}Ö½¾Žz2ÝÖ‡Žïo>ua$/QEuh9Ó>Elݯ_×§Ï7qíKL›2i˜6<´igzƒ‡B=uÚÇÄqæõ¡åv†§ÇA-Ú´Y”Ó€§4í”Öì¹Ð´ëC«ßùH¦Mœ À˜60ín¸÷Ø%j¨íiM›;Ai0mx2Óþy5¶?%Çç[nuhíàðãRiOeÚô Š €iÀS™v°úwnñìšäÕ£ÇKê[ÚñÀo?[Ÿ6ƒ‚r`Ú -ŽùM[ÆA!0mЗÎ3›¶¼ƒ`Ú -δg6mÙÅÀ´`¡mq܆k˜6›‚²`Ú -þºé|%Ó–‰P0L´Å™¶|„o7€i€¶8–mÚ²Š€i€¶8Ó–›ðí0mÐDzM[†Ba0mÐgÚòJ€i€¶8–mÚ²Š€i€¶8Ó–³PL´Å±lÓ–¹PL´Å™¶ü…Ü0mÐDzM[ûvÀ´@[œiËeÈwÓmq,Û´e´o7L´Å™¶¼†0mÐDzM[vûvÀ´@[œiËqÈkÓmq,Û´eºo7L´Å™¶|‡\0mÐDzM[ÖûvÀ´àŠmq܆¥™6cÚ0m¸:<GyÅõá c¹¦ L›i?­ió1¦ Ó€—6íÐW¶)d+€i–h,‚•Až˜6`fm Ä 2À´L››An˜6`ÁÎÀ%èd%€i˜6Cƒ|0mÀ½…¡Œ$"i‰¦ àÒrÀ´üARCö˜6€?@jË;˜6€?HpÈ8Óði¹`ÚþÉ.Ë`ÚÀ<ü¥<ä€iøƒÄ‡Ì0m€ô—S¦ àM¦ àròÀ´üAFH`Úþy!w`Úþ ; kLÀää €iødŠL€i€|0m5¦ à;òÀ´ü2HFÀ´Ü õ ¯€¾:¾GÓp»V‘DÎÒYP6 !¦ @­)ñ`é¬ Hé ù 0mjMHgé¬(οÓ Ö”ÎÒY !å¥3®–¿œ`Ú U$¥³H*’]:ƒiL€V‘tÆó¦³ò Í¥ó½y߬w‘]oÞûûú;úlßöko¿m™6ÓÓ­Oé,E\:רöˆh3m¦ ¦ ZŸÒY:‹-¤¶tž¢Ú;‘îý£¡3ëдÇ>ŸÎ߇x¼ÐPꇡ3m€iÐ*‚t–Î †²GOçFwÙ Dûø‡ýþƒ0Ÿv×›öá_­MïÎï[}ê¶½ Ó˜6­"Hgé¬l(xŽt9)Ú}ÕžÒ§]õ¹çñL`Ú´Š ¥³hKd|½ÙLìÓî‡oô8À´hA:Kg·À´¡¿rþ2m0mÐ*’ÎÒÙ]àcÕG‚(½`ÚÓ U$ñŠé¬À\I³%¬¢ ¦ 0mZEÒ¯›ÎÊÌ•L[Â*·`ÚÓ U$ñºé¬Ø\C³É¶B ¦ 0mZEÒ/ÎJÓ~š4ÇsÀ´Á´@;^:Kg7EùȶâŠ9ó—iƒi€V‘t–Îî‹lÓleL`Ú´Š¤3¤³"Ä´_« nßVoÛ«G-w• ¯~Éé·¹q¦ 0mšïÒÒY)º¾lK–—4í+]ý¼Óß7ë]R­×_ׇÿoÞ™6À´hA:Kg7È´1VDw2Ù7ÈÃŽ¿öjz¤ÔVVCkíïü>üEþ*¦½Ù\k‘¯g7í÷;gÚ¯Ô6½}N)L;ã›IUÎùs[“Å2¸PxXhÊ£“½ §Lþü- ñ“ÃÇHâ(±Z}âÌ´Üôù ïý(¾Ú3ÛŽÊÞLŸ¯ãŒO?O{°|<ÓfSÏÔxšãr‰×æmG¾nÊÆ“™v¹+»)!ÁhòL'ybRO¥içâ3¯i‡‘dÚZ}âÌ´Üýù•}v,¾W6í„î3íqÓþHδgÚLû)ÚOó™öõ2Kqu¦Ýßfq¡O;¬ñ &G\߯´ãKçbÿXÀ´µúä/Ó°ÀgîRF%3@éŒÅµ]n–eW0íÔâýƒß°öÿgg …Áo[¿ý–ê*®±5 ü¿M¿IQ»¶lÚÉŽç6ùÂJ«pižv¥i'U»Þ´ã®ç çi\Hß‹§™VŸüeÚñÌ-­¦Ê!µ]n“kW1íÑõ{‹R_¡×.™ ¿¾ÞrT½À3oRÔ®-›v·àvÿ·‰vœBfíñÒðòø ì,èhôÐÓÇͳöx|V°Öúaå“·ŸLû [}…ÙWz»üeÚÓ”ÎÒùéD÷0íøm ¡1—§Õ|$zV›£^ÖÄ›µkÇL{Ññ§Iƒ‰ÝÀ´G¿#L›i`& P:ãÑÚ.×λ+švB€“¦]x³}2üJO¾IQ»ö¡MLì.¦]÷ùIï¢?kü™}½¼üeÚÓ”ÎÒù‘G÷™§Êð°Q;SŸvúMмœÁÓ —˜vî5òå×ËGôl<ñzyùË´'梞K ·Ig_ê‡n»\/¯¶öøht'̓nœhžöð¨â<í šx“"oñdð4Ã%¦]~|aÍ:ùÓÆ•È_¦ h¨Ë¥³tÖ6]îCé*ïÓN,¦•ž~…Þpg¸ÖÕà€xíñ³Ï7"šiÃÓŒioÏÜÏ´™6í(¥óRÛ.×ÈÇg.s¿I‘iÃÓìåM;÷ùòëå™6Ó ýÁ¥³t^vÛeö¬|Þ²1ÿ›_Ì´_|´+­2}Éé“ÏebW0íÜkäsû“ï¢gÚLðÌ”ÎÒùÙŸNÊÓ>Ëë^ÖÃïbÚÍRë¯ëõ”õ©™Ø¹¦Ý{'ÂϺ×ȧæ´dßEÏ´™6à™ (¥ós? ” ¦=Á“+þnžüÑò‚£Òä%3÷&¤Þ„6¸ŒÉÄëåõvWX÷Ðsžt²TùóàÒ™W:ÕÜÅv¿ºŸ·@iõÉ_¦ <÷3÷ö¿åßt¼Ùkà ºqÎ d‘é4§½´œ>å1¿£¼DÛe®·_ãàµEÛNﺷõ;âd¾¹‰Þ»ÁK’ïHÊŸ›Ž[]€¥‡Vwh&YÎ{Ü'_éTsÇc¦½³Ž‰1m¦ àéMûr=»éx³G3À[þð1µ7cÎÛYžiÞ½4ßk‹/UñWi»Ì’­L›i§D{ôqwÆ4æÊu›b;:àv– ±­I–úçùèPáú21¦-™6ð,ÏÜÌà±xLZïý3?ãQvU-’{Ž7»§i§§?Fýýö[6yû-¤ýñÁçæcfHa?}×›Í[6#ÎÈßíY¡-дsÆÛìßD6ý}é¿Òégâ¥PƒDaÚsæ,ÓfÚc&<ø&æÔwµ*=?¦¼!iµ¿ÜyqË3ö ®ª¸/1íÑ»Pb™¶üeÚÀ“>ssƒÇ2cÒ 5ëy•óMÇ›ÝÏ´ Õ[¸3=ꯔ¼mƅݯáÎBöµé{87JÞô(ÊIÙ;-´Ž?µ ¿ä4»÷{ûRÜÜð¶÷î¥CòºŒ¢Æzo:%Óž3s™6Ó3ápDxÒ?“œmÚÉ ëì¸Ux%ÓŽ/4íš»Pb™¶üeÚÀs>ssƒÇ’cÒâij—6¿éx³û™v9éÎj÷œÚ9ûƒÞÞVíçíÇØÂ°…”¼ôÙi~fhËœvGoªœüþV¿ò8)Ò¹0µ].Í_¦Í´sÜUüáÞo‚áó3> ÷´ˆ•|øÐK™qáÜdÜj¬4í\²œWçûãw¡Ä^û»€ÛÀ´TþÒŸ“Öí¯øÿBÓ¾Êx³û™v9éÎëa8þ5þïÇØÂÑK3íÞO$AßvÙ´ã/šv¸æÓ¾B3m¦ø.æI‹~oÞº©I«òÉG_{…⸤è'¼Ü¹‰Kÿ¬°¶"Î$KU5”š ŸUwJì2¯ÄÓYvO`ÚÅQÙçÿ2}çñfw5íBÒ9–oØ›ýöý?Kƒ™vÜŽÍØu¶ûúc¬O»õ¯OûZ¹¬}É´¯ùL˜ë‡àkWm5óÌ•X¦ à Ï‚Üà±’j§GÙצ¸éx³%™vnžö@ÌŠÉfXä_yHaï'7ž”¿£¦m‰+¢õW/è÷iK¼õû§·Wœ§Ýpδ¯ÑÚ—LûV,úu•J¬´’LÀ½žÙÁc©1iÑãá(»ª¦Ã=Ç›ÝÏ´SI—[{|èÒ•/ÁzlqHa»²×æm<#Î]{¼6´Eöi§_§Ý˜özl™ñä—ègjµ0´ŸL{Ö¼Ö¾dÚPbá»ðdeRvž¹ž›³§óuFÞìEÑ“Î5¹ð¨P—¤s0üáa_–ç™D}ÙXHaPÓ©™¶ïÓà™Ë´Åâ¦í1íe§óþ®ƒáw,·ûÔç¸gÝÊÆõ ÃÌ£–ä¾ ß¦ À3—iKgé¼üvÀØË„ƒÝ$†`þiw¸Òr¸8\4¢·î{oªEe¦+W+c…!ñ–øäê€åcÔt`Ú¾ Ê$Ó—ikõiðÌe€ÒY:_µO{äÈÜËÞvª®ó>á-q£Y¯lÜÉ´s…aÞw ÞàÉpK½‹i7ïLX]'ÞÀj´úÀ´œñ,À5V ’Î÷Jg_ê›ö蛇ÃV«‰&VÎ}eƒi§ ÚÛϰ{½ûóúßÿÇ[ô¦€ÁËSÓNö6ÿÈßIúƒT¤sê-½ ý£ Ó7¶o;ʲ™öœµ¤æÇ$˜6f²ˆº\:Kç—7íÁxá\ŸvY0FL,YŽ;• ¦‹p\ ÿmïÙ±~w{²ÓŽÿØÎøúu}ú\‘>ƒá“æ¨'_™˜p1>}ãxÌ2ÞBÁ´ïdÚ’h®”dÚ >½Lü¡a[üQ`Òô×|šiõ©C™6¦Í¥³t¾:'xGøvœê@N;7oí ÜaÁ }žÇãÒ’0(Lûje£®0¤O)¬=>’¿sÔtÃqÝ]‡ï°:ñ£Ñ”i•¦]³ºÁå¦=:}ƒióCÏI¦ €i3@é,Ú6]DÛ=–me㕽%û‰ ƒ¾åÜø‹úi70í82É«×LßPbù¡çä<¥ŽiðTe€ÒY:?§i Óž«l,9 q7í£co¢ÂÛ3íÞâbuÓ*M;îz¾pžvqÂE~ú†§Ó–DLÓf€ÒY:k›Ö–¦}yÙXrŽ.­ˆ–Â~˜ñÐ_•lpL@{í4‡ÚyìÝÊàó¬=ŸU7}ÃÓŒiƒi`Ú P:KgmÓª"Á´/) O½šðTÅÿêÖO“†§ÓfÚL`Ú`€ÒY:?ÂMå,KÙ8£l<¨cǾ]q3¯vO3¦Í´™6àIáKÊ¥³t~» ]KŸöy©·Ø²±šŽlõ4ÓfÚ˜6”ÎÒYÛt~+“é×óØå#[=ÍÀ´™6¦Í¥³tÖ6õ¬»¿iëÓ†§ÓÓ õÉ¥³tÖ6U6æ,¯4OžfZ}¾)L€Ö'”ÎÒYÛTÙ¸UÙx ÷{]´öø4Â÷TŸ½ø,Ëç¹0ð»ß—§ÓfÚLÐúôT•ÎÒ™i{Ö={Ùx¢÷iÏkÚ‹µÐ»˜öñÍÞëõ×õáÿ7ZÍi3m¦ @ë“B:3íEÈÊäÊÆÂ“×þéèƒN2xËö×"÷´ ÷´gx¡6 øÜÁŸN=…—Öž¡$U¹àσÈg®žKºfÿáÜíÛŽ[¾3i3m¦ €i3@Hg¦íY§lÜ7÷·îý°Ë¡ä~ŒìÚrd±¹ ¥ƒ;Ûx¼t´·`Ú»£Ú öã=N"ò‰«ç“.ü bÿy÷çÛ gÚL›iÐúd€ÎlʳNÙXNîç¦[×ﯜ°<쌅CÑþ˜8ü;>xôê³ÌTWb™6ÓfÚ€Ö' t–ίõ¬«]hêç_ÿ·áØÞ}å¿ÿ¯ÍzÒþ¿¶ow–×1ívur ÷Œ¦]¾Ð¼¦‹ê¼¦=štJì‹›ö±„\¡HtEoJ¦ €™0@é mÓ+švó^obð©ûïï,¯`ÚáPìû´ãñÒ£ZŽiÇ‘O^½&锨7í+ÙøÕži¸ê“—¯$¥óK¥ó3´/ ;u­¹xA©ôþàc«Ëƒ!¼S÷/ÁµŸžö*øÉ#œlœÈÙÂþŠyÚé …“©Ëó´'™vÜõ|á<í¤ü'ïˆi3í¤7Ÿ7½•õâa‰ÕÛpöÿÿí·^Ýõö³Èð¦þî8d¦ àŸáÓ9.Oç%°ü¼~Žt~îòü¨®•XØ)» Tn¢qÓ÷æ3ö/@µŸôx°–øæ­m¸wKm×ÏífXÔ¦.´mvV¯=>Å´ÃæY{<>+—tJìËšvûóËáÃ_ Ó^5käõž=õmVÕÛÆÇdOéÛîY=X§? ¹}â—–dÚ®eÚ`ÚÒYy~¥öåè‚R¹ýå©­gïç-·¶‹³òî^$ûÙ#Ó¾“i·jÛ9n¢O»ø9üg¸Ž}î˜ä7"쾎{¸{!'¢Ì´ܲ¢Ó–ÎÊó³·/g\«mrºù†MÁ©û™6ÓÓ~Óî{ì»NZtü@.šv0(sÊhÈÍýfÚjÖ‘Rã¯TÚç]«õææi3mÏ:(±bÚsõiÇ}Ñ£¦}SûäÚÑã§ÕÖÖër8‰=7(uLxõG­–”(í—7*çl»yŸ¶g”ØÇ6m¥Ži4ÛÃêT(íóØñ\r<_H¼åžÏºòï%gýšÒôÊ…¯ÔšΕ–|»äôÙ~XbÚL›i˜6 ´?v Rî_ÁÃÏó¹4õ.¦ÝLž]]ÇÓs•X¦Í´™6 ©EPÚ™ö#æ~;I4~ÏP¸ç0+ÿ4Ÿ¿Y)>Z!ùëêÿÚïl‚ »» ïs;üõtý`­æÜ$ÒÁ_“}ÚñíŒ(qèT\+¾þQÝÎðÈù۷[6ÓfÚL€V&” @igÚ ÈýØ#Ûå“ûKѽû#õõû÷ÿñÖ³ÌîíI$‚¬ó4¼X}Ÿv´wÁ$VÉÏ.vß¿Éq¢+dn§½@ïû©Ô¼hi†žy¦Í´™6ML(€Òδï›ûIÑ.©loï¥lû?³o\52lå¤ëò)qÎ…“<þcâðïʤKpÅåþ˜6ÓfÚî(í¡…=-î'¼D~Âà·ç¥ÓΚvØcÛìË9ì,¦¼Ã¨4€|ü¾f5í0VLûYM{Pöf™ ðÑ›{0[˜LÀܦ ,¶´ã‘×ëõŒòs®³¿ŠiÇ#–§™öécÛ9|_Ó®€=‹iW&]8]ŸöÓ›öi¬ÇŒãùyú禷 ^ÿ{—º^ô„¿Övpzö±Ï´L¸Ô´{ÃSkJõ»kÖ_¿®Û®›îˆòÊVÇ%¬JöÆL`vE«ôÊXÓÇÚãÑÏ1ÃA¬‡Ý¹ÙщJ†k¤JÁøÀï\q¤å@ºÑ¹ó¬=ž¾îÎW‰ÙìLû¹L»÷L-ÛïÐ|›¥ñ¿hF¿=u‡œs­Áw6¨¶Ý¥™6â¸Ü´ HÇÖv3>#ªëЮ<·ÿ[}‰ìþ‹TÛû´ÇÓmj¢NÏpþ8+ò—òHùË´ïgÚq1«éß.ý)õËQàâ=Õ®¼Ö{äÓíÃ^™6€ôwž8iÓM;¹¦Ta©§Ò­ïž>L ¼qíIszGWÆbÚ³›vy°÷‹ê.Ó~UÓn†äôçZ\dÚO2êÊkûLe‚bÌiÚÉÇ3Mûô¹¼`U¶ÏùàÚç™v®Û”i{Ö1m\×´/¶ßäôìpçì}ÚïgŽgÚ­O Ú´“’:jÚéIÔu‹ç¦§lÞQ¼DnE«´j›§íYÇ´qmÓî?õZîÏ–7íŸÁKôš}áâz‰yÚ•×JÌÓ<¨™6­(?Àü¦ý‘˜8nÚ¹…ÁãÑʼni‡ÛÜÊÕá:Õ¹KdW´Š/díqÏ:¦kšvêÝ×ÝÐïÍä>íüë´SkW^+ûx4z@ú®é­OàqKû×b¾ìLL›iƒi´>G-íÛ·«ºö™Á¿¤ißâwú|[­oÆš½ÜñÞæ%§{ËÓfÚLÐX„â(íZLûZÆXaê ®o©»çÞ̇]]_4/–i3m¦Í´«~«=Á=¥i_”ûÁDÐ` §Ãœ÷xvh07ôçpÚüá¬&´áÓĤÕD±Ê& · ­R׃@§jüàZáý7˜KÏ0e¶o;®`ÙL›i3mš‰P¨¥iß?÷ƒÅ»%ÝÃEÜÃeÜc –Äëô1rD}Ÿv´KQKÒ¾à=ºÉqR1_E«áçÓs˜2¹Ñ)±L›i3mà€ÒδŸ'÷s¡['¼Áí£ð.ôº/4í8^=<þcâðïÑ_ Ýã7÷δ™6Ó u¥ Xfi?OfÓ‰Ÿ_ûm1 r½€i‡o'Jfqâ]n™×™Ç.ZùBÓ¼Wiªç_É´kîZ‰eÚL›iKú{äi÷3íѿΣM(‹YýúÙM;ì¦4ìûÆ9÷ÆEçL;ríšR2‹i—cF)™žL›i3m¦7 ǃžiç–öš¥žÒÚZ_*Zÿ)±lUf5© ì…¸öóÏÓn“0¯¸MþØ1{GæÆo'CNÎv., V=O»î—£¨ëùÂyÚɸ%Ó“i3m¦Í´såì¿þÓf³-y#ÛL8»´×.õ”òœÔúRƒõŸ’ËV%/IüTûùGkŒoÞ‚7WËŒÇã¢û¿µd|2 9ý³KÂdÓîÅ©*neðyÖOþD”LO%–i3m¦Í´m¶5mµÓÎ*í•PËëQ}×ÓŠ»=Ç j!ªý’ïÓ¾ñ ç{ò´Î¦Í´™¶Íf;Ç´U L¸¡içÄ8Þ/[Å´=ë Ä2m¦Í´™¶Íö8¦­nÐú&–öÊ¥žÎ6íä\Z¦íY%–i3m¦Í´m¶‡2mÕƒÖ'0©´W/õ4ôáÌúRÃðRªžãjž¶g”X¦Í´_Õ´ÿþçÛê×í߿ϣ¹ÐÚýÉæÃå±½} KÛ&ÝÔUS`´D½Œi«!´>)¥½f©§dÏsr}©„"GËVåW“²ö¸g”X¦Í´™öí¼hÆëž­…w1íÅ*âå)s[cÚ* ­OàÁK»÷i{ÖA‰eÚLûµMûøáÏ/Í›0¿}O´þO°ý|úYûGÏûWùù¯œ~ :º÷Ÿƒ?µ1ÌE,}|Oîà\Èß|Ê„P¾åä‰?~=¥Ytã5Éœ^{Ê sÛ>}yoCœX™2y¹µ8er=øü÷X‰zö·|y϶Ö'ð¥ýç×ß~[Ì¢Ñ/ð>ídZ—F(ÔPü5e5xýÕÔ@FcuÑï<7?W‰eÚL›i•æ$c{ÅJij«jÁïß~iMì BÅýIƒ >ïŽ<žµ;}õùŸ’•#Ö—±a¥žÛDÈ»ß~üÞXnBñ–‡'Öwæ'“·Üs›É‘á/ í-„±*_k4Íë§œR©I™`O*£K¦KöŸ¿ u* ´3í)~8»ÓN}‹õ-u÷¼Ós(Öë¯ëuÿMòJ,ÓöœdÚg›võàáî¬ï›O¿lþü½zñrõ;k†=Ÿwpót·ÜÛÙ—ÌšaÏ…Ó'²ì,3)'™vùç†ÊÜÏ%ûKš¶º–{qiG=ÏoÚí4úÎû½Í£t;Ë/²Pžü_ÞáÚyÑb•þœZ¡à´¯»×n9‚vgxäáÜíÛŽ{[6ÓžÙ´1Ës’i_Ç´»¿©ßéz$Óþgÿ5¡Õúš;ñ´¿æ‚"ÆÉŸ^sJi—³r‰¦]ñ«Á+™¶ê–i9þ@5OjÚ«žä—¯9 ÛuTͽf5yäo‰+Ùßúm?®ã¤VÝ_E÷°í:Ü9øÝaÿ9ùB;¦ý õþBALJYüÏI¦}ÓûWëú!û´ëÄ2Ñÿœ?qt@x2yãÓëO‰j1+g6í(e˜ö¦­ÆeÚÓfÚ#£Ç[W};zyQú‚%—M{ Σ„ÇLþmX,sÉδÁ´¦Í´3¶Yöçò³˜ö¶?âo4\ôæ5í0VL›ißS³ã\fÚ/iÚá:ØŸ¾l:yn—•Î-Ä=\{¼¿úµÇÇ#V±öøàà|Èí×c+ig—[_ECÊ;Õ c[LÞ`AïÌèñdŽ 2·]f<\{ád—õèóöi×ì,¦_+·p,¼>mõþ"bûp1gÚu¦m{âmÞ!Ö£‹Û˜¶îoÚA×qnžöÈ×™§]eÚq×ó…ó´“q;ýÖЛ§Í´Õû÷ŒêcEži3m¦=Ÿi¿ð¼h¦­Æ€‡2í`iñœÐóöúeÃÓ]ÄÁX횎ñneðyÖÏ –^ß¼ ÞδÕûL›i3m›iƒið¬»é³.žá|¯@^(ÕûOVïçâù@ñgÚLÛfcÚj\ð¬Óö]`ÚL›iÛl6¦­õ žu`ÚÏø]X¡L2m›ÍÆ´Õ¸ð w<:ê}ß…×,“Oô–¯G\Ü«æ½Sµ¾÷•¶æ^O±¤Ù˯ͦÆeÚàY÷Rù«Þ?ƒ?p§·m3íg1í+ÉØ-¯uǤ{ÐÛdÚj\­Oð¬cÚx Óó—i?©i7}ž«Õ§/ï­Ãüý}óiUê Ý¿`ùô–„oßSRÔ„°þö¥³£ý£`“;ÿë?·ŸO SþïÓNœ¼‹BÌã˜dâÖ‹Ã)À.´Á%öÑøeóçï½Ôûôå¯ýÇE‰Ü»ú!„Áýæ"?é6½hMùÉ¥Û¤’0ZD™¶Ö'xÖi3m¦Í´ïdÚ«Õçv^wúüþí—·¿7cÛÂ[-ßûRBu¶Ÿ·ÜÕºÐN™ŽZµßy 6¹s÷¹°ƒh•L;wpâ.Fc |·alCK^¢ŸŒ»`w»4‘ëçr­®O»â6ûN^¾hMù‰ÓmbI¹ÓÖ:ÑúàY¦ ßßMœ§0íA?dÛKyꃯ›žD} *<`(l…ýNàò<íÜÁ£w1º3÷yôàr$»í 9ξ(ÀÓ¾ä6Ó’òç•„IE”i«½À³L¾¿Lû~¦:OHNÕòLP©±ã;ñ.Έù9Z>¼DÛ7Û|˜7‘‡÷{_Ñ´s-ÞÚå%¡&õ˜¶Ú <ëÀ´¦}'Ó; Ã>írë~Šl3v÷ÒNËë™vê.΋ù´ÛÌ\¢ù\ŒÞÙQŒ#˜´"Ú¥}ÚÅ £]ôgšöØ%˜¶´Ö'Ï:0m€ißÏ´ƒþÀä<í¬)c¡Óó´KI`«Á»­†;Sfõþí—£¥ì>´ÆÒìL…³³»FDû§‡vøÐœ»¿ÏÿLªcxþF¢[.‡sØÓEìxéFÞ§ï øõë§ÓçðBm<Û8'{¤ûÉ;r§±CF·3šVaÊ”;┡Ù„š”àá)¹-¥ÁçóbÅ´µ>À³L`Úg›vqrF»mg)uÙËÒçÆ‚bSíNo;¨“W,„öx—£ZW\ËåTêõÆ>§R¬¿½ÊÕwšm4„à€ÑýÉ‘ ç:´Ë·6=Á'%H}fÏ´µ>àÊÏ:<7ê}m¦ý¦Ý[D­hÚÍþø¿™pöæ|²¦¸»µÚp]·:Ó.ßÈ¥¦]ÖݳM{ÊfC›’V£'&ÆßδGb5’¹L[íÏÅÏN,Ÿ/…®­Â´Ÿ×´Ã>ÌÑ>í¸ÿùs;¾7N+؃nժЊ:0z#ÙþÞûšöèÖôiW‡PâØT‚sM»˜à¹©Ï\¦­ö¦Í´™6´U˜öL; Ý“«„·ËeuÓh{=ŸQ8í`àoéùÃÙÐÂɺ™-5Ý7y##ŠU=O»¦c¹ÿûB«f•¯DòŽÜir°zÿó¤´JÏÓ1íta˜P#×;,«ÑR:>O›i«½€i/Q³ÿeÚÐVaÚÏbÚáZÓŸ¾lZ Ûï¬éÉ ,N³àYÆÛ ¡Ëegb­PEà°tö˜bÕ¯=>Ú§Ý®¬ïu mwkƒ4ï"3r§íÁyc¬N«þÚãñ‰Á íNwK…¡.¡F×ÿû÷äJé«ÊRZ³ö8ÓV{ùTÿ €i/Þ´—³eÄ~ÝWdÛlL›i€Gúé‘À´™v‚f:´™¶ÍÆ´™60퉦ý¾Y¯VoÛ‹.¾}»4@[…iÛl6¦­ö,õyžûœ1äÕz³ycÚÐVaÚLÛf³1mµ`Ó.{òûf½Þ¼wüuðóã*4ÛaaPý§“»šW=ô¬öÚ*LÛf³1mµ` óò?§˜öð/Çíþ{ôï½75í“Bï]ýë×õéóéàÝÇaH€¶ Ó¶ÙlL[íxØg{Ánƒní®C»Ð•ûkÛ7>ø|< ì<§ÚÀkšöÒVêÏ\¼+à Ò§y=UðʱÛgÊ“-ØvƒÛ-?³Æi3mðlŸÉ´; >}8Ï´ãQ§w Ó^Ä´—¬Uɸ]ãuÇ•áœq¹I§ŒÞ¯má¦}“80m¦ žís™v0f¼Æ¥Ï3ínð8 ­ò2¦½€#ý¹ã‡¦uõöã÷öÈýçæÜï›OÁ¹e=øóËúô;ÞúÛ÷Î=Nû›‡mûùô‹ßÈO’’SñÓí¬ 6;øPyÚé…d¹±Mî)ßÚ ›4™æ…ûÊÆ!òéÔ(ƒ8gƒx®¿}‰ü„BrŒÒ¹äÊ@)Gv×ê¿snø§/ýïü%úF]*ÉL[íX„i7‹•Åã¿3ó´»?®jM›jC[åM;ç¥~>ïíâׯŸNŸO¿û¥Ñ­½|þçHß§/ïÇK´!/qÜß¶¶ƒ¨ŒöiGdï¨bXø r!ïn¹½£)Îwwt¼»Ým&m,æ‰4ë,M\1“ò™Ô(ƒdÎî/Úêþ¢•f´;7lÉ‘þUvýŸÿ¨¼ÄxIfÚj/ÀuŸí•öÃÉÕáY9>Šy}Ÿö *¤Ú*¯nÚ§Þ¼áç¶k1ÚyöŒÖd°SG‡ôÄV™v:äÍNÉ÷« ¹fÜ{ùv’i~ÆÕë£1Z Ògí¢TÌÙú±Öƒ#/ $N«ñ¤®xèÐ~ÿû÷ªbSS’™¶Ú àÙå¯gÚ£62f½ã«{žg4í  /`ÚÆ5ò ¦Ió©WŸfÚc‘Ÿ³c¦Ôð€³¤UÅœÿ¶kºë£.\bRIfÚj/€g;¦í·ïÇú+:¯eÚÝ<ÞêyÚ™ßÝÚ5!ÏÓ§Oóú«ÏhÚrv¬ÀŒÕsq ÉQßnö×Ý2ÓÖx¶`ÚùÁáã~ØÍ˜­1 Á-^bû¹ë›ÍÏÓN !ŽÇ<7«gÍoÚÍ ÞlgCîFÏ6O»Ò´³ó´ç2íTΙ˜ž§}^!ÌÓ¾ ATK9rºhñ³ü%&•d¦­5ðl‡òŒ§5ífAæ ÏL­ïUë›xMô~Üõæsÿ}ÑÃK´‹KçWlŽ#œÒ¼3¢ÄèåËFýþåßÿÿ”Bc[^麦 :æÅ«çÖŸkôx”³a¬zkÇ‘ŸTH¢¥é'’L«rޤ~.»Ä”’Ì´Õ^Ïv(ÏxZÓžkËt«Ú^wÛ‰òºô6¦­öx¶CyÓ_ê™ ½ø/&M˜¶Ú àÙ(ÏLÛf³1mµÀ³ÓfÚ6Ó†Öx¶`ÚLÛf³1m­1€g;”g0m›ÍÆ´Ÿ²öz߬W«·­„f(ÏLûZ¦Ý¼Êh+Z=÷rk3ÞݵêT*Òïµ²1í®½¶o«Õz³ycÚÀLå™i_Ë´G_¹l[¦¨_5³Ê/ß–Lûj¯mÉ´·ÝÙ·íÁ‡~ðÓžæ:Æ€™Ê3Ó>¾»aýí{ð–¦c×eÿuMß|ì9¸VH_ºŽj:BWo?~oß¾êØGº ÿ—ÍŸ¿‡Ñû·ÿûÿùÃÔS„÷±mibµþö%-~ƒ«ÔD;üÓÈ]1$fM"´²zÆÝ S/Btû]u1sËôi'O¯bcÚiÚñŸæ½Þ¼Wi:€™x)ÓÞ™Ò§/ïGÚZ¬O½NË÷o¿4~¸3¨Ïÿì¬ $¡g‡Ówúõë§ÓçS˜ÛÏîBnlC>l»¾þÏp¬òéZ½@9ß’2ºÄUF£üiä®ÃtK'f1zÖ:ý• !yûà·9Úyì̤ÁfÚÏaÚûþêþßm`Ú˜vyxpδÃçäÁ‰Ãã‹çv;ƒ«ìÕôË_ÉYÁ½ã‹Ã§Çqd˜ôè]—/ZŸ3Þ]U"# r·YŸŒéTŠ®bcÚjÚíðñÓL˜6 <3í‚iwƒ£±ÁÉ.ÓÂÁ•SˆÇ|õýÛ/mìi@{A«¥÷LÓ»ëlYcÚgÝ]Õï&ñ öè6'˜væôx¾€iߪº*ÿsºièú¶ïþÀ´€™Ê3Óæ§' §û´§¬‰u‘î>_ÓE§õiW†9)1ïmÚÝÄò¨ó¼~éµdß{¼CÙmLûf5VîóuL[§60@yfÚÉÞÙyÚÝeÝ\ÜYL;9…øt¡á¢byiÜrŒÕÄyÚu¦=z×É⣓Þ+M{ôî’#ÒëyàÀ‰Ûo6ëC;j½yÿû¦s~oéöœÂÙÿÿ·ßzí±·Ÿ½@zþüá¸;™iÀ²d[âL˜vdÖ­A¯7%Lûð÷£ð6¶;ì >ü{½ÙÆÇdOéz³ÿð¯clr!ï9Ý~`ÚÀ´0mXŽi·jÛ9n¢O»ø9ügãÅï¥cbÑt_Ç=ܽQfÚp?Ù–,xÅÓõDž¾ÇN°ë¤EF}™v;*<{ÊhÈçÏýfÚÀ´¦Í´à:õÄ<}Úq_ô¨iOÙ]°Ü§YŸ6,G¶%˜6ÓõDŠñyÚñçÖ“‡Þ\mÚé~éXž£ÍÓ¦ 0m¦ VOÔõcwc¿  U?­¶¶^—ÃIì™%­˜6\(Û’`ÚÀ´Á´€i`ÚÀ´™6€…é%`Y8-(¦ ê 0mž±P6 ™6¨'˜6ÏX(ƒLÔL€g,” ÈA¦ ê ¦Í´xÆB0m˜­ؽªkø2l­"`ÚP§@ñ–0±xÔìV°wÿL¾[«žØ´_♯NÁK~»™6ܧžH¶®Ž;ôÅ{ÿÿãž¶'¼=7<«Ýžxê4oN½ÿ¹Vmq¦­NÓfÚ0=*p¾Õu:ª'ÐëÍöà̃¶Y#Ò¡¢þÕºùîRƒ®t­"ÚâK1íš_H“?­†UÊzÝûKüã,Ó†o7Ó€g¯'‚Ó@­GÿÙ÷ûh‹­ôY«€¶øÒL»îÒÞO«A(‰ý™g™6|»™6üó.³Õ)Z#JÓu›V€›?cN{TÔPNO{‚‡#wÿ9pðàæ·§%=¹àσ'ƪÙßDjËV§h(uLÔmZEîhÚ'mÝûêIt[U w¶6»Û¹þúu}ú|òçÝÇæÓÄ>éèð‰±êÄúxL¨S´F”:¦ ê6­"·7íVJ“~ÜîÜ}hvðùxÀÞ¿£g‰ö´X‘juŠÖˆRÇ´@ݦUàLû4L|0¢»tVxJnù…¦]ŽÔ)Z#JÓu›V€…šv0"<Ý{œ;ë<éOí¸‘Ð Ï÷i'›ú´µd˜6¦­ncÚ˜6žÒ´Gæi÷v¤äxÛuŒ÷æi— þ¼&‡o Ó€uìÌà.uÓðê¦}¥wAÍÅ·ŸÙxî÷ïÿºô›¸³i§×Ï6‚žìÓÊã›·ÓþýΞçƜ3m-¦ ê6¦ ਫ਼±£žóv]×ÓßéžîbÚKÊ4­¦ ¯ÜÔ³ ÍW¯àñPžéÚÿk׫xìûíw0޼y0í6êœ ×ÕŠ…*û×ðu̓—I…—¯Þÿ%ø|ü¾q*Õú¶ÍÁy>†Ï7ÔìÉW-·F<.˜6<ê³5Y©ÛX?xÓ>ÈhVa£¿–—•}%ò˜©NŒMÏ…ÖÕŠ_¿Ü:õQÁÛÏCÑ>^-µJ¢NyµÖÈ`¢Üê:?1¯’|Z¼ø×pçªô#Ó€g®á˜¶V€[›và§Õ ˆ.grùèäΤPgÿœúkò©×/Ÿä{ÿï·ÁHðÑ@²¿¨S^¬5’sày;vøÜþ BÝ×6ü5écÞÚ™6¿aôóëo¿-£’bÚ.6í^ïlº¡_þk¶owô•Èã¦==йKĽŽÇ¿Æÿ­Œ'ÓÖ)™v¸`ÛÏö°ÓÞ½õ6ÝÌýqéžñú>ípO£Ú¦€l³ >ãô£uéâ™ LSÃkÚ7jè4—YF³J«ÀŦý´É'õiŸH}%rØW|ƒ>í´ õz³ß’ãÙ™¶ÖÈdÓNMµ8ég«¢»¿:›Ãbõ牦Ýuo'¾ÄÝ7¨÷/›ö`)†^˜M0Ûâ÷–iOÿôÄ’I5¥ŽoúÈ-Ÿ­Ä“« ]+¹·‘¥~—MNÝÓ*ðè¦i7Wý5ýÆãÑW"_ožvB€³ªÝï›K©ó6çßOË´C¦Ók—L»=g½^NLþô?º?h¢ä_§š3ß”g•Ç“2RÓ4˜6 …edD4O;äTXÛ¦?ÅnŠi'y\µÍÓ Ó~Ö½ÔÉN@ ™w½×Oýh®Ä“päøèòÚã™KX{€zœÇ7L€çæ“äQyAÜŒW¯æÖaïÓ çñMÓà¹ùÐytEg>›Ÿ_ûmqÑ*ð°õàÂ×ìFo©KŸÍ/uŠ#Óà¹) U`iÏØšw@<ÊRÝ‹Z—£xýù"§NÑaÚ<7å´Š\ã[ž¨Óÿk°F°Hòé€`¹ãÃz•ƒ/€Œ] ÞK””Ëì_Á6á…/ Î.Å¿‹éphfYŽá¢u—ËF5ºëã•{1‹‹™ËµÕ)Z#L€ç¦<‚V€™Ÿ±ñÛq‹ý®¯¤í±é×+޼bjlzúü¡ûm ¼`"n½÷=B‰£—¼åÞåꥷ}aF…ð²¹J]a&ÕV§h0mž›òZEæ{Æ…±æ¯!Ž_ø®‡ØKšxì Îý9õ×Ah}.¼½1<2½Ñ[ž$Ú£IQ‡ÙT[¢5´xnÊ£© [!ËYÕL«À]Ÿ±ý~Ô¤d—ÿºÊuùÆKZN6íéQ,ÄapX!neû-ßòµM;ŽÓÖbÔ’aÚ€ç&–aÚ‹zS—V€û?cÏéÓ>‘v½–û´K3ÃçèÓŽÏÛØé[þ˜ò:ÊKL;º ¦­Å¨%ôÏMLÌ£ë®móè®­U`ÆzðÌ™Ñ=í ‡jŸ>g¸Ò<í^`)÷MÆ-^ø,½¬iÏ1O;QE…éVü¹@¢Å¨%ôÏMT›öõÖ¶yxÕÖ*0w=X¿öx´v³/Z"{½Ù´áî€k®=íZå–ïãüj›ù}6¾åørƒ{)T3™µÇÛ†¿·&OS§h1jÉ0mÀsÓLûzkÛ<¼jkPÞ€ó«Šäúeó^Çû´}S˜6Ó<7q±i_cm¦ @=ˆQŸ=£²˜ZÅœQ%/uŠo Óà¹É´¯¼¶ Ó Ô)¾)L€çæK›öÜkÛ˜§ @=¨S|S˜6ÏÍ7í9×¶±ö8õ  NñMaÚ<7åÑxŸ6ÏX@â›Â´xnÊ£™ùùõ·ß\³µŠkwó Skð„«ûÇ>ÅÂS§LЀ<Ò*à;Þæ¥N˜6 …y¤UàeŸ±Í¢ëͦ÷v†f÷àŠí‹N+dì >?>ÅBê€i`qòH«H«À9ÏØí[ãɳ¼:Xl2~DëÔGo?G¢MµÕ)ÓXä‘V€×zƆïFܾ%^Ùð‘~ƒÃé¼ý¿ßš}ÛÁÿ©¶:`Ú‹ÃãäÑœ6+¢xíglò=ˆ¦Ý|ˆÿË´™6À´- ¼¶i{˦}®iG½Ùo½¿0mu À´‡…çQ³,Ï*Ñ,ÒÓß×í„0lû=n3P«À…ÏØöØŸ§Ý{(Æó´Û‡m'׫”Žmu À´¦…·ã©Æ_¢M×MCÌÙõÃ6µŠ\\6?O×O³*õu÷—P³ö¸:`ÚÓÆ‚ó(ÙàKvnw½2ý‚·Ñ<Ý4B­"³Õƒáâhã}ÚL`Ú€žÉ´?Ú¾— C%„iðŒ Ÿ~«U¢¿z}ÉI¦ 0mLû‰ó(îd5íÓy]/wÜÖcÚü± ŽÅ&ÔÚ¢´ÀõI4K¦ 0m0m¦ Ð,˜6UÓžI³Ûòô@«Ó NÅleFù­L€:3E´ 0mêT0mÀr[–F˜6 N…Ò¢´O¦ @M¦ ÐD˜6Õî^TÊH"Ð*À´¨S€VÀ´¨SÀk´ Œ™˜6 N…’ðlWÝL€ J@0mj/(9`Ú_‚’x®gûöí8úm[{Æûf=åpLàKàuZ;Í^oÞOú\aÏ{/_o6og™¶¥Ñ¦ 0mðä­‚í[ãÙk×êóöíÜ>m-€iLJà‰Ÿía®÷çóM[Õ0m€/AÉ<1÷1mLàKPrL›iLàK€’/ü0/ÿ³oÌ·ž§­˜6À—ÀãµrŸÓÊŸÍÿŸ>î ¾¾Œ2ùwð‹Ýâ8çE˜6¦ \Y³É6˜6,÷*ºVÛÁ ŸVÿI˜öö÷;áílwÚA½ýùiõ¯“ÝdlèÝòíO»ÒärÞ¬²]{øÀ´¦ ¼WÙV9À»¾°Ø3tPÛ½ã&ú´‹ŸÃ;/~+­‹þ¤û:îáåœ(òõLÛ%`ÚÓÀ´ghÒrçÚuÒ¢'£¾k¦=Œ ÏnRÍù¨g¿OÕ§íª0mà:²­Zà~EŒ,ô =MŸvÜ]5íÝ&ë–û´9/ªOÛU`ÚÓ‹ƒÎЈúsÚñçÁ“§ÞÜlÚé~éXž£œ=§ 0m€l»#‹€»Š¶õcïÇ~O&@k=Þ϶öôTÎ'±äTuÅ´¦ 0m¸G¸Š.Ô´Õ<À´ËɶªÜÇp†¾ÓVùÓ˜6·’€3T]1m€iƒ¸â"h9¢/‚"(ú¢Ï´™6¦ ßšPÏZÎÝÖ•:¡®ÔÓ˜6à[S=;"u%‚êÊ©+µñ®M[À´ßšp§¦-‚ŽH]½çÚÞŸuÊ×híß6}Çö;0mmLp }ãj9¢/‚Î_Ñý›öä•×e7Ž^‹Öì!“õ§| 6Ó˜6àzí^MË}tþоèßiOŒz¼·ËÂ%»•rë¤íz¿öþ÷áú©Ï›ÿãí– ç‡È¼–0møÖÔhÝ«i9LÎ_ð¥ó›vÒ“;Ÿe8½NïÃÓ¾ñÝ*Ãý¯Ë¦½ßÕvóQž]6¯C¾ZÀ´÷[îÕ´ÑAç¯è‹þM˜vV†³YEÏ#Ñ«v­O;±ùd#q_NËqƒiî·àN]ô™¶:EŸiOx<µYδëŒ1è ¦û£Àás¹] å8‘Á´÷[p§.úL[¿¢ÿîM{´`˜Û¬ÚÕŸÍÿŸ>=„|}e2òïà»Åq΢ïºñî®b¦ øÖô«åˆ¾ŠàME­¶ƒA?­þ“0ííïwÂÛÙî´ƒzûóÓê5^'»ÉØÐ»åÛŸv¥Éå¼Ye»öðAô]7\7¦ øÖô«åˆ¾Šàr¢?¨íÞq}ÚÅÏá¿•Ö‰EÒ}÷prNYô]7\7¦ øÖô«åˆ¾Šà2¢?öØv´èɨïši£Â³›Ts>üÙo¾äºq‹× ‘Ó|kúÆÕrD_Eð¢š>í¸/ºjÚ»MÖ;,÷i'rÖ§íºÁ´¦ øÖô«åˆ¾Šà…B|¶ç´ãσ'O½¹Ù´ÓýÒ±‰%®ÞäÍu`ÚÀÒ¾5/ðFÓ3íbÒ±±þ±Q|ã^Þ´ áèVxYd@ß½i×÷z;§çÝŸ¿±oó%¾Ä´µ€iË1íð•§¹wœ&_©Ü`g^‘šè‘˜¼fºßÝã—ÊŒÞàšì Mõ¢l³Í¼‹µtŒù0íc„m¨Ö]÷Õ¤¿t)eÚùɇlË=ÞKæ}œ¿¡oó%¾Ä´ßIË20mà†L;ºI½ã´õ•ªÉ…û-Â÷¿¤_µºýq¸÷^¯t•…÷ØùÝM'õ–×Ò1 À´6íýÒ ÎP¦ÝhÚqn1š÷tþ›ðõÇ´iÓ˜6°(Ó.¿ã´üyr§[˜“¶ñU«åûûdXÛ¤;Ó;ò¦ØeÚ-Â6 SèC‹è»7íÂs¶ÉÏÉ?žÝL4î_LÛ¹pnÒrœï`ÚÀ švå§¹Iq¢qžszÎôÛx {Ë­|$3Ž‘i l탗ÐwoÚËDáÖ¢©O›i3혣­`Ú`Ú³L;ùŽÓÊ­íxHèû´Ì­å-¯³Ž‘iŸÛ´P¦Ý—dn/šžÓfÚ|‰i3m€i3íä;NÛL)­Ðåç´[M{3Ö0¸t·¬ûqò¤fzï™·¼2í˘ö´ÆP¦Ýø( 7Ms_õÝlÇ¿XŽi3m¦íïk`ÚÀ-™vâ§/MƒH³7Óÿ”ço3í×Â=aðZ×—òÞ'#Üg#Ó>Ê´‡õ“G/4 L»}0Â4 7Íwÿ>íÜ»Ù.iÚ¿XŽiLLpå½ëzfÚ¢¦-‚÷p’ÎŒ~IekM}Þ¿¼-þJîun¿XÎÕpÙÓ\vÝ©k9¢/‚"¸¼ègÞÍÖòÈOjpÓhJ»ø“×¹ûb9Ѧ ¸Ûv§®åˆ¾Šà£Ÿz7[Ë4–ÕÎçä3JqnG½XNô€iî¶Ý©k9¢/‚"¸ÌèÞÍvÀ¼t¹Ù@ R;ù?Í/–}ÀÅLpÁu§®åˆ¾Šà£Ÿ{‹ÛIú´“¯s;á‹åDpñÓ\pÝ©k9¢/‚"¸ˆèçÞÍ–{N{ÿÓøÉìäËÛF#¼‹¯sk1íd¢¸þƒi®¶îÔµÑA\Zôó¯ÓNÍ=3_•ßµ?þ-9úÅr¢Lp·íN]Ë}AÑ}Ó†û-¸SÓAç¯è®Ó\gÝ«i9LÎ_0m¸˜6à:ë^MË}AÑ}À¥Lp‘…;uÑgÚ"èü}À¥`Úp‘…{5-‡iÃù ¦ Lp¿å^MË}tþоèÓÜo¹WÓrD_EPôEpA˜6\^á^MËaÚ"èü}ÀÝ À´×V÷jZŽè‹ óWôEpMÓ\[Ý«i9¢/‚"(ú¢`Úp¿wê`Ú"èü}ÓÓ†{5-‡iÃù ¦ ÷„Ó\UÝ«i9¢/‚Î_Ñ}ÀÅLpIu¯æN]ôEPE_ô€iÃ%îÕ´¦-‚Î_ÑWÀ´÷[îÕ´ÑAç¯è‹>0mÀý–{5-GôEPE_ô—€iã.¦¸ ÷w§Ž›Ž¾ 8ñ>£¯ÕX LwοoÝ©ÞúÜZÎ{FEPôEÿýv“þß¿$I’âÔ(ÛLî·˜¶;u0mtþŠ>˜¶$I­¦ýOÃc Lî·Î®ÙÃw¶{5-GôEPE_ô™¶$Iw`ÚUÙfÚÀ™¿§SŸÓ–$é¦M»|‡ïÖ¸„f“m˜¶$IwfÚ…;|÷ýÓL[’¤CL;w“ᄌf“m˜¶$I÷gÚÉ›|7ýÓL[’¤oùª¾gÛM?p–¯çã_v˜¶$I·ûüöMâ 7öm ˜¶$IwmÚáoÒZy ´Xp!ÓþõÇׇ¯¿þºè]ûUvzîôçÇm—Ùqǵ¨š9Sa’µÀ&1·HÝú':\>§ÝË|Ód›iZ,X˜iß®FVüç¯cKrõpœ»7ÔÞ†¢6–¹êÀÕ|Zv´ÓÞÉ6Ó´XÐ`Ú»]ãÃ×?wKúÏ»•~yêŸv|úþ£ÏáÇêq·äK`ŒÝ‡ª—ö{L¯™Üã(«hG}±&úÚgµÉ'±N”Ïfý0‡ÿõ¿ß¾&‹šÞc¡ Áæ“5ã¢æ³\ñî’9Lª7Y˜öZMká$ÃÌQ\²–›Ü¸H/GAÏm¾Þ×ç/oÝ¿Ó5ų̀üýQ¼üýßÓ½0íY×¾ð¦ .bÚý]þÚ?þþØVxìoî }}þ°ú¹ùðöýÃ`ëÏ­eãù{ 3{ Óës `ý’}mʳÛõîÐvY…‡d˜.pÿyÿÛ¶>íJ*}ÚQQ«õ×C´»DÉêÍt°·ÖjTªÔVÅ s;,7¹êÈ‹ÆÍã5KA¹mÓNJõ•›iƒi€Å™vØGWq° ƒ±±ƒ:9ü8¹Çé §êÓNxÓ§ý ؾZŸv¹>‹õ;À]’iŸªfF¼·>ãPÝ<^3×7~äÙtuÓîÿ`0ñê͇—¾³ûa/Ûo«§É’í&ÝâíÂa£S+:ÓÓË3í`(ïèùØNÒÏÇeÚé=ôœvñprîîÝïe¼ÕÌç´ ¦#|®Ôgú9íÑî9$«7S˜ÖZ=ÍsÚi‡óL; ú¦ßPù N;쵎»²G}Úk¡î~Ø/ÞŠõÓê-‘Ûé¯}LL,˴ù”¿¬ž§Jæ|ÞêÁCÓDÓ…±¸É=6÷xúpòŽæïh¼Um´vÛLZCžy}-Õgnîñ_Ó¿A<ä¦Ñ«7[˜¶Z-Ì=~Ó>²ÆM®eœÂ¨NŠ-6õàÃ~Ͱñ̨üÚÌó·cÚkÑ”:7¼|Ó»}&ÕfÚ`ÚàB¦}´VÓÔ´Ø’tѤ.Ø´_ÇÎçäî'X{=ýµiƒi€0íòŒe’t™¤ÞNŸvaî´1gèÛfÚ`Úà6L[’$¦]3íýCØ)ÕfÚÓ€iK’Ä´g˜ön0øÞ™ƒäÉÑãg›yœiƒi¦-IÒ˜ö-]û˜ö}·a hЦ-I’Ä´qGßmîÆ´Ëï¯:dž'J/ŸûôùÛ¦æ:ÛAE™ŸåÎ8i™¹Ê˜6Óǘv‹5ݦi¿>oßrüçLJӾꂦ}®C¸¤i_« Ð~¦í›€ãA8£iï”cmk»ˆÖæÖ¿0ió¹[çÇê1xÒþJÃ_~yÚ.zúþ#zñÒHiÖ~Øï+°¾•W4e÷5)áúÇ«ŸuoW~>?~yû5²ÓÎW“¯eîËÔí(·÷ÜA¥‹=”0:Þxå;öò%©þI%ŽW&ŸRÍÄ-$Óf‚Mâ¦8¿bû-VB>4'n–L›iƒã‰wdÚ½ lîø?þþØî­ãíû‡Nä»› ØÃÃN‚­ÖеSŽõ惮?²u°À—ZL#³¯¸„ÃŽ6{Ø—d÷a¤¦É>áõŽùv”ß{â rÅîë!Q¥ñó™Ÿàr%©šv*^É|ÚjfÔK·™ÄH„ã+v¼ßz%¤Bsòfy{¦ óKq<ˆyÓîûÖ¦Ÿ£Ž¸¤kUe,™Õty¦W¶IüR%\ ÏÖm6nöÜyNçiQ¬zþaU«œ,pz¹;Å!Ì*I9^éF2³fª$üJöŠV±á¾j[Íp³4z¼E]‰÷eÚUg9Æ´ó~YŸaÚ…Æÿ6kê(Ûƒ*+„©W÷x€i7e˜*ISóâUm$2íƒ*¶Z ÉNÞ,™6x‹ºÞ“iÿX=Τä494·©ó0±Õ´Ûð95ô=+uA·í‘•îz¿ÇÙ;mÉ0]u­Sßíc×ÉiL;ÚËI*¶^ ©NÞ,™6x‹ºÞ‘i‡›fÇœ'¶J>s”M<¸{˜i'KØÏÚµPöa´ëúCÎá„UÅžüÊA¥ô¬R¥ãÇÝ“™Ÿàr%IfÒMî•W2Ÿ¶šÙ0?è9íƒ*v¼ßÖJ(=§}‚fÉ´‘¾^›Åã‰wiÚá Ò£Ù§«#ɣѶûÙ˜ƒÉ¨ƒq¶¥žÃ¹%œ®<3Ü2q÷0½öã—ÕÖ_þþ–9ÒÔAÕîÍTéhãÉ´çÏ=^>„BI§×µ—W6µš ÒÁsV±á~«•ÌáäÍ’iƒisÅú yòC¸×¿ûÜÅq1m¦Íñ 4›öà»]âöEÍ›%ýçýk®;ž¾ÿèsø±zÜ-ù¸h·0Û9¯Ðïîa"´ýN7{L¬å¾‹{³þ‡ÿúßoÓ­²F”,ÕÇ—¿Ç9Ä+nÞ¯ì}r ñj¹Ú((Ñ9œ¯«x§“øVk;WSÉ\LÄ ¸éêmlõó¯‘N•Ÿn< ­htB1íû¾"\šisO]h½d'›|v›ìŠ´ÛiXŒ@?Ò;ê?ïÛÖ©˜ÈmâEá&ËnÞ¯Yé$­–­B€j¨Ó:Ÿì4ßq>¥’Ũ†õÂu’>ÀÊ!Ìncqå'Ddv<¡˜6ÓfÚO¸]Óînñ£Ïy‘Ûô¿Å ±^ضy]EÊ ³;Ú8á÷µzÙÔ¡åV.Lõ¨ û- GÏx{€6ý¢‘çë'O(¦Í´™6ÇîԴÕ¿¬ž§3BO¦J~}Ο5÷xºù%ç…·*ªé4·ä~ózVÚ|ÖkÃÚk£6æyo‰É™Ãà “ñ k»C}ÀvßÎZ'©ÜN ^/XSK=G°¯ü¸ñÔžÑ0÷8Ó^®i?`¢pÇQ kÚ'LkýHM3¾¸”™bZ’×P—tB1m¦-¾Ëª+QPW€{6íxŠ©…ÛËž}•¤[9¡˜6Ó_¦- \Ê´%Iz‰i3mñeÚ¢Ó–$‰i3mÞ²ìøŠÂ­G¦-IÓfÚi‹L[’$¦Í´™6Ǧ ¸9Ó¾ê a¯ÏÃ{¤ndBµšV-ù6©ê&ÕÉ®GSv5¿Òl™áXp_>ÿöéó·Eµ[¦Í´Å—i‹ͦ}½ôú¼}Ûð-N~[¦=k«??>4¾Y*ý¾ëKUKËÛ³Ƕ=v§F{,î×´_·×ùúzò«àëþç¯ ¸"3mŽ' Lpo¦=éù캗î?ïßDÝñôýGŸÃÕãnÉ—@~º…ÙžêEì;HCkŠ—||ívåï(|]öfÛ«Ÿ¥ _=äñ _Þbµ«éôUÞQNbQÁP˜ÌîÖ’ÖožêÓŽ7ÊßiÞÁ¦ÝcÜBÒen9êI“F’ÝoÔ–rUT­ØDÆm2yŒ“p'Úvj«@°³±È5æ¸rÇ{G}ÚØøÙžiƒi3mÓN˜vÇ¿¾_üøûcÿyXaPÍÀf_Ÿ;•}ûþa†õçÎ2ýr{… ÛzùN6¹í6ÜlØõ8¥wÔÞÿ6Yø–C^ÿ>Ê6,sõHGUªÀh`v¹<%SZfP¬­_·Jmžp¹Æ®Ô´i÷‘ °REÕ£Næ0íÓNì7Ñ–ªUTîdNi®f›Ü~GÉší+Wc¹ÆœªüÄñÞfŸv÷yµêþš°zû'ì›Ìù­[e¿¤Ïgóÿ§O£'¾¾Œ2ùwð‹Ýâ8g¦ ¦Í´¨švßý;ýœt¡+/Ä»YضyËç‚åw´Ñ•ïkmKùyû!Ov/¯é¬q¼õòlº@³Îaa²¥±ƒGÏk Õ£.†¦Úl’YͨØÚ8ùö? ÑÜäf=_=ÞåšöZmƒ~Zý'aÚÛßï„·³Ýiõöç§Õk¼Nv“±¡wË·?íJ“Ëy³ÊvíáÓÓfÚ¦ý¯yê2 «p?™4ë|¦ßѦ[/pŒöÂÏ3íÚ‘– |pµŒGVgõ/ÙhxüyMûèÆÌá@ÓÎ{l{Ŧ«:uŒõ&W›U.‹gâo¿²ij»wÜDŸvñsøcçÅo¥ubÑŸt_Ç=Ü£œEfÚ`ÚLÀ´çuý~õž´¶Ž¸ôigWþúŒ¼m/üì>íö~ìTæVËþï5C.Tà~Èqªü§ïÓ>®1T yÓ.UlT€DŸvzb“k¨üt;i?oÉ´Ç;î“=õ]3íaTxv“j·?ûÍ´9ž(0mÀû6íÉìbÓ ´ÒÏiW|mzN;«éõO·Ž÷¾µ75˜4«ðœvÓˆñDžÌ´Çø–BN…ïDÏi'Ê\©¢†£Nä{À!øœ|N;]Es*6y앎šÜ~¨E²fšžÓnnÌ•ã½Ç>í¸/ºjÚ»MÖ;,÷i'rÖ§ ¦Í´8Ö´Ãù™¿¬ž§AæfaZéœ×ÿ¶¾Mêæ^Ié½tòw8K¿j~a±XÊÜã‰~ìýØïÉhÍ£ÇûÙÖžžÊù$–œÉvûøGŸ6Ǧ xŸ¦]žâ«nË6ÒLû¸Ga¯¼4{éåóoŸ>[T‘û>í{s€Á±—lâË´E€óš¶$Iï#1í³;Àı™6Ǧ `Ú’$1mpàyxûV A|™¶(À´%IbÚ7`Ú·…À1mQ€iK’Ä´—nÚú´9ž(0mÓ¾ÑYŽžõíèùÒƒ·.6ܲ¦‚Ë­9kf¸îUó«âWòMfG—GbÚWqÏisóŒXvÈõŠÝ–œU®ö†Þ–nÕöü ÕX¾ÞnÚÕÚ ›Ó~…j‘fFyÖsÚqy ËOÒ®˜ö¹M;Ù§½ýùiõºuénq/ä£—Ø ^v€1í«þóÿ¦ò´z{‡¦†ã°Hþ=«e…€i®dÚ‡šF÷!þ÷0Óþc|Ÿv6Óí¨Ý´÷ݳµ>çæòüù±“íSšvª35Ðùö,Ó.×^¡“¹P¤óšöœåL{IÏi—íz¢mÃq¿e×YÈmÿ½šöÿCvŽ7„yý㕟8¸´iw’÷(uT3£*œÔgû_:¦ ¸1ÓŽ{³ŸS#«gôií`ugztçõimÂ…aÛ‡çUZ²“i4ü»qôx®ö‚Áðé>íæ9ÕNcÚ¹òËÉ´o¹O»jé¡,®kû~Mû¤ƒÿë„@yzJt¤'ÿŽ3n¥üGíª_nØÿé¦Û4­£6í¼Î¶†#9b$õËt& Ó\Ç´ƒg}ÇÏit"ñôl?·ÖþÜà XmÏiv±šw´N8ðxû¹›^ë0Ó2,«fá9í}Gôqå²Ê>œ\Ë¿üœö ÓÎï(‚ø9ípÊ´ÂsÚ ó‡WYo|N;]žÜò£ÛÓ¾¶i¿…£ÆËÆ1Sδ/lÚ‡þO®dtäfF/dþ:SÌüw€°ðÛŸ1\ï*ßõ{YÓ.ôÏ!2kÄŽ/'Lp-ÓÆ÷NçOK.HÐgØ8÷ø¯éÄ`Ó1Æá[µº2¬÷rDßc0ûôê9˜Yºñ9íQ¿èìò³yÇ5™[˜Ï?3­w¢SDzJyGarµ7™Ï<åÏM£Ç«Ó°7ŽÏ•'^~p»zʽ(ÓžÞS§î±sÊŽ0gÚ§5ísþŸ¬†=izlÉÿpÕ|禽´0mÀÕL;ìç<øÝ×çxŸ¶$IL;}½ ž“«Âóe=«ýîû´[ÿÏ2ùq©¨{!ÿä|õL»ýð—¦ ¸ŽiÇs\îÖüåóoŸ>£(’Ä´Á´ü_]gæ_jšòúÌo¨O{ÞsÚ…îèöç´‹‡¿ˆ0mÀuL[’$‰i3í šö!ƒÿ“áŽ÷Óq +í–¦çÐnÉ’ým˜v<7Øë×Ð’7ËÇOHG‡^œ{¼!“Å…€i˜¶$IL›iß½i0ø?¹Î>Ÿ~êíòÜ»œƒ¾4æß-êf6/¾$|1¦=9”Øe7µ²*‡£ô>í†L^¦ `Ú’$1m¦}‹¦ã+ ·˜¶$IL›is<¦- Ü­iŸf¦´K¾f©{;T󻦮UN¡ÌeuÚ(–Û·¦Í´Å—i‹×0í6‘8ýÛ¿Î-0Õüßë;–OÊk™ö¬ý¶¼­]Ÿ6`Ú`ÚLÀ´{…ø±z Þòµ³ˆŸ_žöKÆ+ /ß&<ùþ#x[Ø`#©­+ëó}=$KÒÑÓ÷/_ˇ™µÄ(‡D”'GZŠÅÛ¯¿*¡LÖÞPýavǸ]øò?ë…ãcÙ,ï÷[ŒÔº}­ŽB[ž/[¦J“Í`rÕ ,¡%0m¦Íñ˜¶(pÓžjÒî¦gSÛ%oß?t+ rµ^gXág/HcÏLl5ÙWª/t½ÕÎ+ÖÇèN –ñÛti ’<––ÎÌCL»×¡¯~üý±ÿ<¬*Éësg›Û:é&«7ßùåEyš}Ío3,Å¢!”ÉíDiKÍ :ŠjPИ6ÓæxL[¸ˆiÿJöõK¾͖>Û}?[´Õ4çTnk¯ØÚÂFºž;sè,·y}¿›N¿t§ëÜ1ÆÇšvX¶ÆZ]>Þc-(Óí|™ Çx@,ªõV^8t˜·ü}$®Š–å³";¯Ì:S®Ñ˜6ÓæxL[Ô`1¦ýGøbÎÔPÛÜÈêh«zÿÛhÚ•ÒÖ9×z>=^öºz­NFz7 ®oe9±8Ò´ƒnÛ|·p[£:¸„GýÁ¥”«·¦Í´9Óµ¸”ieÙ>íØ‚^âlßrmªLÜ´õ93¼}¿£§|ÛŽå*¦]¯ÕT÷æaSyµ˜ö±8Ú´;ÁžôH7¶º+›v-(Wo L›is<¦- jp!ÓN?§=ºwß?šœªjô´íÞŽ[µ=Ü»›½lÿèéCFƒ“cq3û-švêXJе]¹›†ê´¦.ÉPié§sÛ' Ÿ>§=*jú)å\,ŽzN»¬ß[ÇþžëÐ.ÕÏäyìÜòÜŒk Ïi§ÆiŸ+AY@K`ÚL›ã1mQàB¦ž{|ì$ÃÎaßãŽÇ/«Ac6 3??Lz,ëVGf[îG-ï7a›ÞcòX*Ãé×¥:ÃèñdIúòfœNfáß©¹ÇËsqb1#”©¹ÇksÚ=¾üýíkõVGM1uŠ<«ùœ¶ðí¹åÖœUž>‹h~ì2ÓfÚi‹'3íü›—%9ç°â“ï…+ð`Ã?ÞÕ%¦Í´™6˜¶(6íý{ƒ‚~ãÍk™7<}ÿѯöcõ˜·PãV›û·7mV>?~y²MiÒ§,O×õZ}Víxs*^Ȼڶû½´B=Ÿø·ÉÂäv×ÈôX†<‡z®Vf²º²K†:Œrˆ‹t¾–“k­¥Ró…£NÓÆ}‚è3mQ dÚ ±ì}lcÝò·ï¾þÙÛÈói}mØj½dç$냟 +ÝÎÁÂDyë¤M–¼¶÷RžÕ>äÆ|«bòÛA;GÇò°ÙzyT] ÁÝéõyµ æ‡Ÿléb-§OM­¥¸¤rÔÉCcÚ`Ú`ÚÎ#ÓNtáîu.èQ¬Žë.oÕ›ÞZ]¾>wÚ³ùüç_3L;™ó¶ßršÏ¬Ñã9}mɆi—ó‰~ÛnÚ-åÙäßP™W×1Oœ¦åÄØÖZ 5ßÞH˜6˜6˜¶óÀ´ç¨TH£/¥¶ê~ÿ;Ë´3åé—W|û0Ó.çß®¾ÕrN~;Ë´G5“[¡­2OeÚq‘ÎÚrÒØÖZr5Ï´™6CàxL[8ƒi÷¡³Ü5³Õ´Oò¹6b9ݧ/Ou(ïÁ¦]È–i·”³q({ô¸uÊûÝlŸö骫ÐE?¯OûЖ“¨À™­%®y¦Í´ÇcÚ¢ÀA¦?œ|”wŽŒ¥·ÚÎ;µn¶ÜE\}Nû@Ó.ï°B7“Ö\ÓÌö4¦&¹»ðóä9ípÊ´ÂsÚ £¾3O,çL;U¤³¶œTõÎk-©¿qTŽšiƒiƒi;ø'~ŸöÆ[*=ukÙ˜=À8¹Õtåš—fúN§9ƒ„K¨ÇÛå¶.[bHsåOCæY/çý6]˜Ôî‚9ÆWÏÁÛÈÓqÜÂ,Ü9ÏŒŠTŸïíØ–“¨Þ¶Ö’o?å£~Ϧ˰LÓÆÕ#¨boý<Ü¿ißez73BKÒuL;¼¦xöµÞ¶½Äe T··~˜öm¤wÓÍ(IL›!0m¦ `Ú’$1m0mç€iK’Ä´ ¦ €iK’Ä´¦-IÓ˜¶$IL`Ú’$IL`Ú’$1m€iK’Ä´€»1m˜ Óšø7Ì„iLÓ˜6¦ À;áÿkök¨ÍIEND®B`‚ addFilter(Filter) Image 1110.8240579870107 412.38305701571016 Volume 746.3823937606093 414.32909259072375 TreeNode 625.8940609153292 195.78347904495376 File 497.8940609153292 296.78347904495376 dest Symlink 620.8940609153292 296.78347904495376 Directory 754.8940609153292 301.78347904495376 «interface» FileSource 567.7128002861876 674.7337520453865 FilterFileSource 404.71280028618764 774.7337520453866 «interface» Filter 254.78439895092293 770.0234642237775 LocalFileSource 556.36965453568 774.2484706711477 PreviousImageSource 709.0702627569951 767.6450499937722 ExternalAppFilter 110.5229299160843 923.5005293319075 EncryptionFilter 239.49694258624206 916.3652866418913 CompressionFilter 371.4989323515441 917.5544937568939 SplittedFile 287.78812183065844 413.84407602094683 Special 386.0027189390628 302.28361365806154 DataSource 731.4111571155465 901.4839567978983 «interface» BurnSource 1335.6119487863941 398.7730655710078 ... 1477.8641028983907 783.298732945339 PVDWriter 1198.964607961779 781.9555871948312 WriterState 1336.8762962072105 661.712946507712 Libburn 1330.0 370.0 Ecma119Image 1095.4640655004926 540.6103494032706 Ecma119Source 1326.9200188086058 535.6052987693873 Ecma119Node 881.981844994835 577.6636135370297 DirectoryInfoWriter 1316.10674358551 783.9555871948312 LocalFile 396.0059692959062 407.89804044593336 FilteredFile 505.41302387615644 412.6548689059442 PrevImgFile 617.1984926864122 410.2764546759387 Ecma119File 743.3186217548513 674.795410727119 Ecma119Symlink 874.2922359743702 669.9419255760395 Ecma119Dir 1037.9535936976897 672.3203398060449 FileSourceRegistry 550.129075763134 551.543289325507 Base object for all interaction with user. Represents a context for image creation and manipulation. 1042.275395468971 308.29855659733465 Registry to ensure the same file is only written once to the image 271.5290039756343 521.8448045156721 The context data for image burn sources, contains references to the tree, creation options... 770.7463914933367 497.80317395532944 A filter to be applied to file contents. A single filter can be used to several files. 151.32085117392114 668.9230150024738 1 * children 1 root [create] TransformStream 374.71280028618764 246.7337520453866 get_size() read() open() close() is_repeatable() «interface» Stream 576.3280239753375 43.34897573453627 FileStream 741.9465965652432 246.8166228690261 CompressionFilter EncryptionFilter ExtAppFilter filter(in, out) «interface» Filter Filters 270.0 480.0 A Stream to read data from an abstract file represented by a SourceFile 781.6101730552666 137.2161620284267 A stream to get data from an arbitrary file descritor. size must be know in advance. 580.8730162779191 392.3137084989848 fd : int size : off_t FdStream 565.61818228198 253.24264068711926 281.2426406871193 620.6274169979695 429.51546936508925 624.9910026589843 568.2426406871186 624.6274169979695 A Filter do a tranformation on a stream of data. The main difference with TransformSources is that a Filter can be applied to several sources. NOTES: - filter() method still to define - A filter_changes_size() method can be useful 724.6274169979696 510.3015151901651 FilteredStream 439.0 357.0 size : off_t lba: off_t CutOutStream 321.0 358.0 This can be implemented as a Filter, but it has no sense to have the same cut out filter to several sources, so this is a better place. 67.0 276.0 A stream that applies some transformation to the contents of another stream. 122.0 183.0 437.57046683437824 509.23933115391503 «interface» SourceFile 920.6530291048848 248.90158697766475 1 ‰PNG  IHDR»#dJ®T…IDATxÚíÝKŽä¶Þ°ù\RÐÃ9ëœ×äì û«IMÚ¹¨t¯çí±ßa%<úÚ»8 ×ÙGu\t¡Ä«âªˆø= ì•DQü3¥' òé'àÁxÂ4nðˆÆ¬ÔÆ `ê c°@u€1Xàuë꯵0fccÀ˜Œ™1`ÌÆÌ˜0fcfÌ3€13fŒp³Æüþº]ïõýäåÛg|¦Ì3Æ ¸”1ŸÉªÏ&âŒc\Ö˜Cµí^¿½½ìú†_Þ>~†}Åt»Œ[ú|¶ÿÿ׿žB^¿O2™xtðûÍqÎŒc\Ò˜7Š:˜ðËÛ¿Ƽû÷½¸vÖ:ï0Þ½y{÷É25ínûîݾ4¹œ·»ìö^0fŒp>cutÕDsñuø¶óÛÒ>±°Ïº“ãçIΉ"3fŒpcžúèKNÚðl4E͘‡ÑÙCª9>6š1`ÌÀ˜Û8MsÜ7\5æý!›–û˜9ëcÀ˜4æŸ-ã˜ã׃ïÎý·Ù˜ÓýıG9Ç €1.lÌ ƒnèWÇTÌ~è×<*£ÿUáËK9ŸÄ–“ÔcÀ˜€1ƒ1`ÌÆÌ˜0fcfÌ3€13fŒÀ˜3Æ `ÌŒcÜ1‡Ëî¸cÀ˜÷j̳µA6osKU3fŒðˆÆœ4ã`µÙB$Ûÿ‡ þÅ«“ÌV ì;±»C¯ßŸÍ˜0f`Ì­Êùkr­¾™¿¼½ïÜwæÛ‡ª½{78öæT©µ3Æ X£1ÿL/@]^ÝzxÛ™óÇÏœm·­¶Í˜0fÀšyGßÜéo‹1Gžþ†1`Ì€û2æ²ÑÎÞ~äGclŒ[3Æ ¸c~ÆTì%¶{;h<ÜÔ8äéP Æ €1î˜óÓ1Û_¿§71ú¹Ûôòò’<1`Ì€[4æÇ®+Æ ÀM33Æ `ÌŒc0fÆ €13cÀ˜Œ™1`ÌÆÌ˜0fcfÌ3€13fŒÀ˜3c3€1ƒ1`Ì€ÞÑcÀ˜àY%´×cঠŒŒc0fÆ €13cÀ˜Œ™1`ÌÆÌ˜0fcfÌ3€13fŒÀ˜3Æ X3o/OO¯ïŒ™1`Ì€ˆ÷×§§—··WÆÌ˜0f@Ñ›3cÀ˜Œ™1`ÌÆÌ˜0fcfÌ3€13fŒÀ˜3Æ X¿+‡´z3cfÌ3€13fŒp¨?a Œc€{–æ¤7ëc^T‡Œ€›&<œ73fÆ €1JÞ|)c^<‰ÇÀ÷_þõ¯eÌ3àho>Ô˜7àCNs¡³1fŒÀ9­ ·Ë4¤o/Û/oo¥vûI놙ìò;Nv72;v–Ãìs-_ƒ33fŒÀ¡nýÓÎTt_Þ>íuóº{5zkÙ`ãMílKbzÌ ”™1`Ìóc¹r*‚£¸ŠºÙØiô´«¸ °[ßžþs´˜kÓ)®¯ÌŒcÀ˜Ë•S ­´<Ž¢ê¯ÝQ™QñÛê)3Æ €1ã²®ÜlÌaÇóBûšß§£¢Çî埭§`Ì3ÆŒk„)ÇD]1fnšø–‚1`ÌøD1`ÌøD1`Ìøn,‚·þû?Æ €1`Ì"xFî`Ž9Æ €1`Ì"ø3Xž$\Âo·&I¿jÉǸó°jÉtç~ó¸ï]¬c˜Õp‹0fŒõK`OÀž+ñàËÑZØ»SYÜÁZÙŒyY»úÏKÒm¥FiöØÀ˜<‚¡Ì¯g†;¼ÝñЉœÜ¹šcfÌ’´.cny®yì`ÌŒ¹Ýw‡áÀ Æ Æ,ݶ1Wm{3c^ÔÇ‹/cc–nÞ˜ËO7=ŒùÑ#˜Çœúá^J™“Æl3c–¤3æÂÎccÁÌ\쓹2‘…QæÊ`Ì’t{Æœ{Æyì`Ì"sÅ53c–¤›3æäMÒccÁsóOkþ1濽>}~ÿñgêŸ~{HšÙíþR¡NíÓžþø|5|ÚK^8»\užf=ŒYÁ˜ÏeÌß>mÇõ|û=©Ë¯üyegºŠ¥oÌ‹Š}%=c,Ž¿¢†3¾%‚"Ș/bÌ[-ÞxIÊNâÛ-Agó~‡¿¿¾Œ[†>éàÀn‡§ÑË÷v½ª;)ïsN ú4ÃxK\抴Kï_úáÿdu-½Ï,ø™3¶Õj²Ú*°‚~{âóRCàžž¿~ U4l|úüýŸ_+W”¬ RóòÆþÕ»ià["Æ|cވΗßö"˜­­Ù|zûûÏ´Fï--ЦoŸºLúl·û ;ls °—§çÏ¿<"U÷M9÷޵íߟ"¶ÏŠ´Ûs°·'\-½OöƒŠJ±±V—¼´s!Øo_zÔPí›ícyâW™+ÊÔÆ¼¶õ1‡ønšø–‚1ŸÁ˜·½z½oÅ+è2œ eugÖÓ9æVî´Þ¾÷l;ª|ö¤f-(R°gvç–}ÂkÌ÷‚7^cîŸU`µx‡5~AñgbOcþ…êZbÌû¿}7M|Kgœrn ¿ü{\cº]û¾Ìôwúû=÷Ò\q»ÔÏ'Û‹ºvzc>¨H 2_”aþ”‡ss, AûQ™èšP9ÿjÎŒcƪŒÙìrkÌóy0¢‘©Kû&‡ÇÜϾcû˜—i©1ׯ1µÃÅŒyQêG…½ìQõæ¥ÌƒçeŘ0f,à°TIrE’Ùâ&á&ÃÆYsñ¶‚ÉÃ󴃹ÒÍ<écÎŽ O¼'øq 1–Ö0Žù "M¯=7Ž9½OöS'šÆ1OvÈŒc>¨[Œ¶rÔô猳d/ö RÿqÜ3ÆŒz#¬tZ@{n¿ãÒÙ9K¶JöCólHnh<“!Ë©ÛY²Cu‡©$ÆC‚©Þ¾ 3!,T0œ±q®ŒåEšÎª‘Ÿ+#·O2ðØÉGýææÊXVm!h9*˜#”2Zo¡Tê?¬´ú\ŒcÁœÌ[’Ë_ò< ó°ŽvòØVfÆ|`³$ÝþZ€ŒcÁƒùç0,#èMaÌŒY’3ÏEÆ|W‡T4óÏi_sœcf̒Ęx.âŽ"¸pslÌ Êl3c–$Æ €1ã–#¸h®ŒÄL“­©Ÿþ™+ƒ1KcÀ˜!‚ ÌÇ̘%‰1à[Á Öü{\cî&ù ækX6âSËz%';×lá˜ó×va½7cÀ˜EPó™ù’š¸^ƒ1‡ËÝ­¡63ÏE• ‚ŒYcž-Š‘_Û¹²VÈvͶü û}‚å*>ÂíåcÃu. Ešdµ[/±ÞGúB†•MÒë•$ ¯Ø/Mצ+O‹¡6®QÒTç™*J.˜ÒnÆ €oAóÃsÙÉvÇõ¥Sëiw’7Hprÿý>ñ³íõcçê–=ÅLg§Ç&/d³qÐÙôšØ³Â÷+BÏsó鎮+¹ övã|µÅÖu°£z‹W5oªÉÂ͘ð-ˆ cfÌs… —Ñn´Ü¾On{òØhCõÙ®Ódæ¶¶^àîí|!ñ|ìâRÛÉLÚ¯+­Â¹*ªc®†›1à[X}oq&‹\™›®eÑóyŒù·é:‘U¡LíßjÌ™sõÛ;o®ž"«›Í«\`÷öýK/—…Þ÷êû6Çî¥ù”ÆÜp±‡…›1`ÌÁÓ ñ Œyé„wŒù<ÆÜw©6þž,¹ÿ‚>æü¹Æ±µSdû˜Øpv€r˜Ï^”gÕñoû{ 7æèÒZ.ö°p3f| «àt¡‘Äâ$‘pv ž KdÇKœ¼¾Yí× ìˆw¶uûFk¦Ìw(œ"áÔ¹–/ªÂ˜ÏbÌáÀÖ¶!‰ý»žË©h6söÉ«]6™y8¸4ŽyØÞc?|Ëu0÷§NŽcŽû˜³ã˜wùt?ÈK÷—Æ1/2æj¸3ÆŒÛ1æ‡ÜYj ÁãúØ“¥µçþ\ÚyÈm\v{ºè`z‡§‰Gù¦¶²p7c>“1‡³I<µM¾6ß:‘ÅSqøoêØa.‹?+§ˆ³ÝÎqQ¼`^䦹2"ÅŠ~À7ŸâãùëÛÌÅs6â¹2Æ›³ä§ONMpQª¢bg)ÜŒcÆíó(¤EcwÙ2ølμÃüã.q÷܃—Ï•+c>ƒ1¯p^äJ·3¡„5ÿ€1?š1ÿFDäE2–β˜&wžmÏåÐ~ŠÂ¹3c¾•R2ÌcÀ˜±cþYëkÎuî–­zè÷wO–ì ÎíŽNö1·Œ1ߦ1KcÀ˜q Æœ4Ó}ÿnbìpfçÞ}'ÔÃ1ɦ? ,ŒcΕÞ8fÆ,IŒcÆBc®Ï”‘ÔÌ`DÐÑ;ÌT1™+#Þ9Øôòö6ü o»1»Ã÷Ù\ÉŸæÎe® Æ,IŒcÆé" nc–¤µó ™(cf6ŒŒ™13fÆ,I×1æ[‘QÆÌlΡËCëbÌÚ]cfÌ’T2æ›ðQÆŒóµ(­ ÀoSŒY’ÖfÌë—Nƒ³6' cfÌ’T7æ•K¡cÜ63c–¤ëóšï°ný8w[ÒÆ@—Á˜5濽>}~ÿñç ÐJŠ:ㆪîéÏ»©Û.UÕömÍÓìîÆ €1¯Ô˜?¾}ÚlùöûJ5ó:ŒùÇïoÏ#É{I׸íJüþϯ¯ý뱇õï¯/ã–MnOó~Çn‡§ÑË÷ö9¿þ±Ïª=¦ÜËŸbvÔìŠjåyzþú znû¬9yíAí%Jÿk\ò–ºJžº^»-¹Úˆ+¹Ð %ïϵ;¤v!å8Î?;NWÿ¸Ÿ‡ˆ1À-~&T ŒùâÆ¼1˜/¿m^¼‰l5ÙÃ[Ëà‘»îê.“>Ûí>Ã[›Ô'0¶çÏ¿<ö–t¾ÔÃÜÒ§HIj¡<ûÏ ŸÝ>?KúÚ ý£©ÝDa¯zÛŽÿ° źJœº¥˵Wr¡%J>ù˜Q¼jƒ×•Ó%Rf]{¸-]v«dÌ7æmßä§·¿{G Ü7켜˜taŒD˜[R'¢îÙvTËE#Œs;·o_¤®=®½ê¶ZWíÕ^–øtÇyþZªqIÔÞò Ws.ol0æUI³Ç´+@{fÌë3æi—d¾›9øê¼dÌÃå¹ïës_¯·Ipåuþ™+Êî|ˆ1W®=íÍ-uÛ$¦©S7ó²è4´„jíGÆ hWó¥yüw3Lk^Ò ‰Î÷R·[à"c®žbQyïcÎ+Zy@¹n›.¿h‡•5G§±%lÌí…aÌ€v€1_ȘSc^ÓÝÌsOÊ~Ÿ>¨èZ0ðã@cN19n!YΦò½³³qÌéíùqÌsvxn¥®J§.X‚1WKÞÚ']Œc}3c´+hÆ`Ì'4æpðëÌÞÂnæà[òÑɶÓdûq7*3Ì5ñö%˜6¡Ñ˜sg _G§ØnŽÊÍP1+Ï0QÃlNŒÜö”5F# ¢ÚK ‡f½ÈLQ««DµW+°\ù±‰QÕ’7^H9Ž-se0f Ë`Ì'3f©eÒß_ äÞ×dÌ0fÆÌ˜%Æ hW3c–$Æ hWÐtÁ˜³$1f@»‚v ÆÌ˜%‰1{B@»4ZÆÌ˜%‰1{HðÆü8¿‡ë&;;ÛÅúe!cfÌà6ȘWmÌ‹tmád·«0Ë#Ëy¦Ë¼@MqÆ hW¸Í¶ª¹2æ•szˆÌ³š®ë±_zmºìvF×ârëP„™'öIæS;jX#;‘\rñ‘ÔÎÚ&Ãåg뤘m{MNöŒŠ\Úný‘Ü)ÆÅ¨ç—–»¢>çíª%}áKë 2ffÆ ÐeÜ1§›/;\êíÀ Vx×jN­¿\‚;³Öq”ù´¨‰Uªu»Öy«bXüo¶ÎvT' ‹9·Õd¸gµG9wŠBíå®hÿz»äç_žû×wÑ͘Á˜€1/4æª\Vw—Ý®æPȳ¾1u¢¦£ò£Õëã¼–æš,&¾´B4Ûô}îY<Š130f`Ì ž7Œ7( í8‰1§NÔ¢¼…AKyR†“síóåé.­dÌ™05^cf6àÖÆ|\sQ¤âïcn+^òB’ƒ sا{®>æ†ÏqjŽ’Î¼ùŠ³Ç¸ïáñŒ¹ÿª½isê{ùàõ8@6?V¸msÒ2Ç¢&NÔpTɘ—Žc²­Ij&Ûå5¹Ø˜³ƒ(2™·^cöä#4N<’1ws,4Ì•1Û9ïOA,ÎGÑîž`Ì<ù¸oÐe0fÀÝГÚ0fÌЮ0f`]Obcfí â«™1n”jÚÄWcÜ4U ÷ 7.0fà2·NC5fwÆ Æ ÀÑsñ&y6–¢ÿåß0¦b¿µ{;h<Ï05y–=cvWaÌ`ÌÜó­ó0j¢Ÿºb·Cn:æq{zJÄèçnS7Gt cvWaÌ`ÌccÖ®´ ÑgÌ"Ș¸3z.jWچ苾2f‹Ú•¶!ú¢/‚Œ€;£»ªv¥mˆ¾è‹ càÎ讪]i¢/ú"(‚B¸3º«jWÚcý¿Ö&d̪wÆÙZhñâÃ'e<ÛióÏÍ7ÆŠ8gZmôã›Oav¿ÛøÃ_yô÷5uÒ¿ß+†àøöØòl;ËÍå"§˜­M±y{ÆÛ%cfÌÚÆI¹ðZÞaø7ýØ›`.—4æƒÛcp2cî·lÿ?|vO®ñúÖõ3} a×CtøAªÓ9yÞ~ ‹ý9ºÌ÷§+¬Ÿf=Éö}RÄÙÒÊékÌ€13fÆ|*ã)ôÎþ`×ø‡CѽùŸyJ÷öÜë1FñzC¹¨Ü~3€st{én;á-)x–Lnw…{Ù˜Óöýî_ú§Ðì¼ÁÛᎷÙ5èÝŸhùÓÍ/§;ç{XìÒ5 À˜3c>·1ǰ+üÿ¹èZúóˆÆÕQ¼·çŒy ÇîðIž©¨Û~ÖnÌÀró}Ì¥=kwÀø¦7ÿÄìßíœ|àUV-N,€\Ê'|;¹£/¿Æô­÷í0æ¸g·ùÏ-ùñz-ø·û—rpôS!h¹·W;ƒ“£8·£ÚÏÚùçþ[’'­Mš2æé@‹Ê]/Øyþ-ç^òÜú˜Ž iypF÷ú×Șõ1ëc¾XsæveøÖÇÜ‚>„ä¿ð|j–³¨ý0fIZ1¯Ê'Ž0æáËÈ’é—mgìc®~%×бô3cfÌ3æâ·ékùÿ¡èŸbs.'ïcNFí„ígåÆüã·ÔœVŸßü¹û§Ý‹Ð<’Ï—>Ýñå¼ð•®°`“¶1m'/Ãù/ꤷ¤ëóô[­E ›Vá–±nõ›ìö§ÃWlûmÝÛÙxÃôÙ§ßÎ-ºFÆœoWÝG¥äžö‡Ö¢ó}sü»Æ?ü›ˆþéæÊÈ… woß;S㘋Q;¬ýÜbs¬,kPÆ+–á¢bºäDk(ØÉÕyQ>4¼­Aš•ÑÿÌïå¥:ùeøXöÑõ³þ{ê¶çVnJÐqû¬¨ñW}Ó‘# ®‘1Çíª`º'”à[õiÆ|˜ñ$þ`×÷‡¿òèŸ~>æütÌéÉ÷ÇáomßO~¬rtû¹cþûkW­ß~ŸïöÇçIä<Ãßßž§ÿÚg5ÏmvЏTÉž¿~ 'Šó/(]ŸÃë]§Û×…S„WŸ«šmá¨ðzÇ~Üü¹véýKß¾þÈøb\½Õ%iùµ{ñýŸ_ë…Ÿ\r^s›¢9érc^ƒ4ûöœ¹] O¥Äƒj7}T{Çó°}÷b2T2qmÃ]Eô “Þ´1÷î²5žeßËo otÇ}nƒMr‹N øòÛhxýëoŸºü7š5ìv»Þ®6‡?þå¹î0ä0ä\`â\Õl Gåª4]›ƒÑîÔ9UQéê-Jj|Ècn*|xÉÕ q #c>¸c;¾]÷ÞäÎÈŠ.ÜǼÝé· ŒÊ¾ÙÝzs ÑÚc}|lcÎËÊ®7qâÄíß›'Õ§eØôö¤ÃÙ?½ýÝpàöE¸gtÔüÉ+MUÎ6wTÑÿÒG[Æ$4ƨ¯–qÌõ·Ùm6Ðg4æëÞžÜYÑe9柇ŽcJnÔ6ø–è‹ cN;ßtÂÁÖj̳žã§ìƒª”/0æÔ¹š²m;*U¥¥Ÿ`æ|1®ÞrŒr‡ÜÇÜRøVcnþŒ±pv¹õÌÓìÎÈŠ.kÌ3¯eÌŒÙ1‹àÅŒy ËVßG8Ö<:ÜjÌa÷jØÇÜØÏz¼1§ÎU϶ù¨y7módžhÔø¼zë1Êrˆ17¾¯d OfÌ‹úžÝÝU3cfÌî "(‚ç7æä8æA‰’mKÝÒÁoãqÌ'0æàåqÌíÆÜ~ÔT“×Vfvs\½c.²À˜‹…_`Ì©@ùw¿)Lœôxc¾Öo¥ÝòXÑyn»O§4æã§ŠÒ6ÜUD_ïÕ˜ƒ¯à3ßøGÓ&3!¼í´o7ïAèŒÌ„Ãô'•1Ì‘œ¥!>Wƒ1/8j{ù£&•™Ÿ+#]½ÅE‡T¦LÖj¹ð †”dݵ´"gOz¼1¯êçÒ`Eç0æÉ\_ÇÌ•ï¼pseð-ÑÁ1æ‡M«]©ä.Sæ‰ë®x•û¦;#+Ò®´ Ñ}dÌŒYºªfÌYÛ}ÑAd̒ĘÝU33³2fIbÌ`ÌÚccAÆ,IŒ¬H»Ò6DŸ1ƒ1Kc+Ò®´ ÑgÌX¡1·¯ºL°®U3BÀ˜ÁŠNЮ.»ÚÈ{·äèûìµ¶á®"ú"xÛ}Ìå¹rD×]ìYk¦º¼Ë~À8C=0fwU}ÌgóåÜŠƒWõxÆ Æ,‚Çô1«]dVé×øxùö{0·nǸ1³ÚH)Ÿq1”Œ ÅyÆË£ä2o9v¾òÜ,Û|Ùkf{Æéu›ž¿þûŸÿj-I¶êò×’»ü8jÓÕC¶kôg×=©žh¶C¤û¥–­ù%90f0æ¸]½«M— ÿaÚõ;¬rR^Ê$ÛŸü4ÏÿõûüŸÊjî*Œë6æô Ø¡Íìà ¸–ÇK®z[ y–Ïn•¸½º}|ûôÔ/‚]îVWcž,j*d˱ùÓ5”­­f¦çÚ¯z½ $™ª[t-Ý–tÔz ݪüç_žû×'J/ŠÞ°Äzpø¼žæÀ˜Á˜VÖüË(o°mãËÝ˅ݾÃJÙ[ãNd¡Ù]ÅAŘÖ²Þæ°íhL/ÈÜâ@éÌ£<Ã.Ûr>Ƕ\ì¢Ó5ÓŸwì`>®$õki ÇöE˜O\·K*ícnoŒŒyA» ô5禛]öª;³Èb“r̘ù–;ƒ2æùˆ…Ä0†„¨fÌqž“óf‡¤Ë“<öäÆœª™oŸ†nÔÝP£KRÍ!ޏlÇŸˆ1»3z.®ô‰;ûÁÝÌMã·É¡Œ™1»3ˆ c>\h‚þËøÀà ýó,Ïí©v÷–=­1çj¦{îv\Iª9¤/?*Ûñ'Šw`Ì‹ëyâŽ}Í¡›Nº—û-‡™+cæ[î "øpÆ<ýZEh‚asu¬p*ÏqXíRcN;Ïg¬„c¬jf›Ið»½%É\]%‡„1§ÊÖð!¡z¢Ô8æÝ‰º_Xvó‚3ó"cNZê1Ê|Ü8æ™»kî*Œk6ænj…æîÛa†ç¯o_¶NÌfÐ<*£i>ŠDžï_†4›˜_bÉ\ 5ì3¾Ö’ä?”rH^~:jõnõJQÓ³—lØ\o­åêyQŒŒ9ñ˿ًê\á?ÄŽÛ>WÆ"cÞm® wÆ,‚+7fI²‚ cvWåL"”õk.h˜Ý³2fIbÌ`Ì`Ì`Ì"Ș%‰1ƒ1kWÚ†è3fdÌ’tÇÆŒËðhÆŒ‡mzëÑ”F1KÒ•9ä¯[`ßùV×Í¡óýÃ_¸ñ;ƒ¸¬$‚ŒY’3cfÌŒ™1ƒ1‹à"cîf &;ÃR«KpÕëm¬„SÕÕúêœ1·ërì™1ó-¾%‚cÌe‘:FqV®¤ö©` ×»¨ )ü£s=ÞÃ8cfÌçFñ”^9b*ÊÁŠ$ƒ¸$ªøü¾]@.¹üÄSëj áâ¹ÓmßN—|Ûœ÷ùë¿ÿù¯z©º×Oãª{9 K#y-™5œÃ“&ª+Ú’æNQD1fÆ €1/FÅǶóxleÄmJŧë‹þ°¬^ª°$åqÌ©bdÆ1ï2ì~‚–ΰ4Žù$ÆÜ4Ž9ÀpFcNÕs¢ºßžÇœ-ü¡í1³11cp^cîæF˜ü€oÁ·áá±å¹2‚¯ÚCšŸ.ÙUÙRª`~†·/Óɧ«ÅH–¼Û¸)FÞ>S3c¤Š:=cm¸H½lÉÔ?%ê¿0|"­þQ=GãR‚¹2¢óíaû9¦2f6&F`ÌŽ2fim©½³Y23ƒ1fŒùáÒ£­DȘÙĈ1`̒ĘÙĈ1`̒ĘÙĈ1`̒ĘÙĈ1`̒ĘÁÆÄˆ1@³1û šÄ˜ÁÆÄˆ1`ÌŒYbÌžšlLŒ3ÔŒy¶¨Äó×x]âí ÁÃJ{‰å9R vHcfc¸ÜÍÍøÛ°Ô˜ŸÆ–7ê<.ݯ¦6hôt}æìÐC&’Ę3®Á_Aø'|0Z €æ>æÉâÏùe‡”W„îW®6¢CbÌŒŒ™1x,cÞ÷C?#.Æîc`†Ä˜3óm µ8…1ïF'wÃ-&ã>f–,1fÆ À}sÐ%œÇüÔ·hÇ,IŒ¹úGˆ‹àŽïœ®y˜ #9WF0“ÆÛ—a`FbfŒ÷/µÁ’Ę=ÝÕ3Îó)T=8³1“Z‰139Æ áÆ,IŒ™B°Ü˜%‰13fÆ @lÌ®¸î“SÏ8ëN=8Áý$|c&N4.p’•ÌÉÊäÔ3ÄcofÌžèÌ B€1ŒÙãœ6ǘƒ]„Ž09õ cH³¹z†€1ð WÏЀ1ã%O&Oqõ `ÌofrêÀ˜09õ -€[‡*ÀeQžRžßêÆ xJ©õ íc<«Ô†z0fà2úÂ`˜œz0fLN=C«À˜S<´ù¹å™­ž¡a`ÌoöÀVÏÐ00fØêÀ˜KÊÍãø “SÏÆ f&§ž¡…`ÌÀ-<Éž°õ¼òz†Oé300}H7ñ)E=«+áÀ˜[}˜y2fŠ`Ì ì„13fcqÈNÕÕ_=êyõŒUÝ^0fà~„*13fhÃ3<Þ<3ciÀ˜Ôã­ú„ódÌŒÀ˜&ǘ3Ü40f á!—|Îyø1fÆ cJ:?ÆÌ˜¡=`ÌÀaO¾÷×qÑâ—·òsx}_|øÇÛË‘`ÌŒÀ˜ã¼$ûÓÀNVÍݼ/+ï^Žƒ}ö9t¢;yÓ–Éì¡Ñ‰3c0fàÜvÒà¿Éé^Oõ%Ïs˜¼¯gò=]„øDýnÛÿ‡ª5l ì;±»C³fϘ3´jŒžy5aNÉnªë7ΡåÀ„VGþš<ÑL„_ÞÞwî;»‚IÇypààØ›S{Ã3c†† €1ÃoEÆüsÒ9œË9÷¶3çÅ…/ú`ÌŒÚ6Æ O»uó޾¸ÓßcŽ<;ü !cfÌÆ or‹Æ1º‡ÛÇ1mµ]Í?ò£16Æ­™13p"cŽ÷þÚîvûtq4þ·8WFC&ï¯ÃîûÒYÍ75y:Tƒ13f\ìN€1÷þœKN1Ž’x{M|HŽ‘˜:hC&ßsÓ1‡û¤71ú¹Ûôòò’<13ffŒð»¶É©gÆìf€1rLN=3f`ÌcfrŒ™1333c†» Æ x¶1fÆŒ3FPƒ3À˜™cfÌÐàÆ x°á¼Æ<[ž°<çÝ9óÏÍäǘŒ`ÌW6悟۞»<¿ÏW}Ù¼½Œ²3f`ÌcfÌ‹9aÉßÓ Á ½ÂÑ‚áÛÿÏzŽÃ³ô¹u˹¤WI­>Ó’rÕ›ðÀ¾;ZT’1ß럌; À˜ÆÌ˜ÏnÌÉîÞn™ñ÷w†ë—ÏEu¶OQç¬Û—¡ÊæKØ”ÿÔçÃÂïÞ Ž½9UjAsÆì€1+}ž¡³c.[rR^7o;kýhÝ'´ÔÉþ‰¥Å³ò_¤þÙQ×2f͈‡ 3 èôuµŽ>æªm/5ò=}pEÁ ùOCxƒÆüó?ÿ-I“H33À˜ïؘ?R#!–ù}Ø·ÚÇL¡¤G3fO0fà$Ƽњ ΖÐu\îξ~tcžùMžg˜'<KÑÿòoØi¿51„£1ÿYöŒY’nÀ˜I33p[}Ì¡—Mý²}cG;äF'2LŒNîóé§®ØmÏMÇœô{cþݦn&Žâ$ÓŒY’Öb̤ŒXhr£ Z3ñ >ÍY£ÚÎòçSËåùt•îgkþ­¿ž³$fÌnS`Ì@»ÉÅ‚›RÞ`Û°ÜÅÒnßÍîûÞÌ­q'²¸û>f0fIZ—1»S1&èkÎM7»LÆÓ†ã\Û-6)ÇŒŒY’®iÌnV`Ì@£ÉÍ~p7sÓømr¨cöºcþñÛëÓç÷²«³WΟw·‘›ªí\ \¬Ùô•öýŸ_»3.9µyšÁ˜ãLnìkÝtÒ½Üo9Ì\3nØ?¾}Úþ’ñÛïW0È+ž±1Ûòn‹Êv£ŸLªÅ>ëu%3?ÅݯÀ˜Ã9i©Ç(óqã˜SË+3fÆ|ZcþñûÛóF>ò Òõð |Oc/éþ¨¿¿¾Œ[6¹=Í»Q»žF/ߨçüúÇ>«þõܦÆ[W´?××ñ¢f«eòþ¥ÿRé ffg_iîBU—¼Ò¶Z*×s¿=ý¡¨rTß³;4ƒç¯AñêM¢’ÿÿö¿ÏB°ý×Ooÿ™­´Ys²Éµ³5êÁ˜‚ÉÍgÊh˜+#¹ÀrKgp<åE»1ï6Wc>³1oLèËo{GLxXlÒYÙ Í P»îê.“>Ûí>Ã[I í°7ªçÏ¿<vUéPÜ”s/^Û®ñý)æ²ÛùÖ®ï¼;0]°Œcmvär§Î}™ÓÔF¼CîB¢ªK\×ÒZÊÕó~ûaG Ūz³Û¬ ×Õÿ¬á…•Ÿª´ù‘]b̤Œ¸-“ eýš š0æÇ2æmÿ\ߟ·u²QàÂþÚ‰I' &έüeú¼±í¨òÙ']æ3ÁJ,;<7عéBšÇø–ª.ç…͵T-ÞaG¥£\èñÍŸ%—ØðêŸÐf§®5¹c&Í`Ì“cÌŒ¹hÌÓn¹|7s0 ¢}!ñ8Šèëõ& >zçdÁe7î@-dïp¬17_ø¢zn?ªPÔFc®æ|-è'®s¦É1f0f€13fÆ|c€ÄÝÌãhך…ý»É^êÃ\ðÆœ,ØBc®_Hj‡‹ó¢zn?jÖ½½´¹%ÿA”g½û­}Ìþ1ƒ1LŽ13æcNûLw3Ï9û=ø8¨4ùC®ªvgEs©†qÌÝ%¤Ç1g²ÍUÂdsúBR'šÆ1ÏúY“㘪¥–7G͆SgÇ1gšDKþÃH˜o©Èíã˜33À˜³z>±1'ûóâÑÌÁ·Þ£—l§MÈlf™+˜fáíËtbÝÆñÛæÊèv˜Ì•‘-Xn(sj®ŒèB¶ÛÃL’;´sv®ŒeµÔVÏíGESv¤‹WhÕüÇ)S ²ÚûžŒ,cc3cf̧éc¾û4ô†JëO gº8ÉZ€ŒŒ(˜\r1‘S®*8[a1ƒ1Ÿw¹Cz¥kG-=B†1ƒ1wlÌk_Ø13fIzðĘÁ˜%Æ<̃®¯·]£o¶¹}™’ñí÷Ä &…3cf̒ĘÁ˜Õs «ãZØï¯ƒ=Ë[z‰[ [3cf̒ĘÁ˜5÷1ÿ ;–­€ý3/ÇŒŒY’33p‡Æ<ÓÜÆ5«3c¾c^¸ÈpÛϹÎüÛ» œâ*Å»Ìuö,+ccŽ0æÙàåýöp(†>fÆÌ˜Î¥Nv+7æÇŽ2ccÚ9èBÇ1&}‰qÌ¡µ3fÆ|icV˜,Õ1.á8Pb ‹én{gÚ.í6.T±ûo¼òEj-ŒþÀ’uÍÊ0YÒ"¤x!3ÉëóÜ.ÑÒç“XìúGj)“äfòŸ¬@ÞR°ñ\»™×&µÔ-˜¨ÏpmšíÎÁëÙè³Aâœç‹>ö¿‡v˜µŸïÿü×|eœÝyÿý¿¢Ã³—ÕL¹®33p~cæÊ½6˜@ãíu˜õ¢q®ŒEƼ?Ú\Œù:ÆüñíÓ`*ïÁuãÊÃú™åý& ïÕmвü m©å §BÙØ‹™ì× ÖÂH\Hbòæ][±ûüËsÿº°Êtc1†ü‡ó+ª4¬_PpRŸ}-«pÇõ9„u·Þø¯sž}Þˆržì9#Ø!Ý~¦W´ÙþËÿûkâðüåÌV½®ÔccÎcÌW¤}x4cfÌç7æÙBÙ£?Û'c?.ŠcR14mÌ e{R“’[8cþº`Ì ÅHWÈæÀ| G¾m|w¾by±ê†×½šoíóKg™±‰ÖTþÜÒÒ~ªÛõO#µºbÌ`ÌÀÝó›œz¾5c.nr„C~ÄÂeŒ¹Z†ÄÛÔ…$ Üî£-UqX Ç+fÌÝ‹ø¿Gs¼˜b¾òÃNî` FþðJÿ}±®33À˜³z¾†1÷]¡IÕ¿Uv»€1ÏË0{=A[¾cŒ¹PŒÂâÏ“>概}„ÝÌKJ8ï]þRûQ5æ°[=µD&©^ùzÿ}ÜÇ|–2f0f a hçcÆêùdÆ<ý¦>=޹`«éqÌ9cÅ13ŽùXcNŒ:(_H«1ïu·ÿñYÁ˜§²“Îc®lÊœ—ãy}ö¿M‡ÿ>%*g‰1˜iaû™ ¡N^pýÜ8fÆ Æ èc¾³¾Ï¿VCxEëäŠÆÌB0+#˜á)H0êKr®Œd§ãVÚjseäW¤¾ OuX¦¿»Ÿ_È"cOº¾Ä¹Æª/07WÆt‡Ö‚õÓeü?ÿ߯å!"O¥ßœÐ7ÊfüxûÎË‘n?ó“F‡ï§RI]NbfŒTk<¶ã™1ƒ1Œ™13æ6c–$kþ1f0f€13fÆÌ˜%‰1ƒ1ŒyÆ,úë­ ¶$IŒŒàLŒYô³$1f0f€31fÑg̒ĘÁ˜ÎĘEŸ1Kcc8c}Æ,IŒŒàLŒYô³$1f0f€31fÑ¿‘ÚpÅu7Á˜ÎĘEÿ–ø cc8c}Æ 0f0f€31fÑgÌcc8c}c8c}c8c}c8c}c8c}c3cfÌÆ <’âa×ÁbÌÆ ,ÃÌ£6G)c0f€13fÆ `ÌcfÌŒÀ˜€130fcÆ `ÌÀ˜Œ3€1c€1`Ì0f`ÌÆ ŒÀ˜€130fcÆ `ÌÀ˜Œ3€1c€1`Ì0f`ÌÆ ŒÀ˜€130fcÆ `ÌÀ˜Œ3€1c€1`Ì0f`ÌÆ ŒÀ˜€130fcÆ `ÌÀ˜Œ3€1c€1`Ì0f`ÌÆ ŒÀ˜qsvr‹c€1ƒ0*mÆ v¨ ¾Æ v¢Ì_c;¹t™ÿêAmÆ v˜µI3Va'ï¯ã\¯ïÝ–Ù Æ Æ `ÌxhcÎiñºÔ™13f3VbÌQþ>vB»}¼½Ì¶ìé6ï6Ž=×'SmÆÌ˜`ÌX£1Ç}Ì1îÞŒ›w‚üòö‘Í“1ƒ13nؘ«ã˜CÞ󠯹aÛÞæ“+3cfÌ0f\˘õ1‡†KöÏ™ˆŸÐ›3c€1ã&Œy”‘:dÊIûš3c€1cÍÆ<RN)3cc0f<´1ïYŒ»#3’£2Î0ScfÌ0f°ƬMÀ˜ÁN33€1ƒ0f0fc;aÌÐ&Œì„1C›0f<°À}à–Àúóð°üp7À˜áƒGo‡¹×€»Æ Ïh‡Œîf3îÃnÖ`Ì}u…5óÏÿü·$IçNÒìf ÑWW`Ì’ô¸ÆÜr7v³€è«+0fIzhc®ÞݬÁ úgc`Ì’$ÍŒ¹üüò`g‚è3f0fIbÌ¥G˜8DŸ1ƒ1Kc.=Å<ØÀ™ úŒŒY’séAæÁ΄uFÿýu;áÏëûÉË·ÏøL™3fÆ|VcþñÛëÓç÷fþi`·Ï°sᨃÓ9ò<2ýñy¼ö œn…5°òÔ kÔjã<Í´Œ·ý¬úl"ΘóùùãÛ§Íö—o¿/—éS«óÚ²ºŒiÝ¢%¯¤ÌÅ(ïvÙkaÌ`̸¹>æîõÛÛËîcÿËÛǤ¯80àn—qKŸÏöÿÿúפûàõû$“‰Gÿ°ßç̘óeùÇïoÏ]ÈHC¼}*Êßÿùõ5ìî2œmÙò÷×—L_àû—þ¯âàtc÷vÆfº Ÿº?;6.@|ìdŸÝƸé-cÎ»ÚøüÞõzn.çÏáíëd±“Em:]áCÎò£ •9éÊ.³X½qHžb`¹º*a-µt»*×KUmÌá_=­cÆ:¢¿QÔÁ„_Þþ0æÝ¿ïŵ³Öy‡ñîýËÛ{¼Oö©iwÛwïö¥Éå¼Ýe·÷ð‚13æ3óÆW¾ü¶×‹„¦ÔŒ9îcþøö©Ë§Ï¹“’ç¯)­Ùì?xÉNqº¬6¯÷Û·]àû|f2Ü*NäL±zû' ÷™lsN)Î9”òçÏ¿<‚Þg˜>õÔns5,d¤‰‹ZT™Õê͵\ª«éÒÒºœnW-õ_®Šóð‡OkÀ˜±‚èŠ:ºj¢¹ø:|ÛùíGiŸB—öÄÊs9'ŠÌ˜óùŒyÛøéíïÞb©mÇ<Ž Ã–aáþ¹Cª_š'whÌ¡|Æ\&¹KÛ¾ /?ªŠ#O]½¨ÃŽªWæ¶Ïxòªg² d/|yuµ4­\»Z4¬è<£5fû´ŒWþÔGXrÒ†g£)jÆ<Œ¶ÈRÍùð±ÑŒ™1·óÐØwÅÍ»™—ö1O ;5Ò ,%‹$/¶ùvc.|8Ö˜‹ûyêós¹2ƒºR2æTh¿ðÓs9.ÅöY­ Æ Æ }Ì)µû†«Æ¼?dsÂrs"g}ÌŒùrÆ<6 ˜u3/6æý¨è?ÿ|p©1‡½žKû˜“Ç^Ƙ<õ™Œ¹Z™“áε<Óm ùÂÏjÌõb4WccÆ]E¿>Ž9~=øîÜ›9ÝOKp”³qÌŒù‚Æ<í`Nw3·óø ø8†µíÛíðŒËÆ1?ãIÒ‚•ù–?ylƒÕÇÑ6vòÔ‰¢VN—©ÕCŽªVfÚ˜³ƒ(2m ­Î¾´–vÕTÿmUÁ˜Á˜q×ÑoëWÇTÌ~è×<*£ÿUáËK9ŸÄ–Ûµ1Ü’1Ïz†Jv3·s7‘ÂÄ,[GeL»º—Í•1ÌÞðüõíKj·¡Tq¢c»i.ê]È©i(ÊH;YÔòéêã+–U®Ì` Â¨Â…êÛ@{|iíª\ÿÕr2f0fˆ>Ãcõ1K’tµ38DŸ1ƒ1K’ĘÁ™ úl ŒY’$Æ ÎÑgc`Ì’$1fp&ˆ>c–$‰1ƒ3AôÙĈ1Kcg‚èg˜­¨·bè{i¥6†G2æùÂlÍ“Á6|®ì$wá|agY³íì—_™Œ/¸Ó–í´8_.çÚq9 áèï‚1ƒ1cÆ\⻵gÆ,F ú˜ 3.¯¹¯®\웸„ö5½Ï)p—È<\Hï¶Òaª¸'ccÆMsÂ’¿Oz£g+]§–)ÙþÕn͘ó Œy»àÙnžÝ*kÓnÂüºÄýê[=ê{CGUêó³uÇ'r›¯I±;p·ÆDW¶ðu¸ôqbå‹îtãúÉù}ŠõP¨™p¥˜mVÁëç¯ÿÞ-1/p[UÔ·û§\…Ç™Ëv¤+0³tH®n\ô\l¥)X©UêÈÏí6+^®êf•\[y‡1ƒ1ãV9ÙǼ{ÿòö¾ÓæÙò|«ï‰fÌŒùhcÞ.xöÑ)W÷¯ãÒÇ©åµ{QèEíùó/σ´õÂ1¬&¯“œ9i˜64eºîqqÉådÉÃÓµìi¾x­f†bo×v~×yîí*³Ô_±*êÛ¿MWx”yí{†ÌòÔÓs•£~ZXº w¬¦Ô·´,HY‹K¹UëccÆísauë‚1o;sþÈ*8Ãö1Ï­%è+ÍŽ¾ w¨îÜê»ée½ÛsH–|¾ºumŸ‚`Ukf#R;‡ÛzÞ—Îç:竾° ô’á4•‹ŠFMTË“X¼ý}7ðD²‹Ù–ÎÕ‚òP¥»µÄ¥ÍÂ33jÛl f̳œ¿æ.½Îüʰ]Gsªä‰ÜŠû”Œ¹±fâÿžß˜ ž©¥Î1æ¶òÿñ¹“æSs&ñ¸ BçqÓn [5ccƽóG8£˜!Ããô1öTw¬ 3ï>æƒ9UòDsÛg€xÏjÍĽË_¢‘ç0ær…'sHŽ+8­1·Ëqé %!HŽl)\û [5ccÆÝó^‘»á‰÷ŒnÌáPÑ9ø²>=޹¢‰áèÕÇ1篺²Oðº4ˆ¶8SÛ8Ðö)éXÉ‘ Çs±ÂÛ¹:àø°>æl¶»2w¿äK è²kÌŸd+y>h›1ƒ1ã>Œ9˜cþC?£2À˜ÿœMUq਌aR‚ç¯o_‚9ÚŒ9œÓw6WF5‡yÉS§(íÇVB©f’’°må4WE㨌B…g†Ü$FeT'µ¨E?˜|cZ?Éi%º›**¯… ¾¨ì˜ãün-q‰*¹ŸÜ\`Ì}6†»1fI’¬ùÎÑgc#Æ,IŒœ ¢Æ,FŒY’3À™D_ô³1fIbÌg‚è3f1b̒ĘÎÑgÌ`̒Ę38DŸ1㌹q‰¾«§ëæ°:YUÞD=3f€3Aô³­Õ˜k“¯Á¢VbÌǵr%eÌŒœ 7}´Ã˜s“1oW)ëØ¯¾ÖºFÆ|eŠüêûCºÅŽíi’C¾œÁBÓ•¢³ ‹NçVÞ>_á"}Þa•q–D~XuÅÂ…ÉwûvºøÜæzŸ¿þûŸÿª‡)W3qIÅ(®x2«±ð¤‰ö“ªÉøŒŒ`Ìýc>]°%ÎG•Þ§1o|eоƕ„WÚ +Þ8͸îñÔb3 _g‹æ-çSwºýýëDyfæN7ì<»ôöyU$Ï»Ù8èþ¸Êw¾Â‹H×z|ç^ø¾ÂëaÊÖLS¿xeUíÙz{AÛ(-W¸>f€3Aô×ic«fç7ÙÇ\$PÇ<ïXÖ^´ÎsuàG‹´m‹õ°V5+Wàöíã‹d=L»{«R¿êmçëôóFí¼cóAaj-F¾;ùÇŸMUZ¿¢ÌŠÖŒàLýõÛ;¿=cŽø˜>æIn‘d¯é­Æ<íÂ,\`õtsC=oÌAá;}Ì\ïзݽh SµfJÅ8•1g"8;#c8Dÿaù¾í|£2‚¾ºÆ/Üë}ÌK~ÍïŸ,R“1‡Ý·as¹_¹vºÃû˜›µþc‡b¯·;|Qñj&[Œƒ9ÙÎȘÎÑgÌ÷jçCŒ.Ùw>Çü|_¸1¶:Ž:mz‘ǩɘ§¿ŒÇ1gGb$Oä6é›ÞžÇœ¹Øü8æ±2+ã˜S☻ÞmÁ&ëaª¢XŒÌ8æ]†Ý/ùÒ–Æ13f€3Aô³]¬ï<=ËÁó×·ØáZŒ¹Ëdò{µÖQÉ™¢"ífäh•‘™?a^žÜ<³Ó 3EÌæÄÈm®4qÞñbSseŒåi›+#±:eúzgÝð-a*¢\ŒdÉ»›bä;­S3c¤Š:=ãMý1ƒ3Aô3Ö#kþ>ë°Ùˆ%Æ ÎÑgc`Ìc–38DŸ13fIbÌàL}0f1b̒ĘÁ™ ú`ÌbĘ%‰1œ ¢Ï˜Åˆ1Kc8DŸ1‹Ñ5W0ÙÎÒ•]ß8œ_,»ª\zίaû÷í‚̵U÷$‰1œ ¢Ï˜±bcþôòœYˆ.\"šÍ·4UsrE=I’38DŸá6y·$ǰž\`½óÕ׆%îsÛÊےĘÎÑgÌXµ1ÿøs\ˆøGa1¶xå?ÞvTO;§%‰13fp&ˆ>cÆÍóöÅ~Íçc95dy¿îñ°¦´$1fÆ ÎÑg̸=cÞ ºxùöûéû˜ÃA¤Y’38DŸá†y;pùÓÛß'Çlø²$1fp&\>úh‡13æ¥Æ¼süüéå„seèc–$Æ Æ Ñgc¸+cþÏ|ûôTž¹bÌ3þÇ¿Úu6KŒ™1ƒ3Aô3nŘ%IbÌàL}6Ƙň1Kcg‚èƒ1‹c–$Æ p&ˆ>c#Æ,IŒàLXý÷ý¯Œ^¿o_½¾w›ö/äý5ÌíÜlÎö~¾ºbÌŒ™1KcgƒG?éµG©ó¥L98ÏÙNɘ3c–$Æ Î„þ{?‰ÕÌ·/¾ÏÿuÃÇÛËlËînónã(¯Síî}yûhPáÔY‚×qÙÎäÌŒ™1ŸÌ˜» æ>ÿç×ìr€ Ó÷ÿù?þõ?=—‘\¼Œ—H<ùÜyÕ ã±RÆ 0fÑÿ™‘Ñ÷|óFz#?Ý™k`Á©Žß`Ÿ­7WÌv³w´o¹gTfÆÌ˜OcÌI];λ¥g+žP+ÏmÌÔÉ1m%EÆ 0f\Ę7;¨qnØFUm«bÛžCáÔŒ™1¯É˜ÇIݨ»ÍÙÒ$¿¿=§+Ù®Øo Dy¾ w¨€ýÊ)ÛúbŒ;ÏN”,gwÒ§—o¿§/'0øþ\û—q‰®º¥9>o¦$³Ï‰Rµ\x©¢IŒŒŒ9ø×Æ Æ|\ßmÆ?¾}ê¤vÐâ½Ò‹cO,9ÙÍÜYà.ÛíªÝŸyî_÷çÊœhê£û“GmN½wÇíº†ýy7¯¡ÜIjC÷ð’:If’,I˜r¥j¸ðÀ¤£H.1f0f0æ ¹ðAÆ Æ| cÞö_~zûûÏÒ…òÛqc˜OœgíDå2ç²Ê–gÛ/;é ?Ò˜«Hr¥j¿ðêÅËÁ˜Á˜Á˜ûã å”2Ï=53Ž¹Ý˜sã˜ÇmO©²Ç̘¯iÌã×ú™/ës*«¹ê¾Å-Ȇò—ÖyóÙ9Sªö o¯7þʘÁ˜ñØÆ¼‹1ªh02#3ý\n®ŒJñ,‹Ù\ÃÆ"§Ëf® Æ|G}ÌÍòw”1×NtrcÞ§axÃÕŒ¹ùÂÛëMbÌ`Ìý¥4Ékä§>%cfÌk6æqüÀ8L6?¸msE.3'J 9^'G‡åIcNs~tDu¨IÃ8æ\©*5\£’8\bÌàLýƒ ¶}°òùÏÆ˜óʹ›Gbò›¶Ò¨Œ©%—æÊ¨uÇÎO–¤”C4 aœ>"3WF0˜aâúíseÌi™+#_ªR WGu'ëMbÌàL}6ƘÅè(c>C:Ë|̒ĘÎÑgÌbt7Æ|ö5ÿ$‰1œIôÁ˜ÅèÖY’3cg£v3cf̒ĘÁ˜ñÐüµö 2¼ý­­…13fIbÌ`Ì`ÌŒ™13fÆ,IŒ`ÌXŸ1‡­qÍÒ¬µ0æsyuíÔiÛ Ë¾ÿóke’µ“¤•/ò|DñÎû›Èƒ v—Ñìmf‚¿3–Š1ƒ1ãq[£–‰û6æíZOݺMnQŸøŽyUóîU—|Lc®ž”1ƒ1gjŠ'îÖ˜»–Ë |$òØu9O×Ë׿˜jÜvE½–=‚³t‡ßo7æöì3|ùöõµP¶±'²¸äÇlŸI%„ÔNÔ²¶K®í}þãΉŠj¨·ÌŠ*Ãr'éµ]’Ò‚Ìu==ýøñg6ÿ–qÈZª¥¼‚:cc3sýûëÌ-X¥/cãB̓ îõ%ô¤âªÑóCÂÅ«‡L&+ZG{n3ìÖ¬ÞõG‹HOÊVÓ¦xŸäQ'NÔº~x/y›kyþüËsÿºñ²µ‘Xµ{óz°ÛÜúቸ4‡`~]Ãõnö)VÔ‚)ƒoHjÕ˜Á˜ ¶CíwhÌÛþ¹Î/w¢©m»1‡Yµ«IYIÛÇ„Œ÷]æñƸlÛ¾É愨Ÿ5úi‘Âzk©„ìE…&K[̹}Ií0«j%—£Ö¬É™Ë©7¼8GW cc3Ú˜sÃ2£énæÆœ:Ý y’ÛîõÑȇt ƒ}ÊúÛR í‚X½„yayÒ-ê0c®E¤¹á8®Z33pX#,£Šp?}ÌãЀY7ó²>æ…]•É£ª‹í:˜ìxŽSr8AnŸ\·zîrª’Ýj̵œOlÌq\šCë;Ÿ÷17”Jö럪Z330æ–̵QÅec}hQºdR‚æ3£GŠº6d˜Ç|:cNŽüNž¨msQò²Õ›Û0-d<Ž9,Ï‚qÌÍ!HÏÎcÎóîtÝ續1W[]2Æ Æ Œyñæ™N&4¨uÑmç.åc˜aš$çÊ(L’ðüõíËt~èÌ÷“¹2Òe{z*ʈ÷iW0?Qû\EcÎæ\Š|¾^X0WF{rsedæëÈO—±aÓJuÖa¥Õ¥óaÌ`Ì€6 ƼÞå?žêº*=šW/ÞYæc–¬ùvh“`Ì÷fÌ÷'åëYóObÌ`'€6 ÆÌ˜%‰1k'ñ< ¶¬j‹JXÃÆÌ˜%‰1jÌú/{ýKa̒Ę3<]/à/…1K’Ęá¹èz!F`Ì’$1f°× 1c–$‰1ƒÚ¤(<1¸.Œž‹Ð&! «6æ¿\Æ ÏE× øKaÌ3<]/à/…1`Ìð\t½#Ü«1¸þ­@€¸^13Æ vmR›Æ €1ÃsÐ&E1`Ìð\t½€¿Æ €1ÃsÑõþR3ÆŒÇ~.ZFè&Á•G¡ú7ˆSµvÆ €1ã:ƬŠDGFá|ǪgÆ €1ƒoA33Æ ¾dÌ`Ì3øÎÁ‚%ˆà¢À˜/ÖÚ3·T0f0fƬž3Æ Æ ÆÌ˜Á˜0f0f0fÆ,FŒcccÆÌ˜0f0f0f0fÆ €1ƒ1«"Æ ÆÌ˜0f0fÏrÆ ÆÌ˜0f€13f0fÆ €1Œ™Cˆ cfÌŒ`ÌccfÌê™1`Ì`Ì`ÌŒŒcccfÌbĘ0f0f0fQ`ÌŒcccÆÌ˜0f0f0f0fÆ €1ƒ1{–3f0fÆ €1Œ™1ƒ13fŒ`ÌB3cÀ˜Æ ÆÌ˜Õ3cÀ˜Á˜Á˜33Æ Æ ÆÌ˜U cÀ˜ñ8Æüþú4ðòöq+u°/öëûádž¼~ŸdxLæ—1æÄ%¼ ÄêŒùø0}¼½Y7ÐÚ3ÆŒuóþq<>,7ï¯%^«’¼³ûĉŒù*Á:ᩯˆKsK™3{öº{Äeß@kgÌ3VmÌåÇãØ75îÑÑÿÛ¶SºÓÛçm¿9Ú¾;vŸcwä¤öp{bã¼ì…ÒfKRó†ï“þ¹ðsÅlK|iW1æÜõNë'ìbÜþÿÿüå%Øy³¥{™Ì-®Ä5Œ9ñ!õéÿø¿ÿ¯t'ôæ_ÞÞß^&Á¬Tæ-¶vÆ €1cÍÆ\æñËàÝ^OõOÏáIºù·ð¡Þí³}79ü»¬Ëø=üÚ¹útOo,—vÔúÖ³”zÝòÅ^Ð7wNcޝwÿ.èIÃ}wŽÕ^"Ž‘?Ý` ®ÔǼ߸«›ÍËBUtÿú1UæÊßW-^klíŒcÆsè5¡û´°ã0Ú2ôA‡Ù0æ ÄÇ1Ïú{Æ?Ø;Á§Ö•¶vÆ €1cÍÆ\xô´9Ý5Uš ù½ïtãñ]›‡9D²Ø+7æZs·Çëûä—Ÿq¼á@\ϘãNæøê¯TBÁn‰KµKx]­1`ÌXµ1G?ÚÛ< [¤¶s?L3?ž2êf‹žÊóËzÀ¥mÔͱ(©b¯Ü˜sã˜ç?¸|}}:9ã8&‡ÕÞd ®eÌ“ñÉÉ«Ç7Ç[þûªÿÝ­°µ3fŒë6æñù—ø¡~i®Œc~ÉýÄ>}îxÌåü§Nå-m]7ƒ1!³Ÿ>]}TÆ‚IvÓseÌ•,ŽÇI…|¿å@\c>æíÌ$óŠÙ7êñê6ûD${uýwåï«éïnm­1`ÌX¿1Ÿœ+N|K\ך‰¯ßï?Ž·¹æ_..ëcÀ˜Á˜óó|’`ÆÌ˜3Æ Æ ÆŒ5æ;l팀[*33cVÏŒcccfÌ`Ì333c#Æ €1ƒ1ƒ1‹cfÌ3333cÀ˜Á˜UccfÌ3³g9ccfÌ3À˜33cÀ˜ÆÌ!D13fÆ 0f€1ƒ13fõ̘0f0f0fÆ Æ €1ƒ1ƒ13f1bÌ33³(0fÆ €1ƒ1ƒ1‹cfÌ3333cÀ˜Á˜=Ë33cÀ˜ÆÌ˜Á˜3Æ 0f!‚Œ™1`ÌccfÌê™1`Ì`Ì`ÌŒŒcccfÌ*1`Ì`Ì`ÌŒÙ_cÀ˜q¿ÆŒõSv\= Õ¿Aœªµ3fŒW0æ¿°zDpýQ£óÕ3cÀ˜Á˜Á˜33Æ Æ ÆÌ˜Á˜0fÜ®1ÀÊïfŒ­cÆ €1ƒ1cÀ˜Á˜€1`Ì`ÌÀ˜0f0f`Ì3á¾ò"Æ ´b†WfÅÀ˜Æ €1`ÌcÀ˜0f3þ‹ç¤K‰1IEND®B`‚ =============================================================================== ISO/IEC 9660:1999 Cookbook =============================================================================== Creation date: 2008-Jan-14 Author: Vreixo Formoso _______________________________________________________________________________ Contents: --------- 1. References 2. General 3. Features 4. Implementation 5. Known implementation bugs and specification ambiguities/problems ------------------------------------------------------------------------------- 1. References: ISO/IEC DIS 9660:1999(E) "Information processing. Volume and file structure of CD­-ROM for Information Interchange" ------------------------------------------------------------------------------- 2. General ISO 9660:1999, also known as ISO-9660 version 2 is an update of the old ISO 9660:1988 standard for writing data images for CD. In the same way Joliet does, it is based on a Secondary Volume Descriptor (that is called Enhanced Volume Descriptor), that provides a second tree where the new file information is recorded. ------------------------------------------------------------------------------- 3. Features It makes some improvements with respect to ECMA-119, mainly related to relax the constraints imposed by its predecessor. - It removes the limit to the deep of the directory hierarchy (6.8.2.1). However, it still keep a limit to the path length, of 255 characters as in ECMA-119. - File names don't need the version number (;1) anymore, and the "." and ";", used as SEPARATORS for extension and version number, have no special meaning now. - The file name max length is incremented to 207 bytes. - The file name is not restricted to d-characters. ------------------------------------------------------------------------------- 4. Implementation ISO 9660:1999 is very similar to old ISO 9660:1988 (ECMA-119). It needs two tree hierarchies: one, identified by the Primary Volume Descriptor, is recorded in the same way that an ECMA-119 structure. The second structure is identified by a Enhanced Volume Descriptor (8.5). The structure is exactly like defined in ECMA-119, with the exceptions named above. Thus, to write an ISO 9660:1999: - First 16 blocks are set to 0. - Block 16 identifies a PVD (8.4), associated with a directory structure written following ECMA-119. - It is needed a Enhanced Volume descriptor to describe the additional structure. It is much like a SVD, with version number set to 2 to identify this new version. - We can also write boot records (El-Torito) and additional SVD (Joliet). - We write a Volume Descriptor Set Terminator (8.3) - We write directory structure and path tables (L and M) for both ECMA-119 tree and enhanced tree. Path table record and directory record format is the same in both structures. However, ECMA-119 is constrained by the usual restrictions. - And write the contents of the files. Interchange levels 1, 2 and 3 are also defined. For PVD tree, they have the same meaning as in ECMA-119. For EVD tree, in levels 1 and 2 files are restricted to one file section (i.e., 4 GB filesize limit). In level 3 we can have more than one section per file. Level 1 does not impose other restrictions than that in the EVD tree. It seems that both El-Torito and Joliet can coexist in a ISO 9660:1999 image. However, Joliet has no utility at all in this kind of images, as it has no benefit over ISO 9660:1999, and it is more restrictive in filename length. ------------------------------------------------------------------------------- 5. Known implementation bugs and specification ambiguities/problems - While the specification clearly states that the tree speficied by the Primary Volume Descriptor should remain compatible with ISO-9660 (ECMA-119), i.e., it should be constrained by ECMA-119 restrictions, some image generation applications out there just make both Primary and Enhanced Volume Descriptors to point to the same directory structure. That is a specification violation, as for a) the directory hierarchy specified in the Primary Volume Descriptor doesn't follow the restrictions specified in the specs, and b) the same directories are part of two different hiearchies (6.8.3 "A directory shall not be a part of more than one Directory Hierarchy."). Thus, we should keep two trees as we do with Joliet. Or are there strong reasons against this? - It's not very clear what characters are allowed for files and dir names. For the tree identified in the Enhanced Volume Descriptor, it seems that a "sequence of characters rather than d-characters or d1-characters" is allowed. It also seems that the charset is determined by the escape sequence in the EVD. Anyway, leaving escape sequence to 0 and use any user-specified sequence (such as UTF-8) seems a good solution and is what many other applications do. Linux correctly mounts the images in this case. - It is not clear if RR extensions are allowed in the tree identified by the Enhanced Volume Descriptor. However, it seems not a good idea. With 207 bytes filenames and XA extensions, there is no place for RR entries in the directory records of the enhanced tree. In my opinion, RR extension should be attached to the ECMA-119 tree that must also be written to image. =============================================================================== ISO-9660 Level 3 Cookbook =============================================================================== Creation date: 2008-Aug-17 Author: Vreixo Formoso _______________________________________________________________________________ Contents: --------- 1. References 2. General 3. OS Support 4. Implementation ------------------------------------------------------------------------------- 1. References: ECMA-119 "Volume and File Structure of CDROM for Information Interchange" ------------------------------------------------------------------------------- 2. General In ECMA-119 standard, the size of a file section cannot be bigger than 4GB - 1, because the Data Length field of the Directory Record is just 32 bits (9.1.4). However, "each file shall consist of one or more File Sections" (6.5.1), and that way we can store files greater than 4GB in a ECMA-119 image. Such image, with multiple File Sections, is only supported at Level 3 (10.3), as Level 2 (10.2) states that "each file shall consist of only one File Section". On disc, each file section is stored in a Extent (6.4.2), i.e. a set of contiguous Logical Blocks. ------------------------------------------------------------------------------- 3. OS Support Wikipedia states that "Microsoft Windows XP supports this, while Mac OS X (as of 10.4.8) does not handle this case properly. In the case of Mac OS X, the driver appears not to support file fragmentation at all (i.e. it only supports ISO 9660 Level 2 but not Level 3). Linux supports multiple extents. FreeBSD only shows and reads the last extent of a multi-extent file." ------------------------------------------------------------------------------- 4. Implementation Each File Section will have its own Directory Record (6.5.1). So, for files greater than 4 GB, we need to store several directory records, that will have the same File Identifier, and stored in the order of the File Sections they refer (9.3). All but the last Directory Record must have the Multi-Extent flag set (9.1.6) # Doxyfile 1.5.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file that # follow. The default is UTF-8 which is also the encoding used for all text before # the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into # libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list # of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = @PACKAGE_NAME@ # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @PACKAGE_VERSION@ # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, # Italian, Japanese, Japanese-en (Japanese with English messages), Korean, # Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, # Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = @top_srcdir@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to # include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be extracted # and appear in the documentation as a namespace called 'anonymous_namespace{file}', # where file will be replaced with the base name of the file that contains the anonymous # namespace. By default anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from the # version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = libisofs \ doc # This tag can be used to specify the character encoding of the source files that # doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default # input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. # See http://www.gnu.org/software/libiconv for the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py FILE_PATTERNS = libisofs.h \ comments # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the output. # The symbol name can be a fully qualified name, a word, or if the wildcard * is used, # a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = demo/demo.c # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH # then you must also enable this option. If you don't then doxygen will produce # a warning and turn it on anyway SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = YES # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentstion. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = OB \ OTK \ _ #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = doc/html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 200 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = letter # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = DOXYGEN # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to # produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to # specify the directory where the mscgen tool resides. If left empty the tool is assumed to # be found in the default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) # ts B10415: dot causes sigsegv on Debian buildd HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a caller dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected # functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = NO # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the number # of direct children of the root node in a graph is already larger than # MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO Overview of ISO 9660 hybrid filesystems as libisofs output by Thomas Schmitt - mailto:scdbackup@gmx.net Libburnia project - mailto:libburn-hackers@pykix.org 07 Jun 2012 The overall framework for the filesystem images produced by libisofs is given by ECMA-119, which is also known as ISO 9660. The hybrid aspect is the opportunity to add access structures of other filesystems. The framework suggests a logical block size of 2048 and divides the space of filesystem blocks into several parts: - The System Area. Beginning at the image start block. 32 KiB of arbitrary data, which are not considered to be part of structure or payload of the ISO image. - The Volume Descriptors. Beginning at image start block + 16. The Primary Volume Descriptor block is the starting point of the ECMA-119 tree of directories and files. Among other information, it records the size of the image block space. Other descriptor blocks may lead to boot images or to the directory trees of add-on filesystems (e.g. Joliet). - The area of directory structures and data file content. libisofs divides it into two sub areas: - Directory structures. They record the file names and attributes of the ECMA-119 tree and of eventual add-on filesystem. - Data file content. The blocks in this area are referred by zero or more file entries in the directory trees. They store the data content or regular files. Start block address of a file and exact byte count are stored in the trees. libisofs may slide-in some data blocks which are neither part of the structure nor part of file content. See doc/checksums.txt, Checksum Array, Checksum Tags. In the same way, the superblocks of other filesystems could be inserted into the image. The only block addresses which are fixely occupied are image_start+16 (Primary Volume Descriptor) and image_start+17 (first possible position of Volume Descriptor Set Terminator). Nevertheless, libisofs considers as reserved the blocks image_start+16 to image_start+31, because add-ons like El Torito, Joliet, or ISO 9660:1999 need their own volume descriptors stored before the volume descriptor set terminator block. Only one volume descriptor per add-on filesystem may be written there, and its exact position will be chosen by libisofs. The System Area in image_start to image_start+15 may be used for a partition table or the superblock of an additional filesystem structure. Another place for superblocks is after image_start+31. E.g. UDF stores its Anchor at block address 256, or at media_size - 1 - 256, or at media_size - 1. In both cases the superblocks would point to filesystem-specific data which are stored in the area of directory structures. These data would then refer to the same file contents as the ECMA-119 directory structure. ----------------------------------------------------------------------- What libisofs needs to get implemented for a new add-on filesystem: The emerging overall image is represented by an Ecma119Image object. This is an instance of quite fat struct ecma119_image which, among many others, holds some parameters which are specific to the implemented add-on filesystems. It is defined in libisofs/ecma119.h. It gets programmed by applications via API calls for IsoWriteOpts which is defined as struct iso_write_opts in libisofs/ecma119.h. The content of the System Area may be submitted opaquely via Ecma119Image.system_area_data or it may get generated underneath libisofs/system_area.c:iso_write_system_area() by a specific "System area type" in Ecma119Image.system_area_options. The latter happens when the block adresses of all components, directories, and files are determined. (One may have to dig deep in the graph of objects to obtain everything.) If a new system area type is needed, then it has to be documented in libisofs/ecma119.h at struct ecma119_image.system_area_options and in libisofs/libisofs.h at call iso_write_opts_set_system_area(). See e.g. "MIPS Big Endian Volume Header". libisofs/system_area.h offers an inner API to define partitions for Apple Partition Map (APM) and for GPT from within the compute_data_blocks methods of the IsoImageWriter objects (see below). If both get combined, then APM block size must be 2048. In this case, the partition entries of APM and GPT overwrite the following bytes of the submitted Ecma119Image.system_area_data: 0x0000 to 0x0007 by { 0x45, 0x52, 0x08 0x00, 0xeb, 0x02, 0xff, 0xff} 0x0200 to 0x02ff by GPT header block 0x0800 to APM_end by APM blocks (APM_end = 2048*(Num_APM_partitions + 1) APM_end+1 to 0x7fff by GPT entries This offers still room for a PC-BIOS MBR which has essentially no-op commands in its first 8 bytes. If no GPT is desired, then the bytes after APM_end stay unaltered. If more modesty with overwriting is needed, then this would have to be caused by either a specialized system area type or by additional elements of the inner API for APM and GPT. The layout of the areas above image_start+16 is defined in function libisofs/ecma119.c:ecma119_image_new(). This is done by creating and registering writer objects. Writers are instances of typedef struct Iso_Image_Writer IsoImageWriter. The struct is defined in libisofs/writer.h. The Joliet writer is a comprehensive example of an add-on filesystem writer. First it gets counted for the allocation of the registration array if (target->joliet) { nwriters++; } Later it gets created and registered if (target->joliet) { ret = joliet_writer_create(target); The function libisofs/joliet.c:joliet_writer_create() accounts for one block that will hold the Joliet volume descriptor /* we need the volume descriptor */ target->curblock++; Not all add-on filesystems will need a volume descriptor. Joliet does. joliet_writer_create() further generates a tree of JolietNode objects by traversing the image model tree of IsoNode objects. ret = joliet_tree_create(target); If a JolietNode represents a regular file then it refers to an IsoFileSrc object, which represents its data content in the emerging image. struct Iso_File_Src is defined in libisofs/filesrc.h. libisofs will call the methods of the writer object when it computes the block addresses of the various image components, when it writes volume descriptors, when it writes directory trees, and when it finally disposes the Ecma119Image object. Before calling the first method, it will publish the number of data file content blocks in Ecma119Image.filesrc_blocks. The method IsoImageWriter.compute_data_blocks() has to predict the storage needs in the area of directory trees. It computes and records Joliet-specific addresses and sizes: Ecma119Image.joliet_ndirs, Ecma119Image.joliet_l_path_table_pos, Ecma119Image.joliet_m_path_table_pos , Ecma119Image.joliet_path_table_size Ecma119Image.j_part_l_path_table_pos, Ecma119Image.j_part_m_path_table_pos as well as the sizes and block addresses of Joliet directories. It increases the counter of virtually written blocks: Ecma119Image.curblock which is used to determine the start addresses of the image parts and finally gives the overall image size. The method IsoImageWriter.write_vol_desc() composes and writes the Joliet volume descriptor. (Such writing is not necessarily needed for add-on filesystems.) IsoImageWriter.write_data() writes the records of the Joliet directory tree. This has to be exactly the same number of blocks by which Ecma119Image.curblock was increased during IsoImageWriter.compute_data_blocks(). When it gets called, the number of content data extents, their sizes, and their addresses are known: JolietNode.IsoFileSrc->nsections, ->sections[].size, ->sections[].block. struct iso_file_section is defined in libisofs/libisofs.h. IsoImageWriter.free_data() disposes the writer and the JolietNode tree. Further examples for add-on writers are those created by: hfsplus_writer_create() is in charge for initial part of an embedded HFS+ filesystem hfsplus_tail_writer_create() is in charge for trailing part of HFS+ after the data file content area gpt_tail_writer_create() is in charge for the backup GPT near the end of the ISO image ------------------------------------------------------------------------------- This text is under Copyright (c) 2012 Thomas Schmitt It shall only be modified in sync with libisofs. Please mail change requests to mailing list or to the copyright holder in private. If you make use of the license to derive modified versions of libisofs then you are entitled to modify this text under that same license. Arbitrary Attribute Interchange Protocol Version 2.0 Mar 18 2009 Interchange of Persistent File Attributes by Thomas Schmitt - mailto:scdbackup@gmx.net Libburnia project - mailto:libburn-hackers@pykix.org AAIP is intended as companion of the Rock Ridge Interchange Protocol RRIP which under the general design of System Use Sharing Protocol SUSP extends ISO 9660 aka ECMA-119 filesystem semantics to match POSIX needs. Goal is to have for each file an arbitrary number of attributes which consist of two components (Name and Value) of arbitrary length and to have a compact representation of ACLs. This document describes a SUSP entry with Signature Word "AL" which collides neither with SUSP 1.12 nor with RRIP 1.12. The AL entry has been designed to be as similar to the RRIP entry SL as possible. The presence of AAIP shall be announced by a particular ER entry. Since the size of a SUSP entry is limited to 255, multiple entries may be needed to describe one component. The CE mechanism of SUSP shall be used to address enough storage if needed. AL entries and the ER entry of AAIP shall only be present if the ER entry of RRIP is present. ------------------------------------------------------------------------------- System Entries Provided by this Specification * AL Description of the "AL" System Use Entry The entry has exactly the same layout as RRIP entry SL. One has to expect more data bytes than with SL, though, and any of the 256 possible byte values. The reader shall be prepared to detect and handle oversized data. One or more AL entries form the Attribute List of a file object with an even number of components. Each two consecutive components form a pair of Name and Value. The empty name indicates that the value is a compact representation of ACLs. Names must not contain byte value 0x00. Names which begin by bytes 0x01 to 0x1f represent names in particular namespaces. See below: Namespaces. The meaning of any other names or name parts is not specified by this document. All AL entries except the last one shall have the CONTINUE flag set. An AL entry with CONTINUE set to 0 indicates the end of the Attribute List. The format of the AL System Use Field is as follows: [1] "BP 1 to BP 2 - Signature Word" shall be (41)(4C) ("AL"). [2] "BP 3 - Length" shall specify as an 8-bit number the length in bytes of the AL entry recorded according to ISO 9660:7.1.1. [3] "BP 4 - System Use Entry Version" shall be 1 as in ISO 9660:7.1.1. [4] "BP 5 - Flags" shall contain bit field flags numbered 0 to 7 starting with the least significant bit as follows: 0 CONTINUE This AL entry continues in the next AL entry. All other bits shall be set to 0. [5] "BP 6 to Length - Component Area" shall contain Component Records as described below. | 'A' | 'L' | LENGTH | 1 | FLAGS | COMPONENT AREA | Within AL entries each component (Name or Value) shall be recorded as one or more component records. If a component does not fit into the remaining space of an AL entry then it shall be continued in following AL entries. All Component Records of a component except the last one shall have the CONTINUE flag set. A Component Record with CONTINUE set to 0 indicates the end of the component. An eventually following Component Record starts the next component. ------------------------------------------------------------------------------- The Component Record format is identical to the one of the SL entry. The complete form of the following summary can be found in RRIP 1.12 "4.1.3.1". In case of discrepancies, RRIP 1.12 is the decisive specification. Please inform the author of this document if you find such a discrepancy. Component Records shall be recorded contiguously within each Component Area, starting in the first byte of the Component Area. The last Component Record in the Component Area of an AL System Use Entry may be continued in the Component Area of the next recorded AL System Use Entry in the same System Use Area. Each Component Record shall have the following format: [A] "BP 1 - Component Flags" shall contain bit field flags numbered 0 to 7, starting with the least significant bit, as follows: 0 CONTINUE This Component Record continues in the next AL Component Record. all others are RESERVED and shall be 0. [B] "BP 2 - Component Length (LEN_CP)" shall specify as an 8-bit number the number of component bytes in the Component Record. This length shall not include the first two bytes of the Component Record. This field shall be recorded according to ISO 9660 Format section 7.1.1. [C] "BP 3 to 2 + LEN_CP - Component Content" shall contain the component bytes in the Component Record. | COMPONENT FLAGS | LEN_CP | COMPONENT BYTES | Example: Two pairs of "name"="long...content" and "one"="more" encoded as two AL entries Field 1 contains the Component Record of Name and one Component Record of Value : { 'A', 'L', 255, 1, 1, 0, 4, 'n', 'a', 'm', 'e', 1, 255, 'l', 'o', 'n', 'g', ... 238 more bytes, 13 go to next AL ... } Field 2 contains the rest of "long...content" and the complete second pair. It marks the end of the Attribute List : { 'A', 'L', 38, 1, 0, ... 13 remaining bytes of the Component Record in first entry ... 0, 7, 'c', 'o', 'n', 't', 'e', 'n', 't', 0, 3, 'o', 'n', 'e', 0, 4, 'm', 'o', 'r', 'e' } ------------------------------------------------------------------------------- Namespaces AAIP provides a short notation for namespaces which uses a single non-printable byte at the start of the name. Reserved start bytes of names are 0x01 to 0x1F The names of extended file attributes are traditionally organized in namespaces, which get expressed as first part of an attribute name up to a period "." character. It is also tradition that names are printable text, single words and especially contain no 0-bytes. AAIP does not enforce the use of any namespace but it urges that names in the following registered namespaces are used according to traditions. The namespaces "system." and "user." are available with many file system types. "system." is file system dependent and often restricted in the choice of names. "user." is portable and allows to choose about any name. Namespace "isofs." is defined for internal use of AAIP enhanced ISO 9660 file systems. Names in this namespace should be registered at libburnia-project.org. Further namespaces may be registered at libburnia-project.org. The reserved start bytes of names have the following meaning 0x01 escape reserved character at start of name 0x02 namespace "system." 0x03 namespace "user." 0x04 namespace "isofs." 0x05 namespace "trusted." 0x06 namespace "security." 0x07 to 0x1F shall not be used yet. Examples: Name "user.abc" with and without short notation. Both is allowed. 0, 4, 0x03, 'a', 'b', 'c' 0 8, 'u', 's', 'e', 'r', '.', 'a', 'b', 'c' Name "\003abc" (if really desired) 0, 5, 0x01, 0x03, 'a', 'b', 'c' ------------------------------------------------------------------------------- Specification of binary ACL representation as special Arbitrary Attribute The Name component of a binary ACL shall be of length 0. The Value shall be an arbitrary number of ACL Entries: [a] "BP 1 - Entry Flags" shall contain bit field flags numbered 0 to 7, starting with the least significant bit, as follows: 0 EXEC indicates that this entry grants execute permission 1 WRITE write permission 2 READ read permission 3 QUALIFIER indicates that one or more Qualifier Records follow 4 - 7 TYPE shall contain the tag type of the ACL entry as four bit code: 0 TRANSLATE Entry for a global map of name to numeric id Qualifier is a record of number and text 1 ACL_USER_OBJ Permissions of owning user (as of PX entry) 3 ACL_GROUP_OBJ Permissions of owning group (as of PX entry) 5 ACL_MASK Restricts 10, 3, and 12 via logical AND 6 ACL_OTHER Permissions of non-listed, non-owning users 8 SWITCH_MARK Switch from "access" ACL to "default" ACL 10 ACL_USER_N Permissions of arbitrary user. Qualifier is the numeric user id (max. 4 bytes). 12 ACL_GROUP_N Permissions of arbitrary group. Qualifier is the numeric group id (max. 4 bytes). 15 FUTURE_VERSION Will indicate that this document does not apply to the entry. The other values are reserved. Readers shall ignore them if they are not aware of updates of this document which would assign a meaning to them. The entries must match the permission bits of the PX entry. This shall obey the rule that ACL_USER_OBJ must match S_IRWXU, ACL_OTHER must match S_IRWXO, ACL_MASK - if present - must match S_IRWXG, else ACL_GROUP_OBJ must match S_IRWXG. If there is ACL_USER_N or ACL_GROUP_N there must also be ACL_MASK. A numeric qualifier is a binary number of variable length up to 4 bytes. The Most Significant Byte comes first. The number shall be the "POSIX File User ID" or "POSIX File Group ID" as also used in RRIP PX entries. The ids of owning user and owning group shall be taken from the PX entry of the file object. Optional TRANSLATE entries may associate user or group names with numeric ids to allow the reading system to remap the numeric ids. See below. The writer is not obliged to write them and the reader is not obliged to interpret them. The ACL entries belong to the "access" ACL of a file object. An optional SWITCH_MARK entry may direct further entries to the "default" ACL which is defined for directory objects. The EXEC bit of SWITCH_MARK shall be 1. The bits for WRITE, READ, QUALIFIER shall be 0. An eventually needed qualifier is stored in one or more Qualifier Records. [b] "BP 2 - Qualifier Record Head" shall be present only if QUALIFIER is set to 1. It shall give the number of Qualifier Bytes and eventually indicate that the qualifier continues in a Qualifier Record which comes immediately after this record. 0 to 127 Q_LENGTH, the qualifier is complete by this record 128 to 255 Q_LENGTH+128, the qualifier is continued by next record So a Qualifier Record can contain at most 127 Qualifier Bytes. This field shall be recorded according to ISO 9660 Format section 7.1.1. [c] "BP 3 to BP 2 + Q_LENGTH - Qualifier Bytes" shall be present only if QUALIFIER is set to 1 and hold the announced number of bytes of the user or group name. | ENTRY FLAGS [ | QUALIFIER HEAD | QUALIFIER BYTES | ] Example: From man 5 acl: u::rw-,u:lisa:rw-,g::r--,g:toolies:rw-,m::r--,o::r-- "lisa" has user number 123, "toolies" has group number 65534 { 'A', 'L', 20, 1, 0, 0, 0, 0, 11, 0x16, 0xAE, 1, 123, 0x34, 0xCE, 2, 255, 254, 0x54, 0x64 } Example: "Access" ACL and "default" ACL (0x81 is the switch mark) u::rwx,g::r-x,o::r-x, du::rwx,dg::r-x,dm::rwx,do::r-x,du:lisa:rwx { 'A', 'L', 20, 1, 0, 0, 0, 0, 11, 0x17, 0x35, 0x65, 0x81, 0x17, 0x35, 0x57, 0x65, 0xA7, 1, 123 } ------------------------------------------------------------------------------- Association of Names and Numeric Identifiers The entry flag value 0x08 TRANSLATE is not a ACL entry of the hosting object but rather a global hint about the relation of roles, names and numeric ids. If it is recorded at all, then it shall be recorded with the first Directory Entry of the volume's root directory. According to the description of SUSP entry ER, this has to be "dot" or (00). Other than with ER, a TRANSLATE entry may not appear in the root of directory sub trees. An interested reader shall examine the Arbitrary Attributes of this Directory Entry in order to collect a translation table. The advised translation is: PX or AL Id number -> name -> local id number. The Qualifier Bytes of a TRANSLATE entry shall have the following format: [i] "BP 0 - Role" shall tell whether it is about a user name (role 0) or a group name (role 1). Other values are not allowed. [ii] "BP 1 to BP 8 - Numeric Id" shall hold the 32 bit POSIX Id number of the entry. This field shall be recorded according to ISO 9660:7.3.3. [iii] "BP 9 to End Of Qualifier - Name" shall hold the name bytes of this entry. | ROLE | NUMERIC ID | NAME | Example: User id number 123 gets associated with user name "lisa" 0x08, 13, 0, 123,0,0,0, 0,0,0,123, 'l', 'i', 's', 'a', Example: A very long qualifier naming "His_Excellency_..._the_Boss" as user #1. This needs two qualifier records. 0x08, 255, 0, 1,0,0,0, 0,0,0,1, 'H', 'i', 's', '_', 'E', 'x', 'c', 'e', 'l', 'e', ... 108 more bytes ... 8, 't', 'h', 'e', '_', 'B', 'o', 's', 's', ------------------------------------------------------------------------------- Specification of the ER System Use Entry Values for AAIP: This ER system entry shall only be present if the ER entry of RRIP is present. To be compliant with SUSP-1.12, this ER entry must be present if AL entries are present, and ES entries have to mark RRIP and AAIP entries. If for some reason compliance with SUSP-1.10 is intended, then this ER entry and the ES entries must not be present, although SUSP-1.10 would allow ER. (See below: Compatibility considerations.) The Extension Version number for this version of AAIP shall be 1. The Extension Identifier field shall be "AAIP_0200" with Identifier Length 9. The mandatory content form of the Extension Descriptor is "AL PROVIDES VIA AAIP 2.0 SUPPORT FOR ARBITRARY FILE ATTRIBUTES IN ISO 9660 IMAGES" The Description Length is 81. The recommended content of the Extension Source is "PLEASE CONTACT THE LIBBURNIA PROJECT VIA LIBBURNIA-PROJECT.ORG". The corresponding Source Length is 62. ------------------------------------------------------------------------------- Compatibility Considerations This extension is supposed not to disturb any reader system which complies to SUSP-1.10: "6.2 Requirements for a Receiving System [...] Any System Use Field which the receiving system does not recognize is to be ignored and skipped." SUSP-1.12 extends this prescription by: "Any System Use Entry, with the exception of the set of System Use Entries defined in this document, following an "ES" System Use Entry that indicates an extension specification which the receiving system does not recognize shall be ignored and skipped." According to SUSP-1.12 the ER entry is mandatory for a conformant extension. It also prescribes that in the case that ER entries of RRIP and AAIP are present, then ES entries shall be used to separate RRIP entries from AAIP entries. SUSP-1.12 frowns on extensions which are not announced by ER. Nevertheless is does not totally outrule them. SUSP-1.10 does not specify ES entries at all and allows to have extension entries without announcing them by an ER entry. So if a second ER entry is not bearable, then the SUSP-1.10 downgrade of AAIP allows to omit the AAIP ER and the ES entries. But if there is the AAIP ER then there must be ES at the appropriate places. Else the format would explicitly violate SUSP-1.12. ------------------------------------------------------------------------------- Model Relations: Attribute List ------------- [1:0..1] ------------- ACL [1:0..n] [1:0..n] Arbitrary Attribute ( [1:0..1] ACL ) Entry [1:2..2n] [1:0..1] Component ( [1..m:1..n] AL Field ) Qualifier [1:1..n] << one of >> Component Record / \ [1..m:1..n] Translation Entry , Numeric Id AL Field | | [1:1..n] [1:1] \ / Qualifier Record ------------------------------------------------------------------------------- Revoked drafts: The following outdated versions may be interpreted at read time but they shall not be written any more. AAIP-1.0 Previous versions up to AAIP 1.0 used field signature "AA" rather than "AL". This nearly collides with "Apple ISO 9660 Extensions". The Apple "AA" field of version 1 has a length of 7, whereas the shortest first AAIP field "AA" had length 9. Beginning with AAIP 2.0, the field name has been changed to "AL". If a reader interprets old AAIP "AA" fields, then it must take precautions to distinguish them from Apple "AA" fields. But it is well compliant with AAIP 2.0 to just ignore any kind of "AA" fields. AAIP 1.0 had ER signature "AAIP_0100". AAIP-0.2 AAIP 0.2 with ER signature "AAIP_0002" allowed to announce and use a different signature than "AA". This was revoked because ES entries serve the purpose to distinguish AAIP entries from eventual "AA" entries of any other extension. Regrettably no reader (kernel) was found which neatly interprets ES. Many do not even recognize the RRIP-1.12 ER signatures "IEEE_P1282", "IEEE_1282". AAIP 0.2 defined two ACL types which did not make it into AAIP 1.0 2 ACL_USER of arbitrary user, with name as qualifier 4 ACL_GROUP of arbitrary group, with name as qualifier Their job was transferred to ACL_USER_N and ACL_GROUP_N which have numeric qualifiers. AAIP-0.0 There was a draft AAIP 0.0 with ER signature "AAIP_2008A". It did not resemble the existing entry SL and was never implemented. ------------------------------------------------------------------------------- References: ECMA-119 aka ISO 9660 http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf SUSP 1.12 (entries CE , PD , SP , ST , ER , ES) ftp://ftp.ymi.com/pub/rockridge/susp112.ps RRIP 1.12 (entries PX , PN , SL , NM , CL , PL , RE , TF , SF , obsolete: RR) ftp://ftp.ymi.com/pub/rockridge/rrip112.ps Apple ISO 9660 Extensions (entries AA and BA) http://developer.apple.com/technotes/fl/fl_36.html Amiga AS entry http://www.estamos.de/makecd/Rock_Ridge_Amiga_Specific zisofs entry ZF (prepared by zisofs-tools, written by mkisofs) http://freshmeat.net/projects/zisofs-tools/ Program mkisofs emits entry XA ftp://ftp.berlios.de/pub/cdrecord/alpha ------------------------------------------------------------------------------- This text is under Copyright (c) 2009 - 2013 Thomas Schmitt It shall only be modified in sync with libisofs and other software which makes use of AAIP. Please mail change requests to mailing list or to the copyright holder in private. Only if you cannot reach the copyright holder for at least one month it is permissible to modify this text under the same license as the affected copy of libisofs. If you do so, you commit yourself to taking reasonable effort to stay in sync with the other interested users of this text. Arbitrary Attribute Interchange Protocol Interchange of Persistent File Attributes Directory of Namespace "isofs." by Thomas Schmitt - mailto:scdbackup@gmx.net Libburnia project - mailto:bug-xorriso@gnu.org The following names are defined for AAIP namespace "isofs." as mentioned in specification of AAIP. Unless explicitly stated otherwise, numbers with names like *_LEN are 8 bit unsigned integers, those with *_BYTES are 32 bit unsigned integers. ------------------------------------------------------------------------------- Name: isofs.ca Purpose: Records the range of checksummed image data (START, END), the number of checksum items (COUNT), the number of bytes in a single checksum item (SIZE), and the name of the checksum algorithm (CHECKSUM_TYPE). END is also the block address of the start of the checksum recording area in the image. See also isofs.cx . This attribute shall be attached to the root directory entry and be global for the whole image. Format of Value: START_LEN | START_BYTES | END_LEN | END_BYTES | COUNT_LEN | COUNT_BYTES | SIZE_LEN | SIZE_BYTES | CHECKSUM_TYPE Each number is encoded as _LEN byte and _BYTES value string. The _LEN fields comply to ISO 9660 Format section 7.1.1. The byte strings START_BYTES, END_BYTES, COUNT_BYTES, SIZE_BYTES begin with the most significant byte. Leading zero bytes are allowed. CHECKSUM_TYPE consists of the bytes after START_LEN + END_LEN + COUNT_LEN + SIZE_LEN + 4. It shall be a string of printable characters without terminating 0-byte. Type names shall be registered here. For now there is: "MD5" 128 bit message digest, see RFC 1321, see man md5sum Example: LBA range 32 to 1000000 , 520 checksums recorded, MD5 { 1, 32, 3, 15, 66, 64, 2, 2, 8, 1, 16, 'M', 'D', '5' } or { 4, 0, 0, 0, 32, 4, 0, 15, 66, 64, 4, 0, 0, 2, 8, 1, 16, 'M', 'D', '5' } Registered: 16 Jul 2009 by Thomas Schmitt for libisofs. ------------------------------------------------------------------------------- Name: isofs.cs Purpose: Records the name of the character set that was used as output character set when writing the RRIP name tree of the ISO 9660 image. It shall be suitable as parameter for function iconv_open(3). This attribute shall be attached to the root directory entry and be global for the whole image. Format of Value: Shall hold the character set name without terminating 0-byte. Example: { 'I', 'S', 'O', '-', '8', '8', '5', '9' , '-', '1' } Registered: 18 Mar 2009 by Thomas Schmitt for libisofs. ------------------------------------------------------------------------------- Name: isofs.cx Purpose: Records the index of the file's checksum in the checksum area at the end of the image. The byte address of the checksum is checksum_area_lba * 2048 + isofs.cx * checksum_size Default checksum algorithm is MD5 with a size of 16 byte. See also isofs.ca . Format of Value: A byte string which begins with the most significant byte. Example: Index 123456 { 1, 226, 64 } Registered: 16 Jul 2009 by Thomas Schmitt for libisofs. ------------------------------------------------------------------------------- Name: isofs.di Purpose: Records .st_dev and .st_ino of struct stat of the file source in the local filesystem. See man 2 stat. Both values may be unsigned integers up to 255 bytes. Format of Value: DEV_LEN | DEV_BYTES | INO_LEN | INO_BYTES The _LEN fields comply to ISO 9660 Format section 7.1.1. The byte strings begin with the most significant byte. Example: Device number 2001, inode number 176343 { 2, 7, 209, 3, 2, 176, 215 } Registered: 17 Feb 2009 by Thomas Schmitt for xorriso. ------------------------------------------------------------------------------- Name: isofs.fa Purpose: Records the file attribute flags as of Linux program chattr(1) and ioctl(FS_IOC_GETFLAGS) in as bits in a byte string. Known from Debian GNU/Linux 8 to 12 are: bit0= FS_SECRM_FL 's' zero blocks on deletion + bit1= FS_UNRM_FL 'u' on delete prepare for undelete + bit2= FS_COMPR_FL 'c' compress + bit3= FS_SYNC_FL 'S' synchronous write + bit4= FS_IMMUTABLE_FL 'i' immutable +* bit5= FS_APPEND_FL 'a' appending write only +* bit6= FS_NODUMP_FL 'd' dump(8) shall ignore this file + bit7= FS_NOATIME_FL 'A' do not update atime + bit8= FS_DIRTY_FL 'Z' compressed dirty file bit9= FS_COMPRBLK_FL (? one or more compressed clusters) bit10= FS_NOCOMP_FL 'm' do not compress + bit11= FS_ECOMPR_FL 'E' compression error (old) FS_ENCRYPT_FL 'E' encrypted file (new) bit12= FS_BTREE_FL (? btree format dir) FS_INDEX_FL 'I' hash-indexed directory bit13= FS_IMAGIC_FL (? AFS directory) bit14= FS_JOURNAL_DATA_FL 'j' data journalling +* bit15= FS_NOTAIL_FL 't' no tail-merging + bit16= FS_DIRSYNC_FL 'D' synchronous directory updates +/ bit17= FS_TOPDIR_FL 'T' top of directory hierarchy +/ bit18= FS_HUGE_FILE_FL ('h' huge file ? 'h' old, FS_HUGE_FILE_FL new) bit19= FS_EXTENT_FL 'e' using extents bit20= FS_DIRECTIO_FL (? use direct i/o) (old) FS_VERITY_FL 'V' fs-verity enabled (new) bit21= FS_EA_INODE_FL (? Inode used for large EA) bit22= FS_EOFBLOCKS_FL (? reserved for ext4) bit23= FS_NOCOW_FL 'C' no copy on write + bit25= FS_DAX_FL 'x' direct access + bit28= FS_INLINE_DATA_FL 'N' data stored in inode bit29= FS_PROJINHERIT_FL 'P' project hierarchy +/ bit30= FS_CASEFOLD_FL 'F' case-insensitive directory lookups +/ bit31= FS_RESERVED_FL (? reserved for ext2 lib) Marks for supposed settability properties according to man chattr: "+" means "settable". "*" means "superuser power is needed". "/" means "for directories only". Format of Value: A byte string which begins with the most significant byte. Example: (FS_SECRM_FL|FS_APPEND_FL|FS_NOCOMP_FL) = 0x421 { 4 , 33 } Registered: 12 Jul 2024 by Thomas Schmitt for libisofs. ------------------------------------------------------------------------------- Name: isofs.hb Purpose: Records the IsoHfsplusBlessings blessing of a IsoNode as defined in libisofs.h. At image load time, this info may be converted back into a relation between IsoImage and IsoNode so that it is available for the HFS+ writer when a new ISO 9660 / HFS+ image gets produced. Format of Value: BLESSING This is a single byte out of {'p', 'i', 's', '9', 'x'} for ISO_HFSPLUS_BLESS_PPC_BOOTDIR, ISO_HFSPLUS_BLESS_INTEL_BOOTFILE, ISO_HFSPLUS_BLESS_SHOWFOLDER, ISO_HFSPLUS_BLESS_OS9_FOLDER, ISO_HFSPLUS_BLESS_OSX_FOLDER. Example: { 'p' } Registered: 07 Jun 2012 by Thomas Schmitt for xorriso. ------------------------------------------------------------------------------- Name: isofs.hx Purpose: Records the iso_hfsplus_xinfo_data information as defined in libisofs.h. At image load time, this info may be converted back into an xinfo attachment for iso_hfsplus_xinfo_func so that it is available for the HFS+ writer when a new ISO 9660 / HFS+ image gets produced. Format of Value: VERSION_LEN | VERSION | CREATOR | TYPE VERSION_LEN complies to ISO 9660 Format section 7.1.1. The byte string VERSION begins with the most significant byte. VERSION == 0 is the only one that is currently defined. It assures the existence of 4 bytes CREATOR and 4 bytes TYPE. Higher versions will keep these 8 bytes and possibly add new ones. Example: { 1, 0, 'Y', 'Y', 'D', 'N', 'T', 'E', 'X', 'T' } Registered: 07 Jun 2012 by Thomas Schmitt for xorriso. ------------------------------------------------------------------------------- Name: isofs.nt Purpose: Records the name truncation mode and the truncation length for Rock Ridge names. See iso_image_set_truncate_mode() in libisofs.h. This attribute shall be attached to the root directory entry and be global for the whole image. Format of Value: MODE_LEN | MODE_BYTES | LENGTH_LEN | LENGTH_BYTES Example: { 1, 1, 1, 255 } Registered: 24 Sep 2015 by Thomas Schmitt for libisofs. ------------------------------------------------------------------------------- Name: isofs.st Purpose: Records a time point at least 1 second before any nodes were added to a freshly loaded or created ISO image. Nodes in the image which have younger timestamps are suspect to have changed their content during image production and might bear inconsistent content. The RRIP timestamps have a blind second during which a change after node registration would not be recognizable for incremental backups which are based in "isofs.di" rather than on content comparison. This attribute shall be attached to the root directory entry and be global for the whole image. Format of Value: Shall hold UTC seconds since 1970 as decimal number string without terminating 0-byte. Example: { '1', '2', '3', '8', '7', '4', '2', '2', '9', '6' } Registered: 03 Apr 2009 by Thomas Schmitt for xorriso. ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- This text is under Copyright (c) 2009 - 2024 Thomas Schmitt It shall only be modified in sync with libisofs and other software which makes use of AAIP. Please mail change requests to mailing list or to the copyright holder in private. Only if you cannot reach the copyright holder for at least one month it is permissible to modify this text under the same license as the affected copy of libisofs. Currently: GNU General Public License version 2 or later. If you do so, you commit yourself to taking reasonable effort to stay in sync with the other interested users of this text. Description of the zisofs2 Format Revision 2.0-dev as of zisofs2-tools by Valentín KIVACHUK BURDà and Thomas SCHMITT 1 Oct 2020 The zisofs2 format was invented by Valentín KIVACHUK BURDà and Thomas SCHMITT (as extension of zisofs by H. Peter Anvin). It compresses data file content, marks it by a header and provides a pointer array for coarse random access. Within a RRIP enhanced ISO 9660 image the format is additionally marked by a System Use entry with signature "ZF" or "Z2". The uncompressed size of a single zisofs2 compressed file is restricted to 2^64 - 1 bytes. Larger files shall not be compressed. The format of version 1 of zisofs is supported by this specification. Using it for files with uncompressed size smaller than 4 GiB is friendly towards software which does not know about zisofs2. See section **LEGACY** for a summary of version 1 of zisofs. Data Types ISO 9660:7.3.1 - little endian 4-byte words ISO 9660:7.1.1 - unsigned single bytes ISO 9660:7.3.3 - 8-bytes value, first in little endian, then big endian. #uint64 - 8-bytes unsigned value in little endian Supported compressors The file header has this layout: @alg_id @alg_char Description 1 'PZ' (50)(5A) Zlib 2 'XZ' (78)(7A) XZ 3 'L4' (6C)(34) LZ4 4 'ZD' (7A)(64) Zstandard 5 'B2' (62)(32) Bzip2 @alg_id is a 7.1.1 value. @alg_char is 2 ASCII characters stored as 2 bytes Values of @alg_id = 0 and @alg_char = 'pz'(70)(7A) are reserved and must not be used. Other compressors are allowed and may be added to this list in the future Compressor strategy The default strategy for a compressor is to compress each input data block independently. The zisofs2 spec may define in the future other strategies, which will have a new @alg_id, @alg_char and a description in this section. File Header The file header has this layout: Offset Type Identifier Contents -------------------------------------------------------------------------- 0 (8 bytes) @hdr_magic Magic num (EF 22 55 A1 BC 1B 95 A0) 8 7.1.1 @hdr_version File header version (0) 9 7.1.1 @hdr_size header_size >> 2 (6) 10 7.1.1 @alg_id Algorithm Type (>=1) 11 7.1.1 @hdr_bsize log2(block_size) (15, 16, or 17) 12 #uint64 @size Uncompressed file size 20 (4 bytes) - Padding. Ignored So its size is 24. Readers shall be able to handle log2(block_size) values 15, 16 and 17 i.e. block sizes 32 kB, 64 kB, and 128 kB. Writers must not use other sizes. Block Pointers There are ceil(input_size / block_size) input resp. output blocks. Each input block is of fixed size whereas the output blocks have varying size (down to 0). For each output block there is an offset pointer giving its byte address in the overall file content. The next block pointer in the array tells the start of the next block which begins immediately after the end of its predecessor. A final pointer (*eob*) gives the first invalid byte address and thus marks the end of the last block. So there are ceil(input_size / block_size) + 1 block pointers. They are stored directly after the file header, i.e. beginning at byte 24, as an array of values in #uint64 format (8 bytes). Legacy format (zisofs) may be used, which is described in section *LEGACY* Data Part The data part begins immediately after the pointer array (*eob*). In principle it consists of the variable length output blocks as delivered by different compression algorithms when fed with the fixed size input blocks. A special case of input and output block is defined: Zero-length blocks represent a block full of 0-bytes. Such input blocks do not get processed by compress2() but shall be mapped to 0-sized output directly. Vice versa 0-sized blocks have to bypass uncompress() when being read. ZF System Use Entry Format The ZF entry follows the general layout of SUSP resp. RRIP. Its fields are: [1] "BP 1 to BP 2 - Signature Word" shall be (5A)(46) ("ZF"). [2] "BP 3 - Length" shall specify as an 8-bit number the length in bytes of the ZF entry recorded according to ISO 9660:7.1.1. This length is 16 decimal. Refer to **LEGACY** [3] "BP 4 - System Use Entry Version" shall be 2 as in ISO 9660:7.1.1. Refer to **LEGACY** [4] "BP 5 to BP 6 - Algorithm" shall be two chars to indicate the compression algorithm. For example, (50)(5A) ("PZ") (This is a copy of @alg_char). Refer to **LEGACY** [5] "BP 7 - Header Size Div 4" shall specify as an 8-bit number the number of 4-byte words in the header part of the file data recorded according to ISO 9660:7.1.1. (This is a copy of @hdr_size). [6] "BP 8 - Log2 of Block Size" shall specify as an 8-bit number the binary logarithm of the compression block size recorded according to ISO 9660:7.1.1. (This is a copy of header byte 13 (@hdr_bsize), resp. header BP 14. The value has to be 15, 16 or 17 i.e. 32 kiB, 64 kiB, or 128 kiB.) [7] "BP 9 to BP 16 - Virtual Uncompressed File Size" shall contain as a 64-bit unsigned little endian number the uncompressed file size represented by the given extent. Refer to **LEGACY** | 'Z' | 'F' | LENGTH | 2 | 'P' | 'Z' | HEADER SIZE DIV 4 | | LOG2 BLOCK SIZE | UNCOMPRESSED SIZE | Example (block size 128 kiB, uncompressed file size = 40 TB): { 'Z', 'F', 16, 2, 'P', 'Z', 8, 17, 0x00, 0x80, 0xCA, 0x39, 0x61, 0x24, 0x00, 0x00 } Z2 System Use Entry Format Linux kernels which are configured by CONFIG_ZISOFS to recognize zisofs but are not aware of zisofs2 will complain about ZF entries which announce algorithms other than "pz". The system log will show for each zisofs2 compressed file at first stat(2) or open(2) a line like this: isofs: Unknown ZF compression algorithm: PZ To avoid these complaints, it is possible to use [1] "BP 1 to BP 2 - Signature Word" shall be (5A)(32) ("Z2"). instead of "ZF" with the System Use Entry Format specified above. Everything else shall be like in ZF format version 2, including the version number itself: [3] "BP 4 - System Use Entry Version" shall be 2 as in ISO 9660:7.1.1. **LEGACY** zisofs2 supports old readers by respecting the zisofs format. This section describes which definitions from zisofs2 must change to be compatible with zisofs. - General behaviour The uncompressed size of a single zisofs compressed file is restricted to 4 GiB - 1. Larger files shall not be compressed. - Supported algorithms Only algorithm Zlib with default strategy is supported. - The file header must follow this structure: Offset Type Identifier Contents 0 (8 bytes) @hdr_magic Magic number (37 E4 53 96 C9 DB D6 07) 8 7.3.1 @size Uncompressed file size 12 7.1.1 @hdr_size header_size >> 2 (4) 13 7.1.1 @hdr_bsize log2(block_size) (15, 16, or 17) 14 (2 bytes) - Reserved, must be zero So its size is 16. - Block pointers The array must use ISO 9660:7.3.1 (4 bytes) values. - ZF entry Its fields are: [1] "BP 1 to BP 2 - Signature Word" shall be (5A)(46) ("ZF"). [2] "BP 3 - Length" must be 16 decimal. [3] "BP 4 - System Use Entry Version" must be 1. [4] "BP 5 to BP 6 - Algorithm" must be (70)(7A) ("pz"). [5] "BP 7 - Header Size Div 4" - same as zisofs2. [6] "BP 8 - Log2 of Block Size" - same as zisofs2. [7] "BP 9 to BP 16 - Uncompressed Size" This field shall be recorded according to ISO 9660:7.3.3. (This number is the same as @size ) | 'Z' | 'F' | LENGTH | 1 | 'p' | 'z' | HEADER SIZE DIV 4 | | LOG2 BLOCK SIZE | UNCOMPRESSED SIZE | Example (block size 32 kiB, uncompressed file size = 1,234,567 bytes): { 'Z', 'F', 16, 1, 'p', 'z', 4, 15, 0x87, 0xD6, 0x12, 0x00, 0x00, 0x12, 0xD6, 0x87 } References: zisofs2-tools https://github.com/vk496/zisofs2-tools zisofs-tools http://freshmeat.net/projects/zisofs-tools/ zlib: /usr/include/zlib.h cdrtools with mkisofs ftp://ftp.berlios.de/pub/cdrecord/alpha ECMA-119 aka ISO 9660 http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf SUSP 1.12 ftp://ftp.ymi.com/pub/rockridge/susp112.ps RRIP 1.12 ftp://ftp.ymi.com/pub/rockridge/rrip112.ps zisofs version 1 libisofs-*/doc/zisofs_format.txt https://dev.lovelyhq.com/libburnia/libisofs/raw/branch/master/doc/zisofs_format.txt --------------------------------------------------------------------------- This text is under Copyright (c) 2009 - 2010, 2020 Thomas SCHMITT Copyright (c) 2020 - Valentín KIVACHUK BURDà It shall reflect the effective technical specifications as implemented in zisofs2-tools and the Linux kernel. So please contact mailing list or to the copyright holders in private, if you want to make changes. Only if you cannot reach the copyright holder for at least one month it is permissible to modify and distribute this text under the license "GPLv3". Description of the zisofs Format as of zisofs-tools-1.0.8 by H. Peter Anvin and cdrtools-2.01.01a39 by Joerg Schilling For libburnia-project.org by Thomas Schmitt - distribute freely , please report any errors or ambiguities - Apr 11 2009 The zisofs format was invented by H. Peter Anvin. It compresses data file content, marks it by a header and provides a pointer array for coarse random access. Within a RRIP enhanced ISO 9660 image the format is additionally marked by a System Use entry with signature "ZF". The uncompressed size of a single zisofs compressed file is restricted to 4 GiB - 1. Larger files shall not be compressed. File Header The file header has this layout (quoted from zisofs-tools-1.0.8/mkzftree.c): Byte offset iso9660 type Contents 0 (8 bytes) Magic number (37 E4 53 96 C9 DB D6 07) 8 7.3.1 Uncompressed file size 12 7.1.1 header_size >> 2 (currently 4) 13 7.1.1 log2(block_size) 14 (2 bytes) Reserved, must be zero So its size is 16. 7.3.1 means little endian 4-byte words. 7.1.1. means unsigned single bytes. Readers shall be able to handle log2(block_size) values 15, 16 and 17 i.e. block sizes 32 kB, 64 kB, and 128 kB. Writers must not use other sizes. Block Pointers There are ceil(input_size / block_size) input and output blocks. Each input block is of fixed size whereas the output blocks have varying size (down to 0). For each output block there is an offset pointer giving its byte address in the overall file content. The next block pointer in the array tells the start of the next block which begins immediately after the end of its predecessor. A final pointer gives the first invalid byte address and thus marks the end of the last block. So there are ceil(input_size / block_size) + 1 block pointers. They are stored as an array of 4-byte values which are in ISO 9660:7.3.1 format directly after the file header, i.e. beginning at byte 16. Data Part The data part begins immediately after the pointer array. In principle it consists of the variable length output blocks as delivered by zlib function compress2() when fed with the fixed size input blocks. A special case of input and output block is defined: Zero-length blocks represent a block full of 0-bytes. Such input blocks do not get processed by compress2() but shall be mapped to 0-sized output directly. Vice versa 0-sized blocks have to bypass uncompress() when being read. ZF System Use Entry Format ZF may only be applied to files with a single extent and less than 4 GiB of uncompressed size. The ZF entry follows the general layout of SUSP and RRIP. Its fields are: [1] "BP 1 to BP 2 - Signature Word" shall be (5A)(46) ("ZF"). [2] "BP 3 - Length" shall specify as an 8-bit number the length in bytes of the ZF entry recorded according to ISO 9660:7.1.1. This length is 16 decimal. [3] "BP 4 - System Use Entry Version" shall be 1 as in ISO 9660:7.1.1. [4] "BP 5 to BP 6 - Algorithm" shall be (70)(7A) ("pz") to indicate "paged zlib". [5] "BP 7 - Header Size Div 4" shall specify as an 8-bit number the number of 4-byte words in the header part of the file data recorded according to ISO 9660:7.1.1. (This is a copy of header byte 12 / BP 13). [6] "BP 8 - Log2 of Block Size" shall specify as an 8-bit number the binary logarithm of the compression block size recorded according to ISO 9660:7.1.1. (This is a copy of header byte 13 / BP 14. The value has to be 15, 16 or 17 i.e. 32 kiB, 64 kiB, or 128 kiB.) [7] "BP 9 to BP 16 - Uncompressed Size" shall tell the number of uncompressed bytes represented by the given extent. This field shall be recorded according to ISO 9660:7.3.3. (This number is the same as in header bytes 8 to 11 / BP 9 to BP 12.) | 'Z' | 'F' | LENGTH | 1 | 'p' | 'z' | HEADER SIZE DIV 4 | LOG2 BLOCK SIZE | UNCOMPRESSED SIZE | ISO 9660:7.3.3 means 4-byte word in both byte orders, first little endian, then big endian. Example (block size 32 kiB, uncompressed file size = 1,234,567 bytes): { 'Z', "F', 16, 1, 'p', 'z', 4, 15, 0x87, 0xD6, 0x12, 0x00, 0x00, 0x12, 0xD6, 0x87 } ------------------------------------------------------------------------------- Revoked specification aspects: A comment in zisofs-tools-1.0.8 indicates a special case of output block: "a block the length of which is equal to the block size is unencoded." This is not implemented in zisofs-tools and in the Linux kernel. Existing zisofs enhanced ISO images might contain encoded blocks which could be mistaken for unencoded blocks. Therefore this rule is not part of this description and must not be implemented. ------------------------------------------------------------------------------- References: zisofs-tools http://freshmeat.net/projects/zisofs-tools/ zlib: /usr/include/zlib.h cdrtools with mkisofs ftp://ftp.berlios.de/pub/cdrecord/alpha ECMA-119 aka ISO 9660 http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf SUSP 1.12 ftp://ftp.ymi.com/pub/rockridge/susp112.ps RRIP 1.12 ftp://ftp.ymi.com/pub/rockridge/rrip112.ps ------------------------------------------------------------------------------- This text is under Copyright (c) 2009 - 2010 Thomas Schmitt It shall reflect the effective technical specifications as implemented in zisofs-tools and the Linux kernel. So please contact mailing list or to the copyright holder in private, if you want to make changes. Only if you cannot reach the copyright holder for at least one month it is permissible to modify and distribute this text under the license "BSD revised". prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libisofs Description: ISO9660 filesystem creation library Version: @VERSION@ Requires: Libs: -L${libdir} -lisofs Cflags: -I${includedir}/libisofs /* aaip-os-dummy.c Idle placeholder for: Arbitrary Attribute Interchange Protocol , system adapter for getting and setting of ACLs and xattr. See aaip-os-linux.c for a real implementation of this interface. To be included by aaip_0_2.c Copyright (c) 2009 - 2024 Thomas Schmitt This file is part of the libisofs project; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later as published by the Free Software Foundation. See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include #include #include #include #include #include /* ------------------------------ Inquiry --------------------------------- */ /* See also API iso_local_attr_support(). @param flag Bitfield for control purposes bit0= inquire availability of ACL bit1= inquire availability of xattr bit2 - bit7= Reserved for future types. It is permissibile to set them to 1 already now. bit8 and higher: reserved, submit 0 @return Bitfield corresponding to flag. bit0= ACL adapter is enabled bit1= xattr adapter is enabled bit2 - bit7= Reserved for future types. bit8 and higher: reserved, do not interpret these */ int aaip_local_attr_support(int flag) { return(0); } /* ------------------------------ Getters --------------------------------- */ /* Obtain the ACL of the given file in long text form. @return 0 ACL support not enabled at compile time */ int aaip_get_acl_text(char *path, char **text, int flag) { return(0); } /* Obtain the Extended Attributes and/or the ACLs of the given file in a form that is ready for aaip_encode(). @return 1 ok */ int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag) { *num_attrs= 0; *names= NULL; *value_lengths= NULL; *values= NULL; return(1); } /* Obtain the file attribute flags of the given file as bit array in uint64_t. The bit numbers are compatible to the FS_*_FL definitions in Linux. */ int aaip_get_lfa_flags(char *path, uint64_t *lfa_flags, int *max_bit, int *os_errno, int flag) { *lfa_flags= 0; *max_bit= -1; *os_errno= 0; return(0); } /* ------------------------------ Setters --------------------------------- */ /* Set the ACL of the given file to a given list in long text form. @return 0 ACL support not enabled at compile time */ int aaip_set_acl_text(char *path, char *text, int flag) { return(0); } /* Bring the given attributes and/or ACLs into effect with the given file. @param flag Bitfield for control purposes bit0= decode and set ACLs bit1= first clear all existing attributes of the file bit2= do not set attributes other than ACLs @return 1 success (there was nothing to do) -6 support of xattr not enabled at compile time -7 support of ACL not enabled at compile time */ int aaip_set_attr_list(char *path, size_t num_attrs, char **names, size_t *value_lengths, char **values, int *errnos, int flag) { size_t i; for(i= 0; i < num_attrs; i++) errnos[i]= 0; for(i= 0; i < num_attrs; i++) { if(names[i] == NULL || values[i] == NULL) continue; if(names[i][0] == 0) { /* ACLs */ if(flag & 1) return(-7); continue; } /* Extended Attribute */ if(flag & 4) continue; if(!(flag & 8)) if(strncmp(names[i], "user.", 5)) continue; return(-6); } if(flag & 2) return(-6); return(1); } int aaip_set_lfa_flags(char *path, uint64_t lfa_flags, int max_bit, int *os_errno, int flag) { *os_errno= 0; return(0); } /* aaip-os-freebsd.c Arbitrary Attribute Interchange Protocol , system adapter for getting and setting of ACLs and xattr. To be included by aaip_0_2.c for FreeBSD, NetBSD, and OpenBSD Copyright (c) 2009 - 2024 Thomas Schmitt This file is part of the libisofs project; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later as published by the Free Software Foundation. See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include #include #include #include #include #include #include #ifdef Libisofs_with_aaip_acL #include #endif #ifdef Libisofs_with_freebsd_extattR #include #endif #include /* <<< Use old ACL adapter code that is unable to deal with extattr */ /* # define Libisofs_old_freebsd_acl_adapteR */ /* ------------------------------ Inquiry --------------------------------- */ /* See also API iso_local_attr_support(). @param flag Bitfield for control purposes bit0= inquire availability of ACL bit1= inquire availability of xattr bit2 - bit7= Reserved for future types. It is permissibile to set them to 1 already now. bit8 and higher: reserved, submit 0 @return Bitfield corresponding to flag. bit0= ACL adapter is enabled bit1= xattr adapter is enabled bit2 - bit7= Reserved for future types. bit8 and higher: reserved, do not interpret these */ int aaip_local_attr_support(int flag) { int ret= 0; #ifdef Libisofs_with_aaip_acL if(flag & 1) ret|= 1; #endif #ifdef Libisofs_with_freebsd_extattR if(flag & 2) ret|= 2; #endif return(ret); } #ifdef Libisofs_with_freebsd_extattR static int aaip_extattr_path_supp(char *path, int flag) { #ifdef MNT_EXTATTR int ret; struct statvfs statvfs_buf; ret = statvfs(path, &statvfs_buf); if(ret == -1) return(1); return(!!(statvfs_buf.f_flag & MNT_EXTATTR)); #else /* MNT_EXTATTR */ return(1); #endif /* ! MNT_EXTATTR */ } #endif /* Libisofs_with_freebsd_extattR */ /* ------------------------------ Getters --------------------------------- */ /* Obtain the ACL of the given file in long text form. @param path Path to the file @param text Will hold the result. This is a managed object which finally has to be freed by a call to this function with bit15 of flag. @param flag Bitfield for control purposes (bit0= obtain default ACL rather than access ACL) bit4= set *text = NULL and return 2 if the ACL matches st_mode permissions. bit5= in case of symbolic link: inquire link target bit15= free text and return 1 @return > 0 ok 0 ACL support not enabled at compile time or filesystem does not support ACL -1 failure of system ACL service (see errno) -2 attempt to inquire ACL of a symbolic link without bit4 or bit5 */ int aaip_get_acl_text(char *path, char **text, int flag) { #ifdef Libisofs_with_aaip_acL acl_t acl= NULL; #endif struct stat stbuf; int ret; if(flag & (1 << 15)) { if(*text != NULL) #ifdef Libisofs_with_aaip_acL acl_free(*text); #else free(*text); #endif *text= NULL; return(1); } *text= NULL; if(flag & 32) ret= stat(path, &stbuf); else ret= lstat(path, &stbuf); if(ret == -1) return(-1); if((stbuf.st_mode & S_IFMT) == S_IFLNK) { if(flag & 16) return(2); return(-2); } /* Note: no ACL_TYPE_DEFAULT in FreeBSD */ if(flag & 1) return(0); #ifdef Libisofs_with_aaip_acL acl= acl_get_file(path, ACL_TYPE_ACCESS); if(acl == NULL) { if(errno == EOPNOTSUPP) { /* filesystem does not support ACL */ if(flag & 16) return(2); /* >>> ??? fake ACL from POSIX permissions ? */; return(0); } return(-1); } *text= acl_to_text(acl, NULL); acl_free(acl); #else /* Libisofs_with_aaip_acL */ /* ??? >>> Fake ACL */; return(0); #endif /* ! Libisofs_with_aaip_acL */ if(*text == NULL) return(-1); if(flag & 16) { ret = aaip_cleanout_st_mode(*text, &(stbuf.st_mode), 2); if(!(ret & (7 | 64))) (*text)[0]= 0; if((*text)[0] == 0 || strcmp(*text, "\n") == 0) { #ifdef Libisofs_with_aaip_acL acl_free(*text); #else free(*text); #endif *text= NULL; return(2); } } return(1); } #ifndef Libisofs_old_freebsd_acl_adapteR #ifdef Libisofs_with_freebsd_extattR /* @param flag Bitfield for control purposes bit5= in case of symbolic link: inquire link target */ static int aaip_extattr_make_list(char *path, int attrnamespace, char **list, ssize_t *list_size, int flag) { *list= NULL; *list_size= 0; /* man 2 extattr_list_file: If data is NULL in a call to extattr_get_file() and extattr_list_file() then the size of defined extended attribute data will be returned, */ if(flag & 32) /* follow link */ *list_size= extattr_list_file(path, attrnamespace, NULL, (size_t) 0); else *list_size= extattr_list_link(path, attrnamespace, NULL, (size_t) 0); if(*list_size == -1) { if(! aaip_extattr_path_supp(path, 0)) { *list_size = 0; return(2); } if(errno == EPERM && attrnamespace == EXTATTR_NAMESPACE_SYSTEM) { *list_size = 0; return(3); } return(0); } if(*list_size == 0) return(2); *list= calloc(*list_size, 1); if(*list == NULL) return(-1); if(flag & 32) *list_size= extattr_list_file(path, attrnamespace, *list, (size_t) *list_size); else *list_size= extattr_list_link(path, attrnamespace, *list, (size_t) *list_size); if(*list_size == -1) return(0); return(1); } /* @param flag Bitfield for control purposes bit0= preserve existing namelist content bit1= ignore names with NUL rather than returning error */ static int aaip_extattr_make_namelist(char *path, char *attrnamespace, char *list, ssize_t list_size, char **namelist, ssize_t *namelist_size, ssize_t *num_names, int flag) { int i, j, len, new_bytes= 0, space_len; char *new_list= NULL, *wpt; if(!(flag & 1)) { *namelist= NULL; *namelist_size= 0; *num_names= 0; } if(list_size <= 0) return(1); space_len= strlen(attrnamespace); for(i= 0; i < list_size; i+= len + 1) { len= *((unsigned char *) (list + i)); if(len == 0) return ISO_AAIP_BAD_ATTR_NAME; /* empty name is reserved for ACL */ for(j= 0; j < len; j++) if(list[i + 1 + j] == 0) { if(flag & 2) continue; return ISO_AAIP_BAD_ATTR_NAME; /* names may not contain 0-bytes */ } new_bytes+= space_len + 1 + len + 1; } if((flag & 1) && *namelist_size > 0) new_bytes+= *namelist_size; new_list= calloc(new_bytes, 1); if(new_list == NULL) return(ISO_OUT_OF_MEM); wpt= new_list; if((flag & 1) && *namelist_size > 0) { memcpy(new_list, *namelist, *namelist_size); wpt= new_list + *namelist_size; } for(i= 0; i < list_size; i+= len + 1) { len= *((unsigned char *) (list + i)); if(flag & 2) { for(j= 0; j < len; j++) if(list[i + j] == 0) continue; } memcpy(wpt, attrnamespace, space_len); wpt[space_len]= '.'; wpt+= space_len + 1; memcpy(wpt, list + i + 1, len); wpt+= len; *(wpt++)= 0; (*num_names)++; } if((flag & 1) && *namelist != NULL) free(*namelist); *namelist= new_list; *namelist_size= new_bytes; return(1); } static int get_single_attr(char *path, char *name, size_t *value_length, char **value_bytes, int flag) { char *namept; int attrnamespace; ssize_t value_ret; *value_bytes= NULL; *value_length= 0; if(strncmp(name, "user.", 5) == 0) { attrnamespace= EXTATTR_NAMESPACE_USER; namept= name + 5; } else { if(!(flag & 8)) return(0); attrnamespace= EXTATTR_NAMESPACE_SYSTEM; namept= name + 7; } /* Predict length of value */ if(flag & 32) /* follow link */ value_ret= extattr_get_file(path, attrnamespace, namept, NULL, (size_t) 0); else value_ret= extattr_get_link(path, attrnamespace, namept, NULL, (size_t) 0); if(value_ret == -1) return(0); *value_bytes= calloc(value_ret + 1, 1); if(*value_bytes == NULL) return(-1); /* Obtain value */ if(flag & 32) /* follow link */ value_ret= extattr_get_file(path, attrnamespace, namept, *value_bytes, (size_t) value_ret); else value_ret= extattr_get_link(path, attrnamespace, namept, *value_bytes, (size_t) value_ret); if(value_ret == -1) { free(*value_bytes); *value_bytes= NULL; *value_length= 0; return(0); } *value_length= value_ret; return(1); } #endif /* Libisofs_with_freebsd_extattR */ /* Obtain the Extended Attributes and/or the ACLs of the given file in a form that is ready for aaip_encode(). @param path Path to the file @param num_attrs Will return the number of name-value pairs @param names Will return an array of pointers to 0-terminated names @param value_lengths Will return an array with the lengths of values @param values Will return an array of pointers to 8-bit values @param flag Bitfield for control purposes bit0= obtain ACL (access and eventually default) bit1= use numeric ACL qualifiers rather than names bit2= do not obtain attributes other than ACL bit3= do not ignore eventual non-user attributes I.e. those with a name which does not begin by "user." bit4= do not return trivial ACL that matches st_mode bit5= in case of symbolic link: inquire link target bit6= do not obtain Linux style file attribute flags (chattr). This obtaining is not implemented here anyways. bit15= free memory of names, value_lengths, values @return 1 ok 2 ok, no permission to inspect non-user namespaces <=0 error -1= out of memory -2= program error with prediction of result size -3= error with conversion of name to uid or gid */ int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag) { int ret; ssize_t i, num_names= 0; #ifdef Libisofs_with_aaip_acL unsigned char *a_acl= NULL; char *a_acl_text= NULL; size_t a_acl_len= 0; #endif #ifdef Libisofs_with_freebsd_extattR char *list= NULL, *user_list= NULL, *sys_list= NULL; ssize_t value_ret, list_size= 0, user_list_size= 0; ssize_t sys_list_size= 0; int acl_names= 0; #endif int no_perm_for_system= 0; if(flag & (1 << 15)) { /* Free memory */ {ret= 1; goto ex;} } *num_attrs= 0; *names= NULL; *value_lengths= NULL; *values= NULL; /* Set up arrays */ #ifdef Libisofs_with_freebsd_extattR if(!(flag & 4)) { /* Get extattr names */ /* Linux : Names are encoded as name NUL FreeBSD: Names are encoded as length_byte:chars (no NUL) AAIP demands names not to contain NUL bytes. */ /* Obtain lists of names Must be done separately for namespaces. See man 9 extattr : EXTATTR_NAMESPACE_USER , EXTATTR_NAMESPACE_SYSTEM Must then be marked by "user." and "system." for libisofs use. */ ret= aaip_extattr_make_list(path, EXTATTR_NAMESPACE_USER, &user_list, &user_list_size, flag & 32); if(ret <= 0) {ret= -1; goto ex;} if(flag & 8) { ret= aaip_extattr_make_list(path, EXTATTR_NAMESPACE_SYSTEM, &sys_list, &sys_list_size, flag & 32); if(ret <= 0) {ret= -1; goto ex;} if(ret == 3) no_perm_for_system= 1; } /* Check for NUL in names, convert into a linuxish list of namespace.name */ ret= aaip_extattr_make_namelist(path, "user", user_list, user_list_size, &list, &list_size, &num_names, 0); if(ret <= 0) goto ex; ret= aaip_extattr_make_namelist(path, "system", sys_list, sys_list_size, &list, &list_size, &num_names, 1); if(ret <= 0) goto ex; } #endif /* Libisofs_with_freebsd_extattR */ #ifdef Libisofs_with_aaip_acL if(flag & 1) { num_names++; #ifdef Libisofs_with_freebsd_extattR acl_names= 1; #endif } #endif if(num_names == 0) {ret= 1; goto ex;} (*names)= calloc(num_names, sizeof(char *)); (*value_lengths)= calloc(num_names, sizeof(size_t)); (*values)= calloc(num_names, sizeof(char *)); if(*names == NULL || *value_lengths == NULL || *values == NULL) {ret= -1; goto ex;} for(i= 0; i < num_names; i++) { (*names)[i]= NULL; (*values)[i]= NULL; (*value_lengths)[i]= 0; } #ifdef Libisofs_with_freebsd_extattR if(!(flag & 4)) { /* Get xattr values */ for(i= 0; i < list_size && (size_t) num_names - acl_names > *num_attrs; i+= strlen(list + i) + 1) { if(!(flag & 8)) if(strncmp(list + i, "user.", 5)) continue; (*names)[(*num_attrs)++]= strdup(list + i); if((*names)[(*num_attrs) - 1] == NULL) {ret= -1; goto ex;} } for(i= 0; (size_t) i < *num_attrs; i++) { value_ret= get_single_attr(path, (*names)[i], *value_lengths + i, *values + i, flag & (8 | 32)); if(value_ret <= 0) {ret= -1; goto ex;} } } #endif /* Libisofs_with_freebsd_extattR */ #ifdef Libisofs_with_aaip_acL if(flag & 1) { /* Obtain ACL */ /* access-ACL */ aaip_get_acl_text(path, &a_acl_text, flag & (16 | 32)); if(a_acl_text == NULL) { /* empty ACL / only st_mode info was found in ACL */ ret= 1 + no_perm_for_system; goto ex; } ret= aaip_encode_acl(a_acl_text, (mode_t) 0, &a_acl_len, &a_acl, flag & 2); if(ret <= 0) goto ex; /* Note: There are no default-ACL in FreeBSD */ /* Set as attribute with empty name */; (*names)[*num_attrs]= strdup(""); if((*names)[*num_attrs] == NULL) {ret= -1; goto ex;} (*values)[*num_attrs]= (char *) a_acl; a_acl= NULL; (*value_lengths)[*num_attrs]= a_acl_len; (*num_attrs)++; } #endif /* Libisofs_with_aaip_acL */ ret= 1 + no_perm_for_system; ex:; #ifdef Libisofs_with_aaip_acL if(a_acl != NULL) free(a_acl); if(a_acl_text != NULL) aaip_get_acl_text("", &a_acl_text, 1 << 15); /* free */ #endif #ifdef Libisofs_with_freebsd_extattR if(list != NULL) free(list); if(user_list != NULL) free(user_list); if(sys_list != NULL) free(sys_list); #endif if(ret <= 0 || (flag & (1 << 15))) { if(*names != NULL) { for(i= 0; (size_t) i < *num_attrs; i++) free((*names)[i]); free(*names); } *names= NULL; if(*value_lengths != NULL) free(*value_lengths); *value_lengths= NULL; if(*values != NULL) { for(i= 0; (size_t) i < *num_attrs; i++) free((*values)[i]); free(*values); } *values= NULL; *num_attrs= 0; } return(ret); } #else /* ! Libisofs_old_freebsd_acl_adapteR */ /* Obtain the Extended Attributes and/or the ACLs of the given file in a form that is ready for aaip_encode(). Note: There are no Extended Attributes in FreeBSD. So only ACL will be obtained. @param path Path to the file @param num_attrs Will return the number of name-value pairs @param names Will return an array of pointers to 0-terminated names @param value_lengths Will return an array with the lengths of values @param values Will return an array of pointers to 8-bit values @param flag Bitfield for control purposes bit0= obtain ACL (access and eventually default) bit1= use numeric ACL qualifiers rather than names bit2= do not encode attributes other than ACL bit3= do not ignore eventual non-user attributes I.e. those which are not from name space EXTATTR_NAMESPACE_USER bit4= do not return trivial ACL that matches st_mode bit5= in case of symbolic link: inquire link target bit6= do not obtain Linux style file attribute flags (chattr). This obtaining is not implemented here anyways. bit15= free memory of names, value_lengths, values @return >0 ok <=0 error -1= out of memory -2= program error with prediction of result size -3= error with conversion of name to uid or gid */ int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag) { int ret; ssize_t i, num_names; #ifdef Libisofs_with_aaip_acL size_t a_acl_len= 0; unsigned char *a_acl= NULL; char *acl_text= NULL; #endif if(flag & (1 << 15)) { /* Free memory */ {ret= 1; goto ex;} } *num_attrs= 0; *names= NULL; *value_lengths= NULL; *values= NULL; num_names= 0; if(flag & 1) num_names++; if(num_names == 0) {ret= 1; goto ex;} (*names)= calloc(num_names, sizeof(char *)); (*value_lengths)= calloc(num_names, sizeof(size_t)); (*values)= calloc(num_names, sizeof(char *)); if(*names == NULL || *value_lengths == NULL || *values == NULL) {ret= -1; goto ex;} for(i= 0; i < num_names; i++) { (*names)[i]= NULL; (*values)[i]= NULL; (*value_lengths)[i]= 0; } #ifdef Libisofs_with_aaip_acL if(flag & 1) { /* Obtain ACL */ /* access-ACL */ aaip_get_acl_text(path, &acl_text, flag & (16 | 32)); if(acl_text == NULL) {ret= 1; goto ex;} /* empty ACL / only st_mode info was found in ACL */ ret= aaip_encode_acl(acl_text, (mode_t) 0, &a_acl_len, &a_acl, flag & 2); if(ret <= 0) goto ex; aaip_get_acl_text("", &acl_text, 1 << 15); /* free */ /* Note: There are no default-ACL in FreeBSD */ /* Set as attribute with empty name */; (*names)[*num_attrs]= strdup(""); if((*names)[*num_attrs] == NULL) {ret= -1; goto ex;} (*values)[*num_attrs]= (char *) a_acl; a_acl= NULL; (*value_lengths)[*num_attrs]= a_acl_len; (*num_attrs)++; } #endif /* ! Libisofs_with_aaip_acL */ ret= 1; ex:; #ifdef Libisofs_with_aaip_acL if(a_acl != NULL) free(a_acl); if(acl_text != NULL) aaip_get_acl_text("", &acl_text, 1 << 15); /* free */ #endif /* Libisofs_with_aaip_acL */ if(ret <= 0 || (flag & (1 << 15))) { if(*names != NULL) { for(i= 0; i < (ssize_t) *num_attrs; i++) free((*names)[i]); free(*names); } *names= NULL; if(*value_lengths != NULL) free(*value_lengths); *value_lengths= NULL; if(*values != NULL) { for(i= 0; i < (ssize_t) *num_attrs; i++) free((*values)[i]); free(*values); } *values= NULL; *num_attrs= 0; } return(ret); } #endif /* Libisofs_old_freebsd_acl_adapteR */ /* Obtain the file attribute flags of the given file as bit array in uint64_t. The bit numbers are compatible to the FS_*_FL definitions in Linux. */ int aaip_get_lfa_flags(char *path, uint64_t *lfa_flags, int *max_bit, int *os_errno, int flag) { *lfa_flags= 0; *max_bit= -1; *os_errno= 0; return(0); } /* ------------------------------ Setters --------------------------------- */ /* Set the ACL of the given file to a given list in long text form. @param path Path to the file @param text The input text (0 terminated, ACL long text form) @param flag Bitfield for control purposes bit0= set default ACL rather than access ACL bit5= in case of symbolic link: manipulate link target bit6= tolerate inappropriate presence or absence of directory default ACL @return > 0 ok 0 no suitable ACL manipulation adapter available -1 failure of system ACL service (see errno) -2 attempt to manipulate ACL of a symbolic link without bit5 or with no suitable link target */ int aaip_set_acl_text(char *path, char *text, int flag) { #ifdef Libisofs_with_aaip_acL int ret; acl_t acl= NULL; struct stat stbuf; if(flag & 32) ret= stat(path, &stbuf); else ret= lstat(path, &stbuf); if(ret == -1) return(-1); if((stbuf.st_mode & S_IFMT) == S_IFLNK) return(-2); acl= acl_from_text(text); if(acl == NULL) { ret= -1; goto ex; } /* Note: no ACL_TYPE_DEFAULT in FreeBSD */ if(flag & 1) {ret= 0; goto ex;} ret= acl_set_file(path, ACL_TYPE_ACCESS, acl); if(ret == -1) goto ex; ret= 1; ex: if(acl != NULL) acl_free(acl); return(ret); #else /* Libisofs_with_aaip_acL */ return(0); #endif /* ! Libisofs_with_aaip_acL */ } #ifndef Libisofs_old_freebsd_acl_adapteR #ifdef Libisofs_with_freebsd_extattR /* @param flag Bitfield for control purposes bit5= in case of symbolic link: manipulate link target */ static int aaip_extattr_delete_names(char *path, int attrnamespace, char *list, ssize_t list_size, int flag) { int len; char name[256]; ssize_t value_ret, i; for(i= 0; i < list_size; i+= len + 1) { len= *((unsigned char *) (list + i)); if(len > 0) strncpy(name, list + i + 1, len); name[len]= 0; if(flag & 32) value_ret= extattr_delete_file(path, attrnamespace, name); else value_ret= extattr_delete_file(path, attrnamespace, name); if(value_ret == -1) return(0); } return(1); } #endif /* Libisofs_with_freebsd_extattR */ static void register_errno(int *errnos, int i, int in_errno) { if(in_errno > 0) errnos[i]= in_errno; else errnos[i]= -1; } /* Bring the given attributes and/or ACLs into effect with the given file. @param flag Bitfield for control purposes bit0= decode and set ACLs bit1= first clear all existing attributes of the file bit2= do not set attributes other than ACLs bit3= do not ignore eventual non-user attributes. I.e. those with a name which does not begin by "user." bit5= in case of symbolic link: manipulate link target bit6= tolerate inappropriate presence or absence of directory default ACL bit7= void setting a name value pair if it already exists and has the desired value. @return 1 success -1 error memory allocation -2 error with decoding of ACL -3 error with setting ACL -4 error with setting attribute -5 error with deleting attributes -6 support of xattr not enabled at compile time -7 support of ACL not enabled at compile time -8 unsupported xattr namespace ISO_AAIP_ACL_MULT_OBJ multiple entries of user::, group::, other:: */ int aaip_set_attr_list(char *path, size_t num_attrs, char **names, size_t *value_lengths, char **values, int *errnos, int flag) { int ret, has_default_acl= 0, end_ret= 1; size_t i, consumed, acl_text_fill, acl_idx= 0; char *acl_text= NULL; #ifdef Libisofs_with_freebsd_extattR char *user_list= NULL, *sys_list= NULL, *namept, *old_value; ssize_t user_list_size= 0, sys_list_size= 0, value_ret; int attrnamespace; size_t old_value_l; int skip; #endif for(i= 0; i < num_attrs; i++) errnos[i]= 0; #ifdef Libisofs_with_freebsd_extattR if(flag & 2) { /* Delete all file attributes */ ret= aaip_extattr_make_list(path, EXTATTR_NAMESPACE_USER, &user_list, &user_list_size, flag & 32); if(ret <= 0) {ret= -1; goto ex;} ret= aaip_extattr_delete_names(path, EXTATTR_NAMESPACE_USER, user_list, user_list_size, flag & 32); if(ret <= 0) {ret= -5; goto ex;} if(flag & 8) { ret= aaip_extattr_make_list(path, EXTATTR_NAMESPACE_SYSTEM, &sys_list, &sys_list_size, flag & 32); if(ret <= 0) {ret= -5; goto ex;} ret= aaip_extattr_delete_names(path, EXTATTR_NAMESPACE_SYSTEM, sys_list, sys_list_size, flag & 32); if(ret <= 0) {ret= -5; goto ex;} } } #endif /* Libisofs_with_freebsd_extattR */ for(i= 0; i < num_attrs; i++) { if(names[i] == NULL || values[i] == NULL) continue; if(names[i][0] == 0) { /* ACLs */ if(flag & 1) acl_idx= i + 1; continue; } /* Extended Attribute */ if(flag & 4) continue; #ifdef Libisofs_with_freebsd_extattR if(strncmp(names[i], "user.", 5) == 0) { attrnamespace= EXTATTR_NAMESPACE_USER; namept= names[i] + 5; } else if(strncmp(names[i], "isofs.", 6) == 0 || !(flag & 8)) { continue; } else if(strncmp(names[i], "system.", 7) == 0) { attrnamespace= EXTATTR_NAMESPACE_SYSTEM; namept= names[i] + 7; } else { register_errno(errnos, i, (int) EFAULT); end_ret= -8; continue; } skip= 0; if(flag & 128) { value_ret= get_single_attr(path, names[i], &old_value_l, &old_value, flag & (8 | 32)); if(value_ret > 0 && old_value_l == value_lengths[i]) { if(memcmp(old_value, values[i], value_lengths[i]) == 0) skip= 1; } if(old_value != NULL) free(old_value); } if(!skip) { if(flag & 32) ret= extattr_set_file(path, attrnamespace, namept, values[i], value_lengths[i]); else ret= extattr_set_link(path, attrnamespace, namept, values[i], value_lengths[i]); if(ret == -1) { register_errno(errnos, i, errno); if(end_ret != 1) end_ret= -4; continue; } } #else if(strncmp(names[i], "user.", 5) == 0) ; else if(strncmp(names[i], "isofs.", 6) == 0 || !(flag & 8)) continue; {ret= -6; goto ex;} #endif /* Libisofs_with_freebsd_extattR */ } /* Decode ACLs */ /* It is important that this happens after restoring xattr which might be representations of ACL, too. If isofs ACL are enabled, then they shall override the xattr ones. */ if(acl_idx == 0) {ret= end_ret; goto ex;} i= acl_idx - 1; ret= aaip_decode_acl((unsigned char *) values[i], value_lengths[i], &consumed, NULL, 0, &acl_text_fill, 1); if(ret < -3) goto ex; if(ret <= 0) {ret= -2; goto ex;} acl_text= calloc(acl_text_fill, 1); if(acl_text == NULL) {ret= -1; goto ex;} ret= aaip_decode_acl((unsigned char *) values[i], value_lengths[i], &consumed, acl_text, acl_text_fill, &acl_text_fill, 0); if(ret < -3) goto ex; if(ret <= 0) {ret= -2; goto ex;} has_default_acl= (ret == 2); #ifdef Libisofs_with_aaip_acL ret= aaip_set_acl_text(path, acl_text, flag & (32 | 64)); if(ret == -1) register_errno(errnos, i, errno); if(ret <= 0) {ret= -3; goto ex;} #else {ret= -7; goto ex;} #endif if(has_default_acl && !(flag & 64)) {ret= -3; goto ex;} ret= end_ret; ex:; if(acl_text != NULL) free(acl_text); #ifdef Libisofs_with_freebsd_extattR if(user_list != NULL) free(user_list); if(sys_list != NULL) free(sys_list); #endif /* Libisofs_with_freebsd_extattR */ return(ret); } #else /* ! Libisofs_old_freebsd_acl_adapteR */ /* Bring the given attributes and/or ACLs into effect with the given file. Note: There are no Extended Attributes in FreeBSD. So only ACL get set. @param flag Bitfield for control purposes bit0= decode and set ACLs ( bit1= first clear all existing attributes of the file ) ( bit2= do not set attributes other than ACLs ) ( bit3= do not ignore eventual non-user attributes. I.e. those with a name which does not begin by "user." ) @return 1 success -1 error memory allocation -2 error with decoding of ACL -3 error with setting ACL ( -4 error with setting attribute ) ( -5 error with deleting attribute ) -6 support of xattr not enabled at compile time -7 support of ACL not enabled at compile time */ int aaip_set_attr_list(char *path, size_t num_attrs, char **names, size_t *value_lengths, char **values, int flag) { int ret, has_default_acl= 0, was_xattr= 0; size_t i, consumed, acl_text_fill; char *acl_text= NULL, *list= NULL; for(i= 0; i < num_attrs; i++) { if(names[i] == NULL || values[i] == NULL) continue; if(names[i][0] == 0) { /* Decode ACLs */ /* access ACL */ if(!(flag & 1)) continue; ret= aaip_decode_acl((unsigned char *) values[i], value_lengths[i], &consumed, NULL, 0, &acl_text_fill, 1); if(ret <= 0) {ret= -2; goto ex;} acl_text= calloc(acl_text_fill, 1); if(acl_text == NULL) {ret= -1; goto ex;} ret= aaip_decode_acl((unsigned char *) values[i], value_lengths[i], &consumed, acl_text, acl_text_fill, &acl_text_fill, 0); if(ret <= 0) {ret= -2; goto ex;} has_default_acl= (ret == 2); #ifdef Libisofs_with_aaip_acL ret= aaip_set_acl_text(path, acl_text, flag & 32); if(ret <= 0) {ret= -3; goto ex;} #else {ret= -7; goto ex;} #endif /* "default" ACL */ if(has_default_acl) { free(acl_text); acl_text= NULL; ret= aaip_decode_acl((unsigned char *) (values[i] + consumed), value_lengths[i] - consumed, &consumed, NULL, 0, &acl_text_fill, 1); if(ret <= 0) {ret= -2; goto ex;} acl_text= calloc(acl_text_fill, 1); if(acl_text == NULL) {ret= -1; goto ex;} ret= aaip_decode_acl((unsigned char *) (values[i] + consumed), value_lengths[i] - consumed, &consumed, acl_text, acl_text_fill, &acl_text_fill, 0); if(ret <= 0) {ret= -2; goto ex;} ret= aaip_set_acl_text(path, acl_text, 1 | (flag & 32)); if(ret <= 0) {ret= -3; goto ex;} } } else { if(flag & 4) continue; if(!(flag & 8)) if(strncmp(names[i], "user.", 5)) continue; was_xattr= 1; } } ret= 1; if(was_xattr) ret= -6; ex:; if(acl_text != NULL) free(acl_text); if(list != NULL) free(list); return(ret); } #endif /* Libisofs_old_freebsd_acl_adapteR */ int aaip_set_lfa_flags(char *path, uint64_t lfa_flags, int max_bit, int *os_errno, int flag) { *os_errno= 0; return(0); } /* aaip-os-linux.c Arbitrary Attribute Interchange Protocol , system adapter for getting and setting of ACLs and xattr. To be included by aaip_0_2.c for Linux Copyright (c) 2009 - 2024 Thomas Schmitt This file is part of the libisofs project; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later as published by the Free Software Foundation. See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include #include #include #include #include #include #include #ifdef Libisofs_with_aaip_acL #include #endif #ifdef Libisofs_with_aaip_xattR #ifdef Libisofs_with_sys_xattR #include #else #include #endif #endif #ifdef Libisofs_with_aaip_lfa_flagS #include #include #endif /* ------------------------------ Inquiry --------------------------------- */ /* See also API iso_local_attr_support(). @param flag Bitfield for control purposes bit0= inquire availability of ACL bit1= inquire availability of xattr bit2= inquire availability of Linux-like file attribute flags bit3 - bit7= Reserved for future types. It is permissibile to set them to 1 already now. bit8 and higher: reserved, submit 0 @return Bitfield corresponding to flag. If bits are set, th bit0= ACL adapter is enabled bit1= xattr adapter is enabled bit2= Linux-like file attribute flags adapter is enabled bit3 - bit7= Reserved for future types. bit8 and higher: reserved, do not interpret these */ int aaip_local_attr_support(int flag) { int ret= 0; #ifdef Libisofs_with_aaip_acL if(flag & 1) ret|= 1; #endif #ifdef Libisofs_with_aaip_xattR if(flag & 2) ret|= 2; #endif #ifdef Libisofs_with_aaip_lfa_flagS #ifdef FS_IOC_GETFLAGS #ifdef FS_IOC_SETFLAGS if(flag & 4) ret|= 4; #endif #endif #endif return(ret); } /* -------------------------- Error reporting ----------------------------- */ /* Report an error with local ACL or xattr calls. @param flag bit0-7: mode 0=NO_GET_LOCAL , 1=NO_SET_LOCAL */ static void aaip_local_error(char *function_name, char *path, int err, int flag) { int mode, err_code; mode= (flag & 255); if(mode == 1) err_code= ISO_AAIP_NO_SET_LOCAL_S; else err_code= ISO_AAIP_NO_GET_LOCAL_S; if(err > 0) { if(path[0]) iso_msg_submit(-1, err_code, 0, "Function %s with file \"%s\" failed with errno %d '%s'", function_name, path, err, strerror(err)); else iso_msg_submit(-1, err_code, 0, "Function %s failed with %d '%s'", function_name, err, strerror(err)); } else { if(path[0]) iso_msg_submit(-1, err_code, 0, "Function %s with file \"%s\" failed without error code", function_name, path); else iso_msg_submit(-1, err_code, 0, "Function %s failed without error code", function_name); } } /* ------------------------------ Getters --------------------------------- */ /* Obtain the ACL of the given file in long text form. @param path Path to the file @param text Will hold the result. This is a managed object which finally has to be freed by a call to this function with bit15 of flag. @param flag Bitfield for control purposes bit0= obtain default ACL rather than access ACL behave like bit4 if ACL is empty bit4= set *text = NULL and return 2 if the ACL matches st_mode permissions. bit5= in case of symbolic link: inquire link target bit15= free text and return 1 @return 1 ok 2 only st_mode permissions exist and bit 4 is set or empty ACL and bit0 is set 0 ACL support not enabled at compile time or filesystem does not support ACL -1 failure of system ACL service (see errno) -2 attempt to inquire ACL of a symbolic link without bit4 or bit5 or with no suitable link target */ int aaip_get_acl_text(char *path, char **text, int flag) { #ifdef Libisofs_with_aaip_acL acl_t acl= NULL; struct stat stbuf; int ret; if(flag & (1 << 15)) { if(*text != NULL) acl_free(*text); *text= NULL; return(1); } *text= NULL; if(flag & 32) ret= stat(path, &stbuf); else ret= lstat(path, &stbuf); if(ret == -1) return(-1); if((stbuf.st_mode & S_IFMT) == S_IFLNK) { if(flag & 16) return(2); return(-2); } acl= acl_get_file(path, (flag & 1) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS); if(acl == NULL) { if(errno == ENOTSUP) { /* filesystem does not support ACL */ if(flag & 16) return(2); /* >>> ??? fake ACL from POSIX permissions ? */; return(0); } return(-1); } *text= acl_to_text(acl, NULL); acl_free(acl); if(*text == NULL) return(-1); if(flag & 16) { ret = aaip_cleanout_st_mode(*text, &(stbuf.st_mode), 2); if(!(ret & (7 | 64))) (*text)[0]= 0; } if(flag & (1 | 16)) { if((*text)[0] == 0 || strcmp(*text, "\n") == 0) { acl_free(*text); *text= NULL; return(2); } } return(1); #else /* Libisofs_with_aaip_acL */ return(0); #endif /* ! Libisofs_with_aaip_acL */ } #ifdef Libisofs_with_aaip_xattR static int get_single_attr(char *path, char *name, size_t *value_length, char **value_bytes, int flag) { ssize_t value_ret; *value_bytes= NULL; *value_length= 0; if(flag & 32) value_ret= getxattr(path, name, NULL, 0); else value_ret= lgetxattr(path, name, NULL, 0); if(value_ret == -1) { aaip_local_error((flag & 32) ? "getxattr(2)" : "lgetxattr(2)", path, errno, 0); return(0); } *value_bytes= calloc(value_ret + 1, 1); if(*value_bytes == NULL) return(-1); if(flag & 32) value_ret= getxattr(path, name, *value_bytes, value_ret); else value_ret= lgetxattr(path, name, *value_bytes, value_ret); if(value_ret == -1) { aaip_local_error((flag & 32) ? "getxattr(2)" : "lgetxattr(2)", path, errno, 0); free(*value_bytes); *value_bytes= NULL; *value_length= 0; return(0); } *value_length= value_ret; return(1); } #endif /* Libisofs_with_aaip_xattR */ /* Obtain the Extended Attributes and/or the ACLs of the given file in a form that is ready for aaip_encode(). @param path Path to the file @param num_attrs Will return the number of name-value pairs @param names Will return an array of pointers to 0-terminated names @param value_lengths Will return an array with the lengths of values @param values Will return an array of pointers to 8-bit values @param flag Bitfield for control purposes bit0= obtain ACL (access and eventually default) bit1= use numeric ACL qualifiers rather than names bit2= do not obtain attributes other than ACL bit3= do not ignore eventual non-user attributes I.e. those with a name which does not begin by "user." bit4= do not return trivial ACL that matches st_mode bit5= in case of symbolic link: inquire link target bit6= do not obtain Linux style file attribute flags (chattr) bit7= Without bit6: Ignore non-settable flags and do not record "isofs.fa" if all flags are zero bit15= free memory of names, value_lengths, values @return 1 ok (reserved for FreeBSD: 2 ok, no permission to inspect non-user namespaces.) <=0 error -1= out of memory -2= program error with prediction of result size -3= error with conversion of name to uid or gid */ int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag) { int ret; #ifdef Libisofs_with_aaip_acL unsigned char *acl= NULL; char *a_acl_text= NULL, *d_acl_text= NULL; size_t acl_len= 0; #define Libisofs_aaip_get_attr_activE yes #endif #ifdef Libisofs_with_aaip_xattR char *list= NULL; ssize_t value_ret, list_size= 0; #define Libisofs_aaip_get_attr_activE yes #endif #ifdef Libisofs_aaip_get_attr_activE ssize_t i, num_names= 0; #endif #ifdef Libisofs_with_aaip_lfa_flagS uint64_t lfa_flags; int max_bit, os_errno, lfa_length; unsigned char lfa_value[8]; #endif if(flag & (1 << 15)) { /* Free memory */ {ret= 1; goto ex;} } *num_attrs= 0; *names= NULL; *value_lengths= NULL; *values= NULL; #ifndef Libisofs_aaip_get_attr_activE ret = 1; ex:; return ret; #else /* Libisofs_aaip_get_attr_activE */ /* Set up arrays */ #ifdef Libisofs_with_aaip_xattR if(!(flag & 4)) { /* Get xattr names */ if(flag & 32) list_size= listxattr(path, list, 0); else list_size= llistxattr(path, list, 0); if(list_size == -1) { if(errno == ENOSYS) { /* Function not implemented */ list_size= 0; /* Handle as if xattr was disabled at compile time */ } else { aaip_local_error((flag & 32) ? "listxattr(2)" : "llistxattr(2)", path, errno, 0); {ret= -1; goto ex;} } } if(list_size > 0) { list= calloc(list_size, 1); if(list == NULL) {ret= -1; goto ex;} if(flag & 32) list_size= listxattr(path, list, list_size); else list_size= llistxattr(path, list, list_size); if(list_size == -1) { aaip_local_error((flag & 32) ? "listxattr(2)" : "llistxattr(2)", path, errno, 0); {ret= -1; goto ex;} } } for(i= 0; i < list_size; i+= strlen(list + i) + 1) num_names++; } #endif /* ! Libisofs_with_aaip_xattR */ #ifdef Libisofs_with_aaip_acL if(flag & 1) num_names++; #endif #ifdef Libisofs_with_aaip_lfa_flagS if(!(flag & 64)) num_names++; #endif if(num_names == 0) {ret= 1; goto ex;} (*names)= calloc(num_names, sizeof(char *)); (*value_lengths)= calloc(num_names, sizeof(size_t)); (*values)= calloc(num_names, sizeof(char *)); if(*names == NULL || *value_lengths == NULL || *values == NULL) {ret= -1; goto ex;} for(i= 0; i < num_names; i++) { (*names)[i]= NULL; (*values)[i]= NULL; (*value_lengths)[i]= 0; } #ifdef Libisofs_with_aaip_xattR if(!(flag & 4)) { /* Get xattr values */ for(i= 0; i < list_size && (size_t) num_names > *num_attrs; i+= strlen(list + i) + 1) { if(!(flag & 8)) if(strncmp(list + i, "user.", 5)) continue; (*names)[(*num_attrs)++]= strdup(list + i); if((*names)[(*num_attrs) - 1] == NULL) {ret= -1; goto ex;} } for(i= 0; (size_t) i < *num_attrs; i++) { if(!(flag & 8)) if(strncmp((*names)[i], "user.", 5)) continue; value_ret= get_single_attr(path, (*names)[i], *value_lengths + i, *values + i, flag & 32); if(value_ret <= 0) {ret= -1; goto ex;} } } #endif /* Libisofs_with_aaip_xattR */ #ifdef Libisofs_with_aaip_acL if(flag & 1) { /* Obtain ACL */ aaip_get_acl_text(path, &a_acl_text, flag & (16 | 32)); aaip_get_acl_text(path, &d_acl_text, 1 | (flag & 32)); if(a_acl_text == NULL && d_acl_text == NULL) goto try_lfa_flags; ret= aaip_encode_both_acl(a_acl_text, d_acl_text, (mode_t) 0, &acl_len, &acl, (flag & 2)); if(ret <= 0) goto ex; /* Set as attribute with empty name */; (*names)[*num_attrs]= strdup(""); if((*names)[*num_attrs] == NULL) {ret= -1; goto ex;} (*values)[*num_attrs]= (char *) acl; acl= NULL; (*value_lengths)[*num_attrs]= acl_len; (*num_attrs)++; } #endif /* Libisofs_with_aaip_acL */ try_lfa_flags:; #ifdef Libisofs_with_aaip_lfa_flagS if(!(flag & 64)) { /* ( aaip_get_lfa_flags() does not gracefully handle dead symbolic links) */ ret= iso_local_get_lfa_flags(path, &lfa_flags, &max_bit, &os_errno, flag & (1 << 7)); if((flag & (1 << 7)) && lfa_flags == (uint64_t) 0) { /* virtually no lfa_flags because no settable ones */ ret= 4; } if(ret == 1 || ret == 2) { ret= aaip_encode_lfa_flags(lfa_flags, lfa_value, &lfa_length, 0); if(ret > 0) { (*names)[*num_attrs]= strdup("isofs.fa"); if((*names)[*num_attrs] == NULL) {ret= -1; goto ex;} (*values)[*num_attrs]= calloc(lfa_length, 1); if((*values)[*num_attrs] == NULL) {ret= -1; goto ex;} memcpy((*values)[*num_attrs], (char *) lfa_value, lfa_length); (*value_lengths)[*num_attrs]= lfa_length; (*num_attrs)++; } } } #endif /* Libisofs_with_aaip_lfa_flagS */ ret= 1; ex:; #ifdef Libisofs_with_aaip_acL if(a_acl_text != NULL) aaip_get_acl_text("", &a_acl_text, 1 << 15); /* free */ if(d_acl_text != NULL) aaip_get_acl_text("", &d_acl_text, 1 << 15); /* free */ if(acl != NULL) free(acl); #endif #ifdef Libisofs_with_aaip_xattR if(list != NULL) free(list); #endif if(ret <= 0 || (flag & (1 << 15))) { if(*names != NULL) { for(i= 0; (size_t) i < *num_attrs; i++) free((*names)[i]); free(*names); } *names= NULL; if(*value_lengths != NULL) free(*value_lengths); *value_lengths= NULL; if(*values != NULL) { for(i= 0; (size_t) i < *num_attrs; i++) free((*values)[i]); free(*values); } *values= NULL; *num_attrs= 0; } return(ret); #endif /* Libisofs_aaip_get_attr_activE */ } /* Obtain the file attribute flags of the given file as bit array in uint64_t. The bit numbers are compatible to the FS_*_FL definitions in Linux include file . A (possibly outdated) copy of them is in doc/susp_aaip_isofs_names.txt, name isofs.fa . The attribute flags of other systems may or may not be mappable to these flags. @param path Path to the file @param lfa_flags Will get filled with the FS_*_FL @param max_bit Will tell the highest bit that is possibly set (-1 = surely no bit is valid) @param flag Bitfield for control purposes. bit0= consider ENOTTY from FS_IOC_GETFLAGS an error (else return 4 on ENOTTY) bit2= do not issue own error messages with operating system errors bit7= Ignore non-settable flags @return 1= ok, all local attribute flags are in lfa_flags 2= ok, but some local flags could not be mapped to the FS_*_FL bits 4= ok, ENOTTY from FS_IOC_GETFLAGS 0= local flag retrieval not enabled at compile time <0 error with system calls: -1= error with open(2) -2= error with ioctl(2), not pardoned by bit0 */ int aaip_get_lfa_flags(char *path, uint64_t *lfa_flags, int *max_bit, int *os_errno, int flag) { int ret= 0; static uint64_t user_settable= 0, su_settable= 0, non_settable= 0, unknown= 0; #ifdef Libisofs_with_aaip_lfa_flagS int fd; long ioctl_result= 0; #endif *lfa_flags= 0; *max_bit= -1; *os_errno= 0; if(non_settable == (uint64_t) 0) iso_util_get_lfa_masks(&user_settable, &su_settable, &non_settable, &unknown); #ifdef Libisofs_with_aaip_lfa_flagS #ifdef FS_IOC_GETFLAGS fd= open(path, O_RDONLY | O_NDELAY); if(fd == -1) { if(!(flag & 4)) aaip_local_error("open(2)", path, errno, 0); *os_errno= errno; return(-1); } ret= ioctl(fd, FS_IOC_GETFLAGS, &ioctl_result); close(fd); if(ret == -1) { if(errno == ENOTTY && !(flag & 1)) { /* Usual result with file type or filesystem without Linux attributes */ *max_bit= 23; return(4); } if(!(flag & 4)) aaip_local_error("ioctl(FS_IOC_GETFLAGS)", path, errno, 0); *os_errno= errno; return(-2); } *lfa_flags= ioctl_result; if(*lfa_flags < 1 << 24) *max_bit= 23; else if(*lfa_flags < (uint64_t) 1 << 32) *max_bit= 31; else *max_bit= sizeof(long) * 8 - 1; if(flag & (1 << 7)) *lfa_flags&= ~non_settable; ret= 1; #endif /* FS_IOC_GETFLAGS */ #endif /* Libisofs_with_aaip_lfa_flagS */ return(ret); } /* ------------------------------ Setters --------------------------------- */ /* Set the ACL of the given file to a given list in long text form. @param path Path to the file @param text The input text (0 terminated, ACL long text form) @param flag Bitfield for control purposes bit0= set default ACL rather than access ACL bit5= in case of symbolic link: manipulate link target @return >0 ok 0 ACL support not enabled at compile time -1 failure of system ACL service (see errno) -2 attempt to manipulate ACL of a symbolic link without bit5 or with no suitable link target */ int aaip_set_acl_text(char *path, char *text, int flag) { #ifdef Libisofs_with_aaip_acL int ret; acl_t acl= NULL; struct stat stbuf; if(flag & 32) ret= stat(path, &stbuf); else ret= lstat(path, &stbuf); if(ret == -1) return(-1); if((stbuf.st_mode & S_IFMT) == S_IFLNK) return(-2); acl= acl_from_text(text); if(acl == NULL) { aaip_local_error("acl_from_text(3)", "", errno, 1); ret= -1; goto ex; } ret= acl_set_file(path, (flag & 1) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS, acl); if(ret == -1) { aaip_local_error("acl_set_file(3)", path, errno, 1); goto ex; } ret= 1; ex: if(acl != NULL) acl_free(acl); return(ret); #else /* Libisofs_with_aaip_acL */ return(0); #endif /* ! Libisofs_with_aaip_acL */ } static void register_errno(int *errnos, int i) { if(errno > 0) errnos[i]= errno; else errnos[i]= -1; } /* Bring the given attributes and/or ACLs into effect with the given file. @param flag Bitfield for control purposes bit0= decode and set ACLs bit1= first clear all existing attributes of the file bit2= do not set attributes other than ACLs bit3= do not ignore eventual non-user attributes. I.e. those with a name which does not begin by "user." bit5= in case of symbolic link: manipulate link target bit6= tolerate inappropriate presence or absence of directory default ACL bit7= avoid setting a name value pair if it already exists and has the desired value. @return 1 success -1 error memory allocation -2 error with decoding of ACL -3 error with setting ACL -4 error with setting attribute -5 error with deleting attributes -6 support of xattr not enabled at compile time -7 support of ACL not enabled at compile time ( -8 unsupported xattr namespace ) ISO_AAIP_ACL_MULT_OBJ multiple entries of user::, group::, other:: */ int aaip_set_attr_list(char *path, size_t num_attrs, char **names, size_t *value_lengths, char **values, int *errnos, int flag) { int ret, end_ret= 1; size_t i, consumed, acl_text_fill, acl_idx= 0; char *acl_text= NULL; #ifdef Libisofs_with_aaip_xattR char *list= NULL, *old_value; ssize_t list_size= 0, value_ret; size_t old_value_l; int skip; #endif #ifdef Libisofs_with_aaip_acL size_t h_consumed; int has_default_acl= 0; #endif for(i= 0; i < num_attrs; i++) errnos[i]= 0; #ifdef Libisofs_with_aaip_xattR if(flag & 2) { /* Delete all file attributes */ if(flag & 32) list_size= listxattr(path, list, 0); else list_size= llistxattr(path, list, 0); } if(list_size > 0) { /* Delete all file attributes */ list= calloc(list_size, 1); if(list == NULL) {ret= -5; goto ex;} if(flag & 32) list_size= listxattr(path, list, list_size); else list_size= llistxattr(path, list, list_size); if(list_size == -1) { aaip_local_error((flag & 32) ? "listxattr(2)" : "llistxattr(2)", path, errno, 1); {ret= -5; goto ex;} } for(i= 0; i < (size_t) list_size; i+= strlen(list + i) + 1) { if(!(flag & 8)) if(strncmp(list + i, "user.", 5)) continue; if(flag & 32) ret= removexattr(path, list + i); else ret= lremovexattr(path, list + i); if(ret == -1) { aaip_local_error((flag & 32) ? "removexattr(2)" : "lremovexattr(2)", path, errno, 1); {ret= -5; goto ex;} } } free(list); list= NULL; } #endif /* Libisofs_with_aaip_xattR */ for(i= 0; i < num_attrs; i++) { if(names[i] == NULL || values[i] == NULL) continue; if(names[i][0] == 0) { /* ACLs */ if(flag & 1) acl_idx= i + 1; continue; } /* Extended Attribute */ if(flag & 4) continue; if(strncmp(names[i], "isofs.", 6) == 0) continue; if(!(flag & 8)) if(strncmp(names[i], "user.", 5)) continue; #ifdef Libisofs_with_aaip_xattR skip= 0; if(flag & 128) { value_ret= get_single_attr(path, names[i], &old_value_l, &old_value, flag & 32); if(value_ret > 0 && old_value_l == value_lengths[i]) { if(memcmp(old_value, values[i], value_lengths[i]) == 0) skip= 1; } if(old_value != NULL) free(old_value); } if(!skip) { if(flag & 32) ret= setxattr(path, names[i], values[i], value_lengths[i], 0); else ret= lsetxattr(path, names[i], values[i], value_lengths[i], 0); if(ret == -1) { aaip_local_error((flag & 32) ? "setxattr(2)" : "lsetxattr(2)", path, errno, 1); register_errno(errnos, i); end_ret= -4; continue; } } #else {ret= -6; goto ex;} #endif /* Libisofs_with_aaip_xattR */ } /* Decode ACLs */ /* It is important that this happens after restoring xattr which might be representations of ACL, too. If isofs ACL are enabled, then they shall override the xattr ones. */ if(acl_idx == 0) {ret= end_ret; goto ex;} i= acl_idx - 1; /* "access" ACL */ ret= aaip_decode_acl((unsigned char *) values[i], value_lengths[i], &consumed, NULL, 0, &acl_text_fill, 1); if(ret < -3) goto ex; if(ret <= 0) {ret= -2; goto ex;} acl_text= calloc(acl_text_fill, 1); if(acl_text == NULL) {ret= -1; goto ex;} ret= aaip_decode_acl((unsigned char *) values[i], value_lengths[i], &consumed, acl_text, acl_text_fill, &acl_text_fill, 0); if(ret < -3) goto ex; if(ret <= 0) {ret= -2; goto ex;} #ifdef Libisofs_with_aaip_acL has_default_acl= (ret == 2); ret= aaip_set_acl_text(path, acl_text, flag & 32); if(ret == -1) register_errno(errnos, i); if(ret <= 0) {ret= -3; goto ex;} /* "default" ACL */ if(has_default_acl) { free(acl_text); acl_text= NULL; ret= aaip_decode_acl((unsigned char *) (values[i] + consumed), value_lengths[i] - consumed, &h_consumed, NULL, 0, &acl_text_fill, 1); if(ret < -3) goto ex; if(ret <= 0) {ret= -2; goto ex;} acl_text= calloc(acl_text_fill, 1); if(acl_text == NULL) {ret= -1; goto ex;} ret= aaip_decode_acl((unsigned char *) (values[i] + consumed), value_lengths[i] - consumed, &h_consumed, acl_text, acl_text_fill, &acl_text_fill, 0); if(ret < -3) goto ex; if(ret <= 0) {ret= -2; goto ex;} ret= aaip_set_acl_text(path, acl_text, 1 | (flag & 32)); if(ret == -1) register_errno(errnos, i); if(ret <= 0) {ret= -3; goto ex;} } else { if(!(flag & 64)) { /* >>> ??? take offense from missing default ACL ? ??? does Linux demand a default ACL for directories with access ACL ? */; } } ret= end_ret; #else ret= -7; #endif /* !Libisofs_with_aaip_acL */ ex:; if(acl_text != NULL) free(acl_text); #ifdef Libisofs_with_aaip_xattR if(list != NULL) free(list); #endif return(ret); } /* @param flag Bitfield for control purposes. bit2= do not issue own error messages with operating system errors @return 1= ok, all lfa_flags bits were written 2= ok, but some FS_*_FL bits could not be mapped to local flags 0= local flags setting not enabled at compile time <0 error with system calls or with max_bit: -1= error with open(2) -2= error with ioctl(2) -3= error with max_bit */ int aaip_set_lfa_flags(char *path, uint64_t lfa_flags, int max_bit, int *os_errno, int flag) { int ret= 0; #ifdef Libisofs_with_aaip_lfa_flagS int fd; long ioctl_arg; #endif *os_errno= 0; #ifdef Libisofs_with_aaip_lfa_flagS #ifdef FS_IOC_SETFLAGS if(max_bit > (int) sizeof(long) * 8 - 1) { aaip_local_error("ioctl(FS_IOC_SETFLAGS) with too many bits", path, 0, 0); return(-3); } fd= open(path, O_RDONLY | O_NDELAY); if(fd == -1) { if(!(flag & 4)) aaip_local_error("open(2)", path, errno, 0); *os_errno= errno; return(-1); } if(max_bit < 0) ioctl_arg= 0; else ioctl_arg= lfa_flags; ret= ioctl(fd, FS_IOC_SETFLAGS, &ioctl_arg); close(fd); if(ret == -1) { if(!(flag & 4)) aaip_local_error("ioctl(FS_IOC_SETFLAGS)", path, errno, 0); *os_errno= errno; return(-2); } ret= 1; #endif /* FS_IOC_SETFLAGS */ #endif /* Libisofs_with_aaip_lfa_flagS */ return(ret); } /* Arbitrary Attribute Interchange Protocol , AAIP versions 0.2 , 1.0 , 2.0. Implementation of encoding and decoding xattr and ACL. See libisofs/aaip_0_2.h http://libburnia-project.org/wiki/AAIP Copyright (c) 2009 - 2019 Thomas Schmitt This file is part of the libisofs project; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later as published by the Free Software Foundation. See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include #include #include #include #include #include #include #include #include "libisofs.h" #include "util.h" #include "messages.h" /* #define Aaip_encode_debuG 1 */ #include "aaip_0_2.h" #define Aaip_EXEC 1 #define Aaip_WRITE 2 #define Aaip_READ 4 #define Aaip_TRANSLATE 0 #define Aaip_ACL_USER_OBJ 1 #define Aaip_ACL_USER 2 #define Aaip_ACL_GROUP_OBJ 3 #define Aaip_ACL_GROUP 4 #define Aaip_ACL_MASK 5 #define Aaip_ACL_OTHER 6 #define Aaip_SWITCH_MARK 8 #define Aaip_ACL_USER_N 10 #define Aaip_ACL_GROUP_N 12 #define Aaip_FUTURE_VERSION 15 #define Aaip_with_short_namespaceS yes #define Aaip_max_named_spacE 0x06 #define Aaip_min_named_spacE 0x02 #define Aaip_maxdef_namespacE 0x1f #define Aaip_namespace_literaL 0x01 #define Aaip_namespace_systeM 0x02 #define Aaip_namespace_useR 0x03 #define Aaip_namespace_isofS 0x04 #define Aaip_namespace_trusteD 0x05 #define Aaip_namespace_securitY 0x06 /* maximum expansion: "security." */ #define Aaip_max_name_expansioN 9 static char Aaip_namespace_textS[][Aaip_max_name_expansioN + 1]= {"", "", "system.", "user.", "isofs.", "trusted.", "security."}; /* --------------------------------- Encoder ---------------------------- */ static int aaip_encode_pair(char *name, size_t attr_length, char *attr, unsigned int *num_recs, size_t *comp_size, unsigned char *result, size_t result_fill, int flag); /* Convert an array of Arbitrary Attributes into a series of AAIP fields. @param num_attrs Number of attributes @param names Array of pointers to 0 terminated name strings @param value_lengths Array of byte lengths for each value @param values Array of pointers to the value bytes @param result_len Number of bytes in the resulting SUSP field string @param result *result will point to the start of the result string. This is malloc() memory which needs to be freed when no longer needed @param flag Bitfield for control purposes bit0= set CONTINUE bit of last AAIP field to 1 @return >= 0 is the number of SUSP fields generated, < 0 means error */ ssize_t aaip_encode(size_t num_attrs, char **names, size_t *value_lengths, char **values, size_t *result_len, unsigned char **result, int flag) { size_t mem_size= 0, comp_size; ssize_t ret; unsigned int number_of_fields, i, num_recs; /* Predict memory needs, number of SUSP fields and component records */ *result = NULL; *result_len= 0; for(i= 0; i < num_attrs; i++) { ret= aaip_encode_pair(names[i], value_lengths[i], values[i], &num_recs, &comp_size, NULL, (size_t) 0, 1); if(ret < 0) return(ret); mem_size+= comp_size; } number_of_fields= mem_size / 250 + !!(mem_size % 250); if(number_of_fields == 0) return(0); mem_size+= number_of_fields * 5; #ifdef Aaip_encode_debuG *result= (unsigned char *) calloc(1, mem_size + 1024000); /* generous honeypot for overflows */ #else *result= (unsigned char *) calloc(1, mem_size); #endif if(*result == NULL) return ISO_OUT_OF_MEM; /* Encode pairs into result */ for(i= 0; i < num_attrs; i++) { ret= aaip_encode_pair(names[i], value_lengths[i], values[i], &num_recs, &comp_size, *result, *result_len, 0); if(ret < 0) { free(*result); *result = NULL; *result_len = 0; return(ret); } (*result_len)+= comp_size; } /* write the field headers */ for(i= 0; i < number_of_fields; i++) { (*result)[i * 255 + 0]= 'A'; (*result)[i * 255 + 1]= 'L'; if(i < number_of_fields - 1 || (mem_size % 255) == 0) (*result)[i * 255 + 2]= 255; else (*result)[i * 255 + 2]= mem_size % 255; (*result)[i * 255 + 3]= 1; (*result)[i * 255 + 4]= (flag & 1) || (i < number_of_fields - 1); } (*result_len)+= number_of_fields * 5; #ifdef Aaip_encode_debuG if(*result_len != mem_size) { fprintf(stderr, "aaip_encode(): MEMORY MISMATCH BY %d BYTES\n", (int) (mem_size - *result_len)); } else { unsigned char *hpt; hpt= malloc(*result_len); if(hpt != NULL) { memcpy(hpt, *result, *result_len); free(*result); *result= hpt; } } ret= 0; for(i= 0; i < *result_len; i+= ((unsigned char *) (*result))[i + 2]) ret++; if(ret != (int) number_of_fields) { fprintf(stderr, "aaip_encode(): WRONG NUMBER OF FIELDS %d <> %d\n", (int) number_of_fields, ret); } #endif /* Aaip_encode_debuG */ return(number_of_fields); } static void aaip_encode_byte(unsigned char *result, size_t *result_fill, unsigned char value) { result[(*result_fill / 250) * 255 + 5 + (*result_fill % 250)]= value; (*result_fill)++; } static int aaip_encode_comp(unsigned char *result, size_t *result_fill, int prefix, char *data, size_t l, int flag) { size_t todo; char *rpt, *comp_start; if(l == 0 && prefix <= 0) { aaip_encode_byte(result, result_fill, 0); aaip_encode_byte(result, result_fill, 0); return(1); } for(rpt= data; rpt - data < (ssize_t) l;) { todo= l - (rpt - data) + (prefix > 0); aaip_encode_byte(result, result_fill, (todo > 255)); if(todo > 255) todo= 255; aaip_encode_byte(result, result_fill, todo); if(prefix > 0) { aaip_encode_byte(result, result_fill, prefix); todo--; prefix= 0; } for(comp_start= rpt; rpt - comp_start < (ssize_t) todo; rpt++) aaip_encode_byte(result, result_fill, *((unsigned char *) rpt)); } return(1); } /* Write the component records for name and attr. Skip the positions of AAIP field headers. @param flag bit0= only count but do not produce result */ static int aaip_encode_pair(char *name, size_t attr_length, char *attr, unsigned int *num_recs, size_t *comp_size, unsigned char *result, size_t result_fill, int flag) { size_t l; int i, prefix= 0; #ifdef Aaip_with_short_namespaceS /* translate name into eventual short form */ for(i= Aaip_min_named_spacE; i <= Aaip_max_named_spacE; i++) if(strncmp(name, Aaip_namespace_textS[i], strlen(Aaip_namespace_textS[i])) == 0) { name+= strlen(Aaip_namespace_textS[i]); prefix= i; } /* Eventually prepend escape marker for strange names */ if(prefix <= 0 && name[0] > 0 && name[0] <= Aaip_maxdef_namespacE) prefix= Aaip_namespace_literaL; #endif /* Aaip_with_short_namespaceS */ l= strlen(name) + (prefix > 0); *num_recs= l / 255 + (!!(l % 255)) + (l == 0) + attr_length / 255 + (!!(attr_length % 255)) + (attr_length == 0); *comp_size= l + attr_length + 2 * *num_recs; if(flag & 1) return(1); aaip_encode_comp(result, &result_fill, prefix, name, l - (prefix > 0), 0); aaip_encode_comp(result, &result_fill, 0, attr, attr_length, 0); return(1); } /* ----------- Encoder for ACLs ----------- */ static ssize_t aaip_encode_acl_text(char *acl_text, mode_t st_mode, size_t result_size, unsigned char *result, int flag); /* Convert an ACL text as of acl_to_text(3) into the value of an Arbitrary Attribute. According to AAIP this value is to be stored together with an empty name. @param acl_text The ACL in long text form @param st_mode The stat(2) permission bits to be used with flag bit3 @param result_len Number of bytes in the resulting value @param result *result will point to the start of the result string. This is malloc() memory which needs to be freed when no longer needed @param flag Bitfield for control purposes bit0= count only bit1= use numeric qualifiers rather than names bit2= this is a default ACL, prepend SWITCH_MARK bit3= check for completeness of list and eventually fill up with entries deduced from st_mode bit4= be verbose about failure causes @return >0 means ok <=0 means error -1= out of memory -2= program error with prediction of result size -3= error with conversion of name to uid or gid ISO_AAIP_ACL_MULT_OBJ= multiple entries of user::, group::, other:: */ int aaip_encode_acl(char *acl_text, mode_t st_mode, size_t *result_len, unsigned char **result, int flag) { ssize_t bytes; *result= NULL; *result_len= 0; bytes= aaip_encode_acl_text(acl_text, st_mode, (size_t) 0, NULL, 1 | (flag & (2 | 4 | 8 | 16))); if(bytes < -2) return(bytes); if(bytes < 0) return((int) bytes - 1); if(flag & 1) { *result_len= bytes; return(1); } *result= calloc(bytes + 1, 1); if(*result == NULL) return(-1); (*result)[bytes]= 0; *result_len= bytes; bytes= aaip_encode_acl_text(acl_text, st_mode, *result_len, *result, (flag & (2 | 4 | 8 | 16))); if(bytes < -2) return(bytes); if(bytes < 0) return((int) bytes - 1); if((size_t) bytes != *result_len) { *result_len= 0; return(-2); } return(1); } static double aaip_numeric_id(char *name, int flag) { double num; char *cpt; for(cpt= name; *cpt != 0; cpt++) if(*cpt < '0' || *cpt >'9') break; if(*cpt != 0) return(-1); sscanf(name, "%lf", &num); return(num); } static int aaip_make_aaip_perms(int r, int w, int x) { int perms; perms= 0; if(r) perms|= Aaip_READ; if(w) perms|= Aaip_WRITE; if(x) perms|= Aaip_EXEC; return(perms); } /* @param result_size Number of bytes to store result @param result Pointer to the start of the result string. @param flag Bitfield for control purposes bit0= count only, do not really produce bytes bit1= use numeric qualifiers bit2= this is a default ACL, prepend SWITCH_MARK 1 bit3= check for completeness of list and eventually fill up with entries deduced from st_mode bit4= be verbose about failure causes @return >=0 number of bytes produced resp. counted <0 means error -1: result size overflow -2: conversion error with user name or group name ISO_AAIP_ACL_MULT_OBJ: multiple entries of user::, group::, other:: */ static ssize_t aaip_encode_acl_text(char *acl_text, mode_t st_mode, size_t result_size, unsigned char *result, int flag) { char *rpt, *npt, *cpt; int qualifier= 0, perms, type, i, qualifier_len= 0, num_recs, needed= 0, ret; unsigned int has_u= 0, has_g= 0, has_o= 0, has_m= 0, is_trivial= 1; uid_t uid, huid; gid_t gid, hgid; ssize_t count= 0; struct passwd *pwd; struct group *grp; char *name = NULL; int name_size= 1024; double num; LIBISO_ALLOC_MEM(name, char, name_size); if(flag & 4) { /* set SWITCH_MARK to indicate a default ACL */; if(!(flag & 1)) { if((size_t) count >= result_size) goto result_size_overflow; result[count]= (Aaip_SWITCH_MARK << 4) | Aaip_EXEC; } count++; } for(rpt= acl_text; *rpt != 0; rpt= npt) { npt= strchr(rpt, '\n'); if(npt == 0) npt= rpt + strlen(rpt); else npt++; if(*rpt == '#') continue; cpt= strchr(rpt, ':'); if(cpt == NULL) continue; cpt= strchr(cpt + 1, ':'); if(cpt == NULL) continue; qualifier= 0; if(strncmp(rpt, "user:", 5) == 0) { if(cpt - rpt == 5) { type= Aaip_ACL_USER_OBJ; if (has_u) { /* >>> Duplicate u:: entry. */; /* >>> ??? If it matches the previous one: ignore */ if(flag & 16) iso_msg_submit(-1, ISO_AAIP_ACL_MULT_OBJ, 0, "Duplicate u:: entry detected in ACL text"); ret = ISO_AAIP_ACL_MULT_OBJ; goto ex; } has_u++; } else { if(cpt - (rpt + 5) >= name_size) continue; is_trivial= 0; strncpy(name, rpt + 5, cpt - (rpt + 5)); name[cpt - (rpt + 5)]= 0; if(flag & 2) { type= Aaip_ACL_USER_N; pwd= getpwnam(name); if(pwd == NULL) { num= aaip_numeric_id(name, 0); if(num <= 0) { /* ACL_USER is not part of AAIP 2.0 */ if(flag & 16) iso_msg_submit(-1, ISO_AAIP_BAD_ACL_TEXT, 0, "Unknown user name found in ACL text: '%s'", name); {ret= -2; goto ex;} } uid= huid= num; } else uid= huid= pwd->pw_uid; /* Convert uid into Qualifier Record */ for(i= 0; huid != 0; i++) huid= huid >> 8; qualifier_len= i; if(qualifier_len <= 0) qualifier_len= 1; for(i= 0; i < qualifier_len ; i++) name[i]= uid >> (8 * (qualifier_len - i - 1)); } else { type= Aaip_ACL_USER; qualifier_len= strlen(name); if(qualifier_len <= 0) qualifier_len= 1; } qualifier= 1; } } else if(strncmp(rpt, "group:", 6) == 0) { if(cpt - rpt == 6) { type= Aaip_ACL_GROUP_OBJ; if (has_g) { /* >>> Duplicate g:: entry. */; /* >>> ??? If it matches the previous one: ignore */ if(flag & 16) iso_msg_submit(-1, ISO_AAIP_ACL_MULT_OBJ, 0, "Duplicate g:: entry detected in ACL text"); ret = ISO_AAIP_ACL_MULT_OBJ; goto ex; } has_g++; } else { if(cpt - (rpt + 6) >= name_size) continue; is_trivial= 0; strncpy(name, rpt + 6, cpt - (rpt + 6)); name[cpt - (rpt + 6)]= 0; if(flag & 2) { type= Aaip_ACL_GROUP_N; grp= getgrnam(name); if(grp == NULL) { num= aaip_numeric_id(name, 0); if(num <= 0) { /* ACL_GROUP is not part of AAIP 2.0 */ if(flag & 16) iso_msg_submit(-1, ISO_AAIP_BAD_ACL_TEXT, 0, "Unknown group name found in ACL text: '%s'", name); {ret= -2; goto ex;} } gid= hgid= num; } else gid= hgid= grp->gr_gid; /* Convert gid into Qualifier Record */ for(i= 0; hgid != 0; i++) hgid= hgid >> 8; qualifier_len= i; if(qualifier_len <= 0) qualifier_len= 1; for(i= 0; i < qualifier_len ; i++) name[i]= gid >> (8 * (qualifier_len - i - 1)); } else { type= Aaip_ACL_GROUP; qualifier_len= strlen(name); if(qualifier_len <= 0) qualifier_len= 1; } qualifier= 1; } } else if(strncmp(rpt, "other:", 6) == 0) { type= Aaip_ACL_OTHER; if (has_o) { /* >>> Duplicate o:: entry. */; /* >>> ??? If it matches the previous one: ignore */ if(flag & 16) iso_msg_submit(-1, ISO_AAIP_ACL_MULT_OBJ, 0, "Duplicate o:: entry detected in ACL text"); ret = ISO_AAIP_ACL_MULT_OBJ; goto ex; } has_o++; } else if(strncmp(rpt, "mask:", 5) == 0) { type= Aaip_ACL_MASK; has_m++; } else continue; if(npt - cpt < 4) continue; perms= aaip_make_aaip_perms(cpt[1] == 'r', cpt[2] == 'w', cpt[3] == 'x'); if(!(flag & 1)) { if((size_t) count >= result_size) goto result_size_overflow; result[count]= perms | ((!!qualifier) << 3) | (type << 4); } count++; if(qualifier) { num_recs= (qualifier_len / 127) + !!(qualifier_len % 127); if(!(flag & 1)) { if((size_t) (count + 1) > result_size) goto result_size_overflow; for(i= 0; i < num_recs; i++) { if(i < num_recs - 1) result[count++]= 255; else { result[count++]= (qualifier_len % 127); if(result[count - 1] == 0) result[count - 1]= 127; } if((size_t) (count + (result[count - 1] & 127)) > result_size) goto result_size_overflow; memcpy(result + count, name + i * 127, result[count - 1] & 127); count+= result[count - 1] & 127; } } else count+= qualifier_len + num_recs; } } if (flag & 8) { /* add eventually missing mandatory ACL entries */ needed= (!has_u) + (!has_g) + (!has_o) + !(is_trivial || has_m); if(flag & 1) count+= needed; else { if((size_t) (count + needed) > result_size) goto result_size_overflow; } } if ((flag & 8) && needed > 0 && !(flag & 1)) { if(!has_u) { perms= aaip_make_aaip_perms(st_mode & S_IRUSR, st_mode & S_IWUSR, st_mode * S_IXUSR); result[count++]= perms | (Aaip_ACL_USER_OBJ << 4); } if(!has_g) { perms= aaip_make_aaip_perms(st_mode & S_IRGRP, st_mode & S_IWGRP, st_mode * S_IXGRP); result[count++]= perms | (Aaip_ACL_GROUP_OBJ << 4); } if(!has_o) { perms= aaip_make_aaip_perms(st_mode & S_IROTH, st_mode & S_IWOTH, st_mode * S_IXOTH); result[count++]= perms | (Aaip_ACL_OTHER << 4); } if(!(is_trivial | has_m)) { perms= aaip_make_aaip_perms(st_mode & S_IRGRP, st_mode & S_IWGRP, st_mode * S_IXGRP); result[count++]= perms | (Aaip_ACL_MASK << 4); } } ret= count; ex:; LIBISO_FREE_MEM(name); return(ret); result_size_overflow:; if(flag & 16) iso_msg_submit(-1, ISO_ASSERT_FAILURE, 0, "Program error: Text to ACL conversion result size overflow"); ret= -1; goto ex; } int aaip_encode_both_acl(char *a_acl_text, char *d_acl_text, mode_t st_mode, size_t *result_len, unsigned char **result, int flag) { int ret; size_t a_acl_len= 0, d_acl_len= 0, acl_len= 0; unsigned char *a_acl= NULL, *d_acl= NULL, *acl= NULL; if(a_acl_text != NULL) { ret= aaip_encode_acl(a_acl_text, st_mode, &a_acl_len, &a_acl, flag & (1 | 2 | 8 | 16)); if(ret <= 0) goto ex; } if(d_acl_text != NULL) { ret= aaip_encode_acl(d_acl_text, (mode_t) 0, &d_acl_len, &d_acl, (flag & (1 | 2 | 16)) | 4); if(ret <= 0) goto ex; } if(a_acl == NULL || a_acl_len == 0) { acl= d_acl; d_acl= NULL; acl_len= d_acl_len; } else if (d_acl == NULL || d_acl_len == 0) { acl= a_acl; a_acl= NULL; acl_len= a_acl_len; } else { acl= calloc(a_acl_len + d_acl_len, 1); if(acl == NULL) {ret = -1; goto ex;} memcpy(acl, a_acl, a_acl_len); memcpy(acl + a_acl_len, d_acl, d_acl_len); acl_len= a_acl_len + d_acl_len; } *result= acl; *result_len= acl_len; ret= 1; ex:; if(a_acl != NULL) free(a_acl); if(d_acl != NULL) free(d_acl); return(ret); } /* GNU/Linux man 5 acl says: The permissions defined by ACLs are a superset of the permissions speci- fied by the file permission bits. The permissions defined for the file owner correspond to the permissions of the ACL_USER_OBJ entry. The per- missions defined for the file group correspond to the permissions of the ACL_GROUP_OBJ entry, if the ACL has no ACL_MASK entry. If the ACL has an ACL_MASK entry, then the permissions defined for the file group corre- spond to the permissions of the ACL_MASK entry. The permissions defined for the other class correspond to the permissions of the ACL_OTHER_OBJ entry. Modification of the file permission bits results in the modification of the permissions in the associated ACL entries. Modification of the per- missions in the ACL entries results in the modification of the file per- mission bits. */ /* Analyze occurrence of ACL tag types in long text form. If not disabled by parameter flag remove the entries of type "user::" , "group::" , "other::" , or "other:" from an ACL in long text form if they match the bits in st_mode as described by man 2 stat and man 5 acl. @param acl_text The text to be analyzed and eventually shortened. @param st_mode The component of struct stat which tells POSIX permission bits and eventually shall take equivalent bits as read from the ACL. The caller should submit a pointer to the st_mode variable which holds permissions as indicated by stat(2) resp. ECMA-119 and RRIP data. @param flag bit0= do not remove entries, only determine return value bit1= like bit0 but return immediately if a non-st_mode ACL entry is found bit2= update *st_mode by acl_text ("user::" -> S_IRWXU, "mask::"|"group::" -> S_IRWXG, "other::" -> S_IRWXO) bit3= update acl_text by *st_mode (same mapping as bit 2 but with reversed transfer direction) bit4= map "group::" <-> S_IRWXG in any case. I.e. ignore "mask::". @return <0 failure >=0 tells in its bits which tag types were found. The first three tell which types deviate from the corresponding st_mode settings: bit0= "other::" overrides S_IRWXO bit1= "group::" overrides S_IRWXG (no "mask::" found) bit2= "user::" overrides S_IRWXU The second three tell which types comply with st_mode: bit3= "other::" matches S_IRWXO bit4= "group::" matches S_IRWXG (no "mask::" found) bit5= "user::" matches S_IRWXU Given the nature of ACLs nearly all combinations are possible although some would come from invalid ACLs. bit6= other ACL tag types are present. Particularly: bit7= "user:...:" is present bit8= "group:...:" is present bit9= "mask::" is present */ int aaip_cleanout_st_mode(char *acl_text, mode_t *in_st_mode, int flag) { char *rpt, *wpt, *npt, *cpt; mode_t m, list_mode, st_mode; int tag_types= 0, has_mask= 0, do_cleanout = 0; list_mode= st_mode= *in_st_mode; do_cleanout = !(flag & 15); has_mask= strncmp(acl_text, "mask:", 5) == 0 || strstr(acl_text, "\nmask:") != NULL; if(has_mask && (flag & 2)) return(64 | 512); for(npt= wpt= rpt= acl_text; *npt != 0; rpt= npt + 1) { npt= strchr(rpt, '\n'); if(npt == NULL) npt= rpt + strlen(rpt); if(strncmp(rpt, "user:", 5) == 0) { if(rpt[5] == ':' && npt - rpt == 9) { cpt= rpt + 6; m= 0; if(cpt[0] == 'r') m|= S_IRUSR; if(cpt[1] == 'w') m|= S_IWUSR; if(cpt[2] == 'x') m|= S_IXUSR; list_mode= (list_mode & ~S_IRWXU) | m; if((st_mode & S_IRWXU) == (m & S_IRWXU)) { tag_types|= 32; continue; } if(flag & 8) { cpt[0]= st_mode & S_IRUSR ? 'r' : '-'; cpt[1]= st_mode & S_IWUSR ? 'w' : '-'; cpt[2]= st_mode & S_IXUSR ? 'x' : '-'; } tag_types|= 4; } else { tag_types|= 64 | 128; } } else if(strncmp(rpt, "group:", 6) == 0) { if(rpt[6] == ':' && npt - rpt == 10 && ((flag & 16) || !has_mask)) { /* oddly: mask overrides group in st_mode */ cpt= rpt + 7; m= 0; if(cpt[0] == 'r') m|= S_IRGRP; if(cpt[1] == 'w') m|= S_IWGRP; if(cpt[2] == 'x') m|= S_IXGRP; list_mode= (list_mode & ~S_IRWXG) | m; if((st_mode & S_IRWXG) == (m & S_IRWXG)) { tag_types|= 16; continue; } if(flag & 8) { cpt[0]= st_mode & S_IRGRP ? 'r' : '-'; cpt[1]= st_mode & S_IWGRP ? 'w' : '-'; cpt[2]= st_mode & S_IXGRP ? 'x' : '-'; } tag_types|= 2; } else { if(rpt[6] == ':' && npt - rpt == 10) tag_types|= 1024; else tag_types|= 64 | 256; } } else if(strncmp(rpt, "other::", 7) == 0 && npt - rpt == 10) { cpt= rpt + 7; others_st_mode:; m= 0; if(cpt[0] == 'r') m|= S_IROTH; if(cpt[1] == 'w') m|= S_IWOTH; if(cpt[2] == 'x') m|= S_IXOTH; list_mode= (list_mode & ~S_IRWXO) | m; if((st_mode & S_IRWXO) == (m & S_IRWXO)) { tag_types|= 8; continue; } if(flag & 8) { cpt[0]= st_mode & S_IROTH ? 'r' : '-'; cpt[1]= st_mode & S_IWOTH ? 'w' : '-'; cpt[2]= st_mode & S_IXOTH ? 'x' : '-'; } tag_types|= 1; } else if(strncmp(rpt, "other:", 6) == 0 && npt - rpt == 9) { cpt= rpt + 7; goto others_st_mode; } else if(strncmp(rpt, "mask::", 6) == 0 && npt - rpt == 9) { cpt= rpt + 6; mask_st_mode:; tag_types|= 64 | 512; if(!(flag & 16)) { /* oddly: mask overrides group in st_mode */ m= 0; if(cpt[0] == 'r') m|= S_IRGRP; if(cpt[1] == 'w') m|= S_IWGRP; if(cpt[2] == 'x') m|= S_IXGRP; list_mode= (list_mode & ~S_IRWXG) | m; if(flag & 8) { cpt[0]= st_mode & S_IRGRP ? 'r' : '-'; cpt[1]= st_mode & S_IWGRP ? 'w' : '-'; cpt[2]= st_mode & S_IXGRP ? 'x' : '-'; } } } else if(strncmp(rpt, "mask:", 5) == 0 && npt - rpt == 8) { cpt= rpt + 5; goto mask_st_mode; } else if(*rpt != 0) { tag_types|= 64; } if (flag & 2) goto ex; if(wpt == rpt) { wpt= npt + 1; continue; } if(do_cleanout) memmove(wpt, rpt, 1 + npt - rpt); wpt+= 1 + npt - rpt; } if(do_cleanout) { if(wpt == acl_text) *wpt= 0; else if(*(wpt - 1) != 0) *wpt= 0; } ex:; if(flag & 4) *in_st_mode= list_mode; return(tag_types); } /* Important: acl_text must provide 42 bytes more than its current length ! */ int aaip_add_acl_st_mode(char *acl_text, mode_t st_mode, int flag) { char *wpt; int tag_types= 0; tag_types = aaip_cleanout_st_mode(acl_text, &st_mode, 1); if(!(tag_types & (4 | 32))) { wpt= acl_text + strlen(acl_text); sprintf(wpt, "user::%c%c%c\n", st_mode & S_IRUSR ? 'r' : '-', st_mode & S_IWUSR ? 'w' : '-', st_mode & S_IXUSR ? 'x' : '-'); } if(!(tag_types & (2 | 16 | 1024))) { wpt= acl_text + strlen(acl_text); sprintf(wpt, "group::%c%c%c\n", st_mode & S_IRGRP ? 'r' : '-', st_mode & S_IWGRP ? 'w' : '-', st_mode & S_IXGRP ? 'x' : '-'); } if(!(tag_types & (1 | 8))) { wpt= acl_text + strlen(acl_text); sprintf(wpt, "other::%c%c%c\n", st_mode & S_IROTH ? 'r' : '-', st_mode & S_IWOTH ? 'w' : '-', st_mode & S_IXOTH ? 'x' : '-'); } if((tag_types & (128 | 256)) && !(tag_types & 512)) { wpt= acl_text + strlen(acl_text); sprintf(wpt, "mask::%c%c%c\n", st_mode & S_IRGRP ? 'r' : '-', st_mode & S_IWGRP ? 'w' : '-', st_mode & S_IXGRP ? 'x' : '-'); } return(1); } int aaip_encode_lfa_flags(uint64_t lfa_flags, unsigned char value[8], int *length, int flag) { int i, l; *length = 1; value[0] = 0; /* How many bytes are needed to catch all set bits ? Minimum is 1. */ l= 8; for(i= 1; i < l; i++) if(lfa_flags < (uint64_t) 1 << (i * 8)) break; *length= i; for(i= 0; i < *length; i++) value[*length - 1 - i]= (lfa_flags >> (8 * i)) & 0xff; return(1); } /* --------------------------------- Decoder ---------------------------- */ /* --- private --- */ /* Not less than 2 * 2048 */ #define Aaip_buffer_sizE 4096 /* Enough for one full component record and three empty ones which might get added in case of unclean end of attribute list. */ #define Aaip_buffer_reservE (257 + 3 * 2) struct aaip_state { /* AAIP field status */ int aa_head_missing; /* number of bytes needed to complete field header */ int aa_missing; /* number of bytes needed to complete current field */ int aa_ends; /* 0= still fields expected, 1= last field being processed, 2= all fields processed, 3= all is delivered */ /* Buffer for component records */ int recs_invalid; /* number of components to skip */ unsigned char recs[Aaip_buffer_sizE + Aaip_buffer_reservE]; size_t recs_fill; unsigned char *recs_start; int rec_head_missing; /* number of bytes needed to complete rec header */ int rec_missing; /* number of bytes needed to complete current rec */ int rec_ends; /* Counter for completed data */ unsigned int num_recs; size_t ready_bytes; /* Counter and meaning for completed components */ unsigned int num_components; size_t end_of_components; /* start index of eventual incomplete component */ int first_is_name; /* Last return value of aaip_decode_pair() */ int pair_status; unsigned int pairs_skipped; /* status of aaip_decode_attrs() */ size_t list_mem_used; size_t list_size; size_t list_num_attrs; char **list_names; size_t *list_value_lengths; char **list_values; char *name_buf; size_t name_buf_size; size_t name_buf_fill; char *value_buf; size_t value_buf_size; size_t value_buf_fill; int list_pending_pair; }; /* ------- functions ------ */ size_t aaip_count_bytes(unsigned char *data, int flag) { int done = 0; unsigned char *aapt; for(aapt= data; !done; aapt += aapt[2]) done = !(aapt[4] & 1); return((size_t) (aapt - data)); } size_t aaip_sizeof_aaip_state(void) { return((size_t) sizeof(struct aaip_state)); } int aaip_init_aaip_state(struct aaip_state *aaip, int flag) { aaip->aa_head_missing= 5; aaip->aa_missing= 0; aaip->recs_invalid= 0; memset(aaip->recs, 0, Aaip_buffer_sizE + Aaip_buffer_reservE); aaip->recs_fill= 0; aaip->recs_start= aaip->recs; aaip->rec_head_missing= 2; aaip->rec_missing= 0; aaip->rec_ends= 0; aaip->num_recs= 0; aaip->ready_bytes= 0; aaip->num_components= 0; aaip->end_of_components= 0; aaip->first_is_name= 1; aaip->pair_status= 2; aaip->pairs_skipped= 0; aaip->list_mem_used= 0; aaip->list_size= 0; aaip->list_num_attrs= 0; aaip->list_names= NULL; aaip->list_value_lengths= NULL; aaip->list_values= NULL; aaip->name_buf= NULL; aaip->name_buf_size= 0; aaip->name_buf_fill= 0; aaip->value_buf= NULL; aaip->value_buf_size= 0; aaip->value_buf_fill= 0; aaip->list_pending_pair= 0; return(1); } /* */ #define Aaip_with_ring_buffeR yes #ifdef Aaip_with_ring_buffeR /* Compute the one or two byte intervals in the ring buffer which form a given byte interval in the virtual shift fifo. @param idx The byte start index in the virtual shift fifo. @param todo Number of bytes to cover @param start_pt Will return the start address of the first interval @param at_start_pt Will return the size of the first interval @param at_recs Will return the size of the second interval which always starts at aaip->recs @param flag Bitfield for control purposes @return 1= next start_pt is *start_pt + *at_start_pt 2= next start_pt is aaip->recs + *at_recs */ static int aaip_ring_adr(struct aaip_state *aaip, size_t idx, size_t todo, unsigned char **start_pt, size_t *at_start_pt, size_t *at_recs, int flag) { size_t ahead; ahead= Aaip_buffer_sizE + Aaip_buffer_reservE - (aaip->recs_start - aaip->recs); if(idx < ahead) *start_pt= (aaip->recs_start + idx); else *start_pt= aaip->recs + (idx - ahead); ahead= Aaip_buffer_sizE + Aaip_buffer_reservE - (*start_pt - aaip->recs); if(todo >= ahead) { *at_start_pt= ahead; *at_recs= todo - ahead; return(2); } *at_start_pt= todo; *at_recs= 0; return(1); } /* @param flag Bitfield for control purposes bit0= count as ready_bytes */ static int aaip_push_to_recs(struct aaip_state *aaip, unsigned char *data, size_t todo, int flag) { unsigned char *start_pt; size_t at_start_pt, at_recs; aaip_ring_adr(aaip, aaip->recs_fill, todo, &start_pt, &at_start_pt, &at_recs, 0); if(at_start_pt > 0) memcpy(start_pt, data, at_start_pt); if(at_recs > 0) memcpy(aaip->recs, data + at_start_pt, at_recs); aaip->recs_fill+= todo; if(flag & 1) aaip->ready_bytes+= todo; return(1); } static int aaip_read_from_recs(struct aaip_state *aaip, size_t idx, unsigned char *data, size_t num_data, int flag) { unsigned char *start_pt; size_t at_start_pt, at_recs; aaip_ring_adr(aaip, idx, num_data, &start_pt, &at_start_pt, &at_recs, 0); if(at_start_pt > 0) memcpy(data, start_pt, at_start_pt); if(at_recs > 0) memcpy(data + at_start_pt, aaip->recs, at_recs); return(1); } static int aaip_set_buffer_byte(struct aaip_state *aaip, size_t idx, unsigned char data, int flag) { unsigned char *start_pt; size_t at_start_pt, at_recs; aaip_ring_adr(aaip, idx, 1, &start_pt, &at_start_pt, &at_recs, 0); *start_pt= data; return(1); } static int aaip_get_buffer_byte(struct aaip_state *aaip, size_t idx, int flag) { unsigned char *start_pt; size_t at_start_pt, at_recs; aaip_ring_adr(aaip, idx, 1, &start_pt, &at_start_pt, &at_recs, 0); return((int) *start_pt); } static int aaip_shift_recs(struct aaip_state *aaip, size_t todo, int flag) { int ret; unsigned char *start_pt; size_t at_start_pt, at_recs; if(todo < aaip->recs_fill) { ret= aaip_ring_adr(aaip, 0, todo, &start_pt, &at_start_pt, &at_recs, 0); if(ret == 1) aaip->recs_start= start_pt + todo; else aaip->recs_start= aaip->recs + at_recs; } else { aaip->recs_start= aaip->recs; } aaip->recs_fill-= todo; if(aaip->end_of_components >= todo) aaip->end_of_components-= todo; else aaip->end_of_components= 0; return(1); } #else /* Aaip_with_ring_buffeR */ /* @param flag Bitfield for control purposes bit0= count as ready_bytes */ static int aaip_push_to_recs(struct aaip_state *aaip, unsigned char *data, size_t todo, int flag) { memcpy(aaip->recs + aaip->recs_fill, data, todo); aaip->recs_fill+= todo; if(flag & 1) aaip->ready_bytes+= todo; return(1); } static int aaip_read_from_recs(struct aaip_state *aaip, size_t idx, unsigned char *data, size_t num_data, int flag) { memcpy(data, aaip->recs + idx, num_data); return(1); } static int aaip_set_buffer_byte(struct aaip_state *aaip, size_t idx, unsigned char data, int flag) { aaip->recs[idx]= data; return(1); } static int aaip_get_buffer_byte(struct aaip_state *aaip, size_t idx, int flag) { return((int) aaip->recs[idx]); } static int aaip_shift_recs(struct aaip_state *aaip, size_t todo, int flag) { if(todo < aaip->recs_fill) memmove(aaip->recs, aaip->recs + todo, aaip->recs_fill - todo); aaip->recs_fill-= todo; if(aaip->end_of_components >= todo) aaip->end_of_components-= todo; else aaip->end_of_components= 0; return(1); } #endif /* ! Aaip_with_ring_buffeR */ static int aaip_consume_rec_head(struct aaip_state *aaip, unsigned char **data, size_t *num_data, int flag) { size_t todo; todo= *num_data; if(todo > (size_t) aaip->aa_missing) todo= aaip->aa_missing; if(todo >= (size_t) aaip->rec_head_missing) todo= aaip->rec_head_missing; if(!aaip->recs_invalid) aaip_push_to_recs(aaip, *data, todo, 0); aaip->rec_head_missing-= todo; if(aaip->rec_head_missing == 0) { aaip->rec_missing= aaip_get_buffer_byte(aaip, aaip->recs_fill - 1, 0); aaip->rec_ends= !(aaip_get_buffer_byte(aaip, aaip->recs_fill - 2, 0) & 1); } aaip->aa_missing-= todo; (*num_data)-= todo; (*data)+= todo; return(1); } static int aaip_consume_rec_data(struct aaip_state *aaip, unsigned char **data, size_t *num_data, int flag) { size_t todo; todo= *num_data; if(todo > (size_t) aaip->aa_missing) todo= aaip->aa_missing; if(todo > (size_t) aaip->rec_missing) todo= aaip->rec_missing; if(!aaip->recs_invalid) aaip_push_to_recs(aaip, *data, todo, 1); aaip->rec_missing-= todo; aaip->aa_missing-= todo; (*num_data)-= todo; (*data)+= todo; if(aaip->rec_missing <= 0) { if(aaip->recs_invalid > 0) { if(aaip->rec_ends) aaip->recs_invalid--; } else { aaip->num_recs++; if(aaip->rec_ends) { aaip->num_components++; aaip->end_of_components= aaip->recs_fill; } } aaip->rec_head_missing= 2; } return(0); } static int aaip_consume_aa_head(struct aaip_state *aaip, unsigned char **data, size_t *num_data, int flag) { size_t todo; unsigned char aa_head[5]; todo= *num_data; if(todo >= (size_t) aaip->aa_head_missing) todo= aaip->aa_head_missing; aaip_push_to_recs(aaip, *data, todo, 0); aaip->aa_head_missing-= todo; if(aaip->aa_head_missing == 0) { aaip_read_from_recs(aaip, aaip->recs_fill - 5, aa_head, 5, 0); if(aa_head[0] != 'A' || (aa_head[1] != 'L' && aa_head[1] != 'A') || aa_head[3] != 1) return(-1); aaip->aa_missing= aa_head[2]; aaip->aa_ends= !(aa_head[4] & 1); aaip->recs_fill-= 5; /* AAIP field heads do not get delivered */ if(aaip->aa_missing >= 5) aaip->aa_missing-= 5; else aaip->aa_missing= 0; } (*num_data)-= todo; (*data)+= todo; return(1); } static int aaip_consume_aa_data(struct aaip_state *aaip, unsigned char **data, size_t *num_data, int flag) { size_t i; static unsigned char zero_char[2]= {0, 0}; while(*num_data > 0 && aaip->aa_missing > 0) { if(aaip->rec_head_missing > 0) { aaip_consume_rec_head(aaip, data, num_data, 0); if(*num_data == 0 || aaip->aa_missing <= 0) return(1); } aaip_consume_rec_data(aaip, data, num_data, 0); } if(aaip->aa_missing <= 0) { if(aaip->aa_ends) { /* Check for incomplete pair and eventually make emergency closure */ if(aaip->rec_head_missing != 2) { /* incomplete record detected */ if(aaip->rec_head_missing) { /* fake 0 length record */ aaip_set_buffer_byte(aaip, aaip->recs_fill - 1, (unsigned char) 0, 0); aaip_push_to_recs(aaip, zero_char, 1, 0); } else { /* fill in missing btes */ for(i= 0; (int) i < aaip->rec_missing; i++) aaip_push_to_recs(aaip, zero_char, 1, 1); } aaip->rec_head_missing= 2; aaip->rec_missing= 0; aaip->num_recs++; if(aaip->rec_ends) { aaip->num_components++; aaip->end_of_components= aaip->recs_fill; } } if(aaip->end_of_components != aaip->recs_fill && aaip->end_of_components != 0) { /* incomplete component detected */ /* add empty end record */ aaip_push_to_recs(aaip, zero_char, 2, 0); aaip->num_recs++; aaip->num_components++; aaip->end_of_components= aaip->recs_fill; } if(!(aaip->first_is_name ^ (aaip->num_components % 2))) { /* value component is missing */ /* add dummy component */ aaip_push_to_recs(aaip, zero_char, 2, 0); aaip->num_recs++; aaip->num_components++; aaip->end_of_components= aaip->recs_fill; } aaip->aa_ends= 2; } else aaip->aa_head_missing= 5; } return(0); } /* Submit small data chunk for decoding. The return value will tell whether data are pending for being fetched. @param aaip The AAIP decoder context @param data Not more than 2048 bytes input for the decoder @parm num_data Number of bytes in data 0 inquires the buffer status avoiding replies <= 0 @param ready_bytes Number of decoded bytes ready for delivery @param flag Bitfield for control purposes @return -1= non-AAIP field detected *ready_bytes gives number of consumed bytes in data 0= cannot accept data because buffer full 1= no component record complete, submit more data 2= component record complete, may be delivered 3= component complete, may be delivered 4= no component available, no more data expected, done */ int aaip_submit_data(struct aaip_state *aaip, unsigned char *data, size_t num_data, size_t *ready_bytes, int flag) { int ret; unsigned char *in_data; if(aaip->aa_ends == 3) return(4); in_data= data; if(num_data == 0) goto ex; if(aaip->recs_fill + num_data > Aaip_buffer_sizE) return(0); while(num_data > 0) { if(aaip->aa_head_missing > 0) { ret= aaip_consume_aa_head(aaip, &data, &num_data, 0); if(ret < 0) { *ready_bytes= data - in_data; return(-1); } if(num_data == 0 || aaip->aa_missing <= 0) goto ex; } aaip_consume_aa_data(aaip, &data, &num_data, 0); if(aaip->aa_missing) break; } ex:; *ready_bytes= aaip->ready_bytes; if(aaip->num_components > 0) return(3); if(aaip->num_recs > 0) return(2); if(aaip->aa_ends && aaip->aa_head_missing == 0 && aaip->aa_missing == 0) aaip->aa_ends= 2; if(aaip->aa_ends == 2 && aaip->num_recs == 0) aaip->aa_ends= 3; if(aaip->aa_ends == 3) return(4); return(1); } /* Fetch the available part of current component. The return value will tell whether it belongs to name or to value and whether that name or value is completed now. @param aaip The AAIP decoder context @param result Has to point to storage for the component data @param result_size Gives the amount of provided result storage @param num_result Will tell the number of fetched result bytes @param flag Bitfield for control purposes bit0= discard data rather than copying to result @return -2 = insufficient result_size -1 = no data ready for delivery 0 = result holds the final part of a name 1 = result holds an intermediate part of a name 2 = result holds the final part of a value 3 = result holds an intermediate part of a value */ int aaip_fetch_data(struct aaip_state *aaip, char *result, size_t result_size, size_t *num_result, int flag) { int ret= -1, complete= 0, payload; unsigned int i, num_bytes= 0, h; if(aaip->num_recs == 0) return(-1); /* Copy data until end of buffer or end of component */ h= 0; for(i= 0; i < aaip->num_recs && !complete; i++) { payload= aaip_get_buffer_byte(aaip, h + 1, 0); if(!(flag & 1)) { if(num_bytes + payload > result_size) return(-2); aaip_read_from_recs(aaip, h + 2, (unsigned char *) (result + num_bytes), payload, 0); *num_result= num_bytes + payload; } num_bytes+= payload; if(!(aaip_get_buffer_byte(aaip, h, 0) & 1)) complete= 1; h+= payload + 2; } aaip->ready_bytes-= num_bytes; aaip->num_recs-= i; /* Shift buffer */ aaip_shift_recs(aaip, h, 0); /* Compute reply */ ret= 2 * !aaip->first_is_name; if(complete) { aaip->first_is_name= !aaip->first_is_name; if(aaip->num_components > 0) aaip->num_components--; } else ret|= 1; return(ret); } /* Skip the current component and eventually the following value component. This has to be called if fetching of a component shall be aborted but the next component resp. pair shall be fetchable again. aaip_submit_data() will not indicate readiness for fetching until all bytes of the skipped components are submitted. Those bytes get discarded. @param aaip The AAIP decoder context @param flag Bitfield for control purposes bit0= do not skip value if current component is name @return <=0 error , 1= now in skip state, 2= not in skip state */ int aaip_skip_component(struct aaip_state *aaip, int flag) { int to_skip= 1; if(aaip->first_is_name && !(flag & 1)) to_skip= 2; if(aaip->recs_invalid) { aaip->recs_invalid+= to_skip; return(1); } if(aaip->num_components) { /* null-fetch */ aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1); to_skip--; } if(aaip->num_components && to_skip) { /* null-fetch */ aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1); to_skip--; } if(to_skip) { aaip->recs_fill= 0; aaip->num_recs= 0; aaip->ready_bytes= 0; } aaip->recs_invalid= to_skip; if(aaip->aa_ends == 2 && aaip->num_recs == 0) aaip->aa_ends= 3; return(1 + (aaip->num_recs > 0)); } /* ------------------------- Pair Level Interface ------------------------ */ /* @param flag Bitfield for control purposes bit0= do not skip oversized component but return -2 @return see aaip_decode_pair */ static int aaip_advance_pair(struct aaip_state *aaip, char *name, size_t name_size, size_t *name_fill, char *value, size_t value_size, size_t *value_fill, int flag) { int ret; char *wpt; size_t size, num; retry:; if(aaip->first_is_name) { wpt= name + *name_fill; size= name_size - *name_fill; } else { wpt= value + *value_fill; size= value_size - *value_fill; } ret= aaip_fetch_data(aaip, wpt, size, &num, 0); if(ret == -2) { /* insufficient result size */ if(flag & 1) return(-2); ret= aaip_skip_component(aaip, 0); *name_fill= *value_fill= 0; aaip->pairs_skipped++; if(ret == 2) /* Skip performed, valid data pending */ goto retry; } else if(ret == -1) { /* No data ready for delivery : may not happen */ return(-1); } else if(ret == 0) { /* result holds the final part of a name */ (*name_fill)+= num; /* peek for value data */ ret= aaip_submit_data(aaip, NULL, (size_t) 0, &num, 0); if(ret == 2 || ret == 3) { /* fetch value data */; ret= aaip_advance_pair(aaip, name, name_size, name_fill, value, value_size, value_fill, flag); return ret; } else if(ret == 4) return(5); } else if(ret == 1) { /* result holds an intermediate part of a name */ (*name_fill)+= num; } else if(ret == 2) { /* result holds the final part of a value */ (*value_fill)+= num; if(aaip->num_components >= 2) return(3); if(aaip->aa_ends == 2 && aaip->num_recs == 0) aaip->aa_ends= 3; if(aaip->aa_ends == 3) return(4); return(2); } else if(ret == 3) { /* result holds an intermediate part of a value */; (*value_fill)+= num; } else { return(-1); /* unknown reply from aaip_fetch_data() */ } return(1); } /* Accept raw input data and collect a pair of name and value. The return value will indicate whether the pair is complete, whether more pairs are complete or whether more data are desired. No input data will be accepted as long as complete pairs are pending. The end of the attribute list will be indicated. @param aaip The AAIP decoder context @param data The raw data to decode @param num_data Number of data bytes provided @param consumed Returns the number of consumed data bytes @param name Buffer to build the name string @param name_size Maximum number of bytes in name @param name_fill Holds the current buffer fill of name @param value Buffer to build the value string @param value_size Maximum number of bytes in value @param value_fill Holds the current buffer fill of value @param flag Bitfield for control purposes bit0= do not skip oversized pair but return -2 @return <0 error -3 buffer full (program error) -2 insufficient result_size (only with flag bit0) -1 non-AAIP field detected 0 data not accepted, first fetch pending pairs with num_data == 0 1 name and value are not valid yet, submit more data 2 name and value are valid, submit more data 3 name and value are valid, pairs pending, fetch with num_data == 0 4 name and value are valid, no more data expected 5 name and value are not valid, no more data expected */ int aaip_decode_pair(struct aaip_state *aaip, unsigned char *data, size_t num_data, size_t *consumed, char *name, size_t name_size, size_t *name_fill, char *value, size_t value_size, size_t *value_fill, int flag) { int ret; size_t ready_bytes= 0; #ifdef Aaip_with_short_namespaceS char prefix[Aaip_max_name_expansioN + 1]; size_t nl, pl; #endif *consumed= 0; if((aaip->pair_status < 0 && aaip->pair_status != -2) || aaip->pair_status == 4 || aaip->pair_status == 5) { /* dead ends */ ret= aaip->pair_status; goto ex; } else if(aaip->pair_status == 2 || aaip->pair_status == 3) { if(aaip->pair_status == 3 && num_data > 0) {ret= 0; goto ex;} /* Start a new pair */ if(!aaip->first_is_name) /* Eventually skip orphaned value */ aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1); *name_fill= *value_fill= 0; } if(num_data > 0) { ret= aaip_submit_data(aaip, data, num_data, &ready_bytes, 0); } else { ret= 1; if(aaip->num_components) ret= 3; else if(aaip->num_recs) ret= 2; } if(ret < 0) { /* non-AAIP field detected */ *consumed= ready_bytes; {ret= -1; goto ex;} } else if(ret == 0) { /* buffer overflow */; /* should not happen with correct usage */ {ret= -3; goto ex;} } else if(ret == 1) { /* no component record complete */ goto ex; } else if(ret == 2) { /* component record complete, may be delivered */ ; } else if(ret == 3) { /* component complete, may be delivered */ ; } else if(ret == 4) { /* no component available, no more data expected */ {ret= 5; goto ex;} } else {ret= -1; goto ex;} /* unknown reply from aaip_submit_data() */ *consumed= num_data; ret= aaip_advance_pair(aaip, name, name_size - Aaip_max_name_expansioN, name_fill, value, value_size, value_fill, flag & 1); if(aaip->aa_ends == 3) { if(ret >= 2 && ret <= 4) ret= 4; else ret= 5; } ex:; #ifdef Aaip_with_short_namespaceS if(ret >= 2 && ret <= 4 && *name_fill > 0) { /* Translate name from eventual short form */ nl= *name_fill; if(name[0] > 0 && name[0] <= Aaip_maxdef_namespacE) { prefix[0]= 0; if(name[0] == Aaip_namespace_literaL) { if(nl > 1) { /* Remove first character of name */ memmove(name, name + 1, nl - 1); (*name_fill)--; } } else if(name[0] == Aaip_namespace_systeM || name[0] == Aaip_namespace_useR || name[0] == Aaip_namespace_isofS || name[0] == Aaip_namespace_trusteD || name[0] == Aaip_namespace_securitY ) { strcpy(prefix, Aaip_namespace_textS[(int) name[0]]); pl= strlen(prefix); memmove(name + pl, name + 1, nl - 1); memcpy(name, prefix, pl); *name_fill= pl + nl - 1; } } } #endif /* Aaip_with_short_namespaceS */ aaip->pair_status= ret; return(ret); } unsigned int aaip_get_pairs_skipped(struct aaip_state *aaip, int flag) { return(aaip->pairs_skipped); } /* ------------------------- List Level Interface ------------------------ */ #define Aaip_initial_name_leN 256 #define Aaip_initial_value_leN 256 #define Aaip_initial_list_sizE 2 #define Aaip_list_enlargeR 1.5 /* @param flag Bitfield for control purposes bit0= do not update *buf_size */ static int aaip_enlarge_buf(struct aaip_state *aaip, size_t memory_limit, size_t item_size, char **buf, size_t *buf_size, int flag) { size_t new_size; char *new_buf; new_size= *buf_size * Aaip_list_enlargeR; if(aaip->list_mem_used + (new_size - *buf_size) * item_size >= memory_limit) return(3); aaip->list_mem_used+= (new_size - *buf_size) * item_size; new_buf= realloc(*buf, new_size * item_size); if(new_buf == NULL) return(-1); *buf= new_buf; if(!(flag & 1)) *buf_size= new_size; return(1); } /* Accept raw input data and collect arrays of name pointers, value lengths and value pointers. A handle object will emerge which finally has to be be freed by a call with bit 15. @param handle The decoding context. It will be created by this call with flag bit 0 or if *handle == NULL. This handle has to be the same as long as decoding goes on and finally has to be freed by a call with bit15. @param memory_limit Maximum number of bytes to allocate @param num_attr_limit Maximum number of name-value pairs to allocate @param data The raw data to decode @param num_data Number of data bytes provided @param consumed Returns the number of consumed data bytes @param flag Bitfield for control purposes bit0= this is the first call with the given handle (also in effect if *handle is NULL) bit15= end decoding : Free handle and its intermediate list memory. @return <=0 error -4 interpretation stalled, no valid result -3 program error, unexpected reply from lower layers -2 non-AAIP-field detected, arrays are complete, call aaip_get_decoded_attrs() -1 out of memory 1 not complete yet, submit more data 2 arrays are complete, call aaip_get_decoded_attrs() 3 limit exceeded, not complete yet, enlarge memory_limit or call with bit15 and give up 4 limit exceeded, call aaip_get_decoded_attrs() and try again */ int aaip_decode_attrs(struct aaip_state **handle, size_t memory_limit, size_t num_attr_limit, unsigned char *data, size_t num_data, size_t *consumed, int flag) { int ret; struct aaip_state *aaip; size_t h_num, *h_lengths, i, new_mem, pair_consumed= 0; char **h_names, **h_values, *hpt; *consumed= 0; if(flag & (1 << 15)) { if(*handle == NULL) return(0); ret= aaip_get_decoded_attrs(handle, &h_num, &h_names, &h_lengths, &h_values, 0); if(ret > 0) aaip_get_decoded_attrs(handle, &h_num, &h_names, &h_lengths, &h_values, 1 << 15); if((*handle)->name_buf != NULL) free((*handle)->name_buf); if((*handle)->value_buf != NULL) free((*handle)->value_buf); free((char *) *handle); *handle= NULL; return(1); } aaip= *handle; if(aaip == NULL || (flag & 1)) { aaip= *handle= calloc(1, sizeof(struct aaip_state)); if(*handle == NULL) return(-1); aaip_init_aaip_state(*handle, 0); } if(aaip->list_names == NULL || aaip->list_values == NULL || aaip->list_value_lengths == NULL) { /* Initialize arrays */ aaip->list_size= Aaip_initial_list_sizE; if(num_attr_limit > 0 && num_attr_limit < aaip->list_size) aaip->list_size= num_attr_limit; new_mem= aaip->list_size * (2*sizeof(char *) + sizeof(size_t)) + Aaip_initial_name_leN + Aaip_initial_value_leN; if(aaip->list_mem_used + new_mem >= memory_limit) return(3); aaip->list_mem_used+= new_mem; aaip->list_names= calloc(sizeof(char *), aaip->list_size); aaip->list_value_lengths= calloc(sizeof(size_t), aaip->list_size); aaip->list_values= calloc(sizeof(char *), aaip->list_size); if(aaip->list_names == NULL || aaip->list_value_lengths == NULL || aaip->list_values == NULL) return(-1); for(i= 0; i < aaip->list_size; i++) { aaip->list_names[i]= NULL; aaip->list_value_lengths[i]= 0; aaip->list_values[i]= NULL; } } if(aaip->name_buf == NULL || aaip->value_buf == NULL) { new_mem= Aaip_initial_name_leN + Aaip_initial_value_leN; if(aaip->list_mem_used >= memory_limit) return(3); aaip->list_mem_used+= new_mem; aaip->name_buf= calloc(1, Aaip_initial_name_leN); aaip->value_buf= calloc(1, Aaip_initial_value_leN); if(aaip->name_buf == NULL || aaip->value_buf == NULL) return(-1); aaip->name_buf_size= Aaip_initial_name_leN; aaip->value_buf_size= Aaip_initial_name_leN; } while(1) { if(aaip->list_pending_pair > 0) { /* the buffer holds a complete pair from a previous memory limit refusal */ ret= aaip->list_pending_pair; aaip->list_pending_pair= 0; } else { ret= aaip_decode_pair(aaip, data, num_data, &pair_consumed, aaip->name_buf, aaip->name_buf_size, &aaip->name_buf_fill, aaip->value_buf, aaip->value_buf_size, &aaip->value_buf_fill, 1); *consumed+= pair_consumed; } if(ret == -2) { /* insufficient result_size */ if(aaip->first_is_name) ret= aaip_enlarge_buf(aaip, memory_limit, (size_t) 1, &(aaip->name_buf), &(aaip->name_buf_size), 0); else ret= aaip_enlarge_buf(aaip, memory_limit, (size_t) 1, &(aaip->value_buf), &(aaip->value_buf_size), 0); if(ret != 1) return(ret); } else if(ret == -1) { /* non-AAIP field detected */ if(pair_consumed <= 0) return(-4); /* interpretation did not advance */ } else if(ret < 0) { /* other error */ return(-3); } else if(ret == 0) { /* first fetch pending pairs with num_data == 0 */ /* should not happen, fetch more pairs */; } else if(ret == 1) { /* name and value are not valid yet, submit more data */ return(1); } else if(ret == 2 || ret == 3 || ret == 4) { /* name and value are valid, submit more data */ /* name and value are valid, pairs pending, fetch with num_data == 0 */ /* name and value are valid, no more data expected */ aaip->list_pending_pair= ret; if(aaip->list_num_attrs >= aaip->list_size) { hpt= (char *) aaip->list_names; ret= aaip_enlarge_buf(aaip, memory_limit, sizeof(char *), &hpt, &(aaip->list_size), 1); if(ret != 1) return(ret); aaip->list_names= (char **) hpt; hpt= (char *) aaip->list_values; ret= aaip_enlarge_buf(aaip, memory_limit, sizeof(char *), &hpt, &(aaip->list_size), 1); if(ret != 1) return(ret); aaip->list_values= (char **) hpt; hpt= (char *) aaip->list_value_lengths; ret= aaip_enlarge_buf(aaip, memory_limit, sizeof(size_t), &hpt, &(aaip->list_size), 0); if(ret != 1) return(ret); aaip->list_value_lengths= (size_t *) hpt; } /* Allocate name and value in list */; if(aaip->list_mem_used + aaip->name_buf_fill + aaip->value_buf_fill + 2 > memory_limit) { return(3); } aaip->list_mem_used+= aaip->name_buf_fill + aaip->value_buf_fill + 2; i= aaip->list_num_attrs; aaip->list_names[i]= calloc(aaip->name_buf_fill + 1, 1); aaip->list_values[i]= calloc(aaip->value_buf_fill + 1, 1); memcpy(aaip->list_names[i], aaip->name_buf, aaip->name_buf_fill); aaip->list_names[i][aaip->name_buf_fill]= 0; memcpy(aaip->list_values[i], aaip->value_buf, aaip->value_buf_fill); aaip->list_values[i][aaip->value_buf_fill]= 0; aaip->list_value_lengths[i]= aaip->value_buf_fill; aaip->list_num_attrs++; aaip->name_buf_fill= aaip->value_buf_fill= 0; ret= aaip->list_pending_pair; aaip->list_pending_pair= 0; if(ret == 2) return(1); if(ret == 4) break; } else if(ret == 5) break; else return(-2); num_data= 0; /* consume pending pairs */ } aaip->list_pending_pair= 5; return(2); } /* Obtain the resulting attributes when aaip_decode_attrs() indicates to be done or to have the maximum possible amount of result ready. The returned data objects finally have to be freed by a call with flag bit 15. @param handle The decoding context created by aaip_decode_attrs() @param num_attrs Will return the number of name-value pairs @param names Will return an array of pointers to 0-terminated names @param value_lengths Will return an array with the lengths of values @param values Will return an array of pointers to 8-bit values @param flag Bitfield for control purposes bit15= free memory of names, value_lengths, values @return <0 error 0 no attribute list ready 1 ok */ int aaip_get_decoded_attrs(struct aaip_state **handle, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag) { size_t i; struct aaip_state *aaip; aaip= *((struct aaip_state **) handle); if(flag & (1 << 15)) { if(*names != NULL) { for(i= 0; i < *num_attrs; i++) { if((*names)[i] != NULL) free((*names)[i]); (*names)[i]= NULL; } free(*names); *names= NULL; } if(*values != NULL) { for(i= 0; i < *num_attrs; i++) { if((*values)[i] != NULL) free((*values)[i]); (*values)[i]= NULL; } free(*values); *values= NULL; } if(*value_lengths != NULL) free(*value_lengths); *value_lengths= NULL; *num_attrs= 0; return(1); } /* Check whether decoding is finished yet */ if(aaip->list_pending_pair != 5) return(0); *num_attrs= aaip->list_num_attrs; *names= aaip->list_names; *value_lengths= aaip->list_value_lengths; *values= aaip->list_values; /* Now the memory is owned by the caller */ aaip->list_num_attrs= 0; aaip->list_names= NULL; aaip->list_value_lengths= NULL; aaip->list_values= NULL; aaip->list_size= 0; aaip->list_pending_pair= 0; return(1); } /* ------ Decoder for ACLs ------ */ static int aaip_write_acl_line(char **result, size_t *result_size, char *tag_type, char *qualifier, char *permissions, int flag) { size_t needed, tag_len, perm_len, qualifier_len; tag_len= strlen(tag_type); qualifier_len= strlen(qualifier); perm_len= strlen(permissions); needed= tag_len + qualifier_len + perm_len + 3; if((flag & 1)) { (*result_size)+= needed; return(1); } if(needed + 1 > *result_size) /* +1 : want to append a trailing 0 */ return(-1); memcpy((*result), tag_type, tag_len); (*result)[tag_len]= ':'; memcpy((*result) + tag_len + 1, qualifier, qualifier_len); (*result)[tag_len + 1 + qualifier_len]= ':'; memcpy((*result) + tag_len + 1 + qualifier_len + 1, permissions, perm_len); (*result)[tag_len + 1 + qualifier_len + 1 + perm_len]= '\n'; (*result)[tag_len + 1 + qualifier_len + 1 + perm_len + 1] = 0; (*result)+= needed; (*result_size)-= needed; return(1); } static int aaip_read_qualifier(unsigned char *data, size_t num_data, char *name, size_t name_size, size_t *name_fill, int flag) { int is_done= 0; size_t rec_len= 0; unsigned char *rpt; *name_fill= 0; for(rpt= data; !is_done; rpt+= rec_len) { rec_len= (*rpt) & 127; is_done= !((*rpt) & 128); if(*name_fill + rec_len >= name_size || (size_t) (rpt + 1 + rec_len - data) > num_data) return(-1); memcpy(name + *name_fill, rpt + 1, rec_len); rpt+= 1 + rec_len; (*name_fill)+= rec_len; name[*name_fill]= 0; } return(1); } /* Convert an AAIP ACL attribute value into the long text form of ACL. @param data The raw data to decode @param num_data Number of data bytes provided @param consumed Returns the number of consumed data bytes @param acl_text Will be filled with ACL long text form @param acl_text_size Maximum number of bytes to be written to acl_text @param acl_text_fill Will return the number of bytes in acl_text @param flag Bitfield for control purposes bit0= count only, do not really produce bytes: acl_text will not be touched, acl_text_size will be ignored, *acl_text_fill will return the counted number plus 1 for a trailing zero. bit1= expected is a default ACL (see return value 2) @return 1 success 2 success, begin of default/access ACL encountered, submit data + *consumed for access/default ACL -1 error with reading of qualifier -2 error with writing of ACL text line -3 version mismatch -4 unknown tag type encountered */ int aaip_decode_acl(unsigned char *data, size_t num_data, size_t *consumed, char *acl_text, size_t acl_text_size, size_t *acl_text_fill, int flag) { unsigned char *rpt; char perm_text[4], *wpt, *name= NULL; int type, qualifier= 0, perm, ret, cnt, name_size= 1024; size_t w_size= 0, name_fill= 0, i; uid_t uid; gid_t gid; struct passwd *pwd; struct group *grp; LIBISO_ALLOC_MEM(name, char, name_size); cnt= flag & 1; *consumed= 0; wpt= acl_text; w_size= acl_text_size; *acl_text_fill= 0; for(rpt= data; (size_t) (rpt - data) < num_data; ) { perm= *rpt; strcpy(perm_text, "---"); if(perm & Aaip_READ) perm_text[0]= 'r'; if(perm & Aaip_WRITE) perm_text[1]= 'w'; if(perm & Aaip_EXEC) perm_text[2]= 'x'; type= (*rpt) >> 4; if(type == Aaip_FUTURE_VERSION) /* indicate to caller: version mismatch */ {ret = -3; goto ex;} qualifier= !!((*rpt) & 8); if(qualifier) { ret= aaip_read_qualifier(rpt + 1, num_data - (rpt + 1 - data), name, name_size, &name_fill, 0); if(ret <= 0) {ret = -1; goto ex;} } /* Advance read pointer */ (*consumed)+= 1 + (qualifier ? name_fill + 1 : 0); rpt+= 1 + (qualifier ? name_fill + 1 : 0); ret= 1; if(type == Aaip_TRANSLATE) { /* rightfully ignored yet */; continue; } else if(type == Aaip_ACL_USER_OBJ) { /* user::rwx */ ret= aaip_write_acl_line(&wpt, &w_size, "user", "", perm_text, cnt); } else if(type == Aaip_ACL_USER) { /* user::rwx */; ret= aaip_write_acl_line(&wpt, &w_size, "user", name, perm_text, cnt); } else if(type == Aaip_ACL_GROUP_OBJ) { /* user::rwx */ ret= aaip_write_acl_line(&wpt, &w_size, "group", "", perm_text, cnt); } else if(type == Aaip_ACL_GROUP) { /* group::rwx */; ret= aaip_write_acl_line(&wpt, &w_size, "group", name, perm_text, cnt); } else if(type == Aaip_ACL_MASK) { /* mask::rwx */ ret= aaip_write_acl_line(&wpt, &w_size, "mask", "", perm_text, cnt); } else if(type == Aaip_ACL_OTHER) { /* other::rwx */ ret= aaip_write_acl_line(&wpt, &w_size, "other", "", perm_text, cnt); } else if(type == Aaip_SWITCH_MARK) { /* Indicate to caller: end of desired ACL type access/default */ if((perm & Aaip_EXEC) ^ (!!(flag & 2))) {ret= 2; goto ex;} } else if(type == Aaip_ACL_USER_N) { /* determine username from uid */ uid= 0; for(i= 0; i < name_fill; i++) uid= (uid << 8) | ((unsigned char *) name)[i]; pwd= getpwuid(uid); if(pwd == NULL) sprintf(name, "%.f", (double) uid); else if(strlen(pwd->pw_name) >= (size_t) name_size) sprintf(name, "%.f", (double) uid); else strcpy(name, pwd->pw_name); /* user::rwx */; ret= aaip_write_acl_line(&wpt, &w_size, "user", name, perm_text, cnt); } else if(type == Aaip_ACL_GROUP_N) { /* determine username from gid */; gid= 0; for(i= 0; i < name_fill; i++) gid= (gid << 8) | ((unsigned char *) name)[i]; grp= getgrgid(gid); if(grp == NULL) sprintf(name, "%.f", (double) gid); else if(strlen(grp->gr_name) >= (size_t) name_size) sprintf(name, "%.f", (double) gid); else strcpy(name, grp->gr_name); /* user::rwx */; ret= aaip_write_acl_line(&wpt, &w_size, "group", name, perm_text, cnt); } else { /* indicate to caller: unknown type */ {ret = -4; goto ex;} } if(ret <= 0) {ret = -2; goto ex;} } ret= 1; ex:; *acl_text_fill= w_size; if(flag & 1) (*acl_text_fill)++; LIBISO_FREE_MEM(name); return(ret); } /* ----------------------- Adapter for operating systems ----------------- */ #ifdef Libisofs_use_os_dummY #include "aaip-os-dummy.c" #else #ifdef __FreeBSD__ #include "aaip-os-freebsd.c" #else #ifdef __FreeBSD_kernel__ #ifdef NIX #ifdef Libisofs_with_aaip_xattR /* ts B51213: xattr system library calls are only stubs */ #include "aaip-os-linux.c" #else /* ts B51213: extattr system library calls are not even present */ #include "aaip-os-freebsd.c" #endif /* ! Libisofs_with_aaip_xattR */ #else /* NIX */ /* ts B51213: so we still end up at the dummy */ #include "aaip-os-dummy.c" #endif /* ! NIX */ #else #ifdef __NetBSD__ #include "aaip-os-freebsd.c" #else #ifdef __OpenBSD__ #include "aaip-os-freebsd.c" #else #ifdef __linux #include "aaip-os-linux.c" /* August 2011: aaip-os-linux.c would also work for GNU/Hurd : ifdef __GNU__ Libraries and headers are present on Debian GNU/Hurd but there is no ACL or xattr support in the filesystems yet. Further, llistxattr() produces ENOSYS "Function not implemented". So it makes few sense to enable it here. */ #else #include "aaip-os-dummy.c" #endif /* ! __linux */ #endif /* ! __OpenBSD__ */ #endif /* ! __NetBSD__ */ #endif /* ! __FreeBSD_kernel__ */ #endif /* ! __FreeBSD__ */ #endif /* ! Libisofs_use_os_dummY */ /* Arbitrary Attribute Interchange Protocol , AAIP versions 0.2 and 1.0. Implementation for encoding and decoding xattr and ACL. See http://libburnia-project.org/wiki/AAIP or doc/susp_aaip_2_0.txt test/aaip_0_2.h - Public declarations Copyright (c) 2009 - 2024 Thomas Schmitt This file is part of the libisofs project; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later as published by the Free Software Foundation. See COPYING file for details. */ #ifndef Aaip_h_is_includeD #define Aaip_h_is_includeD yes /* For ssize_t */ #include /* --------------------------------- Encoder ---------------------------- */ /* Convert an array of Arbitrary Attributes into a series of AAIP fields. @param num_attrs Number of attributes @param names Array of pointers to 0 terminated name strings @param value_lengths Array of byte lengths for each value @param values Array of pointers to the value bytes @param result_len Number of bytes in the resulting SUSP field string @param result *result will point to the start of the result string. This is malloc() memory which needs to be freed when no longer needed @param flag Bitfield for control purposes bit0= set CONTINUE bit of last AAIP field to 1 @return >= 0 is the number of SUSP fields generated, < 0 means error */ ssize_t aaip_encode(size_t num_attrs, char **names, size_t *value_lengths, char **values, size_t *result_len, unsigned char **result, int flag); /* ------ ACL representation ------ */ /* Convert an ACL from long text form into the value of an Arbitrary Attribute. According to AAIP this value is to be stored together with an empty name. @param acl_text The ACL in long text form @param st_mode The stat(2) permission bits to be used with flag bit3 @param result_len Number of bytes in the resulting value @param result *result will point to the start of the result string. This is malloc() memory which needs to be freed when no longer needed @param flag Bitfield for control purposes bit0= count only bit1= use numeric qualifiers rather than names bit2= this is a default ACL, prepend SWITCH_MARK bit3= check for completeness of list and eventually fill up with entries deduced from st_mode @return >0 means ok <=0 means error -1= out of memory -2= program error with prediction of result size -3= error with conversion of name to uid or gid ISO_AAIP_ACL_MULT_OBJ= multiple entries of user::, group::, other:: */ int aaip_encode_acl(char *acl_text, mode_t st_mode, size_t *result_len, unsigned char **result, int flag); /* Convert an "access" and "default" ACL from long text form into the value of an Arbitrary Attribute. According to AAIP this value is to be stored together with an empty name. @param a_acl_text The "access" ACL in long text form. Submit NULL if there is no such ACL to be encoded. @param d_acl_text The "default" ACL in long text form. Submit NULL if there is no such ACL to be encoded. @param st_mode The stat(2) permission bits to be used with flag bit3 @param result_len Number of bytes in the resulting value @param result *result will point to the start of the result string. This is malloc() memory which needs to be freed when no longer needed @param flag Bitfield for control purposes bit0= count only bit1= use numeric qualifiers rather than names bit3= check for completeness of list and eventually fill up with entries deduced from st_mode @return >0 means ok <=0 means error, see aaip_encode_acl */ int aaip_encode_both_acl(char *a_acl_text, char *d_acl_text, mode_t st_mode, size_t *result_len, unsigned char **result, int flag); /* Analyze occurrence of ACL tag types in long text form. If not disabled by parameter flag remove the entries of type "user::" , "group::" , "other::" , or "other:" from an ACL in long text form if they match the bits in st_mode as described by man 2 stat and man 5 acl. @param acl_text The text to be analyzed and eventually shortened. @param st_mode The component of struct stat which tells permission bits and eventually shall take equivalent bits as read from the ACL. The caller should submit a pointer to the st_mode variable which holds permissions as indicated by stat(2) resp. ECMA-119 and RRIP data. @param flag bit0= do not remove entries, only determine return value bit1= like bit0 but return immediately if a non-st_mode ACL entry is found bit2= update *st_mode by acl_text ("user::" -> S_IRWXU, "mask::"|"group::" -> S_IRWXG, "other::" -> S_IRWXO) bit3= update acl_text by *st_mode (same mapping as bit 2 but with reversed transfer direction) bit4= map "group::" <-> S_IRWXG in any case. I.e. ignore "mask::". @return <0 failure >=0 tells in its bits which tag types were found. The first three tell which types deviate from the corresponding st_mode settings: bit0= "other::" overrides S_IRWXO bit1= "group::" overrides S_IRWXG (no "mask::" found) bit2= "user::" overrides S_IRWXU The second three tell which types comply with st_mode: bit3= "other::" matches S_IRWXO bit4= "group::" matches S_IRWXG (no "mask::" found) bit5= "user::" matches S_IRWXU Given the nature of ACLs nearly all combinations are possible although some would come from invalid ACLs. bit6= other ACL tag types are present. Particularly: bit7= "user:...:" is present bit8= "group:...:" is present bit9= "mask::" is present bit10= "group::" found and "mask::" exists */ int aaip_cleanout_st_mode(char *acl_text, mode_t *st_mode, int flag); /* Append entries of type "user::" , "group::" , "other::" representing the permission bits in st_mode if those tag types are not present in the ACL text. Append "mask::" if missing although "user:...:" or "group:...:" is present. Eventually set it to S_IRWXG bits of st_mode. @param acl_text The text to be made longer. It must offer 43 bytes more storage space than its length when it is submitted. @param st_mode The component of struct stat which shall provide the permission information. @param flag Unused yet. Submit 0. @return <0 failure */ int aaip_add_acl_st_mode(char *acl_text, mode_t st_mode, int flag); /* Encode a Linux style file attribute flag bits array to a byte string which represents the flags bits in isofs.fa . @param lfa_flags Bit array as obtained by aaip_get_lfa_flags() @param value Will be filled with 1 to 8 byte values @param length Will return the number of filled-in value bytes @return <0 failure */ int aaip_encode_lfa_flags(uint64_t lfa_flags, unsigned char value[8], int *length, int flag); /* ------ OS interface ------ */ /* See also API iso_local_attr_support(). @param flag Bitfield for control purposes bit0= inquire availability of ACL bit1= inquire availability of xattr bit2= inquire availability of Linux-like file attribute flags bit3 - bit7= Reserved for future types. It is permissibile to set them to 1 already now. bit8 and higher: reserved, submit 0 @return Bitfield corresponding to flag. If bits are set, th bit0= ACL adapter is enabled bit1= xattr adapter is enabled bit2= Linux-like file attribute flags adapter is enabled bit3 - bit7= Reserved for future types. It is permissibile to set them to 1 already now. bit8 and higher: reserved, do not interpret these */ int aaip_local_attr_support(int flag); /* Obtain the ACL of the given file in long text form. @param path Path to the file @param text Will hold the result. This is a managed object which finally has to be freed by a call to this function with bit15 of flag. @param flag Bitfield for control purposes bit0= obtain default ACL rather than access ACL bit4= set *text = NULL and return 2 if the ACL matches st_mode permissions. bit15= free text and return 1 @return 1 ok 2 only st_mode permissions exist and bit 4 is set 0 ACL support not enabled at compile time -1 failure of system ACL service (see errno) */ int aaip_get_acl_text(char *path, char **text, int flag); /* Obtain the Extended Attributes and/or the ACLs of the given file in a form that is ready for aaip_encode(). The returned data objects finally have to be freed by a call with flag bit 15. @param path Path to the file @param num_attrs Will return the number of name-value pairs @param names Will return an array of pointers to 0-terminated names @param value_lengths Will return an array with the lengths of values @param values Will return an array of pointers to 8-bit values @param flag Bitfield for control purposes bit0= obtain ACLs (access and eventually default) via system ACL API and encode bit1= use numeric ACL qualifiers rather than names bit2= do not obtain attributes other than ACLs bit3= do not ignore eventual non-user attributes. I.e. those with a name which does not begin by "user." bit4= do not return trivial ACL that matches st_mode bit5= in case of symbolic link: inquire link target bit6= do not obtain Linux style file attribute flags (chattr) bit15= free memory of names, value_lengths, values @return >0 ok <=0 error */ int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag); /* Obtain the file attribute flags of the given file as bit array in uint64_t. The bit numbers are compatible to the FS_*_FL definitions in Linux include file . A (possibly outdated) copy of them is in doc/susp_aaip_isofs_names.txt, name isofs.fa . The attribute flags of other systems may or may not be mappable to these flags. @param path Path to the file @param lfa_flags Will get filled with the FS_*_FL @param max_bit Will tell the highest bit that is possibly set (-1 = surely no bit is valid) @param os_errno Will get filled with errno if a system call fails @param flag Bitfield for control purposes. bit0= consider ENOTTY from FS_IOC_GETFLAGS an error (else return 4 on ENOTTY) bit2= do not issue own error messages with operating system errors bit7= Ignore non-settable flags @return 1= ok, all local attribute flags are in lfa_flags 2= ok, but some local flags could not be mapped to the FS_*_FL bits 4= ok, ENOTTY from FS_IOC_GETFLAGS 0= local flags retrieval not enabled at compile time <0 error with system calls: -1= error with open(2) -2= error with ioctl(2), not pardoned by bit0 */ int aaip_get_lfa_flags(char *path, uint64_t *lfa_flags, int *max_bit, int *os_errno, int flag); /* --------------------------------- Decoder ---------------------------- */ /* The AAIP decoder offers several levels of abstraction of which the lower two avoid the use of dynamic memory. It provides a stateful decoding context with a small buffer which delivers results to caller provided memory locations. The lowest level is the stream-like Component Level Interface. It allows to decode very many very long attributes. Next is the Pair Level Interface which delivers to fixly sized storage for name and value. It allows to decode very many attributes. The List Level Interface uses dynamic memory allocation to provide arrays of names, values and value lengths. It is intended for moderately sized attribute lists but may also be used as alternative to Pair Level. */ /* Operations on complete AAIP field strings which need no decoder context. These function expect to get submitted a complete chain of AAIP fields. */ /* Determine the size of the AAIP string by interpreting the SUSP structure. @param data An arbitrary number of bytes beginning with the complete chain of AAIP fields. Trailing trash is ignored. @param flag Unused yet. Submit 0. @return The number of bytes of the AAIP field chain. */ size_t aaip_count_bytes(unsigned char *data, int flag); /* The AAIP decoder context. */ struct aaip_state; /* Obtain the size in bytes of an aaip_state object. */ size_t aaip_sizeof_aaip_state(void); /* Initialize a AAIP decoder context. This has to be done before the first AAIP field of a node is processed. The caller has to provide the storage of the struct aaip_state. @param aaip The AAIP decoder context to be initialized @param flag Bitfield for control purposes submit 0 @return <=0 error , >0 ok */ int aaip_init_aaip_state(struct aaip_state *aaip, int flag); /* ------------------------- Component Level Interface ------------------- */ /* Provides support for unlimited component size but demands the caller to have a growing storage facility resp. to do own oversize handling. This interface expects moderatly sized input pieces and will hand out moderately sized result pieces. The number of transactions is virtually unlimited. */ /* Submit small data chunk for decoding. The return value will tell whether data are pending for being fetched. @param aaip The AAIP decoder context @param data Not more than 2048 bytes input for the decoder @param num_data Number of bytes in data 0 inquires the buffer status avoiding replies <= 0 @param ready_bytes Number of decoded bytes ready for delivery @param flag Bitfield for control purposes @return -1= non-AAIP field detected *ready_bytes gives number of consumed bytes in data 0= cannot accept data because buffer full 1= no component record complete, submit more data 2= component record complete, may be delivered 3= component complete, may be delivered 4= no component available, no more data expected, done */ int aaip_submit_data(struct aaip_state *aaip, unsigned char *data, size_t num_data, size_t *ready_bytes, int flag); /* Fetch the available part of current component. The return value will tell whether it belongs to name or to value and whether that name or value is completed now. @param aaip The AAIP decoder context @param result Has to point to storage for the component data @param result_size Gives the amount of provided result storage @param num_result Will tell the number of fetched result bytes @param flag Bitfield for control purposes bit0= discard data rather than copying to result @return -2 = insufficient result_size -1 = no data ready for delivery 0 = result holds the final part of a name 1 = result holds an intermediate part of a name 2 = result holds the final part of a value 3 = result holds an intermediate part of a value */ int aaip_fetch_data(struct aaip_state *aaip, char *result, size_t result_size, size_t *num_result, int flag); /* Skip the current component and eventually the following value component. This has to be called if fetching of a component shall be aborted but the next component resp. pair shall be fetchable again. aaip_submit_data() will not indicate readiness for fetching until all bytes of the skipped components are submitted. Those bytes get discarded. @param aaip The AAIP decoder context @param flag Bitfield for control purposes bit0= do not skip value if current component is name @return <=0 error , 1= now in skip state, 2= not in skip state */ int aaip_skip_component(struct aaip_state *aaip, int flag); /* ------------------------- Pair Level Interface ------------------------ */ /* Provides support for names and values of limited size. The limits are given by the caller who has to provide the storage for name and value. This interface expects moderatly sized input pieces. The number of input transcations is virtually unlimited. The number of pair transactions after aaip_init() should be limited to 4 billion. */ /* Accept raw input data and collect a pair of name and value. The return value will indicate whether the pair is complete, whether more pairs are complete or whether more data are desired. No input data will be accepted as long as complete pairs are pending. The end of the attribute list will be indicated. @param aaip The AAIP decoder context @param data The raw data to decode @param num_data Number of data bytes provided @param consumed Returns the number of consumed data bytes @param name Buffer to build the name string @param name_size Maximum number of bytes in name @param name_fill Holds the current buffer fill of name @param value Buffer to build the value string @param value_size Maximum number of bytes in value @param value_fill Holds the current buffer fill of value @param flag Bitfield for control purposes - submit 0 for now @return <0 error 0 data not accepted, first fetch pending pairs with num_data == 0 1 name and value are not valid yet, submit more data 2 name and value are valid, submit more data 3 name and value are valid, pairs pending, fetch with num_data == 0 4 name and value are valid, no more data expected 5 name and value are not valid, no more data expected */ int aaip_decode_pair(struct aaip_state *aaip, unsigned char *data, size_t num_data, size_t *consumed, char *name, size_t name_size, size_t *name_fill, char *value, size_t value_size, size_t *value_fill, int flag); /* Inquire the number of pairs which were skipped because being oversized. @param aaip The AAIP decoder context @param flag Bitfield for control purposes - submit 0 for now @return The number of pairs skipped since aaip_init() */ unsigned int aaip_get_pairs_skipped(struct aaip_state *aaip, int flag); /* ------------------------- List Level Interface ------------------------ */ /* Provides support for names and values of limited size. The limits are given for total memory consumption and for number of attributes. Iterated decoding is supported as long as no single attribute exceeds the memory limit. */ /* Accept raw input data and collect arrays of name pointers, value lengths and value pointers. A handle object will emerge which finally has to be be freed by a call with bit 15. @param handle The decoding context. It will be created by this call with flag bit 0 or if *handle == NULL. This handle has to be the same as long as decoding goes on and finally has to be freed by a call with bit15. @param memory_limit Maximum number of bytes to allocate @param num_attr_limit Maximum number of name-value pairs to allocate @param data The raw data to decode @param num_data Number of data bytes provided @param consumed Returns the number of consumed data bytes @param flag Bitfield for control purposes bit0= this is the first call for a file object bit15= end decoding : Free handle and its intermediate list memory. @return <=0 error 1 not complete yet, submit more data 2 arrays are complete, call aaip_get_decoded_attrs() 3 limit exceeded, not complete yet, call with bit15 and give up 4 limit exceeded, call aaip_get_decoded_attrs() and try again */ int aaip_decode_attrs(struct aaip_state **handle, size_t memory_limit, size_t num_attr_limit, unsigned char *data, size_t num_data, size_t *consumed, int flag); /* Obtain the resulting attributes when aaip_decode_attrs() indicates to be done or to have the maximum possible amount of result ready. The returned data objects get detached from handle making it ready for the next round of decoding with possibly a different input source. The returned data objects finally have to be freed by a call with flag bit 15. @param handle The decoding context created by aaip_decode_attrs() @param num_attrs Will return the number of name-value pairs @param names Will return an array of pointers to 0-terminated names @param value_lengths Will return an array with the lengths of values @param values Will return an array of pointers to 8-bit values @param flag Bitfield for control purposes bit15= free memory of names, value_lengths, values */ int aaip_get_decoded_attrs(struct aaip_state **handle, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag); /* ------ ACL representation ------ */ /* Convert an AAIP ACL attribute value into the long text form of ACL. @param data The raw data to decode @param num_data Number of data bytes provided @param consumed Returns the number of consumed data bytes @param acl_text Will be filled with ACL long text form @param acl_text_size Maximum number of bytes to be written to acl_text @param acl_text_fill Will return the number of bytes in acl_text @param flag Bitfield for control purposes bit0= count only, do not really produce bytes: acl_text will not be touched, acl_text_size will be ignored, *acl_text_fill will return the counted number bit1= expected is a default ACL (see return value 2) @return 1 success 2 success, begin of default/access ACL encountered, submit data + *consumed for access/default ACL -1 error with reading of qualifier -2 error with writing of ACL text line -3 version mismatch -4 unknown tag type encountered */ int aaip_decode_acl(unsigned char *data, size_t num_data, size_t *consumed, char *acl_text, size_t acl_text_size, size_t *acl_text_fill, int flag); /* ------ OS interface ------ */ /* Set the ACL of the given file to a given list in long text form. @param path Path to the file @param text The input text (0 terminated, ACL long text form) @param flag Bitfield for control purposes bit0= set default ACL rather than access ACL @return >0 ok 0 ACL support not enabled at compile time -1 failure of system ACL service (see errno) */ int aaip_set_acl_text(char *path, char *text, int flag); /* Bring the given attributes and/or ACLs into effect with the given file. @param path Path to the file @param num_attrs Number of attributes @param names Array of pointers to 0 terminated name strings @param value_lengths Array of byte lengths for each attribute payload @param values Array of pointers to the attribute payload bytes @param flag Bitfield for control purposes bit0= decode and set ACLs bit1= first clear all existing attributes of the file bit2= do not set attributes other than ACLs bit3= do not ignore eventual non-user attributes. I.e. those with a name which does not begin by "user." @return 1 success -1 error memory allocation -2 error with decoding of ACL -3 error with setting ACL -4 error with setting attribute -5 error with deleting attributes -6 support of xattr not enabled at compile time -7 support of ACL not enabled at compile time -8 unsupported xattr namespace ISO_AAIP_ACL_MULT_OBJ multiple entries of user::, group::, other:: */ int aaip_set_attr_list(char *path, size_t num_attrs, char **names, size_t *value_lengths, char **values, int *errnos, int flag); /* Bring the given file attribute flags into effect with the given file. The bit numbers are compatible to the FS_*_FL definitions in Linux include file . A (possibly outdated) copy of them is in doc/susp_aaip_isofs_names.txt, name isofs.fa . The attribute flags of other systems may or may not be mappable to these flags. @param path Path to the file @param lfa_flags File attribute flag bits @param max_bit Gives an upper limit of the highest set flag bit. (-1 = surely no bit is valid) On Linux this must be smaller than sizeof(long) * 8. @param os_errno Will get filled with errno if a system call fails @param flag Bitfield for control purposes: bit2= do not issue own error messages with operating system errors @return 1= ok, all lfa_flags bits were written 2= ok, but some FS_*_FL bits could not be mapped to local flags 0= local flags setting not enabled at compile time <0 error with system calls or with max_bit: -1= error with open(2) -2= error with ioctl(2) -3= error with max_bit */ int aaip_set_lfa_flags(char *path, uint64_t lfa_flags, int max_bit, int *os_errno, int flag); #endif /* ! Aaip_h_is_includeD */ /* * Copyright (c) 2007 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /* * Synchronized ring buffer, works with a writer thread and a read thread. * * TODO #00010 : optimize ring buffer * - write/read at the end of buffer requires a second mutex_lock, even if * there's enough space/data at the beginning * - pre-buffer for writes < BLOCK_SIZE * */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /* Use the copy of the struct burn_source definition in libisofs.h */ #define LIBISOFS_WITHOUT_LIBBURN yes #include "libisofs.h" #include "buffer.h" #include "ecma119.h" #include #include #ifndef MIN # define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif struct iso_ring_buffer { uint8_t *buf; /* * Max number of bytes in buffer */ size_t cap; /* * Number of bytes available. */ size_t size; /* position for reading and writing, offset from buf */ size_t rpos; size_t wpos; /* * flags to report if read or writer threads ends execution * 0 not finished, 1 finished ok, 2 finish with error */ unsigned int rend :2; unsigned int wend :2; /* just for statistical purposes */ unsigned int times_full; unsigned int times_empty; pthread_mutex_t mutex; pthread_cond_t empty; pthread_cond_t full; }; /** * Create a new buffer. * * The created buffer should be freed with iso_ring_buffer_free() * * @param size * Number of blocks in buffer. You should supply a number >= 32, otherwise * size will be ignored and 32 will be used by default, which leads to a * 64 KiB buffer. * @return * 1 success, < 0 error */ int iso_ring_buffer_new(size_t size, IsoRingBuffer **rbuf) { IsoRingBuffer *buffer; if (rbuf == NULL) { return ISO_NULL_POINTER; } buffer = malloc(sizeof(IsoRingBuffer)); if (buffer == NULL) { return ISO_OUT_OF_MEM; } buffer->cap = (size > 32 ? size : 32) * BLOCK_SIZE; buffer->buf = malloc(buffer->cap); if (buffer->buf == NULL) { free(buffer); return ISO_OUT_OF_MEM; } buffer->size = 0; buffer->wpos = 0; buffer->rpos = 0; buffer->times_full = 0; buffer->times_empty = 0; buffer->rend = buffer->wend = 0; /* init mutex and waiting queues */ pthread_mutex_init(&buffer->mutex, NULL); pthread_cond_init(&buffer->empty, NULL); pthread_cond_init(&buffer->full, NULL); *rbuf = buffer; return ISO_SUCCESS; } void iso_ring_buffer_free(IsoRingBuffer *buf) { if (buf == NULL) { return; } free(buf->buf); pthread_mutex_destroy(&buf->mutex); pthread_cond_destroy(&buf->empty); pthread_cond_destroy(&buf->full); free(buf); } /** * Write count bytes into buffer. It blocks until all bytes where written or * reader close the buffer. * * @param buf * the buffer * @param data * pointer to a memory region of at least coun bytes, from which data * will be read. * @param * Number of bytes to write * @return * 1 success, 0 read finished, < 0 error */ int iso_ring_buffer_write(IsoRingBuffer *buf, uint8_t *data, size_t count) { size_t len; size_t bytes_write = 0; if (buf == NULL || data == NULL) { return ISO_NULL_POINTER; } while (bytes_write < count) { pthread_mutex_lock(&buf->mutex); while (buf->size == buf->cap) { /* * Note. There's only a writer, so we have no race conditions. * Thus, the while(buf->size == buf->cap) is used here * only to properly detect the reader has been cancelled */ if (buf->rend) { /* the read procces has been finished */ pthread_mutex_unlock(&buf->mutex); return 0; } buf->times_full++; /* wait until space available */ pthread_cond_wait(&buf->full, &buf->mutex); } len = MIN(count - bytes_write, buf->cap - buf->size); if (buf->wpos + len > buf->cap) { len = buf->cap - buf->wpos; } memcpy(buf->buf + buf->wpos, data + bytes_write, len); buf->wpos = (buf->wpos + len) % (buf->cap); bytes_write += len; buf->size += len; /* wake up reader */ pthread_cond_signal(&buf->empty); pthread_mutex_unlock(&buf->mutex); } return ISO_SUCCESS; } /** * Read count bytes from the buffer into dest. It blocks until the desired * bytes has been read. If the writer finishes before outputting enough * bytes, 0 (EOF) is returned, the number of bytes already read remains * unknown. * * @return * 1 success, 0 EOF, < 0 error */ int iso_ring_buffer_read(IsoRingBuffer *buf, uint8_t *dest, size_t count) { size_t len; size_t bytes_read = 0; if (buf == NULL || dest == NULL) { return ISO_NULL_POINTER; } while (bytes_read < count) { pthread_mutex_lock(&buf->mutex); while (buf->size == 0) { /* * Note. There's only a reader, so we have no race conditions. * Thus, the while(buf->size == 0) is used here just to ensure * a reader detects the EOF properly if the writer has been * canceled while the reader was waiting */ if (buf->wend) { /* the writer procces has been finished */ pthread_mutex_unlock(&buf->mutex); return 0; /* EOF */ } buf->times_empty++; /* wait until data available */ pthread_cond_wait(&buf->empty, &buf->mutex); } len = MIN(count - bytes_read, buf->size); if (buf->rpos + len > buf->cap) { len = buf->cap - buf->rpos; } memcpy(dest + bytes_read, buf->buf + buf->rpos, len); buf->rpos = (buf->rpos + len) % (buf->cap); bytes_read += len; buf->size -= len; /* wake up the writer */ pthread_cond_signal(&buf->full); pthread_mutex_unlock(&buf->mutex); } return ISO_SUCCESS; } void iso_ring_buffer_writer_close(IsoRingBuffer *buf, int error) { pthread_mutex_lock(&buf->mutex); buf->wend = error ? 2 : 1; /* ensure no reader is waiting */ pthread_cond_signal(&buf->empty); pthread_mutex_unlock(&buf->mutex); } void iso_ring_buffer_reader_close(IsoRingBuffer *buf, int error) { pthread_mutex_lock(&buf->mutex); if (buf->rend) { /* reader already closed */ pthread_mutex_unlock(&buf->mutex); return; } buf->rend = error ? 2 : 1; /* ensure no writer is waiting */ pthread_cond_signal(&buf->full); pthread_mutex_unlock(&buf->mutex); } /** * Get the times the buffer was full. */ unsigned int iso_ring_buffer_get_times_full(IsoRingBuffer *buf) { return buf->times_full; } /** * Get the times the buffer was empty. */ unsigned int iso_ring_buffer_get_times_empty(IsoRingBuffer *buf) { return buf->times_empty; } /** Internal via buffer.h * * Get the status of a ring buffer. * * @param buf * The ring buffer object to inquire * @param size * Will be filled with the total size of the buffer, in bytes * @param free_bytes * Will be filled with the bytes currently available in buffer * @return * < 0 error, > 0 state: * 1="active" : input and consumption are active * 2="ending" : input has ended without error * 3="failing" : input had error and ended, * 5="abandoned" : consumption has ended prematurely * 6="ended" : consumption has ended without input error * 7="aborted" : consumption has ended after input error */ int iso_ring_buffer_get_buf_status(IsoRingBuffer *buf, size_t *size, size_t *free_bytes) { int ret; if (buf == NULL) { return ISO_NULL_POINTER; } /* get mutex */ pthread_mutex_lock(&buf->mutex); if (size) { *size = buf->cap; } if (free_bytes) { *free_bytes = buf->cap - buf->size; } ret = (buf->rend ? 4 : 0) + (buf->wend + 1); pthread_mutex_unlock(&buf->mutex); return ret; } /** API via libisofs.h * * Get the status of the buffer used by a burn_source. * * @param b * A burn_source previously obtained with * iso_image_create_burn_source(). * @param size * Will be filled with the total size of the buffer, in bytes * @param free_bytes * Will be filled with the bytes currently available in buffer * @return * < 0 error, > 0 state: * 1="active" : input and consumption are active * 2="ending" : input has ended without error * 3="failing" : input had error and ended, * 5="abandoned" : consumption has ended prematurely * 6="ended" : consumption has ended without input error * 7="aborted" : consumption has ended after input error */ int iso_ring_buffer_get_status(struct burn_source *b, size_t *size, size_t *free_bytes) { int ret; IsoRingBuffer *buf; if (b == NULL) { return ISO_NULL_POINTER; } buf = ((Ecma119Image*)(b->data))->buffer; ret = iso_ring_buffer_get_buf_status(buf, size, free_bytes); return ret; } /* * Copyright (c) 2007 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_BUFFER_H_ #define LIBISO_BUFFER_H_ #include #ifdef HAVE_STDINT_H #include #else #ifdef HAVE_INTTYPES_H #include #endif #endif #define BLOCK_SIZE 2048 typedef struct iso_ring_buffer IsoRingBuffer; /** * Create a new buffer. * * The created buffer should be freed with iso_ring_buffer_free() * * @param size * Number of blocks in buffer. You should supply a number >= 32, otherwise * size will be ignored and 32 will be used by default, which leads to a * 64 KiB buffer. * @return * 1 success, < 0 error */ int iso_ring_buffer_new(size_t size, IsoRingBuffer **rbuf); /** * Free a given buffer */ void iso_ring_buffer_free(IsoRingBuffer *buf); /** * Write count bytes into buffer. It blocks until all bytes where written or * reader close the buffer. * * @param buf * the buffer * @param data * pointer to a memory region of at least coun bytes, from which data * will be read. * @param * Number of bytes to write * @return * 1 success, 0 read finished, < 0 error */ int iso_ring_buffer_write(IsoRingBuffer *buf, uint8_t *data, size_t count); /** * Read count bytes from the buffer into dest. It blocks until the desired * bytes has been read. If the writer finishes before outputting enough * bytes, 0 (EOF) is returned, the number of bytes already read remains * unknown. * * @return * 1 success, 0 EOF, < 0 error */ int iso_ring_buffer_read(IsoRingBuffer *buf, uint8_t *dest, size_t count); /** Backend of API call iso_ring_buffer_get_status() * * Get the status of a ring buffer. * * @param buf * The ring buffer object to inquire * @param size * Will be filled with the total size of the buffer, in bytes * @param free_bytes * Will be filled with the bytes currently available in buffer * @return * < 0 error, > 0 state: * 1="active" : input and consumption are active * 2="ending" : input has ended without error * 3="failing" : input had error and ended, * 5="abandoned" : consumption has ended prematurely * 6="ended" : consumption has ended without input error * 7="aborted" : consumption has ended after input error */ int iso_ring_buffer_get_buf_status(IsoRingBuffer *buf, size_t *size, size_t *free_bytes); /** * Close the buffer (to be called by the writer). * You have to explicitly close the buffer when you don't have more data to * write, otherwise reader will be waiting forever. * * @param error * Writer has finished prematurely due to an error */ void iso_ring_buffer_writer_close(IsoRingBuffer *buf, int error); /** * Close the buffer (to be called by the reader). * If for any reason you don't want to read more data, you need to call this * to let the writer thread finish. * * @param error * Reader has finished prematurely due to an error */ void iso_ring_buffer_reader_close(IsoRingBuffer *buf, int error); /** * Get the times the buffer was full. */ unsigned int iso_ring_buffer_get_times_full(IsoRingBuffer *buf); /** * Get the times the buffer was empty. */ unsigned int iso_ring_buffer_get_times_empty(IsoRingBuffer *buf); #endif /*LIBISO_BUFFER_H_*/ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /* libisofs.h defines aaip_xinfo_func */ #include "libisofs.h" #include "builder.h" #include "node.h" #include "fsource.h" #include "image.h" #include "aaip_0_2.h" #include "util.h" #include "messages.h" #include #include #include #include void iso_node_builder_ref(IsoNodeBuilder *builder) { ++builder->refcount; } void iso_node_builder_unref(IsoNodeBuilder *builder) { if (--builder->refcount == 0) { /* free private data */ builder->free(builder); free(builder); } } static int default_create_file(IsoNodeBuilder *builder, IsoImage *image, IsoFileSource *src, IsoFile **file) { int ret; struct stat info; IsoStream *stream; IsoFile *node; char *name; if (builder == NULL || src == NULL || file == NULL) { return ISO_NULL_POINTER; } ret = iso_file_source_stat(src, &info); if (ret < 0) { return ret; } /* this will fail if src is a dir, is not accessible... */ ret = iso_file_source_stream_new(src, &stream); if (ret < 0) { return ret; } /* take a ref to the src, as stream has taken our ref */ iso_file_source_ref(src); name = iso_file_source_get_name(src); if ((int) strlen(name) > image->truncate_length) { ret = iso_truncate_rr_name(image->truncate_mode, image->truncate_length, name, 0); if (ret < 0) { iso_stream_unref(stream); free(name); return ret; } } ret = iso_node_new_file(name, stream, &node); if (ret < 0) { iso_stream_unref(stream); free(name); return ret; } /* fill node fields */ iso_node_set_permissions((IsoNode*)node, info.st_mode); iso_node_set_uid((IsoNode*)node, info.st_uid); iso_node_set_gid((IsoNode*)node, info.st_gid); iso_node_set_atime((IsoNode*)node, info.st_atime); iso_node_set_mtime((IsoNode*)node, info.st_mtime); iso_node_set_ctime((IsoNode*)node, info.st_ctime); iso_node_set_uid((IsoNode*)node, info.st_uid); *file = node; return ISO_SUCCESS; } static int default_create_node(IsoNodeBuilder *builder, IsoImage *image, IsoFileSource *src, char *in_name, IsoNode **node) { int ret, name_is_attached = 0; struct stat info; IsoNode *new; IsoFilesystem *fs; char *name = NULL; unsigned char *aa_string = NULL; char *a_text = NULL, *d_text = NULL; char *dest = NULL; IsoSymlink *link; if (builder == NULL || src == NULL || node == NULL) { {ret = ISO_NULL_POINTER; goto ex;} } /* get info about source */ if (iso_tree_get_follow_symlinks(image)) { ret = iso_file_source_stat(src, &info); } else { ret = iso_file_source_lstat(src, &info); } if (ret < 0) { goto ex; } if (in_name == NULL) { name = iso_file_source_get_name(src); } else { name = strdup(in_name); if (name == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } } if ((int) strlen(name) > image->truncate_length) { ret = iso_truncate_rr_name(image->truncate_mode, image->truncate_length, name, 0); if (ret < 0) goto ex; } fs = iso_file_source_get_filesystem(src); new = NULL; switch (info.st_mode & S_IFMT) { case S_IFREG: { /* source is a regular file */ IsoStream *stream; IsoFile *file; ret = iso_file_source_stream_new(src, &stream); if (ret < 0) { break; } /* take a ref to the src, as stream has taken our ref */ iso_file_source_ref(src); /* create the file */ ret = iso_node_new_file(name, stream, &file); if (ret < 0) { iso_stream_unref(stream); } new = (IsoNode*) file; } break; case S_IFDIR: { /* source is a directory */ IsoDir *dir; ret = iso_node_new_dir(name, &dir); new = (IsoNode*)dir; } break; case S_IFLNK: { /* source is a symbolic link */ LIBISO_ALLOC_MEM(dest, char, LIBISOFS_NODE_PATH_MAX); ret = iso_file_source_readlink(src, dest, LIBISOFS_NODE_PATH_MAX); if (ret < 0) { break; } ret = iso_node_new_symlink(name, strdup(dest), &link); new = (IsoNode*) link; if (fs != NULL) { link->fs_id = fs->get_id(fs); if (link->fs_id != 0) { link->st_ino = info.st_ino; link->st_dev = info.st_dev; } } } break; case S_IFSOCK: case S_IFBLK: case S_IFCHR: case S_IFIFO: { /* source is an special file */ IsoSpecial *special; ret = iso_node_new_special(name, info.st_mode, info.st_rdev, &special); new = (IsoNode*) special; if (fs != NULL) { special->fs_id = fs->get_id(fs); if (special->fs_id != 0) { special->st_ino = info.st_ino; special->st_dev = info.st_dev; } } } break; default: ret = ISO_BAD_FSRC_FILETYPE; goto ex; } if (ret < 0) goto ex; name_is_attached = 1; /* fill fields */ iso_node_set_perms_internal(new, info.st_mode, 1); iso_node_set_uid(new, info.st_uid); iso_node_set_gid(new, info.st_gid); iso_node_set_atime(new, info.st_atime); iso_node_set_mtime(new, info.st_mtime); iso_node_set_ctime(new, info.st_ctime); iso_node_set_uid(new, info.st_uid); /* Eventually set S_IRWXG from ACL */ if (image->builder_ignore_acl) { ret = iso_file_source_get_aa_string(src, &aa_string, 4); if (ret >= 0 && aa_string != NULL) iso_aa_get_acl_text(aa_string, info.st_mode, &a_text, &d_text, 16); if (ret >= 0 && a_text != NULL) { aaip_cleanout_st_mode(a_text, &(info.st_mode), 4 | 16); iso_node_set_perms_internal(new, info.st_mode, 1); } iso_aa_get_acl_text(aa_string, info.st_mode, &a_text, &d_text, 1 << 15); /* free ACL texts */ if(aa_string != NULL) free(aa_string); aa_string = NULL; } /* Obtain ownership of eventual AAIP string */ ret = iso_file_source_get_aa_string(src, &aa_string, 1 | (image->builder_ignore_acl << 1) | (image->builder_ignore_ea << 2) | (image->builder_take_all_ea << 3) | ((!image->builder_ignore_lfa_flags) << 4) | (image->builder_ignore_ro_lfa_flags << 5) ); if(ret == 2) image->blind_on_local_get_attrs = 1; if (ret > 0 && aa_string != NULL) { ret = iso_node_add_xinfo(new, aaip_xinfo_func, aa_string); if (ret < 0) goto ex; } else if(aa_string != NULL) { free(aa_string); } *node = new; ret = ISO_SUCCESS; ex:; if (name != NULL && !name_is_attached) free(name); LIBISO_FREE_MEM(dest); return ret; } static void default_free(IsoNodeBuilder *builder) { /* The .free() method of IsoNodeBuilder shall free private data but not the builder itself. The latter is done in iso_node_builder_unref(). */ return; } int iso_node_basic_builder_new(IsoNodeBuilder **builder) { IsoNodeBuilder *b; if (builder == NULL) { return ISO_NULL_POINTER; } b = malloc(sizeof(IsoNodeBuilder)); if (b == NULL) { return ISO_OUT_OF_MEM; } b->refcount = 1; b->create_file_data = NULL; b->create_node_data = NULL; b->create_file = default_create_file; b->create_node = default_create_node; b->free = default_free; *builder = b; return ISO_SUCCESS; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2014 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_BUILDER_H_ #define LIBISO_BUILDER_H_ /* * Definitions for IsoNode builders. */ /* * Some functions here will be moved to libisofs.h when we expose * Builder. */ #include "libisofs.h" #include "fsource.h" typedef struct Iso_Node_Builder IsoNodeBuilder; struct Iso_Node_Builder { /** * Create a new IsoFile from an IsoFileSource. Name, permissions * and other attributes are taken from src, but a regular file will * always be created, even if src is another kind of file. * * In that case, if the implementation can't do the conversion, it * should fail properly. * * Note that the src is never unref, so you need to free it. * * @return * 1 on success, < 0 on error */ int (*create_file)(IsoNodeBuilder *builder, IsoImage *image, IsoFileSource *src, IsoFile **file); /** * Create a new IsoNode from a IsoFileSource. The type of the node to be * created is determined from the type of the file source. Name, * permissions and other attributes are taken from source file. * But name may be overridden by parameter name if it is not NULL. * * Note that the src is never unref, so you need to free it. * * @return * 1 on success, < 0 on error */ int (*create_node)(IsoNodeBuilder *builder, IsoImage *image, IsoFileSource *src, char *name, IsoNode **node); /** * Free implementation specific data. Should never be called by user. * Use iso_node_builder_unref() instead. */ void (*free)(IsoNodeBuilder *builder); int refcount; void *create_file_data; void *create_node_data; }; void iso_node_builder_ref(IsoNodeBuilder *builder); void iso_node_builder_unref(IsoNodeBuilder *builder); /** * Create a new basic builder ... * * @return * 1 success, < 0 error */ int iso_node_basic_builder_new(IsoNodeBuilder **builder); #endif /*LIBISO_BUILDER_H_*/ ------------------------------------------------------------------------------ libburnia-project.org / dev.lovelyhq.com libisofs ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ Changelog ------------------------------------------------------------------------------ 13 Nov 2016 [8bf32d8] [1340] doc/susp_aaip_isofs_names.txt Prepared some of the documentation for move to dev.lovelyhq.com 13 Nov 2016 [c51efce] [1341] libisofs/fs_image.c Bug fix: iso_read_opts_set_no_rockridge() did not prevent reading of root SUSP 13 Nov 2016 [dc6cd94] [1342] libisofs/libisofs.h libisofs/eltorito.c libisofs/libisofs.ver New API calls el_torito_set_full_load(), el_torito_get_full_load() 13 Nov 2016 [ed209e0] [1343] COPYRIGHT Mentioned Vladimir Serbinenko in libisofs copyright list 13 Nov 2016 [2961bde] [1344] ChangeLog + libisofs/changelog.txt Updated change log 23 Nov 2016 [01020ef] [1345] libisofs/eltorito.h Committed missing part of rev dc6cd94/1342 23 Nov 2016 [8ec75ee] [1346] libisofs/fs_image.c libisofs/rockridge.h libisofs/rockridge_read.c Bug fix: Non-SUSP data in System Use Area prevented image loading if Rock Ridge was enabled. Thanks to Jonathan Dowland. 25 Dec 2016 [76181d0] [1347] libisofs/ecma119.c Restricted volume size of PVD with non-zero partition offset to filesystem size even if the first PVD claims the whole output size 25 Dec 2016 [afb10aa] [1348] libisofs/eltorito.c Claiming full output size in first PVD if partition offset is non-zero 25 Dec 2016 [3043b5f] [1349] libisofs/fs_image.c Enabled recognition of partition offset of grub-mkrescue-sed.sh mode "gpt_appended" 03 Jan 2017 [2152804] [1350] libisofs/system_area.c Bug fix: Protective MBR for GPT could emerge with boot flag set. 24 Jan 2014 [5c1c5cd] [1351] libisofs/system_area.c Bug fix: Appended partitions of size >= 4 GiB led to abort with error message "FATAL : ISO overwrite". Thanks to Sven Haardiek. 24 Jan 2014 [094b3f7] [1352] libisofs/system_area.c Updated copyright year in system_area.c 27 Feb 2014 [e66b9bf] [1353] libisofs/libisofs.h libisofs/ecma119.h libisofs/ecma119.c libisofs/system_area.c libisofs/libisofs.ver New API call iso_write_opts_set_iso_mbr_part_type() 27 Feb 2014 [5600f3d] [1354] libisofs/system_area.c When deciding boot flag, consider MBR partition slot empty only if entirely 0 19 Mar 2017 [86f6ffc] [1355] libisofs/system_area.c Let iso_mbr_part_type 0xee override ban on 0xee without GPT 09 Apr 2017 [94e4bfb] [1356] libisofs/fs_image.c Adapted recognizing of partition offset to the changes of rev afb10aa / 1348 10 Apr 2017 [0e7300b] [1357] doc/boot_sectors.txt Clarified meaning of MBR partition boot flag 25 Apr 2017 [fb86970] [1358] libisofs/system_area.c Reacted on harmless compiler warning about uninitialized variable. 03 Jun 2017 [d7737e3] [] libisofs/fs_image.c Removed ban on reading El Torito platform ids other than 0 and 0xef 29 Jun 2017 [6282bbc] libisofs/system_area.c Bug fix: Bit 15 of iso_write_opts_set_system_area did not work with generic MBR 01 Jul 2017 [18ab601] libisofs/eltorito.c libisofs/system_area.c Let ISO size cover appended partitions if --protective-msdos-label or nonzero -partition_offset is given 13 Aug 2017 [c6e4035] doc/boot_sectors.txt Added boot sector knowledge gained from Natalia Portillo 13 Aug 2017 [e19a338] doc/boot_sectors.txt Re-added two empty lines which were lost by previous commit 14 Aug 2014 [1e40ed3] libisofs/fs_image.c Bug fix: Keeping and patching of loaded boot images failed. Regression by version 1.4.4. 14 Aug 2017 [280108d] ChangeLog libisofs/changelog.txt Updated change log 17 Aug 2017 [860a91d] libisofs/libisofs.h libisofs/fs_image.c libisofs/messages.c Preventing NULL dereference if root directory bears a RRIP RE entry 18 Aug 2017 [36c8800] libisofs/rockridge_read.c Preventing buffer underread with empty RRIP SL component. Debian bug 872475. Thanks Jakub Wilk and American Fuzzy Lop. 18 Aug 2017 [16bde11] libisofs/fs_image.c Preventing memory leak caused by RRIP SL entry without PX entry that marks the file as symbolic link 18 Aug 2017 [661b68c] libisofs/rockridge_read.c Preventing buffer overflow with AAIP AL entry of insufficient size. Debian bug 872545. Thanks Jakub Wilk and American Fuzzy Lop. 19 Aug 2017 [91490d5] libisofs/libisofs.h libisofs/messages.c libisofs/rockridge_read.c Preventing use of zero sized SUSP CE entry which causes SIGSEGV. Debian bug 872590. Thanks Jakub Wilk and American Fuzzy Lop. 19 Aug 2017 [31088d9] libisofs/rockridge_read.c Avoid to read blocks from start of CE area which do not belong to the given file 19 Aug 2017 [2a64d89] libisofs/libisofs.h libisofs/rockridge.h libisofs/fs_image.c libisofs/rockridge_read.c libisofs/messages.c Refuse to read CE data blocks from after the end of ISO filesystem 21 Aug 2017 [a7152f5] libisofs/fs_image.c Correcting previous commit for supporting multi-session 21 Aug 2017 [78b0a7b] libisofs/fs_image.c Disallowed RRIP CL chaining in order to break any endless loops. Debian bug 872761. Thanks Jakub Wilk and American Fuzzy Lop. 21 Aug 2017 [e599a57] libisofs/fs_image.c Closed a memory leak about RRIP CL following 24 Aug 2017 [cace41e] libisofs/ecma119.h libisofs/ecma119.c libisofs/system_area.c Enabled partition intervals with source "imported_iso" with ISO growing 26 Aug 2017 [028f927] libisofs/libisofs.h libisofs/ecma119.c libisofs/system_area.c libisofs/messages.c Throw error if imported_iso interval would be overwritten by multi-session 09 Sep 2017 [4e5a54c] libisofs/fs_image.c Swapped at recognition time the precendence of MBR properties "isohybrid" and "protective-msdos-label" 12 Sep 2017 [7234425] ChangeLog Updated change log 12 Sep 2017 [dfc6de9] configure.ac libisofs/libisofs.h Version leap to 1.4.8. 12 Sep 2017 [bdfd4c4] libisofs/changelog.txt Updated change log ------------------------------------ release - libisofs-1.4.8 - 12 Sep 2017 * Bug fix: iso_read_opts_set_no_rockridge() did not prevent reading of root SUSP * Bug fix: Non-SUSP data in System Use Area prevented image loading if Rock Ridge was enabled. Thanks to Jonathan Dowland. * Bug fix: Protective MBR for GPT could emerge with boot flag set. * Bug fix: Appended partitions of size >= 4 GiB led to abort with error message "FATAL : ISO overwrite". Thanks to Sven Haardiek. * Bug fix: Bit 15 of iso_write_opts_set_system_area did not work with generic MBR. * Bug fix: Keeping and patching of loaded boot images failed. Regression by version 1.4.4. * Bug fix: Program crashes by intentionally wrong ISO image input. Found by American Fuzzy Lop and Jakub Wilk. Debian bug reports: 872372, 872475, 872545, 872590, 872761. * New API calls el_torito_set_full_load(), el_torito_get_full_load(). * New API call iso_write_opts_set_iso_mbr_part_type(). 12 Sep 2017 [48ee49a] configure.ac libisofs/libisofs.h Version leap to 1.4.9 12 Sep 2017 [ce831f1] ChangeLog libisofs/changelog.txt Updated change log 15 Sep 2017 [34e3586] libisofs/node.c Silenced harmless compiler warning -Wimplicit-fallthrough 16 Sep 2017 [874dc16] libisofs/hfsplus.c Fixed a message typo found by lintian 22 Sep 2017 [53b2d6d] libisofs/hfsplus_classes.c Bug fix: Reading beyond array end for HFS+ production caused SIGSEGV with FreeBSD 11 CLANG -O2. Thanks ASX of GhostBSD. 22 Sep 2017 [79baab3] libisofs/hfsplus_classes.c Fixed a harmless lapse with static array initialization 07 Oct 2017 [7d45c88] libisofs/libisofs.h libisofs/image.h libisofs/image.c libisofs/builder.c libisofs/fs_local.c libisofs/libisofs.ver New API call iso_image_get_ignore_aclea(), new iso_image_set_ignore_aclea() and iso_file_source_get_aa_string() flag bit3 to import all xattr namespaces 23 Oct 2017 [4b031b5] libisofs/libisofs.h libisofs/image.h libisofs/image.c libisofs/fs_local.c libisofs/builder.c libisofs/aaip_0_2.h libisofs/aaip_0_2.c libisofs/aaip-os-linux.c libisofs/aaip-os-freebsd.c libisofs/aaip-os-dummy.c libisofs/libisofs.ver New flag bit7 with iso_local_set_attrs() to avoid unnecessary write attempts. New return value 2 of IsoFileSource.get_aa_string() and iso_local_get_attrs(). New API calls iso_image_was_blind_attrs(), iso_local_set_attrs_errno(). 23 Oct 2017 [633b4d5] doc/boot_sectors.txt Updated project mail addresses 31 Oct 2017 [1da3b17] libisofs/aaip-os-linux.c Changed a comment in Linux OS adapter 31 Oct 2017 [580b154] libisofs/node.c Adapted iso_node_merge_xattr to handling of all namespaces 22 Nov 2017 [a936409] libisofs/system_area.c Fixed failure to compile with experimental Libisofs_appended_partitions_inlinE 30 Mar 2018 [615dc7e] libisofs/ecma119.h libisofs/ecma119.c libisofs/system_area.c Bug fix: Add-on sessions with partition offset claimed too many blocks as size. Regression of version 1.4.8. 31 Mar 2018 [ad843f1] libisofs/joliet.c Bug fix: Long Joliet names without dot were mangled with one character too many 31 Mar 2018 [3106121] libisofs/joliet.c Bug fix: Long Joliet names with leading dot were mangled one char too short 01 May 2018 [c5a9cc5] libisofs/ecma119.h libisofs/ecma119.c libisofs/system_area.c Changed bug fix 615dc7e9978ea0ba1eed7b4b661fe4e9f6f2769e of Mar 30 13:51:21 2018 +0200 18 May 2018 [848e039] configure.ac libisofs/aaip-os-linux.c Preferring Linux include file sys/xattr.h over attr/attr.h 04 Jun 2018 [f39d4ee] libisofs/ecma119.h libisofs/system_area.h libisofs/ecma119.c libisofs/system_area.c Putting user defined padding after appended partitions 10 Jun 2018 [69c8c54] libisofs/libisofs.h libisofs/messages.c libisofs/fs_image.c Improved message at image load time about hidden El Torito images for EFI 15 Sep 2018 [d3c17d0] libisofs/libisofs.h Version leap to 1.5.0. 15 Sep 2018 [e317a8d] ChangeLog libisofs/changelog.txt Updated change log 16 Sep 2018 [066c6f6] libisofs/aaip-os-freebsd.c Fixed failure to build on NetBSD because of undeclared variable ------------------------------------ release - libisofs-1.5.0 - 15 Sep 2018 * New API call iso_image_get_ignore_aclea(), new iso_image_set_ignore_aclea() and iso_file_source_get_aa_string() flag bit3 to import all xattr namespaces * New API calls iso_image_was_blind_attrs(), iso_local_set_attrs_errno(). * New flag bit7 with iso_local_set_attrs() to avoid unnecessary write attempts. * New return value 2 of IsoFileSource.get_aa_string() and iso_local_get_attrs(). * Now putting user defined padding after appended partitions. * Bug fix: Add-on sessions with partition offset claimed too many blocks as size. Regression of version 1.4.8. * Bug fix: Long Joliet names without dot were mangled with one character too many. Long Joliet names with leading dot were mangled one char too short. * Bug fix: Reading beyond array end for HFS+ production caused SIGSEGV with FreeBSD 11 CLANG -O2. Thanks ASX of GhostBSD. 16 Sep 2018 [31c4c26] configure.ac libisofs/libisofs.h Version leap to 1.5,1 16 Sep 2018 [a63b16f] ChangeLog libisofs/changelog.txt Updated change log 24 Sep 2018 [6a6343c] COPYRIGHT README Updated copyright dates in COPYING and README 06 Oct 2018 [241b9ea] README Makefile.am acinclude.m4 libisofs/make_isohybrid_mbr.c libisofs/aaip-os-dummy.c libisofs/aaip-os-freebsd.c libisofs/aaip-os-linux.c libisofs/aaip_0_2.h libisofs/aaip_0_2.c doc/susp_aaip_isofs_names.txt Corrected and updated copyright statements 05 Nov 2018 [01415ae] libisofs/libisofs.h libisofs/eltorito.h libisofs/fs_image.c New report line with iso_image_report_el_torito() "El Torito hdsiz/512:" 10 Jan 2019 [9626158] libisofs/system_area.c Bug fix: Appended GPT partitions were not covered by the protective MBR partition 10 Jan 2019 [4064a7e] libisofs/ecma119.c libisofs/system_area.h libisofs/system_area.c Bug fix: Multi-session emulation spoiled GPT production "GPT partitions ... overlap". Regression towards 1.4.8 15 Jan 2019 [a1e7500] libisofs/ecma119.h libisofs/ecma119.c libisofs/system_area.c Bug fix: Appending partitions 5 to 8 caused damaged ISO filesystems if not for SUN disk label 15 Jan 2019 [e1097db] libisofs/ecma119.h libisofs/ecma119.c libisofs/system_area.c Changed interface of helper function iso_tell_max_part_range() 18 Feb 2019 [aed8bda] libisofs/libisofs.h libisofs/ecma119.h libisofs/ecma119.c libisofs/system_area.c libisofs/libisofs.ver New API calls iso_write_opts_set_part_type_guid(), iso_write_opts_set_iso_type_guid() 05 Apr 2019 [8fbc2fc] acinclude.m4 Makefile.am Made libisofs ready for building out-of-source. Thanks Ross Burton. 07 Apr 2019 [3aab1ca] configure.ac Disabled autotools macro AM_MAINTAINER_MODE on advise of Ross Burton. 07 Apr 2019 [c62d9d7] Makefile.am libisofs/util.c Replaced inclusion of version numbers from autotools by those from libisofs.h 18 Apr 2019 [4b21386] libisofs/system_area.c Bug fix: SIGSEGV happened if options bit 14 of iso_write_opts_set_system_area() is set and no El Torito boot image is defined 18 Apr 2019 [458ab43] libisofs/libisofs.h libisofs/libisofs.ver libisofs/ecma119.c libisofs/node.c libisofs/tree.c libisofs/eltorito.c libisofs/util.c New API call iso_nowtime() 22 Apr 2019 [a5e2092] Makefile.am Putting doc/boot_sectors.txt into release tarball 24 Jul 2019 [eb7dc40] doc/boot_sectors.txt Added Alpha to table of content of boot sectors description 24 Jul 2019 [130b46c] libisofs/libisofs.h libisofs/node.c libisofs/aaip_0_2.c New flag bit2 of iso_node_set_acl_text() to be verbous about failures 13 Aug 2019 [fe98b35] libisofs/eltorito.c Made sure that iso_image_get_bootcat() cannot return non-zero size with NULL content 26 Oct 2019 [65c4dce] configure.ac libisofs/libisofs.h Version leap to 1.5.2. 26 Oct 2019 [4d8a467] ChangeLog libisofs/changelog.txt Updated change log ------------------------------------ release - libisofs-1.5.2 - 26 Oct 2019 * New API calls iso_write_opts_set_part_type_guid(), iso_write_opts_set_iso_type_guid() * New API call iso_nowtime() * New flag bit2 of iso_node_set_acl_text() to be verbous about failures * Made libisofs ready for building out-of-source. Thanks Ross Burton. * Bug fix: Appended GPT partitions were not covered by the protective MBR partition * Bug fix: Multi-session emulation spoiled GPT production. "GPT partitions ... overlap". Regression towards 1.4.8 * Bug fix: Appending partitions 5 to 8 caused damaged ISO filesystems if not for SUN disk label * Bug fix: SIGSEGV happened if options bit 14 of iso_write_opts_set_system_area() is set and no El Torito boot image is defined 27 Oct 2019 [fa43a5a] configure.ac libisofs/libisofs.h Version leap to 1.5.3 27 Oct 2019 [560c116] ChangeLog libisofs/changelog.txt Updated change log 28 Oct 2019 [dc3d82c] libisofs/libisofs.h libisofs/node.h libisofs/node.c libisofs/builder.h libisofs/tree.c libisofs/find.c libisofs/image.h libisofs/fs_local.c libisofs/fs_image.c libisofs/ecma119.h libisofs/ecma119.c libisofs/joliet.c libisofs/iso1999.c libisofs/eltorito.c libisofs/rockridge.h libisofs/rockridge.c libisofs/ecma119_tree.c libisofs/util_rbtree.c libisofs/system_area.h libisofs/system_area.c libisofs/make_isohybrid_mbr.c libisofs/buffer.h libisofs/buffer.c libisofs/md5.c libisofs/stream.h libisofs/util.h libisofs/util.c libisofs/libiso_msgs.h libisofs/messages.c libisofs/aaip_0_2.h libisofs/aaip_0_2.c libisofs/aaip-os-linux.c libisofs/aaip-os-freebsd.c doc/susp_aaip_2_0.txt doc/checksums.txt doc/boot_sectors.txt Fixed spelling errors found by fossies.org with codespell 30 Oct 2019 [773be79] libisofs/libisofs.h libisofs/system_area.c libisofs/buffer.c libisofs/ecma119.h Fixed more spelling errors found by fossies.org with codespell 24 Nov 2019 [c1d9639] configure.ac libisofs/libisofs.h Switched to usage of libjte-2.0.0 26 Nov 2019 [ac24887] configure.ac Re-enabled variable LT_RELEASE in configure.ac. Disabling was unintentional. 13 Jun 2020 [c84f6ae] libisofs/libisofs.h Removed a germanism from description of iso_image_get_session_md5 13 Jun 2020 [6ca841e] libisofs/fs_local.c libisofs/fs_image.c Reacted on compiler warnings of Debian Sid 13 Jun 2020 [69e332d] libisofs/libisofs.h libisofs/messages.c libisofs/hfsplus.c New error code ISO_HFSPLUS_TOO_MANY_FILES instead of ISO_MANGLE_TOO_MUCH_FILES 07 Jul 2020 [gitlab 2384800] [gitea b0230b6] libisofs/system_area.c Changed strncpy() to memcpy() in order to please static analyzers 07 Jul 2020 [gitlab 30a2e85] [gitea f962d0d] libisofs/system_area.c Bug fix: Big-Endian MIPS Volume Header boot file size was rounded up to full 2048. Thanks René Rebe. 21 Sep 2020 [d297ce3] libisofs/util.c Prevented time rollover outside year intervals 1900-2155 and 1-9999 14 Oct 2020 [b107443] libisofs/libisofs.h libisofs/libisofs.ver libisofs/fs_image.c libisofs/messages.c libisofs/node.h libisofs/node.c libisofs/rockridge.h libisofs/rockridge.c libisofs/rockridge_read.c libisofs/stream.h libisofs/stream.c libisofs/util.h libisofs/util.c libisofs/filters/zisofs.c + doc/zisofs2_format.txt Implemented production and reading of zisofs2 for files larger than 4 GiB - 1. New API call iso_stream_get_zisofs_par(). New struct iso_zisofs_ctrl version 2. 15 Oct 2020 [f291e37] libisofs/filters/zisofs.c Fixed wrong start block pointer of zisofs2 compression 17 Oct 2020 [2ca3b29] libisofs/libisofs.h libisofs/messages.c libisofs/filters/zisofs.c New iso_zisofs_ctrl parameter .block_number_target 18 Oct 2020 [239ba69] libisofs/filters/zisofs.c libisofs/fs_image.c Accepting zisofs2 algorithms 2 to 5 for ZF by magic, but not for decompression 22 Oct 2020 [cc2e0e3] libisofs/libisofs.h libisofs/filters/zisofs.c New iso_zisofs_ctrl parameters bpt_discard_file_blocks , bpt_discard_free_ratio 25 Oct 2020 [80449f0] libisofs/libisofs.h libisofs/libisofs.ver libisofs/filters/zisofs.c libisofs/image.c New API calls iso_stream_zisofs_discard_bpt() and iso_image_zisofs_discard_bpt() 26 Oct 2020 [d5ffecf] libisofs/filters/zisofs.c Silenced a compiler warning if zlib is not enabled 27 Oct 2020 [dc61e7d] libisofs/libisofs.h libisofs/node.c New flag bits 8 to 15 in API call iso_node_zf_by_magic() 29 Oct 2020 [2ac62f0] libisofs/filters/zisofs.c Fixed header size of ZF entries made for zisofs2 files compressed by libisofs 29 Oct 2020 [46186e5] doc/zisofs2_format.txt Added Z2 System Use Entry Format to zisofs2 specs 29 Oct 2020 [9605bbe] libisofs/libisofs.h libisofs/libisofs.ver libisofs/rockridge.c libisofs/fs_image.c libisofs/rockridge_read.c libisofs/filters/zisofs.c New API call iso_zisofs_ctrl_susp_z2() 29 Oct 2020 [8d70c75] Makefile.am libisofs/rockridge.c libisofs/rockridge_read.c Added doc/zisofs2_format.txt to EXTRA_DIST 29 Oct 2020 [b7a90c5] libisofs/libisofs.h Corrected description of new call iso_zisofs_ctrl_susp_z2() 31 Oct 2020 [5a98a4c] libisofs/fs_image.c Corrected declaration of ziso_add_osiz_filter(). (Lapse in commit b107443) 07 Nov 2020 [b068764] libisofs/eltorito.c Bug fix: El Torito production failed if no catalog name was given and the boot image path contains no slash 07 Nov 2020 [ac9d553] libisofs/eltorito.c Fixed a new bug introduced with previous commit 13 Nov 2020 [1d5566f] README Changed Public contact from libburn-hackers@pykix.org to bug-xorriso@gnu.org 13 Nov 2020 [7e3b01b] libisofs/system_area.c Bug fix: Apple Partition Map entries wrote uninitialized data 13 Nov 2020 [daaee5e] libisofs/hfsplus.c Fixed access to packed members of struct hfsplus_volheader. Thanks Felipe Franciosi. 13 Nov 2020 [92af0c9] libisofs/system_area.c Adjusted fix 7e3b01b after learning that the bug stems from b0230b6 (unreleased) 14 Nov 2020 [29cc5c8] libisofs/system_area.c Prevented writing of undesired bytes in make_sun_disk_label() (commit b0230b6) 15 Nov 2020 [cece6fb] libisofs/ecma119.c libisofs/hfsplus.h libisofs/hfsplus.c libisofs/system_area.c Bug fix: Appended APM partitions without HFS+ production had start and size 1 22 Nov 2020 [c068a19] libisofs/libisofs.h libisofs/libisofs.ver libisofs/fs_image.c New API call iso_read_opts_set_joliet_map(), new default joliet_map=stripped 26 Nov 2020 [8f3ff65] libisofs/ecma119.c libisofs/system_area.h libisofs/system_area.c Corrected size of GPT protective MBR partition with multi-session emulation 07 Dec 2020 [6241141] libisofs/libisofs.h libisofs/libisofs.ver libisofs/fs_image.c New API calls iso_read_image_features_tree_loaded() and iso_read_image_features_rr_loaded() 07 Dec 2020 [2a20e93] libisofs/fs_image.c Small correction to commit 6241141 30 Jan 2021 [4219bf4] configure.ac libisofs/libisofs.h Version leap to 1.5.4. 30 Jan 2021 [2d1fec2] ChangeLog libisofs/changelog.txt Updated change log ------------------------------------ release - libisofs-1.5.4 - 30 Jan 2021 * Bug fix: Big-Endian MIPS Volume Header boot file size was rounded up to full 2048. Thanks René Rebe. * Bug fix: El Torito production failed if no catalog path is given and the first boot image path contains no slash * Bug fix: zisofs production was wrong on big-endian machines * Bug fix: Appended APM partitions without HFS+ production had start and size 1 * Switched to usage of libjte-2.0.0 * Implemented production and reading of zisofs2 for files larger than 4 GiB - 1. * New struct iso_zisofs_ctrl version 2 * New API call iso_stream_get_zisofs_par() * New API call iso_stream_zisofs_discard_bpt() * New API call iso_image_zisofs_discard_bpt() * New flag bits 8 to 15 in API call iso_node_zf_by_magic() * New API call iso_zisofs_ctrl_susp_z2() * New API call iso_read_opts_set_joliet_map(), new default joliet_map=stripped * New API calls iso_read_image_features_tree_loaded() and iso_read_image_features_rr_loaded() 30 Jan 2021 [5add62b] configure.ac libisofs/libisofs.h Version leap to 1.5.5 01 Feb 2021 [058f18d] libisofs/rockridge.c Bug fix: Large amounts of AAIP data or many long file names could cause with zisofs an unreadable filesystem after the warning "Calculated and written ECMA-119 tree end differ" 03 Feb 2021 [98aea0c] libisofs/rockridge.c Heuristic fix for a new problem introduced by commit 058f18d 07 Feb 2021 [release-1.5.4.branch: 408eb3f] libisofs/rockridge.c Bug fix: Large amounts of AAIP data or many long file names could cause with zisofs an unreadable filesystem after the warning "Calculated and written ECMA-119 tree end differ" 07 Feb 2021 [release-1.5.4.branch: a7a9c29] ChangeLog libisofs/changelog.txt Updated change log ------------------------------------ release - libisofs-1.5.4 - 07 Feb 2021 * Bug fix: Large amounts of AAIP data or many long file names could cause with zisofs an unreadable filesystem after the warning "Calculated and written ECMA-119 tree end differ" 28 Feb 2021 [7d248c4] libisofs/fs_image.c Ignore mad MBR partitions which extend outside of medium size 28 Feb 2021 [9e38918] libisofs/rockridge.c Leave prediction of first CE gap to susp_*_to_ce() functions 12 Mar 2021 [75499bc] libisofs/filters/zisofs.c Silenced a warning on 32 bit about value ISO_ZISOFS_V1_LIMIT too large for int 25 May 2021 [1c4c04d] libisofs/libisofs.h libisofs/ecma119.c libisofs/system_area.c New iso_write_opts_set_system_area() option bits 16: GPT "Legacy BIOS bootable" and 17: GPT writable 02 Sep 2021 [80a0691] configure.ac Removed unneeded configure.ac macro AC_C_BIGENDIAN 28 Oct 2021 [3e61a61] README Updated URLs, build instructions, and copyright in README file 22 Apr 2022 [da8e3e6] libisofs/fs_image.c Exempted MBR partitions of type 0xEE from being ignored due to wrong size 23 Apr 2022 [99251ad] libisofs/system_area.c Avoid to overwrite the loaded MBR partition table just because partition offset is 16 23 Apr 2022 [1d61b51] libisofs/system_area.c libisofs/make_isohybrid_mbr.c Bug fix: iso_write_opts_set_part_like_isohybrid() did not cause a MBR partition table if the partitions are data files in the ISO rather than appended 23 Apr 2022 [da00291] libisofs/make_isohybrid_mbr.c Let the original isohybrid GPT obey system_area() option bit 17: GPT writable 26 Apr 2022 [2af1749] libisofs/fs_local.c libisofs/fs_image.c Bug fix: The lseek methods of IsoFileSource for local filesystem and loaded ISO returned libisofs error codes as positive off_t numbers 26 Apr 2022 [f457a4f] libisofs/stream.c Added missing stream type names to a diagnostic function 26 Apr 2022 [011e2e8] libisofs/libisofs.h libisofs/tree.c libisofs/stream.c libisofs/fsource.h libisofs/fsource.c Allowed lseekable device files with iso_tree_add_new_cut_out_node(). Proof-of-concept by Ivan Shmakov. 13 May 2022 [ad55ec7] libisofs/system_area.c libisofs/make_isohybrid_mbr.c Avoided automatic MBR partition type 0x00 with iso_write_opts_set_part_like_isohybrid() if partitions do not overlap 30 May 2022 [c6cb7df] libisofs/tree.c libisofs/stream.c libisofs/fsource.h libisofs/fsource.c Widened the lseek capacity determination to SEEK_SET with wanted size 20 Sep 2022 [9b7ccc9] libisofs/libisofs.h libisofs/aaip-os-linux.c Improved error messages in case of failing Linux-specific ACL or xattr functions 20 Sep 2022 [83e5832] libisofs/libisofs.h libisofs/libisofs.ver libisofs/image.h libisofs/image.c libisofs/fs_image.c libisofs/tree.c libisofs/messages.c libisofs/util.h libisofs/util.c New API calls iso_assess_written_features(), iso_read_image_feature_named(), iso_read_image_features_text() 07 Oct 2022 [71772ba] libisofs/fs_image.c Fixed assessment of omit_version_numbers and no_force_dots 27 Oct 2022 [acb4bd1] libisofs/tree.c Bug fix: Freshly cloned data files from imported image were not marked as imported 13 Dec 2022 [d35435b] libisofs/rockridge.c Bug fix: Size of further CE area was calculated wrong if its CE entry ended exactly at a block boundary 11 Jan 2023 [7109ba5] libisofs/rockridge_read.c Prevented endless CE loops when reading a very bad ISO fileystem 22 Jan 2023 [bd41540] libisofs/libisofs.h libisofs/node.h libisofs/node.c libisofs/ecma119.h libisofs/ecma119.c libisofs/rockridge.h libisofs/rockridge.c libisofs/messages.c libisofs/libisofs.ver New API call iso_write_opts_set_max_ce_entries() 14 Apr 2023 [cdc7f52] libisofs/ecma119.h libisofs/ecma119.c libisofs/joliet.c Reduced number of warnings about special files or symlinks in Joliet 07 Jun 2023 [c2d17b1] configure.ac libisofs/libisofs.h Version leap to 1.5.6 07 Jun 2023 [170318c] ChangeLog libisofs/changelog.txt Updated change log ------------------------------------ release - libisofs-1.5.6 - 07 Jun 2023 * New iso_write_opts_set_system_area() option bits 16: GPT "Legacy BIOS bootable" and 17: GPT writable * New API calls iso_assess_written_features(), iso_read_image_feature_named(), iso_read_image_features_text() * Allowed lseekable device files with iso_tree_add_new_cut_out_node(). Proof-of-concept by Ivan Shmakov. * New API call iso_write_opts_set_max_ce_entries() * Bug fix: iso_write_opts_set_part_like_isohybrid() did not cause a MBR partition table if the partitions are data files in the ISO rather than appended * Bug fix: The lseek methods of IsoFileSource for local filesystem and loaded ISO returned libisofs error codes as positive off_t numbers * Bug fix: Freshly cloned data files from imported image were not marked as imported. Thanks to Ivan Shmakov. (Closes: #1022851) * Bug fix: Size of further CE area was calculated wrong if its CE entry ended exactly at a block boundary 07 Jun 2023 [5a867c4] configure.ac libisofs/libisofs.h Version leap to 1.5.7 [master: ][branch 1.5.6.pl01: ] libisofs/rockridge.h ChangeLog libisofs/changelog.txt Bug fix: On non-GNU/Linux systems ssize_t was not defined in rockridge.h . Report and fix proposal by Rui Chen ------------------------------- release - libisofs-1.5.6.pl01 - 09 Jun 2023 * Bug fix: On non-GNU/Linux systems ssize_t was not defined in rockridge.h . Report and fix proposal by Rui Chen [] libisofs/ecma119.c libisofs/fs_image.c libisofs/rockridge.c libisofs/stream.c [] README Updated copyright year in README ------------------------------------ release - libisofs-1.5.8 - ------------------------------------------------------------------------ Todo about iso_read_image_feature_named() : Declared but not yet filled: {"hfsplus", 0, 0, 0, NULL}, {"fat", 0, 0, 0, NULL}, {"hfsp_serial_number", 0, 1, 0, NULL}, {"hfsp_block_size", 0, 0, 0, NULL}, {"hardlinks", 0, 0, 0, NULL}, {"rr_reloc_dir", 0, 1, 0, NULL}, {"rr_reloc_flags", 0, 0, 0, NULL, 0}, {"allow_7bit_ascii", 0, 0, 0, NULL}, ??? How to recognize this ? {"scdbackup_tag_name", 0, 1, 0, NULL}, {"scdbackup_tag_time", 0, 1, 0, NULL}, {"always_gmt", 0, 0, 0, NULL}, IsoWriteOpts properties not yet covered by above list: ??? dir_rec_mtime ??? sort_files ??? output_charset ??? appendable ??? ms_block ??? data_start_lba ??? tail_blocks ??? vol_*_time ??? vol_uuid ??? system_area_data ??? system_area_size ??? system_area_options ??? partition_offset ??? partition_secs_per_head , partition_heads_per_cyl ??? Other properties of partitions and boot equipment /* * Copyright (c) 2007 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libisofs.h" #include "util.h" #include #include #include #include #include #include /* O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif /** * Private data for File IsoDataSource */ struct file_data_src { char *path; int fd; }; /** * Increments the reference counting of the given IsoDataSource. */ void iso_data_source_ref(IsoDataSource *src) { src->refcount++; } /** * Decrements the reference counting of the given IsoDataSource, freeing it * if refcount reach 0. */ void iso_data_source_unref(IsoDataSource *src) { if (--src->refcount == 0) { src->free_data(src); free(src); } } static int ds_open(IsoDataSource *src) { int fd; struct file_data_src *data; if (src == NULL || src->data == NULL) { return ISO_NULL_POINTER; } data = (struct file_data_src*) src->data; if (data->fd != -1) { return ISO_FILE_ALREADY_OPENED; } fd = open(data->path, O_RDONLY | O_BINARY); if (fd == -1) { return ISO_FILE_ERROR; } data->fd = fd; return ISO_SUCCESS; } static int ds_close(IsoDataSource *src) { int ret; struct file_data_src *data; if (src == NULL || src->data == NULL) { return ISO_NULL_POINTER; } data = (struct file_data_src*) src->data; if (data->fd == -1) { return ISO_FILE_NOT_OPENED; } /* close can fail if fd is not valid, but that should never happen */ ret = close(data->fd); /* in any case we mark file as closed */ data->fd = -1; return ret == 0 ? ISO_SUCCESS : ISO_FILE_ERROR; } static int ds_read_block(IsoDataSource *src, uint32_t lba, uint8_t *buffer) { struct file_data_src *data; if (src == NULL || src->data == NULL || buffer == NULL) { return ISO_NULL_POINTER; } data = (struct file_data_src*) src->data; if (data->fd == -1) { return ISO_FILE_NOT_OPENED; } /* goes to requested block */ if (lseek(data->fd, (off_t)lba * (off_t)2048, SEEK_SET) == (off_t) -1) { return ISO_FILE_SEEK_ERROR; } /* TODO #00008 : guard against partial reads. */ if (read(data->fd, buffer, 2048) != 2048) { return ISO_FILE_READ_ERROR; } return ISO_SUCCESS; } static void ds_free_data(IsoDataSource *src) { struct file_data_src *data; data = (struct file_data_src*)src->data; /* close the file if needed */ if (data->fd != -1) { close(data->fd); } free(data->path); free(data); } /** * Create a new IsoDataSource from a local file. This is suitable for * accessing regular .iso images, or to access drives via its block device * and standard POSIX I/O calls. * * @param path * The path of the file * @param src * Will be filled with the pointer to the newly created data source. * @return * 1 on success, < 0 on error. */ int iso_data_source_new_from_file(const char *path, IsoDataSource **src) { int ret; struct file_data_src *data; IsoDataSource *ds; if (path == NULL || src == NULL) { return ISO_NULL_POINTER; } /* ensure we have read access to the file */ ret = iso_eaccess(path); if (ret < 0) { return ret; } data = malloc(sizeof(struct file_data_src)); if (data == NULL) { return ISO_OUT_OF_MEM; } ds = malloc(sizeof(IsoDataSource)); if (ds == NULL) { free(data); return ISO_OUT_OF_MEM; } /* fill data fields */ data->path = strdup(path); if (data->path == NULL) { free(data); free(ds); return ISO_OUT_OF_MEM; } data->fd = -1; ds->version = 0; ds->refcount = 1; ds->data = data; ds->open = ds_open; ds->close = ds_close; ds->read_block = ds_read_block; ds->free_data = ds_free_data; *src = ds; return ISO_SUCCESS; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2007 Mario Danic * Copyright (c) 2009 - 2023 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /* Use the copy of the struct burn_source definition in libisofs.h */ #define LIBISOFS_WITHOUT_LIBBURN yes #include "libisofs.h" #include "ecma119.h" #include "joliet.h" #include "hfsplus.h" #include "iso1999.h" #include "eltorito.h" #include "ecma119_tree.h" #include "filesrc.h" #include "image.h" #include "writer.h" #include "messages.h" #include "rockridge.h" #include "util.h" #include "system_area.h" #include "md5.h" #include #include #include #include #include #include #include #include #ifdef Xorriso_standalonE #ifdef Xorriso_with_libjtE #include "../libjte/libjte.h" #endif #else #ifdef Libisofs_with_libjtE #include #endif #endif /* ! Xorriso_standalonE */ int iso_write_opts_clone(IsoWriteOpts *in, IsoWriteOpts **out, int flag); /* * TODO #00011 : guard against bad path table usage with more than 65535 dirs * image with more than 65535 directories have path_table related problems * due to 16 bits parent id. Note that this problem only affects to folders * that are parent of another folder. */ static void ecma119_image_free(Ecma119Image *t) { size_t i; if (t == NULL) return; if (t->refcount > 1) { t->refcount--; return; } if (t->root != NULL) ecma119_node_free(t->root); if (t->opts != NULL) iso_write_opts_free(t->opts); if (t->image != NULL) iso_image_unref(t->image); if (t->files != NULL) iso_rbtree_destroy(t->files, iso_file_src_free); if (t->ecma119_hidden_list != NULL) iso_filesrc_list_destroy(&(t->ecma119_hidden_list)); if (t->buffer != NULL) iso_ring_buffer_free(t->buffer); for (i = 0; i < t->nwriters; ++i) { IsoImageWriter *writer = t->writers[i]; writer->free_data(writer); free(writer); } if (t->input_charset != NULL) free(t->input_charset); if (t->output_charset != NULL) free(t->output_charset); if (t->bootsrc != NULL) free(t->bootsrc); if (t->boot_appended_idx != NULL) free(t->boot_appended_idx); if (t->boot_intvl_start != NULL) free(t->boot_intvl_start); if (t->boot_intvl_size != NULL) free(t->boot_intvl_size); if (t->system_area_data != NULL) free(t->system_area_data); if (t->checksum_ctx != NULL) { /* dispose checksum context */ char md5[16]; iso_md5_end(&(t->checksum_ctx), md5); } if (t->checksum_buffer != NULL) free(t->checksum_buffer); if (t->writers != NULL) free(t->writers); if (t->partition_root != NULL) ecma119_node_free(t->partition_root); for (i = 0; i < ISO_HFSPLUS_BLESS_MAX; i++) if (t->hfsplus_blessed[i] != NULL) iso_node_unref(t->hfsplus_blessed[i]); for (i = 0; (int) i < t->apm_req_count; i++) if (t->apm_req[i] != NULL) free(t->apm_req[i]); for (i = 0; (int) i < t->mbr_req_count; i++) if (t->mbr_req[i] != NULL) free(t->mbr_req[i]); for (i = 0; (int) i < t->gpt_req_count; i++) if (t->gpt_req[i] != NULL) free(t->gpt_req[i]); free(t); } static int show_chunk_to_jte(Ecma119Image *target, char *buf, int count) { #ifdef Libisofs_with_libjtE int ret; if (target->opts->libjte_handle == NULL) return ISO_SUCCESS; ret = libjte_show_data_chunk(target->opts->libjte_handle, buf, count, 1); if (ret <= 0) { iso_libjte_forward_msgs(target->opts->libjte_handle, target->image->id, ISO_LIBJTE_FILE_FAILED, 0); return ISO_LIBJTE_FILE_FAILED; } #endif /* Libisofs_with_libjtE */ return ISO_SUCCESS; } /** * Check if we should add version number ";" to the given node name. */ static int need_version_number(IsoWriteOpts *opts, enum ecma119_node_type node_type) { if ((opts->omit_version_numbers & 1) || opts->max_37_char_filenames || opts->untranslated_name_len > 0) { return 0; } if (node_type == ECMA119_DIR || node_type == ECMA119_PLACEHOLDER) { return 0; } else { return 1; } } /** * Compute the size of a directory entry for a single node */ static size_t calc_dirent_len(Ecma119Image *t, Ecma119Node *n) { int ret = n->iso_name ? strlen(n->iso_name) + 33 : 34; if (need_version_number(t->opts, n->type)) { ret += 2; /* take into account version numbers */ } if (ret % 2) ret++; return ret; } /** * Computes the total size of all directory entries of a single dir, * according to ECMA-119 6.8.1.1 * * This also take into account the size needed for RR entries and * SUSP continuation areas (SUSP, 5.1). * * @param ce * Will be filled with the size needed for Continuation Areas * @return * The size needed for all dir entries of the given dir, without * taking into account the continuation areas. */ static ssize_t calc_dir_size(Ecma119Image *t, Ecma119Node *dir, size_t *ce) { size_t i, len; ssize_t ret; size_t ce_len = 0; /* size of "." and ".." entries */ len = 34 + 34; if (t->opts->rockridge) { ret = rrip_calc_len(t, dir, 1, 34, &ce_len, *ce); if (ret < 0) return ret; len += ret; *ce += ce_len; ret = rrip_calc_len(t, dir, 2, 34, &ce_len, *ce); if (ret < 0) return ret; len += ret; *ce += ce_len; } for (i = 0; i < dir->info.dir->nchildren; ++i) { size_t remaining; int section, nsections; Ecma119Node *child = dir->info.dir->children[i]; nsections = (child->type == ECMA119_FILE) ? child->info.file->nsections : 1; for (section = 0; section < nsections; ++section) { size_t dirent_len = calc_dirent_len(t, child); if (t->opts->rockridge) { ret = rrip_calc_len(t, child, 0, dirent_len, &ce_len, *ce); if (ret < 0) return ret; dirent_len += ret; *ce += ce_len; } remaining = BLOCK_SIZE - (len % BLOCK_SIZE); if (dirent_len > remaining) { /* child directory entry doesn't fit on block */ len += remaining + dirent_len; } else { len += dirent_len; } } } /* * The size of a dir is always a multiple of block size, as we must add * the size of the unused space after the last directory record * (ECMA-119, 6.8.1.3) */ len = ROUND_UP(len, BLOCK_SIZE); /* cache the len */ dir->info.dir->len = len; return len; } static int calc_dir_pos(Ecma119Image *t, Ecma119Node *dir) { size_t i, len; ssize_t ret; size_t ce_len = 0; t->ndirs++; dir->info.dir->block = t->curblock; ret = calc_dir_size(t, dir, &ce_len); if (ret < 0) return (int) ret; len = ret; t->curblock += DIV_UP(len, BLOCK_SIZE); if (t->opts->rockridge) { t->curblock += DIV_UP(ce_len, BLOCK_SIZE); } for (i = 0; i < dir->info.dir->nchildren; i++) { Ecma119Node *child = dir->info.dir->children[i]; if (child->type == ECMA119_DIR) { ret = calc_dir_pos(t, child); if (ret < 0) return (int) ret; } } return ISO_SUCCESS; } /** * Compute the length of the path table, in bytes. */ static uint32_t calc_path_table_size(Ecma119Node *dir) { uint32_t size; size_t i; /* size of path table for this entry */ size = 8; size += dir->iso_name ? strlen(dir->iso_name) : 1; size += (size % 2); /* and recurse */ for (i = 0; i < dir->info.dir->nchildren; i++) { Ecma119Node *child = dir->info.dir->children[i]; if (child->type == ECMA119_DIR) { size += calc_path_table_size(child); } } return size; } static int ecma119_writer_compute_data_blocks(IsoImageWriter *writer) { Ecma119Image *target; uint32_t path_table_size; size_t ndirs; int ret; if (writer == NULL) { return ISO_ASSERT_FAILURE; } target = writer->target; /* compute position of directories */ iso_msg_debug(target->image->id, "Computing position of dir structure"); target->ndirs = 0; ret = calc_dir_pos(target, target->root); if (ret < 0) return ret; /* compute length of pathlist */ iso_msg_debug(target->image->id, "Computing length of pathlist"); path_table_size = calc_path_table_size(target->root); /* compute location for path tables */ target->l_path_table_pos = target->curblock; target->curblock += DIV_UP(path_table_size, BLOCK_SIZE); target->m_path_table_pos = target->curblock; target->curblock += DIV_UP(path_table_size, BLOCK_SIZE); target->path_table_size = path_table_size; if (target->opts->md5_session_checksum) { /* Account for first tree checksum tag */ target->checksum_tree_tag_pos = target->curblock; target->curblock++; } if (target->opts->partition_offset > 0) { /* Take into respect the second directory tree */ ndirs = target->ndirs; target->ndirs = 0; ret = calc_dir_pos(target, target->partition_root); if (ret < 0) return ret; if (target->ndirs != ndirs) { iso_msg_submit(target->image->id, ISO_ASSERT_FAILURE, 0, "Number of directories differs in ECMA-119 partiton_tree"); return ISO_ASSERT_FAILURE; } /* Take into respect the second set of path tables */ path_table_size = calc_path_table_size(target->partition_root); target->partition_l_table_pos = target->curblock; target->curblock += DIV_UP(path_table_size, BLOCK_SIZE); target->partition_m_table_pos = target->curblock; target->curblock += DIV_UP(path_table_size, BLOCK_SIZE); /* >>> TWINTREE: >>> For now, checksum tags are only for the image start and not for the partition */; } target->tree_end_block = target->curblock; return ISO_SUCCESS; } /** * Write a single directory record (ECMA-119, 9.1) * * @param file_id * if >= 0, we use it instead of the filename (for "." and ".." entries). * @param len_fi * Computed length of the file identifier. Total size of the directory * entry will be len + 33 + padding if needed (ECMA-119, 9.1.12) * @param info * SUSP entries for the given directory record. It will be NULL for the * root directory record in the PVD (ECMA-119, 8.4.18) (in order to * distinguish it from the "." entry in the root directory) */ static void write_one_dir_record(Ecma119Image *t, Ecma119Node *node, int file_id, uint8_t *buf, size_t len_fi, struct susp_info *info, int extent) { uint32_t len; uint32_t block; uint8_t len_dr; /*< size of dir entry without SUSP fields */ int multi_extend = 0; uint8_t *name = (file_id >= 0) ? (uint8_t*)&file_id : (uint8_t*)node->iso_name; struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; IsoNode *iso; len_dr = 33 + len_fi + ((len_fi % 2) ? 0 : 1); memcpy(rec->file_id, name, len_fi); if (need_version_number(t->opts, node->type)) { len_dr += 2; rec->file_id[len_fi++] = ';'; rec->file_id[len_fi++] = '1'; } if (node->type == ECMA119_DIR) { /* use the cached length */ len = node->info.dir->len; block = node->info.dir->block; } else if (node->type == ECMA119_FILE) { block = node->info.file->sections[extent].block; len = node->info.file->sections[extent].size; multi_extend = (node->info.file->nsections - 1 == extent) ? 0 : 1; } else { /* * for nodes other than files and dirs, we set len to 0, and * the content block address to a dummy value. */ len = 0; if (! t->opts->old_empty) block = t->empty_file_block; else block = 0; } /* * For ".." entry we need to write the parent info! */ if (file_id == 1 && node->parent) node = node->parent; rec->len_dr[0] = len_dr + (info != NULL ? info->suf_len : 0); iso_bb(rec->block, block - t->eff_partition_offset, 4); iso_bb(rec->length, len, 4); if (t->opts->dir_rec_mtime & 1) { iso= node->node; iso_datetime_7(rec->recording_time, t->replace_timestamps ? t->timestamp : iso->mtime, t->opts->always_gmt); } else { iso_datetime_7(rec->recording_time, t->now, t->opts->always_gmt); } rec->flags[0] = ((node->type == ECMA119_DIR) ? 2 : 0) | (multi_extend ? 0x80 : 0); iso_bb(rec->vol_seq_number, (uint32_t) 1, 2); rec->len_fi[0] = len_fi; /* * and finally write the SUSP fields. */ if (info != NULL) { rrip_write_susp_fields(t, info, buf + len_dr); } } static char *get_relaxed_vol_id(Ecma119Image *t, const char *name) { int ret; if (name == NULL) { return NULL; } if (strcmp(t->input_charset, t->output_charset)) { /* charset conversion needed */ char *str; ret = strconv(name, t->input_charset, t->output_charset, &str); if (ret == ISO_SUCCESS) { return str; } iso_msg_submit(t->image->id, ISO_FILENAME_WRONG_CHARSET, ret, "Charset conversion error. Cannot convert from %s to %s", t->input_charset, t->output_charset); } return strdup(name); } /** * Set the timestamps of Primary, Supplementary, or Enhanced Volume Descriptor. */ void ecma119_set_voldescr_times(IsoImageWriter *writer, struct ecma119_pri_vol_desc *vol) { Ecma119Image *t = writer->target; IsoWriteOpts *o; int i; o = t->opts; if (o->vol_uuid[0]) { for(i = 0; i < 16; i++) if(o->vol_uuid[i] < '0' || o->vol_uuid[i] > '9') break; else vol->vol_creation_time[i] = o->vol_uuid[i]; for(; i < 16; i++) vol->vol_creation_time[i] = '1'; vol->vol_creation_time[16] = 0; } else if (o->vol_creation_time > 0) iso_datetime_17(vol->vol_creation_time, o->vol_creation_time, o->always_gmt); else iso_datetime_17(vol->vol_creation_time, t->now, o->always_gmt); if (o->vol_uuid[0]) { for(i = 0; i < 16; i++) if(o->vol_uuid[i] < '0' || o->vol_uuid[i] > '9') break; else vol->vol_modification_time[i] = o->vol_uuid[i]; for(; i < 16; i++) vol->vol_modification_time[i] = '1'; vol->vol_modification_time[16] = 0; } else if (o->vol_modification_time > 0) iso_datetime_17(vol->vol_modification_time, o->vol_modification_time, o->always_gmt); else iso_datetime_17(vol->vol_modification_time, t->now, o->always_gmt); if (o->vol_expiration_time > 0) { iso_datetime_17(vol->vol_expiration_time, o->vol_expiration_time, o->always_gmt); } else { for(i = 0; i < 16; i++) vol->vol_expiration_time[i] = '0'; vol->vol_expiration_time[16] = 0; } if (o->vol_effective_time > 0) { iso_datetime_17(vol->vol_effective_time, o->vol_effective_time, o->always_gmt); } else { for(i = 0; i < 16; i++) vol->vol_effective_time[i] = '0'; vol->vol_effective_time[16] = 0; } } /** * Write the Primary Volume Descriptor (ECMA-119, 8.4) */ static int ecma119_writer_write_vol_desc(IsoImageWriter *writer) { IsoImage *image; Ecma119Image *t; struct ecma119_pri_vol_desc vol; char *vol_id, *pub_id, *data_id, *volset_id; char *system_id, *application_id, *copyright_file_id; char *abstract_file_id, *biblio_file_id; if (writer == NULL) { return ISO_ASSERT_FAILURE; } t = writer->target; image = t->image; iso_msg_debug(image->id, "Write Primary Volume Descriptor"); memset(&vol, 0, sizeof(struct ecma119_pri_vol_desc)); if (t->opts->relaxed_vol_atts) { vol_id = get_relaxed_vol_id(t, image->volume_id); volset_id = get_relaxed_vol_id(t, image->volset_id); } else { str2d_char(t->input_charset, image->volume_id, &vol_id); str2d_char(t->input_charset, image->volset_id, &volset_id); } str2a_char(t->input_charset, image->publisher_id, &pub_id); str2a_char(t->input_charset, image->data_preparer_id, &data_id); str2a_char(t->input_charset, image->system_id, &system_id); str2a_char(t->input_charset, image->application_id, &application_id); str2d_char(t->input_charset, image->copyright_file_id, ©right_file_id); str2d_char(t->input_charset, image->abstract_file_id, &abstract_file_id); str2d_char(t->input_charset, image->biblio_file_id, &biblio_file_id); vol.vol_desc_type[0] = 1; memcpy(vol.std_identifier, "CD001", 5); vol.vol_desc_version[0] = 1; strncpy_pad((char*)vol.system_id, system_id, 32); strncpy_pad((char*)vol.volume_id, vol_id, 32); if (t->pvd_size_is_total_size && t->eff_partition_offset <= 0) { iso_bb(vol.vol_space_size, t->total_size / 2048, 4); } else { iso_bb(vol.vol_space_size, t->vol_space_size - t->eff_partition_offset, 4); } iso_bb(vol.vol_set_size, (uint32_t) 1, 2); iso_bb(vol.vol_seq_number, (uint32_t) 1, 2); iso_bb(vol.block_size, (uint32_t) BLOCK_SIZE, 2); iso_bb(vol.path_table_size, t->path_table_size, 4); if (t->eff_partition_offset > 0) { /* Point to second tables and second root */ iso_lsb(vol.l_path_table_pos, t->partition_l_table_pos - t->eff_partition_offset, 4); iso_msb(vol.m_path_table_pos, t->partition_m_table_pos - t->eff_partition_offset, 4); write_one_dir_record(t, t->partition_root, 0, vol.root_dir_record, 1, NULL, 0); } else { iso_lsb(vol.l_path_table_pos, t->l_path_table_pos, 4); iso_msb(vol.m_path_table_pos, t->m_path_table_pos, 4); write_one_dir_record(t, t->root, 0, vol.root_dir_record, 1, NULL, 0); } strncpy_pad((char*)vol.vol_set_id, volset_id, 128); strncpy_pad((char*)vol.publisher_id, pub_id, 128); strncpy_pad((char*)vol.data_prep_id, data_id, 128); strncpy_pad((char*)vol.application_id, application_id, 128); strncpy_pad((char*)vol.copyright_file_id, copyright_file_id, 37); strncpy_pad((char*)vol.abstract_file_id, abstract_file_id, 37); strncpy_pad((char*)vol.bibliographic_file_id, biblio_file_id, 37); ecma119_set_voldescr_times(writer, &vol); vol.file_structure_version[0] = 1; memcpy(vol.app_use, image->application_use, 512); free(vol_id); free(volset_id); free(pub_id); free(data_id); free(system_id); free(application_id); free(copyright_file_id); free(abstract_file_id); free(biblio_file_id); /* Finally write the Volume Descriptor */ return iso_write(t, &vol, sizeof(struct ecma119_pri_vol_desc)); } static int write_one_dir(Ecma119Image *t, Ecma119Node *dir, Ecma119Node *parent) { int ret; uint8_t *buffer = NULL; size_t i; size_t fi_len, len; struct susp_info info; /* buf will point to current write position on buffer */ uint8_t *buf; LIBISO_ALLOC_MEM(buffer, uint8_t, BLOCK_SIZE); buf = buffer; /* * set susp_info to 0's, this way code for both plain ECMA-119 and * RR is very similar */ memset(&info, 0, sizeof(struct susp_info)); if (t->opts->rockridge) { /* initialize the ce_block, it might be needed */ info.ce_block = dir->info.dir->block + DIV_UP(dir->info.dir->len, BLOCK_SIZE); info.ce_susp_fields = NULL; } /* write the "." and ".." entries first */ if (t->opts->rockridge) { ret = rrip_get_susp_fields(t, dir, 1, 34, &info); if (ret < 0) { goto ex; } } len = 34 + info.suf_len; write_one_dir_record(t, dir, 0, buf, 1, &info, 0); buf += len; if (t->opts->rockridge) { ret = rrip_get_susp_fields(t, dir, 2, 34, &info); if (ret < 0) { goto ex; } } len = 34 + info.suf_len; write_one_dir_record(t, parent, 1, buf, 1, &info, 0); buf += len; for (i = 0; i < dir->info.dir->nchildren; i++) { int section, nsections; Ecma119Node *child = dir->info.dir->children[i]; fi_len = strlen(child->iso_name); nsections = (child->type == ECMA119_FILE) ? child->info.file->nsections : 1; for (section = 0; section < nsections; ++section) { /* compute len of directory entry */ len = fi_len + 33 + ((fi_len % 2) ? 0 : 1); if (need_version_number(t->opts, child->type)) { len += 2; } /* get the SUSP fields if rockridge is enabled */ if (t->opts->rockridge) { ret = rrip_get_susp_fields(t, child, 0, len, &info); if (ret < 0) { goto ex; } len += info.suf_len; } if ( (buf + len - buffer) > BLOCK_SIZE) { /* dir doesn't fit in current block */ ret = iso_write(t, buffer, BLOCK_SIZE); if (ret < 0) { goto ex; } memset(buffer, 0, BLOCK_SIZE); buf = buffer; } /* write the directory entry in any case */ write_one_dir_record(t, child, -1, buf, fi_len, &info, section); buf += len; } } /* write the last block */ ret = iso_write(t, buffer, BLOCK_SIZE); if (ret < 0) { goto ex; } /* write the Continuation Area if needed */ if (info.ce_len > 0) { ret = rrip_write_ce_fields(t, &info); } ex:; LIBISO_FREE_MEM(buffer); return ret; } static int write_dirs(Ecma119Image *t, Ecma119Node *root, Ecma119Node *parent) { int ret; size_t i; /* write all directory entries for this dir */ ret = write_one_dir(t, root, parent); if (ret < 0) { return ret; } /* recurse */ for (i = 0; i < root->info.dir->nchildren; i++) { Ecma119Node *child = root->info.dir->children[i]; if (child->type == ECMA119_DIR) { ret = write_dirs(t, child, root); if (ret < 0) { return ret; } } } return ISO_SUCCESS; } static int write_path_table(Ecma119Image *t, Ecma119Node **pathlist, int l_type) { size_t i, len; uint8_t buf[64]; /* 64 is just a convenient size larger enough */ struct ecma119_path_table_record *rec; void (*write_int)(uint8_t*, uint32_t, int); Ecma119Node *dir; uint32_t path_table_size; int parent = 0; int ret= ISO_SUCCESS; uint8_t *zeros = NULL; path_table_size = 0; write_int = l_type ? iso_lsb : iso_msb; for (i = 0; i < t->ndirs; i++) { dir = pathlist[i]; /* find the index of the parent in the table */ while ((i) && pathlist[parent] != dir->parent) { parent++; } /* write the Path Table Record (ECMA-119, 9.4) */ memset(buf, 0, 64); rec = (struct ecma119_path_table_record*) buf; rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->iso_name) : 1; rec->len_xa[0] = 0; write_int(rec->block, dir->info.dir->block - t->eff_partition_offset, 4); write_int(rec->parent, parent + 1, 2); if (dir->parent) { memcpy(rec->dir_id, dir->iso_name, rec->len_di[0]); } len = 8 + rec->len_di[0] + (rec->len_di[0] % 2); ret = iso_write(t, buf, len); if (ret < 0) { /* error */ goto ex; } path_table_size += len; } /* we need to fill the last block with zeros */ path_table_size %= BLOCK_SIZE; if (path_table_size) { len = BLOCK_SIZE - path_table_size; LIBISO_ALLOC_MEM(zeros, uint8_t, len); ret = iso_write(t, zeros, len); } ex:; LIBISO_FREE_MEM(zeros); return ret; } static int write_path_tables(Ecma119Image *t) { int ret; size_t i, j, cur; Ecma119Node **pathlist; iso_msg_debug(t->image->id, "Writing ISO Path tables"); /* allocate temporal pathlist */ pathlist = malloc(sizeof(void*) * t->ndirs); if (pathlist == NULL) { return ISO_OUT_OF_MEM; } if (t->eff_partition_offset > 0) { pathlist[0] = t->partition_root; } else { pathlist[0] = t->root; } cur = 1; for (i = 0; i < t->ndirs; i++) { Ecma119Node *dir = pathlist[i]; for (j = 0; j < dir->info.dir->nchildren; j++) { Ecma119Node *child = dir->info.dir->children[j]; if (child->type == ECMA119_DIR) { pathlist[cur++] = child; } } } /* Write L Path Table */ ret = write_path_table(t, pathlist, 1); if (ret < 0) { goto write_path_tables_exit; } /* Write L Path Table */ ret = write_path_table(t, pathlist, 0); write_path_tables_exit: ; free(pathlist); return ret; } /** * Write the directory structure (ECMA-119, 6.8) and the L and M * Path Tables (ECMA-119, 6.9). */ static int ecma119_writer_write_dirs(IsoImageWriter *writer) { int ret, isofs_ca_changed = 0; Ecma119Image *t; Ecma119Node *root; char *value = NULL; size_t value_length; t = writer->target; /* first of all, we write the directory structure */ if (t->eff_partition_offset > 0) { root = t->partition_root; if ((t->opts->md5_file_checksums & 1) || t->opts->md5_session_checksum) { /* Take into respect the address offset in "isofs.ca" */ ret = iso_node_lookup_attr((IsoNode *) t->image->root, "isofs.ca", &value_length, &value, 0); if (value != NULL) free(value); if (ret == 1 && value_length == 20) { /* "isofs.ca" does really exist and has the expected length */ ret = iso_root_set_isofsca((IsoNode *) t->image->root, t->checksum_range_start - t->eff_partition_offset, t->checksum_array_pos - t->eff_partition_offset, t->checksum_idx_counter + 2, 16, "MD5", 0); if (ret < 0) return ret; isofs_ca_changed = 1; } } } else { root = t->root; } ret = write_dirs(t, root, root); if (ret < 0) { return ret; } /* and write the path tables */ ret = write_path_tables(t); if (ret < 0) return ret; if (t->opts->md5_session_checksum) { /* Write tree checksum tag */ if (t->eff_partition_offset > 0) { /* >>> TWINTREE: >>> For now, tags are only for the image start and not for the partition */; } else { ret = iso_md5_write_tag(t, 3); } } if (isofs_ca_changed) { /* Restore old addresses offset in "isofs.ca" of root node */ ret = iso_root_set_isofsca((IsoNode *) t->image->root, t->checksum_range_start, t->checksum_array_pos, t->checksum_idx_counter + 2, 16, "MD5", 0); if (ret < 0) return ret; } return ret; } /** * Write directory structure and Path Tables of the ECMA-119 tree. * This happens eventually a second time for the duplicates which use * addresses with partition offset. */ static int ecma119_writer_write_data(IsoImageWriter *writer) { int ret; Ecma119Image *t; uint32_t curblock; char *msg = NULL; if (writer == NULL) {ret = ISO_ASSERT_FAILURE; goto ex;} t = writer->target; ret = ecma119_writer_write_dirs(writer); if (ret < 0) goto ex; if (t->opts->partition_offset > 0) { t->eff_partition_offset = t->opts->partition_offset; ret = ecma119_writer_write_dirs(writer); t->eff_partition_offset = 0; if (ret < 0) goto ex; } curblock = (t->bytes_written / 2048) + t->opts->ms_block; if (curblock != t->tree_end_block) { LIBISO_ALLOC_MEM(msg, char, 100); sprintf(msg, "Calculated and written ECMA-119 tree end differ: %lu <> %lu", (unsigned long) t->tree_end_block, (unsigned long) curblock); iso_msgs_submit(0, msg, 0, "WARNING", 0); t->tree_end_block = 1;/* Mark for harsher reaction at end of writing */ } ret = ISO_SUCCESS; ex:; LIBISO_FREE_MEM(msg); return ret; } static int ecma119_writer_free_data(IsoImageWriter *writer) { /* nothing to do */ return ISO_SUCCESS; } int ecma119_writer_create(Ecma119Image *target) { int ret; IsoImageWriter *writer; writer = malloc(sizeof(IsoImageWriter)); if (writer == NULL) { return ISO_OUT_OF_MEM; } writer->compute_data_blocks = ecma119_writer_compute_data_blocks; writer->write_vol_desc = ecma119_writer_write_vol_desc; writer->write_data = ecma119_writer_write_data; writer->free_data = ecma119_writer_free_data; writer->data = NULL; writer->target = target; /* add this writer to image */ target->writers[target->nwriters++] = writer; iso_msg_debug(target->image->id, "Creating low level ECMA-119 tree..."); ret = ecma119_tree_create(target); if (ret < 0) { return ret; } if (target->image->sparc_core_node != NULL) { /* Obtain a duplicate of the IsoFile's Ecma119Node->file */ ret = iso_file_src_create(target, target->image->sparc_core_node, &target->sparc_core_src); if (ret < 0) return ret; } if(target->opts->partition_offset > 0) { /* Create second tree */ target->eff_partition_offset = target->opts->partition_offset; ret = ecma119_tree_create(target); target->eff_partition_offset = 0; if (ret < 0) return ret; } /* we need the volume descriptor */ target->curblock++; return ISO_SUCCESS; } /** compute how many padding bytes are needed */ static int mspad_writer_compute_data_blocks(IsoImageWriter *writer) { Ecma119Image *target; uint32_t min_size; if (writer == NULL) { return ISO_ASSERT_FAILURE; } target = writer->target; min_size = 32 + target->opts->partition_offset; if (target->curblock < min_size) { target->mspad_blocks = min_size - target->curblock; target->curblock = min_size; } return ISO_SUCCESS; } static int mspad_writer_write_vol_desc(IsoImageWriter *writer) { /* nothing to do */ return ISO_SUCCESS; } static int mspad_writer_write_data(IsoImageWriter *writer) { int ret; Ecma119Image *t; uint8_t *pad = NULL; size_t i; if (writer == NULL) { {ret = ISO_ASSERT_FAILURE; goto ex;} } t = writer->target; if (t->mspad_blocks == 0) { {ret = ISO_SUCCESS; goto ex;} } LIBISO_ALLOC_MEM(pad, uint8_t, BLOCK_SIZE); for (i = 0; i < t->mspad_blocks; ++i) { ret = iso_write(t, pad, BLOCK_SIZE); if (ret < 0) { goto ex; } } ret = ISO_SUCCESS; ex:; LIBISO_FREE_MEM(pad); return ret; } static int mspad_writer_free_data(IsoImageWriter *writer) { /* nothing to do */ return ISO_SUCCESS; } static int mspad_writer_create(Ecma119Image *target) { IsoImageWriter *writer; writer = malloc(sizeof(IsoImageWriter)); if (writer == NULL) { return ISO_OUT_OF_MEM; } writer->compute_data_blocks = mspad_writer_compute_data_blocks; writer->write_vol_desc = mspad_writer_write_vol_desc; writer->write_data = mspad_writer_write_data; writer->free_data = mspad_writer_free_data; writer->data = NULL; writer->target = target; /* add this writer to image */ target->writers[target->nwriters++] = writer; return ISO_SUCCESS; } /** ----- Zero padding writer ----- */ struct iso_zero_writer_data_struct { uint32_t num_blocks; }; static int zero_writer_compute_data_blocks(IsoImageWriter *writer) { Ecma119Image *target; struct iso_zero_writer_data_struct *data; if (writer == NULL) return ISO_ASSERT_FAILURE; target = writer->target; data = (struct iso_zero_writer_data_struct *) writer->data; target->curblock += data->num_blocks; return ISO_SUCCESS; } static int zero_writer_write_vol_desc(IsoImageWriter *writer) { /* nothing to do */ return ISO_SUCCESS; } static int zero_writer_write_data(IsoImageWriter *writer) { int ret; Ecma119Image *t; struct iso_zero_writer_data_struct *data; uint8_t *pad = NULL; size_t i; if (writer == NULL) {ret = ISO_ASSERT_FAILURE; goto ex;} t = writer->target; data = (struct iso_zero_writer_data_struct *) writer->data; if (data->num_blocks == 0) {ret = ISO_SUCCESS; goto ex;} LIBISO_ALLOC_MEM(pad, uint8_t, BLOCK_SIZE); for (i = 0; i < data->num_blocks; ++i) { ret = iso_write(t, pad, BLOCK_SIZE); if (ret < 0) goto ex; } ret = ISO_SUCCESS; ex:; LIBISO_FREE_MEM(pad); return ret; } static int zero_writer_free_data(IsoImageWriter *writer) { if (writer == NULL) return ISO_SUCCESS; if (writer->data == NULL) return ISO_SUCCESS; free(writer->data); writer->data = NULL; return ISO_SUCCESS; } static int tail_writer_compute_data_blocks(IsoImageWriter *writer) { int ret; Ecma119Image *target; #ifdef Libisofs_part_align_writeR target = writer->target; #else struct iso_zero_writer_data_struct *data; char msg[80]; target = writer->target; ret = iso_align_isohybrid(target, 0); if (ret < 0) return ret; data = (struct iso_zero_writer_data_struct *) writer->data; if (data->num_blocks != target->opts->tail_blocks) { sprintf(msg, "Aligned image size to cylinder size by %d blocks", target->opts->tail_blocks - data->num_blocks); iso_msgs_submit(0, msg, 0, "NOTE", 0); data->num_blocks = target->opts->tail_blocks; } #endif /* ! Libisofs_part_align_writeR */ if (target->opts->tail_blocks <= 0) return ISO_SUCCESS; ret = zero_writer_compute_data_blocks(writer); return ret; } static int part_align_writer_compute_data_blocks(IsoImageWriter *writer) { int ret; Ecma119Image *target; struct iso_zero_writer_data_struct *data; char msg[80]; target = writer->target; /* Default setting in case no alignment is needed */ target->alignment_end_block = target->curblock; ret = iso_align_isohybrid(target, 0); if (ret < 0) return ret; data = (struct iso_zero_writer_data_struct *) writer->data; if (target->part_align_blocks != 0) { sprintf(msg, "Aligned image size to cylinder size by %d blocks", target->part_align_blocks); iso_msgs_submit(0, msg, 0, "NOTE", 0); data->num_blocks = target->part_align_blocks; } if (target->part_align_blocks <= 0) return ISO_SUCCESS; ret = zero_writer_compute_data_blocks(writer); target->alignment_end_block = target->curblock; return ret; } /* @param flag bit0-3= compute_data_blocks mode: 0= zero_writer_compute_data_blocks 1= tail_writer_compute_data_blocks 2= part_align_writer_compute_data_blocks */ static int zero_writer_create(Ecma119Image *target, uint32_t num_blocks, int flag) { IsoImageWriter *writer; struct iso_zero_writer_data_struct *data; int mode; writer = malloc(sizeof(IsoImageWriter)); if (writer == NULL) { return ISO_OUT_OF_MEM; } data = calloc(1, sizeof(struct iso_zero_writer_data_struct)); if (data == NULL) { free(writer); return ISO_OUT_OF_MEM; } data->num_blocks = num_blocks; mode = (flag & 15); if (mode == 1) { writer->compute_data_blocks = tail_writer_compute_data_blocks; } else if (mode == 2) { writer->compute_data_blocks = part_align_writer_compute_data_blocks; } else { writer->compute_data_blocks = zero_writer_compute_data_blocks; } writer->write_vol_desc = zero_writer_write_vol_desc; writer->write_data = zero_writer_write_data; writer->free_data = zero_writer_free_data; writer->data = data; writer->target = target; /* add this writer to image */ target->writers[target->nwriters++] = writer; return ISO_SUCCESS; } /* @param flag bit0= restore preserved cx (else dispose them) */ static int process_preserved_cx(IsoDir *dir, int flag) { int ret, i; unsigned int cx_value; void *xipt; IsoNode *pos; pos = dir->children; for (pos = dir->children; pos != NULL; pos = pos->next) { if (pos->type == LIBISO_FILE) { if (flag & 1) { /* Restore preserved cx state of nodes */ ret = iso_node_get_xinfo(pos, checksum_cx_xinfo_func, &xipt); if (ret == 1) { /* xipt is an int disguised as void pointer */ cx_value = 0; for (i = 0; i < 4; i++) cx_value = (cx_value << 8) | ((unsigned char *) &xipt)[i]; ret = iso_file_set_isofscx((IsoFile *) pos, cx_value, 0); if (ret < 0) return ret; } else if (ret == 0) { /* Node had no cx before the write run. Delete cx. */ iso_file_set_isofscx((IsoFile *) pos, 0, 1); } } iso_node_remove_xinfo(pos, checksum_cx_xinfo_func); } else if (pos->type == LIBISO_DIR) { ret = process_preserved_cx((IsoDir *) pos, flag); if (ret != 0) return ret; } } return 0; } static int transplant_checksum_buffer(Ecma119Image *target, int flag) { /* Transplant checksum buffer from Ecma119Image to IsoImage */ iso_image_set_checksums(target->image, target->checksum_buffer, target->checksum_range_start, target->checksum_array_pos, target->checksum_idx_counter + 2, 0); target->checksum_buffer = NULL; target->checksum_idx_counter = 0; /* Delete recorded cx xinfo */ process_preserved_cx(target->image->root, 0); return 1; } static int write_vol_desc_terminator(Ecma119Image *target) { int ret; uint8_t *buf = NULL; struct ecma119_vol_desc_terminator *vol; LIBISO_ALLOC_MEM(buf, uint8_t, BLOCK_SIZE); vol = (struct ecma119_vol_desc_terminator *) buf; vol->vol_desc_type[0] = 255; memcpy(vol->std_identifier, "CD001", 5); vol->vol_desc_version[0] = 1; ret = iso_write(target, buf, BLOCK_SIZE); ex: LIBISO_FREE_MEM(buf); return ret; } /* @param flag bit0= initialize system area by target->opts_overwrite bit1= fifo is not yet draining. Inquire write_count from fifo. bit2= target->opts->ms_block is not counted in target->total_size */ static int write_head_part1(Ecma119Image *target, int *write_count, int flag) { int res, i, ret; uint8_t *sa, *sa_local = NULL; IsoImageWriter *writer; size_t buffer_size = 0, buffer_free = 0, buffer_start_free = 0; if (target->sys_area_already_written) { LIBISO_ALLOC_MEM(sa_local, uint8_t, 16 * BLOCK_SIZE); sa = sa_local; } else { sa = target->sys_area_as_written; target->sys_area_already_written = 1; } iso_ring_buffer_get_buf_status(target->buffer, &buffer_size, &buffer_start_free); *write_count = 0; /* Write System Area (ECMA-119, 6.2.1) */ if ((flag & 1) && target->opts_overwrite != NULL) memcpy(sa, target->opts_overwrite, 16 * BLOCK_SIZE); res = iso_write_system_area(target, sa, (flag & 4) >> 2); if (res < 0) goto write_error; res = iso_write(target, sa, 16 * BLOCK_SIZE); if (res < 0) goto write_error; *write_count = 16; /* write volume descriptors, one per writer */ iso_msg_debug(target->image->id, "Write volume descriptors"); for (i = 0; i < (int) target->nwriters; ++i) { writer = target->writers[i]; res = writer->write_vol_desc(writer); if (res < 0) goto write_error; } /* write Volume Descriptor Set Terminator (ECMA-119, 8.3) */ res = write_vol_desc_terminator(target); if (res < 0) goto write_error; if(flag & 2) { iso_ring_buffer_get_buf_status(target->buffer, &buffer_size, &buffer_free); *write_count = ( buffer_start_free - buffer_free ) / BLOCK_SIZE; } else { *write_count = target->bytes_written / BLOCK_SIZE; } ret = ISO_SUCCESS; goto ex; write_error:; ret = res; goto ex; ex: LIBISO_FREE_MEM(sa_local); return ret; } static int write_head_part2(Ecma119Image *target, int *write_count, int flag) { int ret, i; uint8_t *buf = NULL; IsoImageWriter *writer; if (target->opts->partition_offset <= 0) {ret = ISO_SUCCESS; goto ex;} /* Write multi-session padding up to target->opts->partition_offset + 16 */ LIBISO_ALLOC_MEM(buf, uint8_t, BLOCK_SIZE); for(; *write_count < (int) target->opts->partition_offset + 16; (*write_count)++) { ret = iso_write(target, buf, BLOCK_SIZE); if (ret < 0) goto ex; } /* Write volume descriptors subtracting target->partiton_offset from any LBA pointer. */ target->eff_partition_offset = target->opts->partition_offset; target->pvd_size_is_total_size = 0; for (i = 0; i < (int) target->nwriters; ++i) { writer = target->writers[i]; /* Not all writers have an entry in the partition volume descriptor set. It must be guaranteed that they write exactly one block. */ /* >>> TWINTREE: Enhance ISO1999 writer and add it here */ if(writer->write_vol_desc != ecma119_writer_write_vol_desc && writer->write_vol_desc != joliet_writer_write_vol_desc) continue; ret = writer->write_vol_desc(writer); if (ret < 0) goto ex; (*write_count)++; } ret = write_vol_desc_terminator(target); if (ret < 0) goto ex; (*write_count)++; target->eff_partition_offset = 0; /* >>> TWINTREE: Postponed for now: Write second superblock checksum tag */; ret = ISO_SUCCESS; ex:; if (buf != NULL) free(buf); return ret; } static int write_head_part(Ecma119Image *target, int flag) { int res, write_count = 0; /* System area and volume descriptors */ res = write_head_part1(target, &write_count, 4); if (res < 0) return res; /* Write superblock checksum tag */ if (target->opts->md5_session_checksum && target->checksum_ctx != NULL) { res = iso_md5_write_tag(target, 2); if (res < 0) return res; write_count++; } /* Second set of system area and volume descriptors for partition_offset */ res = write_head_part2(target, &write_count, 0); if (res < 0) return res; return ISO_SUCCESS; } /* Eventually end Jigdo Template Extraction */ static int finish_libjte(Ecma119Image *target) { #ifdef Libisofs_with_libjtE int ret; if (target->opts->libjte_handle != NULL) { ret = libjte_write_footer(target->opts->libjte_handle); if (ret <= 0) { iso_libjte_forward_msgs(target->opts->libjte_handle, target->image->id, ISO_LIBJTE_END_FAILED, 0); return ISO_LIBJTE_END_FAILED; } } #endif /* Libisofs_with_libjtE */ return 1; } struct iso_interval_zeroizer { int z_type; /* 0= $zero_start"-"$zero_end , 1= "zero_mbrpt" , 2= "zero_gpt" , 3= "zero_apm" */ off_t zero_start; off_t zero_end; }; struct iso_interval_reader { /* Setup */ IsoImage *image; char *path; int flags; /* bit0= imported_iso, else local_fs */ off_t start_byte; off_t end_byte; struct iso_interval_zeroizer *zeroizers; int num_zeroizers; char *source_pt; /* This is a parasite pointer of path. Do not free */ /* State information */ int initialized; int is_block_aligned; off_t cur_block; int fd; uint8_t read_buf[BLOCK_SIZE]; uint8_t *pending_read_pt; int pending_read_bytes; off_t read_count; int eof; int src_is_open; uint32_t apm_block_size; }; static int iso_ivr_next_comp(char *read_pt, char **next_pt, int flag) { *next_pt = NULL; if (read_pt == NULL) return 0; *next_pt = strchr(read_pt, ':'); if (*next_pt != NULL) (*next_pt)++; return 1; } /* @param flag bit1= end number requested, forward to iso_scanf_io_size() */ static int iso_ivr_read_number(char *start_pt, char *end_pt, off_t *result, int flag) { char txt[20]; off_t num; if (end_pt - start_pt <= 0 || end_pt - start_pt > 16) { iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, "Number text too short or too long in interval reader description string"); return ISO_MALFORMED_READ_INTVL; } if (end_pt - start_pt > 0) strncpy(txt, start_pt, end_pt - start_pt); txt[end_pt - start_pt] = 0; num = iso_scanf_io_size(start_pt, 1 | (flag & 2)); if (num < 0.0 || num > 281474976710655.0) { iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, "Negative or overly large number in interval reader description string"); return ISO_MALFORMED_READ_INTVL; } *result = num; return 1; } static int iso_ivr_parse_interval(char *start_pt, char *end_pt, off_t *start_byte, off_t *end_byte, int flag) { int ret; char *m_pt; m_pt = strchr(start_pt, '-'); if (m_pt == NULL) { iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, "Malformed byte interval in interval reader description string"); return ISO_MALFORMED_READ_INTVL; } ret = iso_ivr_read_number(start_pt, m_pt, start_byte, 0); if (ret < 0) return ret; ret = iso_ivr_read_number(m_pt + 1, end_pt - 1, end_byte, 2); if (ret < 0) return ret; return ISO_SUCCESS; } static int iso_ivr_parse_zeroizers(struct iso_interval_reader *ivr, char *pathpt, char *end_pt, int flag) { int ret, num_zs = 1, idx, i; char *rpt, *cpt; ivr->num_zeroizers = 0; if (pathpt[0] == 0 || pathpt == end_pt) return ISO_SUCCESS; for(cpt = pathpt - 1; cpt != NULL && cpt < end_pt; num_zs++) cpt = strchr(cpt + 1, ','); LIBISO_ALLOC_MEM(ivr->zeroizers, struct iso_interval_zeroizer, num_zs); for (i = 0; i < num_zs; i++) ivr->zeroizers[i].zero_end = -1; idx = 0; for (rpt = pathpt; rpt != NULL && rpt < end_pt; idx++) { cpt = strchr(rpt, ','); if (cpt == NULL || cpt > end_pt) cpt = end_pt; if (cpt == rpt) { continue; } else if (strncmp(rpt, "zero_mbrpt", cpt - rpt) == 0) { ivr->zeroizers[idx].z_type = 1; } else if (strncmp(rpt, "zero_gpt", cpt - rpt) == 0) { ivr->zeroizers[idx].z_type = 2; } else if (strncmp(rpt, "zero_apm", cpt - rpt) == 0) { ivr->zeroizers[idx].z_type = 3; } else { ivr->zeroizers[idx].z_type = 0; ret = iso_ivr_parse_interval(rpt, cpt, &(ivr->zeroizers[idx].zero_start), &(ivr->zeroizers[idx].zero_end), 0); if (ret < 0) goto ex; } rpt = cpt + 1; ivr->num_zeroizers++; } ret = ISO_SUCCESS; ex:; return ret; } static int iso_ivr_parse(struct iso_interval_reader *ivr, char *path, int flag) { int ret; char *flags_pt, *interval_pt, *zeroize_pt; flags_pt = path; iso_ivr_next_comp(flags_pt, &interval_pt, 0); iso_ivr_next_comp(interval_pt, &zeroize_pt, 0); iso_ivr_next_comp(zeroize_pt, &(ivr->source_pt), 0); if (ivr->source_pt == NULL) { iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, "Not enough components in interval reader description string"); return ISO_MALFORMED_READ_INTVL; } ivr->flags = 0; if (strncmp(flags_pt, "imported_iso", 12) == 0) { ivr->flags |= 1; } else if (strncmp(flags_pt, "local_fs", 8) == 0) { ; } else { iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, "Unknown flag name in first component of interval reader description string"); return ISO_MALFORMED_READ_INTVL; } ret = iso_ivr_parse_interval(interval_pt, zeroize_pt, &(ivr->start_byte), &(ivr->end_byte), 0); if (ret < 0) goto ex; ret = iso_ivr_parse_zeroizers(ivr, zeroize_pt, ivr->source_pt - 1, 0); if (ret < 0) goto ex; ret = ISO_SUCCESS; ex:; return ret; } int iso_interval_reader_destroy(struct iso_interval_reader **ivr, int flag) { struct iso_interval_reader *o; if (*ivr == NULL) return 0; o = *ivr; LIBISO_FREE_MEM(o->path); LIBISO_FREE_MEM(o->zeroizers); if (o->fd != -1) close(o->fd); if (o->src_is_open) (*o->image->import_src->close)(o->image->import_src); LIBISO_FREE_MEM(*ivr); return ISO_SUCCESS; } /* @param flag bit0= tolerate lack of import_src */ int iso_interval_reader_new(IsoImage *img, char *path, struct iso_interval_reader **ivr, off_t *byte_count, int flag) { int ret, no_img = 0; struct iso_interval_reader *o = NULL; *ivr = NULL; *byte_count = 0; LIBISO_ALLOC_MEM(o, struct iso_interval_reader, 1); o->image = img; o->path = NULL; o->zeroizers = NULL; o->num_zeroizers = 0; o->source_pt = NULL; o->initialized = 0; o->is_block_aligned = 0; o->fd = -1; o->pending_read_pt = NULL; o->pending_read_bytes = 0; o->eof = 0; o->read_count = 0; o->src_is_open = 0; o->apm_block_size = 0; LIBISO_ALLOC_MEM(o->path, char, strlen(path) + 1); strcpy(o->path, path); ret = iso_ivr_parse(o, path, 0); if (ret < 0) goto ex; if (o->image == NULL) no_img = 1; else if (o->image->import_src == NULL) no_img = 1; if ((o->flags & 1) && no_img) { iso_msg_submit(-1, ISO_NO_KEPT_DATA_SRC, 0, "Interval reader lacks of data source object of imported ISO"); if (!(flag & 1)) { ret = ISO_BAD_PARTITION_FILE; goto ex; } o->eof = 1; } *byte_count = o->end_byte - o->start_byte + 1; *ivr = o; ret = ISO_SUCCESS; ex:; if (ret < 0) iso_interval_reader_destroy(&o, 0); return ret; } static int iso_ivr_zeroize(struct iso_interval_reader *ivr, uint8_t *buf, int buf_fill, int flag) { int i; off_t low, high, part_start, entry_count, apm_offset = -1, map_entries; uint8_t *apm_buf; struct iso_interval_zeroizer *zr; for (i = 0; i < ivr->num_zeroizers; i++) { zr = ivr->zeroizers + i; if (zr->z_type == 1) { /* zero_mbrpt */ if (ivr->read_count > 0 || buf_fill < 512) continue; if (buf[510] != 0x55 || buf[511] != 0xaa) continue; memset(buf + 446, 0, 64); } else if (zr->z_type == 2) { /* zero_gpt */ if (zr->zero_start <= zr->zero_end) goto process_interval; if (ivr->read_count > 0 || buf_fill < 512 + 92) continue; if (strncmp((char *) buf + 512, "EFI PART", 8) != 0 || buf[520] != 0 || buf[521] != 0 || buf[522] != 1 || buf[523] != 0) continue; /* head_size , curr_lba , entry_size */ if (iso_read_lsb(buf + 524, 4) != 92 || iso_read_lsb(buf + 536, 4) != 1 || iso_read_lsb(buf + 596, 4) != 128) continue; part_start = iso_read_lsb(buf + 584, 4); entry_count = iso_read_lsb(buf + 592, 4); if (part_start < 2 || part_start + (entry_count + 3) / 4 > 64) continue; zr->zero_start = part_start * 512; zr->zero_end = (part_start + (entry_count + 3) / 4) * 512 - 1; memset(buf + 512, 0, 92); } else if (zr->z_type == 3) { /* zero_apm */ if (zr->zero_start <= zr->zero_end) goto process_interval; if (ivr->read_count == 0) { if (buf_fill < 512) continue; if (buf[0] != 'E' || buf[1] != 'R') continue; ivr->apm_block_size = iso_read_msb(buf + 2, 2); if ((ivr->apm_block_size != 512 && ivr->apm_block_size != 1024 && ivr->apm_block_size != 2048) || ((uint32_t) buf_fill) < ivr->apm_block_size) { ivr->apm_block_size = 0; continue; } if (ivr->read_count + buf_fill >= 2 * ivr->apm_block_size) apm_offset = ivr->apm_block_size; } else if (ivr->read_count == 2048 && ivr->apm_block_size == 2048 && buf_fill == 2048) { apm_offset = 0; } if (apm_offset < 0) continue; /* Check for first APM entry */ apm_buf = buf + apm_offset; if(apm_buf[0] != 'P' || apm_buf[1] != 'M') continue; if (iso_read_msb(apm_buf + 8, 4) != 1) continue; map_entries = iso_read_msb(apm_buf + 4, 4); if ((1 + map_entries) * ivr->apm_block_size > 16 * 2048) continue; zr->zero_start = ivr->apm_block_size; zr->zero_end = (1 + map_entries) * ivr->apm_block_size; } process_interval:; /* If an interval is defined by now: zeroize its intersection with buf */ if (zr->zero_start <= zr->zero_end) { low = ivr->read_count >= zr->zero_start ? ivr->read_count : zr->zero_start; high = ivr->read_count + buf_fill - 1 <= zr->zero_end ? ivr->read_count + buf_fill - 1 : zr->zero_end; if (low <= high) memset(buf + low - ivr->read_count, 0, high - low + 1); } } return ISO_SUCCESS; } int iso_interval_reader_read(struct iso_interval_reader *ivr, uint8_t *buf, int *buf_fill, int flag) { int ret, read_done, to_copy, initializing = 0; IsoDataSource *src; uint8_t *read_buf; off_t to_read; *buf_fill = 0; src = ivr->image->import_src; if (ivr->eof) { eof:; memset(buf, 0, BLOCK_SIZE); return 0; } if (ivr->initialized) { ivr->cur_block++; } else { initializing = 1; ivr->cur_block = ivr->start_byte / BLOCK_SIZE; ivr->is_block_aligned = !(ivr->start_byte % BLOCK_SIZE); if (ivr->flags & 1) { if (src == NULL) goto eof; ret = (*src->open)(src); if (ret < 0) { ivr->eof = 1; return ret; } ivr->src_is_open = 1; } else { ivr->fd = open(ivr->source_pt, O_RDONLY); if (ivr->fd == -1) { iso_msg_submit(-1, ISO_BAD_PARTITION_FILE, 0, "Cannot open local file for interval reading"); ivr->eof = 1; return ISO_BAD_PARTITION_FILE; } if (ivr->cur_block != 0) { if (lseek(ivr->fd, ivr->cur_block * BLOCK_SIZE, SEEK_SET) == -1) { iso_msg_submit(-1, ISO_INTVL_READ_PROBLEM, 0, "Cannot address interval start in local file"); ivr->eof = 1; goto eof; } } } ivr->initialized = 1; } if (ivr->is_block_aligned) { read_buf = buf; } else { process_pending:; read_buf = ivr->read_buf; /* Copy pending bytes from previous read */ if (ivr->pending_read_bytes > 0) { memcpy(buf, ivr->pending_read_pt, ivr->pending_read_bytes); *buf_fill = ivr->pending_read_bytes; ivr->pending_read_bytes = 0; } } /* Read next block */ read_done = 0; if (ivr->cur_block * BLOCK_SIZE <= ivr->end_byte) { if (ivr->flags & 1) { ret = (*src->read_block)(src, (uint32_t) ivr->cur_block, read_buf); if (ret < 0) { if (iso_error_get_severity(ret) > 0x68000000) /* > FAILURE */ return ret; iso_msg_submit(-1, ISO_INTVL_READ_PROBLEM, 0, "Premature EOF while interval reading from imported ISO"); ivr->eof = 1; } read_done = BLOCK_SIZE; } else { read_done = 0; to_read = ivr->end_byte - ivr->start_byte + 1 - ivr->read_count; if (to_read > BLOCK_SIZE) to_read = BLOCK_SIZE; while (read_done < to_read) { ret = read(ivr->fd, read_buf, to_read - read_done); if (ret == -1) { iso_msg_submit(-1, ISO_INTVL_READ_PROBLEM, 0, "Read error while interval reading from local file"); ivr->eof = 1; break; } else if (ret == 0) { iso_msg_submit(-1, ISO_INTVL_READ_PROBLEM, 0, "Premature EOF while interval reading from local file"); ivr->eof = 1; break; } else read_done += ret; } } } if (ivr->is_block_aligned) { *buf_fill = read_done; } else if (initializing) { ivr->pending_read_pt = ivr->read_buf + (ivr->start_byte - ivr->cur_block * BLOCK_SIZE); ivr->pending_read_bytes = (((off_t) ivr->cur_block) + 1) * BLOCK_SIZE - ivr->start_byte; initializing = 0; goto process_pending; } else if (read_done > 0) { /* Copy bytes from new read */ to_copy = read_done > BLOCK_SIZE - *buf_fill ? BLOCK_SIZE - *buf_fill : read_done; memcpy(buf + *buf_fill, ivr->read_buf, to_copy); *buf_fill += to_copy; ivr->pending_read_pt = ivr->read_buf + to_copy; ivr->pending_read_bytes = read_done - to_copy; } if (ivr->start_byte + ivr->read_count + *buf_fill - 1 > ivr->end_byte) { *buf_fill = ivr->end_byte - ivr->start_byte + 1 - ivr->read_count; ivr->eof = 1; } if (*buf_fill < BLOCK_SIZE) memset(buf + *buf_fill, 0, BLOCK_SIZE - *buf_fill); ret = iso_ivr_zeroize(ivr, buf, *buf_fill, 0); if (ret < 0) return ret; ivr->read_count += *buf_fill; return ISO_SUCCESS; } /* Tells whether ivr is a reader from imported_iso in a multi-session add-on situation, and thus to be kept in place. */ int iso_interval_reader_keep(Ecma119Image *target, struct iso_interval_reader *ivr, int flag) { /* Source must be "imported_iso" */ if (!(ivr->flags & 1)) return 0; /* It must not be a new ISO */ if (!target->opts->appendable) return 0; /* --- From here on return either 1 or <0 --- */ /* multi-session write offset must be larger than interval end */ if (target->opts->ms_block <= ivr->end_byte / BLOCK_SIZE) return ISO_MULTI_OVER_IMPORTED; return 1; } int iso_interval_reader_start_size(Ecma119Image *t, char *path, off_t *start_byte, off_t *byte_count, int flag) { struct iso_interval_reader *ivr; int keep, ret; ret = iso_interval_reader_new(t->image, path, &ivr, byte_count, 0); if (ret < 0) return ret; *start_byte = ivr->start_byte; keep = iso_interval_reader_keep(t, ivr, 0); if (keep < 0) return(keep); iso_interval_reader_destroy(&ivr, 0); return ISO_SUCCESS + (keep > 0); } int iso_write_partition_file(Ecma119Image *target, char *path, uint32_t prepad, uint32_t blocks, int flag) { struct iso_interval_reader *ivr = NULL; int buf_fill; off_t byte_count; FILE *fp = NULL; uint32_t i; uint8_t *buf = NULL; int ret; LIBISO_ALLOC_MEM(buf, uint8_t, BLOCK_SIZE); for (i = 0; i < prepad; i++) { ret = iso_write(target, buf, BLOCK_SIZE); if (ret < 0) goto ex; } if (flag & 1) { ret = iso_interval_reader_new(target->image, path, &ivr, &byte_count, 0); if (ret < 0) goto ex; ret = iso_interval_reader_keep(target, ivr, 0); if (ret < 0) goto ex; if (ret > 0) { /* From imported_iso and for add-on session. Leave it in place. */ ret = ISO_SUCCESS; goto ex; } for (i = 0; i < blocks; i++) { ret = iso_interval_reader_read(ivr, buf, &buf_fill, 0); if (ret < 0) goto ex; ret = iso_write(target, buf, BLOCK_SIZE); if (ret < 0) goto ex; } } else { fp = fopen(path, "rb"); if (fp == NULL) {ret = ISO_BAD_PARTITION_FILE; goto ex;} for (i = 0; i < blocks; i++) { memset(buf, 0, BLOCK_SIZE); if (fp != NULL) { ret = fread(buf, 1, BLOCK_SIZE, fp); if (ret != BLOCK_SIZE) { fclose(fp); fp = NULL; } } ret = iso_write(target, buf, BLOCK_SIZE); if (ret < 0) { if (fp != NULL) fclose(fp); goto ex; } } if (fp != NULL) fclose(fp); } ret = ISO_SUCCESS; ex:; iso_interval_reader_destroy(&ivr, 0); LIBISO_FREE_MEM(buf); return ret; } void issue_ucs2_warning_summary(size_t failures) { if (failures > ISO_JOLIET_UCS2_WARN_MAX) { iso_msg_submit(-1, ISO_NAME_NOT_UCS2, 0, "More filenames found which were not suitable for Joliet character set UCS-2"); } if (failures > 0) { iso_msg_submit(-1, ISO_NAME_NOT_UCS2, 0, "Sum of filenames not suitable for Joliet character set UCS-2: %.f", (double) failures); } } static void *write_function(void *arg) { int res, i; #ifndef Libisofs_appended_partitions_inlinE int first_partition = 1, last_partition = 0; #endif IsoImageWriter *writer; Ecma119Image *target = (Ecma119Image*)arg; iso_msg_debug(target->image->id, "Starting image writing..."); target->bytes_written = (off_t) 0; target->percent_written = 0; res = write_head_part(target, 0); if (res < 0) goto write_error; /* write data for each writer */ for (i = 0; i < (int) target->nwriters; ++i) { writer = target->writers[i]; if (target->gpt_backup_outside && writer->write_vol_desc == gpt_tail_writer_write_vol_desc) continue; res = writer->write_data(writer); if (res < 0) { goto write_error; } } #ifndef Libisofs_appended_partitions_inlinE /* Append partition data */ iso_count_appended_partitions(target, &first_partition, &last_partition); for (i = first_partition - 1; i <= last_partition - 1; i++) { if (target->opts->appended_partitions[i] == NULL) continue; if (target->opts->appended_partitions[i][0] == 0) continue; res = iso_write_partition_file(target, target->opts->appended_partitions[i], target->appended_part_prepad[i], target->appended_part_size[i], target->opts->appended_part_flags[i] & 1); if (res < 0) goto write_error; } #endif /* ! Libisofs_appended_partitions_inlinE */ if (target->gpt_backup_outside) { for (i = 0; i < (int) target->nwriters; ++i) { writer = target->writers[i]; if (writer->write_vol_desc != gpt_tail_writer_write_vol_desc) continue; res = writer->write_data(writer); if (res < 0) goto write_error; } } /* Transplant checksum buffer from Ecma119Image to IsoImage */ transplant_checksum_buffer(target, 0); iso_ring_buffer_writer_close(target->buffer, 0); res = finish_libjte(target); if (res <= 0) goto write_error; issue_ucs2_warning_summary(target->joliet_ucs2_failures); target->image->generator_is_running = 0; /* Give up reference claim made in ecma119_image_new(). Eventually free target */ ecma119_image_free(target); if (target->tree_end_block == 1) { iso_msgs_submit(0, "Image is most likely damaged. Calculated/written tree end address mismatch.", 0, "FATAL", 0); } if (target->bytes_written != target->total_size) { iso_msg_debug(target->image->id, "bytes_written = %.f <-> total_size = %.f", (double) target->bytes_written, (double) target->total_size); iso_msgs_submit(0, "Image is most likely damaged. Calculated/written image end address mismatch.", 0, "FATAL", 0); } #ifdef Libisofs_with_pthread_exiT pthread_exit(NULL); #else return NULL; #endif write_error: ; if (res != (int) ISO_LIBJTE_END_FAILED) finish_libjte(target); target->eff_partition_offset = 0; if (res == (int) ISO_CANCELED) { /* canceled */ if (!target->opts->will_cancel) iso_msg_submit(target->image->id, ISO_IMAGE_WRITE_CANCELED, 0, NULL); } else { /* image write error */ iso_msg_submit(target->image->id, ISO_WRITE_ERROR, res, "Image write error"); } iso_ring_buffer_writer_close(target->buffer, 1); /* Re-activate recorded cx xinfo */ process_preserved_cx(target->image->root, 1); target->image->generator_is_running = 0; /* Give up reference claim made in ecma119_image_new(). Eventually free target */ ecma119_image_free(target); #ifdef Libisofs_with_pthread_exiT pthread_exit(NULL); #else return NULL; #endif } static int checksum_prepare_image(IsoImage *src, int flag) { int ret; /* Set provisory value of isofs.ca with 4 byte LBA, 4 byte count, size 16, name MD5 */ ret = iso_root_set_isofsca((IsoNode *) src->root, 0, 0, 0, 16, "MD5", 0); if (ret < 0) return ret; return ISO_SUCCESS; } /* @flag bit0= recursion */ static int checksum_prepare_nodes(Ecma119Image *target, IsoNode *node, int flag) { IsoNode *pos; IsoFile *file; IsoImage *img; int ret, i, no_md5 = 0, has_xinfo = 0, has_attr = 0; size_t old_cx_value_length = 0; unsigned int idx = 0; char *old_cx_value= NULL; void *xipt = NULL; img= target->image; if (node->type == LIBISO_FILE) { file = (IsoFile *) node; if (file->from_old_session) { /* Record attribute isofs.cx as xinfo before it can get overwritten for the emerging image. The recorded index will be used to retrieve the loaded MD5 and it will be brought back into effect if cancellation of image production prevents that the old MD5 array gets replaced by the new one. */ has_attr = iso_node_lookup_attr(node, "isofs.cx", &old_cx_value_length, &old_cx_value, 0); if (has_attr == 1 && old_cx_value_length == 4) { for (i = 0; i < 4; i++) idx = (idx << 8) | ((unsigned char *) old_cx_value)[i]; if (idx > 0 && idx < 0x8000000) { /* xipt is an int disguised as void pointer */ for (i = 0; i < 4; i++) ((char *) &xipt)[i] = old_cx_value[i]; ret = iso_node_add_xinfo(node, checksum_cx_xinfo_func, xipt); if (ret < 0) goto ex; } else no_md5 = 1; } } if (file->from_old_session && target->opts->appendable) { /* Save MD5 data of files from old image which will not be copied and have an MD5 recorded in the old image. */ has_xinfo = iso_node_get_xinfo(node, checksum_md5_xinfo_func, &xipt); if (has_xinfo > 0) { /* xinfo MD5 overrides everything else unless data get copied and checksummed during that copying */; } else if (has_attr == 1 && img->checksum_array == NULL) { /* No checksum array loaded. Delete "isofs.cx" */ if (!target->opts->will_cancel) iso_file_set_isofscx((IsoFile *) node, 0, 1); no_md5 = 1; } else if (!(has_attr == 1 && old_cx_value_length == 4)) { no_md5 = 1; } } /* Equip nodes with provisory isofs.cx numbers: 4 byte, all 0. Omit those from old image which will not be copied and have no MD5. Do not alter the nodes if this is only a will_cancel run. */ if (!(target->opts->will_cancel || no_md5)) { /* Record provisory new index */ ret = iso_file_set_isofscx(file, (unsigned int) 0, 0); if (ret < 0) goto ex; } } else if (node->type == LIBISO_DIR) { for (pos = ((IsoDir *) node)->children; pos != NULL; pos = pos->next) { ret = checksum_prepare_nodes(target, pos, 1); if (ret < 0) goto ex; } } ret = ISO_SUCCESS; ex:; if (old_cx_value != NULL) free(old_cx_value); return ret; } /* Determine the alleged time of image production by predicting the volume creation and modification timestamps and taking the maximum of both. */ static void ecma119_determine_now_time(Ecma119Image *target) { IsoWriteOpts *o; time_t now = 0, t, t0; uint8_t time_text[18]; int i; iso_nowtime(&t0, 0); o = target->opts; if (o->vol_uuid[0]) { for(i = 0; i < 16; i++) if(o->vol_uuid[i] < '0' || o->vol_uuid[i] > '9') break; else time_text[i] = o->vol_uuid[i]; for(; i < 16; i++) time_text[i] = '1'; time_text[16] = time_text[17] = 0; t = iso_datetime_read_17(time_text); if (t > now) now = t; } else { if (o->vol_creation_time > 0) { if (o->vol_creation_time > now) now = o->vol_creation_time; } else if (t0 > now) { now = t0; } if (o->vol_modification_time > 0) { if (o->vol_modification_time > now) now = o->vol_modification_time; } else if (t0 > now) { now = t0; } } target->now = now; } static int gpt_disk_guid_setup(Ecma119Image *target) { if (target->opts->gpt_disk_guid_mode == 0) { /* Random UUID production delayed until really needed */ return ISO_SUCCESS; } else if (target->opts->gpt_disk_guid_mode == 1) { memcpy(target->gpt_uuid_base, target->opts->gpt_disk_guid, 16); } else if (target->opts->gpt_disk_guid_mode == 2) { if (target->opts->vol_uuid[0] == 0) return ISO_GPT_NO_VOL_UUID; /* Move centi-seconds part to byte 9 and 10 */ memcpy(target->gpt_uuid_base, target->opts->vol_uuid, 9); memcpy(target->gpt_uuid_base + 9, target->opts->vol_uuid + 14, 2); memcpy(target->gpt_uuid_base + 11, target->opts->vol_uuid + 9, 5); iso_mark_guid_version_4(target->gpt_uuid_base); } else { return ISO_BAD_GPT_GUID_MODE; } memcpy(target->gpt_disk_guid, target->gpt_uuid_base, 16); target->gpt_disk_guid_set = 1; target->gpt_uuid_counter = 1; return ISO_SUCCESS; } static int ecma119_image_new(IsoImage *src, IsoWriteOpts *in_opts, Ecma119Image **img) { int ret, i, voldesc_size, nwriters, tag_pos; int sa_type; Ecma119Image *target; IsoWriteOpts *opts; IsoImageWriter *writer; int file_src_writer_index = -1; int system_area_options = 0; char *system_area = NULL; int write_count = 0, write_count_mem; uint32_t vol_space_size_mem; off_t total_size_mem; #ifdef Libisofs_appended_partitions_inlinE int fap, lap, app_part_count; #endif /* 1. Allocate target and attach a copy of in_opts there */ target = calloc(1, sizeof(Ecma119Image)); if (target == NULL) { return ISO_OUT_OF_MEM; } /* This reference will be transferred to the burn_source and released by bs_free_data. */ target->refcount = 1; target->opts = NULL; /* Record a copy of in_opts. It is a copy because in_opts is prone to manipulations from the application thread while the image production thread is running. */ ret = iso_write_opts_clone(in_opts, &(target->opts), 0); if (ret != ISO_SUCCESS) goto target_cleanup; opts = target->opts; /* create the tree for file caching */ ret = iso_rbtree_new(iso_file_src_cmp, &(target->files)); if (ret < 0) { goto target_cleanup; } target->ecma119_hidden_list = NULL; target->image = src; iso_image_ref(src); target->rr_reloc_node = NULL; target->replace_uid = opts->replace_uid ? 1 : 0; target->replace_gid = opts->replace_gid ? 1 : 0; target->replace_dir_mode = opts->replace_dir_mode ? 1 : 0; target->replace_file_mode = opts->replace_file_mode ? 1 : 0; target->uid = opts->replace_uid == 2 ? opts->uid : 0; target->gid = opts->replace_gid == 2 ? opts->gid : 0; target->dir_mode = opts->replace_dir_mode == 2 ? opts->dir_mode : 0555; target->file_mode = opts->replace_file_mode == 2 ? opts->file_mode : 0444; ecma119_determine_now_time(target); target->replace_timestamps = opts->replace_timestamps ? 1 : 0; target->timestamp = opts->replace_timestamps == 2 ? opts->timestamp : target->now; /* el-torito? */ target->eltorito = (src->bootcat == NULL ? 0 : 1); target->catalog = src->bootcat; if (target->catalog != NULL) { target->num_bootsrc = target->catalog->num_bootimages; target->bootsrc = calloc(target->num_bootsrc + 1, sizeof(IsoFileSrc *)); target->boot_appended_idx = calloc(target->num_bootsrc + 1, sizeof(int)); target->boot_intvl_start = calloc(target->num_bootsrc + 1, sizeof(uint32_t)); target->boot_intvl_size = calloc(target->num_bootsrc + 1, sizeof(uint32_t)); if (target->bootsrc == NULL || target->boot_appended_idx == NULL || target->boot_intvl_start == NULL || target->boot_intvl_size == NULL) { ret = ISO_OUT_OF_MEM; goto target_cleanup; } for (i= 0; i < target->num_bootsrc; i++) { target->bootsrc[i] = NULL; target->boot_appended_idx[i] = -1; target->boot_intvl_start[i] = 0; target->boot_intvl_size[i] = 0; } /* It is not easy to predict when the node gets created and can be manipulated. So it is better for reproducibility to derive its timestamps from the well controllable now-time. */ if (target->catalog->node != NULL) { iso_node_set_mtime((IsoNode *) target->catalog->node, target->now); iso_node_set_atime((IsoNode *) target->catalog->node, target->now); iso_node_set_ctime((IsoNode *) target->catalog->node, target->now); } } else { target->num_bootsrc = 0; target->bootsrc = NULL; } if (opts->system_area_data != NULL) { system_area = opts->system_area_data; system_area_options = opts->system_area_options; } else if (src->system_area_data != NULL) { system_area = src->system_area_data; system_area_options = src->system_area_options; } else { system_area_options = opts->system_area_options & 0xfffffffd; } sa_type = (system_area_options >> 2) & 0x3f; if (sa_type != 0 && sa_type != 3) for (i = 0; i < ISO_MAX_PARTITIONS; i++) if (opts->appended_partitions[i] != NULL) { ret = ISO_NON_MBR_SYS_AREA; goto target_cleanup; } if (sa_type != 0 && opts->prep_partition != NULL) { ret = ISO_NON_MBR_SYS_AREA; goto target_cleanup; } target->system_area_data = NULL; if (system_area != NULL) { target->system_area_data = calloc(32768, 1); if (target->system_area_data == NULL) { ret = ISO_OUT_OF_MEM; goto target_cleanup; } memcpy(target->system_area_data, system_area, 32768); } target->system_area_options = system_area_options; target->partition_secs_per_head = opts->partition_secs_per_head; target->partition_heads_per_cyl = opts->partition_heads_per_cyl; if (target->partition_secs_per_head == 0) target->partition_secs_per_head = 32; if (target->partition_heads_per_cyl == 0) target->partition_heads_per_cyl = 64; target->eff_partition_offset = 0; target->partition_root = NULL; target->partition_l_table_pos = 0; target->partition_m_table_pos = 0; target->j_part_root = NULL; target->j_part_l_path_table_pos = 0; target->j_part_m_path_table_pos = 0; target->input_charset = strdup(iso_get_local_charset(0)); if (target->input_charset == NULL) { ret = ISO_OUT_OF_MEM; goto target_cleanup; } if (opts->output_charset != NULL) { target->output_charset = strdup(opts->output_charset); } else { target->output_charset = strdup(target->input_charset); } if (target->output_charset == NULL) { ret = ISO_OUT_OF_MEM; goto target_cleanup; } target->total_size = 0; target->vol_space_size = 0; target->pvd_size_is_total_size = 0; target->checksum_idx_counter = 0; target->checksum_ctx = NULL; target->checksum_counter = 0; target->checksum_rlsb_tag_pos = 0; target->checksum_sb_tag_pos = 0; target->checksum_tree_tag_pos = 0; target->checksum_tag_pos = 0; target->checksum_buffer = NULL; target->checksum_array_pos = 0; target->checksum_range_start = 0; target->checksum_range_size = 0; target->opts_overwrite = NULL; #ifdef Libisofs_with_libjtE /* Eventually start Jigdo Template Extraction */ if (opts->libjte_handle != NULL) { ret = libjte_write_header(opts->libjte_handle); if (ret <= 0) { iso_libjte_forward_msgs(opts->libjte_handle, target->image->id, ISO_LIBJTE_START_FAILED, 0); ret = ISO_LIBJTE_START_FAILED; goto target_cleanup; } } #endif /* Libisofs_with_libjtE */ target->mipsel_e_entry = 0; target->mipsel_p_offset = 0; target->mipsel_p_vaddr = 0; target->mipsel_p_filesz = 0; target->sparc_core_src = NULL; target->empty_file_block = 0; target->tree_end_block = 0; target->wthread_is_running = 0; target->part_align_blocks = 0; target->alignment_end_block = 0; target->post_iso_part_pad = 0; target->prep_part_size = 0; target->efi_boot_part_size = 0; target->efi_boot_part_filesrc = NULL; for (i = 0; i < ISO_MAX_PARTITIONS; i++) { target->appended_part_prepad[i] = 0; target->appended_part_start[i] = target->appended_part_size[i] = 0; } target->have_appended_partitions = 0; for (i = 0; i < ISO_HFSPLUS_BLESS_MAX; i++) { target->hfsplus_blessed[i] = src->hfsplus_blessed[i]; if (target->hfsplus_blessed[i] != NULL) iso_node_ref(target->hfsplus_blessed[i]); } target->hfsp_cat_node_size = 0; target->hfsp_iso_block_fac = 0; target->hfsp_collision_count = 0; iso_setup_hfsplus_block_size(target); target->apm_req_count = 0; target->apm_req_flags = 0; for (i = 0; i < ISO_APM_ENTRIES_MAX; i++) target->apm_req[i] = NULL; for (i = 0; i < ISO_MBR_ENTRIES_MAX; i++) target->mbr_req[i] = NULL; target->mbr_req_count = 0; for (i = 0; i < ISO_GPT_ENTRIES_MAX; i++) target->gpt_req[i] = NULL; target->gpt_req_count = 0; target->gpt_req_flags = 0; target->gpt_backup_outside = 0; memset(target->gpt_uuid_base, 0, 16); target->gpt_uuid_counter = 0; target->gpt_disk_guid_set = 0; ret = gpt_disk_guid_setup(target); if (ret < 0) goto target_cleanup; target->gpt_part_start = 0; target->gpt_backup_end = 0; target->gpt_backup_size = 0; target->gpt_max_entries = 0; target->gpt_is_computed = 0; target->sys_area_already_written = 0; target->filesrc_start = 0; target->filesrc_blocks = 0; target->curr_ce_entries = 0; target->joliet_ucs2_failures = 0; target->joliet_symlinks = 0; target->joliet_specials = 0; target->iso1999_symlinks = 0; target->iso1999_specials = 0; /* If partitions get appended, then the backup GPT cannot be part of the ISO filesystem. */ for (i = 0; i < ISO_MAX_PARTITIONS; i++) if (target->opts->appended_partitions[i] != NULL) { target->gpt_backup_outside = 1; target->have_appended_partitions = 1; break; } ret = iso_root_set_isofsnt((IsoNode *) (src->root), (uint32_t) src->truncate_mode, (uint32_t) src->truncate_length, 0); if (ret < 0) goto target_cleanup; /* * 2. Based on those options, create needed writers: iso, joliet... * Each writer inits its structures and stores needed info into * target. * If the writer needs an volume descriptor, it increments image * current block. * Finally, create Writer for files. */ target->curblock = opts->ms_block + 16; if (opts->overwrite != NULL && opts->ms_block != 0 && opts->ms_block < opts->partition_offset + 32) { /* Not enough room for superblock relocation */ ret = ISO_OVWRT_MS_TOO_SMALL; goto target_cleanup; } /* the number of writers is dependent of the extensions */ nwriters = 1 + 1 + 1; /* ECMA-119 + multi-session padding + files */ if (target->eltorito) { nwriters++; } if (opts->joliet) { nwriters++; } if (opts->iso1999) { nwriters++; } nwriters++; /* Partition Prepend writer */ if (opts->hfsplus || opts->fat) { nwriters+= 2; } #ifdef Libisofs_part_align_writeR nwriters++; /* part_align_blocks writer */ #endif #ifdef Libisofs_appended_partitions_inlinE nwriters++; /* Inline Partition Append Writer */ #endif nwriters++; /* GPT backup tail writer */ nwriters++; /* Tail padding writer */ if ((opts->md5_file_checksums & 1) || opts->md5_session_checksum) { nwriters++; ret = checksum_prepare_image(src, 0); if (ret < 0) goto target_cleanup; if (opts->appendable) { ret = checksum_prepare_nodes(target, (IsoNode *) src->root, 0); if (ret < 0) goto target_cleanup; } target->checksum_idx_counter = 0; } target->writers = malloc(nwriters * sizeof(void*)); if (target->writers == NULL) { ret = ISO_OUT_OF_MEM; goto target_cleanup; } /* create writer for ECMA-119 structure */ ret = ecma119_writer_create(target); if (ret < 0) { goto target_cleanup; } /* create writer for El-Torito */ if (target->eltorito) { ret = eltorito_writer_create(target); if (ret < 0) { goto target_cleanup; } } /* create writer for Joliet structure */ if (opts->joliet) { ret = joliet_writer_create(target); if (ret < 0) { goto target_cleanup; } } /* create writer for ISO 9660:1999 structure */ if (opts->iso1999) { ret = iso1999_writer_create(target); if (ret < 0) { goto target_cleanup; } } voldesc_size = target->curblock - opts->ms_block - 16; /* Volume Descriptor Set Terminator */ target->curblock++; /* All empty files point to the Volume Descriptor Set Terminator * rather than to addresses of non-empty files. */ target->empty_file_block = target->curblock - 1; /* * Create the writer for possible padding to ensure that in case of image * growing we can safely overwrite the first 64 KiB of image. */ ret = mspad_writer_create(target); if (ret < 0) { goto target_cleanup; } /* The writer for MBR and GPT partitions outside iso_file_src. * If PreP or FAT are desired, it creates MBR partition entries and * surrounding protecting partition entries. * If EFI boot partition is desired, it creates a GPT entry for it. */ ret = partprepend_writer_create(target); if (ret < 0) goto target_cleanup; /* create writer for HFS+/FAT structure */ /* Impotant: It must be created directly before iso_file_src_writer_create. */ if (opts->hfsplus || opts->fat) { ret = hfsplus_writer_create(target); if (ret < 0) { goto target_cleanup; } } /* create writer for file contents */ ret = iso_file_src_writer_create(target); if (ret < 0) { goto target_cleanup; } file_src_writer_index = target->nwriters - 1; /* create writer for HFS+ structure */ if (opts->hfsplus || opts->fat) { ret = hfsplus_tail_writer_create(target); if (ret < 0) { goto target_cleanup; } } /* >>> Decide whether the GPT tail writer can be the last of all */ #define Libisofs_gpt_writer_lasT yes #ifndef Libisofs_gpt_writer_lasT /* This writer has to be added to the list after any writer which might request production of APM or GPT partition entries by its compute_data_blocks() method. Its compute_data_blocks() fills the gaps in APM requests. It determines the position of primary GPT and backup GPT. Further it reserves blocks for the backup GPT. */ ret = gpt_tail_writer_create(target); if (ret < 0) goto target_cleanup; #endif /* ! Libisofs_gpt_writer_lasT */ if ((opts->md5_file_checksums & 1) || opts->md5_session_checksum) { ret = checksum_writer_create(target); if (ret < 0) goto target_cleanup; } #ifdef Libisofs_part_align_writeR /* Alignment padding before appended partitions */ ret = zero_writer_create(target, 0, 2); #else ret = zero_writer_create(target, opts->tail_blocks, 1); #endif /* ! Libisofs_part_align_writeR */ if (ret < 0) goto target_cleanup; #ifdef Libisofs_appended_partitions_inlinE ret = partappend_writer_create(target); if (ret < 0) goto target_cleanup; #endif /* Libisofs_appended_partitions_inlinE */ #ifdef Libisofs_part_align_writeR ret = zero_writer_create(target, opts->tail_blocks, 0); if (ret < 0) goto target_cleanup; #endif /* Libisofs_part_align_writeR */ #ifdef Libisofs_gpt_writer_lasT /* This writer shall be the last one in the list of writers of valuable data, because it protects the image on media which are seen as GPT partitioned. In any case it has to come after any writer which might request production of APM or GPT partition entries by its compute_data_blocks() method. gpt_tail_writer_compute_data_blocks() fills the gaps in APM requests. It determines the position of primary GPT and backup GPT. Further it reserves blocks for the backup GPT. */ ret = gpt_tail_writer_create(target); if (ret < 0) goto target_cleanup; #endif /* Libisofs_gpt_writer_lasT */ if (opts->partition_offset > 0) { /* After volume descriptors and superblock tag are accounted for: account for second volset */ if (opts->ms_block + opts->partition_offset + 16 < target->curblock) { /* Overflow of partition system area */ ret = ISO_PART_OFFST_TOO_SMALL; goto target_cleanup; } target->curblock = opts->ms_block + opts->partition_offset + 16; /* Account for partition tree volume descriptors */ for (i = 0; i < (int) target->nwriters; ++i) { /* Not all writers have an entry in the partition volume descriptor set. */ writer = target->writers[i]; /* >>> TWINTREE: Enhance ISO1999 writer and add it here */ if(writer->write_vol_desc != ecma119_writer_write_vol_desc && writer->write_vol_desc != joliet_writer_write_vol_desc) continue; target->curblock++; } target->curblock++; /* + Terminator */ /* >>> TWINTREE: eventually later : second superblock checksum tag */; } /* At least the FAT computation needs to know the size of filesrc data in advance. So this call produces target->filesrc_blocks and relative extent addresses, which get absolute in filesrc_writer_compute_data_blocks(). */ ret = filesrc_writer_pre_compute(target->writers[file_src_writer_index]); if (ret < 0) goto target_cleanup; /* * 3. * Call compute_data_blocks() in each Writer. * That function computes the size needed by its structures and * increments image current block properly. */ for (i = 0; i < (int) target->nwriters; ++i) { IsoImageWriter *writer = target->writers[i]; if (target->gpt_backup_outside && writer->write_vol_desc == gpt_tail_writer_write_vol_desc) continue; /* Exposing address of data start to IsoWriteOpts and memorizing this address for all files which have no block address: symbolic links, device files, empty data files. filesrc_writer_compute_data_blocks() and filesrc_writer_write_data() will account resp. write this single block. */ if (i == file_src_writer_index) { if (! opts->old_empty) target->empty_file_block = target->curblock; in_opts->data_start_lba = opts->data_start_lba = target->curblock; } ret = writer->compute_data_blocks(writer); if (ret < 0) { goto target_cleanup; } } ret = iso_patch_eltoritos(target); if (ret < 0) goto target_cleanup; if (((target->system_area_options & 0xfc) >> 2) == 2) { ret = iso_read_mipsel_elf(target, 0); if (ret < 0) goto target_cleanup; } /* * The volume space size is just the size of the last session, in * case of ms images. */ #ifdef Libisofs_appended_partitions_inlinE app_part_count = iso_count_appended_partitions(target, &fap, &lap); if (app_part_count == 0) target->vol_space_size = target->curblock - opts->ms_block; else target->vol_space_size = target->alignment_end_block - opts->ms_block; target->total_size = (off_t) (target->curblock - opts->ms_block) * BLOCK_SIZE; #else /* Libisofs_appended_partitions_inlinE */ target->vol_space_size = target->curblock - opts->ms_block; target->total_size = (off_t) target->vol_space_size * BLOCK_SIZE; /* Add sizes of eventually appended partitions */ ret = iso_compute_append_partitions(target, 0); if (ret < 0) goto target_cleanup; #endif /* ! Libisofs_appended_partitions_inlinE */ if (target->gpt_backup_outside) { for (i = 0; i < (int) target->nwriters; ++i) { IsoImageWriter *writer = target->writers[i]; if (writer->write_vol_desc != gpt_tail_writer_write_vol_desc) continue; ret = writer->compute_data_blocks(writer); if (ret < 0) goto target_cleanup; } } /* create the ring buffer */ if (opts->overwrite != NULL && opts->fifo_size < 32 + opts->partition_offset) { /* The ring buffer must be large enough to take opts->overwrite */ ret = ISO_OVWRT_FIFO_TOO_SMALL; goto target_cleanup; } ret = iso_ring_buffer_new(opts->fifo_size, &target->buffer); if (ret < 0) { goto target_cleanup; } /* check if we need to provide a copy of volume descriptors */ vol_space_size_mem = target->vol_space_size; if (opts->overwrite != NULL) { /* opts->overwrite must be larger by partion_offset This storage is provided by the application. */ /* * In the PVM to be written in the 16th sector of the disc, we * need to specify the full size. */ target->vol_space_size += opts->ms_block; /* System area and volume descriptors */ target->opts_overwrite = (char *) opts->overwrite; total_size_mem = target->total_size; target->total_size += target->opts->ms_block * BLOCK_SIZE; ret = write_head_part1(target, &write_count, 1 | 2); target->opts_overwrite = NULL; target->total_size = total_size_mem; if (ret < 0) goto target_cleanup; /* copy the volume descriptors to the overwrite buffer... */ voldesc_size *= BLOCK_SIZE; ret = iso_ring_buffer_read(target->buffer, opts->overwrite, write_count * BLOCK_SIZE); if (ret < 0) { iso_msg_debug(target->image->id, "Error reading overwrite volume descriptors"); goto target_cleanup; } /* Write relocated superblock checksum tag */ tag_pos = voldesc_size / BLOCK_SIZE + 16 + 1; if (opts->md5_session_checksum) { target->checksum_rlsb_tag_pos = tag_pos; if (target->checksum_rlsb_tag_pos < 32) { ret = iso_md5_start(&(target->checksum_ctx)); if (ret < 0) goto target_cleanup; target->opts_overwrite = (char *) opts->overwrite; iso_md5_compute(target->checksum_ctx, target->opts_overwrite, target->checksum_rlsb_tag_pos * 2048); ret = iso_md5_write_tag(target, 4); target->opts_overwrite = NULL; /* opts might not persist */ if (ret < 0) goto target_cleanup; } tag_pos++; write_count++; } /* Clean out eventual obsolete checksum tags */ for (i = tag_pos; i < 32; i++) { int tag_type; uint32_t pos, range_start, range_size, next_tag; char md5[16]; ret = iso_util_decode_md5_tag((char *)(opts->overwrite + i * 2048), &tag_type, &pos, &range_start, &range_size, &next_tag, md5, 0); if (ret > 0) opts->overwrite[i * 2048] = 0; } /* Write second set of volume descriptors */ write_count_mem= write_count; ret = write_head_part2(target, &write_count, 0); if (ret < 0) goto target_cleanup; /* Read written data into opts->overwrite */ ret = iso_ring_buffer_read(target->buffer, opts->overwrite + write_count_mem * BLOCK_SIZE, (write_count - write_count_mem) * BLOCK_SIZE); if (ret < 0) { iso_msg_debug(target->image->id, "Error reading overwrite volume descriptors"); goto target_cleanup; } /* Delete the filler partitions of GPT and APM so that write_function() can insert new ones for a possibly different total_size */; iso_delete_gpt_apm_fillers(target, 0); } /* This was possibly altered by above overwrite buffer production */ target->vol_space_size = vol_space_size_mem; /* */ #define Libisofs_print_size_no_forK 1 #ifdef Libisofs_print_size_no_forK if (opts->will_cancel) { iso_msg_debug(target->image->id, "Will not start write thread because of iso_write_opts_set_will_cancel"); *img = target; return ISO_SUCCESS; } #endif /* 4. Create and start writing thread */ if (opts->md5_session_checksum) { /* After any fake writes are done: Initialize image checksum context */ if (target->checksum_ctx != NULL) iso_md5_end(&(target->checksum_ctx), target->image_md5); ret = iso_md5_start(&(target->checksum_ctx)); if (ret < 0) goto target_cleanup; } if (opts->apm_block_size == 0) { if (target->gpt_req_count) opts->apm_block_size = 2048; /* Combinable with GPT */ else opts->apm_block_size = 512; /* Mountable on Linux */ } /* ensure the thread is created joinable */ pthread_attr_init(&(target->th_attr)); pthread_attr_setdetachstate(&(target->th_attr), PTHREAD_CREATE_JOINABLE); /* To avoid race conditions with the caller, this mark must be set before the thread starts. So the caller can rely on a value of 0 really meaning that the write has ended, and not that it might not have begun yet. In normal processing, the value will be changed to 0 at the end of write_function(). */ target->image->generator_is_running = 1; /* Claim that target may not get destroyed by bs_free_data() before write_function() is done with it */ target->refcount++; ret = pthread_create(&(target->wthread), &(target->th_attr), write_function, (void *) target); if (ret != 0) { target->refcount--; iso_msg_submit(target->image->id, ISO_THREAD_ERROR, 0, "Cannot create writer thread"); ret = ISO_THREAD_ERROR; goto target_cleanup; } target->wthread_is_running= 1; /* * Notice that once we reach this point, target belongs to the writer * thread and should not be modified until the writer thread finished. * There're however, specific fields in target that can be accessed, or * even modified by the read thread (look inside bs_* functions) */ if (target->joliet_symlinks > 0) { iso_msg_submit(target->image->id, ISO_FILE_IGNORED, 0, "Number of symbolic links omitted from Joliet tree: %lu", target->joliet_symlinks); } if (target->joliet_specials > 0) { iso_msg_submit(target->image->id, ISO_FILE_IGNORED, 0, "Number of special files omitted from Joliet tree: %lu", target->joliet_specials); } if (target->iso1999_symlinks > 0) { iso_msg_submit(target->image->id, ISO_FILE_IGNORED, 0, "Number of symbolic links omitted from ISO 9660:1999 tree: %lu", target->iso1999_symlinks); } if (target->iso1999_specials > 0) { iso_msg_submit(target->image->id, ISO_FILE_IGNORED, 0, "Number of special files omitted from ISO 9660:1999 tree: %lu", target->iso1999_specials); } *img = target; return ISO_SUCCESS; target_cleanup: ; target->image->generator_is_running = 0; ecma119_image_free(target); return ret; } static int bs_read(struct burn_source *bs, unsigned char *buf, int size) { int ret; Ecma119Image *t = (Ecma119Image*)bs->data; ret = iso_ring_buffer_read(t->buffer, buf, size); if (ret == ISO_SUCCESS) { return size; } else if (ret < 0) { /* error */ iso_msg_submit(t->image->id, ISO_BUF_READ_ERROR, ret, NULL); return -1; } else { /* EOF */ return 0; } } static off_t bs_get_size(struct burn_source *bs) { Ecma119Image *target = (Ecma119Image*)bs->data; return target->total_size; } static void bs_free_data(struct burn_source *bs) { int st; Ecma119Image *target = (Ecma119Image*)bs->data; st = iso_ring_buffer_get_status(bs, NULL, NULL); /* was read already finished (i.e, canceled)? */ if (st < 4) { /* forces writer to stop if it is still running */ iso_ring_buffer_reader_close(target->buffer, 0); /* wait until writer thread finishes */ if (target->wthread_is_running) { pthread_join(target->wthread, NULL); target->wthread_is_running = 0; iso_msg_debug(target->image->id, "Writer thread joined"); } } iso_msg_debug(target->image->id, "Ring buffer was %d times full and %d times empty", iso_ring_buffer_get_times_full(target->buffer), iso_ring_buffer_get_times_empty(target->buffer)); /* The reference to target was inherited from ecma119_image_new() */ ecma119_image_free(target); } static int bs_cancel(struct burn_source *bs) { int st; size_t cap, free; Ecma119Image *target = (Ecma119Image*)bs->data; st = iso_ring_buffer_get_status(bs, &cap, &free); if (free == cap && (st == 2 || st == 3)) { /* image was already consumed */ iso_ring_buffer_reader_close(target->buffer, 0); } else { iso_msg_debug(target->image->id, "Reader thread being cancelled"); /* forces writer to stop if it is still running */ iso_ring_buffer_reader_close(target->buffer, ISO_CANCELED); } /* wait until writer thread finishes */ if (target->wthread_is_running) { pthread_join(target->wthread, NULL); target->wthread_is_running = 0; iso_msg_debug(target->image->id, "Writer thread joined"); } return ISO_SUCCESS; } static int bs_set_size(struct burn_source *bs, off_t size) { Ecma119Image *target = (Ecma119Image*)bs->data; target->total_size = size; return 1; } static int dive_to_depth_8(IsoDir *dir, int depth) { int ret; IsoNode *pos; if (depth >= 8) return 1; pos = dir->children; for (pos = dir->children; pos != NULL; pos = pos->next) { if (pos->type != LIBISO_DIR) continue; ret = dive_to_depth_8((IsoDir *) pos, depth + 1); if (ret != 0) return ret; } return 0; } static int make_reloc_dir_if_needed(IsoImage *img, IsoWriteOpts *opts, int flag) { int ret; IsoDir *dir; /* Two forms to express the root directory */ if (opts->rr_reloc_dir == NULL) return 1; if (opts->rr_reloc_dir[0] == 0) return 1; if (strchr(opts->rr_reloc_dir, '/') != NULL) return 0; /* Check existence of opts->rr_reloc_dir */ ret = iso_dir_get_node(img->root, opts->rr_reloc_dir, NULL); if (ret > 0) return 1; if (ret < 0) return ret; /* Check whether there is a directory of depth 8 (root is depth 1) */ ret = dive_to_depth_8(img->root, 1); if (ret < 0) return ret; if (ret == 0) return 1; /* Make IsoDir with same permissions as root directory */ ret = iso_tree_add_new_dir(img->root, opts->rr_reloc_dir, &dir); if (ret < 0) return ret; opts->rr_reloc_flags |= 2; /* Auto-created relocation directory */ return 1; } int iso_image_create_burn_source(IsoImage *image, IsoWriteOpts *opts, struct burn_source **burn_src) { int ret; struct burn_source *source; Ecma119Image *target= NULL; if (image == NULL || opts == NULL || burn_src == NULL) { return ISO_NULL_POINTER; } source = calloc(1, sizeof(struct burn_source)); if (source == NULL) { return ISO_OUT_OF_MEM; } if (!opts->allow_deep_paths) { ret = make_reloc_dir_if_needed(image, opts, 0); if (ret < 0) { free(source); return ret; } } ret = ecma119_image_new(image, opts, &target); if (ret < 0) { free(source); return ret; } source->refcount = 1; source->version = 1; source->read = NULL; source->get_size = bs_get_size; source->set_size = bs_set_size; source->free_data = bs_free_data; source->read_xt = bs_read; source->cancel = bs_cancel; source->data = target; *burn_src = source; return ISO_SUCCESS; } int iso_write(Ecma119Image *target, void *buf, size_t count) { int ret; if (target->bytes_written + (off_t) count > target->total_size) { iso_msg_submit(target->image->id, ISO_ASSERT_FAILURE, 0, "ISO overwrite"); return ISO_ASSERT_FAILURE; } ret = iso_ring_buffer_write(target->buffer, buf, count); if (ret == 0) { /* reader cancelled */ return ISO_CANCELED; } if (ret < 0) return ret; if (target->checksum_ctx != NULL) { /* Add to image checksum */ target->checksum_counter += count; iso_md5_compute(target->checksum_ctx, (char *) buf, (int) count); } ret = show_chunk_to_jte(target, buf, count); if (ret != ISO_SUCCESS) return ret; /* total size is 0 when writing the overwrite buffer */ if (target->total_size != (off_t) 0){ unsigned int kbw, kbt; int percent; target->bytes_written += (off_t) count; kbw = (unsigned int) (target->bytes_written >> 10); kbt = (unsigned int) (target->total_size >> 10); percent = (kbw * 100) / kbt; /* only report in 5% chunks */ if (percent >= target->percent_written + 5) { iso_msg_debug(target->image->id, "Processed %u of %u KB (%d %%)", kbw, kbt, percent); target->percent_written = percent; } } return ISO_SUCCESS; } int iso_write_opts_new(IsoWriteOpts **opts, int profile) { int i; IsoWriteOpts *wopts; if (opts == NULL) { return ISO_NULL_POINTER; } if (profile < 0 || profile > 2) { return ISO_WRONG_ARG_VALUE; } wopts = calloc(1, sizeof(IsoWriteOpts)); if (wopts == NULL) { return ISO_OUT_OF_MEM; } wopts->scdbackup_tag_written = NULL; switch (profile) { case 0: wopts->iso_level = 1; break; case 1: wopts->iso_level = 3; wopts->rockridge = 1; break; case 2: wopts->iso_level = 2; wopts->rockridge = 1; wopts->joliet = 1; wopts->replace_dir_mode = 1; wopts->replace_file_mode = 1; wopts->replace_uid = 1; wopts->replace_gid = 1; wopts->replace_timestamps = 1; wopts->always_gmt = 1; break; default: /* should never happen */ free(wopts); return ISO_ASSERT_FAILURE; break; } wopts->hfsplus = 0; wopts->fat = 0; wopts->fifo_size = 1024; /* 2 MB buffer */ wopts->sort_files = 1; /* file sorting is always good */ wopts->joliet_utf16 = 0; wopts->rr_reloc_dir = NULL; wopts->rr_reloc_flags = 0; wopts->system_area_data = NULL; wopts->system_area_size = 0; wopts->system_area_options = 0; wopts->vol_creation_time = 0; wopts->vol_modification_time = 0; wopts->vol_expiration_time = 0; wopts->vol_effective_time = 0; memset(wopts->vol_uuid, 0, 17); wopts->partition_offset = 0; wopts->partition_secs_per_head = 0; wopts->partition_heads_per_cyl = 0; #ifdef Libisofs_with_libjtE wopts->libjte_handle = NULL; #endif /* Libisofs_with_libjtE */ wopts->tail_blocks = 0; wopts->prep_partition = NULL; wopts->prep_part_flag = 0; wopts->efi_boot_partition = NULL; wopts->efi_boot_part_flag = 0; for (i = 0; i < ISO_MAX_PARTITIONS; i++) { wopts->appended_partitions[i] = NULL; wopts->appended_part_types[i] = 0; wopts->appended_part_flags[i] = 0; memset(wopts->appended_part_type_guids[i], 0, 16); wopts->appended_part_gpt_flags[i] = 0; } wopts->appended_as_gpt = 0; wopts->appended_as_apm = 0; wopts->part_like_isohybrid = 0; wopts->iso_mbr_part_type = -1; memset(wopts->iso_gpt_type_guid, 0, 16); wopts->iso_gpt_flag= 0; wopts->ascii_disc_label[0] = 0; wopts->will_cancel = 0; wopts->allow_dir_id_ext = 0; wopts->old_empty = 0; wopts->untranslated_name_len = 0; for (i = 0; i < 8; i++) wopts->hfsp_serial_number[i] = 0; wopts->apm_block_size = 0; wopts->hfsp_block_size = 0; memset(wopts->gpt_disk_guid, 0, 16); wopts->gpt_disk_guid_mode = 0; wopts->max_ce_entries = 31; /* Linux hates >= RR_MAX_CE_ENTRIES = 32 */ wopts->max_ce_drop_attr = 2; /* If needed drop non-isofs fattr and ACL */ *opts = wopts; return ISO_SUCCESS; } int iso_write_opts_clone(IsoWriteOpts *in, IsoWriteOpts **out, int flag) { int ret, i; IsoWriteOpts *o = NULL; ret = iso_write_opts_new(&o, 0); if (ret != 1) return ret; *out = o; /* Provisory copy of all values and un-managed pointers */ memcpy(o, in, sizeof(IsoWriteOpts)); /* Set managed pointers to NULL */ o->output_charset = NULL; o->rr_reloc_dir = NULL; o->system_area_data = NULL; o->prep_partition = NULL; o->efi_boot_partition = NULL; for (i = 0; i < ISO_MAX_PARTITIONS; i++) o->appended_partitions[i] = NULL; /* Clone managed objects */ if (iso_clone_mem(in->output_charset, &(o->output_charset), 0) != 1) goto out_of_mem; if (iso_clone_mem(in->rr_reloc_dir, &(o->rr_reloc_dir), 0) != 1) goto out_of_mem; if (iso_clone_mem(in->system_area_data, &(o->system_area_data), in->system_area_size) != 1) goto out_of_mem; if (iso_clone_mem(in->prep_partition, &(o->prep_partition), 0) != 1) goto out_of_mem; if (iso_clone_mem(in->efi_boot_partition, &(o->efi_boot_partition), 0) != 1) goto out_of_mem; for (i = 0; i < ISO_MAX_PARTITIONS; i++) if (iso_clone_mem(in->appended_partitions[i], &(o->appended_partitions[i]), 0) != 1) goto out_of_mem; return ISO_SUCCESS; out_of_mem:; iso_write_opts_free(o); return ISO_OUT_OF_MEM; } void iso_write_opts_free(IsoWriteOpts *opts) { int i; if (opts == NULL) { return; } free(opts->output_charset); if (opts->rr_reloc_dir != NULL) free(opts->rr_reloc_dir); if (opts->system_area_data != NULL) free(opts->system_area_data); if (opts->prep_partition != NULL) free(opts->prep_partition); if (opts->efi_boot_partition != NULL) free(opts->efi_boot_partition); for (i = 0; i < ISO_MAX_PARTITIONS; i++) if (opts->appended_partitions[i] != NULL) free(opts->appended_partitions[i]); free(opts); } int iso_write_opts_set_will_cancel(IsoWriteOpts *opts, int will_cancel) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->will_cancel = !!will_cancel; return ISO_SUCCESS; } int iso_write_opts_set_iso_level(IsoWriteOpts *opts, int level) { if (opts == NULL) { return ISO_NULL_POINTER; } if (level != 1 && level != 2 && level != 3) { return ISO_WRONG_ARG_VALUE; } opts->iso_level = level; return ISO_SUCCESS; } int iso_write_opts_set_rockridge(IsoWriteOpts *opts, int enable) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->rockridge = enable ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_joliet(IsoWriteOpts *opts, int enable) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->joliet = enable ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_hfsplus(IsoWriteOpts *opts, int enable) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->hfsplus = enable ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_fat(IsoWriteOpts *opts, int enable) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->fat = enable ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_iso1999(IsoWriteOpts *opts, int enable) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->iso1999 = enable ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_hardlinks(IsoWriteOpts *opts, int enable) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->hardlinks = enable ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_aaip(IsoWriteOpts *opts, int enable) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->aaip = enable ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_old_empty(IsoWriteOpts *opts, int enable) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->old_empty = enable ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_untranslated_name_len(IsoWriteOpts *opts, int len) { if (opts == NULL) { return ISO_NULL_POINTER; } if (len == -1) opts->untranslated_name_len = ISO_UNTRANSLATED_NAMES_MAX; else if(len == 0) opts->untranslated_name_len = 0; else if(len > ISO_UNTRANSLATED_NAMES_MAX || len < 0) return ISO_WRONG_ARG_VALUE; else opts->untranslated_name_len = len; return opts->untranslated_name_len; } int iso_write_opts_set_allow_dir_id_ext(IsoWriteOpts *opts, int allow) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->allow_dir_id_ext = allow ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_omit_version_numbers(IsoWriteOpts *opts, int omit) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->omit_version_numbers = omit & 3; return ISO_SUCCESS; } int iso_write_opts_set_allow_deep_paths(IsoWriteOpts *opts, int allow) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->allow_deep_paths = allow ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_allow_longer_paths(IsoWriteOpts *opts, int allow) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->allow_longer_paths = allow ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_max_37_char_filenames(IsoWriteOpts *opts, int allow) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->max_37_char_filenames = allow ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_no_force_dots(IsoWriteOpts *opts, int no) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->no_force_dots = no & 3; return ISO_SUCCESS; } int iso_write_opts_set_allow_lowercase(IsoWriteOpts *opts, int allow) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->allow_lowercase = allow ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_allow_full_ascii(IsoWriteOpts *opts, int allow) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->allow_full_ascii = allow ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_allow_7bit_ascii(IsoWriteOpts *opts, int allow) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->allow_7bit_ascii = allow ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_relaxed_vol_atts(IsoWriteOpts *opts, int allow) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->relaxed_vol_atts = allow ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_joliet_longer_paths(IsoWriteOpts *opts, int allow) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->joliet_longer_paths = allow ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_joliet_long_names(IsoWriteOpts *opts, int allow) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->joliet_long_names = allow ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_joliet_utf16(IsoWriteOpts *opts, int allow) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->joliet_utf16 = allow ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_rrip_version_1_10(IsoWriteOpts *opts, int oldvers) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->rrip_version_1_10 = oldvers ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_rrip_1_10_px_ino(IsoWriteOpts *opts, int enable) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->rrip_1_10_px_ino = enable ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_aaip_susp_1_10(IsoWriteOpts *opts, int oldvers) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->aaip_susp_1_10 = oldvers ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_dir_rec_mtime(IsoWriteOpts *opts, int allow) { if (opts == NULL) { return ISO_NULL_POINTER; } if (allow < 0) allow = 1; else if (allow & (1 << 14)) allow &= ~1; else if (allow & 6) allow |= 1; opts->dir_rec_mtime = allow & 7; return ISO_SUCCESS; } int iso_write_opts_set_rr_reloc(IsoWriteOpts *opts, char *name, int flags) { if (opts->rr_reloc_dir != name) { if (opts->rr_reloc_dir != NULL) free(opts->rr_reloc_dir); opts->rr_reloc_dir = NULL; if (name != NULL) { opts->rr_reloc_dir = strdup(name); if (opts->rr_reloc_dir == NULL) return ISO_OUT_OF_MEM; } } opts->rr_reloc_flags = flags & 1; return ISO_SUCCESS; } int iso_write_opts_set_sort_files(IsoWriteOpts *opts, int sort) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->sort_files = sort ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_record_md5(IsoWriteOpts *opts, int session, int files) { opts->md5_session_checksum = session & 1; opts->md5_file_checksums = files & 3; return ISO_SUCCESS; } int iso_write_opts_set_scdbackup_tag(IsoWriteOpts *opts, char *name, char *timestamp, char *tag_written) { char eff_name[81], eff_time[19]; int i; for (i = 0; name[i] != 0 && i < 80; i++) if (isspace((int) ((unsigned char *) name)[i])) eff_name[i] = '_'; else eff_name[i] = name[i]; if (i == 0) eff_name[i++] = '_'; eff_name[i] = 0; for (i = 0; timestamp[i] != 0 && i < 18; i++) if (isspace((int) ((unsigned char *) timestamp)[i])) eff_time[i] = '_'; else eff_time[i] = timestamp[i]; if (i == 0) eff_time[i++] = '_'; eff_time[i] = 0; sprintf(opts->scdbackup_tag_parm, "%s %s", eff_name, eff_time); opts->scdbackup_tag_written = tag_written; if (tag_written != NULL) tag_written[0] = 0; return ISO_SUCCESS; } int iso_write_opts_set_replace_mode(IsoWriteOpts *opts, int dir_mode, int file_mode, int uid, int gid) { if (opts == NULL) { return ISO_NULL_POINTER; } if (dir_mode < 0 || dir_mode > 2) { return ISO_WRONG_ARG_VALUE; } if (file_mode < 0 || file_mode > 2) { return ISO_WRONG_ARG_VALUE; } if (uid < 0 || uid > 2) { return ISO_WRONG_ARG_VALUE; } if (gid < 0 || gid > 2) { return ISO_WRONG_ARG_VALUE; } opts->replace_dir_mode = dir_mode; opts->replace_file_mode = file_mode; opts->replace_uid = uid; opts->replace_gid = gid; return ISO_SUCCESS; } int iso_write_opts_set_default_dir_mode(IsoWriteOpts *opts, mode_t dir_mode) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->dir_mode = dir_mode; return ISO_SUCCESS; } int iso_write_opts_set_default_file_mode(IsoWriteOpts *opts, mode_t file_mode) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->file_mode = file_mode; return ISO_SUCCESS; } int iso_write_opts_set_default_uid(IsoWriteOpts *opts, uid_t uid) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->uid = uid; return ISO_SUCCESS; } int iso_write_opts_set_default_gid(IsoWriteOpts *opts, gid_t gid) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->gid = gid; return ISO_SUCCESS; } int iso_write_opts_set_replace_timestamps(IsoWriteOpts *opts, int replace) { if (opts == NULL) { return ISO_NULL_POINTER; } if (replace < 0 || replace > 2) { return ISO_WRONG_ARG_VALUE; } opts->replace_timestamps = replace; return ISO_SUCCESS; } int iso_write_opts_set_default_timestamp(IsoWriteOpts *opts, time_t timestamp) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->timestamp = timestamp; return ISO_SUCCESS; } int iso_write_opts_set_always_gmt(IsoWriteOpts *opts, int gmt) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->always_gmt = gmt ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_output_charset(IsoWriteOpts *opts, const char *charset) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->output_charset = charset ? strdup(charset) : NULL; return ISO_SUCCESS; } int iso_write_opts_set_appendable(IsoWriteOpts *opts, int appendable) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->appendable = appendable ? 1 : 0; return ISO_SUCCESS; } int iso_write_opts_set_ms_block(IsoWriteOpts *opts, uint32_t ms_block) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->ms_block = ms_block; return ISO_SUCCESS; } int iso_write_opts_set_overwrite_buf(IsoWriteOpts *opts, uint8_t *overwrite) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->overwrite = overwrite; return ISO_SUCCESS; } int iso_write_opts_set_fifo_size(IsoWriteOpts *opts, size_t fifo_size) { if (opts == NULL) { return ISO_NULL_POINTER; } if (fifo_size < 32) { return ISO_WRONG_ARG_VALUE; } opts->fifo_size = fifo_size; return ISO_SUCCESS; } int iso_write_opts_get_data_start(IsoWriteOpts *opts, uint32_t *data_start, int flag) { if (opts->data_start_lba == 0) return ISO_ERROR; *data_start = opts->data_start_lba; return ISO_SUCCESS; } /* * @param data Either NULL or 32 kB of data. Do not submit less bytes ! * @param options * Can cause manipulations of submitted data before they get written: * bit0= apply a --protective-msdos-label as of grub-mkisofs. * This means to patch bytes 446 to 512 of the system area so * that one partition is defined which begins at the second * 512-byte block of the image and ends where the image ends. * This works with and without system_area_data. * bit1= apply isohybrid MBR patching to the system area. * This works only with system area data from SYSLINUX plus an * ISOLINUX boot image (see iso_image_set_boot_image()) and * only if not bit0 is set. * bit2-7= System area type * bit8-9= Only with System area type 0 = MBR * Cylinder alignment mode * bit10-13= System area sub type * @param flag bit0 = invalidate any attached system area data * same as data == NULL * bit1 = keep data unaltered * bit2 = keep options unaltered */ int iso_write_opts_set_system_area(IsoWriteOpts *opts, char data[32768], int options, int flag) { if (data == NULL || (flag & 1)) { /* Disable */ if (opts->system_area_data != NULL) free(opts->system_area_data); opts->system_area_data = NULL; opts->system_area_size = 0; } else if (!(flag & 2)) { if (opts->system_area_data == NULL) { opts->system_area_data = calloc(32768, 1); if (opts->system_area_data == NULL) return ISO_OUT_OF_MEM; } memcpy(opts->system_area_data, data, 32768); opts->system_area_size = 32768; } if (!(flag & 4)) opts->system_area_options = options & 0x3ffff; return ISO_SUCCESS; } int iso_write_opts_set_pvd_times(IsoWriteOpts *opts, time_t vol_creation_time, time_t vol_modification_time, time_t vol_expiration_time, time_t vol_effective_time, char *vol_uuid) { opts->vol_creation_time = vol_creation_time; opts->vol_modification_time = vol_modification_time; opts->vol_expiration_time = vol_expiration_time; opts->vol_effective_time = vol_effective_time; strncpy(opts->vol_uuid, vol_uuid, 16); opts->vol_uuid[16] = 0; return ISO_SUCCESS; } int iso_write_opts_set_part_offset(IsoWriteOpts *opts, uint32_t block_offset_2k, int secs_512_per_head, int heads_per_cyl) { if (block_offset_2k > 0 && block_offset_2k < 16) return ISO_PART_OFFST_TOO_SMALL; opts->partition_offset = block_offset_2k; opts->partition_secs_per_head = secs_512_per_head; opts->partition_heads_per_cyl = heads_per_cyl; return ISO_SUCCESS; } int iso_write_opts_attach_jte(IsoWriteOpts *opts, void *libjte_handle) { #ifdef Libisofs_with_libjtE opts->libjte_handle = libjte_handle; return ISO_SUCCESS; #else return ISO_LIBJTE_NOT_ENABLED; #endif /* Libisofs_with_libjtE */ } int iso_write_opts_detach_jte(IsoWriteOpts *opts, void **libjte_handle) { #ifdef Libisofs_with_libjtE if (*libjte_handle != NULL) *libjte_handle = opts->libjte_handle; opts->libjte_handle = NULL; return ISO_SUCCESS; #else return ISO_LIBJTE_NOT_ENABLED; #endif /* Libisofs_with_libjtE */ } int iso_write_opts_set_tail_blocks(IsoWriteOpts *opts, uint32_t num_blocks) { opts->tail_blocks = num_blocks; return ISO_SUCCESS; } int iso_write_opts_set_prep_img(IsoWriteOpts *opts, char *image_path, int flag) { if (opts->prep_partition != NULL) free(opts->prep_partition); if (image_path == NULL) return ISO_SUCCESS; opts->prep_partition = strdup(image_path); if (opts->prep_partition == NULL) return ISO_OUT_OF_MEM; opts->prep_part_flag = (flag & 1); return ISO_SUCCESS; } int iso_write_opts_set_efi_bootp(IsoWriteOpts *opts, char *image_path, int flag) { if (opts->efi_boot_partition != NULL) free(opts->efi_boot_partition); if (image_path == NULL) return ISO_SUCCESS; opts->efi_boot_partition = strdup(image_path); if (opts->efi_boot_partition == NULL) return ISO_OUT_OF_MEM; opts->efi_boot_part_flag = (flag & 1); return ISO_SUCCESS; } int iso_write_opts_set_partition_img(IsoWriteOpts *opts, int partition_number, uint8_t partition_type, char *image_path, int flag) { if (partition_number < 1 || partition_number > ISO_MAX_PARTITIONS) return ISO_BAD_PARTITION_NO; if (opts->appended_partitions[partition_number - 1] != NULL) free(opts->appended_partitions[partition_number - 1]); if (image_path == NULL) return ISO_SUCCESS; opts->appended_partitions[partition_number - 1] = strdup(image_path); if (opts->appended_partitions[partition_number - 1] == NULL) return ISO_OUT_OF_MEM; opts->appended_part_types[partition_number - 1] = partition_type; opts->appended_part_flags[partition_number - 1] = (flag & 1); return ISO_SUCCESS; } int iso_write_opts_set_part_type_guid(IsoWriteOpts *opts, int partition_number, uint8_t guid[16], int valid) { if (partition_number < 1 || partition_number > ISO_MAX_PARTITIONS) return ISO_BAD_PARTITION_NO; if (valid) memcpy(opts->appended_part_type_guids[partition_number - 1], guid, 16); if (valid) opts->appended_part_gpt_flags[partition_number - 1]|= 1; else opts->appended_part_gpt_flags[partition_number - 1]&= ~1; return ISO_SUCCESS; } int iso_write_opts_set_appended_as_gpt(IsoWriteOpts *opts, int gpt) { opts->appended_as_gpt = !!gpt; return ISO_SUCCESS; } int iso_write_opts_set_appended_as_apm(IsoWriteOpts *opts, int apm) { opts->appended_as_apm = !!apm; return ISO_SUCCESS; } int iso_write_opts_set_part_like_isohybrid(IsoWriteOpts *opts, int alike) { opts->part_like_isohybrid = !!alike; return ISO_SUCCESS; } int iso_write_opts_set_iso_mbr_part_type(IsoWriteOpts *opts, int part_type) { if (part_type < -1 || part_type > 255) part_type = -1; opts->iso_mbr_part_type = part_type; return ISO_SUCCESS; } int iso_write_opts_set_iso_type_guid(IsoWriteOpts *opts, uint8_t guid[16], int valid) { if (valid) memcpy(opts->iso_gpt_type_guid, guid, 16); opts->iso_gpt_flag = (opts->iso_gpt_flag & ~1) | !!valid; return ISO_SUCCESS; } int iso_write_opts_set_disc_label(IsoWriteOpts *opts, char *label) { strncpy(opts->ascii_disc_label, label, ISO_DISC_LABEL_SIZE - 1); opts->ascii_disc_label[ISO_DISC_LABEL_SIZE - 1] = 0; return ISO_SUCCESS; } int iso_write_opts_set_hfsp_serial_number(IsoWriteOpts *opts, uint8_t serial_number[8]) { memcpy(opts->hfsp_serial_number, serial_number, 8); return ISO_SUCCESS; } int iso_write_opts_set_hfsp_block_size(IsoWriteOpts *opts, int hfsp_block_size, int apm_block_size) { if (hfsp_block_size != 0 && hfsp_block_size != 512 && hfsp_block_size != 2048) return ISO_BOOT_HFSP_BAD_BSIZE; opts->hfsp_block_size = hfsp_block_size; if (apm_block_size != 0 && apm_block_size != 512 && apm_block_size != 2048) return ISO_BOOT_HFSP_BAD_BSIZE; opts->apm_block_size = apm_block_size; return ISO_SUCCESS; } int iso_write_opts_set_gpt_guid(IsoWriteOpts *opts, uint8_t guid[16], int mode) { if (mode < 0 || mode > 2) return ISO_BAD_GPT_GUID_MODE; opts->gpt_disk_guid_mode = mode; if (opts->gpt_disk_guid_mode == 1) memcpy(opts->gpt_disk_guid, guid, 16); return ISO_SUCCESS; } int iso_write_opts_set_max_ce_entries(IsoWriteOpts *opts, uint32_t num, int flag) { if (num > 100000) return ISO_TOO_MANY_CE; if (num == 0) num = 1; opts->max_ce_entries = num; opts->max_ce_drop_attr = flag & 15; return ISO_SUCCESS; } /* * @param flag * Bitfield for control purposes. * bit0-bit7= Name space * 0= generic (to_charset is valid, no reserved characters, * no length limits) * 1= Rock Ridge (to_charset is valid) * 2= Joliet (to_charset gets overridden by UCS-2 or UTF-16) * 3= ECMA-119 (dull ISO 9660 character set) * 4= HFS+ (to_charset gets overridden by UTF-16BE) * bit8= Treat input text as directory name * (matters for Joliet and ECMA-119) * bit9= Do not issue error messages * bit15= Reverse operation (best to be done only with results of * previous conversions) */ int iso_conv_name_chars(IsoWriteOpts *opts, char *in_name, size_t name_len, char **result, size_t *result_len, int flag) { int name_space, ret, reverse; size_t i; size_t joliet_ucs2_failures = ISO_JOLIET_UCS2_WARN_MAX + 1;/* no warning */ size_t conved_len; char *from_charset, *to_charset, *conved, *smashed = NULL, *name; char *tr, *with_version = NULL; uint16_t *ucs = NULL, *hfspcmp = NULL; uint32_t ucs_len; enum IsoNodeType node_type; *result = NULL; *result_len = 0; name = in_name; name_space = flag & 0xff; reverse = !!(flag & (1 << 15)); node_type = (flag & 256) ? LIBISO_DIR : LIBISO_FILE; from_charset = iso_get_local_charset(0); /* Note: Joliet, ECMA-119, HFS+ actually use to_charset only for the reverse conversion case */ if (opts->output_charset != NULL) to_charset = opts->output_charset; else to_charset = from_charset; if (name_space == 1) { /* Rock Ridge */ if (!reverse) { LIBISO_ALLOC_MEM(smashed, char, name_len + 1); memcpy(smashed, name, name_len); smashed[name_len] = 0; for (i = 0; i < name_len; i++) if (smashed[i] == '/') smashed[i] = '_'; name = smashed; /* >>> ??? truncate to 255 chars */ } } else if (name_space == 2) { /* Joliet */ if (opts->joliet_utf16) to_charset = "UTF-16BE"; else to_charset = "UCS-2BE"; } else if (name_space == 3) { /* ECMA-119 */ to_charset = "ASCII"; } else if (name_space == 4) { /* HFS+ */ to_charset= "UTF-16BE"; } if (reverse) { tr = from_charset; from_charset = to_charset; to_charset = tr; } if (name_space == 0 || reverse) { ret = strnconvl(name, from_charset, to_charset, name_len, &conved, &conved_len); if (ret != ISO_SUCCESS) goto ex; } else if (name_space == 1) { /* Rock Ridge */ ret = iso_get_rr_name(opts, from_charset, to_charset, -1, name, &conved, !!(flag & 512)); if (ret != ISO_SUCCESS) goto ex; conved_len = strlen(conved); } else if (name_space == 2) { /* Joliet */ ret = iso_get_joliet_name(opts, from_charset, -1, name, node_type, &joliet_ucs2_failures, &ucs, !!(flag & 512)); if (ret != ISO_SUCCESS) goto ex; conved_len = ucslen(ucs) * 2; conved = (char *) ucs; ucs = NULL; if (node_type != LIBISO_DIR && !(opts->omit_version_numbers & 3)) { LIBISO_ALLOC_MEM(with_version, char, conved_len + 6); memcpy(with_version, conved, conved_len); with_version[conved_len++] = 0; with_version[conved_len++] = ';'; with_version[conved_len++] = 0; with_version[conved_len++] = '1'; with_version[conved_len] = 0; with_version[conved_len + 1] = 0; free(conved); conved = with_version; with_version = NULL; } } else if (name_space == 3) { /* ECMA-119 */ ret = iso_get_ecma119_name(opts, from_charset, -1, name, node_type, &conved, !!(flag & 512)); if (ret != ISO_SUCCESS) goto ex; conved_len = strlen(conved); if (need_version_number(opts, node_type == LIBISO_DIR ? ECMA119_DIR : ECMA119_FILE)) { LIBISO_ALLOC_MEM(with_version, char, conved_len + 3); memcpy(with_version, conved, conved_len + 1); strcat(with_version, ";1"); free(conved); conved = with_version; with_version = NULL; conved_len += 2; } } else if (name_space == 4) { /* HFS+ */ ret = iso_get_hfsplus_name(from_charset, -1, name, &ucs, &ucs_len, &hfspcmp); if (ret != ISO_SUCCESS) goto ex; conved = (char *) ucs; ucs = NULL; conved_len = ucs_len * 2; } else { ret = ISO_WRONG_ARG_VALUE; goto ex; } *result = conved; *result_len = conved_len; ret = ISO_SUCCESS; ex: LIBISO_FREE_MEM(with_version); LIBISO_FREE_MEM(smashed); LIBISO_FREE_MEM(ucs); LIBISO_FREE_MEM(hfspcmp); return ret; } static void ecma119_filesrc_array(Ecma119Node *dir, int (*include_item)(void *), IsoFileSrc **filelist, size_t *size, int just_count) { size_t i; Ecma119Node *child; for (i = 0; i < dir->info.dir->nchildren; i++) { child = dir->info.dir->children[i]; if (child->type == ECMA119_DIR) { ecma119_filesrc_array(child, include_item, filelist, size, just_count); } else if (child->type == ECMA119_FILE) { if (include_item != NULL) if (!include_item((void *) child->info.file)) continue; if (just_count) { (*size)++; } else { if (!child->info.file->taken) { filelist[*size] = child->info.file; (*size)++; child->info.file->taken = *size; } } } } } static void hidden_filesrc_array(Ecma119Image *t, int (*include_item)(void *), IsoFileSrc **filelist, size_t *size, int just_count) { struct iso_filesrc_list_item *item; for (item = t->ecma119_hidden_list; item != NULL; item = item->next) { if (include_item != NULL) if (!include_item((void *) item->src)) continue; if (just_count) { (*size)++; } else { if (!item->src->taken) { filelist[*size] = item->src; (*size)++; item->src->taken = *size; } } } } IsoFileSrc **iso_ecma119_to_filesrc_array(Ecma119Image *t, int (*include_item)(void *), size_t *size) { IsoFileSrc **filelist = NULL; /* Count nodes */ *size = 0; ecma119_filesrc_array(t->root, include_item, filelist, size, 1); hidden_filesrc_array(t, include_item, filelist, size, 1); LIBISO_ALLOC_MEM_VOID(filelist, IsoFileSrc *, *size + 1); /* Fill array */ *size = 0; ecma119_filesrc_array(t->root, include_item, filelist, size, 0); hidden_filesrc_array(t, include_item, filelist, size, 0); filelist[*size] = NULL; return filelist; ex: /* LIBISO_ALLOC_MEM failed */ *size = 0; return NULL; } /* Determines the range of valid partition numbers depending on partition table type. */ void iso_tell_max_part_range(IsoWriteOpts *opts, int *first_partition, int *last_partition, int flag) { int sa_type; sa_type = (opts->system_area_options >> 2) & 0x3f; if (sa_type == 3) { /* SUN Disk Label */ *first_partition = 2; *last_partition = 8; } else if(sa_type == 0 && opts->appended_as_gpt) { *first_partition = 1; *last_partition = 8; } else { *first_partition = 1; *last_partition = 4; } } /* Obtains start and end number of appended partition range and returns the number of valid entries in the list of appended partitions. */ int iso_count_appended_partitions(Ecma119Image *target, int *first_partition, int *last_partition) { int i, count= 0; iso_tell_max_part_range(target->opts, first_partition, last_partition, 0); for (i = *first_partition - 1; i <= *last_partition - 1; i++) { if (target->opts->appended_partitions[i] == NULL) continue; if (target->opts->appended_partitions[i][0] == 0) continue; count++; } return(count); } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2023 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_ECMA119_H_ #define LIBISO_ECMA119_H_ #include "libisofs.h" #include "util.h" #include "buffer.h" #ifdef HAVE_STDINT_H #include #else #ifdef HAVE_INTTYPES_H #include #endif #endif #include #define BLOCK_SIZE 2048 /* * Maximum file section size. Set to 4GB - 1 = 0xffffffff */ #define MAX_ISO_FILE_SECTION_SIZE 0xffffffff /* * When a file need to be split in several sections, the maximum size * of such sections, but the last one. Set to a multiple of BLOCK_SIZE. * Default to 4GB - 2048 = 0xFFFFF800 */ #define ISO_EXTENT_SIZE 0xFFFFF800 /* * The maximum number of partition images that can be registered. Depending * on the system area type, the effectively usable number may be smaller or * even 0. */ #define ISO_MAX_PARTITIONS 8 /* * The cylindersize with SUN Disk Label * (512 bytes/sector, 640 sectors/head, 1 head/cyl = 320 KiB). * Expressed in ECMA-119 blocks of 2048 bytes/block. */ #define ISO_SUN_CYL_SIZE 160 /* * Maximum length of a disc label text plus 1. */ #define ISO_DISC_LABEL_SIZE 129 /* The maximum length of an specs violating ECMA-119 file identifier. The theoretical limit is 254 - 34 - 28 (len of SUSP CE entry) = 192 Currently the practical limit is 254 - 34 - 96 (non-CE RR entries) - 28 (CE) */ #ifdef Libisofs_with_rrip_rR #define ISO_UNTRANSLATED_NAMES_MAX 92 #else #define ISO_UNTRANSLATED_NAMES_MAX 96 #endif /* The theoretical maximum number of Apple Partition Map entries in the System Area of an ISO image: Block0 plus 63 entries with block size 512 */ #define ISO_APM_ENTRIES_MAX 63 /* The maximum number of MBR partition table entries. */ #define ISO_MBR_ENTRIES_MAX 4 /* The theoretical maximum number of GPT entries in the System Area of an ISO image: MBR plus GPT header block plus 248 GPT entries of 128 bytes each. */ #define ISO_GPT_ENTRIES_MAX 248 /* How many warnings to issue about writing Joliet names which cannot be properly represented in UCS-2 and thus had to be defaulted to '_'. */ #define ISO_JOLIET_UCS2_WARN_MAX 3 /** * Holds the options for the image generation. */ struct iso_write_opts { int will_cancel; int iso_level; /**< ISO level to write at. (ECMA-119, 10) */ /** Which extensions to support. */ unsigned int rockridge :1; unsigned int joliet :1; unsigned int iso1999 :1; unsigned int hfsplus :1; unsigned int fat :1; unsigned int aaip :1; /* whether to write eventual ACL and EAs */ /* always write timestamps in GMT */ unsigned int always_gmt :1; /* * Relaxed constraints. Setting any of these to 1 break the specifications, * but it is supposed to work on most moderns systems. Use with caution. */ /** * Convert directory names for ECMA-119 the same way as other file names * but do not force dots or add version numbers. * This violates ECMA-119 by allowing one "." and especially ISO level 1 * by allowing DOS style 8.3 names rather than only 8 characters. */ unsigned int allow_dir_id_ext :1; /** * Omit the version number (";1") at the end of the ISO-9660 identifiers. * Version numbers are usually not used. * bit0= ECMA-119 and Joliet (for historical reasons) * bit1= Joliet */ unsigned int omit_version_numbers :2; /** * Allow ISO-9660 directory hierarchy to be deeper than 8 levels. */ unsigned int allow_deep_paths :1; /** * Allow path in the ISO-9660 tree to have more than 255 characters. */ unsigned int allow_longer_paths :1; /** * Allow a single file or directory hierarchy to have up to 37 characters. * This is larger than the 31 characters allowed by ISO level 2, and the * extra space is taken from the version number, so this also forces * omit_version_numbers. */ unsigned int max_37_char_filenames :1; /** * ISO-9660 forces filenames to have a ".", that separates file name from * extension. libisofs adds it if original filename doesn't has one. Set * this to 1 to prevent this behavior * bit0= ECMA-119 * bit1= Joliet */ unsigned int no_force_dots :2; /** * Allow lowercase characters in ISO-9660 filenames. By default, only * uppercase characters, numbers and a few other characters are allowed. */ unsigned int allow_lowercase :1; /** * Allow all ASCII characters to be appear on an ISO-9660 filename. Note * that "/" and "\0" characters are never allowed, even in RR names. */ unsigned int allow_full_ascii :1; /** * If not allow_full_ascii is set: allow all 7 bit characters that would * be allowed by allow_full_ascii. But still map lowercase to uppercase if * not allow_lowercase is set to 1. */ unsigned int allow_7bit_ascii :1; /** * Allow all characters to be part of Volume and Volset identifiers on * the Primary Volume Descriptor. This breaks ISO-9660 constraints, but * should work on modern systems. */ unsigned int relaxed_vol_atts :1; /** * Allow paths in the Joliet tree to have more than 240 characters. */ unsigned int joliet_longer_paths :1; /** * Allow Joliet names up to 103 characters rather than 64. */ unsigned int joliet_long_names :1; /** * Use UTF-16BE rather than its subset UCS-2 */ unsigned int joliet_utf16 :1; /** * Write Rock Ridge info as of specification RRIP-1.10 rather than * RRIP-1.12: signature "RRIP_1991A" rather than "IEEE_1282", * field PX without file serial number */ unsigned int rrip_version_1_10 :1; /** * Write field PX with file serial number even with RRIP-1.10 */ unsigned int rrip_1_10_px_ino :1; /** * See iso_write_opts_set_hardlinks() */ unsigned int hardlinks:1; /** * Write AAIP as extension according to SUSP 1.10 rather than SUSP 1.12. * I.e. without announcing it by an ER field and thus without the need * to precede the RRIP fields by an ES and to precede the AA field by ES. * This saves bytes and might avoid problems with readers which dislike * ER fields other than the ones for RRIP. * On the other hand, SUSP 1.12 frowns on such unannounced extensions * and prescribes ER and ES. It does this since year 1994. * * In effect only if above flag .aaip is set to 1. */ unsigned int aaip_susp_1_10 :1; /** * Store as ECMA-119 Directory Record timestamp the mtime of the source * rather than the image creation time. (The ECMA-119 prescription seems * to expect that we do have a creation timestamp with the source. * mkisofs writes mtimes and the result seems more suitable if mounted * without Rock Ridge support.) * bit0= ECMA-119, bit1= Joliet, bit2= ISO 9660:1999 */ unsigned int dir_rec_mtime :3; /** * This describes the directory where to store Rock Ridge relocated * directories. * If not relaxation "allow_deep_paths" is in effect, it is necessary to * relocate directories so that no ECMA-119 file path has more than * 8 components. For Rock Ridge the relocated directories are linked forth * and back to a placeholder at their original position in path level 8 * (entries CL and PL). Directories marked by entry RE are to be considered * artefacts of relocation and shall not be read into a Rock Ridge tree. * For plain ECMA-119, the relocation directory is just a normal directory * which contains normal files and directories. */ char *rr_reloc_dir; /* IsoNode name in root directory */ int rr_reloc_flags; /* bit0= mark auto-created rr_reloc_dir by RE bit1= directory was auto-created (cannot be set via API) */ /** * Compute MD5 checksum for the whole session and record it as index 0 of * the checksum blocks after the data area of the session. The layout and * position of these blocks will be recorded in xattr "isofs.ca" of the * root node. See see also API call iso_image_get_session_md5(). */ unsigned int md5_session_checksum :1; /** * Compute MD5 checksums for IsoFile objects and write them to blocks * after the data area of the session. The layout and position of these * blocks will be recorded in xattr "isofs.ca" of the root node. * The indice of the MD5 sums will be recorded with the IsoFile directory * entries as xattr "isofs.cx". See also API call iso_file_get_md5(). * bit0= compute individual checksums * bit1= pre-compute checksum and compare it with actual one. * Raise MISHAP if mismatch. */ unsigned int md5_file_checksums :2; /** If files should be sorted based on their weight. */ unsigned int sort_files :1; /** * The following options set the default values for files and directory * permissions, gid and uid. All these take one of three values: 0, 1 or 2. * If 0, the corresponding attribute will be kept as set in the IsoNode. * Unless you have changed it, it corresponds to the value on disc, so it * is suitable for backup purposes. If set to 1, the corresponding attrib. * will be changed by a default suitable value. Finally, if you set it to * 2, the attrib. will be changed with the value specified in the options * below. Note that for mode attributes, only the permissions are set, the * file type remains unchanged. */ unsigned int replace_dir_mode :2; unsigned int replace_file_mode :2; unsigned int replace_uid :2; unsigned int replace_gid :2; mode_t dir_mode; /** Mode to use on dirs when replace_dir_mode == 2. */ mode_t file_mode; /** Mode to use on files when replace_file_mode == 2. */ uid_t uid; /** uid to use when replace_uid == 2. */ gid_t gid; /** gid to use when replace_gid == 2. */ /** * See API call iso_write_opts_set_old_empty(). */ unsigned int old_empty :1; /** * Extra Caution: This option breaks any assumptions about names that * are supported by ECMA-119 specifications. * Omit any translation which would make a file name compliant to the * ECMA-119 rules. This includes and exceeds omit_version_numbers, * max_37_char_filenames, no_force_dots bit0, allow_lowercase. * The maximum name length is given by this variable. * There is a length limit of ISO_UNTRANSLATED_NAMES_MAX characters, * because ECMA-119 allows 254 byte in a directory record, some * of them are occupied by ECMA-119, some more are needed for SUSP CE, * and some are fixely occupied by libisofs Rock Ridge code. * The default value 0 disables this feature. */ unsigned int untranslated_name_len; /** * 0 to use IsoNode timestamps, 1 to use recording time, 2 to use * values from timestamp field. This has only meaning if RR extensions * are enabled. */ unsigned int replace_timestamps :2; time_t timestamp; /** * Charset for the RR filenames that will be created. * NULL to use default charset, the locale one. */ char *output_charset; /** * This flags control the type of the image to create. Libisofs support * two kind of images: stand-alone and appendable. * * A stand-alone image is an image that is valid alone, and that can be * mounted by its own. This is the kind of image you will want to create * in most cases. A stand-alone image can be burned in an empty CD or DVD, * or write to an .iso file for future burning or distribution. * * On the other side, an appendable image is not self contained, it refers * to several files that are stored outside the image. Its usage is for * multisession discs, where you add data in a new session, while the * previous session data can still be accessed. In those cases, the old * data is not written again. Instead, the new image refers to it, and thus * it's only valid when appended to the original. Note that in those cases * the image will be written after the original, and thus you will want * to use a ms_block greater than 0. * * Note that if you haven't import a previous image (by means of * iso_image_import()), the image will always be a stand-alone image, as * there is no previous data to refer to. */ unsigned int appendable : 1; /** * Start block of the image. It is supposed to be the lba where the first * block of the image will be written on disc. All references inside the * ISO image will take this into account, thus providing a mountable image. * * For appendable images, that are written to a new session, you should * pass here the lba of the next writable address on disc. * * In stand alone images this is usually 0. However, you may want to * provide a different ms_block if you don't plan to burn the image in the * first session on disc, such as in some CD-Extra disc whether the data * image is written in a new session after some audio tracks. */ uint32_t ms_block; /** * When not NULL, it should point to a buffer of at least 64KiB, where * libisofs will write the contents that should be written at the beginning * of a overwritable media, to grow the image. The growing of an image is * a way, used by first time in growisofs by Andy Polyakov, to allow the * appending of new data to non-multisession media, such as DVD+RW, in the * same way you append a new session to a multisession disc, i.e., without * need to write again the contents of the previous image. * * Note that if you want this kind of image growing, you will also need to * set appendable to "1" and provide a valid ms_block after the previous * image. * * You should initialize the buffer either with 0s, or with the contents of * the first blocks of the image you're growing. In most cases, 0 is good * enough. */ uint8_t *overwrite; /** * Size, in number of blocks, of the FIFO buffer used between the writer * thread and the burn_source. You have to provide at least a 32 blocks * buffer. */ size_t fifo_size; /** * This is not an option setting but a value returned after the options * were used to compute the layout of the image. * It tells the LBA of the first plain file data block in the image. */ uint32_t data_start_lba; /** * If not empty: A text holding parameters "name" and "timestamp" for * a scdbackup stream checksum tag. See scdbackup/README appendix VERIFY. * It makes sense only for single session images which start at LBA 0. * Such a tag may be part of a libisofs checksum tag block after the * session tag line. It then covers the whole session up to its own start * position. */ char scdbackup_tag_parm[100]; /* If not NULL: A pointer to an application provided array with at least 512 characters. The effectively written scdbackup tag will be copied to this memory location. */ char *scdbackup_tag_written; /* * See ecma119_image : System Area related information */ char *system_area_data; int system_area_size; int system_area_options; /* User settable PVD time stamps */ time_t vol_creation_time; time_t vol_modification_time; time_t vol_expiration_time; time_t vol_effective_time; /* To eventually override vol_creation_time and vol_modification_time * by unconverted string with timezone 0 */ char vol_uuid[17]; /* The number of unclaimed 2K blocks before start of partition 1 as of the MBR in system area. Must be 0 or >= 16. (Actually >= number of voldescr + checksum tag) */ uint32_t partition_offset; /* Partition table parameter: 1 to 63, 0= disabled/default */ int partition_secs_per_head; /* 1 to 255, 0= disabled/default */ int partition_heads_per_cyl; #ifdef Libisofs_with_libjtE /* Parameters and state of Jigdo Template Export environment. */ struct libjte_env *libjte_handle; #endif /* Libisofs_with_libjtE */ /* A trailing padding of zero bytes which belongs to the image */ uint32_t tail_blocks; /* Eventual disk file path of a PreP partition which shall be prepended to HFS+/FAT and IsoFileSrc areas and marked by an MBR partition entry. */ char *prep_partition; int prep_part_flag; /* Eventual disk file path of an EFI system partition image which shall be prepended to HFS+/FAT and IsoFileSrc areas and marked by a GPT entry. */ char *efi_boot_partition; int efi_boot_part_flag; /* Disk file paths of prepared images which shall be appended after the ISO image and described by partition table entries in a MBR. NULL means unused. */ char *appended_partitions[ISO_MAX_PARTITIONS]; uint8_t appended_part_types[ISO_MAX_PARTITIONS]; int appended_part_flags[ISO_MAX_PARTITIONS]; uint8_t appended_part_type_guids[ISO_MAX_PARTITIONS][16]; /* Flags in case that appended partitions show up in GPT: bit0= appended_part_type_guids is valid */ uint8_t appended_part_gpt_flags[ISO_MAX_PARTITIONS]; /* If 1: With appended partitions: create protective MBR and mark by GPT */ int appended_as_gpt; /* If 1: With appended partitions: mark by APM partition */ int appended_as_apm; /* If 1: Obey struct el_torito_boot_image.isolinux_options bit2-7 and bit8. I.e. mention boot image as partition in GPT and/or APM. */ int part_like_isohybrid; /* The type to use for the mountable ISO partition if there is any and if the type is not mandatorily determined for particular circumstances like compliant GPT, CHRP, or PReP. -1 = use the default value (e.g. 0xcd, 0x83, 0x17) 0x00 to 0xff = value to use if possible */ int iso_mbr_part_type; /* iso_write_opts_set_iso_type_guid */ uint8_t iso_gpt_type_guid[16]; /* bit0= iso_gpt_type_guid is valid */ int iso_gpt_flag; /* Eventual name of the non-ISO aspect of the image. E.g. SUN ASCII label. */ char ascii_disc_label[ISO_DISC_LABEL_SIZE]; /* HFS+ image serial number. * 00...00 means that it shall be generated by libisofs. */ uint8_t hfsp_serial_number[8]; /* Allocation block size of HFS+ : 0= auto , 512, or 2048 */ int hfsp_block_size; /* Block size of and in APM : 0= auto , 512, or 2048 */ int apm_block_size; /* User defined GUID for GPT header and base of reproducible partition GUIDs. (Not to be confused with volume "UUID", which is actually a timestamp.) See API call iso_write_opts_set_gpt_guid(). */ uint8_t gpt_disk_guid[16]; int gpt_disk_guid_mode; /* Maximum number of CE entries per file */ uint32_t max_ce_entries; /* Whether to try dropping AAIP data on too many CE: bit0-3 = Mode: 0 = throw ISO_TOO_MANY_CE, without trying to drop anything 1 = drop non-isofs fattr 2 = drop ACL if dropping non-isofs fattr does not suffice */ int max_ce_drop_attr; }; typedef struct ecma119_image Ecma119Image; typedef struct ecma119_node Ecma119Node; typedef struct joliet_node JolietNode; typedef struct iso1999_node Iso1999Node; typedef struct hfsplus_node HFSPlusNode; typedef struct Iso_File_Src IsoFileSrc; typedef struct Iso_Image_Writer IsoImageWriter; struct ecma119_image { int refcount; IsoImage *image; Ecma119Node *root; IsoWriteOpts *opts; /** Whether El Torito data will be produced */ unsigned int eltorito :1; /* The ECMA-119 directory node where to store Rock Ridge relocated directories. (Path is in IsoWriteOpts.rr_reloc_dir) */ Ecma119Node *rr_reloc_node; /* Directory node in ecma119_image */ /* * Mode replace. If one of these flags is set, the correspodent values are * replaced with values below. Both get computed from IsoWriteOpts. */ unsigned int replace_uid :1; unsigned int replace_gid :1; unsigned int replace_file_mode :1; unsigned int replace_dir_mode :1; unsigned int replace_timestamps :1; /* Mode replacement values. */ uid_t uid; gid_t gid; mode_t file_mode; mode_t dir_mode; time_t timestamp; /* Effective charsets */ char *input_charset; char *output_charset; time_t now; /**< Time at which writing began. */ /* Total size of the output. Counted in bytes. * Includes ISO filesystem and appended data. */ off_t total_size; /** Size actually governed by the ISO filesystem part of the output */ uint32_t vol_space_size; /* 1= write the total size into the PVD of the ISO, * 0= write vol_space_size */ int pvd_size_is_total_size; /* Bytes already written to image output */ off_t bytes_written; /* just for progress notification */ int percent_written; /* * Block being processed, either during image writing or structure * size calculation. */ uint32_t curblock; /* * The address to be used for the content pointer of empty data files. */ uint32_t empty_file_block; /* * The calculated block address after ECMA-119 tree and eventual * tree checksum tag. */ uint32_t tree_end_block; /* * number of dirs in ECMA-119 tree, computed together with dir position, * and needed for path table computation in a efficient way */ size_t ndirs; uint32_t path_table_size; uint32_t l_path_table_pos; uint32_t m_path_table_pos; /* * Joliet related information */ JolietNode *joliet_root; size_t joliet_ndirs; uint32_t joliet_path_table_size; uint32_t joliet_l_path_table_pos; uint32_t joliet_m_path_table_pos; size_t joliet_ucs2_failures; /* * HFS+ related information * (by Vladimir Serbinenko, see libisofs/hfsplus.c) */ HFSPlusNode *hfsp_leafs; struct hfsplus_btree_level *hfsp_levels; uint32_t hfsp_nlevels; uint32_t hfsp_part_start; uint32_t hfsp_nfiles; uint32_t hfsp_ndirs; uint32_t hfsp_cat_id; uint32_t hfsp_allocation_blocks; uint32_t hfsp_allocation_file_start; uint32_t hfsp_extent_file_start; uint32_t hfsp_catalog_file_start; uint32_t hfsp_total_blocks; uint32_t hfsp_allocation_size; uint32_t hfsp_nleafs; uint32_t hfsp_curleaf; uint32_t hfsp_nnodes; uint32_t hfsp_bless_id[ISO_HFSPLUS_BLESS_MAX]; uint32_t hfsp_collision_count; /* * ISO 9660:1999 related information */ Iso1999Node *iso1999_root; size_t iso1999_ndirs; uint32_t iso1999_path_table_size; uint32_t iso1999_l_path_table_pos; uint32_t iso1999_m_path_table_pos; /* * El-Torito related information */ struct el_torito_boot_catalog *catalog; IsoFileSrc *cat; /**< location of the boot catalog in the new image */ int num_bootsrc; IsoFileSrc **bootsrc; /* location of the boot images in the new image */ int *boot_appended_idx; /* Appended partition which serve as boot images */ uint32_t *boot_intvl_start; /* In blocks of 2048 bytes */ uint32_t *boot_intvl_size; /* In blocks of 512 bytes */ /* * System Area related information */ /* Content of an embedded boot image. Valid if not NULL. * In that case it must point to a memory buffer at least 32 kB. */ char *system_area_data; /* * bit0= Only with DOS MBR * Make bytes 446 - 512 of the system area a partition * table which reserves partition 1 from byte 63*512 to the * end of the ISO image. Assumed are 63 secs/hed, 255 head/cyl. * (GRUB protective msdos label.) * This works with and without system_area_data. * bit1= Only with DOS MBR * Apply isohybrid MBR patching to the system area. * This works only with system_area_data plus ISOLINUX boot image * and only if not bit0 is set. * bit2-7= System area type * 0= DOS MBR * 1= MIPS Big Endian Volume Header * 2= DEC Boot Block for MIPS Little Endian * 3= SUN Disk Label for SUN SPARC * bit8-9= Only with DOS MBR * Cylinder alignment mode eventually pads the image to make it * end at a cylinder boundary. * 0 = auto (align if bit1) * 1 = always align to cylinder boundary * 2 = never align to cylinder boundary * 3 = always align, additionally pad up and align partitions * which were appended by iso_write_opts_set_partition_img() * bit10-13= System area sub type * With type 0 = MBR: * Gets overridden by bit0 and bit1. * 0 = no particular sub type * 1 = CHRP: A single MBR partition of type 0x96 covers the * ISO image. Not compatible with any other feature * which needs to have own MBR partition entries. */ int system_area_options; /* * Number of pad blocks that we need to write. Padding blocks are blocks * filled by 0s that we put between the directory structures and the file * data. These padding blocks are added by libisofs to improve the handling * of image growing. The idea is that the first blocks in the image are * overwritten with the volume descriptors of the new image. These first * blocks usually correspond to the volume descriptors and directory * structure of the old image, and can be safety overwritten. However, * with very small images they might correspond to valid data. To ensure * this never happens, what we do is to add padding bytes, to ensure no * file data is written in the first 64 KiB, that are the bytes we usually * overwrite. */ uint32_t mspad_blocks; size_t nwriters; IsoImageWriter **writers; /* tree of files sources */ IsoRBTree *files; struct iso_filesrc_list_item *ecma119_hidden_list; unsigned int checksum_idx_counter; void *checksum_ctx; off_t checksum_counter; uint32_t checksum_rlsb_tag_pos; uint32_t checksum_sb_tag_pos; uint32_t checksum_tree_tag_pos; uint32_t checksum_tag_pos; char image_md5[16]; char *checksum_buffer; uint32_t checksum_array_pos; uint32_t checksum_range_start; uint32_t checksum_range_size; char *opts_overwrite; /* Points to IsoWriteOpts->overwrite. Use only underneath ecma119_image_new() and if not NULL*/ /* Buffer for communication between burn_source and writer thread */ IsoRingBuffer *buffer; /* writer thread descriptor */ pthread_t wthread; int wthread_is_running; pthread_attr_t th_attr; /* Effective partition table parameter: 1 to 63, 0= disabled/default */ int partition_secs_per_head; /* 1 to 255, 0= disabled/default */ int partition_heads_per_cyl; /* The currently applicable LBA offset. To be subtracted from any LBA * that is mentioned in volume descriptors, trees, path tables, * Either 0 or .partition_offset */ uint32_t eff_partition_offset; /* The second ECMA-119 directory tree and path tables */ Ecma119Node *partition_root; uint32_t partition_l_table_pos; uint32_t partition_m_table_pos; /* The second Joliet directory tree and path tables */ JolietNode *j_part_root; uint32_t j_part_l_path_table_pos; uint32_t j_part_m_path_table_pos; /* Memorized ELF parameters from MIPS Little Endian boot file */ uint32_t mipsel_e_entry; uint32_t mipsel_p_offset; uint32_t mipsel_p_vaddr; uint32_t mipsel_p_filesz; /* A data file of which the position and size shall be written after a SUN Disk Label. */ IsoFileSrc *sparc_core_src; /* Trailing padding of ISO filesystem partition for cylinder alignment */ /* Only in effect with Libisofs_part_align_writeR */ uint32_t part_align_blocks; uint32_t alignment_end_block; /* Counted in blocks of 2048 */ uint32_t appended_part_prepad[ISO_MAX_PARTITIONS]; uint32_t appended_part_start[ISO_MAX_PARTITIONS]; uint32_t appended_part_size[ISO_MAX_PARTITIONS]; int have_appended_partitions; /* See IsoImage and libisofs.h */ IsoNode *hfsplus_blessed[ISO_HFSPLUS_BLESS_MAX]; /* Block sizes come from write options. Only change a block size if it is 0. Set only to 512 or 2048. If it stays 0 then it will become 512 or 2048 in time. */ /* Allocation block size of HFS+ May be defined to 512 or 2048 before hfsplus_writer_create(). */ int hfsp_cat_node_size; /* 2 * hfsp_block_size */ int hfsp_iso_block_fac; /* 2048 / hfsp_block_size */ /* Apple Partition Map description. To be composed during IsoImageWriter method ->compute_data_blocks() by calling iso_register_apm_entry(). Make sure that the composing writers get registered before the gpt_tail_writer. */ struct iso_apm_partition_request *apm_req[ISO_APM_ENTRIES_MAX]; int apm_req_count; /* bit1= Do not fill gaps in Apple Partition Map bit2= apm_req entries use apm_block_size in start_block and block_count. Normally these two parameters are counted in 2 KiB blocks. */ int apm_req_flags; /* MBR partition table description. To be composed during IsoImageWriter method ->compute_data_blocks() by calling iso_register_mbr_entry(). */ struct iso_mbr_partition_request *mbr_req[ISO_MBR_ENTRIES_MAX]; int mbr_req_count; /* Number of bytes which have to be added after the cylinder aligned end of the overall ISO partition because clinder size is not a multiple of 2048 */ int post_iso_part_pad; uint32_t prep_part_size; /* GPT description. To be composed during IsoImageWriter method ->compute_data_blocks() by calling iso_register_gpt_entry(). Make sure that the composing writers get registered before the gpt_tail_writer. */ struct iso_gpt_partition_request *gpt_req[ISO_GPT_ENTRIES_MAX]; int gpt_req_count; /* bit0= GPT partitions may overlap */ int gpt_req_flags; /* Whether the eventual backup GPT is not part of the ISO filesystem */ int gpt_backup_outside; /* The base UUID for the generated GPT UUIDs */ uint8_t gpt_uuid_base[16]; /* The counter which distinguishes the GPT UUIDs */ uint32_t gpt_uuid_counter; uint32_t efi_boot_part_size; IsoFileSrc *efi_boot_part_filesrc; /* Just a pointer. Do not free. */ /* Messages from gpt_tail_writer_compute_data_blocks() to iso_write_system_area(). */ uint8_t gpt_disk_guid[16]; int gpt_disk_guid_set; /* Start of GPT entries in System Area, block size 512 */ uint32_t gpt_part_start; /* The ISO block number after the backup GPT header , block size 2048 */ uint32_t gpt_backup_end; uint32_t gpt_backup_size; uint32_t gpt_max_entries; int gpt_is_computed; /* Message from write_head_part1()/iso_write_system_area() to the write_data() methods of the writers. */ uint8_t sys_area_as_written[16 * BLOCK_SIZE]; int sys_area_already_written; /* Size of the filesrc_writer area (data file content). This is available before any IsoImageWriter.compute_data_blocks() is called. */ uint32_t filesrc_start; uint32_t filesrc_blocks; /* Number of CE entries in currently processed node */ uint32_t curr_ce_entries; /* Count of symbolic links and special files which could not be represented in Joliet. */ unsigned long joliet_symlinks; unsigned long joliet_specials; /* Count of symbolic links and special files which could not be represented in ISO 9660:1999. */ unsigned long iso1999_symlinks; unsigned long iso1999_specials; }; #define BP(a,b) [(b) - (a) + 1] /* ECMA-119, 8.4 */ struct ecma119_pri_vol_desc { uint8_t vol_desc_type BP(1, 1); uint8_t std_identifier BP(2, 6); uint8_t vol_desc_version BP(7, 7); uint8_t unused1 BP(8, 8); uint8_t system_id BP(9, 40); uint8_t volume_id BP(41, 72); uint8_t unused2 BP(73, 80); uint8_t vol_space_size BP(81, 88); uint8_t unused3 BP(89, 120); uint8_t vol_set_size BP(121, 124); uint8_t vol_seq_number BP(125, 128); uint8_t block_size BP(129, 132); uint8_t path_table_size BP(133, 140); uint8_t l_path_table_pos BP(141, 144); uint8_t opt_l_path_table_pos BP(145, 148); uint8_t m_path_table_pos BP(149, 152); uint8_t opt_m_path_table_pos BP(153, 156); uint8_t root_dir_record BP(157, 190); uint8_t vol_set_id BP(191, 318); uint8_t publisher_id BP(319, 446); uint8_t data_prep_id BP(447, 574); uint8_t application_id BP(575, 702); uint8_t copyright_file_id BP(703, 739); uint8_t abstract_file_id BP(740, 776); uint8_t bibliographic_file_id BP(777, 813); uint8_t vol_creation_time BP(814, 830); uint8_t vol_modification_time BP(831, 847); uint8_t vol_expiration_time BP(848, 864); uint8_t vol_effective_time BP(865, 881); uint8_t file_structure_version BP(882, 882); uint8_t reserved1 BP(883, 883); uint8_t app_use BP(884, 1395); uint8_t reserved2 BP(1396, 2048); }; /* ECMA-119, 8.5 */ struct ecma119_sup_vol_desc { uint8_t vol_desc_type BP(1, 1); uint8_t std_identifier BP(2, 6); uint8_t vol_desc_version BP(7, 7); uint8_t vol_flags BP(8, 8); uint8_t system_id BP(9, 40); uint8_t volume_id BP(41, 72); uint8_t unused2 BP(73, 80); uint8_t vol_space_size BP(81, 88); uint8_t esc_sequences BP(89, 120); uint8_t vol_set_size BP(121, 124); uint8_t vol_seq_number BP(125, 128); uint8_t block_size BP(129, 132); uint8_t path_table_size BP(133, 140); uint8_t l_path_table_pos BP(141, 144); uint8_t opt_l_path_table_pos BP(145, 148); uint8_t m_path_table_pos BP(149, 152); uint8_t opt_m_path_table_pos BP(153, 156); uint8_t root_dir_record BP(157, 190); uint8_t vol_set_id BP(191, 318); uint8_t publisher_id BP(319, 446); uint8_t data_prep_id BP(447, 574); uint8_t application_id BP(575, 702); uint8_t copyright_file_id BP(703, 739); uint8_t abstract_file_id BP(740, 776); uint8_t bibliographic_file_id BP(777, 813); uint8_t vol_creation_time BP(814, 830); uint8_t vol_modification_time BP(831, 847); uint8_t vol_expiration_time BP(848, 864); uint8_t vol_effective_time BP(865, 881); uint8_t file_structure_version BP(882, 882); uint8_t reserved1 BP(883, 883); uint8_t app_use BP(884, 1395); uint8_t reserved2 BP(1396, 2048); }; /* ECMA-119, 8.2 */ struct ecma119_boot_rec_vol_desc { uint8_t vol_desc_type BP(1, 1); uint8_t std_identifier BP(2, 6); uint8_t vol_desc_version BP(7, 7); uint8_t boot_sys_id BP(8, 39); uint8_t boot_id BP(40, 71); uint8_t boot_catalog BP(72, 75); uint8_t unused BP(76, 2048); }; /* ECMA-119, 9.1 */ struct ecma119_dir_record { uint8_t len_dr BP(1, 1); uint8_t len_xa BP(2, 2); uint8_t block BP(3, 10); uint8_t length BP(11, 18); uint8_t recording_time BP(19, 25); uint8_t flags BP(26, 26); uint8_t file_unit_size BP(27, 27); uint8_t interleave_gap_size BP(28, 28); uint8_t vol_seq_number BP(29, 32); uint8_t len_fi BP(33, 33); uint8_t file_id BP(34, 34); /* 34 to 33+len_fi */ /* padding field (if len_fi is even) */ /* system use (len_dr - len_su + 1 to len_dr) */ }; /* ECMA-119, 9.4 */ struct ecma119_path_table_record { uint8_t len_di BP(1, 1); uint8_t len_xa BP(2, 2); uint8_t block BP(3, 6); uint8_t parent BP(7, 8); uint8_t dir_id BP(9, 9); /* 9 to 8+len_di */ /* padding field (if len_di is odd) */ }; /* ECMA-119, 8.3 */ struct ecma119_vol_desc_terminator { uint8_t vol_desc_type BP(1, 1); uint8_t std_identifier BP(2, 6); uint8_t vol_desc_version BP(7, 7); uint8_t reserved BP(8, 2048); }; void ecma119_set_voldescr_times(IsoImageWriter *writer, struct ecma119_pri_vol_desc *vol); /* Copies a data file into the ISO image output stream */ int iso_write_partition_file(Ecma119Image *target, char *path, uint32_t prepad, uint32_t blocks, int flag); void issue_ucs2_warning_summary(size_t failures); /* Tells whether ivr is a reader from imported_iso in a multi-session add-on situation, and thus to be kept in place. */ int iso_interval_reader_keep(Ecma119Image *target, struct iso_interval_reader *ivr, int flag); /* @return: ISO_SUCCESS = ok, ISO_SUCCESS + 1 = keep , < 0 = error */ int iso_interval_reader_start_size(Ecma119Image *t, char *path, off_t *start_byte, off_t *byte_count, int flag); /* Obtains start and end number of appended partition range and returns the number of valid entries in the list of appended partitions. */ int iso_count_appended_partitions(Ecma119Image *target, int *first_partition, int *last_partition); /* Determines the range of valid partition numbers depending on partition table type. */ void iso_tell_max_part_range(IsoWriteOpts *opts, int *first_partition, int *last_partition, int flag); #endif /*LIBISO_ECMA119_H_*/ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2016 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif /* Must be before ecma119.h because of eventual Libisofs_with_rrip_rR */ #include "libisofs.h" #include "ecma119_tree.h" #include "ecma119.h" #include "node.h" #include "util.h" #include "filesrc.h" #include "messages.h" #include "image.h" #include "stream.h" #include "eltorito.h" #include #include #include /* @param flag bit0= Do not issue error messages */ int iso_get_ecma119_name(IsoWriteOpts *opts, char *input_charset, int imgid, char *node_name, enum IsoNodeType node_type, char **name, int flag) { int ret, relaxed, free_ascii_name = 0, force_dots = 0; char *ascii_name; char *isoname = NULL; if (node_name == NULL) { /* it is not necessarily an error, it can be the root */ return ISO_SUCCESS; } if (opts->untranslated_name_len > 0) { ascii_name = node_name; ret = 1; } else { ret = str2ascii(input_charset, node_name, &ascii_name); free_ascii_name = 1; } if (ret < 0) { if (!(flag & 512)) iso_msg_submit(imgid, ret, 0, "Cannot convert name '%s' to ASCII", node_name); return ret; } if (opts->allow_full_ascii) { relaxed = 2; } else { relaxed = (int)opts->allow_lowercase; } if (opts->allow_7bit_ascii) relaxed |= 4; if (node_type == LIBISO_DIR && !(opts->allow_dir_id_ext)) { if (opts->untranslated_name_len > 0) { if (strlen(ascii_name) > opts->untranslated_name_len) { needs_transl:; if (!(flag & 512)) iso_msg_submit(imgid, ISO_NAME_NEEDS_TRANSL, 0, "File name too long (%d > %d) for untranslated recording: '%s'", strlen(ascii_name), opts->untranslated_name_len, ascii_name); return ISO_NAME_NEEDS_TRANSL; } isoname = strdup(ascii_name); } else if (opts->max_37_char_filenames) { isoname = iso_r_dirid(ascii_name, 37, relaxed); } else if (opts->iso_level == 1) { #ifdef Libisofs_old_ecma119_nameS if (relaxed) { isoname = iso_r_dirid(ascii_name, 8, relaxed); } else { isoname = iso_1_dirid(ascii_name, 0); } #else /* Libisofs_old_ecma119_nameS */ isoname = iso_1_dirid(ascii_name, relaxed); #endif /* ! Libisofs_old_ecma119_nameS */ } else { if (relaxed) { isoname = iso_r_dirid(ascii_name, 31, relaxed); } else { isoname = iso_2_dirid(ascii_name); } } } else { force_dots = !((opts->no_force_dots & 1) || node_type == LIBISO_DIR); if (opts->untranslated_name_len > 0) { if (strlen(ascii_name) > opts->untranslated_name_len) goto needs_transl; isoname = strdup(ascii_name); } else if (opts->max_37_char_filenames) { isoname = iso_r_fileid(ascii_name, 36, relaxed, force_dots); } else if (opts->iso_level == 1) { #ifdef Libisofs_old_ecma119_nameS int max_len; if (relaxed) { if (strchr(ascii_name, '.') == NULL) max_len = 8; else max_len = 11; isoname = iso_r_fileid(ascii_name, max_len, relaxed, force_dots); } else { isoname = iso_1_fileid(ascii_name, 0, force_dots); } #else /* Libisofs_old_ecma119_nameS */ isoname = iso_1_fileid(ascii_name, relaxed, force_dots); #endif /* ! Libisofs_old_ecma119_nameS */ } else { if (relaxed || !force_dots) { isoname = iso_r_fileid(ascii_name, 30, relaxed, force_dots); } else { isoname = iso_2_fileid(ascii_name); } } } if (free_ascii_name) free(ascii_name); if (isoname != NULL) { *name = isoname; return ISO_SUCCESS; } else { /* * only possible if mem error, as check for empty names is done * in public tree */ return ISO_OUT_OF_MEM; } } static int get_iso_name(Ecma119Image *img, IsoNode *iso, char **name) { int ret; ret = iso_get_ecma119_name(img->opts, img->input_charset, img->image->id, iso->name, iso->type, name, 0); return ret; } int ecma119_is_dedicated_reloc_dir(Ecma119Image *img, Ecma119Node *node) { if (img->rr_reloc_node == node && node != img->root && node != img->partition_root && (img->opts->rr_reloc_flags & 2)) return 1; return 0; } static int create_ecma119_node(Ecma119Image *img, IsoNode *iso, Ecma119Node **node) { Ecma119Node *ecma; ecma = calloc(1, sizeof(Ecma119Node)); if (ecma == NULL) { return ISO_OUT_OF_MEM; } ecma->node = iso; iso_node_ref(iso); ecma->nlink = 1; *node = ecma; return ISO_SUCCESS; } /** * Create a new ECMA-119 node representing a directory from a iso directory * node. */ static int create_dir(Ecma119Image *img, IsoDir *iso, Ecma119Node **node) { int ret; Ecma119Node **children = NULL; struct ecma119_dir_info *dir_info; if (iso->nchildren > 0) { children = calloc(1, sizeof(void*) * iso->nchildren); if (children == NULL) return ISO_OUT_OF_MEM; } dir_info = calloc(1, sizeof(struct ecma119_dir_info)); if (dir_info == NULL) { if (children != NULL) free(children); return ISO_OUT_OF_MEM; } ret = create_ecma119_node(img, (IsoNode*)iso, node); if (ret < 0) { if (children != NULL) free(children); free(dir_info); return ret; } (*node)->type = ECMA119_DIR; (*node)->info.dir = dir_info; (*node)->info.dir->nchildren = 0; (*node)->info.dir->children = children; return ISO_SUCCESS; } static int create_file_src(Ecma119Image *img, IsoFile *iso, IsoFileSrc **src) { int ret; off_t size; size = iso_stream_get_size(iso->stream); if (size > (off_t)MAX_ISO_FILE_SECTION_SIZE && img->opts->iso_level != 3) { char *ipath = iso_tree_get_node_path(ISO_NODE(iso)); iso_msg_submit(img->image->id, ISO_FILE_TOO_BIG, 0, "File \"%s\" cannot be added to image because " "its size is 4 GiB or larger", ipath); free(ipath); return ISO_FILE_TOO_BIG; } ret = iso_file_src_create(img, iso, src); if (ret < 0) { return ret; } return 0; } /** * Create a new ECMA-119 node representing a regular file from a iso file * node. */ static int create_file(Ecma119Image *img, IsoFile *iso, Ecma119Node **node) { int ret; IsoFileSrc *src; ret = create_file_src(img, iso, &src); if (ret < 0) { return ret; } ret = create_ecma119_node(img, (IsoNode*)iso, node); if (ret < 0) { /* * the src doesn't need to be freed, it is free together with * the Ecma119Image */ return ret; } (*node)->type = ECMA119_FILE; (*node)->info.file = src; return ret; } /** * Create a new ECMA-119 node representing a regular file from an El-Torito * boot catalog */ static int create_boot_cat(Ecma119Image *img, IsoBoot *iso, Ecma119Node **node) { int ret; IsoFileSrc *src; ret = el_torito_catalog_file_src_create(img, &src); if (ret < 0) { return ret; } ret = create_ecma119_node(img, (IsoNode*)iso, node); if (ret < 0) { /* * the src doesn't need to be freed, it is free together with * the Ecma119Image */ return ret; } (*node)->type = ECMA119_FILE; (*node)->info.file = src; return ret; } /** * Create a new ECMA-119 node representing a symbolic link from a iso symlink * node. */ static int create_symlink(Ecma119Image *img, IsoSymlink *iso, Ecma119Node **node) { int ret; ret = create_ecma119_node(img, (IsoNode*)iso, node); if (ret < 0) { return ret; } (*node)->type = ECMA119_SYMLINK; return ISO_SUCCESS; } /** * Create a new ECMA-119 node representing a special file. */ static int create_special(Ecma119Image *img, IsoSpecial *iso, Ecma119Node **node) { int ret; ret = create_ecma119_node(img, (IsoNode*)iso, node); if (ret < 0) { return ret; } (*node)->type = ECMA119_SPECIAL; return ISO_SUCCESS; } void ecma119_node_free(Ecma119Node *node) { if (node == NULL) { return; } if (node->type == ECMA119_DIR) { size_t i; for (i = 0; i < node->info.dir->nchildren; i++) { ecma119_node_free(node->info.dir->children[i]); } if (node->info.dir->children != NULL) free(node->info.dir->children); free(node->info.dir); } free(node->iso_name); iso_node_unref(node->node); free(node); } static int add_to_hidden_list(Ecma119Image *image, IsoFileSrc *src) { int ret; struct iso_filesrc_list_item *item; LIBISO_ALLOC_MEM(item, struct iso_filesrc_list_item, 1); item->src = src; item->next = image->ecma119_hidden_list; image->ecma119_hidden_list = item; ret = ISO_SUCCESS; ex: return ret; } int iso_filesrc_list_destroy(struct iso_filesrc_list_item **start_item) { struct iso_filesrc_list_item *item, *next; for (item = *start_item; item != NULL; item = next) { next = item->next; LIBISO_FREE_MEM(item); } return ISO_SUCCESS; } /** * @param flag * bit0= iso is in a hidden directory. Thus hide it. * @return * 1 success, 0 node ignored, < 0 error * */ static int create_tree(Ecma119Image *image, IsoNode *iso, Ecma119Node **tree, int depth, int pathlen, int flag) { int ret, hidden; Ecma119Node *node = NULL; int max_path; char *iso_name= NULL, *ipath = NULL; IsoFileSrc *src = NULL; IsoWriteOpts *opts; if (image == NULL || iso == NULL || tree == NULL) { return ISO_NULL_POINTER; } opts = image->opts; *tree = NULL; hidden = flag & 1; if (iso->hidden & LIBISO_HIDE_ON_RR) { hidden = 1; if (!((iso->hidden & LIBISO_HIDE_BUT_WRITE) || iso->type == LIBISO_BOOT)) { return 0; /* file will be ignored */ } } if (hidden) { max_path= pathlen; } else { ret = get_iso_name(image, iso, &iso_name); if (ret < 0) { iso_name = NULL; /* invalid, do not free */ goto ex; } max_path = pathlen + 1 + (iso_name ? strlen(iso_name) : 0); if (!opts->rockridge) { if ((iso->type == LIBISO_DIR && depth > 8) && !opts->allow_deep_paths) { ipath = iso_tree_get_node_path(iso); ret = iso_msg_submit(image->image->id, ISO_FILE_IMGPATH_WRONG, 0, "File \"%s\" can't be added, " "because directory depth " "is greater than 8.", ipath); goto ex; } else if (max_path > 255 && !opts->allow_longer_paths) { ipath = iso_tree_get_node_path(iso); ret = iso_msg_submit(image->image->id, ISO_FILE_IMGPATH_WRONG, 0, "File \"%s\" can't be added, " "because path length " "is greater than 255 characters", ipath); goto ex; } } } switch (iso->type) { case LIBISO_FILE: if (hidden) { ret = create_file_src(image, (IsoFile *) iso, &src); if (ret <= 0) goto ex; ret = add_to_hidden_list(image, src); } else { ret = create_file(image, (IsoFile*)iso, &node); } break; case LIBISO_SYMLINK: if (hidden) { ret = 0; /* Hidden means non-existing */ goto ex; } if (opts->rockridge) { ret = create_symlink(image, (IsoSymlink*)iso, &node); } else { /* symlinks are only supported when RR is enabled */ char *ipath = iso_tree_get_node_path(iso); ret = iso_msg_submit(image->image->id, ISO_FILE_IGNORED, 0, "File \"%s\" ignored. Symlinks need RockRidge extensions.", ipath); free(ipath); } break; case LIBISO_SPECIAL: if (hidden) { ret = 0; /* Hidden means non-existing */ goto ex; } if (opts->rockridge) { ret = create_special(image, (IsoSpecial*)iso, &node); } else { /* special files are only supported when RR is enabled */ char *ipath = iso_tree_get_node_path(iso); ret = iso_msg_submit(image->image->id, ISO_FILE_IGNORED, 0, "File \"%s\" ignored. Special files need RockRidge extensions.", ipath); free(ipath); } break; case LIBISO_BOOT: if (image->eltorito) { if (hidden) { ret = el_torito_catalog_file_src_create(image, &src); if (ret <= 0) goto ex; ret = add_to_hidden_list(image, src); } else { ret = create_boot_cat(image, (IsoBoot*)iso, &node); } } else { /* log and ignore */ ret = iso_msg_submit(image->image->id, ISO_FILE_IGNORED, 0, "El-Torito catalog found on a image without El-Torito."); } break; case LIBISO_DIR: { IsoNode *pos; IsoDir *dir = (IsoDir*)iso; if (!hidden) { ret = create_dir(image, dir, &node); if (ret < 0) { goto ex; } if (depth == 1) { /* root is default */ image->rr_reloc_node = node; } else if (depth == 2) { /* Directories in root may be used as relocation dir */ if (opts->rr_reloc_dir != NULL) if (opts->rr_reloc_dir[0] != 0 && strcmp(iso->name, opts->rr_reloc_dir) == 0) image->rr_reloc_node = node; } } ret = ISO_SUCCESS; pos = dir->children; while (pos) { int cret; Ecma119Node *child; cret = create_tree(image, pos, &child, depth + 1, max_path, !!hidden); if (cret < 0) { /* error */ ret = cret; break; } else if (cret == ISO_SUCCESS && !hidden) { /* add child to this node */ int nchildren = node->info.dir->nchildren++; node->info.dir->children[nchildren] = child; child->parent = node; } pos = pos->next; } } break; default: /* should never happen */ ret = ISO_ASSERT_FAILURE; goto ex; } if (ret <= 0) { goto ex; } if (!hidden) { node->iso_name = iso_name; iso_name = NULL; /* now owned by node, do not free */ *tree = node; node = NULL; /* now owned by caller, do not free */ } ret = ISO_SUCCESS; ex: if (iso_name != NULL) free(iso_name); if (ipath != NULL) free(ipath); if (node != NULL) ecma119_node_free(node); if (hidden && ret == ISO_SUCCESS) ret = 0; /* The sources of hidden files are now owned by the rb-tree */ return ret; } /** * Compare the iso name of two ECMA-119 nodes */ static int cmp_node_name(const void *f1, const void *f2) { Ecma119Node *f = *((Ecma119Node**)f1); Ecma119Node *g = *((Ecma119Node**)f2); return strcmp(f->iso_name, g->iso_name); } /** * Compare the iso name of two ECMA-119 nodes, without equivalences. * Nodes with equal iso names are instead ordered by their real names. * * This is used to make the initial tree sort deterministic, before * iso names are mangled to become unique. */ static int cmp_node_name_tiebreak(const void *f1, const void *f2) { Ecma119Node *f, *g; int cmp; cmp = cmp_node_name(f1, f2); if (cmp) return cmp; f = *((Ecma119Node**)f1); g = *((Ecma119Node**)f2); return strcmp(f->node->name, g->node->name); } /** * Sorts a the children of each directory in the ECMA-119 tree represented * by \p root, according to the order specified in ECMA-119, section 9.3. */ static void sort_tree(Ecma119Node *root) { size_t i; if (root->info.dir->children == NULL) return; qsort(root->info.dir->children, root->info.dir->nchildren, sizeof(void*), cmp_node_name_tiebreak); for (i = 0; i < root->info.dir->nchildren; i++) { if (root->info.dir->children[i]->type == ECMA119_DIR) sort_tree(root->info.dir->children[i]); } } /** * Ensures that the ISO name of each children of the given dir is unique, * changing some of them if needed. * It also ensures that resulting filename is always <= than given * max_name_len, including extension. If needed, the extension will be reduced, * but never under 3 characters. */ static int mangle_single_dir(Ecma119Image *img, Ecma119Node *dir, int max_file_len, int max_dir_len) { int ret; int i, nchildren; Ecma119Node **children; IsoHTable *table; int need_sort = 0; nchildren = dir->info.dir->nchildren; children = dir->info.dir->children; if (nchildren <= 0) return ISO_SUCCESS; /* nothing to do */ /* a hash table will temporary hold the names, for fast searching */ ret = iso_htable_create((nchildren * 100) / 80, iso_str_hash, (compare_function_t)strcmp, &table); if (ret < 0) { return ret; } for (i = 0; i < nchildren; ++i) { char *name = children[i]->iso_name; ret = iso_htable_add(table, name, name); if (ret < 0) { goto mangle_cleanup; } } for (i = 0; i < nchildren; ++i) { char *name, *ext; char full_name[40]; const int full_max_len = 40 - 1; int max; /* computed max len for name, without extension */ int j = i; int digits = 1; /* characters to change per name */ /* first, find all child with same name */ while (j + 1 < nchildren && !cmp_node_name(children + i, children + j + 1)) { ++j; } if (j == i) { /* name is unique */ continue; } if (img->opts->untranslated_name_len) { /* This should not happen because no two IsoNode names should be identical and only unaltered IsoNode names should be seen here. Thus the Ema119Node names should be unique. */ iso_msg_submit(img->image->id, ISO_NAME_NEEDS_TRANSL, 0, "ECMA-119 file name collision: '%s'", children[i]->iso_name); ret = ISO_NAME_NEEDS_TRANSL; goto mangle_cleanup; } /* * A max of 7 characters is good enough, it allows handling up to * 9,999,999 files with same name. We can increment this to * max_name_len, but the int_pow() function must then be modified * to return a bigger integer. */ while (digits < 8) { int ok, k; char *dot; int change = 0; /* number to be written */ /* copy name to buffer */ strncpy(full_name, children[i]->iso_name, full_max_len); full_name[full_max_len] = 0; /* compute name and extension */ dot = strrchr(full_name, '.'); if (dot != NULL && (children[i]->type != ECMA119_DIR || img->opts->allow_dir_id_ext)) { /* * File (normally not dir) with extension * Note that we don't need to check for placeholders, as * tree reparent happens later, so no placeholders can be * here at this time. */ int extlen; full_name[dot - full_name] = '\0'; name = full_name; ext = dot + 1; /* * For iso level 1 we force ext len to be 3, as name * can't grow on the extension space */ extlen = (max_file_len == 12) ? 3 : strlen(ext); max = max_file_len - extlen - 1 - digits; if (max <= 0) { /* this can happen if extension is too long */ if (extlen + max > 3) { /* * reduce extension len, to give name an extra char * note that max is negative or 0 */ extlen = extlen + max - 1; ext[extlen] = '\0'; max = max_file_len - extlen - 1 - digits; } else { /* * error, we don't support extensions < 3 * This can't happen with current limit of digits. */ ret = ISO_ERROR; goto mangle_cleanup; } } /* ok, reduce name by digits */ if (name + max < dot) { name[max] = '\0'; } } else { /* Directory (normally), or file without extension */ if (children[i]->type == ECMA119_DIR) { max = max_dir_len - digits; dot = NULL; /* dots (normally) have no meaning in dirs */ } else { max = max_file_len - digits; } name = full_name; if ((size_t) max < strlen(name)) { name[max] = '\0'; } /* let ext be an empty string */ ext = name + strlen(name); } ok = 1; /* change name of each file */ for (k = i; k <= j; ++k) { char tmp[40]; char fmt[16]; if (dot != NULL) { sprintf(fmt, "%%s%%0%dd.%%s", digits); } else { sprintf(fmt, "%%s%%0%dd%%s", digits); } while (1) { sprintf(tmp, fmt, name, change, ext); ++change; if (change > int_pow(10, digits)) { ok = 0; break; } if (!iso_htable_get(table, tmp, NULL)) { /* the name is unique, so it can be used */ break; } } if (ok) { char *new = strdup(tmp); if (new == NULL) { ret = ISO_OUT_OF_MEM; goto mangle_cleanup; } #ifdef Libisofs_extra_verbose_debuG iso_msg_debug(img->image->id, "\"%s\" renamed to \"%s\"", children[k]->iso_name, new); #endif iso_htable_remove_ptr(table, children[k]->iso_name, NULL); free(children[k]->iso_name); children[k]->iso_name = new; iso_htable_add(table, new, new); /* * if we change a name we need to sort again children * at the end */ need_sort = 1; } else { /* we need to increment digits */ break; } } if (ok) { break; } else { ++digits; } } if (digits == 8) { ret = ISO_MANGLE_TOO_MUCH_FILES; goto mangle_cleanup; } i = j; } /* * If needed, sort again the files inside dir */ if (need_sort) { qsort(children, nchildren, sizeof(void*), cmp_node_name); } ret = ISO_SUCCESS; mangle_cleanup : ; iso_htable_destroy(table, NULL); return ret; } static int mangle_dir(Ecma119Image *img, Ecma119Node *dir, int max_file_len, int max_dir_len) { int ret; size_t i; ret = mangle_single_dir(img, dir, max_file_len, max_dir_len); if (ret < 0) { return ret; } /* recurse */ for (i = 0; i < dir->info.dir->nchildren; ++i) { if (dir->info.dir->children[i]->type == ECMA119_DIR) { ret = mangle_dir(img, dir->info.dir->children[i], max_file_len, max_dir_len); if (ret < 0) { /* error */ return ret; } } } return ISO_SUCCESS; } static int mangle_tree(Ecma119Image *img, Ecma119Node *dir, int recurse) { int max_file, max_dir; Ecma119Node *root; if (img->opts->untranslated_name_len > 0) { max_file = max_dir = img->opts->untranslated_name_len; } else if (img->opts->max_37_char_filenames) { max_file = max_dir = 37; } else if (img->opts->iso_level == 1) { max_file = 12; /* 8 + 3 + 1 */ max_dir = 8; } else { max_file = max_dir = 31; } if (dir != NULL) { root = dir; } else if (img->eff_partition_offset > 0) { root = img->partition_root; } else { root = img->root; } if (recurse) { return mangle_dir(img, root, max_file, max_dir); } else { return mangle_single_dir(img, root, max_file, max_dir); } } /** * Create a new ECMA-119 node representing a placeholder for a relocated * dir. * * See IEEE P1282, section 4.1.5 for details */ static int create_placeholder(Ecma119Node *parent, Ecma119Node *real, Ecma119Node **node) { Ecma119Node *ret; ret = calloc(1, sizeof(Ecma119Node)); if (ret == NULL) { return ISO_OUT_OF_MEM; } /* * TODO * If real is a dir, while placeholder is a file, ISO name restricctions * are different, what to do? */ ret->iso_name = strdup(real->iso_name); if (ret->iso_name == NULL) { free(ret); return ISO_OUT_OF_MEM; } /* take a ref to the IsoNode */ ret->node = real->node; iso_node_ref(real->node); ret->parent = parent; ret->type = ECMA119_PLACEHOLDER; ret->info.real_me = real; ret->ino = real->ino; ret->nlink = real->nlink; *node = ret; return ISO_SUCCESS; } static size_t max_child_name_len(Ecma119Node *dir) { size_t ret = 0, i; for (i = 0; i < dir->info.dir->nchildren; i++) { size_t len = strlen(dir->info.dir->children[i]->iso_name); ret = MAX(ret, len); } return ret; } /** * Relocates a directory, as specified in Rock Ridge Specification * (see IEEE P1282, section 4.1.5). This is needed when the number of levels * on a directory hierarchy exceeds 8, or the length of a path is higher * than 255 characters, as specified in ECMA-119, section 6.8.2.1 */ static int reparent(Ecma119Node *child, Ecma119Node *parent) { int ret; size_t i; Ecma119Node *placeholder; /* replace the child in the original parent with a placeholder */ for (i = 0; i < child->parent->info.dir->nchildren; i++) { if (child->parent->info.dir->children[i] == child) { ret = create_placeholder(child->parent, child, &placeholder); if (ret < 0) { return ret; } child->parent->info.dir->children[i] = placeholder; break; } } /* just for debug, this should never happen... */ if (i == child->parent->info.dir->nchildren) { return ISO_ASSERT_FAILURE; } /* keep track of the real parent */ child->info.dir->real_parent = child->parent; /* add the child to its new parent */ child->parent = parent; parent->info.dir->nchildren++; parent->info.dir->children = realloc(parent->info.dir->children, sizeof(void*) * parent->info.dir->nchildren); parent->info.dir->children[parent->info.dir->nchildren - 1] = child; return ISO_SUCCESS; } /** * Reorder the tree, if necessary, to ensure that * - the depth is at most 8 * - each path length is at most 255 characters * This restriction is imposed by ECMA-119 specification (ECMA-119, 6.8.2.1). * * @param dir * Dir we are currently processing * @param level * Level of the directory in the hierarchy * @param pathlen * Length of the path until dir, including it * @return * 1 success, < 0 error */ static int reorder_tree(Ecma119Image *img, Ecma119Node *dir, int dir_level, int dir_pathlen) { int ret, level, pathlen, newpathlen; size_t max_path, i; Ecma119Node *reloc, *child; /* might change by relocation */ level = dir_level; pathlen = dir_pathlen; max_path = pathlen + 1 + max_child_name_len(dir); if (level > 8 || max_path > 255) { reloc = img->rr_reloc_node; if (reloc == NULL) { if (img->eff_partition_offset > 0) { reloc = img->partition_root; } else { reloc = img->root; } } ret = reparent(dir, reloc); if (ret < 0) { return ret; } if (reloc == img->root || reloc == img->partition_root) { /* * we are appended to the root's children now, so there is no * need to recurse (the root will hit us again) */ return ISO_SUCCESS; } /* dir is now the relocated Ecma119Node */ pathlen = 37 + 1; /* The dir name might get longer by mangling */ level = 2; if (img->opts->rr_reloc_dir != NULL) { pathlen += strlen(img->rr_reloc_node->iso_name) + 1; if(img->opts->rr_reloc_dir[0] != 0) level = 3; } } if (ecma119_is_dedicated_reloc_dir(img, (Ecma119Node *) dir)) return ISO_SUCCESS; for (i = 0; i < dir->info.dir->nchildren; i++) { child = dir->info.dir->children[i]; if (child->type == ECMA119_DIR) { newpathlen = pathlen + 1 + strlen(child->iso_name); ret = reorder_tree(img, child, level + 1, newpathlen); if (ret < 0) return ret; } } return ISO_SUCCESS; } /* * @param flag * bit0= recursion * bit1= count nodes rather than fill them into *nodes * @return * <0 error * bit0= saw ino == 0 * bit1= saw ino != 0 */ static int make_node_array(Ecma119Image *img, Ecma119Node *dir, Ecma119Node **nodes, size_t nodes_size, size_t *node_count, int flag) { int ret, result = 0; size_t i; Ecma119Node *child; if (!(flag & 1)) { *node_count = 0; if (!(flag & 2)) { /* Register the tree root node */ if (*node_count >= nodes_size) { iso_msg_submit(img->image->id, ISO_ASSERT_FAILURE, 0, "Programming error: Overflow of hardlink sort array"); return ISO_ASSERT_FAILURE; } nodes[*node_count] = dir; } result|= (dir->ino == 0 ? 1 : 2); (*node_count)++; } for (i = 0; i < dir->info.dir->nchildren; i++) { child = dir->info.dir->children[i]; if (!(flag & 2)) { if (*node_count >= nodes_size) { iso_msg_submit(img->image->id, ISO_ASSERT_FAILURE, 0, "Programming error: Overflow of hardlink sort array"); return ISO_ASSERT_FAILURE; } nodes[*node_count] = child; } result|= (child->ino == 0 ? 1 : 2); (*node_count)++; if (child->type == ECMA119_DIR) { ret = make_node_array(img, child, nodes, nodes_size, node_count, flag | 1); if (ret < 0) return ret; } } return result; } /* * @param flag * bit0= compare stat properties and attributes * bit1= treat all nodes with image ino == 0 as unique */ static int ecma119_node_cmp_flag(const void *v1, const void *v2, int flag) { int ret; Ecma119Node *n1, *n2; n1 = *((Ecma119Node **) v1); n2 = *((Ecma119Node **) v2); if (n1 == n2) return 0; ret = iso_node_cmp_flag(n1->node, n2->node, flag & (1 | 2)); return ret; } static int ecma119_node_cmp_hard(const void *v1, const void *v2) { return ecma119_node_cmp_flag(v1, v2, 1); } static int ecma119_node_cmp_nohard(const void *v1, const void *v2) { return ecma119_node_cmp_flag(v1, v2, 1 | 2); } static int family_set_ino(Ecma119Image *img, Ecma119Node **nodes, size_t family_start, size_t next_family, ino_t img_ino, ino_t prev_ino, int flag) { size_t i; if (img_ino != 0) { /* Check whether this is the same img_ino as in the previous family (e.g. by property divergence of imported hardlink). */ if (img_ino == prev_ino) img_ino = 0; /* Accept only if it is within the 32 bit range. */ if (((uint64_t) img_ino) > 0xffffffff) img_ino = 0; } if (img_ino == 0) { img_ino = img_give_ino_number(img->image, 0); } for (i = family_start; i < next_family; i++) { nodes[i]->ino = img_ino; nodes[i]->nlink = next_family - family_start; } return 1; } static int match_hardlinks(Ecma119Image *img, Ecma119Node *dir, int flag) { int ret; size_t nodes_size = 0, node_count = 0, i, family_start; Ecma119Node **nodes = NULL; unsigned int fs_id; dev_t dev_id; ino_t img_ino = 0, prev_ino = 0; ret = make_node_array(img, dir, nodes, nodes_size, &node_count, 2); if (ret < 0) return ret; nodes_size = node_count; nodes = (Ecma119Node **) calloc(sizeof(Ecma119Node *), nodes_size); if (nodes == NULL) return ISO_OUT_OF_MEM; ret = make_node_array(img, dir, nodes, nodes_size, &node_count, 0); if (ret < 0) goto ex; /* Sort according to id tuples, IsoFileSrc identity, properties, xattr. */ if (img->opts->hardlinks) qsort(nodes, node_count, sizeof(Ecma119Node *), ecma119_node_cmp_hard); else qsort(nodes, node_count, sizeof(Ecma119Node *), ecma119_node_cmp_nohard); /* Hand out image inode numbers to all Ecma119Node.ino == 0 . Same sorting rank gets same inode number. Split those image inode number families where the sort criterion differs. */ iso_node_get_id(nodes[0]->node, &fs_id, &dev_id, &img_ino, 1); family_start = 0; for (i = 1; i < node_count; i++) { if (nodes[i]->type != ECMA119_DIR && ecma119_node_cmp_hard(nodes + (i - 1), nodes + i) == 0) { /* Still in same ino family */ if (img_ino == 0) { /* Just in case any member knows its img_ino */ iso_node_get_id(nodes[0]->node, &fs_id, &dev_id, &img_ino, 1); } continue; } family_set_ino(img, nodes, family_start, i, img_ino, prev_ino, 0); prev_ino = img_ino; iso_node_get_id(nodes[i]->node, &fs_id, &dev_id, &img_ino, 1); family_start = i; } family_set_ino(img, nodes, family_start, i, img_ino, prev_ino, 0); ret = ISO_SUCCESS; ex:; if (nodes != NULL) free((char *) nodes); return ret; } int ecma119_tree_create(Ecma119Image *img) { int ret; Ecma119Node *root; ret = create_tree(img, (IsoNode*)img->image->root, &root, 1, 0, 0); if (ret <= 0) { if (ret == 0) { /* unexpected error, root ignored!! This can't happen */ ret = ISO_ASSERT_FAILURE; } return ret; } if (img->eff_partition_offset > 0) { img->partition_root = root; } else { img->root = root; } iso_msg_debug(img->image->id, "Matching hardlinks..."); ret = match_hardlinks(img, root, 0); if (ret < 0) { return ret; } iso_msg_debug(img->image->id, "Sorting the low level tree..."); sort_tree(root); iso_msg_debug(img->image->id, "Mangling names..."); ret = mangle_tree(img, NULL, 1); if (ret < 0) { return ret; } if (img->opts->rockridge && !img->opts->allow_deep_paths) { /* Relocate deep directories, according to RRIP, 4.1.5 */ ret = reorder_tree(img, root, 1, 0); if (ret < 0) { return ret; } /* * and we need to remangle the root directory, as the function * above could insert new directories into the relocation directory. * Note that recurse = 0, as we don't need to recurse. */ ret = mangle_tree(img, img->rr_reloc_node, 0); if (ret < 0) { return ret; } } return ISO_SUCCESS; } /** * Search the tree for a certain IsoNode and return its owning Ecma119Node * or NULL. */ static Ecma119Node *search_iso_node(Ecma119Node *root, IsoNode *node) { size_t i; Ecma119Node *res = NULL; if (root->node == node) return root; for (i = 0; i < root->info.dir->nchildren && res == NULL; i++) { if (root->info.dir->children[i]->type == ECMA119_DIR) res = search_iso_node(root->info.dir->children[i], node); else if (root->info.dir->children[i]->node == node) res = root->info.dir->children[i]; } return res; } Ecma119Node *ecma119_search_iso_node(Ecma119Image *img, IsoNode *node) { Ecma119Node *res = NULL; if (img->root != NULL) res = search_iso_node(img->root, node); return res; } /* * Copyright (c) 2007 Vreixo Formoso * 2012 - 2014 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_ECMA119_TREE_H_ #define LIBISO_ECMA119_TREE_H_ #include "libisofs.h" #include "ecma119.h" enum ecma119_node_type { ECMA119_FILE, ECMA119_DIR, ECMA119_SYMLINK, ECMA119_SPECIAL, ECMA119_PLACEHOLDER }; /** * Struct with info about a node representing a directory */ struct ecma119_dir_info { /* Block where the directory entries will be written on image */ size_t block; size_t nchildren; Ecma119Node **children; /* * Size of the dir, i.e., sum of the lengths of all directory records. * It is computed by calc_dir_size() [ecma119.c]. * Note that this don't include the length of any SUSP Continuation * Area needed by the dir, but it includes the size of the SUSP entries * than fit in the directory records System Use Field. */ size_t len; /** * Real parent if the dir has been reallocated. NULL otherwise. */ Ecma119Node *real_parent; }; /** * A node for a tree containing all the information necessary for writing * an ISO9660 volume. */ struct ecma119_node { /** * Name in ASCII, conforming to selected ISO level. * Version number is not include, it is added on the fly */ char *iso_name; Ecma119Node *parent; IsoNode *node; /*< reference to the iso node */ uint32_t ino; nlink_t nlink; /**< file, symlink, special, directory or placeholder */ enum ecma119_node_type type; union { IsoFileSrc *file; struct ecma119_dir_info *dir; /** this field points to the relocated directory. */ Ecma119Node *real_me; } info; }; /* For recording files which are hidden in ECMA-119 */ struct iso_filesrc_list_item { IsoFileSrc *src; struct iso_filesrc_list_item *next; }; int iso_filesrc_list_destroy(struct iso_filesrc_list_item **start_item); /** * */ int ecma119_tree_create(Ecma119Image *img); /** * Free an Ecma119Node, and its children if node is a dir */ void ecma119_node_free(Ecma119Node *node); /** * Search the tree for a certain IsoNode and return its owning Ecma119Node * or NULL. */ Ecma119Node *ecma119_search_iso_node(Ecma119Image *img, IsoNode *node); /** * Tell whether node is a dedicated relocation directory which only contains * relocated directories. */ int ecma119_is_dedicated_reloc_dir(Ecma119Image *img, Ecma119Node *node); /** * Determines the ECMA-119 name from node name. * @param flag bit0= Do not issue error messages */ int iso_get_ecma119_name(IsoWriteOpts *opts, char *input_charset, int imgid, char *node_name, enum IsoNodeType node_type, char **name, int flag); #endif /*LIBISO_ECMA119_TREE_H_*/ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2010 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libisofs.h" #include "eltorito.h" #include "fsource.h" #include "filesrc.h" #include "image.h" #include "messages.h" #include "writer.h" #include "ecma119.h" #include #include #include /** * This table should be written with the actual values at offset * 8 of boot image, when used ISOLINUX boot loader */ struct boot_info_table { uint8_t bi_pvd BP(1, 4); /* LBA of primary volume descriptor */ uint8_t bi_file BP(5, 8); /* LBA of boot file */ uint8_t bi_length BP(9, 12); /* Length of boot file */ uint8_t bi_csum BP(13, 16); /* Checksum of boot file */ uint8_t bi_reserved BP(17, 56); /* Reserved */ }; /** * Structure for each one of the four entries in a partition table on a * hard disk image. */ struct partition_desc { uint8_t boot_ind; uint8_t begin_chs[3]; uint8_t type; uint8_t end_chs[3]; uint8_t start[4]; uint8_t size[4]; }; /** * Structures for a Master Boot Record of a hard disk image. */ struct hard_disc_mbr { uint8_t code_area[440]; uint8_t opt_disk_sg[4]; uint8_t pad[2]; struct partition_desc partition[4]; uint8_t sign1; uint8_t sign2; }; /* API */ int el_torito_set_boot_platform_id(ElToritoBootImage *bootimg, uint8_t id) { bootimg->platform_id = id; return 1; } /* API */ int el_torito_get_boot_platform_id(ElToritoBootImage *bootimg) { return bootimg->platform_id; } /** * Sets the load segment for the initial boot image. This is only for * no emulation boot images, and is a NOP for other image types. */ void el_torito_set_load_seg(ElToritoBootImage *bootimg, short segment) { if (bootimg->type != 0) return; if (segment < 0) bootimg->load_seg = 0x1000 + segment; else bootimg->load_seg = segment; } /* API */ int el_torito_get_load_seg(ElToritoBootImage *bootimg) { return (int) bootimg->load_seg; } /** * Sets the number of sectors (512b) to be load at load segment during * the initial boot procedure. This is only for no emulation boot images, * and is a NOP for other image types. */ void el_torito_set_load_size(ElToritoBootImage *bootimg, short sectors) { if (bootimg->type != 0) return; if (sectors < 0) bootimg->load_size = 0x10000 + sectors; else bootimg->load_size = sectors; } /* API */ int el_torito_get_load_size(ElToritoBootImage *bootimg) { return (int) bootimg->load_size; } /* API */ void el_torito_set_full_load(ElToritoBootImage *bootimg, int mode) { if (bootimg->type != 0) return; bootimg->load_size_full= !!mode; } /* API */ int el_torito_get_full_load(ElToritoBootImage *bootimg) { return bootimg->load_size_full; } /** * Marks the specified boot image as not bootable */ void el_torito_set_no_bootable(ElToritoBootImage *bootimg) { bootimg->bootable = 0; } /* API */ int el_torito_get_bootable(ElToritoBootImage *bootimg) { return !!bootimg->bootable; } /* API */ int el_torito_set_id_string(ElToritoBootImage *bootimg, uint8_t id_string[28]) { memcpy(bootimg->id_string, id_string, 28); return 1; } /* API */ int el_torito_get_id_string(ElToritoBootImage *bootimg, uint8_t id_string[28]) { memcpy(id_string, bootimg->id_string, 28); return 1; } /* API */ int el_torito_set_selection_crit(ElToritoBootImage *bootimg, uint8_t crit[20]) { memcpy(bootimg->selection_crit, crit, 20); return 1; } /* API */ int el_torito_get_selection_crit(ElToritoBootImage *bootimg, uint8_t crit[20]) { memcpy(crit, bootimg->selection_crit, 20); return 1; } /* API */ int el_torito_seems_boot_info_table(ElToritoBootImage *bootimg, int flag) { switch (flag & 15) { case 0: return bootimg->seems_boot_info_table; case 1: return bootimg->seems_grub2_boot_info; } return 0; } /** * Specifies that this image needs to be patched. This involves the writing * of a 56 bytes boot information table at offset 8 of the boot image file. * The original boot image file won't be modified. * This is needed for isolinux boot images. */ void el_torito_patch_isolinux_image(ElToritoBootImage *bootimg) { bootimg->isolinux_options |= 0x01; } /** * Specifies options for IsoLinux boot images. This should only be used with * isolinux boot images. * * @param options * bitmask style flag. The following values are defined: * * bit 0 -> 1 to path the image, 0 to not * Patching the image involves the writing of a 56 bytes * boot information table at offset 8 of the boot image file. * The original boot image file won't be modified. This is needed * to allow isolinux images to be bootable. * bit 1 -> 1 to generate an hybrid image, 0 to not * An hybrid image is a boot image that boots from either CD/DVD * media or from USB sticks. For that, you should use an isolinux * image that supports hybrid mode. Recent images support this. * @return * 1 if success, < 0 on error * @since 0.6.12 */ int el_torito_set_isolinux_options(ElToritoBootImage *bootimg, int options, int flag) { bootimg->isolinux_options = (options & 0x03ff); bootimg->seems_boot_info_table = !!(options & 1); bootimg->seems_grub2_boot_info = !!(options & (1 << 9)); return ISO_SUCCESS; } /* API */ int el_torito_get_isolinux_options(ElToritoBootImage *bootimg, int flag) { return bootimg->isolinux_options & 0x03ff; } /* API */ int el_torito_get_boot_media_type(ElToritoBootImage *bootimg, enum eltorito_boot_media_type *media_type) { if (bootimg) { switch (bootimg->type) { case 1: case 2: case 3: *media_type = ELTORITO_FLOPPY_EMUL; return 1; case 4: *media_type = ELTORITO_HARD_DISC_EMUL; return 1; case 0: *media_type = ELTORITO_NO_EMUL; return 1; default: /* should never happen */ return ISO_ASSERT_FAILURE; break; } } return ISO_WRONG_ARG_VALUE; } static int iso_tree_add_boot_node(IsoDir *parent, const char *name, IsoBoot **boot) { IsoBoot *node; IsoNode **pos; time_t now; int ret; if (parent == NULL || name == NULL || boot == NULL) { return ISO_NULL_POINTER; } if (boot) { *boot = NULL; } /* check if the name is valid */ ret = iso_node_is_valid_name(name); if (ret < 0) return ret; /* find place where to insert */ pos = &(parent->children); while (*pos != NULL && strcmp((*pos)->name, name) < 0) { pos = &((*pos)->next); } if (*pos != NULL && !strcmp((*pos)->name, name)) { /* a node with same name already exists */ return ISO_NODE_NAME_NOT_UNIQUE; } node = calloc(1, sizeof(IsoBoot)); if (node == NULL) { return ISO_OUT_OF_MEM; } node->node.refcount = 1; node->node.type = LIBISO_BOOT; node->node.name = strdup(name); if (node->node.name == NULL) { free(node); return ISO_OUT_OF_MEM; } node->lba = 0; node->size = 0; node->content = NULL; /* attributes from parent */ node->node.mode = S_IFREG | (parent->node.mode & 0444); node->node.uid = parent->node.uid; node->node.gid = parent->node.gid; node->node.hidden = parent->node.hidden; /* current time */ iso_nowtime(&now, 0); node->node.atime = now; node->node.ctime = now; node->node.mtime = now; /* add to dir */ node->node.parent = parent; node->node.next = *pos; *pos = (IsoNode*)node; if (boot) { *boot = node; } return ++parent->nchildren; } /* Get start and size from "%d_start_%lus_size_%lud" */ static void iso_parse_start_size(char *text, unsigned long *part_start, unsigned long *part_size) { char *cpt; unsigned long start, size; cpt = strchr(text, '_'); if (cpt == NULL) return; if (strncmp(cpt, "_start_", 7) != 0) return; sscanf(cpt + 7, "%lu", &start); cpt = strchr(cpt + 7, '_'); if (cpt == NULL) return; if (*(cpt - 1) != 's') return; if (strncmp(cpt, "_size_", 6) != 0) return; sscanf(cpt + 6, "%lu", &size); for (cpt = cpt + 6; *cpt >= '0' && *cpt <= '9'; cpt++); if (*cpt != 'd') return; *part_start = start; *part_size = size; } static int create_image(IsoImage *image, const char *image_path, enum eltorito_boot_media_type type, struct el_torito_boot_image **bootimg, IsoFile **bootnode) { int ret; struct el_torito_boot_image *boot; int boot_media_type = 0; int load_sectors = 0; /* number of sector to load */ int part_idx = -1; unsigned long part_start = 0, part_size = 0; unsigned char partition_type = 0; off_t size; IsoNode *imgfile = NULL; IsoStream *stream = NULL; *bootnode = NULL; if (strncmp(image_path, "--interval:appended_partition_", 30) == 0) { /* --interval:appended_partition_N... */ if (type != ELTORITO_NO_EMUL) { /* >>> ??? lift this ban by making a temporary IsoStream from partition source, determine size, and read ELTORITO_HARD_DISC_EMUL MBR ? */ iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Appended partition cannot serve as El Torito boot image with FD/HD emulation"); return ISO_BOOT_IMAGE_NOT_VALID; } sscanf(image_path + 30, "%d", &part_idx); if (part_idx < 1 || part_idx > ISO_MAX_PARTITIONS) { iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Appended partition index for El Torito boot image is out of range"); return ISO_BOOT_IMAGE_NOT_VALID; } iso_parse_start_size((char *) (image_path + 30), &part_start, &part_size); part_idx--; size = 1; } else { ret = iso_tree_path_to_node(image, image_path, &imgfile); if (ret < 0) { return ret; } if (ret == 0) { iso_msg_submit(image->id, ISO_NODE_DOESNT_EXIST, 0, "El Torito boot image file missing in ISO image: '%s'", image_path); return ISO_NODE_DOESNT_EXIST; } if (imgfile->type != LIBISO_FILE) { return ISO_BOOT_IMAGE_NOT_VALID; } *bootnode = (IsoFile *) imgfile; stream = ((IsoFile*)imgfile)->stream; /* we need to read the image at least two times */ if (!iso_stream_is_repeatable(stream)) { return ISO_BOOT_IMAGE_NOT_VALID; } size = iso_stream_get_size(stream); } if (size <= 0) { iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Boot image file is empty"); return ISO_BOOT_IMAGE_NOT_VALID; } switch (type) { case ELTORITO_FLOPPY_EMUL: switch (size) { case 1200 * 1024: boot_media_type = 1; /* 1.2 meg diskette */ break; case 1440 * 1024: boot_media_type = 2; /* 1.44 meg diskette */ break; case 2880 * 1024: boot_media_type = 3; /* 2.88 meg diskette */ break; default: iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Invalid image size %d Kb. Must be one of 1.2, 1.44" "or 2.88 Mb", iso_stream_get_size(stream) / 1024); return ISO_BOOT_IMAGE_NOT_VALID; break; } /* it seems that for floppy emulation we need to load * a single sector (512b) */ load_sectors = 1; break; case ELTORITO_HARD_DISC_EMUL: { size_t i; struct hard_disc_mbr mbr; int used_partition; /* read the MBR on disc and get the type of the partition */ ret = iso_stream_open(stream); if (ret < 0) { iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, ret, "Can't open image file."); return ret; } ret = iso_stream_read(stream, &mbr, sizeof(mbr)); iso_stream_close(stream); if (ret != sizeof(mbr)) { iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Can't read MBR from image file."); return ret < 0 ? ret : (int) ISO_FILE_READ_ERROR; } /* check valid MBR signature */ if ( mbr.sign1 != 0x55 || mbr.sign2 != 0xAA ) { iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Invalid MBR. Wrong signature."); return (int) ISO_BOOT_IMAGE_NOT_VALID; } /* ensure single partition */ used_partition = -1; for (i = 0; i < 4; ++i) { if (mbr.partition[i].type != 0) { /* it's an used partition */ if (used_partition != -1) { iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Invalid MBR. At least 2 partitions: %d and " "%d, are being used\n", used_partition, i); return ISO_BOOT_IMAGE_NOT_VALID; } else used_partition = i; } } partition_type = mbr.partition[used_partition].type; } boot_media_type = 4; /* only load the MBR */ load_sectors = 1; break; case ELTORITO_NO_EMUL: boot_media_type = 0; break; } boot = calloc(1, sizeof(struct el_torito_boot_image)); if (boot == NULL) { return ISO_OUT_OF_MEM; } boot->image = (IsoFile*)imgfile; boot->appended_idx = part_idx; boot->appended_start = part_start; boot->appended_size = part_size; if (imgfile != NULL) iso_node_ref(imgfile); /* get our ref */ boot->bootable = 1; boot->seems_boot_info_table = 0; boot->seems_grub2_boot_info = 0; boot->seems_isohybrid_capable = 0; boot->isolinux_options = 0; boot->type = boot_media_type; boot->partition_type = partition_type; boot->load_seg = 0; boot->load_size = load_sectors; boot->load_size_full = 0; boot->platform_id = 0; /* 80x86 */ memset(boot->id_string, 0, sizeof(boot->id_string)); memset(boot->selection_crit, 0, sizeof(boot->selection_crit)); *bootimg = boot; return ISO_SUCCESS; } int iso_image_set_boot_image(IsoImage *image, const char *image_path, enum eltorito_boot_media_type type, const char *catalog_path, ElToritoBootImage **boot) { int ret, i; struct el_torito_boot_catalog *catalog; ElToritoBootImage *boot_image= NULL; IsoBoot *cat_node= NULL; IsoFile *boot_node; if (image == NULL || image_path == NULL || catalog_path == NULL) { return ISO_NULL_POINTER; } if (image->bootcat != NULL) { return ISO_IMAGE_ALREADY_BOOTABLE; } /* create the node for the catalog */ { IsoDir *parent; char *catdir = NULL, *catname = NULL; catdir = strdup(catalog_path); if (catdir == NULL) { return ISO_OUT_OF_MEM; } /* get both the dir and the name */ catname = strrchr(catdir, '/'); if (catname == NULL) catname = catdir; if (catname == catdir) { /* we are appending catalog to root node */ parent = image->root; } else { IsoNode *p; catname[0] = '\0'; ret = iso_tree_path_to_node(image, catdir, &p); if (ret <= 0) { iso_msg_submit(image->id, ISO_NODE_DOESNT_EXIST, 0, "Cannot find directory for El Torito boot catalog in ISO image: '%s'", catdir); free(catdir); return ret < 0 ? ret : (int) ISO_NODE_DOESNT_EXIST; } if (p->type != LIBISO_DIR) { free(catdir); return ISO_WRONG_ARG_VALUE; } parent = (IsoDir*)p; } if (catname[0] == '/' || catname[0] == 0) catname++; ret = iso_tree_add_boot_node(parent, catname, &cat_node); free(catdir); if (ret < 0) { return ret; } } /* create the boot image */ ret = create_image(image, image_path, type, &boot_image, &boot_node); if (ret < 0) { goto boot_image_cleanup; } /* creates the catalog with the given image */ catalog = calloc(1, sizeof(struct el_torito_boot_catalog)); if (catalog == NULL) { ret = ISO_OUT_OF_MEM; goto boot_image_cleanup; } catalog->num_bootimages = 1; catalog->bootimages[0] = boot_image; for (i = 1; i < Libisofs_max_boot_imageS; i++) catalog->bootimages[i] = NULL; catalog->node = cat_node; catalog->sort_weight = 1000000000; /* very high */ if (boot_node != NULL) if (!(boot_node->explicit_weight || boot_node->from_old_session)) boot_node->sort_weight = 2; iso_node_ref((IsoNode*)cat_node); image->bootcat = catalog; if (boot) { *boot = boot_image; } return ISO_SUCCESS; boot_image_cleanup:; if (cat_node) { iso_node_take((IsoNode*)cat_node); iso_node_unref((IsoNode*)cat_node); } if (boot_image) { if (boot_image->image != NULL) iso_node_unref((IsoNode*)boot_image->image); free(boot_image); } return ret; } /** * Get the boot catalog and the El-Torito default boot image of an ISO image. * * This can be useful, for example, to check if a volume read from a previous * session or an existing image is bootable. It can also be useful to get * the image and catalog tree nodes. An application would want those, for * example, to prevent the user removing it. * * Both nodes are owned by libisofs and should not be freed. You can get your * own ref with iso_node_ref(). You can can also check if the node is already * on the tree by getting its parent (note that when reading El-Torito info * from a previous image, the nodes might not be on the tree even if you haven't * removed them). Remember that you'll need to get a new ref * (with iso_node_ref()) before inserting them again to the tree, and probably * you will also need to set the name or permissions. * * @param image * The image from which to get the boot image. * @param boot * If not NULL, it will be filled with a pointer to the boot image, if * any. That object is owned by the IsoImage and should not be freed by * the user, nor dereferenced once the last reference to the IsoImage was * disposed via iso_image_unref(). * @param imgnode * When not NULL, it will be filled with the image tree node. No extra ref * is added, you can use iso_node_ref() to get one if you need it. * @param catnode * When not NULL, it will be filled with the catnode tree node. No extra * ref is added, you can use iso_node_ref() to get one if you need it. * @return * 1 on success, 0 is the image is not bootable (i.e., it has no El-Torito * image), < 0 error. */ int iso_image_get_boot_image(IsoImage *image, ElToritoBootImage **boot, IsoFile **imgnode, IsoBoot **catnode) { if (image == NULL) { return ISO_NULL_POINTER; } if (image->bootcat == NULL) { return 0; } /* ok, image is bootable */ if (boot) { *boot = image->bootcat->bootimages[0]; } if (imgnode) { *imgnode = image->bootcat->bootimages[0]->image; } if (catnode) { *catnode = image->bootcat->node; } return ISO_SUCCESS; } int iso_image_get_bootcat(IsoImage *image, IsoBoot **catnode, uint32_t *lba, char **content, off_t *size) { IsoBoot *bootcat; *catnode = NULL; *lba = 0; *content = NULL; *size = 0; bootcat = image->bootcat->node; if (bootcat == NULL) return 0; *catnode = bootcat; *lba = bootcat->lba; if (bootcat->size > 0 && bootcat->content != NULL) { *content = calloc(1, bootcat->size); if (*content == NULL) return ISO_OUT_OF_MEM; memcpy(*content, bootcat->content, bootcat->size); } if (*content != NULL) *size = bootcat->size; return 1; } int iso_image_get_all_boot_imgs(IsoImage *image, int *num_boots, ElToritoBootImage ***boots, IsoFile ***bootnodes, int flag) { int i; struct el_torito_boot_catalog *cat; if (image == NULL) return ISO_NULL_POINTER; if (image->bootcat == NULL) return 0; cat = image->bootcat; *num_boots = cat->num_bootimages; *boots = NULL; *bootnodes = NULL; if (*num_boots <= 0) return 0; *boots = calloc(*num_boots, sizeof(ElToritoBootImage *)); *bootnodes = calloc(*num_boots, sizeof(IsoFile *)); if(*boots == NULL || *bootnodes == NULL) { if (*boots != NULL) free(*boots); if (*bootnodes != NULL) free(*bootnodes); *boots = NULL; *bootnodes = NULL; return ISO_OUT_OF_MEM; } for (i = 0; i < *num_boots; i++) { (*boots)[i] = cat->bootimages[i]; (*bootnodes)[i] = image->bootcat->bootimages[i]->image; } return 1; } /** * Removes the El-Torito bootable image. * * The IsoBoot node that acts as placeholder for the catalog is also removed * for the image tree, if there. * If the image is not bootable (don't have el-torito boot image) this function * just returns. */ void iso_image_remove_boot_image(IsoImage *image) { if (image == NULL || image->bootcat == NULL) return; /* * remove catalog node from its parent and dispose it * (another reference is with the catalog) */ if (iso_node_get_parent((IsoNode*) image->bootcat->node) != NULL) { iso_node_take((IsoNode*) image->bootcat->node); iso_node_unref((IsoNode*) image->bootcat->node); } /* free boot catalog and image, including references to nodes */ el_torito_boot_catalog_free(image->bootcat); image->bootcat = NULL; } /* API */ int iso_image_add_boot_image(IsoImage *image, const char *image_path, enum eltorito_boot_media_type type, int flag, ElToritoBootImage **boot) { int ret; struct el_torito_boot_catalog *catalog = image->bootcat; ElToritoBootImage *boot_img; IsoFile *boot_node; if(catalog == NULL) return ISO_BOOT_NO_CATALOG; if (catalog->num_bootimages >= Libisofs_max_boot_imageS) return ISO_BOOT_IMAGE_OVERFLOW; ret = create_image(image, image_path, type, &boot_img, &boot_node); if (ret < 0) return ret; if (boot_node != NULL) if (!(boot_node->explicit_weight || boot_node->from_old_session)) boot_node->sort_weight = 2; catalog->bootimages[catalog->num_bootimages] = boot_img; catalog->num_bootimages++; if (boot != NULL) *boot = boot_img; return 1; } /* API */ int iso_image_set_boot_catalog_weight(IsoImage *image, int sort_weight) { if (image->bootcat == NULL) return 0; image->bootcat->sort_weight = sort_weight; return 1; } /* API */ int iso_image_set_boot_catalog_hidden(IsoImage *image, int hide_attrs) { if (image->bootcat == NULL) return 0; if (image->bootcat->node == NULL) return 0; iso_node_set_hidden((IsoNode *) image->bootcat->node, hide_attrs); return 1; } void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat) { struct el_torito_boot_image *image; int i; if (cat == NULL) { return; } for (i = 0; i < Libisofs_max_boot_imageS; i++) { image = cat->bootimages[i]; if (image == NULL) continue; if ((IsoNode*)image->image != NULL) iso_node_unref((IsoNode*)image->image); if (image->image_path != NULL) free(image->image_path); free(image); } if ((IsoNode*)cat->node != NULL) iso_node_unref((IsoNode*)cat->node); free(cat); } /** * Stream that generates the contents of a El-Torito catalog. */ struct catalog_stream { Ecma119Image *target; uint8_t buffer[BLOCK_SIZE]; int offset; /* -1 if stream is not opened */ }; static void write_validation_entry(uint8_t *buf, uint8_t platform_id, uint8_t id_string[24]) { size_t i; int checksum; struct el_torito_validation_entry *ve = (struct el_torito_validation_entry*)buf; ve->header_id[0] = 1; ve->platform_id[0] = platform_id; memcpy(ve->id_string, id_string, sizeof(ve->id_string)); ve->key_byte1[0] = 0x55; ve->key_byte2[0] = 0xAA; /* calculate the checksum, to ensure sum of all words is 0 */ checksum = 0; for (i = 0; i < sizeof(struct el_torito_validation_entry); i += 2) { checksum -= (int16_t) ((buf[i+1] << 8) | buf[i]); } iso_lsb(ve->checksum, checksum, 2); } static void write_section_header(uint8_t *buf, Ecma119Image *t, int idx, int num_entries) { char *id_string; struct el_torito_section_header *e = (struct el_torito_section_header *) buf; /* 0x90 = more section headers follow , 0x91 = final section */ e->header_indicator[0] = 0x90 + (idx == t->catalog->num_bootimages - num_entries); e->platform_id[0] = t->catalog->bootimages[idx]->platform_id; e->num_entries[0] = num_entries & 0xff; e->num_entries[1] = (num_entries >> 8) & 0xff;; id_string = (char *) e->id_string; memcpy(id_string, t->catalog->bootimages[idx]->id_string, sizeof(e->id_string)); } static int write_section_load_size(struct el_torito_boot_image *img, struct el_torito_section_entry *se, uint16_t load_size, off_t full_byte_size, int flag) { uint16_t size; off_t blocks; size= load_size; if(img->type == 0 && img->load_size_full) { blocks= ((full_byte_size + 2047) / 2048) * 4; if (blocks > 65535) { if (img->platform_id == 0xef) size= 0; else size= 65535; } else if(blocks <= 0) { size= 1; } else { size= blocks; } } iso_lsb(se->sec_count, size, 2); return(1); } /** * Write one section entry. * Usable for the Default Entry * and for Section Entries with Selection criteria type == 0 */ static int write_section_entry(uint8_t *buf, Ecma119Image *t, int idx) { struct el_torito_boot_image *img; struct el_torito_section_entry *se = (struct el_torito_section_entry*)buf; int app_idx, mode = 0; img = t->catalog->bootimages[idx]; se->boot_indicator[0] = img->bootable ? 0x88 : 0x00; se->boot_media_type[0] = img->type; iso_lsb(se->load_seg, img->load_seg, 2); se->system_type[0] = img->partition_type; if (t->boot_appended_idx[idx] >= 0) if (t->appended_part_size[t->boot_appended_idx[idx]] > 0) mode = 2; /* appended partition */ if (mode == 0 && t->opts->appendable && (t->boot_intvl_start[idx] > 0 || t->boot_intvl_size[idx] > 0) && t->boot_intvl_start[idx] + (t->boot_intvl_size[idx] + 3) / 4 <= t->opts->ms_block) mode = 1; /* image interval */ if (mode == 0 && t->boot_appended_idx[idx] >= 0) { iso_msg_submit(t->image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Appended partition which shall serve as boot image does not exist"); return ISO_BOOT_IMAGE_NOT_VALID; } if (mode == 1) { if (t->boot_intvl_start[idx] + (t->boot_intvl_size[idx] + 3) / 4 > t->total_size / 2048 + t->opts->ms_block - t->eff_partition_offset ) { iso_msg_submit(t->image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Block interval which shall serve as boot image is outside result range"); return ISO_BOOT_IMAGE_NOT_VALID; } /* >>> check for non-automatic load size */; if (t->boot_intvl_size[idx] > 65535) { if (img->platform_id == 0xef) iso_lsb(se->sec_count, 0, 2); else iso_lsb(se->sec_count, 65535, 2); } else { if (t->boot_intvl_size[idx] == 0) { iso_msg_submit(t->image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Block interval which shall serve as boot image has zero size"); return ISO_BOOT_IMAGE_NOT_VALID; } iso_lsb(se->sec_count, t->boot_intvl_size[idx], 2); } iso_lsb(se->block, t->boot_intvl_start[idx], 4); } else if (mode == 2) { app_idx = t->boot_appended_idx[idx]; /* >>> check for non-automatic load size */; if (t->appended_part_size[app_idx] * 4 > 65535) { if (img->platform_id == 0xef) iso_lsb(se->sec_count, 0, 2); else iso_lsb(se->sec_count, 65535, 2); } else { iso_lsb(se->sec_count, t->appended_part_size[app_idx] * 4, 2); } iso_lsb(se->block, t->appended_part_start[app_idx], 4); } else { write_section_load_size(img, se, (uint16_t) img->load_size, (off_t) t->bootsrc[idx]->sections[0].size, 0); iso_lsb(se->block, t->bootsrc[idx]->sections[0].block, 4); } se->selec_criteria[0] = img->selection_crit[0]; memcpy(se->vendor_sc, img->selection_crit + 1, 19); return ISO_SUCCESS; } static int catalog_open(IsoStream *stream) { int i, j, k, num_entries, ret; struct catalog_stream *data; uint8_t *wpt; struct el_torito_boot_catalog *cat; struct el_torito_boot_image **boots; if (stream == NULL) { return ISO_NULL_POINTER; } data = stream->data; cat = data->target->catalog; boots = cat->bootimages; if (data->offset != -1) { return ISO_FILE_ALREADY_OPENED; } memset(data->buffer, 0, BLOCK_SIZE); /* fill the buffer with the catalog contents */ write_validation_entry(data->buffer, boots[0]->platform_id, boots[0]->id_string); /* write default entry = first boot image */ ret = write_section_entry(data->buffer + 32, data->target, 0); if (ret < 0) return ret; /* IMPORTANT: The maximum number of boot images must fit into BLOCK_SIZE */ wpt = data->buffer + 64; for (i = 1; i < cat->num_bootimages; ) { /* Look ahead and put images of same platform_id and id_string into the same section */ for (j = i + 1; j < cat->num_bootimages; j++) { if (boots[i]->platform_id != boots[j]->platform_id) break; for (k = 0; k < (int) sizeof(boots[i]->id_string); k++) if (boots[i]->id_string[k] != boots[j]->id_string[k]) break; if (k < (int) sizeof(boots[i]->id_string)) break; } num_entries = j - i; write_section_header(wpt, data->target, i, num_entries); wpt += 32; for (j = 0; j < num_entries; j++) { ret = write_section_entry(wpt, data->target, i); if (ret < 0) return ret; wpt += 32; i++; } } data->offset = 0; return ISO_SUCCESS; } static int catalog_close(IsoStream *stream) { struct catalog_stream *data; if (stream == NULL) { return ISO_NULL_POINTER; } data = stream->data; if (data->offset == -1) { return ISO_FILE_NOT_OPENED; } data->offset = -1; return ISO_SUCCESS; } static off_t catalog_get_size(IsoStream *stream) { return BLOCK_SIZE; } static int catalog_read(IsoStream *stream, void *buf, size_t count) { size_t len; struct catalog_stream *data; if (stream == NULL || buf == NULL) { return ISO_NULL_POINTER; } if (count == 0) { return ISO_WRONG_ARG_VALUE; } data = stream->data; if (data->offset == -1) { return ISO_FILE_NOT_OPENED; } len = MIN(count, (size_t) (BLOCK_SIZE - data->offset)); memcpy(buf, data->buffer + data->offset, len); return len; } static int catalog_is_repeatable(IsoStream *stream) { return 1; } /** * fs_id will be the id reserved for El-Torito * dev_id will be 0 for catalog, 1 for boot image (if needed) * ino_id 0 is supposed to be unique. At write time it will get assigned * an automatic file serial number in the ISO, if needed. */ static void catalog_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, ino_t *ino_id) { *fs_id = ISO_ELTORITO_FS_ID; *dev_id = 0; *ino_id = 0; } static void catalog_free(IsoStream *stream) { free(stream->data); } IsoStreamIface catalog_stream_class = { 0, "boot", catalog_open, catalog_close, catalog_get_size, catalog_read, catalog_is_repeatable, catalog_get_id, catalog_free, NULL, NULL, NULL, NULL }; /** * Create an IsoStream for writing El-Torito catalog for a given target. */ static int catalog_stream_new(Ecma119Image *target, IsoStream **stream) { IsoStream *str; struct catalog_stream *data; if (target == NULL || stream == NULL || target->catalog == NULL) { return ISO_NULL_POINTER; } str = calloc(1, sizeof(IsoStream)); if (str == NULL) { return ISO_OUT_OF_MEM; } data = calloc(1, sizeof(struct catalog_stream)); if (data == NULL) { free(str); return ISO_OUT_OF_MEM; } /* fill data */ data->target = target; data->offset = -1; str->refcount = 1; str->data = data; str->class = &catalog_stream_class; *stream = str; return ISO_SUCCESS; } int el_torito_catalog_file_src_create(Ecma119Image *target, IsoFileSrc **src) { int ret; IsoFileSrc *file; IsoStream *stream; if (target == NULL || src == NULL || target->catalog == NULL) { return ISO_OUT_OF_MEM; } if (target->cat != NULL) { /* catalog file src already created */ *src = target->cat; return ISO_SUCCESS; } file = calloc(1, sizeof(IsoFileSrc)); if (file == NULL) { return ISO_OUT_OF_MEM; } ret = catalog_stream_new(target, &stream); if (ret < 0) { free(file); return ret; } /* fill fields */ file->no_write = 0; /* TODO allow copy of old img catalog???? */ file->checksum_index = 0; file->nsections = 1; file->sections = calloc(1, sizeof(struct iso_file_section)); file->sort_weight = target->catalog->sort_weight; file->stream = stream; ret = iso_file_src_add(target, file, src); if (ret <= 0) { iso_stream_unref(stream); free(file); } else { target->cat = *src; } return ret; } /******************* EL-TORITO WRITER *******************************/ /** * Insert boot info table content into buf. * * @return * 1 on success, 0 error (but continue), < 0 error */ int make_boot_info_table(uint8_t *buf, uint32_t pvd_lba, uint32_t boot_lba, uint32_t imgsize) { struct boot_info_table *info; uint32_t checksum; uint32_t offset; info = (struct boot_info_table *) (buf + 8); if (imgsize < 64) return ISO_ISOLINUX_CANT_PATCH; /* compute checksum, as the the sum of all 32 bit words in boot image * from offset 64 */ checksum = 0; offset = 64; while (offset <= imgsize - 4) { checksum += iso_read_lsb(buf + offset, 4); offset += 4; } if (offset != imgsize) { /* * file length not multiple of 4 * empty space in isofs is padded with zero; * assume same for last dword */ checksum += iso_read_lsb(buf + offset, imgsize - offset); } /*memset(info, 0, sizeof(struct boot_info_table));*/ iso_lsb(info->bi_pvd, pvd_lba, 4); iso_lsb(info->bi_file, boot_lba, 4); iso_lsb(info->bi_length, imgsize, 4); iso_lsb(info->bi_csum, checksum, 4); memset(buf + 24, 0, 40); return ISO_SUCCESS; } /** * Patch an El Torito boot image by a boot info table. * * @return * 1 on success, 0 error (but continue), < 0 error */ static int patch_boot_info_table(uint8_t *buf, Ecma119Image *t, size_t imgsize, int idx) { int ret; if (imgsize < 64) { return iso_msg_submit(t->image->id, ISO_ISOLINUX_CANT_PATCH, 0, "Isolinux image too small. We won't patch it."); } if (t->bootsrc[idx] == NULL) return iso_msg_submit(t->image->id, ISO_ISOLINUX_CANT_PATCH, 0, "Cannot apply ISOLINUX patching outside of ISO 9660 filesystem."); ret = make_boot_info_table(buf, t->opts->ms_block + (uint32_t) 16, t->bootsrc[idx]->sections[0].block, (uint32_t) imgsize); return ret; } /** * Patch a GRUB2 El Torito boot image. */ static int patch_grub2_boot_image(uint8_t *buf, Ecma119Image *t, size_t imgsize, int idx, size_t pos, int offst) { uint64_t blk; if (imgsize < pos + 8) return iso_msg_submit(t->image->id, ISO_ISOLINUX_CANT_PATCH, 0, "Boot image too small for GRUB2. Will not patch it."); if (t->bootsrc[idx] == NULL) return iso_msg_submit(t->image->id, ISO_ISOLINUX_CANT_PATCH, 0, "Cannot apply GRUB2 patching outside of ISO 9660 filesystem."); blk = ((uint64_t) t->bootsrc[idx]->sections[0].block) * 4 + offst; iso_lsb((buf + pos), blk & 0xffffffff, 4); iso_lsb((buf + pos + 4), blk >> 32, 4); return ISO_SUCCESS; } /* Patch the boot images if indicated */ int iso_patch_eltoritos(Ecma119Image *t) { int ret, idx; size_t size; uint8_t *buf; IsoStream *new = NULL; IsoStream *original = NULL; if (t->catalog == NULL) return ISO_SUCCESS; for (idx = 0; idx < t->catalog->num_bootimages; idx++) { if (!(t->catalog->bootimages[idx]->isolinux_options & 0x201)) continue; if (t->bootsrc[idx] == NULL) return iso_msg_submit(t->image->id, ISO_ISOLINUX_CANT_PATCH, 0, "Cannot apply boot image patching outside of ISO 9660 filesystem"); original = t->bootsrc[idx]->stream; size = (size_t) iso_stream_get_size(original); if (size > Libisofs_elto_max_patchablE) return ISO_PATCH_OVERSIZED_BOOT; if (iso_stream_get_input_stream(original, 0) != NULL) return ISO_PATCH_FILTERED_BOOT; buf = calloc(1, size); if (buf == NULL) { return ISO_OUT_OF_MEM; } ret = iso_stream_open(original); if (ret < 0) { free(buf); return ret; } ret = iso_stream_read(original, buf, size); iso_stream_close(original); if (ret != (int) size) { if (ret >= 0) iso_msg_submit(t->image->id, ISO_FILE_READ_ERROR, 0, "Cannot read all bytes from El Torito boot image for boot info table"); return (ret < 0) ? ret : (int) ISO_FILE_READ_ERROR; } /* ok, patch the read buffer */ if (t->catalog->bootimages[idx]->isolinux_options & 0x200) { /* GRUB2 boot provisions */ ret = patch_grub2_boot_image(buf, t, size, idx, Libisofs_grub2_elto_patch_poS, Libisofs_grub2_elto_patch_offsT); if (ret < 0) return ret; } /* Must be done as last patching */ if (t->catalog->bootimages[idx]->isolinux_options & 0x01) { /* Boot Info Table */ ret = patch_boot_info_table(buf, t, size, idx); if (ret < 0) return ret; } /* replace the original stream with a memory stream that reads from * the patched buffer */ ret = iso_memory_stream_new(buf, size, &new); if (ret < 0) { return ret; } t->bootsrc[idx]->stream = new; iso_stream_unref(original); } return ISO_SUCCESS; } static int eltorito_writer_compute_data_blocks(IsoImageWriter *writer) { /* * We have nothing to write. */ return ISO_SUCCESS; } /** * Write the Boot Record Volume Descriptor (ECMA-119, 8.2) */ static int eltorito_writer_write_vol_desc(IsoImageWriter *writer) { Ecma119Image *t; struct ecma119_boot_rec_vol_desc vol; if (writer == NULL) { return ISO_NULL_POINTER; } t = writer->target; iso_msg_debug(t->image->id, "Write El-Torito boot record"); memset(&vol, 0, sizeof(struct ecma119_boot_rec_vol_desc)); vol.vol_desc_type[0] = 0; memcpy(vol.std_identifier, "CD001", 5); vol.vol_desc_version[0] = 1; memcpy(vol.boot_sys_id, "EL TORITO SPECIFICATION", 23); iso_lsb(vol.boot_catalog, t->cat->sections[0].block - t->eff_partition_offset, 4); return iso_write(t, &vol, sizeof(struct ecma119_boot_rec_vol_desc)); } static int eltorito_writer_write_data(IsoImageWriter *writer) { /* nothing to do, the files are written by the file writer */ return ISO_SUCCESS; } static int eltorito_writer_free_data(IsoImageWriter *writer) { /* nothing to do */ return ISO_SUCCESS; } int eltorito_writer_create(Ecma119Image *target) { int ret, idx, outsource_efi = 0; IsoImageWriter *writer; IsoFile *bootimg = NULL; IsoFileSrc *src = NULL; writer = calloc(1, sizeof(IsoImageWriter)); if (writer == NULL) { return ISO_OUT_OF_MEM; } writer->compute_data_blocks = eltorito_writer_compute_data_blocks; writer->write_vol_desc = eltorito_writer_write_vol_desc; writer->write_data = eltorito_writer_write_data; writer->free_data = eltorito_writer_free_data; writer->data = NULL; writer->target = target; /* add this writer to image */ target->writers[target->nwriters++] = writer; /* * get catalog and image file sources. * Note that the catalog may be already added, when creating the low * level ECMA-119 tree. */ if (target->cat == NULL) { ret = el_torito_catalog_file_src_create(target, &src); if (ret < 0) { return ret; } } if (target->opts->efi_boot_partition != NULL) if (strcmp(target->opts->efi_boot_partition, "--efi-boot-image") == 0) outsource_efi = 1; for (idx = 0; idx < target->catalog->num_bootimages; idx++) { target->bootsrc[idx] = NULL; if (target->catalog->bootimages[idx]->appended_idx >= 0) { /* Use an appended partition as boot image rather than IsoFile */ target->boot_appended_idx[idx] = target->catalog->bootimages[idx]->appended_idx; target->boot_intvl_start[idx] = target->catalog->bootimages[idx]->appended_start; target->boot_intvl_size[idx] = target->catalog->bootimages[idx]->appended_size; continue; } bootimg = target->catalog->bootimages[idx]->image; ret = iso_file_src_create(target, bootimg, &src); if (ret < 0) { return ret; } target->bootsrc[idx] = src; /* For patching an image, it needs to be copied always */ if (target->catalog->bootimages[idx]->isolinux_options & 0x01) { src->no_write = 0; } /* If desired: Recognize first EFI boot image that will be newly written, and mark it as claimed for being a partition. */ if (outsource_efi && target->catalog->bootimages[idx]->platform_id == 0xef && src->no_write == 0) { target->efi_boot_part_filesrc = src; src->sections[0].block = 0xfffffffe; ((IsoNode *) bootimg)->hidden |= LIBISO_HIDE_ON_HFSPLUS | LIBISO_HIDE_ON_FAT; outsource_efi = 0; } } /* we need the bootable volume descriptor */ target->curblock++; if (outsource_efi) { /* Disable EFI Boot partition and complain */ free(target->opts->efi_boot_partition); target->opts->efi_boot_partition = NULL; iso_msg_submit(target->image->id, ISO_BOOT_NO_EFI_ELTO, 0, "No newly added El Torito EFI boot image found for exposure as GPT partition"); return ISO_BOOT_NO_EFI_ELTO; } return ISO_SUCCESS; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2010 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /** * Declare El-Torito related structures. * References: * "El Torito" Bootable CD-ROM Format Specification Version 1.0 (1995) */ #ifndef LIBISO_ELTORITO_H #define LIBISO_ELTORITO_H #include "ecma119.h" #include "node.h" /** * A node that acts as a placeholder for an El-Torito catalog. */ struct Iso_Boot { IsoNode node; /* Want to get content of loaded boot catalog. Vreixo took care not to make it an IsoFile at load time. So this is implemented independently of IsoStream. */ uint32_t lba; off_t size; char *content; }; /* Not more than 32 so that all entries fit into 2048 bytes */ #define Libisofs_max_boot_imageS 32 struct el_torito_boot_catalog { IsoBoot *node; /* node of the catalog */ int num_bootimages; struct el_torito_boot_image *bootimages[Libisofs_max_boot_imageS]; /* [0]= default boot image */ /* Weight value for image sorting */ int sort_weight; }; struct el_torito_boot_image { IsoFile *image; /* Path of image at the time of ISO image loading (NULL = hidden image) */ char *image_path; /* Overrides .image if >= 0 : array index of appended partition */ int appended_idx; uint32_t appended_start; /* In blocks of 2048 bytes */ uint32_t appended_size; /* In blocks of 512 bytes */ unsigned int bootable:1; /**< If the entry is bootable. */ /** * Whether the boot image seems to contain a boot_info_table */ unsigned int seems_boot_info_table:1; unsigned int seems_grub2_boot_info:1; /** * Whether the boot image seems to be capable of isohybrid */ unsigned int seems_isohybrid_capable:1; /** * isolinux options * bit 0 -> whether to patch image * bit 1 -> whether to put built-in isolinux 3.72 isohybrid-MBR into image * System Area (deprecated) * * bit2-7= Mentioning in isohybrid GPT * 0= do not mention in GPT * 1= mention as EFI partition * 2= Mention as HFS+ partition * bit8= Mention in isohybrid Apple partition map */ unsigned int isolinux_options; unsigned char type; /**< The type of image : 0=no emulation , 1=fd 1.2 MB , 2=fd 1.4 MB 3=fd 3.8 MB , 4=hdd (size in partition table) */ unsigned char partition_type; /**< type of partition for HD-emul images */ uint32_t emul_hdd_size; /* 512-bytes LBA after highest partition end from HD-emul partition table */ uint16_t load_seg; /**< Load segment for the initial boot image. */ uint16_t load_size; /**< Number of sectors to load. */ int load_size_full; /* 1= override load_size by image size */ /* Byte 1 of Validation Entry or Section Header Entry: 0= 80x86, 1= PowerPC, 2= Mac, 0xef= EFI */ uint8_t platform_id; uint8_t id_string[28]; uint8_t selection_crit[20]; }; /** El-Torito, 2.1 */ struct el_torito_validation_entry { uint8_t header_id BP(1, 1); uint8_t platform_id BP(2, 2); uint8_t reserved BP(3, 4); uint8_t id_string BP(5, 28); uint8_t checksum BP(29, 30); uint8_t key_byte1 BP(31, 31); uint8_t key_byte2 BP(32, 32); }; /** El-Torito, 2.2 */ struct el_torito_default_entry { uint8_t boot_indicator BP(1, 1); uint8_t boot_media_type BP(2, 2); uint8_t load_seg BP(3, 4); uint8_t system_type BP(5, 5); uint8_t unused1 BP(6, 6); uint8_t sec_count BP(7, 8); uint8_t block BP(9, 12); uint8_t unused2 BP(13, 32); }; /** El-Torito, 2.3 */ struct el_torito_section_header { uint8_t header_indicator BP(1, 1); uint8_t platform_id BP(2, 2); uint8_t num_entries BP(3, 4); uint8_t id_string BP(5, 32); }; /** El-Torito, 2.4 */ struct el_torito_section_entry { uint8_t boot_indicator BP(1, 1); uint8_t boot_media_type BP(2, 2); uint8_t load_seg BP(3, 4); uint8_t system_type BP(5, 5); uint8_t unused1 BP(6, 6); uint8_t sec_count BP(7, 8); uint8_t block BP(9, 12); uint8_t selec_criteria BP(13, 13); uint8_t vendor_sc BP(14, 32); }; void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat); /** * Create a IsoFileSrc for writing the el-torito catalog for the given * target, and add it to target. If the target already has a src for the * catalog, it just returns. */ int el_torito_catalog_file_src_create(Ecma119Image *target, IsoFileSrc **src); /** * Create a writer for el-torito information. */ int eltorito_writer_create(Ecma119Image *target); /** * Insert boot info table content into buf. * * @return * 1 on success, 0 error (but continue), < 0 error */ int make_boot_info_table(uint8_t *buf, uint32_t pvd_lba, uint32_t boot_lba, uint32_t imgsize); /* Patch the boot images if indicated. */ int iso_patch_eltoritos(Ecma119Image *t); /* Parameters for patch_grub2_boot_image() Might later become variables in struct el_torito_boot_image. */ #define Libisofs_grub2_elto_patch_poS (512 * 5 - 12) #define Libisofs_grub2_elto_patch_offsT 5 /* Maximum size of a boot image which is marked by el_torito_set_isolinux_options() for patching (boot info table, GRUB2 boot info, maybe others). */ #define Libisofs_elto_max_patchablE (32 * 1024 * 1024) #endif /* LIBISO_ELTORITO_H */ /* * Copyright (c) 2007 Vreixo Formoso * 2010 - 2012 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libisofs.h" #include "filesrc.h" #include "node.h" #include "util.h" #include "writer.h" #include "messages.h" #include "image.h" #include "stream.h" #include "md5.h" #include #include #include /* <<< */ #include #ifdef Xorriso_standalonE #ifdef Xorriso_with_libjtE #include "../libjte/libjte.h" #endif #else #ifdef Libisofs_with_libjtE #include #endif #endif /* ! Xorriso_standalonE */ #ifndef PATH_MAX #define PATH_MAX Libisofs_default_path_maX #endif int iso_file_src_cmp(const void *n1, const void *n2) { int ret; const IsoFileSrc *f1, *f2; if (n1 == n2) { return 0; /* Normally just a shortcut. But important if Libisofs_file_src_cmp_non_zerO */ } f1 = (const IsoFileSrc *)n1; f2 = (const IsoFileSrc *)n2; ret = iso_stream_cmp_ino(f1->stream, f2->stream, 0); return ret; } int iso_file_src_create(Ecma119Image *img, IsoFile *file, IsoFileSrc **src) { int ret, i; IsoFileSrc *fsrc; unsigned int fs_id; dev_t dev_id; ino_t ino_id; int cret, no_md5= 0; void *xipt = NULL; if (img == NULL || file == NULL || src == NULL) { return ISO_NULL_POINTER; } iso_stream_get_id(file->stream, &fs_id, &dev_id, &ino_id); fsrc = calloc(1, sizeof(IsoFileSrc)); if (fsrc == NULL) { return ISO_OUT_OF_MEM; } /* fill key and other atts */ fsrc->no_write = (file->from_old_session && img->opts->appendable); if (file->from_old_session && img->opts->appendable) { /* * On multisession discs we keep file sections from old image. */ int ret = iso_file_get_old_image_sections(file, &(fsrc->nsections), &(fsrc->sections), 0); if (ret < 0) { free(fsrc); return ISO_OUT_OF_MEM; } } else { /* * For new files, or for image copy, we compute our own file sections. * Block and size of each section will be filled later. */ off_t section_size = iso_stream_get_size(file->stream); if (section_size > (off_t) MAX_ISO_FILE_SECTION_SIZE) { fsrc->nsections = DIV_UP(section_size - (off_t) MAX_ISO_FILE_SECTION_SIZE, (off_t)ISO_EXTENT_SIZE) + 1; } else { fsrc->nsections = 1; } fsrc->sections = calloc(fsrc->nsections, sizeof(struct iso_file_section)); if (fsrc->sections == NULL) { free(fsrc); return ISO_OUT_OF_MEM; } for (i = 0; i < fsrc->nsections; i++) fsrc->sections[i].block = 0; } fsrc->sort_weight = file->sort_weight; fsrc->stream = file->stream; /* insert the filesrc in the tree */ ret = iso_rbtree_insert(img->files, fsrc, (void**)src); if (ret <= 0) { if (ret == 0 && (*src)->checksum_index > 0 && !img->opts->will_cancel) { /* Duplicate file source was mapped to previously registered source */ cret = iso_file_set_isofscx(file, (*src)->checksum_index, 0); if (cret < 0) ret = cret; } free(fsrc->sections); free(fsrc); return ret; } iso_stream_ref(fsrc->stream); if ((img->opts->md5_file_checksums & 1) && file->from_old_session && img->opts->appendable) { ret = iso_node_get_xinfo((IsoNode *) file, checksum_md5_xinfo_func, &xipt); if (ret <= 0) ret = iso_node_get_xinfo((IsoNode *) file, checksum_cx_xinfo_func, &xipt); if (ret <= 0) /* Omit MD5 indexing with old image nodes which have no MD5 */ no_md5 = 1; } if ((img->opts->md5_file_checksums & 1) && !(no_md5 || img->opts->will_cancel)) { img->checksum_idx_counter++; if (img->checksum_idx_counter < 0x7fffffff) { fsrc->checksum_index = img->checksum_idx_counter; } else { fsrc->checksum_index= 0; img->checksum_idx_counter= 0x7ffffffe; /* keep from rolling over */ } cret = iso_file_set_isofscx(file, (*src)->checksum_index, 0); if (cret < 0) return cret; } return ISO_SUCCESS; } /** * Add a given IsoFileSrc to the given image target. * * The IsoFileSrc will be cached in a tree to prevent the same file for * being written several times to image. If you call again this function * with a node that refers to the same source file, the previously * created one will be returned. * * @param img * The image where this file is to be written * @param new * The IsoFileSrc to add * @param src * Will be filled with a pointer to the IsoFileSrc really present in * the tree. It could be different than new if the same file already * exists in the tree. * @return * 1 on success, 0 if file already exists on tree, < 0 error */ int iso_file_src_add(Ecma119Image *img, IsoFileSrc *new, IsoFileSrc **src) { int ret; if (img == NULL || new == NULL || src == NULL) { return ISO_NULL_POINTER; } /* insert the filesrc in the tree */ ret = iso_rbtree_insert(img->files, new, (void**)src); return ret; } void iso_file_src_free(void *node) { iso_stream_unref(((IsoFileSrc*)node)->stream); free(((IsoFileSrc*)node)->sections); free(node); } off_t iso_file_src_get_size(IsoFileSrc *file) { return iso_stream_get_size(file->stream); } static int cmp_by_weight(const void *f1, const void *f2) { IsoFileSrc *f = *((IsoFileSrc**)f1); IsoFileSrc *g = *((IsoFileSrc**)f2); int cmp; /* higher weighted first */ cmp = g->sort_weight - f->sort_weight; if (cmp) return cmp; /* Make the result of qsort(3) stable by avoiding return value 0 as good as possible. If not f == g, then their ->taken values are supposed to differ. */ if(f->taken == g->taken) return 0; return f->taken < g->taken ? -1 : 1; } static int shall_be_written(void *arg) { IsoFileSrc *f = (IsoFileSrc *)arg; return f->no_write ? 0 : 1; } static int shall_be_written_if_not_taken(void *arg) { IsoFileSrc *f = (IsoFileSrc *)arg; return f->no_write || f->taken ? 0 : 1; } int filesrc_writer_pre_compute(IsoImageWriter *writer) { size_t i, size, is_external; Ecma119Image *t; IsoFileSrc **filelist; int (*inc_item)(void *); size_t omitted_count; IsoFileSrc **iso_ecma119_to_filesrc_array(Ecma119Image *t, int (*include_item)(void *), size_t *size); if (writer == NULL) { return ISO_ASSERT_FAILURE; } t = writer->target; t->filesrc_blocks = 0; /* Normally reserve a single zeroed block for all files which have no block address: symbolic links, device files, empty data files. */ if (! t->opts->old_empty) t->filesrc_blocks++; /* on appendable images, ms files shouldn't be included */ if (t->opts->appendable) { inc_item = shall_be_written; } else { inc_item = NULL; } /* store the filesrcs in a array */ filelist = (IsoFileSrc**) iso_ecma119_to_filesrc_array(t, inc_item, &size); omitted_count = iso_rbtree_count_array(t->files, (size_t) 0, shall_be_written_if_not_taken); if (omitted_count > 0) { iso_msg_submit(t->image->id, ISO_NOT_REPRODUCIBLE, 0, "Cannot arrange content of data files in surely reproducible way"); LIBISO_FREE_MEM(filelist); filelist = (IsoFileSrc**)iso_rbtree_to_array( t->files, inc_item, &size); } if (filelist == NULL) { return ISO_OUT_OF_MEM; } /* sort files by weight, if needed */ if (t->opts->sort_files) { qsort(filelist, size, sizeof(void*), cmp_by_weight); } /* fill block value */ for (i = 0; i < size; ++i) { int extent = 0; IsoFileSrc *file = filelist[i]; off_t section_size; /* 0xfffffffe in emerging image means that this is an external partition. Only assess extent sizes but do not count as part of filesrc_writer output. */ is_external = (file->no_write == 0 && file->sections[0].block == 0xfffffffe); section_size = iso_stream_get_size(file->stream); for (extent = 0; extent < file->nsections - 1; ++extent) { file->sections[extent].block = t->filesrc_blocks + extent * (ISO_EXTENT_SIZE / BLOCK_SIZE); file->sections[extent].size = ISO_EXTENT_SIZE; section_size -= (off_t) ISO_EXTENT_SIZE; } /* * final section */ if (section_size <= 0) { /* Will become t->empty_file_block in filesrc_writer_compute_data_blocks() Special use of 0xffffffe0 to 0xffffffff is covered by mspad_writer which enforces a minimum start of filesrc at block 0x00000020. */ file->sections[extent].block = 0xffffffff; } else { file->sections[extent].block = t->filesrc_blocks + extent * (ISO_EXTENT_SIZE / BLOCK_SIZE); } file->sections[extent].size = (uint32_t)section_size; /* 0xfffffffe in emerging image means that this is an external partition. Others will take care of the content data. */ if (is_external) { file->sections[0].block = 0xfffffffe; file->no_write = 1; /* Ban for filesrc_writer */ continue; } t->filesrc_blocks += DIV_UP(iso_file_src_get_size(file), BLOCK_SIZE); } /* the list is only needed by this writer, store locally */ writer->data = filelist; return ISO_SUCCESS; } static int filesrc_writer_compute_data_blocks(IsoImageWriter *writer) { Ecma119Image *t; int extent = 0; size_t i; IsoFileSrc *file; IsoFileSrc **filelist; if (writer == NULL) { return ISO_ASSERT_FAILURE; } t = writer->target; filelist = (IsoFileSrc **) writer->data; /* >>> HFS: need to align to allocation block size */; /* >>> HFS: ??? how to handle multi-extent files ? */; t->filesrc_start = t->curblock; /* Give all extent addresses their final absolute value */ i = 0; while ((file = filelist[i++]) != NULL) { /* Skip external partitions */ if (file->no_write) continue; for (extent = 0; extent < file->nsections; ++extent) { if (file->sections[extent].block == 0xffffffff) file->sections[extent].block = t->empty_file_block; else file->sections[extent].block += t->curblock; } } t->curblock += t->filesrc_blocks; return ISO_SUCCESS; } static int filesrc_writer_write_vol_desc(IsoImageWriter *writer) { /* nothing needed */ return ISO_SUCCESS; } /* open a file, i.e., its Stream */ static inline int filesrc_open(IsoFileSrc *file) { return iso_stream_open(file->stream); } static inline int filesrc_close(IsoFileSrc *file) { return iso_stream_close(file->stream); } /** * @return * 1 ok, 0 EOF, < 0 error */ static int filesrc_read(IsoFileSrc *file, char *buf, size_t count) { size_t got; return iso_stream_read_buffer(file->stream, buf, count, &got); } /* @return 1=ok, md5 is valid, 0= not ok, go on, <0 fatal error, abort */ static int filesrc_make_md5(Ecma119Image *t, IsoFileSrc *file, char md5[16], int flag) { return iso_stream_make_md5(file->stream, md5, 0); } /* name must be NULL or offer at least PATH_MAX characters. buffer must be NULL or offer at least BLOCK_SIZE characters. */ int iso_filesrc_write_data(Ecma119Image *t, IsoFileSrc *file, char *name, char *buffer, int flag) { int res, ret, was_error; char *name_data = NULL; char *buffer_data = NULL; size_t b; off_t file_size; uint32_t nblocks; void *ctx= NULL; char md5[16], pre_md5[16]; int pre_md5_valid = 0; IsoStream *stream, *inp; #ifdef Libisofs_with_libjtE int jte_begun = 0; #endif if (name == NULL) { LIBISO_ALLOC_MEM(name_data, char, PATH_MAX); name = name_data; } if (buffer == NULL) { LIBISO_ALLOC_MEM(buffer_data, char, BLOCK_SIZE); buffer = buffer_data; } was_error = 0; file_size = iso_file_src_get_size(file); nblocks = DIV_UP(file_size, BLOCK_SIZE); pre_md5_valid = 0; if (file->checksum_index > 0 && (t->opts->md5_file_checksums & 2)) { /* Obtain an MD5 of content by a first read pass */ pre_md5_valid = filesrc_make_md5(t, file, pre_md5, 0); } res = filesrc_open(file); /* Get file name from end of filter chain */ for (stream = file->stream; ; stream = inp) { inp = iso_stream_get_input_stream(stream, 0); if (inp == NULL) break; } iso_stream_get_file_name(stream, name); if (res < 0) { /* * UPS, very ugly error, the best we can do is just to write * 0's to image */ iso_report_errfile(name, ISO_FILE_CANT_WRITE, 0, 0); was_error = 1; res = iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, res, "File \"%s\" can't be opened. Filling with 0s.", name); if (res < 0) { ret = res; /* aborted due to error severity */ goto ex; } memset(buffer, 0, BLOCK_SIZE); for (b = 0; b < nblocks; ++b) { res = iso_write(t, buffer, BLOCK_SIZE); if (res < 0) { /* ko, writer error, we need to go out! */ ret = res; goto ex; } } ret = ISO_SUCCESS; goto ex; } else if (res > 1) { iso_report_errfile(name, ISO_FILE_CANT_WRITE, 0, 0); was_error = 1; res = iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, 0, "Size of file \"%s\" has changed. It will be %s", name, (res == 2 ? "truncated" : "padded with 0's")); if (res < 0) { filesrc_close(file); ret = res; /* aborted due to error severity */ goto ex; } } #ifdef LIBISOFS_VERBOSE_DEBUG else { iso_msg_debug(t->image->id, "Writing file %s", name); } #endif /* >>> HFS: need to align to allocation block size */; #ifdef Libisofs_with_libjtE if (t->opts->libjte_handle != NULL) { res = libjte_begin_data_file(t->opts->libjte_handle, name, BLOCK_SIZE, file_size); if (res <= 0) { res = iso_libjte_forward_msgs(t->opts->libjte_handle, t->image->id, ISO_LIBJTE_FILE_FAILED, 0); if (res < 0) { filesrc_close(file); ret = ISO_LIBJTE_FILE_FAILED; goto ex; } } jte_begun = 1; } #endif /* Libisofs_with_libjtE */ if (file->checksum_index > 0) { /* initialize file checksum */ res = iso_md5_start(&ctx); if (res <= 0) file->checksum_index = 0; } /* write file contents to image */ for (b = 0; b < nblocks; ++b) { int wres; res = filesrc_read(file, buffer, BLOCK_SIZE); if (res < 0) { /* read error */ break; } wres = iso_write(t, buffer, BLOCK_SIZE); if (wres < 0) { /* ko, writer error, we need to go out! */ filesrc_close(file); ret = wres; goto ex; } if (file->checksum_index > 0) { /* Add to file checksum */ if (file_size - b * BLOCK_SIZE > BLOCK_SIZE) res = BLOCK_SIZE; else res = file_size - b * BLOCK_SIZE; res = iso_md5_compute(ctx, buffer, res); if (res <= 0) file->checksum_index = 0; } } filesrc_close(file); if (b < nblocks) { /* premature end of file, due to error or eof */ iso_report_errfile(name, ISO_FILE_CANT_WRITE, 0, 0); was_error = 1; if (res < 0) { /* error */ res = iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, res, "Read error in file %s.", name); } else { /* eof */ res = iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, 0, "Premature end of file %s.", name); } if (res < 0) { ret = res; /* aborted due error severity */ goto ex; } /* fill with 0s */ iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, 0, "Filling with 0"); memset(buffer, 0, BLOCK_SIZE); while (b++ < nblocks) { res = iso_write(t, buffer, BLOCK_SIZE); if (res < 0) { /* ko, writer error, we need to go out! */ ret = res; goto ex; } if (file->checksum_index > 0) { /* Add to file checksum */ if (file_size - b * BLOCK_SIZE > BLOCK_SIZE) res = BLOCK_SIZE; else res = file_size - b * BLOCK_SIZE; res = iso_md5_compute(ctx, buffer, res); if (res <= 0) file->checksum_index = 0; } } } if (file->checksum_index > 0 && file->checksum_index <= t->checksum_idx_counter) { /* Obtain checksum and dispose checksum context */ res = iso_md5_end(&ctx, md5); if (res <= 0) file->checksum_index = 0; if ((t->opts->md5_file_checksums & 2) && pre_md5_valid > 0 && !was_error) { if (! iso_md5_match(md5, pre_md5)) { /* Issue MISHAP event */ iso_report_errfile(name, ISO_MD5_STREAM_CHANGE, 0, 0); was_error = 1; res = iso_msg_submit(t->image->id, ISO_MD5_STREAM_CHANGE,0, "Content of file '%s' changed while it was written into the image.", name); if (res < 0) { ret = res; /* aborted due to error severity */ goto ex; } } } /* Write md5 into checksum buffer at file->checksum_index */ memcpy(t->checksum_buffer + 16 * file->checksum_index, md5, 16); } ret = ISO_SUCCESS; ex:; if (ctx != NULL) /* avoid any memory leak */ iso_md5_end(&ctx, md5); #ifdef Libisofs_with_libjtE if (jte_begun) { res = libjte_end_data_file(t->opts->libjte_handle); iso_libjte_forward_msgs(t->opts->libjte_handle, t->image->id, ISO_LIBJTE_END_FAILED, 0); if (res <= 0 && ret >= 0) ret = ISO_LIBJTE_FILE_FAILED; } #endif /* Libisofs_with_libjtE */ LIBISO_FREE_MEM(buffer_data); LIBISO_FREE_MEM(name_data); return ret; } static int filesrc_writer_write_data(IsoImageWriter *writer) { int ret; size_t i; Ecma119Image *t = NULL; IsoFileSrc *file; IsoFileSrc **filelist; char *name = NULL; char *buffer = NULL; if (writer == NULL) { ret = ISO_ASSERT_FAILURE; goto ex; } LIBISO_ALLOC_MEM(name, char, PATH_MAX); LIBISO_ALLOC_MEM(buffer, char, BLOCK_SIZE); t = writer->target; filelist = writer->data; iso_msg_debug(t->image->id, "Writing Files..."); /* Normally write a single zeroed block as block address target for all files which have no block address: symbolic links, device files, empty data files. */ if (! t->opts->old_empty) { ret = iso_write(t, buffer, BLOCK_SIZE); if (ret < 0) goto ex; } i = 0; while ((file = filelist[i++]) != NULL) { if (file->no_write) { /* Do not write external partitions */ iso_msg_debug(t->image->id, "filesrc_writer: Skipping no_write-src [%.f , %.f]", (double) file->sections[0].block, (double) (file->sections[0].block - 1 + (file->sections[0].size + 2047) / BLOCK_SIZE)); continue; } ret = iso_filesrc_write_data(t, file, name, buffer, 0); if (ret < 0) goto ex; } ret = ISO_SUCCESS; ex:; LIBISO_FREE_MEM(buffer); LIBISO_FREE_MEM(name); return ret; } static int filesrc_writer_free_data(IsoImageWriter *writer) { /* free the list of files (contents are free together with the tree) */ free(writer->data); return ISO_SUCCESS; } int iso_file_src_writer_create(Ecma119Image *target) { IsoImageWriter *writer; writer = calloc(1, sizeof(IsoImageWriter)); if (writer == NULL) { return ISO_OUT_OF_MEM; } writer->compute_data_blocks = filesrc_writer_compute_data_blocks; writer->write_vol_desc = filesrc_writer_write_vol_desc; writer->write_data = filesrc_writer_write_data; writer->free_data = filesrc_writer_free_data; writer->data = NULL; writer->target = target; /* add this writer to image */ target->writers[target->nwriters++] = writer; return ISO_SUCCESS; } /* * Copyright (c) 2007 Vreixo Formoso * 2012 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_FILESRC_H_ #define LIBISO_FILESRC_H_ #include "libisofs.h" #include "stream.h" #include "ecma119.h" #ifdef HAVE_STDINT_H #include #else #ifdef HAVE_INTTYPES_H #include #endif #endif /* Abstraction of data file content in the emerging image. */ struct Iso_File_Src { /* This marks an IsoFileSrc which shall only expose its extent addresses and sizes but shall not be counted or written by filesrc_writer. */ unsigned int no_write :1; unsigned int checksum_index :31; /* Is > 0 if the object was already put into the filelist array. In this case, the value is the order in which it was inserted, which can be used for stable sorting of the filelist array in case of identical file weights. */ size_t taken; /** File Sections of the file in the image */ /* Special sections[0].block values while they are relative before filesrc_writer_compute_data_blocks(). Valid only with .no_write == 0: 0xfffffffe This Iso_File_Src is claimed as external partition. Others will take care of the content data. filesrc_writer shall neither count nor write it. At write_data time it is already converted to a fileadress between Ecma119Image.ms_block and Ecma119Image.filesrc_start - 1. 0xffffffff This is the block to which empty files shall point. Normal data files have relative addresses from 0 to 0xffffffdf. They cannot be higher, because mspad_writer forces the absolute filesrc addresses to start at least at 0x20. */ struct iso_file_section *sections; int nsections; int sort_weight; IsoStream *stream; }; int iso_file_src_cmp(const void *n1, const void *n2); /** * Create a new IsoFileSrc to get data from a specific IsoFile. * * The IsoFileSrc will be cached in a tree to prevent the same file for * being written several times to image. If you call again this function * with a node that refers to the same source file, the previously * created one will be returned. No new IsoFileSrc is created in that case. * * @param img * The image where this file is to be written * @param file * The IsoNode we want to write * @param src * Will be filled with a pointer to the IsoFileSrc * @return * 1 if new object was created, 0 if object existed, < 0 on error */ int iso_file_src_create(Ecma119Image *img, IsoFile *file, IsoFileSrc **src); /** * Add a given IsoFileSrc to the given image target. * * The IsoFileSrc will be cached in a tree to prevent the same file for * being written several times to image. If you call again this function * with a node that refers to the same source file, the previously * created one will be returned. * * @param img * The image where this file is to be written * @param new * The IsoFileSrc to add * @param src * Will be filled with a pointer to the IsoFileSrc really present in * the tree. It could be different than new if the same file already * exists in the tree. * @return * 1 on success, 0 if file already exists on tree, < 0 error */ int iso_file_src_add(Ecma119Image *img, IsoFileSrc *new, IsoFileSrc **src); /** * Free the IsoFileSrc especific data */ void iso_file_src_free(void *node); /** * Get the size of the file this IsoFileSrc represents */ off_t iso_file_src_get_size(IsoFileSrc *file); /** * Create a Writer for file contents. * * It takes care of written the files in the correct order. */ int iso_file_src_writer_create(Ecma119Image *target); /** * Determine number of filesrc blocks in the image and compute extent addresses * relative to start of the file source writer area. * filesrc_writer_compute_data_blocks() later makes them absolute. */ int filesrc_writer_pre_compute(IsoImageWriter *writer); /** * Write the content of file into the output stream of t. * name must be NULL or offer at least PATH_MAX characters of storage. * buffer must be NULL or offer at least BLOCK_SIZE characters of storage. * flag is not used yet, submit 0. */ int iso_filesrc_write_data(Ecma119Image *t, IsoFileSrc *file, char *name, char *buffer, int flag); #endif /*LIBISO_FILESRC_H_*/ /* * Copyright (c) 2008 Vreixo Formoso * Copyright (c) 2009 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libisofs.h" #include "filter.h" #include "node.h" void iso_filter_ref(FilterContext *filter) { ++filter->refcount; } void iso_filter_unref(FilterContext *filter) { if (--filter->refcount == 0) { filter->free(filter); free(filter); } } int iso_file_add_filter(IsoFile *file, FilterContext *filter, int flag) { int ret; IsoStream *original, *filtered; if (file == NULL || filter == NULL) { return ISO_NULL_POINTER; } original = file->stream; if (!iso_stream_is_repeatable(original)) { /* TODO use custom error */ return ISO_WRONG_ARG_VALUE; } ret = filter->get_filter(filter, original, &filtered); if (ret < 0) { return ret; } iso_stream_unref(original); file->stream = filtered; return ISO_SUCCESS; } int iso_file_remove_filter(IsoFile *file, int flag) { IsoStream *file_stream, *input_stream; file_stream = file->stream; input_stream = iso_stream_get_input_stream(file_stream, 0); if (input_stream == NULL) return 0; file->stream = input_stream; iso_stream_ref(input_stream); /* Protect against _unref(file_stream) */ iso_stream_unref(file_stream); return 1; } /* * Copyright (c) 2008 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_FILTER_H_ #define LIBISO_FILTER_H_ /* * Definitions of filters. */ /* dev_id for stream identification */ /* libisofs/filters/xor_encrypt.c */ #define XOR_ENCRYPT_DEV_ID 1 /* libisofs/filters/external.c */ #define ISO_FILTER_EXTERNAL_DEV_ID 2 /* libisofs/filters/zisofs.c */ #define ISO_FILTER_ZISOFS_DEV_ID 3 /* libisofs/filters/gzip.c */ #define ISO_FILTER_GZIP_DEV_ID 4 typedef struct filter_context FilterContext; struct filter_context { int version; /* reserved for future usage, set to 0 */ int refcount; /** filter specific shared data */ void *data; /** * Factory method to create a filtered stream from another stream. * * @param original * The original stream to be filtered. If the filter needs a ref to * it (most cases), it should take a ref to it with iso_stream_ref(). * @param filtered * Will be filled with the filtered IsoStream (reference belongs to * caller). * @return * 1 on success, < 0 on error */ int (*get_filter)(FilterContext *filter, IsoStream *original, IsoStream **filtered); /** * Free implementation specific data. Should never be called by user. * Use iso_filter_unref() instead. */ void (*free)(FilterContext *filter); }; /** * * @param flag * Reserved for future usage, pass always 0 for now. * TODO in a future a different value can mean filter caching, where * the filter is applied once and the filtered file is stored in a temp * dir. This prevent filter to be applied several times. */ int iso_file_add_filter(IsoFile *file, FilterContext *filter, int flag); void iso_filter_ref(FilterContext *filter); void iso_filter_unref(FilterContext *filter); #endif /*LIBISO_FILTER_H_*/ /* * Copyright (c) 2009 - 2011 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. * * It implements a filter facility which can pipe a IsoStream into an external * process, read its output and forward it as IsoStream output to an IsoFile. * The external processes get started according to an IsoExternalFilterCommand * which is described in libisofs.h. * */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "../libisofs.h" #include "../filter.h" #include "../fsource.h" #include "../stream.h" #include #include #include #include #include #include #include #include #include #ifdef Libisofs_external_filters_selecT #include #endif /* * A filter that starts an external process and uses its stdin and stdout * for classical pipe filtering. */ /* IMPORTANT: Any change must be reflected by extf_clone_stream() */ /* * Individual runtime properties exist only as long as the stream is opened. */ typedef struct { int send_fd; int recv_fd; pid_t pid; off_t in_counter; int in_eof; off_t out_counter; int out_eof; uint8_t pipebuf[2048]; /* buffers in case of EAGAIN on write() */ int pipebuf_fill; } ExternalFilterRuntime; static int extf_running_new(ExternalFilterRuntime **running, int send_fd, int recv_fd, pid_t child_pid, int flag) { ExternalFilterRuntime *o; *running = o = calloc(sizeof(ExternalFilterRuntime), 1); if (o == NULL) { return ISO_OUT_OF_MEM; } o->send_fd = send_fd; o->recv_fd = recv_fd; o->pid = child_pid; o->in_counter = 0; o->in_eof = 0; o->out_counter = 0; o->out_eof = 0; memset(o->pipebuf, 0, sizeof(o->pipebuf)); o->pipebuf_fill = 0; return 1; } /* * The data payload of an individual IsoStream from External Filter */ typedef struct { ino_t id; IsoStream *orig; IsoExternalFilterCommand *cmd; off_t size; /* -1 means that the size is unknown yet */ ExternalFilterRuntime *running; /* is non-NULL when open */ } ExternalFilterStreamData; /* Each individual ExternalFilterStreamData needs a unique id number. */ /* >>> This is very suboptimal: The counter can rollover. */ static ino_t extf_ino_id = 0; /* <<< */ static int print_fd= 0; /* * Methods for the IsoStreamIface of an External Filter object. */ /* * @param flag bit0= original stream is not open */ static int extf_stream_close_flag(IsoStream *stream, int flag) { int ret, status; ExternalFilterStreamData *data; if (stream == NULL) { return ISO_NULL_POINTER; } data = stream->data; if (data->running == NULL) { return 1; } /* <<< */ if (print_fd) { fprintf(stderr, "libisofs_DEBUG: filter close in = %d , ic= %.f\n", data->running->recv_fd, (double) data->running->in_counter); fprintf(stderr, "libisofs_DEBUG: filter close out = %d , oc= %.f\n", data->running->send_fd, (double) data->running->out_counter); } if(data->running->recv_fd != -1) close(data->running->recv_fd); if(data->running->send_fd != -1) close(data->running->send_fd); ret = waitpid(data->running->pid, &status, WNOHANG); if (ret == 0 && data->running->pid != 0) { kill(data->running->pid, SIGKILL); waitpid(data->running->pid, &status, 0); } free(data->running); data->running = NULL; if (flag & 1) return 1; return iso_stream_close(data->orig); } static int extf_stream_close(IsoStream *stream) { return extf_stream_close_flag(stream, 0); } /* * @param flag bit0= do not run .get_size() if size is < 0 */ static int extf_stream_open_flag(IsoStream *stream, int flag) { ExternalFilterStreamData *data; ExternalFilterRuntime *running = NULL; pid_t child_pid; int send_pipe[2], recv_pipe[2], ret, stream_open = 0; send_pipe[0] = send_pipe[1] = recv_pipe[0] = recv_pipe[1] = -1; if (stream == NULL) { return ISO_NULL_POINTER; } data = (ExternalFilterStreamData*)stream->data; if (data->running != NULL) { return ISO_FILE_ALREADY_OPENED; } if (data->size < 0 && !(flag & 1)) { /* Do the size determination run now, so that the size gets cached and .get_size() will not fail on an opened stream. */ stream->class->get_size(stream); } ret = pipe(send_pipe); if (ret == -1) { ret = ISO_OUT_OF_MEM; goto parent_failed; } ret = pipe(recv_pipe); if (ret == -1) { ret = ISO_OUT_OF_MEM; goto parent_failed; } child_pid= fork(); if (child_pid == -1) { ret = ISO_DATA_SOURCE_FATAL; goto parent_failed; } if (child_pid != 0) { /* parent */ ret = extf_running_new(&running, send_pipe[1], recv_pipe[0], child_pid, 0); if (ret < 0) { goto parent_failed; } data->running = running; /* <<< */ if (print_fd) { fprintf(stderr, "libisofs_DEBUG: filter parent in = %d\n", data->running->recv_fd); fprintf(stderr, "libisofs_DEBUG: filter parent out = %d\n", data->running->send_fd); } /* Give up the child-side pipe ends */ close(send_pipe[0]); close(recv_pipe[1]); /* Open stream only after forking so that the child does not know the pipe inlets of eventually underlying other filter streams. They would stay open and prevent those underlying filter children from seeing EOF at their input. */ ret = iso_stream_open(data->orig); /* <<< TEST <<< ret= ISO_FILE_READ_ERROR; */ if (ret < 0) { /* Dispose pipes and child */ extf_stream_close_flag(stream, 1); return ret; } stream_open = 1; /* Make filter outlet non-blocking */ ret = fcntl(recv_pipe[0], F_GETFL); if (ret != -1) { ret |= O_NONBLOCK; fcntl(recv_pipe[0], F_SETFL, ret); } /* Make filter sink non-blocking */ ret = fcntl(send_pipe[1], F_GETFL); if (ret != -1) { ret |= O_NONBLOCK; fcntl(send_pipe[1], F_SETFL, ret); } return 1; } /* child */ /* Give up the parent-side pipe ends */ close(send_pipe[1]); close(recv_pipe[0]); /* attach pipe ends to stdin and stdout */; close(0); ret = dup2(send_pipe[0], 0); if (ret == -1) { goto child_failed; } close(1); ret = dup2(recv_pipe[1], 1); if (ret == -1) { goto child_failed; } /* <<< */ if (print_fd) { fprintf(stderr, "libisofs_DEBUG: filter child in = %d\n", send_pipe[0]); fprintf(stderr, "libisofs_DEBUG: filter child out = %d\n", recv_pipe[1]); } /* Self conversion into external program */ execv(data->cmd->path, data->cmd->argv); /* should never come back */ child_failed:; fprintf(stderr,"--- execution of external filter command failed:\n"); fprintf(stderr," %s\n", data->cmd->path); exit(127); parent_failed:; /* <<< */ if (print_fd) { fprintf(stderr, "libisofs_DEBUG: FAILED : filter parent in = %d\n", recv_pipe[0]); fprintf(stderr, "libisofs_DEBUG: FAILED : filter parent out = %d\n", send_pipe[1]); } if (stream_open) iso_stream_close(data->orig); if(send_pipe[0] != -1) close(send_pipe[0]); if(send_pipe[1] != -1) close(send_pipe[1]); if(recv_pipe[0] != -1) close(recv_pipe[0]); if(recv_pipe[1] != -1) close(recv_pipe[1]); return ret; } static int extf_stream_open(IsoStream *stream) { return extf_stream_open_flag(stream, 0); } #ifdef Libisofs_external_filters_selecT /* Performance is weaker than with non-blocking i/o and usleep(). */ static int extf_wait_for_io(int fd_in, int fd_out, int microsec, int flag) { struct timeval wt; fd_set rds,wts,exs; int ready, fd_max; fd_max = fd_out; if (fd_in > fd_out) fd_max = fd_in; FD_ZERO(&rds); FD_ZERO(&wts); FD_ZERO(&exs); if (fd_in >= 0) { FD_SET(fd_in,&rds); FD_SET(fd_in,&exs); } if (fd_out >= 0) { FD_SET(fd_out,&rds); FD_SET(fd_in,&exs); } wt.tv_sec = microsec/1000000; wt.tv_usec = microsec%1000000; ready = select(fd_max + 1, &rds, &wts, &exs, &wt); if (ready <= 0) return 0; if (fd_in >= 0) { if (FD_ISSET(fd_in, &rds)) return 1; } if (fd_out >= 0) { if (FD_ISSET(fd_out, &rds)) return 2; } if (fd_in >= 0) { if (FD_ISSET(fd_in, &exs)) return -1; } if (fd_out >= 0) { if (FD_ISSET(fd_out, &exs)) return -2; } return(0); } #endif /* Libisofs_external_filters_selecT */ static int extf_stream_read(IsoStream *stream, void *buf, size_t desired) { int ret, blocking = 0; ExternalFilterStreamData *data; ExternalFilterRuntime *running; size_t fill = 0; if (stream == NULL) { return ISO_NULL_POINTER; } data = stream->data; running= data->running; if (running == NULL) { return ISO_FILE_NOT_OPENED; } if (running->out_eof) { return 0; } while (1) { if (running->in_eof && !blocking) { /* Make filter outlet blocking */ ret = fcntl(running->recv_fd, F_GETFL); if (ret != -1) { ret &= ~O_NONBLOCK; fcntl(running->recv_fd, F_SETFL, ret); } blocking = 1; } /* Try to read desired amount from filter */; while (1) { ret = read(running->recv_fd, ((char *) buf) + fill, desired - fill); if (ret < 0) { if (errno == EAGAIN) break; return ISO_FILE_READ_ERROR; } fill += ret; if (ret == 0) { running->out_eof = 1; } if (ret == 0 || fill >= desired) { running->out_counter += fill; return fill; } } if (running->in_eof) { usleep(1000); /* just in case it is still non-blocking */ continue; } if (running->pipebuf_fill) { ret = running->pipebuf_fill; running->pipebuf_fill = 0; } else { ret = iso_stream_read(data->orig, running->pipebuf, sizeof(running->pipebuf)); if (ret > 0) running->in_counter += ret; } if (ret < 0) { running->in_eof = 1; return ret; } if (ret == 0) { /* <<< */ if (print_fd) { fprintf(stderr, "libisofs_DEBUG: filter close out = %d , ic= %.f\n", running->send_fd, (double) running->in_counter); } running->in_eof = 1; close(running->send_fd); /* Tell the filter: it is over */ running->send_fd = -1; } else { running->pipebuf_fill = ret; ret = write(running->send_fd, running->pipebuf, running->pipebuf_fill); if (ret == -1) { if (errno == EAGAIN) { #ifdef Libisofs_external_filters_selecT /* This select() based waiting saves 10 % CPU load but needs 50 % more real time */ ret = extf_wait_for_io(running->recv_fd, running->send_fd, 100000, 0); if (ret < 0) usleep(1000); /* To make sure sufficient laziness */ #else /* No sleeping needs 90 % more CPU and saves 6 % time */ usleep(1000); /* go lazy because the filter is slow */ #endif /* ! Libisofs_external_filters_selecT */ continue; } /* From the view of the caller it _is_ a read error */ running->in_eof = 1; return ISO_FILE_READ_ERROR; } running->pipebuf_fill = 0; } } return ISO_FILE_READ_ERROR; /* should never be hit */ } static off_t extf_stream_get_size(IsoStream *stream) { int ret, ret_close; off_t count = 0; ExternalFilterStreamData *data; char buf[64 * 1024]; size_t bufsize = 64 * 1024; if (stream == NULL) { return ISO_NULL_POINTER; } data = stream->data; if (data->size >= 0) { return data->size; } /* Run filter command and count output bytes */ ret = extf_stream_open_flag(stream, 1); if (ret < 0) { return ret; } while (1) { ret = extf_stream_read(stream, buf, bufsize); if (ret <= 0) break; count += ret; } ret_close = extf_stream_close(stream); if (ret < 0) return ret; if (ret_close < 0) return ret_close; data->size = count; return count; } static int extf_stream_is_repeatable(IsoStream *stream) { /* Only repeatable streams are accepted as orig */ return 1; } static void extf_stream_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, ino_t *ino_id) { ExternalFilterStreamData *data; data = stream->data; *fs_id = ISO_FILTER_FS_ID; *dev_id = ISO_FILTER_EXTERNAL_DEV_ID; *ino_id = data->id; } static void extf_stream_free(IsoStream *stream) { ExternalFilterStreamData *data; if (stream == NULL) { return; } data = stream->data; if (data->running != NULL) { extf_stream_close(stream); } iso_stream_unref(data->orig); if (data->cmd->refcount > 0) data->cmd->refcount--; free(data); } static int extf_update_size(IsoStream *stream) { /* By principle size is determined only once */ return 1; } static IsoStream *extf_get_input_stream(IsoStream *stream, int flag) { ExternalFilterStreamData *data; if (stream == NULL) { return NULL; } data = stream->data; return data->orig; } static int extf_clone_stream(IsoStream *old_stream, IsoStream **new_stream, int flag) { int ret; IsoStream *new_input_stream, *stream; ExternalFilterStreamData *stream_data, *old_stream_data; if (flag) return ISO_STREAM_NO_CLONE; /* unknown option required */ stream_data = calloc(1, sizeof(ExternalFilterStreamData)); if (stream_data == NULL) return ISO_OUT_OF_MEM; ret = iso_stream_clone_filter_common(old_stream, &stream, &new_input_stream, 0); if (ret < 0) { free((char *) stream_data); return ret; } old_stream_data = (ExternalFilterStreamData *) old_stream->data; stream_data->id = ++extf_ino_id; stream_data->orig = new_input_stream; stream_data->cmd = old_stream_data->cmd; stream_data->cmd->refcount++; stream_data->size = old_stream_data->size; stream_data->running = NULL; stream->data = stream_data; *new_stream = stream; return ISO_SUCCESS; } static int extf_cmp_ino(IsoStream *s1, IsoStream *s2); /* Function is defined after definition of extf_stream_class */ IsoStreamIface extf_stream_class = { 4, "extf", extf_stream_open, extf_stream_close, extf_stream_get_size, extf_stream_read, extf_stream_is_repeatable, extf_stream_get_id, extf_stream_free, extf_update_size, extf_get_input_stream, extf_cmp_ino, extf_clone_stream }; static int extf_cmp_ino(IsoStream *s1, IsoStream *s2) { int i; ExternalFilterStreamData *data1, *data2; IsoExternalFilterCommand *cmd1, *cmd2; /* This function may rely on being called by iso_stream_cmp_ino() only with s1, s2 which both point to it as their .cmp_ino() function. It would be a programming error to let any other than extf_stream_class point to extf_cmp_ino(). This fallback endangers transitivity of iso_stream_cmp_ino(). */ if (s1->class != &extf_stream_class || s2->class != &extf_stream_class) return iso_stream_cmp_ino(s1, s2, 1); data1 = (ExternalFilterStreamData*) s1->data; data2 = (ExternalFilterStreamData*) s2->data; cmd1 = data1->cmd; cmd2 = data2->cmd; if (cmd1 != cmd2) { if (strcmp(cmd1->name, cmd2->name) != 0) return strcmp(cmd1->name, cmd2->name); if (strcmp(cmd1->path, cmd2->path) != 0) return strcmp(cmd1->path, cmd2->path); if (cmd1->argc != cmd2->argc) return cmd1->argc < cmd2->argc ? -1 : 1; for (i = 0; i < cmd1->argc; i++) { if (strcmp(cmd1->argv[i], cmd2->argv[i]) != 0) return strcmp(cmd1->argv[i], cmd2->argv[i]); } if (cmd1->behavior != cmd2->behavior) return cmd1->behavior < cmd2->behavior ? -1 : 1; if (strcmp(cmd1->suffix, cmd2->suffix) != 0) return strcmp(cmd1->suffix, cmd2->suffix); } /* Both streams apply the same treatment to their input streams */ return iso_stream_cmp_ino(data1->orig, data2->orig, 0); } /* ------------------------------------------------------------------------- */ static void extf_filter_free(FilterContext *filter) { /* no data are allocated */; } /* To be called by iso_file_add_filter(). * The FilterContext input parameter is not furtherly needed for the * emerging IsoStream. */ static int extf_filter_get_filter(FilterContext *filter, IsoStream *original, IsoStream **filtered) { IsoStream *str; ExternalFilterStreamData *data; IsoExternalFilterCommand *cmd; if (filter == NULL || original == NULL || filtered == NULL) { return ISO_NULL_POINTER; } cmd = (IsoExternalFilterCommand *) filter->data; if (cmd->refcount + 1 <= 0) { return ISO_EXTF_TOO_OFTEN; } str = malloc(sizeof(IsoStream)); if (str == NULL) { return ISO_OUT_OF_MEM; } data = malloc(sizeof(ExternalFilterStreamData)); if (data == NULL) { free(str); return ISO_OUT_OF_MEM; } /* These data items are not owned by this filter object */ data->id = ++extf_ino_id; data->orig = original; data->cmd = cmd; data->size = -1; data->running = NULL; /* get reference to the source */ iso_stream_ref(data->orig); str->refcount = 1; str->data = data; str->class = &extf_stream_class; *filtered = str; cmd->refcount++; return ISO_SUCCESS; } /* Produce a parameter object suitable for iso_file_add_filter(). * It may be disposed by free() after all those calls are made. * * This is an internal call of libisofs to be used by an API call that * attaches an IsoExternalFilterCommand to one or more IsoFile objects. * See libisofs.h for IsoExternalFilterCommand. */ static int extf_create_context(IsoExternalFilterCommand *cmd, FilterContext **filter, int flag) { FilterContext *f; *filter = f = calloc(1, sizeof(FilterContext)); if (f == NULL) { return ISO_OUT_OF_MEM; } f->refcount = 1; f->version = 0; f->data = cmd; f->free = extf_filter_free; f->get_filter = extf_filter_get_filter; return ISO_SUCCESS; } /* * A function which adds a filter to an IsoFile shall create a temporary * FilterContext by iso_extf_create_context(), use it in one or more calls * of filter.c:iso_file_add_filter() and finally dispose it by free(). */ int iso_file_add_external_filter(IsoFile *file, IsoExternalFilterCommand *cmd, int flag) { int ret; FilterContext *f = NULL; IsoStream *stream; off_t original_size = 0, filtered_size = 0; if (cmd->behavior & (1 | 2 | 4)) { original_size = iso_file_get_size(file); if (original_size <= 0 || ((cmd->behavior & 4) && original_size <= 2048)) { return 2; } } ret = extf_create_context(cmd, &f, 0); if (ret < 0) { return ret; } ret = iso_file_add_filter(file, f, 0); free(f); if (ret < 0) { return ret; } /* Run a full filter process getsize so that the size is cached */ stream = iso_file_get_stream(file); filtered_size = iso_stream_get_size(stream); if (filtered_size < 0) { iso_file_remove_filter(file, 0); return filtered_size; } if (((cmd->behavior & 2) && filtered_size >= original_size) || ((cmd->behavior & 4) && filtered_size / 2048 >= original_size / 2048)){ ret = iso_file_remove_filter(file, 0); if (ret < 0) { return ret; } return 2; } return ISO_SUCCESS; } int iso_stream_get_external_filter(IsoStream *stream, IsoExternalFilterCommand **cmd, int flag) { ExternalFilterStreamData *data; if (stream->class != &extf_stream_class) return 0; data = stream->data; *cmd = data->cmd; return 1; } /* * Copyright (c) 2009 - 2011 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. * * It implements a filter facility which can pipe a IsoStream into gzip * compression resp. uncompression, read its output and forward it as IsoStream * output to an IsoFile. * The gzip compression is done via zlib by Jean-loup Gailly and Mark Adler * who state in : * "The data format used by the zlib library is described by RFCs (Request for * Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt * (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format)." * */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "../libisofs.h" #include "../filter.h" #include "../fsource.h" #include "../util.h" #include "../stream.h" #include #include #include #include #include #include #include #ifdef Libisofs_with_zliB #include #else /* If zlib is not available then this code is a dummy */ #endif /* * A filter that encodes or decodes the content of gzip compressed files. */ /* --------------------------- GzipFilterRuntime ------------------------- */ /* Individual runtime properties exist only as long as the stream is opened. */ typedef struct { #ifdef Libisofs_with_zliB z_stream strm; /* The zlib processing context */ #endif char *in_buffer; char *out_buffer; int in_buffer_size; int out_buffer_size; char *rpt; /* out_buffer + read_bytes */ off_t in_counter; off_t out_counter; int do_flush; /* flush mode for deflate() changes at end of input */ int error_ret; } GzipFilterRuntime; #ifdef Libisofs_with_zliB static int gzip_running_destroy(GzipFilterRuntime **running, int flag) { GzipFilterRuntime *o= *running; if (o == NULL) return 0; if (o->in_buffer != NULL) free(o->in_buffer); if (o->out_buffer != NULL) free(o->out_buffer); free((char *) o); *running = NULL; return 1; } static int gzip_running_new(GzipFilterRuntime **running, int flag) { GzipFilterRuntime *o; *running = o = calloc(sizeof(GzipFilterRuntime), 1); if (o == NULL) { return ISO_OUT_OF_MEM; } memset(&(o->strm), 0, sizeof(o->strm)); o->in_buffer = NULL; o->out_buffer = NULL; o->in_buffer_size = 0; o->out_buffer_size = 0; o->rpt = NULL; o->in_counter = 0; o->out_counter = 0; o->do_flush = Z_NO_FLUSH; o->error_ret = 1; o->in_buffer_size= 2048; o->out_buffer_size= 2048; o->in_buffer = calloc(o->in_buffer_size, 1); o->out_buffer = calloc(o->out_buffer_size, 1); if (o->in_buffer == NULL || o->out_buffer == NULL) goto failed; o->rpt = o->out_buffer; return 1; failed: gzip_running_destroy(running, 0); return -1; } #endif /* Libisofs_with_zliB */ /* ---------------------------- GzipFilterStreamData --------------------- */ /* Counts the number of active compression filters */ static off_t gzip_ref_count = 0; /* Counts the number of active uncompression filters */ static off_t gunzip_ref_count = 0; #ifdef Libisofs_with_zliB /* Parameter for deflateInit2() , see */ /* ??? get this from zisofs.c ziso_compression_level ? * ??? have an own global parameter API ? * For now level 6 seems to be a fine compromise between speed and size. */ static int gzip_compression_level = 6; #endif /* Libisofs_with_zliB */ /* * The data payload of an individual Gzip Filter IsoStream */ /* IMPORTANT: Any change must be reflected by gzip_clone_stream() */ typedef struct { IsoStream *orig; off_t size; /* -1 means that the size is unknown yet */ GzipFilterRuntime *running; /* is non-NULL when open */ ino_t id; } GzipFilterStreamData; #ifdef Libisofs_with_zliB /* Each individual GzipFilterStreamData needs a unique id number. */ /* >>> This is very suboptimal: The counter can rollover. */ static ino_t gzip_ino_id = 0; #endif /* Libisofs_with_zliB */ static int gzip_stream_uncompress(IsoStream *stream, void *buf, size_t desired); /* * Methods for the IsoStreamIface of a Gzip Filter object. */ /* * @param flag bit0= original stream is not open */ static int gzip_stream_close_flag(IsoStream *stream, int flag) { #ifdef Libisofs_with_zliB GzipFilterStreamData *data; if (stream == NULL) { return ISO_NULL_POINTER; } data = stream->data; if (data->running == NULL) { return 1; } if (stream->class->read == &gzip_stream_uncompress) { inflateEnd(&(data->running->strm)); } else { deflateEnd(&(data->running->strm)); } gzip_running_destroy(&(data->running), 0); if (flag & 1) return 1; return iso_stream_close(data->orig); #else return ISO_ZLIB_NOT_ENABLED; #endif } static int gzip_stream_close(IsoStream *stream) { return gzip_stream_close_flag(stream, 0); } /* * @param flag bit0= do not run .get_size() if size is < 0 */ static int gzip_stream_open_flag(IsoStream *stream, int flag) { #ifdef Libisofs_with_zliB GzipFilterStreamData *data; GzipFilterRuntime *running = NULL; int ret; z_stream *strm; if (stream == NULL) { return ISO_NULL_POINTER; } data = (GzipFilterStreamData*) stream->data; if (data->running != NULL) { return ISO_FILE_ALREADY_OPENED; } if (data->size < 0 && !(flag & 1)) { /* Do the size determination run now, so that the size gets cached and .get_size() will not fail on an opened stream. */ stream->class->get_size(stream); } ret = gzip_running_new(&running, stream->class->read == &gzip_stream_uncompress); if (ret < 0) { return ret; } data->running = running; /* Start up zlib compression context */ strm = &(running->strm); strm->zalloc = Z_NULL; strm->zfree = Z_NULL; strm->opaque = Z_NULL; if (stream->class->read == &gzip_stream_uncompress) { ret = inflateInit2(strm, 15 | 16); } else { ret = deflateInit2(strm, gzip_compression_level, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY); } if (ret != Z_OK) return ISO_ZLIB_COMPR_ERR; strm->next_out = (Bytef *) running->out_buffer; strm->avail_out = running->out_buffer_size; /* Open input stream */ ret = iso_stream_open(data->orig); if (ret < 0) { return ret; } return 1; #else return ISO_ZLIB_NOT_ENABLED; #endif } static int gzip_stream_open(IsoStream *stream) { return gzip_stream_open_flag(stream, 0); } /* * @param flag bit1= uncompress rather than compress */ static int gzip_stream_convert(IsoStream *stream, void *buf, size_t desired, int flag) { #ifdef Libisofs_with_zliB int ret, todo, cnv_ret, c_bytes; GzipFilterStreamData *data; GzipFilterRuntime *rng; size_t fill = 0; z_stream *strm; if (stream == NULL) { return ISO_NULL_POINTER; } data = stream->data; rng= data->running; if (rng == NULL) { return ISO_FILE_NOT_OPENED; } strm = &(rng->strm); if (rng->error_ret < 0) { return rng->error_ret; } else if (rng->error_ret == 0) { if (rng->out_buffer_size - strm->avail_out - (rng->rpt - rng->out_buffer) <= 0) return 0; } while (1) { /* Transfer eventual converted bytes from strm to buf */ c_bytes = rng->out_buffer_size - strm->avail_out - (rng->rpt - rng->out_buffer); if (c_bytes > 0) { todo = desired - fill; if (todo > c_bytes) todo = c_bytes; memcpy(((char *) buf) + fill, rng->rpt, todo); rng->rpt += todo; fill += todo; rng->out_counter += todo; } if (fill >= desired || rng->error_ret == 0) return fill; /* All buffered out data are consumed now */ rng->rpt = rng->out_buffer; strm->next_out = (Bytef *) rng->out_buffer; strm->avail_out = rng->out_buffer_size; if (strm->avail_in == 0) { /* All pending input is consumed. Get new input. */ ret = iso_stream_read(data->orig, rng->in_buffer, rng->in_buffer_size); if (ret < 0) return (rng->error_ret = ret); if (ret == 0) { if (flag & 2) { /* Early input EOF */ return (rng->error_ret = ISO_ZLIB_EARLY_EOF); } else { /* Tell zlib by the next call that it is over */ rng->do_flush = Z_FINISH; } } strm->next_in = (Bytef *) rng->in_buffer; strm->avail_in = ret; rng->in_counter += ret; } /* Submit input and fetch output until input is consumed */ while (1) { if (flag & 2) { cnv_ret = inflate(strm, rng->do_flush); } else { cnv_ret = deflate(strm, rng->do_flush); } if (cnv_ret == Z_STREAM_ERROR || cnv_ret == Z_BUF_ERROR) { return (rng->error_ret = ISO_ZLIB_COMPR_ERR); } if ((int) strm->avail_out < rng->out_buffer_size) break; /* output is available */ if (strm->avail_in == 0) /* all pending input consumed */ break; } if (cnv_ret == Z_STREAM_END) rng->error_ret = 0; } return fill; #else return ISO_ZLIB_NOT_ENABLED; #endif } static int gzip_stream_compress(IsoStream *stream, void *buf, size_t desired) { return gzip_stream_convert(stream, buf, desired, 0); } static int gzip_stream_uncompress(IsoStream *stream, void *buf, size_t desired) { return gzip_stream_convert(stream, buf, desired, 2); } static off_t gzip_stream_get_size(IsoStream *stream) { int ret, ret_close; off_t count = 0; GzipFilterStreamData *data; char buf[64 * 1024]; size_t bufsize = 64 * 1024; if (stream == NULL) { return ISO_NULL_POINTER; } data = stream->data; if (data->size >= 0) { return data->size; } /* Run filter command and count output bytes */ ret = gzip_stream_open_flag(stream, 1); if (ret < 0) { return ret; } while (1) { ret = stream->class->read(stream, buf, bufsize); if (ret <= 0) break; count += ret; } ret_close = gzip_stream_close(stream); if (ret < 0) return ret; if (ret_close < 0) return ret_close; data->size = count; return count; } static int gzip_stream_is_repeatable(IsoStream *stream) { /* Only repeatable streams are accepted as orig */ return 1; } static void gzip_stream_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, ino_t *ino_id) { GzipFilterStreamData *data; data = stream->data; *fs_id = ISO_FILTER_FS_ID; *dev_id = ISO_FILTER_GZIP_DEV_ID; *ino_id = data->id; } static void gzip_stream_free(IsoStream *stream) { GzipFilterStreamData *data; if (stream == NULL) { return; } data = stream->data; if (data->running != NULL) { gzip_stream_close(stream); } if (stream->class->read == &gzip_stream_uncompress) { if (--gunzip_ref_count < 0) gunzip_ref_count = 0; } else { if (--gzip_ref_count < 0) gzip_ref_count = 0; } iso_stream_unref(data->orig); free(data); } static int gzip_update_size(IsoStream *stream) { /* By principle size is determined only once */ return 1; } static IsoStream *gzip_get_input_stream(IsoStream *stream, int flag) { GzipFilterStreamData *data; if (stream == NULL) { return NULL; } data = stream->data; return data->orig; } static int gzip_clone_stream(IsoStream *old_stream, IsoStream **new_stream, int flag) { #ifdef Libisofs_with_zliB int ret; IsoStream *new_input_stream, *stream; GzipFilterStreamData *stream_data, *old_stream_data; if (flag) return ISO_STREAM_NO_CLONE; /* unknown option required */ stream_data = calloc(1, sizeof(GzipFilterStreamData)); if (stream_data == NULL) return ISO_OUT_OF_MEM; ret = iso_stream_clone_filter_common(old_stream, &stream, &new_input_stream, 0); if (ret < 0) { free((char *) stream_data); return ret; } old_stream_data = (GzipFilterStreamData *) old_stream->data; stream_data->orig = new_input_stream; stream_data->size = old_stream_data->size; stream_data->running = NULL; stream_data->id = ++gzip_ino_id; stream->data = stream_data; *new_stream = stream; return ISO_SUCCESS; #else /* Libisofs_with_zliB */ return ISO_STREAM_NO_CLONE; #endif /* ! Libisofs_with_zliB */ } static int gzip_cmp_ino(IsoStream *s1, IsoStream *s2); static int gzip_uncompress_cmp_ino(IsoStream *s1, IsoStream *s2); IsoStreamIface gzip_stream_compress_class = { 4, "gzip", gzip_stream_open, gzip_stream_close, gzip_stream_get_size, gzip_stream_compress, gzip_stream_is_repeatable, gzip_stream_get_id, gzip_stream_free, gzip_update_size, gzip_get_input_stream, gzip_cmp_ino, gzip_clone_stream }; IsoStreamIface gzip_stream_uncompress_class = { 4, "pizg", gzip_stream_open, gzip_stream_close, gzip_stream_get_size, gzip_stream_uncompress, gzip_stream_is_repeatable, gzip_stream_get_id, gzip_stream_free, gzip_update_size, gzip_get_input_stream, gzip_uncompress_cmp_ino, gzip_clone_stream }; static int gzip_cmp_ino(IsoStream *s1, IsoStream *s2) { /* This function may rely on being called by iso_stream_cmp_ino() only with s1, s2 which both point to it as their .cmp_ino() function. It would be a programming error to let any other than gzip_stream_compress_class point to gzip_cmp_ino(). This fallback endangers transitivity of iso_stream_cmp_ino(). */ if (s1->class != s2->class || (s1->class != &gzip_stream_compress_class && s2->class != &gzip_stream_compress_class)) return iso_stream_cmp_ino(s1, s2, 1); /* Both streams apply the same treatment to their input streams */ return iso_stream_cmp_ino(iso_stream_get_input_stream(s1, 0), iso_stream_get_input_stream(s2, 0), 0); } static int gzip_uncompress_cmp_ino(IsoStream *s1, IsoStream *s2) { /* This function may rely on being called by iso_stream_cmp_ino() only with s1, s2 which both point to it as their .cmp_ino() function. It would be a programming error to let any other than gzip_stream_uncompress_class point to gzip_uncompress_cmp_ino(). */ if (s1->class != s2->class || (s1->class != &gzip_stream_uncompress_class && s2->class != &gzip_stream_uncompress_class)) return iso_stream_cmp_ino(s1, s2, 1); /* Both streams apply the same treatment to their input streams */ return iso_stream_cmp_ino(iso_stream_get_input_stream(s1, 0), iso_stream_get_input_stream(s2, 0), 0); } /* ------------------------------------------------------------------------- */ #ifdef Libisofs_with_zliB static void gzip_filter_free(FilterContext *filter) { /* no data are allocated */; } /* * @param flag bit1= Install a decompression filter */ static int gzip_filter_get_filter(FilterContext *filter, IsoStream *original, IsoStream **filtered, int flag) { IsoStream *str; GzipFilterStreamData *data; if (filter == NULL || original == NULL || filtered == NULL) { return ISO_NULL_POINTER; } str = calloc(sizeof(IsoStream), 1); if (str == NULL) { return ISO_OUT_OF_MEM; } data = calloc(sizeof(GzipFilterStreamData), 1); if (data == NULL) { free(str); return ISO_OUT_OF_MEM; } /* These data items are not owned by this filter object */ data->id = ++gzip_ino_id; data->orig = original; data->size = -1; data->running = NULL; /* get reference to the source */ iso_stream_ref(data->orig); str->refcount = 1; str->data = data; if (flag & 2) { str->class = &gzip_stream_uncompress_class; gunzip_ref_count++; } else { str->class = &gzip_stream_compress_class; gzip_ref_count++; } *filtered = str; return ISO_SUCCESS; } /* To be called by iso_file_add_filter(). * The FilterContext input parameter is not furtherly needed for the * emerging IsoStream. */ static int gzip_filter_get_compressor(FilterContext *filter, IsoStream *original, IsoStream **filtered) { return gzip_filter_get_filter(filter, original, filtered, 0); } static int gzip_filter_get_uncompressor(FilterContext *filter, IsoStream *original, IsoStream **filtered) { return gzip_filter_get_filter(filter, original, filtered, 2); } /* Produce a parameter object suitable for iso_file_add_filter(). * It may be disposed by free() after all those calls are made. * * This is quite a dummy as it does not carry individual data. * @param flag bit1= Install a decompression filter */ static int gzip_create_context(FilterContext **filter, int flag) { FilterContext *f; *filter = f = calloc(1, sizeof(FilterContext)); if (f == NULL) { return ISO_OUT_OF_MEM; } f->refcount = 1; f->version = 0; f->data = NULL; f->free = gzip_filter_free; if (flag & 2) f->get_filter = gzip_filter_get_uncompressor; else f->get_filter = gzip_filter_get_compressor; return ISO_SUCCESS; } #endif /* Libisofs_with_zliB */ /* * @param flag bit0= if_block_reduction rather than if_reduction * bit1= Install a decompression filter * bit2= only inquire availability of gzip filtering * bit3= do not inquire size */ int gzip_add_filter(IsoFile *file, int flag) { #ifdef Libisofs_with_zliB int ret; FilterContext *f = NULL; IsoStream *stream; off_t original_size = 0, filtered_size = 0; if (flag & 4) return 2; original_size = iso_file_get_size(file); ret = gzip_create_context(&f, flag & 2); if (ret < 0) { return ret; } ret = iso_file_add_filter(file, f, 0); free(f); if (ret < 0) { return ret; } if (flag & 8) /* size will be filled in by caller */ return ISO_SUCCESS; /* Run a full filter process getsize so that the size is cached */ stream = iso_file_get_stream(file); filtered_size = iso_stream_get_size(stream); if (filtered_size < 0) { iso_file_remove_filter(file, 0); return filtered_size; } if ((filtered_size >= original_size || ((flag & 1) && filtered_size / 2048 >= original_size / 2048)) && !(flag & 2)){ ret = iso_file_remove_filter(file, 0); if (ret < 0) { return ret; } return 2; } return ISO_SUCCESS; #else return ISO_ZLIB_NOT_ENABLED; #endif /* ! Libisofs_with_zliB */ } /* API function */ int iso_file_add_gzip_filter(IsoFile *file, int flag) { return gzip_add_filter(file, flag & ~8); } /* API function */ int iso_gzip_get_refcounts(off_t *gzip_count, off_t *gunzip_count, int flag) { *gzip_count = gzip_ref_count; *gunzip_count = gunzip_ref_count; return ISO_SUCCESS; } /* * Copyright (c) 2009 - 2020 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. * * It implements a filter facility which can pipe a IsoStream into zisofs * compression resp. uncompression, read its output and forward it as IsoStream * output to an IsoFile. * The zisofs format was invented by H. Peter Anvin. See doc/zisofs_format.txt * It is writeable and readable by zisofs-tools, readable by Linux kernels. * */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "../libisofs.h" #include "../filter.h" #include "../fsource.h" #include "../util.h" #include "../stream.h" #include "../messages.h" #include #include #include #include #include #include #include #ifdef Libisofs_with_zliB #include #else /* If zlib is not available then this code is a dummy */ #endif /* * A filter that encodes or decodes the content of zisofs compressed files. */ /* The lowest size of a file which shall not be represented by zisofs v1 */ /* The constant 4294967296 causes protests about int size on 32 bit machines */ #define ISO_ZISOFS_V1_LIMIT ((off_t) (1 << 16) * (off_t) (1 << 16)) /* zisofs2: Test value for small mixed-version ISOs: 1 million ISO_ZISOFS_V1_LIMIT 1000000 */ /* Minimum and maximum blocks sizes for version 1 and 2 */ #define ISO_ZISOFS_V1_MIN_LOG2 15 #define ISO_ZISOFS_V1_MAX_LOG2 17 #define ISO_ZISOFS_V2_MIN_LOG2 15 #define ISO_ZISOFS_V2_MAX_LOG2 20 /* ------------------- Defaults of runtime parameters ------------------ */ /* Limit for overall count of allocated block pointers: 2 exp 25 = 256 MiB blocklist buffer = 4 TiB uncompressed at 128 KiB */ #define ISO_ZISOFS_MAX_BLOCKS_T 0x2000000 /* Function to account for global block pointers */ static uint64_t ziso_block_pointer_mgt(uint64_t num, int mode); /* Limit for single files: 2 exp 25 = 256 MiB blocklist buffer = 4 TiB uncompressed at 128 KiB */ #define ISO_ZISOFS_MAX_BLOCKS_F 0x2000000 /* The number of blocks from which on the block pointer list shall be discarded * on iso_stream_close() of a compressing stream. This means that the pointers * have to be determined again on next ziso_stream_compress(), so that adding * a zisofs compression filter and writing the compressed stream needs in the * sum three read runs of the input stream. * <= 0 disables this file size based discarding. */ #define ISO_ZISOFS_MANY_BLOCKS 0 /* A ratio describing the part of the maximum number of block pointers which * shall be kept free by intermediate discarding of block pointers. See above * ISO_ZISOFS_MANY_BLOCKS. -1.0 disables this feature. */ #define ISO_ZISOFS_KBF_RATIO -1.0 /* --------------------------- Runtime parameters ------------------------- */ /* Sizes to be used for compression. Decompression learns from input header. */ static uint8_t ziso_block_size_log2 = 15; static int ziso_v2_enabled = 0; static int ziso_v2_block_size_log2 = 17; static int64_t ziso_block_number_target = -1; static int64_t ziso_max_total_blocks = ISO_ZISOFS_MAX_BLOCKS_T; static int64_t ziso_max_file_blocks = ISO_ZISOFS_MAX_BLOCKS_F; static int64_t ziso_many_block_limit = ISO_ZISOFS_MANY_BLOCKS; static double ziso_keep_blocks_free_ratio = ISO_ZISOFS_KBF_RATIO; /* Discard block pointers on last stream close even if the size constraints * are not met. To be set to 1 at block pointer overflow. To be set to 0 * when all compression filters are deleted. */ static int ziso_early_bpt_discard = 0; /* 1 = produce Z2 entries for zisofs2 , 0 = produce ZF for zisofs2 * This is used as extern variable in rockridge.c */ int iso_zisofs2_enable_susp_z2 = 0; static int ziso_decide_v2_usage(off_t orig_size) { if (ziso_v2_enabled > 1 || (ziso_v2_enabled == 1 && orig_size >= (off_t) ISO_ZISOFS_V1_LIMIT)) return 1; return 0; } static int ziso_decide_bs_log2(off_t orig_size) { int bs_log2, bs_log2_min, i; off_t bs; if (ziso_decide_v2_usage(orig_size)) { bs_log2 = ziso_v2_block_size_log2; bs_log2_min = ISO_ZISOFS_V2_MIN_LOG2; } else { bs_log2 = ziso_block_size_log2; bs_log2_min = ISO_ZISOFS_V1_MIN_LOG2; } if (ziso_block_number_target <= 0) return bs_log2; for (i = bs_log2_min; i < bs_log2; i++) { bs = (1 << i); if (orig_size / bs + !!(orig_size % bs) + 1 <= ziso_block_number_target) return i; } return bs_log2; } /* --------------------------- ZisofsFilterRuntime ------------------------- */ /* Individual runtime properties exist only as long as the stream is opened. */ typedef struct { int state; /* processing: 0= header, 1= block pointers, 2= data blocks */ int zisofs_version; /* 1 or 2 */ int block_size; int64_t block_pointer_fill; int64_t block_pointer_rpos; uint64_t *block_pointers; /* These are in use only with uncompression. Compression streams hold the pointer in their persistent data. */ char *read_buffer; char *block_buffer; int buffer_size; int buffer_fill; int buffer_rpos; off_t block_counter; off_t in_counter; off_t out_counter; int error_ret; } ZisofsFilterRuntime; static int ziso_running_destroy(ZisofsFilterRuntime **running, int flag) { ZisofsFilterRuntime *o= *running; if (o == NULL) return 0; if (o->block_pointers != NULL) { ziso_block_pointer_mgt((uint64_t) o->block_pointer_fill, 2); free(o->block_pointers); } if (o->read_buffer != NULL) free(o->read_buffer); if (o->block_buffer != NULL) free(o->block_buffer); free((char *) o); *running = NULL; return 1; } /* * @param flag bit0= do not set block_size, do not allocate buffers */ static int ziso_running_new(ZisofsFilterRuntime **running, off_t orig_size, int flag) { ZisofsFilterRuntime *o; *running = o = calloc(sizeof(ZisofsFilterRuntime), 1); if (o == NULL) { return ISO_OUT_OF_MEM; } o->state = 0; o->block_size= 0; o->zisofs_version = 0; o->block_pointer_fill = 0; o->block_pointer_rpos = 0; o->block_pointers = NULL; o->read_buffer = NULL; o->block_buffer = NULL; o->buffer_size = 0; o->buffer_fill = 0; o->buffer_rpos = 0; o->block_counter = 0; o->in_counter = 0; o->out_counter = 0; o->error_ret = 0; if (flag & 1) return 1; o->block_size = (1 << ziso_decide_bs_log2(orig_size)); #ifdef Libisofs_with_zliB o->buffer_size = compressBound((uLong) o->block_size); #else o->buffer_size = 2 * o->block_size; #endif o->read_buffer = calloc(o->block_size, 1); o->block_buffer = calloc(o->buffer_size, 1); if (o->block_buffer == NULL || o->read_buffer == NULL) goto failed; return 1; failed: ziso_running_destroy(running, 0); return -1; } /* --------------------------- Resource accounting ------------------------- */ /* @param mode 0= inquire whether num block pointers would fit 1= register num block pointers 2= unregister num block_pointers 3= return number of accounted block pointers @return if not mode 3: 0= does not fit , 1= fits */ static uint64_t ziso_block_pointer_mgt(uint64_t num, int mode) { static uint64_t global_count = 0; static int underrun = 0; if (mode == 2) { if (global_count < num) { if (underrun < 3) iso_msg_submit(-1, ISO_ZISOFS_BPT_UNDERRUN, 0, "Prevented global block pointer counter underrun"); underrun++; global_count = 0; } else { global_count -= num; } } else if (mode == 3) { return global_count; } else { if (global_count + num > (uint64_t) ziso_max_total_blocks) return 0; if (mode == 1) global_count += num; } return 1; } /* ---------------------------- ZisofsFilterStreamData --------------------- */ /* The first 8 bytes of a zisofs compressed data file */ static unsigned char zisofs_magic[9] = {0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07}; /* The first 8 bytes of a zisofs2 compressed data file */ static unsigned char zisofs2_magic[9] = {0xEF, 0x22, 0x55, 0xA1, 0xBC, 0x1B, 0x95, 0xA0}; /* Counts the number of active compression filters */ static off_t ziso_ref_count = 0; /* Counts the number of active uncompression filters */ static off_t ziso_osiz_ref_count = 0; #ifdef Libisofs_with_zliB /* Parameter for compress2() , see */ static int ziso_compression_level = 6; #endif /* Libisofs_with_zliB */ /* * The common data payload of an individual Zisofs Filter IsoStream * IMPORTANT: Any change must be reflected by ziso_clone_stream(). */ typedef struct { IsoStream *orig; off_t size; /* -1 means that the size is unknown yet */ ZisofsFilterRuntime *running; /* is non-NULL when open */ ino_t id; } ZisofsFilterStreamData; /* * The data payload of an individual Zisofs Filter Compressor IsoStream * IMPORTANT: Any change must be reflected by ziso_clone_stream(). */ typedef struct { ZisofsFilterStreamData std; uint64_t orig_size; uint64_t *block_pointers; /* Cache for output block addresses. They get written before the data and so need 2 passes. This cache avoids surplus passes. */ uint64_t block_pointer_counter; uint64_t open_counter; int block_pointers_dropped; } ZisofsComprStreamData; /* * The data payload of an individual Zisofs Filter Uncompressor IsoStream * IMPORTANT: Any change must be reflected by ziso_clone_stream(). */ typedef struct { ZisofsFilterStreamData std; uint8_t zisofs_algo_num; unsigned char header_size_div4; unsigned char block_size_log2; } ZisofsUncomprStreamData; /* Each individual ZisofsFilterStreamData needs a unique id number. */ /* >>> This is very suboptimal: The counter can rollover. */ static ino_t ziso_ino_id = 0; /* * Methods for the IsoStreamIface of an Zisofs Filter object. */ static int ziso_stream_uncompress(IsoStream *stream, void *buf, size_t desired); static int ziso_stream_compress(IsoStream *stream, void *buf, size_t desired); /* * @param flag bit0= discard even if the size conditions are not met bit1= check for open_counter == 1 rather than == 0 */ static int ziso_discard_bpt(IsoStream *stream, int flag) { ZisofsFilterStreamData *data; ZisofsComprStreamData *cstd = NULL; int block_size; double max_blocks, free_blocks; data = stream->data; if (stream->class->read == &ziso_stream_compress) cstd = (ZisofsComprStreamData *) data; if (cstd == NULL) return 0; block_size = (1 << ziso_decide_bs_log2(cstd->orig_size)); max_blocks = ziso_max_file_blocks; if (max_blocks < 1.0) max_blocks = 1.0; free_blocks = ziso_max_total_blocks - ziso_block_pointer_mgt((uint64_t) 0, 3); if (cstd->block_pointers == NULL) { return 0; } else if (cstd->open_counter != !!(flag & 2)) { return 0; } else if (!((flag & 1) || ziso_early_bpt_discard)) { if (ziso_many_block_limit <= 0 || cstd->orig_size / block_size + !!(cstd->orig_size % block_size) + 1 < (uint64_t) ziso_many_block_limit) if (ziso_keep_blocks_free_ratio < 0.0 || free_blocks / max_blocks >= ziso_keep_blocks_free_ratio) return 0; } ziso_block_pointer_mgt(cstd->block_pointer_counter, 2); free((char *) cstd->block_pointers); cstd->block_pointers_dropped = 1; cstd->block_pointers = NULL; cstd->block_pointer_counter = 0; return 1; } /* * @param flag bit0= original stream is not open * bit1= do not destroy large * ZisofsComprStreamData->block_pointers */ static int ziso_stream_close_flag(IsoStream *stream, int flag) { ZisofsFilterStreamData *data; ZisofsComprStreamData *cstd = NULL; if (stream == NULL) { return ISO_NULL_POINTER; } data = stream->data; if (stream->class->read == &ziso_stream_compress) cstd = (ZisofsComprStreamData *) data; if (cstd != NULL && !(flag & 2)) ziso_discard_bpt(stream, 2); if (data->running == NULL) { return 1; } ziso_running_destroy(&(data->running), 0); if (flag & 1) return 1; if (cstd != NULL) if (cstd->open_counter > 0) cstd->open_counter--; return iso_stream_close(data->orig); } static int ziso_stream_close(IsoStream *stream) { return ziso_stream_close_flag(stream, 0); } /* * @param flag bit0= do not run .get_size() if size is < 0 */ static int ziso_stream_open_flag(IsoStream *stream, int flag) { ZisofsFilterStreamData *data; ZisofsComprStreamData *cstd; ZisofsFilterRuntime *running = NULL; int ret; off_t orig_size = 0; if (stream == NULL) { return ISO_NULL_POINTER; } data = (ZisofsFilterStreamData*) stream->data; if (data->running != NULL) { return ISO_FILE_ALREADY_OPENED; } if (data->size < 0 && !(flag & 1)) { /* Do the size determination run now, so that the size gets cached and .get_size() will not fail on an opened stream. */ stream->class->get_size(stream); } orig_size = data->size; if (stream->class->read == &ziso_stream_compress) { cstd = (ZisofsComprStreamData *) data; cstd->open_counter++; orig_size = cstd->orig_size; } if (orig_size < 0) return ISO_ZISOFS_UNKNOWN_SIZE; ret = ziso_running_new(&running, orig_size, (stream->class->read == &ziso_stream_uncompress)); if (ret < 0) { return ret; } data->running = running; ret = iso_stream_open(data->orig); if (ret < 0) { return ret; } return 1; } static int ziso_stream_open(IsoStream *stream) { return ziso_stream_open_flag(stream, 0); } /* @param flag bit0= stream is already open bit1= close stream with flag bit1 */ static off_t ziso_stream_measure_size(IsoStream *stream, int flag) { int ret, ret_close; off_t count = 0; ZisofsFilterStreamData *data; char buf[64 * 1024]; size_t bufsize = 64 * 1024; if (stream == NULL) return ISO_NULL_POINTER; data = stream->data; /* Run filter command and count output bytes */ if (!(flag & 1)) { ret = ziso_stream_open_flag(stream, 1); if (ret < 0) return ret; } if (stream->class->read == &ziso_stream_uncompress) { /* It is enough to read the header part of a compressed file */ ret = ziso_stream_uncompress(stream, buf, 0); count = data->size; } else { /* The size of the compression result has to be counted */ while (1) { ret = stream->class->read(stream, buf, bufsize); if (ret <= 0) break; count += ret; } } ret_close = ziso_stream_close_flag(stream, flag & 2); if (ret < 0) return ret; if (ret_close < 0) return ret_close; data->size = count; return count; } static int ziso_stream_compress(IsoStream *stream, void *buf, size_t desired) { #ifdef Libisofs_with_zliB int ret, todo, i; ZisofsComprStreamData *data; ZisofsFilterRuntime *rng; size_t fill = 0; off_t orig_size, next_pt, measure_ret; char *cbuf = buf; uLongf buf_len; uint64_t *copy_base, num_blocks = 0; if (stream == NULL) { return ISO_NULL_POINTER; } data = stream->data; rng= data->std.running; if (rng == NULL) { return ISO_FILE_NOT_OPENED; } if (rng->error_ret < 0) { return rng->error_ret; } if (data->block_pointers_dropped) { /* The list was dropped after measurement of compressed size. But this * run of the function expects it as already filled with pointer * values. So now they have to be re-computed by extra runs of this * function in the course of compressed size measurement. */ data->block_pointers_dropped = 0; measure_ret = ziso_stream_measure_size(stream, 1 | 2); if (measure_ret < 0) return (rng->error_ret = measure_ret); /* Stream was closed. Open it again, without any size determination. */ ret = ziso_stream_open_flag(stream, 1); if (ret < 0) return ret; } while (1) { if (rng->state == 0) { /* Delivering file header */ if (rng->buffer_fill == 0) { orig_size = iso_stream_get_size(data->std.orig); num_blocks = orig_size / rng->block_size + 1 + !!(orig_size % rng->block_size); if (num_blocks > (uint64_t) ziso_max_file_blocks) return (rng->error_ret = ISO_ZISOFS_TOO_LARGE); if (ziso_block_pointer_mgt((uint64_t) num_blocks, 0) == 0) { ziso_early_bpt_discard = 1; return (rng->error_ret = ISO_ZISOFS_TOO_MANY_PTR); } if (orig_size != (off_t) data->orig_size) return (rng->error_ret = ISO_FILTER_WRONG_INPUT); if (ziso_decide_v2_usage(orig_size)) { rng->zisofs_version = 2; memcpy(rng->block_buffer, zisofs2_magic, 8); rng->block_buffer[8] = 0; /* @hdr_version */ rng->block_buffer[9] = 6; /* @hdr_size */ rng->block_buffer[10] = 1; /* @alg_id */ rng->block_buffer[11] = ziso_decide_bs_log2(orig_size); iso_lsb64((uint8_t *) (rng->block_buffer + 12), (uint64_t) orig_size); memset(rng->block_buffer + 20, 0, 4); rng->buffer_fill = 24; } else { if (orig_size >= (off_t) ISO_ZISOFS_V1_LIMIT) { return (rng->error_ret = ISO_ZISOFS_TOO_LARGE); } rng->zisofs_version = 1; memcpy(rng->block_buffer, zisofs_magic, 8); iso_lsb((unsigned char *) (rng->block_buffer + 8), (uint32_t) orig_size, 4); rng->block_buffer[12] = 4; rng->block_buffer[13] = ziso_decide_bs_log2(orig_size); rng->block_buffer[14] = rng->block_buffer[15] = 0; rng->buffer_fill = 16; } rng->buffer_rpos = 0; } else if (rng->buffer_rpos >= rng->buffer_fill) { rng->buffer_fill = rng->buffer_rpos = 0; rng->state = 1; /* header is delivered */ } } if (rng->state == 1) { /* Delivering block pointers */; if (rng->block_pointer_fill == 0 || data->block_pointers == NULL) { /* Initialize block pointer writing */ rng->block_pointer_rpos = 0; num_blocks = data->orig_size / rng->block_size + 1 + !!(data->orig_size % rng->block_size); if (rng->block_pointer_fill > 0 && (int64_t) num_blocks != rng->block_pointer_fill) return (rng->error_ret = ISO_FILTER_WRONG_INPUT); rng->block_pointer_fill = num_blocks; if (data->block_pointers == NULL) { /* On the first pass, create pointer array with all 0s */ if (ziso_block_pointer_mgt(num_blocks, 1) == 0) { rng->block_pointer_fill = 0; ziso_early_bpt_discard = 1; return (rng->error_ret = ISO_ZISOFS_TOO_MANY_PTR); } data->block_pointers = calloc(rng->block_pointer_fill, 8); if (data->block_pointers == NULL) { ziso_block_pointer_mgt(num_blocks, 2); rng->block_pointer_fill = 0; return (rng->error_ret = ISO_OUT_OF_MEM); } data->block_pointer_counter = rng->block_pointer_fill; } } if (rng->buffer_rpos >= rng->buffer_fill) { if (rng->block_pointer_rpos >= rng->block_pointer_fill) { rng->buffer_fill = rng->buffer_rpos = 0; rng->block_counter = 0; if (rng->zisofs_version == 1) data->block_pointers[0] = 16 + rng->block_pointer_fill * 4; else data->block_pointers[0] = 24 + rng->block_pointer_fill * 8; rng->state = 2; /* block pointers are delivered */ } else { /* Provide a buffer full of block pointers */ /* data->block_pointers was filled by ziso_stream_open() */ todo = rng->block_pointer_fill - rng->block_pointer_rpos; copy_base = data->block_pointers + rng->block_pointer_rpos; if (rng->zisofs_version == 1) { if (todo * 4 > rng->buffer_size) todo = rng->buffer_size / 4; for (i = 0; i < todo; i++) iso_lsb((unsigned char *) (rng->block_buffer + 4 * i), (uint32_t) (copy_base[i] & 0xffffffff), 4); rng->buffer_fill = todo * 4; } else { if (todo * 8 > rng->buffer_size) todo = rng->buffer_size / 8; for (i = 0; i < todo; i++) iso_lsb64((uint8_t *) rng->block_buffer + 8 * i, copy_base[i]); rng->buffer_fill = todo * 8; } rng->buffer_rpos = 0; rng->block_pointer_rpos += todo; } } } if (rng->state == 2 && rng->buffer_rpos >= rng->buffer_fill) { /* Delivering data blocks */; ret = iso_stream_read(data->std.orig, rng->read_buffer, rng->block_size); if (ret > 0) { rng->in_counter += ret; if ((uint64_t) rng->in_counter > data->orig_size) { /* Input size became larger */ return (rng->error_ret = ISO_FILTER_WRONG_INPUT); } /* Check whether all 0 : represent as 0-length block */; for (i = 0; i < ret; i++) if (rng->read_buffer[i]) break; if (i >= ret) { /* All 0-bytes. Bypass compression. */ buf_len = 0; } else { buf_len = rng->buffer_size; ret = compress2((Bytef *) rng->block_buffer, &buf_len, (Bytef *) rng->read_buffer, (uLong) ret, ziso_compression_level); if (ret != Z_OK) { return (rng->error_ret = ISO_ZLIB_COMPR_ERR); } } rng->buffer_fill = buf_len; rng->buffer_rpos = 0; next_pt = data->block_pointers[rng->block_counter] + buf_len; if (data->std.size >= 0 && next_pt > data->std.size) { /* Compression yields more bytes than on first run */ return (rng->error_ret = ISO_FILTER_WRONG_INPUT); } /* Check or record check block pointer */ rng->block_counter++; if (data->block_pointers[rng->block_counter] > 0) { if ((uint64_t) next_pt != data->block_pointers[rng->block_counter]) { /* block pointers mismatch , content has changed */ return (rng->error_ret = ISO_FILTER_WRONG_INPUT); } } else { data->block_pointers[rng->block_counter] = next_pt; } } else if (ret == 0) { rng->state = 3; if ((uint64_t) rng->in_counter != data->orig_size) { /* Input size shrunk */ return (rng->error_ret = ISO_FILTER_WRONG_INPUT); } return fill; } else return (rng->error_ret = ret); if (rng->buffer_fill == 0) { continue; } } if (rng->state == 3 && rng->buffer_rpos >= rng->buffer_fill) { return 0; /* EOF */ } /* Transfer from rng->block_buffer to buf */ todo = desired - fill; if (todo > rng->buffer_fill - rng->buffer_rpos) todo = rng->buffer_fill - rng->buffer_rpos; memcpy(cbuf + fill, rng->block_buffer + rng->buffer_rpos, todo); fill += todo; rng->buffer_rpos += todo; rng->out_counter += todo; if (fill >= desired) { return fill; } } return ISO_FILE_READ_ERROR; /* should never be hit */ #else return ISO_ZLIB_NOT_ENABLED; #endif } #ifdef Libisofs_with_zliB static int ziso_algo_to_num(uint8_t zisofs_algo[2]) { if (zisofs_algo[0] == 'p' && zisofs_algo[1] == 'z') return 0; if (zisofs_algo[0] == 'P' && zisofs_algo[1] == 'Z') return 1; if (zisofs_algo[0] == 'X' && zisofs_algo[1] == 'Z') return 2; if (zisofs_algo[0] == 'L' && zisofs_algo[1] == '4') return 3; if (zisofs_algo[0] == 'Z' && zisofs_algo[1] == 'D') return 4; if (zisofs_algo[0] == 'B' && zisofs_algo[1] == '2') return 5; return -1; } #endif /* Libisofs_with_zliB */ static int ziso_num_to_algo(uint8_t num, uint8_t zisofs_algo[2]) { if (num == 0) { zisofs_algo[0] = 'p'; zisofs_algo[1] = 'z'; return 1; } else if (num == 1) { zisofs_algo[0] = 'P'; zisofs_algo[1] = 'Z'; return 1; } else if (num == 2) { zisofs_algo[0] = 'X'; zisofs_algo[1] = 'Z'; return 2; } else if (num == 3) { zisofs_algo[0] = 'L'; zisofs_algo[1] = '4'; return 2; } else if (num == 4) { zisofs_algo[0] = 'Z'; zisofs_algo[1] = 'D'; return 2; } else if (num == 5) { zisofs_algo[0] = 'B'; zisofs_algo[1] = '2'; return 2; } return -1; } /* @param flag bit0= recognize zisofs2 only if ziso_v2_enabled bit1= do not accept algorithms which libisofs does not support */ static int ziso_parse_zisofs_head(IsoStream *stream, uint8_t *ziso_algo_num, int *header_size_div4, int *block_size_log2, uint64_t *uncompressed_size, int flag) { int ret, consumed = 0, i; char zisofs_head[24]; char waste_word[4]; ret = iso_stream_read(stream, zisofs_head, 8); if (ret < 0) return ret; if (ret != 8) return ISO_ZISOFS_WRONG_INPUT; consumed = 8; if (memcmp(zisofs_head, zisofs_magic, 8) == 0) { *ziso_algo_num = 0; ret = iso_stream_read(stream, zisofs_head + 8, 8); if (ret < 0) return ret; if (ret != 8) return ISO_ZISOFS_WRONG_INPUT; consumed += 8; *header_size_div4 = ((unsigned char *) zisofs_head)[12]; *block_size_log2 = ((unsigned char *) zisofs_head)[13]; *uncompressed_size = iso_read_lsb(((uint8_t *) zisofs_head) + 8, 4); if (*header_size_div4 < 4 || *block_size_log2 < ISO_ZISOFS_V1_MIN_LOG2 || *block_size_log2 > ISO_ZISOFS_V1_MAX_LOG2) return ISO_ZISOFS_WRONG_INPUT; } else if (memcmp(zisofs_head, zisofs2_magic, 8) == 0 && !(ziso_v2_enabled == 0 && (flag & 1))) { ret = iso_stream_read(stream, zisofs_head + 8, 16); if (ret < 0) return ret; if (ret != 16) return ISO_ZISOFS_WRONG_INPUT; consumed += 16; *ziso_algo_num = zisofs_head[10]; *header_size_div4 = ((unsigned char *) zisofs_head)[9]; *block_size_log2 = ((unsigned char *) zisofs_head)[11]; *uncompressed_size = iso_read_lsb64(((uint8_t *) zisofs_head) + 12); if (*header_size_div4 < 4 || *block_size_log2 < ISO_ZISOFS_V2_MIN_LOG2 || *block_size_log2 > ISO_ZISOFS_V2_MAX_LOG2 || (*ziso_algo_num != 1 && (flag & 2))) return ISO_ZISOFS_WRONG_INPUT; } else { return ISO_ZISOFS_WRONG_INPUT; } for (i = consumed; i < *header_size_div4; i++) { /* Skip surplus header words */ ret = iso_stream_read(stream, waste_word, 4); if (ret < 0) return ret; if (ret != 4) return ISO_ZISOFS_WRONG_INPUT; } return 1; } /* Note: A call with desired==0 directly after .open() only checks the file head and loads the uncompressed size from that head. */ static int ziso_stream_uncompress(IsoStream *stream, void *buf, size_t desired) { #ifdef Libisofs_with_zliB int ret, todo, header_size, bs_log2, block_max = 1, blpt_size; ZisofsFilterStreamData *data; ZisofsFilterRuntime *rng; ZisofsUncomprStreamData *nstd; size_t fill = 0; char *cbuf = buf; uLongf buf_len; uint64_t uncompressed_size; int64_t i; uint8_t algo_num, *rpt, *wpt; if (stream == NULL) { return ISO_NULL_POINTER; } data = stream->data; nstd = stream->data; rng= data->running; if (rng == NULL) { return ISO_FILE_NOT_OPENED; } if (rng->error_ret < 0) { return rng->error_ret; } while (1) { if (rng->state == 0) { /* Reading file header */ ret = ziso_parse_zisofs_head(data->orig, &algo_num, &header_size, &bs_log2, &uncompressed_size, 2); if (ret < 0) return (rng->error_ret = ret); if (algo_num == 0) blpt_size = 4; else blpt_size = 8; nstd->header_size_div4 = header_size; header_size *= 4; data->size = uncompressed_size; nstd->block_size_log2 = bs_log2; rng->block_size = 1 << bs_log2; if (desired == 0) return 0; /* Create and read pointer array */ rng->block_pointer_rpos = 0; rng->block_pointer_fill = data->size / rng->block_size + 1 + !!(data->size % rng->block_size); if (rng->block_pointer_fill > ziso_max_file_blocks) { rng->block_pointer_fill = 0; return (rng->error_ret = ISO_ZISOFS_TOO_LARGE); } if (ziso_block_pointer_mgt((uint64_t) rng->block_pointer_fill, 1) == 0) return ISO_ZISOFS_TOO_MANY_PTR; rng->block_pointers = calloc(rng->block_pointer_fill, 8); if (rng->block_pointers == NULL) { ziso_block_pointer_mgt((uint64_t) rng->block_pointer_fill, 2); rng->block_pointer_fill = 0; return (rng->error_ret = ISO_OUT_OF_MEM); } ret = iso_stream_read(data->orig, rng->block_pointers, rng->block_pointer_fill * blpt_size); if (ret < 0) return (rng->error_ret = ret); if (algo_num == 0) { /* Spread 4 byte little-endian pointer values over 8 byte */ rpt = ((uint8_t *) rng->block_pointers) + rng->block_pointer_fill * 4; wpt = ((uint8_t *) rng->block_pointers) + rng->block_pointer_fill * 8; while (rpt > ((uint8_t *) rng->block_pointers) + 4) { rpt -= 4; wpt -= 8; memcpy(wpt, rpt, 4); memset(wpt + 4, 0, 4); } memset(((uint8_t *) rng->block_pointers) + 4, 0, 4); } if (ret != rng->block_pointer_fill * blpt_size) return (rng->error_ret = ISO_ZISOFS_WRONG_INPUT); for (i = 0; i < rng->block_pointer_fill; i++) { rng->block_pointers[i] = iso_read_lsb64((uint8_t *) (rng->block_pointers + i)); if (i > 0) if ((int) (rng->block_pointers[i] - rng->block_pointers[i - 1]) > block_max) block_max = rng->block_pointers[i] - rng->block_pointers[i - 1]; } rng->read_buffer = calloc(block_max, 1); rng->block_buffer = calloc(rng->block_size, 1); if (rng->read_buffer == NULL || rng->block_buffer == NULL) return (rng->error_ret = ISO_OUT_OF_MEM); rng->state = 2; /* block pointers are read */ rng->buffer_fill = rng->buffer_rpos = 0; } if (rng->state == 2 && rng->buffer_rpos >= rng->buffer_fill) { /* Delivering data blocks */; i = ++(rng->block_pointer_rpos); if (i >= rng->block_pointer_fill) { if (rng->out_counter == data->size) { rng->state = 3; rng->block_pointer_rpos--; return fill; } /* More data blocks needed than announced */ return (rng->error_ret = ISO_FILTER_WRONG_INPUT); } todo = rng->block_pointers[i] - rng->block_pointers[i- 1]; if (todo == 0) { memset(rng->block_buffer, 0, rng->block_size); rng->buffer_fill = rng->block_size; if (rng->out_counter + rng->buffer_fill > data->size && i == rng->block_pointer_fill - 1) rng->buffer_fill = data->size - rng->out_counter; } else { ret = iso_stream_read(data->orig, rng->read_buffer, todo); if (ret > 0) { rng->in_counter += ret; buf_len = rng->block_size; ret = uncompress((Bytef *) rng->block_buffer, &buf_len, (Bytef *) rng->read_buffer, (uLong) ret); if (ret != Z_OK) return (rng->error_ret = ISO_ZLIB_COMPR_ERR); rng->buffer_fill = buf_len; if ((int) buf_len < rng->block_size && i != rng->block_pointer_fill - 1) return (rng->error_ret = ISO_ZISOFS_WRONG_INPUT); } else if(ret == 0) { rng->state = 3; if (rng->out_counter != data->size) { /* Input size shrunk */ return (rng->error_ret = ISO_FILTER_WRONG_INPUT); } return fill; } else return (rng->error_ret = ret); } rng->buffer_rpos = 0; if (rng->out_counter + rng->buffer_fill > data->size) { /* Uncompression yields more bytes than announced by header */ return (rng->error_ret = ISO_FILTER_WRONG_INPUT); } } if (rng->state == 3 && rng->buffer_rpos >= rng->buffer_fill) { return 0; /* EOF */ } /* Transfer from rng->block_buffer to buf */ todo = desired - fill; if (todo > rng->buffer_fill - rng->buffer_rpos) todo = rng->buffer_fill - rng->buffer_rpos; memcpy(cbuf + fill, rng->block_buffer + rng->buffer_rpos, todo); fill += todo; rng->buffer_rpos += todo; rng->out_counter += todo; if (fill >= desired) { return fill; } } return (rng->error_ret = ISO_FILE_READ_ERROR); /* should never be hit */ #else return ISO_ZLIB_NOT_ENABLED; #endif } static off_t ziso_stream_get_size(IsoStream *stream) { off_t ret; ZisofsFilterStreamData *data; if (stream == NULL) return ISO_NULL_POINTER; data = stream->data; if (data->size >= 0) return data->size; ret = ziso_stream_measure_size(stream, 0); return ret; } static int ziso_stream_is_repeatable(IsoStream *stream) { /* Only repeatable streams are accepted as orig */ return 1; } static void ziso_stream_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, ino_t *ino_id) { ZisofsFilterStreamData *data; data = stream->data; *fs_id = ISO_FILTER_FS_ID; *dev_id = ISO_FILTER_ZISOFS_DEV_ID; *ino_id = data->id; } static void ziso_stream_free(IsoStream *stream) { ZisofsFilterStreamData *data; ZisofsComprStreamData *nstd; if (stream == NULL) { return; } data = stream->data; if (data->running != NULL) { ziso_stream_close(stream); } if (stream->class->read == &ziso_stream_uncompress) { if (--ziso_osiz_ref_count < 0) ziso_osiz_ref_count = 0; } else { nstd = stream->data; if (nstd->block_pointers != NULL) { ziso_block_pointer_mgt(nstd->block_pointer_counter, 2); free((char *) nstd->block_pointers); } if (--ziso_ref_count < 0) ziso_ref_count = 0; if (ziso_ref_count == 0) ziso_early_bpt_discard = 0; } iso_stream_unref(data->orig); free(data); } static int ziso_update_size(IsoStream *stream) { /* By principle size is determined only once */ return 1; } static IsoStream *ziso_get_input_stream(IsoStream *stream, int flag) { ZisofsFilterStreamData *data; if (stream == NULL) { return NULL; } data = stream->data; return data->orig; } static int ziso_clone_stream(IsoStream *old_stream, IsoStream **new_stream, int flag) { int ret; IsoStream *new_input_stream = NULL, *stream = NULL; ZisofsFilterStreamData *stream_data, *old_stream_data; ZisofsUncomprStreamData *uncompr, *old_uncompr; ZisofsComprStreamData *compr, *old_compr; if (flag) return ISO_STREAM_NO_CLONE; /* unknown option required */ ret = iso_stream_clone_filter_common(old_stream, &stream, &new_input_stream, 0); if (ret < 0) return ret; if (old_stream->class->read == &ziso_stream_uncompress) { uncompr = calloc(1, sizeof(ZisofsUncomprStreamData)); if (uncompr == NULL) goto no_mem; stream_data = (ZisofsFilterStreamData *) uncompr; old_uncompr = (ZisofsUncomprStreamData *) old_stream->data; uncompr->zisofs_algo_num = old_uncompr->zisofs_algo_num; uncompr->header_size_div4 = old_uncompr->header_size_div4; uncompr->block_size_log2 = old_uncompr->block_size_log2; } else { compr = calloc(1, sizeof(ZisofsComprStreamData)); if (compr == NULL) goto no_mem; stream_data = (ZisofsFilterStreamData *) compr; old_compr = (ZisofsComprStreamData *) old_stream->data; compr->orig_size = old_compr->orig_size; compr->block_pointers = NULL; compr->block_pointer_counter = 0; compr->open_counter = 0; if (old_compr->block_pointers != NULL || old_compr->block_pointers_dropped) compr->block_pointers_dropped = 1; else compr->block_pointers_dropped = 0; } old_stream_data = (ZisofsFilterStreamData *) old_stream->data; stream_data->orig = new_input_stream; stream_data->size = old_stream_data->size; stream_data->running = NULL; stream_data->id = ++ziso_ino_id; stream->data = stream_data; *new_stream = stream; return ISO_SUCCESS; no_mem: if (new_input_stream != NULL) iso_stream_unref(new_input_stream); if (stream != NULL) iso_stream_unref(stream); return ISO_OUT_OF_MEM; } static int ziso_cmp_ino(IsoStream *s1, IsoStream *s2); static int ziso_uncompress_cmp_ino(IsoStream *s1, IsoStream *s2); IsoStreamIface ziso_stream_compress_class = { 4, "ziso", ziso_stream_open, ziso_stream_close, ziso_stream_get_size, ziso_stream_compress, ziso_stream_is_repeatable, ziso_stream_get_id, ziso_stream_free, ziso_update_size, ziso_get_input_stream, ziso_cmp_ino, ziso_clone_stream }; IsoStreamIface ziso_stream_uncompress_class = { 4, "osiz", ziso_stream_open, ziso_stream_close, ziso_stream_get_size, ziso_stream_uncompress, ziso_stream_is_repeatable, ziso_stream_get_id, ziso_stream_free, ziso_update_size, ziso_get_input_stream, ziso_uncompress_cmp_ino, ziso_clone_stream }; static int ziso_cmp_ino(IsoStream *s1, IsoStream *s2) { /* This function may rely on being called by iso_stream_cmp_ino() only with s1, s2 which both point to it as their .cmp_ino() function. It would be a programming error to let any other than ziso_stream_compress_class point to ziso_cmp_ino(). */ if (s1->class != s2->class || (s1->class != &ziso_stream_compress_class && s2->class != &ziso_stream_uncompress_class)) iso_stream_cmp_ino(s1, s2, 1); /* Both streams apply the same treatment to their input streams */ return iso_stream_cmp_ino(iso_stream_get_input_stream(s1, 0), iso_stream_get_input_stream(s2, 0), 0); } static int ziso_uncompress_cmp_ino(IsoStream *s1, IsoStream *s2) { /* This function may rely on being called by iso_stream_cmp_ino() only with s1, s2 which both point to it as their .cmp_ino() function. It would be a programming error to let any other than ziso_stream_uncompress_class point to ziso_uncompress_cmp_ino(). This fallback endangers transitivity of iso_stream_cmp_ino(). */ if (s1->class != s2->class || (s1->class != &ziso_stream_uncompress_class && s2->class != &ziso_stream_uncompress_class)) iso_stream_cmp_ino(s1, s2, 1); /* Both streams apply the same treatment to their input streams */ return iso_stream_cmp_ino(iso_stream_get_input_stream(s1, 0), iso_stream_get_input_stream(s2, 0), 0); } /* ------------------------------------------------------------------------- */ #ifdef Libisofs_with_zliB static void ziso_filter_free(FilterContext *filter) { /* no data are allocated */; } /* * @param flag bit1= Install a decompression filter */ static int ziso_filter_get_filter(FilterContext *filter, IsoStream *original, IsoStream **filtered, int flag) { IsoStream *str; ZisofsFilterStreamData *data; ZisofsComprStreamData *cnstd = NULL; ZisofsUncomprStreamData *unstd = NULL; if (filter == NULL || original == NULL || filtered == NULL) { return ISO_NULL_POINTER; } str = calloc(sizeof(IsoStream), 1); if (str == NULL) { return ISO_OUT_OF_MEM; } if (flag & 2) { unstd = calloc(sizeof(ZisofsUncomprStreamData), 1); data = (ZisofsFilterStreamData *) unstd; } else { cnstd = calloc(sizeof(ZisofsComprStreamData), 1); data = (ZisofsFilterStreamData *) cnstd; } if (data == NULL) { free(str); return ISO_OUT_OF_MEM; } /* These data items are not owned by this filter object */ data->id = ++ziso_ino_id; data->orig = original; data->size = -1; data->running = NULL; /* get reference to the source */ iso_stream_ref(data->orig); str->refcount = 1; str->data = data; if (flag & 2) { unstd->zisofs_algo_num = 0; unstd->header_size_div4 = 0; unstd->block_size_log2 = 0; str->class = &ziso_stream_uncompress_class; ziso_osiz_ref_count++; } else { cnstd->orig_size = iso_stream_get_size(original); cnstd->block_pointers = NULL; cnstd->block_pointer_counter = 0; cnstd->open_counter = 0; cnstd->block_pointers_dropped = 0; str->class = &ziso_stream_compress_class; ziso_ref_count++; } *filtered = str; return ISO_SUCCESS; } /* To be called by iso_file_add_filter(). * The FilterContext input parameter is not furtherly needed for the * emerging IsoStream. */ static int ziso_filter_get_compressor(FilterContext *filter, IsoStream *original, IsoStream **filtered) { return ziso_filter_get_filter(filter, original, filtered, 0); } static int ziso_filter_get_uncompressor(FilterContext *filter, IsoStream *original, IsoStream **filtered) { return ziso_filter_get_filter(filter, original, filtered, 2); } /* Produce a parameter object suitable for iso_file_add_filter(). * It may be disposed by free() after all those calls are made. * * This is quite a dummy as it does not carry individual data. * @param flag bit1= Install a decompression filter */ static int ziso_create_context(FilterContext **filter, int flag) { FilterContext *f; *filter = f = calloc(1, sizeof(FilterContext)); if (f == NULL) { return ISO_OUT_OF_MEM; } f->refcount = 1; f->version = 0; f->data = NULL; f->free = ziso_filter_free; if (flag & 2) f->get_filter = ziso_filter_get_uncompressor; else f->get_filter = ziso_filter_get_compressor; return ISO_SUCCESS; } #endif /* Libisofs_with_zliB */ /* * @param flag bit0= if_block_reduction rather than if_reduction * bit1= Install a decompression filter * bit2= only inquire availability of zisofs filtering * bit3= do not inquire size */ int ziso_add_filter(IsoFile *file, int flag) { #ifdef Libisofs_with_zliB int ret; FilterContext *f = NULL; IsoStream *stream; off_t original_size = 0, filtered_size = 0; if (flag & 4) return 2; original_size = iso_file_get_size(file); if (!(flag & 2)) { if (original_size <= 0 || ((flag & 1) && original_size <= 2048)) { return 2; } if (original_size >= (off_t) ISO_ZISOFS_V1_LIMIT && !ziso_v2_enabled) { return ISO_ZISOFS_TOO_LARGE; } } ret = ziso_create_context(&f, flag & 2); if (ret < 0) { return ret; } ret = iso_file_add_filter(file, f, 0); free(f); if (ret < 0) { return ret; } if (flag & 8) /* size will be filled in by caller */ return ISO_SUCCESS; /* Run a full filter process getsize so that the size is cached */ stream = iso_file_get_stream(file); filtered_size = iso_stream_get_size(stream); if (filtered_size < 0) { iso_file_remove_filter(file, 0); return filtered_size; } if ((filtered_size >= original_size || ((flag & 1) && filtered_size / 2048 >= original_size / 2048)) && !(flag & 2)){ ret = iso_file_remove_filter(file, 0); if (ret < 0) { return ret; } return 2; } return ISO_SUCCESS; #else return ISO_ZLIB_NOT_ENABLED; #endif /* ! Libisofs_with_zliB */ } /* API function */ int iso_file_add_zisofs_filter(IsoFile *file, int flag) { return ziso_add_filter(file, flag & ~8); } /* API function */ int iso_zisofs_get_refcounts(off_t *ziso_count, off_t *osiz_count, int flag) { *ziso_count = ziso_ref_count; *osiz_count = ziso_osiz_ref_count; return ISO_SUCCESS; } int ziso_add_osiz_filter(IsoFile *file, uint8_t zisofs_algo[2], uint8_t header_size_div4, uint8_t block_size_log2, uint64_t uncompressed_size, int flag) { #ifdef Libisofs_with_zliB int ret; ZisofsUncomprStreamData *unstd; ret = ziso_add_filter(file, 2 | 8); if (ret < 0) return ret; unstd = iso_file_get_stream(file)->data; ret = ziso_algo_to_num(zisofs_algo); if (ret < 0) return ISO_ZISOFS_WRONG_INPUT; unstd->zisofs_algo_num = ret; unstd->header_size_div4 = header_size_div4; unstd->block_size_log2 = block_size_log2; unstd->std.size = uncompressed_size; return ISO_SUCCESS; #else return ISO_ZLIB_NOT_ENABLED; #endif /* ! Libisofs_with_zliB */ } /* Determine stream type : 1=ziso , -1=osiz , 0=other , 2=ziso_by_content and eventual ZF field parameters @param flag bit0= allow ziso_by_content which is based on content reading bit1= do not inquire stream->class for filters bit2= recognize zisofs2 by magic only if ziso_v2_enabled */ int ziso_is_zisofs_stream(IsoStream *stream, int *stream_type, uint8_t zisofs_algo[2], int *header_size_div4, int *block_size_log2, uint64_t *uncompressed_size, int flag) { int ret, close_ret, algo_ret; ZisofsFilterStreamData *data; ZisofsComprStreamData *cnstd; ZisofsUncomprStreamData *unstd; uint8_t algo_num; *stream_type = 0; if (stream->class == &ziso_stream_compress_class && !(flag & 2)) { *stream_type = 1; cnstd = stream->data; *uncompressed_size = cnstd->orig_size; *block_size_log2 = ziso_decide_bs_log2((off_t) *uncompressed_size); if (ziso_decide_v2_usage((off_t) *uncompressed_size)) { zisofs_algo[0] = 'P'; zisofs_algo[1] = 'Z'; *header_size_div4 = 6; } else if (*uncompressed_size < (uint64_t) ISO_ZISOFS_V1_LIMIT) { zisofs_algo[0] = 'p'; zisofs_algo[1] = 'z'; *header_size_div4 = 4; } else { return 0; } return 1; } else if(stream->class == &ziso_stream_uncompress_class && !(flag & 2)) { *stream_type = -1; data = stream->data; unstd = stream->data; ret = ziso_num_to_algo(unstd->zisofs_algo_num, zisofs_algo); if (ret < 0) return ISO_ZISOFS_WRONG_INPUT; *header_size_div4 = unstd->header_size_div4; *block_size_log2 = unstd->block_size_log2; *uncompressed_size = data->size; return 1; } if (!(flag & 1)) return 0; ret = iso_stream_open(stream); if (ret < 0) return ret; ret = ziso_parse_zisofs_head(stream, &algo_num, header_size_div4, block_size_log2, uncompressed_size, (flag >> 2) & 1); if (ret == 1) { *stream_type = 2; algo_ret = ziso_num_to_algo(algo_num, zisofs_algo); } else { ret = 0; algo_ret = 1; } close_ret = iso_stream_close(stream); if (algo_ret < 0) return ISO_ZISOFS_WRONG_INPUT; if (close_ret < 0) return close_ret; return ret; } /* API */ int iso_zisofs_set_params(struct iso_zisofs_ctrl *params, int flag) { #ifdef Libisofs_with_zliB if (params->version < 0 || params->version > 1) return ISO_WRONG_ARG_VALUE; if (params->compression_level < 0 || params->compression_level > 9 || params->block_size_log2 < ISO_ZISOFS_V1_MIN_LOG2 || params->block_size_log2 > ISO_ZISOFS_V1_MAX_LOG2) { return ISO_WRONG_ARG_VALUE; } if (params->version >= 1) if (params->v2_enabled < 0 || params->v2_enabled > 2 || (params->v2_block_size_log2 != 0 && (params->v2_block_size_log2 < ISO_ZISOFS_V2_MIN_LOG2 || params->v2_block_size_log2 > ISO_ZISOFS_V2_MAX_LOG2))) return ISO_WRONG_ARG_VALUE; if (ziso_ref_count > 0) { return ISO_ZISOFS_PARAM_LOCK; } ziso_compression_level = params->compression_level; ziso_block_size_log2 = params->block_size_log2; if (params->version == 0) return 1; ziso_v2_enabled = params->v2_enabled; if (params->v2_block_size_log2 > 0) ziso_v2_block_size_log2 = params->v2_block_size_log2; if (params->max_total_blocks > 0) ziso_max_total_blocks = params->max_total_blocks; if (params->max_file_blocks > 0) ziso_max_file_blocks = params->max_file_blocks; if (params->block_number_target != 0) ziso_block_number_target = params->block_number_target; if (params->bpt_discard_file_blocks != 0) ziso_many_block_limit = params->bpt_discard_file_blocks; if (params->bpt_discard_free_ratio != 0.0) ziso_keep_blocks_free_ratio = params->bpt_discard_free_ratio; return 1; #else return ISO_ZLIB_NOT_ENABLED; #endif /* ! Libisofs_with_zliB */ } /* API */ int iso_zisofs_get_params(struct iso_zisofs_ctrl *params, int flag) { #ifdef Libisofs_with_zliB if (params->version < 0 || params->version > 1) return ISO_WRONG_ARG_VALUE; params->compression_level = ziso_compression_level; params->block_size_log2 = ziso_block_size_log2; if (params->version == 1) { params->v2_enabled = ziso_v2_enabled; params->v2_block_size_log2 = ziso_v2_block_size_log2; params->max_total_blocks = ziso_max_total_blocks; params->current_total_blocks = ziso_block_pointer_mgt((uint64_t) 0, 3); params->max_file_blocks = ziso_max_file_blocks; params->block_number_target = ziso_block_number_target; params->bpt_discard_file_blocks = ziso_many_block_limit; params->bpt_discard_free_ratio = ziso_keep_blocks_free_ratio; } return 1; #else return ISO_ZLIB_NOT_ENABLED; #endif /* ! Libisofs_with_zliB */ } /* API */ int iso_stream_get_zisofs_par(IsoStream *stream, int *stream_type, uint8_t zisofs_algo[2], uint8_t* algo_num, int *block_size_log2, int flag) { #ifdef Libisofs_with_zliB uint64_t uncompressed_size; int header_size_div4, ret; if (stream == NULL) return ISO_NULL_POINTER; ret = ziso_is_zisofs_stream(stream, stream_type, zisofs_algo, &header_size_div4, block_size_log2, &uncompressed_size, 0); if (ret <= 0 || (*stream_type != -1 && *stream_type != 1)) return 0; *algo_num = ziso_algo_to_num(zisofs_algo); return 1; #else return ISO_ZLIB_NOT_ENABLED; #endif /* ! Libisofs_with_zliB */ } /* API */ int iso_stream_zisofs_discard_bpt(IsoStream *stream, int flag) { #ifdef Libisofs_with_zliB int ret; if (stream == NULL) return ISO_NULL_POINTER; ret = ziso_discard_bpt(stream, 1); return ret; #else return ISO_ZLIB_NOT_ENABLED; #endif /* ! Libisofs_with_zliB */ } /* API */ int iso_zisofs_ctrl_susp_z2(int enable) { if (enable == 0 || enable == 1) iso_zisofs2_enable_susp_z2 = enable; return iso_zisofs2_enable_susp_z2; } /* * Copyright (c) 2008 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libisofs.h" #include "node.h" #include #include struct iso_find_condition { /* * Check whether the given node matches this condition. * * @param cond * The condition to check * @param node * The node that should be checked * @return * 1 if the node matches the condition, 0 if not */ int (*matches)(IsoFindCondition *cond, IsoNode *node); /** * Free condition specific data */ void (*free)(IsoFindCondition*); /** condition specific data */ void *data; }; struct find_iter_data { IsoDir *dir; /**< original dir of the iterator */ IsoDirIter *iter; IsoDirIter *itersec; /**< iterator to deal with child dirs */ IsoFindCondition *cond; int err; /**< error? */ IsoNode *current; /**< node to be returned next */ IsoNode *prev; /**< last returned node, needed for removal */ int free_cond; /**< whether to free cond on iter_free */ }; static int get_next(struct find_iter_data *iter, IsoNode **n) { int ret; if (iter->itersec != NULL) { ret = iso_dir_iter_next(iter->itersec, n); if (ret <= 0) { /* secondary item no more needed */ iso_dir_iter_free(iter->itersec); iter->itersec = NULL; } if (ret != 0) { /* success or error */ return ret; } } /* * we reach here if: * - no secondary item is present * - secondary item has no more items */ while ((ret = iso_dir_iter_next(iter->iter, n)) == 1) { if (iter->cond->matches(iter->cond, *n)) { return ISO_SUCCESS; } else if (ISO_NODE_IS_DIR(*n)) { /* recurse on child dir */ struct find_iter_data *data; ret = iso_dir_find_children((IsoDir*)*n, iter->cond, &iter->itersec); if (ret < 0) { return ret; } data = iter->itersec->data; data->free_cond = 0; /* we don't need sec iter to free cond */ return get_next(iter, n); } } return ret; } static void update_next(IsoDirIter *iter) { int ret; IsoNode *n; struct find_iter_data *data = iter->data; if (data->prev) { iso_node_unref(data->prev); } data->prev = data->current; if (data->itersec == NULL && data->current != NULL && ISO_NODE_IS_DIR(data->current)) { /* we need to recurse on child dir */ struct find_iter_data *data2; ret = iso_dir_find_children((IsoDir*)data->current, data->cond, &data->itersec); if (ret < 0) { data->current = NULL; data->err = ret; return; } data2 = data->itersec->data; data2->free_cond = 0; /* we don't need sec iter to free cond */ } ret = get_next(data, &n); iso_node_unref((IsoNode*)iter->dir); if (ret == 1) { data->current = n; iso_node_ref(n); data->err = 0; iter->dir = n->parent; } else { data->current = NULL; data->err = ret; iter->dir = data->dir; } iso_node_ref((IsoNode*)iter->dir); } static int find_iter_next(IsoDirIter *iter, IsoNode **node) { struct find_iter_data *data; if (iter == NULL || node == NULL) { return ISO_NULL_POINTER; } data = iter->data; if (data->err < 0) { return data->err; } *node = data->current; update_next(iter); return (*node == NULL) ? 0 : ISO_SUCCESS; } static int find_iter_has_next(IsoDirIter *iter) { struct find_iter_data *data = iter->data; return (data->current != NULL); } static void find_iter_free(IsoDirIter *iter) { struct find_iter_data *data = iter->data; if (data->free_cond) { data->cond->free(data->cond); free(data->cond); } iso_node_unref((IsoNode*)data->dir); /* free refs to nodes */ if (data->prev) { iso_node_unref(data->prev); } if (data->current) { iso_node_unref(data->current); } /* free underlying iter */ iso_dir_iter_free(data->iter); free(iter->data); } static int find_iter_take(IsoDirIter *iter) { struct find_iter_data *data = iter->data; if (data->prev == NULL) { return ISO_ERROR; /* next not called or end of dir */ } return iso_node_take(data->prev); } static int find_iter_remove(IsoDirIter *iter) { struct find_iter_data *data = iter->data; if (data->prev == NULL) { return ISO_ERROR; /* next not called or end of dir */ } return iso_node_remove(data->prev); } void find_notify_child_taken(IsoDirIter *iter, IsoNode *node) { struct find_iter_data *data = iter->data; if (data->prev == node) { /* free our ref */ iso_node_unref(node); data->prev = NULL; } else if (data->current == node) { iso_node_unref(node); data->current = NULL; update_next(iter); } } static struct iso_dir_iter_iface find_iter_class = { find_iter_next, find_iter_has_next, find_iter_free, find_iter_take, find_iter_remove, find_notify_child_taken }; int iso_dir_find_children(IsoDir* dir, IsoFindCondition *cond, IsoDirIter **iter) { int ret; IsoDirIter *children; IsoDirIter *it; struct find_iter_data *data; if (dir == NULL || cond == NULL || iter == NULL) { return ISO_NULL_POINTER; } it = malloc(sizeof(IsoDirIter)); if (it == NULL) { return ISO_OUT_OF_MEM; } data = malloc(sizeof(struct find_iter_data)); if (data == NULL) { free(it); return ISO_OUT_OF_MEM; } ret = iso_dir_get_children(dir, &children); if (ret < 0) { free(it); free(data); return ret; } it->class = &find_iter_class; it->dir = (IsoDir*)dir; data->iter = children; data->itersec = NULL; data->cond = cond; data->free_cond = 1; data->err = 0; data->prev = data->current = NULL; it->data = data; if (iso_dir_iter_register(it) < 0) { free(it); return ISO_OUT_OF_MEM; } iso_node_ref((IsoNode*)dir); /* take another ref to the original dir */ data->dir = (IsoDir*)dir; iso_node_ref((IsoNode*)dir); update_next(it); *iter = it; return ISO_SUCCESS; } /*************** find by name wildcard condition *****************/ static int cond_name_matches(IsoFindCondition *cond, IsoNode *node) { char *pattern = (char*) cond->data; int ret = fnmatch(pattern, node->name, 0); return ret == 0 ? 1 : 0; } static void cond_name_free(IsoFindCondition *cond) { free(cond->data); } /** * Create a new condition that checks if the node name matches the given * wildcard. * * @param wildcard * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_name(const char *wildcard) { IsoFindCondition *cond; if (wildcard == NULL) { return NULL; } cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } cond->data = strdup(wildcard); cond->free = cond_name_free; cond->matches = cond_name_matches; return cond; } /*************** find by mode condition *****************/ static int cond_mode_matches(IsoFindCondition *cond, IsoNode *node) { mode_t *mask = (mode_t*) cond->data; return node->mode & *mask ? 1 : 0; } static void cond_mode_free(IsoFindCondition *cond) { free(cond->data); } /** * Create a new condition that checks the node mode against a mode mask. It * can be used to check both file type and permissions. * * For example: * * iso_new_find_conditions_mode(S_IFREG) : search for regular files * iso_new_find_conditions_mode(S_IFCHR | S_IWUSR) : search for character * devices where owner has write permissions. * * @param mask * Mode mask to AND against node mode. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_mode(mode_t mask) { IsoFindCondition *cond; mode_t *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(mode_t)); if (data == NULL) { free(cond); return NULL; } *data = mask; cond->data = data; cond->free = cond_mode_free; cond->matches = cond_mode_matches; return cond; } /*************** find by gid condition *****************/ static int cond_gid_matches(IsoFindCondition *cond, IsoNode *node) { gid_t *gid = (gid_t*) cond->data; return node->gid == *gid ? 1 : 0; } static void cond_gid_free(IsoFindCondition *cond) { free(cond->data); } /** * Create a new condition that checks the node gid. * * @param gid * Desired Group Id. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_gid(gid_t gid) { IsoFindCondition *cond; gid_t *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(gid_t)); if (data == NULL) { free(cond); return NULL; } *data = gid; cond->data = data; cond->free = cond_gid_free; cond->matches = cond_gid_matches; return cond; } /*************** find by uid condition *****************/ static int cond_uid_matches(IsoFindCondition *cond, IsoNode *node) { uid_t *uid = (uid_t*) cond->data; return node->uid == *uid ? 1 : 0; } static void cond_uid_free(IsoFindCondition *cond) { free(cond->data); } /** * Create a new condition that checks the node uid. * * @param uid * Desired User Id. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_uid(uid_t uid) { IsoFindCondition *cond; uid_t *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(uid_t)); if (data == NULL) { free(cond); return NULL; } *data = uid; cond->data = data; cond->free = cond_uid_free; cond->matches = cond_uid_matches; return cond; } /*************** find by timestamps condition *****************/ struct cond_times { time_t time; int what_time; /* 0 atime, 1 mtime, 2 ctime */ enum iso_find_comparisons comparison; }; static int cond_time_matches(IsoFindCondition *cond, IsoNode *node) { time_t node_time; struct cond_times *data = cond->data; switch (data->what_time) { case 0: node_time = node->atime; break; case 1: node_time = node->mtime; break; default: node_time = node->ctime; break; } switch (data->comparison) { case ISO_FIND_COND_GREATER: return node_time > data->time ? 1 : 0; case ISO_FIND_COND_GREATER_OR_EQUAL: return node_time >= data->time ? 1 : 0; case ISO_FIND_COND_EQUAL: return node_time == data->time ? 1 : 0; case ISO_FIND_COND_LESS: return node_time < data->time ? 1 : 0; case ISO_FIND_COND_LESS_OR_EQUAL: return node_time <= data->time ? 1 : 0; } /* should never happen */ return 0; } static void cond_time_free(IsoFindCondition *cond) { free(cond->data); } /** * Create a new condition that checks the time of last access. * * @param time * Time to compare against IsoNode atime. * @param comparison * Comparison to be done between IsoNode atime and submitted time. * Note that ISO_FIND_COND_GREATER, for example, is true if the node * time is greater than the submitted time. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_atime(time_t time, enum iso_find_comparisons comparison) { IsoFindCondition *cond; struct cond_times *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(struct cond_times)); if (data == NULL) { free(cond); return NULL; } data->time = time; data->comparison = comparison; data->what_time = 0; /* atime */ cond->data = data; cond->free = cond_time_free; cond->matches = cond_time_matches; return cond; } /** * Create a new condition that checks the time of last modification. * * @param time * Time to compare against IsoNode mtime. * @param comparison * Comparison to be done between IsoNode mtime and submitted time. * Note that ISO_FIND_COND_GREATER, for example, is true if the node * time is greater than the submitted time. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_mtime(time_t time, enum iso_find_comparisons comparison) { IsoFindCondition *cond; struct cond_times *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(struct cond_times)); if (data == NULL) { free(cond); return NULL; } data->time = time; data->comparison = comparison; data->what_time = 1; /* mtime */ cond->data = data; cond->free = cond_time_free; cond->matches = cond_time_matches; return cond; } /** * Create a new condition that checks the time of last status change. * * @param time * Time to compare against IsoNode ctime. * @param comparison * Comparison to be done between IsoNode ctime and submitted time. * Note that ISO_FIND_COND_GREATER, for example, is true if the node * time is greater than the submitted time. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_ctime(time_t time, enum iso_find_comparisons comparison) { IsoFindCondition *cond; struct cond_times *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(struct cond_times)); if (data == NULL) { free(cond); return NULL; } data->time = time; data->comparison = comparison; data->what_time = 2; /* ctime */ cond->data = data; cond->free = cond_time_free; cond->matches = cond_time_matches; return cond; } /*************** logical operations on conditions *****************/ struct logical_binary_conditions { IsoFindCondition *a; IsoFindCondition *b; }; static void cond_logical_binary_free(IsoFindCondition *cond) { struct logical_binary_conditions *data; data = cond->data; data->a->free(data->a); free(data->a); data->b->free(data->b); free(data->b); free(cond->data); } static int cond_logical_and_matches(IsoFindCondition *cond, IsoNode *node) { struct logical_binary_conditions *data = cond->data; return data->a->matches(data->a, node) && data->b->matches(data->b, node); } /** * Create a new condition that check if the two given conditions are * valid. * * @param a * @param b * IsoFindCondition to compare * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_and(IsoFindCondition *a, IsoFindCondition *b) { IsoFindCondition *cond; struct logical_binary_conditions *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(struct logical_binary_conditions)); if (data == NULL) { free(cond); return NULL; } data->a = a; data->b = b; cond->data = data; cond->free = cond_logical_binary_free; cond->matches = cond_logical_and_matches; return cond; } static int cond_logical_or_matches(IsoFindCondition *cond, IsoNode *node) { struct logical_binary_conditions *data = cond->data; return data->a->matches(data->a, node) || data->b->matches(data->b, node); } /** * Create a new condition that check if at least one the two given conditions * is valid. * * @param a * @param b * IsoFindCondition to compare * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_or(IsoFindCondition *a, IsoFindCondition *b) { IsoFindCondition *cond; struct logical_binary_conditions *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(struct logical_binary_conditions)); if (data == NULL) { free(cond); return NULL; } data->a = a; data->b = b; cond->data = data; cond->free = cond_logical_binary_free; cond->matches = cond_logical_or_matches; return cond; } static void cond_not_free(IsoFindCondition *cond) { IsoFindCondition *negate = cond->data; negate->free(negate); free(negate); } static int cond_not_matches(IsoFindCondition *cond, IsoNode *node) { IsoFindCondition *negate = cond->data; return !(negate->matches(negate, node)); } /** * Create a new condition that check if the given conditions is false. * * @param negate * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_not(IsoFindCondition *negate) { IsoFindCondition *cond; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } cond->data = negate; cond->free = cond_not_free; cond->matches = cond_not_matches; return cond; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /* * Filesystem/FileSource implementation to access an ISO image, using an * IsoDataSource to read image data. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libisofs.h" #include "ecma119.h" #include "messages.h" #include "rockridge.h" #include "image.h" #include "tree.h" #include "eltorito.h" #include "node.h" #include "aaip_0_2.h" #include "system_area.h" #include "ecma119.h" #include #include #include #include #include #include #include #include /* Enable this and write the correct absolute path into the include statement below in order to test the pending contribution to syslinux: http://www.syslinux.org/archives/2013-March/019755.html # def ine Libisofs_syslinux_tesT 1 */ #ifdef Libisofs_syslinux_tesT #define Isolinux_rockridge_in_libisofS 1 #include "/reiser/syslinux/core/fs/iso9660/susp_rr.c" /* # inc lude "/home/thomas/projekte/cdrskin_dir/libisoburn-develop/test/susp_rr.c" */ #endif /* Libisofs_syslinux_tesT */ /** * Options for image reading. * There are four kind of options: * - Related to multisession support. * In most cases, an image begins at LBA 0 of the data source. However, * in multisession discs, the later image begins in the last session on * disc. The block option can be used to specify the start of that last * session. * - Related to the tree that will be read. * As default, when Rock Ridge extensions are present in the image, that * will be used to get the tree. If RR extensions are not present, libisofs * will use the Joliet extensions if available. Finally, the plain ISO-9660 * tree is used if neither RR nor Joliet extensions are available. With * norock, nojoliet, and preferjoliet options, you can change this * default behavior. * - Related to default POSIX attributes. * When Rock Ridege extensions are not used, libisofs can't figure out what * are the the permissions, uid or gid for the files. You should supply * default values for that. */ struct iso_read_opts { /** * Block where the image begins, usually 0, can be different on a * multisession disc. */ uint32_t block; unsigned int norock : 1; /*< Do not read Rock Ridge extensions */ unsigned int nojoliet : 1; /*< Do not read Joliet extensions */ unsigned int noiso1999 : 1; /*< Do not read ISO 9660:1999 enhanced tree */ unsigned int noaaip : 1; /* Do not read AAIP extension for xattr and ACL */ unsigned int nomd5 : 2; /* Do not read MD5 array */ /** * Hand out new inode numbers and overwrite eventually read PX inode * numbers. This will split apart any hardlinks. */ unsigned int make_new_ino : 1 ; /** * When both Joliet and RR extensions are present, the RR tree is used. * If you prefer using Joliet, set this to 1. */ unsigned int preferjoliet : 1; /** * If neither Rock Ridge nor Joliet is used, the ECMA-119 names are mapped * according to one of these rules * 0 = unmapped: show name as recorded in ECMA-119 directory record * (not suitable for writing it to a new ISO filesystem) * 1 = stripped: like unmapped, but strip off trailing ";1" or ".;1" * 2 = uppercase: like stripped, but {a-z} mapped to {A-Z} * 3 = lowercase: like stripped, but {A-Z} mapped to {a-z} */ unsigned int ecma119_map : 2; /** * If Joliet is used, apply one of these mapping rules: * 0 = unmapped: show name as recorded in Joliet directory record * (not suitable for writing it to a new ISO filesystem) * 1 = stripped: strip off trailing ";1" or ".;1" */ unsigned int joliet_map : 1; uid_t uid; /**< Default uid when no RR */ gid_t gid; /**< Default uid when no RR */ mode_t dir_mode; /**< Default mode when no RR (only permissions) */ mode_t file_mode; /* TODO #00024 : option to convert names to lower case for iso reading */ /** * Input charset for RR file names. NULL to use default locale charset. */ char *input_charset; /** * Enable or disable methods to automatically choose an input charset. * This eventually overrides input_charset. * * bit0= allow to set the input character set automatically from * attribute "isofs.cs" of root directory */ int auto_input_charset; /** * Enable or disable loading of the first 32768 bytes of the session and * submission by iso_write_opts_set_system_area(data, 0). */ int load_system_area; /** * Keep data source of imported ISO filesystem in IsoImage.import_src */ int keep_import_src; /** * What to do in case of name longer than truncate_length: * 0= throw FAILURE * 1= truncate to truncate_length with MD5 of whole name at end */ int truncate_mode; int truncate_length; /** * Whether to inspect during image load the directory tree for features * like relaxed compliance. The result then gets recorded in a IsoWriteOpts * object which is attached to IsoImage as .tree_compliance . * bit0: 0= No such inspection * 1= Do the deeper inspection * bit1: 0= let iso_image_import() create a new IsoReadImageFeatures * object * 1= re-use in iso_image_import() the submitted *features object * bit2: 0= normal image loading * 1= filesystem assessment with less warning messages, * no assessment of system area */ int read_features; }; /** * Return information for image. */ struct iso_read_img_feature { const char *name; /* Name by which the feature will be set and inquired */ int valid; /* -1= invalid , 0= not yet set , 1= valid */ const int type; /* 0= num_value , 1= pt_value */ int64_t num_value; void *pt_value; ssize_t pt_size; }; static struct iso_read_img_feature iso_read_img_feature_list[] = { {"size", 0, 0, 0, NULL, 0}, {"iso_level", 0, 0, 0, NULL, 0}, {"rockridge", 0, 0, 0, NULL, 0}, {"joliet", 0, 0, 0, NULL, 0}, {"hfsplus", 0, 0, 0, NULL, 0}, {"iso1999", 0, 0, 0, NULL, 0}, {"fat", 0, 0, 0, NULL, 0}, {"eltorito", 0, 0, 0, NULL, 0}, {"tree_loaded", 0, 0, 0, NULL, 0}, {"tree_loaded_text", 0, 1, 0, NULL, 0}, {"rr_loaded", 0, 0, 0, NULL, 0}, {"hfsp_serial_number", 0, 1, 0, NULL, 0}, {"hfsp_block_size", 0, 0, 0, NULL, 0}, {"hardlinks", 0, 0, 0, NULL, 0}, {"aaip", 0, 0, 0, NULL, 0}, {"untranslated_name_len", 0, 0, 0, NULL, 0}, {"allow_dir_id_ext", 0, 0, 0, NULL, 0}, {"omit_version_numbers", 0, 0, 0, NULL, 0}, {"allow_deep_paths", 0, 0, 0, NULL, 0}, {"rr_reloc_dir", 0, 1, 0, NULL, 0}, {"rr_reloc_flags", 0, 0, 0, NULL, 0}, {"allow_longer_paths", 0, 0, 0, NULL, 0}, {"max_37_char_filenames", 0, 0, 0, NULL, 0}, {"no_force_dots", 0, 0, 0, NULL, 0}, {"allow_lowercase", 0, 0, 0, NULL, 0}, {"allow_full_ascii", 0, 0, 0, NULL, 0}, {"allow_7bit_ascii", 0, 0, 0, NULL, 0}, {"relaxed_vol_atts", 0, 0, 0, NULL, 0}, {"joliet_longer_paths", 0, 0, 0, NULL, 0}, {"joliet_long_names", 0, 0, 0, NULL, 0}, {"joliet_utf16", 0, 0, 0, NULL, 0}, {"rrip_version_1_10", 0, 0, 0, NULL, 0}, {"rrip_1_10_px_ino", 0, 0, 0, NULL, 0}, {"aaip_susp_1_10", 0, 0, 0, NULL, 0}, {"record_md5_session", 0, 0, 0, NULL, 0}, {"record_md5_files", 0, 0, 0, NULL, 0}, {"scdbackup_tag_name", 0, 1, 0, NULL, 0}, {"scdbackup_tag_time", 0, 1, 0, NULL, 0}, {"always_gmt", 0, 0, 0, NULL, 0}, {"", 0, 0, 0, NULL, 0} }; struct iso_read_image_features { /** * Will be filled with the size (in 2048 byte block) of the image, as * reported in the PVM. */ uint32_t size; /** It will be set to 1 if RR extensions are present, to 0 if not. */ unsigned int hasRR :1; /** It will be set to 1 if Joliet extensions are present, to 0 if not. */ unsigned int hasJoliet :1; /** * It will be set to 1 if the image is an ISO 9660:1999, i.e. it has * a version 2 Enhanced Volume Descriptor. */ unsigned int hasIso1999 :1; /** It will be set to 1 if El-Torito boot record is present, to 0 if not.*/ unsigned int hasElTorito :1; /** * Which tree was loaded: * 0= ISO 9660 + Rock Ridge , 1= Joliet , 2= ISO 9660:1999 */ int tree_loaded; /** Whether Rock Ridge info was used while loading: 0= no, 1= yes */ int rr_loaded; /* List of features by name. Especially those of deep inspection. */ int num_named_feat; struct iso_read_img_feature *named_feat; }; static int ifs_fs_open(IsoImageFilesystem *fs); static int ifs_fs_close(IsoImageFilesystem *fs); static int iso_file_source_new_ifs(IsoImageFilesystem *fs, IsoFileSource *parent, struct ecma119_dir_record *record, IsoFileSource **src, int flag); /** unique identifier for each image */ unsigned int fs_dev_id = 0; /** * Should the RR extensions be read? */ enum read_rr_ext { RR_EXT_NO = 0, /*< Do not use RR extensions */ RR_EXT_110 = 1, /*< RR extensions conforming version 1.10 */ RR_EXT_112 = 2 /*< RR extensions conforming version 1.12 */ }; /** * Private data for the image IsoFilesystem */ typedef struct { /** DataSource from where data will be read */ IsoDataSource *src; /** unique id for the each image (filesystem instance) */ unsigned int id; /** * Counter of the times the filesystem has been opened still pending of * close. It is used to keep track of when we need to actually open or * close the IsoDataSource. */ unsigned int open_count; uid_t uid; /**< Default uid when no RR */ gid_t gid; /**< Default uid when no RR */ mode_t dir_mode; /**< Default mode when no RR (only permissions) */ mode_t file_mode; int msgid; char *input_charset; /**< Input charset for RR names */ char *local_charset; /**< For RR names, will be set to the locale one */ /** * Enable or disable methods to automatically choose an input charset. * This eventually overrides input_charset. * * bit0= allow to set the input character set automatically from * attribute "isofs.cs" of root directory */ int auto_input_charset; /** * Will be filled with the block lba of the extend for the root directory * of the hierarchy that will be read, either from the PVD (ISO, RR) or * from the SVD (Joliet) */ uint32_t iso_root_block; /** * Will be filled with the block lba of the extend for the root directory, * as read from the PVM */ uint32_t pvd_root_block; /** * Will be filled with the block lba of the extend for the root directory, * as read from the SVD */ uint32_t svd_root_block; /** * Will be filled with the block lba of the extend for the root directory, * as read from the enhanced volume descriptor (ISO 9660:1999) */ uint32_t evd_root_block; /** * If we need to read RR extensions. i.e., if the image contains RR * extensions, and the user wants to read them. */ enum read_rr_ext rr; /** * Bytes skipped within the System Use field of a directory record, before * the beginning of the SUSP system user entries. See IEEE 1281, SUSP. 5.3. */ uint8_t len_skp; /* Volume attributes */ char *volset_id; char *volume_id; /**< Volume identifier. */ char *publisher_id; /**< Volume publisher. */ char *data_preparer_id; /**< Volume data preparer. */ char *system_id; /**< Volume system identifier. */ char *application_id; /**< Volume application id */ char *copyright_file_id; char *abstract_file_id; char *biblio_file_id; char *creation_time; char *modification_time; char *expiration_time; char *effective_time; /* extension information */ /** * RR version being used in image. * 0 no RR extension, 1 RRIP 1.10, 2 RRIP 1.12 */ enum read_rr_ext rr_version; /** If Joliet extensions are available on image */ unsigned int joliet : 1; /** If ISO 9660:1999 is available on image */ unsigned int iso1999 : 1; /** * See struct iso_read_opts. */ int truncate_mode; int truncate_length; unsigned int ecma119_map : 2; unsigned int joliet_map : 1; /** Whether AAIP info shall be loaded if it is present. * 1 = yes , 0 = no */ int aaip_load; /** Whether the MD5 array shall be read if available. * 2 = yes, but do not check tags , 1 = yes , 0 = no */ int md5_load; /** Whether MD5 sums were checked during filesystem assessment * bit0= root checksum tag * bit1= tree checksum tag * (not yet: bit2= session checksum tag) */ int md5_checked; /** Whether AAIP is present. Version major.minor = major * 100 + minor * Value -1 means that no AAIP ER was detected yet. */ int aaip_version; /** * Start block of loaded session. */ uint32_t session_lba; /** * Number of blocks of the volume, as reported in the PVM. */ uint32_t nblocks; /* el-torito information */ unsigned int eltorito : 1; /* is el-torito available */ int num_bootimgs; unsigned char platform_ids[Libisofs_max_boot_imageS]; unsigned char id_strings[Libisofs_max_boot_imageS][28]; unsigned char selection_crits[Libisofs_max_boot_imageS][20]; unsigned char boot_flags[Libisofs_max_boot_imageS]; /* bit0= bootable */ unsigned char media_types[Libisofs_max_boot_imageS]; unsigned char partition_types[Libisofs_max_boot_imageS]; short load_segs[Libisofs_max_boot_imageS]; short load_sizes[Libisofs_max_boot_imageS]; /** Block addresses of for El-Torito boot images. Needed to recognize them when the get read from the directory tree. */ uint32_t bootblocks[Libisofs_max_boot_imageS]; uint32_t catblock; /**< Block for El-Torito catalog */ off_t catsize; /* Size of boot catalog in bytes */ char *catcontent; /* Whether inode numbers from PX entries shall be discarded */ unsigned int make_new_ino : 1 ; /* Inode number generator counter. 32 bit because for Rock Ridge PX. */ uint32_t inode_counter; /* PX inode number status bit0= there were nodes with PX inode numbers bit1= there were nodes with PX but without inode numbers bit2= there were nodes without PX bit3= there were nodes with faulty PX */ int px_ino_status; /* Which Rock Ridge error messages already have occurred bit0= Invalid PX entry bit1= Invalid TF entry bit2= New NM entry found without previous CONTINUE flag bit3= Invalid NM entry bit4= New SL entry found without previous CONTINUE flag bit5= Invalid SL entry bit6= Invalid CL entry, no child location / found in CL target bit7= Invalid PN entry bit8= Sparse files not supported bit9= SP entry found in a directory entry other than '.' entry of root bit10= ER entry found in a directory entry other than '.' entry of root bit11= Invalid AA entry bit12= Invalid AL entry bit13= Invalid ZF entry bit14= Rock Ridge PX entry is not present or invalid bit15= Incomplete NM bit16= Incomplete SL bit17= Charset conversion error bit18= Link without destination bit19= SL with a non-link file */ int rr_err_reported; int rr_err_repeated; size_t joliet_ucs2_failures; } _ImageFsData; typedef struct image_fs_data ImageFileSourceData; /* IMPORTANT: Any change must be reflected by ifs_clone_src */ struct image_fs_data { IsoImageFilesystem *fs; /**< reference to the image it belongs to */ IsoFileSource *parent; /**< reference to the parent (NULL if root) */ struct stat info; /**< filled struct stat */ char *name; /**< name of this file */ /** * Location of file extents. */ struct iso_file_section *sections; int nsections; unsigned int opened : 2; /**< 0 not opened, 1 opened file, 2 opened dir */ #ifdef Libisofs_with_zliB uint8_t zisofs_algo[2]; uint8_t header_size_div4; uint8_t block_size_log2; uint64_t uncompressed_size; #endif /* info for content reading */ struct { /** * - For regular files, once opened it points to a temporary data * buffer of 2048 bytes. * - For dirs, once opened it points to a IsoFileSource* array with * its children * - For symlinks, it points to link destination */ void *content; /** * - For regular files, number of bytes already read. */ off_t offset; } data; /** * malloc() storage for the string of AAIP fields which represent * ACLs and XFS-style Extended Attributes. (Not to be confused with * ECMA-119 Extended Attributes.) */ unsigned char *aa_string; }; static int iso_impsysa_reduce_next_above(IsoImage *image, uint32_t block, uint32_t *next_above, int flag); struct child_list { IsoFileSource *file; struct child_list *next; }; void child_list_free(struct child_list *list) { struct child_list *temp; struct child_list *next = list; while (next != NULL) { temp = next->next; iso_file_source_unref(next->file); free(next); next = temp; } } static char* ifs_get_path(IsoFileSource *src) { ImageFileSourceData *data; data = src->data; if (data->parent == NULL) { return strdup(""); } else { char *path, *new_path; int pathlen; if (data->name == NULL) return NULL; path = ifs_get_path(data->parent); if (path == NULL) return NULL; pathlen = strlen(path); new_path = realloc(path, pathlen + strlen(data->name) + 2); if (new_path == NULL) { free(path); return NULL; } path= new_path; path[pathlen] = '/'; path[pathlen + 1] = '\0'; return strcat(path, data->name); } } static char* ifs_get_name(IsoFileSource *src) { ImageFileSourceData *data; data = src->data; return data->name == NULL ? NULL : strdup(data->name); } static int ifs_lstat(IsoFileSource *src, struct stat *info) { ImageFileSourceData *data; if (src == NULL || info == NULL) { return ISO_NULL_POINTER; } data = src->data; if (data == NULL) return ISO_NULL_POINTER; *info = data->info; return ISO_SUCCESS; } static int ifs_stat(IsoFileSource *src, struct stat *info) { ImageFileSourceData *data; if (src == NULL || info == NULL || src->data == NULL) { return ISO_NULL_POINTER; } data = (ImageFileSourceData*)src->data; if (S_ISLNK(data->info.st_mode)) { /* TODO #00012 : support follow symlinks on image filesystem */ return ISO_FILE_BAD_PATH; } *info = data->info; return ISO_SUCCESS; } static int ifs_access(IsoFileSource *src) { /* we always have access, it is controlled by DataSource */ return ISO_SUCCESS; } /** * Read all directory records in a directory, and creates an IsoFileSource for * each of them, storing them in the data field of the IsoFileSource for the * given dir. */ static int read_dir(ImageFileSourceData *data) { int ret; uint32_t size; uint32_t block; IsoImageFilesystem *fs; _ImageFsData *fsdata; struct ecma119_dir_record *record; uint8_t *buffer = NULL; IsoFileSource *child = NULL; uint32_t pos = 0; uint32_t tlen = 0; if (data == NULL) { ret = ISO_NULL_POINTER; goto ex; } LIBISO_ALLOC_MEM(buffer, uint8_t, BLOCK_SIZE); fs = data->fs; fsdata = fs->data; /* a dir has always a single extent */ block = data->sections[0].block; ret = fsdata->src->read_block(fsdata->src, block, buffer); if (ret < 0) { goto ex; } /* "." entry, get size of the dir and skip */ record = (struct ecma119_dir_record *)(buffer + pos); size = iso_read_bb(record->length, 4, NULL); tlen += record->len_dr[0]; pos += record->len_dr[0]; /* skip ".." */ record = (struct ecma119_dir_record *)(buffer + pos); tlen += record->len_dr[0]; pos += record->len_dr[0]; while (tlen < size) { record = (struct ecma119_dir_record *)(buffer + pos); if (pos == 2048 || record->len_dr[0] == 0) { /* * The directory entries are split in several blocks * read next block */ ret = fsdata->src->read_block(fsdata->src, ++block, buffer); if (ret < 0) { goto ex; } tlen += 2048 - pos; pos = 0; continue; } /* (Vreixo:) * What about ignoring files with existence flag? * if (record->flags[0] & 0x01) * continue; * ts B20306 : >>> One should rather record that flag and write it * >>> to the new image. */ #ifdef Libisofs_wrongly_skip_rr_moveD /* ts B20306 : This skipping by name is wrong resp. redundant: If no rr reading is enabled, then it is the only access point for the content of relocated directories. So one should not ignore it. If rr reading is enabled, then the RE entry of mkisofs' RR_MOVED will cause it to be skipped. */ /* (Vreixo:) * For a extrange reason, mkisofs relocates directories under * a RR_MOVED dir. It seems that it is only used for that purposes, * and thus it should be removed from the iso tree before * generating a new image with libisofs, that don't uses it. */ if (data->parent == NULL && record->len_fi[0] == 8 && !strncmp((char*)record->file_id, "RR_MOVED", 8)) { iso_msg_debug(fsdata->msgid, "Skipping RR_MOVE entry."); tlen += record->len_dr[0]; pos += record->len_dr[0]; continue; } #endif /* Libisofs_wrongly_skip_rr_moveD */ /* * We pass a NULL parent instead of dir, to prevent the circular * reference from child to parent. */ ret = iso_file_source_new_ifs(fs, NULL, record, &child, 0); if (ret < 0) { if (child) { /* * This can only happen with multi-extent files. */ ImageFileSourceData *ifsdata = child->data; free(ifsdata->sections); free(ifsdata->name); free(ifsdata); free(child); } goto ex; } /* add to the child list */ if (ret == 1) { struct child_list *node; node = malloc(sizeof(struct child_list)); if (node == NULL) { iso_file_source_unref(child); {ret = ISO_OUT_OF_MEM; goto ex;} } /* * Note that we insert in reverse order. This leads to faster * addition here, but also when adding to the tree, as insertion * will be done, sorted, in the first position of the list. */ node->next = data->data.content; node->file = child; data->data.content = node; child = NULL; } tlen += record->len_dr[0]; pos += record->len_dr[0]; } ret = ISO_SUCCESS; ex:; LIBISO_FREE_MEM(buffer); return ret; } static int ifs_open(IsoFileSource *src) { int ret; ImageFileSourceData *data; if (src == NULL || src->data == NULL) { return ISO_NULL_POINTER; } data = (ImageFileSourceData*)src->data; if (data->opened) { return ISO_FILE_ALREADY_OPENED; } if (S_ISDIR(data->info.st_mode)) { /* ensure fs is opened */ ret = data->fs->open(data->fs); if (ret < 0) { return ret; } /* * Cache all directory entries. * This can waste more memory, but improves as disc is read in much more * sequentially way, thus reducing jump between tracks on disc */ ret = read_dir(data); data->fs->close(data->fs); if (ret < 0) { /* free probably allocated children */ child_list_free((struct child_list*)data->data.content); } else { data->opened = 2; } return ret; } else if (S_ISREG(data->info.st_mode)) { /* ensure fs is opened */ ret = data->fs->open(data->fs); if (ret < 0) { return ret; } data->data.content = malloc(BLOCK_SIZE); if (data->data.content == NULL) { return ISO_OUT_OF_MEM; } data->data.offset = 0; data->opened = 1; } else { /* symlinks and special files inside image can't be opened */ return ISO_FILE_ERROR; } return ISO_SUCCESS; } static int ifs_close(IsoFileSource *src) { ImageFileSourceData *data; if (src == NULL || src->data == NULL) { return ISO_NULL_POINTER; } data = (ImageFileSourceData*)src->data; if (!data->opened) { return ISO_FILE_NOT_OPENED; } if (data->opened == 2) { /* * close a dir, free all pending pre-allocated children. * not that we don't need to close the filesystem, it was already * closed */ child_list_free((struct child_list*) data->data.content); data->data.content = NULL; data->opened = 0; } else if (data->opened == 1) { /* close regular file */ free(data->data.content); data->fs->close(data->fs); data->data.content = NULL; data->opened = 0; } else { /* TODO only dirs and files supported for now */ return ISO_ERROR; } return ISO_SUCCESS; } /** * Computes the block where the given offset should start. */ static uint32_t block_from_offset(int nsections, struct iso_file_section *sections, off_t offset) { int section = 0; off_t bytes = 0; do { if ( (offset - bytes) < (off_t) sections[section].size ) { return sections[section].block + (offset - bytes) / BLOCK_SIZE; } else { bytes += (off_t) sections[section].size; section++; } } while(section < nsections); return 0; /* should never happen */ } /** * Get the size available for reading on the corresponding block */ static uint32_t size_available(int nsections, struct iso_file_section *sections, off_t offset) { int section = 0; off_t bytes = 0; do { if ( (offset - bytes) < (off_t) sections[section].size ) { uint32_t curr_section_offset = (uint32_t)(offset - bytes); uint32_t curr_section_left = sections[section].size - curr_section_offset; uint32_t available = BLOCK_SIZE - curr_section_offset % BLOCK_SIZE; return MIN(curr_section_left, available); } else { bytes += (off_t) sections[section].size; section++; } } while(section < nsections); return 0; /* should never happen */ } /** * Get the block offset for reading the given file offset */ static uint32_t block_offset(int nsections, struct iso_file_section *sections, off_t offset) { int section = 0; off_t bytes = 0; do { if ( (offset - bytes) < (off_t) sections[section].size ) { return (uint32_t)(offset - bytes) % BLOCK_SIZE; } else { bytes += (off_t) sections[section].size; section++; } } while(section < nsections); return 0; /* should never happen */ } /** * Attempts to read up to count bytes from the given source into * the buffer starting at buf. * * The file src must be open() before calling this, and close() when no * more needed. Not valid for dirs. On symlinks it reads the destination * file. * * @return * number of bytes read, 0 if EOF, < 0 on error * Error codes: * ISO_FILE_ERROR * ISO_NULL_POINTER * ISO_FILE_NOT_OPENED * ISO_FILE_IS_DIR * ISO_OUT_OF_MEM * ISO_INTERRUPTED */ static int ifs_read(IsoFileSource *src, void *buf, size_t count) { int ret; ImageFileSourceData *data; uint32_t read = 0; if (src == NULL || src->data == NULL || buf == NULL) { return ISO_NULL_POINTER; } if (count == 0) { return ISO_WRONG_ARG_VALUE; } data = (ImageFileSourceData*)src->data; if (!data->opened) { return ISO_FILE_NOT_OPENED; } else if (data->opened != 1) { return ISO_FILE_IS_DIR; } while (read < count && data->data.offset < data->info.st_size) { size_t bytes; uint8_t *orig; if (block_offset(data->nsections, data->sections, data->data.offset) == 0) { /* we need to buffer next block */ uint32_t block; _ImageFsData *fsdata; if (data->data.offset >= data->info.st_size) { /* EOF */ break; } fsdata = data->fs->data; block = block_from_offset(data->nsections, data->sections, data->data.offset); ret = fsdata->src->read_block(fsdata->src, block, data->data.content); if (ret < 0) { return ret; } } /* how much can I read */ bytes = MIN(size_available(data->nsections, data->sections, data->data.offset), count - read); if (data->data.offset + (off_t)bytes > data->info.st_size) { bytes = data->info.st_size - data->data.offset; } orig = data->data.content; orig += block_offset(data->nsections, data->sections, data->data.offset); memcpy((uint8_t*)buf + read, orig, bytes); read += bytes; data->data.offset += (off_t)bytes; } return read; } static off_t ifs_lseek(IsoFileSource *src, off_t offset, int flag) { ImageFileSourceData *data; if (src == NULL) { return (off_t)((int) ISO_NULL_POINTER); } if (offset < (off_t)0) { return (off_t)((int) ISO_WRONG_ARG_VALUE); } data = src->data; if (!data->opened) { return (off_t)((int) ISO_FILE_NOT_OPENED); } else if (data->opened != 1) { return (off_t)((int) ISO_FILE_IS_DIR); } switch (flag) { case 0: /* SEEK_SET */ data->data.offset = offset; break; case 1: /* SEEK_CUR */ data->data.offset += offset; break; case 2: /* SEEK_END */ /* do this make sense? */ data->data.offset = data->info.st_size + offset; break; default: return (off_t)((int) ISO_WRONG_ARG_VALUE); } /* * We check for block_offset != 0 because if it is already 0, the block * will be read from image in the read function */ if (block_offset(data->nsections, data->sections, data->data.offset) != 0) { /* we need to buffer the block */ uint32_t block; _ImageFsData *fsdata; if (data->data.offset < data->info.st_size) { int ret; fsdata = data->fs->data; block = block_from_offset(data->nsections, data->sections, data->data.offset); ret = fsdata->src->read_block(fsdata->src, block, data->data.content); if (ret < 0) { return (off_t)ret; } } } return data->data.offset; } static int ifs_readdir(IsoFileSource *src, IsoFileSource **child) { ImageFileSourceData *data, *cdata; struct child_list *children; if (src == NULL || src->data == NULL || child == NULL) { return ISO_NULL_POINTER; } data = (ImageFileSourceData*)src->data; if (!data->opened) { return ISO_FILE_NOT_OPENED; } else if (data->opened != 2) { return ISO_FILE_IS_NOT_DIR; } /* return the first child and free it */ if (data->data.content == NULL) { return 0; /* EOF */ } children = (struct child_list*)data->data.content; *child = children->file; cdata = (ImageFileSourceData*)(*child)->data; /* set the ref to the parent */ cdata->parent = src; iso_file_source_ref(src); /* free the first element of the list */ data->data.content = children->next; free(children); return ISO_SUCCESS; } /** * Read the destination of a symlink. You don't need to open the file * to call this. * * @param buf * allocated buffer of at least bufsiz bytes. * The dest. will be copied there, and it will be NULL-terminated * @param bufsiz * characters to be copied. Destination link will be truncated if * it is larger than given size. This include the \0 character. * @return * 1 on success, < 0 on error * Error codes: * ISO_FILE_ERROR * ISO_NULL_POINTER * ISO_WRONG_ARG_VALUE -> if bufsiz <= 0 * ISO_FILE_IS_NOT_SYMLINK * ISO_OUT_OF_MEM * ISO_FILE_BAD_PATH * ISO_FILE_DOESNT_EXIST * */ static int ifs_readlink(IsoFileSource *src, char *buf, size_t bufsiz) { char *dest; size_t len; int ret; ImageFileSourceData *data; if (src == NULL || buf == NULL || src->data == NULL) { return ISO_NULL_POINTER; } if (bufsiz <= 0) { return ISO_WRONG_ARG_VALUE; } data = (ImageFileSourceData*)src->data; if (!S_ISLNK(data->info.st_mode)) { return ISO_FILE_IS_NOT_SYMLINK; } dest = (char*)data->data.content; len = strlen(dest); ret = ISO_SUCCESS; if (len >= bufsiz) { ret = ISO_RR_PATH_TOO_LONG; len = bufsiz - 1; } strncpy(buf, dest, len); buf[len] = '\0'; return ret; } static IsoFilesystem* ifs_get_filesystem(IsoFileSource *src) { ImageFileSourceData *data; if (src == NULL) { return NULL; } data = src->data; return data->fs; } static void ifs_free(IsoFileSource *src) { ImageFileSourceData *data; data = src->data; /* close the file if it is already opened */ if (data->opened) { src->class->close(src); } /* free destination if it is a link */ if (S_ISLNK(data->info.st_mode)) { free(data->data.content); } iso_filesystem_unref(data->fs); if (data->parent != NULL) { iso_file_source_unref(data->parent); } free(data->sections); free(data->name); if (data->aa_string != NULL) free(data->aa_string); free(data); } static int ifs_get_aa_string(IsoFileSource *src, unsigned char **aa_string, int flag) { size_t len; ImageFileSourceData *data; data = src->data; if ((flag & 1) || data->aa_string == NULL) { *aa_string = data->aa_string; data->aa_string = NULL; } else { len = aaip_count_bytes(data->aa_string, 0); *aa_string = calloc(len, 1); if (*aa_string == NULL) return ISO_OUT_OF_MEM; memcpy(*aa_string, data->aa_string, len); } return 1; } static int ifs_clone_src(IsoFileSource *old_source, IsoFileSource **new_source, int flag) { IsoFileSource *src = NULL; ImageFileSourceData *old_data, *new_data = NULL; char *new_name = NULL; struct iso_file_section *new_sections = NULL; void *new_aa_string = NULL; int i, ret; if (flag) return ISO_STREAM_NO_CLONE; /* unknown option required */ old_data = (ImageFileSourceData *) old_source->data; *new_source = NULL; src = calloc(1, sizeof(IsoFileSource)); if (src == NULL) goto no_mem; new_name = strdup(old_data->name); if (new_name == NULL) goto no_mem; new_data = calloc(1, sizeof(ImageFileSourceData)); if (new_data == NULL) goto no_mem; if (old_data->nsections > 0) { new_sections = calloc(old_data->nsections, sizeof(struct iso_file_section)); if (new_sections == NULL) goto no_mem; } ret = aaip_xinfo_cloner(old_data->aa_string, &new_aa_string, 0); if (ret < 0) goto no_mem; new_data->fs = old_data->fs; new_data->parent = old_data->parent; memcpy(&(new_data->info), &(old_data->info), sizeof(struct stat)); new_data->name = new_name; new_data->sections = new_sections; new_data->nsections = old_data->nsections; for (i = 0; i < new_data->nsections; i++) memcpy(new_data->sections + i, old_data->sections + i, sizeof(struct iso_file_section)); new_data->opened = old_data->opened; #ifdef Libisofs_with_zliB new_data->header_size_div4 = old_data->header_size_div4; new_data->block_size_log2 = old_data->block_size_log2; new_data->uncompressed_size = old_data->uncompressed_size; #endif new_data->data.content = NULL; new_data->aa_string = (unsigned char *) new_aa_string; src->class = old_source->class; src->refcount = 1; src->data = new_data; *new_source = src; iso_file_source_ref(new_data->parent); iso_filesystem_ref(new_data->fs); return ISO_SUCCESS; no_mem:; if (src != NULL) free((char *) src); if (new_data != NULL) free((char *) new_data); if (new_name != NULL) free(new_name); if (new_sections != NULL) free((char *) new_sections); if (new_aa_string != NULL) aaip_xinfo_func(new_aa_string, 1); return ISO_OUT_OF_MEM; } IsoFileSourceIface ifs_class = { 2, /* version */ ifs_get_path, ifs_get_name, ifs_lstat, ifs_stat, ifs_access, ifs_open, ifs_close, ifs_read, ifs_readdir, ifs_readlink, ifs_get_filesystem, ifs_free, ifs_lseek, ifs_get_aa_string, ifs_clone_src }; /* Used from libisofs/stream.c : iso_stream_get_src_zf() */ int iso_ifs_source_get_zf(IsoFileSource *src, uint8_t zisofs_algo[2], int *header_size_div4, int *block_size_log2, uint64_t *uncompressed_size, int flag) { #ifdef Libisofs_with_zliB ImageFileSourceData *data; if (src->class != &ifs_class) return 0; data = src->data; zisofs_algo[0] = data->zisofs_algo[0]; zisofs_algo[1] = data->zisofs_algo[1]; *header_size_div4 = data->header_size_div4; *block_size_log2 = data->block_size_log2; *uncompressed_size = data->uncompressed_size; return 1; #else return 0; #endif /* ! Libisofs_with_zliB */ } static int make_hopefully_unique_name(_ImageFsData *fsdata, char *str, size_t len, char **name) { int ret, name_len, i; char c, *smashed = NULL, md5[16]; void *md5_context = NULL; /* Shorten so that 32 characters of MD5 fit. If shorter than 8, pad up to 8 by '_'. Smash characters to [0-9A-Za-z_.]. Append MD5 of original str as hex digits. */ name_len = len > 223 ? 223 : len; LIBISO_ALLOC_MEM(smashed, char, (name_len >= 8 ? name_len : 8) + 32 + 1); memcpy(smashed, str, name_len); for (; name_len < 8; name_len++) smashed[name_len] = '_'; smashed[name_len] = 0; for (i = 0; i < name_len; i++) { c = smashed[i]; if (c == '.' || (c >= '0' && c <= '9') || c == '_' || (c >= 'a' && c <= 'z')) continue; smashed[i] = '_'; } ret = iso_md5_start(&md5_context); if (ret != 1) goto ex; ret = iso_md5_compute(md5_context, str, len); if (ret != 1) goto ex; ret = iso_md5_end(&md5_context, md5); if (ret != 1) goto ex; for (i = 0; i < 16; i++) sprintf(smashed + i * 2 + name_len, "%2.2x", ((unsigned char *) md5)[i]); name_len += 32; smashed[name_len] = 0; *name = smashed; smashed = NULL; ret = ISO_SUCCESS; ex: LIBISO_FREE_MEM(smashed); if (md5_context != NULL) iso_md5_end(&md5_context, md5); return ret; } /** * Read a file name from a directory record, doing the needed charset * conversion */ static char *get_name(_ImageFsData *fsdata, char *str, size_t len) { int ret; char *name = NULL, *from_ucs = NULL; if (strcmp(fsdata->local_charset, fsdata->input_charset)) { /* charset conversion needed */ ret = strnconv(str, fsdata->input_charset, fsdata->local_charset, len, &name); if (ret == 1) { if (fsdata->iso_root_block == fsdata->svd_root_block) { /* Reading from Joliet : Check whether UTF-16 was needed */ ret = strnconv(str, "UCS-2BE", fsdata->local_charset, len, &from_ucs); if (ret == 1) ret = (strcmp(name, from_ucs) == 0); if (ret != 1) { fsdata->joliet_ucs2_failures++; if (fsdata->joliet_ucs2_failures <= ISO_JOLIET_UCS2_WARN_MAX) iso_msg_submit(-1, ISO_NAME_NOT_UCS2, 0, "Joliet filename valid only with character set UTF-16 : \"%s\"", name); } if (from_ucs != NULL) free(from_ucs); } return name; } else { ret = iso_msg_submit(fsdata->msgid, ISO_FILENAME_WRONG_CHARSET, ret, "Cannot convert from charset %s to %s", fsdata->input_charset, fsdata->local_charset); if (ret < 0) { return NULL; /* aborted */ } /* fallback */ ret = make_hopefully_unique_name(fsdata, str, len, &name); if (ret == ISO_SUCCESS) return name; return NULL; } } /* we reach here when the charset conversion is not needed */ name = malloc(len + 1); if (name == NULL) { return NULL; } memcpy(name, str, len); name[len] = '\0'; return name; } static int iso_rr_msg_submit(_ImageFsData *fsdata, int rr_err_bit, int errcode, int causedby, const char *msg) { int ret; if ((fsdata->rr_err_reported & (1 << rr_err_bit)) && (fsdata->rr_err_repeated & (1 << rr_err_bit))) { if (iso_msg_is_abort(errcode)) return ISO_CANCELED; return 0; } if (fsdata->rr_err_reported & (1 << rr_err_bit)) { ret = iso_msg_submit(fsdata->msgid, errcode, causedby, "MORE THAN ONCE : %s", msg); fsdata->rr_err_repeated |= (1 << rr_err_bit); } else { ret = iso_msg_submit(fsdata->msgid, errcode, causedby, "%s", msg); fsdata->rr_err_reported |= (1 << rr_err_bit); } return ret; } /** * * @param src * if not-NULL, it points to a multi-extent file returned by a previous * call to this function. * @param flag * bit0= this is the root node attribute load call * (parameter parent is not reliable for this) * bit1= this is a call caused by CL. Do not obey CL again. * @return * 2 node is still incomplete (multi-extent) * 1 success, 0 record ignored (not an error, can be a relocated dir), * < 0 error */ static int iso_file_source_new_ifs(IsoImageFilesystem *fs, IsoFileSource *parent, struct ecma119_dir_record *record, IsoFileSource **src, int flag) { int ret, ecma119_map, skip_nm = 0; struct stat atts; time_t recorded; _ImageFsData *fsdata; IsoFileSource *ifsrc = NULL; ImageFileSourceData *ifsdata = NULL; int namecont = 0; /* 1 if found a NM with CONTINUE flag */ char *name = NULL; /* 1 if found a SL with CONTINUE flag, * 2 if found a component with continue flag */ int linkdestcont = 0; char *linkdest = NULL; uint32_t relocated_dir = 0; unsigned char *aa_string = NULL; size_t aa_size = 0, aa_len = 0, prev_field = 0; int aa_done = 0; char *msg = NULL; uint8_t *buffer = NULL; char *cpt; int has_px = 0; #ifdef Libisofs_with_zliB uint8_t zisofs_alg[2], zisofs_hs4 = 0, zisofs_bsl2 = 0; uint64_t zisofs_usize = 0; #endif if (fs == NULL || fs->data == NULL || record == NULL || src == NULL) { ret = ISO_NULL_POINTER; goto ex; } fsdata = (_ImageFsData*)fs->data; memset(&atts, 0, sizeof(struct stat)); atts.st_nlink = 1; /* Set preliminary file type */ if (record->flags[0] & 0x02) { atts.st_mode = S_IFDIR; } else { atts.st_mode = S_IFREG; } /* * First of all, check for unsupported ECMA-119 features */ /* check for unsupported interleaved mode */ if (record->file_unit_size[0] || record->interleave_gap_size[0]) { iso_msg_submit(fsdata->msgid, ISO_UNSUPPORTED_ECMA119, 0, "Unsupported image. This image has at least one file recorded " "in interleaved mode. We do not support this mode, as we think " "it is not used. If you are reading this, then we are wrong :) " "Please contact libisofs developers, so we can fix this."); {ret = ISO_UNSUPPORTED_ECMA119; goto ex;} } /* TODO #00013 : check for unsupported flags when reading a dir record */ /* * If src is not-NULL, it refers to more extents of this file. We ensure * name matches, otherwise it means we are dealing with wrong image */ if (*src != NULL) { ImageFileSourceData* data = (*src)->data; char* new_name = get_name(fsdata, (char*)record->file_id, record->len_fi[0]); if (new_name == NULL) { iso_msg_submit(fsdata->msgid, ISO_WRONG_ECMA119, 0, "Cannot retrieve file name"); {ret = ISO_WRONG_ECMA119; goto ex;} } if (strcmp(new_name, data->name)) { iso_msg_submit(fsdata->msgid, ISO_WRONG_ECMA119, 0, "Multi-extent file lacks last entry."); free(new_name); {ret = ISO_WRONG_ECMA119; goto ex;} } free(new_name); } /* check for multi-extent */ if (record->flags[0] & 0x80) { iso_msg_debug(fsdata->msgid, "Found multi-extent file"); /* * Directory entries can only have one section (ECMA-119, 6.8.1) */ if ((record->flags[0] & 0x02) || (flag & 1)) { iso_msg_submit(fsdata->msgid, ISO_WRONG_ECMA119, 0, "Directories with more than one section are not allowed."); {ret = ISO_WRONG_ECMA119; goto ex;} } if (*src == NULL) { ifsdata = calloc(1, sizeof(ImageFileSourceData)); if (ifsdata == NULL) { ret = ISO_OUT_OF_MEM; goto ifs_cleanup; } ifsrc = calloc(1, sizeof(IsoFileSource)); if (ifsrc == NULL) { ret = ISO_OUT_OF_MEM; goto ifs_cleanup; } ifsrc->data = ifsdata; ifsdata->name = get_name(fsdata, (char*)record->file_id, record->len_fi[0]); if (ifsdata->name == NULL) { iso_msg_submit(fsdata->msgid, ISO_WRONG_ECMA119, 0, "Cannot retrieve file name"); ret = ISO_WRONG_ECMA119; goto ifs_cleanup; } *src = ifsrc; } else { ifsdata = (*src)->data; } /* store current extent */ ifsdata->sections = realloc(ifsdata->sections, (1 + ifsdata->nsections) * sizeof(struct iso_file_section)); if (ifsdata->sections == NULL) { free(ifsdata->name); ret = ISO_OUT_OF_MEM; goto ifs_cleanup; } ifsdata->sections[ifsdata->nsections].block = iso_read_bb(record->block, 4, NULL) + record->len_xa[0]; ifsdata->sections[ifsdata->nsections].size = iso_read_bb(record->length, 4, NULL); ifsdata->info.st_size += (off_t) ifsdata->sections[ifsdata->nsections].size; ifsdata->nsections++; {ret = 2; goto ex;} } /* * The idea is to read all the RR entries (if we want to do that and RR * extensions exist on image), storing the info we want from that. * Then, we need some sanity checks. * Finally, we select what kind of node it is, and set values properly. */ if (fsdata->rr) { struct susp_sys_user_entry *sue; SuspIterator *iter; iter = susp_iter_new(fsdata->src, record, fsdata->session_lba + fsdata->nblocks, fsdata->len_skp, fsdata->msgid); if (iter == NULL) { {ret = ISO_OUT_OF_MEM; goto ex;} } while ((ret = susp_iter_next(iter, &sue, 0)) > 0) { /* ignore entries from different version */ if (sue->version[0] != 1 && !(SUSP_SIG(sue, 'Z', 'F') || SUSP_SIG(sue, 'Z', '2'))) continue; if (SUSP_SIG(sue, 'P', 'X')) { has_px = 1; ret = read_rr_PX(sue, &atts); if (ret < 0) { /* notify and continue */ ret = iso_rr_msg_submit(fsdata, 0, ISO_WRONG_RR_WARN, ret, "Invalid PX entry"); fsdata->px_ino_status |= 8; } if (ret == 2) { if (fsdata->inode_counter < atts.st_ino) fsdata->inode_counter = atts.st_ino; fsdata->px_ino_status |= 1; } else { fsdata->px_ino_status |= 2; } } else if (SUSP_SIG(sue, 'T', 'F')) { ret = read_rr_TF(sue, &atts); if (ret < 0) { /* notify and continue */ ret = iso_rr_msg_submit(fsdata, 1, ISO_WRONG_RR_WARN, ret, "Invalid TF entry"); } } else if (SUSP_SIG(sue, 'N', 'M')) { if (skip_nm) continue; /* in NM error bailout mode */ if (name != NULL && namecont == 0) { /* ups, RR standard violation */ ret = iso_rr_msg_submit(fsdata, 2, ISO_WRONG_RR_WARN, 0, "New NM entry found without previous" "CONTINUE flag. Ignored"); skip_nm = 1; continue; } ret = read_rr_NM(sue, &name, &namecont); if (ret < 0) { /* notify and continue */ ret = iso_rr_msg_submit(fsdata, 3, ISO_WRONG_RR_WARN, ret, "Invalid NM entry"); continue; } if (name != NULL) if (strlen(name) > 4095) { /* Preliminarily truncate totally oversized name */ ret = iso_rr_msg_submit(fsdata, 3, ISO_WRONG_RR_WARN, ret, "Totally oversized NM list"); skip_nm = 1; continue; } #ifdef Libisofs_syslinux_tesT if (name != NULL && !namecont) { struct device syslinux_dev; struct iso_sb_info syslinux_sbi; struct fs_info syslinux_fsi; char *syslinux_name = NULL; int syslinux_name_len; syslinux_dev.src = fsdata->src; memset(&(syslinux_sbi.root), 0, 256); syslinux_sbi.do_rr = 1; syslinux_sbi.susp_skip = 0; syslinux_fsi.fs_dev = &syslinux_dev; syslinux_fsi.fs_info = &syslinux_sbi; ret = susp_rr_get_nm(&syslinux_fsi, (char *) record, &syslinux_name, &syslinux_name_len); if (ret == 1) { if (name == NULL || syslinux_name == NULL) fprintf(stderr, "################ Hoppla. NULL\n"); else if(strcmp(syslinux_name, name) != 0) fprintf(stderr, "################ libisofs '%s' != '%s' susp_rr_get_nm()\n", name, syslinux_name); } else if (ret == 0) { fprintf(stderr, "################ '%s' not found by susp_rr_get_nm()\n", name); } else { fprintf(stderr, "################ 'susp_rr_get_nm() returned error\n"); } if (syslinux_name != NULL) free(syslinux_name); } #endif /* Libisofs_syslinux_tesT */ } else if (SUSP_SIG(sue, 'S', 'L')) { if (linkdest != NULL && linkdestcont == 0) { /* ups, RR standard violation */ ret = iso_rr_msg_submit(fsdata, 4, ISO_WRONG_RR_WARN, 0, "New SL entry found without previous " "CONTINUE flag. Ignored"); continue; } ret = read_rr_SL(sue, &linkdest, &linkdestcont); if (ret < 0) { /* notify and continue */ ret = iso_rr_msg_submit(fsdata, 5, ISO_WRONG_RR_WARN, ret, "Invalid SL entry"); } } else if (SUSP_SIG(sue, 'R', 'E')) { /* * this directory entry refers to a relocated directory. * We simply ignore it, as it will be correctly handled * when found the CL */ susp_iter_free(iter); free(name); if (flag & 1) { ret = iso_rr_msg_submit(fsdata, 3, ISO_NO_ROOT_DIR, 0, "Root directory is marked by RRIP RE as relocated"); ret= ISO_NO_ROOT_DIR; goto ex; } {ret = 0; goto ex;} /* it's not an error */ } else if (SUSP_SIG(sue, 'C', 'L') && (flag & 2)) { ret = iso_rr_msg_submit(fsdata, 6, ISO_WRONG_RR, 0, "Invalid CL entry, found in CL target"); } else if (SUSP_SIG(sue, 'C', 'L')) { /* * This entry is a placeholder for a relocated dir. * We need to ignore other entries, with the exception of NM. * Then we create a directory node that represents the * relocated dir, and iterate over its children. */ relocated_dir = iso_read_bb(sue->data.CL.child_loc, 4, NULL); if (relocated_dir == 0) { ret = iso_rr_msg_submit(fsdata, 6, ISO_WRONG_RR, 0, "Invalid CL entry, no child location"); break; } } else if (SUSP_SIG(sue, 'P', 'N')) { ret = read_rr_PN(sue, &atts); if (ret < 0) { /* notify and continue */ ret = iso_rr_msg_submit(fsdata, 7, ISO_WRONG_RR_WARN, ret, "Invalid PN entry"); } } else if (SUSP_SIG(sue, 'S', 'F')) { ret = iso_rr_msg_submit(fsdata, 8, ISO_UNSUPPORTED_RR, 0, "Sparse files not supported."); break; } else if (SUSP_SIG(sue, 'R', 'R')) { /* This was an optional flag byte in RRIP 1.09 which told the reader what other RRIP fields to expect. mkisofs emits it. We don't. */ continue; } else if (SUSP_SIG(sue, 'S', 'P')) { /* * Ignore this, to prevent the hint message, if we are dealing * with root node (SP is only valid in "." of root node) */ if (!(flag & 1)) { /* notify and continue */ ret = iso_rr_msg_submit(fsdata, 9, ISO_WRONG_RR, 0, "SP entry found in a directory entry other " "than '.' entry of root node"); } continue; } else if (SUSP_SIG(sue, 'E', 'R')) { /* * Ignore this, to prevent the hint message, if we are dealing * with root node (ER is only valid in "." of root node) */ if (!(flag & 1)) { /* notify and continue */ ret = iso_rr_msg_submit(fsdata, 10, ISO_WRONG_RR, 0, "ER entry found in a directory entry other " "than '.' entry of root node"); } continue; /* Need to read AA resp. AL in any case so it is available for S_IRWXG mapping in case that fsdata->aaip_load != 1 */ } else if (SUSP_SIG(sue, 'A', 'A')) { ret = read_aaip_AA(sue, &aa_string, &aa_size, &aa_len, &prev_field, &aa_done, 0); if (ret < 0) { /* notify and continue */ ret = iso_rr_msg_submit(fsdata, 11, ISO_WRONG_RR_WARN, ret, "Invalid AA entry"); continue; } } else if (SUSP_SIG(sue, 'A', 'L')) { ret = read_aaip_AL(sue, &aa_string, &aa_size, &aa_len, &prev_field, &aa_done, 0); if (ret < 0) { /* notify and continue */ ret = iso_rr_msg_submit(fsdata, 12, ISO_WRONG_RR_WARN, ret, "Invalid AL entry"); continue; } #ifdef Libisofs_with_zliB } else if (SUSP_SIG(sue, 'Z', 'F') || SUSP_SIG(sue, 'Z', '2')) { ret = read_zisofs_ZF(sue, zisofs_alg, &zisofs_hs4, &zisofs_bsl2, &zisofs_usize, 0); if (ret < 0) { invalid_zf: /* notify and continue */ ret = iso_rr_msg_submit(fsdata, 13, ISO_WRONG_RR_WARN, ret, SUSP_SIG(sue, 'Z', 'F') ? "Invalid ZF entry" : "Invalid Z2 entry"); zisofs_hs4 = 0; continue; } if (zisofs_alg[0] == 'p' || zisofs_alg[1] == 'z') { if (sue->version[0] != 1) goto invalid_zf; } else if (zisofs_alg[0] == 'P' || zisofs_alg[1] == 'Z') { if (sue->version[0] != 2) goto invalid_zf; } else { ret = 0; goto invalid_zf; } #endif /* Libisofs_with_zliB */ /* This message is inflationary */ /* } else { ret = iso_msg_submit(fsdata->msgid, ISO_SUSP_UNHANDLED, 0, "Unhandled SUSP entry %c%c.", sue->sig[0], sue->sig[1]); */ } } susp_iter_free(iter); /* check for RR problems */ if (ret < 0) { /* error was already submitted above */ iso_msg_debug(fsdata->msgid, "Error parsing RR entries"); } else if (!relocated_dir && atts.st_mode == (mode_t) 0 ) { ret = iso_rr_msg_submit(fsdata, 14, ISO_WRONG_RR, 0, "Mandatory " "Rock Ridge PX entry is not present or it " "contains invalid values."); } else { /* ensure both name and link dest are finished */ if (namecont != 0) { ret = iso_rr_msg_submit(fsdata, 15, ISO_WRONG_RR, 0, "Incomplete Rock Ridge name, last NM entry continues"); } if (linkdestcont != 0) { ret = iso_rr_msg_submit(fsdata, 16, ISO_WRONG_RR, 0, "Incomplete link destination, last SL entry continues"); } } if (ret < 0) { free(name); goto ex; } /* convert name to needed charset */ if (strcmp(fsdata->input_charset, fsdata->local_charset) && name) { /* we need to convert name charset */ char *newname = NULL; ret = strconv(name, fsdata->input_charset, fsdata->local_charset, &newname); if (ret < 0) { /* its just a hint message */ LIBISO_FREE_MEM(msg); LIBISO_ALLOC_MEM(msg, char, 160); sprintf(msg, "Cannot convert from charset %.40s to %.40s", fsdata->input_charset, fsdata->local_charset); ret = iso_rr_msg_submit(fsdata, 17, ISO_FILENAME_WRONG_CHARSET, ret, msg); free(newname); if (ret < 0) { free(name); goto ex; } } else { free(name); name = newname; } } /* convert link destination to needed charset */ if (strcmp(fsdata->input_charset, fsdata->local_charset) && linkdest) { /* we need to convert name charset */ char *newlinkdest = NULL; ret = strconv(linkdest, fsdata->input_charset, fsdata->local_charset, &newlinkdest); if (ret < 0) { LIBISO_FREE_MEM(msg); LIBISO_ALLOC_MEM(msg, char, 160); sprintf(msg, "Charset conversion error. Cannot convert from %.40s to %.40s", fsdata->input_charset, fsdata->local_charset); ret = iso_rr_msg_submit(fsdata, 17, ISO_FILENAME_WRONG_CHARSET, ret, msg); free(newlinkdest); if (ret < 0) { free(name); goto ex; } } else { free(linkdest); linkdest = newlinkdest; } } } else { /* RR extensions are not read / used */ atts.st_gid = fsdata->gid; atts.st_uid = fsdata->uid; if (record->flags[0] & 0x02) { atts.st_mode = S_IFDIR | fsdata->dir_mode; } else { atts.st_mode = S_IFREG | fsdata->file_mode; } } if (!has_px) { fsdata->px_ino_status |= 4; } /* * if we haven't RR extensions, or no NM entry is present, * we use the name in directory record */ if (!name) { size_t len; if (record->len_fi[0] == 1 && record->file_id[0] == 0) { /* "." entry, we can call this for root node, so... */ if (!(atts.st_mode & S_IFDIR)) { ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_ECMA119, 0, "Wrong ISO file name. \".\" not dir"); goto ex; } } else { name = get_name(fsdata, (char*)record->file_id, record->len_fi[0]); if (name == NULL) { ret = iso_msg_submit(fsdata->msgid, ISO_WRONG_ECMA119, 0, "Cannot retrieve file name"); goto ex; } /* remove trailing version number */ len = strlen(name); if (fsdata->iso_root_block == fsdata->svd_root_block) ecma119_map = fsdata->joliet_map; else ecma119_map = fsdata->ecma119_map; if (ecma119_map >= 1 && ecma119_map <= 3 && len > 2 && name[len-2] == ';' && name[len-1] == '1') { if (len > 3 && name[len-3] == '.') { /* * the "." is mandatory, so in most cases is included only * for standard compliance */ name[len-3] = '\0'; } else { name[len-2] = '\0'; } } if (ecma119_map == 2 || ecma119_map == 3) { for (cpt = name; *cpt != 0; cpt++) { if (ecma119_map == 2) { if (islower(*cpt)) *cpt = toupper(*cpt); } else { if (isupper(*cpt)) *cpt = tolower(*cpt); } } } } } if (name != NULL) { if ((int) strlen(name) > fsdata->truncate_length) { ret = iso_truncate_rr_name(fsdata->truncate_mode, fsdata->truncate_length, name, 0); if (ret < 0) goto ex; } } if (relocated_dir) { /* * We are dealing with a placeholder for a relocated dir. * Thus, we need to read attributes for this directory from the "." * entry of the relocated dir. */ LIBISO_ALLOC_MEM(buffer, uint8_t, BLOCK_SIZE); ret = fsdata->src->read_block(fsdata->src, relocated_dir, buffer); if (ret < 0) { goto ex; } /* Call with flag bit1 to prevent further CL relocation */ ret = iso_file_source_new_ifs(fs, parent, (struct ecma119_dir_record*) buffer, src, flag | 2); if (ret <= 0) { goto ex; } /* but the real name is the name of the placeholder */ ifsdata = (ImageFileSourceData*) (*src)->data; if (ifsdata->name != NULL) free(ifsdata->name); ifsdata->name = name; {ret = ISO_SUCCESS; goto ex;} } /* Production of missing inode numbers is delayed until the image is complete. Then all nodes which shall get a new inode number will be served. */ /* * if we haven't RR extensions, or a needed TF time stamp is not present, * we use plain iso recording time */ recorded = iso_datetime_read_7(record->recording_time); if (atts.st_atime == (time_t) 0) { atts.st_atime = recorded; } if (atts.st_ctime == (time_t) 0) { atts.st_ctime = recorded; } if (atts.st_mtime == (time_t) 0) { atts.st_mtime = recorded; } /* the size is read from iso directory record */ atts.st_size = iso_read_bb(record->length, 4, NULL); /* Fill last entries */ atts.st_dev = fsdata->id; atts.st_blksize = BLOCK_SIZE; atts.st_blocks = DIV_UP(atts.st_size, BLOCK_SIZE); /* TODO #00014 : more sanity checks to ensure dir record info is valid */ if (S_ISLNK(atts.st_mode) && (linkdest == NULL)) { ret = iso_rr_msg_submit(fsdata, 18, ISO_WRONG_RR, 0, "Link without destination."); free(name); goto ex; } /* ok, we can now create the file source */ if (*src == NULL) { ifsdata = calloc(1, sizeof(ImageFileSourceData)); if (ifsdata == NULL) { ret = ISO_OUT_OF_MEM; goto ifs_cleanup; } ifsrc = calloc(1, sizeof(IsoFileSource)); if (ifsrc == NULL) { ret = ISO_OUT_OF_MEM; goto ifs_cleanup; } } else { ifsdata = (*src)->data; ifsrc = (*src); free(ifsdata->name); /* we will assign a new one */ ifsdata->name = NULL; atts.st_size += (off_t)ifsdata->info.st_size; if (ifsdata->aa_string != NULL) free(ifsdata->aa_string); ifsdata->aa_string = NULL; } /* fill data */ ifsdata->fs = fs; iso_filesystem_ref(fs); if (parent != NULL) { ifsdata->parent = parent; iso_file_source_ref(parent); } ifsdata->info = atts; ifsdata->name = name; ifsdata->aa_string = aa_string; #ifdef Libisofs_with_zliB if (zisofs_hs4 > 0) { ifsdata->zisofs_algo[0] = zisofs_alg[0]; ifsdata->zisofs_algo[1] = zisofs_alg[1]; ifsdata->header_size_div4 = zisofs_hs4; ifsdata->block_size_log2 = zisofs_bsl2; ifsdata->uncompressed_size = zisofs_usize; } else { ifsdata->header_size_div4 = 0; } #endif /* save extents */ ifsdata->sections = realloc(ifsdata->sections, (1 + ifsdata->nsections) * sizeof(struct iso_file_section)); if (ifsdata->sections == NULL) { free(ifsdata->name); ret = ISO_OUT_OF_MEM; goto ifs_cleanup; } ifsdata->sections[ifsdata->nsections].block = iso_read_bb(record->block, 4, NULL) + record->len_xa[0]; ifsdata->sections[ifsdata->nsections].size = iso_read_bb(record->length, 4, NULL); ifsdata->nsections++; if (S_ISLNK(atts.st_mode)) { ifsdata->data.content = linkdest; } else if (linkdest != NULL) { ret = iso_rr_msg_submit(fsdata, 19, ISO_WRONG_RR_WARN, 0, "RRIP SL link destination with file that is not a link."); free(linkdest); linkdest = NULL; } ifsrc->class = &ifs_class; ifsrc->data = ifsdata; ifsrc->refcount = 1; *src = ifsrc; {ret = ISO_SUCCESS; goto ex;} ifs_cleanup: ; free(name); free(linkdest); free(ifsdata); free(ifsrc); ex:; LIBISO_FREE_MEM(msg); LIBISO_FREE_MEM(buffer); return ret; } static int ifs_get_root(IsoFilesystem *fs, IsoFileSource **root) { int ret; _ImageFsData *data; uint8_t *buffer = NULL; if (fs == NULL || fs->data == NULL || root == NULL) { ret = ISO_NULL_POINTER; goto ex; } LIBISO_ALLOC_MEM(buffer, uint8_t, BLOCK_SIZE); data = (_ImageFsData*)fs->data; /* open the filesystem */ ret = ifs_fs_open((IsoImageFilesystem*)fs); if (ret < 0) { goto ex; } /* read extend for root record */ ret = data->src->read_block(data->src, data->iso_root_block, buffer); if (ret < 0) { ifs_fs_close((IsoImageFilesystem*)fs); goto ex; } /* get root attributes from "." entry */ *root = NULL; ret = iso_file_source_new_ifs((IsoImageFilesystem*)fs, NULL, (struct ecma119_dir_record*) buffer, root, 1); ifs_fs_close((IsoImageFilesystem*)fs); ex:; LIBISO_FREE_MEM(buffer); return ret; } /** * Find a file inside a node. * * @param file * it is not modified if requested file is not found * @return * 1 success, 0 not found, < 0 error */ static int ifs_get_file(IsoFileSource *dir, const char *name, IsoFileSource **file) { int ret; IsoFileSource *src; ret = iso_file_source_open(dir); if (ret < 0) { return ret; } while ((ret = iso_file_source_readdir(dir, &src)) == 1) { char *fname = iso_file_source_get_name(src); if (!strcmp(name, fname)) { free(fname); *file = src; ret = ISO_SUCCESS; break; } free(fname); iso_file_source_unref(src); } iso_file_source_close(dir); return ret; } static int ifs_get_by_path(IsoFilesystem *fs, const char *path, IsoFileSource **file) { int ret; IsoFileSource *src = NULL; char *ptr, *brk_info, *component; if (fs == NULL || fs->data == NULL || path == NULL || file == NULL) { return ISO_NULL_POINTER; } if (path[0] != '/') { /* only absolute paths supported */ return ISO_FILE_BAD_PATH; } /* open the filesystem */ ret = ifs_fs_open((IsoImageFilesystem*)fs); if (ret < 0) { return ret; } ret = ifs_get_root(fs, &src); if (ret < 0) { return ret; } if (!strcmp(path, "/")) { /* we are looking for root */ *file = src; ret = ISO_SUCCESS; goto get_path_exit; } ptr = strdup(path); if (ptr == NULL) { iso_file_source_unref(src); ret = ISO_OUT_OF_MEM; goto get_path_exit; } component = strtok_r(ptr, "/", &brk_info); while (component) { IsoFileSource *child = NULL; ImageFileSourceData *fdata; fdata = src->data; if (!S_ISDIR(fdata->info.st_mode)) { ret = ISO_FILE_BAD_PATH; break; } ret = ifs_get_file(src, component, &child); iso_file_source_unref(src); src = NULL; if (ret <= 0) { break; } src = child; component = strtok_r(NULL, "/", &brk_info); } free(ptr); if (ret < 0) { if (src != NULL) iso_file_source_unref(src); } else if (ret == 0) { ret = ISO_FILE_DOESNT_EXIST; } else { *file = src; } get_path_exit:; ifs_fs_close((IsoImageFilesystem*)fs); return ret; } unsigned int ifs_get_id(IsoFilesystem *fs) { return ISO_IMAGE_FS_ID; } static int ifs_fs_open(IsoImageFilesystem *fs) { _ImageFsData *data; if (fs == NULL || fs->data == NULL) { return ISO_NULL_POINTER; } data = (_ImageFsData*)fs->data; if (data->open_count == 0) { /* we need to actually open the data source */ int res = data->src->open(data->src); if (res < 0) { return res; } } ++data->open_count; return ISO_SUCCESS; } static int ifs_fs_close(IsoImageFilesystem *fs) { _ImageFsData *data; if (fs == NULL || fs->data == NULL) { return ISO_NULL_POINTER; } data = (_ImageFsData*)fs->data; if (--data->open_count == 0) { /* we need to actually close the data source */ return data->src->close(data->src); } return ISO_SUCCESS; } static void ifs_fs_free(IsoFilesystem *fs) { _ImageFsData *data; data = (_ImageFsData*) fs->data; /* close data source if already opened */ if (data->open_count > 0) { data->src->close(data->src); } /* free our ref to datasource */ iso_data_source_unref(data->src); /* free volume atts */ free(data->volset_id); free(data->volume_id); free(data->publisher_id); free(data->data_preparer_id); free(data->system_id); free(data->application_id); free(data->copyright_file_id); free(data->abstract_file_id); free(data->biblio_file_id); free(data->creation_time); free(data->modification_time); free(data->expiration_time); free(data->effective_time); free(data->input_charset); free(data->local_charset); if(data->catcontent != NULL) free(data->catcontent); free(data); } /** * Read the SUSP system user entries of the "." entry of the root directory, * identifying when Rock Ridge extensions are being used. * * @return * 1 success, 0 ignored, < 0 error */ static int read_root_susp_entries(_ImageFsData *data, uint32_t block) { int ret; unsigned char *buffer = NULL; struct ecma119_dir_record *record; struct susp_sys_user_entry *sue; SuspIterator *iter; LIBISO_ALLOC_MEM(buffer, unsigned char, 2048); ret = data->src->read_block(data->src, block, buffer); if (ret < 0) { goto ex; } /* record will be the "." directory entry for the root record */ record = (struct ecma119_dir_record *)buffer; #ifdef Libisofs_syslinux_tesT { struct device syslinux_dev; struct iso_sb_info syslinux_sbi; struct fs_info syslinux_fsi; syslinux_dev.src = data->src; memcpy(&(syslinux_sbi.root), (char *) record, 256); syslinux_sbi.do_rr = 1; syslinux_sbi.susp_skip = 0; syslinux_fsi.fs_dev = &syslinux_dev; syslinux_fsi.fs_info = &syslinux_sbi; ret = susp_rr_check_signatures(&syslinux_fsi, 1); fprintf(stderr, "--------- susp_rr_check_signatures == %d , syslinux_sbi.do_rr == %d\n", ret, syslinux_sbi.do_rr); } #endif /* Libisofs_syslinux_tesT */ /* * TODO #00015 : take care of CD-ROM XA discs when reading SP entry * SUSP specification claims that for CD-ROM XA the SP entry * is not at position BP 1, but at BP 15. Is that used? * In that case, we need to set info->len_skp to 15!! */ iter = susp_iter_new(data->src, record, data->session_lba + data->nblocks, data->len_skp, data->msgid); if (iter == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } /* first entry must be an SP system use entry */ ret = susp_iter_next(iter, &sue, 1); if (ret < 0) { /* error */ susp_iter_free(iter); goto ex; } else if (ret == 0 || !SUSP_SIG(sue, 'S', 'P') ) { iso_msg_debug(data->msgid, "SUSP/RR is not being used."); susp_iter_free(iter); {ret = ISO_SUCCESS; goto ex;} } /* it is a SP system use entry */ if (sue->version[0] != 1 || sue->data.SP.be[0] != 0xBE || sue->data.SP.ef[0] != 0xEF) { susp_iter_free(iter); ret = iso_msg_submit(data->msgid, ISO_UNSUPPORTED_SUSP, 0, "SUSP SP system use entry seems to be wrong. " "Ignoring Rock Ridge Extensions."); goto ex; } iso_msg_debug(data->msgid, "SUSP/RR is being used."); /* * The LEN_SKP field, defined in IEEE 1281, SUSP. 5.3, specifies the * number of bytes to be skipped within each System Use field. * I think this will be always 0, but given that support this standard * feature is easy... */ data->len_skp = sue->data.SP.len_skp[0]; /* * Ok, now search for ER entry. * Just notice that the attributes for root dir are read elsewhere. * * TODO #00016 : handle non RR ER entries * * if several ER are present, we need to identify the position of * what refers to RR, and then look for corresponding ES entry in * each directory record. I have not implemented this (it's not used, * no?), but if we finally need it, it can be easily implemented in * the iterator, transparently for the rest of the code. */ while ((ret = susp_iter_next(iter, &sue, 0)) > 0) { /* ignore entries from different version */ if (sue->version[0] != 1) continue; if (SUSP_SIG(sue, 'E', 'R')) { /* * it seems that Rock Ridge can be identified with any * of the following */ if ( sue->data.ER.len_id[0] == 10 && !strncmp((char*)sue->data.ER.ext_id, "RRIP_1991A", 10) ) { iso_msg_debug(data->msgid, "Suitable Rock Ridge ER found. Version 1.10."); data->rr_version = RR_EXT_110; } else if ( (sue->data.ER.len_id[0] == 10 && !strncmp((char*)sue->data.ER.ext_id, "IEEE_P1282", 10)) || (sue->data.ER.len_id[0] == 9 && !strncmp((char*)sue->data.ER.ext_id, "IEEE_1282", 9)) ) { iso_msg_debug(data->msgid, "Suitable Rock Ridge ER found. Version 1.12."); data->rr_version = RR_EXT_112; } else if (sue->data.ER.len_id[0] == 9 && (strncmp((char*)sue->data.ER.ext_id, "AAIP_0002", 9) == 0 || strncmp((char*)sue->data.ER.ext_id, "AAIP_0100", 9) == 0 || strncmp((char*)sue->data.ER.ext_id, "AAIP_0200", 9) == 0)) { /* Tolerate AAIP ER even if not supported */ iso_msg_debug(data->msgid, "Suitable AAIP ER found."); if (strncmp((char*)sue->data.ER.ext_id, "AAIP_0200", 9) == 0) data->aaip_version = 200; else if (((char*)sue->data.ER.ext_id)[6] == '1') data->aaip_version = 100; else data->aaip_version = 2; if (!data->aaip_load) iso_msg_submit(data->msgid, ISO_AAIP_IGNORED, 0, "Identifier for extension AAIP found, but loading is not enabled."); } else { ret = iso_msg_submit(data->msgid, ISO_SUSP_MULTIPLE_ER, 0, "Unknown Extension Signature found in ER.\n" "It will be ignored, but can cause problems in " "image reading. Please notify us about this."); if (ret < 0) { break; } } } } susp_iter_free(iter); if (ret < 0) { goto ex; } ret = ISO_SUCCESS; ex: LIBISO_FREE_MEM(buffer); return ret; } static int read_pvd_block(IsoDataSource *src, uint32_t block, uint8_t *buffer, uint32_t *image_size) { int ret; struct ecma119_pri_vol_desc *pvm; ret = src->read_block(src, block, buffer); if (ret < 0) return ret; pvm = (struct ecma119_pri_vol_desc *)buffer; /* sanity checks */ if (pvm->vol_desc_type[0] != 1 || pvm->vol_desc_version[0] != 1 || strncmp((char*)pvm->std_identifier, "CD001", 5) || pvm->file_structure_version[0] != 1) { return ISO_WRONG_PVD; } if (image_size != NULL) *image_size = iso_read_bb(pvm->vol_space_size, 4, NULL); return ISO_SUCCESS; } static int read_pvm(_ImageFsData *data, uint32_t block) { int ret; struct ecma119_pri_vol_desc *pvm; struct ecma119_dir_record *rootdr; uint8_t *buffer = NULL; LIBISO_ALLOC_MEM(buffer, uint8_t, BLOCK_SIZE); ret = read_pvd_block(data->src, block, buffer, NULL); if (ret < 0) goto ex; /* ok, it is a valid PVD */ pvm = (struct ecma119_pri_vol_desc *)buffer; /* fill volume attributes */ /* TODO take care of input charset */ data->volset_id = iso_util_strcopy_untail((char*)pvm->vol_set_id, 128); data->volume_id = iso_util_strcopy_untail((char*)pvm->volume_id, 32); data->publisher_id = iso_util_strcopy_untail((char*)pvm->publisher_id, 128); data->data_preparer_id = iso_util_strcopy_untail((char*)pvm->data_prep_id, 128); data->system_id = iso_util_strcopy_untail((char*)pvm->system_id, 32); data->application_id = iso_util_strcopy_untail((char*)pvm->application_id, 128); data->copyright_file_id = iso_util_strcopy_untail((char*) pvm->copyright_file_id, 37); data->abstract_file_id = iso_util_strcopy_untail((char*) pvm->abstract_file_id, 37); data->biblio_file_id = iso_util_strcopy_untail((char*) pvm->bibliographic_file_id, 37); if (data->copyright_file_id[0] == '_' && data->copyright_file_id[1] == 0 && data->abstract_file_id[0] == '_' && data->abstract_file_id[1] == 0 && data->biblio_file_id[0] == '_' && data->biblio_file_id[1] == 0) { /* This is bug output from libisofs <= 0.6.23 . The texts mean file names and should have been empty to indicate that there are no such files. It is obvious that not all three roles can be fulfilled by one file "_" so that one cannot spoil anything by assuming them empty now. */ data->copyright_file_id[0] = 0; data->abstract_file_id[0] = 0; data->biblio_file_id[0] = 0; } data->creation_time = iso_util_strcopy_untail((char*) pvm->vol_creation_time, 17); data->modification_time = iso_util_strcopy_untail((char*) pvm->vol_modification_time, 17); data->expiration_time = iso_util_strcopy_untail((char*) pvm->vol_expiration_time, 17); data->effective_time = iso_util_strcopy_untail((char*) pvm->vol_effective_time, 17); data->session_lba = 0; if (block >= 16) /* The session begins 16 blocks before the PVD */ data->session_lba = block - 16; data->nblocks = iso_read_bb(pvm->vol_space_size, 4, NULL); rootdr = (struct ecma119_dir_record*) pvm->root_dir_record; data->pvd_root_block = iso_read_bb(rootdr->block, 4, NULL) + rootdr->len_xa[0]; /* * TODO #00017 : take advantage of other atts of PVD * PVD has other things that could be interesting, but that don't have a * member in IsoImage, such as creation date. In a multisession disc, we * could keep the creation date and update the modification date, for * example. */ ret = ISO_SUCCESS; ex:; LIBISO_FREE_MEM(buffer); return ret; } /** * @return * 1 success, 0 ignored, < 0 error */ static int read_el_torito_boot_catalog(_ImageFsData *data, uint32_t block) { int ret, i, rx, last_done, idx, bufsize; struct el_torito_validation_entry *ve; struct el_torito_section_header *sh; struct el_torito_section_entry *entry; /* also usable as default_entry */ unsigned char *buffer = NULL, *rpt; LIBISO_ALLOC_MEM(buffer, unsigned char, BLOCK_SIZE); data->num_bootimgs = 0; data->catsize = 0; ret = data->src->read_block(data->src, block, buffer); if (ret < 0) { goto ex; } ve = (struct el_torito_validation_entry*)buffer; /* check if it is a valid catalog (TODO: check also the checksum)*/ if ( (ve->header_id[0] != 1) || (ve->key_byte1[0] != 0x55) || (ve->key_byte2[0] != 0xAA) ) { iso_msg_submit(data->msgid, ISO_WRONG_EL_TORITO, 0, "Wrong or damaged El-Torito Catalog. El-Torito info " "will be ignored."); {ret = ISO_WRONG_EL_TORITO; goto ex;} } /* ok, once we are here we assume it is a valid catalog */ /* parse the default entry */ entry = (struct el_torito_section_entry *)(buffer + 32); data->eltorito = 1; /* The Default Entry is declared mandatory */ data->catsize = 64; data->num_bootimgs = 1; data->platform_ids[0] = ve->platform_id[0]; memcpy(data->id_strings[0], ve->id_string, 24); memset(data->id_strings[0] + 24, 0, 4); data->boot_flags[0] = entry->boot_indicator[0] ? 1 : 0; data->media_types[0] = entry->boot_media_type[0]; data->partition_types[0] = entry->system_type[0]; data->load_segs[0] = iso_read_lsb(entry->load_seg, 2); data->load_sizes[0] = iso_read_lsb(entry->sec_count, 2); data->bootblocks[0] = iso_read_lsb(entry->block, 4); /* The Default Entry has no selection criterion */ memset(data->selection_crits[0], 0, 20); /* Read eventual more entries from the boot catalog */ last_done = 0; for (rx = 64; (buffer[rx] & 0xfe) == 0x90 && !last_done; rx += 32) { last_done = buffer[rx] & 1; /* Read Section Header */ /* >>> ts B10703 : load a new buffer if needed */; sh = (struct el_torito_section_header *) (buffer + rx); data->catsize += 32; for (i = 0; i < sh->num_entries[0]; i++) { rx += 32; data->catsize += 32; /* >>> ts B10703 : load a new buffer if needed */; if (data->num_bootimgs >= Libisofs_max_boot_imageS) { /* >>> ts B10703 : need to continue rather than abort */; iso_msg_submit(data->msgid, ISO_EL_TORITO_WARN, 0, "Too many boot images found. List truncated."); goto after_bootblocks; } /* Read bootblock from section entry */ entry = (struct el_torito_section_entry *)(buffer + rx); idx = data->num_bootimgs; data->platform_ids[idx] = sh->platform_id[0]; memcpy(data->id_strings[idx], sh->id_string, 28); data->boot_flags[idx] = entry->boot_indicator[0] ? 1 : 0; data->media_types[idx] = entry->boot_media_type[0]; data->partition_types[idx] = entry->system_type[0]; data->load_segs[idx] = iso_read_lsb(entry->load_seg, 2); data->load_sizes[idx] = iso_read_lsb(entry->sec_count, 2); data->bootblocks[idx] = iso_read_lsb(entry->block, 4); data->selection_crits[idx][0] = entry->selec_criteria[0]; memcpy(data->selection_crits[idx] + 1, entry->vendor_sc, 19); data->num_bootimgs++; } } after_bootblocks:; if(data->catsize > 0) { if(data->catcontent != NULL) free(data->catcontent); if(data->catsize > 10 * BLOCK_SIZE) data->catsize = 10 * BLOCK_SIZE; bufsize = data->catsize; if (bufsize % BLOCK_SIZE) bufsize += BLOCK_SIZE - (bufsize % BLOCK_SIZE); data->catcontent = calloc(bufsize , 1); if(data->catcontent == NULL) { data->catsize = 0; ret = ISO_OUT_OF_MEM; goto ex; } for(rx = 0; rx < bufsize; rx += BLOCK_SIZE) { rpt = (unsigned char *) (data->catcontent + rx); ret = data->src->read_block(data->src, block + rx / BLOCK_SIZE, rpt); if (ret < 0) goto ex; } } ret = ISO_SUCCESS; ex:; LIBISO_FREE_MEM(buffer); return ret; } /* @return 1= ok, checked, go on with loading 2= no checksum tags found, go on with loading <0= libisofs error especially ISO_SB_TREE_CORRUPTED */ static int iso_src_check_sb_tree(IsoDataSource *src, uint32_t start_lba, int flag) { int tag_type, ret; char *block = NULL, md5[16]; int desired = (1 << 2); void *ctx = NULL; uint32_t next_tag = 0, i; LIBISO_ALLOC_MEM(block, char, 2048); ret = iso_md5_start(&ctx); if (ret < 0) goto ex; if (start_lba == 0) desired |= (1 << 4); for (i = 0; i < 32; i++) { ret = src->read_block(src, start_lba + i, (uint8_t *) block); if (ret < 0) goto ex; ret = 0; if (i >= 16) ret = iso_util_eval_md5_tag(block, desired, start_lba + i, ctx, start_lba, &tag_type, &next_tag, 0); iso_md5_compute(ctx, block, 2048); if (ret == (int) ISO_MD5_TAG_COPIED) {/* growing without emulated TOC */ ret = 2; goto ex; } if (ret == (int) ISO_MD5_AREA_CORRUPTED || ret == (int) ISO_MD5_TAG_MISMATCH) ret = ISO_SB_TREE_CORRUPTED; if (ret < 0) goto ex; if (ret == 1) break; } if (i >= 32) { ret = 2; goto ex; } if (tag_type == 4) { /* Relocated Superblock: restart checking at real session start */ if (next_tag < 32) { /* Non plausible session_start address */ ret = ISO_SB_TREE_CORRUPTED; iso_msg_submit(-1, ret, 0, NULL); goto ex; } /* Check real session */ ret = iso_src_check_sb_tree(src, next_tag, 0); goto ex; } /* Go on with tree */ for (i++; start_lba + i <= next_tag; i++) { ret = src->read_block(src, start_lba + i, (uint8_t *) block); if (ret < 0) goto ex; if (start_lba + i < next_tag) iso_md5_compute(ctx, block, 2048); } ret = iso_util_eval_md5_tag(block, (1 << 3), start_lba + i - 1, ctx, start_lba, &tag_type, &next_tag, 0); if (ret == (int) ISO_MD5_AREA_CORRUPTED || ret == (int) ISO_MD5_TAG_MISMATCH) ret = ISO_SB_TREE_CORRUPTED; if (ret < 0) goto ex; ret = 1; ex: if (ctx != NULL) iso_md5_end(&ctx, md5); LIBISO_FREE_MEM(block); return ret; } int iso_image_filesystem_new(IsoDataSource *src, struct iso_read_opts *opts, int msgid, IsoImageFilesystem **fs) { int ret, i; uint32_t block; IsoImageFilesystem *ifs; _ImageFsData *data; uint8_t *buffer = NULL; if (src == NULL || opts == NULL || fs == NULL) { ret = ISO_NULL_POINTER; goto ex; } LIBISO_ALLOC_MEM(buffer, uint8_t, BLOCK_SIZE); data = calloc(1, sizeof(_ImageFsData)); if (data == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } ifs = calloc(1, sizeof(IsoImageFilesystem)); if (ifs == NULL) { free(data); {ret = ISO_OUT_OF_MEM; goto ex;} } data->rr = RR_EXT_NO; /* get our ref to IsoDataSource */ data->src = src; iso_data_source_ref(src); data->open_count = 0; data->catcontent = NULL; /* get an id for the filesystem */ data->id = ++fs_dev_id; /* fill data from opts */ data->gid = opts->gid; data->uid = opts->uid; data->file_mode = opts->file_mode & ~S_IFMT; data->dir_mode = opts->dir_mode & ~S_IFMT; data->msgid = msgid; data->aaip_load = !opts->noaaip; if (opts->nomd5 == 0) data->md5_load = 1; else if (opts->nomd5 == 2) data->md5_load = 2; else data->md5_load = 0; data->md5_checked = 0; data->aaip_version = -1; data->make_new_ino = opts->make_new_ino; data->num_bootimgs = 0; for (i = 0; i < Libisofs_max_boot_imageS; i++) data->bootblocks[i] = 0; data->inode_counter = 0; data->px_ino_status = 0; data->rr_err_reported = 0; data->rr_err_repeated = 0; data->joliet_ucs2_failures = 0; data->local_charset = strdup(iso_get_local_charset(0)); if (data->local_charset == NULL) { ret = ISO_OUT_OF_MEM; LIBISO_FREE_MEM(data); data = NULL; goto fs_cleanup; } memcpy(ifs->type, "iso ", 4); ifs->data = data; ifs->refcount = 1; ifs->version = 0; ifs->get_root = ifs_get_root; ifs->get_by_path = ifs_get_by_path; ifs->get_id = ifs_get_id; ifs->open = ifs_fs_open; ifs->close = ifs_fs_close; ifs->free = ifs_fs_free; /* read Volume Descriptors and ensure it is a valid image */ if (data->md5_load == 1) { /* From opts->block on : check for superblock and tree tags */; ret = iso_src_check_sb_tree(src, opts->block, 0); if (ret < 0) { iso_msgs_submit(0, "Image loading aborted due to MD5 mismatch of image tree data", 0, "FAILURE", 0); iso_msgs_submit(0, "You may override this refusal by disabling MD5 checking", 0, "HINT", 0); goto fs_cleanup; } if (ret == 1) data->md5_checked = 3; } /* 1. first, open the filesystem */ ifs_fs_open(ifs); /* 2. read primary volume description */ ret = read_pvm(data, opts->block + 16); if (ret < 0) { goto fs_cleanup; } /* 3. read next volume descriptors */ block = opts->block + 17; do { ret = src->read_block(src, block, buffer); if (ret < 0) { /* cleanup and exit */ goto fs_cleanup; } switch (buffer[0]) { case 0: /* boot record */ { struct ecma119_boot_rec_vol_desc *vol; vol = (struct ecma119_boot_rec_vol_desc*)buffer; /* some sanity checks */ if (strncmp((char*)vol->std_identifier, "CD001", 5) || vol->vol_desc_version[0] != 1 || strncmp((char*)vol->boot_sys_id, "EL TORITO SPECIFICATION", 23)) { iso_msg_submit(data->msgid, ISO_UNSUPPORTED_EL_TORITO, 0, "Unsupported Boot Vol. Desc. Only El-Torito " "Specification, Version 1.0 Volume " "Descriptors are supported. Ignoring boot info"); } else { data->catblock = iso_read_lsb(vol->boot_catalog, 4); ret = read_el_torito_boot_catalog(data, data->catblock); if (ret < 0 && ret != (int) ISO_UNSUPPORTED_EL_TORITO && ret != (int) ISO_WRONG_EL_TORITO) { goto fs_cleanup; } } } break; case 2: /* supplementary volume descritor */ { struct ecma119_sup_vol_desc *sup; struct ecma119_dir_record *root; sup = (struct ecma119_sup_vol_desc*)buffer; if (sup->esc_sequences[0] == 0x25 && sup->esc_sequences[1] == 0x2F && (sup->esc_sequences[2] == 0x40 || sup->esc_sequences[2] == 0x43 || sup->esc_sequences[2] == 0x45) ) { /* it's a Joliet Sup. Vol. Desc. */ iso_msg_debug(data->msgid, "Found Joliet extensions"); data->joliet = 1; root = (struct ecma119_dir_record*)sup->root_dir_record; data->svd_root_block = iso_read_bb(root->block, 4, NULL) + root->len_xa[0]; /* TODO #00019 : set IsoImage attribs from Joliet SVD? */ /* TODO #00020 : handle RR info in Joliet tree */ } else if (sup->vol_desc_version[0] == 2) { /* * It is an Enhanced Volume Descriptor, image is an * ISO 9660:1999 */ iso_msg_debug(data->msgid, "Found ISO 9660:1999"); data->iso1999 = 1; root = (struct ecma119_dir_record*)sup->root_dir_record; data->evd_root_block = iso_read_bb(root->block, 4, NULL) + root->len_xa[0]; /* TODO #00021 : handle RR info in ISO 9660:1999 tree */ } else { ret = iso_msg_submit(data->msgid, ISO_UNSUPPORTED_VD, 0, "Unsupported Sup. Vol. Desc found."); if (ret < 0) { goto fs_cleanup; } } } break; case 255: /* * volume set terminator * ignore, as it's checked in loop end condition */ break; default: iso_msg_submit(data->msgid, ISO_UNSUPPORTED_VD, 0, "Ignoring Volume descriptor %x.", buffer[0]); break; } block++; } while (buffer[0] != 255); /* 4. check if RR extensions are being used */ ret = read_root_susp_entries(data, data->pvd_root_block); if (ret < 0) goto fs_cleanup; if (!opts->norock) data->rr = data->rr_version; /* select what tree to read */ if (data->rr) { /* RR extensions are available */ if (!opts->nojoliet && opts->preferjoliet && data->joliet) { /* if user prefers joliet, that is used */ iso_msg_debug(data->msgid, "Reading Joliet extensions."); /* Although Joliet prescribes UCS-2BE, interpret names by its superset UTF-16BE in order to avoid conversion failures. */ data->input_charset = strdup("UTF-16BE"); data->rr = RR_EXT_NO; data->iso_root_block = data->svd_root_block; } else { /* RR will be used */ iso_msg_debug(data->msgid, "Reading Rock Ridge extensions."); data->iso_root_block = data->pvd_root_block; } } else { /* RR extensions are not available */ if (!opts->nojoliet && data->joliet) { /* joliet will be used */ iso_msg_debug(data->msgid, "Reading Joliet extensions."); data->input_charset = strdup("UTF-16BE"); data->iso_root_block = data->svd_root_block; } else if (!opts->noiso1999 && data->iso1999) { /* we will read ISO 9660:1999 */ iso_msg_debug(data->msgid, "Reading ISO-9660:1999 tree."); data->iso_root_block = data->evd_root_block; } else { /* default to plain iso */ iso_msg_debug(data->msgid, "Reading plain ISO-9660 tree."); data->iso_root_block = data->pvd_root_block; data->input_charset = strdup("ASCII"); } } data->truncate_mode = opts->truncate_mode; data->truncate_length = opts->truncate_length; data->ecma119_map = opts->ecma119_map; data->joliet_map = opts->joliet_map; if (data->input_charset == NULL) { if (opts->input_charset != NULL) { data->input_charset = strdup(opts->input_charset); } else { data->input_charset = strdup(data->local_charset); } } if (data->input_charset == NULL) { ret = ISO_OUT_OF_MEM; goto fs_cleanup; } data->auto_input_charset = opts->auto_input_charset; /* and finally return. Note that we keep the DataSource opened */ *fs = ifs; {ret = ISO_SUCCESS; goto ex;} fs_cleanup: ; ifs_fs_free(ifs); free(ifs); ex:; LIBISO_FREE_MEM(buffer); return ret; } /* Take over aa_string from file source to node or discard it after making the necessary change in node->mode group permissions. node->mode must already be set. */ static int src_aa_to_node(IsoFileSource *src, IsoNode *node, int flag) { int ret; unsigned char *aa_string; ImageFileSourceData *data; _ImageFsData *fsdata; char *a_text = NULL, *d_text = NULL; data = (ImageFileSourceData*)src->data; fsdata = data->fs->data; /* Obtain ownership of eventual AAIP string */ ret = iso_file_source_get_aa_string(src, &aa_string, 1); if (ret != 1 || aa_string == NULL) return 1; if (fsdata->aaip_load == 1) { /* Attach aa_string to node */ ret = iso_node_add_xinfo(node, aaip_xinfo_func, aa_string); if (ret < 0) return ret; } else { /* Look for ACL and perform S_IRWXG mapping */ iso_aa_get_acl_text(aa_string, node->mode, &a_text, &d_text, 16); if (a_text != NULL) aaip_cleanout_st_mode(a_text, &(node->mode), 4 | 16); /* Dispose ACL a_text and d_text */ iso_aa_get_acl_text(aa_string, node->mode, &a_text, &d_text, 1 << 15); /* Dispose aa_string */ aaip_xinfo_func(aa_string, 1); } return 1; } static int image_builder_create_node(IsoNodeBuilder *builder, IsoImage *image, IsoFileSource *src, char *in_name, IsoNode **node) { int ret, idx, to_copy; struct stat info; IsoNode *new = NULL; IsoBoot *bootcat; char *name = NULL; char *dest = NULL; ImageFileSourceData *data; _ImageFsData *fsdata; #ifdef Libisofs_with_zliB /* Intimate friendship with this function in filters/zisofs.c */ int ziso_add_osiz_filter(IsoFile *file, uint8_t zisofs_algo[2], uint8_t header_size_div4, uint8_t block_size_log2, uint64_t uncompressed_size, int flag); #endif /* Libisofs_with_zliB */ if (builder == NULL || src == NULL || node == NULL || src->data == NULL) { ret = ISO_NULL_POINTER; goto ex; } data = (ImageFileSourceData*)src->data; fsdata = data->fs->data; if (in_name == NULL) { name = iso_file_source_get_name(src); } else { name = strdup(in_name); if (name == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } } /* get info about source */ ret = iso_file_source_lstat(src, &info); if (ret < 0) { goto ex; } switch (info.st_mode & S_IFMT) { case S_IFREG: { /* source is a regular file */ /* El-Torito images have only one section */ if (fsdata->eltorito && data->sections[0].block == fsdata->catblock) { if (image->bootcat->node != NULL) { ret = iso_msg_submit(image->id, ISO_EL_TORITO_WARN, 0, "More than one catalog node has been found. " "We can continue, but that could lead to " "problems"); if (ret < 0) goto ex; iso_node_unref((IsoNode*)image->bootcat->node); } /* we create a placeholder for the catalog instead of * a regular file */ new = calloc(1, sizeof(IsoBoot)); if (new == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } bootcat = (IsoBoot *) new; bootcat->lba = data->sections[0].block; bootcat->size = info.st_size; if (bootcat->size > 10 * BLOCK_SIZE) bootcat->size = 10 * BLOCK_SIZE; bootcat->content = NULL; if (bootcat->size > 0) { bootcat->content = calloc(1, bootcat->size); if (bootcat->content == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } to_copy = bootcat->size; if (bootcat->size > fsdata->catsize) to_copy = fsdata->catsize; memcpy(bootcat->content, fsdata->catcontent, to_copy); } /* and set the image node */ image->bootcat->node = bootcat; new->type = LIBISO_BOOT; new->refcount = 1; } else { IsoStream *stream; IsoFile *file; ret = iso_file_source_stream_new(src, &stream); if (ret < 0) goto ex; /* take a ref to the src, as stream has taken our ref */ iso_file_source_ref(src); file = calloc(1, sizeof(IsoFile)); if (file == NULL) { iso_stream_unref(stream); {ret = ISO_OUT_OF_MEM; goto ex;} } /* mark file as from old session */ file->from_old_session = 1; /* * and we set the sort weight based on the block on image, to * improve performance on image modifying. * * This was too obtrusive because it occupied the highest * possible weight ranks: * file->sort_weight = INT_MAX - data->sections[0].block; * * So a try to be more nice and rely on caching with tiles * of at least 16 blocks. This occupies a range within * the interval of 1 to 2 exp 28 = 268,435,456. * (Dividing each number separately saves from integer * rollover problems.) */ file->sort_weight = fsdata->nblocks / 16 - data->sections[0].block / 16 + 1; file->stream = stream; file->node.type = LIBISO_FILE; #ifdef Libisofs_with_zliB if (data->header_size_div4 > 0) { ret = ziso_add_osiz_filter(file, data->zisofs_algo, data->header_size_div4, data->block_size_log2, data->uncompressed_size, 0); if (ret < 0) { iso_stream_unref(stream); goto ex; } } #endif /* Libisofs_with_zliB */ new = (IsoNode*) file; new->refcount = 0; if (data->sections[0].size > 0) { for (idx = 0; idx < fsdata->num_bootimgs; idx++) if (fsdata->eltorito && data->sections[0].block == fsdata->bootblocks[idx]) break; } else { idx = fsdata->num_bootimgs; } if (idx < fsdata->num_bootimgs) { /* it is boot image node */ if (image->bootcat->bootimages[idx]->image != NULL) { /* idx is already occupied, try to find unoccupied one which has the same block address. */ for (; idx < fsdata->num_bootimgs; idx++) if (fsdata->eltorito && data->sections[0].block == fsdata->bootblocks[idx] && image->bootcat->bootimages[idx]->image == NULL) break; } if (idx >= fsdata->num_bootimgs) { ret = iso_msg_submit(image->id, ISO_EL_TORITO_WARN, 0, "More than one ISO node has been found for the same boot image."); if (ret < 0) { iso_stream_unref(stream); goto ex; } } else { /* and set the image node */ image->bootcat->bootimages[idx]->image = file; new->refcount++; } } } } break; case S_IFDIR: { /* source is a directory */ new = calloc(1, sizeof(IsoDir)); if (new == NULL) { {ret = ISO_OUT_OF_MEM; goto ex;} } new->type = LIBISO_DIR; new->refcount = 0; } break; case S_IFLNK: { /* source is a symbolic link */ IsoSymlink *link; LIBISO_ALLOC_MEM(dest, char, LIBISOFS_NODE_PATH_MAX); ret = iso_file_source_readlink(src, dest, LIBISOFS_NODE_PATH_MAX); if (ret < 0) { goto ex; } link = calloc(1, sizeof(IsoSymlink)); if (link == NULL) { {ret = ISO_OUT_OF_MEM; goto ex;} } link->dest = strdup(dest); link->node.type = LIBISO_SYMLINK; link->fs_id = ISO_IMAGE_FS_ID; link->st_dev = info.st_dev; link->st_ino = info.st_ino; new = (IsoNode*) link; new->refcount = 0; } break; case S_IFSOCK: case S_IFBLK: case S_IFCHR: case S_IFIFO: { /* source is an special file */ IsoSpecial *special; special = calloc(1, sizeof(IsoSpecial)); if (special == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } special->dev = info.st_rdev; special->node.type = LIBISO_SPECIAL; special->fs_id = ISO_IMAGE_FS_ID; special->st_dev = info.st_dev; special->st_ino = info.st_ino; new = (IsoNode*) special; new->refcount = 0; } break; default: ret = ISO_BAD_ISO_FILETYPE; goto ex; } /* fill fields */ new->refcount++; new->name = name; name = NULL; new->mode = info.st_mode; new->uid = info.st_uid; new->gid = info.st_gid; new->atime = info.st_atime; new->mtime = info.st_mtime; new->ctime = info.st_ctime; new->hidden = 0; new->parent = NULL; new->next = NULL; ret = src_aa_to_node(src, new, 0); if (ret < 0) { goto ex; } /* Attach ino as xinfo if valid and no IsoStream is involved */ if (info.st_ino != 0 && (info.st_mode & S_IFMT) != S_IFREG && !fsdata->make_new_ino) { ret = iso_node_set_ino(new, info.st_ino, 0); if (ret < 0) goto ex; } *node = new; new = NULL; {ret = ISO_SUCCESS; goto ex;} ex:; if (name != NULL) free(name); if (new != NULL) iso_node_unref(new); LIBISO_FREE_MEM(dest); return ret; } /** * Create a new builder, that is exactly a copy of an old builder, but where * create_node() function has been replaced by image_builder_create_node. */ static int iso_image_builder_new(IsoNodeBuilder *old, IsoNodeBuilder **builder) { IsoNodeBuilder *b; if (builder == NULL) { return ISO_NULL_POINTER; } b = malloc(sizeof(IsoNodeBuilder)); if (b == NULL) { return ISO_OUT_OF_MEM; } b->refcount = 1; b->create_file_data = old->create_file_data; b->create_node_data = old->create_node_data; b->create_file = old->create_file; b->create_node = image_builder_create_node; b->free = old->free; *builder = b; return ISO_SUCCESS; } /** * Create a file source to access the El-Torito boot image, when it is not * accessible from the ISO filesystem. */ static int create_boot_img_filesrc(IsoImageFilesystem *fs, IsoImage *image, int idx, IsoFileSource **src) { int ret; struct stat atts; _ImageFsData *fsdata; IsoFileSource *ifsrc = NULL; ImageFileSourceData *ifsdata = NULL; IsoNode *node; uint32_t size, next_above = 0, start_block; if (fs == NULL || fs->data == NULL || src == NULL) { return ISO_NULL_POINTER; } fsdata = (_ImageFsData*)fs->data; memset(&atts, 0, sizeof(struct stat)); atts.st_mode = S_IFREG; atts.st_ino = img_give_ino_number(image, 0); atts.st_nlink = 1; /* * Old comment from Vreixo Formoso: * this is the greater problem. We don't know the size. For now, we * just use a single block of data. In a future, maybe we could figure out * a better idea. Another alternative is to use several blocks, that way * is less probable that we throw out valid data. */ /* ts C31112 : Make use of size estimation "El Torito img blks :" */ /* (Code from iso_impsysa_report_blockpath() ) */ size = 1; start_block = fsdata->bootblocks[idx]; iso_tree_get_node_of_block(image, NULL, start_block, &node, &next_above, 0); ret = iso_impsysa_reduce_next_above(image, start_block, &next_above, 0); if (ret >= 0) { if (next_above != 0) { /* It has to fit in a single extent */ if ((off_t) (next_above - start_block) * BLOCK_SIZE < (off_t) 0xffffffff) { size = next_above - start_block; } else { size = 0xffffffff / BLOCK_SIZE; } } } atts.st_size = (off_t) size * BLOCK_SIZE; /* Fill last entries */ atts.st_dev = fsdata->id; atts.st_blksize = BLOCK_SIZE; atts.st_blocks = DIV_UP(atts.st_size, BLOCK_SIZE); /* ok, we can now create the file source */ ifsdata = calloc(1, sizeof(ImageFileSourceData)); if (ifsdata == NULL) { ret = ISO_OUT_OF_MEM; goto boot_fs_cleanup; } ifsrc = calloc(1, sizeof(IsoFileSource)); if (ifsrc == NULL) { ret = ISO_OUT_OF_MEM; goto boot_fs_cleanup; } ifsdata->sections = malloc(sizeof(struct iso_file_section)); if (ifsdata->sections == NULL) { ret = ISO_OUT_OF_MEM; goto boot_fs_cleanup; } /* fill data */ ifsdata->fs = fs; iso_filesystem_ref(fs); ifsdata->parent = NULL; ifsdata->info = atts; ifsdata->name = NULL; ifsdata->sections[0].block = fsdata->bootblocks[idx]; ifsdata->sections[0].size = size * BLOCK_SIZE; ifsdata->nsections = 1; ifsrc->class = &ifs_class; ifsrc->data = ifsdata; ifsrc->refcount = 1; *src = ifsrc; return ISO_SUCCESS; boot_fs_cleanup: ; free(ifsdata); free(ifsrc); return ret; } /** ??? >>> ts B00428 : should the max size become public ? */ #define Libisofs_boot_info_image_max_sizE (4096*1024) /** Guess which of the loaded boot images contain boot information tables. Set boot->seems_boot_info_table accordingly. */ static int iso_image_eval_boot_info_table(IsoImage *image, struct iso_read_opts *opts, IsoDataSource *src, uint32_t iso_image_size, int flag) { int i, j, ret, section_count, todo, chunk; uint32_t img_lba, img_size, boot_pvd_found, image_pvd, alleged_size; struct iso_file_section *sections = NULL; struct el_torito_boot_image *boot; uint8_t *boot_image_buf = NULL, boot_info_found[16], *buf = NULL; IsoStream *stream = NULL; IsoFile *boot_file; uint64_t blk; if (image->bootcat == NULL) {ret = ISO_SUCCESS; goto ex;} LIBISO_ALLOC_MEM(buf, uint8_t, BLOCK_SIZE); for (i = 0; i < image->bootcat->num_bootimages; i++) { boot = image->bootcat->bootimages[i]; boot_file = boot->image; boot->seems_boot_info_table = 0; boot->seems_grub2_boot_info = 0; boot->seems_isohybrid_capable = 0; img_size = iso_file_get_size(boot_file); if (img_size > Libisofs_boot_info_image_max_sizE || img_size < 64) continue; img_lba = 0; sections = NULL; ret = iso_file_get_old_image_sections(boot_file, §ion_count, §ions, 0); if (ret == 1 && section_count > 0) img_lba = sections[0].block; if (sections != NULL) { free(sections); sections = NULL; } if(img_lba == 0) continue; boot_image_buf = calloc(1, img_size); if (boot_image_buf == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } stream = iso_file_get_stream(boot_file); ret = iso_stream_open(stream); if (ret < 0) { stream = NULL; goto ex; } for (todo = img_size; todo > 0; ) { if (todo > BLOCK_SIZE) chunk = BLOCK_SIZE; else chunk = todo; ret = iso_stream_read(stream, boot_image_buf + (img_size - todo), chunk); if (ret != chunk) { ret = (ret < 0) ? ret : (int) ISO_FILE_READ_ERROR; goto ex; } todo -= chunk; } iso_stream_close(stream); stream = NULL; memcpy(boot_info_found, boot_image_buf + 8, 16); boot_pvd_found = iso_read_lsb(boot_info_found, 4); image_pvd = (uint32_t) (opts->block + 16); /* Accommodate to eventually relocated superblock */ if (image_pvd != boot_pvd_found && image_pvd == 16 && boot_pvd_found < iso_image_size) { /* Check whether there is a PVD at boot_pvd_found and whether it bears the same image size */ ret = read_pvd_block(src, boot_pvd_found, buf, &alleged_size); if (ret == 1 && alleged_size + boot_pvd_found == iso_image_size + image_pvd) image_pvd = boot_pvd_found; } ret = make_boot_info_table(boot_image_buf, image_pvd, img_lba, img_size); if (ret < 0) goto ex; if (memcmp(boot_image_buf + 8, boot_info_found, 16) == 0) boot->seems_boot_info_table = 1; if (img_size >= Libisofs_grub2_elto_patch_poS + 8) { blk = 0; for (j = Libisofs_grub2_elto_patch_poS + 7; j >= Libisofs_grub2_elto_patch_poS; j--) blk = (blk << 8) | boot_image_buf[j]; if (blk == img_lba * 4 + Libisofs_grub2_elto_patch_offsT) boot->seems_grub2_boot_info = 1; } if (img_size >= 68 && boot->seems_boot_info_table) if (boot_image_buf[64] == 0xfb && boot_image_buf[65] == 0xc0 && boot_image_buf[66] == 0x78 && boot_image_buf[67] == 0x70) boot->seems_isohybrid_capable = 1; free(boot_image_buf); boot_image_buf = NULL; } ret = 1; ex:; if (boot_image_buf != NULL) free(boot_image_buf); if (stream != NULL) iso_stream_close(stream); LIBISO_FREE_MEM(buf); return ret; } static void issue_collision_warning_summary(size_t failures) { if (failures > ISO_IMPORT_COLL_WARN_MAX) { iso_msg_submit(-1, ISO_IMPORT_COLLISION, 0, "More file name collisions had to be resolved"); } if (failures > 0) { iso_msg_submit(-1, ISO_IMPORT_COLLISION, 0, "Sum of resolved file name collisions: %.f", (double) failures); } } /* Mark all non-matching combinations of head_per_cyl and sectors_per_head in the matches bitmap. This is a brute force approach to find the common intersections of up to 8 hyperbolas additionally intersected with the grid of integer coordinates {1..255}x{1..63}. Given the solution space size of only 14 bits, it seems inappropriate to employ any algebra. */ static void iso_scan_hc_sh(uint32_t lba, int c, int h, int s, uint8_t *matches) { int i, j; uint32_t res; /* fprintf(stderr, "iso_scan_hc_sh :%d = %4d/%3d/%2d :\n", lba, c, h, s); */ if (lba == ((uint32_t) s) - 1 && c == 0 && h == 0) /* trivial solutions */ return; if (c == 1023 && h >= 254 && s == 63) /* Indicators for invalid CHS */ return; /* matches(i=0,j=1) == 0 indicates presence of non-trivial equations */ matches[0] &= ~1; for (i = 1; i <= 255; i++) { for (j = 1; j <= 63; j++) { res = ((c * i) + h) * j + (s - 1); if (res != lba) { matches[(i / 8) * 32 + (j - 1)] &= ~(1 << (i % 8)); /* } else { if (matches[(i / 8) * 32 + (j - 1)] & (1 << (i % 8))) fprintf(stderr, "iso_scan_hc_sh :%d = %4d/%3d/%2d : H/C= %3d S/H= %2d\n", lba, c, h, s, i, j); */ } } } } /* Pick a good remaining solution from the matches bitmap. */ static void iso_get_hc_sh(uint8_t *matches, uint32_t iso_image_size, int *hc, int *sh, int flag) { int i, j, k; static int pref[][2] = {{64, 32}, {255, 63}}, prefs = 2; *hc = *sh = 0; if (matches[0] & 1) return; /* Only trivial equations seen */ /* Look for preferred layouts */ for (k = 0; k < prefs; k++) { i = pref[k][0]; j = pref[k][1]; if ((uint32_t) (1024 / 4 * i * j) <= iso_image_size) continue; if (matches[(i / 8) * 32 + (j - 1)] & (1 << (i % 8))) { *hc = i; *sh = j; return; } } /* Look for largest possible cylinder */ for (i = 1; i <= 255; i++) { for (j = 1; j <= 63; j++) { if ((uint32_t) (1024 / 4 * i * j) <= iso_image_size) continue; if (matches[(i / 8) * 32 + (j - 1)] & (1 << (i % 8))) { if( i * j < *hc * *sh) continue; *hc = i; *sh = j; } } } } static int iso_analyze_mbr_ptable(IsoImage *image, IsoDataSource *src, int flag) { int i, j, ret, cyl_align_mode, part_after_image = 0, ignore_part; uint32_t start_h, start_s, start_c, end_h, end_s, end_c, sph = 0, hpc = 0; uint32_t start_lba, num_blocks, end_chs_lba, image_size, lba, cyl_size; uint8_t *data, pstatus, ptype, *hc_sh = NULL, *buf = NULL; struct iso_imported_sys_area *sai; /* Bitmap for finding head_per_cyl and sectors_per_head. */ LIBISO_ALLOC_MEM(hc_sh, uint8_t, 32 * 63); memset(hc_sh, 0xff, 32 * 63); LIBISO_ALLOC_MEM(buf, uint8_t, 2048); sai = image->imported_sa_info; image_size = sai->image_size; for (i = 0; i < 4; i++) { data = (uint8_t *) (image->system_area_data + 446 + 16 * i); for (j = 0; j < 16; j++) if (data[j]) break; if (j == 16) continue; pstatus = data[0]; ptype = data[4]; start_c = ((data[2] & 0xc0) << 2) | data[3]; start_h = data[1]; start_s = data[2] & 63; end_c = ((data[6] & 0xc0) << 2) | data[7]; end_h = data[5]; end_s = data[6] & 63; start_lba = iso_read_lsb(data + 8, 4); num_blocks = iso_read_lsb(data + 12, 4); if (num_blocks <= 0) continue; /* Check whether the partition fits into size of medium */ ignore_part= ((off_t) num_blocks + (off_t) start_lba - (off_t) 1 > (off_t) 0xffffffff); if (!ignore_part) { ret= src->read_block(src, start_lba / 4, buf); if (ret != 1) ignore_part = 1; } if (!ignore_part) { lba = (start_lba + num_blocks - 1) / 4; /* make sure not to ignore because of incomplete last 2048 block */ if (lba > 0 && (off_t) lba * (off_t) 4 + (off_t) 3 != (off_t) (start_lba + num_blocks - 1)) lba--; ret= src->read_block(src, lba, buf); if (ret != 1) ignore_part = 1; } if (ignore_part) { if (ptype == 0xee) { iso_msg_submit(image->id, ISO_GENERAL_NOTE, 0, "Found Protective MBR with size range larger than the medium capacity"); } else { iso_msg_submit(image->id, ISO_GENERAL_NOTE, 0, "Ignored non-empty MBR partition outside of medium capacity"); continue; } } if (sph > 0) { if (end_s != sph) sph = 0xffffffff; } else if (sph == 0) { sph = end_s; } if (hpc > 0) { if (end_h + 1 != hpc) hpc = 0xffffffff; } else if (hpc == 0) { hpc = end_h + 1; } /* Check whether start_lba + num_blocks - 1 matches chs,hpc,spc */ end_chs_lba = ((end_c * hpc) + end_h) * sph + end_s; if (hpc > 0 && hpc < 0xffffffff && sph > 0 && sph < 0xffffffff) if (end_chs_lba != start_lba + num_blocks) hpc = sph = 0xffffffff; /* In case that end CHS does not give cylinder layout */ iso_scan_hc_sh(start_lba, start_c, start_h, start_s, hc_sh); iso_scan_hc_sh(start_lba + num_blocks - 1, end_c, end_h, end_s, hc_sh); /* Register partition as iso_mbr_partition_request */ if (sai->mbr_req == NULL) { sai->mbr_req = calloc(ISO_MBR_ENTRIES_MAX, sizeof(struct iso_mbr_partition_request *)); if (sai->mbr_req == NULL) {ret = ISO_OUT_OF_MEM; goto ex;} } ret = iso_quick_mbr_entry(sai->mbr_req, &(sai->mbr_req_count), (uint64_t) start_lba, (uint64_t) num_blocks, ptype, pstatus, i + 1); if (ret < 0) goto ex; if ((start_lba + num_blocks + 3) / 4 > image_size) image_size = (start_lba + num_blocks + 3) / 4; } if (hpc > 0 && hpc < 0xffffffff && sph > 0 && sph < 0xffffffff) { sai->partition_secs_per_head = sph; sai->partition_heads_per_cyl = hpc; } else { /* Look for the best C/H/S parameters caught in scan */ iso_get_hc_sh(hc_sh, image_size, &(sai->partition_heads_per_cyl), &(sai->partition_secs_per_head), 0); } cyl_align_mode = 2; /* off */ if (sai->partition_secs_per_head >0 && sai->partition_heads_per_cyl > 0 && sai->mbr_req_count > 0) { /* Check for cylinder alignment */ for (i = 0; i < sai->mbr_req_count; i++) { cyl_size = sai->partition_secs_per_head * sai->partition_heads_per_cyl; lba = sai->mbr_req[i]->start_block + sai->mbr_req[i]->block_count; if (sai->mbr_req[i]->start_block >= sai->image_size) part_after_image = 1; end_c = lba / cyl_size; if (end_c * cyl_size != lba) break; } if (i == sai->mbr_req_count && part_after_image) cyl_align_mode = 3; /* all */ else if (i >= 1) cyl_align_mode = 1; /* on */ } sai->system_area_options &= ~(3 << 8); sai->system_area_options |= (cyl_align_mode << 8); ret = 1; ex: LIBISO_FREE_MEM(buf); LIBISO_FREE_MEM(hc_sh); return ret; } /* @return 0= no hybrid detected 1= ISOLINUX isohybrid (options & 2) 2= GRUB2 MBR patching (options & (1 << 14)) */ static int iso_analyze_isohybrid(IsoImage *image, int flag) { uint8_t *sad; uint32_t eltorito_lba = 0; uint64_t mbr_lba; int i, section_count, ret; ElToritoBootImage *boot; struct iso_file_section *sections; sad = (uint8_t *) image->system_area_data; /* Learn LBA of boot image */; if (image->bootcat == NULL) return 0; if (image->bootcat->num_bootimages < 1) return 0; boot = image->bootcat->bootimages[0]; if (boot == NULL) return 0; ret = iso_file_get_old_image_sections(boot->image, §ion_count, §ions, 0); if (ret < 0) return ret; if (ret > 0 && section_count > 0) eltorito_lba = sections[0].block; free(sections); /* Check MBR whether it is ISOLINUX and learn LBA to which it points */ if (!boot->seems_isohybrid_capable) goto try_grub2_mbr; for (i= 0; i < 426; i++) if(strncmp((char *) (sad + i), "isolinux", 8) == 0) break; if (i < 426) { /* search text was found */ mbr_lba = iso_read_lsb(sad + 432, 4); mbr_lba /= 4; if (mbr_lba == eltorito_lba) return 1; goto try_grub2_mbr; } try_grub2_mbr:; /* Check for GRUB2 MBR patching */ mbr_lba = iso_read_lsb64(sad + 0x1b0); if (mbr_lba / 4 - 1 == eltorito_lba) return 2; return 0; } static int iso_analyze_partition_offset(IsoImage *image, IsoDataSource *src, uint64_t start_block, uint64_t block_count, int flag) { int ret; uint8_t *buf = NULL; uint32_t iso_size; off_t p_offset; struct ecma119_pri_vol_desc *pvm; struct iso_imported_sys_area *sai; sai = image->imported_sa_info; /* Check for PVD at partition start with same end */ LIBISO_ALLOC_MEM(buf, uint8_t, 2048); p_offset = start_block / 4; ret = src->read_block(src, p_offset + 16, buf); if (ret > 0) { pvm = (struct ecma119_pri_vol_desc *) buf; iso_size = iso_read_lsb(pvm->vol_space_size, 4); if (strncmp((char*) pvm->std_identifier, "CD001", 5) == 0 && pvm->vol_desc_type[0] == 1 && pvm->vol_desc_version[0] == 1 && pvm->file_structure_version[0] == 1 && (iso_size + p_offset == sai->image_size || iso_size == block_count / 4)) sai->partition_offset = p_offset; } ret = 1; ex:; LIBISO_FREE_MEM(buf); return ret; } /* @param flag bit0= Pre-run: Only assess partition table. */ static int iso_analyze_mbr(IsoImage *image, IsoDataSource *src, int flag) { int sub_type = 2, ret, i, is_isohybrid = 0, is_grub2_mbr = 0; int is_protective_label = 0; uint32_t next_above = 0; uint64_t part2_start; char *sad; struct iso_imported_sys_area *sai; struct iso_mbr_partition_request *part; IsoNode *node; sad = image->system_area_data; sai = image->imported_sa_info; /* Is it an MBR ? */ if (((unsigned char *) sad)[510] != 0x55 || ((unsigned char *) sad)[511] != 0xaa) {ret = 0; goto ex;} ret = iso_analyze_mbr_ptable(image, src, 0); if (ret <= 0) goto ex; if((flag & 1)) { ret= 1; goto ex; } /* Possibly obtain ISO paths of MBR partition content */ for (i = 0; i < sai->mbr_req_count; i++) { part = sai->mbr_req[i]; if (part->block_count == 0 || part->image_path != NULL) continue; ret = iso_tree_get_node_of_block(image, NULL, part->start_block / 4, &node, &next_above, 0); if (ret > 0) part->image_path = iso_tree_get_node_path(node); } ret = iso_analyze_isohybrid(image, 0); if (ret < 0) goto ex; if (ret == 1) { sub_type = 0; is_isohybrid = 1; } else if(ret == 2) { /* will become sub_type 0 if protective_label */ is_grub2_mbr = 1; } if (sai->mbr_req_count == 3 && !is_isohybrid) { /* Check for libisofs PReP partitions : 0xee or 0xcd from 0 to a-1 0x41 from a to b 0x0c or 0xcd from b+1 to end */ if ((sai->mbr_req[0]->start_block == 0 && (sai->mbr_req[0]->type_byte == 0xee || sai->mbr_req[0]->type_byte == 0xcd)) && sai->mbr_req[0]->block_count == sai->mbr_req[1]->start_block && sai->mbr_req[1]->type_byte == 0x41 && (sai->mbr_req[1]->start_block % 4) == 0 && sai->mbr_req[1]->start_block + sai->mbr_req[1]->block_count == sai->mbr_req[2]->start_block && (sai->mbr_req[2]->type_byte == 0x0c || sai->mbr_req[2]->type_byte == 0xcd) && (sai->mbr_req[2]->start_block + sai->mbr_req[2]->block_count) / 4 == sai->image_size) { sai->prep_part_start = sai->mbr_req[1]->start_block / 4; sai->prep_part_size = (sai->mbr_req[1]->block_count + 3) / 4; sub_type = 0; } } if (sai->mbr_req_count >= 1 && (sai->mbr_req[0]->type_byte == 0xee || !is_isohybrid) && !(sai->prep_part_start > 0)) { part = sai->mbr_req[0]; part2_start = 0; if (sai->mbr_req_count >= 2) part2_start = sai->mbr_req[1]->start_block; if (part->start_block == 1 && (part->block_count + 1 == ((uint64_t) sai->image_size) * 4 || (part->type_byte == 0xee && part->block_count + 1 >= ((uint64_t) sai->image_size) * 4 && (sai->mbr_req_count == 1 || (sai->mbr_req_count == 2 && sai->mbr_req[1]->type_byte == 0x00))) || part->block_count + 1 == part2_start)) { /* libisofs protective msdos label for GRUB2 */ is_protective_label = 1; sub_type = 0; } else if (sai->mbr_req_count == 1 && part->start_block == 0 && part->block_count <= ((uint64_t) sai->image_size) * 4 && part->block_count + 600 >= ((uint64_t) sai->image_size) * 4 && part->type_byte == 0x96) { /* CHRP (possibly without padding) */ sub_type = 1; } else if (sai->mbr_req_count == 1 && sai->mbr_req[0]->start_block > 0 && (sai->mbr_req[0]->start_block % 4) == 0 && (sai->mbr_req[0]->start_block + sai->mbr_req[0]->block_count) / 4 <= sai->image_size && part->type_byte == 0x41) { /* mkisofs PReP partition */ sai->prep_part_start = sai->mbr_req[0]->start_block / 4; sai->prep_part_size = (sai->mbr_req[0]->block_count + 3) / 4; sub_type = 0; } } /* Check for partition offset with extra set of meta data */ if (sai->mbr_req_count > 0) { part = sai->mbr_req[0]; if ((part->status_byte == 0x80 || part->status_byte == 0) && part->start_block >= 64 && part->block_count >= 72 && part->start_block <= 2048 && part->start_block % 4 == 0 && part->block_count % 4 == 0 && (part->start_block + part->block_count) / 4 <= sai->image_size) { ret = iso_analyze_partition_offset(image, src, part->start_block, part->block_count, 0); if (ret < 0) goto ex; } } /* Set sa type 0, sub type as chosen */ sai->system_area_options = (sai->system_area_options & 0xffff8300) | is_protective_label | (is_isohybrid << 1) | (sub_type << 10) | (is_grub2_mbr << 14); ret = 1; ex:; return ret; } static int iso_seems_usable_gpt_head(uint8_t *head, int flag) { uint32_t head_size, entry_size; if (strncmp((char *) head, "EFI PART", 8) != 0) /* signature */ return 0; if (head[8] || head[9] || head[10] != 1 || head[11]) /* revision */ return 0; head_size = iso_read_lsb(head + 12, 4); if (head_size < 92) return 0; entry_size = iso_read_lsb(head + 84, 4); if (entry_size != 128) return 0; return 1; } static int iso_analyze_gpt_backup(IsoImage *image, IsoDataSource *src, int flag) { struct iso_imported_sys_area *sai; uint64_t part_start; uint32_t iso_block, found_crc, crc, entry_count, array_crc; uint8_t *head, *part_array, *b_part, *m_part; int ret, i, num_iso_blocks, l, j, entries_diff; unsigned char *buf = NULL; char *comments = NULL; sai = image->imported_sa_info; LIBISO_ALLOC_MEM(buf, unsigned char, 34 * 1024); LIBISO_ALLOC_MEM(comments, char, 4096); /* Read ISO block with backup head */ if (sai->gpt_backup_lba >= ((uint64_t) sai->image_size) * 4 && (sai->mbr_req_count < 1 || sai->mbr_req[0]->start_block + sai->mbr_req[0]->block_count > sai->gpt_backup_lba + 1)) sprintf(comments + strlen(comments), "Implausible header LBA %.f, ", (double) sai->gpt_backup_lba); iso_block = sai->gpt_backup_lba / 4; ret = src->read_block(src, iso_block, buf); if (ret < 0) { sprintf(comments + strlen(comments), "Cannot read header block at 2k LBA %.f, ", (double) iso_block); ret = 0; goto ex; } head = buf + (sai->gpt_backup_lba % 4) * 512; ret = iso_seems_usable_gpt_head(head, 0); if (ret == 0) strcat(comments, "Not a GPT 1.0 header of 92 bytes for 128 bytes per entry, "); if (ret <= 0) { ret = 0; goto ex; } /* Check head CRC */ found_crc = iso_read_lsb(head + 16, 4); memset(head + 16, 0, 4); crc = iso_crc32_gpt((unsigned char *) head, 92, 0); if (found_crc != crc) { sprintf(comments + strlen(comments), "Head CRC 0x%8x wrong. Should be 0x%8x", found_crc, crc); crc = iso_crc32_gpt((unsigned char *) head, 512, 0); if (found_crc == crc) { strcat(comments, ". Matches all 512 block bytes, "); } else { strcat(comments, ", "); ret = 0; goto ex; } } for (i = 0; i < 16; i ++) if (head[i + 56] != sai->gpt_disk_guid[i]) break; if (i < 16) { sprintf(comments + strlen(comments), "Disk GUID differs ("); iso_util_bin_to_hex(comments + strlen(comments), head + 56, 16, 0); sprintf(comments + strlen(comments), "), "); } /* Header content will possibly be overwritten now */ array_crc = iso_read_lsb(head + 88, 4); part_start = iso_read_lsb64(head + 72); entry_count = iso_read_lsb(head + 80, 4); head = NULL; /* Read backup array */ if (entry_count != sai->gpt_max_entries) { sprintf(comments + strlen(comments), "Number of array entries %u differs from main GPT %u, ", entry_count, sai->gpt_max_entries); ret = 0; goto ex; } if (part_start + (entry_count + 3) / 4 != sai->gpt_backup_lba) sprintf(comments + strlen(comments), "Implausible array LBA %.f, ", (double) part_start); iso_block = part_start / 4; num_iso_blocks = (part_start + (entry_count + 3) / 4) / 4 - iso_block + 1; for (i = 0; i < num_iso_blocks; i++) { ret = src->read_block(src, iso_block + (uint32_t) i, buf + i * 2048); if (ret < 0) { sprintf(comments + strlen(comments), "Cannot read array block at 2k LBA %.f, ", (double) iso_block); ret = 0; goto ex; } } part_array = buf + (part_start % 4) * 512; crc = iso_crc32_gpt((unsigned char *) part_array, 128 * entry_count, 0); if (crc != array_crc) sprintf(comments + strlen(comments), "Array CRC 0x%8x wrong. Should be 0x%8x, ", array_crc, crc); /* Compare entries */ entries_diff = 0; for (i = 0; i < (int) entry_count; i++) { b_part = part_array + 128 * i; m_part = ((uint8_t *) image->system_area_data) + sai->gpt_part_start * 512 + 128 * i; for (j = 0; j < 128; j++) if (b_part[j] != m_part[j]) break; if (j < 128) { if (!entries_diff) { strcat(comments, "Entries differ for partitions"); entries_diff = 1; } sprintf(comments + strlen(comments), " %d", i + 1); } } if (entries_diff) { strcat(comments, ", "); ret = 0; goto ex; } ret = 1; ex:; if (comments != NULL) { l = strlen(comments); if (l > 2) if (comments[l - 2] == ',' && comments[l - 1] == ' ') comments[l - 2] = 0; sai->gpt_backup_comments = strdup(comments); if (sai->gpt_backup_comments == NULL) ret = ISO_OUT_OF_MEM; } LIBISO_FREE_MEM(comments); LIBISO_FREE_MEM(buf); return ret; } static int iso_analyze_gpt_head(IsoImage *image, IsoDataSource *src, int flag) { struct iso_imported_sys_area *sai; uint8_t *head; uint32_t crc; uint64_t part_start; int ret; unsigned char *crc_buf = NULL; sai = image->imported_sa_info; head = ((uint8_t *) image->system_area_data) + 512; LIBISO_ALLOC_MEM(crc_buf, unsigned char, 512); /* Is this a GPT header with digestible parameters ? */ ret = iso_seems_usable_gpt_head(head, 0); if (ret <= 0) goto ex; memcpy(crc_buf, head, 512); memset(crc_buf + 16, 0, 4); /* CRC is computed when head_crc is 0 */ sai->gpt_head_crc_found = iso_read_lsb(head + 16, 4); sai->gpt_head_crc_should = iso_crc32_gpt((unsigned char *) crc_buf, 92, 0); if (sai->gpt_head_crc_found != sai->gpt_head_crc_should) { /* There was a bug during libisofs-1.2.4 to libisofs-1.2.8 (fixed in rev 1071). So accept the buggy CRC if it matches the whole GPT header block. */ crc = iso_crc32_gpt((unsigned char *) crc_buf, 512, 0); if (sai->gpt_head_crc_found != crc) {ret = 0; goto ex;} } part_start = iso_read_lsb64(head + 72); sai->gpt_max_entries = iso_read_lsb(head + 80, 4); if (part_start + (sai->gpt_max_entries + 3) / 4 > 64) {ret = 0; goto ex;} /* Fetch desired information */ memcpy(sai->gpt_disk_guid, head + 56, 16); sai->gpt_part_start = part_start; sai->gpt_backup_lba = iso_read_lsb64(head + 32); sai->gpt_first_lba = iso_read_lsb64(head + 40); sai->gpt_last_lba = iso_read_lsb64(head + 48); sai->gpt_array_crc_found = iso_read_lsb(head + 88, 4); sai->gpt_array_crc_should = iso_crc32_gpt((unsigned char *) image->system_area_data + sai->gpt_part_start * 512, sai->gpt_max_entries * 128, 0); ret = iso_analyze_gpt_backup(image, src, 0); if (ret < 0) goto ex; ret = 1; ex: LIBISO_FREE_MEM(crc_buf); return ret; } /* @param flag bit0= Pre-run: Only assess partition table. */ static int iso_analyze_gpt(IsoImage *image, IsoDataSource *src, int flag) { int ret, i, j; uint64_t start_block, block_count, flags, end_block, j_end, j_start; uint8_t *part; struct iso_imported_sys_area *sai; struct iso_gpt_partition_request *gpt_entry; IsoNode *node; sai = image->imported_sa_info; ret = iso_analyze_gpt_head(image, src, 0); if (ret <= 0) return ret; for (i = 0; i < (int) sai->gpt_max_entries; i++) { part = ((uint8_t *) image->system_area_data) + sai->gpt_part_start * 512 + 128 * i; for (j = 0; j < 128; j++) if (part[j]) break; if (j >= 128) /* all zero, invalid entry */ continue; start_block = iso_read_lsb64(part + 32); block_count = iso_read_lsb64(part + 40); flags = iso_read_lsb64(part + 48); if ((start_block == 0 && block_count == 0) || block_count + 1 < start_block) continue; block_count = block_count + 1 - start_block; if (sai->gpt_req == NULL) { sai->gpt_req = calloc(ISO_GPT_ENTRIES_MAX, sizeof(struct iso_gpt_partition_request *)); if (sai->gpt_req == NULL) return ISO_OUT_OF_MEM; } ret = iso_quick_gpt_entry(sai->gpt_req, &(sai->gpt_req_count), start_block, block_count, part, part + 16, flags, part + 56); if (ret < 0) return ret; sai->gpt_req[sai->gpt_req_count - 1]->idx = i + 1; } /* sai->gpt_req_flags : bit0= GPT partitions may overlap >>> bit1= with bit0: neatly nested partitions without : neatly divided disk */ for (i = 0; i < (int) sai->gpt_req_count && !(sai->gpt_req_flags & 1); i++) { if (sai->gpt_req[i]->block_count == 0) continue; start_block = sai->gpt_req[i]->start_block; end_block = start_block + sai->gpt_req[i]->block_count; for (j = i + 1; j < (int) sai->gpt_req_count; j++) { if (sai->gpt_req[j]->block_count == 0) continue; j_start = sai->gpt_req[j]->start_block; j_end = j_start + sai->gpt_req[j]->block_count; if ((start_block <= j_start && j_start < end_block) || (start_block <= j_end && j_end < end_block) || (j_start <= start_block && start_block < j_end)) { sai->gpt_req_flags |= 1; break; } } } /* Check first GPT partition for ISO partition offset */ if (sai->partition_offset == 0 && sai->mbr_req_count > 0 && sai->gpt_req_count > 0) { if (sai->mbr_req[0]->type_byte == 0xee && sai->mbr_req[0]->start_block == 1) { /* protective MBR */ start_block = sai->gpt_req[0]->start_block; block_count = sai->gpt_req[0]->block_count; if (start_block >= 64 && block_count >= 72 && start_block <= 2048 && start_block % 4 == 0 && block_count % 4 == 0) { ret = iso_analyze_partition_offset(image, src, start_block, block_count, 0); if (ret < 0) return ret; } } } if (flag & 1) return 1; /* Possibly obtain ISO paths of GPT partition content */ for (i = 0; i < sai->gpt_req_count; i++) { gpt_entry = sai->gpt_req[i]; if (gpt_entry->block_count == 0 || gpt_entry->image_path != NULL) continue; ret = iso_tree_get_node_of_block(image, NULL, (uint32_t) (gpt_entry->start_block / 4), &node, NULL, 0); if (ret > 0) gpt_entry->image_path = iso_tree_get_node_path(node); } return 1; } static int iso_analyze_apm_head(IsoImage *image, IsoDataSource *src, int flag) { struct iso_imported_sys_area *sai; char *sad; uint32_t block_size; sai = image->imported_sa_info; sad = image->system_area_data; if (sad[0] != 'E' || sad[1] != 'R') return 0; block_size = iso_read_msb(((uint8_t *) sad) + 2, 2); if (block_size != 2048 && block_size != 512) return 0; sai->apm_block_size = block_size; sai->apm_req_flags |= 4 | 2; /* start_block and block_count are in block_size units, do not fill gaps */ return 1; } /* @param flag bit0= Pre-run: Only assess partition table. */ static int iso_analyze_apm(IsoImage *image, IsoDataSource *src, int flag) { int ret, i; uint32_t map_entries, start_block, block_count, flags; char *sad, *part, name[33], type_string[33]; struct iso_imported_sys_area *sai; struct iso_apm_partition_request *apm_entry; IsoNode *node; sai = image->imported_sa_info; sad = image->system_area_data; ret = iso_analyze_apm_head(image, src, 0); if (ret <= 0) return ret; part = sad + sai->apm_block_size; map_entries = iso_read_msb(((uint8_t *) part) + 4, 4); for (i = 0; i < (int) map_entries; i++) { part = sad + (i + 1) * sai->apm_block_size; if (part[0] != 'P' || part[1] != 'M') break; flags = iso_read_msb(((uint8_t *) part) + 88, 4); if (!(flags & 3)) continue; memcpy(type_string, part + 48, 32); type_string[32] = 0; if(strcmp(type_string, "Apple_partition_map") == 0) continue; start_block = iso_read_msb(((uint8_t *) part) + 8, 4); block_count = iso_read_msb(((uint8_t *) part + 12), 4); memcpy(name, part + 16, 32); name[32] = 0; if (sai->apm_req == NULL) { sai->apm_req = calloc(ISO_APM_ENTRIES_MAX, sizeof(struct iso_apm_partition_request *)); if (sai->apm_req == NULL) return ISO_OUT_OF_MEM; } ret = iso_quick_apm_entry(sai->apm_req, &(sai->apm_req_count), start_block, block_count, name, type_string); if (ret <= 0) return ret; if (strncmp(name, "Gap", 3) == 0 && strcmp(type_string, "ISO9660_data") == 0) { if ('0' <= name[3] && name[3] <= '9' && (name[4] == 0 || ('0' <= name[4] && name[4] <= '9' && name[5] == 0))) { sai->apm_gap_count++; sai->apm_req_flags &= ~2; } } } if (flag & 1) return 1; /* Possibly obtain ISO paths of APM partition content */ for (i = 0; i < sai->apm_req_count; i++) { apm_entry = sai->apm_req[i]; if (apm_entry->block_count == 0 || apm_entry->image_path != NULL) continue; ret = iso_tree_get_node_of_block(image, NULL, (uint32_t) (apm_entry->start_block / (2048 / sai->apm_block_size)), &node, NULL, 0); if (ret > 0) apm_entry->image_path = iso_tree_get_node_path(node); } return 1; } /* @param flag bit0= Pre-run: Only assess partition table. */ static int iso_analyze_mips(IsoImage *image, IsoDataSource *src, int flag) { int ret = 0, spt, bps, i, j, idx; uint32_t magic, chk, head_chk; char *sad; uint8_t *usad, *upart; struct iso_imported_sys_area *sai; IsoNode *node; sai = image->imported_sa_info; sad = image->system_area_data; usad = (uint8_t *) sad; magic = iso_read_msb(usad, 4); if (magic != 0x0be5a941) return 0; spt = iso_read_msb(usad + 38, 2); bps = iso_read_msb(usad + 40, 2); if (spt != 32 || bps != 512) return 0; chk = 0; for (i = 0; i < 504; i += 4) chk -= iso_read_msb(usad + i, 4); head_chk = iso_read_msb(usad + 504, 4); if (chk != head_chk) return 0; /* Verify that partitions 1 to 8 are empty */ for (j = 312; j < 408; j++) if (sad[j]) return 0; /* >>> verify that partitions 9 and 10 match the image size */; for (i = 0; i < 15; i++) { upart = usad + 72 + 16 * i; for (j = 0; j < 16; j++) if (upart[j]) break; if (j == 16) continue; if (sai->mips_vd_entries == NULL) { sai->mips_boot_file_paths = calloc(15, sizeof(char *)); sai->mips_vd_entries = calloc(15, sizeof(struct iso_mips_voldir_entry *)); if (sai->mips_vd_entries == NULL || sai->mips_boot_file_paths == NULL) return ISO_OUT_OF_MEM; sai->num_mips_boot_files = 0; for (j = 0; j < 15; j++) { sai->mips_boot_file_paths[j] = NULL; sai->mips_vd_entries[j] = NULL; } } /* Assess boot file entry */ if (sai->num_mips_boot_files >= 15) return ISO_BOOT_TOO_MANY_MIPS; idx = sai->num_mips_boot_files; sai->mips_vd_entries[idx] = calloc(1, sizeof(struct iso_mips_voldir_entry)); if (sai->mips_vd_entries[idx] == NULL) return ISO_OUT_OF_MEM; memcpy(sai->mips_vd_entries[idx]->name, upart, 8); sai->mips_vd_entries[idx]->name[8] = 0; sai->mips_vd_entries[idx]->boot_block = iso_read_msb(upart + 8, 4); sai->mips_vd_entries[idx]->boot_bytes = iso_read_msb(upart + 12, 4); if (!(flag & 1)) { ret = iso_tree_get_node_of_block(image, NULL, sai->mips_vd_entries[idx]->boot_block / 4, &node, NULL, 0); if (ret > 0) sai->mips_boot_file_paths[idx] = iso_tree_get_node_path(node); } sai->num_mips_boot_files++; } if (sai->num_mips_boot_files > 0) sai->system_area_options = (1 << 2);/* MIPS Big Endian Volume Header */ return ret; } /* @param flag bit0= Pre-run: Only assess partition table. */ static int iso_analyze_mipsel(IsoImage *image, IsoDataSource *src, int flag) { int ret = 0, i, section_count; char *sad; uint8_t *usad; uint32_t magic; struct iso_imported_sys_area *sai; IsoNode *node; IsoFile *file; struct iso_file_section *sections = NULL; sai = image->imported_sa_info; sad = image->system_area_data; usad = (uint8_t *) sad; for (i = 0; i < 8; i++) if (sad[i]) return 0; magic = iso_read_lsb(usad + 8, 4); if (magic != 0x0002757a) return 0; sai->mipsel_p_vaddr = iso_read_lsb(usad + 16, 4); sai->mipsel_e_entry = iso_read_lsb(usad + 20, 4); sai->mipsel_p_filesz = iso_read_lsb(usad + 24, 4) * 512; sai->mipsel_seg_start = iso_read_lsb(usad + 28, 4); if(!(flag & 1)) { ret = iso_tree_get_node_of_block(image, NULL, sai->mipsel_seg_start / 4, &node, NULL, 0); if (ret > 0) { sai->mipsel_boot_file_path = iso_tree_get_node_path(node); file = (IsoFile *) node; ret = iso_file_get_old_image_sections(file, §ion_count, §ions, 0); if (ret > 0 && section_count > 0) { if (sections[0].block < (1 << 30) && sections[0].block * 4 < sai->mipsel_seg_start) sai->mipsel_p_offset = sai->mipsel_seg_start - sections[0].block * 4; free(sections); } } } /* DEC Boot Block for MIPS Little Endian */ sai->system_area_options = (2 << 2); return 1; } /* @param flag bit0= Pre-run: Only assess partition table. */ static int iso_analyze_sun(IsoImage *image, IsoDataSource *src, int flag) { int ret = 0, i, idx; char *sad; uint8_t *usad, checksum[2]; uint16_t perms; uint64_t last_core_block; struct iso_imported_sys_area *sai; IsoNode *node; sai = image->imported_sa_info; sad = image->system_area_data; usad = (uint8_t *) sad; if (iso_read_msb(usad + 128, 4) != 1 || iso_read_msb(usad + 140, 2) != 8 || iso_read_msb(usad + 188, 4) != 0x600ddeee || iso_read_msb(usad + 430, 2) != 1 || iso_read_msb(usad + 508, 2) != 0xdabe) return 0; if (iso_read_msb(usad + 142, 2) != 4 || iso_read_msb(usad + 144, 2) != 0x10 || iso_read_msb(usad + 444, 4) != 0 || sai->image_size > 0x3fffffff || iso_read_msb(usad + 448, 4) < ((int64_t) sai->image_size * 4) - 600 || iso_read_msb(usad + 448, 4) > sai->image_size * 4) return 0; checksum[0] = checksum[1] = 0; for (i = 0; i < 510; i += 2) { checksum[0] ^= usad[i]; checksum[1] ^= usad[i + 1]; } if (checksum[0] != usad[510] || checksum[1] != usad[511]) return 0; sai->sparc_disc_label = calloc(1, 129); if (sai->sparc_disc_label == NULL) return ISO_OUT_OF_MEM; memcpy(sai->sparc_disc_label, sad, 128); sai->sparc_disc_label[128] = 0; sai->sparc_heads_per_cyl = iso_read_msb(usad + 436, 2); sai->sparc_secs_per_head = iso_read_msb(usad + 438, 2); for (i = 0; i < 8; i++) { perms = iso_read_msb(usad + 144 + 4 * i, 2); if (perms == 0) continue; if (sai->sparc_entries == NULL) { sai->sparc_entries = calloc(8, sizeof(struct iso_sun_disk_label_entry)); if (sai->sparc_entries == NULL) return ISO_OUT_OF_MEM; } idx = sai->sparc_entry_count; sai->sparc_entries[idx].idx = i + 1; sai->sparc_entries[idx].id_tag = iso_read_msb(usad + 142 + 4 * i, 2); sai->sparc_entries[idx].permissions = perms; sai->sparc_entries[idx].start_cyl = iso_read_msb(usad + 444 + 8 * i, 4); sai->sparc_entries[idx].num_blocks = iso_read_msb(usad + 448 + 8 * i, 4); sai->sparc_entry_count++; } /* GRUB2 SUN SPARC Core File Address */ sai->sparc_grub2_core_adr = iso_read_msb64(usad + 552); sai->sparc_grub2_core_size = iso_read_msb(usad + 560, 4); last_core_block = (sai->sparc_grub2_core_adr + sai->sparc_grub2_core_size + 2047) / 2048; if (last_core_block > 0) last_core_block--; if (last_core_block > 17 && last_core_block < sai->image_size && !(flag & 1)) { ret = iso_tree_get_node_of_block(image, NULL, (uint32_t) last_core_block, &node, NULL, 0); if (ret > 0) { iso_node_ref(node); sai->sparc_core_node = (IsoFile *) node; sai->sparc_core_node_path = iso_tree_get_node_path(node); } } else { sai->sparc_grub2_core_adr = 0; sai->sparc_grub2_core_size = 0; } /* SUN Disk Label for SUN SPARC */ sai->system_area_options = (3 << 2); return 1; } /* @param flag bit0= Pre-run: Only assess partition table. */ static int iso_analyze_hppa(IsoImage *image, IsoDataSource *src, int flag) { int ret = 0, i, cmd_adr, cmd_len; char *sad, *paths[4]; uint8_t *usad; uint16_t magic; uint32_t adrs[4]; struct iso_imported_sys_area *sai; IsoNode *node; sai = image->imported_sa_info; sad = image->system_area_data; usad = (uint8_t *) sad; magic = iso_read_msb(usad, 2); if (magic != 0x8000 || strncmp(sad + 2, "PALO", 5) != 0 || sad[7] < 4 || sad[7] > 5) return 0; sai->hppa_hdrversion = sad[7]; if (sai->hppa_hdrversion == 4) { cmd_len = 127; cmd_adr = 24; } else { cmd_len = 1023; cmd_adr = 1024; } sai->hppa_cmdline = calloc(1, cmd_len + 1); if (sai->hppa_cmdline == NULL) return ISO_OUT_OF_MEM; memcpy(sai->hppa_cmdline, sad + cmd_adr, cmd_len); sai->hppa_cmdline[cmd_len] = 0; adrs[0] = sai->hppa_kern32_adr = iso_read_msb(usad + 8, 4); sai->hppa_kern32_len = iso_read_msb(usad + 12, 4); adrs[1] = sai->hppa_kern64_adr = iso_read_msb(usad + 232, 4); sai->hppa_kern64_len = iso_read_msb(usad + 236, 4); adrs[2] = sai->hppa_ramdisk_adr = iso_read_msb(usad + 16, 4); sai->hppa_ramdisk_len = iso_read_msb(usad + 20, 4); adrs[3] = sai->hppa_bootloader_adr = iso_read_msb(usad + 240, 4); sai->hppa_bootloader_len = iso_read_msb(usad + 244, 4); if(!(flag & 1)) { for (i = 0; i < 4; i++) { paths[i] = NULL; ret = iso_tree_get_node_of_block(image, NULL, adrs[i] / 2048, &node, NULL, 0); if (ret > 0) paths[i] = iso_tree_get_node_path(node); } sai->hppa_kernel_32 = paths[0]; sai->hppa_kernel_64 = paths[1]; sai->hppa_ramdisk = paths[2]; sai->hppa_bootloader = paths[3]; } if (sai->hppa_hdrversion == 5) sai->hppa_ipl_entry = iso_read_msb(usad + 248, 4); /* HP-PA PALO boot sector version 4 or 5 for HP PA-RISC */ sai->system_area_options = (sai->hppa_hdrversion << 2); return 1; } /* @param flag bit0= Pre-run: Only assess partition table. */ static int iso_analyze_alpha_boot(IsoImage *image, IsoDataSource *src, int flag) { int ret = 0, i, section_count; char *sad; uint8_t *usad; struct iso_imported_sys_area *sai; IsoNode *node; IsoFile *file; uint64_t checksum_found, checksum_should = 0, size; struct iso_file_section *sections = NULL; sai = image->imported_sa_info; sad = image->system_area_data; usad = (uint8_t *) sad; checksum_found = iso_read_lsb64(usad + 504); for (i = 0; i < 63; i++) checksum_should += iso_read_lsb64(usad + 8 * i); if (checksum_found != checksum_should) return 0; sai->alpha_boot_image = NULL; sai->alpha_boot_image_size = (uint64_t) iso_read_lsb64(usad + 480); sai->alpha_boot_image_adr = (uint64_t) iso_read_lsb64(usad + 488); if (!(flag & 1)) { ret = iso_tree_get_node_of_block(image, NULL, (uint32_t) (sai->alpha_boot_image_adr / 4), &node, NULL, 0); if (ret > 0) { if (iso_node_get_type(node) != LIBISO_FILE) return 0; file = (IsoFile *) node; ret = iso_file_get_old_image_sections(file, §ion_count, §ions, 0); if (ret > 0 && section_count > 0) { size = sections[0].size / 512 + !!(sections[0].size % 512); free(sections); if (size != sai->alpha_boot_image_size) return 0; } sai->alpha_boot_image = iso_tree_get_node_path(node); } else if (strncmp(sad, "Linux/Alpha aboot for ISO filesystem.", 37) != 0 || sad[37] != 0) { return 0; /* Want to see either boot file or genisoimage string */ } } sai->system_area_options = (6 << 2); return 1; } struct iso_impsysa_result { char *buf; int byte_count; char **lines; int line_count; }; static int iso_impsysa_result_new(struct iso_impsysa_result **r, int flag) { int ret; LIBISO_ALLOC_MEM(*r, struct iso_impsysa_result, 1); (*r)->buf = NULL; (*r)->lines = NULL; ret = 1; ex: if (ret <= 0) { LIBISO_FREE_MEM(*r); *r = NULL; } return ret; } static void iso_impsysa_result_destroy(struct iso_impsysa_result **r, int flag) { if (*r == NULL) return; if ((*r)->buf != NULL) free((*r)->buf); if ((*r)->lines != NULL) free((*r)->lines); free(*r); *r = NULL; } static void iso_impsysa_line(struct iso_impsysa_result *target, char *msg) { if (target->buf != NULL) strcpy(target->buf + target->byte_count, msg); if (target->lines != NULL) target->lines[target->line_count] = target->buf + target->byte_count; target->byte_count += strlen(msg) + 1; target->line_count++; } static void iso_impsysa_report_text(struct iso_impsysa_result *target, char *msg, char *path, int flag) { if (strlen(msg) + strlen(path) >= ISO_MAX_SYSAREA_LINE_LENGTH) sprintf(msg + strlen(msg), "(too long to show here)"); else strcat(msg, path); iso_impsysa_line(target, msg); } static void iso_impsysa_reduce_na(uint32_t block, uint32_t *na, uint32_t claim) { if ((*na == 0 || *na > claim) && block < claim) *na = claim; } static int iso_impsysa_reduce_next_above(IsoImage *image, uint32_t block, uint32_t *next_above, int flag) { int i, section_count, ret; struct iso_imported_sys_area *sai; struct el_torito_boot_image *img; struct iso_file_section *sections = NULL; sai = image->imported_sa_info; if (sai == NULL) return 0; /* PVD, path table, root directory of active and of first session */ for (i = 0; i < sai->num_meta_struct_blocks; i++) iso_impsysa_reduce_na(block, next_above, sai->meta_struct_blocks[i]); /* Partition tables */ for (i = 0; i < sai->mbr_req_count; i++) { iso_impsysa_reduce_na(block, next_above, (uint32_t) (sai->mbr_req[i]->start_block / 4)); iso_impsysa_reduce_na(block, next_above, (uint32_t) ((sai->mbr_req[i]->start_block + sai->mbr_req[i]->block_count) / 4)); } for (i = 0; i < sai->gpt_req_count; i++) { iso_impsysa_reduce_na(block, next_above, (uint32_t) (sai->gpt_req[i]->start_block / 4)); iso_impsysa_reduce_na(block, next_above, (uint32_t) ((sai->gpt_req[i]->start_block + sai->gpt_req[i]->block_count) / 4)); } for (i = 0; i < sai->apm_req_count; i++) { iso_impsysa_reduce_na(block, next_above, (uint32_t) (sai->apm_req[i]->start_block / (2048 / sai->apm_block_size))); iso_impsysa_reduce_na(block, next_above, (uint32_t) ((sai->apm_req[i]->start_block + sai->apm_req[i]->block_count) / (2048 / sai->apm_block_size))); } if (image->bootcat != NULL) { if (image->bootcat->node != NULL) iso_impsysa_reduce_na(block, next_above, image->bootcat->node->lba); for (i= 0; i < image->bootcat->num_bootimages; i++) { img = image->bootcat->bootimages[i]; ret = iso_file_get_old_image_sections(img->image, §ion_count, §ions, 0); if (ret > 0 && section_count > 0) if (block != sections[0].block) iso_impsysa_reduce_na(block, next_above, sections[0].block); if (sections != NULL) { free(sections); sections = NULL; } } } iso_impsysa_reduce_na(block, next_above, sai->image_size); return 1; } /* @param flag bit0= try to estimate the size if no path is found */ static void iso_impsysa_report_blockpath(IsoImage *image, struct iso_impsysa_result *target, char *msg, uint32_t start_block, int flag) { int ret; char *path = NULL, *cpt; IsoNode *node; uint32_t next_above = 0; uint32_t size; ret = iso_tree_get_node_of_block(image, NULL, start_block, &node, &next_above, 0); if (ret <= 0) { if (!(flag & 1)) return; /* Look for next claimed block for estimating file size. next_above already holds the best data file candidate. */ ret = iso_impsysa_reduce_next_above(image, start_block, &next_above, 0); if (ret < 0) return; if (next_above == 0) return; size = next_above - start_block; /* Replace in msg "path" by "blks", report number in blocks of 2048 */ cpt = strstr(msg, "path"); if (cpt == NULL) return; path = iso_alloc_mem(strlen(msg) + 20, 1, 0); if (path == NULL) return; strcpy(path, msg); memcpy(path + (cpt - msg), "blks", 4); sprintf(path + strlen(path), "%u", (unsigned int) size); iso_impsysa_report_text(target, path, "", 0); free(path); return; } path = iso_tree_get_node_path(node); if (path != NULL) { iso_impsysa_report_text(target, msg, path, 0); free(path); } } static int iso_impsysa_report(IsoImage *image, struct iso_impsysa_result *target, int flag) { char *msg = NULL, *local_name = NULL, *path; int i, j, sa_type, sao, sa_sub, ret, idx; size_t local_len; struct iso_imported_sys_area *sai; struct iso_mbr_partition_request *part; struct iso_gpt_partition_request *gpt_entry; struct iso_apm_partition_request *apm_entry; static char *alignments[4] = {"auto", "on", "off", "all"}; IsoWriteOpts *opts = NULL; struct iso_sun_disk_label_entry *sparc_entry; sai = image->imported_sa_info; LIBISO_ALLOC_MEM(msg, char, ISO_MAX_SYSAREA_LINE_LENGTH); if (sai == NULL) {ret = 0; goto ex;} if (!sai->is_not_zero) {ret = 0; goto ex;} sao = sai->system_area_options; sprintf(msg, "System area options: 0x%-8.8x", (unsigned int) sao); iso_impsysa_line(target, msg); /* Human readable form of system_area_options */ sa_type = (sao >> 2) & 63; sa_sub = (sao >> 10) & 15; strcpy(msg, "System area summary:"); if (sa_type == 0) { if ((sao & 3) || sa_sub == 1 || sa_sub == 2) { /* >>> ??? Should isohybrid and protective-msdos-label be combinable ? */ strcat(msg, " MBR"); if (sao & 2) strcat(msg, " isohybrid"); else if (sao & 1) strcat(msg, " protective-msdos-label"); else if (sa_sub == 1) { strcat(msg, " CHRP"); } if ((sao & (1 << 14)) && !(sao & 2)) strcat(msg, " grub2-mbr"); sprintf(msg + strlen(msg), " cyl-align-%s", alignments[(sao >> 8) & 3]); } else if (sai->prep_part_start > 0 && sai->prep_part_size > 0) { strcat(msg, " PReP"); } else if (sai->mbr_req_count > 0) { strcat(msg, " MBR"); } else { strcat(msg, " not-recognized"); } } else if (sa_type == 1) { strcat(msg, " MIPS-Big-Endian"); } else if (sa_type == 2) { strcat(msg, " MIPS-Little-Endian"); } else if (sa_type == 3) { strcat(msg, " SUN-SPARC-Disk-Label"); } else if (sa_type == 4 || sa_type == 5) { sprintf(msg + strlen(msg), " HP-PA-PALO"); } else if (sa_type == 6) { sprintf(msg + strlen(msg), " DEC-Alpha"); } else { sprintf(msg + strlen(msg), " unkown-system-area-type-%d", sa_type); } if (sai->gpt_req_count > 0) strcat(msg, " GPT"); if (sai->apm_req_count > 0) strcat(msg, " APM"); iso_impsysa_line(target, msg); /* System area summary */ sprintf(msg, "ISO image size/512 : %.f", ((double) sai->image_size) * 4.0); iso_impsysa_line(target, msg); if (sai->mbr_req_count > 0 && sa_type == 0) { sprintf(msg, "Partition offset : %d", sai->partition_offset); iso_impsysa_line(target, msg); } if (sa_type >= 4 && sa_type <= 5) { sprintf(msg, "PALO header version: %d", sai->hppa_hdrversion); iso_impsysa_line(target, msg); sprintf(msg, "HP-PA cmdline : "); iso_impsysa_report_text(target, msg, sai->hppa_cmdline, 0); sprintf(msg, "HP-PA boot files : ByteAddr ByteSize Path"); iso_impsysa_line(target, msg); sprintf(msg, "HP-PA 32-bit kernel: %10u %10u ", sai->hppa_kern32_adr, sai->hppa_kern32_len); iso_impsysa_report_text(target, msg, sai->hppa_kernel_32 != NULL ? sai->hppa_kernel_32 : "(not found in ISO)", 0); sprintf(msg, "HP-PA 64-bit kernel: %10u %10u ", sai->hppa_kern64_adr, sai->hppa_kern64_len); iso_impsysa_report_text(target, msg, sai->hppa_kernel_64 != NULL ? sai->hppa_kernel_64 : "(not found in ISO)", 0); sprintf(msg, "HP-PA ramdisk : %10u %10u ", sai->hppa_ramdisk_adr, sai->hppa_ramdisk_len); iso_impsysa_report_text(target, msg, sai->hppa_ramdisk != NULL ? sai->hppa_ramdisk : "(not found in ISO)", 0); sprintf(msg, "HP-PA bootloader : %10u %10u ", sai->hppa_bootloader_adr, sai->hppa_bootloader_len); iso_impsysa_report_text(target, msg, sai->hppa_bootloader != NULL ? sai->hppa_bootloader : "(not found in ISO)", 0); } else if (sa_type == 6) { sprintf(msg, "DEC Alpha ldr size : %.f", (double) sai->alpha_boot_image_size); iso_impsysa_line(target, msg); sprintf(msg, "DEC Alpha ldr adr : %.f", (double) sai->alpha_boot_image_adr); iso_impsysa_line(target, msg); if (sai->alpha_boot_image != NULL) { sprintf(msg, "DEC Alpha ldr path : %s", sai->alpha_boot_image); iso_impsysa_line(target, msg); } } if (sai->mbr_req_count > 0) { sprintf(msg, "MBR heads per cyl : %d", sai->partition_heads_per_cyl); iso_impsysa_line(target, msg); sprintf(msg, "MBR secs per head : %d", sai->partition_secs_per_head); iso_impsysa_line(target, msg); sprintf(msg, "MBR partition table: N Status Type Start Blocks"); iso_impsysa_line(target, msg); } for (i = 0; i < sai->mbr_req_count; i++) { part = sai->mbr_req[i]; sprintf(msg, "MBR partition : %3d 0x%2.2x 0x%2.2x %11.f %11.f", part->desired_slot, (unsigned int) part->status_byte, (unsigned int) part->type_byte, (double) part->start_block, (double) part->block_count); iso_impsysa_line(target, msg); } for (i = 0; i < sai->mbr_req_count; i++) { part = sai->mbr_req[i]; if (part->block_count == 0) continue; sprintf(msg, "MBR partition path : %3d ", part->desired_slot); if (part->image_path != NULL) { iso_impsysa_report_text(target, msg, part->image_path, 0); } else { iso_impsysa_report_blockpath(image, target, msg, (uint32_t) (part->start_block / 4), 0); } } if (sai->prep_part_start > 0 && sai->prep_part_size > 0) { sprintf(msg, "PReP boot partition: %u %u", sai->prep_part_start, sai->prep_part_size); iso_impsysa_line(target, msg); } if (sa_type == 1) { sprintf(msg, "MIPS-BE volume dir : N Name Block Bytes"); iso_impsysa_line(target, msg); for (i = 0; i < sai->num_mips_boot_files; i++) { sprintf(msg, "MIPS-BE boot entry : %3d %8s %10u %10u", i + 1, sai->mips_vd_entries[i]->name, sai->mips_vd_entries[i]->boot_block, sai->mips_vd_entries[i]->boot_bytes); iso_impsysa_line(target, msg); if (sai->mips_boot_file_paths[i] != NULL) { sprintf(msg, "MIPS-BE boot path : %3d ", i + 1); iso_impsysa_report_text(target, msg, sai->mips_boot_file_paths[i], 0); } } } else if (sa_type == 2) { sprintf(msg, "MIPS-LE boot map : LoadAddr ExecAddr SegmentSize SegmentStart"); iso_impsysa_line(target, msg); sprintf(msg, "MIPS-LE boot params: %10u %10u %10u %10u", sai->mipsel_p_vaddr, sai->mipsel_e_entry, sai->mipsel_p_filesz, sai->mipsel_seg_start); iso_impsysa_line(target, msg); if (sai->mipsel_boot_file_path != NULL) { sprintf(msg, "MIPS-LE boot path : "); iso_impsysa_report_text(target, msg, sai->mipsel_boot_file_path, 0); sprintf(msg, "MIPS-LE elf offset : %u", sai->mipsel_p_offset); iso_impsysa_line(target, msg); } } else if (sa_type == 3) { sprintf(msg, "SUN SPARC disklabel: %s", sai->sparc_disc_label); iso_impsysa_line(target, msg); sprintf(msg, "SUN SPARC secs/head: %d", sai->sparc_secs_per_head); iso_impsysa_line(target, msg); sprintf(msg, "SUN SPARC heads/cyl: %d", sai->sparc_heads_per_cyl); iso_impsysa_line(target, msg); sprintf(msg, "SUN SPARC partmap : N IdTag Perms StartCyl NumBlocks"); iso_impsysa_line(target, msg); for (i = 0; i < sai->sparc_entry_count; i++) { sparc_entry = sai->sparc_entries + i; sprintf(msg, "SUN SPARC partition: %3d 0x%4.4x 0x%4.4x %10u %10u", sparc_entry->idx, sparc_entry->id_tag, sparc_entry->permissions, sparc_entry->start_cyl, sparc_entry->num_blocks); iso_impsysa_line(target, msg); } if (sai->sparc_grub2_core_adr > 0) { sprintf(msg, "SPARC GRUB2 core : %.f %u", (double) sai->sparc_grub2_core_adr, sai->sparc_grub2_core_size); iso_impsysa_line(target, msg); if (sai->sparc_core_node != NULL) { if(sai->sparc_core_node_path != NULL) { path = sai->sparc_core_node_path; } else { path = iso_tree_get_node_path( (IsoNode *) sai->sparc_core_node); } if (path != NULL) { sprintf(msg, "SPARC GRUB2 path : "); iso_impsysa_report_text(target, msg, path, 0); if (sai->sparc_core_node_path == NULL) free(path); } } } } if (sai->gpt_req_count > 0) { sprintf(msg, "GPT : N Info"); iso_impsysa_line(target, msg); if (sai->gpt_head_crc_should != sai->gpt_head_crc_found) { sprintf(msg, "GPT CRC should be : 0x%8.8x to match first 92 GPT header block bytes", sai->gpt_head_crc_should); iso_impsysa_line(target, msg); sprintf(msg, "GPT CRC found : 0x%8.8x matches all 512 bytes of GPT header block", sai->gpt_head_crc_found); iso_impsysa_line(target, msg); } if (sai->gpt_array_crc_should != sai->gpt_array_crc_found) { sprintf(msg, "GPT array CRC wrong: should be 0x%8.8x , found 0x%8.8x", sai->gpt_array_crc_should, sai->gpt_array_crc_found); iso_impsysa_line(target, msg); } if (sai->gpt_backup_comments != NULL) { if (sai->gpt_backup_comments[0]) { sprintf(msg, "GPT backup problems: "); iso_impsysa_report_text(target, msg, sai->gpt_backup_comments, 0); } } sprintf(msg, "GPT disk GUID : "); iso_util_bin_to_hex(msg + 26, sai->gpt_disk_guid, 16, 0); iso_impsysa_line(target, msg); sprintf(msg, "GPT entry array : %u %u %s", (unsigned int) sai->gpt_part_start, (unsigned int) sai->gpt_max_entries, sai->gpt_req_flags & 1 ? "overlapping" : "separated"); iso_impsysa_line(target, msg); sprintf(msg, "GPT lba range : %.f %.f %.f", (double) sai->gpt_first_lba, (double) sai->gpt_last_lba, (double) sai->gpt_backup_lba); iso_impsysa_line(target, msg); ret = iso_write_opts_new(&opts, 0); if (ret < 0) goto ex; ret = iso_write_opts_set_output_charset(opts, "UTF-16LE"); if (ret < 0) goto ex; } for (i = 0; i < sai->gpt_req_count; i++) { gpt_entry = sai->gpt_req[i]; idx = gpt_entry->idx; sprintf(msg, "GPT partition name : %3d ", idx); for (j = 72; j >= 2; j -= 2) if (gpt_entry->name[j - 2] || gpt_entry->name[j - 1]) break; iso_util_bin_to_hex(msg + 26, gpt_entry->name, j, 0); iso_impsysa_line(target, msg); if (j > 0) ret = iso_conv_name_chars(opts, (char *) gpt_entry->name, j, &local_name, &local_len, 0 | 512 | (1 << 15)); else ret = 0; if (ret == 1 && local_len <= 228) { sprintf(msg, "GPT partname local : %3d ", idx); memcpy(msg + 26, local_name, local_len); LIBISO_FREE_MEM(local_name); local_name = NULL; msg[26 + local_len] = 0; iso_impsysa_line(target, msg); } sprintf(msg, "GPT partition GUID : %3d ", idx); iso_util_bin_to_hex(msg + 26, gpt_entry->partition_guid, 16, 0); iso_impsysa_line(target, msg); sprintf(msg, "GPT type GUID : %3d ", idx); iso_util_bin_to_hex(msg + 26, gpt_entry->type_guid, 16, 0); iso_impsysa_line(target, msg); sprintf(msg, "GPT partition flags: %3d 0x%8.8x%8.8x", idx, (unsigned int) ((gpt_entry->flags >> 32) & 0xffffffff), (unsigned int) (gpt_entry->flags & 0xffffffff)); iso_impsysa_line(target, msg); sprintf(msg, "GPT start and size : %3d %.f %.f", idx, (double) gpt_entry->start_block, (double) gpt_entry->block_count); iso_impsysa_line(target, msg); if (gpt_entry->block_count == 0) continue; sprintf(msg, "GPT partition path : %3d ", idx); if (gpt_entry->image_path != NULL) { iso_impsysa_report_text(target, msg, gpt_entry->image_path, 0); } else { iso_impsysa_report_blockpath(image, target, msg, (uint32_t) (gpt_entry->start_block / 4), 0); } } if (sai->apm_req_count > 0) { sprintf(msg, "APM : N Info"); iso_impsysa_line(target, msg); sprintf(msg, "APM block size : %u", sai->apm_block_size); iso_impsysa_line(target, msg); sprintf(msg, "APM gap fillers : %d", sai->apm_gap_count); iso_impsysa_line(target, msg); } for (i = 0; i < sai->apm_req_count; i++) { apm_entry = sai->apm_req[i]; idx = i + 1; sprintf(msg, "APM partition name : %3d %s", idx, apm_entry->name); iso_impsysa_line(target, msg); sprintf(msg, "APM partition type : %3d %s", idx, apm_entry->type); iso_impsysa_line(target, msg); sprintf(msg, "APM start and size : %3d %.f %.f", idx, (double) apm_entry->start_block, (double) apm_entry->block_count); iso_impsysa_line(target, msg); if (apm_entry->block_count == 0) continue; sprintf(msg, "APM partition path : %3d ", idx); if (apm_entry->image_path != NULL) { iso_impsysa_report_text(target, msg, apm_entry->image_path, 0); } else { iso_impsysa_report_blockpath(image, target, msg, (uint32_t) (apm_entry->start_block / (2048 / sai->apm_block_size)), 0); } } ret = 1; ex: LIBISO_FREE_MEM(local_name); if (opts != NULL) iso_write_opts_free(opts); LIBISO_FREE_MEM(msg); return ret; } static int iso_report_result_destroy(char ***result, int flag) { if (*result == NULL) return ISO_SUCCESS; if ((*result)[0] != NULL) /* points to the whole multi-line buffer */ free((*result)[0]); free(*result); *result = NULL; return ISO_SUCCESS; } static int iso_report_help(char **doc, char ***result, int *line_count, int flag) { int i, count = 0; char *buf = NULL; *line_count = 0; for (i = 0; strcmp(doc[i], "@END_OF_DOC@") != 0; i++) count += strlen(doc[i]) + 1; if (i == 0) return ISO_SUCCESS; *result = calloc(i, sizeof(char *)); if (*result == NULL) return ISO_OUT_OF_MEM; buf = calloc(1, count); if (buf == NULL) { free(*result); *result = NULL; return ISO_OUT_OF_MEM; } *line_count = i; count = 0; for (i = 0; strcmp(doc[i], "@END_OF_DOC@") != 0; i++) { strcpy(buf + count, doc[i]); (*result)[i] = buf + count; count += strlen(doc[i]) + 1; } return ISO_SUCCESS; } static uint32_t iso_impsysa_hdd_emul_size(IsoImage *image, IsoDataSource *src, uint32_t lba, int flag) { uint32_t max_size = 0, start_lba, num_blocks; int i, ret; uint8_t *buffer = NULL; /* Obtain first block of image */ LIBISO_ALLOC_MEM(buffer, uint8_t, 2048); ret = src->read_block(src, lba, buffer); if (ret < 0) goto ex; /* Check for magic number of MBR */ if (buffer[510] != 0x55 || buffer[511] != 0xaa) goto ex; for (i = 0; i < 4; i++) { start_lba = iso_read_lsb(buffer + 454 + 16 * i, 4); num_blocks = iso_read_lsb(buffer + 458 + 16 * i, 4); if (start_lba + num_blocks > max_size) max_size = start_lba + num_blocks; } ex:; LIBISO_FREE_MEM(buffer); return max_size; } static int iso_eltorito_report(IsoImage *image, struct iso_impsysa_result *target, int flag) { char *msg = NULL, emul_code[6], pltf[5], *path; int i, j, ret, section_count; uint32_t lba, *lba_mem = NULL; struct el_torito_boot_catalog *bootcat; IsoBoot *bootnode; struct el_torito_boot_image *img; struct iso_file_section *sections = NULL; static char emul_names[5][6] = {"none", "fd1.2", "fd1.4", "fd2.8", "hd"}; static char pltf_names[3][5] = {"BIOS", "PPC", "Mac"}; static int num_emuls = 5, num_pltf = 3; bootcat = image->bootcat; LIBISO_ALLOC_MEM(msg, char, ISO_MAX_SYSAREA_LINE_LENGTH); if (bootcat == NULL) {ret= 0; goto ex;} bootnode = image->bootcat->node; if (bootnode == NULL) {ret= 0; goto ex;} sprintf(msg, "El Torito catalog : %u %u", (unsigned int) bootnode->lba, (unsigned int) (bootnode->size + 2047) / 2048); iso_impsysa_line(target, msg); path = iso_tree_get_node_path((IsoNode *) bootnode); if (path != NULL) { sprintf(msg, "El Torito cat path : "); iso_impsysa_report_text(target, msg, path, 0); free(path); } if (bootcat->num_bootimages > 0) { sprintf(msg, "El Torito images : N Pltf B Emul Ld_seg Hdpt Ldsiz LBA"); iso_impsysa_line(target, msg); LIBISO_ALLOC_MEM(lba_mem, uint32_t, bootcat->num_bootimages); } for (i= 0; i < bootcat->num_bootimages; i++) { img = bootcat->bootimages[i]; if (img->type < num_emuls) strcpy(emul_code, emul_names[img->type]); else sprintf(emul_code, "0x%2.2x", (unsigned int) img->type); if (img->platform_id < num_pltf) strcpy(pltf, pltf_names[img->platform_id]); else if(img->platform_id == 0xef) strcpy(pltf, "UEFI"); else sprintf(pltf, "0x%2.2x", (unsigned int) img->platform_id); lba = 0xffffffff; ret = iso_file_get_old_image_sections(img->image, §ion_count, §ions, 0); if (ret > 0 && section_count > 0) lba = sections[0].block; lba_mem[i]= lba; if (sections != NULL) { free(sections); sections = NULL; } sprintf(msg, "El Torito boot img : %3d %4s %c %5s 0x%4.4x 0x%2.2x %5u %10u", i + 1, pltf, img->bootable ? 'y' : 'n', emul_code, (unsigned int) img->load_seg, (unsigned int) img->partition_type, (unsigned int) img->load_size, (unsigned int) lba); iso_impsysa_line(target, msg); } for (i= 0; i < bootcat->num_bootimages; i++) { img = bootcat->bootimages[i]; sprintf(msg, "El Torito img path : %3d ", i + 1); if (img->image_path != NULL) { iso_impsysa_report_text(target, msg, img->image_path, 0); } else if (lba_mem[i] != 0xffffffff) { iso_impsysa_report_blockpath(image, target, msg, lba_mem[i], 1); } if (lba_mem[i] != 0xffffffff) { if (img->type == 4 && img->emul_hdd_size > 0) { sprintf(msg, "El Torito hdsiz/512: %3d %u", i + 1, (unsigned int) img->emul_hdd_size); iso_impsysa_line(target, msg); } } sprintf(msg, "El Torito img opts : %3d ", i + 1); if (img->seems_boot_info_table) strcat(msg, "boot-info-table "); if (img->seems_isohybrid_capable) strcat(msg, "isohybrid-suitable "); if (img->seems_grub2_boot_info) strcat(msg, "grub2-boot-info "); if (strlen(msg) > 27) { msg[strlen(msg) - 1] = 0; iso_impsysa_line(target, msg); } for (j = 0; j < (int) sizeof(img->id_string); j++) if (img->id_string[j]) break; if (j < (int) sizeof(img->id_string)) { sprintf(msg, "El Torito id string: %3d ", i + 1); iso_util_bin_to_hex(msg + strlen(msg), img->id_string, 24 + 4 * (i > 0), 0); } for (j = 0; j < (int) sizeof(img->selection_crit); j++) if (img->selection_crit[j]) break; if (j < (int) sizeof(img->selection_crit) && i > 0) { sprintf(msg, "El Torito sel crit : %3d ", i + 1); iso_util_bin_to_hex(msg + strlen(msg), img->selection_crit, 20, 0); } } ret = ISO_SUCCESS; ex:; LIBISO_FREE_MEM(msg); LIBISO_FREE_MEM(lba_mem); return ret; } /* API */ /* @param flag bit1= do not report system area but rather reply help text bit15= dispose result from previous call */ static int iso_image_report_boot_eqp(IsoImage *image, int what, char ***result, int *line_count, int flag) { int ret; char **doc; struct iso_impsysa_result *target = NULL; static char *sysarea_doc[] = { ISO_SYSAREA_REPORT_DOC , ISO_SYSAREA_REPORT_DOC_MBR , ISO_SYSAREA_REPORT_DOC_GPT1 , ISO_SYSAREA_REPORT_DOC_GPT2 , ISO_SYSAREA_REPORT_DOC_APM , ISO_SYSAREA_REPORT_DOC_MIPS , ISO_SYSAREA_REPORT_DOC_SUN , ISO_SYSAREA_REPORT_DOC_HPPA , ISO_SYSAREA_REPORT_DOC_ALPHA , "@END_OF_DOC@" }; static char *eltorito_doc[] = { ISO_ELTORITO_REPORT_DOC , "@END_OF_DOC@" }; if (flag & (1 << 15)) return iso_report_result_destroy(result, 0); if (flag & 1) { if (what == 0) doc = sysarea_doc; else doc = eltorito_doc; return iso_report_help(doc, result, line_count, 0); } *result = NULL; *line_count = 0; ret = iso_impsysa_result_new(&target, 0); if (ret < 0) goto ex; if (what == 0) ret = iso_impsysa_report(image, target, 0); else ret = iso_eltorito_report(image, target, 0); if (ret <= 0) goto ex; target->buf = calloc(1, target->byte_count + 1); target->lines = calloc(target->line_count + 1, sizeof(char *)); if (target->buf == NULL || target->lines == NULL) {ret = ISO_OUT_OF_MEM; goto ex;} target->lines[0] = target->buf; /* even if no lines get reported */ target->byte_count = 0; target->line_count = 0; if (what == 0) ret = iso_impsysa_report(image, target, 0); else ret = iso_eltorito_report(image, target, 0); if (ret <= 0) goto ex; /* target to result */ *result = target->lines; target->lines = NULL; target->buf = NULL; *line_count = target->line_count; ret = ISO_SUCCESS; ex: iso_impsysa_result_destroy(&target, 0); return ret; } /* API */ /* @param flag bit1= do not report system area but rather reply help text bit15= dispose result from previous call */ int iso_image_report_system_area(IsoImage *image, char ***result, int *line_count, int flag) { return iso_image_report_boot_eqp(image, 0, result, line_count, flag); } static int iso_record_pvd_blocks(IsoImage *image, IsoDataSource *src, uint32_t block, int flag) { int ret; uint8_t *buffer = NULL; struct iso_imported_sys_area *sai; LIBISO_ALLOC_MEM(buffer, uint8_t, 2048); sai = image->imported_sa_info; sai->meta_struct_blocks[sai->num_meta_struct_blocks++] = block; ret = src->read_block(src, block, buffer); if (ret < 0) goto ex; /* Verify that it is a PVD of a volume not larger than sai->image_size */ if (buffer[0] != 1 || strncmp((char *) buffer + 1, "CD001", 5) != 0) {ret = 0; goto ex;} if (iso_read_lsb(buffer + 80, 4) > sai->image_size) {ret = 0; goto ex;} /* L pathtable, Opt L, M pathtable , Opt M, Root directory extent*/ sai->meta_struct_blocks[sai->num_meta_struct_blocks++] = iso_read_lsb(buffer + 140, 4); sai->meta_struct_blocks[sai->num_meta_struct_blocks++] = iso_read_lsb(buffer + 144, 4); sai->meta_struct_blocks[sai->num_meta_struct_blocks++] = iso_read_lsb(buffer + 148, 4); sai->meta_struct_blocks[sai->num_meta_struct_blocks++] = iso_read_lsb(buffer + 152, 4); sai->meta_struct_blocks[sai->num_meta_struct_blocks++] = iso_read_lsb(buffer + 158, 4); ret = ISO_SUCCESS; ex:; LIBISO_FREE_MEM(buffer); return ret; } static int iso_record_meta_struct_blocks(IsoImage *image, IsoDataSource *src, int flag) { int ret; struct iso_imported_sys_area *sai; sai = image->imported_sa_info; ret = iso_record_pvd_blocks(image, src, sai->pvd_block, 0); if (ret < 0) goto ex; /* Try block 32 as first session PVD */ ret = iso_record_pvd_blocks(image, src, 16, 0); if (ret < 0) goto ex; if (ret == 0 && sai->pvd_block > 16) { /* No emulated multi-session: Try block 16 as first session PVD */ ret = iso_record_pvd_blocks(image, src, 16, 0); if (ret < 0) goto ex; } ret = ISO_SUCCESS; ex: return ret; } /* @param flag bit0= Pre-run: Only assess partition tables. Do not refer to loaded tree or El Torito boot equipment. */ static int iso_analyze_system_area(IsoImage *image, IsoDataSource *src, struct iso_read_opts *opts, uint32_t image_size, int flag) { int ret, i, sao, sa_type, sa_sub; iso_imported_sa_unref(&(image->imported_sa_info), 0); ret = iso_imported_sa_new(&(image->imported_sa_info), 0); if (ret < 0) goto ex; for (i = 0; i < 32768; i++) if (image->system_area_data[i] != 0) break; if (i < 32768) image->imported_sa_info->is_not_zero = 1; image->imported_sa_info->image_size = image_size; image->imported_sa_info->pvd_block = opts->block + 16; ret = iso_analyze_mbr(image, src, flag & 1); if (ret < 0) goto ex; ret = iso_analyze_gpt(image, src, flag & 1); if (ret < 0) goto ex; ret = iso_analyze_apm(image, src, flag & 1); if (ret < 0) goto ex; sao = image->imported_sa_info->system_area_options; sa_type = (sao >> 2) & 0x3f; sa_sub = (sao >> 10) & 0xf; if (sa_type == 0 && !((sao & 3) || sa_sub == 1 || sa_sub == 2)) { ret = iso_analyze_mips(image, src, flag & 1); if (ret < 0) goto ex; if (ret == 0) { ret = iso_analyze_mipsel(image, src, flag & 1); if (ret < 0) goto ex; } if (ret == 0) { ret = iso_analyze_sun(image, src, flag & 1); if (ret < 0) goto ex; } } if (sa_type == 0 && !((sao & 3) || sa_sub == 1)) { /* HP-PA PALO v5 can look like generic MBR */ ret = iso_analyze_hppa(image, src, flag & 1); if (ret < 0) goto ex; /* DEC Alpha has checksum bytes where MBR has its magic number */ if (ret == 0) { ret = iso_analyze_alpha_boot(image, src, flag & 1); if (ret < 0) goto ex; } } if (!(flag & 1)) { ret = iso_record_meta_struct_blocks(image, src, 0); if (ret < 0) goto ex; } ret = ISO_SUCCESS; ex:; image->imported_sa_info->overall_return = ret; return ret; } /* API */ /* @param flag bit1= do not report system area but rather reply help text bit15= dispose result from previous call */ int iso_image_report_el_torito(IsoImage *image, char ***reply, int *line_count, int flag) { return iso_image_report_boot_eqp(image, 1, reply, line_count, flag); } /* Set named feature name to num_value or pt_value depending on its type. @param pt_size >=0 : size with .type == 1 , <0 : determine pt_size by strlen() @return -1= name unknown , 1= ok */ int iso_img_features_set_named_pts(IsoReadImageFeatures *f, char *name, int64_t num_value, void *pt_value, ssize_t pt_size) { int i; for (i = 0; i < f->num_named_feat; i++) if (strcmp(f->named_feat[i].name, name) == 0) break; if (i >= f->num_named_feat) return -1; if (f->named_feat[i].type == 1) { if (f->named_feat[i].pt_value != NULL) free(f->named_feat[i].pt_value); if (pt_size >= 0) f->named_feat[i].pt_size = pt_size; else f->named_feat[i].pt_size = strlen(pt_value) + 1; f->named_feat[i].pt_value = calloc(1, f->named_feat[i].pt_size); if (f->named_feat[i].pt_value == NULL) return ISO_OUT_OF_MEM; memcpy(f->named_feat[i].pt_value, pt_value, f->named_feat[i].pt_size); } else { f->named_feat[i].num_value = num_value; } f->named_feat[i].valid = 1; return 1; } int iso_img_features_set_named(IsoReadImageFeatures *f, char *name, int64_t num_value, void *pt_value) { int ret; ret = iso_img_features_set_named_pts(f, name, num_value, pt_value, (ssize_t) -1); return ret; } static int iso_img_feature_to_text(struct iso_read_img_feature *f, char **result) { int count= 0, pass, i; char num_text[81], *t= NULL; *result = NULL; if (f->valid != 1) return(0); for (pass = 0; pass < 2; pass++) { if (pass == 0) count = strlen(f->name) + 1; else sprintf(t, "%s=", f->name); if (f->type == 0) { /* >>> sprintf int64_t */; sprintf(num_text, "%.f", (double) f->num_value); if (pass == 0) count += strlen(num_text); else strcat(t, num_text); } else if (strcmp(f->name, "tree_loaded_text") == 0 || strcmp(f->name, "rr_reloc_dir") == 0 || strcmp(f->name, "scdbackup_tag_name") == 0 || strcmp(f->name, "scdbackup_tag_time") == 0) { if (pass == 0) count += f->pt_size; else strcat(t, (char *) f->pt_value); } else if (strcmp(f->name, "hfsp_serial_number") == 0) { if (pass == 0) { count += 16; } else { for (i = 0; i < 8 && i < f->pt_size; i++) sprintf(t + strlen(t), "%2.2X", ((uint8_t *) f->pt_value)[i]); } } if (pass == 0) { *result = calloc(1, count + 1); if (*result == NULL) return ISO_OUT_OF_MEM; t = *result; } } return 1; } /* API */ /* Get named feature as text, num_value, or pt_value depending on its type. @return 0 = Feature was not yet examined. Reply is not valid. 1 = Reply is valid ISO_UNDEF_READ_FEATURE = Given name is not known <0 = other error */ int iso_read_image_feature_named(IsoReadImageFeatures *f, char *name, char **text, int *type, int64_t *num_value, void **pt_value, size_t *pt_size) { int i, ret; *num_value = 0; *pt_value = NULL; *pt_size = 0; for (i = 0; i < f->num_named_feat; i++) if (strcmp(f->named_feat[i].name, name) == 0) break; if (i >= f->num_named_feat) return ISO_UNDEF_READ_FEATURE; if (text != NULL) { ret = iso_img_feature_to_text(&(f->named_feat[i]), text); if (ret < 0) return ret; } if (f->named_feat[i].type == 1) { if (pt_value != NULL) *pt_value = f->named_feat[i].pt_value; if (pt_size != NULL) *pt_size = (size_t) f->named_feat[i].pt_size; } else { if (num_value != NULL) *num_value = f->named_feat[i].num_value; } *type = f->named_feat[i].type; return 1; } /* API */ /* Get all valid named features as one string */ int iso_read_image_features_text(IsoReadImageFeatures *f, int with_values, char **feature_text) { int i, ret, count = 0, pass; char *r = NULL; *feature_text = NULL; for (pass = 0; pass < 2; pass++) { for (i = 0; i < f->num_named_feat; i++) { if(f->named_feat[i].valid != 1) continue; if (!with_values) { r = strdup(f->named_feat[i].name); if (r == NULL) return ISO_OUT_OF_MEM; } else { ret = iso_img_feature_to_text(&(f->named_feat[i]), &r); if (ret < 0) return ret; if (ret == 0) continue; } if (pass == 0) { count += strlen(r) + 1; } else { strcat(*feature_text, r); if (i < f->num_named_feat - 1) strcat(*feature_text, "\n"); } free(r); } if (pass == 0) { *feature_text = calloc(1, count + 1); if (*feature_text == NULL) return ISO_OUT_OF_MEM; } } return 1; } /* @param cset which charset to test for: d or a @param with_separators 0=no separators 1=accept "." and ";" */ static int iso_is_valid_id(char *name, char cset, int with_separators, int main_length, int suffix_length) { int l = 0, ml = -1, sep2_count= 0 ; char *cpt; int (*valid)(char c); if (cset == 'a') valid = valid_a_char; else valid = valid_d_char; for(cpt = name; *cpt != 0; cpt++) { if (*cpt == '.') { if (!with_separators) return 0; if (ml >= 0) return 0; /* more than one . */ ml = l; l = 0; if (main_length > 0 && ml > main_length) return 0; continue; } else if (*cpt == ';') { if (!with_separators) return 0; if (sep2_count > 0 || ml < 0) return 0; /* multiple ; or ; before . */ sep2_count++; continue; } if (!(*valid)(*cpt)) return 0; l++; } if (ml >= 0 && suffix_length > 0 && l > suffix_length) return(0); return 1; } /* File identifier: d or d1, . , ; level 1: 8.3 level 2 and 3: no length restrictions Directory identifier: 0x00, 0x01, d or d1 volset_id: d volume_id: d publisher_id: a data_preparer_id: a system_id: a application_id: a copyright_file_id: d , . , ; abstract_file_id: d , . , ; biblio_file_id: d , . , ; */ static int iso_image_has_relaxed_vol_atts(IsoImage *image) { if (!iso_is_valid_id(image->volset_id, 'c', 0, 0, 0)) return 1; if (!iso_is_valid_id(image->volume_id, 'c', 0, 0, 0)) return 1; if (!iso_is_valid_id(image->publisher_id, 'a', 0, 0, 0)) return 1; if (!iso_is_valid_id(image->data_preparer_id, 'a', 0, 0, 0)) return 1; if (!iso_is_valid_id(image->system_id, 'a', 0, 0, 0)) return 1; if (!iso_is_valid_id(image->application_id, 'a', 0, 0, 0)) return 1; if (!iso_is_valid_id(image->copyright_file_id, 'd', 1, 0, 0)) return 1; if (!iso_is_valid_id(image->abstract_file_id, 'd', 1, 0, 0)) return 1; if (!iso_is_valid_id(image->biblio_file_id, 'd', 1, 0, 0)) return 1; return 0; } static int iso_image_assess_tree_compliance(IsoImage *image, struct iso_read_opts *opts, IsoReadImageFeatures *features) { int ret; IsoWriteOpts *wopts; if (features == NULL) return 2; /* (Put features here which do not depend on deeper tree inspection) */ wopts = image->tree_compliance; if (wopts == NULL || !(opts->read_features & 1)) return 2; /* From here on only features which depend on deeper tree inspection */ if (features->tree_loaded == 0 && features->rr_loaded) { /* >>> hardlinks */; /* >>> ??? always_gmt */; } else if (features->tree_loaded == 0 && !features->rr_loaded) { /* ECMA-119 tree */ if ((ret = iso_img_features_set_named(features, "iso_level", (int64_t) (wopts->iso_level), NULL)) < 0) return ret; if ((ret = iso_img_features_set_named(features, "untranslated_name_len", (int64_t) (wopts->untranslated_name_len), NULL)) < 0) return ret; if ((ret = iso_img_features_set_named(features, "allow_dir_id_ext", (int64_t) (wopts->allow_dir_id_ext), NULL)) < 0) return ret; if ((ret = iso_img_features_set_named(features, "omit_version_numbers", (int64_t) (wopts->omit_version_numbers), NULL)) < 0) return ret; if ((ret = iso_img_features_set_named(features, "allow_deep_paths", (int64_t) (wopts->allow_deep_paths), NULL)) < 0) return ret; if ((ret = iso_img_features_set_named(features, "allow_longer_paths", (int64_t) (wopts->allow_longer_paths), NULL)) < 0) return ret; if ((ret = iso_img_features_set_named(features, "max_37_char_filenames", (int64_t) (wopts->max_37_char_filenames), NULL)) < 0) return ret; if ((ret = iso_img_features_set_named(features, "no_force_dots", (int64_t) (wopts->no_force_dots), NULL)) < 0) return ret; if ((ret = iso_img_features_set_named(features, "allow_lowercase", (int64_t) (wopts->allow_lowercase), NULL)) < 0) return ret; if ((ret = iso_img_features_set_named(features, "allow_full_ascii", (int64_t) (wopts->allow_full_ascii), NULL)) < 0) return ret; } else if (features->tree_loaded == 1) { /* Joliet tree */ if ((ret = iso_img_features_set_named(features, "joliet_longer_paths", (int64_t) (wopts->joliet_longer_paths), NULL)) < 0) return ret; if ((ret = iso_img_features_set_named(features, "joliet_long_names", (int64_t) (wopts->joliet_long_names), NULL)) < 0) return ret; if ((ret = iso_img_features_set_named(features, "joliet_utf16", (int64_t) (wopts->joliet_utf16), NULL)) < 0) return ret; /* (wopts->no_force_dots still has bit0 as set by tree_loaded == 0) */ if ((ret = iso_img_features_set_named(features, "no_force_dots", (int64_t) (wopts->no_force_dots), NULL)) < 0) return ret; } else if (features->tree_loaded == 2) { /* ISO 9960:1999 tree */ /* ??? is there anything to record ? */; } return 1; } void iso_image_assess_ecma119_name(IsoImage *image, struct stat *info, char *path, char *name) { char *dpt, *spt; int lname, lpref, lid, lpid, slash_count = 0, is_dir, i, was_dot; IsoWriteOpts *tc; tc = image->tree_compliance; if (tc == NULL) return; is_dir = S_ISDIR(info->st_mode); dpt = strchr(name, '.'); spt = strchr(name, ';'); lname = strlen(name); if (spt == NULL) { lid = lname; lpid = strlen(path); } else { lid = spt - name; lpid = strlen(path) - lname + lid; } if (dpt == NULL) { if (!is_dir) tc->no_force_dots |= 1; } else { if (is_dir) tc->allow_dir_id_ext = 1; lpref = dpt - name; if (tc->iso_level <= 1) { if (lpref > 8) tc->iso_level = 2; if (spt == NULL) { if (lname - lpref - 1 > 3) tc->iso_level = 2; } else { if (spt - dpt - 1 > 3) tc->iso_level = 2; } } } if (spt == NULL && !is_dir) tc->omit_version_numbers = 1; for (i = 0; path[i] != 0; i++) if (path[i] == '/') slash_count++; if (slash_count + is_dir > 8) tc->allow_deep_paths = 1; if (lpid > 255) tc->allow_longer_paths = 1; if (lid > 31 && lid <= 37) { tc->max_37_char_filenames = 1; } else if (lid > 37) { if (tc->untranslated_name_len < (unsigned int) lid) tc->untranslated_name_len = lid; } was_dot = 1; for (i = 0; i < lid; i++) { if (islower(name[i])) { tc->allow_lowercase = 1; } else if (name[i] == '.' && !was_dot) { was_dot = 1; } else if (!valid_d_char(name[i])) { tc->allow_full_ascii = 1; } } } void iso_image_assess_joliet_name(IsoImage *image, struct stat *info, char *path, char *name) { char *local_charset, *joliet_str = NULL, *str; size_t str_len, joliet_len; int ret, pass, is_dir; IsoWriteOpts *tc; tc = image->tree_compliance; if (tc == NULL) return; local_charset = iso_get_local_charset(0); for (pass = 0; pass < 2; pass++) { if (pass == 0) { str = name; } else { str = path; } str_len = strlen(str); ret = strnconvl(str, local_charset, "UCS-2BE", str_len, &joliet_str, &joliet_len); if (ret != 1) { ret = strnconvl(str, local_charset, "UTF-16BE", str_len, &joliet_str, &joliet_len); if (ret != 1) { /* >>> Warn ? return ? */; } else { tc->joliet_utf16 = 1; } } if (joliet_str != NULL) { free(joliet_str); joliet_str = NULL; } if (pass == 0) { if (joliet_len > 64) tc->joliet_long_names = 1; is_dir = S_ISDIR(info->st_mode); if (strchr(name, ';') == NULL && !is_dir) tc->omit_version_numbers = 2; if (strchr(name, '.') == NULL && !is_dir) tc->no_force_dots |= 2; } else { if (joliet_len > 240) tc->joliet_longer_paths = 1; } } } int iso_image_import(IsoImage *image, IsoDataSource *src, struct iso_read_opts *opts, IsoReadImageFeatures **features) { int ret, hflag, i, idx; IsoImageFilesystem *fs; IsoFilesystem *fsback; IsoNodeBuilder *blback; IsoDir *oldroot; IsoFileSource *newroot; _ImageFsData *data; struct el_torito_boot_catalog *oldbootcat; uint8_t *rpt; IsoFileSource *boot_src; IsoNode *node, *boot_image_node; char *old_checksum_array = NULL; char checksum_type[81]; uint32_t checksum_size, truncate_mode, truncate_length; size_t size, attr_value_length; char *attr_value; unsigned char *aa_string = NULL; void *ctx = NULL; char md5[16]; struct el_torito_boot_catalog *catalog = NULL; ElToritoBootImage *boot_image = NULL; int features_allocated = 0; static char *tree_loaded_names[3]= {"ISO9660", "Joliet", "ISO9660:1999"}; int root_has_aaip = 0, rrip_version_1_10; unsigned long img_size; if (image == NULL || src == NULL || opts == NULL) { return ISO_NULL_POINTER; } opts->truncate_mode = image->truncate_mode; opts->truncate_length = image->truncate_length; ret = iso_image_filesystem_new(src, opts, image->id, &fs); if (ret < 0) { return ret; } data = fs->data; image->tree_loaded = 0; if (data->iso_root_block == data->svd_root_block) image->tree_loaded = 1; else if (data->iso_root_block == data->evd_root_block && data->iso_root_block != data->pvd_root_block) image->tree_loaded = 2; image->rr_loaded = (data->rr != RR_EXT_NO); if (opts->keep_import_src) { iso_data_source_ref(src); image->import_src = src; } if (opts->load_system_area) { if (image->system_area_data != NULL) free(image->system_area_data); image->system_area_data = calloc(32768, 1); if (image->system_area_data == NULL) { iso_filesystem_unref(fs); return ISO_OUT_OF_MEM; } image->system_area_options = 0; /* Read 32768 bytes */ for (i = 0; i < 16; i++) { rpt = (uint8_t *) (image->system_area_data + i * 2048); ret = src->read_block(src, opts->block + i, rpt); if (ret < 0) { iso_filesystem_unref(fs); return ret; } } } /* get root from filesystem */ ret = fs->get_root(fs, &newroot); if (ret < 0) { iso_filesystem_unref(fs); return ret; } if (newroot == NULL) { iso_filesystem_unref(fs); return ISO_NO_ROOT_DIR; } /* Lookup character set even if no AAIP loading is enabled */ ret = iso_file_source_get_aa_string(newroot, &aa_string, 2); if (ret == 1 && aa_string != NULL) { ret = iso_aa_lookup_attr(aa_string, "isofs.cs", &attr_value_length, &attr_value, 0); free(aa_string); root_has_aaip = 1; } else { ret = 0; } if (ret == 1) { if (data->auto_input_charset & 1) { if (data->input_charset != NULL) free(data->input_charset); data->input_charset = attr_value; if (!(opts->read_features & 4)) iso_msg_submit(image->id, ISO_GENERAL_NOTE, 0, "Learned from ISO image: input character set '%.80s'", attr_value); } else { if (!(opts->read_features & 4)) iso_msg_submit(image->id, ISO_GENERAL_NOTE, 0, "Ignored character set name recorded in ISO image: '%.80s'", attr_value); free(attr_value); } attr_value = NULL; } /* backup image filesystem, builder and root */ fsback = image->fs; blback = image->builder; oldroot = image->root; oldbootcat = image->bootcat; /* could be NULL */ image->bootcat = NULL; old_checksum_array = image->checksum_array; image->checksum_array = NULL; /* create new builder */ ret = iso_image_builder_new(blback, &image->builder); if (ret < 0) { goto import_revert; } image->fs = fs; /* create new root, and set root attributes from source */ ret = iso_node_new_root(&image->root); if (ret < 0) { iso_node_builder_unref(image->builder); goto import_revert; } { struct stat info; /* I know this will not fail */ iso_file_source_lstat(newroot, &info); image->root->node.mode = info.st_mode; image->root->node.uid = info.st_uid; image->root->node.gid = info.st_gid; image->root->node.atime = info.st_atime; image->root->node.mtime = info.st_mtime; image->root->node.ctime = info.st_ctime; /* This might fail in iso_node_add_xinfo() */ ret = src_aa_to_node(newroot, &(image->root->node), 0); if (ret < 0) { iso_node_builder_unref(image->builder); goto import_revert; } /* Attach ino as xinfo if valid */ if (info.st_ino != 0 && !data->make_new_ino) { ret = iso_node_set_ino(&(image->root->node), info.st_ino, 0); if (ret < 0) { iso_node_builder_unref(image->builder); goto import_revert; } } } ret = iso_root_get_isofsnt(&(image->root->node), &truncate_mode, &truncate_length, 0); if (ret == 1 && (int) truncate_mode == image->truncate_mode && image->truncate_mode == 1 && truncate_length >= 64 && truncate_length <= 255 && (int) truncate_length != image->truncate_length) { data->truncate_mode = opts->truncate_mode = image->truncate_mode = truncate_mode; data->truncate_length = opts->truncate_length = image->truncate_length = truncate_length; if (!(opts->read_features & 4)) { iso_msg_submit(image->id, ISO_TRUNCATE_ISOFSNT, 0, "File name truncation length changed by loaded image info: %d", (int) truncate_length); } } /* Pre-assessment of System Area because image->imported_sa_info with assessed partitions is needed in create_boot_img_filesrc(). Nevertheless, some properties can only be determined later, when other image properties have been assessed. So there will be another run of iso_analyze_system_area() without flag bit0 when more of the imported image is known. */ if (opts->load_system_area && image->system_area_data != NULL && !(opts->read_features & 4)) { /* Preliminary assessment. Not complete because some aspects of the filesystem have not been loaded yet. */ ret = iso_analyze_system_area(image, src, opts, data->nblocks, 1); if (ret < 0) { iso_msg_submit(-1, ISO_SYSAREA_PROBLEMS, 0, "Problem encountered during inspection of System Area:"); iso_msg_submit(-1, ISO_SYSAREA_PROBLEMS, 0, iso_error_to_msg(ret)); } } /* if old image has el-torito, add a new catalog */ if (data->eltorito) { catalog = calloc(1, sizeof(struct el_torito_boot_catalog)); if (catalog == NULL) { ret = ISO_OUT_OF_MEM; iso_node_builder_unref(image->builder); goto import_revert; } catalog->num_bootimages = 0; for (idx = 0; idx < data->num_bootimgs; idx++) { boot_image = calloc(1, sizeof(ElToritoBootImage)); if (boot_image == NULL) { ret = ISO_OUT_OF_MEM; iso_node_builder_unref(image->builder); goto import_revert; } boot_image->image = NULL; boot_image->image_path = NULL; boot_image->bootable = data->boot_flags[idx] & 1; boot_image->type = data->media_types[idx]; boot_image->partition_type = data->partition_types[idx]; boot_image->load_seg = data->load_segs[idx]; boot_image->load_size = data->load_sizes[idx]; boot_image->platform_id = data->platform_ids[idx]; memcpy(boot_image->id_string, data->id_strings[idx], 28); memcpy(boot_image->selection_crit, data->selection_crits, 20); boot_image->appended_idx = -1; boot_image->appended_start = data->bootblocks[idx]; if (boot_image->type == 4) { boot_image->emul_hdd_size = iso_impsysa_hdd_emul_size( image, src, data->bootblocks[idx], 0); } else { boot_image->emul_hdd_size = 0; } catalog->bootimages[catalog->num_bootimages] = boot_image; boot_image = NULL; catalog->num_bootimages++; } for ( ; idx < Libisofs_max_boot_imageS; idx++) catalog->bootimages[idx] = NULL; image->bootcat = catalog; catalog = NULL; /* So it does not get freed */ } /* recursively add image */ if (opts->read_features & 1) { image->do_deeper_tree_inspection = 1; if (image->tree_compliance == NULL) { ret = iso_write_opts_new(&(image->tree_compliance), 0); if (ret < 0) { iso_node_builder_unref(image->builder); goto import_revert; } image->tree_compliance->iso_level = 1; } } ret = iso_add_dir_src_rec(image, image->root, newroot); if (ret < 0) { /* error during recursive image addition */ iso_node_builder_unref(image->builder); goto import_revert; } issue_ucs2_warning_summary(data->joliet_ucs2_failures); issue_collision_warning_summary(image->collision_warnings); /* Take over inode management from IsoImageFilesystem. data->inode_counter is supposed to hold the maximum PX inode number. */ image->inode_counter = data->inode_counter; if ((data->px_ino_status & (2 | 4 | 8)) || opts->make_new_ino) { /* Attach new inode numbers to any node which does not have one, resp. to all nodes in case of opts->make_new_ino */ if (opts->make_new_ino) hflag = 1; /* Equip all data files with new unique inos */ else hflag = 2 | 4 | 8; /* Equip any file type if it has ino == 0 */ ret = img_make_inos(image, image->root, hflag); if (ret < 0) { iso_node_builder_unref(image->builder); goto import_revert; } } if (data->eltorito) { /* if catalog and boot image nodes were not filled, we create them here */ for (idx = 0; idx < image->bootcat->num_bootimages; idx++) { if (image->bootcat->bootimages[idx]->image != NULL) continue; ret = create_boot_img_filesrc(fs, image, idx, &boot_src); if (ret < 0) { iso_node_builder_unref(image->builder); goto import_revert; } ret = image_builder_create_node(image->builder, image, boot_src, NULL, &node); iso_file_source_unref(boot_src); /* Now owned by node */ if (ret < 0) { iso_node_builder_unref(image->builder); goto import_revert; } if (image->bootcat->bootimages[idx]->image != NULL) { /* Already added to bootimages in image_builder_create_node(). * Now it has one refcount for tree and one for bootimages. * But it will not go to tree. So unref. */ iso_node_unref(node); } else { image->bootcat->bootimages[idx]->image = (IsoFile*)node; } /* warn about hidden images */ if (!(opts->read_features & 4)) { img_size = iso_file_get_size((IsoFile *) node); if (image->bootcat->bootimages[idx]->platform_id == 0xef) { iso_msg_submit(image->id, ISO_ELTO_EFI_HIDDEN, 0, "Found hidden El-Torito image for EFI."); iso_msg_submit(image->id, ISO_GENERAL_NOTE, 0, "EFI image start and load size: %lu * 2048 , %lu * 512", (unsigned long int) image->bootcat->bootimages[idx]->appended_start, (unsigned long int) image->bootcat->bootimages[idx]->load_size); iso_msg_submit(image->id, ISO_GENERAL_NOTE, 0, "Roughly estimated EFI image size: %lu bytes", img_size); } else { iso_msg_submit(image->id, ISO_EL_TORITO_HIDDEN, 0, "Found hidden El-Torito image for Non-EFI platform.", img_size); iso_msg_submit(image->id, ISO_EL_TORITO_HIDDEN, 0, "Roughly estimated image size: %lu bytes", img_size); iso_msg_submit(image->id, ISO_EL_TORITO_HIDDEN, 0, "Image modification or boot image patching may lead to bad results."); } } } /* Try to obtain boot image paths */ for (idx = 0; idx < image->bootcat->num_bootimages; idx++) { boot_image_node = (IsoNode *) image->bootcat->bootimages[idx]->image; if (boot_image_node == NULL) continue; if (image->bootcat->bootimages[idx]->image_path != NULL) free(image->bootcat->bootimages[idx]->image_path); image->bootcat->bootimages[idx]->image_path = iso_tree_get_node_path(boot_image_node); } if (image->bootcat->node == NULL) { IsoNode *node; IsoBoot *bootcat; node = calloc(1, sizeof(IsoBoot)); if (node == NULL) { ret = ISO_OUT_OF_MEM; iso_node_builder_unref(image->builder); goto import_revert; } bootcat = (IsoBoot *) node; bootcat->lba = data->catblock; bootcat->size = data->catsize; bootcat->content = NULL; if (bootcat->size > 0) { bootcat->content = calloc(1, bootcat->size); if (bootcat->content == NULL) { free(node); ret = ISO_OUT_OF_MEM; iso_node_builder_unref(image->builder); goto import_revert; } memcpy(bootcat->content, data->catcontent, bootcat->size); } node->type = LIBISO_BOOT; node->mode = S_IFREG; node->refcount = 1; image->bootcat->node = (IsoBoot*)node; } } iso_node_builder_unref(image->builder); /* set volume attributes */ iso_image_set_volset_id(image, data->volset_id); iso_image_set_volume_id(image, data->volume_id); iso_image_set_publisher_id(image, data->publisher_id); iso_image_set_data_preparer_id(image, data->data_preparer_id); iso_image_set_system_id(image, data->system_id); iso_image_set_application_id(image, data->application_id); iso_image_set_copyright_file_id(image, data->copyright_file_id); iso_image_set_abstract_file_id(image, data->abstract_file_id); iso_image_set_biblio_file_id(image, data->biblio_file_id); iso_image_set_pvd_times(image, data->creation_time, data->modification_time, data->expiration_time, data->effective_time); if ((opts->read_features & 2) && features != NULL) { if (*features == NULL) { ret = ISO_NULL_POINTER; goto import_revert; } } else if (features != NULL) { *features = malloc(sizeof(IsoReadImageFeatures)); if (*features == NULL) { ret = ISO_OUT_OF_MEM; goto import_revert; } features_allocated = 1; (*features)->num_named_feat = 0; (*features)->named_feat = NULL; for (i = 0; iso_read_img_feature_list[i].name[0] != 0; i++); (*features)->named_feat = calloc(i, sizeof(struct iso_read_img_feature)); if ((*features)->named_feat == NULL) { ret = ISO_OUT_OF_MEM; goto import_revert; } for (i = 0; iso_read_img_feature_list[i].name[0] != 0; i++) memcpy((*features)->named_feat + i, iso_read_img_feature_list + i, sizeof(struct iso_read_img_feature)); (*features)->num_named_feat = i; (*features)->hasJoliet = data->joliet; if ((ret = iso_img_features_set_named((*features), "joliet", (int64_t) ((*features)->hasJoliet), NULL)) < 0) goto import_revert; if (image->tree_compliance != NULL) iso_write_opts_set_joliet(image->tree_compliance, (*features)->hasJoliet); (*features)->hasRR = data->rr_version != 0; if ((ret = iso_img_features_set_named((*features), "rockridge", (int64_t) ((*features)->hasRR), NULL)) < 0) goto import_revert; if (image->tree_compliance != NULL) iso_write_opts_set_rockridge(image->tree_compliance, (*features)->hasRR); (*features)->hasIso1999 = data->iso1999; if ((ret = iso_img_features_set_named((*features), "iso1999", (int64_t) ((*features)->hasIso1999), NULL)) < 0) goto import_revert; if (image->tree_compliance != NULL) iso_write_opts_set_iso1999(image->tree_compliance, (*features)->hasIso1999); (*features)->hasElTorito = data->eltorito; if ((ret = iso_img_features_set_named((*features), "eltorito", (int64_t) ((*features)->hasElTorito), NULL)) < 0) goto import_revert; (*features)->size = data->nblocks; if ((ret = iso_img_features_set_named((*features), "size", (int64_t) ((*features)->size), NULL)) < 0) goto import_revert; if ((ret = iso_img_features_set_named((*features), "relaxed_vol_atts", (int64_t) iso_image_has_relaxed_vol_atts(image), NULL)) < 0) goto import_revert; if (image->tree_compliance != NULL) iso_write_opts_set_relaxed_vol_atts(image->tree_compliance, !!iso_image_has_relaxed_vol_atts(image)); } if (*features != NULL) { (*features)->tree_loaded = image->tree_loaded; if ((ret = iso_img_features_set_named((*features), "tree_loaded", (int64_t)(*features)->tree_loaded, NULL)) < 0) goto import_revert; if ((ret = iso_img_features_set_named((*features), "tree_loaded_text", (int64_t) 0, tree_loaded_names[(*features)->tree_loaded])) < 0) goto import_revert; (*features)->rr_loaded = image->rr_loaded; if ((ret = iso_img_features_set_named((*features), "rr_loaded", (int64_t) ((*features)->rr_loaded), NULL)) < 0) goto import_revert; } if (features != NULL && image->tree_loaded == 0) { /* ISO 9660 tree with or without Rock Ridge */ if (image->rr_loaded) { rrip_version_1_10 = (data->rr_version == RR_EXT_110); if ((ret = iso_img_features_set_named((*features), "rrip_version_1_10", (int64_t) rrip_version_1_10, NULL)) < 0) goto import_revert; if (image->tree_compliance != NULL) iso_write_opts_set_rrip_version_1_10(image->tree_compliance, rrip_version_1_10); if ((ret = iso_img_features_set_named((*features), "rrip_1_10_px_ino", (int64_t) ((data->px_ino_status & 1) && rrip_version_1_10), NULL)) < 0) goto import_revert; if (image->tree_compliance != NULL) iso_write_opts_set_rrip_1_10_px_ino(image->tree_compliance, (data->px_ino_status & 1) && rrip_version_1_10); } if (data->aaip_version > 0) { if ((ret = iso_img_features_set_named((*features), "aaip", (int64_t) 1, NULL)) < 0) goto import_revert; if (image->tree_compliance != NULL) iso_write_opts_set_aaip(image->tree_compliance, 1); } else { if ((ret = iso_img_features_set_named((*features), "aaip", (int64_t) !!root_has_aaip, NULL)) < 0) goto import_revert; if (image->tree_compliance != NULL) iso_write_opts_set_aaip(image->tree_compliance, !!root_has_aaip); } if ((ret = iso_img_features_set_named((*features), "aaip_susp_1_10", (int64_t) (root_has_aaip && data->aaip_version == 0), NULL)) < 0) goto import_revert; if (image->tree_compliance != NULL) iso_write_opts_set_aaip_susp_1_10(image->tree_compliance, root_has_aaip && data->aaip_version == 0); if ((ret = iso_img_features_set_named((*features), "record_md5_session", (int64_t) !!(data->md5_checked & 3), NULL)) < 0) goto import_revert; if ((ret = iso_img_features_set_named((*features), "record_md5_files", (int64_t) 0, NULL)) < 0) goto import_revert; if (image->tree_compliance != NULL) iso_write_opts_set_record_md5(image->tree_compliance, !!(data->md5_checked & 3), 0); } if (data->md5_load) { /* Read checksum array */ ret = iso_root_get_isofsca((IsoNode *) image->root, &(image->checksum_start_lba), &(image->checksum_end_lba), &(image->checksum_idx_count), &checksum_size, checksum_type, 0); if (ret > 0) if (checksum_size != 16 || strcmp(checksum_type, "MD5") != 0) ret = 0; if (ret > 0 && image->checksum_idx_count > 1) { size = image->checksum_idx_count / 128; if (size * 128 < image->checksum_idx_count) size++; image->checksum_array = calloc(size, 2048); if (image->checksum_array == NULL) { ret = ISO_OUT_OF_MEM; goto import_revert; } /* Load from image->checksum_end_lba */; for (i = 0; i < (int) size; i++) { rpt = (uint8_t *) (image->checksum_array + i * 2048); ret = src->read_block(src, image->checksum_end_lba + i, rpt); if (ret <= 0) goto import_revert; } if (features != NULL && (image->tree_loaded == 0 || data->aaip_version > 0 || root_has_aaip)) { if ((ret = iso_img_features_set_named((*features), "record_md5_files", (int64_t) 1, NULL)) < 0) goto import_revert; if (image->tree_compliance != NULL) iso_write_opts_set_record_md5(image->tree_compliance, !!(data->md5_checked & 3), 1); } /* Compute MD5 and compare with recorded MD5 */ ret = iso_md5_start(&ctx); if (ret < 0) { ret = ISO_OUT_OF_MEM; goto import_revert; } for (i = 0; i < (int) image->checksum_idx_count - 1; i++) iso_md5_compute(ctx, image->checksum_array + i * 16, 16); iso_md5_end(&ctx, md5); for (i = 0; i < 16; i++) if (md5[i] != image->checksum_array[ (image->checksum_idx_count - 1) * 16 + i] ) break; if (i < 16) { iso_msg_submit(image->id, ISO_MD5_AREA_CORRUPTED, 0, "MD5 checksum array appears damaged and not trustworthy for verifications."); free(image->checksum_array); image->checksum_array = NULL; image->checksum_idx_count = 0; } } } ret = iso_image_eval_boot_info_table(image, opts, src, data->nblocks, 0); if (ret < 0) goto import_revert; /* Second run of iso_analyze_system_area() after El Torito equipment and file tree have been assessed */ if (opts->load_system_area && image->system_area_data != NULL && !(opts->read_features & 4)) { ret = iso_analyze_system_area(image, src, opts, data->nblocks, 0); if (ret < 0) { iso_msg_submit(-1, ISO_SYSAREA_PROBLEMS, 0, "Problem encountered during inspection of System Area:"); iso_msg_submit(-1, ISO_SYSAREA_PROBLEMS, 0, iso_error_to_msg(ret)); } } ret = iso_image_assess_tree_compliance(image, opts, *features); if (ret < 0) goto import_revert; ret = ISO_SUCCESS; goto import_cleanup; import_revert:; iso_node_unref((IsoNode*)image->root); el_torito_boot_catalog_free(image->bootcat); image->root = oldroot; oldroot = NULL; image->bootcat = oldbootcat; oldbootcat = NULL; image->checksum_array = old_checksum_array; old_checksum_array = NULL; if (features_allocated) { iso_read_image_features_destroy(*features); *features = NULL; } import_cleanup:; /* recover backed fs and builder */ image->fs = fsback; image->builder = blback; /* free old root */ if (oldroot != NULL) iso_node_unref((IsoNode*)oldroot); /* free old boot catalog */ if (oldbootcat != NULL) el_torito_boot_catalog_free(oldbootcat); if (catalog != NULL) el_torito_boot_catalog_free(catalog); if (boot_image != NULL) free((char *) boot_image); iso_file_source_unref(newroot); fs->close(fs); iso_filesystem_unref(fs); if (old_checksum_array != NULL) free(old_checksum_array); if (ctx != NULL) iso_md5_end(&ctx, md5); return ret; } const char *iso_image_fs_get_volset_id(IsoImageFilesystem *fs) { _ImageFsData *data = (_ImageFsData*) fs->data; return data->volset_id; } const char *iso_image_fs_get_volume_id(IsoImageFilesystem *fs) { _ImageFsData *data = (_ImageFsData*) fs->data; return data->volume_id; } const char *iso_image_fs_get_publisher_id(IsoImageFilesystem *fs) { _ImageFsData *data = (_ImageFsData*) fs->data; return data->publisher_id; } const char *iso_image_fs_get_data_preparer_id(IsoImageFilesystem *fs) { _ImageFsData *data = (_ImageFsData*) fs->data; return data->data_preparer_id; } const char *iso_image_fs_get_system_id(IsoImageFilesystem *fs) { _ImageFsData *data = (_ImageFsData*) fs->data; return data->system_id; } const char *iso_image_fs_get_application_id(IsoImageFilesystem *fs) { _ImageFsData *data = (_ImageFsData*) fs->data; return data->application_id; } const char *iso_image_fs_get_copyright_file_id(IsoImageFilesystem *fs) { _ImageFsData *data = (_ImageFsData*) fs->data; return data->copyright_file_id; } const char *iso_image_fs_get_abstract_file_id(IsoImageFilesystem *fs) { _ImageFsData *data; data = (_ImageFsData*) fs->data; return data->abstract_file_id; } const char *iso_image_fs_get_biblio_file_id(IsoImageFilesystem *fs) { _ImageFsData *data = (_ImageFsData*) fs->data; return data->biblio_file_id; } int iso_read_opts_new(IsoReadOpts **opts, int profile) { IsoReadOpts *ropts; if (opts == NULL) { return ISO_NULL_POINTER; } if (profile != 0) { return ISO_WRONG_ARG_VALUE; } ropts = calloc(1, sizeof(IsoReadOpts)); if (ropts == NULL) { return ISO_OUT_OF_MEM; } ropts->file_mode = 0444; ropts->dir_mode = 0555; ropts->noaaip = 1; ropts->ecma119_map = 1; ropts->joliet_map = 1; ropts->nomd5 = 1; ropts->load_system_area = 0; ropts->keep_import_src = 0; ropts->truncate_mode = 1; ropts->truncate_length = LIBISOFS_NODE_NAME_MAX; ropts->read_features = 0; *opts = ropts; return ISO_SUCCESS; } void iso_read_opts_free(IsoReadOpts *opts) { if (opts == NULL) { return; } free(opts->input_charset); free(opts); } int iso_read_opts_set_start_block(IsoReadOpts *opts, uint32_t block) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->block = block; return ISO_SUCCESS; } int iso_read_opts_set_no_rockridge(IsoReadOpts *opts, int norr) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->norock = norr ? 1 :0; return ISO_SUCCESS; } int iso_read_opts_set_no_joliet(IsoReadOpts *opts, int nojoliet) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->nojoliet = nojoliet ? 1 :0; return ISO_SUCCESS; } int iso_read_opts_set_no_iso1999(IsoReadOpts *opts, int noiso1999) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->noiso1999 = noiso1999 ? 1 :0; return ISO_SUCCESS; } int iso_read_opts_set_no_aaip(IsoReadOpts *opts, int noaaip) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->noaaip = noaaip ? 1 : 0; return ISO_SUCCESS; } int iso_read_opts_set_no_md5(IsoReadOpts *opts, int no_md5) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->nomd5 = no_md5 == 2 ? 2 : no_md5 == 1 ? 1 : 0; return ISO_SUCCESS; } int iso_read_opts_set_new_inos(IsoReadOpts *opts, int new_inos) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->make_new_ino = new_inos ? 1 : 0; return ISO_SUCCESS; } int iso_read_opts_set_preferjoliet(IsoReadOpts *opts, int preferjoliet) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->preferjoliet = preferjoliet ? 1 :0; return ISO_SUCCESS; } int iso_read_opts_set_ecma119_map(IsoReadOpts *opts, int ecma119_map) { if (opts == NULL) { return ISO_NULL_POINTER; } if (ecma119_map < 0 || ecma119_map > 3) return 0; opts->ecma119_map = ecma119_map; return ISO_SUCCESS; } int iso_read_opts_set_joliet_map(IsoReadOpts *opts, int joliet_map) { if (opts == NULL) return ISO_NULL_POINTER; if (joliet_map < 0 || joliet_map > 1) return 0; opts->joliet_map = joliet_map; return ISO_SUCCESS; } int iso_read_opts_set_default_uid(IsoReadOpts *opts, uid_t uid) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->uid = uid; return ISO_SUCCESS; } int iso_read_opts_set_default_gid(IsoReadOpts *opts, gid_t gid) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->gid = gid; return ISO_SUCCESS; } int iso_read_opts_set_default_permissions(IsoReadOpts *opts, mode_t file_perm, mode_t dir_perm) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->file_mode = file_perm; opts->dir_mode = dir_perm; return ISO_SUCCESS; } int iso_read_opts_set_input_charset(IsoReadOpts *opts, const char *charset) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->input_charset = charset ? strdup(charset) : NULL; return ISO_SUCCESS; } int iso_read_opts_auto_input_charset(IsoReadOpts *opts, int mode) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->auto_input_charset = mode; return ISO_SUCCESS; } int iso_read_opts_load_system_area(IsoReadOpts *opts, int mode) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->load_system_area = mode & 1; return ISO_SUCCESS; } int iso_read_opts_keep_import_src(IsoReadOpts *opts, int mode) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->keep_import_src = mode & 1; return ISO_SUCCESS; } /* (Not API) */ int iso_read_opts_read_features(IsoReadOpts *opts, int mode) { if (opts == NULL) { return ISO_NULL_POINTER; } opts->read_features = mode & 7; return ISO_SUCCESS; } /** * Destroy an IsoReadImageFeatures object obtained with iso_image_import. */ void iso_read_image_features_destroy(IsoReadImageFeatures *f) { int i; if (f == NULL) return; if (f->named_feat != NULL) { for (i = 0; i < f->num_named_feat; i++) if (f->named_feat[i].pt_value != NULL) free(f->named_feat[i].pt_value); free (f->named_feat); } free(f); } /** * Get the size (in 2048 byte block) of the image, as reported in the PVM. */ uint32_t iso_read_image_features_get_size(IsoReadImageFeatures *f) { return f->size; } /** * Whether RockRidge extensions are present in the image imported. */ int iso_read_image_features_has_rockridge(IsoReadImageFeatures *f) { return f->hasRR; } /** * Whether Joliet extensions are present in the image imported. */ int iso_read_image_features_has_joliet(IsoReadImageFeatures *f) { return f->hasJoliet; } /** * Whether the image is recorded according to ISO 9660:1999, i.e. it has * a version 2 Enhanced Volume Descriptor. */ int iso_read_image_features_has_iso1999(IsoReadImageFeatures *f) { return f->hasIso1999; } /** * Whether El-Torito boot record is present present in the image imported. */ int iso_read_image_features_has_eltorito(IsoReadImageFeatures *f) { return f->hasElTorito; } /** * Tells what directory tree was loaded: * 0= ISO 9660 , 1 = Joliet , 2 = ISO 9660:1999 */ int iso_read_image_features_tree_loaded(IsoReadImageFeatures *f) { return f->tree_loaded; } /** * Tells whether Rock Ridge information was used while loading the tree. */ int iso_read_image_features_rr_loaded(IsoReadImageFeatures *f) { return f->rr_loaded; } /** * Get the start addresses and the sizes of the data extents of a file node * if it was imported from an old image. * * @param file * The file * @param section_count * Returns the number of extent entries in sections arrays * @param sections * Returns the array of file sections. Apply free() to dispose it. * @param flag * Reserved for future usage, submit 0 * @return * 1 if there are valid extents (file comes from old image), * 0 if file was newly added, i.e. it does not come from an old image, * < 0 error */ int iso_file_get_old_image_sections(IsoFile *file, int *section_count, struct iso_file_section **sections, int flag) { if (file == NULL || section_count == NULL || sections == NULL) { return ISO_NULL_POINTER; } if (flag != 0) { return ISO_WRONG_ARG_VALUE; } *section_count = 0; *sections = NULL; if (file->from_old_session != 0) { /* * When file is from old session, we retrieve the original IsoFileSource * to get the sections. This break encapsultation, but safes memory as * we don't need to store the sections in the IsoFile node. */ IsoStream *stream = file->stream, *input_stream; FSrcStreamData *data; ImageFileSourceData *ifsdata; /* Get the most original stream */ while (1) { input_stream = iso_stream_get_input_stream(stream, 0); if (input_stream == NULL || input_stream == stream) break; stream = input_stream; } /* From here on it must be a stream with FSrcStreamData. */ /* ??? Shall one rather check : stream->class == extern IsoStreamIface fsrc_stream_class (its storage location is global in stream.c) */ if (stream->class->type[0] != 'f' || stream->class->type[1] != 's' || stream->class->type[2] != 'r' || stream->class->type[3] != 'c') return 0; data = stream->data; ifsdata = data->src->data; *section_count = ifsdata->nsections; if (*section_count <= 0) return 1; *sections = malloc(ifsdata->nsections * sizeof(struct iso_file_section)); if (*sections == NULL) { return ISO_OUT_OF_MEM; } memcpy(*sections, ifsdata->sections, ifsdata->nsections * sizeof(struct iso_file_section)); return 1; } return 0; } /* Rank two IsoFileSource by their eventual old image LBAs if still non-zero. Other IsoFileSource classes and zeroized LBAs will be ranked only roughly. flag bit0 preserves transitivity of the caller by evaluating ifs_class with non-zero block address as smaller than anything else. flag bit1 could harm reproducibility of ISO image output. @param flag bit0= if s1 exor s2 is of applicable class, then enforce a valid test result by comparing classes bit1= if both are applicable but also have sections[].block == 0 then enforce a valid test result by comparing object addresses. */ int iso_ifs_sections_cmp(IsoFileSource *s1, IsoFileSource *s2, int *cmp_ret, int flag) { int i; ImageFileSourceData *d1 = NULL, *d2 = NULL; IsoFileSourceIface *class1 = NULL, *class2 = NULL; /* Newly created IsoFileSrc from imported IsoFile (e.g. boot image) is not an applicable source. It must be kept from causing a decision with other non-applicables. */ if (s1 != NULL) { class1 = (IsoFileSourceIface *) s1->class; if (class1 == &ifs_class) { d1 = (ImageFileSourceData *) s1->data; if (d1->nsections > 0) if (d1->sections[0].block == 0) class1 = NULL; } } if (s2 != NULL) { class2 = (IsoFileSourceIface *) s2->class; if (class2 == &ifs_class) { d2 = (ImageFileSourceData *) s2->data; if (d2->nsections > 0) if (d2->sections[0].block == 0) class2 = NULL; } } if (class1 != &ifs_class && class2 != &ifs_class) { *cmp_ret = 0; return 0; } if (class1 != class2) { *cmp_ret = (class1 == &ifs_class ? -1 : 1); if (flag & 1) return 1; return 0; } if (d1->nsections != d2->nsections) { *cmp_ret = d1->nsections < d2->nsections ? -1 : 1; return 1; } if (d1->nsections == 0) { *cmp_ret = 0; return 1; } if (d1->sections[0].size < 1 || d2->sections[0].size < 1) { if (d1->sections[0].size > d2->sections[0].size) *cmp_ret = 1; else if (d1->sections[0].size < d2->sections[0].size) *cmp_ret = -1; else *cmp_ret = 0; return 1; } for (i = 0; i < d1->nsections; i++) { if (d1->sections[i].block != d2->sections[i].block) { *cmp_ret = (d1->sections[i].block < d2->sections[i].block ? -1 : 1); return 1; } if (d1->sections[i].size != d2->sections[i].size) { *cmp_ret = (d1->sections[i].size < d2->sections[i].size ? -1 : 1); return 1; } } *cmp_ret = 0; return 1; } /* API */ int iso_assess_written_features(IsoDataSource *src, IsoReadOpts *opts, IsoReadImageFeatures **features, IsoWriteOpts **write_opts) { int pass, ret, reuse_features = 0, omit, opts_mem[5]; IsoImage *image = NULL; /* Memorize read_opts values which might change */ opts_mem[0]= opts->norock; opts_mem[1]= opts->nojoliet; opts_mem[2]= opts->noiso1999; opts_mem[3]= opts->preferjoliet; opts_mem[4]= opts->read_features; if (features == NULL) return ISO_NULL_POINTER; *features = NULL; *write_opts = NULL; ret = iso_write_opts_new(write_opts, 0); if (ret < 0) goto ex; for (pass= 0; pass < 4; pass++) { ret = iso_image_new("ISOIMAGE", &image); if (ret < 0) goto ex; image->tree_compliance = *write_opts; iso_read_opts_set_no_rockridge(opts, 1); iso_read_opts_set_no_joliet(opts, 1); iso_read_opts_set_no_iso1999(opts, 1); iso_read_opts_set_preferjoliet(opts, 0); omit = 0; if (pass == 0) { image->tree_compliance->iso_level = 1; iso_read_opts_set_ecma119_map(opts, 0); } else if(pass == 1) { if (*features != NULL) if (!iso_read_image_features_has_rockridge(*features)) omit = 1; iso_read_opts_set_no_rockridge(opts, 0); } else if(pass == 2) { if (*features != NULL) if (!iso_read_image_features_has_joliet(*features)) omit = 1; iso_read_opts_set_no_joliet(opts, 0); } else if(pass == 3) { if (*features != NULL) if (!iso_read_image_features_has_iso1999(*features)) omit = 1; iso_read_opts_set_no_iso1999(opts, 0); } if (!omit) { iso_read_opts_read_features(opts, 1 | reuse_features | 4); ret = iso_image_import(image, src, opts, features); image->tree_compliance = NULL; if (ret < 0) goto ex; reuse_features = 2; } else { image->tree_compliance = NULL; } iso_image_unref(image); image = NULL; } /* >>> ??? copy some features to write_opts if not set by image import ?*/; ret = 1; ex:; if (image != NULL) iso_image_unref(image); if (ret < 0) { if (*features != NULL) iso_read_image_features_destroy(*features); *features = NULL; if (*write_opts != NULL) iso_write_opts_free(*write_opts); *write_opts = NULL; } /* Restore memorized read_opts settings */ opts->norock= opts_mem[0]; opts->nojoliet= opts_mem[1]; opts->noiso1999= opts_mem[2]; opts->preferjoliet= opts_mem[3]; opts->read_features= opts_mem[4]; return ret; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /* * Filesystem/FileSource implementation to access the local filesystem. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "fsource.h" #include "util.h" #include "aaip_0_2.h" #include #include #include #include #include #include #include #include #include /* O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif static int iso_file_source_new_lfs(IsoFileSource *parent, const char *name, IsoFileSource **src); /* * We can share a local filesystem object, as it has no private atts. */ IsoFilesystem *lfs= NULL; /* IMPORTANT: Any change must be reflected by lfs_clone_src() */ typedef struct { /** reference to the parent (if root it points to itself) */ IsoFileSource *parent; char *name; unsigned int openned :2; /* 0: not opened, 1: file, 2:dir */ union { int fd; DIR *dir; } info; } _LocalFsFileSource; static char* lfs_get_path(IsoFileSource *src) { _LocalFsFileSource *data; data = src->data; if (data->parent == src) { return strdup("/"); } else { char *path, *new_path; int pathlen; path = lfs_get_path(data->parent); if (path == NULL) return NULL; pathlen = strlen(path); new_path = realloc(path, pathlen + strlen(data->name) + 2); if (new_path == NULL) { free(path); return NULL; } path= new_path; if (pathlen != 1) { /* pathlen can only be 1 for root */ path[pathlen] = '/'; path[pathlen + 1] = '\0'; } return strcat(path, data->name); } } static char* lfs_get_name(IsoFileSource *src) { _LocalFsFileSource *data; data = src->data; return strdup(data->name); } static int lfs_lstat(IsoFileSource *src, struct stat *info) { char *path; if (src == NULL || info == NULL) { return ISO_NULL_POINTER; } path = lfs_get_path(src); if (path == NULL) return ISO_OUT_OF_MEM; if (lstat(path, info) != 0) { int err; /* error, choose an appropriate return code */ switch (errno) { case EACCES: err = ISO_FILE_ACCESS_DENIED; break; case ENOTDIR: case ENAMETOOLONG: case ELOOP: err = ISO_FILE_BAD_PATH; break; case ENOENT: err = ISO_FILE_DOESNT_EXIST; break; case EFAULT: case ENOMEM: err = ISO_OUT_OF_MEM; break; default: err = ISO_FILE_ERROR; break; } free(path); return err; } free(path); return ISO_SUCCESS; } static int lfs_stat(IsoFileSource *src, struct stat *info) { char *path; if (src == NULL || info == NULL) { return ISO_NULL_POINTER; } path = lfs_get_path(src); if (path == NULL) return ISO_OUT_OF_MEM; if (stat(path, info) != 0) { int err; /* error, choose an appropriate return code */ switch (errno) { case EACCES: err = ISO_FILE_ACCESS_DENIED; break; case ENOTDIR: case ENAMETOOLONG: case ELOOP: err = ISO_FILE_BAD_PATH; break; case ENOENT: err = ISO_FILE_DOESNT_EXIST; break; case EFAULT: case ENOMEM: err = ISO_OUT_OF_MEM; break; default: err = ISO_FILE_ERROR; break; } free(path); return err; } free(path); return ISO_SUCCESS; } static int lfs_access(IsoFileSource *src) { int ret; char *path; if (src == NULL) { return ISO_NULL_POINTER; } path = lfs_get_path(src); ret = iso_eaccess(path); free(path); return ret; } static int lfs_open(IsoFileSource *src) { int err; struct stat info; _LocalFsFileSource *data; char *path; if (src == NULL) { return ISO_NULL_POINTER; } data = src->data; if (data->openned) { return ISO_FILE_ALREADY_OPENED; } /* is a file or a dir ? */ err = lfs_stat(src, &info); if (err < 0) { return err; } path = lfs_get_path(src); if (S_ISDIR(info.st_mode)) { data->info.dir = opendir(path); data->openned = data->info.dir ? 2 : 0; } else { data->info.fd = open(path, O_RDONLY | O_BINARY); data->openned = data->info.fd != -1 ? 1 : 0; } free(path); /* * check for possible errors, note that many of possible ones are * parsed in the lstat call above */ if (data->openned == 0) { switch (errno) { case EACCES: err = ISO_FILE_ACCESS_DENIED; break; case EFAULT: case ENOMEM: err = ISO_OUT_OF_MEM; break; default: err = ISO_FILE_ERROR; break; } return err; } return ISO_SUCCESS; } static int lfs_close(IsoFileSource *src) { int ret; _LocalFsFileSource *data; if (src == NULL) { return ISO_NULL_POINTER; } data = src->data; switch (data->openned) { case 1: /* not dir */ ret = close(data->info.fd) == 0 ? ISO_SUCCESS : ISO_FILE_ERROR; break; case 2: /* directory */ ret = closedir(data->info.dir) == 0 ? ISO_SUCCESS : ISO_FILE_ERROR; break; default: ret = ISO_FILE_NOT_OPENED; break; } if (ret == ISO_SUCCESS) { data->openned = 0; } return ret; } static int lfs_read(IsoFileSource *src, void *buf, size_t count) { _LocalFsFileSource *data; size_t to_read, done = 0; int ret; uint8_t *buf8; if (src == NULL || buf == NULL) { return ISO_NULL_POINTER; } if (count == 0) { return ISO_WRONG_ARG_VALUE; } data = src->data; switch (data->openned) { case 1: /* not dir */ buf8 = (uint8_t *) buf; /* for pointer arithmetic */ for (to_read = count; to_read > 0; to_read = count - done) { if (to_read > 1024 * 1024) to_read = 1024 * 1024; ret = read(data->info.fd, buf8 + done, to_read); if (ret < 0) { /* error on read */ switch (errno) { case EINTR: return ISO_INTERRUPTED; case EFAULT: return ISO_OUT_OF_MEM; case EIO: return ISO_FILE_READ_ERROR; } return ISO_FILE_ERROR; } if (ret == 0) /* EOF */ break; done += ret; } return done; case 2: /* directory */ return ISO_FILE_IS_DIR; default: return ISO_FILE_NOT_OPENED; } } static off_t lfs_lseek(IsoFileSource *src, off_t offset, int flag) { _LocalFsFileSource *data; int whence; if (src == NULL) { return (off_t)((int) ISO_NULL_POINTER); } switch (flag) { case 0: whence = SEEK_SET; break; case 1: whence = SEEK_CUR; break; case 2: whence = SEEK_END; break; default: return (off_t)((int) ISO_WRONG_ARG_VALUE); } data = src->data; switch (data->openned) { case 1: /* not dir */ { off_t ret; ret = lseek(data->info.fd, offset, whence); if (ret < 0) { /* error on read */ switch (errno) { case ESPIPE: ret = (off_t)((int) ISO_FILE_ERROR); break; default: ret = (off_t)((int) ISO_ERROR); break; } } return ret; } case 2: /* directory */ return (off_t)((int) ISO_FILE_IS_DIR); default: return (off_t)((int) ISO_FILE_NOT_OPENED); } } static int lfs_readdir(IsoFileSource *src, IsoFileSource **child) { _LocalFsFileSource *data; if (src == NULL || child == NULL) { return ISO_NULL_POINTER; } data = src->data; switch (data->openned) { case 1: /* not dir */ return ISO_FILE_IS_NOT_DIR; case 2: /* directory */ { struct dirent *entry; int ret; /* while to skip "." and ".." dirs */ while (1) { entry = readdir(data->info.dir); if (entry == NULL) { if (errno == EBADF) return ISO_FILE_ERROR; else return 0; /* EOF */ } if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) { break; } } /* create the new FileSrc */ ret = iso_file_source_new_lfs(src, entry->d_name, child); return ret; } default: return ISO_FILE_NOT_OPENED; } } static int lfs_readlink(IsoFileSource *src, char *buf, size_t bufsiz) { int size, ret; char *path; if (src == NULL || buf == NULL) { return ISO_NULL_POINTER; } if (bufsiz <= 0) { return ISO_WRONG_ARG_VALUE; } path = lfs_get_path(src); /* * invoke readlink, with bufsiz -1 to reserve an space for * the NULL character */ size = readlink(path, buf, bufsiz); free(path); if (size < 0) { /* error */ switch (errno) { case EACCES: return ISO_FILE_ACCESS_DENIED; case ENOTDIR: case ENAMETOOLONG: case ELOOP: return ISO_FILE_BAD_PATH; case ENOENT: return ISO_FILE_DOESNT_EXIST; case EINVAL: return ISO_FILE_IS_NOT_SYMLINK; case EFAULT: case ENOMEM: return ISO_OUT_OF_MEM; default: return ISO_FILE_ERROR; } } /* NULL-terminate the buf */ ret = ISO_SUCCESS; if ((size_t) size >= bufsiz) { ret = ISO_RR_PATH_TOO_LONG; size = bufsiz - 1; } buf[size] = '\0'; return ret; } static IsoFilesystem* lfs_get_filesystem(IsoFileSource *src) { return src == NULL ? NULL : lfs; } static void lfs_free(IsoFileSource *src) { _LocalFsFileSource *data; data = src->data; /* close the file if it is already opened */ if (data->openned) { src->class->close(src); } if (data->parent != src) { iso_file_source_unref(data->parent); } free(data->name); free(data); iso_filesystem_unref(lfs); } static int lfs_get_aa_string(IsoFileSource *src, unsigned char **aa_string, int flag) { int ret, no_non_user_perm= 0; size_t num_attrs = 0, *value_lengths = NULL, result_len; ssize_t sret; char *path = NULL, **names = NULL, **values = NULL; unsigned char *result = NULL; *aa_string = NULL; if ((flag & (2 | 4 | 16) ) == (2 | 4)) { /* Neither ACL nor xattr shall be read, lfa_flags are not wanted */ ret = 1; goto ex; } /* Obtain EAs and ACLs ("access" and "default"). ACLs encoded according to AAIP ACL representation. Clean out st_mode ACL entries. Obtain Linux style attribute flags. */ path = iso_file_source_get_path(src); if (path == NULL) { ret = ISO_NULL_POINTER; goto ex; } ret = aaip_get_attr_list(path, &num_attrs, &names, &value_lengths, &values, (!(flag & 2)) | 2 | (flag & 4) | (flag & 8) | 16 | ((!(flag & 16)) << 6) | ((!!(flag & 32)) << 7)); if (ret <= 0) { if (ret == -2) ret = ISO_AAIP_NO_GET_LOCAL; else ret = ISO_FILE_ERROR; goto ex; } if(ret == 2) no_non_user_perm= 1; if (num_attrs == 0) result = NULL; else { sret = aaip_encode(num_attrs, names, value_lengths, values, &result_len, &result, 0); if (sret < 0) { ret = sret; goto ex; } } *aa_string = result; ret = 1 + no_non_user_perm; ex:; if (path != NULL) free(path); if (names != NULL || value_lengths != NULL || values != NULL) aaip_get_attr_list(NULL, &num_attrs, &names, &value_lengths, &values, 1 << 15); /* free memory */ return ret; } static int lfs_clone_src(IsoFileSource *old_source, IsoFileSource **new_source, int flag) { IsoFileSource *src = NULL; char *new_name = NULL; _LocalFsFileSource *old_data, *new_data = NULL; if (flag) return ISO_STREAM_NO_CLONE; /* unknown option required */ old_data = (_LocalFsFileSource *) old_source->data; *new_source = NULL; src = calloc(1, sizeof(IsoFileSource)); if (src == NULL) goto no_mem; new_name = strdup(old_data->name); if (new_name == NULL) goto no_mem; new_data = calloc(1, sizeof(_LocalFsFileSource)); if (new_data == NULL) goto no_mem; new_data->openned = 0; new_data->info.fd = -1; /* the value does not matter with (openned == 0) */ new_data->name = new_name; new_data->parent = old_data->parent; src->class = old_source->class; src->refcount = 1; src->data = new_data; *new_source = src; iso_file_source_ref(new_data->parent); iso_filesystem_ref(lfs); return ISO_SUCCESS; no_mem:; if (src != NULL) free((char *) src); if (new_data != NULL) free((char *) new_data); if (new_name != NULL) free(new_name); return ISO_OUT_OF_MEM; } IsoFileSourceIface lfs_class = { 2, /* version */ lfs_get_path, lfs_get_name, lfs_lstat, lfs_stat, lfs_access, lfs_open, lfs_close, lfs_read, lfs_readdir, lfs_readlink, lfs_get_filesystem, lfs_free, lfs_lseek, lfs_get_aa_string, lfs_clone_src }; /** * * @return * 1 success, < 0 error */ static int iso_file_source_new_lfs(IsoFileSource *parent, const char *name, IsoFileSource **src) { IsoFileSource *lfs_src; _LocalFsFileSource *data; if (src == NULL) { return ISO_NULL_POINTER; } if (lfs == NULL) { /* this should never happen */ return ISO_ASSERT_FAILURE; } /* allocate memory */ data = malloc(sizeof(_LocalFsFileSource)); if (data == NULL) { return ISO_OUT_OF_MEM; } lfs_src = malloc(sizeof(IsoFileSource)); if (lfs_src == NULL) { free(data); return ISO_OUT_OF_MEM; } /* fill struct */ data->name = name ? strdup(name) : NULL; data->openned = 0; if (parent) { data->parent = parent; iso_file_source_ref(parent); } else { data->parent = lfs_src; } lfs_src->refcount = 1; lfs_src->data = data; lfs_src->class = &lfs_class; /* take a ref to local filesystem */ iso_filesystem_ref(lfs); /* return */ *src = lfs_src; return ISO_SUCCESS; } static int lfs_get_root(IsoFilesystem *fs, IsoFileSource **root) { if (fs == NULL || root == NULL) { return ISO_NULL_POINTER; } return iso_file_source_new_lfs(NULL, NULL, root); } static int lfs_get_by_path(IsoFilesystem *fs, const char *path, IsoFileSource **file) { int ret; IsoFileSource *src; struct stat info; char *ptr, *brk_info, *component; if (fs == NULL || path == NULL || file == NULL) { return ISO_NULL_POINTER; } /* * first of all check that it is a valid path. */ if (lstat(path, &info) != 0) { int err; /* error, choose an appropriate return code */ switch (errno) { case EACCES: err = ISO_FILE_ACCESS_DENIED; break; case ENOTDIR: case ENAMETOOLONG: case ELOOP: err = ISO_FILE_BAD_PATH; break; case ENOENT: err = ISO_FILE_DOESNT_EXIST; break; case EFAULT: case ENOMEM: err = ISO_OUT_OF_MEM; break; default: err = ISO_FILE_ERROR; break; } return err; } /* ok, path is valid. create the file source */ ret = lfs_get_root(fs, &src); if (ret < 0) { return ret; } if (!strcmp(path, "/")) { /* we are looking for root */ *file = src; return ISO_SUCCESS; } ptr = strdup(path); if (ptr == NULL) { iso_file_source_unref(src); return ISO_OUT_OF_MEM; } component = strtok_r(ptr, "/", &brk_info); while (component) { IsoFileSource *child = NULL; if (!strcmp(component, ".")) { child = src; } else if (!strcmp(component, "..")) { child = ((_LocalFsFileSource*)src->data)->parent; iso_file_source_ref(child); iso_file_source_unref(src); } else { ret = iso_file_source_new_lfs(src, component, &child); iso_file_source_unref(src); if (ret < 0) { break; } } src = child; component = strtok_r(NULL, "/", &brk_info); } free(ptr); if (ret > 0) { *file = src; } return ret; } static unsigned int lfs_get_id(IsoFilesystem *fs) { return ISO_LOCAL_FS_ID; } static int lfs_fs_open(IsoFilesystem *fs) { /* open() operation is not needed */ return ISO_SUCCESS; } static int lfs_fs_close(IsoFilesystem *fs) { /* close() operation is not needed */ return ISO_SUCCESS; } static void lfs_fs_free(IsoFilesystem *fs) { lfs = NULL; } int iso_local_filesystem_new(IsoFilesystem **fs) { if (fs == NULL) { return ISO_NULL_POINTER; } if (lfs != NULL) { /* just take a new ref */ iso_filesystem_ref(lfs); } else { lfs = malloc(sizeof(IsoFilesystem)); if (lfs == NULL) { return ISO_OUT_OF_MEM; } /* fill struct */ memcpy(lfs->type, "file", 4); lfs->refcount = 1; lfs->version = 0; lfs->data = NULL; /* we don't need private data */ lfs->get_root = lfs_get_root; lfs->get_by_path = lfs_get_by_path; lfs->get_id = lfs_get_id; lfs->open = lfs_fs_open; lfs->close = lfs_fs_close; lfs->free = lfs_fs_free; } *fs = lfs; return ISO_SUCCESS; } int iso_local_attr_support(int flag) { int ret; ret= aaip_local_attr_support(flag & 255); return ret; } int iso_local_get_acl_text(char *disk_path, char **text, int flag) { int ret; ret = aaip_get_acl_text(disk_path, text, flag & (1 | 16 | 32 | (1 << 15))); return ret; } int iso_local_set_acl_text(char *disk_path, char *text, int flag) { int ret; ret = aaip_set_acl_text(disk_path, text, flag & (1 | 32)); if (ret < 0) return ISO_AAIP_NO_SET_LOCAL; return ret; } int iso_local_get_attrs(char *disk_path, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag) { int ret, lfa; lfa = (flag & 64) ^ 64; ret = aaip_get_attr_list(disk_path, num_attrs, names, value_lengths, values, (flag & (1 | 4 | 8 | 32 | (1 << 15))) | 2 | 16 | lfa); if (ret <= 0) return ISO_AAIP_NO_GET_LOCAL; return 1 + (ret == 2); } int iso_local_set_attrs_errno(char *disk_path, size_t num_attrs, char **names, size_t *value_lengths, char **values, int *errnos, int flag) { int ret; ret = aaip_set_attr_list(disk_path, num_attrs, names, value_lengths, values, errnos, (flag & (8 | 32 | 64 | 128)) | !(flag & 1)); if (ret <= 0) { if (ret == -1) return ISO_OUT_OF_MEM; if (ret == -2) return ISO_AAIP_BAD_AASTRING; if (ret >= -5) return ISO_AAIP_NO_SET_LOCAL; if (ret == -6 || ret == -7) return ISO_AAIP_NOT_ENABLED; if (ret == -8) return ISO_AAIP_BAD_ATTR_NAME; return ret; } return 1; } int iso_local_set_attrs(char *disk_path, size_t num_attrs, char **names, size_t *value_lengths, char **values, int flag) { int ret; int *errnos = NULL; if(num_attrs > 0) { errnos= calloc(num_attrs, sizeof(int)); if(errnos == NULL) return ISO_OUT_OF_MEM; } ret= iso_local_set_attrs_errno(disk_path, num_attrs, names, value_lengths, values, errnos, flag); if(errnos != NULL) free(errnos); return ret; } int iso_local_get_perms_wo_acl(char *disk_path, mode_t *st_mode, int flag) { struct stat stbuf; int ret; char *a_text = NULL; if (flag & 32) ret = stat(disk_path, &stbuf); else ret = lstat(disk_path, &stbuf); if (ret == -1) return -1; *st_mode = stbuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); ret = iso_local_get_acl_text(disk_path, &a_text, 16 | (flag & 32)); if (a_text != NULL) { aaip_cleanout_st_mode(a_text, st_mode, 4 | 16); iso_local_get_acl_text(disk_path, &a_text, 1 << 15); /* free a_text */ } return 1; } /* * @param flag * Bitfield for control purposes * bit2= do not issue own error messages with operating system errors * bit5= in case of symbolic link: inquire link target * bit7= Ignore non-settable Linux-like file attribute flags * @return * 1= ok, lfa_flags is valid * 2= ok, but some local flags could not be mapped to the FS_*_FL bits * 3= ok, symbolic link encountered, flag bit5 not set, lfa_flags set to 0 * 4= ok, file did not bear attribute flags. E.g. because not S_IFDIR or * S_IFREG, or because unsuitable filesystem. * lfa_flags is set to 0 * 0= local flags retrieval not enabled at compile time * <0= error with system calls */ int iso_local_get_lfa_flags(char *disk_path, uint64_t *lfa_flags, int *max_bit, int *os_errno, int flag) { int ret; struct stat stbuf; *lfa_flags = 0; *max_bit = -1; *os_errno = 0; if (flag & 32) ret = stat(disk_path, &stbuf); else ret = lstat(disk_path, &stbuf); if (ret == -1) { *os_errno = errno; return -1; } if ((stbuf.st_mode & S_IFMT) == S_IFLNK && !(flag & 32)) return 3; ret = aaip_get_lfa_flags(disk_path, lfa_flags, max_bit, os_errno, flag & (4 | (1 << 7))); if(ret == 0) return ISO_LFA_NOT_ENABLED; if(ret == -1) return ISO_LFA_NO_OPEN_LOCAL; if(ret < 0) return ISO_LFA_NO_SET_LOCAL; return ret; } /* * @param flag Bitfield for control purposes * bit0= do not try to change known superuser flags * bit1= change only known chattr settable flags * bit2= do not issue own error messages with operating system errors * bit5= in case of symbolic link: inquire link target * @return * 1 = ok, all lfa_flags bits were written * 2 = ok, but some FS_*_FL bits could not be mapped to local flags * 3 = ok, symbolic link encountered, flag bit5 not set, nothing done * <0 = error */ int iso_local_set_lfa_flags(char *disk_path, uint64_t lfa_flags, int max_bit, uint64_t change_mask, int *os_errno, int flag) { int ret, old_max_bit; struct stat stbuf; uint64_t eff_flags; *os_errno = 0; if (flag & 32) ret = stat(disk_path, &stbuf); else ret = lstat(disk_path, &stbuf); if (ret == -1) { *os_errno = errno; return -1; } if ((stbuf.st_mode & S_IFMT) == S_IFLNK && !(flag & 32)) return 3; change_mask= iso_util_get_effective_lfa_mask(change_mask, flag & 3); if (change_mask == 0) { return 1; } else if (change_mask == ~((uint64_t) 0)) { eff_flags = lfa_flags; } else { ret = aaip_get_lfa_flags(disk_path, &eff_flags, &old_max_bit, os_errno, flag & 4); if (ret == 0) return ISO_LFA_NOT_ENABLED; if (ret == -1) return ISO_LFA_NO_OPEN_LOCAL; if (ret < 0) return ISO_LFA_NO_GET_LOCAL; eff_flags &= ~change_mask; eff_flags |= (lfa_flags & change_mask); } ret= aaip_set_lfa_flags(disk_path, eff_flags, max_bit, os_errno, flag & 4); if(ret == 0) return ISO_LFA_NOT_ENABLED; if (ret == -1) return ISO_LFA_NO_OPEN_LOCAL; if(ret < 0) return ISO_LFA_NO_SET_LOCAL; return ret; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2022 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "fsource.h" #include /** * Values belong 1000 are reserved for libisofs usage */ unsigned int iso_fs_global_id = 1000; void iso_file_source_ref(IsoFileSource *src) { ++src->refcount; } void iso_file_source_unref(IsoFileSource *src) { if (--src->refcount == 0) { src->class->free(src); free(src); } } void iso_filesystem_ref(IsoFilesystem *fs) { ++fs->refcount; } void iso_filesystem_unref(IsoFilesystem *fs) { if (--fs->refcount == 0) { fs->free(fs); free(fs); } } /* * this are just helpers to invoque methods in class */ inline char* iso_file_source_get_path(IsoFileSource *src) { return src->class->get_path(src); } inline char* iso_file_source_get_name(IsoFileSource *src) { return src->class->get_name(src); } inline int iso_file_source_lstat(IsoFileSource *src, struct stat *info) { return src->class->lstat(src, info); } inline int iso_file_source_access(IsoFileSource *src) { return src->class->access(src); } inline int iso_file_source_stat(IsoFileSource *src, struct stat *info) { return src->class->stat(src, info); } inline int iso_file_source_open(IsoFileSource *src) { return src->class->open(src); } inline int iso_file_source_close(IsoFileSource *src) { return src->class->close(src); } inline int iso_file_source_read(IsoFileSource *src, void *buf, size_t count) { return src->class->read(src, buf, count); } inline off_t iso_file_source_lseek(IsoFileSource *src, off_t offset, int flag) { return src->class->lseek(src, offset, flag); } inline int iso_file_source_readdir(IsoFileSource *src, IsoFileSource **child) { return src->class->readdir(src, child); } inline int iso_file_source_readlink(IsoFileSource *src, char *buf, size_t bufsiz) { return src->class->readlink(src, buf, bufsiz); } inline IsoFilesystem* iso_file_source_get_filesystem(IsoFileSource *src) { return src->class->get_filesystem(src); } inline int iso_file_source_get_aa_string(IsoFileSource *src, unsigned char **aa_string, int flag) { if (src->class->version < 1) { *aa_string = NULL; return 1; } return src->class->get_aa_string(src, aa_string, flag); } /* @flag bit0= Open and close src bit1= Try iso_file_source_lseek(, 0) (=SEEK_SET) with wanted_size @return <0 iso_file_source_lseek failed , >= 0 readable capacity */ off_t iso_file_source_lseek_capacity(IsoFileSource *src, off_t wanted_size, int flag) { int ret, opened = 0; off_t end, old, reset; struct stat info; ret = iso_file_source_stat(src, &info); if (ret < 0) { end = -1; goto ex; } if (S_ISDIR(info.st_mode) || S_ISLNK(info.st_mode) || S_ISFIFO(info.st_mode) || S_ISSOCK(info.st_mode)) { /* open(2) on fifo can block and have side effects. Active Unix sockets have not been tested but they make as few sense as directories or symbolic links. */ end = -1; goto ex; } if (flag & 1) { ret = iso_file_source_open(src); if (ret < 0) { end = -1; goto ex; } opened = 1; } old = iso_file_source_lseek(src, 0, 1); if (old < 0) { end = -1; goto ex; } if(flag & 2) { end = iso_file_source_lseek(src, wanted_size, 0); } else { end = iso_file_source_lseek(src, 0, 2); } if (end < 0) { end = -1; goto ex; } reset = iso_file_source_lseek(src, old, 0); if (reset != old) { end = -1; goto ex; } ex:; if (opened) { iso_file_source_close(src); } return end; } /* Determine whether src is random-access readable and return its capacity. @flag bit0= For iso_file_source_lseek_capacity(): Open and close src bit1= wanted_size is valid */ off_t iso_file_source_determine_capacity(IsoFileSource *src, off_t wanted_size, int flag) { int ret; off_t src_size, src_seek_size= -1; struct stat info; ret = iso_file_source_stat(src, &info); if (ret < 0) { return (off_t) -1; } if (S_ISREG(info.st_mode)) { return info.st_size; } src_size = iso_file_source_lseek_capacity(src, wanted_size, (flag & 1)); if (src_size > 0) { return src_size; } if (!(flag & 2)) { if (src_size == 0) { return 0; } return -1; } src_seek_size= src_size; src_size = iso_file_source_lseek_capacity(src, wanted_size, 2 | (flag & 1)); if (src_size >= 0) { return src_size; } else if (src_seek_size >= 0) { return src_seek_size; } return -1; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2022 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_FSOURCE_H_ #define LIBISO_FSOURCE_H_ /* * Definitions for the file sources. Most functions/structures related with * this were moved to libisofs.h. */ #include "libisofs.h" #define ISO_LOCAL_FS_ID 1 #define ISO_IMAGE_FS_ID 2 #define ISO_ELTORITO_FS_ID 3 #define ISO_MEM_FS_ID 4 #define ISO_FILTER_FS_ID 5 /** * Create a new IsoFilesystem to deal with local filesystem. * * @return * 1 success, < 0 error */ int iso_local_filesystem_new(IsoFilesystem **fs); /* Rank two IsoFileSource of ifs_class by their eventual old image LBAs. * @param cmp_ret will return the reply value -1, 0, or 1. * @return 1= *cmp_ret is a valid reply * 0= not both streams are of ifs_class, * *cmp_ret is only a rough estimation. */ int iso_ifs_sections_cmp(IsoFileSource *s1, IsoFileSource *s2, int *cmp_ret, int flag); /* Create an independent copy of an ifs_class IsoFileSource. */ int iso_ifs_source_clone(IsoFileSource *old_source, IsoFileSource **new_source, int flag); off_t iso_file_source_lseek_capacity(IsoFileSource *src, off_t wanted_size, int flag); /* Determine whether src is random-access readable and return its capacity. */ off_t iso_file_source_determine_capacity(IsoFileSource *src, off_t wanted_size, int flag); #endif /*LIBISO_FSOURCE_H_*/ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2007 Mario Danic * Copyright (c) 2011-2012 Thomas Schmitt * Copyright (c) 2012 Vladimir Serbinenko * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /* Some extra debugging messages for Vladimir Serbinenko #define Libisofs_hfsplus_verbose_debuG yes */ /* Some extra debugging messages for Thomas Schmitt */ #define Libisofs_ts_debuG yes #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "hfsplus.h" #include "messages.h" #include "writer.h" #include "image.h" #include "filesrc.h" #include "eltorito.h" #include "libisofs.h" #include "util.h" #include "ecma119.h" #include "system_area.h" #include #include #include /* To be used if Ecma119.hfsplus_block_size == 0 in hfsplus_writer_create(). It cannot be larger than 2048 because filesrc_writer aligns data file content start to 2048. */ #define HFSPLUS_DEFAULT_BLOCK_SIZE 2048 /* To be used with storage allocation. */ #define HFSPLUS_MAX_BLOCK_SIZE 2048 /* In libisofs/hfsplus_case.c */ extern uint16_t iso_hfsplus_cichar(uint16_t x); /* ts B20623: pad up output block to full 2048 bytes */ static int pad_up_block(Ecma119Image *t) { int ret; static char buffer[2048], buf_zeroed = 0; if (!buf_zeroed) { memset(buffer, 0, 2048); buf_zeroed = 1; } if (t->bytes_written % 2048) { ret = iso_write(t, buffer, 2048 - (t->bytes_written % 2048)); if (ret < 0) return ret; } return 1; } static int filesrc_block_and_size(Ecma119Image *t, IsoFileSrc *src, uint32_t *start_block, uint64_t *total_size) { int i; uint32_t pos; *start_block = 0; *total_size = 0; if (src->nsections <= 0) return 0; pos = *start_block = src->sections[0].block; for (i = 0; i < src->nsections; i++) { *total_size += src->sections[i].size; if (pos != src->sections[i].block) { iso_msg_submit(t->image->id, ISO_SECT_SCATTERED, 0, "File sections do not form consecutive array of blocks"); return ISO_SECT_SCATTERED; } /* If .size is not aligned to blocks then there is a byte gap. No need to trace the exact byte address. */ pos = src->sections[i].block + src->sections[i].size / 2048; } return 1; } static uint8_t get_class (uint16_t v) { uint16_t s; uint8_t high, low; s = iso_ntohs (v); high = s >> 8; low = v & 0xff; if (!hfsplus_class_pages[high]) return 0; return hfsplus_class_pages[high][low]; } int iso_get_hfsplus_name(char *input_charset, int imgid, char *name, uint16_t **result, uint32_t *result_len, uint16_t **cmp_name) { int ret; uint16_t *ucs_name, *iptr, *optr; uint32_t curlen; int done; if (name == NULL) { /* it is not necessarily an error, it can be the root */ return ISO_SUCCESS; } ret = str2utf16be(input_charset, name, &ucs_name); if (ret < 0) { iso_msg_debug(imgid, "Cannot convert '%s'", name); return ret; } curlen = ucslen (ucs_name); *result = calloc ((curlen * HFSPLUS_MAX_DECOMPOSE_LEN + 1), sizeof (uint16_t)); if (*result == NULL) { free(ucs_name); return ISO_OUT_OF_MEM; } for (iptr = ucs_name, optr = *result; *iptr; iptr++) { const uint16_t *dptr; uint16_t val = iso_ntohs (*iptr); uint8_t high = val >> 8; uint8_t low = val & 0xff; if (val == ':') { *optr++ = iso_htons ('/'); continue; } if (val >= 0xac00 && val <= 0xd7a3) { uint16_t s, l, v, t; s = val - 0xac00; l = s / (21 * 28); v = (s % (21 * 28)) / 28; t = s % 28; *optr++ = iso_htons (l + 0x1100); *optr++ = iso_htons (v + 0x1161); if (t) *optr++ = iso_htons (t + 0x11a7); continue; } if (!hfsplus_decompose_pages[high]) { *optr++ = *iptr; continue; } dptr = hfsplus_decompose_pages[high][low]; if (!dptr[0]) { *optr++ = *iptr; continue; } for (; *dptr; dptr++) *optr++ = iso_htons (*dptr); } *optr = 0; do { uint8_t last_class; done = 0; if (!ucs_name[0]) break; last_class = get_class (ucs_name[0]); for (optr = *result + 1; *optr; optr++) { uint8_t new_class = get_class (*optr); if (last_class == 0 || new_class == 0 || last_class <= new_class) last_class = new_class; else { uint16_t t; t = *(optr - 1); *(optr - 1) = *optr; *optr = t; } } } while (done); *cmp_name = calloc ((ucslen (*result) + 1), sizeof (uint16_t)); if (*cmp_name == NULL) { free(ucs_name); free(*result); *result = NULL; return ISO_OUT_OF_MEM; } for (iptr = *result, optr = *cmp_name; *iptr; iptr++) { *optr = iso_hfsplus_cichar(*iptr); if (*optr != 0) optr++; } *optr = 0; free (ucs_name); *result_len = ucslen (*result); return ISO_SUCCESS; } static int set_hfsplus_name(Ecma119Image *t, char *name, HFSPlusNode *node) { int ret; ret = iso_get_hfsplus_name(t->input_charset, t->image->id, name, &(node->name), &(node->strlen), &(node->cmp_name)); return ret; } /* >>> ts B20617 This should be HFSPlusNode rather than IsoNode in order to have access to IsoFileSrc.no_write which indicates that the file content will not be in written the range of filesrc_writer. */ static int hfsplus_count_tree(Ecma119Image *t, IsoNode *iso) { if (t == NULL || iso == NULL) { return ISO_NULL_POINTER; } if (iso->hidden & LIBISO_HIDE_ON_HFSPLUS) { /* file will be ignored */ return 0; } switch (iso->type) { case LIBISO_SYMLINK: case LIBISO_SPECIAL: case LIBISO_FILE: t->hfsp_nfiles++; return ISO_SUCCESS; case LIBISO_DIR: t->hfsp_ndirs++; { IsoNode *pos; IsoDir *dir = (IsoDir*)iso; pos = dir->children; while (pos) { int cret; cret = hfsplus_count_tree(t, pos); if (cret < 0) { /* error */ return cret; } pos = pos->next; } } return ISO_SUCCESS; case LIBISO_BOOT: return ISO_SUCCESS; default: /* should never happen */ return ISO_ASSERT_FAILURE; } } /** * Create the low level Hfsplus tree from the high level ISO tree. * * @return * 1 success, 0 file ignored, < 0 error */ static int create_tree(Ecma119Image *t, IsoNode *iso, uint32_t parent_id) { int ret; uint32_t cat_id, cleaf; int i; if (t == NULL || iso == NULL) { return ISO_NULL_POINTER; } if (iso->hidden & LIBISO_HIDE_ON_HFSPLUS) { /* file will be ignored */ return 0; } if (iso->type != LIBISO_FILE && iso->type != LIBISO_DIR && iso->type != LIBISO_SYMLINK && iso->type != LIBISO_SPECIAL) return 0; cat_id = t->hfsp_cat_id++; for (i = 0; i < ISO_HFSPLUS_BLESS_MAX; i++) if (t->hfsplus_blessed[i] == iso) { #ifdef Libisofs_ts_debuG iso_msg_debug(t->image->id, "hfsplus bless %d to cat_id %u ('%s')", i, cat_id, iso->name); #endif /* Libisofs_ts_debuG */ t->hfsp_bless_id[i] = cat_id; } t->hfsp_leafs[t->hfsp_curleaf].node = iso; t->hfsp_leafs[t->hfsp_curleaf].parent_id = parent_id; ret = set_hfsplus_name (t, iso->name, &t->hfsp_leafs[t->hfsp_curleaf]); if (ret < 0) return ret; t->hfsp_leafs[t->hfsp_curleaf].cat_id = cat_id; t->hfsp_leafs[t->hfsp_curleaf].unix_type = UNIX_NONE; t->hfsp_leafs[t->hfsp_curleaf].symlink_dest = NULL; switch (iso->type) { case LIBISO_SYMLINK: { IsoSymlink *sym = (IsoSymlink*) iso; t->hfsp_leafs[t->hfsp_curleaf].type = HFSPLUS_FILE; t->hfsp_leafs[t->hfsp_curleaf].symlink_dest = strdup(sym->dest); if (t->hfsp_leafs[t->hfsp_curleaf].symlink_dest == NULL) return ISO_OUT_OF_MEM; t->hfsp_leafs[t->hfsp_curleaf].unix_type = UNIX_SYMLINK; t->hfsp_leafs[t->hfsp_curleaf].used_size = t->hfsp_leafs[t->hfsp_curleaf].strlen * 2 + 8 + 2 + sizeof (struct hfsplus_catfile_common) + 2 * sizeof (struct hfsplus_forkdata); break; } case LIBISO_SPECIAL: t->hfsp_leafs[t->hfsp_curleaf].unix_type = UNIX_SPECIAL; t->hfsp_leafs[t->hfsp_curleaf].type = HFSPLUS_FILE; t->hfsp_leafs[t->hfsp_curleaf].used_size = t->hfsp_leafs[t->hfsp_curleaf].strlen * 2 + 8 + 2 + sizeof (struct hfsplus_catfile_common) + 2 * sizeof (struct hfsplus_forkdata); break; case LIBISO_FILE: { IsoFile *file = (IsoFile*) iso; t->hfsp_leafs[t->hfsp_curleaf].type = HFSPLUS_FILE; ret = iso_file_src_create(t, file, &t->hfsp_leafs[t->hfsp_curleaf].file); if (ret < 0) { return ret; } t->hfsp_leafs[t->hfsp_curleaf].used_size = t->hfsp_leafs[t->hfsp_curleaf].strlen * 2 + 8 + 2 + sizeof (struct hfsplus_catfile_common) + 2 * sizeof (struct hfsplus_forkdata); } break; case LIBISO_DIR: { t->hfsp_leafs[t->hfsp_curleaf].type = HFSPLUS_DIR; t->hfsp_leafs[t->hfsp_curleaf].used_size = t->hfsp_leafs[t->hfsp_curleaf].strlen * 2 + 8 + 2 + sizeof (struct hfsplus_catfile_common); break; } default: return ISO_ASSERT_FAILURE; } cleaf = t->hfsp_curleaf; t->hfsp_leafs[t->hfsp_curleaf].nchildren = 0; t->hfsp_curleaf++; t->hfsp_leafs[t->hfsp_curleaf].name = t->hfsp_leafs[t->hfsp_curleaf - 1].name; t->hfsp_leafs[t->hfsp_curleaf].cmp_name = NULL; t->hfsp_leafs[t->hfsp_curleaf].strlen = t->hfsp_leafs[t->hfsp_curleaf - 1].strlen; t->hfsp_leafs[t->hfsp_curleaf].used_size = t->hfsp_leafs[t->hfsp_curleaf].strlen * 2 + 8 + 2 + sizeof (struct hfsplus_catfile_thread); t->hfsp_leafs[t->hfsp_curleaf].node = iso; t->hfsp_leafs[t->hfsp_curleaf].type = (iso->type == LIBISO_DIR) ? HFSPLUS_DIR_THREAD : HFSPLUS_FILE_THREAD; t->hfsp_leafs[t->hfsp_curleaf].file = 0; t->hfsp_leafs[t->hfsp_curleaf].cat_id = parent_id; t->hfsp_leafs[t->hfsp_curleaf].parent_id = cat_id; t->hfsp_leafs[t->hfsp_curleaf].unix_type = UNIX_NONE; t->hfsp_curleaf++; if (iso->type == LIBISO_DIR) { IsoNode *pos; IsoDir *dir = (IsoDir*)iso; pos = dir->children; while (pos) { int cret; cret = create_tree(t, pos, cat_id); if (cret < 0) return cret; pos = pos->next; if (cret > 0) t->hfsp_leafs[cleaf].nchildren++; } } return ISO_SUCCESS; } static int cmp_node(const void *f1, const void *f2) { HFSPlusNode *f = (HFSPlusNode*) f1; HFSPlusNode *g = (HFSPlusNode*) f2; const uint16_t empty[1] = {0}; const uint16_t *a, *b; if (f->parent_id > g->parent_id) return +1; if (f->parent_id < g->parent_id) return -1; a = f->cmp_name; b = g->cmp_name; if (!a) a = empty; if (!b) b = empty; return ucscmp(a, b); } static int hfsplus_tail_writer_compute_data_blocks(IsoImageWriter *writer) { Ecma119Image *t; uint32_t hfsp_size, hfsp_curblock, block_fac, block_size; if (writer == NULL) { return ISO_OUT_OF_MEM; } t = writer->target; block_size = t->opts->hfsp_block_size; block_fac = t->hfsp_iso_block_fac; #ifdef Libisofs_ts_debuG iso_msg_debug(t->image->id, "hfsplus tail writer start = %.f", ((double) t->curblock) * 2048.0); #endif hfsp_curblock = t->curblock * block_fac; hfsp_size = hfsp_curblock - t->hfsp_part_start + 1; /* We need one bit for every block. */ /* So if we allocate x blocks we have to satisfy: 8 * block_size * x >= total_size + x (8 * block_size - 1) * x >= total_size */ t->hfsp_allocation_blocks = hfsp_size / (8 * block_size - 1) + 1; t->hfsp_allocation_file_start = hfsp_curblock; hfsp_curblock += t->hfsp_allocation_blocks; /* write_data() will need to pad up ISO block before superblock copy */ t->curblock = hfsp_curblock / block_fac; if (hfsp_curblock % block_fac) t->curblock++; hfsp_curblock = t->curblock * block_fac; /* Superblock always occupies 2K */ hfsp_curblock += block_fac; t->curblock++; #ifdef Libisofs_ts_debuG iso_msg_debug(t->image->id, "hfsplus tail writer end = %.f", ((double) hfsp_curblock) * block_size); #endif t->hfsp_total_blocks = hfsp_curblock - t->hfsp_part_start; return iso_quick_apm_entry(t->apm_req, &(t->apm_req_count), t->hfsp_part_start / block_fac, t->hfsp_total_blocks / block_fac + !!(t->hfsp_total_blocks % block_fac), "HFSPLUS_Hybrid", "Apple_HFS"); } static int hfsplus_writer_compute_data_blocks(IsoImageWriter *writer) { Ecma119Image *t; uint32_t i, hfsp_curblock; uint32_t block_fac, block_size; if (writer == NULL) { return ISO_OUT_OF_MEM; } t = writer->target; block_size = t->opts->hfsp_block_size; block_fac = t->hfsp_iso_block_fac; iso_msg_debug(t->image->id, "(b) curblock=%d, nodes =%d", t->curblock, t->hfsp_nnodes); t->hfsp_part_start = t->curblock * block_fac; hfsp_curblock = t->curblock * block_fac; /* Superblock always occupies 2K */ hfsp_curblock += block_fac; t->hfsp_catalog_file_start = hfsp_curblock; /* hfsp_curblock += (t->hfsp_nnodes * t->hfsp_cat_node_size + block_size - 1) / block_size; */ hfsp_curblock += 2 * t->hfsp_nnodes; t->hfsp_extent_file_start = hfsp_curblock; hfsp_curblock++; iso_msg_debug(t->image->id, "(d) hfsp_curblock=%d, nodes =%d", hfsp_curblock, t->hfsp_nnodes); for (i = 0; i < t->hfsp_nleafs; i++) if (t->hfsp_leafs[i].unix_type == UNIX_SYMLINK) { t->hfsp_leafs[i].symlink_block = hfsp_curblock; hfsp_curblock += (strlen(t->hfsp_leafs[i].symlink_dest) + block_size - 1) / block_size; } t->curblock = hfsp_curblock / block_fac; if (hfsp_curblock % block_fac) t->curblock++; iso_msg_debug(t->image->id, "(a) curblock=%d, nodes =%d", t->curblock, t->hfsp_nnodes); return ISO_SUCCESS; } static inline uint32_t mac_time_offset(uint32_t t) { uint32_t val; iso_msb ((uint8_t *) &val, t + 2082844800, sizeof(val)); return val; } int nop_writer_write_vol_desc(IsoImageWriter *writer) { return ISO_SUCCESS; } static uid_t px_get_uid(Ecma119Image *t, IsoNode *n) { if (t->replace_uid) { return t->uid; } else { return n->uid; } } static uid_t px_get_gid(Ecma119Image *t, IsoNode *n) { if (t->replace_gid) { return t->gid; } else { return n->gid; } } static mode_t px_get_mode(Ecma119Image *t, IsoNode *n, int isdir) { if (isdir) { if (t->replace_dir_mode) { return (n->mode & S_IFMT) | t->dir_mode; } } else { if (t->replace_file_mode) { return (n->mode & S_IFMT) | t->file_mode; } } return n->mode; } int write_sb (Ecma119Image *t) { struct hfsplus_volheader sb; static char buffer[1024]; int ret; int i; uint32_t block_size; iso_msg_debug(t->image->id, "Write HFS+ superblock"); block_size = t->opts->hfsp_block_size; memset (buffer, 0, sizeof (buffer)); ret = iso_write(t, buffer, 1024); if (ret < 0) return ret; memset (&sb, 0, sizeof (sb)); t->hfsp_allocation_size = (t->hfsp_total_blocks + 7) >> 3; iso_msb ((uint8_t *) &sb.magic, 0x482b, 2); iso_msb ((uint8_t *) &sb.version, 4, 2); /* Cleanly unmounted, software locked. */ iso_msb ((uint8_t *) &sb.attributes, (1 << 8) | (1 << 15), 4); iso_msb ((uint8_t *) &sb.last_mounted_version, 0x6c69736f, 4); sb.ctime = mac_time_offset(t->now); sb.utime = mac_time_offset(t->now); sb.fsck_time = mac_time_offset(t->now); iso_msb ((uint8_t *) &sb.file_count, t->hfsp_nfiles, 4); iso_msb ((uint8_t *) &sb.folder_count, t->hfsp_ndirs - 1, 4); iso_msb ((uint8_t *) &sb.blksize, block_size, 4); iso_msb ((uint8_t *) &sb.catalog_node_id, t->hfsp_cat_id, 4); iso_msb ((uint8_t *) &sb.rsrc_clumpsize, block_size, 4); iso_msb ((uint8_t *) &sb.data_clumpsize, block_size, 4); iso_msb ((uint8_t *) &sb.total_blocks, t->hfsp_total_blocks, 4); iso_msb ((uint8_t *) &sb.encodings_bitmap + 4, 1, 4); iso_msb ((uint8_t *) &sb.allocations_file.size + 4, t->hfsp_allocation_size, 4); iso_msb ((uint8_t *) &sb.allocations_file.clumpsize, block_size, 4); iso_msb ((uint8_t *) &sb.allocations_file.blocks, (t->hfsp_allocation_size + block_size - 1) / block_size, 4); iso_msb ((uint8_t *) &sb.allocations_file.extents[0].start, t->hfsp_allocation_file_start - t->hfsp_part_start, 4); iso_msb ((uint8_t *) &sb.allocations_file.extents[0].count, (t->hfsp_allocation_size + block_size - 1) / block_size, 4); iso_msb ((uint8_t *) &sb.extents_file.size + 4, block_size, 4); iso_msb ((uint8_t *) &sb.extents_file.clumpsize, block_size, 4); iso_msb ((uint8_t *) &sb.extents_file.blocks, 1, 4); iso_msb ((uint8_t *) &sb.extents_file.extents[0].start, t->hfsp_extent_file_start - t->hfsp_part_start, 4); iso_msb ((uint8_t *) &sb.extents_file.extents[0].count, 1, 4); iso_msg_debug(t->image->id, "extent_file_start = %d\n", (int)t->hfsp_extent_file_start); iso_msb ((uint8_t *) &sb.catalog_file.size + 4, block_size * 2 * t->hfsp_nnodes, 4); iso_msb ((uint8_t *) &sb.catalog_file.clumpsize, block_size * 2, 4); iso_msb ((uint8_t *) &sb.catalog_file.blocks, 2 * t->hfsp_nnodes, 4); iso_msb ((uint8_t *) &sb.catalog_file.extents[0].start, t->hfsp_catalog_file_start - t->hfsp_part_start, 4); iso_msb ((uint8_t *) &sb.catalog_file.extents[0].count, 2 * t->hfsp_nnodes, 4); iso_msg_debug(t->image->id, "catalog_file_start = %d\n", (int)t->hfsp_catalog_file_start); for (i = 0; i < ISO_HFSPLUS_BLESS_MAX; i++) { iso_msb ((uint8_t *) (&sb.ppc_bootdir + i + (i == ISO_HFSPLUS_BLESS_OSX_FOLDER)), t->hfsp_bless_id[i], 4); #ifdef Libisofs_ts_debuG iso_msg_debug(t->image->id, "hfsplus bless %d written for cat_id %u", i, t->hfsp_bless_id[i]); #endif /* Libisofs_ts_debuG */ } memcpy (&sb.num_serial, &t->opts->hfsp_serial_number, 8); ret = iso_write(t, &sb, sizeof (sb)); if (ret < 0) return ret; return iso_write(t, buffer, 512); } static int hfsplus_writer_write_data(IsoImageWriter *writer) { int ret; static char buffer[2 * HFSPLUS_MAX_BLOCK_SIZE]; Ecma119Image *t; struct hfsplus_btnode *node_head; struct hfsplus_btheader *tree_head; int level; uint32_t curpos = 1, i, block_fac, cat_node_size, block_size; if (writer == NULL) { return ISO_NULL_POINTER; } t = writer->target; block_size = t->opts->hfsp_block_size; block_fac = t->hfsp_iso_block_fac; cat_node_size = t->hfsp_cat_node_size; iso_msg_debug(t->image->id, "(b) %d written", (int) t->bytes_written / 0x800); ret = write_sb (t); if (ret < 0) return ret; iso_msg_debug(t->image->id, "(c) %d written", (int) t->bytes_written / 0x800); iso_msg_debug(t->image->id, "real catalog_file_start = %d\n", (int)t->bytes_written / 2048); memset (buffer, 0, sizeof (buffer)); node_head = (struct hfsplus_btnode *) buffer; node_head->type = 1; iso_msb ((uint8_t *) &node_head->count, 3, 2); tree_head = (struct hfsplus_btheader *) (node_head + 1); iso_msb ((uint8_t *) &tree_head->depth, t->hfsp_nlevels, 2); iso_msb ((uint8_t *) &tree_head->root, 1, 4); iso_msb ((uint8_t *) &tree_head->leaf_records, t->hfsp_nleafs, 4); iso_msb ((uint8_t *) &tree_head->first_leaf_node, t->hfsp_nnodes - t->hfsp_levels[0].level_size, 4); iso_msb ((uint8_t *) &tree_head->last_leaf_node, t->hfsp_nnodes - 1, 4); iso_msb ((uint8_t *) &tree_head->nodesize, cat_node_size, 2); iso_msb ((uint8_t *) &tree_head->keysize, 6 + 2 * LIBISO_HFSPLUS_NAME_MAX, 2); iso_msb ((uint8_t *) &tree_head->total_nodes, t->hfsp_nnodes, 4); iso_msb ((uint8_t *) &tree_head->free_nodes, 0, 4); iso_msb ((uint8_t *) &tree_head->clump_size, cat_node_size, 4); tree_head->key_compare = 0xcf; iso_msb ((uint8_t *) &tree_head->attributes, 2 | 4, 4); memset (buffer + 0xf8, -1, t->hfsp_nnodes / 8); buffer[0xf8 + (t->hfsp_nnodes / 8)] = 0xff00 >> (t->hfsp_nnodes % 8); buffer[cat_node_size - 1] = sizeof (*node_head); buffer[cat_node_size - 3] = sizeof (*node_head) + sizeof (*tree_head); buffer[cat_node_size - 5] = (char) 0xf8; buffer[cat_node_size - 7] = (char) ((cat_node_size - 8) & 0xff); buffer[cat_node_size - 8] = (cat_node_size - 8) >> 8; #ifdef Libisofs_hfsplus_verbose_debuG iso_msg_debug(t->image->id, "Write\n"); #endif ret = iso_write(t, buffer, cat_node_size); if (ret < 0) return ret; for (level = t->hfsp_nlevels - 1; level > 0; level--) { uint32_t i; uint32_t next_lev = curpos + t->hfsp_levels[level].level_size; for (i = 0; i < t->hfsp_levels[level].level_size; i++) { uint32_t curoff; uint32_t j; uint32_t curnode = t->hfsp_levels[level].nodes[i].start; memset (buffer, 0, sizeof (buffer)); node_head = (struct hfsplus_btnode *) buffer; if (i != t->hfsp_levels[level].level_size - 1) iso_msb ((uint8_t *) &node_head->next, curpos + i + 1, 4); if (i != 0) iso_msb ((uint8_t *) &node_head->prev, curpos + i - 1, 4); node_head->type = 0; node_head->height = level + 1; iso_msb ((uint8_t *) &node_head->count, t->hfsp_levels[level].nodes[i].cnt, 2); curoff = sizeof (struct hfsplus_btnode); for (j = 0; j < t->hfsp_levels[level].nodes[i].cnt; j++) { iso_msb ((uint8_t *) buffer + cat_node_size - j * 2 - 2, curoff, 2); iso_msb ((uint8_t *) buffer + curoff, 2 * t->hfsp_levels[level - 1].nodes[curnode].strlen + 6, 2); iso_msb ((uint8_t *) buffer + curoff + 2, t->hfsp_levels[level - 1].nodes[curnode].parent_id, 4); iso_msb ((uint8_t *) buffer + curoff + 6, t->hfsp_levels[level - 1].nodes[curnode].strlen, 2); curoff += 8; memcpy ((uint8_t *) buffer + curoff, t->hfsp_levels[level - 1].nodes[curnode].str, 2 * t->hfsp_levels[level - 1].nodes[curnode].strlen); curoff += 2 * t->hfsp_levels[level - 1].nodes[curnode].strlen; iso_msb ((uint8_t *) buffer + curoff, next_lev + curnode, 4); curoff += 4; curnode++; } iso_msb ((uint8_t *) buffer + cat_node_size - j * 2 - 2, curoff, 2); #ifdef Libisofs_hfsplus_verbose_debuG iso_msg_debug(t->image->id, "Write\n"); #endif ret = iso_write(t, buffer, cat_node_size); if (ret < 0) return ret; } curpos = next_lev; } { uint32_t i; uint32_t next_lev = curpos + t->hfsp_levels[level].level_size; for (i = 0; i < t->hfsp_levels[level].level_size; i++) { uint32_t curoff; uint32_t j; uint32_t curnode = t->hfsp_levels[level].nodes[i].start; memset (buffer, 0, sizeof (buffer)); node_head = (struct hfsplus_btnode *) buffer; if (i != t->hfsp_levels[level].level_size - 1) iso_msb ((uint8_t *) &node_head->next, curpos + i + 1, 4); if (i != 0) iso_msb ((uint8_t *) &node_head->prev, curpos + i - 1, 4); node_head->type = -1; node_head->height = level + 1; iso_msb ((uint8_t *) &node_head->count, t->hfsp_levels[level].nodes[i].cnt, 2); curoff = sizeof (struct hfsplus_btnode); for (j = 0; j < t->hfsp_levels[level].nodes[i].cnt; j++) { iso_msb ((uint8_t *) buffer + cat_node_size - j * 2 - 2, curoff, 2); #ifdef Libisofs_hfsplus_verbose_debuG if (t->hfsp_leafs[curnode].node->name == NULL) { iso_msg_debug(t->image->id, "%d out of %d", (int) curnode, t->hfsp_nleafs); } else { iso_msg_debug(t->image->id, "%d out of %d, %s", (int) curnode, t->hfsp_nleafs, t->hfsp_leafs[curnode].node->name); } #endif /* Libisofs_hfsplus_verbose_debuG */ switch (t->hfsp_leafs[curnode].type) { case HFSPLUS_FILE_THREAD: case HFSPLUS_DIR_THREAD: { struct hfsplus_catfile_thread *thread; iso_msb ((uint8_t *) buffer + curoff, 6, 2); iso_msb ((uint8_t *) buffer + curoff + 2, t->hfsp_leafs[curnode].parent_id, 4); iso_msb ((uint8_t *) buffer + curoff + 6, 0, 2); curoff += 8; thread = (struct hfsplus_catfile_thread *) (buffer + curoff); ((uint8_t *) &thread->type)[1] = t->hfsp_leafs[curnode].type; iso_msb ((uint8_t *) &thread->parentid, t->hfsp_leafs[curnode].cat_id, 4); iso_msb ((uint8_t *) &thread->namelen, t->hfsp_leafs[curnode].strlen, 2); curoff += sizeof (*thread); memcpy (buffer + curoff, t->hfsp_leafs[curnode].name, t->hfsp_leafs[curnode].strlen * 2); curoff += t->hfsp_leafs[curnode].strlen * 2; break; } case HFSPLUS_FILE: case HFSPLUS_DIR: { struct hfsplus_catfile_common *common; struct hfsplus_forkdata *data_fork; iso_msb ((uint8_t *) buffer + curoff, 6 + 2 * t->hfsp_leafs[curnode].strlen, 2); iso_msb ((uint8_t *) buffer + curoff + 2, t->hfsp_leafs[curnode].parent_id, 4); iso_msb ((uint8_t *) buffer + curoff + 6, t->hfsp_leafs[curnode].strlen, 2); curoff += 8; memcpy (buffer + curoff, t->hfsp_leafs[curnode].name, t->hfsp_leafs[curnode].strlen * 2); curoff += t->hfsp_leafs[curnode].strlen * 2; common = (struct hfsplus_catfile_common *) (buffer + curoff); ((uint8_t *) &common->type)[1] = t->hfsp_leafs[curnode].type; iso_msb ((uint8_t *) &common->valence, t->hfsp_leafs[curnode].nchildren, 4); iso_msb ((uint8_t *) &common->fileid, t->hfsp_leafs[curnode].cat_id, 4); common->ctime = mac_time_offset(t->hfsp_leafs[curnode].node->ctime); common->mtime = mac_time_offset(t->hfsp_leafs[curnode].node->mtime); /* FIXME: distinguish attr_mtime and mtime. */ common->attr_mtime = mac_time_offset(t->hfsp_leafs[curnode].node->mtime); common->atime = mac_time_offset(t->hfsp_leafs[curnode].node->atime); iso_msb ((uint8_t *) &common->uid, px_get_uid (t, t->hfsp_leafs[curnode].node), 4); iso_msb ((uint8_t *) &common->gid, px_get_gid (t, t->hfsp_leafs[curnode].node), 4); iso_msb ((uint8_t *) &common->mode, px_get_mode (t, t->hfsp_leafs[curnode].node, (t->hfsp_leafs[curnode].type == HFSPLUS_DIR)), 2); /* FIXME: uint8_t user_flags; uint8_t group_flags; finder info */ if (t->hfsp_leafs[curnode].type == HFSPLUS_FILE) { if (t->hfsp_leafs[curnode].unix_type == UNIX_SYMLINK) { memcpy (common->file_type, "slnk", 4); memcpy (common->file_creator, "rhap", 4); } else { struct iso_hfsplus_xinfo_data *xinfo; ret = iso_node_get_xinfo(t->hfsp_leafs[curnode].node, iso_hfsplus_xinfo_func, (void *) &xinfo); if (ret > 0) { memcpy (common->file_type, xinfo->type_code, 4); memcpy (common->file_creator, xinfo->creator_code, 4); #ifdef Libisofs_ts_debuG { char crtp[14]; crtp[0] = '\''; memcpy(crtp+1, xinfo->creator_code, 4); strcpy(crtp + 5, "','"); memcpy(crtp + 8, xinfo->type_code, 4); crtp[12] = '\''; crtp[13]= 0; iso_msg_debug(t->image->id, "hfsplus creator,type %s to '%s/%s'", crtp, ((IsoNode *) t->hfsp_leafs[curnode].node->parent)->name, t->hfsp_leafs[curnode].node->name); } #endif /* Libisofs_ts_debuG */ } else if (ret < 0) return ret; else { memcpy (common->file_type, "????", 4); memcpy (common->file_creator, "????", 4); } } if (t->hfsp_leafs[curnode].unix_type == UNIX_SPECIAL && (S_ISBLK(t->hfsp_leafs[curnode].node->mode) || S_ISCHR(t->hfsp_leafs[curnode].node->mode))) iso_msb ((uint8_t *) &common->special, (((IsoSpecial*) t->hfsp_leafs[curnode].node)->dev & 0xffffffff), 4); iso_msb ((uint8_t *) &common->flags, 2, 2); } else if (t->hfsp_leafs[curnode].type == HFSPLUS_DIR) { iso_msb ((uint8_t *) &common->flags, 0, 2); } curoff += sizeof (*common); if (t->hfsp_leafs[curnode].type == HFSPLUS_FILE) { uint64_t sz; uint32_t blk; data_fork = (struct hfsplus_forkdata *) (buffer + curoff); if (t->hfsp_leafs[curnode].unix_type == UNIX_SYMLINK) { blk = t->hfsp_leafs[curnode].symlink_block; sz = strlen(t->hfsp_leafs[curnode].symlink_dest); } else if (t->hfsp_leafs[curnode].unix_type == UNIX_SPECIAL) { blk = 0; sz = 0; } else { ret = filesrc_block_and_size(t, t->hfsp_leafs[curnode].file, &blk, &sz); if (ret <= 0) return ret; blk *= block_fac; } if (sz == 0) blk = t->hfsp_part_start; iso_msb ((uint8_t *) &data_fork->size, sz >> 32, 4); iso_msb ((uint8_t *) &data_fork->size + 4, sz, 4); iso_msb ((uint8_t *) &data_fork->clumpsize, block_size, 4); iso_msb ((uint8_t *) &data_fork->blocks, (sz + block_size - 1) / block_size, 4); iso_msb ((uint8_t *) &data_fork->extents[0].start, blk - t->hfsp_part_start, 4); iso_msb ((uint8_t *) &data_fork->extents[0].count, (sz + block_size - 1) / block_size, 4); curoff += sizeof (*data_fork) * 2; /* FIXME: resource fork */ } break; } } curnode++; } iso_msb ((uint8_t *) buffer + cat_node_size - j * 2 - 2, curoff, 2); #ifdef Libisofs_hfsplus_verbose_debuG iso_msg_debug(t->image->id, "Write\n"); #endif ret = iso_write(t, buffer, cat_node_size); if (ret < 0) return ret; } curpos = next_lev; } memset (buffer, 0, sizeof (buffer)); iso_msg_debug(t->image->id, "real extent_file_start = %d\n", (int)t->bytes_written / 2048); node_head = (struct hfsplus_btnode *) buffer; node_head->type = 1; iso_msb ((uint8_t *) &node_head->count, 3, 2); tree_head = (struct hfsplus_btheader *) (node_head + 1); iso_msb ((uint8_t *) &tree_head->nodesize, block_size, 2); iso_msb ((uint8_t *) &tree_head->keysize, 10, 2); iso_msb ((uint8_t *) &tree_head->total_nodes, 1, 4); iso_msb ((uint8_t *) &tree_head->free_nodes, 0, 4); iso_msb ((uint8_t *) &tree_head->clump_size, block_size, 4); iso_msb ((uint8_t *) &tree_head->attributes, 2, 4); buffer[0xf8] = (char) 0x80; buffer[block_size - 1] = sizeof (*node_head); buffer[block_size - 3] = sizeof (*node_head) + sizeof (*tree_head); buffer[block_size - 5] = (char) 0xf8; buffer[block_size - 7] = (char) ((block_size - 8) & 0xff); buffer[block_size - 8] = (block_size - 8) >> 8; ret = iso_write(t, buffer, block_size); if (ret < 0) return ret; iso_msg_debug(t->image->id, "(d) %d written", (int) t->bytes_written / 0x800); memset (buffer, 0, sizeof (buffer)); for (i = 0; i < t->hfsp_nleafs; i++) if (t->hfsp_leafs[i].unix_type == UNIX_SYMLINK) { int overhead; ret = iso_write(t, t->hfsp_leafs[i].symlink_dest, strlen(t->hfsp_leafs[i].symlink_dest)); if (ret < 0) return ret; overhead = strlen(t->hfsp_leafs[i].symlink_dest) % block_size; if (overhead) overhead = block_size - overhead; ret = iso_write(t, buffer, overhead); if (ret < 0) return ret; } /* Need to align for start of next writer */ ret = pad_up_block(t); if (ret < 0) return ret; iso_msg_debug(t->image->id, "(a) %d written", (int) t->bytes_written / 0x800); return ISO_SUCCESS; } static int hfsplus_tail_writer_write_data(IsoImageWriter *writer) { int ret; static char buffer[2 * HFSPLUS_MAX_BLOCK_SIZE]; uint32_t complete_blocks, remaining_blocks, block_size; int over; Ecma119Image *t; if (writer == NULL) { return ISO_NULL_POINTER; } t = writer->target; block_size = t->opts->hfsp_block_size; #ifdef Libisofs_ts_debuG iso_msg_debug(t->image->id, "hfsplus tail writer writes at = %.f", (double) t->bytes_written); #endif memset (buffer, -1, sizeof (buffer)); complete_blocks = (t->hfsp_allocation_size - 1) / block_size; remaining_blocks = t->hfsp_allocation_blocks - complete_blocks; while (complete_blocks--) { ret = iso_write(t, buffer, block_size); if (ret < 0) return ret; } over = (t->hfsp_allocation_size - 1) % block_size; if (over) { memset (buffer + over, 0, sizeof (buffer) - over); buffer[over] = 0xff00 >> (t->hfsp_total_blocks % 8); ret = iso_write(t, buffer, block_size); if (ret < 0) return ret; remaining_blocks--; } memset (buffer, 0, sizeof (buffer)); /* When we have both FAT and HFS+ we may to overestimate needed blocks a bit. */ while (remaining_blocks--) { ret = iso_write(t, buffer, block_size); if (ret < 0) return ret; } ret = pad_up_block(t); if (ret < 0) return ret; iso_msg_debug(t->image->id, "%d written", (int) t->bytes_written); ret = write_sb (t); #ifdef Libisofs_ts_debuG iso_msg_debug(t->image->id, "hfsplus tail writer ends at = %.f", (double) t->bytes_written); #endif return ret; } static int hfsplus_writer_free_data(IsoImageWriter *writer) { /* free the Hfsplus tree */ Ecma119Image *t = writer->target; uint32_t i; for (i = 0; i < t->hfsp_curleaf; i++) if (t->hfsp_leafs[i].type != HFSPLUS_FILE_THREAD && t->hfsp_leafs[i].type != HFSPLUS_DIR_THREAD) { free (t->hfsp_leafs[i].name); free (t->hfsp_leafs[i].cmp_name); if (t->hfsp_leafs[i].symlink_dest != NULL) free (t->hfsp_leafs[i].symlink_dest); } free(t->hfsp_leafs); for (i = 0; i < t->hfsp_nlevels; i++) free (t->hfsp_levels[i].nodes); free(t->hfsp_levels); return ISO_SUCCESS; } static int nop_writer_free_data(IsoImageWriter *writer) { return ISO_SUCCESS; } /* ??? : Change this to binary search ? Expected advantage is low except with prefix "MANGLED". @param flag bit0= array is unsorted, do not abort on first larger element @return 0 = collision (collider in *new_idx), 1 = insert at *new_idx */ static int search_mangled_pos(Ecma119Image *target, uint32_t idx, uint32_t *new_idx, uint32_t search_start, uint32_t search_end, int flag) { uint32_t i; int rel; for (i = search_start; i < search_end; i++) { if (target->hfsp_leafs[i].type == HFSPLUS_DIR_THREAD || target->hfsp_leafs[i].type == HFSPLUS_FILE_THREAD) continue; rel = cmp_node(&(target->hfsp_leafs[idx]), &(target->hfsp_leafs[i])); if (rel == 0 && idx != i) { *new_idx = i; return 0; /* Collision */ } if (rel < 0 && !(flag & 1)) { if (i <= idx) *new_idx = i; else *new_idx = i - 1; return 1; } } *new_idx = search_end - 1; return 1; } static void rotate_hfs_list(Ecma119Image *target, uint32_t old_idx, uint32_t new_idx, int flag) { uint32_t i, sz; HFSPlusNode tr; if (old_idx == new_idx) return; sz = sizeof(HFSPlusNode); memcpy(&tr, &target->hfsp_leafs[old_idx], sz); if (old_idx > new_idx) { for (i = old_idx; i > new_idx; i--) memcpy(&target->hfsp_leafs[i], &target->hfsp_leafs[i - 1], sz); } else { for (i = old_idx; i < new_idx; i++) memcpy(&target->hfsp_leafs[i], &target->hfsp_leafs[i + 1], sz); } memcpy(&target->hfsp_leafs[new_idx], &tr, sz); } static int subst_symlink_dest_comp(Ecma119Image *target, uint32_t idx, char **dest, unsigned int *dest_len, char **comp_start, char **comp_end, char *new_name, int flag) { int new_len; unsigned int new_dest_len; char *new_dest, *wpt; new_len = strlen(new_name); new_dest_len = *comp_start - *dest + new_len + *dest_len - (*comp_end - *dest); new_dest = calloc(1, new_dest_len + 1); if (new_dest == NULL) return ISO_OUT_OF_MEM; wpt = new_dest; if (*comp_start - *dest > 0) memcpy(wpt, *dest, *comp_start - *dest); wpt += *comp_start - *dest; memcpy(wpt, new_name, new_len); wpt += new_len; if ((unsigned int) (*comp_end - *dest) < *dest_len) memcpy(wpt, *comp_end, *dest_len - (*comp_end - *dest)); wpt += *dest_len - (*comp_end - *dest); *wpt = 0; *comp_start = new_dest + (*comp_start - *dest); *comp_end = *comp_start + new_len; target->hfsp_leafs[idx].symlink_dest = new_dest; *dest_len = new_dest_len; free(*dest); *dest = new_dest; return ISO_SUCCESS; } /* A specialized version of API call iso_tree_resolve_symlink(). It updates symlink destination components which lead to the HFS+ node [changed_idx] in sync with resolution of the IsoImage destination path. It seems too much prone to weird link loopings if one would let a function underneath iso_tree_resolve_symlink() watch out for the IsoNode in question. Multiple passes through that node are possible. So this function exchanges components when encountered. */ static int update_symlink(Ecma119Image *target, uint32_t changed_idx, char *new_name, uint32_t link_idx, int *depth, int flag) { IsoSymlink *sym; IsoDir *cur_dir = NULL; IsoNode *n, *resolved_node; char *orig_dest, *orig_start, *orig_end; char *hfsp_dest, *hfsp_start, *hfsp_end; int ret = 0; unsigned int comp_len, orig_len, hfsp_len; if (target->hfsp_leafs[link_idx].node->type != LIBISO_SYMLINK) return ISO_SUCCESS; sym = (IsoSymlink *) target->hfsp_leafs[link_idx].node; orig_dest = sym->dest; orig_len = strlen(orig_dest); hfsp_dest = target->hfsp_leafs[link_idx].symlink_dest; hfsp_len = strlen(hfsp_dest); if (orig_dest[0] == '/') { /* >>> ??? How to salvage absolute links without knowing the path of the future mount point ? ??? Would it be better to leave them as is ? I can only assume that it gets mounted at / during some stage of booting. */; cur_dir = target->image->root; orig_end = orig_dest; } else { cur_dir = sym->node.parent; if (cur_dir == NULL) cur_dir = target->image->root; orig_end = orig_dest - 1; } if (hfsp_dest[0] == '/') hfsp_end = hfsp_dest; else hfsp_end = hfsp_dest - 1; while (orig_end < orig_dest + orig_len) { orig_start = orig_end + 1; hfsp_start = hfsp_end + 1; orig_end = strchr(orig_start, '/'); if (orig_end == NULL) orig_end = orig_start + strlen(orig_start); comp_len = orig_end - orig_start; hfsp_end = strchr(hfsp_start, '/'); if (hfsp_end == NULL) hfsp_end = hfsp_start + strlen(hfsp_start); if (comp_len == 0 || (comp_len == 1 && orig_start[0] == '.')) continue; if (comp_len == 2 && orig_start[0] == '.' && orig_start[1] == '.') { cur_dir = cur_dir->node.parent; if (cur_dir == NULL) /* link shoots over root */ return ISO_SUCCESS; continue; } /* Search node in cur_dir */ for (n = cur_dir->children; n != NULL; n = n->next) if (strncmp(orig_start, n->name, comp_len) == 0 && strlen(n->name) == comp_len) break; if (n == NULL) /* dead link */ return ISO_SUCCESS; if (n == target->hfsp_leafs[changed_idx].node) { iso_msg_debug(target->image->id, " link path '%s' touches RR '%s', HFS+ '%s'", orig_dest, (n->name != NULL ? n->name : ""), new_name); /* Exchange HFS+ component by new_name */ ret = subst_symlink_dest_comp(target, link_idx, &hfsp_dest, &hfsp_len, &hfsp_start, &hfsp_end, new_name, 0); if (ret < 0) return ret; } if (n->type == LIBISO_DIR) { cur_dir = (IsoDir *) n; } else if (n->type == LIBISO_SYMLINK) { /* Resolve link and check whether it is a directory */ if (*depth >= LIBISO_MAX_LINK_DEPTH) return ISO_SUCCESS; (*depth)++; ret = iso_tree_resolve_symlink(target->image, (IsoSymlink *) n, &resolved_node, depth, 0); if (ret == (int) ISO_DEAD_SYMLINK || ret == (int) ISO_DEEP_SYMLINK) return ISO_SUCCESS; if (ret < 0) return ret; if (resolved_node->type != LIBISO_DIR) return ISO_SUCCESS; cur_dir = (IsoDir *) resolved_node; } else { break; } } return ISO_SUCCESS; } /* Find the other nodes with old_name and switch to new .name One could make assumptions where name-followers are. But then there are still the symbolic links. They can be located anywhere. */ static int update_name_followers(Ecma119Image *target, uint32_t idx, char *new_name, uint16_t *old_name, uint16_t *old_cmp_name, uint32_t old_strlen) { uint32_t i; int ret, link_depth; for (i = 0; i < target->hfsp_nleafs; i++) { if (target->hfsp_leafs[i].unix_type == UNIX_SYMLINK) { link_depth = 0; ret = update_symlink(target, idx, new_name, i, &link_depth, 0); if (ret < 0) return ret; } if (target->hfsp_leafs[i].name != old_name) continue; target->hfsp_leafs[i].name = target->hfsp_leafs[idx].name; target->hfsp_leafs[i].strlen = target->hfsp_leafs[idx].strlen; if (target->hfsp_leafs[i].cmp_name == old_cmp_name) target->hfsp_leafs[i].cmp_name = target->hfsp_leafs[idx].cmp_name; if (target->hfsp_leafs[i].strlen > old_strlen) target->hfsp_leafs[i].used_size += (target->hfsp_leafs[i].strlen - old_strlen) * 2; else target->hfsp_leafs[i].used_size -= 2 * (old_strlen - target->hfsp_leafs[i].strlen); } return 1; } /* @param flag bit0= node is new: do not rotate, do not update followers */ static int try_mangle(Ecma119Image *target, uint32_t idx, uint32_t prev_idx, uint32_t search_start, uint32_t search_end, uint32_t *new_idx, char *prefix, int flag) { int i, ret = 0; char new_name[LIBISO_HFSPLUS_NAME_MAX + 1], number[9]; uint16_t *old_name, *old_cmp_name; uint32_t old_strlen; old_name = target->hfsp_leafs[idx].name; old_cmp_name = target->hfsp_leafs[idx].cmp_name; old_strlen = target->hfsp_leafs[idx].strlen; for (i = -1; i < 0x7fffffff; i++) { if (i == -1) number[0] = 0; else sprintf(number, "%X", (unsigned int) i); if (strlen(prefix) + 1 + strlen(number) > LIBISO_HFSPLUS_NAME_MAX) { ret = 0; goto no_success; } /* "-" would sort lower than capital letters , traditional "_" causes longer rotations */ sprintf(new_name, "%s_%s", prefix, number); /* The original name is kept until the end of the try */ if (target->hfsp_leafs[idx].name != old_name) free(target->hfsp_leafs[idx].name); if (target->hfsp_leafs[idx].cmp_name != old_cmp_name) free(target->hfsp_leafs[idx].cmp_name); ret = set_hfsplus_name(target, new_name, &(target->hfsp_leafs[idx])); if (ret < 0) goto no_success; ret = search_mangled_pos(target, idx, new_idx, search_start, search_end, (flag & 1)); if (ret < 0) goto no_success; if (ret == 0) continue; /* collision */ if (flag & 1) *new_idx = idx; else rotate_hfs_list(target, idx, *new_idx, 0); /* >>> Get full ISO-RR paths of colliding nodes */; /* >>> iso_tree_get_node_path(node); */ iso_msg_debug(target->image->id, "HFS+ name collision with \"%s\" : \"%s\" renamed to \"%s\"", target->hfsp_leafs[prev_idx].node->name, target->hfsp_leafs[*new_idx].node->name, new_name); break; } target->hfsp_leafs[*new_idx].used_size += (target->hfsp_leafs[*new_idx].strlen - old_strlen) * 2; if (!(flag & 1)) { ret = update_name_followers(target, *new_idx, new_name, old_name, old_cmp_name, old_strlen); if (ret < 0) goto no_success; } free(old_name); free(old_cmp_name); return 1; no_success:; target->hfsp_leafs[idx].name = old_name; target->hfsp_leafs[idx].cmp_name = old_cmp_name; target->hfsp_leafs[idx].strlen = old_strlen; return ret; } static int mangle_leafs(Ecma119Image *target, int flag) { int ret; uint32_t i, new_idx, prev, first_prev; iso_msg_debug(target->image->id, "%s", "HFS+ mangling started ..."); /* Look for the first owner of a name */ for (prev = 0; prev < target->hfsp_nleafs; prev++) { if (target->hfsp_leafs[prev].type == HFSPLUS_DIR_THREAD || target->hfsp_leafs[prev].type == HFSPLUS_FILE_THREAD || target->hfsp_leafs[prev].node == NULL || target->hfsp_leafs[prev].name == NULL || target->hfsp_leafs[prev].cmp_name == NULL) continue; if (target->hfsp_leafs[prev].node->name == NULL) continue; break; } first_prev = prev; for (i = prev + 1; i < target->hfsp_nleafs; i++) { if (target->hfsp_leafs[i].type == HFSPLUS_DIR_THREAD || target->hfsp_leafs[i].type == HFSPLUS_FILE_THREAD || target->hfsp_leafs[i].node == NULL || target->hfsp_leafs[i].name == NULL || target->hfsp_leafs[i].cmp_name == NULL) continue; if (target->hfsp_leafs[i].node->name == NULL) continue; if (cmp_node(&(target->hfsp_leafs[prev]), &(target->hfsp_leafs[i])) != 0) { prev = i; continue; } target->hfsp_collision_count++; #ifdef Libisofs_with_mangle_masK /* >>> Development sketch: */ /* >>> define in libisofs.h : enum with LIBISO_NOMANGLE_xyz xinfo function for uint32_t */ /* >>> inquire xinfo for mangle protection : uint32_t mangle_mask */ if (mangle_mask & (1 << LIBISO_NOMANGLE_HFSPLUS)) { /* >>> Get full ISO-RR paths of colliding nodes and print error message */; return ISO_HFSP_NO_MANGLE; } else { #else /* Libisofs_with_mangle_masK */ { #endif /* ! Libisofs_with_mangle_masK */ ret= try_mangle(target, i, prev, i + 1, target->hfsp_nleafs, &new_idx, target->hfsp_leafs[i].node->name, 0); if (ret == 0) ret= try_mangle(target, i, prev, 0, target->hfsp_nleafs, &new_idx, "MANGLED", 0); if (ret < 0) return(ret); if (new_idx > i) { i--; /* an unprocessed candidate has been rotated to i */ } else { prev = i; /* advance */ } } } if (target->hfsp_collision_count > 0) { /* Mangling cannot be properly performed if the name owners do not stay in sorting order. */ prev = first_prev; for (i = prev + 1; i < target->hfsp_nleafs; i++) { if (target->hfsp_leafs[i].type == HFSPLUS_DIR_THREAD || target->hfsp_leafs[i].type == HFSPLUS_FILE_THREAD || target->hfsp_leafs[i].node == NULL || target->hfsp_leafs[i].name == NULL || target->hfsp_leafs[i].cmp_name == NULL) continue; if (target->hfsp_leafs[i].node->name == NULL) continue; if (cmp_node(&(target->hfsp_leafs[prev]), &(target->hfsp_leafs[i])) > 0) { iso_msg_debug(target->image->id, "*********** Mangling messed up sorting *************\n"); break; } prev = i; } /* Only the owners of names were considered during mangling. The HFSPLUS_*_THREAD types must get in line by sorting again. */ qsort(target->hfsp_leafs, target->hfsp_nleafs, sizeof(*target->hfsp_leafs), cmp_node); } iso_msg_debug(target->image->id, "HFS+ mangling done. Resolved Collisions: %lu", (unsigned long) target->hfsp_collision_count); return ISO_SUCCESS; } void iso_setup_hfsplus_block_size(Ecma119Image *target) { if (target->opts->hfsp_block_size == 0) target->opts->hfsp_block_size = HFSPLUS_DEFAULT_BLOCK_SIZE; target->hfsp_cat_node_size = 2 * target->opts->hfsp_block_size; target->hfsp_iso_block_fac = 2048 / target->opts->hfsp_block_size; } int hfsplus_writer_create(Ecma119Image *target) { int ret; IsoImageWriter *writer = NULL; int max_levels; int level = 0; IsoNode *pos; IsoDir *dir; int i; uint32_t cat_node_size; writer = calloc(1, sizeof(IsoImageWriter)); if (writer == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } make_hfsplus_decompose_pages(); make_hfsplus_class_pages(); iso_setup_hfsplus_block_size(target); cat_node_size = target->hfsp_cat_node_size; writer->compute_data_blocks = hfsplus_writer_compute_data_blocks; writer->write_vol_desc = nop_writer_write_vol_desc; writer->write_data = hfsplus_writer_write_data; writer->free_data = hfsplus_writer_free_data; writer->data = NULL; writer->target = target; iso_msg_debug(target->image->id, "Creating HFS+ tree..."); target->hfsp_nfiles = 0; target->hfsp_ndirs = 0; target->hfsp_cat_id = 16; ret = hfsplus_count_tree(target, (IsoNode*)target->image->root); if (ret < 0) goto ex; for (i = 0; i < ISO_HFSPLUS_BLESS_MAX; i++) target->hfsp_bless_id[i] = 0; target->hfsp_nleafs = 2 * (target->hfsp_nfiles + target->hfsp_ndirs); target->hfsp_curleaf = 0; target->hfsp_leafs = calloc (target->hfsp_nleafs, sizeof (target->hfsp_leafs[0])); if (target->hfsp_leafs == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } ret = set_hfsplus_name (target, target->image->volume_id, &target->hfsp_leafs[target->hfsp_curleaf]); if (ret < 0) goto ex; target->hfsp_leafs[target->hfsp_curleaf].node = (IsoNode *) target->image->root; target->hfsp_leafs[target->hfsp_curleaf].used_size = target->hfsp_leafs[target->hfsp_curleaf].strlen * 2 + 8 + 2 + sizeof (struct hfsplus_catfile_common); target->hfsp_leafs[target->hfsp_curleaf].type = HFSPLUS_DIR; target->hfsp_leafs[target->hfsp_curleaf].file = 0; target->hfsp_leafs[target->hfsp_curleaf].cat_id = 2; target->hfsp_leafs[target->hfsp_curleaf].parent_id = 1; target->hfsp_leafs[target->hfsp_curleaf].nchildren = 0; target->hfsp_leafs[target->hfsp_curleaf].unix_type = UNIX_NONE; target->hfsp_curleaf++; target->hfsp_leafs[target->hfsp_curleaf].name = target->hfsp_leafs[target->hfsp_curleaf - 1].name; target->hfsp_leafs[target->hfsp_curleaf].cmp_name = 0; target->hfsp_leafs[target->hfsp_curleaf].strlen = target->hfsp_leafs[target->hfsp_curleaf - 1].strlen; target->hfsp_leafs[target->hfsp_curleaf].used_size = target->hfsp_leafs[target->hfsp_curleaf].strlen * 2 + 8 + 2 + sizeof (struct hfsplus_catfile_thread); target->hfsp_leafs[target->hfsp_curleaf].node = (IsoNode *) target->image->root; target->hfsp_leafs[target->hfsp_curleaf].type = HFSPLUS_DIR_THREAD; target->hfsp_leafs[target->hfsp_curleaf].file = 0; target->hfsp_leafs[target->hfsp_curleaf].cat_id = 1; target->hfsp_leafs[target->hfsp_curleaf].parent_id = 2; target->hfsp_leafs[target->hfsp_curleaf].unix_type = UNIX_NONE; target->hfsp_curleaf++; dir = (IsoDir*)target->image->root; pos = dir->children; while (pos) { int cret; cret = create_tree(target, pos, 2); if (cret < 0) { ret = cret; goto ex; } pos = pos->next; if (cret > 0) target->hfsp_leafs[0].nchildren++; } qsort(target->hfsp_leafs, target->hfsp_nleafs, sizeof(*target->hfsp_leafs), cmp_node); ret = mangle_leafs(target, 0); if (ret < 0) goto ex; for (max_levels = 0; target->hfsp_nleafs >> max_levels; max_levels++); max_levels += 2; target->hfsp_levels = calloc (max_levels, sizeof (target->hfsp_levels[0])); if (target->hfsp_levels == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } target->hfsp_nnodes = 1; { uint32_t last_start = 0; uint32_t i; unsigned bytes_rem = cat_node_size - sizeof (struct hfsplus_btnode) - 2; target->hfsp_levels[level].nodes = calloc ((target->hfsp_nleafs + 1), sizeof (target->hfsp_levels[level].nodes[0])); if (!target->hfsp_levels[level].nodes) { ret = ISO_OUT_OF_MEM; goto ex; } target->hfsp_levels[level].level_size = 0; for (i = 0; i < target->hfsp_nleafs; i++) { if (bytes_rem < target->hfsp_leafs[i].used_size) { target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].start = last_start; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].cnt = i - last_start; if (target->hfsp_leafs[last_start].cmp_name) { target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].strlen = target->hfsp_leafs[last_start].strlen; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].str = target->hfsp_leafs[last_start].name; } else { target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].strlen = 0; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].str = NULL; } target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].parent_id = target->hfsp_leafs[last_start].parent_id; target->hfsp_levels[level].level_size++; last_start = i; bytes_rem = cat_node_size - sizeof (struct hfsplus_btnode) - 2; } bytes_rem -= target->hfsp_leafs[i].used_size; } target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].start = last_start; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].cnt = i - last_start; if (target->hfsp_leafs[last_start].cmp_name) { target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].strlen = target->hfsp_leafs[last_start].strlen; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].str = target->hfsp_leafs[last_start].name; } else { target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].strlen = 0; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].str = NULL; } target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].parent_id = target->hfsp_leafs[last_start].parent_id; target->hfsp_levels[level].level_size++; target->hfsp_nnodes += target->hfsp_levels[level].level_size; } while (target->hfsp_levels[level].level_size > 1) { uint32_t last_start = 0; uint32_t i; uint32_t last_size; unsigned bytes_rem = cat_node_size - sizeof (struct hfsplus_btnode) - 2; last_size = target->hfsp_levels[level].level_size; level++; target->hfsp_levels[level].nodes = calloc (((last_size + 1) / 2), sizeof (target->hfsp_levels[level].nodes[0])); if (!target->hfsp_levels[level].nodes) { ret = ISO_OUT_OF_MEM; goto ex; } target->hfsp_levels[level].level_size = 0; for (i = 0; i < last_size; i++) { uint32_t used_size; used_size = target->hfsp_levels[level - 1].nodes[i].strlen * 2 + 14; if (bytes_rem < used_size) { target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].start = last_start; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].cnt = i - last_start; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].strlen = target->hfsp_levels[level - 1].nodes[last_start].strlen; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].str = target->hfsp_levels[level - 1].nodes[last_start].str; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].parent_id = target->hfsp_levels[level - 1].nodes[last_start].parent_id; target->hfsp_levels[level].level_size++; last_start = i; bytes_rem = cat_node_size - sizeof (struct hfsplus_btnode) - 2; } bytes_rem -= used_size; } target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].start = last_start; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].cnt = i - last_start; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].strlen = target->hfsp_levels[level - 1].nodes[last_start].strlen; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].str = target->hfsp_levels[level - 1].nodes[last_start].str; target->hfsp_levels[level].nodes[target->hfsp_levels[level].level_size].parent_id = target->hfsp_levels[level - 1].nodes[last_start].parent_id; target->hfsp_levels[level].level_size++; target->hfsp_nnodes += target->hfsp_levels[level].level_size; } target->hfsp_nlevels = level + 1; if (target->hfsp_nnodes > (cat_node_size - 0x100) * 8) { iso_msg_submit(target->image->id, ISO_HFSPLUS_TOO_MANY_FILES, 0, "HFS+ map nodes aren't implemented"); ret = ISO_HFSPLUS_TOO_MANY_FILES; goto ex; } /* add this writer to image */ target->writers[target->nwriters++] = writer; writer = NULL; ret = ISO_SUCCESS; ex:; if (writer != NULL) free(writer); return ret; } int hfsplus_tail_writer_create(Ecma119Image *target) { IsoImageWriter *writer; writer = calloc(1, sizeof(IsoImageWriter)); if (writer == NULL) { return ISO_OUT_OF_MEM; } writer->compute_data_blocks = hfsplus_tail_writer_compute_data_blocks; writer->write_vol_desc = nop_writer_write_vol_desc; writer->write_data = hfsplus_tail_writer_write_data; writer->free_data = nop_writer_free_data; writer->data = NULL; writer->target = target; /* add this writer to image */ target->writers[target->nwriters++] = writer; return ISO_SUCCESS; } /* API */ int iso_hfsplus_xinfo_func(void *data, int flag) { if (flag == 1 && data != NULL) free(data); return 1; } /* API */ struct iso_hfsplus_xinfo_data *iso_hfsplus_xinfo_new(int flag) { struct iso_hfsplus_xinfo_data *o; o = calloc(1, sizeof(struct iso_hfsplus_xinfo_data)); if (o == NULL) return NULL; o->version = 0; return o; } /* The iso_node_xinfo_cloner function which gets associated to * iso_hfsplus_xinfo_func by iso_init() or iso_init_with_flag() via * iso_node_xinfo_make_clonable() */ int iso_hfsplus_xinfo_cloner(void *old_data, void **new_data, int flag) { *new_data = NULL; if (flag) return ISO_XINFO_NO_CLONE; if (old_data == NULL) return 0; *new_data = iso_hfsplus_xinfo_new(0); if(*new_data == NULL) return ISO_OUT_OF_MEM; memcpy(*new_data, old_data, sizeof(struct iso_hfsplus_xinfo_data)); return ISO_SUCCESS; } /* * Copyright (c) 2012 Vladimir Serbinenko * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /** * Declare HFS+ related structures. */ #ifndef LIBISO_HFSPLUS_H #define LIBISO_HFSPLUS_H #include "libisofs.h" #include "ecma119.h" #define LIBISO_HFSPLUS_NAME_MAX 255 enum hfsplus_node_type { HFSPLUS_DIR = 1, HFSPLUS_FILE = 2, HFSPLUS_DIR_THREAD = 3, HFSPLUS_FILE_THREAD = 4 }; struct hfsplus_btree_node { uint32_t start; uint32_t cnt; uint32_t strlen; uint16_t *str; uint32_t parent_id; }; struct hfsplus_btree_level { uint32_t level_size; struct hfsplus_btree_node *nodes; }; struct hfsplus_node { /* Note: .type HFSPLUS_DIR_THREAD and HFSPLUS_FILE_THREAD do not own their .name and .cmp_name. They have copies of others, if ever. */ uint16_t *name; /* Name in UTF-16BE, decomposed. */ uint16_t *cmp_name; /* Name used for comparing. */ IsoNode *node; /*< reference to the iso node */ enum { UNIX_NONE, UNIX_SYMLINK, UNIX_SPECIAL } unix_type; uint32_t symlink_block; char *symlink_dest; enum hfsplus_node_type type; IsoFileSrc *file; uint32_t cat_id; uint32_t parent_id; uint32_t nchildren; uint32_t strlen; uint32_t used_size; }; int hfsplus_writer_create(Ecma119Image *target); int hfsplus_tail_writer_create(Ecma119Image *target); struct hfsplus_extent { /* The first block of a file on disk. */ uint32_t start; /* The amount of blocks described by this extent. */ uint32_t count; } __attribute__ ((packed)); struct hfsplus_forkdata { uint64_t size; uint32_t clumpsize; uint32_t blocks; struct hfsplus_extent extents[8]; } __attribute__ ((packed)); struct hfsplus_volheader { uint16_t magic; uint16_t version; uint32_t attributes; uint32_t last_mounted_version; uint32_t journal; uint32_t ctime; uint32_t utime; uint32_t backup_time; uint32_t fsck_time; uint32_t file_count; uint32_t folder_count; uint32_t blksize; uint32_t total_blocks; uint32_t free_blocks; uint32_t next_allocation; uint32_t rsrc_clumpsize; uint32_t data_clumpsize; uint32_t catalog_node_id; uint32_t write_count; uint64_t encodings_bitmap; uint32_t ppc_bootdir; uint32_t intel_bootfile; /* Folder opened when disk is mounted. */ uint32_t showfolder; uint32_t os9folder; uint32_t unused; uint32_t osxfolder; uint64_t num_serial; struct hfsplus_forkdata allocations_file; struct hfsplus_forkdata extents_file; struct hfsplus_forkdata catalog_file; struct hfsplus_forkdata attrib_file; struct hfsplus_forkdata startup_file; } __attribute__ ((packed)); struct hfsplus_btnode { uint32_t next; uint32_t prev; int8_t type; uint8_t height; uint16_t count; uint16_t unused; } __attribute__ ((packed)); /* The header of a HFS+ B+ Tree. */ struct hfsplus_btheader { uint16_t depth; uint32_t root; uint32_t leaf_records; uint32_t first_leaf_node; uint32_t last_leaf_node; uint16_t nodesize; uint16_t keysize; uint32_t total_nodes; uint32_t free_nodes; uint16_t reserved1; uint32_t clump_size; uint8_t btree_type; uint8_t key_compare; uint32_t attributes; uint32_t reserved[16]; } __attribute__ ((packed)); struct hfsplus_catfile_thread { uint16_t type; uint16_t reserved; uint32_t parentid; uint16_t namelen; } __attribute__ ((packed)); struct hfsplus_catfile_common { uint16_t type; uint16_t flags; uint32_t valence; /* for files: reserved. */ uint32_t fileid; uint32_t ctime; uint32_t mtime; uint32_t attr_mtime; uint32_t atime; uint32_t backup_time; uint32_t uid; uint32_t gid; uint8_t user_flags; uint8_t group_flags; uint16_t mode; uint32_t special; uint8_t file_type[4]; /* For folders: window size */ uint8_t file_creator[4]; /* For folders: window size */ uint8_t finder_info[24]; uint32_t text_encoding; uint32_t reserved; } __attribute__ ((packed)); #define HFSPLUS_MAX_DECOMPOSE_LEN 4 extern uint16_t (*hfsplus_decompose_pages[256])[HFSPLUS_MAX_DECOMPOSE_LEN + 1]; void make_hfsplus_decompose_pages(); extern uint16_t *hfsplus_class_pages[256]; void make_hfsplus_class_pages(); extern const uint16_t hfsplus_casefold[]; int iso_get_hfsplus_name(char *input_charset, int imgid, char *name, uint16_t **result, uint32_t *result_len, uint16_t **cmp_name); void iso_setup_hfsplus_block_size(Ecma119Image *target); #endif /* LIBISO_HFSPLUS_H */ /* * Copyright (c) 2012 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /** * Maps UTF-16BE double-byte characters to the representative of their * equivalence class under the relation of HFS+ case-insensitivity. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libisofs.h" /* The translation list utf16be_transl was generated by a program which compared input and output of existing example code by Apple Inc. found published on http://developer.apple.com/legacy/mac/library/#technotes/tn/tn1150.html Each deviation was recorded as pair of byte pairs. The first pair gives the input, the second pair gives the output. If a byte pair is not mentioned in this list as input, then it gets mapped to itself. Pairs which get mapped to pair 0,0 shall be ignored with HFS+ comparisons. Another comparison run verified that both implementations yield the same character translation with all 65536 possible input bit patterns. */ static uint8_t utf16be_transl[] = { 0x00, 0x00, 0xff, 0xff, 0x00, 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x43, 0x00, 0x63, 0x00, 0x44, 0x00, 0x64, 0x00, 0x45, 0x00, 0x65, 0x00, 0x46, 0x00, 0x66, 0x00, 0x47, 0x00, 0x67, 0x00, 0x48, 0x00, 0x68, 0x00, 0x49, 0x00, 0x69, 0x00, 0x4a, 0x00, 0x6a, 0x00, 0x4b, 0x00, 0x6b, 0x00, 0x4c, 0x00, 0x6c, 0x00, 0x4d, 0x00, 0x6d, 0x00, 0x4e, 0x00, 0x6e, 0x00, 0x4f, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x70, 0x00, 0x51, 0x00, 0x71, 0x00, 0x52, 0x00, 0x72, 0x00, 0x53, 0x00, 0x73, 0x00, 0x54, 0x00, 0x74, 0x00, 0x55, 0x00, 0x75, 0x00, 0x56, 0x00, 0x76, 0x00, 0x57, 0x00, 0x77, 0x00, 0x58, 0x00, 0x78, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5a, 0x00, 0x7a, 0x00, 0xc6, 0x00, 0xe6, 0x00, 0xd0, 0x00, 0xf0, 0x00, 0xd8, 0x00, 0xf8, 0x00, 0xde, 0x00, 0xfe, 0x01, 0x10, 0x01, 0x11, 0x01, 0x26, 0x01, 0x27, 0x01, 0x32, 0x01, 0x33, 0x01, 0x3f, 0x01, 0x40, 0x01, 0x41, 0x01, 0x42, 0x01, 0x4a, 0x01, 0x4b, 0x01, 0x52, 0x01, 0x53, 0x01, 0x66, 0x01, 0x67, 0x01, 0x81, 0x02, 0x53, 0x01, 0x82, 0x01, 0x83, 0x01, 0x84, 0x01, 0x85, 0x01, 0x86, 0x02, 0x54, 0x01, 0x87, 0x01, 0x88, 0x01, 0x89, 0x02, 0x56, 0x01, 0x8a, 0x02, 0x57, 0x01, 0x8b, 0x01, 0x8c, 0x01, 0x8e, 0x01, 0xdd, 0x01, 0x8f, 0x02, 0x59, 0x01, 0x90, 0x02, 0x5b, 0x01, 0x91, 0x01, 0x92, 0x01, 0x93, 0x02, 0x60, 0x01, 0x94, 0x02, 0x63, 0x01, 0x96, 0x02, 0x69, 0x01, 0x97, 0x02, 0x68, 0x01, 0x98, 0x01, 0x99, 0x01, 0x9c, 0x02, 0x6f, 0x01, 0x9d, 0x02, 0x72, 0x01, 0x9f, 0x02, 0x75, 0x01, 0xa2, 0x01, 0xa3, 0x01, 0xa4, 0x01, 0xa5, 0x01, 0xa7, 0x01, 0xa8, 0x01, 0xa9, 0x02, 0x83, 0x01, 0xac, 0x01, 0xad, 0x01, 0xae, 0x02, 0x88, 0x01, 0xb1, 0x02, 0x8a, 0x01, 0xb2, 0x02, 0x8b, 0x01, 0xb3, 0x01, 0xb4, 0x01, 0xb5, 0x01, 0xb6, 0x01, 0xb7, 0x02, 0x92, 0x01, 0xb8, 0x01, 0xb9, 0x01, 0xbc, 0x01, 0xbd, 0x01, 0xc4, 0x01, 0xc6, 0x01, 0xc5, 0x01, 0xc6, 0x01, 0xc7, 0x01, 0xc9, 0x01, 0xc8, 0x01, 0xc9, 0x01, 0xca, 0x01, 0xcc, 0x01, 0xcb, 0x01, 0xcc, 0x01, 0xe4, 0x01, 0xe5, 0x01, 0xf1, 0x01, 0xf3, 0x01, 0xf2, 0x01, 0xf3, 0x03, 0x91, 0x03, 0xb1, 0x03, 0x92, 0x03, 0xb2, 0x03, 0x93, 0x03, 0xb3, 0x03, 0x94, 0x03, 0xb4, 0x03, 0x95, 0x03, 0xb5, 0x03, 0x96, 0x03, 0xb6, 0x03, 0x97, 0x03, 0xb7, 0x03, 0x98, 0x03, 0xb8, 0x03, 0x99, 0x03, 0xb9, 0x03, 0x9a, 0x03, 0xba, 0x03, 0x9b, 0x03, 0xbb, 0x03, 0x9c, 0x03, 0xbc, 0x03, 0x9d, 0x03, 0xbd, 0x03, 0x9e, 0x03, 0xbe, 0x03, 0x9f, 0x03, 0xbf, 0x03, 0xa0, 0x03, 0xc0, 0x03, 0xa1, 0x03, 0xc1, 0x03, 0xa3, 0x03, 0xc3, 0x03, 0xa4, 0x03, 0xc4, 0x03, 0xa5, 0x03, 0xc5, 0x03, 0xa6, 0x03, 0xc6, 0x03, 0xa7, 0x03, 0xc7, 0x03, 0xa8, 0x03, 0xc8, 0x03, 0xa9, 0x03, 0xc9, 0x03, 0xe2, 0x03, 0xe3, 0x03, 0xe4, 0x03, 0xe5, 0x03, 0xe6, 0x03, 0xe7, 0x03, 0xe8, 0x03, 0xe9, 0x03, 0xea, 0x03, 0xeb, 0x03, 0xec, 0x03, 0xed, 0x03, 0xee, 0x03, 0xef, 0x04, 0x02, 0x04, 0x52, 0x04, 0x04, 0x04, 0x54, 0x04, 0x05, 0x04, 0x55, 0x04, 0x06, 0x04, 0x56, 0x04, 0x08, 0x04, 0x58, 0x04, 0x09, 0x04, 0x59, 0x04, 0x0a, 0x04, 0x5a, 0x04, 0x0b, 0x04, 0x5b, 0x04, 0x0f, 0x04, 0x5f, 0x04, 0x10, 0x04, 0x30, 0x04, 0x11, 0x04, 0x31, 0x04, 0x12, 0x04, 0x32, 0x04, 0x13, 0x04, 0x33, 0x04, 0x14, 0x04, 0x34, 0x04, 0x15, 0x04, 0x35, 0x04, 0x16, 0x04, 0x36, 0x04, 0x17, 0x04, 0x37, 0x04, 0x18, 0x04, 0x38, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1b, 0x04, 0x3b, 0x04, 0x1c, 0x04, 0x3c, 0x04, 0x1d, 0x04, 0x3d, 0x04, 0x1e, 0x04, 0x3e, 0x04, 0x1f, 0x04, 0x3f, 0x04, 0x20, 0x04, 0x40, 0x04, 0x21, 0x04, 0x41, 0x04, 0x22, 0x04, 0x42, 0x04, 0x23, 0x04, 0x43, 0x04, 0x24, 0x04, 0x44, 0x04, 0x25, 0x04, 0x45, 0x04, 0x26, 0x04, 0x46, 0x04, 0x27, 0x04, 0x47, 0x04, 0x28, 0x04, 0x48, 0x04, 0x29, 0x04, 0x49, 0x04, 0x2a, 0x04, 0x4a, 0x04, 0x2b, 0x04, 0x4b, 0x04, 0x2c, 0x04, 0x4c, 0x04, 0x2d, 0x04, 0x4d, 0x04, 0x2e, 0x04, 0x4e, 0x04, 0x2f, 0x04, 0x4f, 0x04, 0x60, 0x04, 0x61, 0x04, 0x62, 0x04, 0x63, 0x04, 0x64, 0x04, 0x65, 0x04, 0x66, 0x04, 0x67, 0x04, 0x68, 0x04, 0x69, 0x04, 0x6a, 0x04, 0x6b, 0x04, 0x6c, 0x04, 0x6d, 0x04, 0x6e, 0x04, 0x6f, 0x04, 0x70, 0x04, 0x71, 0x04, 0x72, 0x04, 0x73, 0x04, 0x74, 0x04, 0x75, 0x04, 0x78, 0x04, 0x79, 0x04, 0x7a, 0x04, 0x7b, 0x04, 0x7c, 0x04, 0x7d, 0x04, 0x7e, 0x04, 0x7f, 0x04, 0x80, 0x04, 0x81, 0x04, 0x90, 0x04, 0x91, 0x04, 0x92, 0x04, 0x93, 0x04, 0x94, 0x04, 0x95, 0x04, 0x96, 0x04, 0x97, 0x04, 0x98, 0x04, 0x99, 0x04, 0x9a, 0x04, 0x9b, 0x04, 0x9c, 0x04, 0x9d, 0x04, 0x9e, 0x04, 0x9f, 0x04, 0xa0, 0x04, 0xa1, 0x04, 0xa2, 0x04, 0xa3, 0x04, 0xa4, 0x04, 0xa5, 0x04, 0xa6, 0x04, 0xa7, 0x04, 0xa8, 0x04, 0xa9, 0x04, 0xaa, 0x04, 0xab, 0x04, 0xac, 0x04, 0xad, 0x04, 0xae, 0x04, 0xaf, 0x04, 0xb0, 0x04, 0xb1, 0x04, 0xb2, 0x04, 0xb3, 0x04, 0xb4, 0x04, 0xb5, 0x04, 0xb6, 0x04, 0xb7, 0x04, 0xb8, 0x04, 0xb9, 0x04, 0xba, 0x04, 0xbb, 0x04, 0xbc, 0x04, 0xbd, 0x04, 0xbe, 0x04, 0xbf, 0x04, 0xc3, 0x04, 0xc4, 0x04, 0xc7, 0x04, 0xc8, 0x04, 0xcb, 0x04, 0xcc, 0x05, 0x31, 0x05, 0x61, 0x05, 0x32, 0x05, 0x62, 0x05, 0x33, 0x05, 0x63, 0x05, 0x34, 0x05, 0x64, 0x05, 0x35, 0x05, 0x65, 0x05, 0x36, 0x05, 0x66, 0x05, 0x37, 0x05, 0x67, 0x05, 0x38, 0x05, 0x68, 0x05, 0x39, 0x05, 0x69, 0x05, 0x3a, 0x05, 0x6a, 0x05, 0x3b, 0x05, 0x6b, 0x05, 0x3c, 0x05, 0x6c, 0x05, 0x3d, 0x05, 0x6d, 0x05, 0x3e, 0x05, 0x6e, 0x05, 0x3f, 0x05, 0x6f, 0x05, 0x40, 0x05, 0x70, 0x05, 0x41, 0x05, 0x71, 0x05, 0x42, 0x05, 0x72, 0x05, 0x43, 0x05, 0x73, 0x05, 0x44, 0x05, 0x74, 0x05, 0x45, 0x05, 0x75, 0x05, 0x46, 0x05, 0x76, 0x05, 0x47, 0x05, 0x77, 0x05, 0x48, 0x05, 0x78, 0x05, 0x49, 0x05, 0x79, 0x05, 0x4a, 0x05, 0x7a, 0x05, 0x4b, 0x05, 0x7b, 0x05, 0x4c, 0x05, 0x7c, 0x05, 0x4d, 0x05, 0x7d, 0x05, 0x4e, 0x05, 0x7e, 0x05, 0x4f, 0x05, 0x7f, 0x05, 0x50, 0x05, 0x80, 0x05, 0x51, 0x05, 0x81, 0x05, 0x52, 0x05, 0x82, 0x05, 0x53, 0x05, 0x83, 0x05, 0x54, 0x05, 0x84, 0x05, 0x55, 0x05, 0x85, 0x05, 0x56, 0x05, 0x86, 0x10, 0xa0, 0x10, 0xd0, 0x10, 0xa1, 0x10, 0xd1, 0x10, 0xa2, 0x10, 0xd2, 0x10, 0xa3, 0x10, 0xd3, 0x10, 0xa4, 0x10, 0xd4, 0x10, 0xa5, 0x10, 0xd5, 0x10, 0xa6, 0x10, 0xd6, 0x10, 0xa7, 0x10, 0xd7, 0x10, 0xa8, 0x10, 0xd8, 0x10, 0xa9, 0x10, 0xd9, 0x10, 0xaa, 0x10, 0xda, 0x10, 0xab, 0x10, 0xdb, 0x10, 0xac, 0x10, 0xdc, 0x10, 0xad, 0x10, 0xdd, 0x10, 0xae, 0x10, 0xde, 0x10, 0xaf, 0x10, 0xdf, 0x10, 0xb0, 0x10, 0xe0, 0x10, 0xb1, 0x10, 0xe1, 0x10, 0xb2, 0x10, 0xe2, 0x10, 0xb3, 0x10, 0xe3, 0x10, 0xb4, 0x10, 0xe4, 0x10, 0xb5, 0x10, 0xe5, 0x10, 0xb6, 0x10, 0xe6, 0x10, 0xb7, 0x10, 0xe7, 0x10, 0xb8, 0x10, 0xe8, 0x10, 0xb9, 0x10, 0xe9, 0x10, 0xba, 0x10, 0xea, 0x10, 0xbb, 0x10, 0xeb, 0x10, 0xbc, 0x10, 0xec, 0x10, 0xbd, 0x10, 0xed, 0x10, 0xbe, 0x10, 0xee, 0x10, 0xbf, 0x10, 0xef, 0x10, 0xc0, 0x10, 0xf0, 0x10, 0xc1, 0x10, 0xf1, 0x10, 0xc2, 0x10, 0xf2, 0x10, 0xc3, 0x10, 0xf3, 0x10, 0xc4, 0x10, 0xf4, 0x10, 0xc5, 0x10, 0xf5, 0x20, 0x0c, 0x00, 0x00, 0x20, 0x0d, 0x00, 0x00, 0x20, 0x0e, 0x00, 0x00, 0x20, 0x0f, 0x00, 0x00, 0x20, 0x2a, 0x00, 0x00, 0x20, 0x2b, 0x00, 0x00, 0x20, 0x2c, 0x00, 0x00, 0x20, 0x2d, 0x00, 0x00, 0x20, 0x2e, 0x00, 0x00, 0x20, 0x6a, 0x00, 0x00, 0x20, 0x6b, 0x00, 0x00, 0x20, 0x6c, 0x00, 0x00, 0x20, 0x6d, 0x00, 0x00, 0x20, 0x6e, 0x00, 0x00, 0x20, 0x6f, 0x00, 0x00, 0x21, 0x60, 0x21, 0x70, 0x21, 0x61, 0x21, 0x71, 0x21, 0x62, 0x21, 0x72, 0x21, 0x63, 0x21, 0x73, 0x21, 0x64, 0x21, 0x74, 0x21, 0x65, 0x21, 0x75, 0x21, 0x66, 0x21, 0x76, 0x21, 0x67, 0x21, 0x77, 0x21, 0x68, 0x21, 0x78, 0x21, 0x69, 0x21, 0x79, 0x21, 0x6a, 0x21, 0x7a, 0x21, 0x6b, 0x21, 0x7b, 0x21, 0x6c, 0x21, 0x7c, 0x21, 0x6d, 0x21, 0x7d, 0x21, 0x6e, 0x21, 0x7e, 0x21, 0x6f, 0x21, 0x7f, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x21, 0xff, 0x41, 0xff, 0x22, 0xff, 0x42, 0xff, 0x23, 0xff, 0x43, 0xff, 0x24, 0xff, 0x44, 0xff, 0x25, 0xff, 0x45, 0xff, 0x26, 0xff, 0x46, 0xff, 0x27, 0xff, 0x47, 0xff, 0x28, 0xff, 0x48, 0xff, 0x29, 0xff, 0x49, 0xff, 0x2a, 0xff, 0x4a, 0xff, 0x2b, 0xff, 0x4b, 0xff, 0x2c, 0xff, 0x4c, 0xff, 0x2d, 0xff, 0x4d, 0xff, 0x2e, 0xff, 0x4e, 0xff, 0x2f, 0xff, 0x4f, 0xff, 0x30, 0xff, 0x50, 0xff, 0x31, 0xff, 0x51, 0xff, 0x32, 0xff, 0x52, 0xff, 0x33, 0xff, 0x53, 0xff, 0x34, 0xff, 0x54, 0xff, 0x35, 0xff, 0x55, 0xff, 0x36, 0xff, 0x56, 0xff, 0x37, 0xff, 0x57, 0xff, 0x38, 0xff, 0x58, 0xff, 0x39, 0xff, 0x59, 0xff, 0x3a, 0xff, 0x5a, 0x00 }; static int utf16be_transl_count = 329; /* These are the start indice in utf16be_transl[] for the page numbers 0 to 9 as classified by function what_page(). As soon as the first byte of the input pair in utf16be_transl[] changes, the search can be ended and output is equal to input. If page -1 is returned by what_page(), then input is equal to output. */ static int utf16be_transl_starts[] = { 0, 31, 81, 112, 195, 233, 271, 286, 302, 303 }; static int what_page(uint16_t x) { switch(((uint8_t *) &x)[0]) { case 0: return 0; case 1: return 1; case 3: return 2; case 4: return 3; case 5: return 4; case 16: return 5; case 32: return 6; case 33: return 7; case 254: return 8; case 255: return 9; default: return -1; /* no mapping */ } } /* Accelerator for the ASCII subset which is expected to be the most frequently used one. */ static uint16_t cmp_name_page0(uint16_t x) { uint8_t *low; low = ((uint8_t *) &x) + 1; if (x == 0) return 0xffff; if (*low <= 0x40) ; else if (*low <= 0x5a) *low = *low + 0x20; else if (*low < 0xc6) ; else if (*low == 0xc6) *low = 0xe6; else if (*low == 0xd0) *low = 0xf0; else if (*low == 0xd8) *low = 0xf8; else if (*low == 0xde) *low = 0xfe; return x; } /* Converts a character into the representative of its HFS+ equivalence class. @param x The UTF-16BE character to be converted. @return 0 = ignore character with comparisons else the case-insensitive character. */ uint16_t iso_hfsplus_cichar(uint16_t x) { int page, i; uint16_t ret; uint8_t low, high; high = ((uint8_t *) &x)[0]; low = ((uint8_t *) &x)[1]; page = what_page(x); if (page < 0) return x; /* No translation needed */ if (page == 0) return cmp_name_page0(x); /* Accelerator for ASCII subset */ for (i = utf16be_transl_starts[page] * 4; i < utf16be_transl_count * 4; i += 4) { if (utf16be_transl[i] != high) break; if (utf16be_transl[i + 1] == low) { ((uint8_t *) &ret)[0] = utf16be_transl[i + 2]; ((uint8_t *) &ret)[1] = utf16be_transl[i + 3]; return ret; } } return x; } /* * * Based on Unicode 3.2.0. * See http://www.unicode.org/copyright.html * Quote from there: * "Copyright (c) 1991-2012 Unicode, Inc. All rights reserved. * [...] * Permission is hereby granted, free of charge, to any person obtaining a * copy of the Unicode data files and any associated documentation * (the "Data Files") or Unicode software and any associated documentation * (the "Software") to deal in the Data Files or Software without restriction, * including without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Data Files or Software, and to permit * persons to whom the Data Files or Software are furnished to do so, provided * that (a) the above copyright notice(s) and this permission notice appear * with all copies of the Data Files or Software, (b) both the above copyright * notice(s) and this permission notice appear in associated documentation, * and (c) there is clear notice in each modified Data File or in the Software * as well as in the documentation associated with the Data File(s) or * Software that the data or software has been modified." * * * For this particular implementation: * * Copyright (c) 2012 Vladimir Serbinenko * Copyright (c) 2012 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include "hfsplus.h" /* This encodes a matrix of page and character, with 16-bit words as elements. Initially the matrix is filled with zeros. 1: The first element is the page number. If it is equal or lower than the previous one, then the matrix is done. 2: The next element is the character number If it is equal or lower than the previous one, the page is done. Goto 1. 3: The next element is the byte value. Goto 2. */ static uint16_t class_page_data[] = { /* page 03 */ 0x03, 0x00, 0x230, 0x01, 0x230, 0x02, 0x230, 0x03, 0x230, 0x04, 0x230, 0x05, 0x230, 0x06, 0x230, 0x07, 0x230, 0x08, 0x230, 0x09, 0x230, 0x0a, 0x230, 0x0b, 0x230, 0x0c, 0x230, 0x0d, 0x230, 0x0e, 0x230, 0x0f, 0x230, 0x10, 0x230, 0x11, 0x230, 0x12, 0x230, 0x13, 0x230, 0x14, 0x230, 0x15, 0x232, 0x16, 0x220, 0x17, 0x220, 0x18, 0x220, 0x19, 0x220, 0x1a, 0x232, 0x1b, 0x216, 0x1c, 0x220, 0x1d, 0x220, 0x1e, 0x220, 0x1f, 0x220, 0x20, 0x220, 0x21, 0x202, 0x22, 0x202, 0x23, 0x220, 0x24, 0x220, 0x25, 0x220, 0x26, 0x220, 0x27, 0x202, 0x28, 0x202, 0x29, 0x220, 0x2a, 0x220, 0x2b, 0x220, 0x2c, 0x220, 0x2d, 0x220, 0x2e, 0x220, 0x2f, 0x220, 0x30, 0x220, 0x31, 0x220, 0x32, 0x220, 0x33, 0x220, 0x34, 0x1, 0x35, 0x1, 0x36, 0x1, 0x37, 0x1, 0x38, 0x1, 0x39, 0x220, 0x3a, 0x220, 0x3b, 0x220, 0x3c, 0x220, 0x3d, 0x230, 0x3e, 0x230, 0x3f, 0x230, 0x40, 0x230, 0x41, 0x230, 0x42, 0x230, 0x43, 0x230, 0x44, 0x230, 0x45, 0x240, 0x46, 0x230, 0x47, 0x220, 0x48, 0x220, 0x49, 0x220, 0x4a, 0x230, 0x4b, 0x230, 0x4c, 0x230, 0x4d, 0x220, 0x4e, 0x220, 0x60, 0x234, 0x61, 0x234, 0x62, 0x233, 0x63, 0x230, 0x64, 0x230, 0x65, 0x230, 0x66, 0x230, 0x67, 0x230, 0x68, 0x230, 0x69, 0x230, 0x6a, 0x230, 0x6b, 0x230, 0x6c, 0x230, 0x6d, 0x230, 0x6e, 0x230, 0x6f, 0x230, 0x00, /* page04 */ 0x04, 0x83, 0x230, 0x84, 0x230, 0x85, 0x230, 0x86, 0x230, 0x00, /* page05 */ 0x05, 0x91, 0x220, 0x92, 0x230, 0x93, 0x230, 0x94, 0x230, 0x95, 0x230, 0x96, 0x220, 0x97, 0x230, 0x98, 0x230, 0x99, 0x230, 0x9a, 0x222, 0x9b, 0x220, 0x9c, 0x230, 0x9d, 0x230, 0x9e, 0x230, 0x9f, 0x230, 0xa0, 0x230, 0xa1, 0x230, 0xa3, 0x220, 0xa4, 0x220, 0xa5, 0x220, 0xa6, 0x220, 0xa7, 0x220, 0xa8, 0x230, 0xa9, 0x230, 0xaa, 0x220, 0xab, 0x230, 0xac, 0x230, 0xad, 0x222, 0xae, 0x228, 0xaf, 0x230, 0xb0, 0x10, 0xb1, 0x11, 0xb2, 0x12, 0xb3, 0x13, 0xb4, 0x14, 0xb5, 0x15, 0xb6, 0x16, 0xb7, 0x17, 0xb8, 0x18, 0xb9, 0x19, 0xbb, 0x20, 0xbc, 0x21, 0xbd, 0x22, 0xbf, 0x23, 0xc1, 0x24, 0xc2, 0x25, 0xc4, 0x230, 0x00, /* page06 */ 0x06, 0x4b, 0x27, 0x4c, 0x28, 0x4d, 0x29, 0x4e, 0x30, 0x4f, 0x31, 0x50, 0x32, 0x51, 0x33, 0x52, 0x34, 0x53, 0x230, 0x54, 0x230, 0x55, 0x220, 0x70, 0x35, 0xd6, 0x230, 0xd7, 0x230, 0xd8, 0x230, 0xd9, 0x230, 0xda, 0x230, 0xdb, 0x230, 0xdc, 0x230, 0xdf, 0x230, 0xe0, 0x230, 0xe1, 0x230, 0xe2, 0x230, 0xe3, 0x220, 0xe4, 0x230, 0xe7, 0x230, 0xe8, 0x230, 0xea, 0x220, 0xeb, 0x230, 0xec, 0x230, 0xed, 0x220, 0x00, /* page07 */ 0x07, 0x11, 0x36, 0x30, 0x230, 0x31, 0x220, 0x32, 0x230, 0x33, 0x230, 0x34, 0x220, 0x35, 0x230, 0x36, 0x230, 0x37, 0x220, 0x38, 0x220, 0x39, 0x220, 0x3a, 0x230, 0x3b, 0x220, 0x3c, 0x220, 0x3d, 0x230, 0x3e, 0x220, 0x3f, 0x230, 0x40, 0x230, 0x41, 0x230, 0x42, 0x220, 0x43, 0x230, 0x44, 0x220, 0x45, 0x230, 0x46, 0x220, 0x47, 0x230, 0x48, 0x220, 0x49, 0x230, 0x4a, 0x230, 0x00, /* page09 */ 0x09, 0x3c, 0x7, 0x4d, 0x9, 0x51, 0x230, 0x52, 0x220, 0x53, 0x230, 0x54, 0x230, 0xbc, 0x7, 0xcd, 0x9, 0x00, /* page0a */ 0x0a, 0x3c, 0x7, 0x4d, 0x9, 0xbc, 0x7, 0xcd, 0x9, 0x00, /* page0b */ 0x0b, 0x3c, 0x7, 0x4d, 0x9, 0xcd, 0x9, 0x00, /* page0c */ 0x0c, 0x4d, 0x9, 0x55, 0x84, 0x56, 0x91, 0xcd, 0x9, 0x00, /* page0d */ 0x0d, 0x4d, 0x9, 0xca, 0x9, 0x00, /* page0e */ 0x0e, 0x38, 0x103, 0x39, 0x103, 0x3a, 0x9, 0x48, 0x107, 0x49, 0x107, 0x4a, 0x107, 0x4b, 0x107, 0xb8, 0x118, 0xb9, 0x118, 0xc8, 0x122, 0xc9, 0x122, 0xca, 0x122, 0xcb, 0x122, 0x00, /* page0f */ 0x0f, 0x18, 0x220, 0x19, 0x220, 0x35, 0x220, 0x37, 0x220, 0x39, 0x216, 0x71, 0x129, 0x72, 0x130, 0x74, 0x132, 0x7a, 0x130, 0x7b, 0x130, 0x7c, 0x130, 0x7d, 0x130, 0x80, 0x130, 0x82, 0x230, 0x83, 0x230, 0x84, 0x9, 0x86, 0x230, 0x87, 0x230, 0xc6, 0x220, 0x00, /* page10 */ 0x10, 0x37, 0x7, 0x39, 0x9, 0x00, /* page17 */ 0x17, 0x14, 0x9, 0x34, 0x9, 0xd2, 0x9, 0x00, /* page18 */ 0x18, 0xa9, 0x228, 0x00, /* page20 */ 0x20, 0xd0, 0x230, 0xd1, 0x230, 0xd2, 0x1, 0xd3, 0x1, 0xd4, 0x230, 0xd5, 0x230, 0xd6, 0x230, 0xd7, 0x230, 0xd8, 0x1, 0xd9, 0x1, 0xda, 0x1, 0xdb, 0x230, 0xdc, 0x230, 0xe1, 0x230, 0xe5, 0x1, 0xe6, 0x1, 0xe7, 0x230, 0xe8, 0x220, 0xe9, 0x230, 0xea, 0x1, 0x00, /* page30 */ 0x30, 0x2a, 0x218, 0x2b, 0x228, 0x2c, 0x232, 0x2d, 0x222, 0x2e, 0x224, 0x2f, 0x224, 0x99, 0x8, 0x9a, 0x8, 0x00, /* pagefb */ 0xfb, 0x1e, 0x26, 0x00, /* pagefe */ 0xfe, 0x20, 0x230, 0x21, 0x230, 0x22, 0x230, 0x23, 0x230, 0x00, /* End of list */ 0x00 }; uint16_t *hfsplus_class_pages[256]; static uint16_t class_pages[19][256]; void make_hfsplus_class_pages() { int page_idx = -1, char_idx, i; uint16_t *rpt, *page_pt; int page_count = 0; memset(class_pages, 0, 19 * 256 * sizeof(uint16_t)); for (i = 0; i < 256; i++) hfsplus_class_pages[i] = NULL; rpt = (uint16_t *) class_page_data; page_pt = (uint16_t *) class_pages; while (1) { if (*rpt <= page_idx) break; page_count++; page_idx = *(rpt++); char_idx = -1; while (1) { if(*rpt <= char_idx) break; char_idx = *(rpt++); page_pt[char_idx] = *(rpt++); } rpt++; hfsplus_class_pages[page_idx] = class_pages[page_count - 1]; page_pt += 256; } } /* * Copyright (c) 2012 Vladimir Serbinenko * Copyright (c) 2012 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include "hfsplus.h" /* Based on tn1150 (HFS+ format specification) */ /* This encodes a matrix of page and character, with a number of HFSPLUS_MAX_DECOMPOSE_LEN + 1 elements of 16 bit each. Initially the matrix is filled with zeros. 1: The first element is the page number. If it is equal or lower than the previous one, then the matrix is done. 2: The next element is the character number If it is equal or lower than the previous one, the page is done. Goto 1. 3: The next elements are matrix elements. If element 0 is encountered, then the character is done. Goto 2. */ static const uint16_t decompose_page_data[] = { /* page00 */ 0x00, 0xc0, 0x0041, 0x0300, 0, 0xc1, 0x0041, 0x0301, 0, 0xc2, 0x0041, 0x0302, 0, 0xc3, 0x0041, 0x0303, 0, 0xc4, 0x0041, 0x0308, 0, 0xc5, 0x0041, 0x030A, 0, 0xc7, 0x0043, 0x0327, 0, 0xc8, 0x0045, 0x0300, 0, 0xc9, 0x0045, 0x0301, 0, 0xca, 0x0045, 0x0302, 0, 0xcb, 0x0045, 0x0308, 0, 0xcc, 0x0049, 0x0300, 0, 0xcd, 0x0049, 0x0301, 0, 0xce, 0x0049, 0x0302, 0, 0xcf, 0x0049, 0x0308, 0, 0xd1, 0x004E, 0x0303, 0, 0xd2, 0x004F, 0x0300, 0, 0xd3, 0x004F, 0x0301, 0, 0xd4, 0x004F, 0x0302, 0, 0xd5, 0x004F, 0x0303, 0, 0xd6, 0x004F, 0x0308, 0, 0xd9, 0x0055, 0x0300, 0, 0xda, 0x0055, 0x0301, 0, 0xdb, 0x0055, 0x0302, 0, 0xdc, 0x0055, 0x0308, 0, 0xdd, 0x0059, 0x0301, 0, 0xe0, 0x0061, 0x0300, 0, 0xe1, 0x0061, 0x0301, 0, 0xe2, 0x0061, 0x0302, 0, 0xe3, 0x0061, 0x0303, 0, 0xe4, 0x0061, 0x0308, 0, 0xe5, 0x0061, 0x030A, 0, 0xe7, 0x0063, 0x0327, 0, 0xe8, 0x0065, 0x0300, 0, 0xe9, 0x0065, 0x0301, 0, 0xea, 0x0065, 0x0302, 0, 0xeb, 0x0065, 0x0308, 0, 0xec, 0x0069, 0x0300, 0, 0xed, 0x0069, 0x0301, 0, 0xee, 0x0069, 0x0302, 0, 0xef, 0x0069, 0x0308, 0, 0xf1, 0x006E, 0x0303, 0, 0xf2, 0x006F, 0x0300, 0, 0xf3, 0x006F, 0x0301, 0, 0xf4, 0x006F, 0x0302, 0, 0xf5, 0x006F, 0x0303, 0, 0xf6, 0x006F, 0x0308, 0, 0xf9, 0x0075, 0x0300, 0, 0xfa, 0x0075, 0x0301, 0, 0xfb, 0x0075, 0x0302, 0, 0xfc, 0x0075, 0x0308, 0, 0xfd, 0x0079, 0x0301, 0, 0xff, 0x0079, 0x0308, 0, 0x00, /* page01 */ 0x01, 0x00, 0x0041, 0x0304, 0, 0x01, 0x0061, 0x0304, 0, 0x02, 0x0041, 0x0306, 0, 0x03, 0x0061, 0x0306, 0, 0x04, 0x0041, 0x0328, 0, 0x05, 0x0061, 0x0328, 0, 0x06, 0x0043, 0x0301, 0, 0x07, 0x0063, 0x0301, 0, 0x08, 0x0043, 0x0302, 0, 0x09, 0x0063, 0x0302, 0, 0x0a, 0x0043, 0x0307, 0, 0x0b, 0x0063, 0x0307, 0, 0x0c, 0x0043, 0x030C, 0, 0x0d, 0x0063, 0x030C, 0, 0x0e, 0x0044, 0x030C, 0, 0x0f, 0x0064, 0x030C, 0, 0x12, 0x0045, 0x0304, 0, 0x13, 0x0065, 0x0304, 0, 0x14, 0x0045, 0x0306, 0, 0x15, 0x0065, 0x0306, 0, 0x16, 0x0045, 0x0307, 0, 0x17, 0x0065, 0x0307, 0, 0x18, 0x0045, 0x0328, 0, 0x19, 0x0065, 0x0328, 0, 0x1a, 0x0045, 0x030C, 0, 0x1b, 0x0065, 0x030C, 0, 0x1c, 0x0047, 0x0302, 0, 0x1d, 0x0067, 0x0302, 0, 0x1e, 0x0047, 0x0306, 0, 0x1f, 0x0067, 0x0306, 0, 0x20, 0x0047, 0x0307, 0, 0x21, 0x0067, 0x0307, 0, 0x22, 0x0047, 0x0327, 0, 0x23, 0x0067, 0x0327, 0, 0x24, 0x0048, 0x0302, 0, 0x25, 0x0068, 0x0302, 0, 0x28, 0x0049, 0x0303, 0, 0x29, 0x0069, 0x0303, 0, 0x2a, 0x0049, 0x0304, 0, 0x2b, 0x0069, 0x0304, 0, 0x2c, 0x0049, 0x0306, 0, 0x2d, 0x0069, 0x0306, 0, 0x2e, 0x0049, 0x0328, 0, 0x2f, 0x0069, 0x0328, 0, 0x30, 0x0049, 0x0307, 0, 0x34, 0x004A, 0x0302, 0, 0x35, 0x006A, 0x0302, 0, 0x36, 0x004B, 0x0327, 0, 0x37, 0x006B, 0x0327, 0, 0x39, 0x004C, 0x0301, 0, 0x3a, 0x006C, 0x0301, 0, 0x3b, 0x004C, 0x0327, 0, 0x3c, 0x006C, 0x0327, 0, 0x3d, 0x004C, 0x030C, 0, 0x3e, 0x006C, 0x030C, 0, 0x43, 0x004E, 0x0301, 0, 0x44, 0x006E, 0x0301, 0, 0x45, 0x004E, 0x0327, 0, 0x46, 0x006E, 0x0327, 0, 0x47, 0x004E, 0x030C, 0, 0x48, 0x006E, 0x030C, 0, 0x4c, 0x004F, 0x0304, 0, 0x4d, 0x006F, 0x0304, 0, 0x4e, 0x004F, 0x0306, 0, 0x4f, 0x006F, 0x0306, 0, 0x50, 0x004F, 0x030B, 0, 0x51, 0x006F, 0x030B, 0, 0x54, 0x0052, 0x0301, 0, 0x55, 0x0072, 0x0301, 0, 0x56, 0x0052, 0x0327, 0, 0x57, 0x0072, 0x0327, 0, 0x58, 0x0052, 0x030C, 0, 0x59, 0x0072, 0x030C, 0, 0x5a, 0x0053, 0x0301, 0, 0x5b, 0x0073, 0x0301, 0, 0x5c, 0x0053, 0x0302, 0, 0x5d, 0x0073, 0x0302, 0, 0x5e, 0x0053, 0x0327, 0, 0x5f, 0x0073, 0x0327, 0, 0x60, 0x0053, 0x030C, 0, 0x61, 0x0073, 0x030C, 0, 0x62, 0x0054, 0x0327, 0, 0x63, 0x0074, 0x0327, 0, 0x64, 0x0054, 0x030C, 0, 0x65, 0x0074, 0x030C, 0, 0x68, 0x0055, 0x0303, 0, 0x69, 0x0075, 0x0303, 0, 0x6a, 0x0055, 0x0304, 0, 0x6b, 0x0075, 0x0304, 0, 0x6c, 0x0055, 0x0306, 0, 0x6d, 0x0075, 0x0306, 0, 0x6e, 0x0055, 0x030A, 0, 0x6f, 0x0075, 0x030A, 0, 0x70, 0x0055, 0x030B, 0, 0x71, 0x0075, 0x030B, 0, 0x72, 0x0055, 0x0328, 0, 0x73, 0x0075, 0x0328, 0, 0x74, 0x0057, 0x0302, 0, 0x75, 0x0077, 0x0302, 0, 0x76, 0x0059, 0x0302, 0, 0x77, 0x0079, 0x0302, 0, 0x78, 0x0059, 0x0308, 0, 0x79, 0x005A, 0x0301, 0, 0x7a, 0x007A, 0x0301, 0, 0x7b, 0x005A, 0x0307, 0, 0x7c, 0x007A, 0x0307, 0, 0x7d, 0x005A, 0x030C, 0, 0x7e, 0x007A, 0x030C, 0, 0xa0, 0x004F, 0x031B, 0, 0xa1, 0x006F, 0x031B, 0, 0xaf, 0x0055, 0x031B, 0, 0xb0, 0x0075, 0x031B, 0, 0xcd, 0x0041, 0x030C, 0, 0xce, 0x0061, 0x030C, 0, 0xcf, 0x0049, 0x030C, 0, 0xd0, 0x0069, 0x030C, 0, 0xd1, 0x004F, 0x030C, 0, 0xd2, 0x006F, 0x030C, 0, 0xd3, 0x0055, 0x030C, 0, 0xd4, 0x0075, 0x030C, 0, 0xd5, 0x0055, 0x0308, 0x0304, 0, 0xd6, 0x0075, 0x0308, 0x0304, 0, 0xd7, 0x0055, 0x0308, 0x0301, 0, 0xd8, 0x0075, 0x0308, 0x0301, 0, 0xd9, 0x0055, 0x0308, 0x030C, 0, 0xda, 0x0075, 0x0308, 0x030C, 0, 0xdb, 0x0055, 0x0308, 0x0300, 0, 0xdc, 0x0075, 0x0308, 0x0300, 0, 0xde, 0x0041, 0x0308, 0x0304, 0, 0xdf, 0x0061, 0x0308, 0x0304, 0, 0xe0, 0x0041, 0x0307, 0x0304, 0, 0xe1, 0x0061, 0x0307, 0x0304, 0, 0xe2, 0x00C6, 0x0304, 0, 0xe3, 0x00E6, 0x0304, 0, 0xe6, 0x0047, 0x030C, 0, 0xe7, 0x0067, 0x030C, 0, 0xe8, 0x004B, 0x030C, 0, 0xe9, 0x006B, 0x030C, 0, 0xea, 0x004F, 0x0328, 0, 0xeb, 0x006F, 0x0328, 0, 0xec, 0x004F, 0x0328, 0x0304, 0, 0xed, 0x006F, 0x0328, 0x0304, 0, 0xee, 0x01B7, 0x030C, 0, 0xef, 0x0292, 0x030C, 0, 0xf0, 0x006A, 0x030C, 0, 0xf4, 0x0047, 0x0301, 0, 0xf5, 0x0067, 0x0301, 0, 0xfa, 0x0041, 0x030A, 0x0301, 0, 0xfb, 0x0061, 0x030A, 0x0301, 0, 0xfc, 0x00C6, 0x0301, 0, 0xfd, 0x00E6, 0x0301, 0, 0xfe, 0x00D8, 0x0301, 0, 0xff, 0x00F8, 0x0301, 0, 0x00, /* page02 */ 0x02, 0x00, 0x0041, 0x030F, 0, 0x01, 0x0061, 0x030F, 0, 0x02, 0x0041, 0x0311, 0, 0x03, 0x0061, 0x0311, 0, 0x04, 0x0045, 0x030F, 0, 0x05, 0x0065, 0x030F, 0, 0x06, 0x0045, 0x0311, 0, 0x07, 0x0065, 0x0311, 0, 0x08, 0x0049, 0x030F, 0, 0x09, 0x0069, 0x030F, 0, 0x0a, 0x0049, 0x0311, 0, 0x0b, 0x0069, 0x0311, 0, 0x0c, 0x004F, 0x030F, 0, 0x0d, 0x006F, 0x030F, 0, 0x0e, 0x004F, 0x0311, 0, 0x0f, 0x006F, 0x0311, 0, 0x10, 0x0052, 0x030F, 0, 0x11, 0x0072, 0x030F, 0, 0x12, 0x0052, 0x0311, 0, 0x13, 0x0072, 0x0311, 0, 0x14, 0x0055, 0x030F, 0, 0x15, 0x0075, 0x030F, 0, 0x16, 0x0055, 0x0311, 0, 0x17, 0x0075, 0x0311, 0, 0x00, /* page03 */ 0x03, 0x10, 0x0306, 0x0307, 0, 0x40, 0x0300, 0, 0x41, 0x0301, 0, 0x43, 0x0313, 0, 0x44, 0x0308, 0x030D, 0, 0x74, 0x02B9, 0, 0x7e, 0x003B, 0, 0x85, 0x00A8, 0x030D, 0, 0x86, 0x0391, 0x030D, 0, 0x87, 0x00B7, 0, 0x88, 0x0395, 0x030D, 0, 0x89, 0x0397, 0x030D, 0, 0x8a, 0x0399, 0x030D, 0, 0x8c, 0x039F, 0x030D, 0, 0x8e, 0x03A5, 0x030D, 0, 0x8f, 0x03A9, 0x030D, 0, 0x90, 0x03B9, 0x0308, 0x030D, 0, 0xaa, 0x0399, 0x0308, 0, 0xab, 0x03A5, 0x0308, 0, 0xac, 0x03B1, 0x030D, 0, 0xad, 0x03B5, 0x030D, 0, 0xae, 0x03B7, 0x030D, 0, 0xaf, 0x03B9, 0x030D, 0, 0xb0, 0x03C5, 0x0308, 0x030D, 0, 0xca, 0x03B9, 0x0308, 0, 0xcb, 0x03C5, 0x0308, 0, 0xcc, 0x03BF, 0x030D, 0, 0xcd, 0x03C5, 0x030D, 0, 0xce, 0x03C9, 0x030D, 0, 0xd3, 0x03D2, 0x030D, 0, 0xd4, 0x03D2, 0x0308, 0, 0x00, /* page04 */ 0x04, 0x01, 0x0415, 0x0308, 0, 0x03, 0x0413, 0x0301, 0, 0x07, 0x0406, 0x0308, 0, 0x0c, 0x041A, 0x0301, 0, 0x0e, 0x0423, 0x0306, 0, 0x19, 0x0418, 0x0306, 0, 0x39, 0x0438, 0x0306, 0, 0x51, 0x0435, 0x0308, 0, 0x53, 0x0433, 0x0301, 0, 0x57, 0x0456, 0x0308, 0, 0x5c, 0x043A, 0x0301, 0, 0x5e, 0x0443, 0x0306, 0, 0x76, 0x0474, 0x030F, 0, 0x77, 0x0475, 0x030F, 0, 0xc1, 0x0416, 0x0306, 0, 0xc2, 0x0436, 0x0306, 0, 0xd0, 0x0410, 0x0306, 0, 0xd1, 0x0430, 0x0306, 0, 0xd2, 0x0410, 0x0308, 0, 0xd3, 0x0430, 0x0308, 0, 0xd4, 0x00C6, 0, 0xd5, 0x00E6, 0, 0xd6, 0x0415, 0x0306, 0, 0xd7, 0x0435, 0x0306, 0, 0xd8, 0x018F, 0, 0xd9, 0x0259, 0, 0xda, 0x018F, 0x0308, 0, 0xdb, 0x0259, 0x0308, 0, 0xdc, 0x0416, 0x0308, 0, 0xdd, 0x0436, 0x0308, 0, 0xde, 0x0417, 0x0308, 0, 0xdf, 0x0437, 0x0308, 0, 0xe0, 0x01B7, 0, 0xe1, 0x0292, 0, 0xe2, 0x0418, 0x0304, 0, 0xe3, 0x0438, 0x0304, 0, 0xe4, 0x0418, 0x0308, 0, 0xe5, 0x0438, 0x0308, 0, 0xe6, 0x041E, 0x0308, 0, 0xe7, 0x043E, 0x0308, 0, 0xe8, 0x019F, 0, 0xe9, 0x0275, 0, 0xea, 0x019F, 0x0308, 0, 0xeb, 0x0275, 0x0308, 0, 0xee, 0x0423, 0x0304, 0, 0xef, 0x0443, 0x0304, 0, 0xf0, 0x0423, 0x0308, 0, 0xf1, 0x0443, 0x0308, 0, 0xf2, 0x0423, 0x030B, 0, 0xf3, 0x0443, 0x030B, 0, 0xf4, 0x0427, 0x0308, 0, 0xf5, 0x0447, 0x0308, 0, 0xf8, 0x042B, 0x0308, 0, 0xf9, 0x044B, 0x0308, 0, 0x00, /* page09 */ 0x09, 0x29, 0x0928, 0x093C, 0, 0x31, 0x0930, 0x093C, 0, 0x34, 0x0933, 0x093C, 0, 0x58, 0x0915, 0x093C, 0, 0x59, 0x0916, 0x093C, 0, 0x5a, 0x0917, 0x093C, 0, 0x5b, 0x091C, 0x093C, 0, 0x5c, 0x0921, 0x093C, 0, 0x5d, 0x0922, 0x093C, 0, 0x5e, 0x092B, 0x093C, 0, 0x5f, 0x092F, 0x093C, 0, 0xb0, 0x09AC, 0x09BC, 0, 0xcb, 0x09C7, 0x09BE, 0, 0xcc, 0x09C7, 0x09D7, 0, 0xdc, 0x09A1, 0x09BC, 0, 0xdd, 0x09A2, 0x09BC, 0, 0xdf, 0x09AF, 0x09BC, 0, 0x00, /* page0a */ 0x0a, 0x59, 0x0A16, 0x0A3C, 0, 0x5a, 0x0A17, 0x0A3C, 0, 0x5b, 0x0A1C, 0x0A3C, 0, 0x5c, 0x0A21, 0x0A3C, 0, 0x5e, 0x0A2B, 0x0A3C, 0, 0x00, /* page0b */ 0x0b, 0x48, 0x0B47, 0x0B56, 0, 0x4b, 0x0B47, 0x0B3E, 0, 0x4c, 0x0B47, 0x0B57, 0, 0x5c, 0x0B21, 0x0B3C, 0, 0x5d, 0x0B22, 0x0B3C, 0, 0x5f, 0x0B2F, 0x0B3C, 0, 0x94, 0x0B92, 0x0BD7, 0, 0xca, 0x0BC6, 0x0BBE, 0, 0xcb, 0x0BC7, 0x0BBE, 0, 0xcc, 0x0BC6, 0x0BD7, 0, 0x00, /* page0c */ 0x0c, 0x48, 0x0C46, 0x0C56, 0, 0xc0, 0x0CBF, 0x0CD5, 0, 0xc7, 0x0CC6, 0x0CD5, 0, 0xc8, 0x0CC6, 0x0CD6, 0, 0xca, 0x0CC6, 0x0CC2, 0, 0xcb, 0x0CC6, 0x0CC2, 0x0CD5, 0, 0x00, /* page0d */ 0x0d, 0x4a, 0x0D46, 0x0D3E, 0, 0x4b, 0x0D47, 0x0D3E, 0, 0x4c, 0x0D46, 0x0D57, 0, 0x00, /* page0e */ 0x0e, 0x33, 0x0E4D, 0x0E32, 0, 0xb3, 0x0ECD, 0x0EB2, 0, 0x00, /* page0f */ 0x0f, 0x43, 0x0F42, 0x0FB7, 0, 0x4d, 0x0F4C, 0x0FB7, 0, 0x52, 0x0F51, 0x0FB7, 0, 0x57, 0x0F56, 0x0FB7, 0, 0x5c, 0x0F5B, 0x0FB7, 0, 0x69, 0x0F40, 0x0FB5, 0, 0x73, 0x0F72, 0x0F71, 0, 0x75, 0x0F74, 0x0F71, 0, 0x76, 0x0FB2, 0x0F80, 0, 0x77, 0x0FB2, 0x0F80, 0x0F71, 0, 0x78, 0x0FB3, 0x0F80, 0, 0x79, 0x0FB3, 0x0F80, 0x0F71, 0, 0x81, 0x0F80, 0x0F71, 0, 0x93, 0x0F92, 0x0FB7, 0, 0x9d, 0x0F9C, 0x0FB7, 0, 0xa2, 0x0FA1, 0x0FB7, 0, 0xa7, 0x0FA6, 0x0FB7, 0, 0xac, 0x0FAB, 0x0FB7, 0, 0xb9, 0x0F90, 0x0FB5, 0, 0x00, /* page1e */ 0x1e, 0x00, 0x0041, 0x0325, 0, 0x01, 0x0061, 0x0325, 0, 0x02, 0x0042, 0x0307, 0, 0x03, 0x0062, 0x0307, 0, 0x04, 0x0042, 0x0323, 0, 0x05, 0x0062, 0x0323, 0, 0x06, 0x0042, 0x0331, 0, 0x07, 0x0062, 0x0331, 0, 0x08, 0x0043, 0x0327, 0x0301, 0, 0x09, 0x0063, 0x0327, 0x0301, 0, 0x0a, 0x0044, 0x0307, 0, 0x0b, 0x0064, 0x0307, 0, 0x0c, 0x0044, 0x0323, 0, 0x0d, 0x0064, 0x0323, 0, 0x0e, 0x0044, 0x0331, 0, 0x0f, 0x0064, 0x0331, 0, 0x10, 0x0044, 0x0327, 0, 0x11, 0x0064, 0x0327, 0, 0x12, 0x0044, 0x032D, 0, 0x13, 0x0064, 0x032D, 0, 0x14, 0x0045, 0x0304, 0x0300, 0, 0x15, 0x0065, 0x0304, 0x0300, 0, 0x16, 0x0045, 0x0304, 0x0301, 0, 0x17, 0x0065, 0x0304, 0x0301, 0, 0x18, 0x0045, 0x032D, 0, 0x19, 0x0065, 0x032D, 0, 0x1a, 0x0045, 0x0330, 0, 0x1b, 0x0065, 0x0330, 0, 0x1c, 0x0045, 0x0327, 0x0306, 0, 0x1d, 0x0065, 0x0327, 0x0306, 0, 0x1e, 0x0046, 0x0307, 0, 0x1f, 0x0066, 0x0307, 0, 0x20, 0x0047, 0x0304, 0, 0x21, 0x0067, 0x0304, 0, 0x22, 0x0048, 0x0307, 0, 0x23, 0x0068, 0x0307, 0, 0x24, 0x0048, 0x0323, 0, 0x25, 0x0068, 0x0323, 0, 0x26, 0x0048, 0x0308, 0, 0x27, 0x0068, 0x0308, 0, 0x28, 0x0048, 0x0327, 0, 0x29, 0x0068, 0x0327, 0, 0x2a, 0x0048, 0x032E, 0, 0x2b, 0x0068, 0x032E, 0, 0x2c, 0x0049, 0x0330, 0, 0x2d, 0x0069, 0x0330, 0, 0x2e, 0x0049, 0x0308, 0x0301, 0, 0x2f, 0x0069, 0x0308, 0x0301, 0, 0x30, 0x004B, 0x0301, 0, 0x31, 0x006B, 0x0301, 0, 0x32, 0x004B, 0x0323, 0, 0x33, 0x006B, 0x0323, 0, 0x34, 0x004B, 0x0331, 0, 0x35, 0x006B, 0x0331, 0, 0x36, 0x004C, 0x0323, 0, 0x37, 0x006C, 0x0323, 0, 0x38, 0x004C, 0x0323, 0x0304, 0, 0x39, 0x006C, 0x0323, 0x0304, 0, 0x3a, 0x004C, 0x0331, 0, 0x3b, 0x006C, 0x0331, 0, 0x3c, 0x004C, 0x032D, 0, 0x3d, 0x006C, 0x032D, 0, 0x3e, 0x004D, 0x0301, 0, 0x3f, 0x006D, 0x0301, 0, 0x40, 0x004D, 0x0307, 0, 0x41, 0x006D, 0x0307, 0, 0x42, 0x004D, 0x0323, 0, 0x43, 0x006D, 0x0323, 0, 0x44, 0x004E, 0x0307, 0, 0x45, 0x006E, 0x0307, 0, 0x46, 0x004E, 0x0323, 0, 0x47, 0x006E, 0x0323, 0, 0x48, 0x004E, 0x0331, 0, 0x49, 0x006E, 0x0331, 0, 0x4a, 0x004E, 0x032D, 0, 0x4b, 0x006E, 0x032D, 0, 0x4c, 0x004F, 0x0303, 0x0301, 0, 0x4d, 0x006F, 0x0303, 0x0301, 0, 0x4e, 0x004F, 0x0303, 0x0308, 0, 0x4f, 0x006F, 0x0303, 0x0308, 0, 0x50, 0x004F, 0x0304, 0x0300, 0, 0x51, 0x006F, 0x0304, 0x0300, 0, 0x52, 0x004F, 0x0304, 0x0301, 0, 0x53, 0x006F, 0x0304, 0x0301, 0, 0x54, 0x0050, 0x0301, 0, 0x55, 0x0070, 0x0301, 0, 0x56, 0x0050, 0x0307, 0, 0x57, 0x0070, 0x0307, 0, 0x58, 0x0052, 0x0307, 0, 0x59, 0x0072, 0x0307, 0, 0x5a, 0x0052, 0x0323, 0, 0x5b, 0x0072, 0x0323, 0, 0x5c, 0x0052, 0x0323, 0x0304, 0, 0x5d, 0x0072, 0x0323, 0x0304, 0, 0x5e, 0x0052, 0x0331, 0, 0x5f, 0x0072, 0x0331, 0, 0x60, 0x0053, 0x0307, 0, 0x61, 0x0073, 0x0307, 0, 0x62, 0x0053, 0x0323, 0, 0x63, 0x0073, 0x0323, 0, 0x64, 0x0053, 0x0301, 0x0307, 0, 0x65, 0x0073, 0x0301, 0x0307, 0, 0x66, 0x0053, 0x030C, 0x0307, 0, 0x67, 0x0073, 0x030C, 0x0307, 0, 0x68, 0x0053, 0x0323, 0x0307, 0, 0x69, 0x0073, 0x0323, 0x0307, 0, 0x6a, 0x0054, 0x0307, 0, 0x6b, 0x0074, 0x0307, 0, 0x6c, 0x0054, 0x0323, 0, 0x6d, 0x0074, 0x0323, 0, 0x6e, 0x0054, 0x0331, 0, 0x6f, 0x0074, 0x0331, 0, 0x70, 0x0054, 0x032D, 0, 0x71, 0x0074, 0x032D, 0, 0x72, 0x0055, 0x0324, 0, 0x73, 0x0075, 0x0324, 0, 0x74, 0x0055, 0x0330, 0, 0x75, 0x0075, 0x0330, 0, 0x76, 0x0055, 0x032D, 0, 0x77, 0x0075, 0x032D, 0, 0x78, 0x0055, 0x0303, 0x0301, 0, 0x79, 0x0075, 0x0303, 0x0301, 0, 0x7a, 0x0055, 0x0304, 0x0308, 0, 0x7b, 0x0075, 0x0304, 0x0308, 0, 0x7c, 0x0056, 0x0303, 0, 0x7d, 0x0076, 0x0303, 0, 0x7e, 0x0056, 0x0323, 0, 0x7f, 0x0076, 0x0323, 0, 0x80, 0x0057, 0x0300, 0, 0x81, 0x0077, 0x0300, 0, 0x82, 0x0057, 0x0301, 0, 0x83, 0x0077, 0x0301, 0, 0x84, 0x0057, 0x0308, 0, 0x85, 0x0077, 0x0308, 0, 0x86, 0x0057, 0x0307, 0, 0x87, 0x0077, 0x0307, 0, 0x88, 0x0057, 0x0323, 0, 0x89, 0x0077, 0x0323, 0, 0x8a, 0x0058, 0x0307, 0, 0x8b, 0x0078, 0x0307, 0, 0x8c, 0x0058, 0x0308, 0, 0x8d, 0x0078, 0x0308, 0, 0x8e, 0x0059, 0x0307, 0, 0x8f, 0x0079, 0x0307, 0, 0x90, 0x005A, 0x0302, 0, 0x91, 0x007A, 0x0302, 0, 0x92, 0x005A, 0x0323, 0, 0x93, 0x007A, 0x0323, 0, 0x94, 0x005A, 0x0331, 0, 0x95, 0x007A, 0x0331, 0, 0x96, 0x0068, 0x0331, 0, 0x97, 0x0074, 0x0308, 0, 0x98, 0x0077, 0x030A, 0, 0x99, 0x0079, 0x030A, 0, 0x9b, 0x017F, 0x0307, 0, 0xa0, 0x0041, 0x0323, 0, 0xa1, 0x0061, 0x0323, 0, 0xa2, 0x0041, 0x0309, 0, 0xa3, 0x0061, 0x0309, 0, 0xa4, 0x0041, 0x0302, 0x0301, 0, 0xa5, 0x0061, 0x0302, 0x0301, 0, 0xa6, 0x0041, 0x0302, 0x0300, 0, 0xa7, 0x0061, 0x0302, 0x0300, 0, 0xa8, 0x0041, 0x0302, 0x0309, 0, 0xa9, 0x0061, 0x0302, 0x0309, 0, 0xaa, 0x0041, 0x0302, 0x0303, 0, 0xab, 0x0061, 0x0302, 0x0303, 0, 0xac, 0x0041, 0x0323, 0x0302, 0, 0xad, 0x0061, 0x0323, 0x0302, 0, 0xae, 0x0041, 0x0306, 0x0301, 0, 0xaf, 0x0061, 0x0306, 0x0301, 0, 0xb0, 0x0041, 0x0306, 0x0300, 0, 0xb1, 0x0061, 0x0306, 0x0300, 0, 0xb2, 0x0041, 0x0306, 0x0309, 0, 0xb3, 0x0061, 0x0306, 0x0309, 0, 0xb4, 0x0041, 0x0306, 0x0303, 0, 0xb5, 0x0061, 0x0306, 0x0303, 0, 0xb6, 0x0041, 0x0323, 0x0306, 0, 0xb7, 0x0061, 0x0323, 0x0306, 0, 0xb8, 0x0045, 0x0323, 0, 0xb9, 0x0065, 0x0323, 0, 0xba, 0x0045, 0x0309, 0, 0xbb, 0x0065, 0x0309, 0, 0xbc, 0x0045, 0x0303, 0, 0xbd, 0x0065, 0x0303, 0, 0xbe, 0x0045, 0x0302, 0x0301, 0, 0xbf, 0x0065, 0x0302, 0x0301, 0, 0xc0, 0x0045, 0x0302, 0x0300, 0, 0xc1, 0x0065, 0x0302, 0x0300, 0, 0xc2, 0x0045, 0x0302, 0x0309, 0, 0xc3, 0x0065, 0x0302, 0x0309, 0, 0xc4, 0x0045, 0x0302, 0x0303, 0, 0xc5, 0x0065, 0x0302, 0x0303, 0, 0xc6, 0x0045, 0x0323, 0x0302, 0, 0xc7, 0x0065, 0x0323, 0x0302, 0, 0xc8, 0x0049, 0x0309, 0, 0xc9, 0x0069, 0x0309, 0, 0xca, 0x0049, 0x0323, 0, 0xcb, 0x0069, 0x0323, 0, 0xcc, 0x004F, 0x0323, 0, 0xcd, 0x006F, 0x0323, 0, 0xce, 0x004F, 0x0309, 0, 0xcf, 0x006F, 0x0309, 0, 0xd0, 0x004F, 0x0302, 0x0301, 0, 0xd1, 0x006F, 0x0302, 0x0301, 0, 0xd2, 0x004F, 0x0302, 0x0300, 0, 0xd3, 0x006F, 0x0302, 0x0300, 0, 0xd4, 0x004F, 0x0302, 0x0309, 0, 0xd5, 0x006F, 0x0302, 0x0309, 0, 0xd6, 0x004F, 0x0302, 0x0303, 0, 0xd7, 0x006F, 0x0302, 0x0303, 0, 0xd8, 0x004F, 0x0323, 0x0302, 0, 0xd9, 0x006F, 0x0323, 0x0302, 0, 0xda, 0x004F, 0x031B, 0x0301, 0, 0xdb, 0x006F, 0x031B, 0x0301, 0, 0xdc, 0x004F, 0x031B, 0x0300, 0, 0xdd, 0x006F, 0x031B, 0x0300, 0, 0xde, 0x004F, 0x031B, 0x0309, 0, 0xdf, 0x006F, 0x031B, 0x0309, 0, 0xe0, 0x004F, 0x031B, 0x0303, 0, 0xe1, 0x006F, 0x031B, 0x0303, 0, 0xe2, 0x004F, 0x031B, 0x0323, 0, 0xe3, 0x006F, 0x031B, 0x0323, 0, 0xe4, 0x0055, 0x0323, 0, 0xe5, 0x0075, 0x0323, 0, 0xe6, 0x0055, 0x0309, 0, 0xe7, 0x0075, 0x0309, 0, 0xe8, 0x0055, 0x031B, 0x0301, 0, 0xe9, 0x0075, 0x031B, 0x0301, 0, 0xea, 0x0055, 0x031B, 0x0300, 0, 0xeb, 0x0075, 0x031B, 0x0300, 0, 0xec, 0x0055, 0x031B, 0x0309, 0, 0xed, 0x0075, 0x031B, 0x0309, 0, 0xee, 0x0055, 0x031B, 0x0303, 0, 0xef, 0x0075, 0x031B, 0x0303, 0, 0xf0, 0x0055, 0x031B, 0x0323, 0, 0xf1, 0x0075, 0x031B, 0x0323, 0, 0xf2, 0x0059, 0x0300, 0, 0xf3, 0x0079, 0x0300, 0, 0xf4, 0x0059, 0x0323, 0, 0xf5, 0x0079, 0x0323, 0, 0xf6, 0x0059, 0x0309, 0, 0xf7, 0x0079, 0x0309, 0, 0xf8, 0x0059, 0x0303, 0, 0xf9, 0x0079, 0x0303, 0, 0x00, /* page1f */ 0x1f, 0x00, 0x03B1, 0x0313, 0, 0x01, 0x03B1, 0x0314, 0, 0x02, 0x03B1, 0x0313, 0x0300, 0, 0x03, 0x03B1, 0x0314, 0x0300, 0, 0x04, 0x03B1, 0x0313, 0x0301, 0, 0x05, 0x03B1, 0x0314, 0x0301, 0, 0x06, 0x03B1, 0x0313, 0x0342, 0, 0x07, 0x03B1, 0x0314, 0x0342, 0, 0x08, 0x0391, 0x0313, 0, 0x09, 0x0391, 0x0314, 0, 0x0a, 0x0391, 0x0313, 0x0300, 0, 0x0b, 0x0391, 0x0314, 0x0300, 0, 0x0c, 0x0391, 0x0313, 0x0301, 0, 0x0d, 0x0391, 0x0314, 0x0301, 0, 0x0e, 0x0391, 0x0313, 0x0342, 0, 0x0f, 0x0391, 0x0314, 0x0342, 0, 0x10, 0x03B5, 0x0313, 0, 0x11, 0x03B5, 0x0314, 0, 0x12, 0x03B5, 0x0313, 0x0300, 0, 0x13, 0x03B5, 0x0314, 0x0300, 0, 0x14, 0x03B5, 0x0313, 0x0301, 0, 0x15, 0x03B5, 0x0314, 0x0301, 0, 0x18, 0x0395, 0x0313, 0, 0x19, 0x0395, 0x0314, 0, 0x1a, 0x0395, 0x0313, 0x0300, 0, 0x1b, 0x0395, 0x0314, 0x0300, 0, 0x1c, 0x0395, 0x0313, 0x0301, 0, 0x1d, 0x0395, 0x0314, 0x0301, 0, 0x20, 0x03B7, 0x0313, 0, 0x21, 0x03B7, 0x0314, 0, 0x22, 0x03B7, 0x0313, 0x0300, 0, 0x23, 0x03B7, 0x0314, 0x0300, 0, 0x24, 0x03B7, 0x0313, 0x0301, 0, 0x25, 0x03B7, 0x0314, 0x0301, 0, 0x26, 0x03B7, 0x0313, 0x0342, 0, 0x27, 0x03B7, 0x0314, 0x0342, 0, 0x28, 0x0397, 0x0313, 0, 0x29, 0x0397, 0x0314, 0, 0x2a, 0x0397, 0x0313, 0x0300, 0, 0x2b, 0x0397, 0x0314, 0x0300, 0, 0x2c, 0x0397, 0x0313, 0x0301, 0, 0x2d, 0x0397, 0x0314, 0x0301, 0, 0x2e, 0x0397, 0x0313, 0x0342, 0, 0x2f, 0x0397, 0x0314, 0x0342, 0, 0x30, 0x03B9, 0x0313, 0, 0x31, 0x03B9, 0x0314, 0, 0x32, 0x03B9, 0x0313, 0x0300, 0, 0x33, 0x03B9, 0x0314, 0x0300, 0, 0x34, 0x03B9, 0x0313, 0x0301, 0, 0x35, 0x03B9, 0x0314, 0x0301, 0, 0x36, 0x03B9, 0x0313, 0x0342, 0, 0x37, 0x03B9, 0x0314, 0x0342, 0, 0x38, 0x0399, 0x0313, 0, 0x39, 0x0399, 0x0314, 0, 0x3a, 0x0399, 0x0313, 0x0300, 0, 0x3b, 0x0399, 0x0314, 0x0300, 0, 0x3c, 0x0399, 0x0313, 0x0301, 0, 0x3d, 0x0399, 0x0314, 0x0301, 0, 0x3e, 0x0399, 0x0313, 0x0342, 0, 0x3f, 0x0399, 0x0314, 0x0342, 0, 0x40, 0x03BF, 0x0313, 0, 0x41, 0x03BF, 0x0314, 0, 0x42, 0x03BF, 0x0313, 0x0300, 0, 0x43, 0x03BF, 0x0314, 0x0300, 0, 0x44, 0x03BF, 0x0313, 0x0301, 0, 0x45, 0x03BF, 0x0314, 0x0301, 0, 0x48, 0x039F, 0x0313, 0, 0x49, 0x039F, 0x0314, 0, 0x4a, 0x039F, 0x0313, 0x0300, 0, 0x4b, 0x039F, 0x0314, 0x0300, 0, 0x4c, 0x039F, 0x0313, 0x0301, 0, 0x4d, 0x039F, 0x0314, 0x0301, 0, 0x50, 0x03C5, 0x0313, 0, 0x51, 0x03C5, 0x0314, 0, 0x52, 0x03C5, 0x0313, 0x0300, 0, 0x53, 0x03C5, 0x0314, 0x0300, 0, 0x54, 0x03C5, 0x0313, 0x0301, 0, 0x55, 0x03C5, 0x0314, 0x0301, 0, 0x56, 0x03C5, 0x0313, 0x0342, 0, 0x57, 0x03C5, 0x0314, 0x0342, 0, 0x59, 0x03A5, 0x0314, 0, 0x5b, 0x03A5, 0x0314, 0x0300, 0, 0x5d, 0x03A5, 0x0314, 0x0301, 0, 0x5f, 0x03A5, 0x0314, 0x0342, 0, 0x60, 0x03C9, 0x0313, 0, 0x61, 0x03C9, 0x0314, 0, 0x62, 0x03C9, 0x0313, 0x0300, 0, 0x63, 0x03C9, 0x0314, 0x0300, 0, 0x64, 0x03C9, 0x0313, 0x0301, 0, 0x65, 0x03C9, 0x0314, 0x0301, 0, 0x66, 0x03C9, 0x0313, 0x0342, 0, 0x67, 0x03C9, 0x0314, 0x0342, 0, 0x68, 0x03A9, 0x0313, 0, 0x69, 0x03A9, 0x0314, 0, 0x6a, 0x03A9, 0x0313, 0x0300, 0, 0x6b, 0x03A9, 0x0314, 0x0300, 0, 0x6c, 0x03A9, 0x0313, 0x0301, 0, 0x6d, 0x03A9, 0x0314, 0x0301, 0, 0x6e, 0x03A9, 0x0313, 0x0342, 0, 0x6f, 0x03A9, 0x0314, 0x0342, 0, 0x70, 0x03B1, 0x0300, 0, 0x71, 0x03B1, 0x0301, 0, 0x72, 0x03B5, 0x0300, 0, 0x73, 0x03B5, 0x0301, 0, 0x74, 0x03B7, 0x0300, 0, 0x75, 0x03B7, 0x0301, 0, 0x76, 0x03B9, 0x0300, 0, 0x77, 0x03B9, 0x0301, 0, 0x78, 0x03BF, 0x0300, 0, 0x79, 0x03BF, 0x0301, 0, 0x7a, 0x03C5, 0x0300, 0, 0x7b, 0x03C5, 0x0301, 0, 0x7c, 0x03C9, 0x0300, 0, 0x7d, 0x03C9, 0x0301, 0, 0x80, 0x03B1, 0x0345, 0x0313, 0, 0x81, 0x03B1, 0x0345, 0x0314, 0, 0x82, 0x03B1, 0x0345, 0x0313, 0x0300, 0, 0x83, 0x03B1, 0x0345, 0x0314, 0x0300, 0, 0x84, 0x03B1, 0x0345, 0x0313, 0x0301, 0, 0x85, 0x03B1, 0x0345, 0x0314, 0x0301, 0, 0x86, 0x03B1, 0x0345, 0x0313, 0x0342, 0, 0x87, 0x03B1, 0x0345, 0x0314, 0x0342, 0, 0x88, 0x0391, 0x0345, 0x0313, 0, 0x89, 0x0391, 0x0345, 0x0314, 0, 0x8a, 0x0391, 0x0345, 0x0313, 0x0300, 0, 0x8b, 0x0391, 0x0345, 0x0314, 0x0300, 0, 0x8c, 0x0391, 0x0345, 0x0313, 0x0301, 0, 0x8d, 0x0391, 0x0345, 0x0314, 0x0301, 0, 0x8e, 0x0391, 0x0345, 0x0313, 0x0342, 0, 0x8f, 0x0391, 0x0345, 0x0314, 0x0342, 0, 0x90, 0x03B7, 0x0345, 0x0313, 0, 0x91, 0x03B7, 0x0345, 0x0314, 0, 0x92, 0x03B7, 0x0345, 0x0313, 0x0300, 0, 0x93, 0x03B7, 0x0345, 0x0314, 0x0300, 0, 0x94, 0x03B7, 0x0345, 0x0313, 0x0301, 0, 0x95, 0x03B7, 0x0345, 0x0314, 0x0301, 0, 0x96, 0x03B7, 0x0345, 0x0313, 0x0342, 0, 0x97, 0x03B7, 0x0345, 0x0314, 0x0342, 0, 0x98, 0x0397, 0x0345, 0x0313, 0, 0x99, 0x0397, 0x0345, 0x0314, 0, 0x9a, 0x0397, 0x0345, 0x0313, 0x0300, 0, 0x9b, 0x0397, 0x0345, 0x0314, 0x0300, 0, 0x9c, 0x0397, 0x0345, 0x0313, 0x0301, 0, 0x9d, 0x0397, 0x0345, 0x0314, 0x0301, 0, 0x9e, 0x0397, 0x0345, 0x0313, 0x0342, 0, 0x9f, 0x0397, 0x0345, 0x0314, 0x0342, 0, 0xa0, 0x03C9, 0x0345, 0x0313, 0, 0xa1, 0x03C9, 0x0345, 0x0314, 0, 0xa2, 0x03C9, 0x0345, 0x0313, 0x0300, 0, 0xa3, 0x03C9, 0x0345, 0x0314, 0x0300, 0, 0xa4, 0x03C9, 0x0345, 0x0313, 0x0301, 0, 0xa5, 0x03C9, 0x0345, 0x0314, 0x0301, 0, 0xa6, 0x03C9, 0x0345, 0x0313, 0x0342, 0, 0xa7, 0x03C9, 0x0345, 0x0314, 0x0342, 0, 0xa8, 0x03A9, 0x0345, 0x0313, 0, 0xa9, 0x03A9, 0x0345, 0x0314, 0, 0xaa, 0x03A9, 0x0345, 0x0313, 0x0300, 0, 0xab, 0x03A9, 0x0345, 0x0314, 0x0300, 0, 0xac, 0x03A9, 0x0345, 0x0313, 0x0301, 0, 0xad, 0x03A9, 0x0345, 0x0314, 0x0301, 0, 0xae, 0x03A9, 0x0345, 0x0313, 0x0342, 0, 0xaf, 0x03A9, 0x0345, 0x0314, 0x0342, 0, 0xb0, 0x03B1, 0x0306, 0, 0xb1, 0x03B1, 0x0304, 0, 0xb2, 0x03B1, 0x0345, 0x0300, 0, 0xb3, 0x03B1, 0x0345, 0, 0xb4, 0x03B1, 0x0345, 0x0301, 0, 0xb6, 0x03B1, 0x0342, 0, 0xb7, 0x03B1, 0x0345, 0x0342, 0, 0xb8, 0x0391, 0x0306, 0, 0xb9, 0x0391, 0x0304, 0, 0xba, 0x0391, 0x0300, 0, 0xbb, 0x0391, 0x0301, 0, 0xbc, 0x0391, 0x0345, 0, 0xbe, 0x03B9, 0, 0xc1, 0x00A8, 0x0342, 0, 0xc2, 0x03B7, 0x0345, 0x0300, 0, 0xc3, 0x03B7, 0x0345, 0, 0xc4, 0x03B7, 0x0345, 0x0301, 0, 0xc6, 0x03B7, 0x0342, 0, 0xc7, 0x03B7, 0x0345, 0x0342, 0, 0xc8, 0x0395, 0x0300, 0, 0xc9, 0x0395, 0x0301, 0, 0xca, 0x0397, 0x0300, 0, 0xcb, 0x0397, 0x0301, 0, 0xcc, 0x0397, 0x0345, 0, 0xcd, 0x1FBF, 0x0300, 0, 0xce, 0x1FBF, 0x0301, 0, 0xcf, 0x1FBF, 0x0342, 0, 0xd0, 0x03B9, 0x0306, 0, 0xd1, 0x03B9, 0x0304, 0, 0xd2, 0x03B9, 0x0308, 0x0300, 0, 0xd3, 0x03B9, 0x0308, 0x0301, 0, 0xd6, 0x03B9, 0x0342, 0, 0xd7, 0x03B9, 0x0308, 0x0342, 0, 0xd8, 0x0399, 0x0306, 0, 0xd9, 0x0399, 0x0304, 0, 0xda, 0x0399, 0x0300, 0, 0xdb, 0x0399, 0x0301, 0, 0xdd, 0x1FFE, 0x0300, 0, 0xde, 0x1FFE, 0x0301, 0, 0xdf, 0x1FFE, 0x0342, 0, 0xe0, 0x03C5, 0x0306, 0, 0xe1, 0x03C5, 0x0304, 0, 0xe2, 0x03C5, 0x0308, 0x0300, 0, 0xe3, 0x03C5, 0x0308, 0x0301, 0, 0xe4, 0x03C1, 0x0313, 0, 0xe5, 0x03C1, 0x0314, 0, 0xe6, 0x03C5, 0x0342, 0, 0xe7, 0x03C5, 0x0308, 0x0342, 0, 0xe8, 0x03A5, 0x0306, 0, 0xe9, 0x03A5, 0x0304, 0, 0xea, 0x03A5, 0x0300, 0, 0xeb, 0x03A5, 0x0301, 0, 0xec, 0x03A1, 0x0314, 0, 0xed, 0x00A8, 0x0300, 0, 0xee, 0x00A8, 0x0301, 0, 0xef, 0x0060, 0, 0xf2, 0x03C9, 0x0345, 0x0300, 0, 0xf3, 0x03C9, 0x0345, 0, 0xf4, 0x03BF, 0x0345, 0x0301, 0, 0xf6, 0x03C9, 0x0342, 0, 0xf7, 0x03C9, 0x0345, 0x0342, 0, 0xf8, 0x039F, 0x0300, 0, 0xf9, 0x039F, 0x0301, 0, 0xfa, 0x03A9, 0x0300, 0, 0xfb, 0x03A9, 0x0301, 0, 0xfc, 0x03A9, 0x0345, 0, 0xfd, 0x00B4, 0, 0x00, /* page30 */ 0x30, 0x4c, 0x304B, 0x3099, 0, 0x4e, 0x304D, 0x3099, 0, 0x50, 0x304F, 0x3099, 0, 0x52, 0x3051, 0x3099, 0, 0x54, 0x3053, 0x3099, 0, 0x56, 0x3055, 0x3099, 0, 0x58, 0x3057, 0x3099, 0, 0x5a, 0x3059, 0x3099, 0, 0x5c, 0x305B, 0x3099, 0, 0x5e, 0x305D, 0x3099, 0, 0x60, 0x305F, 0x3099, 0, 0x62, 0x3061, 0x3099, 0, 0x65, 0x3064, 0x3099, 0, 0x67, 0x3066, 0x3099, 0, 0x69, 0x3068, 0x3099, 0, 0x70, 0x306F, 0x3099, 0, 0x71, 0x306F, 0x309A, 0, 0x73, 0x3072, 0x3099, 0, 0x74, 0x3072, 0x309A, 0, 0x76, 0x3075, 0x3099, 0, 0x77, 0x3075, 0x309A, 0, 0x79, 0x3078, 0x3099, 0, 0x7a, 0x3078, 0x309A, 0, 0x7c, 0x307B, 0x3099, 0, 0x7d, 0x307B, 0x309A, 0, 0x94, 0x3046, 0x3099, 0, 0x9e, 0x309D, 0x3099, 0, 0xac, 0x30AB, 0x3099, 0, 0xae, 0x30AD, 0x3099, 0, 0xb0, 0x30AF, 0x3099, 0, 0xb2, 0x30B1, 0x3099, 0, 0xb4, 0x30B3, 0x3099, 0, 0xb6, 0x30B5, 0x3099, 0, 0xb8, 0x30B7, 0x3099, 0, 0xba, 0x30B9, 0x3099, 0, 0xbc, 0x30BB, 0x3099, 0, 0xbe, 0x30BD, 0x3099, 0, 0xc0, 0x30BF, 0x3099, 0, 0xc2, 0x30C1, 0x3099, 0, 0xc5, 0x30C4, 0x3099, 0, 0xc7, 0x30C6, 0x3099, 0, 0xc9, 0x30C8, 0x3099, 0, 0xd0, 0x30CF, 0x3099, 0, 0xd1, 0x30CF, 0x309A, 0, 0xd3, 0x30D2, 0x3099, 0, 0xd4, 0x30D2, 0x309A, 0, 0xd6, 0x30D5, 0x3099, 0, 0xd7, 0x30D5, 0x309A, 0, 0xd9, 0x30D8, 0x3099, 0, 0xda, 0x30D8, 0x309A, 0, 0xdc, 0x30DB, 0x3099, 0, 0xdd, 0x30DB, 0x309A, 0, 0xf4, 0x30A6, 0x3099, 0, 0xf7, 0x30EF, 0x3099, 0, 0xf8, 0x30F0, 0x3099, 0, 0xf9, 0x30F1, 0x3099, 0, 0xfa, 0x30F2, 0x3099, 0, 0xfe, 0x30FD, 0x3099, 0, 0x00, /* pagefb */ 0xfb, 0x1f, 0x05F2, 0x05B7, 0, 0x2a, 0x05E9, 0x05C1, 0, 0x2b, 0x05E9, 0x05C2, 0, 0x2c, 0x05E9, 0x05BC, 0x05C1, 0, 0x2d, 0x05E9, 0x05BC, 0x05C2, 0, 0x2e, 0x05D0, 0x05B7, 0, 0x2f, 0x05D0, 0x05B8, 0, 0x30, 0x05D0, 0x05BC, 0, 0x31, 0x05D1, 0x05BC, 0, 0x32, 0x05D2, 0x05BC, 0, 0x33, 0x05D3, 0x05BC, 0, 0x34, 0x05D4, 0x05BC, 0, 0x35, 0x05D5, 0x05BC, 0, 0x36, 0x05D6, 0x05BC, 0, 0x38, 0x05D8, 0x05BC, 0, 0x39, 0x05D9, 0x05BC, 0, 0x3a, 0x05DA, 0x05BC, 0, 0x3b, 0x05DB, 0x05BC, 0, 0x3c, 0x05DC, 0x05BC, 0, 0x3e, 0x05DE, 0x05BC, 0, 0x40, 0x05E0, 0x05BC, 0, 0x41, 0x05E1, 0x05BC, 0, 0x43, 0x05E3, 0x05BC, 0, 0x44, 0x05E4, 0x05BC, 0, 0x46, 0x05E6, 0x05BC, 0, 0x47, 0x05E7, 0x05BC, 0, 0x48, 0x05E8, 0x05BC, 0, 0x49, 0x05E9, 0x05BC, 0, 0x4a, 0x05EA, 0x05BC, 0, 0x4b, 0x05D5, 0x05B9, 0, 0x4c, 0x05D1, 0x05BF, 0, 0x4d, 0x05DB, 0x05BF, 0, 0x4e, 0x05E4, 0x05BF, 0, 0x00, /* end of list */ 0x00 }; #define HFSPLUS_DECOMPOSE_PAGE_SIZE (256 * (HFSPLUS_MAX_DECOMPOSE_LEN + 1)) static uint16_t decompose_pages[16 * 256][HFSPLUS_MAX_DECOMPOSE_LEN + 1]; uint16_t (*hfsplus_decompose_pages[256])[HFSPLUS_MAX_DECOMPOSE_LEN + 1]; void make_hfsplus_decompose_pages() { int page_idx = -1, char_idx, i; uint16_t *rpt, *page_pt, *value_pt; int page_count = 0; memset(decompose_pages, 0, 16 * HFSPLUS_DECOMPOSE_PAGE_SIZE); for (i = 0; i < 256; i++) hfsplus_decompose_pages[i] = NULL; rpt = (uint16_t *) decompose_page_data; page_pt = (uint16_t *) decompose_pages; while (1) { if (*rpt <= page_idx) break; page_count++; page_idx = *(rpt++); char_idx = -1; while (1) { if(*rpt <= char_idx) break; char_idx = *(rpt++); value_pt = page_pt + char_idx * (HFSPLUS_MAX_DECOMPOSE_LEN + 1); while (1) { if(*rpt == 0) break; *(value_pt++) = *(rpt++); } rpt++; } rpt++; hfsplus_decompose_pages[page_idx] = decompose_pages + (page_count - 1) * 256; page_pt += HFSPLUS_DECOMPOSE_PAGE_SIZE; } } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libisofs.h" #include "image.h" #include "node.h" #include "messages.h" #include "eltorito.h" #include "system_area.h" #include #include #include int iso_imported_sa_new(struct iso_imported_sys_area **boots, int flag) { struct iso_imported_sys_area *b; *boots = NULL; b = calloc(1, sizeof(struct iso_imported_sys_area)); if (b == NULL) return ISO_OUT_OF_MEM; b->mbr_req = NULL; b->apm_req = NULL; b->gpt_req = NULL; b->gpt_backup_comments = NULL; b->mips_boot_file_paths = NULL; b->mips_vd_entries = NULL; b->sparc_disc_label = NULL; b->sparc_core_node = NULL; b->sparc_core_node_path = NULL; b->sparc_entries = NULL; b->hppa_cmdline = NULL; b->hppa_bootloader = NULL; b->hppa_kernel_32 = NULL; b->hppa_kernel_64 = NULL; b->hppa_ramdisk = NULL; b->alpha_boot_image = NULL; *boots = b; return 1; } int iso_imported_sa_unref(struct iso_imported_sys_area **boots, int flag) { int i; struct iso_imported_sys_area *b; b = *boots; if (b == NULL) return 2; if (b->refcount > 0) b->refcount--; if (b->refcount > 0) return 2; if (b->mbr_req != NULL) { for (i = 0; i < b->mbr_req_count; i++) { if (b->mbr_req[i] != NULL) LIBISO_FREE_MEM(b->mbr_req[i]->image_path); LIBISO_FREE_MEM(b->mbr_req[i]); } LIBISO_FREE_MEM(b->mbr_req); } if (b->apm_req != NULL) { for (i = 0; i < b->apm_req_count; i++) { if (b->apm_req[i] != NULL) LIBISO_FREE_MEM(b->apm_req[i]->image_path); LIBISO_FREE_MEM(b->apm_req[i]); } LIBISO_FREE_MEM(b->apm_req); } if (b->gpt_req != NULL) { for (i = 0; i < b->gpt_req_count; i++) { if (b->gpt_req[i] != NULL) LIBISO_FREE_MEM(b->gpt_req[i]->image_path); LIBISO_FREE_MEM(b->gpt_req[i]); } LIBISO_FREE_MEM(b->gpt_req); } LIBISO_FREE_MEM(b->gpt_backup_comments); if (b->mips_boot_file_paths != NULL) { for (i = 0; i < b->num_mips_boot_files; i++) LIBISO_FREE_MEM(b->mips_boot_file_paths[i]); LIBISO_FREE_MEM(b->mips_boot_file_paths); } if (b->mips_vd_entries != NULL) { for (i = 0; i < b->num_mips_boot_files; i++) LIBISO_FREE_MEM(b->mips_vd_entries[i]); LIBISO_FREE_MEM(b->mips_vd_entries); } LIBISO_FREE_MEM(b->mipsel_boot_file_path); LIBISO_FREE_MEM(b->sparc_disc_label); if (b->sparc_core_node != NULL) iso_node_unref((IsoNode *) b->sparc_core_node); LIBISO_FREE_MEM(b->sparc_core_node_path); LIBISO_FREE_MEM(b->sparc_entries); LIBISO_FREE_MEM(b->hppa_cmdline); LIBISO_FREE_MEM(b->hppa_bootloader); LIBISO_FREE_MEM(b->hppa_kernel_32); LIBISO_FREE_MEM(b->hppa_kernel_64); LIBISO_FREE_MEM(b->hppa_ramdisk); LIBISO_FREE_MEM(b->alpha_boot_image); LIBISO_FREE_MEM(b); *boots = NULL; return 1; } /** * Create a new image, empty. * * The image will be owned by you and should be unref() when no more needed. * * @param name * Name of the image. This will be used as volset_id and volume_id. * @param image * Location where the image pointer will be stored. * @return * 1 success, < 0 error */ int iso_image_new(const char *name, IsoImage **image) { int res, i; IsoImage *img; if (image == NULL) { return ISO_NULL_POINTER; } img = calloc(1, sizeof(IsoImage)); if (img == NULL) { return ISO_OUT_OF_MEM; } /* local filesystem will be used by default */ res = iso_local_filesystem_new(&(img->fs)); if (res < 0) { free(img); return ISO_OUT_OF_MEM; } /* use basic builder as default */ res = iso_node_basic_builder_new(&(img->builder)); if (res < 0) { iso_filesystem_unref(img->fs); free(img); return ISO_OUT_OF_MEM; } /* fill image fields */ res = iso_node_new_root(&img->root); if (res < 0) { iso_node_builder_unref(img->builder); iso_filesystem_unref(img->fs); free(img); return res; } img->refcount = 1; img->id = iso_message_id++; if (name != NULL) { img->volset_id = strdup(name); img->volume_id = strdup(name); } memset(img->application_use, 0, 512); img->system_area_data = NULL; img->system_area_options = 0; img->num_mips_boot_files = 0; for (i = 0; i < 15; i++) img->mips_boot_file_paths[i] = NULL; img->sparc_core_node = NULL; img->hppa_cmdline= NULL; img->hppa_bootloader = NULL; img->hppa_kernel_32 = NULL; img->hppa_kernel_64 = NULL; img->hppa_ramdisk = NULL; img->alpha_boot_image = NULL; img->import_src = NULL; img->builder_ignore_acl = 1; img->builder_ignore_ea = 1; img->builder_ignore_lfa_flags = 1; img->builder_ignore_ro_lfa_flags = 0; img->truncate_mode = 1; img->truncate_length = LIBISOFS_NODE_NAME_MAX; img->truncate_buffer[0] = 0; img->inode_counter = 0; img->used_inodes = NULL; img->used_inodes_start = 0; img->checksum_start_lba = 0; img->checksum_end_lba = 0; img->checksum_idx_count = 0; img->checksum_array = NULL; img->generator_is_running = 0; for (i = 0; i < ISO_HFSPLUS_BLESS_MAX; i++) img->hfsplus_blessed[i] = NULL; img->collision_warnings = 0; img->imported_sa_info = NULL; img->blind_on_local_get_attrs = 0; img->do_deeper_tree_inspection = 0; img->tree_loaded = 0; img->rr_loaded = 0; img->tree_compliance = NULL; *image = img; return ISO_SUCCESS; } /** * Increments the reference counting of the given image. */ void iso_image_ref(IsoImage *image) { ++image->refcount; } /** * Decrements the reference counting of the given image. * If it reaches 0, the image is free, together with its tree nodes (whether * their refcount reach 0 too, of course). */ void iso_image_unref(IsoImage *image) { int nexcl, i; if (--image->refcount == 0) { /* we need to free the image */ if (image->user_data_free != NULL) { /* free attached data */ image->user_data_free(image->user_data); } for (nexcl = 0; nexcl < image->nexcludes; ++nexcl) { free(image->excludes[nexcl]); } free(image->excludes); for (i = 0; i < ISO_HFSPLUS_BLESS_MAX; i++) if (image->hfsplus_blessed[i] != NULL) iso_node_unref(image->hfsplus_blessed[i]); iso_node_unref((IsoNode*)image->root); iso_node_builder_unref(image->builder); iso_filesystem_unref(image->fs); el_torito_boot_catalog_free(image->bootcat); iso_image_give_up_mips_boot(image, 0); if (image->sparc_core_node != NULL) iso_node_unref((IsoNode *) image->sparc_core_node); iso_image_set_hppa_palo(image, NULL, NULL, NULL, NULL, NULL, 1); if (image->alpha_boot_image != NULL) free(image->alpha_boot_image); if (image->import_src != NULL) iso_data_source_unref(image->import_src); free(image->volset_id); free(image->volume_id); free(image->publisher_id); free(image->data_preparer_id); free(image->system_id); free(image->application_id); free(image->copyright_file_id); free(image->abstract_file_id); free(image->biblio_file_id); free(image->creation_time); free(image->modification_time); free(image->expiration_time); free(image->effective_time); if (image->used_inodes != NULL) free(image->used_inodes); if (image->system_area_data != NULL) free(image->system_area_data); iso_image_free_checksums(image, 0); iso_imported_sa_unref(&(image->imported_sa_info), 0); if (image->tree_compliance != NULL) iso_write_opts_free(image->tree_compliance); free(image); } } int iso_image_free_checksums(IsoImage *image, int flag) { image->checksum_start_lba = 0; image->checksum_end_lba = 0; image->checksum_idx_count = 0; if (image->checksum_array != NULL) free(image->checksum_array); image->checksum_array = NULL; return 1; } /** * Attach user defined data to the image. Use this if your application needs * to store addition info together with the IsoImage. If the image already * has data attached, the old data will be freed. * * @param data * Pointer to application defined data that will be attached to the * image. You can pass NULL to remove any already attached data. * @param give_up * Function that will be called when the image does not need the data * any more. It receives the data pointer as an argumente, and eventually * causes data to be free. It can be NULL if you don't need it. */ int iso_image_attach_data(IsoImage *image, void *data, void (*give_up)(void*)) { if (image == NULL) { return ISO_NULL_POINTER; } if (image->user_data != NULL) { /* free previously attached data */ if (image->user_data_free != NULL) { image->user_data_free(image->user_data); } image->user_data = NULL; image->user_data_free = NULL; } if (data != NULL) { image->user_data = data; image->user_data_free = give_up; } return ISO_SUCCESS; } /** * The the data previously attached with iso_image_attach_data() */ void *iso_image_get_attached_data(IsoImage *image) { return image->user_data; } IsoDir *iso_image_get_root(const IsoImage *image) { return image->root; } void iso_image_set_volset_id(IsoImage *image, const char *volset_id) { free(image->volset_id); image->volset_id = strdup(volset_id); } const char *iso_image_get_volset_id(const IsoImage *image) { if (image->volset_id == NULL) return ""; return image->volset_id; } void iso_image_set_volume_id(IsoImage *image, const char *volume_id) { free(image->volume_id); image->volume_id = strdup(volume_id); } const char *iso_image_get_volume_id(const IsoImage *image) { if (image->volume_id == NULL) return ""; return image->volume_id; } void iso_image_set_publisher_id(IsoImage *image, const char *publisher_id) { free(image->publisher_id); image->publisher_id = strdup(publisher_id); } const char *iso_image_get_publisher_id(const IsoImage *image) { if (image->publisher_id == NULL) return ""; return image->publisher_id; } void iso_image_set_data_preparer_id(IsoImage *image, const char *data_preparer_id) { free(image->data_preparer_id); image->data_preparer_id = strdup(data_preparer_id); } const char *iso_image_get_data_preparer_id(const IsoImage *image) { if (image->data_preparer_id == NULL) return ""; return image->data_preparer_id; } void iso_image_set_system_id(IsoImage *image, const char *system_id) { free(image->system_id); image->system_id = strdup(system_id); } const char *iso_image_get_system_id(const IsoImage *image) { if (image->system_id == NULL) return ""; return image->system_id; } void iso_image_set_application_id(IsoImage *image, const char *application_id) { free(image->application_id); image->application_id = strdup(application_id); } const char *iso_image_get_application_id(const IsoImage *image) { if (image->application_id == NULL) return ""; return image->application_id; } void iso_image_set_copyright_file_id(IsoImage *image, const char *copyright_file_id) { free(image->copyright_file_id); image->copyright_file_id = strdup(copyright_file_id); } const char *iso_image_get_copyright_file_id(const IsoImage *image) { if (image->copyright_file_id == NULL) return ""; return image->copyright_file_id; } void iso_image_set_abstract_file_id(IsoImage *image, const char *abstract_file_id) { free(image->abstract_file_id); image->abstract_file_id = strdup(abstract_file_id); } const char *iso_image_get_abstract_file_id(const IsoImage *image) { if (image->abstract_file_id == NULL) return ""; return image->abstract_file_id; } void iso_image_set_biblio_file_id(IsoImage *image, const char *biblio_file_id) { free(image->biblio_file_id); image->biblio_file_id = strdup(biblio_file_id); } const char *iso_image_get_biblio_file_id(const IsoImage *image) { if (image->biblio_file_id == NULL) return ""; return image->biblio_file_id; } int iso_image_set_pvd_times(IsoImage *image, char *creation_time, char *modification_time, char *expiration_time, char *effective_time) { if (creation_time == NULL || modification_time == NULL || expiration_time == NULL || effective_time == NULL) return ISO_NULL_POINTER; image->creation_time = calloc(18, 1); /* Surely including a trailing 0 */ image->modification_time = calloc(18, 1); image->expiration_time = calloc(18, 1); image->effective_time = calloc(18, 1); if (image->creation_time == NULL || image->modification_time == NULL || image->expiration_time == NULL || image->effective_time == NULL) return ISO_OUT_OF_MEM; /* (If the string is too short, a non-zero timezone will not be stored) */ strncpy(image->creation_time, creation_time, 17); strncpy(image->modification_time, modification_time, 17); strncpy(image->expiration_time, expiration_time, 17); strncpy(image->effective_time, effective_time, 17); return ISO_SUCCESS; } int iso_image_get_pvd_times(IsoImage *image, char **creation_time, char **modification_time, char **expiration_time, char **effective_time) { if (image->creation_time == NULL || image->modification_time == NULL || image->expiration_time == NULL || image->effective_time == NULL) return ISO_NULL_POINTER; *creation_time = image->creation_time; *modification_time = image->modification_time; *expiration_time = image->expiration_time; *effective_time = image->effective_time; return ISO_SUCCESS; } void iso_image_set_app_use(IsoImage *image, const char *app_use_data, int count) { if (count < 0) count= 0; else if(count > 512) count= 512; if (count > 0) memcpy(image->application_use, app_use_data, count); if (count < 512) memset(image->application_use + count, 0, 512 - count); } int iso_image_get_msg_id(IsoImage *image) { return image->id; } int iso_image_get_system_area(IsoImage *img, char system_area_data[32768], int *options, int flag) { *options = img->system_area_options; if (img->system_area_data == NULL) return 0; memcpy(system_area_data, img->system_area_data, 32768); return 1; } static int dir_update_size(IsoImage *image, IsoDir *dir) { IsoNode *pos; int ret; #ifdef Libisofs_update_sizes_abortablE char *path= NULL; IsoStream *base_stream; int cancel_ret, ret; uint32_t lba; #endif pos = dir->children; while (pos) { if (pos->type == LIBISO_FILE) { ret = iso_stream_update_size(ISO_FILE(pos)->stream); } else if (pos->type == LIBISO_DIR) { /* recurse */ ret = dir_update_size(image, ISO_DIR(pos)); #ifdef Libisofs_update_sizes_abortablE if (ret == ISO_CANCELED) return ret; /* Message already issued by dir_update_size */ #endif } else { ret = 1; } #ifdef Libisofs_update_sizes_abortablE /* This would report error and abort according to severity threshold. But it is desirable to let the update_size crawler continue its work after e.g. a file has vanished from hard disk. So normally this macro case should be disabled. */ if (ret < 0) { cancel_ret = iso_msg_submit(image->id, ret, 0, NULL); path = iso_tree_get_node_path(pos); if (path != NULL) { iso_msg_submit(image->id, ret, 0, "ISO path : %s", path); free(path); } /* Report source path with streams which do not come from the loaded ISO filesystem */ if (pos->type == LIBISO_FILE && iso_node_get_old_image_lba(pos, &lba, 0) == 0) { base_stream = iso_stream_get_input_stream( ISO_FILE(pos)->stream, 1); if (base_stream == NULL) base_stream = ISO_FILE(pos)->stream; path = iso_stream_get_source_path(base_stream, 0); if (path != NULL) { iso_msg_submit(image->id, ret, 0, "Local path: %s", path); free(path); } } if (cancel_ret < 0) return cancel_ret; /* cancel due error threshold */ } #else if (ret < 0) ret = 1; /* ignore error */ #endif /* ! Libisofs_update_sizes_abortablE */ pos = pos->next; } return ISO_SUCCESS; } int iso_image_update_sizes(IsoImage *image) { if (image == NULL) { return ISO_NULL_POINTER; } return dir_update_size(image, image->root); } void iso_image_set_ignore_aclea(IsoImage *image, int what) { image->builder_ignore_acl = (what & 1); image->builder_ignore_ea = !!(what & 2); image->builder_ignore_lfa_flags= !(what & 4); image->builder_take_all_ea = !!(what & 8); image->builder_ignore_ro_lfa_flags = !!(what & 32); } int iso_image_get_ignore_aclea(IsoImage *image) { return image->builder_ignore_acl | (image->builder_ignore_ea << 1) | ((!image->builder_ignore_lfa_flags) << 2) | (image->builder_take_all_ea << 3) | (image->builder_ignore_ro_lfa_flags << 5); } static int img_register_ino(IsoImage *image, IsoNode *node, int flag) { int ret; ino_t ino; unsigned int fs_id; dev_t dev_id; ret = iso_node_get_id(node, &fs_id, &dev_id, &ino, 1); if (ret < 0) return ret; if (ret > 0 && ino >= image->used_inodes_start && ino <= image->used_inodes_start + (ISO_USED_INODE_RANGE - 1)) { /* without -1 : rollover hazard on 32 bit */ image->used_inodes[(ino - image->used_inodes_start) / 8] |= (1 << (ino % 8)); } return 1; } /* Collect the bitmap of used inode numbers in the range of _ImageFsData.used_inodes_start + ISO_USED_INODE_RANGE @param flag bit0= recursion is active */ int img_collect_inos(IsoImage *image, IsoDir *dir, int flag) { int ret, register_dir = 1; IsoDirIter *iter = NULL; IsoNode *node; IsoDir *subdir; if (dir == NULL) dir = image->root; if (image->used_inodes == NULL) { image->used_inodes = calloc(ISO_USED_INODE_RANGE / 8, 1); if (image->used_inodes == NULL) return ISO_OUT_OF_MEM; } else if(!(flag & 1)) { memset(image->used_inodes, 0, ISO_USED_INODE_RANGE / 8); } else { register_dir = 0; } if (register_dir) { node = (IsoNode *) dir; ret = img_register_ino(image, node, 0); if (ret < 0) return ret; } ret = iso_dir_get_children(dir, &iter); if (ret < 0) return ret; while (iso_dir_iter_next(iter, &node) == 1 ) { ret = img_register_ino(image, node, 0); if (ret < 0) goto ex; if (iso_node_get_type(node) == LIBISO_DIR) { subdir = (IsoDir *) node; ret = img_collect_inos(image, subdir, flag | 1); if (ret < 0) goto ex; } } ret = 1; ex:; if (iter != NULL) iso_dir_iter_free(iter); return ret; } /** * A global counter for Rock Ridge inode numbers in the ISO image filesystem. * * On image import it gets maxed by the eventual inode numbers from PX * entries. Up to the first 32 bit rollover it simply increments the counter. * After the first rollover it uses a look ahead bitmap which gets filled * by a full tree traversal. It covers the next inode numbers to come * (somewhere between 1 and ISO_USED_INODE_RANGE which is quite many) * and advances when being exhausted. * @param image The image where the number shall be used * @param flag bit0= reset count (Caution: image must get new inos then) * @return * Since 0 is used as default and considered self-unique, * the value 0 should only be returned in case of error. */ uint32_t img_give_ino_number(IsoImage *image, int flag) { int ret; uint64_t new_ino, ino_idx; static uint64_t limit = 0xffffffff; if (flag & 1) { image->inode_counter = 0; if (image->used_inodes != NULL) free(image->used_inodes); image->used_inodes = NULL; image->used_inodes_start = 0; } new_ino = ((uint64_t) image->inode_counter) + 1; if (image->used_inodes == NULL) { if (new_ino > 0 && new_ino <= limit) { image->inode_counter = (uint32_t) new_ino; return image->inode_counter; } } /* Look for free number in used territory */ while (1) { if (new_ino <= 0 || new_ino > limit || new_ino >= image->used_inodes_start + ISO_USED_INODE_RANGE ) { /* Collect a bitmap of used inode numbers ahead */ image->used_inodes_start += ISO_USED_INODE_RANGE; if (image->used_inodes_start > 0xffffffff || image->used_inodes_start <= 0) image->used_inodes_start = 0; ret = img_collect_inos(image, NULL, 0); if (ret < 0) goto return_result; /* >>> need error return value */ new_ino = image->used_inodes_start + !image->used_inodes_start; } ino_idx = (new_ino - image->used_inodes_start) / 8; if (!(image->used_inodes[ino_idx] & (1 << (new_ino % 8)))) { image->used_inodes[ino_idx] |= (1 << (new_ino % 8)); break; } new_ino++; } return_result:; image->inode_counter = new_ino; return image->inode_counter; } /* @param flag bit0= overwrite any ino, else only ino == 0 bit1= install inode with non-data, non-directory files bit2= install inode with directories */ static int img_update_ino(IsoImage *image, IsoNode *node, int flag) { int ret; ino_t ino; unsigned int fs_id; dev_t dev_id; ret = iso_node_get_id(node, &fs_id, &dev_id, &ino, 1); if (ret < 0) return ret; if (ret == 0) ino = 0; if (((flag & 1) || ino == 0) && (iso_node_get_type(node) == LIBISO_FILE || (flag & (2 | 4))) && ((flag & 4) || iso_node_get_type(node) != LIBISO_DIR)) { ret = iso_node_set_unique_id(node, image, 0); if (ret < 0) return ret; } return 1; } /* @param flag bit0= overwrite any ino, else only ino == 0 bit1= install inode with non-data, non-directory files bit2= install inode with directories bit3= with bit2: install inode on parameter dir */ int img_make_inos(IsoImage *image, IsoDir *dir, int flag) { int ret; IsoDirIter *iter = NULL; IsoNode *node; IsoDir *subdir; if (flag & 8) { node = (IsoNode *) dir; ret = img_update_ino(image, node, flag & 7); if (ret < 0) goto ex; } ret = iso_dir_get_children(dir, &iter); if (ret < 0) return ret; while (iso_dir_iter_next(iter, &node) == 1) { ret = img_update_ino(image, node, flag & 7); if (ret < 0) goto ex; if (iso_node_get_type(node) == LIBISO_DIR) { subdir = (IsoDir *) node; ret = img_make_inos(image, subdir, flag & ~8); if (ret < 0) goto ex; } } ret = 1; ex:; if (iter != NULL) iso_dir_iter_free(iter); return ret; } /* API */ int iso_image_get_session_md5(IsoImage *image, uint32_t *start_lba, uint32_t *end_lba, char md5[16], int flag) { if (image->checksum_array == NULL || image->checksum_idx_count < 1) return 0; *start_lba = image->checksum_start_lba; *end_lba = image->checksum_end_lba; memcpy(md5, image->checksum_array, 16); return ISO_SUCCESS; } int iso_image_set_checksums(IsoImage *image, char *checksum_array, uint32_t start_lba, uint32_t end_lba, uint32_t idx_count, int flag) { iso_image_free_checksums(image, 0); image->checksum_array = checksum_array; image->checksum_start_lba = start_lba; image->checksum_end_lba = end_lba; image->checksum_idx_count = idx_count; return 1; } int iso_image_generator_is_running(IsoImage *image) { return image->generator_is_running; } /* API */ int iso_image_add_mips_boot_file(IsoImage *image, char *path, int flag) { if (image->num_mips_boot_files >= 15) return ISO_BOOT_TOO_MANY_MIPS; image->mips_boot_file_paths[image->num_mips_boot_files] = strdup(path); if (image->mips_boot_file_paths[image->num_mips_boot_files] == NULL) return ISO_OUT_OF_MEM; image->num_mips_boot_files++; return ISO_SUCCESS; } /* API */ int iso_image_get_mips_boot_files(IsoImage *image, char *paths[15], int flag) { int i; for (i = 0; i < image->num_mips_boot_files; i++) paths[i] = image->mips_boot_file_paths[i]; for (; i < 15; i++) paths[i] = NULL; return image->num_mips_boot_files; } /* API */ int iso_image_give_up_mips_boot(IsoImage *image, int flag) { int i; for (i = 0; i < image->num_mips_boot_files; i++) if (image->mips_boot_file_paths[i] != NULL) { free(image->mips_boot_file_paths[i]); image->mips_boot_file_paths[i] = NULL; } image->num_mips_boot_files = 0; return ISO_SUCCESS; } static void unset_blessing(IsoImage *img, unsigned int idx) { if (img->hfsplus_blessed[idx] != NULL) iso_node_unref(img->hfsplus_blessed[idx]); img->hfsplus_blessed[idx] = NULL; } /* API */ int iso_image_hfsplus_bless(IsoImage *img, enum IsoHfsplusBlessings blessing, IsoNode *node, int flag) { unsigned int i, ok = 0; if (flag & 2) { /* Delete any blessing */ for (i = 0; i < ISO_HFSPLUS_BLESS_MAX; i++) { if (img->hfsplus_blessed[i] == node || node == NULL) { unset_blessing(img, i); ok = 1; } } return ok; } if (blessing == ISO_HFSPLUS_BLESS_MAX) return ISO_WRONG_ARG_VALUE; if (flag & 1) { /* Delete a particular blessing */ if (img->hfsplus_blessed[blessing] == node || node == NULL) { unset_blessing(img, (unsigned int) blessing); return 1; } return 0; } if (node == NULL) { unset_blessing(img, (unsigned int) blessing); return 1; } /* No two hats on one node */ for (i = 0; i < ISO_HFSPLUS_BLESS_MAX && node != NULL; i++) if (i != blessing && img->hfsplus_blessed[i] == node) return 0; /* Enforce correct file type */ if (blessing == ISO_HFSPLUS_BLESS_INTEL_BOOTFILE) { if (node->type != LIBISO_FILE) return 0; } else { if (node->type != LIBISO_DIR) return 0; } unset_blessing(img, (unsigned int) blessing); img->hfsplus_blessed[blessing] = node; if (node != NULL) iso_node_ref(node); return 1; } /* API */ int iso_image_hfsplus_get_blessed(IsoImage *img, IsoNode ***blessed_nodes, int *bless_max, int flag) { *blessed_nodes = img->hfsplus_blessed; *bless_max = ISO_HFSPLUS_BLESS_MAX; return 1; } /* API */ int iso_image_set_sparc_core(IsoImage *img, IsoFile *sparc_core, int flag) { if (img->sparc_core_node != NULL) iso_node_unref((IsoNode *) img->sparc_core_node); img->sparc_core_node = sparc_core; if (sparc_core != NULL) iso_node_ref((IsoNode *) sparc_core); return 1; } /* API */ int iso_image_get_sparc_core(IsoImage *img, IsoFile **sparc_core, int flag) { *sparc_core = img->sparc_core_node; return 1; } /* @param flag bit0= Let NULL parameters free the corresponding image properties. Else only the non-NULL parameters of this call have an effect. */ static int hppa_palo_set_path(IsoImage *img, char *path, char **target, char *what, int flag) { int ret, err; IsoNode *node; IsoFile *file; if (path == NULL && !(flag & 1)) return ISO_SUCCESS; if (iso_clone_mgtd_mem(path, target, 0) < 0) return ISO_OUT_OF_MEM; if (path == NULL) return ISO_SUCCESS; ret = iso_tree_path_to_node(img, path, &node); if (ret < 0) return ret; if (ret == 0) { iso_msg_submit(img->id, ISO_BOOT_FILE_MISSING, 0, "Cannot find in ISO image: %s file '%s'", what, path); return ISO_BOOT_FILE_MISSING; } if (iso_node_get_type(node) != LIBISO_FILE) { err = ISO_HPPA_PALO_NOTREG; if (strncmp(what, "DEC Alpha", 9) == 0) err = ISO_ALPHA_BOOT_NOTREG; iso_msg_submit(img->id, err, 0, "%s file is not a data file: '%s'", what, path); return err; } file = (IsoFile *) node; if (!(file->explicit_weight || file->from_old_session)) file->sort_weight = 2; return ISO_SUCCESS; } /* API */ /* @param flag Bitfield for control purposes bit0= Let NULL parameters free the corresponding image properties. Else only the non-NULL parameters of this call have an effect. */ int iso_image_set_hppa_palo(IsoImage *img, char *cmdline, char *bootloader, char *kernel_32, char *kernel_64, char *ramdisk, int flag) { int ret; static char *what = "HP-PA PALO"; if (cmdline != NULL || (flag & 1)) if (iso_clone_mgtd_mem(cmdline, &(img->hppa_cmdline), 0) < 0) return ISO_OUT_OF_MEM; ret = hppa_palo_set_path(img, bootloader, &(img->hppa_bootloader), what, flag & 1); if (ret < 0) return ret; ret = hppa_palo_set_path(img, kernel_32, &(img->hppa_kernel_32), what, flag & 1); if (ret < 0) return ret; ret = hppa_palo_set_path(img, kernel_64, &(img->hppa_kernel_64), what, flag & 1); if (ret < 0) return ret; ret = hppa_palo_set_path(img, ramdisk, &(img->hppa_ramdisk), what, flag & 1); if (ret < 0) return ret; return ISO_SUCCESS; } /* API */ int iso_image_get_hppa_palo(IsoImage *img, char **cmdline, char **bootloader, char **kernel_32, char **kernel_64, char **ramdisk) { *cmdline = img->hppa_cmdline; *bootloader = img->hppa_bootloader; *kernel_32 = img->hppa_kernel_32; *kernel_64 = img->hppa_kernel_64; *ramdisk = img->hppa_ramdisk; return ISO_SUCCESS; } /* API */ int iso_image_set_alpha_boot(IsoImage *img, char *boot_loader_path, int flag) { int ret; ret = hppa_palo_set_path(img, boot_loader_path, &(img->alpha_boot_image), "DEC Alpha Bootloader", 1); if (ret < 0) return ret; return ISO_SUCCESS; } /* API */ int iso_image_get_alpha_boot(IsoImage *img, char **boot_loader_path) { *boot_loader_path = img->alpha_boot_image; return ISO_SUCCESS; } /* API */ int iso_image_set_truncate_mode(IsoImage *img, int mode, int length) { if (mode < 0 || mode > 1) return ISO_WRONG_ARG_VALUE; if (length < 64 || length > LIBISOFS_NODE_NAME_MAX) return ISO_WRONG_ARG_VALUE; img->truncate_mode = mode; img->truncate_length = length; return ISO_SUCCESS; } /* API */ int iso_image_get_truncate_mode(IsoImage *img, int *mode, int *length) { *mode = img->truncate_mode; *length = img->truncate_length; return ISO_SUCCESS; } /* Warning: Not thread-safe */ int iso_image_truncate_name(IsoImage *image, const char *name, char **namept, int flag) { int ret; if (name == NULL) return ISO_NULL_POINTER; if ((int) strlen(name) <= image->truncate_length) { *namept = (char *) name; return ISO_SUCCESS; } *namept = image->truncate_buffer; if (name != image->truncate_buffer) strncpy(image->truncate_buffer, name, 4095); image->truncate_buffer[4095] = 0; ret = iso_truncate_rr_name(image->truncate_mode, image->truncate_length, image->truncate_buffer, 0); return ret; } /* API */ int iso_image_was_blind_attrs(IsoImage *image, int flag) { int ret; if (image == NULL) return ISO_NULL_POINTER; ret = image->blind_on_local_get_attrs; if (flag & 1) image->blind_on_local_get_attrs = 0; return ret; } /* * @param flag bit0= recursion is active */ static int iso_dir_zisofs_discard_bpt(IsoDir *dir, int flag) { int ret; IsoDirIter *iter = NULL; IsoNode *node; IsoDir *subdir; IsoFile *file; IsoStream *stream; ret = iso_dir_get_children(dir, &iter); if (ret < 0) return ret; while (iso_dir_iter_next(iter, &node) == 1) { if (iso_node_get_type(node) == LIBISO_DIR) { subdir = (IsoDir *) node; ret = iso_dir_zisofs_discard_bpt(subdir, flag | 1); if (ret < 0) goto ex; continue; } if (iso_node_get_type(node) != LIBISO_FILE) continue; file = (IsoFile *) node; stream = iso_file_get_stream(file); if (stream == NULL) continue; ret = iso_stream_zisofs_discard_bpt(stream, 0); if (ret < 0) goto ex; } ret = ISO_SUCCESS; ex:; if (iter != NULL) iso_dir_iter_free(iter); return ret; } /* API */ int iso_image_zisofs_discard_bpt(IsoImage *image, int flag) { int ret; IsoDir *dir; if (image == NULL) return ISO_NULL_POINTER; dir = image->root; if (dir == NULL) return ISO_SUCCESS; ret = iso_dir_zisofs_discard_bpt(dir, 0); return ret; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_IMAGE_H_ #define LIBISO_IMAGE_H_ #include "libisofs.h" #include "node.h" #include "fsource.h" #include "builder.h" /* Size of a inode recycling window. Each new window causes a tree traversal. Window memory consumption is ISO_USED_INODE_RANGE / 8. This must be a power of 2 smaller than 30 bit and larger than 8 bit. Here: 32 kB memory for 256k inodes. */ #define ISO_USED_INODE_RANGE (1 << 18) /* How many warnings to issue about name collisions during iso_image_import() */ #define ISO_IMPORT_COLL_WARN_MAX 10 /* * Image is a context for image manipulation. * Global objects such as the message_queues must belogn to that * context. Thus we will have, for example, a msg queue per image, * so images are completely independent and can be managed together. * (Useful, for example, in Multiple-Document-Interface GUI apps. * [The stuff we have in init belongs really to image!] */ struct Iso_Image { int refcount; IsoDir *root; char *volset_id; char *volume_id; /**< Volume identifier. */ char *publisher_id; /**< Volume publisher. */ char *data_preparer_id; /**< Volume data preparer. */ char *system_id; /**< Volume system identifier. */ char *application_id; /**< Volume application id */ char *copyright_file_id; char *abstract_file_id; char *biblio_file_id; char *creation_time; char *modification_time; char *expiration_time; char *effective_time; char application_use[512]; /* el-torito boot catalog */ struct el_torito_boot_catalog *bootcat; /* Eventually loaded system area data, or NULL */ char *system_area_data; /* Prescribed/detected options, see iso_write_opts_set_system_area() */ /* >>> Needs to be coordinated with .imported_sa_info->system_area_options */ int system_area_options; /* * Up to 15 boot files can be referred by a MIPS Big Endian Volume Header. The mips_boot_file_paths are ISO 9660 Rock Ridge paths. */ int num_mips_boot_files; char *mips_boot_file_paths[15]; /* ISO 9660 Rock Ridge Paths */ /* A data file of which the position and size shall be written after a SUN Disk Label. */ IsoFile *sparc_core_node; /* * Parameters for HP-PA PALO boot sector. cmdline is a string. The other * four are absolute paths to data files in the ISO image. */ char *hppa_cmdline; char *hppa_bootloader; char *hppa_kernel_32; char *hppa_kernel_64; char *hppa_ramdisk; /* Absolute DEC Alpha boot image path in the ISO image */ char *alpha_boot_image; /* image identifier, for message origin identifier */ int id; /** * Default filesystem to use when adding files to the image tree. */ IsoFilesystem *fs; /** * Block storage of imported ISO if demanded by IsoReadOpts. */ IsoDataSource *import_src; /* * Default builder to use when adding files to the image tree. */ IsoNodeBuilder *builder; /** * Whether to follow symlinks or just add them as symlinks */ unsigned int follow_symlinks : 1; /** * Whether to skip hidden files */ unsigned int ignore_hidden : 1; /** * Flags that determine what special files should be ignore. It is a * bitmask: * bit0: ignore FIFOs * bit1: ignore Sockets * bit2: ignore char devices * bit3: ignore block devices */ int ignore_special; /** * Whether to ignore ACL when inserting nodes into the image. * Not in effect with loading a complete ISO image but only with image * manipulation. */ unsigned int builder_ignore_acl : 1; /** * Whether to ignore EAs when inserting nodes into the image. * Not in effect with loading a complete ISO image but only with image * manipulation. ACL does not count as EA. */ unsigned int builder_ignore_ea : 1; /** * Whether to ignore Linux style file attribute flags (chattr). * Not in effect with loading a complete ISO image but only with image * manipulation. */ unsigned int builder_ignore_lfa_flags : 1; /** * Whether to ignore Linux style file attribute flags (chattr) which are * only readable but non-settable ("ZEIheVN"). * Not in effect with loading a complete ISO image but only with image * manipulation. */ unsigned int builder_ignore_ro_lfa_flags : 1; /** * If not builder_ignore_ea : import all xattr namespaces from local * filesystem, not only "user. */ unsigned int builder_take_all_ea : 1; /** * Files to exclude. Wildcard support is included. */ char** excludes; int nexcludes; /** * if the dir already contains a node with the same name, whether to * replace or not the old node with the new. */ enum iso_replace_mode replace; /* TODO enum iso_replace_mode (*confirm_replace)(IsoFileSource *src, IsoNode *node); */ /** * What to do in case of name longer than truncate_length: * 0= throw FAILURE * 1= truncate to truncate_length with MD5 of whole name at end */ int truncate_mode; int truncate_length; /** * This is a convenience buffer for name truncation during image * manipulation where libisofs is not thread-safe anyway. */ char truncate_buffer[4096]; /** * When this is not NULL, it is a pointer to a function that will * be called just before a file will be added. You can control where * the file will be in fact added or ignored. * * @return * 1 add, 0 ignore, < 0 cancel */ int (*report)(IsoImage *image, IsoFileSource *src); /** * User supplied data */ void *user_data; void (*user_data_free)(void *ptr); /** * Inode number management. inode_counter is taken over from * IsoImageFilesystem._ImageFsData after image import. * It is to be used with img_give_ino_number() * This is a Rock Ridge file serial number. Thus 32 bit. */ uint32_t inode_counter; /* * A bitmap of used inode numbers in an interval beginning at * used_inodes_start and holding ISO_USED_INODE_RANGE bits. * If a bit is set, then the corresponding inode number is occupied. * This interval is kept around inode_counter and eventually gets * advanced by ISO_USED_INODE_RANGE numbers in a tree traversal * done by img_collect_inos(). The value will stay in the 32 bit range, * although used_inodes_start is 64 bit to better handle rollovers. */ uint8_t *used_inodes; uint64_t used_inodes_start; /** * Array of MD5 checksums as announced by xattr "isofs.ca" of the * root node. Array element 0 contains an overall image checksum for the * block range checksum_start_lba,checksum_end_lba. Element size is * 16 bytes. IsoFile objects in the image may have xattr "isofs.cx" * which gives their index in checksum_array. */ uint32_t checksum_start_lba; uint32_t checksum_end_lba; uint32_t checksum_idx_count; char *checksum_array; /** * Whether a write run has been started by iso_image_create_burn_source() * and has not yet been finished. */ int generator_is_running; /* Pointers to directories or files which shall be get a HFS+ blessing. * libisofs/hfsplus.c et.al. will compare these pointers * with the ->node pointer of Ecma119Nodes. * See libisofs.h */ IsoNode *hfsplus_blessed[ISO_HFSPLUS_BLESS_MAX]; /* Counts the name collisions while iso_image_import() */ size_t collision_warnings; /* Contains the assessment of boot aspects of the loaded image */ struct iso_imported_sys_area *imported_sa_info; /* Whether some local filesystem xattr namespace could not be explored * during node building. */ int blind_on_local_get_attrs; /* Deeper tree inspection when reading an IsoImage assesses traces of the used write options. */ int do_deeper_tree_inspection; int tree_loaded; /* 0=ISO 9660/ECMA-119 1=Joliet 2=ISO 9660:1999 */ int rr_loaded; /* 0=plain ISO 9660/ECMA-119 1=Rock Ridge */ IsoWriteOpts *tree_compliance; }; /* Apply truncation mode to name, using image->truncate_buffer to perform truncation if needed. Warning: Not thread-safe ! */ int iso_image_truncate_name(IsoImage *image, const char *name, char **namept, int flag); /* Collect the bitmap of used inode numbers in the range of _ImageFsData.used_inodes_start + ISO_USED_INODE_RANGE @param flag bit0= recursion is active */ int img_collect_inos(IsoImage *image, IsoDir *dir, int flag); /** * A global counter for inode numbers for the ISO image filesystem. * On image import it gets maxed by the eventual inode numbers from PX * entries. Up to the first 32 bit rollover it simply increments the counter. * After the first rollover it uses a look ahead bitmap which gets filled * by a full tree traversal. It covers the next inode numbers to come * (somewhere between 1 and ISO_USED_INODE_RANGE which is quite many) * and advances when being exhausted. * @param image The image where the number shall be used * @param flag bit0= reset count (Caution: image must get new inos then) * @return * Since 0 is used as default and considered self-unique, * the value 0 should only be returned in case of error. */ uint32_t img_give_ino_number(IsoImage *image, int flag); /* @param flag bit0= overwrite any ino, else only ino == 0 bit1= install inode with non-data, non-directory files bit2= install inode with directories bit3= with bit2: install inode on parameter dir */ int img_make_inos(IsoImage *image, IsoDir *dir, int flag); /* Free the checksum array of an image and reset its layout parameters */ int iso_image_free_checksums(IsoImage *image, int flag); /* Equip an ISO image with a new checksum array buffer (after isofs.ca and isofs.cx have already been adjusted). */ int iso_image_set_checksums(IsoImage *image, char *checksum_array, uint32_t start_lba, uint32_t end_lba, uint32_t idx_count, int flag); int iso_image_set_pvd_times(IsoImage *image, char *creation_time, char *modification_time, char *expiration_time, char *effective_time); /* Collects boot block information obtained from the system area of imported images */ struct iso_imported_sys_area { int refcount; /* Whether there was some System Area data at all */ int is_not_zero; /* Giving the error number if the assessment ended by an error */ int overall_return; /* Block address of loaded Primar Volume Descriptor */ uint32_t pvd_block; /* Size of the imported ISO image */ uint32_t image_size; /* see libisofs.h : iso_write_opts_set_system_area() */ int system_area_options; /* The perceived MBR partitions */ struct iso_mbr_partition_request **mbr_req; int mbr_req_count; /* see ecma119.h : struct ecma119_image , struct iso_write_opts */ /* Effective partition table parameter: 1 to 63, 0= disabled/default */ int partition_secs_per_head; /* 1 to 255, 0= disabled/default */ int partition_heads_per_cyl; /* see ecma119.h : struct iso_write_opts */ uint32_t partition_offset; /* 2048-byte start LBA and block count of PreP partition */ uint32_t prep_part_start; uint32_t prep_part_size; /* see ecma119.h : struct ecma119_image */ struct iso_apm_partition_request **apm_req; int apm_req_count; int apm_req_flags; /* Number of found "GapNN", "ISO9660_data" partitions in APM */ int apm_gap_count; /* see ecma119.h : struct iso_write_opts */ int apm_block_size; /* >>> see ecma119.h : struct iso_write_opts */ int hfsp_block_size; /* see ecma119.h : struct ecma119_image */ struct iso_gpt_partition_request **gpt_req; int gpt_req_count; int gpt_req_flags; /* see ecma119.h : struct ecma119_image */ uint8_t gpt_disk_guid[16]; /* Start of GPT entries in System Area, block size 512 */ uint64_t gpt_part_start; uint32_t gpt_max_entries; uint64_t gpt_first_lba; uint64_t gpt_last_lba; uint64_t gpt_backup_lba; char *gpt_backup_comments; uint32_t gpt_head_crc_found; uint32_t gpt_head_crc_should; uint32_t gpt_array_crc_found; uint32_t gpt_array_crc_should; /* see image.h : struct Iso_Image */ int num_mips_boot_files; char **mips_boot_file_paths; /* ISO 9660 Rock Ridge Paths */ struct iso_mips_voldir_entry **mips_vd_entries; /* see ecma119.h : struct ecma119_image */ /* Memorized ELF parameters from MIPS Little Endian boot file */ uint32_t mipsel_e_entry; uint32_t mipsel_p_offset; uint32_t mipsel_p_vaddr; uint32_t mipsel_p_filesz; uint32_t mipsel_seg_start; char *mipsel_boot_file_path; /* see image.h : struct Iso_Image */ char *sparc_disc_label; int sparc_secs_per_head; int sparc_heads_per_cyl; struct iso_sun_disk_label_entry *sparc_entries; int sparc_entry_count; /* grub2-sparc-core : a node in the ISO image published at bytes 0x228 to 0x233 */ uint64_t sparc_grub2_core_adr; uint32_t sparc_grub2_core_size; IsoFile *sparc_core_node; /* Only for representing the imported ISO: Path of file which held the partition content. NULL = no such file */ char *sparc_core_node_path; /* see image.h : struct Iso_Image */ int hppa_hdrversion; char *hppa_cmdline; uint32_t hppa_kern32_adr; uint32_t hppa_kern32_len; uint32_t hppa_kern64_adr; uint32_t hppa_kern64_len; uint32_t hppa_ramdisk_adr; uint32_t hppa_ramdisk_len; uint32_t hppa_bootloader_adr; uint32_t hppa_bootloader_len; uint32_t hppa_ipl_entry; char *hppa_kernel_32; char *hppa_kernel_64; char *hppa_ramdisk; char *hppa_bootloader; uint64_t alpha_boot_image_size; uint64_t alpha_boot_image_adr; char *alpha_boot_image; /* Some block addresses of active and first session: PVD, L Pathtable, Opt L, M Pathtable, Opt M, root directory */ uint32_t meta_struct_blocks[12]; int num_meta_struct_blocks; }; int iso_imported_sa_new(struct iso_imported_sys_area **sa_info, int flag); int iso_imported_sa_unref(struct iso_imported_sys_area **sa_info, int flag); void iso_image_assess_ecma119_name(IsoImage *image, struct stat *info, char *path, char *name); void iso_image_assess_joliet_name(IsoImage *image, struct stat *info, char *path, char *name); #endif /*LIBISO_IMAGE_H_*/ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2011-2014 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "iso1999.h" #include "messages.h" #include "writer.h" #include "image.h" #include "filesrc.h" #include "eltorito.h" #include "util.h" #include "ecma119.h" #include #include #include static int get_iso1999_name(Ecma119Image *t, const char *str, char **fname) { int ret; char *name; if (fname == NULL) { return ISO_ASSERT_FAILURE; } if (str == NULL) { /* not an error, can be root node */ *fname = NULL; return ISO_SUCCESS; } if (!strcmp(t->input_charset, t->output_charset)) { /* no conversion needed */ name = strdup(str); } else { ret = strconv(str, t->input_charset, t->output_charset, &name); if (ret < 0) { ret = iso_msg_submit(t->image->id, ISO_FILENAME_WRONG_CHARSET, ret, "Charset conversion error. Can't convert %s from %s to %s", str, t->input_charset, t->output_charset); if (ret < 0) { return ret; /* aborted */ } /* use the original name, it's the best we can do */ name = strdup(str); } } /* ISO 9660:1999 7.5.1 */ if (strlen(name) > 207) { name[207] = '\0'; } *fname = name; return ISO_SUCCESS; } static void iso1999_node_free(Iso1999Node *node) { if (node == NULL) { return; } if (node->type == ISO1999_DIR) { size_t i; for (i = 0; i < node->info.dir->nchildren; i++) { iso1999_node_free(node->info.dir->children[i]); } if (node->info.dir->children != NULL) free(node->info.dir->children); free(node->info.dir); } iso_node_unref(node->node); free(node->name); free(node); } /** * Create a low level ISO 9660:1999 node * @return * 1 success, 0 ignored, < 0 error */ static int create_node(Ecma119Image *t, IsoNode *iso, Iso1999Node **node) { int ret; Iso1999Node *n; n = calloc(1, sizeof(Iso1999Node)); if (n == NULL) { return ISO_OUT_OF_MEM; } if (iso->type == LIBISO_DIR) { IsoDir *dir = (IsoDir*) iso; n->info.dir = calloc(1, sizeof(struct iso1999_dir_info)); if (n->info.dir == NULL) { free(n); return ISO_OUT_OF_MEM; } n->info.dir->children = NULL; if (dir->nchildren > 0) { n->info.dir->children = calloc(sizeof(void*), dir->nchildren); if (n->info.dir->children == NULL) { free(n->info.dir); free(n); return ISO_OUT_OF_MEM; } } n->type = ISO1999_DIR; } else if (iso->type == LIBISO_FILE) { /* it's a file */ off_t size; IsoFileSrc *src; IsoFile *file = (IsoFile*) iso; size = iso_stream_get_size(file->stream); if (size > (off_t)MAX_ISO_FILE_SECTION_SIZE && t->opts->iso_level != 3) { char *ipath = iso_tree_get_node_path(iso); ret = iso_msg_submit(t->image->id, ISO_FILE_TOO_BIG, 0, "File \"%s\" can't be added to image because is " "greater than 4GB", ipath); free(n); free(ipath); return ret; } ret = iso_file_src_create(t, file, &src); if (ret < 0) { free(n); return ret; } n->info.file = src; n->type = ISO1999_FILE; } else if (iso->type == LIBISO_BOOT) { /* it's a el-torito boot catalog, that we write as a file */ IsoFileSrc *src; ret = el_torito_catalog_file_src_create(t, &src); if (ret < 0) { free(n); return ret; } n->info.file = src; n->type = ISO1999_FILE; } else { /* should never happen */ free(n); return ISO_ASSERT_FAILURE; } /* take a ref to the IsoNode */ n->node = iso; iso_node_ref(iso); *node = n; return ISO_SUCCESS; } /** * Create the low level ISO 9660:1999 tree from the high level ISO tree. * * @return * 1 success, 0 file ignored, < 0 error */ static int create_tree(Ecma119Image *t, IsoNode *iso, Iso1999Node **tree, int pathlen) { int ret, max_path; Iso1999Node *node = NULL; char *iso_name = NULL; if (t == NULL || iso == NULL || tree == NULL) { return ISO_NULL_POINTER; } if (iso->hidden & LIBISO_HIDE_ON_1999) { /* file will be ignored */ return 0; } ret = get_iso1999_name(t, iso->name, &iso_name); if (ret < 0) { return ret; } max_path = pathlen + 1 + (iso_name ? strlen(iso_name): 0); if (!t->opts->allow_longer_paths && max_path > 255) { char *ipath = iso_tree_get_node_path(iso); ret = iso_msg_submit(t->image->id, ISO_FILE_IMGPATH_WRONG, 0, "File \"%s\" can't be added to ISO 9660:1999 tree, " "because its path length is larger than 255", ipath); free(iso_name); free(ipath); return ret; } switch (iso->type) { case LIBISO_FILE: ret = create_node(t, iso, &node); break; case LIBISO_DIR: { IsoNode *pos; IsoDir *dir = (IsoDir*)iso; ret = create_node(t, iso, &node); if (ret < 0) { free(iso_name); return ret; } pos = dir->children; while (pos) { int cret; Iso1999Node *child; cret = create_tree(t, pos, &child, max_path); if (cret < 0) { /* error */ iso1999_node_free(node); ret = cret; break; } else if (cret == ISO_SUCCESS) { /* add child to this node */ int nchildren = node->info.dir->nchildren++; node->info.dir->children[nchildren] = child; child->parent = node; } pos = pos->next; } } break; case LIBISO_BOOT: if (t->eltorito) { ret = create_node(t, iso, &node); } else { /* log and ignore */ ret = iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, "El-Torito catalog found on a image without El-Torito."); } break; case LIBISO_SYMLINK: t->iso1999_symlinks++; if (t->iso1999_symlinks == 1) { char *ipath = iso_tree_get_node_path(iso); ret = iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, "Cannot add symbolic link %s to ISO 9660:1999 tree. Skipping.", ipath); free(ipath); } else { if (t->iso1999_symlinks == 2) iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, "More symbolic links were omitted from ISO 9660:1999 tree."); ret = 0; } break; case LIBISO_SPECIAL: t->iso1999_specials++; if (t->iso1999_specials == 1) { char *ipath = iso_tree_get_node_path(iso); ret = iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, "Cannot add special file %s to ISO 9660:1999 tree. Skipping.", ipath); free(ipath); } else { if (t->iso1999_specials == 2) iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, "More special files were omitted from ISO 9660:1999 tree."); ret = 0; } break; default: /* should never happen */ return ISO_ASSERT_FAILURE; } if (ret <= 0) { free(iso_name); return ret; } node->name = iso_name; *tree = node; return ISO_SUCCESS; } static int cmp_node(const void *f1, const void *f2) { Iso1999Node *f = *((Iso1999Node**)f1); Iso1999Node *g = *((Iso1999Node**)f2); /** * TODO #00027 Follow ISO 9660:1999 specs when sorting files * strcmp do not does exactly what ISO 9660:1999, 9.3, as characters * < 0x20 " " are allowed, so name len must be taken into account */ return strcmp(f->name, g->name); } /** * Sort the entries inside an ISO 9660:1999 directory, according to * ISO 9660:1999, 9.3 */ static void sort_tree(Iso1999Node *root) { size_t i; if (root->info.dir->children == NULL) return; qsort(root->info.dir->children, root->info.dir->nchildren, sizeof(void*), cmp_node); for (i = 0; i < root->info.dir->nchildren; i++) { Iso1999Node *child = root->info.dir->children[i]; if (child->type == ISO1999_DIR) sort_tree(child); } } static int mangle_single_dir(Ecma119Image *img, Iso1999Node *dir) { int ret; int i, nchildren; Iso1999Node **children; IsoHTable *table = NULL; int need_sort = 0; char *full_name = NULL, *tmp = NULL; nchildren = dir->info.dir->nchildren; if (nchildren <= 0) { ret = ISO_SUCCESS; goto ex; } children = dir->info.dir->children; LIBISO_ALLOC_MEM(full_name, char, 208); LIBISO_ALLOC_MEM(tmp, char, 208); /* a hash table will temporary hold the names, for fast searching */ ret = iso_htable_create((nchildren * 100) / 80, iso_str_hash, (compare_function_t)strcmp, &table); if (ret < 0) { goto ex; } for (i = 0; i < nchildren; ++i) { char *name = children[i]->name; ret = iso_htable_add(table, name, name); if (ret < 0) { goto ex; } } for (i = 0; i < nchildren; ++i) { char *name, *ext; int max; /* computed max len for name, without extension */ int j = i; int digits = 1; /* characters to change per name */ /* first, find all child with same name */ while (j + 1 < nchildren && !cmp_node(children + i, children + j + 1)) { ++j; } if (j == i) { /* name is unique */ continue; } /* * A max of 7 characters is good enough, it allows handling up to * 9,999,999 files with same name. */ while (digits < 8) { int ok, k; char *dot; int change = 0; /* number to be written */ /* copy name to buffer */ strcpy(full_name, children[i]->name); /* compute name and extension */ dot = strrchr(full_name, '.'); if (dot != NULL && children[i]->type != ISO1999_DIR) { /* * File (not dir) with extension. */ int extlen; full_name[dot - full_name] = '\0'; name = full_name; ext = dot + 1; extlen = strlen(ext); max = 207 - extlen - 1 - digits; if (max <= 0) { /* this can happen if extension is too long */ if (extlen + max > 3) { /* * reduce extension len, to give name an extra char * note that max is negative or 0 */ extlen = extlen + max - 1; ext[extlen] = '\0'; max = 207 - extlen - 1 - digits; } else { /* * error, we don't support extensions < 3 * This can't happen with current limit of digits. */ ret = ISO_ERROR; goto ex; } } /* ok, reduce name by digits */ if (name + max < dot) { name[max] = '\0'; } } else { /* Directory, or file without extension */ if (children[i]->type == ISO1999_DIR) { dot = NULL; /* dots have no meaning in dirs */ } max = 207 - digits; name = full_name; if ((size_t) max < strlen(name)) { name[max] = '\0'; } /* let ext be an empty string */ ext = name + strlen(name); } ok = 1; /* change name of each file */ for (k = i; k <= j; ++k) { char fmt[16]; if (dot != NULL) { sprintf(fmt, "%%s%%0%dd.%%s", digits); } else { sprintf(fmt, "%%s%%0%dd%%s", digits); } while (1) { sprintf(tmp, fmt, name, change, ext); ++change; if (change > int_pow(10, digits)) { ok = 0; break; } if (!iso_htable_get(table, tmp, NULL)) { /* the name is unique, so it can be used */ break; } } if (ok) { char *new = strdup(tmp); if (new == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } iso_msg_debug(img->image->id, "\"%s\" renamed to \"%s\"", children[k]->name, new); iso_htable_remove_ptr(table, children[k]->name, NULL); free(children[k]->name); children[k]->name = new; iso_htable_add(table, new, new); /* * if we change a name we need to sort again children * at the end */ need_sort = 1; } else { /* we need to increment digits */ break; } } if (ok) { break; } else { ++digits; } } if (digits == 8) { ret = ISO_MANGLE_TOO_MUCH_FILES; goto ex; } i = j; } /* * If needed, sort again the files inside dir */ if (need_sort) { qsort(children, nchildren, sizeof(void*), cmp_node); } ret = ISO_SUCCESS; ex:; iso_htable_destroy(table, NULL); LIBISO_FREE_MEM(tmp); LIBISO_FREE_MEM(full_name); return ret; } static int mangle_tree(Ecma119Image *t, Iso1999Node *dir) { int ret; size_t i; ret = mangle_single_dir(t, dir); if (ret < 0) { return ret; } /* recurse */ for (i = 0; i < dir->info.dir->nchildren; ++i) { if (dir->info.dir->children[i]->type == ISO1999_DIR) { ret = mangle_tree(t, dir->info.dir->children[i]); if (ret < 0) { /* error */ return ret; } } } return ISO_SUCCESS; } static int iso1999_tree_create(Ecma119Image *t) { int ret; Iso1999Node *root; if (t == NULL) { return ISO_NULL_POINTER; } ret = create_tree(t, (IsoNode*)t->image->root, &root, 0); if (ret <= 0) { if (ret == 0) { /* unexpected error, root ignored!! This can't happen */ ret = ISO_ASSERT_FAILURE; } return ret; } /* the ISO 9660:1999 tree is stored in Ecma119Image target */ t->iso1999_root = root; iso_msg_debug(t->image->id, "Sorting the ISO 9660:1999 tree..."); sort_tree(root); iso_msg_debug(t->image->id, "Mangling ISO 9660:1999 names..."); ret = mangle_tree(t, t->iso1999_root); if (ret < 0) { return ret; } return ISO_SUCCESS; } /** * Compute the size of a directory entry for a single node */ static size_t calc_dirent_len(Ecma119Image *t, Iso1999Node *n) { int ret = n->name ? strlen(n->name) + 33 : 34; if (ret % 2) ret++; return ret; } /** * Computes the total size of all directory entries of a single dir, as * stated in ISO 9660:1999, 6.8.1.3 */ static size_t calc_dir_size(Ecma119Image *t, Iso1999Node *dir) { size_t i, len; /* size of "." and ".." entries */ len = 34 + 34; for (i = 0; i < dir->info.dir->nchildren; ++i) { size_t remaining; int section, nsections; Iso1999Node *child = dir->info.dir->children[i]; size_t dirent_len = calc_dirent_len(t, child); nsections = (child->type == ISO1999_FILE) ? child->info.file->nsections : 1; for (section = 0; section < nsections; ++section) { remaining = BLOCK_SIZE - (len % BLOCK_SIZE); if (dirent_len > remaining) { /* child directory entry doesn't fit on block */ len += remaining + dirent_len; } else { len += dirent_len; } } } /* * The size of a dir is always a multiple of block size, as we must add * the size of the unused space after the last directory record * (ISO 9660:1999, 6.8.1.3) */ len = ROUND_UP(len, BLOCK_SIZE); /* cache the len */ dir->info.dir->len = len; return len; } static void calc_dir_pos(Ecma119Image *t, Iso1999Node *dir) { size_t i, len; t->iso1999_ndirs++; dir->info.dir->block = t->curblock; len = calc_dir_size(t, dir); t->curblock += DIV_UP(len, BLOCK_SIZE); for (i = 0; i < dir->info.dir->nchildren; i++) { Iso1999Node *child = dir->info.dir->children[i]; if (child->type == ISO1999_DIR) { calc_dir_pos(t, child); } } } /** * Compute the length of the path table (ISO 9660:1999, 6.9), in bytes. */ static uint32_t calc_path_table_size(Iso1999Node *dir) { uint32_t size; size_t i; /* size of path table for this entry */ size = 8; size += dir->name ? strlen(dir->name) : 2; size += (size % 2); /* and recurse */ for (i = 0; i < dir->info.dir->nchildren; i++) { Iso1999Node *child = dir->info.dir->children[i]; if (child->type == ISO1999_DIR) { size += calc_path_table_size(child); } } return size; } static int iso1999_writer_compute_data_blocks(IsoImageWriter *writer) { Ecma119Image *t; uint32_t path_table_size; if (writer == NULL) { return ISO_OUT_OF_MEM; } t = writer->target; /* compute position of directories */ iso_msg_debug(t->image->id, "Computing position of ISO 9660:1999 dir structure"); t->iso1999_ndirs = 0; calc_dir_pos(t, t->iso1999_root); /* compute length of pathlist */ iso_msg_debug(t->image->id, "Computing length of ISO 9660:1999 pathlist"); path_table_size = calc_path_table_size(t->iso1999_root); /* compute location for path tables */ t->iso1999_l_path_table_pos = t->curblock; t->curblock += DIV_UP(path_table_size, BLOCK_SIZE); t->iso1999_m_path_table_pos = t->curblock; t->curblock += DIV_UP(path_table_size, BLOCK_SIZE); t->iso1999_path_table_size = path_table_size; return ISO_SUCCESS; } /** * Write a single directory record (ISO 9660:1999, 9.1). * * @param file_id * if >= 0, we use it instead of the filename (for "." and ".." entries). * @param len_fi * Computed length of the file identifier. */ static void write_one_dir_record(Ecma119Image *t, Iso1999Node *node, int file_id, uint8_t *buf, size_t len_fi, int extent) { uint32_t len; uint32_t block; uint8_t len_dr; /*< size of dir entry */ int multi_extend = 0; uint8_t *name = (file_id >= 0) ? (uint8_t*)&file_id : (uint8_t*)node->name; struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; IsoNode *iso; len_dr = 33 + len_fi + ((len_fi % 2) ? 0 : 1); memcpy(rec->file_id, name, len_fi); if (node->type == ISO1999_DIR) { /* use the cached length */ len = node->info.dir->len; block = node->info.dir->block; } else if (node->type == ISO1999_FILE) { block = node->info.file->sections[extent].block; len = node->info.file->sections[extent].size; multi_extend = (node->info.file->nsections - 1 == extent) ? 0 : 1; } else { /* * for nodes other than files and dirs, we set both * len and block to 0 */ len = 0; block = 0; } /* * For ".." entry we need to write the parent info! */ if (file_id == 1 && node->parent) node = node->parent; rec->len_dr[0] = len_dr; iso_bb(rec->block, block, 4); iso_bb(rec->length, len, 4); /* was: iso_datetime_7(rec->recording_time, t->now, t->opts->always_gmt); */ iso= node->node; iso_datetime_7(rec->recording_time, (t->opts->dir_rec_mtime & 4) ? ( t->replace_timestamps ? t->timestamp : iso->mtime ) : t->now, t->opts->always_gmt); rec->flags[0] = ((node->type == ISO1999_DIR) ? 2 : 0) | (multi_extend ? 0x80 : 0); iso_bb(rec->vol_seq_number, (uint32_t) 1, 2); rec->len_fi[0] = len_fi; } /** * Write the enhanced volume descriptor (ISO/IEC 9660:1999, 8.5) */ static int iso1999_writer_write_vol_desc(IsoImageWriter *writer) { IsoImage *image; Ecma119Image *t; /* The enhanced volume descriptor is like the sup vol desc */ struct ecma119_sup_vol_desc vol; char *vol_id = NULL, *pub_id = NULL, *data_id = NULL; char *volset_id = NULL, *system_id = NULL, *application_id = NULL; char *copyright_file_id = NULL, *abstract_file_id = NULL; char *biblio_file_id = NULL; if (writer == NULL) { return ISO_OUT_OF_MEM; } t = writer->target; image = t->image; iso_msg_debug(image->id, "Write Enhanced Vol Desc (ISO 9660:1999)"); memset(&vol, 0, sizeof(struct ecma119_sup_vol_desc)); get_iso1999_name(t, image->volume_id, &vol_id); str2a_char(t->input_charset, image->publisher_id, &pub_id); str2a_char(t->input_charset, image->data_preparer_id, &data_id); get_iso1999_name(t, image->volset_id, &volset_id); str2a_char(t->input_charset, image->system_id, &system_id); str2a_char(t->input_charset, image->application_id, &application_id); get_iso1999_name(t, image->copyright_file_id, ©right_file_id); get_iso1999_name(t, image->abstract_file_id, &abstract_file_id); get_iso1999_name(t, image->biblio_file_id, &biblio_file_id); vol.vol_desc_type[0] = 2; memcpy(vol.std_identifier, "CD001", 5); /* descriptor version is 2 (ISO/IEC 9660:1999, 8.5.2) */ vol.vol_desc_version[0] = 2; strncpy_pad((char*)vol.volume_id, vol_id, 32); iso_bb(vol.vol_space_size, t->vol_space_size, 4); iso_bb(vol.vol_set_size, (uint32_t) 1, 2); iso_bb(vol.vol_seq_number, (uint32_t) 1, 2); iso_bb(vol.block_size, (uint32_t) BLOCK_SIZE, 2); iso_bb(vol.path_table_size, t->iso1999_path_table_size, 4); iso_lsb(vol.l_path_table_pos, t->iso1999_l_path_table_pos, 4); iso_msb(vol.m_path_table_pos, t->iso1999_m_path_table_pos, 4); write_one_dir_record(t, t->iso1999_root, 0, vol.root_dir_record, 1, 0); strncpy_pad((char*)vol.vol_set_id, volset_id, 128); strncpy_pad((char*)vol.publisher_id, pub_id, 128); strncpy_pad((char*)vol.data_prep_id, data_id, 128); strncpy_pad((char*)vol.system_id, system_id, 32); strncpy_pad((char*)vol.application_id, application_id, 128); strncpy_pad((char*)vol.copyright_file_id, copyright_file_id, 37); strncpy_pad((char*)vol.abstract_file_id, abstract_file_id, 37); strncpy_pad((char*)vol.bibliographic_file_id, biblio_file_id, 37); ecma119_set_voldescr_times(writer, (struct ecma119_pri_vol_desc *) &vol); vol.file_structure_version[0] = 2; free(vol_id); free(volset_id); free(pub_id); free(data_id); free(system_id); free(application_id); free(copyright_file_id); free(abstract_file_id); free(biblio_file_id); /* Finally write the Volume Descriptor */ return iso_write(t, &vol, sizeof(struct ecma119_sup_vol_desc)); } static int write_one_dir(Ecma119Image *t, Iso1999Node *dir) { int ret; uint8_t *buffer = NULL; size_t i; size_t fi_len, len; /* buf will point to current write position on buffer */ uint8_t *buf; LIBISO_ALLOC_MEM(buffer, uint8_t, BLOCK_SIZE); buf = buffer; /* write the "." and ".." entries first */ write_one_dir_record(t, dir, 0, buf, 1, 0); buf += 34; write_one_dir_record(t, dir, 1, buf, 1, 0); buf += 34; for (i = 0; i < dir->info.dir->nchildren; i++) { int section, nsections; Iso1999Node *child = dir->info.dir->children[i]; /* compute len of directory entry */ fi_len = strlen(child->name); len = fi_len + 33 + ((fi_len % 2) ? 0 : 1); nsections = (child->type == ISO1999_FILE) ? child->info.file->nsections : 1; for (section = 0; section < nsections; ++section) { if ( (buf + len - buffer) > BLOCK_SIZE) { /* dir doesn't fit in current block */ ret = iso_write(t, buffer, BLOCK_SIZE); if (ret < 0) { goto ex; } memset(buffer, 0, BLOCK_SIZE); buf = buffer; } /* write the directory entry in any case */ write_one_dir_record(t, child, -1, buf, fi_len, section); buf += len; } } /* write the last block */ ret = iso_write(t, buffer, BLOCK_SIZE); ex:; LIBISO_FREE_MEM(buffer); return ret; } static int write_dirs(Ecma119Image *t, Iso1999Node *root) { int ret; size_t i; /* write all directory entries for this dir */ ret = write_one_dir(t, root); if (ret < 0) { return ret; } /* recurse */ for (i = 0; i < root->info.dir->nchildren; i++) { Iso1999Node *child = root->info.dir->children[i]; if (child->type == ISO1999_DIR) { ret = write_dirs(t, child); if (ret < 0) { return ret; } } } return ISO_SUCCESS; } static int write_path_table(Ecma119Image *t, Iso1999Node **pathlist, int l_type) { size_t i, len; uint8_t *buf = NULL; struct ecma119_path_table_record *rec; void (*write_int)(uint8_t*, uint32_t, int); Iso1999Node *dir; uint32_t path_table_size; int parent = 0; int ret= ISO_SUCCESS; uint8_t *zeros = NULL; /* 256 is just a convenient size large enough */ LIBISO_ALLOC_MEM(buf, uint8_t, 256); path_table_size = 0; write_int = l_type ? iso_lsb : iso_msb; for (i = 0; i < t->iso1999_ndirs; i++) { dir = pathlist[i]; /* find the index of the parent in the table */ while ((i) && pathlist[parent] != dir->parent) { parent++; } /* write the Path Table Record (ECMA-119, 9.4) */ memset(buf, 0, 256); rec = (struct ecma119_path_table_record*) buf; rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->name) : 1; rec->len_xa[0] = 0; write_int(rec->block, dir->info.dir->block, 4); write_int(rec->parent, parent + 1, 2); if (dir->parent) { memcpy(rec->dir_id, dir->name, rec->len_di[0]); } len = 8 + rec->len_di[0] + (rec->len_di[0] % 2); ret = iso_write(t, buf, len); if (ret < 0) { /* error */ goto ex; } path_table_size += len; } /* we need to fill the last block with zeros */ path_table_size %= BLOCK_SIZE; if (path_table_size) { LIBISO_ALLOC_MEM(zeros, uint8_t, BLOCK_SIZE); len = BLOCK_SIZE - path_table_size; memset(zeros, 0, len); ret = iso_write(t, zeros, len); } ex:; LIBISO_FREE_MEM(zeros); LIBISO_FREE_MEM(buf); return ret; } static int write_path_tables(Ecma119Image *t) { int ret; size_t i, j, cur; Iso1999Node **pathlist; iso_msg_debug(t->image->id, "Writing ISO 9660:1999 Path tables"); /* allocate temporal pathlist */ pathlist = malloc(sizeof(void*) * t->iso1999_ndirs); if (pathlist == NULL) { return ISO_OUT_OF_MEM; } pathlist[0] = t->iso1999_root; cur = 1; for (i = 0; i < t->iso1999_ndirs; i++) { Iso1999Node *dir = pathlist[i]; for (j = 0; j < dir->info.dir->nchildren; j++) { Iso1999Node *child = dir->info.dir->children[j]; if (child->type == ISO1999_DIR) { pathlist[cur++] = child; } } } /* Write L Path Table */ ret = write_path_table(t, pathlist, 1); if (ret < 0) { goto write_path_tables_exit; } /* Write L Path Table */ ret = write_path_table(t, pathlist, 0); write_path_tables_exit: ; free(pathlist); return ret; } static int iso1999_writer_write_data(IsoImageWriter *writer) { int ret; Ecma119Image *t; if (writer == NULL) { return ISO_NULL_POINTER; } t = writer->target; /* first of all, we write the directory structure */ ret = write_dirs(t, t->iso1999_root); if (ret < 0) { return ret; } /* and write the path tables */ ret = write_path_tables(t); return ret; } static int iso1999_writer_free_data(IsoImageWriter *writer) { /* free the ISO 9660:1999 tree */ Ecma119Image *t = writer->target; iso1999_node_free(t->iso1999_root); return ISO_SUCCESS; } int iso1999_writer_create(Ecma119Image *target) { int ret; IsoImageWriter *writer; writer = malloc(sizeof(IsoImageWriter)); if (writer == NULL) { return ISO_OUT_OF_MEM; } writer->compute_data_blocks = iso1999_writer_compute_data_blocks; writer->write_vol_desc = iso1999_writer_write_vol_desc; writer->write_data = iso1999_writer_write_data; writer->free_data = iso1999_writer_free_data; writer->data = NULL; writer->target = target; iso_msg_debug(target->image->id, "Creating low level ISO 9660:1999 tree..."); ret = iso1999_tree_create(target); if (ret < 0) { free((char *) writer); return ret; } /* add this writer to image */ target->writers[target->nwriters++] = writer; /* we need the volume descriptor */ target->curblock++; return ISO_SUCCESS; } /* * Copyright (c) 2007 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /** * Structures related to ISO/IEC 9660:1999, that is version 2 of ISO-9660 * "See doc/devel/cookbook/ISO 9660-1999" and * ISO/IEC DIS 9660:1999(E) "Information processing. Volume and file structure * of CD­-ROM for Information Interchange" * for further details. */ #ifndef LIBISO_ISO1999_H #define LIBISO_ISO1999_H #include "libisofs.h" #include "ecma119.h" enum iso1999_node_type { ISO1999_FILE, ISO1999_DIR }; struct iso1999_dir_info { Iso1999Node **children; size_t nchildren; size_t len; size_t block; }; struct iso1999_node { char *name; /**< Name chosen output charset. */ Iso1999Node *parent; IsoNode *node; /*< reference to the iso node */ enum iso1999_node_type type; union { IsoFileSrc *file; struct iso1999_dir_info *dir; } info; }; /** * Create a IsoWriter to deal with ISO 9660:1999 estructures, and add it to * the given target. * * @return * 1 on success, < 0 on error */ int iso1999_writer_create(Ecma119Image *target); #endif /* LIBISO_ISO1999_H */ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2007 Mario Danic * Copyright (c) 2011-2018 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "joliet.h" #include "messages.h" #include "writer.h" #include "image.h" #include "filesrc.h" #include "eltorito.h" #include "libisofs.h" #include "util.h" #include "ecma119.h" #include #include #include /* @param flag bit0= Do not issue error messages */ int iso_get_joliet_name(IsoWriteOpts *opts, char *input_charset, int imgid, char *node_name, enum IsoNodeType node_type, size_t *joliet_ucs2_failures, uint16_t **name, int flag) { int ret = ISO_SUCCESS; uint16_t *ucs_name = NULL, *utf16_name = NULL; uint16_t *jname = NULL; if (node_name == NULL) { /* it is not necessarily an error, it can be the root */ *name = NULL; return ISO_SUCCESS; } if (opts->joliet_utf16) { ret = str2utf16be(input_charset, node_name, &ucs_name); if (ret < 0) { if (!(flag & 512)) iso_msg_debug(imgid, "Cannot convert to UTF-16 : \"%s\"", node_name); goto ex; } } else { ret = str2ucs(input_charset, node_name, &ucs_name); if (ret < 0) { if (!(flag & 512)) iso_msg_debug(imgid, "Cannot convert to UCS-2 : \"%s\"", node_name); goto ex; } ret = str2utf16be(input_charset, node_name, &utf16_name); if (ret == ISO_SUCCESS) { if (ucscmp(ucs_name, utf16_name) != 0) { (*joliet_ucs2_failures)++; if (*joliet_ucs2_failures <= ISO_JOLIET_UCS2_WARN_MAX && !(flag & 512)) { iso_msg_submit(imgid, ISO_NAME_NOT_UCS2, 0, "Filename not suitable for Joliet character set UCS-2 : \"%s\"", node_name); } } } } if (node_type == LIBISO_DIR) { jname = iso_j_dir_id(ucs_name, opts->joliet_long_names << 1); } else { jname = iso_j_file_id(ucs_name, (opts->joliet_long_names << 1) | !!(opts->no_force_dots & 2)); } ret = ISO_SUCCESS; ex:; if (ucs_name != NULL) free(ucs_name); if (utf16_name != NULL) free(utf16_name); if (ret != ISO_SUCCESS) { if (jname != NULL) free(jname); return ret; } else if (jname != NULL) { *name = jname; return ISO_SUCCESS; } else { /* * only possible if mem error, as check for empty names is done * in public tree */ return ISO_OUT_OF_MEM; } } static int get_joliet_name(Ecma119Image *t, IsoNode *iso, uint16_t **name) { int ret; ret = iso_get_joliet_name(t->opts, t->input_charset, t->image->id, iso->name, iso->type, &(t->joliet_ucs2_failures), name, 0); return ret; } static void joliet_node_free(JolietNode *node) { if (node == NULL) { return; } if (node->type == JOLIET_DIR) { size_t i; for (i = 0; i < node->info.dir->nchildren; i++) { joliet_node_free(node->info.dir->children[i]); } if (node->info.dir->children != NULL) free(node->info.dir->children); free(node->info.dir); } iso_node_unref(node->node); free(node->name); free(node); } /** * Create a low level Joliet node * @return * 1 success, 0 ignored, < 0 error */ static int create_node(Ecma119Image *t, IsoNode *iso, JolietNode **node) { int ret; JolietNode *joliet; joliet = calloc(1, sizeof(JolietNode)); if (joliet == NULL) { return ISO_OUT_OF_MEM; } if (iso->type == LIBISO_DIR) { IsoDir *dir = (IsoDir*) iso; joliet->info.dir = calloc(1, sizeof(struct joliet_dir_info)); if (joliet->info.dir == NULL) { free(joliet); return ISO_OUT_OF_MEM; } joliet->info.dir->children = NULL; if (dir->nchildren > 0) { joliet->info.dir->children = calloc(sizeof(void*), dir->nchildren); if (joliet->info.dir->children == NULL) { free(joliet->info.dir); free(joliet); return ISO_OUT_OF_MEM; } } joliet->type = JOLIET_DIR; } else if (iso->type == LIBISO_FILE) { /* it's a file */ off_t size; IsoFileSrc *src; IsoFile *file = (IsoFile*) iso; size = iso_stream_get_size(file->stream); if (size > (off_t)MAX_ISO_FILE_SECTION_SIZE && t->opts->iso_level != 3) { char *ipath = iso_tree_get_node_path(iso); free(joliet); ret = iso_msg_submit(t->image->id, ISO_FILE_TOO_BIG, 0, "File \"%s\" can't be added to image because is " "greater than 4GB", ipath); free(ipath); return ret; } ret = iso_file_src_create(t, file, &src); if (ret < 0) { free(joliet); return ret; } joliet->info.file = src; joliet->type = JOLIET_FILE; } else if (iso->type == LIBISO_BOOT) { /* it's a el-torito boot catalog, that we write as a file */ IsoFileSrc *src; ret = el_torito_catalog_file_src_create(t, &src); if (ret < 0) { free(joliet); return ret; } joliet->info.file = src; joliet->type = JOLIET_FILE; } else { /* should never happen */ free(joliet); return ISO_ASSERT_FAILURE; } /* take a ref to the IsoNode */ joliet->node = iso; iso_node_ref(iso); *node = joliet; return ISO_SUCCESS; } /** * Create the low level Joliet tree from the high level ISO tree. * * @return * 1 success, 0 file ignored, < 0 error */ static int create_tree(Ecma119Image *t, IsoNode *iso, JolietNode **tree, int pathlen) { int ret, max_path; JolietNode *node = NULL; uint16_t *jname = NULL; if (t == NULL || iso == NULL || tree == NULL) { return ISO_NULL_POINTER; } if (iso->hidden & LIBISO_HIDE_ON_JOLIET) { /* file will be ignored */ return 0; } ret = get_joliet_name(t, iso, &jname); if (ret < 0) { return ret; } max_path = pathlen + 1 + (jname ? ucslen(jname) * 2 : 0); if (!t->opts->joliet_longer_paths && max_path > 240) { char *ipath = iso_tree_get_node_path(iso); /* * Wow!! Joliet is even more restrictive than plain ISO-9660, * that allows up to 255 bytes!! */ ret = iso_msg_submit(t->image->id, ISO_FILE_IMGPATH_WRONG, 0, "File \"%s\" can't be added to Joliet tree, because " "its path length is larger than 240", ipath); free(jname); free(ipath); return ret; } switch (iso->type) { case LIBISO_FILE: ret = create_node(t, iso, &node); break; case LIBISO_DIR: { IsoNode *pos; IsoDir *dir = (IsoDir*)iso; ret = create_node(t, iso, &node); if (ret < 0) { free(jname); return ret; } pos = dir->children; while (pos) { int cret; JolietNode *child; cret = create_tree(t, pos, &child, max_path); if (cret < 0) { /* error */ joliet_node_free(node); ret = cret; break; } else if (cret == ISO_SUCCESS) { /* add child to this node */ int nchildren = node->info.dir->nchildren++; node->info.dir->children[nchildren] = child; child->parent = node; } pos = pos->next; } } break; case LIBISO_BOOT: if (t->eltorito) { ret = create_node(t, iso, &node); } else { /* log and ignore */ ret = iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, "El-Torito catalog found on a image without El-Torito."); } break; case LIBISO_SYMLINK: t->joliet_symlinks++; if (t->joliet_symlinks == 1) { char *ipath = iso_tree_get_node_path(iso); /* This first ret might indicate the need to abort */ ret = iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, "Cannot add %s to Joliet tree. Symlinks can only be added to a " "Rock Ridge tree.", ipath); free(ipath); } else { if (t->joliet_symlinks == 2) iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, "More symbolic links were omitted from Joliet tree."); ret = 0; } break; case LIBISO_SPECIAL: t->joliet_specials++; if (t->joliet_specials == 1) { char *ipath = iso_tree_get_node_path(iso); /* This first ret might indicate the need to abort */ ret = iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, "Cannot add %s to Joliet tree. " "Special files can only be added to a Rock Ridge tree.", ipath); free(ipath); } else { if (t->joliet_specials == 2) iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0, "More special files were omitted from Joliet tree."); ret = 0; } break; default: /* should never happen */ return ISO_ASSERT_FAILURE; } if (ret <= 0) { free(jname); return ret; } node->name = jname; *tree = node; return ISO_SUCCESS; } static int cmp_node(const void *f1, const void *f2) { JolietNode *f = *((JolietNode**)f1); JolietNode *g = *((JolietNode**)f2); return ucscmp(f->name, g->name); } static void sort_tree(JolietNode *root) { size_t i; if (root->info.dir->children == NULL) return; qsort(root->info.dir->children, root->info.dir->nchildren, sizeof(void*), cmp_node); for (i = 0; i < root->info.dir->nchildren; i++) { JolietNode *child = root->info.dir->children[i]; if (child->type == JOLIET_DIR) sort_tree(child); } } static int cmp_node_name(const void *f1, const void *f2) { JolietNode *f = *((JolietNode**)f1); JolietNode *g = *((JolietNode**)f2); return ucscmp(f->name, g->name); } static int joliet_create_mangled_name(uint16_t *dest, uint16_t *src, int digits, int number, uint16_t *ext) { int ret, pos; uint16_t *ucsnumber; char fmt[16]; char nstr[72]; /* was: The only caller of this function allocates dest with 66 elements and limits digits to < 8 But this does not match the usage of nstr which has to take the decimal representation of an int. */ if (digits >= 8) return ISO_ASSERT_FAILURE; sprintf(fmt, "%%0%dd", digits); sprintf(nstr, fmt, number); ret = str2ucs("ASCII", nstr, &ucsnumber); if (ret < 0) { return ret; } /* copy name */ pos = ucslen(src); ucsncpy(dest, src, pos); /* copy number */ ucsncpy(dest + pos, ucsnumber, digits); pos += digits; if (ext[0] != (uint16_t)0) { size_t extlen = ucslen(ext); iso_msb((uint8_t *) (dest + pos), 0x002E, 2); /* '.' in UCS */ pos++; ucsncpy(dest + pos, ext, extlen); pos += extlen; } iso_msb((uint8_t *) (dest + pos), 0, 2); free(ucsnumber); return ISO_SUCCESS; } /* * From Joliet specs: * "ISO 9660 (Section 7.5.1) states that the sum of the following shall not * exceed 30: * - If there is a file name, the length of the file name. * - If there is a file name extension, the length of the file name extension. * On Joliet compliant media, however, the sum as calculated above shall not * exceed 128 [bytes], to allow for longer file identifiers." * * I.e. the dot does not count. * * (We have an option to lift the limit from 64*2 to 103*2, which is the * maximum to fit into an ISO 9660 directory record.) */ static int mangle_single_dir(Ecma119Image *t, JolietNode *dir) { int ret; int i, nchildren, maxchar = 64; JolietNode **children; IsoHTable *table = NULL; int need_sort = 0; uint16_t *full_name = NULL; uint16_t *tmp = NULL; nchildren = dir->info.dir->nchildren; if (nchildren <= 0) { ret = ISO_SUCCESS; goto ex; } children = dir->info.dir->children; LIBISO_ALLOC_MEM(full_name, uint16_t, LIBISO_JOLIET_NAME_MAX); LIBISO_ALLOC_MEM(tmp, uint16_t, LIBISO_JOLIET_NAME_MAX); if (t->opts->joliet_long_names) maxchar = 103; /* a hash table will temporary hold the names, for fast searching */ ret = iso_htable_create((nchildren * 100) / 80, iso_str_hash, (compare_function_t)ucscmp, &table); if (ret < 0) { goto ex; } for (i = 0; i < nchildren; ++i) { uint16_t *name = children[i]->name; ret = iso_htable_add(table, name, name); if (ret < 0) { goto mangle_cleanup; } } for (i = 0; i < nchildren; ++i) { uint16_t *name, *ext; int max; /* computed max len for name, without extension */ int j = i; int digits = 1; /* characters to change per name */ /* first, find all child with same name */ while (j + 1 < nchildren && !cmp_node_name(children + i, children + j + 1)) { ++j; } if (j == i) { /* name is unique */ continue; } /* * A max of 7 characters is good enough, it allows handling up to * 9,999,999 files with same name. */ /* Important: joliet_create_mangled_name() relies on digits < 8 */ while (digits < 8) { int ok, k; uint16_t *dot; int change = 0; /* number to be written */ /* copy name to buffer */ ucscpy(full_name, children[i]->name); /* compute name and extension */ dot = ucsrchr(full_name, '.'); if (dot != NULL && children[i]->type != JOLIET_DIR) { /* * File (not dir) with extension */ int extlen; full_name[dot - full_name] = 0; name = full_name; ext = dot + 1; extlen = ucslen(ext); max = maxchar - extlen - digits; if (max <= 0) { /* * This can happen if the extension is too long. * Reduce its length, to give name at least one * original character, if it has any. */ max = (dot > full_name); extlen = maxchar - max - digits; if (extlen < 3) { /* * error, we do not reduce extensions to length < 3 * * This cannot happen with current limit of digits * because maxchar is at least 64 and digits at most 7. */ ret = ISO_ERROR; goto mangle_cleanup; } ext[extlen] = 0; } /* ok, reduce name by digits */ if (name + max < dot) { name[max] = 0; } } else { /* Directory, or file without extension */ if (children[i]->type == JOLIET_DIR) { max = maxchar - digits; dot = NULL; /* dots have no meaning in dirs */ } else { max = maxchar - digits; } name = full_name; if ((size_t) max < ucslen(name)) { name[max] = 0; } /* let ext be an empty string */ ext = name + ucslen(name); } ok = 1; /* change name of each file */ for (k = i; k <= j; ++k) { while (1) { ret = joliet_create_mangled_name(tmp, name, digits, change, ext); if (ret < 0) { goto mangle_cleanup; } ++change; if (change > int_pow(10, digits)) { ok = 0; break; } if (!iso_htable_get(table, tmp, NULL)) { /* the name is unique, so it can be used */ break; } } if (ok) { uint16_t *new = ucsdup(tmp); if (new == NULL) { ret = ISO_OUT_OF_MEM; goto mangle_cleanup; } iso_htable_remove_ptr(table, children[k]->name, NULL); free(children[k]->name); children[k]->name = new; iso_htable_add(table, new, new); /* * if we change a name we need to sort again children * at the end */ need_sort = 1; } else { /* we need to increment digits */ break; } } if (ok) { break; } else { ++digits; } } if (digits == 8) { ret = ISO_MANGLE_TOO_MUCH_FILES; goto mangle_cleanup; } i = j; } /* * If needed, sort again the files inside dir */ if (need_sort) { qsort(children, nchildren, sizeof(void*), cmp_node_name); } ret = ISO_SUCCESS; mangle_cleanup : ; ex:; iso_htable_destroy(table, NULL); LIBISO_FREE_MEM(tmp); LIBISO_FREE_MEM(full_name); return ret; } static int mangle_tree(Ecma119Image *t, JolietNode *dir) { int ret; size_t i; ret = mangle_single_dir(t, dir); if (ret < 0) { return ret; } /* recurse */ for (i = 0; i < dir->info.dir->nchildren; ++i) { if (dir->info.dir->children[i]->type == JOLIET_DIR) { ret = mangle_tree(t, dir->info.dir->children[i]); if (ret < 0) { /* error */ return ret; } } } return ISO_SUCCESS; } static int joliet_tree_create(Ecma119Image *t) { int ret; JolietNode *root; if (t == NULL) { return ISO_NULL_POINTER; } ret = create_tree(t, (IsoNode*)t->image->root, &root, 0); if (ret <= 0) { if (ret == 0) { /* unexpected error, root ignored!! This can't happen */ ret = ISO_ASSERT_FAILURE; } return ret; } /* the Joliet tree is stored in Ecma119Image target */ if (t->eff_partition_offset > 0) { t->j_part_root = root; } else { t->joliet_root = root; } iso_msg_debug(t->image->id, "Sorting the Joliet tree..."); sort_tree(root); iso_msg_debug(t->image->id, "Mangling Joliet names..."); ret = mangle_tree(t, root); if (ret < 0) return ret; return ISO_SUCCESS; } /** * Compute the size of a directory entry for a single node */ static size_t calc_dirent_len(Ecma119Image *t, JolietNode *n) { /* note than name len is always even, so we always need the pad byte */ int ret = n->name ? ucslen(n->name) * 2 + 34 : 34; if (n->type == JOLIET_FILE && !(t->opts->omit_version_numbers & 3)) { /* take into account version numbers */ ret += 4; } return ret; } /** * Computes the total size of all directory entries of a single joliet dir. * This is like ECMA-119 6.8.1.1, but taking care that names are stored in * UCS. */ static size_t calc_dir_size(Ecma119Image *t, JolietNode *dir) { size_t i, len; /* size of "." and ".." entries */ len = 34 + 34; for (i = 0; i < dir->info.dir->nchildren; ++i) { size_t remaining; int section, nsections; JolietNode *child = dir->info.dir->children[i]; size_t dirent_len = calc_dirent_len(t, child); nsections = (child->type == JOLIET_FILE) ? child->info.file->nsections : 1; for (section = 0; section < nsections; ++section) { remaining = BLOCK_SIZE - (len % BLOCK_SIZE); if (dirent_len > remaining) { /* child directory entry doesn't fit on block */ len += remaining + dirent_len; } else { len += dirent_len; } } } /* * The size of a dir is always a multiple of block size, as we must add * the size of the unused space after the last directory record * (ECMA-119, 6.8.1.3) */ len = ROUND_UP(len, BLOCK_SIZE); /* cache the len */ dir->info.dir->len = len; return len; } static void calc_dir_pos(Ecma119Image *t, JolietNode *dir) { size_t i, len; t->joliet_ndirs++; dir->info.dir->block = t->curblock; len = calc_dir_size(t, dir); t->curblock += DIV_UP(len, BLOCK_SIZE); for (i = 0; i < dir->info.dir->nchildren; i++) { JolietNode *child = dir->info.dir->children[i]; if (child->type == JOLIET_DIR) { calc_dir_pos(t, child); } } } /** * Compute the length of the joliet path table, in bytes. */ static uint32_t calc_path_table_size(JolietNode *dir) { uint32_t size; size_t i; /* size of path table for this entry */ size = 8; size += dir->name ? ucslen(dir->name) * 2 : 2; /* and recurse */ for (i = 0; i < dir->info.dir->nchildren; i++) { JolietNode *child = dir->info.dir->children[i]; if (child->type == JOLIET_DIR) { size += calc_path_table_size(child); } } return size; } static int joliet_writer_compute_data_blocks(IsoImageWriter *writer) { Ecma119Image *t; uint32_t path_table_size; size_t ndirs; if (writer == NULL) { return ISO_OUT_OF_MEM; } t = writer->target; /* compute position of directories */ iso_msg_debug(t->image->id, "Computing position of Joliet dir structure"); t->joliet_ndirs = 0; calc_dir_pos(t, t->joliet_root); /* compute length of pathlist */ iso_msg_debug(t->image->id, "Computing length of Joliet pathlist"); path_table_size = calc_path_table_size(t->joliet_root); /* compute location for path tables */ t->joliet_l_path_table_pos = t->curblock; t->curblock += DIV_UP(path_table_size, BLOCK_SIZE); t->joliet_m_path_table_pos = t->curblock; t->curblock += DIV_UP(path_table_size, BLOCK_SIZE); t->joliet_path_table_size = path_table_size; if (t->opts->partition_offset > 0) { /* Take into respect second directory tree */ ndirs = t->joliet_ndirs; t->joliet_ndirs = 0; calc_dir_pos(t, t->j_part_root); if (t->joliet_ndirs != ndirs) { iso_msg_submit(t->image->id, ISO_ASSERT_FAILURE, 0, "Number of directories differs in Joliet partiton_tree"); return ISO_ASSERT_FAILURE; } /* Take into respect second set of path tables */ path_table_size = calc_path_table_size(t->j_part_root); t->j_part_l_path_table_pos = t->curblock; t->curblock += DIV_UP(path_table_size, BLOCK_SIZE); t->j_part_m_path_table_pos = t->curblock; t->curblock += DIV_UP(path_table_size, BLOCK_SIZE); } return ISO_SUCCESS; } /** * Write a single directory record for Joliet. It is like (ECMA-119, 9.1), * but file identifier is stored in UCS. * * @param file_id * if >= 0, we use it instead of the filename (for "." and ".." entries). * @param len_fi * Computed length of the file identifier. Total size of the directory * entry will be len + 34 (ECMA-119, 9.1.12), as padding is always needed */ static void write_one_dir_record(Ecma119Image *t, JolietNode *node, int file_id, uint8_t *buf, size_t len_fi, int extent) { uint32_t len; uint32_t block; uint8_t len_dr; /*< size of dir entry */ int multi_extend = 0; uint8_t *name = (file_id >= 0) ? (uint8_t*)&file_id : (uint8_t*)node->name; struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; IsoNode *iso; len_dr = 33 + len_fi + ((len_fi % 2) ? 0 : 1); memcpy(rec->file_id, name, len_fi); if (node->type == JOLIET_FILE && !(t->opts->omit_version_numbers & 3)) { len_dr += 4; rec->file_id[len_fi++] = 0; rec->file_id[len_fi++] = ';'; rec->file_id[len_fi++] = 0; rec->file_id[len_fi++] = '1'; } if (node->type == JOLIET_DIR) { /* use the cached length */ len = node->info.dir->len; block = node->info.dir->block; } else if (node->type == JOLIET_FILE) { block = node->info.file->sections[extent].block; len = node->info.file->sections[extent].size; multi_extend = (node->info.file->nsections - 1 == extent) ? 0 : 1; } else { /* * for nodes other than files and dirs, we set both * len and block to 0 */ len = 0; block = 0; } /* * For ".." entry we need to write the parent info! */ if (file_id == 1 && node->parent) node = node->parent; rec->len_dr[0] = len_dr; iso_bb(rec->block, block - t->eff_partition_offset, 4); iso_bb(rec->length, len, 4); /* was: iso_datetime_7(rec->recording_time, t->now, t->opts->always_gmt); */ iso= node->node; iso_datetime_7(rec->recording_time, (t->opts->dir_rec_mtime & 2) ? ( t->replace_timestamps ? t->timestamp : iso->mtime ) : t->now, t->opts->always_gmt); rec->flags[0] = ((node->type == JOLIET_DIR) ? 2 : 0) | (multi_extend ? 0x80 : 0); iso_bb(rec->vol_seq_number, (uint32_t) 1, 2); rec->len_fi[0] = len_fi; } /** * Copy up to \p max characters from \p src to \p dest. If \p src has less than * \p max characters, we pad dest with " " characters. */ static void ucsncpy_pad(uint16_t *dest, const uint16_t *src, size_t max) { char *cdest, *csrc; size_t len, i; cdest = (char*)dest; csrc = (char*)src; if (src != NULL) { len = MIN(ucslen(src) * 2, max - (max % 2)); } else { len = 0; } for (i = 0; i < len; ++i) cdest[i] = csrc[i]; if (len >= 2) iso_handle_split_utf16(dest + (len / 2 - 1)); for (i = len; i + 1 < max; i += 2) { cdest[i] = '\0'; cdest[i + 1] = ' '; } if (max % 2) cdest[max - 1] = 0; } int joliet_writer_write_vol_desc(IsoImageWriter *writer) { IsoImage *image; Ecma119Image *t; struct ecma119_sup_vol_desc vol; uint16_t *vol_id = NULL, *pub_id = NULL, *data_id = NULL; uint16_t *volset_id = NULL, *system_id = NULL, *application_id = NULL; uint16_t *copyright_file_id = NULL, *abstract_file_id = NULL; uint16_t *biblio_file_id = NULL; if (writer == NULL) { return ISO_OUT_OF_MEM; } t = writer->target; image = t->image; iso_msg_debug(image->id, "Write SVD for Joliet"); memset(&vol, 0, sizeof(struct ecma119_sup_vol_desc)); str2ucs(t->input_charset, image->volume_id, &vol_id); str2ucs(t->input_charset, image->publisher_id, &pub_id); str2ucs(t->input_charset, image->data_preparer_id, &data_id); str2ucs(t->input_charset, image->volset_id, &volset_id); str2ucs(t->input_charset, image->system_id, &system_id); str2ucs(t->input_charset, image->application_id, &application_id); str2ucs(t->input_charset, image->copyright_file_id, ©right_file_id); str2ucs(t->input_charset, image->abstract_file_id, &abstract_file_id); str2ucs(t->input_charset, image->biblio_file_id, &biblio_file_id); vol.vol_desc_type[0] = 2; memcpy(vol.std_identifier, "CD001", 5); vol.vol_desc_version[0] = 1; ucsncpy_pad((uint16_t*)vol.volume_id, vol_id, 32); /* make use of UCS-2 Level 3 */ memcpy(vol.esc_sequences, "%/E", 3); iso_bb(vol.vol_space_size, t->vol_space_size - t->eff_partition_offset, 4); iso_bb(vol.vol_set_size, (uint32_t) 1, 2); iso_bb(vol.vol_seq_number, (uint32_t) 1, 2); iso_bb(vol.block_size, (uint32_t) BLOCK_SIZE, 2); iso_bb(vol.path_table_size, t->joliet_path_table_size, 4); if (t->eff_partition_offset > 0) { /* Point to second tables and second root */ iso_lsb(vol.l_path_table_pos, t->j_part_l_path_table_pos - t->eff_partition_offset, 4); iso_msb(vol.m_path_table_pos, t->j_part_m_path_table_pos - t->eff_partition_offset, 4); write_one_dir_record(t, t->j_part_root, 0, vol.root_dir_record, 1, 0); } else { iso_lsb(vol.l_path_table_pos, t->joliet_l_path_table_pos, 4); iso_msb(vol.m_path_table_pos, t->joliet_m_path_table_pos, 4); write_one_dir_record(t, t->joliet_root, 0, vol.root_dir_record, 1, 0); } ucsncpy_pad((uint16_t*)vol.vol_set_id, volset_id, 128); ucsncpy_pad((uint16_t*)vol.publisher_id, pub_id, 128); ucsncpy_pad((uint16_t*)vol.data_prep_id, data_id, 128); ucsncpy_pad((uint16_t*)vol.system_id, system_id, 32); ucsncpy_pad((uint16_t*)vol.application_id, application_id, 128); ucsncpy_pad((uint16_t*)vol.copyright_file_id, copyright_file_id, 37); ucsncpy_pad((uint16_t*)vol.abstract_file_id, abstract_file_id, 37); ucsncpy_pad((uint16_t*)vol.bibliographic_file_id, biblio_file_id, 37); ecma119_set_voldescr_times(writer, (struct ecma119_pri_vol_desc *) &vol); vol.file_structure_version[0] = 1; free(vol_id); free(volset_id); free(pub_id); free(data_id); free(system_id); free(application_id); free(copyright_file_id); free(abstract_file_id); free(biblio_file_id); /* Finally write the Volume Descriptor */ return iso_write(t, &vol, sizeof(struct ecma119_sup_vol_desc)); } static int write_one_dir(Ecma119Image *t, JolietNode *dir) { int ret; uint8_t *buffer = NULL; size_t i; size_t fi_len, len; /* buf will point to current write position on buffer */ uint8_t *buf; /* initialize buffer with 0s */ LIBISO_ALLOC_MEM(buffer, uint8_t, BLOCK_SIZE); buf = buffer; /* write the "." and ".." entries first */ write_one_dir_record(t, dir, 0, buf, 1, 0); buf += 34; write_one_dir_record(t, dir, 1, buf, 1, 0); buf += 34; for (i = 0; i < dir->info.dir->nchildren; i++) { int section, nsections; JolietNode *child = dir->info.dir->children[i]; /* compute len of directory entry */ fi_len = ucslen(child->name) * 2; len = fi_len + 34; if (child->type == JOLIET_FILE && !(t->opts->omit_version_numbers & 3)) { len += 4; } nsections = (child->type == JOLIET_FILE) ? child->info.file->nsections : 1; for (section = 0; section < nsections; ++section) { if ( (buf + len - buffer) > BLOCK_SIZE) { /* dir doesn't fit in current block */ ret = iso_write(t, buffer, BLOCK_SIZE); if (ret < 0) { goto ex; } memset(buffer, 0, BLOCK_SIZE); buf = buffer; } /* write the directory entry in any case */ write_one_dir_record(t, child, -1, buf, fi_len, section); buf += len; } } /* write the last block */ ret = iso_write(t, buffer, BLOCK_SIZE); ex:; LIBISO_FREE_MEM(buffer); return ret; } static int write_dirs(Ecma119Image *t, JolietNode *root) { int ret; size_t i; /* write all directory entries for this dir */ ret = write_one_dir(t, root); if (ret < 0) { return ret; } /* recurse */ for (i = 0; i < root->info.dir->nchildren; i++) { JolietNode *child = root->info.dir->children[i]; if (child->type == JOLIET_DIR) { ret = write_dirs(t, child); if (ret < 0) { return ret; } } } return ISO_SUCCESS; } static int write_path_table(Ecma119Image *t, JolietNode **pathlist, int l_type) { size_t i, len; uint8_t *buf = NULL; struct ecma119_path_table_record *rec; void (*write_int)(uint8_t*, uint32_t, int); JolietNode *dir; uint32_t path_table_size; int parent = 0; int ret= ISO_SUCCESS; uint8_t *zeros = NULL; /* 256 is just a convenient size large enough */ LIBISO_ALLOC_MEM(buf, uint8_t, 256); LIBISO_ALLOC_MEM(zeros, uint8_t, BLOCK_SIZE); path_table_size = 0; write_int = l_type ? iso_lsb : iso_msb; for (i = 0; i < t->joliet_ndirs; i++) { dir = pathlist[i]; /* find the index of the parent in the table */ while ((i) && pathlist[parent] != dir->parent) { parent++; } /* write the Path Table Record (ECMA-119, 9.4) */ memset(buf, 0, 256); rec = (struct ecma119_path_table_record*) buf; rec->len_di[0] = dir->parent ? (uint8_t) ucslen(dir->name) * 2 : 1; rec->len_xa[0] = 0; write_int(rec->block, dir->info.dir->block - t->eff_partition_offset, 4); write_int(rec->parent, parent + 1, 2); if (dir->parent) { memcpy(rec->dir_id, dir->name, rec->len_di[0]); } len = 8 + rec->len_di[0] + (rec->len_di[0] % 2); ret = iso_write(t, buf, len); if (ret < 0) { /* error */ goto ex; } path_table_size += len; } /* we need to fill the last block with zeros */ path_table_size %= BLOCK_SIZE; if (path_table_size) { len = BLOCK_SIZE - path_table_size; memset(zeros, 0, len); ret = iso_write(t, zeros, len); } ex:; LIBISO_FREE_MEM(zeros); LIBISO_FREE_MEM(buf); return ret; } static int write_path_tables(Ecma119Image *t) { int ret; size_t i, j, cur; JolietNode **pathlist; iso_msg_debug(t->image->id, "Writing Joliet Path tables"); /* allocate temporal pathlist */ pathlist = malloc(sizeof(void*) * t->joliet_ndirs); if (pathlist == NULL) { return ISO_OUT_OF_MEM; } if (t->eff_partition_offset > 0) { pathlist[0] = t->j_part_root; } else { pathlist[0] = t->joliet_root; } cur = 1; for (i = 0; i < t->joliet_ndirs; i++) { JolietNode *dir = pathlist[i]; for (j = 0; j < dir->info.dir->nchildren; j++) { JolietNode *child = dir->info.dir->children[j]; if (child->type == JOLIET_DIR) { pathlist[cur++] = child; } } } /* Write L Path Table */ ret = write_path_table(t, pathlist, 1); if (ret < 0) { goto write_path_tables_exit; } /* Write L Path Table */ ret = write_path_table(t, pathlist, 0); write_path_tables_exit: ; free(pathlist); return ret; } static int joliet_writer_write_dirs(IsoImageWriter *writer) { int ret; Ecma119Image *t; JolietNode *root; t = writer->target; /* first of all, we write the directory structure */ if (t->eff_partition_offset > 0) { root = t->j_part_root; } else { root = t->joliet_root; } ret = write_dirs(t, root); if (ret < 0) { return ret; } /* and write the path tables */ ret = write_path_tables(t); return ret; } static int joliet_writer_write_data(IsoImageWriter *writer) { int ret; Ecma119Image *t; if (writer == NULL) { return ISO_NULL_POINTER; } t = writer->target; ret = joliet_writer_write_dirs(writer); if (ret < 0) return ret; if (t->opts->partition_offset > 0) { t->eff_partition_offset = t->opts->partition_offset; ret = joliet_writer_write_dirs(writer); t->eff_partition_offset = 0; if (ret < 0) return ret; } return ISO_SUCCESS; } static int joliet_writer_free_data(IsoImageWriter *writer) { /* free the Joliet tree */ Ecma119Image *t = writer->target; joliet_node_free(t->joliet_root); if (t->j_part_root != NULL) joliet_node_free(t->j_part_root); t->j_part_root = NULL; return ISO_SUCCESS; } int joliet_writer_create(Ecma119Image *target) { int ret; IsoImageWriter *writer; writer = malloc(sizeof(IsoImageWriter)); if (writer == NULL) { return ISO_OUT_OF_MEM; } writer->compute_data_blocks = joliet_writer_compute_data_blocks; writer->write_vol_desc = joliet_writer_write_vol_desc; writer->write_data = joliet_writer_write_data; writer->free_data = joliet_writer_free_data; writer->data = NULL; writer->target = target; iso_msg_debug(target->image->id, "Creating low level Joliet tree..."); ret = joliet_tree_create(target); if (ret < 0) { free((char *) writer); return ret; } /* add this writer to image */ target->writers[target->nwriters++] = writer; if(target->opts->partition_offset > 0) { /* Create second tree */ target->eff_partition_offset = target->opts->partition_offset; ret = joliet_tree_create(target); if (ret < 0) { return ret; } target->eff_partition_offset = 0; } /* we need the volume descriptor */ target->curblock++; return ISO_SUCCESS; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2007 Mario Danic * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /** * Declare Joliet related structures. */ #ifndef LIBISO_JOLIET_H #define LIBISO_JOLIET_H #include "libisofs.h" #include "ecma119.h" /* was formerly 66 = 64 + 2. Now 105 = 103 + 2. */ #define LIBISO_JOLIET_NAME_MAX 105 enum joliet_node_type { JOLIET_FILE, JOLIET_DIR }; struct joliet_dir_info { JolietNode **children; size_t nchildren; size_t len; size_t block; }; struct joliet_node { uint16_t *name; /**< Name in UCS-2BE. */ JolietNode *parent; IsoNode *node; /*< reference to the iso node */ enum joliet_node_type type; union { IsoFileSrc *file; struct joliet_dir_info *dir; } info; }; /** * Create a IsoWriter to deal with Joliet estructures, and add it to the given * target. * * @return * 1 on success, < 0 on error */ int joliet_writer_create(Ecma119Image *target); /* Not to be called but only for comparison with target->writers[i] */ int joliet_writer_write_vol_desc(IsoImageWriter *writer); /** * Determine the Joliet name from node name. * @param flag bit0= Do not issue error messages */ int iso_get_joliet_name(IsoWriteOpts *opts, char *input_charset, int imgid, char *node_name, enum IsoNodeType node_type, size_t *joliet_ucs2_failures, uint16_t **name, int flag); #endif /* LIBISO_JOLIET_H */ /* libiso_msgs (generated from libdax_msgs : Fri Feb 22 19:42:52 CET 2008) Message handling facility of libisofs. Copyright (C) 2006 - 2016 Thomas Schmitt , provided under GPL version 2 or later */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include #include #include #include #include #include /* Only this single source module is entitled to do this */ #define LIBISO_MSGS_H_INTERNAL 1 /* All participants in the messaging system must do this */ #include "libiso_msgs.h" /* ----------------------------- libiso_msgs_item ------------------------- */ static int libiso_msgs_item_new(struct libiso_msgs_item **item, struct libiso_msgs_item *link, int flag) { int ret; struct libiso_msgs_item *o; struct timeval tv; (*item)= o= (struct libiso_msgs_item *) malloc(sizeof(struct libiso_msgs_item)); if(o==NULL) return(-1); o->timestamp= 0.0; ret= gettimeofday(&tv, NULL); if(ret==0) o->timestamp= tv.tv_sec+0.000001*tv.tv_usec; o->process_id= getpid(); o->origin= -1; o->severity= LIBISO_MSGS_SEV_ALL; o->priority= LIBISO_MSGS_PRIO_ZERO; o->error_code= 0; o->msg_text= NULL; o->os_errno= 0; o->prev= link; o->next= NULL; if(link!=NULL) { if(link->next!=NULL) { link->next->prev= o; o->next= link->next; } link->next= o; } return(1); } /** Detaches item from its queue and eventually readjusts start, end pointers of the queue */ int libiso_msgs_item_unlink(struct libiso_msgs_item *o, struct libiso_msgs_item **chain_start, struct libiso_msgs_item **chain_end, int flag) { if(o->prev!=NULL) o->prev->next= o->next; if(o->next!=NULL) o->next->prev= o->prev; if(chain_start!=NULL) if(*chain_start == o) *chain_start= o->next; if(chain_end!=NULL) if(*chain_end == o) *chain_end= o->prev; o->next= o->prev= NULL; return(1); } int libiso_msgs_item_destroy(struct libiso_msgs_item **item, int flag) { struct libiso_msgs_item *o; o= *item; if(o==NULL) return(0); libiso_msgs_item_unlink(o,NULL,NULL,0); if(o->msg_text!=NULL) free((char *) o->msg_text); free((char *) o); *item= NULL; return(1); } int libiso_msgs_item_get_msg(struct libiso_msgs_item *item, int *error_code, char **msg_text, int *os_errno, int flag) { *error_code= item->error_code; *msg_text= item->msg_text; *os_errno= item->os_errno; return(1); } int libiso_msgs_item_get_origin(struct libiso_msgs_item *item, double *timestamp, pid_t *process_id, int *origin, int flag) { *timestamp= item->timestamp; *process_id= item->process_id; *origin= item->origin; return(1); } int libiso_msgs_item_get_rank(struct libiso_msgs_item *item, int *severity, int *priority, int flag) { *severity= item->severity; *priority= item->priority; return(1); } /* ------------------------------- libiso_msgs ---------------------------- */ int libiso_msgs_new(struct libiso_msgs **m, int flag) { struct libiso_msgs *o; (*m)= o= (struct libiso_msgs *) malloc(sizeof(struct libiso_msgs)); if(o==NULL) return(-1); o->refcount= 1; o->oldest= NULL; o->youngest= NULL; o->count= 0; o->queue_severity= LIBISO_MSGS_SEV_ALL; o->print_severity= LIBISO_MSGS_SEV_NEVER; strcpy(o->print_id,"libiso: "); #ifndef LIBISO_MSGS_SINGLE_THREADED pthread_mutex_init(&(o->lock_mutex),NULL); #endif return(1); } static int libiso_msgs_lock(struct libiso_msgs *m, int flag) { #ifndef LIBISO_MSGS_SINGLE_THREADED int ret; ret= pthread_mutex_lock(&(m->lock_mutex)); if(ret!=0) return(0); #endif return(1); } static int libiso_msgs_unlock(struct libiso_msgs *m, int flag) { #ifndef LIBISO_MSGS_SINGLE_THREADED int ret; ret= pthread_mutex_unlock(&(m->lock_mutex)); if(ret!=0) return(0); #endif return(1); } int libiso_msgs_destroy(struct libiso_msgs **m, int flag) { struct libiso_msgs *o; struct libiso_msgs_item *item, *next_item; o= *m; if(o==NULL) return(0); if(o->refcount > 1) { if(libiso_msgs_lock(*m,0)<=0) return(-1); o->refcount--; libiso_msgs_unlock(*m,0); *m= NULL; return(1); } #ifndef LIBISO_MSGS_SINGLE_THREADED if(pthread_mutex_destroy(&(o->lock_mutex))!=0) { pthread_mutex_unlock(&(o->lock_mutex)); pthread_mutex_destroy(&(o->lock_mutex)); } #endif for(item= o->oldest; item!=NULL; item= next_item) { next_item= item->next; libiso_msgs_item_destroy(&item,0); } free((char *) o); *m= NULL; return(1); } int libiso_msgs_refer(struct libiso_msgs **pt, struct libiso_msgs *m, int flag) { if(libiso_msgs_lock(m,0)<=0) return(0); m->refcount++; *pt= m; libiso_msgs_unlock(m,0); return(1); } int libiso_msgs_set_severities(struct libiso_msgs *m, int queue_severity, int print_severity, char *print_id, int flag) { if(libiso_msgs_lock(m,0)<=0) return(0); m->queue_severity= queue_severity; m->print_severity= print_severity; strncpy(m->print_id,print_id,80); m->print_id[80]= 0; libiso_msgs_unlock(m,0); return(1); } int libiso_msgs__text_to_sev(char *severity_name, int *severity, int flag) { if(strncmp(severity_name,"NEVER",5)==0) *severity= LIBISO_MSGS_SEV_NEVER; else if(strncmp(severity_name,"ABORT",5)==0) *severity= LIBISO_MSGS_SEV_ABORT; else if(strncmp(severity_name,"FATAL",5)==0) *severity= LIBISO_MSGS_SEV_FATAL; else if(strncmp(severity_name,"FAILURE",7)==0) *severity= LIBISO_MSGS_SEV_FAILURE; else if(strncmp(severity_name,"MISHAP",6)==0) *severity= LIBISO_MSGS_SEV_MISHAP; else if(strncmp(severity_name,"SORRY",5)==0) *severity= LIBISO_MSGS_SEV_SORRY; else if(strncmp(severity_name,"WARNING",7)==0) *severity= LIBISO_MSGS_SEV_WARNING; else if(strncmp(severity_name,"HINT",4)==0) *severity= LIBISO_MSGS_SEV_HINT; else if(strncmp(severity_name,"NOTE",4)==0) *severity= LIBISO_MSGS_SEV_NOTE; else if(strncmp(severity_name,"UPDATE",6)==0) *severity= LIBISO_MSGS_SEV_UPDATE; else if(strncmp(severity_name,"DEBUG",5)==0) *severity= LIBISO_MSGS_SEV_DEBUG; else if(strncmp(severity_name,"ERRFILE",7)==0) *severity= LIBISO_MSGS_SEV_ERRFILE; else if(strncmp(severity_name,"ALL",3)==0) *severity= LIBISO_MSGS_SEV_ALL; else { *severity= LIBISO_MSGS_SEV_ALL; return(0); } return(1); } int libiso_msgs__sev_to_text(int severity, char **severity_name, int flag) { if(flag&1) { *severity_name= "ALL ERRFILE DEBUG UPDATE NOTE HINT WARNING SORRY MISHAP FAILURE FATAL ABORT NEVER"; return(1); } *severity_name= ""; if(severity>=LIBISO_MSGS_SEV_NEVER) *severity_name= "NEVER"; else if(severity>=LIBISO_MSGS_SEV_ABORT) *severity_name= "ABORT"; else if(severity>=LIBISO_MSGS_SEV_FATAL) *severity_name= "FATAL"; else if(severity>=LIBISO_MSGS_SEV_FAILURE) *severity_name= "FAILURE"; else if(severity>=LIBISO_MSGS_SEV_MISHAP) *severity_name= "MISHAP"; else if(severity>=LIBISO_MSGS_SEV_SORRY) *severity_name= "SORRY"; else if(severity>=LIBISO_MSGS_SEV_WARNING) *severity_name= "WARNING"; else if(severity>=LIBISO_MSGS_SEV_HINT) *severity_name= "HINT"; else if(severity>=LIBISO_MSGS_SEV_NOTE) *severity_name= "NOTE"; else if(severity>=LIBISO_MSGS_SEV_UPDATE) *severity_name= "UPDATE"; else if(severity>=LIBISO_MSGS_SEV_DEBUG) *severity_name= "DEBUG"; else if(severity>=LIBISO_MSGS_SEV_ERRFILE) *severity_name= "ERRFILE"; else if(severity>=LIBISO_MSGS_SEV_ALL) *severity_name= "ALL"; else { *severity_name= ""; return(0); } return(1); } int libiso_msgs_submit(struct libiso_msgs *m, int origin, int error_code, int severity, int priority, char *msg_text, int os_errno, int flag) { int ret; char *textpt,*sev_name,sev_text[81]; struct libiso_msgs_item *item= NULL; if(severity >= m->print_severity) { if(msg_text==NULL) textpt= ""; else textpt= msg_text; sev_text[0]= 0; ret= libiso_msgs__sev_to_text(severity,&sev_name,0); if(ret>0) sprintf(sev_text,"%s : ",sev_name); fprintf(stderr,"%s%s%s\n",m->print_id,sev_text,textpt); if(os_errno!=0) { ret= libiso_msgs_lock(m,0); if(ret<=0) return(-1); fprintf(stderr,"%s( Most recent system error: %d '%s' )\n", m->print_id,os_errno,strerror(os_errno)); libiso_msgs_unlock(m,0); } } if(severity < m->queue_severity) return(0); ret= libiso_msgs_lock(m,0); if(ret<=0) return(-1); ret= libiso_msgs_item_new(&item,m->youngest,0); if(ret<=0) goto failed; item->origin= origin; item->error_code= error_code; item->severity= severity; item->priority= priority; if(msg_text!=NULL) { item->msg_text= malloc(strlen(msg_text)+1); if(item->msg_text==NULL) goto failed; strcpy(item->msg_text,msg_text); } item->os_errno= os_errno; if(m->oldest==NULL) m->oldest= item; m->youngest= item; m->count++; libiso_msgs_unlock(m,0); /* fprintf(stderr,"libiso_experimental: message submitted to queue (now %d)\n", m->count); */ return(1); failed:; libiso_msgs_item_destroy(&item,0); libiso_msgs_unlock(m,0); return(-1); } int libiso_msgs_obtain(struct libiso_msgs *m, struct libiso_msgs_item **item, int severity, int priority, int flag) { int ret; struct libiso_msgs_item *im, *next_im= NULL; *item= NULL; ret= libiso_msgs_lock(m,0); if(ret<=0) return(-1); for(im= m->oldest; im!=NULL; im= next_im) { for(; im!=NULL; im= next_im) { next_im= im->next; if(im->severity>=severity) break; libiso_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0); libiso_msgs_item_destroy(&im,0); /* severity too low: delete */ } if(im==NULL) break; if(im->priority>=priority) break; } if(im==NULL) {ret= 0; goto ex;} libiso_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0); *item= im; ret= 1; ex:; libiso_msgs_unlock(m,0); return(ret); } int libiso_msgs_destroy_item(struct libiso_msgs *m, struct libiso_msgs_item **item, int flag) { int ret; ret= libiso_msgs_lock(m,0); if(ret<=0) return(-1); ret= libiso_msgs_item_destroy(item,0); libiso_msgs_unlock(m,0); return(ret); } /* libiso_msgs (generated from libdax_msgs : Fri Feb 22 19:42:52 CET 2008) Message handling facility of libisofs. Copyright (C) 2006-2016 Thomas Schmitt , This file is part of the libisofs project; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later as published by the Free Software Foundation. See COPYING file for details. */ /* *Never* set this macro outside libiso_msgs.c ! The entrails of the message handling facility are not to be seen by the other library components or the applications. */ #ifdef LIBISO_MSGS_H_INTERNAL #ifndef LIBISO_MSGS_SINGLE_THREADED #include #endif struct libiso_msgs_item { double timestamp; pid_t process_id; int origin; int severity; int priority; /* Apply for your developer's error code range at libburn-hackers@pykix.org Report introduced codes in the list below. */ int error_code; char *msg_text; int os_errno; struct libiso_msgs_item *prev,*next; }; struct libiso_msgs { int refcount; struct libiso_msgs_item *oldest; struct libiso_msgs_item *youngest; int count; int queue_severity; int print_severity; char print_id[81]; #ifndef LIBISO_MSGS_SINGLE_THREADED pthread_mutex_t lock_mutex; #endif }; #endif /* LIBISO_MSGS_H_INTERNAL */ #ifndef LIBISO_MSGS_H_INCLUDED #define LIBISO_MSGS_H_INCLUDED 1 #ifndef LIBISO_MSGS_H_INTERNAL /* Architectural aspects */ /* libdax_msgs is designed to serve in libraries which want to offer their applications a way to control the output of library messages. It shall be incorporated by an owner, i.e. a software entity which encloses the code of the .c file. Owner of libdax_msgs is libburn. A fully compatible variant named libiso_msgs is owned by libisofs and can get generated by a script of the libburn project: libburn/libiso_msgs_to_xyz_msgs.sh . Reason: One cannot link two owners of the same variant together because both would offer the same functions to the linker. For that situation one has to create a compatible variant as it is done for libisofs. Compatible variants may get plugged together by call combinations like burn_set_messenger(iso_get_messenger()); A new variant would demand a _set_messenger() function if it has to work with libisofs. If only libburn is planned as link partner then a simple _get_messenger() does suffice. Take care to shutdown libburn before its provider of the *_msgs object gets shut down. */ /* Public Opaque Handles */ /** A pointer to this is a opaque handle to a message handling facility */ struct libiso_msgs; /** A pointer to this is a opaque handle to a single message item */ struct libiso_msgs_item; #endif /* ! LIBISO_MSGS_H_INTERNAL */ /* Public Macros */ /* Registered Severities */ /* It is well advisable to let applications select severities via strings and forwarded functions libiso_msgs__text_to_sev(), libiso_msgs__sev_to_text(). These macros are for use by the owner of libiso_msgs. */ /** Use this to get messages of any severity. Do not use for submitting. */ #define LIBISO_MSGS_SEV_ALL 0x00000000 /** Messages of this severity shall transport plain disk file paths whenever an event of severity SORRY or above is related with an individual disk file. No message text shall be added to the file path. The ERRFILE message shall be issued before the human readable message which carries the true event severity. That message should contain the file path so it can be found by strstr(message, path)!=NULL. The error code shall be the same as with the human readable message. */ #define LIBISO_MSGS_SEV_ERRFILE 0x08000000 /** Debugging messages not to be visible to normal users by default */ #define LIBISO_MSGS_SEV_DEBUG 0x10000000 /** Update of a progress report about long running actions */ #define LIBISO_MSGS_SEV_UPDATE 0x20000000 /** Not so usual events which were gracefully handled */ #define LIBISO_MSGS_SEV_NOTE 0x30000000 /** Possibilities to achieve a better result */ #define LIBISO_MSGS_SEV_HINT 0x40000000 /** Warnings about problems which could not be handled optimally */ #define LIBISO_MSGS_SEV_WARNING 0x50000000 /** Non-fatal error messages indicating that parts of an action failed but processing may go on if one accepts deviations from the desired result. SORRY may also be the severity for incidents which are severe enough for FAILURE but happen within already started irrevocable actions, like ISO image generation. A precondition for such a severity ease is that the action can be continued after the incident. See below MISHAP for what xorriso would need instead of this kind of SORRY and generates for itself in case of libisofs image generation. E.g.: A pattern yields no result. A speed setting cannot be made. A libisofs input file is inaccessible during image generation. After SORRY a function should try to go on if that makes any sense and if no threshold prescribes abort on SORRY. The function should nevertheless indicate some failure in its return value. It should - but it does not have to. */ #define LIBISO_MSGS_SEV_SORRY 0x60000000 /** A FAILURE (see below) which can be tolerated during long lasting operations just because they cannot simply be stopped or revoked. xorriso converts libisofs SORRY messages issued during image generation into MISHAP messages in order to allow its evaluators to distinguish image generation problems from minor image composition problems. E.g.: A libisofs input file is inaccessible during image generation. After a MISHAP a function should behave like after SORRY. */ #define LIBISO_MSGS_SEV_MISHAP 0x64000000 /** Non-fatal error indicating that an important part of an action failed and that only a new setup of preconditions will give hope for sufficient success. E.g.: No media is inserted in the output drive. No write mode can be found for inserted media. A libisofs input file is inaccessible during grafting. After FAILURE a function should end with a return value indicating failure. It is at the discretion of the function whether it ends immediately in any case or whether it tries to go on if the eventual threshold allows. */ #define LIBISO_MSGS_SEV_FAILURE 0x68000000 /** An error message which puts the whole operation of the program in question E.g.: Not enough memory for essential temporary objects. Irregular errors from resources. Programming errors (soft assert). After FATAL a function should end very soon with a return value indicating severe failure. */ #define LIBISO_MSGS_SEV_FATAL 0x70000000 /** A message from an abort handler which will finally finish libburn */ #define LIBISO_MSGS_SEV_ABORT 0x71000000 /** A severity to exclude or discard any possible message. Do not use this severity for submitting. */ #define LIBISO_MSGS_SEV_NEVER 0x7fffffff /* Registered Priorities */ /* Priorities are to be selected by the programmers and not by the user. */ #define LIBISO_MSGS_PRIO_ZERO 0x00000000 #define LIBISO_MSGS_PRIO_LOW 0x10000000 #define LIBISO_MSGS_PRIO_MEDIUM 0x20000000 #define LIBISO_MSGS_PRIO_HIGH 0x30000000 #define LIBISO_MSGS_PRIO_TOP 0x7ffffffe /* Do not use this priority for submitting */ #define LIBISO_MSGS_PRIO_NEVER 0x7fffffff /* Origin numbers of libburn drives may range from 0 to 1048575 */ #define LIBISO_MSGS_ORIGIN_DRIVE_BASE 0 #define LIBISO_MSGS_ORIGIN_DRIVE_TOP 0xfffff /* Origin numbers of libisofs images may range from 1048575 to 2097152 */ #define LIBISO_MSGS_ORIGIN_IMAGE_BASE 0x100000 #define LIBISO_MSGS_ORIGIN_IMAGE_TOP 0x1fffff /* Public Functions */ /* Calls initiated from inside the direct owner (e.g. from libburn) */ /** Create new empty message handling facility with queue and issue a first official reference to it. @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success, <=0 failure */ int libiso_msgs_new(struct libiso_msgs **m, int flag); /** Destroy a message handling facility and all its eventual messages. The submitted pointer gets set to NULL. Actually only the last destroy call of all official references to the object will really dispose it. All others just decrement the reference counter. Call this function only with official reference pointers obtained by libiso_msgs_new() or libiso_msgs_refer(), and only once per such pointer. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 for success, 0 for pointer to NULL, -1 for fatal error */ int libiso_msgs_destroy(struct libiso_msgs **m, int flag); /** Create an official reference to an existing libiso_msgs object. The references keep the object alive at least until it is released by a matching number of destroy calls. So each reference MUST be revoked by exactly one call to libiso_msgs_destroy(). @param pt The pointer to be set and registered @param m A pointer to the existing object @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 for success, 0 for failure */ int libiso_msgs_refer(struct libiso_msgs **pt, struct libiso_msgs *o, int flag); /** Submit a message to a message handling facility. @param origin program specific identification number of the originator of a message. E.g. drive number. Programs should have an own range of origin numbers. See above LIBISO_MSGS_ORIGIN_*_BASE Use -1 if no number is known. @param error_code Unique error code. Use only registered codes. See below. The same unique error_code may be issued at different occasions but those should be equivalent out of the view of a libiso_msgs application. (E.g. "cannot open ATA drive" versus "cannot open SCSI drive" would be equivalent.) @param severity The LIBISO_MSGS_SEV_* of the event. @param priority The LIBISO_MSGS_PRIO_* number of the event. @param msg_text Printable and human readable message text. @param os_errno Eventual error code from operating system (0 if none) @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 on success, 0 on rejection, <0 for severe errors */ int libiso_msgs_submit(struct libiso_msgs *m, int origin, int error_code, int severity, int priority, char *msg_text, int os_errno, int flag); /* Calls from applications (to be forwarded by direct owner) */ /** Convert a registered severity number into a severity name @param flag Bitfield for control purposes: bit0= list all severity names in a blank separated string @return >0 success, <=0 failure */ int libiso_msgs__sev_to_text(int severity, char **severity_name, int flag); /** Convert a severity name into a severity number, @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success, <=0 failure */ int libiso_msgs__text_to_sev(char *severity_name, int *severity, int flag); /** Set minimum severity for messages to be queued (default LIBISO_MSGS_SEV_ALL) and for messages to be printed directly to stderr (default LIBISO_MSGS_SEV_NEVER). @param print_id A text of at most 80 characters to be printed before any eventually printed message (default is "libiso: "). @param flag Bitfield for control purposes (unused yet, submit 0) @return always 1 for now */ int libiso_msgs_set_severities(struct libiso_msgs *m, int queue_severity, int print_severity, char *print_id, int flag); /** Obtain a message item that has at least the given severity and priority. Usually all older messages of lower severity are discarded then. If no item of sufficient severity was found, all others are discarded from the queue. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 if a matching item was found, 0 if not, <0 for severe errors */ int libiso_msgs_obtain(struct libiso_msgs *m, struct libiso_msgs_item **item, int severity, int priority, int flag); /** Destroy a message item obtained by libiso_msgs_obtain(). The submitted pointer gets set to NULL. Caution: Copy eventually obtained msg_text before destroying the item, if you want to use it further. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 for success, 0 for pointer to NULL, <0 for severe errors */ int libiso_msgs_destroy_item(struct libiso_msgs *m, struct libiso_msgs_item **item, int flag); /** Obtain from a message item the three application oriented components as submitted with the originating call of libiso_msgs_submit(). Caution: msg_text becomes a pointer into item, not a copy. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 on success, 0 on invalid item, <0 for servere errors */ int libiso_msgs_item_get_msg(struct libiso_msgs_item *item, int *error_code, char **msg_text, int *os_errno, int flag); /** Obtain from a message item the submitter identification submitted with the originating call of libiso_msgs_submit(). @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 on success, 0 on invalid item, <0 for servere errors */ int libiso_msgs_item_get_origin(struct libiso_msgs_item *item, double *timestamp, pid_t *process_id, int *origin, int flag); /** Obtain from a message item severity and priority as submitted with the originating call of libiso_msgs_submit(). @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 on success, 0 on invalid item, <0 for servere errors */ int libiso_msgs_item_get_rank(struct libiso_msgs_item *item, int *severity, int *priority, int flag); #ifdef LIDBAX_MSGS_________________ /* Registered Error Codes */ Format: error_code (LIBISO_MSGS_SEV_*,LIBISO_MSGS_PRIO_*) = explanation If no severity or priority are fixely associated, use "(,)". ------------------------------------------------------------------------------ Range "libiso_msgs" : 0x00000000 to 0x0000ffff 0x00000000 (ALL,ZERO) = Initial setting in new libiso_msgs_item 0x00000001 (DEBUG,ZERO) = Test error message 0x00000002 (DEBUG,ZERO) = Debugging message 0x00000003 (FATAL,HIGH) = Out of virtual memory ------------------------------------------------------------------------------ Range "elmom" : 0x00010000 to 0x0001ffff ------------------------------------------------------------------------------ Range "scdbackup" : 0x00020000 to 0x0002ffff Accessing and defending drives: 0x00020001 (SORRY,LOW) = Cannot open busy device 0x00020002 (SORRY,HIGH) = Encountered error when closing drive 0x00020003 (SORRY,HIGH) = Could not grab drive 0x00020004 (NOTE,HIGH) = Opened O_EXCL scsi sibling 0x00020005 (SORRY,HIGH) = Failed to open device 0x00020006 (FATAL,HIGH) = Too many scsi siblings 0x00020007 (NOTE,HIGH) = Closed O_EXCL scsi siblings 0x00020008 (SORRY,HIGH) = Device busy. Failed to fcntl-lock 0x00020009 (SORRY,HIGH) = Neither stdio-path nor its directory exist General library operations: 0x00020101 (WARNING,HIGH) = Cannot find given worker item 0x00020102 (SORRY,HIGH) = A drive operation is still going on 0x00020103 (WARNING,HIGH) = After scan a drive operation is still going on 0x00020104 (SORRY,HIGH) = NULL pointer caught 0x00020105 (SORRY,HIGH) = Drive is already released 0x00020106 (SORRY,HIGH) = Drive is busy on attempt to close 0x00020107 (WARNING,HIGH) = A drive is still busy on shutdown of library 0x00020108 (SORRY,HIGH) = Drive is not grabbed on disc status inquiry 0x00020108 (FATAL,HIGH) = Could not allocate new drive object 0x00020109 (FATAL,HIGH) = Library not running 0x0002010a (FATAL,HIGH) = Unsuitable track mode 0x0002010b (FATAL,HIGH) = Burn run failed 0x0002010c (FATAL,HIGH) = Failed to transfer command to drive 0x0002010d (DEBUG,HIGH) = Could not inquire TOC 0x0002010e (FATAL,HIGH) = Attempt to read ATIP from ungrabbed drive 0x0002010f (DEBUG,HIGH) = SCSI error condition on command 0x00020110 (FATAL,HIGH) = Persistent drive address too long 0x00020111 (FATAL,HIGH) = Could not allocate new auxiliary object 0x00020112 (SORRY,HIGH) = Bad combination of write_type and block_type 0x00020113 (FATAL,HIGH) = Drive capabilities not inquired yet 0x00020114 (SORRY,HIGH) = Attempt to set ISRC with bad data 0x00020115 (SORRY,HIGH) = Attempt to set track mode to unusable value 0x00020116 (FATAL,HIGH) = Track mode has unusable value 0x00020117 (FATAL,HIGH) = toc_entry of drive is already in use 0x00020118 (DEBUG,HIGH) = Closing track 0x00020119 (DEBUG,HIGH) = Closing session 0x0002011a (NOTE,HIGH) = Padding up track to minimum size 0x0002011b (FATAL,HIGH) = Attempt to read track info from ungrabbed drive 0x0002011c (FATAL,HIGH) = Attempt to read track info from busy drive 0x0002011d (FATAL,HIGH) = SCSI error on write 0x0002011e (SORRY,HIGH) = Unsuitable media detected 0x0002011f (SORRY,HIGH) = Burning is restricted to a single track 0x00020120 (NOTE,HIGH) = FORMAT UNIT ignored 0x00020121 (FATAL,HIGH) = Write preparation setup failed 0x00020122 (FATAL,HIGH) = SCSI error on format_unit 0x00020123 (SORRY,HIGH) = DVD Media are unsuitable for desired track type 0x00020124 (SORRY,HIGH) = SCSI error on set_streaming 0x00020125 (SORRY,HIGH) = Write start address not supported 0x00020126 (SORRY,HIGH) = Write start address not properly aligned 0x00020127 (NOTE,HIGH) = Write start address is ... 0x00020128 (FATAL,HIGH) = Unsupported inquiry_type with mmc_get_performance 0x00020129 (SORRY,HIGH) = Will not format media type 0x0002012a (FATAL,HIGH) = Cannot inquire write mode capabilities 0x0002012b (FATAL,HIGH) = Drive offers no suitable write mode with this job 0x0002012c (SORRY,HIGH) = Too many logical tracks recorded 0x0002012d (FATAL,HIGH) = Exceeding range of permissible write addresses 0x0002012e (NOTE,HIGH) = Activated track default size 0x0002012f (SORRY,HIGH) = SAO is restricted to single fixed size session 0x00020130 (SORRY,HIGH) = Drive and media state unsuitable for blanking 0x00020131 (SORRY,HIGH) = No suitable formatting type offered by drive 0x00020132 (SORRY,HIGH) = Selected format is not suitable for libburn 0x00020133 (SORRY,HIGH) = Cannot mix data and audio in SAO mode 0x00020134 (NOTE,HIGH) = Defaulted TAO to DAO 0x00020135 (SORRY,HIGH) = Cannot perform TAO, job unsuitable for DAO 0x00020136 (SORRY,HIGH) = DAO burning restricted to single fixed size track 0x00020137 (HINT,HIGH) = TAO would be possible 0x00020138 (FATAL,HIGH) = Cannot reserve track 0x00020139 (SORRY,HIGH) = Write job parameters are unsuitable 0x0002013a (FATAL,HIGH) = No suitable media detected 0x0002013b (DEBUG,HIGH) = SCSI command indicates host or driver error 0x0002013c (SORRY,HIGH) = Malformed capabilities page 2Ah received 0x0002013d (DEBUG,LOW) = Waiting for free buffer space takes long time 0x0002013e (SORRY,HIGH) = Timeout with waiting for free buffer. Now disabled 0x0002013f (DEBUG,LOW) = Reporting total time spent with waiting for buffer 0x00020140 (FATAL,HIGH) = Drive is busy on attempt to write random access 0x00020141 (SORRY,HIGH) = Write data count not properly aligned 0x00020142 (FATAL,HIGH) = Drive is not grabbed on random access write 0x00020143 (SORRY,HIGH) = Read start address not properly aligned 0x00020144 (SORRY,HIGH) = SCSI error on read 0x00020145 (FATAL,HIGH) = Drive is busy on attempt to read data 0x00020146 (FATAL,HIGH) = Drive is a virtual placeholder 0x00020147 (SORRY,HIGH) = Cannot address start byte 0x00020148 (SORRY,HIGH) = Cannot write desired amount of data 0x00020149 (SORRY,HIGH) = Unsuitable filetype for pseudo-drive 0x0002014a (SORRY,HIGH) = Cannot read desired amount of data 0x0002014b (SORRY,HIGH) = Drive is already registered and scanned 0x0002014c (FATAL,HIGH) = Emulated drive caught in SCSI function 0x0002014d (SORRY,HIGH) = Asynchromous SCSI error 0x0002014f (SORRY,HIGH) = Timeout with asynchromous SCSI command 0x00020150 (DEBUG,LOW) = Reporting asynchronous waiting time 0x00020151 (FATAL,HIGH) = Read attempt on write-only drive 0x00020152 (FATAL,HIGH) = Cannot start fifo thread 0x00020153 (SORRY,HIGH) = Read error on fifo input 0x00020154 (NOTE,HIGH) = Forwarded input error ends output 0x00020155 (SORRY,HIGH) = Desired fifo buffer too large 0x00020156 (SORRY,HIGH) = Desired fifo buffer too small 0x00020157 (FATAL,HIGH) = burn_source is not a fifo object 0x00020158 (DEBUG,LOW) = Reporting thread disposal precautions 0x00020159 (DEBUG,HIGH) = TOC Format 0 returns inconsistent data libiso_audioxtr: 0x00020200 (SORRY,HIGH) = Cannot open audio source file 0x00020201 (SORRY,HIGH) = Audio source file has unsuitable format 0x00020202 (SORRY,HIGH) = Failed to prepare reading of audio data ------------------------------------------------------------------------------ Range "vreixo" : 0x00030000 to 0x0003ffff 0x0003ffff (FAILURE,HIGH) = Operation canceled 0x0003fffe (FATAL,HIGH) = Unknown or unexpected fatal error 0x0003fffd (FAILURE,HIGH) = Unknown or unexpected error 0x0003fffc (FATAL,HIGH) = Internal programming error 0x0003fffb (FAILURE,HIGH) = NULL pointer where NULL not allowed 0x0003fffa (FATAL,HIGH) = Memory allocation error 0x0003fff9 (FATAL,HIGH) = Interrupted by a signal 0x0003fff8 (FAILURE,HIGH) = Invalid parameter value 0x0003fff7 (FATAL,HIGH) = Cannot create a needed thread 0x0003fff6 (FAILURE,HIGH) = Write error 0x0003fff5 (FAILURE,HIGH) = Buffer read error 0x0003ffc0 (FAILURE,HIGH) = Trying to add a node already added to another dir 0x0003ffbf (FAILURE,HIGH) = Node with same name already exist 0x0003ffbe (FAILURE,HIGH) = Trying to remove a node that was not added to dir 0x0003ffbd (FAILURE,HIGH) = A requested node does not exist 0x0003ffbc (FAILURE,HIGH) = Image already bootable 0x0003ffbb (FAILURE,HIGH) = Trying to use an invalid file as boot image 0x0003ff80 (FAILURE,HIGH) = Error on file operation 0x0003ff7f (FAILURE,HIGH) = Trying to open an already opened file 0x0003ff7e (FAILURE,HIGH) = Access to file is not allowed 0x0003ff7d (FAILURE,HIGH) = Incorrect path to file 0x0003ff7c (FAILURE,HIGH) = The file does not exist in the filesystem 0x0003ff7b (FAILURE,HIGH) = Trying to read or close a file not opened 0x0003ff7a (FAILURE,HIGH) = Directory used where no dir is expected 0x0003ff79 (FAILURE,HIGH) = File read error 0x0003ff78 (FAILURE,HIGH) = Not dir used where a dir is expected 0x0003ff77 (FAILURE,HIGH) = Not symlink used where a symlink is expected 0x0003ff76 (FAILURE,HIGH) = Cannot seek to specified location 0x0003ff75 (HINT,MEDIUM) = File not supported in ECMA-119 tree and ignored 0x0003ff74 (HINT,MEDIUM) = File bigger than supported by used standard 0x0003ff73 (MISHAP,HIGH) = File read error during image creation 0x0003ff72 (HINT,MEDIUM) = Cannot convert filename to requested charset 0x0003ff71 (SORRY,HIGH) = File cannot be added to the tree 0x0003ff70 (HINT,MEDIUM) = File path breaks specification constraints 0x0003ff00 (FAILURE,HIGH) = Charset conversion error 0x0003feff (FAILURE,HIGH) = Too much files to mangle 0x0003fec0 (FAILURE,HIGH) = Wrong or damaged Primary Volume Descriptor 0x0003febf (SORRY,HIGH) = Wrong or damaged RR entry 0x0003febe (SORRY,HIGH) = Unsupported RR feature 0x0003febd (FAILURE,HIGH) = Wrong or damaged ECMA-119 0x0003febc (FAILURE,HIGH) = Unsupported ECMA-119 feature 0x0003febb (SORRY,HIGH) = Wrong or damaged El-Torito catalog 0x0003feba (SORRY,HIGH) = Unsupported El-Torito feature 0x0003feb9 (SORRY,HIGH) = Cannot patch isolinux boot image 0x0003feb8 (SORRY,HIGH) = Unsupported SUSP feature 0x0003feb7 (WARNING,HIGH) = Error on a RR entry that can be ignored 0x0003feb6 (HINT,MEDIUM) = Error on a RR entry that can be ignored 0x0003feb5 (WARNING,HIGH) = Multiple ER SUSP entries found 0x0003feb4 (HINT,MEDIUM) = Unsupported volume descriptor found 0x0003feb3 (WARNING,HIGH) = El-Torito related warning 0x0003feb2 (MISHAP,HIGH) = Image write cancelled 0x0003feb1 (WARNING,HIGH) = El-Torito image is hidden Outdated codes which may not be re-used for other purposes than re-instating them, if ever: X 0x00031001 (SORRY,HIGH) = Cannot read file (ignored) X 0x00031002 (FATAL,HIGH) = Cannot read file (operation canceled) X 0x00031000 (FATAL,HIGH) = Unsupported ISO-9660 image X 0x00031001 (HINT,MEDIUM) = Unsupported Vol Desc that will be ignored X 0x00031002 (FATAL,HIGH) = Damaged ISO-9660 image X 0x00031003 (SORRY,HIGH) = Cannot read previous image file X 0x00030101 (HINT,MEDIUM) = Unsupported SUSP entry that will be ignored X 0x00030102 (SORRY,HIGH) = Wrong/damaged SUSP entry X 0x00030103 (WARNING,MEDIUM)= Multiple SUSP ER entries where found X 0x00030111 (SORRY,HIGH) = Unsupported RR feature X 0x00030112 (SORRY,HIGH) = Error in a Rock Ridge entry X 0x00030201 (HINT,MEDIUM) = Unsupported Boot Vol Desc that will be ignored X 0x00030202 (SORRY,HIGH) = Wrong El-Torito catalog X 0x00030203 (HINT,MEDIUM) = Unsupported El-Torito feature X 0x00030204 (SORRY,HIGH) = Invalid file to be an El-Torito image X 0x00030205 (WARNING,MEDIUM)= Cannot properly patch isolinux image X 0x00030206 (WARNING,MEDIUM)= Copying El-Torito from a previous image without X enough info about it X 0x00030301 (NOTE,MEDIUM) = Unsupported file type for Joliet tree ------------------------------------------------------------------------------ Range "application" : 0x00040000 to 0x0004ffff 0x00040000 (ABORT,HIGH) : Application supplied message 0x00040001 (FATAL,HIGH) : Application supplied message 0x00040002 (SORRY,HIGH) : Application supplied message 0x00040003 (WARNING,HIGH) : Application supplied message 0x00040004 (HINT,HIGH) : Application supplied message 0x00040005 (NOTE,HIGH) : Application supplied message 0x00040006 (UPDATE,HIGH) : Application supplied message 0x00040007 (DEBUG,HIGH) : Application supplied message 0x00040008 (*,HIGH) : Application supplied message ------------------------------------------------------------------------------ Range "libisofs-xorriso" : 0x00050000 to 0x0005ffff This is an alternative representation of libisofs.so.6 error codes in xorriso. If values returned by iso_error_get_code() do not fit into 0x30000 to 0x3ffff then they get truncated to 16 bit and mapped into this range. (This should never need to happen, of course.) ------------------------------------------------------------------------------ Range "libisoburn" : 0x00060000 to 0x00006ffff 0x00060000 (*,*) : Message which shall be attributed to libisoburn >>> the messages of libisoburn need to be registered individually ------------------------------------------------------------------------------ #endif /* LIDBAX_MSGS_________________ */ #ifdef LIBISO_MSGS_H_INTERNAL /* Internal Functions */ /** Lock before doing side effect operations on m */ static int libiso_msgs_lock(struct libiso_msgs *m, int flag); /** Unlock after effect operations on m are done */ static int libiso_msgs_unlock(struct libiso_msgs *m, int flag); /** Create new empty message item. @param link Previous item in queue @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success, <=0 failure */ static int libiso_msgs_item_new(struct libiso_msgs_item **item, struct libiso_msgs_item *link, int flag); /** Destroy a message item obtained by libiso_msgs_obtain(). The submitted pointer gets set to NULL. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 for success, 0 for pointer to NULL */ static int libiso_msgs_item_destroy(struct libiso_msgs_item **item, int flag); #endif /* LIBISO_MSGS_H_INTERNAL */ #endif /* ! LIBISO_MSGS_H_INCLUDED */ #ifndef LIBISO_LIBISOFS_H_ #define LIBISO_LIBISOFS_H_ /* * Copyright (c) 2007-2008 Vreixo Formoso, Mario Danic * Copyright (c) 2009-2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /* Important: If you add a public API function then add its name to file libisofs/libisofs.ver in the node LIBISOFS6_Major.Minor.Micro with the numbers of the next release version. */ #ifdef __cplusplus extern "C" { #endif /* * * Applications must use 64 bit off_t. * E.g. on 32-bit GNU/Linux by defining * #define _LARGEFILE_SOURCE * #define _FILE_OFFSET_BITS 64 * The minimum requirement is to interface with the library by 64 bit signed * integers where libisofs.h or libisoburn.h prescribe off_t. * Failure to do so may result in surprising malfunction or memory faults. * * Application files which include libisofs/libisofs.h must provide * definitions for uint32_t and uint8_t. * This can be achieved either: * - by using autotools which will define HAVE_STDINT_H or HAVE_INTTYPES_H * according to its ./configure tests, * - or by defining the macros HAVE_STDINT_H or HAVE_INTTYPES_H according * to the local situation, * - or by appropriately defining uint32_t and uint8_t by other means, * e.g. by including inttypes.h before including libisofs.h */ #ifdef HAVE_STDINT_H #include #else #ifdef HAVE_INTTYPES_H #include #endif #endif /* * Normally this API is operated via public functions and opaque object * handles. But it also exposes several C structures which may be used to * provide custom functionality for the objects of the API. The same * structures are used for internal objects of libisofs, too. * You are not supposed to manipulate the entrails of such objects if they * are not your own custom extensions. * * See for an example IsoStream = struct iso_stream below. */ #include #include /* Because AIX defines "open" as "open64". There are struct members named "open" in libisofs.h which get affected. So all includers of libisofs.h must get included fcntl.h to see the same. */ #include /** * The following two functions and three macros are utilities to help ensuring * version match of application, compile time header, and runtime library. */ /** * These three release version numbers tell the revision of this header file * and of the API it describes. They are memorized by applications at * compile time. * They must show the same values as these symbols in ./configure.ac * LIBISOFS_MAJOR_VERSION=... * LIBISOFS_MINOR_VERSION=... * LIBISOFS_MICRO_VERSION=... * Note to anybody who does own work inside libisofs: * Any change of configure.ac or libisofs.h has to keep up this equality ! * * Before usage of these macros on your code, please read the usage discussion * below. * * @since 0.6.2 */ #define iso_lib_header_version_major 1 #define iso_lib_header_version_minor 5 #define iso_lib_header_version_micro 7 /** * Get version of the libisofs library at runtime. * NOTE: This function may be called before iso_init(). * * @since 0.6.2 */ void iso_lib_version(int *major, int *minor, int *micro); /** * Check at runtime if the library is ABI compatible with the given version. * NOTE: This function may be called before iso_init(). * * @return * 1 lib is compatible, 0 is not. * * @since 0.6.2 */ int iso_lib_is_compatible(int major, int minor, int micro); /** * Usage discussion: * * Some developers of the libburnia project have differing opinions how to * ensure the compatibility of libraries and applications. * * It is about whether to use at compile time and at runtime the version * numbers provided here. Thomas Schmitt advises to use them. Vreixo Formoso * advises to use other means. * * At compile time: * * Vreixo Formoso advises to leave proper version matching to properly * programmed checks in the the application's build system, which will * refuse compilation in case of mismatch. * * Thomas Schmitt advises to use the macros defined here for comparison with * the application's requirements of library revisions and to break compilation * in case of mismatch. * * Both advises are combinable. I.e. be master of your build system and have * #if checks in the source code of your application, nevertheless. * * At runtime (via iso_lib_is_compatible()): * * Vreixo Formoso advises to compare the application's requirements of * library revisions with the runtime library. This is to allow runtime * libraries which are young enough for the application but too old for * the lib*.h files seen at compile time. * * Thomas Schmitt advises to compare the header revisions defined here with * the runtime library. This is to enforce a strictly monotonous chain of * revisions from app to header to library, at the cost of excluding some older * libraries. * * These two advises are mutually exclusive. */ struct burn_source; /** * Context for image creation. It holds the files that will be added to image, * and several options to control libisofs behavior. * * @since 0.6.2 */ typedef struct Iso_Image IsoImage; /* * A node in the iso tree, i.e. a file that will be written to image. * * It can represent any kind of files. When needed, you can get the type with * iso_node_get_type() and cast it to the appropriate subtype. Useful macros * are provided, see below. * * @since 0.6.2 */ typedef struct Iso_Node IsoNode; /** * A directory in the iso tree. It is an special type of IsoNode and can be * casted to it in any case. * * @since 0.6.2 */ typedef struct Iso_Dir IsoDir; /** * A symbolic link in the iso tree. It is an special type of IsoNode and can be * casted to it in any case. * * @since 0.6.2 */ typedef struct Iso_Symlink IsoSymlink; /** * A regular file in the iso tree. It is an special type of IsoNode and can be * casted to it in any case. * * @since 0.6.2 */ typedef struct Iso_File IsoFile; /** * An special file in the iso tree. This is used to represent any POSIX file * other that regular files, directories or symlinks, i.e.: socket, block and * character devices, and fifos. * It is an special type of IsoNode and can be casted to it in any case. * * @since 0.6.2 */ typedef struct Iso_Special IsoSpecial; /** * The type of an IsoNode. * * When an user gets an IsoNode from an image, (s)he can use * iso_node_get_type() to get the current type of the node, and then * cast to the appropriate subtype. For example: * * ... * IsoNode *node; * res = iso_dir_iter_next(iter, &node); * if (res == 1 && iso_node_get_type(node) == LIBISO_DIR) { * IsoDir *dir = (IsoDir *)node; * ... * } * * @since 0.6.2 */ enum IsoNodeType { LIBISO_DIR, LIBISO_FILE, LIBISO_SYMLINK, LIBISO_SPECIAL, LIBISO_BOOT }; /* macros to check node type */ #define ISO_NODE_IS_DIR(n) (iso_node_get_type(n) == LIBISO_DIR) #define ISO_NODE_IS_FILE(n) (iso_node_get_type(n) == LIBISO_FILE) #define ISO_NODE_IS_SYMLINK(n) (iso_node_get_type(n) == LIBISO_SYMLINK) #define ISO_NODE_IS_SPECIAL(n) (iso_node_get_type(n) == LIBISO_SPECIAL) #define ISO_NODE_IS_BOOTCAT(n) (iso_node_get_type(n) == LIBISO_BOOT) /* macros for safe downcasting */ #define ISO_DIR(n) ((IsoDir*)(ISO_NODE_IS_DIR(n) ? n : NULL)) #define ISO_FILE(n) ((IsoFile*)(ISO_NODE_IS_FILE(n) ? n : NULL)) #define ISO_SYMLINK(n) ((IsoSymlink*)(ISO_NODE_IS_SYMLINK(n) ? n : NULL)) #define ISO_SPECIAL(n) ((IsoSpecial*)(ISO_NODE_IS_SPECIAL(n) ? n : NULL)) #define ISO_NODE(n) ((IsoNode*)n) /** * File section in an imported or emerging image. * * @since 0.6.8 */ struct iso_file_section { uint32_t block; uint32_t size; }; /* If you get here because of a compilation error like /usr/include/libisofs/libisofs.h:166: error: expected specifier-qualifier-list before 'uint32_t' then see the paragraph above about the definition of uint32_t. */ /** * Context for iterate on directory children. * @see iso_dir_get_children() * * @since 0.6.2 */ typedef struct Iso_Dir_Iter IsoDirIter; /** * It represents an El-Torito boot image. * * @since 0.6.2 */ typedef struct el_torito_boot_image ElToritoBootImage; /** * An special type of IsoNode that acts as a placeholder for an El-Torito * boot catalog. Once written, it will appear as a regular file. * * @since 0.6.2 */ typedef struct Iso_Boot IsoBoot; /** * Flag used to hide a file in the RR/ISO or Joliet tree. * * @see iso_node_set_hidden * @since 0.6.2 */ enum IsoHideNodeFlag { /** Hide the node in the ECMA-119 / RR tree */ LIBISO_HIDE_ON_RR = 1 << 0, /** Hide the node in the Joliet tree, if Joliet extension are enabled */ LIBISO_HIDE_ON_JOLIET = 1 << 1, /** Hide the node in the ISO-9660:1999 tree, if that format is enabled */ LIBISO_HIDE_ON_1999 = 1 << 2, /** Hide the node in the HFS+ tree, if that format is enabled. @since 1.2.4 */ LIBISO_HIDE_ON_HFSPLUS = 1 << 4, /** Hide the node in the FAT tree, if that format is enabled. @since 1.2.4 */ LIBISO_HIDE_ON_FAT = 1 << 5, /** With IsoNode and IsoBoot: Write data content even if the node is * not visible in any tree. * With directory nodes : Write data content of IsoNode and IsoBoot * in the directory's tree unless they are * explicitly marked LIBISO_HIDE_ON_RR * without LIBISO_HIDE_BUT_WRITE. * @since 0.6.34 */ LIBISO_HIDE_BUT_WRITE = 1 << 3 }; /** * El-Torito bootable image type. * * @since 0.6.2 */ enum eltorito_boot_media_type { ELTORITO_FLOPPY_EMUL, ELTORITO_HARD_DISC_EMUL, ELTORITO_NO_EMUL }; /** * Replace mode used when adding a node to a directory. * This controls how libisofs will act when you tried to add to a dir a file * with the same name that an existing file. * * @since 0.6.2 */ enum iso_replace_mode { /** * Never replace an existing node, and instead fail with * ISO_NODE_NAME_NOT_UNIQUE. */ ISO_REPLACE_NEVER, /** * Always replace the old node with the new. */ ISO_REPLACE_ALWAYS, /** * Replace with the new node if it is the same file type */ ISO_REPLACE_IF_SAME_TYPE, /** * Replace with the new node if it is the same file type and its ctime * is newer than the old one. */ ISO_REPLACE_IF_SAME_TYPE_AND_NEWER, /** * Replace with the new node if its ctime is newer than the old one. */ ISO_REPLACE_IF_NEWER /* * TODO #00006 define more values * -if both are dirs, add contents (and what to do with conflicts?) */ }; /** * Options for image writing. * @see iso_write_opts_new() * @since 0.6.2 */ typedef struct iso_write_opts IsoWriteOpts; /** * Options for image reading or import. * @see iso_read_opts_new() * @since 0.6.2 */ typedef struct iso_read_opts IsoReadOpts; /** * Source for image reading. * * @see struct iso_data_source * @since 0.6.2 */ typedef struct iso_data_source IsoDataSource; /** * Data source used by libisofs for reading an existing image. * * It offers homogeneous read access to arbitrary blocks to different sources * for images, such as .iso files, CD/DVD drives, etc... * * To create a multisession image, libisofs needs a IsoDataSource, that the * user must provide. The function iso_data_source_new_from_file() constructs * an IsoDataSource that uses POSIX I/O functions to access data. You can use * it with regular .iso images, and also with block devices that represent a * drive. * * @since 0.6.2 */ struct iso_data_source { /* reserved for future usage, set to 0 */ int version; /** * Reference count for the data source. Should be 1 when a new source * is created. Don't access it directly, but with iso_data_source_ref() * and iso_data_source_unref() functions. */ unsigned int refcount; /** * Opens the given source. You must open() the source before any attempt * to read data from it. The open is the right place for grabbing the * underlying resources. * * @return * 1 if success, < 0 on error (has to be a valid libisofs error code) */ int (*open)(IsoDataSource *src); /** * Close a given source, freeing all system resources previously grabbed in * open(). * * @return * 1 if success, < 0 on error (has to be a valid libisofs error code) */ int (*close)(IsoDataSource *src); /** * Read an arbitrary block (2048 bytes) of data from the source. * * @param lba * Block to be read. * @param buffer * Buffer where the data will be written. It should have at least * 2048 bytes. * @return * 1 if success, * < 0 if error. This function has to emit a valid libisofs error code. * Predefined (but not mandatory) for this purpose are: * ISO_DATA_SOURCE_SORRY , ISO_DATA_SOURCE_MISHAP, * ISO_DATA_SOURCE_FAILURE , ISO_DATA_SOURCE_FATAL */ int (*read_block)(IsoDataSource *src, uint32_t lba, uint8_t *buffer); /** * Clean up the source specific data. Never call this directly, it is * automatically called by iso_data_source_unref() when refcount reach * 0. */ void (*free_data)(IsoDataSource *src); /** Source specific data */ void *data; }; /** * Return information for image. This is optionally allocated by libisofs, * as a way to inform user about the features of an existing image, such as * extensions present, size, ... * * @see iso_image_import() * @since 0.6.2 */ typedef struct iso_read_image_features IsoReadImageFeatures; /** * POSIX abstraction for source files. * * @see struct iso_file_source * @since 0.6.2 */ typedef struct iso_file_source IsoFileSource; /** * Abstract for source filesystems. * * @see struct iso_filesystem * @since 0.6.2 */ typedef struct iso_filesystem IsoFilesystem; /** * Interface that defines the operations (methods) available for an * IsoFileSource. * * @see struct IsoFileSource_Iface * @since 0.6.2 */ typedef struct IsoFileSource_Iface IsoFileSourceIface; /** * IsoFilesystem implementation to deal with ISO images, and to offer a way to * access specific information of the image, such as several volume attributes, * extensions being used, El-Torito artifacts... * * @since 0.6.2 */ typedef IsoFilesystem IsoImageFilesystem; /** * See IsoFilesystem->get_id() for info about this. * @since 0.6.2 */ extern unsigned int iso_fs_global_id; /** * An IsoFilesystem is a handler for a source of files, or a "filesystem". * That is defined as a set of files that are organized in a hierarchical * structure. * * A filesystem allows libisofs to access files from several sources in * an homogeneous way, thus abstracting the underlying operations needed to * access and read file contents. Note that this doesn't need to be tied * to the disc filesystem used in the partition being accessed. For example, * we have an IsoFilesystem implementation to access any mounted filesystem, * using standard POSIX functions. It is also legal, of course, to implement * an IsoFilesystem to deal with a specific filesystem over raw partitions. * That is what we do, for example, to access an ISO Image. * * Each file inside an IsoFilesystem is represented as an IsoFileSource object, * that defines POSIX-like interface for accessing files. * * @since 0.6.2 */ struct iso_filesystem { /** * Type of filesystem. * "file" -> local filesystem * "iso " -> iso image filesystem */ char type[4]; /* reserved for future usage, set to 0 */ int version; /** * Get the root of a filesystem. * * @return * 1 on success, < 0 on error (has to be a valid libisofs error code) */ int (*get_root)(IsoFilesystem *fs, IsoFileSource **root); /** * Retrieve a file from its absolute path inside the filesystem. * @param file * Returns a pointer to a IsoFileSource object representing the * file. It has to be disposed by iso_file_source_unref() when * no longer needed. * @return * 1 success, < 0 error (has to be a valid libisofs error code) * Error codes: * ISO_FILE_ACCESS_DENIED * ISO_FILE_BAD_PATH * ISO_FILE_DOESNT_EXIST * ISO_OUT_OF_MEM * ISO_FILE_ERROR * ISO_NULL_POINTER */ int (*get_by_path)(IsoFilesystem *fs, const char *path, IsoFileSource **file); /** * Get filesystem identifier. * * If the filesystem is able to generate correct values of the st_dev * and st_ino fields for the struct stat of each file, this should * return an unique number, greater than 0. * * To get a identifier for your filesystem implementation you should * use iso_fs_global_id, incrementing it by one each time. * * Otherwise, if you can't ensure values in the struct stat are valid, * this should return 0. */ unsigned int (*get_id)(IsoFilesystem *fs); /** * Opens the filesystem for several read operations. Calling this function * is not needed at all, each time that the underlying system resource * needs to be accessed, it is opened property. * However, if you plan to execute several operations on the filesystem, * it is a good idea to open it previously, to prevent several open/close * operations to occur. * * @return 1 on success, < 0 on error (has to be a valid libisofs error code) */ int (*open)(IsoFilesystem *fs); /** * Close the filesystem, thus freeing all system resources. You should * call this function if you have previously open() it. * Note that you can open()/close() a filesystem several times. * * @return 1 on success, < 0 on error (has to be a valid libisofs error code) */ int (*close)(IsoFilesystem *fs); /** * Free implementation specific data. Should never be called by user. * Use iso_filesystem_unref() instead. */ void (*free)(IsoFilesystem *fs); /* internal usage, do never access them directly */ unsigned int refcount; void *data; }; /** * Interface definition for an IsoFileSource. Defines the POSIX-like function * to access files and abstract underlying source. * * @since 0.6.2 */ struct IsoFileSource_Iface { /** * Tells the version of the interface: * Version 0 provides functions up to (*lseek)(). * @since 0.6.2 * Version 1 additionally provides function *(get_aa_string)(). * @since 0.6.14 * Version 2 additionally provides function *(clone_src)(). * @since 1.0.2 */ int version; /** * Get the absolute path in the filesystem this file source belongs to. * * @return * the path of the FileSource inside the filesystem, it should be * freed when no more needed. */ char* (*get_path)(IsoFileSource *src); /** * Get the name of the file, with the dir component of the path. * * @return * the name of the file, it should be freed when no more needed. */ char* (*get_name)(IsoFileSource *src); /** * Get information about the file. It is equivalent to lstat(2). * * @return * 1 success, < 0 error (has to be a valid libisofs error code) * Error codes: * ISO_FILE_ACCESS_DENIED * ISO_FILE_BAD_PATH * ISO_FILE_DOESNT_EXIST * ISO_OUT_OF_MEM * ISO_FILE_ERROR * ISO_NULL_POINTER */ int (*lstat)(IsoFileSource *src, struct stat *info); /** * Get information about the file. If the file is a symlink, the info * returned refers to the destination. It is equivalent to stat(2). * * @return * 1 success, < 0 error * Error codes: * ISO_FILE_ACCESS_DENIED * ISO_FILE_BAD_PATH * ISO_FILE_DOESNT_EXIST * ISO_OUT_OF_MEM * ISO_FILE_ERROR * ISO_NULL_POINTER */ int (*stat)(IsoFileSource *src, struct stat *info); /** * Check if the process has access to read file contents. Note that this * is not necessarily related with (l)stat functions. For example, in a * filesystem implementation to deal with an ISO image, if the user has * read access to the image it will be able to read all files inside it, * despite of the particular permission of each file in the RR tree, that * are what the above functions return. * * @return * 1 if process has read access, < 0 on error (has to be a valid * libisofs error code) * Error codes: * ISO_FILE_ACCESS_DENIED * ISO_FILE_BAD_PATH * ISO_FILE_DOESNT_EXIST * ISO_OUT_OF_MEM * ISO_FILE_ERROR * ISO_NULL_POINTER */ int (*access)(IsoFileSource *src); /** * Opens the source. * @return 1 on success, < 0 on error (has to be a valid libisofs error code) * Error codes: * ISO_FILE_ALREADY_OPENED * ISO_FILE_ACCESS_DENIED * ISO_FILE_BAD_PATH * ISO_FILE_DOESNT_EXIST * ISO_OUT_OF_MEM * ISO_FILE_ERROR * ISO_NULL_POINTER */ int (*open)(IsoFileSource *src); /** * Close a previously opened file * @return 1 on success, < 0 on error * Error codes: * ISO_FILE_ERROR * ISO_NULL_POINTER * ISO_FILE_NOT_OPENED */ int (*close)(IsoFileSource *src); /** * Attempts to read up to count bytes from the given source into * the buffer starting at buf. * * The file src must be open() before calling this, and close() when no * more needed. Not valid for dirs. On symlinks it reads the destination * file. * * @return * number of bytes read, 0 if EOF, < 0 on error (has to be a valid * libisofs error code) * Error codes: * ISO_FILE_ERROR * ISO_NULL_POINTER * ISO_FILE_NOT_OPENED * ISO_WRONG_ARG_VALUE -> if count == 0 * ISO_FILE_IS_DIR * ISO_OUT_OF_MEM * ISO_INTERRUPTED */ int (*read)(IsoFileSource *src, void *buf, size_t count); /** * Read a directory. * * Each call to this function will return a new children, until we reach * the end of file (i.e, no more children), in that case it returns 0. * * The dir must be open() before calling this, and close() when no more * needed. Only valid for dirs. * * Note that "." and ".." children MUST NOT BE returned. * * @param child * pointer to be filled with the given child. Undefined on error or OEF * @return * 1 on success, 0 if EOF (no more children), < 0 on error (has to be * a valid libisofs error code) * Error codes: * ISO_FILE_ERROR * ISO_NULL_POINTER * ISO_FILE_NOT_OPENED * ISO_FILE_IS_NOT_DIR * ISO_OUT_OF_MEM */ int (*readdir)(IsoFileSource *src, IsoFileSource **child); /** * Read the destination of a symlink. You don't need to open the file * to call this. * * @param buf * allocated buffer of at least bufsiz bytes. * The dest. will be copied there, and it will be NULL-terminated * @param bufsiz * characters to be copied. Destination link will be truncated if * it is larger than given size. This include the 0x0 character. * @return * 1 on success, < 0 on error (has to be a valid libisofs error code) * Error codes: * ISO_FILE_ERROR * ISO_NULL_POINTER * ISO_WRONG_ARG_VALUE -> if bufsiz <= 0 * ISO_FILE_IS_NOT_SYMLINK * ISO_OUT_OF_MEM * ISO_FILE_BAD_PATH * ISO_FILE_DOESNT_EXIST * */ int (*readlink)(IsoFileSource *src, char *buf, size_t bufsiz); /** * Get the filesystem for this source. No extra ref is added, so you * must not unref the IsoFilesystem. * * @return * The filesystem, NULL on error */ IsoFilesystem* (*get_filesystem)(IsoFileSource *src); /** * Free implementation specific data. Should never be called by user. * Use iso_file_source_unref() instead. */ void (*free)(IsoFileSource *src); /** * Repositions the offset of the IsoFileSource (must be opened) to the * given offset according to the value of flag. * * @param offset * in bytes * @param flag * 0 The offset is set to offset bytes (SEEK_SET) * 1 The offset is set to its current location plus offset bytes * (SEEK_CUR) * 2 The offset is set to the size of the file plus offset bytes * (SEEK_END). * @return * Absolute offset position of the file, or < 0 on error. Cast the * returning value to int to get a valid libisofs error. * * @since 0.6.4 */ off_t (*lseek)(IsoFileSource *src, off_t offset, int flag); /* Add-ons of .version 1 begin here */ /** * Valid only if .version is > 0. See above. * Get the AAIP string with encoded ACL and xattr. * (Not to be confused with ECMA-119 Extended Attributes). * * bit1 and bit2 of flag should be implemented so that freshly fetched * info does not include the undesired ACL or xattr. Nevertheless if the * aa_string is cached, then it is permissible that ACL and xattr are still * delivered. * * @param flag Bitfield for control purposes * bit0= Transfer ownership of AAIP string data. * src will free the possibly cached data and might * not be able to produce it again. * bit1= No need to get ACL (no guarantee of exclusion) * bit2= No need to get xattr (no guarantee of exclusion) * bit3= if not bit2: import all xattr namespaces from * local filesystem, not only "user." * bit4= Try to get Linux-like file attribute flags * (chattr) as "isofs.fa" * @since 1.5.8 * bit5= With bit4: Ignore non-settable Linux-like file * attribute flags and do not record "isofs.fa" * if the other flags are all zero * @since 1.5.8 * @param aa_string Returns a pointer to the AAIP string data. If no AAIP * string is available, *aa_string becomes NULL. * (See doc/susp_aaip_*_*.txt for the meaning of AAIP and * libisofs/aaip_0_2.h for encoding and decoding.) * The caller is responsible for finally calling free() * on non-NULL results. * @return 1 means success (*aa_string == NULL is possible) * 2 means success, but it is possible that attributes * exist in non-user namespaces which could not be * explored due to lack of permission. * @since 1.5.0 * <0 means failure and must b a valid libisofs error code * (e.g. ISO_FILE_ERROR if no better one can be found). * @since 0.6.14 */ int (*get_aa_string)(IsoFileSource *src, unsigned char **aa_string, int flag); /** * Produce a copy of a source. It must be possible to operate both source * objects concurrently. * * @param old_src * The existing source object to be copied * @param new_stream * Will return a pointer to the copy * @param flag * Bitfield for control purposes. Submit 0 for now. * The function shall return ISO_STREAM_NO_CLONE on unknown flag bits. * * @since 1.0.2 * Present if .version is 2 or higher. */ int (*clone_src)(IsoFileSource *old_src, IsoFileSource **new_src, int flag); /* * TODO #00004 Add a get_mime_type() function. * This can be useful for GUI apps, to choose the icon of the file */ }; #ifndef __cplusplus #ifndef Libisofs_h_as_cpluspluS /** * An IsoFile Source is a POSIX abstraction of a file. * * @since 0.6.2 */ struct iso_file_source { const IsoFileSourceIface *class; int refcount; void *data; }; #endif /* ! Libisofs_h_as_cpluspluS */ #endif /* ! __cplusplus */ /* A class of IsoStream is implemented by a class description * IsoStreamIface = struct IsoStream_Iface * and a structure of data storage for each instance of IsoStream. * This structure shall be known to the functions of the IsoStreamIface. * To create a custom IsoStream class: * - Define the structure of the custom instance data. * - Implement the methods which are described by the definition of * struct IsoStream_Iface (see below), * - Create a static instance of IsoStreamIface which lists the methods as * C function pointers. (Example in libisofs/stream.c : fsrc_stream_class) * To create an instance of that class: * - Allocate sizeof(IsoStream) bytes of memory and initialize it as * struct iso_stream : * - Point to the custom IsoStreamIface by member .class . * - Set member .refcount to 1. * - Let member .data point to the custom instance data. * * Regrettably the choice of the structure member name "class" makes it * impossible to implement this generic interface in C++ language directly. * If C++ is absolutely necessary then you will have to make own copies * of the public API structures. Use other names but take care to maintain * the same memory layout. */ /** * Representation of file contents. It is an stream of bytes, functionally * like a pipe. * * @since 0.6.4 */ typedef struct iso_stream IsoStream; /** * Interface that defines the operations (methods) available for an * IsoStream. * * @see struct IsoStream_Iface * @since 0.6.4 */ typedef struct IsoStream_Iface IsoStreamIface; /** * Serial number to be used when you can't get a valid id for a Stream by other * means. If you use this, both fs_id and dev_id should be set to 0. * This must be incremented each time you get a reference to it. * * @see IsoStreamIface->get_id() * @since 0.6.4 */ extern ino_t serial_id; /** * Interface definition for IsoStream methods. It is public to allow * implementation of own stream types. * The methods defined here typically make use of stream.data which points * to the individual state data of stream instances. * * @since 0.6.4 */ struct IsoStream_Iface { /* * Current version of the interface. * Version 0 (since 0.6.4) * deprecated but still valid. * Version 1 (since 0.6.8) * update_size() added. * Version 2 (since 0.6.18) * get_input_stream() added. * A filter stream must have version 2 at least. * Version 3 (since 0.6.20) * cmp_ino() added. * A filter stream should have version 3 at least. * Version 4 (since 1.0.2) * clone_stream() added. */ int version; /** * Type of Stream. * "fsrc" -> Read from file source * "cout" -> Cut out interval from disk file * "mem " -> Read from memory * "boot" -> Boot catalog * "extf" -> External filter program * "ziso" -> zisofs compression * "osiz" -> zisofs uncompression * "gzip" -> gzip compression * "pizg" -> gzip uncompression (gunzip) * "user" -> User supplied stream */ char type[4]; /** * Opens the stream. * * @return * 1 on success, 2 file greater than expected, 3 file smaller than * expected, < 0 on error (has to be a valid libisofs error code) */ int (*open)(IsoStream *stream); /** * Close the Stream. * @return * 1 on success, < 0 on error (has to be a valid libisofs error code) */ int (*close)(IsoStream *stream); /** * Get the size (in bytes) of the stream. This function should always * return the same size, even if the underlying source size changes, * unless you call update_size() method. */ off_t (*get_size)(IsoStream *stream); /** * Attempt to read up to count bytes from the given stream into * the buffer starting at buf. The implementation has to make sure that * either the full desired count of bytes is delivered or that the * next call to this function will return EOF or error. * I.e. only the last read block may be shorter than parameter count. * * The stream must be open() before calling this, and close() when no * more needed. * * @return * number of bytes read, 0 if EOF, < 0 on error (has to be a valid * libisofs error code) */ int (*read)(IsoStream *stream, void *buf, size_t count); /** * Tell whether this IsoStream can be read several times, with the same * results. For example, a regular file is repeatable, you can read it * as many times as you want. However, a pipe is not. * * @return * 1 if stream is repeatable, 0 if not, * < 0 on error (has to be a valid libisofs error code) */ int (*is_repeatable)(IsoStream *stream); /** * Get an unique identifier for the IsoStream. */ void (*get_id)(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, ino_t *ino_id); /** * Free implementation specific data. Should never be called by user. * Use iso_stream_unref() instead. */ void (*free)(IsoStream *stream); /** * Update the size of the IsoStream with the current size of the underlying * source, if the source is prone to size changes. After calling this, * get_size() shall return the new size. * This will never be called after iso_image_create_burn_source() was * called and before the image was completely written. * (The API call to update the size of all files in the image is * iso_image_update_sizes()). * * @return * 1 if ok, < 0 on error (has to be a valid libisofs error code) * * @since 0.6.8 * Present if .version is 1 or higher. */ int (*update_size)(IsoStream *stream); /** * Retrieve the input stream of a filter stream. * * @param stream * The filter stream to be inquired. * @param flag * Bitfield for control purposes. 0 means normal behavior. * @return * The input stream, if one exists. Elsewise NULL. * No extra reference to the stream shall be taken by this call. * * @since 0.6.18 * Present if .version is 2 or higher. */ IsoStream *(*get_input_stream)(IsoStream *stream, int flag); /** * Compare two streams whether they are based on the same input and will * produce the same output. If in any doubt, then this comparison should * indicate no match. A match might allow hardlinking of IsoFile objects. * * A pointer value of NULL is permissible. In this case, function * iso_stream_cmp_ino() will decide on its own. * * If not NULL, this function .cmp_ino() will be called by * iso_stream_cmp_ino() if both compared streams point to it, and if not * flag bit0 of iso_stream_cmp_ino() prevents it. * So a .cmp_ino() function must be able to compare any pair of streams * which name it as their .cmp_ino(). A fallback to iso_stream_cmp_ino(,,1) * would endanger transitivity of iso_stream_cmp_ino(,,0). * * With filter streams, the decision whether the underlying chains of * streams match, should be delegated to * iso_stream_cmp_ino(iso_stream_get_input_stream(s1, 0), * iso_stream_get_input_stream(s2, 0), 0); * * The stream.cmp_ino() function has to establish an equivalence and order * relation: * cmp_ino(A,A) == 0 * cmp_ino(A,B) == -cmp_ino(B,A) * if cmp_ino(A,B) == 0 && cmp_ino(B,C) == 0 then cmp_ino(A,C) == 0 * Most tricky is the demand for transitivity: * if cmp_ino(A,B) < 0 && cmp_ino(B,C) < 0 then cmp_ino(A,C) < 0 * * @param s1 * The first stream to compare. Expect foreign stream types. * @param s2 * The second stream to compare. Expect foreign stream types. * @return * -1 if s1 is smaller s2 , 0 if s1 matches s2 , 1 if s1 is larger s2 * * @since 0.6.20 * Present if .version is 3 or higher. */ int (*cmp_ino)(IsoStream *s1, IsoStream *s2); /** * Produce a copy of a stream. It must be possible to operate both stream * objects concurrently. * * @param old_stream * The existing stream object to be copied * @param new_stream * Will return a pointer to the copy * @param flag * Bitfield for control purposes. 0 means normal behavior. * The function shall return ISO_STREAM_NO_CLONE on unknown flag bits. * @return * 1 in case of success, or an error code < 0 * * @since 1.0.2 * Present if .version is 4 or higher. */ int (*clone_stream)(IsoStream *old_stream, IsoStream **new_stream, int flag); }; #ifndef __cplusplus #ifndef Libisofs_h_as_cpluspluS /** * Representation of file contents as a stream of bytes. * * @since 0.6.4 */ struct iso_stream { IsoStreamIface *class; int refcount; void *data; }; #endif /* ! Libisofs_h_as_cpluspluS */ #endif /* ! __cplusplus */ /** * Initialize libisofs. Before any usage of the library you must either call * this function or iso_init_with_flag(). * Only exception from this rule: iso_lib_version(), iso_lib_is_compatible(). * @return 1 on success, < 0 on error * * @since 0.6.2 */ int iso_init(); /** * Initialize libisofs. Before any usage of the library you must either call * this function or iso_init() which is equivalent to iso_init_with_flag(0). * Only exception from this rule: iso_lib_version(), iso_lib_is_compatible(). * @param flag * Bitfield for control purposes * bit0= do not set up locale by LC_* environment variables * @return 1 on success, < 0 on error * * @since 0.6.18 */ int iso_init_with_flag(int flag); /** * Finalize libisofs. * * @since 0.6.2 */ void iso_finish(); /** * Override the reply of libc function nl_langinfo(CODESET) which may or may * not give the name of the character set which is in effect for your * environment. So this call can compensate for inconsistent terminal setups. * Another use case is to choose UTF-8 as intermediate character set for a * conversion from an exotic input character set to an exotic output set. * * @param name * Name of the character set to be assumed as "local" one. * @param flag * Unused yet. Submit 0. * @return * 1 indicates success, <=0 failure * * @since 0.6.12 */ int iso_set_local_charset(char *name, int flag); /** * Obtain the local charset as currently assumed by libisofs. * The result points to internal memory. It is volatile and must not be * altered. * * @param flag * Unused yet. Submit 0. * * @since 0.6.12 */ char *iso_get_local_charset(int flag); /** * Inquire and maybe define the time which is considered to be "now" and * used for timestamps of freshly created ISO nodes and as default of * image timestamps. * If ever, this should normally be enabled and defined before iso_image_new(). * If it is disabled, time(NULL) is considered to be "now". * * @param now * Returns the "now" value and maybe submits it as definition. * @param flag * Bitfield for control purposes * bit0= *now contains the time to be set as nowtime override. Enable the override if not bit1 is set, too. * bit1= Disable the nowtime override * @return 1= *now is not overridden , 2= *now is overridden * * @since 1.5.2 */ int iso_nowtime(time_t *now, int flag); /** * Create a new image, empty. * * The image will be owned by you and should be unref() when no more needed. * * @param name * Name of the image. This will be used as volset_id and volume_id. * @param image * Location where the image pointer will be stored. * @return * 1 success, < 0 error * * @since 0.6.2 */ int iso_image_new(const char *name, IsoImage **image); /** * Control whether ACL and xattr will be imported from external filesystems * (typically the local POSIX filesystem) when new nodes get inserted. If * enabled by iso_write_opts_set_aaip() they will later be written into the * image as AAIP extension fields. * * A change of this setting does neither affect existing IsoNode objects * nor the way how ACL and xattr are handled when loading an ISO image. * The latter is controlled by iso_read_opts_set_no_aaip(). * * @param image * The image of which the behavior is to be controlled * @param what * A bit field which sets the behavior: * bit0= ignore ACLs if the external file object bears some * bit1= ignore xattr if the external file object bears some * bit2= read Linux-like file attribute flags (chattr) * if the external file object bears some. * (I.e. a do-not-ignore, because ignoring was default before) * @since 1.5.8 * bit3= if not bit1: import all xattr namespaces, not only "user." * @since 1.5.0 * bit5= with bit2: Ignore non-settable Linux-like file attribute flags * and do not record "isofs.fa" if the other flags are all zero * @since 1.5.8 * all other bits are reserved * * @since 0.6.14 */ void iso_image_set_ignore_aclea(IsoImage *image, int what); /** * Obtain the current setting of iso_image_set_ignore_aclea(). * * @param image * The image to be inquired * @return * The currently set value. * * @since 1.5.0 */ int iso_image_get_ignore_aclea(IsoImage *image); /** * Creates an IsoWriteOpts for writing an image. You should set the options * desired with the correspondent setters. * * Options by default are determined by the selected profile. Fifo size is set * by default to 2 MB. * * @param opts * Pointer to the location where the newly created IsoWriteOpts will be * stored. You should free it with iso_write_opts_free() when no more * needed. * @param profile * Default profile for image creation. For now the following values are * defined: * ---> 0 [BASIC] * No extensions are enabled, and ISO level is set to 1. Only suitable * for usage for very old and limited systems (like MS-DOS), or by a * start point from which to set your custom options. * ---> 1 [BACKUP] * POSIX compatibility for backup. Simple settings, ISO level is set to * 3 and RR extensions are enabled. Useful for backup purposes. * Note that ACL and xattr are not enabled by default. * If you enable them, expect them not to show up in the mounted image. * They will have to be retrieved by libisofs applications like xorriso. * ---> 2 [DISTRIBUTION] * Setting for information distribution. Both RR and Joliet are enabled * to maximize compatibility with most systems. Permissions are set to * default values, and timestamps to the time of recording. * @return * 1 success, < 0 error * * @since 0.6.2 */ int iso_write_opts_new(IsoWriteOpts **opts, int profile); /** * Free an IsoWriteOpts previously allocated with iso_write_opts_new(). * * @since 0.6.2 */ void iso_write_opts_free(IsoWriteOpts *opts); /** * Announce that only the image size is desired, that the struct burn_source * which is set to consume the image output stream will stay inactive, * and that the write thread will be cancelled anyway by the .cancel() method * of the struct burn_source. * This avoids to create a write thread which would begin production of the * image stream and would generate a MISHAP event when burn_source.cancel() * gets into effect. * * @param opts * The option set to be manipulated. * @param will_cancel * 0= normal image generation * 1= prepare for being canceled before image stream output is completed * @return * 1 success, < 0 error * * @since 0.6.40 */ int iso_write_opts_set_will_cancel(IsoWriteOpts *opts, int will_cancel); /** * Set the ISO-9960 level to write at. * * @param opts * The option set to be manipulated. * @param level * -> 1 for higher compatibility with old systems. With this level * filenames are restricted to 8.3 characters. * -> 2 to allow up to 31 filename characters. * -> 3 to allow files greater than 4GB * @return * 1 success, < 0 error * * @since 0.6.2 */ int iso_write_opts_set_iso_level(IsoWriteOpts *opts, int level); /** * Whether to use or not Rock Ridge extensions. * * This are standard extensions to ECMA-119, intended to add POSIX filesystem * features to ECMA-119 images. Thus, usage of this flag is highly recommended * for images used on GNU/Linux systems. With the usage of RR extension, the * resulting image will have long filenames (up to 255 characters), deeper * directory structure, POSIX permissions and owner info on files and * directories, support for symbolic links or special files... All that * attributes can be modified/set with the appropriate function. * * @param opts * The option set to be manipulated. * @param enable * 1 to enable RR extension, 0 to not add them * @return * 1 success, < 0 error * * @since 0.6.2 */ int iso_write_opts_set_rockridge(IsoWriteOpts *opts, int enable); /** * Whether to add the non-standard Joliet extension to the image. * * This extensions are heavily used in Microsoft Windows systems, so if you * plan to use your disc on such a system you should add this extension. * Usage of Joliet supplies longer filesystem length (up to 64 unicode * characters), and deeper directory structure. * * @param opts * The option set to be manipulated. * @param enable * 1 to enable Joliet extension, 0 to not add them * @return * 1 success, < 0 error * * @since 0.6.2 */ int iso_write_opts_set_joliet(IsoWriteOpts *opts, int enable); /** * Whether to add a HFS+ filesystem to the image which points to the same * file content as the other directory trees. * It will get marked by an Apple Partition Map in the System Area of the ISO * image. This may collide with data submitted by * iso_write_opts_set_system_area() * and with settings made by * el_torito_set_isolinux_options() * The first 8 bytes of the System Area get overwritten by * {0x45, 0x52, 0x08 0x00, 0xeb, 0x02, 0xff, 0xff} * which can be executed as x86 machine code without negative effects. * So if an MBR gets combined with this feature, then its first 8 bytes * should contain no essential commands. * The next blocks of 2 KiB in the System Area will be occupied by APM entries. * The first one covers the part of the ISO image before the HFS+ filesystem * metadata. The second one marks the range from HFS+ metadata to the end * of file content data. If more ISO image data follow, then a third partition * entry gets produced. Other features of libisofs might cause the need for * more APM entries. * * @param opts * The option set to be manipulated. * @param enable * 1 to enable HFS+ extension, 0 to not add HFS+ metadata and APM * @return * 1 success, < 0 error * * @since 1.2.4 */ int iso_write_opts_set_hfsplus(IsoWriteOpts *opts, int enable); /** * >>> Production of FAT32 is not implemented yet. * >>> This call exists only as preparation for implementation. * * Whether to add a FAT32 filesystem to the image which points to the same * file content as the other directory trees. * * >>> FAT32 is planned to get implemented in co-existence with HFS+ * >>> Describe impact on MBR * * @param opts * The option set to be manipulated. * @param enable * 1 to enable FAT32 extension, 0 to not add FAT metadata * @return * 1 success, < 0 error * * @since 1.2.4 */ int iso_write_opts_set_fat(IsoWriteOpts *opts, int enable); /** * Supply a serial number for the HFS+ extension of the emerging image. * * @param opts * The option set to be manipulated. * @param serial_number * 8 bytes which should be unique to the image. * If all bytes are 0, then the serial number will be generated as * random number by libisofs. This is the default setting. * @return * 1 success, < 0 error * * @since 1.2.4 */ int iso_write_opts_set_hfsp_serial_number(IsoWriteOpts *opts, uint8_t serial_number[8]); /** * Set the block size for Apple Partition Map and for HFS+. * * @param opts * The option set to be manipulated. * @param hfsp_block_size * The allocation block size to be used by the HFS+ filesystem. * 0, 512, or 2048 * @param apm_block_size * The block size to be used for and within the Apple Partition Map. * 0, 512, or 2048. * Size 512 is not compatible with options which produce GPT. * @return * 1 success, < 0 error * * @since 1.2.4 */ int iso_write_opts_set_hfsp_block_size(IsoWriteOpts *opts, int hfsp_block_size, int apm_block_size); /** * Whether to use newer ISO-9660:1999 version. * * This is the second version of ISO-9660. It allows longer filenames and has * less restrictions than old ISO-9660. However, nobody is using it so there * are no much reasons to enable this. * * @since 0.6.2 */ int iso_write_opts_set_iso1999(IsoWriteOpts *opts, int enable); /** * Control generation of non-unique inode numbers for the emerging image. * Inode numbers get written as "file serial number" with PX entries as of * RRIP-1.12. They may mark families of hardlinks. * RRIP-1.10 prescribes a PX entry without file serial number.If not overridden * by iso_write_opts_set_rrip_1_10_px_ino() there will be no file serial number * written into RRIP-1.10 images. * * Inode number generation does not affect IsoNode objects which imported their * inode numbers from the old ISO image (see iso_read_opts_set_new_inos()) * and which have not been altered since import. It rather applies to IsoNode * objects which were newly added to the image, or to IsoNode which brought no * inode number from the old image, or to IsoNode where certain properties * have been altered since image import. * * If two IsoNode are found with same imported inode number but differing * properties, then one of them will get assigned a new unique inode number. * I.e. the hardlink relation between both IsoNode objects ends. * * @param opts * The option set to be manipulated. * @param enable * 1 = Collect IsoNode objects which have identical data sources and * properties. * 0 = Generate unique inode numbers for all IsoNode objects which do not * have a valid inode number from an imported ISO image. * All other values are reserved. * * @since 0.6.20 */ int iso_write_opts_set_hardlinks(IsoWriteOpts *opts, int enable); /** * Control writing of AAIP information for ACL and xattr. * For importing ACL and xattr when inserting nodes from external filesystems * (e.g. the local POSIX filesystem) see iso_image_set_ignore_aclea(). * For loading of this information from images see iso_read_opts_set_no_aaip(). * * @param opts * The option set to be manipulated. * @param enable * 1 = write AAIP information from nodes into the image * 0 = do not write AAIP information into the image * All other values are reserved. * * @since 0.6.14 */ int iso_write_opts_set_aaip(IsoWriteOpts *opts, int enable); /** * Use this only if you need to reproduce a suboptimal behavior of older * versions of libisofs. They used address 0 for links and device files, * and the address of the Volume Descriptor Set Terminator for empty data * files. * New versions let symbolic links, device files, and empty data files point * to a dedicated block of zero-bytes after the end of the directory trees. * (Single-pass reader libarchive needs to see all directory info before * processing any data files.) * * @param opts * The option set to be manipulated. * @param enable * 1 = use the suboptimal block addresses in the range of 0 to 115. * 0 = use the address of a block after the directory tree. (Default) * * @since 1.0.2 */ int iso_write_opts_set_old_empty(IsoWriteOpts *opts, int enable); /** * Caution: This option breaks any assumptions about names that * are supported by ECMA-119 specifications. * Try to omit any translation which would make a file name compliant to the * ECMA-119 rules. This includes and exceeds omit_version_numbers, * max_37_char_filenames, no_force_dots bit0, allow_full_ascii. Further it * prevents the conversion from local character set to ASCII. * The maximum name length is given by this call. If a filename exceeds * this length or cannot be recorded untranslated for other reasons, then * image production is aborted with ISO_NAME_NEEDS_TRANSL. * Currently the length limit is 96 characters, because an ECMA-119 directory * record may at most have 254 bytes and up to 158 other bytes must fit into * the record. Probably 96 more bytes can be made free for the name in future. * @param opts * The option set to be manipulated. * @param len * 0 = disable this feature and perform name translation according to * other settings. * >0 = Omit any translation. Abort image production if a name is longer * than the given value. * -1 = Like >0. Allow maximum possible length (currently 96) * @return >=0 success, <0 failure * In case of >=0 the return value tells the effectively set len. * E.g. 96 after using len == -1. * @since 1.0.0 */ int iso_write_opts_set_untranslated_name_len(IsoWriteOpts *opts, int len); /** * Convert directory names for ECMA-119 similar to other file names, but do * not force a dot or add a version number. * This violates ECMA-119 by allowing one "." and especially ISO level 1 * by allowing DOS style 8.3 names rather than only 8 characters. * (mkisofs and its clones seem to do this violation.) * @param opts * The option set to be manipulated. * @param allow * 1= allow dots , 0= disallow dots and convert them * @return * 1 success, < 0 error * @since 1.0.0 */ int iso_write_opts_set_allow_dir_id_ext(IsoWriteOpts *opts, int allow); /** * Omit the version number (";1") at the end of the ISO-9660 identifiers. * This breaks ECMA-119 specification, but version numbers are usually not * used, so it should work on most systems. Use with caution. * @param opts * The option set to be manipulated. * @param omit * bit0= omit version number with ECMA-119 and Joliet * bit1= omit version number with Joliet alone (@since 0.6.30) * @since 0.6.2 */ int iso_write_opts_set_omit_version_numbers(IsoWriteOpts *opts, int omit); /** * Allow ISO-9660 directory hierarchy to be deeper than 8 levels. * This breaks ECMA-119 specification. Use with caution. * * @since 0.6.2 */ int iso_write_opts_set_allow_deep_paths(IsoWriteOpts *opts, int allow); /** * This call describes the directory where to store Rock Ridge relocated * directories. * If not iso_write_opts_set_allow_deep_paths(,1) is in effect, then it may * become necessary to relocate directories so that no ECMA-119 file path * has more than 8 components. These directories are grafted into either * the root directory of the ISO image or into a dedicated relocation * directory. * For Rock Ridge, the relocated directories are linked forth and back to * placeholders at their original positions in path level 8. Directories * marked by Rock Ridge entry RE are to be considered artefacts of relocation * and shall not be read into a Rock Ridge tree. Instead they are to be read * via their placeholders and their links. * For plain ECMA-119, the relocation directory and the relocated directories * are just normal directories which contain normal files and directories. * @param opts * The option set to be manipulated. * @param name * The name of the relocation directory in the root directory. Do not * prepend "/". An empty name or NULL will direct relocated directories * into the root directory. This is the default. * If the given name does not exist in the root directory when * iso_image_create_burn_source() is called, and if there are directories * at path level 8, then directory /name will be created automatically. * The name given by this call will be compared with iso_node_get_name() * of the directories in the root directory, not with the final ECMA-119 * names of those directories. * @param flags * Bitfield for control purposes. * bit0= Mark the relocation directory by a Rock Ridge RE entry, if it * gets created during iso_image_create_burn_source(). This will * make it invisible for most Rock Ridge readers. * bit1= not settable via API (used internally) * @return * 1 success, < 0 error * @since 1.2.2 */ int iso_write_opts_set_rr_reloc(IsoWriteOpts *opts, char *name, int flags); /** * Allow path in the ISO-9660 tree to have more than 255 characters. * This breaks ECMA-119 specification. Use with caution. * * @since 0.6.2 */ int iso_write_opts_set_allow_longer_paths(IsoWriteOpts *opts, int allow); /** * Allow a single file or directory identifier to have up to 37 characters. * This is larger than the 31 characters allowed by ISO level 2, and the * extra space is taken from the version number, so this also forces * omit_version_numbers. * This breaks ECMA-119 specification and could lead to buffer overflow * problems on old systems. Use with caution. * * @since 0.6.2 */ int iso_write_opts_set_max_37_char_filenames(IsoWriteOpts *opts, int allow); /** * ISO-9660 forces filenames to have a ".", that separates file name from * extension. libisofs adds it if original filename doesn't has one. Set * this to 1 to prevent this behavior. * This breaks ECMA-119 specification. Use with caution. * * @param opts * The option set to be manipulated. * @param no * bit0= no forced dot with ECMA-119 * bit1= no forced dot with Joliet (@since 0.6.30) * * @since 0.6.2 */ int iso_write_opts_set_no_force_dots(IsoWriteOpts *opts, int no); /** * Allow lowercase characters in ISO-9660 filenames. By default, only * uppercase characters, numbers and a few other characters are allowed. * This breaks ECMA-119 specification. Use with caution. * If lowercase is not allowed then those letters get mapped to uppercase * letters. * * @since 0.6.2 */ int iso_write_opts_set_allow_lowercase(IsoWriteOpts *opts, int allow); /** * Allow all 8-bit characters to appear on an ISO-9660 filename. Note * that "/" and 0x0 characters are never allowed, even in RR names. * This breaks ECMA-119 specification. Use with caution. * * @since 0.6.2 */ int iso_write_opts_set_allow_full_ascii(IsoWriteOpts *opts, int allow); /** * If not iso_write_opts_set_allow_full_ascii() is set to 1: * Allow all 7-bit characters that would be allowed by allow_full_ascii, but * map lowercase to uppercase if iso_write_opts_set_allow_lowercase() * is not set to 1. * @param opts * The option set to be manipulated. * @param allow * If not zero, then allow what is described above. * * @since 1.2.2 */ int iso_write_opts_set_allow_7bit_ascii(IsoWriteOpts *opts, int allow); /** * Allow all characters to be part of Volume and Volset identifiers on * the Primary Volume Descriptor. This breaks ISO-9660 constraints, but * should work on modern systems. * * @since 0.6.2 */ int iso_write_opts_set_relaxed_vol_atts(IsoWriteOpts *opts, int allow); /** * Allow paths in the Joliet tree to have more than 240 characters. * This breaks Joliet specification. Use with caution. * * @since 0.6.2 */ int iso_write_opts_set_joliet_longer_paths(IsoWriteOpts *opts, int allow); /** * Allow leaf names in the Joliet tree to have up to 103 characters. * Normal limit is 64. * This breaks Joliet specification. Use with caution. * * @since 1.0.6 */ int iso_write_opts_set_joliet_long_names(IsoWriteOpts *opts, int allow); /** * Use character set UTF-16BE with Joliet, which is a superset of the * actually prescribed character set UCS-2. * This breaks Joliet specification with exotic characters which would * elsewise be mapped to underscore '_'. Use with caution. * * @since 1.3.6 */ int iso_write_opts_set_joliet_utf16(IsoWriteOpts *opts, int allow); /** * Write Rock Ridge info as of specification RRIP-1.10 rather than RRIP-1.12: * signature "RRIP_1991A" rather than "IEEE_1282", field PX without file * serial number. * * @since 0.6.12 */ int iso_write_opts_set_rrip_version_1_10(IsoWriteOpts *opts, int oldvers); /** * Write field PX with file serial number (i.e. inode number) even if * iso_write_opts_set_rrip_version_1_10(,1) is in effect. * This clearly violates the RRIP-1.10 specs. But it is done by mkisofs since * a while and no widespread protest is visible in the web. * If this option is not enabled, then iso_write_opts_set_hardlinks() will * only have an effect with iso_write_opts_set_rrip_version_1_10(,0). * * @since 0.6.20 */ int iso_write_opts_set_rrip_1_10_px_ino(IsoWriteOpts *opts, int enable); /** * Write AAIP as extension according to SUSP 1.10 rather than SUSP 1.12. * I.e. without announcing it by an ER field and thus without the need * to precede the RRIP fields and the AAIP field by ES fields. * This saves 5 to 10 bytes per file and might avoid problems with readers * which dislike ER fields other than the ones for RRIP. * On the other hand, SUSP 1.12 frowns on such unannounced extensions * and prescribes ER and ES. It does this since the year 1994. * * In effect only if above iso_write_opts_set_aaip() enables writing of AAIP. * * @since 0.6.14 */ int iso_write_opts_set_aaip_susp_1_10(IsoWriteOpts *opts, int oldvers); /** * Store as ECMA-119 Directory Record timestamp the mtime of the source node * rather than the image creation time. * If storing of mtime is enabled, then the settings of * iso_write_opts_set_replace_timestamps() apply. (replace==1 will revoke, * replace==2 will override mtime by iso_write_opts_set_default_timestamp(). * * Since version 1.2.0 this may apply also to Joliet and ISO 9660:1999. To * reduce the probability of unwanted behavior changes between pre-1.2.0 and * post-1.2.0, the bits for Joliet and ISO 9660:1999 also enable ECMA-119. * The hopefully unlikely bit14 may then be used to disable mtime for ECMA-119. * * To enable mtime for all three directory trees, submit 7. * To disable this feature completely, submit 0. * * @param opts * The option set to be manipulated. * @param allow * If this parameter is negative, then mtime is enabled only for ECMA-119. * With positive numbers, the parameter is interpreted as bit field : * bit0= enable mtime for ECMA-119 * bit1= enable mtime for Joliet and ECMA-119 * bit2= enable mtime for ISO 9660:1999 and ECMA-119 * bit14= disable mtime for ECMA-119 although some of the other bits * would enable it * @since 1.2.0 * Before version 1.2.0 this applied only to ECMA-119 : * 0 stored image creation time in ECMA-119 tree. * Any other value caused storing of mtime. * Joliet and ISO 9660:1999 always stored the image creation time. * @since 0.6.12 */ int iso_write_opts_set_dir_rec_mtime(IsoWriteOpts *opts, int allow); /** * Whether to sort files based on their weight. * * @see iso_node_set_sort_weight * @since 0.6.2 */ int iso_write_opts_set_sort_files(IsoWriteOpts *opts, int sort); /** * Whether to compute and record MD5 checksums for the whole session and/or * for each single IsoFile object. The checksums represent the data as they * were written into the image output stream, not necessarily as they were * on hard disk at any point of time. * See also calls iso_image_get_session_md5() and iso_file_get_md5(). * @param opts * The option set to be manipulated. * @param session * If bit0 set: Compute session checksum * @param files * If bit0 set: Compute a checksum for each single IsoFile object which * gets its data content written into the session. Copy * checksums from files which keep their data in older * sessions. * If bit1 set: Check content stability (only with bit0). I.e. before * writing the file content into to image stream, read it * once and compute a MD5. Do a second reading for writing * into the image stream. Afterwards compare both MD5 and * issue a MISHAP event ISO_MD5_STREAM_CHANGE if they do not * match. * Such a mismatch indicates content changes between the * time point when the first MD5 reading started and the * time point when the last block was read for writing. * So there is high risk that the image stream was fed from * changing and possibly inconsistent file content. * * @since 0.6.22 */ int iso_write_opts_set_record_md5(IsoWriteOpts *opts, int session, int files); /** * Set the parameters "name" and "timestamp" for a scdbackup checksum tag. * It will be appended to the libisofs session tag if the image starts at * LBA 0 (see iso_write_opts_set_ms_block()). The scdbackup tag can be used * to verify the image by command scdbackup_verify device -auto_end. * See scdbackup/README appendix VERIFY for its inner details. * * @param opts * The option set to be manipulated. * @param name * A word of up to 80 characters. Typically volno_totalno telling * that this is volume volno of a total of totalno volumes. * @param timestamp * A string of 13 characters YYMMDD.hhmmss (e.g. A90831.190324). * A9 = 2009, B0 = 2010, B1 = 2011, ... C0 = 2020, ... * @param tag_written * Either NULL or the address of an array with at least 512 characters. * In the latter case the possibly produced scdbackup tag will be * copied to this array when the image gets written. This call sets * scdbackup_tag_written[0] = 0 to mark its preliminary invalidity. * @return * 1 indicates success, <0 is error * * @since 0.6.24 */ int iso_write_opts_set_scdbackup_tag(IsoWriteOpts *opts, char *name, char *timestamp, char *tag_written); /** * Whether to set default values for files and directory permissions, gid and * uid. All these take one of three values: 0, 1 or 2. * * If 0, the corresponding attribute will be kept as set in the IsoNode. * Unless you have changed it, it corresponds to the value on disc, so it * is suitable for backup purposes. If set to 1, the corresponding attrib. * will be changed by a default suitable value. Finally, if you set it to * 2, the attrib. will be changed with the value specified by the functioins * below. Note that for mode attributes, only the permissions are set, the * file type remains unchanged. * * @see iso_write_opts_set_default_dir_mode * @see iso_write_opts_set_default_file_mode * @see iso_write_opts_set_default_uid * @see iso_write_opts_set_default_gid * @since 0.6.2 */ int iso_write_opts_set_replace_mode(IsoWriteOpts *opts, int dir_mode, int file_mode, int uid, int gid); /** * Set the mode to use on dirs when you set the replace_mode of dirs to 2. * * @see iso_write_opts_set_replace_mode * @since 0.6.2 */ int iso_write_opts_set_default_dir_mode(IsoWriteOpts *opts, mode_t dir_mode); /** * Set the mode to use on files when you set the replace_mode of files to 2. * * @see iso_write_opts_set_replace_mode * @since 0.6.2 */ int iso_write_opts_set_default_file_mode(IsoWriteOpts *opts, mode_t file_mode); /** * Set the uid to use when you set the replace_uid to 2. * * @see iso_write_opts_set_replace_mode * @since 0.6.2 */ int iso_write_opts_set_default_uid(IsoWriteOpts *opts, uid_t uid); /** * Set the gid to use when you set the replace_gid to 2. * * @see iso_write_opts_set_replace_mode * @since 0.6.2 */ int iso_write_opts_set_default_gid(IsoWriteOpts *opts, gid_t gid); /** * 0 to use IsoNode timestamps, 1 to use recording time, 2 to use * values from timestamp field. This applies to the timestamps of Rock Ridge * and if the use of mtime is enabled by iso_write_opts_set_dir_rec_mtime(). * In the latter case, value 1 will revoke the recording of mtime, value * 2 will override mtime by iso_write_opts_set_default_timestamp(). * * @see iso_write_opts_set_default_timestamp * @since 0.6.2 */ int iso_write_opts_set_replace_timestamps(IsoWriteOpts *opts, int replace); /** * Set the timestamp to use when you set the replace_timestamps to 2. * * @see iso_write_opts_set_replace_timestamps * @since 0.6.2 */ int iso_write_opts_set_default_timestamp(IsoWriteOpts *opts, time_t timestamp); /** * Whether to always record timestamps in GMT. * * By default, libisofs stores local time information on image. You can set * this to always store timestamps converted to GMT. This prevents any * discrimination of the timezone of the image preparer by the image reader. * * It is useful if you want to hide your timezone, or you live in a timezone * that can't be represented in ECMA-119. These are timezones with an offset * from GMT greater than +13 hours, lower than -12 hours, or not a multiple * of 15 minutes. * Negative timezones (west of GMT) can trigger bugs in some operating systems * which typically appear in mounted ISO images as if the timezone shift from * GMT was applied twice (e.g. in New York 22:36 becomes 17:36). * * @since 0.6.2 */ int iso_write_opts_set_always_gmt(IsoWriteOpts *opts, int gmt); /** * Set the charset to use for the RR names of the files that will be created * on the image. * NULL to use default charset, that is the locale charset. * You can obtain the list of charsets supported on your system executing * "iconv -l" in a shell. * * @since 0.6.2 */ int iso_write_opts_set_output_charset(IsoWriteOpts *opts, const char *charset); /** * Set the type of image creation in case there was already an existing * image imported. Libisofs supports two types of creation: * stand-alone and appended. * * A stand-alone image is an image that does not need the old image any more * for being mounted by the operating system or imported by libisofs. It may * be written beginning with byte 0 of optical media or disk file objects. * There will be no distinction between files from the old image and those * which have been added by the new image generation. * * On the other side, an appended image is not self contained. It may refer * to files that stay stored in the imported existing image. * This usage model is inspired by CD multi-session. It demands that the * appended image is finally written to the same media or disk file * as the imported image at an address behind the end of that imported image. * The exact address may depend on media peculiarities and thus has to be * announced by the application via iso_write_opts_set_ms_block(). * The real address where the data will be written is under control of the * consumer of the struct burn_source which takes the output of libisofs * image generation. It may be the one announced to libisofs or an intermediate * one. Nevertheless, the image will be readable only at the announced address. * * If you have not imported a previous image by iso_image_import(), then the * image will always be a stand-alone image, as there is no previous data to * refer to. * * @param opts * The option set to be manipulated. * @param append * 1 to create an appended image, 0 for an stand-alone one. * * @since 0.6.2 */ int iso_write_opts_set_appendable(IsoWriteOpts *opts, int append); /** * Set the start block of the image. It is supposed to be the lba where the * first block of the image will be written on disc. All references inside the * ISO image will take this into account, thus providing a mountable image. * * For appendable images, that are written to a new session, you should * pass here the lba of the next writable address on disc. * * In stand alone images this is usually 0. However, you may want to * provide a different ms_block if you don't plan to burn the image in the * first session on disc, such as in some CD-Extra disc whether the data * image is written in a new session after some audio tracks. * * @since 0.6.2 */ int iso_write_opts_set_ms_block(IsoWriteOpts *opts, uint32_t ms_block); /** * Sets the buffer where to store the descriptors which shall be written * at the beginning of an overwritable media to point to the newly written * image. * This is needed if the write start address of the image is not 0. * In this case the first 64 KiB of the media have to be overwritten * by the buffer content after the session was written and the buffer * was updated by libisofs. Otherwise the new session would not be * found by operating system function mount() or by libisoburn. * (One could still mount that session if its start address is known.) * * If you do not need this information, for example because you are creating a * new image for LBA 0 or because you will create an image for a true * multisession media, just do not use this call or set buffer to NULL. * * Use cases: * * - Together with iso_write_opts_set_appendable(opts, 1) the buffer serves * for the growing of an image as done in growisofs by Andy Polyakov. * This allows appending of a new session to non-multisession media, such * as DVD+RW. The new session will refer to the data of previous sessions * on the same media. * libisoburn emulates multisession appendability on overwritable media * and disk files by performing this use case. * * - Together with iso_write_opts_set_appendable(opts, 0) the buffer allows * to write the first session on overwritable media to start addresses * other than 0. * This address must not be smaller than 32 blocks plus the possible * partition offset as defined by iso_write_opts_set_part_offset(). * libisoburn in most cases writes the first session on overwritable media * and disk files to LBA (32 + partition_offset) in order to preserve its * descriptors from the subsequent overwriting by the descriptor buffer of * later sessions. * * @param opts * The option set to be manipulated. * @param overwrite * When not NULL, it should point to at least 64KiB of memory, where * libisofs will install the contents that shall be written at the * beginning of overwritable media. * You should initialize the buffer either with 0s, or with the contents * of the first 32 blocks of the image you are growing. In most cases, * 0 is good enough. * IMPORTANT: If you use iso_write_opts_set_part_offset() then the * overwrite buffer must be larger by the offset defined there. * * @since 0.6.2 */ int iso_write_opts_set_overwrite_buf(IsoWriteOpts *opts, uint8_t *overwrite); /** * Set the size, in number of blocks, of the ring buffer used between the * writer thread and the burn_source. You have to provide at least a 32 * blocks buffer. Default value is set to 2MB, if that is ok for you, you * don't need to call this function. * * @since 0.6.2 */ int iso_write_opts_set_fifo_size(IsoWriteOpts *opts, size_t fifo_size); /* * Attach 32 kB of binary data which shall get written to the first 32 kB * of the ISO image, the ECMA-119 System Area. This space is intended for * system dependent boot software, e.g. a Master Boot Record which allows to * boot from USB sticks or hard disks. ECMA-119 makes no own assumptions or * prescriptions about the byte content. * * If system area data are given or options bit0 is set, then bit1 of * el_torito_set_isolinux_options() is automatically disabled. * * @param opts * The option set to be manipulated. * @param data * Either NULL or 32768 bytes of data. Do not submit less bytes ! * NULL means that the possibly imported System Area content of the * possibly imported ISO image is to be re-used unchanged. * Submit 32 KiB of 0-bytes to surely get an empty System Area written. * @param options * Can cause manipulations of submitted data before they get written: * bit0= Only with System area type 0 = MBR * Apply a --protective-msdos-label as of grub-mkisofs. * This means to patch bytes 446 to 512 of the system area so * that one partition is defined which begins at the second * 512-byte block of the image and ends where the image ends. * This works with and without system_area_data. * Modern GRUB2 system areas get also treated by bit14. See below. * bit1= Only with System area type 0 = MBR * Apply isohybrid MBR patching to the system area. * This works only with system area data from SYSLINUX plus an * ISOLINUX boot image as first submitted boot image * (see iso_image_set_boot_image()) and only if not bit0 is set. * bit2-7= System area type * 0= with bit0 or bit1: MBR * else: type depends on bits bit10-13: System area sub type * 1= MIPS Big Endian Volume Header * @since 0.6.38 * Submit up to 15 MIPS Big Endian boot files by * iso_image_add_mips_boot_file(). * This will overwrite the first 512 bytes of the submitted * data. * 2= DEC Boot Block for MIPS Little Endian * @since 0.6.38 * The first boot file submitted by * iso_image_add_mips_boot_file() will be activated. * This will overwrite the first 512 bytes of the submitted * data. * 3= SUN Disk Label for SUN SPARC * @since 0.6.40 * Submit up to 7 SPARC boot images by * iso_write_opts_set_partition_img() for partition numbers 2 * to 8. * This will overwrite the first 512 bytes of the submitted * data. * 4= HP-PA PALO boot sector version 4 for HP PA-RISC * @since 1.3.8 * Suitable for older PALO of e.g. Debian 4 and 5. * Submit all five parameters of iso_image_set_hppa_palo(): * cmdline, bootloader, kernel_32, kernel_64, ramdisk * 5= HP-PA PALO boot sector version 5 for HP PA-RISC * @since 1.3.8 * Suitable for newer PALO, where PALOHDRVERSION in * lib/common.h is defined as 5. * Submit all five parameters of iso_image_set_hppa_palo(): * cmdline, bootloader, kernel_32, kernel_64, ramdisk * 6= DEC Alpha SRM boot sector * @since 1.4.0 * Submit bootloader path in ISO by iso_image_set_alpha_boot(). * bit8-9= Only with System area type 0 = MBR * @since 1.0.4 * Cylinder alignment mode pads the image to make it end at a * cylinder boundary. * 0 = auto (align if bit1) * 1 = always align to cylinder boundary * 2 = never align to cylinder boundary * 3 = always align, additionally pad up and align partitions * which were appended by iso_write_opts_set_partition_img() * @since 1.2.6 * bit10-13= System area sub type * @since 1.2.4 * With type 0: * if options bit0 ... MBR with partition start at block 1 * if options bit1 ... ISOLINUX isohybrid MBR * else: * 0 = no particular sub type, use unaltered * 1 = CHRP: A single MBR partition of type 0x96 covers the * ISO image. Not compatible with any other feature * which needs to have own MBR partition entries. * 2 = generic MBR @since 1.3.8 * bit14= Only with System area type 0 = MBR * GRUB2 boot provisions: * @since 1.3.0 * Patch system area at byte 0x1b0 to 0x1b7 with * (512-block address + 4) of the first boot image file. * Little-endian 8-byte. * Is normally combined with options bit0. * Will not be in effect if options bit1 is set. * bit15= Only with System area type MBR but not with CHRP * @since 1.4.4 * Enforce MBR "bootable/active" flag. In worst case by dummy * partition of type 0x00 which occupies block 0. * bit16= "Legacy BIOS bootable" in GPT * @since 1.5.6 * If this bit is set and a GPT partition for the ISO 9660 * filesystem gets written, then set the GPT partition flags bit 2 * "Legacy BIOS bootable". * bit17= ISO not read-only * @since 1.5.6 * Do not set GPT partition flag bit 60 "read-only" for the * ISO 9660 filesystem partition, if such a partition gets * written. * @param flag * bit0 = invalidate any attached system area data. Same as data == NULL * (This re-activates possibly loaded image System Area data. * To erase those, submit 32 kB of zeros without flag bit0.) * bit1 = keep data unaltered * bit2 = keep options unaltered * @return * ISO_SUCCESS or error * @since 0.6.30 */ int iso_write_opts_set_system_area(IsoWriteOpts *opts, char data[32768], int options, int flag); /** * Set a name for the system area. This setting is ignored unless system area * type 3 "SUN Disk Label" is in effect by iso_write_opts_set_system_area(). * In this case it will replace the default text at the start of the image: * "CD-ROM Disc with Sun sparc boot created by libisofs" * * @param opts * The option set to be manipulated. * @param label * A text of up to 128 characters. * @return * ISO_SUCCESS or error * @since 0.6.40 */ int iso_write_opts_set_disc_label(IsoWriteOpts *opts, char *label); /** * Explicitly set the four timestamps of the emerging Primary Volume * Descriptor and in the volume descriptors of Joliet and ISO 9660:1999, * if those are to be generated. * Default with all parameters is 0. * * ECMA-119 defines them as: * @param opts * The option set to be manipulated. * @param vol_creation_time * When "the information in the volume was created." * A value of 0 means that the timepoint of write start is to be used. * @param vol_modification_time * When "the information in the volume was last modified." * A value of 0 means that the timepoint of write start is to be used. * @param vol_expiration_time * When "the information in the volume may be regarded as obsolete." * A value of 0 means that the information never shall expire. * @param vol_effective_time * When "the information in the volume may be used." * A value of 0 means that not such retention is intended. * @param vol_uuid * If this text is not empty, then it overrides vol_creation_time and * vol_modification_time by copying the first 16 decimal digits from * uuid, possibly padding up with decimal '1', and writing a NUL-byte * as timezone. * Other than with vol_*_time the resulting string in the ISO image * is fully predictable and free of timezone pitfalls. * It should express a reasonable time in form YYYYMMDDhhmmsscc. * The timezone will always be recorded as GMT. * E.g.: "2010040711405800" = 7 Apr 2010 11:40:58 (+0 centiseconds) * @return * ISO_SUCCESS or error * * @since 0.6.30 */ int iso_write_opts_set_pvd_times(IsoWriteOpts *opts, time_t vol_creation_time, time_t vol_modification_time, time_t vol_expiration_time, time_t vol_effective_time, char *vol_uuid); /* * Control production of a second set of volume descriptors (superblock) * and directory trees, together with a partition table in the MBR where the * first partition has non-zero start address and the others are zeroed. * The first partition stretches to the end of the whole ISO image. * The additional volume descriptor set and trees will allow to mount the * ISO image at the start of the first partition, while it is still possible * to mount it via the normal first volume descriptor set and tree at the * start of the image or storage device. * This makes few sense on optical media. But on USB sticks it creates a * conventional partition table which makes it mountable on e.g. Linux via * /dev/sdb and /dev/sdb1 alike. * IMPORTANT: When submitting memory by iso_write_opts_set_overwrite_buf() * then its size must be at least 64 KiB + partition offset. * * @param opts * The option set to be manipulated. * @param block_offset_2k * The offset of the partition start relative to device start. * This is counted in 2 kB blocks. The partition table will show the * according number of 512 byte sectors. * Default is 0 which causes no special partition table preparations. * If it is not 0 then it must not be smaller than 16. * @param secs_512_per_head * Number of 512 byte sectors per head. 1 to 63. 0=automatic. * @param heads_per_cyl * Number of heads per cylinder. 1 to 255. 0=automatic. * @return * ISO_SUCCESS or error * * @since 0.6.36 */ int iso_write_opts_set_part_offset(IsoWriteOpts *opts, uint32_t block_offset_2k, int secs_512_per_head, int heads_per_cyl); /** The minimum version of libjte to be used with this version of libisofs at compile time. The use of libjte is optional and depends on configure tests. It can be prevented by ./configure option --disable-libjte . @since 0.6.38 */ #define iso_libjte_req_major 2 #define iso_libjte_req_minor 0 #define iso_libjte_req_micro 0 /** * Associate a libjte environment object to the upcoming write run. * libjte implements Jigdo Template Extraction as of Steve McIntyre and * Richard Atterer. * The call will fail if no libjte support was enabled at compile time. * @param opts * The option set to be manipulated. * @param libjte_handle * Pointer to a struct libjte_env e.g. created by libjte_new(). * It must stay existent from the start of image generation by * iso_image_create_burn_source() until the write thread has ended. * This can be inquired by iso_image_generator_is_running(). * In order to keep the libisofs API identical with and without * libjte support the parameter type is (void *). * @return * ISO_SUCCESS or error * * @since 0.6.38 */ int iso_write_opts_attach_jte(IsoWriteOpts *opts, void *libjte_handle); /** * Remove association to a libjte environment handle. * The call will fail if no libjte support was enabled at compile time. * @param opts * The option set to be manipulated. * @param libjte_handle * If not submitted as NULL, this will return the previously set * libjte handle. * @return * ISO_SUCCESS or error * * @since 0.6.38 */ int iso_write_opts_detach_jte(IsoWriteOpts *opts, void **libjte_handle); /** * Cause a number of blocks with zero bytes to be written after the payload * data, but before the possible checksum data. Unlike libburn tail padding, * these blocks are counted as part of the image and covered by image * checksums. * A reason for such padding can be the wish to prevent the Linux read-ahead * bug by sacrificial data which still belong to image and Jigdo template. * Normally such padding would be the job of the burn program which should know * that it is needed with CD write type TAO if Linux read(2) shall be able * to read all payload blocks. * 150 blocks = 300 kB is the traditional sacrifice to the Linux kernel. * @param opts * The option set to be manipulated. * @param num_blocks * Number of extra 2 kB blocks to be written. * @return * ISO_SUCCESS or error * * @since 0.6.38 */ int iso_write_opts_set_tail_blocks(IsoWriteOpts *opts, uint32_t num_blocks); /** * The libisofs interval reader is used internally and offered by libisofs API: * @since 1.4.0 * The functions iso_write_opts_set_prep_img(), iso_write_opts_set_efi_bootp(), * and iso_write_opts_set_partition_img() accept with their flag bit0 an * interval reader description string instead of a disk path. * The API calls are iso_interval_reader_new(), iso_interval_reader_read(), * and iso_interval_reader_destroy(). * The data may be cut out and optionally partly zeroized. * * An interval reader description string has the form: * $flags:$interval:$zeroizers:$source * The component $flags modifies the further interpretation: * "local_fs" ....... demands to read from a file depicted by the path in * $source. * "imported_iso" ... demands to read from the IsoDataSource object that was * used with iso_image_import() when * iso_read_opts_keep_import_src() was enabled. * The text in $source is ignored. * The application has to ensure that reading from the * import source does not disturb production of the new * ISO session. Especially this would be the case if the * import source is the same libburn drive with a * sequential optical medium to which the new session shall * get burned. * The component $interval consists of two byte address numbers separated * by a "-" character. E.g. "0-429" means to read bytes 0 to 429. * The component $zeroizers consists of zero or more comma separated strings. * They define which part of the read data to zeroize. Byte number 0 means * the byte read from the $interval start address. * Each string may be either * "zero_mbrpt" ..... demands to zeroize bytes 446 to 509 of the read data if * bytes 510 and 511 bear the MBR signature 0x55 0xaa. * "zero_gpt" ....... demands to check for a GPT header in bytes 512 to 1023, * to zeroize it and its partition table blocks. * "zero_apm" ....... demands to check for an APM block 0 and to zeroize * its partition table blocks. But not the block 0 itself, * because it could be actually MBR x86 machine code. * $zero_start"-"$zero_end ... demands to zeroize the read-in bytes beginning * with number $zero_start and ending after $zero_end. * The component $source is the file path with "local_fs", and ignored with * "imported_iso". * Byte numbers may be scaled by a suffix out of {k,m,g,t,s,d} meaning * multiplication by {1024, 1024k, 1024m, 1024g, 2048, 512}. A scaled value * as end number depicts the last byte of the scaled range. * E.g. "0d-0d" is "0-511". * Examples: * "local_fs:0-32767:zero_mbrpt,zero_gpt,440-443:/tmp/template.iso" * "imported_iso:45056d-47103d::" */ struct iso_interval_reader; /** * Create an interval reader object. * * @param img * The IsoImage object which can provide the "imported_iso" data source. * @param path * The interval reader description string. See above. * @param ivr * Returns in case of success a pointer to the created object. * Dispose it by iso_interval_reader_destroy() when no longer needed. * @param byte_count * Returns in case of success the number of bytes in the interval. * @param flag * bit0= tolerate (src == NULL) with "imported_iso". * (Will immediately cause eof of interval input.) * @return * ISO_SUCCESS or error (which is < 0) * * @since 1.4.0 */ int iso_interval_reader_new(IsoImage *img, char *path, struct iso_interval_reader **ivr, off_t *byte_count, int flag); /** * Dispose an interval reader object. * * @param ivr * The reader object to be disposed. *ivr will be set to NULL. * @param flag * Unused yet. Submit 0. * @return * ISO_SUCCESS or error (which is < 0) * * @since 1.4.0 */ int iso_interval_reader_destroy(struct iso_interval_reader **ivr, int flag); /** * Read the next block of 2048 bytes from an interval reader object. * If end-of-input happens, the interval will get filled up with 0 bytes. * * @param ivr * The object to read from. * @param buf * Pointer to memory for filling in at least 2048 bytes. * @param buf_fill * Will in case of success return the number of valid bytes. * If this is smaller than 2048, then end-of-interval has occurred. * @param flag * Unused yet. Submit 0. * @return * ISO_SUCCESS if data were read, 0 if not, < 0 if error * * @since 1.4.0 */ int iso_interval_reader_read(struct iso_interval_reader *ivr, uint8_t *buf, int *buf_fill, int flag); /** * Copy a data file from the local filesystem into the emerging ISO image. * Mark it by an MBR partition entry as PreP partition and also cause * protective MBR partition entries before and after this partition. * Vladimir Serbinenko stated aboy PreP = PowerPC Reference Platform : * "PreP [...] refers mainly to IBM hardware. PreP boot is a partition * containing only raw ELF and having type 0x41." * * This feature is only combinable with system area type 0 * and currently not combinable with ISOLINUX isohybrid production. * It overrides --protective-msdos-label. See iso_write_opts_set_system_area(). * Only partition 4 stays available for iso_write_opts_set_partition_img(). * It is compatible with HFS+/FAT production by storing the PreP partition * before the start of the HFS+/FAT partition. * * @param opts * The option set to be manipulated. * @param image_path * File address in the local file system or instructions for interval * reader. See flag bit0. * NULL revokes production of the PreP partition. * @param flag * bit0= The path contains instructions for the interval reader. * See above. * @since 1.4.0 * All other bits are reserved for future usage. Set them to 0. * @return * ISO_SUCCESS or error * * @since 1.2.4 */ int iso_write_opts_set_prep_img(IsoWriteOpts *opts, char *image_path, int flag); /** * Copy a data file from the local filesystem into the emerging ISO image. * Mark it by an GPT partition entry as EFI System partition, and also cause * protective GPT partition entries before and after the partition. * GPT = Globally Unique Identifier Partition Table * * This feature may collide with data submitted by * iso_write_opts_set_system_area() * and with settings made by * el_torito_set_isolinux_options() * It is compatible with HFS+/FAT production by storing the EFI partition * before the start of the HFS+/FAT partition. * The GPT overwrites byte 0x0200 to 0x03ff of the system area and all * further bytes above 0x0800 which are not used by an Apple Partition Map. * * @param opts * The option set to be manipulated. * @param image_path * File address in the local file system or instructions for interval * reader. See flag bit0. * NULL revokes production of the EFI boot partition. * @param flag * bit0= The path contains instructions for the interval reader * See above. * @since 1.4.0 * All other bits are reserved for future usage. Set them to 0. * @return * ISO_SUCCESS or error * * @since 1.2.4 */ int iso_write_opts_set_efi_bootp(IsoWriteOpts *opts, char *image_path, int flag); /** * Control whether the emerging GPT gets a pseudo-randomly generated disk GUID * or whether it gets a user supplied GUID. * The partition GUIDs will be generated in a reproducible way by exoring the * little-endian 32 bit partition number with the disk GUID beginning at byte * offset 9. * * @param opts * The option set to be manipulated. * @param guid * 16 bytes of user supplied GUID. Readily byte-swapped from the text * form as prescribed by UEFI specs: * 4 byte, 2 byte, 2 byte as little-endian. * 2 byte, 6 byte as big-endian. * The upper 4 bit of guid[7] should bear the value 4 to express the * RFC 4122 version 4. Bit 7 of byte[8] should be set to 1 and bit 6 * be set to 0, in order to express the RFC 4122 variant of UUID, * where version 4 means "pseudo-random uuid". * @param mode * 0 = ignore parameter guid and produce the GPT disk GUID by a * pseudo-random algorithm. This is the default setting. * 1 = use parameter guid as GPT disk GUID * 2 = ignore parameter guid and derive the GPT disk GUID from * parameter vol_uuid of iso_write_opts_set_pvd_times(). * The 16 bytes of vol_uuid get copied and bytes 7, 8 get their * upper bits changed to comply to RFC 4122 and UEFI. * Error ISO_GPT_NO_VOL_UUID will occur if image production begins * before vol_uuid was set. * * @return * ISO_SUCCESS or ISO_BAD_GPT_GUID_MODE * * @since 1.4.6 */ int iso_write_opts_set_gpt_guid(IsoWriteOpts *opts, uint8_t guid[16], int mode); /** * Set the maximum number of SUSP CE entries and thus continuation areas. * Each continuation area can hold at most 2048 bytes of SUSP data (Rock Ridge * or AAIP). The first area can be smaller. There might be some waste at the * end of each area. * When the maximum number is exceeded during ISO filesystem production * then possibly xattr and ACL get removed or error ISO_TOO_MANY_CE gets * reported and filesystem production is prevented. * * Files with 32 or more CE entries do not show up in mounted filesystems on * Linux. So the default setting is 31 with drop mode 2. If a higher limit is * chosen and 31 gets surpassed, then a warning message gets reported. * * @param opts * The option set to be manipulated. * @param num * The maximum number of CE entries per file. * Not more than 100000 may be set here. * 0 gets silently mapped to 1, because the root directory needs one CE. * @param flag * bit0-bit3 = Drop mode: What to do with AAIP data on too many CE: * 0 = throw ISO_TOO_MANY_CE, without dropping anything * 1 = permanently drop non-isofs fattr from IsoNode and * retry filesystem production * 2 = drop ACL if dropping non-isofs fattr does not suffice * @return * ISO_SUCCESS or ISO_TOO_MANY_CE * * @since 1.5.6 */ int iso_write_opts_set_max_ce_entries(IsoWriteOpts *opts, uint32_t num, int flag); /** * Generate a pseudo-random GUID suitable for iso_write_opts_set_gpt_guid(). * * @param guid * Will be filled by 16 bytes of generated GUID. * * @since 1.4.6 */ void iso_generate_gpt_guid(uint8_t guid[16]); /** * Cause an arbitrary data file to be appended to the ISO image and to be * described by a partition table entry in an MBR or SUN Disk Label at the * start of the ISO image. * The partition entry will bear the size of the image file rounded up to * the next multiple of 2048 bytes. * MBR or SUN Disk Label are selected by iso_write_opts_set_system_area() * system area type: 0 selects MBR partition table. 3 selects a SUN partition * table with 320 kB start alignment. * * @param opts * The option set to be manipulated. * @param partition_number * Depicts the partition table entry which shall describe the * appended image. * Range with MBR: 1 to 4. 1 will cause the whole ISO image to be * unclaimable space before partition 1. * Range with SUN Disk Label: 2 to 8. * @param partition_type * The MBR partition type. E.g. FAT12 = 0x01 , FAT16 = 0x06, * Linux Native Partition = 0x83. See fdisk command L. * This parameter is ignored with SUN Disk Label. * @param image_path * File address in the local file system or instructions for interval * reader. See flag bit0. * With SUN Disk Label: an empty name causes the partition to become * a copy of the next lower partition. * @param flag * bit0= The path contains instructions for the interval reader * See above. * @since 1.4.0 * All other bits are reserved for future usage. Set them to 0. * @return * ISO_SUCCESS or error * * @since 0.6.38 */ int iso_write_opts_set_partition_img(IsoWriteOpts *opts, int partition_number, uint8_t partition_type, char *image_path, int flag); /** * Control whether partitions created by iso_write_opts_set_partition_img() * are to be represented in MBR or as GPT partitions. * * @param opts * The option set to be manipulated. * @param gpt * 0= represent as MBR partition; as GPT only if other GPT partitions * are present * 1= represent as GPT partition and cause protective MBR with a single * partition which covers the whole output data. * This may fail if other settings demand MBR partitions. * @return * ISO_SUCCESS or error * * @since 1.4.0 */ int iso_write_opts_set_appended_as_gpt(IsoWriteOpts *opts, int gpt); /** * Set the GPT Type GUID for a partition defined by * iso_write_opts_set_partition_img(). * * @param opts * The option set to be manipulated. * @param partition_number * Depicts the partition table entry which shall get the Type GUID. * @param guid * 16 bytes of user supplied GUID. Readily byte-swapped from the text * form as prescribed by UEFI specs: * 4 byte, 2 byte, 2 byte as little-endian. * 2 byte, 6 byte as big-endian. * @param valid * Set to 1 to make this Type GUID valid. * Set to 0 in order to invalidate a previously made setting. In this * case MBR type 0xEF will become the EFI Type GUID. All others will * become the Basic Data Partition Type GUID. * @return * ISO_SUCCESS or error * * @since 1.5.2 */ int iso_write_opts_set_part_type_guid(IsoWriteOpts *opts, int partition_number, uint8_t guid[16], int valid); /** * Control whether partitions created by iso_write_opts_set_partition_img() * are to be represented in Apple Partition Map. * * @param opts * The option set to be manipulated. * @param apm * 0= do not represent appended partitions in APM * 1= represent in APM, even if not * iso_write_opts_set_part_like_isohybrid() enables it and no * other APM partitions emerge. * @return * ISO_SUCCESS or error * * @since 1.4.4 */ int iso_write_opts_set_appended_as_apm(IsoWriteOpts *opts, int apm); /** * Control whether bits 2 to 8 of el_torito_set_isolinux_options() * shall apply even if not isohybrid MBR patching is enabled (bit1 of * parameter options of iso_write_opts_set_system_area()): * - Mentioning of El Torito boot images in GPT. * - Mentioning of El Torito boot images in APM. * * In this case some other behavior from isohybrid processing will apply too: * - No MBR partition of type 0xee emerges, even if GPT gets produced. * - Gaps between GPT and APM partitions will not be filled by more partitions. * * An extra feature towards isohybrid is enabled: * - Appended partitions get mentioned in APM if other APM partitions emerge. * * @param opts * The option set to be manipulated. * @param alike * 0= Apply the described behavior only with ISOLINUX isohybrid. * Do not mention appended partitions in APM unless * iso_write_opts_set_appended_as_apm() is enabled. * 1= Apply the described behavior even without ISOLINUX isohybrid. * * @return * ISO_SUCCESS or error * * @since 1.4.4 */ int iso_write_opts_set_part_like_isohybrid(IsoWriteOpts *opts, int alike); /** * Set the partition type of the MBR partition which represents the ISO * filesystem or at least protects it. * This is without effect if no such partition emerges by other settings or * if the partition type is prescribed mandatorily like 0xee for GPT protective * MBR or 0x96 for CHRP. * @param opts * The option set to be manipulated. * @param part_type * 0x00 to 0xff as desired partition type. * Any other value (e.g. -1) enables the default types of the various * occasions. * @return * ISO_SUCCESS or error * @since 1.4.8 */ int iso_write_opts_set_iso_mbr_part_type(IsoWriteOpts *opts, int part_type); /** * Set the GPT Type GUID for the partition which represents the ISO 9660 * filesystem, if such a partition emerges in GPT. * @param opts * The option set to be manipulated. * @param guid * 16 bytes of user supplied GUID. Readily byte-swapped from the text * form as prescribed by UEFI specs: * 4 byte, 2 byte, 2 byte as little-endian. * 2 byte, 6 byte as big-endian. * @param valid * Set to 1 to make this Type GUID valid. * Set to 0 in order to invalidate a previously made setting. In this * case the setting of iso_write_opts_set_iso_mbr_part_type() or its * default will get into effect. * @return * ISO_SUCCESS or error * * @since 1.5.2 */ int iso_write_opts_set_iso_type_guid(IsoWriteOpts *opts, uint8_t guid[16], int valid); /** * Inquire the start address of the file data blocks after having used * IsoWriteOpts with iso_image_create_burn_source(). * @param opts * The option set that was used when starting image creation * @param data_start * Returns the logical block address if it is already valid * @param flag * Reserved for future usage, set to 0. * @return * 1 indicates valid data_start, <0 indicates invalid data_start * * @since 0.6.16 */ int iso_write_opts_get_data_start(IsoWriteOpts *opts, uint32_t *data_start, int flag); /** * Update the sizes of all files added to image. * * This may be called just before iso_image_create_burn_source() to force * libisofs to check the file sizes again (they're already checked when added * to IsoImage). It is useful if you have changed some files after adding then * to the image. * * @return * 1 on success, < 0 on error * @since 0.6.8 */ int iso_image_update_sizes(IsoImage *image); /** * Create a burn_source and a thread which immediately begins to generate * the image. That burn_source can be used with libburn as a data source * for a track. A copy of its public declaration in libburn.h can be found * further below in this text. * * If image generation shall be aborted by the application program, then * the .cancel() method of the burn_source must be called to end the * generation thread: burn_src->cancel(burn_src); * * @param image * The image to write. * @param opts * The options for image generation. All needed data will be copied, so * you can free the given struct once this function returns. * @param burn_src * Location where the pointer to the burn_source will be stored * @return * 1 on success, < 0 on error * * @since 0.6.2 */ int iso_image_create_burn_source(IsoImage *image, IsoWriteOpts *opts, struct burn_source **burn_src); /** * Inquire whether the image generator thread is still at work. As soon as the * reply is 0, the caller of iso_image_create_burn_source() may assume that * the image generation has ended. * Nevertheless there may still be readily formatted output data pending in * the burn_source or its consumers. So the final delivery of the image has * also to be checked at the data consumer side,e.g. by burn_drive_get_status() * in case of libburn as consumer. * @param image * The image to inquire. * @return * 1 generating of image stream is still in progress * 0 generating of image stream has ended meanwhile * * @since 0.6.38 */ int iso_image_generator_is_running(IsoImage *image); /** * Creates an IsoReadOpts for reading an existent image. You should set the * options desired with the correspondent setters. Note that you may want to * set the start block value. * * Options by default are determined by the selected profile. * * @param opts * Pointer to the location where the newly created IsoReadOpts will be * stored. You should free it with iso_read_opts_free() when no more * needed. * @param profile * Default profile for image reading. For now the following values are * defined: * ---> 0 [STANDARD] * Suitable for most situations. Most extension are read. When both * Joliet and RR extension are present, RR is used. * AAIP for ACL and xattr is not enabled by default. * @return * 1 success, < 0 error * * @since 0.6.2 */ int iso_read_opts_new(IsoReadOpts **opts, int profile); /** * Free an IsoReadOpts previously allocated with iso_read_opts_new(). * * @since 0.6.2 */ void iso_read_opts_free(IsoReadOpts *opts); /** * Set the block where the image begins. It is usually 0, but may be different * on a multisession disc. * * @since 0.6.2 */ int iso_read_opts_set_start_block(IsoReadOpts *opts, uint32_t block); /** * Do not read Rock Ridge extensions. * In most cases you don't want to use this. It could be useful if RR info * is damaged, or if you want to use the Joliet tree. * * @since 0.6.2 */ int iso_read_opts_set_no_rockridge(IsoReadOpts *opts, int norr); /** * Do not read Joliet extensions. * * @since 0.6.2 */ int iso_read_opts_set_no_joliet(IsoReadOpts *opts, int nojoliet); /** * Do not read ISO 9660:1999 enhanced tree * * @since 0.6.2 */ int iso_read_opts_set_no_iso1999(IsoReadOpts *opts, int noiso1999); /** * Control reading of AAIP information about ACL and xattr when loading * existing images. * For importing ACL and xattr when inserting nodes from external filesystems * (e.g. the local POSIX filesystem) see iso_image_set_ignore_aclea(). * For writing of this information see iso_write_opts_set_aaip(). * * @param opts * The option set to be manipulated * @param noaaip * 1 = Do not read AAIP information * 0 = Read AAIP information if available * All other values are reserved. * @since 0.6.14 */ int iso_read_opts_set_no_aaip(IsoReadOpts *opts, int noaaip); /** * Control reading of an array of MD5 checksums which is possibly stored * at the end of a session. See also iso_write_opts_set_record_md5(). * Important: Loading of the MD5 array will only work if AAIP is enabled * because its position and layout is recorded in xattr "isofs.ca". * * @param opts * The option set to be manipulated * @param no_md5 * 0 = Read MD5 array if available, refuse on non-matching MD5 tags * 1 = Do not read MD5 checksum array * 2 = Read MD5 array, but do not check MD5 tags * @since 1.0.4 * All other values are reserved. * * @since 0.6.22 */ int iso_read_opts_set_no_md5(IsoReadOpts *opts, int no_md5); /** * Control discarding of inode numbers from existing images. * Such numbers may come from RRIP 1.12 entries PX. If not discarded they * get written unchanged when the file object gets written into an ISO image. * If this inode number is missing with a file in the imported image, * or if it has been discarded during image reading, then a unique inode number * will be generated at some time before the file gets written into an ISO * image. * Two image nodes which have the same inode number represent two hardlinks * of the same file object. So discarding the numbers splits hardlinks. * * @param opts * The option set to be manipulated * @param new_inos * 1 = Discard imported inode numbers and finally hand out a unique new * one to each single file before it gets written into an ISO image. * 0 = Keep imported inode numbers from PX entries. * All other values are reserved. * @since 0.6.20 */ int iso_read_opts_set_new_inos(IsoReadOpts *opts, int new_inos); /** * Whether to prefer Joliet over RR. libisofs usually prefers RR over * Joliet, as it give us much more info about files. So, if both extensions * are present, RR is used. You can set this if you prefer Joliet, but * note that this is not very recommended. This doesn't mean than RR * extensions are not read: if no Joliet is present, libisofs will read * RR tree. * * @since 0.6.2 */ int iso_read_opts_set_preferjoliet(IsoReadOpts *opts, int preferjoliet); /** * How to convert file names if neither Rock Ridge nor Joliet names * are present and acceptable. * * @param opts * The option set to be manipulated * @param ecma119_map * The conversion mode to apply: * 0 = unmapped: Take name as recorded in ECMA-119 directory record * (not suitable for writing it to a new ISO filesystem) * 1 = stripped: Like unmapped, but strip off trailing ";1" or ".;1" * 2 = uppercase: Like stripped, but map {a-z} to {A-Z} * 3 = lowercase: Like stripped, but map {A-Z} to {a-z} * @return * ISO_SUCCESS if ecma119_map was accepted * 0 if the value was out of range * < 0 if other error * * @since 1.4.2 */ int iso_read_opts_set_ecma119_map(IsoReadOpts *opts, int ecma119_map); /** * How to convert Joliet file names. * * @param opts * The option set to be manipulated * @param ecma119_map * The conversion mode to apply: * 0 = unmapped: Take name as recorded in Joliet directory record * (not suitable for writing it to a new ISO filesystem) * 1 = stripped: Strip off trailing ";1" or ".;1" * @return * ISO_SUCCESS if joliet_map was accepted * 0 if the value was out of range * < 0 if other error * * @since 1.5.4 */ int iso_read_opts_set_joliet_map(IsoReadOpts *opts, int joliet_map); /** * Set default uid for files when RR extensions are not present. * * @since 0.6.2 */ int iso_read_opts_set_default_uid(IsoReadOpts *opts, uid_t uid); /** * Set default gid for files when RR extensions are not present. * * @since 0.6.2 */ int iso_read_opts_set_default_gid(IsoReadOpts *opts, gid_t gid); /** * Set default permissions for files when RR extensions are not present. * * @param opts * The option set to be manipulated * @param file_perm * Permissions for files. * @param dir_perm * Permissions for directories. * * @since 0.6.2 */ int iso_read_opts_set_default_permissions(IsoReadOpts *opts, mode_t file_perm, mode_t dir_perm); /** * Set the input charset of the file names on the image. NULL to use locale * charset. You have to specify a charset if the image filenames are encoded * in a charset different that the local one. This could happen, for example, * if the image was created on a system with different charset. * * @param opts * The option set to be manipulated * @param charset * The charset to use as input charset. You can obtain the list of * charsets supported on your system executing "iconv -l" in a shell. * * @since 0.6.2 */ int iso_read_opts_set_input_charset(IsoReadOpts *opts, const char *charset); /** * Enable or disable methods to automatically choose an input charset. * This overrides the name set via iso_read_opts_set_input_charset() * * @param opts * The option set to be manipulated * @param mode * Bitfield for control purposes: * bit0= Allow to use the input character set name which is possibly * stored in attribute "isofs.cs" of the root directory. * Applications may attach this xattr by iso_node_set_attrs() to * the root node, call iso_write_opts_set_output_charset() with the * same name, and enable iso_write_opts_set_aaip() when writing * an image. * Submit any other bits with value 0. * * @since 0.6.18 * */ int iso_read_opts_auto_input_charset(IsoReadOpts *opts, int mode); /** * Enable or disable loading of the first 32768 bytes of the session. * * @param opts * The option set to be manipulated * @param mode * Bitfield for control purposes: * bit0= Load System Area data and attach them to the image so that they * get written by the next session, if not overridden by * iso_write_opts_set_system_area(). * Submit any other bits with value 0. * * @since 0.6.30 * */ int iso_read_opts_load_system_area(IsoReadOpts *opts, int mode); /** * Control whether to keep a reference to the IsoDataSource object which * allows access to the blocks of the imported ISO 9660 filesystem. * This is needed if the interval reader shall read from "imported_iso". * * @param opts * The option set to be manipulated * @param mode * Bitfield for control purposes: * bit0= Keep a reference to the IsoDataSource until the IsoImage object * gets disposed by its final iso_image_unref(). * Submit any other bits with value 0. * * @since 1.4.0 * */ int iso_read_opts_keep_import_src(IsoReadOpts *opts, int mode); /** * Import a previous session or image, for growing or modify. * * @param image * The image context to which old image will be imported. Note that all * files added to image, and image attributes, will be replaced with the * contents of the old image. * TODO #00025 support for merging old image files * @param src * Data Source from which old image will be read. A extra reference is * added, so you still need to iso_data_source_unref() yours. * @param opts * Options for image import. All needed data will be copied, so you * can free the given struct once this function returns. * @param features * If not NULL, a new IsoReadImageFeatures will be allocated and filled * with the features of the old image. It should be freed with * iso_read_image_features_destroy() when no more needed. You can pass * NULL if you're not interested on them. * @return * 1 on success, < 0 on error * * @since 0.6.2 */ int iso_image_import(IsoImage *image, IsoDataSource *src, IsoReadOpts *opts, IsoReadImageFeatures **features); /** * Assess features of the importable directory trees of src and an estimation * of the write options which would cause the recognized features. * This goes deeper than the feature assessment of iso_image_import(), e.g. by * inspecting file names. * * For the parameters "src", "opts", and "features" see also the description of * iso_image_import(). * * @param src * Data Source from which old image will be read. * @param opts * Options for image import. Settings about tree choice will be ignored. * @param features * Returns the pointer to a newly allocated and filled IsoReadImageFeatures * object. NULL is not allowed, other than with iso_image_import(). * If *features is returned as non-NULL, then it should be freed with * iso_read_image_features_destroy() when no more needed. * @param write_opts * Returns the pointer to a newly allocated and filled IsoWriteOpts object. * If *write_opts is returned as non-NULL, then it should be freed with * iso_write_opts_free() when no more needed. * * @return * 1 on success, < 0 on error * * @since 1.5.6 */ int iso_assess_written_features(IsoDataSource *src, IsoReadOpts *opts, IsoReadImageFeatures **features, IsoWriteOpts **write_opts); /** * Destroy an IsoReadImageFeatures object obtained with iso_image_import() or * iso_assess_written_features(). * * @since 0.6.2 */ void iso_read_image_features_destroy(IsoReadImageFeatures *f); /** * Get the size (in 2048 byte block) of the image, as reported in the PVM. * * @since 0.6.2 */ uint32_t iso_read_image_features_get_size(IsoReadImageFeatures *f); /** * Whether RockRidge extensions are present in the image imported. * * @since 0.6.2 */ int iso_read_image_features_has_rockridge(IsoReadImageFeatures *f); /** * Whether Joliet extensions are present in the image imported. * * @since 0.6.2 */ int iso_read_image_features_has_joliet(IsoReadImageFeatures *f); /** * Whether the image is recorded according to ISO 9660:1999, i.e. it has * a version 2 Enhanced Volume Descriptor. * * @since 0.6.2 */ int iso_read_image_features_has_iso1999(IsoReadImageFeatures *f); /** * Whether El-Torito boot record is present present in the image imported. * * @since 0.6.2 */ int iso_read_image_features_has_eltorito(IsoReadImageFeatures *f); /** * Tells what directory tree was loaded: * 0= ISO 9660 , 1 = Joliet , 2 = ISO 9660:1999 * * @since 1.5.4 */ int iso_read_image_features_tree_loaded(IsoReadImageFeatures *f); /** * Tells whether Rock Ridge information was used while loading the tree: * 1= yes, 0= no * * @since 1.5.4 */ int iso_read_image_features_rr_loaded(IsoReadImageFeatures *f); /** * Get a named feature as text, num_value, or pt_value depending on its type. * The set of named features includes the features which can be inquired by * own iso_read_image_features_*() functions: * size See iso_read_image_features_get_size() * rockridge See iso_read_image_features_has_rockridge() * iso_write_opts_set_rockridge() * joliet See iso_read_image_features_has_joliet() * iso_write_opts_set_joliet() * iso1999 See iso_read_image_features_has_iso1999() * iso_write_opts_set_iso1999() * eltorito See iso_read_image_features_has_eltorito() * tree_loaded See iso_read_image_features_tree_loaded() * rr_loaded See iso_read_image_features_rr_loaded() * Other named features are: * tree_loaded_text Text form of "tree_loaded": * 0="ISO9660", 1="Joliet", 2="ISO9660:1999" * aaip 1=AAIP information was seen, 0= no AAIP seen * Detected traces of potential write option settings: * iso_level See iso_write_opts_set_iso_level() * untranslated_name_len See iso_write_opts_set_untranslated_name_len() * allow_dir_id_ext See iso_write_opts_set_allow_dir_id_ext() * omit_version_numbers See iso_write_opts_set_omit_version_numbers() * allow_deep_paths See iso_write_opts_set_allow_deep_paths() * allow_longer_paths See iso_write_opts_set_allow_longer_paths() * max_37_char_filenames See iso_write_opts_set_max_37_char_filenames() * no_force_dots See iso_write_opts_set_no_force_dots() * allow_lowercase See iso_write_opts_set_allow_lowercase() * allow_full_ascii See iso_write_opts_set_allow_full_ascii() * relaxed_vol_atts See iso_write_opts_set_relaxed_vol_atts() * joliet_longer_paths See iso_write_opts_set_joliet_longer_paths() * joliet_long_names See iso_write_opts_set_joliet_long_names() * joliet_utf16 See iso_write_opts_set_joliet_utf16() * rrip_version_1_10 See iso_write_opts_set_rrip_version_1_10() * rrip_1_10_px_ino See iso_write_opts_set_rrip_1_10_px_ino() * aaip_susp_1_10 See iso_write_opts_set_aaip_susp_1_10() * record_md5_session See iso_write_opts_set_record_md5() param session * record_md5_files See iso_write_opts_set_record_md5() param files * * @param f * A features object returned by iso_image_import() or * iso_assess_written_features(). * @param name * The name of the feature to be inquired. * @param text * If text is not NULL, *text returns a textual representation of the * reply. Dispose *text by free(2) when no longer needed. * @param type * Returns which of num_value or pt_value is valid: * 0= *num_value is valid * 1= *pt_value is valid * @param num_value * Returns the numerical value of the feature if type == 0. * @param pt_value * Returns a pointer to a memory area inside the features object if type * is 1. The area is not necessarily 0-terminated. * Do _not_ dispose *pt_value and do not use it after f was disposed. * @param pt_size * Returns the size of the pt_value memory area if type is 1. * This counting includes a terminating 0-byte if it is present. * @return * 0 = Feature was not yet examined. Reply is not valid. * 1 = Reply is valid * ISO_UNDEF_READ_FEATURE = Given name is not known * <0 = other error * * @since 1.5.6 */ int iso_read_image_feature_named(IsoReadImageFeatures *f, char *name, char **text, int *type, int64_t *num_value, void **pt_value, size_t *pt_size); /** * Get all validly assessed named features as one single 0-terminated string * consisting of single lines for each feature. * * @param f * A features object returned by iso_image_import() or * iso_assess_written_features(). * @param with_values * If set to 1: return lines of form name=value\n * If set to 0: return lines of form name\n * @param feature_text * Returns the result string. Dispose by free(2) when no longer needed. * @return * 1 = result is valid, <0 indicates error * * @since 1.5.6 */ int iso_read_image_features_text(IsoReadImageFeatures *f, int with_values, char **feature_text); /** * Increments the reference counting of the given image. * * @since 0.6.2 */ void iso_image_ref(IsoImage *image); /** * Decrements the reference counting of the given image. * If it reaches 0, the image is free, together with its tree nodes (whether * their refcount reach 0 too, of course). * * @since 0.6.2 */ void iso_image_unref(IsoImage *image); /** * Attach user defined data to the image. Use this if your application needs * to store addition info together with the IsoImage. If the image already * has data attached, the old data will be freed. * * @param image * The image to which data shall be attached. * @param data * Pointer to application defined data that will be attached to the * image. You can pass NULL to remove any already attached data. * @param give_up * Function that will be called when the image does not need the data * any more. It receives the data pointer as an argument, and should free * allocated data if needed. give_up may be NULL if not needed. * @return * 1 on success, < 0 on error * * @since 0.6.2 */ int iso_image_attach_data(IsoImage *image, void *data, void (*give_up)(void*)); /** * The the data previously attached with iso_image_attach_data() * * @since 0.6.2 */ void *iso_image_get_attached_data(IsoImage *image); /** * Set the name truncation mode and the maximum name length for nodes from * image importing, creation of new IsoNode objects, and name changing image * manipulations. * * Truncated names are supposed to be nearly unique because they end by the MD5 * of the first 4095 characters of the untruncated name. One should treat them * as if they were the untruncated original names. * * For proper processing of truncated names it is necessary to use * iso_image_set_node_name() instead of iso_node_set_name() * iso_image_add_new_dir() iso_tree_add_new_dir() * iso_image_add_new_file() iso_tree_add_new_file() * iso_image_add_new_special() iso_tree_add_new_special() * iso_image_add_new_symlink() iso_tree_add_new_symlink() * iso_image_tree_clone() iso_tree_clone() * iso_image_dir_get_node() iso_dir_get_node() * iso_image_path_to_node() iso_tree_path_to_node() * * Beware of ambiguities if both, the full name and the truncated name, * exist in the same directory. Best is to only set truncation parameters * once with an ISO filesystem and to never change them later. * * If writing of AAIP is enabled, then the mode and length are recorded in * xattr "isofs.nt" of the root node. * If reading of AAIP is enabled and "isofs.nt" is found, then it gets into * effect if both, the truncate mode value from "isofs.nt" and the current * truncate mode of the IsoImage are 1, and the length is between 64 and 255. * * @param img * The image which shall be manipulated. * @param mode * 0= Do not truncate but throw error ISO_RR_NAME_TOO_LONG if a file name * is longer than parameter length. * 1= Truncate to length and overwrite the last 33 bytes of that length * by a colon ':' and the hex representation of the MD5 of the first * 4095 bytes of the whole oversized name. * Potential incomplete UTF-8 characters will get their leading bytes * replaced by '_'. * Mode 1 is the default. * @param length * Maximum byte count of a file name. Permissible values are 64 to 255. * Default is 255. * @return * ISO_SUCCESS or ISO_WRONG_ARG_VALUE * * @since 1.4.2 */ int iso_image_set_truncate_mode(IsoImage *img, int mode, int length); /** * Inquire the current setting of iso_image_set_truncate_mode(). * * @param img * The image which shall be inquired. * @param mode * Returns the mode value. * @param length * Returns the length value. * @return * ISO_SUCCESS or <0 = error * * @since 1.4.2 */ int iso_image_get_truncate_mode(IsoImage *img, int *mode, int *length); /** * Immediately apply the given truncate mode and length to the given string. * * @param mode * See iso_image_set_truncate_mode() * @param length * See iso_image_set_truncate_mode() * @param name * The string to be inspected and truncated if mode says so. * @param flag * Bitfield for control purposes. Unused yet. Submit 0. * @return * ISO_SUCCESS, ISO_WRONG_ARG_VALUE, ISO_RR_NAME_TOO_LONG * * @since 1.4.2 */ int iso_truncate_leaf_name(int mode, int length, char *name, int flag); /** * Get the root directory of the image. * No extra ref is added to it, so you must not unref it. Use iso_node_ref() * if you want to get your own reference. * * @since 0.6.2 */ IsoDir *iso_image_get_root(const IsoImage *image); /** * Fill in the volset identifier for a image. * * @since 0.6.2 */ void iso_image_set_volset_id(IsoImage *image, const char *volset_id); /** * Get the volset identifier. * The returned string is owned by the image and must not be freed nor * changed. * * @since 0.6.2 */ const char *iso_image_get_volset_id(const IsoImage *image); /** * Fill in the volume identifier for a image. * * @since 0.6.2 */ void iso_image_set_volume_id(IsoImage *image, const char *volume_id); /** * Get the volume identifier. * The returned string is owned by the image and must not be freed nor * changed. * * @since 0.6.2 */ const char *iso_image_get_volume_id(const IsoImage *image); /** * Fill in the publisher for a image. * * @since 0.6.2 */ void iso_image_set_publisher_id(IsoImage *image, const char *publisher_id); /** * Get the publisher of a image. * The returned string is owned by the image and must not be freed nor * changed. * * @since 0.6.2 */ const char *iso_image_get_publisher_id(const IsoImage *image); /** * Fill in the data preparer for a image. * * @since 0.6.2 */ void iso_image_set_data_preparer_id(IsoImage *image, const char *data_preparer_id); /** * Get the data preparer of a image. * The returned string is owned by the image and must not be freed nor * changed. * * @since 0.6.2 */ const char *iso_image_get_data_preparer_id(const IsoImage *image); /** * Fill in the system id for a image. Up to 32 characters. * * @since 0.6.2 */ void iso_image_set_system_id(IsoImage *image, const char *system_id); /** * Get the system id of a image. * The returned string is owned by the image and must not be freed nor * changed. * * @since 0.6.2 */ const char *iso_image_get_system_id(const IsoImage *image); /** * Fill in the application id for a image. Up to 128 chars. * * @since 0.6.2 */ void iso_image_set_application_id(IsoImage *image, const char *application_id); /** * Get the application id of a image. * The returned string is owned by the image and must not be freed nor * changed. * * @since 0.6.2 */ const char *iso_image_get_application_id(const IsoImage *image); /** * Fill copyright information for the image. Usually this refers * to a file on disc. Up to 37 characters. * * @since 0.6.2 */ void iso_image_set_copyright_file_id(IsoImage *image, const char *copyright_file_id); /** * Get the copyright information of a image. * The returned string is owned by the image and must not be freed nor * changed. * * @since 0.6.2 */ const char *iso_image_get_copyright_file_id(const IsoImage *image); /** * Fill abstract information for the image. Usually this refers * to a file on disc. Up to 37 characters. * * @since 0.6.2 */ void iso_image_set_abstract_file_id(IsoImage *image, const char *abstract_file_id); /** * Get the abstract information of a image. * The returned string is owned by the image and must not be freed nor * changed. * * @since 0.6.2 */ const char *iso_image_get_abstract_file_id(const IsoImage *image); /** * Fill biblio information for the image. Usually this refers * to a file on disc. Up to 37 characters. * * @since 0.6.2 */ void iso_image_set_biblio_file_id(IsoImage *image, const char *biblio_file_id); /** * Get the biblio information of a image. * The returned string is owned by the image and must not be freed or changed. * * @since 0.6.2 */ const char *iso_image_get_biblio_file_id(const IsoImage *image); /** * Fill Application Use field of the Primary Volume Descriptor. * ECMA-119 8.4.32 Application Use (BP 884 to 1395) * "This field shall be reserved for application use. Its content * is not specified by this Standard." * * @param image * The image to manipulate. * @param app_use_data * Up to 512 bytes of data. * @param count * The number of bytes in app_use_data. If the number is smaller than 512, * then the remaining bytes will be set to 0. * @since 1.3.2 */ void iso_image_set_app_use(IsoImage *image, const char *app_use_data, int count); /** * Get the current setting for the Application Use field of the Primary Volume * Descriptor. * The returned char array of 512 bytes is owned by the image and must not * be freed or changed. * * @param image * The image to inquire * @since 1.3.2 */ const char *iso_image_get_app_use(IsoImage *image); /** * Get the four timestamps from the Primary Volume Descriptor of the imported * ISO image. The timestamps are strings which are either empty or consist * of 16 digits of the form YYYYMMDDhhmmsscc, plus a signed byte in the range * of -48 to +52, which gives the timezone offset in steps of 15 minutes. * None of the returned string pointers shall be used for altering or freeing * data. They are just for reading. * * @param image * The image to be inquired. * @param creation_time * Returns a pointer to the Volume Creation time: * When "the information in the volume was created." * @param modification_time * Returns a pointer to Volume Modification time: * When "the information in the volume was last modified." * @param expiration_time * Returns a pointer to Volume Expiration time: * When "the information in the volume may be regarded as obsolete." * @param effective_time * Returns a pointer to Volume Expiration time: * When "the information in the volume may be used." * @return * ISO_SUCCESS or error * * @since 1.2.8 */ int iso_image_get_pvd_times(IsoImage *image, char **creation_time, char **modification_time, char **expiration_time, char **effective_time); /** * Create a new set of El-Torito bootable images by adding a boot catalog * and the default boot image. * Further boot images may then be added by iso_image_add_boot_image(). * * @param image * The image to make bootable. If it was already bootable this function * returns an error and the image remains unmodified. * @param image_path * The absolute path of a IsoFile to be used as default boot image or * --interval:appended_partition_$number[_start_$start_size_$size]:... * if type is ELTORITO_NO_EMUL. $number gives the partition number. * If no partitition with the given $number was set by functions like * iso_write_opts_set_partition_img() and if the optional image_path part * "_start_$start_size_$size" is present, then $start gets read as 2 KiB * start block of the interval and $size as number of blocks of 512 bytes. * If this range of block addresses is below the address set by * iso_write_opts_set_ms_block(), then that range gets marked as boot * image. I.e. an old appended partition can be marked as boot image. * @param type * The boot media type. This can be one of 3 types: * - ELTORITO_FLOPPY_EMUL. * Floppy emulation: Boot image file must be exactly * 1200 KiB, 1440 KiB or 2880 KiB. * - ELTORITO_HARD_DISC_EMUL. * Hard disc emulation: The image must begin with a master * boot record with a single image. * - ELTORITO_NO_EMUL. * No emulation. You should specify load segment and load size * of image. * @param catalog_path * The absolute path in the image tree where the catalog will be stored. * The directory component of this path must be a directory existent on * the image tree, and the filename component must be unique among all * children of that directory on image. Otherwise a correspodent error * code will be returned. This function will add an IsoBoot node that acts * as a placeholder for the real catalog, that will be generated at image * creation time. * @param boot * Location where a pointer to the added boot image will be stored. That * object is owned by the IsoImage and must not be freed by the user, * nor dereferenced once the last reference to the IsoImage was disposed * via iso_image_unref(). A NULL value is allowed if you don't need a * reference to the boot image. * @return * 1 on success, < 0 on error * * @since 0.6.2 */ int iso_image_set_boot_image(IsoImage *image, const char *image_path, enum eltorito_boot_media_type type, const char *catalog_path, ElToritoBootImage **boot); /** * Add a further boot image to the set of El-Torito bootable images. * This set has already to be created by iso_image_set_boot_image(). * Up to 31 further boot images may be added. * * @param image * The image to which the boot image shall be added. * returns an error and the image remains unmodified. * @param image_path * The absolute path of a IsoFile to be used as boot image or * --interval:appended_partition_$number[_start_$start_size_$size]:... * if type is ELTORITO_NO_EMUL. See iso_image_set_boot_image. * @param type * The boot media type. See iso_image_set_boot_image. * @param flag * Bitfield for control purposes. Unused yet. Submit 0. * @param boot * Location where a pointer to the added boot image will be stored. * See iso_image_set_boot_image * @return * 1 on success, < 0 on error * ISO_BOOT_NO_CATALOG means iso_image_set_boot_image() * was not called first. * * @since 0.6.32 */ int iso_image_add_boot_image(IsoImage *image, const char *image_path, enum eltorito_boot_media_type type, int flag, ElToritoBootImage **boot); /** * Get the El-Torito boot catalog and the default boot image of an ISO image. * * This can be useful, for example, to check if a volume read from a previous * session or an existing image is bootable. It can also be useful to get * the image and catalog tree nodes. An application would want those, for * example, to prevent the user removing it. * * Both nodes are owned by libisofs and must not be freed. You can get your * own ref with iso_node_ref(). You can also check if the node is already * on the tree by getting its parent (note that when reading El-Torito info * from a previous image, the nodes might not be on the tree even if you haven't * removed them). Remember that you'll need to get a new ref * (with iso_node_ref()) before inserting them again to the tree, and probably * you will also need to set the name or permissions. * * @param image * The image from which to get the boot image. * @param boot * If not NULL, it will be filled with a pointer to the boot image, if * any. That object is owned by the IsoImage and must not be freed by * the user, nor dereferenced once the last reference to the IsoImage was * disposed via iso_image_unref(). * @param imgnode * When not NULL, it will be filled with the image tree node. No extra ref * is added, you can use iso_node_ref() to get one if you need it. * The returned value is NULL if the boot image source is no IsoFile. * @param catnode * When not NULL, it will be filled with the catnode tree node. No extra * ref is added, you can use iso_node_ref() to get one if you need it. * @return * 1 on success, 0 is the image is not bootable (i.e., it has no El-Torito * image), < 0 error. * * @since 0.6.2 */ int iso_image_get_boot_image(IsoImage *image, ElToritoBootImage **boot, IsoFile **imgnode, IsoBoot **catnode); /** * Get detailed information about the boot catalog that was loaded from * an ISO image. * The boot catalog links the El Torito boot record at LBA 17 with the * boot images which are IsoFile objects in the image. The boot catalog * itself is not a regular file and thus will not deliver an IsoStream. * Its content is usually quite short and can be obtained by this call. * * @param image * The image to inquire. * @param catnode * Will return the boot catalog tree node. No extra ref is taken. * @param lba * Will return the block address of the boot catalog in the image. * @param content * Will return either NULL or an allocated memory buffer with the * content bytes of the boot catalog. * Dispose it by free() when no longer needed. * @param size * Will return the number of bytes in content. * @return * 1 if reply is valid, 0 if not boot catalog was loaded, < 0 on error. * * @since 1.1.2 */ int iso_image_get_bootcat(IsoImage *image, IsoBoot **catnode, uint32_t *lba, char **content, off_t *size); /** * Get all El-Torito boot images of an ISO image. * * The first of these boot images is the same as returned by * iso_image_get_boot_image(). The others are alternative boot images. * * @param image * The image from which to get the boot images. * @param num_boots * The number of available array elements in boots and bootnodes. * @param boots * Returns NULL or an allocated array of pointers to boot images. * Apply system call free(boots) to dispose it. * @param bootnodes * Returns NULL or an allocated array of pointers to the IsoFile nodes * which bear the content of the boot images in boots. * An array entry is NULL if the boot image source is no IsoFile. >>> Need getter for partition index * @param flag * Bitfield for control purposes. Unused yet. Submit 0. * @return * 1 on success, 0 no El-Torito catalog and boot image attached, * < 0 error. * * @since 0.6.32 */ int iso_image_get_all_boot_imgs(IsoImage *image, int *num_boots, ElToritoBootImage ***boots, IsoFile ***bootnodes, int flag); /** * Removes all El-Torito boot images from the ISO image. * * The IsoBoot node that acts as placeholder for the catalog is also removed * for the image tree, if there. * If the image is not bootable (don't have el-torito boot image) this function * just returns. * * @since 0.6.2 */ void iso_image_remove_boot_image(IsoImage *image); /** * Sets the sort weight of the boot catalog that is attached to an IsoImage. * * For the meaning of sort weights see iso_node_set_sort_weight(). * That function cannot be applied to the emerging boot catalog because * it is not represented by an IsoFile. * * @param image * The image to manipulate. * @param sort_weight * The larger this value, the lower will be the block address of the * boot catalog record. * @return * 0= no boot catalog attached , 1= ok , <0 = error * * @since 0.6.32 */ int iso_image_set_boot_catalog_weight(IsoImage *image, int sort_weight); /** * Hides the boot catalog file from directory trees. * * For the meaning of hiding files see iso_node_set_hidden(). * * * @param image * The image to manipulate. * @param hide_attrs * Or-combination of values from enum IsoHideNodeFlag to set the trees * in which the record. * @return * 0= no boot catalog attached , 1= ok , <0 = error * * @since 0.6.34 */ int iso_image_set_boot_catalog_hidden(IsoImage *image, int hide_attrs); /** * Get the boot media type as of parameter "type" of iso_image_set_boot_image() * or iso_image_add_boot_image(). * * @param bootimg * The image to inquire * @param media_type * Returns the media type * @return * 1 = ok , < 0 = error * * @since 0.6.32 */ int el_torito_get_boot_media_type(ElToritoBootImage *bootimg, enum eltorito_boot_media_type *media_type); /** * Sets the platform ID of the boot image. * * The Platform ID gets written into the boot catalog at byte 1 of the * Validation Entry, or at byte 1 of a Section Header Entry. * If Platform ID and ID String of two consecutive bootimages are the same * * @param bootimg * The image to manipulate. * @param id * A Platform ID as of * El Torito 1.0 : 0x00= 80x86, 0x01= PowerPC, 0x02= Mac * Others : 0xef= EFI * @return * 1 ok , <=0 error * * @since 0.6.32 */ int el_torito_set_boot_platform_id(ElToritoBootImage *bootimg, uint8_t id); /** * Get the platform ID value. See el_torito_set_boot_platform_id(). * * @param bootimg * The image to inquire * @return * 0 - 255 : The platform ID * < 0 : error * * @since 0.6.32 */ int el_torito_get_boot_platform_id(ElToritoBootImage *bootimg); /** * Sets the load segment for the initial boot image. This is only for * no emulation boot images, and is a NOP for other image types. * * @param bootimg * The image to to manipulate * @param segment * Load segment address. * The data type of this parameter is not fully suitable. You may submit * negative numbers in the range ((short) 0x8000) to ((short) 0xffff) * in order to express the non-negative numbers 0x8000 to 0xffff. * * @since 0.6.2 */ void el_torito_set_load_seg(ElToritoBootImage *bootimg, short segment); /** * Get the load segment value. See el_torito_set_load_seg(). * * @param bootimg * The image to inquire * @return * 0 - 65535 : The load segment value * < 0 : error * * @since 0.6.32 */ int el_torito_get_load_seg(ElToritoBootImage *bootimg); /** * Sets the number of sectors (512b) to be load at load segment during * the initial boot procedure. This is only for * no emulation boot images, and is a NOP for other image types. * * @param bootimg * The image to to manipulate * @param sectors * Number of 512-byte blocks to be loaded by the BIOS. * The data type of this parameter is not fully suitable. You may submit * negative numbers in the range ((short) 0x8000) to ((short) 0xffff) * in order to express the non-negative numbers 0x8000 to 0xffff. * * @since 0.6.2 */ void el_torito_set_load_size(ElToritoBootImage *bootimg, short sectors); /** * Get the load size. See el_torito_set_load_size(). * * @param bootimg * The image to inquire * @return * 0 - 65535 : The load size value * < 0 : error * * @since 0.6.32 */ int el_torito_get_load_size(ElToritoBootImage *bootimg); /** * State that the load size shall be the size of the boot image automatically. * This overrides el_torito_set_load_size(). * @param bootimg * The image to to manipulate * @param mode * 0= use value of el_torito_set_load_size() * 1= determine value from boot image */ void el_torito_set_full_load(ElToritoBootImage *bootimg, int mode); /** * Inquire the setting of el_torito_set_full_load(). * @param bootimg * The image to inquire * @return * The mode set with el_torito_set_full_load(). */ int el_torito_get_full_load(ElToritoBootImage *bootimg); /** * Marks the specified boot image as not bootable * * @since 0.6.2 */ void el_torito_set_no_bootable(ElToritoBootImage *bootimg); /** * Get the bootability flag. See el_torito_set_no_bootable(). * * @param bootimg * The image to inquire * @return * 0 = not bootable, 1 = bootable , <0 = error * * @since 0.6.32 */ int el_torito_get_bootable(ElToritoBootImage *bootimg); /** * Set the id_string of the Validation Entry or Sector Header Entry which * will govern the boot image Section Entry in the El Torito Catalog. * * @param bootimg * The image to manipulate. * @param id_string * The first boot image puts 24 bytes of ID string into the Validation * Entry, where they shall "identify the manufacturer/developer of * the CD-ROM". * Further boot images put 28 bytes into their Section Header. * El Torito 1.0 states that "If the BIOS understands the ID string, it * may choose to boot the system using one of these entries in place * of the INITIAL/DEFAULT entry." (The INITIAL/DEFAULT entry points to the * first boot image.) * @return * 1 = ok , <0 = error * * @since 0.6.32 */ int el_torito_set_id_string(ElToritoBootImage *bootimg, uint8_t id_string[28]); /** * Get the id_string as of el_torito_set_id_string(). * * @param bootimg * The image to inquire * @param id_string * Returns 28 bytes of id string * @return * 1 = ok , <0 = error * * @since 0.6.32 */ int el_torito_get_id_string(ElToritoBootImage *bootimg, uint8_t id_string[28]); /** * Set the Selection Criteria of a boot image. * * @param bootimg * The image to manipulate. * @param crit * The first boot image has no selection criteria. They will be ignored. * Further boot images put 1 byte of Selection Criteria Type and 19 * bytes of data into their Section Entry. * El Torito 1.0 states that "The format of the selection criteria is * a function of the BIOS vendor. In the case of a foreign language * BIOS three bytes would be used to identify the language". * Type byte == 0 means "no criteria", * type byte == 1 means "Language and Version Information (IBM)". * @return * 1 = ok , <0 = error * * @since 0.6.32 */ int el_torito_set_selection_crit(ElToritoBootImage *bootimg, uint8_t crit[20]); /** * Get the Selection Criteria bytes as of el_torito_set_selection_crit(). * * @param bootimg * The image to inquire * @param crit * Returns 20 bytes of type and data * @return * 1 = ok , <0 = error * * @since 0.6.32 */ int el_torito_get_selection_crit(ElToritoBootImage *bootimg, uint8_t crit[20]); /** * Makes a guess whether the boot image was patched by a boot information * table. It is advisable to patch such boot images if their content gets * copied to a new location. See el_torito_set_isolinux_options(). * Note: The reply can be positive only if the boot image was imported * from an existing ISO image. * * @param bootimg * The image to inquire * @param flag * Bitfield for control purposes: * bit0 - bit3= mode * 0 = inquire for classic boot info table as described in man mkisofs * @since 0.6.32 * 1 = inquire for GRUB2 boot info as of bit9 of options of * el_torito_set_isolinux_options() * @since 1.3.0 * @return * 1 = seems to contain the inquired boot info, 0 = quite surely not * @since 0.6.32 */ int el_torito_seems_boot_info_table(ElToritoBootImage *bootimg, int flag); /** * Specifies options for ISOLINUX or GRUB boot images. This should only be used * if the type of boot image is known. * * @param bootimg * The image to set options on * @param options * bitmask style flag. The following values are defined: * * bit0= Patch the boot info table of the boot image. * This does the same as mkisofs option -boot-info-table. * Needed for ISOLINUX or GRUB boot images with platform ID 0. * The table is located at byte 8 of the boot image file. * Its size is 56 bytes. * The original boot image file on disk will not be modified. * * One may use el_torito_seems_boot_info_table() for a * qualified guess whether a boot info table is present in * the boot image. If the result is 1 then it should get bit0 * set if its content gets copied to a new LBA. * * bit1= Generate a ISOLINUX isohybrid image with MBR. * ---------------------------------------------------------- * @deprecated since 31 Mar 2010: * The author of syslinux, H. Peter Anvin requested that this * feature shall not be used any more. He intends to cease * support for the MBR template that is included in libisofs. * ---------------------------------------------------------- * A hybrid image is a boot image that boots from either * CD/DVD media or from disk-like media, e.g. USB stick. * For that you need isolinux.bin from SYSLINUX 3.72 or later. * IMPORTANT: The application has to take care that the image * on media gets padded up to the next full MB. * Under seiveral circumstances it might get aligned * automatically. But there is no warranty. * bit2-7= Mentioning in isohybrid GPT * 0= Do not mention in GPT * 1= Mention as Basic Data partition. * This cannot be combined with GPT partitions as of * iso_write_opts_set_efi_bootp() * @since 1.2.4 * 2= Mention as HFS+ partition. * This cannot be combined with HFS+ production by * iso_write_opts_set_hfsplus(). * @since 1.2.4 * Primary GPT and backup GPT get written if at least one * ElToritoBootImage shall be mentioned. * The first three mentioned GPT partitions get mirrored in the * the partition table of the isohybrid MBR. They get type 0xfe. * The MBR partition entry for PC-BIOS gets type 0x00 rather * than 0x17. * Often it is one of the further MBR partitions which actually * gets used by EFI. * @since 1.2.4 * bit8= Mention in isohybrid Apple partition map * APM get written if at least one ElToritoBootImage shall be * mentioned. The ISOLINUX MBR must look suitable or else an error * event will happen at image generation time. * @since 1.2.4 * bit9= GRUB2 boot info * Patch the boot image file at byte 1012 with the 512-block * address + 2. Two little endian 32-bit words. Low word first. * This is combinable with bit0. * @since 1.3.0 * @param flag * Reserved for future usage, set to 0. * @return * 1 success, < 0 on error * @since 0.6.12 */ int el_torito_set_isolinux_options(ElToritoBootImage *bootimg, int options, int flag); /** * Get the options as of el_torito_set_isolinux_options(). * * @param bootimg * The image to inquire * @param flag * Reserved for future usage, set to 0. * @return * >= 0 returned option bits , <0 = error * * @since 0.6.32 */ int el_torito_get_isolinux_options(ElToritoBootImage *bootimg, int flag); /** Deprecated: * Specifies that this image needs to be patched. This involves the writing * of a 16 bytes boot information table at offset 8 of the boot image file. * The original boot image file won't be modified. * This is needed for isolinux boot images. * * @since 0.6.2 * @deprecated Use el_torito_set_isolinux_options() instead */ void el_torito_patch_isolinux_image(ElToritoBootImage *bootimg); /** * Obtain a copy of the possibly loaded first 32768 bytes of the imported * session, the System Area. * It will be written to the start of the next session unless it gets * overwritten by iso_write_opts_set_system_area(). * * @param img * The image to be inquired. * @param data * A byte array of at least 32768 bytes to take the loaded bytes. * @param options * The option bits which will be applied if not overridden by * iso_write_opts_set_system_area(). See there. * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1 on success, 0 if no System Area was loaded, < 0 error. * @since 0.6.30 */ int iso_image_get_system_area(IsoImage *img, char data[32768], int *options, int flag); /** * The maximum length of a single line in the output of function * iso_image_report_system_area() and iso_image_report_el_torito(). * This number includes the trailing 0. * @since 1.3.8 */ #define ISO_MAX_SYSAREA_LINE_LENGTH 4096 /** * Texts which describe the output format of iso_image_report_system_area(). * They are publicly defined here only as part of the API description. * Do not use these macros in your application but rather call * iso_image_report_system_area() with flag bit0. */ #define ISO_SYSAREA_REPORT_DOC \ \ "Report format for recognized System Area data.", \ "", \ "No text will be reported if no System Area was loaded or if it was", \ "entirely filled with 0-bytes.", \ "Else there will be at least these three lines:", \ " System area options: hex", \ " see libisofs.h, parameter of iso_write_opts_set_system_area().", \ " System area summary: word ... word", \ " human readable interpretation of system area options and other info", \ " The words are from the set:", \ " { MBR, CHRP, PReP, GPT, APM, MIPS-Big-Endian, MIPS-Little-Endian,", \ " SUN-SPARC-Disk-Label, HP-PA-PALO, DEC-Alpha, ", \ " protective-msdos-label, isohybrid, grub2-mbr,", \ " cyl-align-{auto,on,off,all}, not-recognized, }", \ " The acronyms indicate boot data for particular hardware/firmware.", \ " protective-msdos-label is an MBR conformant to specs of GPT.", \ " isohybrid is an MBR implementing ISOLINUX isohybrid functionality.", \ " grub2-mbr is an MBR with GRUB2 64 bit address patching.", \ " cyl-align-on indicates that the ISO image MBR partition ends at a", \ " cylinder boundary. cyl-align-all means that more MBR partitions", \ " exist and all end at a cylinder boundary.", \ " not-recognized tells about unrecognized non-zero system area data.", \ " ISO image size/512 : decimal", \ " size of ISO image in block units of 512 bytes.", \ "" #define ISO_SYSAREA_REPORT_DOC_MBR \ \ "If an MBR is detected, with at least one partition entry of non-zero size,", \ "then there may be:", \ " Partition offset : decimal", \ " if not 0 then a second ISO 9660 superblock was found to which", \ " MBR partition 1 or GPT partition 1 is pointing.", \ " MBR heads per cyl : decimal", \ " conversion factor between MBR C/H/S address and LBA. 0=inconsistent.", \ " MBR secs per head : decimal", \ " conversion factor between MBR C/H/S address and LBA. 0=inconsistent.", \ " MBR partition table: N Status Type Start Blocks", \ " headline for MBR partition table.", \ " MBR partition : X hex hex decimal decimal", \ " gives partition number, status byte, type byte, start block,", \ " and number of blocks. 512 bytes per block.", \ " MBR partition path : X path", \ " the path of a file in the ISO image which began at the start block", \ " of partition X when the ISO filesystem was imported.", \ " PReP boot partition: decimal decimal", \ " gives start block and size of a PReP boot partition in ISO 9660", \ " block units of 2048 bytes.", \ "" #define ISO_SYSAREA_REPORT_DOC_GPT1 \ \ "GUID Partition Table can coexist with MBR:", \ " GPT : N Info", \ " headline for GPT partition table. The fields are too wide for a", \ " neat table. So they are listed with a partition number and a text.", \ " GPT CRC should be : to match first 92 GPT header block bytes", \ " GPT CRC found : matches all 512 bytes of GPT header block", \ " libisofs-1.2.4 to 1.2.8 had a bug with the GPT header CRC. So", \ " libisofs is willing to recognize GPT with the buggy CRC. These", \ " two lines inform that most partition editors will not accept it.", \ " GPT array CRC wrong: should be , found ", \ " GPT entry arrays are accepted even if their CRC does not match.", \ " In this case, both CRCs are reported by this line.", \ " GPT backup problems: text", \ " reports about inconsistencies between main GPT and backup GPT.", \ " The statements are comma separated:", \ " Implausible header LBA ", \ " Cannot read header block at 2k LBA ", \ " Not a GPT 1.0 header of 92 bytes for 128 bytes per entry", \ " Head CRC wrong. Should be ", \ " Head CRC wrong. Should be . Matches all 512 block bytes", \ " Disk GUID differs ()", \ " Cannot read array block at 2k LBA ", \ " Array CRC wrong. Should be ", \ " Entries differ for partitions [... ]", \ " GPT disk GUID : hex_digits", \ " 32 hex digits giving the byte string of the disk's GUID", \ " GPT entry array : decimal decimal word", \ " start block of partition entry array and number of entries. 512 bytes", \ " per block. The word may be \"separated\" if partitions are disjoint,", \ " \"overlapping\" if they are not. In future there may be \"nested\"", \ " as special case where all overlapping partitions are superset and", \ " subset, and \"covering\" as special case of disjoint partitions", \ " covering the whole GPT block range for partitions.", \ " GPT lba range : decimal decimal decimal", \ " addresses of first payload block, last payload block, and of the", \ " GPT backup header block. 512 bytes per block." \ #define ISO_SYSAREA_REPORT_DOC_GPT2 \ \ " GPT partition name : X hex_digits", \ " up to 144 hex digits giving the UTF-16LE name byte string of", \ " partition X. Trailing 16 bit 0-characters are omitted.", \ " GPT partname local : X text", \ " the name of partition X converted to the local character set.", \ " This line may be missing if the name cannot be converted, or is", \ " empty.", \ " GPT partition GUID : X hex_digits", \ " 32 hex digits giving the byte string of the GUID of partition X.", \ " GPT type GUID : X hex_digits", \ " 32 hex digits giving the byte string of the type GUID of partition X.", \ " GPT partition flags: X hex", \ " 64 flag bits of partition X in hex representation.", \ " Known bit meanings are:", \ " bit0 = \"System Partition\" Do not alter.", \ " bit2 = Legacy BIOS bootable (MBR partition type 0x80)", \ " bit60= read-only", \ " GPT start and size : X decimal decimal", \ " start block and number of blocks of partition X. 512 bytes per block.", \ " GPT partition path : X path", \ " the path of a file in the ISO image which began at the start block", \ " of partition X when the ISO filesystem was imported.", \ "" #define ISO_SYSAREA_REPORT_DOC_APM \ \ "Apple partition map can coexist with MBR and GPT:", \ " APM : N Info", \ " headline for human readers.", \ " APM block size : decimal", \ " block size of Apple Partition Map. 512 or 2048. This applies to", \ " start address and size of all partitions in the APM.", \ " APM gap fillers : decimal", \ " tells the number of partitions with name \"Gap[0-9[0-9]]\" and type", \ " \"ISO9660_data\".", \ " APM partition name : X text", \ " the name of partition X. Up to 32 characters.", \ " APM partition type : X text", \ " the type string of partition X. Up to 32 characters.", \ " APM start and size : X decimal decimal", \ " start block and number of blocks of partition X.", \ " APM partition path : X path", \ " the path of a file in the ISO image which began at the start block", \ " of partition X when the ISO filesystem was imported.", \ "" #define ISO_SYSAREA_REPORT_DOC_MIPS \ \ "If a MIPS Big Endian Volume Header is detected, there may be:", \ " MIPS-BE volume dir : N Name Block Bytes", \ " headline for human readers.", \ " MIPS-BE boot entry : X upto8chr decimal decimal", \ " tells name, 512-byte block address, and byte count of boot entry X.", \ " MIPS-BE boot path : X path", \ " tells the path to the boot image file in the ISO image which began", \ " at the block address given by boot entry X when the ISO filesystem", \ " was imported.", \ "", \ "If a DEC Boot Block for MIPS Little Endian is detected, there may be:", \ " MIPS-LE boot map : LoadAddr ExecAddr SegmentSize SegmentStart", \ " headline for human readers.", \ " MIPS-LE boot params: decimal decimal decimal decimal", \ " tells four numbers which are originally derived from the ELF header", \ " of the boot file. The first two are counted in bytes, the other two", \ " are counted in blocks of 512 bytes.", \ " MIPS-LE boot path : path", \ " tells the path to the boot image file in the ISO image which began", \ " at the block address given by SegmentStart when the ISO filesystem", \ " was imported.", \ " MIPS-LE elf offset : decimal", \ " tells the relative 512-byte block offset inside the boot file:", \ " SegmentStart - FileStartBlock", \ "" #define ISO_SYSAREA_REPORT_DOC_SUN \ \ "If a SUN SPARC Disk Label is present:", \ " SUN SPARC disklabel: text", \ " tells the disk label text.", \ " SUN SPARC secs/head: decimal", \ " tells the number of sectors per head.", \ " SUN SPARC heads/cyl: decimal", \ " tells the number of heads per cylinder.", \ " SUN SPARC partmap : N IdTag Perms StartCyl NumBlock", \ " headline for human readers.", \ " SUN SPARC partition: X hex hex decimal decimal", \ " gives partition number, type word, permission word, start cylinder,", \ " and number of of blocks. 512 bytes per block. Type word may be: ", \ " 0=unused, 2=root partition with boot, 4=user partition.", \ " Permission word is 0x10 = read-only.", \ " SPARC GRUB2 core : decimal decimal", \ " tells byte address and byte count of the GRUB2 SPARC core file.", \ " SPARC GRUB2 path : path", \ " tells the path to the data file in the ISO image which began at the", \ " address given by core when the ISO filesystem was imported.", \ "" #define ISO_SYSAREA_REPORT_DOC_HPPA \ \ "If a HP-PA PALO boot sector version 4 or 5 is present:", \ " PALO header version: decimal", \ " tells the PALO header version: 4 or 5.", \ " HP-PA cmdline : text", \ " tells the command line for the kernels.", \ " HP-PA boot files : ByteAddr ByteSize Path", \ " headline for human readers.", \ " HP-PA 32-bit kernel: decimal decimal path", \ " tells start byte and byte count of the 32-bit kernel and the path", \ " to the data file in the ISO image which began at the start byte", \ " when the ISO filesystem was imported.", \ " HP-PA 64-bit kernel: decimal decimal path", \ " tells the same for the 64-bit kernel.", \ " HP-PA ramdisk : decimal decimal path", \ " tells the same for the ramdisk file.", \ " HP-PA bootloader : decimal decimal path", \ " tells the same for the bootloader file.", \ "" #define ISO_SYSAREA_REPORT_DOC_ALPHA \ "If a DEC Alpha SRM boot sector is present:", \ " DEC Alpha ldr size : decimal", \ " tells the number of 512-byte blocks in DEC Alpha Secondary Bootstrap", \ " Loader file.", \ " DEC Alpha ldr adr : decimal", \ " tells the start of the loader file in units of 512-byte blocks.", \ " DEC Alpha ldr path : path", \ " tells the path to a file in the ISO image which began at the", \ " loader start address when the ISO filesystem was imported." /** * Obtain an array of texts describing the detected properties of the * possibly loaded System Area. * The array will be NULL if no System Area was loaded. It will be non-NULL * with zero line count if the System Area was loaded and contains only * 0-bytes. * Else it will consist of lines as described in ISO_SYSAREA_REPORT_DOC above. * * File paths and other long texts are reported as "(too long to show here)" * if their length plus preceding text plus trailing 0-byte exceeds the * line length limit of ISO_MAX_SYSAREA_LINE_LENGTH bytes. * Texts which may contain whitespace or unprintable characters will start * at fixed positions and extend to the end of the line. * Note that newline characters may well appearing in the middle of a "line". * * @param image * The image to be inquired. * @param reply * Will return an array of pointers to the result text lines or NULL. * Dispose a non-NULL reply by a call to iso_image_report_system_area() * with flag bit15, when no longer needed. * Be prepared for a long text with up to ISO_MAX_SYSAREA_LINE_LENGTH * characters per line. * @param line_count * Will return the number of valid pointers in reply. * @param flag * Bitfield for control purposes * bit0= do not report system area but rather reply a copy of * above text line arrays ISO_SYSAREA_REPORT_DOC*. * With this bit it is permissible to submit image as NULL. * bit15= dispose result from previous call. * @return * 1 on success, 0 if no System Area was loaded, < 0 error. * @since 1.3.8 */ int iso_image_report_system_area(IsoImage *image, char ***reply, int *line_count, int flag); /** * Text which describes the output format of iso_image_report_el_torito(). * It is publicly defined here only as part of the API description. * Do not use it as macro in your application but rather call * iso_image_report_el_torito() with flag bit0. */ #define ISO_ELTORITO_REPORT_DOC \ "Report format for recognized El Torito boot information.", \ "", \ "No text will be reported if no El Torito information was found.", \ "Else there will be at least these three lines", \ " El Torito catalog : decimal decimal", \ " tells the block address and number of 2048-blocks of the boot catalog.", \ " El Torito images : N Pltf B Emul Ld_seg Hdpt Ldsiz LBA", \ " is the headline of the boot image list.", \ " El Torito boot img : X word char word hex hex decimal decimal", \ " tells about boot image number X:", \ " - Platform Id: \"BIOS\", \"PPC\", \"Mac\", \"UEFI\" or a hex number.", \ " - Bootability: either \"y\" or \"n\".", \ " - Emulation: \"none\", \"fd1.2\", \"fd1.4\", \"fd2.8\", \"hd\"", \ " for no emulation, three floppy MB sizes, hard disk.", \ " - Load Segment: start offset in boot image. 0x0000 means 0x07c0.", \ " - Hard disk emulation partition type: MBR partition type code.", \ " - Load size: number of 512-blocks to load with emulation mode \"none\".", \ " - LBA: start block number in ISO filesystem (2048-block).", \ "", \ "The following lines appear conditionally:", \ " El Torito cat path : iso_rr_path", \ " tells the path to the data file in the ISO image which began at the", \ " block address where the boot catalog starts when the ISO filesystem", \ " was imported.", \ " (This line is not reported if no path points to that block.)", \ " El Torito img path : X iso_rr_path", \ " tells the path to the data file in the ISO image which began at the", \ " LBA of boot image X when the ISO filesystem was imported.", \ " (This line is not reported if no path points to that block.)", \ " El Torito img opts : X word ... word", \ " tells the presence of extra features:", \ " \"boot-info-table\" image got boot info table patching.", \ " \"isohybrid-suitable\" image is suitable for ISOLINUX isohybrid MBR.", \ " \"grub2-boot-info\" image got GRUB2 boot info patching.", \ " (This line is not reported if no such options were detected.)", \ " El Torito id string: X hex_digits", \ " tells the id string of the catalog section which hosts boot image X.", \ " (This line is not reported if the id string is all zero.)", \ " El Torito sel crit : X hex_digits", \ " tells the selection criterion of boot image X.", \ " (This line is not reported if the criterion is all zero.)", \ " El Torito img blks : X decimal", \ " gives an upper limit of the number of 2048-blocks in the boot image", \ " if it is not accessible via a path in the ISO directory tree.", \ " The boot image is supposed to end before the start block of any", \ " other entity of the ISO filesystem.", \ " (This line is not reported if no limiting entity is found.)", \ " El Torito hdsiz/512: X decimal", \ " gives with a boot image of emulation type \"hd\" the lowest block", \ " number which is above any partition end in the boot image's MBR", \ " partition table. This can be considered the claimed size of the", \ " emulated hard disk given in blocks of 512 bytes.", \ " (This line is not reported if no partition is found in the image.)", \ "" /** * Obtain an array of texts describing the detected properties of the * possibly loaded El Torito boot information. * The array will be NULL if no El Torito info was loaded. * Else it will consist of lines as described in ISO_ELTORITO_REPORT_DOC above. * * The lines have the same length restrictions and whitespace rules as the ones * returned by iso_image_report_system_area(). * * @param image * The image to be inquired. * @param reply * Will return an array of pointers to the result text lines or NULL. * Dispose a non-NULL reply by a call to iso_image_report_el_torito() * with flag bit15, when no longer needed. * Be prepared for a long text with up to ISO_MAX_SYSAREA_LINE_LENGTH * characters per line. * @param line_count * Will return the number of valid pointers in reply. * @param flag * Bitfield for control purposes * bit0= do not report system area but rather reply a copy of * above text line array ISO_ELTORITO_REPORT_DOC. * With this bit it is permissible to submit image as NULL. * bit15= dispose result from previous call. * @return * 1 on success, 0 if no El Torito information was loaded, < 0 error. * @since 1.3.8 */ int iso_image_report_el_torito(IsoImage *image, char ***reply, int *line_count, int flag); /** * Compute a CRC number as expected in the GPT main and backup header blocks. * * The CRC at byte offset 88 is supposed to cover the array of partition * entries. * The CRC at byte offset 16 is supposed to cover the readily produced * first 92 bytes of the header block while its bytes 16 to 19 are still * set to 0. * Block size is 512 bytes. Numbers are stored little-endian. * See doc/boot_sectors.txt for the byte layout of GPT. * * This might be helpful for applications which want to manipulate GPT * directly. The function is in libisofs/system_area.c and self-contained. * So if you want to copy+paste it under the license of that file: Be invited. * Be warned that this implementation works bit-wise and thus is much slower * than table-driven ones. For less than 32 KiB, it fully suffices, though. * * @param data * The memory buffer with the data to sum up. * @param count * Number of bytes in data. * @param flag * Bitfield for control purposes. Submit 0. * @return * The CRC of data. * @since 1.3.8 */ uint32_t iso_crc32_gpt(unsigned char *data, int count, int flag); /** * Add a MIPS boot file path to the image. * Up to 15 such files can be written into a MIPS Big Endian Volume Header * if this is enabled by value 1 in iso_write_opts_set_system_area() option * bits 2 to 7. * A single file can be written into a DEC Boot Block if this is enabled by * value 2 in iso_write_opts_set_system_area() option bits 2 to 7. So only * the first added file gets into effect with this system area type. * The data files which shall serve as MIPS boot files have to be brought into * the image by the normal means. * @param image * The image to be manipulated. * @param path * Absolute path of the boot file in the ISO 9660 Rock Ridge tree. * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1 on success, < 0 error * @since 0.6.38 */ int iso_image_add_mips_boot_file(IsoImage *image, char *path, int flag); /** * Obtain the number of added MIPS Big Endian boot files and pointers to * their paths in the ISO 9660 Rock Ridge tree. * @param image * The image to be inquired. * @param paths * An array of pointers to be set to the registered boot file paths. * This are just pointers to data inside IsoImage. Do not free() them. * Make own copies of the data before manipulating the image. * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * >= 0 is the number of valid path pointers , <0 means error * @since 0.6.38 */ int iso_image_get_mips_boot_files(IsoImage *image, char *paths[15], int flag); /** * Clear the list of MIPS Big Endian boot file paths. * @param image * The image to be manipulated. * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1 is success , <0 means error * @since 0.6.38 */ int iso_image_give_up_mips_boot(IsoImage *image, int flag); /** * Designate a data file in the ISO image of which the position and size * shall be written after the SUN Disk Label. The position is written as * 64-bit big-endian number to byte position 0x228. The size is written * as 32-bit big-endian to 0x230. * This setting has an effect only if system area type is set to 3 * with iso_write_opts_set_system_area(). * * @param img * The image to be manipulated. * @param sparc_core * The IsoFile which shall be mentioned after the SUN Disk label. * NULL is a permissible value. It disables this feature. * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1 is success , <0 means error * @since 1.3.0 */ int iso_image_set_sparc_core(IsoImage *img, IsoFile *sparc_core, int flag); /** * Obtain the current setting of iso_image_set_sparc_core(). * * @param img * The image to be inquired. * @param sparc_core * Will return a pointer to the IsoFile (or NULL, which is not an error) * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1 is success , <0 means error * @since 1.3.0 */ int iso_image_get_sparc_core(IsoImage *img, IsoFile **sparc_core, int flag); /** * Define a command line and submit the paths of four mandatory files for * production of a HP-PA PALO boot sector for PA-RISC machines. * The paths must lead to already existing data files in the ISO image * which stay with these paths until image production. * * @param img * The image to be manipulated. * @param cmdline * Up to 127 characters of command line. * @param bootloader * Absolute path of a data file in the ISO image. * @param kernel_32 * Absolute path of a data file in the ISO image which serves as * 32 bit kernel. * @param kernel_64 * Absolute path of a data file in the ISO image which serves as * 64 bit kernel. * @param ramdisk * Absolute path of a data file in the ISO image. * @param flag * Bitfield for control purposes * bit0= Let NULL parameters free the corresponding image properties. * Else only the non-NULL parameters of this call have an effect * @return * 1 is success , <0 means error * @since 1.3.8 */ int iso_image_set_hppa_palo(IsoImage *img, char *cmdline, char *bootloader, char *kernel_32, char *kernel_64, char *ramdisk, int flag); /** * Inquire the current settings of iso_image_set_hppa_palo(). * Do not free() the returned pointers. * * @param img * The image to be inquired. * @param cmdline * Will return the command line. * @param bootloader * Will return the absolute path of the bootloader file. * @param kernel_32 * Will return the absolute path of the 32 bit kernel file. * @param kernel_64 * Will return the absolute path of the 64 bit kernel file. * @param ramdisk * Will return the absolute path of the RAM disk file. * @return * 1 is success , <0 means error * @since 1.3.8 */ int iso_image_get_hppa_palo(IsoImage *img, char **cmdline, char **bootloader, char **kernel_32, char **kernel_64, char **ramdisk); /** * Submit the path of the DEC Alpha Secondary Bootstrap Loader file. * The path must lead to an already existing data file in the ISO image * which stays with this path until image production. * This setting has an effect only if system area type is set to 6 * with iso_write_opts_set_system_area(). * * @param img * The image to be manipulated. * @param boot_loader_path * Absolute path of a data file in the ISO image. * Submit NULL to free this image property. * @param flag * Bitfield for control purposes. Unused yet. Submit 0. * @return * 1 is success , <0 means error * @since 1.4.0 */ int iso_image_set_alpha_boot(IsoImage *img, char *boot_loader_path, int flag); /** * Inquire the path submitted by iso_image_set_alpha_boot() * Do not free() the returned pointer. * * @param img * The image to be inquired. * @param boot_loader_path * Will return the path. NULL if none is currently submitted. * @return * 1 is success , <0 means error * @since 1.4.0 */ int iso_image_get_alpha_boot(IsoImage *img, char **boot_loader_path); /** * Increments the reference counting of the given node. * * @since 0.6.2 */ void iso_node_ref(IsoNode *node); /** * Decrements the reference counting of the given node. * If it reach 0, the node is free, and, if the node is a directory, * its children will be unref() too. * * @since 0.6.2 */ void iso_node_unref(IsoNode *node); /** * Get the type of an IsoNode. * * @since 0.6.2 */ enum IsoNodeType iso_node_get_type(IsoNode *node); /** * Class of functions to handle particular extended information. A function * instance acts as an identifier for the type of the information. Structs * with same information type must use a pointer to the same function. * * @param data * Attached data * @param flag * What to do with the data. At this time the following values are * defined: * -> 1 the data must be freed * @return * 1 in any case. * * @since 0.6.4 */ typedef int (*iso_node_xinfo_func)(void *data, int flag); /** * Add extended information to the given node. Extended info allows * applications (and libisofs itself) to add more information to an IsoNode. * You can use this facilities to associate temporary information with a given * node. This information is not written into the ISO 9660 image on media * and thus does not persist longer than the node memory object. * * Each node keeps a list of added extended info, meaning you can add several * extended info data to each node. Each extended info you add is identified * by the proc parameter, a pointer to a function that knows how to manage * the external info data. Thus, in order to add several types of extended * info, you need to define a "proc" function for each type. * * @param node * The node where to add the extended info * @param proc * A function pointer used to identify the type of the data, and that * knows how to manage it * @param data * Extended info to add. * @return * 1 if success, 0 if the given node already has extended info of the * type defined by the "proc" function, < 0 on error * * @since 0.6.4 */ int iso_node_add_xinfo(IsoNode *node, iso_node_xinfo_func proc, void *data); /** * Remove the given extended info (defined by the proc function) from the * given node. * * @return * 1 on success, 0 if node does not have extended info of the requested * type, < 0 on error * * @since 0.6.4 */ int iso_node_remove_xinfo(IsoNode *node, iso_node_xinfo_func proc); /** * Remove all extended information from the given node. * * @param node * The node where to remove all extended info * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1 on success, < 0 on error * * @since 1.0.2 */ int iso_node_remove_all_xinfo(IsoNode *node, int flag); /** * Get the given extended info (defined by the proc function) from the * given node. * * @param node * The node to inquire * @param proc * The function pointer which serves as key * @param data * Will after successful call point to the xinfo data corresponding * to the given proc. This is a pointer, not a feeable data copy. * @return * 1 on success, 0 if node does not have extended info of the requested * type, < 0 on error * * @since 0.6.4 */ int iso_node_get_xinfo(IsoNode *node, iso_node_xinfo_func proc, void **data); /** * Get the next pair of function pointer and data of an iteration of the * list of extended information. Like: * iso_node_xinfo_func proc; * void *handle = NULL, *data; * while (iso_node_get_next_xinfo(node, &handle, &proc, &data) == 1) { * ... make use of proc and data ... * } * The iteration allocates no memory. So you may end it without any disposal * action. * IMPORTANT: Do not continue iterations after manipulating the extended * information of a node. Memory corruption hazard ! * @param node * The node to inquire * @param handle * The opaque iteration handle. Initialize iteration by submitting * a pointer to a void pointer with value NULL. * Do not alter its content until iteration has ended. * @param proc * The function pointer which serves as key * @param data * Will be filled with the extended info corresponding to the given proc * function * @return * 1 on success * 0 if iteration has ended (proc and data are invalid then) * < 0 on error * * @since 1.0.2 */ int iso_node_get_next_xinfo(IsoNode *node, void **handle, iso_node_xinfo_func *proc, void **data); /** * Class of functions to clone extended information. A function instance gets * associated to a particular iso_node_xinfo_func instance by function * iso_node_xinfo_make_clonable(). This is a precondition to have IsoNode * objects clonable which carry data for a particular iso_node_xinfo_func. * * @param old_data * Data item to be cloned * @param new_data * Shall return the cloned data item * @param flag * Unused yet, submit 0 * The function shall return ISO_XINFO_NO_CLONE on unknown flag bits. * @return * > 0 number of allocated bytes * 0 no size info is available * < 0 error * * @since 1.0.2 */ typedef int (*iso_node_xinfo_cloner)(void *old_data, void **new_data,int flag); /** * Associate a iso_node_xinfo_cloner to a particular class of extended * information in order to make it clonable. * * @param proc * The key and disposal function which identifies the particular * extended information class. * @param cloner * The cloner function which shall be associated with proc. * @param flag * Unused yet, submit 0 * @return * 1 success, < 0 error * * @since 1.0.2 */ int iso_node_xinfo_make_clonable(iso_node_xinfo_func proc, iso_node_xinfo_cloner cloner, int flag); /** * Inquire the registered cloner function for a particular class of * extended information. * * @param proc * The key and disposal function which identifies the particular * extended information class. * @param cloner * Will return the cloner function which is associated with proc, or NULL. * @param flag * Unused yet, submit 0 * @return * 1 success, 0 no cloner registered for proc, < 0 error * * @since 1.0.2 */ int iso_node_xinfo_get_cloner(iso_node_xinfo_func proc, iso_node_xinfo_cloner *cloner, int flag); /** * Set the name of a node. Note that if the node is already added to a dir * this can fail if dir already contains a node with the new name. * The IsoImage context defines a maximum permissible name length and a mode * how to react on oversized names. See iso_image_set_truncate_mode(). * * @param image * The image object to which the node belongs or shall belong in future. * @param node * The node of which you want to change the name. One cannot change the * name of the root directory. * @param name * The new name for the node. It may not be empty. If it is oversized * then it will be handled according to iso_image_set_truncate_mode(). * @param flag * bit0= issue warning in case of truncation * @return * 1 on success, < 0 on error * * @since 1.4.2 */ int iso_image_set_node_name(IsoImage *image, IsoNode *node, const char *name, int flag); /** * *** Deprecated *** * use iso_image_set_node_name() instead * * Set the name of a node without taking into respect name truncation mode of * an IsoImage. * * @param node * The node whose name you want to change. Note that you can't change * the name of the root. * @param name * The name for the node. If you supply an empty string or a * name greater than 255 characters this returns with failure, and * node name is not modified. * @return * 1 on success, < 0 on error * * @since 0.6.2 */ int iso_node_set_name(IsoNode *node, const char *name); /** * Get the name of a node. * The returned string belongs to the node and must not be modified nor * freed. Use strdup if you really need your own copy. * * Up to version 1.4.2 inquiry of the root directory name returned NULL, * which is a bug in the light of above description. * Since 1.4.2 the return value is an empty string. * * @since 0.6.2 */ const char *iso_node_get_name(const IsoNode *node); /** * Set the permissions for the node. This attribute is only useful when * Rock Ridge extensions are enabled. * * @param node * The node to change * @param mode * bitmask with the permissions of the node, as specified in 'man 2 stat'. * The file type bitfields will be ignored, only file permissions will be * modified. * * @since 0.6.2 */ void iso_node_set_permissions(IsoNode *node, mode_t mode); /** * Get the permissions for the node * * @since 0.6.2 */ mode_t iso_node_get_permissions(const IsoNode *node); /** * Get the mode of the node, both permissions and file type, as specified in * 'man 2 stat'. * * @since 0.6.2 */ mode_t iso_node_get_mode(const IsoNode *node); /** * Set the user id for the node. This attribute is only useful when * Rock Ridge extensions are enabled. * * @since 0.6.2 */ void iso_node_set_uid(IsoNode *node, uid_t uid); /** * Get the user id of the node. * * @since 0.6.2 */ uid_t iso_node_get_uid(const IsoNode *node); /** * Set the group id for the node. This attribute is only useful when * Rock Ridge extensions are enabled. * * @since 0.6.2 */ void iso_node_set_gid(IsoNode *node, gid_t gid); /** * Get the group id of the node. * * @since 0.6.2 */ gid_t iso_node_get_gid(const IsoNode *node); /** * Set the time of last modification of the file * * @since 0.6.2 */ void iso_node_set_mtime(IsoNode *node, time_t time); /** * Get the time of last modification of the file * * @since 0.6.2 */ time_t iso_node_get_mtime(const IsoNode *node); /** * Set the time of last access to the file * * @since 0.6.2 */ void iso_node_set_atime(IsoNode *node, time_t time); /** * Get the time of last access to the file * * @since 0.6.2 */ time_t iso_node_get_atime(const IsoNode *node); /** * Set the time of last status change of the file * * @since 0.6.2 */ void iso_node_set_ctime(IsoNode *node, time_t time); /** * Get the time of last status change of the file * * @since 0.6.2 */ time_t iso_node_get_ctime(const IsoNode *node); /** * Set whether the node will be hidden in the directory trees of RR/ISO 9660, * or of Joliet (if enabled at all), or of ISO-9660:1999 (if enabled at all). * * A hidden file does not show up by name in the affected directory tree. * For example, if a file is hidden only in Joliet, it will normally * not be visible on Windows systems, while being shown on GNU/Linux. * * If a file is not shown in any of the enabled trees, then its content will * not be written to the image, unless LIBISO_HIDE_BUT_WRITE is given (which * is available only since release 0.6.34). * * @param node * The node that is to be hidden. * @param hide_attrs * Or-combination of values from enum IsoHideNodeFlag to set the trees * in which the node's name shall be hidden. * * @since 0.6.2 */ void iso_node_set_hidden(IsoNode *node, int hide_attrs); /** * Get the hide_attrs as possibly set by iso_node_set_hidden(). * * @param node * The node to inquire. * @return * Or-combination of values from enum IsoHideNodeFlag which are * currently set for the node. * * @since 0.6.34 */ int iso_node_get_hidden(IsoNode *node); /** * Compare two nodes whether they are based on the same input and * can be considered as hardlinks to the same file objects. * * @param n1 * The first node to compare. * @param n2 * The second node to compare. * @return * -1 if n1 is smaller n2 , 0 if n1 matches n2 , 1 if n1 is larger n2 * @param flag * Bitfield for control purposes, unused yet, submit 0 * @since 0.6.20 */ int iso_node_cmp_ino(IsoNode *n1, IsoNode *n2, int flag); /** * Add a new node to a dir. Note that this function don't add a new ref to * the node, so you don't need to free it, it will be automatically freed * when the dir is deleted. Of course, if you want to keep using the node * after the dir life, you need to iso_node_ref() it. * * @param dir * the dir where to add the node * @param child * the node to add. You must ensure that the node hasn't previously added * to other dir, and that the node name is unique inside the child. * Otherwise this function will return a failure, and the child won't be * inserted. * @param replace * if the dir already contains a node with the same name, whether to * replace or not the old node with this. * @return * number of nodes in dir if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if dir or child are NULL * ISO_NODE_ALREADY_ADDED, if child is already added to other dir * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_WRONG_ARG_VALUE, if child == dir, or replace != (0,1) * * @since 0.6.2 */ int iso_dir_add_node(IsoDir *dir, IsoNode *child, enum iso_replace_mode replace); /** * Locate a node inside a given dir. * * The IsoImage context defines a maximum permissible name length and a mode * how to react on oversized names. See iso_image_set_truncate_mode(). * If the caller looks for an oversized name and image truncate mode is 1, * then this call looks for the truncated name among the nodes of dir. * * @param image * The image object to which dir belongs. * @param dir * The dir where to look for the node. * @param name * The name of the node. (Will not be changed if truncation happens.) * @param node * Location for a pointer to the node, it will filled with NULL if the dir * doesn't have a child with the given name. * The node will be owned by the dir and shouldn't be unref(). Just call * iso_node_ref() to get your own reference to the node. * Note that you can pass NULL is the only thing you want to do is check * if a node with such name already exists on dir. * @param flag * Bitfield for control purposes. * bit0= do not truncate name but lookup exactly as given. * @return * 1 node found * 0 no name truncation was needed, name not found in dir * 2 name truncation happened, truncated name not found in dir * < 0 error, see iso_dir_get_node(). * * @since 1.4.2 */ int iso_image_dir_get_node(IsoImage *image, IsoDir *dir, const char *name, IsoNode **node, int flag); /** * *** Deprecated *** * In most cases use iso_image_dir_get_node() instead. * * Locate a node inside a given dir without taking into respect name truncation * mode of an IsoImage. * * @param dir * The dir where to look for the node. * @param name * The name of the node * @param node * Location for a pointer to the node. See iso_image_get_node(). * @return * 1 node found, 0 child has no such node, < 0 error * Possible errors: * ISO_NULL_POINTER, if dir or name are NULL * * @since 0.6.2 */ int iso_dir_get_node(IsoDir *dir, const char *name, IsoNode **node); /** * Get the number of children of a directory. * * @return * >= 0 number of items, < 0 error * Possible errors: * ISO_NULL_POINTER, if dir is NULL * * @since 0.6.2 */ int iso_dir_get_children_count(IsoDir *dir); /** * Removes a child from a directory. * The child is not freed, so you will become the owner of the node. Later * you can add the node to another dir (calling iso_dir_add_node), or free * it if you don't need it (with iso_node_unref). * * @return * 1 on success, < 0 error * Possible errors: * ISO_NULL_POINTER, if node is NULL * ISO_NODE_NOT_ADDED_TO_DIR, if node doesn't belong to a dir * * @since 0.6.2 */ int iso_node_take(IsoNode *node); /** * Removes a child from a directory and free (unref) it. * If you want to keep the child alive, you need to iso_node_ref() it * before this call, but in that case iso_node_take() is a better * alternative. * * @return * 1 on success, < 0 error * * @since 0.6.2 */ int iso_node_remove(IsoNode *node); /* * Get the parent of the given iso tree node. No extra ref is added to the * returned directory, you must take your ref. with iso_node_ref() if you * need it. * * If node is the root node, the same node will be returned as its parent. * * This returns NULL if the node doesn't pertain to any tree * (it was removed/taken). * * @since 0.6.2 */ IsoDir *iso_node_get_parent(IsoNode *node); /** * Get an iterator for the children of the given dir. * * You can iterate over the children with iso_dir_iter_next. When finished, * you should free the iterator with iso_dir_iter_free. * You must not delete a child of the same dir, using iso_node_take() or * iso_node_remove(), while you're using the iterator. You can use * iso_dir_iter_take() or iso_dir_iter_remove() instead. * * You can use the iterator in the way like this * * IsoDirIter *iter; * IsoNode *node; * if ( iso_dir_get_children(dir, &iter) != 1 ) { * // handle error * } * while ( iso_dir_iter_next(iter, &node) == 1 ) { * // do something with the child * } * iso_dir_iter_free(iter); * * An iterator is intended to be used in a single iteration over the * children of a dir. Thus, it should be treated as a temporary object, * and free as soon as possible. * * @return * 1 success, < 0 error * Possible errors: * ISO_NULL_POINTER, if dir or iter are NULL * ISO_OUT_OF_MEM * * @since 0.6.2 */ int iso_dir_get_children(const IsoDir *dir, IsoDirIter **iter); /** * Get the next child. * Take care that the node is owned by its parent, and will be unref() when * the parent is freed. If you want your own ref to it, call iso_node_ref() * on it. * * @return * 1 success, 0 if dir has no more elements, < 0 error * Possible errors: * ISO_NULL_POINTER, if node or iter are NULL * ISO_ERROR, on wrong iter usage, usual caused by modiying the * dir during iteration * * @since 0.6.2 */ int iso_dir_iter_next(IsoDirIter *iter, IsoNode **node); /** * Check if there're more children. * * @return * 1 dir has more elements, 0 no, < 0 error * Possible errors: * ISO_NULL_POINTER, if iter is NULL * * @since 0.6.2 */ int iso_dir_iter_has_next(IsoDirIter *iter); /** * Free a dir iterator. * * @since 0.6.2 */ void iso_dir_iter_free(IsoDirIter *iter); /** * Removes a child from a directory during an iteration, without freeing it. * It's like iso_node_take(), but to be used during a directory iteration. * The node removed will be the last returned by the iteration. * * If you call this function twice without calling iso_dir_iter_next between * them is not allowed and you will get an ISO_ERROR in second call. * * @return * 1 on success, < 0 error * Possible errors: * ISO_NULL_POINTER, if iter is NULL * ISO_ERROR, on wrong iter usage, for example by call this before * iso_dir_iter_next. * * @since 0.6.2 */ int iso_dir_iter_take(IsoDirIter *iter); /** * Removes a child from a directory during an iteration and unref() it. * Like iso_node_remove(), but to be used during a directory iteration. * The node removed will be the one returned by the previous iteration. * * It is not allowed to call this function twice without calling * iso_dir_iter_next between the calls. * * @return * 1 on success, < 0 error * Possible errors: * ISO_NULL_POINTER, if iter is NULL * ISO_ERROR, on wrong iter usage, for example by calling this before * iso_dir_iter_next. * * @since 0.6.2 */ int iso_dir_iter_remove(IsoDirIter *iter); /** * Removes a node by iso_node_remove() or iso_dir_iter_remove(). If the node * is a directory then the whole tree of nodes underneath is removed too. * * @param node * The node to be removed. * @param boss_iter * If not NULL, then the node will be removed by * iso_dir_iter_remove(boss_iter) * else it will be removed by iso_node_remove(node). * @return * 1 is success, <0 indicates error * * @since 1.0.2 */ int iso_node_remove_tree(IsoNode *node, IsoDirIter *boss_iter); /** * @since 0.6.4 */ typedef struct iso_find_condition IsoFindCondition; /** * Create a new condition that checks if the node name matches the given * wildcard. * * @param wildcard * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_name(const char *wildcard); /** * Create a new condition that checks the node mode against a mode mask. It * can be used to check both file type and permissions. * * For example: * * iso_new_find_conditions_mode(S_IFREG) : search for regular files * iso_new_find_conditions_mode(S_IFCHR | S_IWUSR) : search for character * devices where owner has write permissions. * * @param mask * Mode mask to AND against node mode. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_mode(mode_t mask); /** * Create a new condition that checks the node gid. * * @param gid * Desired Group Id. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_gid(gid_t gid); /** * Create a new condition that checks the node uid. * * @param uid * Desired User Id. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_uid(uid_t uid); /** * Possible comparison between IsoNode and given conditions. * * @since 0.6.4 */ enum iso_find_comparisons { ISO_FIND_COND_GREATER, ISO_FIND_COND_GREATER_OR_EQUAL, ISO_FIND_COND_EQUAL, ISO_FIND_COND_LESS, ISO_FIND_COND_LESS_OR_EQUAL }; /** * Create a new condition that checks the time of last access. * * @param time * Time to compare against IsoNode atime. * @param comparison * Comparison to be done between IsoNode atime and submitted time. * Note that ISO_FIND_COND_GREATER, for example, is true if the node * time is greater than the submitted time. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_atime(time_t time, enum iso_find_comparisons comparison); /** * Create a new condition that checks the time of last modification. * * @param time * Time to compare against IsoNode mtime. * @param comparison * Comparison to be done between IsoNode mtime and submitted time. * Note that ISO_FIND_COND_GREATER, for example, is true if the node * time is greater than the submitted time. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_mtime(time_t time, enum iso_find_comparisons comparison); /** * Create a new condition that checks the time of last status change. * * @param time * Time to compare against IsoNode ctime. * @param comparison * Comparison to be done between IsoNode ctime and submitted time. * Note that ISO_FIND_COND_GREATER, for example, is true if the node * time is greater than the submitted time. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_ctime(time_t time, enum iso_find_comparisons comparison); /** * Create a new condition that check if the two given conditions are * valid. * * @param a * @param b * IsoFindCondition to compare * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_and(IsoFindCondition *a, IsoFindCondition *b); /** * Create a new condition that check if at least one the two given conditions * is valid. * * @param a * @param b * IsoFindCondition to compare * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_or(IsoFindCondition *a, IsoFindCondition *b); /** * Create a new condition that check if the given conditions is false. * * @param negate * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_not(IsoFindCondition *negate); /** * Find all directory children that match the given condition. * * @param dir * Directory where we will search children. * @param cond * Condition that the children must match in order to be returned. * It will be free together with the iterator. Remember to delete it * if this function return error. * @param iter * Iterator that returns only the children that match condition. * @return * 1 on success, < 0 on error * * @since 0.6.4 */ int iso_dir_find_children(IsoDir* dir, IsoFindCondition *cond, IsoDirIter **iter); /** * Get the destination of a node. * The returned string belongs to the node and must not be modified nor * freed. Use strdup if you really need your own copy. * * @since 0.6.2 */ const char *iso_symlink_get_dest(const IsoSymlink *link); /** * Set the destination of a symbolic * * @param link * The link node to be manipulated * @param dest * New destination for the link. It must be a non-empty string, otherwise * this function doesn't modify previous destination. * @return * 1 on success, < 0 on error * * @since 0.6.2 */ int iso_symlink_set_dest(IsoSymlink *link, const char *dest); /** * Sets the order in which a node will be written on image. The data content * of files with high weight will be written to low block addresses. * * @param node * The node which weight will be changed. If it's a dir, this function * will change the weight of all its children. For nodes other that dirs * or regular files, this function has no effect. * @param w * The weight as a integer number, the greater this value is, the * closer from the beginning of image the file will be written. * Default value at IsoNode creation is 0. * * @since 0.6.2 */ void iso_node_set_sort_weight(IsoNode *node, int w); /** * Get the sort weight of a file. * * @since 0.6.2 */ int iso_file_get_sort_weight(IsoFile *file); /** * Get the size of the file, in bytes * * @since 0.6.2 */ off_t iso_file_get_size(IsoFile *file); /** * Get the device id (major/minor numbers) of the given block or * character device file. The result is undefined for other kind * of special files, of first be sure iso_node_get_mode() returns either * S_IFBLK or S_IFCHR. * * @since 0.6.6 */ dev_t iso_special_get_dev(IsoSpecial *special); /** * Get the IsoStream that represents the contents of the given IsoFile. * The stream may be a filter stream which itself get its input from a * further stream. This may be inquired by iso_stream_get_input_stream(). * * If you iso_stream_open() the stream, iso_stream_close() it before * image generation begins. * * @return * The IsoStream. No extra ref is added, so the IsoStream belongs to the * IsoFile, and it may be freed together with it. Add your own ref with * iso_stream_ref() if you need it. * * @since 0.6.4 */ IsoStream *iso_file_get_stream(IsoFile *file); /** * Get the block lba of a file node, if it was imported from an old image. * * @param file * The file * @param lba * Will be filled with the kba * @param flag * Reserved for future usage, submit 0 * @return * 1 if lba is valid (file comes from old image and has only one section), * 0 if file was newly added, i.e. it does not come from an old image, * < 0 error, especially ISO_WRONG_ARG_VALUE if the file has more than * one file section. * * @since 0.6.4 * * @deprecated Use iso_file_get_old_image_sections(), as this function does * not work with multi-extend files. */ int iso_file_get_old_image_lba(IsoFile *file, uint32_t *lba, int flag); /** * Get the start addresses and the sizes of the data extents of a file node * if it was imported from an old image. * * @param file * The file * @param section_count * Returns the number of extent entries in sections array. * @param sections * Returns the array of file sections if section_count > 0. * In this case, apply free() to dispose it. * @param flag * Reserved for future usage, submit 0 * @return * 1 if there are valid extents (file comes from old image), * 0 if file was newly added, i.e. it does not come from an old image, * < 0 error * * @since 0.6.8 */ int iso_file_get_old_image_sections(IsoFile *file, int *section_count, struct iso_file_section **sections, int flag); /* * Like iso_file_get_old_image_lba(), but take an IsoNode. * * @return * 1 if lba is valid (file comes from old image), 0 if file was newly * added, i.e. it does not come from an old image, 2 node type has no * LBA (no regular file), < 0 error * * @since 0.6.4 */ int iso_node_get_old_image_lba(IsoNode *node, uint32_t *lba, int flag); /** * Add a new directory to the iso tree. Permissions, owner and hidden atts * are taken from parent, you can modify them later. * * @param image * The image object to which the new directory shall belong. * @param parent * The directory node where the new directory will be grafted in. * @param name * Name for the new directory. If truncation mode is set to 1, * an oversized name gets truncated before further processing. * If a node with same name already exists on parent, this function * fails with ISO_NODE_NAME_NOT_UNIQUE. * @param dir * place where to store a pointer to the newly created dir. No extra * ref is added, so you will need to call iso_node_ref() if you really * need it. You can pass NULL in this parameter if you don't need the * pointer. * @return * number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if parent or name are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_OUT_OF_MEM * ISO_RR_NAME_TOO_LONG * * @since 1.4.2 */ int iso_image_add_new_dir(IsoImage *image, IsoDir *parent, const char *name, IsoDir **dir); /** * *** Deprecated *** * use iso_image_add_new_dir() instead * * Add a new directory to the iso tree without taking into respect name * truncation mode of an IsoImage. * For detailed description of parameters, see above iso_image_add_new_dir(). * * @param parent * the dir where the new directory will be created * @param name * name for the new dir. * @param dir * place where to store a pointer to the newly created dir.i * @return * number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if parent or name are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_OUT_OF_MEM * * @since 0.6.2 */ int iso_tree_add_new_dir(IsoDir *parent, const char *name, IsoDir **dir); /** * Add a new regular file to the iso tree. Permissions are set to 0444, * owner and hidden atts are taken from parent. You can modify any of them * later. * * @param image * The image object to which the new file shall belong. * @param parent * The directory node where the new directory will be grafted in. * @param name * Name for the new file. If truncation mode is set to 1, * an oversized name gets truncated before further processing. * If a node with same name already exists on parent, this function * fails with ISO_NODE_NAME_NOT_UNIQUE. * @param stream * IsoStream for the contents of the file. The reference will be taken * by the newly created file, you will need to take an extra ref to it * if you need it. * @param file * place where to store a pointer to the newly created file. No extra * ref is added, so you will need to call iso_node_ref() if you really * need it. You can pass NULL in this parameter if you don't need the * pointer * @return * number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if parent, name or dest are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_OUT_OF_MEM * ISO_RR_NAME_TOO_LONG * * @since 1.4.2 */ int iso_image_add_new_file(IsoImage *image, IsoDir *parent, const char *name, IsoStream *stream, IsoFile **file); /** * *** Deprecated *** * use iso_image_add_new_file() instead * * Add a new regular file to the iso tree without taking into respect name * truncation mode of an IsoImage. * For detailed description of parameters, see above iso_image_add_new_file(). * * @param parent * the dir where the new file will be created * @param name * name for the new file. * @param stream * IsoStream for the contents of the file. * @param file * place where to store a pointer to the newly created file. * @return * number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if parent, name or dest are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_OUT_OF_MEM * * @since 0.6.4 */ int iso_tree_add_new_file(IsoDir *parent, const char *name, IsoStream *stream, IsoFile **file); /** * Create an IsoStream object from content which is stored in a dynamically * allocated memory buffer. The new stream will become owner of the buffer * and apply free() to it when the stream finally gets destroyed itself. * * @param buf * The dynamically allocated memory buffer with the stream content. * @param size * The number of bytes which may be read from buf. * @param stream * Will return a reference to the newly created stream. * @return * ISO_SUCCESS or <0 for error. E.g. ISO_NULL_POINTER, ISO_OUT_OF_MEM. * * @since 1.0.0 */ int iso_memory_stream_new(unsigned char *buf, size_t size, IsoStream **stream); /** * Add a new symbolic link to the directory tree. Permissions are set to 0777, * owner and hidden atts are taken from parent. You can modify any of them * later. * * @param image * The image object to which the new directory shall belong. * @param parent * The directory node where the new symlink will be grafted in. * @param name * Name for the new symlink. If truncation mode is set to 1, * an oversized name gets truncated before further processing. * If a node with same name already exists on parent, this function * fails with ISO_NODE_NAME_NOT_UNIQUE. * @param dest * The destination path of the link. The components of this path are * not checked for being oversized. * @param link * Place where to store a pointer to the newly created link. No extra * ref is added, so you will need to call iso_node_ref() if you really * need it. You can pass NULL in this parameter if you don't need the * pointer * @return * number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if parent, name or dest are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_OUT_OF_MEM * ISO_RR_NAME_TOO_LONG * * @since 1.4.2 */ int iso_image_add_new_symlink(IsoImage *image, IsoDir *parent, const char *name, const char *dest, IsoSymlink **link); /** * *** Deprecated *** * use iso_image_add_new_symlink() instead * * Add a new symlink to the directory tree without taking into respect name * truncation mode of an IsoImage. * For detailed description of parameters, see above * iso_image_add_new_isymlink(). * * @param parent * the dir where the new symlink will be created * @param name * name for the new symlink. * @param dest * destination of the link * @param link * place where to store a pointer to the newly created link. * @return * number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if parent, name or dest are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_OUT_OF_MEM * * @since 0.6.2 */ int iso_tree_add_new_symlink(IsoDir *parent, const char *name, const char *dest, IsoSymlink **link); /** * Add a new special file to the directory tree. As far as libisofs concerns, * a special file is a block device, a character device, a FIFO (named pipe) * or a socket. You can choose the specific kind of file you want to add * by setting mode properly (see man 2 stat). * * Note that special files are only written to image when Rock Ridge * extensions are enabled. Moreover, a special file is just a directory entry * in the image tree, no data is written beyond that. * * Owner and hidden atts are taken from parent. You can modify any of them * later. * * @param image * The image object to which the new special file shall belong. * @param parent * The directory node where the new special file will be grafted in. * @param name * Name for the new special file. If truncation mode is set to 1, * an oversized name gets truncated before further processing. * If a node with same name already exists on parent, this function * fails with ISO_NODE_NAME_NOT_UNIQUE. * @param mode * File type and permissions for the new node. Note that only the file * types S_IFSOCK, S_IFBLK, S_IFCHR, and S_IFIFO are allowed. * S_IFLNK, S_IFREG, or S_IFDIR are not. * @param dev * Device ID, equivalent to the st_rdev field in man 2 stat. * @param special * Place where to store a pointer to the newly created special file. No * extra ref is added, so you will need to call iso_node_ref() if you * really need it. You can pass NULL in this parameter if you don't need * the pointer. * @return * Number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if parent, name or dest are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_WRONG_ARG_VALUE if you select a incorrect mode * ISO_OUT_OF_MEM * ISO_RR_NAME_TOO_LONG * * @since 1.4.2 */ int iso_image_add_new_special(IsoImage *image, IsoDir *parent, const char *name, mode_t mode, dev_t dev, IsoSpecial **special); /** * *** Deprecated *** * use iso_image_add_new_special() instead * * Add a new special file to the directory tree without taking into respect name * truncation mode of an IsoImage. * For detailed description of parameters, see above * iso_image_add_new_special(). * * @param parent * the dir where the new special file will be created * @param name * name for the new special file. * @param mode * file type and permissions for the new node. * @param dev * device ID, equivalent to the st_rdev field in man 2 stat. * @param special * place where to store a pointer to the newly created special file. * @return * number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if parent, name or dest are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_WRONG_ARG_VALUE if you select a incorrect mode * ISO_OUT_OF_MEM * * @since 0.6.2 */ int iso_tree_add_new_special(IsoDir *parent, const char *name, mode_t mode, dev_t dev, IsoSpecial **special); /** * Set whether to follow or not symbolic links when added a file from a source * to IsoImage. Default behavior is to not follow symlinks. * * @since 0.6.2 */ void iso_tree_set_follow_symlinks(IsoImage *image, int follow); /** * Get current setting for follow_symlinks. * * @see iso_tree_set_follow_symlinks * @since 0.6.2 */ int iso_tree_get_follow_symlinks(IsoImage *image); /** * Set whether to skip or not disk files with names beginning by '.' * when adding a directory recursively. * Default behavior is to not ignore them. * * Clarification: This is not related to the IsoNode property to be hidden * in one or more of the resulting image trees as of * IsoHideNodeFlag and iso_node_set_hidden(). * * @since 0.6.2 */ void iso_tree_set_ignore_hidden(IsoImage *image, int skip); /** * Get current setting for ignore_hidden. * * @see iso_tree_set_ignore_hidden * @since 0.6.2 */ int iso_tree_get_ignore_hidden(IsoImage *image); /** * Set the replace mode, that defines the behavior of libisofs when adding * a node whit the same name that an existent one, during a recursive * directory addition. * * @since 0.6.2 */ void iso_tree_set_replace_mode(IsoImage *image, enum iso_replace_mode mode); /** * Get current setting for replace_mode. * * @see iso_tree_set_replace_mode * @since 0.6.2 */ enum iso_replace_mode iso_tree_get_replace_mode(IsoImage *image); /** * Set whether to skip or not special files. Default behavior is to not skip * them. Note that, despite of this setting, special files will never be added * to an image unless RR extensions were enabled. * * @param image * The image to manipulate. * @param skip * Bitmask to determine what kind of special files will be skipped: * bit0: ignore FIFOs * bit1: ignore Sockets * bit2: ignore char devices * bit3: ignore block devices * * @since 0.6.2 */ void iso_tree_set_ignore_special(IsoImage *image, int skip); /** * Get current setting for ignore_special. * * @see iso_tree_set_ignore_special * @since 0.6.2 */ int iso_tree_get_ignore_special(IsoImage *image); /** * Add a excluded path. These are paths that won't never added to image, and * will be excluded even when adding recursively its parent directory. * * For example, in * * iso_tree_add_exclude(image, "/home/user/data/private"); * iso_tree_add_dir_rec(image, root, "/home/user/data"); * * the directory /home/user/data/private won't be added to image. * * However, if you explicitly add a deeper dir, it won't be excluded. i.e., * in the following example. * * iso_tree_add_exclude(image, "/home/user/data"); * iso_tree_add_dir_rec(image, root, "/home/user/data/private"); * * the directory /home/user/data/private is added. On the other, side, and * following the example above, * * iso_tree_add_dir_rec(image, root, "/home/user"); * * will exclude the directory "/home/user/data". * * Absolute paths are not mandatory, you can, for example, add a relative * path such as: * * iso_tree_add_exclude(image, "private"); * iso_tree_add_exclude(image, "user/data"); * * to exclude, respectively, all files or dirs named private, and also all * files or dirs named data that belong to a folder named "user". Note that the * above rule about deeper dirs is still valid. i.e., if you call * * iso_tree_add_dir_rec(image, root, "/home/user/data/music"); * * it is included even containing "user/data" string. However, a possible * "/home/user/data/music/user/data" is not added. * * Usual wildcards, such as * or ? are also supported, with the usual meaning * as stated in "man 7 glob". For example * * // to exclude backup text files * iso_tree_add_exclude(image, "*.~"); * * @return * 1 on success, < 0 on error * * @since 0.6.2 */ int iso_tree_add_exclude(IsoImage *image, const char *path); /** * Remove a previously added exclude. * * @see iso_tree_add_exclude * @return * 1 on success, 0 exclude do not exists, < 0 on error * * @since 0.6.2 */ int iso_tree_remove_exclude(IsoImage *image, const char *path); /** * Set a callback function that libisofs will call for each file that is * added to the given image by a recursive addition function. This includes * image import. * * @param image * The image to manipulate. * @param report * pointer to a function that will be called just before a file will be * added to the image. You can control whether the file will be in fact * added or ignored. * This function should return 1 to add the file, 0 to ignore it and * continue, < 0 to abort the process * NULL is allowed if you don't want any callback. * * @since 0.6.2 */ void iso_tree_set_report_callback(IsoImage *image, int (*report)(IsoImage*, IsoFileSource*)); /** * Add a new node to the image tree, from an existing file. * * TODO comment Builder and Filesystem related issues when exposing both * * All attributes will be taken from the source file. The appropriate file * type will be created. * * @param image * The image * @param parent * The directory in the image tree where the node will be added. * @param path * The absolute path of the file in the local filesystem. * The node will have the same leaf name as the file on disk, possibly * truncated according to iso_image_set_truncate_mode(). * Its directory path depends on the parent node. * @param node * place where to store a pointer to the newly added file. No * extra ref is added, so you will need to call iso_node_ref() if you * really need it. You can pass NULL in this parameter if you don't need * the pointer. * @return * number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if image, parent or path are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_OUT_OF_MEM * ISO_RR_NAME_TOO_LONG * * @since 0.6.2 */ int iso_tree_add_node(IsoImage *image, IsoDir *parent, const char *path, IsoNode **node); /** * This is a more versatile form of iso_tree_add_node which allows to set * the node name in ISO image already when it gets added. * * Add a new node to the image tree, from an existing file, and with the * given name, that must not exist on dir. * * @param image * The image * @param parent * The directory in the image tree where the node will be added. * @param name * The leaf name that the node will have on image, possibly truncated * according to iso_image_set_truncate_mode(). * Its directory path depends on the parent node. * @param path * The absolute path of the file in the local filesystem. * @param node * place where to store a pointer to the newly added file. No * extra ref is added, so you will need to call iso_node_ref() if you * really need it. You can pass NULL in this parameter if you don't need * the pointer. * @return * number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if image, parent or path are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_OUT_OF_MEM * ISO_RR_NAME_TOO_LONG * * @since 0.6.4 */ int iso_tree_add_new_node(IsoImage *image, IsoDir *parent, const char *name, const char *path, IsoNode **node); /** * Add a new node to the image tree with the given name that must not exist * on dir. The node data content will be a byte interval out of the data * content of a file in the local filesystem. * * @param image * The image * @param parent * The directory in the image tree where the node will be added. * @param name * The leaf name that the node will have on image, possibly truncated * according to iso_image_set_truncate_mode(). * Its directory path depends on the parent node. * @param path * The absolute path of the random-access capable file in the local * filesystem. Only regular files and device files are supported. * On Linux, only regular files and block device offer random-access. * @param offset * Byte number in the given file from where to start reading data. * @param size * Max size of the file. This may be more than actually available from * byte offset to the end of the file in the local filesystem. * @param node * place where to store a pointer to the newly added file. No * extra ref is added, so you will need to call iso_node_ref() if you * really need it. You can pass NULL in this parameter if you don't need * the pointer. * @return * number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if image, parent or path are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_OUT_OF_MEM * ISO_RR_NAME_TOO_LONG * ISO_WRONG_ARG_VALUE, if path is not suitable for random access * * @since 0.6.4 * * Device files as path: @since 1.5.6 */ int iso_tree_add_new_cut_out_node(IsoImage *image, IsoDir *parent, const char *name, const char *path, off_t offset, off_t size, IsoNode **node); /** * Create a copy of the given node under a different path. If the node is * actually a directory then clone its whole subtree. * This call may fail because an IsoFile is encountered which gets fed by an * IsoStream which cannot be cloned. See also IsoStream_Iface method * clone_stream(). * Surely clonable node types are: * IsoDir, * IsoSymlink, * IsoSpecial, * IsoFile from a loaded ISO image, * IsoFile referring to local filesystem files, * IsoFile created by iso_tree_add_new_file * from a stream created by iso_memory_stream_new(), * IsoFile created by iso_tree_add_new_cut_out_node() * Silently ignored are nodes of type IsoBoot. * An IsoFile node with IsoStream filters can be cloned if all those filters * are clonable and the node would be clonable without filter. * Clonable IsoStream filters are created by: * iso_file_add_zisofs_filter() * iso_file_add_gzip_filter() * iso_file_add_external_filter() * An IsoNode with extended information as of iso_node_add_xinfo() can only be * cloned if each of the iso_node_xinfo_func instances is associated to a * clone function. See iso_node_xinfo_make_clonable(). * All internally used classes of extended information are clonable. * * The IsoImage context defines a maximum permissible name length and a mode * how to react on oversized names. See iso_image_set_truncate_mode(). * * @param image * The image object to which the node belongs. * @param node * The node to be cloned. * @param new_parent * The existing directory node where to insert the cloned node. * @param new_name * The name for the cloned node. It must not yet exist in new_parent, * unless it is a directory and node is a directory and flag bit0 is set. * @param new_node * Will return a pointer (without reference) to the newly created clone. * @param flag * Bitfield for control purposes. Submit any undefined bits as 0. * bit0= Merge directories rather than returning ISO_NODE_NAME_NOT_UNIQUE. * This will not allow to overwrite any existing node. * Attributes of existing directories will not be overwritten. * bit1= issue warning in case of new_name truncation * @return * <0 means error, 1 = new node created, * 2 = if flag bit0 is set: new_node is a directory which already existed. * * @since 1.4.2 */ int iso_image_tree_clone(IsoImage *image, IsoNode *node, IsoDir *new_parent, char *new_name, IsoNode **new_node, int flag); /** * *** Deprecated *** * use iso_image_tree_clone() instead * * Create a copy of the given node under a different path without taking * into respect name truncation mode of an IsoImage. * * @param node * The node to be cloned. * @param new_parent * The existing directory node where to insert the cloned node. * @param new_name * The name for the cloned node. It must not yet exist in new_parent, * unless it is a directory and node is a directory and flag bit0 is set. * @param new_node * Will return a pointer (without reference) to the newly created clone. * @param flag * Bitfield for control purposes. Submit any undefined bits as 0. * bit0= Merge directories rather than returning ISO_NODE_NAME_NOT_UNIQUE. * This will not allow to overwrite any existing node. * Attributes of existing directories will not be overwritten. * @return * <0 means error, 1 = new node created, * 2 = if flag bit0 is set: new_node is a directory which already existed. * * @since 1.0.2 */ int iso_tree_clone(IsoNode *node, IsoDir *new_parent, char *new_name, IsoNode **new_node, int flag); /** * Add the contents of a dir to a given directory of the iso tree. * * There are several options to control what files are added or how they are * managed. Take a look at iso_tree_set_* functions to see different options * for recursive directory addition. * * TODO comment Builder and Filesystem related issues when exposing both * * @param image * The image to which the directory belongs. * @param parent * Directory on the image tree where to add the contents of the dir * @param dir * Path to a dir in the filesystem * @return * number of nodes in parent if success, < 0 otherwise * * @since 0.6.2 */ int iso_tree_add_dir_rec(IsoImage *image, IsoDir *parent, const char *dir); /** * Inquire whether some local filesystem xattr namespace could not be explored * during node building.This may happen due to lack of administrator privileges * e.g. on FreeBSD namespace "system". * It may well be that the processed local files have no attributes which * would require special privileges. But already their existence was neither * denied nor confirmed. * * @param image * The image to inquire. * @param flag * Bitfield for control purposes: * bit0 = reset internal value to 0 * @return * 1 = Exploration was prevented * 0 = No such prevention occurred * * @since 1.5.0 */ int iso_image_was_blind_attrs(IsoImage *image, int flag); /** * Locate a node by its absolute path in the image. * The IsoImage context defines a maximum permissible name length and a mode * how to react on oversized names. See iso_image_set_truncate_mode(). * * @param image * The image to which the node belongs. * @param path * File path beginning at the root directory of image. If truncation mode * is set to 1, oversized path components will be truncated before lookup. * @param node * Location for a pointer to the node, it will be filled with NULL if the * given path does not exists on image. * The node will be owned by the image and shouldn't be unref(). Just call * iso_node_ref() to get your own reference to the node. * Note that you can pass NULL is the only thing you want to do is check * if a node with such path really exists. * * @return * 1 node found * 0 no truncation was needed, path not found in image * 2 truncation happened, truncated path component not found in parent dir * < 0 error, see iso_dir_get_node(). * * @since 1.4.2 */ int iso_image_path_to_node(IsoImage *image, const char *path, IsoNode **node); /** * *** Deprecated *** * In most cases use iso_image_path_to_node() instead * * Locate a node by its absolute path on image without taking into respect * name truncation mode of the image. * * @param image * The image to which the node belongs. * @param path * File path beginning at the root directory of image. No truncation will * happen. * @param node * Location for a pointer to the node, it will be filled with NULL if the * given path does not exists on image. See iso_image_path_to_node(). * @return * 1 found, 0 not found, < 0 error * * @since 0.6.2 */ int iso_tree_path_to_node(IsoImage *image, const char *path, IsoNode **node); /** * Get the absolute path on image of the given node. * * @return * The path on the image, that must be freed when no more needed. If the * given node is not added to any image, this returns NULL. * @since 0.6.4 */ char *iso_tree_get_node_path(IsoNode *node); /** * Get the destination node of a symbolic link within the IsoImage. * * @param img * The image wherein to try resolving the link. * @param sym * The symbolic link node which to resolve. * @param res * Will return the found destination node, in case of success. * Call iso_node_ref() / iso_node_unref() if you intend to use the node * over API calls which might in any event delete it. * @param depth * Prevents endless loops. Submit as 0. * @param flag * Bitfield for control purposes. Submit 0 for now. * @return * 1 on success, * < 0 on failure, especially ISO_DEEP_SYMLINK and ISO_DEAD_SYMLINK * * @since 1.2.4 */ int iso_tree_resolve_symlink(IsoImage *img, IsoSymlink *sym, IsoNode **res, int *depth, int flag); /* Maximum number link resolution steps before ISO_DEEP_SYMLINK gets * returned by iso_tree_resolve_symlink(). * * @since 1.2.4 */ #define LIBISO_MAX_LINK_DEPTH 100 /** * Increments the reference counting of the given IsoDataSource. * * @since 0.6.2 */ void iso_data_source_ref(IsoDataSource *src); /** * Decrements the reference counting of the given IsoDataSource, freeing it * if refcount reach 0. * * @since 0.6.2 */ void iso_data_source_unref(IsoDataSource *src); /** * Create a new IsoDataSource from a local file. This is suitable for * accessing regular files or block devices with ISO images. * * @param path * The absolute path of the file * @param src * Will be filled with the pointer to the newly created data source. * @return * 1 on success, < 0 on error. * * @since 0.6.2 */ int iso_data_source_new_from_file(const char *path, IsoDataSource **src); /** * Get the status of the buffer used by a burn_source. * * @param b * A burn_source previously obtained with * iso_image_create_burn_source(). * @param size * Will be filled with the total size of the buffer, in bytes * @param free_bytes * Will be filled with the bytes currently available in buffer * @return * < 0 error, > 0 state: * 1="active" : input and consumption are active * 2="ending" : input has ended without error * 3="failing" : input had error and ended, * 5="abandoned" : consumption has ended prematurely * 6="ended" : consumption has ended without input error * 7="aborted" : consumption has ended after input error * * @since 0.6.2 */ int iso_ring_buffer_get_status(struct burn_source *b, size_t *size, size_t *free_bytes); #define ISO_MSGS_MESSAGE_LEN 4096 /** * Control queueing and stderr printing of messages from libisofs. * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", * "NOTE", "UPDATE", "DEBUG", "ALL". * * @param queue_severity Gives the minimum limit for messages to be queued. * Default: "NEVER". If you queue messages then you * must consume them by iso_obtain_msgs(). * @param print_severity Does the same for messages to be printed directly * to stderr. * @param print_id A text prefix to be printed before the message. * @return >0 for success, <=0 for error * * @since 0.6.2 */ int iso_set_msgs_severities(char *queue_severity, char *print_severity, char *print_id); /** * Obtain the oldest pending libisofs message from the queue which has at * least the given minimum_severity. This message and any older message of * lower severity will get discarded from the queue and is then lost forever. * * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", * "NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER" * will discard the whole queue. * * @param minimum_severity * Threshold * @param error_code * Will become a unique error code as listed at the end of this header * @param imgid * Id of the image that was issued the message. * @param msg_text * Must provide at least ISO_MSGS_MESSAGE_LEN bytes. * @param severity * Will become the severity related to the message and should provide at * least 80 bytes. * @return * 1 if a matching item was found, 0 if not, <0 for severe errors * * @since 0.6.2 */ int iso_obtain_msgs(char *minimum_severity, int *error_code, int *imgid, char msg_text[], char severity[]); /** * Submit a message to the libisofs queueing system. It will be queued or * printed as if it was generated by libisofs itself. * * @param error_code * The unique error code of your message. * Submit 0 if you do not have reserved error codes within the libburnia * project. * @param msg_text * Not more than ISO_MSGS_MESSAGE_LEN characters of message text. * @param os_errno * errno related to the message. Submit 0 if the message is not related * to an operating system error. * @param severity * One of "ABORT", "FATAL", "FAILURE", "SORRY", "WARNING", "HINT", "NOTE", * "UPDATE", "DEBUG". Defaults to "FATAL". * @param origin * Submit 0 for now. * @return * 1 if message was delivered, <=0 if failure * * @since 0.6.4 */ int iso_msgs_submit(int error_code, char msg_text[], int os_errno, char severity[], int origin); /** * Convert a severity name into a severity number, which gives the severity * rank of the name. * * @param severity_name * A name as with iso_msgs_submit(), e.g. "SORRY". * @param severity_number * The rank number: the higher, the more severe. * @return * >0 success, <=0 failure * * @since 0.6.4 */ int iso_text_to_sev(char *severity_name, int *severity_number); /** * Convert a severity number into a severity name * * @param severity_number * The rank number: the higher, the more severe. * @param severity_name * A name as with iso_msgs_submit(), e.g. "SORRY". * * @since 0.6.4 */ int iso_sev_to_text(int severity_number, char **severity_name); /** * Get the id of an IsoImage, used for message reporting. This message id, * retrieved with iso_obtain_msgs(), can be used to distinguish what * IsoImage has issued a given message. * * @since 0.6.2 */ int iso_image_get_msg_id(IsoImage *image); /** * Get a textual description of a libisofs error. * * @since 0.6.2 */ const char *iso_error_to_msg(int errcode); /** * Get the severity of a given error code * @return * 0x10000000 -> DEBUG * 0x20000000 -> UPDATE * 0x30000000 -> NOTE * 0x40000000 -> HINT * 0x50000000 -> WARNING * 0x60000000 -> SORRY * 0x64000000 -> MISHAP * 0x68000000 -> FAILURE * 0x70000000 -> FATAL * 0x71000000 -> ABORT * * @since 0.6.2 */ int iso_error_get_severity(int e); /** * Get the priority of a given error. * @return * 0x00000000 -> ZERO * 0x10000000 -> LOW * 0x20000000 -> MEDIUM * 0x30000000 -> HIGH * * @since 0.6.2 */ int iso_error_get_priority(int e); /** * Get the message queue code of a libisofs error. */ int iso_error_get_code(int e); /** * Set the minimum error severity that causes a libisofs operation to * be aborted as soon as possible. * * @param severity * one of "FAILURE", "MISHAP", "SORRY", "WARNING", "HINT", "NOTE". * Severities greater or equal than FAILURE always cause program to abort. * Severities under NOTE won't never cause function abort. * @return * Previous abort priority on success, < 0 on error. * * @since 0.6.2 */ int iso_set_abort_severity(char *severity); /** * Return the messenger object handle used by libisofs. This handle * may be used by related libraries to their own compatible * messenger objects and thus to direct their messages to the libisofs * message queue. See also: libburn, API function burn_set_messenger(). * * @return the handle. Do only use with compatible * * @since 0.6.2 */ void *iso_get_messenger(); /** * Take a ref to the given IsoFileSource. * * @since 0.6.2 */ void iso_file_source_ref(IsoFileSource *src); /** * Drop your ref to the given IsoFileSource, freeing the associated system * resources if not still needed by other objects. * * @since 0.6.2 */ void iso_file_source_unref(IsoFileSource *src); /* * this are just helpers to invoque methods in class */ /** * Get the absolute path in the filesystem this file source belongs to. * * @return * the path of the FileSource inside the filesystem, it should be * freed when no more needed. * * @since 0.6.2 */ char* iso_file_source_get_path(IsoFileSource *src); /** * Get the name of the file, with the dir component of the path. * * @return * the name of the file, it should be freed when no more needed. * * @since 0.6.2 */ char* iso_file_source_get_name(IsoFileSource *src); /** * Get information about the file. * @return * 1 success, < 0 error * Error codes: * ISO_FILE_ACCESS_DENIED * ISO_FILE_BAD_PATH * ISO_FILE_DOESNT_EXIST * ISO_OUT_OF_MEM * ISO_FILE_ERROR * ISO_NULL_POINTER * * @since 0.6.2 */ int iso_file_source_lstat(IsoFileSource *src, struct stat *info); /** * Check if the process has access to read file contents. Note that this * is not necessarily related with (l)stat functions. For example, in a * filesystem implementation to deal with an ISO image, if the user has * read access to the image it will be able to read all files inside it, * despite of the particular permission of each file in the RR tree, that * are what the above functions return. * * @return * 1 if process has read access, < 0 on error * Error codes: * ISO_FILE_ACCESS_DENIED * ISO_FILE_BAD_PATH * ISO_FILE_DOESNT_EXIST * ISO_OUT_OF_MEM * ISO_FILE_ERROR * ISO_NULL_POINTER * * @since 0.6.2 */ int iso_file_source_access(IsoFileSource *src); /** * Get information about the file. If the file is a symlink, the info * returned refers to the destination. * * @return * 1 success, < 0 error * Error codes: * ISO_FILE_ACCESS_DENIED * ISO_FILE_BAD_PATH * ISO_FILE_DOESNT_EXIST * ISO_OUT_OF_MEM * ISO_FILE_ERROR * ISO_NULL_POINTER * * @since 0.6.2 */ int iso_file_source_stat(IsoFileSource *src, struct stat *info); /** * Opens the source. * @return 1 on success, < 0 on error * Error codes: * ISO_FILE_ALREADY_OPENED * ISO_FILE_ACCESS_DENIED * ISO_FILE_BAD_PATH * ISO_FILE_DOESNT_EXIST * ISO_OUT_OF_MEM * ISO_FILE_ERROR * ISO_NULL_POINTER * * @since 0.6.2 */ int iso_file_source_open(IsoFileSource *src); /** * Close a previously opened file * @return 1 on success, < 0 on error * Error codes: * ISO_FILE_ERROR * ISO_NULL_POINTER * ISO_FILE_NOT_OPENED * * @since 0.6.2 */ int iso_file_source_close(IsoFileSource *src); /** * Attempts to read up to count bytes from the given source into * the buffer starting at buf. * * The file src must be open() before calling this, and close() when no * more needed. Not valid for dirs. On symlinks it reads the destination * file. * * @param src * The given source * @param buf * Pointer to a buffer of at least count bytes where the read data will be * stored * @param count * Bytes to read * @return * number of bytes read, 0 if EOF, < 0 on error * Error codes: * ISO_FILE_ERROR * ISO_NULL_POINTER * ISO_FILE_NOT_OPENED * ISO_WRONG_ARG_VALUE -> if count == 0 * ISO_FILE_IS_DIR * ISO_OUT_OF_MEM * ISO_INTERRUPTED * * @since 0.6.2 */ int iso_file_source_read(IsoFileSource *src, void *buf, size_t count); /** * Repositions the offset of the given IsoFileSource (must be opened) to the * given offset according to the value of flag. * * @param src * The given source * @param offset * in bytes * @param flag * 0 The offset is set to offset bytes (SEEK_SET) * 1 The offset is set to its current location plus offset bytes * (SEEK_CUR) * 2 The offset is set to the size of the file plus offset bytes * (SEEK_END). * @return * Absolute offset position on the file, or < 0 on error. Cast the * returning value to int to get a valid libisofs error. * @since 0.6.4 */ off_t iso_file_source_lseek(IsoFileSource *src, off_t offset, int flag); /** * Read a directory. * * Each call to this function will return a new child, until we reach * the end of file (i.e, no more children), in that case it returns 0. * * The dir must be open() before calling this, and close() when no more * needed. Only valid for dirs. * * Note that "." and ".." children MUST NOT BE returned. * * @param src * The given source * @param child * pointer to be filled with the given child. Undefined on error or OEF * @return * 1 on success, 0 if EOF (no more children), < 0 on error * Error codes: * ISO_FILE_ERROR * ISO_NULL_POINTER * ISO_FILE_NOT_OPENED * ISO_FILE_IS_NOT_DIR * ISO_OUT_OF_MEM * * @since 0.6.2 */ int iso_file_source_readdir(IsoFileSource *src, IsoFileSource **child); /** * Read the destination of a symlink. You don't need to open the file * to call this. * * @param src * An IsoFileSource corresponding to a symbolic link. * @param buf * Allocated buffer of at least bufsiz bytes. * The destination string will be copied there, and it will be 0-terminated * if the return value indicates success or ISO_RR_PATH_TOO_LONG. * @param bufsiz * Maximum number of buf characters + 1. The string will be truncated if * it is larger than bufsiz - 1 and ISO_RR_PATH_TOO_LONG. will be returned. * @return * 1 on success, < 0 on error * Error codes: * ISO_FILE_ERROR * ISO_NULL_POINTER * ISO_WRONG_ARG_VALUE -> if bufsiz <= 0 * ISO_FILE_IS_NOT_SYMLINK * ISO_OUT_OF_MEM * ISO_FILE_BAD_PATH * ISO_FILE_DOESNT_EXIST * ISO_RR_PATH_TOO_LONG (@since 1.0.6) * * @since 0.6.2 */ int iso_file_source_readlink(IsoFileSource *src, char *buf, size_t bufsiz); /** * Get the AAIP string with encoded ACL and xattr. * (Not to be confused with ECMA-119 Extended Attributes). * @param src The file source object to be inquired. * @param aa_string Returns a pointer to the AAIP string data. If no AAIP * string is available, *aa_string becomes NULL. * (See doc/susp_aaip_2_0.txt for the meaning of AAIP.) * The caller is responsible for finally calling free() * on non-NULL results. * @param flag Bitfield for control purposes * bit0= Transfer ownership of AAIP string data. * src will free the possibly cached data and might * not be able to produce it again. * bit1= No need to get ACL (but no guarantee of exclusion) * bit2= No need to get xattr (but no guarantee of exclusion) * bit3= if not bit2: import all xattr namespaces from * local filesystem, not only "user." * @since 1.5.0 * bit4= Try to get Linux-like file attribute flags (chattr) * as "isofs.fa" * @since 1.5.8 * bit5= With bit4: Ignore non-settable Linux-like file * attribute flags and do not record "isofs.fa" * if the other flags are all zero * @since 1.5.8 * @return 1 means success (*aa_string == NULL is possible) * <0 means failure and must b a valid libisofs error code * (e.g. ISO_FILE_ERROR if no better one can be found). * @since 0.6.14 */ int iso_file_source_get_aa_string(IsoFileSource *src, unsigned char **aa_string, int flag); /** * Get the filesystem for this source. No extra ref is added, so you * must not unref the IsoFilesystem. * * @return * The filesystem, NULL on error * * @since 0.6.2 */ IsoFilesystem* iso_file_source_get_filesystem(IsoFileSource *src); /** * Take a ref to the given IsoFilesystem * * @since 0.6.2 */ void iso_filesystem_ref(IsoFilesystem *fs); /** * Drop your ref to the given IsoFilesystem, evetually freeing associated * resources. * * @since 0.6.2 */ void iso_filesystem_unref(IsoFilesystem *fs); /** * Create a new IsoFilesystem to access a existent ISO image. * * @param src * Data source to access data. * @param opts * Image read options * @param msgid * An image identifier, obtained with iso_image_get_msg_id(), used to * associated messages issued by the filesystem implementation with an * existent image. If you are not using this filesystem in relation with * any image context, just use 0x1fffff as the value for this parameter. * @param fs * Will be filled with a pointer to the filesystem that can be used * to access image contents. * @return * 1 on success, < 0 on error * * @since 0.6.2 */ int iso_image_filesystem_new(IsoDataSource *src, IsoReadOpts *opts, int msgid, IsoImageFilesystem **fs); /** * Get the volset identifier for an existent image. The returned string belong * to the IsoImageFilesystem and shouldn't be free() nor modified. * * @since 0.6.2 */ const char *iso_image_fs_get_volset_id(IsoImageFilesystem *fs); /** * Get the volume identifier for an existent image. The returned string belong * to the IsoImageFilesystem and shouldn't be free() nor modified. * * @since 0.6.2 */ const char *iso_image_fs_get_volume_id(IsoImageFilesystem *fs); /** * Get the publisher identifier for an existent image. The returned string * belong to the IsoImageFilesystem and shouldn't be free() nor modified. * * @since 0.6.2 */ const char *iso_image_fs_get_publisher_id(IsoImageFilesystem *fs); /** * Get the data preparer identifier for an existent image. The returned string * belong to the IsoImageFilesystem and shouldn't be free() nor modified. * * @since 0.6.2 */ const char *iso_image_fs_get_data_preparer_id(IsoImageFilesystem *fs); /** * Get the system identifier for an existent image. The returned string belong * to the IsoImageFilesystem and shouldn't be free() nor modified. * * @since 0.6.2 */ const char *iso_image_fs_get_system_id(IsoImageFilesystem *fs); /** * Get the application identifier for an existent image. The returned string * belong to the IsoImageFilesystem and shouldn't be free() nor modified. * * @since 0.6.2 */ const char *iso_image_fs_get_application_id(IsoImageFilesystem *fs); /** * Get the copyright file identifier for an existent image. The returned string * belong to the IsoImageFilesystem and shouldn't be free() nor modified. * * @since 0.6.2 */ const char *iso_image_fs_get_copyright_file_id(IsoImageFilesystem *fs); /** * Get the abstract file identifier for an existent image. The returned string * belong to the IsoImageFilesystem and shouldn't be free() nor modified. * * @since 0.6.2 */ const char *iso_image_fs_get_abstract_file_id(IsoImageFilesystem *fs); /** * Get the biblio file identifier for an existent image. The returned string * belong to the IsoImageFilesystem and shouldn't be free() nor modified. * * @since 0.6.2 */ const char *iso_image_fs_get_biblio_file_id(IsoImageFilesystem *fs); /** * Increment reference count of an IsoStream. * * @since 0.6.4 */ void iso_stream_ref(IsoStream *stream); /** * Decrement reference count of an IsoStream, and free it if reference count * reaches 0. * * @since 0.6.4 */ void iso_stream_unref(IsoStream *stream); /** * Opens the given stream. Remember to close the Stream before writing the * image. * * @return * 1 on success, 2 file greater than expected, 3 file smaller than * expected, < 0 on error * * @since 0.6.4 */ int iso_stream_open(IsoStream *stream); /** * Close a previously opened IsoStream. * * @return * 1 on success, < 0 on error * * @since 0.6.4 */ int iso_stream_close(IsoStream *stream); /** * Get the size of a given stream. This function should always return the same * size, even if the underlying source size changes, unless you call * iso_stream_update_size(). * * @return * IsoStream size in bytes * * @since 0.6.4 */ off_t iso_stream_get_size(IsoStream *stream); /** * Attempts to read up to count bytes from the given stream into * the buffer starting at buf. * * The stream must be open() before calling this, and close() when no * more needed. * * @return * number of bytes read, 0 if EOF, < 0 on error * * @since 0.6.4 */ int iso_stream_read(IsoStream *stream, void *buf, size_t count); /** * Whether the given IsoStream can be read several times, with the same * results. * For example, a regular file is repeatable, you can read it as many * times as you want. However, a pipe isn't. * * This function doesn't take into account if the file has been modified * between the two reads. * * @return * 1 if stream is repeatable, 0 if not, < 0 on error * * @since 0.6.4 */ int iso_stream_is_repeatable(IsoStream *stream); /** * Updates the size of the IsoStream with the current size of the * underlying source. * * @return * 1 if ok, < 0 on error (has to be a valid libisofs error code), * 0 if the IsoStream does not support this function. * @since 0.6.8 */ int iso_stream_update_size(IsoStream *stream); /** * Get an unique identifier for a given IsoStream. * * @since 0.6.4 */ void iso_stream_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, ino_t *ino_id); /** * Try to get the source path string of a stream. Meaning and availability * of this string depends on the stream.class . Expect valid results with * types "fsrc" and "cout". Result formats are * fsrc: result of file_source_get_path() * cout: result of file_source_get_path() " " offset " " size * @param stream * The stream to be inquired. * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * A copy of the path string. Apply free() when no longer needed. * NULL if no path string is available. * * @since 0.6.18 */ char *iso_stream_get_source_path(IsoStream *stream, int flag); /** * Compare two streams whether they are based on the same input and will * produce the same output. If in any doubt, then this comparison will * indicate no match. * * @param s1 * The first stream to compare. * @param s2 * The second stream to compare. * @return * -1 if s1 is smaller s2 , 0 if s1 matches s2 , 1 if s1 is larger s2 * @param flag * bit0= do not use s1->class->cmp_ino() even if available * * @since 0.6.20 */ int iso_stream_cmp_ino(IsoStream *s1, IsoStream *s2, int flag); /** * Produce a copy of a stream. It must be possible to operate both stream * objects concurrently. The success of this function depends on the * existence of a IsoStream_Iface.clone_stream() method with the stream * and with its subordinate streams, if there are any. * See iso_tree_clone() for a list of surely clonable built-in streams. * * @param old_stream * The existing stream object to be copied * @param new_stream * Will return a pointer to the copy * @param flag * Bitfield for control purposes. Submit 0 for now. * @return * >0 means success * ISO_STREAM_NO_CLONE is issued if no .clone_stream() exists * other error return values < 0 may occur depending on kind of stream * * @since 1.0.2 */ int iso_stream_clone(IsoStream *old_stream, IsoStream **new_stream, int flag); /* --------------------------------- AAIP --------------------------------- */ /** * Function to identify and manage AAIP strings as xinfo of IsoNode. * * An AAIP string contains the Attribute List with the xattr and ACL of a node * in the image tree. It is formatted according to libisofs specification * AAIP-2.0 and ready to be written into the System Use Area or Continuation * Area of a directory entry in an ISO image. * * Applications are not supposed to manipulate AAIP strings directly. * They should rather make use of the appropriate iso_node_get_* and * iso_node_set_* calls. * * AAIP represents ACLs as xattr with empty name and AAIP-specific binary * content. Local filesystems may represent ACLs as xattr with names like * "system.posix_acl_access". libisofs does not interpret those local * xattr representations of ACL directly but rather uses the ACL interface of * the local system. By default the local xattr representations of ACL will * not become part of the AAIP Attribute List via iso_local_get_attrs() and * not be attached to local files via iso_local_set_attrs(). * * @since 0.6.14 */ int aaip_xinfo_func(void *data, int flag); /** * The iso_node_xinfo_cloner function which gets associated to aaip_xinfo_func * by iso_init() or iso_init_with_flag() via iso_node_xinfo_make_clonable(). * @since 1.0.2 */ int aaip_xinfo_cloner(void *old_data, void **new_data, int flag); /** * Get the ACLs which are associated with the node. * The result will be in "long" text form as of man acl and acl_to_text(). * Call this function with flag bit15 to finally release the memory * occupied by an ACL inquiry. * * @param node * The node that is to be inquired. * @param access_text * Will return a pointer to the "access" ACL text or NULL if it is * not available and flag bit 4 is set. * @param default_text * Will return a pointer to the "default" ACL or NULL if it is * not available. * (GNU/Linux directories can have a "default" ACL which influences * the permissions of newly created files.) * @param flag * Bitfield for control purposes * bit4= if no "access" ACL is available: return *access_text == NULL * else: produce ACL from stat(2) permissions * bit15= free memory and return 1 (node may be NULL) * @return * 2 *access_text was produced from stat(2) permissions * 1 *access_text was produced from ACL of node * 0 if flag bit4 is set and no ACL is available * < 0 on error * * @since 0.6.14 */ int iso_node_get_acl_text(IsoNode *node, char **access_text, char **default_text, int flag); /** * Set the ACLs of the given node to the lists in parameters access_text and * default_text or delete them. * * The stat(2) permission bits get updated according to the new "access" ACL if * neither bit1 of parameter flag is set nor parameter access_text is NULL. * Note that S_IRWXG permission bits correspond to ACL mask permissions * if a "mask::" entry exists in the ACL. Only if there is no "mask::" then * the "group::" entry corresponds to to S_IRWXG. * * @param node * The node that is to be manipulated. * @param access_text * The text to be set into effect as "access" ACL. NULL will delete a * possibly existing "access" ACL of the node. * @param default_text * The text to be set into effect as "default" ACL. NULL will delete a * possibly existing "default" ACL of the node. * (GNU/Linux directories can have a "default" ACL which influences * the permissions of newly created files.) * @param flag * Bitfield for control purposes * bit0= Do not change the stat(2) permissions. * Caution: This can make the node's permission set inconsistent. * bit1= Ignore text parameters but rather update the "access" ACL * to the stat(2) permissions of node. If no "access" ACL exists, * then do nothing and return success. * bit2= Be verbose about failure causes. * @since 1.5.2 * @return * > 0 success * < 0 failure * * @since 0.6.14 */ int iso_node_set_acl_text(IsoNode *node, char *access_text, char *default_text, int flag); /** * Like iso_node_get_permissions but reflecting ACL entry "group::" in S_IRWXG * rather than ACL entry "mask::". This is necessary if the permissions of a * node with ACL shall be restored to a filesystem without restoring the ACL. * The same mapping happens internally when the ACL of a node is deleted. * If the node has no ACL then the result is iso_node_get_permissions(node). * @param node * The node that is to be inquired. * @return * Permission bits as of stat(2) * * @since 0.6.14 */ mode_t iso_node_get_perms_wo_acl(const IsoNode *node); /** * Get the list of xattr which is associated with the node. * The resulting data may finally be disposed by a call to this function * with flag bit15 set, or its components may be freed one-by-one. * The following values are either NULL or malloc() memory: * *names, *value_lengths, *values, (*names)[i], (*values)[i] * with 0 <= i < *num_attrs. * It is allowed to replace or reallocate those memory items in order to * to manipulate the attribute list before submitting it to other calls. * * If enabled by flag bit0, this list possibly includes the ACLs of the node. * They are encoded in a pair with empty name. It is not advisable to alter * the value or name of that pair. One may decide to erase both ACLs by * deleting this pair or to copy both ACLs by copying the content of this * pair to an empty named pair of another node. * For all other ACL purposes use iso_node_get_acl_text(). * * @param node * The node that is to be inquired. * @param num_attrs * Will return the number of name-value pairs * @param names * Will return an array of pointers to 0-terminated names * @param value_lengths * Will return an array with the lengths of values * @param values * Will return an array of pointers to strings of 8-bit bytes * @param flag * Bitfield for control purposes * bit0= obtain ACLs as attribute with empty name * bit2= with bit0: do not obtain attributes other than ACLs * bit15= free memory (node may be NULL) * @return * 1 = ok (but *num_attrs may be 0) * < 0 = error * * @since 0.6.14 */ int iso_node_get_attrs(IsoNode *node, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag); /** * Obtain the value of a particular xattr name. Make a copy of that value and * add a trailing 0 byte for caller convenience. * @param node * The node that is to be inquired. * @param name * The xattr name that shall be looked up. * @param value_length * Will return the length of value * @param value * Will return a string of 8-bit bytes. free() it when no longer needed. * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1= name found , 0= name not found , <0 indicates error * * @since 0.6.18 */ int iso_node_lookup_attr(IsoNode *node, char *name, size_t *value_length, char **value, int flag); /** * Set the list of xattr which is associated with the node. * The data get copied so that you may dispose your input data afterwards. * * If enabled by flag bit0 then the submitted list of attributes will not only * overwrite xattr but also both ACLs of the node. ACLs in the submitted list * have to reside in an attribute with empty name. * * @param node * The node that is to be manipulated. * @param num_attrs * Number of attributes * @param names * Array of pointers to 0 terminated name strings * @param value_lengths * Array of byte lengths for each value * @param values * Array of pointers to the value bytes * @param flag * Bitfield for control purposes * bit0= Do not maintain existing ACLs of the node. * Set new ACLs from value of empty name. * bit1= Do not clear the existing attribute list but merge it with * the list given by this call. * The given values override the values of their possibly existing * names. If no xattr with a given name exists, then it will be * added as new xattr. So this bit can be used to set a single * xattr without inquiring any other xattr of the node. * bit2= Delete the attributes with the given names * bit3= Allow to affect non-user attributes. * I.e. those with a non-empty name which does not begin by "user." * (The empty name is always allowed and governed by bit0.) This * deletes all previously existing attributes if not bit1 is set. * bit4= Do not affect attributes from namespace "isofs". * To be combined with bit3 for copying attributes from local * filesystem to ISO image. * @since 1.2.4 * @return * 1 = ok * < 0 = error * * @since 0.6.14 */ int iso_node_set_attrs(IsoNode *node, size_t num_attrs, char **names, size_t *value_lengths, char **values, int flag); /** * Obtain the Linux-like file attribute flags (chattr) as bit array. * The bit numbers are compatible to the FS_*_FL definitions in Linux * include file . A (possibly outdated) copy of them is in * doc/susp_aaip_isofs_names.txt, name isofs.fa . * * @param node * The node that is to be inquired. * @param lfa_flags * Will get filled with the FS_*_FL bits * @param max_bit * Will tell the highest bit that is possibly set * (-1 = surely no bit is valid) * @param flag * Bitfield for control purposes. Submit 0. * @return * 1 = ok, lfa_flags and max_bit are valid * 0 = no Linux-like file attributes are associated with the node * < 0 = error * * @since 1.5.8 */ int iso_node_get_lfa_flags(IsoNode *node, uint64_t *lfa_flags, int *max_bit, int flag); /** * Set the Linux-like file attribute flags (chattr) which are associated with * the node. * The bit numbers are compatible to the FS_*_FL definitions in Linux * include file . A (possibly outdated) copy of them is in * doc/susp_aaip_isofs_names.txt, name isofs.fa . * * @param node * The node that is to be manipulated. * @param lfa_flags * File attribute flag bits * @param flag * Bitfield for control purposes. Submit 0. * @return * 1 = ok * < 0 = error * * @since 1.5.8 */ int iso_node_set_lfa_flags(IsoNode *node, uint64_t lfa_flags, int flag); /* ----- This is an interface to ACL and xattr of the local filesystem ----- */ /** * libisofs has an internal system dependent adapter to ACL and xattr * operations. For the sake of completeness and simplicity it exposes this * functionality to its applications which might want to get and set ACLs * from local files. */ /** * Inquire whether local filesystem operations with ACL or xattr are enabled * inside libisofs. They may be disabled because of compile time decisions. * E.g. because the operating system does not support these features or * because libisofs has not yet an adapter to use them. * * @param flag * Bitfield for control purposes * bit0= inquire availability of ACL * bit1= inquire availability of xattr * bit2 - bit7= Reserved for future types. * It is permissibile to set them to 1 already now. * bit8 and higher: reserved, submit 0 * @return * Bitfield corresponding to flag. * bit0= ACL adapter is enabled * bit1= xattr adapter is enabled * bit2= Linux-like file attribute flags (chattr) adapter is enabled * @since 1.5.8 * bit3 - bit7= Reserved for future types. * It is permissibile to set them to 1 already now. * bit8 and higher: reserved, do not interpret these * * @since 1.1.6 */ int iso_local_attr_support(int flag); /** * Get an ACL of the given file in the local filesystem in long text form. * * @param disk_path * Absolute path to the file * @param text * Will return a pointer to the ACL text. If not NULL the text will be * 0 terminated and finally has to be disposed by a call to this function * with bit15 set. * @param flag * Bitfield for control purposes * bit0= get "default" ACL rather than "access" ACL * bit4= set *text = NULL and return 2 * if the ACL matches st_mode permissions. * bit5= in case of symbolic link: inquire link target * bit15= free text and return 1 * @return * 1 ok * 2 ok, trivial ACL found while bit4 is set, *text is NULL * 0 no ACL manipulation adapter available / ACL not supported on fs * -1 failure of system ACL service (see errno) * -2 attempt to inquire ACL of a symbolic link without bit4 or bit5 * or with no suitable link target * * @since 0.6.14 */ int iso_local_get_acl_text(char *disk_path, char **text, int flag); /** * Set the ACL of the given file in the local filesystem to a given list * in long text form. * * @param disk_path * Absolute path to the file * @param text * The input text (0 terminated, ACL long text form) * @param flag * Bitfield for control purposes * bit0= set "default" ACL rather than "access" ACL * bit5= in case of symbolic link: manipulate link target * @return * > 0 ok * 0 no ACL manipulation adapter available for desired ACL type * -1 failure of system ACL service (see errno) * -2 attempt to manipulate ACL of a symbolic link without bit5 * or with no suitable link target * * @since 0.6.14 */ int iso_local_set_acl_text(char *disk_path, char *text, int flag); /** * Obtain permissions of a file in the local filesystem which shall reflect * ACL entry "group::" in S_IRWXG rather than ACL entry "mask::". This is * necessary if the permissions of a disk file with ACL shall be copied to * an object which has no ACL. * @param disk_path * Absolute path to the local file which may have an "access" ACL or not. * @param flag * Bitfield for control purposes * bit5= in case of symbolic link: inquire link target * @param st_mode * Returns permission bits as of stat(2) * @return * 1 success * -1 failure of lstat() or stat() (see errno) * * @since 0.6.14 */ int iso_local_get_perms_wo_acl(char *disk_path, mode_t *st_mode, int flag); /** * Get xattr, non-trivial ACLs, and possible Linux-like file attribute flags * (chattr) of the given file in the local filesystem. * The resulting data has finally to be disposed by a call to this function * with flag bit15 set. * * ACLs will get encoded as attribute pair with empty name if this is * enabled by flag bit0. An ACL which simply reflects stat(2) permissions * will not be put into the result. * * @param disk_path * Absolute path to the file * @param num_attrs * Will return the number of name-value pairs * @param names * Will return an array of pointers to 0-terminated names * @param value_lengths * Will return an array with the lengths of values * @param values * Will return an array of pointers to 8-bit values * @param flag * Bitfield for control purposes * bit0= obtain non-trivial ACLs as attribute with empty name * bit2= do not obtain attributes other than non-trivial ACLs * bit3= do not ignore non-user attributes. * I.e. those with a name which does not begin by "user." * bit5= in case of symbolic link: inquire link target * bit6= obtain Linux-like file attribute flags (chattr) as "isofs.fa" * bit15= free memory * @return * 1 ok * 2 ok, but it is possible that attributes exist in non-user namespaces * which could not be explored due to lack of permission. * @since 1.5.0 * < 0 failure * * @since 0.6.14 */ int iso_local_get_attrs(char *disk_path, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag); /** * Attach a list of xattr and ACLs to the given file in the local filesystem. * * Given ACLs have to be encoded as attribute pair with empty name. * * @param disk_path * Absolute path to the file * @param num_attrs * Number of attributes * @param names * Array of pointers to 0 terminated name strings * @param value_lengths * Array of byte lengths for each attribute payload * @param values * Array of pointers to the attribute payload bytes * @param errnos * Array of integers to return error numbers if encountered at the attempt * to process the name-value pair at the given array index number: * 0 = no error , -1 = unknown error * >0 = errno as of local system calls to set xattr and ACLs * @param flag * Bitfield for control purposes * bit0= do not attach ACLs from a given attribute with empty name * bit3= do not ignore non-user attributes. * I.e. those with a name which does not begin by "user." * bit5= in case of symbolic link: manipulate link target * bit6= @since 1.1.6 * tolerate inappropriate presence or absence of * directory "default" ACL * bit7= @since 1.5.0 * avoid setting a name value pair if it already exists and * has the desired value. * @return * 1 = ok * < 0 = error * * @since 1.5.0 */ int iso_local_set_attrs_errno(char *disk_path, size_t num_attrs, char **names, size_t *value_lengths, char **values, int *errnos, int flag); /** * Older version of iso_local_set_attrs_errno() without the errnos array. * All other parameters and the return value have the same meaning. * * @since 0.6.14 */ int iso_local_set_attrs(char *disk_path, size_t num_attrs, char **names, size_t *value_lengths, char **values, int flag); /** * Obtain the Linux-like file attribute flags (chattr) of the given file as * bit array. * The bit numbers are compatible to the FS_*_FL definitions in Linux * include file . A (possibly outdated) copy of them is in * doc/susp_aaip_isofs_names.txt, name isofs.fa . * The attribute flags of other systems may or may not be mappable to these * flags. * @param disk_path * Path to the file * @param lfa_flags * Will get filled with the FS_*_FL bits * @param max_bit * Will tell the highest bit that is possibly set * (-1 = surely no bit is valid) * @param os_errno * Will get filled with errno if a system call fails. * Else it will be filled with 0. * @param flag * Bitfield for control purposes * bit2= do not issue own error messages with system call errors * bit5= in case of symbolic link: inquire link target * bit7= Ignore non-settable Linux-like file attribute flags * @since 1.5.8 * @return * 1 = ok, lfa_flags is valid * 2 = ok, but some local flags could not be mapped to the FS_*_FL bits * 3 = ok, symbolic link encountered, flag bit5 not set,lfa_flags set to 0 * 4 = ok, file did not bear attribute flags. E.g. because not S_IFDIR or * S_IFREG, or because unsuitable filesystem. * lfa_flags is set to 0 * <0 = error * * @since 1.5.8 */ int iso_local_get_lfa_flags(char *disk_path, uint64_t *lfa_flags, int *max_bit, int *os_errno, int flag); /** * Bring the given Linux-like file attribute flags (chattr) into effect with * the given file. * The bit numbers are compatible to the FS_*_FL definitions in Linux * include file . A (possibly outdated) copy of them is in * doc/susp_aaip_isofs_names.txt, name isofs.fa . * The attribute flags of other systems may or may not be mappable to these * flags. * @param disk_path * Path to the file * @param lfa_flags * File attribute flag bits * @param max_bit * Gives an upper limit of the highest possibly set flag bit. * (-1 = surely no bit is valid) * On Linux this must be smaller than sizeof(long) * 8. * @param os_errno * Will get filled with errno if a system call fails. * Else it will be filled with 0. * @param change_mask * Tells which bits of lfa_flags are meant to change attribute flags * of the file. Submit ~((uint64_t) 0) if all bits may cause changes. * The set of changeable bits may further be restricted by bit0 and bit1 * of parameter flag. * @param flag * Bitfield for control purposes * bit0= do not try to change known chattr superuser flags: iaj * bit1= change only known chattr settable flags: sucSiadAmjtDTCxPF * bit2= do not issue own error messages with system call errors * bit5= in case of symbolic link: operate on link target * @return * 1 = ok, all lfa_flags bits were written * 2 = ok, but some FS_*_FL bits could not be mapped to local flags * 3 = ok, symbolic link encountered, flag bit5 not set, nothing done * <0 = error * * @since 1.5.8 */ int iso_local_set_lfa_flags(char *disk_path, uint64_t lfa_flags, int max_bit, uint64_t change_mask, int *os_errno, int flag); /** * Convert the known set bits of the given Linux-like file attribute flags * to a string of flag letters like expected by chattr(1). * * @param lfa_flags * File attribute flag bits to be converted * @param flags_text * Will return the string of known set flag letters. * If it is not returned as NULL, then submit it to free(2) when * no longer needed. * @param flag * Bitfield for control purposes. * bit0= produce lsattr(1) format with '-' and peculiar sequence * (This might be more prone to ISO_LFA_UNKNOWN_BIT. * Some versions of lsattr showed less bits. Maybe newer versions * will show more bits.) * @return * >0 = success * <0 = error * ISO_LFA_UNKNOWN_BIT indicates a partial result in flags_text * * @since 1.5.8 */ int iso_util_encode_lfa_flags(uint64_t lfa_flags, char **flags_text, int flag); /** * Convert the given string of flag letters to a bit array usable by * iso_*_set_lfa_flags(). The letters are as expected by chattr(1) or shown * by lsattr(1). * * @param flags_text * The string of flag letters to be converted * @param lfa_flags * Will return the resultig file attribute flag bits * @param flag * Bitfield for control purposes. Submit 0. * @return * >0 = success * <0 = error * * @since 1.5.8 */ int iso_util_decode_lfa_flags(char *flags_text, uint64_t *lfa_flags, int flag); /** * Return the bit patterns of four classes of Linux-like file attribute flags. * They were determined according to man 1 chattr and the source of lsattr * in juli 2024. The symbolic strings and bit numbers are given here only * for convenience. Decisive are the returned bit patterns. * * @param user_settable * The flag bits which are known to be user settable with chattr: * AcCdDFmPsStTux * @param su_settable * The flag bits which are known to be settable with chattr only by * bearers of various superuser powers: * aij * @param non_settable * The flags bits which are known to be not settable or not clearable * by chattr or only known to lsattr: * eEhINVZ * * @param unknown * The flags bits for which no symbolic letter is known. Some of them are * defined in with sparse info, some not even that: * 9, 13, 21, 22, 24, 26, 27, 31 * * @since 1.5.8 */ void iso_util_get_lfa_masks(uint64_t *user_settable, uint64_t *su_settable, uint64_t *non_settable, uint64_t *unknown); /** * Return the effective change mask which will be used by * iso_local_set_lfa_flags() with the same input of parameters change_mask * and flag bits 0 and 1. * * @param change_mask * Tells which bits of lfa_flags are meant to change attribute flags * of the file. Submit ~((uint64_t) 0) if all bits may cause changes. * The set of changeable bits may further be restricted by bit0 and bit1 * of parameter flag. * @param flag * Bitfield for control purposes * bit0= do not try to change known superuser flags * bit1= change only known chattr settable flags * * @since 1.5.8 */ uint64_t iso_util_get_effective_lfa_mask(uint64_t change_mask, int flag); /* Default in case that the compile environment has no macro PATH_MAX. */ #define Libisofs_default_path_maX 4096 /* --------------------------- Filters in General -------------------------- */ /* * A filter is an IsoStream which uses another IsoStream as input. It gets * attached to an IsoFile by specialized calls iso_file_add_*_filter() which * replace its current IsoStream by the filter stream which takes over the * current IsoStream as input. * The consequences are: * iso_file_get_stream() will return the filter stream. * iso_stream_get_size() will return the (cached) size of the filtered data, * iso_stream_open() will start child processes if there are any, * iso_stream_close() will kill child processes if there are any, * iso_stream_read() will return filtered data. E.g. as data file content * during ISO image generation. * * There are external filters which run child processes * iso_file_add_external_filter() * and internal filters * iso_file_add_zisofs_filter() * iso_file_add_gzip_filter() * which may or may not be available depending on compile time settings and * installed software packages like libz. * * During image generation filters get not in effect if the original IsoStream * is an "fsrc" stream based on a file in the loaded ISO image and if the * image generation type is set to 1 by iso_write_opts_set_appendable(). */ /** * Delete the top filter stream from a data file. This is the most recent one * which was added by iso_file_add_*_filter(). * Caution: One should not do this while the IsoStream of the file is opened. * For now there is no general way to determine this state. * Filter stream implementations are urged to call .close() inside * method .free() . This will close the input stream too. * @param file * The data file node which shall get rid of one layer of content * filtering. * @param flag * Bitfield for control purposes, unused yet, submit 0. * @return * 1 on success, 0 if no filter was present * <0 on error * * @since 0.6.18 */ int iso_file_remove_filter(IsoFile *file, int flag); /** * Obtain the input stream of a filter stream. * @param stream * The stream to be inquired. * @param flag * Bitfield for control purposes. * bit0= Follow the chain of input streams and return the one at the * end of the chain. * @since 1.3.2 * @return * The input stream, if one exists. Elsewise NULL. * No extra reference to the stream is taken by this call. * * @since 0.6.18 */ IsoStream *iso_stream_get_input_stream(IsoStream *stream, int flag); /* ---------------------------- External Filters --------------------------- */ /** * Representation of an external program that shall serve as filter for * an IsoStream. This object may be shared among many IsoStream objects. * It is to be created and disposed by the application. * * The filter will act as proxy between the original IsoStream of an IsoFile. * Up to completed image generation it will be run at least twice: * for IsoStream.class.get_size() and for .open() with subsequent .read(). * So the original IsoStream has to return 1 by its .class.is_repeatable(). * The filter program has to be repeateable too. I.e. it must produce the same * output on the same input. * * @since 0.6.18 */ struct iso_external_filter_command { /* Will indicate future extensions. It has to be 0 for now. */ int version; /* Tells how many IsoStream objects depend on this command object. * One may only dispose an IsoExternalFilterCommand when this count is 0. * Initially this value has to be 0. */ int refcount; /* An optional instance id. * Set to empty text if no individual name for this object is intended. */ char *name; /* Absolute local filesystem path to the executable program. */ char *path; /* Tells the number of arguments. */ int argc; /* NULL terminated list suitable for system call execv(3). * I.e. argv[0] points to the alleged program name, * argv[1] to argv[argc] point to program arguments (if argc > 0) * argv[argc+1] is NULL */ char **argv; /* A bit field which controls behavior variations: * bit0= Do not install filter if the input has size 0. * bit1= Do not install filter if the output is not smaller than the input. * bit2= Do not install filter if the number of output blocks is * not smaller than the number of input blocks. Block size is 2048. * Assume that non-empty input yields non-empty output and thus do * not attempt to attach a filter to files smaller than 2049 bytes. * bit3= suffix removed rather than added. * (Removal and adding suffixes is the task of the application. * This behavior bit serves only as reminder for the application.) */ int behavior; /* The possible suffix which is supposed to be added to the IsoFile name * or to be removed from the name. * (This is to be done by the application, not by calls * iso_file_add_external_filter() or iso_file_remove_filter(). * The value recorded here serves only as reminder for the application.) */ char *suffix; }; typedef struct iso_external_filter_command IsoExternalFilterCommand; /** * Install an external filter command on top of the content stream of a data * file. The filter process must be repeatable. It will be run once by this * call in order to cache the output size. * @param file * The data file node which shall show filtered content. * @param cmd * The external program and its arguments which shall do the filtering. * @param flag * Bitfield for control purposes, unused yet, submit 0. * @return * 1 on success, 2 if filter installation revoked (e.g. cmd.behavior bit1) * <0 on error * * @since 0.6.18 */ int iso_file_add_external_filter(IsoFile *file, IsoExternalFilterCommand *cmd, int flag); /** * Obtain the IsoExternalFilterCommand which is associated with the given * stream. (Typically obtained from an IsoFile by iso_file_get_stream() * or from an IsoStream by iso_stream_get_input_stream()). * @param stream * The stream to be inquired. * @param cmd * Will return the external IsoExternalFilterCommand. Valid only if * the call returns 1. This does not increment cmd->refcount. * @param flag * Bitfield for control purposes, unused yet, submit 0. * @return * 1 on success, 0 if the stream is not an external filter * <0 on error * * @since 0.6.18 */ int iso_stream_get_external_filter(IsoStream *stream, IsoExternalFilterCommand **cmd, int flag); /* ---------------------------- Internal Filters --------------------------- */ /** * Install a zisofs filter on top of the content stream of a data file. * zisofs is a compression format which is decompressed by some Linux kernels. * See also doc/zisofs_format.txt and doc/zisofs2_format.txt. * The filter will not be installed if its output size is not smaller than * the size of the input stream. * This is only enabled if the use of libz was enabled at compile time. * @param file * The data file node which shall show filtered content. * @param flag * Bitfield for control purposes * bit0= Do not install filter if the number of output blocks is * not smaller than the number of input blocks. Block size is 2048. * bit1= Install a decompression filter rather than one for compression. * bit2= Only inquire availability of zisofs filtering. file may be NULL. * If available return 2, else return error. * bit3= is reserved for internal use and will be forced to 0 * @return * 1 on success, 2 if filter available but installation revoked * <0 on error, e.g. ISO_ZLIB_NOT_ENABLED * * @since 0.6.18 */ int iso_file_add_zisofs_filter(IsoFile *file, int flag); /** * Obtain the parameters of a zisofs filter stream. * @param stream * The stream to be inquired. * @param stream_type * 1=compressing ("ziso") * -1=uncompressing ("osiz") * 0 other (any obtained parameters have invalid content) * @param zisofs_algo * Algorithm as of ZF field: * {'p', 'z'} = zisofs version 1 (Zlib) * {'P', 'Z'} = zisofs version 2 (Zlib) * @param algo_num * Algorithm as of zisofs header: * 0 = zisofs version 1 (Zlib) * 1 = zisofs version 2 (Zlib) * @param block_size_log2 * Log2 of the compression block size * 15 = 32 kiB , 16 = 64 kiB , 17 = 128 kiB, ... * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1 on success, 0 if the stream has not class->type "ziso" or "osiz" * <0 on error * @since 1.5.4 */ int iso_stream_get_zisofs_par(IsoStream *stream, int *stream_type, uint8_t zisofs_algo[2], uint8_t* algo_num, int *block_size_log2, int flag); /** * Discard the buffered zisofs compression block pointers of a stream, if the * stream is a zisofs compression stream and not currently opened. * @param stream * The stream to be manipulated. * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1 on success, 0 if no block pointers were reoved, <0 on error * @since 1.5.4 */ int iso_stream_zisofs_discard_bpt(IsoStream *stream, int flag); /** * Discard all buffered zisofs compression block pointers of streams in the * given image, which are zisofs compression streams and not currently opened. * @param image * The image to be manipulated. * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * ISO_SUCCESS on success, <0 on error * @since 1.5.4 */ int iso_image_zisofs_discard_bpt(IsoImage *image, int flag); /** * Inquire the number of zisofs compression and uncompression filters which * are in use. * @param ziso_count * Will return the number of currently installed compression filters. * @param osiz_count * Will return the number of currently installed uncompression filters. * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1 on success, <0 on error * * @since 0.6.18 */ int iso_zisofs_get_refcounts(off_t *ziso_count, off_t *osiz_count, int flag); /** * Parameter set for iso_zisofs_set_params(). * * @since 0.6.18 */ struct iso_zisofs_ctrl { /* Set to 0 or 1 for this version of the structure * 0 = only members up to .block_size_log2 are valid * 1 = members up to .bpt_discard_free_ratio are valid * @since 1.5.4 */ int version; /* Compression level for zlib function compress2(). From : * "between 0 and 9: * 1 gives best speed, 9 gives best compression, 0 gives no compression" * Default is 6. */ int compression_level; /* Log2 of the block size for compression filters of zisofs version 1. * Allowed values are: * 15 = 32 kiB , 16 = 64 kiB , 17 = 128 kiB */ uint8_t block_size_log2; /* ------------------- Only valid with .version >= 1 ------------------- */ /* * Whether to produce zisofs2 (zisofs version 2) file headers and ZF * entries for files which get compressed: * 0 = do not produce zisofs2, * do not recognize zisofs2 file headers by magic * This is the default. * 1 = zisofs2 is enabled for file size 4 GiB or more * 2 = zisofs2 shall be used if zisofs is used at all * @since 1.5.4 */ int v2_enabled; /* * Log2 of block size for zisofs2 files. 0 keeps current setting. * Allowed are 15 = 32 kiB to 20 = 1024 kiB. * @since 1.5.4 */ uint8_t v2_block_size_log2; /* * Maximum overall number of blocklist pointers. 0 keeps current setting. * @since 1.5.4 */ uint64_t max_total_blocks; /* * Ignored as input value: Number of allocated zisofs block pointers. * @since 1.5.4 */ uint64_t current_total_blocks; /* * Maximum number of blocklist pointers per file. 0 keeps current setting. * @since 1.5.4 */ uint64_t max_file_blocks; /* * Number of block pointers of a file, which is considered low enough to * justify a reduction of block size. If this number is > 0, then the * lowest permissible block size is used, with which not more than the * given number of block pointers gets produced. Upper limit is the * setting of block size log2. * The inavoidable end block pointer counts. E.g. a file of 55 KiB has * 3 blocks pointers with block size log2 15, and 2 blocks pointers with * block size log2 16. * -1 disables this automatic block size adjustment. * 0 keeps the current setting. * @since 1.5.4 */ int64_t block_number_target; /* * The number of blocks from which on the block pointer list shall be * discarded on iso_stream_close() of a compressing stream. This means that * the pointers have to be determined again on next ziso_stream_compress(), * so that adding a zisofs compression filter and writing the compressed * stream needs in the sum three read runs of the input stream. * 0 keeps the current setting. * < 0 disables this file size based discarding. * @since 1.5.4 */ int64_t bpt_discard_file_blocks; /* * A ratio describing the part of max_file_blocks which shall be kept free * by intermediate discarding of block pointers. * See above bpt_discard_file_blocks . * It makes sense to set this to 1.0 if max_file_blocks is substantially * smaller than max_total_blocks. * 0.0 keeps the current setting. * < 0.0 disables this memory consumption based discarding. * @since 1.5.4 */ double bpt_discard_free_ratio; }; /** * Set the global parameters for zisofs filtering. * This is only allowed while no zisofs compression filters are installed. * i.e. ziso_count returned by iso_zisofs_get_refcounts() has to be 0. * @param params * Pointer to a structure with the intended settings. * The caller sets params->version to indicate which set of members * has been filled. I.e. params->version == 0 causes all members after * params->block_size_log2 to be ignored. * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1 on success, <0 on error * * @since 0.6.18 */ int iso_zisofs_set_params(struct iso_zisofs_ctrl *params, int flag); /** * Get the current global parameters for zisofs filtering. * @param params * Pointer to a caller provided structure which shall take the settings. * The caller sets params->version to indicate which set of members * shall be filled. I.e. params->version == 0 leaves all members after * params->block_size_log2 untouched. * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1 on success, <0 on error * * @since 0.6.18 */ int iso_zisofs_get_params(struct iso_zisofs_ctrl *params, int flag); /** * Enable or disable the production of "Z2" SUSP entries instead of "ZF" * entries for zisofs2 compressed files. * "ZF" with zisofs2 causes unaware Linux kernels to complain like: * isofs: Unknown ZF compression algorithm: PZ * "Z2" is silently ignored by unaware Linux kernels. * @param enable * 1 = produce "Z2" , 0 = only "ZF" , -1 = do not change * @return * 1 = enabled , 0 = not enabled * @since 1.5.4 */ int iso_zisofs_ctrl_susp_z2(int enable); /** * Check for the given node or for its subtree whether the data file content * effectively bears zisofs file headers and mark the outcome by an xinfo data * record if not already marked by a zisofs compressor filter. * This does not install any filter but only a hint for image generation * that the already compressed files shall get written with zisofs ZF entries. * Use this if you insert the compressed results of program mkzftree from disk * into the image. * @param node * The node which shall be checked and, if appropriate, be marked. * @param flag * Bitfield for control purposes * bit0= prepare for a run with iso_write_opts_set_appendable(,1). * Take into account that files from the imported image * do not get their content filtered. * bit1= permission to overwrite existing zisofs_zf_info * bit2= if no zisofs header is found: * create xinfo with parameters which indicate no zisofs * bit3= no tree recursion if node is a directory * bit4= skip files which stem from the imported image * bit8-bit15= maximum zisofs version to be recognized (0 means 1) * @return * 0= no zisofs data found * 1= zf xinfo added * 2= found existing zf xinfo and flag bit1 was not set * 3= both encountered: 1 and 2 * <0 means error * * @since 0.6.18 */ int iso_node_zf_by_magic(IsoNode *node, int flag); /** * Install a gzip or gunzip filter on top of the content stream of a data file. * gzip is a compression format which is used by programs gzip and gunzip. * The filter will not be installed if its output size is not smaller than * the size of the input stream. * This is only enabled if the use of libz was enabled at compile time. * @param file * The data file node which shall show filtered content. * @param flag * Bitfield for control purposes * bit0= Do not install filter if the number of output blocks is * not smaller than the number of input blocks. Block size is 2048. * bit1= Install a decompression filter rather than one for compression. * bit2= Only inquire availability of gzip filtering. file may be NULL. * If available return 2, else return error. * bit3= is reserved for internal use and will be forced to 0 * @return * 1 on success, 2 if filter available but installation revoked * <0 on error, e.g. ISO_ZLIB_NOT_ENABLED * * @since 0.6.18 */ int iso_file_add_gzip_filter(IsoFile *file, int flag); /** * Inquire the number of gzip compression and uncompression filters which * are in use. * @param gzip_count * Will return the number of currently installed compression filters. * @param gunzip_count * Will return the number of currently installed uncompression filters. * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1 on success, <0 on error * * @since 0.6.18 */ int iso_gzip_get_refcounts(off_t *gzip_count, off_t *gunzip_count, int flag); /* ---------------------------- MD5 Checksums --------------------------- */ /* Production and loading of MD5 checksums is controlled by calls iso_write_opts_set_record_md5() and iso_read_opts_set_no_md5(). For data representation details see doc/checksums.txt . */ /** * Obtain the recorded MD5 checksum of the session which was * loaded as ISO image. Such a checksum may be stored together with others * in a contiguous array at the end of the session. The session checksum * covers the data blocks from address start_lba to address end_lba - 1. * It does not cover the recorded array of md5 checksums. * Layout, size, and position of the checksum array is recorded in the xattr * "isofs.ca" of the session root node. * @param image * The image to inquire * @param start_lba * Returns the first block address covered by md5 * @param end_lba * Returns the first block address not covered by md5 any more * @param md5 * Returns 16 byte of MD5 checksum * @param flag * Bitfield for control purposes, unused yet, submit 0 * @return * 1= md5 found * 0= no md5 available (i.e. start_lba, end_lba, md5 are invalid) * <0 indicates error * * @since 0.6.22 */ int iso_image_get_session_md5(IsoImage *image, uint32_t *start_lba, uint32_t *end_lba, char md5[16], int flag); /** * Obtain the recorded MD5 checksum of a data file from the loaded * ISO image. * Such a checksum may be stored with others in a contiguous array at the end * of the loaded session. The data file then has an xattr "isofs.cx" which * gives the index in that array. * @param image * The image from which file stems. * @param file * The file object to inquire * @param md5 * Will be filled with 16 byte of MD5 checksum if available * @param flag * Bitfield for control purposes * bit0= only determine return value, do not touch parameter md5 * @return * 1= md5 found , 0= no md5 available , <0 indicates error * * @since 0.6.22 */ int iso_file_get_md5(IsoImage *image, IsoFile *file, char md5[16], int flag); /** * Read the content of an IsoFile object, compute its MD5 and attach it to * the IsoFile. It can then be inquired by iso_file_get_md5() and will get * written into the next session if this is enabled at write time and if the * image write process does not compute an MD5 from content which it copies. * So this call can be used to equip nodes from the old image with checksums * or to make available checksums of newly added files before the session gets * written. * @param file * The file object to read data from and to which to attach the checksum. * If the file is from the imported image, then its most original stream * will be checksummed. Else the possible filter streams of file will get * into effect. * @param flag * Bitfield for control purposes. Unused yet. Submit 0. * @return * 1= ok, MD5 is computed and attached , <0 indicates error * * @since 0.6.22 */ int iso_file_make_md5(IsoFile *file, int flag); /** * Check a data block whether it is a libisofs checksum tag and if so, obtain * its recorded parameters. These tags get written after volume descriptors, * directory tree and checksum array and can be detected without loading the * image tree. * One may start reading and computing MD5 at the suspected image session * start and look out for a checksum tag on the fly. See doc/checksum.txt . * @param data * A complete and aligned data block read from an ISO image session. * @param tag_type * Returns the tag type: * 0= no tag * 1= session tag * 2= superblock tag * 3= tree tag * 4= relocated 64 kB superblock tag (at LBA 0 of overwritable media) * @param pos * Returns the LBA where the tag supposes itself to be stored. * If this does not match the data block LBA then the tag might be * image data payload and should be ignored for image checksumming. * @param range_start * Returns the block address where the session is supposed to start. * If this does not match the session start on media then the image * volume descriptors have been been relocated. * A proper checksum will only emerge if computing started at range_start. * @param range_size * Returns the number of blocks beginning at range_start which are * covered by parameter md5. * @param next_tag * Returns the predicted block address of the next tag. * next_tag is valid only if not 0 and only with tag types 2, 3, 4. * With tag types 2 and 3, reading shall go on sequentially and the MD5 * computation shall continue up to that address. * With tag type 4, reading shall resume either at LBA 32 for the first * session or at the given address for the session which is to be loaded * by default. In both cases the MD5 computation shall be re-started from * scratch. * @param md5 * Returns 16 byte of MD5 checksum. * @param flag * Bitfield for control purposes: * bit0-bit7= tag type being looked for * 0= any checksum tag * 1= session tag * 2= superblock tag * 3= tree tag * 4= relocated superblock tag * @return * 0= not a checksum tag, return parameters are invalid * 1= checksum tag found, return parameters are valid * <0= error * (return parameters are valid with error ISO_MD5_AREA_CORRUPTED * but not trustworthy because the tag seems corrupted) * * @since 0.6.22 */ int iso_util_decode_md5_tag(char data[2048], int *tag_type, uint32_t *pos, uint32_t *range_start, uint32_t *range_size, uint32_t *next_tag, char md5[16], int flag); /* The following functions allow to do own MD5 computations. E.g for comparing the result with a recorded checksum. */ /** * Create a MD5 computation context and hand out an opaque handle. * * @param md5_context * Returns the opaque handle. Submitted *md5_context must be NULL or * point to freeable memory. * @return * 1= success , <0 indicates error * * @since 0.6.22 */ int iso_md5_start(void **md5_context); /** * Advance the computation of a MD5 checksum by a chunk of data bytes. * * @param md5_context * An opaque handle once returned by iso_md5_start() or iso_md5_clone(). * @param data * The bytes which shall be processed into to the checksum. * @param datalen * The number of bytes to be processed. * @return * 1= success , <0 indicates error * * @since 0.6.22 */ int iso_md5_compute(void *md5_context, char *data, int datalen); /** * Create a MD5 computation context as clone of an existing one. One may call * iso_md5_clone(old, &new, 0) and then iso_md5_end(&new, result, 0) in order * to obtain an intermediate MD5 sum before the computation goes on. * * @param old_md5_context * An opaque handle once returned by iso_md5_start() or iso_md5_clone(). * @param new_md5_context * Returns the opaque handle to the new MD5 context. Submitted * *md5_context must be NULL or point to freeable memory. * @return * 1= success , <0 indicates error * * @since 0.6.22 */ int iso_md5_clone(void *old_md5_context, void **new_md5_context); /** * Obtain the MD5 checksum from a MD5 computation context and dispose this * context. (If you want to keep the context then call iso_md5_clone() and * apply iso_md5_end() to the clone.) * * @param md5_context * A pointer to an opaque handle once returned by iso_md5_start() or * iso_md5_clone(). *md5_context will be set to NULL in this call. * @param result * Gets filled with the 16 bytes of MD5 checksum. * @return * 1= success , <0 indicates error * * @since 0.6.22 */ int iso_md5_end(void **md5_context, char result[16]); /** * Inquire whether two MD5 checksums match. (This is trivial but such a call * is convenient and completes the interface.) * @param first_md5 * A MD5 byte string as returned by iso_md5_end() * @param second_md5 * A MD5 byte string as returned by iso_md5_end() * @return * 1= match , 0= mismatch * * @since 0.6.22 */ int iso_md5_match(char first_md5[16], char second_md5[16]); /* -------------------------------- For HFS+ ------------------------------- */ /** * HFS+ attributes which may be attached to IsoNode objects as data parameter * of iso_node_add_xinfo(). As parameter proc use iso_hfsplus_xinfo_func(). * Create instances of this struct by iso_hfsplus_xinfo_new(). * * @since 1.2.4 */ struct iso_hfsplus_xinfo_data { /* Currently set to 0 by iso_hfsplus_xinfo_new() */ int version; /* Attributes available with version 0. * See: http://en.wikipedia.org/wiki/Creator_code , .../Type_code * @since 1.2.4 */ uint8_t creator_code[4]; uint8_t type_code[4]; }; /** * The function that is used to mark struct iso_hfsplus_xinfo_data at IsoNodes * and finally disposes such structs when their IsoNodes get disposed. * Usually an application does not call this function, but only uses it as * parameter of xinfo calls like iso_node_add_xinfo() or iso_node_get_xinfo(). * * @since 1.2.4 */ int iso_hfsplus_xinfo_func(void *data, int flag); /** * Create an instance of struct iso_hfsplus_xinfo_new(). * * @param flag * Bitfield for control purposes. Unused yet. Submit 0. * @return * A pointer to the new object * NULL indicates failure to allocate memory * * @since 1.2.4 */ struct iso_hfsplus_xinfo_data *iso_hfsplus_xinfo_new(int flag); /** * HFS+ blessings are relationships between HFS+ enhanced ISO images and * particular files in such images. Except for ISO_HFSPLUS_BLESS_INTEL_BOOTFILE * and ISO_HFSPLUS_BLESS_MAX, these files have to be directories. * No file may have more than one blessing. Each blessing can only be issued * to one file. * * @since 1.2.4 */ enum IsoHfsplusBlessings { /* The blessing that is issued by mkisofs option -hfs-bless. */ ISO_HFSPLUS_BLESS_PPC_BOOTDIR, /* To be applied to a data file */ ISO_HFSPLUS_BLESS_INTEL_BOOTFILE, /* Further blessings for directories */ ISO_HFSPLUS_BLESS_SHOWFOLDER, ISO_HFSPLUS_BLESS_OS9_FOLDER, ISO_HFSPLUS_BLESS_OSX_FOLDER, /* Not a blessing, but telling the number of blessings in this list */ ISO_HFSPLUS_BLESS_MAX }; /** * Issue a blessing to a particular IsoNode. If the blessing is already issued * to some file, then it gets revoked from that one. * * @param img * The image to manipulate. * @param blessing * The kind of blessing to be issued. * @param node * The file that shall be blessed. It must actually be an IsoDir or * IsoFile as is appropriate for the kind of blessing. (See above enum.) * The node may not yet bear a blessing other than the desired one. * If node is NULL, then the blessing will be revoked from any node * which bears it. * @param flag * Bitfield for control purposes. * bit0= Revoke blessing if node != NULL bears it. * bit1= Revoke any blessing of the node, regardless of parameter * blessing. If node is NULL, then revoke all blessings in * the image. * @return * 1 means successful blessing or revokation of an existing blessing. * 0 means the node already bears another blessing, or is of wrong type, * or that the node was not blessed and revokation was desired. * <0 is one of the listed error codes. * * @since 1.2.4 */ int iso_image_hfsplus_bless(IsoImage *img, enum IsoHfsplusBlessings blessing, IsoNode *node, int flag); /** * Get the array of nodes which are currently blessed. * Array indice correspond to enum IsoHfsplusBlessings. * Array element value NULL means that no node bears that blessing. * * Several usage restrictions apply. See parameter blessed_nodes. * * @param img * The image to inquire. * @param blessed_nodes * Will return a pointer to an internal node array of image. * This pointer is valid only as long as image exists and only until * iso_image_hfsplus_bless() gets used to manipulate the blessings. * Do not free() this array. Do not alter the content of the array * directly, but rather use iso_image_hfsplus_bless() and re-inquire * by iso_image_hfsplus_get_blessed(). * This call does not impose an extra reference on the nodes in the * array. So do not iso_node_unref() them. * Nodes listed here are not necessarily grafted into the tree of * the IsoImage. * @param bless_max * Will return the number of elements in the array. * It is unlikely but not outruled that it will be larger than * ISO_HFSPLUS_BLESS_MAX in this libisofs.h file. * @param flag * Bitfield for control purposes. Submit 0. * @return * 1 means success, <0 means error * * @since 1.2.4 */ int iso_image_hfsplus_get_blessed(IsoImage *img, IsoNode ***blessed_nodes, int *bless_max, int flag); /* ----------------------------- Character sets ---------------------------- */ /** * Convert the characters in name from local charset to another charset or * convert name to the representation of a particular ISO image name space. * In the latter case it is assumed that the conversion result does not * collide with any other converted name in the same directory. * I.e. this function does not take into respect possible name changes * due to collision handling. * * @param opts * Defines output charset, UCS-2 versus UTF-16 for Joliet, * and naming restrictions. * @param name * The input text which shall be converted. * @param name_len * The number of bytes in input text. * @param result * Will return the conversion result in case of success. Terminated by * a trailing zero byte. * Use free() to dispose it when no longer needed. * @param result_len * Will return the number of bytes in result (excluding trailing zero) * @param flag * Bitfield for control purposes. * bit0-bit7= Name space * 0= generic (output charset is used, * no reserved characters, no length limits) * 1= Rock Ridge (output charset is used) * 2= Joliet (output charset gets overridden by UCS-2 or * UTF-16) * 3= ECMA-119 (output charset gets overridden by the * dull ISO 9660 subset of ASCII) * 4= HFS+ (output charset gets overridden by UTF-16BE) * bit8= Treat input text as directory name * (matters for Joliet and ECMA-119) * bit9= Do not issue error messages * bit15= Reverse operation (best to be done only with results of * previous conversions) * @return * 1 means success, <0 means error * * @since 1.3.6 */ int iso_conv_name_chars(IsoWriteOpts *opts, char *name, size_t name_len, char **result, size_t *result_len, int flag); /************ Error codes and return values for libisofs ********************/ /** successfully execution */ #define ISO_SUCCESS 1 /** * special return value, it could be or not an error depending on the * context. */ #define ISO_NONE 0 /** Operation canceled (FAILURE,HIGH, -1) */ #define ISO_CANCELED 0xE830FFFF /** Unknown or unexpected fatal error (FATAL,HIGH, -2) */ #define ISO_FATAL_ERROR 0xF030FFFE /** Unknown or unexpected error (FAILURE,HIGH, -3) */ #define ISO_ERROR 0xE830FFFD /** Internal programming error. Please report this bug (FATAL,HIGH, -4) */ #define ISO_ASSERT_FAILURE 0xF030FFFC /** * NULL pointer as value for an arg. that doesn't allow NULL (FAILURE,HIGH, -5) */ #define ISO_NULL_POINTER 0xE830FFFB /** Memory allocation error (FATAL,HIGH, -6) */ #define ISO_OUT_OF_MEM 0xF030FFFA /** Interrupted by a signal (FATAL,HIGH, -7) */ #define ISO_INTERRUPTED 0xF030FFF9 /** Invalid parameter value (FAILURE,HIGH, -8) */ #define ISO_WRONG_ARG_VALUE 0xE830FFF8 /** Can't create a needed thread (FATAL,HIGH, -9) */ #define ISO_THREAD_ERROR 0xF030FFF7 /** Write error (FAILURE,HIGH, -10) */ #define ISO_WRITE_ERROR 0xE830FFF6 /** Buffer read error (FAILURE,HIGH, -11) */ #define ISO_BUF_READ_ERROR 0xE830FFF5 /** Trying to add to a dir a node already added to a dir (FAILURE,HIGH, -64) */ #define ISO_NODE_ALREADY_ADDED 0xE830FFC0 /** Node with same name already exists (FAILURE,HIGH, -65) */ #define ISO_NODE_NAME_NOT_UNIQUE 0xE830FFBF /** Trying to remove a node that was not added to dir (FAILURE,HIGH, -65) */ #define ISO_NODE_NOT_ADDED_TO_DIR 0xE830FFBE /** A requested node does not exist (FAILURE,HIGH, -66) */ #define ISO_NODE_DOESNT_EXIST 0xE830FFBD /** * Try to set the boot image of an already bootable image (FAILURE,HIGH, -67) */ #define ISO_IMAGE_ALREADY_BOOTABLE 0xE830FFBC /** Trying to use an invalid file as boot image (FAILURE,HIGH, -68) */ #define ISO_BOOT_IMAGE_NOT_VALID 0xE830FFBB /** Too many boot images (FAILURE,HIGH, -69) */ #define ISO_BOOT_IMAGE_OVERFLOW 0xE830FFBA /** No boot catalog created yet ((FAILURE,HIGH, -70) */ /* @since 0.6.34 */ #define ISO_BOOT_NO_CATALOG 0xE830FFB9 /** * Error on file operation (FAILURE,HIGH, -128) * (take a look at more specified error codes below) */ #define ISO_FILE_ERROR 0xE830FF80 /** Trying to open an already opened file (FAILURE,HIGH, -129) */ #define ISO_FILE_ALREADY_OPENED 0xE830FF7F /* @deprecated use ISO_FILE_ALREADY_OPENED instead */ #define ISO_FILE_ALREADY_OPENNED 0xE830FF7F /** Access to file is not allowed (FAILURE,HIGH, -130) */ #define ISO_FILE_ACCESS_DENIED 0xE830FF7E /** Incorrect path to file (FAILURE,HIGH, -131) */ #define ISO_FILE_BAD_PATH 0xE830FF7D /** The file does not exist in the filesystem (FAILURE,HIGH, -132) */ #define ISO_FILE_DOESNT_EXIST 0xE830FF7C /** Trying to read or close a file not opened (FAILURE,HIGH, -133) */ #define ISO_FILE_NOT_OPENED 0xE830FF7B /* @deprecated use ISO_FILE_NOT_OPENED instead */ #define ISO_FILE_NOT_OPENNED ISO_FILE_NOT_OPENED /** Directory used where no dir is expected (FAILURE,HIGH, -134) */ #define ISO_FILE_IS_DIR 0xE830FF7A /** Read error (FAILURE,HIGH, -135) */ #define ISO_FILE_READ_ERROR 0xE830FF79 /** Not dir used where a dir is expected (FAILURE,HIGH, -136) */ #define ISO_FILE_IS_NOT_DIR 0xE830FF78 /** Not symlink used where a symlink is expected (FAILURE,HIGH, -137) */ #define ISO_FILE_IS_NOT_SYMLINK 0xE830FF77 /** Can't seek to specified location (FAILURE,HIGH, -138) */ #define ISO_FILE_SEEK_ERROR 0xE830FF76 /** File not supported in ECMA-119 tree and thus ignored (WARNING,MEDIUM, -139) */ #define ISO_FILE_IGNORED 0xD020FF75 /* A file is bigger than supported by used standard (FAILURE,HIGH, -140) */ #define ISO_FILE_TOO_BIG 0xE830FF74 /* File read error during image creation (MISHAP,HIGH, -141) */ #define ISO_FILE_CANT_WRITE 0xE430FF73 /* Can't convert filename to requested charset (WARNING,MEDIUM, -142) */ #define ISO_FILENAME_WRONG_CHARSET 0xD020FF72 /* This was once a HINT. Deprecated now. */ #define ISO_FILENAME_WRONG_CHARSET_OLD 0xC020FF72 /* File can't be added to the tree (SORRY,HIGH, -143) */ #define ISO_FILE_CANT_ADD 0xE030FF71 /** * File path break specification constraints and will be ignored * (WARNING,MEDIUM, -144) */ #define ISO_FILE_IMGPATH_WRONG 0xD020FF70 /** * Offset greater than file size (FAILURE,HIGH, -150) * @since 0.6.4 */ #define ISO_FILE_OFFSET_TOO_BIG 0xE830FF6A /** Charset conversion error (FAILURE,HIGH, -256) */ #define ISO_CHARSET_CONV_ERROR 0xE830FF00 /** * Too many files to mangle, i.e. we cannot guarantee unique file names * (FAILURE,HIGH, -257) */ #define ISO_MANGLE_TOO_MUCH_FILES 0xE830FEFF /* image related errors */ /** * Wrong or damaged Primary Volume Descriptor (FAILURE,HIGH, -320) * This could mean that the file is not a valid ISO image. */ #define ISO_WRONG_PVD 0xE830FEC0 /** Wrong or damaged RR entry (SORRY,HIGH, -321) */ #define ISO_WRONG_RR 0xE030FEBF /** Unsupported RR feature (SORRY,HIGH, -322) */ #define ISO_UNSUPPORTED_RR 0xE030FEBE /** Wrong or damaged ECMA-119 (FAILURE,HIGH, -323) */ #define ISO_WRONG_ECMA119 0xE830FEBD /** Unsupported ECMA-119 feature (FAILURE,HIGH, -324) */ #define ISO_UNSUPPORTED_ECMA119 0xE830FEBC /** Wrong or damaged El-Torito catalog (WARN,HIGH, -325) */ #define ISO_WRONG_EL_TORITO 0xD030FEBB /** Unsupported El-Torito feature (WARN,HIGH, -326) */ #define ISO_UNSUPPORTED_EL_TORITO 0xD030FEBA /** Can't patch an isolinux boot image (SORRY,HIGH, -327) */ #define ISO_ISOLINUX_CANT_PATCH 0xE030FEB9 /** Unsupported SUSP feature (SORRY,HIGH, -328) */ #define ISO_UNSUPPORTED_SUSP 0xE030FEB8 /** Error on a RR entry that can be ignored (WARNING,HIGH, -329) */ #define ISO_WRONG_RR_WARN 0xD030FEB7 /** Error on a RR entry that can be ignored (HINT,MEDIUM, -330) */ #define ISO_SUSP_UNHANDLED 0xC020FEB6 /** Multiple ER SUSP entries found (WARNING,HIGH, -331) */ #define ISO_SUSP_MULTIPLE_ER 0xD030FEB5 /** Unsupported volume descriptor found (HINT,MEDIUM, -332) */ #define ISO_UNSUPPORTED_VD 0xC020FEB4 /** El-Torito related warning (WARNING,HIGH, -333) */ #define ISO_EL_TORITO_WARN 0xD030FEB3 /** Image write cancelled (MISHAP,HIGH, -334) */ #define ISO_IMAGE_WRITE_CANCELED 0xE430FEB2 /** El-Torito image is hidden (WARNING,HIGH, -335) */ #define ISO_EL_TORITO_HIDDEN 0xD030FEB1 /** AAIP info with ACL or xattr in ISO image will be ignored (NOTE, HIGH, -336) */ #define ISO_AAIP_IGNORED 0xB030FEB0 /** Error with decoding ACL from AAIP info (FAILURE, HIGH, -337) */ #define ISO_AAIP_BAD_ACL 0xE830FEAF /** Error with encoding ACL for AAIP (FAILURE, HIGH, -338) */ #define ISO_AAIP_BAD_ACL_TEXT 0xE830FEAE /** AAIP processing for ACL or xattr not enabled at compile time (FAILURE, HIGH, -339) */ #define ISO_AAIP_NOT_ENABLED 0xE830FEAD /** Error with decoding AAIP info for ACL or xattr (FAILURE, HIGH, -340) */ #define ISO_AAIP_BAD_AASTRING 0xE830FEAC /** Error with reading ACL or xattr from local file (FAILURE, HIGH, -341) */ #define ISO_AAIP_NO_GET_LOCAL 0xE830FEAB /** Error with reading ACL or xattr from local file (SORRY, HIGH, -341) */ #define ISO_AAIP_NO_GET_LOCAL_S 0xE030FEAB /** Error with attaching ACL or xattr to local file (FAILURE, HIGH, -342) */ #define ISO_AAIP_NO_SET_LOCAL 0xE830FEAA /** Error with attaching ACL or xattr to local file (SORRY, HIGH, -342) */ #define ISO_AAIP_NO_SET_LOCAL_S 0xE030FEAA /** Unallowed attempt to set an xattr with non-userspace name (FAILURE, HIGH, -343) */ #define ISO_AAIP_NON_USER_NAME 0xE830FEA9 /** Too many references on a single IsoExternalFilterCommand (FAILURE, HIGH, -344) */ #define ISO_EXTF_TOO_OFTEN 0xE830FEA8 /** Use of zlib was not enabled at compile time (FAILURE, HIGH, -345) */ #define ISO_ZLIB_NOT_ENABLED 0xE830FEA7 /** File too large. Cannot apply zisofs filter. (FAILURE, HIGH, -346) */ #define ISO_ZISOFS_TOO_LARGE 0xE830FEA6 /** Filter input differs from previous run (FAILURE, HIGH, -347) */ #define ISO_FILTER_WRONG_INPUT 0xE830FEA5 /** zlib compression/decompression error (FAILURE, HIGH, -348) */ #define ISO_ZLIB_COMPR_ERR 0xE830FEA4 /** Input stream is not in a supported zisofs format (FAILURE, HIGH, -349) */ #define ISO_ZISOFS_WRONG_INPUT 0xE830FEA3 /** Cannot set global zisofs parameters while filters exist (FAILURE, HIGH, -350) */ #define ISO_ZISOFS_PARAM_LOCK 0xE830FEA2 /** Premature EOF of zlib input stream (FAILURE, HIGH, -351) */ #define ISO_ZLIB_EARLY_EOF 0xE830FEA1 /** * Checksum area or checksum tag appear corrupted (WARNING,HIGH, -352) * @since 0.6.22 */ #define ISO_MD5_AREA_CORRUPTED 0xD030FEA0 /** * Checksum mismatch between checksum tag and data blocks * (FAILURE, HIGH, -353) * @since 0.6.22 */ #define ISO_MD5_TAG_MISMATCH 0xE830FE9F /** * Checksum mismatch in System Area, Volume Descriptors, or directory tree. * (FAILURE, HIGH, -354) * @since 0.6.22 */ #define ISO_SB_TREE_CORRUPTED 0xE830FE9E /** * Unexpected checksum tag type encountered. (WARNING, HIGH, -355) * @since 0.6.22 */ #define ISO_MD5_TAG_UNEXPECTED 0xD030FE9D /** * Misplaced checksum tag encountered. (WARNING, HIGH, -356) * @since 0.6.22 */ #define ISO_MD5_TAG_MISPLACED 0xD030FE9C /** * Checksum tag with unexpected address range encountered. * (WARNING, HIGH, -357) * @since 0.6.22 */ #define ISO_MD5_TAG_OTHER_RANGE 0xD030FE9B /** * Detected file content changes while it was written into the image. * (MISHAP, HIGH, -358) * @since 0.6.22 */ #define ISO_MD5_STREAM_CHANGE 0xE430FE9A /** * Session does not start at LBA 0. scdbackup checksum tag not written. * (WARNING, HIGH, -359) * @since 0.6.24 */ #define ISO_SCDBACKUP_TAG_NOT_0 0xD030FE99 /** * The setting of iso_write_opts_set_ms_block() leaves not enough room * for the prescibed size of iso_write_opts_set_overwrite_buf(). * (FAILURE, HIGH, -360) * @since 0.6.36 */ #define ISO_OVWRT_MS_TOO_SMALL 0xE830FE98 /** * The partition offset is not 0 and leaves not not enough room for * system area, volume descriptors, and checksum tags of the first tree. * (FAILURE, HIGH, -361) */ #define ISO_PART_OFFST_TOO_SMALL 0xE830FE97 /** * The ring buffer is smaller than 64 kB + partition offset. * (FAILURE, HIGH, -362) */ #define ISO_OVWRT_FIFO_TOO_SMALL 0xE830FE96 /** Use of libjte was not enabled at compile time (FAILURE, HIGH, -363) */ #define ISO_LIBJTE_NOT_ENABLED 0xE830FE95 /** Failed to start up Jigdo Template Extraction (FAILURE, HIGH, -364) */ #define ISO_LIBJTE_START_FAILED 0xE830FE94 /** Failed to finish Jigdo Template Extraction (FAILURE, HIGH, -365) */ #define ISO_LIBJTE_END_FAILED 0xE830FE93 /** Failed to process file for Jigdo Template Extraction (MISHAP, HIGH, -366) */ #define ISO_LIBJTE_FILE_FAILED 0xE430FE92 /** Too many MIPS Big Endian boot files given (max. 15) (FAILURE, HIGH, -367)*/ #define ISO_BOOT_TOO_MANY_MIPS 0xE830FE91 /** Boot file missing in image (MISHAP, HIGH, -368) */ #define ISO_BOOT_FILE_MISSING 0xE430FE90 /** Partition number out of range (FAILURE, HIGH, -369) */ #define ISO_BAD_PARTITION_NO 0xE830FE8F /** Cannot open data file for appended partition (FAILURE, HIGH, -370) */ #define ISO_BAD_PARTITION_FILE 0xE830FE8E /** May not combine MBR partition with non-MBR system area (FAILURE, HIGH, -371) */ #define ISO_NON_MBR_SYS_AREA 0xE830FE8D /** Displacement offset leads outside 32 bit range (FAILURE, HIGH, -372) */ #define ISO_DISPLACE_ROLLOVER 0xE830FE8C /** File name cannot be written into ECMA-119 untranslated (FAILURE, HIGH, -373) */ #define ISO_NAME_NEEDS_TRANSL 0xE830FE8B /** Data file input stream object offers no cloning method (FAILURE, HIGH, -374) */ #define ISO_STREAM_NO_CLONE 0xE830FE8A /** Extended information class offers no cloning method (FAILURE, HIGH, -375) */ #define ISO_XINFO_NO_CLONE 0xE830FE89 /** Found copied superblock checksum tag (WARNING, HIGH, -376) */ #define ISO_MD5_TAG_COPIED 0xD030FE88 /** Rock Ridge leaf name too long (FAILURE, HIGH, -377) */ #define ISO_RR_NAME_TOO_LONG 0xE830FE87 /** Reserved Rock Ridge leaf name (FAILURE, HIGH, -378) */ #define ISO_RR_NAME_RESERVED 0xE830FE86 /** Rock Ridge path too long (FAILURE, HIGH, -379) */ #define ISO_RR_PATH_TOO_LONG 0xE830FE85 /** Attribute name cannot be represented (FAILURE, HIGH, -380) */ #define ISO_AAIP_BAD_ATTR_NAME 0xE830FE84 /** ACL text contains multiple entries of user::, group::, other:: (FAILURE, HIGH, -381) */ #define ISO_AAIP_ACL_MULT_OBJ 0xE830FE83 /** File sections do not form consecutive array of blocks (FAILURE, HIGH, -382) */ #define ISO_SECT_SCATTERED 0xE830FE82 /** Too many Apple Partition Map entries requested (FAILURE, HIGH, -383) */ #define ISO_BOOT_TOO_MANY_APM 0xE830FE81 /** Overlapping Apple Partition Map entries requested (FAILURE, HIGH, -384) */ #define ISO_BOOT_APM_OVERLAP 0xE830FE80 /** Too many GPT entries requested (FAILURE, HIGH, -385) */ #define ISO_BOOT_TOO_MANY_GPT 0xE830FE7F /** Overlapping GPT entries requested (FAILURE, HIGH, -386) */ #define ISO_BOOT_GPT_OVERLAP 0xE830FE7E /** Too many MBR partition entries requested (FAILURE, HIGH, -387) */ #define ISO_BOOT_TOO_MANY_MBR 0xE830FE7D /** Overlapping MBR partition entries requested (FAILURE, HIGH, -388) */ #define ISO_BOOT_MBR_OVERLAP 0xE830FE7C /** Attempt to use an MBR partition entry twice (FAILURE, HIGH, -389) */ #define ISO_BOOT_MBR_COLLISION 0xE830FE7B /** No suitable El Torito EFI boot image for exposure as GPT partition (FAILURE, HIGH, -390) */ #define ISO_BOOT_NO_EFI_ELTO 0xE830FE7A /** Not a supported HFS+ or APM block size (FAILURE, HIGH, -391) */ #define ISO_BOOT_HFSP_BAD_BSIZE 0xE830FE79 /** APM block size prevents coexistence with GPT (FAILURE, HIGH, -392) */ #define ISO_BOOT_APM_GPT_BSIZE 0xE830FE78 /** Name collision in HFS+, mangling not possible (FAILURE, HIGH, -393) */ #define ISO_HFSP_NO_MANGLE 0xE830FE77 /** Symbolic link cannot be resolved (FAILURE, HIGH, -394) */ #define ISO_DEAD_SYMLINK 0xE830FE76 /** Too many chained symbolic links (FAILURE, HIGH, -395) */ #define ISO_DEEP_SYMLINK 0xE830FE75 /** Unrecognized file type in ISO image (FAILURE, HIGH, -396) */ #define ISO_BAD_ISO_FILETYPE 0xE830FE74 /** Filename not suitable for character set UCS-2 (WARNING, HIGH, -397) */ #define ISO_NAME_NOT_UCS2 0xD030FE73 /** File name collision during ISO image import (WARNING, HIGH, -398) */ #define ISO_IMPORT_COLLISION 0xD030FE72 /** Incomplete HP-PA PALO boot parameters (FAILURE, HIGH, -399) */ #define ISO_HPPA_PALO_INCOMPL 0xE830FE71 /** HP-PA PALO boot address exceeds 2 GB (FAILURE, HIGH, -400) */ #define ISO_HPPA_PALO_OFLOW 0xE830FE70 /** HP-PA PALO file is not a data file (FAILURE, HIGH, -401) */ #define ISO_HPPA_PALO_NOTREG 0xE830FE6F /** HP-PA PALO command line too long (FAILURE, HIGH, -402) */ #define ISO_HPPA_PALO_CMDLEN 0xE830FE6E /** Problems encountered during inspection of System Area (WARN, HIGH, -403) */ #define ISO_SYSAREA_PROBLEMS 0xD030FE6D /** Unrecognized inquiry for system area property (FAILURE, HIGH, -404) */ #define ISO_INQ_SYSAREA_PROP 0xE830FE6C /** DEC Alpha Boot Loader file is not a data file (FAILURE, HIGH, -405) */ #define ISO_ALPHA_BOOT_NOTREG 0xE830FE6B /** No data source of imported ISO image available (WARNING, HIGH, -406) */ #define ISO_NO_KEPT_DATA_SRC 0xD030FE6A /** Malformed description string for interval reader (FAILURE, HIGH, -407) */ #define ISO_MALFORMED_READ_INTVL 0xE830FE69 /** Unreadable file, premature EOF, or failure to seek for interval reader (WARNING, HIGH, -408) */ #define ISO_INTVL_READ_PROBLEM 0xD030FE68 /** Cannot arrange content of data files in surely reproducible way (NOTE, HIGH, -409) */ #define ISO_NOT_REPRODUCIBLE 0xB030FE67 /** May not write boot info into filtered stream of boot image (FAILURE, HIGH, -410) */ #define ISO_PATCH_FILTERED_BOOT 0xE830FE66 /** Boot image to large to buffer for writing boot info (FAILURE, HIGH, -411) */ #define ISO_PATCH_OVERSIZED_BOOT 0xE830FE65 /** File name had to be truncated and MD5 marked (WARNING, HIGH, -412) */ #define ISO_RR_NAME_TRUNCATED 0xD030FE64 /** File name truncation length changed by loaded image info (NOTE, HIGH, -413) */ #define ISO_TRUNCATE_ISOFSNT 0xB030FE63 /** General note (NOTE, HIGH, -414) */ #define ISO_GENERAL_NOTE 0xB030FE62 /** Unrecognized file type of IsoFileSrc object (SORRY, HIGH, -415) */ #define ISO_BAD_FSRC_FILETYPE 0xE030FE61 /** Cannot derive GPT GUID from undefined pseudo-UUID volume timestamp (FAILURE, HIGH, -416) */ #define ISO_GPT_NO_VOL_UUID 0xE830FE60 /** Unrecognized GPT disk GUID setup mode (FAILURE, HIGH, -417) */ #define ISO_BAD_GPT_GUID_MODE 0xE830FE5F /** Unable to obtain root directory (FATAL,HIGH, -418) */ #define ISO_NO_ROOT_DIR 0xF030FE5E /** Zero sized, oversized, or mislocated SUSP CE area found (FAILURE, HIGH, -419) */ #define ISO_SUSP_WRONG_CE_SIZE 0xE830FE5D /** Multi-session would overwrite imported_iso interval (FAILURE, HIGH, -420) */ #define ISO_MULTI_OVER_IMPORTED 0xE830FE5C /** El-Torito EFI image is hidden (NOTE,HIGH, -421) */ #define ISO_ELTO_EFI_HIDDEN 0xB030FE5B /** Too many files in HFS+ directory tree (FAILURE, HIGH, -422) */ #define ISO_HFSPLUS_TOO_MANY_FILES 0xE830FE5A /** Too many zisofs block pointers needed overall (FAILURE, HIGH, -423) */ #define ISO_ZISOFS_TOO_MANY_PTR 0xE830FE59 /** Prevented zisofs block pointer counter underrun (WARNING,MEDIUM, -424) */ #define ISO_ZISOFS_BPT_UNDERRUN 0xD020FE58 /** Cannot obtain size of zisofs compressed stream (FAILURE, HIGH, -425) */ #define ISO_ZISOFS_UNKNOWN_SIZE 0xE830FE57 /** Undefined IsoReadImageFeatures name (SORRY, HIGH, -426) */ #define ISO_UNDEF_READ_FEATURE 0xE030FE56 /** Too many CE entries for single file (FAILURE,HIGH, -427) */ #define ISO_TOO_MANY_CE 0xE830FE55 /** Too many CE entries for single file when mounted by Linux (WARNING,HIGH, -428) */ #define ISO_TOO_MANY_CE_FOR_LINUX 0xD030FE54 /** Too many CE entries for single file, omitting attributes (WARNING,HIGH, -429) */ #define ISO_CE_REMOVING_ATTR 0xD030FE53 /** Unknown Linux-like chattr letter encountered during conversion (WARNING,HIGH, -430) */ #define ISO_LFA_UNKNOWN_LETTER 0xD030FE52 /** Unknown Linux-like file attribute flag bit encountered during conversion (WARNING,HIGH, -431) */ #define ISO_LFA_UNKNOWN_BIT 0xD030FE51 /** Local Linux-like file attribute processing not enabled at compile time (SORRY,HIGH, -432) */ #define ISO_LFA_NOT_ENABLED 0xE030FE50 /** Error with getting Linux-like file attributes of local file (SORRY,HIGH, -433) */ #define ISO_LFA_NO_GET_LOCAL 0xE030FE4F /** Error with setting Linux-like file attributes of local file (SORRY,HIGH, -434) */ #define ISO_LFA_NO_SET_LOCAL 0xE030FE4E /** Failure to open local file for setting Linux-like file attributes (SORRY,HIGH, -435) */ #define ISO_LFA_NO_OPEN_LOCAL 0xE030FE4D /* Internal developer note: Place new error codes directly above this comment. Newly introduced errors must get a message entry in libisofs/messages.c, function iso_error_to_msg() */ /* ! PLACE NEW ERROR CODES ABOVE. NOT AFTER THIS LINE ! */ /** Read error occurred with IsoDataSource (SORRY,HIGH, -513) */ #define ISO_DATA_SOURCE_SORRY 0xE030FCFF /** Read error occurred with IsoDataSource (MISHAP,HIGH, -513) */ #define ISO_DATA_SOURCE_MISHAP 0xE430FCFF /** Read error occurred with IsoDataSource (FAILURE,HIGH, -513) */ #define ISO_DATA_SOURCE_FAILURE 0xE830FCFF /** Read error occurred with IsoDataSource (FATAL,HIGH, -513) */ #define ISO_DATA_SOURCE_FATAL 0xF030FCFF /* ! PLACE NEW ERROR CODES SEVERAL LINES ABOVE. NOT HERE ! */ /* ------------------------------------------------------------------------- */ #ifdef LIBISOFS_WITHOUT_LIBBURN /** This is a copy from the API of libburn-0.6.0 (under GPL). It is supposed to be as stable as any overall include of libburn.h. I.e. if this definition is out of sync then you cannot rely on any contract that was made with libburn.h. Libisofs does not need to be linked with libburn at all. But if it is linked with libburn then it must be libburn-0.4.2 or later. An application that provides own struct burn_source objects and does not include libburn/libburn.h has to define LIBISOFS_WITHOUT_LIBBURN before including libisofs/libisofs.h in order to make this copy available. */ /** Data source interface for tracks. This allows to use arbitrary program code as provider of track input data. Objects compliant to this interface are either provided by the application or by API calls of libburn: burn_fd_source_new(), burn_file_source_new(), and burn_fifo_source_new(). libisofs acts as "application" and implements an own class of burn_source. Instances of that class are handed out by iso_image_create_burn_source(). */ struct burn_source { /** Reference count for the data source. MUST be 1 when a new source is created and thus the first reference is handed out. Increment it to take more references for yourself. Use burn_source_free() to destroy your references to it. */ int refcount; /** Read data from the source. Semantics like with read(2), but MUST either deliver the full buffer as defined by size or MUST deliver EOF (return 0) or failure (return -1) at this call or at the next following call. I.e. the only incomplete buffer may be the last one from that source. libburn will read a single sector by each call to (*read). The size of a sector depends on BURN_MODE_*. The known range is 2048 to 2352. If this call is reading from a pipe then it will learn about the end of data only when that pipe gets closed on the feeder side. So if the track size is not fixed or if the pipe delivers less than the predicted amount or if the size is not block aligned, then burning will halt until the input process closes the pipe. IMPORTANT: If this function pointer is NULL, then the struct burn_source is of version >= 1 and the job of .(*read)() is done by .(*read_xt)(). See below, member .version. */ int (*read)(struct burn_source *, unsigned char *buffer, int size); /** Read subchannel data from the source (NULL if lib generated) WARNING: This is an obscure feature with CD raw write modes. Unless you checked the libburn code for correctness in that aspect you should not rely on raw writing with own subchannels. ADVICE: Set this pointer to NULL. */ int (*read_sub)(struct burn_source *, unsigned char *buffer, int size); /** Get the size of the source's data. Return 0 means unpredictable size. If application provided (*get_size) allows return 0, then the application MUST provide a fully functional (*set_size). */ off_t (*get_size)(struct burn_source *); /* @since 0.3.2 */ /** Program the reply of (*get_size) to a fixed value. It is advised to implement this by a attribute off_t fixed_size; in *data . The read() function does not have to take into respect this fake setting. It is rather a note of libburn to itself. Possibly necessary truncation or padding is done in libburn. Truncation is usually considered a misburn. Padding is considered ok. libburn is supposed to work even if (*get_size) ignores the setting by (*set_size). But your application will not be able to enforce fixed track sizes by burn_track_set_size() and possibly even padding might be left out. */ int (*set_size)(struct burn_source *source, off_t size); /** Clean up the source specific data. This function will be called once by burn_source_free() when the last referer disposes the source. */ void (*free_data)(struct burn_source *); /** Next source, for when a source runs dry and padding is disabled WARNING: This is an obscure feature. Set to NULL at creation and from then on leave untouched and uninterpreted. */ struct burn_source *next; /** Source specific data. Here the various source classes express their specific properties and the instance objects store their individual management data. E.g. data could point to a struct like this: struct app_burn_source { struct my_app *app_handle; ... other individual source parameters ... off_t fixed_size; }; Function (*free_data) has to be prepared to clean up and free the struct. */ void *data; /* @since 0.4.2 */ /** Valid only if above member .(*read)() is NULL. This indicates a version of struct burn_source younger than 0. From then on, member .version tells which further members exist in the memory layout of struct burn_source. libburn will only touch those announced extensions. Versions: 0 has .(*read)() != NULL, not even .version is present. 1 has .version, .(*read_xt)(), .(*cancel)() */ int version; /** This substitutes for (*read)() in versions above 0. */ int (*read_xt)(struct burn_source *, unsigned char *buffer, int size); /** Informs the burn_source that the consumer of data prematurely ended reading. This call may or may not be issued by libburn before (*free_data)() is called. */ int (*cancel)(struct burn_source *source); }; #endif /* LIBISOFS_WITHOUT_LIBBURN */ /* ----------------------------- Bug Fixes ----------------------------- */ /* currently none being tested */ /* ---------------------------- Improvements --------------------------- */ /* currently none being tested */ /* ---------------------------- Experiments ---------------------------- */ /* Experiment: Write obsolete RR entries with Rock Ridge. I suspect Solaris wants to see them. DID NOT HELP: Solaris knows only RRIP_1991A. #define Libisofs_with_rrip_rR yes */ #ifdef __cplusplus } /* extern "C" */ #endif #endif /*LIBISO_LIBISOFS_H_*/ LIBISOFS6 { global: aaip_xinfo_cloner; aaip_xinfo_func; el_torito_get_bootable; el_torito_get_boot_media_type; el_torito_get_boot_platform_id; el_torito_get_full_load; el_torito_get_id_string; el_torito_get_isolinux_options; el_torito_get_load_seg; el_torito_get_load_size; el_torito_get_selection_crit; el_torito_patch_isolinux_image; el_torito_seems_boot_info_table; el_torito_set_boot_platform_id; el_torito_set_full_load; el_torito_set_id_string; el_torito_set_isolinux_options; el_torito_set_load_seg; el_torito_set_load_size; el_torito_set_no_bootable; el_torito_set_selection_crit; iso_assess_written_features; iso_conv_name_chars; iso_crc32_gpt; iso_data_source_new_from_file; iso_data_source_ref; iso_data_source_unref; iso_dir_add_node; iso_dir_find_children; iso_dir_get_children; iso_dir_get_children_count; iso_dir_get_node; iso_dir_iter_free; iso_dir_iter_has_next; iso_dir_iter_next; iso_dir_iter_remove; iso_dir_iter_take; iso_error_get_code; iso_error_get_priority; iso_error_get_severity; iso_error_to_msg; iso_file_add_external_filter; iso_file_add_gzip_filter; iso_file_add_zisofs_filter; iso_file_get_md5; iso_file_get_old_image_lba; iso_file_get_old_image_sections; iso_file_get_size; iso_file_get_sort_weight; iso_file_get_stream; iso_file_make_md5; iso_file_remove_filter; iso_file_source_access; iso_file_source_close; iso_file_source_get_aa_string; iso_file_source_get_filesystem; iso_file_source_get_name; iso_file_source_get_path; iso_file_source_lseek; iso_file_source_lstat; iso_file_source_open; iso_file_source_read; iso_file_source_readdir; iso_file_source_readlink; iso_file_source_ref; iso_file_source_stat; iso_file_source_unref; iso_filesystem_ref; iso_filesystem_unref; iso_finish; iso_fs_global_id; iso_generate_gpt_guid; iso_get_local_charset; iso_get_messenger; iso_gzip_get_refcounts; iso_hfsplus_xinfo_func; iso_hfsplus_xinfo_new; iso_image_add_boot_image; iso_image_add_mips_boot_file; iso_image_add_new_dir; iso_image_add_new_file; iso_image_add_new_special; iso_image_add_new_symlink; iso_image_attach_data; iso_image_create_burn_source; iso_image_dir_get_node; iso_image_filesystem_new; iso_image_fs_get_abstract_file_id; iso_image_fs_get_application_id; iso_image_fs_get_biblio_file_id; iso_image_fs_get_copyright_file_id; iso_image_fs_get_data_preparer_id; iso_image_fs_get_publisher_id; iso_image_fs_get_system_id; iso_image_fs_get_volset_id; iso_image_fs_get_volume_id; iso_image_generator_is_running; iso_image_get_abstract_file_id; iso_image_get_all_boot_imgs; iso_image_get_alpha_boot; iso_image_get_app_use; iso_image_get_application_id; iso_image_get_attached_data; iso_image_get_biblio_file_id; iso_image_get_bootcat; iso_image_get_boot_image; iso_image_get_copyright_file_id; iso_image_get_data_preparer_id; iso_image_get_hppa_palo; iso_image_get_ignore_aclea; iso_image_get_mips_boot_files; iso_image_get_msg_id; iso_image_get_publisher_id; iso_image_get_pvd_times; iso_image_get_root; iso_image_get_session_md5; iso_image_get_sparc_core; iso_image_get_system_area; iso_image_get_system_id; iso_image_get_truncate_mode; iso_image_get_volset_id; iso_image_get_volume_id; iso_image_give_up_mips_boot; iso_image_hfsplus_bless; iso_image_hfsplus_get_blessed; iso_image_import; iso_image_new; iso_image_path_to_node; iso_image_ref; iso_image_remove_boot_image; iso_image_report_el_torito; iso_image_report_system_area; iso_image_set_abstract_file_id; iso_image_set_alpha_boot; iso_image_set_app_use; iso_image_set_application_id; iso_image_set_biblio_file_id; iso_image_set_boot_catalog_hidden; iso_image_set_boot_catalog_weight; iso_image_set_boot_image; iso_image_set_copyright_file_id; iso_image_set_data_preparer_id; iso_image_set_hppa_palo; iso_image_set_ignore_aclea; iso_image_set_node_name; iso_image_set_publisher_id; iso_image_set_sparc_core; iso_image_set_system_id; iso_image_set_truncate_mode; iso_image_set_volset_id; iso_image_set_volume_id; iso_image_tree_clone; iso_image_unref; iso_image_update_sizes; iso_image_was_blind_attrs; iso_image_zisofs_discard_bpt; iso_init; iso_init_with_flag; iso_interval_reader_destroy; iso_interval_reader_new; iso_interval_reader_read; iso_lib_is_compatible; iso_lib_version; iso_local_attr_support; iso_local_get_acl_text; iso_local_get_attrs; iso_local_get_perms_wo_acl; iso_local_set_acl_text; iso_local_set_attrs; iso_local_set_attrs_errno; iso_md5_clone; iso_md5_compute; iso_md5_end; iso_md5_match; iso_md5_start; iso_memory_stream_new; iso_msgs_submit; iso_new_find_conditions_and; iso_new_find_conditions_atime; iso_new_find_conditions_ctime; iso_new_find_conditions_gid; iso_new_find_conditions_mode; iso_new_find_conditions_mtime; iso_new_find_conditions_name; iso_new_find_conditions_not; iso_new_find_conditions_or; iso_new_find_conditions_uid; iso_node_add_xinfo; iso_node_cmp_ino; iso_node_get_acl_text; iso_node_get_atime; iso_node_get_attrs; iso_node_get_ctime; iso_node_get_gid; iso_node_get_hidden; iso_node_get_mode; iso_node_get_mtime; iso_node_get_name; iso_node_get_next_xinfo; iso_node_get_old_image_lba; iso_node_get_parent; iso_node_get_permissions; iso_node_get_perms_wo_acl; iso_node_get_type; iso_node_get_uid; iso_node_get_xinfo; iso_node_lookup_attr; iso_node_ref; iso_node_remove; iso_node_remove_all_xinfo; iso_node_remove_tree; iso_node_remove_xinfo; iso_node_set_acl_text; iso_node_set_atime; iso_node_set_attrs; iso_node_set_ctime; iso_node_set_gid; iso_node_set_hidden; iso_node_set_mtime; iso_node_set_name; iso_node_set_permissions; iso_node_set_sort_weight; iso_node_set_uid; iso_node_take; iso_node_unref; iso_node_xinfo_get_cloner; iso_node_xinfo_make_clonable; iso_node_zf_by_magic; iso_nowtime; iso_obtain_msgs; iso_read_image_feature_named; iso_read_image_features_destroy; iso_read_image_features_get_size; iso_read_image_features_has_eltorito; iso_read_image_features_has_iso1999; iso_read_image_features_has_joliet; iso_read_image_features_has_rockridge; iso_read_image_features_rr_loaded; iso_read_image_features_text; iso_read_image_features_tree_loaded; iso_read_opts_auto_input_charset; iso_read_opts_free; iso_read_opts_keep_import_src; iso_read_opts_load_system_area; iso_read_opts_new; iso_read_opts_set_default_gid; iso_read_opts_set_default_permissions; iso_read_opts_set_default_uid; iso_read_opts_set_ecma119_map; iso_read_opts_set_input_charset; iso_read_opts_set_joliet_map; iso_read_opts_set_new_inos; iso_read_opts_set_no_aaip; iso_read_opts_set_no_iso1999; iso_read_opts_set_no_joliet; iso_read_opts_set_no_md5; iso_read_opts_set_no_rockridge; iso_read_opts_set_preferjoliet; iso_read_opts_set_start_block; iso_ring_buffer_get_status; iso_set_abort_severity; iso_set_local_charset; iso_set_msgs_severities; iso_sev_to_text; iso_special_get_dev; iso_stream_clone; iso_stream_close; iso_stream_cmp_ino; iso_stream_get_external_filter; iso_stream_get_id; iso_stream_get_input_stream; iso_stream_get_size; iso_stream_get_source_path; iso_stream_get_zisofs_par; iso_stream_is_repeatable; iso_stream_open; iso_stream_read; iso_stream_ref; iso_stream_unref; iso_stream_update_size; iso_stream_zisofs_discard_bpt; iso_symlink_get_dest; iso_symlink_set_dest; iso_text_to_sev; iso_tree_add_dir_rec; iso_tree_add_exclude; iso_tree_add_new_cut_out_node; iso_tree_add_new_dir; iso_tree_add_new_file; iso_tree_add_new_node; iso_tree_add_new_special; iso_tree_add_new_symlink; iso_tree_add_node; iso_tree_clone; iso_tree_get_follow_symlinks; iso_tree_get_ignore_hidden; iso_tree_get_ignore_special; iso_tree_get_node_path; iso_tree_get_replace_mode; iso_tree_path_to_node; iso_tree_remove_exclude; iso_tree_resolve_symlink; iso_tree_set_follow_symlinks; iso_tree_set_ignore_hidden; iso_tree_set_ignore_special; iso_tree_set_replace_mode; iso_tree_set_report_callback; iso_truncate_leaf_name; iso_util_decode_md5_tag; iso_write_opts_attach_jte; iso_write_opts_detach_jte; iso_write_opts_free; iso_write_opts_get_data_start; iso_write_opts_new; iso_write_opts_set_aaip; iso_write_opts_set_aaip_susp_1_10; iso_write_opts_set_allow_7bit_ascii; iso_write_opts_set_allow_deep_paths; iso_write_opts_set_allow_dir_id_ext; iso_write_opts_set_allow_full_ascii; iso_write_opts_set_allow_longer_paths; iso_write_opts_set_allow_lowercase; iso_write_opts_set_always_gmt; iso_write_opts_set_appendable; iso_write_opts_set_appended_as_apm; iso_write_opts_set_appended_as_gpt; iso_write_opts_set_default_dir_mode; iso_write_opts_set_default_file_mode; iso_write_opts_set_default_gid; iso_write_opts_set_default_timestamp; iso_write_opts_set_default_uid; iso_write_opts_set_dir_rec_mtime; iso_write_opts_set_disc_label; iso_write_opts_set_efi_bootp; iso_write_opts_set_fat; iso_write_opts_set_fifo_size; iso_write_opts_set_gpt_guid; iso_write_opts_set_hardlinks; iso_write_opts_set_hfsp_block_size; iso_write_opts_set_hfsp_serial_number; iso_write_opts_set_hfsplus; iso_write_opts_set_iso1999; iso_write_opts_set_iso_level; iso_write_opts_set_iso_mbr_part_type; iso_write_opts_set_iso_type_guid; iso_write_opts_set_joliet; iso_write_opts_set_joliet_long_names; iso_write_opts_set_joliet_longer_paths; iso_write_opts_set_joliet_utf16; iso_write_opts_set_max_37_char_filenames; iso_write_opts_set_max_ce_entries; iso_write_opts_set_ms_block; iso_write_opts_set_no_force_dots; iso_write_opts_set_old_empty; iso_write_opts_set_omit_version_numbers; iso_write_opts_set_output_charset; iso_write_opts_set_overwrite_buf; iso_write_opts_set_part_offset; iso_write_opts_set_part_like_isohybrid; iso_write_opts_set_part_type_guid; iso_write_opts_set_partition_img; iso_write_opts_set_prep_img; iso_write_opts_set_pvd_times; iso_write_opts_set_record_md5; iso_write_opts_set_relaxed_vol_atts; iso_write_opts_set_replace_mode; iso_write_opts_set_replace_timestamps; iso_write_opts_set_rockridge; iso_write_opts_set_rr_reloc; iso_write_opts_set_rrip_1_10_px_ino; iso_write_opts_set_rrip_version_1_10; iso_write_opts_set_scdbackup_tag; iso_write_opts_set_sort_files; iso_write_opts_set_system_area; iso_write_opts_set_tail_blocks; iso_write_opts_set_untranslated_name_len; iso_write_opts_set_will_cancel; iso_zisofs_ctrl_susp_z2; iso_zisofs_get_params; iso_zisofs_get_refcounts; iso_zisofs_set_params; serial_id; local: *; }; LIBISOFS6_1.5.8 { iso_local_get_lfa_flags; iso_local_set_lfa_flags; iso_node_get_lfa_flags; iso_node_set_lfa_flags; iso_util_decode_lfa_flags; iso_util_encode_lfa_flags; iso_util_get_effective_lfa_mask; iso_util_get_lfa_masks; } LIBISOFS6; /* * Copyright (c) 2002 - 2008 H. Peter Anvin * Copyright (c) 2008 - 2022 Thomas Schmitt * with special credits to Matthew Garrett for isohybrid with GPT and APM * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #ifdef HAVE_STDINT_H #include #else #ifdef HAVE_INTTYPES_H #include #endif #endif #include #include #include #include #include #include /* for gettimeofday() */ #include #include "filesrc.h" #include "ecma119.h" #include "eltorito.h" #include "system_area.h" #include "image.h" #include "messages.h" /* This code stems from syslinux-3.72/utils/isohybrid, a perl script under GPL which is Copyright 2002-2008 H. Peter Anvin. Line numbers in comments refer to the lines of that script. It has been analyzed and re-written in C language 2008 by Thomas Schmitt, and is now under the licenses to which H.Peter Anvin agreed: http://syslinux.zytor.com/archives/2008-November/011105.html Date: Mon, 10 Nov 2008 08:36:46 -0800 From: H. Peter Anvin I hereby give permission for this code, translated to the C language, to be released under either the LGPL or the MIT/ISC/2-clause BSD licenses or both, at your option. Sincerely, H. Peter Anvin In the context of GNU xorriso, this code is under GPLv3+ derived from LGPL. In the context of libisofs this code derives its matching free software license from above stem licenses, typically from LGPL. In case its generosity is needed, here is the 2-clause BSD license: make_isohybrid_mbr.c is copyright 2002-2008 H. Peter Anvin and 2008-2015 Thomas Schmitt 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED `AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROVIDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* A helper function. One could replace it by one or two macros. */ static int lsb_to_buf(char **wpt, uint32_t value, int bits, int flag) { int b; for (b = 0; b < bits; b += 8) *((unsigned char *) ((*wpt)++)) = (value >> b) & 0xff; return (1); } /* ====================================================================== */ /* Deprecated Function */ /* ====================================================================== */ /* * Create a MBR for an isohybrid enabled ISOLINUX boot image. * * It is assumed that the caller has verified the readiness of the boot image * by checking for 0xfb 0xc0 0x78 0x70 at bytes 0x40 to 0x43 of isolinux.bin. * * @param bin_lba The predicted LBA of isolinux.bin within the emerging ISO. * @param img_blocks The predicted number of 2048 byte blocks in the ISO. * It will get rounded up to full MBs and that many blocks * must really be written as ISO 9660 image. * @param mbr A buffer of at least 512 bytes to take the result which is * to be written as the very beginning of the ISO. * @param flag unused yet, submit 0 * @return <0 = fatal, 0 = failed , 1 = ok , 2 = ok with size warning */ int make_isohybrid_mbr(int bin_lba, int *img_blocks, char *mbr, int flag) { /* According to H. Peter Anvin this binary code stems from syslinux-3.72/mbr/isohdpfx.S */ static int mbr_code_size = 271; static unsigned char mbr_code[271] = { 0xfa, 0x31, 0xc0, 0x8e, 0xd8, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x89, 0xe6, 0x06, 0x57, 0x52, 0x8e, 0xc0, 0xfb, 0xfc, 0xbf, 0x00, 0x06, 0xb9, 0x00, 0x01, 0xf3, 0xa5, 0xea, 0x20, 0x06, 0x00, 0x00, 0x52, 0xb4, 0x41, 0xbb, 0xaa, 0x55, 0x31, 0xc9, 0x30, 0xf6, 0xf9, 0xcd, 0x13, 0x72, 0x14, 0x81, 0xfb, 0x55, 0xaa, 0x75, 0x0e, 0x83, 0xe1, 0x01, 0x74, 0x09, 0x66, 0xc7, 0x06, 0xb4, 0x06, 0xb4, 0x42, 0xeb, 0x15, 0x5a, 0x51, 0xb4, 0x08, 0xcd, 0x13, 0x83, 0xe1, 0x3f, 0x51, 0x0f, 0xb6, 0xc6, 0x40, 0x50, 0xf7, 0xe1, 0x52, 0x50, 0xbb, 0x00, 0x7c, 0xb9, 0x04, 0x00, 0x66, 0xa1, 0xb0, 0x07, 0xe8, 0x40, 0x00, 0x72, 0x74, 0x66, 0x40, 0x80, 0xc7, 0x02, 0xe2, 0xf4, 0x66, 0x81, 0x3e, 0x40, 0x7c, 0xfb, 0xc0, 0x78, 0x70, 0x75, 0x07, 0xfa, 0xbc, 0xf4, 0x7b, 0xe9, 0xc6, 0x75, 0xe8, 0x79, 0x00, 0x69, 0x73, 0x6f, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x2e, 0x62, 0x69, 0x6e, 0x20, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x72, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x75, 0x70, 0x74, 0x2e, 0x0d, 0x0a, 0x66, 0x60, 0x66, 0x31, 0xd2, 0x66, 0x52, 0x66, 0x50, 0x06, 0x53, 0x6a, 0x01, 0x6a, 0x10, 0x89, 0xe6, 0x66, 0xf7, 0x36, 0xf0, 0x7b, 0xc0, 0xe4, 0x06, 0x88, 0xe1, 0x88, 0xc5, 0x92, 0xf6, 0x36, 0xf6, 0x7b, 0x88, 0xc6, 0x08, 0xe1, 0x41, 0xb8, 0x01, 0x02, 0x8a, 0x16, 0xfa, 0x7b, 0xcd, 0x13, 0x8d, 0x64, 0x10, 0x66, 0x61, 0xc3, 0xe8, 0x1e, 0x00, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x0d, 0x0a, 0x5e, 0xac, 0xb4, 0x0e, 0x8a, 0x3e, 0x62, 0x04, 0xb3, 0x07, 0xcd, 0x10, 0x3c, 0x0a, 0x75, 0xf1, 0xcd, 0x18, 0xf4, 0xeb, 0xfd }; static int h = 64, s = 32; int i, id; char *wpt; off_t imgsize, cylsize, frac, padding, c, cc; /* For generating a weak random number */ struct timeval tv; if (bin_lba < 0 || bin_lba >= (1 << 29)) return (0); /* 1 TB limit of signed 32 bit addressing of 512 byte blocks */ /* 84: Gets size of image in bytes. 89: */ imgsize = ((off_t) *img_blocks) * (off_t) 2048; /* 90: Computes $padding, size of padded image and number $c of pseudo-cylinders ($h and $s are constants set in line 24). 102: $cc is $c curbed to 1024. */ cylsize = h * s * 512; frac = imgsize % cylsize; padding = (frac > 0 ? cylsize - frac : 0); imgsize += padding; *img_blocks = imgsize / (off_t) 2048; c = imgsize / cylsize; if (c > 1024) { cc = 1024; } else cc = c; /* 107: Brings the hex-encoded bytes from the file __END__ into 113: the MBR buffer. (This function holds them in mbr_code[].) */ for (i = 0; i < mbr_code_size; i++) mbr[i] = mbr_code[i]; /* 118: Pads up to 432 */ for (i = mbr_code_size; i < 432; i++) mbr[i] = 0; /* From here on use write cursor wpt */ wpt = mbr + 432; /* 120: Converts LBA of isolinux.bin to blocksize 512 and writes to buffer. Appends 4 zero bytes. */ lsb_to_buf(&wpt, bin_lba * 4, 32, 0); lsb_to_buf(&wpt, 0, 32, 0); /* 121: I do not understand where $id should eventually come from. An environment variable ? 125: Whatever, i use some 32-bit random value with no crypto strength. */ gettimeofday(&tv, NULL); id = 0xffffffff & (tv.tv_sec ^ (tv.tv_usec * 2000)); /* 126: Adds 4 id bytes and 2 zero bytes. */ lsb_to_buf(&wpt, id, 32, 0); lsb_to_buf(&wpt, 0, 16, 0); /* 129: Composes 16 byte record from the parameters determined 147: so far. Appends it to buffer and add 48 zero bytes. */ /* There are 4 "partition slots". Only the first is non-zero here. In the perl script, the fields are set in variables and then written to buffer. I omit the variables. */ /* 0x80 */ lsb_to_buf(&wpt, 0x80, 8, 0); /* bhead */ lsb_to_buf(&wpt, 0, 8, 0); /* bsect */ lsb_to_buf(&wpt, 1, 8, 0); /* bcyl */ lsb_to_buf(&wpt, 0, 8, 0); /* fstype */ lsb_to_buf(&wpt, 0x83, 8, 0); /* ehead */ lsb_to_buf(&wpt, h - 1, 8, 0); /* esect */ lsb_to_buf(&wpt, s + (((cc - 1) & 0x300) >> 2), 8, 0); /* ecyl */ lsb_to_buf(&wpt, (cc - 1) & 0xff, 8, 0); /* 0 */ lsb_to_buf(&wpt, 0, 32, 0); /* psize */ lsb_to_buf(&wpt, c * h * s, 32, 0); /* Fill the other three slots with zeros */ for (i = 0; i < 3 * 4; i++) lsb_to_buf(&wpt, 0, 32, 0); /* 148: Appends bytes 0x55 , 0xAA. */ lsb_to_buf(&wpt, 0x55, 8, 0); lsb_to_buf(&wpt, 0xaa, 8, 0); return (1); } /* ====================================================================== */ /* The New MBR Producer */ /* ====================================================================== */ /* The new MBR producer for isohybrid is a slightly generalized version of the deprecated function make_isohybrid_mbr(). It complies to the urge of H.Peter Anvin not to hardcode MBR templates but rather to read a file from the Syslinux tree, and to patch it as was done with the old MBR producer. The old algorithm was clarified publicly by the following mail. Changes towards the old algorithm: - 512-byte LBA of boot image is extended to 64 bit (we stay with 32) - check for a magic number is now gone The new implementation tries to use similar terms as the mail in order to facilitate its future discussion with Syslinux developers. From hpa@zytor.com Thu Apr 1 08:32:52 2010 Date: Wed, 31 Mar 2010 14:53:51 -0700 From: H. Peter Anvin To: For discussion of Syslinux and tftp-hpa Cc: Thomas Schmitt Subject: Re: [syslinux] port syslinux isohybrid perl script to C [...] [me:] > Currently i lack of blob and prescriptions. The blobs are available in the Syslinux build tree under the names: mbr/isohdp[fp]x*.bin The default probably should be mbr/isohdppx.bin, but it's ultimately up to the user. User definable parameters: -> MBR ID (default random 32-bit number, or preserved from previous instance) -> Sector count (default 32, range 1-63) -> Head count (default 64, range 1-256) -> Partition offset (default 0, range 0-64) -> Partition number (default 1, range 1-4) -> Filesystem type (default 0x17, range 1-255) Note: the filesystem type is largely arbitrary, in theory it can be any value other than 0x00, 0x05, 0x0f, 0x85, 0xee, or 0xef. 0x17 ("Windows IFS Hidden") seems safeish, some people believe 0x83 (Linux) is better. Here is the prescriptions for how to install it: All numbers are littleendian. "word" means 16 bits, "dword" means 32 bits, "qword" means 64 bits. Common subroutine LBA_to_CHS(): s = (lba % sector_count) + 1 t = (lba / sector_count) h = (t % head_count) c = (t / head_count) if (c >= 1024): c = 1023 h = head_count s = sector_count s = s | ((c & 0x300) >> 2) c = c & 0xff write byte h write byte s write byte c Main: Pad image_size to a multiple of sector_count*head_count Use the input file unmodified for bytes 0..431 write qword boot_lba # Offset 432 write dword mbr_id # Offset 440 write word 0 # Offset 444 # Offset 446 For each partition entry 1..4: if this_partition != partition_number: write 16 zero bytes else: write byte 0x80 write LBA_to_CHS(partition_offset) write byte filesystem_type write LBA_to_CHS(image_size-1) write dword partition_offset write dword image_size # Offset 510 write word 0xaa55 Use the input file unmodified for bytes 512..32767 (pad with zero as necessary) [...] -hpa */ /* The new stuff about GPT and APM which was learned from Matthew Garret and isohybrid.c is described in doc/boot_sectord.txt chapter "SYSLINUX isohybrid for MBR, UEFI and x86-Mac" */ static int lba512chs_to_buf(char **wpt, off_t lba, int head_count, int sector_count) { int s, t, h, c; s = (lba % sector_count) + 1; t = (lba / sector_count); h = (t % head_count); c = (t / head_count); if (c >= 1024) { c = 1023; h = head_count; /* >>> not -1 ? Limits head_count to 255 */ s = sector_count; } s = s | ((c & 0x300) >> 2); c = c & 0xff; (*((unsigned char **) wpt))[0] = h; (*((unsigned char **) wpt))[1] = s; (*((unsigned char **) wpt))[2] = c; (*wpt)+= 3; return(1); } /* Find out whether GPT and APM are desired flag bit0 = register APM and GPT requests in Ecma119Image bit1 = do not asses and register APM bit2 = do not register overall GPT partition */ int assess_isohybrid_gpt_apm(Ecma119Image *t, int *gpt_count, int gpt_idx[128], int *apm_count, int flag) { int i, ilx_opts, j, ret, num_img; uint32_t block_count; uint64_t start_block; uint8_t gpt_name[72]; static uint8_t zero_uuid[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uint8_t basic_data_uuid[16] = { 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 }; static uint8_t hfs_uuid[16] = { 0x00, 0x53, 0x46, 0x48, 0x00, 0x00, 0xaa, 0x11, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac }; uint8_t *uuid, *type_guid; uint64_t gpt_flags = (((uint64_t) 1) << 60) | 1; *gpt_count = 0; *apm_count = 0; if (t->catalog != NULL) num_img = t->catalog->num_bootimages; else num_img = 0; for (i = 0; i < num_img; i++) { ilx_opts = t->catalog->bootimages[i]->isolinux_options; if ((((ilx_opts >> 2) & 63) == 1 || ((ilx_opts >> 2) & 63) == 2) && !(t->boot_appended_idx[i] >= 0 && t->opts->appended_as_gpt)) { if (*gpt_count < 128) gpt_idx[*gpt_count] = i; (*gpt_count)++; if ((flag & 1) && (t->bootsrc[i] != NULL || t->boot_appended_idx[i] >= 0)) { /* Register GPT entry */ memset(gpt_name, 0, 72); sprintf((char *) gpt_name, "ISOHybrid%d", *gpt_count); iso_ascii_utf_16le(gpt_name); if (((ilx_opts >> 2) & 63) == 2) uuid = hfs_uuid; else uuid = basic_data_uuid; if (t->boot_appended_idx[i] >= 0) { block_count = t->appended_part_size[ t->boot_appended_idx[i]]; start_block = ((uint64_t) t->appended_part_start[ t->boot_appended_idx[i]]) * 4; } else { block_count = 0; for (j = 0; j < t->bootsrc[i]->nsections; j++) block_count += t->bootsrc[i]->sections[j].size / 2048; start_block = ((uint64_t) t->bootsrc[i]->sections[0].block) * 4; } ret = iso_quick_gpt_entry( t->gpt_req, &(t->gpt_req_count), start_block, ((uint64_t) block_count) * 4, uuid, zero_uuid, gpt_flags, (uint8_t *) gpt_name); if (ret < 0) return ret; } } if ((ilx_opts & 256) && !(flag & 2)) { (*apm_count)++; if ((flag & 1) && (t->bootsrc[i] != NULL || t->boot_appended_idx[i] >= 0)) { /* Register APM entry */ if (t->boot_appended_idx[i] >= 0) { block_count = t->appended_part_size[ t->boot_appended_idx[i]]; start_block = t->appended_part_start[ t->boot_appended_idx[i]]; } else { block_count = 0; for (j = 0; j < t->bootsrc[i]->nsections; j++) block_count += t->bootsrc[i]->sections[j].size / 2048; start_block = t->bootsrc[i]->sections[0].block; } ret = iso_quick_apm_entry(t->apm_req, &(t->apm_req_count), (uint32_t) start_block, block_count, "EFI", "Apple_HFS"); if (ret < 0) return ret; /* Prevent gap filling */ t->apm_req_flags |= 2; t->opts->apm_block_size = 2048; } } } if (*gpt_count > 0 && !(flag & 4)) { (*gpt_count)++; if (*gpt_count < 128) gpt_idx[*gpt_count] = -1; } if ((flag & 1) && *gpt_count > 0 && !(flag & 4)) { /* Register overall GPT partition */ memset(gpt_name, 0, 72); sprintf((char *) gpt_name, "ISOHybrid"); iso_ascii_utf_16le(gpt_name); if (t->opts->iso_gpt_flag & 1) type_guid = t->opts->iso_gpt_type_guid; else type_guid = basic_data_uuid; if (t->system_area_options & (1 << 16)) gpt_flags|= 4; /* Legacy BIOS bootable */ if (t->opts->system_area_options & (1 << 17)) gpt_flags &= ~(((uint64_t) 1) << 60); /* Not read-only */ /* Let it be open ended. iso_write_gpt() will truncate it as needed. */ block_count = 0xffffffff; ret = iso_quick_gpt_entry(t->gpt_req, &(t->gpt_req_count), (uint64_t) t->opts->partition_offset * 4, ((uint64_t) block_count) * 4, type_guid, zero_uuid, gpt_flags, (uint8_t *) gpt_name); if (ret < 0) return ret; /* Remove ban on GPT overlapping */ t->gpt_req_flags |= 1; } return ISO_SUCCESS; } /* Insert APM head into MBR */ static int insert_apm_head(uint8_t *buf, int apm_count) { int i; static uint8_t apm_mbr_start[32] = { 0x33, 0xed, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }; static uint8_t apm_head[32] = { 0x45, 0x52, 0x08, 0x00, 0x00, 0x00, 0x90, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; if (apm_count) { for (i = 0; i < 32; i++) if(buf[i] != apm_mbr_start[i]) break; if (i < 32) { /* Maybe it is already patched by apm_head ? */ for (i = 0; i < 32; i++) if(buf[i] != apm_head[i]) break; } if (i < 32) { iso_msgs_submit(0, "MBR template file seems not prepared for Apple Partition Map.", 0, "FAILURE", 0); return ISO_ISOLINUX_CANT_PATCH; } for (i = 0; i < 32; i++) buf[i] = apm_head[i]; } return ISO_SUCCESS; } /* Describe GPT boot images as MBR partitions */ static int gpt_images_as_mbr_partitions(Ecma119Image *t, char *wpt, int gpt_idx[128], int *gpt_cursor) { int ilx_opts, skip = 0; off_t hd_blocks; static uint8_t dummy_chs[3] = { 0xfe, 0xff, 0xff, }; if (gpt_idx[*gpt_cursor] < 0) skip = 1; else if (t->bootsrc[gpt_idx[*gpt_cursor]] == NULL) skip = 1; if (skip) { (*gpt_cursor)++; return 2; } wpt[0] = 0; memcpy(wpt + 1, dummy_chs, 3); ilx_opts = t->catalog->bootimages[gpt_idx[*gpt_cursor]]->isolinux_options; if (((ilx_opts >> 2) & 63) == 2) wpt[4] = 0x00; /* HFS gets marked as "Empty" */ else ((unsigned char *) wpt)[4] = 0xef; /* "EFI (FAT-12/16)" */ memcpy(wpt + 5, dummy_chs, 3); /* Start LBA (in 512 blocks) */ wpt += 8; lsb_to_buf(&wpt, t->bootsrc[gpt_idx[*gpt_cursor]]->sections[0].block * 4, 32, 0); /* Number of blocks */ hd_blocks = t->bootsrc[gpt_idx[*gpt_cursor]]->sections[0].size; hd_blocks = hd_blocks / 512 + !!(hd_blocks % 512); lsb_to_buf(&wpt, (int) hd_blocks, 32, 0); (*gpt_cursor)++; return ISO_SUCCESS; } /* For generating a weak random number */ static uint32_t iso_make_mbr_id(Ecma119Image *t, int flag) { uint32_t id; struct timeval tv; if(t->opts->vol_uuid[0]) { id = iso_crc32_gpt((unsigned char *) t->opts->vol_uuid, 16, 0); } else if(t->opts->vol_modification_time > 0) { id = iso_crc32_gpt((unsigned char *) &(t->opts->vol_modification_time), sizeof(time_t), 0); } else { gettimeofday(&tv, NULL); id = 0xffffffff & (tv.tv_sec ^ (tv.tv_usec * 2000)); } return id; } /* * @param flag bit0= make own random MBR Id from current time * or from overridden modification time * bit1= create protective MBR as of UEFI/GPT specs * bit2= write only partition table * do not insert APM mockup head * do not treat bytes before code as isohybrid MBR * do not create MBR id * bit3= replace fs_type 0x00 by 0x17 if appropriate */ int make_isolinux_mbr(uint32_t *img_blocks, Ecma119Image *t, int part_offset, int part_number, int fs_type, uint8_t *buf, int flag) { uint32_t id, part, nominal_part_size, mbr_part_start; off_t hd_img_blocks, hd_boot_lba; char *wpt, *fs_type_wpt = NULL; uint32_t boot_lba; int head_count, sector_count, ret, part_is_in_img = 0; int gpt_count = 0, gpt_idx[128], apm_count = 0, gpt_cursor, i; if (t->bootsrc[0] == NULL) return iso_msg_submit(t->image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Cannot refer by isohybrid MBR to data outside of ISO 9660 filesystem."); for (i = 0; i < 128; i++) gpt_idx[i] = -1; if (flag & 2) { part_number = 1; part_offset = 1; } hd_img_blocks = ((off_t) *img_blocks) * (off_t) 4 - t->post_iso_part_pad / 512; boot_lba = t->bootsrc[0]->sections[0].block; head_count = t->partition_heads_per_cyl; sector_count = t->partition_secs_per_head; ret = assess_isohybrid_gpt_apm(t, &gpt_count, gpt_idx, &apm_count, 0); if (ret < 0) return ret; if(flag & 4) { wpt= (char *) buf + 446; } else { /* The rest of APM has already been written by iso_write_apm(). But the isohybrid APM head differs from the hfsplus_writer APM head. */ ret = insert_apm_head(buf, apm_count); if (ret < 0) return ret; /* Padding of image_size to a multiple of sector_count*head_count happens already at compute time and is implemented by an appropriate increase of Ecma119Image->tail_blocks. */ wpt = (char *) buf + 432; /* write qword boot_lba # Offset 432 */ hd_boot_lba = ((off_t) boot_lba) * (off_t) 4; lsb_to_buf(&wpt, hd_boot_lba & 0xffffffff, 32, 0); lsb_to_buf(&wpt, hd_boot_lba >> 32, 32, 0); /* write dword mbr_id # Offset 440 (here some 32-bit random value with no crypto strength) */ if (flag & 1) { id = iso_make_mbr_id(t, 0); lsb_to_buf(&wpt, id, 32, 0); } else { wpt+= 4; } /* write word 0 # Offset 444 */ lsb_to_buf(&wpt, 0, 16, 0); } /* # Offset 446 */ gpt_cursor= 0; for (part = 1 ; part <= 4; part++) { if ((int) part != part_number) { /* if this_partition != partition_number: write 16 zero bytes (this is now overridden by the eventual desire to announce EFI and HFS boot images.) */ memset(wpt, 0, 16); if (gpt_cursor < gpt_count) { ret = gpt_images_as_mbr_partitions(t, wpt, gpt_idx, &gpt_cursor); if (ret < 0) return ret; } /* Will this hit the part_number partition ? */ mbr_part_start = iso_read_lsb((uint8_t *) (wpt + 8), 4); if (mbr_part_start > 0 && mbr_part_start < hd_img_blocks + part_offset) part_is_in_img = 1; wpt+= 16; continue; } /* write byte 0x80 if bootable write LBA_to_CHS(partition_offset) write byte filesystem_type write LBA_to_CHS(image_size-1) write dword partition_offset write dword image_size */ if (flag & 2) lsb_to_buf(&wpt, 0x00, 8, 0); else lsb_to_buf(&wpt, 0x80, 8, 0); lba512chs_to_buf(&wpt, part_offset, head_count, sector_count); fs_type_wpt = wpt; lsb_to_buf(&wpt, fs_type, 8, 0); lba512chs_to_buf(&wpt, hd_img_blocks - 1, head_count, sector_count); lsb_to_buf(&wpt, part_offset, 32, 0); if (hd_img_blocks - (off_t) part_offset > (off_t) 0xffffffff) nominal_part_size = 0xffffffff; else nominal_part_size = hd_img_blocks - (off_t) part_offset; lsb_to_buf(&wpt, nominal_part_size, 32, 0); } /* write word 0xaa55 # Offset 510 */ lsb_to_buf(&wpt, 0xaa55, 16, 0); /* Check whether automatically determined fs_type 0x00 can become 0x17 */ if ((flag & 8) && fs_type_wpt != NULL && fs_type == 0x00 && t->opts->iso_mbr_part_type != fs_type && !part_is_in_img) { if (t->opts->iso_mbr_part_type >= 0 && t->opts->iso_mbr_part_type <= 255) { lsb_to_buf(&fs_type_wpt, t->opts->iso_mbr_part_type, 8, 0); } else { lsb_to_buf(&fs_type_wpt, 0x17, 8, 0); } } return(1); } /* * Copyright (c) 2009 - 2013 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #ifdef HAVE_STDINT_H #include #else #ifdef HAVE_INTTYPES_H #include #endif #endif #include #include #include #include "writer.h" #include "messages.h" #include "ecma119.h" #include "image.h" #include "util.h" #include "md5.h" /* This code is derived from RFC 1321 and implements computation of the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" */ #define Libisofs_md5_S11 7 #define Libisofs_md5_S12 12 #define Libisofs_md5_S13 17 #define Libisofs_md5_S14 22 #define Libisofs_md5_S21 5 #define Libisofs_md5_S22 9 #define Libisofs_md5_S23 14 #define Libisofs_md5_S24 20 #define Libisofs_md5_S31 4 #define Libisofs_md5_S32 11 #define Libisofs_md5_S33 16 #define Libisofs_md5_S34 23 #define Libisofs_md5_S41 6 #define Libisofs_md5_S42 10 #define Libisofs_md5_S43 15 #define Libisofs_md5_S44 21 /* F, G, H and I are basic MD5 functions. */ #define Libisofs_md5_F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define Libisofs_md5_G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define Libisofs_md5_H(x, y, z) ((x) ^ (y) ^ (z)) #define Libisofs_md5_I(x, y, z) ((y) ^ ((x) | (~z))) /* ROTATE_LEFT rotates x left n bits. */ #define Libisofs_md5_ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. Rotation is separate from addition to prevent recomputation. */ #define Libisofs_md5_FF(a, b, c, d, x, s, ac) { \ (a) += Libisofs_md5_F ((b), (c), (d)) + (x) + (uint32_t)(ac); \ (a) = Libisofs_md5_ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define Libisofs_md5_GG(a, b, c, d, x, s, ac) { \ (a) += Libisofs_md5_G ((b), (c), (d)) + (x) + (uint32_t)(ac); \ (a) = Libisofs_md5_ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define Libisofs_md5_HH(a, b, c, d, x, s, ac) { \ (a) += Libisofs_md5_H ((b), (c), (d)) + (x) + (uint32_t)(ac); \ (a) = Libisofs_md5_ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define Libisofs_md5_II(a, b, c, d, x, s, ac) { \ (a) += Libisofs_md5_I ((b), (c), (d)) + (x) + (uint32_t)(ac); \ (a) = Libisofs_md5_ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } /* MD5 context. */ struct _libisofs_md5_ctx { uint32_t state[4]; /* state (ABCD) */ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ }; typedef struct _libisofs_md5_ctx libisofs_md5_ctx; /* MD5 basic transformation. Transforms state based on block. */ static int md5__transform (uint32_t state[4], unsigned char block[64]) { uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16]; unsigned int i, j; for (i = 0, j = 0; j < 64; i++, j += 4) x[i] = ((uint32_t)block[j]) | (((uint32_t)block[j+1]) << 8) | (((uint32_t)block[j+2]) << 16) | (((uint32_t)block[j+3]) << 24); /* Round 1 */ Libisofs_md5_FF (a, b, c, d, x[ 0], Libisofs_md5_S11, 0xd76aa478); /* 1 */ Libisofs_md5_FF (d, a, b, c, x[ 1], Libisofs_md5_S12, 0xe8c7b756); /* 2 */ Libisofs_md5_FF (c, d, a, b, x[ 2], Libisofs_md5_S13, 0x242070db); /* 3 */ Libisofs_md5_FF (b, c, d, a, x[ 3], Libisofs_md5_S14, 0xc1bdceee); /* 4 */ Libisofs_md5_FF (a, b, c, d, x[ 4], Libisofs_md5_S11, 0xf57c0faf); /* 5 */ Libisofs_md5_FF (d, a, b, c, x[ 5], Libisofs_md5_S12, 0x4787c62a); /* 6 */ Libisofs_md5_FF (c, d, a, b, x[ 6], Libisofs_md5_S13, 0xa8304613); /* 7 */ Libisofs_md5_FF (b, c, d, a, x[ 7], Libisofs_md5_S14, 0xfd469501); /* 8 */ Libisofs_md5_FF (a, b, c, d, x[ 8], Libisofs_md5_S11, 0x698098d8); /* 9 */ Libisofs_md5_FF (d, a, b, c, x[ 9], Libisofs_md5_S12, 0x8b44f7af); /* 10 */ Libisofs_md5_FF (c, d, a, b, x[10], Libisofs_md5_S13, 0xffff5bb1); /* 11 */ Libisofs_md5_FF (b, c, d, a, x[11], Libisofs_md5_S14, 0x895cd7be); /* 12 */ Libisofs_md5_FF (a, b, c, d, x[12], Libisofs_md5_S11, 0x6b901122); /* 13 */ Libisofs_md5_FF (d, a, b, c, x[13], Libisofs_md5_S12, 0xfd987193); /* 14 */ Libisofs_md5_FF (c, d, a, b, x[14], Libisofs_md5_S13, 0xa679438e); /* 15 */ Libisofs_md5_FF (b, c, d, a, x[15], Libisofs_md5_S14, 0x49b40821); /* 16 */ /* Round 2 */ Libisofs_md5_GG (a, b, c, d, x[ 1], Libisofs_md5_S21, 0xf61e2562); /* 17 */ Libisofs_md5_GG (d, a, b, c, x[ 6], Libisofs_md5_S22, 0xc040b340); /* 18 */ Libisofs_md5_GG (c, d, a, b, x[11], Libisofs_md5_S23, 0x265e5a51); /* 19 */ Libisofs_md5_GG (b, c, d, a, x[ 0], Libisofs_md5_S24, 0xe9b6c7aa); /* 20 */ Libisofs_md5_GG (a, b, c, d, x[ 5], Libisofs_md5_S21, 0xd62f105d); /* 21 */ Libisofs_md5_GG (d, a, b, c, x[10], Libisofs_md5_S22, 0x2441453); /* 22 */ Libisofs_md5_GG (c, d, a, b, x[15], Libisofs_md5_S23, 0xd8a1e681); /* 23 */ Libisofs_md5_GG (b, c, d, a, x[ 4], Libisofs_md5_S24, 0xe7d3fbc8); /* 24 */ Libisofs_md5_GG (a, b, c, d, x[ 9], Libisofs_md5_S21, 0x21e1cde6); /* 25 */ Libisofs_md5_GG (d, a, b, c, x[14], Libisofs_md5_S22, 0xc33707d6); /* 26 */ Libisofs_md5_GG (c, d, a, b, x[ 3], Libisofs_md5_S23, 0xf4d50d87); /* 27 */ Libisofs_md5_GG (b, c, d, a, x[ 8], Libisofs_md5_S24, 0x455a14ed); /* 28 */ Libisofs_md5_GG (a, b, c, d, x[13], Libisofs_md5_S21, 0xa9e3e905); /* 29 */ Libisofs_md5_GG (d, a, b, c, x[ 2], Libisofs_md5_S22, 0xfcefa3f8); /* 30 */ Libisofs_md5_GG (c, d, a, b, x[ 7], Libisofs_md5_S23, 0x676f02d9); /* 31 */ Libisofs_md5_GG (b, c, d, a, x[12], Libisofs_md5_S24, 0x8d2a4c8a); /* 32 */ /* Round 3 */ Libisofs_md5_HH (a, b, c, d, x[ 5], Libisofs_md5_S31, 0xfffa3942); /* 33 */ Libisofs_md5_HH (d, a, b, c, x[ 8], Libisofs_md5_S32, 0x8771f681); /* 34 */ Libisofs_md5_HH (c, d, a, b, x[11], Libisofs_md5_S33, 0x6d9d6122); /* 35 */ Libisofs_md5_HH (b, c, d, a, x[14], Libisofs_md5_S34, 0xfde5380c); /* 36 */ Libisofs_md5_HH (a, b, c, d, x[ 1], Libisofs_md5_S31, 0xa4beea44); /* 37 */ Libisofs_md5_HH (d, a, b, c, x[ 4], Libisofs_md5_S32, 0x4bdecfa9); /* 38 */ Libisofs_md5_HH (c, d, a, b, x[ 7], Libisofs_md5_S33, 0xf6bb4b60); /* 39 */ Libisofs_md5_HH (b, c, d, a, x[10], Libisofs_md5_S34, 0xbebfbc70); /* 40 */ Libisofs_md5_HH (a, b, c, d, x[13], Libisofs_md5_S31, 0x289b7ec6); /* 41 */ Libisofs_md5_HH (d, a, b, c, x[ 0], Libisofs_md5_S32, 0xeaa127fa); /* 42 */ Libisofs_md5_HH (c, d, a, b, x[ 3], Libisofs_md5_S33, 0xd4ef3085); /* 43 */ Libisofs_md5_HH (b, c, d, a, x[ 6], Libisofs_md5_S34, 0x4881d05); /* 44 */ Libisofs_md5_HH (a, b, c, d, x[ 9], Libisofs_md5_S31, 0xd9d4d039); /* 45 */ Libisofs_md5_HH (d, a, b, c, x[12], Libisofs_md5_S32, 0xe6db99e5); /* 46 */ Libisofs_md5_HH (c, d, a, b, x[15], Libisofs_md5_S33, 0x1fa27cf8); /* 47 */ Libisofs_md5_HH (b, c, d, a, x[ 2], Libisofs_md5_S34, 0xc4ac5665); /* 48 */ /* Round 4 */ Libisofs_md5_II (a, b, c, d, x[ 0], Libisofs_md5_S41, 0xf4292244); /* 49 */ Libisofs_md5_II (d, a, b, c, x[ 7], Libisofs_md5_S42, 0x432aff97); /* 50 */ Libisofs_md5_II (c, d, a, b, x[14], Libisofs_md5_S43, 0xab9423a7); /* 51 */ Libisofs_md5_II (b, c, d, a, x[ 5], Libisofs_md5_S44, 0xfc93a039); /* 52 */ Libisofs_md5_II (a, b, c, d, x[12], Libisofs_md5_S41, 0x655b59c3); /* 53 */ Libisofs_md5_II (d, a, b, c, x[ 3], Libisofs_md5_S42, 0x8f0ccc92); /* 54 */ Libisofs_md5_II (c, d, a, b, x[10], Libisofs_md5_S43, 0xffeff47d); /* 55 */ Libisofs_md5_II (b, c, d, a, x[ 1], Libisofs_md5_S44, 0x85845dd1); /* 56 */ Libisofs_md5_II (a, b, c, d, x[ 8], Libisofs_md5_S41, 0x6fa87e4f); /* 57 */ Libisofs_md5_II (d, a, b, c, x[15], Libisofs_md5_S42, 0xfe2ce6e0); /* 58 */ Libisofs_md5_II (c, d, a, b, x[ 6], Libisofs_md5_S43, 0xa3014314); /* 59 */ Libisofs_md5_II (b, c, d, a, x[13], Libisofs_md5_S44, 0x4e0811a1); /* 60 */ Libisofs_md5_II (a, b, c, d, x[ 4], Libisofs_md5_S41, 0xf7537e82); /* 61 */ Libisofs_md5_II (d, a, b, c, x[11], Libisofs_md5_S42, 0xbd3af235); /* 62 */ Libisofs_md5_II (c, d, a, b, x[ 2], Libisofs_md5_S43, 0x2ad7d2bb); /* 63 */ Libisofs_md5_II (b, c, d, a, x[ 9], Libisofs_md5_S44, 0xeb86d391); /* 64 */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; /* Zeroize sensitive information. */ memset ((char *) x, 0, sizeof (x)); return(1); } static int md5__encode(unsigned char *output, uint32_t *input, unsigned int len) { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) { output[j] = (unsigned char)(input[i] & 0xff); output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); } return(1); } static int md5_init(libisofs_md5_ctx *ctx, int flag) { ctx->count[0] = ctx->count[1] = 0; /* Load magic initialization constants. */ ctx->state[0] = 0x67452301; ctx->state[1] = 0xefcdab89; ctx->state[2] = 0x98badcfe; ctx->state[3] = 0x10325476; return(1); } /* MD5 block update operation. Continues an MD5 message-digest operation, processing another message block, and updating the context. */ static int md5_update(libisofs_md5_ctx *ctx, unsigned char *data, int datalen, int flag) { int i, index, partlen; /* Compute number of bytes mod 64 */ index = ((ctx->count[0] >> 3) & 0x3F); /* Update number of bits */ if ((ctx->count[0] += ((uint32_t) datalen << 3)) < ((uint32_t) datalen << 3)) ctx->count[1]++; ctx->count[1] += ((uint32_t) datalen >> 29); partlen = 64 - index; /* Transform as many times as possible. */ if (datalen >= partlen) { memcpy((char *) &ctx->buffer[index], (char *) data, partlen); md5__transform(ctx->state, ctx->buffer); for (i = partlen; i + 63 < datalen; i += 64) md5__transform(ctx->state, &data[i]); index = 0; } else i = 0; /* Buffer remaining data */ memcpy((char *) &ctx->buffer[index], (char *) &data[i],datalen-i); return(1); } static int md5_final(libisofs_md5_ctx *ctx, char result[16], int flag) { unsigned char bits[8], *respt; unsigned int index, padlen; static unsigned char PADDING[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* Save number of bits */ md5__encode(bits, ctx->count, 8); /* Pad out to 56 mod 64. */ index = (unsigned int)((ctx->count[0] >> 3) & 0x3f); padlen = (index < 56) ? (56 - index) : (120 - index); md5_update(ctx, PADDING, padlen,0); /* Append length (before padding) */ md5_update(ctx, bits, 8,0); /* Store state in result */ respt= (unsigned char *) result; md5__encode(respt, ctx->state, 16); /* Zeroize sensitive information. */ memset ((char *) ctx, 0, sizeof (*ctx)); return(1); } /** Compute a MD5 checksum from one or more calls of this function. The first call has to be made with flag bit0 == 1. It may already submit processing payload in data and datalen. The last call has to be made with bit15 set. Normally bit1 will be set too in order to receive the checksum before it gets disposed. bit1 may only be set in the last call or together with bit2. The combination of bit1 and bit2 may be used to get an intermediate result without hampering an ongoing checksum computation. @param ctx the checksum context which stores the state between calls. It gets created with flag bit0 and disposed with bit15. With flag bit0, *ctx has to be NULL or point to freeable memory. @param data the bytes to be checksummed @param datalen the number of bytes in data @param result returns the 16 bytes of checksum if called with bit1 set @param flag bit0= allocate and init *ctx bit1= transfer ctx to result bit2= with bit 0 : clone new *ctx from data bit15= free *ctx */ static int libisofs_md5(void **ctx_in, char *data, int datalen, char result[16], int flag) /* *ctx has to be NULL or point to freeable memory */ /* bit0= allocate and init *ctx bit1= transfer ctx to result bit2= with bit 0 : clone new *ctx from data bit15= free *ctx */ { unsigned char *datapt; libisofs_md5_ctx **ctx; ctx= (libisofs_md5_ctx **) ctx_in; if(flag&1) { if(*ctx!=NULL) free((char *) *ctx); *ctx= calloc(1, sizeof(libisofs_md5_ctx)); if(*ctx==NULL) return(-1); md5_init(*ctx,0); if(flag&4) memcpy((char *) *ctx,data,sizeof(libisofs_md5_ctx)); } if(*ctx==NULL) return(0); if(datalen>0) { datapt= (unsigned char *) data; md5_update(*ctx, datapt, datalen, 0); } if(flag&2) md5_final(*ctx, result, 0); if(flag&(1<<15)) { free((char *) *ctx); *ctx= NULL; } return(1); } /* ----------------------------------------------------------------------- */ /* Public MD5 computing facility */ /* API */ int iso_md5_start(void **md5_context) { int ret; ret = libisofs_md5(md5_context, NULL, 0, NULL, 1); if (ret <= 0) return ISO_OUT_OF_MEM; return 1; } /* API */ int iso_md5_compute(void *md5_context, char *data, int datalen) { int ret; ret = libisofs_md5(&md5_context, data, datalen, NULL, 0); if (ret <= 0) return ISO_NULL_POINTER; return 1; } /* API */ int iso_md5_clone(void *old_md5_context, void **new_md5_context) { int ret; ret = libisofs_md5(new_md5_context, old_md5_context, 0, NULL, 1 | 4); if (ret < 0) return ISO_OUT_OF_MEM; if (ret == 0) return ISO_NULL_POINTER; return 1; } /* API */ int iso_md5_end(void **md5_context, char result[16]) { int ret; ret = libisofs_md5(md5_context, NULL, 0, result, 2 | (1 << 15)); if (ret <= 0) return ISO_NULL_POINTER; return 1; } /* API */ int iso_md5_match(char first_md5[16], char second_md5[16]) { int i; for (i= 0; i < 16; i++) if (first_md5[i] != second_md5[i]) return 0; return 1; } /* ----------------------------------------------------------------------- */ /* Function to identify and manage md5sum indice of the old image. * data is supposed to be a 4 byte integer, bit 31 shall be 0, * value 0 of this integer means that it is not a valid index. */ int checksum_cx_xinfo_func(void *data, int flag) { /* data is an int disguised as pointer. It does not point to memory. */ return 1; } /* The iso_node_xinfo_cloner function which gets associated to * checksum_cx_xinfo_func by iso_init() resp. iso_init_with_flag() via * iso_node_xinfo_make_clonable() */ int checksum_cx_xinfo_cloner(void *old_data, void **new_data, int flag) { *new_data = NULL; if (flag) return ISO_XINFO_NO_CLONE; if (old_data == NULL) return 0; /* data is an int disguised as pointer. It does not point to memory. */ *new_data = old_data; return 0; } /* Function to identify and manage md5 sums of unspecified providence stored * directly in this xinfo. */ int checksum_md5_xinfo_func(void *data, int flag) { if (data == NULL) return 1; free(data); return 1; } /* The iso_node_xinfo_cloner function which gets associated to * checksum_md5_xinfo_func by iso_init() resp. iso_init_with_flag() via * iso_node_xinfo_make_clonable() */ int checksum_md5_xinfo_cloner(void *old_data, void **new_data, int flag) { *new_data = NULL; if (flag) return ISO_XINFO_NO_CLONE; if (old_data == NULL) return 0; *new_data = calloc(1, 16); if (*new_data == NULL) return ISO_OUT_OF_MEM; memcpy(*new_data, old_data, 16); return 16; } /* ----------------------------------------------------------------------- */ /* MD5 checksum image writer */ /* @flag bit0= recursion bit1= session will be appended to an existing image */ static int checksum_copy_old_nodes(Ecma119Image *target, IsoNode *node, int flag) { IsoNode *pos; IsoFile *file; IsoImage *img; int ret, i; size_t value_length; unsigned int idx = 0, old_idx = 0; char *value = NULL, *md5_pt = NULL; void *xipt; img = target->image; if (target->checksum_buffer == NULL) return 0; if (node->type == LIBISO_FILE) { file = (IsoFile *) node; if (file->from_old_session && target->opts->appendable) { /* Look for checksums at various places */ /* Try checksum directly stored with node */ if (md5_pt == NULL) { ret = iso_node_get_xinfo(node, checksum_md5_xinfo_func, &xipt); if (ret < 0) return ret; if (ret == 1) md5_pt = (char *) xipt; } /* Try checksum index to image checksum buffer */ if (md5_pt == NULL && img->checksum_array != NULL) { ret = iso_node_get_xinfo(node, checksum_cx_xinfo_func, &xipt); if (ret <= 0) return ret; /* xipt is an int disguised as void pointer */ old_idx = 0; for (i = 0; i < 4; i++) old_idx = (old_idx << 8) | ((unsigned char *) &xipt)[i]; if (old_idx == 0 || old_idx > img->checksum_idx_count - 1) return 0; md5_pt = img->checksum_array + 16 * old_idx; } if (md5_pt == NULL) return 0; if (!target->opts->will_cancel) { ret = iso_node_lookup_attr(node, "isofs.cx", &value_length, &value, 0); if (ret == 1 && value_length == 4) { for (i = 0; i < 4; i++) idx = (idx << 8) | ((unsigned char *) value)[i]; if (idx > 0 && idx <= target->checksum_idx_counter) { memcpy(target->checksum_buffer + 16 * idx, md5_pt, 16); } } if (value != NULL) free(value); /* >>> ts B30114 : It is unclear why these are removed here. At least with the opts->will_cancel runs, this is not appropriate. */ iso_node_remove_xinfo(node, checksum_md5_xinfo_func); } } } else if (node->type == LIBISO_DIR) { for (pos = ((IsoDir *) node)->children; pos != NULL; pos = pos->next) { ret = checksum_copy_old_nodes(target, pos, 1); if (ret < 0) return ret; } } return ISO_SUCCESS; } static int checksum_writer_compute_data_blocks(IsoImageWriter *writer) { size_t size; Ecma119Image *t; int ret; if (writer == NULL) { return ISO_ASSERT_FAILURE; } t = writer->target; t->checksum_array_pos = t->curblock; /* (t->curblock already contains t->opts->ms_block) */ t->checksum_range_start = t->opts->ms_block; size = (t->checksum_idx_counter + 2) / 128; if (size * 128 < t->checksum_idx_counter + 2) size++; t->curblock += size; t->checksum_range_size = t->checksum_array_pos + size - t->checksum_range_start; /* Extra block for stream detectable checksum tag */ t->checksum_tag_pos = t->curblock; t->curblock++; /* Allocate array of MD5 sums */ t->checksum_buffer = calloc(size, 2048); if (t->checksum_buffer == NULL) return ISO_OUT_OF_MEM; /* Copy MD5 from nodes of old image into writer->data */ ret = checksum_copy_old_nodes(t, (IsoNode *) t->image->root, 0); if (ret < 0) return ret; /* Record lba,count,size,cecksum_type in "isofs.ca" of root node */ ret = iso_root_set_isofsca((IsoNode *) t->image->root, t->checksum_range_start, t->checksum_array_pos, t->checksum_idx_counter + 2, 16, "MD5", 0); if (ret < 0) return ret; return ISO_SUCCESS; } static int checksum_writer_write_vol_desc(IsoImageWriter *writer) { /* The superblock checksum tag has to be written after the Volume Descriptor Set Terminator and thus may not be written by this function. (It would have been neat, though). */ return ISO_SUCCESS; } static int checksum_writer_write_data(IsoImageWriter *writer) { int wres, res; size_t i, size; Ecma119Image *t; void *ctx = NULL; char md5[16]; if (writer == NULL) { return ISO_ASSERT_FAILURE; } t = writer->target; iso_msg_debug(t->image->id, "Writing Checksums..."); /* Write image checksum to index 0 */ if (t->checksum_ctx != NULL) { res = iso_md5_clone(t->checksum_ctx, &ctx); if (res > 0) { res = iso_md5_end(&ctx, t->image_md5); if (res > 0) memcpy(t->checksum_buffer + 0 * 16, t->image_md5, 16); } } size = (t->checksum_idx_counter + 2) / 128; if (size * 128 < t->checksum_idx_counter + 2) size++; /* Write checksum of checksum array as index t->checksum_idx_counter + 1 */ res = iso_md5_start(&ctx); if (res > 0) { for (i = 0; i < t->checksum_idx_counter + 1; i++) iso_md5_compute(ctx, t->checksum_buffer + ((size_t) i) * (size_t) 16, 16); res = iso_md5_end(&ctx, md5); if (res > 0) memcpy(t->checksum_buffer + (t->checksum_idx_counter + 1) * 16, md5, 16); } for (i = 0; i < size; i++) { wres = iso_write(t, t->checksum_buffer + ((size_t) 2048) * i, 2048); if (wres < 0) { res = wres; goto ex; } } if (t->checksum_ctx == NULL) { res = ISO_SUCCESS; goto ex; } /* Write stream detectable checksum tag to extra block */; res = iso_md5_write_tag(t, 1); if (res < 0) goto ex; res = ISO_SUCCESS; ex:; if (ctx != NULL) iso_md5_end(&ctx, md5); return(res); } static int checksum_writer_free_data(IsoImageWriter *writer) { /* nothing was allocated at writer->data */ return ISO_SUCCESS; } int checksum_writer_create(Ecma119Image *target) { IsoImageWriter *writer; writer = malloc(sizeof(IsoImageWriter)); if (writer == NULL) { return ISO_OUT_OF_MEM; } writer->compute_data_blocks = checksum_writer_compute_data_blocks; writer->write_vol_desc = checksum_writer_write_vol_desc; writer->write_data = checksum_writer_write_data; writer->free_data = checksum_writer_free_data; writer->data = NULL; writer->target = target; /* add this writer to image */ target->writers[target->nwriters++] = writer; /* Account for superblock checksum tag */ if (target->opts->md5_session_checksum) { target->checksum_sb_tag_pos = target->curblock; target->curblock++; } return ISO_SUCCESS; } static int iso_md5_write_scdbackup_tag(Ecma119Image *t, char *tag_block, int flag) { void *ctx = NULL; off_t pos = 0, line_start; int record_len, block_len, ret, i; char postext[40], md5[16], *record = NULL; LIBISO_ALLOC_MEM(record, char, 160); line_start = strlen(tag_block); iso_md5_compute(t->checksum_ctx, tag_block, line_start); ret = iso_md5_clone(t->checksum_ctx, &ctx); if (ret < 0) goto ex; ret = iso_md5_end(&ctx, md5); pos = (off_t) t->checksum_tag_pos * (off_t) 2048 + line_start; if(pos >= 1000000000) sprintf(postext, "%u%9.9u", (unsigned int) (pos / 1000000000), (unsigned int) (pos % 1000000000)); else sprintf(postext, "%u", (unsigned int) pos); sprintf(record, "%s %s ", t->opts->scdbackup_tag_parm, postext); record_len = strlen(record); for (i = 0; i < 16; i++) sprintf(record + record_len + 2 * i, "%2.2x", ((unsigned char *) md5)[i]); record_len += 32; ret = iso_md5_start(&ctx); if (ret < 0) goto ex; iso_md5_compute(ctx, record, record_len); iso_md5_end(&ctx, md5); sprintf(tag_block + line_start, "scdbackup_checksum_tag_v0.1 %s %d %s ", postext, record_len, record); block_len = strlen(tag_block); for (i = 0; i < 16; i++) sprintf(tag_block + block_len + 2 * i, "%2.2x", ((unsigned char *) md5)[i]); block_len+= 32; tag_block[block_len++]= '\n'; if (t->opts->scdbackup_tag_written != NULL) strncpy(t->opts->scdbackup_tag_written, tag_block + line_start, block_len - line_start); ret = ISO_SUCCESS; ex:; if (ctx != NULL) iso_md5_end(&ctx, md5); LIBISO_FREE_MEM(record); return ret; } /* Write stream detectable checksum tag to extra block. * @flag bit0-7= tag type * 1= session tag (End checksumming.) * 2= superblock tag (System Area and Volume Descriptors) * 3= tree tag (ECMA-119 and Rock Ridge tree) * 4= relocated superblock tag (at LBA 0 of overwritable media) * Write to target->opts_overwrite rather than to iso_write(). */ int iso_md5_write_tag(Ecma119Image *t, int flag) { int ret, mode, l, i, wres, tag_id_len; void *ctx = NULL; char md5[16], *tag_block = NULL, *tag_id; uint32_t size = 0, pos = 0, start; LIBISO_ALLOC_MEM(tag_block, char, 2048); start = t->checksum_range_start; mode = flag & 255; if (mode < 1 || mode > 4) {ret = ISO_WRONG_ARG_VALUE; goto ex;} ret = iso_md5_clone(t->checksum_ctx, &ctx); if (ret < 0) goto ex; ret = iso_md5_end(&ctx, md5); if (mode == 1) { size = t->checksum_range_size; pos = t->checksum_tag_pos; } else { if (mode == 2) { pos = t->checksum_sb_tag_pos; } else if (mode == 3) { pos = t->checksum_tree_tag_pos; } else if (mode == 4) { pos = t->checksum_rlsb_tag_pos; start = pos - (pos % 32); } size = pos - start; } if (ret < 0) goto ex; iso_util_tag_magic(mode, &tag_id, &tag_id_len, 0); sprintf(tag_block, "%s pos=%u range_start=%u range_size=%u", tag_id, pos, start, size); l = strlen(tag_block); if (mode == 2) { sprintf(tag_block + l, " next=%u", t->checksum_tree_tag_pos); } else if (mode == 3) { sprintf(tag_block + l, " next=%u", t->checksum_tag_pos); } else if (mode == 4) { sprintf(tag_block + l, " session_start=%u", t->opts->ms_block); } strcat(tag_block + l, " md5="); l = strlen(tag_block); for (i = 0; i < 16; i++) sprintf(tag_block + l + 2 * i, "%2.2x", ((unsigned char *) md5)[i]); l+= 32; ret = iso_md5_start(&ctx); if (ret > 0) { iso_md5_compute(ctx, tag_block, l); iso_md5_end(&ctx, md5); strcpy(tag_block + l, " self="); l += 6; for (i = 0; i < 16; i++) sprintf(tag_block + l + 2 * i, "%2.2x", ((unsigned char *) md5)[i]); } tag_block[l + 32] = '\n'; if (mode == 1 && t->opts->scdbackup_tag_parm[0]) { if (t->opts->ms_block > 0) { iso_msg_submit(t->image->id, ISO_SCDBACKUP_TAG_NOT_0, 0, NULL); } else { ret = iso_md5_write_scdbackup_tag(t, tag_block, 0); if (ret < 0) goto ex; } } if (mode == 4) { if (t->opts_overwrite != NULL) memcpy(t->opts_overwrite + pos * 2048, tag_block, 2048); } else { wres = iso_write(t, tag_block, 2048); if (wres < 0) { ret = wres; goto ex; } } ret = ISO_SUCCESS; ex:; if (ctx != NULL) iso_md5_end(&ctx, md5); LIBISO_FREE_MEM(tag_block); return ret; } /* * Copyright (c) 2009 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_MD5_H_ #define LIBISO_MD5_H_ /* The MD5 computation API is in libisofs.h : iso_md5_start() et.al. */ /** Create a writer object for checksums and add it to the writer list of the given Ecma119Image. */ int checksum_writer_create(Ecma119Image *target); /* Write stream detectable checksum tag to extra block. * All tag ranges start at the beginning of the System Area (i.e. t->ms_block) * and stem from the same MD5 computation context. Tag types 2 and 3 are * intermediate checksums. Type 2 announces the existence of type 3. * If both match, then at least the directory tree is trustworthy. * Type 1 is written at the very end of the session. If it matches, then * the whole image is trustworthy. * @param t The image being written * @flag bit0-7= tag type * 1= session tag (End checksumming.) * 2= superblock tag (System Area and Volume Descriptors) * 3= tree tag (ECMA-119 and Rock Ridge tree) */ int iso_md5_write_tag(Ecma119Image *t, int flag); #endif /* ! LIBISO_MD5_H_ */ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2023 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include #include #include #include #ifdef Xorriso_standalonE #ifdef Xorriso_with_libjtE #include "../libjte/libjte.h" #endif #else #ifdef Libisofs_with_libjtE #include #endif #endif /* ! Xorriso_standalonE */ #include "libiso_msgs.h" #include "libisofs.h" #include "messages.h" #include "util.h" #include "node.h" #include "stream.h" /* * error codes are 32 bit numbers, that follow the following conventions: * * bit 31 (MSB) -> 1 (to make the value always negative) * bits 30-24 -> Encoded severity (Use ISO_ERR_SEV to translate an error code * to a LIBISO_MSGS_SEV_* constant) * = 0x10 -> DEBUG * = 0x20 -> UPDATE * = 0x30 -> NOTE * = 0x40 -> HINT * = 0x50 -> WARNING * = 0x60 -> SORRY * = 0x64 -> MISHAP * = 0x68 -> FAILURE * = 0x70 -> FATAL * = 0x71 -> ABORT * bits 23-20 -> Encoded priority (Use ISO_ERR_PRIO to translate an error code * to a LIBISO_MSGS_PRIO_* constant) * = 0x0 -> ZERO * = 0x1 -> LOW * = 0x2 -> MEDIUM * = 0x3 -> HIGH * bits 19-16 -> Reserved for future usage (maybe message ranges) * bits 15-0 -> Error code */ #define ISO_ERR_SEV(e) (e & 0x7F000000) #define ISO_ERR_PRIO(e) ((e & 0x00700000) << 8) #define ISO_ERR_CODE(e) ((e & 0x0000FFFF) | 0x00030000) int iso_message_id = LIBISO_MSGS_ORIGIN_IMAGE_BASE; /** * Threshold for aborting. */ int abort_threshold = LIBISO_MSGS_SEV_FAILURE; #define MAX_MSG_LEN 4096 struct libiso_msgs *libiso_msgr = NULL; /* ------------- List of xinfo clone functions ----------- */ struct iso_xinfo_cloner_assoc { iso_node_xinfo_func proc; iso_node_xinfo_cloner cloner; struct iso_xinfo_cloner_assoc *next; }; struct iso_xinfo_cloner_assoc *iso_xinfo_cloner_list = NULL; /* API */ int iso_node_xinfo_make_clonable(iso_node_xinfo_func proc, iso_node_xinfo_cloner cloner, int flag) { struct iso_xinfo_cloner_assoc *assoc; /* Look for existing assoc of proc */ for (assoc = iso_xinfo_cloner_list; assoc != NULL; assoc = assoc->next) if (assoc->proc == proc) break; if (assoc == NULL) { assoc = calloc(1, sizeof(struct iso_xinfo_cloner_assoc)); if (assoc == NULL) return ISO_OUT_OF_MEM; assoc->proc = proc; assoc->next = iso_xinfo_cloner_list; iso_xinfo_cloner_list = assoc; } assoc->cloner = cloner; return ISO_SUCCESS; } /* API */ int iso_node_xinfo_get_cloner(iso_node_xinfo_func proc, iso_node_xinfo_cloner *cloner, int flag) { struct iso_xinfo_cloner_assoc *assoc; *cloner = NULL; for (assoc = iso_xinfo_cloner_list; assoc != NULL; assoc = assoc->next) { if (assoc->proc != proc) continue; *cloner = assoc->cloner; return 1; } return 0; } static int iso_node_xinfo_dispose_cloners(int flag) { struct iso_xinfo_cloner_assoc *assoc, *next; for (assoc = iso_xinfo_cloner_list; assoc != NULL; assoc = next) { next = assoc->next; free((char *) assoc); } iso_xinfo_cloner_list= NULL; return(1); } /* ------------- End of xinfo clone functions list ----------- */ /* @param flag bit0= do not set up locale by LC_* environment variables */ int iso_init_with_flag(int flag) { int ret; #ifdef Libisofs_with_libjtE /* Ugly compile time check for header version compatibility. If everything matches, then it produces no C code. In case of mismatch, intentionally faulty C code will be inserted. */ /* The indentation is an advise of man gcc to help old compilers ignoring */ #if iso_libjte_req_major > LIBJTE_VERSION_MAJOR #define Libisofs_libjte_dot_h_too_olD 1 #endif #if iso_libjte_req_major == LIBJTE_VERSION_MAJOR && iso_libjte_req_minor > LIBJTE_VERSION_MINOR #define Libisofs_libjte_dot_h_too_olD 1 #endif #if iso_libjte_req_minor == LIBJTE_VERSION_MINOR && iso_libjte_req_micro > LIBJTE_VERSION_MICRO #define Libisofs_libjte_dot_h_too_olD 1 #endif #ifdef Libisofs_libjte_dot_h_too_olD LIBJTE_MISCONFIGURATION = 0; INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libjte_dot_h_TOO_OLD__SEE_libisofs_dot_h_AND_messages_c = 0; LIBJTE_MISCONFIGURATION_ = 0; #endif if (! libjte__is_compatible(LIBJTE_VERSION_MAJOR, LIBJTE_VERSION_MINOR, LIBJTE_VERSION_MICRO, 0)) { fprintf(stderr, "\nlibisofs: libjte TOO OLD ! Need at least libjte-%d.%d.%d\n\n", LIBJTE_VERSION_MAJOR, LIBJTE_VERSION_MINOR, LIBJTE_VERSION_MICRO); return ISO_FATAL_ERROR; } #endif /* Libisofs_with_libjtE */ if (! (flag & 1)) { iso_init_locale(0); } if (libiso_msgr == NULL) { if (libiso_msgs_new(&libiso_msgr, 0) <= 0) return ISO_FATAL_ERROR; } libiso_msgs_set_severities(libiso_msgr, LIBISO_MSGS_SEV_NEVER, LIBISO_MSGS_SEV_FATAL, "libisofs: ", 0); ret = iso_node_xinfo_make_clonable(aaip_xinfo_func, aaip_xinfo_cloner, 0); if (ret < 0) return ret; ret = iso_node_xinfo_make_clonable(checksum_cx_xinfo_func, checksum_cx_xinfo_cloner, 0); if (ret < 0) return ret; ret = iso_node_xinfo_make_clonable(checksum_md5_xinfo_func, checksum_md5_xinfo_cloner, 0); if (ret < 0) return ret; ret = iso_node_xinfo_make_clonable(zisofs_zf_xinfo_func, zisofs_zf_xinfo_cloner, 0); if (ret < 0) return ret; ret = iso_node_xinfo_make_clonable(iso_px_ino_xinfo_func, iso_px_ino_xinfo_cloner, 0); if (ret < 0) return ret; ret = iso_node_xinfo_make_clonable(iso_hfsplus_xinfo_func, iso_hfsplus_xinfo_cloner, 0); if (ret < 0) return ret; return 1; } int iso_init() { return iso_init_with_flag(0); } void iso_finish() { libiso_msgs_destroy(&libiso_msgr, 0); iso_node_xinfo_dispose_cloners(0); iso_stream_destroy_cmpranks(0); } int iso_set_abort_severity(char *severity) { int ret, sevno; ret = libiso_msgs__text_to_sev(severity, &sevno, 0); if (ret <= 0) return ISO_WRONG_ARG_VALUE; if (sevno > LIBISO_MSGS_SEV_FAILURE || sevno < LIBISO_MSGS_SEV_NOTE) return ISO_WRONG_ARG_VALUE; ret = abort_threshold; abort_threshold = sevno; return ret; } void iso_msg_debug(int imgid, const char *fmt, ...) { char *msg = NULL; va_list ap; LIBISO_ALLOC_MEM_VOID(msg, char, MAX_MSG_LEN); va_start(ap, fmt); vsnprintf(msg, MAX_MSG_LEN, fmt, ap); va_end(ap); libiso_msgs_submit(libiso_msgr, imgid, 0x00000002, LIBISO_MSGS_SEV_DEBUG, LIBISO_MSGS_PRIO_ZERO, msg, 0, 0); ex:; LIBISO_FREE_MEM(msg); } const char *iso_error_to_msg(int errcode) { switch(errcode) { case ISO_CANCELED: return "Operation canceled"; case ISO_FATAL_ERROR: return "Unknown or unexpected fatal error"; case ISO_ERROR: return "Unknown or unexpected error"; case ISO_ASSERT_FAILURE: return "Internal programming error. Please report this bug"; case ISO_NULL_POINTER: return "NULL pointer as value for an arg. that does not allow NULL"; case ISO_OUT_OF_MEM: return "Memory allocation error"; case ISO_INTERRUPTED: return "Interrupted by a signal"; case ISO_WRONG_ARG_VALUE: return "Invalid parameter value"; case ISO_THREAD_ERROR: return "Cannot create a needed thread"; case ISO_WRITE_ERROR: return "Write error"; case ISO_BUF_READ_ERROR: return "Buffer read error"; case ISO_NODE_ALREADY_ADDED: return "Trying to add to a dir a node already added to a dir"; case ISO_NODE_NAME_NOT_UNIQUE: return "Node with same name already exists"; case ISO_NODE_NOT_ADDED_TO_DIR: return "Trying to remove a node that was not added to dir"; case ISO_NODE_DOESNT_EXIST: return "A requested node does not exist"; case ISO_IMAGE_ALREADY_BOOTABLE: return "Try to set the boot image of an already bootable image"; case ISO_BOOT_IMAGE_NOT_VALID: return "Trying to use an invalid file as boot image"; case ISO_BOOT_IMAGE_OVERFLOW: return "Too many boot images added"; case ISO_FILE_ERROR: return "Error on file operation"; case ISO_FILE_ALREADY_OPENED: return "Trying to open an already opened file"; case ISO_FILE_ACCESS_DENIED: return "Access to file is not allowed"; case ISO_FILE_BAD_PATH: return "Incorrect path to file"; case ISO_FILE_DOESNT_EXIST: return "The file does not exist in the filesystem"; case ISO_FILE_NOT_OPENED: return "Trying to read or close a file not opened"; case ISO_FILE_IS_DIR: return "Directory used where no dir is expected"; case ISO_FILE_READ_ERROR: return "Read error"; case ISO_FILE_IS_NOT_DIR: return "Not dir used where a dir is expected"; case ISO_FILE_IS_NOT_SYMLINK: return "Not symlink used where a symlink is expected"; case ISO_FILE_SEEK_ERROR: return "Cannot seek to specified location"; case ISO_FILE_IGNORED: return "File not supported in ECMA-119 tree and thus ignored"; case ISO_FILE_TOO_BIG: return "A file is bigger than supported by used standard"; case ISO_FILE_CANT_WRITE: return "File read error during image creation"; case ISO_FILENAME_WRONG_CHARSET: case ISO_FILENAME_WRONG_CHARSET_OLD: return "Cannot convert filename to requested charset"; case ISO_FILE_CANT_ADD: return "File cannot be added to the tree"; case ISO_FILE_IMGPATH_WRONG: return "File path break specification constraints and will be ignored"; case ISO_CHARSET_CONV_ERROR: return "Charset conversion error"; case ISO_MANGLE_TOO_MUCH_FILES: return "Too much files to mangle, cannot guarantee unique file names"; case ISO_WRONG_PVD: return "Wrong or damaged Primary Volume Descriptor"; case ISO_WRONG_RR: return "Wrong or damaged Rock Ridge entry"; case ISO_UNSUPPORTED_RR: return "Unsupported Rock Ridge feature"; case ISO_WRONG_ECMA119: return "Wrong or damaged ECMA-119"; case ISO_UNSUPPORTED_ECMA119: return "Unsupported ECMA-119 feature"; case ISO_WRONG_EL_TORITO: return "Wrong or damaged El-Torito catalog"; case ISO_UNSUPPORTED_EL_TORITO: return "Unsupported El-Torito feature"; case ISO_ISOLINUX_CANT_PATCH: return "Cannot patch isolinux boot image"; case ISO_UNSUPPORTED_SUSP: return "Unsupported SUSP feature"; case ISO_WRONG_RR_WARN: return "Error on a Rock Ridge entry that can be ignored"; case ISO_SUSP_UNHANDLED: return "Unhandled SUSP entry"; case ISO_SUSP_MULTIPLE_ER: return "Multiple ER SUSP entries found"; case ISO_UNSUPPORTED_VD: return "Unsupported volume descriptor found"; case ISO_EL_TORITO_WARN: return "El-Torito related warning"; case ISO_IMAGE_WRITE_CANCELED: return "Image write cancelled"; case ISO_EL_TORITO_HIDDEN: return "El-Torito image is hidden"; case ISO_DATA_SOURCE_SORRY: case ISO_DATA_SOURCE_MISHAP: case ISO_DATA_SOURCE_FAILURE: case ISO_DATA_SOURCE_FATAL: return "Read error occurred with IsoDataSource"; case ISO_AAIP_IGNORED: return "AAIP info with ACL or xattr in ISO image will be ignored"; case ISO_AAIP_BAD_ACL: return "Error with decoding ACL from AAIP info"; case ISO_AAIP_BAD_ACL_TEXT: return "Error with encoding ACL for AAIP"; case ISO_AAIP_NOT_ENABLED: return "AAIP processing for ACL or xattr not enabled at compile time"; case ISO_AAIP_BAD_AASTRING: return "Error with decoding AAIP info for ACL or xattr"; case ISO_AAIP_NO_GET_LOCAL: case ISO_AAIP_NO_GET_LOCAL_S: return "Error with reading ACL or other attributes from local file"; case ISO_AAIP_NO_SET_LOCAL: case ISO_AAIP_NO_SET_LOCAL_S: return "Error with attaching ACL or other attributes to local file"; case ISO_AAIP_NON_USER_NAME: return "Unallowed attempt to set an xattr with non-userspace name"; case ISO_EXTF_TOO_OFTEN: return "Too many references on a single external filter command"; case ISO_ZLIB_NOT_ENABLED: return "Use of zlib was not enabled at compile time"; case ISO_ZISOFS_TOO_LARGE: return "File too large. Cannot apply zisofs filter."; case ISO_FILTER_WRONG_INPUT: return "Filter input differs from previous run"; case ISO_ZLIB_COMPR_ERR: return "zlib compression/decompression error"; case ISO_ZISOFS_WRONG_INPUT: return "Input stream is not in a supported zisofs format"; case ISO_ZISOFS_PARAM_LOCK: return "Cannot set global zisofs parameters while filters exist"; case ISO_ZLIB_EARLY_EOF: return "Premature EOF of zlib input stream"; case ISO_MD5_AREA_CORRUPTED: return "Checksum area or checksum tag appear corrupted"; case ISO_MD5_TAG_MISMATCH: return "Checksum mismatch between checksum tag and data blocks"; case ISO_SB_TREE_CORRUPTED: return "Checksum mismatch in System Area, Volume Descriptors, or directory tree"; case ISO_MD5_TAG_UNEXPECTED: return "Unexpected checksum tag type encountered"; case ISO_MD5_TAG_MISPLACED: return "Misplaced checksum tag type encountered"; case ISO_MD5_TAG_OTHER_RANGE: return "Checksum tag with unexpected address range encountered"; case ISO_MD5_STREAM_CHANGE: return "Detected file content changes while it was written into the image"; case ISO_SCDBACKUP_TAG_NOT_0: return "Session does not start at LBA 0. scdbackup checksum tag not written."; case ISO_BOOT_NO_CATALOG: return "No boot catalog created yet"; case ISO_OVWRT_MS_TOO_SMALL: return "Multi-session offset too small for overwrite buffer"; case ISO_PART_OFFST_TOO_SMALL: return "Partition offset too small for first tree root."; case ISO_OVWRT_FIFO_TOO_SMALL: return "The ring buffer is too small for overwrite buffer"; case ISO_LIBJTE_NOT_ENABLED: return "Use of libjte was not enabled at compile time"; case ISO_LIBJTE_START_FAILED: return "Failed to start up Jigdo Template Extraction"; case ISO_LIBJTE_END_FAILED: return "Failed to finish Jigdo Template Extraction"; case ISO_LIBJTE_FILE_FAILED: return "Failed to process file for Jigdo Template Extraction"; case ISO_BOOT_TOO_MANY_MIPS: return "Too many MIPS Big Endian boot files given (max. 15)"; case ISO_BOOT_FILE_MISSING: return "Boot file missing in image"; case ISO_BAD_PARTITION_NO: return "Partition number out of range"; case ISO_BAD_PARTITION_FILE: return "Cannot open data file for appended partition"; case ISO_NON_MBR_SYS_AREA: return "May not combine MBR partition with non-MBR system area"; case ISO_DISPLACE_ROLLOVER: return "Displacement offset leads outside 32 bit range"; case ISO_NAME_NEEDS_TRANSL: return "File name cannot be written into ECMA-119 untranslated"; case ISO_STREAM_NO_CLONE: return "Data file input stream object offers no cloning method"; case ISO_XINFO_NO_CLONE: return "Extended information class offers no cloning method"; case ISO_MD5_TAG_COPIED: return "Found copied superblock checksum tag"; case ISO_RR_NAME_TOO_LONG: return "Rock Ridge leaf name too long"; case ISO_RR_NAME_RESERVED: return "Reserved Rock Ridge leaf name"; case ISO_RR_PATH_TOO_LONG: return "Rock Ridge path too long"; case ISO_AAIP_BAD_ATTR_NAME: return "Attribute name cannot be represented"; case ISO_AAIP_ACL_MULT_OBJ: return "ACL text contains multiple entries of user::, group::, other::"; case ISO_SECT_SCATTERED: return "File sections do not form consecutive array of blocks"; case ISO_BOOT_TOO_MANY_APM: return "Too many Apple Partition Map entries requested"; case ISO_BOOT_APM_OVERLAP: return "Overlapping Apple Partition Map entries requested"; case ISO_BOOT_TOO_MANY_GPT: return "Too many GPT entries requested"; case ISO_BOOT_GPT_OVERLAP: return "Overlapping GPT entries requested"; case ISO_BOOT_TOO_MANY_MBR: return "Too many MBR partition entries requested"; case ISO_BOOT_MBR_OVERLAP: return "Overlapping MBR partition entries requested"; case ISO_BOOT_MBR_COLLISION: return "Attempt to use an MBR partition entry twice"; case ISO_BOOT_NO_EFI_ELTO: return "No suitable El Torito EFI boot image for exposure as GPT partition"; case ISO_BOOT_HFSP_BAD_BSIZE: return "Not a supported HFS+ or APM block size"; case ISO_BOOT_APM_GPT_BSIZE: return "APM block size prevents coexistence with GPT"; case ISO_HFSP_NO_MANGLE: return "Name collision in HFS+, mangling not possible"; case ISO_DEAD_SYMLINK: return "Symbolic link cannot be resolved"; case ISO_DEEP_SYMLINK: return "Too many chained symbolic links"; case ISO_BAD_ISO_FILETYPE: return "Unrecognized file type in ISO image"; case ISO_NAME_NOT_UCS2: return "Filename not suitable for character set UCS-2"; case ISO_IMPORT_COLLISION: return "File name collision during ISO image import"; case ISO_HPPA_PALO_INCOMPL: return "Incomplete HP-PA PALO boot parameters"; case ISO_HPPA_PALO_OFLOW: return "HP-PA PALO boot address exceeds 2 GB"; case ISO_HPPA_PALO_NOTREG: return "HP-PA PALO file is not a data file"; case ISO_HPPA_PALO_CMDLEN: return "HP-PA PALO command line too long"; case ISO_SYSAREA_PROBLEMS: return "Problems encountered during inspection of System Area"; case ISO_INQ_SYSAREA_PROP: return "Unrecognized inquiry for system area property"; case ISO_ALPHA_BOOT_NOTREG: return "DEC Alpha Boot Loader file is not a data file"; case ISO_NO_KEPT_DATA_SRC: return "No data source of imported ISO image available"; case ISO_MALFORMED_READ_INTVL: return "Malformed description string for interval reader"; case ISO_INTVL_READ_PROBLEM: return "Unreadable file, premature EOF, or failure to seek for interval reader"; case ISO_NOT_REPRODUCIBLE: return "Cannot arrange content of data files in surely reproducible way"; case ISO_PATCH_FILTERED_BOOT: return "May not write boot info into filtered stream of boot image"; case ISO_PATCH_OVERSIZED_BOOT: return "Boot image to large to buffer for writing boot info"; case ISO_RR_NAME_TRUNCATED: return "File name had to be truncated and MD5 marked"; case ISO_TRUNCATE_ISOFSNT: return "File name truncation length changed by loaded image info"; case ISO_GENERAL_NOTE: return "A general note message was issued"; case ISO_BAD_FSRC_FILETYPE: return "Unrecognized file type of IsoFileSrc object"; case ISO_GPT_NO_VOL_UUID: return "Cannot derive GPT GUID from undefined pseudo-UUID volume timestamp"; case ISO_BAD_GPT_GUID_MODE: return "Unrecognized GPT disk GUID setup mode"; case ISO_NO_ROOT_DIR: return "Unable to obtain root directory"; case ISO_SUSP_WRONG_CE_SIZE: return "Zero sized, oversized, or mislocated SUSP CE area found"; case ISO_MULTI_OVER_IMPORTED: return "Multi-session would overwrite imported_iso interval"; case ISO_ELTO_EFI_HIDDEN: return "El-Torito EFI image is hidden"; case ISO_HFSPLUS_TOO_MANY_FILES: return "Too many files in HFS+ directory tree"; case ISO_ZISOFS_TOO_MANY_PTR: return "Too many zisofs block pointers needed overall"; case ISO_ZISOFS_BPT_UNDERRUN: return "Prevented zisofs block pointer counter underrun"; case ISO_ZISOFS_UNKNOWN_SIZE: return "Cannot obtain size of zisofs compressed stream"; case ISO_UNDEF_READ_FEATURE: return "Undefined IsoReadImageFeatures name"; case ISO_TOO_MANY_CE: return "Too many CE entries for single file"; case ISO_TOO_MANY_CE_FOR_LINUX: return "Too many CE entries for single file when mounted by Linux"; case ISO_CE_REMOVING_ATTR: return "Too many CE entries for single file, removing attributes"; case ISO_LFA_UNKNOWN_LETTER: return "Unknown Linux-like chattr letter encountered during conversion"; case ISO_LFA_UNKNOWN_BIT: return "Unknown Linux-like file attribute flag bit encountered during conversion"; case ISO_LFA_NOT_ENABLED: return "Local Linux-like file attribute processing not enabled at compile time"; case ISO_LFA_NO_GET_LOCAL: return "Error with getting Linux-like file attributes of local file"; case ISO_LFA_NO_SET_LOCAL: return "Error with setting Linux-like file attributes of local file"; case ISO_LFA_NO_OPEN_LOCAL: return "Failure to open local file for Linux-like file attributes"; default: return "Unknown error"; } } int iso_msg_is_abort(int errcode) { if (ISO_ERR_SEV(errcode) >= abort_threshold) return 1; return 0; } int iso_msg_submit(int imgid, int errcode, int causedby, const char *fmt, ...) { char msg[MAX_MSG_LEN]; va_list ap; /* when called with ISO_CANCELED, we don't need to submit any message */ if (errcode == (int) ISO_CANCELED && fmt == NULL) { return ISO_CANCELED; } if (fmt) { va_start(ap, fmt); vsnprintf(msg, MAX_MSG_LEN, fmt, ap); va_end(ap); } else { strncpy(msg, iso_error_to_msg(errcode), MAX_MSG_LEN - 1); msg[MAX_MSG_LEN - 1] = 0; } libiso_msgs_submit(libiso_msgr, imgid, ISO_ERR_CODE(errcode), ISO_ERR_SEV(errcode), ISO_ERR_PRIO(errcode), msg, 0, 0); if (causedby != 0) { snprintf(msg, MAX_MSG_LEN, " > Caused by: %s", iso_error_to_msg(causedby)); libiso_msgs_submit(libiso_msgr, imgid, ISO_ERR_CODE(causedby), LIBISO_MSGS_SEV_NOTE, LIBISO_MSGS_PRIO_LOW, msg, 0, 0); if (ISO_ERR_SEV(causedby) == LIBISO_MSGS_SEV_FATAL) { return ISO_CANCELED; } } if (iso_msg_is_abort(errcode)) { return ISO_CANCELED; } else { return 0; } } /** * Control queueing and stderr printing of messages from libisofs. * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", * "NOTE", "UPDATE", "DEBUG", "ALL". * * @param queue_severity Gives the minimum limit for messages to be queued. * Default: "NEVER". If you queue messages then you * must consume them by iso_msgs_obtain(). * @param print_severity Does the same for messages to be printed directly * to stderr. * @param print_id A text prefix to be printed before the message. * @return >0 for success, <=0 for error */ int iso_set_msgs_severities(char *queue_severity, char *print_severity, char *print_id) { int ret, queue_sevno, print_sevno; ret = libiso_msgs__text_to_sev(queue_severity, &queue_sevno, 0); if (ret <= 0) return 0; ret = libiso_msgs__text_to_sev(print_severity, &print_sevno, 0); if (ret <= 0) return 0; ret = libiso_msgs_set_severities(libiso_msgr, queue_sevno, print_sevno, print_id, 0); if (ret <= 0) return 0; return 1; } /** * Obtain the oldest pending libisofs message from the queue which has at * least the given minimum_severity. This message and any older message of * lower severity will get discarded from the queue and is then lost forever. * * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", * "NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER" * will discard the whole queue. * * @param error_code Will become a unique error code as listed in messages.h * @param imgid Id of the image that was issued the message. * @param msg_text Must provide at least ISO_MSGS_MESSAGE_LEN bytes. * @param severity Will become the severity related to the message and * should provide at least 80 bytes. * @return 1 if a matching item was found, 0 if not, <0 for severe errors */ int iso_obtain_msgs(char *minimum_severity, int *error_code, int *imgid, char msg_text[], char severity[]) { int ret, minimum_sevno, sevno, priority, os_errno; double timestamp; pid_t pid; char *textpt, *sev_name; struct libiso_msgs_item *item= NULL; ret = libiso_msgs__text_to_sev(minimum_severity, &minimum_sevno, 0); if (ret <= 0) return 0; ret = libiso_msgs_obtain(libiso_msgr, &item, minimum_sevno, LIBISO_MSGS_PRIO_ZERO, 0); if (ret <= 0) goto ex; ret = libiso_msgs_item_get_msg(item, error_code, &textpt, &os_errno, 0); if (ret <= 0) goto ex; strncpy(msg_text, textpt, ISO_MSGS_MESSAGE_LEN-1); if (strlen(textpt) >= ISO_MSGS_MESSAGE_LEN) msg_text[ISO_MSGS_MESSAGE_LEN-1] = 0; ret = libiso_msgs_item_get_origin(item, ×tamp, &pid, imgid, 0); if (ret <= 0) goto ex; severity[0]= 0; ret = libiso_msgs_item_get_rank(item, &sevno, &priority, 0); if (ret <= 0) goto ex; ret = libiso_msgs__sev_to_text(sevno, &sev_name, 0); if (ret <= 0) goto ex; strcpy(severity, sev_name); ret = 1; ex: ; libiso_msgs_destroy_item(libiso_msgr, &item, 0); return ret; } int iso_msgs_submit(int error_code, char msg_text[], int os_errno, char severity[], int origin) { int ret, sevno; ret = libiso_msgs__text_to_sev(severity, &sevno, 0); if (ret <= 0) sevno = LIBISO_MSGS_SEV_ALL; if (error_code <= 0) { switch(sevno) { case LIBISO_MSGS_SEV_ABORT: error_code = 0x00040000; break; case LIBISO_MSGS_SEV_FATAL: error_code = 0x00040001; break; case LIBISO_MSGS_SEV_SORRY: error_code = 0x00040002; break; case LIBISO_MSGS_SEV_WARNING: error_code = 0x00040003; break; case LIBISO_MSGS_SEV_HINT: error_code = 0x00040004; break; case LIBISO_MSGS_SEV_NOTE: error_code = 0x00040005; break; case LIBISO_MSGS_SEV_UPDATE: error_code = 0x00040006; break; case LIBISO_MSGS_SEV_DEBUG: error_code = 0x00040007; break; default: error_code = 0x00040008; } } ret = libiso_msgs_submit(libiso_msgr, origin, error_code, sevno, LIBISO_MSGS_PRIO_HIGH, msg_text, os_errno, 0); return ret; } int iso_text_to_sev(char *severity_name, int *sevno) { int ret; ret = libiso_msgs__text_to_sev(severity_name, sevno, 0); if (ret <= 0) *sevno = LIBISO_MSGS_SEV_FATAL; return ret; } int iso_sev_to_text(int severity_number, char **severity_name) { int ret; ret = libiso_msgs__sev_to_text(severity_number, severity_name, 0); return ret; } /** * Return the messenger object handle used by libisofs. This handle * may be used by related libraries to their own compatible * messenger objects and thus to direct their messages to the libisofs * message queue. See also: libburn, API function burn_set_messenger(). * * @return the handle. Do only use with compatible */ void *iso_get_messenger() { return libiso_msgr; } int iso_error_get_severity(int e) { return ISO_ERR_SEV(e); } int iso_error_get_priority(int e) { return ISO_ERR_PRIO(e); } int iso_error_get_code(int e) { return ISO_ERR_CODE(e); } int iso_report_errfile(char *path, int error_code, int os_errno, int flag) { libiso_msgs_submit(libiso_msgr, 0, error_code, LIBISO_MSGS_SEV_ERRFILE, LIBISO_MSGS_PRIO_HIGH, path, os_errno, 0); return(1); } int iso_libjte_forward_msgs(void *libjte_handle, int imgid, int errcode, int flag) { #ifdef Libisofs_with_libjtE char *msg = NULL; int res; struct libjte_env *handle = (struct libjte_env *) libjte_handle; res = ISO_SUCCESS; while(1) { msg= libjte_get_next_message(handle); if(msg == NULL) break; res = iso_msg_submit(imgid, errcode, 0, msg); free(msg); } return res; #else /* Libisofs_with_libjtE */ return ISO_SUCCESS; #endif /* ! Libisofs_with_libjtE */ } /* * Copyright (c) 2007 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /* * Message handling for libisofs */ #ifndef MESSAGES_H_ #define MESSAGES_H_ /** * Take and increment this variable to get a valid identifier for message * origin. */ extern int iso_message_id; /** * Submit a debug message. */ void iso_msg_debug(int imgid, const char *fmt, ...); /** * Inquire whether the given error code triggers the abort threshold */ int iso_msg_is_abort(int errcode); /** * * @param errcode * The error code. * @param causedby * Error that was caused the errcode. If this error is a FATAL error, * < 0 will be returned in any case. Use 0 if there is no previous * cause for the error. * @return * 0 on success, < 0 if function must abort asap. */ int iso_msg_submit(int imgid, int errcode, int causedby, const char *fmt, ...); /* To be called with events which report incidents with individual input files from the local filesystem. Not with image nodes, files containing an image or similar file-like objects. */ int iso_report_errfile(char *path, int error_code, int os_errno, int flag); /* Drains the libjte message list and puts out the messages via iso_msg_submit() */ int iso_libjte_forward_msgs(void *libjte_handle, int imgid, int errcode, int flag); #endif /*MESSAGES_H_*/ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libisofs.h" #include "image.h" #include "node.h" #include "stream.h" #include "aaip_0_2.h" #include "messages.h" #include "util.h" #include "eltorito.h" #include #include #include #include #include struct dir_iter_data { /* points to the last visited child, to NULL before start */ IsoNode *pos; /* Some control flags. * bit 0 -> 1 if next called, 0 reset at start or on deletion */ int flag; }; /** * Increments the reference counting of the given node. */ void iso_node_ref(IsoNode *node) { ++node->refcount; } /** * Decrements the reference counting of the given node. * If it reach 0, the node is free, and, if the node is a directory, * its children will be unref() too. */ void iso_node_unref(IsoNode *node) { if (node == NULL) return; if (--node->refcount == 0) { switch (node->type) { case LIBISO_DIR: { IsoNode *child = ((IsoDir*)node)->children; while (child != NULL) { IsoNode *tmp = child->next; child->parent = NULL; iso_node_unref(child); child = tmp; } } break; case LIBISO_FILE: { IsoFile *file = (IsoFile*) node; iso_stream_unref(file->stream); } break; case LIBISO_SYMLINK: { IsoSymlink *link = (IsoSymlink*) node; free(link->dest); } break; case LIBISO_BOOT: { IsoBoot *bootcat = (IsoBoot *) node; if (bootcat->content != NULL) free(bootcat->content); } break; default: /* other kind of nodes does not need to delete anything here */ break; } if (node->xinfo) { IsoExtendedInfo *info = node->xinfo; while (info != NULL) { IsoExtendedInfo *tmp = info->next; /* free extended info */ info->process(info->data, 1); free(info); info = tmp; } } free(node->name); free(node); } } /** * Add extended information to the given node. Extended info allows * applications (and libisofs itself) to add more information to an IsoNode. * You can use this facilities to associate new information with a given * node. * * Each node keeps a list of added extended info, meaning you can add several * extended info data to each node. Each extended info you add is identified * by the proc parameter, a pointer to a function that knows how to manage * the external info data. Thus, in order to add several types of extended * info, you need to define a "proc" function for each type. * * @param node * The node where to add the extended info * @param proc * A function pointer used to identify the type of the data, and that * knows how to manage it * @param data * Extended info to add. * @return * 1 if success, 0 if the given node already has extended info of the * type defined by the "proc" function, < 0 on error */ int iso_node_add_xinfo(IsoNode *node, iso_node_xinfo_func proc, void *data) { IsoExtendedInfo *info; IsoExtendedInfo *pos; if (node == NULL || proc == NULL) { return ISO_NULL_POINTER; } pos = node->xinfo; while (pos != NULL) { if (pos->process == proc) { return 0; /* extended info already added */ } pos = pos->next; } info = malloc(sizeof(IsoExtendedInfo)); if (info == NULL) { return ISO_OUT_OF_MEM; } info->next = node->xinfo; info->data = data; info->process = proc; node->xinfo = info; return ISO_SUCCESS; } /** * Remove the given extended info (defined by the proc function) from the * given node. * * @return * 1 on success, 0 if node does not have extended info of the requested * type, < 0 on error */ int iso_node_remove_xinfo(IsoNode *node, iso_node_xinfo_func proc) { IsoExtendedInfo *pos, *prev; if (node == NULL || proc == NULL) { return ISO_NULL_POINTER; } prev = NULL; pos = node->xinfo; while (pos != NULL) { if (pos->process == proc) { /* this is the extended info we want to remove */ pos->process(pos->data, 1); if (prev != NULL) { prev->next = pos->next; } else { node->xinfo = pos->next; } free(pos); return ISO_SUCCESS; } prev = pos; pos = pos->next; } /* requested xinfo not found */ return 0; } /** * Get the given extended info (defined by the proc function) from the * given node. * * @param data * Will be filled with the extended info corresponding to the given proc * function * @return * 1 on success, 0 if node does not have extended info of the requested * type, < 0 on error */ int iso_node_get_xinfo(IsoNode *node, iso_node_xinfo_func proc, void **data) { IsoExtendedInfo *pos; if (node == NULL || proc == NULL || data == NULL) { return ISO_NULL_POINTER; } *data = NULL; pos = node->xinfo; while (pos != NULL) { if (pos->process == proc) { /* this is the extended info we want */ *data = pos->data; return ISO_SUCCESS; } pos = pos->next; } /* requested xinfo not found */ return 0; } /* API */ int iso_node_get_next_xinfo(IsoNode *node, void **handle, iso_node_xinfo_func *proc, void **data) { IsoExtendedInfo *xinfo; if (node == NULL || handle == NULL || proc == NULL || data == NULL) return ISO_NULL_POINTER; *proc = NULL; *data = NULL; xinfo = (IsoExtendedInfo *) *handle; if (xinfo == NULL) xinfo = node->xinfo; else xinfo = xinfo->next; *handle = xinfo; if (xinfo == NULL) return 0; *proc = xinfo->process; *data = xinfo->data; return ISO_SUCCESS; } int iso_node_remove_all_xinfo(IsoNode *node, int flag) { IsoExtendedInfo *pos, *next; for (pos = node->xinfo; pos != NULL; pos = next) { next = pos->next; pos->process(pos->data, 1); free((char *) pos); } node->xinfo = NULL; return ISO_SUCCESS; } static int iso_node_revert_xinfo_list(IsoNode *node, int flag) { IsoExtendedInfo *pos, *next, *prev = NULL; for (pos = node->xinfo; pos != NULL; pos = next) { next = pos->next; pos->next = prev; prev = pos; } node->xinfo = prev; return ISO_SUCCESS; } int iso_node_clone_xinfo(IsoNode *from_node, IsoNode *to_node, int flag) { void *handle = NULL, *data, *new_data; iso_node_xinfo_func proc; iso_node_xinfo_cloner cloner; int ret; iso_node_remove_all_xinfo(to_node, 0); while (1) { ret = iso_node_get_next_xinfo(from_node, &handle, &proc, &data); if (ret <= 0) break; ret = iso_node_xinfo_get_cloner(proc, &cloner, 0); if (ret == 0) return ISO_XINFO_NO_CLONE; if (ret < 0) return ret; ret = (*cloner)(data, &new_data, 0); if (ret < 0) break; ret = iso_node_add_xinfo(to_node, proc, new_data); if (ret < 0) break; } if (ret < 0) { iso_node_remove_all_xinfo(to_node, 0); } else { ret = iso_node_revert_xinfo_list(to_node, 0); } return ret; } /** * Get the type of an IsoNode. */ enum IsoNodeType iso_node_get_type(IsoNode *node) { return node->type; } /** * Set the name of a node. * * @param name The name in UTF-8 encoding * @param truncate_length (<64 = return on oversized name ) * @param flag bit0= issue warning in case of truncation */ int iso_node_set_name_trunc(IsoNode *node, const char *in_name, int truncate_length, int flag) { char *new, *name, *trunc = NULL; int ret; if ((IsoNode*)node->parent == node) { /* you can't change name of the root node */ ret = ISO_WRONG_ARG_VALUE; goto ex; } name = (char *) in_name; if (truncate_length >= 64) { trunc = strdup(name); if (trunc == 0) { ret = ISO_OUT_OF_MEM; goto ex; } ret = iso_truncate_rr_name(1, truncate_length, trunc, !(flag & 1)); if (ret < 0) goto ex; name = trunc; } /* check if the name is valid */ ret = iso_node_is_valid_name(name); if (ret < 0) goto ex; if (node->parent != NULL) { /* check if parent already has a node with same name */ if (iso_dir_get_node(node->parent, name, NULL) == 1) { ret = ISO_NODE_NAME_NOT_UNIQUE; goto ex; } } new = strdup(name); if (new == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } free(node->name); node->name = new; if (node->parent != NULL) { IsoDir *parent; int res; /* take and add again to ensure correct children order */ parent = node->parent; iso_node_take(node); res = iso_dir_add_node(parent, node, 0); if (res < 0) { ret = res; goto ex; } } ret = ISO_SUCCESS; ex: if (trunc != NULL) free(trunc); return ret; } int iso_node_set_name(IsoNode *node, const char *name) { return iso_node_set_name_trunc(node, name, 0, 0); } int iso_image_set_node_name(IsoImage *image, IsoNode *node, const char *name, int flag) { if (image->truncate_mode == 0) if ((int) strlen(name) > image->truncate_length) return ISO_RR_NAME_TOO_LONG; return iso_node_set_name_trunc(node, name, image->truncate_length, flag); } /** * Get the name of a node (in UTF-8). * The returned string belongs to the node and should not be modified nor * freed. Use strdup if you really need your own copy. */ const char *iso_node_get_name(const IsoNode *node) { static char *root = {""}; if (node->name == NULL) return root; return node->name; } /** * See API function iso_node_set_permissions() * * @param flag bit0= do not adjust ACL * @return >0 success , <0 error */ int iso_node_set_perms_internal(IsoNode *node, mode_t mode, int flag) { int ret; node->mode = (node->mode & S_IFMT) | (mode & ~S_IFMT); /* If the node has ACL info : update ACL */ ret = 1; if (!(flag & 1)) ret = iso_node_set_acl_text(node, "", "", 2); return ret; } /** * Set the permissions for the node. This attribute is only useful when * Rock Ridge extensions are enabled. * * @param mode * bitmask with the permissions of the node, as specified in 'man 2 stat'. * The file type bitfields will be ignored, only file permissions will be * modified. */ void iso_node_set_permissions(IsoNode *node, mode_t mode) { iso_node_set_perms_internal(node, mode, 0); } /** * Get the permissions for the node */ mode_t iso_node_get_permissions(const IsoNode *node) { return node->mode & ~S_IFMT; } /** * Get the mode of the node, both permissions and file type, as specified in * 'man 2 stat'. */ mode_t iso_node_get_mode(const IsoNode *node) { return node->mode; } /** * Set the user id for the node. This attribute is only useful when * Rock Ridge extensions are enabled. */ void iso_node_set_uid(IsoNode *node, uid_t uid) { node->uid = uid; } /** * Get the user id of the node. */ uid_t iso_node_get_uid(const IsoNode *node) { return node->uid; } /** * Set the group id for the node. This attribute is only useful when * Rock Ridge extensions are enabled. */ void iso_node_set_gid(IsoNode *node, gid_t gid) { node->gid = gid; } /** * Get the group id of the node. */ gid_t iso_node_get_gid(const IsoNode *node) { return node->gid; } /** * Set the time of last modification of the file */ void iso_node_set_mtime(IsoNode *node, time_t time) { node->mtime = time; } /** * Get the time of last modification of the file */ time_t iso_node_get_mtime(const IsoNode *node) { return node->mtime; } /** * Set the time of last access to the file */ void iso_node_set_atime(IsoNode *node, time_t time) { node->atime = time; } /** * Get the time of last access to the file */ time_t iso_node_get_atime(const IsoNode *node) { return node->atime; } /** * Set the time of last status change of the file */ void iso_node_set_ctime(IsoNode *node, time_t time) { node->ctime = time; } /** * Get the time of last status change of the file */ time_t iso_node_get_ctime(const IsoNode *node) { return node->ctime; } void iso_node_set_hidden(IsoNode *node, int hide_attrs) { /* you can't hide root node */ if ((IsoNode*)node->parent != node) { node->hidden = hide_attrs; } } int iso_node_get_hidden(IsoNode *node) { return node->hidden; } /** * Add a new node to a dir. Note that this function don't add a new ref to * the node, so you don't need to free it, it will be automatically freed * when the dir is deleted. Of course, if you want to keep using the node * after the dir life, you need to iso_node_ref() it. * * @param dir * the dir where to add the node * @param child * the node to add. You must ensure that the node hasn't previously added * to other dir, and that the node name is unique inside the child. * Otherwise this function will return a failure, and the child won't be * inserted. * @param replace * if the dir already contains a node with the same name, whether to * replace or not the old node with this. * @return * number of nodes in dir if success, < 0 otherwise */ int iso_dir_add_node(IsoDir *dir, IsoNode *child, enum iso_replace_mode replace) { IsoNode **pos; if (dir == NULL || child == NULL) { return ISO_NULL_POINTER; } if ((IsoNode*)dir == child) { return ISO_WRONG_ARG_VALUE; } /* * check if child is already added to another dir, or if child * is the root node, where parent == itself */ if (child->parent != NULL || child->parent == (IsoDir*)child) { return ISO_NODE_ALREADY_ADDED; } iso_dir_find(dir, child->name, &pos); return iso_dir_insert(dir, child, pos, replace); } /** * Locate a node inside a given dir. * * @param name * The name of the node * @param node * Location for a pointer to the node, it will filled with NULL if the dir * doesn't have a child with the given name. * The node will be owned by the dir and shouldn't be unref(). Just call * iso_node_ref() to get your own reference to the node. * Note that you can pass NULL is the only thing you want to do is check * if a node with such name already exists on dir. * @return * 1 node found, 0 child has no such node, < 0 error * Possible errors: * ISO_NULL_POINTER, if dir or name are NULL */ int iso_dir_get_node(IsoDir *dir, const char *name, IsoNode **node) { int ret; IsoNode **pos; if (dir == NULL || name == NULL) { return ISO_NULL_POINTER; } ret = iso_dir_exists(dir, name, &pos); if (ret == 0) { if (node) { *node = NULL; } return 0; /* node not found */ } if (node) { *node = *pos; } return 1; } int iso_dir_get_node_trunc(IsoDir *dir, int truncate_length, const char *name, IsoNode **node) { int ret; char *trunc = NULL; if ((int) strlen(name) <= truncate_length) { ret = iso_dir_get_node(dir, name, node); return ret; } trunc = strdup(name); if (trunc == NULL) return ISO_OUT_OF_MEM; ret = iso_truncate_rr_name(1, truncate_length, trunc, 1); if (ret < 0) goto ex; ret = iso_dir_get_node(dir, trunc, node); if (ret == 0) ret = 2; ex:; LIBISO_FREE_MEM(trunc); return ret; } /* API */ int iso_image_dir_get_node(IsoImage *image, IsoDir *dir, const char *name, IsoNode **node, int flag) { int ret; if (image->truncate_mode == 0 || (flag & 1)) ret = iso_dir_get_node(dir, name, node); else ret = iso_dir_get_node_trunc(dir, image->truncate_length, name, node); return ret; } /** * Get the number of children of a directory. * * @return * >= 0 number of items, < 0 error * Possible errors: * ISO_NULL_POINTER, if dir is NULL */ int iso_dir_get_children_count(IsoDir *dir) { if (dir == NULL) { return ISO_NULL_POINTER; } return dir->nchildren; } static int iter_next(IsoDirIter *iter, IsoNode **node) { struct dir_iter_data *data; if (iter == NULL || node == NULL) { return ISO_NULL_POINTER; } data = iter->data; /* clear next flag */ data->flag &= ~0x01; if (data->pos == NULL) { /* we are at the beginning */ data->pos = iter->dir->children; if (data->pos == NULL) { /* empty dir */ *node = NULL; return 0; } } else { if (data->pos->parent != iter->dir) { /* this can happen if the node has been moved to another dir */ /* TODO specific error */ return ISO_ERROR; } if (data->pos->next == NULL) { /* no more children */ *node = NULL; return 0; } else { /* free reference to current position */ iso_node_unref(data->pos); /* it is never last ref!! */ /* advance a position */ data->pos = data->pos->next; } } /* ok, take a ref to the current position, to prevent internal errors * if deleted somewhere */ iso_node_ref(data->pos); data->flag |= 0x01; /* set next flag */ /* return pointed node */ *node = data->pos; return ISO_SUCCESS; } /** * Check if there're more children. * * @return * 1 dir has more elements, 0 no, < 0 error * Possible errors: * ISO_NULL_POINTER, if iter is NULL */ static int iter_has_next(IsoDirIter *iter) { struct dir_iter_data *data; if (iter == NULL) { return ISO_NULL_POINTER; } data = iter->data; if (data->pos == NULL) { return iter->dir->children == NULL ? 0 : 1; } else { return data->pos->next == NULL ? 0 : 1; } } static void iter_free(IsoDirIter *iter) { struct dir_iter_data *data; data = iter->data; if (data->pos != NULL) { iso_node_unref(data->pos); } free(data); } static IsoNode** iso_dir_find_node(IsoDir *dir, IsoNode *node) { IsoNode **pos; pos = &(dir->children); while (*pos != NULL && *pos != node) { pos = &((*pos)->next); } return pos; } /** * Removes a child from a directory. * The child is not freed, so you will become the owner of the node. Later * you can add the node to another dir (calling iso_dir_add_node), or free * it if you don't need it (with iso_node_unref). * * @return * 1 on success, < 0 error */ int iso_node_take(IsoNode *node) { IsoNode **pos; IsoDir* dir; if (node == NULL) { return ISO_NULL_POINTER; } dir = node->parent; if (dir == NULL) { return ISO_NODE_NOT_ADDED_TO_DIR; } /* >>> Do not take root directory ! (dir == node) ? */; pos = iso_dir_find_node(dir, node); if (pos == NULL) { /* should never occur */ return ISO_ASSERT_FAILURE; } /* notify iterators just before remove */ iso_notify_dir_iters(node, 0); *pos = node->next; node->parent = NULL; node->next = NULL; dir->nchildren--; return ISO_SUCCESS; } /** * Removes a child from a directory and free (unref) it. * If you want to keep the child alive, you need to iso_node_ref() it * before this call, but in that case iso_node_take() is a better * alternative. * * @return * 1 on success, < 0 error */ int iso_node_remove(IsoNode *node) { int ret; ret = iso_node_take(node); if (ret == ISO_SUCCESS) { iso_node_unref(node); } return ret; } /* API */ int iso_node_remove_tree(IsoNode *node, IsoDirIter *boss_iter) { IsoDirIter *iter = NULL; IsoNode *sub_node; int ret; if (node->type != LIBISO_DIR) { /* >>> Do not remove root directory ! (node->parent == node) ? */; ret = iso_dir_get_children((IsoDir *) node, &iter); if (ret < 0) goto ex; while(1) { ret = iso_dir_iter_next(iter, &sub_node); if (ret == 0) break; ret = iso_node_remove_tree(sub_node, iter); if (ret < 0) goto ex; } if (node->parent == NULL) { /* node is not grafted into a boss directory */ iso_node_unref(node); goto ex; } } if (boss_iter != NULL) ret = iso_dir_iter_remove(boss_iter); else ret = iso_node_remove(node); ex:; if (iter != NULL) iso_dir_iter_free(iter); return ret; } /* * Get the parent of the given iso tree node. No extra ref is added to the * returned directory, you must take your ref. with iso_node_ref() if you * need it. * * If node is the root node, the same node will be returned as its parent. * * This returns NULL if the node doesn't pertain to any tree * (it was removed/take). */ IsoDir *iso_node_get_parent(IsoNode *node) { return node->parent; } /* TODO #00005 optimize iso_dir_iter_take */ static int iter_take(IsoDirIter *iter) { struct dir_iter_data *data; if (iter == NULL) { return ISO_NULL_POINTER; } data = iter->data; if (!(data->flag & 0x01)) { return ISO_ERROR; /* next not called or end of dir */ } if (data->pos == NULL) { return ISO_ASSERT_FAILURE; } /* clear next flag */ data->flag &= ~0x01; return iso_node_take(data->pos); } static int iter_remove(IsoDirIter *iter) { int ret; IsoNode *pos; struct dir_iter_data *data; if (iter == NULL) { return ISO_NULL_POINTER; } data = iter->data; pos = data->pos; ret = iter_take(iter); if (ret == ISO_SUCCESS) { /* remove node */ iso_node_unref(pos); } return ret; } void iter_notify_child_taken(IsoDirIter *iter, IsoNode *node) { IsoNode *pos, *pre; struct dir_iter_data *data; data = iter->data; if (data->pos == node) { pos = iter->dir->children; pre = NULL; while (pos != NULL && pos != data->pos) { pre = pos; pos = pos->next; } if (pos == NULL || pos != data->pos) { return; } /* dispose iterator reference */ iso_node_unref(data->pos); if (pre == NULL) { /* node is a first position */ iter->dir->children = pos->next; data->pos = NULL; } else { pre->next = pos->next; data->pos = pre; iso_node_ref(pre); /* take iter ref */ } } } static struct iso_dir_iter_iface iter_class = { iter_next, iter_has_next, iter_free, iter_take, iter_remove, iter_notify_child_taken }; int iso_dir_get_children(const IsoDir *dir, IsoDirIter **iter) { IsoDirIter *it; struct dir_iter_data *data; if (dir == NULL || iter == NULL) { return ISO_NULL_POINTER; } it = malloc(sizeof(IsoDirIter)); if (it == NULL) { return ISO_OUT_OF_MEM; } data = malloc(sizeof(struct dir_iter_data)); if (data == NULL) { free(it); return ISO_OUT_OF_MEM; } it->class = &iter_class; it->dir = (IsoDir*)dir; data->pos = NULL; data->flag = 0x00; it->data = data; if (iso_dir_iter_register(it) < 0) { free(it); return ISO_OUT_OF_MEM; } iso_node_ref((IsoNode*)dir); /* tak a ref to the dir */ *iter = it; return ISO_SUCCESS; } int iso_dir_iter_next(IsoDirIter *iter, IsoNode **node) { if (iter == NULL || node == NULL) { return ISO_NULL_POINTER; } return iter->class->next(iter, node); } int iso_dir_iter_has_next(IsoDirIter *iter) { if (iter == NULL) { return ISO_NULL_POINTER; } return iter->class->has_next(iter); } void iso_dir_iter_free(IsoDirIter *iter) { if (iter != NULL) { iso_dir_iter_unregister(iter); iter->class->free(iter); iso_node_unref((IsoNode*)iter->dir); free(iter); } } int iso_dir_iter_take(IsoDirIter *iter) { if (iter == NULL) { return ISO_NULL_POINTER; } return iter->class->take(iter); } int iso_dir_iter_remove(IsoDirIter *iter) { if (iter == NULL) { return ISO_NULL_POINTER; } return iter->class->remove(iter); } /** * Get the destination of a node. * The returned string belongs to the node and should not be modified nor * freed. Use strdup if you really need your own copy. */ const char *iso_symlink_get_dest(const IsoSymlink *link) { return link->dest; } /** * Set the destination of a link. */ int iso_symlink_set_dest(IsoSymlink *link, const char *dest) { char *d; int ret; ret = iso_node_is_valid_link_dest(dest); if (ret < 0) return ret; d = strdup(dest); if (d == NULL) { return ISO_OUT_OF_MEM; } free(link->dest); link->dest = d; return ISO_SUCCESS; } /** * Sets the order in which a node will be written on image. High weihted files * will be written first, so in a disc them will be written near the center. * * @param node * The node which weight will be changed. If it's a dir, this function * will change the weight of all its children. For nodes other that dirs * or regular files, this function has no effect. * @param w * The weight as a integer number, the greater this value is, the * closer from the beginning of image the file will be written. */ void iso_node_set_sort_weight(IsoNode *node, int w) { if (node->type == LIBISO_DIR) { IsoNode *child = ((IsoDir*)node)->children; while (child) { iso_node_set_sort_weight(child, w); child = child->next; } } else if (node->type == LIBISO_FILE) { ((IsoFile*)node)->sort_weight = w; ((IsoFile*)node)->explicit_weight = 1; } } /** * Get the sort weight of a file. */ int iso_file_get_sort_weight(IsoFile *file) { return file->sort_weight; } /** * Get the size of the file, in bytes */ off_t iso_file_get_size(IsoFile *file) { return iso_stream_get_size(file->stream); } /** * Get the IsoStream that represents the contents of the given IsoFile. * * If you open() the stream, it should be close() before image generation. * * @return * The IsoStream. No extra ref is added, so the IsoStream belong to the * IsoFile, and it may be freed together with it. Add your own ref with * iso_stream_ref() if you need it. * * @since 0.6.4 */ IsoStream *iso_file_get_stream(IsoFile *file) { return file->stream; } /** * Get the device id (major/minor numbers) of the given block or * character device file. The result is undefined for other kind * of special files, of first be sure iso_node_get_mode() returns either * S_IFBLK or S_IFCHR. * * @since 0.6.6 */ dev_t iso_special_get_dev(IsoSpecial *special) { return special->dev; } /** * Get the block lba of a file node, if it was imported from an old image. * * @param file * The file * @param lba * Will be filled with the kba * @param flag * Reserved for future usage, submit 0 * @return * 1 if lba is valid (file comes from old image), 0 if file was newly * added, i.e. it does not come from an old image, < 0 error * * @since 0.6.4 */ int iso_file_get_old_image_lba(IsoFile *file, uint32_t *lba, int flag) { int ret; int section_count; struct iso_file_section *sections = NULL; if (file == NULL || lba == NULL) { return ISO_NULL_POINTER; } ret = iso_file_get_old_image_sections(file, §ion_count, §ions, 0); if (ret <= 0) return ret; if (section_count != 1) { if (sections != NULL) free(sections); return ISO_WRONG_ARG_VALUE; } *lba = sections[0].block; free(sections); return 1; } /* * Like iso_file_get_old_image_lba(), but take an IsoNode. * * @return * 1 if lba is valid (file comes from old image), 0 if file was newly * added, i.e. it does not come from an old image, 2 node type has no * LBA (no regular file), < 0 error * * @since 0.6.4 */ int iso_node_get_old_image_lba(IsoNode *node, uint32_t *lba, int flag) { if (node == NULL) { return ISO_NULL_POINTER; } if (ISO_NODE_IS_FILE(node)) { return iso_file_get_old_image_lba((IsoFile*)node, lba, flag); } else { return 2; } } /** * Check if a given name is valid for an iso node. * * @return * 1 if yes, 0 if not */ int iso_node_is_valid_name(const char *name) { /* a name can't be NULL */ if (name == NULL) { return ISO_NULL_POINTER; } /* guard against the empty string or big names... */ if (name[0] == '\0') goto rr_reserved; if (strlen(name) > LIBISOFS_NODE_NAME_MAX) return ISO_RR_NAME_TOO_LONG; /* ...against "." and ".." names... */ if (!strcmp(name, ".") || !strcmp(name, "..")) goto rr_reserved; /* ...and against names with '/' */ if (strchr(name, '/') != NULL) goto rr_reserved; return 1; rr_reserved:; /* # define Libisofs_debug_rr_reserveD */ #ifdef Libisofs_debug_rr_reserveD fprintf(stderr, "libisofs_DEBUG: ISO_RR_NAME_RESERVED with '%s'\n", name); #endif return ISO_RR_NAME_RESERVED; } /** * Check if a given path is valid for the destination of a link. * * @return * 1 if yes, 0 if not */ int iso_node_is_valid_link_dest(const char *dest) { int ret; char *ptr, *brk_info, *component; /* a dest can't be NULL */ if (dest == NULL) { return ISO_NULL_POINTER; } /* guard against the empty string or big dest... */ if (dest[0] == '\0') { #ifdef Libisofs_debug_rr_reserveD fprintf(stderr, "libisofs_DEBUG: ISO_RR_NAME_RESERVED by empty link target\n"); #endif return ISO_RR_NAME_RESERVED; } if (strlen(dest) > LIBISOFS_NODE_PATH_MAX) return ISO_RR_PATH_TOO_LONG; /* check that all components are valid */ if (!strcmp(dest, "/")) { /* "/" is a valid component */ return 1; } ptr = strdup(dest); if (ptr == NULL) { return ISO_OUT_OF_MEM; } ret = 1; component = strtok_r(ptr, "/", &brk_info); while (component) { if (strcmp(component, ".") && strcmp(component, "..")) { ret = iso_node_is_valid_name(component); if (ret < 0) { break; } } component = strtok_r(NULL, "/", &brk_info); } free(ptr); return ret; } void iso_dir_find(IsoDir *dir, const char *name, IsoNode ***pos) { *pos = &(dir->children); while (**pos != NULL && strcmp((**pos)->name, name) < 0) { *pos = &((**pos)->next); } } int iso_dir_exists(IsoDir *dir, const char *name, IsoNode ***pos) { IsoNode **node; iso_dir_find(dir, name, &node); if (pos) { *pos = node; } return (*node != NULL && !strcmp((*node)->name, name)) ? 1 : 0; } int iso_dir_insert(IsoDir *dir, IsoNode *node, IsoNode **pos, enum iso_replace_mode replace) { if (*pos != NULL && !strcmp((*pos)->name, node->name)) { /* a node with same name already exists */ switch(replace) { case ISO_REPLACE_NEVER: return ISO_NODE_NAME_NOT_UNIQUE; case ISO_REPLACE_IF_NEWER: if ((*pos)->mtime >= node->mtime) { /* old file is newer */ return ISO_NODE_NAME_NOT_UNIQUE; } break; case ISO_REPLACE_IF_SAME_TYPE_AND_NEWER: if ((*pos)->mtime >= node->mtime) { /* old file is newer */ return ISO_NODE_NAME_NOT_UNIQUE; } if ((node->mode & S_IFMT) != ((*pos)->mode & S_IFMT)) { /* different file types */ return ISO_NODE_NAME_NOT_UNIQUE; } break; case ISO_REPLACE_IF_SAME_TYPE: if ((node->mode & S_IFMT) != ((*pos)->mode & S_IFMT)) { /* different file types */ return ISO_NODE_NAME_NOT_UNIQUE; } break; case ISO_REPLACE_ALWAYS: break; default: /* CAN'T HAPPEN */ return ISO_ASSERT_FAILURE; } /* if we are reach here we have to replace */ node->next = (*pos)->next; (*pos)->parent = NULL; (*pos)->next = NULL; iso_node_unref(*pos); *pos = node; node->parent = dir; return dir->nchildren; } node->next = *pos; *pos = node; node->parent = dir; return ++dir->nchildren; } /* iterators are stored in a linked list */ struct iter_reg_node { IsoDirIter *iter; struct iter_reg_node *next; }; /* list header */ static struct iter_reg_node *iter_reg = NULL; /** * Add a new iterator to the registry. The iterator register keeps track of * all iterators being used, and are notified when directory structure * changes. */ int iso_dir_iter_register(IsoDirIter *iter) { struct iter_reg_node *new; new = malloc(sizeof(struct iter_reg_node)); if (new == NULL) { return ISO_OUT_OF_MEM; } new->iter = iter; new->next = iter_reg; iter_reg = new; return ISO_SUCCESS; } /** * Unregister a directory iterator. */ void iso_dir_iter_unregister(IsoDirIter *iter) { struct iter_reg_node **pos; pos = &iter_reg; while (*pos != NULL && (*pos)->iter != iter) { pos = &(*pos)->next; } if (*pos) { struct iter_reg_node *tmp = (*pos)->next; free(*pos); *pos = tmp; } } void iso_notify_dir_iters(IsoNode *node, int flag) { struct iter_reg_node *pos = iter_reg; while (pos != NULL) { IsoDirIter *iter = pos->iter; if (iter->dir == node->parent) { iter->class->notify_child_taken(iter, node); } pos = pos->next; } } int iso_node_new_root(IsoDir **root) { IsoDir *dir; time_t now; dir = calloc(1, sizeof(IsoDir)); if (dir == NULL) { return ISO_OUT_OF_MEM; } dir->node.refcount = 1; dir->node.type = LIBISO_DIR; iso_nowtime(&now, 0); dir->node.atime = dir->node.ctime = dir->node.mtime = now; dir->node.mode = S_IFDIR | 0555; /* set parent to itself, to prevent root to be added to another dir */ dir->node.parent = dir; *root = dir; return ISO_SUCCESS; } int iso_node_new_dir(char *name, IsoDir **dir) { IsoDir *new; int ret; if (dir == NULL || name == NULL) { return ISO_NULL_POINTER; } /* check if the name is valid */ ret = iso_node_is_valid_name(name); if (ret < 0) return ret; new = calloc(1, sizeof(IsoDir)); if (new == NULL) { return ISO_OUT_OF_MEM; } new->node.refcount = 1; new->node.type = LIBISO_DIR; new->node.name = name; new->node.mode = S_IFDIR; *dir = new; return ISO_SUCCESS; } int iso_node_new_file(char *name, IsoStream *stream, IsoFile **file) { IsoFile *new; int ret; if (file == NULL || name == NULL || stream == NULL) { return ISO_NULL_POINTER; } /* check if the name is valid */ ret = iso_node_is_valid_name(name); if (ret < 0) return ret; new = calloc(1, sizeof(IsoFile)); if (new == NULL) { return ISO_OUT_OF_MEM; } new->node.refcount = 1; new->node.type = LIBISO_FILE; new->node.name = name; new->node.mode = S_IFREG; new->from_old_session = 0; new->explicit_weight = 0; new->sort_weight = 0; new->stream = stream; *file = new; return ISO_SUCCESS; } int iso_node_new_symlink(char *name, char *dest, IsoSymlink **link) { IsoSymlink *new; int ret; if (link == NULL || name == NULL || dest == NULL) { return ISO_NULL_POINTER; } /* check if the name is valid */ ret = iso_node_is_valid_name(name); if (ret < 0) return ret; /* check if destination is valid */ ret = iso_node_is_valid_link_dest(dest); if (ret < 0) return ret; new = calloc(1, sizeof(IsoSymlink)); if (new == NULL) { return ISO_OUT_OF_MEM; } new->node.refcount = 1; new->node.type = LIBISO_SYMLINK; new->node.name = name; new->dest = dest; new->node.mode = S_IFLNK; new->fs_id = 0; new->st_dev = 0; new->st_ino = 0; *link = new; return ISO_SUCCESS; } int iso_node_new_special(char *name, mode_t mode, dev_t dev, IsoSpecial **special) { IsoSpecial *new; int ret; if (special == NULL || name == NULL) { return ISO_NULL_POINTER; } if (S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)) { return ISO_WRONG_ARG_VALUE; } /* check if the name is valid */ ret = iso_node_is_valid_name(name); if (ret < 0) return ret; new = calloc(1, sizeof(IsoSpecial)); if (new == NULL) { return ISO_OUT_OF_MEM; } new->node.refcount = 1; new->node.type = LIBISO_SPECIAL; new->node.name = name; new->node.mode = mode; new->dev = dev; new->fs_id = 0; new->st_dev = 0; new->st_ino = 0; *special = new; return ISO_SUCCESS; } /* @param flag bit0= inverse: cleanout everything but del_name */ static int attrs_cleanout_name(char *del_name, size_t *num_attrs, char **names, size_t *value_lengths, char **values, int flag) { size_t i, w; for (w = i = 0; i < *num_attrs; i++) { if ((strcmp(names[i], del_name) == 0) ^ (flag & 1)) { if (names[i] != NULL) free(names[i]); if (values[i] != NULL) free(values[i]); names[i] = values[i] = NULL; continue; } if (w == i) { w++; continue; } names[w] = names[i]; value_lengths[w] = value_lengths[i]; values[w] = values[i]; names[i] = values[i] = NULL; value_lengths[i] = 0; w++; } *num_attrs = w; return 1; } /** * Backend of iso_node_get_attrs() with parameter node replaced by the * AAIP string from where to get the attribute list. * All other parameter specs apply. */ int iso_aa_get_attrs(unsigned char *aa_string, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag) { struct aaip_state *aaip= NULL; unsigned char *rpt; size_t len, todo, consumed; int is_done = 0, first_round= 1, ret; if (flag & (1 << 15)) aaip_get_decoded_attrs(&aaip, num_attrs, names, value_lengths, values, 1 << 15); *num_attrs = 0; *names = NULL; *value_lengths = NULL; *values = NULL; if (flag & (1 << 15)) return 1; rpt = aa_string; len = aaip_count_bytes(rpt, 0); while (!is_done) { todo = len - (rpt - aa_string); if (todo > 2048) todo = 2048; if (todo == 0) { /* Out of data while still prompted to submit */ ret = ISO_AAIP_BAD_AASTRING; goto ex; } /* Allow 1 million bytes of memory consumption, 100,000 attributes */ ret = aaip_decode_attrs(&aaip, (size_t) 1000000, (size_t) 100000, rpt, todo, &consumed, first_round); rpt+= consumed; first_round= 0; if (ret == 1) continue; if (ret == 2) break; /* aaip_decode_attrs() reports error */ ret = ISO_AAIP_BAD_AASTRING; goto ex; } if ((size_t) (rpt - aa_string) != len) { /* aaip_decode_attrs() returns 2 but still bytes are left */ ret = ISO_AAIP_BAD_AASTRING; goto ex; } ret = aaip_get_decoded_attrs(&aaip, num_attrs, names, value_lengths, values, 0); if (ret != 1) { /* aaip_get_decoded_attrs() failed */ ret = ISO_AAIP_BAD_AASTRING; goto ex; } if (!(flag & 1)) { /* Clean out eventual ACL attribute resp. all other xattr */ attrs_cleanout_name("", num_attrs, *names, *value_lengths, *values, !!(flag & 4)); } ret = 1; ex:; aaip_decode_attrs(&aaip, (size_t) 1000000, (size_t) 100000, rpt, todo, &consumed, 1 << 15); return ret; } /** * Search given name. Eventually calloc() and copy value. Add trailing 0 byte * for caller convenience. * * @return 1= found , 0= not found , <0 error */ int iso_aa_lookup_attr(unsigned char *aa_string, char *name, size_t *value_length, char **value, int flag) { size_t num_attrs = 0, *value_lengths = NULL; char **names = NULL, **values = NULL; int i, ret = 0, found = 0; ret = iso_aa_get_attrs(aa_string, &num_attrs, &names, &value_lengths, &values, 0); if (ret < 0) return ret; for (i = 0; i < (int) num_attrs; i++) { if (strcmp(names[i], name)) continue; *value_length = value_lengths[i]; *value = calloc(*value_length + 1, 1); if (*value == NULL) { found = ISO_OUT_OF_MEM; break; } if (*value_length > 0) memcpy(*value, values[i], *value_length); (*value)[*value_length] = 0; found = 1; break; } iso_aa_get_attrs(aa_string, &num_attrs, &names, &value_lengths, &values, 1 << 15); return found; } /* API */ int iso_node_lookup_attr(IsoNode *node, char *name, size_t *value_length, char **value, int flag) { void *xipt; unsigned char *aa_string = NULL; int ret; *value_length= 0; *value= NULL; ret = iso_node_get_xinfo(node, aaip_xinfo_func, &xipt); if (ret != 1) return 0; aa_string = (unsigned char *) xipt; ret = iso_aa_lookup_attr(aa_string, name, value_length, value, 0); return ret; } /* API */ int iso_node_get_attrs(IsoNode *node, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag) { void *xipt; unsigned char *aa_string = NULL; int ret; if (flag & (1 << 15)) { iso_aa_get_attrs(aa_string, num_attrs, names, value_lengths, values, 1 << 15); return 1; } *num_attrs = 0; *names = NULL; *value_lengths = NULL; *values = NULL; ret = iso_node_get_xinfo(node, aaip_xinfo_func, &xipt); if (ret != 1) return 1; aa_string = (unsigned char *) xipt; ret = iso_aa_get_attrs(aa_string, num_attrs, names, value_lengths, values, flag); return ret; } /* Enlarge attribute list */ static int attr_enlarge_list(char ***names, size_t **value_lengths, char ***values, size_t new_num, int flag) { void *newpt; newpt = realloc(*names, new_num * sizeof(char *)); if (newpt == NULL) return ISO_OUT_OF_MEM; *names = (char **) newpt; newpt = realloc(*values, new_num * sizeof(char *)); if (newpt == NULL) return ISO_OUT_OF_MEM; *values = (char **) newpt; newpt = realloc(*value_lengths, new_num * sizeof(size_t)); if (newpt == NULL) return ISO_OUT_OF_MEM; *value_lengths = (size_t *) newpt; return 1; } /* Merge attribute list of node and given new attribute list into attribute list returned by m_* parameters. The m_* parameters have finally to be freed by a call with bit15 set. @param flag Bitfield for control purposes bit0= delete all old names which begin by "user." (but not if bit2 is set) bit2= delete the given names rather than overwrite their content bit3= with bit0: delete all old non-"isofs." names bit4= do not overwrite value of empty name bit5= do not overwrite isofs attributes bit15= release memory and return 1 */ static int iso_node_merge_xattr(IsoNode *node, size_t num_attrs, char **names, size_t *value_lengths, char **values, size_t *m_num_attrs, char ***m_names, size_t **m_value_lengths, char ***m_values, int flag) { int ret; size_t new_names = 0, deleted = 0, i, j, w; if (flag & (1 << 15)) { iso_node_get_attrs(node, m_num_attrs, m_names, m_value_lengths, m_values, 1 << 15); return 1; } ret = iso_node_get_attrs(node, m_num_attrs, m_names, m_value_lengths, m_values, 1); if (ret < 0) return ret; if ((flag & 1) && (!(flag & 4))) { /* Delete unmatched settable pairs */ for (j = 0; j < *m_num_attrs; j++) { if (strncmp((*m_names)[j], "isofs.", 6) == 0) continue; if (strncmp((*m_names)[j], "user.", 5) != 0 && !(flag & 8)) continue; for (i = 0; i < num_attrs; i++) { if (names[i] == NULL || (*m_names)[j] == NULL) continue; if (strcmp(names[i], (*m_names)[j]) == 0) break; } if (i >= num_attrs) { /* Delete unmatched pair */ free((*m_names)[j]); (*m_names)[j] = NULL; deleted++; } } } /* Handle existing names, count non-existing names */ for (i = 0; i < num_attrs; i++) { if (names[i] == NULL) continue; if (names[i][0] == 0 && (flag & 16)) continue; if ((flag & 32) && strncmp(names[i], "isofs.", 6) == 0) continue; for (j = 0; j < *m_num_attrs; j++) { if ((*m_names)[j] == NULL) continue; if (strcmp(names[i], (*m_names)[j]) == 0) { if ((*m_values)[j] != NULL) free((*m_values)[j]); (*m_values)[j] = NULL; (*m_value_lengths)[j] = 0; if (flag & 4) { /* Delete pair */ free((*m_names)[j]); (*m_names)[j] = NULL; deleted++; } else { (*m_values)[j] = calloc(value_lengths[i] + 1, 1); if ((*m_values)[j] == NULL) return ISO_OUT_OF_MEM; memcpy((*m_values)[j], values[i], value_lengths[i]); (*m_values)[j][value_lengths[i]] = 0; (*m_value_lengths)[j] = value_lengths[i]; } break; } } if (j >= *m_num_attrs) new_names++; } if (new_names > 0 && (flag & 4)) { /* >>> warn of non-existing name on delete ? */; } else if (new_names > 0) { ret = attr_enlarge_list(m_names, m_value_lengths, m_values, *m_num_attrs + new_names, 0); if (ret < 0) return ret; /* Set new pairs */; w = *m_num_attrs; for (i = 0; i < num_attrs; i++) { if (names[i] == NULL) continue; if (names[i][0] == 0 && (flag & 16)) continue; if ((flag & 32) && strncmp(names[i], "isofs.", 6) == 0) continue; for (j = 0; j < *m_num_attrs; j++) { if ((*m_names)[j] == NULL) continue; if (strcmp(names[i], (*m_names)[j]) == 0) continue; } if (j < *m_num_attrs) /* Name is not new */ continue; (*m_names)[w] = strdup(names[i]); if ((*m_names)[w] == NULL) return ISO_OUT_OF_MEM; (*m_values)[w] = calloc(value_lengths[i] + 1, 1); if ((*m_values)[w] == NULL) return ISO_OUT_OF_MEM; memcpy((*m_values)[w], values[i], value_lengths[i]); (*m_values)[w][value_lengths[i]] = 0; (*m_value_lengths)[w] = value_lengths[i]; w++; } *m_num_attrs = w; } if (deleted > 0) { /* Garbage collection */ w = 0; for (j = 0; j < *m_num_attrs; j++) { if ((*m_names)[j] == NULL) continue; (*m_names)[w] = (*m_names)[j]; (*m_values)[w] = (*m_values)[j]; (*m_value_lengths)[w] = (*m_value_lengths)[j]; w++; } *m_num_attrs = w; } return 1; } int iso_node_set_attrs(IsoNode *node, size_t num_attrs, char **names, size_t *value_lengths, char **values, int flag) { int ret, acl_saved = 0; ssize_t sret; size_t result_len, m_num = 0, *m_value_lengths = NULL, i; unsigned char *result = NULL; char *a_acl = NULL, *d_acl = NULL, **m_names = NULL, **m_values = NULL; if (!(flag & 8)) for (i = 0; i < num_attrs; i++) if (strncmp(names[i], "user.", 5) != 0 && names[i][0] != 0) return ISO_AAIP_NON_USER_NAME; if ((flag & (2 | 4 | 16)) || !(flag & 8)) { /* Merge old and new lists */ ret = iso_node_merge_xattr( node, num_attrs, names, value_lengths, values, &m_num, &m_names, &m_value_lengths, &m_values, (flag & 4) | (!(flag & 2)) | ((!(flag & 1)) << 4) | ((flag & 16) << 1) | (flag & 8)); if (ret < 0) goto ex; num_attrs = m_num; names = m_names; value_lengths = m_value_lengths; values = m_values; } else if (!(flag & 1)) { iso_node_get_acl_text(node, &a_acl, &d_acl, 16); acl_saved = 1; } if (num_attrs == 0) { ret = iso_node_remove_xinfo(node, aaip_xinfo_func); if (ret < 0) goto ex; if (acl_saved && (a_acl != NULL || d_acl != NULL)) { ret = iso_node_set_acl_text(node, a_acl, d_acl, 0); if (ret < 0) goto ex; } ret = 1; goto ex; } sret = aaip_encode(num_attrs, names, value_lengths, values, &result_len, &result, 0); if (sret < 0) { ret = sret; goto ex; } ret = iso_node_remove_xinfo(node, aaip_xinfo_func); if (ret < 0) { if (result != NULL) free(result); goto ex; } if (sret > 0) { ret = iso_node_add_xinfo(node, aaip_xinfo_func, result); if (ret < 0) goto ex; if (ret == 0) { /* >>> something is messed up with xinfo: an aa_string still exists */; ret = ISO_ERROR; goto ex; } if (acl_saved) { ret = iso_node_set_acl_text(node, a_acl, d_acl, 0); if (ret < 0) goto ex; } } ret = 1; ex:; /* Dispose merged list if it was created */ iso_node_merge_xattr(node, num_attrs, names, value_lengths, values, &m_num, &m_names, &m_value_lengths, &m_values, 1 << 15); /* Dispose ACL if saved */ iso_node_get_acl_text(node, &a_acl, &d_acl, 1 << 15); return ret; } /* @param flag bit0= delete ACL, too bit1= delete file attribute flags (isofs.fa), too */ int iso_node_remove_fattr(IsoNode *node, int flag) { int ret; size_t num_attrs, *value_lengths = NULL, i, w; char **names = NULL, **values = NULL; ret = iso_node_get_attrs(node, &num_attrs, &names, &value_lengths, &values, flag & 1); if (ret < 0) goto ex; /* Delete variables of all namespaces except isofs */ w = 0; for (i = 0; i < num_attrs; i++) { if (strncmp(names[i], "isofs.", 6) != 0 || ((flag & 2) && strcmp(names[i], "isofs.fa") == 0)) { free(names[i]); names[i] = NULL; free(values[i]); values[i] = NULL; continue; } if (w != i) { /* move i to w , nullify i */ names[w] = names[i]; names[i] = NULL; values[w] = values[i]; values[i] = NULL; value_lengths[w] = value_lengths[i]; } w++; } num_attrs = w; ret = iso_node_set_attrs(node, num_attrs, names, value_lengths, values, (flag & 1) | 8); ex:; if (names != NULL) iso_node_get_attrs(NULL, &num_attrs, &names, &value_lengths, &values, 1 << 15); return ret; } static int iso_decode_acl(unsigned char *v_data, size_t v_len, size_t *consumed, char **text, size_t *text_fill, int flag) { int ret; *text= NULL; ret = aaip_decode_acl(v_data, v_len, consumed, NULL, (size_t) 0, text_fill, 1); if (ret <= 0) return 0; if (*text_fill == 0) return ret; *text = calloc(*text_fill + 42, 1); /* 42 for aaip_update_acl_st_mode */ if (*text == NULL) return ISO_OUT_OF_MEM; ret = aaip_decode_acl(v_data, v_len, consumed, *text, *text_fill, text_fill, 0); if (ret <= 0) { free(*text); *text= NULL; return 0; } return ret; } /** * Backend of iso_node_get_acl_text() with parameter node replaced by the * attribute list from where to get the ACL and by the associated st_mode * permission bits. All other parameter specs apply. */ static int iso_attr_get_acl_text(size_t num_attrs, char **names, size_t *value_lengths, char **values, mode_t st_mode, char **access_text, char **default_text, int flag) { size_t i, consumed, text_fill = 0; size_t v_len; unsigned char *v_data; int ret, from_posix= 0; if (flag & (1 << 15)) { if (*access_text != NULL) free(*access_text); *access_text = NULL; if (*default_text != NULL) free(*default_text); *default_text = NULL; return 1; } *access_text = *default_text = NULL; for(i = 0; i < num_attrs; i++) { if (names[i][0]) /* searching the empty name */ continue; v_data = (unsigned char *) values[i]; v_len = value_lengths[i]; /* "access" ACL */ ret = iso_decode_acl(v_data, v_len, &consumed, access_text, &text_fill, 0); if (ret <= 0) goto bad_decode; if (ret == 2) { v_data += consumed; v_len -= consumed; ret = iso_decode_acl(v_data, v_len, &consumed, default_text, &text_fill, 0); if (ret == 0) goto bad_decode; } break; } if (*access_text == NULL && !(flag & 16)) { from_posix = 1; *access_text = calloc(42, 1); /* 42 for aaip_update_acl_st_mode */ } if (*access_text != NULL) { aaip_add_acl_st_mode(*access_text, st_mode, 0); text_fill = strlen(*access_text); } if (*access_text == NULL && *default_text == NULL) ret = 0; else ret = 1 + from_posix; ex:; return ret; bad_decode:; ret = ISO_AAIP_BAD_ACL; goto ex; } int iso_node_get_acl_text(IsoNode *node, char **access_text, char **default_text, int flag) { size_t num_attrs = 0, *value_lengths = NULL; char **names = NULL, **values = NULL; mode_t st_mode = 0; int ret; if (flag & (1 << 15)) { iso_attr_get_acl_text(num_attrs, names, value_lengths, values, st_mode, access_text, default_text, 1 << 15); return 1; } ret = iso_node_get_attrs(node, &num_attrs, &names, &value_lengths, &values, 1); if (ret < 0) return ret; st_mode = iso_node_get_permissions(node); ret = iso_attr_get_acl_text(num_attrs, names, value_lengths, values, st_mode, access_text, default_text, flag); iso_node_get_attrs(node, &num_attrs, &names, &value_lengths, &values, 1 << 15); /* free memory */ return ret; } int iso_aa_get_acl_text(unsigned char *aa_string, mode_t st_mode, char **access_text, char **default_text, int flag) { int ret; size_t num_attrs = 0, *value_lengths = NULL; char **names = NULL, **values = NULL; if (flag & (1 << 15)) { iso_attr_get_acl_text(num_attrs, names, value_lengths, values, st_mode, access_text, default_text, 1 << 15); return 1; } ret = iso_aa_get_attrs(aa_string, &num_attrs, &names, &value_lengths, &values, 1); if (ret < 0) goto ex; ret = iso_attr_get_acl_text(num_attrs, names, value_lengths, values, st_mode, access_text, default_text, flag); ex:; iso_aa_get_attrs(aa_string, &num_attrs, &names, &value_lengths, &values, 1 << 15); return ret; } int iso_node_set_acl_text(IsoNode *node, char *access_text, char *default_text, int flag) { size_t num_attrs = 0, *value_lengths = NULL, i, j, consumed; size_t a_text_fill = 0, d_text_fill = 0; size_t v_len, acl_len= 0; char **names = NULL, **values = NULL, *a_text = NULL, *d_text = NULL; unsigned char *v_data, *acl= NULL; int ret; mode_t st_mode; st_mode = iso_node_get_permissions(node); if (!(flag & 2)) { /* want not to update ACL by st_mode */ /* >>> validate and rectify text */; } ret = iso_node_get_attrs(node, &num_attrs, &names, &value_lengths, &values, 1); if (ret < 0) return ret; for(i = 0; i < num_attrs; i++) { if (names[i][0]) /* searching the empty name */ continue; v_data = (unsigned char *) values[i]; v_len = value_lengths[i]; if (flag & 2) { /* update "access" ACL by st_mode */ /* read "access" ACL */ ret = iso_decode_acl(v_data, v_len, &consumed, &a_text, &a_text_fill, 0); if (ret == 0) goto bad_decode; if (ret < 0) goto ex; if (ret == 2) { /* read "default" ACL */ v_data += consumed; v_len -= consumed; ret = iso_decode_acl(v_data, v_len, &consumed, &d_text, &d_text_fill, 0); if (ret == 0) goto bad_decode; if (ret < 0) goto ex; } /* Update "access" ACL by st_mode */ if (a_text == NULL) { ret = 1; goto ex; } ret = aaip_cleanout_st_mode(a_text, &st_mode, 8); if (ret < 0) { ret = ISO_AAIP_BAD_ACL_TEXT; goto ex; } ret = aaip_encode_both_acl(a_text, d_text, st_mode, &acl_len, &acl, 2 | 8 | ((flag & 4) << 2)); } else { ret = 1; if (access_text != NULL || default_text != NULL) ret = aaip_encode_both_acl(access_text, default_text, st_mode, &acl_len, &acl, 2 | 8 | ((flag & 4) << 2)); } if (ret == -1) ret = ISO_OUT_OF_MEM; else if (ret <= 0 && ret >= -3) ret = ISO_AAIP_BAD_ACL_TEXT; if (ret <= 0) goto ex; if(acl == NULL) { /* Delete whole ACL attribute */ /* Update S_IRWXG by eventual "group::" ACL entry. With ACL it reflected the "mask::" entry. */ if (a_text != NULL) free(a_text); ret = iso_decode_acl(v_data, v_len, &consumed, &a_text, &a_text_fill, 0); if (ret == 0) goto bad_decode; if (ret < 0) goto ex; ret = aaip_cleanout_st_mode(a_text, &st_mode, 4 | 16); if (ret < 0) goto ex; iso_node_set_perms_internal(node, st_mode, 1); /* Delete the attribute pair */ if (values[i] != NULL) free(values[i]); for (j = i + 1; j < num_attrs; j++) { names[j - 1] = names[j]; value_lengths[j - 1] = value_lengths[j]; values[j - 1] = values[j]; } num_attrs--; } else { /* replace variable value */; if (values[i] != NULL) free(values[i]); values[i] = (char *) acl; acl = NULL; value_lengths[i] = acl_len; } /* Encode attributes and attach to node */ ret = iso_node_set_attrs(node, num_attrs, names, value_lengths, values, 1 | 8); if (ret <= 0) goto ex; goto update_perms; } /* There is no ACL yet */ if ((flag & 2) || (access_text == NULL && default_text == NULL)) { /* thus no need to update ACL by st_mode or to delete ACL */ ret = 1; goto ex; } ret = aaip_encode_both_acl(access_text, default_text, st_mode, &acl_len, &acl, 2 | 8 | ((flag & 4) << 2)); if (ret < -3) goto ex; if (ret <= 0) { ret = ISO_AAIP_BAD_ACL_TEXT; goto ex; } ret = attr_enlarge_list(&names, &value_lengths, &values, num_attrs + 1, 0); if (ret < 0) goto ex; /* Set new ACL attribute */ names[num_attrs] = strdup(""); if (names[num_attrs] == NULL) { ret = ISO_OUT_OF_MEM; goto ex; } values[num_attrs] = (char *) acl; acl = NULL; value_lengths[num_attrs] = acl_len; num_attrs++; /* Encode attributes and attach to node */ ret = iso_node_set_attrs(node, num_attrs, names, value_lengths, values, 1 | 8); if (ret < 0) goto ex; update_perms:; if(access_text != NULL && !(flag & (1 | 2))) { /* Update node permissions by acl_text */ st_mode = iso_node_get_permissions(node); ret = aaip_cleanout_st_mode(access_text, &st_mode, 4); if (ret < 0) { ret = ISO_AAIP_BAD_ACL_TEXT; goto ex; } iso_node_set_perms_internal(node, st_mode, 1); } ret = 1; ex:; iso_node_get_attrs(node, &num_attrs, &names, &value_lengths, &values, 1 << 15); /* free memory */ if (a_text != NULL) free(a_text); if (d_text != NULL) free(d_text); if(acl != NULL) free(acl); return ret; bad_decode:; ret = ISO_AAIP_BAD_ACL; goto ex; } mode_t iso_node_get_perms_wo_acl(const IsoNode *node) { mode_t st_mode; int ret; char *a_text = NULL, *d_text = NULL; st_mode = iso_node_get_permissions(node); ret = iso_node_get_acl_text((IsoNode *) node, &a_text, &d_text, 16); if (ret != 1) goto ex; aaip_cleanout_st_mode(a_text, &st_mode, 4 | 16); ex:; iso_node_get_acl_text((IsoNode *) node, &a_text, &d_text, 1 << 15); return st_mode; } int iso_node_set_lfa_flags(IsoNode *node, uint64_t lfa_flags, int flag) { static char *names = "isofs.fa"; static size_t value_lengths[1]; unsigned char value[8]; char *valuept; int ret, l; ret = aaip_encode_lfa_flags(lfa_flags, value, &l, 0); if (ret < 0) return ret; value_lengths[0] = l; valuept= (char *) value; ret = iso_node_set_attrs(node, (size_t) 1, &names, value_lengths, &valuept, 2 | 8); return ret; } int iso_node_get_lfa_flags(IsoNode *node, uint64_t *lfa_flags, int *max_bit, int flag) { int ret, i; size_t value_len; char *value = NULL; *lfa_flags = 0; *max_bit = -1; ret = iso_node_lookup_attr(node, "isofs.fa", &value_len, &value, 0); if (ret <= 0) return ret; if (value_len <= 0) { *max_bit = 7; return 1; } if (value_len > 8) { value += value_len - 8; value_len = 8; } for (i = 0; i < (int) value_len; i++) *lfa_flags = (*lfa_flags << 8) | ((unsigned char *) value)[i]; *max_bit = value_len * 8 - 1; return 1; } /* Function to identify and manage ZF parameters. * data is supposed to be a pointer to struct zisofs_zf_info */ int zisofs_zf_xinfo_func(void *data, int flag) { if (flag & 1) { free(data); } return 1; } /* The iso_node_xinfo_cloner function which gets associated to * zisofs_zf_xinfo_func by iso_init() resp. iso_init_with_flag() via * iso_node_xinfo_make_clonable() */ int zisofs_zf_xinfo_cloner(void *old_data, void **new_data, int flag) { *new_data = NULL; if (flag) return ISO_XINFO_NO_CLONE; if (old_data == NULL) return 0; *new_data = calloc(1, sizeof(struct zisofs_zf_info)); if (*new_data == NULL) return ISO_OUT_OF_MEM; memcpy(*new_data, old_data, sizeof(struct zisofs_zf_info)); return (int) sizeof(struct zisofs_zf_info); } /* Checks whether a file effectively bears a zisofs file header and eventually * marks this by a struct zisofs_zf_info as xinfo of the file node. * @param flag bit0= inquire the most original stream of the file * bit1= permission to overwrite existing zisofs_zf_info * bit2= if no zisofs header is found: * create xinfo with parameters which indicate no zisofs * bit8-bit15= maximum zisofs version to be recognized (0 means 1) * @return 1= zf xinfo added, 0= no zisofs data found , * 2= found existing zf xinfo and flag bit1 was not set * <0 means error */ int iso_file_zf_by_magic(IsoFile *file, int flag) { int ret, stream_type, header_size_div4, block_size_log2, version; uint64_t uncompressed_size; IsoStream *stream, *input_stream; struct zisofs_zf_info *zf = NULL; void *xipt; uint8_t algo[2]; /* Intimate friendship with this function in filters/zisofs.c */ int ziso_is_zisofs_stream(IsoStream *stream, int *stream_type, uint8_t zisofs_algo[2], int *header_size_div4, int *block_size_log2, uint64_t *uncompressed_size, int flag); ret = iso_node_get_xinfo((IsoNode *) file, zisofs_zf_xinfo_func, &xipt); if (ret == 1) { if (!(flag & 2)) return 2; ret = iso_node_remove_xinfo((IsoNode *) file, zisofs_zf_xinfo_func); if (ret < 0) return ret; } input_stream = stream = iso_file_get_stream(file); while (flag & 1) { input_stream = iso_stream_get_input_stream(stream, 0); if (input_stream == NULL) break; stream = input_stream; } version = ((flag >> 8) & 0xff); algo[0] = algo[1] = 0; ret = ziso_is_zisofs_stream(stream, &stream_type, algo, &header_size_div4, &block_size_log2, &uncompressed_size, 3); if (ret < 0) return ret; if (version < 2 && ret > 0 && (algo[0] != 'p' || algo[1] != 'z')) ret = 0; if (ret != 1 || stream_type != 2) { if (!(flag & 4)) return 0; algo[0] = algo[1] = 0; header_size_div4 = 0; block_size_log2 = 0; uncompressed_size = 0; } zf = calloc(1, sizeof(struct zisofs_zf_info)); if (zf == NULL) return ISO_OUT_OF_MEM; zf->zisofs_algo[0] = algo[0]; zf->zisofs_algo[1] = algo[1]; zf->uncompressed_size = uncompressed_size; zf->header_size_div4 = header_size_div4; zf->block_size_log2 = block_size_log2; ret = iso_node_add_xinfo((IsoNode *) file, zisofs_zf_xinfo_func, zf); return ret; } /* API */ int iso_node_zf_by_magic(IsoNode *node, int flag) { int ret = 1, total_ret = 0, hflag; IsoFile *file; IsoNode *pos; IsoDir *dir; if (node->type == LIBISO_FILE) return iso_file_zf_by_magic((IsoFile *) node, flag & 0xff06); if (node->type != LIBISO_DIR || (flag & 8)) return 0; dir = (IsoDir *) node; pos = dir->children; while (pos) { ret = 1; if (pos->type == LIBISO_FILE) { file = (IsoFile *) pos; if ((flag & 16) && file->from_old_session) return 0; if (!((flag & 1) && file->from_old_session)) { if (strncmp(file->stream->class->type, "ziso", 4) == 0) return 1; /* The stream is enough of marking */ if (strncmp(file->stream->class->type, "osiz", 4) == 0) { if (flag & 2) iso_node_remove_xinfo(pos, zisofs_zf_xinfo_func); return 0; /* Will not be zisofs format */ } } hflag = flag & 0xff06; if ((flag & 1) && file->from_old_session) hflag |= 1; ret = iso_file_zf_by_magic(file, hflag); } else if (pos->type == LIBISO_DIR) { ret = iso_node_zf_by_magic(pos, flag); } if (ret < 0) { total_ret = ret; ret = iso_msg_submit(-1, ret, 0, NULL); if (ret < 0) { return ret; /* cancel due error threshold */ } } else if (total_ret >= 0) { total_ret |= ret; } pos = pos->next; } return total_ret; } int iso_px_ino_xinfo_func(void *data, int flag) { if (flag == 1) { free(data); } return 1; } /* The iso_node_xinfo_cloner function which gets associated to * iso_px_ino_xinfo_func by iso_init() resp. iso_init_with_flag() via * iso_node_xinfo_make_clonable() */ int iso_px_ino_xinfo_cloner(void *old_data, void **new_data, int flag) { *new_data = NULL; if (flag) return ISO_XINFO_NO_CLONE; *new_data = calloc(1, sizeof(ino_t)); if (*new_data == NULL) return ISO_OUT_OF_MEM; memcpy(*new_data, old_data, sizeof(ino_t)); return (int) sizeof(ino_t); } /* * @param flag * bit0= do only retrieve id if node is in imported ISO image * or has an explicit xinfo inode number * @return * 1= reply is valid from stream, 2= reply is valid from xinfo * 0= no id available, <0= error * (fs_id, dev_id, ino_id) will be (0,0,0) in case of return <= 0 */ int iso_node_get_id(IsoNode *node, unsigned int *fs_id, dev_t *dev_id, ino_t *ino_id, int flag) { int ret; IsoFile *file; IsoSymlink *symlink; IsoSpecial *special; void *xipt; ret = iso_node_get_xinfo(node, iso_px_ino_xinfo_func, &xipt); if (ret < 0) goto no_id; if (ret == 1) { *fs_id = ISO_IMAGE_FS_ID; *dev_id = 0; *ino_id = *((ino_t *) xipt); return 2; } if (node->type == LIBISO_FILE) { file= (IsoFile *) node; iso_stream_get_id(file->stream, fs_id, dev_id, ino_id); if (*fs_id != ISO_IMAGE_FS_ID && (flag & 1)) { ret = 0; goto no_id; } return 1; } else if (node->type == LIBISO_SYMLINK) { symlink = (IsoSymlink *) node; if (symlink->fs_id != ISO_IMAGE_FS_ID && (flag & 1)) { ret = 0; goto no_id; } *fs_id = symlink->fs_id; *dev_id = symlink->st_dev; *ino_id = symlink->st_ino; return 1; } else if (node->type == LIBISO_SPECIAL) { special = (IsoSpecial *) node; if (special->fs_id != ISO_IMAGE_FS_ID && (flag & 1)) { ret = 0; goto no_id; } *fs_id = special->fs_id; *dev_id = special->st_dev; *ino_id = special->st_ino; return 1; } ret = 0; no_id:; *fs_id = 0; *dev_id = 0; *ino_id = 0; return ret; } static int iso_node_set_ino_xinfo(IsoNode *node, ino_t ino, int flag) { int ret; void *xipt; if (flag & 1) { ret = iso_node_remove_xinfo(node, iso_px_ino_xinfo_func); if (ret < 0) return ret; } xipt = calloc(1, sizeof(ino_t)); if (xipt == NULL) return ISO_OUT_OF_MEM; memcpy(xipt, &ino, sizeof(ino_t)); ret = iso_node_add_xinfo(node, iso_px_ino_xinfo_func, xipt); return ret; } int iso_node_set_ino(IsoNode *node, ino_t ino, int flag) { int ret; IsoFile *file; IsoSymlink *symlink; IsoSpecial *special; void *xipt; ret = iso_node_get_xinfo(node, iso_px_ino_xinfo_func, &xipt); if (ret < 0) return ret; if (ret == 1) { ret = iso_node_set_ino_xinfo(node, ino, 1); if (ret < 0) return ret; return 2; } if (node->type == LIBISO_FILE) { file= (IsoFile *) node; ret = iso_stream_set_image_ino(file->stream, ino, 0); if (ret < 0 || ret == 1) return ret; /* ret == 0 means that the stream is not from loaded ISO image */ } else if (node->type == LIBISO_SYMLINK) { symlink = (IsoSymlink *) node; if (symlink->fs_id == ISO_IMAGE_FS_ID) { symlink->st_ino = ino; return 1; } } else if (node->type == LIBISO_SPECIAL) { special = (IsoSpecial *) node; if (special->fs_id == ISO_IMAGE_FS_ID) { special->st_ino = ino; return 1; } } ret = iso_node_set_ino_xinfo(node, ino, 0); if (ret < 0) return ret; return 2; } int iso_node_set_unique_id(IsoNode *node, IsoImage *image, int flag) { int ret; ino_t ino; ino = img_give_ino_number(image, 0); ret = iso_node_set_ino(node, ino, 0); return ret; } /* * Note to programmers: It is crucial not to break the following constraints. * Anti-symmetry: cmp(X,Y) == - cmp(Y,X) * Transitivity : if cmp(A,B) < 0 && cmp(B,C) < 0 then cmp(A,C) < 0 * if cmp(A,B) == 0 && cmp(B,C) == 0 then cmp(A,C) == 0 * A big transitivity hazard are tests which do not apply to some nodes. * In this case for any A that is applicable and any B that is not applicable * the comparison must have the same non-zero result. I.e. a pair of applicable * and non-applicable node must return that non-zero result before the test * for a pair of applicable nodes would happen. * * @param flag * bit0= compare stat properties and attributes * bit1= treat all nodes with image ino == 0 as unique */ int iso_node_cmp_flag(IsoNode *n1, IsoNode *n2, int flag) { int ret1, ret2; unsigned int fs_id1, fs_id2; dev_t dev_id1, dev_id2; ino_t ino_id1, ino_id2; IsoFile *f1 = NULL, *f2 = NULL; IsoSymlink *l1 = NULL, *l2 = NULL; IsoSpecial *s1 = NULL, *s2 = NULL; void *x1, *x2; if (n1 == n2) return 0; if (n1->type != n2->type) return (n1->type < n2->type ? -1 : 1); /* Imported or explicit ISO image node id has priority */ ret1 = (iso_node_get_id(n1, &fs_id1, &dev_id1, &ino_id1, 1) > 0); ret2 = (iso_node_get_id(n2, &fs_id2, &dev_id2, &ino_id2, 1) > 0); if (ret1 != ret2) return (ret1 < ret2 ? -1 : 1); if (ret1) { /* fs_id and dev_id do not matter here. Both nodes have explicit inode numbers of the emerging image. */ if (ino_id1 != ino_id2) return (ino_id1 < ino_id2 ? -1 : 1); if (ino_id1 == 0) /* Image ino 0 is always unique */ return (n1 < n2 ? -1 : 1); goto image_inode_match; } if (n1->type == LIBISO_FILE) { f1 = (IsoFile *) n1; f2 = (IsoFile *) n2; ret1 = iso_stream_cmp_ino(f1->stream, f2->stream, 0); if (ret1) return ret1; goto inode_match; } else if (n1->type == LIBISO_SYMLINK) { l1 = (IsoSymlink *) n1; l2 = (IsoSymlink *) n2; fs_id1 = l1->fs_id; dev_id1 = l1->st_dev; ino_id1 = l1->st_ino; fs_id2 = l2->fs_id; dev_id2 = l2->st_dev; ino_id2 = l2->st_ino; } else if (n1->type == LIBISO_SPECIAL) { s1 = (IsoSpecial *) n1; s2 = (IsoSpecial *) n2; fs_id1 = s1->fs_id; dev_id1 = s1->st_dev; ino_id1 = s1->st_ino; fs_id2 = s2->fs_id; dev_id2 = s2->st_dev; ino_id2 = s2->st_ino; } else { return (n1 < n2 ? -1 : 1); /* case n1 == n2 is handled above */ } if (fs_id1 != fs_id2) return (fs_id1 < fs_id2 ? -1 : 1); if (dev_id1 != dev_id2) return (dev_id1 < dev_id2 ? -1 : 1); if (ino_id1 != ino_id2) return (ino_id1 < ino_id2 ? -1 : 1); if (fs_id1 == 0 && dev_id1 == 0 && ino_id1 == 0) return (n1 < n2 ? -1 : 1); inode_match:; if (flag & 2) { /* What comes here has no predefined image ino resp. image_ino == 0 . Regard this as not equal. */ return (n1 < n2 ? -1 : 1); } image_inode_match:; if (!(flag & 1)) return 0; if (n1->type == LIBISO_SYMLINK) { l1 = (IsoSymlink *) n1; l2 = (IsoSymlink *) n2; ret1 = strcmp(l1->dest, l2->dest); if (ret1) return ret1; } else if (n1->type == LIBISO_SPECIAL) { s1 = (IsoSpecial *) n1; s2 = (IsoSpecial *) n2; if (s1->dev != s2->dev) return (s1->dev < s2->dev ? -1 : 1); } if (n1->mode != n2->mode) return (n1->mode < n2->mode ? -1 : 1); if (n1->uid != n2->uid) return (n1->uid < n2->uid ? -1 : 1); if (n1->gid != n2->gid) return (n1->gid < n2->gid ? -1 : 1); if (n1->atime != n2->atime) return (n1->atime < n2->atime ? -1 : 1); if (n1->mtime != n2->mtime) return (n1->mtime < n2->mtime ? -1 : 1); if (n1->ctime != n2->ctime) return (n1->ctime < n2->ctime ? -1 : 1); /* Compare xinfo */ /* :( cannot compare general xinfo because data length is not known :( */ /* compare aa_string */ ret1 = iso_node_get_xinfo(n1, aaip_xinfo_func, &x1); ret2 = iso_node_get_xinfo(n2, aaip_xinfo_func, &x2); if (ret1 != ret2) return (ret1 < ret2 ? -1 : 1); if (ret1 == 1) { ret1 = aaip_count_bytes((unsigned char *) x1, 0); ret2 = aaip_count_bytes((unsigned char *) x2, 0); if (ret1 != ret2) return (ret1 < ret2 ? -1 : 1); ret1 = memcmp(x1, x2, ret1); if (ret1) return ret1; } return 0; } /* API */ int iso_node_cmp_ino(IsoNode *n1, IsoNode *n2, int flag) { return iso_node_cmp_flag(n1, n2, 1); } /* @param flag bit0= delete isofs.cx rather than setting it */ int iso_file_set_isofscx(IsoFile *file, unsigned int checksum_index, int flag) { static char *names = "isofs.cx"; static size_t value_lengths[1] = {4}; unsigned char value[4]; char *valuept; int i, ret; valuept= (char *) value; if (flag & 1) { ret = iso_node_set_attrs((IsoNode *) file, (size_t) 1, &names, value_lengths, &valuept, 4 | 8); return ret; } for(i = 0; i < 4; i++) value[3 - i] = (checksum_index >> (8 * i)) & 0xff; ret = iso_node_set_attrs((IsoNode *) file, (size_t) 1, &names, value_lengths, &valuept, 2 | 8); return ret; } int iso_root_set_isofsca(IsoNode *node, uint32_t start_lba, uint32_t end_lba, uint32_t count, uint32_t size, char *typetext, int flag) { char buffer[5 + 5 + 5 + 2 + 81], *wpt = buffer, *valuept = buffer; int result_len, ret; static char *names = "isofs.ca"; static size_t value_lengths[1]; /* Set value of isofs.ca with 4 byte START, 4 byte END, 4 byte COUNT, SIZE = 16, MD5 */ iso_util_encode_len_bytes(start_lba, wpt, 4, &result_len, 0); wpt += result_len; iso_util_encode_len_bytes(end_lba, wpt, 4, &result_len, 0); wpt += result_len; iso_util_encode_len_bytes(count, wpt, 4, &result_len, 0); wpt += result_len; iso_util_encode_len_bytes(size, wpt, 1, &result_len, 0); wpt += result_len; strncpy(wpt, typetext, 80); if (strlen(typetext) > 80) wpt += 80; else wpt += strlen(typetext); value_lengths[0] = wpt - buffer; ret = iso_node_set_attrs(node, (size_t) 1, &names, value_lengths, &valuept, 2 | 8); return ret; } int iso_root_get_isofsca(IsoNode *node, uint32_t *start_lba, uint32_t *end_lba, uint32_t *count, uint32_t *size, char typetext[81], int flag) { int ret, len; size_t value_len; char *value = NULL, *rpt; ret = iso_node_lookup_attr(node, "isofs.ca", &value_len, &value, 0); if (ret <= 0) goto ex; /* Parse value of isofs.ca with 4 byte START, 4 byte END, 4 byte COUNT, SIZE = 16, MD5 */ rpt = value; iso_util_decode_len_bytes(start_lba, rpt, &len, value_len - (rpt - value), 0); rpt += len + 1; iso_util_decode_len_bytes(end_lba, rpt, &len, value_len - (rpt - value), 0); rpt += len + 1; iso_util_decode_len_bytes(count, rpt, &len, value_len - (rpt - value), 0); rpt += len + 1; iso_util_decode_len_bytes(size, rpt, &len, value_len - (rpt - value), 0); rpt += len + 1; len = value_len - (rpt - value); if (len > 80) len = 80; memcpy(typetext, rpt, len); typetext[len] = 0; ret= ISO_SUCCESS; ex:; if (value != NULL) free(value); return ret; } int iso_root_set_isofsnt(IsoNode *node, uint32_t truncate_mode, uint32_t truncate_length, int flag) { char buffer[5 + 5], *wpt = buffer, *valuept = buffer; int result_len, ret; static char *names = "isofs.nt"; static size_t value_lengths[1]; iso_util_encode_len_bytes(truncate_mode, wpt, 0, &result_len, 0); wpt += result_len; iso_util_encode_len_bytes(truncate_length, wpt, 0, &result_len, 0); wpt += result_len; value_lengths[0] = wpt - buffer; ret = iso_node_set_attrs(node, (size_t) 1, &names, value_lengths, &valuept, 2 | 8); return ret; } int iso_root_get_isofsnt(IsoNode *node, uint32_t *truncate_mode, uint32_t *truncate_length, int flag) { int ret, len; size_t value_len; char *value = NULL, *rpt; ret = iso_node_lookup_attr(node, "isofs.nt", &value_len, &value, 0); if (ret <= 0) goto ex; rpt = value; iso_util_decode_len_bytes(truncate_mode, rpt, &len, value_len - (rpt - value), 0); rpt += len + 1; iso_util_decode_len_bytes(truncate_length, rpt, &len, value_len - (rpt - value), 0); ret= ISO_SUCCESS; ex:; if (value != NULL) free(value); return ret; } /* API */ int iso_file_get_md5(IsoImage *image, IsoFile *file, char md5[16], int flag) { int ret, i; size_t value_len; char *value = NULL; uint32_t idx = 0; void *xipt; /* xinfo MD5 overrides everything else */ ret = iso_node_get_xinfo((IsoNode *) file, checksum_md5_xinfo_func, &xipt); if (ret == 1) { memcpy(md5, (char *) xipt, 16); return 1; } if (image->checksum_array == NULL) return 0; ret = iso_node_lookup_attr((IsoNode *) file, "isofs.cx", &value_len, &value, 0); if (ret <= 0) goto ex; if (value_len > 4) { ret = 0; goto ex; } for (i = 0; i < (int) value_len; i++) idx = (idx << 8) | ((unsigned char *) value)[i]; if (idx == 0 || idx > image->checksum_idx_count - 1) { /* (last index is not MD5 of a file) */ ret = 0; goto ex; } if (!(flag & 1)) { memcpy(md5, image->checksum_array + ((size_t) 16) * ((size_t) idx), 16); } ret = 1; ex:; if (value != NULL) free(value); return ret; } /* API */ int iso_file_make_md5(IsoFile *file, int flag) { int ret, dig = 0; char *md5 = NULL; if (file->from_old_session) dig = 1; md5 = calloc(16, 1); if (md5 == NULL) return ISO_OUT_OF_MEM; ret = iso_stream_make_md5(file->stream, md5, dig); if (ret < 0) { free(md5); return ret; } iso_node_remove_xinfo((IsoNode *) file, checksum_md5_xinfo_func); ret = iso_node_add_xinfo((IsoNode *) file, checksum_md5_xinfo_func, md5); if (ret == 0) ret = ISO_ERROR; /* should not happen after iso_node_remove_xinfo() */ if (ret < 0) { free(md5); return ret; } return 1; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2023 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_NODE_H_ #define LIBISO_NODE_H_ /* * Definitions for the public iso tree */ #include "libisofs.h" #include "stream.h" #include #include #include #ifdef HAVE_STDINT_H #include #else #ifdef HAVE_INTTYPES_H #include #endif #endif /* Maximum length of a leaf name in the libisofs node tree. This is currently restricted by the implemented maximum length of a Rock Ridge name. This might later become larger and may then be limited to smaller values. Rock Ridge specs do not impose an explicit limit on name length. But 255 is also specified by http://pubs.opengroup.org/onlinepubs/009695399/basedefs/limits.h.html which says NAME_MAX >= _XOPEN_NAME_MAX = 255 */ #define LIBISOFS_NODE_NAME_MAX 255 /* Maximum length of a path in the libisofs node tree. Rock Ridge specs do not impose an explicit limit on path length. http://pubs.opengroup.org/onlinepubs/009695399/basedefs/limits.h.html says PATH_MAX >= _XOPEN_PATH_MAX = 1024 */ #define LIBISOFS_NODE_PATH_MAX 1024 /** * The extended information is a way to attach additional information to each * IsoNode. External applications may want to use this extension system to * store application specific information related to each node. On the other * side, libisofs may make use of this struct to attach information to nodes in * some particular, uncommon, cases, without incrementing the size of the * IsoNode struct. * * It is implemented like a chained list. */ typedef struct iso_extended_info IsoExtendedInfo; struct iso_extended_info { /** * Next struct in the chain. NULL if it is the last item */ IsoExtendedInfo *next; /** * Function to handle this particular extended information. The function * pointer acts as an identifier for the type of the information. Structs * with same information type must use the same function. * * @param data * Attached data * @param flag * What to do with the data. At this time the following values are * defined: * -> 1 the data must be freed * @return * 1 */ iso_node_xinfo_func process; /** * Pointer to information specific data. */ void *data; }; /** * */ struct Iso_Node { /* * Initialized to 1, originally owned by user, until added to another node. * Then it is owned by the parent node, so the user must take his own ref * if needed. With the exception of the creation functions, none of the * other libisofs functions that return an IsoNode increment its * refcount. This is responsablity of the client, if (s)he needs it. */ int refcount; /** Type of the IsoNode, do not confuse with mode */ enum IsoNodeType type; char *name; /**< Real name, in default charset */ mode_t mode; /**< protection */ uid_t uid; /**< user ID of owner */ gid_t gid; /**< group ID of owner */ /* TODO #00001 : consider adding new timestamps */ time_t atime; /**< time of last access */ time_t mtime; /**< time of last modification */ time_t ctime; /**< time of last status change */ int hidden; /**< whether the node will be hidden, see IsoHideNodeFlag */ IsoDir *parent; /**< parent node, NULL for root */ /* * Pointer to the linked list of children in a dir. */ IsoNode *next; /** * Extended information for the node. */ IsoExtendedInfo *xinfo; }; struct Iso_Dir { IsoNode node; size_t nchildren; /**< The number of children of this directory. */ IsoNode *children; /**< list of children. ptr to first child */ }; /* IMPORTANT: Any change must be reflected by iso_tree_clone_file. */ struct Iso_File { IsoNode node; /* 1 = The node was loaded from an existing ISO image and still refers to its data content there. */ unsigned int from_old_session : 1; /* 1 = The node got attributed a weight by iso_node_set_sort_weight(). */ unsigned int explicit_weight : 1; /** * It sorts the order in which the file data is written to the CD image. * Higher weighting files are written at the beginning of image */ int sort_weight; IsoStream *stream; /* Knows fs_id, st_dev, and st_ino */ }; struct Iso_Symlink { IsoNode node; char *dest; /* If the IsoNode represents an object in an existing filesystem then the following three numbers should unique identify it. (0,0,0) will always be taken as unique. */ unsigned int fs_id; dev_t st_dev; ino_t st_ino; }; struct Iso_Special { IsoNode node; dev_t dev; /* If the IsoNode represents an object in an existing filesystem then the following three numbers should unique identify it. (0,0,0) will always be taken as unique. */ unsigned int fs_id; dev_t st_dev; ino_t st_ino; }; struct iso_dir_iter_iface { int (*next)(IsoDirIter *iter, IsoNode **node); int (*has_next)(IsoDirIter *iter); void (*free)(IsoDirIter *iter); int (*take)(IsoDirIter *iter); int (*remove)(IsoDirIter *iter); /** * This is called just before remove a node from a directory. The iterator * may want to update its internal state according to this. */ void (*notify_child_taken)(IsoDirIter *iter, IsoNode *node); }; /** * An iterator for directory children. */ struct Iso_Dir_Iter { struct iso_dir_iter_iface *class; /* the directory this iterator iterates over */ IsoDir *dir; void *data; }; int iso_node_new_root(IsoDir **root); /** * Create a new IsoDir. Attributes, uid/gid, timestamps, etc are set to * default (0) values. You must set them. * * @param name * Name for the node. It is not strdup() so you shouldn't use this * reference when this function returns successfully. NULL is not * allowed. * @param dir * * @return * 1 on success, < 0 on error. */ int iso_node_new_dir(char *name, IsoDir **dir); /** * Create a new file node. Attributes, uid/gid, timestamps, etc are set to * default (0) values. You must set them. * * @param name * Name for the node. It is not strdup() so you shouldn't use this * reference when this function returns successfully. NULL is not * allowed. * @param stream * Source for file contents. The reference is taken by the node, * you must call iso_stream_ref() if you need your own ref. * @return * 1 on success, < 0 on error. */ int iso_node_new_file(char *name, IsoStream *stream, IsoFile **file); /** * Creates a new IsoSymlink node. Attributes, uid/gid, timestamps, etc are set * to default (0) values. You must set them. * * @param name * name for the new symlink. It is not strdup() so you shouldn't use this * reference when this function returns successfully. NULL is not * allowed. * @param dest * destination of the link. It is not strdup() so you shouldn't use this * reference when this function returns successfully. NULL is not * allowed. * @param link * place where to store a pointer to the newly created link. * @return * 1 on success, < 0 otherwise */ int iso_node_new_symlink(char *name, char *dest, IsoSymlink **link); /** * Create a new special file node. As far as libisofs concerns, * an special file is a block device, a character device, a FIFO (named pipe) * or a socket. You can choose the specific kind of file you want to add * by setting mode properly (see man 2 stat). * * Note that special files are only written to image when Rock Ridge * extensions are enabled. Moreover, a special file is just a directory entry * in the image tree, no data is written beyond that. * * Owner and hidden atts are taken from parent. You can modify any of them * later. * * @param name * name for the new special file. It is not strdup() so you shouldn't use * this reference when this function returns successfully. NULL is not * allowed. * @param mode * file type and permissions for the new node. Note that you can't * specify any kind of file here, only special types are allowed. i.e, * S_IFSOCK, S_IFBLK, S_IFCHR and S_IFIFO are valid types; S_IFLNK, * S_IFREG and S_IFDIR aren't. * @param dev * device ID, equivalent to the st_rdev field in man 2 stat. * @param special * place where to store a pointer to the newly created special file. * @return * 1 on success, < 0 otherwise */ int iso_node_new_special(char *name, mode_t mode, dev_t dev, IsoSpecial **special); /** * Check if a given name is valid for an iso node. * * @return * 1 if yes, <0 if not. The value is a specific ISO_* error code. */ int iso_node_is_valid_name(const char *name); /** * Check if a given path is valid for the destination of a link. * * @return * 1 if yes, 0 if not */ int iso_node_is_valid_link_dest(const char *dest); /** * Find the position where to insert a node * * @param dir * A valid dir. It can't be NULL * @param name * The node name to search for. It can't be NULL * @param pos * Will be filled with the position where to insert. It can't be NULL */ void iso_dir_find(IsoDir *dir, const char *name, IsoNode ***pos); /** * Check if a node with the given name exists in a dir. * * @param dir * A valid dir. It can't be NULL * @param name * The node name to search for. It can't be NULL * @param pos * If not NULL, will be filled with the position where to insert. If the * node exists, (**pos) will refer to the given node. * @return * 1 if node exists, 0 if not */ int iso_dir_exists(IsoDir *dir, const char *name, IsoNode ***pos); /** * Inserts a given node in a dir, at the specified position. * * @param dir * Dir where to insert. It can't be NULL * @param node * The node to insert. It can't be NULL * @param pos * Position where the node will be inserted. It is a pointer previously * obtained with a call to iso_dir_exists() or iso_dir_find(). * It can't be NULL. * @param replace * Whether to replace an old node with the same name with the new node. * @return * If success, number of children in dir. < 0 on error */ int iso_dir_insert(IsoDir *dir, IsoNode *node, IsoNode **pos, enum iso_replace_mode replace); /** * Add a new iterator to the registry. The iterator register keeps track of * all iterators being used, and are notified when directory structure * changes. */ int iso_dir_iter_register(IsoDirIter *iter); /** * Unregister a directory iterator. */ void iso_dir_iter_unregister(IsoDirIter *iter); void iso_notify_dir_iters(IsoNode *node, int flag); /** * See API function iso_node_set_permissions() * * @param flag bit0= do not adjust ACL * @return >0 success , <0 error */ int iso_node_set_perms_internal(IsoNode *node, mode_t mode, int flag); /** * Like iso_node_get_acl_text() with param node replaced by aa_string and * st_mode from where to obtain the ACLs. All other parameter specs apply. */ int iso_aa_get_acl_text(unsigned char *aa_string, mode_t st_mode, char **access_text, char **default_text, int flag); /** * Backend of iso_node_get_attrs() with parameter node replaced by the * AAIP string from where to get the attribute list. * All other parameter specs apply. */ int iso_aa_get_attrs(unsigned char *aa_string, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag); /** * Search given name. Eventually calloc() and copy value. Add trailing 0 byte * for caller convenience. * * @return 1= found , 0= not found , <0 error */ int iso_aa_lookup_attr(unsigned char *aa_string, char *name, size_t *value_length, char **value, int flag); /** * Delete variables of all namespaces except isofs * * @param flag bit0= delete ACL, too */ int iso_node_remove_fattr(IsoNode *node, int flag); /** * Function to identify and manage ZF parameters which do not stem from ZF * fields (those are known to the FileSource) and do not stem from filters * ("ziso" knows them globally, "osiz" knows them individually) but rather * from an inspection of the file content header for zisofs magic number and * plausible parameters. * The parameters get attached in struct zisofs_zf_info as xinfo to an IsoNode. */ int zisofs_zf_xinfo_func(void *data, int flag); /** * Parameter structure which is to be managed by zisofs_zf_xinfo_func. */ struct zisofs_zf_info { uint64_t uncompressed_size; uint8_t header_size_div4; uint8_t block_size_log2; uint8_t zisofs_algo[2]; }; /** * Checks whether a file effectively bears a zisofs file header and eventually * marks this by a struct zisofs_zf_info as xinfo of the file node. * @param flag bit0= inquire the most original stream of the file * bit1= permission to overwrite existing zisofs_zf_info * bit2= if no zisofs header is found: create xinfo with parameters which indicate no zisofs * @return 1= zf xinfo added, 0= no zisofs data found , * 2= found existing zf xinfo and flag bit1 was not set * <0 means error */ int iso_file_zf_by_magic(IsoFile *file, int flag); /* * @param flag * bit0= do only retrieve id if node is in imported ISO image * or has an explicit xinfo inode number * @return * 1= reply is valid from stream, 2= reply is valid from xinfo * 0= no id available, <0= error * (fs_id, dev_id, ino_id) will be (0,0,0) in case of return <= 0 */ int iso_node_get_id(IsoNode *node, unsigned int *fs_id, dev_t *dev_id, ino_t *ino_id, int flag); /* Set a new unique inode ISO image number to the given node. * This number shall eventually persist during image generation. */ int iso_node_set_unique_id(IsoNode *node, IsoImage *image, int flag); /* Use this with extreme care. Duplicate inode numbers will indicate hardlink * relationship between the nodes. */ int iso_node_set_ino(IsoNode *node, ino_t ino, int flag); /* * @param flag * bit0= compare stat properties and attributes * bit1= treat all nodes with image ino == 0 as unique * (those with 0,0,0 are treated as unique anyway) */ int iso_node_cmp_flag(IsoNode *n1, IsoNode *n2, int flag); /** * Set the checksum index (typically coming from IsoFileSrc.checksum_index) * of a regular file node. The index is encoded as xattr "isofs.cx" with * four bytes of value. */ int iso_file_set_isofscx(IsoFile *file, unsigned int checksum_index, int flag); /** * Set the checksum area description. node should be the root node. * It is encoded as xattr "isofs.ca". */ int iso_root_set_isofsca(IsoNode *node, uint32_t start_lba, uint32_t end_lba, uint32_t count, uint32_t size, char *typetext, int flag); /** * Get the checksum area description. node should be the root node. * It is encoded as xattr "isofs.ca". */ int iso_root_get_isofsca(IsoNode *node, uint32_t *start_lba, uint32_t *end_lba, uint32_t *count, uint32_t *size, char typetext[81], int flag); /** * Record and get truncation parameters as of iso_image_set_truncate_mode() by * "isofs.nt". */ int iso_root_set_isofsnt(IsoNode *node, uint32_t truncate_mode, uint32_t truncate_length, int flag); int iso_root_get_isofsnt(IsoNode *node, uint32_t *truncate_mode, uint32_t *truncate_length, int flag); /** * Copy the xinfo list from one node to the another. */ int iso_node_clone_xinfo(IsoNode *from_node, IsoNode *to_node, int flag); /** * The iso_node_xinfo_func instance which governs the storing of the inode * number from Rock Ridge field PX. */ int iso_px_ino_xinfo_func(void *data, int flag); /* The iso_node_xinfo_cloner function which gets associated to * iso_px_ino_xinfo_func by iso_init() resp. iso_init_with_flag() via * iso_node_xinfo_make_clonable() */ int iso_px_ino_xinfo_cloner(void *old_data, void **new_data, int flag); /* Function to identify and manage ZF parameters of zisofs compression. * data is supposed to be a pointer to struct zisofs_zf_info */ int zisofs_zf_xinfo_func(void *data, int flag); /* The iso_node_xinfo_cloner function which gets associated to * zisofs_zf_xinfo_func by iso_init() resp. iso_init_with_flag() via * iso_node_xinfo_make_clonable() */ int zisofs_zf_xinfo_cloner(void *old_data, void **new_data, int flag); /* Performing search for possibly truncated node name. */ int iso_dir_get_node_trunc(IsoDir *dir, int truncate_length, const char *name, IsoNode **node); #endif /*LIBISO_NODE_H_*/ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2007 Mario Danic * Copyright (c) 2009 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include #include #include "rockridge.h" #include "node.h" #include "ecma119_tree.h" #include "writer.h" #include "messages.h" #include "image.h" #include "aaip_0_2.h" #include "libisofs.h" #ifdef Libisofs_with_rrip_rR #define ISO_ROCKRIDGE_IN_DIR_REC 128 #else #define ISO_ROCKRIDGE_IN_DIR_REC 124 #endif #define ISO_CE_ENTRY_SIZE 28 static int susp_add_ES(Ecma119Image *t, struct susp_info *susp, int to_ce, int seqno); static int susp_make_CE(Ecma119Image *t, uint8_t **CE, uint32_t block_offset, uint32_t byte_offset, uint32_t size); static int susp_append(Ecma119Image *t, struct susp_info *susp, uint8_t *data) { susp->n_susp_fields++; susp->susp_fields = realloc(susp->susp_fields, sizeof(void*) * susp->n_susp_fields); if (susp->susp_fields == NULL) { return ISO_OUT_OF_MEM; } susp->susp_fields[susp->n_susp_fields - 1] = data; susp->suf_len += data[2]; return ISO_SUCCESS; } static int susp_append_ce(Ecma119Image *t, struct susp_info *susp, uint8_t *data) { int to_alloc = 1, ret; unsigned char *pad; uint8_t *CE; size_t next_alloc; if (data[0] && (susp->ce_len + data[2] + ISO_CE_ENTRY_SIZE - 1) / BLOCK_SIZE != susp->ce_len / BLOCK_SIZE) { /* Linux fs/isofs wants byte_offset + ce_len <= BLOCK_SIZE. So this Continuation Area needs to end by a CE which points to the start of the next block. */ to_alloc = 2; if ((susp->ce_len + ISO_CE_ENTRY_SIZE) % BLOCK_SIZE) to_alloc = 3; /* need a PAD pseudo entry */ } if (susp->ce_susp_fields == NULL) susp->alloc_ce_susp_fields = 0; if (susp->n_ce_susp_fields + to_alloc > susp->alloc_ce_susp_fields) { next_alloc = susp->alloc_ce_susp_fields; while (susp->n_ce_susp_fields + to_alloc > next_alloc) next_alloc += ISO_SUSP_CE_ALLOC_STEP; susp->ce_susp_fields = realloc(susp->ce_susp_fields, sizeof(uint8_t *) * next_alloc); if (susp->ce_susp_fields == NULL) return ISO_OUT_OF_MEM; susp->alloc_ce_susp_fields = next_alloc; } if (to_alloc >= 2) { /* Insert CE entry (actual CE size later by susp_update_CE_sizes) */ ret = susp_make_CE(t, &CE, (uint32_t) (susp->ce_block + susp->ce_len / BLOCK_SIZE + 1), (uint32_t) 0, (uint32_t) 0); if (ret < 0) return ret; susp->ce_susp_fields[susp->n_ce_susp_fields] = CE; susp->ce_len += ISO_CE_ENTRY_SIZE; susp->n_ce_susp_fields++; } if (to_alloc >= 3) { #ifdef Libisofs_ce_calc_debuG fprintf(stderr, "\nlibburn_DEBUG: Inserting %d bytes of CE padding\n\n", (int) (BLOCK_SIZE - (susp->ce_len % BLOCK_SIZE))); #endif /* Libisofs_ce_calc_debuG */ pad = malloc(1); if (pad == NULL) return ISO_OUT_OF_MEM; pad[0] = 0; susp->ce_susp_fields[susp->n_ce_susp_fields] = pad; if (susp->ce_len % BLOCK_SIZE) susp->ce_len += BLOCK_SIZE - (susp->ce_len % BLOCK_SIZE); susp->n_ce_susp_fields++; } susp->ce_susp_fields[susp->n_ce_susp_fields] = data; susp->n_ce_susp_fields++; if (data[0] == 0) { if (susp->ce_len % BLOCK_SIZE) susp->ce_len += BLOCK_SIZE - (susp->ce_len % BLOCK_SIZE); } else { susp->ce_len += data[2]; } return ISO_SUCCESS; } static uid_t px_get_uid(Ecma119Image *t, Ecma119Node *n) { if (t->replace_uid) { return t->uid; } else { return n->node->uid; } } static uid_t px_get_gid(Ecma119Image *t, Ecma119Node *n) { if (t->replace_gid) { return t->gid; } else { return n->node->gid; } } static mode_t px_get_mode(Ecma119Image *t, Ecma119Node *n) { if ((n->type == ECMA119_DIR || n->type == ECMA119_PLACEHOLDER)) { if (t->replace_dir_mode) { return (n->node->mode & S_IFMT) | t->dir_mode; } } else { if (t->replace_file_mode) { return (n->node->mode & S_IFMT) | t->file_mode; } } return n->node->mode; } /** * Add a PX System Use Entry. The PX System Use Entry is used to add POSIX * file attributes, such as access permissions or user and group id, to a * ECMA 119 directory record. (RRIP, 4.1.1) */ static int rrip_add_PX(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp) { uint8_t *PX = malloc(44); if (PX == NULL) { return ISO_OUT_OF_MEM; } PX[0] = 'P'; PX[1] = 'X'; if (t->opts->rrip_1_10_px_ino || !t->opts->rrip_version_1_10 ) { PX[2] = 44; } else { PX[2] = 36; } PX[3] = 1; iso_bb(&PX[4], (uint32_t) px_get_mode(t, n), 4); iso_bb(&PX[12], (uint32_t) n->nlink, 4); iso_bb(&PX[20], (uint32_t) px_get_uid(t, n), 4); iso_bb(&PX[28], (uint32_t) px_get_gid(t, n), 4); if (t->opts->rrip_1_10_px_ino || !t->opts->rrip_version_1_10) { iso_bb(&PX[36], (uint32_t) n->ino, 4); } return susp_append(t, susp, PX); } /** * Add to the given tree node a TF System Use Entry, used to record some * time stamps related to the file (RRIP, 4.1.6). */ static int rrip_add_TF(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp) { IsoNode *iso; uint8_t *TF = malloc(5 + 3 * 7); if (TF == NULL) { return ISO_OUT_OF_MEM; } TF[0] = 'T'; TF[1] = 'F'; TF[2] = 5 + 3 * 7; TF[3] = 1; TF[4] = (1 << 1) | (1 << 2) | (1 << 3); iso = n->node; iso_datetime_7(&TF[5], t->replace_timestamps ? t->timestamp : iso->mtime, t->opts->always_gmt); iso_datetime_7(&TF[12], t->replace_timestamps ? t->timestamp : iso->atime, t->opts->always_gmt); iso_datetime_7(&TF[19], t->replace_timestamps ? t->timestamp : iso->ctime, t->opts->always_gmt); return susp_append(t, susp, TF); } /** * Add a PL System Use Entry, used to record the location of the original * parent directory of a directory which has been relocated. * * This is special because it doesn't modify the susp fields of the directory * that gets passed to it; it modifies the susp fields of the ".." entry in * that directory. * * See RRIP, 4.1.5.2 for more details. */ static int rrip_add_PL(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp) { uint8_t *PL; if (n->type != ECMA119_DIR || n->info.dir->real_parent == NULL) { /* should never occur */ return ISO_ASSERT_FAILURE; } PL = malloc(12); if (PL == NULL) { return ISO_OUT_OF_MEM; } PL[0] = 'P'; PL[1] = 'L'; PL[2] = 12; PL[3] = 1; /* write the location of the real parent, already computed */ iso_bb(&PL[4], n->info.dir->real_parent->info.dir->block - t->eff_partition_offset, 4); return susp_append(t, susp, PL); } /** * Add a RE System Use Entry to the given tree node. The purpose of the * this System Use Entry is to indicate to an RRIP-compliant receiving * system that the Directory Record in which an "RE" System Use Entry is * recorded has been relocated from another position in the original * Directory Hierarchy. * * See RRIP, 4.1.5.3 for more details. */ static int rrip_add_RE(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp) { uint8_t *RE = malloc(4); if (RE == NULL) { return ISO_OUT_OF_MEM; } RE[0] = 'R'; RE[1] = 'E'; RE[2] = 4; RE[3] = 1; return susp_append(t, susp, RE); } /** * Add a PN System Use Entry to the given tree node. * The PN System Use Entry is used to store the device number, and it's * mandatory if the tree node corresponds to a character or block device. * * See RRIP, 4.1.2 for more details. */ static int rrip_add_PN(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp) { IsoSpecial *node; uint8_t *PN; int high_shift= 0; node = (IsoSpecial*)n->node; if (node->node.type != LIBISO_SPECIAL) { /* should never occur */ return ISO_ASSERT_FAILURE; } PN = malloc(20); if (PN == NULL) { return ISO_OUT_OF_MEM; } PN[0] = 'P'; PN[1] = 'N'; PN[2] = 20; PN[3] = 1; /* (dev_t >> 32) causes compiler warnings on FreeBSD. RRIP 1.10 4.1.2 prescribes PN "Dev_t High" to be 0 on 32 bit dev_t. */ if (sizeof(node->dev) > 4) { high_shift = 32; iso_bb(&PN[4], (uint32_t) (node->dev >> high_shift), 4); } else iso_bb(&PN[4], 0, 4); iso_bb(&PN[12], (uint32_t) (node->dev & 0xffffffff), 4); return susp_append(t, susp, PN); } /** * Add to the given tree node a CL System Use Entry, that is used to record * the new location of a directory which has been relocated. * * See RRIP, 4.1.5.1 for more details. */ static int rrip_add_CL(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp) { uint8_t *CL; if (n->type != ECMA119_PLACEHOLDER) { /* should never occur */ return ISO_ASSERT_FAILURE; } CL = malloc(12); if (CL == NULL) { return ISO_OUT_OF_MEM; } CL[0] = 'C'; CL[1] = 'L'; CL[2] = 12; CL[3] = 1; iso_bb(&CL[4], n->info.real_me->info.dir->block - t->eff_partition_offset, 4); return susp_append(t, susp, CL); } /** * Convert a RR filename to the requested charset. On any conversion error, * the original name will be used. * @param flag bit0= do not issue error messages */ int iso_get_rr_name(IsoWriteOpts *opts, char *input_charset, char *output_charset, int imgid, char *str, char **name, int flag) { int ret; if (!strcmp(input_charset, output_charset)) { /* no conversion needed */ ret = iso_clone_mem(str, name, 0); return ret; } ret = strconv(str, input_charset, output_charset, name); if (ret < 0) { /* TODO we should check for possible cancellation */ if (!(flag & 1)) iso_msg_submit(imgid, ISO_FILENAME_WRONG_CHARSET, ret, "Charset conversion error. Cannot convert %s from %s to %s", str, input_charset, output_charset); *name = NULL; return ISO_FILENAME_WRONG_CHARSET; } return ISO_SUCCESS; } static char *get_rr_fname(Ecma119Image *t, char *str) { int ret; char *name = NULL; ret = iso_get_rr_name(t->opts, t->input_charset, t->output_charset, t->image->id, str, &name, 0); if (ret < 0) return NULL; return name; } /** * Add a NM System Use Entry to the given tree node. The purpose of this * System Use Entry is to store the content of an Alternate Name to support * POSIX-style or other names. * * See RRIP, 4.1.4 for more details. * * @param size * Length of the name to be included into the NM * @param flags * @param ce * Whether to add or not to CE */ static int rrip_add_NM(Ecma119Image *t, struct susp_info *susp, char *name, int size, int flags, int ce) { uint8_t *NM; if (size > 250) return ISO_ASSERT_FAILURE; NM = malloc(size + 5); if (NM == NULL) { return ISO_OUT_OF_MEM; } NM[0] = 'N'; NM[1] = 'M'; NM[2] = size + 5; NM[3] = 1; NM[4] = flags; if (size) { memcpy(&NM[5], name, size); } if (ce) { return susp_append_ce(t, susp, NM); } else { return susp_append(t, susp, NM); } } /** * Add a new SL component (RRIP, 4.1.3.1) to a list of components. * * @param n * Number of components. It will be updated. * @param compos * Pointer to the list of components. * @param s * The component content * @param size * Size of the component content * @param fl * Flags * @return * 1 on success, < 0 on error */ static int rrip_SL_append_comp(size_t *n, uint8_t ***comps, char *s, int size, char fl) { uint8_t *comp = malloc(size + 2); if (comp == NULL) { return ISO_OUT_OF_MEM; } (*n)++; comp[0] = fl; comp[1] = size; *comps = realloc(*comps, (*n) * sizeof(void*)); if (*comps == NULL) { free(comp); return ISO_OUT_OF_MEM; } (*comps)[(*n) - 1] = comp; if (size) { memcpy(&comp[2], s, size); } return ISO_SUCCESS; } #ifdef Libisofs_with_rrip_rR /** * Add a RR System Use Entry to the given tree node. This is an obsolete * entry from before RRIP-1.10. Nevertheless mkisofs produces it. There * is the suspicion that some operating systems could take it as indication * for Rock Ridge. * * The meaning of the payload byte is documented e.g. in * /usr/src/linux/fs/isofs/rock.h * It announces the presence of entries PX, PN, SL, NM, CL, PL, RE, TF * by payload byte bits 0 to 7. */ static int rrip_add_RR(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp) { uint8_t *RR; RR = malloc(5); if (RR == NULL) { return ISO_OUT_OF_MEM; } RR[0] = 'R'; RR[1] = 'R'; RR[2] = 5; RR[3] = 1; /* <<< ts B20307 : Not all directories have NM, many files have more entries */ RR[4] = 0x89; /* TF, NM , PX */ /* >>> ts B20307 : find out whether n carries PX, PN, SL, NM, CL, PL, RE, TF and mark by bit0 to bit7 in RR[4] */ return susp_append(t, susp, RR); } #endif /* Libisofs_with_rrip_rR */ /** * Add a SL System Use Entry to the given tree node. This is used to store * the content of a symbolic link, and is mandatory if the tree node * indicates a symbolic link (RRIP, 4.1.3). * * @param comp * Components of the SL System Use Entry. If they don't fit in a single * SL, more than one SL will be added. * @param n * Number of components in comp * @param ce * Whether to add to a continuation area or system use field. */ static int rrip_add_SL(Ecma119Image *t, struct susp_info *susp, uint8_t **comp, size_t n, int ce) { int ret; size_t i, j; int total_comp_len = 0; size_t pos, written = 0; uint8_t *SL; for (i = 0; i < n; i++) { total_comp_len += comp[i][1] + 2; if (total_comp_len > 250) { /* we need a new SL entry */ total_comp_len -= comp[i][1] + 2; SL = malloc(total_comp_len + 5); if (SL == NULL) { return ISO_OUT_OF_MEM; } SL[0] = 'S'; SL[1] = 'L'; SL[2] = total_comp_len + 5; SL[3] = 1; SL[4] = 1; /* CONTINUE */ pos = 5; for (j = written; j < i; j++) { memcpy(&SL[pos], comp[j], comp[j][1] + 2); pos += comp[j][1] + 2; } /* * In this case we are sure we're writing to CE. Check for * debug purposes */ if (ce == 0) { free(SL); return ISO_ASSERT_FAILURE; } ret = susp_append_ce(t, susp, SL); if (ret < 0) { free(SL); return ret; } SL = NULL; /* now owned by susp */ written = i; total_comp_len = comp[i][1] + 2; } } SL = malloc(total_comp_len + 5); if (SL == NULL) { return ISO_OUT_OF_MEM; } SL[0] = 'S'; SL[1] = 'L'; SL[2] = total_comp_len + 5; SL[3] = 1; SL[4] = 0; pos = 5; for (j = written; j < n; j++) { memcpy(&SL[pos], comp[j], comp[j][1] + 2); pos += comp[j][1] + 2; } if (ce) { ret = susp_append_ce(t, susp, SL); } else { ret = susp_append(t, susp, SL); } return ret; } /* @param flag bit1= care about crossing block boundaries */ static int susp_calc_add_to_ce(Ecma119Image *t, size_t *ce, size_t base_ce, int add, int flag) { if (flag & 2) { /* Account for inserted CE before size exceeds block size */ if ((*ce + base_ce + add + ISO_CE_ENTRY_SIZE - 1) / BLOCK_SIZE != (*ce + base_ce) / BLOCK_SIZE) { /* Insert CE and padding */ t->curr_ce_entries++; *ce += ISO_CE_ENTRY_SIZE; if ((*ce + base_ce) % BLOCK_SIZE) *ce += BLOCK_SIZE - ((*ce + base_ce) % BLOCK_SIZE); } } *ce += add; return ISO_SUCCESS; } /* @param flag bit0= only account sizes in sua_free resp. ce_len. Parameter susp may be NULL in this case bit1= account for crossing block boundaries (implied by bit0 == 0) @param ce_len counts the freshly added CA size of the current node @param ce_mem tells the CA size of previous nodes in the same directory */ static int aaip_add_AL(Ecma119Image *t, struct susp_info *susp, uint8_t **data, size_t num_data, size_t *sua_free, size_t *ce_len, size_t ce_mem, int flag) { int ret, done = 0, len, es_extra = 0; uint8_t *aapt, *cpt; size_t count = 0; if (!(flag & 1)) flag |= 2; if (!t->opts->aaip_susp_1_10) es_extra = 5; if (*sua_free < num_data + es_extra || *ce_len > 0) { if (es_extra > 0) susp_calc_add_to_ce(t, ce_len, ce_mem, es_extra, flag & 2); done = 0; for (aapt = *data; !done; aapt += aapt[2]) { done = !(aapt[4] & 1); len = aapt[2]; susp_calc_add_to_ce(t, ce_len, ce_mem, len, flag & 2); count += len; } } else { *sua_free -= num_data + es_extra; } if (flag & 1) return ISO_SUCCESS; /* If AAIP enabled and announced by ER : Write ES field to announce AAIP */ if (t->opts->aaip && !t->opts->aaip_susp_1_10) { ret = susp_add_ES(t, susp, (*ce_len > 0), 1); if (ret < 0) return ret; } aapt = *data; if (!(aapt[4] & 1)) { /* Single field can be handed over directly */ if (*ce_len > 0) { ret = susp_append_ce(t, susp, aapt); } else { ret = susp_append(t, susp, aapt); } *data = NULL; return ISO_SUCCESS; } /* Multiple fields have to be handed over as single field copies */ done = 0; for (aapt = *data; !done; aapt += aapt[2]) { done = !(aapt[4] & 1); len = aapt[2]; cpt = calloc(aapt[2], 1); if (cpt == NULL) return ISO_OUT_OF_MEM; memcpy(cpt, aapt, len); if (*ce_len > 0) { ret = susp_append_ce(t, susp, cpt); } else { ret = susp_append(t, susp, cpt); } if (ret < 0) return ret; } free(*data); *data = NULL; return ISO_SUCCESS; } /** * Add a SUSP "ER" System Use Entry to identify the Rock Ridge specification. * * The "ER" System Use Entry is used to uniquely identify a specification * compliant with SUSP. This method adds to the given tree node "." entry * the "ER" corresponding to the RR protocol. * * See SUSP, 5.5 and RRIP, 4.3 for more details. */ static int rrip_add_ER(Ecma119Image *t, struct susp_info *susp) { unsigned char *ER; if (!t->opts->rrip_version_1_10) { /* According to RRIP 1.12 this is the future form: 4.3 "Specification of the ER System Use Entry Values for RRIP" talks of "IEEE_P1282" in each of the three strings and finally states "Note: Upon adoption as an IEEE standard, these lengths will each decrease by 1." So "IEEE_P1282" would be the new form, "RRIP_1991A" is the old form. */ ER = malloc(182); if (ER == NULL) { return ISO_OUT_OF_MEM; } ER[0] = 'E'; ER[1] = 'R'; ER[2] = 182; ER[3] = 1; ER[4] = 9; ER[5] = 72; ER[6] = 93; ER[7] = 1; memcpy(&ER[8], "IEEE_1282", 9); memcpy(&ER[17], "THE IEEE 1282 PROTOCOL PROVIDES SUPPORT FOR POSIX " "FILE SYSTEM SEMANTICS.", 72); memcpy(&ER[89], "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, " "PISCATAWAY, NJ, USA FOR THE 1282 SPECIFICATION.", 93); } else { /* RRIP 1.09 and 1.10: 4.3 Specification of the ER System Use Field Values for RRIP The Extension Version number for the version of the RRIP defined herein shall be 1. The content of the Extension Identifier field shall be "RRIP_1991A". The Identifier Length shall be 10. The recommended content of the Extension Descriptor shall be "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS." The corresponding Description Length is 84. The recommended content of the Extension Source shall be "PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE. SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR CONTACT INFORMATION." The corresponding Source Length is 135. */ ER = malloc(237); if (ER == NULL) { return ISO_OUT_OF_MEM; } ER[0] = 'E'; ER[1] = 'R'; ER[2] = 237; ER[3] = 1; ER[4] = 10; ER[5] = 84; ER[6] = 135; ER[7] = 1; memcpy(&ER[8], "RRIP_1991A", 10); memcpy(&ER[18], "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS", 84); memcpy(&ER[102], "PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE. SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR CONTACT INFORMATION.", 135); } /** This always goes to continuation area */ return susp_append_ce(t, susp, ER); } static int aaip_add_ER(Ecma119Image *t, struct susp_info *susp, int flag) { unsigned char *ER; ER = malloc(160); if (ER == NULL) { return ISO_OUT_OF_MEM; } ER[0] = 'E'; ER[1] = 'R'; ER[2] = 160; ER[3] = 1; ER[4] = 9; ER[5] = 81; ER[6] = 62; ER[7] = 1; memcpy(ER + 8, "AAIP_0200", 9); memcpy(ER + 17, "AL PROVIDES VIA AAIP 2.0 SUPPORT FOR ARBITRARY FILE ATTRIBUTES" " IN ISO 9660 IMAGES", 81); memcpy(ER + 98, "PLEASE CONTACT THE LIBBURNIA PROJECT VIA LIBBURNIA-PROJECT.ORG", 62); /** This always goes to continuation area */ return susp_append_ce(t, susp, ER); } /** * Create the byte representation of a CE entry. * (SUSP, 5.1). */ static int susp_make_CE(Ecma119Image *t, uint8_t **CE, uint32_t block_offset, uint32_t byte_offset, uint32_t size) { uint8_t *data; *CE = NULL; data = calloc(1, 28); if (data == NULL) return ISO_OUT_OF_MEM; *CE = data; data[0] = 'C'; data[1] = 'E'; data[2] = 28; data[3] = 1; iso_bb(&data[4], block_offset - t->eff_partition_offset, 4); iso_bb(&data[12], byte_offset, 4); iso_bb(&data[20], size, 4); return ISO_SUCCESS; } /** * Add a CE System Use Entry to the given tree node. A "CE" is used to add * a continuation area, where additional System Use Entry can be written. * (SUSP, 5.1). */ static int susp_add_CE(Ecma119Image *t, size_t ce_len, struct susp_info *susp) { uint32_t block_offset, byte_offset; uint8_t *CE; int ret; /* Linux fs/isofs wants byte_offset + ce_len <= BLOCK_SIZE * Here the byte_offset is reduced to the minimum. */ block_offset = susp->ce_block + susp->ce_len / BLOCK_SIZE; byte_offset = susp->ce_len % BLOCK_SIZE; ret = susp_make_CE(t, &CE, block_offset, byte_offset, (uint32_t) ce_len); if (ret < 0) return ret; return susp_append(t, susp, CE); } /** * Add a SP System Use Entry. The SP provide an identifier that the SUSP is * used within the volume. The SP shall be recorded in the "." entry of the * root directory. See SUSP, 5.3 for more details. */ static int susp_add_SP(Ecma119Image *t, struct susp_info *susp) { unsigned char *SP = malloc(7); if (SP == NULL) { return ISO_OUT_OF_MEM; } SP[0] = 'S'; SP[1] = 'P'; SP[2] = (char)7; SP[3] = (char)1; SP[4] = 0xbe; SP[5] = 0xef; SP[6] = 0; return susp_append(t, susp, SP); } /** * SUSP 1.12: [...] shall specify as an 8-bit number the Extension * Sequence Number of the extension specification utilized in the entries * immediately following this System Use Entry. The Extension Sequence * Numbers of multiple extension specifications on a volume shall correspond to * the order in which their "ER" System Use Entries are recorded [...] */ static int susp_add_ES(Ecma119Image *t, struct susp_info *susp, int to_ce, int seqno) { unsigned char *ES = malloc(5); if (ES == NULL) { return ISO_OUT_OF_MEM; } ES[0] = 'E'; ES[1] = 'S'; ES[2] = (unsigned char) 5; ES[3] = (unsigned char) 1; ES[4] = (unsigned char) seqno; if (to_ce) { return susp_append_ce(t, susp, ES); } else { return susp_append(t, susp, ES); } } /** * see doc/zisofs_format.txt : "ZF System Use Entry Format" * see doc/zisofs2_format.txt : "ZF System Use Entry Format", "Z2 ..." */ static int zisofs_add_ZF(Ecma119Image *t, struct susp_info *susp, int to_ce, uint8_t algo[2], int header_size_div4, int block_size_log2, uint64_t uncompressed_size, int flag) { unsigned char *ZF = malloc(16); /* Intimate friendship with this variable in filters/zisofs.c */ extern int iso_zisofs2_enable_susp_z2; if (ZF == NULL) { return ISO_OUT_OF_MEM; } ZF[0] = 'Z'; ZF[1] = 'F'; ZF[2] = (unsigned char) 16; if (algo[0] == 'p' && algo[1] == 'z') { ZF[3] = (unsigned char) 1; } else { ZF[3] = (unsigned char) 2; if (iso_zisofs2_enable_susp_z2) ZF[1] = '2'; } ZF[4] = (unsigned char) algo[0]; ZF[5] = (unsigned char) algo[1]; ZF[6] = (unsigned char) header_size_div4; ZF[7] = (unsigned char) block_size_log2; if (algo[0] == 'p' && algo[1] == 'z') { if (uncompressed_size > (uint64_t) 0xffffffff) return ISO_ZISOFS_TOO_LARGE; iso_bb(&ZF[8], (uint32_t) uncompressed_size, 4); } else { iso_lsb64(&ZF[8], uncompressed_size); } if (to_ce) { return susp_append_ce(t, susp, ZF); } else { return susp_append(t, susp, ZF); } } /* @param flag bit0= Do not add data but only count sua_free and ce_len bit1= account for crossing block boundaries (implied by bit0 == 0) */ static int add_zf_field(Ecma119Image *t, Ecma119Node *n, struct susp_info *info, size_t *sua_free, size_t *ce_len, size_t base_ce, int flag) { int ret, will_copy = 1, stream_type = 0, do_zf = 0; int header_size_div4 = 0, block_size_log2 = 0; uint64_t uncompressed_size = 0; IsoStream *stream = NULL, *input_stream, *last_stream, *first_stream; IsoStream *first_filter = NULL; IsoFile *file; void *xipt; struct zisofs_zf_info *zf; uint8_t algo[2]; /* Intimate friendship with this function in filters/zisofs.c */ int ziso_is_zisofs_stream(IsoStream *stream, int *stream_type, uint8_t zisofs_algo[2], int *header_size_div4, int *block_size_log2, uint64_t *uncompressed_size, int flag); if (!(flag & 1)) flag |= 2; if (iso_node_get_type(n->node) != LIBISO_FILE) return 2; file = (IsoFile *) n->node; /* Inspect: last_stream < ... < first_filter < first_stream */ /* The content is in zisofs format if: It gets copied and the last stream is a ziso stream, or it had a ZF entry and is unfiltered or it has a zf xinfo record (because its last stream delivered a zisofs file header when inquired) or it stays uncopied and the first filter is an osiz stream, or it had a ZF entry or it has a zf xinfo record (because its first stream delivered a zisofs file header when inquired) */ if (t->opts->appendable && file->from_old_session) will_copy = 0; first_filter = first_stream = last_stream = iso_file_get_stream(file); while (1) { input_stream = iso_stream_get_input_stream(first_stream, 0); if (input_stream == NULL) break; first_filter = first_stream; first_stream = input_stream; } if (will_copy) { stream = last_stream; } else { /* (the eventual osiz filter on the image stream) */ stream = first_filter; } /* Determine stream type : 1=ziso , -1=osiz , 0=other */ algo[0] = algo[1] = 0; ret = ziso_is_zisofs_stream(stream, &stream_type, algo, &header_size_div4, &block_size_log2, &uncompressed_size, 0); if (ret < 0) return ret; if (stream_type == 1 && will_copy) { do_zf = 1; } else if (stream_type == -1 && !will_copy) { do_zf = 1; } else if(first_stream == last_stream || !will_copy) { /* Try whether the image side stream remembers a ZF field */ ret = iso_stream_get_src_zf(first_stream, algo, &header_size_div4, &block_size_log2, &uncompressed_size, 0); if (ret == 1 && header_size_div4 > 0) do_zf = 1; } if (!do_zf) { /* Look for an xinfo mark of a zisofs header */ ret = iso_node_get_xinfo((IsoNode *) file, zisofs_zf_xinfo_func, &xipt); if (ret == 1) { zf = xipt; header_size_div4 = zf->header_size_div4; block_size_log2 = zf->block_size_log2; uncompressed_size = zf->uncompressed_size; algo[0] = zf->zisofs_algo[0]; algo[1] = zf->zisofs_algo[1]; if (header_size_div4 > 0) do_zf = 1; } } if (!do_zf) return 2; /* Account for field size */ if (*sua_free < 16 || *ce_len > 0) { susp_calc_add_to_ce(t, ce_len, base_ce, 16, flag & 2); } else { *sua_free -= 16; } if (flag & 1) return 1; /* write ZF field */ ret = zisofs_add_ZF(t, info, (*ce_len > 0), algo, header_size_div4, block_size_log2, uncompressed_size, 0); if (ret < 0) return ret; return 1; } /* API */ int aaip_xinfo_func(void *data, int flag) { if (flag & 1) { free(data); } return 1; } /* API */ int aaip_xinfo_cloner(void *old_data, void **new_data, int flag) { size_t aa_size; *new_data = NULL; if (old_data == NULL) return 0; aa_size = aaip_count_bytes((unsigned char *) old_data, 0); if (aa_size <= 0) return ISO_AAIP_BAD_AASTRING; *new_data = calloc(1, aa_size); if (*new_data == NULL) return ISO_OUT_OF_MEM; memcpy(*new_data, old_data, aa_size); return (int) aa_size; } /** * Compute SUA length and eventual Continuation Area length of field NM and * eventually fields SL and AL. Because CA usage makes necessary the use of * a CE entry of 28 bytes in SUA, this computation fails if not the 28 bytes * are taken into account at start. In this case the caller should retry with * bit0 set. * If the resulting *ce added to base_ce is in a different block than base_ce, * then computation with bit0 fails and the caller should finally try bit1. * * @param flag bit0= assume CA usage (else return 0 on SUA overflow) * bit1= let CA start at block start (else return 0 if * *ce crosses a block boundary) * @return 1= ok, computation of *su_size and *ce is valid * 0= not ok, CA usage is necessary but bit0 was not set * or *ce crosses boundary and bit1 was not set * (*su_size and *ce stay unaltered in this case) * <0= error: * -1= not enough SUA space for 28 bytes of CE entry * -2= out of memory * (int) ISO_TOO_MANY_CE */ static int susp_calc_nm_sl_al(Ecma119Image *t, Ecma119Node *n, size_t space, size_t *su_size, size_t *ce, size_t base_ce, int flag) { char *name; size_t namelen, su_mem, ce_mem; void *xipt; size_t num_aapt = 0, sua_free = 0; int ret; uint8_t *aapt; uint32_t curr_ce_entries_mem; #ifdef Libisofs_ce_calc_debug_extrA if (n->node->name != NULL) fprintf(stderr, "libburn_DEBUG: susp_calc_nm_sl_al : %u %.f %s \n", (unsigned int) t->curr_ce_entries, (double) base_ce, n->node->name); #endif /* Libisofs_ce_calc_debug_extrA */ su_mem = *su_size; ce_mem = *ce; curr_ce_entries_mem = t->curr_ce_entries; if (*ce > 0 && !(flag & 1)) goto unannounced_ca; if (flag & 2) flag |= 1; namelen = 0; name = get_rr_fname(t, n->node->name); if (name != NULL) { namelen = strlen(name); free(name); } if (flag & 1) { /* Account for 28 bytes of CE field */ if (*su_size + 28 > space) return -1; *su_size += 28; t->curr_ce_entries++; } /* NM entry */ if (*su_size + 5 + namelen <= space) { /* ok, it fits in System Use Area */ *su_size += 5 + namelen; } else { /* the NM will be divided in a CA */ if (!(flag & 1)) goto unannounced_ca; namelen = namelen - (space - *su_size - 5); /* >>> SUPER_LONG_RR: Need to handle CA part lengths > 250 (This cannot happen with name lengths <= 256, as a part of the name will always fit into the directory entry.) */; susp_calc_add_to_ce(t, ce, base_ce, 5 + namelen, flag & 2); *su_size = space; } if (n->type == ECMA119_SYMLINK) { /* * for symlinks, we also need to write the SL */ char *dest, *cur, *prev; size_t sl_len = 5; int cew = (*ce != 0); /* are we writing to CA ? */ dest = get_rr_fname(t, ((IsoSymlink*)n->node)->dest); if (dest == NULL) return -2; prev = dest; cur = strchr(prev, '/'); while (1) { size_t clen; if (cur) { clen = cur - prev; } else { /* last component */ clen = strlen(prev); } if (clen == 1 && prev[0] == '.') { clen = 0; } else if (clen == 2 && prev[0] == '.' && prev[1] == '.') { clen = 0; } /* flags and len for each component record (RRIP, 4.1.3.1) */ clen += 2; if (!cew) { /* we are still writing to the SUA */ if (*su_size + sl_len + clen > space) { /* * ok, we need a Continuation Area anyway * TODO this can be handled better, but for now SL * will be completely moved into the CA */ if (!(flag & 1)) { free(dest); goto unannounced_ca; } cew = 1; } else { sl_len += clen; } } if (cew) { if (sl_len + clen > 255) { /* we need an additional SL entry */ if (clen > 250) { /* * case 1, component too large to fit in a * single SL entry. Thus, the component need * to be divided anyway. * Note than clen can be up to 255 + 2 = 257. * * First, we check how many bytes fit in current * SL field */ ssize_t fit = 255 - sl_len - 2; if ((ssize_t) (clen - 250) <= fit) { /* * the component can be divided between this * and another SL entry */ /* Will fill up old SL and write it */ susp_calc_add_to_ce(t, ce, base_ce, 255, flag & 2); sl_len = 5 + (clen - fit); /* Start new SL */ } else { /* * the component will need a 2rd SL entry in * any case, so we prefer to don't write * anything in this SL */ /* Will write non-full old SL */ susp_calc_add_to_ce(t, ce, base_ce, sl_len, flag & 2); /* Will write another full SL */ susp_calc_add_to_ce(t, ce, base_ce, 255, flag & 2); sl_len = 5 + (clen - 250) + 2; /* Start new SL */ } } else { /* case 2, create a new SL entry */ /* Will write non-full old SL */ susp_calc_add_to_ce(t, ce, base_ce, sl_len, flag & 2); sl_len = 5 + clen; /* Start new SL */ } } else { sl_len += clen; } } if (!cur || cur[1] == '\0') { /* cur[1] can be \0 if dest ends with '/' */ break; } prev = cur + 1; cur = strchr(prev, '/'); } free(dest); /* and finally write the pending SL field */ if (!cew) { /* the whole SL fits into the SUA */ *su_size += sl_len; } else { susp_calc_add_to_ce(t, ce, base_ce, sl_len, flag & 2); } } /* Find out whether ZF is to be added and account for its bytes */ sua_free = space - *su_size; add_zf_field(t, n, NULL, &sua_free, ce, base_ce, 1 | (flag & 2)); *su_size = space - sua_free; if (*ce > 0 && !(flag & 1)) goto unannounced_ca; /* obtain num_aapt from node */ xipt = NULL; num_aapt = 0; if (t->opts->aaip) { ret = iso_node_get_xinfo(n->node, aaip_xinfo_func, &xipt); if (ret == 1) { num_aapt = aaip_count_bytes((unsigned char *) xipt, 0); } } /* let the expert decide where to add num_aapt */ if (num_aapt > 0) { sua_free = space - *su_size; aapt = (uint8_t *) xipt; aaip_add_AL(t, NULL, &aapt, num_aapt, &sua_free, ce, base_ce, 1 | (flag & 2)); *su_size = space - sua_free; if (*ce > 0 && !(flag & 1)) goto unannounced_ca; } #ifdef Libisofs_ce_calc_debug_filetraP if (n->node->name != NULL) if (strcmp(n->node->name, "...insert.leaf.name.here...") == 0) fprintf(stderr, "libburn_DEBUG: filename breakpoint susp_calc_nm_sl_al\n"); #endif /* Libisofs_ce_calc_debug_filetraP */ if (*ce > 0 && !(flag & 2)) { if (base_ce / BLOCK_SIZE != (base_ce + *ce + ISO_CE_ENTRY_SIZE - 1) / BLOCK_SIZE) { #ifdef Libisofs_ce_calc_debuG fprintf(stderr, "\nlibburn_DEBUG: Crossed block boundary: %.f (%lu) -> %.f (%lu) : %s\n\n", (double) base_ce, (unsigned long) (base_ce / BLOCK_SIZE), (double) (base_ce + *ce - 1), (unsigned long) ((base_ce + *ce + ISO_CE_ENTRY_SIZE - 1) / BLOCK_SIZE), n->node->name); #endif /* Libisofs_ce_calc_debuG */ /* Crossed a block boundary */ *su_size = su_mem; *ce = ce_mem; t->curr_ce_entries = curr_ce_entries_mem; return 0; } } return 1; unannounced_ca:; *su_size = su_mem; *ce = ce_mem; t->curr_ce_entries = curr_ce_entries_mem; return 0; } /* @param flag bit0= Do not add data but only count sua_free and ce_len param info may be NULL in this case bit1= account for crossing block boundaries (implied by bit0 == 0) */ static int add_aa_string(Ecma119Image *t, Ecma119Node *n, struct susp_info *info, size_t *sua_free, size_t *ce_len, size_t base_ce, int flag) { int ret; uint8_t *aapt; void *xipt; size_t num_aapt= 0; if (!t->opts->aaip) return 1; ret = iso_node_get_xinfo(n->node, aaip_xinfo_func, &xipt); if (ret == 1) { num_aapt = aaip_count_bytes((unsigned char *) xipt, 0); if (num_aapt > 0) { if (flag & 1) { aapt = (unsigned char *) xipt; ret = aaip_add_AL(t, NULL, &aapt, num_aapt, sua_free, ce_len, base_ce, flag & (1 | 2)); } else { aapt = malloc(num_aapt); if (aapt == NULL) return ISO_OUT_OF_MEM; memcpy(aapt, xipt, num_aapt); ret = aaip_add_AL(t, info, &aapt, num_aapt, sua_free, ce_len, base_ce, 0); /* aapt is NULL now and the memory is owned by t */ } if (ret < 0) return ret; } } return 1; } static void iso_msg_too_many_ce(Ecma119Image *t, Ecma119Node *n, int err) { if (n->node->name != NULL) { iso_msg_submit(t->image->id, err, 0, "Too many CE entries for file with name: %s", n->node->name); } else { iso_msg_submit(t->image->id, err, 0, "Too many CE entries for a single file", n->node->name); } } /** * Compute the length needed for write all RR and SUSP entries for a given * node. * * @param type * 0 normal entry, 1 "." entry for that node (it is a dir), 2 ".." * for that node (i.e., it will refer to the parent) * @param used_up * Already occupied space in the directory record. * @param ce * Will be filled with the space needed in a CE * @param base_ce * Predicted fill of continuation area by previous nodes of same dir * @return * The size needed for the RR entries in the System Use Area */ ssize_t rrip_calc_len(Ecma119Image *t, Ecma119Node *n, int type, size_t used_up, size_t *ce, size_t base_ce) { size_t su_size, space; int ret, retry = 0; size_t aaip_sua_free= 0, aaip_len= 0; try_again: /* Directory record length must be even (ECMA-119, 9.1.13). Maximum is 254. */ space = 254 - used_up - (used_up % 2); if (type < 0 || type > 2 || space < ISO_ROCKRIDGE_IN_DIR_REC) { iso_msg_submit(t->image->id, ISO_ASSERT_FAILURE, 0, "Unknown node type %d or short RR space %d < %d in directory record", type, (int) space, ISO_ROCKRIDGE_IN_DIR_REC); return ISO_ASSERT_FAILURE; } *ce = 0; su_size = 0; t->curr_ce_entries = 0; /* If AAIP enabled and announced by ER : account for 5 bytes of ES */; if (t->opts->aaip && !t->opts->aaip_susp_1_10) su_size += 5; #ifdef Libisofs_with_rrip_rR /* obsolete RR field (once in RRIP-1.09) */ su_size += 5; #endif /* PX and TF, we are sure they always fit in SUA */ if (t->opts->rrip_1_10_px_ino || !t->opts->rrip_version_1_10) { su_size += 44 + 26; } else { su_size += 36 + 26; } if (n->type == ECMA119_DIR) { if (n->info.dir->real_parent != NULL) { /* it is a reallocated entry */ if (type == 2) { /* we need to add a PL entry */ su_size += 12; } else if (type == 0) { /* we need to add a RE entry */ su_size += 4; } } else if(ecma119_is_dedicated_reloc_dir(t, n) && (t->opts->rr_reloc_flags & 1)) { /* The dedicated relocation directory shall be marked by RE */ su_size += 4; } } else if (n->type == ECMA119_SPECIAL) { if (S_ISBLK(n->node->mode) || S_ISCHR(n->node->mode)) { /* block or char device, we need a PN entry */ su_size += 20; } } else if (n->type == ECMA119_PLACEHOLDER) { /* we need the CL entry */ su_size += 12; } if (type == 0) { /* Try without CE */ ret = susp_calc_nm_sl_al(t, n, space, &su_size, ce, base_ce, 0); if (ret == 0) /* Retry with CE but no block crossing */ ret = susp_calc_nm_sl_al(t, n, space, &su_size, ce, base_ce, 1); if (ret == 0) /* Retry with aligned CE and block hopping */ ret = susp_calc_nm_sl_al(t, n, space, &su_size, ce, base_ce, 1 | 2); if (ret == -2) return ISO_OUT_OF_MEM; /* -1 should not occur. By tradition it would not cause return */ if (ret < -2) { if (n->node->name != NULL) iso_msg_submit(t->image->id, ret, 0, "SUSP planning failed for file with name: %s", n->node->name); return ret; } } else { /* "." or ".." entry */ if (!t->opts->rrip_version_1_10) su_size += 5; /* NM field */ if (type == 1 && n->parent == NULL) { /* * "." for root directory * we need to write SP and ER entries. The first fits in SUA, * ER needs a Continuation Area, thus we also need a CE entry */ su_size += 7 + 28; /* SP + CE */ t->curr_ce_entries++; /* ER of RRIP */ if (t->opts->rrip_version_1_10) { *ce = 237; } else { *ce = 182; } if (t->opts->aaip && !t->opts->aaip_susp_1_10) { *ce += 160; /* ER of AAIP */ } /* Compute length of AAIP string of root node. Will write all AIIP to CA, which already starts at block boundary. So no need for three tries. */ aaip_sua_free= 0; ret = add_aa_string(t, n, NULL, &aaip_sua_free, &aaip_len, base_ce, 1 | 2); if (ret < 0) return ret; *ce += aaip_len; } } if (t->curr_ce_entries > t->opts->max_ce_entries) { /* If permitted by API setting: Remove non-isofs-non-ACL fattr */ retry++; if (retry == 1) { if ((t->opts->max_ce_drop_attr & 15) >= 1) { ret = iso_node_remove_fattr(n->node, 0); if (ret > 0) { iso_msg_too_many_ce(t, n, ISO_CE_REMOVING_ATTR); iso_msg_submit(t->image->id, ISO_CE_REMOVING_ATTR, 0, "Removed non-isofs attributes"); goto try_again; } } } else if (retry == 2) { if ((t->opts->max_ce_drop_attr & 15) >= 2) { ret = iso_node_remove_fattr(n->node, 1 | 2); if (ret > 0) { iso_msg_submit(t->image->id, ISO_CE_REMOVING_ATTR, 0, "Removed ACL and attribute flags"); goto try_again; } } } iso_msg_too_many_ce(t, n, ISO_TOO_MANY_CE); return (ssize_t) (int) ISO_TOO_MANY_CE; } else if (t->curr_ce_entries >= 32) { if (n->node->name != NULL) iso_msg_submit(t->image->id, ISO_TOO_MANY_CE_FOR_LINUX, 0, "SUSP planning risky for file with name: %s", n->node->name); iso_msg_submit(t->image->id, ISO_TOO_MANY_CE_FOR_LINUX, 0, "Too many CE entries for single file when mounted by Linux"); } /* * The System Use field inside the directory record must be padded if * it is an odd number (ECMA-119, 9.1.13) */ su_size += (su_size % 2); return su_size; } /** * Free all info in a struct susp_info. */ static void susp_info_free(struct susp_info* susp) { size_t i; for (i = 0; i < susp->n_susp_fields; ++i) { free(susp->susp_fields[i]); } free(susp->susp_fields); for (i = 0; i < susp->n_ce_susp_fields; ++i) { free(susp->ce_susp_fields[i]); } free(susp->ce_susp_fields); } /** * Fill a struct susp_info with the RR/SUSP entries needed for a given * node. * * @param type * 0 normal entry, 1 "." entry for that node (it is a dir), 2 ".." * for that node (i.e., it will refer to the parent) * @param used_up * Already occupied space in the directory record. * @param info * Pointer to the struct susp_info where the entries will be stored. * If some entries need to go to a Continuation Area, they will be added * to the existing ce_susp_fields, and ce_len will be incremented * properly. Please ensure ce_block is initialized properly. * @return * 1 success, < 0 error */ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type, size_t used_up, struct susp_info *info) { int ret; size_t i; Ecma119Node *node; char *name = NULL; char *dest = NULL; size_t aaip_er_len= 0; size_t rrip_er_len= 182; size_t su_size_pd, ce_len_pd; /* predicted sizes of SUA and CA */ int ce_is_predicted = 0; size_t aaip_sua_free= 0, aaip_len= 0, ce_mem; int space; if (t == NULL || n == NULL || info == NULL) { return ISO_NULL_POINTER; } /* Directory record length must be even (ECMA-119, 9.1.13). Maximum is 254. */ space = 254 - used_up - (used_up % 2); if (type < 0 || type > 2 || space < ISO_ROCKRIDGE_IN_DIR_REC) { iso_msg_submit(t->image->id, ISO_ASSERT_FAILURE, 0, "Unknown node type %d or short RR space %d < %d in directory record", type, space, ISO_ROCKRIDGE_IN_DIR_REC); return ISO_ASSERT_FAILURE; } /* Mark start index of node's continuation area for later update */ info->current_ce_start = info->n_ce_susp_fields; ce_mem = info->ce_len; #ifdef Libisofs_ce_calc_debug_filetraP if (n->node->name != NULL) if (strcmp(n->node->name, "...put.leafname.here...") == 0) fprintf(stderr, "libburn_DEBUG: filename breakpoint\n"); #endif /* Libisofs_ce_calc_debug_filetraP */ if (type == 2 && n->parent != NULL) { node = n->parent; } else { node = n; } /* * SP must be the first entry for the "." record of the root directory * (SUSP, 5.3) */ if (type == 1 && n->parent == NULL) { ret = susp_add_SP(t, info); if (ret < 0) { goto add_susp_cleanup; } } /* If AAIP enabled and announced by ER : Announce RRIP by ES */ if (t->opts->aaip && !t->opts->aaip_susp_1_10) { ret = susp_add_ES(t, info, 0, 0); if (ret < 0) goto add_susp_cleanup; } #ifdef Libisofs_with_rrip_rR ret = rrip_add_RR(t, node, info); if (ret < 0) { goto add_susp_cleanup; } #endif /* Libisofs_with_rrip_rR */ /* PX and TF, we are sure they always fit in SUA */ ret = rrip_add_PX(t, node, info); if (ret < 0) { goto add_susp_cleanup; } ret = rrip_add_TF(t, node, info); if (ret < 0) { goto add_susp_cleanup; } if (n->type == ECMA119_DIR) { if (n->info.dir->real_parent != NULL) { /* it is a reallocated entry */ if (type == 2) { /* * we need to add a PL entry * Note that we pass "n" as parameter, not "node" */ ret = rrip_add_PL(t, n, info); if (ret < 0) { goto add_susp_cleanup; } } else if (type == 0) { /* we need to add a RE entry */ ret = rrip_add_RE(t, node, info); if (ret < 0) { goto add_susp_cleanup; } } } else if(ecma119_is_dedicated_reloc_dir(t, n) && (t->opts->rr_reloc_flags & 1)) { /* The dedicated relocation directory shall be marked by RE */ ret = rrip_add_RE(t, node, info); if (ret < 0) goto add_susp_cleanup; } } else if (n->type == ECMA119_SPECIAL) { if (S_ISBLK(n->node->mode) || S_ISCHR(n->node->mode)) { /* block or char device, we need a PN entry */ ret = rrip_add_PN(t, node, info); if (ret < 0) { goto add_susp_cleanup; } } } else if (n->type == ECMA119_PLACEHOLDER) { /* we need the CL entry */ ret = rrip_add_CL(t, node, info); if (ret < 0) { goto add_susp_cleanup; } } if (info->suf_len + 28 > space) { iso_msg_submit(t->image->id, ISO_ASSERT_FAILURE, 0, "Directory Record overflow. name='%s' , suf_len=%d > space=%d - 28\n", node->iso_name, (int) info->suf_len, space); return ISO_ASSERT_FAILURE; } if (type == 0) { size_t sua_free; /* free space in the SUA */ int nm_type = 0; /* 0 whole entry in SUA, 1 part in CE */ size_t ce_len = 0; /* len of the CE */ size_t namelen; /* this two are only defined for symlinks */ uint8_t **comps= NULL; /* components of the SL field */ size_t n_comp = 0; /* number of components */ namelen = 0; name = get_rr_fname(t, n->node->name); if (name == NULL) name = strdup(""); if (name == NULL) { ret = ISO_OUT_OF_MEM; goto add_susp_cleanup; } namelen = strlen(name); sua_free = space - info->suf_len; /* Try whether NM, SL, AL will fit into SUA */ su_size_pd = info->suf_len; ce_len_pd = ce_len; ret = susp_calc_nm_sl_al(t, n, (size_t) space, &su_size_pd, &ce_len_pd, info->ce_len, 0); if (ret == 0) { /* Have to use CA. 28 bytes of CE are necessary */ ret = susp_calc_nm_sl_al(t, n, (size_t) space, &su_size_pd, &ce_len_pd, info->ce_len, 1); if (ret == 0) /* Retry with aligned CE and block hopping */ ret = susp_calc_nm_sl_al(t, n, (size_t) space, &su_size_pd, &ce_len_pd, info->ce_len, 1 | 2); sua_free -= 28; ce_is_predicted = 1; } if (ret == -2) { ret = ISO_OUT_OF_MEM; goto add_susp_cleanup; } /* -1 should not occur. By tradition it would not cause return */ if (ret < -2) goto add_susp_cleanup; /* NM entry */ if (5 + namelen <= sua_free) { /* ok, it fits in System Use Area */ sua_free -= (5 + namelen); nm_type = 0; } else { /* the NM will be divided in a CE */ nm_type = 1; namelen = namelen - (sua_free - 5); ce_len = 5 + namelen; sua_free = 0; } if (n->type == ECMA119_SYMLINK) { /* * for symlinks, we also need to write the SL */ char *cur, *prev; size_t sl_len = 5; int cew = (nm_type == 1); /* are we writing to CE? */ dest = get_rr_fname(t, ((IsoSymlink*)n->node)->dest); if (dest == NULL) dest = strdup(""); if (dest == NULL) { ret = ISO_OUT_OF_MEM; goto add_susp_cleanup; } prev = dest; cur = strchr(prev, '/'); while (1) { size_t clen; char cflag = 0; /* component flag (RRIP, 4.1.3.1) */ if (cur) { clen = cur - prev; } else { /* last component */ clen = strlen(prev); } if (clen == 0) { /* this refers to the roor directory, '/' */ cflag = 1 << 3; } if (clen == 1 && prev[0] == '.') { clen = 0; cflag = 1 << 1; } else if (clen == 2 && prev[0] == '.' && prev[1] == '.') { clen = 0; cflag = 1 << 2; } /* flags and len for each component record (RRIP, 4.1.3.1) */ clen += 2; if (!cew) { /* we are still writing to the SUA */ if (sl_len + clen > sua_free) { /* * ok, we need a Continuation Area anyway * TODO this can be handled better, but for now SL * will be completely moved into the CA */ /* sua_free, ce_len, nm_type already account for CE */ cew = 1; } else { /* add the component */ ret = rrip_SL_append_comp(&n_comp, &comps, prev, clen - 2, cflag); if (ret < 0) { goto add_susp_cleanup; } sl_len += clen; } } if (cew) { if (sl_len + clen > 255) { /* we need an addition SL entry */ if (clen > 250) { /* * case 1, component too large to fit in a * single SL entry. Thus, the component need * to be divided anyway. * Note than clen can be up to 255 + 2 = 257. * * First, we check how many bytes fit in current * SL field */ ssize_t fit = 255 - sl_len - 2; if ((ssize_t) (clen - 250) <= fit) { /* * the component can be divided between this * and another SL entry */ ret = rrip_SL_append_comp(&n_comp, &comps, prev, fit, 0x01); if (ret < 0) { goto add_susp_cleanup; } /* * and another component, that will go in * other SL entry */ ret = rrip_SL_append_comp(&n_comp, &comps, prev + fit, clen - fit - 2, 0); if (ret < 0) { goto add_susp_cleanup; } ce_len += 255; /* this SL, full */ sl_len = 5 + (clen - fit); } else { /* * the component will need a 2rd SL entry in * any case, so we prefer to don't write * anything in this SL */ ret = rrip_SL_append_comp(&n_comp, &comps, prev, 248, 0x01); if (ret < 0) { goto add_susp_cleanup; } ret = rrip_SL_append_comp(&n_comp, &comps, prev + 248, strlen(prev + 248), 0x00); if (ret < 0) { goto add_susp_cleanup; } ce_len += sl_len + 255; sl_len = 5 + (clen - 250) + 2; } } else { /* case 2, create a new SL entry */ ret = rrip_SL_append_comp(&n_comp, &comps, prev, clen - 2, cflag); if (ret < 0) { goto add_susp_cleanup; } ce_len += sl_len; sl_len = 5 + clen; } } else { /* the component fit in the SL entry */ ret = rrip_SL_append_comp(&n_comp, &comps, prev, clen - 2, cflag); if (ret < 0) { goto add_susp_cleanup; } sl_len += clen; } } if (!cur || cur[1] == '\0') { /* cur[1] can be \0 if dest ends with '/' */ break; } prev = cur + 1; cur = strchr(prev, '/'); } if (cew) { ce_len += sl_len; } } /* * We we reach here: * - We know if NM fill in the SUA (nm_type == 0) * - If SL needs an to be written in CE (ce_len > 0) * - The components for SL entry (or entries) */ if (nm_type == 0) { /* the full NM fills in SUA */ ret = rrip_add_NM(t, info, name, strlen(name), 0, 0); if (ret < 0) { goto add_susp_cleanup; } } else { /* * Write the NM part that fits in SUA... Note that CE * entry and NM in the continuation area is added below */ namelen = space - info->suf_len - 28 * (!!ce_is_predicted) - 5; ret = rrip_add_NM(t, info, name, namelen, 1, 0); if (ret < 0) { goto add_susp_cleanup; } } if (ce_is_predicted) { /* Add the CE entry */ ret = susp_add_CE(t, ce_len_pd, info); if (ret < 0) { goto add_susp_cleanup; } } if (nm_type == 1) { /* * ..and the part that goes to continuation area. */ /* >>> SUPER_LONG_RR : Need a loop to handle CA lengths > 250 (This cannot happen with name lengths <= 256, as a part of the name will always fit into the directory entry.) */; ret = rrip_add_NM(t, info, name + namelen, strlen(name + namelen), 0, 1); if (ret < 0) { goto add_susp_cleanup; } } if (n->type == ECMA119_SYMLINK) { /* add the SL entry (or entries) */ ret = rrip_add_SL(t, info, comps, n_comp, (ce_len > 0)); /* free the components */ for (i = 0; i < n_comp; i++) { free(comps[i]); } free(comps); if (ret < 0) { goto add_susp_cleanup; } } /* Eventually write zisofs ZF field */ ret = add_zf_field(t, n, info, &sua_free, &ce_len, ce_mem, 0); if (ret < 0) goto add_susp_cleanup; /* Eventually obtain AAIP field string from node and write it to directory entry or CE area. */ ret = add_aa_string(t, n, info, &sua_free, &ce_len, ce_mem, 0); if (ret < 0) goto add_susp_cleanup; } else { /* "." or ".." entry */ /* write the NM entry */ if (t->opts->rrip_version_1_10) { /* RRIP-1.10: "NM" System Use Fields recorded for the ISO 9660 directory records with names (00) and (01), used to designate the current and parent directories, respectively, should be ignored. Instead, the receiving system should convert these names to the appropriate receiving system-dependent designations for the current and parent directories. */ /* mkisofs obviously writes no NM for '.' and '..' . Program isoinfo shows empty names with records as of RRIP-1.12 */ /* no op */; } else { /* RRIP-1.12: If the ISO 9660 Directory Record File Identifier is (00), then the CURRENT bit of the "NM" Flags field [...], if present, shall be set to ONE. If the ISO 9660 Directory Record File Identifier is (01), then the PARENT bit of the "NM" Flags field [...], if present, shall be set to ONE. [...] "BP 3 - Length (LEN_NM)" shall specify as an 8-bit number the length in bytes [...]. If bit position 1, 2, or 5 of the "NM" Flags is set to ONE, the value of this field shall be 5 and no Name Content shall be recorded. [The CURRENT bit has position 1. The PARENT bit has position 2.] */ ret = rrip_add_NM(t, info, NULL, 0, 1 << type, 0); if (ret < 0) goto add_susp_cleanup; } if (type == 1 && n->parent == NULL) { /* * "." for root directory * we need to write SP and ER entries. The first fits in SUA, * ER needs a Continuation Area, thus we also need a CE entry. * Note that SP entry was already added above */ if (t->opts->rrip_version_1_10) { rrip_er_len = 237; } else { rrip_er_len = 182; } if (t->opts->aaip && !t->opts->aaip_susp_1_10) { aaip_er_len = 160; } /* Compute length of AAIP string of root node */ aaip_sua_free= 0; /* (just to give t->curr_ce_entries a defined state) */ t->curr_ce_entries = 0; ret = add_aa_string(t, n, NULL, &aaip_sua_free, &aaip_len, ce_mem, 1 | 2); if (ret < 0) goto add_susp_cleanup; /* Allocate the necessary CE space */ ret = susp_add_CE(t, rrip_er_len + aaip_er_len + aaip_len, info); if (ret < 0) { goto add_susp_cleanup; } ret = rrip_add_ER(t, info); if (ret < 0) { goto add_susp_cleanup; } if (t->opts->aaip && !t->opts->aaip_susp_1_10) { ret = aaip_add_ER(t, info, 0); if (ret < 0) { goto add_susp_cleanup; } } /* Write AAIP string of root node */ aaip_sua_free= aaip_len= 0; ret = add_aa_string(t, n, info, &aaip_sua_free, &aaip_len, ce_mem, 0); if (ret < 0) goto add_susp_cleanup; } } /* * The System Use field inside the directory record must be padded if * it is an odd number (ECMA-119, 9.1.13) */ info->suf_len += (info->suf_len % 2); free(name); free(dest); return ISO_SUCCESS; add_susp_cleanup: ; if (name != NULL) free(name); if (dest != NULL) free(dest); susp_info_free(info); return ret; } /* Update the sizes of CE fields at end of info->susp_fields and in single node range of info->ce_susp_fields. */ static int susp_update_CE_sizes(Ecma119Image *t, struct susp_info *info, int flag) { size_t i, curr_pos; uint8_t *curr_ce; uint32_t size; if (info->n_susp_fields == 0 || info->n_ce_susp_fields - info->current_ce_start == 0) return ISO_SUCCESS; for (i = 0; i < info->n_susp_fields; i++) if (info->susp_fields[i][0] == 'C') if(info->susp_fields[i][1] == 'E') break; if (i >= info->n_susp_fields) { iso_msg_submit(t->image->id, ISO_ASSERT_FAILURE, 0, "System Use Area field contains no CE, but there are fields in Continuation Area"); return ISO_ASSERT_FAILURE; } curr_ce = info->susp_fields[i]; curr_pos = 0; for (i = info->current_ce_start; i < info->n_ce_susp_fields; i++) { if (info->ce_susp_fields[i][0] == 0) { /* ignore pseudo SUSP PAD */ continue; } if (info->ce_susp_fields[i][0] == 'C' && info->ce_susp_fields[i][1] == 'E') { size = (curr_pos + info->ce_susp_fields[i][2]) % BLOCK_SIZE; if (size == 0) size = BLOCK_SIZE; iso_bb(curr_ce + 20, size, 4); curr_ce = info->ce_susp_fields[i]; /* Start a new CE Area */ curr_pos = 0; continue; } curr_pos = (curr_pos + info->ce_susp_fields[i][2]) % 2048; } if (curr_pos > 0) { size = curr_pos; if (size > BLOCK_SIZE) { /* Should never happen */ iso_msg_submit(t->image->id, ISO_WRONG_RR_WARN, 0, "Encountered and truncated oversized Continuation Area"); size = BLOCK_SIZE; } iso_bb(curr_ce + 20, size, 4); } return ISO_SUCCESS; } /** * Write the given SUSP fields into buf. Note that Continuation Area * fields are not written. * If info does not contain any SUSP entry this function just return. * After written, the info susp_fields array will be freed, and the counters * updated properly. */ void rrip_write_susp_fields(Ecma119Image *t, struct susp_info *info, uint8_t *buf) { size_t i; size_t pos = 0; int ret; if (info->n_susp_fields == 0) { return; } ret = susp_update_CE_sizes(t, info, 0); if (ret < 0) return; for (i = 0; i < info->n_susp_fields; i++) { memcpy(buf + pos, info->susp_fields[i], info->susp_fields[i][2]); pos += info->susp_fields[i][2]; } /* free susp_fields */ for (i = 0; i < info->n_susp_fields; ++i) { free(info->susp_fields[i]); } free(info->susp_fields); info->susp_fields = NULL; info->n_susp_fields = 0; info->suf_len = 0; } /** * Write the Continuation Area entries for the given struct susp_info, using * the iso_write() function. * After written, the ce_susp_fields array will be freed. */ int rrip_write_ce_fields(Ecma119Image *t, struct susp_info *info) { size_t i; uint8_t *padding = NULL; int ret= ISO_SUCCESS; uint64_t written = 0, pad_size; if (info->n_ce_susp_fields == 0) { goto ex; } LIBISO_ALLOC_MEM(padding, uint8_t, BLOCK_SIZE); for (i = 0; i < info->n_ce_susp_fields; i++) { if (info->ce_susp_fields[i][0] == 0) { /* Pseudo field: pad up to next block boundary */ pad_size = BLOCK_SIZE - (written % BLOCK_SIZE); if (pad_size == BLOCK_SIZE) continue; memset(padding, 0, pad_size); ret = iso_write(t, padding, pad_size); if (ret < 0) goto write_ce_field_cleanup; written += pad_size; continue; } ret = iso_write(t, info->ce_susp_fields[i], info->ce_susp_fields[i][2]); if (ret < 0) { goto write_ce_field_cleanup; } written += info->ce_susp_fields[i][2]; } /* pad continuation area until block size */ i = BLOCK_SIZE - (info->ce_len % BLOCK_SIZE); if (i > 0 && i < BLOCK_SIZE) { memset(padding, 0, i); ret = iso_write(t, padding, i); if (ret < 0) goto write_ce_field_cleanup; written += i; } write_ce_field_cleanup: ; /* free ce_susp_fields */ for (i = 0; i < info->n_ce_susp_fields; ++i) { free(info->ce_susp_fields[i]); } free(info->ce_susp_fields); info->ce_susp_fields = NULL; info->n_ce_susp_fields = 0; info->alloc_ce_susp_fields = 0; info->ce_len = 0; ex:; LIBISO_FREE_MEM(padding); return ret; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2007 Mario Danic * Copyright (c) 2009 - 2023 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /** * This header defines the functions and structures needed to add RockRidge * extensions to an ISO image. It also handles AAIP and zisofs extensions. * * References: * * - SUSP (IEEE 1281). * System Use Sharing Protocol, draft standard version 1.12. * See ftp://ftp.ymi.com/pub/rockridge/susp112.ps * * - RRIP (IEEE 1282) * Rock Ridge Interchange Protocol, Draft Standard version 1.12. * See ftp://ftp.ymi.com/pub/rockridge/rrip112.ps * * - ECMA-119 (ISO-9660) * Volume and File Structure of CDROM for Information Interchange. See * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf * * - AAIP * Arbitrary Attribute Interchange Protocol. See doc/susp_aaip_2_0.txt * * - zisofs * Blockwise compression of data file content with transparent read support * in the Linux kernel. See doc/zisofs_format.txt * */ #ifndef LIBISO_ROCKRIDGE_H #define LIBISO_ROCKRIDGE_H #include "ecma119.h" /* For ssize_t */ #include #define SUSP_SIG(entry, a, b) ((entry->sig[0] == a) && (entry->sig[1] == b)) /** * This contains the information about the System Use Fields (SUSP, 4.1), * that will be written in the System Use Areas, both in the ISO directory * record System Use field (ECMA-119, 9.1.13) or in a Continuation Area as * defined by SUSP. */ struct susp_info { /** Number of SUSP fields in the System Use field */ size_t n_susp_fields; uint8_t **susp_fields; /** Length of the part of the SUSP area that fits in the dirent. */ int suf_len; /** Length of the part of the SUSP area that will go in a CE area. */ uint32_t ce_block; uint32_t ce_len; /* Storage for Continuation Area for a whole directory */ size_t n_ce_susp_fields; uint8_t **ce_susp_fields; /* The number of allocated members in ce_susp_fields */ size_t alloc_ce_susp_fields; /* Marks the start index in ce_susp_fields of the current node */ size_t current_ce_start; }; /* Step to increase allocated size of susp_info.ce_susp_fields */ #define ISO_SUSP_CE_ALLOC_STEP 16 /* SUSP 5.1 */ struct susp_CE { uint8_t block[8]; uint8_t offset[8]; uint8_t len[8]; }; /* SUSP 5.3 */ struct susp_SP { uint8_t be[1]; uint8_t ef[1]; uint8_t len_skp[1]; }; /* SUSP 5.5 */ struct susp_ER { uint8_t len_id[1]; uint8_t len_des[1]; uint8_t len_src[1]; uint8_t ext_ver[1]; uint8_t ext_id[1]; /*< up to len_id bytes */ /* ext_des, ext_src */ }; /** POSIX file attributes (RRIP, 4.1.1) */ struct rr_PX { uint8_t mode[8]; uint8_t links[8]; uint8_t uid[8]; uint8_t gid[8]; uint8_t serial[8]; }; /** Time stamps for a file (RRIP, 4.1.6) */ struct rr_TF { uint8_t flags[1]; uint8_t t_stamps[1]; }; /** Info for character and block device (RRIP, 4.1.2) */ struct rr_PN { uint8_t high[8]; uint8_t low[8]; }; /** Alternate name (RRIP, 4.1.4) */ struct rr_NM { uint8_t flags[1]; uint8_t name[1]; }; /** Link for a relocated directory (RRIP, 4.1.5.1) */ struct rr_CL { uint8_t child_loc[8]; }; /** Sim link (RRIP, 4.1.3) */ struct rr_SL { uint8_t flags[1]; uint8_t comps[1]; }; /** Outdated Arbitrary Attribute (AAIP, see doc/susp_aaip_1_0.txt) * It collided with pre-SUSP Apple AA field. */ struct aaip_AA { uint8_t flags[1]; uint8_t comps[1]; }; /** Arbitrary Attribute (AAIP, see doc/susp_aaip_2_0.txt) */ struct aaip_AL { uint8_t flags[1]; uint8_t comps[1]; }; /** zisofs entry (see doc/zisofs_format.txt) */ struct zisofs_ZF { uint8_t parameters[1]; /* begins with BP 5 */ }; /** * Struct for a SUSP System User Entry (SUSP, 4.1) */ struct susp_sys_user_entry { uint8_t sig[2]; uint8_t len_sue[1]; uint8_t version[1]; union { struct susp_CE CE; struct susp_SP SP; struct susp_ER ER; struct rr_PX PX; struct rr_TF TF; struct rr_PN PN; struct rr_NM NM; struct rr_CL CL; struct rr_SL SL; struct aaip_AA AA; struct aaip_AL AL; struct zisofs_ZF ZF; } data; /* 5 to 4+len_sue */ }; /** * Compute the length needed for write all RR and SUSP entries for a given * node. * * @param type * 0 normal entry, 1 "." entry for that node (it is a dir), 2 ".." * for that node (i.e., it will refer to the parent) * @param space * Available space in the System Use Area for the directory record. * @param ce * Will be filled with the space needed in a CE * @param base_ce * Fill of continuation area by previous nodes of same dir * @return * The size needed for the RR entries in the System Use Area */ ssize_t rrip_calc_len(Ecma119Image *t, Ecma119Node *n, int type, size_t space, size_t *ce, size_t base_ce); /** * Fill a struct susp_info with the RR/SUSP entries needed for a given * node. * * @param type * 0 normal entry, 1 "." entry for that node (it is a dir), 2 ".." * for that node (i.e., it will refer to the parent) * @param space * Available space in the System Use Area for the directory record. * @param info * Pointer to the struct susp_info where the entries will be stored. * If some entries need to go to a Continuation Area, they will be added * to the existing ce_susp_fields, and ce_len will be incremented * properly. Please ensure ce_block is initialized properly. * @return * 1 success, < 0 error */ int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type, size_t space, struct susp_info *info); /** * Write the given SUSP fields into buf. Note that Continuation Area * fields are not written. * If info does not contain any SUSP entry this function just return. * After written, the info susp_fields array will be freed, and the counters * updated properly. */ void rrip_write_susp_fields(Ecma119Image *t, struct susp_info *info, uint8_t *buf); /** * Write the Continuation Area entries for the given struct susp_info, using * the iso_write() function. * After written, the ce_susp_fields array will be freed. */ int rrip_write_ce_fields(Ecma119Image *t, struct susp_info *info); /** * The SUSP iterator is used to iterate over the System User Entries * of a ECMA-168 directory record. * It takes care about Continuation Areas, handles the end of the different * system user entries and skip padding areas. Thus, using an iteration * we are accessing just to the meaning entries. */ typedef struct susp_iterator SuspIterator; SuspIterator * susp_iter_new(IsoDataSource *src, struct ecma119_dir_record *record, uint32_t fs_blocks, uint8_t len_skp, int msgid); /** * Get the next SUSP System User Entry using given iterator. * * @param sue * Pointer to the next susp entry. It refers to an internal buffer and * it's not guaranteed to be allocated after calling susp_iter_next() * again. Thus, if you need to keep some entry you have to do a copy. * @return * 1 on success, 0 if no more entries, < 0 error */ int susp_iter_next(SuspIterator *iter, struct susp_sys_user_entry **sue, int flag); /** * Free a given susp iterator. */ void susp_iter_free(SuspIterator *iter); /** * Fills a struct stat with the values of a Rock Ridge PX entry (RRIP, 4.1.1). * * @return * < 0 on error * 1 on success with no inode number, * 2 on success with inode number, */ int read_rr_PX(struct susp_sys_user_entry *px, struct stat *st); /** * Fills a struct stat with the values of a Rock Ridge TF entry (RRIP, 4.1.6) * * @return * 1 on success, < 0 on error */ int read_rr_TF(struct susp_sys_user_entry *tf, struct stat *st); /** * Read a RR NM entry (RRIP, 4.1.4), and appends the name stored there to * the given name. You can pass a pointer to NULL as name. * * @return * 1 on success, < 0 on error */ int read_rr_NM(struct susp_sys_user_entry *nm, char **name, int *cont); /** * Read a SL RR entry (RRIP, 4.1.3), checking if the destination continues. * * @param cont * 0 not continue, 1 continue, 2 continue component * @return * 1 on success, < 0 on error */ int read_rr_SL(struct susp_sys_user_entry *sl, char **dest, int *cont); /** * Fills a struct stat with the values of a Rock Ridge PN entry (RRIP, 4.1.2). * * @return * 1 on success, < 0 on error */ int read_rr_PN(struct susp_sys_user_entry *pn, struct stat *st); /** * Collects the AAIP field string from single AAIP fields. * (see doc/susp_aaip_1_0.txt) * @param aa_string Storage location of the emerging string. * Begin with *aa_string == NULL, or own malloc() storage. * @param aa_size Current allocated size of aa_string. * Begin with *aa_size == 0, or own storage size. * @param aa_len Current occupied size of aa_string. * Begin with *aa_len == 0 * @param prev_field Returns the index of start of the previous field * in the string. * @param is_done The current completion state of the AAIP field string. * Fields will be ignored as soon as it is 1. * Begin with *is_done == 0 * @param flag Unused yet. Submit 0. * @return * 1 on success, < 0 on error */ int read_aaip_AA(struct susp_sys_user_entry *sue, unsigned char **aa_string, size_t *aa_size, size_t *aa_len, size_t *prev_field, int *is_done, int flag); /** * Collects the AAIP field string from single AL fields. * (see doc/susp_aaip_2_0.txt) */ int read_aaip_AL(struct susp_sys_user_entry *sue, unsigned char **aa_string, size_t *aa_size, size_t *aa_len, size_t *prev_field, int *is_done, int flag); /** * Reads the zisofs parameters from a ZF field (see doc/zisofs_format.txt). * * @return * 1 on success, < 0 on error */ int read_zisofs_ZF(struct susp_sys_user_entry *zf, uint8_t algorithm[2], uint8_t *header_size_div4, uint8_t *block_size_log2, uint64_t *uncompressed_size, int flag); /** * Convert a RR filename to the requested charset. * @param flag bit0= do not issue error messages */ int iso_get_rr_name(IsoWriteOpts *opts, char *input_charset, char *output_charset, int imgid, char *str, char **name, int flag); #endif /* LIBISO_ROCKRIDGE_H */ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2023 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /* * This file contains functions related to the reading of SUSP, * Rock Ridge and AAIP extensions on an ECMA-119 image. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libisofs.h" #include "ecma119.h" #include "util.h" #include "rockridge.h" #include "messages.h" #include #include #include struct susp_iterator { uint8_t* base; int pos; int size; IsoDataSource *src; int msgid; /* Number of blocks in the ISO 9660 filesystem */ uint32_t fs_blocks; /* For detecting (nearly) endless loops */ uint32_t ce_counter; /* block and offset for next continuation area */ uint32_t ce_block; uint32_t ce_off; /** Length of the next continuation area, 0 if no more CA are specified */ uint32_t ce_len; uint8_t *buffer; /*< If there are continuation areas */ }; SuspIterator* susp_iter_new(IsoDataSource *src, struct ecma119_dir_record *record, uint32_t fs_blocks, uint8_t len_skp, int msgid) { int pad = (record->len_fi[0] + 1) % 2; struct susp_iterator *iter = malloc(sizeof(struct susp_iterator)); if (iter == NULL) { return NULL; } iter->base = record->file_id + record->len_fi[0] + pad; iter->pos = len_skp; /* 0 in most cases */ iter->size = record->len_dr[0] - record->len_fi[0] - 33 - pad; iter->src = src; iter->msgid = msgid; iter->fs_blocks = fs_blocks; iter->ce_counter = 0; iter->ce_len = 0; iter->ce_block = 0; iter->ce_off = 0; iter->buffer = NULL; return iter; } /* More than 1 MiB in a single file's CE area is suspicious */ #define ISO_SUSP_MAX_CE_BYTES (1024 * 1024) /* More than 100000 CE entries in a file is suspicious */ #define ISO_SUSP_MAX_CE_HOPS 100000 /* @param flag bit0 = First call on root: Not yet clear whether this is SUSP at all */ int susp_iter_next(SuspIterator *iter, struct susp_sys_user_entry **sue, int flag) { struct susp_sys_user_entry *entry; process_entry:; entry = (struct susp_sys_user_entry*)(iter->base + iter->pos); if (flag & 1) { /* Yet unclear whether it is SUSP at all */ if (iter->size < 7) return 0; if (!SUSP_SIG(entry, 'S', 'P')) return 0; if (entry->len_sue[0] < 7) return 0; /* Looks like SUSP enough to pass the further processing here. */ /* In case of CE hop do not run this check again */ flag &= ~1; } if ( (iter->pos + 4 > iter->size) || (SUSP_SIG(entry, 'S', 'T'))) { /* * End of the System Use Area or Continuation Area. * Note that ST is not needed when the space left is less than 4. * (IEEE 1281, SUSP. section 4) */ if (iter->ce_len) { uint32_t block, nblocks, skipped_blocks, skipped_bytes; /* A CE was found, there is another continuation area */ skipped_blocks = iter->ce_off / BLOCK_SIZE; skipped_bytes = skipped_blocks * BLOCK_SIZE; nblocks = DIV_UP(iter->ce_off - skipped_bytes + iter->ce_len, BLOCK_SIZE); if (nblocks <= 0 || iter->ce_len > ISO_SUSP_MAX_CE_BYTES) return ISO_SUSP_WRONG_CE_SIZE; if (((uint64_t) iter->ce_block) + skipped_blocks + nblocks > (uint64_t) iter->fs_blocks) return ISO_SUSP_WRONG_CE_SIZE; iter->buffer = realloc(iter->buffer, nblocks * BLOCK_SIZE); /* Read blocks needed to cache the given CE area range */ for (block = 0; block < nblocks; ++block) { int ret; ret = iter->src->read_block(iter->src, iter->ce_block + skipped_blocks + block, iter->buffer + block * BLOCK_SIZE); if (ret < 0) { return ret; } } iter->base = iter->buffer + (iter->ce_off - skipped_bytes); iter->pos = 0; iter->size = iter->ce_len; iter->ce_len = 0; entry = (struct susp_sys_user_entry*)iter->base; } else { return 0; } } if (entry->len_sue[0] == 0) { /* a wrong image with this lead us to a infinity loop */ iso_msg_submit(iter->msgid, ISO_WRONG_RR, 0, "Damaged RR/SUSP information."); return ISO_WRONG_RR; } iter->pos += entry->len_sue[0]; if (SUSP_SIG(entry, 'C', 'E')) { /* Continuation entry */ if (iter->ce_len) { int ret; ret = iso_msg_submit(iter->msgid, ISO_UNSUPPORTED_SUSP, 0, "More than one CE System user entry was found in a single " "System Use field or continuation area. This breaks SUSP " "standard and is not supported. Ignoring last CE. Maybe " "the image is damaged."); if (ret < 0) { return ret; } } else { iter->ce_block = iso_read_bb(entry->data.CE.block, 4, NULL); iter->ce_off = iso_read_bb(entry->data.CE.offset, 4, NULL); iter->ce_len = iso_read_bb(entry->data.CE.len, 4, NULL); } /* we don't want to return CE entry to the user */ if (++(iter->ce_counter) > ISO_SUSP_MAX_CE_HOPS) { iso_msg_submit(iter->msgid, ISO_WRONG_RR, 0, "Damaged RR/SUSP information: Too many CE hops."); return ISO_WRONG_RR; } goto process_entry; } else if (SUSP_SIG(entry, 'P', 'D')) { /* skip padding */ goto process_entry; } *sue = entry; return ISO_SUCCESS; } void susp_iter_free(SuspIterator *iter) { free(iter->buffer); free(iter); } /** * Fills a struct stat with the values of a Rock Ridge PX entry (RRIP, 4.1.1). * * @return * 1 on success, < 0 on error */ int read_rr_PX(struct susp_sys_user_entry *px, struct stat *st) { if (px == NULL || st == NULL) { return ISO_NULL_POINTER; } if (px->sig[0] != 'P' || px->sig[1] != 'X') { return ISO_WRONG_ARG_VALUE; } if (px->len_sue[0] != 44 && px->len_sue[0] != 36) { return ISO_WRONG_RR; } st->st_mode = iso_read_bb(px->data.PX.mode, 4, NULL); st->st_nlink = iso_read_bb(px->data.PX.links, 4, NULL); st->st_uid = iso_read_bb(px->data.PX.uid, 4, NULL); st->st_gid = iso_read_bb(px->data.PX.gid, 4, NULL); st->st_ino = 0; if (px->len_sue[0] == 44) { /* this corresponds to RRIP 1.12, so we have inode serial number */ st->st_ino = iso_read_bb(px->data.PX.serial, 4, NULL); /* Indicate that st_ino is valid */ return 2; } return 1; } /** * Fills a struct stat with the values of a Rock Ridge TF entry (RRIP, 4.1.6) * * @return * 1 on success, < 0 on error */ int read_rr_TF(struct susp_sys_user_entry *tf, struct stat *st) { time_t time; int s; int nts = 0; if (tf == NULL || st == NULL) { return ISO_NULL_POINTER; } if (tf->sig[0] != 'T' || tf->sig[1] != 'F') { return ISO_WRONG_ARG_VALUE; } if (tf->data.TF.flags[0] & (1 << 7)) { /* long form */ s = 17; } else { s = 7; } /* 1. Creation time */ if (tf->data.TF.flags[0] & (1 << 0)) { /* Linux accepts ctime by Creation time and by Attributes time. * If both are given, then Attribute time will win. */ if (tf->len_sue[0] < 5 + (nts+1) * s) { /* RR TF entry too short. */ return ISO_WRONG_RR; } if (s == 7) { time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]); } else { time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]); } st->st_ctime = time; ++nts; } /* 2. modify time */ if (tf->data.TF.flags[0] & (1 << 1)) { if (tf->len_sue[0] < 5 + (nts+1) * s) { /* RR TF entry too short. */ return ISO_WRONG_RR; } if (s == 7) { time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]); } else { time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]); } st->st_mtime = time; ++nts; } /* 3. access time */ if (tf->data.TF.flags[0] & (1 << 2)) { if (tf->len_sue[0] < 5 + (nts+1) * s) { /* RR TF entry too short. */ return ISO_WRONG_RR; } if (s == 7) { time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]); } else { time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]); } st->st_atime = time; ++nts; } /* 4. attributes time */ if (tf->data.TF.flags[0] & (1 << 3)) { if (tf->len_sue[0] < 5 + (nts+1) * s) { /* RR TF entry too short. */ return ISO_WRONG_RR; } if (s == 7) { time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]); } else { time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]); } st->st_ctime = time; ++nts; } /* we ignore backup, expire and effect times */ return ISO_SUCCESS; } /** * Read a RR NM entry (RRIP, 4.1.4), and appends the name stored there to * the given name. You can pass a pointer to NULL as name. * * @return * 1 on success, < 0 on error */ int read_rr_NM(struct susp_sys_user_entry *nm, char **name, int *cont) { if (nm == NULL || name == NULL) { return ISO_NULL_POINTER; } if (nm->sig[0] != 'N' || nm->sig[1] != 'M') { return ISO_WRONG_ARG_VALUE; } if (nm->len_sue[0] == 5) { if (nm->data.NM.flags[0] & 0x2) { /* it is a "." entry */ if (*name == NULL) { return ISO_SUCCESS; } else { /* we can't have a previous not-NULL name */ return ISO_WRONG_RR; } } } if (nm->len_sue[0] <= 5) { /* ".." entry is an error, as we will never call it */ return ISO_WRONG_RR; } /* concatenate the results */ if (*cont) { *name = realloc(*name, strlen(*name) + nm->len_sue[0] - 5 + 1); strncat(*name, (char*)nm->data.NM.name, nm->len_sue[0] - 5); } else { *name = iso_util_strcopy((char*)nm->data.NM.name, nm->len_sue[0] - 5); } if (*name == NULL) { return ISO_OUT_OF_MEM; } /* and set cond according to the value of CONTINUE flag */ *cont = nm->data.NM.flags[0] & 0x01; return ISO_SUCCESS; } /** * Read a SL RR entry (RRIP, 4.1.3), checking if the destination continues. * * @param cont * 0 not continue, 1 continue, 2 continue component * @return * 1 on success, < 0 on error */ int read_rr_SL(struct susp_sys_user_entry *sl, char **dest, int *cont) { int pos; if (sl == NULL || dest == NULL) { return ISO_NULL_POINTER; } if (sl->sig[0] != 'S' || sl->sig[1] != 'L') { return ISO_WRONG_ARG_VALUE; } for (pos = 0; pos + 5 < sl->len_sue[0]; pos += 2 + sl->data.SL.comps[pos + 1]) { char *comp; uint8_t len; uint8_t flags = sl->data.SL.comps[pos]; if (flags & 0x2) { /* current directory */ len = 1; comp = "."; } else if (flags & 0x4) { /* parent directory */ len = 2; comp = ".."; } else if (flags & 0x8) { /* root directory */ len = 1; comp = "/"; } else if (flags & ~0x01) { /* unsupported flag component */ return ISO_UNSUPPORTED_RR; } else { len = sl->data.SL.comps[pos + 1]; comp = (char*)&sl->data.SL.comps[pos + 2]; } if (*cont == 1) { /* new component */ size_t size = strlen(*dest); int has_slash; *dest = realloc(*dest, strlen(*dest) + len + 2); if (*dest == NULL) { return ISO_OUT_OF_MEM; } /* it is a new compoenent, add the '/' */ has_slash = 0; if (size > 0) if ((*dest)[size - 1] == '/') has_slash = 1; if (!has_slash) { (*dest)[size] = '/'; (*dest)[size+1] = '\0'; } strncat(*dest, comp, len); } else if (*cont == 2) { /* the component continues */ *dest = realloc(*dest, strlen(*dest) + len + 1); if (*dest == NULL) { return ISO_OUT_OF_MEM; } /* we don't have to add the '/' */ strncat(*dest, comp, len); } else { *dest = iso_util_strcopy(comp, len); } if (*dest == NULL) { return ISO_OUT_OF_MEM; } /* do the component continue or not? */ *cont = (flags & 0x01) ? 2 : 1; } if (*cont == 2) { /* TODO check that SL flag is set to continute too ?*/ } else { *cont = sl->data.SL.flags[0] & 0x1 ? 1 : 0; } return ISO_SUCCESS; } /** * Fills a struct stat with the values of a Rock Ridge PN entry (RRIP, 4.1.2). * * @return * 1 on success, < 0 on error */ int read_rr_PN(struct susp_sys_user_entry *pn, struct stat *st) { int high_shift= 0; if (pn == NULL || st == NULL) { return ISO_NULL_POINTER; } if (pn->sig[0] != 'P' || pn->sig[1] != 'N') { return ISO_WRONG_ARG_VALUE; } if (pn->len_sue[0] != 20) { return ISO_WRONG_RR; } /* (dev_t << 32) causes compiler warnings on FreeBSD because sizeof(dev_t) is 4. */ st->st_rdev = (dev_t)iso_read_bb(pn->data.PN.low, 4, NULL); if (sizeof(st->st_rdev) > 4) { high_shift = 32; st->st_rdev |= (dev_t)((dev_t)iso_read_bb(pn->data.PN.high, 4, NULL) << high_shift); } /* was originally: st->st_rdev = (dev_t)((dev_t)iso_read_bb(pn->data.PN.high, 4, NULL) << 32) | (dev_t)iso_read_bb(pn->data.PN.low, 4, NULL); */ return ISO_SUCCESS; } /* AA is the obsolete field signature of AAIP versions < 2.0 */ int read_aaip_AA(struct susp_sys_user_entry *sue, unsigned char **aa_string, size_t *aa_size, size_t *aa_len, size_t *prev_field, int *is_done, int flag) { unsigned char *aapt; if (*is_done) { /* To coexist with Apple ISO : Gracefully react on possibly trailing Apple AA */ if (sue->version[0] != 1 || sue->len_sue[0] == 7) return ISO_SUCCESS; return ISO_WRONG_RR; } if (*aa_size == 0 || *aa_string == NULL) { /* Gracefully react on possibly leading Apple AA */ if (sue->version[0] != 1 || sue->len_sue[0] < 9) return ISO_SUCCESS; } /* A valid AAIP AA entry has 5 header bytes and at least 1 component byte */ if (sue->len_sue[0] < 6) return ISO_WRONG_RR; /* Possibly create or grow storage */ if (*aa_size == 0 || *aa_string == NULL) { *aa_size = *aa_len + sue->len_sue[0]; *aa_string = calloc(*aa_size, 1); *aa_len = 0; } else if (*aa_len + sue->len_sue[0] > *aa_size) { if (sue->version[0] != 1) { /* Apple ISO within the AAIP field group is not AAIP compliant */ return ISO_WRONG_RR; } *aa_size += *aa_len + sue->len_sue[0]; *aa_string = realloc(*aa_string, *aa_size); } if (*aa_string == NULL) return ISO_OUT_OF_MEM; if (*aa_len > 0) { /* Mark prev_field as being continued */ (*aa_string)[*prev_field + 4] = 1; } *prev_field = *aa_len; /* Compose new SUSP header with signature aa[], cont == 0 */ aapt = *aa_string + *aa_len; aapt[0] = 'A'; aapt[1] = 'L'; aapt[2] = sue->len_sue[0]; aapt[3] = 1; aapt[4] = 0; /* Append sue payload */ memcpy(aapt + 5, sue->data.AL.comps, sue->len_sue[0] - 5); *is_done = !(sue->data.AL.flags[0] & 1); *aa_len += sue->len_sue[0]; return ISO_SUCCESS; } /* AL is the field signature of AAIP versions >= 2.0 */ int read_aaip_AL(struct susp_sys_user_entry *sue, unsigned char **aa_string, size_t *aa_size, size_t *aa_len, size_t *prev_field, int *is_done, int flag) { unsigned char *aapt; if (*is_done) return ISO_WRONG_RR; if (sue->version[0] != 1) return ISO_WRONG_RR; /* A valid AL entry has 5 header bytes and at least 1 component byte */ if (sue->len_sue[0] < 6) return ISO_WRONG_RR; /* Possibly create or grow storage */ if (*aa_size == 0 || *aa_string == NULL) { *aa_size = *aa_len + sue->len_sue[0]; *aa_string = calloc(*aa_size, 1); *aa_len = 0; } else if (*aa_len + sue->len_sue[0] > *aa_size) { *aa_size += *aa_len + sue->len_sue[0]; *aa_string = realloc(*aa_string, *aa_size); } if (*aa_string == NULL) return ISO_OUT_OF_MEM; if (*aa_len > 0) { /* Mark prev_field as being continued */ (*aa_string)[*prev_field + 4] = 1; } *prev_field = *aa_len; /* Compose new SUSP header with signature aa[], cont == 0 */ aapt = *aa_string + *aa_len; aapt[0] = 'A'; aapt[1] = 'L'; aapt[2] = sue->len_sue[0]; aapt[3] = 1; aapt[4] = 0; /* Append sue payload */ memcpy(aapt + 5, sue->data.AL.comps, sue->len_sue[0] - 5); *is_done = !(sue->data.AL.flags[0] & 1); *aa_len += sue->len_sue[0]; return ISO_SUCCESS; } /** * Reads the zisofs parameters from a ZF field (see doc/zisofs_format.txt * and doc/zisofs2_format.txt). * * @return * 1 on success, < 0 on error */ int read_zisofs_ZF(struct susp_sys_user_entry *zf, uint8_t algorithm[2], uint8_t *header_size_div4, uint8_t *block_size_log2, uint64_t *uncompressed_size, int flag) { if (zf == NULL) { return ISO_NULL_POINTER; } if ((zf->sig[0] != 'Z' || zf->sig[1] != 'F') && (zf->sig[0] != 'Z' || zf->sig[1] != '2')) return ISO_WRONG_ARG_VALUE; if (zf->len_sue[0] != 16) { return ISO_WRONG_RR; } if (zf->version[0] > 2) return ISO_WRONG_RR; algorithm[0] = zf->data.ZF.parameters[0]; algorithm[1] = zf->data.ZF.parameters[1]; *header_size_div4 = zf->data.ZF.parameters[2]; *block_size_log2 = zf->data.ZF.parameters[3]; if (zf->version[0] == 1) *uncompressed_size = iso_read_bb(&(zf->data.ZF.parameters[4]), 4, NULL); else *uncompressed_size = iso_read_lsb64(&(zf->data.ZF.parameters[4])); return ISO_SUCCESS; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2022 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libisofs.h" #include "stream.h" #include "fsource.h" #include "util.h" #include "node.h" #include #include #include #include #include #ifndef PATH_MAX #define PATH_MAX Libisofs_default_path_maX #endif ino_t serial_id = (ino_t)1; ino_t mem_serial_id = (ino_t)1; ino_t cut_out_serial_id = (ino_t)1; static int fsrc_open(IsoStream *stream) { int ret; struct stat info; off_t esize; IsoFileSource *src; if (stream == NULL) { return ISO_NULL_POINTER; } src = ((FSrcStreamData*)stream->data)->src; ret = iso_file_source_stat(src, &info); if (ret < 0) { return ret; } ret = iso_file_source_open(src); if (ret < 0) { return ret; } esize = ((FSrcStreamData*)stream->data)->size; if (info.st_size == esize) { return ISO_SUCCESS; } else { return (esize > info.st_size) ? 3 : 2; } } static int fsrc_close(IsoStream *stream) { IsoFileSource *src; if (stream == NULL) { return ISO_NULL_POINTER; } src = ((FSrcStreamData*)stream->data)->src; return iso_file_source_close(src); } static off_t fsrc_get_size(IsoStream *stream) { FSrcStreamData *data; data = (FSrcStreamData*)stream->data; return data->size; } static int fsrc_read(IsoStream *stream, void *buf, size_t count) { IsoFileSource *src; if (stream == NULL) { return ISO_NULL_POINTER; } src = ((FSrcStreamData*)stream->data)->src; return iso_file_source_read(src, buf, count); } static int fsrc_is_repeatable(IsoStream *stream) { int ret; struct stat info; FSrcStreamData *data; if (stream == NULL) { return ISO_NULL_POINTER; } data = (FSrcStreamData*)stream->data; /* mode is not cached, this function is only useful for filters */ ret = iso_file_source_stat(data->src, &info); if (ret < 0) { return ret; } if (S_ISREG(info.st_mode) || S_ISBLK(info.st_mode)) { return 1; } else { return 0; } } static void fsrc_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, ino_t *ino_id) { FSrcStreamData *data; IsoFilesystem *fs; data = (FSrcStreamData*)stream->data; fs = iso_file_source_get_filesystem(data->src); *fs_id = fs->get_id(fs); *dev_id = data->dev_id; *ino_id = data->ino_id; } static void fsrc_free(IsoStream *stream) { FSrcStreamData *data; data = (FSrcStreamData*)stream->data; iso_file_source_unref(data->src); free(data); } static int fsrc_update_size(IsoStream *stream) { int ret; struct stat info; IsoFileSource *src; if (stream == NULL) { return ISO_NULL_POINTER; } src = ((FSrcStreamData*)stream->data)->src; ret = iso_file_source_stat(src, &info); if (ret < 0) { return ret; } ((FSrcStreamData*)stream->data)->size = info.st_size; return ISO_SUCCESS; } static IsoStream *fsrc_get_input_stream(IsoStream *stream, int flag) { return NULL; } int fsrc_clone_stream(IsoStream *old_stream, IsoStream **new_stream, int flag) { FSrcStreamData *data, *new_data; IsoStream *stream; int ret; if (flag) return ISO_STREAM_NO_CLONE; /* unknown option required */ data = (FSrcStreamData*) old_stream->data; if (data->src->class->version < 2) return ISO_STREAM_NO_CLONE; /* No clone_src() method available */ *new_stream = NULL; stream = calloc(1, sizeof(IsoStream)); if (stream == NULL) return ISO_OUT_OF_MEM; new_data = calloc(1, sizeof(FSrcStreamData)); if (new_data == NULL) { free((char *) stream); return ISO_OUT_OF_MEM; } *new_stream = stream; stream->class = old_stream->class; stream->refcount = 1; stream->data = new_data; ret = data->src->class->clone_src(data->src, &(new_data->src), 0); if (ret < 0) { free((char *) stream); free((char *) new_data); return ret; } new_data->dev_id = data->dev_id; new_data->ino_id = data->ino_id; new_data->size = data->size; return ISO_SUCCESS; } static IsoStreamIface fsrc_stream_class = { 4, /* version */ "fsrc", fsrc_open, fsrc_close, fsrc_get_size, fsrc_read, fsrc_is_repeatable, fsrc_get_id, fsrc_free, fsrc_update_size, fsrc_get_input_stream, NULL, fsrc_clone_stream }; int iso_file_source_stream_new(IsoFileSource *src, IsoStream **stream) { int r; struct stat info; IsoStream *str; FSrcStreamData *data; if (src == NULL || stream == NULL) { return ISO_NULL_POINTER; } r = iso_file_source_stat(src, &info); if (r < 0) { return r; } if (S_ISDIR(info.st_mode)) { return ISO_FILE_IS_DIR; } /* check for read access to contents */ r = iso_file_source_access(src); if (r < 0) { return r; } str = malloc(sizeof(IsoStream)); if (str == NULL) { return ISO_OUT_OF_MEM; } data = malloc(sizeof(FSrcStreamData)); if (data == NULL) { free(str); return ISO_OUT_OF_MEM; } /* take the ref to IsoFileSource */ data->src = src; data->size = info.st_size; /* get the id numbers */ { IsoFilesystem *fs; unsigned int fs_id; fs = iso_file_source_get_filesystem(data->src); fs_id = fs->get_id(fs); if (fs_id == 0) { /* * the filesystem implementation is unable to provide valid * st_dev and st_ino fields. Use serial_id. */ data->dev_id = (dev_t) 0; data->ino_id = serial_id++; } else { data->dev_id = info.st_dev; data->ino_id = info.st_ino; } } str->refcount = 1; str->data = data; str->class = &fsrc_stream_class; *stream = str; return ISO_SUCCESS; } int iso_stream_get_src_zf(IsoStream *stream, uint8_t zisofs_algo[2], int *header_size_div4, int *block_size_log2, uint64_t *uncompressed_size, int flag) { int ret; FSrcStreamData *data; IsoFileSource *src; /* Intimate friendship with libisofs/fs_image.c */ int iso_ifs_source_get_zf(IsoFileSource *src, uint8_t zisofs_algo[2], int *header_size_div4, int *block_size_log2, uint64_t *uncompressed_size, int flag); if (stream->class != &fsrc_stream_class) return 0; data = stream->data; src = data->src; ret = iso_ifs_source_get_zf(src, zisofs_algo, header_size_div4, block_size_log2, uncompressed_size, 0); return ret; } struct cut_out_stream { IsoFileSource *src; /* key for file identification inside filesystem */ dev_t dev_id; ino_t ino_id; off_t offset; /**< offset where read begins */ off_t size; /**< size of this file */ off_t pos; /* position on the file for read */ }; static int cut_out_open(IsoStream *stream) { int ret; struct stat info; off_t src_size, pos; IsoFileSource *src; struct cut_out_stream *data; if (stream == NULL) { return ISO_NULL_POINTER; } data = stream->data; src = data->src; ret = iso_file_source_stat(data->src, &info); if (ret < 0) { return ret; } ret = iso_file_source_open(src); if (ret < 0) { return ret; } if (S_ISREG(info.st_mode)) { src_size= info.st_size; } else { /* Determine src_size and lseekability of device */ src_size = iso_file_source_determine_capacity(src, data->offset + data->size, 2); if (src_size <= 0) return ISO_WRONG_ARG_VALUE; } if (data->offset > src_size) { /* file is smaller than expected */ pos = iso_file_source_lseek(src, src_size, 0); } else { pos = iso_file_source_lseek(src, data->offset, 0); } if (pos < 0) { return (int) pos; } data->pos = 0; if (data->offset + data->size > src_size) { return 3; /* file smaller than expected */ } else { return ISO_SUCCESS; } } static int cut_out_close(IsoStream *stream) { IsoFileSource *src; if (stream == NULL) { return ISO_NULL_POINTER; } src = ((struct cut_out_stream*)stream->data)->src; return iso_file_source_close(src); } static off_t cut_out_get_size(IsoStream *stream) { struct cut_out_stream *data = stream->data; return data->size; } static int cut_out_read(IsoStream *stream, void *buf, size_t count) { struct cut_out_stream *data = stream->data; count = (size_t) MIN((size_t) (data->size - data->pos), count); if (count == 0) { return 0; } return iso_file_source_read(data->src, buf, count); } static int cut_out_is_repeatable(IsoStream *stream) { /* reg files are always repeatable */ return 1; } static void cut_out_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, ino_t *ino_id) { FSrcStreamData *data; IsoFilesystem *fs; data = (FSrcStreamData*)stream->data; fs = iso_file_source_get_filesystem(data->src); *fs_id = fs->get_id(fs); *dev_id = data->dev_id; *ino_id = data->ino_id; } static void cut_out_free(IsoStream *stream) { struct cut_out_stream *data = stream->data; iso_file_source_unref(data->src); free(data); } static int cut_out_update_size(IsoStream *stream) { return ISO_SUCCESS; } static IsoStream* cut_out_get_input_stream(IsoStream *stream, int flag) { return NULL; } static int cut_out_clone_stream(IsoStream *old_stream, IsoStream **new_stream, int flag) { struct cut_out_stream *data, *new_data; IsoStream *stream; int ret; if (flag) return ISO_STREAM_NO_CLONE; /* unknown option required */ data = (struct cut_out_stream *) old_stream->data; if (data->src->class->version < 2) return ISO_STREAM_NO_CLONE; /* No clone_src() method available */ *new_stream = NULL; stream = calloc(1, sizeof(IsoStream)); if (stream == NULL) return ISO_OUT_OF_MEM; stream->refcount = 1; stream->class = old_stream->class; new_data = calloc(1, sizeof(struct cut_out_stream)); if (new_data == NULL) { free((char *) stream); return ISO_OUT_OF_MEM; } ret = data->src->class->clone_src(data->src, &(new_data->src), 0); if (ret < 0) { free((char *) stream); free((char *) new_data); return ret; } new_data->dev_id = (dev_t) 0; new_data->ino_id = cut_out_serial_id++; new_data->offset = data->offset; new_data->size = data->size; new_data->pos = 0; stream->data = new_data; *new_stream = stream; return ISO_SUCCESS; } /* * TODO update cut out streams to deal with update_size(). Seems hard. */ static IsoStreamIface cut_out_stream_class = { 4, /* version */ "cout", cut_out_open, cut_out_close, cut_out_get_size, cut_out_read, cut_out_is_repeatable, cut_out_get_id, cut_out_free, cut_out_update_size, cut_out_get_input_stream, NULL, cut_out_clone_stream }; int iso_cut_out_stream_new(IsoFileSource *src, off_t offset, off_t size, IsoStream **stream) { int r; off_t src_size; struct stat info; IsoStream *str; struct cut_out_stream *data; if (src == NULL || stream == NULL) { return ISO_NULL_POINTER; } if (size == 0) { return ISO_WRONG_ARG_VALUE; } r = iso_file_source_stat(src, &info); if (r < 0) { return r; } if (S_ISREG(info.st_mode)) { src_size = info.st_size; } else { /* Open src, do iso_source_lseek(SEEK_END), close src */ src_size = iso_file_source_determine_capacity(src, offset + size, 3); if (src_size <= 0) return ISO_WRONG_ARG_VALUE; } if (offset > src_size) { return ISO_FILE_OFFSET_TOO_BIG; } /* check for read access to contents */ r = iso_file_source_access(src); if (r < 0) { return r; } str = malloc(sizeof(IsoStream)); if (str == NULL) { return ISO_OUT_OF_MEM; } data = malloc(sizeof(struct cut_out_stream)); if (data == NULL) { free(str); return ISO_OUT_OF_MEM; } /* take a new ref to IsoFileSource */ data->src = src; iso_file_source_ref(src); data->offset = offset; data->size = MIN(src_size - offset, size); /* get the id numbers */ data->dev_id = (dev_t) 0; data->ino_id = cut_out_serial_id++; str->refcount = 1; str->data = data; str->class = &cut_out_stream_class; *stream = str; return ISO_SUCCESS; } typedef struct { uint8_t *buf; ssize_t offset; /* -1 if stream closed */ ino_t ino_id; size_t size; } MemStreamData; static int mem_open(IsoStream *stream) { MemStreamData *data; if (stream == NULL) { return ISO_NULL_POINTER; } data = (MemStreamData*)stream->data; if (data->offset != -1) { return ISO_FILE_ALREADY_OPENED; } data->offset = 0; return ISO_SUCCESS; } static int mem_close(IsoStream *stream) { MemStreamData *data; if (stream == NULL) { return ISO_NULL_POINTER; } data = (MemStreamData*)stream->data; if (data->offset == -1) { return ISO_FILE_NOT_OPENED; } data->offset = -1; return ISO_SUCCESS; } static off_t mem_get_size(IsoStream *stream) { MemStreamData *data; data = (MemStreamData*)stream->data; return (off_t)data->size; } static int mem_read(IsoStream *stream, void *buf, size_t count) { size_t len; MemStreamData *data; if (stream == NULL || buf == NULL) { return ISO_NULL_POINTER; } if (count == 0) { return ISO_WRONG_ARG_VALUE; } data = stream->data; if (data->offset == -1) { return ISO_FILE_NOT_OPENED; } if (data->offset >= (ssize_t) data->size) { return 0; /* EOF */ } len = MIN(count, data->size - data->offset); memcpy(buf, data->buf + data->offset, len); data->offset += len; return len; } static int mem_is_repeatable(IsoStream *stream) { return 1; } static void mem_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, ino_t *ino_id) { MemStreamData *data; data = (MemStreamData*)stream->data; *fs_id = ISO_MEM_FS_ID; *dev_id = 0; *ino_id = data->ino_id; } static void mem_free(IsoStream *stream) { MemStreamData *data; data = (MemStreamData*)stream->data; if (data->buf != NULL) free(data->buf); free(data); } static int mem_update_size(IsoStream *stream) { return ISO_SUCCESS; } static IsoStream* mem_get_input_stream(IsoStream *stream, int flag) { return NULL; } static int mem_clone_stream(IsoStream *old_stream, IsoStream **new_stream, int flag) { MemStreamData *data, *new_data; IsoStream *stream; uint8_t *new_buf = NULL; if (flag) return ISO_STREAM_NO_CLONE; /* unknown option required */ *new_stream = NULL; stream = calloc(1, sizeof(IsoStream)); if (stream == NULL) return ISO_OUT_OF_MEM; stream->refcount = 1; stream->class = old_stream->class; new_data = calloc(1, sizeof(MemStreamData)); if (new_data == NULL) { free((char *) stream); return ISO_OUT_OF_MEM; } data = (MemStreamData *) old_stream->data; if (data->size > 0) { new_buf = calloc(1, data->size); if (new_buf == NULL) { free((char *) stream); free((char *) new_data); return ISO_OUT_OF_MEM; } memcpy(new_buf, data->buf, data->size); } new_data->buf = new_buf; new_data->offset = -1; new_data->ino_id = mem_serial_id++; new_data->size = data->size; stream->data = new_data; *new_stream = stream; return ISO_SUCCESS; } static IsoStreamIface mem_stream_class = { 4, /* version */ "mem ", mem_open, mem_close, mem_get_size, mem_read, mem_is_repeatable, mem_get_id, mem_free, mem_update_size, mem_get_input_stream, NULL, mem_clone_stream }; /** * Create a stream for reading from a arbitrary memory buffer. * When the Stream refcount reach 0, the buffer is free(3). * * @return * 1 success, < 0 error */ int iso_memory_stream_new(unsigned char *buf, size_t size, IsoStream **stream) { IsoStream *str; MemStreamData *data; if (buf == NULL || stream == NULL) { return ISO_NULL_POINTER; } str = malloc(sizeof(IsoStream)); if (str == NULL) { return ISO_OUT_OF_MEM; } data = malloc(sizeof(MemStreamData)); if (data == NULL) { free(str); return ISO_OUT_OF_MEM; } /* fill data */ data->buf = buf; data->size = size; data->offset = -1; data->ino_id = mem_serial_id++; str->refcount = 1; str->data = data; str->class = &mem_stream_class; *stream = str; return ISO_SUCCESS; } void iso_stream_ref(IsoStream *stream) { ++stream->refcount; } void iso_stream_unref(IsoStream *stream) { if (--stream->refcount == 0) { stream->class->free(stream); free(stream); } } inline int iso_stream_open(IsoStream *stream) { return stream->class->open(stream); } inline int iso_stream_close(IsoStream *stream) { return stream->class->close(stream); } inline off_t iso_stream_get_size(IsoStream *stream) { return stream->class->get_size(stream); } inline int iso_stream_read(IsoStream *stream, void *buf, size_t count) { return stream->class->read(stream, buf, count); } inline int iso_stream_is_repeatable(IsoStream *stream) { return stream->class->is_repeatable(stream); } inline int iso_stream_update_size(IsoStream *stream) { IsoStreamIface* class = stream->class; return (class->version >= 1) ? class->update_size(stream) : 0; } inline void iso_stream_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id, ino_t *ino_id) { stream->class->get_id(stream, fs_id, dev_id, ino_id); } void iso_stream_get_file_name(IsoStream *stream, char *name) { char *type = stream->class->type; if (!strncmp(type, "fsrc", 4)) { FSrcStreamData *data = stream->data; char *path = iso_file_source_get_path(data->src); if (path == NULL) { name[0] = 0; return; } strncpy(name, path, PATH_MAX - 1); name[PATH_MAX - 1] = 0; free(path); } else if (!strncmp(type, "cout", 4)) { strcpy(name, "CUT_OUT FILE"); } else if (!strncmp(type, "mem ", 4)) { strcpy(name, "MEM SOURCE"); } else if (!strncmp(type, "boot", 4)) { strcpy(name, "BOOT CATALOG"); } else if (!strncmp(type, "extf", 4)) { strcpy(name, "EXTERNAL FILTER"); } else if (!strncmp(type, "ziso", 4)) { strcpy(name, "ZISOFS COMPRESSION FILTER"); } else if (!strncmp(type, "osiz", 4)) { strcpy(name, "ZISOFS DECOMPRESSION FILTER"); } else if (!strncmp(type, "gzip", 4)) { strcpy(name, "GZIP COMPRESSION FILTER"); } else if (!strncmp(type, "pizg", 4)) { strcpy(name, "GZIP DECOMPRESSION FILTER"); } else if (!strncmp(type, "user", 4)) { strcpy(name, "USER SUPPLIED STREAM"); } else { strcpy(name, "UNKNOWN SOURCE"); } } /* @param flag bit0= Obtain most fundamental stream */ IsoStream *iso_stream_get_input_stream(IsoStream *stream, int flag) { IsoStreamIface* class; IsoStream *result = NULL, *next; if (stream == NULL) { return NULL; } while (1) { class = stream->class; if (class->version < 2) return result; next = class->get_input_stream(stream, 0); if (next == NULL) return result; result = next; if (!(flag & 1)) return result; stream = result; } } char *iso_stream_get_source_path(IsoStream *stream, int flag) { char *path = NULL, ivd[80], *raw_path = NULL; if (stream == NULL) { return NULL; } if (stream->class == &fsrc_stream_class) { FSrcStreamData *fsrc_data = stream->data; path = iso_file_source_get_path(fsrc_data->src); } else if (stream->class == &cut_out_stream_class) { struct cut_out_stream *cout_data = stream->data; raw_path = iso_file_source_get_path(cout_data->src); sprintf(ivd, " %.f %.f", (double) cout_data->offset, (double) cout_data->size); path= calloc(strlen(raw_path) + strlen(ivd) + 1, 1); if (path == NULL) { goto ex; } strcpy(path, raw_path); strcat(path, ivd); } ex:; if (raw_path != NULL) free(raw_path); return path; } /* @param flag bit0= in case of filter stream do not dig for base stream @return 1 = ok , 0 = not an ISO image stream , <0 = error */ int iso_stream_set_image_ino(IsoStream *stream, ino_t ino, int flag) { IsoStream *base_stream; if (stream == NULL) { return ISO_NULL_POINTER; } if (!(flag & 1)) { base_stream = iso_stream_get_input_stream(stream, 1); if (base_stream != NULL) stream = base_stream; } if (stream->class == &fsrc_stream_class) { FSrcStreamData *fsrc_data = stream->data; fsrc_data->ino_id = ino; return 1; } return 0; } int iso_stream_cmp_ifs_sections(IsoStream *s1, IsoStream *s2, int *cmp_ret, int flag) { int ret; FSrcStreamData *fssd1, *fssd2; IsoFileSource *src1, *src2; /* Must keep any suspect in the game to preserve transitivity of the calling function by ranking applicable streams lower than non-applicable. ones. */ if (s1->class != &fsrc_stream_class && s2->class != &fsrc_stream_class) return 0; /* Compare eventual image data section LBA and sizes */ if (s1->class == &fsrc_stream_class) { fssd1= (FSrcStreamData *) s1->data; src1 = fssd1->src; } else { src1 = NULL; } if (s2->class == &fsrc_stream_class) { fssd2= (FSrcStreamData *) s2->data; src2 = fssd2->src; } else { src2 = NULL; } ret = iso_ifs_sections_cmp(src1, src2, cmp_ret, 1); if (ret <= 0) return 0; return 1; } /* Maintain and exploit a list of stream compare functions seen by iso_stream_cmp_ino(). This is needed to separate stream comparison families in order to keep iso_stream_cmp_ino() transitive while alternative stream->class->cmp_ino() decide inside the families. */ struct iso_streamcmprank { int (*cmp_func)(IsoStream *s1, IsoStream *s2); struct iso_streamcmprank *next; }; static struct iso_streamcmprank *streamcmpranks = NULL; static int iso_get_streamcmprank(int (*cmp_func)(IsoStream *s1, IsoStream *s2), int flag) { int idx; struct iso_streamcmprank *cpr, *last_cpr = NULL; idx = 0; for (cpr = streamcmpranks; cpr != NULL; cpr = cpr->next) { if (cpr->cmp_func == cmp_func) break; idx++; last_cpr = cpr; } if (cpr != NULL) return idx; LIBISO_ALLOC_MEM_VOID(cpr, struct iso_streamcmprank, 1); cpr->cmp_func = cmp_func; cpr->next = NULL; if (last_cpr != NULL) last_cpr->next = cpr; if (streamcmpranks == NULL) streamcmpranks = cpr; return idx; ex:; return -1; } static int iso_cmp_streamcmpranks(int (*cf1)(IsoStream *s1, IsoStream *s2), int (*cf2)(IsoStream *s1, IsoStream *s2)) { int rank1, rank2; rank1 = iso_get_streamcmprank(cf1, 0); rank2 = iso_get_streamcmprank(cf2, 0); return rank1 < rank2 ? -1 : 1; } int iso_stream_destroy_cmpranks(int flag) { struct iso_streamcmprank *cpr, *next; for (cpr = streamcmpranks; cpr != NULL; cpr = next) { next = cpr->next; LIBISO_FREE_MEM(cpr); } streamcmpranks = NULL; return ISO_SUCCESS; } /* API */ int iso_stream_cmp_ino(IsoStream *s1, IsoStream *s2, int flag) { int ret; unsigned int fs_id1, fs_id2; dev_t dev_id1, dev_id2; ino_t ino_id1, ino_id2; off_t size1, size2; /* #define Libisofs_stream_cmp_ino_debuG 1 */ #ifdef Libisofs_stream_cmp_ino_debuG static int report_counter = 0; static int debug = 1; #endif /* Libisofs_stream_cmp_ino_debuG */ if (s1 == s2) return 0; if (s1 == NULL) return -1; if (s2 == NULL) return 1; /* This stays transitive by the fact that iso_stream_cmp_ifs_sections() is transitive, returns > 0 if s1 or s2 are applicable, ret is -1 if s1 is applicable but s2 is not, ret is 1 if s1 is not applicable but s2 is. Proof: Be A the set of applicable streams, S and G transitive and antisymmetric relations in respect to outcome {-1, 0, 1}. The combined relation R shall be defined by I. R(a,b) = S(a,b) if a in A or b in A, else G(a,b) Further S shall have the property II. S(a,b) = -1 if a in A and b not in A Then R can be proven to be transitive: By enumerating the 8 combinations of a,b,c being in A or not, we get 5 cases of pure S or pure G. Three cases are mixed: a,b not in A, c in A : G(a,b) == -1, S(b,c) == -1 -> S(a,c) == -1 Impossible because S(b,c) == -1 contradicts II. a,c not in A, b in A : S(a,b) == -1, S(b,c) == -1 -> G(a,c) == -1 Impossible because S(a,b) == -1 contradicts II. b,c not in A, a in A : S(a,b) == -1, G(b,c) == -1 -> S(a,c) == -1 Always true because S(a,c) == -1 by definition II. */ if (iso_stream_cmp_ifs_sections(s1, s2, &ret, 0) > 0) return ret; /* Both are unfiltered from loaded ISO filesystem */ if (!(flag & 1)) { /* Filters may have smarter methods to compare themselves with others. Transitivity is ensured by ranking mixed pairs by the rank of their comparison functions, and by ranking streams with .cmp_ino lower than streams without. (One could merge (class->version < 3) and (cmp_ino == NULL).) Here we define S for "and" rather than "or" I. R(a,b) = S(a,b) if a in A and b in A, else G(a,b) and the function ranking in case of "exor" makes sure that II. G(a,b) = -1 if a in A and b not in A Again we get three mixed cases: a not in A, b,c in A : G(a,b) == -1, S(b,c) == -1 -> G(a,c) == -1 Impossible because G(a,b) == -1 contradicts II. b not in A, a,c in A : G(a,b) == -1, G(b,c) == -1 -> S(a,c) == -1 Impossible because G(b,c) == -1 contradicts II. c not in A, a,b in A : S(a,b) == -1, G(b,c) == -1 -> G(a,c) == -1 Always true because G(a,c) == -1 by definition II. */ if ((s1->class->version >= 3) ^ (s2->class->version >= 3)) { /* One of both has no own com_ino function. Rank it as larger. */ return s1->class->version >= 3 ? -1 : 1; } else if (s1->class->version >= 3) { if (s1->class->cmp_ino == s2->class->cmp_ino) { if (s1->class->cmp_ino == NULL) { /* Both are NULL. No decision by .cmp_ino(). */; } else { /* Both are compared by the same function */ ret = s1->class->cmp_ino(s1, s2); return ret; } } else { /* Not the same cmp_ino() function. Decide by list rank of function while building the list on the fly. */ ret = iso_cmp_streamcmpranks(s1->class->cmp_ino, s2->class->cmp_ino); return ret; } } } iso_stream_get_id(s1, &fs_id1, &dev_id1, &ino_id1); iso_stream_get_id(s2, &fs_id2, &dev_id2, &ino_id2); if (fs_id1 < fs_id2) { return -1; } else if (fs_id1 > fs_id2) { return 1; } /* files belong to the same fs */ if (dev_id1 > dev_id2) { return -1; } else if (dev_id1 < dev_id2) { return 1; } else if (ino_id1 < ino_id2) { return -1; } else if (ino_id1 > ino_id2) { return 1; } size1 = iso_stream_get_size(s1); size2 = iso_stream_get_size(s2); if (size1 < size2) { #ifdef Libisofs_stream_cmp_ino_debuG if (debug) { if (report_counter < 5) fprintf(stderr, "\n\nlibisofs_DEBUG : Program error: same ino but differing size\n\n\n"); else if (report_counter == 5) fprintf(stderr, "\n\nlibisofs_DEBUG : Inode error: more of same ino but differing size\n\n\n"); report_counter++; } #endif /* Libisofs_stream_cmp_ino_debuG */ return -1; } else if (size1 > size2) { #ifdef Libisofs_stream_cmp_ino_debuG if (debug) { if (report_counter < 5) fprintf(stderr, "\n\nlibisofs_DEBUG : Inode error: same ino but differing size\n\n\n"); else if (report_counter == 5) fprintf(stderr, "\n\nlibisofs_DEBUG : Program error: more of same ino but differing size\n\n\n"); report_counter++; } #endif /* Libisofs_stream_cmp_ino_debuG */ return 1; } if (s1->class != s2->class) return (s1->class < s2->class ? -1 : 1); if (fs_id1 == 0 && dev_id1 == 0 && ino_id1 == 0) { return (s1 < s2 ? -1 : 1); } return 0; } /** * @return * 1 ok, 0 EOF, < 0 error */ int iso_stream_read_buffer(IsoStream *stream, char *buf, size_t count, size_t *got) { ssize_t result; *got = 0; do { result = iso_stream_read(stream, buf + *got, count - *got); if (result < 0) { memset(buf + *got, 0, count - *got); return result; } if (result == 0) break; *got += result; } while (*got < count); if (*got < count) { /* eof */ memset(buf + *got, 0, count - *got); return 0; } return 1; } /* @param flag bit0= dig out most original stream (e.g. because from old image) @return 1=ok, md5 is valid, 0= not ok, <0 fatal error, abort */ int iso_stream_make_md5(IsoStream *stream, char md5[16], int flag) { int ret, is_open = 0; char * buffer = NULL; void *ctx= NULL; off_t file_size; uint32_t b, nblocks; size_t got_bytes; IsoStream *input_stream; LIBISO_ALLOC_MEM(buffer, char, 2048); if (flag & 1) { while(1) { input_stream = iso_stream_get_input_stream(stream, 0); if (input_stream == NULL) break; stream = input_stream; } } if (! iso_stream_is_repeatable(stream)) {ret = 0; goto ex;} ret = iso_md5_start(&ctx); if (ret < 0) goto ex; ret = iso_stream_open(stream); if (ret < 0) goto ex; is_open = 1; file_size = iso_stream_get_size(stream); nblocks = DIV_UP(file_size, 2048); for (b = 0; b < nblocks; ++b) { ret = iso_stream_read_buffer(stream, buffer, 2048, &got_bytes); if (ret < 0) { ret = 0; goto ex; } /* Do not use got_bytes to stay closer to IsoFileSrc processing */ if (file_size - b * 2048 > 2048) ret = 2048; else ret = file_size - b * 2048; iso_md5_compute(ctx, buffer, ret); } ret = 1; ex:; if (is_open) iso_stream_close(stream); if (ctx != NULL) iso_md5_end(&ctx, md5); LIBISO_FREE_MEM(buffer); return ret; } /* API */ int iso_stream_clone(IsoStream *old_stream, IsoStream **new_stream, int flag) { int ret; if (old_stream->class->version < 4) return ISO_STREAM_NO_CLONE; ret = old_stream->class->clone_stream(old_stream, new_stream, 0); return ret; } int iso_stream_clone_filter_common(IsoStream *old_stream, IsoStream **new_stream, IsoStream **new_input, int flag) { IsoStream *stream, *input_stream; int ret; *new_stream = NULL; *new_input = NULL; input_stream = iso_stream_get_input_stream(old_stream, 0); if (input_stream == NULL) return ISO_STREAM_NO_CLONE; stream = calloc(1, sizeof(IsoStream)); if (stream == NULL) return ISO_OUT_OF_MEM; ret = iso_stream_clone(input_stream, new_input, 0); if (ret < 0) { free((char *) stream); return ret; } stream->class = old_stream->class; stream->refcount = 1; stream->data = NULL; *new_stream = stream; return ISO_SUCCESS; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2016 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_STREAM_H_ #define LIBISO_STREAM_H_ /* * Definitions of streams. */ #include "fsource.h" /* IMPORTANT: Any change must be reflected by fsrc_clone_stream */ typedef struct { IsoFileSource *src; /* key for file identification inside filesystem */ dev_t dev_id; ino_t ino_id; off_t size; /**< size of this file */ } FSrcStreamData; /** * Get an identifier for the file of the source, for debug purposes * @param name * Must provide at least PATH_MAX bytes. If no PATH_MAX is defined * then assume PATH_MAX = Libisofs_default_path_maX from libisofs.h */ void iso_stream_get_file_name(IsoStream *stream, char *name); /** * Create a stream to read from a IsoFileSource. * The stream will take the ref. to the IsoFileSource, so after a successfully * execution of this function, you must not unref() the source, unless you * take an extra ref. * * @return * 1 success, < 0 error * Possible errors: * */ int iso_file_source_stream_new(IsoFileSource *src, IsoStream **stream); /** * Create a new stream to read a chunk of an IsoFileSource.. * The stream will add a ref. to the IsoFileSource. * * @return * 1 success, < 0 error */ int iso_cut_out_stream_new(IsoFileSource *src, off_t offset, off_t size, IsoStream **stream); /** * Obtain eventual zisofs ZF field entry parameters from a file source out * of a loaded ISO image. * To make hope for non-zero reply the stream has to be the original stream * of an IsoFile with .from_old_session==1. The call is safe with any stream * type, though, unless fsrc_stream_class would be used without FSrcStreamData. * @return 1= returned parameters are valid, 0=no ZF info found , <0 error */ int iso_stream_get_src_zf(IsoStream *stream, uint8_t zisofs_algo[2], int *header_size_div4, int *block_size_log2, uint64_t *uncompressed_size, int flag); /** * Set the inode number of a stream that is based on FSrcStreamData, i.e. * stems from the imported ISO image. * @return 1 = ok , 0 = not an ISO image stream , <0 = error */ int iso_stream_set_image_ino(IsoStream *stream, ino_t ino, int flag); /** * Read the full required amount of data unless error or EOF occurs. * Fill missing bytes by 0s. * @param count Required amount * @param got Returns number of actually read bytes * @return * 1 no problem encountered, 0 EOF encountered, < 0 error */ int iso_stream_read_buffer(IsoStream *stream, char *buf, size_t count, size_t *got); /** * @return 1=ok, md5 is valid, * 0= not ok * <0 fatal error, abort */ int iso_stream_make_md5(IsoStream *stream, char md5[16], int flag); /** * Create a clone of the input stream of old_stream and a roughly initialized * clone of old_stream which has the same class and refcount 1. Its data * pointer will be NULL and needs to be filled by an expert which knows how * to clone the data of old_stream. * @param old_stream The existing stream which is in process of cloning * @param new_stream Will return the uninitialized memory object which shall * later become the clone of old_stream. * @param new_input The clone of the input stream of old stream. * @param flag Submit 0 for now. * @return ISO_SUCCESS or an error code <0 */ int iso_stream_clone_filter_common(IsoStream *old_stream, IsoStream **new_stream, IsoStream **new_input, int flag); /** * Dispose the internal list of stream class cmp_ino() functions. It is * a static global of stream.c, created and used by iso_stream_cmp_ino(). * This function is supposed to be called by iso_finish() only. */ int iso_stream_destroy_cmpranks(int flag); #endif /*STREAM_H_*/ /* * Copyright (c) 2008 Vreixo Formoso * Copyright (c) 2010 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libisofs.h" #include "system_area.h" #include "eltorito.h" #include "filesrc.h" #include "ecma119_tree.h" #include "image.h" #include "messages.h" #include "ecma119.h" #include "writer.h" #include #include #include #include #include #include /* for gettimeofday() */ #include /* >>> Need ./configure test for uuid_generate() which checks for: uuid_t, uuid_generate, the need for -luuid */ /* #define Libisofs_with_uuid_generatE 1 */ #ifdef Libisofs_with_uuid_generatE #include #endif /* O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif /* * Create a MBR for an isohybrid enabled ISOLINUX boot image. * See libisofs/make_isohybrid_mbr.c * Deprecated. */ int make_isohybrid_mbr(int bin_lba, int *img_blocks, char *mbr, int flag); /* * The New ISOLINUX MBR Producer. * Be cautious with changing parameters. Only few combinations are tested. * */ int make_isolinux_mbr(uint32_t *img_blocks, Ecma119Image *t, int part_offset, int part_number, int fs_type, uint8_t *buf, int flag); /* Find out whether GPT and APM are desired by isohybrid flag bit0 = register APM and GPT requests in Ecma119Image */ int assess_isohybrid_gpt_apm(Ecma119Image *t, int *gpt_count, int gpt_idx[128], int *apm_count, int flag); static int precompute_gpt(Ecma119Image *t); /* * @param flag bit0= img_blocks is start address rather than end address: do not subtract 1 bit1= img_blocks is counted in 512-byte units rather than 2 KiB */ static void iso_compute_cyl_head_sec(uint64_t img_blocks, int hpc, int sph, uint32_t *end_lba, uint32_t *end_sec, uint32_t *end_head, uint32_t *end_cyl, int flag) { uint64_t secs; if(flag & 2) secs = img_blocks; else secs = img_blocks * 4; if (secs > (uint64_t) 0xfffffffc) secs = 0xfffffffc; /* truncate rather than roll over */ if (flag & 1) *end_lba = secs; /* first valid 512-lba */ else secs = *end_lba = secs - 1; /* last valid 512-lba */ *end_cyl = secs / (sph * hpc); secs -= *end_cyl * sph * hpc; *end_head = secs / sph; *end_sec = secs - *end_head * sph + 1; /* Sector count starts by 1 */ if (*end_cyl >= 1024) { *end_cyl = 1023; *end_head = hpc - 1; *end_sec = sph; } } /* @param flag bit0= The path contains instructions for the interval reader @return ISO_SUCCESS = ok, partition will be written ISO_SUCCESS + 1 = interval which shall be kept in place else : error code */ static int compute_partition_size(Ecma119Image *t, char *disk_path, uint32_t *size, int flag) { int ret, keep; off_t num; struct stat stbuf; struct iso_interval_reader *ivr; off_t byte_count; if (flag & 1) { ret = iso_interval_reader_new(t->image, disk_path, &ivr, &byte_count, 0); if (ret < 0) return ret; *size = (byte_count + BLOCK_SIZE - 1) / BLOCK_SIZE; keep = iso_interval_reader_keep(t, ivr, 0); iso_interval_reader_destroy(&ivr, 0); if (keep < 0) return keep; return ISO_SUCCESS + (keep > 0); } *size = 0; ret = stat(disk_path, &stbuf); if (ret == -1) return ISO_BAD_PARTITION_FILE; if (! S_ISREG(stbuf.st_mode)) return ISO_BAD_PARTITION_FILE; num = ((stbuf.st_size + 2047) / 2048); if (num > 0x3fffffff || num == 0) return ISO_BAD_PARTITION_FILE; *size = num; return ISO_SUCCESS; } /* Compute size and position of appended partitions. @param flag bit0= Partitions inside ISO : update t->curblock */ int iso_compute_append_partitions(Ecma119Image *t, int flag) { int ret, i, sa_type, cyl_align, cyl_size = 0; int first_partition, last_partition; uint32_t pos, size, add_pos = 0; off_t start_byte, byte_count; char msg[128]; sa_type = (t->system_area_options >> 2) & 0x3f; cyl_align = (t->system_area_options >> 8) & 0x3; if (sa_type == 0 && cyl_align == 3) { cyl_size = t->partition_heads_per_cyl * t->partition_secs_per_head; if (cyl_size % 4) cyl_size = 0; else cyl_size /= 4; } #ifdef Libisofs_appended_partitions_inlinE pos = t->curblock; #else pos = (t->vol_space_size + t->opts->ms_block); #endif iso_tell_max_part_range(t->opts, &first_partition, &last_partition, 0); for (i = 0; i < ISO_MAX_PARTITIONS; i++) { if (t->opts->appended_partitions[i] == NULL) continue; if (t->opts->appended_partitions[i][0] == 0) continue; if (i + 1 > last_partition || i + 1 < first_partition) { sprintf(msg, "Partition number %d of appended partition is out of range [%d - %d]", i + 1, first_partition, last_partition); iso_msgs_submit(0, msg, 0, "FAILURE", 0); return ISO_BAD_PARTITION_NO; } ret = compute_partition_size(t, t->opts->appended_partitions[i], &size, t->opts->appended_part_flags[i]); if (ret < 0) return ret; if (ret == ISO_SUCCESS + 1) { /* Interval from imported_iso in add-on session */ t->appended_part_prepad[i] = 0; ret = iso_interval_reader_start_size(t, t->opts->appended_partitions[i], &start_byte, &byte_count, 0); if (ret < 0) return ret; t->appended_part_start[i] = start_byte / 2048; t->appended_part_size[i] = size; t->opts->iso_mbr_part_type = 0; continue; } add_pos = 0; if (sa_type == 3 && (pos % ISO_SUN_CYL_SIZE)) { add_pos = ISO_SUN_CYL_SIZE - (pos % ISO_SUN_CYL_SIZE); } else if (cyl_size > 0 && (pos % cyl_size)) { add_pos = cyl_size - (pos % cyl_size); } t->appended_part_prepad[i] = add_pos; t->appended_part_start[i] = pos + add_pos; if (cyl_size > 0 && (size % cyl_size)) { /* Obey cylinder alignment (missing data will be written as zeros by iso_write_partition_file()) */ size += cyl_size - (size % cyl_size); } t->appended_part_size[i] = size; pos += add_pos + size; t->total_size += (((off_t) add_pos) + size) * 2048; if (flag & 1) t->curblock = pos; } return ISO_SUCCESS; } static int mbr_part_slot_is_unused(uint8_t *slot) { int i; for (i = 0; i < 16; i++) if (slot[i] != 0) break; if (i >= 16) return 1; return 0; } /* @param flag bit1= partition_offset and partition_size are counted in blocks of 512 rather than 2048 */ static int write_mbr_partition_entry(int partition_number, int partition_type, uint64_t partition_offset, uint64_t partition_size, int sph, int hpc, uint8_t *buf, int flag) { uint8_t *wpt; uint32_t end_lba, end_sec, end_head, end_cyl; uint32_t start_lba, start_sec, start_head, start_cyl; uint32_t after_end; int i; after_end = partition_offset + partition_size; iso_compute_cyl_head_sec((uint64_t) partition_offset, hpc, sph, &start_lba, &start_sec, &start_head, &start_cyl, 1 | (flag & 2)); iso_compute_cyl_head_sec((uint64_t) after_end, hpc, sph, &end_lba, &end_sec, &end_head, &end_cyl, (flag & 2)); wpt = buf + 446 + (partition_number - 1) * 16; /* Not bootable */ *(wpt++) = 0x00; /* C/H/S of the start */ *(wpt++) = start_head; *(wpt++) = start_sec | ((start_cyl & 0x300) >> 2); *(wpt++) = start_cyl & 0xff; /* (partition type) */ *(wpt++) = partition_type; /* 3 bytes of C/H/S end */ *(wpt++) = end_head; *(wpt++) = end_sec | ((end_cyl & 0x300) >> 2); *(wpt++) = end_cyl & 0xff; /* LBA start in little endian */ for (i = 0; i < 4; i++) *(wpt++) = (start_lba >> (8 * i)) & 0xff; /* Number of sectors in partition, little endian */ end_lba = end_lba - start_lba + 1; for (i = 0; i < 4; i++) *(wpt++) = (end_lba >> (8 * i)) & 0xff; /* Afaik, partition tables are recognize donly with MBR signature */ buf[510] = 0x55; buf[511] = 0xAA; return ISO_SUCCESS; } /* This is the gesture of grub-mkisofs --protective-msdos-label as explained by Vladimir Serbinenko , 2 April 2010, on grub-devel@gnu.org "Currently we use first and not last entry. You need to: 1) Zero-fill 446-510 2) Put 0x55, 0xAA into 510-512 3) Put 0x80 (for bootable partition), 0, 2, 0 (C/H/S of the start), 0xcd (partition type), [3 bytes of C/H/S end], 0x01, 0x00, 0x00, 0x00 (LBA start in little endian), [LBA end in little endian] at 446-462 " "C/H/S end" means the CHS address of the last block in the partition. It seems that not "[LBA end in little endian]" but "number of blocks" should go into bytes 458-461. But with a start lba of 1, this is the same number. See also http://en.wikipedia.org/wiki/Master_boot_record flag bit0= do not write 0x55, 0xAA to 510,511 bit1= do not mark partition as bootable */ static int make_grub_msdos_label(uint32_t img_blocks, int sph, int hpc, uint8_t part_type, uint8_t *buf, int flag) { uint8_t *wpt; uint32_t end_lba, end_sec, end_head, end_cyl; int i; iso_compute_cyl_head_sec((uint64_t) img_blocks, hpc, sph, &end_lba, &end_sec, &end_head, &end_cyl, 0); /* 1) Zero-fill 446-510 */ wpt = buf + 446; memset(wpt, 0, 64); if (!(flag & 1)) { /* 2) Put 0x55, 0xAA into 510-512 (actually 510-511) */ buf[510] = 0x55; buf[511] = 0xAA; } if ((!(flag & 2)) && part_type != 0xee && part_type != 0xef) { /* 3) Put 0x80 (for bootable partition), */ *(wpt++) = 0x80; } else { *(wpt++) = 0; } /* 0, 2, 0 (C/H/S of the start), */ *(wpt++) = 0; *(wpt++) = 2; *(wpt++) = 0; /* 0xcd (partition type) */ *(wpt++) = part_type; /* [3 bytes of C/H/S end], */ *(wpt++) = end_head; *(wpt++) = end_sec | ((end_cyl & 0x300) >> 2); *(wpt++) = end_cyl & 0xff; /* 0x01, 0x00, 0x00, 0x00 (LBA start in little endian), */ *(wpt++) = 0x01; *(wpt++) = 0x00; *(wpt++) = 0x00; *(wpt++) = 0x00; /* [LBA end in little endian] */ for (i = 0; i < 4; i++) *(wpt++) = (end_lba >> (8 * i)) & 0xff; /* at 446-462 */ if (wpt - buf != 462) { fprintf(stderr, "libisofs: program error in make_grub_msdos_label: \"assert 462\"\n"); return ISO_ASSERT_FAILURE; } return ISO_SUCCESS; } /* @param flag bit0= zeroize partitions entries 2, 3, 4 bit1= UEFI protective MBR: start LBA = 1 */ static int iso_offset_partition_start(uint32_t img_blocks, int post_part_pad, uint32_t partition_offset, int sph, int hpc, uint8_t *buf, int flag) { uint8_t *wpt; uint32_t end_lba, end_sec, end_head, end_cyl; uint32_t start_lba, start_sec, start_head, start_cyl; uint64_t img_hd_blocks; int i; iso_compute_cyl_head_sec((uint64_t) partition_offset, hpc, sph, &start_lba, &start_sec, &start_head, &start_cyl, 1); img_hd_blocks = ((uint64_t) img_blocks) * 4 - post_part_pad / 512; iso_compute_cyl_head_sec(img_hd_blocks, hpc, sph, &end_lba, &end_sec, &end_head, &end_cyl, 2); if (flag & 2) { start_lba = 1; start_sec = 2; start_head = start_cyl = 0; } wpt = buf + 446; /* Let pass only legal bootability values */ if (*wpt != 0 && *wpt != 0x80) (*wpt) = 0; wpt++; /* C/H/S of the start */ *(wpt++) = start_head; *(wpt++) = start_sec | ((start_cyl & 0x300) >> 2); *(wpt++) = start_cyl & 0xff; /* (partition type) */ wpt++; /* 3 bytes of C/H/S end */ *(wpt++) = end_head; *(wpt++) = end_sec | ((end_cyl & 0x300) >> 2); *(wpt++) = end_cyl & 0xff; /* LBA start in little endian */ for (i = 0; i < 4; i++) *(wpt++) = (start_lba >> (8 * i)) & 0xff; /* Number of sectors in partition, little endian */ end_lba = end_lba - start_lba + 1; for (i = 0; i < 4; i++) *(wpt++) = (end_lba >> (8 * i)) & 0xff; if (wpt - buf != 462) { fprintf(stderr, "libisofs: program error in iso_offset_partition_start: \"assert 462\"\n"); return ISO_ASSERT_FAILURE; } if (flag & 1) /* zeroize the other partition entries */ memset(wpt, 0, 3 * 16); return ISO_SUCCESS; } static int boot_nodes_from_iso_path(Ecma119Image *t, char *path, IsoNode **iso_node, Ecma119Node **ecma_node, char *purpose, int flag) { int ret; ret = iso_tree_path_to_node(t->image, path, iso_node); if (ret <= 0) { iso_msg_submit(t->image->id, ISO_BOOT_FILE_MISSING, 0, "Cannot find in ISO image: %s '%s'", purpose, path); return ISO_BOOT_FILE_MISSING; } if ((*iso_node)->type != LIBISO_FILE) { iso_msg_submit(t->image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Designated boot file is not a data file: '%s'", path); return ISO_BOOT_IMAGE_NOT_VALID; } *ecma_node= ecma119_search_iso_node(t, *iso_node); if (*ecma_node == NULL) { iso_msg_submit(t->image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Program error: IsoFile has no Ecma119Node: '%s'", path); return ISO_ASSERT_FAILURE; } else { if ((*ecma_node)->type != ECMA119_FILE) { iso_msg_submit(t->image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Program error: Ecma119Node of IsoFile is no ECMA119_FILE: '%s'", path); return ISO_ASSERT_FAILURE; } } return ISO_SUCCESS; } /* This function was implemented according to doc/boot_sectors.txt section "MIPS Volume Header" which was derived by Thomas Schmitt from cdrkit-1.1.10/genisoimage/boot-mips.c by Steve McIntyre which is based on work of Florian Lohoff and Thiemo Seufer who possibly learned from documents of MIPS Computer Systems, Inc. and Silicon Graphics Computer Systems, Inc. This function itself is entirely under copyright (C) 2010 Thomas Schmitt. */ static int make_mips_volume_header(Ecma119Image *t, uint8_t *buf, int flag) { char *namept, *name_field; uint32_t num_cyl, idx, blocks, num, checksum; off_t image_size; static uint32_t bps = 512, spt = 32; Ecma119Node *ecma_node; IsoNode *node; IsoStream *stream; off_t file_size; uint32_t file_lba; int ret; /* Bytes 512 to 32767 may come from image or external file */ memset(buf, 0, 512); image_size = t->curblock * 2048; /* 0 - 3 | 0x0be5a941 | Magic number */ iso_msb(buf, 0x0be5a941, 4); /* 28 - 29 | num_cyl_l | Number of usable cylinder, lower two bytes */ num_cyl = (image_size + (bps * spt) - 1) / (bps * spt); iso_msb(buf + 28, num_cyl & 0xffff, 2); /* 32 - 33 | 1 | Number of tracks per cylinder */ iso_msb(buf + 32, 1, 2); /* 35 - 35 | num_cyl_h | Number of usable cylinders, high byte */ buf[35] = (num_cyl >> 16) & 0xff; /* 38 - 39 | 32 | Sectors per track */ iso_msb(buf + 38, spt, 2); /* 40 - 41 | 512 | Bytes per sector */ iso_msb(buf + 40, bps, 2); /* 44 - 47 | 0x00000034 | Controller characteristics */ iso_msb(buf + 44, 0x00000034, 4); /* 72 - 87 | ========== | Volume Directory Entry 1 */ /* 72 - 79 | boot_name | Boot file basename */ /* 80 - 83 | boot_block | ISO 9660 LBA of boot file * 4 */ /* 84 - 87 | boot_bytes | File length in bytes */ /* 88 - 311 | 0 | Volume Directory Entries 2 to 15 */ for (idx = 0; (int) idx < t->image->num_mips_boot_files; idx++) { ret = boot_nodes_from_iso_path(t, t->image->mips_boot_file_paths[idx], &node, &ecma_node, "MIPS boot file", 0); if (ret < 0) return ret; namept = (char *) iso_node_get_name(node); name_field = (char *) (buf + (72 + 16 * idx)); strncpy(name_field, namept, 8); file_lba = ecma_node->info.file->sections[0].block; iso_msb(buf + (72 + 16 * idx) + 8, file_lba * 4, 4); stream = iso_file_get_stream((IsoFile *) node); file_size = iso_stream_get_size(stream); /* genisoimage rounds up to full multiples of 2048. libisofs did this too until 2020, but the arcload mips boot loader throws error if the rounded size is stored here. So now the exact bytecount gets stored. */ iso_msb(buf + (72 + 16 * idx) + 12, file_size, 4); } /* 408 - 411 | part_blks | Number of 512 byte blocks in partition */ blocks = (image_size + bps - 1) / bps; iso_msb(buf + 408, blocks, 4); /* 416 - 419 | 0 | Partition is volume header */ iso_msb(buf + 416, 0, 4); /* 432 - 435 | part_blks | Number of 512 byte blocks in partition */ iso_msb(buf + 432, blocks, 4); iso_msb(buf + 444, 6, 4); /* 504 - 507 | head_chk | Volume header checksum The two's complement of bytes 0 to 503 read as big endian unsigned 32 bit: sum(32-bit-words) + head_chk == 0 */ checksum = 0; for (idx = 0; idx < 504; idx += 4) { num = iso_read_msb(buf + idx, 4); /* Addition modulo a natural number is commutative and associative. Thus the inverse of a sum is the sum of the inverses of the addends. */ checksum -= num; } iso_msb(buf + 504, checksum, 4); return ISO_SUCCESS; } /* The following two functions were implemented according to doc/boot_sectors.txt section "MIPS Little Endian" which was derived by Thomas Schmitt from cdrkit-1.1.10/genisoimage/boot-mipsel.c by Steve McIntyre which is based on work of Florian Lohoff and Thiemo Seufer, and from by Free Software Foundation, Inc. Both functions are entirely under copyright (C) 2010 Thomas Schmitt. */ /** * Read the necessary ELF information from the first MIPS boot file. * This is done before image writing starts. */ int iso_read_mipsel_elf(Ecma119Image *t, int flag) { uint32_t phdr_adr, todo, count; int ret; uint8_t *elf_buf = NULL; IsoNode *iso_node; Ecma119Node *ecma_node; IsoStream *stream; if (t->image->num_mips_boot_files <= 0) {ret = ISO_SUCCESS; goto ex;} LIBISO_ALLOC_MEM(elf_buf, uint8_t, 2048); ret = boot_nodes_from_iso_path(t, t->image->mips_boot_file_paths[0], &iso_node, &ecma_node, "MIPS boot file", 0); if (ret < 0) goto ex; stream = iso_file_get_stream((IsoFile *) iso_node); ret = iso_stream_open(stream); if (ret < 0) { iso_msg_submit(t->image->id, ret, 0, "Cannot open designated MIPS boot file '%s'", t->image->mips_boot_file_paths[0]); goto ex; } ret = iso_stream_read(stream, elf_buf, 32); if (ret != 32) { cannot_read:; iso_stream_close(stream); iso_msg_submit(t->image->id, ret, 0, "Cannot read from designated MIPS boot file '%s'", t->image->mips_boot_file_paths[0]); goto ex; } /* 24 - 27 | e_entry | Entry point virtual address */ t->mipsel_e_entry = iso_read_lsb(elf_buf + 24, 4); /* 28 - 31 | e_phoff | Program header table file offset */ phdr_adr = iso_read_lsb(elf_buf + 28, 4); /* Skip stream up to byte address phdr_adr */ todo = phdr_adr - 32; while (todo > 0) { if (todo > 2048) count = 2048; else count = todo; todo -= count; ret = iso_stream_read(stream, elf_buf, count); if (ret != (int) count) goto cannot_read; } ret = iso_stream_read(stream, elf_buf, 20); if (ret != 20) goto cannot_read; /* 4 - 7 | p_offset | Segment file offset */ t->mipsel_p_offset = iso_read_lsb(elf_buf + 4, 4); /* 8 - 11 | p_vaddr | Segment virtual address */ t->mipsel_p_vaddr = iso_read_lsb(elf_buf + 8, 4); /* 16 - 19 | p_filesz | Segment size in file */ t->mipsel_p_filesz = iso_read_lsb(elf_buf + 16, 4); iso_stream_close(stream); ret = ISO_SUCCESS; ex:; LIBISO_FREE_MEM(elf_buf); return ret; } /** * Write DEC Bootblock from previously read ELF parameters. * This is done when image writing has already begun. */ static int make_mipsel_boot_block(Ecma119Image *t, uint8_t *buf, int flag) { int ret; uint32_t seg_size, seg_start; IsoNode *iso_node; Ecma119Node *ecma_node; /* Bytes 512 to 32767 may come from image or external file */ memset(buf, 0, 512); if (t->image->num_mips_boot_files <= 0) return ISO_SUCCESS; ret = boot_nodes_from_iso_path(t, t->image->mips_boot_file_paths[0], &iso_node, &ecma_node, "MIPS boot file", 0); if (ret < 0) return ret; /* 8 - 11 | 0x0002757a | Magic number */ iso_lsb(buf + 8, 0x0002757a, 4); /* 12 - 15 | 1 | Mode 1: Multi extent boot */ iso_lsb(buf + 12, 1, 4); /* 16 - 19 | load_adr | Load address */ iso_lsb(buf + 16, t->mipsel_p_vaddr, 4); /* 20 - 23 | exec_adr | Execution address */ iso_lsb(buf + 20, t->mipsel_e_entry, 4); /* 24 - 27 | seg_size | Segment size in file. */ seg_size = (t->mipsel_p_filesz + 511) / 512; iso_lsb(buf + 24, seg_size, 4); /* 28 - 31 | seg_start | Segment file offset */ seg_start = ecma_node->info.file->sections[0].block * 4 + (t->mipsel_p_offset + 511) / 512; iso_lsb(buf + 28, seg_start, 4); return ISO_SUCCESS; } /* The following two functions were implemented according to doc/boot_sectors.txt section "SUN Disk Label and boot images" which was derived by Thomas Schmitt from cdrtools-2.01.01a77/mkisofs/sunlabel.h cdrtools-2.01.01a77/mkisofs/mkisofs.8 by Joerg Schilling Both functions are entirely under copyright (C) 2010 Thomas Schmitt. */ /* @parm flag bit0= copy from next lower valid partition table entry */ static int write_sun_partition_entry(int partition_number, char *appended_partitions[], uint32_t partition_offset[], uint32_t partition_size[], uint32_t cyl_size, uint8_t *buf, int flag) { uint8_t *wpt; int read_idx, i; if (partition_number < 1 || partition_number > 8) return ISO_ASSERT_FAILURE; /* 142 - 173 | ========== | 8 partition entries of 4 bytes */ wpt = buf + 142 + (partition_number - 1) * 4; if (partition_number == 1) iso_msb(wpt, 4, 2); /* 4 = User partition */ else iso_msb(wpt, 2, 2); /* 2 = Root partition with boot image */ iso_msb(wpt + 2, 0x10, 2); /* Permissions: 0x10 = read-only */ /* 444 - 507 | ========== | Partition table */ wpt = buf + 444 + (partition_number - 1) * 8; read_idx = partition_number - 1; if (flag & 1) { /* Search next lower valid partition table entry. #1 is default */ for (read_idx = partition_number - 2; read_idx > 0; read_idx--) if (appended_partitions[read_idx] != NULL) if (appended_partitions[read_idx][0] != 0) break; } iso_msb(wpt, partition_offset[read_idx] / (uint32_t) ISO_SUN_CYL_SIZE, 4); iso_msb(wpt + 4, partition_size[read_idx] * 4, 4); /* 510 - 511 | checksum | The result of exoring 2-byte words 0 to 254 */ buf[510] = buf[511] = 0; for (i = 0; i < 510; i += 2) { buf[510] ^= buf[i]; buf[511] ^= buf[i + 1]; } return ISO_SUCCESS; } /** * Write SUN Disk Label with ISO in partition 1 and unused 2 to 8 */ static int make_sun_disk_label(Ecma119Image *t, uint8_t *buf, int flag) { int ret, i, l; uint64_t blk; /* Bytes 512 to 32767 may come from image or external file */ memset(buf, 0, 512); /* 0 - 127 | label | ASCII Label */ if (t->opts->ascii_disc_label[0]) { for (l = 0; l < 128 && t->opts->ascii_disc_label[l] != 0; l++); if (l > 0) memcpy((char *) buf, t->opts->ascii_disc_label, l); } else { strcpy((char *) buf, "CD-ROM Disc with Sun sparc boot created by libisofs"); } /* 128 - 131 | 1 | Layout version */ iso_msb(buf + 128, 1, 4); /* 140 - 141 | 8 | Number of partitions */ iso_msb(buf + 140, 8, 2); /* 188 - 191 | 0x600ddeee | vtoc sanity */ iso_msb(buf + 188, 0x600ddeee, 4); /* 420 - 421 | 350 | Rotations per minute */ iso_msb(buf + 420, 350, 2); /* 422 - 423 | 2048 | Number of physical cylinders (fixely 640 MB) */ iso_msb(buf + 422, 2048, 2); /* 430 - 431 | 1 | interleave factor */ iso_msb(buf + 430, 1, 2); /* 432 - 433 | 2048 | Number of data cylinders (fixely 640 MB) */ iso_msb(buf + 432, 2048, 2); /* 436 - 437 | 1 | Number of heads per cylinder (1 cyl = 320 kB)*/ iso_msb(buf + 436, 1, 2); /* 438 - 439 | 640 | Number of sectors per head (1 head = 320 kB) */ iso_msb(buf + 438, 640, 2); /* 508 - 509 | 0xdabe | Magic Number */ iso_msb(buf + 508, 0xdabe, 2); if (t->sparc_core_src != NULL) { /* May be used for grub-sparc. */ blk= ((uint64_t) t->sparc_core_src->sections[0].block) * (uint64_t) 2048; for (i = 0; i < 8; i++) buf[Libisofs_grub2_sparc_patch_adr_poS + i] = blk >> ((7 - i) * 8); iso_msb(buf + Libisofs_grub2_sparc_patch_size_poS, t->sparc_core_src->sections[0].size, 4); } /* Set partition 1 to describe ISO image and compute checksum */ t->appended_part_start[0] = 0; t->appended_part_size[0] = t->curblock; ret = write_sun_partition_entry(1, t->opts->appended_partitions, t->appended_part_start, t->appended_part_size, ISO_SUN_CYL_SIZE, buf, 0); if (ret < 0) return ret; return ISO_SUCCESS; } static int hppa_palo_get_filepar(Ecma119Image *t, char *path, uint32_t *adr, uint32_t *len, int flag) { int ret; IsoNode *iso_node; Ecma119Node *ecma_node; off_t adr64; ret = boot_nodes_from_iso_path(t, path, &iso_node, &ecma_node, "HP-PA PALO boot file", 0); if (ret < 0) return ret; if (iso_node_get_type(iso_node) != LIBISO_FILE) { iso_msg_submit(t->image->id, ISO_HPPA_PALO_NOTREG, 0, "HP-PA PALO file is not a data file"); return ISO_HPPA_PALO_NOTREG; } adr64 = ((off_t) 2048) * (off_t) ecma_node->info.file->sections[0].block; if (adr64 > 0x7fffffff) { iso_msg_submit(t->image->id, ISO_HPPA_PALO_OFLOW, 0, "HP-PA PALO boot address exceeds 2 GB"); return ISO_HPPA_PALO_OFLOW; } *adr = adr64; *len = ecma_node->info.file->sections[0].size; return ISO_SUCCESS; } /** * Write HP-PA PALO boot sector. See doc/boot_sectors.txt * * learned from cdrkit-1.1.10/genisoimage/boot-hppa.c * by Steve McIntyre * who states "Heavily inspired by palo" * Public mail conversations with Helge Deller, beginning with * https://lists.debian.org/debian-hppa/2014/01/msg00016.html * http://git.kernel.org/cgit/linux/kernel/git/deller/palo.git/tree/lib/ * (especially struct firstblock in common.h and struct partition in part.h) * */ static int make_hppa_palo_sector(Ecma119Image *t, uint8_t *buf, int hdrversion, int flag) { int ret; IsoImage *img; uint32_t adr, len; img = t->image; if (img->hppa_cmdline == NULL && img->hppa_bootloader == NULL && img->hppa_kernel_32 == NULL && img->hppa_kernel_64 == NULL && img->hppa_ramdisk == NULL) return ISO_SUCCESS; if (img->hppa_cmdline == NULL || img->hppa_bootloader == NULL || img->hppa_kernel_32 == NULL || img->hppa_kernel_64 == NULL || img->hppa_ramdisk == NULL) { iso_msg_submit(img->id, ISO_HPPA_PALO_INCOMPL, 0, "Incomplete HP-PA PALO boot parameters"); return ISO_HPPA_PALO_INCOMPL; } if (hdrversion == 4) { /* Bytes 256 to 32767 may come from loaded ISO image or external file */ memset(buf, 0, 256); } else if(hdrversion == 5) { memset(buf, 0, 512); memset(buf + 1024, 0, 1024); } else { iso_msg_submit(img->id, ISO_WRONG_ARG_VALUE, 0, "Unsupported HP-PA PALO header version %d (can do 4 or 5)", hdrversion); return ISO_WRONG_ARG_VALUE; } /* Magic */ iso_msb(buf + 0, 0x8000, 2); /* Name of boot loader */ memcpy(buf + 2, "PALO", 5); /* Version */ buf[7] = hdrversion; /* Byte address and byte count of the "HPPA 32-bit kernel" file */ ret = hppa_palo_get_filepar(t, img->hppa_kernel_32, &adr, &len, 0); if (ret < 0) return ret; iso_msb(buf + 8, adr, 4); iso_msb(buf + 12, len, 4); /* Byte address and byte count of the "HPPA ramdisk" file */ ret = hppa_palo_get_filepar(t, img->hppa_ramdisk, &adr, &len, 0); if (ret < 0) return ret; iso_msb(buf + 16, adr, 4); iso_msb(buf + 20, len, 4); if (hdrversion == 4) { /* "Command line" */ if (strlen(img->hppa_cmdline) > 127) { iso_msg_submit(img->id, ISO_HPPA_PALO_CMDLEN, 0, "HP-PA PALO command line too long"); return ISO_HPPA_PALO_CMDLEN; } memcpy(buf + 24, img->hppa_cmdline, strlen(img->hppa_cmdline) + 1); } /* Byte address and byte count of the "HPPA 64-bit kernel" file */ ret = hppa_palo_get_filepar(t, img->hppa_kernel_64, &adr, &len, 0); if (ret < 0) return ret; iso_msb(buf + 232, adr, 4); iso_msb(buf + 236, len, 4); /* Byte address and byte count of the "HPPA bootloader" file */ ret = hppa_palo_get_filepar(t, img->hppa_bootloader, &adr, &len, 0); if (ret < 0) return ret; iso_msb(buf + 240, adr, 4); iso_msb(buf + 244, len, 4); if (hdrversion == 5) { if (strlen(img->hppa_cmdline) > 1023) { iso_msg_submit(img->id, ISO_HPPA_PALO_CMDLEN, 0, "HP-PA PALO command line too long"); return ISO_HPPA_PALO_CMDLEN; } memcpy(buf + 1024, img->hppa_cmdline, strlen(img->hppa_cmdline) + 1); } return ISO_SUCCESS; } /** * Write DEC Alpha boot sector. See doc/boot_sectors.txt * * learned from cdrkit-1.1.10/genisoimage/boot-alpha.c * by Steve McIntyre * who states "Heavily inspired by isomarkboot by David Mosberger in 1996" * */ static int make_dec_alpha_sector(Ecma119Image *t, uint8_t *buf, int flag) { int ret, i; IsoImage *img; IsoNode *iso_node; Ecma119Node *ecma_node; uint64_t size, lba, checksum = 0; img = t->image; if (img->alpha_boot_image == NULL) return ISO_SUCCESS; ret = boot_nodes_from_iso_path(t, img->alpha_boot_image, &iso_node, &ecma_node, "DEC Alpha boot file", 0); if (ret < 0) return ret; memset(buf, 0, 512); strcpy((char *) buf, "Linux/Alpha aboot for ISO filesystem."); lba = ecma_node->info.file->sections[0].block * 4; size = ecma_node->info.file->sections[0].size / 512 + !!(ecma_node->info.file->sections[0].size % 512); iso_lsb(buf + 480, size & 0xffffffff, 4); iso_lsb(buf + 484, (size >> 32) & 0xffffffff, 4); iso_lsb(buf + 488, lba & 0xffffffff, 4); iso_lsb(buf + 492, (lba >> 32) & 0xffffffff, 4); for (i = 0; i < 63; i++) checksum += iso_read_lsb64(buf + 8 * i); iso_lsb(buf + 504, checksum & 0xffffffff, 4); iso_lsb(buf + 508, (checksum >> 32) & 0xffffffff, 4); return ISO_SUCCESS; } /* Convenience frontend for iso_register_apm_entry(). name and type are 0-terminated strings. */ int iso_quick_apm_entry(struct iso_apm_partition_request **req_array, int *apm_req_count, uint32_t start_block, uint32_t block_count, char *name, char *type) { int ret, l; struct iso_apm_partition_request *entry; entry = calloc(1, sizeof(struct iso_apm_partition_request)); if (entry == NULL) return ISO_OUT_OF_MEM; entry->start_block = start_block; entry->block_count = block_count; for (l = 0; l < 32 && name[l] != 0; l++); if (l > 0) memcpy((char *) entry->name, name, l); for (l = 0; l < 32 && type[l] != 0; l++); if (l > 0) memcpy((char *) entry->type, type, l); entry->req_status = 0; entry->image_path = NULL; ret = iso_register_apm_entry(req_array, apm_req_count, entry, 0); free(entry); return ret; } static int iso_find_gpt_entry(struct iso_gpt_partition_request **req_array, int gpt_req_count, uint64_t start_block, uint64_t block_count, int *index, int flag) { struct iso_gpt_partition_request *entry; for (*index = 0; *index < gpt_req_count; (*index)++) { entry = req_array[*index]; if (entry->start_block == start_block && entry->block_count == block_count) return 1; } *index = -1; return 0; } /* Convenience frontend for iso_register_gpt_entry(). name has to be already encoded as UTF-16LE. */ int iso_quick_gpt_entry(struct iso_gpt_partition_request **req_array, int *gpt_req_count, uint64_t start_block, uint64_t block_count, uint8_t type_guid[16], uint8_t partition_guid[16], uint64_t flags, uint8_t name[72]) { int ret; struct iso_gpt_partition_request *entry; entry = calloc(1, sizeof(struct iso_gpt_partition_request)); if (entry == NULL) return ISO_OUT_OF_MEM; entry->start_block = start_block; entry->block_count = block_count; memcpy(entry->type_guid, type_guid, 16); memcpy(entry->partition_guid, partition_guid, 16); entry->flags = flags; memcpy(entry->name, name, 72); entry->req_status = 0; entry->image_path = NULL; ret = iso_register_gpt_entry(req_array, gpt_req_count, entry, 0); free(entry); return ret; } int iso_quick_mbr_entry(struct iso_mbr_partition_request **req_array, int *mbr_req_count, uint64_t start_block, uint64_t block_count, uint8_t type_byte, uint8_t status_byte, int desired_slot) { int ret; struct iso_mbr_partition_request *entry; ret = iso_mbr_entry_slot_is_free(req_array, *mbr_req_count, desired_slot); if (ret < 0) desired_slot = 0; else if (ret == 0) return ISO_BOOT_MBR_COLLISION; entry = calloc(1, sizeof(struct iso_mbr_partition_request)); if (entry == NULL) return ISO_OUT_OF_MEM; entry->start_block = start_block; entry->block_count = block_count; entry->type_byte = type_byte; entry->status_byte = status_byte; entry->desired_slot = desired_slot; entry->image_path = NULL; ret = iso_register_mbr_entry(req_array, mbr_req_count, entry, 0); free(entry); return ret; } int iso_mbr_entry_slot_is_free(struct iso_mbr_partition_request **req_array, int mbr_req_count, int slot) { int i; if (slot < 0 || slot > ISO_MBR_ENTRIES_MAX) return -1; if (slot == 0) return 1; for (i = 0; i < mbr_req_count; i++) if (req_array[i]->desired_slot == slot) return 0; return 1; } /** * Compare the block interval positions of two iso_apm_partition_request */ static int cmp_partition_request(const void *f1, const void *f2) { struct iso_partition_request { uint64_t start_block; uint64_t block_count; } *r1, *r2; r1 = *((struct iso_partition_request **) f1); r2 = *((struct iso_partition_request **) f2); if (r1->start_block < r2->start_block) return -1; if (r1->start_block > r2->start_block) return 1; /* In case of overlapping the largest partition shall be first */ if (r1->block_count > r2->block_count) return -1; if (r1->block_count < r2->block_count) return 1; return 0; } /* @param flag bit0= This is the entry in block 1. Its blocks are already in the desired apm_block_size unit. Set block_fac to 1. Set flags to 3 rather than 0x13. */ static int iso_write_apm_entry(Ecma119Image *t, int apm_block_size, struct iso_apm_partition_request *req, uint8_t *buf, int map_entries, int flag) { uint8_t *wpt; uint32_t flags; int block_fac; if ((flag & 1) || (t->apm_req_flags & 4)) block_fac = 1; else block_fac = 2048 / apm_block_size; memset(buf, 0, apm_block_size); wpt = buf; /* Signature */ wpt[0] = 'P'; wpt[1] = 'M'; wpt+= 2; /* reserved */ wpt += 2; /* Number of partition entries */ iso_msb(wpt, (uint32_t) map_entries, 4); wpt += 4; /* Physical block start of partition */ iso_msb(wpt, (uint32_t) (req->start_block * block_fac), 4); wpt += 4; /* Physical block count of partition */ iso_msb(wpt, (uint32_t) (req->block_count * block_fac), 4); wpt += 4; /* Partition name */ memcpy(wpt, req->name, 32); wpt += 32; /* Type string */ memcpy(wpt, req->type, 32); wpt += 32; /* Logical block start */ iso_msb(wpt, (uint32_t) 0, 4); wpt += 4; /* Logical block count */ iso_msb(wpt, (uint32_t) (req->block_count * block_fac), 4); wpt += 4; /* Status flags : bit0= entry is valid , bit1= entry is allocated bit4= partition is readable , bit5= partition is writable bit30= automatic mount (legacy Mac) */ if (flag & 1) { flags = 3; } else { flags = 0x13; if (strncmp((char *) req->type, "Apple_HFS", 9) == 0 && req->type[9] == 0) flags |= 0x40000000; } iso_msb(wpt, flags, 4); wpt += 4; /* boot_block , boot_bytes , processor , reserved : are all 0 */ return ISO_SUCCESS; } /* Sort and fill gaps in requested APM */ static int fill_apm_gaps(Ecma119Image *t, uint32_t img_blocks) { int i, ret, gap_counter = 0, up_to; uint32_t part_end, goal, block_fac = 1; char gap_name[33]; if (t->apm_req_flags & 4) { if (t->opts->apm_block_size == 0) t->opts->apm_block_size = 2048; block_fac = 2048 / t->opts->apm_block_size; } /* Find out whether an entry with start_block <= 1 is requested */ for (i = 0; i < t->apm_req_count; i++) { if (t->apm_req[i]->start_block <= 1) break; } if (i >= t->apm_req_count) { ret = iso_quick_apm_entry(t->apm_req, &(t->apm_req_count), 1, 0, "Apple", "Apple_partition_map"); if (ret < 0) return ret; } qsort(t->apm_req, t->apm_req_count, sizeof(struct iso_apm_partition_request *), cmp_partition_request); if (t->opts->part_like_isohybrid) return 1; /* No filling, only sorting */ /* t->apm_req_count will grow during the loop */ up_to = t->apm_req_count + 1; for (i = 1; i < up_to; i++) { if (i < up_to - 1) goal = (uint32_t) t->apm_req[i]->start_block; else goal = img_blocks * block_fac; if (i == 1) { /* Description of APM itself */ /* Actual APM size is not yet known. Protection begins at PVD */ part_end = 16 * block_fac; if (goal < part_end && goal> 1) part_end = goal; } else { part_end = t->apm_req[i - 1]->start_block + t->apm_req[i - 1]->block_count; } if (part_end > goal) { iso_msg_submit(t->image->id, ISO_BOOT_APM_OVERLAP, 0, "Program error: APM partitions %d and %d overlap by %lu blocks", i - 1, i, part_end - goal); return ISO_BOOT_APM_OVERLAP; } if (t->apm_req_flags & 2) /* Do not fill gaps */ continue; if (part_end < goal || i == up_to - 1) { /* Always add a final entry */ sprintf(gap_name, "Gap%d", gap_counter); gap_counter++; ret = iso_quick_apm_entry(t->apm_req, &(t->apm_req_count), part_end, goal - part_end, gap_name, "ISO9660_data"); if (ret < 0) return ret; /* Mark as automatically placed filler request */ t->apm_req[t->apm_req_count - 1]->req_status |= 1; } } /* Merge list of gap partitions with list of already sorted entries */ if (!(t->apm_req_flags & 2)) /* No gaps were filled */ qsort(t->apm_req, t->apm_req_count, sizeof(struct iso_apm_partition_request *), cmp_partition_request); return 1; } static int rectify_apm(Ecma119Image *t) { int ret; #ifdef NIX /* Disabled */ /* <<< ts B20526 : Dummy mock-up */ if (t->apm_req_count <= 0) { /* ret = iso_quick_apm_entry(t->apm_req, &(t->apm_req_count), 16, 20, "Test1_name_16_20", "Test1_type"); / * >>> Caution: Size 90 causes intentional partition overlap error * / ret = iso_quick_apm_entry(t->apm_req, &(t->apm_req_count), 30, 90, "BAD_30_90_BAD", "Test1_type"); */ ret = iso_quick_apm_entry(t->apm_req, &(t->apm_req_count), 30, 20, "Test1_name_30_20", "Test1_type"); if (ret < 0) return ret; ret = iso_quick_apm_entry(t->apm_req, &(t->apm_req_count), 100, 400, "Test2_name_100_400", "Test2_type"); if (ret < 0) return ret; } #endif /* NIX */ if (t->apm_req_count == 0) return 1; if (t->gpt_req_count > 0 && t->opts->apm_block_size != 2048 && t->apm_req_count > 0) { iso_msgs_submit(0, "GPT and APM requested. APM block size would have to be 2048.", 0, "FAILURE", 0); return ISO_BOOT_APM_GPT_BSIZE; } if (t->apm_req_count > 0) { ret = fill_apm_gaps(t, t->curblock); if (ret < 0) return ret; } return 1; } /* flag bit0= do not write Block0 */ static int iso_write_apm(Ecma119Image *t, uint32_t img_blocks, uint8_t *buf, int flag) { int i, ret; uint32_t block_fac = 1; /* This is a micro mock-up of an APM Block0 and also harmless x86 machine code. */ static uint8_t block0_template[8] = { 0x45, 0x52, 0x02, 0x00, 0xeb, 0x02, 0xff, 0xff }; if (t->apm_req_count <= 0) return 2; if (t->opts->apm_block_size == 0) { /* One cannot be sure that all GPT partitions are registered already. So it is necessary to choose the block size which is combinable with GPT but not mountable on Linux. */ t->opts->apm_block_size = 2048; } if (t->apm_req_flags & 4) block_fac = 2048 / t->opts->apm_block_size; if (!(t->apm_req_flags & 2)) { /* Gaps have been filled. Care for the final one */ /* Adjust last partition to img_size. This size was not known when the number of APM partitions was determined. */ if (img_blocks * block_fac < t->apm_req[t->apm_req_count - 1]->start_block) t->apm_req[t->apm_req_count - 1]->block_count = 0; else t->apm_req[t->apm_req_count - 1]->block_count = img_blocks * block_fac - t->apm_req[t->apm_req_count - 1]->start_block; /* If it is still empty, remove it */ if(t->apm_req[t->apm_req_count - 1]->block_count == 0) { free(t->apm_req[t->apm_req_count - 1]); t->apm_req_count--; } } /* If block size is larger than 512, then not all 63 entries will fit */ if ((t->apm_req_count + 1) * t->opts->apm_block_size > 32768) return ISO_BOOT_TOO_MANY_APM; /* Block 1 describes the APM itself */ t->apm_req[0]->start_block = 1; t->apm_req[0]->block_count = t->apm_req_count; if (!(flag & 1)) { /* Write APM block 0. Very sparse, not to overwrite much of possible MBR. */ memcpy(buf, block0_template, 8); buf[2]= (t->opts->apm_block_size >> 8) & 0xff; buf[3]= 0; } /* Write APM Block 1 to t->apm_req_count */ for (i = 0; i < t->apm_req_count; i++) { ret = iso_write_apm_entry(t, t->opts->apm_block_size, t->apm_req[i], buf + (i + 1) * t->opts->apm_block_size, t->apm_req_count, i == 0); if (ret < 0) return ret; } return ISO_SUCCESS; } static int iso_write_mbr(Ecma119Image *t, uint32_t img_blocks, uint8_t *buf) { int i, ret, req_of_slot[ISO_MBR_ENTRIES_MAX], q, j; #ifdef NIX /* Disabled */ /* <<< Dummy mock-up */ if (t->mbr_req_count <= 0) { ret = iso_quick_mbr_entry(t->mbr_req, &(t->mbr_req_count), (uint64_t) 0, (uint64_t) 0, 0xee, 0, 0); if (ret < 0) return ret; ret = iso_quick_mbr_entry(t->mbr_req, &(t->mbr_req_count), ((uint64_t) 100) * 4, (uint64_t) 0, 0x0c, 0x80, 1); if (ret < 0) return ret; } #endif /* NIX */ if (t->mbr_req_count <= 0) return 2; /* >>> Sort by start block ? */ /* Adjust partition ends */ for (i = 0; i < t->mbr_req_count; i++) { if (i > 0) { if (t->mbr_req[i]->start_block <= t->mbr_req[i - 1]->start_block && !(t->mbr_req[i]->block_count == 0 && t->mbr_req[i]->start_block == t->mbr_req[i - 1]->start_block)) return ISO_BOOT_MBR_OVERLAP; if (t->mbr_req[i - 1]->start_block + t->mbr_req[i - 1]->block_count > t->mbr_req[i]->start_block) return ISO_BOOT_MBR_OVERLAP; } if (t->mbr_req[i]->block_count != 0) continue; if (i < t->mbr_req_count - 1) t->mbr_req[i]->block_count = t->mbr_req[i + 1]->start_block - t->mbr_req[i]->start_block; else t->mbr_req[i]->block_count = ((uint64_t) img_blocks) * 4 - t->mbr_req[i]->start_block; } /* Assign requested entries to slot numbers */ for (i = 0; i < ISO_MBR_ENTRIES_MAX; i++) req_of_slot[i] = -1; for (i = 0; i < t->mbr_req_count; i++) { if (t->mbr_req[i]->desired_slot < 1 || t->mbr_req[i]->desired_slot > ISO_MBR_ENTRIES_MAX) continue; if (req_of_slot[t->mbr_req[i]->desired_slot - 1] >= 0) return ISO_BOOT_MBR_COLLISION; req_of_slot[t->mbr_req[i]->desired_slot - 1] = i; } for (i = 0; i < t->mbr_req_count; i++) { if (t->mbr_req[i]->desired_slot > 0) continue; for (j = 0; j < ISO_MBR_ENTRIES_MAX; j++) if (req_of_slot[j] < 0) break; if (j >= ISO_MBR_ENTRIES_MAX) return ISO_BOOT_TOO_MANY_MBR; req_of_slot[j] = i; } /* Write partition slots */ for (i = 0; i < ISO_MBR_ENTRIES_MAX; i++) { memset(buf + 446 + i * 16, 0, 16); q = req_of_slot[i]; if (q < 0) continue; if (t->mbr_req[q]->block_count == 0) continue; ret = write_mbr_partition_entry(i + 1, (int) t->mbr_req[q]->type_byte, t->mbr_req[q]->start_block, t->mbr_req[q]->block_count, t->partition_secs_per_head, t->partition_heads_per_cyl, buf, 2); if (ret < 0) return ret; buf[446 + i * 16] = t->mbr_req[q]->status_byte; } return ISO_SUCCESS; } static void iso_write_gpt_entry(Ecma119Image *t, uint8_t *buf, uint8_t type_guid[16], uint8_t part_uuid[16], uint64_t start_lba, uint64_t end_lba, uint64_t flags, uint8_t name[72]) { char *wpt; int i; wpt = (char *) buf; memcpy(wpt, type_guid, 16); wpt += 16; for (i = 0; i < 16; i++) if (part_uuid[i]) break; if (i == 16) { if (!t->gpt_disk_guid_set) iso_gpt_uuid(t, t->gpt_disk_guid); t->gpt_disk_guid_set = 1; iso_gpt_uuid(t, part_uuid); } memcpy(wpt, part_uuid, 16); wpt += 16; iso_lsb_to_buf(&wpt, start_lba & 0xffffffff, 4, 0); iso_lsb_to_buf(&wpt, (start_lba >> 32) & 0xffffffff, 4, 0); iso_lsb_to_buf(&wpt, end_lba & 0xffffffff, 4, 0); iso_lsb_to_buf(&wpt, (end_lba >> 32) & 0xffffffff, 4, 0); iso_lsb_to_buf(&wpt, flags & 0xffffffff, 4, 0); iso_lsb_to_buf(&wpt, (flags >> 32) & 0xffffffff, 4, 0); memcpy(wpt, name, 72); } int iso_write_gpt_header_block(Ecma119Image *t, uint32_t img_blocks, uint8_t *buf, uint32_t max_entries, uint32_t part_start, uint32_t p_arr_crc) { static char *sig = "EFI PART"; static char revision[4] = {0x00, 0x00, 0x01, 0x00}; char *wpt; uint32_t crc; off_t back_lba; memset(buf, 0, 512); wpt = (char *) buf; /* >>> Make signature adjustable */ memcpy(wpt, sig, 8); /* no trailing 0 */ wpt += 8; memcpy(wpt, revision, 4); wpt += 4; iso_lsb_to_buf(&wpt, 92, 4, 0); /* CRC will be inserted later */ wpt += 4; /* reserved */ iso_lsb_to_buf(&wpt, 0, 4, 0); /* Own LBA low 32 */ iso_lsb_to_buf(&wpt, 1, 4, 0); /* Own LBA high 32 */ iso_lsb_to_buf(&wpt, 0, 4, 0); /* Backup header LBA is 1 hd block before backup GPT area end */ back_lba = t->gpt_backup_end * 4 - 1; iso_lsb_to_buf(&wpt, (uint32_t) (back_lba & 0xffffffff), 4, 1); iso_lsb_to_buf(&wpt, (uint32_t) (back_lba >> 32), 4, 1); /* First usable LBA for partitions (4 entries per hd block) */ iso_lsb_to_buf(&wpt, part_start + max_entries / 4, 4, 0); iso_lsb_to_buf(&wpt, 0, 4, 0); /* Last usable LBA for partitions is 1 hd block before first backup entry*/ iso_lsb_to_buf(&wpt, (uint32_t) ((back_lba - max_entries / 4 - 1) & 0xffffffff), 4, 1); iso_lsb_to_buf(&wpt, (uint32_t) ((back_lba - max_entries / 4 - 1) >> 32), 4, 1); /* Disk GUID */ if (!t->gpt_disk_guid_set) iso_gpt_uuid(t, t->gpt_disk_guid); t->gpt_disk_guid_set = 1; memcpy(wpt, t->gpt_disk_guid, 16); wpt += 16; /* Partition entries start */ iso_lsb_to_buf(&wpt, part_start, 4, 0); iso_lsb_to_buf(&wpt, 0, 4, 0); /* Number of partition entries */ iso_lsb_to_buf(&wpt, max_entries, 4, 0); /* Size of a partition entry */ iso_lsb_to_buf(&wpt, 128, 4, 0); /* CRC-32 of the partition array */ iso_lsb_to_buf(&wpt, p_arr_crc, 4, 0); /* <<< Only for a first test */ if (wpt - (char *) buf != 92) { iso_msgs_submit(0, "program error : write_gpt_header_block : wpt != 92", 0, "FATAL", 0); return ISO_ISOLINUX_CANT_PATCH; } /* CRC-32 of this header while head_crc is 0 */ crc = iso_crc32_gpt((unsigned char *) buf, 92, 0); wpt = ((char *) buf) + 16; iso_lsb_to_buf(&wpt, crc, 4, 0); return ISO_SUCCESS; } /* Only for up to 36 characters ISO-8859-1 (or ASCII) input */ void iso_ascii_utf_16le(uint8_t gap_name[72]) { int i; for (i = strlen((char *) gap_name) - 1; i >= 0; i--) { gap_name[2 * i] = gap_name[i]; gap_name[2 * i + 1] = 0; } } static int intvl_overlap(uint64_t start1, uint64_t end1, uint64_t start2, uint64_t end2, int second) { if (start1 >= start2 && start1 <= end2) return 1; if (end1 >= start2 && end1 <= end2) return 1; if (!second) return intvl_overlap(start2, end2, start1, end1, 1); return 0; } /* Check APM HFS+ partitions whether they would fit in gaps. If so, add them as GPT partitions, too. */ static int iso_copy_apmhfs_to_gpt(Ecma119Image *t, int flag) { int a, i, counter = 0, ret; uint64_t bfac = 4; static uint8_t hfs_plus_uuid[16] = { 0x00, 0x53, 0x46, 0x48, 0x00, 0x00, 0xaa, 0x11, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac }; static uint8_t zero_uuid[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; uint8_t gpt_name[72]; static uint64_t gpt_flags = (((uint64_t) 1) << 60) | 1; if ((t->apm_req_flags & 4) && t->opts->apm_block_size / 512 > 0) bfac = t->opts->apm_block_size / 512; for (a = 0; a < t->apm_req_count; a++) { if (strcmp((char *) t->apm_req[a]->type, "Apple_HFS") != 0) continue; for (i = 0; i < t->gpt_req_count; i++) if (intvl_overlap(t->apm_req[a]->start_block * bfac, (t->apm_req[a]->start_block + t->apm_req[a]->block_count - 1) * bfac, t->gpt_req[i]->start_block, t->gpt_req[i]->start_block + t->gpt_req[i]->block_count - 1, 0)) break; if (i >= t->gpt_req_count) { memset(gpt_name, 0, 72); counter++; if (counter > 1) sprintf((char *) gpt_name, "HFSPLUS_%d", counter); else sprintf((char *) gpt_name, "HFSPLUS"); iso_ascii_utf_16le(gpt_name); ret = iso_quick_gpt_entry(t->gpt_req, &(t->gpt_req_count), t->apm_req[a]->start_block * bfac, t->apm_req[a]->block_count * bfac, hfs_plus_uuid, zero_uuid, gpt_flags, gpt_name); if (ret < 0) return ret; } } return 1; } static int iso_write_gpt(Ecma119Image *t, uint32_t img_blocks, uint8_t *buf) { static uint8_t basic_data_uuid[16] = { 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 }; uint8_t *wpt; uint32_t p_arr_crc = 0; uint64_t start_lba, end_lba, goal, part_end, next_end, backup_end_lba; uint64_t eff_gpt_flags; int ret, i, gap_counter = 0, up_to, slot, j, to_insert; struct iso_gpt_partition_request *req; char msg[81]; uint8_t gpt_name[72]; static uint8_t zero_uuid[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uint8_t *type_guid; static uint64_t gpt_flags = (((uint64_t) 1) << 60) | 1; if (t->gpt_req_count == 0) return 2; backup_end_lba = ((uint64_t) t->gpt_backup_end - t->gpt_backup_size) * 4; ret = iso_copy_apmhfs_to_gpt(t, 0); if (ret <= 0) return ret; /* Sort and fill gaps */ qsort(t->gpt_req, t->gpt_req_count, sizeof(struct iso_gpt_partition_request *), cmp_partition_request); /* t->gpt_req_count will grow during the loop */ up_to = t->gpt_req_count + 1; goal = 0; part_end = 0; if (t->opts->part_like_isohybrid) up_to = 0; /* No gap filling */ for (i = 0; i < up_to; i++) { if (i < up_to - 1) { goal = t->gpt_req[i]->start_block; } else { goal = ((uint64_t) img_blocks) * 4; if (goal > backup_end_lba) goal = backup_end_lba; } if (i == 0) { if (goal <= 16 * 4) continue; next_end = 16 * 4; } else { next_end = t->gpt_req[i - 1]->start_block + t->gpt_req[i - 1]->block_count; } if (next_end > part_end) part_end = next_end; if (part_end > goal) { if (!(t->gpt_req_flags & 1)) { iso_msg_submit(t->image->id, ISO_BOOT_GPT_OVERLAP, 0, "Program error: GPT partitions %d and %d overlap by %.f blocks", i - 1, i, (double) (part_end - goal)); return ISO_BOOT_GPT_OVERLAP; } } else if (part_end < goal) { memset(gpt_name, 0, 72); type_guid = basic_data_uuid; eff_gpt_flags= gpt_flags; if (goal == t->vol_space_size * (uint64_t) 4 && part_end == t->opts->partition_offset * (uint64_t) 4) { sprintf((char *) gpt_name, "ISO9660"); if (t->opts->iso_gpt_flag & 1) type_guid = t->opts->iso_gpt_type_guid; if (t->system_area_options & (1 << 16)) eff_gpt_flags|= 4; /* Legacy BIOS bootable */ if (t->system_area_options & (1 << 17)) eff_gpt_flags&= ~(((uint64_t) 1) << 60);/* Not read-only */ } else { sprintf((char *) gpt_name, "Gap%d", gap_counter); } iso_ascii_utf_16le(gpt_name); gap_counter++; ret = iso_quick_gpt_entry(t->gpt_req, &(t->gpt_req_count), part_end, goal - part_end, type_guid, zero_uuid, eff_gpt_flags, gpt_name); if (ret < 0) return ret; /* Mark as automatically placed filler request */ t->gpt_req[t->gpt_req_count - 1]->req_status |= 1; } } /* Merge list of gap partitions with list of already sorted entries */ qsort(t->gpt_req, t->gpt_req_count, sizeof(struct iso_gpt_partition_request *), cmp_partition_request); if ((int) t->gpt_max_entries < t->gpt_req_count) return ISO_BOOT_TOO_MANY_GPT; /* Write the GPT entries to buf */ wpt= buf + 512 * t->gpt_part_start; slot= 1; for (i = 0; i < t->gpt_req_count; i++) { req = t->gpt_req[i]; start_lba = req->start_block; end_lba = req->start_block + req->block_count; if (req->start_block == t->opts->partition_offset * ((uint64_t) 4) && req->block_count == ((uint64_t) 4) * 0xffffffff) end_lba = t->vol_space_size * 4; if (end_lba > backup_end_lba) end_lba = backup_end_lba; end_lba = end_lba - 1; if (req->desired_slot > 0) { to_insert= req->desired_slot - slot; if (to_insert > 0 && slot - 1 + to_insert + t->gpt_req_count - i <= (int) t->gpt_max_entries) { /* There is need and room to insert empty partition entries */ for (j = 0; j < to_insert; j++) { memset(wpt, 0, 128); wpt+= 128; slot++; } if (!t->wthread_is_running) { sprintf(msg, "Inserted %d empty GPT slots before appended partition %d", to_insert, req->desired_slot); iso_msgs_submit(0, msg, 0, "DEBUG", 0); } } else if (slot != req->desired_slot) { if (!t->wthread_is_running) { sprintf(msg, "Appended GPT partition becomes number %d instead of desired %d", i + 1, req->desired_slot); iso_msgs_submit(0, msg, 0, "NOTE", 0); } } } iso_write_gpt_entry(t, wpt, req->type_guid, req->partition_guid, start_lba, end_lba, req->flags, req->name); wpt+= 128; slot++; } for (i= slot - 1; i < (int) t->gpt_max_entries; i++) memset(buf + 512 * t->gpt_part_start + 128 * i, 0, 128); p_arr_crc = iso_crc32_gpt((unsigned char *) buf + 512 * t->gpt_part_start, 128 * t->gpt_max_entries, 0); ret = iso_write_gpt_header_block(t, img_blocks, buf + 512, t->gpt_max_entries, t->gpt_part_start, p_arr_crc); if (ret < 0) return ret; return ISO_SUCCESS; } /* Add a dummy MBR partition of type 0 with boot flag */ static void iso_dummy_mbr_partition(uint8_t *buf, int mode) { int i; /* bootable , start 0/0/1, type 0x00, end 0/0/1, start LBA 0, block count 1 */ static uint8_t dummy_entry[16] = { 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; for (i = 0; i < 4; i++) { if (mbr_part_slot_is_unused(buf + 446 + 16 * i)) { memcpy(buf + 446 + 16 * i, dummy_entry, 16); return; } } /* Abundance of 0xee and 0xef partitions. No other one free. */ for (i = 0; i < 4; i++) { if (buf[446 + 16 * i + 4] != 0xef) { buf[446 + 16 * i] |= 0x80; return; } } i = 3; buf[446 + 16 * i] |= 0x80; } /* flag bit0= only accept partition 1 as match for partition_offset */ static int iso_ensure_mbr_part_table(Ecma119Image *t, uint32_t img_blocks, uint8_t *buf, int flag) { int part_type, ret, i, found_part = 0; uint32_t start_lba, num_blocks; /* Look for MBR partition which starts at t->opts->partition_offset * 4 and has non-zero length */ if (buf[510] == 0x55 && buf[511] == 0xaa && t->opts->partition_offset < 0x3fffffff && img_blocks < 0x3fffffff ) { for (i = 0; i < 4; i++) { start_lba = iso_read_lsb(buf + 446 + i * 16 + 8, 4); num_blocks = iso_read_lsb(buf + 446 + i * 16 + 12, 4); if (t->opts->partition_offset * 4 == start_lba && num_blocks > 0) { found_part = i + 1; break; } if (flag & 1) break; } } if (found_part > 0) { /* Update size fields in found_part */ part_type = buf[446 + (found_part - 1) * 16 + 4]; if (t->opts->iso_mbr_part_type >= 0 && t->opts->iso_mbr_part_type <= 255) part_type= t->opts->iso_mbr_part_type; ret = write_mbr_partition_entry(found_part, part_type, start_lba, img_blocks * 4, t->partition_secs_per_head, t->partition_heads_per_cyl, buf, 2); } else { part_type = 0xcd; if (t->opts->iso_mbr_part_type >= 0 && t->opts->iso_mbr_part_type <= 255) part_type= t->opts->iso_mbr_part_type; ret = make_grub_msdos_label(img_blocks, t->partition_secs_per_head, t->partition_heads_per_cyl, (uint8_t) part_type, buf, 2); } if (ret != ISO_SUCCESS) /* error should never happen */ return ISO_ASSERT_FAILURE; return ISO_SUCCESS; } /* @param flag bit0= t->opts->ms_block is not counted in t->total_size */ int iso_write_system_area(Ecma119Image *t, uint8_t *buf, int flag) { int ret, int_img_blocks, sa_type, i, will_append = 0, do_isohybrid = 0; int first_partition = 1, last_partition = 4, apm_flag, part_type = 0; int gpt_count = 0, gpt_idx[128], apm_count = 0, no_boot_mbr = 0; int offset_flag = 0, risk_of_ee = 0; uint32_t img_blocks, gpt_blocks, mbrp1_blocks, pml_blocks; uint64_t blk; uint8_t *wpt; if ((t == NULL) || (buf == NULL)) { return ISO_NULL_POINTER; } /* set buf to 0s */ memset(buf, 0, 16 * BLOCK_SIZE); sa_type = (t->system_area_options >> 2) & 0x3f; iso_tell_max_part_range(t->opts, &first_partition, &last_partition, 0); for (i = first_partition - 1; i <= last_partition - 1; i++) if (t->opts->appended_partitions[i] != NULL) { will_append = 1; break; } #ifdef Libisofs_appended_partitions_inlinE img_blocks = t->vol_space_size; #else img_blocks = t->curblock; #endif if (t->system_area_data != NULL) { /* Write more or less opaque boot image */ memcpy(buf, t->system_area_data, 16 * BLOCK_SIZE); } else if (sa_type == 0 && t->catalog != NULL && (t->catalog->bootimages[0]->isolinux_options & 0x0a) == 0x02) { /* Check for isolinux image with magic number of 3.72 and produce an MBR from our built-in template. (Deprecated since 31 Mar 2010) */ if (t->bootsrc[0] == NULL) return iso_msg_submit(t->image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Cannot refer by isohybrid MBR to data outside of ISO 9660 filesystem."); if (img_blocks < 0x80000000) { int_img_blocks= img_blocks; } else { int_img_blocks= 0x7ffffff0; } ret = make_isohybrid_mbr(t->bootsrc[0]->sections[0].block, &int_img_blocks, (char*)buf, 0); if (ret != 1) { /* error, it should never happen */ return ISO_ASSERT_FAILURE; } return ISO_SUCCESS; } /* If APM entries were submitted to iso_register_apm_entry(), then they get sprinkled over the system area after the system area template was loaded and before any other data get inserted. Note that if APM and MBR get combined, then the first 8 bytes of the MBR template must be replaceable by: {0x45, 0x52, 0x08 0x00, 0xeb, 0x02, 0xff, 0xff} >>> ts B20526 >>> This does not care for eventual image enlargements in last minute. >>> A sa_type, that does this, will have to adjust the last APM entry >>> if exactness matters. */ apm_flag = 0; if (sa_type == 0 && ((t->system_area_options & 3) == 2 || t->opts->part_like_isohybrid)) { if (sa_type == 0 && (t->system_area_options & 3) == 2) do_isohybrid = 1; /* >>> Coordinate with partprepend writer */ /* <<< provisory trap */ if (t->mbr_req_count > 0) return ISO_BOOT_MBR_OVERLAP; /* If own APM is desired, set flag bit0 to prevent writing of Block0 which would interfere with the own Block0 of isohybrid. */ ret = assess_isohybrid_gpt_apm(t, &gpt_count, gpt_idx, &apm_count, 0); if (ret < 0) return ret; if (apm_count > 0) apm_flag |= 1; } ret = iso_write_apm(t, img_blocks, buf, apm_flag); if (ret < 0) { iso_msg_submit(t->image->id, ret, 0, "Cannot set up Apple Partition Map"); return ret; } ret = iso_write_mbr(t, img_blocks, buf); if (ret < 0) { iso_msg_submit(t->image->id, ret, 0, "Cannot set up MBR partition table"); return ret; } if (t->mbr_req_count > 0) { if (sa_type != 0) return ISO_NON_MBR_SYS_AREA; risk_of_ee = 1; } if (t->gpt_backup_outside) gpt_blocks = t->total_size / BLOCK_SIZE + (flag & 1) * t->opts->ms_block; else gpt_blocks = img_blocks; ret = iso_write_gpt(t, gpt_blocks, buf); if (ret < 0) { iso_msg_submit(t->image->id, ret, 0, "Cannot set up GPT"); return ret; } if (sa_type == 0 && (t->system_area_options & 1)) { if (t->mbr_req_count == 0){ /* Write GRUB protective msdos label, i.e. a simple partition table */ if (t->gpt_req_count > 0 && ! t->opts->part_like_isohybrid) { part_type = 0xee; pml_blocks = gpt_blocks; } else { part_type = 0xcd; if (t->opts->iso_mbr_part_type >= 0 && t->opts->iso_mbr_part_type <= 255) part_type= t->opts->iso_mbr_part_type; pml_blocks = img_blocks; } ret = make_grub_msdos_label(pml_blocks, t->partition_secs_per_head, t->partition_heads_per_cyl, (uint8_t) part_type, buf, 0); if (ret != ISO_SUCCESS) /* error should never happen */ return ISO_ASSERT_FAILURE; risk_of_ee = 1; } else if (t->gpt_req_count > 0) { /* >>> ??? change first partition type to 0xee */; } } else if (do_isohybrid || t->opts->part_like_isohybrid) { /* Patch externally provided system area as isohybrid MBR or at least write an MBR partition table as of isohybrid */ if ((t->catalog == NULL || t->system_area_data == NULL) && do_isohybrid) { /* isohybrid makes only sense together with ISOLINUX boot image and externally provided System Area. */ return ISO_ISOLINUX_CANT_PATCH; } if (gpt_count > 0 || apm_count > 0) { /* Decision can be revoked in make_isolinux_mbr if !do_isohybrid */ part_type = 0x00; } else { part_type = 0x17; } /* By tradition, real isohybrid insists in 0x00 if GPT or APM */ if (part_type != 0x00 || !do_isohybrid) if (t->opts->iso_mbr_part_type >= 0 && t->opts->iso_mbr_part_type <= 255) part_type= t->opts->iso_mbr_part_type; if (t->opts->appended_as_gpt && t->have_appended_partitions) { part_type = 0xee; risk_of_ee = 1; img_blocks = gpt_blocks; no_boot_mbr = 2; } /* ??? Why was partition_offset 0 here ? It gets adjusted later by iso_offset_partition_start() Does it harm to give the real offset here ? Now this is really needed for checking whether partitions are inside the ISO 9660 partition if !do_isohybrid */ ret = make_isolinux_mbr(&img_blocks, t, t->opts->partition_offset * 4, 1, part_type, buf, 1 | no_boot_mbr | ((!do_isohybrid) << 2) | ((!do_isohybrid) << 3)); if (ret != 1) return ret; } else if (sa_type == 1) { ret = make_mips_volume_header(t, buf, 0); if (ret != ISO_SUCCESS) return ret; } else if (sa_type == 2) { ret = make_mipsel_boot_block(t, buf, 0); if (ret != ISO_SUCCESS) return ret; } else if (sa_type == 3) { ret = make_sun_disk_label(t, buf, 0); if (ret != ISO_SUCCESS) return ret; } else if (sa_type == 4 || sa_type == 5) { /* (By coincidence, sa_type and PALO header versions match) */ ret = make_hppa_palo_sector(t, buf, sa_type, 0); if (ret != ISO_SUCCESS) return ret; } else if (sa_type == 6) { ret = make_dec_alpha_sector(t, buf, 0); if (ret != ISO_SUCCESS) return ret; } else if ((t->opts->partition_offset > 0 || will_append) && sa_type == 0 && t->mbr_req_count == 0) { ret= iso_ensure_mbr_part_table(t, img_blocks, buf, ((t->opts->appended_as_gpt && t->have_appended_partitions) || t->opts->partition_offset == 0)); if (ret != ISO_SUCCESS) /* error should never happen */ return ISO_ASSERT_FAILURE; risk_of_ee = 1; if (t->opts->appended_as_gpt && t->have_appended_partitions) { /* >>> ??? Do this in any case of t->gpt_req_count > ? */; /* Re-write partition entry 1 : protective MBR for GPT */ part_type = 0xee; risk_of_ee = 1; ret = write_mbr_partition_entry(1, part_type, (uint64_t) 1, ((uint64_t) gpt_blocks) * 4 - 1, t->partition_secs_per_head, t->partition_heads_per_cyl, buf, 2); if (ret < 0) return ret; } else if (t->opts->partition_offset == 0) { /* Re-write partition entry 1 : start at 0, type Linux */ blk = ((uint64_t) img_blocks) * 4 - t->post_iso_part_pad / 512; part_type = 0x83; if (t->opts->iso_mbr_part_type >= 0 && t->opts->iso_mbr_part_type <= 255) part_type= t->opts->iso_mbr_part_type; ret = write_mbr_partition_entry(1, part_type, (uint64_t) 0, blk, t->partition_secs_per_head, t->partition_heads_per_cyl, buf, 2); if (ret < 0) return ret; } } /* Check for protective MBR in mbr_req and adjust to GPT size */ if (t->gpt_req_count > 0 && sa_type == 0 && t->mbr_req_count == 1) { if (t->mbr_req[0]->type_byte == 0xee && buf[450] == 0xee && t->mbr_req[0]->desired_slot <= 1) { part_type = 0xee; risk_of_ee = 1; ret = write_mbr_partition_entry(1, part_type, (uint64_t) 1, ((uint64_t) gpt_blocks) * 4 - 1, t->partition_secs_per_head, t->partition_heads_per_cyl, buf, 2); if (ret < 0) return ret; } } if (t->opts->partition_offset > 0 && sa_type == 0 && t->mbr_req_count == 0) { /* Adjust partition table to partition offset. With t->mbr_req_count > 0 this has already been done, */ #ifndef Libisofs_appended_partitions_inlinE img_blocks = t->curblock; /* value might have been altered */ #else /* A change of t->curblock does not matter in this case */ #endif if (part_type == 0xee && t->gpt_req_count > 0) { mbrp1_blocks = t->total_size / BLOCK_SIZE + (flag & 1) * t->opts->ms_block; offset_flag |= 2 | 1; /* protective MBR, no other partitions */ } else { mbrp1_blocks = img_blocks; } ret = iso_offset_partition_start(mbrp1_blocks, t->post_iso_part_pad, t->opts->partition_offset, t->partition_secs_per_head, t->partition_heads_per_cyl, buf, offset_flag); if (ret != ISO_SUCCESS) /* error should never happen */ return ISO_ASSERT_FAILURE; } /* This possibly overwrites the non-mbr_req partition table entries made so far. Overwriting those from t->mbr_req is not allowed. */ if (sa_type == 3 || !(t->opts->appended_as_gpt || t->opts->appended_as_apm)) { for (i = first_partition - 1; i <= last_partition - 1; i++) { if (t->opts->appended_partitions[i] == NULL) continue; if (i < t->mbr_req_count) return ISO_BOOT_MBR_COLLISION; if (sa_type == 3) { ret = write_sun_partition_entry(i + 1, t->opts->appended_partitions, t->appended_part_start, t->appended_part_size, ISO_SUN_CYL_SIZE, buf, t->opts->appended_partitions[i][0] == 0); } else { ret = write_mbr_partition_entry(i + 1, t->opts->appended_part_types[i], (uint64_t) t->appended_part_start[i], (uint64_t) t->appended_part_size[i], t->partition_secs_per_head, t->partition_heads_per_cyl, buf, 0); } if (ret < 0) return ret; } } if (sa_type == 0 && (t->system_area_options & 0x4000) && !do_isohybrid) { /* Patch MBR for GRUB2 */ if (t->num_bootsrc <= 0) return iso_msg_submit(t->image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "No boot image found as jump target for GRUB2 MBR."); if (t->bootsrc[0] == NULL) return iso_msg_submit(t->image->id, ISO_BOOT_IMAGE_NOT_VALID, 0, "Cannot refer by GRUB2 MBR to data outside of ISO 9660 filesystem."); blk = t->bootsrc[0]->sections[0].block * 4 + Libisofs_grub2_mbr_patch_offsT; wpt = buf + Libisofs_grub2_mbr_patch_poS; for (i = 0; i < 8; i++) wpt[i] = blk >> (i * 8); } /* Prevent partition type 0xee if no GPT emerged */ /* >>> ??? check for GPT magic number at byte 512 ff. ? */; if (sa_type == 0 && ((t->system_area_options & 3) || risk_of_ee) && (t->opts->part_like_isohybrid || t->gpt_req_count == 0) && t->opts->iso_mbr_part_type != 0xee) { for (i = 0; i < 4; i++) { if (buf[446 + 16 * i + 4] == 0xee) { iso_msgs_submit(0, "Prevented partition type 0xEE in MBR without GPT", 0, "WARNING", 0); part_type = 0xcd; if (t->opts->iso_mbr_part_type >= 0 && t->opts->iso_mbr_part_type <= 255) part_type= t->opts->iso_mbr_part_type; buf[446 + 16 * i + 4] = (uint8_t) part_type; } } } if (sa_type == 0 && ( (t->system_area_options & 3) || (t->system_area_options & (1 << 14)) || (((t->system_area_options >> 10) & 15) != 1 && (t->system_area_options & (1 << 15))) )) { /* This is an MBR which shall have a bootable/active flag protective-msdos-label, isohybrid, grub2-mbr, mbr-force-bootable */ for (i = 0; i < 4; i++) if (buf[446 + 16 * i] & 0x80) break; if (i >= 4) { /* no bootable/active flag set yet */ for (i = 0; i < 4; i++) { if ((!mbr_part_slot_is_unused(buf + 446 + 16 * i)) && buf[446 + 16 * i + 4] != 0xee && buf[446 + 16 * i + 4] != 0xef) { buf[446 + 16 * i] |= 0x80; break; } } if (i >= 4) { /* still no bootable/active flag set */ if (t->system_area_options & (1 << 15)) /* Force it */ iso_dummy_mbr_partition(buf, 0); } } } if ((((t->system_area_options >> 2) & 0x3f) == 0 && (t->system_area_options & 3) == 1) || t->opts->partition_offset > 0) { /* Protective MBR || partition offset ISO will not be a partition or add-on session. It can span the whole image. */ t->pvd_size_is_total_size = 1; } return ISO_SUCCESS; } /* Choose *heads_per_cyl so that - *heads_per_cyl * secs_per_head * 1024 >= imgsize / 512 - *heads_per_cyl * secs_per_head is divisible by 4 - it is as small as possible (to reduce alignment overhead) - it is <= 255 @return 1= success , 0= cannot achieve goals */ static int try_sph(off_t imgsize, int secs_per_head, int *heads_per_cyl, int flag) { off_t hd_blocks, hpc; hd_blocks= imgsize / 512; hpc = hd_blocks / secs_per_head / 1024; if (hpc * secs_per_head * 1024 < hd_blocks) hpc++; if ((secs_per_head % 4) == 0) { ; } else if ((secs_per_head % 2) == 0) { hpc += (hpc % 2); } else if(hpc % 4) { hpc += 4 - (hpc % 4); } if (hpc > 255) return 0; *heads_per_cyl = hpc; return 1; } int iso_align_isohybrid(Ecma119Image *t, int flag) { int sa_type, ret, always_align; uint32_t img_blocks; off_t imgsize, cylsize = 0, frac; char *msg = NULL; #ifdef Libisofs_part_align_writeR int fap, lap, app_part_count; #endif LIBISO_ALLOC_MEM(msg, char, 160); sa_type = (t->system_area_options >> 2) & 0x3f; if (sa_type != 0) {ret = ISO_SUCCESS; goto ex;} always_align = (t->system_area_options >> 8) & 3; if (!t->gpt_backup_outside) { /* Take into account the backup GPT */; ret = precompute_gpt(t); if (ret < 0) goto ex; } #ifdef Libisofs_part_align_writeR /* If partitions get appended then t->opts->tail_blocks and t->gpt_backup_size come after the alignment padding. */ app_part_count = iso_count_appended_partitions(t, &fap, &lap); img_blocks = t->curblock; if (app_part_count == 0) img_blocks += t->opts->tail_blocks + t->gpt_backup_size; #else img_blocks = t->curblock + t->opts->tail_blocks + t->gpt_backup_size; #endif imgsize = ((off_t) img_blocks) * (off_t) 2048; if ((!(t->opts->appended_as_gpt && t->have_appended_partitions)) && ((t->system_area_options & 3) || always_align) && (off_t) (t->partition_heads_per_cyl * t->partition_secs_per_head * 1024) * (off_t) 512 < imgsize) { /* Choose small values which can represent the image size */ /* First try 32 sectors per head */ ret = try_sph(imgsize, 32, &(t->partition_heads_per_cyl), 0); if (ret == 1) { t->partition_secs_per_head = 32; } else { /* Did not work with 32. Try 63 */ t->partition_secs_per_head = 63; ret = try_sph(imgsize, 63, &(t->partition_heads_per_cyl), 0); if (ret != 1) t->partition_heads_per_cyl = 255; } cylsize = t->partition_heads_per_cyl * t->partition_secs_per_head *512; frac = imgsize % cylsize; sprintf(msg, "Automatically adjusted MBR geometry to %d/%d/%d", (int) (imgsize / cylsize + !!frac), t->partition_heads_per_cyl, t->partition_secs_per_head); iso_msgs_submit(0, msg, 0, "NOTE", 0); } if (always_align == 2) {ret = ISO_SUCCESS; goto ex;} cylsize = 0; if (t->catalog != NULL && (t->catalog->bootimages[0]->isolinux_options & 0x0a) == 0x02) { /* Check for isolinux image with magic number of 3.72 and produce an MBR from our built-in template. (Deprecated since 31 Mar 2010) */ if (img_blocks >= 0x40000000) {ret = ISO_SUCCESS; goto ex;} cylsize = 64 * 32 * 512; } else if (t->system_area_options & 2) { /* Patch externally provided system area as isohybrid MBR */ if (t->catalog == NULL || t->system_area_data == NULL) { /* isohybrid makes only sense together with ISOLINUX boot image and externally provided System Area. */ {ret = ISO_ISOLINUX_CANT_PATCH; goto ex;} } cylsize = t->partition_heads_per_cyl * t->partition_secs_per_head * 512; } else if (always_align) { cylsize = t->partition_heads_per_cyl * t->partition_secs_per_head * 512; } if (cylsize == 0) {ret = ISO_SUCCESS; goto ex;} if (((double) imgsize) / (double) cylsize > 1024.0) { iso_msgs_submit(0, "Image size exceeds 1024 cylinders. Cannot align partition.", 0, "WARNING", 0); iso_msgs_submit(0, "There are said to be BIOSes which will not boot this via MBR.", 0, "WARNING", 0); {ret = ISO_SUCCESS; goto ex;} } frac = imgsize % cylsize; imgsize += (frac > 0 ? cylsize - frac : 0); frac = imgsize - ((off_t) img_blocks) * (off_t) 2048; if (frac == 0) {ret = ISO_SUCCESS; goto ex;} t->post_iso_part_pad = 0; if (frac % 2048) { t->post_iso_part_pad = 2048 - frac % 2048; sprintf(msg, "Cylinder aligned image size is not divisible by 2048. Have to add %d bytes.", t->post_iso_part_pad); iso_msgs_submit(0, msg, 0, "WARNING", 0); } #ifdef Libisofs_part_align_writeR t->part_align_blocks = (frac + 2047) / 2048; #else t->opts->tail_blocks += (frac + 2047) / 2048; #endif /* ! Libisofs_part_align_writeR */ ret = ISO_SUCCESS; ex:; LIBISO_FREE_MEM(msg); return ret; } int iso_register_apm_entry(struct iso_apm_partition_request **req_array, int *apm_req_count, struct iso_apm_partition_request *req, int flag) { struct iso_apm_partition_request *entry; if (*apm_req_count >= ISO_APM_ENTRIES_MAX) return ISO_BOOT_TOO_MANY_APM; entry = calloc(1, sizeof(struct iso_apm_partition_request)); if (entry == NULL) return ISO_OUT_OF_MEM; memcpy(entry, req, sizeof(struct iso_apm_partition_request)); if (req->image_path != NULL) { entry->image_path = strdup(req->image_path); if (entry->image_path == NULL) { LIBISO_FREE_MEM(entry); return ISO_OUT_OF_MEM; } } req_array[*apm_req_count] = entry; (*apm_req_count)++; return ISO_SUCCESS; } int iso_register_mbr_entry(struct iso_mbr_partition_request **req_array, int *mbr_req_count, struct iso_mbr_partition_request *req, int flag) { struct iso_mbr_partition_request *entry; if (*mbr_req_count >= ISO_MBR_ENTRIES_MAX) return ISO_BOOT_TOO_MANY_MBR; entry = calloc(1, sizeof(struct iso_mbr_partition_request)); if (entry == NULL) return ISO_OUT_OF_MEM; memcpy(entry, req, sizeof(struct iso_mbr_partition_request)); if (req->image_path != NULL) { entry->image_path = strdup(req->image_path); if (entry->image_path == NULL) { LIBISO_FREE_MEM(entry); return ISO_OUT_OF_MEM; } } req_array[*mbr_req_count] = entry; (*mbr_req_count)++; return ISO_SUCCESS; } int iso_register_gpt_entry(struct iso_gpt_partition_request **req_array, int *gpt_req_count, struct iso_gpt_partition_request *req, int flag) { struct iso_gpt_partition_request *entry; if (*gpt_req_count >= ISO_GPT_ENTRIES_MAX) return ISO_BOOT_TOO_MANY_GPT; entry = calloc(1, sizeof(struct iso_gpt_partition_request)); if (entry == NULL) return ISO_OUT_OF_MEM; memcpy(entry, req, sizeof(struct iso_gpt_partition_request)); if (req->image_path != NULL) { entry->image_path = strdup(req->image_path); if (entry->image_path == NULL) { LIBISO_FREE_MEM(entry); return ISO_OUT_OF_MEM; } } req_array[*gpt_req_count] = entry; (*gpt_req_count)++; return ISO_SUCCESS; } #ifdef Libisofs_with_uuid_generatE static void swap_uuid(void *u_pt) { uint8_t tr, *u; u = (uint8_t *) u_pt; tr = u[0]; u[0] = u[3]; u[3] = tr; tr = u[1]; u[1] = u[2]; u[2] = tr; tr = u[4]; u[4] = u[5]; u[5] = tr; tr = u[6]; u[6] = u[7]; u[7] = tr; } #endif /* Libisofs_with_uuid_generatE */ /* CRC-32 as of GPT and Ethernet. Parameters are deduced from a table driven implementation in isohybrid.c */ uint32_t iso_crc32_gpt(unsigned char *data, int count, int flag) { unsigned int acc, top, result = 0; long int i; /* Chosen so that the CRC of 0 bytes of input is 0x00000000 */ acc = 0x46af6449; /* Process data bits and flush numerator by 32 zero bits */ for (i = 0; i < count * 8 + 32; i++) { top = acc & 0x80000000; acc = (acc << 1); if (i < count * 8) /* The least significant bits of input bytes get processed first */ acc |= ((data[i / 8] >> (i % 8)) & 1); if (top) /* Division by the generating polynomial */ acc ^= 0x04c11db7; } /* Mirror residue bits */ for (i = 0; i < 32; i++) if (acc & (1 << i)) result |= 1 << (31 - i); /* Return bit complement */ return result ^ 0xffffffff; } void iso_mark_guid_version_4(uint8_t *u) { /* Mark as UUID version 4. RFC 4122 says u[6], but UEFI prescribes bytes 6 and 7 to be swapped. */ u[7] = (u[7] & 0x0f) | 0x40; /* Variant is "1 0 x" as described in RFC 4122. */ u[8] = (u[8] & 0x3f) | 0x80; return; } void iso_generate_gpt_guid(uint8_t guid[16]) { #ifdef Libisofs_with_uuid_generatE uuid_t u; uuid_generate(u); swap_uuid((void *) u); memcpy(guid, u, 16); #else uint8_t *u; /* produced by uuid_generate() and byte-swapped to UEFI specs */ static uint8_t uuid_template[16] = { 0xee, 0x29, 0x9d, 0xfc, 0x65, 0xcc, 0x7c, 0x40, 0x92, 0x61, 0x5b, 0xcd, 0x6f, 0xed, 0x08, 0x34 }; uint32_t rnd, salt; struct timeval tv; pid_t pid; int i, ret, fd; u = guid; /* First try /dev/urandom */ fd = open("/dev/urandom", O_RDONLY | O_BINARY); if (fd == -1) goto fallback; ret = read(fd, u, 16); if (ret != 16) { close(fd); goto fallback; } close(fd); iso_mark_guid_version_4(u); return; fallback:; pid = getpid(); salt = iso_crc32_gpt((unsigned char *) &guid, sizeof(uint8_t *), 0) ^ pid; /* This relies on the uniqueness of the template and the rareness of bootable ISO image production via libisofs. Estimated 48 bits of entropy should influence the production of a single day. So first collisions are to be expected with about 16 million images per day. */ memcpy(u, uuid_template, 16); gettimeofday(&tv, NULL); for (i = 0; i < 4; i++) u[i] = (salt >> (8 * i)) & 0xff; for (i = 0; i < 2; i++) u[4 + i] = (pid >> (8 * i)) & 0xff; u[6] = ((salt >> 8) ^ (pid >> 16)) & 0xff; rnd = ((0xffffff & tv.tv_sec) << 8) | (((tv.tv_usec >> 16) ^ (salt & 0xf0)) & 0xff); for (i = 0; i < 4; i++) u[10 + i] ^= (rnd >> (8 * i)) & 0xff; u[14] ^= (tv.tv_usec >> 8) & 0xff; u[15] ^= tv.tv_usec & 0xff; iso_mark_guid_version_4(u); return; #endif /* ! Libisofs_with_uuid_generatE */ } void iso_gpt_uuid(Ecma119Image *t, uint8_t uuid[16]) { if (t->gpt_uuid_counter == 0) iso_generate_gpt_guid(t->gpt_uuid_base); memcpy(uuid, t->gpt_uuid_base, 16); /* Previous implementation changed only byte 9. So i expand it by applying the counter in little-endian style. */ uuid[9] ^= t->gpt_uuid_counter & 0xff; uuid[10] ^= (t->gpt_uuid_counter >> 8) & 0xff; uuid[11] ^= (t->gpt_uuid_counter >> 16) & 0xff; uuid[12] ^= (t->gpt_uuid_counter >> 24) & 0xff; t->gpt_uuid_counter++; return; } int assess_appended_gpt(Ecma119Image *t, int flag) { static uint8_t basic_data_uuid[16] = { 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 }; static uint8_t efi_sys_uuid[16] = { 0x28, 0x73, 0x2a, 0xc1, 0x1f, 0xf8, 0xd2, 0x11, 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b }; static uint8_t hfs_plus_uuid[16] = { 0x00, 0x53, 0x46, 0x48, 0x00, 0x00, 0xaa, 0x11, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac }; static uint8_t zero_uuid[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; int i, ret, do_apm = 0, do_gpt = 0, index, already_in_gpt = 0; int first_partition, last_partition; uint8_t gpt_name[72], *type_uuid; char apm_type[33]; #ifndef Libisofs_appended_partitions_inlinE if (!t->gpt_backup_outside) return 2; #endif if ((t->apm_req_count > 0 && t->opts->part_like_isohybrid) || (t->have_appended_partitions && t->opts->appended_as_apm)) do_apm = 1; if (t->gpt_req_count > 0 || (t->have_appended_partitions && t->opts->appended_as_gpt)) do_gpt = 1; if (do_apm == 0 && do_gpt == 0) return 2; /* Represent appended partitions */ iso_tell_max_part_range(t->opts, &first_partition, &last_partition, 0); for (i = first_partition - 1; i < last_partition; i++) { if (t->opts->appended_partitions[i] == NULL) continue; if (do_apm) { memset(gpt_name, 0, 32); sprintf((char *) gpt_name, "Appended%d", i + 1); strcpy(apm_type, "Data"); if (t->opts->appended_part_gpt_flags[i] & 1) { if (memcmp(t->opts->appended_part_type_guids[i], hfs_plus_uuid, 16) == 0) strcpy(apm_type, "Apple_HFS"); } ret = iso_quick_apm_entry(t->apm_req, &(t->apm_req_count), t->appended_part_start[i] * t->hfsp_iso_block_fac, t->appended_part_size[i] * t->hfsp_iso_block_fac, (char *) gpt_name, apm_type); if (ret < 0) return ret; } if (do_gpt) already_in_gpt = iso_find_gpt_entry(t->gpt_req, t->gpt_req_count, ((uint64_t) t->appended_part_start[i]) * 4, ((uint64_t) t->appended_part_size[i]) * 4, &index, 0); if (do_gpt && !already_in_gpt) { memset(gpt_name, 0, 72); sprintf((char *) gpt_name, "Appended%d", i + 1); iso_ascii_utf_16le(gpt_name); if (t->opts->appended_part_gpt_flags[i] & 1) type_uuid = t->opts->appended_part_type_guids[i]; else if (t->opts->appended_part_types[i] == 0xef) type_uuid = efi_sys_uuid; else type_uuid = basic_data_uuid; ret = iso_quick_gpt_entry(t->gpt_req, &(t->gpt_req_count), ((uint64_t) t->appended_part_start[i]) * 4, ((uint64_t) t->appended_part_size[i]) * 4, type_uuid, zero_uuid, (uint64_t) 0, gpt_name); if (ret < 0) return ret; t->gpt_req[t->gpt_req_count - 1]->desired_slot = i + 1; } else if(do_gpt) { t->gpt_req[index]->desired_slot = i + 1; } } return ISO_SUCCESS; } /* Probably already called by tail_writer_compute_data_blocks via iso_align_isohybrid */ static int precompute_gpt(Ecma119Image *t) { uint32_t gpt_part_start; int ret, sa_type; int gpt_count, gpt_idx[128], apm_count; /* Avoid repetition by gpt_tail_writer_compute_data_blocks */ t->gpt_is_computed = 1; /* Assess APM and GPT requests of isohybrid */ sa_type = (t->system_area_options >> 2) & 0x3f; if (sa_type == 0 && (t->system_area_options & 3) == 2) { /* >>> ISOHYBRID : Shall isohybrid be combinable with other APM and GPT requesters ? */; /* <<< provisorily: Not compatible */ ret = assess_isohybrid_gpt_apm(t, &gpt_count, gpt_idx, &apm_count, 0); if (ret < 0) return ret; if (t->gpt_req_count > 0 && gpt_count > 0) return ISO_BOOT_GPT_OVERLAP; if (t->apm_req_count > 0 && apm_count > 0) return ISO_BOOT_APM_OVERLAP; /* Register the GPT and APM partition entries */ ret = assess_isohybrid_gpt_apm(t, &gpt_count, gpt_idx, &apm_count, 1); if (ret < 0) return ret; } /* With part_like_isohybrid: If no GPT is registered yet, and MBR, but neither CHRP nor ISOLINUX isohybrid is desired, then try to apply the isohybrid GPT and APM flags nevertheless. Avoid an overall ISO image GPT partition. */ if (t->opts->part_like_isohybrid && t->gpt_req_count <= 0 && ((t->system_area_options >> 2) & 0x3f) == 0 && ((t->system_area_options >> 10) & 0xf) != 1 && (!(t->system_area_options & 2))) { ret = assess_isohybrid_gpt_apm(t, &gpt_count, gpt_idx, &apm_count, 1 | ((t->apm_req_count > 0) << 1) | 4); if (ret <= 0) return ret; t->apm_req_flags |= 2; /* Do not fill APM gaps, do not adjust final APM partition size */ } /* Assess impact of appended partitions on GPT */ ret = assess_appended_gpt(t, 0); if (ret < 0) return ret; /* Rectify APM requests early in order to learn the size of GPT. iso_write_apm() relies on this being already done here. So perform even if no GPT is required. */ ret = rectify_apm(t); if (ret < 0) return ret; #ifdef NIX /* Disabled */ /* <<< ts B20526 : Dummy mock-up */ if (t->gpt_req_count <= 0) { /* <<< ??? Move to system_area.h and publish as macro ? Or to make_isohybrid_mbr.c ? */ static uint8_t hfs_uuid[16] = { 0x00, 0x53, 0x46, 0x48, 0x00, 0x00, 0xaa, 0x11, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac }; static uint8_t basic_data_uuid[16] = { 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 }; uint8_t gpt_name[72]; static uint8_t zero_uuid[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uint64_t gpt_flags = (((uint64_t) 1) << 60) | 1; memset(gpt_name, 0, 72); gpt_name[0] = 'T'; gpt_name[2] = '1'; strcpy((char *) gpt_name, "GPT Test 1"); iso_ascii_utf_16le(gpt_name); /* ret = iso_quick_gpt_entry(t->gpt_req, &(t->gpt_req_count), (uint64_t) (16 * 4), (uint64_t) (20 * 4), hfs_uuid, zero_uuid, gpt_flags, gpt_name); / * Caution: Size 90 causes intentional partition overlap error * / ret = iso_quick_gpt_entry(t->gpt_req, &(t->gpt_req_count), (uint64_t) (30 * 4), (uint64_t) (90 * 4), hfs_uuid, zero_uuid, gpt_flags, gpt_name); */ ret = iso_quick_gpt_entry(t->gpt_req, &(t->gpt_req_count), (uint64_t) (30 * 4), (uint64_t) (40 * 4), hfs_uuid, zero_uuid, gpt_flags, gpt_name); if (ret < 0) return ret; strcpy((char *) gpt_name, "GPT Test 2"); iso_ascii_utf_16le(gpt_name); ret = iso_quick_gpt_entry(t->gpt_req, &(t->gpt_req_count), (uint64_t) (110 * 4), (uint64_t) (60 * 4), basic_data_uuid, zero_uuid, gpt_flags, gpt_name); if (ret < 0) return ret; } #endif /* NIX */ /* Is a GPT requested ? */ t->gpt_backup_end = 0; t->gpt_max_entries = 0; if (t->gpt_req_count == 0) return ISO_SUCCESS; /* Determine GPT partition start in System Area, */ gpt_part_start = 0; if (t->apm_req_count > 0) { if (t->opts->apm_block_size == 0) t->opts->apm_block_size = 2048; gpt_part_start = (t->apm_req_count + 1) * (t->opts->apm_block_size / 512); } if (gpt_part_start < 2) gpt_part_start = 2; else if (gpt_part_start >= 64) return ISO_BOOT_TOO_MANY_GPT; t->gpt_part_start = gpt_part_start; /* Necessary number of 2K blocks */ t->gpt_max_entries = (64 - t->gpt_part_start) * 4; t->gpt_backup_size = ((t->gpt_max_entries / 4 + 1) * 512 + 2047) / 2048; return ISO_SUCCESS; } int gpt_tail_writer_compute_data_blocks(IsoImageWriter *writer) { Ecma119Image *t; int ret; if (writer == NULL) return ISO_ASSERT_FAILURE; t = writer->target; if (! t->gpt_is_computed) { ret = precompute_gpt(t); if (ret < 0) return ret; } if (t->gpt_backup_outside) { t->total_size += t->gpt_backup_size * 2048; /* The ISO block number after the backup GPT header */ t->gpt_backup_end = t->total_size / BLOCK_SIZE + t->opts->ms_block; } else { t->curblock += t->gpt_backup_size; /* The ISO block number after the backup GPT header */ t->gpt_backup_end = t->curblock; } return ISO_SUCCESS; } int gpt_tail_writer_write_vol_desc(IsoImageWriter *writer) { return ISO_SUCCESS; } static int gpt_tail_writer_write_data(IsoImageWriter *writer) { Ecma119Image *t; uint8_t *head, *new_head, *entries; uint8_t *backup_buf = NULL; uint32_t crc, i; uint64_t part_start; int ret; t = writer->target; if (t->gpt_backup_end == 0 || t->gpt_max_entries == 0) return ISO_SUCCESS; /* No backup GPT area reserved by compute_data() */ backup_buf = calloc(1, t->gpt_backup_size * 2048); if (backup_buf == NULL) return ISO_OUT_OF_MEM; memset(backup_buf, 0, t->gpt_backup_size * 2048); /* Check whether GPT header block came through */ head = t->sys_area_as_written + 512; if (strncmp((char *) head, "EFI PART", 8) != 0) { tampered_head:; /* Send error message but do not prevent further image production */ iso_msgs_submit(0, "GPT header block was altered before writing to System Area.", 0, "FAILURE", 0); goto write_zeros; } for (i = 92; i < 512; i++) if (head[i]) goto tampered_head; /* Patch memorized header block */ new_head = backup_buf + t->gpt_backup_size * 2048 - 512; memcpy(new_head, head, 512); /* Exchange "Location of this header" and "Location of header backup" */ memcpy(new_head + 24, head + 32, 8); memcpy(new_head + 32, head + 24, 8); /* Point to the backup partition entries */ part_start = ((uint64_t) t->gpt_backup_end) * 4 - 1 - t->gpt_max_entries / 4; iso_lsb(new_head + 72, part_start & 0xffffffff, 4); iso_lsb(new_head + 76, (part_start >> 32) & 0xffffffff, 4); /* Compute new header CRC */ memset(new_head + 16, 0, 4); crc = iso_crc32_gpt((unsigned char *) new_head, 92, 0); iso_lsb(new_head + 16, crc, 4); /* Copy GPT entries */ entries = t->sys_area_as_written + t->gpt_part_start * 512; memcpy(new_head - t->gpt_max_entries * 128, entries, t->gpt_max_entries * 128); ret = iso_write(t, backup_buf, t->gpt_backup_size * 2048); free(backup_buf); if (ret < 0) return ret; if (!t->gpt_backup_outside) { /* >>> Why isn't t->curblock updated ? */; } return ISO_SUCCESS; write_zeros:; ret = iso_write(t, backup_buf, t->gpt_backup_size * 2048); free(backup_buf); if (ret < 0) return ret; return ISO_SUCCESS; } static int gpt_tail_writer_free_data(IsoImageWriter *writer) { return ISO_SUCCESS; } int gpt_tail_writer_create(Ecma119Image *target) { IsoImageWriter *writer; writer = calloc(1, sizeof(IsoImageWriter)); if (writer == NULL) { return ISO_OUT_OF_MEM; } writer->compute_data_blocks = gpt_tail_writer_compute_data_blocks; writer->write_vol_desc = gpt_tail_writer_write_vol_desc; writer->write_data = gpt_tail_writer_write_data; writer->free_data = gpt_tail_writer_free_data; writer->data = NULL; writer->target = target; /* add this writer to image */ target->writers[target->nwriters++] = writer; return ISO_SUCCESS; } /* ---------------------- Partition Prepend Writer --------------------- */ static int partprepend_writer_compute_data_blocks(IsoImageWriter *writer) { Ecma119Image *t; IsoFileSrc *src; int ret, will_have_gpt = 0, with_chrp = 0, i, part_type, keep; static uint8_t zero_uuid[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uint64_t gpt_flags = (((uint64_t) 1) << 60) | 1; uint8_t gpt_name[72]; uint64_t part_start; off_t start_byte, byte_count; /* <<< ??? Move to system_area.h and publish as macro ? */ static uint8_t efi_sys_uuid[16] = { 0x28, 0x73, 0x2a, 0xc1, 0x1f, 0xf8, 0xd2, 0x11, 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b }; if (writer == NULL) return ISO_ASSERT_FAILURE; t = writer->target; with_chrp = ((t->system_area_options & 0x3cff) == 0x0400); if (t->opts->efi_boot_partition != NULL || t->gpt_req_count > 0) /* Might not catch all cases with GPT */ will_have_gpt = 1; if (t->opts->efi_boot_partition != NULL) { keep = 0; if (t->efi_boot_part_filesrc != NULL) { /* A file in the emerging ISO image shall store its content as prepended partition. Install absolute block addresses and determine partition size. */ src = t->efi_boot_part_filesrc; t->efi_boot_part_size = 0; for (i = 0; i < src->nsections; i++) { src->sections[i].block = t->curblock + t->efi_boot_part_size; t->efi_boot_part_size += (src->sections[i].size + 2047) / 2048; } part_start = t->curblock * 4; } else { ret = compute_partition_size(t, t->opts->efi_boot_partition, &(t->efi_boot_part_size), t->opts->efi_boot_part_flag & 1); if (ret < 0) return ret; part_start = t->curblock * 4; if (ret == ISO_SUCCESS + 1) { /* Interval from imported_iso in add-on session will be kept */ ret = iso_interval_reader_start_size(t, t->opts->efi_boot_partition, &start_byte, &byte_count, 0); if (ret < 0) return ret; part_start = start_byte / 512; keep = 1; } } memset(gpt_name, 0, 72); strcpy((char *) gpt_name, "EFI boot partition"); iso_ascii_utf_16le(gpt_name); ret = iso_quick_gpt_entry(t->gpt_req, &(t->gpt_req_count), part_start, ((uint64_t) t->efi_boot_part_size) * 4, efi_sys_uuid, zero_uuid, gpt_flags, gpt_name); if (ret < 0) return ret; if (!keep) t->curblock += t->efi_boot_part_size; } if (with_chrp) { /* CHRP is not compatible with any other partition in MBR */ if (t->opts->prep_partition != NULL || t->opts->fat || will_have_gpt || t->mbr_req_count > 0) return ISO_BOOT_MBR_OVERLAP; ret = iso_quick_mbr_entry(t->mbr_req, &(t->mbr_req_count), (uint64_t) 0, (uint64_t) 0, 0x96, 0x80, 0); if (ret < 0) return ret; return ISO_SUCCESS; } part_start = t->curblock * 4; keep = 0; if (t->opts->prep_partition != NULL) { ret = compute_partition_size(t, t->opts->prep_partition, &(t->prep_part_size), t->opts->prep_part_flag & 1); if (ret < 0) return ret; if (ret == ISO_SUCCESS + 1) { /* Interval from imported_iso in add-on session will be kept */ ret = iso_interval_reader_start_size(t, t->opts->prep_partition, &start_byte, &byte_count, 0); if (ret < 0) return ret; part_start = start_byte / 512; keep = 1; } } if (t->prep_part_size > 0 || t->opts->fat || will_have_gpt) { /* Protecting MBR entry for ISO start or whole ISO */ part_type = 0xcd; if (t->opts->iso_mbr_part_type >= 0 && t->opts->iso_mbr_part_type <= 255) part_type= t->opts->iso_mbr_part_type; if (will_have_gpt) part_type = 0xee; ret = iso_quick_mbr_entry(t->mbr_req, &(t->mbr_req_count), will_have_gpt ? (uint64_t) 1 : ((uint64_t) t->opts->partition_offset) * 4, (uint64_t) 0, part_type, 0, 0); if (ret < 0) return ret; } if (t->prep_part_size > 0) { ret = iso_quick_mbr_entry(t->mbr_req, &(t->mbr_req_count), part_start, ((uint64_t) t->prep_part_size) * 4, 0x41, 0, 0); if (ret < 0) return ret; if (!keep) { t->curblock += t->prep_part_size; part_start = t->curblock * 4; } else { part_start += t->prep_part_size * 4; } } else { part_start = t->curblock * 4; } if (t->prep_part_size > 0 || t->opts->fat) { /* FAT partition or protecting MBR entry for ISO end */ ret = iso_quick_mbr_entry(t->mbr_req, &(t->mbr_req_count), part_start, (uint64_t) 0, t->opts->fat ? 0x0c : 0xcd, 0, 0); if (ret < 0) return ret; } return ISO_SUCCESS; } static int partprepend_writer_write_vol_desc(IsoImageWriter *writer) { return ISO_SUCCESS; } static int partprepend_writer_write_data(IsoImageWriter *writer) { Ecma119Image *t; int ret; t = writer->target; if (t->opts->efi_boot_partition != NULL && t->efi_boot_part_size) { if (t->efi_boot_part_filesrc != NULL) { ret = iso_filesrc_write_data(t, t->efi_boot_part_filesrc, NULL, NULL, 0); } else { ret = iso_write_partition_file(t, t->opts->efi_boot_partition, (uint32_t) 0, t->efi_boot_part_size, t->opts->efi_boot_part_flag & 1); } if (ret < 0) return ret; } if (t->opts->prep_partition != NULL && t->prep_part_size) { ret = iso_write_partition_file(t, t->opts->prep_partition, (uint32_t) 0, t->prep_part_size, t->opts->prep_part_flag & 1); if (ret < 0) return ret; } return ISO_SUCCESS; } static int partprepend_writer_free_data(IsoImageWriter *writer) { return ISO_SUCCESS; } int partprepend_writer_create(Ecma119Image *target) { IsoImageWriter *writer; writer = calloc(1, sizeof(IsoImageWriter)); if (writer == NULL) { return ISO_OUT_OF_MEM; } writer->compute_data_blocks = partprepend_writer_compute_data_blocks; writer->write_vol_desc = partprepend_writer_write_vol_desc; writer->write_data = partprepend_writer_write_data; writer->free_data = partprepend_writer_free_data; writer->data = NULL; writer->target = target; /* add this writer to image */ target->writers[target->nwriters++] = writer; return ISO_SUCCESS; } #ifdef Libisofs_appended_partitions_inlinE /* ----------------- Inline Partition Append Writer ------------------ */ static int partappend_writer_compute_data_blocks(IsoImageWriter *writer) { int ret; ret = iso_compute_append_partitions(writer->target, 1); return ret; } static int partappend_writer_write_vol_desc(IsoImageWriter *writer) { return ISO_SUCCESS; } static int partappend_writer_write_data(IsoImageWriter *writer) { Ecma119Image *target; int res, first_partition = 1, last_partition = 0; int i; target = writer->target; /* Append partition data */ iso_tell_max_part_range(target->opts, &first_partition, &last_partition, 0); for (i = first_partition - 1; i <= last_partition - 1; i++) { if (target->opts->appended_partitions[i] == NULL) continue; if (target->opts->appended_partitions[i][0] == 0) continue; res = iso_write_partition_file(target, target->opts->appended_partitions[i], target->appended_part_prepad[i], target->appended_part_size[i], target->opts->appended_part_flags[i] & 1); if (res < 0) return res; target->curblock += target->appended_part_size[i]; } return ISO_SUCCESS; } static int partappend_writer_free_data(IsoImageWriter *writer) { return ISO_SUCCESS; } int partappend_writer_create(Ecma119Image *target) { IsoImageWriter *writer; writer = calloc(1, sizeof(IsoImageWriter)); if (writer == NULL) { return ISO_OUT_OF_MEM; } writer->compute_data_blocks = partappend_writer_compute_data_blocks; writer->write_vol_desc = partappend_writer_write_vol_desc; writer->write_data = partappend_writer_write_data; writer->free_data = partappend_writer_free_data; writer->data = NULL; writer->target = target; /* add this writer to image */ target->writers[target->nwriters++] = writer; return ISO_SUCCESS; } #endif /* Libisofs_appended_partitions_inlinE */ void iso_delete_gpt_apm_fillers(Ecma119Image *target, int flag) { int i, widx; /* Dispose the requests with req_status bit0 */ for (i = 0; i < target->gpt_req_count; i++) { if (target->gpt_req[i]->req_status & 1) { free(target->gpt_req[i]); target->gpt_req[i] = NULL; } } /* Densify the request arrays */ widx = 0; for (i = 0; i < target->gpt_req_count; i++) { if (target->gpt_req[i] != NULL) { target->gpt_req[widx] = target->gpt_req[i]; widx++; } } target->gpt_req_count = widx; /* And again for APM */ for (i = 0; i < target->apm_req_count; i++) { if (target->apm_req[i]->req_status & 1) { free(target->apm_req[i]); target->apm_req[i] = NULL; } } widx = 0; for (i = 0; i < target->apm_req_count; i++) { if (target->apm_req[i] != NULL) { target->apm_req[widx] = target->apm_req[i]; widx++; } } target->apm_req_count = widx; } /* * Copyright (c) 2008 Vreixo Formoso * Copyright (c) 2012 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /* * Functions for dealing with the system area, this is, the first 16 blocks * of the image. * * At this time, this is only used for hybrid boot images with isolinux. */ #ifndef SYSTEM_AREA_H_ #define SYSTEM_AREA_H_ #include "ecma119.h" /* * Create a MBR for an isohybrid enabled ISOLINUX boot image. * * It is assumed that the caller has verified the readiness of the boot image * by checking for 0xfb 0xc0 0x78 0x70 at bytes 0x40 to 0x43 of isolinux.bin. * * @param bin_lba The predicted LBA of isolinux.bin within the emerging ISO. * @param img_blocks The predicted number of 2048 byte blocks in the ISO. * It will get rounded up to full MBs and that many blocks * must really be written as ISO 9660 image. * @param mbr A buffer of at least 512 bytes to take the result which is * to be written as the very beginning of the ISO. * @param flag unused yet, submit 0 * @return <0 = fatal, 0 = failed , 1 = ok , 2 = ok with size warning */ int make_isohybrid_mbr(int bin_lba, int *img_blocks, char *mbr, int flag); /** * Write the system area for the given image to the given buffer. * * @param buf * A buffer with at least 32 K allocated * @param flag * bit0= t->opts->ms_block is not counted in t->total_size * @return * 1 if success, < 0 on error */ int iso_write_system_area(Ecma119Image *t, uint8_t *buf, int flag); /** * Adjust t->tail_blocks to the eventual alignment needs of isohybrid booting. */ int iso_align_isohybrid(Ecma119Image *t, int flag); /** * Read the necessary ELF information from the first MIPS boot file. * See doc/boot_sectors.txt "DEC Boot Block" for "MIPS Little Endian". */ int iso_read_mipsel_elf(Ecma119Image *t, int flag); /* Compute size and position of appended partitions. */ int iso_compute_append_partitions(Ecma119Image *t, int flag); /* The parameter struct for production of a single MBR partition entry. See also the description of MBR in doc/boot_sectors.txt. No sorting by start sector and gap filling is done before the System Area gets written. But the entries may get assigned to a desired slot number in the table. Requested entries with block_count == 0 get expanded to the start of the next requested entry resp. to image end, if no entry follows. start_block of a following entry must be at least a high as the sum of start_block and block_count of the previous entry. Empty requested entries will be represented as 16 bytes of 0. */ struct iso_mbr_partition_request { /* Always given in blocks of 512 bytes */ uint64_t start_block; /* A block count of 0 means that the partition reaches up to the start of the next one. */ uint64_t block_count; /* Partition type */ uint8_t type_byte; /* 0x80 = bootable */ uint8_t status_byte; /* If >= 1 && <= 4 : The partition slot number in MBR. If more than one partition desires the same slot, then an error ISO_BOOT_MBR_COLLISION occurs at registration time. Use iso_mbr_entry_slot_is_free() to detect this in advance. If desired_slot is 0, then the partition entry is put into the lowest MBR slot that is not occupied by an entry with desired_slot > 0 or by an entry that was registered before this entry. */ int desired_slot; /* Only when representing an imported partition: Path of file in imported ISO which holds the partition content. NULL = no such file */ char *image_path; }; /* Copies the content of req and registers it in t.mbr_req[]. I.e. after the call the submitted storage of req can be disposed or re-used. Submit 0 as value flag. */ int iso_register_mbr_entry(struct iso_mbr_partition_request **req_array, int *mbr_req_count, struct iso_mbr_partition_request *req, int flag); /* Convenience frontend for iso_register_mbr_entry(). name and type are 0-terminated strings, which may get silently truncated. */ int iso_quick_mbr_entry(struct iso_mbr_partition_request **req_array, int *mbr_req_count, uint64_t start_block, uint64_t block_count, uint8_t type_byte, uint8_t status_byte, int desired_slot); /* Peek in advance whether a desired slot number is already occupied by a registered MBR entry. Parameter slot may be between 0 and 4. 0 always returns "free". Return value is 0 if occupied, 1 if free, and -1 if the slot number is out of range. */ int iso_mbr_entry_slot_is_free(struct iso_mbr_partition_request **req_array, int mbr_req_count, int slot); /* The parameter struct for production of a single Apple Partition Map entry. See also the partial APM description in doc/boot_sectors.txt. The list of entries is stored e.g. in Ecma119Image.apm_req, .apm_req_count. The size of a block can be chosen by setting Ecma119Image.apm_block_size. If an entry has start_block <=1, then its block_count will be adjusted to the final size of the partition map. If no such entry is requested, then it will be prepended automatically with name "Apple" and type "Apple_partition_map". The requested entries will get sorted and gaps will be filled by more entries. */ struct iso_apm_partition_request { /* Given in blocks of 2 KiB unless (Ecma119Image.apm_req_flags & 4). Written to the ISO image according to Ecma119Image.apm_block_size. */ uint64_t start_block; uint64_t block_count; /* All 32 bytes get copied to the system area. Take care to pad up short strings by 0. */ uint8_t name[32]; uint8_t type[32]; /* Status of the request object itself: bit0= this is an automatically placed filler partition */ uint32_t req_status; /* Only when representing an imported partition: Path of file in imported ISO which holds the partition content. NULL = no such file */ char *image_path; }; /* Copies the content of req and registers it in t.apm_req[]. I.e. after the call the submitted storage of req can be disposed or re-used. Submit 0 as value flag. */ int iso_register_apm_entry(struct iso_apm_partition_request **req_array, int *apm_req_count, struct iso_apm_partition_request *req, int flag); /* Convenience frontend for iso_register_apm_entry(). name and type are 0-terminated strings, which may get silently truncated. */ int iso_quick_apm_entry(struct iso_apm_partition_request **req_array, int *apm_req_count, uint32_t start_block, uint32_t block_count, char *name, char *type); /* These two pseudo-random generators produce byte strings which will surely not duplicate in the first 256 calls. If more calls are necessary in the same process, then one must wait until the output of gettimeofday(2) changes. It is advised to obtain them as late as possible, so that Ecma119Image *t can distinguish itself from other image production setups which might be run on other machines with the same process number at the same time. */ /* Produces a GPT disk or partition GUID. Pseudo-random by iso_generate_gpt_guid() if t->gpt_uuid_counter is 0. Else derived reproducibly by counter number from t->gpt_uuid_base. */ void iso_gpt_uuid(Ecma119Image *t, uint8_t uuid[16]); /* Mark a given byte string as UUID version 4, RFC 4122. */ void iso_mark_guid_version_4(uint8_t *u); /* The parameter struct for production of a single GPT entry. See also the partial GPT description in doc/boot_sectors.txt. The list of entries is stored in Ecma119Image.gpt_req. The GPT header block at byte 0x200 will get produced automatically. The requested entries will get sorted and gaps will be filled by more entries. Overlapping partitions are allowed only if (Ecma119Image.gpt_req_flags & 1). The block_count will be truncated to the image size before the GPT backup. The GPT entries will be stored after the Apple Partition Map, if such gets generated too. Both partition descriptions must fit into the 32 KiB of the ISO 9660 System Area. GPT can be combined with APM only if (Ecma119Image.apm_block_size > 512). Otherwise, block 1 of APM and GPT header block would collide. So Ecma119Image.apm_block_size is set automatically to 2048 if at least one GPT entry is requested. (One could try 1024 ...). */ struct iso_gpt_partition_request { /* Always given in blocks of 512 bytes. */ uint64_t start_block; uint64_t block_count; /* The registered GUID which defines the partition type */ uint8_t type_guid[16]; /* An individual GUID which shall be unique to the partition. If the caller submits 0...0 then a (weak) random uuid will be generated. */ uint8_t partition_guid[16]; /* bit0= "System Partition" Do not alter, bit2= Legacy BIOS bootable (MBR partition type 0x80) bit60= read-only */ uint64_t flags; /* Fill with text encoded as UTF-16LE. All 72 bytes get copied to the system area. Take care to pad up short strings by 0. */ uint8_t name[72]; /* Only if read from imported image: Table index of partition (first = 1) */ uint32_t idx; /* Status of the request object itself: bit0= this is an automatically placed filler partition */ uint32_t req_status; /* Desired partition number in emerging GPT: first = 1, no desire = 0 GPT partitions get sorted by start LBA. Gaps of uncovered blocks get filled. If the resulting sequence positions the partition at a lower slot than desired, then empty slots get inserted to match the desire. If the sequence positions the partition at a higher slot, then a mere note is issued and the partition gets into the higher slot. */ int desired_slot; /* Only when representing an imported partition: Path of file in imported ISO which holds the partition content. NULL = no such file */ char *image_path; }; /* Copies the content of req and registers it in t.gpt_req[]. I.e. after the call the submitted storage of req can be disposed or re-used. Submit 0 as value flag. */ int iso_register_gpt_entry(struct iso_gpt_partition_request **req_array, int *gpt_req_count, struct iso_gpt_partition_request *req, int flag); /* Convenience frontend for iso_register_gpt_entry(). name has to be already encoded as UTF-16LE. */ int iso_quick_gpt_entry(struct iso_gpt_partition_request **req_array, int *gpt_req_count, uint64_t start_block, uint64_t block_count, uint8_t type_guid[16], uint8_t partition_guid[16], uint64_t flags, uint8_t name[72]); /* Deletes the partition requests for gap filling in GPT and APM. Purpose is to get the request list clean again after a multi-session emulation superblock was created and handed to the application. */ void iso_delete_gpt_apm_fillers(Ecma119Image *target, int flag); /* Internal helper that will be used by system_area.c and make_isohybrid_mbr.c */ int iso_write_gpt_header_block(Ecma119Image *t, uint32_t img_blocks, uint8_t *buf, uint32_t max_entries, uint32_t part_start, uint32_t p_arr_crc); /* The description of a loaded MIPS Big Endian Volume Directory Entry */ struct iso_mips_voldir_entry { char name[9]; uint32_t boot_block; uint32_t boot_bytes; }; /* The description of a loaded SUN Disk Label partition */ struct iso_sun_disk_label_entry { int idx; uint16_t id_tag; uint16_t permissions; uint32_t start_cyl; uint32_t num_blocks; }; /* Creates the Partition Prepend writer. */ int partprepend_writer_create(Ecma119Image *target); /* Creates the Inline Partition Append Writer */ int partappend_writer_create(Ecma119Image *target); /* Creates the GPT backup tail writer. */ int gpt_tail_writer_create(Ecma119Image *target); /* Not for execution but only to identify the writer by ( writer->write_vol_desc == gpt_tail_writer_write_vol_desc ) */ int gpt_tail_writer_write_vol_desc(IsoImageWriter *writer); /* Only for up to 36 characters ISO-8859-1 (or ASCII) input */ void iso_ascii_utf_16le(uint8_t gap_name[72]); /* Parameters of MBR patching for GRUB2 Might later become variables in Ecma119Image */ #define Libisofs_grub2_mbr_patch_poS 0x1b0 #define Libisofs_grub2_mbr_patch_offsT 4 /* Parameters of SUN Disk Label patching for GRUB2 See API iso_image_set_sparc_core(). */ #define Libisofs_grub2_sparc_patch_adr_poS 0x228 #define Libisofs_grub2_sparc_patch_size_poS 0x230 /* Put appended partitions into the writer range */ #define Libisofs_appended_partitions_inlinE yes #ifdef Libisofs_appended_partitions_inlinE /* For padding after appended partitions (and also after backup GPT) */ #define Libisofs_part_align_writeR yes #endif #endif /* SYSTEM_AREA_H_ */ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2011 - 2022 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ /* * Functions that act on the iso tree. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "libisofs.h" #include "node.h" #include "image.h" #include "fsource.h" #include "builder.h" #include "messages.h" #include "tree.h" #include "util.h" #include "ecma119.h" #include #include #include #include #include #include /** * Add a new directory to the iso tree. * * @param parent * the dir where the new directory will be created * @param name * name for the new dir. If a node with same name already exists on * parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE. * @param dir * place where to store a pointer to the newly created dir. No extra * ref is added, so you will need to call iso_node_ref() if you really * need it. You can pass NULL in this parameter if you don't need the * pointer. * @return * number of nodes in dir if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if parent or name are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists */ int iso_tree_add_new_dir(IsoDir *parent, const char *name, IsoDir **dir) { int ret; char *n; IsoDir *node; IsoNode **pos; time_t now; if (parent == NULL || name == NULL) { return ISO_NULL_POINTER; } if (dir) { *dir = NULL; } /* find place where to insert and check if it exists */ if (iso_dir_exists(parent, name, &pos)) { /* a node with same name already exists */ return ISO_NODE_NAME_NOT_UNIQUE; } n = strdup(name); ret = iso_node_new_dir(n, &node); if (ret < 0) { free(n); return ret; } /* permissions from parent */ iso_node_set_permissions((IsoNode*)node, parent->node.mode); iso_node_set_uid((IsoNode*)node, parent->node.uid); iso_node_set_gid((IsoNode*)node, parent->node.gid); iso_node_set_hidden((IsoNode*)node, parent->node.hidden); /* current time */ iso_nowtime(&now, 0); iso_node_set_atime((IsoNode*)node, now); iso_node_set_ctime((IsoNode*)node, now); iso_node_set_mtime((IsoNode*)node, now); if (dir) { *dir = node; } /* add to dir */ return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER); } int iso_image_add_new_dir(IsoImage *image, IsoDir *parent, const char *name, IsoDir **dir) { int ret; char *namept; ret = iso_image_truncate_name(image, name, &namept, 0); if (ret < 0) return ret; ret = iso_tree_add_new_dir(parent, namept, dir); return ret; } /** * Add a new symlink to the directory tree. Permissions are set to 0777, * owner and hidden atts are taken from parent. You can modify any of them * later. * * @param parent * the dir where the new symlink will be created * @param name * name for the new dir. If a node with same name already exists on * parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE. * @param dest * destination of the link * @param link * place where to store a pointer to the newly created link. No extra * ref is added, so you will need to call iso_node_ref() if you really * need it. You can pass NULL in this parameter if you don't need the * pointer * @return * number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if parent, name or dest are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_OUT_OF_MEM */ int iso_tree_add_new_symlink(IsoDir *parent, const char *name, const char *dest, IsoSymlink **link) { int ret; char *n, *d; IsoSymlink *node; IsoNode **pos; time_t now; if (parent == NULL || name == NULL || dest == NULL) { return ISO_NULL_POINTER; } if (link) { *link = NULL; } /* find place where to insert */ if (iso_dir_exists(parent, name, &pos)) { /* a node with same name already exists */ return ISO_NODE_NAME_NOT_UNIQUE; } n = strdup(name); d = strdup(dest); ret = iso_node_new_symlink(n, d, &node); if (ret < 0) { free(n); free(d); return ret; } /* permissions from parent */ iso_node_set_permissions((IsoNode*)node, 0777); iso_node_set_uid((IsoNode*)node, parent->node.uid); iso_node_set_gid((IsoNode*)node, parent->node.gid); iso_node_set_hidden((IsoNode*)node, parent->node.hidden); /* current time */ iso_nowtime(&now, 0); iso_node_set_atime((IsoNode*)node, now); iso_node_set_ctime((IsoNode*)node, now); iso_node_set_mtime((IsoNode*)node, now); if (link) { *link = node; } /* add to dir */ return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER); } int iso_image_add_new_symlink(IsoImage *image, IsoDir *parent, const char *name, const char *dest, IsoSymlink **link) { int ret; char *namept; ret = iso_image_truncate_name(image, name, &namept, 0); if (ret < 0) return ret; ret = iso_tree_add_new_symlink(parent, namept, dest, link); return ret; } /** * Add a new special file to the directory tree. As far as libisofs concerns, * an special file is a block device, a character device, a FIFO (named pipe) * or a socket. You can choose the specific kind of file you want to add * by setting mode properly (see man 2 stat). * * Note that special files are only written to image when Rock Ridge * extensions are enabled. Moreover, a special file is just a directory entry * in the image tree, no data is written beyond that. * * Owner and hidden atts are taken from parent. You can modify any of them * later. * * @param parent * the dir where the new special file will be created * @param name * name for the new special file. If a node with same name already exists * on parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE. * @param mode * file type and permissions for the new node. Note that you can't * specify any kind of file here, only special types are allowed. i.e, * S_IFSOCK, S_IFBLK, S_IFCHR and S_IFIFO are valid types; S_IFLNK, * S_IFREG and S_IFDIR aren't. * @param dev * device ID, equivalent to the st_rdev field in man 2 stat. * @param special * place where to store a pointer to the newly created special file. No * extra ref is added, so you will need to call iso_node_ref() if you * really need it. You can pass NULL in this parameter if you don't need * the pointer. * @return * number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if parent, name or dest are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_OUT_OF_MEM * */ int iso_tree_add_new_special(IsoDir *parent, const char *name, mode_t mode, dev_t dev, IsoSpecial **special) { int ret; char *n; IsoSpecial *node; IsoNode **pos; time_t now; if (parent == NULL || name == NULL) { return ISO_NULL_POINTER; } if (S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)) { return ISO_WRONG_ARG_VALUE; } if (special) { *special = NULL; } /* find place where to insert */ if (iso_dir_exists(parent, name, &pos)) { /* a node with same name already exists */ return ISO_NODE_NAME_NOT_UNIQUE; } n = strdup(name); ret = iso_node_new_special(n, mode, dev, &node); if (ret < 0) { free(n); return ret; } /* atts from parent */ iso_node_set_uid((IsoNode*)node, parent->node.uid); iso_node_set_gid((IsoNode*)node, parent->node.gid); iso_node_set_hidden((IsoNode*)node, parent->node.hidden); /* current time */ iso_nowtime(&now, 0); iso_node_set_atime((IsoNode*)node, now); iso_node_set_ctime((IsoNode*)node, now); iso_node_set_mtime((IsoNode*)node, now); if (special) { *special = node; } /* add to dir */ return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER); } int iso_image_add_new_special(IsoImage *image, IsoDir *parent, const char *name, mode_t mode, dev_t dev, IsoSpecial **special) { int ret; char *namept; ret = iso_image_truncate_name(image, name, &namept, 0); if (ret < 0) return ret; ret = iso_tree_add_new_special(parent, namept, mode, dev, special); return ret; } /** * Add a new regular file to the iso tree. Permissions are set to 0444, * owner and hidden atts are taken from parent. You can modify any of them * later. * * @param parent * the dir where the new file will be created * @param name * name for the new file. If a node with same name already exists on * parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE. * @param stream * IsoStream for the contents of the file * @param file * place where to store a pointer to the newly created file. No extra * ref is added, so you will need to call iso_node_ref() if you really * need it. You can pass NULL in this parameter if you don't need the * pointer * @return * number of nodes in parent if success, < 0 otherwise * Possible errors: * ISO_NULL_POINTER, if parent, name or dest are NULL * ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists * ISO_OUT_OF_MEM * * @since 0.6.4 */ int iso_tree_add_new_file(IsoDir *parent, const char *name, IsoStream *stream, IsoFile **file) { int ret; char *n; IsoFile *node; IsoNode **pos; time_t now; if (parent == NULL || name == NULL || stream == NULL) { return ISO_NULL_POINTER; } if (file) { *file = NULL; } /* find place where to insert */ if (iso_dir_exists(parent, name, &pos)) { /* a node with same name already exists */ return ISO_NODE_NAME_NOT_UNIQUE; } n = strdup(name); ret = iso_node_new_file(n, stream, &node); if (ret < 0) { free(n); return ret; } /* permissions from parent */ iso_node_set_permissions((IsoNode*)node, 0444); iso_node_set_uid((IsoNode*)node, parent->node.uid); iso_node_set_gid((IsoNode*)node, parent->node.gid); iso_node_set_hidden((IsoNode*)node, parent->node.hidden); /* current time */ iso_nowtime(&now, 0); iso_node_set_atime((IsoNode*)node, now); iso_node_set_ctime((IsoNode*)node, now); iso_node_set_mtime((IsoNode*)node, now); if (file) { *file = node; } /* add to dir */ return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER); } int iso_image_add_new_file(IsoImage *image, IsoDir *parent, const char *name, IsoStream *stream, IsoFile **file) { int ret; char *namept; ret = iso_image_truncate_name(image, name, &namept, 0); if (ret < 0) return ret; ret = iso_tree_add_new_file(parent, namept, stream, file); return ret; } /** * Set whether to follow or not symbolic links when added a file from a source * to IsoImage. */ void iso_tree_set_follow_symlinks(IsoImage *image, int follow) { image->follow_symlinks = follow ? 1 : 0; } /** * Get current setting for follow_symlinks. * * @see iso_tree_set_follow_symlinks */ int iso_tree_get_follow_symlinks(IsoImage *image) { return image->follow_symlinks; } /** * Set whether to skip or not hidden files when adding a directory recursibely. * Default behavior is to not ignore them, i.e., to add hidden files to image. */ void iso_tree_set_ignore_hidden(IsoImage *image, int skip) { image->ignore_hidden = skip ? 1 : 0; } /** * Get current setting for ignore_hidden. * * @see iso_tree_set_ignore_hidden */ int iso_tree_get_ignore_hidden(IsoImage *image) { return image->ignore_hidden; } void iso_tree_set_replace_mode(IsoImage *image, enum iso_replace_mode mode) { image->replace = mode; } enum iso_replace_mode iso_tree_get_replace_mode(IsoImage *image) { return image->replace; } /** * Set whether to skip or not special files. Default behavior is to not skip * them. Note that, despite of this setting, special files won't never be added * to an image unless RR extensions were enabled. * * @param skip * Bitmask to determine what kind of special files will be skipped: * bit0: ignore FIFOs * bit1: ignore Sockets * bit2: ignore char devices * bit3: ignore block devices */ void iso_tree_set_ignore_special(IsoImage *image, int skip) { image->ignore_special = skip & 0x0F; } /** * Get current setting for ignore_special. * * @see iso_tree_set_ignore_special */ int iso_tree_get_ignore_special(IsoImage *image) { return image->ignore_special; } /** * Set a callback function that libisofs will call for each file that is * added to the given image by a recursive addition function. This includes * image import. * * @param report * pointer to a function that will be called just before a file will be * added to the image. You can control whether the file will be in fact * added or ignored. * This function should return 1 to add the file, 0 to ignore it and * continue, < 0 to abort the process * NULL is allowed if you don't want any callback. */ void iso_tree_set_report_callback(IsoImage *image, int (*report)(IsoImage*, IsoFileSource*)) { image->report = report; } /** * Add a excluded path. These are paths that won't never added to image, * and will be excluded even when adding recursively its parent directory. * * For example, in * * iso_tree_add_exclude(image, "/home/user/data/private"); * iso_tree_add_dir_rec(image, root, "/home/user/data"); * * the directory /home/user/data/private won't be added to image. * * @return * 1 on success, < 0 on error */ int iso_tree_add_exclude(IsoImage *image, const char *path) { if (image == NULL || path == NULL) { return ISO_NULL_POINTER; } image->excludes = realloc(image->excludes, ++image->nexcludes * sizeof(void*)); if (image->excludes == NULL) { return ISO_OUT_OF_MEM; } image->excludes[image->nexcludes - 1] = strdup(path); if (image->excludes[image->nexcludes - 1] == NULL) { return ISO_OUT_OF_MEM; } return ISO_SUCCESS; } /** * Remove a previously added exclude. * * @see iso_tree_add_exclude * @return * 1 on success, 0 exclude do not exists, < 0 on error */ int iso_tree_remove_exclude(IsoImage *image, const char *path) { size_t i, j; if (image == NULL || path == NULL) { return ISO_NULL_POINTER; } for (i = 0; (int) i < image->nexcludes; ++i) { if (strcmp(image->excludes[i], path) == 0) { /* exclude found */ free(image->excludes[i]); --image->nexcludes; for (j = i; (int) j < image->nexcludes; ++j) { image->excludes[j] = image->excludes[j+1]; } image->excludes = realloc(image->excludes, image->nexcludes * sizeof(void*)); return ISO_SUCCESS; } } return 0; } static int iso_tree_add_node_builder(IsoImage *image, IsoDir *parent, IsoFileSource *src, IsoNodeBuilder *builder, IsoNode **node) { int result; IsoNode *new; IsoNode **pos; char *name = NULL, *namept; if (parent == NULL || src == NULL || builder == NULL) { result = ISO_NULL_POINTER; goto ex; } if (node) { *node = NULL; } name = iso_file_source_get_name(src); result = iso_image_truncate_name(image, name, &namept, 0); if (result < 0) return result; /* find place where to insert */ result = iso_dir_exists(parent, namept, &pos); if (result) { /* a node with same name already exists */ result = ISO_NODE_NAME_NOT_UNIQUE; goto ex; } result = builder->create_node(builder, image, src, namept, &new); if (result < 0) goto ex; if (node) { *node = new; } /* finally, add node to parent */ result = iso_dir_insert(parent, (IsoNode*)new, pos, ISO_REPLACE_NEVER); ex: if (name != NULL) free(name); return result; } int iso_tree_add_node(IsoImage *image, IsoDir *parent, const char *path, IsoNode **node) { int result; IsoFilesystem *fs; IsoFileSource *file; if (image == NULL || parent == NULL || path == NULL) { return ISO_NULL_POINTER; } fs = image->fs; result = fs->get_by_path(fs, path, &file); if (result < 0) { return result; } result = iso_tree_add_node_builder(image, parent, file, image->builder, node); /* free the file */ iso_file_source_unref(file); return result; } int iso_tree_add_new_node(IsoImage *image, IsoDir *parent, const char *name, const char *path, IsoNode **node) { int result; IsoFilesystem *fs; IsoFileSource *file; IsoNode *new; IsoNode **pos; char *namept; if (image == NULL || parent == NULL || name == NULL || path == NULL) { return ISO_NULL_POINTER; } if (node) { *node = NULL; } result = iso_image_truncate_name(image, name, &namept, 0); if (result < 0) return result; /* find place where to insert */ result = iso_dir_exists(parent, namept, &pos); if (result) { /* a node with same name already exists */ return ISO_NODE_NAME_NOT_UNIQUE; } fs = image->fs; result = fs->get_by_path(fs, path, &file); if (result < 0) { return result; } result = image->builder->create_node(image->builder, image, file, namept, &new); /* free the file */ iso_file_source_unref(file); if (result < 0) { return result; } if (node) { *node = new; } /* finally, add node to parent */ return iso_dir_insert(parent, new, pos, ISO_REPLACE_NEVER); } int iso_tree_add_new_cut_out_node(IsoImage *image, IsoDir *parent, const char *name, const char *path, off_t offset, off_t size, IsoNode **node) { int result; off_t src_size; struct stat info; IsoFilesystem *fs; IsoFileSource *src; IsoFile *new; IsoNode **pos; IsoStream *stream; char *namept; if (image == NULL || parent == NULL || name == NULL || path == NULL) { return ISO_NULL_POINTER; } if (node) { *node = NULL; } result = iso_image_truncate_name(image, name, &namept, 0); if (result < 0) return result; /* find place where to insert */ result = iso_dir_exists(parent, namept, &pos); if (result) { /* a node with same name already exists */ return ISO_NODE_NAME_NOT_UNIQUE; } fs = image->fs; result = fs->get_by_path(fs, path, &src); if (result < 0) { return result; } result = iso_file_source_stat(src, &info); if (result < 0) { iso_file_source_unref(src); return result; } if (S_ISREG(info.st_mode)) { src_size = info.st_size; } else { src_size = iso_file_source_determine_capacity(src, offset + size, 3); if (src_size <= 0) return ISO_WRONG_ARG_VALUE; } if (offset >= src_size) { return ISO_WRONG_ARG_VALUE; } /* force regular file */ result = image->builder->create_file(image->builder, image, src, &new); /* Give up the newly acquired surplus reference to src */ iso_file_source_unref(src); if (result < 0) { return result; } /* replace file iso stream with a cut-out-stream */ result = iso_cut_out_stream_new(src, offset, size, &stream); if (result < 0) { iso_node_unref((IsoNode*)new); return result; } iso_stream_unref(new->stream); new->stream = stream; result = iso_node_set_name((IsoNode*)new, namept); if (result < 0) { iso_node_unref((IsoNode*)new); return result; } if (node) { *node = (IsoNode*)new; } /* finally, add node to parent */ return iso_dir_insert(parent, (IsoNode*)new, pos, ISO_REPLACE_NEVER); } static int check_excludes(IsoImage *image, const char *path) { int i; for (i = 0; i < image->nexcludes; ++i) { char *exclude = image->excludes[i]; if (exclude[0] == '/') { /* absolute exclude, must completely match path */ if (!fnmatch(exclude, path, FNM_PERIOD|FNM_PATHNAME)) { return 1; } } else { /* relative exclude, it is enough if a part of the path matches */ char *pos = (char*)path; while (pos != NULL) { pos++; if (!fnmatch(exclude, pos, FNM_PERIOD|FNM_PATHNAME)) { return 1; } pos = strchr(pos, '/'); } } } return 0; } static int check_hidden(IsoImage *image, const char *name) { return (image->ignore_hidden && name[0] == '.'); } static int check_special(IsoImage *image, mode_t mode) { if (image->ignore_special != 0) { switch(mode & S_IFMT) { case S_IFBLK: return image->ignore_special & 0x08 ? 1 : 0; case S_IFCHR: return image->ignore_special & 0x04 ? 1 : 0; case S_IFSOCK: return image->ignore_special & 0x02 ? 1 : 0; case S_IFIFO: return image->ignore_special & 0x01 ? 1 : 0; default: return 0; } } return 0; } static void ascii_increment(char *name, int len, int pos, int rollover_carry) { int c; again:; if (pos < 0 || pos >= len) pos = len - 1; c = name[pos]; if (c >= '0' && c < '9') { c++; } else if (c == '9') { c = 'A'; } else if (c >= 'A' && c < 'Z') { c++; } else if (c == 'Z') { c = '_'; } else if (c == '_') { c = 'a'; } else if (c >= 'a' && c < 'z') { c++; } else if (c == 'z') { c = '0'; name[pos] = c; pos--; if (pos >= 0 || rollover_carry) goto again; return; } else { if (pos == len - 1 || name[pos + 1] == '.') c = '_'; /* Make first change less riddling */ else c = '0'; /* But else use the full range of valid characters */ } name[pos] = c; } static int insert_underscores(char *name, int *len, int *at_pos, int count, char **new_name) { int ret; LIBISO_ALLOC_MEM(*new_name, char, count + *len + 1); if (*at_pos > 0) memcpy(*new_name, name, *at_pos); if (count > 0) memset(*new_name + *at_pos, '_', count); if (*len > *at_pos) memcpy(*new_name + *at_pos + count, name + *at_pos, *len - *at_pos); (*new_name)[count + *len] = 0; *len += count; *at_pos += count; ret= ISO_SUCCESS; ex:; return ret; } static int make_incrementable_name(char **name, char **unique_name, int *low_pos, int *rollover_carry, int *pre_check) { char *dpt, *npt; int first, len, ret; /* The incrementable part of the file shall have at least 7 characters. There may be up to pow(2.0,32.0)*2048/33 = 266548273400 files. The set of increment result characters has 63 elements. pow(63.0,7.0) is nearly 15 times larger than 266548273400. */ static int min_incr = 7; /* At most two suffixes of total length up to 12, like .tar.bz2, shall be preserved. The incrementable part will eventually be padded up. Incrementing begins before the last suffix in any case. But when this rolls over on short prefixes, then long last suffixes will get used as high characters of the incremental part. This is indicated by *rollover_carry which corresponds to the parameter of ascii_increment() with the same name. */ static int max_suffix = 12; *rollover_carry = 0; *pre_check = 0; len = strlen(*name); /* Check if the part before the first dot is long enough. If not, then preserve the last two short suffixes. */ dpt = strchr(*name, '.'); if (dpt != NULL) if ((dpt - *name) < min_incr) dpt = strrchr(*name, '.'); if (dpt != NULL) { first= (dpt - *name); if (dpt > *name && len - first < max_suffix) { for(npt = dpt - 1; npt >= *name && *npt != '.'; npt--); if (npt >= *name) { if (len - (npt - *name) <= max_suffix) { first= (npt - *name); dpt = npt; } } } } else first= len; if (first < min_incr && (len - first) <= max_suffix) { ret = insert_underscores(*name, &len, &first, min_incr - first, unique_name); if (ret < 0) goto ex; *pre_check = 1; /* It might now already be unique */ } else if (len < 64) { /* Insert an underscore to preserve the original name at least for the first few increments */ ret = insert_underscores(*name, &len, &first, 1, unique_name); if (ret < 0) goto ex; *pre_check = 1; } else { LIBISO_ALLOC_MEM(*unique_name, char, len + 1); memcpy(*unique_name, *name, len); if (first < min_incr) *rollover_carry = 1; /* Do not get caged before the dots */ } (*unique_name)[len] = 0; *low_pos = first - 1; ret = 1; ex:; return(ret); } static int make_really_unique_name(IsoDir *parent, char **name, char **unique_name, IsoNode ***pos, int flag) { int ret, rollover_carry = 0, pre_check = 0, ascii_idx = -1, len; ret = make_incrementable_name(name, unique_name, &ascii_idx, &rollover_carry, &pre_check); if (ret < 0) goto ex; len = strlen(*unique_name); while (1) { if (!pre_check) ascii_increment(*unique_name, len, ascii_idx, !!rollover_carry); else pre_check = 0; ret = iso_dir_exists(parent, *unique_name, pos); if (ret < 0) goto ex; if (ret == 0) break; } *name = *unique_name; ret = ISO_SUCCESS; ex:; if (ret < 0) { LIBISO_FREE_MEM(*unique_name); *unique_name = NULL; } return ret; } /** * Recursively add a given directory to the image tree. * * @return * 1 continue, < 0 error (ISO_CANCELED stop) */ int iso_add_dir_src_rec(IsoImage *image, IsoDir *parent, IsoFileSource *dir) { int ret, dir_is_open = 0; IsoNodeBuilder *builder; IsoFileSource *file; IsoNode **pos; struct stat info; char *name, *path, *allocated_name = NULL; IsoNode *new; enum iso_replace_mode replace; ret = iso_file_source_open(dir); if (ret < 0) { path = iso_file_source_get_path(dir); /* instead of the probable error, we throw a sorry event */ if (path != NULL) { ret = iso_msg_submit(image->id, ISO_FILE_CANT_ADD, ret, "Can't open dir %s", path); free(path); } else { ret = iso_msg_submit(image->id, ISO_NULL_POINTER, ret, "Can't open dir. NULL pointer caught as dir name"); } goto ex; } dir_is_open = 1; builder = image->builder; /* iterate over all directory children */ while (1) { int skip = 0; ret = iso_file_source_readdir(dir, &file); if (ret <= 0) { if (ret < 0) { /* error reading dir */ ret = iso_msg_submit(image->id, ret, ret, "Error reading dir"); goto ex; } break; /* End of directory */ } path = iso_file_source_get_path(file); if (path == NULL) { ret = iso_msg_submit(image->id, ISO_NULL_POINTER, ret, "NULL pointer caught as file path"); goto ex; } name = strrchr(path, '/') + 1; if (image->follow_symlinks) { ret = iso_file_source_stat(file, &info); } else { ret = iso_file_source_lstat(file, &info); } if (ret < 0) { ret = iso_msg_submit(image->id, ISO_FILE_CANT_ADD, ret, "Error when adding file %s", path); goto dir_rec_continue; } if (image->do_deeper_tree_inspection) { if (image->tree_loaded == 0 && image->rr_loaded == 0) { iso_image_assess_ecma119_name(image, &info, path, name); } else if (image->tree_loaded == 1) { iso_image_assess_joliet_name(image, &info, path, name); } if (info.st_size > MAX_ISO_FILE_SECTION_SIZE && image->tree_compliance != NULL) image->tree_compliance->iso_level = 3; } if (check_excludes(image, path)) { iso_msg_debug(image->id, "Skipping excluded file %s", path); skip = 1; } else if (check_hidden(image, name)) { iso_msg_debug(image->id, "Skipping hidden file %s", path); skip = 1; } else if (check_special(image, info.st_mode)) { iso_msg_debug(image->id, "Skipping special file %s", path); skip = 1; } if (skip) { goto dir_rec_continue; } replace = image->replace; /* find place where to insert */ ret = iso_dir_exists(parent, name, &pos); if (ret) { /* Resolve name collision e.g. caused by fs_image.c:make_hopefully_unique_name() */ LIBISO_FREE_MEM(allocated_name); allocated_name = NULL; ret = make_really_unique_name(parent, &name, &allocated_name, &pos, 0); if (ret < 0) goto ex; image->collision_warnings++; if (image->collision_warnings < ISO_IMPORT_COLL_WARN_MAX) { ret = iso_msg_submit(image->id, ISO_IMPORT_COLLISION, 0, "File name collision resolved with %s . Now: %s", path, name); if (ret < 0) goto ex; } } /* if we are here we must insert. Give user a chance for cancel */ if (image->report) { int r = image->report(image, file); if (r <= 0) { ret = (r < 0 ? ISO_CANCELED : ISO_SUCCESS); goto dir_rec_continue; } } ret = builder->create_node(builder, image, file, name, &new); if (ret < 0) { ret = iso_msg_submit(image->id, ISO_FILE_CANT_ADD, ret, "Error when adding file %s", path); goto dir_rec_continue; } /* ok, node has correctly created, we need to add it */ ret = iso_dir_insert(parent, new, pos, replace); if (ret < 0) { iso_node_unref(new); if (ret != (int) ISO_NODE_NAME_NOT_UNIQUE) { /* error */ goto dir_rec_continue; } else { /* file ignored because a file with same node already exists */ iso_msg_debug(image->id, "Skipping file %s. A node with same " "file already exists", path); ret = 0; } } else { iso_msg_debug(image->id, "Added file %s", path); } /* finally, if the node is a directory we need to recurse */ if (new->type == LIBISO_DIR && S_ISDIR(info.st_mode)) { ret = iso_add_dir_src_rec(image, (IsoDir*)new, file); } dir_rec_continue:; free(path); iso_file_source_unref(file); /* check for error severity to decide what to do */ if (ret < 0) { ret = iso_msg_submit(image->id, ret, 0, NULL); if (ret < 0) goto ex; } } /* while */ ret = ISO_SUCCESS; ex:; if (dir_is_open) iso_file_source_close(dir); LIBISO_FREE_MEM(allocated_name); return ret; } int iso_tree_add_dir_rec(IsoImage *image, IsoDir *parent, const char *dir) { int result; struct stat info; IsoFilesystem *fs; IsoFileSource *file; if (image == NULL || parent == NULL || dir == NULL) { return ISO_NULL_POINTER; } fs = image->fs; result = fs->get_by_path(fs, dir, &file); if (result < 0) { return result; } /* we also allow dir path to be a symlink to a dir */ result = iso_file_source_stat(file, &info); if (result < 0) { iso_file_source_unref(file); return result; } if (!S_ISDIR(info.st_mode)) { iso_file_source_unref(file); return ISO_FILE_IS_NOT_DIR; } result = iso_add_dir_src_rec(image, parent, file); iso_file_source_unref(file); return result; } /* @param flag bit0= truncate according to image truncate mode and length */ int iso_tree_path_to_node_flag(IsoImage *image, const char *path, IsoNode **node, int flag) { int result; IsoNode *n; IsoDir *dir; char *ptr, *brk_info = NULL, *component; if (image == NULL || path == NULL) { return ISO_NULL_POINTER; } /* get the first child at the root of the image that is "/" */ dir = image->root; n = (IsoNode *)dir; if (!strcmp(path, "/")) { if (node) { *node = n; } return ISO_SUCCESS; } ptr = strdup(path); if (ptr == NULL) return ISO_OUT_OF_MEM; result = 0; /* get the first component of the path */ component = strtok_r(ptr, "/", &brk_info); while (component) { if (n->type != LIBISO_DIR) { n = NULL; result = 0; break; } dir = (IsoDir *)n; if ((flag & 1) && image->truncate_mode == 1) { result = iso_dir_get_node_trunc(dir, image->truncate_length, component, &n); } else { result = iso_dir_get_node(dir, component, &n); } if (result != 1) { n = NULL; break; } component = strtok_r(NULL, "/", &brk_info); } free(ptr); if (node) { *node = n; } return result; } int iso_tree_path_to_node(IsoImage *image, const char *path, IsoNode **node) { return iso_tree_path_to_node_flag(image, path, node, 0); } int iso_image_path_to_node(IsoImage *image, const char *path, IsoNode **node) { return iso_tree_path_to_node_flag(image, path, node, 1); } char *iso_tree_get_node_path(IsoNode *node) { char *path = NULL, *parent_path = NULL; if (node == NULL || node->parent == NULL) return NULL; if ((IsoNode*)node->parent == node) { return strdup("/"); } else { parent_path = iso_tree_get_node_path((IsoNode*)node->parent); if (parent_path == NULL) goto ex; if (strlen(parent_path) == 1) { path = calloc(1, strlen(node->name) + 2); if (path == NULL) goto ex; sprintf(path, "/%s", node->name); } else { path = calloc(1, strlen(parent_path) + strlen(node->name) + 2); if (path == NULL) goto ex; sprintf(path, "%s/%s", parent_path, node->name); } } ex:; if (parent_path != NULL) free(parent_path); return path; } /* Note: No reference is taken to the found node. Warning: Do not submit next_above uninitialized. Submit NULL if next_above is not of interest. @param flag bit0= recursion */ int iso_tree_get_node_of_block(IsoImage *image, IsoDir *dir, uint32_t block, IsoNode **found, uint32_t *next_above, int flag) { int ret, section_count, i; IsoDirIter *iter = NULL; IsoNode *node; IsoDir *subdir; IsoFile *file; struct iso_file_section *sections = NULL; uint32_t na = 0; if (dir == NULL) dir = image->root; ret = iso_dir_get_children(dir, &iter); while (iso_dir_iter_next(iter, &node) == 1 ) { if (ISO_NODE_IS_FILE(node)) { file = (IsoFile *) node; ret = iso_file_get_old_image_sections(file, §ion_count, §ions, 0); if (ret <= 0) continue; for (i = 0; i < section_count; i++) { if (sections[i].block <= block && block - sections[i].block < (((off_t) sections[i].size) + 2047) / 2048) { *found = node; ret = 1; goto ex; } if ((na == 0 || sections[i].block < na) && sections[i].block > block) na = sections[i].block; } free(sections); sections = NULL; } else if (ISO_NODE_IS_DIR(node)) { subdir = (IsoDir *) node; ret = iso_tree_get_node_of_block(image, subdir, block, found, &na, 1); if (ret != 0) goto ex; } } if (next_above != NULL && (na > 0 || !(flag & 1))) if (*next_above == 0 || *next_above > na || !(flag & 1)) *next_above = na; ret = 0; ex: if (sections != NULL) free(sections); if (iter != NULL) iso_dir_iter_free(iter); return ret; } /* ------------------------- tree cloning ------------------------------ */ static int iso_tree_copy_node_attr(IsoNode *old_node, IsoNode *new_node, int flag) { int ret; new_node->mode = old_node->mode; new_node->uid = old_node->uid; new_node->gid = old_node->gid; new_node->atime = old_node->atime; new_node->mtime = old_node->mtime; new_node->ctime = old_node->ctime; new_node->hidden = old_node->hidden; ret = iso_node_clone_xinfo(old_node, new_node, 0); if (ret < 0) return ret; return ISO_SUCCESS; } /* @param flag bit0= merge directory with *new_node */ static int iso_tree_clone_dir(IsoDir *old_dir, IsoDir *new_parent, char *new_name, IsoNode **new_node, int flag) { IsoDir *new_dir = NULL; IsoNode *sub_node = NULL, *new_sub_node = NULL; IsoDirIter *iter = NULL; int ret; if (flag & 1) { new_dir = (IsoDir *) *new_node; } else { *new_node = NULL; ret = iso_tree_add_new_dir(new_parent, new_name, &new_dir); if (ret < 0) return ret; } /* Avoid traversal of target directory to allow cloning of old_dir to a subordinate of old_dir. */ iso_node_take((IsoNode *) new_dir); ret = iso_dir_get_children(old_dir, &iter); if (ret < 0) goto ex; while(1) { ret = iso_dir_iter_next(iter, &sub_node); if (ret == 0) break; ret = iso_tree_clone(sub_node, new_dir, sub_node->name, &new_sub_node, flag & 1); if (ret < 0) goto ex; } /* Now graft in the new tree resp. graft back the merged tree */ ret = iso_dir_add_node(new_parent, (IsoNode *) new_dir, 0); if (ret < 0) goto ex; if (!(flag & 1)) *new_node = (IsoNode *) new_dir; ret = ISO_SUCCESS; ex:; if (iter != NULL) iso_dir_iter_free(iter); if (ret < 0 && new_dir != NULL) { if (flag & 1) { /* graft back the merged tree (eventually with half copy) */ iso_dir_add_node(new_parent, (IsoNode *) new_dir, 0); } else { iso_node_remove_tree((IsoNode *) new_dir, NULL); *new_node = NULL; } } return ret; } static int iso_tree_clone_file(IsoFile *old_file, IsoDir *new_parent, char *new_name, IsoNode **new_node, int flag) { IsoStream *new_stream = NULL; IsoFile *new_file = NULL; int ret; *new_node = NULL; ret = iso_stream_clone(old_file->stream, &new_stream, 0); if (ret < 0) return ret; ret = iso_tree_add_new_file(new_parent, new_name, new_stream, &new_file); if (ret < 0) goto ex; new_stream = NULL; /* now owned by new_file */ new_file->from_old_session = old_file->from_old_session; new_file->explicit_weight = old_file->explicit_weight; new_file->sort_weight = old_file->sort_weight; *new_node = (IsoNode *) new_file; ret = ISO_SUCCESS; ex:; if (new_stream != NULL) iso_stream_unref(new_stream); return ret; } static int iso_tree_clone_symlink(IsoSymlink *node, IsoDir *new_parent, char *new_name, IsoNode **new_node, int flag) { IsoSymlink *new_sym; int ret; *new_node = NULL; ret = iso_tree_add_new_symlink(new_parent, new_name, node->dest, &new_sym); if (ret < 0) return ret; new_sym->fs_id = node->fs_id; new_sym->st_dev = node->st_dev; new_sym->st_ino = node->st_ino; *new_node = (IsoNode *) new_sym; return ISO_SUCCESS; } static int iso_tree_clone_special(IsoSpecial *node, IsoDir *new_parent, char *new_name, IsoNode **new_node, int flag) { IsoSpecial *new_spec; IsoNode *iso_node; int ret; iso_node = (IsoNode *) node; ret = iso_tree_add_new_special(new_parent, new_name, iso_node->mode, node->dev, &new_spec); if (ret < 0) return ret; new_spec->fs_id = node->fs_id; new_spec->st_dev = node->st_dev; new_spec->st_ino = node->st_ino; *new_node = (IsoNode *) new_spec; return ISO_SUCCESS; } /* @param flag bit0= Merge directories rather than ISO_NODE_NAME_NOT_UNIQUE. bit1= issue warning in case of truncation */ int iso_tree_clone_trunc(IsoNode *node, IsoDir *new_parent, char *new_name_in, IsoNode **new_node, int truncate_length, int flag) { int ret = ISO_SUCCESS; char *new_name, *trunc = NULL; *new_node = NULL; new_name = new_name_in; if (truncate_length >= 64 && (int) strlen(new_name) > truncate_length) { trunc = strdup(new_name); if (trunc == 0) { ret = ISO_OUT_OF_MEM; goto ex; } ret = iso_truncate_rr_name(1, truncate_length, trunc, !(flag & 2)); if (ret < 0) goto ex; new_name = trunc; } if (iso_dir_get_node(new_parent, new_name, new_node) == 1) { if (! (node->type == LIBISO_DIR && (*new_node)->type == LIBISO_DIR && (flag & 1))) { *new_node = NULL; ret = ISO_NODE_NAME_NOT_UNIQUE; goto ex; } } else flag &= ~1; if (node->type == LIBISO_DIR) { ret = iso_tree_clone_dir((IsoDir *) node, new_parent, new_name, new_node, flag & 1); } else if (node->type == LIBISO_FILE) { ret = iso_tree_clone_file((IsoFile *) node, new_parent, new_name, new_node, 0); } else if (node->type == LIBISO_SYMLINK) { ret = iso_tree_clone_symlink((IsoSymlink *) node, new_parent, new_name, new_node, 0); } else if (node->type == LIBISO_SPECIAL) { ret = iso_tree_clone_special((IsoSpecial *) node, new_parent, new_name, new_node, 0); } else if (node->type == LIBISO_BOOT) { ret = ISO_SUCCESS; /* API says they are silently ignored */ } if (ret < 0) goto ex; if (flag & 1) { ret = 2; /* merged two directories, *new_node is not new */ goto ex; } ret = iso_tree_copy_node_attr(node, *new_node, 0); ex:; if (trunc != NULL) free(trunc); return ret; } /* API */ int iso_tree_clone(IsoNode *node, IsoDir *new_parent, char *new_name, IsoNode **new_node, int flag) { return iso_tree_clone_trunc(node, new_parent, new_name, new_node, 0, flag & 1); } /* API */ int iso_image_tree_clone(IsoImage *image, IsoNode *node, IsoDir *new_parent, char *new_name, IsoNode **new_node, int flag) { int length, ret; if (image->truncate_mode == 0) length = 0; else length = image->truncate_length; ret = iso_tree_clone_trunc(node, new_parent, new_name, new_node, length, flag & 3); return ret; } int iso_tree_resolve_symlink(IsoImage *img, IsoSymlink *sym, IsoNode **res, int *depth, int flag) { IsoDir *cur_dir = NULL; IsoNode *n, *resolved_node; char *dest, *dest_start, *dest_end; int ret = 0; unsigned int comp_len, dest_len; dest = sym->dest; dest_len = strlen(dest); if (dest[0] == '/') { /* ??? How to resolve absolute links without knowing the path of the future mount point ? ??? Would it be better to throw error ? I can only assume that it gets mounted at / during some stage of booting. */; cur_dir = img->root; dest_end = dest; } else { cur_dir = sym->node.parent; if (cur_dir == NULL) cur_dir = img->root; dest_end = dest - 1; } while (dest_end < dest + dest_len) { dest_start = dest_end + 1; dest_end = strchr(dest_start, '/'); if (dest_end == NULL) dest_end = dest_start + strlen(dest_start); comp_len = dest_end - dest_start; if (comp_len == 0 || (comp_len == 1 && dest_start[0] == '.')) continue; if (comp_len == 2 && dest_start[0] == '.' && dest_start[1] == '.') { cur_dir = cur_dir->node.parent; if (cur_dir == NULL) /* link shoots over root */ return ISO_DEAD_SYMLINK; continue; } /* Search node in cur_dir */ for (n = cur_dir->children; n != NULL; n = n->next) if (strncmp(dest_start, n->name, comp_len) == 0 && strlen(n->name) == comp_len) break; if (n == NULL) return ISO_DEAD_SYMLINK; if (n->type == LIBISO_DIR) { cur_dir = (IsoDir *) n; } else if (n->type == LIBISO_SYMLINK) { if (*depth >= LIBISO_MAX_LINK_DEPTH) return ISO_DEEP_SYMLINK; (*depth)++; ret = iso_tree_resolve_symlink(img, (IsoSymlink *) n, &resolved_node, depth, 0); if (ret < 0) return ret; if (resolved_node->type != LIBISO_DIR) { n = resolved_node; goto leaf_type; } cur_dir = (IsoDir *) resolved_node; } else { leaf_type:; if (dest_end < dest + dest_len) /* attempt to dive into file */ return ISO_DEAD_SYMLINK; *res = n; return ISO_SUCCESS; } } *res = (IsoNode *) cur_dir; return ISO_SUCCESS; } /* * Copyright (c) 2007 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_IMAGE_TREE_H_ #define LIBISO_IMAGE_TREE_H_ #include "image.h" /** * Recursively add a given directory to the image tree. * * @return * 1 continue, 0 stop, < 0 error */ int iso_add_dir_src_rec(IsoImage *image, IsoDir *parent, IsoFileSource *dir); int iso_tree_get_node_of_block(IsoImage *image, IsoDir *dir, uint32_t block, IsoNode **found, uint32_t *next_above, int flag); #endif /*LIBISO_IMAGE_TREE_H_*/ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2007 Mario Danic * Copyright (c) 2009 - 2022 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "util.h" #include "libisofs.h" #include "messages.h" #include "joliet.h" #include "node.h" #include #include #include #include #include #include #include #include #include #include #include /* if we don't have eaccess, we check file access by opening it */ #ifndef HAVE_EACCESS #include #include #include #endif /* Produce possibly inflationary error messages directly to stderr */ static int iso_iconv_debug = 0; struct iso_iconv_handle { int status; /* bit0= open , bit1= identical mapping */ iconv_t descr; }; /* @param flag bit0= shortcut by identical mapping is not allowed */ static int iso_iconv_open(struct iso_iconv_handle *handle, char *tocode, char *fromcode, int flag) { handle->status = 0; handle->descr = (iconv_t) -1; if (strcmp(tocode, fromcode) == 0 && !(flag & 1)) { handle->status = 1 | 2; return 1; } handle->descr = iconv_open(tocode, fromcode); if (handle->descr == (iconv_t) -1) { if (strlen(tocode) + strlen(fromcode) <= 160 && iso_iconv_debug) fprintf(stderr, "libisofs_DEBUG: iconv_open(\"%s\", \"%s\") failed: errno= %d %s\n", tocode, fromcode, errno, strerror(errno)); return 0; } handle->status = 1; return 1; } static size_t iso_iconv(struct iso_iconv_handle *handle, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft, int flag) { size_t ret; /* The build system might indicate iconv(,const char **inbuf,) by defining ICONV_CONST const */ #ifndef ICONV_CONST #define ICONV_CONST #endif ICONV_CONST char **local_inbuf; local_inbuf = (ICONV_CONST char **) inbuf; if (!(handle->status & 1)) { if (iso_iconv_debug) fprintf(stderr, "libisofs_DEBUG: iso_iconv(): iso_iconv_handle not in open state\n"); return (size_t) -1; } if (handle->status & 2) { if (inbuf == NULL || outbuf == NULL) { null_buf:; if (iso_iconv_debug) fprintf(stderr, "libisofs_DEBUG: iso_iconv(): NULL buffers not allowed in shortcut mapping\n"); return (size_t) -1; } if (*inbuf == NULL || *outbuf == NULL) goto null_buf; while (*inbytesleft > 0 && *outbytesleft > 0) { *((*outbuf)++) = *((*inbuf)++); (*inbytesleft)--; (*outbytesleft)--; } if (*inbytesleft > 0 && *outbytesleft <= 0) return (size_t) -1; return (size_t) 0; } ret = iconv(handle->descr, local_inbuf, inbytesleft, outbuf, outbytesleft); if (ret == (size_t) -1) { if (iso_iconv_debug) fprintf(stderr, "libisofs_DEBUG: iconv() failed: errno= %d %s\n", errno, strerror(errno)); return (size_t) -1; } return ret; } static int iso_iconv_close(struct iso_iconv_handle *handle, int flag) { int ret; if (!(handle->status & 1)) { if (iso_iconv_debug) fprintf(stderr, "libisofs_DEBUG: iso_iconv_close(): iso_iconv_handle not in open state\n"); return -1; } handle->status &= ~1; if (handle->status & 2) return 0; ret = iconv_close(handle->descr); if (ret == -1) { if (iso_iconv_debug) fprintf(stderr, "libisofs_DEBUG: iconv_close() failed: errno= %d %s\n", errno, strerror(errno)); return -1; } return ret; } int int_pow(int base, int power) { int result = 1; while (--power >= 0) { result *= base; } return result; } /* This static variable can override the locale's charset by its getter function which should be used whenever the local character set name is to be inquired. I.e. instead of calling nl_langinfo(CODESET) directly. If the variable is empty then it forwards nl_langinfo(CODESET). */ static char libisofs_local_charset[4096]= {""}; /* API function */ int iso_set_local_charset(char *name, int flag) { if(strlen(name) >= sizeof(libisofs_local_charset)) return(0); strcpy(libisofs_local_charset, name); return 1; } /* API function */ char *iso_get_local_charset(int flag) { if(libisofs_local_charset[0]) return libisofs_local_charset; return nl_langinfo(CODESET); } int strconv(const char *str, const char *icharset, const char *ocharset, char **output) { size_t inbytes; size_t outbytes; size_t n; struct iso_iconv_handle conv; int conv_ret; char *out = NULL; char *src; char *ret; int retval; inbytes = strlen(str); outbytes = (inbytes + 1) * MB_LEN_MAX; out = calloc(outbytes, 1); if (out == NULL) { retval = ISO_OUT_OF_MEM; goto ex; } conv_ret = iso_iconv_open(&conv, (char *) ocharset, (char *) icharset, 0); if (conv_ret <= 0) { retval = ISO_CHARSET_CONV_ERROR; goto ex; } src = (char *)str; ret = (char *)out; n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); if (n == (size_t) -1) { /* error */ iso_iconv_close(&conv, 0); retval = ISO_CHARSET_CONV_ERROR; goto ex; } *ret = '\0'; iso_iconv_close(&conv, 0); *output = malloc(ret - out + 1); if (*output == NULL) { retval = ISO_OUT_OF_MEM; goto ex; } memcpy(*output, out, ret - out + 1); retval = ISO_SUCCESS; ex:; if (out != NULL) free(out); return retval; } int strnconvl(const char *str, const char *icharset, const char *ocharset, size_t len, char **output, size_t *out_len) { size_t inbytes; size_t outbytes; size_t n; struct iso_iconv_handle conv; int conv_ret; char *out = NULL; char *src; char *ret; int retval; inbytes = len; outbytes = (inbytes + 1) * MB_LEN_MAX; out = calloc(outbytes, 1); if (out == NULL) { retval = ISO_OUT_OF_MEM; goto ex; } conv_ret = iso_iconv_open(&conv, (char *) ocharset, (char *) icharset, 0); if (conv_ret <= 0) { retval = ISO_CHARSET_CONV_ERROR; goto ex; } src = (char *)str; ret = (char *)out; n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); if (n == (size_t) -1) { /* error */ iso_iconv_close(&conv, 0); retval = ISO_CHARSET_CONV_ERROR; goto ex; } *ret = '\0'; iso_iconv_close(&conv, 0); *out_len = ret - out; *output = malloc(*out_len + 1); if (*output == NULL) { retval = ISO_OUT_OF_MEM; goto ex; } memcpy(*output, out, ret - out + 1); retval = ISO_SUCCESS; ex:; if (out != NULL) free(out); return retval; } int strnconv(const char *str, const char *icharset, const char *ocharset, size_t len, char **output) { size_t l; return strnconvl(str, icharset, ocharset, len, output, &l); } /** * Convert a str in a specified codeset to WCHAR_T. * The result must be free() when no more needed * * @return * 1 success, < 0 error */ static int str2wchar(const char *icharset, const char *input, wchar_t **output) { struct iso_iconv_handle conv; int conv_ret; /* That while loop smells like a potential show stopper */ size_t loop_counter = 0, loop_limit = 3; size_t inbytes; size_t outbytes; char *ret; char *src; wchar_t *wstr; size_t n; if (icharset == NULL || input == NULL || output == NULL) { return ISO_NULL_POINTER; } conv_ret = iso_iconv_open(&conv, "WCHAR_T", (char *) icharset, 0); if (conv_ret <= 0) { return ISO_CHARSET_CONV_ERROR; } inbytes = strlen(input); loop_limit = inbytes + 3; outbytes = (inbytes + 1) * sizeof(wchar_t); /* we are sure that numchars <= inbytes */ wstr = malloc(outbytes); if (wstr == NULL) { return ISO_OUT_OF_MEM; } ret = (char *)wstr; src = (char *)input; n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); while (n == (size_t) -1) { if (errno == E2BIG) { /* error, should never occur */ goto conv_error; } else { wchar_t *wret; /* * Invalid input string charset. * This can happen if input is in fact encoded in a charset * different than icharset. * We can't do anything better than replace by "_" and continue. */ inbytes--; src++; wret = (wchar_t*) ret; *wret++ = (wchar_t) '_'; ret = (char *) wret; outbytes -= sizeof(wchar_t); if (!inbytes) break; /* Just to appease my remorse about unclear loop ends */ loop_counter++; if (loop_counter > loop_limit) goto conv_error; n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); } } iso_iconv_close(&conv, 0); *( (wchar_t *)ret )='\0'; *output = wstr; return ISO_SUCCESS; conv_error:; iso_iconv_close(&conv, 0); free(wstr); return ISO_CHARSET_CONV_ERROR; } int str2ascii(const char *icharset, const char *input, char **output) { int result; wchar_t *wsrc_ = NULL; char *ret = NULL; char *ret_ = NULL; char *src; struct iso_iconv_handle conv; int conv_ret; int direct_conv = 0; /* That while loop smells like a potential show stopper */ size_t loop_counter = 0, loop_limit = 3; /* Fallback in case that iconv() is too demanding for system */ unsigned char *cpt; size_t numchars; size_t outbytes; size_t inbytes; size_t n; if (icharset == NULL || input == NULL || output == NULL) { return ISO_NULL_POINTER; } /* First try the traditional way via intermediate character set WCHAR_T. * Up to August 2011 this was the only way. But it will not work if * there is no character set "WCHAR_T". E.g. on Solaris. */ /* convert the string to a wide character string. Note: outbytes * is in fact the number of characters in the string and doesn't * include the last NULL character. */ conv_ret = 0; result = str2wchar(icharset, input, &wsrc_); if (result == (int) ISO_SUCCESS) { src = (char *)wsrc_; numchars = wcslen(wsrc_); inbytes = numchars * sizeof(wchar_t); loop_limit = inbytes + 3; ret_ = malloc(numchars + 1); if (ret_ == NULL) { free(wsrc_); return ISO_OUT_OF_MEM; } outbytes = numchars; ret = ret_; /* initialize iconv */ conv_ret = iso_iconv_open(&conv, "ASCII", "WCHAR_T", 0); if (conv_ret <= 0) { free(wsrc_); wsrc_ = NULL; free(ret_); ret = ret_ = NULL; } } else if (result != (int) ISO_CHARSET_CONV_ERROR) return result; /* If this did not succeed : Try the untraditional direct conversion. */ if (conv_ret <= 0) { conv_ret = iso_iconv_open(&conv, "ASCII", (char *) icharset, 0); if (conv_ret <= 0) goto fallback; direct_conv = 1; src = (char *) input; inbytes = strlen(input); loop_limit = inbytes + 3; outbytes = (inbytes + 1) * sizeof(uint16_t); ret_ = malloc(outbytes); if (ret_ == NULL) return ISO_OUT_OF_MEM; ret = ret_; } n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); while (n == (size_t) -1) { /* The destination buffer is too small. Stops here. */ if (errno == E2BIG) break; /* An incomplete multi bytes sequence was found. We * can't do anything here. That's quite unlikely. */ if (errno == EINVAL) break; /* The last possible error is an invalid multi bytes * sequence. Just replace the character with a "_". * Probably the character doesn't exist in ascii like * "é, è, à, ç, ..." in French. */ *ret++ = '_'; outbytes--; if (!outbytes) break; /* There was an error with one character but some other remain * to be converted. That's probably a multibyte character. * See above comment. */ if (direct_conv) { src++; inbytes--; } else { src += sizeof(wchar_t); inbytes -= sizeof(wchar_t); } if (!inbytes) break; /* Just to appease my remorse about unclear loop ends */ loop_counter++; if (loop_counter > loop_limit) break; n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); } iso_iconv_close(&conv, 0); *ret = 0; if (wsrc_ != NULL) free(wsrc_); *output = ret_; return ISO_SUCCESS; fallback:; /* Assume to have a single byte charset with ASCII as core. Anything suspicious will be mapped to '_'. */ *output = strdup(input); for (cpt = (unsigned char *) *output; *cpt; cpt++) { if (*cpt < 32 || *cpt > 126) *cpt = '_'; } return ISO_SUCCESS; } static void set_ucsbe(uint16_t *ucs, char c) { char *v = (char*)ucs; v[0] = (char)0; v[1] = c; } /** * @return * -1, 0, 1 if *ucs <, == or > than c */ static int cmp_ucsbe(const uint16_t *ucs, char c) { char *v = (char*)ucs; if (v[0] != 0) { return 1; } else if (v[1] == c) { return 0; } else { return (uint8_t)c > (uint8_t)v[1] ? -1 : 1; } } int str2ucs(const char *icharset, const char *input, uint16_t **output) { int result; wchar_t *wsrc_ = NULL; char *src; char *ret = NULL; char *ret_ = NULL; struct iso_iconv_handle conv; int conv_ret = 0; int direct_conv = 0; /* That while loop smells like a potential show stopper */ size_t loop_counter = 0, loop_limit = 3; size_t numchars; size_t outbytes; size_t inbytes; size_t n; if (icharset == NULL || input == NULL || output == NULL) { return ISO_NULL_POINTER; } /* convert the string to a wide character string. Note: outbytes * is in fact the number of characters in the string and doesn't * include the last NULL character. */ /* First try the traditional way via intermediate character set WCHAR_T. * Up to August 2011 this was the only way. But it will not work if * there is no character set "WCHAR_T". E.g. on Solaris. */ conv_ret = 0; result = str2wchar(icharset, input, &wsrc_); if (result == (int) ISO_SUCCESS) { src = (char *)wsrc_; numchars = wcslen(wsrc_); inbytes = numchars * sizeof(wchar_t); loop_limit = inbytes + 3; ret_ = malloc((numchars+1) * sizeof(uint16_t)); if (ret_ == NULL) { free(wsrc_); return ISO_OUT_OF_MEM; } outbytes = numchars * sizeof(uint16_t); ret = ret_; /* initialize iconv */ conv_ret = iso_iconv_open(&conv, "UCS-2BE", "WCHAR_T", 0); if (conv_ret <= 0) { free(wsrc_); wsrc_ = NULL; free(ret_); ret = ret_ = NULL; } } else if (result != (int) ISO_CHARSET_CONV_ERROR) return result; /* If this did not succeed : Try the untraditional direct conversion. */ if (conv_ret <= 0) { conv_ret = iso_iconv_open(&conv, "UCS-2BE", (char *) icharset, 0); if (conv_ret <= 0) { return ISO_CHARSET_CONV_ERROR; } direct_conv = 1; src = (char *) input; inbytes = strlen(input); loop_limit = inbytes + 3; outbytes = (inbytes + 1) * sizeof(uint16_t); ret_ = malloc(outbytes); if (ret_ == NULL) return ISO_OUT_OF_MEM; ret = ret_; } n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); while (n == (size_t) -1) { /* The destination buffer is too small. Stops here. */ if (errno == E2BIG) break; /* An incomplete multi bytes sequence was found. We * can't do anything here. That's quite unlikely. */ if (errno == EINVAL) break; /* The last possible error is an invalid multi bytes * sequence. Just replace the character with a "_". * Probably the character doesn't exist in UCS */ set_ucsbe((uint16_t*) ret, '_'); ret += sizeof(uint16_t); outbytes -= sizeof(uint16_t); if (!outbytes) break; /* There was an error with one character but some other remain * to be converted. That's probably a multibyte character. * See above comment. */ if (direct_conv) { src++; inbytes--; } else { src += sizeof(wchar_t); inbytes -= sizeof(wchar_t); } if (!inbytes) break; /* Just to appease my remorse about unclear loop ends */ loop_counter++; if (loop_counter > loop_limit) break; n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); } iso_iconv_close(&conv, 0); /* close the ucs string */ set_ucsbe((uint16_t*) ret, '\0'); if (wsrc_ != NULL) free(wsrc_); *output = (uint16_t*)ret_; return ISO_SUCCESS; } int str2utf16be(const char *icharset, const char *input, uint16_t **output) { int result; wchar_t *wsrc_ = NULL; char *src; char *ret = NULL; char *ret_ = NULL; struct iso_iconv_handle conv; int conv_ret = 0; int direct_conv = 0; size_t loop_counter = 0, loop_limit = 3; size_t numchars; size_t outbytes; size_t inbytes; size_t n; if (icharset == NULL || input == NULL || output == NULL) { return ISO_NULL_POINTER; } /* Try the direct conversion. */ conv_ret = iso_iconv_open(&conv, "UTF-16BE", (char *) icharset, 0); if (conv_ret > 0) { direct_conv = 1; src = (char *) input; inbytes = strlen(input); loop_limit = inbytes + 3; outbytes = (2 * inbytes + 1) * sizeof(uint16_t); ret_ = malloc(outbytes); if (ret_ == NULL) return ISO_OUT_OF_MEM; ret = ret_; } else { /* Try via intermediate character set WCHAR_T. */ result = str2wchar(icharset, input, &wsrc_); if (result == (int) ISO_SUCCESS) { src = (char *)wsrc_; numchars = wcslen(wsrc_); inbytes = numchars * sizeof(wchar_t); loop_limit = inbytes + 3; ret_ = malloc((2 * numchars+1) * sizeof(uint16_t)); if (ret_ == NULL) { free(wsrc_); return ISO_OUT_OF_MEM; } outbytes = 2 * numchars * sizeof(uint16_t); ret = ret_; /* initialize iconv */ conv_ret = iso_iconv_open(&conv, "UTF-16BE", "WCHAR_T", 0); if (conv_ret <= 0) { free(wsrc_); free(ret_); } } else if (result != (int) ISO_CHARSET_CONV_ERROR) return result; } if (conv_ret <= 0) { return ISO_CHARSET_CONV_ERROR; } n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); while (n == (size_t) -1) { /* The destination buffer is too small. Stops here. */ if (errno == E2BIG) break; /* An incomplete multi bytes sequence was found. We * can't do anything here. That's quite unlikely. */ if (errno == EINVAL) break; /* The last possible error is an invalid multi bytes * sequence. Just replace the character with a "_". * Probably the character doesn't exist in UCS */ set_ucsbe((uint16_t*) ret, '_'); ret += sizeof(uint16_t); outbytes -= sizeof(uint16_t); if (!outbytes) break; /* There was an error with one character but some other remain * to be converted. That's probably a multibyte character. * See above comment. */ if (direct_conv) { src++; inbytes--; } else { src += sizeof(wchar_t); inbytes -= sizeof(wchar_t); } if (!inbytes) break; /* Just to appease my remorse about unclear loop ends */ loop_counter++; if (loop_counter > loop_limit) break; n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0); } iso_iconv_close(&conv, 0); /* close the UTF-16 string */ set_ucsbe((uint16_t*) ret, '\0'); if (wsrc_ != NULL) free(wsrc_); *output = (uint16_t*)ret_; return ISO_SUCCESS; } 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(uint16_t c) { return cmp_ucsbe(&c, ' ') != -1 && cmp_ucsbe(&c, '*') && cmp_ucsbe(&c, '/') && cmp_ucsbe(&c, ':') && cmp_ucsbe(&c, ';') && cmp_ucsbe(&c, '?') && cmp_ucsbe(&c, '\\'); } /* @param relaxed bit0+1 0= strict ECMA-119 1= additionally allow lowercase (else map to upper) 2= allow all 8-bit characters bit2 allow all 7-bit characters (but map to upper if not bit0+1 == 2) */ static char map_fileid_char(char c, int relaxed) { char upper; if (c == '/') /* Allowing slashes would cause lots of confusion */ return '_'; if ((relaxed & 3) == 2) return c; if (valid_d_char(c)) return c; if ((relaxed & 4) && (c & 0x7f) == c && (c < 'a' || c > 'z')) return c; upper= toupper(c); if (valid_d_char(upper)) { if (relaxed & 3) { /* lower chars are allowed */ return c; } return upper; } return '_'; } static char *iso_dirid(const char *src, int size, int relaxed) { size_t len, i; char name[32]; len = strlen(src); if ((int) len > size) { len = size; } for (i = 0; i < len; i++) { #ifdef Libisofs_old_ecma119_nameS char c= toupper(src[i]); name[i] = valid_d_char(c) ? c : '_'; #else /* Libisofs_old_ecma119_nameS */ name[i] = map_fileid_char(src[i], relaxed); #endif /* ! Libisofs_old_ecma119_nameS */ } name[len] = '\0'; return strdup(name); } char *iso_1_dirid(const char *src, int relaxed) { return iso_dirid(src, 8, relaxed); } char *iso_2_dirid(const char *src) { return iso_dirid(src, 31, 0); } char *iso_1_fileid(const char *src, int relaxed, int force_dots) { char *dot; /* Position of the last dot in the filename, will be used * to calculate lname and lext. */ int lname, lext, pos, i; char dest[13]; /* 13 = 8 (name) + 1 (.) + 3 (ext) + 1 (\0) */ if (src == NULL) { return NULL; } dot = strrchr(src, '.'); if (dot == src && strlen(src) > 4) dot = NULL; /* Use the long extension instead of the empty name */ lext = dot ? strlen(dot + 1) : 0; lname = strlen(src) - lext - (dot ? 1 : 0); /* If we can't build a filename, return NULL. */ if (lname == 0 && lext == 0) { return NULL; } pos = 0; /* Convert up to 8 characters of the filename. */ for (i = 0; i < lname && i < 8; i++) { #ifdef Libisofs_old_ecma119_nameS char c= toupper(src[i]); dest[pos++] = valid_d_char(c) ? c : '_'; #else /* Libisofs_old_ecma119_nameS */ if (dot == NULL && src[i] == '.') dest[pos++] = '_'; /* make sure that ignored dots do not appear */ else dest[pos++] = map_fileid_char(src[i], relaxed); #endif /* ! Libisofs_old_ecma119_nameS */ } /* This dot is mandatory, even if there is no extension. */ if (force_dots || lext > 0) dest[pos++] = '.'; /* Convert up to 3 characters of the extension, if any. */ for (i = 0; i < lext && i < 3; i++) { #ifdef Libisofs_old_ecma119_nameS char c= toupper(src[lname + 1 + i]); dest[pos++] = valid_d_char(c) ? c : '_'; #else /* Libisofs_old_ecma119_nameS */ dest[pos++] = map_fileid_char(src[lname + 1 + i], relaxed); #endif /* ! Libisofs_old_ecma119_nameS */ } dest[pos] = '\0'; return strdup(dest); } char *iso_2_fileid(const char *src) { char *dot; int lname, lext, lnname, lnext, pos, i; char dest[32]; /* 32 = 30 (name + ext) + 1 (.) + 1 (\0) */ if (src == NULL) { return NULL; } dot = strrchr(src, '.'); /* * Since the maximum length can be divided freely over the name and * extension, we need to calculate their new lengths (lnname and * lnext). If the original filename is too long, we start by trimming * the extension, but keep a minimum extension length of 3. */ if (dot == NULL || *(dot + 1) == '\0') { lname = strlen(src); lnname = (lname > 30) ? 30 : lname; lext = lnext = 0; } else { lext = strlen(dot + 1); lname = strlen(src) - lext - 1; lnext = (strlen(src) > 31 && lext > 3) ? (lname < 27 ? 30 - lname : 3) : lext; lnname = (strlen(src) > 31) ? 30 - lnext : lname; } if (lnname == 0 && lnext == 0) { return NULL; } pos = 0; /* Convert up to lnname characters of the filename. */ for (i = 0; i < lnname; i++) { char c= toupper(src[i]); dest[pos++] = valid_d_char(c) ? c : '_'; } dest[pos++] = '.'; /* Convert up to lnext characters of the extension, if any. */ for (i = 0; i < lnext; i++) { char c= toupper(src[lname + 1 + i]); dest[pos++] = valid_d_char(c) ? c : '_'; } dest[pos] = '\0'; return strdup(dest); } /** * Create a dir name suitable for an ISO image with relaxed constraints. * * @param size * Max len for the name * @param relaxed * bit0+1: 0 only allow d-characters, * 1 allow also lowe case chars, * 2 allow all 8-bit characters, * bit2: allow 7-bit characters (but map lowercase to uppercase if * not bit0+1 == 2) */ char *iso_r_dirid(const char *src, int size, int relaxed) { size_t len, i; char *dest; len = strlen(src); if ((int) len > size) { len = size; } dest = malloc(len + 1); if (dest == NULL) return NULL; for (i = 0; i < len; i++) { #ifdef Libisofs_old_ecma119_nameS char c= src[i]; if (relaxed == 2) { /* all chars are allowed */ dest[i] = c; } else if (valid_d_char(c)) { /* it is a valid char */ dest[i] = c; } else { c= toupper(src[i]); if (valid_d_char(c)) { if (relaxed) { /* lower chars are allowed */ dest[i] = src[i]; } else { dest[i] = c; } } else { dest[i] = '_'; } } #else /* Libisofs_old_ecma119_nameS */ dest[i] = map_fileid_char(src[i], relaxed); #endif /* ! Libisofs_old_ecma119_nameS */ } dest[len] = '\0'; return dest; } /** * Create a file name suitable for an ISO image with level > 1 and * with relaxed constraints. * * @param len * Max len for the name, without taken the "." into account. * @param relaxed * bit0+1: 0 only allow d-characters, * 1 allow also lowe case chars, * 2 allow all 8-bit characters, * bit2: allow 7-bit characters (but map lowercase to uppercase if * not bit0+1 == 2) * @param forcedot * Whether to ensure that "." is added */ char *iso_r_fileid(const char *src, size_t len, int relaxed, int forcedot) { char *dot, *retval = NULL; int lname, lext, lnname, lnext, pos, i; char *dest = NULL; dest = calloc(len + 1 + 1, 1); if (dest == NULL) goto ex; if (src == NULL) { goto ex; } dot = strrchr(src, '.'); /* * Since the maximum length can be divided freely over the name and * extension, we need to calculate their new lengths (lnname and * lnext). If the original filename is too long, we start by trimming * the extension, but keep a minimum extension length of 3. */ if (dot == NULL || *(dot + 1) == '\0') { lname = strlen(src); lnname = (lname > (int) len) ? (int) len : lname; lext = lnext = 0; } else { lext = strlen(dot + 1); lname = strlen(src) - lext - 1; lnext = (strlen(src) > len + 1 && lext > 3) ? (lname < (int) len - 3 ? (int) len - lname : 3) : lext; lnname = (strlen(src) > len + 1) ? (int) len - lnext : lname; } if (lnname == 0 && lnext == 0) { goto ex; } pos = 0; /* Convert up to lnname characters of the filename. */ for (i = 0; i < lnname; i++) { #ifdef Libisofs_old_ecma119_nameS char c= src[i]; if (relaxed == 2) { /* all chars are allowed */ dest[pos++] = c; } else if (valid_d_char(c)) { /* it is a valid char */ dest[pos++] = c; } else { c= toupper(src[i]); if (valid_d_char(c)) { if (relaxed) { /* lower chars are allowed */ dest[pos++] = src[i]; } else { dest[pos++] = c; } } else { dest[pos++] = '_'; } } #else /* Libisofs_old_ecma119_nameS */ dest[pos++] = map_fileid_char(src[i], relaxed); #endif /* ! Libisofs_old_ecma119_nameS */ } if (lnext > 0 || forcedot) { dest[pos++] = '.'; } /* Convert up to lnext characters of the extension, if any. */ for (i = lname + 1; i < lname + 1 + lnext; i++) { #ifdef Libisofs_old_ecma119_nameS char c= src[i]; if (relaxed == 2) { /* all chars are allowed */ dest[pos++] = c; } else if (valid_d_char(c)) { /* it is a valid char */ dest[pos++] = c; } else { c= toupper(src[i]); if (valid_d_char(c)) { if (relaxed) { /* lower chars are allowed */ dest[pos++] = src[i]; } else { dest[pos++] = c; } } else { dest[pos++] = '_'; } } #else /* Libisofs_old_ecma119_nameS */ dest[pos++] = map_fileid_char(src[i], relaxed); #endif /* ! Libisofs_old_ecma119_nameS */ } dest[pos] = '\0'; retval = strdup(dest); ex:; if (dest != NULL) free(dest); return retval; } /* bit0= no_force_dots bit1= allow 103 characters rather than 64 */ uint16_t *iso_j_file_id(const uint16_t *src, int flag) { uint16_t *dot, *retval = NULL; size_t lname, lext, lnname, lnext, pos, i, maxchar = 64; uint16_t *dest = NULL, c; LIBISO_ALLOC_MEM_VOID(dest, uint16_t, LIBISO_JOLIET_NAME_MAX); /* was: 66 = 64 (name + ext) + 1 (.) + 1 (\0) */ if (src == NULL) { goto ex; } if (flag & 2) maxchar = 103; dot = ucsrchr(src, '.'); /* * Since the maximum length can be divided freely over the name and * extension, we need to calculate their new lengths (lnname and * lnext). If the original filename is too long, we start by trimming * the extension, but keep a minimum extension length of 3. */ if (dot == NULL || cmp_ucsbe(dot + 1, '\0') == 0) { lname = ucslen(src); lnname = (lname > maxchar) ? maxchar : lname; lext = lnext = 0; } else { lext = ucslen(dot + 1); lname = ucslen(src) - lext - 1; lnext = (ucslen(src) > maxchar + 1 && lext > 3) ? (lname < maxchar - 3 ? maxchar - lname : 3) : lext; lnname = (ucslen(src) > maxchar + 1) ? maxchar - lnext : lname; } if (lnname == 0 && lnext == 0) { goto ex; } pos = 0; /* Convert up to lnname characters of the filename. */ for (i = 0; i < lnname; i++) { c = src[i]; if (valid_j_char(c)) { dest[pos++] = c; } else { set_ucsbe(dest + pos, '_'); pos++; } } if (pos > 0) iso_handle_split_utf16(dest + (pos - 1)); if ((flag & 1) && lnext <= 0) goto is_done; set_ucsbe(dest + pos, '.'); pos++; /* Convert up to lnext characters of the extension, if any. */ for (i = 0; i < lnext; i++) { uint16_t c = src[lname + 1 + i]; if (valid_j_char(c)) { dest[pos++] = c; } else { set_ucsbe(dest + pos, '_'); pos++; } } iso_handle_split_utf16(dest + (pos - 1)); is_done:; set_ucsbe(dest + pos, '\0'); retval = ucsdup(dest); ex:; LIBISO_FREE_MEM(dest); return retval; } /* @param flag bit1= allow 103 characters rather than 64 */ uint16_t *iso_j_dir_id(const uint16_t *src, int flag) { size_t len, i, maxchar = 64; uint16_t *dest = NULL, *retval = NULL; /* was: 65 = 64 + 1 (\0) */ LIBISO_ALLOC_MEM_VOID(dest, uint16_t, LIBISO_JOLIET_NAME_MAX); if (src == NULL) { goto ex; } if (flag & 2) maxchar = 103; len = ucslen(src); if (len > maxchar) { len = maxchar; } for (i = 0; i < len; i++) { uint16_t c = src[i]; if (valid_j_char(c)) { dest[i] = c; } else { set_ucsbe(dest + i, '_'); } } iso_handle_split_utf16(dest + (len - 1)); set_ucsbe(dest + len, '\0'); retval = ucsdup(dest); ex: LIBISO_FREE_MEM(dest); return retval; } size_t ucslen(const uint16_t *str) { size_t i; for (i = 0; str[i]; i++) ; return i; } uint16_t *ucsrchr(const uint16_t *str, char c) { size_t len = ucslen(str); while (len-- > 0) { if (cmp_ucsbe(str + len, c) == 0) { return (uint16_t*)(str + len); } } return NULL; } uint16_t *ucsdup(const uint16_t *str) { uint16_t *ret; size_t len = ucslen(str); ret = malloc(2 * (len + 1)); if (ret == NULL) return NULL; if (ret != NULL) { memcpy(ret, str, 2 * (len + 1)); } return ret; } /** * Although each character is 2 bytes, we actually compare byte-by-byte * because the words are big-endian. Comparing possibly swapped words * would make the sorting order depend on the machine byte order. */ int ucscmp(const uint16_t *s1, const uint16_t *s2) { const uint8_t *s = (const uint8_t*)s1; const uint8_t *t = (const uint8_t*)s2; size_t len1 = ucslen(s1); size_t len2 = ucslen(s2); size_t i, len = MIN(len1, len2) * 2; for (i = 0; i < len; i++) { if (s[i] < t[i]) { return -1; } else if (s[i] > t[i]) { return 1; } } if (len1 < len2) return -1; else if (len1 > len2) return 1; return 0; } uint16_t *ucscpy(uint16_t *dest, const uint16_t *src) { size_t n = ucslen(src) + 1; memcpy(dest, src, n*2); return dest; } uint16_t *ucsncpy(uint16_t *dest, const uint16_t *src, size_t n) { n = MIN(n, ucslen(src) + 1); memcpy(dest, src, n*2); if (n >= 2) iso_handle_split_utf16(dest + (n - 2)); return dest; } int str2d_char(const char *icharset, const char *input, char **output) { int ret; char *ascii; size_t len, i; if (output == NULL) { return ISO_OUT_OF_MEM; } /** allow NULL input */ if (input == NULL) { *output = NULL; return 0; } /* this checks for NULL parameters */ ret = str2ascii(icharset, input, &ascii); if (ret < 0) { *output = NULL; return ret; } len = strlen(ascii); for (i = 0; i < len; ++i) { char c= toupper(ascii[i]); ascii[i] = valid_d_char(c) ? c : '_'; } *output = ascii; return ISO_SUCCESS; } int str2a_char(const char *icharset, const char *input, char **output) { int ret; char *ascii; size_t len, i; if (output == NULL) { return ISO_OUT_OF_MEM; } /** allow NULL input */ if (input == NULL) { *output = NULL; return 0; } /* this checks for NULL parameters */ ret = str2ascii(icharset, input, &ascii); if (ret < 0) { *output = NULL; return ret; } len = strlen(ascii); for (i = 0; i < len; ++i) { char c= toupper(ascii[i]); ascii[i] = valid_a_char(c) ? c : '_'; } *output = ascii; return ISO_SUCCESS; } void iso_lsb(uint8_t *buf, uint32_t num, int bytes) { int i; for (i = 0; i < bytes; ++i) buf[i] = (num >> (8 * i)) & 0xff; } void iso_lsb64(uint8_t *buf, uint64_t num) { int i; for (i = 0; i < 8; ++i) buf[i] = (num >> (8 * i)) & 0xff; } void iso_msb(uint8_t *buf, uint32_t num, int bytes) { int i; for (i = 0; i < bytes; ++i) buf[bytes - 1 - i] = (num >> (8 * i)) & 0xff; } void iso_bb(uint8_t *buf, uint32_t num, int bytes) { iso_lsb(buf, num, bytes); iso_msb(buf+bytes, num, bytes); } /* An alternative to iso_lsb() which advances the write pointer */ int iso_lsb_to_buf(char **wpt, uint32_t value, int bytes, int flag) { int b, bits; bits = bytes * 8; for (b = 0; b < bits; b += 8) *((unsigned char *) ((*wpt)++)) = (value >> b) & 0xff; return (1); } uint32_t iso_read_lsb(const uint8_t *buf, int bytes) { int i; uint32_t ret = 0; for (i=0; i 52 || tzoffset < -48 || always_gmt) { /* absurd timezone offset, represent time in GMT */ gmtime_r(&t, &tm); tzoffset = 0; } if (tm.tm_year < 0) { tm.tm_year = 0; tm.tm_mon = 0; tm.tm_mday = 1; tm.tm_hour = 0; tm.tm_min = 0; tm.tm_sec = 0; } else if (tm.tm_year > 255) { tm.tm_year = 255; tm.tm_mon = 11; tm.tm_mday = 31; tm.tm_hour = 23; tm.tm_min = 59; tm.tm_sec = 59; } buf[0] = tm.tm_year; buf[1] = tm.tm_mon + 1; buf[2] = tm.tm_mday; buf[3] = tm.tm_hour; buf[4] = tm.tm_min; buf[5] = tm.tm_sec; buf[6] = tzoffset; } void iso_datetime_17(unsigned char *buf, time_t t, int always_gmt) { static int tzsetup = 0; static int tzoffset; struct tm tm; if (t == (time_t) - 1) { /* unspecified time */ memset(buf, '0', 16); buf[16] = 0; return; } if (!tzsetup) { tzset(); tzsetup = 1; } memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; /* some OSes change tm_isdst only if it is -1 */ localtime_r(&t, &tm); localtime_r(&t, &tm); #ifdef HAVE_TM_GMTOFF tzoffset = tm.tm_gmtoff / 60 / 15; #else if (tm.tm_isdst < 0) tm.tm_isdst = 0; #ifndef Libburnia_timezonE #define Libburnia_timezonE timezone #endif #if Libburnia_timezonE == 0 always_gmt = 1; #endif tzoffset = ( - Libburnia_timezonE / 60 / 15 ) + 4 * tm.tm_isdst; #endif /* ! HAVE_TM_GMTOFF */ if (tzoffset > 52 || tzoffset < -48 || always_gmt) { /* absurd timezone offset, represent time in GMT */ gmtime_r(&t, &tm); tzoffset = 0; } if (tm.tm_year <= -1900) { strcpy((char *) buf, "00010101000000"); } else if (tm.tm_year >= 8100) { strcpy((char *) buf, "99991231235959"); } else { sprintf((char*)&buf[0], "%04d", tm.tm_year + 1900); sprintf((char*)&buf[4], "%02d", tm.tm_mon + 1); sprintf((char*)&buf[6], "%02d", tm.tm_mday); sprintf((char*)&buf[8], "%02d", tm.tm_hour); sprintf((char*)&buf[10], "%02d", tm.tm_min); sprintf((char*)&buf[12], "%02d", MIN(59, tm.tm_sec)); } memcpy(&buf[14], "00", 2); buf[16] = tzoffset; } #ifndef HAVE_TIMEGM /* putenv is SVr4, POSIX.1-2001, 4.3BSD , setenv is 4.3BSD, POSIX.1-2001. So putenv is more widely available. Also, setenv spoils eventual putenv expectation of applications because putenv installs the original string which then may be altered from its owner. setenv installs a copy that may not be altered. Both are slow. Thus first try with a naive implementation that assumes no leap seconds. If it fails a test with gmtime() then use the slow function with mktime(). */ #define Libisofs_use_putenV yes static time_t env_timegm(struct tm *tm) { time_t ret; char *tz; #ifdef Libisofs_use_putenV static char unset_name[] = {"TZ"}; tz = getenv("TZ"); putenv("TZ="); tzset(); ret = mktime(tm); if (tz != NULL) { /* tz is a pointer to the value part in a string of form "TZ="value */ putenv(tz - 3); } else putenv(unset_name); /* not daring to submit constant */ tzset(); #else /* Libisofs_use_putenV */ tz = getenv("TZ"); setenv("TZ", "", 1); tzset(); ret = mktime(tm); if (tz) setenv("TZ", tz, 1); else unsetenv("TZ"); tzset(); #endif /* ! Libisofs_use_putenV */ return ret; } static int ts_is_leapyear(int tm_year) /* years since 1900 */ { return ((tm_year % 4) == 0 && ((tm_year % 100) != 0 || (tm_year % 400) == 100)); } /* Fast implementation without leap seconds. Inspired by but not copied from code by Kungliga Tekniska Hgskolan (Royal Institute of Technology, Stockholm, Sweden), which was modified by Andrew Tridgell for Samba4. I claim own copyright 2011 Thomas Schmitt . */ static time_t ts_timegm(struct tm *tm) { time_t ret; static int month_length_normal[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; static int month_length_leap[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int *month_length_pt; int years, i; ret = 0; years = tm->tm_year - 70; /* Years since 1970 */ if (years < 0) return ret; for (i = 0; i < years; i++) { ret += 365 * 86400; if (ts_is_leapyear(70 + i)) ret += 86400; } if (ts_is_leapyear(tm->tm_year)) month_length_pt = month_length_leap; else month_length_pt = month_length_normal; for (i = 0; i < tm->tm_mon; i++) ret += month_length_pt[i] * 86400; ret += (tm->tm_mday - 1) * 86400; ret += tm->tm_hour * 3600; ret += tm->tm_min * 60; ret += tm->tm_sec; return ret; } static time_t timegm(struct tm *tm) { time_t raw_t, ret; struct tm *test_tm, input_tm_copy; /* Beware of ill effects if tm is result of gmtime() or alike */ memcpy(&input_tm_copy, tm, sizeof(struct tm)); /* Try without leapseconds (which are rarely implemented, as it seems) */ raw_t = ts_timegm(tm); if (raw_t == 0) return raw_t; /* Check whether this translates back to the input values */ test_tm = gmtime(&raw_t); if (input_tm_copy.tm_sec == test_tm->tm_sec && input_tm_copy.tm_min == test_tm->tm_min && input_tm_copy.tm_hour == test_tm->tm_hour && input_tm_copy.tm_mday == test_tm->tm_mday && input_tm_copy.tm_mon == test_tm->tm_mon && input_tm_copy.tm_year == test_tm->tm_year) { ret = raw_t; } else { /* Mismatch. Use slow method around mktime() */ ret = env_timegm(&input_tm_copy); } return ret; } #endif /* ! HAVE_TIMEGM */ 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]; tm.tm_isdst = 0; return timegm(&tm) - ((int8_t)buf[6]) * 60 * 15; } time_t iso_datetime_read_17(const uint8_t *buf) { struct tm tm; sscanf((char*)&buf[0], "%4d", &tm.tm_year); sscanf((char*)&buf[4], "%2d", &tm.tm_mon); sscanf((char*)&buf[6], "%2d", &tm.tm_mday); sscanf((char*)&buf[8], "%2d", &tm.tm_hour); sscanf((char*)&buf[10], "%2d", &tm.tm_min); sscanf((char*)&buf[12], "%2d", &tm.tm_sec); tm.tm_year -= 1900; tm.tm_mon -= 1; tm.tm_isdst = 0; return timegm(&tm) - ((int8_t)buf[16]) * 60 * 15; } /** * Check whether the caller process has read access to the given local file. * * @return * 1 on success (i.e, the process has read access), < 0 on error * (including ISO_FILE_ACCESS_DENIED on access denied to the specified file * or any directory on the path). */ int iso_eaccess(const char *path) { int access; /* use non standard eaccess when available, open() otherwise */ #ifdef HAVE_EACCESS access = !eaccess(path, R_OK); #else int fd = open(path, O_RDONLY); if (fd != -1) { close(fd); access = 1; } else { access = 0; } #endif if (!access) { int err; /* error, choose an appropriate return code */ switch (errno) { case EACCES: err = ISO_FILE_ACCESS_DENIED; break; case ENOTDIR: case ENAMETOOLONG: case ELOOP: err = ISO_FILE_BAD_PATH; break; case ENOENT: err = ISO_FILE_DOESNT_EXIST; break; case EFAULT: case ENOMEM: err = ISO_OUT_OF_MEM; break; default: err = ISO_FILE_ERROR; break; } return err; } return ISO_SUCCESS; } char *iso_util_strcopy(const char *buf, size_t len) { char *str; str = calloc(len + 1, 1); if (str == NULL) { return NULL; } strncpy(str, buf, len); str[len] = '\0'; return str; } char *iso_util_strcopy_untail(const char *buf, size_t len_in) { char *str; int len; str = iso_util_strcopy(buf, len_in); if (str == NULL) { return NULL; } /* remove trailing spaces */ for (len = len_in - 1; len >= 0; --len) { if (str[len] != ' ') break; str[len] = 0; } return str; } /** * Copy up to \p max characters from \p src to \p dest. If \p src has less than * \p max characters, we pad dest with " " characters. */ void strncpy_pad(char *dest, const char *src, size_t max) { size_t len, i; if (src != NULL) { len = MIN(strlen(src), max); for (i = 0; i < len; ++i) dest[i] = src[i]; } else { len = 0; } for (i = len; i < max; ++i) dest[i] = ' '; } char *ucs2str(const char *buf, size_t len) { size_t outbytes, inbytes; char *str, *src, *out = NULL, *retval = NULL; struct iso_iconv_handle conv; int conv_ret; size_t n; inbytes = len; outbytes = (inbytes+1) * MB_LEN_MAX; /* ensure enough space */ out = calloc(outbytes, 1); if (out == NULL) return NULL; /* convert to local charset */ conv_ret = iso_iconv_open(&conv, iso_get_local_charset(0), "UCS-2BE", 0); if (conv_ret <= 0) { goto ex; } src = (char *)buf; str = (char *)out; n = iso_iconv(&conv, &src, &inbytes, &str, &outbytes, 0); iso_iconv_close(&conv, 0); if (n == (size_t) -1) { /* error */ goto ex; } *str = '\0'; /* remove trailing spaces */ for (len = strlen(out) - 1; out[len] == ' ' && len > 0; --len) out[len] = '\0'; retval = strdup(out); ex:; if (out != NULL) free(out); return retval; } void iso_lib_version(int *major, int *minor, int *micro) { *major = iso_lib_header_version_major; *minor = iso_lib_header_version_minor; *micro = iso_lib_header_version_micro; /* No more: values from version.h generated from version.h.in and macro values defined in configure.ac *major = LIBISOFS_MAJOR_VERSION; *minor = LIBISOFS_MINOR_VERSION; *micro = LIBISOFS_MICRO_VERSION; */ } int iso_lib_is_compatible(int major, int minor, int micro) { int cmajor, cminor, cmicro; /* for now, the rule is that library is compatible if requested * version is lower */ iso_lib_version(&cmajor, &cminor, &cmicro); return cmajor > major || (cmajor == major && (cminor > minor || (cminor == minor && cmicro >= micro))); } int iso_init_locale(int flag) { setlocale(LC_CTYPE, ""); return 1; } int iso_util_encode_len_bytes(uint32_t data, char *buffer, int data_len, int *result_len, int flag) { uint32_t x; int i, l; char *wpt = buffer; if (data_len <= 0) { x = data; for (i = 0; i < 4 && x != 0; i++) x = x >> 8; l = i; if (l == 0) l = 1; } else l = data_len; *((unsigned char *) (wpt++)) = l; for (i = 0; i < l; i++) *((unsigned char *) (wpt++)) = data >> (8 * (l - i - 1)); *result_len = l + 1; return ISO_SUCCESS; } int iso_util_decode_len_bytes(uint32_t *data, char *buffer, int *data_len, int buffer_len, int flag) { int i; *data = 0; *data_len = ((unsigned char *) buffer)[0]; if (*data_len > buffer_len - 1) *data_len = buffer_len - 1; for (i = 1; i <= *data_len; i++) *data = (*data << 8) | ((unsigned char *) buffer)[i]; return ISO_SUCCESS; } int iso_util_dec_to_uint32(char *dec, uint32_t *value, int flag) { double num; sscanf(dec, "%lf", &num); if (num < 0 || num > 4294967295.0) return 0; *value = num; return 1; } int iso_util_bin_to_hex(char *target, uint8_t *bytes, int num_bytes, int flag) { int i; for (i = 0; i < num_bytes; i++) sprintf(target + 2 * i, "%-2.2x", bytes[i]); target[2 * num_bytes] = 0; return 1; } int iso_util_hex_to_bin(char *hex, char *bin, int bin_size, int *bin_count, int flag) { static char *allowed = {"0123456789ABCDEFabcdef"}; char b[3]; int i; unsigned int u; b[2] = 0; *bin_count = 0; for (i = 0; i < bin_size; i++) { b[0] = hex[2 * i]; b[1] = hex[2 * i + 1]; if (strchr(allowed, b[0]) == NULL || strchr(allowed, b[1]) == NULL) break; sscanf(b, "%x", &u); ((unsigned char *) bin)[i] = u; (*bin_count)++; } return (*bin_count > 0); } int iso_util_tag_magic(int tag_type, char **tag_magic, int *len, int flag) { static char *magic[] = {"", "libisofs_checksum_tag_v1", "libisofs_sb_checksum_tag_v1", "libisofs_tree_checksum_tag_v1", "libisofs_rlsb32_checksum_tag_v1"}; static int magic_len[]= {0, 24, 27, 29, 31}; static int magic_max = 4; *tag_magic = NULL; *len = 0; if (tag_type < 0 || tag_type > magic_max) return ISO_WRONG_ARG_VALUE; *tag_magic = magic[tag_type]; *len = magic_len[tag_type]; return magic_max; } int iso_util_decode_md5_tag(char data[2048], int *tag_type, uint32_t *pos, uint32_t *range_start, uint32_t *range_size, uint32_t *next_tag, char md5[16], int flag) { int ret, bin_count, i, mode, magic_first = 1, magic_last = 4; int magic_len = 0; char *cpt, self_md5[16], tag_md5[16], *tag_magic; void *ctx = NULL; *next_tag = 0; mode = flag & 255; if (mode > magic_last) return ISO_WRONG_ARG_VALUE; if (mode > 0) magic_first = magic_last = mode; for (i = magic_first; i <= magic_last; i++) { iso_util_tag_magic(i, &tag_magic, &magic_len, 0); if (strncmp(data, tag_magic, magic_len) == 0) break; } if (i > magic_last ) return 0; *tag_type = i; cpt = data + magic_len + 1; if (strncmp(cpt, "pos=", 4) != 0) return 0; cpt+= 4; ret = iso_util_dec_to_uint32(cpt, pos, 0); if (ret <= 0) return 0; cpt = strstr(cpt, "range_start="); if (cpt == NULL) return(0); ret = iso_util_dec_to_uint32(cpt + 12, range_start, 0); if (ret <= 0) return 0; cpt = strstr(cpt, "range_size="); if (cpt == NULL) return(0); ret = iso_util_dec_to_uint32(cpt + 11, range_size, 0); if (ret <= 0) return 0; if (*tag_type == 2 || *tag_type == 3) { cpt = strstr(cpt, "next="); if (cpt == NULL) return(0); ret = iso_util_dec_to_uint32(cpt + 5, next_tag, 0); if (ret <= 0) return 0; } else if (*tag_type == 4) { cpt = strstr(cpt, "session_start="); if (cpt == NULL) return(0); ret = iso_util_dec_to_uint32(cpt + 14, next_tag, 0); if (ret <= 0) return 0; } cpt = strstr(cpt, "md5="); if (cpt == NULL) return(0); ret = iso_util_hex_to_bin(cpt + 4, md5, 16, &bin_count, 0); if (ret <= 0 || bin_count != 16) return 0; cpt += 4 + 32; ret = iso_md5_start(&ctx); if (ret < 0) return ret; iso_md5_compute(ctx, data , cpt - data); iso_md5_end(&ctx, tag_md5); cpt = strstr(cpt, "self="); if (cpt == NULL) return(0); ret = iso_util_hex_to_bin(cpt + 5, self_md5, 16, &bin_count, 0); if (ret <= 0 || bin_count != 16) return 0; for(i= 0; i < 16; i++) if(self_md5[i] != tag_md5[i]) return ISO_MD5_AREA_CORRUPTED; if (*(cpt + 5 + 32) != '\n') return 0; return(1); } int iso_util_eval_md5_tag(char *block, int desired, uint32_t lba, void *ctx, uint32_t ctx_start_lba, int *tag_type, uint32_t *next_tag, int flag) { int decode_ret, ret; char md5[16], cloned_md5[16]; uint32_t pos, range_start, range_size; void *cloned_ctx = NULL; *tag_type = 0; decode_ret = iso_util_decode_md5_tag(block, tag_type, &pos, &range_start, &range_size, next_tag, md5, 0); if (decode_ret != 1 && decode_ret != (int) ISO_MD5_AREA_CORRUPTED) return 0; if (*tag_type > 30) goto unexpected_type; if (decode_ret == (int) ISO_MD5_AREA_CORRUPTED) { ret = decode_ret; goto ex; } else if (!((1 << *tag_type) & desired)) { unexpected_type:; iso_msg_submit(-1, ISO_MD5_TAG_UNEXPECTED, 0, NULL); ret = 0; goto ex; } else if (pos != lba) { if (*tag_type == 2) { /* Superblock tag */ if (lba < 32) { /* Check whether this is a copied superblock */ range_start -= (off_t) pos - (off_t) lba; if (range_start != ctx_start_lba) { /* >>> check for matching MD5 ? */; ret = ISO_MD5_TAG_MISPLACED; } else ret = ISO_MD5_TAG_COPIED; goto ex; } } ret = ISO_MD5_TAG_MISPLACED; goto ex; } else if (range_start != ctx_start_lba) { ret = ISO_MD5_TAG_MISPLACED; goto ex; } ret = iso_md5_clone(ctx, &cloned_ctx); if (ret < 0) goto ex; iso_md5_end(&cloned_ctx, cloned_md5); if (! iso_md5_match(cloned_md5, md5)) { ret = ISO_MD5_TAG_MISMATCH; goto ex; } ret = 1; ex:; if (ret < 0) iso_msg_submit(-1, ret, 0, NULL); return ret; } void *iso_alloc_mem(size_t size, size_t count, int flag) { void *pt; pt = calloc(size, count); if(pt == NULL) iso_msg_submit(-1, ISO_OUT_OF_MEM, 0, "Out of virtual memory"); return pt; } uint16_t iso_ntohs(uint16_t v) { return iso_read_msb((uint8_t *) &v, 2); } uint16_t iso_htons(uint16_t v) { uint16_t ret; iso_msb((uint8_t *) &ret, (uint32_t) v, 2); return ret; } /* If an UTF-16 surrogate pair was split : Change to UTF-16 '_'. (UCS-2 is promised to reserve 0xd800 to 0xdbff for UTF-16). */ void iso_handle_split_utf16(uint16_t *utf_word) { unsigned char *hb; hb = (unsigned char *) utf_word; if ((hb[0] & 0xfc) == 0xd8) set_ucsbe(utf_word, '_'); } int iso_clone_mem(char *in, char **out, size_t size) { if (in == NULL) { *out = NULL; return 1; } if (size == 0) size = strlen(in) + 1; *out = calloc(1, size); if (*out == NULL) return ISO_OUT_OF_MEM; memcpy(*out, in, size); return ISO_SUCCESS; } int iso_clone_mgtd_mem(char *in, char **out, size_t size) { if (*out != NULL) free(*out); return iso_clone_mem(in, out, size); } /** Convert a text into a number of type double and multiply it by unit code [kmgt] (2^10 to 2^40) or [s] (2048) or [d] (512). (Also accepts capital letters.) @param text Input like "42", "223062s", "3m" or "-1g" @param flag Bitfield for control purposes: bit0= return -1 rather than 0 on failure bit1= if scaled then compute the last byte of the last unit @return The derived value */ off_t iso_scanf_io_size(char *text, int flag) { int c; off_t ret = 0, fac = 1; char *rpt; for (rpt = text; *rpt >= '0' && *rpt <= '9'; rpt++) ret = ret * 10 + (*rpt - '0'); if (rpt == text) return (off_t) (flag & 1 ? -1 : 0); c = *rpt; if (c=='k' || c=='K') fac = 1024; else if (c=='m' || c=='M') fac = 1024 * 1024; else if (c=='g' || c=='G') fac = 1024 * 1024 * 1024; else if (c=='t' || c=='T') fac = ((off_t) 1024) * 1024 * 1024 * 1024; else if (c=='s' || c=='S') fac = 2048; else if (c=='d' || c=='D') fac = 512; ret *= fac; if (flag & 2) ret += fac - 1; return ret; } /* Find backward from idx the start byte of a possible UTF-8 character. https://en.wikipedia.org/wiki/UTF-8#Description */ static int find_utf8_start(char *name, int idx, int flag) { unsigned char *uname, uch; int i; uname= (unsigned char *) name; if ((uname[idx] & 0xc0) != 0x80) return idx; /* not an UTF-8 tail byte */ for (i = 0; i < 5 && idx - 1 - i >= 0; i++) { /* up to deprecated 6-byte codes */ uch = uname[idx - 1 - i]; if ((uch & 0xe0) == 0xc0 || (uch & 0xf0) == 0xe0 || (uch & 0xf8) == 0xf0 || (uch & 0xfc) == 0xf8 || (uch & 0xfe) == 0xfc) return (idx - 1 - i); /* UTF-8 start byte found */ if ((uch & 0xc0) != 0x80) return idx; /* not an UTF-8 tail byte, so no UTF-8 */ } return idx; /* no UTF-8 start found */ } /* @param flag bit0= do not issue warning message */ int iso_truncate_rr_name(int truncate_mode, int truncate_length, char *name, int flag) { int neck, goal, ret, l, i; static int hash_size = 32; void *ctx = NULL; char hashval[16]; l = strlen(name); if (l <= truncate_length) return ISO_SUCCESS; if (truncate_mode == 0) return ISO_RR_NAME_TOO_LONG; /* Compute hash */ ret = iso_md5_start(&ctx); if (ret < 0) goto ex; ret = iso_md5_compute(ctx, name, l > 4095 ? 4095 : l); if (ret < 0) goto ex; ret = iso_md5_end(&ctx, hashval); if (ret < 0) goto ex; if (!(flag & 1)) iso_msg_submit(-1, ISO_RR_NAME_TRUNCATED, 0, "File name had to be truncated and MD5 marked: %s", name); /* Avoid to produce incomplete UTF-8 characters */ goal = truncate_length - hash_size - 1; neck = find_utf8_start(name, goal, 0); for (; neck < goal; neck++) name[neck] = '_'; /* Write colon and hash text over end of truncated name */ name[goal] = ':'; goal++; for (i = 0; goal < truncate_length - 1 && i < hash_size / 2; goal += 2) { sprintf(name + goal, "%2.2x", *((unsigned char *) (hashval + i))); i++; } name[truncate_length] = 0; ret = ISO_SUCCESS; ex:; if (ctx != NULL) iso_md5_end(&ctx, hashval); return ret; } /* API */ int iso_truncate_leaf_name(int mode, int length, char *name, int flag) { int ret; if (mode < 0 || mode > 1) return ISO_WRONG_ARG_VALUE; if (length < 64 || length > LIBISOFS_NODE_NAME_MAX) return ISO_WRONG_ARG_VALUE; ret = iso_truncate_rr_name(mode, length, name, 1); return ret; } /* API */ /* @param flag bit0= *now contains the time to be set as nowtime override bit1= disable the nowtime override @return 1= *now is not overridden , 2= *now is overridden */ int iso_nowtime(time_t *now, int flag) { static int now_time_overridden = 0; static time_t now_time_override = 0; if (flag & 1) { now_time_overridden = 1; now_time_override = *now; } if (flag & 2) { now_time_overridden = 0; } *now = time(NULL); if (!now_time_overridden) return 1; *now = now_time_override; return 2; } /* Bit sequence as of Linux 6.1.0 FS_*_FL . Letters as of e2fsprogs 1.47.1, man chattr and lib/pf.c for lsattr(1). */ static char lfa_flag_letters[] = { 's', 'u', 'c', 'S', 'i', 'a', 'd', 'A', 'Z', '-', 'm', 'E', 'I', '-', 'j', 't', 'D', 'T', 'h', 'e', 'V', '-', '-', 'C', '-', 'x', '-', '-', 'N', 'P', 'F', '-' }; /* Sequence and coverage as of e2fsprogs 1.47.1, lib/pf.c for lsattr(1) */ static int lsattr_sequence[] = { 0, 1, 3, 16, 4, 5, 6, 7, 2, 11, 14, 12, 15, 17, 19, 23, 25, 30, 28, 29, 20, 10, -1 }; /* Semi-alphabetic sequence: aAcCdDeE FhiIjNmP sStTuVxZ */ static int semi_alphabetic[] = { 5, 7, 2, 23, 6, 16, 19, 11, 30, 18, 4, 12, 14, 28, 10, 29, 0, 3, 15, 17, 1, 20, 25, 8, -1 }; /* Unknown bits up to 31 */ static int unknown_flags[] = { 9, 13, 21, 22, 24, 26, 27, 31, -1 }; static int num_lsattr_bits = 22; static int max_lsattr_bit = 30; static int non_lsattr_bits[] = { 8, 9, 13, 18, 21, 22, 24, 26, 27, -1 }; /* @param flag bit0= produce lsattr format with '-' and peculiar sequence */ int iso_util_encode_lfa_flags(uint64_t lfa_flags, char **flags_text, int flag) { int i, len = 0, w = 0, was_unknown = 0, pi; *flags_text = NULL; if (flag & 1) goto lsattr_format; for (i = 0; i < 64; i++) if (lfa_flags & (((uint64_t) 1) << i)) len++; *flags_text = calloc(len + 1, 1); if (*flags_text == NULL) return ISO_OUT_OF_MEM; for (i = 0; semi_alphabetic[i] >= 0; i++) { pi = semi_alphabetic[i]; if (lfa_flags & (((uint64_t) 1) << pi)) (*flags_text)[w++] = lfa_flag_letters[pi]; } for (i = 0; unknown_flags[i] >= 0; i++) { pi = unknown_flags[i]; if (lfa_flags & (((uint64_t) 1) << pi)) was_unknown = 1; } for (i = 32; i < 64; i++) { if (lfa_flags & (((uint64_t) 1) << i)) was_unknown = 1; } (*flags_text)[w] = 0; if (was_unknown) return ISO_LFA_UNKNOWN_BIT; return ISO_SUCCESS; lsattr_format:; *flags_text = calloc(num_lsattr_bits + 1, 1); if (*flags_text == NULL) return ISO_OUT_OF_MEM; for (i = 0; i < num_lsattr_bits; i++) { pi = lsattr_sequence[i]; if (pi < 0) break; if (lfa_flags & (((uint64_t) 1) << pi)) { (*flags_text)[w++] = lfa_flag_letters[pi]; } else { (*flags_text)[w++] = '-'; } } (*flags_text)[num_lsattr_bits] = 0; for (i = 0; non_lsattr_bits[i] >= 0; i++) if (lfa_flags & (((uint64_t) 1) << non_lsattr_bits[i])) return ISO_LFA_UNKNOWN_BIT; for (i= max_lsattr_bit + 1; i < 63; i++) if (lfa_flags & (((uint64_t) 1) << i)) return ISO_LFA_UNKNOWN_BIT; return ISO_SUCCESS; } int iso_util_decode_lfa_flags(char *flags_text, uint64_t *lfa_flags, int flag) { int i, j, was_unknown = 0; *lfa_flags = 0; for (i = 0; flags_text[i] != 0; i++) { if (flags_text[i] == '-') continue; for (j = 0; j < 32; j++) if (lfa_flag_letters[j] == flags_text[i]) break; if (j >= 32) { was_unknown = 1; continue; } *lfa_flags |= ((uint64_t) 1) << j; } if (was_unknown) return ISO_LFA_UNKNOWN_LETTER; return 1; } void iso_util_get_lfa_masks(uint64_t *user_settable, uint64_t *su_settable, uint64_t *non_settable, uint64_t *unknown) { /* chattr letters: User settable : sucSdAmtDTCxPF Superuser settable: iaj Non-settable : ZEIheVN unknown : 9, 13, 21, 22, 24, 26, 27, >= 31 */ *user_settable = 0x628384cf; *su_settable = 0x00004030; *non_settable = 0x101c1900; *unknown = 0xffffffff8d602200; } /* * @param flag Bitfield for control purposes * bit0= do not try to change known superuser flags * bit1= change only known chattr settable flags */ uint64_t iso_util_get_effective_lfa_mask(uint64_t change_mask, int flag) { uint64_t known_user_mask, known_su_mask, non_settable, unknown; iso_util_get_lfa_masks(&known_user_mask, &known_su_mask, &non_settable, &unknown); if (flag & 1) change_mask &= ~known_su_mask; if (flag & 2) change_mask &= (known_user_mask | known_su_mask); return change_mask; } /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2009 - 2022 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_UTIL_H_ #define LIBISO_UTIL_H_ #ifdef HAVE_STDINT_H #include #else #ifdef HAVE_INTTYPES_H #include #endif #endif #include #ifdef HAVE_STDLIB_H #include #endif #include #ifndef MAX # define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif #ifndef MIN # define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif #define DIV_UP(n,div) ((n + div - 1) / div) #define ROUND_UP(n,mul) (DIV_UP(n, mul) * mul) int int_pow(int base, int power); /** * Set up locale by LC_* environment variables. */ int iso_init_locale(int flag); /** * Convert the charset encoding of a given string. * * @param input * Input string * @param icharset * Input charset. Must be supported by iconv * @param ocharset * Output charset. Must be supported by iconv * @param output * Location where the pointer to the output string will be stored * @return * 1 on success, < 0 on error */ int strconv(const char *input, const char *icharset, const char *ocharset, char **output); /* Like strconv but processing len input bytes rather than strlen(input) */ int strnconv(const char *str, const char *icharset, const char *ocharset, size_t len, char **output); /* Like strnconv but also returning the number of bytes in *output. */ int strnconvl(const char *str, const char *icharset, const char *ocharset, size_t len, char **output, size_t *out_len); /** * Convert a given string from any input charset to ASCII * * @param icharset * Input charset. Must be supported by iconv * @param input * Input string * @param output * Location where the pointer to the output string will be stored * @return * 1 on success, < 0 on error */ int str2ascii(const char *icharset, const char *input, char **output); /** * Convert a given string from any input charset to UCS-2BE charset, * used for Joliet file identifiers. * * @param icharset * Input charset. Must be supported by iconv * @param input * Input string * @param output * Location where the pointer to the output string will be stored * @return * 1 on success, < 0 on error */ int str2ucs(const char *icharset, const char *input, uint16_t **output); /** * Convert a given string from any input charset to UTF-16BE charset, * used for HFS+ file identifiers. * (UTF-16 differs from older UCS-2 by having multi word characters.) * * @param icharset * Input charset. Must be supported by iconv * @param input * Input string * @param output * Location where the pointer to the output string will be stored * @return * 1 on success, < 0 on error */ int str2utf16be(const char *icharset, const char *input, uint16_t **output); /** * Create a level 1 directory identifier. * * @param src * The identifier, in ASCII encoding. * @param relaxed * 0 only allow d-characters, 1 allow also lowe case chars, * 2 allow all characters */ char *iso_1_dirid(const char *src, int relaxed); /** * Create a level 2 directory identifier. * * @param src * The identifier, in ASCII encoding. */ char *iso_2_dirid(const char *src); /** * Create a dir name suitable for an ISO image with relaxed constraints. * * @param src * The identifier, in ASCII encoding. * @param size * Max len for the name * @param relaxed * 0 only allow d-characters, 1 allow also lowe case chars, * 2 allow all characters */ char *iso_r_dirid(const char *src, int size, int relaxed); /** * Create a level 1 file identifier that consists of a name, in 8.3 * format. * Note that version number is not added to the file name * * @param src * The identifier, in ASCII encoding. * @param relaxed * 0 only allow d-characters, 1 allow also lowe case chars, * 2 allow all characters * @param force_dots * If 1 then prepend empty extension by SEPARATOR1 = '.' */ char *iso_1_fileid(const char *src, int relaxed, int force_dots); /** * Create a level 2 file identifier. * Note that version number is not added to the file name * * @param src * The identifier, in ASCII encoding. */ char *iso_2_fileid(const char *src); /** * Create a file name suitable for an ISO image with relaxed constraints. * * @param src * The identifier, in ASCII encoding. * @param len * Max len for the name, without taken the "." into account. * @param relaxed * 0 only allow d-characters, 1 allow also lowe case chars, * 2 allow all characters * @param forcedot * Whether to ensure that "." is added */ char *iso_r_fileid(const char *src, size_t len, int relaxed, int forcedot); /** * Create a Joliet file identifier that consists of name and extension. The * combined name and extension length will normally not exceed 64 characters * (= 128 bytes). The name and the extension will be separated (.). * All characters consist of 2 bytes and the resulting string is * NULL-terminated by a 2-byte NULL. * * Note that version number and (;1) is not appended. * @param flag * bit0= no_force_dots * bit1= allow 103 characters rather than 64 * @return * NULL if the original name and extension both are of length 0. */ uint16_t *iso_j_file_id(const uint16_t *src, int flag); /** * Create a Joliet directory identifier that consists of name and optionally * extension. The combined name and extension length will not exceed 128 bytes, * and the name and extension will be separated (.). All characters consist of * 2 bytes and the resulting string is NULL-terminated by a 2-byte NULL. * * @param flag * bit1= allow 103 characters rather than 64 * @return * NULL if the original name and extension both are of length 0. */ uint16_t *iso_j_dir_id(const uint16_t *src, int flag); /** * Like strlen, but for Joliet strings. */ size_t ucslen(const uint16_t *str); /** * Like strrchr, but for Joliet strings. */ uint16_t *ucsrchr(const uint16_t *str, char c); /** * Like strdup, but for Joliet strings. */ uint16_t *ucsdup(const uint16_t *str); /** * Like strcmp, but for Joliet strings. */ int ucscmp(const uint16_t *s1, const uint16_t *s2); /** * Like strcpy, but for Joliet strings. */ uint16_t *ucscpy(uint16_t *dest, const uint16_t *src); /** * Like strncpy, but for Joliet strings. * @param n * Maximum number of characters to copy (2 bytes per char). */ uint16_t *ucsncpy(uint16_t *dest, const uint16_t *src, size_t n); /** * Check whether utf_word is the first surrogate word of a pair. * If so, change it to UTF-16 character '_'. */ void iso_handle_split_utf16(uint16_t *utf_word); /** * Convert a given input string to d-chars. * @return * 1 on success, < 0 error, 0 if input was null (output is set to null) */ int str2d_char(const char *icharset, const char *input, char **output); int str2a_char(const char *icharset, const char *input, char **output); /* Check for membership in the d-, a-, or j-character set */ int valid_d_char(char c); int valid_a_char(char c); int valid_j_char(uint16_t c); void iso_lsb(uint8_t *buf, uint32_t num, int bytes); void iso_lsb64(uint8_t *buf, uint64_t num); void iso_msb(uint8_t *buf, uint32_t num, int bytes); void iso_bb(uint8_t *buf, uint32_t num, int bytes); /* An alternative to iso_lsb() which advances the write pointer */ int iso_lsb_to_buf(char **wpt, uint32_t value, int bytes, int flag); uint32_t iso_read_lsb(const uint8_t *buf, int bytes); uint32_t iso_read_msb(const uint8_t *buf, int bytes); /** * if error != NULL it will be set to 1 if LSB and MSB integers don't match. */ uint32_t iso_read_bb(const uint8_t *buf, int bytes, int *error); uint64_t iso_read_lsb64(const uint8_t *buf); uint64_t iso_read_msb64(const uint8_t *buf); /** * Records the date/time into a 7 byte buffer (ECMA-119, 9.1.5) * * @param buf * Buffer where the date will be written * @param t * The time to be written * @param always_gmt * Always write the date in GMT and not in local time. */ void iso_datetime_7(uint8_t *buf, time_t t, int always_gmt); /** Records the date/time into a 17 byte buffer (ECMA-119, 8.4.26.1) */ void iso_datetime_17(uint8_t *buf, time_t t, int always_gmt); time_t iso_datetime_read_7(const uint8_t *buf); time_t iso_datetime_read_17(const uint8_t *buf); /** * Check whether the caller process has read access to the given local file. * * @return * 1 on success (i.e, the process has read access), < 0 on error * (including ISO_FILE_ACCESS_DENIED on access denied to the specified file * or any directory on the path). */ int iso_eaccess(const char *path); /** * Copy up to \p len chars from \p buf and return this newly allocated * string. The new string is null-terminated. */ char *iso_util_strcopy(const char *buf, size_t len); /** * Copy up to \p len chars from \p buf and return this newly allocated * string. The new string is null-terminated. * Any trailing blanks will be removed. */ char *iso_util_strcopy_untail(const char *buf, size_t len); /** * Copy up to \p max characters from \p src to \p dest. If \p src has less than * \p max characters, we pad dest with " " characters. */ void strncpy_pad(char *dest, const char *src, size_t max); /** * Convert a Joliet string with a length of \p len bytes to a new string * in local charset. */ char *ucs2str(const char *buf, size_t len); typedef struct iso_rbtree IsoRBTree; typedef struct iso_htable IsoHTable; typedef unsigned int (*hash_funtion_t)(const void *key); typedef int (*compare_function_t)(const void *a, const void *b); typedef void (*hfree_data_t)(void *key, void *data); /** * Create a new binary tree. libisofs binary trees allow you to add any data * passing it as a pointer. You must provide a function suitable for compare * two elements. * * @param compare * A function to compare two keys. It takes a pointer to both keys * and return 0, -1 or 1 if the first key is equal, less or greater * than the second one. * @param tree * Location where the tree structure will be stored. */ int iso_rbtree_new(int (*compare)(const void*, const void*), IsoRBTree **tree); /** * Destroy a given tree. * * Note that only the structure itself is deleted. To delete the elements, you * should provide a valid free_data function. It will be called for each * element of the tree, so you can use it to free any related data. */ void iso_rbtree_destroy(IsoRBTree *tree, void (*free_data)(void *)); /** * Inserts a given element in a Red-Black tree. * * @param tree * the tree where to insert * @param data * element to be inserted on the tree. It can't be NULL * @param item * if not NULL, it will point to a location where the tree element ptr * will be stored. If data was inserted, *item == data. If data was * already on the tree, *item points to the previously inserted object * that is equal to data. * @return * 1 success, 0 element already inserted, < 0 error */ int iso_rbtree_insert(IsoRBTree *tree, void *data, void **item); /** * Get the number of elements in a given tree. */ size_t iso_rbtree_get_size(IsoRBTree *tree); /** * Get an array view of the elements of the tree. * * @param include_item * Function to select which elements to include in the array. It that takes * a pointer to an element and returns 1 if the element should be included, * 0 if not. If you want to add all elements to the array, you can pass a * NULL pointer. * @param size * If not null, will be filled with the number of elements in the array, * without counting the final NULL item. * @return * A sorted array with the contents of the tree, or NULL if there is not * enough memory to allocate the array. You should free(3) the array when * no more needed. Note that the array is NULL-terminated, and thus it * has size + 1 length. */ void **iso_rbtree_to_array(IsoRBTree *tree, int (*include_item)(void *), size_t *size); /** Predict the size of the array which gets returned by iso_rbtree_to_array(). */ size_t iso_rbtree_count_array(IsoRBTree *tree, size_t initial_count, int (*include_item)(void *)); /** * Create a new hash table. * * @param size * Number of slots in table. * @param hash * Function used to generate */ int iso_htable_create(size_t size, hash_funtion_t hash, compare_function_t compare, IsoHTable **table); /** * Put an element in a Hash Table. The element will be identified by * the given key, that you should use to retrieve the element again. * * This function allow duplicates, i.e., two items with the same key. In those * cases, the value returned by iso_htable_get() is undefined. If you don't * want to allow duplicates, use iso_htable_put() instead; * * Both the key and data pointers will be stored internally, so you should * free the objects they point to. Use iso_htable_remove() to delete an * element from the table. */ int iso_htable_add(IsoHTable *table, void *key, void *data); /** * Like iso_htable_add(), but this doesn't allow dulpicates. * * @return * 1 success, 0 if an item with the same key already exists, < 0 error */ int iso_htable_put(IsoHTable *table, void *key, void *data); /** * Retrieve an element from the given table. * * @param table * Hash table * @param key * Key of the element that will be removed * @param data * Will be filled with the element found. Remains untouched if no * element with the given key is found. * @return * 1 if found, 0 if not, < 0 on error */ int iso_htable_get(IsoHTable *table, void *key, void **data); /** * Remove an item with the given key from the table. In tables that allow * duplicates, it is undefined the element that will be deleted. * * @param table * Hash table * @param key * Key of the element that will be removed * @param free_data * Function that will be called passing as parameters both the key and * the element that will be deleted. The user can use it to free the * element. You can pass NULL if you don't want to delete the item itself. * @return * 1 success, 0 no element exists with the given key, < 0 error */ int iso_htable_remove(IsoHTable *table, void *key, hfree_data_t free_data); /** * Like remove, but instead of checking for key equality using the compare * function, it just compare the key pointers. If the table allows duplicates, * and you provide different keys (i.e. different pointers) to elements * with same key (i.e. same content), this function ensure the exact element * is removed. * * It has the problem that you must provide the same key pointer, and not just * a key whose contents are equal. Moreover, if you use the same key (same * pointer) to identify several objects, what of those are removed is * undefined. * * @param table * Hash table * @param key * Key of the element that will be removed * @param free_data * Function that will be called passing as parameters both the key and * the element that will be deleted. The user can use it to free the * element. You can pass NULL if you don't want to delete the item itself. * @return * 1 success, 0 no element exists with the given key, < 0 error */ int iso_htable_remove_ptr(IsoHTable *table, void *key, hfree_data_t free_data); /** * Destroy the given hash table. * * Note that you're responsible to actually destroy the elements by providing * a valid free_data function. You can pass NULL if you only want to delete * the hash structure. */ void iso_htable_destroy(IsoHTable *table, hfree_data_t free_data); /** * Hash function suitable for keys that are char strings. */ unsigned int iso_str_hash(const void *key); /** * Encode an integer as LEN,BYTES for being a component in certain AAIP * attribute values. */ int iso_util_encode_len_bytes(uint32_t data, char *buffer, int data_len, int *result_len, int flag); /** * Decode an integer as LEN,BYTES for being a component in certain AAIP * attribute values. * @param data returns the decoded value * @param buffer contains the encoded value * @param data_len returns the number of value bytes (without len byte) * @param buffer_len tells the number of valid buffer bytes */ int iso_util_decode_len_bytes(uint32_t *data, char *buffer, int *data_len, int buffer_len, int flag); /* Evaluate a data block whether it is a libisofs session checksum tag of desired type and eventually use it to verify the MD5 checksum computed so far. @param block The data block to be evaluated @param desired Bit map which tells what tag types are expected (0 to 30) @param lba The address from where block was read @param ctx The checksum context computed so far @param ctx_start_lba The block address where checksum computing started @param tag_type Returns the tag type (0 means invalid tag type) @param flag Bitfield for control purposes, unused yet, submit 0 @return 1= tag is desired and matches 0= not a recognizable tag or a undesired tag <0 is error or mismatch */ int iso_util_eval_md5_tag(char *block, int desired, uint32_t lba, void *ctx, uint32_t ctx_start_lba, int *tag_type, uint32_t *next_tag, int flag); int iso_util_tag_magic(int tag_type, char **tag_magic, int *len, int flag); int iso_util_bin_to_hex(char *target, uint8_t *bytes, int num_bytes, int flag); int iso_util_hex_to_bin(char *hex, char *bin, int bin_size, int *bin_count, int flag); int iso_truncate_rr_name(int truncate_mode, int truncate_length, char *name, int flag); /* ------------------------------------------------------------------------- */ /* In md5.h these function prototypes would be neighbors of (Ecma119Image *) which needs inclusion of ecma119.h and more. So, being generic, they ended up here. */ /* Function to identify and manage md5sum indice of the old image. * data is supposed to be a 4 byte integer, bit 31 shall be 0, * value 0 of this integer means that it is not a valid index. */ int checksum_cx_xinfo_func(void *data, int flag); /* The iso_node_xinfo_cloner function which gets associated to * checksum_cx_xinfo_func by iso_init() resp. iso_init_with_flag() via * iso_node_xinfo_make_clonable() */ int checksum_cx_xinfo_cloner(void *old_data, void **new_data, int flag); /* Function to identify and manage md5 sums of unspecified providence stored * directly in this xinfo. This is supposed to override any other recorded * MD5 of the node unless data get copied and checksummed during that copying. */ int checksum_md5_xinfo_func(void *data, int flag); /* The iso_node_xinfo_cloner function which gets associated to * checksum_md5_xinfo_func by iso_init() resp. iso_init_with_flag() via * iso_node_xinfo_make_clonable() */ int checksum_md5_xinfo_cloner(void *old_data, void **new_data, int flag); /* The iso_node_xinfo_cloner function which gets associated to * iso_hfsplus_xinfo_func by iso_init() resp. iso_init_with_flag() via * iso_node_xinfo_make_clonable() */ int iso_hfsplus_xinfo_cloner(void *old_data, void **new_data, int flag); /* ------------------------------------------------------------------------- */ void *iso_alloc_mem(size_t size, size_t count, int flag); #define LIBISO_ALLOC_MEM(pt, typ, count) { \ pt= (typ *) iso_alloc_mem(sizeof(typ), (size_t) (count), 0); \ if(pt == NULL) { \ ret= ISO_OUT_OF_MEM; goto ex; \ } } #define LIBISO_ALLOC_MEM_VOID(pt, typ, count) { \ pt= (typ *) iso_alloc_mem(sizeof(typ), (size_t) (count), 0); \ if(pt == NULL) { \ goto ex; \ } } #define LIBISO_FREE_MEM(pt) { \ if(pt != NULL) \ free((char *) pt); \ } /* @param in Valid memory or NULL @param out Returns valid memory or NULL @param size Number of bytes to copy. 0 means strlen(in)+1 if not NULL. @return 1 or ISO_OUT_OF_MEM */ int iso_clone_mem(char *in, char **out, size_t size); /* Like iso_clone_mem but first freeing *out if not NULL */ int iso_clone_mgtd_mem(char *in, char **out, size_t size); /** Convert a text into a number of type double and multiply it by unit code [kmgt] (2^10 to 2^40) or [s] (2048) or [d] (512). (Also accepts capital letters.) @param text Input like "42", "223062s", "3m" or "-1g" @param flag Bitfield for control purposes: bit0= return -1 rather than 0 on failure bit1= if scaled then compute the last byte of the last unit @return The derived value */ off_t iso_scanf_io_size(char *text, int flag); /* ------------------------------------------------------------------------- */ /* To avoid the need to include more system header files */ uint16_t iso_ntohs(uint16_t v); uint16_t iso_htons(uint16_t v); #endif /*LIBISO_UTIL_H_*/ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2014 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "util.h" #include "libisofs.h" #include #include /* * Hash table implementation */ struct iso_hnode { void *key; void *data; /** next node for chaining */ struct iso_hnode *next; }; struct iso_htable { struct iso_hnode **table; size_t size; /**< number of items in table */ size_t cap; /**< number of slots in table */ hash_funtion_t hash; compare_function_t compare; }; static struct iso_hnode *iso_hnode_new(void *key, void *data) { struct iso_hnode *node = malloc(sizeof(struct iso_hnode)); if (node == NULL) return NULL; node->data = data; node->key = key; node->next = NULL; return node; } /** * Put an element in a Hash Table. The element will be identified by * the given key, that you should use to retrieve the element again. * * This function allow duplicates, i.e., two items with the same key. In those * cases, the value returned by iso_htable_get() is undefined. If you don't * want to allow duplicates, use iso_htable_put() instead; * * Both the key and data pointers will be stored internally, so you should * free the objects they point to. Use iso_htable_remove() to delete an * element from the table. */ int iso_htable_add(IsoHTable *table, void *key, void *data) { struct iso_hnode *node; struct iso_hnode *new; unsigned int hash; if (table == NULL || key == NULL) { return ISO_NULL_POINTER; } new = iso_hnode_new(key, data); if (new == NULL) { return ISO_OUT_OF_MEM; } hash = table->hash(key) % table->cap; node = table->table[hash]; table->size++; new->next = node; table->table[hash] = new; return ISO_SUCCESS; } /** * Like iso_htable_add(), but this doesn't allow dulpicates. * * @return * 1 success, 0 if an item with the same key already exists, < 0 error */ int iso_htable_put(IsoHTable *table, void *key, void *data) { struct iso_hnode *node; struct iso_hnode *new; unsigned int hash; if (table == NULL || key == NULL) { return ISO_NULL_POINTER; } hash = table->hash(key) % table->cap; node = table->table[hash]; while (node) { if (!table->compare(key, node->key)) { return 0; } node = node->next; } new = iso_hnode_new(key, data); if (new == NULL) { return ISO_OUT_OF_MEM; } table->size++; new->next = table->table[hash]; table->table[hash] = new; return ISO_SUCCESS; } /** * Retrieve an element from the given table. * * @param table * Hash table * @param key * Key of the element that will be removed * @param data * Will be filled with the element found. Remains untouched if no * element with the given key is found. * @return * 1 if found, 0 if not, < 0 on error */ int iso_htable_get(IsoHTable *table, void *key, void **data) { struct iso_hnode *node; unsigned int hash; if (table == NULL || key == NULL) { return ISO_NULL_POINTER; } hash = table->hash(key) % table->cap; node = table->table[hash]; while (node) { if (!table->compare(key, node->key)) { if (data) { *data = node->data; } return 1; } node = node->next; } return 0; } /** * Remove an item with the given key from the table. In tables that allow * duplicates, it is undefined the element that will be deleted. * * @param table * Hash table * @param key * Key of the element that will be removed * @param free_data * Function that will be called passing as parameters both the key and * the element that will be deleted. The user can use it to free the * element. You can pass NULL if you don't want to delete the item itself. * @return * 1 success, 0 no element exists with the given key, < 0 error */ int iso_htable_remove(IsoHTable *table, void *key, hfree_data_t free_data) { struct iso_hnode *node, *prev; unsigned int hash; if (table == NULL || key == NULL) { return ISO_NULL_POINTER; } hash = table->hash(key) % table->cap; node = table->table[hash]; prev = NULL; while (node) { if (!table->compare(key, node->key)) { if (free_data) free_data(node->key, node->data); if (prev) { prev->next = node->next; } else { table->table[hash] = node->next; } free(node); table->size--; return 1; } prev = node; node = node->next; } return 0; } /** * Like remove, but instead of checking for key equality using the compare * function, it just compare the key pointers. If the table allows duplicates, * and you provide different keys (i.e. different pointers) to elements * with same key (i.e. same content), this function ensure the exact element * is removed. * * It has the problem that you must provide the same key pointer, and not just * a key whose contents are equal. Moreover, if you use the same key (same * pointer) to identify several objects, what of those are removed is * undefined. * * @param table * Hash table * @param key * Key of the element that will be removed * @param free_data * Function that will be called passing as parameters both the key and * the element that will be deleted. The user can use it to free the * element. You can pass NULL if you don't want to delete the item itself. * @return * 1 success, 0 no element exists with the given key, < 0 error */ int iso_htable_remove_ptr(IsoHTable *table, void *key, hfree_data_t free_data) { struct iso_hnode *node, *prev; unsigned int hash; if (table == NULL || key == NULL) { return ISO_NULL_POINTER; } hash = table->hash(key) % table->cap; node = table->table[hash]; prev = NULL; while (node) { if (key == node->key) { if (free_data) free_data(node->key, node->data); if (prev) { prev->next = node->next; } else { table->table[hash] = node->next; } free(node); table->size--; return 1; } prev = node; node = node->next; } return 0; } /** * Hash function suitable for keys that are char strings. */ unsigned int iso_str_hash(const void *key) { int i, len; const char *p = key; unsigned int h = 2166136261u; len = strlen(p); for (i = 0; i < len; i++) h = (h * 16777619 ) ^ p[i]; return h; } /** * Destroy the given hash table. * * Note that you're responsible to actually destroy the elements by providing * a valid free_data function. You can pass NULL if you only want to delete * the hash structure. */ void iso_htable_destroy(IsoHTable *table, hfree_data_t free_data) { size_t i; struct iso_hnode *node, *tmp; if (table == NULL) { return; } for (i = 0; i < table->cap; ++i) { node = table->table[i]; while (node) { tmp = node->next; if (free_data) free_data(node->key, node->data); free(node); node = tmp; } } free(table->table); free(table); } /** * Create a new hash table. * * @param size * Number of slots in table. * @param hash * Function used to generate */ int iso_htable_create(size_t size, hash_funtion_t hash, compare_function_t compare, IsoHTable **table) { IsoHTable *t; if (size <= 0) return ISO_WRONG_ARG_VALUE; if (table == NULL) return ISO_NULL_POINTER; t = malloc(sizeof(IsoHTable)); if (t == NULL) { return ISO_OUT_OF_MEM; } t->table = calloc(size, sizeof(void*)); if (t->table == NULL) { free(t); return ISO_OUT_OF_MEM; } t->cap = size; t->size = 0; t->hash = hash; t->compare = compare; *table = t; return ISO_SUCCESS; } /* * Copyright (c) 2007 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "util.h" #include "libisofs.h" #include /* * This implementation of Red-Black tree is based on the public domain * implementation of Julienne Walker. */ struct iso_rbnode { void *data; struct iso_rbnode *ch[2]; unsigned int red :1; }; struct iso_rbtree { struct iso_rbnode *root; size_t size; int (*compare)(const void *a, const void *b); }; /** * Create a new binary tree. libisofs binary trees allow you to add any data * passing it as a pointer. You must provide a function suitable for compare * two elements. * * @param compare * A function to compare two elements. It takes a pointer to both elements * and return 0, -1 or 1 if the first element is equal, less or greater * than the second one. * @param tree * Location where the tree structure will be stored. */ int iso_rbtree_new(int (*compare)(const void*, const void*), IsoRBTree **tree) { if (compare == NULL || tree == NULL) { return ISO_NULL_POINTER; } *tree = calloc(1, sizeof(IsoRBTree)); if (*tree == NULL) { return ISO_OUT_OF_MEM; } (*tree)->compare = compare; return ISO_SUCCESS; } static void rbtree_destroy_aux(struct iso_rbnode *root, void (*free_data)(void *)) { if (root == NULL) { return; } if (free_data != NULL) { free_data(root->data); } rbtree_destroy_aux(root->ch[0], free_data); rbtree_destroy_aux(root->ch[1], free_data); free(root); } /** * Destroy a given tree. * * Note that only the structure itself is deleted. To delete the elements, you * should provide a valid free_data function. It will be called for each * element of the tree, so you can use it to free any related data. */ void iso_rbtree_destroy(IsoRBTree *tree, void (*free_data)(void *)) { if (tree == NULL) { return; } rbtree_destroy_aux(tree->root, free_data); free(tree); } static inline int is_red(struct iso_rbnode *root) { return root != NULL && root->red; } static struct iso_rbnode *iso_rbtree_single(struct iso_rbnode *root, int dir) { struct iso_rbnode *save = root->ch[!dir]; root->ch[!dir] = save->ch[dir]; save->ch[dir] = root; root->red = 1; save->red = 0; return save; } static struct iso_rbnode *iso_rbtree_double(struct iso_rbnode *root, int dir) { root->ch[!dir] = iso_rbtree_single(root->ch[!dir], !dir); return iso_rbtree_single(root, dir); } static struct iso_rbnode *iso_rbnode_new(void *data) { struct iso_rbnode *rn = malloc(sizeof(struct iso_rbnode)); if (rn != NULL) { rn->data = data; rn->red = 1; rn->ch[0] = NULL; rn->ch[1] = NULL; } return rn; } /** * Inserts a given element in a Red-Black tree. * * @param tree * the tree where to insert * @param data * element to be inserted on the tree. It can't be NULL * @param item * if not NULL, it will point to a location where the tree element ptr * will be stored. If data was inserted, *item == data. If data was * already on the tree, *item points to the previously inserted object * that is equal to data. * @return * 1 success, 0 element already inserted, < 0 error */ int iso_rbtree_insert(IsoRBTree *tree, void *data, void **item) { struct iso_rbnode *new; int added = 0; /* has a new node been added? */ if (tree == NULL || data == NULL) { return ISO_NULL_POINTER; } if (tree->root == NULL) { /* Empty tree case */ tree->root = iso_rbnode_new(data); if (tree->root == NULL) { return ISO_OUT_OF_MEM; } new = data; added = 1; } else { struct iso_rbnode head = { 0, {NULL, NULL}, 0 }; /* False tree root */ struct iso_rbnode *g, *t; /* Grandparent & parent */ struct iso_rbnode *p, *q; /* Iterator & parent */ int dir = 0, last = 0; int comp; /* Set up helpers */ t = &head; g = p = NULL; q = t->ch[1] = tree->root; /* Search down the tree */ while (1) { if (q == NULL) { /* Insert new node at the bottom */ p->ch[dir] = q = iso_rbnode_new(data); if (q == NULL) { return ISO_OUT_OF_MEM; } added = 1; } else if (is_red(q->ch[0]) && is_red(q->ch[1])) { /* Color flip */ q->red = 1; q->ch[0]->red = 0; q->ch[1]->red = 0; } /* Fix red violation */ if (is_red(q) && is_red(p)) { int dir2 = (t->ch[1] == g); if (q == p->ch[last]) { t->ch[dir2] = iso_rbtree_single(g, !last); } else { t->ch[dir2] = iso_rbtree_double(g, !last); } } if (q->data == data) { comp = 0; } else { comp = tree->compare(q->data, data); } /* Stop if found */ if (comp == 0) { new = q->data; break; } last = dir; dir = (comp < 0); /* Update helpers */ if (g != NULL) t = g; g = p, p = q; q = q->ch[dir]; } /* Update root */ tree->root = head.ch[1]; } /* Make root black */ tree->root->red = 0; if (item != NULL) { *item = new; } if (added) { /* a new element has been added */ tree->size++; return 1; } else { return 0; } } /** * Get the number of elements in a given tree. */ size_t iso_rbtree_get_size(IsoRBTree *tree) { return tree->size; } static size_t rbtree_to_array_aux(struct iso_rbnode *root, void **array, size_t pos, int (*include_item)(void *)) { if (root == NULL) { return pos; } pos = rbtree_to_array_aux(root->ch[0], array, pos, include_item); if (include_item == NULL || include_item(root->data)) { array[pos++] = root->data; } pos = rbtree_to_array_aux(root->ch[1], array, pos, include_item); return pos; } /** * Get an array view of the elements of the tree. * * @param include_item * Function to select which elements to include in the array. It that takes * a pointer to an element and returns 1 if the element should be included, * 0 if not. If you want to add all elements to the array, you can pass a * NULL pointer. * @return * A sorted array with the contents of the tree, or NULL if there is not * enough memory to allocate the array. You should free(3) the array when * no more needed. Note that the array is NULL-terminated, and thus it * has size + 1 length. */ void ** iso_rbtree_to_array(IsoRBTree *tree, int (*include_item)(void *), size_t *size) { size_t pos; void **array, **new_array; array = malloc((tree->size + 1) * sizeof(void*)); if (array == NULL) { return NULL; } /* fill array */ pos = rbtree_to_array_aux(tree->root, array, 0, include_item); array[pos] = NULL; new_array = realloc(array, (pos + 1) * sizeof(void*)); if (new_array == NULL) { free((char *) array); return NULL; } array= new_array; if (size) { *size = pos; } return array; } static size_t rbtree_count_array_aux(struct iso_rbnode *root, size_t pos, int (*include_item)(void *)) { if (root == NULL) { return pos; } pos = rbtree_count_array_aux(root->ch[0], pos, include_item); if (include_item == NULL || include_item(root->data)) { /* { IsoFileSrc* src = (IsoFileSrc*) root->data; fprintf(stderr, "libisofs_DEBUG: rbtree_count_array_aux : not taken : '%s'\n", iso_stream_get_source_path(src->stream, 0)); } */ pos++; } pos = rbtree_count_array_aux(root->ch[1], pos, include_item); return pos; } size_t iso_rbtree_count_array(IsoRBTree *tree, size_t initial_count, int (*include_item)(void *)) { size_t pos; pos = rbtree_count_array_aux(tree->root, initial_count, include_item); return pos; } /* * Copyright (c) 2007 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * or later as published by the Free Software Foundation. * See COPYING file for details. */ #ifndef LIBISO_IMAGE_WRITER_H_ #define LIBISO_IMAGE_WRITER_H_ #include "ecma119.h" struct Iso_Image_Writer { /** * */ int (*compute_data_blocks)(IsoImageWriter *writer); int (*write_vol_desc)(IsoImageWriter *writer); int (*write_data)(IsoImageWriter *writer); int (*free_data)(IsoImageWriter *writer); void *data; Ecma119Image *target; }; /** * This is the function all Writers should call to write data to image. * Currently, it is just a wrapper for write(2) Unix system call. * * It is implemented in ecma119.c * * @return * 1 on success, < 0 error */ int iso_write(Ecma119Image *target, void *buf, size_t count); int ecma119_writer_create(Ecma119Image *target); #endif /*LIBISO_IMAGE_WRITER_H_*/ /* * * */ #include "fsource.h" #include "mocked_fsrc.h" #include "libisofs.h" #include #include #include #include #include #include #include static struct mock_file *path_to_node(IsoFilesystem *fs, const char *path); static char *get_path_aux(struct mock_file *file) { if (file->parent == NULL) { return strdup(""); } else { char *path = get_path_aux(file->parent); int pathlen = strlen(path); path = realloc(path, pathlen + strlen(file->name) + 2); path[pathlen] = '/'; path[pathlen + 1] = '\0'; return strcat(path, file->name); } } static char* mfs_get_path(IsoFileSource *src) { struct mock_file *data; data = src->data; return get_path_aux(data); } static char* mfs_get_name(IsoFileSource *src) { struct mock_file *data; data = src->data; return strdup(data->name); } static int mfs_lstat(IsoFileSource *src, struct stat *info) { struct mock_file *data; if (src == NULL || info == NULL) { return ISO_NULL_POINTER; } data = src->data; *info = data->atts; return ISO_SUCCESS; } static int mfs_stat(IsoFileSource *src, struct stat *info) { struct mock_file *node; if (src == NULL || info == NULL) { return ISO_NULL_POINTER; } node = src->data; while ( S_ISLNK(node->atts.st_mode) ) { /* the destination is stated */ node = path_to_node(node->fs, (char *)node->content); if (node == NULL) { return ISO_FILE_ERROR; } } *info = node->atts; return ISO_SUCCESS; } static int mfs_access(IsoFileSource *src) { // TODO not implemented return ISO_ERROR; } static int mfs_open(IsoFileSource *src) { // TODO not implemented return ISO_ERROR; } static int mfs_close(IsoFileSource *src) { // TODO not implemented return ISO_ERROR; } static int mfs_read(IsoFileSource *src, void *buf, size_t count) { // TODO not implemented return ISO_ERROR; } static int mfs_readdir(IsoFileSource *src, IsoFileSource **child) { // TODO not implemented return ISO_ERROR; } static int mfs_readlink(IsoFileSource *src, char *buf, size_t bufsiz) { struct mock_file *data; if (src == NULL || buf == NULL) { return ISO_NULL_POINTER; } if (bufsiz <= 0) { return ISO_WRONG_ARG_VALUE; } data = src->data; if (!S_ISLNK(data->atts.st_mode)) { return ISO_FILE_IS_NOT_SYMLINK; } strncpy(buf, data->content, bufsiz); buf[bufsiz-1] = '\0'; return ISO_SUCCESS; } static IsoFilesystem* mfs_get_filesystem(IsoFileSource *src) { struct mock_file *data; data = src->data; return data->fs; } static void mfs_free(IsoFileSource *src) { /* nothing to do */ } IsoFileSourceIface mfs_class = { 0, mfs_get_path, mfs_get_name, mfs_lstat, mfs_stat, mfs_access, mfs_open, mfs_close, mfs_read, mfs_readdir, mfs_readlink, mfs_get_filesystem, mfs_free }; /** * * @return * 1 success, < 0 error */ static int mocked_file_source_new(struct mock_file *data, IsoFileSource **src) { IsoFileSource *mocked_src; if (src == NULL || data == NULL) { return ISO_NULL_POINTER; } /* allocate memory */ mocked_src = malloc(sizeof(IsoFileSource)); if (mocked_src == NULL) { free(data); return ISO_OUT_OF_MEM; } /* fill struct */ mocked_src->refcount = 1; mocked_src->data = data; mocked_src->class = &mfs_class; /* take a ref to filesystem */ //iso_filesystem_ref(fs); /* return */ *src = mocked_src; return ISO_SUCCESS; } static struct mock_file *path_to_node(IsoFilesystem *fs, const char *path) { struct mock_file *node; struct mock_file *dir; char *ptr, *brk_info, *component; /* get the first child at the root of the volume * that is "/" */ dir = fs->data; node = dir; if (!strcmp(path, "/")) return node; ptr = strdup(path); /* get the first component of the path */ component = strtok_r(ptr, "/", &brk_info); while (component) { size_t i; if ( !S_ISDIR(node->atts.st_mode) ) { node=NULL; break; } dir = node; node=NULL; if (!dir->content) { break; } i = 0; while (((struct mock_file**)dir->content)[i]) { if (!strcmp(component, ((struct mock_file**)dir->content)[i]->name)) { node = ((struct mock_file**)dir->content)[i]; break; } ++i; } /* see if a node could be found */ if (node==NULL) { break; } component = strtok_r(NULL, "/", &brk_info); } free(ptr); return node; } static void add_node(struct mock_file *parent, struct mock_file *node) { int i; i = 0; if (parent->content) { while (((struct mock_file**)parent->content)[i]) { ++i; } } parent->content = realloc(parent->content, (i+2) * sizeof(void*)); ((struct mock_file**)parent->content)[i] = node; ((struct mock_file**)parent->content)[i+1] = NULL; } struct mock_file *test_mocked_fs_get_root(IsoFilesystem *fs) { return fs->data; } int test_mocked_fs_add_dir(const char *name, struct mock_file *p, struct stat atts, struct mock_file **node) { struct mock_file *dir = calloc(1, sizeof(struct mock_file)); dir->fs = p->fs; dir->atts = atts; dir->name = strdup(name); dir->parent = p; add_node(p, dir); *node = dir; return ISO_SUCCESS; } int test_mocked_fs_add_symlink(const char *name, struct mock_file *p, struct stat atts, const char *dest, struct mock_file **node) { struct mock_file *link = calloc(1, sizeof(struct mock_file)); link->fs = p->fs; link->atts = atts; link->name = strdup(name); link->parent = p; add_node(p, link); link->content = strdup(dest); *node = link; return ISO_SUCCESS; } static int mocked_get_root(IsoFilesystem *fs, IsoFileSource **root) { if (fs == NULL || root == NULL) { return ISO_NULL_POINTER; } return mocked_file_source_new(fs->data, root); } static int mocked_get_by_path(IsoFilesystem *fs, const char *path, IsoFileSource **file) { struct mock_file *f; if (fs == NULL || path == NULL || file == NULL) { return ISO_NULL_POINTER; } f = path_to_node(fs, path); return mocked_file_source_new(f, file); } static void free_mocked_file(struct mock_file *file) { if (S_ISDIR(file->atts.st_mode)) { if (file->content) { int i = 0; while (((struct mock_file**)file->content)[i]) { free_mocked_file(((struct mock_file**)file->content)[i]); ++i; } } } free(file->content); free(file->name); free(file); } static void mocked_fs_free(IsoFilesystem *fs) { free_mocked_file((struct mock_file *)fs->data); } int test_mocked_filesystem_new(IsoFilesystem **fs) { struct mock_file *root; IsoFilesystem *filesystem; if (fs == NULL) { return ISO_NULL_POINTER; } root = calloc(1, sizeof(struct mock_file)); root->atts.st_atime = time(NULL); root->atts.st_ctime = time(NULL); root->atts.st_mtime = time(NULL); root->atts.st_uid = 0; root->atts.st_gid = 0; root->atts.st_mode = S_IFDIR | 0777; filesystem = malloc(sizeof(IsoFilesystem)); filesystem->refcount = 1; root->fs = filesystem; filesystem->data = root; filesystem->get_root = mocked_get_root; filesystem->get_by_path = mocked_get_by_path; filesystem->free = mocked_fs_free; *fs = filesystem; return ISO_SUCCESS; } /* * Mocked objects to simulate an input filesystem. */ #ifndef MOCKED_FSRC_H_ #define MOCKED_FSRC_H_ struct mock_file { IsoFilesystem *fs; struct mock_file *parent; struct stat atts; char *name; /* for links, link dest. For dirs, children */ void *content; }; /** * A mocked fs. */ int test_mocked_filesystem_new(IsoFilesystem **fs); struct mock_file *test_mocked_fs_get_root(IsoFilesystem *fs); int test_mocked_fs_add_dir(const char *name, struct mock_file *parent, struct stat atts, struct mock_file **dir); int test_mocked_fs_add_symlink(const char *name, struct mock_file *p, struct stat atts, const char *dest, struct mock_file **node); #endif /*MOCKED_FSRC_H_*/ #include "test.h" static void create_test_suite() { add_node_suite(); add_image_suite(); add_tree_suite(); add_util_suite(); add_rockridge_suite(); add_stream_suite(); } int main(int argc, char **argv) { /* initialize the CUnit test registry */ if (CUE_SUCCESS != CU_initialize_registry()) return CU_get_error(); create_test_suite(); /* Run all tests using the console interface */ CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); CU_cleanup_registry(); return CU_get_error(); } #ifndef TEST_H_ #define TEST_H_ #include #include "libisofs.h" void add_node_suite(); void add_image_suite(); void add_tree_suite(); void add_util_suite(); void add_rockridge_suite(); void add_stream_suite(); #endif /*TEST_H_*/ /* * Unit test for image.h */ #include "libisofs.h" #include "test.h" #include "image.h" #include #include #include #include #include #include static void test_iso_image_new() { int ret; IsoImage *image; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NOT_NULL(image); CU_ASSERT_EQUAL(image->refcount, 1); CU_ASSERT_PTR_NOT_NULL(image->root); CU_ASSERT_STRING_EQUAL(image->volume_id, "volume_id"); CU_ASSERT_STRING_EQUAL(image->volset_id, "volume_id"); CU_ASSERT_PTR_NULL(image->publisher_id); CU_ASSERT_PTR_NULL(image->data_preparer_id); CU_ASSERT_PTR_NULL(image->system_id); CU_ASSERT_PTR_NULL(image->application_id); CU_ASSERT_PTR_NULL(image->copyright_file_id); CU_ASSERT_PTR_NULL(image->abstract_file_id); CU_ASSERT_PTR_NULL(image->biblio_file_id); //CU_ASSERT_PTR_NULL(image->bootcat); iso_image_unref(image); } static void test_iso_image_set_volume_id() { int ret; IsoImage *image; char *volid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_STRING_EQUAL(image->volume_id, "volume_id"); volid = "new volume id"; iso_image_set_volume_id(image, volid); CU_ASSERT_STRING_EQUAL(image->volume_id, "new volume id"); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL(image->volume_id, volid); iso_image_unref(image); } static void test_iso_image_get_volume_id() { int ret; IsoImage *image; char *volid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_STRING_EQUAL(iso_image_get_volume_id(image), "volume_id"); volid = "new volume id"; iso_image_set_volume_id(image, volid); CU_ASSERT_STRING_EQUAL( iso_image_get_volume_id(image), "new volume id" ); iso_image_unref(image); } static void test_iso_image_set_publisher_id() { int ret; IsoImage *image; char *pubid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(image->publisher_id); pubid = "new publisher id"; iso_image_set_publisher_id(image, pubid); CU_ASSERT_STRING_EQUAL( image->publisher_id, "new publisher id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( image->publisher_id, pubid ); iso_image_unref(image); } static void test_iso_image_get_publisher_id() { int ret; IsoImage *image; char *pubid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(image->publisher_id); pubid = "new publisher id"; iso_image_set_publisher_id(image, pubid); CU_ASSERT_STRING_EQUAL(iso_image_get_publisher_id(image), "new publisher id"); iso_image_unref(image); } static void test_iso_image_set_data_preparer_id() { int ret; IsoImage *image; char *dpid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(image->data_preparer_id); dpid = "new data preparer id"; iso_image_set_data_preparer_id(image, dpid); CU_ASSERT_STRING_EQUAL(image->data_preparer_id, "new data preparer id"); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL(image->data_preparer_id, dpid); iso_image_unref(image); } static void test_iso_image_get_data_preparer_id() { int ret; IsoImage *image; char *dpid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(image->data_preparer_id); dpid = "new data preparer id"; iso_image_set_data_preparer_id(image, dpid); CU_ASSERT_STRING_EQUAL( iso_image_get_data_preparer_id(image), "new data preparer id" ); iso_image_unref(image); } static void test_iso_image_set_system_id() { int ret; IsoImage *image; char *sysid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(image->system_id); sysid = "new system id"; iso_image_set_system_id(image, sysid); CU_ASSERT_STRING_EQUAL( image->system_id, "new system id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( image->system_id, sysid ); iso_image_unref(image); } static void test_iso_image_get_system_id() { int ret; IsoImage *image; char *sysid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(iso_image_get_system_id(image)); sysid = "new system id"; iso_image_set_system_id(image, sysid); CU_ASSERT_STRING_EQUAL( iso_image_get_system_id(image), "new system id" ); iso_image_unref(image); } static void test_iso_image_set_application_id() { int ret; IsoImage *image; char *appid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(image->application_id); appid = "new application id"; iso_image_set_application_id(image, appid); CU_ASSERT_STRING_EQUAL( image->application_id, "new application id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( image->application_id, appid ); iso_image_unref(image); } static void test_iso_image_get_application_id() { int ret; IsoImage *image; char *appid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(iso_image_get_application_id(image)); appid = "new application id"; iso_image_set_application_id(image, appid); CU_ASSERT_STRING_EQUAL( iso_image_get_application_id(image), "new application id" ); iso_image_unref(image); } static void test_iso_image_set_copyright_file_id() { int ret; IsoImage *image; char *copid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(image->copyright_file_id); copid = "new copyright id"; iso_image_set_copyright_file_id(image, copid); CU_ASSERT_STRING_EQUAL( image->copyright_file_id, "new copyright id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( image->copyright_file_id, copid ); iso_image_unref(image); } static void test_iso_image_get_copyright_file_id() { int ret; IsoImage *image; char *copid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(iso_image_get_copyright_file_id(image)); copid = "new copyright id"; iso_image_set_copyright_file_id(image, copid); CU_ASSERT_STRING_EQUAL( iso_image_get_copyright_file_id(image), "new copyright id" ); iso_image_unref(image); } static void test_iso_image_set_abstract_file_id() { int ret; IsoImage *image; char *absid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(image->abstract_file_id); absid = "new abstract id"; iso_image_set_abstract_file_id(image, absid); CU_ASSERT_STRING_EQUAL( image->abstract_file_id, "new abstract id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( image->abstract_file_id, absid ); iso_image_unref(image); } static void test_iso_image_get_abstract_file_id() { int ret; IsoImage *image; char *absid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(iso_image_get_abstract_file_id(image)); absid = "new abstract id"; iso_image_set_abstract_file_id(image, absid); CU_ASSERT_STRING_EQUAL(iso_image_get_abstract_file_id(image), "new abstract id"); iso_image_unref(image); } static void test_iso_image_set_biblio_file_id() { int ret; IsoImage *image; char *bibid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(image->biblio_file_id); bibid = "new biblio id"; iso_image_set_biblio_file_id(image, bibid); CU_ASSERT_STRING_EQUAL( image->biblio_file_id, "new biblio id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( image->biblio_file_id, bibid ); iso_image_unref(image); } static void test_iso_image_get_biblio_file_id() { int ret; IsoImage *image; char *bibid; ret = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_PTR_NULL(iso_image_get_biblio_file_id(image)); bibid = "new biblio id"; iso_image_set_biblio_file_id(image, bibid); CU_ASSERT_STRING_EQUAL(iso_image_get_biblio_file_id(image), "new biblio id"); iso_image_unref(image); } void add_image_suite() { CU_pSuite pSuite = CU_add_suite("imageSuite", NULL, NULL); CU_add_test(pSuite, "iso_image_new()", test_iso_image_new); CU_add_test(pSuite, "iso_image_set_volume_id()", test_iso_image_set_volume_id); CU_add_test(pSuite, "iso_image_get_volume_id()", test_iso_image_get_volume_id); CU_add_test(pSuite, "iso_image_set_publisher_id()", test_iso_image_set_publisher_id); CU_add_test(pSuite, "iso_image_get_publisher_id()", test_iso_image_get_publisher_id); CU_add_test(pSuite, "iso_image_set_data_preparer_id()", test_iso_image_set_data_preparer_id); CU_add_test(pSuite, "iso_image_get_data_preparer_id()", test_iso_image_get_data_preparer_id); CU_add_test(pSuite, "iso_image_set_system_id()", test_iso_image_set_system_id); CU_add_test(pSuite, "iso_image_get_system_id()", test_iso_image_get_system_id); CU_add_test(pSuite, "iso_image_set_application_id()", test_iso_image_set_application_id); CU_add_test(pSuite, "iso_image_get_application_id()", test_iso_image_get_application_id); CU_add_test(pSuite, "iso_image_set_copyright_file_id()", test_iso_image_set_copyright_file_id); CU_add_test(pSuite, "iso_image_get_copyright_file_id()", test_iso_image_get_copyright_file_id); CU_add_test(pSuite, "iso_image_set_abstract_file_id()", test_iso_image_set_abstract_file_id); CU_add_test(pSuite, "iso_image_get_abstract_file_id()", test_iso_image_get_abstract_file_id); CU_add_test(pSuite, "iso_image_set_biblio_file_id()", test_iso_image_set_biblio_file_id); CU_add_test(pSuite, "iso_image_get_biblio_file_id()", test_iso_image_get_biblio_file_id); } /* * Unit test for node.h */ #include "libisofs.h" #include "node.h" #include "test.h" #include static void test_iso_node_new_root() { int ret; IsoDir *dir; ret = iso_node_new_root(&dir); CU_ASSERT_EQUAL(ret, ISO_SUCCESS); CU_ASSERT_EQUAL(dir->node.refcount, 1); CU_ASSERT_EQUAL(dir->node.type, LIBISO_DIR); CU_ASSERT_EQUAL(dir->node.mode, S_IFDIR | 0555); CU_ASSERT_EQUAL(dir->node.uid, 0); CU_ASSERT_EQUAL(dir->node.gid, 0); CU_ASSERT_PTR_NULL(dir->node.name); CU_ASSERT_EQUAL(dir->node.hidden, 0); CU_ASSERT_PTR_EQUAL(dir->node.parent, dir); CU_ASSERT_PTR_NULL(dir->node.next); CU_ASSERT_EQUAL(dir->nchildren, 0); CU_ASSERT_PTR_NULL(dir->children); iso_node_unref((IsoNode*)dir); } static void test_iso_node_new_dir() { int ret; IsoDir *dir; char *name; name = strdup("name1"); ret = iso_node_new_dir(name, &dir); CU_ASSERT_EQUAL(ret, ISO_SUCCESS); CU_ASSERT_EQUAL(dir->node.refcount, 1); CU_ASSERT_EQUAL(dir->node.type, LIBISO_DIR); CU_ASSERT_EQUAL(dir->node.mode, S_IFDIR); CU_ASSERT_EQUAL(dir->node.uid, 0); CU_ASSERT_EQUAL(dir->node.gid, 0); CU_ASSERT_EQUAL(dir->node.atime, 0); CU_ASSERT_EQUAL(dir->node.mtime, 0); CU_ASSERT_EQUAL(dir->node.ctime, 0); CU_ASSERT_STRING_EQUAL(dir->node.name, "name1"); CU_ASSERT_EQUAL(dir->node.hidden, 0); CU_ASSERT_PTR_NULL(dir->node.parent); CU_ASSERT_PTR_NULL(dir->node.next); CU_ASSERT_EQUAL(dir->nchildren, 0); CU_ASSERT_PTR_NULL(dir->children); iso_node_unref((IsoNode*)dir); /* try with invalid names */ ret = iso_node_new_dir("H/DHS/s", &dir); CU_ASSERT_EQUAL(ret, ISO_WRONG_ARG_VALUE); ret = iso_node_new_dir(".", &dir); CU_ASSERT_EQUAL(ret, ISO_WRONG_ARG_VALUE); ret = iso_node_new_dir("..", &dir); CU_ASSERT_EQUAL(ret, ISO_WRONG_ARG_VALUE); ret = iso_node_new_dir(NULL, &dir); CU_ASSERT_EQUAL(ret, ISO_NULL_POINTER); } static void test_iso_node_new_symlink() { int ret; IsoSymlink *link; char *name, *dest; name = strdup("name1"); dest = strdup("/home"); ret = iso_node_new_symlink(name, dest, &link); CU_ASSERT_EQUAL(ret, ISO_SUCCESS); CU_ASSERT_EQUAL(link->node.refcount, 1); CU_ASSERT_EQUAL(link->node.type, LIBISO_SYMLINK); CU_ASSERT_EQUAL(link->node.mode, S_IFLNK); CU_ASSERT_EQUAL(link->node.uid, 0); CU_ASSERT_EQUAL(link->node.gid, 0); CU_ASSERT_EQUAL(link->node.atime, 0); CU_ASSERT_EQUAL(link->node.mtime, 0); CU_ASSERT_EQUAL(link->node.ctime, 0); CU_ASSERT_STRING_EQUAL(link->node.name, "name1"); CU_ASSERT_EQUAL(link->node.hidden, 0); CU_ASSERT_PTR_NULL(link->node.parent); CU_ASSERT_PTR_NULL(link->node.next); CU_ASSERT_STRING_EQUAL(link->dest, "/home"); iso_node_unref((IsoNode*)link); /* try with invalid names */ ret = iso_node_new_symlink("H/DHS/s", "/home", &link); CU_ASSERT_EQUAL(ret, ISO_WRONG_ARG_VALUE); ret = iso_node_new_symlink(".", "/home", &link); CU_ASSERT_EQUAL(ret, ISO_WRONG_ARG_VALUE); ret = iso_node_new_symlink("..", "/home", &link); CU_ASSERT_EQUAL(ret, ISO_WRONG_ARG_VALUE); } static void test_iso_node_set_permissions() { IsoNode *node; node = malloc(sizeof(IsoNode)); node->mode = S_IFDIR | 0777; /* set permissions propertly */ iso_node_set_permissions(node, 0555); CU_ASSERT_EQUAL(node->mode, S_IFDIR | 0555); iso_node_set_permissions(node, 0640); CU_ASSERT_EQUAL(node->mode, S_IFDIR | 0640); /* try to change file type via this call */ iso_node_set_permissions(node, S_IFBLK | 0440); CU_ASSERT_EQUAL(node->mode, S_IFDIR | 0440); free(node); } static void test_iso_node_get_permissions() { IsoNode *node; mode_t mode; node = malloc(sizeof(IsoNode)); node->mode = S_IFDIR | 0777; mode = iso_node_get_permissions(node); CU_ASSERT_EQUAL(mode, 0777); iso_node_set_permissions(node, 0640); mode = iso_node_get_permissions(node); CU_ASSERT_EQUAL(mode, 0640); iso_node_set_permissions(node, S_IFBLK | 0440); mode = iso_node_get_permissions(node); CU_ASSERT_EQUAL(mode, 0440); free(node); } static void test_iso_node_get_mode() { IsoNode *node; mode_t mode; node = malloc(sizeof(IsoNode)); node->mode = S_IFDIR | 0777; mode = iso_node_get_mode(node); CU_ASSERT_EQUAL(mode, S_IFDIR | 0777); iso_node_set_permissions(node, 0640); mode = iso_node_get_mode(node); CU_ASSERT_EQUAL(mode, S_IFDIR | 0640); iso_node_set_permissions(node, S_IFBLK | 0440); mode = iso_node_get_mode(node); CU_ASSERT_EQUAL(mode, S_IFDIR | 0440); free(node); } static void test_iso_node_set_uid() { IsoNode *node; node = malloc(sizeof(IsoNode)); node->uid = 0; iso_node_set_uid(node, 23); CU_ASSERT_EQUAL(node->uid, 23); iso_node_set_uid(node, 0); CU_ASSERT_EQUAL(node->uid, 0); free(node); } static void test_iso_node_get_uid() { IsoNode *node; uid_t uid; node = malloc(sizeof(IsoNode)); node->uid = 0; uid = iso_node_get_uid(node); CU_ASSERT_EQUAL(uid, 0); iso_node_set_uid(node, 25); uid = iso_node_get_uid(node); CU_ASSERT_EQUAL(uid, 25); free(node); } static void test_iso_node_set_gid() { IsoNode *node; node = malloc(sizeof(IsoNode)); node->gid = 0; iso_node_set_gid(node, 23); CU_ASSERT_EQUAL(node->gid, 23); iso_node_set_gid(node, 0); CU_ASSERT_EQUAL(node->gid, 0); free(node); } static void test_iso_node_get_gid() { IsoNode *node; gid_t gid; node = malloc(sizeof(IsoNode)); node->gid = 0; gid = iso_node_get_gid(node); CU_ASSERT_EQUAL(gid, 0); iso_node_set_gid(node, 25); gid = iso_node_get_gid(node); CU_ASSERT_EQUAL(gid, 25); free(node); } static void test_iso_dir_add_node() { int result; IsoDir *dir; IsoNode *node1, *node2, *node3, *node4, *node5; /* init dir with default values, not all field need to be initialized */ dir = malloc(sizeof(IsoDir)); dir->children = NULL; dir->nchildren = 0; /* 1st node to be added */ node1 = calloc(1, sizeof(IsoNode)); node1->name = "Node1"; /* addition of node to an empty dir */ result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->nchildren, 1); CU_ASSERT_PTR_EQUAL(dir->children, node1); CU_ASSERT_PTR_NULL(node1->next); CU_ASSERT_PTR_EQUAL(node1->parent, dir); /* addition of a node, to be inserted before */ node2 = calloc(1, sizeof(IsoNode)); node2->name = "A node to be added first"; result = iso_dir_add_node(dir, node2, 0); CU_ASSERT_EQUAL(result, 2); CU_ASSERT_EQUAL(dir->nchildren, 2); CU_ASSERT_PTR_EQUAL(dir->children, node2); CU_ASSERT_PTR_EQUAL(node2->next, node1); CU_ASSERT_PTR_NULL(node1->next); CU_ASSERT_PTR_EQUAL(node2->parent, dir); /* addition of a node, to be inserted last */ node3 = calloc(1, sizeof(IsoNode)); node3->name = "This node will be inserted last"; result = iso_dir_add_node(dir, node3, 0); CU_ASSERT_EQUAL(result, 3); CU_ASSERT_EQUAL(dir->nchildren, 3); CU_ASSERT_PTR_EQUAL(dir->children, node2); CU_ASSERT_PTR_EQUAL(node2->next, node1); CU_ASSERT_PTR_EQUAL(node1->next, node3); CU_ASSERT_PTR_NULL(node3->next); CU_ASSERT_PTR_EQUAL(node3->parent, dir); /* force some failures */ result = iso_dir_add_node(NULL, node3, 0); CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); result = iso_dir_add_node(dir, NULL, 0); CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); result = iso_dir_add_node(dir, (IsoNode*)dir, 0); CU_ASSERT_EQUAL(result, ISO_WRONG_ARG_VALUE); /* a node with same name */ node4 = calloc(1, sizeof(IsoNode)); node4->name = "This node will be inserted last"; result = iso_dir_add_node(dir, node4, 0); CU_ASSERT_EQUAL(result, ISO_NODE_NAME_NOT_UNIQUE); CU_ASSERT_EQUAL(dir->nchildren, 3); CU_ASSERT_PTR_EQUAL(dir->children, node2); CU_ASSERT_PTR_EQUAL(node2->next, node1); CU_ASSERT_PTR_EQUAL(node1->next, node3); CU_ASSERT_PTR_NULL(node3->next); CU_ASSERT_PTR_NULL(node4->parent); /* a node already added to another dir should fail */ node5 = calloc(1, sizeof(IsoNode)); node5->name = "other node"; node5->parent = (IsoDir*)node4; result = iso_dir_add_node(dir, node5, 0); CU_ASSERT_EQUAL(result, ISO_NODE_ALREADY_ADDED); free(node1); free(node2); free(node3); free(node4); free(node5); free(dir); } static void test_iso_dir_get_node() { int result; IsoDir *dir; IsoNode *node1, *node2, *node3; IsoNode *node; /* init dir with default values, not all field need to be initialized */ dir = malloc(sizeof(IsoDir)); dir->children = NULL; dir->nchildren = 0; /* try to find a node in an empty dir */ result = iso_dir_get_node(dir, "a inexistent name", &node); CU_ASSERT_EQUAL(result, 0); CU_ASSERT_PTR_NULL(node); /* add a node */ node1 = calloc(1, sizeof(IsoNode)); node1->name = "Node1"; result = iso_dir_add_node(dir, node1, 0); /* try to find a node not existent */ result = iso_dir_get_node(dir, "a inexistent name", &node); CU_ASSERT_EQUAL(result, 0); CU_ASSERT_PTR_NULL(node); /* and an existing one */ result = iso_dir_get_node(dir, "Node1", &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node1); /* add another node */ node2 = calloc(1, sizeof(IsoNode)); node2->name = "A node to be added first"; result = iso_dir_add_node(dir, node2, 0); /* try to find a node not existent */ result = iso_dir_get_node(dir, "a inexistent name", &node); CU_ASSERT_EQUAL(result, 0); CU_ASSERT_PTR_NULL(node); /* and the two existing */ result = iso_dir_get_node(dir, "Node1", &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node1); result = iso_dir_get_node(dir, "A node to be added first", &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node2); /* insert another node */ node3 = calloc(1, sizeof(IsoNode)); node3->name = "This node will be inserted last"; result = iso_dir_add_node(dir, node3, 0); /* get again */ result = iso_dir_get_node(dir, "a inexistent name", &node); CU_ASSERT_EQUAL(result, 0); CU_ASSERT_PTR_NULL(node); result = iso_dir_get_node(dir, "This node will be inserted last", &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node3); /* force some failures */ result = iso_dir_get_node(NULL, "asas", &node); CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); result = iso_dir_get_node(dir, NULL, &node); CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); /* and try with null node */ result = iso_dir_get_node(dir, "asas", NULL); CU_ASSERT_EQUAL(result, 0); result = iso_dir_get_node(dir, "This node will be inserted last", NULL); CU_ASSERT_EQUAL(result, 1); free(node1); free(node2); free(node3); free(dir); } static void test_iso_dir_get_children() { int result; IsoDirIter *iter; IsoDir *dir; IsoNode *node, *node1, *node2, *node3; /* init dir with default values, not all field need to be initialized */ dir = malloc(sizeof(IsoDir)); dir->node.refcount = 1; dir->children = NULL; dir->nchildren = 0; result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->node.refcount, 2); /* item should have no items */ result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 0); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 0); CU_ASSERT_PTR_NULL(node); iso_dir_iter_free(iter); /* 1st node to be added */ node1 = calloc(1, sizeof(IsoNode)); node1->name = "Node1"; node1->refcount = 1; result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(dir->nchildren, 1); /* test iteration again */ result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); /* iter should have a single item... */ result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 1); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node1); /* ...and no more */ result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 0); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 0); CU_ASSERT_PTR_NULL(node); iso_dir_iter_free(iter); /* add another node */ node2 = calloc(1, sizeof(IsoNode)); node2->name = "A node to be added first"; node2->refcount = 1; result = iso_dir_add_node(dir, node2, 0); CU_ASSERT_EQUAL(result, 2); result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); /* iter should have two items... */ result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 1); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node2); result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 1); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node1); /* ...and no more */ result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 0); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 0); CU_ASSERT_PTR_NULL(node); iso_dir_iter_free(iter); /* addition of a 3rd node, to be inserted last */ node3 = calloc(1, sizeof(IsoNode)); node3->name = "This node will be inserted last"; node3->refcount = 1; result = iso_dir_add_node(dir, node3, 0); CU_ASSERT_EQUAL(result, 3); result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); /* iter should have 3 items... */ result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 1); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node2); result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 1); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node1); result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 1); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node3); /* ...and no more */ result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 0); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 0); CU_ASSERT_PTR_NULL(node); iso_dir_iter_free(iter); CU_ASSERT_EQUAL(dir->node.refcount, 1); /* assert correct refcount */ CU_ASSERT_EQUAL(node1->refcount, 1); free(node1); CU_ASSERT_EQUAL(node2->refcount, 1); free(node2); CU_ASSERT_EQUAL(node3->refcount, 1); free(node3); free(dir); } static void test_iso_dir_iter_take() { int result; IsoDirIter *iter; IsoDir *dir; IsoNode *node, *node1, *node2, *node3; /* init dir with default values, not all field need to be initialized */ dir = malloc(sizeof(IsoDir)); dir->node.refcount = 1; dir->children = NULL; dir->nchildren = 0; result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->node.refcount, 2); /* remove on empty dir! */ result = iso_dir_iter_take(iter); CU_ASSERT_TRUE(result < 0); /* should fail */ iso_dir_iter_free(iter); /* 1st node to be added */ node1 = calloc(1, sizeof(IsoNode)); node1->name = "Node1"; node1->refcount = 1; result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(dir->nchildren, 1); /* test iteration again */ result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); /* remove before iso_dir_iter_next() */ result = iso_dir_iter_take(iter); CU_ASSERT_TRUE(result < 0); /* should fail */ result = iso_dir_iter_next(iter, &node); /* this should remove the child */ result = iso_dir_iter_take(iter); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->nchildren, 0); CU_ASSERT_PTR_NULL(dir->children); result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 0); iso_dir_iter_free(iter); /* add two node */ result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(dir->nchildren, 1); node2 = calloc(1, sizeof(IsoNode)); node2->name = "A node to be added first"; node2->refcount = 1; result = iso_dir_add_node(dir, node2, 0); CU_ASSERT_EQUAL(result, 2); result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); /* remove before iso_dir_iter_next() */ result = iso_dir_iter_take(iter); CU_ASSERT_TRUE(result < 0); /* should fail */ /* iter should have two items... */ result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node2); /* remove node 2 */ result = iso_dir_iter_take(iter); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->nchildren, 1); CU_ASSERT_PTR_EQUAL(dir->children, node1); /* we can't take two times without next()!! */ result = iso_dir_iter_take(iter); CU_ASSERT_TRUE(result < 0); /* should fail */ /* next should still work */ result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node1); /* ...and no more */ result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 0); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 0); CU_ASSERT_PTR_NULL(node); iso_dir_iter_free(iter); /* now remove only last child */ result = iso_dir_add_node(dir, node2, 0); CU_ASSERT_EQUAL(result, 2); result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node2); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node1); /* take last child */ result = iso_dir_iter_take(iter); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->nchildren, 1); CU_ASSERT_PTR_EQUAL(dir->children, node2); result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 0); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 0); CU_ASSERT_PTR_NULL(node); iso_dir_iter_free(iter); /* Ok, now another situation. Modification of dir during iteration */ result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(result, 2); result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node2); /* returned dir is node2, it should be the node taken next, but * let's insert a node after node2 and before node1 */ node3 = calloc(1, sizeof(IsoNode)); node3->name = "A node to be added second"; node3->refcount = 1; result = iso_dir_add_node(dir, node3, 0); CU_ASSERT_EQUAL(dir->nchildren, 3); /* is the node 2 the removed one? */ result = iso_dir_iter_take(iter); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->nchildren, 2); CU_ASSERT_PTR_EQUAL(dir->children, node3); CU_ASSERT_PTR_EQUAL(node3->next, node1); iso_dir_iter_free(iter); CU_ASSERT_EQUAL(dir->node.refcount, 1); /* assert correct refcount */ CU_ASSERT_EQUAL(node1->refcount, 1); free(node1); CU_ASSERT_EQUAL(node2->refcount, 1); free(node2); CU_ASSERT_EQUAL(node3->refcount, 1); free(node3); free(dir); } static void test_iso_dir_iter_remove() { int result; IsoDirIter *iter; IsoDir *dir; IsoNode *node, *node1, *node2, *node3; /* init dir with default values, not all field need to be initialized */ dir = malloc(sizeof(IsoDir)); dir->node.refcount = 1; dir->children = NULL; dir->nchildren = 0; result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); /* remove on empty dir! */ result = iso_dir_iter_remove(iter); CU_ASSERT_TRUE(result < 0); /* should fail */ iso_dir_iter_free(iter); /* 1st node to be added */ node1 = calloc(1, sizeof(IsoNode)); node1->name = "Node1"; node1->refcount = 2; result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(dir->nchildren, 1); /* test iteration again */ result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); /* remove before iso_dir_iter_next() */ result = iso_dir_iter_remove(iter); CU_ASSERT_TRUE(result < 0); /* should fail */ result = iso_dir_iter_next(iter, &node); /* this should remove the child */ result = iso_dir_iter_remove(iter); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->nchildren, 0); CU_ASSERT_PTR_NULL(dir->children); result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 0); iso_dir_iter_free(iter); /* add two node */ node1->refcount++; /* was removed above */ result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(dir->nchildren, 1); node2 = calloc(1, sizeof(IsoNode)); node2->name = "A node to be added first"; node2->refcount = 2; result = iso_dir_add_node(dir, node2, 0); CU_ASSERT_EQUAL(result, 2); result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); /* remove before iso_dir_iter_next() */ result = iso_dir_iter_remove(iter); CU_ASSERT_TRUE(result < 0); /* should fail */ /* iter should have two items... */ result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node2); /* remove node 2 */ result = iso_dir_iter_remove(iter); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->nchildren, 1); CU_ASSERT_PTR_EQUAL(dir->children, node1); /* next should still work */ result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node1); /* ...and no more */ result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 0); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 0); CU_ASSERT_PTR_NULL(node); iso_dir_iter_free(iter); /* now remove only last child */ node2->refcount++; /* was removed above */ result = iso_dir_add_node(dir, node2, 0); CU_ASSERT_EQUAL(result, 2); result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node2); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node1); /* take last child */ result = iso_dir_iter_remove(iter); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->nchildren, 1); CU_ASSERT_PTR_EQUAL(dir->children, node2); result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 0); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 0); CU_ASSERT_PTR_NULL(node); iso_dir_iter_free(iter); /* Ok, now another situation. Modification of dir during iteration */ node1->refcount++; result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(result, 2); result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node2); /* returned dir is node2, it should be the node taken next, but * let's insert a node after node2 and before node1 */ node3 = calloc(1, sizeof(IsoNode)); node3->name = "A node to be added second"; node3->refcount = 2; result = iso_dir_add_node(dir, node3, 0); CU_ASSERT_EQUAL(dir->nchildren, 3); /* is the node 2 the removed one? */ result = iso_dir_iter_remove(iter); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->nchildren, 2); CU_ASSERT_PTR_EQUAL(dir->children, node3); CU_ASSERT_PTR_EQUAL(node3->next, node1); iso_dir_iter_free(iter); /* assert correct refcount */ CU_ASSERT_EQUAL(node1->refcount, 2); /* node1 is not removed */ free(node1); CU_ASSERT_EQUAL(node2->refcount, 1); free(node2); CU_ASSERT_EQUAL(node3->refcount, 2); /* node3 is not removed */ free(node3); free(dir); } static void test_iso_dir_iter_when_external_take() { int result; IsoDirIter *iter; IsoDir *dir; IsoNode *node, *node1, *node2; /* init dir with default values, not all field need to be initialized */ dir = malloc(sizeof(IsoDir)); dir->node.refcount = 1; dir->children = NULL; dir->nchildren = 0; /* 1st node to be added */ node1 = calloc(1, sizeof(IsoNode)); node1->name = "Node1"; node1->refcount = 1; result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(dir->nchildren, 1); /* test iteration again */ result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); /* take node */ result = iso_node_take(node1); CU_ASSERT_EQUAL(result, 1); /* iter should reflect changes */ result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 0); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 0); iso_dir_iter_free(iter); /* add two nodes */ result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(dir->nchildren, 1); node2 = calloc(1, sizeof(IsoNode)); node2->name = "A node to be added first"; node2->refcount = 1; result = iso_dir_add_node(dir, node2, 0); CU_ASSERT_EQUAL(result, 2); result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); /* iter should have two items... */ result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node2); /* remove node 1 asynchronously */ result = iso_node_take(node1); CU_ASSERT_EQUAL(result, 1); /* iter should reflect changes */ result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 0); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 0); iso_dir_iter_free(iter); /* now remove iter has itered */ result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(dir->nchildren, 2); result = iso_dir_get_children(dir, &iter); CU_ASSERT_EQUAL(result, 1); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node2); /* remove node 2 asynchronously */ result = iso_node_take(node2); CU_ASSERT_EQUAL(result, 1); /* iter should reflect changes */ result = iso_dir_iter_has_next(iter); CU_ASSERT_EQUAL(result, 1); result = iso_dir_iter_next(iter, &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node1); iso_dir_iter_free(iter); /* assert correct refcount */ CU_ASSERT_EQUAL(node1->refcount, 1); free(node1); CU_ASSERT_EQUAL(node2->refcount, 1); free(node2); free(dir); } static void test_iso_node_take() { int result; IsoDir *dir; IsoNode *node1, *node2, *node3; /* init dir with default values, not all field need to be initialized */ dir = malloc(sizeof(IsoDir)); dir->children = NULL; dir->nchildren = 0; /* 1st node to be added */ node1 = calloc(1, sizeof(IsoNode)); node1->name = "Node1"; node1->refcount = 1; /* addition of node to an empty dir */ result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(result, 1); /* and take it */ result = iso_node_take(node1); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->nchildren, 0); CU_ASSERT_PTR_NULL(dir->children); CU_ASSERT_PTR_NULL(node1->next); CU_ASSERT_PTR_NULL(node1->parent); /* insert it again */ result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(result, 1); /* addition of a 2nd node, to be inserted before */ node2 = calloc(1, sizeof(IsoNode)); node2->name = "A node to be added first"; node2->refcount = 1; result = iso_dir_add_node(dir, node2, 0); CU_ASSERT_EQUAL(result, 2); /* take first child */ result = iso_node_take(node2); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->nchildren, 1); CU_ASSERT_PTR_EQUAL(dir->children, node1); CU_ASSERT_PTR_NULL(node1->next); CU_ASSERT_PTR_EQUAL(node1->parent, dir); CU_ASSERT_PTR_NULL(node2->next); CU_ASSERT_PTR_NULL(node2->parent); /* insert node 2 again */ result = iso_dir_add_node(dir, node2, 0); CU_ASSERT_EQUAL(result, 2); /* now take last child */ result = iso_node_take(node1); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->nchildren, 1); CU_ASSERT_PTR_EQUAL(dir->children, node2); CU_ASSERT_PTR_NULL(node2->next); CU_ASSERT_PTR_EQUAL(node2->parent, dir); CU_ASSERT_PTR_NULL(node1->next); CU_ASSERT_PTR_NULL(node1->parent); /* insert again node1... */ result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(result, 2); /* ...and a 3rd child, to be inserted last */ node3 = calloc(1, sizeof(IsoNode)); node3->name = "This node will be inserted last"; node3->refcount = 1; result = iso_dir_add_node(dir, node3, 0); CU_ASSERT_EQUAL(result, 3); /* and take the node in the middle */ result = iso_node_take(node1); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(dir->nchildren, 2); CU_ASSERT_PTR_EQUAL(dir->children, node2); CU_ASSERT_PTR_EQUAL(node2->next, node3); CU_ASSERT_PTR_EQUAL(node2->parent, dir); CU_ASSERT_PTR_NULL(node3->next); CU_ASSERT_PTR_EQUAL(node3->parent, dir); CU_ASSERT_PTR_NULL(node1->next); CU_ASSERT_PTR_NULL(node1->parent); /* assert correct refcount */ CU_ASSERT_EQUAL(node1->refcount, 1); free(node1); CU_ASSERT_EQUAL(node2->refcount, 1); free(node2); CU_ASSERT_EQUAL(node3->refcount, 1); free(node3); free(dir); } static void test_iso_node_set_name() { int result; IsoDir *dir; IsoNode *node1, *node2; /* init dir with default values, not all field need to be initialized */ dir = malloc(sizeof(IsoDir)); dir->children = NULL; dir->nchildren = 0; /* cretae a node */ node1 = calloc(1, sizeof(IsoNode)); node1->name = strdup("Node1"); /* check name change */ result = iso_node_set_name(node1, "New name"); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_STRING_EQUAL(node1->name, "New name"); /* add node dir */ result = iso_dir_add_node(dir, node1, 0); CU_ASSERT_EQUAL(result, 1); /* check name change */ result = iso_node_set_name(node1, "Another name"); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_STRING_EQUAL(node1->name, "Another name"); /* addition of a 2nd node */ node2 = calloc(1, sizeof(IsoNode)); node2->name = strdup("A node to be added first"); result = iso_dir_add_node(dir, node2, 0); CU_ASSERT_EQUAL(result, 2); result = iso_node_set_name(node2, "New name"); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_STRING_EQUAL(node2->name, "New name"); /* and now try to give an existing name */ result = iso_node_set_name(node2, "Another name"); CU_ASSERT_EQUAL(result, ISO_NODE_NAME_NOT_UNIQUE); CU_ASSERT_STRING_EQUAL(node2->name, "New name"); free(node1->name); free(node2->name); free(node1); free(node2); free(dir); } static int xinfo_a(void *data, int flag) { return 1; } static int xinfo_b(void *data, int flag) { return 1; } static void test_iso_node_add_xinfo() { int result; IsoNode *node; /* cretae a node */ node = calloc(1, sizeof(IsoNode)); /* add xinfo data */ result = iso_node_add_xinfo(node, xinfo_a, NULL); CU_ASSERT_EQUAL(result, 1); /* we can't add again the same xinfo type */ result = iso_node_add_xinfo(node, xinfo_a, &result); CU_ASSERT_EQUAL(result, 0); result = iso_node_add_xinfo(node, xinfo_b, NULL); CU_ASSERT_EQUAL(result, 1); free(node->xinfo->next); free(node->xinfo); free(node); } static void test_iso_node_get_xinfo() { int result; IsoNode *node; char *one_data = "my data"; char *another_data = "my data 2"; void *data; /* cretae a node */ node = calloc(1, sizeof(IsoNode)); /* at the beginning we have no data */ result = iso_node_get_xinfo(node, xinfo_b, &data); CU_ASSERT_EQUAL(result, 0); result = iso_node_get_xinfo(node, xinfo_a, &data); CU_ASSERT_EQUAL(result, 0); /* add xinfo data */ result = iso_node_add_xinfo(node, xinfo_a, one_data); CU_ASSERT_EQUAL(result, 1); /* we get the correct data */ result = iso_node_get_xinfo(node, xinfo_a, &data); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(data, one_data); /* we can't add again the same xinfo type */ result = iso_node_add_xinfo(node, xinfo_a, &result); CU_ASSERT_EQUAL(result, 0); /* we get the correct data again */ result = iso_node_get_xinfo(node, xinfo_a, &data); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(data, one_data); /* xinfo_b has no data */ result = iso_node_get_xinfo(node, xinfo_b, &data); CU_ASSERT_EQUAL(result, 0); /* add another xinfo */ result = iso_node_add_xinfo(node, xinfo_b, another_data); CU_ASSERT_EQUAL(result, 1); /* test both data is returned propertly */ result = iso_node_get_xinfo(node, xinfo_a, &data); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(data, one_data); result = iso_node_get_xinfo(node, xinfo_b, &data); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(data, another_data); free(node->xinfo->next); free(node->xinfo); free(node); } static void test_iso_node_remove_xinfo() { int result; IsoNode *node; char *one_data = "my data"; char *another_data = "my data 2"; void *data; /* cretae a node */ node = calloc(1, sizeof(IsoNode)); /* try to remove inexistent data */ result = iso_node_remove_xinfo(node, xinfo_b); CU_ASSERT_EQUAL(result, 0); result = iso_node_remove_xinfo(node, xinfo_a); CU_ASSERT_EQUAL(result, 0); /* add xinfo data */ result = iso_node_add_xinfo(node, xinfo_a, one_data); CU_ASSERT_EQUAL(result, 1); result = iso_node_get_xinfo(node, xinfo_a, &data); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(data, one_data); /* remove it */ result = iso_node_remove_xinfo(node, xinfo_b); CU_ASSERT_EQUAL(result, 0); result = iso_node_remove_xinfo(node, xinfo_a); CU_ASSERT_EQUAL(result, 1); result = iso_node_get_xinfo(node, xinfo_a, &data); CU_ASSERT_EQUAL(result, 0); /* add again the same xinfo type */ result = iso_node_add_xinfo(node, xinfo_a, one_data); CU_ASSERT_EQUAL(result, 1); /* we get the correct data again */ result = iso_node_get_xinfo(node, xinfo_a, &data); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(data, one_data); /* add another xinfo */ result = iso_node_add_xinfo(node, xinfo_b, another_data); CU_ASSERT_EQUAL(result, 1); /* test both data is returned propertly */ result = iso_node_get_xinfo(node, xinfo_a, &data); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(data, one_data); result = iso_node_get_xinfo(node, xinfo_b, &data); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(data, another_data); /* remove b */ result = iso_node_remove_xinfo(node, xinfo_b); CU_ASSERT_EQUAL(result, 1); /* only a can be get */ result = iso_node_get_xinfo(node, xinfo_a, &data); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(data, one_data); result = iso_node_get_xinfo(node, xinfo_b, &data); CU_ASSERT_EQUAL(result, 0); /* try to remove b again */ result = iso_node_remove_xinfo(node, xinfo_b); CU_ASSERT_EQUAL(result, 0); /* remove a */ result = iso_node_remove_xinfo(node, xinfo_a); CU_ASSERT_EQUAL(result, 1); result = iso_node_get_xinfo(node, xinfo_a, &data); CU_ASSERT_EQUAL(result, 0); result = iso_node_get_xinfo(node, xinfo_b, &data); CU_ASSERT_EQUAL(result, 0); free(node); } void add_node_suite() { CU_pSuite pSuite = CU_add_suite("Node Test Suite", NULL, NULL); CU_add_test(pSuite, "iso_node_new_root()", test_iso_node_new_root); CU_add_test(pSuite, "iso_node_new_dir()", test_iso_node_new_dir); CU_add_test(pSuite, "iso_node_new_symlink()", test_iso_node_new_symlink); CU_add_test(pSuite, "iso_node_set_permissions()", test_iso_node_set_permissions); CU_add_test(pSuite, "iso_node_get_permissions()", test_iso_node_get_permissions); CU_add_test(pSuite, "iso_node_get_mode()", test_iso_node_get_mode); CU_add_test(pSuite, "iso_node_set_uid()", test_iso_node_set_uid); CU_add_test(pSuite, "iso_node_get_uid()", test_iso_node_get_uid); CU_add_test(pSuite, "iso_node_set_gid()", test_iso_node_set_gid); CU_add_test(pSuite, "iso_node_get_gid()", test_iso_node_get_gid); CU_add_test(pSuite, "iso_dir_add_node()", test_iso_dir_add_node); CU_add_test(pSuite, "iso_dir_get_node()", test_iso_dir_get_node); CU_add_test(pSuite, "iso_dir_get_children()", test_iso_dir_get_children); CU_add_test(pSuite, "iso_dir_iter_take()", test_iso_dir_iter_take); CU_add_test(pSuite, "iso_dir_iter_remove()", test_iso_dir_iter_remove); CU_add_test(pSuite, "iso_node_take()", test_iso_node_take); CU_add_test(pSuite, "iso_node_take() during iteration", test_iso_dir_iter_when_external_take); CU_add_test(pSuite, "iso_node_set_name()", test_iso_node_set_name); CU_add_test(pSuite, "iso_node_add_xinfo()", test_iso_node_add_xinfo); CU_add_test(pSuite, "iso_node_get_xinfo()", test_iso_node_get_xinfo); CU_add_test(pSuite, "iso_node_remove_xinfo()", test_iso_node_remove_xinfo); } /* * Unit test for util.h * * This test utiliy functions */ #include "test.h" #include "ecma119_tree.h" #include "rockridge.h" #include "node.h" #include static void test_rrip_calc_len_file() { IsoFile *file; Ecma119Node *node; Ecma119Image t; size_t sua_len = 0, ce_len = 0; memset(&t, 0, sizeof(Ecma119Image)); t.input_charset = "UTF-8"; t.output_charset = "UTF-8"; file = malloc(sizeof(IsoFile)); CU_ASSERT_PTR_NOT_NULL_FATAL(file); file->from_old_session = 0; file->sort_weight = 0; file->stream = NULL; /* it is not needed here */ file->node.type = LIBISO_FILE; node = malloc(sizeof(Ecma119Node)); CU_ASSERT_PTR_NOT_NULL_FATAL(node); node->node = (IsoNode*)file; node->parent = (Ecma119Node*)0x55555555; /* just to make it not NULL */ node->info.file = NULL; /* it is not needed here */ node->type = ECMA119_FILE; /* Case 1. Name fit in System Use field */ file->node.name = "a small name.txt"; node->iso_name = "A_SMALL_.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 0); CU_ASSERT_EQUAL(sua_len, 44 + (5 + 16) + (5 + 3*7) + 1); /* Case 2. Name fits exactly */ file->node.name = "a big name, with 133 characters, that it is the max " "that fits in System Use field of the directory record " "PADPADPADADPADPADPADPAD.txt"; node->iso_name = "A_BIG_NA.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 0); /* note that 254 is the max length of a directory record, as it needs to * be an even number */ CU_ASSERT_EQUAL(sua_len, 254 - 46); /* case 3. A name just 1 character too big to fit in SUA */ file->node.name = "a big name, with 133 characters, that it is the max " "that fits in System Use field of the directory record " "PADPADPADADPADPADPADPAD1.txt"; node->iso_name = "A_BIG_NA.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); /* 28 (the chars moved to include the CE entry) + 5 (header of NM in CE) + * 1 (the char that originally didn't fit) */ CU_ASSERT_EQUAL(ce_len, 28 + 5 + 1); /* note that 254 is the max length of a directory record, as it needs to * be an even number */ CU_ASSERT_EQUAL(sua_len, 254 - 46); /* case 4. A 255 characters name */ file->node.name = "a big name, with 255 characters, that it is the max " "that a POSIX filename can have. PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP"; node->iso_name = "A_BIG_NA.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); /* 150 + 5 (header + characters that don't fit in sua) */ CU_ASSERT_EQUAL(ce_len, 150 + 5); /* note that 254 is the max length of a directory record, as it needs to * be an even number */ CU_ASSERT_EQUAL(sua_len, 254 - 46); free(node); free(file); } static void test_rrip_calc_len_symlink() { IsoSymlink *link; Ecma119Node *node; Ecma119Image t; size_t sua_len = 0, ce_len = 0; memset(&t, 0, sizeof(Ecma119Image)); t.input_charset = "UTF-8"; t.output_charset = "UTF-8"; link = malloc(sizeof(IsoSymlink)); CU_ASSERT_PTR_NOT_NULL_FATAL(link); link->node.type = LIBISO_SYMLINK; node = malloc(sizeof(Ecma119Node)); CU_ASSERT_PTR_NOT_NULL_FATAL(node); node->node = (IsoNode*)link; node->parent = (Ecma119Node*)0x55555555; /* just to make it not NULL */ node->type = ECMA119_SYMLINK; /* Case 1. Name and dest fit in System Use field */ link->node.name = "a small name.txt"; link->dest = "/three/components"; node->iso_name = "A_SMALL_.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 0); CU_ASSERT_EQUAL(sua_len, 44 + (5 + 16) + (5 + 3*7) + 1 + (5 + 2 + (2+5) + (2+10)) ); /* case 2. name + dest fits exactly */ link->node.name = "this name will have 74 characters as it is the max " "that fits in the SU.txt"; link->dest = "./and/../a/./big/destination/with/10/components"; node->iso_name = "THIS_NAM.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 0); CU_ASSERT_EQUAL(sua_len, 254 - 46); /* case 3. name fits, dest is one byte larger to fit */ /* 3.a extra byte in dest */ link->node.name = "this name will have 74 characters as it is the max " "that fits in the SU.txt"; link->dest = "./and/../a/./big/destination/with/10/componentsk"; node->iso_name = "THIS_NAM.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 60); CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); /* 3.b extra byte in name */ link->node.name = "this name will have 75 characters as it is the max " "that fits in the SUx.txt"; link->dest = "./and/../a/./big/destination/with/10/components"; node->iso_name = "THIS_NAM.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 59); CU_ASSERT_EQUAL(sua_len, 44 + (5 + 75) + (5 + 3*7) + 28); /* case 4. name seems to fit, but SL no, and when CE is added NM * doesn't fit too */ /* 4.a it just fits */ link->node.name = "this name will have 105 characters as it is just the " "max that fits in the SU once we add the CE entry.txt"; link->dest = "./and/../a/./big/destination/with/10/components"; node->iso_name = "THIS_NAM.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 59); CU_ASSERT_EQUAL(sua_len, 254 - 46); /* 4.b it just fits, the the component ends in '/' */ link->node.name = "this name will have 105 characters as it is just the " "max that fits in the SU once we add the CE entry.txt"; link->dest = "./and/../a/./big/destination/with/10/components/"; node->iso_name = "THIS_NAM.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 59); CU_ASSERT_EQUAL(sua_len, 254 - 46); /* 4.c extra char in name, that forces it to be divided */ link->node.name = "this name will have 105 characters as it is just the " "max that fits in the SU once we add the CE entryc.txt"; link->dest = "./and/../a/./big/destination/with/10/components"; node->iso_name = "THIS_NAM.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 59 + 6); CU_ASSERT_EQUAL(sua_len, 254 - 46); /* 5 max destination length to fit in a single SL entry (250) */ link->node.name = "this name will have 74 characters as it is the max " "that fits in the SU.txt"; link->dest = "./and/../a/./very/big/destination/with/10/components/that/" "conforms/the/max/that/fits/in/a/single/SL/as/it/takes/" "just/two/hundred/and/fifty/bytes/bytes/bytes/bytes/bytes" "/bytes/bytes/bytes/bytes/bytes/bytes/../bytes"; node->iso_name = "THIS_NAM.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 255); CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); /* 6 min destination length to need two SL entries (251) */ link->node.name = "this name will have 74 characters as it is the max " "that fits in the SU.txt"; link->dest = "./and/../a/./very/big/destination/with/10/components/that/" "conforms/the/max/that/fits/in/a/single/SL/as/it/takes/" "just/two/hundred/and/fifty/bytes/bytes/bytes/bytes/bytes" "/bytes/bytes/bytes/bytes/bytes/bytes/../bytess"; node->iso_name = "THIS_NAM.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 261); CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); /* 7 destination with big component that need to be splited * in two SL entries */ /* 7.a just fits in one */ link->node.name = "this name will have 74 characters as it is the max " "that fits in the SU.txt"; link->dest = "very big component with 248 characters, that is the max that" " fits in a single SL entry. Take care that SL header takes 5 " "bytes, and component header another 2, one for size, another" " for flags. This last characters are just padding to get 248 " "bytes."; node->iso_name = "THIS_NAM.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 255); CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); /* 7.b doesn't fits by one character */ link->node.name = "this name will have 74 characters as it is the max " "that fits in the SU.txt"; link->dest = "very big component with 249 characters, that is the min that" " doesn't fit in a single SL entry. Take care that SL header " "takes 5 bytes, and component header another 2, one for size," " another for flags. This last characters are just padding to" " get 249."; node->iso_name = "THIS_NAM.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 255 + (5+2+1)); CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); /* 7.c several components before, such as it has just the right len * to fit in the SL entry plus another one */ link->node.name = "this name will have 74 characters as it is the max " "that fits in the SU.txt"; link->dest = "the/first/components/take just 245 characters/and thus the " "first SL entry will have/255 - 5 - 245 - 2 (component " "header) = 3/ just the space for another component with a " "single character/This makes that last component fit in exactly 2 " "SLs/very big component with 249 characters, that is the min " "that doesn't fit in a single SL entry. Take care that SL " "header takes 5 bytes, and component header another 2, one " "for size, another for flags. This last characters are just " "padding to get 249."; node->iso_name = "THIS_NAM.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 255 + 255); CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); /* * 7.d several components before, and then a big component that doesn't * fit in the 1st SL entry and another one. That case needs a 3rd SL entry, * but instead of divide the component in 2 entries, we put it in 2, * without completelly fill the first one. */ link->node.name = "this name will have 74 characters as it is the max " "that fits in the SU.txt"; link->dest = "the/first/components/take just 245 characters/and thus the " "first SL entry will have/255 - 5 - 245 - 2 (component " "header) = 3/ just the space for another component with a " "single character/This makes that last component fit in exactly 2 " "SLs/very big component with 250 characters, that is the min " "that does not fit in a single SL entry. Take care that SL " "header takes 5 bytes, and component header another 2, one " "for size, another for flags. This last characters are just " "padding to get 249."; node->iso_name = "THIS_NAM.TXT"; sua_len = rrip_calc_len(&t, node, 0, 255 - 46, &ce_len); CU_ASSERT_EQUAL(ce_len, 252 + 255 + 9); CU_ASSERT_EQUAL(sua_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); free(link); free(node); } static void susp_info_free(struct susp_info *susp) { size_t i; for (i = 0; i < susp->n_susp_fields; ++i) { free(susp->susp_fields[i]); } free(susp->susp_fields); for (i = 0; i < susp->n_ce_susp_fields; ++i) { free(susp->ce_susp_fields[i]); } free(susp->ce_susp_fields); } static void test_rrip_get_susp_fields_file() { IsoFile *file; Ecma119Node *node; int ret; struct susp_info susp; Ecma119Image t; uint8_t *entry; memset(&t, 0, sizeof(Ecma119Image)); t.input_charset = "UTF-8"; t.output_charset = "UTF-8"; file = malloc(sizeof(IsoFile)); CU_ASSERT_PTR_NOT_NULL_FATAL(file); file->from_old_session = 0; file->sort_weight = 0; file->stream = NULL; /* it is not needed here */ file->node.type = LIBISO_FILE; file->node.mode = S_IFREG | 0555; file->node.uid = 235; file->node.gid = 654; file->node.mtime = 675757578; file->node.atime = 546462546; file->node.ctime = 323245342; node = malloc(sizeof(Ecma119Node)); CU_ASSERT_PTR_NOT_NULL_FATAL(node); node->node = (IsoNode*)file; node->parent = (Ecma119Node*)0x55555555; /* just to make it not NULL */ node->info.file = NULL; /* it is not needed here */ node->type = ECMA119_FILE; node->nlink = 1; node->ino = 0x03447892; /* Case 1. Name fit in System Use field */ file->node.name = "a small name.txt"; node->iso_name = "A_SMALL_.TXT"; memset(&susp, 0, sizeof(struct susp_info)); ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_EQUAL(susp.ce_len, 0); CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 0); CU_ASSERT_EQUAL(susp.n_susp_fields, 3); /* PX + TF + NM */ CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 16) + (5 + 3*7) + 1); /* PX is the first entry */ entry = susp.susp_fields[0]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'P'); CU_ASSERT_EQUAL(entry[1], 'X'); CU_ASSERT_EQUAL(entry[2], 44); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), S_IFREG | 0555); CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), S_IFREG | 0555); CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 1); CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 1); CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 235); CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 235); CU_ASSERT_EQUAL(iso_read_lsb(entry + 28, 4), 654); CU_ASSERT_EQUAL(iso_read_msb(entry + 32, 4), 654); CU_ASSERT_EQUAL(iso_read_lsb(entry + 36, 4), 0x03447892); CU_ASSERT_EQUAL(iso_read_msb(entry + 40, 4), 0x03447892); /* TF is the second entry */ entry = susp.susp_fields[1]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'T'); CU_ASSERT_EQUAL(entry[1], 'F'); CU_ASSERT_EQUAL(entry[2], 5 + 3*7); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0x0E); CU_ASSERT_EQUAL(iso_datetime_read_7(entry + 5), 675757578); CU_ASSERT_EQUAL(iso_datetime_read_7(entry + 12), 546462546); CU_ASSERT_EQUAL(iso_datetime_read_7(entry + 19), 323245342); /* NM is the last entry */ entry = susp.susp_fields[2]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 16); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); CU_ASSERT_NSTRING_EQUAL(entry + 5, "a small name.txt", 16); susp_info_free(&susp); /* Case 2. Name fits exactly */ file->node.name = "a big name, with 133 characters, that it is the max " "that fits in System Use field of the directory record " "PADPADPADADPADPADPADPAD.txt"; node->iso_name = "A_BIG_NA.TXT"; memset(&susp, 0, sizeof(struct susp_info)); ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_EQUAL(susp.ce_len, 0); CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 0); CU_ASSERT_EQUAL(susp.suf_len, 254 - 46); CU_ASSERT_EQUAL(susp.n_susp_fields, 3); /* PX + TF + NM */ /* NM is the last entry */ entry = susp.susp_fields[2]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 133); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); CU_ASSERT_NSTRING_EQUAL(entry + 5, "a big name, with 133 characters, that " "it is the max that fits in System Use field of the " "directory record PADPADPADADPADPADPADPAD.txt", 133); susp_info_free(&susp); /* case 3. A name just 1 character too big to fit in SUA */ file->node.name = "a big name, with 133 characters, that it is the max " "that fits in System Use field of the directory record " "PADPADPADADPADPADPADPAD1.txt"; node->iso_name = "A_BIG_NA.TXT"; memset(&susp, 0, sizeof(struct susp_info)); ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_EQUAL(susp.ce_len, 28 + 5 + 1); CU_ASSERT_EQUAL(susp.suf_len, 254 - 46); CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* NM */ /* test NM entry */ entry = susp.susp_fields[2]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 105); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 1); /* CONTINUE */ CU_ASSERT_NSTRING_EQUAL(entry + 5, "a big name, with 133 characters, that " "it is the max that fits in System Use field of the " "directory record", 105); /* and CE entry */ entry = susp.susp_fields[3]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'C'); CU_ASSERT_EQUAL(entry[1], 'E'); CU_ASSERT_EQUAL(entry[2], 28); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 0); CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 0); CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 0); CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 0); CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 34); CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 34); /* and check Continuation area */ entry = susp.ce_susp_fields[0]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 29); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); CU_ASSERT_NSTRING_EQUAL(entry + 5, " PADPADPADADPADPADPADPAD1.txt", 29); susp_info_free(&susp); /* case 4. A 255 characters name */ file->node.name = "a big name, with 255 characters, that it is the max " "that a POSIX filename can have. PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP"; node->iso_name = "A_BIG_NA.TXT"; memset(&susp, 0, sizeof(struct susp_info)); susp.ce_block = 12; susp.ce_len = 456; ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_EQUAL(susp.ce_len, 150 + 5 + 456); CU_ASSERT_EQUAL(susp.suf_len, 254 - 46); CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* NM */ /* test NM entry */ entry = susp.susp_fields[2]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 105); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 1); /* CONTINUE */ CU_ASSERT_NSTRING_EQUAL(entry + 5, "a big name, with 255 characters, that " "it is the max that a POSIX filename can have. PPP" "PPPPPPPPPPPPPPPPPP", 105); /* and CE entry */ entry = susp.susp_fields[3]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'C'); CU_ASSERT_EQUAL(entry[1], 'E'); CU_ASSERT_EQUAL(entry[2], 28); CU_ASSERT_EQUAL(entry[3], 1); /* block, offset, size */ CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 12); CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 12); CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 456); CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 456); CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 155); CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 155); /* and check Continuation area */ entry = susp.ce_susp_fields[0]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 150); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); CU_ASSERT_NSTRING_EQUAL(entry + 5, "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" "PPPPPPPPPPPPPP", 150); susp_info_free(&susp); free(node); free(file); } static void test_rrip_get_susp_fields_symlink() { IsoSymlink *link; Ecma119Node *node; Ecma119Image t; int ret; struct susp_info susp; uint8_t *entry; memset(&t, 0, sizeof(Ecma119Image)); t.input_charset = "UTF-8"; t.output_charset = "UTF-8"; link = malloc(sizeof(IsoSymlink)); CU_ASSERT_PTR_NOT_NULL_FATAL(link); link->node.type = LIBISO_SYMLINK; link->node.mode = S_IFREG | 0555; link->node.uid = 235; link->node.gid = 654; link->node.mtime = 675757578; link->node.atime = 546462546; link->node.ctime = 323245342; node = malloc(sizeof(Ecma119Node)); CU_ASSERT_PTR_NOT_NULL_FATAL(node); node->node = (IsoNode*)link; node->parent = (Ecma119Node*)0x55555555; /* just to make it not NULL */ node->type = ECMA119_SYMLINK; node->nlink = 1; node->ino = 0x03447892; /* Case 1. Name and dest fit in System Use field */ link->node.name = "a small name.txt"; link->dest = "/three/components"; node->iso_name = "A_SMALL_.TXT"; memset(&susp, 0, sizeof(struct susp_info)); ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_EQUAL(susp.ce_len, 0); CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 0); CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + SL */ CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 16) + (5 + 3*7) + 1 + (5 + 2 + (2 + 5) + (2 + 10))); /* PX is the first entry */ entry = susp.susp_fields[0]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'P'); CU_ASSERT_EQUAL(entry[1], 'X'); CU_ASSERT_EQUAL(entry[2], 44); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), S_IFREG | 0555); CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), S_IFREG | 0555); CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 1); CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 1); CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 235); CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 235); CU_ASSERT_EQUAL(iso_read_lsb(entry + 28, 4), 654); CU_ASSERT_EQUAL(iso_read_msb(entry + 32, 4), 654); CU_ASSERT_EQUAL(iso_read_lsb(entry + 36, 4), 0x03447892); CU_ASSERT_EQUAL(iso_read_msb(entry + 40, 4), 0x03447892); /* TF is the second entry */ entry = susp.susp_fields[1]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'T'); CU_ASSERT_EQUAL(entry[1], 'F'); CU_ASSERT_EQUAL(entry[2], 5 + 3*7); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0x0E); CU_ASSERT_EQUAL(iso_datetime_read_7(entry + 5), 675757578); CU_ASSERT_EQUAL(iso_datetime_read_7(entry + 12), 546462546); CU_ASSERT_EQUAL(iso_datetime_read_7(entry + 19), 323245342); /* NM is the 3rd entry */ entry = susp.susp_fields[2]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 16); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); CU_ASSERT_NSTRING_EQUAL(entry + 5, "a small name.txt", 16); /* SL is the last entry */ entry = susp.susp_fields[3]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'S'); CU_ASSERT_EQUAL(entry[1], 'L'); CU_ASSERT_EQUAL(entry[2], 5 + 2 + (2 + 5) + (2 + 10)); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); /* first component */ CU_ASSERT_EQUAL(entry[5], 0x8); /* root */ CU_ASSERT_EQUAL(entry[6], 0); /* 2nd component */ CU_ASSERT_EQUAL(entry[7], 0); CU_ASSERT_EQUAL(entry[8], 5); CU_ASSERT_NSTRING_EQUAL(entry + 9, "three", 5); /* 3rd component */ CU_ASSERT_EQUAL(entry[14], 0); CU_ASSERT_EQUAL(entry[15], 10); CU_ASSERT_NSTRING_EQUAL(entry + 16, "components", 10); susp_info_free(&susp); /* case 2. name + dest fits exactly */ link->node.name = "this name will have 74 characters as it is the max " "that fits in the SU.txt"; link->dest = "./and/../a/./big/destination/with/10/components"; node->iso_name = "THIS_NAM.TXT"; memset(&susp, 0, sizeof(struct susp_info)); ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_EQUAL(susp.ce_len, 0); CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 0); CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + SL */ CU_ASSERT_EQUAL(susp.suf_len, 254 - 46); /* NM is the 3rd entry */ entry = susp.susp_fields[2]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 74); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); CU_ASSERT_NSTRING_EQUAL(entry + 5, "this name will have 74 characters as " "it is the max that fits in the SU.txt", 74); /* SL is the last entry */ entry = susp.susp_fields[3]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'S'); CU_ASSERT_EQUAL(entry[1], 'L'); CU_ASSERT_EQUAL(entry[2], 5 + 2 + 5 + 2 + 3 + 2 + 5 + 13 + 6 + 4 + 12); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); /* first component */ CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ CU_ASSERT_EQUAL(entry[6], 0); /* 2nd component */ CU_ASSERT_EQUAL(entry[7], 0); CU_ASSERT_EQUAL(entry[8], 3); CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); /* 3rd component */ CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ CU_ASSERT_EQUAL(entry[13], 0); /* 4th component */ CU_ASSERT_EQUAL(entry[14], 0); CU_ASSERT_EQUAL(entry[15], 1); CU_ASSERT_EQUAL(entry[16], 'a'); /* 5th component */ CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ CU_ASSERT_EQUAL(entry[18], 0); /* 6th component */ CU_ASSERT_EQUAL(entry[19], 0); CU_ASSERT_EQUAL(entry[20], 3); CU_ASSERT_NSTRING_EQUAL(entry + 21, "big", 3); /* 7th component */ CU_ASSERT_EQUAL(entry[24], 0); CU_ASSERT_EQUAL(entry[25], 11); CU_ASSERT_NSTRING_EQUAL(entry + 26, "destination", 11); /* 8th component */ CU_ASSERT_EQUAL(entry[37], 0); CU_ASSERT_EQUAL(entry[38], 4); CU_ASSERT_NSTRING_EQUAL(entry + 39, "with", 4); /* 9th component */ CU_ASSERT_EQUAL(entry[43], 0); CU_ASSERT_EQUAL(entry[44], 2); CU_ASSERT_NSTRING_EQUAL(entry + 45, "10", 2); /* 10th component */ CU_ASSERT_EQUAL(entry[47], 0); CU_ASSERT_EQUAL(entry[48], 10); CU_ASSERT_NSTRING_EQUAL(entry + 49, "components", 10); susp_info_free(&susp); /* case 3. name fits, dest is one byte larger to fit */ /* 3.a extra byte in dest */ link->node.name = "this name will have 74 characters as it is the max " "that fits in the SU.txt"; link->dest = "./and/../a/./big/destination/with/10/componentsk"; node->iso_name = "THIS_NAM.TXT"; memset(&susp, 0, sizeof(struct susp_info)); ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_EQUAL(susp.ce_len, 60); CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* SL */ CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 74) + (5 + 3*7) + 1 + 28); /* PX is the first entry */ entry = susp.susp_fields[0]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'P'); CU_ASSERT_EQUAL(entry[1], 'X'); CU_ASSERT_EQUAL(entry[2], 44); /* TF is the second entry */ entry = susp.susp_fields[1]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'T'); CU_ASSERT_EQUAL(entry[1], 'F'); CU_ASSERT_EQUAL(entry[2], 5 + 3*7); /* NM is the 3rd entry */ entry = susp.susp_fields[2]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 74); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); CU_ASSERT_NSTRING_EQUAL(entry + 5, "this name will have 74 characters as " "it is the max that fits in the SU.txt", 74); /* and CE entry is last */ entry = susp.susp_fields[3]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'C'); CU_ASSERT_EQUAL(entry[1], 'E'); CU_ASSERT_EQUAL(entry[2], 28); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 0); CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 0); CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 0); CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 0); CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 60); CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 60); /* finally, SL is the single entry in CE */ entry = susp.ce_susp_fields[0]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'S'); CU_ASSERT_EQUAL(entry[1], 'L'); CU_ASSERT_EQUAL(entry[2], 60); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); /* first component */ CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ CU_ASSERT_EQUAL(entry[6], 0); /* 2nd component */ CU_ASSERT_EQUAL(entry[7], 0); CU_ASSERT_EQUAL(entry[8], 3); CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); /* 3rd component */ CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ CU_ASSERT_EQUAL(entry[13], 0); /* 4th component */ CU_ASSERT_EQUAL(entry[14], 0); CU_ASSERT_EQUAL(entry[15], 1); CU_ASSERT_EQUAL(entry[16], 'a'); /* 5th component */ CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ CU_ASSERT_EQUAL(entry[18], 0); /* 6th component */ CU_ASSERT_EQUAL(entry[19], 0); CU_ASSERT_EQUAL(entry[20], 3); CU_ASSERT_NSTRING_EQUAL(entry + 21, "big", 3); /* 7th component */ CU_ASSERT_EQUAL(entry[24], 0); CU_ASSERT_EQUAL(entry[25], 11); CU_ASSERT_NSTRING_EQUAL(entry + 26, "destination", 11); /* 8th component */ CU_ASSERT_EQUAL(entry[37], 0); CU_ASSERT_EQUAL(entry[38], 4); CU_ASSERT_NSTRING_EQUAL(entry + 39, "with", 4); /* 9th component */ CU_ASSERT_EQUAL(entry[43], 0); CU_ASSERT_EQUAL(entry[44], 2); CU_ASSERT_NSTRING_EQUAL(entry + 45, "10", 2); /* 10th component */ CU_ASSERT_EQUAL(entry[47], 0); CU_ASSERT_EQUAL(entry[48], 11); CU_ASSERT_NSTRING_EQUAL(entry + 49, "componentsk", 11); susp_info_free(&susp); /* 3.b extra byte in name */ link->node.name = "this name will have 75 characters as it is the max " "that fits in the SUx.txt"; link->dest = "./and/../a/./big/destination/with/10/components"; node->iso_name = "THIS_NAM.TXT"; memset(&susp, 0, sizeof(struct susp_info)); ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_EQUAL(susp.ce_len, 59); CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* SL */ CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 75) + (5 + 3*7) + 28); /* NM is the 3rd entry */ entry = susp.susp_fields[2]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 75); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); CU_ASSERT_NSTRING_EQUAL(entry + 5, "this name will have 75 characters as it " "is the max that fits in the SUx.txt", 75); /* and CE entry is last */ entry = susp.susp_fields[3]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'C'); CU_ASSERT_EQUAL(entry[1], 'E'); CU_ASSERT_EQUAL(entry[2], 28); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 0); CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 0); CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 0); CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 0); CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 59); CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 59); /* finally, SL is the single entry in CE */ entry = susp.ce_susp_fields[0]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'S'); CU_ASSERT_EQUAL(entry[1], 'L'); CU_ASSERT_EQUAL(entry[2], 59); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); /* first component */ CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ CU_ASSERT_EQUAL(entry[6], 0); /* 2nd component */ CU_ASSERT_EQUAL(entry[7], 0); CU_ASSERT_EQUAL(entry[8], 3); CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); /* 3rd component */ CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ CU_ASSERT_EQUAL(entry[13], 0); /* 4th component */ CU_ASSERT_EQUAL(entry[14], 0); CU_ASSERT_EQUAL(entry[15], 1); CU_ASSERT_EQUAL(entry[16], 'a'); /* 5th component */ CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ CU_ASSERT_EQUAL(entry[18], 0); /* 6th component */ CU_ASSERT_EQUAL(entry[19], 0); CU_ASSERT_EQUAL(entry[20], 3); CU_ASSERT_NSTRING_EQUAL(entry + 21, "big", 3); /* 7th component */ CU_ASSERT_EQUAL(entry[24], 0); CU_ASSERT_EQUAL(entry[25], 11); CU_ASSERT_NSTRING_EQUAL(entry + 26, "destination", 11); /* 8th component */ CU_ASSERT_EQUAL(entry[37], 0); CU_ASSERT_EQUAL(entry[38], 4); CU_ASSERT_NSTRING_EQUAL(entry + 39, "with", 4); /* 9th component */ CU_ASSERT_EQUAL(entry[43], 0); CU_ASSERT_EQUAL(entry[44], 2); CU_ASSERT_NSTRING_EQUAL(entry + 45, "10", 2); /* 10th component */ CU_ASSERT_EQUAL(entry[47], 0); CU_ASSERT_EQUAL(entry[48], 10); CU_ASSERT_NSTRING_EQUAL(entry + 49, "components", 10); susp_info_free(&susp); /* case 4. name seems to fit, but SL no, and when CE is added NM * doesn't fit too */ /* 4.a it just fits */ link->node.name = "this name will have 105 characters as it is just the " "max that fits in the SU once we add the CE entry.txt"; link->dest = "./and/../a/./big/destination/with/10/components"; node->iso_name = "THIS_NAM.TXT"; memset(&susp, 0, sizeof(struct susp_info)); ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_EQUAL(susp.ce_len, 59); CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* SL */ CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 3*7) + (5 + 105) + 28); /* NM is the 3rd entry */ entry = susp.susp_fields[2]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 105); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); CU_ASSERT_NSTRING_EQUAL(entry + 5, "this name will have 105 characters as " "it is just the max that fits in the SU once we " "add the CE entry.txt", 105); /* and CE entry is last */ entry = susp.susp_fields[3]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'C'); CU_ASSERT_EQUAL(entry[1], 'E'); CU_ASSERT_EQUAL(entry[2], 28); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 0); CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 0); CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 0); CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 0); CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 59); CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 59); /* finally, SL is the single entry in CE */ entry = susp.ce_susp_fields[0]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'S'); CU_ASSERT_EQUAL(entry[1], 'L'); CU_ASSERT_EQUAL(entry[2], 59); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); /* first component */ CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ CU_ASSERT_EQUAL(entry[6], 0); /* 2nd component */ CU_ASSERT_EQUAL(entry[7], 0); CU_ASSERT_EQUAL(entry[8], 3); CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); /* 3rd component */ CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ CU_ASSERT_EQUAL(entry[13], 0); /* 4th component */ CU_ASSERT_EQUAL(entry[14], 0); CU_ASSERT_EQUAL(entry[15], 1); CU_ASSERT_EQUAL(entry[16], 'a'); /* 5th component */ CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ CU_ASSERT_EQUAL(entry[18], 0); /* 6th component */ CU_ASSERT_EQUAL(entry[19], 0); CU_ASSERT_EQUAL(entry[20], 3); CU_ASSERT_NSTRING_EQUAL(entry + 21, "big", 3); /* 7th component */ CU_ASSERT_EQUAL(entry[24], 0); CU_ASSERT_EQUAL(entry[25], 11); CU_ASSERT_NSTRING_EQUAL(entry + 26, "destination", 11); /* 8th component */ CU_ASSERT_EQUAL(entry[37], 0); CU_ASSERT_EQUAL(entry[38], 4); CU_ASSERT_NSTRING_EQUAL(entry + 39, "with", 4); /* 9th component */ CU_ASSERT_EQUAL(entry[43], 0); CU_ASSERT_EQUAL(entry[44], 2); CU_ASSERT_NSTRING_EQUAL(entry + 45, "10", 2); /* 10th component */ CU_ASSERT_EQUAL(entry[47], 0); CU_ASSERT_EQUAL(entry[48], 10); CU_ASSERT_NSTRING_EQUAL(entry + 49, "components", 10); susp_info_free(&susp); /* 4.b it just fits, the the component ends in '/' */ link->node.name = "this name will have 105 characters as it is just the " "max that fits in the SU once we add the CE entry.txt"; link->dest = "./and/../a/./big/destination/with/10/components/"; node->iso_name = "THIS_NAM.TXT"; memset(&susp, 0, sizeof(struct susp_info)); ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_EQUAL(susp.ce_len, 59); CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* SL */ CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 3*7) + (5 + 105) + 28); /* NM is the 3rd entry */ entry = susp.susp_fields[2]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 105); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); CU_ASSERT_NSTRING_EQUAL(entry + 5, "this name will have 105 characters as " "it is just the max that fits in the SU once we " "add the CE entry.txt", 105); /* and CE entry is last */ entry = susp.susp_fields[3]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'C'); CU_ASSERT_EQUAL(entry[1], 'E'); CU_ASSERT_EQUAL(entry[2], 28); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 0); CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 0); CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 0); CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 0); CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 59); CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 59); /* finally, SL is the single entry in CE */ entry = susp.ce_susp_fields[0]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'S'); CU_ASSERT_EQUAL(entry[1], 'L'); CU_ASSERT_EQUAL(entry[2], 59); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); /* first component */ CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ CU_ASSERT_EQUAL(entry[6], 0); /* 2nd component */ CU_ASSERT_EQUAL(entry[7], 0); CU_ASSERT_EQUAL(entry[8], 3); CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); /* 3rd component */ CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ CU_ASSERT_EQUAL(entry[13], 0); /* 4th component */ CU_ASSERT_EQUAL(entry[14], 0); CU_ASSERT_EQUAL(entry[15], 1); CU_ASSERT_EQUAL(entry[16], 'a'); /* 5th component */ CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ CU_ASSERT_EQUAL(entry[18], 0); /* 6th component */ CU_ASSERT_EQUAL(entry[19], 0); CU_ASSERT_EQUAL(entry[20], 3); CU_ASSERT_NSTRING_EQUAL(entry + 21, "big", 3); /* 7th component */ CU_ASSERT_EQUAL(entry[24], 0); CU_ASSERT_EQUAL(entry[25], 11); CU_ASSERT_NSTRING_EQUAL(entry + 26, "destination", 11); /* 8th component */ CU_ASSERT_EQUAL(entry[37], 0); CU_ASSERT_EQUAL(entry[38], 4); CU_ASSERT_NSTRING_EQUAL(entry + 39, "with", 4); /* 9th component */ CU_ASSERT_EQUAL(entry[43], 0); CU_ASSERT_EQUAL(entry[44], 2); CU_ASSERT_NSTRING_EQUAL(entry + 45, "10", 2); /* 10th component */ CU_ASSERT_EQUAL(entry[47], 0); CU_ASSERT_EQUAL(entry[48], 10); CU_ASSERT_NSTRING_EQUAL(entry + 49, "components", 10); susp_info_free(&susp); /* 4.c extra char in name, that forces it to be divided */ link->node.name = "this name will have 106 characters as it is just the " "max that fits in the SU once we add the CE entryc.txt"; link->dest = "./and/../a/./big/destination/with/10/components"; node->iso_name = "THIS_NAM.TXT"; memset(&susp, 0, sizeof(struct susp_info)); ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_EQUAL(susp.ce_len, 6 + 59); CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 2); /* NM + SL */ CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 3*7) + (5 + 105) + 28); /* NM is the 3rd entry */ entry = susp.susp_fields[2]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 105); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0x1); /* continue */ CU_ASSERT_NSTRING_EQUAL(entry + 5, "this name will have 106 characters as " "it is just the max that fits in the SU once we " "add the CE entryc.tx", 105); /* and CE entry is last */ entry = susp.susp_fields[3]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'C'); CU_ASSERT_EQUAL(entry[1], 'E'); CU_ASSERT_EQUAL(entry[2], 28); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(iso_read_lsb(entry + 4, 4), 0); CU_ASSERT_EQUAL(iso_read_msb(entry + 8, 4), 0); CU_ASSERT_EQUAL(iso_read_lsb(entry + 12, 4), 0); CU_ASSERT_EQUAL(iso_read_msb(entry + 16, 4), 0); CU_ASSERT_EQUAL(iso_read_lsb(entry + 20, 4), 59 + 6); CU_ASSERT_EQUAL(iso_read_msb(entry + 24, 4), 59 + 6); /* NM is the 1st entry in CE */ entry = susp.ce_susp_fields[0]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'N'); CU_ASSERT_EQUAL(entry[1], 'M'); CU_ASSERT_EQUAL(entry[2], 5 + 1); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); CU_ASSERT_EQUAL(entry[5], 't'); /* finally, SL is the single entry in CE */ entry = susp.ce_susp_fields[1]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'S'); CU_ASSERT_EQUAL(entry[1], 'L'); CU_ASSERT_EQUAL(entry[2], 59); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); /* first component */ CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ CU_ASSERT_EQUAL(entry[6], 0); /* 2nd component */ CU_ASSERT_EQUAL(entry[7], 0); CU_ASSERT_EQUAL(entry[8], 3); CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); /* 3rd component */ CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ CU_ASSERT_EQUAL(entry[13], 0); /* 4th component */ CU_ASSERT_EQUAL(entry[14], 0); CU_ASSERT_EQUAL(entry[15], 1); CU_ASSERT_EQUAL(entry[16], 'a'); /* 5th component */ CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ CU_ASSERT_EQUAL(entry[18], 0); /* 6th component */ CU_ASSERT_EQUAL(entry[19], 0); CU_ASSERT_EQUAL(entry[20], 3); CU_ASSERT_NSTRING_EQUAL(entry + 21, "big", 3); /* 7th component */ CU_ASSERT_EQUAL(entry[24], 0); CU_ASSERT_EQUAL(entry[25], 11); CU_ASSERT_NSTRING_EQUAL(entry + 26, "destination", 11); /* 8th component */ CU_ASSERT_EQUAL(entry[37], 0); CU_ASSERT_EQUAL(entry[38], 4); CU_ASSERT_NSTRING_EQUAL(entry + 39, "with", 4); /* 9th component */ CU_ASSERT_EQUAL(entry[43], 0); CU_ASSERT_EQUAL(entry[44], 2); CU_ASSERT_NSTRING_EQUAL(entry + 45, "10", 2); /* 10th component */ CU_ASSERT_EQUAL(entry[47], 0); CU_ASSERT_EQUAL(entry[48], 10); CU_ASSERT_NSTRING_EQUAL(entry + 49, "components", 10); susp_info_free(&susp); /* 5 max destination length to fit in a single SL entry (250) */ link->node.name = "this name will have 74 characters as it is the max " "that fits in the SU.txt"; link->dest = "./and/../a/./very/big/destination/with/10/components/that/" "conforms/the/max/that/fits/in/a single SL/entry as it takes " "just two hundred and/fifty bytes bytes bytes bytes/bytes" " bytes bytes bytes bytes bytes bytes bytes bytes/../bytes"; node->iso_name = "THIS_NAM.TXT"; memset(&susp, 0, sizeof(struct susp_info)); ret = rrip_get_susp_fields(&t, node, 0, 255 - 46, &susp); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_EQUAL(susp.ce_len, 255); CU_ASSERT_EQUAL(susp.n_ce_susp_fields, 1); /* SL */ CU_ASSERT_EQUAL(susp.n_susp_fields, 4); /* PX + TF + NM + CE */ CU_ASSERT_EQUAL(susp.suf_len, 44 + (5 + 3*7) + (5 + 74) + 1 + 28); /* just check the SL entry */ entry = susp.ce_susp_fields[0]; CU_ASSERT_PTR_NOT_NULL(entry); CU_ASSERT_EQUAL(entry[0], 'S'); CU_ASSERT_EQUAL(entry[1], 'L'); CU_ASSERT_EQUAL(entry[2], 255); CU_ASSERT_EQUAL(entry[3], 1); CU_ASSERT_EQUAL(entry[4], 0); /* first component */ CU_ASSERT_EQUAL(entry[5], 0x2); /* current */ CU_ASSERT_EQUAL(entry[6], 0); /* 2nd component */ CU_ASSERT_EQUAL(entry[7], 0); CU_ASSERT_EQUAL(entry[8], 3); CU_ASSERT_NSTRING_EQUAL(entry + 9, "and", 3); /* 3rd component */ CU_ASSERT_EQUAL(entry[12], 0x4); /* parent */ CU_ASSERT_EQUAL(entry[13], 0); /* 4th component */ CU_ASSERT_EQUAL(entry[14], 0); CU_ASSERT_EQUAL(entry[15], 1); CU_ASSERT_EQUAL(entry[16], 'a'); /* 5th component */ CU_ASSERT_EQUAL(entry[17], 0x2); /* current */ CU_ASSERT_EQUAL(entry[18], 0); /* 6th component */ CU_ASSERT_EQUAL(entry[19], 0); CU_ASSERT_EQUAL(entry[20], 4); CU_ASSERT_NSTRING_EQUAL(entry + 21, "very", 4); /* 7th component */ CU_ASSERT_EQUAL(entry[25], 0); CU_ASSERT_EQUAL(entry[26], 3); CU_ASSERT_NSTRING_EQUAL(entry + 27, "big", 3); /* 8th component */ CU_ASSERT_EQUAL(entry[30], 0); CU_ASSERT_EQUAL(entry[31], 11); CU_ASSERT_NSTRING_EQUAL(entry + 32, "destination", 11); /* 9th component */ CU_ASSERT_EQUAL(entry[43], 0); CU_ASSERT_EQUAL(entry[44], 4); CU_ASSERT_NSTRING_EQUAL(entry + 45, "with", 4); /* 10th component */ CU_ASSERT_EQUAL(entry[49], 0); CU_ASSERT_EQUAL(entry[50], 2); CU_ASSERT_NSTRING_EQUAL(entry + 51, "10", 2); /* 11th component */ CU_ASSERT_EQUAL(entry[53], 0); CU_ASSERT_EQUAL(entry[54], 10); CU_ASSERT_NSTRING_EQUAL(entry + 55, "components", 10); /* 12th component */ CU_ASSERT_EQUAL(entry[65], 0); CU_ASSERT_EQUAL(entry[66], 4); CU_ASSERT_NSTRING_EQUAL(entry + 67, "that", 4); /* 13th component */ CU_ASSERT_EQUAL(entry[71], 0); CU_ASSERT_EQUAL(entry[72], 8); CU_ASSERT_NSTRING_EQUAL(entry + 73, "conforms", 8); /* 14th component */ CU_ASSERT_EQUAL(entry[81], 0); CU_ASSERT_EQUAL(entry[82], 3); CU_ASSERT_NSTRING_EQUAL(entry + 83, "the", 3); /* 15th component */ CU_ASSERT_EQUAL(entry[86], 0); CU_ASSERT_EQUAL(entry[87], 3); CU_ASSERT_NSTRING_EQUAL(entry + 88, "max", 3); /* 16th component */ CU_ASSERT_EQUAL(entry[91], 0); CU_ASSERT_EQUAL(entry[92], 4); CU_ASSERT_NSTRING_EQUAL(entry + 93, "that", 4); /* 17th component */ CU_ASSERT_EQUAL(entry[97], 0); CU_ASSERT_EQUAL(entry[98], 4); CU_ASSERT_NSTRING_EQUAL(entry + 99, "fits", 4); /* 18th component */ CU_ASSERT_EQUAL(entry[103], 0); CU_ASSERT_EQUAL(entry[104], 2); CU_ASSERT_NSTRING_EQUAL(entry + 105, "in", 2); /* 19th component */ CU_ASSERT_EQUAL(entry[107], 0); CU_ASSERT_EQUAL(entry[108], 11); CU_ASSERT_NSTRING_EQUAL(entry + 109, "a single SL", 11); /* 20th component */ CU_ASSERT_EQUAL(entry[120], 0); CU_ASSERT_EQUAL(entry[121], 38); CU_ASSERT_NSTRING_EQUAL(entry + 122, "entry as it takes " "just two hundred and", 38); /* 21th component */ CU_ASSERT_EQUAL(entry[160], 0); CU_ASSERT_EQUAL(entry[161], 29); CU_ASSERT_NSTRING_EQUAL(entry + 162, "fifty bytes bytes bytes bytes", 29); /* 22th component */ CU_ASSERT_EQUAL(entry[191], 0); CU_ASSERT_EQUAL(entry[192], 53); CU_ASSERT_NSTRING_EQUAL(entry + 193, "bytes bytes bytes bytes bytes bytes" " bytes bytes bytes", 53); /* 23th component */ CU_ASSERT_EQUAL(entry[246], 0x4); /* parent */ CU_ASSERT_EQUAL(entry[247], 0); /* 24th component */ CU_ASSERT_EQUAL(entry[248], 0); CU_ASSERT_EQUAL(entry[249], 5); CU_ASSERT_NSTRING_EQUAL(entry + 250, "bytes", 5); susp_info_free(&susp); free(node); free(link); } void add_rockridge_suite() { CU_pSuite pSuite = CU_add_suite("RockRidge Suite", NULL, NULL); CU_add_test(pSuite, "rrip_calc_len(file)", test_rrip_calc_len_file); CU_add_test(pSuite, "rrip_calc_len(symlink)", test_rrip_calc_len_symlink); CU_add_test(pSuite, "rrip_get_susp_fields(file)", test_rrip_get_susp_fields_file); CU_add_test(pSuite, "rrip_get_susp_fields(symlink)", test_rrip_get_susp_fields_symlink); } /* * Unit test for util.h * * This test utiliy functions */ #include "test.h" #include "stream.h" #include static void test_mem_new() { int ret; IsoStream *stream; unsigned char *buf; buf = malloc(3000); ret = iso_memory_stream_new(buf, 3000, &stream); CU_ASSERT_EQUAL(ret, 1); iso_stream_unref(stream); ret = iso_memory_stream_new(NULL, 3000, &stream); CU_ASSERT_EQUAL(ret, ISO_NULL_POINTER); ret = iso_memory_stream_new(buf, 3000, NULL); CU_ASSERT_EQUAL(ret, ISO_NULL_POINTER); } static void test_mem_open() { int ret; IsoStream *stream; unsigned char *buf; buf = malloc(3000); ret = iso_memory_stream_new(buf, 3000, &stream); CU_ASSERT_EQUAL(ret, 1); ret = iso_stream_open(stream); CU_ASSERT_EQUAL(ret, 1); /* try to open an already opened stream */ ret = iso_stream_open(stream); CU_ASSERT_EQUAL(ret, ISO_FILE_ALREADY_OPENED); ret = iso_stream_close(stream); CU_ASSERT_EQUAL(ret, 1); ret = iso_stream_close(stream); CU_ASSERT_EQUAL(ret, ISO_FILE_NOT_OPENED); iso_stream_unref(stream); } static void test_mem_read() { int ret; IsoStream *stream; unsigned char *buf; unsigned char rbuf[3000]; buf = malloc(3000); memset(buf, 2, 200); memset(buf + 200, 3, 300); memset(buf + 500, 5, 500); memset(buf + 1000, 10, 1000); memset(buf + 2000, 56, 48); memset(buf + 2048, 137, 22); memset(buf + 2070, 13, 130); memset(buf + 2200, 88, 800); ret = iso_memory_stream_new(buf, 3000, &stream); CU_ASSERT_EQUAL(ret, 1); /* test 1: read full buf */ ret = iso_stream_open(stream); CU_ASSERT_EQUAL(ret, 1); ret = iso_stream_read(stream, rbuf, 3000); CU_ASSERT_EQUAL(ret, 3000); CU_ASSERT_NSTRING_EQUAL(rbuf, buf, 3000); /* read again is EOF */ ret = iso_stream_read(stream, rbuf, 20); CU_ASSERT_EQUAL(ret, 0); ret = iso_stream_close(stream); CU_ASSERT_EQUAL(ret, 1); /* test 2: read more than available bytes */ ret = iso_stream_open(stream); CU_ASSERT_EQUAL(ret, 1); ret = iso_stream_read(stream, rbuf, 3050); CU_ASSERT_EQUAL(ret, 3000); CU_ASSERT_NSTRING_EQUAL(rbuf, buf, 3000); /* read again is EOF */ ret = iso_stream_read(stream, rbuf, 20); CU_ASSERT_EQUAL(ret, 0); ret = iso_stream_close(stream); CU_ASSERT_EQUAL(ret, 1); /* test 3: read in block size */ ret = iso_stream_open(stream); CU_ASSERT_EQUAL(ret, 1); ret = iso_stream_read(stream, rbuf, 2048); CU_ASSERT_EQUAL(ret, 2048); CU_ASSERT_NSTRING_EQUAL(rbuf, buf, 2048); ret = iso_stream_read(stream, rbuf, 2048); CU_ASSERT_EQUAL(ret, 3000 - 2048); CU_ASSERT_NSTRING_EQUAL(rbuf, buf + 2048, 3000 - 2048); ret = iso_stream_read(stream, rbuf, 20); CU_ASSERT_EQUAL(ret, 0); ret = iso_stream_close(stream); CU_ASSERT_EQUAL(ret, 1); iso_stream_unref(stream); } static void test_mem_size() { int ret; off_t size; IsoStream *stream; unsigned char *buf; buf = malloc(3000); ret = iso_memory_stream_new(buf, 3000, &stream); CU_ASSERT_EQUAL(ret, 1); size = iso_stream_get_size(stream); CU_ASSERT_EQUAL(size, 3000); iso_stream_unref(stream); } void add_stream_suite() { CU_pSuite pSuite = CU_add_suite("IsoStreamSuite", NULL, NULL); CU_add_test(pSuite, "iso_memory_stream_new()", test_mem_new); CU_add_test(pSuite, "MemoryStream->open()", test_mem_open); CU_add_test(pSuite, "MemoryStream->read()", test_mem_read); CU_add_test(pSuite, "MemoryStream->get_size()", test_mem_size); } /* * Unit test for node.h */ #include "libisofs.h" #include "node.h" #include "image.h" #include "test.h" #include "mocked_fsrc.h" #include static void test_iso_tree_add_new_dir() { int result; IsoDir *root; IsoDir *node1, *node2, *node3, *node4; IsoImage *image; result = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(result, 1); root = iso_image_get_root(image); CU_ASSERT_PTR_NOT_NULL(root); result = iso_tree_add_new_dir(root, "Dir1", &node1); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(root->nchildren, 1); CU_ASSERT_PTR_EQUAL(root->children, node1); CU_ASSERT_PTR_NULL(node1->node.next); CU_ASSERT_PTR_EQUAL(node1->node.parent, root); CU_ASSERT_EQUAL(node1->node.type, LIBISO_DIR); CU_ASSERT_STRING_EQUAL(node1->node.name, "Dir1"); /* creation of a second dir, to be inserted before */ result = iso_tree_add_new_dir(root, "A node to be added first", &node2); CU_ASSERT_EQUAL(result, 2); CU_ASSERT_EQUAL(root->nchildren, 2); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->node.next, node1); CU_ASSERT_PTR_NULL(node1->node.next); CU_ASSERT_PTR_EQUAL(node2->node.parent, root); CU_ASSERT_EQUAL(node2->node.type, LIBISO_DIR); CU_ASSERT_STRING_EQUAL(node2->node.name, "A node to be added first"); /* creation of a 3rd node, to be inserted last */ result = iso_tree_add_new_dir(root, "This node will be inserted last", &node3); CU_ASSERT_EQUAL(result, 3); CU_ASSERT_EQUAL(root->nchildren, 3); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->node.next, node1); CU_ASSERT_PTR_EQUAL(node1->node.next, node3); CU_ASSERT_PTR_NULL(node3->node.next); CU_ASSERT_PTR_EQUAL(node3->node.parent, root); CU_ASSERT_EQUAL(node3->node.type, LIBISO_DIR); CU_ASSERT_STRING_EQUAL(node3->node.name, "This node will be inserted last"); /* force some failures */ result = iso_tree_add_new_dir(NULL, "dsadas", &node4); CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); result = iso_tree_add_new_dir(root, NULL, &node4); CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); /* try to insert a new dir with same name */ result = iso_tree_add_new_dir(root, "This node will be inserted last", &node4); CU_ASSERT_EQUAL(result, ISO_NODE_NAME_NOT_UNIQUE); CU_ASSERT_EQUAL(root->nchildren, 3); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->node.next, node1); CU_ASSERT_PTR_EQUAL(node1->node.next, node3); CU_ASSERT_PTR_NULL(node3->node.next); CU_ASSERT_PTR_NULL(node4); /* but pointer to new dir can be null */ result = iso_tree_add_new_dir(root, "Another node", NULL); CU_ASSERT_EQUAL(result, 4); CU_ASSERT_EQUAL(root->nchildren, 4); CU_ASSERT_PTR_EQUAL(node2->node.next->next, node1); CU_ASSERT_STRING_EQUAL(node2->node.next->name, "Another node"); iso_image_unref(image); } static void test_iso_tree_add_new_symlink() { int result; IsoDir *root; IsoSymlink *node1, *node2, *node3, *node4; IsoImage *image; result = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(result, 1); root = iso_image_get_root(image); CU_ASSERT_PTR_NOT_NULL(root); result = iso_tree_add_new_symlink(root, "Link1", "/path/to/dest", &node1); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(root->nchildren, 1); CU_ASSERT_PTR_EQUAL(root->children, node1); CU_ASSERT_PTR_NULL(node1->node.next); CU_ASSERT_PTR_EQUAL(node1->node.parent, root); CU_ASSERT_EQUAL(node1->node.type, LIBISO_SYMLINK); CU_ASSERT_STRING_EQUAL(node1->node.name, "Link1"); CU_ASSERT_STRING_EQUAL(node1->dest, "/path/to/dest"); /* creation of a second link, to be inserted before */ result = iso_tree_add_new_symlink(root, "A node to be added first", "/home/me", &node2); CU_ASSERT_EQUAL(result, 2); CU_ASSERT_EQUAL(root->nchildren, 2); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->node.next, node1); CU_ASSERT_PTR_NULL(node1->node.next); CU_ASSERT_PTR_EQUAL(node2->node.parent, root); CU_ASSERT_EQUAL(node2->node.type, LIBISO_SYMLINK); CU_ASSERT_STRING_EQUAL(node2->node.name, "A node to be added first"); CU_ASSERT_STRING_EQUAL(node2->dest, "/home/me"); /* creation of a 3rd node, to be inserted last */ result = iso_tree_add_new_symlink(root, "This node will be inserted last", "/path/to/dest", &node3); CU_ASSERT_EQUAL(result, 3); CU_ASSERT_EQUAL(root->nchildren, 3); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->node.next, node1); CU_ASSERT_PTR_EQUAL(node1->node.next, node3); CU_ASSERT_PTR_NULL(node3->node.next); CU_ASSERT_PTR_EQUAL(node3->node.parent, root); CU_ASSERT_EQUAL(node3->node.type, LIBISO_SYMLINK); CU_ASSERT_STRING_EQUAL(node3->node.name, "This node will be inserted last"); CU_ASSERT_STRING_EQUAL(node3->dest, "/path/to/dest"); /* force some failures */ result = iso_tree_add_new_symlink(NULL, "dsadas", "/path/to/dest", &node4); CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); result = iso_tree_add_new_symlink(root, NULL, "/path/to/dest", &node4); CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); result = iso_tree_add_new_symlink(root, "dsadas", NULL, &node4); CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); /* try to insert a new link with same name */ result = iso_tree_add_new_symlink(root, "This node will be inserted last", "/", &node4); CU_ASSERT_EQUAL(result, ISO_NODE_NAME_NOT_UNIQUE); CU_ASSERT_EQUAL(root->nchildren, 3); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->node.next, node1); CU_ASSERT_PTR_EQUAL(node1->node.next, node3); CU_ASSERT_PTR_NULL(node3->node.next); CU_ASSERT_PTR_NULL(node4); /* but pointer to new link can be null */ result = iso_tree_add_new_symlink(root, "Another node", ".", NULL); CU_ASSERT_EQUAL(result, 4); CU_ASSERT_EQUAL(root->nchildren, 4); CU_ASSERT_PTR_EQUAL(node2->node.next->next, node1); CU_ASSERT_EQUAL(node2->node.next->type, LIBISO_SYMLINK); CU_ASSERT_STRING_EQUAL(((IsoSymlink*)(node2->node.next))->dest, "."); CU_ASSERT_STRING_EQUAL(node2->node.next->name, "Another node"); iso_image_unref(image); } static void test_iso_tree_add_new_special() { int result; IsoDir *root; IsoSpecial *node1, *node2, *node3, *node4; IsoImage *image; result = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(result, 1); root = iso_image_get_root(image); CU_ASSERT_PTR_NOT_NULL(root); result = iso_tree_add_new_special(root, "Special1", S_IFSOCK | 0644, 0, &node1); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(root->nchildren, 1); CU_ASSERT_PTR_EQUAL(root->children, node1); CU_ASSERT_PTR_NULL(node1->node.next); CU_ASSERT_PTR_EQUAL(node1->node.parent, root); CU_ASSERT_EQUAL(node1->node.type, LIBISO_SPECIAL); CU_ASSERT_STRING_EQUAL(node1->node.name, "Special1"); CU_ASSERT_EQUAL(node1->dev, 0); CU_ASSERT_EQUAL(node1->node.mode, S_IFSOCK | 0644); /* creation of a block dev, to be inserted before */ result = iso_tree_add_new_special(root, "A node to be added first", S_IFBLK | 0640, 34, &node2); CU_ASSERT_EQUAL(result, 2); CU_ASSERT_EQUAL(root->nchildren, 2); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->node.next, node1); CU_ASSERT_PTR_NULL(node1->node.next); CU_ASSERT_PTR_EQUAL(node2->node.parent, root); CU_ASSERT_EQUAL(node2->node.type, LIBISO_SPECIAL); CU_ASSERT_STRING_EQUAL(node2->node.name, "A node to be added first"); CU_ASSERT_EQUAL(node2->dev, 34); CU_ASSERT_EQUAL(node2->node.mode, S_IFBLK | 0640); /* creation of a 3rd node, to be inserted last */ result = iso_tree_add_new_special(root, "This node will be inserted last", S_IFCHR | 0440, 345, &node3); CU_ASSERT_EQUAL(result, 3); CU_ASSERT_EQUAL(root->nchildren, 3); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->node.next, node1); CU_ASSERT_PTR_EQUAL(node1->node.next, node3); CU_ASSERT_PTR_NULL(node3->node.next); CU_ASSERT_PTR_EQUAL(node3->node.parent, root); CU_ASSERT_EQUAL(node3->node.type, LIBISO_SPECIAL); CU_ASSERT_STRING_EQUAL(node3->node.name, "This node will be inserted last"); CU_ASSERT_EQUAL(node3->dev, 345); CU_ASSERT_EQUAL(node3->node.mode, S_IFCHR | 0440); /* force some failures */ result = iso_tree_add_new_special(NULL, "dsadas", S_IFBLK | 0440, 345, &node4); CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); result = iso_tree_add_new_special(root, NULL, S_IFBLK | 0440, 345, &node4); CU_ASSERT_EQUAL(result, ISO_NULL_POINTER); result = iso_tree_add_new_special(root, "dsadas", S_IFDIR | 0666, 0, &node4); CU_ASSERT_EQUAL(result, ISO_WRONG_ARG_VALUE); result = iso_tree_add_new_special(root, "dsadas", S_IFREG | 0666, 0, &node4); CU_ASSERT_EQUAL(result, ISO_WRONG_ARG_VALUE); result = iso_tree_add_new_special(root, "dsadas", S_IFLNK | 0666, 0, &node4); CU_ASSERT_EQUAL(result, ISO_WRONG_ARG_VALUE); /* try to insert a new special file with same name */ result = iso_tree_add_new_special(root, "This node will be inserted last", S_IFIFO | 0666, 0, &node4); CU_ASSERT_EQUAL(result, ISO_NODE_NAME_NOT_UNIQUE); CU_ASSERT_EQUAL(root->nchildren, 3); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->node.next, node1); CU_ASSERT_PTR_EQUAL(node1->node.next, node3); CU_ASSERT_PTR_NULL(node3->node.next); CU_ASSERT_PTR_NULL(node4); /* but pointer to new special can be null */ result = iso_tree_add_new_special(root, "Another node", S_IFIFO | 0666, 0, NULL); CU_ASSERT_EQUAL(result, 4); CU_ASSERT_EQUAL(root->nchildren, 4); CU_ASSERT_PTR_EQUAL(node2->node.next->next, node1); CU_ASSERT_EQUAL(node2->node.next->type, LIBISO_SPECIAL); CU_ASSERT_EQUAL(((IsoSpecial*)(node2->node.next))->dev, 0); CU_ASSERT_EQUAL(node2->node.next->mode, S_IFIFO | 0666); CU_ASSERT_STRING_EQUAL(node2->node.next->name, "Another node"); iso_image_unref(image); } static void test_iso_tree_add_node_dir() { int result; IsoDir *root; IsoNode *node1, *node2, *node3, *node4; IsoImage *image; IsoFilesystem *fs; struct stat info; struct mock_file *mroot, *dir1, *dir2; result = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(result, 1); root = iso_image_get_root(image); CU_ASSERT_PTR_NOT_NULL(root); /* replace image filesystem with out mockep one */ iso_filesystem_unref(image->fs); result = test_mocked_filesystem_new(&fs); CU_ASSERT_EQUAL(result, 1); image->fs = fs; mroot = test_mocked_fs_get_root(fs); /* add some files to the filesystem */ info.st_mode = S_IFDIR | 0550; info.st_uid = 20; info.st_gid = 21; info.st_atime = 234523; info.st_ctime = 23432432; info.st_mtime = 1111123; result = test_mocked_fs_add_dir("dir", mroot, info, &dir1); CU_ASSERT_EQUAL(result, 1); info.st_mode = S_IFDIR | 0555; info.st_uid = 30; info.st_gid = 31; info.st_atime = 3234523; info.st_ctime = 3234432; info.st_mtime = 3111123; result = test_mocked_fs_add_dir("a child node", dir1, info, &dir2); CU_ASSERT_EQUAL(result, 1); info.st_mode = S_IFDIR | 0750; info.st_uid = 40; info.st_gid = 41; info.st_atime = 4234523; info.st_ctime = 4234432; info.st_mtime = 4111123; result = test_mocked_fs_add_dir("another one", dir1, info, &dir2); CU_ASSERT_EQUAL(result, 1); info.st_mode = S_IFDIR | 0755; info.st_uid = 50; info.st_gid = 51; info.st_atime = 5234523; info.st_ctime = 5234432; info.st_mtime = 5111123; result = test_mocked_fs_add_dir("zzzz", mroot, info, &dir2); CU_ASSERT_EQUAL(result, 1); /* and now insert those files to the image */ result = iso_tree_add_node(image, root, "/dir", &node1); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(root->nchildren, 1); CU_ASSERT_PTR_EQUAL(root->children, node1); CU_ASSERT_PTR_NULL(node1->next); CU_ASSERT_PTR_EQUAL(node1->parent, root); CU_ASSERT_EQUAL(node1->type, LIBISO_DIR); CU_ASSERT_STRING_EQUAL(node1->name, "dir"); CU_ASSERT_EQUAL(node1->mode, S_IFDIR | 0550); CU_ASSERT_EQUAL(node1->uid, 20); CU_ASSERT_EQUAL(node1->gid, 21); CU_ASSERT_EQUAL(node1->atime, 234523); CU_ASSERT_EQUAL(node1->ctime, 23432432); CU_ASSERT_EQUAL(node1->mtime, 1111123); CU_ASSERT_PTR_NULL(((IsoDir*)node1)->children); CU_ASSERT_EQUAL(((IsoDir*)node1)->nchildren, 0); result = iso_tree_add_node(image, root, "/dir/a child node", &node2); CU_ASSERT_EQUAL(result, 2); CU_ASSERT_EQUAL(root->nchildren, 2); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->next, node1); CU_ASSERT_PTR_NULL(node1->next); CU_ASSERT_PTR_EQUAL(node1->parent, root); CU_ASSERT_PTR_EQUAL(node2->parent, root); CU_ASSERT_EQUAL(node2->type, LIBISO_DIR); CU_ASSERT_STRING_EQUAL(node2->name, "a child node"); CU_ASSERT_EQUAL(node2->mode, S_IFDIR | 0555); CU_ASSERT_EQUAL(node2->uid, 30); CU_ASSERT_EQUAL(node2->gid, 31); CU_ASSERT_EQUAL(node2->atime, 3234523); CU_ASSERT_EQUAL(node2->ctime, 3234432); CU_ASSERT_EQUAL(node2->mtime, 3111123); CU_ASSERT_PTR_NULL(((IsoDir*)node2)->children); CU_ASSERT_EQUAL(((IsoDir*)node2)->nchildren, 0); result = iso_tree_add_node(image, root, "/dir/another one", &node3); CU_ASSERT_EQUAL(result, 3); CU_ASSERT_EQUAL(root->nchildren, 3); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->next, node3); CU_ASSERT_PTR_EQUAL(node3->next, node1); CU_ASSERT_PTR_NULL(node1->next); CU_ASSERT_PTR_EQUAL(node1->parent, root); CU_ASSERT_PTR_EQUAL(node2->parent, root); CU_ASSERT_PTR_EQUAL(node3->parent, root); CU_ASSERT_EQUAL(node3->type, LIBISO_DIR); CU_ASSERT_STRING_EQUAL(node3->name, "another one"); CU_ASSERT_EQUAL(node3->mode, S_IFDIR | 0750); CU_ASSERT_EQUAL(node3->uid, 40); CU_ASSERT_EQUAL(node3->gid, 41); CU_ASSERT_EQUAL(node3->atime, 4234523); CU_ASSERT_EQUAL(node3->ctime, 4234432); CU_ASSERT_EQUAL(node3->mtime, 4111123); CU_ASSERT_PTR_NULL(((IsoDir*)node3)->children); CU_ASSERT_EQUAL(((IsoDir*)node3)->nchildren, 0); result = iso_tree_add_node(image, root, "/zzzz", &node4); CU_ASSERT_EQUAL(result, 4); CU_ASSERT_EQUAL(root->nchildren, 4); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->next, node3); CU_ASSERT_PTR_EQUAL(node3->next, node1); CU_ASSERT_PTR_EQUAL(node1->next, node4); CU_ASSERT_PTR_NULL(node4->next); CU_ASSERT_PTR_EQUAL(node1->parent, root); CU_ASSERT_PTR_EQUAL(node2->parent, root); CU_ASSERT_PTR_EQUAL(node3->parent, root); CU_ASSERT_PTR_EQUAL(node4->parent, root); CU_ASSERT_EQUAL(node4->type, LIBISO_DIR); CU_ASSERT_STRING_EQUAL(node4->name, "zzzz"); CU_ASSERT_EQUAL(node4->mode, S_IFDIR | 0755); CU_ASSERT_EQUAL(node4->uid, 50); CU_ASSERT_EQUAL(node4->gid, 51); CU_ASSERT_EQUAL(node4->atime, 5234523); CU_ASSERT_EQUAL(node4->ctime, 5234432); CU_ASSERT_EQUAL(node4->mtime, 5111123); CU_ASSERT_PTR_NULL(((IsoDir*)node4)->children); CU_ASSERT_EQUAL(((IsoDir*)node4)->nchildren, 0); iso_image_unref(image); } static void test_iso_tree_add_node_link() { int result; IsoDir *root; IsoNode *node1, *node2, *node3; IsoImage *image; IsoFilesystem *fs; struct stat info; struct mock_file *mroot, *link; result = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(result, 1); root = iso_image_get_root(image); CU_ASSERT_PTR_NOT_NULL(root); /* replace image filesystem with out mockep one */ iso_filesystem_unref(image->fs); result = test_mocked_filesystem_new(&fs); CU_ASSERT_EQUAL(result, 1); image->fs = fs; mroot = test_mocked_fs_get_root(fs); /* add some files to the filesystem */ info.st_mode = S_IFLNK | 0777; info.st_uid = 12; info.st_gid = 13; info.st_atime = 123444; info.st_ctime = 123555; info.st_mtime = 123666; result = test_mocked_fs_add_symlink("link1", mroot, info, "/home/me", &link); CU_ASSERT_EQUAL(result, 1); info.st_mode = S_IFLNK | 0555; info.st_uid = 22; info.st_gid = 23; info.st_atime = 223444; info.st_ctime = 223555; info.st_mtime = 223666; result = test_mocked_fs_add_symlink("another link", mroot, info, "/", &link); CU_ASSERT_EQUAL(result, 1); info.st_mode = S_IFLNK | 0750; info.st_uid = 32; info.st_gid = 33; info.st_atime = 323444; info.st_ctime = 323555; info.st_mtime = 323666; result = test_mocked_fs_add_symlink("this will be the last", mroot, info, "/etc", &link); CU_ASSERT_EQUAL(result, 1); /* and now insert those files to the image */ result = iso_tree_add_node(image, root, "/link1", &node1); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(root->nchildren, 1); CU_ASSERT_PTR_EQUAL(root->children, node1); CU_ASSERT_PTR_NULL(node1->next); CU_ASSERT_PTR_EQUAL(node1->parent, root); CU_ASSERT_EQUAL(node1->type, LIBISO_SYMLINK); CU_ASSERT_STRING_EQUAL(node1->name, "link1"); CU_ASSERT_EQUAL(node1->mode, S_IFLNK | 0777); CU_ASSERT_EQUAL(node1->uid, 12); CU_ASSERT_EQUAL(node1->gid, 13); CU_ASSERT_EQUAL(node1->atime, 123444); CU_ASSERT_EQUAL(node1->ctime, 123555); CU_ASSERT_EQUAL(node1->mtime, 123666); CU_ASSERT_STRING_EQUAL(((IsoSymlink*)node1)->dest, "/home/me"); result = iso_tree_add_node(image, root, "/another link", &node2); CU_ASSERT_EQUAL(result, 2); CU_ASSERT_EQUAL(root->nchildren, 2); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->next, node1); CU_ASSERT_PTR_NULL(node1->next); CU_ASSERT_PTR_EQUAL(node1->parent, root); CU_ASSERT_PTR_EQUAL(node2->parent, root); CU_ASSERT_EQUAL(node2->type, LIBISO_SYMLINK); CU_ASSERT_STRING_EQUAL(node2->name, "another link"); CU_ASSERT_EQUAL(node2->mode, S_IFLNK | 0555); CU_ASSERT_EQUAL(node2->uid, 22); CU_ASSERT_EQUAL(node2->gid, 23); CU_ASSERT_EQUAL(node2->atime, 223444); CU_ASSERT_EQUAL(node2->ctime, 223555); CU_ASSERT_EQUAL(node2->mtime, 223666); CU_ASSERT_STRING_EQUAL(((IsoSymlink*)node2)->dest, "/"); result = iso_tree_add_node(image, root, "/this will be the last", &node3); CU_ASSERT_EQUAL(result, 3); CU_ASSERT_EQUAL(root->nchildren, 3); CU_ASSERT_PTR_EQUAL(root->children, node2); CU_ASSERT_PTR_EQUAL(node2->next, node1); CU_ASSERT_PTR_EQUAL(node1->next, node3); CU_ASSERT_PTR_NULL(node3->next); CU_ASSERT_PTR_EQUAL(node1->parent, root); CU_ASSERT_PTR_EQUAL(node2->parent, root); CU_ASSERT_PTR_EQUAL(node3->parent, root); CU_ASSERT_EQUAL(node3->type, LIBISO_SYMLINK); CU_ASSERT_STRING_EQUAL(node3->name, "this will be the last"); CU_ASSERT_EQUAL(node3->mode, S_IFLNK | 0750); CU_ASSERT_EQUAL(node3->uid, 32); CU_ASSERT_EQUAL(node3->gid, 33); CU_ASSERT_EQUAL(node3->atime, 323444); CU_ASSERT_EQUAL(node3->ctime, 323555); CU_ASSERT_EQUAL(node3->mtime, 323666); CU_ASSERT_STRING_EQUAL(((IsoSymlink*)node3)->dest, "/etc"); iso_image_unref(image); } static void test_iso_tree_path_to_node() { int result; IsoDir *root; IsoDir *node1, *node2, *node11; IsoNode *node; IsoImage *image; IsoFilesystem *fs; result = iso_image_new("volume_id", &image); CU_ASSERT_EQUAL(result, 1); root = iso_image_get_root(image); CU_ASSERT_PTR_NOT_NULL(root); /* replace image filesystem with out mockep one */ iso_filesystem_unref(image->fs); result = test_mocked_filesystem_new(&fs); CU_ASSERT_EQUAL(result, 1); image->fs = fs; /* add some files */ result = iso_tree_add_new_dir(root, "Dir1", &node1); CU_ASSERT_EQUAL(result, 1); result = iso_tree_add_new_dir(root, "Dir2", (IsoDir**)&node2); CU_ASSERT_EQUAL(result, 2); result = iso_tree_add_new_dir((IsoDir*)node1, "Dir11", (IsoDir**)&node11); CU_ASSERT_EQUAL(result, 1); /* retrive some items */ result = iso_tree_path_to_node(image, "/", &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, root); result = iso_tree_path_to_node(image, "/Dir1", &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node1); result = iso_tree_path_to_node(image, "/Dir2", &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node2); result = iso_tree_path_to_node(image, "/Dir1/Dir11", &node); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_PTR_EQUAL(node, node11); /* some failtures */ result = iso_tree_path_to_node(image, "/Dir2/Dir11", &node); CU_ASSERT_EQUAL(result, 0); CU_ASSERT_PTR_NULL(node); iso_image_unref(image); } void add_tree_suite() { CU_pSuite pSuite = CU_add_suite("Iso Tree Suite", NULL, NULL); CU_add_test(pSuite, "iso_tree_add_new_dir()", test_iso_tree_add_new_dir); CU_add_test(pSuite, "iso_tree_add_new_symlink()", test_iso_tree_add_new_symlink); CU_add_test(pSuite, "iso_tree_add_new_special()", test_iso_tree_add_new_special); CU_add_test(pSuite, "iso_tree_add_node() [1. dir]", test_iso_tree_add_node_dir); CU_add_test(pSuite, "iso_tree_add_node() [2. symlink]", test_iso_tree_add_node_link); CU_add_test(pSuite, "iso_tree_path_to_node()", test_iso_tree_path_to_node); } /* * Unit test for util.h * * This test utiliy functions */ #include "test.h" #include "util.h" #include #include #include static void test_int_pow() { CU_ASSERT_EQUAL(int_pow(1, 2), 1); CU_ASSERT_EQUAL(int_pow(2, 2), 4); CU_ASSERT_EQUAL(int_pow(0, 2), 0); CU_ASSERT_EQUAL(int_pow(-1, 2), 1); CU_ASSERT_EQUAL(int_pow(-1, 3), -1); CU_ASSERT_EQUAL(int_pow(3, 2), 9); CU_ASSERT_EQUAL(int_pow(3, 10), 59049); } static void test_strconv() { int ret; char *out; /* Prova de cadeia com codificação ISO-8859-15 */ unsigned char in1[45] = {0x50, 0x72, 0x6f, 0x76, 0x61, 0x20, 0x64, 0x65, 0x20, 0x63, 0x61, 0x64, 0x65, 0x69, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x20, 0x63, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0xe7, 0xe3, 0x6f, 0x20, 0x49, 0x53, 0x4f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, 0x31, 0x35, 0x0a, 0x00}; /* encoded in ISO-8859-15 */ unsigned char out1[47] = {0x50, 0x72, 0x6f, 0x76, 0x61, 0x20, 0x64, 0x65, 0x20, 0x63, 0x61, 0x64, 0x65, 0x69, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x20, 0x63, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0xc3, 0xa7, 0xc3, 0xa3, 0x6f, 0x20, 0x49, 0x53, 0x4f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, 0x31, 0x35, 0x0a, 0x00}; /* encoded in UTF-8 */ unsigned char in2[45] = {0x50, 0x72, 0x6f, 0x76, 0x61, 0x20, 0x64, 0x65, 0x20, 0x63, 0x61, 0x64, 0x65, 0x69, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x20, 0x63, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0xe7, 0xe3, 0x6f, 0x20, 0x49, 0x53, 0x4f, 0x2d, 0x38, 0x38, 0xff, 0xff, 0x2d, 0x31, 0x35, 0x0a, 0x00}; /* incorrect encoding */ /* ISO-8859-15 to UTF-8 */ ret = strconv((char*)in1, "ISO-8859-15", "UTF-8", &out); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_STRING_EQUAL(out, (char*)out1); free(out); /* UTF-8 to ISO-8859-15 */ ret = strconv((char*)out1, "UTF-8", "ISO-8859-15", &out); CU_ASSERT_EQUAL(ret, 1); CU_ASSERT_STRING_EQUAL(out, (char*)in1); free(out); /* try with an incorrect input */ ret = strconv((char*)in2, "UTF-8", "ISO-8859-15", &out); CU_ASSERT_EQUAL(ret, ISO_CHARSET_CONV_ERROR); } static void test_div_up() { CU_ASSERT_EQUAL( DIV_UP(1, 2), 1 ); CU_ASSERT_EQUAL( DIV_UP(2, 2), 1 ); CU_ASSERT_EQUAL( DIV_UP(0, 2), 0 ); CU_ASSERT_EQUAL( DIV_UP(-1, 2), 0 ); CU_ASSERT_EQUAL( DIV_UP(3, 2), 2 ); } static void test_round_up() { CU_ASSERT_EQUAL( ROUND_UP(1, 2), 2 ); CU_ASSERT_EQUAL( ROUND_UP(2, 2), 2 ); CU_ASSERT_EQUAL( ROUND_UP(0, 2), 0 ); CU_ASSERT_EQUAL( ROUND_UP(-1, 2), 0 ); CU_ASSERT_EQUAL( ROUND_UP(3, 2), 4 ); CU_ASSERT_EQUAL( ROUND_UP(15, 7), 21 ); CU_ASSERT_EQUAL( ROUND_UP(13, 7), 14 ); CU_ASSERT_EQUAL( ROUND_UP(14, 7), 14 ); } static void test_iso_lsb_msb() { uint8_t buf[4]; uint32_t num; num = 0x01020304; iso_lsb(buf, num, 4); CU_ASSERT_EQUAL( buf[0], 0x04 ); CU_ASSERT_EQUAL( buf[1], 0x03 ); CU_ASSERT_EQUAL( buf[2], 0x02 ); CU_ASSERT_EQUAL( buf[3], 0x01 ); iso_msb(buf, num, 4); CU_ASSERT_EQUAL( buf[0], 0x01 ); CU_ASSERT_EQUAL( buf[1], 0x02 ); CU_ASSERT_EQUAL( buf[2], 0x03 ); CU_ASSERT_EQUAL( buf[3], 0x04 ); iso_lsb(buf, num, 2); CU_ASSERT_EQUAL( buf[0], 0x04 ); CU_ASSERT_EQUAL( buf[1], 0x03 ); iso_msb(buf, num, 2); CU_ASSERT_EQUAL( buf[0], 0x03 ); CU_ASSERT_EQUAL( buf[1], 0x04 ); } static void test_iso_read_lsb_msb() { uint8_t buf[4]; uint32_t num; buf[0] = 0x04; buf[1] = 0x03; buf[2] = 0x02; buf[3] = 0x01; num = iso_read_lsb(buf, 4); CU_ASSERT_EQUAL(num, 0x01020304); num = iso_read_msb(buf, 4); CU_ASSERT_EQUAL(num, 0x04030201); num = iso_read_lsb(buf, 2); CU_ASSERT_EQUAL(num, 0x0304); num = iso_read_msb(buf, 2); CU_ASSERT_EQUAL(num, 0x0403); } static void test_iso_bb() { uint8_t buf[8]; uint32_t num; num = 0x01020304; iso_bb(buf, num, 4); CU_ASSERT_EQUAL( buf[0], 0x04 ); CU_ASSERT_EQUAL( buf[1], 0x03 ); CU_ASSERT_EQUAL( buf[2], 0x02 ); CU_ASSERT_EQUAL( buf[3], 0x01 ); CU_ASSERT_EQUAL( buf[4], 0x01 ); CU_ASSERT_EQUAL( buf[5], 0x02 ); CU_ASSERT_EQUAL( buf[6], 0x03 ); CU_ASSERT_EQUAL( buf[7], 0x04 ); iso_bb(buf, num, 2); CU_ASSERT_EQUAL( buf[0], 0x04 ); CU_ASSERT_EQUAL( buf[1], 0x03 ); CU_ASSERT_EQUAL( buf[2], 0x03 ); CU_ASSERT_EQUAL( buf[3], 0x04 ); } static void test_iso_datetime_7() { uint8_t buf[7]; time_t t1, t2, tr; char *tz; struct tm tp; tz = getenv("TZ"); setenv("TZ", "", 1); tzset(); strptime("01-03-1976 13:27:45", "%d-%m-%Y %T", &tp); t1 = mktime(&tp); /* t1 in GMT */ strptime("01-07-2007 13:27:45", "%d-%m-%Y %T", &tp); t2 = mktime(&tp); /* t1 in GMT (summer time) */ /* ----------------- European Timezones ----------------------*/ setenv("TZ", "Europe/Madrid", 1); tzset(); iso_datetime_7(buf, t1, 0); CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ CU_ASSERT_EQUAL(buf[1], 3); /* month */ CU_ASSERT_EQUAL(buf[2], 1); /* day */ CU_ASSERT_EQUAL(buf[3], 14); /* hour (GMT+1) */ CU_ASSERT_EQUAL(buf[4], 27); /* minute */ CU_ASSERT_EQUAL(buf[5], 45); /* second */ CU_ASSERT_EQUAL((int8_t)buf[6], 4); /* GMT+1 hour for CET */ /* check that reading returns the same time */ tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); CU_ASSERT_EQUAL(buf[0], 107); /* year since 1900 */ CU_ASSERT_EQUAL(buf[1], 7); /* month */ CU_ASSERT_EQUAL(buf[2], 1); /* day */ CU_ASSERT_EQUAL(buf[3], 15); /* hour (GMT+2, summer time) */ CU_ASSERT_EQUAL(buf[4], 27); /* minute */ CU_ASSERT_EQUAL(buf[5], 45); /* second */ CU_ASSERT_EQUAL((int8_t)buf[6], 8); /* GMT+2 hour for CEST */ /* check that reading returns the same time */ tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "Europe/London", 1); tzset(); iso_datetime_7(buf, t1, 0); CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ CU_ASSERT_EQUAL(buf[1], 3); /* month */ CU_ASSERT_EQUAL(buf[2], 1); /* day */ CU_ASSERT_EQUAL(buf[3], 13); /* hour (GMT+0) */ CU_ASSERT_EQUAL(buf[4], 27); /* minute */ CU_ASSERT_EQUAL(buf[5], 45); /* second */ CU_ASSERT_EQUAL((int8_t)buf[6], 0); /* GMT+0 */ /* check that reading returns the same time */ tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); CU_ASSERT_EQUAL(buf[0], 107); /* year since 1900 */ CU_ASSERT_EQUAL(buf[1], 7); /* month */ CU_ASSERT_EQUAL(buf[2], 1); /* day */ CU_ASSERT_EQUAL(buf[3], 14); /* hour (GMT+1, summer time) */ CU_ASSERT_EQUAL(buf[4], 27); /* minute */ CU_ASSERT_EQUAL(buf[5], 45); /* second */ CU_ASSERT_EQUAL((int8_t)buf[6], 4); /* GMT+1 */ /* check that reading returns the same time */ tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); /* ----------------- American Timezones ----------------------*/ setenv("TZ", "America/New_York", 1); tzset(); iso_datetime_7(buf, t1, 0); CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ CU_ASSERT_EQUAL(buf[1], 3); /* month */ CU_ASSERT_EQUAL(buf[2], 1); /* day */ CU_ASSERT_EQUAL(buf[3], 8); /* hour */ CU_ASSERT_EQUAL(buf[4], 27); /* minute */ CU_ASSERT_EQUAL(buf[5], 45); /* second */ CU_ASSERT_EQUAL((int8_t)buf[6], -5*4); /* GMT-5 for EST */ /* check that reading returns the same time */ tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); /* ----------------- Asia Timezones ----------------------*/ setenv("TZ", "Asia/Hong_Kong", 1); tzset(); iso_datetime_7(buf, t1, 0); CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ CU_ASSERT_EQUAL(buf[1], 3); /* month */ CU_ASSERT_EQUAL(buf[2], 1); /* day */ CU_ASSERT_EQUAL(buf[3], 21); /* hour */ CU_ASSERT_EQUAL(buf[4], 27); /* minute */ CU_ASSERT_EQUAL(buf[5], 45); /* second */ CU_ASSERT_EQUAL((int8_t)buf[6], 8*4); /* GMT+8 */ /* check that reading returns the same time */ tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); /* read from another timestamp */ setenv("TZ", "Europe/Madrid", 1); tzset(); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); /* ----------------- Africa Timezones ----------------------*/ /* Africa country without Daylight saving time */ setenv("TZ", "Africa/Luanda", 1); tzset(); iso_datetime_7(buf, t1, 0); CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ CU_ASSERT_EQUAL(buf[1], 3); /* month */ CU_ASSERT_EQUAL(buf[2], 1); /* day */ CU_ASSERT_EQUAL(buf[3], 14); /* hour (GMT+1) */ CU_ASSERT_EQUAL(buf[4], 27); /* minute */ CU_ASSERT_EQUAL(buf[5], 45); /* second */ CU_ASSERT_EQUAL((int8_t)buf[6], 4); /* GMT+1 hour */ /* check that reading returns the same time */ tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); CU_ASSERT_EQUAL(buf[0], 107); /* year since 1900 */ CU_ASSERT_EQUAL(buf[1], 7); /* month */ CU_ASSERT_EQUAL(buf[2], 1); /* day */ CU_ASSERT_EQUAL(buf[3], 14); /* hour (GMT+1, no summer time) */ CU_ASSERT_EQUAL(buf[4], 27); /* minute */ CU_ASSERT_EQUAL(buf[5], 45); /* second */ CU_ASSERT_EQUAL((int8_t)buf[6], 4); /* GMT+1 hour */ /* check that reading returns the same time */ tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); /* ----------------- Australia Timezones ----------------------*/ /* this is GMT+9:30 (note that in South summer is winter in North) */ setenv("TZ", "Australia/Broken_Hill", 1); tzset(); iso_datetime_7(buf, t1, 0); CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ CU_ASSERT_EQUAL(buf[1], 3); /* month */ CU_ASSERT_EQUAL(buf[2], 1); /* day */ CU_ASSERT_EQUAL(buf[3], 23); /* hour GMT+9+1 (summer time!!) */ CU_ASSERT_EQUAL(buf[4], 57); /* minute + 30 */ CU_ASSERT_EQUAL(buf[5], 45); /* second */ CU_ASSERT_EQUAL((int8_t)buf[6], 42); /* GMT+9:30 hour + 1 (summer time) */ /* check that reading returns the same time */ tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); CU_ASSERT_EQUAL(buf[0], 107); /* year since 1900 */ CU_ASSERT_EQUAL(buf[1], 7); /* month */ CU_ASSERT_EQUAL(buf[2], 1); /* day */ CU_ASSERT_EQUAL(buf[3], 22); /* hour (GMT+9) */ CU_ASSERT_EQUAL(buf[4], 57); /* minute +30 */ CU_ASSERT_EQUAL(buf[5], 45); /* second */ CU_ASSERT_EQUAL((int8_t)buf[6], 38); /* GMT+9:30 */ /* check that reading returns the same time */ tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); /* ----------------- Pacific Timezones ----------------------*/ /* this is GMT+13, the max supported */ setenv("TZ", "Pacific/Tongatapu", 1); tzset(); iso_datetime_7(buf, t1, 0); CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ CU_ASSERT_EQUAL(buf[1], 3); /* month */ CU_ASSERT_EQUAL(buf[2], 2); /* day */ CU_ASSERT_EQUAL(buf[3], 2); /* hour (GMT+13) */ CU_ASSERT_EQUAL(buf[4], 27); /* minute */ CU_ASSERT_EQUAL(buf[5], 45); /* second */ CU_ASSERT_EQUAL((int8_t)buf[6], 52); /* GMT+13 hour */ /* check that reading returns the same time */ tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); /* this is GMT-11, I can't found a -12 timezone */ setenv("TZ", "Pacific/Pago_Pago", 1); tzset(); iso_datetime_7(buf, t1, 0); CU_ASSERT_EQUAL(buf[0], 76); /* year since 1900 */ CU_ASSERT_EQUAL(buf[1], 3); /* month */ CU_ASSERT_EQUAL(buf[2], 1); /* day */ CU_ASSERT_EQUAL(buf[3], 2); /* hour (GMT-11) */ CU_ASSERT_EQUAL(buf[4], 27); /* minute */ CU_ASSERT_EQUAL(buf[5], 45); /* second */ CU_ASSERT_EQUAL((int8_t)buf[6], -44); /* GMT-11 hour */ /* check that reading returns the same time */ tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); /* --- and now test from several zones, just for write/read compatibilty */ setenv("TZ", "Pacific/Kiritimati", 1); tzset(); iso_datetime_7(buf, t1, 1); /* this needs GMT */ tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "America/Argentina/La_Rioja", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "America/Argentina/La_Rioja", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "America/Caracas", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "Asia/Bangkok", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "Asia/Tehran", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "Pacific/Pitcairn", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "Antarctica/McMurdo", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "EET", 1); /* Eastern European Time */ tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "Europe/Moscow", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "Asia/Novosibirsk", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "Asia/Vladivostok", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "Asia/Anadyr", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "Atlantic/Canary", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "Indian/Mauritius", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); setenv("TZ", "America/Los_Angeles", 1); tzset(); iso_datetime_7(buf, t1, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t1); iso_datetime_7(buf, t2, 0); tr = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(tr, t2); if (tz) setenv("TZ", tz, 1); else unsetenv("TZ"); tzset(); } static void test_iso_1_dirid() { char *dir; dir = iso_1_dirid("dir1"); CU_ASSERT_STRING_EQUAL(dir, "DIR1"); free(dir); dir = iso_1_dirid("dIR1"); CU_ASSERT_STRING_EQUAL(dir, "DIR1"); free(dir); dir = iso_1_dirid("DIR1"); CU_ASSERT_STRING_EQUAL(dir, "DIR1"); free(dir); dir = iso_1_dirid("dirwithbigname"); CU_ASSERT_STRING_EQUAL(dir, "DIRWITHB"); free(dir); dir = iso_1_dirid("dirwith8"); CU_ASSERT_STRING_EQUAL(dir, "DIRWITH8"); free(dir); dir = iso_1_dirid("dir.1"); CU_ASSERT_STRING_EQUAL(dir, "DIR_1"); free(dir); dir = iso_1_dirid("4f<0KmM::xcvf"); CU_ASSERT_STRING_EQUAL(dir, "4F_0KMM_"); free(dir); } static void test_iso_2_dirid() { char *dir; dir = iso_2_dirid("dir1"); CU_ASSERT_STRING_EQUAL(dir, "DIR1"); free(dir); dir = iso_2_dirid("dIR1"); CU_ASSERT_STRING_EQUAL(dir, "DIR1"); free(dir); dir = iso_2_dirid("DIR1"); CU_ASSERT_STRING_EQUAL(dir, "DIR1"); free(dir); dir = iso_2_dirid("dirwithbigname"); CU_ASSERT_STRING_EQUAL(dir, "DIRWITHBIGNAME"); free(dir); dir = iso_2_dirid("dirwith8"); CU_ASSERT_STRING_EQUAL(dir, "DIRWITH8"); free(dir); dir = iso_2_dirid("dir.1"); CU_ASSERT_STRING_EQUAL(dir, "DIR_1"); free(dir); dir = iso_2_dirid("4f<0KmM::xcvf"); CU_ASSERT_STRING_EQUAL(dir, "4F_0KMM__XCVF"); free(dir); dir = iso_2_dirid("directory with 31 characters ok"); CU_ASSERT_STRING_EQUAL(dir, "DIRECTORY_WITH_31_CHARACTERS_OK"); free(dir); dir = iso_2_dirid("directory with more than 31 characters"); CU_ASSERT_STRING_EQUAL(dir, "DIRECTORY_WITH_MORE_THAN_31_CHA"); free(dir); } static void test_iso_1_fileid() { char *file; file = iso_1_fileid("file1"); CU_ASSERT_STRING_EQUAL(file, "FILE1."); free(file); file = iso_1_fileid("fILe1"); CU_ASSERT_STRING_EQUAL(file, "FILE1."); free(file); file = iso_1_fileid("FILE1"); CU_ASSERT_STRING_EQUAL(file, "FILE1."); free(file); file = iso_1_fileid(".EXT"); CU_ASSERT_STRING_EQUAL(file, ".EXT"); free(file); file = iso_1_fileid("file.ext"); CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); free(file); file = iso_1_fileid("fiLE.ext"); CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); free(file); file = iso_1_fileid("file.EXt"); CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); free(file); file = iso_1_fileid("FILE.EXT"); CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); free(file); file = iso_1_fileid("bigfilename"); CU_ASSERT_STRING_EQUAL(file, "BIGFILEN."); free(file); file = iso_1_fileid("bigfilename.ext"); CU_ASSERT_STRING_EQUAL(file, "BIGFILEN.EXT"); free(file); file = iso_1_fileid("bigfilename.e"); CU_ASSERT_STRING_EQUAL(file, "BIGFILEN.E"); free(file); file = iso_1_fileid("file.bigext"); CU_ASSERT_STRING_EQUAL(file, "FILE.BIG"); free(file); file = iso_1_fileid(".bigext"); CU_ASSERT_STRING_EQUAL(file, ".BIG"); free(file); file = iso_1_fileid("bigfilename.bigext"); CU_ASSERT_STRING_EQUAL(file, "BIGFILEN.BIG"); free(file); file = iso_1_fileid("file<:a.ext"); CU_ASSERT_STRING_EQUAL(file, "FILE__A.EXT"); free(file); file = iso_1_fileid("file.<:a"); CU_ASSERT_STRING_EQUAL(file, "FILE.__A"); free(file); file = iso_1_fileid("file<:a.--a"); CU_ASSERT_STRING_EQUAL(file, "FILE__A.__A"); free(file); file = iso_1_fileid("file.ex1.ex2"); CU_ASSERT_STRING_EQUAL(file, "FILE_EX1.EX2"); free(file); file = iso_1_fileid("file.ex1.ex2.ex3"); CU_ASSERT_STRING_EQUAL(file, "FILE_EX1.EX3"); free(file); file = iso_1_fileid("fil.ex1.ex2.ex3"); CU_ASSERT_STRING_EQUAL(file, "FIL_EX1_.EX3"); free(file); } static void test_iso_2_fileid() { char *file; file = iso_2_fileid("file1"); CU_ASSERT_STRING_EQUAL(file, "FILE1."); free(file); file = iso_2_fileid("fILe1"); CU_ASSERT_STRING_EQUAL(file, "FILE1."); free(file); file = iso_2_fileid("FILE1"); CU_ASSERT_STRING_EQUAL(file, "FILE1."); free(file); file = iso_2_fileid(".EXT"); CU_ASSERT_STRING_EQUAL(file, ".EXT"); free(file); file = iso_2_fileid("file.ext"); CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); free(file); file = iso_2_fileid("fiLE.ext"); CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); free(file); file = iso_2_fileid("file.EXt"); CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); free(file); file = iso_2_fileid("FILE.EXT"); CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); free(file); file = iso_2_fileid("bigfilename"); CU_ASSERT_STRING_EQUAL(file, "BIGFILENAME."); free(file); file = iso_2_fileid("bigfilename.ext"); CU_ASSERT_STRING_EQUAL(file, "BIGFILENAME.EXT"); free(file); file = iso_2_fileid("bigfilename.e"); CU_ASSERT_STRING_EQUAL(file, "BIGFILENAME.E"); free(file); file = iso_2_fileid("31 characters filename.extensio"); CU_ASSERT_STRING_EQUAL(file, "31_CHARACTERS_FILENAME.EXTENSIO"); free(file); file = iso_2_fileid("32 characters filename.extension"); CU_ASSERT_STRING_EQUAL(file, "32_CHARACTERS_FILENAME.EXTENSIO"); free(file); file = iso_2_fileid("more than 30 characters filename.extension"); CU_ASSERT_STRING_EQUAL(file, "MORE_THAN_30_CHARACTERS_FIL.EXT"); free(file); file = iso_2_fileid("file.bigext"); CU_ASSERT_STRING_EQUAL(file, "FILE.BIGEXT"); free(file); file = iso_2_fileid(".bigext"); CU_ASSERT_STRING_EQUAL(file, ".BIGEXT"); free(file); file = iso_2_fileid("bigfilename.bigext"); CU_ASSERT_STRING_EQUAL(file, "BIGFILENAME.BIGEXT"); free(file); file = iso_2_fileid("file<:a.ext"); CU_ASSERT_STRING_EQUAL(file, "FILE__A.EXT"); free(file); file = iso_2_fileid("file.<:a"); CU_ASSERT_STRING_EQUAL(file, "FILE.__A"); free(file); file = iso_2_fileid("file<:a.--a"); CU_ASSERT_STRING_EQUAL(file, "FILE__A.__A"); free(file); file = iso_2_fileid("file.ex1.ex2"); CU_ASSERT_STRING_EQUAL(file, "FILE_EX1.EX2"); free(file); file = iso_2_fileid("file.ex1.ex2.ex3"); CU_ASSERT_STRING_EQUAL(file, "FILE_EX1_EX2.EX3"); free(file); file = iso_2_fileid("fil.ex1.ex2.ex3"); CU_ASSERT_STRING_EQUAL(file, "FIL_EX1_EX2.EX3"); free(file); file = iso_2_fileid(".file.bigext"); CU_ASSERT_STRING_EQUAL(file, "_FILE.BIGEXT"); free(file); } static void test_iso_r_dirid() { char *dir; dir = iso_r_dirid("dir1", 31, 0); CU_ASSERT_STRING_EQUAL(dir, "DIR1"); free(dir); dir = iso_r_dirid("dIR1", 31, 0); CU_ASSERT_STRING_EQUAL(dir, "DIR1"); free(dir); /* allow lowercase */ dir = iso_r_dirid("dIR1", 31, 1); CU_ASSERT_STRING_EQUAL(dir, "dIR1"); free(dir); dir = iso_r_dirid("dIR1", 31, 2); CU_ASSERT_STRING_EQUAL(dir, "dIR1"); free(dir); dir = iso_r_dirid("DIR1", 31, 0); CU_ASSERT_STRING_EQUAL(dir, "DIR1"); free(dir); dir = iso_r_dirid("dirwithbigname", 31, 0); CU_ASSERT_STRING_EQUAL(dir, "DIRWITHBIGNAME"); free(dir); dir = iso_r_dirid("dirwith8", 31, 0); CU_ASSERT_STRING_EQUAL(dir, "DIRWITH8"); free(dir); /* dot is not allowed */ dir = iso_r_dirid("dir.1", 31, 0); CU_ASSERT_STRING_EQUAL(dir, "DIR_1"); free(dir); dir = iso_r_dirid("dir.1", 31, 1); CU_ASSERT_STRING_EQUAL(dir, "dir_1"); free(dir); dir = iso_r_dirid("dir.1", 31, 2); CU_ASSERT_STRING_EQUAL(dir, "dir.1"); free(dir); dir = iso_r_dirid("4f<0KmM::xcvf", 31, 0); CU_ASSERT_STRING_EQUAL(dir, "4F_0KMM__XCVF"); free(dir); dir = iso_r_dirid("4f<0KmM::xcvf", 31, 1); CU_ASSERT_STRING_EQUAL(dir, "4f_0KmM__xcvf"); free(dir); dir = iso_r_dirid("4f<0KmM::xcvf", 31, 2); CU_ASSERT_STRING_EQUAL(dir, "4f<0KmM::xcvf"); free(dir); dir = iso_r_dirid("directory with 31 characters ok", 31, 0); CU_ASSERT_STRING_EQUAL(dir, "DIRECTORY_WITH_31_CHARACTERS_OK"); free(dir); dir = iso_r_dirid("directory with more than 31 characters", 31, 0); CU_ASSERT_STRING_EQUAL(dir, "DIRECTORY_WITH_MORE_THAN_31_CHA"); free(dir); dir = iso_r_dirid("directory with more than 31 characters", 35, 0); CU_ASSERT_STRING_EQUAL(dir, "DIRECTORY_WITH_MORE_THAN_31_CHARACT"); free(dir); } static void test_iso_r_fileid() { char *file; /* force dot */ file = iso_r_fileid("file1", 30, 0, 1); CU_ASSERT_STRING_EQUAL(file, "FILE1."); free(file); /* and not */ file = iso_r_fileid("file1", 30, 0, 0); CU_ASSERT_STRING_EQUAL(file, "FILE1"); free(file); /* allow lowercase */ file = iso_r_fileid("file1", 30, 1, 0); CU_ASSERT_STRING_EQUAL(file, "file1"); free(file); file = iso_r_fileid("file1", 30, 2, 0); CU_ASSERT_STRING_EQUAL(file, "file1"); free(file); /* force d-char and dot */ file = iso_r_fileid("fILe1", 30, 0, 1); CU_ASSERT_STRING_EQUAL(file, "FILE1."); free(file); /* force d-char but not dot */ file = iso_r_fileid("fILe1", 30, 0, 0); CU_ASSERT_STRING_EQUAL(file, "FILE1"); free(file); /* allow lower case but force dot */ file = iso_r_fileid("fILe1", 30, 1, 1); CU_ASSERT_STRING_EQUAL(file, "fILe1."); free(file); file = iso_r_fileid("FILE1", 30, 0, 1); CU_ASSERT_STRING_EQUAL(file, "FILE1."); free(file); file = iso_r_fileid(".EXT", 30, 0, 1); CU_ASSERT_STRING_EQUAL(file, ".EXT"); free(file); file = iso_r_fileid(".EXT", 30, 1, 0); CU_ASSERT_STRING_EQUAL(file, ".EXT"); free(file); file = iso_r_fileid("file.ext", 30, 0, 1); CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); free(file); /* not force dot is the same in this case */ file = iso_r_fileid("fiLE.ext", 30, 0, 0); CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); free(file); file = iso_r_fileid("fiLE.ext", 30, 2, 0); CU_ASSERT_STRING_EQUAL(file, "fiLE.ext"); free(file); file = iso_r_fileid("file.EXt", 30, 0, 1); CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); free(file); file = iso_r_fileid("FILE.EXT", 30, 0, 1); CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); free(file); file = iso_r_fileid("31 characters filename.extensio", 30, 0, 1); CU_ASSERT_STRING_EQUAL(file, "31_CHARACTERS_FILENAME.EXTENSIO"); free(file); file = iso_r_fileid("32 characters filename.extension", 30, 0, 1); CU_ASSERT_STRING_EQUAL(file, "32_CHARACTERS_FILENAME.EXTENSIO"); free(file); /* allow lowercase */ file = iso_r_fileid("31 characters filename.extensio", 30, 1, 1); CU_ASSERT_STRING_EQUAL(file, "31_characters_filename.extensio"); free(file); /* and all characters */ file = iso_r_fileid("31 characters filename.extensio", 30, 2, 1); CU_ASSERT_STRING_EQUAL(file, "31 characters filename.extensio"); free(file); file = iso_r_fileid("more than 30 characters filename.extension", 30, 0, 0); CU_ASSERT_STRING_EQUAL(file, "MORE_THAN_30_CHARACTERS_FIL.EXT"); free(file); /* incrementing the size... */ file = iso_r_fileid("more than 30 characters filename.extension", 35, 0, 0); CU_ASSERT_STRING_EQUAL(file, "MORE_THAN_30_CHARACTERS_FILENAME.EXT"); free(file); file = iso_r_fileid("more than 30 characters filename.extension", 36, 0, 0); CU_ASSERT_STRING_EQUAL(file, "MORE_THAN_30_CHARACTERS_FILENAME.EXTE"); free(file); file = iso_r_fileid("file.bigext", 30, 1, 0); CU_ASSERT_STRING_EQUAL(file, "file.bigext"); free(file); file = iso_r_fileid(".bigext", 30, 0, 0); CU_ASSERT_STRING_EQUAL(file, ".BIGEXT"); free(file); /* "strange" characters */ file = iso_r_fileid("file<:a.ext", 30, 0, 0); CU_ASSERT_STRING_EQUAL(file, "FILE__A.EXT"); free(file); file = iso_r_fileid("file<:a.ext", 30, 1, 0); CU_ASSERT_STRING_EQUAL(file, "file__a.ext"); free(file); file = iso_r_fileid("file<:a.ext", 30, 2, 0); CU_ASSERT_STRING_EQUAL(file, "file<:a.ext"); free(file); /* multiple dots */ file = iso_r_fileid("fi.le.a.ext", 30, 0, 0); CU_ASSERT_STRING_EQUAL(file, "FI_LE_A.EXT"); free(file); file = iso_r_fileid("fi.le.a.ext", 30, 1, 0); CU_ASSERT_STRING_EQUAL(file, "fi_le_a.ext"); free(file); file = iso_r_fileid("fi.le.a.ext", 30, 2, 0); CU_ASSERT_STRING_EQUAL(file, "fi.le.a.ext"); free(file); file = iso_r_fileid("file.<:a", 30, 0, 0); CU_ASSERT_STRING_EQUAL(file, "FILE.__A"); free(file); file = iso_r_fileid("file<:a.--a", 30, 0, 0); CU_ASSERT_STRING_EQUAL(file, "FILE__A.__A"); free(file); file = iso_r_fileid(".file.bigext", 30, 0, 0); CU_ASSERT_STRING_EQUAL(file, "_FILE.BIGEXT"); free(file); file = iso_r_fileid(".file.bigext", 30, 2, 0); CU_ASSERT_STRING_EQUAL(file, ".file.bigext"); free(file); } static void test_iso_rbtree_insert() { int res; IsoRBTree *tree; char *str1, *str2, *str3, *str4, *str5; void *str; res = iso_rbtree_new((compare_function_t)strcmp, &tree); CU_ASSERT_EQUAL(res, 1); /* ok, insert one str */ str1 = "first str"; res = iso_rbtree_insert(tree, str1, &str); CU_ASSERT_EQUAL(res, 1); CU_ASSERT_PTR_EQUAL(str, str1); str2 = "second str"; res = iso_rbtree_insert(tree, str2, &str); CU_ASSERT_EQUAL(res, 1); CU_ASSERT_PTR_EQUAL(str, str2); /* an already inserted string */ str3 = "second str"; res = iso_rbtree_insert(tree, str3, &str); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_PTR_EQUAL(str, str2); /* an already inserted string */ str3 = "first str"; res = iso_rbtree_insert(tree, str3, &str); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_PTR_EQUAL(str, str1); str4 = "a string to be inserted first"; res = iso_rbtree_insert(tree, str4, &str); CU_ASSERT_EQUAL(res, 1); CU_ASSERT_PTR_EQUAL(str, str4); str5 = "this to be inserted last"; res = iso_rbtree_insert(tree, str5, &str); CU_ASSERT_EQUAL(res, 1); CU_ASSERT_PTR_EQUAL(str, str5); /* * TODO write a really good test to check all possible estrange * behaviors of a red-black tree */ iso_rbtree_destroy(tree, NULL); } void test_iso_htable_put_get() { int res; IsoHTable *table; char *str1, *str2, *str3, *str4, *str5; void *str; res = iso_htable_create(4, iso_str_hash, (compare_function_t)strcmp, &table); CU_ASSERT_EQUAL(res, 1); /* try to get str from empty table */ res = iso_htable_get(table, "first str", &str); CU_ASSERT_EQUAL(res, 0); /* ok, insert one str */ str1 = "first str"; res = iso_htable_put(table, str1, str1); CU_ASSERT_EQUAL(res, 1); /* and now get str from table */ res = iso_htable_get(table, "first str", &str); CU_ASSERT_EQUAL(res, 1); CU_ASSERT_PTR_EQUAL(str, str1); res = iso_htable_get(table, "second str", &str); CU_ASSERT_EQUAL(res, 0); str2 = "second str"; res = iso_htable_put(table, str2, str2); CU_ASSERT_EQUAL(res, 1); str = NULL; res = iso_htable_get(table, "first str", &str); CU_ASSERT_EQUAL(res, 1); CU_ASSERT_PTR_EQUAL(str, str1); res = iso_htable_get(table, "second str", &str); CU_ASSERT_EQUAL(res, 1); CU_ASSERT_PTR_EQUAL(str, str2); /* insert again, with same key but other data */ res = iso_htable_put(table, str2, str1); CU_ASSERT_EQUAL(res, 0); res = iso_htable_get(table, "second str", &str); CU_ASSERT_EQUAL(res, 1); CU_ASSERT_PTR_EQUAL(str, str2); str3 = "third str"; res = iso_htable_put(table, str3, str3); CU_ASSERT_EQUAL(res, 1); str4 = "four str"; res = iso_htable_put(table, str4, str4); CU_ASSERT_EQUAL(res, 1); str5 = "fifth str"; res = iso_htable_put(table, str5, str5); CU_ASSERT_EQUAL(res, 1); /* some searches */ res = iso_htable_get(table, "sixth str", &str); CU_ASSERT_EQUAL(res, 0); res = iso_htable_get(table, "fifth str", &str); CU_ASSERT_EQUAL(res, 1); CU_ASSERT_PTR_EQUAL(str, str5); iso_htable_destroy(table, NULL); } void add_util_suite() { CU_pSuite pSuite = CU_add_suite("UtilSuite", NULL, NULL); CU_add_test(pSuite, "strconv()", test_strconv); CU_add_test(pSuite, "int_pow()", test_int_pow); CU_add_test(pSuite, "DIV_UP()", test_div_up); CU_add_test(pSuite, "ROUND_UP()", test_round_up); CU_add_test(pSuite, "iso_bb()", test_iso_bb); CU_add_test(pSuite, "iso_lsb/msb()", test_iso_lsb_msb); CU_add_test(pSuite, "iso_read_lsb/msb()", test_iso_read_lsb_msb); CU_add_test(pSuite, "iso_datetime_7()", test_iso_datetime_7); CU_add_test(pSuite, "iso_1_dirid()", test_iso_1_dirid); CU_add_test(pSuite, "iso_2_dirid()", test_iso_2_dirid); CU_add_test(pSuite, "iso_1_fileid()", test_iso_1_fileid); CU_add_test(pSuite, "iso_2_fileid()", test_iso_2_fileid); CU_add_test(pSuite, "iso_r_dirid()", test_iso_r_dirid); CU_add_test(pSuite, "iso_r_fileid()", test_iso_r_fileid); CU_add_test(pSuite, "iso_rbtree_insert()", test_iso_rbtree_insert); CU_add_test(pSuite, "iso_htable_put/get()", test_iso_htable_put_get); } #define LIBISOFS_MAJOR_VERSION @LIBISOFS_MAJOR_VERSION@ #define LIBISOFS_MINOR_VERSION @LIBISOFS_MINOR_VERSION@ #define LIBISOFS_MICRO_VERSION @LIBISOFS_MICRO_VERSION@ Mario Danic Joe Neeman Philippe Rouquier Suriyan Laohaprapanon Vreixo Formoso Lopes 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 Derek Foreman and Ben Jansens Copyright (C) 2002-2006 Derek Foreman and Ben Jansens Mario Danic , Thomas Schmitt Copyright (C) 2006 Mario Danic, Thomas Schmitt This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Development =========== - Support for reading of plain iso images. - Support for reading RR extensions Version 0.2.8 ============= TODOInstallation Instructions ************************* Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== Briefly, the shell commands `./configure; make; make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf bug. Until the bug is fixed you can use this workaround: CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. pkgconfigdir=$(libdir)/pkgconfig libincludedir=$(includedir)/libisofs lib_LTLIBRARIES = libisofs/libisofs.la ## ========================================================================= ## # Build libraries libisofs_libisofs_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) libisofs_libisofs_la_SOURCES = \ libisofs/tree.h \ libisofs/tree.c \ libisofs/volume.h \ libisofs/volume.c \ libisofs/util.h \ libisofs/util.c \ libisofs/ecma119.c \ libisofs/ecma119.h \ libisofs/ecma119_tree.c \ libisofs/ecma119_tree.h \ libisofs/susp.h \ libisofs/susp.c \ libisofs/rockridge.h \ libisofs/rockridge.c \ libisofs/joliet.c \ libisofs/joliet.h \ libisofs/exclude.c \ libisofs/exclude.h \ libisofs/hash.h \ libisofs/hash.c \ libisofs/file.h \ libisofs/file.c \ libisofs/file_src.h \ libisofs/file_src.c \ libisofs/eltorito.h \ libisofs/eltorito.c \ libisofs/data_source.c \ libisofs/ecma119_read.h \ libisofs/ecma119_read.c \ libisofs/ecma119_read_rr.h \ libisofs/ecma119_read_rr.c \ libisofs/libiso_msgs.h \ libisofs/libiso_msgs.c \ libisofs/messages.h \ libisofs/messages.c libinclude_HEADERS = \ libisofs/libisofs.h ## ========================================================================= ## ## Build test applications noinst_PROGRAMS = \ test/iso \ test/isoread \ test/isoms \ test/isoadd \ test/isogrow test_iso_CPPFLAGS = -Ilibisofs test_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) test_iso_SOURCES = test/iso.c test_isoread_CPPFLAGS = -Ilibisofs test_isoread_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) test_isoread_SOURCES = test/iso_read.c test_isoms_CPPFLAGS = -Ilibisofs test_isoms_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) test_isoms_SOURCES = test/iso_ms.c test_isoadd_CPPFLAGS = -Ilibisofs test_isoadd_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) test_isoadd_SOURCES = test/iso_add.c test_isogrow_CPPFLAGS = -Ilibisofs -Ilibburn test_isogrow_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) -lburn test_isogrow_SOURCES = test/iso_grow.c ## Build unit test check_PROGRAMS = \ test/test test_test_CPPFLAGS = -Ilibisofs test_test_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) -lcunit test_test_LDFLAGS = -L.. -lm test_test_SOURCES = \ test/test_exclude.c \ test/test_tree.c \ test/test_ecma119_tree.c \ test/test_file_hashtable.c \ test/test_util.c \ test/test_volume.c \ test/test_data_source.c \ test/test_read.c \ test/test.c ## ========================================================================= ## ## Build documentation (You need Doxygen for this to work) webhost = http://libburn-api.pykix.org webpath = / docdir = $(DESTDIR)$(prefix)/share/doc/$(PACKAGE)-$(VERSION) doc: doc/html doc/html: doc/doxygen.conf if [ -f ./doc/doc.lock ]; then \ $(RM) -r doc/html; \ doxygen doc/doxygen.conf; \ fi doc-upload: doc/html scp -r $ and Thomas Schmitt Copyright (C) 2006-2007 Mario Danic, Thomas Schmitt Still containing parts of Libburn. By Derek Foreman and Ben Jansens Copyright (C) 2002-2006 Derek Foreman and Ben Jansens These parts are to be replaced by own code of above libburnia-project.org copyright holders and then libburnia-project.org is to be their sole copyright. This is done to achieve the right to issue the clarification and the commitment as written at the end of this text. The rights and merits of the Libburn-copyright holders Derek Foreman and Ben Jansens will be duely respected. This libburnia-project.org toplevel README (C) 2006-2007 Thomas Schmitt ------------------------------------------------------------------------------ Build and Installation Our build system is based on autotools. For preparing the build of a SVN snapshot you will need autotools of at least version 1.7. Check out from SVN by svn co http://svn.libburnia-project.org/libburn/trunk libburn go into directory libburn and apply autotools by ./bootstrap Alternatively you may unpack a release tarball for which you do not need autotools installed. To build a libburnia-project.org subproject it should be sufficient to go into its toplevel directory (here: "libburn") and execute ./configure --prefix=/usr make To make the libraries accessible for running resp. developing applications make install The other half of the project, libisofs, is hosted in the libburnia SVN, too: svn co http://svn.libburnia-project.org/libisofs/trunk libisofs See README file there. ------------------------------------------------------------------------------ Overview of libburnia-project.org libburnia-project.org is an open-source software project for reading, mastering and writing optical discs. For now this means only CD media and all single layer DVD media except DVD+R. The project comprises of several more or less interdependent parts which together strive to be a usable foundation for application development. These are libraries, language bindings, and middleware binaries which emulate classical (and valuable) Linux tools. Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems we would need : login on a development machine resp. a live OS on CD or DVD, advise from a system person about the equivalent of Linux sg or FreeBSD CAM, volunteers for testing of realistic use cases. We have a workable code base for burning CD and most single layer DVD. The burn API is quite comprehensively documented and can be used to build a presentable application. We have a functional binary which emulates parts of cdrecord in order to prove that usability, and in order to allow you to explore libburnia's scope by help of existing cdrecord frontends. The project components (list subject to growth, hopefully): - libburn is the library by which preformatted data get onto optical media. It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or /dev/hdX (e.g. on kernel 2.6). libburn is the foundation of our cdrecord emulation. Its code is independent of cdrecord. Its DVD capabilities are learned from studying the code of dvd+rw-tools and MMC-5 specs. No code but only the pure SCSI knowledge has been taken from dvd+rw-tools, though. - libisofs is the library to pack up hard disk files and directories into a ISO 9660 disk image. This may then be brought to media via libburn. libisofs is to be the foundation of our upcoming mkisofs emulation. - cdrskin is a limited cdrecord compatibility wrapper for libburn. Cdrecord is a powerful GPL'ed burn program included in Joerg Schilling's cdrtools. cdrskin strives to be a second source for the services traditionally provided by cdrecord. Additionally it provides libburn's DVD capabilities, where only -sao is compatible with cdrecord. cdrskin does not contain any bytes copied from cdrecord's sources. Many bytes have been copied from the message output of cdrecord runs, though. See cdrskin/README and man cdrskin/cdrskin.1 for more. - test is a collection of application gestures and examples given by the authors of the library features. The main API example for libburn is test/libburner.c . Explore these examples if you look for inspiration. We plan to be a responsive upstream. Bear with us. We are still practicing. ------------------------------------------------------------------------------ Project history as far as known to me: - Founded in 2002 as it seems. See mailing list archives http://lists.freedesktop.org/archives/libburn/ The site of this founder team is reachable and offers download of a (somewhat outdated) tarball and from CVS : http://icculus.org/burn/ Copyright holders and most probably founders: Derek Foreman and Ben Jansens. - I came to using libburn in 2005. Founded the cdrskin project and submitted necessary patches which were accepted or implemented better. Except one remaining patch which prevented cdrskin from using vanilla libburn from CVS. The cdrskin project site is reachable and offers download of the heavily patched (elsewise outdated) tarball under the name cdrskin-0.1.2 : http://scdbackup.sourceforge.net/cdrskin_eng.html It has meanwhile moved to use vanilla libburn.pykix.org , though. Version 0.1.4 constitutes the first release of this kind. - In July 2006 our team mate Mario Danic announced a revival of libburn which by about nearly everybody else was perceived as unfriendly fork. Derek Foreman four days later posted a message which expressed his discontent. The situation first caused me to publically regret it and then - after i got the opportunity to move in with cdrskin - gave me true reason to personally apologize to Derek Foreman, Ben Jansens and the contibutors at icculus.org/burn. Posted to both projects: http://lists.freedesktop.org/archives/libburn/2006-August/000446.html http://mailman-mail1.webfaction.com/pipermail/libburn-hackers/2006-August/000024.html - Mid August 2006 project cdrskin established a branch office in libburn.pykix.org so that all maintainers of our tools have one single place to get the current (at least slightely) usable coordinated versions of everything. Project cdrskin will live forth independendly for a while but it is committed to stay in sync with libburn.pykix.org (or some successor, if ever). cdrskin is also committed to support icculus.org/burn if the pending fork is made reality by content changes in that project. It will cease to maintain a patched version of icculus.org/burn though. Precondition for a new release of cdrskin on base of icculus.org/burn would be the pending "whitelist patch" therefore. I would rather prefer if both projects find consense and merge, or at least cooperate. I have not given up hope totally, yet. I, personally, will honor any approach. - 2nd September 2006 the decision is made to strive for a consolidation of copyright and a commitment to GPL in a reasonable and open minded way. This is to avoid long term problems with code of unknown origin and with finding consense among the not so clearly defined group of copyright claimers and -holders. libisofs is already claimed sole copyright Mario Danic. cdrskin and libburner are already claimed sole copyright Thomas Schmitt. Rewrites of other components will follow and concluded by claiming full copyright within the group of libburn.pykix.org-copyright holders. - 16th September 2006 feature freeze for release of libburn-0.2.2 . - 20th September 2006 release of libburn-0.2.2 . - 26th October 2006 feature freeze for cdrskin-0.2.4 based on libburn-0.2.3 . This version of cdrskin is much more cdrecord compatible in repect to drive addressing and audio features. - 30th October 2006 release of cdrskin-0.2.4 . - 13th November 2006 splitting releases of libburn+cdrskin from libisofs. - 24th November 2006 release of libburn-0.2.6 and cdrskin-0.2.6 . cdrskin has become suitable for unaware frontends as long as they perform only the core of cdrecord use cases (including open-ended input streams, audio, and multi-session). - 28th November 2006 the umbrella project which encloses both, libisofs and libburn, is now called libburnia. For the origin of this name, see http://en.wikipedia.org/wiki/Liburnians . - 16th January 2007 release of libburn-0.3.0 and cdrskin-0.3.0 . Now the scope is widened to a first class of DVD media: overwriteable single layer types DVD-RAM, DVD+RW, DVD-RW. This is not a cdrecord emulation but rather inspired by dvd+rw-tools' "poor man" writing facility for this class of media. Taking a bow towards Andy Polyakov. - 11th February 2007 version 0.3.2 covers sequential DVD-RW and DVD-R with multi-session and with DAO. - 12th March 2007 version 0.3.4 supports DVD+R and thus covers all single layer DVD media. Code for double layer DVD+/-R is implemented but awaits a tester yet. - 23th April 2007 version 0.3.6 follows the unanimous opinion of Linux kernel people that one should not use /dev/sg on kernel 2.6. - 31st July 2007 version 0.3.8 marks the first anniversary of libburn revival. We look back on improved stability, a substantially extended list of media and write modes, and better protection against typical user mishaps. ------------------------------------------------------------------------------ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. To be exact: version 2 of that License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------------ Clarification in my name and in the name of Mario Danic, upcoming copyright holders on toplevel of libburnia. To be fully in effect after the remaining other copyrighted code has been replaced by ours and by copyright-free contributions of our friends: ------------------------------------------------------------------------------ We, the copyright holders, agree on the interpretation that dynamical linking of our libraries constitutes "use of" and not "derivation from" our work in the sense of GPL, provided those libraries are compiled from our unaltered code. Thus you may link our libraries dynamically with applications which are not under GPL. You may distribute our libraries and application tools in binary form, if you fulfill the usual condition of GPL to offer a copy of the source code -altered or unaltered- under GPL. We ask you politely to use our work in open source spirit and with the due reference to the entire open source community. If there should really arise the case where above clarification does not suffice to fulfill a clear and neat request in open source spirit that would otherwise be declined for mere formal reasons, only in that case we will duely consider to issue a special license covering only that special case. It is the open source idea of responsible freedom which will be decisive and you will have to prove that you exhausted all own means to qualify for GPL. For now we are firmly committed to maintain one single license: GPL. signed: Mario Danic, Thomas Schmitt GENERAL ======= Improve documentation Build system improvements Clarify licencing (GPL2 only (!) with exception) FEATURES ======== El-Torito Support for multiple images HFS/HFS+ CD reading [ok] plain iso [ok] Rock Ridge [ok] Joliet Merge RR and Joliet trees [ok] User options to customize reading [ok] Read El-Torito info [ok] Multisession [ok] DVD+RW image growing El-Torito images from previous session Add new el-torito image from prev. session file Path for isolinux from prev. session UDF [ok] ISO relaxed contraints ISO 9660:1998 Support for special files (only dirs, reg. files and symlinks are supported). Modification of timestamps attribs on nodes TESTS ===== [several done] Test all util.h functions, especially date-related ones Test for RR read functions For all IMPLEMENTATION ============== a way to return NULL sources meaning a failure!! [ok] Error message queue Better charset support [ok] default input charset to locale one, no always UTF-8 add charset management on image reading use iso-8859-1 instead of UTF-8 on RR? [ok] Improve date handling for DVD+RW, the VD to be written at the beginning of disc must be returned as 32KB block Sources to write file contents encyrption/compression of files auto cut of files to 2gb limit BUGS ==== Joliet names need ";1" at the end RR Continuation Areas can't be in Directory Record block [ok] Fix mangle names when iso relaxed constraints AC_DEFUN([TARGET_SHIZZLE], [ ARCH="" AC_MSG_CHECKING([target operating system]) case $target in *-*-linux*) ARCH=linux LIBBURN_ARCH_LIBS= ;; *-*-freebsd*) ARCH=freebsd LIBBURN_ARCH_LIBS=-lcam ;; *) AC_ERROR([You are attempting to compile for an unsupported platform]) ;; esac AC_MSG_RESULT([$ARCH]) ]) #!/bin/sh -x aclocal libtoolize --copy --force autoconf # ts A61101 : libburn is not prepared for config.h # autoheader automake --foreign --add-missing --copy --include-deps AC_INIT([libisofs], [0.2.9], [http://libburnia-project.org]) AC_PREREQ([2.50]) dnl AC_CONFIG_HEADER([config.h]) AC_CANONICAL_HOST AC_CANONICAL_TARGET AM_INIT_AUTOMAKE([subdir-objects]) dnl A61101 This breaks Linux build (makes 32 bit off_t) dnl http://sourceware.org/autobook/autobook/autobook_96.html says dnl one must include some config.h and this was a pitfall. dnl So why dig the pit at all ? dnl AM_CONFIG_HEADER(config.h) dnl Making releases: dnl BURN_MICRO_VERSION += 1; dnl BURN_INTERFACE_AGE += 1; dnl BURN_BINARY_AGE += 1; dnl if any functions have been added, set BURN_INTERFACE_AGE to 0. dnl if backwards compatibility has been broken, dnl set BURN_BINARY_AGE and BURN_INTERFACE_AGE to 0. dnl dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match dnl BURN_MAJOR_VERSION=0 BURN_MINOR_VERSION=2 BURN_MICRO_VERSION=4 BURN_INTERFACE_AGE=0 BURN_BINARY_AGE=0 BURN_VERSION=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION.$BURN_MICRO_VERSION AC_SUBST(BURN_MAJOR_VERSION) AC_SUBST(BURN_MINOR_VERSION) AC_SUBST(BURN_MICRO_VERSION) AC_SUBST(BURN_INTERFACE_AGE) AC_SUBST(BURN_BINARY_AGE) AC_SUBST(BURN_VERSION) dnl Libtool versioning LT_RELEASE=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION LT_CURRENT=`expr $BURN_MICRO_VERSION - $BURN_INTERFACE_AGE` LT_REVISION=$BURN_INTERFACE_AGE LT_AGE=`expr $BURN_BINARY_AGE - $BURN_INTERFACE_AGE` LT_CURRENT_MINUS_AGE=`expr $LT_CURRENT - $LT_AGE` AC_SUBST(LT_RELEASE) AC_SUBST(LT_CURRENT) AC_SUBST(LT_REVISION) AC_SUBST(LT_AGE) AC_SUBST(LT_CURRENT_MINUS_AGE) AC_PREFIX_DEFAULT([/usr/local]) test "$prefix" = "NONE" && prefix=$ac_default_prefix AM_MAINTAINER_MODE AM_PROG_CC_C_O AC_C_CONST AC_C_INLINE AC_C_BIGENDIAN dnl Large file support AC_SYS_LARGEFILE AC_FUNC_FSEEKO AC_CHECK_FUNC([fseeko]) if test ! $ac_cv_func_fseeko; then AC_ERROR([Libisofs requires largefile support.]) fi AC_PROG_LIBTOOL AC_SUBST(LIBTOOL_DEPS) LIBTOOL="$LIBTOOL --silent" AC_PROG_INSTALL AC_CHECK_HEADERS() AC_CHECK_MEMBER([struct tm.tm_gmtoff], [AC_DEFINE(HAVE_TM_GMTOFF, 1, [Define this if tm structure includes a tm_gmtoff entry.])], , [#include ]) THREAD_LIBS=-lpthread AC_SUBST(THREAD_LIBS) TARGET_SHIZZLE AC_SUBST(ARCH) AC_SUBST(LIBBURN_ARCH_LIBS) dnl Add compiler-specific flags dnl See if the user wants aggressive optimizations of the code AC_ARG_ENABLE(debug, [ --enable-debug Disable aggressive optimizations [default=yes]], , enable_debug=yes) if test x$enable_debug != xyes; then if test x$GCC = xyes; then CFLAGS="$CFLAGS -O3" CFLAGS="$CFLAGS -fexpensive-optimizations" fi CFLAGS="$CFLAGS -DNDEBUG" else if test x$GCC = xyes; then CFLAGS="$CFLAGS -g -pedantic -Wall" fi CFLAGS="$CFLAGS -DDEBUG" fi AC_CONFIG_FILES([ Makefile doc/doxygen.conf version.h libisofs-5.pc ]) AC_OUTPUT all clean: $(MAKE) -C .. -$(MAKEFLAGS) $@ .PHONY: all clean /** @author Mario Danic, Thomas Schmitt @mainpage Libburn Documentation Index @section intro Introduction Libburn is an open-source library for reading, mastering and writing optical discs. For now this means only CD-R and CD-RW. The project comprises of several more or less interdependent parts which together strive to be a usable foundation for application development. These are libraries, language bindings, and middleware binaries which emulate classical (and valuable) Linux tools. Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems we would need : login on a development machine resp. a live OS on CD or DVD, advise from a system person about the equivalent of Linux sg or FreeBSD CAM, volunteers for testing of realistic use cases. We do have a workable code base for burning data CDs, though. The burn API is quite comprehensively documented and can be used to build a presentable application. We do have a functional binary which emulates parts of cdrecord in order to prove that usability, and in order to allow you to explore libburn's scope by help of existing cdrecord frontends. @subsection components The project components (list subject to growth, hopefully): - libburn is the library by which preformatted data get onto optical media. It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or /dev/hdX (e.g. on kernel 2.6). libburn is the foundation of our cdrecord emulation. - libisofs is the library to pack up hard disk files and directories into a ISO 9660 disk image. This may then be brought to CD via libburn. libisofs is to be the foundation of our upcoming mkisofs emulation. - cdrskin is a limited cdrecord compatibility wrapper for libburn. cdrecord is a powerful GPL'ed burn program included in Joerg Schilling's cdrtools. cdrskin strives to be a second source for the services traditionally provided by cdrecord. cdrskin does not contain any bytes copied from cdrecord's sources. Many bytes have been copied from the message output of cdrecord runs, though. See cdrskin/README for more. - "test" is a collection of application gestures and examples given by the authors of the library features. The main API example of libburn is named test/libburner.c . Explore these examples if you look for inspiration. We plan to be a responsive upstream. Bear with us. @section using Using the libraries Our build system is based on autotools. User experience tells us that you will need at least autotools version 1.7. To build libburn and its subprojects it should be sufficient to go into its toplevel directory and execute - ./bootstrap (needed if you downloaded from SVN) - ./configure - make To make the libraries accessible for running resp. developing applications - make install Both libraries are written in C language and get built by autotools. Thus we expect them to be useable by a wide range of Linux-implemented languages and development tools. @section libburner Libburner libburner is a minimal demo application for the library libburn (see: libburn/libburn.h) as provided on http://libburn.pykix.org . It can list the available devices, can blank a CD-RW and can burn to CD-R or CD-RW. It's main purpose, nevertheless, is to show you how to use libburn and also to serve the libburn team as reference application. libburner does indeed define the standard way how above three gestures can be implemented and stay upward compatible for a good while. @subsection libburner-help Libburner --help
Usage: test/libburner
       [--drive 
||"-"] [--blank_fast|--blank_full] [--audio] [--try_to_simulate] [--stdin_size ] [|"-"] Examples A bus scan (needs rw-permissions to see a drive): test/libburner --drive - Burn a file to drive chosen by number: test/libburner --drive 0 my_image_file Burn a file to drive chosen by persistent address: test/libburner --drive /dev/hdc my_image_file Blank a used CD-RW (is combinable with burning in one run): test/libburner --drive /dev/hdc --blank_fast Burn two audio tracks lame --decode -t /path/to/track1.mp3 track1.cd test/dewav /path/to/track2.wav -o track2.cd test/libburner --drive /dev/hdc --audio track1.cd track2.cd Burn a compressed afio archive on-the-fly, pad up to 700 MB: ( cd my_directory ; find . -print | afio -oZ - ) | \ test/libburner --drive /dev/hdc --stdin_size 734003200 - To be read from *not mounted* CD via: afio -tvZ /dev/hdc Program tar would need a clean EOF which our padded CD cannot deliver.
@subsection libburner-source Sourceode of libburner Click on blue names of functions, structures, variables, etc in oder to get to the according specs of libburn API or libburner sourcecode. */ /** @author Mario Danic, Thomas Schmitt @mainpage Libburn Documentation Index @section intro Introduction Libburn is an open-source library for reading, mastering and writing optical discs. For now this means only CD-R and CD-RW. The project comprises of several more or less interdependent parts which together strive to be a usable foundation for application development. These are libraries, language bindings, and middleware binaries which emulate classical (and valuable) Linux tools. Our scope is currently Linux 2.4 and 2.6 and we will have a hard time to widen this for now, because of our history. The project could need advise from or membership of skilled kernel people and people who know how to talk CD/DVD drives into doing things. We do have a workable code base for burning data CDs, though. The burn API is quite comprehensively documented and can be used to build a presentable application. We do have a functional binary which emulates parts of cdrecord in order to prove that usability, and in order to allow you to explore libburn's scope by help of existing cdrecord frontends. @subsection components The project components (list subject to growth, hopefully): - libburn is the library by which preformatted data get onto optical media. It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or /dev/hdX (e.g. on kernel 2.6). libburn is the foundation of our cdrecord emulation. - libisofs is the library to pack up hard disk files and directories into a ISO 9660 disk image. This may then be brought to CD via libburn. libisofs is to be the foundation of our upcoming mkisofs emulation. - cdrskin is a limited cdrecord compatibility wrapper for libburn. cdrecord is a powerful GPL'ed burn program included in Joerg Schilling's cdrtools. cdrskin strives to be a second source for the services traditionally provided by cdrecord. cdrskin does not contain any bytes copied from cdrecord's sources. Many bytes have been copied from the message output of cdrecord runs, though. See cdrskin/README for more. - "test" is a collection of application gestures and examples given by the authors of the library features. The main API example of libburn is named test/libburner.c . Explore these examples if you look for inspiration. We plan to be a responsive upstream. Bear with us. @section using Using the libraries Our build system is based on autotools. User experience tells us that you will need at least autotools version 1.7. To build libburn and its subprojects it should be sufficient to go into its toplevel directory and execute - ./bootstrap (needed if you downloaded from SVN) - ./configure - make To make the libraries accessible for running resp. developing applications - make install Both libraries are written in C language and get built by autotools. Thus we expect them to be useable by a wide range of Linux-implemented languages and development tools. @section libburner Libburner libburner is a minimal demo application for the library libburn (see: libburn/libburn.h) as provided on http://libburn.pykix.org . It can list the available devices, can blank a CD-RW and can burn to CD-R or CD-RW. It's main purpose, nevertheless, is to show you how to use libburn and also to serve the libburn team as reference application. libburner does indeed define the standard way how above three gestures can be implemented and stay upward compatible for a good while. @subsection libburner-help Libburner --help
Usage: test/libburner
       [--drive 
||"-"] [--verbose ] [--blank_fast|--blank_full] [--burn_for_real|--try_to_simulate] [--stdin_size ] [|"-"] Examples A bus scan (needs rw-permissions to see a drive): test/libburner --drive - Burn a file to drive chosen by number: test/libburner --drive 0 --burn_for_real my_image_file Burn a file to drive chosen by persistent address: test/libburner --drive /dev/hdc --burn_for_real my_image_file Blank a used CD-RW (is combinable with burning in one run): test/libburner --drive 0 --blank_fast Burn a compressed afio archive on-the-fly, pad up to 700 MB: ( cd my_directory ; find . -print | afio -oZ - ) | \ test/libburner --drive /dev/hdc --burn_for_real --stdin_size 734003200 - To be read from *not mounted* CD via: afio -tvZ /dev/hdc Program tar would need a clean EOF which our padded CD cannot deliver.
@subsection libburner-source Sourceode of libburner Click on blue names of functions, structures, variables, etc in oder to get to the according specs of libburn API or libburner sourcecode. @include libburner.c */ # Doxyfile 1.5.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file that # follow. The default is UTF-8 which is also the encoding used for all text before # the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into # libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of # possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = @PACKAGE_NAME@ # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @PACKAGE_VERSION@ # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, # Italian, Japanese, Japanese-en (Japanese with English messages), Korean, # Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, # Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = @top_srcdir@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = YES # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to # include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be extracted # and appear in the documentation as a namespace called 'anonymous_namespace{file}', # where file will be replaced with the base name of the file that contains the anonymous # namespace. By default anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from the # version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = libisofs \ doc # This tag can be used to specify the character encoding of the source files that # doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default # input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. # See http://www.gnu.org/software/libiconv for the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py FILE_PATTERNS = libisofs.h \ comments # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the output. # The symbol name can be a fully qualified name, a word, or if the wildcard * is used, # a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = test # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH # then you must also enable this option. If you don't then doxygen will produce # a warning and turn it on anyway SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = YES # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentstion. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = OB \ OTK \ _ #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = doc/html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 200 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = letter # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = DOXYGEN # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to # produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to # specify the directory where the mscgen tool resides. If left empty the tool is assumed to # be found in the default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a caller dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected # functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = NO # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the number # of direct children of the root node in a graph is already larger than # MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libisofs Description: ISO9660 filesystem creation library Version: @VERSION@ Requires: Libs: -L${libdir} -lisofs Cflags: -I${includedir}/libisofs all clean: $(MAKE) -C .. -$(MAKEFLAGS) $@ .PHONY: all clean /* * Contains a simple implementation of a data source that reads from a * given file. */ #include #include #include #include #include #include #include "libisofs.h" #define BLOCK_SIZE 2048 #define BLOCK_OUT_OF_FILE -1; #define READ_ERROR -2; #define SEEK_ERROR -3; struct file_data_src { int fd; int nblocks; }; static int ds_read_block(struct data_source *src, int lba, unsigned char *buffer) { struct file_data_src *data; assert(src && buffer); data = (struct file_data_src*)src->data; /* For block devices size is always 0, so this can't be used. * if (lba >= data->nblocks) * return BLOCK_OUT_OF_FILE; */ /* goes to requested block */ if ( lseek(data->fd, (off_t)lba * (off_t)BLOCK_SIZE, SEEK_SET) == (off_t) -1 ) return SEEK_ERROR; if ( read(data->fd, buffer, BLOCK_SIZE) != BLOCK_SIZE ) return READ_ERROR; return 0; } static int ds_get_size(struct data_source *src) { struct file_data_src *data; assert(src); data = (struct file_data_src*)src->data; return data->nblocks; } static void ds_free_data(struct data_source *src) { struct file_data_src *data; assert(src); data = (struct file_data_src*)src->data; /* close the file */ close(data->fd); free(data); } struct data_source *data_source_from_file(const char *path) { int fd; struct stat info; struct file_data_src *data; struct data_source *ret; assert(path); fd = open(path, O_RDONLY); if (fd == -1) return NULL; fstat(fd, &info); data = malloc(sizeof(struct file_data_src)); data->fd = fd; data->nblocks = info.st_size / BLOCK_SIZE; ret = malloc(sizeof(struct data_source)); ret->refcount = 1; ret->read_block = ds_read_block; ret->get_size = ds_get_size; ret->free_data = ds_free_data; ret->data = data; return ret; } void data_source_free(struct data_source *src) { if (--src->refcount == 0) { src->free_data(src); free(src); } } /* -*- 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 "ecma119.h" #include "ecma119_tree.h" #include "susp.h" #include "rockridge.h" #include "joliet.h" #include "volume.h" #include "tree.h" #include "util.h" #include "file.h" #include "file_src.h" #include "libisofs.h" #include "libburn/libburn.h" #include "eltorito.h" #include "messages.h" /* burn-source compatible stuff */ static int bs_read(struct burn_source *bs, unsigned char *buf, int size); static off_t bs_get_size(struct burn_source *bs); static void bs_free_data(struct burn_source *bs); typedef void (*write_fn)(struct ecma119_write_target*, uint8_t*); /* return true if the given state is only required for Joliet volumes */ static int is_joliet_state(enum ecma119_write_state); static void next_state(struct ecma119_write_target *t); /* write t->state_data to the buf, one block at a time */ static void write_data_chunk(struct ecma119_write_target *t, uint8_t *buf); /* writing functions. All these functions assume the buf is large enough */ static void write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf); static void write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf); static void write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf); static void write_l_path_table(struct ecma119_write_target *t, uint8_t *buf); static void write_m_path_table(struct ecma119_write_target *t, uint8_t *buf); static void write_one_dir_record(struct ecma119_write_target *t, struct ecma119_tree_node *dir, int file_id, uint8_t *buf); static void write_one_dir(struct ecma119_write_target *t, struct ecma119_tree_node *dir, uint8_t *buf); static void write_dirs(struct ecma119_write_target *t, uint8_t *buf); /* wrapper functions for writing */ static void wr_system_area(struct ecma119_write_target*, uint8_t*); static void wr_pri_vol_desc(struct ecma119_write_target*, uint8_t*); static void wr_vol_desc_term(struct ecma119_write_target*, uint8_t*); static void wr_l_path_table(struct ecma119_write_target*, uint8_t*); static void wr_m_path_table(struct ecma119_write_target*, uint8_t*); static void wr_dir_records(struct ecma119_write_target*, uint8_t*); static void wr_files(struct ecma119_write_target*, uint8_t*); static const write_fn writers[] = { NULL, wr_system_area, wr_pri_vol_desc, el_torito_wr_boot_vol_desc, joliet_wr_sup_vol_desc, wr_vol_desc_term, wr_l_path_table, wr_m_path_table, joliet_wr_l_path_table, joliet_wr_m_path_table, wr_dir_records, joliet_wr_dir_records, el_torito_wr_catalog, wr_files }; /* When a writer is created, we * 1) create an ecma119 tree * 2) add SUSP fields (if necessary) * 3) calculate the size and position of all nodes in the tree * 4) finalize SUSP fields (if necessary) */ static void add_susp_fields_rec(struct ecma119_write_target *t, struct ecma119_tree_node *node) { size_t i; rrip_add_PX(t, node); rrip_add_NM(t, node); rrip_add_TF(t, node); switch (node->type) { case ECMA119_FILE: break; case ECMA119_SYMLINK: rrip_add_SL(t, node); break; case ECMA119_DIR: if (node->info.dir.real_parent != node->parent) { rrip_add_RE(t, node); rrip_add_PL(t, node); } for (i = 0; i < node->info.dir.nchildren; i++) { add_susp_fields_rec(t, node->info.dir.children[i]); } break; case ECMA119_PLACEHOLDER: rrip_add_CL(t, node); break; default: // FIXME support for device blocks by uncommenting this //if (node->iso_self->attrib.st_rdev) // rrip_add_PN(t, node); break; } susp_add_CE(t, node); } static void add_susp_fields(struct ecma119_write_target *t) { susp_add_SP(t, t->root); rrip_add_ER(t, t->root); add_susp_fields_rec(t, t->root); } /** * Fill out the dir.len and dir.CE_len fields for each * ecma119_tree_node that is a directory. Also calculate the total number of * directories and the number of files for which we need to write out data. * (dirlist_len and filelist_len) */ static void calc_dir_size(struct ecma119_write_target *t, struct ecma119_tree_node *dir) { size_t i; size_t newlen; assert(dir->type == ECMA119_DIR); t->dirlist_len++; dir->info.dir.len = 34 + dir->info.dir.self_susp.non_CE_len + 34 + dir->info.dir.parent_susp.non_CE_len; dir->info.dir.CE_len = dir->info.dir.self_susp.CE_len + dir->info.dir.parent_susp.CE_len; for (i = 0; i < dir->info.dir.nchildren; ++i) { struct ecma119_tree_node *ch = dir->info.dir.children[i]; newlen = dir->info.dir.len + ch->dirent_len + ch->susp.non_CE_len; if ((newlen % 2048) < (dir->info.dir.len % 2048)) { dir->info.dir.len = newlen + (2048 - (dir->info.dir.len % 2048)); } else { dir->info.dir.len += ch->dirent_len + ch->susp.non_CE_len; } dir->info.dir.CE_len += ch->susp.CE_len; } t->total_dir_size += round_up(dir->info.dir.len + dir->info.dir.CE_len, t->block_size); for (i = 0; i < dir->info.dir.nchildren; i++) { struct ecma119_tree_node *ch = dir->info.dir.children[i]; if (ch->type == ECMA119_DIR) { calc_dir_size(t, ch); } } } /** * Fill out the block field in each ecma119_tree_node that is a directory and * fill out t->dirlist. */ static void calc_dir_pos(struct ecma119_write_target *t, struct ecma119_tree_node *dir) { size_t i; assert(dir->type == ECMA119_DIR); dir->info.dir.block = t->curblock; t->curblock += div_up(dir->info.dir.len + dir->info.dir.CE_len, t->block_size); t->dirlist[t->curfile++] = dir; for (i = 0; i < dir->info.dir.nchildren; i++) { struct ecma119_tree_node *ch = dir->info.dir.children[i]; if (ch->type == ECMA119_DIR) calc_dir_pos(t, ch); } } static int cmp_file(const void *f1, const void *f2) { struct iso_file *f = *((struct iso_file**)f1); struct iso_file *g = *((struct iso_file**)f2); /* higher weighted first */ return g->sort_weight - f->sort_weight; } /** * Fill out the block field for each file and fill out t->filelist. */ static void calc_file_pos(struct ecma119_write_target *t) { size_t i; assert(t); t->filelist = calloc(1, sizeof(struct iso_file *) * t->file_table->count); for (i = 0; i < FILE_HASH_NODES; ++i) { struct iso_file_hash_node *node; node = t->file_table->table[i]; if (!node) continue; do { struct iso_file *file = node->file; /* * We only need to write the file when. * a) The size is greater than 0. * b) The file is new (not from a previous image) or we * are writting an image not for ms (i.e., we are modifying an * image). */ if ( file->size && (!file->prev_img || !t->ms_block) ) t->filelist[t->curfile++] = file; node = node->next; } while (node); } t->filelist_len = t->curfile; /* sort */ if ( t->sort_files ) qsort(t->filelist, t->filelist_len, sizeof(void*), cmp_file); /* fill block value */ for ( i = 0; i < t->filelist_len; ++i) { struct iso_file *file = t->filelist[i]; file->block = t->curblock; t->curblock += div_up(file->size, t->block_size); } /* reset curfile when we're finished */ t->curfile = 0; } /** * Create a new ecma119_write_target from the given volume number of the * given volume set. * * \pre \p volnum is less than \p volset-\>volset_size. * \post For each node in the tree, writer_data has been allocated. * \post The directory heirarchy has been reorganised to be ecma119-compatible. */ static struct ecma119_write_target* ecma119_target_new(struct iso_volset *volset, struct ecma119_source_opts *opts) { struct ecma119_write_target *t = calloc(1, sizeof(struct ecma119_write_target)); size_t i, j, cur; struct iso_tree_node *iso_root = (struct iso_tree_node*) volset->volume[opts->volnum]->root; t->cache_inodes = opts->no_cache_inodes ? 0 : 1; t->replace_mode = opts->default_mode ? 0 : 1; if ( opts->replace_dir_mode ) t->replace_mode |= 0x02; if ( opts->replace_file_mode ) t->replace_mode |= 0x04; if ( opts->replace_gid ) t->replace_mode |= 0x08; if ( opts->replace_uid ) t->replace_mode |= 0x10; t->dir_mode = opts->dir_mode; t->file_mode = opts->file_mode; t->gid = opts->gid; t->uid = opts->uid; if (opts->input_charset) { t->input_charset = opts->input_charset; } else { /* default to locale charset */ setlocale(LC_CTYPE, ""); t->input_charset = nl_langinfo(CODESET); } t->ouput_charset = opts->ouput_charset ? opts->ouput_charset : "UTF-8"; t->sort_files = opts->sort_files; t->ms_block = opts->ms_block; t->src = opts->src; t->file_table = iso_file_table_new(t->cache_inodes); volset->refcount++; t->iso_level = opts->level; t->block_size = 2048; t->relaxed_constraints = opts->relaxed_constraints; t->rockridge = (opts->flags & ECMA119_ROCKRIDGE) ? 1 : 0; t->joliet = (opts->flags & ECMA119_JOLIET) ? 1 : 0; t->catalog = volset->volume[opts->volnum]->bootcat; t->eltorito = t->catalog ? 1 : 0; t->write_eltorito = opts->copy_eltorito; if (t->eltorito && (!t->ms_block || !t->catalog->proc) ) { /* * For new and modified images we always need to write el-torito. * For ms images, if the boot catalog was newly added, we also need * to write it! */ t->write_eltorito = 1; } /* create the trees */ t->root = ecma119_tree_create(t, iso_root); if (t->joliet) t->joliet_root = joliet_tree_create(t, iso_root); t->volset = volset; t->volnum = opts->volnum; t->now = time(NULL); if (t->rockridge) add_susp_fields(t); calc_dir_size(t, t->root); if (t->joliet) { joliet_calc_dir_size(t, t->joliet_root); t->pathlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len_joliet); t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len_joliet); } t->dirlist = calloc(1, sizeof(void*) * t->dirlist_len); t->pathlist = calloc(1, sizeof(void*) * t->dirlist_len); /* fill out the pathlist */ t->pathlist[0] = t->root; t->path_table_size = 10; /* root directory record */ cur = 1; for (i = 0; i < t->dirlist_len; i++) { struct ecma119_tree_node *dir = t->pathlist[i]; for (j = 0; j < dir->info.dir.nchildren; j++) { struct ecma119_tree_node *ch = dir->info.dir.children[j]; if (ch->type == ECMA119_DIR) { size_t len = 8 + strlen(ch->iso_name); t->pathlist[cur++] = ch; t->path_table_size += len + len % 2; } } } t->curblock = t->ms_block /* nwa for ms, usually 0 */ + 16 /* system area */ + 1 /* primary volume desc */ + 1; /* volume desc terminator */ if (t->eltorito) t->curblock += 1; /* boot record volume descriptor */ if (t->joliet) /* supplementary vol desc */ t->curblock += div_up (2048, t->block_size); t->l_path_table_pos = t->curblock; t->curblock += div_up(t->path_table_size, t->block_size); t->m_path_table_pos = t->curblock; t->curblock += div_up(t->path_table_size, t->block_size); if (t->joliet) { joliet_prepare_path_tables(t); t->l_path_table_pos_joliet = t->curblock; t->curblock += div_up(t->path_table_size_joliet, t->block_size); t->m_path_table_pos_joliet = t->curblock; t->curblock += div_up(t->path_table_size_joliet, t->block_size); } calc_dir_pos(t, t->root); /* reset curfile when we're finished */ t->curfile = 0; if (t->joliet) { joliet_calc_dir_pos(t, t->joliet_root); /* reset curfile when we're finished */ t->curfile = 0; } /* el-torito? */ if (t->eltorito) { if (t->write_eltorito) { /* add catalog block */ t->catblock = t->curblock; t->curblock += div_up(2048, t->block_size); /* add img block */ t->imgblock = t->curblock; t->curblock += div_up(t->catalog->image->node->node.attrib.st_size, t->block_size); } else { assert(t->ms_block); assert(t->catalog->proc); t->catblock = t->catalog->node->loc.block; t->imgblock = t->catalog->image->node->loc.block; } } calc_file_pos(t); if (t->rockridge) { susp_finalize(t, t->root); rrip_finalize(t, t->root); } t->total_size = (t->curblock - t->ms_block) * t->block_size; if (opts->overwrite) { /* * Get a copy of the volume descriptors to be written in a DVD+RW * disc */ uint8_t *buf; /* skip the first 16 blocks (system area) */ buf = opts->overwrite + 16 * t->block_size; /* * In the PVM to be written in the 16th sector of the disc, we * need to specify the full size. */ t->vol_space_size = t->curblock; write_pri_vol_desc(t, buf); buf += t->block_size; if (t->joliet) { joliet_write_sup_vol_desc(t, buf); buf += t->block_size; } if (t->eltorito) { el_torito_write_boot_vol_desc(t, buf); buf += t->block_size; } write_vol_desc_terminator(t, buf); } /* * The volume space size is just the size of the last session, in * case of ms images. */ t->vol_space_size = t->curblock - t->ms_block; /* prepare for writing */ t->curblock = 0; t->state = ECMA119_WRITE_SYSTEM_AREA; t->bytes_read = 2048; return t; } static int is_joliet_state(enum ecma119_write_state state) { return state == ECMA119_WRITE_SUP_VOL_DESC_JOLIET || state == ECMA119_WRITE_L_PATH_TABLE_JOLIET || state == ECMA119_WRITE_M_PATH_TABLE_JOLIET || state == ECMA119_WRITE_DIR_RECORDS_JOLIET; } static int is_eltorito_state(enum ecma119_write_state state) { return state == ECMA119_WRITE_ELTORITO_BOOT_VOL_DESC || state == ECMA119_WRITE_ELTORITO_CATALOG; } static void next_state(struct ecma119_write_target *t) { char msg[42]; t->state++; while ( (!t->joliet && is_joliet_state(t->state)) ||(!t->eltorito && is_eltorito_state(t->state)) ||(!t->write_eltorito && t->state == ECMA119_WRITE_ELTORITO_CATALOG) ) t->state++; sprintf(msg, "Now in state %d, curblock=%d.", t->state, t->curblock); iso_msg_debug(msg); } static void wr_system_area(struct ecma119_write_target *t, uint8_t *buf) { memset(buf, 0, t->block_size); if (t->curblock == 15) { next_state(t); } } static void wr_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, write_pri_vol_desc, 2048, buf); } static void wr_vol_desc_term(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, write_vol_desc_terminator, 2048, buf); } static void wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, write_l_path_table, t->path_table_size, buf); } static void wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, write_m_path_table, t->path_table_size, buf); } static void wr_dir_records(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, write_dirs, t->total_dir_size, buf); } static void wr_files(struct ecma119_write_target *t, uint8_t *buf) { int res; struct state_files *f_st = &t->state_files; struct iso_file *f = t->filelist[f_st->file]; if (!f_st->src) { if (!f->prev_img) { char msg[PATH_MAX + 14]; sprintf(msg, "Writing file %s", f->path); iso_msg_debug(msg); } f_st->src = f->src; if ( f->src->open(f->src) <= 0) { //FIXME instead of exit, just print a msg error and //skip file (what about reading from /dev/zero? instead) /* can only happen with new files */ err(1, "couldn't open %s for reading", f->path); } assert(t->curblock + t->ms_block == f->block); } res = f_st->src->read_block(f_st->src, buf); if (res == 0) { /* we have read the expected bytes from file */ f_st->src->close(f_st->src); f_st->src = NULL; f_st->file++; if (f_st->file >= t->filelist_len) next_state(t); } } static void write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf) { struct ecma119_pri_vol_desc *vol = (struct ecma119_pri_vol_desc*)buf; struct iso_volume *volume = t->volset->volume[t->volnum]; char *vol_id = str2d_char(volume->volume_id, t->input_charset); char *pub_id = str2a_char(volume->publisher_id, t->input_charset); char *data_id = str2a_char(volume->data_preparer_id, t->input_charset); char *volset_id = str2d_char(t->volset->volset_id, t->input_charset); char *system_id = str2a_char(volume->system_id, t->input_charset); char *application_id = str2a_char(volume->application_id, t->input_charset); char *copyright_file_id = str2d_char(volume->copyright_file_id, t->input_charset); char *abstract_file_id = str2d_char(volume->abstract_file_id, t->input_charset); char *biblio_file_id = str2d_char(volume->biblio_file_id, t->input_charset); vol->vol_desc_type[0] = 1; memcpy(vol->std_identifier, "CD001", 5); vol->vol_desc_version[0] = 1; if (system_id) strncpy((char*)vol->system_id, system_id, 32); else /* put linux by default? */ memcpy(vol->system_id, "LINUX", 5); if (vol_id) strncpy((char*)vol->volume_id, vol_id, 32); iso_bb(vol->vol_space_size, t->vol_space_size, 4); iso_bb(vol->vol_set_size, t->volset->volset_size, 2); iso_bb(vol->vol_seq_number, t->volnum + 1, 2); iso_bb(vol->block_size, t->block_size, 2); iso_bb(vol->path_table_size, t->path_table_size, 4); iso_lsb(vol->l_path_table_pos, t->l_path_table_pos, 4); iso_msb(vol->m_path_table_pos, t->m_path_table_pos, 4); write_one_dir_record(t, t->root, 3, vol->root_dir_record); /* mmm, why not check for null? */ strncpy((char*)vol->vol_set_id, volset_id, 128); strncpy((char*)vol->publisher_id, pub_id, 128); strncpy((char*)vol->data_prep_id, data_id, 128); if (application_id) strncpy((char*)vol->application_id, application_id, 128); if (copyright_file_id) strncpy((char*)vol->copyright_file_id, copyright_file_id, 37); if (abstract_file_id) strncpy((char*)vol->abstract_file_id, abstract_file_id, 37); if (biblio_file_id) strncpy((char*)vol->bibliographic_file_id, biblio_file_id, 37); iso_datetime_17(vol->vol_creation_time, t->now); iso_datetime_17(vol->vol_modification_time, t->now); iso_datetime_17(vol->vol_effective_time, t->now); vol->file_structure_version[0] = 1; free(vol_id); free(volset_id); free(pub_id); free(data_id); free(system_id); free(application_id); free(copyright_file_id); free(abstract_file_id); free(biblio_file_id); } static void write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf) { struct ecma119_vol_desc_terminator *vol = (struct ecma119_vol_desc_terminator*) buf; vol->vol_desc_type[0] = 255; memcpy(vol->std_identifier, "CD001", 5); vol->vol_desc_version[0] = 1; } static void write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf) { void (*write_int)(uint8_t*, uint32_t, int) = l_type ? iso_lsb : iso_msb; size_t i; struct ecma119_path_table_record *rec; struct ecma119_tree_node *dir; int parent = 0; for (i = 0; i < t->dirlist_len; i++) { dir = t->pathlist[i]; assert(dir->type == ECMA119_DIR); while ((i) && t->pathlist[parent] != dir->parent) parent++; assert(parent < i || i == 0); rec = (struct ecma119_path_table_record*) buf; rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->iso_name) : 1; rec->len_xa[0] = 0; write_int(rec->block, dir->info.dir.block, 4); write_int(rec->parent, parent + 1, 2); if (dir->parent) memcpy(rec->dir_id, dir->iso_name, rec->len_di[0]); buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2); } } static void write_l_path_table(struct ecma119_write_target *t, uint8_t *buf) { write_path_table(t, 1, buf); } static void write_m_path_table(struct ecma119_write_target *t, uint8_t *buf) { write_path_table(t, 0, buf); } /* if file_id is >= 0, we use it instead of the filename. As a magic number, * file_id == 3 means that we are writing the root directory record (in order * to distinguish it from the "." entry in the root directory) */ static void write_one_dir_record(struct ecma119_write_target *t, struct ecma119_tree_node *node, int file_id, uint8_t *buf) { uint32_t len; uint32_t block; uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len; uint8_t len_fi = (file_id >= 0) ? 1 : strlen(node->iso_name); uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id); uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->iso_name; struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; if (node->type == ECMA119_DIR) { len = node->info.dir.len; block = node->info.dir.block; } else if (node->type == ECMA119_FILE) { len = node->info.file->size; block = node->info.file->block; } else if (node->type == ECMA119_BOOT) { assert(t->eltorito); if (node->info.boot_img) { block = t->imgblock; len = t->catalog->image->node->node.attrib.st_size; } else { /* we always assume 2048 as catalog len */ block = t->catblock; len = 2048; } } else { /* for nodes other than files and dirs, we set both len and block to 0 */ len = 0; block = 0; } /* we don't write out susp fields for the root node */ if (t->rockridge) { if (file_id == 0) { assert(node->type == ECMA119_DIR); susp_write(t, &node->info.dir.self_susp, &buf[len_dr]); len_dr += node->info.dir.self_susp.non_CE_len; } else if (file_id == 1) { assert(node->type == ECMA119_DIR); susp_write(t, &node->info.dir.parent_susp, &buf[len_dr]); len_dr += node->info.dir.parent_susp.non_CE_len; } else if (file_id < 0) { susp_write(t, &node->susp, &buf[len_dr]); len_dr += node->susp.non_CE_len; } } if (file_id == 1 && node->parent) node = node->parent; rec->len_dr[0] = len_dr; iso_bb(rec->block, block, 4); iso_bb(rec->length, len, 4); iso_datetime_7(rec->recording_time, t->now); rec->flags[0] = (node->type == ECMA119_DIR) ? 2 : 0; iso_bb(rec->vol_seq_number, t->volnum + 1, 2); rec->len_fi[0] = len_fi; memcpy(rec->file_id, name, len_fi); } static void write_one_dir(struct ecma119_write_target *t, struct ecma119_tree_node *dir, uint8_t *buf) { size_t i; int j; size_t len; uint8_t *orig_buf = buf; uint8_t *prior_buf = buf; assert(dir->type == ECMA119_DIR); /* write the "." and ".." entries first */ write_one_dir_record(t, dir, 0, buf); buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; write_one_dir_record(t, dir, 1, buf); buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; for (i = 0; i < dir->info.dir.nchildren; i++) { write_one_dir_record(t, dir->info.dir.children[i], -1, buf); len = ((struct ecma119_dir_record*) buf)->len_dr[0]; if ((buf + len - prior_buf) >= 2048) { for (j = len - 1; j >= 0; j--) { prior_buf[2048 + j] = buf[j]; buf[j] = 0; } prior_buf += 2048; buf = prior_buf + len; } else { buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; } } /* write the susp continuation areas */ if (t->rockridge) { susp_write_CE(t, &dir->info.dir.self_susp, buf); buf += dir->info.dir.self_susp.CE_len; susp_write_CE(t, &dir->info.dir.parent_susp, buf); buf += dir->info.dir.parent_susp.CE_len; for (i = 0; i < dir->info.dir.nchildren; i++) { susp_write_CE(t, &dir->info.dir.children[i]->susp, buf); buf += dir->info.dir.children[i]->susp.CE_len; } } assert (buf - orig_buf == dir->info.dir.len + dir->info.dir.CE_len); } static void write_dirs(struct ecma119_write_target *t, uint8_t *buf) { size_t i; struct ecma119_tree_node *dir; for (i = 0; i < t->dirlist_len; i++) { dir = t->dirlist[i]; write_one_dir(t, dir, buf); buf += round_up(dir->info.dir.len + dir->info.dir.CE_len, t->block_size); } } void ecma119_start_chunking(struct ecma119_write_target *t, write_fn writer, off_t data_size, uint8_t *buf) { if (data_size != t->state_data_size) { data_size = round_up(data_size, t->block_size); t->state_data = realloc(t->state_data, data_size); t->state_data_size = data_size; } memset(t->state_data, 0, t->state_data_size); t->state_data_off = 0; t->state_data_valid = 1; writer(t, t->state_data); write_data_chunk(t, buf); } static void write_data_chunk(struct ecma119_write_target *t, uint8_t *buf) { memcpy(buf, t->state_data + t->state_data_off, t->block_size); t->state_data_off += t->block_size; if (t->state_data_off >= t->state_data_size) { assert (t->state_data_off <= t->state_data_size); t->state_data_valid = 0; next_state(t); } } static int bs_read_block(struct burn_source *bs) { struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; if (t->curblock >= t->vol_space_size) { /* total_size could be setted by libburn */ if ( t->curblock < (t->total_size / (off_t) t->block_size) ) { /* we pad the image */ memset(t->buffer, 0, t->block_size); t->curblock++; return t->block_size; } return 0; } if (t->state_data_valid) write_data_chunk(t, t->buffer); else writers[t->state](t, t->buffer); t->curblock++; return t->block_size; } static int bs_read(struct burn_source *bs, unsigned char *buf, int size) { int ret = 0,summed_ret = 0; struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; /* make safe against partial buffer returns */ while (1) { if (t->bytes_read == 2048) { /* we need to read next block */ t->bytes_read = 0; ret = bs_read_block(bs); if (ret <= 0) return ret; } int bytes_to_read = 2048 - t->bytes_read; if (summed_ret + bytes_to_read > size) { bytes_to_read = size - summed_ret; } memcpy(buf + summed_ret, t->buffer, bytes_to_read); t->bytes_read += bytes_to_read; summed_ret += bytes_to_read; if (summed_ret >= size) break; } return summed_ret; } static off_t bs_get_size(struct burn_source *bs) { struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; return t->total_size; } static void bs_free_data(struct burn_source *bs) { struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; ecma119_tree_free(t->root); iso_file_table_clear(t->file_table); free(t->dirlist); free(t->pathlist); free(t->dirlist_joliet); free(t->pathlist_joliet); free(t->filelist); free(t->state_data); if (t->joliet) joliet_tree_free(t->joliet_root); // TODO is this needed? if (t->state_files.src) t->state_files.src->close(t->state_files.src); } int bs_set_size(struct burn_source *bs, off_t size) { struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data; t->total_size = size; return 1; } struct burn_source *iso_source_new_ecma119(struct iso_volset *volset, struct ecma119_source_opts *opts) { struct burn_source *ret = calloc(1, sizeof(struct burn_source)); ret->refcount = 1; ret->read = bs_read; ret->get_size = bs_get_size; ret->set_size = bs_set_size; ret->free_data = bs_free_data; ret->data = ecma119_target_new(volset, opts); return ret; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* vim: set noet ts=8 sts=8 sw=8 : */ /** * \file ecma119.h * * Structures and definitions used for writing an emca119 (ISO9660) compatible * volume. */ #ifndef LIBISO_ECMA119_H #define LIBISO_ECMA119_H #include #include #include /* for FILE */ #include #include "susp.h" struct ecma119_tree_node; struct joliet_tree_node; /** * The possible states that the ecma119 writer can be in. */ enum ecma119_write_state { ECMA119_WRITE_BEFORE, ECMA119_WRITE_SYSTEM_AREA, ECMA119_WRITE_PRI_VOL_DESC, ECMA119_WRITE_ELTORITO_BOOT_VOL_DESC, ECMA119_WRITE_SUP_VOL_DESC_JOLIET, ECMA119_WRITE_VOL_DESC_TERMINATOR, ECMA119_WRITE_L_PATH_TABLE, ECMA119_WRITE_M_PATH_TABLE, ECMA119_WRITE_L_PATH_TABLE_JOLIET, ECMA119_WRITE_M_PATH_TABLE_JOLIET, ECMA119_WRITE_DIR_RECORDS, ECMA119_WRITE_DIR_RECORDS_JOLIET, ECMA119_WRITE_ELTORITO_CATALOG, ECMA119_WRITE_FILES, ECMA119_WRITE_DONE }; /** * Data describing the state of the ecma119 writer. Everything here should be * considered private! */ struct ecma119_write_target { struct ecma119_tree_node *root; struct joliet_tree_node *joliet_root; struct iso_volset *volset; int volnum; time_t now; /**< Time at which writing began. */ off_t total_size; /**< Total size of the output. This only * includes the current volume. */ uint32_t vol_space_size; unsigned int rockridge:1; unsigned int joliet:1; unsigned int iso_level:2; unsigned int eltorito:1; unsigned int write_eltorito:1; /**< * In multisession discs, select whether to copy el-torito catalog * and boot image. Copy is needed for isolinux images, that need to * be patched. However, it can lead to problems when the image is * not present in the iso filesystem, because we can't figure out * its size. In those cases, we only copy 1 block of data. * When modifying images, we always need to copy data. Thus, this is * always 1 for both new and modified images. */ struct el_torito_boot_catalog *catalog; uint32_t catblock; /**< location of the boot catalog in the new image */ uint32_t imgblock; /**< location of the boot image in the new image */ int relaxed_constraints; /**< see ecma119_relaxed_constraints_flag */ int replace_mode; /**< Replace ownership and modes of files * * 0. filesystem values * 1. useful values * bits 1-4 bitmask: * 2 - replace dir * 3 - replace file * 4 - replace gid * 5 - replace uid */ mode_t dir_mode; mode_t file_mode; gid_t gid; uid_t uid; char *input_charset; char *ouput_charset; int cache_inodes; int sort_files; /**< if sort files or not. Sorting is based of * the weight of each file */ /** * In the CD, each file must have an unique inode number. So each * time we add a new file, this is incremented. */ ino_t ino; uint32_t ms_block; /**< if != 0, nwa for multisession */ struct data_source* src; int curblock; uint16_t block_size; uint32_t path_table_size; uint32_t path_table_size_joliet; uint32_t l_path_table_pos; uint32_t m_path_table_pos; uint32_t l_path_table_pos_joliet; uint32_t m_path_table_pos_joliet; uint32_t total_dir_size; uint32_t total_dir_size_joliet; struct ecma119_tree_node **dirlist; /**< A pre-order list of directories * (this is the order in which we write * out directory records). */ struct ecma119_tree_node **pathlist; /**< A breadth-first list of * directories. This is used for * writing out the path tables. */ size_t dirlist_len; /**< The length of the previous 2 lists. */ struct iso_file_table *file_table; /**< * A hash table with info about all files */ struct iso_file **filelist; /**< A pre-order list of files.*/ size_t filelist_len; /* Length of the previous list. */ int curfile; /**< Used as a helper field for writing out filelist and dirlist */ /* Joliet versions of the above lists. Since Joliet doesn't require * directory relocation, the order of these lists might be different * from the lists above (but they will be the same length). */ struct joliet_tree_node **dirlist_joliet; struct joliet_tree_node **pathlist_joliet; size_t dirlist_len_joliet; enum ecma119_write_state state; /* The current state of the writer. */ /* Most writers work by * 1) making sure state_data is big enough for their data * 2) writing _all_ their data into state_data * 3) relying on write_data_chunk to write the data block * by block. */ uint8_t *state_data; off_t state_data_size; off_t state_data_off; int state_data_valid; /* for writing out files */ struct state_files { struct iso_file_src *src; /* source for reading from the file */ int file; /* The index in filelist that we are * currently writing (or about to write). */ } state_files; /* temp buffer for read functions */ uint8_t buffer[2048]; int bytes_read; }; #define BP(a,b) [(b) - (a) + 1] struct ecma119_pri_vol_desc { uint8_t vol_desc_type BP(1, 1); uint8_t std_identifier BP(2, 6); uint8_t vol_desc_version BP(7, 7); uint8_t unused1 BP(8, 8); uint8_t system_id BP(9, 40); uint8_t volume_id BP(41, 72); uint8_t unused2 BP(73, 80); uint8_t vol_space_size BP(81, 88); uint8_t unused3 BP(89, 120); uint8_t vol_set_size BP(121, 124); uint8_t vol_seq_number BP(125, 128); uint8_t block_size BP(129, 132); uint8_t path_table_size BP(133, 140); uint8_t l_path_table_pos BP(141, 144); uint8_t opt_l_path_table_pos BP(145, 148); uint8_t m_path_table_pos BP(149, 152); uint8_t opt_m_path_table_pos BP(153, 156); uint8_t root_dir_record BP(157, 190); uint8_t vol_set_id BP(191, 318); uint8_t publisher_id BP(319, 446); uint8_t data_prep_id BP(447, 574); uint8_t application_id BP(575, 702); uint8_t copyright_file_id BP(703, 739); uint8_t abstract_file_id BP(740, 776); uint8_t bibliographic_file_id BP(777, 813); uint8_t vol_creation_time BP(814, 830); uint8_t vol_modification_time BP(831, 847); uint8_t vol_expiration_time BP(848, 864); uint8_t vol_effective_time BP(865, 881); uint8_t file_structure_version BP(882, 882); uint8_t reserved1 BP(883, 883); uint8_t app_use BP(884, 1395); uint8_t reserved2 BP(1396, 2048); }; struct ecma119_sup_vol_desc { uint8_t vol_desc_type BP(1, 1); uint8_t std_identifier BP(2, 6); uint8_t vol_desc_version BP(7, 7); uint8_t vol_flags BP(8, 8); uint8_t system_id BP(9, 40); uint8_t volume_id BP(41, 72); uint8_t unused2 BP(73, 80); uint8_t vol_space_size BP(81, 88); uint8_t esc_sequences BP(89, 120); uint8_t vol_set_size BP(121, 124); uint8_t vol_seq_number BP(125, 128); uint8_t block_size BP(129, 132); uint8_t path_table_size BP(133, 140); uint8_t l_path_table_pos BP(141, 144); uint8_t opt_l_path_table_pos BP(145, 148); uint8_t m_path_table_pos BP(149, 152); uint8_t opt_m_path_table_pos BP(153, 156); uint8_t root_dir_record BP(157, 190); uint8_t vol_set_id BP(191, 318); uint8_t publisher_id BP(319, 446); uint8_t data_prep_id BP(447, 574); uint8_t application_id BP(575, 702); uint8_t copyright_file_id BP(703, 739); uint8_t abstract_file_id BP(740, 776); uint8_t bibliographic_file_id BP(777, 813); uint8_t vol_creation_time BP(814, 830); uint8_t vol_modification_time BP(831, 847); uint8_t vol_expiration_time BP(848, 864); uint8_t vol_effective_time BP(865, 881); uint8_t file_structure_version BP(882, 882); uint8_t reserved1 BP(883, 883); uint8_t app_use BP(884, 1395); uint8_t reserved2 BP(1396, 2048); }; struct ecma119_boot_rec_vol_desc { uint8_t vol_desc_type BP(1, 1); uint8_t std_identifier BP(2, 6); uint8_t vol_desc_version BP(7, 7); uint8_t boot_sys_id BP(8, 39); uint8_t boot_id BP(40, 71); uint8_t boot_catalog BP(72, 75); uint8_t unused BP(76, 2048); }; struct ecma119_vol_desc_terminator { uint8_t vol_desc_type BP(1, 1); uint8_t std_identifier BP(2, 6); uint8_t vol_desc_version BP(7, 7); uint8_t reserved BP(8, 2048); }; struct ecma119_dir_record { uint8_t len_dr BP(1, 1); uint8_t len_xa BP(2, 2); uint8_t block BP(3, 10); uint8_t length BP(11, 18); uint8_t recording_time BP(19, 25); uint8_t flags BP(26, 26); uint8_t file_unit_size BP(27, 27); uint8_t interleave_gap_size BP(28, 28); uint8_t vol_seq_number BP(29, 32); uint8_t len_fi BP(33, 33); uint8_t file_id BP(34, 34); /* 34 to 33+len_fi */ /* padding field (if len_fi is even) */ /* system use (len_dr - len_su + 1 to len_dr) */ }; struct ecma119_path_table_record { uint8_t len_di BP(1, 1); uint8_t len_xa BP(2, 2); uint8_t block BP(3, 6); uint8_t parent BP(7, 8); uint8_t dir_id BP(9, 9); /* 9 to 8+len_di */ /* padding field (if len_di is odd) */ }; /** * A utility function for writers that want to write their data all at once * rather than block-by-block. This creates a buffer of size \p size, passes * it to the given writer, then hands out block-sized chunks. */ void ecma119_start_chunking(struct ecma119_write_target *t, void (*)(struct ecma119_write_target*, uint8_t*), off_t size, uint8_t *buf); #endif /* LIBISO_ECMA119_H */ /* * Functions to read an ISO image. */ /* * TODO * we need some kind of force option, to continue reading image on * minor errors, such as incorrect time stamps.... * * TODO * need to check the ZF linux-especific extension for transparent decompresion * TODO * what the RR entry is? */ #include #include #include #include #include #include #include #include "ecma119_read.h" #include "ecma119_read_rr.h" #include "ecma119.h" #include "util.h" #include "volume.h" #include "tree.h" #include "messages.h" #include "eltorito.h" #define BLOCK_SIZE 2048 static int iso_read_dir(struct iso_read_info *info, struct iso_tree_node_dir *parent, uint32_t block); static struct el_torito_boot_catalog * read_el_torito_boot_catalog(struct iso_read_info *info, uint32_t block) { struct el_torito_validation_entry *ve; struct el_torito_default_entry *entry; struct el_torito_boot_catalog *catalog; struct el_torito_boot_image *image; unsigned char buffer[BLOCK_SIZE]; if ( info->src->read_block(info->src, block, buffer) < 0 ) { info->error = LIBISOFS_READ_FAILURE; return NULL; } ve = (struct el_torito_validation_entry*)buffer; /* check if it is a valid catalog (TODO: check also the checksum)*/ if ( (ve->header_id[0] != 1) || (ve->key_byte1[0] != 0x55) || (ve->key_byte2[0] != 0xAA) ) { iso_msg_sorry(LIBISO_EL_TORITO_WRONG, "Wrong or damaged El-Torito " "Catalog.\n El-Torito info will be ignored."); return NULL; } /* check for a valid platform */ if (ve->platform_id[0] != 0) { iso_msg_hint(LIBISO_EL_TORITO_UNHANLED, "Unsupported El-Torito platform.\n" "Only 80x86 si supported. El-Torito info will be ignored."); } /* ok, once we are here we assume it is a valid catalog */ catalog = malloc(sizeof(struct el_torito_boot_catalog)); image = calloc(1, sizeof(struct el_torito_boot_image)); catalog->image = image; catalog->proc = LIBISO_PREVIMG; { /* * Create the placeholder. * Note that this could be modified later if we find a directory entry * for the catalog in the iso tree. */ struct iso_tree_node_boot *boot; boot = calloc(1, sizeof(struct iso_tree_node_boot)); boot->node.refcount = 1; boot->node.attrib.st_mode = S_IFREG | 0777; boot->node.attrib.st_atime = boot->node.attrib.st_mtime = boot->node.attrib.st_ctime = time(NULL); boot->node.attrib.st_size = 2048; boot->node.type = LIBISO_NODE_BOOT; boot->node.procedence = LIBISO_PREVIMG; boot->node.name = NULL; boot->loc.block = block; catalog->node = boot; } /* parse the default entry */ entry = (struct el_torito_default_entry *)(buffer + 32); image->bootable = entry->boot_indicator[0] ? 1 : 0; //FIXME we need a way to handle patch_isolinux in ms images!!! image->isolinux = 0; image->type = entry->boot_media_type[0]; image->partition_type = entry->system_type[0]; image->load_seg = iso_read_lsb(entry->load_seg, 2); image->load_size = iso_read_lsb(entry->sec_count, 2); { /* * Create the placeholder. * Note that this could be modified later if we find a directory entry * for the image in the iso tree. */ struct iso_tree_node_boot *boot; boot = calloc(1, sizeof(struct iso_tree_node_boot)); boot->node.refcount = 1; boot->node.attrib.st_mode = S_IFREG | 0777; boot->node.attrib.st_atime = boot->node.attrib.st_mtime = boot->node.attrib.st_ctime = time(NULL); boot->node.attrib.st_size = 2048; boot->node.type = LIBISO_NODE_BOOT; boot->node.procedence = LIBISO_PREVIMG; boot->node.name = NULL; boot->img = 1; boot->loc.block = iso_read_lsb(entry->block, 4); image->node = boot; } //TODO how can we check if there are more entries? return catalog; } static struct el_torito_boot_catalog * read_el_torito_vol_desc(struct iso_read_info *info, unsigned char *buf) { struct ecma119_boot_rec_vol_desc *vol; vol = (struct ecma119_boot_rec_vol_desc*)buf; /* some sanity checks */ if ( strncmp((char*)vol->std_identifier, "CD001", 5) || vol->vol_desc_version[0] != 1 || strncmp((char*)vol->boot_sys_id, "EL TORITO SPECIFICATION", 23)) { iso_msg_hint(LIBISO_BOOT_VD_UNHANLED, "Unsupported Boot Vol. Desc.\n" "Only El-Torito Specification, Version 1.0 Volume " "Descriptors are supported. Ignoring boot info"); return NULL; } return read_el_torito_boot_catalog(info, iso_read_lsb(vol->boot_catalog, 4)); } /** * This reads the "." directory entry, and set the properties of the * given directory propertly. */ static int iso_read_dot_record(struct iso_read_info *info, struct iso_tree_node_dir *dir, struct ecma119_dir_record *record) { struct susp_sys_user_entry *sue; struct susp_iterator *iter; assert( info && dir && record ); iter = susp_iter_new(info, record); while ( (sue = susp_iter_next(iter)) ) { /* ignore entries from different version */ if (sue->version[0] != 1) continue; /* we don't care about any RR entry but PX and TF */ if (SUSP_SIG(sue, 'P', 'X')) { if (read_rr_PX(info, sue, &dir->node.attrib)) break; } else if (SUSP_SIG(sue, 'T', 'F')) { if (read_rr_TF(info, sue, &dir->node.attrib)) break; } } susp_iter_free(iter); if (info->error) return -1; return 0; } /** * Creates a suitable iso_tree_node from a directory record, and adds * it to parent dir. If the directory record refers to a dir, it calls * recursively iso_read_dir. * On success, return 0. * If file is not supported, return 0 but a new tree node is not added * to parent. * On serious error, returns -1 */ static int iso_read_single_directory_record(struct iso_read_info *info, struct iso_tree_node_dir *parent, struct ecma119_dir_record *record) { struct iso_tree_node *node; struct stat atts; time_t recorded; char *name = NULL; char *linkdest = NULL; uint32_t relocated_dir = 0; assert(info && record && parent); memset(&atts, 0, sizeof(atts)); /* * The idea is to read all the RR entries (if we want to do that and RR * extensions exist on image), storing the info we want from that. * Then, we need some sanity checks. * Finally, we select what kind of node it is, and set values properly. */ if (info->rr) { struct susp_sys_user_entry *sue; struct susp_iterator *iter; iter = susp_iter_new(info, record); while ( (sue = susp_iter_next(iter)) ) { /* ignore entries from different version */ if (sue->version[0] != 1) continue; if (SUSP_SIG(sue, 'P', 'X')) { if (read_rr_PX(info, sue, &atts)) break; } else if (SUSP_SIG(sue, 'T', 'F')) { if (read_rr_TF(info, sue, &atts)) break; } else if (SUSP_SIG(sue, 'N', 'M')) { name = read_rr_NM(sue, name); if (!name) { info->error = LIBISOFS_WRONG_RR; break; } } else if (SUSP_SIG(sue, 'S', 'L')) { linkdest = read_rr_SL(sue, linkdest); if (!linkdest) { info->error = LIBISOFS_WRONG_RR; break; } } else if (SUSP_SIG(sue, 'R', 'E')) { /* * this directory entry refers to a relocated directory. * We simply ignore it, as it will be correctly handled * when found the CL */ susp_iter_free(iter); free(name); return 0; /* is not an error */ } else if (SUSP_SIG(sue, 'C', 'L')) { /* * This entry is a placeholder for a relocated dir. * We need to ignore other entries, with the exception of NM. * Then we create a directory node that represents the * relocated dir, and iterate over its children. */ relocated_dir = iso_read_bb(sue->data.CL.child_loc, 4, NULL); } else if (SUSP_SIG(sue, 'S', 'F')) { iso_msg_sorry(LIBISO_RR_UNSUPPORTED, "Sparse files not supported."); info->error = LIBISOFS_UNSUPPORTED_IMAGE; break; } else if (SUSP_SIG(sue, 'R', 'R')) { /* TODO I've seen this RR on mkisofs images. what's this? */ continue; } else { char msg[28]; sprintf(msg, "Unhandled SUSP entry %c%c.", sue->sig[0], sue->sig[1]); iso_msg_hint(LIBISO_SUSP_UNHANLED, msg); } } if ( !info->error && !relocated_dir && atts.st_mode == (mode_t) 0 ) { iso_msg_sorry(LIBISO_RR_ERROR, "Mandatory Rock Ridge PX entry is " "not present or it contains invalid values."); info->error = LIBISOFS_WRONG_RR; } susp_iter_free(iter); if (info->error) return -1; //TODO convert name to needed charset!! } else { /* RR extensions are not read / used */ atts.st_mode = info->mode; atts.st_gid = info->gid; atts.st_uid = info->uid; if (record->flags[0] & 0x02) atts.st_mode |= S_IFDIR; else atts.st_mode |= S_IFREG; atts.st_ino = ++info->ino; } /* * if we haven't RR extensions, or no NM entry is present, * we use the name in directory record */ if (!name) { size_t len; name = info->get_name((char*)record->file_id, record->len_fi[0]); /* remove trailing version number */ len = strlen(name); if (len > 2 && name[len-2] == ';' && name[len-1] == '1') { name[len-2] = '\0'; } } /* * if we haven't RR extensions, or a needed TF time stamp is not present, * we use plain iso recording time */ recorded = iso_datetime_read_7(record->recording_time); if ( atts.st_atime == (time_t) 0 ) { atts.st_atime = recorded; } if ( atts.st_ctime == (time_t) 0 ) { atts.st_ctime = recorded; } if ( atts.st_mtime == (time_t) 0 ) { atts.st_mtime = recorded; } /* the size is read from iso directory record */ atts.st_size = iso_read_bb(record->length, 4, NULL); if (relocated_dir) { /* * Ensure that a placeholder for a relocated dir appears as * a directory (mode & S_IFDIR). * This is need because the placeholder is really a file, and * in theory PX entry must be ignored. * However, to make code clearer, we don't ignore it, because * anyway it will be replaced by "." entry when recursing. */ atts.st_mode = S_IFDIR | (atts.st_mode & ~S_IFMT); } //TODO sanity checks!! switch(atts.st_mode & S_IFMT) { case S_IFDIR: { node = calloc(1, sizeof(struct iso_tree_node_dir)); node->type = LIBISO_NODE_DIR; } break; case S_IFREG: { uint32_t block; block = iso_read_bb(record->block, 4, NULL); if (info->bootcat && block == info->bootcat->node->loc.block) { /* it is the boot catalog */ node = (struct iso_tree_node*)info->bootcat->node; } else if (info->bootcat && block == info->bootcat->image->node->loc.block) { /* it is the boot image */ node = (struct iso_tree_node*)info->bootcat->image->node; } else { /* it is a file */ node = calloc(1, sizeof(struct iso_tree_node_file)); node->type = LIBISO_NODE_FILE; /* set block with extend */ ((struct iso_tree_node_file*)node)->loc.block = block; } } break; case S_IFLNK: { node = calloc(1, sizeof(struct iso_tree_node_symlink)); node->type = LIBISO_NODE_SYMLINK; /* set the link dest */ ((struct iso_tree_node_symlink*)node)->dest = linkdest; } break; default: iso_msg_sorry(LIBISO_RR_UNSUPPORTED, "File type not supported."); return -1; } node->name = name; node->attrib = atts; node->refcount++; /* 1 for news, 2 for boot nodes */ node->procedence = LIBISO_PREVIMG; iso_tree_add_child(parent, node); if (node->type == LIBISO_NODE_DIR) { uint32_t block; if (relocated_dir) block = relocated_dir; else block = iso_read_bb(record->block, 4, NULL); /* add all children */ return iso_read_dir(info, (struct iso_tree_node_dir*)node, block); } else return 0; } /** * Read all directory records in a directory, and creates a node for each * of them, adding them to \p dir. */ static int iso_read_dir(struct iso_read_info *info, struct iso_tree_node_dir *dir, uint32_t block) { unsigned char buffer[2048]; struct ecma119_dir_record *record; uint32_t size; uint32_t pos = 0; uint32_t tlen = 0; if ( info->src->read_block(info->src, block, buffer) < 0 ) { info->error = LIBISOFS_READ_FAILURE; return -1; } /* Attributes of dir are set in the "." entry */ record = (struct ecma119_dir_record *)(buffer + pos); size = iso_read_bb(record->length, 4, NULL); if (info->rr) iso_read_dot_record(info, dir, record); tlen += record->len_dr[0]; pos += record->len_dr[0]; /* skip ".." */ record = (struct ecma119_dir_record *)(buffer + pos); tlen += record->len_dr[0]; pos += record->len_dr[0]; while( tlen < size ) { record = (struct ecma119_dir_record *)(buffer + pos); if (pos == 2048 || record->len_dr[0] == 0) { /* * The directory entries are splitted in several blocks * read next block */ if ( info->src->read_block(info->src, ++block, buffer) < 0 ) { info->error = LIBISOFS_READ_FAILURE; return -1; } tlen += 2048 - pos; pos = 0; /* next block must begin with a non-0 directory record */ assert(buffer[0] != 0); continue; } /* * What about ignoring files with existence flag? * if (record->flags[0] & 0x01) * continue; */ /* * TODO * For a extrange reason, mkisofs relocates directories under * a RR_MOVED dir. It seems that it is only used for that purposes, * and thus it should be removed from the iso tree before * generating a new image with libisofs, that don't uses it. * We can do that here, but I think it's a better option doing it * on an app. using the library, such as genisofs. * * if ( record->len_fi[0] == 8 && * !strncmp(record->file_id,"RR_MOVED", 8) ) { * continue; * } */ /* check for unsupported multiextend */ if (record->flags[0] & 0x80) { iso_msg_fatal(LIBISO_IMG_UNSUPPORTED, "Unsupported image.\n" "This image makes use of Multi-Extend features, that " "are not supported at this time.\n" "If you need support for that, please request us this feature.\n" "Thank you in advance\n"); info->error = LIBISOFS_UNSUPPORTED_IMAGE; return -1; } /* check for unsupported interleaved mode */ if ( record->file_unit_size[0] || record->interleave_gap_size[0] ) { iso_msg_fatal(LIBISO_IMG_UNSUPPORTED, "Unsupported image.\n" "This image has at least one file recorded in " "interleaved mode.\n" "We don't support this mode, as we think it's not used.\n" "If you're reading this, then we're wrong :)\n" "Please contact libisofs developers, so we can fix this.\n" "Thank you in advance\n"); info->error = LIBISOFS_UNSUPPORTED_IMAGE; return -1; } //TODO check for unsupported extended attribs? //TODO check for other flags? if ( iso_read_single_directory_record(info, dir, record) ) return -1; tlen += record->len_dr[0]; pos += record->len_dr[0]; } return 0; } /** * Read the SUSP system user entries of the "." entry of the root directory, * indentifying when Rock Ridge extensions are being used. */ static int read_root_susp_entries(struct iso_read_info *info, struct iso_tree_node_dir *root, uint32_t block) { unsigned char buffer[2048]; struct ecma119_dir_record *record; struct susp_sys_user_entry *sue; struct susp_iterator *iter; if ( info->src->read_block(info->src, block, buffer) < 0 ) { info->error = LIBISOFS_READ_FAILURE; return -1; } /* record will be the "." directory entry for the root record */ record = (struct ecma119_dir_record *)buffer; /* * TODO * SUSP specification claims that for CD-ROM XA the SP entry * is not at position BP 1, but at BP 15. Is that used? * In that case, we need to set info->len_skp to 15!! */ iter = susp_iter_new(info, record); /* first entry must be an SP system use entry */ sue = susp_iter_next(iter); if (!sue && info->error) { susp_iter_free(iter); return -1; } else if (!sue || !SUSP_SIG(sue, 'S', 'P') ) { iso_msg_debug("SUSP/RR is not being used."); susp_iter_free(iter); return 0; } /* it is a SP system use entry */ if ( sue->version[0] != 1 || sue->data.SP.be[0] != 0xBE || sue->data.SP.ef[0] != 0xEF) { iso_msg_sorry(LIBISO_SUSP_WRONG, "SUSP SP system use entry seems to " "be wrong. Ignoring Rock Ridge Extensions."); susp_iter_free(iter); return 0; } iso_msg_debug("SUSP/RR is being used."); /* * The LEN_SKP field, defined in IEEE 1281, SUSP. 5.3, specifies the * number of bytes to be skipped within each System Use field. * I think this will be always 0, but given that support this standard * features is easy... */ info->len_skp = sue->data.SP.len_skp[0]; /* * Ok, now search for ER entry. * Just notice that the attributes for root dir are read in * iso_read_dir * * TODO if several ER are present, we need to identify the position of * what refers to RR, and then look for corresponding ES entry in * each directory record. I have not implemented this (it's not used, * no?), but if we finally need it, it can be easily implemented in * the iterator, transparently for the rest of the code. */ while ( (sue = susp_iter_next(iter)) ) { /* ignore entries from different version */ if (sue->version[0] != 1) continue; if (SUSP_SIG(sue, 'E', 'R')) { if (info->rr) { iso_msg_warn(LIBISO_SUSP_MULTIPLE_ER, "More than one ER has found. This is not supported.\n" "It will be ignored, but can cause problems. " "Please notify us about this.\n"); } /* * it seems that Rock Ridge can be identified with any * of the following */ if ( sue->data.ER.len_id[0] == 10 && !strncmp((char*)sue->data.ER.ext_id, "RRIP_1991A", 10) ) { iso_msg_debug("Suitable Rock Ridge ER found. Version 1.10."); info->rr = RR_EXT_110; } else if ( ( sue->data.ER.len_id[0] == 10 && !strncmp((char*)sue->data.ER.ext_id, "IEEE_P1282", 10) ) || ( sue->data.ER.len_id[0] == 9 && !strncmp((char*)sue->data.ER.ext_id, "IEEE_1282", 9) ) ) { iso_msg_debug("Suitable Rock Ridge ER found. Version 1.12."); info->rr = RR_EXT_112; //TODO check also version? } else { iso_msg_warn(LIBISO_SUSP_MULTIPLE_ER, "Not Rock Ridge ER found.\n" "That will be ignored, but can cause problems in " "image reading. Please notify us about this"); } } } susp_iter_free(iter); if (info->error) return -1; return 0; } static struct iso_volset * read_pvm(struct iso_read_info *info, uint32_t block) { struct ecma119_pri_vol_desc *pvm; struct iso_volume *volume; struct iso_volset *volset; struct ecma119_dir_record *rootdr; char* volset_id; unsigned char buffer[BLOCK_SIZE]; if ( info->src->read_block(info->src, block, buffer) < 0 ) { info->error = LIBISOFS_READ_FAILURE; return NULL; } pvm = (struct ecma119_pri_vol_desc *)buffer; /* sanity checks */ if ( pvm->vol_desc_type[0] != 1 || strncmp((char*)pvm->std_identifier, "CD001", 5) || pvm->vol_desc_version[0] != 1 || pvm->file_structure_version[0] != 1 ) { iso_msg_fatal(LIBISO_WRONG_IMG, "Wrong PVM. Maybe this is a damaged " "image, or it's not an ISO-9660 image.\n"); info->error = LIBISOFS_WRONG_PVM; return NULL; } volume = iso_volume_new(NULL, NULL, NULL); /* fill strings */ volume->volume_id = strcopy((char*)pvm->volume_id, 32); volume->publisher_id = strcopy((char*)pvm->publisher_id, 128); volume->data_preparer_id = strcopy((char*)pvm->data_prep_id, 128); volume->system_id = strcopy((char*)pvm->system_id, 32); volume->application_id = strcopy((char*)pvm->application_id, 128); volume->copyright_file_id = strcopy((char*)pvm->copyright_file_id, 37); volume->abstract_file_id = strcopy((char*)pvm->abstract_file_id, 37); volume->biblio_file_id = strcopy((char*)pvm->bibliographic_file_id, 37); volset_id = strcopy((char*)pvm->vol_set_id, 128); *(info->size) = iso_read_bb(pvm->vol_space_size, 4, NULL); volset = iso_volset_new(volume, volset_id); free(volset_id); /* * TODO * I don't like the way the differences volset - volume are hanled now. * While theorically right (a volset can contain several volumes), in * practice it seems that this never happen. Current implementation, with * the volume array in volset, make things innecessarily harder. I think * we can refactor that in a single way. */ //volset->volset_size = pvm->vol_set_size[0]; rootdr = (struct ecma119_dir_record *)pvm->root_dir_record; /* * check if RR is being used. Note that this functions returns * != 0 on error. Info about if RR is being used is stored in info */ if ( read_root_susp_entries(info, volume->root, iso_read_bb(rootdr->block, 4, NULL)) ) { /* error, cleanup and return */ iso_volset_free(volset); return NULL; } /* are RR ext present */ info->hasRR = info->rr ? 1 : 0; info->iso_root_block = iso_read_bb(rootdr->block, 4, NULL); /* * PVM has things that can be interested, but don't have a member in * volume struct, such as creation date. In a multisession disc, we could * keep the creation date and update the modification date, for example. */ return volset; } struct iso_volset * iso_volset_read(struct data_source *src, struct ecma119_read_opts *opts) { struct iso_read_info info; struct iso_volset *volset; uint32_t block, root_dir_block; unsigned char buffer[BLOCK_SIZE]; assert(src && opts); /* fill info with suitable values */ info.error = LIBISOFS_READ_OK; info.src = src; info.rr = RR_EXT_NO; info.len_skp = 0; info.ino = 0; info.norock = opts->norock; info.uid = opts->uid; info.gid = opts->gid; info.mode = opts->mode & ~S_IFMT; info.size = &opts->size; info.bootcat = NULL; root_dir_block = 0; /* read primary volume description */ volset = read_pvm(&info, opts->block + 16); if (volset == NULL) { opts->error = info.error; return NULL; } block = opts->block + 17; do { if ( info.src->read_block(info.src, block, buffer) < 0 ) { info.error = LIBISOFS_READ_FAILURE; /* cleanup and exit */ goto read_cleanup; } switch (buffer[0]) { case 0: /* * This is a boot record * Here we handle el-torito */ info.bootcat = read_el_torito_vol_desc(&info, buffer); break; case 2: /* suplementary volume descritor */ { struct ecma119_sup_vol_desc *sup; struct ecma119_dir_record *root; sup = (struct ecma119_sup_vol_desc*)buffer; if (sup->esc_sequences[0] == 0x25 && sup->esc_sequences[1] == 0x2F && (sup->esc_sequences[2] == 0x40 || sup->esc_sequences[2] == 0x43 || sup->esc_sequences[2] == 0x45) ) { /* it's a Joliet Sup. Vol. Desc. */ info.hasJoliet = 1; root = (struct ecma119_dir_record*)sup->root_dir_record; root_dir_block = iso_read_bb(root->block, 4, NULL); //TODO maybe we can set the volume attribs from this //descriptor } else { iso_msg_hint(LIBISO_UNSUPPORTED_VD, "Not supported Sup. Vol. Desc found."); } } break; case 255: /* * volume set terminator * ignore, as it's checked in loop end condition */ break; default: { char msg[32]; sprintf(msg, "Ignoring Volume descriptor %d.", buffer[0]); iso_msg_hint(LIBISO_UNSUPPORTED_VD, msg); } break; } block++; } while (buffer[0] != 255); opts->hasRR = info.hasRR; opts->hasJoliet = info.hasJoliet; /* user doesn't want to read RR extensions */ if (info.norock) info.rr = RR_EXT_NO; /* select what tree to read */ if (info.rr) { /* RR extensions are available */ if (opts->preferjoliet && info.hasJoliet) { /* if user prefers joliet, that is used */ iso_msg_debug("Reading Joliet extensions."); info.get_name = ucs2str; info.rr = RR_EXT_NO; /* root_dir_block already contains root for joliet */ } else { /* RR will be used */ iso_msg_debug("Reading Rock Ridge extensions."); root_dir_block = info.iso_root_block; info.get_name = strcopy; } } else { /* RR extensions are not available */ if (info.hasJoliet && !opts->nojoliet) { /* joliet will be used */ iso_msg_debug("Reading Joliet extensions."); info.get_name = ucs2str; /* root_dir_block already contains root for joliet */ } else { /* default to plain iso */ iso_msg_debug("Reading plain ISO-9660 tree."); root_dir_block = info.iso_root_block; info.get_name = strcopy; } } /* Read the ISO/RR or Joliet tree */ if ( iso_read_dir(&info, volset->volume[0]->root, root_dir_block) ) { /* error, cleanup and return */ goto read_cleanup; } // TODO merge tree info /* Add El-Torito info to the volume */ if (info.bootcat) { /* ok, add the bootcat to the volume */ iso_msg_debug("Found El-Torito bootable volume"); volset->volume[0]->bootcat = info.bootcat; } return volset; read_cleanup:; if (info.bootcat) { el_torito_boot_catalog_free(info.bootcat); } iso_volset_free(volset); return NULL; } /* * ecma119_read.h * * Defines structures for reading from a iso image. */ #ifndef ECMA119_READ_H_ #define ECMA119_READ_H_ #include #include "libisofs.h" enum read_error { LIBISOFS_READ_OK = 0, LIBISOFS_READ_FAILURE, /**< Truncated image or read error */ LIBISOFS_WRONG_PVM, /**< Incorrect PVM */ LIBISOFS_UNSUPPORTED_IMAGE, /**< Format not supported (interleaved...) */ LIBISOFS_WRONG_RR /**< Wrong RR/SUSP extension format */ }; /** * Should the RR extensions be read? */ enum read_rr_ext { RR_EXT_NO = 0, /*< Do not use RR extensions */ RR_EXT_110, /*< RR extensions conforming version 1.10 */ RR_EXT_112 /*< RR extensions conforming version 1.12 */ }; /** * Structure that keeps info needed in the read process. */ struct iso_read_info { struct data_source *src; enum read_error error; uid_t uid; /**< Default uid when no RR */ gid_t gid; /**< Default uid when no RR */ mode_t mode; /**< Default mode when no RR (only permissions) */ uint32_t iso_root_block; /**< Will be filled with the block lba of the * extend for the root directory, as read from * the PVM */ enum read_rr_ext rr; /*< If we need to read RR extensions. i.e., if the image * contains RR extensions, and the user wants to read them. */ char *(*get_name)(const char *, size_t); /**< * The function used to read the name from a directoy record. For * ISO, the name is in US-ASCII. For Joliet, in UCS-2BE. Thus, we * need different functions for both. */ ino_t ino; /*< Joliet and RR version 1.10 does not have file serial numbers, * we need to generate it. */ uint8_t len_skp; /*< bytes skipped within the System Use field of a directory record, before the beginning of the SUSP system user entries. See IEEE 1281, SUSP. 5.3. */ unsigned int norock:1; /*< Do not read Rock Ridge extensions */ unsigned int hasRR:1; /*< It will be set to 1 if RR extensions are present, to 0 if not. */ unsigned int hasJoliet:1; /*< It will be set to 1 if Joliet ext are present, to 0 if not. */ uint32_t *size; /* place for el-torito boot catalog */ struct el_torito_boot_catalog *bootcat; }; #endif /*ECMA119_READ_H_*/ /* * This file contains functions related to the reading of SUSP and * Rock Ridge extensions on an ECMA-119 image. */ #include #include #include #include #include #include #include #include "ecma119.h" #include "ecma119_read.h" #include "ecma119_read_rr.h" #include "util.h" #include "messages.h" #define BLOCK_SIZE 2048 /** * Fills a struct stat with the values of a Rock Ridge PX entry * On error, info->error is set propertly and the function returns != 0 */ int read_rr_PX(struct iso_read_info *info, struct susp_sys_user_entry *px, struct stat *st) { assert( info && px && st); assert( px->sig[0] == 'P' && px->sig[1] == 'X'); if ( info->rr == RR_EXT_112 && px->len_sue[0] != 44 ) { iso_msg_sorry(LIBISO_RR_ERROR, "Invalid PX entry for RR version 1.12"); info->error = LIBISOFS_WRONG_RR; return -1; } else if ( info->rr == RR_EXT_110 && px->len_sue[0] != 36 ) { iso_msg_sorry(LIBISO_RR_ERROR, "Invalid PX entry for RR version 1.10"); info->error = LIBISOFS_WRONG_RR; return -1; } st->st_mode = iso_read_bb(px->data.PX.mode, 4, NULL); st->st_nlink = iso_read_bb(px->data.PX.links, 4, NULL); st->st_uid = iso_read_bb(px->data.PX.uid, 4, NULL); st->st_gid = iso_read_bb(px->data.PX.gid, 4, NULL); if (info->rr == RR_EXT_112) { st->st_ino = iso_read_bb(px->data.PX.serial, 4, NULL); } else { st->st_ino = ++info->ino; } return 0; } /** * Fills a struct stat with the values of a Rock Ridge TF entry * On error, info->error is set propertly and the function returns != 0 */ int read_rr_TF(struct iso_read_info *info, struct susp_sys_user_entry *tf, struct stat *st) { time_t time; int s; int nts = 0; assert( info && tf && st); assert( tf->sig[0] == 'T' && tf->sig[1] == 'F'); if (tf->data.TF.flags[0] & (1 << 7)) { /* long form */ s = 17; } else { s = 7; } /* 1. Creation time */ if (tf->data.TF.flags[0] & (1 << 0)) { /* the creation is the recording time. we ignore this */ /* TODO maybe it would be good to manage it in ms discs, where * the recording time could be different than now!! */ ++nts; } /* 2. modify time */ if (tf->data.TF.flags[0] & (1 << 1)) { if (tf->len_sue[0] < 5 + (nts+1) * s) { iso_msg_sorry(LIBISO_RR_ERROR, "RR TF entry too short."); info->error = LIBISOFS_WRONG_RR; return -1; } if (s == 7) { time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]); } else { time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]); } st->st_mtime = time; ++nts; } /* 3. access time */ if (tf->data.TF.flags[0] & (1 << 2)) { if (tf->len_sue[0] < 5 + (nts+1) * s) { iso_msg_sorry(LIBISO_RR_ERROR, "RR TF entry too short."); info->error = LIBISOFS_WRONG_RR; return -1; } if (s == 7) { time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]); } else { time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]); } st->st_atime = time; ++nts; } /* 4. attributes time */ if (tf->data.TF.flags[0] & (1 << 3)) { if (tf->len_sue[0] < 5 + (nts+1) * s) { iso_msg_sorry(LIBISO_RR_ERROR, "RR TF entry too short."); info->error = LIBISOFS_WRONG_RR; return -1; } if (s == 7) { time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]); } else { time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]); } st->st_ctime = time; ++nts; } /* we ignore backup, expire and effect times */ return 0; } char * read_rr_NM(struct susp_sys_user_entry *nm, char *name) { assert(nm); assert( nm->sig[0] == 'N' && nm->sig[1] == 'M'); /* concatenate the results */ if (name) { name = realloc(name, strlen(name) + nm->len_sue[0] - 5 + 1); strncat(name, (char*)nm->data.NM.name, nm->len_sue[0] - 5); } else { name = strcopy((char*)nm->data.NM.name, nm->len_sue[0] - 5); } return name; } char * read_rr_SL(struct susp_sys_user_entry *sl, char *dest) { int pos; assert(sl); assert( sl->sig[0] == 'S' && sl->sig[1] == 'L'); for (pos = 0; pos + 5 < sl->len_sue[0]; pos += 2 + sl->data.SL.comps[pos + 1]) { char *comp; uint8_t len; uint8_t flags = sl->data.SL.comps[pos]; if (flags & 0x2) { /* current directory */ len = 1; comp = "."; } else if (flags & 0x4) { /* parent directory */ len = 2; comp = ".."; } else if (flags & 0x8) { /* root directory */ len = 1; comp = "/"; } else if (flags & ~0x01) { char msg[38]; sprintf(msg, "SL component flag %x not supported.", flags); iso_msg_sorry(LIBISO_RR_ERROR, msg); return NULL; } else { len = sl->data.SL.comps[pos + 1]; comp = (char*)&sl->data.SL.comps[pos + 2]; } if (dest) { int size = strlen(dest); dest = realloc(dest, strlen(dest) + len + 2); if ( dest[size-1] != '/' ) { dest[size] = '/'; dest[size+1] = '\0'; } strncat(dest, comp, len); } else { dest = strcopy(comp, len); } } return dest; } struct susp_iterator { uint8_t* base; int pos; int size; struct iso_read_info *info; uint32_t ce_block; uint32_t ce_off; uint32_t ce_len; /*< Length of the next continuation area, 0 if no more CA are specified */ uint8_t *buffer; /*< If there are continuation areas */ }; struct susp_iterator * susp_iter_new(struct iso_read_info *info, struct ecma119_dir_record *record) { struct susp_iterator *iter = malloc(sizeof(struct susp_iterator)); int pad = (record->len_fi[0] + 1) % 2; iter->base = record->file_id + record->len_fi[0] + pad; iter->pos = info->len_skp; /* 0 in most cases */ iter->size = record->len_dr[0] - record->len_fi[0] - 33 -pad; iter->info = info; iter->ce_len = 0; iter->buffer = NULL; return iter; } struct susp_sys_user_entry * susp_iter_next(struct susp_iterator* iter) { struct susp_sys_user_entry *entry; entry = (struct susp_sys_user_entry*)(iter->base + iter->pos); if ( (iter->pos + 4 > iter->size) || (SUSP_SIG(entry, 'S', 'T')) ) { /* * End of the System Use Area or Continuation Area. * Note that ST is not needed when the space left is less than 4. * (IEEE 1281, SUSP. section 4) */ if (iter->ce_len) { uint32_t block; int nblocks; /* A CE has found, there is another continuation area */ nblocks = div_up(iter->ce_off + iter->ce_len, BLOCK_SIZE); iter->buffer = realloc(iter->buffer, nblocks * BLOCK_SIZE); /* read all block needed to cache the full CE */ for (block = 0; block < nblocks; ++block) { if (iter->info->src->read_block(iter->info->src, iter->ce_block + block, iter->buffer + block * BLOCK_SIZE)) { iter->info->error = LIBISOFS_READ_FAILURE; return NULL; } } iter->base = iter->buffer + iter->ce_off; iter->pos = 0; iter->size = iter->ce_len; iter->ce_len = 0; entry = (struct susp_sys_user_entry*)iter->base; } else { return NULL; } } if (entry->len_sue[0] == 0) { /* a wrong image with this lead us to a infinity loop */ iso_msg_sorry(LIBISO_RR_ERROR, "Damaged RR/SUSP information."); iter->info->error = LIBISOFS_WRONG_RR; return NULL; } iter->pos += entry->len_sue[0]; if ( SUSP_SIG(entry, 'C', 'E') ) { /* Continuation entry */ if (iter->ce_len) { iso_msg_sorry(LIBISO_RR_ERROR, "More than one CE System user entry " "has found in a single System Use field or continuation area. " "This breaks SUSP standard and it's not supported.\n" "Ignoring last CE. Maybe the image is damaged.\n"); } else { iter->ce_block = iso_read_bb(entry->data.CE.block, 4, NULL); iter->ce_off = iso_read_bb(entry->data.CE.offset, 4, NULL); iter->ce_len = iso_read_bb(entry->data.CE.len, 4, NULL); } /* we don't want to return CE entry to the user */ return susp_iter_next(iter); } else if ( SUSP_SIG(entry, 'P', 'D') ) { /* skip padding */ return susp_iter_next(iter); } return entry; } void susp_iter_free(struct susp_iterator* iter) { free(iter->buffer); free(iter); } /* * This file contains functions related to the reading of SUSP and * Rock Ridge extensions on an ECMA-119 image. */ #ifndef ECMA119_READ_RR_H_ #define ECMA119_READ_RR_H_ #include "libisofs.h" #include "ecma119.h" #include "ecma119_read.h" #define SUSP_SIG(entry, a, b) ( (entry->sig[0] == a) && (entry->sig[1] == b) ) /** * The SUSP iterator is used to iterate over the System User Entries * of a ECMA-168 directory record. * It takes care about Continuation Areas, handles the end of the different * system user entries and skip padding areas. Thus, using an iteration * we are accessing just to the meaning entries. */ struct susp_iterator; struct susp_iterator *susp_iter_new(struct iso_read_info *info, struct ecma119_dir_record *record); /** * Get the next SUSP System User Entry using given iterator. * The returned pointer refers directly to an internal buffer and it's not * guaranteed to be allocated after calling susp_iter_next() again. Thus, * if you need to keep some entry you have to do a copy. * * It return NULL when no more entries are available. Also, it will return * NULL on error. You must check info->error to distinguish between both * situations. */ struct susp_sys_user_entry *susp_iter_next(struct susp_iterator* iter); /** * Free a given susp iterator. */ void susp_iter_free(struct susp_iterator* iter); struct susp_CE { uint8_t block[8]; uint8_t offset[8]; uint8_t len[8]; }; struct susp_SP { uint8_t be[1]; uint8_t ef[1]; uint8_t len_skp[1]; }; struct susp_ER { uint8_t len_id[1]; uint8_t len_des[1]; uint8_t len_src[1]; uint8_t ext_ver[1]; uint8_t ext_id[1]; /*< up to len_id bytes */ /* ext_des, ext_src */ }; /** POSIX file attributes. */ struct rr_PX { uint8_t mode[8]; uint8_t links[8]; uint8_t uid[8]; uint8_t gid[8]; uint8_t serial[8]; }; /** Time stamps for a file. */ struct rr_TF { uint8_t flags[1]; uint8_t t_stamps[1]; }; /** Alternate name. */ struct rr_NM { uint8_t flags[1]; uint8_t name[1]; }; /** Link for a relocated directory. */ struct rr_CL { uint8_t child_loc[8]; }; /** Sim link. */ struct rr_SL { uint8_t flags[1]; uint8_t comps[1]; }; /** * Struct for a SUSP System User Entry */ struct susp_sys_user_entry { uint8_t sig[2]; uint8_t len_sue[1]; uint8_t version[1]; union { struct susp_CE CE; struct susp_SP SP; struct susp_ER ER; struct rr_PX PX; struct rr_TF TF; struct rr_NM NM; struct rr_CL CL; struct rr_SL SL; } data; /* 5 to 4+len_sue */ }; /** * Fills a struct stat with the values of a Rock Ridge PX entry * On error, info->error is set propertly and the function returns != 0 */ int read_rr_PX(struct iso_read_info *info, struct susp_sys_user_entry *px, struct stat *st); /** * Fills a struct stat with the values of a Rock Ridge TF entry * On error, info->error is set propertly and the function returns != 0 */ int read_rr_TF(struct iso_read_info *info, struct susp_sys_user_entry *tf, struct stat *st); /** * Apends the content of given Rock Ridge NM entry to \p name * On error, returns NULL */ char *read_rr_NM(struct susp_sys_user_entry *nm, char *name); /** * Apends the components in specified SL entry to \p dest, adding * needed '/'. * On error, returns NULL */ char *read_rr_SL(struct susp_sys_user_entry *sl, char *dest); #endif /*ECMA119_READ_RR_H_*/ /* vim: set noet ts=8 sts=8 sw=8 : */ #include #include #include #include #include "ecma119.h" #include "ecma119_tree.h" #include "tree.h" #include "util.h" #include "eltorito.h" static size_t calc_dirent_len(struct ecma119_tree_node *n) { int ret = n->iso_name ? strlen(n->iso_name) + 33 : 34; if (ret % 2) ret++; return ret; } /** * Replace the file permissions and user/group id of an ECMA-119 node. * This is used when a replace mode is selected, i.e., when we want to * create a disc where the mode of each file or directory will be * different than the mode in the original source. */ static void replace_node_mode(struct ecma119_write_target *t, struct stat *st) { if ( S_ISDIR(st->st_mode) ) { if ( t->replace_mode & 0x02 ) { /* replace dir mode with specific */ st->st_mode &= S_IFMT; st->st_mode |= t->dir_mode; } else if (t->replace_mode & 0x01) { /* replace dir mode with default */ /* read perm */ mode_t new_mode = (st->st_mode & S_IFMT) | 0444; /* search bit if any */ if ( st->st_mode & 0111) new_mode |= 0111; st->st_mode = new_mode; } } else { if ( t->replace_mode & 0x04 ) { /* replace file mode with specific */ st->st_mode &= S_IFMT; st->st_mode |= t->file_mode; } else if (t->replace_mode & 0x01) { /* replace file mode with default */ /* read perm */ mode_t new_mode = (st->st_mode & S_IFMT) | 0444; /* execute bit if any */ if ( st->st_mode & 0111) new_mode |= 0111; st->st_mode = new_mode; } } if ( t->replace_mode & 0x08 ) { /* replace gid mode with specific */ st->st_gid = t->gid; } else if (t->replace_mode & 0x01) { st->st_gid = 0; } if ( t->replace_mode & 0x10 ) { /* replace gid mode with specific */ st->st_uid = t->uid; } else if (t->replace_mode & 0x01) { st->st_uid = 0; } } /** * Creates a new ECMA-119 node from the given iso tree node, and initializes * the fields that are common to all kind of nodes (dir, reg file, symlink...). * * @param t * The options for the ECMA-119 tree that is being created * @param parent * The parent of the node, or NULL if it's the root. * @param iso * The node from which this function creates a ECMA-119 node * @return * The created node. */ static struct ecma119_tree_node* create_ecma119_node(struct ecma119_write_target *t, struct ecma119_tree_node *parent, struct iso_tree_node *iso) { struct ecma119_tree_node *ret; char *(*iso_name)(const char *, const char *) = ISO_ISDIR(iso) ? ((t->iso_level == 1) ? iso_1_dirid : iso_2_dirid) : ((t->iso_level == 1) ? iso_1_fileid : iso_2_fileid); char *(*iso_r_name)(const char *, const char *, int) = ISO_ISDIR(iso) ? iso_r_dirid : iso_r_fileid; assert(t && (!parent || parent->type == ECMA119_DIR) && iso ); ret = calloc(1, sizeof(struct ecma119_tree_node)); /* * If selected one ISO relaxed constraints other than NO_DIR_REALOCATION, * we use the function that computes the relaxed name, otherwise normal * function for specified level is used. */ ret->iso_name = iso->name ? ( t->relaxed_constraints & ~ECMA119_NO_DIR_REALOCATION ? iso_r_name(iso->name, t->input_charset, t->relaxed_constraints) : iso_name(iso->name, t->input_charset) ) : NULL; ret->dirent_len = calc_dirent_len(ret); /* iso node keeps the same file attribs as the original file. */ ret->attrib = iso->attrib; /* * When using RR extension and replace mode, we will replace the * permissions and uid/gid of each file with those previously selected * by the user. */ if ( t->rockridge && t->replace_mode ) replace_node_mode(t, &ret->attrib); if (!iso->name) ret->full_name = NULL; else if ( strcmp(t->input_charset,t->ouput_charset) ) /* convert the file name charset */ ret->full_name = convert_str(iso->name, t->input_charset, t->ouput_charset); else ret->full_name = strdup(iso->name); ret->target = t; ret->parent = parent; return ret; } /** * Create a new ECMA-119 node representing a directory from a iso directory * node. */ static struct ecma119_tree_node* create_dir(struct ecma119_write_target *t, struct ecma119_tree_node *parent, struct iso_tree_node_dir *iso) { struct ecma119_tree_node *ret; assert(t && (!parent || parent->type == ECMA119_DIR) && iso && S_ISDIR(iso->node.attrib.st_mode)); ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso); ret->type = ECMA119_DIR; ret->info.dir.real_parent = parent; ret->info.dir.depth = parent ? parent->info.dir.depth + 1 : 1; ret->info.dir.nchildren = 0; ret->info.dir.children = calloc(1, sizeof(void*) * iso->nchildren); return ret; } /** * Create a new ECMA-119 node representing a regular file from a iso file * node. */ static struct ecma119_tree_node* create_file(struct ecma119_write_target *t, struct ecma119_tree_node *parent, struct iso_tree_node_file *iso) { struct ecma119_tree_node *ret; struct iso_file *file; assert(t && iso && parent && parent->type == ECMA119_DIR); ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso); ret->type = ECMA119_FILE; /* get iso_file struct */ file = iso_file_table_lookup(t->file_table, iso); if ( file == NULL ) { /* * If the file is not already added to the disc, we add it now * to the file table, and get a new inode number for it. */ file = iso_file_new(t, iso); if (!file) { /* * That was an error. * TODO currently this cause the file to be ignored... Maybe * throw an error is a better alternative */ ecma119_tree_free(ret); return NULL; } iso_file_table_add_file(t->file_table, file); file->ino = ++t->ino; } else { /* increment number of hard-links */ file->nlink++; } ret->attrib.st_nlink = file->nlink; ret->attrib.st_ino = file->ino; ret->info.file = file; return ret; } /** * Create a new ECMA-119 node representing a placeholder for a relocated * dir. * * See IEEE P1282, section 4.1.5 for details */ static struct ecma119_tree_node* create_placeholder(struct ecma119_write_target *t, struct ecma119_tree_node *parent, struct ecma119_tree_node *real) { struct ecma119_tree_node *ret; assert(t && real && real->type == ECMA119_DIR && parent && parent->type == ECMA119_DIR); ret = calloc(1, sizeof(struct ecma119_tree_node)); ret->iso_name = real->iso_name; /* TODO strdup? */ /* FIXME * if we strdup above, if name changes in mangle_all, * this probably keeps as original. * if not, both change, but we need to update dirent_len. * I think that attributes of a placeholder must be taken from * real_me, not keept here. * FIXME * Another question is that real is a dir, while placeholder is * a file, and ISO name restricctions are different, what to do? */ ret->dirent_len = real->dirent_len; ret->attrib = real->attrib; ret->full_name = strdup(real->full_name); ret->target = t; ret->parent = parent; ret->type = ECMA119_PLACEHOLDER; ret->info.real_me = real; ret->attrib.st_nlink = 1; ret->attrib.st_ino = ++t->ino; return ret; } /** * Create a new ECMA-119 node representing a symbolic link from a iso symlink * node. */ static struct ecma119_tree_node* create_symlink(struct ecma119_write_target *t, struct ecma119_tree_node *parent, struct iso_tree_node_symlink *iso) { struct ecma119_tree_node *ret; assert(t && iso && parent && parent->type == ECMA119_DIR); ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso); ret->type = ECMA119_SYMLINK; ret->info.dest = iso->dest; /* TODO strdup? */ ret->attrib.st_nlink = 1; ret->attrib.st_ino = ++t->ino; return ret; } /** * Create a new ECMA-119 node representing a boot catalog or image. * This will be treated as a normal file when written the directory record, * but its contents are written in a different way. * * See "El Torito" Bootable CD-ROM Format Specification Version 1.0 for * more details. */ static struct ecma119_tree_node* create_boot(struct ecma119_write_target *t, struct ecma119_tree_node *parent, struct iso_tree_node_boot *iso) { struct ecma119_tree_node *ret; assert(t && iso && parent && parent->type == ECMA119_DIR); ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso); ret->type = ECMA119_BOOT; ret->info.boot_img = iso->img; ret->attrib.st_nlink = 1; ret->attrib.st_ino = ++t->ino; return ret; } /** * Create a new ECMA-119 node that corresponds to the given iso tree node. * If that node is a dir, this function recurses over all their children, * thus creating a ECMA-119 tree whose root is the given iso dir. */ static struct ecma119_tree_node* create_tree(struct ecma119_write_target *t, struct ecma119_tree_node *parent, struct iso_tree_node *iso) { struct ecma119_tree_node *ret = NULL; assert(t && iso); if ( iso->hide_flags & LIBISO_HIDE_ON_RR ) return NULL; switch ( iso->type ) { case LIBISO_NODE_FILE: ret = create_file(t, parent, (struct iso_tree_node_file*)iso); break; case LIBISO_NODE_SYMLINK: if ( !t->rockridge ) printf("Can't add symlinks to a non ISO tree. Skipping %s \n", iso->name); else ret = create_symlink(t, parent, (struct iso_tree_node_symlink*)iso); break; case LIBISO_NODE_DIR: { size_t i; struct iso_tree_node_dir *dir = (struct iso_tree_node_dir*)iso; ret = create_dir(t, parent, dir); for (i = 0; i < dir->nchildren; i++) { struct ecma119_tree_node *child; child = create_tree(t, ret, dir->children[i]); if (child) ret->info.dir.children[ret->info.dir.nchildren++] = child; } } break; case LIBISO_NODE_BOOT: ret = create_boot(t, parent, (struct iso_tree_node_boot*)iso); break; default: /* should never happen */ assert( 0 ); break; } return ret; } void ecma119_tree_free(struct ecma119_tree_node *root) { size_t i; if (root->type == ECMA119_DIR) { for (i=0; i < root->info.dir.nchildren; i++) { ecma119_tree_free(root->info.dir.children[i]); } free(root->info.dir.children); } free(root->iso_name); free(root->full_name); free(root); } static size_t max_child_name_len(struct ecma119_tree_node *root) { size_t ret = 0, i; assert(root->type == ECMA119_DIR); for (i=0; i < root->info.dir.nchildren; i++) { size_t len = strlen(root->info.dir.children[i]->iso_name); ret = MAX(ret, len); } return ret; } /** * Relocates a directory, as specified in Rock Ridge Specification * (see IEEE P1282, section 4.1.5). This is needed when the number of levels * on a directory hierarchy exceeds 8, or the length of a path is higher * than 255 characters, as specified in ECMA-119, section 6.8.2.1 */ static void reparent(struct ecma119_tree_node *child, struct ecma119_tree_node *parent) { int found = 0; size_t i; struct ecma119_tree_node *placeholder; assert(child && parent && parent->type == ECMA119_DIR && child->parent); /* replace the child in the original parent with a placeholder */ for (i=0; i < child->parent->info.dir.nchildren; i++) { if (child->parent->info.dir.children[i] == child) { placeholder = create_placeholder(child->target, child->parent, child); child->parent->info.dir.children[i] = placeholder; found = 1; break; } } assert(found); /* add the child to its new parent */ child->parent = parent; parent->info.dir.nchildren++; parent->info.dir.children = realloc( parent->info.dir.children, sizeof(void*) * parent->info.dir.nchildren ); parent->info.dir.children[parent->info.dir.nchildren-1] = child; } /** * Reorder the tree, if necessary, to ensure that * - the depth is at most 8 * - each path length is at most 255 characters * This restriction is imposed by ECMA-119 specification (see ECMA-119, * 6.8.2.1). */ static void reorder_tree(struct ecma119_write_target *t, struct ecma119_tree_node *root, struct ecma119_tree_node *cur) { size_t max_path; assert(root && cur && cur->type == ECMA119_DIR); cur->info.dir.depth = cur->parent ? cur->parent->info.dir.depth + 1 : 1; cur->info.dir.path_len = cur->parent ? cur->parent->info.dir.path_len + strlen(cur->iso_name) : 0; max_path = cur->info.dir.path_len + cur->info.dir.depth + max_child_name_len(cur); if (cur->info.dir.depth > 8 || max_path > 255) { if (t->rockridge) { reparent(cur, root); /* we are appended to the root's children now, so there is no * need to recurse (the root will hit us again) */ } else { /* we need to delete cur */ size_t i,j; struct ecma119_tree_node *parent = cur->parent; printf("Can't dirs deeper than 8 without RR. Skipping %s\n", cur->full_name); for (i=0; i < parent->info.dir.nchildren; ++i) { if (parent->info.dir.children[i] == cur) { break; } } assert ( i < parent->info.dir.nchildren); for ( j = i; j < parent->info.dir.nchildren - 1; ++j) parent->info.dir.children[j] = parent->info.dir.children[j+1]; parent->info.dir.nchildren--; ecma119_tree_free(cur); } } else { size_t i; for (i=0; i < cur->info.dir.nchildren; i++) { if (cur->info.dir.children[i]->type == ECMA119_DIR) reorder_tree(t, root, cur->info.dir.children[i]); } } } /** * Compare the iso name of two ECMA-119 nodes */ static int cmp_node(const void *f1, const void *f2) { struct ecma119_tree_node *f = *((struct ecma119_tree_node**)f1); struct ecma119_tree_node *g = *((struct ecma119_tree_node**)f2); return strcmp(f->iso_name, g->iso_name); } /** * Sorts a the children of each directory in the ECMA-119 tree represented * by \p root, acording to the order specified in ECMA-119, section 9.3. */ static void sort_tree(struct ecma119_tree_node *root) { size_t i; assert(root && root->type == ECMA119_DIR); qsort(root->info.dir.children, root->info.dir.nchildren, sizeof(void*), cmp_node); for (i=0; i < root->info.dir.nchildren; i++) { if (root->info.dir.children[i]->type == ECMA119_DIR) sort_tree(root->info.dir.children[i]); } } /** * Change num_change characters of the given filename in order to ensure the * name is unique. If the name is short enough (depending on the ISO level), * we can append the characters instead of changing them. * * \p seq_num is the index of this file in the sequence of identical filenames. * * For example, seq_num=3, num_change=2, name="HELLOTHERE.TXT" changes name to * "HELLOTHE03.TXT" */ static void mangle_name(char **name, int num_change, int level, int relaxed, int seq_num) { char *dot = strrchr(*name, '.'); char *semi = strrchr(*name, ';'); size_t len = strlen(*name); char base[len+1], ext[len+1]; char fmt[12]; size_t baselen, extlen; if (num_change >= len) { return; } strncpy(base, *name, len+1); if (relaxed & ECMA119_RELAXED_FILENAMES) { /* relaxed filenames, don't care about extension */ int maxlen = (relaxed & (1<<1)) ? 37 : 31; base[maxlen - num_change] = '\0'; baselen = strlen(base); if (relaxed & ECMA119_OMIT_VERSION_NUMBERS) { sprintf(fmt, "%%s%%0%1dd", num_change); *name = realloc(*name, baselen + num_change + 1); } else { sprintf(fmt, "%%s%%0%1dd;1", num_change); *name = realloc(*name, baselen + num_change + 3); } sprintf(*name, fmt, base, seq_num); return; } if (dot) { base[dot - *name] = '\0'; strncpy(ext, dot+1, len+1); if (semi) { ext[semi - dot - 1] = '\0'; } } else { base[len-2] = '\0'; ext[0] = '\0'; } baselen = strlen(base); extlen = strlen(ext); if (relaxed & (1<<1)) { /* 37 char filenames */ base[36 - extlen - num_change] = '\0'; } else if (level == 1 && baselen + num_change > 8) { base[8 - num_change] = '\0'; } else if (level != 1 && baselen + extlen + num_change > 30) { base[30 - extlen - num_change] = '\0'; } if (relaxed & ECMA119_OMIT_VERSION_NUMBERS) { sprintf(fmt, "%%s%%0%1dd.%%s", num_change); *name = realloc(*name, baselen + extlen + num_change + 1); } else { sprintf(fmt, "%%s%%0%1dd.%%s;1", num_change); *name = realloc(*name, baselen + extlen + num_change + 4); } sprintf(*name, fmt, base, seq_num, ext); } /** * Ensures that the ISO name of each children of the given dir is unique, * changing some of them if needed. */ static void mangle_all(struct ecma119_tree_node *dir) { size_t i, j, k; struct ecma119_dir_info d = dir->info.dir; size_t n_change; int changed; size_t digits; assert(dir->type == ECMA119_DIR); digits = 1; do { changed = 0; for (i=0; i < d.nchildren; i++) { /* find the number of consecutive equal names */ j = 1; while ( i+j < d.nchildren && !strcmp(d.children[i]->iso_name, d.children[i+j]->iso_name) ) j++; if (j == 1) continue; /* mangle the names */ changed = 1; n_change = j / 10 + digits; for (k=0; k < j; k++) { mangle_name(&(d.children[i+k]->iso_name), n_change, dir->target->iso_level, dir->target->relaxed_constraints, k); d.children[i+k]->dirent_len = calc_dirent_len(d.children[i+k]); } /* skip ahead by the number of mangled names */ i += j - 1; } if (changed) { /* we need to reorder */ qsort(dir->info.dir.children, dir->info.dir.nchildren, sizeof(void*), cmp_node); } digits++; } while (changed); for (i=0; i < d.nchildren; i++) { if (d.children[i]->type == ECMA119_DIR) mangle_all(d.children[i]); } } struct ecma119_tree_node* ecma119_tree_create(struct ecma119_write_target *t, struct iso_tree_node *iso_root) { t->root = create_tree(t, NULL, iso_root); if ( !(t->relaxed_constraints & ECMA119_NO_DIR_REALOCATION) ) reorder_tree(t, t->root, t->root); sort_tree(t->root); mangle_all(t->root); return t->root; } void ecma119_tree_print(struct ecma119_tree_node *root, int spaces) { size_t i; char sp[spaces+1]; memset(sp, ' ', spaces); sp[spaces] = '\0'; printf("%s%s\n", sp, root->iso_name); if (root->type == ECMA119_DIR) for (i=0; i < root->info.dir.nchildren; i++) ecma119_tree_print(root->info.dir.children[i], spaces+2); } /* vim: set noet ts=8 sts=8 sw=8 : */ /** * \file ecma119_tree.h * * Declarations for creating, modifying and printing filesystem trees that * are compatible with ecma119. */ #ifndef LIBISO_ECMA119_TREE_H #define LIBISO_ECMA119_TREE_H #include #include "file.h" struct ecma119_write_target; struct iso_tree_node; enum ecma119_node_type { ECMA119_FILE, ECMA119_SYMLINK, ECMA119_DIR, ECMA119_PLACEHOLDER, /**< placeholder for a relocated dir. */ ECMA119_BOOT }; struct ecma119_dir_info { struct susp_info self_susp; /**< susp entries for "." */ struct susp_info parent_susp; /**< susp entries for ".." */ size_t len; /**< sum of the lengths of children's * Directory Records (including SU) */ size_t CE_len; /**< sum of the lengths of children's * SUSP CE areas */ size_t block; int depth; size_t path_len; /**< The length of a path up to, and * including, this directory. This * cannot exceed 255. */ size_t nchildren; struct ecma119_tree_node **children; struct ecma119_tree_node *real_parent; /**< The parent before relocation */ }; /** * A node for a tree containing all the information necessary for writing * an ISO9660 volume. */ struct ecma119_tree_node { char *iso_name; /**< in ASCII, conforming to the * current ISO level. */ char *full_name; /**< full name, in current locale */ size_t dirent_len; /**< Length of the directory record, * not including SU. */ struct ecma119_tree_node *parent; struct ecma119_write_target *target; struct stat attrib; struct susp_info susp; enum ecma119_node_type type; /**< file, symlink, directory or placeholder */ union { struct iso_file *file; char *dest; struct ecma119_dir_info dir; struct ecma119_tree_node *real_me; /**< this field points to * the relocated directory. */ unsigned int boot_img:1; /** For boot nodes, it identifies if this * corresponds to image(1) or catalog(0). * The block is stored in ecma119_write_target */ } info; }; /** * Create a new ecma119_tree that corresponds to the tree represented by * \p iso_root. */ struct ecma119_tree_node* ecma119_tree_create(struct ecma119_write_target *target, struct iso_tree_node *iso_root); /** * Free an ecma119 tree. */ void ecma119_tree_free(struct ecma119_tree_node *root); /** * Print an ecma119 tree. */ void ecma119_tree_print(struct ecma119_tree_node *root, int spaces); #endif /* LIBISO_ECMA119_TREE_H */ #include "libisofs.h" #include "eltorito.h" #include "volume.h" #include "util.h" #include "messages.h" #include #include #include #include #include #include #include #include #include /** * This table should be written with accuracy values at offset * 8 of boot image, when used ISOLINUX boot loader */ struct boot_info_table { uint8_t bi_pvd BP(1, 4); /* LBA of primary volume descriptor */ uint8_t bi_file BP(5, 8); /* LBA of boot file */ uint8_t bi_length BP(9, 12); /* Length of boot file */ uint8_t bi_csum BP(13, 16); /* Checksum of boot file */ uint8_t bi_reserved BP(17, 56); /* Reserved */ }; struct partition_desc { uint8_t boot_ind; uint8_t begin_chs[3]; uint8_t type; uint8_t end_chs[3]; uint8_t start[4]; uint8_t size[4]; }; struct hard_disc_mbr { uint8_t code_area[440]; uint8_t opt_disk_sg[4]; uint8_t pad[2]; struct partition_desc partition[4]; uint8_t sign1; uint8_t sign2; }; static void el_torito_image_free(struct el_torito_boot_image *image) { assert(image); /* unref image node */ iso_tree_free((struct iso_tree_node*)image->node); free(image); } static struct iso_tree_node_boot* create_boot_image_node(struct iso_tree_node_file *image) { struct iso_tree_node_boot *boot; struct iso_tree_node_dir *parent; assert(image); boot = calloc(1, sizeof(struct iso_tree_node_boot)); boot->node.attrib = image->node.attrib; boot->node.type = LIBISO_NODE_BOOT; boot->node.name = strdup(image->node.name); boot->node.hide_flags = image->node.hide_flags; boot->node.refcount = 2; /* mine and tree */ boot->loc.path = strdup(image->loc.path); boot->img = 1; /* it refers to image */ /* replace iso_tree_node_file with the image */ parent = image->node.parent; iso_tree_node_remove(parent, (struct iso_tree_node*)image); iso_tree_add_child(parent, (struct iso_tree_node*) boot); return boot; } static struct el_torito_boot_image * create_image(const char *path, enum eltorito_boot_media_type type) { struct stat attrib; struct el_torito_boot_image *boot; int boot_media_type = 0; int load_sectors = 0; /* number of sector to load */ unsigned char partition_type = 0; if (stat(path, &attrib)) { iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Can't access file."); libisofs_errno = NO_FILE; return NULL; } if ( !S_ISREG(attrib.st_mode) ) { iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "We can only create boot images from regular files"); libisofs_errno = ELTORITO_WRONG_IMAGE; return NULL; } switch (type) { case ELTORITO_FLOPPY_EMUL: switch (attrib.st_size) { case 1200 * 1024: boot_media_type = 1; /* 1.2 meg diskette */ break; case 1440 * 1024: boot_media_type = 2; /* 1.44 meg diskette */ break; case 2880 * 1024: boot_media_type = 3; /* 2.88 meg diskette */ break; default: { char msg[72]; sprintf(msg, "Invalid image size %d Kb. Must be one of 1.2, 1.44" "or 2.88 Mb", (int) attrib.st_size / 1024); iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, msg); libisofs_errno = ELTORITO_WRONG_IMAGE_SIZE; return NULL; } break; } /* it seems that for floppy emulation we need to load * a single sector (512b) */ load_sectors = 1; break; case ELTORITO_HARD_DISC_EMUL: { size_t i; int fd; struct hard_disc_mbr mbr; int used_partition; /* read the MBR on disc and get the type of the partition */ fd = open(path, O_RDONLY); if ( fd == -1 ) { iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Can't open image file."); libisofs_errno = NO_READ_ACCESS; return NULL; } if ( read(fd, &mbr, sizeof(mbr)) ) { iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Can't read MBR from image file."); libisofs_errno = ELTORITO_WRONG_IMAGE; close(fd); return NULL; } close(fd); /* check valid MBR signature */ if ( mbr.sign1 != 0x55 || mbr.sign2 != 0xAA ) { iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Invalid MBR. Wrong signature."); libisofs_errno = ELTORITO_WRONG_IMAGE; return NULL; } /* ensure single partition */ used_partition = -1; for (i = 0; i < 4; ++i) { if (mbr.partition[i].type != 0) { /* it's an used partition */ if (used_partition != -1) { char msg[72]; sprintf(msg, "Invalid MBR. At least 2 partitions: %d and " "%d, are being used\n", used_partition, i); iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, msg); libisofs_errno = ELTORITO_WRONG_IMAGE; return NULL; } else used_partition = i; } } partition_type = mbr.partition[used_partition].type; } boot_media_type = 4; /* only load the MBR */ load_sectors = 1; break; case ELTORITO_NO_EMUL: boot_media_type = 0; break; } boot = calloc(1, sizeof(struct el_torito_boot_image)); boot->bootable = 1; boot->type = boot_media_type; boot->load_size = load_sectors; boot->partition_type = partition_type; return boot; } /* parent and name can be null */ static struct iso_tree_node_boot* create_boot_catalog_node(struct iso_tree_node_dir *parent, const char *name) { struct iso_tree_node_boot *boot; assert( (parent && name) || (!parent && !name) ); boot = calloc(1, sizeof(struct iso_tree_node_boot)); boot->node.attrib.st_mode = S_IFREG | 0444; boot->node.attrib.st_atime = boot->node.attrib.st_mtime = boot->node.attrib.st_ctime = time(NULL); boot->node.attrib.st_size = 2048; boot->node.type = LIBISO_NODE_BOOT; boot->node.name = name ? strdup(name) : NULL; boot->node.refcount = 1; /* mine */ if (parent) { boot->node.refcount++; /* add tree ref */ iso_tree_add_child(parent, (struct iso_tree_node*) boot); } return boot; } struct el_torito_boot_image* iso_volume_set_boot_image(struct iso_volume *volume, struct iso_tree_node *image, enum eltorito_boot_media_type type, struct iso_tree_node_dir *dir, char *name) { struct el_torito_boot_image *boot_image; struct iso_tree_node_boot *cat_node; struct el_torito_boot_catalog *catalog; struct iso_tree_node_file *imgfile; assert(volume && !volume->bootcat); assert(image && ISO_ISREG(image)); assert(dir && name); imgfile = (struct iso_tree_node_file*)image; if (image->procedence == LIBISO_PREVIMG) { /* FIXME mmm, what to do here? */ iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Can't use prev. image files " "as boot images"); return NULL; } boot_image = create_image(imgfile->loc.path, type); if ( !boot_image ) return NULL; /* create image node */ boot_image->node = create_boot_image_node(imgfile); /* creates the catalog with the given default image */ catalog = malloc(sizeof(struct el_torito_boot_catalog)); if (!catalog) { el_torito_image_free(boot_image); return NULL; } catalog->image = boot_image; /* add catalog file */ cat_node = create_boot_catalog_node(dir, name); catalog->node = cat_node; volume->bootcat = catalog; return boot_image; } struct el_torito_boot_image* iso_volume_set_boot_image_hidden(struct iso_volume *volume, const char* path, enum eltorito_boot_media_type type) { struct el_torito_boot_image *boot_image; struct iso_tree_node_boot *cat_node; struct el_torito_boot_catalog *catalog; assert(volume && !volume->bootcat); assert(path); boot_image = create_image(path, type); if ( !boot_image ) return NULL; /* create image node */ { struct iso_tree_node_boot *boot; boot = calloc(1, sizeof(struct iso_tree_node_boot)); /* we assume the stat won't fail, as we already stat the same file above */ stat(path, &boot->node.attrib); boot->node.type = LIBISO_NODE_BOOT; boot->node.refcount = 1; /* mine */ boot->loc.path = strdup(path); boot->img = 1; /* it refers to image */ boot_image->node = boot; } /* creates the catalog with the given default image */ catalog = malloc(sizeof(struct el_torito_boot_catalog)); if (!catalog) { el_torito_image_free(boot_image); return NULL; } catalog->image = boot_image; /* add catalog file */ cat_node = create_boot_catalog_node(NULL, NULL); catalog->node = cat_node; volume->bootcat = catalog; return boot_image; } struct el_torito_boot_image* iso_volume_get_boot_image(struct iso_volume *volume, struct iso_tree_node **imgnode, struct iso_tree_node **catnode) { struct el_torito_boot_catalog *catalog; assert(volume); catalog = volume->bootcat; if (!catalog) return NULL; if (imgnode) *imgnode = (struct iso_tree_node*)catalog->image->node; if (catnode) *catnode = (struct iso_tree_node*)catalog->node; return catalog->image; } void iso_volume_remove_boot_image(struct iso_volume *volume) { struct el_torito_boot_catalog *catalog; struct iso_tree_node *catnode; struct iso_tree_node *imgnode; assert(volume); catalog = volume->bootcat; if (!catalog) return; /* remove node from tree */ catnode = (struct iso_tree_node*)catalog->node; if (catnode->parent) iso_tree_node_remove(catnode->parent, catnode); /* remove image from tree */ imgnode = (struct iso_tree_node*)catalog->image->node; if (imgnode->parent) iso_tree_node_remove(imgnode->parent, imgnode); /* remove catalog */ el_torito_boot_catalog_free(catalog); volume->bootcat = NULL; } void el_torito_set_load_seg(struct el_torito_boot_image *bootimg, int segment) { if (bootimg->type != ELTORITO_NO_EMUL) return; bootimg->load_seg = segment; } void el_torito_set_load_size(struct el_torito_boot_image *bootimg, int sectors) { if (bootimg->type != ELTORITO_NO_EMUL) return; bootimg->load_size = sectors; } void el_torito_set_no_bootable(struct el_torito_boot_image *bootimg) { bootimg->bootable = 0; } void el_torito_patch_isolinux_image(struct el_torito_boot_image *bootimg) { bootimg->isolinux = 1; } void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat) { assert(cat); el_torito_image_free(cat->image); iso_tree_free((struct iso_tree_node*)cat->node); free(cat); } /** * Write the Boot Record Volume Descriptor */ void el_torito_write_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf) { struct el_torito_boot_catalog *cat = t->catalog; struct ecma119_boot_rec_vol_desc *vol = (struct ecma119_boot_rec_vol_desc*)buf; assert(cat); vol->vol_desc_type[0] = 0; memcpy(vol->std_identifier, "CD001", 5); vol->vol_desc_version[0] = 1; memcpy(vol->boot_sys_id, "EL TORITO SPECIFICATION", 23); iso_lsb(vol->boot_catalog, t->catblock, 4); } static void write_validation_entry(struct ecma119_write_target *t, uint8_t *buf) { size_t i; int checksum; struct el_torito_validation_entry *ve = (struct el_torito_validation_entry*)buf; ve->header_id[0] = 1; ve->platform_id[0] = 0; /* 0: 80x86, 1: PowerPC, 2: Mac */ ve->key_byte1[0] = 0x55; ve->key_byte2[0] = 0xAA; /* calculate the checksum, to ensure sum of all words is 0 */ checksum = 0; for (i = 0; i < sizeof(struct el_torito_validation_entry); i += 2) { checksum -= buf[i]; checksum -= (buf[i] << 8); } iso_lsb(ve->checksum, checksum, 2); } static void patch_boot_image(uint8_t *buf, struct ecma119_write_target *t, ssize_t imgsize) { struct boot_info_table *info; uint32_t checksum; ssize_t offset; memset(&info, 0, sizeof(info)); /* compute checksum, as the the sum of all 32 bit words in boot image * from offset 64 */ checksum = 0; offset = (ssize_t) 64; while (offset < imgsize) { checksum += iso_read_lsb(buf + offset, 4); offset += 4; } if (offset != imgsize) { /* file length not multiple of 4 */ iso_msg_warn(LIBISO_ISOLINUX_CANT_PATCH, "Unexpected isolinux image length. Patch might not work."); } /* patch boot info table */ info = (struct boot_info_table*)(buf + 8); memset(info, 0, sizeof(struct boot_info_table)); iso_lsb(info->bi_pvd, t->ms_block + 16, 4); iso_lsb(info->bi_file, t->imgblock, 4); iso_lsb(info->bi_length, imgsize, 4); iso_lsb(info->bi_csum, checksum, 4); } static void write_boot_image(uint8_t *buf, struct ecma119_write_target *t) { ssize_t imgsize; struct el_torito_boot_image *image; image = t->catalog->image; imgsize= image->node->node.attrib.st_size; /* copy image contents to memory buffer */ if (image->node->node.procedence == LIBISO_PREVIMG) { int block, nblocks; uint8_t *memloc; assert(t->src); block = 0; memloc = buf; nblocks = imgsize ? div_up(imgsize, 2048) : 1; while (block < nblocks) { if (t->src->read_block(t->src, image->node->loc.block + block, memloc)) { iso_msg_sorry(LIBISO_CANT_READ_IMG, "Unable to read boot image from previous image."); break; /* exit loop */ } memloc += 2048; ++block; } } else { int fd; ssize_t nread, tread; uint8_t *memloc; const char *path = image->node->loc.path; memloc = buf; fd = open(path, O_RDONLY); if (fd == -1) { iso_msg_sorry(LIBISO_CANT_READ_FILE, "Can't open boot image file for reading. Skipping"); return; } tread = 0; while (tread < imgsize) { nread = read(fd, memloc, t->block_size); if (nread <= 0) { iso_msg_sorry(LIBISO_CANT_READ_FILE, "Problem reading boot image file. Skipping"); break; /* exit loop */ } tread += nread; memloc += nread; } close(fd); } if (image->isolinux) { /* we need to patch the image */ patch_boot_image(buf, t, imgsize); } } /** * Write one section entry. * Currently this is used only for default image (the only supported just now) */ static void write_section_entry(uint8_t *buf, struct ecma119_write_target *t) { struct el_torito_boot_image *img; struct el_torito_section_entry *se = (struct el_torito_section_entry*)buf; img = t->catalog->image; assert(img); se->boot_indicator[0] = img->bootable ? 0x88 : 0x00; se->boot_media_type[0] = img->type; iso_lsb(se->load_seg, img->load_seg, 2); se->system_type[0] = img->partition_type; iso_lsb(se->sec_count, img->load_size, 2); iso_lsb(se->block, t->imgblock, 4); } /** * Write El-Torito Boot Catalog plus image */ static void write_catalog(struct ecma119_write_target *t, uint8_t *buf) { struct el_torito_boot_catalog *cat = t->catalog; assert(cat); assert(cat->image); write_validation_entry(t, buf); /* write default entry */ write_section_entry(buf + 32, t); write_boot_image(buf + 2048, t); } void el_torito_wr_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf) { assert(t->catalog); ecma119_start_chunking(t, el_torito_write_boot_vol_desc, 2048, buf); } void el_torito_wr_catalog(struct ecma119_write_target *t, uint8_t *buf) { off_t size; off_t imgsize; assert(t->catalog); /* * Note that when this is called, we need to write el-torito info to * image. Note, however, that the image could come from a previous session * and maybe we don't know its size */ imgsize = t->catalog->image->node->node.attrib.st_size; if (imgsize == 0) { iso_msg_warn(LIBISO_EL_TORITO_BLIND_COPY, "Can get boot image size, only one block will be copied"); imgsize = 2048; } size = 2048 + div_up(imgsize, t->block_size); ecma119_start_chunking(t, write_catalog, size, buf); } #ifndef ELTORITO_H_ #define ELTORITO_H_ #include "tree.h" #include "file.h" #include "ecma119.h" struct el_torito_boot_catalog { struct iso_tree_node_boot *node; /* node of the catalog */ struct el_torito_boot_image *image; /* default boot image */ enum tree_node_from proc; /* whether the catalog is new or read from a * prev session/image */ }; struct el_torito_boot_image { struct iso_tree_node_boot *node; unsigned int bootable:1; /**< If the entry is bootable. */ unsigned int isolinux:1; /**< If the image will be patched */ unsigned char type; /**< The type of image */ unsigned char partition_type; /**< type of partition for HD-emul images */ short load_seg; /**< Load segment for the initial boot image. */ short load_size; /**< Number of sector to load. */ }; struct el_torito_validation_entry { uint8_t header_id BP(1, 1); uint8_t platform_id BP(2, 2); uint8_t reserved BP(3, 4); uint8_t id_string BP(5, 28); uint8_t checksum BP(29, 30); uint8_t key_byte1 BP(31, 31); uint8_t key_byte2 BP(32, 32); }; struct el_torito_default_entry { uint8_t boot_indicator BP(1, 1); uint8_t boot_media_type BP(2, 2); uint8_t load_seg BP(3, 4); uint8_t system_type BP(5, 5); uint8_t unused1 BP(6, 6); uint8_t sec_count BP(7, 8); uint8_t block BP(9, 12); uint8_t unused2 BP(13, 32); }; struct el_torito_section_header_entry { uint8_t header_indicator BP(1, 1); uint8_t platform_id BP(2, 2); uint8_t number BP(3, 4); uint8_t character BP(5, 32); }; struct el_torito_section_entry { uint8_t boot_indicator BP(1, 1); uint8_t boot_media_type BP(2, 2); uint8_t load_seg BP(3, 4); uint8_t system_type BP(5, 5); uint8_t unused1 BP(6, 6); uint8_t sec_count BP(7, 8); uint8_t block BP(9, 12); uint8_t selec_criteria BP(13, 13); uint8_t vendor_sc BP(14, 32); }; void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat); /** * Write the Boot Record Volume Descriptor */ void el_torito_write_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf); void el_torito_wr_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf); /** * Write catalog + image */ void el_torito_wr_catalog(struct ecma119_write_target *t, uint8_t *buf); #endif /*ELTORITO_H_*/ #include "exclude.h" void iso_exclude_add_path(struct iso_hash_table *table, const char *path) { if (!path) return; table->num += iso_hash_insert(table->table, path); } void iso_exclude_empty(struct iso_hash_table *table) { if (!table->num) return; iso_hash_empty(table->table); table->num=0; } int iso_exclude_lookup(struct iso_hash_table *table, const char *path) { if (!table->num || !path) return 0; return iso_hash_lookup(table->table, path); } #ifndef ISO_EXCLUDE_H #define ISO_EXCLUDE_H #include "hash.h" struct iso_hash_table { struct iso_hash_node *table[HASH_NODES]; int num; }; /** * Add a path to ignore when adding a directory recursively. * * \param path The path, on the local filesystem, of the file. */ int iso_exclude_lookup(struct iso_hash_table *table, const char *path); /** * Add the path of a file or directory to ignore when adding a directory recursively. * * \param path The path, on the local filesystem, of the file. */ void iso_exclude_add_path(struct iso_hash_table *table, const char *path); /** * Remove all paths that were set to be ignored when adding a directory recusively. */ void iso_exclude_empty(struct iso_hash_table *table); #endif /* ISO_EXCLUDE */ #include #include #include #include #include #include #include #include #include "file.h" #include "tree.h" #include "file_src.h" #include "messages.h" #include "ecma119.h" //TODO: refactor both hash and this hash table into a single one?? struct iso_file * iso_file_new(struct ecma119_write_target *t, struct iso_tree_node_file *f) { struct iso_file *file = calloc(1, sizeof(struct iso_file)); if (!file) return NULL; if (f->node.procedence == LIBISO_NEW) { file->path = f->loc.path; /*TODO strdup? it needs to be free on clear then */ file->real_dev = f->node.attrib.st_dev; file->real_ino = f->node.attrib.st_ino; file->src = iso_file_src_from_path(file->path); } else { file->block = f->loc.block; file->prev_img = 1; file->real_dev = 0; /* we use 0 as dev for prev. session files */ /* don't take care about inode number read from RR TX, block * number is good enouht for this. Moreover, when we are modifying * an image, we will modify file->block with the block where the * file needs to be written in the new image. So, we store the block * in original image here */ file->real_ino = f->loc.block; file->src = iso_file_src_from_prev_img(t->src, f->loc.block, f->node.attrib.st_size); } if (!file->src) goto creation_error; file->size = file->src->get_size(file->src); if (file->size != f->node.attrib.st_size) { char msg[PATH_MAX + 32]; /* can only happen on path files */ assert(f->node.procedence == LIBISO_NEW); sprintf(msg, "Size of file %s has changed\n", file->path); iso_msg_sorry(LIBISO_CANT_READ_FILE, msg); } file->nlink = 1; file->sort_weight = f->sort_weight; return file; creation_error:; free(file); return NULL; } static unsigned int iso_file_table_hash(const char *path) { unsigned int hash_num=0; const char *c; c=path; while(*c) hash_num = (hash_num << 15) + (hash_num << 3) + (hash_num >> 3) + *c++; return hash_num % FILE_HASH_NODES; } static inline unsigned int iso_file_table_hash_inode(dev_t dev, ino_t ino) { return (dev ^ ino) % FILE_HASH_NODES; } struct iso_file_table* iso_file_table_new(int cache_inodes) { struct iso_file_table *table = calloc(1, sizeof(struct iso_file_table)); table->cache_inodes = cache_inodes; return table; } static struct iso_file_hash_node * iso_file_table_node_new(struct iso_file *file) { struct iso_file_hash_node *node; node = calloc(1, sizeof(struct iso_file_hash_node) ); node->file = file; return node; } static void iso_file_table_node_free(struct iso_file_hash_node *node) { iso_file_src_free(node->file->src); free(node->file); free(node); } void iso_file_table_clear(struct iso_file_table *ft) { int i; for (i=0; i < FILE_HASH_NODES; i++) { struct iso_file_hash_node *node; node=ft->table[i]; if (!node) continue; ft->table[i] = NULL; do { struct iso_file_hash_node *next; next = node->next; iso_file_table_node_free(node); node = next; } while (node); } ft->count = 0; } /** * return 0 if equal, != 0 if not */ static int iso_table_compare_files(struct iso_file_table *ft, struct iso_file *f1, struct iso_file *f2) { assert(ft && f1 && f2); if (f1->prev_img || f2->prev_img) { if (f1->prev_img && f2->prev_img) return f1->real_ino != f2->real_ino; else return 1; } if (ft->cache_inodes) { return (f1->real_dev != f2->real_dev) || (f1->real_ino != f2->real_ino); } else { return strcmp(f1->path, f2->path); } } int iso_file_table_add_file(struct iso_file_table *ft, struct iso_file *f) { struct iso_file_hash_node *node; unsigned int hash_num; assert(ft && f); /* find the hash number */ if (f->prev_img) hash_num = f->real_ino % FILE_HASH_NODES; else if (ft->cache_inodes) hash_num = iso_file_table_hash_inode(f->real_dev, f->real_ino); else hash_num = iso_file_table_hash(f->path); /* insert it */ node = ft->table[hash_num]; /* unfortunately, we can't safely consider that a file * won't be twice in the hash table so make sure it * doesn't already exists */ if (!node) { ft->table[hash_num]=iso_file_table_node_new(f); ft->count++; return 1; } /* if it's already in, we don't do anything */ if (!iso_table_compare_files(ft, f, node->file)) return 0; while (node->next) { node = node->next; /* if it's already in, we don't do anything */ if (!iso_table_compare_files(ft, f, node->file)) return 0; } node->next = iso_file_table_node_new(f); ft->count++; return 1; } /** 0 on equal, != 0 otherwise */ static int iso_table_compare_node_file(struct iso_file_table *ft, struct iso_tree_node_file *f1, struct iso_file *f2) { assert(ft && f1 && f2); if (f1->node.procedence || f2->prev_img) { if (f1->node.procedence && f2->prev_img) return f1->loc.block != f2->real_ino; else return 1; } if (ft->cache_inodes) { return (f1->node.attrib.st_dev != f2->real_dev) || (f1->node.attrib.st_ino != f2->real_ino); } else { return strcmp(f1->loc.path, f2->path); } } struct iso_file * iso_file_table_lookup(struct iso_file_table *ft, struct iso_tree_node_file *f) { struct iso_file_hash_node *node; unsigned int hash_num; int equal; assert(ft && f); /* find the hash number */ if (f->node.procedence == LIBISO_PREVIMG) hash_num = f->loc.block % FILE_HASH_NODES; else if ( ft->cache_inodes ) hash_num = iso_file_table_hash_inode(f->node.attrib.st_dev, f->node.attrib.st_ino); else hash_num = iso_file_table_hash(f->loc.path); node = ft->table[hash_num]; if (!node) return NULL; equal = !iso_table_compare_node_file(ft, f, node->file); if (equal) return node->file; while (node->next) { node = node->next; equal = !iso_table_compare_node_file(ft, f, node->file); if (equal) return node->file; } return NULL; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* vim: set noet ts=8 sts=8 sw=8 : */ /** * \file file.h * * Declare the structs to keep track of the files to be written into image. * These files are stored in a hash table. Two modes of operation are supported: * when cache inodes is enabled, the files are indexed into the table by the * device and inode id in the local filesystem. This way, two different files * with same inode and device id are treated as if they were a single file. * This is usually the correct behavior, as a different file with same inode * and device used to be a hard link. * When cache inode is disabled, indexing is done by path on local filesystem. */ #ifndef FILE_H_ #define FILE_H_ #include #define FILE_HASH_NODES 2048 struct iso_file { unsigned int prev_img:1; /**< if the file comes from a previous image */ char *path; /**< Path of the file on local filesystem */ off_t size; /**< size of this file */ ino_t ino; /**< This will be the inode number on CD of the file (RR) */ nlink_t nlink; /**< Number of hard links of the file on CD (RR) */ uint32_t block; /**< Block where this file is to be written on image */ dev_t real_dev; ino_t real_ino; /**< for lookup by inode caching */ int sort_weight; struct iso_file_src *src; }; struct iso_file_hash_node { struct iso_file_hash_node *next; struct iso_file *file; }; struct iso_file_table { struct iso_file_hash_node *table[FILE_HASH_NODES]; size_t count; int cache_inodes; /**< 1 to index by inode number */ }; struct iso_tree_node_file; struct ecma119_write_target; /** * Create a struct that represents the specified iso_tree_node_file, * suitable to be stored into the table, */ struct iso_file *iso_file_new(struct ecma119_write_target *t, struct iso_tree_node_file*); struct iso_file_table *iso_file_table_new(int cache_inodes); /** * Clear a hash table. All iso_file structs stored will also be freed, * but not the path of each iso_file */ void iso_file_table_clear(struct iso_file_table *ft); /** * Add a new file to the table. * \return 1 if the file is added, 0 if the file already exist on table */ int iso_file_table_add_file(struct iso_file_table *ft, struct iso_file *f); struct iso_file *iso_file_table_lookup(struct iso_file_table *ft, struct iso_tree_node_file *f); #endif /*FILE_H_*/ /* * Implementation of iso_file_src for: * * a) read from local filesystem files * b) read from previous session / image files */ #include #include #include #include #include #include #include #include #include #include "file_src.h" #include "messages.h" #include "libisofs.h" #include "util.h" #define BLOCK_SIZE 2048 void iso_file_src_free(struct iso_file_src *src) { src->free_data(src); free(src); } /* * ========================================================================== * A) FILE SRC FOR LOCAL FILESYSTEM FILES * ========================================================================== */ struct local_file_data { int fd; /* the file descriptor, once the file is opened */ off_t size; /* file size */ off_t bytes_read; /* bytes already read from file */ int error; char *path; /* path of the file on local filesystem */ }; static int lf_open(struct iso_file_src *src) { struct local_file_data *data; assert(src); data = (struct local_file_data*) src->data; assert(data->fd == -1); data->fd = open(data->path, O_RDONLY); data->bytes_read = (off_t) 0; data->error = 0; return (data->fd != -1 ? 1 : 0); } static void lf_close(struct iso_file_src *src) { struct local_file_data *data; assert(src); data = (struct local_file_data*) src->data; assert(data->fd != -1); close(data->fd); data->fd = -1; } static int lf_read_block(struct iso_file_src *src, unsigned char *buffer) { ssize_t bytes; struct local_file_data *data; assert(src); assert(buffer); data = (struct local_file_data*) src->data; assert(data->fd != -1); if (data->bytes_read >= data->size) { return 0; } if (data->error) { memset(buffer, 0, BLOCK_SIZE); data->bytes_read += BLOCK_SIZE; return -2; } bytes = 0; do { ssize_t result; result = read(data->fd, buffer + bytes, BLOCK_SIZE - bytes); if (result < 0) { char msg[PATH_MAX + 32]; sprintf(msg, "Problem reading from %s", data->path); iso_msg_sorry(LIBISO_CANT_READ_FILE, msg); /* fill buffer with 0s and return */ memset(buffer + bytes, 0, BLOCK_SIZE - bytes); data->bytes_read += BLOCK_SIZE; return -1; } if (!result) break; bytes += result; } while (bytes < BLOCK_SIZE); if (bytes < BLOCK_SIZE) { /* eof */ memset(buffer + bytes, 0, BLOCK_SIZE - bytes); } data->bytes_read += (off_t) bytes; if (data->bytes_read >= data->size) { return 0; } else { return 1; } } static off_t lf_get_size(struct iso_file_src *src) { struct local_file_data *data; assert(src); data = (struct local_file_data*) src->data; return data->size; } static void lf_free_data(struct iso_file_src *src) { struct local_file_data *data; data = (struct local_file_data*) src->data; free(data->path); free(data); } struct iso_file_src* iso_file_src_from_path(const char *path) { struct stat info; struct iso_file_src *src; struct local_file_data *data; assert(path); if (lstat(path, &info) < 0) { iso_msg_fatal(LIBISO_FILE_DOESNT_EXIST, "File doesn't exist"); return NULL; } if (access(path, R_OK) < 0) { iso_msg_fatal(LIBISO_FILE_NO_READ_ACCESS, "No read access"); return NULL; } data = malloc(sizeof(struct local_file_data)); if (!data) return NULL; src = malloc(sizeof(struct iso_file_src)); if (!src) { free(data); return NULL; } data->fd = -1; data->path = strdup(path); data->size = info.st_size; src->open = lf_open; src->close = lf_close; src->read_block = lf_read_block; src->get_size = lf_get_size; src->free_data = lf_free_data; src->data = data; return src; } /* * ========================================================================== * B) FILE SRC FOR PREVIOUS IMAGE FILES * ========================================================================== */ struct prev_img_file_data { int block; /* block where image begins */ off_t size; /* file size */ int nblocks; /* file size in blocks */ int current; /* last block read from file */ int error; struct data_source *src; /* source for reading from previous image */ }; static int pi_open(struct iso_file_src *src) { struct prev_img_file_data *data; assert(src); data = (struct prev_img_file_data*) src->data; data->current = data->block; data->error = 0; return 1; } static void pi_close(struct iso_file_src *src) { /* nothing needed */ return; } static int pi_read_block(struct iso_file_src *src, unsigned char *buffer) { int res; struct prev_img_file_data *data; assert(src); assert(buffer); data = (struct prev_img_file_data*) src->data; if (data->current >= data->block + data->nblocks) { return 0; } if (data->error) { memset(buffer, 0, BLOCK_SIZE); data->current++; return -2; } res = data->src->read_block(data->src, data->current++, buffer); if (res < 0) { iso_msg_sorry(LIBISO_CANT_READ_IMG, "Problem reading file from previous image"); /* fill buffer with 0s and return */ memset(buffer, 0, BLOCK_SIZE); return -1; } if (data->current >= data->block + data->nblocks) { return 0; } else { return 1; } } static off_t pi_get_size(struct iso_file_src *src) { struct prev_img_file_data *data; assert(src); data = (struct prev_img_file_data*) src->data; return data->size; } static void pi_free_data(struct iso_file_src *src) { free(src->data); } struct iso_file_src* iso_file_src_from_prev_img(struct data_source* data_src, int block, off_t size) { struct iso_file_src *src; struct prev_img_file_data *data; data = malloc(sizeof(struct prev_img_file_data)); if (!data) return NULL; src = malloc(sizeof(struct iso_file_src)); if (!src) { free(data); return NULL; } data->block = block; data->size = size; data->nblocks = div_up(size, BLOCK_SIZE); data->src = data_src; src->open = pi_open; src->close = pi_close; src->read_block = pi_read_block; src->get_size = pi_get_size; src->free_data = pi_free_data; src->data = data; return src; } /* * Class for reading data from files, with base implementations * for reading local filesystem files and previous session files. * * TODO this is kept private for now, it can be useful to make that * public later. */ #ifndef FILE_SRC_H_ #define FILE_SRC_H_ #include struct data_source; struct iso_file_src { /** * Opens the file. * This should be called before any call to read_block(). * @return 1 on success, <= 0 on error */ int (*open)(struct iso_file_src *src); /** * Close the file. * This should be called when the iso_file_src is no more needed. */ void (*close)(struct iso_file_src *src); /** * Read data from the file in 2048 bytes chunks. * * A pointer to the contents of the file will be updated, in such a way * that each time this function is called, new data is read to the buffer. * It behaves in the same way as usual read(2) call this way. * * This always reads 2048 bytes, unless an EOF is found, or we reach the * size previously returned by get_size(). In this case, the buffer is * completed with 0s. * * @param buffer Buffer where the data will be written. Its size must * be at least 2048 bytes. * @return * 1 if file contains more data. If we reach EOF before the expected * size, this keeps returning 1, and next blocks are filled with 0s. * 0 when we reach the expected size, that in normal usage is also EOF * on file. * < 0 on error. If such case, next calls to read_block will keep * returning < 0 until we reach the expected size, filling blocks with * 0s. */ int (*read_block)(struct iso_file_src *src, unsigned char *buffer); /** * Get the size in bytes of the file. * * This is set when the iso_file_src is created, and should not change. If * the actual file size changes, it will be truncated or padding with 0s * when the block is read */ off_t (*get_size)(struct iso_file_src *); /** Clean up the source specific data */ void (*free_data)(struct iso_file_src *); /** Source specific data */ void *data; }; /** * Get a iso_file_src() for reading from a local filesystem file. */ struct iso_file_src* iso_file_src_from_path(const char *path); /** * Get a iso_file_src() for reading from a previous image file. * @param src data_source to read from previous image. It is not freed, * so you need to free it when no more needed. * @param block block on image where file begins * @param nblocks number of block of the file */ struct iso_file_src* iso_file_src_from_prev_img(struct data_source* src, int block, off_t size); /** * free a iso_file_src */ void iso_file_src_free(struct iso_file_src *src); #endif /*FILE_SRC_H_*/ #include #include #include "hash.h" static unsigned int iso_hash_path(const char *path) { unsigned int hash_num=0; const char *c; c=path; while(*c) hash_num = (hash_num << 15) + (hash_num << 3) + (hash_num >> 3) + *c++; return hash_num % HASH_NODES; } int iso_hash_lookup(struct iso_hash_node **table, const char *path) { struct iso_hash_node *node; unsigned int hash_num; hash_num = iso_hash_path(path); node=table[hash_num]; if (!node) return 0; if (!strcmp(path, node->path)) return 1; while (node->next) { node=node->next; if (!strcmp(path, node->path)) return 1; } return 0; } static struct iso_hash_node* iso_hash_node_new (const char *path) { struct iso_hash_node *node; /*create an element to be inserted in the hash table */ node=malloc(sizeof(struct iso_hash_node)); node->path=strdup(path); node->next=NULL; return node; } int iso_hash_insert(struct iso_hash_node **table, const char *path) { struct iso_hash_node *node; unsigned int hash_num; /* find the hash number */ hash_num = iso_hash_path(path); /* insert it */ node = table[hash_num]; /* unfortunately, we can't safely consider that a path * won't be twice in the hash table so make sure it * doesn't already exists */ if (!node) { table[hash_num]=iso_hash_node_new(path); return 1; } /* if it's already in, we don't do anything */ if (!strcmp(path, node->path)) return 0; while (node->next) { node = node->next; /* if it's already in, we don't do anything */ if (!strcmp (path, node->path)) return 0; } node->next = iso_hash_node_new(path); return 1; } static void iso_hash_node_free(struct iso_hash_node *node) { free(node->path); free(node); } int iso_hash_remove(struct iso_hash_node **table, const char *path) { unsigned int hash_num; struct iso_hash_node *node; hash_num = iso_hash_path(path); node=table[hash_num]; if (!node) return 0; if (!strcmp(path, node->path)) { table[hash_num]=node->next; iso_hash_node_free(node); return 1; } while (node->next) { struct iso_hash_node *prev; prev = node; node = node->next; if (!strcmp (path, node->path)) { prev->next=node->next; iso_hash_node_free(node); return 1; } } return 0; } void iso_hash_empty(struct iso_hash_node **table) { int i; for (i=0; i < HASH_NODES; i++) { struct iso_hash_node *node; node=table[i]; if (!node) continue; table[i]=NULL; do { struct iso_hash_node *next; next=node->next; iso_hash_node_free(node); node=next; } while (node); } } #ifndef ISO_HASH_H #define ISO_HASH_H struct iso_hash_node { struct iso_hash_node *next; char *path; }; #define HASH_NODES 128 /** * Searches in the hash table if the path exists. * * \param table The hash table. * \param path The path of the file to look for. * * \return 1 if the path exists in the hash table, 0 otherwise. */ int iso_hash_lookup(struct iso_hash_node **table, const char *path); /** * Insert a new path in the hash table. * * \param table The hash table. * \param path The path of a file to add to the hash table. * * \return 1 if the file wasn't already in the hash table, 0 otherwise. */ int iso_hash_insert(struct iso_hash_node **table, const char *path); /** * Remove a path from the hash table. * * \param table The hash table. * \param path The path of a file to remove from the hash table. * * \return 1 if the file was found and removed, 0 otherwise. */ int iso_hash_remove(struct iso_hash_node **table, const char *path); /** * Empty the hash table. */ void iso_hash_empty(struct iso_hash_node **table); #endif /* ISO_HASH_H */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* vim: set noet ts=8 sts=8 sw=8 : */ #include "joliet.h" #include "ecma119.h" #include "ecma119_tree.h" #include "tree.h" #include "util.h" #include "volume.h" #include "eltorito.h" #include "messages.h" #include #include static struct joliet_tree_node* create_node(struct ecma119_write_target *t, struct joliet_tree_node *parent, struct iso_tree_node *iso) { struct joliet_tree_node *ret = calloc(1, sizeof(struct joliet_tree_node)); ret->name = iso_j_id(iso->name, t->input_charset); ret->dirent_len = 34 + (ret->name ? ucslen(ret->name) * 2 : 0); ret->parent = parent; ret->target = t; if ( ISO_ISDIR(iso) ) { struct iso_tree_node_dir *dir = (struct iso_tree_node_dir *) iso; ret->info.dir.children = calloc(sizeof(void*), dir->nchildren); ret->type = JOLIET_DIR; } else if (ISO_ISREG(iso)) { /* it's a file */ struct iso_tree_node_file *iso_f = (struct iso_tree_node_file *) iso; struct iso_file *file; file = iso_file_table_lookup(t->file_table, iso_f); if ( file == NULL ) { file = iso_file_new(t, iso_f); if (!file) { /* * That was an error. * TODO currently this cause the file to be ignored... Maybe * throw an error is a better alternative */ joliet_tree_free(ret); return NULL; } iso_file_table_add_file(t->file_table, file); } ret->info.file = file; ret->type = JOLIET_FILE; } else if (ISO_ISBOOT(iso)) { /* it's boot catalog info */ struct iso_tree_node_boot *boot = (struct iso_tree_node_boot*) iso; ret->info.boot_img = boot->img; ret->type = JOLIET_BOOT; } else { /* this should never happen */ assert(0); } return ret; } static struct joliet_tree_node* create_tree(struct ecma119_write_target *t, struct joliet_tree_node *parent, struct iso_tree_node *iso) { struct joliet_tree_node *root; assert(t && iso); if ( iso->hide_flags & LIBISO_HIDE_ON_JOLIET ) return NULL; switch (iso->type) { case LIBISO_NODE_FILE: case LIBISO_NODE_BOOT: root = create_node(t, parent, iso); break; case LIBISO_NODE_DIR: { size_t i; struct joliet_tree_node *node; struct iso_tree_node_dir *dir; root = create_node(t, parent, iso); dir = (struct iso_tree_node_dir*)iso; for (i = 0; i < dir->nchildren; ++i) { node = create_tree(t, root, dir->children[i]); if ( node != NULL ) { root->info.dir.children[root->info.dir.nchildren++] = node; } } } break; default: { char msg[512]; sprintf(msg, "Can't add %s to Joliet tree. This kind of files " "can only be added to a Rock Ridget tree. Skipping", iso->name); iso_msg_note(LIBISO_JOLIET_WRONG_FILE_TYPE, msg); return NULL; } break; } return root; } static int cmp_node(const void *f1, const void *f2) { struct joliet_tree_node *f = *((struct joliet_tree_node**)f1); struct joliet_tree_node *g = *((struct joliet_tree_node**)f2); return ucscmp(f->name, g->name); } static void sort_tree(struct joliet_tree_node *root) { size_t i; assert(root && (root->type == JOLIET_DIR)); qsort(root->info.dir.children, root->info.dir.nchildren, sizeof(void*), cmp_node); for (i = 0; i < root->info.dir.nchildren; i++) { struct joliet_tree_node *child = root->info.dir.children[i]; if ( child->type == JOLIET_DIR ) sort_tree(child); } } void joliet_prepare_path_tables(struct ecma119_write_target *t) { size_t cur, i, j; t->pathlist_joliet[0] = t->joliet_root; t->path_table_size_joliet = 10; /* root directory record */ cur = 1; for (i = 0; i < t->dirlist_len_joliet; i++) { struct joliet_tree_node *dir = t->pathlist_joliet[i]; for (j = 0; j < dir->info.dir.nchildren; j++) { struct joliet_tree_node *ch = dir->info.dir.children[j]; if (ch->type == JOLIET_DIR) { size_t len = 8 + ucslen(ch->name)*2; t->pathlist_joliet[cur++] = ch; t->path_table_size_joliet += len; } } } } /** * Calculate the size of each directory. */ void joliet_calc_dir_size(struct ecma119_write_target *t, struct joliet_tree_node *root) { size_t i; size_t newlen; struct joliet_tree_node *ch; assert(root && (root->type == JOLIET_DIR) ); t->dirlist_len_joliet++; root->info.dir.len = 68; /* for "." and ".." entries */ for (i = 0; i < root->info.dir.nchildren; ++i) { ch = root->info.dir.children[i]; newlen = root->info.dir.len + ch->dirent_len; if ((newlen % 2048) < (root->info.dir.len % 2048)) { root->info.dir.len = newlen + (2048 - (root->info.dir.len % 2048)); } else { root->info.dir.len += ch->dirent_len; } if (ch->type == JOLIET_DIR) joliet_calc_dir_size(t, ch); } t->total_dir_size_joliet += round_up (root->info.dir.len, t->block_size); } /** * Calculate the position of each directory. Also fill out t->dirlist_joliet. */ void joliet_calc_dir_pos(struct ecma119_write_target *t, struct joliet_tree_node *root) { size_t i; struct joliet_tree_node *ch; assert(root && (root->type == JOLIET_DIR)); root->info.dir.block = t->curblock; t->curblock += div_up(root->info.dir.len, t->block_size); t->dirlist_joliet[t->curfile++] = root; for (i = 0; i < root->info.dir.nchildren; i++) { ch = root->info.dir.children[i]; if (ch->type == JOLIET_DIR) joliet_calc_dir_pos(t, ch); } } struct joliet_tree_node* joliet_tree_create(struct ecma119_write_target *t, struct iso_tree_node *iso_root) { struct joliet_tree_node *root = create_tree(t, NULL, iso_root); sort_tree(root); return root; } void joliet_tree_free(struct joliet_tree_node *root) { size_t i; if (root->type == JOLIET_DIR) { for (i=0; i < root->info.dir.nchildren; i++) { joliet_tree_free(root->info.dir.children[i]); } free(root->info.dir.children); } free(root->name); free(root); } /* ugh. this is mostly C&P */ static void write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf) { void (*write_int)(uint8_t*, uint32_t, int) = l_type ? iso_lsb : iso_msb; size_t i; struct ecma119_path_table_record *rec; struct joliet_tree_node *dir; int parent = 0; assert (t->joliet); for (i = 0; i < t->dirlist_len; i++) { dir = t->pathlist_joliet[i]; while ((i) && t->pathlist_joliet[parent] != dir->parent) parent++; assert(parent < i || i == 0); rec = (struct ecma119_path_table_record*) buf; rec->len_di[0] = dir->parent ? (uint8_t) ucslen(dir->name) * 2 : 1; rec->len_xa[0] = 0; write_int(rec->block, dir->info.dir.block, 4); write_int(rec->parent, parent + 1, 2); if (dir->parent) memcpy(rec->dir_id, dir->name, rec->len_di[0]); buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2); } } /* if file_id is >= 0, we use it instead of the filename. As a magic number, * file_id == 3 means that we are writing the root directory record (in order * to distinguish it from the "." entry in the root directory) */ static void write_one_dir_record(struct ecma119_write_target *t, struct joliet_tree_node *node, int file_id, uint8_t *buf) { uint32_t len; uint32_t block; uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len; uint8_t len_fi = (file_id >= 0) ? 1 : ucslen(node->name) * 2; uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id); uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name; struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; if (node->type == JOLIET_DIR) { len = node->info.dir.len; block = node->info.dir.block; } else if (node->type == JOLIET_BOOT) { assert(t->eltorito); if (node->info.boot_img) { block = t->imgblock; len = t->catalog->image->node->node.attrib.st_size; } else { /* we always assume 2048 as catalog len */ block = t->catblock; len = 2048; } } else { /* file */ assert(node->type == JOLIET_FILE); len = node->info.file->size; block = node->info.file->block; } if (file_id == 1 && node->parent) node = node->parent; rec->len_dr[0] = len_dr; iso_bb(rec->block, block, 4); iso_bb(rec->length, len, 4); iso_datetime_7(rec->recording_time, t->now); rec->flags[0] = (node->type == JOLIET_DIR) ? 2 : 0; iso_bb(rec->vol_seq_number, t->volnum + 1, 2); rec->len_fi[0] = len_fi; memcpy(rec->file_id, name, len_fi); } static void write_l_path_table(struct ecma119_write_target *t, uint8_t *buf) { write_path_table (t, 1, buf); } static void write_m_path_table(struct ecma119_write_target *t, uint8_t *buf) { write_path_table (t, 0, buf); } void joliet_write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf) { struct ecma119_sup_vol_desc *vol = (struct ecma119_sup_vol_desc*)buf; struct iso_volume *volume = t->volset->volume[t->volnum]; uint16_t *vol_id = str2ucs(volume->volume_id, t->input_charset); uint16_t *pub_id = str2ucs(volume->publisher_id, t->input_charset); uint16_t *data_id = str2ucs(volume->data_preparer_id, t->input_charset); uint16_t *volset_id = str2ucs(t->volset->volset_id, t->input_charset); int vol_id_len = MIN(32, ucslen(vol_id) * 2); int pub_id_len = MIN(128, ucslen(pub_id) * 2); int data_id_len = MIN(128, ucslen(data_id) * 2); int volset_id_len = MIN(128, ucslen(volset_id) * 2); uint16_t *system_id = str2ucs(volume->system_id, t->input_charset); uint16_t *application_id = str2ucs(volume->application_id, t->input_charset); uint16_t *copyright_file_id = str2ucs(volume->copyright_file_id, t->input_charset); uint16_t *abstract_file_id = str2ucs(volume->abstract_file_id, t->input_charset); uint16_t *biblio_file_id = str2ucs(volume->biblio_file_id, t->input_charset); int system_id_len = MIN(32, ucslen(system_id) * 2); int application_id_len = MIN(128, ucslen(application_id) * 2); int copyright_file_id_len = MIN(37, ucslen(copyright_file_id) * 2); int abstract_file_id_len = MIN(37, ucslen(abstract_file_id) * 2); int biblio_file_id_len = MIN(37, ucslen(biblio_file_id) * 2); vol->vol_desc_type[0] = 2; memcpy(vol->std_identifier, "CD001", 5); vol->vol_desc_version[0] = 1; if (vol_id) memcpy(vol->volume_id, vol_id, vol_id_len); memcpy(vol->esc_sequences, "%/E", 3); iso_bb(vol->vol_space_size, t->vol_space_size, 4); iso_bb(vol->vol_set_size, t->volset->volset_size, 2); iso_bb(vol->vol_seq_number, t->volnum + 1, 2); iso_bb(vol->block_size, t->block_size, 2); iso_bb(vol->path_table_size, t->path_table_size_joliet, 4); iso_lsb(vol->l_path_table_pos, t->l_path_table_pos_joliet, 4); iso_msb(vol->m_path_table_pos, t->m_path_table_pos_joliet, 4); write_one_dir_record(t, t->joliet_root, 3, vol->root_dir_record); memcpy(vol->vol_set_id, volset_id, volset_id_len); memcpy(vol->publisher_id, pub_id, pub_id_len); memcpy(vol->data_prep_id, data_id, data_id_len); memcpy(vol->system_id, system_id, system_id_len); memcpy(vol->application_id, "APPID", application_id_len); memcpy(vol->copyright_file_id, copyright_file_id, copyright_file_id_len); memcpy(vol->abstract_file_id, abstract_file_id, abstract_file_id_len); memcpy(vol->bibliographic_file_id, biblio_file_id, biblio_file_id_len); iso_datetime_17(vol->vol_creation_time, t->now); iso_datetime_17(vol->vol_modification_time, t->now); iso_datetime_17(vol->vol_effective_time, t->now); vol->file_structure_version[0] = 1; free(vol_id); free(volset_id); free(pub_id); free(data_id); free(system_id); free(application_id); free(copyright_file_id); free(abstract_file_id); free(biblio_file_id); } static void write_one_dir(struct ecma119_write_target *t, struct joliet_tree_node *dir, uint8_t *buf) { size_t i; int j; size_t len; uint8_t *orig_buf = buf; uint8_t *prior_buf = buf; assert(dir->type == JOLIET_DIR); /* write the "." and ".." entries first */ write_one_dir_record(t, dir, 0, buf); buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; write_one_dir_record(t, dir, 1, buf); buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; for (i = 0; i < dir->info.dir.nchildren; i++) { write_one_dir_record(t, dir->info.dir.children[i], -1, buf); len = ((struct ecma119_dir_record*) buf)->len_dr[0]; if ((buf + len - prior_buf) >= 2048) { for (j = len - 1; j >= 0; j--) { prior_buf[2048 + j] = buf[j]; buf[j] = 0; } prior_buf += 2048; buf = prior_buf + len; } else { buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; } } assert (buf - orig_buf == dir->info.dir.len); } static void write_dirs(struct ecma119_write_target *t, uint8_t *buf) { size_t i; struct joliet_tree_node *dir; assert (t->curblock + t->ms_block == t->dirlist_joliet[0]->info.dir.block); for (i = 0; i < t->dirlist_len_joliet; i++) { dir = t->dirlist_joliet[i]; write_one_dir(t, dir, buf); buf += round_up(dir->info.dir.len, t->block_size); } } void joliet_wr_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, joliet_write_sup_vol_desc, 2048, buf); } void joliet_wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, write_l_path_table, t->path_table_size_joliet, buf); } void joliet_wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, write_m_path_table, t->path_table_size_joliet, buf); } void joliet_wr_dir_records(struct ecma119_write_target *t, uint8_t *buf) { ecma119_start_chunking(t, write_dirs, t->total_dir_size_joliet, buf); } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* vim: set noet ts=8 sts=8 sw=8 : */ /** * \file joliet.h * * Declare the filesystems trees that are Joliet-compatible and the public * functions for tying them into an ecma119 volume. */ #ifndef LIBISO_JOLIET_H #define LIBISO_JOLIET_H #include #include struct ecma119_write_target; struct iso_tree_node; enum joliet_node_type { JOLIET_FILE, JOLIET_DIR, JOLIET_BOOT }; struct joliet_dir_info { struct joliet_tree_node **children; size_t nchildren; size_t len; size_t block; }; struct joliet_tree_node { uint16_t *name; /**< In UCS-2BE. */ size_t dirent_len; struct joliet_tree_node *parent; struct ecma119_write_target *target; enum joliet_node_type type; union { struct iso_file *file; struct joliet_dir_info dir; unsigned int boot_img:1; /** For boot nodes, it identifies if this * corresponds to image(1) or catalog(0). * The block is stored in ecma119_write_target */ } info; }; /** * Create a new joliet_tree that corresponds to the tree represented by * \p iso_root. */ struct joliet_tree_node* joliet_tree_create(struct ecma119_write_target *target, struct iso_tree_node *iso_root); /** * Calculate the size of each directory in the joliet heirarchy. */ void joliet_calc_dir_size(struct ecma119_write_target *t, struct joliet_tree_node*); /** * Calculate the position of each directory in the joliet heirarchy. */ void joliet_calc_dir_pos(struct ecma119_write_target *t, struct joliet_tree_node*); /** * Calculate the size of the joliet path table and fill in the list of * directories. */ void joliet_prepare_path_tables(struct ecma119_write_target *t); void joliet_tree_free(struct joliet_tree_node *root); void joliet_write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf); void joliet_wr_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf); void joliet_wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf); void joliet_wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf); void joliet_wr_dir_records(struct ecma119_write_target *t, uint8_t *buf); #endif /* LIBISO_JOLIET_H */ /* libiso_msgs Message handling facility of libiso. Copyright (C) 2006 Thomas Schmitt , provided under GPL */ #include #include #include #include #include #include #include /* Only this single source module is entitled to do this */ #define LIBISO_MSGS_H_INTERNAL 1 /* All participants in the messaging system must do this */ #include "libiso_msgs.h" /* ----------------------------- libiso_msgs_item ------------------------- */ static int libiso_msgs_item_new(struct libiso_msgs_item **item, struct libiso_msgs_item *link, int flag) { int ret; struct libiso_msgs_item *o; struct timeval tv; struct timezone tz; (*item)= o= (struct libiso_msgs_item *) malloc(sizeof(struct libiso_msgs_item)); if(o==NULL) return(-1); o->timestamp= 0.0; ret= gettimeofday(&tv,&tz); if(ret==0) o->timestamp= tv.tv_sec+0.000001*tv.tv_usec; o->process_id= getpid(); o->driveno= -1; o->severity= LIBISO_MSGS_SEV_ALL; o->priority= LIBISO_MSGS_PRIO_ZERO; o->error_code= 0; o->msg_text= NULL; o->os_errno= 0; o->prev= link; o->next= NULL; if(link!=NULL) { if(link->next!=NULL) { link->next->prev= o; o->next= link->next; } link->next= o; } return(1); } /** Detaches item from its queue and eventually readjusts start, end pointers of the queue */ int libiso_msgs_item_unlink(struct libiso_msgs_item *o, struct libiso_msgs_item **chain_start, struct libiso_msgs_item **chain_end, int flag) { if(o->prev!=NULL) o->prev->next= o->next; if(o->next!=NULL) o->next->prev= o->prev; if(chain_start!=NULL) if(*chain_start == o) *chain_start= o->next; if(chain_end!=NULL) if(*chain_end == o) *chain_end= o->prev; o->next= o->prev= NULL; return(1); } int libiso_msgs_item_destroy(struct libiso_msgs_item **item, int flag) { struct libiso_msgs_item *o; o= *item; if(o==NULL) return(0); libiso_msgs_item_unlink(o,NULL,NULL,0); if(o->msg_text!=NULL) free((char *) o->msg_text); free((char *) o); *item= NULL; return(1); } int libiso_msgs_item_get_msg(struct libiso_msgs_item *item, int *error_code, char **msg_text, int *os_errno, int flag) { *error_code= item->error_code; *msg_text= item->msg_text; *os_errno= item->os_errno; return(1); } int libiso_msgs_item_get_origin(struct libiso_msgs_item *item, double *timestamp, pid_t *process_id, int *driveno, int flag) { *timestamp= item->timestamp; *process_id= item->process_id; *driveno= item->driveno; return(1); } int libiso_msgs_item_get_rank(struct libiso_msgs_item *item, int *severity, int *priority, int flag) { *severity= item->severity; *priority= item->priority; return(1); } /* ------------------------------- libiso_msgs ---------------------------- */ int libiso_msgs_new(struct libiso_msgs **m, int flag) { struct libiso_msgs *o; (*m)= o= (struct libiso_msgs *) malloc(sizeof(struct libiso_msgs)); if(o==NULL) return(-1); o->oldest= NULL; o->youngest= NULL; o->count= 0; o->queue_severity= LIBISO_MSGS_SEV_ALL; o->print_severity= LIBISO_MSGS_SEV_NEVER; strcpy(o->print_id,"libiso: "); #ifndef LIBISO_MSGS_SINGLE_THREADED pthread_mutex_init(&(o->lock_mutex),NULL); #endif return(1); } int libiso_msgs_destroy(struct libiso_msgs **m, int flag) { struct libiso_msgs *o; struct libiso_msgs_item *item, *next_item; o= *m; if(o==NULL) return(0); #ifndef LIBISO_MSGS_SINGLE_THREADED if(pthread_mutex_destroy(&(o->lock_mutex))!=0) { pthread_mutex_unlock(&(o->lock_mutex)); pthread_mutex_destroy(&(o->lock_mutex)); } #endif for(item= o->oldest; item!=NULL; item= next_item) { next_item= item->next; libiso_msgs_item_destroy(&item,0); } free((char *) o); *m= NULL; return(1); } int libiso_msgs_set_severities(struct libiso_msgs *m, int queue_severity, int print_severity, char *print_id, int flag) { m->queue_severity= queue_severity; m->print_severity= print_severity; strncpy(m->print_id,print_id,80); m->print_id[80]= 0; return(1); } static int libiso_msgs_lock(struct libiso_msgs *m, int flag) { #ifndef LIBISO_MSGS_SINGLE_THREADED int ret; ret= pthread_mutex_lock(&(m->lock_mutex)); if(ret!=0) return(0); #endif return(1); } static int libiso_msgs_unlock(struct libiso_msgs *m, int flag) { #ifndef LIBISO_MSGS_SINGLE_THREADED int ret; ret= pthread_mutex_unlock(&(m->lock_mutex)); if(ret!=0) return(0); #endif return(1); } int libiso_msgs__text_to_sev(char *severity_name, int *severity, int flag) { if(strncmp(severity_name,"NEVER",5)==0) *severity= LIBISO_MSGS_SEV_NEVER; else if(strncmp(severity_name,"ABORT",5)==0) *severity= LIBISO_MSGS_SEV_ABORT; else if(strncmp(severity_name,"FATAL",5)==0) *severity= LIBISO_MSGS_SEV_FATAL; else if(strncmp(severity_name,"SORRY",5)==0) *severity= LIBISO_MSGS_SEV_SORRY; else if(strncmp(severity_name,"WARNING",7)==0) *severity= LIBISO_MSGS_SEV_WARNING; else if(strncmp(severity_name,"HINT",4)==0) *severity= LIBISO_MSGS_SEV_HINT; else if(strncmp(severity_name,"NOTE",4)==0) *severity= LIBISO_MSGS_SEV_NOTE; else if(strncmp(severity_name,"UPDATE",6)==0) *severity= LIBISO_MSGS_SEV_UPDATE; else if(strncmp(severity_name,"DEBUG",5)==0) *severity= LIBISO_MSGS_SEV_DEBUG; else if(strncmp(severity_name,"ALL",3)==0) *severity= LIBISO_MSGS_SEV_ALL; else { *severity= LIBISO_MSGS_SEV_NEVER; return(0); } return(1); } int libiso_msgs__sev_to_text(int severity, char **severity_name, int flag) { if(flag&1) { *severity_name= "NEVER\nABORT\nFATAL\nSORRY\nWARNING\nHINT\nNOTE\nUPDATE\nDEBUG\nALL"; return(1); } *severity_name= ""; if(severity>=LIBISO_MSGS_SEV_NEVER) *severity_name= "NEVER"; else if(severity>=LIBISO_MSGS_SEV_ABORT) *severity_name= "ABORT"; else if(severity>=LIBISO_MSGS_SEV_FATAL) *severity_name= "FATAL"; else if(severity>=LIBISO_MSGS_SEV_SORRY) *severity_name= "SORRY"; else if(severity>=LIBISO_MSGS_SEV_WARNING) *severity_name= "WARNING"; else if(severity>=LIBISO_MSGS_SEV_HINT) *severity_name= "HINT"; else if(severity>=LIBISO_MSGS_SEV_NOTE) *severity_name= "NOTE"; else if(severity>=LIBISO_MSGS_SEV_UPDATE) *severity_name= "UPDATE"; else if(severity>=LIBISO_MSGS_SEV_DEBUG) *severity_name= "DEBUG"; else if(severity>=LIBISO_MSGS_SEV_ALL) *severity_name= "ALL"; else { *severity_name= ""; return(0); } return(1); } int libiso_msgs_submit(struct libiso_msgs *m, int driveno, int error_code, int severity, int priority, char *msg_text, int os_errno, int flag) { int ret; char *textpt,*sev_name,sev_text[81]; struct libiso_msgs_item *item= NULL; if(severity >= m->print_severity) { if(msg_text==NULL) textpt= ""; else textpt= msg_text; sev_text[0]= 0; ret= libiso_msgs__sev_to_text(severity,&sev_name,0); if(ret>0) sprintf(sev_text,"%s : ",sev_name); fprintf(stderr,"%s%s%s\n",m->print_id,sev_text,textpt); if(os_errno!=0) { ret= libiso_msgs_lock(m,0); if(ret<=0) return(-1); fprintf(stderr,"%s( Most recent system error: %d '%s' )\n", m->print_id,os_errno,strerror(os_errno)); libiso_msgs_unlock(m,0); } } if(severity < m->queue_severity) return(0); ret= libiso_msgs_lock(m,0); if(ret<=0) return(-1); ret= libiso_msgs_item_new(&item,m->youngest,0); if(ret<=0) goto failed; item->driveno= driveno; item->error_code= error_code; item->severity= severity; item->priority= priority; if(msg_text!=NULL) { item->msg_text= malloc(strlen(msg_text)+1); if(item->msg_text==NULL) goto failed; strcpy(item->msg_text,msg_text); } item->os_errno= os_errno; if(m->oldest==NULL) m->oldest= item; m->youngest= item; m->count++; libiso_msgs_unlock(m,0); /* fprintf(stderr,"libiso_experimental: message submitted to queue (now %d)\n", m->count); */ return(1); failed:; libiso_msgs_item_destroy(&item,0); libiso_msgs_unlock(m,0); return(-1); } int libiso_msgs_obtain(struct libiso_msgs *m, struct libiso_msgs_item **item, int severity, int priority, int flag) { int ret; struct libiso_msgs_item *im, *next_im= NULL; *item= NULL; ret= libiso_msgs_lock(m,0); if(ret<=0) return(-1); for(im= m->oldest; im!=NULL; im= next_im) { for(; im!=NULL; im= next_im) { next_im= im->next; if(im->severity>=severity) break; libiso_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0); libiso_msgs_item_destroy(&im,0); /* severity too low: delete */ } if(im==NULL) break; if(im->priority>=priority) break; } if(im==NULL) {ret= 0; goto ex;} libiso_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0); *item= im; ret= 1; ex:; libiso_msgs_unlock(m,0); return(ret); } int libiso_msgs_destroy_item(struct libiso_msgs *m, struct libiso_msgs_item **item, int flag) { int ret; ret= libiso_msgs_lock(m,0); if(ret<=0) return(-1); ret= libiso_msgs_item_destroy(item,0); libiso_msgs_unlock(m,0); return(ret); } /* libiso_msgs Message handling facility of libisofs. Copyright (C) 2006-2007 Thomas Schmitt , provided under GPL */ /* *Never* set this macro outside libiso_msgs.c ! The entrails of the message handling facility are not to be seen by the other library components or the applications. */ #ifdef LIBISO_MSGS_H_INTERNAL #ifndef LIBISO_MSGS_SINGLE_THREADED #include #endif struct libiso_msgs_item { double timestamp; pid_t process_id; int driveno; int severity; int priority; /* Apply for your developer's error code range at libburn-hackers@pykix.org Report introduced codes in the list below. */ int error_code; char *msg_text; int os_errno; struct libiso_msgs_item *prev,*next; }; struct libiso_msgs { struct libiso_msgs_item *oldest; struct libiso_msgs_item *youngest; int count; int queue_severity; int print_severity; char print_id[81]; #ifndef LIBISO_MSGS_SINGLE_THREADED pthread_mutex_t lock_mutex; #endif }; #endif /* LIBISO_MSGS_H_INTERNAL */ #ifndef LIBISO_MSGS_H_INCLUDED #define LIBISO_MSGS_H_INCLUDED 1 #ifndef LIBISO_MSGS_H_INTERNAL /* Public Opaque Handles */ /** A pointer to this is a opaque handle to a message handling facility */ struct libiso_msgs; /** A pointer to this is a opaque handle to a single message item */ struct libiso_msgs_item; #endif /* ! LIBISO_MSGS_H_INTERNAL */ /* Public Macros */ /* Registered Severities */ /* It is well advisable to let applications select severities via strings and forwarded functions libiso_msgs__text_to_sev(), libiso_msgs__sev_to_text(). These macros are for use by libdax/libburn only. */ /** Use this to get messages of any severity. Do not use for submitting. */ #define LIBISO_MSGS_SEV_ALL 0x00000000 /** Debugging messages not to be visible to normal users by default */ #define LIBISO_MSGS_SEV_DEBUG 0x10000000 /** Update of a progress report about long running actions */ #define LIBISO_MSGS_SEV_UPDATE 0x20000000 /** Not so usual events which were gracefully handled */ #define LIBISO_MSGS_SEV_NOTE 0x30000000 /** Possibilities to achieve a better result */ #define LIBISO_MSGS_SEV_HINT 0x40000000 /** Warnings about problems which could not be handled optimally */ #define LIBISO_MSGS_SEV_WARNING 0x50000000 /** Non-fatal error messages indicating that parts of the action failed but processing will/should go on */ #define LIBISO_MSGS_SEV_SORRY 0x60000000 /** An error message which puts the whole operation of libdax in question */ #define LIBISO_MSGS_SEV_FATAL 0x70000000 /** A message from an abort handler which will finally finish libburn */ #define LIBISO_MSGS_SEV_ABORT 0x71000000 /** A severity to exclude resp. discard any possible message. Do not use this severity for submitting. */ #define LIBISO_MSGS_SEV_NEVER 0x7fffffff /* Registered Priorities */ /* Priorities are to be used by libburn/libdax only. */ #define LIBISO_MSGS_PRIO_ZERO 0x00000000 #define LIBISO_MSGS_PRIO_LOW 0x10000000 #define LIBISO_MSGS_PRIO_MEDIUM 0x20000000 #define LIBISO_MSGS_PRIO_HIGH 0x30000000 #define LIBISO_MSGS_PRIO_TOP 0x7ffffffe /* Do not use this priority for submitting */ #define LIBISO_MSGS_PRIO_NEVER 0x7fffffff /* Public Functions */ /* Calls initiated from inside libdax/libburn */ /** Create new empty message handling facility with queue. @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success, <=0 failure */ int libiso_msgs_new(struct libiso_msgs **m, int flag); /** Destroy a message handling facility and all its eventual messages. The submitted pointer gets set to NULL. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 for success, 0 for pointer to NULL */ int libiso_msgs_destroy(struct libiso_msgs **m, int flag); /** Submit a message to a message handling facility. @param driveno libdax drive number. Use -1 if no number is known. @param error_code Unique error code. Use only registered codes. See below. The same unique error_code may be issued at different occasions but those should be equivalent out of the view of a libdax application. (E.g. "cannot open ATA drive" versus "cannot open SCSI drive" would be equivalent.) @param severity The LIBISO_MSGS_SEV_* of the event. @param priority The LIBISO_MSGS_PRIO_* number of the event. @param msg_text Printable and human readable message text. @param os_errno Eventual error code from operating system (0 if none) @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 on success, 0 on rejection, <0 for severe errors */ int libiso_msgs_submit(struct libiso_msgs *m, int driveno, int error_code, int severity, int priority, char *msg_text, int os_errno, int flag); /* Calls from applications (to be forwarded by libdax/libburn) */ /** Convert a registered severity number into a severity name @param flag Bitfield for control purposes: bit0= list all severity names in a newline separated string @return >0 success, <=0 failure */ int libiso_msgs__sev_to_text(int severity, char **severity_name, int flag); /** Convert a severity name into a severity number, @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success, <=0 failure */ int libiso_msgs__text_to_sev(char *severity_name, int *severity, int flag); /** Set minimum severity for messages to be queued (default LIBISO_MSGS_SEV_ALL) and for messages to be printed directly to stderr (default LIBISO_MSGS_SEV_NEVER). @param print_id A text of at most 80 characters to be printed before any eventually printed message (default is "libdax: "). @param flag Bitfield for control purposes (unused yet, submit 0) @return always 1 for now */ int libiso_msgs_set_severities(struct libiso_msgs *m, int queue_severity, int print_severity, char *print_id, int flag); /** Obtain a message item that has at least the given severity and priority. Usually all older messages of lower severity are discarded then. If no item of sufficient severity was found, all others are discarded from the queue. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 if a matching item was found, 0 if not, <0 for severe errors */ int libiso_msgs_obtain(struct libiso_msgs *m, struct libiso_msgs_item **item, int severity, int priority, int flag); /** Destroy a message item obtained by libiso_msgs_obtain(). The submitted pointer gets set to NULL. Caution: Copy eventually obtained msg_text before destroying the item, if you want to use it further. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 for success, 0 for pointer to NULL, <0 for severe errors */ int libiso_msgs_destroy_item(struct libiso_msgs *m, struct libiso_msgs_item **item, int flag); /** Obtain from a message item the three application oriented components as submitted with the originating call of libiso_msgs_submit(). Caution: msg_text becomes a pointer into item, not a copy. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 on success, 0 on invalid item, <0 for servere errors */ int libiso_msgs_item_get_msg(struct libiso_msgs_item *item, int *error_code, char **msg_text, int *os_errno, int flag); /** Obtain from a message item the submitter identification submitted with the originating call of libiso_msgs_submit(). @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 on success, 0 on invalid item, <0 for servere errors */ int libiso_msgs_item_get_origin(struct libiso_msgs_item *item, double *timestamp, pid_t *process_id, int *driveno, int flag); /** Obtain from a message item severity and priority as submitted with the originating call of libiso_msgs_submit(). @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 on success, 0 on invalid item, <0 for servere errors */ int libiso_msgs_item_get_rank(struct libiso_msgs_item *item, int *severity, int *priority, int flag); #ifdef LIDBAX_MSGS_________________ /* Registered Error Codes */ Format: error_code (LIBISO_MSGS_SEV_*,LIBISO_MSGS_PRIO_*) = explanation If no severity or priority are fixely associates, use "(,)". ------------------------------------------------------------------------------ Range "libiso_msgs" : 0x00000000 to 0x0000ffff 0x00000000 (ALL,ZERO) = Initial setting in new libiso_msgs_item 0x00000001 (DEBUG,ZERO) = Test error message 0x00000002 (DEBUG,ZERO) = Debugging message ------------------------------------------------------------------------------ Range "elmom" : 0x00010000 to 0x0001ffff ------------------------------------------------------------------------------ Range "scdbackup" : 0x00020000 to 0x0002ffff Acessing and defending drives: 0x00020001 (SORRY,LOW) = Cannot open busy device 0x00020002 (SORRY,HIGH) = Encountered error when closing drive 0x00020003 (SORRY,HIGH) = Could not grab drive 0x00020004 (NOTE,HIGH) = Opened O_EXCL scsi sibling 0x00020005 (SORRY,HIGH) = Failed to open device 0x00020006 (FATAL,HIGH) = Too many scsi siblings 0x00020007 (NOTE,HIGH) = Closed O_EXCL scsi siblings 0x00020008 (SORRY,HIGH) = Device busy. Failed to fcntl-lock General library operations: 0x00020101 (WARNING,HIGH) = Cannot find given worker item 0x00020102 (SORRY,HIGH) = A drive operation is still going on 0x00020103 (WARNING,HIGH) = After scan a drive operation is still going on 0x00020104 (SORRY,HIGH) = NULL pointer caught 0x00020105 (SORRY,HIGH) = Drive is already released 0x00020106 (SORRY,HIGH) = Drive is busy on attempt to close 0x00020107 (SORRY,HIGH) = Drive is busy on attempt to shut down library 0x00020108 (SORRY,HIGH) = Drive is not grabbed on disc status inquiry 0x00020108 (FATAL,HIGH) = Could not allocate new drive object 0x00020109 (FATAL,HIGH) = Library not running 0x0002010a (FATAL,HIGH) = Unsuitable track mode 0x0002010b (FATAL,HIGH) = Burn run failed 0x0002010c (FATAL,HIGH) = Failed to transfer command to drive 0x0002010d (DEBUG,HIGH) = Could not inquire TOC 0x0002010e (FATAL,HIGH) = Attempt to read ATIP from ungrabbed drive 0x0002010f (DEBUG,HIGH) = SCSI error condition on command 0x00020110 (FATAL,HIGH) = Persistent drive address too long 0x00020111 (FATAL,HIGH) = Could not allocate new auxiliary object 0x00020112 (SORRY,HIGH) = Bad combination of write_type and block_type 0x00020113 (FATAL,HIGH) = Drive capabilities not inquired yet 0x00020114 (SORRY,HIGH) = Attempt to set ISRC with bad data 0x00020115 (SORRY,HIGH) = Attempt to set track mode to unusable value 0x00020116 (FATAL,HIGH) = Track mode has unusable value 0x00020117 (FATAL,HIGH) = toc_entry of drive is already in use 0x00020118 (DEBUG,HIGH) = Closing track 0x00020119 (DEBUG,HIGH) = Closing session 0x0002011a (NOTE,HIGH) = Padding up track to minimum size 0x0002011b (FATAL,HIGH) = Attempt to read track info from ungrabbed drive 0x0002011c (FATAL,HIGH) = Attempt to read track info from busy drive 0x0002011d (FATAL,HIGH) = SCSI error on write 0x0002011e (SORRY,HIGH) = Unsuitable media detected 0x0002011f (SORRY,HIGH) = Burning is restricted to a single track 0x00020120 (NOTE,HIGH) = FORMAT UNIT ignored 0x00020121 (FATAL,HIGH) = Write preparation setup failed 0x00020122 (FATAL,HIGH) = SCSI error on format_unit 0x00020123 (SORRY,HIGH) = DVD Media are unsuitable for desired track type 0x00020124 (SORRY,HIGH) = SCSI error on set_streaming 0x00020125 (SORRY,HIGH) = Write start address not supported 0x00020126 (SORRY,HIGH) = Write start address not properly aligned 0x00020127 (NOTE,HIGH) = Write start address is ... 0x00020128 (FATAL,HIGH) = Unsupported inquiry_type with mmc_get_performance 0x00020129 (SORRY,HIGH) = Will not format media type 0x0002012a (FATAL,HIGH) = Cannot inquire write mode capabilities 0x0002012b (FATAL,HIGH) = Drive offers no suitable write mode with this job 0x0002012c (SORRY,HIGH) = Too many logical tracks recorded 0x0002012d (FATAL,HIGH) = Exceeding range of permissible write addresses 0x0002012e (NOTE,HIGH) = Activated track default size 0x0002012f (SORRY,HIGH) = SAO is restricted to single fixed size session 0x00020130 (SORRY,HIGH) = Drive and media state unsuitable for blanking 0x00020131 (SORRY,HIGH) = No suitable formatting type offered by drive 0x00020132 (SORRY,HIGH) = Selected format is not suitable for libburn 0x00020133 (SORRY,HIGH) = Cannot mix data and audio in SAO mode 0x00020134 (NOTE,HIGH) = Defaulted TAO to DAO 0x00020135 (SORRY,HIGH) = Cannot perform TAO, job unsuitable for DAO 0x00020136 (SORRY,HIGH) = DAO burning restricted to single fixed size track 0x00020137 (HINT,HIGH) = TAO would be possible 0x00020138 (FATAL,HIGH) = Cannot reserve track 0x00020139 (SORRY,HIGH) = Write job parameters are unsuitable 0x0002013a (FATAL,HIGH) = No suitable media detected 0x0002013b (DEBUG,HIGH) = SCSI command indicates host or driver error 0x0002013c (SORRY,HIGH) = Malformed capabilities page 2Ah received 0x0002013d (DEBUG,LOW) = Waiting for free buffer space takes long time 0x0002013e (SORRY,HIGH) = Timeout with waiting for free buffer. Now disabled 0x0002013f (DEBUG,LOW) = Reporting total time spent with waiting for buffer libiso_audioxtr: 0x00020200 (SORRY,HIGH) = Cannot open audio source file 0x00020201 (SORRY,HIGH) = Audio source file has unsuitable format 0x00020202 (SORRY,HIGH) = Failed to prepare reading of audio data ------------------------------------------------------------------------------ Range "vreixo" : 0x00030000 to 0x0003ffff General: 0x00031001 (SORRY,HIGH) = Cannot read file (ignored) 0x00031002 (FATAL,HIGH) = Cannot read file (operation canceled) 0x00031003 (FATAL,HIGH) = File doesnt exist 0x00031004 (FATAL,HIGH) = Read access denied Image reading: 0x00031000 (FATAL,HIGH) = Unsupported ISO-9660 image 0x00031001 (HINT,MEDIUM) = Unsupported Vol Desc that will be ignored 0x00031002 (FATAL,HIGH) = Damaged ISO-9660 image 0x00031003 (SORRY,HIGH) = Cannot read previous image file Rock-Ridge: 0x00030101 (HINT,MEDIUM) = Unsupported SUSP entry that will be ignored 0x00030102 (SORRY,HIGH) = Wrong/damaged SUSP entry 0x00030103 (WARNING,MEDIUM)= Multiple SUSP ER entries where found 0x00030111 (SORRY,HIGH) = Unsupported RR feature 0x00030112 (SORRY,HIGH) = Error in a Rock Ridge entry El-Torito: 0x00030201 (HINT,MEDIUM) = Unsupported Boot Vol Desc that will be ignored 0x00030202 (SORRY,HIGH) = Wrong El-Torito catalog 0x00030203 (HINT,MEDIUM) = Unsupported El-Torito feature 0x00030204 (SORRY,HIGH) = Invalid file to be an El-Torito image 0x00030205 (WARNING,MEDIUM)= Cannot properly patch isolinux image 0x00030206 (WARNING,MEDIUM)= Copying El-Torito from a previous image without enought info about it Joliet: 0x00030301 (NOTE,MEDIUM) = Unsupported file type for Joliet tree ------------------------------------------------------------------------------ #endif /* LIDBAX_MSGS_________________ */ #ifdef LIBISO_MSGS_H_INTERNAL /* Internal Functions */ /** Lock before doing side effect operations on m */ static int libiso_msgs_lock(struct libiso_msgs *m, int flag); /** Unlock after effect operations on m are done */ static int libiso_msgs_unlock(struct libiso_msgs *m, int flag); /** Create new empty message item. @param link Previous item in queue @param flag Bitfield for control purposes (unused yet, submit 0) @return >0 success, <=0 failure */ static int libiso_msgs_item_new(struct libiso_msgs_item **item, struct libiso_msgs_item *link, int flag); /** Destroy a message item obtained by libiso_msgs_obtain(). The submitted pointer gets set to NULL. @param flag Bitfield for control purposes (unused yet, submit 0) @return 1 for success, 0 for pointer to NULL */ static int libiso_msgs_item_destroy(struct libiso_msgs_item **item, int flag); #endif /* LIBISO_MSGS_H_INTERNAL */ #endif /* ! LIBISO_MSGS_H_INCLUDED */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* vim: set noet ts=8 sts=8 sw=8 : */ /** * Create an ISO-9660 data volume with Rock Ridge and Joliet extensions. * Usage is easy: * - Create a new volume. * - Add files and directories. * - Write the volume to a file or create a burn source for use with Libburn. */ #ifndef LIBISO_LIBISOFS_H #define LIBISO_LIBISOFS_H #include #include /* #include */ struct burn_source; /** * Data volume. * @see volume.h for details. */ struct iso_volume; /** * A set of data volumes. * @see volume.h for details. */ struct iso_volset; /** * A node in the filesystem tree. * * This is opaque struct that represent any kind of nodes. When needed, * you can get the type with iso_tree_node_get_type and cast it to the * appropiate subtype: * * iso_tree_node_dir * iso_tree_node_file * iso_tree_node_symlink * * \see tree.h */ struct iso_tree_node; /** * The type of an iso_tree_node. * When an user gets an iso_tree_node from libisofs, (s)he can use * iso_tree_node_get_type to get the current type of the node, and then * cast to the appropriate subtype. For example: * * ... * struct iso_tree_node *node = iso_tree_iter_next(iter); * if ( iso_tree_node_get_type(node) == LIBISO_NODE_DIR ) { * struct iso_tree_node_dir *dir = (struct iso_tree_node_dir *)node; * ... * } * * Useful macros are provided. */ enum iso_tree_node_type { LIBISO_NODE_DIR, LIBISO_NODE_FILE, LIBISO_NODE_SYMLINK, LIBISO_NODE_BOOT }; #define LIBISO_ISDIR(n) (iso_tree_node_get_type(n) == LIBISO_NODE_DIR) #define LIBISO_ISREG(n) (iso_tree_node_get_type(n) == LIBISO_NODE_FILE) #define LIBISO_ISLNK(n) (iso_tree_node_get_type(n) == LIBISO_NODE_SYMLINK) /** * A directory in the filesystem tree. * The first member of this is an iso_tree_node. * \see tree.h */ struct iso_tree_node_dir; /** * A node in the filesystem tree that represents a regular file */ struct iso_tree_node_file; /** * A node in the filesystem tree that represents a symbolic link */ struct iso_tree_node_symlink; /** * A node that represents an El-Torito file. */ struct iso_tree_node_boot; /** * Information about El-Torito boot image. * \see eltorito.h */ struct el_torito_boot_image; /** Iterator for dir children. */ struct iso_tree_iter; /** * The procedence of the node. */ enum tree_node_from { /** The node has been added by the user */ LIBISO_NEW = 0, /** * The node comes from a previous image. That can be from a previous * session on disc, or from an ISO file we want to modify. */ LIBISO_PREVIMG }; /** * Extensions addition to ECMA-119 (ISO-9660) image. Usage of at least * one of these flags is highly recommended if the disc will be used on a * modern OS. */ enum ecma119_extension_flag { /** * Add the standard Rock Ridge extensions. This adds POSIX filesystem * features to the ECMA-119 image. Thus, usage of this flag is highly * recommended for images used on GNU/Linux systems. With the usage * of RR extension, the resulting image will have long filenames (up to * 255 characters), deeper directory structure, POSIX permissions and * owner info on files and directories, support for symbolic links or * special files... All that attributes can be modified/setted with the * appropiate function. */ ECMA119_ROCKRIDGE = (1<<0), /** * Add the non-standard Joliet extension to the image. This extension is * heavily used in Microsoft Windows systems, so if you plan to use your * disc on such a system you should add this extension. Usage of Joliet * supplies longer filesystem length (up to 64 unicode characters), and * deeper directory structure. */ ECMA119_JOLIET = (1<<1) }; /** * Flag used to hide a file in the RR/ISO or Joliet tree. * * \see iso_tree_node_set_hidden */ enum hide_node_flag { LIBISO_HIDE_ON_RR = 1 << 0, LIBISO_HIDE_ON_JOLIET = 1 << 1 }; /** * El-Torito bootable image type. */ enum eltorito_boot_media_type { ELTORITO_FLOPPY_EMUL, ELTORITO_HARD_DISC_EMUL, ELTORITO_NO_EMUL }; /** * ISO-9660 (ECMA-119) has important restrictions in both file/dir names * and deep of the directory hierarchy. These are intented for compatibility * with old systems, and most modern operative system can safety deal with * ISO filesystems with relaxed constraints. * You can use some of these flags to generate that kind of filesystems with * libisofs. Of course, all these options will lead to an image not conforming * with ISO-9660 specification, so use them with caution. * Moreover, note that there are much better options to have an ISO-9660 image * compliant with modern systems, such as the Rock Ridge and Joliet extensions, * that add support for longer filenames, deeper directory hierarchy and even * file permissions (in case of RR), while keeping a standard ISO structure * suitable for old systems. * Thus, in most cases you don't want to use the relaxed constraints. */ enum ecma119_relaxed_constraints_flag { ECMA119_OMIT_VERSION_NUMBERS = (1<<0), /**< * ISO-9660 requires a version number at the end of each file name. * That number is just ignored on most systems, so you can omit them * if you want. */ ECMA119_37_CHAR_FILENAMES = (1<<1) | (1<<0), /**< * Allow ISO-9660 filenames to be up to 37 characters long. The extra * space is taken from the version number, so this option involves * no version number */ ECMA119_NO_DIR_REALOCATION = (1<<2), /**< * In ISO-9660 images the depth of the directory hierarchy can't be * greater than 8 levels. In addition, a path to a file on disc can't * be more than 255 characteres. Use the ECMA119_NO_DIR_REALOCATION * to disable this restriction. */ ECMA119_RELAXED_FILENAMES = (1<<3) /**< * Allow filenames with any character. Note that with this flag, the * filename provide by the user will be used without any modification * other that a truncate to max. length. */ }; /** * Holds the options for the image generation. */ struct ecma119_source_opts { int volnum; /**< The volume in the set which you want to write (usually 0) */ int level; /**< ISO level to write at. */ int flags; /**< Which extensions to support. */ int relaxed_constraints; /**< see ecma119_relaxed_constraints_flag */ unsigned int copy_eltorito:1; /**< * In multisession discs, select whether to copy el-torito catalog * and boot image. Copy is needed for isolinux images, that need to * be patched. However, it can lead to problems when the image is * not present in the iso filesystem, because we can't figure out * its size. In those cases, we only copy 1 block of data. */ unsigned int no_cache_inodes:1; /**< If use inode caching or not. Set it to 1 to prevent * inode caching. * Usage of inode caching allows detection of hard-links, * which contents are only written once to disc this way. * Don't use inode caching in systems with non unique inodes * per device. */ unsigned int sort_files:1; /**< If files should be sorted based on their weight. */ unsigned int default_mode:1; /**< * The default values for files and directory permissions, * gid and uid. This option can be overwritten when set * one of the following. * 0 to use useful values, 1 to use node modes (this are * the same as filesystem ones if not changed after added * to tree). */ unsigned int replace_dir_mode:1; /**< * When 1, permissions for all dirs will be replaced by the * specified in dir_mode field. */ unsigned int replace_file_mode:1; /**< * When 1, permissions for all files will be replaced by the * specified in file_mode field. */ unsigned int replace_uid:1; /**< * When 1, uid of all nodes (both files and dirs) will be * replaced by the specified in uid field. */ unsigned int replace_gid:1; /**< * When 1, gid of all nodes (both files and dirs) will be * replaced by the specified in gid field. */ mode_t dir_mode; /**< Mode to use on dirs when replace_dir_mode is set. */ mode_t file_mode; /**< Mode to use on files when replace_file_mode is set. */ gid_t gid; /**< gid to use when replace_gid is set. */ uid_t uid; /**< uid to use when replace_uid is set. */ char *input_charset; /**< NULL to use default charset */ char *ouput_charset; /**< NULL to use default charset */ uint32_t ms_block; /**< * Start block for multisession. When this is greater than 0, * it's suppossed to be the lba of the next writable address * on disc; all block lba on image will take this into account, * and files from a previous session will not be written on * image. This behavior is only suitable for images to be * appended to a multisession disc. * When this is 0, no multisession image will be created. If * some files are taken from a previous image, its contents * will be written again to the new image. Use this with new * images or if you plan to modify an existin image. */ struct data_source* src; /**< * When modifying a image, this is the source of the original * image, used to read file contents. * Otherwise it can be NULL. */ uint8_t *overwrite; /**< * When not NULL, it should point to a buffer of at least * 64KiB, where libisofs will write the contents that should * be written at the beginning of a overwriteable media, to * grow the image. * You shoudl initialize the buffer either with 0s, or with * the contents of the first blocks of the image you're * growing. In most cases, 0 is good enought. */ }; /** * Options for image reading. * There are four kind of options: * - Related to multisession support. * In most cases, an image begins at LBA 0 of the data source. However, * in multisession discs, the later image begins in the last session on * disc. The block option can be used to specify the start of that last * session. * - Related to the tree that will be read. * As default, when Rock Ridge extensions are present in the image, that * will be used to get the tree. If RR extensions are not present, libisofs * will use the Joliet extensions if available. Finally, the plain ISO-9660 * tree is used if neither RR nor Joliet extensions are available. With * norock, nojoliet, and preferjoliet options, you can change this * default behavior. * - Related to default POSIX attributes. * When Rock Ridege extensions are not used, libisofs can't figure out what * are the the permissions, uid or gid for the files. You should supply * default values for that. * - Return information for image. * Both size, hasRR and hasJoliet will be filled by libisofs with suitable values. * Also, error is set to non-0 if some error happens (error codes are * private now) */ struct ecma119_read_opts { uint32_t block; /** Block where the image begins, usually 0, can be * different on a multisession disc. */ unsigned int norock:1; /*< Do not read Rock Ridge extensions */ unsigned int nojoliet:1; /*< Do not read Joliet extensions */ unsigned int preferjoliet:1; /*< When both Joliet and RR extensions are present, the RR * tree is used. If you prefer using Joliet, set this to 1. */ uid_t uid; /**< Default uid when no RR */ gid_t gid; /**< Default uid when no RR */ mode_t mode; /**< Default mode when no RR (only permissions) */ //TODO differ file and dir mode //option to convert names to lower case? /* modified by the function */ unsigned int hasRR:1; /*< It will be set to 1 if RR extensions are present, to 0 if not. */ unsigned int hasJoliet:1; /*< It will be set to 1 if Joliet extensions are present, to 0 if not. */ uint32_t size; /**< Will be filled with the size (in 2048 byte block) of * the image, as reported in the PVM. */ int error; }; /** * Data source used by libisofs for reading an existing image. * It contains suitable methods to read arbitrary block. Usually, the block * size is 2048 bytes. */ struct data_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 * data_source_free to destroy your reference to it. */ int refcount; /** * Read data from the source. * @param lba Block to be read. * @param buffer Buffer where the data will be written. Its size must * be at least 2048 bytes. * @return * 0 if ok, < 0 on error */ int (*read_block)(struct data_source *src, int lba, unsigned char *buffer); /** Get the size (number of block) of the source's data */ int (*get_size)(struct data_source *); /** Clean up the source specific data */ void (*free_data)(struct data_source *); /** Source specific data */ void *data; }; /** * This will hold the error code for some functions, if them fail. */ int libisofs_errno; /* an unexpected internal error */ #define INTERNAL_ERROR -1 /* file don't exists, or can't be stat'ed */ #define NO_FILE 1 /* user haven't read access to file */ #define NO_READ_ACCESS 2 /* unexpected file type, eg., passing a dir instead of a regular file */ #define UNEXPECTED_FILE_TYPE 3 /* invalid boot image size */ #define ELTORITO_WRONG_IMAGE_SIZE 4 /* invalid image */ #define ELTORITO_WRONG_IMAGE 5 /** * Controls the bahavior of iso_tree_radd_dir function */ struct iso_tree_radd_dir_behavior { char** excludes; /**< List of paths (file or directory) to be ignored. */ //int follow_sym_link; int stop_on_error; /**< Stop when an error was found?. */ int error; /**< set to 1 on error */ //int notify_errors; //char** errors; }; /** * Initialize libisofs. You must call this before any usage of the library. * @return 1 on success, 0 on error */ int iso_init(); /** * Finalize libisofs. */ void iso_finish(); /** * Create a new volume. * The parameters can be set to NULL if you wish to set them later. */ struct iso_volume *iso_volume_new(const char *volume_id, const char *publisher_id, const char *data_preparer_id); struct iso_volume *iso_volume_new_with_root(const char *volume_id, const char *publisher_id, const char *data_preparer_id, struct iso_tree_node_dir *root); /** * Free a volume. */ void iso_volume_free(struct iso_volume *volume); void iso_volset_ref(struct iso_volset *volset); /** * Free a set of data volumes. */ void iso_volset_free(struct iso_volset *volume); /** * Get a volume from a volume set. */ struct iso_volume *iso_volset_get_volume(struct iso_volset *volset, int volnum); /** * Get the root directory for a volume. */ struct iso_tree_node_dir *iso_volume_get_root(const struct iso_volume *volume); /** * Fill in the volume identifier for a volume. */ void iso_volume_set_volume_id(struct iso_volume *volume, const char *volume_id); /** * Get the volume identifier. * The returned string is owned by libisofs and should not be freed nor * changed. */ const char *iso_volume_get_volume_id(struct iso_volume *volume); /** * Fill in the publisher for a volume. */ void iso_volume_set_publisher_id(struct iso_volume *volume, const char *publisher_id); /** * Get the publisher of a volume. * The returned string is owned by libisofs and should not be freed nor * changed. */ const char *iso_volume_get_publisher_id(struct iso_volume *volume); /** * 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 data preparer of a volume. * The returned string is owned by libisofs and should not be freed nor * changed. */ const char *iso_volume_get_data_preparer_id(struct iso_volume *volume); /** * Fill in the system id for a volume. Up to 32 characters. */ void iso_volume_set_system_id(struct iso_volume *volume, const char *system_id); /** * Get the system id of a volume. * The returned string is owned by libisofs and should not be freed nor * changed. */ const char *iso_volume_get_system_id(struct iso_volume *volume); /** * Fill in the application id for a volume. Up to 128 chars. */ void iso_volume_set_application_id(struct iso_volume *volume, const char *application_id); /** * Get the application id of a volume. * The returned string is owned by libisofs and should not be freed nor * changed. */ const char *iso_volume_get_application_id(struct iso_volume *volume); /** * Fill copyright information for the volume. Usually this refers * to a file on disc. Up to 37 characters. */ void iso_volume_set_copyright_file_id(struct iso_volume *volume, const char *copyright_file_id); /** * Get the copyright information of a volume. * The returned string is owned by libisofs and should not be freed nor * changed. */ const char *iso_volume_get_copyright_file_id(struct iso_volume *volume); /** * Fill abstract information for the volume. Usually this refers * to a file on disc. Up to 37 characters. */ void iso_volume_set_abstract_file_id(struct iso_volume *volume, const char *abstract_file_id); /** * Get the abstract information of a volume. * The returned string is owned by libisofs and should not be freed nor * changed. */ const char *iso_volume_get_abstract_file_id(struct iso_volume *volume); /** * Fill biblio information for the volume. Usually this refers * to a file on disc. Up to 37 characters. */ void iso_volume_set_biblio_file_id(struct iso_volume *volume, const char *biblio_file_id); /** * Get the biblio information of a volume. * The returned string is owned by libisofs and should not be freed nor * changed. */ const char *iso_volume_get_biblio_file_id(struct iso_volume *volume); /** * Create a bootable volume by adding a El-Torito boot image. * * This also add a catalog tree node to the image filesystem tree. The tree * node for the image will be replaced with a iso_tree_node_boot node, that * acts as a placeholder for the real image. * * \param volume The volume to make bootable. * \param image The tree node with the file to use as default boot image. * \param type The boot media type. This can be one of 3 types: * - Floppy emulation: Boot image files must be exactly * 1200 kB, 1440 kB or 2880 kB. * - Hard disc emulation: The image must begin with a master * boot record with a single image. * - No emulation. You should specify load segment and load size * of image. * \param dir The directory node where the boot catalog will be located * in image. Usually both boot catalog and boot image will be * located in the same dir, maybe /boot. * \param name The name of the boot catalog. * * \return The default El-Torito bootable image. If specified image file * seems to be not correct, this returns NULL and libisofs_errno * is set propertly. * * \pre \p volume is a volume without any boot catalog yet * \pre \p image is a file tree node already inserted in the volume tree. * \pre \p image is a file tree node that refers to a newly added file, not * one from a previous session. FIXME allow prev session files too * \pre \p dir is a directory node already inserted in the volume tree. * \pre \p name There isn't any dir child with the same name. * */ struct el_torito_boot_image* iso_volume_set_boot_image(struct iso_volume *volume, struct iso_tree_node *image, enum eltorito_boot_media_type type, struct iso_tree_node_dir *dir, char *name); struct el_torito_boot_image* iso_volume_set_boot_image_hidden(struct iso_volume *volume, const char* path, enum eltorito_boot_media_type type); /** * Get El-Torito boot image of the volume, if any. * * This can be useful, for example, to check if a volume read from a previous * session or an existing image is bootable. It can also be useful to get * the image and catalog tree nodes. An application would want those, for * example, to prevent the user removing it. * * Both tree nodes are owned by libisofs and should not be freed. You can check * if the node is already on the tree by getting its parent (note that when * reading El-Torito info from a previous image, the nodes might not be on * the tree even if you haven't removed them). Remember that you'll need to * get a new ref (with iso_tree_node_ref()) before inserting them again to the * tree, and probably you will also need to set the name or permissions. * * \param imgnode When not NULL, it will be filled with the image tree node, if * any. * \param catnode When not NULL, it will be filled with the catalog tree node, * if any. * * \return The default El-Torito bootable image, or NULL is the volume is not * bootable. */ struct el_torito_boot_image* iso_volume_get_boot_image(struct iso_volume *volume, struct iso_tree_node **imgnode, struct iso_tree_node **catnode); /** * Removes the El-Torito bootable image. Both the catalog and image tree nodes * are also removed from the image filesystem tree, if there. * If the volume is not bootable (don't have el-torito image) this function is * a nop. */ void iso_volume_remove_boot_image(struct iso_volume *volume); /** * Sets the load segment for the initial boot image. This is only for * no emulation boot images, and is a NOP for other image types. */ void el_torito_set_load_seg(struct el_torito_boot_image *bootimg, int segment); /** * Sets the number of sectors (512b) to be load at load segment during * the initial boot procedure. This is only for * no emulation boot images, and is a NOP for other image types. */ void el_torito_set_load_size(struct el_torito_boot_image *bootimg, int sectors); /** * Marks the specified boot image as not bootable */ void el_torito_set_no_bootable(struct el_torito_boot_image *bootimg); /** * Specifies that this image needs to be patched. This involves the writting * of a 56 bytes boot information table at offset 8 of the boot image file. * The original boot image file won't be modified. * This is needed for isolinux boot images. */ void el_torito_patch_isolinux_image(struct el_torito_boot_image *bootimg); /** * Locate a node by its path on disc. * * \param volume The volume to search in. * \param path The path, in the image, of the file. * * \return The node found or NULL. */ struct iso_tree_node *iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path); /** * TODO I don't like this kind of functions here. I think it should be * in genisofs * Add a file or a directory (recursively) to a volume by specifying its path on the volume. * * \param volume The volume to add the file to. * \param disc_path The path on the disc at which to add the disc. * \param path The path, on the local filesystem, of the file. * * \return The node for the file or NULL if the parent doesn't exists on the disc. */ //struct iso_tree_node *iso_tree_volume_add_path(struct iso_volume *volume, // const char *disc_path, // const char *path); /** * TODO I don't like this kind of functions here. I think it should be * in genisofs * Creates a new, empty directory on the volume. * * \param volume The volume to add the directory to. * \param disc_path The path on the volume at which to add the directory. * * \return A pointer to the newly created directory. */ //struct iso_tree_node *iso_tree_volume_add_new_dir(struct iso_volume *volume, // const char *disc_path); /** * Create a new Volume Set consisting of only one volume. * @param volume The first and only volume for the volset to contain. * @param volset_id The Volume Set ID. * @return A new iso_volset. */ struct iso_volset *iso_volset_new(struct iso_volume *volume, const char *volset_id); /** * Creates a new root dir for a filesystem tree */ struct iso_tree_node_dir *iso_tree_new_root(); /** * Add a file to a directory. * * \param path The path, on the local filesystem, of the file. * * \pre \p parent is non-NULL. * \pre \p path is non-NULL. * \return An iso_tree_node_file whose path is \p path and whose parent is * \p parent. * On error, returns NULL and libisofs_errno is set appropriately: * NO_FILE if path doesn't point to a valid file. * NO_READ_ACCESS if user haven't read access on file * UNEXPECTED_FILE_TYPE if path doesn't point to a regular file */ struct iso_tree_node *iso_tree_add_file(struct iso_tree_node_dir *parent, const char *path); /** * Add a symbolic link to a directory. * * \param name The name of the symbolic link * \param dest The distination of the link, i.e., the file this link points * to * * \pre \p parent, name and dest are non-NULL. * * \return An iso_tree_node_symlink */ struct iso_tree_node *iso_tree_add_symlink(struct iso_tree_node_dir *parent, const char *name, const char *dest); /** * Add a new, empty directory to the tree. * * \pre \p parent is non-NULL. * \pre \p name is unique among the children and files belonging to \p parent. * Also, it doesn't contain '/' characters. * * \post \p parent contains a child directory whose name is \p name and whose * POSIX attributes are the same as \p parent's. * \return a pointer to the newly created directory. */ struct iso_tree_node_dir *iso_tree_add_dir(struct iso_tree_node_dir *parent, const char *name); /* TODO iso_tree_new_special */ /** * Add a file to a directory. * * \param path The path, on the local filesystem, of the file. * * \pre \p parent is non-NULL. * \pre \p path is non-NULL and is a valid path to a file or directory on the local * filesystem. * \return An iso_tree_node whose path is \p path and whose parent is \p parent. * On error, returns NULL and libisofs_errno is set appropriately: * NO_FILE if path doesn't point to a valid file. * NO_READ_ACCESS if user haven't read access on file * UNEXPECTED_FILE_TYPE if path refers to non supported file type * (at the momment, only dirs, symlinks and regular * files are supported). */ struct iso_tree_node *iso_tree_add_node(struct iso_tree_node_dir *parent, const char *path); /** * TODO I don't like this kind of functions here. I think it should be * in genisofs * * Recursively add an existing directory to the tree. * Warning: when using this, you'll lose pointers to files or subdirectories. * If you want to have pointers to all files and directories, * use iso_tree_add_file, iso_tree_add_node and iso_tree_add_dir. * * \param path The path, on the local filesystem, of the directory to add. * * \pre \p parent is non-NULL. * \pre \p path is non-NULL and is a valid path to a directory on the local * filesystem. */ void iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path, struct iso_tree_radd_dir_behavior *behavior); /** * Get the type of an iso_tree_node */ enum iso_tree_node_type iso_tree_node_get_type(struct iso_tree_node *node); /** * Set the name of a tree node (using the current locale). */ void iso_tree_node_set_name(struct iso_tree_node *node, const char *name); /** * Get the name of a tree node (using the current locale). * The returned string belongs to the node and should not be modified nor * freed. Use strdup if you really need your own copy. */ const char *iso_tree_node_get_name(struct iso_tree_node *node); /** * Set if the node will be hidden in RR/ISO tree, Joliet tree or both. * * If the file is setted as hidden in one tree, it won't be included there, so * it won't be visible in a OS accessing CD using that tree. For example, * GNU/Linux systems access to Rock Ridge / ISO9960 tree in order to see * what is recorded on CD, while MS Windows make use of the Joliet tree. If a * file is hidden only in Joliet, it won't be visible in Windows systems, * while still visible in Linux. * * If a file is hidden in both trees, it won't be written to image. * * \param node The node that is to be hidden. * \param hide_attrs hide_node_flag's to set the trees in which file * will be hidden. */ void iso_tree_node_set_hidden(struct iso_tree_node *node, int hide_attrs); /** * Check if a node will be hidden in RR/ISO tree, Joliet tree or both. * * @return * 0 if the node won't be hidden, otherwise you can AND the return value * with hide_node_flag's to get in what trees the node will be hidden. */ int iso_tree_node_is_hidden(struct iso_tree_node *node); /** * Set the group id for the node. This attribute is only useful when * Rock Ridge extensions are enabled. */ void iso_tree_node_set_gid(struct iso_tree_node *node, gid_t gid); /** * Get the group id of the node. */ gid_t iso_tree_node_get_gid(struct iso_tree_node *node); /** * Set the user id for the node. This attribute is only useful when * Rock Ridge extensions are enabled. */ void iso_tree_node_set_uid(struct iso_tree_node *node, uid_t uid); /** * Get the user id of the node. */ uid_t iso_tree_node_get_uid(struct iso_tree_node *node); /** * Set the permissions for the node. This attribute is only useful when * Rock Ridge extensions are enabled. * * \param mode bitmask with the permissions of the node, as specified * in 'man 2 stat'. The file type bitfields will be ignored, * only file permissions will be modified. */ void iso_tree_node_set_permissions(struct iso_tree_node *node, mode_t mode); /** Get the permissions for the node */ mode_t iso_tree_node_get_permissions(struct iso_tree_node *node); /** Get the size of the node, in bytes */ off_t iso_tree_node_get_size(struct iso_tree_node *node); /** Set the time of last modification of the file */ void iso_tree_node_set_mtime(struct iso_tree_node *node, time_t time); /** Get the time of last modification of the file */ time_t iso_tree_node_get_mtime(struct iso_tree_node *node); /** Set the time of last access to the file */ void iso_tree_node_set_atime(struct iso_tree_node *node, time_t time); /** Get the time of last access to the file */ time_t iso_tree_node_get_atime(struct iso_tree_node *node); /** Set the time of last status change of the file */ void iso_tree_node_set_ctime(struct iso_tree_node *node, time_t time); /** Get the time of last status change of the file */ time_t iso_tree_node_get_ctime(struct iso_tree_node *node); /** * Sets the order in which a node will be written on image. High weihted files * will be written first, so in a disc them will be written near the center. * * \param node The node which weight will be changed. If it's a dir, this * function will change the weight of all its children. For nodes * other that dirs or regular files, this function has no effect. * \param w The weight as a integer number, the greater this value is, the * closer from the begining of image the file will be written. */ void iso_tree_node_set_sort_weight(struct iso_tree_node *node, int w); /** * Sets the destination of a symbolic link */ void iso_tree_node_symlink_set_dest(struct iso_tree_node_symlink *node, const char *dest); /** * Get the destination of a symbolic link. * The returned string is owned by libisofs and should not be freed nor modified. */ const char *iso_tree_node_symlink_get_dest(struct iso_tree_node_symlink *node); /** * Get an iterator for the children of the given dir. * You can iterate over the children with iso_tree_iter_next. When finished, * you should free the iterator with iso_tree_iter_free. * You musn't delete a child of the same dir, using iso_tree_node_take() or * iso_tree_node_remove(), while you're using the iterator. You can use * iso_tree_node_take_iter() or iso_tree_node_remove_iter() instead. * * The usage of an iterator is: * * struct iso_tree_iter *iter; * struct iso_tree_node *node; * iter = iso_tree_node_children(dir); * while ( (node = iso_tree_iter_next(iter)) != NULL ) { * // do something with the child * } * iso_tree_iter_free(iter); * * An iterator is intended to be used in a single iteration over the * children of a dir. Thus, it should be treated as a temporary object, * and free as soon as possible. */ struct iso_tree_iter *iso_tree_node_children(struct iso_tree_node_dir *dir); /** * Get the next child. * Take care that the node is owned by libisofs, and will be freed whit the * tree it belongs. If you want your own ref to it, call iso_tree_node_ref() * on it. * This returns NULL if no more children are available. */ struct iso_tree_node *iso_tree_iter_next(struct iso_tree_iter *iter); /** * Check if there're more children. * @return * 1 if next call to iso_tree_iter_next() will return != NULL, * 0 otherwise */ int iso_tree_iter_has_next(struct iso_tree_iter *iter); /** Free an iteration */ void iso_tree_iter_free(struct iso_tree_iter *iter); /** * Removes a child from a directory. * The child is not freed, so you will become the owner of the node. Later * you can add the node to another dir (calling iso_tree_add_child), or free * it if you don't need it (with iso_tree_free). * * @return 0 on success, -1 if the node doesn't belong to the dir. */ int iso_tree_node_take(struct iso_tree_node_dir *dir, struct iso_tree_node *node); /** * Removes a child from a directory and free (unref) it. * If you want to keep the child alive, you need to iso_tree_node_ref() it * before this call, but in that case iso_tree_node_take() is a better * alternative. * * @return 0 on success, -1 if the node doesn't belong to the dir (in this * last case the node is not freed). */ int iso_tree_node_remove(struct iso_tree_node_dir *dir, struct iso_tree_node *node); /** * Removes a child from a directory during an iteration, without freeing it. * It's like iso_tree_node_take(), but to be used during a directory * iteration. * The node removed will be the last returned by the iteration. * * The behavior on two call to this function without calling iso_tree_iter_next * between then is undefined, and should never occur. (TODO protect against this?) * * @return 0 on success, < 0 on an invalid usage, i.e., if the user call this * before an inicial iso_tree_iter_next() or if last * iso_tree_iter_next() has returned NULL. */ int iso_tree_node_take_iter(struct iso_tree_iter *iter); /** * Removes a child from a directory during an iteration and free it. * It's like iso_tree_node_remove(), but to be used during a directory * iteration. * The node removed will be the last returned by the iteration. * * The behavior on two call to this function without calling iso_tree_iter_next * between then is undefined, and should never occur. (TODO protect against this?) * * @return 0 on success, < 0 on an invalid usage, i.e., if the user call this * before an inicial iso_tree_iter_next() or if last * iso_tree_iter_next() has returned NULL. */ int iso_tree_node_remove_iter(struct iso_tree_iter *iter); /* * Get the parent of the given iso tree node. * This returns NULL if the node is the root of the tree, or is a node * that doesn't pertain to any tree (it was removed/take) */ struct iso_tree_node_dir *iso_tree_node_get_parent(struct iso_tree_node *node); /** * Adds a child to a directory. * The child will be freed when the parent is freed, so you must be the * owner of the child (maybe calling iso_tree_node_ref) before calling this. * * \pre parent has no child with the same name as \p child */ void iso_tree_add_child(struct iso_tree_node_dir *parent, struct iso_tree_node *child); /** * Increments the reference counting of the given node. * If you call this, you must remember call iso_tree_free when the * node is no more needed. */ void iso_tree_node_ref(struct iso_tree_node *node); /** * Recursively free a directory. * * \param root The root of the directory heirarchy to free. * * \pre \p root is non-NULL. */ void iso_tree_free(struct iso_tree_node *root); /** * Recursively print a directory to stdout. * \param spaces The initial number of spaces on the left. Set to 0 if you * supply a root directory. */ void iso_tree_print(const struct iso_tree_node *root, int spaces); /** Create a burn_source which can be used as a data source for a track * * The volume set used to create the libburn_source can _not_ be modified * until the libburn_source is freed. * * \param volumeset The volume set from which you want to write * \param opts The options for image generation * * \pre \p volumeset is non-NULL * \pre \p volnum is less than \p volset->volset_size. * \return A burn_source to be used for the data source for a track */ struct burn_source* iso_source_new_ecma119(struct iso_volset *volumeset, struct ecma119_source_opts *opts); /** * Creates a new data source from the given file. * * Returns NULL on error */ struct data_source *data_source_from_file(const char *path); /** Free a given data source (decrease its refcount and maybe free it) */ void data_source_free(struct data_source*); /** * Read an existing ISO image. * * TODO documentar */ struct iso_volset *iso_volset_read(struct data_source *src, struct ecma119_read_opts *opts); /** * Control queueing and stderr printing of messages from libisofs. * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", * "NOTE", "UPDATE", "DEBUG", "ALL". * * @param queue_severity Gives the minimum limit for messages to be queued. * Default: "NEVER". If you queue messages then you * must consume them by iso_msgs_obtain(). * @param print_severity Does the same for messages to be printed directly * to stderr. * @param print_id A text prefix to be printed before the message. * @return >0 for success, <=0 for error */ int iso_msgs_set_severities(char *queue_severity, char *print_severity, char *print_id); #define ISO_MSGS_MESSAGE_LEN 4096 /** * Obtain the oldest pending libisofs message from the queue which has at * least the given minimum_severity. This message and any older message of * lower severity will get discarded from the queue and is then lost forever. * * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", * "NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER" * will discard the whole queue. * * @param error_code Will become a unique error code as listed in messages.h * @param msg_text Must provide at least ISO_MSGS_MESSAGE_LEN bytes. * @param os_errno Will become the eventual errno related to the message * @param severity Will become the severity related to the message and * should provide at least 80 bytes. * @return 1 if a matching item was found, 0 if not, <0 for severe errors */ int iso_msgs_obtain(char *minimum_severity, int *error_code, char msg_text[], int *os_errno, char severity[]); /** Return the messenger object handle used by libisofs. This handle may be used by related libraries to replace their own compatible messenger objects and thus to direct their messages to the libisofs message queue. See also: libburn, API function burn_set_messenger(). @return the handle. Do only use with compatible */ void *iso_get_messenger(void); #endif /* LIBISO_LIBISOFS_H */ #include #include #include "libisofs.h" #include "messages.h" struct libiso_msgs *libiso_messenger = NULL; int iso_init() { if (libiso_messenger == NULL) { if (libiso_msgs_new(&libiso_messenger, 0) <= 0) return 0; } libiso_msgs_set_severities(libiso_messenger, LIBISO_MSGS_SEV_NEVER, LIBISO_MSGS_SEV_FATAL, "libisofs: ", 0); return 1; } void iso_finish() { libiso_msgs_destroy(&libiso_messenger,0); } void iso_msg_debug(char *msg_text) { libiso_msgs_submit(libiso_messenger, -1, 0x00000002, LIBISO_MSGS_SEV_DEBUG, LIBISO_MSGS_PRIO_ZERO, msg_text, 0, 0); } void iso_msg_note(int error_code, char *msg_text) { libiso_msgs_submit(libiso_messenger, -1, error_code, LIBISO_MSGS_SEV_NOTE, LIBISO_MSGS_PRIO_MEDIUM, msg_text, 0, 0); } void iso_msg_hint(int error_code, char *msg_text) { libiso_msgs_submit(libiso_messenger, -1, error_code, LIBISO_MSGS_SEV_HINT, LIBISO_MSGS_PRIO_MEDIUM, msg_text, 0, 0); } void iso_msg_warn(int error_code, char *msg_text) { libiso_msgs_submit(libiso_messenger, -1, error_code, LIBISO_MSGS_SEV_WARNING, LIBISO_MSGS_PRIO_MEDIUM, msg_text, 0, 0); } void iso_msg_sorry(int error_code, char *msg_text) { libiso_msgs_submit(libiso_messenger, -1, error_code, LIBISO_MSGS_SEV_SORRY, LIBISO_MSGS_PRIO_HIGH, msg_text, 0, 0); } void iso_msg_fatal(int error_code, char *msg_text) { libiso_msgs_submit(libiso_messenger, -1, error_code, LIBISO_MSGS_SEV_FATAL, LIBISO_MSGS_PRIO_HIGH, msg_text, 0, 0); } /** * Control queueing and stderr printing of messages from libisofs. * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", * "NOTE", "UPDATE", "DEBUG", "ALL". * * @param queue_severity Gives the minimum limit for messages to be queued. * Default: "NEVER". If you queue messages then you * must consume them by iso_msgs_obtain(). * @param print_severity Does the same for messages to be printed directly * to stderr. * @param print_id A text prefix to be printed before the message. * @return >0 for success, <=0 for error */ int iso_msgs_set_severities(char *queue_severity, char *print_severity, char *print_id) { int ret, queue_sevno, print_sevno; ret = libiso_msgs__text_to_sev(queue_severity, &queue_sevno, 0); if (ret <= 0) return 0; ret = libiso_msgs__text_to_sev(print_severity, &print_sevno, 0); if (ret <= 0) return 0; ret = libiso_msgs_set_severities(libiso_messenger, queue_sevno, print_sevno, print_id, 0); if (ret <= 0) return 0; return 1; } #define ISO_MSGS_MESSAGE_LEN 4096 /** * Obtain the oldest pending libisofs message from the queue which has at * least the given minimum_severity. This message and any older message of * lower severity will get discarded from the queue and is then lost forever. * * Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT", * "NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER" * will discard the whole queue. * * @param error_code Will become a unique error code as listed in messages.h * @param msg_text Must provide at least ISO_MSGS_MESSAGE_LEN bytes. * @param os_errno Will become the eventual errno related to the message * @param severity Will become the severity related to the message and * should provide at least 80 bytes. * @return 1 if a matching item was found, 0 if not, <0 for severe errors */ int iso_msgs_obtain(char *minimum_severity, int *error_code, char msg_text[], int *os_errno, char severity[]) { int ret, minimum_sevno, sevno, priority; char *textpt, *sev_name; struct libiso_msgs_item *item = NULL; if (libiso_messenger == NULL) return 0; ret = libiso_msgs__text_to_sev(minimum_severity, &minimum_sevno, 0); if (ret <= 0) return 0; ret = libiso_msgs_obtain(libiso_messenger, &item, minimum_sevno, LIBISO_MSGS_PRIO_ZERO, 0); if (ret <= 0) goto ex; ret = libiso_msgs_item_get_msg(item, error_code, &textpt, os_errno, 0); if (ret <= 0) goto ex; strncpy(msg_text, textpt, ISO_MSGS_MESSAGE_LEN-1); if(strlen(textpt) >= ISO_MSGS_MESSAGE_LEN) msg_text[ISO_MSGS_MESSAGE_LEN-1] = 0; severity[0]= 0; ret = libiso_msgs_item_get_rank(item, &sevno, &priority, 0); if(ret <= 0) goto ex; ret = libiso_msgs__sev_to_text(sevno, &sev_name, 0); if(ret <= 0) goto ex; strcpy(severity,sev_name); ret = 1; ex: libiso_msgs_destroy_item(libiso_messenger, &item, 0); return ret; } void *iso_get_messenger(void) { return libiso_messenger; } /* * Message handling for libisofs */ #ifndef MESSAGES_H_ #define MESSAGES_H_ #include "libiso_msgs.h" /** Can't read file (ignored) */ #define LIBISO_CANT_READ_FILE 0x00031001 /** Can't read file (operation canceled) */ #define LIBISO_FILE_READ_ERROR 0x00031002 /** File doesn't exist */ #define LIBISO_FILE_DOESNT_EXIST 0x00031003 /** Read access denied */ #define LIBISO_FILE_NO_READ_ACCESS 0x00031004 /** Unsupported image feature */ #define LIBISO_IMG_UNSUPPORTED 0x00031000 /** Unsupported Vol Desc that will be ignored */ #define LIBISO_UNSUPPORTED_VD 0x00031001 /** damaged image */ #define LIBISO_WRONG_IMG 0x00031002 /** Can't read previous image file */ #define LIBISO_CANT_READ_IMG 0x00031003 /* Unsupported SUSP entry */ #define LIBISO_SUSP_UNHANLED 0x00030101 /* Wrong SUSP entry, that cause RR to be ignored */ #define LIBISO_SUSP_WRONG 0x00030102 /* Unsupported multiple SUSP ER entries where found */ #define LIBISO_SUSP_MULTIPLE_ER 0x00030103 /** Unsupported RR feature. */ #define LIBISO_RR_UNSUPPORTED 0x00030111 /** Error in a Rock Ridge entry. */ #define LIBISO_RR_ERROR 0x00030112 /** Unsupported boot vol desc. */ #define LIBISO_BOOT_VD_UNHANLED 0x00030201 /** Wrong or damaged el-torito catalog */ #define LIBISO_EL_TORITO_WRONG 0x00030202 /** Unsupproted el-torito feature */ #define LIBISO_EL_TORITO_UNHANLED 0x00030203 /** Trying to add an invalid file as a El-Torito image */ #define LIBISO_EL_TORITO_WRONG_IMG 0x00030204 /** Can't properly patch isolinux image */ #define LIBISO_ISOLINUX_CANT_PATCH 0x00030205 /** Copying El-Torito from a previous image without enought info about it */ #define LIBISO_EL_TORITO_BLIND_COPY 0x00030206 /** Unsupported file type for Joliet tree */ #define LIBISO_JOLIET_WRONG_FILE_TYPE 0x00030301 void iso_msg_debug(char *msg_text); void iso_msg_note(int error_code, char *msg_text); void iso_msg_hint(int error_code, char *msg_text); void iso_msg_warn(int error_code, char *msg_text); void iso_msg_sorry(int error_code, char *msg_text); void iso_msg_fatal(int error_code, char *msg_text); #endif /*MESSAGES_H_*/ /* vim: set noet ts=8 sts=8 sw=8 : */ #include "rockridge.h" #include "util.h" #include "ecma119.h" #include "ecma119_tree.h" #include "tree.h" #include "susp.h" #include #include #include #include #include #include /** See IEEE P1281 Draft Version 1.12/5.5 */ void rrip_add_ER(struct ecma119_write_target *t, struct ecma119_tree_node *dir) { unsigned char *ER = malloc(182); assert(dir->type == ECMA119_DIR); ER[0] = 'E'; ER[1] = 'R'; ER[2] = 182; ER[3] = 1; ER[4] = 9; ER[5] = 72; ER[6] = 93; ER[7] = 1; memcpy(&ER[8], "IEEE_1282", 9); memcpy(&ER[17], "THE IEEE 1282 PROTOCOL PROVIDES SUPPORT FOR POSIX " "FILE SYSTEM SEMANTICS.", 72); memcpy(&ER[89], "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, " "PISCATAWAY, NJ, USA FOR THE 1282 SPECIFICATION.", 93); susp_append(t, &dir->info.dir.self_susp, ER); } /* create a PX field from the permissions on the current node. */ uint8_t *rrip_make_PX(struct ecma119_write_target *t, struct ecma119_tree_node *node) { uint8_t *PX = malloc(44); PX[0] = 'P'; PX[1] = 'X'; PX[2] = 44; PX[3] = 1; iso_bb(&PX[4], node->attrib.st_mode, 4); iso_bb(&PX[12], node->attrib.st_nlink, 4); iso_bb(&PX[20], node->attrib.st_uid, 4); iso_bb(&PX[28], node->attrib.st_gid, 4); iso_bb(&PX[36], node->attrib.st_ino, 4); return PX; } /** See IEEE 1282 4.1.1 */ void rrip_add_PX(struct ecma119_write_target *t, struct ecma119_tree_node *node) { susp_append(t, &node->susp, rrip_make_PX(t, node)); if (node->type == ECMA119_DIR) { susp_append(t, &node->info.dir.self_susp, rrip_make_PX(t, node)); susp_append(t, &node->info.dir.parent_susp, rrip_make_PX(t, node)); } } void rrip_add_PN(struct ecma119_write_target *t, struct ecma119_tree_node *node) { uint8_t *PN = malloc(20); PN[0] = 'P'; PN[1] = 'N'; PN[2] = 20; PN[3] = 1; iso_bb(&PN[4], node->attrib.st_dev >> 32, 4); iso_bb(&PN[12], node->attrib.st_dev & 0xffffffff, 4); susp_append(t, &node->susp, PN); } static void rrip_SL_append_comp(int *n, uint8_t ***comps, char *s, int size, char fl) { uint8_t *comp = malloc(size + 2); (*n)++; comp[0] = fl; comp[1] = size; *comps = realloc(*comps, (*n) * sizeof(void*)); (*comps)[(*n) - 1] = comp; if (size) { memcpy(&comp[2], s, size); } } static void rrip_SL_add_component(char *prev, char *cur, int *n_comp, uint8_t ***comps) { int size = cur - prev; if (size == 0) { rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 3); return; } if (size == 1 && prev[0] == '.') { rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 1); return; } if (size == 2 && !strncmp(prev, "..", 2)) { rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 2); return; } /* we can't make a component any bigger than 250 (is this really a problem)? because then it won't fit inside the SL field */ while (size > 248) { size -= 248; rrip_SL_append_comp(n_comp, comps, prev, 248, 1 << 0); } rrip_SL_append_comp(n_comp, comps, prev, size, 0); } void rrip_add_SL(struct ecma119_write_target *t, struct ecma119_tree_node *node) { int path_size; char *path = NULL, *cur, *prev; int i, j; uint8_t **comp = NULL; int n_comp = 0; int total_comp_len = 0; int written = 0, pos; uint8_t *SL; path = node->info.dest; path_size = strlen(path); prev = path; for (cur = strchr(path, '/'); cur && *cur; cur = strchr(cur, '/')) { rrip_SL_add_component(prev, cur, &n_comp, &comp); cur++; prev = cur; } /* if there was no trailing '/', we need to add the last component. */ if (prev == path || prev != &path[path_size - 1]) { rrip_SL_add_component(prev, &path[path_size], &n_comp, &comp); } for (i = 0; i < n_comp; i++) { total_comp_len += comp[i][1] + 2; if (total_comp_len > 250) { total_comp_len -= comp[i][1] + 2; SL = malloc(total_comp_len + 5); SL[0] = 'S'; SL[1] = 'L'; SL[2] = total_comp_len + 5; SL[3] = 1; SL[4] = 1; /* CONTINUE */ pos = 5; for (j = written; j < i; j++) { memcpy(&SL[pos], comp[j], comp[j][2]); pos += comp[j][2]; } susp_append(t, &node->susp, SL); written = i - 1; total_comp_len = comp[i][1]; } } SL = malloc(total_comp_len + 5); SL[0] = 'S'; SL[1] = 'L'; SL[2] = total_comp_len + 5; SL[3] = 1; SL[4] = 0; pos = 5; for (j = written; j < n_comp; j++) { memcpy(&SL[pos], comp[j], comp[j][1] + 2); pos += comp[j][1] + 2; } susp_append(t, &node->susp, SL); /* free the components */ for (i = 0; i < n_comp; i++) { free(comp[i]); } free(comp); } static void rrip_add_NM_single(struct ecma119_write_target *t, struct susp_info *susp, char *name, int size, int flags) { uint8_t *NM = malloc(size + 5); NM[0] = 'N'; NM[1] = 'M'; NM[2] = size + 5; NM[3] = 1; NM[4] = flags; if (size) { memcpy(&NM[5], name, size); } susp_append(t, susp, NM); } void rrip_add_NM(struct ecma119_write_target *t, struct ecma119_tree_node *node) { char *name = node->full_name; int len = name ? strlen(name) : 0; char *pos = name; if (!len) return; if (node->type == ECMA119_DIR) { rrip_add_NM_single(t, &node->info.dir.self_susp, pos, 0, 1 << 1); rrip_add_NM_single(t, &node->info.dir.parent_susp, pos, 0, 1 << 2); } while (len > 250) { rrip_add_NM_single(t, &node->susp, pos, 250, 1); len -= 250; pos += 250; } rrip_add_NM_single(t, &node->susp, pos, len, 0); } void rrip_add_CL(struct ecma119_write_target *t, struct ecma119_tree_node *node) { uint8_t *CL = calloc(1, 12); CL[0] = 'C'; CL[1] = 'L'; CL[2] = 12; CL[3] = 1; susp_append(t, &node->susp, CL); } void rrip_add_PL(struct ecma119_write_target *t, struct ecma119_tree_node *node) { uint8_t *PL = calloc(1, 12); PL[0] = 'P'; PL[1] = 'L'; PL[2] = 12; PL[3] = 1; susp_append(t, &node->info.dir.parent_susp, PL); } void rrip_add_RE(struct ecma119_write_target *t, struct ecma119_tree_node *node) { uint8_t *RE = malloc(4); RE[0] = 'R'; RE[1] = 'E'; RE[2] = 4; RE[3] = 1; susp_append(t, &node->susp, RE); } void rrip_add_TF(struct ecma119_write_target *t, struct ecma119_tree_node *node) { uint8_t *TF = malloc(5 + 3 * 7); TF[0] = 'T'; TF[1] = 'F'; TF[2] = 5 + 3 * 7; TF[3] = 1; TF[4] = (1 << 1) | (1 << 2) | (1 << 3); iso_datetime_7(&TF[5], node->attrib.st_mtime); iso_datetime_7(&TF[12], node->attrib.st_atime); iso_datetime_7(&TF[19], node->attrib.st_ctime); susp_append(t, &node->susp, TF); } void rrip_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir) { int i; assert(dir->type == ECMA119_DIR); if (dir->parent != dir->info.dir.real_parent) { uint8_t *PL = susp_find(&dir->info.dir.parent_susp, "PL"); assert(PL); iso_bb(&PL[4], dir->info.dir.real_parent->info.dir.block, 4); } for (i = 0; i < dir->info.dir.nchildren; i++) { struct ecma119_tree_node *ch = dir->info.dir.children[i]; if (ch->type == ECMA119_PLACEHOLDER) { uint8_t *CL = susp_find(&ch->susp, "CL"); assert(CL); iso_bb(&CL[4], ch->info.real_me->info.dir.block, 4); } else if (ch->type == ECMA119_DIR) { rrip_finalize(t, ch); } } } /* vim: set noet ts=8 sts=8 sw=8 : */ /** * Functions and structures used for Rock Ridge support. * * See IEEE P1282, Rock Ridge Interchange Protocol, Draft Standard version * 1.12 for further details. */ #ifndef ISO_ROCKRIDGE_H #define ISO_ROCKRIDGE_H struct ecma119_write_target; struct ecma119_tree_node; /** * Add a SUSP "ER" System Use Entry to identify the Rock Ridge specification. * * The "ER" System Use Entry is used to uniquely identify a specification * compliant with SUSP. This method adds to the given tree node "." entry * the "ER" corresponding to the RR protocol. * * See IEEE P1281, section 5.5 and IEEE P1282, section 4.3 for more details. */ void rrip_add_ER(struct ecma119_write_target *, struct ecma119_tree_node *); /** * Add a PX System Use Entry to the given tree node and, if that node is * a directory, to its "." and ".." entries. The PX System Use Entry is * used to add POSIX file attributes, such as access permissions or user and * group id, to a ECMA 119 directory record. * * See IEEE P1282, section 4.1.1 for more details. */ void rrip_add_PX(struct ecma119_write_target *, struct ecma119_tree_node *); /** * Add a PN System Use Entry to the given tree node. * The PN System Use Entry is used to store the device number, and it's * mandatory if the tree node corresponds to a character or block device. * * See IEEE P1282, section 4.1.2 for more details. */ void rrip_add_PN(struct ecma119_write_target *, struct ecma119_tree_node *); /** * Add a SL System Use Entry to the given tree node. This is used to store * the content of a symbolic link, and is mandatory if the tree node * indicates a symbolic link. * * See IEEE P1282, section 4.1.3 for more details. */ void rrip_add_SL(struct ecma119_write_target *, struct ecma119_tree_node *); /** * Add a NM System Use Entry to the given tree node. The purpose of this * System Use Entry is to store the content of an Alternate Name to support * POSIX-style or other names. * * See IEEE P1282, section 4.1.4 for more details. */ void rrip_add_NM(struct ecma119_write_target *, struct ecma119_tree_node *); /* * The next 3 System Use Entries are used to handle Deep Directory * Hierarchies, i.e., hierarchies where the number of directory levels * exceed the eight limit of ECMA-119. */ /** * Add to the given tree node a CL System Use Entry, that is used to record * the new location of a directory which has been relocated. * * See IEEE P1282, section 4.1.5.1 for more details. */ void rrip_add_CL(struct ecma119_write_target *, struct ecma119_tree_node *); /** * Add a PL System Use Entry, used to record the location of the original * parent directory of a directory which has been relocated. * * This is special because it doesn't modify the susp fields of the directory * that gets passed to it; it modifies the susp fields of the ".." entry in * that directory. * * See IEEE P1282, section 4.1.5.2 for more details. */ void rrip_add_PL(struct ecma119_write_target *, struct ecma119_tree_node *); /** * Add a RE System Use Entry to the given tree node. The purpose of the * this System Use Entry is to indicate to an RRIP-compliant receiving * system that the Directory Record in which an "RE" System Use Entry is * recorded has been relocated from another position in the original * Directory Hierarchy. * * See IEEE P1282, section 4.1.5.3 for more details. */ void rrip_add_RE(struct ecma119_write_target *, struct ecma119_tree_node *); /** * Add to the given tree node a TF System Use Entry, used to record some * time stamps related to the file. * * See IEEE P1282, section 4.1.6 for more details. */ void rrip_add_TF(struct ecma119_write_target *, struct ecma119_tree_node *); void rrip_finalize(struct ecma119_write_target *, struct ecma119_tree_node *); #endif /* ISO_ROCKRIDGE_H */ /* vim: set noet ts=8 sts=8 sw=8 : */ #include "susp.h" #include "util.h" #include "ecma119.h" #include "ecma119_tree.h" #include #include #include void susp_insert(struct ecma119_write_target *t, struct susp_info *susp, uint8_t *data, int pos) { int i; if (pos < 0) { pos = susp->n_susp_fields; } assert(pos <= susp->n_susp_fields); susp->n_susp_fields++; susp->susp_fields = realloc(susp->susp_fields, sizeof(void*) * susp->n_susp_fields); for (i = susp->n_susp_fields-1; i > pos; i--) { susp->susp_fields[i] = susp->susp_fields[i - 1]; } susp->susp_fields[pos] = data; } void susp_append(struct ecma119_write_target *t, struct susp_info *susp, uint8_t *data) { susp_insert(t, susp, data, susp->n_susp_fields); } uint8_t *susp_find(struct susp_info *susp, const char *name) { int i; for (i = 0; i < susp->n_susp_fields; i++) { if (!strncmp((char *)susp->susp_fields[i], name, 2)) { return susp->susp_fields[i]; } } return NULL; } /** Utility function for susp_add_CE because susp_add_CE needs to act 3 times * on directories (for the "." and ".." entries. * * \param len The amount of space available for the System Use area. */ #define CE_LEN 28 static unsigned char *susp_add_single_CE(struct ecma119_write_target *t, struct susp_info *susp, int len) { int susp_length = 0, tmp_len; int i; unsigned char *CE; for (i = 0; i < susp->n_susp_fields; i++) { susp_length += susp->susp_fields[i][2]; } if (susp_length <= len) { /* no need for a CE field */ susp->non_CE_len = susp_length; susp->n_fields_fit = susp->n_susp_fields; return NULL; } tmp_len = susp_length; for (i = susp->n_susp_fields - 1; i >= 0; i--) { tmp_len -= susp->susp_fields[i][2]; if (tmp_len + CE_LEN <= len) { susp->non_CE_len = tmp_len + CE_LEN; susp->CE_len = susp_length - tmp_len; /* i+1 because we have to count the CE field */ susp->n_fields_fit = i + 1; CE = calloc(1, CE_LEN); /* we don't fill in the BLOCK LOCATION or OFFSET fields yet. */ CE[0] = 'C'; CE[1] = 'E'; CE[2] = (char)CE_LEN; CE[3] = (char)1; iso_bb(&CE[20], susp_length - tmp_len, 4); return CE; } } assert(0); return NULL; } static void try_add_CE(struct ecma119_write_target *t, struct susp_info *susp, size_t dirent_len) { uint8_t *CE = susp_add_single_CE(t, susp, 255 - dirent_len); if (CE) susp_insert(t, susp, CE, susp->n_fields_fit - 1); } /** See IEEE P1281 Draft Version 1.12/5.2. Because this function depends on the * length of the other SUSP fields, it should always be calculated last. */ void susp_add_CE(struct ecma119_write_target *t, struct ecma119_tree_node *node) { try_add_CE(t, &node->susp, node->dirent_len); if (node->type == ECMA119_DIR) { try_add_CE(t, &node->info.dir.self_susp, 34); try_add_CE(t, &node->info.dir.parent_susp, 34); } } /** See IEEE P1281 Draft Version 1.12/5.3 */ void susp_add_SP(struct ecma119_write_target *t, struct ecma119_tree_node *dir) { unsigned char *SP = malloc(7); assert(dir->type == ECMA119_DIR); SP[0] = 'S'; SP[1] = 'P'; SP[2] = (char)7; SP[3] = (char)1; SP[4] = 0xbe; SP[5] = 0xef; SP[6] = 0; susp_append(t, &dir->info.dir.self_susp, SP); } #if 0 /** See IEEE P1281 Draft Version 1.12/5.4 */ static void susp_add_ST(struct ecma119_write_target *t, struct iso_tree_node *node) { unsigned char *ST = malloc(4); ST[0] = 'S'; ST[1] = 'T'; ST[2] = (char)4; ST[3] = (char)1; susp_append(t, node, ST); } #endif /* calculate the location of the CE areas. Since CE areas don't need to be * aligned to a block boundary, we contatenate all CE areas from a single * directory and dump them immediately after all the directory records. * * Requires that the following be known: * - position of the current directory (dir->info.dir.block) * - length of the current directory (dir->info.dir.len) * - sum of the children's CE lengths (dir->info.dir.CE_len) */ static void susp_fin_1_CE(struct ecma119_write_target *t, struct susp_info *susp, size_t block, size_t *offset) { uint8_t *CE = susp->susp_fields[susp->n_fields_fit - 1]; if (!susp->CE_len) { return; } iso_bb(&CE[4], block + (*offset) / t->block_size, 4); iso_bb(&CE[12], (*offset) % t->block_size, 4); *offset += susp->CE_len; } static void susp_fin_CE(struct ecma119_write_target *t, struct ecma119_tree_node *dir) { int i; size_t CE_offset = dir->info.dir.len; assert(dir->type == ECMA119_DIR); susp_fin_1_CE(t, &dir->info.dir.self_susp, dir->info.dir.block, &CE_offset); susp_fin_1_CE(t, &dir->info.dir.parent_susp, dir->info.dir.block, &CE_offset); for (i = 0; i < dir->info.dir.nchildren; i++) { struct ecma119_tree_node *ch = dir->info.dir.children[i]; susp_fin_1_CE(t, &ch->susp, dir->info.dir.block, &CE_offset); } assert(CE_offset == dir->info.dir.len + dir->info.dir.CE_len); } void susp_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir) { int i; assert(dir->type = ECMA119_DIR); susp_fin_CE(t, dir); for (i = 0; i < dir->info.dir.nchildren; i++) { if (dir->info.dir.children[i]->type == ECMA119_DIR) susp_finalize(t, dir->info.dir.children[i]); } } void susp_write(struct ecma119_write_target *t, struct susp_info *susp, unsigned char *buf) { int i; int pos = 0; for (i = 0; i < susp->n_fields_fit; i++) { memcpy(&buf[pos], susp->susp_fields[i], susp->susp_fields[i][2]); pos += susp->susp_fields[i][2]; } } void susp_write_CE(struct ecma119_write_target *t, struct susp_info *susp, unsigned char *buf) { int i; int pos = 0; for (i = susp->n_fields_fit; i < susp->n_susp_fields; i++) { memcpy(&buf[pos], susp->susp_fields[i], susp->susp_fields[i][2]); pos += susp->susp_fields[i][2]; } } void susp_free_fields(struct susp_info *susp) { int i; for (i=0; in_susp_fields; i++) { free(susp->susp_fields[i]); } if (susp->susp_fields) { free(susp->susp_fields); } memset(susp, 0, sizeof(struct susp_info)); } /* vim: set noet ts=8 sts=8 sw=8 : */ /** * Functions and structures used for SUSP (IEEE 1281). * * Please refer to IEEE P1281 System Use Sharing Protocol, draft standard * version 1.12 for more details. */ #ifndef __ISO_SUSP #define __ISO_SUSP #include /* SUSP is only present in standard ecma119 */ struct ecma119_write_target; struct ecma119_tree_node; /** This contains the information that needs to go in the SUSP area of a file. */ struct susp_info { int n_susp_fields; /**< Number of SUSP fields */ uint8_t **susp_fields; /**< Data for each SUSP field */ /* the next 3 relate to CE and are filled out by susp_add_CE. */ int n_fields_fit; /**< How many of the above SUSP fields fit * within this node's dirent. */ int non_CE_len; /**< Length of the part of the SUSP area that * fits in the dirent. */ int CE_len; /**< Length of the part of the SUSP area that * will go in a CE area. */ }; /** * Add a CE System Use Entry to the given tree node. A "CE" is used to add * a continuation area, where additional System Use Entry can be written. * See IEEE P1281, section 5.1. */ void susp_add_CE(struct ecma119_write_target *, struct ecma119_tree_node *); /** * Add a SP System Use Entry to the "." entry of the directory. The SP provide * an identifier that the SUSP is used within the volume. The SP shall be * recorded in the "." entry of the root directory. * See IEEE P1281, section 5.3 for more details. * * this is special because it doesn't modify the susp fields of the * directory; it modifies the susp fields of the "." entry in the directory. */ void susp_add_SP(struct ecma119_write_target *, struct ecma119_tree_node *); /** Once all the directories and files are laid out, recurse through the tree * and finalize all SUSP CE entries. */ void susp_finalize(struct ecma119_write_target *, struct ecma119_tree_node *); void susp_append(struct ecma119_write_target *, struct susp_info *, uint8_t *); void susp_insert(struct ecma119_write_target *, struct susp_info *, uint8_t *, int pos); uint8_t *susp_find(struct susp_info *, const char *); void susp_write(struct ecma119_write_target *, struct susp_info *, uint8_t *); void susp_write_CE(struct ecma119_write_target *, struct susp_info *, uint8_t *); void susp_free_fields(struct susp_info *); #endif /* __ISO_SUSP */ /* vim: set noet ts=8 sts=8 sw=8 : */ /** * \file tree.c * * Implement filesystem trees. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "tree.h" #include "exclude.h" static void set_default_stat(struct stat *s) { time_t now = time(NULL); memset(s, 0, sizeof(struct stat)); s->st_mode = 0555; s->st_atime = s->st_mtime = s->st_ctime = now; } void iso_tree_add_child(struct iso_tree_node_dir *parent, struct iso_tree_node *child) { assert(parent && child); assert(!child->parent); parent->nchildren++; parent->children = realloc(parent->children, parent->nchildren * sizeof(void*)); parent->children[parent->nchildren-1] = child; child->parent = parent; } struct iso_tree_node_dir* iso_tree_new_root() { struct iso_tree_node_dir *root; root = calloc(1, sizeof(struct iso_tree_node_dir)); set_default_stat(&root->node.attrib); root->node.refcount = 1; root->node.attrib.st_mode = S_IFDIR | 0777; root->node.type = LIBISO_NODE_DIR; return root; } struct iso_tree_node* iso_tree_add_file(struct iso_tree_node_dir *parent, const char *path) { struct iso_tree_node_file *f; char *p; struct stat st; assert( parent && path); if (lstat(path, &st) == -1) { libisofs_errno = NO_FILE; return NULL; } if ( !S_ISREG(st.st_mode) ) { libisofs_errno = UNEXPECTED_FILE_TYPE; return NULL; } if ( access(path, R_OK) ) { libisofs_errno = NO_READ_ACCESS; return NULL; } f = calloc(1, sizeof(struct iso_tree_node_file)); /* fill fields */ f->node.refcount = 1; f->node.attrib = st; f->loc.path = strdup(path); f->node.type = LIBISO_NODE_FILE; p = strdup(path); /* because basename() might modify its arg */ f->node.name = strdup( basename(p) ); free(p); /* add to parent (this also sets f->node->parent) */ iso_tree_add_child(parent, (struct iso_tree_node*) f); return (struct iso_tree_node*) f; } struct iso_tree_node* iso_tree_add_symlink(struct iso_tree_node_dir *parent, const char *name, const char *dest) { struct iso_tree_node_symlink *link; assert( parent && name && dest); link = calloc(1, sizeof(struct iso_tree_node_symlink)); /* fill fields */ set_default_stat(&link->node.attrib); link->node.refcount = 1; link->node.attrib.st_mode |= S_IFLNK; link->node.name = strdup(name); link->node.type = LIBISO_NODE_SYMLINK; link->dest = strdup(dest); /* add to parent (this also sets link->node->parent) */ iso_tree_add_child(parent, (struct iso_tree_node*) link); return (struct iso_tree_node*) link; } struct iso_tree_node_dir* iso_tree_add_dir(struct iso_tree_node_dir *parent, const char *name) { struct iso_tree_node_dir *dir; assert( parent && name ); dir = calloc(1, sizeof(struct iso_tree_node_dir)); dir->node.refcount = 1; dir->node.attrib = parent->node.attrib; dir->node.type = LIBISO_NODE_DIR; dir->node.name = strdup(name); iso_tree_add_child(parent, (struct iso_tree_node*) dir); return dir; } enum iso_tree_node_type iso_tree_node_get_type(struct iso_tree_node *node) { assert(node); return node->type; } void iso_tree_node_set_name(struct iso_tree_node *node, const char *name) { free(node->name); node->name = strdup(name); } const char * iso_tree_node_get_name(struct iso_tree_node *node) { assert(node); return node->name; } void iso_tree_node_set_hidden(struct iso_tree_node *node, int hide_attrs) { assert(node); node->hide_flags = hide_attrs; } int iso_tree_node_is_hidden(struct iso_tree_node *node) { assert(node); return node->hide_flags; } void iso_tree_node_set_gid(struct iso_tree_node *node, gid_t gid) { assert(node); node->attrib.st_gid = gid; } gid_t iso_tree_node_get_gid(struct iso_tree_node *node) { assert(node); return node->attrib.st_gid; } void iso_tree_node_set_uid(struct iso_tree_node *node, uid_t uid) { assert(node); node->attrib.st_uid = uid; } uid_t iso_tree_node_get_uid(struct iso_tree_node *node) { assert(node); return node->attrib.st_uid; } void iso_tree_node_set_permissions(struct iso_tree_node *node, mode_t mode) { assert(node); node->attrib.st_mode = (node->attrib.st_mode & S_IFMT) | (mode & ~S_IFMT); } mode_t iso_tree_node_get_permissions(struct iso_tree_node *node) { assert(node); return node->attrib.st_mode & ~S_IFMT; } off_t iso_tree_node_get_size(struct iso_tree_node *node) { return node->attrib.st_size; } void iso_tree_node_set_mtime(struct iso_tree_node *node, time_t time) { node->attrib.st_mtime = time; } time_t iso_tree_node_get_mtime(struct iso_tree_node *node) { return node->attrib.st_mtime; } void iso_tree_node_set_atime(struct iso_tree_node *node, time_t time) { node->attrib.st_atime = time; } time_t iso_tree_node_get_atime(struct iso_tree_node *node) { return node->attrib.st_atime; } void iso_tree_node_set_ctime(struct iso_tree_node *node, time_t time) { node->attrib.st_ctime = time; } time_t iso_tree_node_get_ctime(struct iso_tree_node *node) { return node->attrib.st_ctime; } void iso_tree_node_set_sort_weight(struct iso_tree_node *node, int w) { assert(node); if ( ISO_ISDIR(node) ) { size_t i; struct iso_tree_node_dir *dir; dir = (struct iso_tree_node_dir *) node; for (i=0; i < dir->nchildren; i++) { iso_tree_node_set_sort_weight(dir->children[i], w); } } else if ( ISO_ISREG(node) ) { struct iso_tree_node_file *file; file = (struct iso_tree_node_file *) node; file->sort_weight = w; } } void iso_tree_node_symlink_set_dest(struct iso_tree_node_symlink *node, const char *dest) { assert(node && dest); free(node->dest); node->dest = strdup(dest); } const char * iso_tree_node_symlink_get_dest(struct iso_tree_node_symlink *node) { assert(node); return node->dest; } struct iso_tree_node* iso_tree_add_node(struct iso_tree_node_dir *parent, const char *path) { struct stat st; struct iso_tree_node *node; assert( parent && path); if (lstat(path, &st) == -1) { libisofs_errno = NO_FILE; return NULL; } if ( access(path, R_OK) ) { libisofs_errno = NO_READ_ACCESS; return NULL; } switch (st.st_mode & S_IFMT) { case S_IFREG: /* regular file */ node = iso_tree_add_file(parent, path); break; case S_IFLNK: /* symlink */ { char dest[PATH_MAX]; char *p; int n; n = readlink(path, dest, PATH_MAX); if ( n == -1 ) { libisofs_errno = INTERNAL_ERROR; return NULL; } dest[n] = '\0'; p = strdup(path); /* because basename() might modify its arg */ node = iso_tree_add_symlink(parent, basename(p), dest); free(p); node->attrib = st; } break; case S_IFDIR: /* directory */ { char *p; p = strdup(path); /* because basename() might modify its arg */ node = (struct iso_tree_node*) iso_tree_add_dir(parent, basename(p)); free(p); node->attrib = st; } break; default: libisofs_errno = UNEXPECTED_FILE_TYPE; node = NULL; break; } return node; } struct iso_tree_iter * iso_tree_node_children(struct iso_tree_node_dir *dir) { struct iso_tree_iter *iter; assert(dir); iter = malloc(sizeof(struct iso_tree_iter)); iter->dir = dir; iter->index = -1; return iter; } struct iso_tree_node * iso_tree_iter_next(struct iso_tree_iter *iter) { assert(iter); if ( ++iter->index < iter->dir->nchildren ) return iter->dir->children[iter->index]; else return NULL; } int iso_tree_iter_has_next(struct iso_tree_iter *iter) { assert(iter); return iter->index + 1 < iter->dir->nchildren; } void iso_tree_iter_free(struct iso_tree_iter *iter) { free(iter); } int iso_tree_node_take(struct iso_tree_node_dir *dir, struct iso_tree_node *node) { int i; assert(dir && node); /* search for the node in the dir */ for (i = 0; i < dir->nchildren; ++i) { if ( dir->children[i] == node ) break; } if (i < dir->nchildren) { int j; for (j = i+1; j < dir->nchildren; ++j) { dir->children[j-1] = dir->children[j]; } --dir->nchildren; dir->children = realloc(dir->children, dir->nchildren * sizeof(void*)); node->parent = NULL; return 0; } else { /* the node doesn't exist on dir */ return -1; } } int iso_tree_node_remove(struct iso_tree_node_dir *dir, struct iso_tree_node *node) { int res; assert(dir && node); res = iso_tree_node_take(dir, node); if (!res) iso_tree_free(node); return res; } int iso_tree_node_take_iter(struct iso_tree_iter *iter) { int j; struct iso_tree_node_dir *dir; struct iso_tree_node *node; assert(iter); dir = iter->dir; if (iter->index < 0) return -1; /* index before beginning */ if (iter->index >= dir->nchildren) return -2; /* index after end */ node = dir->children[iter->index]; node->parent = NULL; for (j = iter->index+1; j < dir->nchildren; ++j) { dir->children[j-1] = dir->children[j]; } --dir->nchildren; dir->children = realloc(dir->children, dir->nchildren * sizeof(void*)); /* update iter index */ --iter->index; return 0; } int iso_tree_node_remove_iter(struct iso_tree_iter *iter) { int j; struct iso_tree_node_dir *dir; struct iso_tree_node *node; assert(iter); dir = iter->dir; if (iter->index < 0) return -1; /* index before beginning */ if (iter->index >= dir->nchildren) return -2; /* index after end */ node = dir->children[iter->index]; for (j = iter->index+1; j < dir->nchildren; ++j) { dir->children[j-1] = dir->children[j]; } --dir->nchildren; dir->children = realloc(dir->children, dir->nchildren * sizeof(void*)); /* update iter index */ --iter->index; /* and free node */ node->parent = NULL; iso_tree_free(node); return 0; } struct iso_tree_node_dir * iso_tree_node_get_parent(struct iso_tree_node *node) { assert(node); return node->parent; } void iso_tree_node_ref(struct iso_tree_node *node) { ++node->refcount; } void iso_tree_free(struct iso_tree_node *root) { if (!root) return; if (--root->refcount < 1) { if ( ISO_ISDIR(root) ) { size_t i; struct iso_tree_node_dir *dir; dir = (struct iso_tree_node_dir *) root; for (i=0; i < dir->nchildren; i++) { iso_tree_free(dir->children[i]); } free(dir->children); } else if ( ISO_ISLNK(root) ) { struct iso_tree_node_symlink *link; link = (struct iso_tree_node_symlink *) root; free(link->dest); } else if ( ISO_ISREG(root) ) { struct iso_tree_node_file *file; file = (struct iso_tree_node_file *) root; if (root->procedence == LIBISO_NEW) free(file->loc.path); } else if ( ISO_ISBOOT(root) ) { struct iso_tree_node_boot *boot; boot = (struct iso_tree_node_boot *) root; if (root->procedence == LIBISO_NEW && boot->img) free(boot->loc.path); } free(root->name); free(root); } } static void iso_tree_radd_dir_aux(struct iso_tree_node_dir *parent, const char *path, struct iso_tree_radd_dir_behavior *behavior, struct iso_hash_table *excludes) { struct iso_tree_node *new; DIR *dir; struct dirent *ent; dir = opendir(path); if (!dir) { warn("couldn't open directory %s: %s\n", path, strerror(errno)); return; } while ((ent = readdir(dir))) { char child[strlen(ent->d_name) + strlen(path) + 2]; if (behavior->stop_on_error & behavior->error) break; if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; //TODO check if path already finished in '/' sprintf(child, "%s/%s", path, ent->d_name); /* see if this child is excluded. */ if (iso_exclude_lookup(excludes, child)) continue; new = iso_tree_add_node(parent, child); if (!new || !ISO_ISDIR(new)) { if (!new) behavior->error = 1; continue; } iso_tree_radd_dir_aux( (struct iso_tree_node_dir *) new, child, behavior, excludes); } closedir(dir); return; } void iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path, struct iso_tree_radd_dir_behavior *behavior) { struct iso_tree_node_dir *dir; struct iso_hash_table table = { {0,}, 0}; assert ( parent && path ); behavior->error = 0; /* initialize exclude hash_table */ if ( behavior->excludes ) { char *exclude; int i = 0; while ( (exclude = behavior->excludes[i++]) ) { iso_exclude_add_path(&table, exclude); } } /* recurse into dir */ iso_tree_radd_dir_aux(parent, path, behavior, &table); /* clear hashtable */ iso_exclude_empty(&table); } void iso_tree_print(const struct iso_tree_node *root, int spaces) { char sp[spaces+1]; memset(sp, ' ', spaces); sp[spaces] = '\0'; printf("%s%s\n", sp, root->name); if ( ISO_ISDIR(root) ) { size_t i; struct iso_tree_node_dir *dir; dir = (struct iso_tree_node_dir *) root; for (i=0; i < dir->nchildren; i++) { iso_tree_print(dir->children[i], spaces+2); } } } void iso_tree_print_verbose(const struct iso_tree_node *root, print_dir_callback dir, print_file_callback file, void *callback_data, int spaces) { (ISO_ISDIR(root) ? dir : file) (root, callback_data, spaces); if ( ISO_ISDIR(root) ) { size_t i; struct iso_tree_node_dir *dir_node; dir_node = (struct iso_tree_node_dir *) root; for (i=0; i < dir_node->nchildren; i++) { iso_tree_print_verbose(dir_node->children[i], dir, file, callback_data, spaces+2); } } } /* vim: set noet ts=8 sts=8 sw=8 : */ /** * \file tree.h * * Declare the structure of a libisofs filesystem tree. The files in this * tree can come from either the local filesystem or from another .iso image * (for multisession). * * This tree preserves as much information as it can about the files; names * are stored in UTF-8 and we preserve POSIX attributes. This tree does * *not* include information that is necessary for writing out, for example, * an ISO level 1 tree. That information will go in a different tree because * the structure is sufficiently different. */ #ifndef LIBISO_TREE_H #define LIBISO_TREE_H #include #include #include #include "libisofs.h" /** * A node in the filesystem tree. */ struct iso_tree_node { /* * Reference counter: * - When created, refcount is set to 1 * - One node is automatically free when free the tree (i.e., dir) it * belongs, and the tree is automatically freed when the volume it * belongs is also freed. * - If the user deon't add the tree to a volume, (s)he has to free the * tree. * - If the user doesn't add a node to a tree (dir), for example after * taken it with iso_tree_node_take(), it should free the node when * no more needed. * - If the user wants an own ref, it should call iso_tree_node_ref() * function to get that ref, and free the node when no more needed. */ int refcount; struct iso_tree_node_dir *parent; char *name; struct stat attrib; /**< The POSIX attributes of this node as * documented in "man 2 stat". */ int hide_flags; /**< If the node is to be hidden in RR/ISO or * Joilet tree */ enum tree_node_from procedence; /**< Procedence of the node. */ enum iso_tree_node_type type; /**< Type of the node. */ }; /** * A node in the filesystem tree that represents a regular file */ struct iso_tree_node_file { struct iso_tree_node node; int sort_weight; /**< It sorts the order in which the file data is * written to the CD image. Higher weighting files * are written at the beginning of image */ union { char *path; /**< the path of the file on local filesystem */ uint32_t block; /**< If the file is from a previous session. * Maybe we can put this in iso_tree_node later. */ } loc; }; /** * A node in the filesystem tree that represents a symbolic link */ struct iso_tree_node_symlink { struct iso_tree_node node; char *dest; /**< Destination of the link */ }; /** * A directory on the filesystem tree */ struct iso_tree_node_dir { struct iso_tree_node node; size_t nchildren; /**< The number of children of this * directory (if this is a directory). */ struct iso_tree_node **children; }; /** * Tree node that corresponds to some El-Torito artifact. * This can be either the boot catalog or a bootable image. */ struct iso_tree_node_boot { struct iso_tree_node node; unsigned int img:1; /*< 1 if img, 0 if catalog */ union { char *path; /**< the path of the file on local filesystem */ uint32_t block; /**< If the file is from a previous session.*/ } loc; }; /** * An iterator for directory children. */ struct iso_tree_iter { struct iso_tree_node_dir *dir; int index; }; /** * A function that prints verbose information about a directory. * * \param dir The directory about which to print information. * \param data Unspecified function-dependent data. * \param spaces The number of spaces to prepend to the output. * * \see iso_tree_print_verbose */ typedef void (*print_dir_callback) (const struct iso_tree_node *dir, void *data, int spaces); /** * A function that prints verbose information about a file. * * \param dir The file about which to print information. * \param data Unspecified function-dependent data. * \param spaces The number of spaces to prepend to the output. * * \see iso_tree_print_verbose */ typedef void (*print_file_callback) (const struct iso_tree_node *file, void *data, int spaces); /** * Recursively print a directory heirarchy. For each node in the directory * heirarchy, call a callback function to print information more verbosely. * * \param root The root of the directory heirarchy to print. * \param dir The callback function to call for each directory in the tree. * \param file The callback function to call for each file in the tree. * \param callback_data The data to pass to the callback functions. * \param spaces The number of spaces to prepend to the output. * * \pre \p root is not NULL. * \pre Neither of the callback functions modifies the directory heirarchy. */ void iso_tree_print_verbose(const struct iso_tree_node *root, print_dir_callback dir, print_file_callback file, void *callback_data, int spaces); #define ISO_ISDIR(n) (n->type == LIBISO_NODE_DIR) #define ISO_ISREG(n) (n->type == LIBISO_NODE_FILE) #define ISO_ISLNK(n) (n->type == LIBISO_NODE_SYMLINK) #define ISO_ISBOOT(n) (n->type == LIBISO_NODE_BOOT) #endif /* LIBISO_TREE_H */ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* vim: set noet ts=8 sts=8 sw=8 : */ /** * Utility functions for the Libisofs library. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "libisofs.h" /* avoids warning and names in iso, joliet and rockridge can't be > 255 bytes * anyway. There are at most 31 characters in iso level 1, 255 for rockridge, * 64 characters (* 2 since UCS) for joliet. */ #define NAME_BUFFER_SIZE 255 int div_up(int n, int div) { return (n + div - 1) / div; } int round_up(int n, int mul) { return div_up(n, mul) * mul; } /** * Convert a string between charsets. * This assumes '\0' means end-of-string, what is not necessarily true, * but given there are lots of strdup around there, it will fail in other * places anyway... */ char * convert_str(const char *str, const char *icharset, const char *ocharset) { char *ret; size_t inbytes; size_t outbytes; inbytes = strlen(str); outbytes = (inbytes+1) * MB_LEN_MAX; { /* ensure enought space */ char out[outbytes]; char *src; size_t n; iconv_t conv = iconv_open(ocharset, icharset); if (conv == (iconv_t)(-1)) { printf("Can't convert from %s to %s\n", icharset, ocharset); return NULL; } src = (char *)str; ret = (char *)out; n = iconv(conv, &src, &inbytes, &ret, &outbytes); if (n == -1) { /* error just return input stream */ perror("Convert error."); printf("Maybe string %s is not encoded in %s\n", str, icharset); iconv_close(conv); return strdup(str); } iconv_close(conv); *ret = '\0'; ret = strdup(out); } return ret; } /** * Convert a str in a specified codeset to WCHAR_T. * The result must be free() when no more needed */ static wchar_t *str2wchar(const char *str, const char *codeset) { iconv_t conv; size_t inbytes; size_t outbytes; char *ret; char *src; wchar_t *wstr; size_t n; conv = iconv_open("WCHAR_T", codeset); if (conv == (iconv_t)-1) { perror("Invalid encodings\n"); return NULL; } inbytes = strlen(str); outbytes = (inbytes + 1) * sizeof(wchar_t); /* we are sure that numchars <= inbytes */ wstr = malloc(outbytes); ret = (char *)wstr; src = (char *)str; n = iconv(conv, &src, &inbytes, &ret, &outbytes); while (n == -1) { if( errno != EINVAL ) { /* error, should never occur */ iconv_close(conv); perror("Convert error\n"); return NULL; } /* invalid input string charset, just log and ignore */ printf("String %s is not encoded in %s\n", str, codeset); inbytes--; if(!inbytes) break; n = iconv(conv, &src, &inbytes, &ret, &outbytes); } iconv_close(conv); *( (wchar_t *)ret )='\0'; return wstr; } /* this function must always return a name * since the caller never checks if a NULL * is returned. It also avoids some warnings. */ char *str2ascii(const char *src_arg, const char *icharset) { wchar_t *wsrc_; char *ret; char *ret_; char *src; iconv_t conv; size_t numchars; size_t outbytes; size_t inbytes; size_t n; assert(icharset); if (!src_arg) return NULL; /* convert the string to a wide character string. Note: outbytes * is in fact the number of characters in the string and doesn't * include the last NULL character. * * For now, just assume input to be in UTF-8, we can change * this later. */ wsrc_ = str2wchar(src_arg, icharset); if (!wsrc_) return NULL; src = (char *)wsrc_; numchars = wcslen(wsrc_); inbytes = numchars * sizeof(wchar_t); ret_ = malloc(numchars+1); outbytes = numchars; ret = ret_; /* initialize iconv */ conv = iconv_open("ASCII", "WCHAR_T"); if (conv == (iconv_t)-1) { free(wsrc_); return NULL; } n = iconv(conv, &src, &inbytes, &ret, &outbytes); while(n == -1) { /* The destination buffer is too small. Stops here. */ if(errno == E2BIG) break; /* An incomplete multi bytes sequence was found. We * can't do anything here. That's quite unlikely. */ if(errno == EINVAL) break; /* The last possible error is an invalid multi bytes * sequence. Just replace the character with a "_". * Probably the character doesn't exist in ascii like * "é, è, à, ç, ..." in French. */ *ret++ = '_'; outbytes--; if(!outbytes) break; /* There was an error with one character but some other remain * to be converted. That's probably a multibyte character. * See above comment. */ src += sizeof(wchar_t); inbytes -= sizeof(wchar_t); if(!inbytes) break; n = iconv(conv, &src, &inbytes, &ret, &outbytes); } iconv_close(conv); *ret='\0'; free(wsrc_); return ret_; } /* FIXME: C&P */ uint16_t *str2ucs(const char *src_arg, const char *icharset) { wchar_t *wsrc_; char *src; char *ret_; char *ret; iconv_t conv; size_t outbytes; size_t numchars; size_t inbytes; size_t n; if (!src_arg) return calloc(2, 1); /* empty UCS string */ /* convert the string to a wide character string. Note: outbytes * is in fact the number of characters in the string and doesn't * include the last NULL character. */ wsrc_ = str2wchar(src_arg, icharset); if (!wsrc_) return calloc(2, 1); /* empty UCS string */ src = (char*)wsrc_; numchars = wcslen(wsrc_); inbytes = numchars * sizeof(wchar_t); outbytes = numchars * sizeof(uint16_t); ret_ = malloc ((numchars+1) * sizeof(uint16_t)); ret = ret_; /* initialize iconv */ conv = iconv_open("UCS-2BE", "WCHAR_T"); if (conv == (iconv_t)-1) return calloc(2, 1); /* empty UCS string */ n = iconv(conv, &src, &inbytes, &ret, &outbytes); while(n == -1) { /* The destination buffer is too small. Stops here. */ if(errno == E2BIG) break; /* An incomplete multi bytes sequence was found. We * can't do anything here. That's quite unlikely. */ if(errno == EINVAL) break; /* The last possible error is an invalid multi bytes * sequence. Just replace the character with a "_". * Probably the character doesn't exist in ascii like * "é, è, à, ç, ..." in French. */ *((uint16_t*) ret) = '_'; ret += sizeof(uint16_t); outbytes -= sizeof(uint16_t); if(!outbytes) break; /* There was an error with one character but some other remain * to be converted. That's probably a multibyte character. * See above comment. */ src += sizeof(wchar_t); inbytes -= sizeof(wchar_t); if(!inbytes) break; n = iconv(conv, &src, &inbytes, &ret, &outbytes); } iconv_close(conv); /* close the ucs string */ *((uint16_t*) ret) = 0; return (uint16_t*)ret_; } static int valid_d_char(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_'); } static int valid_a_char(char c) { return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?') || (c >= 'A' && c <= 'Z') || (c == '_'); } static int valid_j_char(uint16_t c) { return !(c < (uint16_t)' ' || c == (uint16_t)'*' || c == (uint16_t)'/' || c == (uint16_t)':' || c == (uint16_t)';' || c == (uint16_t)'?' || c == (uint16_t)'\\'); } /* FIXME: where are these documented? */ static int valid_p_char(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '.') || (c == '_') || (c == '-'); } char *str2d_char(const char *str, const char *icharset) { char *ret; size_t len, i; if (!str) return NULL; ret = str2ascii(str, icharset); len = strlen(ret); for (i = 0; i < len; ++i) { char c = toupper(ret[i]); ret[i] = valid_d_char(c) ? c : '_'; } return ret; } char *str2a_char(const char *str, const char *icharset) { char *ret; size_t len, i; if (!str) return NULL; ret = str2ascii(str, icharset); len = strlen(ret); for (i = 0; i < len; ++i) { char c = toupper(ret[i]); ret[i] = valid_a_char(c) ? c : '_'; } return ret; } static char *iso_dirid(const char *src, int size, const char *icharset) { char *ret = str2ascii(src, icharset); size_t len, i; if (!ret) return NULL; len = strlen(ret); if (len > size) { ret[size] = '\0'; len = size; } for (i = 0; i < len; i++) { char c = toupper(ret[i]); ret[i] = valid_d_char(c) ? c : '_'; } return ret; } char *iso_1_dirid(const char *src, const char *icharset) { return iso_dirid(src, 8, icharset); } char *iso_2_dirid(const char *src, const char *icharset) { return iso_dirid(src, 31, icharset); } char *iso_r_dirid(const char *src, const char *icharset, int flags) { char *ret = str2ascii(src, icharset); size_t size, len, i; if (!ret) return NULL; size = flags & ECMA119_37_CHAR_FILENAMES ? 37 : 31; len = strlen(ret); if (len > size) { ret[size] = '\0'; len = size; } if (flags & ECMA119_RELAXED_FILENAMES) return ret; for (i = 0; i < len; i++) { char c = toupper(ret[i]); ret[i] = valid_d_char(c) ? c : '_'; } return ret; } char *iso_1_fileid(const char *src_arg, const char *icharset) { char *src = str2ascii(src_arg, icharset); char *dest; char *dot; /* Position of the last dot in the filename, will be used to calculate lname and lext. */ int lname, lext, pos, i; if (!src) return NULL; dest = malloc(15); /* 15 = 8 (name) + 1 (.) + 3 (ext) + 2 (;1) + 1 (\0) */ dot = strrchr(src, '.'); lext = dot ? strlen(dot + 1) : 0; lname = strlen(src) - lext - (dot ? 1 : 0); /* If we can't build a filename, return NULL. */ if (lname == 0 && lext == 0) { free(src); free(dest); return NULL; } pos = 0; /* Convert up to 8 characters of the filename. */ for (i = 0; i < lname && i < 8; i++) { char c = toupper(src[i]); dest[pos++] = valid_d_char(c) ? c : '_'; } /* This dot is mandatory, even if there is no extension. */ dest[pos++] = '.'; /* Convert up to 3 characters of the extension, if any. */ for (i = 0; i < lext && i < 3; i++) { char c = toupper(src[lname + 1 + i]); dest[pos++] = valid_d_char(c) ? c : '_'; } /* File versions are mandatory, even if they aren't used. */ dest[pos++] = ';'; dest[pos++] = '1'; dest[pos] = '\0'; dest = (char *)realloc(dest, pos + 1); free(src); return dest; } char *iso_2_fileid(const char *src_arg, const char *icharset) { char *src = str2ascii(src_arg, icharset); char *dest; char *dot; int lname, lext, lnname, lnext, pos, i; if (!src) return NULL; dest = malloc(34); /* 34 = 30 (name + ext) + 1 (.) + 2 (;1) + 1 (\0) */ dot = strrchr(src, '.'); /* Since the maximum length can be divided freely over the name and extension, we need to calculate their new lengths (lnname and lnext). If the original filename is too long, we start by trimming the extension, but keep a minimum extension length of 3. */ if (dot == NULL || *(dot + 1) == '\0') { lname = strlen(src); lnname = (lname > 30) ? 30 : lname; lext = lnext = 0; } else { lext = strlen(dot + 1); lname = strlen(src) - lext - 1; lnext = (strlen(src) > 31 && lext > 3) ? (lname < 27 ? 30 - lname : 3) : lext; lnname = (strlen(src) > 31) ? 30 - lnext : lname; } if (lnname == 0 && lnext == 0) { free(src); free(dest); return NULL; } pos = 0; /* Convert up to lnname characters of the filename. */ for (i = 0; i < lnname; i++) { char c = toupper(src[i]); dest[pos++] = valid_d_char(c) ? c : '_'; } dest[pos++] = '.'; /* Convert up to lnext characters of the extension, if any. */ for (i = 0; i < lnext; i++) { char c = toupper(src[lname + 1 + i]); dest[pos++] = valid_d_char(c) ? c : '_'; } dest[pos++] = ';'; dest[pos++] = '1'; dest[pos] = '\0'; dest = (char *)realloc(dest, pos + 1); free(src); return dest; } char * iso_r_fileid(const char *src_arg, const char *icharset, int flag) { char *src = str2ascii(src_arg, icharset); char *dest; char *dot; int lname, lext, lnname, lnext, pos, i; size_t max; size_t size = flag & (1<<1) ? 37 : 33; if (!src) return NULL; dest = malloc(size+1); if (flag & ECMA119_RELAXED_FILENAMES) { strncpy(dest, src, size); dest[size] = '\0'; /* ensure 37 / 33 max length */ pos = strlen(dest); pos = pos < (size == 37 ? 37 : 31) ? pos : (size == 37 ? 37 : 31); if ( !(flag & ECMA119_OMIT_VERSION_NUMBERS) ) { dest[pos++] = ';'; dest[pos++] = '1'; } dest[pos] = '\0'; return dest; } /* no relaxed filenames */ dot = strrchr(src, '.'); max = (size == 37 ? 36 : 30); /* Since the maximum length can be divided freely over the name and extension, we need to calculate their new lengths (lnname and lnext). If the original filename is too long, we start by trimming the extension, but keep a minimum extension length of 3. */ if (dot == NULL || *(dot + 1) == '\0') { lname = strlen(src); lnname = (lname > max) ? max : lname; lext = lnext = 0; } else { lext = strlen(dot + 1); lname = strlen(src) - lext - 1; lnext = (strlen(src) > max + 1 && lext > 3) ? (lname < max - 3 ? max - lname : 3) : lext; lnname = (strlen(src) > max +1) ? max - lnext : lname; } if (lnname == 0 && lnext == 0) { free(src); free(dest); return NULL; } pos = 0; /* Convert up to lnname characters of the filename. */ for (i = 0; i < lnname; i++) { char c = toupper(src[i]); dest[pos++] = valid_d_char(c) ? c : '_'; } dest[pos++] = '.'; /* Convert up to lnext characters of the extension, if any. */ for (i = 0; i < lnext; i++) { char c = toupper(src[lname + 1 + i]); dest[pos++] = valid_d_char(c) ? c : '_'; } if ( !(flag & ECMA119_OMIT_VERSION_NUMBERS) ) { dest[pos++] = ';'; dest[pos++] = '1'; } dest[pos] = '\0'; dest = (char *)realloc(dest, pos + 1); free(src); return dest; } char * iso_p_fileid(const char *src, const char *icharset) { char *ret = str2ascii(src, icharset); size_t i, len; if (!ret) return NULL; len = strlen(ret); for (i = 0; i < len; i++) { if (!valid_p_char(ret[i])) { ret[i] = (uint16_t)'_'; } } return ret; } uint16_t * iso_j_id(const char *src_arg, const char *icharset) { uint16_t *j_str = str2ucs(src_arg, icharset); size_t len = ucslen(j_str); size_t n; if (len > 128) { j_str[128] = '\0'; len = 128; } for (n = 0; n < len; n++) if (!valid_j_char(j_str[n])) j_str[n] = '_'; return j_str; } void iso_lsb(uint8_t *buf, uint32_t num, int bytes) { int i; assert(bytes <= 4); for (i = 0; i < bytes; ++i) buf[i] = (num >> (8 * i)) & 0xff; } void iso_msb(uint8_t *buf, uint32_t num, int bytes) { int i; assert(bytes <= 4); for (i = 0; i < bytes; ++i) buf[bytes - 1 - i] = (num >> (8 * i)) & 0xff; } void iso_bb(uint8_t *buf, uint32_t num, int bytes) { iso_lsb(buf, num, bytes); iso_msb(buf+bytes, num, bytes); } void iso_datetime_7(unsigned char *buf, time_t t) { static int tzsetup = 0; int tzoffset; struct tm tm; if (!tzsetup) { tzset(); tzsetup = 1; } localtime_r(&t, &tm); buf[0] = tm.tm_year; buf[1] = tm.tm_mon + 1; buf[2] = tm.tm_mday; buf[3] = tm.tm_hour; buf[4] = tm.tm_min; buf[5] = tm.tm_sec; #ifdef HAVE_TM_GMTOFF tzoffset = tm.tm_gmtoff / 60 / 15; #else tzoffset = timezone / 60 / 15; #endif if (tzoffset > 52) tzoffset -= 101; buf[6] = tzoffset; } time_t iso_datetime_read_7(const uint8_t *buf) { struct tm tm; tm.tm_year = buf[0]; tm.tm_mon = buf[1] - 1; tm.tm_mday = buf[2]; tm.tm_hour = buf[3]; tm.tm_min = buf[4]; tm.tm_sec = buf[5]; return timegm(&tm) - buf[6] * 60 * 15; } void iso_datetime_17(unsigned char *buf, time_t t) { static int tzsetup = 0; static int tzoffset; struct tm tm; if (t == (time_t) - 1) { /* unspecified time */ memset(buf, '0', 16); buf[16] = 0; } else { if (!tzsetup) { tzset(); tzsetup = 1; } localtime_r(&t, &tm); sprintf((char*)&buf[0], "%04d", tm.tm_year + 1900); sprintf((char*)&buf[4], "%02d", tm.tm_mon + 1); sprintf((char*)&buf[6], "%02d", tm.tm_mday); sprintf((char*)&buf[8], "%02d", tm.tm_hour); sprintf((char*)&buf[10], "%02d", tm.tm_min); sprintf((char*)&buf[12], "%02d", MIN(59, tm.tm_sec)); memcpy(&buf[14], "00", 2); #ifdef HAVE_TM_GMTOFF tzoffset = tm.tm_gmtoff / 60 / 15; #else tzoffset = timezone / 60 / 15; #endif if (tzoffset > 52) tzoffset -= 101; buf[16] = tzoffset; } } time_t iso_datetime_read_17(const uint8_t *buf) { struct tm tm; sscanf((char*)&buf[0], "%4d", &tm.tm_year); sscanf((char*)&buf[4], "%2d", &tm.tm_mon); sscanf((char*)&buf[6], "%2d", &tm.tm_mday); sscanf((char*)&buf[8], "%2d", &tm.tm_hour); sscanf((char*)&buf[10], "%2d", &tm.tm_min); sscanf((char*)&buf[12], "%2d", &tm.tm_sec); tm.tm_year -= 1900; tm.tm_mon -= 1; return timegm(&tm) - buf[16] * 60 * 15; } size_t ucslen(const uint16_t *str) { int i; for (i=0; str[i]; i++) ; return i; } /** * Although each character is 2 bytes, we actually compare byte-by-byte * (thats what the spec says). */ int ucscmp(const uint16_t *s1, const uint16_t *s2) { const char *s = (const char*)s1; const char *t = (const char*)s2; size_t len1 = ucslen(s1); size_t len2 = ucslen(s2); size_t i, len = MIN(len1, len2) * 2; for (i=0; i < len; i++) { if (s[i] < t[i]) { return -1; } else if (s[i] > t[i]) { return 1; } } if (len1 < len2) return -1; else if (len1 > len2) return 1; return 0; } uint32_t iso_read_lsb(const uint8_t *buf, int bytes) { int i; uint32_t ret = 0; for (i=0; i 0; --len) str[len] = '\0'; return str; } char *ucs2str(const char *buf, size_t len) { size_t outbytes, inbytes; char *str; inbytes = len; outbytes = (inbytes+1) * MB_LEN_MAX; { /* ensure enought space */ char out[outbytes]; char *src; iconv_t conv; size_t n; /* convert to local charset */ setlocale(LC_CTYPE, ""); conv = iconv_open(nl_langinfo(CODESET), "UCS-2BE"); if (conv == (iconv_t)(-1)) { printf("Can't convert from %s to %s\n", "UCS-2BE", nl_langinfo(CODESET)); return NULL; } src = (char *)buf; str = (char *)out; n = iconv(conv, &src, &inbytes, &str, &outbytes); if (n == -1) { /* error just return input stream */ perror("Convert error."); printf("Maybe string is not encoded in UCS-2BE.\n"); iconv_close(conv); return NULL; } iconv_close(conv); *str = '\0'; /* remove trailing spaces */ for (len = strlen(out) - 1; out[len] == ' ' && len > 0; --len) out[len] = '\0'; str = strdup(out); } return str; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* vim: set noet ts=8 sts=8 sw=8 : */ /** * Utility functions for the Libisofs library. */ #ifndef LIBISO_UTIL_H #define LIBISO_UTIL_H #include #include #include #ifndef MAX # define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif #ifndef MIN # define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif extern inline int div_up(int n, int div) { return (n + div - 1) / div; } extern inline int round_up(int n, int mul) { return div_up(n, mul) * mul; } char *convert_str(const char *str, const char *icharset, const char *ocharset); wchar_t *towcs(const char *); char *str2ascii(const char*, const char *); uint16_t *str2ucs(const char *, const char *); char *str2d_char(const char*, const char *); char *str2a_char(const char*, const char *); /** * Create a level 1 directory identifier. */ char *iso_1_dirid(const char *src, const char *); /** * Create a level 2 directory identifier. */ char *iso_2_dirid(const char *src, const char *); /** * Create a directory identifier with relaxed constraints */ char *iso_r_dirid(const char *src, const char *icharset, int flags); /** * Create a level 1 file identifier that consists of a name, extension and * version number. The resulting string will have a file name of maximum * length 8, followed by a separator (.), an optional extension of maximum * length 3, followed by a separator (;) and a version number (digit 1). * @return NULL if the original name and extension both are of length 0. */ char *iso_1_fileid(const char *src, const char *); /** * Create a level 2 file identifier that consists of a name, extension and * version number. The combined file name and extension length will not exceed * 30, the name and extension will be separated (.), and the extension will be * followed by a separator (;) and a version number (digit 1). * @return NULL if the original name and extension both are of length 0. */ char *iso_2_fileid(const char *src, const char *); /** * Create a file identifier with relaxed constraints. */ char *iso_r_fileid(const char *src, const char *icharset, int flags); /** * Create a Joliet file or directory identifier that consists of a name, * extension and version number. The combined name and extension length will * not exceed 128 bytes, the name and extension will be separated (.), * and the extension will be followed by a separator (;) and a version number * (digit 1). All characters consist of 2 bytes and the resulting string is * NULL-terminated by a 2-byte NULL. Requires the locale to be set correctly. * * @param size will be set to the size (in bytes) of the identifier. * @return NULL if the original name and extension both are of length 0 or the conversion from the current codeset to UCS-2BE is not available. */ uint16_t *iso_j_id(const char *src, const char *icharset); /** * FIXME: what are the requirements for these next two? Is this for RR? * * Create a POSIX portable file name that consists of a name and extension. * The resulting file name will not exceed 250 characters. * @return NULL if the original name and extension both are of length 0. */ char *iso_p_fileid(const char *src, const char *); /** * Create a POSIX portable directory name. * The resulting directory name will not exceed 250 characters. * @return NULL if the original name is of length 0. */ char *iso_p_dirid(const char *src); void iso_lsb(uint8_t *buf, uint32_t num, int bytes); void iso_msb(uint8_t *buf, uint32_t num, int bytes); void iso_bb(uint8_t *buf, uint32_t num, int bytes); uint32_t iso_read_lsb(const uint8_t *buf, int bytes); uint32_t iso_read_msb(const uint8_t *buf, int bytes); /** * if error != NULL it will be set to 1 if LSB and MSB integers * don't match */ uint32_t iso_read_bb(const uint8_t *buf, int bytes, int *error); /** Records the date/time into a 7 byte buffer (9.1.5) */ void iso_datetime_7(uint8_t *buf, time_t t); /** Records the date/time into a 17 byte buffer (8.4.26.1) */ void iso_datetime_17(uint8_t *buf, time_t t); time_t iso_datetime_read_7(const uint8_t *buf); time_t iso_datetime_read_17(const uint8_t *buf); /** * Like strlen, but for Joliet strings. */ size_t ucslen(const uint16_t *str); /** * Like strcmp, but for Joliet strings. */ int ucscmp(const uint16_t *s1, const uint16_t *s2); /** * Copy up to \p len chars from \p buf and return this newly allocated * string. The new string is null-terminated. */ char *strcopy(const char *buf, size_t len); /** * Convert a Joliet string with a length of \p len bytes to a new string * in local charset. */ char *ucs2str(const char *buf, size_t len); #endif /* LIBISO_UTIL_H */ /* -*- 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 "libisofs.h" #include "tree.h" #include "util.h" #include "volume.h" #include "eltorito.h" struct iso_volset* iso_volset_new(struct iso_volume *vol, const char *id) { struct iso_volset *volset = calloc(1, sizeof(struct iso_volset)); volset->volset_size = 1; volset->refcount = 1; volset->volume = malloc(sizeof(void *)); volset->volume[0] = vol; volset->volset_id = strdup(id); return volset; } void iso_volset_ref(struct iso_volset *volset) { ++volset->refcount; } void iso_volset_free(struct iso_volset *volset) { if (--volset->refcount < 1) { int i; for (i = 0; i < volset->volset_size; i++) { iso_volume_free(volset->volume[i]); } free(volset->volume); free(volset->volset_id); free(volset); } } struct iso_volume * iso_volset_get_volume(struct iso_volset *volset, int volnum) { assert(volset); if (volnum < volset->volset_size) return volset->volume[volnum]; else return NULL; } struct iso_volume* iso_volume_new(const char *volume_id, const char *publisher_id, const char *data_preparer_id) { return iso_volume_new_with_root(volume_id, publisher_id, data_preparer_id, NULL); } struct iso_volume* iso_volume_new_with_root(const char *volume_id, const char *publisher_id, const char *data_preparer_id, struct iso_tree_node_dir *root) { struct iso_volume *volume; volume = calloc(1, sizeof(struct iso_volume)); volume->refcount = 1; volume->root = root ? root : iso_tree_new_root(); if (volume_id != NULL) volume->volume_id = strdup(volume_id); if (publisher_id != NULL) volume->publisher_id = strdup(publisher_id); if (data_preparer_id != NULL) volume->data_preparer_id = strdup(data_preparer_id); return volume; } void iso_volume_free(struct iso_volume *volume) { /* Only free if no references are in use. */ if (--volume->refcount < 1) { iso_tree_free( (struct iso_tree_node*) volume->root); free(volume->volume_id); free(volume->publisher_id); free(volume->data_preparer_id); free(volume->system_id); free(volume->application_id); free(volume->copyright_file_id); free(volume->abstract_file_id); free(volume->biblio_file_id); if (volume->bootcat) el_torito_boot_catalog_free(volume->bootcat); free(volume); } } struct iso_tree_node_dir * iso_volume_get_root(const struct iso_volume *volume) { assert(volume); return volume->root; } void iso_volume_set_volume_id(struct iso_volume *volume, const char *volume_id) { assert(volume); free(volume->volume_id); volume->volume_id = strdup(volume_id); } const char * iso_volume_get_volume_id(struct iso_volume *volume) { assert(volume); return volume->volume_id; } void iso_volume_set_publisher_id(struct iso_volume *volume, const char *publisher_id) { assert(volume); free(volume->publisher_id); volume->publisher_id = strdup(publisher_id); } const char * iso_volume_get_publisher_id(struct iso_volume *volume) { assert(volume); return volume->publisher_id; } void iso_volume_set_data_preparer_id(struct iso_volume *volume, const char *data_preparer_id) { assert(volume); free(volume->data_preparer_id); volume->data_preparer_id = strdup(data_preparer_id); } const char * iso_volume_get_data_preparer_id(struct iso_volume *volume) { assert(volume); return volume->data_preparer_id; } void iso_volume_set_system_id(struct iso_volume *volume, const char *system_id) { assert(volume); free(volume->system_id); volume->system_id = strdup(system_id); } const char * iso_volume_get_system_id(struct iso_volume *volume) { assert(volume); return volume->system_id; } void iso_volume_set_application_id(struct iso_volume *volume, const char *application_id) { assert(volume); free(volume->application_id); volume->application_id = strdup(application_id); } const char * iso_volume_get_application_id(struct iso_volume *volume) { assert(volume); return volume->application_id; } void iso_volume_set_copyright_file_id(struct iso_volume *volume, const char *copyright_file_id) { assert(volume); free(volume->copyright_file_id); volume->copyright_file_id = strdup(copyright_file_id); } const char * iso_volume_get_copyright_file_id(struct iso_volume *volume) { assert(volume); return volume->copyright_file_id; } void iso_volume_set_abstract_file_id(struct iso_volume *volume, const char *abstract_file_id) { assert(volume); free(volume->abstract_file_id); volume->abstract_file_id = strdup(abstract_file_id); } const char * iso_volume_get_abstract_file_id(struct iso_volume *volume) { assert(volume); return volume->abstract_file_id; } void iso_volume_set_biblio_file_id(struct iso_volume *volume, const char *biblio_file_id) { assert(volume); free(volume->biblio_file_id); volume->biblio_file_id = strdup(biblio_file_id); } const char * iso_volume_get_biblio_file_id(struct iso_volume *volume) { assert(volume); return volume->biblio_file_id; } struct iso_tree_node * iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path) { struct iso_tree_node *node; struct iso_tree_node_dir *dir; char *ptr, *brk_info, *component; /* get the first child at the root of the volume * that is "/" */ dir = iso_volume_get_root(volume); node = (struct iso_tree_node *)dir; if (!strcmp(path, "/")) return node; if (!dir->nchildren) return NULL; ptr = strdup(path); /* get the first component of the path */ component=strtok_r(ptr, "/", &brk_info); while (component) { size_t max; size_t i; if ( !ISO_ISDIR(node) ) { node=NULL; break; } dir = (struct iso_tree_node_dir *)node; /* search among all the children of this directory if this path component exists */ max=dir->nchildren; for (i=0; i < max; i++) { if (!strcmp(component, dir->children[i]->name)) { node=dir->children[i]; break; } } /* see if a node could be found */ if (i==max) { node=NULL; break; } component=strtok_r(NULL, "/", &brk_info); } free(ptr); return node; } // //struct iso_tree_node * //iso_tree_volume_add_path(struct iso_volume *volume, // const char *disc_path, // const char *path) //{ // char *tmp; // struct iso_tree_node *node; // struct iso_tree_node *parent_node; // // tmp=strdup(disc_path); // parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp)); // free(tmp); // // if (!parent_node) // return NULL; // // node = iso_tree_radd_dir(parent_node, path); // if (!node) // return NULL; // // tmp=strdup(disc_path); // iso_tree_node_set_name(node, basename(tmp)); // free(tmp); // // return node; //} // //struct iso_tree_node * //iso_tree_volume_add_new_dir(struct iso_volume *volume, // const char *disc_path) //{ // char *tmp; // struct iso_tree_node *node; // struct iso_tree_node *parent_node; // // tmp=strdup(disc_path); // parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp)); // free(tmp); // // if (!parent_node) // return NULL; // // tmp=strdup(disc_path); // node = iso_tree_add_new_dir(parent_node, basename(tmp)); // free(tmp); // // return node; //} /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* vim: set noet sts=8 ts=8 sw=8 : */ /** * Extra declarations for use with the iso_volume structure. */ #ifndef LIBISO_VOLUME_H #define LIBISO_VOLUME_H #include "libisofs.h" /** * Data volume. */ struct iso_volume { int refcount; /**< Number of used references to this volume. */ struct iso_tree_node_dir *root; /**< Root of the directory tree for the volume. */ char *volume_id; /**< Volume identifier. */ char *publisher_id; /**< Volume publisher. */ char *data_preparer_id; /**< Volume data preparer. */ char *system_id; /**< Volume system identifier. */ char *application_id; /**< Volume application id */ char *copyright_file_id; char *abstract_file_id; char *biblio_file_id; struct el_torito_boot_catalog *bootcat; /**< El-Torito boot catalog */ }; /** * A set of data volumes. */ struct iso_volset { int refcount; struct iso_volume **volume; /**< The volumes belonging to this volume set. */ int volset_size; /**< The number of volumes in this volume set. */ char *volset_id; /**< The id of this volume set, encoded in the current locale. */ }; #endif /* __ISO_VOLUME */ #!/bin/bash TEST_ROOT=/tmp/libisofs_test rm -rf $TEST_ROOT #create test folders mkdir -p $TEST_ROOT mkdir -p $TEST_ROOT/dir1/dir11 chmod 755 $TEST_ROOT/dir1 chmod 755 $TEST_ROOT/dir1/dir11 touch $TEST_ROOT/dir1/dir11/a echo "This file is to check correct file permissions. set them to 754" > $TEST_ROOT/dir1/permtest chmod 754 $TEST_ROOT/dir1/permtest mkdir -p $TEST_ROOT/dir2 ln -s $TEST_ROOT/dir1 "$TEST_ROOT/link to dir1" echo "README file" > $TEST_ROOT/README chmod 555 $TEST_ROOT/README ln -s $TEST_ROOT/README "$TEST_ROOT/link to readme" echo "No read file" > $TEST_ROOT/no_read chmod 000 $TEST_ROOT/no_read if ! make check then exit 1 fi test/test all clean: $(MAKE) -C .. -$(MAKEFLAGS) $@ .PHONY: all clean /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* vim: set ts=8 sts=8 sw=8 noet : */ #define _GNU_SOURCE #include "libisofs.h" #include "libburn/libburn.h" #include #include #include #include #include #include #include #include #include #include #include #include #define SECSIZE 2048 const char * const optstring = "JRL:b:hV:"; extern char *optarg; extern int optind; void usage(char **argv) { printf("%s [OPTIONS] DIRECTORY OUTPUT\n", argv[0]); help(); } void help() { printf( "Options:\n" " -J Add Joliet support\n" " -R Add Rock Ridge support\n" " -V label Volume Label\n" " -L Set the ISO level (1 or 2)\n" " -b file Specifies a boot image to add to image\n" " -h Print this message\n" ); } int main(int argc, char **argv) { struct ecma119_source_opts opts; struct iso_volset *volset; struct iso_volume *volume; struct iso_tree_node_dir *root; struct burn_source *src; unsigned char buf[2048]; FILE *fd; int c; int constraints; struct iso_tree_radd_dir_behavior behav = {0,0,0}; int level=1, flags=0; char *boot_img = NULL; char *volid = "VOLID"; while ((c = getopt(argc, argv, optstring)) != -1) { switch(c) { case 'h': usage(argv); help(); exit(0); break; case 'J': flags |= ECMA119_JOLIET; break; case 'R': flags |= ECMA119_ROCKRIDGE; break; case 'L': level = atoi(optarg); break; case 'b': boot_img = optarg; break; case 'V': volid = optarg; break; case '?': usage(argv); exit(1); break; } } if (argc < 2) { printf ("Please pass directory from which to build ISO\n"); usage(argv); return 1; } if (argc < 3) { printf ("Please supply output file\n"); usage(argv); return 1; } if (!iso_init()) { err(1, "Can't init libisofs"); } iso_msgs_set_severities("NEVER", "ALL", ""); fd = fopen(argv[optind+1], "w"); if (!fd) { err(1, "error opening output file"); } root = iso_tree_new_root(); iso_tree_radd_dir(root, argv[optind], &behav); if (!root) { err(1, "error opening input directory"); } volume = iso_volume_new_with_root(volid, "PUBID", "PREPID", root); if ( boot_img ) { /* adds El-Torito boot info. Tunned for isolinux */ struct el_torito_boot_image *bootimg; struct iso_tree_node_dir *boot = (struct iso_tree_node_dir *) iso_tree_volume_path_to_node(volume, "isolinux"); struct iso_tree_node *img = iso_tree_volume_path_to_node(volume, boot_img); if (!img) { err(1, "boot image patch is not valid"); } bootimg = iso_volume_set_boot_image(volume, img, ELTORITO_NO_EMUL, boot, "boot.cat"); el_torito_set_load_size(bootimg, 4); el_torito_patch_isolinux_image(bootimg); /* Or just this for hidden img * iso_volume_set_boot_image_hidden(volume, boot_img, ELTORITO_NO_EMUL); */ } volset = iso_volset_new( volume, "VOLSETID" ); /* some tests */ iso_volume_set_application_id(volume, "Libburnia"); iso_volume_set_copyright_file_id(volume, "LICENSE"); constraints = ECMA119_OMIT_VERSION_NUMBERS | ECMA119_37_CHAR_FILENAMES | ECMA119_NO_DIR_REALOCATION | ECMA119_RELAXED_FILENAMES; memset(&opts, 0, sizeof(struct ecma119_source_opts)); opts.level = level; opts.flags = flags; opts.relaxed_constraints = 0; opts.input_charset = NULL; opts.ouput_charset = "UTF-8"; src = iso_source_new_ecma119(volset, &opts); while (src->read(src, buf, 2048) == 2048) { fwrite(buf, 1, 2048, fd); } fclose(fd); iso_finish(); return 0; } 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 /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* vim: set ts=8 sts=8 sw=8 noet : */ /* * Little program that appends a dir to an existing image */ #define _GNU_SOURCE #include "libisofs.h" #include "libburn/libburn.h" #include #include #include #include #include #include #include #include #include #include #include #include #define SECSIZE 2048 const char * const optstring = "JRL:h"; extern char *optarg; extern int optind; void usage() { printf("test [OPTIONS] IMAGE 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 ecma119_source_opts wopts; struct ecma119_read_opts ropts; struct data_source *rsrc; struct iso_volset *volset; struct iso_tree_node_dir *root; struct burn_source *wsrc; unsigned char buf[2048]; FILE *fd; int c; int constraints; struct iso_tree_radd_dir_behavior behav = {0,0,0}; int level=1, flags=0; while ((c = getopt(argc, argv, optstring)) != -1) { switch(c) { case 'h': usage(); help(); exit(0); break; case 'J': flags |= ECMA119_JOLIET; break; case 'R': flags |= ECMA119_ROCKRIDGE; break; case 'L': level = atoi(optarg); break; case '?': usage(); exit(1); break; } } if (argc < optind + 1) { printf ("Please supply old image file\n"); usage(); return 1; } if (argc < optind + 2) { printf ("Please supply directory to add to image\n"); usage(); return 1; } if (argc < optind + 3) { printf ("Please supply output file\n"); usage(); return 1; } if (!iso_init()) { err(1, "Can't init libisofs"); } iso_msgs_set_severities("NEVER", "ALL", ""); rsrc = data_source_from_file(argv[optind]); if (rsrc == NULL) { printf ("Can't open device\n"); return 1; } fd = fopen(argv[optind+2], "w"); if (!fd) { err(1, "error opening output file"); } ropts.block = 0; ropts.norock = 0; ropts.nojoliet = 0; ropts.preferjoliet = 0; ropts.mode = 0555; ropts.uid = 0; ropts.gid = 0; volset = iso_volset_read(rsrc, &ropts); if (volset == NULL) { printf ("Error reading image\n"); return 1; } root = iso_volume_get_root(iso_volset_get_volume(volset, 0)); iso_tree_radd_dir(root, argv[optind+1], &behav); constraints = ECMA119_OMIT_VERSION_NUMBERS | ECMA119_37_CHAR_FILENAMES | ECMA119_NO_DIR_REALOCATION | ECMA119_RELAXED_FILENAMES; memset(&wopts, 0, sizeof(struct ecma119_source_opts)); wopts.level = level; wopts.flags = flags; wopts.relaxed_constraints = 0;//constraints; wopts.input_charset = "UTF-8"; wopts.ouput_charset = "UTF-8"; wopts.ms_block = 0; wopts.src = rsrc; wsrc = iso_source_new_ecma119(volset, &wopts); while (wsrc->read(wsrc, buf, 2048) == 2048) { fwrite(buf, 1, 2048, fd); } fclose(fd); iso_finish(); return 0; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* vim: set ts=8 sts=8 sw=8 noet : */ #define _GNU_SOURCE #include "libisofs.h" #include "libburn/libburn.h" #include #include #include #include #include #include #include #include #include #include #include #include #define SECSIZE 2048 const char * const optstring = "JRL:h"; extern char *optarg; extern int optind; static struct data_source *libburn_data_source_new(struct burn_drive *d); void usage() { printf("test [OPTIONS] DISC DIRECTORY\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 burn_drive_info *drives; struct burn_drive *drive; struct ecma119_source_opts wopts; struct ecma119_read_opts ropts; struct data_source *rsrc; struct iso_volset *volset; struct iso_tree_node_dir *root; struct burn_source *wsrc; int c; struct iso_tree_radd_dir_behavior behav = {0,0,0}; int level=1, flags=0; int ret = 0; while ((c = getopt(argc, argv, optstring)) != -1) { switch(c) { case 'h': usage(); help(); exit(0); break; case 'J': flags |= ECMA119_JOLIET; break; case 'R': flags |= ECMA119_ROCKRIDGE; break; case 'L': level = atoi(optarg); break; case '?': usage(); exit(1); break; } } if (argc < optind + 1) { printf ("Please supply device name\n"); usage(); return 1; } if (argc < optind + 2) { printf ("Please supply directory to add to disc\n"); usage(); return 1; } if (!iso_init()) { err(1, "Can't init libisofs"); } if (!burn_initialize()) { err(1, "Can't init libburn"); } iso_msgs_set_severities("NEVER", "ALL", ""); burn_msgs_set_severities("NEVER", "SORRY", "libburner : "); printf("Reading from %s\n", argv[optind]); if (burn_drive_scan_and_grab(&drives, argv[optind], 0) != 1) { err(1, "Can't open device. Are you sure it is a valid drive?\n"); } drive = drives[0].drive; { /* some check before going on */ enum burn_disc_status state; int pno; char name[80]; state = burn_disc_get_status(drive); burn_disc_get_profile(drive, &pno, name); // my drives report BURN_DISC_BLANK on a DVD+RW with data. // is that correct? if ( (pno != 0x1a) /*|| (state != BURN_DISC_FULL)*/ ) { printf("You need to insert a DVD+RW with some data.\n"); printf("Profile: %x, state: %d.\n", pno, state); ret = 1; goto exit_cleanup; } } rsrc = libburn_data_source_new(drive); if (rsrc == NULL) { printf ("Can't create data source.\n"); ret = 1; goto exit_cleanup; } ropts.block = 0; /* image always start on first block */ ropts.norock = 0; ropts.nojoliet = 0; ropts.preferjoliet = 0; ropts.mode = 0555; ropts.uid = 0; ropts.gid = 0; volset = iso_volset_read(rsrc, &ropts); if (volset == NULL) { printf ("Error reading image\n"); ret = 1; goto exit_cleanup; } printf("Image size: %d blocks.\n", ropts.size); /* close source, no more needed */ data_source_free(rsrc); root = iso_volume_get_root(iso_volset_get_volume(volset, 0)); /* add a new dir */ iso_tree_radd_dir(root, argv[optind+1], &behav); memset(&wopts, 0, sizeof(struct ecma119_source_opts)); wopts.level = level; wopts.flags = flags; wopts.relaxed_constraints = 0; wopts.input_charset = "UTF-8"; wopts.ouput_charset = "UTF-8"; /* round up to 32kb aligment = 16 block*/ wopts.ms_block = ((ropts.size + 15) / 16 ) * 16; wopts.overwrite = calloc(32, 2048); wsrc = iso_source_new_ecma119(volset, &wopts); /* a. write the new image */ printf("Adding new data...\n"); { struct burn_disc *target_disc; struct burn_session *session; struct burn_write_opts *burn_options; struct burn_track *track; struct burn_progress progress; char reasons[BURN_REASONS_LEN]; target_disc = burn_disc_create(); session = burn_session_create(); burn_disc_add_session(target_disc, session, BURN_POS_END); track = burn_track_create(); burn_track_set_source(track, wsrc); burn_session_add_track(session, track, BURN_POS_END); burn_options = burn_write_opts_new(drive); burn_drive_set_speed(drive, 0, 0); burn_write_opts_set_underrun_proof(burn_options, 1); //mmm, check for 32K alignment? burn_write_opts_set_start_byte(burn_options, wopts.ms_block * 2048); if (burn_write_opts_auto_write_type(burn_options, target_disc, reasons, 0) == BURN_WRITE_NONE) { printf("Failed to find a suitable write mode:\n%s\n", reasons); ret = 1; goto exit_cleanup; } /* ok, write the new track */ burn_disc_write(burn_options, target_disc); burn_write_opts_free(burn_options); while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) usleep(1002); while (burn_drive_get_status(drive, &progress) != BURN_DRIVE_IDLE) { printf("Writing: sector %d of %d\n", progress.sector, progress.sectors); sleep(1); } } /* b. write the new vol desc */ printf("Writing the new vol desc...\n"); if ( burn_random_access_write(drive, 0, wopts.overwrite, 32*2048, 0) != 1) { printf("Ups, new vol desc write failed\n"); } free(wopts.overwrite); iso_volset_free(volset); exit_cleanup:; burn_drive_release(drives[0].drive, 0); burn_finish(); iso_finish(); exit(ret); } struct disc_data_src { struct burn_drive *d; int nblocks; }; static int libburn_ds_read_block(struct data_source *src, int lba, unsigned char *buffer) { struct disc_data_src *data; off_t data_count; assert(src && buffer); data = (struct disc_data_src*)src->data; /* if (lba >= data->nblocks) * return BLOCK_OUT_OF_FILE; */ if ( burn_read_data(data->d, (off_t) lba * (off_t) 2048, buffer, 2048, &data_count, 0) < 0 ) { return -1; //error } return 0; } static int libburn_ds_get_size(struct data_source *src) { struct disc_data_src *data; assert(src); data = (struct disc_data_src*)src->data; return data->nblocks; } static void libburn_ds_free_data(struct data_source *src) { free(src->data); } static struct data_source * libburn_data_source_new(struct burn_drive *d) { struct disc_data_src *data; struct data_source *ret; assert(d); data = malloc(sizeof(struct disc_data_src)); data->d = d; //should be filled with the size of disc (or track?) data->nblocks = 0; ret = malloc(sizeof(struct data_source)); ret->refcount = 1; ret->read_block = libburn_ds_read_block; ret->get_size = libburn_ds_get_size; ret->free_data = libburn_ds_free_data; ret->data = data; return ret; } /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* vim: set ts=8 sts=8 sw=8 noet : */ #define _GNU_SOURCE #include "libisofs.h" #include "libburn/libburn.h" #include #include #include #include #include #include #include #include #include #include #include #include #define SECSIZE 2048 const char * const optstring = "JRL:h"; extern char *optarg; extern int optind; void usage() { printf("test [OPTIONS] LSS NWA DISC 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 ecma119_source_opts wopts; struct ecma119_read_opts ropts; struct data_source *rsrc; struct iso_volset *volset; struct iso_volume *volume; struct iso_tree_node_dir *root; struct burn_source *wsrc; unsigned char buf[2048]; FILE *fd; int c; int constraints; struct iso_tree_radd_dir_behavior behav = {0,0,0}; int level=1, flags=0; char *boot_img = NULL; while ((c = getopt(argc, argv, optstring)) != -1) { switch(c) { case 'h': usage(); help(); exit(0); break; case 'J': flags |= ECMA119_JOLIET; break; case 'R': flags |= ECMA119_ROCKRIDGE; break; case 'L': level = atoi(optarg); break; case '?': usage(); exit(1); break; } } if (argc < optind + 2) { printf ("Please supply last_sess_start next_sess_start\n"); usage(); return 1; } if (argc < optind + 3) { printf ("Please supply device name\n"); usage(); return 1; } if (argc < optind + 4) { printf ("Please supply directory to add to disc\n"); usage(); return 1; } if (argc < optind + 5) { printf ("Please supply output file\n"); usage(); return 1; } if (!iso_init()) { err(1, "Can't init libisofs"); } iso_msgs_set_severities("NEVER", "ALL", ""); rsrc = data_source_from_file(argv[optind+2]); if (rsrc == NULL) { printf ("Can't open device\n"); return 1; } fd = fopen(argv[optind+4], "w"); if (!fd) { err(1, "error opening output file"); } ropts.block = atoi(argv[optind]); ropts.norock = 0; ropts.nojoliet = 0; ropts.preferjoliet = 0; ropts.mode = 0555; ropts.uid = 0; ropts.gid = 0; volset = iso_volset_read(rsrc, &ropts); if (volset == NULL) { printf ("Error reading image\n"); return 1; } root = iso_volume_get_root(iso_volset_get_volume(volset, 0)); iso_tree_radd_dir(root, argv[optind+3], &behav); constraints = ECMA119_OMIT_VERSION_NUMBERS | ECMA119_37_CHAR_FILENAMES | ECMA119_NO_DIR_REALOCATION | ECMA119_RELAXED_FILENAMES; memset(&wopts, 0, sizeof(struct ecma119_source_opts)); wopts.level = level; wopts.flags = flags; wopts.relaxed_constraints = 0;//constraints; wopts.input_charset = "UTF-8"; wopts.ouput_charset = "UTF-8"; wopts.ms_block = atoi(argv[optind+1]); wsrc = iso_source_new_ecma119(volset, &wopts); while (wsrc->read(wsrc, buf, 2048) == 2048) { fwrite(buf, 1, 2048, fd); } fclose(fd); iso_finish(); return 0; } /* * Little program that reads an existing ISO image and prints its * contents to stdout. */ #include "libisofs.h" #include #include #include #include #include static void print_permissions(mode_t mode) { char perm[10]; //TODO suid, sticky... perm[9] = '\0'; perm[8] = mode & S_IXOTH ? 'x' : '-'; perm[7] = mode & S_IWOTH ? 'w' : '-'; perm[6] = mode & S_IROTH ? 'r' : '-'; perm[5] = mode & S_IXGRP ? 'x' : '-'; perm[4] = mode & S_IWGRP ? 'w' : '-'; perm[3] = mode & S_IRGRP ? 'r' : '-'; perm[2] = mode & S_IXUSR ? 'x' : '-'; perm[1] = mode & S_IWUSR ? 'w' : '-'; perm[0] = mode & S_IRUSR ? 'r' : '-'; printf("[%s]",perm); } static void print_dir(struct iso_tree_node_dir *dir, int level) { int i; struct iso_tree_iter *iter; struct iso_tree_node *node; char sp[level * 2 + 1]; for (i = 0; i < level * 2; i += 2) { sp[i] = '|'; sp[i+1] = ' '; } sp[level * 2-1] = '-'; sp[level * 2] = '\0'; iter = iso_tree_node_children(dir); while ( (node = iso_tree_iter_next(iter)) != NULL ) { if (LIBISO_ISDIR(node)) { printf("%s+[D] ", sp); print_permissions(iso_tree_node_get_permissions(node)); printf(" %s\n", iso_tree_node_get_name(node) ); print_dir((struct iso_tree_node_dir*)node, level+1); } else if (LIBISO_ISREG(node)) { printf("%s-[F] ", sp); print_permissions(iso_tree_node_get_permissions(node)); printf(" %s\n", iso_tree_node_get_name(node) ); } else if (LIBISO_ISLNK(node)) { printf("%s-[L] ", sp); print_permissions(iso_tree_node_get_permissions(node)); printf(" %s -> %s \n", iso_tree_node_get_name(node), iso_tree_node_symlink_get_dest((struct iso_tree_node_symlink*)node) ); } else { /* boot catalog */ printf("%s-[C] ", sp); print_permissions(iso_tree_node_get_permissions(node)); printf(" %s\n", iso_tree_node_get_name(node) ); } } iso_tree_iter_free(iter); } static void check_el_torito(struct iso_volume *volume) { struct iso_tree_node *cat, *img; if (iso_volume_get_boot_image(volume, &img, &cat)) { printf("\nEL-TORITO INFORMATION\n"); printf("=====================\n\n"); printf("Catalog: %s\n", iso_tree_node_get_name(cat) ); printf("Image: %s\n", iso_tree_node_get_name(img) ); } } int main(int argc, char **argv) { struct ecma119_read_opts opts; struct data_source *src; struct iso_volset *volset; struct iso_volume *volume; if (argc != 2) { printf ("You need to specify a valid ISO image\n"); return 1; } if (!iso_init()) { err(1, "Can't init libisofs"); } iso_msgs_set_severities("NEVER", "ALL", ""); src = data_source_from_file(argv[1]); if (src == NULL) { printf ("Can't open image\n"); return 1; } opts.block = 0; opts.norock = 0; opts.nojoliet = 0; opts.preferjoliet = 1; opts.mode = 0555; opts.uid = 0; opts.gid = 0; volset = iso_volset_read(src, &opts); if (volset == NULL) { printf ("Error reading image\n"); return 1; } volume = iso_volset_get_volume(volset, 0); printf("\nVOLUME INFORMATION\n"); printf("==================\n\n"); printf("Vol. id: %s\n", iso_volume_get_volume_id(volume)); printf("Publisher: %s\n", iso_volume_get_publisher_id(volume)); printf("Data preparer: %s\n", iso_volume_get_data_preparer_id(volume)); printf("System: %s\n", iso_volume_get_system_id(volume)); printf("Application: %s\n", iso_volume_get_application_id(volume)); printf("Copyright: %s\n", iso_volume_get_copyright_file_id(volume)); printf("Abstract: %s\n", iso_volume_get_abstract_file_id(volume)); printf("Biblio: %s\n", iso_volume_get_biblio_file_id(volume)); if (opts.hasRR) printf("Rock Ridge Extensions are available.\n"); if (opts.hasJoliet) printf("Joliet Extensions are available.\n"); printf("\nDIRECTORY TREE\n"); printf("==============\n"); print_dir(iso_volume_get_root(volume), 0); check_el_torito(volume); printf("\n\n"); data_source_free(src); iso_volset_free(volset); iso_finish(); return 0; } #include "test.h" static void create_test_suite() { add_util_suite(); add_tree_suite(); add_exclude_suite(); add_file_hashtable_suite(); add_ecma119_tree_suite(); add_volume_suite(); add_data_source_suite(); add_isoread_suite(); } int main(int argc, char **argv) { CU_pSuite pSuite = NULL; /* initialize the CUnit test registry */ if (CUE_SUCCESS != CU_initialize_registry()) return CU_get_error(); create_test_suite(); /* Run all tests using the console interface */ CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); CU_cleanup_registry(); return CU_get_error(); } #ifndef TEST_H_ #define TEST_H_ #include #include #include #include #include "libisofs.h" void add_tree_suite(); void add_exclude_suite(); void add_file_hashtable_suite(); void add_util_suite(); void add_ecma119_tree_suite(); void add_volume_suite(); void add_data_source_suite(); void add_isoread_suite(); #endif /*TEST_H_*/ /* * Unit test for data_source implementation */ #include "test.h" #include "libisofs.h" #include #include #include void test_data_source_from_file() { int res; struct data_source *ds; unsigned char buffer[2048]; unsigned char rbuff[2048]; char filename[] = "/tmp/temp.iso.XXXXXX"; int fd = mkstemp(filename); /* actually a failure here means that we couldn't test */ CU_ASSERT_NOT_EQUAL(fd, -1); memset(buffer, 0x35, 2048); write(fd, buffer, 2048); memset(buffer, 0x20, 2048); write(fd, buffer, 2048); memset(buffer, 0xF2, 2048); write(fd, buffer, 2048); memset(buffer, 0x11, 2048); write(fd, buffer, 2048); memset(buffer, 0xAB, 2048); write(fd, buffer, 2048); close(fd); ds = data_source_from_file(filename); CU_ASSERT_PTR_NOT_NULL(ds); /* check correct size reported */ CU_ASSERT_EQUAL(ds->get_size(ds), 5); /* check reading */ res = ds->read_block(ds, 4, rbuff); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_NSTRING_EQUAL(rbuff, buffer, 2048); res = ds->read_block(ds, 1, rbuff); CU_ASSERT_EQUAL(res, 0); memset(buffer, 0x20, 2048); CU_ASSERT_NSTRING_EQUAL(rbuff, buffer, 2048); res = ds->read_block(ds, 0, rbuff); CU_ASSERT_EQUAL(res, 0); memset(buffer, 0x35, 2048); CU_ASSERT_NSTRING_EQUAL(rbuff, buffer, 2048); res = ds->read_block(ds, 3, rbuff); CU_ASSERT_EQUAL(res, 0); memset(buffer, 0x11, 2048); CU_ASSERT_NSTRING_EQUAL(rbuff, buffer, 2048); /* and a block outside file */ res = ds->read_block(ds, 5, rbuff); CU_ASSERT_TRUE(res < 0); data_source_free(ds); unlink(filename); } void add_data_source_suite() { CU_pSuite pSuite = CU_add_suite("DataSourceSuite", NULL, NULL); CU_add_test(pSuite, "test of data_source_from_file()", test_data_source_from_file); } /* * Unit test for ecma119_tree.h */ #include "libisofs.h" #include "tree.h" #include "test.h" #include "ecma119.h" #include "ecma119_tree.h" #include #include #include #include #include #include #include struct ecma119_write_target t; static void reset_write_target() { /* inititalize t with default values */ t.root = NULL; t.joliet_root = NULL; t.volset = NULL; t.volnum = time(NULL); t.now; t.total_size = 0; t.vol_space_size = 0; t.rockridge = 0; t.joliet = 0; t.iso_level = 1; t.eltorito = 0; t.relaxed_constraints = 0; t.catalog = NULL; t.replace_mode = 0; t.dir_mode = 0777; t.file_mode = 0777; t.gid = 0; t.uid = 0; t.input_charset = "UTF-8"; t.ouput_charset = "UTF-8"; t.cache_inodes = 0; t.sort_files = 0; t.ino = 0; t.curblock = 0; t.block_size = 2048; t.path_table_size = 0; t.path_table_size_joliet = 0; t.l_path_table_pos = 0; t.m_path_table_pos = 0; t.l_path_table_pos_joliet = 0; t.m_path_table_pos_joliet = 0; t.total_dir_size = 0; t.total_dir_size_joliet = 0; t.dirlist = NULL; t.pathlist = NULL; t.dirlist_len = 0; t.file_table = NULL; t.filelist = NULL; t.filelist_len = 0; t.curfile = 0; t.dirlist_joliet = NULL; t.pathlist_joliet = NULL; t.dirlist_len_joliet = 0; t.state = ECMA119_WRITE_SYSTEM_AREA; } void test_create_tree_only_root() { struct iso_tree_node *root; struct ecma119_tree_node *node; reset_write_target(); root = (struct iso_tree_node*) iso_tree_new_root(); node = ecma119_tree_create(&t, root); CU_ASSERT_PTR_NOT_NULL(node); /* root has no name */ CU_ASSERT_PTR_NULL(node->iso_name); CU_ASSERT_PTR_NULL(node->full_name); /* target root has been set */ CU_ASSERT_PTR_EQUAL(t.root, node); CU_ASSERT_PTR_EQUAL(node->target, &t); CU_ASSERT_PTR_NULL(node->parent); CU_ASSERT_EQUAL(node->attrib.st_mode, root->attrib.st_mode); CU_ASSERT_EQUAL(node->attrib.st_uid, root->attrib.st_uid); CU_ASSERT_EQUAL(node->attrib.st_gid, root->attrib.st_gid); /* the node is a directory */ CU_ASSERT_EQUAL(node->type, ECMA119_DIR); CU_ASSERT_EQUAL(node->info.dir.nchildren, 0); iso_tree_free((struct iso_tree_node *)root); ecma119_tree_free(node); } void add_ecma119_tree_suite() { CU_pSuite pSuite = CU_add_suite("Ecma119TreeSuite", NULL, NULL); CU_add_test(pSuite, "test of ecma119_tree_create() with only root dir", test_create_tree_only_root); //CU_add_test(pSuite, "test of create_dir()", test_create_dir); } /* * Unit test for exclude.h */ #include "exclude.h" #include "test.h" #include #include static void test_exclude() { struct iso_hash_table table = { {0,}, 0}; CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/dir") ); CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/otherdir") ); iso_exclude_add_path(&table, "/otherdir"); CU_ASSERT_TRUE( iso_exclude_lookup(&table, "/otherdir") ); CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/dir") ); iso_exclude_add_path(&table, "/dir"); CU_ASSERT_TRUE( iso_exclude_lookup(&table, "/otherdir") ); CU_ASSERT_TRUE( iso_exclude_lookup(&table, "/dir") ); iso_exclude_empty(&table); CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/dir") ); CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/otherdir") ); } void add_exclude_suite() { CU_pSuite pSuite = CU_add_suite("ExcludeSuite", NULL, NULL); CU_add_test(pSuite, "test of exclude", test_exclude); } /* * Unit test for file.h */ #include "test.h" #include "file.h" #include "tree.h" #include #include #include #include static void test_iso_file_new() { struct iso_tree_node_file *file; struct iso_file *iso; file = calloc(1, sizeof(struct iso_tree_node_file) ); file->node.name = "fileName"; file->node.attrib.st_size = 12; file->node.attrib.st_dev = 15; file->node.attrib.st_ino = 204; file->loc.path = "/tmp/filename"; file->sort_weight = 1; iso = iso_file_new(file); CU_ASSERT_PTR_NOT_NULL(iso); CU_ASSERT_STRING_EQUAL(iso->path, "/tmp/filename"); CU_ASSERT_EQUAL(iso->size, 12); CU_ASSERT_EQUAL(iso->ino, 0); CU_ASSERT_EQUAL(iso->nlink, 1); CU_ASSERT_EQUAL(iso->sort_weight, 1); CU_ASSERT_EQUAL(iso->real_dev, 15); CU_ASSERT_EQUAL(iso->real_ino, 204); } static void test_add_lookup() { struct iso_file_table *table; struct iso_tree_node_file *file1; struct iso_tree_node_file *file2; struct iso_file *iso1; struct iso_file *iso2; struct iso_file *iso3; int r; table = iso_file_table_new(1); CU_ASSERT_PTR_NOT_NULL( table ); CU_ASSERT_TRUE( table->cache_inodes ); CU_ASSERT_EQUAL(table->count, 0); file1 = calloc(1, sizeof(struct iso_tree_node_file) ); file1->node.name = "fileName"; file1->node.attrib.st_dev = 15; file1->node.attrib.st_ino = 204; file1->loc.path = "/tmp/filename"; iso1 = iso_file_new(file1); r = iso_file_table_add_file(table, iso1); CU_ASSERT_EQUAL(r, 1); CU_ASSERT_EQUAL(table->count, 1); iso2 = iso_file_table_lookup(table, file1); CU_ASSERT_PTR_NOT_NULL(iso2); CU_ASSERT_PTR_EQUAL(iso2, iso1); file2 = calloc(1, sizeof(struct iso_tree_node_file) ); file2->node.name = "fileName2"; file2->node.attrib.st_dev = 152; file2->node.attrib.st_ino = 2042; file2->loc.path = "/tmp/filename2"; iso3 = iso_file_new(file2); r = iso_file_table_add_file(table, iso3); CU_ASSERT_EQUAL(r, 1); CU_ASSERT_EQUAL(table->count, 2); /* treat to add the same file again */ r = iso_file_table_add_file(table, iso3); CU_ASSERT_EQUAL(r, 0); CU_ASSERT_EQUAL(table->count, 2); iso2 = iso_file_table_lookup(table, file1); CU_ASSERT_PTR_NOT_NULL(iso2); CU_ASSERT_PTR_EQUAL(iso2, iso1); iso2 = iso_file_table_lookup(table, file2); CU_ASSERT_PTR_NOT_NULL(iso2); CU_ASSERT_PTR_EQUAL(iso2, iso3); iso3 = iso_file_new(file2); r = iso_file_table_add_file(table, iso3); CU_ASSERT_EQUAL(r, 0); CU_ASSERT_EQUAL(table->count, 2); iso_file_table_clear(table); CU_ASSERT_EQUAL(table->count, 0); iso2 = iso_file_table_lookup(table, file2); CU_ASSERT_PTR_NULL(iso2); free( file1 ); free( file2 ); free( table ); } static void test_cache_inodes() { struct iso_file_table *table; struct iso_tree_node_file *file1; struct iso_tree_node_file *file2; struct iso_file *iso1; struct iso_file *iso2; struct iso_file *iso3; int r; table = iso_file_table_new(1); CU_ASSERT_PTR_NOT_NULL( table ); CU_ASSERT_TRUE( table->cache_inodes ); CU_ASSERT_EQUAL(table->count, 0); file1 = calloc(1, sizeof(struct iso_tree_node_file) ); file1->node.name = "fileName"; file1->node.attrib.st_dev = 15; file1->node.attrib.st_ino = 204; file1->loc.path = "/tmp/filename"; iso1 = iso_file_new(file1); r = iso_file_table_add_file(table, iso1); CU_ASSERT_EQUAL(r, 1); /* another file, different but with the same inode id */ file2 = calloc(1, sizeof(struct iso_tree_node_file) ); file2->node.name = "another file"; file2->node.attrib.st_dev = 15; file2->node.attrib.st_ino = 204; file2->loc.path = "/tmp/another"; iso2 = iso_file_new(file2); /* ensure it's not added again... */ r = iso_file_table_add_file(table, iso2); CU_ASSERT_EQUAL(r, 0); /* ...and the lookup returns the first */ iso3 = iso_file_table_lookup(table, file2); CU_ASSERT_PTR_EQUAL(iso1, iso3); free(iso2); free(file2); /* and now a file with same inode but different device */ file2 = calloc(1, sizeof(struct iso_tree_node_file) ); file2->node.name = "different file"; file2->node.attrib.st_dev = 16; /* different dev id */ file2->node.attrib.st_ino = 204; file2->loc.path = "/tmp/different"; iso2 = iso_file_new(file2); r = iso_file_table_add_file(table, iso2); CU_ASSERT_EQUAL(r, 1); iso3 = iso_file_table_lookup(table, file2); CU_ASSERT_PTR_NOT_EQUAL(iso3, iso1); CU_ASSERT_PTR_EQUAL(iso3, iso2); iso_file_table_clear(table); free( file1 ); free( file2 ); free( table ); } static void test_no_cache_inodes() { struct iso_file_table *table; struct iso_tree_node_file *file1; struct iso_tree_node_file *file2; struct iso_tree_node_file *file3; struct iso_file *iso1; struct iso_file *iso2; struct iso_file *iso3; int r; table = iso_file_table_new(0); CU_ASSERT_PTR_NOT_NULL( table ); CU_ASSERT_FALSE( table->cache_inodes ); CU_ASSERT_EQUAL(table->count, 0); file1 = calloc(1, sizeof(struct iso_tree_node_file) ); file1->node.name = "fileName"; file1->node.attrib.st_dev = 15; file1->node.attrib.st_ino = 204; file1->loc.path = "/tmp/filename"; iso1 = iso_file_new(file1); r = iso_file_table_add_file(table, iso1); CU_ASSERT_EQUAL(r, 1); /* another file, different but with the same inode id */ file2 = calloc(1, sizeof(struct iso_tree_node_file) ); file2->node.name = "another file"; file2->node.attrib.st_dev = 15; file2->node.attrib.st_ino = 204; file2->loc.path = "/tmp/another"; iso2 = iso_file_new(file2); /* ensure is added */ r = iso_file_table_add_file(table, iso2); CU_ASSERT_EQUAL(r, 1); iso3 = iso_file_table_lookup(table, file2); CU_ASSERT_PTR_EQUAL(iso3, iso2); /* and now a file with same inode and path */ file3 = calloc(1, sizeof(struct iso_tree_node_file) ); file3->node.name = "different file"; file3->node.attrib.st_dev = 15; file3->node.attrib.st_ino = 204; file3->loc.path = "/tmp/filename"; iso3 = iso_file_new(file3); r = iso_file_table_add_file(table, iso3); CU_ASSERT_EQUAL(r, 0); iso3 = iso_file_table_lookup(table, file3); CU_ASSERT_PTR_EQUAL(iso3, iso1); iso_file_table_clear(table); free(file1); free(file2); free(file3); free(table); } static void test_prev_img_files() { struct iso_file_table *table; struct iso_tree_node_file *file1; struct iso_tree_node_file *file2; struct iso_tree_node_file *file3; struct iso_file *iso1; struct iso_file *iso2; struct iso_file *iso3; int r; table = iso_file_table_new(0); CU_ASSERT_PTR_NOT_NULL( table ); CU_ASSERT_FALSE( table->cache_inodes ); CU_ASSERT_EQUAL(table->count, 0); file1 = calloc(1, sizeof(struct iso_tree_node_file) ); file1->node.name = "fileName"; file1->node.procedence = LIBISO_PREVIMG; file1->node.attrib.st_dev = 0; file1->node.attrib.st_ino = 204; file1->loc.block = 567; iso1 = iso_file_new(file1); r = iso_file_table_add_file(table, iso1); CU_ASSERT_EQUAL(r, 1); /* another file, different but with the same inode id */ file2 = calloc(1, sizeof(struct iso_tree_node_file) ); file2->node.name = "another file"; file2->node.procedence = LIBISO_PREVIMG; file2->node.attrib.st_dev = 0; file2->node.attrib.st_ino = 204; file2->loc.block = 567; iso2 = iso_file_new(file2); /* ensure is not added */ r = iso_file_table_add_file(table, iso2); CU_ASSERT_EQUAL(r, 0); iso3 = iso_file_table_lookup(table, file2); CU_ASSERT_PTR_EQUAL(iso3, iso1); /* and now a file added new */ file3 = calloc(1, sizeof(struct iso_tree_node_file) ); file3->node.name = "different file"; file3->node.attrib.st_dev = 0; file3->node.attrib.st_ino = 204; file3->loc.path = "/tmp/filename"; iso3 = iso_file_new(file3); /* assert it's added */ r = iso_file_table_add_file(table, iso3); CU_ASSERT_EQUAL(r, 1); iso1 = iso_file_table_lookup(table, file3); CU_ASSERT_PTR_EQUAL(iso1, iso3); iso_file_table_clear(table); free(file1); free(file2); free(file3); free(table); } void add_file_hashtable_suite() { CU_pSuite pSuite = CU_add_suite("FileHashtableSuite", NULL, NULL); CU_add_test(pSuite, "iso_file_new()", test_iso_file_new); CU_add_test(pSuite, "add and lookup", test_add_lookup); CU_add_test(pSuite, "with cache_inodes", test_cache_inodes); CU_add_test(pSuite, "with files from previous img", test_prev_img_files); } /* * Unit test iso reading functions */ #include #include #include #include "test.h" #include "ecma119_read_rr.h" #include "util.h" static void test_read_rr_PX() { struct iso_read_info info; struct susp_sys_user_entry px; struct stat atts; int res; info.src = NULL; /* data source is not needed */ info.error = 0; info.ino = 0; memset(&atts, 0, sizeof(atts)); /* fill px struct */ px.sig[0] = 'P'; px.sig[1] = 'X'; px.version[0] = 1; iso_bb(px.data.PX.uid, 33, 4); iso_bb(px.data.PX.gid, 22, 4); iso_bb(px.data.PX.links, 7, 4); iso_bb(px.data.PX.mode, S_IFREG | 0555, 4); /* a) test with RRIP 1.12 */ info.rr = RR_EXT_112; px.len_sue[0] = 44; iso_bb(px.data.PX.serial, 678, 4); res = read_rr_PX(&info, &px, &atts); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_EQUAL(atts.st_uid, 33); CU_ASSERT_EQUAL(atts.st_gid, 22); CU_ASSERT_EQUAL(atts.st_mode, (mode_t) S_IFREG | 0555); CU_ASSERT_EQUAL(atts.st_nlink, 7); CU_ASSERT_EQUAL(atts.st_ino, 678); //TODO } void add_isoread_suite() { CU_pSuite pSuite = CU_add_suite("IsoReadSuite", NULL, NULL); CU_add_test(pSuite, "test of read_rr_PX()", test_read_rr_PX); } /* * Unit test for tree.h */ #include "libisofs.h" #include "tree.h" #include "test.h" #include #include #include #include #include #include static void test_new_root() { struct iso_tree_node_dir *root; root = iso_tree_new_root(); CU_ASSERT_PTR_NOT_NULL(root); CU_ASSERT_EQUAL(root->nchildren, 0); CU_ASSERT_PTR_NULL(root->children); CU_ASSERT_PTR_NULL(root->node.parent); CU_ASSERT_PTR_NULL(root->node.name); CU_ASSERT(S_ISDIR(root->node.attrib.st_mode) ); } static void test_add_dir() { struct iso_tree_node_dir *root; struct iso_tree_node_dir *dir; root = iso_tree_new_root(); CU_ASSERT_PTR_NOT_NULL(root); dir = iso_tree_add_dir(root, "New dir name"); CU_ASSERT_PTR_NOT_NULL(root); CU_ASSERT_PTR_NOT_NULL(dir); CU_ASSERT_PTR_EQUAL(dir->node.parent, root); CU_ASSERT_EQUAL(root->nchildren, 1); CU_ASSERT_PTR_EQUAL(root->children[0], (struct iso_tree_node *)dir); CU_ASSERT_STRING_EQUAL( dir->node.name, "New dir name"); CU_ASSERT( S_ISDIR(dir->node.attrib.st_mode) ); iso_tree_free((struct iso_tree_node *)root); } static void test_add_file() { struct iso_tree_node_dir *root; struct iso_tree_node_file *file; root = iso_tree_new_root(); file = (struct iso_tree_node_file *) iso_tree_add_file(root, "/tmp/libisofs_test/README"); CU_ASSERT_PTR_NOT_NULL(root); CU_ASSERT_PTR_NOT_NULL(file); CU_ASSERT_PTR_EQUAL(file->node.parent, root); CU_ASSERT_EQUAL(root->nchildren, 1); CU_ASSERT_PTR_EQUAL(root->children[0], (struct iso_tree_node *)file); CU_ASSERT_STRING_EQUAL( file->node.name, "README" ); CU_ASSERT_STRING_EQUAL( file->loc.path, "/tmp/libisofs_test/README" ); CU_ASSERT( S_ISREG(file->node.attrib.st_mode) ); iso_tree_free((struct iso_tree_node *)root); } static void test_add_symlink() { struct iso_tree_node_dir *root; struct iso_tree_node *lnk; root = iso_tree_new_root(); lnk = iso_tree_add_symlink(root, "read", "/tmp/libisofs_test/README"); CU_ASSERT_PTR_NOT_NULL(root); CU_ASSERT_PTR_NOT_NULL(lnk); CU_ASSERT_PTR_EQUAL(lnk->parent, root); CU_ASSERT_EQUAL(root->nchildren, 1); CU_ASSERT_PTR_EQUAL(root->children[0], (struct iso_tree_node *)lnk); CU_ASSERT_STRING_EQUAL( lnk->name, "read"); CU_ASSERT_STRING_EQUAL( ((struct iso_tree_node_symlink*)lnk)->dest, "/tmp/libisofs_test/README" ); CU_ASSERT( S_ISLNK(lnk->attrib.st_mode) ); iso_tree_free((struct iso_tree_node *)root); } static void test_add_node() { struct iso_tree_node_dir *root; struct iso_tree_node *node; root = iso_tree_new_root(); /* test addition of a dir */ node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_PTR_EQUAL(node->parent, root); CU_ASSERT_EQUAL(root->nchildren, 1); CU_ASSERT_PTR_EQUAL(root->children[0], node); CU_ASSERT_STRING_EQUAL( node->name, "dir1"); CU_ASSERT( ISO_ISDIR(node) ); CU_ASSERT( S_ISDIR(node->attrib.st_mode) ); /* test addition of a link */ node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_PTR_EQUAL(node->parent, root); CU_ASSERT_EQUAL(root->nchildren, 2); CU_ASSERT_PTR_EQUAL(root->children[1], node); CU_ASSERT( ISO_ISLNK(node) ); CU_ASSERT( S_ISLNK(node->attrib.st_mode) ); CU_ASSERT_STRING_EQUAL( node->name, "link to readme"); CU_ASSERT_STRING_EQUAL( ((struct iso_tree_node_symlink*)node)->dest, "/tmp/libisofs_test/README" ); /* test addition of a file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_PTR_EQUAL(node->parent, root); CU_ASSERT_EQUAL(root->nchildren, 3); CU_ASSERT_PTR_EQUAL(root->children[2], node); CU_ASSERT( S_ISREG(node->attrib.st_mode) ); CU_ASSERT( ISO_ISREG(node) ); CU_ASSERT_STRING_EQUAL( node->name, "README" ); CU_ASSERT_STRING_EQUAL( ((struct iso_tree_node_file *) node)->loc.path, "/tmp/libisofs_test/README" ); /* test no exiting file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/THISNOTEXIST"); CU_ASSERT_PTR_NULL(node); CU_ASSERT_EQUAL(libisofs_errno, NO_FILE); CU_ASSERT_EQUAL(root->nchildren, 3); /* test no valid file */ node = iso_tree_add_node(root, "/dev/zero"); CU_ASSERT_PTR_NULL(node); CU_ASSERT_EQUAL(libisofs_errno, UNEXPECTED_FILE_TYPE); CU_ASSERT_EQUAL(root->nchildren, 3); /* test no read perm file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/no_read"); CU_ASSERT_PTR_NULL(node); CU_ASSERT_EQUAL(libisofs_errno, NO_READ_ACCESS); CU_ASSERT_EQUAL(root->nchildren, 3); iso_tree_free((struct iso_tree_node *)root); } static void test_set_name() { struct iso_tree_node_dir *root; struct iso_tree_node *node; root = iso_tree_new_root(); /* test on a dir */ node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_STRING_EQUAL( node->name, "dir1"); iso_tree_node_set_name(node, "newname"); CU_ASSERT_STRING_EQUAL( node->name, "newname"); /* test on a link */ node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_STRING_EQUAL( node->name, "link to readme"); iso_tree_node_set_name(node, "new link name"); CU_ASSERT_STRING_EQUAL( node->name, "new link name"); /* test on a file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_STRING_EQUAL( node->name, "README" ); iso_tree_node_set_name(node, "new file name"); CU_ASSERT_STRING_EQUAL( node->name, "new file name"); iso_tree_free((struct iso_tree_node *)root); } static void test_get_name() { struct iso_tree_node_dir *root; struct iso_tree_node *node; root = iso_tree_new_root(); /* test on a dir */ node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); CU_ASSERT_STRING_EQUAL( iso_tree_node_get_name(node), "dir1"); iso_tree_node_set_name(node, "newname"); CU_ASSERT_STRING_EQUAL( iso_tree_node_get_name(node), "newname"); /* test on a link */ node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); CU_ASSERT_STRING_EQUAL( iso_tree_node_get_name(node), "link to readme"); iso_tree_node_set_name(node, "new link name"); CU_ASSERT_STRING_EQUAL( iso_tree_node_get_name(node), "new link name"); /* test on a file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); CU_ASSERT_STRING_EQUAL( iso_tree_node_get_name(node), "README" ); iso_tree_node_set_name(node, "new file name"); CU_ASSERT_STRING_EQUAL( iso_tree_node_get_name(node), "new file name"); iso_tree_free((struct iso_tree_node *)root); } static void test_set_hidden() { struct iso_tree_node_dir *root; struct iso_tree_node *node; root = iso_tree_new_root(); /* test on a dir */ node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_EQUAL(node->hide_flags, 0); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR); CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_JOLIET); CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_JOLIET); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); /* test on a link */ node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_EQUAL(node->hide_flags, 0); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR); CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_JOLIET); CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_JOLIET); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); /* test on a file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_EQUAL(node->hide_flags, 0); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR); CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_JOLIET); CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_JOLIET); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); iso_tree_free((struct iso_tree_node *)root); } static void test_is_hidden() { struct iso_tree_node_dir *root; struct iso_tree_node *node; root = iso_tree_new_root(); /* test on a dir */ node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); CU_ASSERT_FALSE(iso_tree_node_is_hidden(node)); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR); CU_ASSERT_EQUAL(iso_tree_node_is_hidden(node), LIBISO_HIDE_ON_RR); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_JOLIET); CU_ASSERT_EQUAL(iso_tree_node_is_hidden(node), LIBISO_HIDE_ON_JOLIET); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); CU_ASSERT_EQUAL(iso_tree_node_is_hidden(node), LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); /* test on a link */ node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); CU_ASSERT_FALSE(iso_tree_node_is_hidden(node)); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR); CU_ASSERT_EQUAL(iso_tree_node_is_hidden(node), LIBISO_HIDE_ON_RR); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_JOLIET); CU_ASSERT_EQUAL(iso_tree_node_is_hidden(node), LIBISO_HIDE_ON_JOLIET); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); CU_ASSERT_EQUAL(iso_tree_node_is_hidden(node), LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); /* test on a file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); CU_ASSERT_FALSE(iso_tree_node_is_hidden(node)); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR); CU_ASSERT_EQUAL(iso_tree_node_is_hidden(node), LIBISO_HIDE_ON_RR); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_JOLIET); CU_ASSERT_EQUAL(iso_tree_node_is_hidden(node), LIBISO_HIDE_ON_JOLIET); iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); CU_ASSERT_EQUAL(iso_tree_node_is_hidden(node), LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET); iso_tree_free((struct iso_tree_node *)root); } static void test_set_gid() { struct iso_tree_node_dir *root; struct iso_tree_node *node; gid_t mygid = getgid(); root = iso_tree_new_root(); /* test on a dir */ node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_EQUAL(node->attrib.st_gid, mygid); iso_tree_node_set_gid(node, 1234); CU_ASSERT_EQUAL(node->attrib.st_gid, 1234); /* test on a link */ node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_EQUAL(node->attrib.st_gid, mygid); iso_tree_node_set_gid(node, 1234); CU_ASSERT_EQUAL(node->attrib.st_gid, 1234); /* test on a file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_EQUAL(node->attrib.st_gid, mygid); iso_tree_node_set_gid(node, 1234); CU_ASSERT_EQUAL(node->attrib.st_gid, 1234); iso_tree_free((struct iso_tree_node *)root); } static void test_get_gid() { struct iso_tree_node_dir *root; struct iso_tree_node *node; gid_t mygid = getgid(); root = iso_tree_new_root(); /* test on a dir */ node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); CU_ASSERT_EQUAL(iso_tree_node_get_gid(node), mygid); iso_tree_node_set_gid(node, 1234); CU_ASSERT_EQUAL(iso_tree_node_get_gid(node), 1234); /* test on a link */ node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); CU_ASSERT_EQUAL(iso_tree_node_get_gid(node), mygid); iso_tree_node_set_gid(node, 1234); CU_ASSERT_EQUAL(iso_tree_node_get_gid(node), 1234); /* test on a file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); CU_ASSERT_EQUAL(iso_tree_node_get_gid(node), mygid); iso_tree_node_set_gid(node, 1234); CU_ASSERT_EQUAL(iso_tree_node_get_gid(node), 1234); iso_tree_free((struct iso_tree_node *)root); } static void test_set_uid() { struct iso_tree_node_dir *root; struct iso_tree_node *node; uid_t myuid = getuid(); root = iso_tree_new_root(); /* test on a dir */ node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_EQUAL(node->attrib.st_uid, myuid); iso_tree_node_set_uid(node, 1234); CU_ASSERT_EQUAL(node->attrib.st_uid, 1234); /* test on a link */ node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_EQUAL(node->attrib.st_uid, myuid); iso_tree_node_set_uid(node, 1234); CU_ASSERT_EQUAL(node->attrib.st_uid, 1234); /* test on a file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_EQUAL(node->attrib.st_uid, myuid); iso_tree_node_set_uid(node, 1234); CU_ASSERT_EQUAL(node->attrib.st_uid, 1234); //TODO iso_tree_free((struct iso_tree_node *)root); } static void test_get_uid() { struct iso_tree_node_dir *root; struct iso_tree_node *node; uid_t myuid = getuid(); root = iso_tree_new_root(); /* test on a dir */ node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); CU_ASSERT_EQUAL(iso_tree_node_get_uid(node), myuid); iso_tree_node_set_uid(node, 1234); CU_ASSERT_EQUAL(iso_tree_node_get_uid(node), 1234); /* test on a link */ node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); CU_ASSERT_EQUAL(iso_tree_node_get_uid(node), myuid); iso_tree_node_set_uid(node, 1234); CU_ASSERT_EQUAL(iso_tree_node_get_uid(node), 1234); /* test on a file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); CU_ASSERT_EQUAL(iso_tree_node_get_uid(node), myuid); iso_tree_node_set_uid(node, 1234); CU_ASSERT_EQUAL(iso_tree_node_get_uid(node), 1234); iso_tree_free((struct iso_tree_node *)root); } static void test_set_permissions() { struct iso_tree_node_dir *root; struct iso_tree_node *node; root = iso_tree_new_root(); /* test on a dir */ node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFDIR | 0755); iso_tree_node_set_permissions(node, 0777); CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFDIR | 0777); iso_tree_node_set_permissions(node, 0744); CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFDIR | 0744); iso_tree_node_set_permissions(node, 0411); CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFDIR | 0411); /* test on a link */ node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFLNK | 0777); iso_tree_node_set_permissions(node, 0555); CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFLNK | 0555); iso_tree_node_set_permissions(node, 0744); CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFLNK | 0744); iso_tree_node_set_permissions(node, 0411); CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFLNK | 0411); /* test on a file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); CU_ASSERT_PTR_NOT_NULL(node); CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFREG | 0555); iso_tree_node_set_permissions(node, 0777); CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFREG | 0777); iso_tree_node_set_permissions(node, 0744); CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFREG | 0744); iso_tree_node_set_permissions(node, 0411); CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFREG | 0411); iso_tree_free((struct iso_tree_node *)root); } static void test_get_permissions() { struct iso_tree_node_dir *root; struct iso_tree_node *node; root = iso_tree_new_root(); /* test on a dir */ node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); CU_ASSERT_EQUAL(iso_tree_node_get_permissions(node), 0755); iso_tree_node_set_permissions(node, 0777); CU_ASSERT_EQUAL(iso_tree_node_get_permissions(node), 0777); iso_tree_node_set_permissions(node, 0744); CU_ASSERT_EQUAL(iso_tree_node_get_permissions(node), 0744); iso_tree_node_set_permissions(node, 0411); CU_ASSERT_EQUAL(iso_tree_node_get_permissions(node), 0411); /* test on a link */ node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); CU_ASSERT_EQUAL(iso_tree_node_get_permissions(node), 0777); iso_tree_node_set_permissions(node, 0555); CU_ASSERT_EQUAL(iso_tree_node_get_permissions(node), 0555); iso_tree_node_set_permissions(node, 0744); CU_ASSERT_EQUAL(iso_tree_node_get_permissions(node), 0744); iso_tree_node_set_permissions(node, 0411); CU_ASSERT_EQUAL(iso_tree_node_get_permissions(node), 0411); /* test on a file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); CU_ASSERT_EQUAL(iso_tree_node_get_permissions(node), 0555); iso_tree_node_set_permissions(node, 0777); CU_ASSERT_EQUAL(iso_tree_node_get_permissions(node), 0777); iso_tree_node_set_permissions(node, 0744); CU_ASSERT_EQUAL(iso_tree_node_get_permissions(node), 0744); iso_tree_node_set_permissions(node, 0411); CU_ASSERT_EQUAL(iso_tree_node_get_permissions(node), 0411); iso_tree_free((struct iso_tree_node *)root); } static void test_set_sort_weight() { struct iso_tree_node_dir *root; struct iso_tree_node_dir *dir; struct iso_tree_node_file *file; root = iso_tree_new_root(); dir = iso_tree_add_dir(root, "New dir name"); CU_ASSERT_PTR_NOT_NULL(dir); file = (struct iso_tree_node_file *) iso_tree_add_file(dir, "/tmp/libisofs_test/README"); CU_ASSERT_EQUAL(file->sort_weight, 0); iso_tree_node_set_sort_weight((struct iso_tree_node *) file, 15); CU_ASSERT_EQUAL(file->sort_weight, 15); iso_tree_node_set_sort_weight((struct iso_tree_node *) file, -15); CU_ASSERT_EQUAL(file->sort_weight, -15); /* changes to dir involve update files inside it */ iso_tree_node_set_sort_weight((struct iso_tree_node *) dir, 28); CU_ASSERT_EQUAL(file->sort_weight, 28); iso_tree_free((struct iso_tree_node *)root); } static void test_set_dest() { struct iso_tree_node_dir *root; struct iso_tree_node *node; struct iso_tree_node_symlink *link; root = iso_tree_new_root(); node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); link = (struct iso_tree_node_symlink *) node; CU_ASSERT_STRING_EQUAL( link->dest, "/tmp/libisofs_test/README"); iso_tree_node_symlink_set_dest(link, "/tmp/inexistent"); CU_ASSERT_STRING_EQUAL( link->dest, "/tmp/inexistent"); iso_tree_free((struct iso_tree_node *)root); } static void test_get_dest() { struct iso_tree_node_dir *root; struct iso_tree_node *node; struct iso_tree_node_symlink *link; root = iso_tree_new_root(); node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); link = (struct iso_tree_node_symlink *) node; CU_ASSERT_STRING_EQUAL( iso_tree_node_symlink_get_dest(link), "/tmp/libisofs_test/README"); iso_tree_node_symlink_set_dest(link, "/tmp/inexistent"); CU_ASSERT_STRING_EQUAL( iso_tree_node_symlink_get_dest(link), "/tmp/inexistent"); iso_tree_free((struct iso_tree_node *)root); } static void test_get_node_type() { struct iso_tree_node_dir *root; struct iso_tree_node *node; root = iso_tree_new_root(); /* test on a dir */ node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); CU_ASSERT_EQUAL(iso_tree_node_get_type(node), LIBISO_NODE_DIR); /* test on a link */ node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); CU_ASSERT_EQUAL(iso_tree_node_get_type(node), LIBISO_NODE_SYMLINK); /* test on a file */ node = iso_tree_add_node(root, "/tmp/libisofs_test/README"); CU_ASSERT_EQUAL(iso_tree_node_get_type(node), LIBISO_NODE_FILE); iso_tree_free((struct iso_tree_node *)root); } static void test_children_iter() { struct iso_tree_node_dir *root; struct iso_tree_iter *iter; struct iso_tree_node *child; struct iso_tree_node *node1, *node2, *node3; root = iso_tree_new_root(); node1 = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); node2 = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); node3 = iso_tree_add_node(root, "/tmp/libisofs_test/README"); /* get the iterator */ iter = iso_tree_node_children(root); CU_ASSERT_PTR_NOT_NULL(iter); /* test correct iteration */ child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node1); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node2); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node3); /* and NULL when no more children */ child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); iso_tree_iter_free(iter); /* now an iter on a empty dir */ iter = iso_tree_node_children((struct iso_tree_node_dir *)node1); CU_ASSERT_PTR_NOT_NULL(iter); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); iso_tree_free((struct iso_tree_node *)root); } static void test_tree_node_take() { int res; struct iso_tree_node_dir *root; struct iso_tree_iter *iter; struct iso_tree_node *child; struct iso_tree_node *node1, *node2, *node3; root = iso_tree_new_root(); node1 = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); node2 = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); node3 = iso_tree_add_node(root, "/tmp/libisofs_test/README"); /* we take a ref to each node to test current reference behavior */ iso_tree_node_ref(node1); iso_tree_node_ref(node2); iso_tree_node_ref(node3); /* remove node2 */ res = iso_tree_node_take(root, node2); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_EQUAL(root->nchildren, 2); CU_ASSERT_PTR_NULL(node2->parent); /* node2 should keep both refs */ CU_ASSERT_EQUAL(node2->refcount, 2); /* test iteration */ iter = iso_tree_node_children(root); CU_ASSERT_PTR_NOT_NULL(iter); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node1); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node3); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); iso_tree_iter_free(iter); /* try to remove again node2 */ res = iso_tree_node_take(root, node2); CU_ASSERT_EQUAL(res, -1); CU_ASSERT_EQUAL(root->nchildren, 2); iso_tree_free(node2); /* ok, now remove node1, and then node3 */ res = iso_tree_node_take(root, node1); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_EQUAL(root->nchildren, 1); CU_ASSERT_EQUAL(node1->refcount, 2); CU_ASSERT_PTR_NULL(node1->parent); iter = iso_tree_node_children(root); CU_ASSERT_PTR_NOT_NULL(iter); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node3); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); iso_tree_iter_free(iter); res = iso_tree_node_take(root, node3); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_EQUAL(root->nchildren, 0); CU_ASSERT_EQUAL(node3->refcount, 2); CU_ASSERT_PTR_NULL(node3->parent); iter = iso_tree_node_children(root); CU_ASSERT_PTR_NOT_NULL(iter); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); iso_tree_iter_free(iter); /* and try to remove on an empty dir */ res = iso_tree_node_take(root, node3); CU_ASSERT_EQUAL(res, -1); iso_tree_free(node1); iso_tree_free(node3); iso_tree_free((struct iso_tree_node *)root); iso_tree_free(node1); iso_tree_free(node2); iso_tree_free(node3); } static void test_tree_node_remove() { int res; struct iso_tree_node_dir *root; struct iso_tree_iter *iter; struct iso_tree_node *child; struct iso_tree_node *node1, *node2, *node3; root = iso_tree_new_root(); node1 = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); node2 = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); node3 = iso_tree_add_node(root, "/tmp/libisofs_test/README"); /* we take a ref to each node to test current reference behavior */ iso_tree_node_ref(node1); iso_tree_node_ref(node2); iso_tree_node_ref(node3); /* remove node2 */ res = iso_tree_node_remove(root, node2); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_EQUAL(root->nchildren, 2); CU_ASSERT_PTR_NULL(node2->parent); /* node2 should be unref */ CU_ASSERT_EQUAL(node2->refcount, 1); /* test iteration */ iter = iso_tree_node_children(root); CU_ASSERT_PTR_NOT_NULL(iter); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node1); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node3); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); iso_tree_iter_free(iter); /* try to remove again node2 */ res = iso_tree_node_remove(root, node2); CU_ASSERT_EQUAL(res, -1); CU_ASSERT_EQUAL(root->nchildren, 2); CU_ASSERT_EQUAL(node2->refcount, 1); /* ok, now remove node1, and then node3 */ res = iso_tree_node_remove(root, node1); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_EQUAL(root->nchildren, 1); CU_ASSERT_EQUAL(node1->refcount, 1); CU_ASSERT_PTR_NULL(node1->parent); iter = iso_tree_node_children(root); CU_ASSERT_PTR_NOT_NULL(iter); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node3); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); iso_tree_iter_free(iter); res = iso_tree_node_remove(root, node3); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_EQUAL(root->nchildren, 0); CU_ASSERT_EQUAL(node3->refcount, 1); CU_ASSERT_PTR_NULL(node3->parent); iter = iso_tree_node_children(root); CU_ASSERT_PTR_NOT_NULL(iter); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); iso_tree_iter_free(iter); /* and try to remove on an empty dir */ res = iso_tree_node_remove(root, node3); CU_ASSERT_EQUAL(res, -1); CU_ASSERT_EQUAL(node3->refcount, 1); iso_tree_free((struct iso_tree_node *)root); iso_tree_free(node1); iso_tree_free(node2); iso_tree_free(node3); } static void test_tree_node_take_iter() { int res; struct iso_tree_node_dir *root; struct iso_tree_iter *iter; struct iso_tree_node *child; struct iso_tree_node *node1, *node2, *node3; root = iso_tree_new_root(); node1 = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); node2 = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); node3 = iso_tree_add_node(root, "/tmp/libisofs_test/README"); /* we take a ref to each node to test current reference behavior */ iso_tree_node_ref(node1); iso_tree_node_ref(node2); iso_tree_node_ref(node3); /* begin iteration */ iter = iso_tree_node_children(root); CU_ASSERT_PTR_NOT_NULL(iter); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node1); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node2); /* ok, take node 2 */ res = iso_tree_node_take_iter(iter); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_EQUAL(root->nchildren, 2); CU_ASSERT_EQUAL(node2->refcount, 2); CU_ASSERT_PTR_NULL(node2->parent); /* the iter have to work after remove */ child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node3); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); iso_tree_iter_free(iter); /* ok, now remove before the begining */ iter = iso_tree_node_children(root); CU_ASSERT_PTR_NOT_NULL(iter); res = iso_tree_node_take_iter(iter); CU_ASSERT_TRUE(res < 0); /* and the iter still works */ child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node1); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node3); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); /* and now try to remove at the end */ res = iso_tree_node_take_iter(iter); CU_ASSERT_TRUE(res < 0); iso_tree_iter_free(iter); /* ok, now remove all during iteration */ iter = iso_tree_node_children(root); CU_ASSERT_PTR_NOT_NULL(iter); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node1); res = iso_tree_node_take_iter(iter); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_EQUAL(root->nchildren, 1); CU_ASSERT_EQUAL(node1->refcount, 2); CU_ASSERT_PTR_NULL(node1->parent); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node3); res = iso_tree_node_take_iter(iter); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_EQUAL(root->nchildren, 0); CU_ASSERT_EQUAL(node3->refcount, 2); CU_ASSERT_PTR_NULL(node3->parent); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); iso_tree_iter_free(iter); iso_tree_free(node1); iso_tree_free(node2); iso_tree_free(node3); iso_tree_free((struct iso_tree_node *)root); iso_tree_free(node1); iso_tree_free(node2); iso_tree_free(node3); } static void test_tree_node_remove_iter() { int res; struct iso_tree_node_dir *root; struct iso_tree_iter *iter; struct iso_tree_node *child; struct iso_tree_node *node1, *node2, *node3; root = iso_tree_new_root(); node1 = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); node2 = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); node3 = iso_tree_add_node(root, "/tmp/libisofs_test/README"); /* we take a ref to each node to test current reference behavior */ iso_tree_node_ref(node1); iso_tree_node_ref(node2); iso_tree_node_ref(node3); /* begin iteration */ iter = iso_tree_node_children(root); CU_ASSERT_PTR_NOT_NULL(iter); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node1); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node2); /* ok, remove node 2 */ res = iso_tree_node_remove_iter(iter); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_EQUAL(root->nchildren, 2); CU_ASSERT_EQUAL(node2->refcount, 1); CU_ASSERT_PTR_NULL(node2->parent); /* the iter have to work after remove */ child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node3); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); iso_tree_iter_free(iter); /* ok, now remove before the begining */ iter = iso_tree_node_children(root); CU_ASSERT_PTR_NOT_NULL(iter); res = iso_tree_node_remove_iter(iter); CU_ASSERT_TRUE(res < 0); /* and the iter still works */ child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node1); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node3); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); /* and now try to remove at the end */ res = iso_tree_node_remove_iter(iter); CU_ASSERT_TRUE(res < 0); iso_tree_iter_free(iter); /* ok, now remove all during iteration */ iter = iso_tree_node_children(root); CU_ASSERT_PTR_NOT_NULL(iter); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node1); res = iso_tree_node_remove_iter(iter); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_EQUAL(root->nchildren, 1); CU_ASSERT_EQUAL(node1->refcount, 1); CU_ASSERT_PTR_NULL(node1->parent); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NOT_NULL(child); CU_ASSERT_PTR_EQUAL(child, node3); res = iso_tree_node_remove_iter(iter); CU_ASSERT_EQUAL(res, 0); CU_ASSERT_EQUAL(root->nchildren, 0); CU_ASSERT_EQUAL(node3->refcount, 1); CU_ASSERT_PTR_NULL(node3->parent); child = iso_tree_iter_next(iter); CU_ASSERT_PTR_NULL(child); iso_tree_iter_free(iter); iso_tree_free((struct iso_tree_node *)root); iso_tree_free(node1); iso_tree_free(node2); iso_tree_free(node3); } static void test_tree_node_get_parent() { struct iso_tree_node_dir *root; struct iso_tree_node_dir *parent; struct iso_tree_node *node1, *node2, *node3; root = iso_tree_new_root(); node1 = iso_tree_add_node(root, "/tmp/libisofs_test/dir1"); node2 = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme"); node3 = iso_tree_add_node( (struct iso_tree_node_dir*)node1, "/tmp/libisofs_test/README"); parent = iso_tree_node_get_parent((struct iso_tree_node *)root); CU_ASSERT_PTR_NULL(parent); parent = iso_tree_node_get_parent(node1); CU_ASSERT_PTR_NOT_NULL(parent); CU_ASSERT_PTR_EQUAL(parent, root); parent = iso_tree_node_get_parent(node2); CU_ASSERT_PTR_NOT_NULL(parent); CU_ASSERT_PTR_EQUAL(parent, root); parent = iso_tree_node_get_parent(node3); CU_ASSERT_PTR_NOT_NULL(parent); CU_ASSERT_PTR_EQUAL(parent, node1); iso_tree_node_take(root, node1); parent = iso_tree_node_get_parent(node1); CU_ASSERT_PTR_NULL(parent); iso_tree_free((struct iso_tree_node *)root); iso_tree_free(node1); } void add_tree_suite() { CU_pSuite pSuite = CU_add_suite("TreeSuite", NULL, NULL); CU_add_test(pSuite, "test of iso_tree_new_root()", test_new_root); CU_add_test(pSuite, "test of iso_tree_add_dir()", test_add_dir); CU_add_test(pSuite, "test of iso_tree_add_file()", test_add_file); CU_add_test(pSuite, "test of iso_tree_add_symlink()", test_add_symlink); CU_add_test(pSuite, "test of iso_tree_add_node()", test_add_node); CU_add_test(pSuite, "test of iso_tree_node_set_name()", test_set_name); CU_add_test(pSuite, "test of iso_tree_node_get_name()", test_get_name); CU_add_test(pSuite, "test of iso_tree_node_set_hidden()", test_set_hidden); CU_add_test(pSuite, "test of iso_tree_node_is_hidden()", test_is_hidden); CU_add_test(pSuite, "test of iso_tree_node_set_gid()", test_set_gid); CU_add_test(pSuite, "test of iso_tree_node_get_gid()", test_get_gid); CU_add_test(pSuite, "test of iso_tree_node_set_uid()", test_set_uid); CU_add_test(pSuite, "test of iso_tree_node_get_uid()", test_get_uid); CU_add_test(pSuite, "test of iso_tree_node_set_permissions()", test_set_permissions); CU_add_test(pSuite, "test of iso_tree_node_get_permissions()", test_get_permissions); CU_add_test(pSuite, "test of iso_tree_node_set_sort_weight()", test_set_sort_weight); CU_add_test(pSuite, "test of iso_tree_node_symlink_set_dest()", test_set_dest); CU_add_test(pSuite, "test of iso_tree_node_symlink_get_dest()", test_get_dest); CU_add_test(pSuite, "test of iso_tree_node_get_type()", test_get_node_type); CU_add_test(pSuite, "test of children iteration", test_children_iter); CU_add_test(pSuite, "test of iso_tree_node_take()", test_tree_node_take); CU_add_test(pSuite, "test of iso_tree_node_remove()", test_tree_node_remove); CU_add_test(pSuite, "test of iso_tree_node_take_iter()", test_tree_node_take_iter); CU_add_test(pSuite, "test of iso_tree_node_remove_iter()", test_tree_node_remove_iter); CU_add_test(pSuite, "test of iso_tree_node_get_parent()", test_tree_node_get_parent); } /* * Unit test for util.h * * This test utiliy functions * */ #include #include #include "test.h" #include "util.h" static void test_div_up() { CU_ASSERT_EQUAL( div_up(1, 2), 1 ); CU_ASSERT_EQUAL( div_up(2, 2), 1 ); CU_ASSERT_EQUAL( div_up(0, 2), 0 ); CU_ASSERT_EQUAL( div_up(-1, 2), 0 ); CU_ASSERT_EQUAL( div_up(3, 2), 2 ); } static void test_round_up() { CU_ASSERT_EQUAL( round_up(1, 2), 2 ); CU_ASSERT_EQUAL( round_up(2, 2), 2 ); CU_ASSERT_EQUAL( round_up(0, 2), 0 ); CU_ASSERT_EQUAL( round_up(-1, 2), 0 ); CU_ASSERT_EQUAL( round_up(3, 2), 4 ); CU_ASSERT_EQUAL( round_up(15, 7), 21 ); CU_ASSERT_EQUAL( round_up(13, 7), 14 ); CU_ASSERT_EQUAL( round_up(14, 7), 14 ); } static void test_iso_lsb_msb() { uint8_t buf[4]; uint32_t num; num = 0x01020304; iso_lsb(buf, num, 4); CU_ASSERT_EQUAL( buf[0], 0x04 ); CU_ASSERT_EQUAL( buf[1], 0x03 ); CU_ASSERT_EQUAL( buf[2], 0x02 ); CU_ASSERT_EQUAL( buf[3], 0x01 ); iso_msb(buf, num, 4); CU_ASSERT_EQUAL( buf[0], 0x01 ); CU_ASSERT_EQUAL( buf[1], 0x02 ); CU_ASSERT_EQUAL( buf[2], 0x03 ); CU_ASSERT_EQUAL( buf[3], 0x04 ); iso_lsb(buf, num, 2); CU_ASSERT_EQUAL( buf[0], 0x04 ); CU_ASSERT_EQUAL( buf[1], 0x03 ); iso_msb(buf, num, 2); CU_ASSERT_EQUAL( buf[0], 0x03 ); CU_ASSERT_EQUAL( buf[1], 0x04 ); } static void test_iso_read_lsb_msb() { uint8_t buf[4]; uint32_t num; buf[0] = 0x04; buf[1] = 0x03; buf[2] = 0x02; buf[3] = 0x01; num = iso_read_lsb(buf, 4); CU_ASSERT_EQUAL(num, 0x01020304); num = iso_read_msb(buf, 4); CU_ASSERT_EQUAL(num, 0x04030201); num = iso_read_lsb(buf, 2); CU_ASSERT_EQUAL(num, 0x0304); num = iso_read_msb(buf, 2); CU_ASSERT_EQUAL(num, 0x0403); } static void test_iso_bb() { uint8_t buf[8]; uint32_t num; num = 0x01020304; iso_bb(buf, num, 4); CU_ASSERT_EQUAL( buf[0], 0x04 ); CU_ASSERT_EQUAL( buf[1], 0x03 ); CU_ASSERT_EQUAL( buf[2], 0x02 ); CU_ASSERT_EQUAL( buf[3], 0x01 ); CU_ASSERT_EQUAL( buf[4], 0x01 ); CU_ASSERT_EQUAL( buf[5], 0x02 ); CU_ASSERT_EQUAL( buf[6], 0x03 ); CU_ASSERT_EQUAL( buf[7], 0x04 ); iso_bb(buf, num, 2); CU_ASSERT_EQUAL( buf[0], 0x04 ); CU_ASSERT_EQUAL( buf[1], 0x03 ); CU_ASSERT_EQUAL( buf[2], 0x03 ); CU_ASSERT_EQUAL( buf[3], 0x04 ); } static void test_iso_read_bb() { uint8_t buf[8]; uint32_t num; int error = 0; buf[0] = 0x04; buf[1] = 0x03; buf[2] = 0x02; buf[3] = 0x01; buf[4] = 0x01; buf[5] = 0x02; buf[6] = 0x03; buf[7] = 0x04; num = iso_read_bb(buf, 4, &error); CU_ASSERT_EQUAL( num, 0x01020304 ); CU_ASSERT_FALSE(error); num = iso_read_bb(buf, 4, NULL); CU_ASSERT_EQUAL( num, 0x01020304 ); buf[2] = 3; num = iso_read_bb(buf, 4, &error); /* return the LSB */ CU_ASSERT_EQUAL( num, 0x01030304 ); CU_ASSERT_TRUE(error); num = iso_read_bb(buf, 4, NULL); /* return the LSB */ CU_ASSERT_EQUAL( num, 0x01030304 ); error = 0; buf[3] = 0x04; num = iso_read_bb(buf, 2, &error); CU_ASSERT_EQUAL( num, 0x0304 ); CU_ASSERT_FALSE(error); num = iso_read_bb(buf, 2, NULL); CU_ASSERT_EQUAL( num, 0x0304 ); } static void test_iso_datetime_7() { uint8_t buf[7]; time_t t, t2; struct tm tp; strptime("01-03-1976 13:27:45", "%d-%m-%Y %T", &tp); t = mktime(&tp); iso_datetime_7(buf, t); CU_ASSERT_EQUAL( buf[0], 76 ); /* year since 1900 */ CU_ASSERT_EQUAL( buf[1], 3 ); /* month */ CU_ASSERT_EQUAL( buf[2], 1 ); /* day */ CU_ASSERT_EQUAL( buf[3], 13 ); /* hour */ CU_ASSERT_EQUAL( buf[4], 27 ); /* minute */ CU_ASSERT_EQUAL( buf[5], 45 ); /* second */ /* the offset depends on current timezone and it's not easy to test */ //CU_ASSERT_EQUAL( buf[6], 4 ); /* 15 min offset */ /* check that reading returns the same time */ t2 = iso_datetime_read_7(buf); CU_ASSERT_EQUAL(t2, t); //TODO check with differnt timezones for reading and writting } static void test_iso_1_dirid() { CU_ASSERT_STRING_EQUAL( iso_1_dirid("dir1", "UTF-8"), "DIR1" ); CU_ASSERT_STRING_EQUAL( iso_1_dirid("dIR1", "UTF-8"), "DIR1" ); CU_ASSERT_STRING_EQUAL( iso_1_dirid("DIR1", "UTF-8"), "DIR1" ); CU_ASSERT_STRING_EQUAL( iso_1_dirid("dirwithbigname", "UTF-8"), "DIRWITHB"); CU_ASSERT_STRING_EQUAL( iso_1_dirid("dirwith8", "UTF-8"), "DIRWITH8"); CU_ASSERT_STRING_EQUAL( iso_1_dirid("dir.1", "UTF-8"), "DIR_1"); CU_ASSERT_STRING_EQUAL( iso_1_dirid("4f<0KmM::xcvf", "UTF-8"), "4F_0KMM_"); } static void test_iso_2_dirid() { CU_ASSERT_STRING_EQUAL( iso_2_dirid("dir1", "UTF-8"), "DIR1" ); CU_ASSERT_STRING_EQUAL( iso_2_dirid("dIR1", "UTF-8"), "DIR1" ); CU_ASSERT_STRING_EQUAL( iso_2_dirid("DIR1", "UTF-8"), "DIR1" ); CU_ASSERT_STRING_EQUAL( iso_2_dirid("dirwithbigname", "UTF-8"), "DIRWITHBIGNAME"); CU_ASSERT_STRING_EQUAL( iso_2_dirid("dirwith8", "UTF-8"), "DIRWITH8"); CU_ASSERT_STRING_EQUAL( iso_2_dirid("dir.1", "UTF-8"), "DIR_1"); CU_ASSERT_STRING_EQUAL( iso_2_dirid("4f<0KmM::xcvf", "UTF-8"), "4F_0KMM__XCVF"); CU_ASSERT_STRING_EQUAL( iso_2_dirid("directory with 31 characters ok", "UTF-8"), "DIRECTORY_WITH_31_CHARACTERS_OK"); CU_ASSERT_STRING_EQUAL( iso_2_dirid("directory with more than 31 characters", "UTF-8"), "DIRECTORY_WITH_MORE_THAN_31_CHA"); } static void test_iso_r_dirid() { int flag; /* 1. only ECMA119_37_CHAR_FILENAMES */ flag = ECMA119_37_CHAR_FILENAMES; CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir1", "UTF-8", flag), "DIR1"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("dIR1", "UTF-8", flag), "DIR1"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("DIR1", "UTF-8", flag), "DIR1"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwithbigname", "UTF-8", flag), "DIRWITHBIGNAME"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwith8", "UTF-8", flag), "DIRWITH8"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir.1", "UTF-8", flag), "DIR_1"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("4f<0KmM::xcvf", "UTF-8", flag), "4F_0KMM__XCVF"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with 31 characters ok", "UTF-8", flag), "DIRECTORY_WITH_31_CHARACTERS_OK"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with more than 37 characters", "UTF-8", flag), "DIRECTORY_WITH_MORE_THAN_37_CHARACTER"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with just 37 characters ok", "UTF-8", flag), "DIRECTORY_WITH_JUST_37_CHARACTERS__OK"); /* 2. only ECMA119_RELAXED_FILENAMES */ flag = ECMA119_RELAXED_FILENAMES; CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir1", "UTF-8", flag), "dir1"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("dIR1", "UTF-8", flag), "dIR1"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("DIR1", "UTF-8", flag), "DIR1"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwithbigname", "UTF-8", flag), "dirwithbigname"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwith8", "UTF-8", flag), "dirwith8"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir.1", "UTF-8", flag), "dir.1"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("4f<0KmM::xcvf", "UTF-8", flag), "4f<0KmM::xcvf"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with 31 characters ok", "UTF-8", flag), "directory with 31 characters ok"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with more than 37 characters", "UTF-8", flag), "directory with more than 37 cha"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with just 37 characters ok", "UTF-8", flag), "directory with just 37 characte"); /* 3. both ECMA119_RELAXED_FILENAMES and ECMA119_37_CHAR_FILENAMES */ flag = ECMA119_RELAXED_FILENAMES | ECMA119_37_CHAR_FILENAMES; CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir1", "UTF-8", flag), "dir1"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("dIR1", "UTF-8", flag), "dIR1"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("DIR1", "UTF-8", flag), "DIR1"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwithbigname", "UTF-8", flag), "dirwithbigname"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwith8", "UTF-8", flag), "dirwith8"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir.1", "UTF-8", flag), "dir.1"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("4f<0KmM::xcvf", "UTF-8", flag), "4f<0KmM::xcvf"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with 31 characters ok", "UTF-8", flag), "directory with 31 characters ok"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with more than 37 characters", "UTF-8", flag), "directory with more than 37 character"); CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with just 37 characters ok", "UTF-8", flag), "directory with just 37 characters ok"); } static void test_iso_1_fileid() { CU_ASSERT_STRING_EQUAL( iso_1_fileid("file1", "UTF-8"), "FILE1.;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("fILe1", "UTF-8"), "FILE1.;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("FILE1", "UTF-8"), "FILE1.;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid(".EXT", "UTF-8"), ".EXT;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.ext", "UTF-8"), "FILE.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("fiLE.ext", "UTF-8"), "FILE.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.EXt", "UTF-8"), "FILE.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("FILE.EXT", "UTF-8"), "FILE.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename", "UTF-8"), "BIGFILEN.;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename.ext", "UTF-8"), "BIGFILEN.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename.e", "UTF-8"), "BIGFILEN.E;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.bigext", "UTF-8"), "FILE.BIG;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid(".bigext", "UTF-8"), ".BIG;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename.bigext", "UTF-8"), "BIGFILEN.BIG;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("file<:a.ext", "UTF-8"), "FILE__A.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.<:a", "UTF-8"), "FILE.__A;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("file<:a.--a", "UTF-8"), "FILE__A.__A;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.ex1.ex2", "UTF-8"), "FILE_EX1.EX2;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.ex1.ex2.ex3", "UTF-8"), "FILE_EX1.EX3;1"); CU_ASSERT_STRING_EQUAL( iso_1_fileid("fil.ex1.ex2.ex3", "UTF-8"), "FIL_EX1_.EX3;1"); } static void test_iso_2_fileid() { CU_ASSERT_STRING_EQUAL( iso_2_fileid("file1", "UTF-8"), "FILE1.;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("fILe1", "UTF-8"), "FILE1.;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("FILE1", "UTF-8"), "FILE1.;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid(".EXT", "UTF-8"), ".EXT;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.ext", "UTF-8"), "FILE.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("fiLE.ext", "UTF-8"), "FILE.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.EXt", "UTF-8"), "FILE.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("FILE.EXT", "UTF-8"), "FILE.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename", "UTF-8"), "BIGFILENAME.;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename.ext", "UTF-8"), "BIGFILENAME.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename.e", "UTF-8"), "BIGFILENAME.E;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("31 characters filename.extensio", "UTF-8"), "31_CHARACTERS_FILENAME.EXTENSIO;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("32 characters filename.extension", "UTF-8"), "32_CHARACTERS_FILENAME.EXTENSIO;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("more than 30 characters filename.extension", "UTF-8"), "MORE_THAN_30_CHARACTERS_FIL.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.bigext", "UTF-8"), "FILE.BIGEXT;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid(".bigext", "UTF-8"), ".BIGEXT;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename.bigext", "UTF-8"), "BIGFILENAME.BIGEXT;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("file<:a.ext", "UTF-8"), "FILE__A.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.<:a", "UTF-8"), "FILE.__A;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("file<:a.--a", "UTF-8"), "FILE__A.__A;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.ex1.ex2", "UTF-8"), "FILE_EX1.EX2;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.ex1.ex2.ex3", "UTF-8"), "FILE_EX1_EX2.EX3;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid("fil.ex1.ex2.ex3", "UTF-8"), "FIL_EX1_EX2.EX3;1"); CU_ASSERT_STRING_EQUAL( iso_2_fileid(".file.bigext", "UTF-8"), "_FILE.BIGEXT;1"); } static void test_iso_r_fileid() { int flag; /* 1. only ECMA119_OMIT_VERSION_NUMBERS */ flag = ECMA119_OMIT_VERSION_NUMBERS; CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "FILE1."); CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "FILE1."); CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31_CHARACTERS_FILENAME.EXTENSIO"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "IT_S_37_CHARACTERS_FILENAME.EXT"); /* 2. only ECMA119_37_CHAR_FILENAMES */ flag = ECMA119_37_CHAR_FILENAMES; CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "FILE1."); CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "FILE1."); CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE1", "UTF-8", flag), "FILE1."); CU_ASSERT_STRING_EQUAL( iso_r_fileid(".EXT", "UTF-8", flag), ".EXT"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.ext", "UTF-8", flag), "FILE.EXT"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("fiLE.ext", "UTF-8", flag), "FILE.EXT"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.EXt", "UTF-8", flag), "FILE.EXT"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE.EXT", "UTF-8", flag), "FILE.EXT"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename", "UTF-8", flag), "BIGFILENAME."); CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.ext", "UTF-8", flag), "BIGFILENAME.EXT"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.e", "UTF-8", flag), "BIGFILENAME.E"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.bigext", "UTF-8", flag), "FILE.BIGEXT"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31_CHARACTERS_FILENAME.EXTENSIO"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "IT_S_37_CHARACTERS_FILENAME.EXTENSION"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("more than 37 characters filename.extension", "UTF-8", flag), "MORE_THAN_37_CHARACTERS_FILENAME.EXTE"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a 37 chars len extension", "UTF-8", flag), "FILE.THIS_IS_A_37_CHARS_LEN_EXTENSION"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a very very big extension", "UTF-8", flag), "FILE.THIS_IS_A_VERY_VERY_BIG_EXTENSIO"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("fil.ex1.ex2.ex3", "UTF-8", flag), "FIL_EX1_EX2.EX3"); /* 3. only ECMA119_RELAXED_FILENAMES */ flag = ECMA119_RELAXED_FILENAMES; CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "file1;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "fILe1;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE1", "UTF-8", flag), "FILE1;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid(".EXT", "UTF-8", flag), ".EXT;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.ext", "UTF-8", flag), "file.ext;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("fiLE.ext", "UTF-8", flag), "fiLE.ext;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.EXt", "UTF-8", flag), "file.EXt;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE.EXT", "UTF-8", flag), "FILE.EXT;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename", "UTF-8", flag), "bigfilename;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.ext", "UTF-8", flag), "bigfilename.ext;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.e", "UTF-8", flag), "bigfilename.e;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.bigext", "UTF-8", flag), "file.bigext;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31 characters filename.extensio;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "it's 37 characters filename.ext;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a 37 chars len extension", "UTF-8", flag), "file.this is a 37 chars len ext;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("fil.ex1.ex2.ex3", "UTF-8", flag), "fil.ex1.ex2.ex3;1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.<:a", "UTF-8", flag), "file.<:a;1"); /* 3. ECMA119_RELAXED_FILENAMES and ECMA119_OMIT_VERSION_NUMBERS*/ flag = ECMA119_RELAXED_FILENAMES | ECMA119_OMIT_VERSION_NUMBERS; CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "file1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "fILe1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE1", "UTF-8", flag), "FILE1"); CU_ASSERT_STRING_EQUAL( iso_r_fileid(".EXT", "UTF-8", flag), ".EXT"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.ext", "UTF-8", flag), "file.ext"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("fiLE.ext", "UTF-8", flag), "fiLE.ext"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.EXt", "UTF-8", flag), "file.EXt"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE.EXT", "UTF-8", flag), "FILE.EXT"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename", "UTF-8", flag), "bigfilename"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.ext", "UTF-8", flag), "bigfilename.ext"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.e", "UTF-8", flag), "bigfilename.e"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.bigext", "UTF-8", flag), "file.bigext"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31 characters filename.extensio"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "it's 37 characters filename.ext"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a 37 chars len extension", "UTF-8", flag), "file.this is a 37 chars len ext"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("fil.ex1.ex2.ex3", "UTF-8", flag), "fil.ex1.ex2.ex3"); CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.<:a", "UTF-8", flag), "file.<:a"); } void add_util_suite() { CU_pSuite pSuite = CU_add_suite("UtilSuite", NULL, NULL); CU_add_test(pSuite, "div_up()", test_div_up); CU_add_test(pSuite, "round_up()", test_round_up); CU_add_test(pSuite, "iso_lsb() and iso_msb()", test_iso_lsb_msb); CU_add_test(pSuite, "iso_read_lsb() and iso_read_msb()", test_iso_read_lsb_msb); CU_add_test(pSuite, "iso_bb()", test_iso_bb); CU_add_test(pSuite, "iso_read_bb()", test_iso_read_bb); CU_add_test(pSuite, "iso_datetime_7()", test_iso_datetime_7); CU_add_test(pSuite, "iso_1_dirid()", test_iso_1_dirid); CU_add_test(pSuite, "iso_2_dirid()", test_iso_2_dirid); CU_add_test(pSuite, "iso_r_dirid()", test_iso_r_dirid); CU_add_test(pSuite, "iso_1_fileid()", test_iso_1_fileid); CU_add_test(pSuite, "iso_2_fileid()", test_iso_2_fileid); CU_add_test(pSuite, "iso_r_fileid()", test_iso_r_fileid); } /* * Unit test for volume.h */ #include "libisofs.h" #include "tree.h" #include "test.h" #include "volume.h" #include #include #include #include #include #include static void test_iso_volume_new() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_PTR_NOT_NULL(volume); CU_ASSERT_EQUAL(volume->refcount, 1); /* a new root must be created */ CU_ASSERT_PTR_NOT_NULL(volume->root); CU_ASSERT_STRING_EQUAL( volume->volume_id, "volume_id" ); CU_ASSERT_STRING_EQUAL( volume->publisher_id, "publisher_id" ); CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "data_preparer_id" ); CU_ASSERT_PTR_NULL(volume->system_id); CU_ASSERT_PTR_NULL(volume->application_id); CU_ASSERT_PTR_NULL(volume->copyright_file_id); CU_ASSERT_PTR_NULL(volume->abstract_file_id); CU_ASSERT_PTR_NULL(volume->biblio_file_id); CU_ASSERT_PTR_NULL(volume->bootcat); iso_volume_free(volume); } static void test_iso_volume_new_with_root() { struct iso_volume *volume; struct iso_tree_node_dir *root; root = iso_tree_new_root(); volume = iso_volume_new_with_root("volume_id", "publisher_id", "data_preparer_id", root); CU_ASSERT_PTR_NOT_NULL(volume); CU_ASSERT_EQUAL(volume->refcount, 1); CU_ASSERT_PTR_NOT_NULL(volume->root); CU_ASSERT_PTR_EQUAL(volume->root, root); CU_ASSERT_STRING_EQUAL( volume->volume_id, "volume_id" ); CU_ASSERT_STRING_EQUAL( volume->publisher_id, "publisher_id" ); CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "data_preparer_id" ); CU_ASSERT_PTR_NULL(volume->system_id); CU_ASSERT_PTR_NULL(volume->application_id); CU_ASSERT_PTR_NULL(volume->copyright_file_id); CU_ASSERT_PTR_NULL(volume->abstract_file_id); CU_ASSERT_PTR_NULL(volume->biblio_file_id); CU_ASSERT_PTR_NULL(volume->bootcat); iso_volume_free(volume); } static void test_iso_volume_get_root() { struct iso_volume *volume; struct iso_tree_node_dir *root; struct iso_tree_node_dir *root2; root = iso_tree_new_root(); volume = iso_volume_new_with_root("volume_id", "publisher_id", "data_preparer_id", root); root2 = iso_volume_get_root(volume); CU_ASSERT_PTR_NOT_NULL(root2); CU_ASSERT_PTR_EQUAL(root2, volume->root); CU_ASSERT_PTR_EQUAL(root2, root); iso_volume_free(volume); } static void test_iso_volume_set_volume_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_STRING_EQUAL( volume->volume_id, "volume_id" ); char *volid = "new volume id"; iso_volume_set_volume_id(volume, volid); CU_ASSERT_STRING_EQUAL( volume->volume_id, "new volume id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( volume->volume_id, volid ); iso_volume_free(volume); } static void test_iso_volume_get_volume_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_STRING_EQUAL( iso_volume_get_volume_id(volume), "volume_id" ); char *volid = "new volume id"; iso_volume_set_volume_id(volume, volid); CU_ASSERT_STRING_EQUAL( iso_volume_get_volume_id(volume), "new volume id" ); iso_volume_free(volume); } static void test_iso_volume_set_publisher_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_STRING_EQUAL( volume->publisher_id, "publisher_id" ); char *pubid = "new publisher id"; iso_volume_set_publisher_id(volume, pubid); CU_ASSERT_STRING_EQUAL( volume->publisher_id, "new publisher id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( volume->publisher_id, pubid ); iso_volume_free(volume); } static void test_iso_volume_get_publisher_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_STRING_EQUAL( iso_volume_get_publisher_id(volume), "publisher_id" ); char *pubid = "new publisher id"; iso_volume_set_publisher_id(volume, pubid); CU_ASSERT_STRING_EQUAL( iso_volume_get_publisher_id(volume), "new publisher id" ); iso_volume_free(volume); } static void test_iso_volume_set_data_preparer_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "data_preparer_id" ); char *dpid = "new data preparer id"; iso_volume_set_data_preparer_id(volume, dpid); CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "new data preparer id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( volume->data_preparer_id, dpid ); iso_volume_free(volume); } static void test_iso_volume_get_data_preparer_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_STRING_EQUAL( iso_volume_get_data_preparer_id(volume), "data_preparer_id" ); char *dpid = "new data preparer id"; iso_volume_set_data_preparer_id(volume, dpid); CU_ASSERT_STRING_EQUAL( iso_volume_get_data_preparer_id(volume), "new data preparer id" ); iso_volume_free(volume); } static void test_iso_volume_set_system_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_PTR_NULL(volume->system_id); char *sysid = "new system id"; iso_volume_set_system_id(volume, sysid); CU_ASSERT_STRING_EQUAL( volume->system_id, "new system id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( volume->system_id, sysid ); iso_volume_free(volume); } static void test_iso_volume_get_system_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_PTR_NULL(iso_volume_get_system_id(volume)); char *sysid = "new system id"; iso_volume_set_system_id(volume, sysid); CU_ASSERT_STRING_EQUAL( iso_volume_get_system_id(volume), "new system id" ); iso_volume_free(volume); } static void test_iso_volume_set_application_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_PTR_NULL(volume->application_id); char *appid = "new application id"; iso_volume_set_application_id(volume, appid); CU_ASSERT_STRING_EQUAL( volume->application_id, "new application id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( volume->application_id, appid ); iso_volume_free(volume); } static void test_iso_volume_get_application_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_PTR_NULL(iso_volume_get_application_id(volume)); char *appid = "new application id"; iso_volume_set_application_id(volume, appid); CU_ASSERT_STRING_EQUAL( iso_volume_get_application_id(volume), "new application id" ); iso_volume_free(volume); } static void test_iso_volume_set_copyright_file_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_PTR_NULL(volume->copyright_file_id); char *copid = "new copyright id"; iso_volume_set_copyright_file_id(volume, copid); CU_ASSERT_STRING_EQUAL( volume->copyright_file_id, "new copyright id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( volume->copyright_file_id, copid ); iso_volume_free(volume); } static void test_iso_volume_get_copyright_file_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_PTR_NULL(iso_volume_get_copyright_file_id(volume)); char *copid = "new copyright id"; iso_volume_set_copyright_file_id(volume, copid); CU_ASSERT_STRING_EQUAL( iso_volume_get_copyright_file_id(volume), "new copyright id" ); iso_volume_free(volume); } static void test_iso_volume_set_abstract_file_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_PTR_NULL(volume->abstract_file_id); char *absid = "new abstract id"; iso_volume_set_abstract_file_id(volume, absid); CU_ASSERT_STRING_EQUAL( volume->abstract_file_id, "new abstract id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( volume->abstract_file_id, absid ); iso_volume_free(volume); } static void test_iso_volume_get_abstract_file_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_PTR_NULL(iso_volume_get_abstract_file_id(volume)); char *absid = "new abstract id"; iso_volume_set_abstract_file_id(volume, absid); CU_ASSERT_STRING_EQUAL(iso_volume_get_abstract_file_id(volume), "new abstract id"); iso_volume_free(volume); } static void test_iso_volume_set_biblio_file_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_PTR_NULL(volume->biblio_file_id); char *bibid = "new biblio id"; iso_volume_set_biblio_file_id(volume, bibid); CU_ASSERT_STRING_EQUAL( volume->biblio_file_id, "new biblio id" ); /* check string was strdup'ed */ CU_ASSERT_PTR_NOT_EQUAL( volume->biblio_file_id, bibid ); iso_volume_free(volume); } static void test_iso_volume_get_biblio_file_id() { struct iso_volume *volume; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); CU_ASSERT_PTR_NULL(iso_volume_get_biblio_file_id(volume)); char *bibid = "new biblio id"; iso_volume_set_biblio_file_id(volume, bibid); CU_ASSERT_STRING_EQUAL(iso_volume_get_biblio_file_id(volume), "new biblio id"); iso_volume_free(volume); } static void test_iso_volset_new() { struct iso_volume *volume; struct iso_volset *volset; volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id"); volset = iso_volset_new(volume, "volset_id"); CU_ASSERT_PTR_NOT_NULL(volset); CU_ASSERT_EQUAL(volset->refcount, 1); CU_ASSERT_EQUAL(volset->volset_size, 1); CU_ASSERT_PTR_NOT_NULL(volset->volume); CU_ASSERT_PTR_NOT_NULL(volset->volume[0]); CU_ASSERT_PTR_EQUAL(volset->volume[0], volume); CU_ASSERT_STRING_EQUAL( volset->volset_id, "volset_id" ); iso_volset_free(volset); } void add_volume_suite() { CU_pSuite pSuite = CU_add_suite("VolumeSuite", NULL, NULL); CU_add_test(pSuite, "test of iso_volume_new()", test_iso_volume_new); CU_add_test(pSuite, "test of iso_volume_new_with_root()", test_iso_volume_new_with_root); CU_add_test(pSuite, "test of iso_volume_get_root()", test_iso_volume_get_root); CU_add_test(pSuite, "test of iso_volume_set_volume_id()", test_iso_volume_set_volume_id); CU_add_test(pSuite, "test of iso_volume_get_volume_id()", test_iso_volume_get_volume_id); CU_add_test(pSuite, "test of iso_volume_set_publisher_id()", test_iso_volume_set_publisher_id); CU_add_test(pSuite, "test of iso_volume_get_publisher_id()", test_iso_volume_get_publisher_id); CU_add_test(pSuite, "test of iso_volume_set_data_preparer_id()", test_iso_volume_set_data_preparer_id); CU_add_test(pSuite, "test of iso_volume_get_data_preparer_id()", test_iso_volume_get_data_preparer_id); CU_add_test(pSuite, "test of iso_volume_set_system_id()", test_iso_volume_set_system_id); CU_add_test(pSuite, "test of iso_volume_get_system_id()", test_iso_volume_get_system_id); CU_add_test(pSuite, "test of iso_volume_set_application_id()", test_iso_volume_set_application_id); CU_add_test(pSuite, "test of iso_volume_get_application_id()", test_iso_volume_get_application_id); CU_add_test(pSuite, "test of iso_volume_set_copyright_file_id()", test_iso_volume_set_copyright_file_id); CU_add_test(pSuite, "test of iso_volume_get_copyright_file_id()", test_iso_volume_get_copyright_file_id); CU_add_test(pSuite, "test of iso_volume_set_abstract_file_id()", test_iso_volume_set_abstract_file_id); CU_add_test(pSuite, "test of iso_volume_get_abstract_file_id()", test_iso_volume_get_abstract_file_id); CU_add_test(pSuite, "test of iso_volume_set_biblio_file_id()", test_iso_volume_set_biblio_file_id); CU_add_test(pSuite, "test of iso_volume_get_biblio_file_id()", test_iso_volume_get_biblio_file_id); CU_add_test(pSuite, "test of iso_volset_new()", test_iso_volset_new); } # 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() #define BURN_MAJOR_VERSION @BURN_MAJOR_VERSION@ #define BURN_MINOR_VERSION @BURN_MINOR_VERSION@ #define BURN_MICRO_VERSION @BURN_MICRO_VERSION@ java-libburn org.eclipse.jdt.core.javabuilder org.eclipse.jdt.core.javanature java-libburn org.eclipse.jdt.core.javabuilder org.eclipse.jdt.core.javanature == Experimental == This part of libburnia project (libburnia-project.org) subversion repository contains and will contain experimental implementations and new explorations of either libburnia project team members or outside contributors. It is merely intented as a playground where we can toy around with new libraries, applications, and other creations. Those toys that reach certain level of maturity will be subject of inclusion in the regular libburnia development and release process. Jaime Thomas (avi.thomas@gmail.com) Mario Danic 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 Jaime Thomas Copyright (C) 2008 Jaime Thomas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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 First post INSTALL See README file for instructions. ## Process this file with automake to produce Makefile.in SUBDIRS = src tools data EXTRA_DIST = \ README AUTHORS COPYING MAINTAINERCLEANFILES = Makefile.in aclocal.m4 config.* \ configure install-sh ltconfig ltmain.sh missing mkinstalldirs \ stamp-h.in Makefile Enlightenment CD Burner INSTALL 1) ./autogen.sh 2) make 3) make install ECDB needs to be installed for audio transcoding to work correctly. NEW NAME! Flourine Einferno Enflame TODO ////////////////////////////////////////////////////////////////////////////// -> unbreak appending to discs -> reset burn data gui better following a burn -> More graceful handling of errors -> ffmpeg as an alternative backend to gstreamer -> finish interface -> more flexible typebuf -> more configuration options (speed and drive choice done) -> figure out what to do about dnd, should it be consolidated into one system, how to handle internel, etc. -> don't load the sources all at one time (lots of memory consumed -- for my entire development tree containing 154256 files, ecdb uses 46mb) -> integrate exchange -> instead of always swallowing widgets, allow theme to specify on a widget-by-widget basis if it wants to emit signals from internal parts (so, for example, the settings could be triggered by a small + in the corner, and expand from there) ->able to allow entire gui to be handled by theme... so we only interact with signals (no widgets at all) DONE ////////////////////////////////////////////////////////////////////////////// -> better way to detect pipe deletion (done? - needs more testing) -> get rid of evas_object_name_find/name_set -> Detect if the media in a drive has changed and update the drive info using dbus. -> make theme and code use a custom signal, rather than mouse,clicked,1 -> fix combo theme (good enough, and hopefully someone will make a nicer default theme anyways) #!/bin/sh rm -rf autom4te.cache rm -f aclocal.m4 ltmain.sh touch README echo "Running aclocal..." ; aclocal $ACLOCAL_FLAGS || exit 1 echo "Running autoheader..." ; autoheader || exit 1 echo "Running autoconf..." ; autoconf || exit 1 echo "Running libtoolize..." ; (libtoolize --copy --automake || glibtoolize --automake) || exit 1 echo "Running automake..." ; automake --add-missing --copy --gnu || exit 1 if [ -z "$NOCONFIGURE" ]; then ./configure "$@" fi dnl Process this file with autoconf to produce a configure script. # get rid of that stupid cache mechanism rm -f config.cache AC_INIT(configure.in) AM_INIT_AUTOMAKE(ecdb, 0.0.1) AM_CONFIG_HEADER(src/config.h) AC_ISC_POSIX AC_PROG_CC AM_PROG_CC_STDC AM_PROG_CC_C_O AC_HEADER_STDC AC_C_CONST AM_ENABLE_SHARED AM_PROG_LIBTOOL AM_WITH_DMALLOC dnl Add the languages which your application supports here. ALL_LINGUAS="" dnl Set PACKAGE_LOCALE_DIR in config.h. if test "x${prefix}" = "xNONE"; then AC_DEFINE_UNQUOTED(PACKAGE_LOCALE_DIR, "${ac_default_prefix}/${DATADIRNAME}/locale", [Locale-specific data directory]) else AC_DEFINE_UNQUOTED(PACKAGE_LOCALE_DIR, "${prefix}/${DATADIRNAME}/locale", [Locale-specific data directory]) fi dnl Set PACKAGE_DATA_DIR in config.h. if test "x${prefix}" = "xNONE"; then AC_DEFINE_UNQUOTED(PACKAGE_DATA_DIR, "${ac_default_prefix}/share/${PACKAGE}", [PACKAGE_DATA_DIR]) else AC_DEFINE_UNQUOTED(PACKAGE_DATA_DIR, "${prefix}/share/${PACKAGE}", [PACKAGE_DATA_DIR]) fi dnl Set PACKAGE_BIN_DIR in config.h. if test "x${datadir}" = 'x${prefix}/bin'; then if test "x${prefix}" = "xNONE"; then AC_DEFINE_UNQUOTED(PACKAGE_BIN_DIR, "${ac_default_prefix}/bin", [Installation directory for user executables]) else AC_DEFINE_UNQUOTED(PACKAGE_BIN_DIR, "${prefix}/bin", [Installation directory for user executables]) fi else AC_DEFINE_UNQUOTED(PACKAGE_BIN_DIR, "${bindir}", [Installation directory for user executables]) fi dnl Set PACKAGE_LIB_DIR in config.h. if test "x${datadir}" = 'x${prefix}/lib'; then if test "x${prefix}" = "xNONE"; then AC_DEFINE_UNQUOTED(PACKAGE_LIB_DIR, "${ac_default_prefix}/lib", [Installation directory for libraries]) else AC_DEFINE_UNQUOTED(PACKAGE_LIB_DIR, "${prefix}/lib", [Installation directory for libraries]) fi else AC_DEFINE_UNQUOTED(PACKAGE_LIB_DIR, "${libdir}", [Installation directory for libraries]) fi dnl Set PACKAGE_SOURCE_DIR in config.h. packagesrcdir=`cd $srcdir && pwd` AC_DEFINE_UNQUOTED(PACKAGE_SOURCE_DIR, "${packagesrcdir}", [Source code directory]) dnl Use -Wall if we have gcc. changequote(,)dnl if test "x$GCC" = "xyes"; then case " $CFLAGS " in *[\ \ ]-Wall[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Wall" ;; esac fi changequote([,])dnl LIBBURN_REQUIRED=0.4.2 LIBISOFS_REQUIRED=0.6.10 GST_MAJORMINOR=0.10 GST_REQS=0.10.2 GSTPLUG_REQS=0.10.1 PKG_CHECK_MODULES(GSTREAMER, [ gstreamer-$GST_MAJORMINOR >= $GST_REQS gstreamer-plugins-base-$GST_MAJORMINOR >= $GSTPLUG_REQS], [ have_gstreamer="yes" AC_DEFINE(BUILD_GSTREAMER_SUPPORT, 1, [Enable Gstreamer Support for Ecdb]) ], [have_gstreamer="false"]) AM_CONDITIONAL(GSTREAMER_BUILD_SUPPORT, test "x$have_gstreamer" = "xyes") dnl Make gstreamer an optional dependancy at some point PKG_CHECK_MODULES(ECDB, [ ecore-file ecore-evas ecore-config ecore eina-0 evas ewl edje efreet-mime efreet edbus ehal libburn-1 >= $LIBBURN_REQUIRED libisofs-1 >= $LIBISOFS_REQUIRED ]) AC_OUTPUT([ Makefile src/Makefile tools/Makefile tools/ecdb_transcode_helper/Makefile data/Makefile data/themes/Makefile data/themes/default/Makefile data/themes/default/groups/Makefile ]) echo echo "ECDB" echo echo "Optional Components:" echo "Gstreamer Support............................: $have_gstreamer" echo SUBDIRS = themes SUBDIRS = default MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = default.edc EDJE_CC = edje_cc EDJE_FLAGS = \ -v \ -id $(top_srcdir)/data/themes/default/images \ -fd $(top_srcdir)/data/themes/default/fonts defaultdir = $(pkgdatadir)/themes default_DATA = default.edj default.edj: Makefile default.edc $(EDJE_CC) $(EDJE_FLAGS) \ $(top_srcdir)/data/themes/default/default.edc \ $(top_builddir)/data/themes/default/default.edj clean-local: rm -f *.edj /* This is just an adaptation of the Detour theme, so most of the credit * here goes to the people that worked on that: Alberto Castro, et al */ #include "images/images.edc" #include "fonts/fonts.edc" collections { #include "groups/window.edc" #include "groups/welcome_page.edc" #include "groups/button.edc" #include "groups/erase_disc.edc" #include "groups/check.edc" #include "groups/burn_image.edc" #include "groups/entry.edc" #include "groups/combo.edc" #include "groups/label.edc" #include "groups/burn_data.edc" #include "groups/capacity.edc" #include "groups/config_inwin.edc" #include "groups/icons.edc" #include "groups/about.edc" } 0FFTMKž¥Z<GDEF7Ü=@XnGPOSÈ¡æȃdGSUB}y œ‡, OS/2ù" ˜LVcmapǪ-'˜¤Šcvt i9¬0þfpgmq4vj®0«gasp®Ü glyfP¦®è}headìn¨M,6hhea ¸ç,<$hmtxëZq,`RXkern«t¿~¸<locaDRøøºÀR\maxp j  namekM  <=post°G"aJDÊJprep;ñ hê”·ÃúQÃúQ  S°±ÅÆÆÇÊËÌÍÑÒÓÔäåéêóôôõõöö÷ÿ     ñòòóôõýþÿ    +,,-/045ST_`ÁÂÊËíîîïðñøùÿ¤¥ª« Ã Ä Ä Å Î Ï Ñ Ò     ) * 0 1 1 2 5 6 7 8 O P Q R j k k lÞßëììííî÷øoppqqrrsuvvw‚ö÷þÿÇÈÒÓ&'./ˆ‰‰Š•±ÆÇÊÍרØÙÙÚäêíîõöö÷÷øúûýþÿ  | DFLT>arabHarmnhcyrlrgrekŒhebršlao ¦latn´nko ètfngôÿÿKUR SND URD ÿÿÿÿMKD SRB ÿÿÿÿÿÿÿÿ(ISM (KSM (LSM (NSM (SKS (SSM (ÿÿÿÿÿÿkern2mark8markFmarkNmarkVmkmk\mkmkdmkmkj     (08@JRZbjrz‚Šš‚Ôp¢0ò ¨ Ör /Œ0´2d466p8`FY¶0&: þ þÔýóVY^VY^Æžî &,28>DJPV\bhntz€†Œ:¤:Ü::ܤþrü Äü4 Ä4Ð `,TUWXZ[\]_'()*+,-.,TUWXZ[\]_'()*+,-.JPV\bhntz€†Œ’˜ž¤ª°°°°°°°°°°°°°°°°°°°$ ýlýàøõö ý¨ý¨N>X  &,ý„öýlwýlwýlwýfný„öîñòóô÷ &,28ýl`ýl~ýl~ýl`ýl~ýl`L€ "FLRX^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú þýÞþýÞþý'þý'þýÚþýØþýØþý'þþ‰þþ‰þþ‰þýÚþþþþtþþtþý™þþ þþ;þýÞþýÞþþ8þþ8þþ8þþ¿þýÚþþ þý-þþ þþþþþþþþþþþýTÇÊÍÑÔâ êíøúþÿ!ÇÊÍÑÔ× Úâ êíøúþÿ ‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú $*06<þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþBÞŽ :v|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú $*06<BHNTZ`flrx~„Š–œ¢¨®´ºÀÆÌþhþhþhþ=þþþDþþþÅþþhþhþ¨þ¨þhþÚþDþ“þþhþhþ=þÐþDþDþþþþÚþnnnnþþþþþhþhþþþþþþþþøþøþøþøþøþøþøþøþøþ±Âîîðóõõ÷÷ûý  !¥ª# * 0)ÈÈ0ÊÊ1ÌÒ2‰‰9 ±Âîîðóõõ÷÷ûý¥ª * 0%ÈÒ,‰‰78âèîôú $*06<BHNTZ`flrx~„Š–œ¢¨®´ºÀÆÌÒØÞäêðöü &,þ{þ{þ{þ°þþþþþþ{þ{þ{þ{þ{þ{þ{þþþþþ{þ{þ°þþþþ{þ{þ{þþ{þþþþþ{þ{þþþþþþþþ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{¸®Ú $6HZl~ cÿœÈÿj cÿœÈÿj cÿœÈÿj cÿœÈÿj cÿœÈýÚ cÿœÈýÚ cÿœÈÿj cÿœÈÿj÷þõö÷øùúûüý VY^>DJPV\bhntz€†Œ’®* ôêðöü &,28>DJPV\bhntz€†Œ’˜ž¤ª°¶¼ÂÈÎÔÚàæìòøþ "(.4:@FLRX^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú $*06<BHNTZ`flrx~„Š–œ¢¨®´ºÀÆÌÒØÞäêðöü &,28>DJPV\bhntz€†Œ’˜ž¤ª°¶¼ÂÈÎÔÚàæìòøþ "(.4:@FLRX^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú     $ * 0 6 < B H N T Z ` f l r x ~ „ Š – œ ¢ ¨ ® ´ º À Æ Ì Ò Ø Þ ä ê ð ö ü      & , 2 8 > D J P V \ b h n t z € † Œ ’ ˜ ž ¤ ª ° ¶ ¼ Â È Î Ô Ú à æ ì ò ø þ     " ( . 4 : @ F L R X ^ d j p v | ‚ ˆ Ž ” š   ¦ ¬ ² ¸ ¾ Ä Ê Ð Ö Ü â è î ô ú     $ * 0 6 < B H N T Z ` f l r x ~ „ Š – œ ¢ ¨ ® ´ º À Æ Ì Ò Ø Þ ä ê ð ö ü      & , 2 8 > D J P V \ b h n t z € † Œ ’ ˜ ž ¤ ª ° ¶ ¼ Â È Î Ô Ú à æ ì ò ø þ "(.4:@FLRX^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú $*06<BHNTZ`flrx~„Š–œ{U:tŒ«Á£!N„‹8©'šQ½Änâââÿœÿœôþ ýÚ¼þpÿœ„þpôÿj„ÿj„ÿj,ýv,ý¨,ývÂÿjÂÿjôþ ôþ Xý¨Xý¨Xý¨XýD[ÿj[ÿj×ý¨×ý¨,ÿœ ÿ8¼ýÚ ÿ8¼þ>ðý¨îþ>ôÿjôþ ¼þp¼ýÚ„ÿj¼ýÚ„ÿj„ÿj„ý¨„þ^„ÿj„ý¨„ÿj„ý¨,ý¨,ý¨,ý¨,ý¨,ý¨,ý¨,ý¨ôþ ôþ ôþ ôþ  ÿ8 ÿ8 ÿ8 ÿj ÿj¼þ>îþ>,ý¨ôþ ¼þp¼þpôÿjI«^`’k/©ÕÝ#€eÝ€YYY£cPþø`Þ{U:ti¾Ái£!N‹©šQÄnU!Q{„ý¨„ý¨+ýÚ+ýÚ„ý¨„ý¨+ýÚ+ýÚ„ý¨„ý¨+ýÚ+ýÚ„ÿj„ÿj+ÿœ+ÿœ„ÿj„ÿj+ÿœ+ÿœ„ÿj„ÿj+ÿœ+ÿœ ÿ8 ÿ8ôÿjÿj ÿ8 ÿ8ôÿjÿj,ý¨,ý¨Xþ Xþ ,ý¨,ý¨XÿXÿ,ý¨,ý¨Xþ Xþ ,ý¨,ý¨Xþ Xþ ôþ ôþ ôþ ôþ  ÿj ÿj,ÿj,ÿj ÿj ÿj,ÿj,ÿjîþ>îý¨ôþ ôþ +ÿœ+ÿœ¼þp¼þp+þ¢+þ¢,ÿœ,ÿœ,ÿœÅÿœ,þ ,ÿœ,ÿœ,ÿœ,ÿœ,þÔ,þÔ,ÿœ,ÿœ,ÿœ,ÿœô2ÿœÿœÿœÿœôþ ôþ ý¨ý¨¼þp¼þp+ÿœ+ÿœÿœÿœ„þp„þp+þ¢+þ¢ôÿjôÿj„ÿj„ÿj+ÿœ+ÿœ„ÿj„ÿj+ÿœ+ÿœ,ý¨,ý¨Xþ¢Xþ¢,ý¨,ý¨XÿœXÿœ,ý¨,ý¨XÿœXÿœÂÿjÂÿjÂÿjÂÿjôþ ôþ ôþ ôþ Xý¨Xý¨ŠÿjŠÿjXý¨Xý¨ŠÿjŠÿjXý¨Xý¨&ÿj&ÿjXý¨Xý¨&ÿj&ÿj[ÿj[ÿjSÿjSÿj[ÿj[ÿjSÿjSÿjŠý¨×ý¨XÿœîÿœŠý¨×ý¨Xÿœîÿœ ÿ8 ÿ8ôÿjÿj¼ýÚ¼ý¨ôÿjÿj ÿ8 ÿ8,ÿj,ÿj¼þ>¼þ>SÿœSÿœðý¨ðý¨&ÿj&ÿjîþ>îý¨+ÿœ+ÿœôÿjôÿjÂÿjôý¨ôþ ôþ ¼þp¼þp¼ýÚ¼ýÚ+þ¢+þ¢„ÿj+ÿœ+ÿœ ÿ8ôÿjÿj¼ý™ôÿjÿj+ÿœ+ÿœ„þ^+þÂ+þ„ÿj+ÿœ+ÿœ,ý¨XÿœXÿœ,ý¨XÿœXÿœ,ý¨XÿœXÿœ,ý¨Xþ Xþ ôþ ôþ ¼þ>SÿœSÿœ¼þp+þ¢+þ¢ôþ ôÿjIII«««^^^```’’’kkk///©©©ÕÕÕÝÝÝ###€€€eeeÝÝÝ€€€YYYYYYYYY (0SnoBqD¤ÁcîöøûŠþ^ŽsöïÙãsåö~™/ˆšõö÷øùúûüý VY^ÉBHNTZ`flrx~„Š–œÿÿ’F˜ 'PV\bhntz€†Œ’˜ž¤ª°¶¼ÂÈÎÔÚàæìòøþ "(.4UŒÑ0+0¬0™08q0µ080É0i0´0E0 0Ø10ýÍ¥0ä0­0Ë0P=Œi0v0¥0v0À0d0½0½0UŒUŒ8Ñ0É00UŒ    !%ëëïóõöøùüý "™0¸®ä $6HZl~ cr– cr– cr–ä cr–ä cr–Ü cr–Ü cr–Ü cr–Ü÷þ,TUWXZ[\]_ì'()*+,-.RX^djpv|‚ˆŽ”𠦬²¸¾Ä`°°°°°°°°°°`°°°°°°°°& ¶b µlrx~„Š–œ¢¨®´ºÀÆÌÒØÞäêðöü &,28>DJPV\bhntz€†Œ’˜ž¤ª°¶¼ÂÈÎÔÚàæìòøþ "(.4:@FLRX^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú $*06<BHNTZ`flrx~„Š–œ¢¨®´ºÀÆÌÒØÞäêðöü &,28>DJPV\bhntz€†Œ’˜ž¤ª°¶¼ÂÈÎÔÚàæìòøþ "(.4:@FLRX^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú     $ * 0 6 < B H N T Z ` f l r x ~ „ Š – œ ¢ ¨ ® ´ º À Æ Ì Ò Ø Þ ä ê ð ö ü      & , 2 8 > D J P V \ b h n t z € † Œ ’ ˜ ž ¤ ª ° ¶ ¼ Â È Î Ô Ú à æ ì ò ø þ     " ( . 4 : @ F L R X ^ d j p v | ‚ ˆ Ž ” š   ¦ ¬ ² ¸ ¾ Ä Ê Ð Ö Ü â è î ô ú     $ * 0 6 < B H N T Z ` f l r x ~ „ Š – œ ¢ ¨ ® ´ º À Æ Ì Ò Ø Þ ä ê ð ö ü      & , 2 8 > D J P V \ b h n t z € † Œ ’ ˜ ž ¤R``S`ž4Âârôr„èÂL„R„LŠèŠèŠâèâ¼î¼èX X X X [r[rÂ~Âx,°¼LôL¼ÜŠŠ¶ŠèÂRôîôîôî„èôL„â„L„è„R„è„è„L„芊܊èŠèŠÜŠèŠè¼x¼°¼î¼â¼L¼L¼Lôxôx¼4ŠîŠâôâôîôâÂRðIð«ð^ððð`ððàððnðð§ð©ðËðÝð#ðò€òðð”ðÝð€òYððððYðYðõ`R``S`„è„è+è+è„è„è+è+è„è„è+è+è„L„L++„L„L++„â„â+ª+ª¼L¼Lô@ª¼L¼Lô@ªŠèŠèXèXèŠèŠèXèXèŠèŠèXèXèŠèŠèXèXè¼â¼â¼x¼xôxôxúúôxôxú¤ú¤ŠîŠŠôâôâ+è+èôîXV+è+è,¤,¤,:Ű,°,Ü,Ü,:,:,°,°,,,:,:ÂLÐИ˜ÂâÂârrôX+F+Frr„è„è+è+èÂL&L„R„R++„L„L++ŠèŠèXèXèŠèŠèXèXèŠâŠâX~X~èèââ¼î¼î¼è¼èX X ЄЄX X RâRâX X & & X X &è&è[r[rSrSr[r[rSrSrÂ~ôèÂ~îèÂxô~Âxî~¼L¼LôF°ôLôRôF°¼Ü¼ÜúúŠŠSrSrжŠR&R&RŠèŠî++ÂR&RôLô ôîôîôîXVôîXî+è+è„è+è+è¼LôL¶ôRôL¶++„R++„è++ŠXªXªŠÜXxXxŠÜXxXxŠâX~X~¼î¼î¼4S4S4¼¶+°+°¼°&RðððIðIðIð«ð«ð«ð^ð^ð^ððððððð`ð`ð`ððððàðàðàððððnðnðnðððð§ð§ð§ð©ð©ð©ðËðËðËðÝðÝðÝð#ð#ð#ðòòò€ò€ò€òðððððð”ð”ð”ðÝðÝðÝð€ò€ò€òYðYðYððððððððððYðYðYðYðYðYð  0Sno'q)¤ÁHòòfÿÿghi^jsö°Ùã4åö?QZ/ˆ[,TUWXZ[\]_ÂÃÄÅÆÇÈÊì'()*+,-.rx~„Š–œ¢¨®´ºÀÆÌÒØÞäêðöü`°°°°°°°°°°{{{{{{{{`°°°°°°°°ö <BHNTZ`flrx~„Š–œ¢¨®´ºÀÆÌÒØÞä]xÜþxÿ@€[")ÿ@>Eö"~~€x2 x::Ñëõöø"> @FLRX^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîô]kxyyÜyšxy€z[f"w)h>yEy`Pö["~[~t`€zxy2{` ”ˆuxJJ:ˆ:ˆÑëÿÿ îñòóô÷ 28>DJPV\bhnttbbbbt`~~`~` T ¦þR©ÇÊÍÑÔ× Úä êíøúþÿ!"Š–œ¢¨®´ºÀÆÌÒØÞäêðöü &,28>DJPþÿÿþÿÿþþþÿþþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿêÎò 8rx~„Š–œ¢¨®´ºÀÆÌÒØÞäêðöü &,28>DJPV\bhntz€†Œ’˜ž¤ª°¶¼ðððððððððððððððððððððððððððððððððððððððððððððððððððððððð$=D]\]4_`6pr ððD6L  $J–s}ÃÕí-±² þ{þ{Þ~ä .^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú $*06<BHNTZ`flÿLž/'¢îѦ–s.}////Ñ–////s}/////.$&(,268DFHLRVXŽ‘®¯°±ÃÕêëìíîïòó-‘’ÊËÌÍ*ÙÚ o pùØþ , ~ ` Nž¤ª°¶¼ÂÈÎÔÚàæìòøþ "(.4:@FLRX^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú $*06<BHNTZ`flrx~„Š–œ¢¨®´ºÀÆÌÒØÞäêðöü &,28>DJPV\bhntz€†Œ’˜ž¤ª°¶¼ÂÈÎÔÚàæìòøþ "(.4:@FLRX^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú $*06<BHNTZ`flrx~„Š–œ¢¨®´ºÀÆÌÒØÞäêðöü &,28>DJPV\bhntz€†Œ’˜ž¤ª°¶¼ÂÈÎÔÚàæìòøþ     " ( . 4 : @ F L R X ^ d j p v | ‚ ˆ Ž ” š   ¦ ¬ ² ¸ ¾ Ä Ê Ð Ö Ü â è î ô ú     $ * 0 6 < B H N T Z ` f l‹‹‹‹‹‹‹‹‹‹¼¼Lìžž\/.þRž™sõ'ž'}“sî¼õÍr¾J·¤f–;¦þR¦þR¦é¦s¦þRˆþR%ô}^Gb`þR////ã}}J–Ð////¦‰þRs}·f7þR/'þz`þRÛþR///¦ô¼î¼ž”.þRþR'}r`þR‡TÖ¦TþRTcRRJ@@îþRjþRjþR¦bþRb}þR¦¦þR€™þþRîþRééþRéþR‰þR‰þR‰}é¦þR555þRþRaþRƒt;Q'þRþRþRñþRþRþRôþR}}^G^dþRþRþR°:°:þR'aH¦£ÿöþR«þR_ˆþR°:þþþRþ£GþRŽ þR~þRJ}'/'}ž¦'}^T¦TT@¦X‚}±½½½Tg^GþX^//LBRþRfÖÞ,4$þR'„˜_úzþRf4÷ÆŽLÆó}÷`þRüe·™™žTô¦ž¦¾'sþR¼^õG^£/÷î<\’‘þRô&ÕRäx9Ô´\þRŠÇG$= D]$Ž‘>¨¨B®²CººHÃÃIÕÕJéïKòóRúúT  UV--WEEXNNYTTZYY[aa\ll]vv^{{_‘’`ÊÍböøf iJqLm®??ÐBBÑEEÒ€€Ó’’ÔâãÕþÿ×Ù**Ú--ÛstÜÞà!)æ-1ï5:ôÙÚú o pü 3 3þ ; >ÿ @ B E E H M T T X X \ ] _ a c c e i l o S SNNPQ Sc"„„3\n4††GˆˆHùúIKL%%MÇÊÍÑÔ× Úä êíøúþÿ!"Š–œ¢¨®´ºÀÆÌÒØÞäêðöü &,28>DJPþÿÿþÿÿþþþÿþþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿŒF Íœ¢¨®´ºÀÆÌÒØÞäêðöü &,28>DJPV\bhnt|‚Š˜ž¤ª°¶¼ÂÈÎÖÜâèîôü &,28>DJRX^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú $*06<BHNTZ`flrx~„Š–œ¢¨°¶¼ÂÈÎÔÜâèîôú $*06<BHNTZ`flrx~„Š–œ¢¨®´ºÀÆÌÒØÞäêðöü &,28>DJPV\bhntz€†Œ’˜ž¤ª°¶¼ÂÈÎÖÜâèîôú     $ * 0 6 < B H N T Z ` f l r x ~ „ Š – œ ¢ ¨ ® ´ º À Æ Ì Ò Ø Þ ä ê ð ö ü      & , 2 8 > D J P V \ b h n t z € † Œ ’ ˜ ž ¤ ª ° ¶ ¼ Â È Î Ô Ú à æ ì ò ø þ     " ( . 4 : @ F L R X ^ d j p v | ‚ ˆ Ž ” š   ¦ ¬ ² ¸ ¾ Ä Ê Ð Ö Ü â è î ô ú     $ * 0 6 < B H N T Z ` f n t z € † Œ ’ ˜ ž ¤ ª ² ¸ ¾ Ä Ê Ð Ö Ü â è î ô ú     $ * 0 6 < B H N T Z ` f l r x ~ „ Š – œ ¢ ¨ ® ´ º À Æ Ì Ò Ø Þ ä ê ð ö ü &,28>DJPV\bhntz‹ð‹ð‹ð‹ð‹ð‹ð‹ð‹ð‹ð‹ð¼ð¼ðLðìðžðžð\ðð/ð.ðžð*ðsðõð'ðžð'ð}ð“ðsðîð¼ðõðÍðrð¾ðJ{#ð¤{þð–{;ðj{ð//ððé{‰{s{ ¦{o{{'{ð}{^{G{b{`{{'ðã{¦{}ð}{ðªðQð{¦{‰{{}{ðð\ðLðþðXðÄðžð;ð\ð¿ð/ð/ððð‚ðõð'ððÀð'{ððsððsð§ðrð`{¾ð{{'ðð‰{ð{¼ðîð¼ðžð{.ð/'ð}ð{rð`{‡{T{Ö{ðÂ{¦{þðþðc{R{R{J|@{@{î{{jðj{¦{b{b{}{ðð/{€{™ðþðððé{é{é{‰{‰{‰{}{{é{¦ð3{3ð3{^{a{p{‚{;{Q{'{ððñ{ð{ð}{}{^{G{^ðd{{{{{°ð:ð°ð:ð'ða{H{¦ð£{/«{{jð°ð:ðþðþðþð£{GðŽ{ ðððJ{~ð^{}ðJ|Eð‰{}ð{£{‚ð‡{{Eð}{p{¦{t{}{j{{ª{b{¤{^{~ð~ð ðî{}{t{^{ï{¦{¦{/'ð¦{ò{ð¦{Hð/ð¼ð™ðžðOðõðîð'ðoðYð‡ðNðsðàðT{a{E{ö{{›{@{‚{t{ø{{‚{}{{¦{¦{T{`{kðb{{K{©{©{{{{²{ö{t{¦{'{///ú{ó{4ð{óðÀð'ð}{ð•{žð¦{Oð›{îðt{îðt{îðððe{ðsðK{£{‰{{'ð}{E{Íðb{ø{^{{{T{¦{TƒT{{@{¦{{{‚{}{±{½{½{½{{{{{T{g{b{^{G{{þ{{^{ø{LðBðRðfðÆðÖðÞð,ð4â${ð'ð „ð˜ð_âúâzðfð4ðÊDÆðŽðLððÆðóçç}1÷{`{üðeðð*ðð*ðžð}ðT{ðððžðð¾ð{'ðsð¼ð^{õðG{^{ð£{ª{ˆðžð/ð÷ðîð<ðð\ð’ð‘ððôðð&ððÕðRðäðx÷9÷Ô÷´{\{õð{{êðð{{{Çðh$= D]$šš>¨©?²²AººBÀÀCèéDóóFúúG  HI55JBBKEELHIMNNOPPPRVQXYV[]X__[aa\ff]ij^lp`txe{{j¬¬köùl pJxLmµ>V×\]ðaaòccóeeôggõooö|‚÷„„þˆˆÿ’’œœŸŸ¡¢¤¤¦¦ªª¯¯ ¶· ¹¼ ¾ÄÆÛàç-ëë5îï6þ8<!">%-@77IDDJOOKUULYYMstN‚‚P‰ŠQžžST!*]-1g4:l>>s 3 3t ; >u @ By E E| H M} T Tƒ X X„ \ ]… _ a‡ c cŠ e i‹ l o S S”Nd•jj¬~~­„„®\n¯††ÂˆˆÃøúÄÇÈÉ"#Ê%%Ì ±Åîóõõ÷÷ûý "¥ª# * 0)ÈÒ0‰‰;<òú "*28>DLRX^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú "(.4:@FLRX^dþ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þŠoþrþrþ{þŠrþ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{ þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{þ{&Ø!0#5PÿÓÿ·Kr9KÿDÿˆÿ­ÿšÿ &ÿÉÿÜ&ÿÓKÿÓÿÜÿÜ9ÿÜÿÜÿÜÿÜÿaÿ}ÿÿaÿÜÿÜÿÜÿ·ÿÜÿÜÿˆÿ­ÿuÿ·ÿÜÿÜÿÜ9ÿÜÿÜÿÜÿÜÿÜÿÜÿÜÿÜÿÜÿÜÿaÿÿ­ÿaÿuÿÜÿÜÿaÿaÿuÿ­ÿþøÿ/ÿÜÿÜÿÜÿÜÿÁÿ·ÿÿÁÿÜÿÜÿÜÿÜÿÜÿÜÿ·ÿÿÜÿ·ÿÿÿ­ÿÜÿÜÿÜÿÜÿÜ&ÿÜÿÜÿÿÜÿÜÿÜÿÿÿÓÿÉÿDþ·ÿaÿDÿÜÿÜÿDÿÿkÿ·ÿkÿÿDÿDÿÿ·ÿÿDÿDÿÿkÿ·ÿÜÿÜÿÿDÿ·ÿÜÿ·ÿDÿÓþˆÿ·ÿšÿÜÿÜÿÓÿÓÿÉÿÜÿ·ÿÁÿ·ÿ·ÿÜÿÜÿÜÿ·ÿÁÿÿ)ÿÜÿÿÿaÿÉÿ·ÿ·ÿÜÿšÿšÿšÿkÿ}ÿÿÜÿšÿšÿšÿÁÿÁÿÜ/ÿ·þæÿšÿÿDþðÿÜÿÜÿÜÿDÿÜÿÜÿÜþaýæ9ÿ­ÿÜÿÜÿÜÿ}ÿÿÜÿÓÿÜÿDÿÓþÁÿ}ÿÓÿ¤ÿ·ÿÓÿÜÿ·ÿÜÿÜÿÜÿÜÿ¤ÿ·ÿ·ÿÜ&&þ·9ÿÓÿÜÿ}ÿ­ÿ·ÿÁÿ­ÿšÿkÿÿ­ÿ}ÿÓÿ¤ÿ¤ÿ¤ÿÿÿÜÿšÿÓÿ¤ÿ¤ÿ¤ÿkÿ}ÿÜ&ÿDÿ ÿÿaÿˆÿÜþ­þ¤þ¤ÿÁþ¤þÓþ­þÉþ­þÁÿDÿÿˆÿþàþôþçþ¤ÿÓþøÿÜÿˆþøÿYÿ}ÿÜÿaÿaÿÓÿaÿuÿÉÿNÿÿaÿaÿaÿuþæÿ­ÿÿˆÿÿ}ÿˆÿÓÿˆÿ¤ÿ·ÿÜÿÿÜÿ}ÿˆÿˆÿ·ÿÜþøÿšÿkÿ}ÿÜÿ¤ÿÿkÿ¤ÿaÿ­ÿÓÿ þaþðÿaÿÿþæþðÿ·þðÿÿÿkÿþæþðþðÿÿÿÜþøÿÜÿÜÿÜÿÜÿÜÿÿkÿ·ÿÜÿÜÿÜÿ·ÿÜAÿÿÜÿ·ÿ·ÿÁÿ·ÿÜÿ·ÿ·ÿÁÿkÿÿ¤&ÿÜÿÁÿkÿ·ÿ}ÿ}ÿDÿÜÿÓÿÜÿÓÿÜÿÜÿÓÿÜÿÉÿ·ÿÓÿÓÿÓHVþÉÿÉÿaÿÿÜÿÜÿÜþðÿDÿÿÜÿÜÿ)ÿÜÿÁÿÁÿÁÿÁÿÜÿÜþÜÿkÿÜÿÜþÓÿÜÿÜÿÜÿÜÿÜÿÿÿÜÿkÿÜÿÜÿÜ—ÿÜÿÜÿ·ÿ·ÿÜÿÜÿÜÿÜÿDÿNÿÿÿÿÜÿÜÿÜÿÜÿÓÿÜÿÜ9ÿÜÿÜÿÜÿÜÿaÿ}ÿÿaÿÜÿÜÿÜÿ·ÿÜÿÜÿˆÿ­ÿuÿ·ÿÜÿÜÿÜ9ÿÜÿÜÿÜÿÜÿÜÿÜÿÜÿÜÿaÿÿ­ÿaÿuÿÜþøÿ/ÿÓÿÜÿÜ9ÿÜÿÜÿÜÿÜÿaÿ}ÿÿaÿÜÿÜÿÜÿ·ÿÜÿÜÿˆÿ­ÿuÿ·ÿÜÿÜÿÜ9ÿÜÿÜÿÜÿÜÿÜÿÜÿÜÿÜÿaÿÿ­ÿaÿuÿÜÿþøÿ/ÿÓÿÜÿÜ9ÿÜÿÜÿÜÿÜÿaÿ}ÿÿaÿÜÿÜÿÜÿ·ÿÜÿÜÿˆÿ­ÿuÿ·ÿÜÿÜÿÜ9ÿÜÿÜÿÜÿÜÿÜÿÜÿÜÿÜÿaÿÿ­ÿaÿuÿþøÿ/ÿ­ÿ¤ÿÿÜÿÜÿÜ&ÿkÿ·ÿÜÿD&ÿÿÿ­ÿ¤ÿÿ·ÿÓÿÜÿÜ9ÿÜÿÜÿÜÿÜÿaÿ}ÿÿaÿÜÿÜÿÜÿ·ÿÜÿÜÿˆÿ­ÿuÿ·ÿÜÿÜ9ÿÜÿÜÿÜÿÜÿÜÿÜÿÜÿaÿÿ­ÿaÿuÿÜÿþøÿ/ÿÓÿÜÿÜ9ÿÜÿÜÿÜÿÜÿaÿ}ÿÿaÿÜÿÜÿÜÿ·ÿÜÿÜÿˆÿ­ÿ·ÿÜÿÜ9ÿÜÿÜÿÜÿÜÿÜÿÜÿaÿÿ­ÿaÿÜÿþøÿ/ÿÜÿÜÿÿÜÿÜÿÓÿÉÿDÿ?ÿ}ÿDÿÜÿÓÿÜÿÓÿÜÿÜÿÓÿÜÿÉÿ·ÿÓÿÓÿÓÿÜVþÉÿDÿ ÿÿaÿˆÿÜþ­þ¤þ¤ÿÁþ¤þÓþ­þÉþ­þÁÿDÿÿˆþ­þ¤þ¤þÉþ¤ÿÓþøÿÓÿ·Kr9KÿDÿˆÿ­ÿšÿ &ÿÉÿÜ&KþøÿÁÿ·ÿÁÿÁÿ·ÿÁÿ·ÿ·ÿˆÿÜÿ·ÿÿkÿÿ·ÿ·ÿ·þ}ÿ·ÿ·ÿkÿ·&ÿ·ÿÿ·ÿ·ÿ·/ÿÿþæþˆÿÿ·þˆÿÜÿ·ÿ·ÿ·ÿÿ<ÿ&ÿÿ·ÿ·ÿ·O$$%%&&''))**++-- .. // 22 33 445566778899::;;<<==HHIINNQQRRUUYYZZ[[ \\!mm"}}#‚‚$ƒ„%……&††%ˆˆ'‰‰(’’”˜ ›žŸŸ  )¡¡*ª­²²+³³´¸¿¿!ÁÁ!ÂÂ,ÄÄ,ÆÆ-ÈÈ(ÎÎ(ÐÐÒÒ.ÝÝààûû ÿÿ /  0  ""&&100::?? š š2 ¦ ¦3 ¨ ¨4¡$$%%&&''))** ++-- ./22 3344 5566 778899::;;<<==DDFFGGHHIIJKLLOOPPQQRRTTUUVV WW!XX"YY#ZZ$[[%\\&mm'}}(‚†ˆˆ)‰‰*”˜ ›žŸŸ ¡¢¢+££¤§+©©ªª,««¬­,²²-³³´´.µµ¶¸.»»/¼¼"½¾/¿¿&ÁÁ&ÂÂ0ÃÃ1ÄÄ0ÅÅ1ÆÆ0ÇÇ1ÈÈÉÉ2ÊÊ3ËË4ÌÌ3ÍÍ5ÎÎÏÏ2ÐÐÑÑ6ÓÓ7ÕÕ8××8ÙÙ8ÛÛ8ÝÝÞÞ3àà9ááââ3ää:ññ;õõ;ûûÿÿ  3<3<=<;    !! "" ## $$>%%5&&''!((?++@--@//@0011"33@55@66A77B88C99D::??ˆˆ4¬¬E­­F®®E¯¯FÚÚ ÜÜGÝÝ4ðð3ññHóó4ôôIõõJ + +A , ,K - -L . .B / /A 0 0B ™ ™C š šD ¦ ¦M § §N ¨ ¨O^$%&')*+-./23456789:;<=HINQRUYZ[\m}‚ƒ„…†ˆ‰’”•–—˜›œžŸ ¡ª«¬­²³´µ¶·¸¿ÁÂÄÆÈÎÐÒÝàûÿ  "&0:? š ¦ ¨ T¦ DFLT>arabJarmn„cyrl’grek´hebrÄlao Ölatnànko .tfng@ÿÿKUR SND (URD (ÿÿ ÿÿ ÿÿMKD SRB ÿÿÿÿÿÿÿÿ ÿÿ(ISM :KSM :LSM :NSM :SKS :SSM :ÿÿ ÿÿ ÿÿ ÿÿaalt¤aaltªaalt°ccmp¸ccmp¾ccmpÄdligÌdligÒdligØfinaÞfinaähligêhligðinitöinitüligaliga ligaloclloclmedi"medi(rlig.rlig8salt@saltFsaltL    !DLT\dlt|„Œ”œ¤¬´¼ÄÌÔÜäìôü $,4<DTÂdh l ° R – : ~ " V  Ü  ú 8 ‚ : J Z    ° D H L j ˆ ’ ¨ Â Æ ø Bõý  ŒŒŒ>9LMñ *_±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅîïðñòóôõ÷ûüýâäØ r * + , - x yi$=EEGGIIKKLMNOWW‚˜š¡ÂÂÄÄÆÆÈÈÊÊÌÌÎÎÐÐÒÒÔÔÖÖØØÚÚÜÜÞÞààââääæêììîîððññòòôôööøøû      ""$$&&((**,,..0022446688:;==??AAHHRRTTVV‘“••¤¤¬¬®®ÚÚÜÜððôô  **__±ÅÇÊÍäêíîõöö÷÷øúûýþÿââä䨨 r r * - x y&   &v6Pblv€’œ¦¸ÂÔÞèòü",6PZdþýýüü ÿ þ ëù       ùøíü ûú   !"#$%(!½ì B/258;>ADGJMPSVY\_behknqtwz}€ƒ†¤ÁŽD„†ˆŠŒ’–˜œ ¤¨¬®°²´¸¼ÀÄÈÌÐÔØÜàäèìðòôÙß.&åè*"ëî>:ñBFLJÜ26NRVôX\0HJSno#q%B0369<?BEHKNQTWZ]`cfilorux{~„‡¤Ál3Ž”šž¢¦ª¶º¾ÂÆÊÎÒÖÚÞâæêîZöÛá0(çê ,$íð@<óDHÞ48PTãö^ 44668<AHJPRSnoq„‹)Ž1B147:=@CFILORUX[^adgjmpsvy|‚…ˆ¤Ál3“™¡¥©µ¹½ÁÅÉÍÑÕÙÝáåéíYõÚà/'æé+#ìï?;òCGÝ37OSâõ] 44668<AHJPRSnoq„‹)Ž12  )Z,Z ,Y)VVYZ~&8Jl'Z(Z -]*Z .]+Z +X*W(U'T .X-WTUWXZ] ýþáâ> $ûŠù†÷„üŠú†ø„áâJ 8 "(ãIOâILáOàLßI ãOâLIß²Rlž¨$*06<€[~Z|YzXxWwVuUsT3^1]0\ &,[Z}Y{XyWtT2]4]5IQSòíÐ 6 , êØèÖçÐæáéáßéåWVäWAˆ(:FPZfr Ñ" Ä Ð Ï" k$; V P0 Q(/ 1F 7X 6R")567DF è è_^`\]ba28?EMS_^`\]ba28?EMS øD %ø,-DO  %,-O Ñ ËÉÌÊÏÐȉÑÍÒÎ ±²³´·¸¹»¼½À óù!ùúùóù"# ! LMñ *_âäØ r x y3™3™×f ç.ÿÒýÿ `)PfEd@ ÿÿþšmãàÿÿÿ,ü€, üа°~éîó÷OSXbu~ŠŒ¡Î†V_‡ŠÃÇêô :UZot‡’•˜¡¤¦©¯µº¿ÆÌÎÕùçõú?‚„ˆŠ—Ÿ£¥§«¹½ÄÆÍÙÝÅü5JR½ê>Pj…–¯ÞáGvœ#.[jx{…¿É›ùEMWY[]}´ÄÓÛïôþ ' c q Ž ” µ Ñ × Ü á! !I!K!N!„"¯"Æ"Í###!#(#,#u#z#}#‡#”#®#Ï#ã#å$#$i&œ&²'' '''K'M'R'V'^'”'¯'¾'à'ë(ÿ)) )A)„)Õ)ë)û***/* *º*ú++#,o,w-e-oMÿðöÅûûû6û<û>ûAûDûOûû•ûŸûÚûéûÿþþ#þtþüþÿÿýÿÿ  ìó÷QW\tz„ŒŽ£Ðˆ1Ya‰°ÆÐð !@Z`ty‘•˜¡¤¦©¯µº¿ÆÌÎÕðÀëø?„‡Š”™¡¥§ª­»ÀÆÈÐÜ Ð 7LTÀì@RtŠ ÞáFn€&0]w{…›Ä  HPY[]_€¶ÆÖÝòö * j t   Ð Ö Û á!! !K!N!S!"²"È"Ö###$#+#s#z#}#‡#”#›#Î#ã#å$"$`%& ''' ')'M'O'V'X'a'˜'±'à'æ'ð)) )@)ƒ)Î)ë)ú** */*}*®*ù++ ,`,q-0-oMÀðöÅûûûû8û>û@ûCûFûRûŠûžûÙûèûüþþ þpþvþÿÿùÿÿÿãÿÂÿÀÿ¼ÿ¹ÿ±ÿ°ÿ­ÿªÿ™ÿ•ÿÿÿŽÿÿŒÿ‹ÿnÿlÿkÿjÿEÿCÿ;ÿ6ÿÿÿÿÿÿ ÿÿþüþøþïþíþëþãþáþàþÞþÙþÔþÐþÌþÆþÁþÀþºþ ýÚý×ýÕ÷‘÷P÷O÷M÷L÷J÷D÷C÷B÷A÷@÷>÷=÷<÷:÷9÷8÷6÷4õrõhòdòcòbòaò`ò_ò]ò\òTòSòRòIòEò<òò ñ¨ñ‚ñyëëëëëëëêúêåêáê«ê§ê¡êŸêê›ê™ê˜ê—ê–ê•ê“ê’ê‘êêŽêŒê‹êŠêˆê‚ê€êêtêZêVêSêOê1ê0ê/ê-ê)êêêêê ê ê êéÁé½é»é²é¦é éénémé1èõè_è\èè è è è èèèèçÿçþçÝçØçÔçÎçÌç˜çWçæùæëæçæÞæÌææræ4æ/æ*äîäíä5ä,ÃÜ!ÜßÓÎÍÌËÊÉÇ¿·~q__O „ß ~ ébìî¬óó¯÷÷°O±QSWX\btu z~„ŠŒŒŽ¡£Î0І\ˆ1VŸY_Åa‡Ì‰Šó°ÃõÆÇ Ðê ðô&  +,-.!:/@UIZZ_`o`ttpy‡q‘’€••‚˜˜ƒ¡¡„¤¤…¦¦†©©‡¯¯ˆµµ‰ººŠ¿¿‹ÆÆŒÌÌÎÎŽÕÕðùÀçšëõÂøúÍ??Ђф„Ó‡ˆÔŠŠÖ×”—Ø™ŸÜ¡£ã¥¥æ§§çª«è­¹ê»½÷ÀÄúÆÆÿÈÍÐÙÜÝ ÅÐü8e l57J˜LR¬T½³ÀêìH>d@P“Rj¤t…½Š–Ï ¯ÜÞÞìááíFGînvð€œù#+&.90[B]jnwx|{{~……›¿€ÄÉ¥›« ù G ¡ · E ½HM ãPW éYY ñ[[ ò]] ó_} ô€´ ¶Ä HÆÓ WÖÛ eÝï kòô ~öþ ' Š * c ² j q ì t Ž ô ”    µ  Ð Ñ * Ö × , Û Ü . á á 0!! 1! !I ;!K!K z!N!N {!S!„ |!"¯ ®"²"Æ Î"È"Í ã"Ö# é## %##! '#$#( -#+#, 2#s#u 4#z#z 7#}#} 8#‡#‡ 9#”#” :#›#® ;#Î#Ï O#ã#ã Q#å#å R$"$# S$`$i U%&œ _& &²ü'''' ' ''')'K3'M'MV'O'RW'V'V['X'^\'a'”c'˜'¯—'±'¾¯'à'à½'æ'ë¾'ð(ÿÄ))Ô) ) Ö)@)AØ)ƒ)„Ú)Î)ÕÜ)ë)ëä)ú)ûå**ç* *ê*/*/û*}* ü*®*º *ù*ú-++/+ +#J,`,oN,q,w^-0-ee-o-o›MÀMÿœððÜöÅöÅÞûûßûûæûû6ëû8û<û>û> û@ûA ûCûD ûFûOûRûûŠû•IûžûŸUûÙûÚWûèûéYûüûÿ[þþ_þ þ#oþpþtsþvþüxþÿþÿÿÿùÿýÓÓVÕ8Õ9\Õ;Õ>^Õ@ÕDbÕFÕFgÕJÕPhÕRÕkoÕ ÕÓ‰×â×ë½   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a†‡‰‹“˜ž£¢¤¦¥§©«ª¬­¯®°±³µ´¶¸·¼»½¾ ªrdei ¬x¡pk Rvj ~ˆš <s ‚ ƒgw / -M Il|6¨ºcn 8T f $m} °b‚…— ž ¦ § ¢ £¹)Á: Ì Á Âàá «y ¤ ¨ ¸„ŒƒŠ‘Ž•–”œ›óˆžqš›œzŸ‰5¸ËËÁªœ¦¸fqË ²…u¸Ãˉ-˦ðÓª‡ËªJ3ËÙôT´œ99N´R¸çÍ7sÍ`s3¢V¦V9Åɸßsºé3¼Dßͪåªˤ{¸o{RÇÍššoËÍžÓðºƒÕ˜HžÕÁËöƒT3fÓǤ͚sÕ þ+¤´œbœ-ÕÕÕð{T¤¸#Ӹ˦Ãì“ Ó\qÛ…#¨H99`Õš#fy```{œw`ªé`b{Å{´RÍf¼fwÍ;…‰{ÍJ/œœ}oo5jo{®²-–{öƒT7öœáföÍD)fîs–·, °%Id°@QX ÈY!-,°%Id°@QX ÈY!-,  °P° y ¸ÿÿPXY°°%°%#á °P° y ¸ÿÿPXY°°%á-,KPX °ýEDY!-,°%E`D-,KSX°%°%EDY!!-,ED-,°%°%I°%°%I`° ch ŠŠ#:Še:-ÿÿfþ–f¤@ ûû/ÄÔì1ÔìÔì0!%!!füsüåþ–øòr)5Õ 5@ƒ üK° TX¹ÿÀ8Y<ì2991/äüÌ0¶ P ]%3#3#5ËËË¢þþÕýqþ›eŪéÕB@„üK°TK°T[X¹ÿÀ8YüÜì1ô<ì20@0 @ P ` p   ¿ ]#!#oª$ªÕýÕ+ýÕ+ž¾`@1 ‡  ‡   üÌ91/<Ô<<ü<<Ô<<Ä2ì220@   ]!! !3!!!!#!#!5!!5!þÝT%Dh$i g8þ¡R>þ›h gþÛg¡hþÅ`Tþ¾if…þ²‡þaŸþašþ²™þbžþbž™NšŸªþÓm!(/½@U" '&( /)/))/B" ) *!††#Љ*Љ- ) " & 0üK° TX¹ÿÀ8YK° TK°T[K°T[X¹@8Y<ìô<ü<ôäì1/äìÄÔäì2Äîî99990KSXíí9í9íY"#.'5.546753.'>54&´diÒjfÑoÝÉÚÌd]®SS¯\ãÖãÖdtzqá{þÓ---´@AÈ$¬–£¼ëè¯*.þU#´œ©Ãš jXV`ÕþOnZXhqÿã)ð #'3‰@6$%&%&'$'B’ ’.’$’ &Œ($‘4'!%   ! + 1 4üK° TK° T[K° T[K°T[K°T[K° T[X¹1ÿÀ8YÄìôìîöî991ä2ô<äìîöîî0KSXííY""32654&'2#"&546"32654&%3#2#"&546ÑWccWUccUžº» º»ü—VcbWWcd1 üZ ž¼»ŸŸ¹º‘”„‚••‚ƒ•Ü»»ÛÛ»¼Ûa•‚„””„–ùó Û»½ÚÛ¼ºÜÿãþð 0Í@–  † †  † †††  !         B  (('•+•'”$‘Œ .  .'.'!!1üìÄÔÔìÆî99999991/ÆäöæîîÆ9990KSXíí9í9í9í9í9íí9í9ííí9Y"²2]@² " ) **&:4D ^YZ UZZY0g{›š™—• “••"™-  ' (   2'') #**(/2; 49?2J LKFO2VZ Y UY\_2j i`2uy z““—•œœŸš › š 2 2°29]]3267 >73#'#"5467.54632.#"ò[UÔ _¦Iþ{ü;Bº h]ühäƒñþΆ†02Þ¸S¥UWžDiƒ;#Q¡X’Â?@ýøYËr„þþ~þã“YW×€ác?}<¢Å$$¶/1oX3gŪoÕ7@ „üK°TK°T[X¹ÿÀ8Yì1ôì0@ @P`p ]#oªÕýÕ+°þò{ 7@˜—  ÜK°TX¹ ÿÀ8YK°TX¹ @8Yä2ì991üì0#&547{†‚ƒ… –•”—æþ>ççþ;åëÆàßÄì¤þòo @˜— Ü<ôì991üì03#654¤ –••– …ƒƒìþ<ßàþ:ëåÅççÂ=JÃðN@,  ™ ™ ‘    Ô<ä2Ü<ä2991ôÔ<ì2Äì2990 %#'-73%Ãþ™g:þ°rþ°:gþ™:PrPßÂÃbËþ‡yËbÃÂcËyþ‡ËÙÛ #@ œ  Üü<ü<ì1/Ô<ü<Ä0!!#!5!®-ýÓ¨ýÓ-ýÓªýÓ-ª-žÿÃþ@ žƒüìÔÌ1üì073#ðÓ¤Rþ¬þÀ@d߃¶œÜÌ1Ôì0!!dý僤ۮþ·ƒüì1/ì073#ÛÓÓþþÿB²Õ-@BŸ/Ä991ôì0KSXííY"3#ªýøªÕùm‡ÿãð #@   ‘Œ üìôì1äôìî0"32'2#"‹œœû þ÷ûûþ÷ PþÍþÌþÍþÍ3343 þsþ†þ‡þsyzáZÕ @@B     ÔK°TX¹@8YìÄüì1/ì2ôìÔì0KSXY"´]7!5%3!!þJþ™eÊJü¤ªsH¸HúÕª–Jðš@'B¡”  ‘   üK°TK°T[K°T[X¹ÿÀ8YÄÔìÀÀ91/ì2ôìôì0KSXíí9Y"@2UVVzzv‡tvust‚†‚‚‚¨¨]]%!!567>54&#"5>32‰ÁüLs3aM§†_ÓxzÔXèE[þôªªªw‘:m—Iw–BCÌ12èÂ\¥pþëœÿãsð(p@. † †     “  “#‘Œ£)&  )üK°TK°T[X¹ ÿÀ8YÄÄÔìôì991ìäôäìæîîîî90@ daa d!]!"&'532654&+532654&#"5>32?‘£þÐþè^ÇjTÈm¾Ç¹¥®¶•ž£˜S¾rsÉYæ Ž%ÄÝò%%Ã12–„•¦wps{$&´ Ѳ|«d¤Õ @   B     ÜK° TK° T[X¹ ÿÀ8YÔ<Äì291/äÔ<ì290KSXÉÉY"@* *HYiwŠ+&+6NO O Vfuz… ]] !33##!5þþ5þÕÕÉý^%üãÍü3¨þ `ÞÿãdÕ^@#†  ‰   Œ¤  üK°TK°T[X¹ÿÀ8YK°TX¹@8YÄÔìÄî1ääôìæîþÄî90!!>32!"&'532654&#"Ýý ,X,ú$þÔþï^ÃhZÀk­ÊÊ­Q¡TÕªþ’þîêñþõ Ë10¶œœ¶$&ÿã–ð $X@$ †   ¥  ‰"‘Œ% " !%üììôìä1äôäüäîîî90@ËËÍÍÍËˤ²]]"32654&.#">32# !2¤ˆŸŸˆˆŸŸ L›LÈÓ;²káþðâþýþîPL›;º¢¡»»¡¢ºy¸$&þòþïW]þïëæþêyb¥¨hÕc@B üÌÄ991/ôì0KSXííY"K°TX½@ÿÀ878Y@X9Hg°°]]!#!¨ÀýâÓþý3ÕVú+‹ÿã‹ð #/C@%  ' - ‘Œ'£0 $*$ !0üÄìôÄìîî991ìäôìîî990"32654&%.54$32#"$54632654&#"‹¥¥¦¥þ¥‚‘ÿÞßþ‘’£þ÷÷÷þ÷¤H‘ƒ‚““‚ƒ‘Åš‡‡š›†‡šV ²€³Ðг€² "ÆÙèèÙÆat‚‚tt‚‚ÿã‡ð$X@#†  ¥ ‰ ‘Œ%!"" %üìäôìì1äôìæþõîî90@ÄÂÀÀÀÂμé]]7532#"543 !"&2654&#"áLœKÈÓ:²làþûâþ±þåLœ>ˆŸŸˆˆŸŸ¸$& V\ëæþsþ†þŸþ[—º¢¡»»¡¢ºðÃ#@ƒ¦ƒü<ì21/ìôì073#3#ðÓÓÓÓþþ#þžÿÃ# %@ƒžƒ¦  ü<ì2ÔÌ1äüìî03#3#ðÓÓÓ¤R#þýÙ¬þÀ@Ù^Û¦M@*œœœœB¨§$#üì291ôì90KSXííííY" 5Ûûøúþðþ‘þ“¶ѦÑÙ`Û¢@ œœ#ü<Ä21ÔìÔì0!!!!Ùúþúþ¢¨ðªÙ^Û¦O@+œœœœB¨§$#ü<ì91ôì90KSXííííY"55Ùúþð¶þ/¦þ/¶m“°ð$e@+$  †ˆ•‘ƒ   &%ÜK° TX¹ÿÀ8YÄüìÔìî99991/îöþôîÍ9990¶y z z ]%3##546?>54&#"5>32‡ËËÅ¿8ZZ93ƒlO³a^Ág¸ßHZX/'þþ‘še‚VY5^1YnFC¼98ŸL‰VV/5<4‡þœq¢ L•@2  ©©L43¬0©7¬$©7CM34( (+(I+*(I,=MÜìüìþýþ<Æî991ÔÄüìþíÔÆÅî2Äî990K° TK° T[K°T[K°T[K°T[X½MÿÀMM@878Y@ NN/N?N]32654&#"#"&5463253>54&'&$#"3267#"$'&5476$32úŽ|{zy!<›g¬×Ø«gœ;’¥?@hþÕ°{â`±smiùhZ}þÙ˜¹þ¸€€†ˆ~R½Ôk{KOþÂþè£¤ŽŒ¥¤þHMIùÈÈúKLƒý ß±k¼Pƒ‹A@fþµÁŸþêjhmWQoagƒ}}I½¶J}‡® bæ{þùþÐhÕ Â@A       B•    ÔÄ91/<äÔì90KSXííííííííY"² ]@BXvpŒ  VXP ghxv|rwx‡ˆ€ ˜™–]] !3#!#¼þî%þ{å9Òˆý_ˆÕý®ú+þÉìÕ C@#• •• ­ . !üì2üìÔì9991/ììôìî90²"]!2654&#!2654&#%!2#!“D££þ¼+”‘‘”þ çú€|•¥þðûýèÉý݇‹Œ…fþ>orqp¦À±‰¢ ˘ÈÚsÿã'ð6@ ¡® •¡®•‘Œ 0üì2ì1äôìôìîöî0´].# !267# !2'fç‚ÿþð‚çfjí„þ­þz†S†íbÕ_^þÇþØþÙþÇ^_ÓHHŸghŸGɰÕ.@• •  2 üìôì99991/ìôì0²`]3 !%! )“ô5þáþËþBŸ²–þhþPþa/ûw.,¦þ—þ€þ~þ–É‹Õ .@•••­   üì2ÔÄÄ1/ììôìî0² ]!!!!!!ɰýÇý9øü>ÕªþFªýãªÉ#Õ )@••­ üì2ÔÄ1/ìôìî0² ]!!!!#ÉZýpPý°ÊÕªþHªý7sÿã‹ð9@ ••¡®•‘Œ43 üìüäüÄ1äôìôìþÔî990%!5!# !2.# !26Ãþ¶uþæ þ¢þu‹^’opü‹þîþík¨Õ‘¦ýSU™mn™HF×_`þÎþÑþÒþÎ%É;Õ ,@•­ 8  üì2üì21/<ä2üì0²P ]3!3#!#ÉÊÞÊÊý"ÊÕýœdú+Çý9É“Õ.·¯üK°TX¹@8Yì1/ì0@ 0@P`Ÿ]3#ÉÊÊÕú+ÿ–þf“Õ B@ •° 9 üK°TX¹@8Yìä991äüì990@ 0 @ P ` Ÿ ]3+53265ÉÊÍãM?†nÕú“þòôª–ÂÉjÕ ï@(B¯  üì2ÔÄ91/<ì290KSXííííY"²]@’ ((764GFCUgvwƒˆ”›ç    (+*66650 A@E@@@ b`hgwp ‹‹Ž š¶µÅÅ×Öèéèê÷øù,]q]q3! !#ÉÊžýþöý3ÊÕý‰wýHüãÏý1ÉjÕ%@ •:üìì1/äì0@ 0P€€]3!!ÉÊ×ü_ÕúÕªÉÕ ¿@4  B ¯   >  üìüì91/<Äì290KSXííííY"²p]@V   && & 45 i|{y €‚‚  #,'( 4<VY ej vy •›]]! !###É-}-ÅþËþÄÕüøú+üúáÉ3Õ y@B¯6 üìüì991/<ì2990KSXííY"² ]@068HGif€ FIWXeiy…Š•šŸ ]]!3!#É–ÄþðýjÄÕûáú+áûsÿãÙð #@•• ‘Œ 3üìüì1äôìî0"32' ! 'ÜþýÜÜþÿÜ:xþˆþÆþÅþ‡yLþ¸þåþæþ¸HH¤þ[þžþŸþ[¤bb¥ÉÕ:@••   ? üì2üì91/ôìÔì0@ ?_¯]32654&#%!2+#“þššþ8ÈûþÿûþÊ/ýÏ’‡†’¦ãÛÝâý¨sþøÙð R@*  B ••‘Œ    3üìüì9991Ääôìî990KSXíí9Y""32#'# ! 'ÜþýÜÜþÿ? ôÝ!#þÅþ‡y;:xÑLþ¸þåþæþ¸HHúÏþÝï¥ab¥þ[þžþüþŽÉTÕ±@5  B• •   ?  üì2üÄì99991/<ôìÔì9990KSXíí9Y"²@]@Bz%%%&'&&& 66FFhuuwˆˆ˜˜]]#.+#! 32654&#A{>ÍÙ¿J‹xÜÊÈüƒý‰þ’••’¼~þh–bý‰ÕÖØºOýƒ…‡ÿã¢ð'~@<    B ¡”••”%‘Œ( "-"(ÜÄìüìä99991äôäìîöîÆ90KSXí9í9Y"²)]¶)/)O)].#"!"&'532654&/.54$32HsÌ_¥³w¦zâ×þÝþçjï€{ìr­¼‡š{âÊõiÚ¤Å76€vce+Ù¶Ùà0/ÐEFˆ~n|-À«Æä&ÿúéÕJ@•@@Ôäüä1/ôì20K° TX½@ÿÀ878Y@  @ p Ÿ ]!!#!ïýîËýîÕªúÕ+²ÿã)Õ@@ •Œ  8AüK°TX¹ÿÀ8Yìüì1ä2ôì99990¶Ÿ]332653! ²Ë®Ã®ËþßþæþåþßÕüuðÓÓð‹ü\þÜþÖ*$hÕ·@'B¯ÔÄ91/ì290KSXííííY"²P]@b*GGZ}ƒ *&&))% 833<<7HEEIIGYVfiizvvyyu€˜—)]]!3 3JýÆÓÙÚÒýÇÕûéú+D¦Õ {@I      B ¯    ÔÌ91/<ì2290KSXííííííííY"²]@ò  ($ >>4 0 LMB @ Yjkg ` {|€ –•     !   # $ %  <:5306 9 ? 0FFJ@E@BBB@@ D M @@XVY Pfgab```d d d wv{xwtyywpx  †‡ˆ‰… Š —Ÿ¯[]]3 3 3# #DÌ:9ã:9Íþ‰þþÅþÂþÕûîûîú+úð=;Õ f@  ÔÄÜÄÄ1´€ ]@¯ /<ì20K°BPX@   ìììì@    Y3 3 # #ÙsuÙþ Ùþ\þYÚÕýÕ+ý3üø{ý…ÿüçÕ”@(B¯@@ Ôäüä91/ì290KSXííííY"² ]@<5000F@@@QQQe„“ &)78@ ghxp Ÿ ]]3 3#Ùž›ÙýðËÕýšfüòý9Ç\Õ @B••B ÜK° TK° T[X¹ÿÀ8YÄÔä991/ìôì0KSXííY"@@ )&8HGH    / 59? GJO UYfio wx Ÿ ]]!!!5!s•üPÇû=°ügÕšûoªš‘°þòX;@©²©±CÜK° TX¹@8YK°TK°T[X¹ÿÀ8YüÌ21üìôì0!#3!°¨ððþXùüÿB²Õ-@BŸ/Ä991ôì0KSXííY"#ªªýøÕùm“Çþòo0@©²©±CüK°TK°T[X¹@8Y<Üì1üìôì0!53#5oþXïïøÞÙ¨ÛÕ@ ÜÌ91ôÌ290 # #¼ÉþHþHÉÕýÓ‹þu-ÿìþþ¬µ©ÄÄ1Ôì0!5ûØþ¬ªð‰f1@ ´³DÜì1ôì0K° TK°T[X½ÿÀ@878Y #o™þºfþŠv{ÿã-{ %¼@'  ©¹ †º¹#¸Œ   E&üìÌÔì22991/ÄäôüôìÆîî9990@n0000 0!0"?'@@@@ @!@"PPPP P!P"P'p'…‡‡‡ ‡!…"' 'ð'000 0!@@@ @!PPP P!``` `!ppp p!€€€ €!]]"326=7#5#"&5463!54&#"5>32¾ß¬o™¹¸¸?¼ˆ¬Ëýû§—`¶Te¾Zóð3f{bsÙ´)LýªfaÁ¢½À‹..ª''üºÿ㤠8@¹  ¹Œ¸—G Füì22ôì1/ìäôÄìÆî0¶`€ ]4&#"326>32#"&'#3å§’’§§’’§ýŽ:±{ÌÿÿÌ{±:¹¹/ËççËËççRdaþ¼þøþøþ¼ad¨qÿãç{?@†ˆ† ˆ ¹¹¸Œ HEüä2ì1äôìþôîõî0@ € ].#"3267#"!2çNP³ÆÆ³PNM¥]ýþÖ-U¢5¬++ãÍÍã++ª$$>:#qÿãZ8@¹¹Œ¸—G Eüìôì221/ìäôÄìÄî0¶`€ ]3#5#"3232654&#"¢¸¸:±|ËÿÿË|±ýǧ’’¨¨’’§¶^ùì¨daDDaþËççËËççqÿã{p@$ †ˆ©¹ »¹¸ ŒKEüìôìÄ91äôìäîîôî90@)?p Ðð?????,// , ooooo ]q]!3267# 32.#"ü² Í·jÇbcÐkþôþÇ)ü⸥ˆš¹^Z¾Ç44®*,8 CþÝÄ—´®ž/øY@ ©‡—¼    LüK° TX¹ @8YK°TX¹ ÿÀ8Y<Äü<ÄÄ991/ä2üìî2990¶@P ]#"!!##535463ø°cM/þѹ°°®½™Phcü/ÑN»«qþVZ{ (J@#  †¹¹&#¸'¼ ¹½& G E)üÄìôì221/ÄäìäôÄìþÕî990¶`*€* *]4&#"326!"&'5326=#"3253¢¥•”¥¥”•¥¸þþúa¬QQžRµ´9²|ÎüüÎ|²9¸=ÈÜÜÈÇÜÜëþâþé³,*½¿[cb::bcªºd4@ ‡¸ — N  Füì2ôì1/<ìôÄì90²`]#4&#"#3>32d¸||•¬¹¹B³uÁƤý\žŸž¾¤ý‡ýžedïÁy+@¾±¼Fü<ì21/äüì0@  @ P ` p ]3#3#Á¸¸¸¸`û éÿÛþVy D@ ¾ ‡½¼ ±O  Fü<ì2ä991ìäôìî990@ @P`p]3+532653#Á¸£µF1iL¸¸`ûŒÖÀœa™(麜 ¼@)B¼— F üì2ÔÄ91/<ìä90KSXííííY"² ]@_ ')+Vfgsw‚‰Ž“–—£    ('(++@ h` ‰…‰š—ª§¶ÅÖ÷ð÷ð]q]33 ##º¹%ëý®kðýǹüiãýôý¬#ýÝÁy"·—Füì1/ì0@ @P`pð]3#Á¸¸ùìº{"Z@&  ‡ ¸¼PPF#üì2üüüì91/<<äô<Äì290@0$P$p$$ $ $¿$ß$ÿ$ ]>32#4&#"#4&#"#3>32)EÀ‚¯¾¹ru¦¹rw¦¹¹?°yz«‰|võâý\ž¡œ¾¤ý‡ž¢›¿£ý‡`®gb|ºd{6@ ‡¸ ¼ N  Füì2ôì1/<äôÄì90´`Ï]#4&#"#3>32d¸||•¬¹¹B³uÁƤý\žŸž¾¤ý‡`®edïqÿãu{ J@¹¹ ¸Œ QEüìôì1äôìî0@#?{{   {  { ð]"32654&'2#"s”¬«•“¬¬“ðþîðñþïßçÉÉçèÈÇéœþÈþìþíþÇ98ºþV¤{>@¹¹¸Œ½¼ GFüì22ôì1äääôÄìÄî0@ `€ à]%#3>32#"&4&#"326s¹¹:±{ÌÿÿÌ{±8§’’§§’’§¨ý® ªdaþ¼þøþøþ¼aëËççËËççqþVZ{ >@¹  ¹¸Œ½¼ GEüìôì221äääôÄìÆî0@ `€ à]32654&#"#"3253#/§’’¨¨’’§s:±|ËÿÿË|±:¸¸/ËççËËççý®daDDadªùöºJ{0@  ‡¸ ¼ FüÄì21/äôìÄÔÌ90´PŸ].#"#3>32JI,œ§¹¹:º….´˾ý²`®fcoÿãÇ{'ç@<  S  SB †‰†‰¹¹%¸Œ( R"E(üÄìÔìä99991äôìþõîõî90KSXí9í9Y"²']@m   . , , , ; ; ; ; $( ( *//*(() )!$'† † † †      '/)?)_))€)) )ð)]]q.#"#"&'532654&/.54632‹N¨Z‰‰b”?Ä¥÷ØZÃlfÆa‚Œe«@«˜àÎf´?®((TT@I!*™‰œ¶##¾55YQKP%$•‚ž¬7òž8@©¼‡  Fü<Äü<Ä2991/ìô<Äì2990²¯]!!;#"&5#53w{þ…Ks½½Õ¢‡‡žþÂý ‰NšŸÒ`>®ÿãX`;@ ‡Œ ¼¸  NFüìô9ì21/ää2ôÄì90´oÀ]332653#5#"&®¸||•­¸¸C±uÁÈϺ¦ýaŸŸ¾¤{û ¬fcð¨=`û@'B¿ÔK° TX¹@8YK°TK°T[X¹ÿÀ8YÄ91/ì290KSXííííY"@ŽHj{†€‘¤  &&)) 55::0FFIIFH@VVYYPffiigh`ut{{uz……‰‰‰†––—š˜˜—¨§°Àßÿ>]]3 3#=Ã^^Ãþ\ú`üT¬û V5` ë@IU U U U   B ¿    ÔK° TK°T[K°T[K°T[K° T[X¹@8YK° TK° T[K°T[X¹ÿÀ8YÌ91/<ì2290KSXííííííííY"@ÿ" 5 IIF @ [[U P nnf yy‡™˜” ¼¼ÎÇÏ         %%#'!%""%' $ ! # 9669 0FHF@B@@@D D D @@VVVPQRRPS T U cdejejjjn a g ouuy}x}zzxy  { v } ‡ˆ——”“œ›˜˜™@/– Ÿ¦¦¤¤««©©«¤ ¯µ±½»¸ ¿ÄÃÌÊy]]333# #V¸æåÙæå¸þÛÙñòÙ`ü–jü–jû –üj;y` C@F      B ¿  ÔK° TK°T[K°T[K°T[X¹@8YK°TX¹ÿÀ8YÄÔÄ91/<ì290KSXííííííííY"@˜   & =1 UWX f vzvt ‚ ™Ÿ—’ ¦©¯¥£       )&% * :9746 9 0 IFE J @ YVYYWVYVV Y P o x ›”«¤° Ï ß ÿ /]] # # 3 dþkªÙþºþºÙ³þrÙ))`ýßýÁ¸þHJþq=þV`‹@C        B  ‡½ ¼  ÔK° TK°T[X¹ @8YK°TX¹ ÿÀ8YÄÄ91ä2ôì9990KSXíííííí2Y"@ð     # 5 I O N Z Z j ‡ € “        '$$  )( % $ $ ' ** 755008 6 6 8 990A@@@@@@@@B E G II@TQQUPPVUVW W U U YYPffh ii`{xx‰Š … … ‰ ‰‰™ • • šš¤ ¤ ««°Ïßÿe]]+5326?3 3“N”|“lLT3!þ;Ã^^ÃhÈzšH†TNü”lXÛ` @B©¼© ÜK° TK° T[X¹ÿÀ8YK°TX¹@8YÄ2Ä991/ìôì0KSXííY"@B&GI  + 690 @@E@@CWY_ ``f``b € ¯ ]]!!!5!qjýL´ü}´ýe`¨üÛ“¨%þ²$w@4 %   ! © ©À ©±% $  C %ÔK° TX¹@8Y<Äü<Ä299999991üìÄôìî99999990²&]#"&=4&+5326=46;#"3>ù©lŽ==k©ù>DV[noZV¾”Ýï—ts•ðÝ“XøŽŽœøXþ®·±Ôì1üÌ0#®ªøþ²$‡@6%   ©©#À©±%#C %ÔK° TX¹ÿÀ8YK°TX¹@8Y<Ä2ü<Ä99999991üìÄôìî99999990²&]326=467.=4&+532;#"+FŒUZooZUŒF?ù§lŽ>>Žl§ù?¾VøœŽŽøŽW“Ýð•st—ïÝ”ÙÓÛ1#@ œœ ÔÄ1ÔüÔìÀ990#"'&'&'&#"5>32326Ûi³an’ ›^X¬bi³an“ ›^V©1²OD;>MS²OE<>L5Õ b@ƒ ü<ì2991/ôüÌ0K° TX½ @ ÿÀ878YK°TK°T[K°T[X½ ÿÀ @878Y¶ P ]#53#3ËËË¢×þú+eþ›¬þÇ#˜!Q@+  †ˆ †ˆ ¹ ¹¸Œ"  "ÜìÔ<Ô<<ì221äô<ÄìÄþôîõî9990%.'>7#&73¦“¤¤JˆDF‰HA‰Mfñþ÷ ñfI‰ƒX⸹⡬)*ü *'ª#þä 32þá!bð`@!† ©  ”‘   Ü<ÌÌü<ÄÔÄ1/ì2ôäìÔ<î2î990K° TX½ÿÀ@878Y´66].#"!!!!53#535632NLˆ=”t‡þy-üìÇÇÖè=—´¶))›Ô×þ/ªªÑîó^R¼²#/ƒ@I -'! - -¹ëì'¹ë!0 *$0* $ $(st*(s0Üäìôäì9999999991ÔäìôäìÀ9999999907'#"&''7.5467'7>324&#"326{ÏrÎ%$&(ÑrÏ;t=:x=ÏqÏ%%&&ÏsÏ7t@?s9ÏqÏ(&%%ÏsÎ>v:@t8ÎsÏ'%$þ|pššprœRÃÕÆ@F  B Ó Ó   fe f eÔ<ì2ìüì2ì99991/ä2Ô<ì2Ô<ì290KSXííííY"K° TX½ÿÀ@878Y@(†¦ µ' ' ')((79‡ ˆ¦ ¥ª©]]!#!5!5'!5!3 3!!!þcÉþ` Tþ´þþ{y¿þÂþµTŸÇþ9Ç{3›{JýD¼ý¶{›3þ¢®˜@ õõÜ<ì21ÔìÔì0##®ªªª˜ý öý ö\ÿ=¢ð >‘@54&.#"#"&'532654/.5467.54632{?>‹ú?>ÌS8alÎÓƒ\]>9Ì­IšXW”:fqÝÖ€][;;ȦI™¨.Z.L…‡-[.Kˆ“¤''PGZswšeZŒ54m@ލ¤''TLf{x™f[1,pE‚Ÿ×F)’@ÎÍddÜüÔì1ü<ì20K° TK° T[X½@ÿÀ878YK° TK° T[K°T[K°T[X½ÿÀ@878YK°TK°T[X½@ÿÀ878Y@````pppp]3#%3#^ËËþyËËÊÊÊåÍ/IC@&=Ë>:ÌAÊ$1Ë04ÌGÊÉÈ$É 7aD=0^* D^ JÜÌüìþí2î1/îöþýîÖîýîÖî02#"$'&5476$"3267>54&'..#"3267#"&54632˜mmllmmþù˜˜þùmmllmm˜ƒâ^^``^^⃄ã^]]^\^ã§B‚B•§«›@zBC‰FØûûØIˆÍnmmþúš˜þûmmnnmm˜šmmng^^^å‚ã^^__^]⃅ã]^^õ! ¯Ÿ®"ôÐÑòsÕ;ð)_@3(%ãÝá%Ý ßÞÝ à‘* "(kl"k *ÜìÌüì22ÀÀ9991ôäüôìÄîíÖîî99990!!#5#"&546;54&#"5>32"326=‹°ýP®•,]€˜¿¼¶uu>ˆDI‘E·³þì¡~bRh‚P{¸þ@p?D‡q‡Š[[""°ðCO@Mrž%# †@Ièèèè è è è  è B  ç¦ o o nüü<Ôì2991ô<ì2990KSXííííííííY" 5 5%þÓ-þ+#þÓ-þ+#¿þôþô¿¢R¢¿þôþô¿¢RÙÛ^@ œÜÔì1ÔÄì0!#!Ù¨û¦^ýÁ•d߃¶œÜÌ1Ôì0!!dý僤åÍ/8L`@6EBC?2ÉH0É9JCÊ 9ÊÉÈ É$HE301B54&'.'2#"$'&5476$#32654&'2#'.+#ƒâ^^``^^⃄ã^]]^\^ㄘmmllmmþù˜˜þùmmllmm}{{nWXf°®i`C.‰¬;I6B›f^^^å‚ã^^__^]⃅ã]^^gnmmþúš˜þûmmnnmm˜šmmnþbþì>KL?gwyVpMIßÑ`3þœDÕb+ö/·ïîÔÌ1üì0K° TK°T[X½ÿÀ@878Y!!ÕVýªö”Ãu=ð  @ÃÄà ‘ Z[ZÜìüì1ôìüì0"32654&'2#"&546PnnPPnoO@v+..¹†‡´¸ooPOmmOOp1.-rB„·´‡†ºÙÛ .@МР œ   Ô<ì2ü<ì21/ìÔ<ìü<ì0!!#!5!!!®-ýÓ¨ýÓ-ýÓúþþ}ªþ}ƒªƒû¦ª^œ´ðJ@$}}BÝÝ÷ Ý‘~ÜÄÔÄì91ôÄìüìî90KSXí2íY"!!56754&#"5>32 ¨ýª"?XhU4zHM…9‘®þµ8rn81^BQ##{„l‹þä0bÍð(H@' Ý Ý Ý Ý ø÷Ý ø#‘)~&~ )ÜÄÄÔìÔì9991ôäìüäìÔìîî90#"&'532654&+532654&#"5>32 \e¾±9}F4wCmxolV^^ad_(fQI€7©Z`mR|†yOFJLl?<:=svcE`sîRf1@ ´³DÔì1ôì0K° TK°T[X½ÿÀ@878Y3#‹Çþº™fþˆ®þVå` M@% ‡Œ ¼½!   NF!üì2ôìÄ91ää2ô<ìÜÄ990¶"`"Ï"]3326533267#"&'#"&'®¸Š‡”•¸#% )I#ER2‘bf*þV ýH‘”¨¨ü¢<9 ”NPOONNýמÿ;9Õ %@Á]] ÔÔüÜì91Ä2ôì90!###.54$yÀ¾Ž×ëÕùfùáNݸ¾èÛH®F·ƒÔì1Ôì03#ÛÓÓFþ#þuÁ@  ó' ÜÔìÔÌ1/ÔüÄ90!#"&'532654&'T76xv.W+"J/;<+->i0Y[ ƒ0.W=‰œÅß ,@Ý ÝÝ ÷‘ |]|| Üôäüä1ôììÔìî2035733!œÌßæ‰Íý× c)t'ý+n`Õdð.@ãáÝ àÝ‘ klk Üìüì991ôìôìüì0!!2#"&546"32654&‹°ýPX³Îγ³Ðгi~hi}|P{Ý¿¿Ûܾ¿Ýs¡ˆ…  …‰ ÁH# †@I è è è è èèèèB  ç¦ o opü<üÔ<ì991ô<ì2990KSXííííííííY"5 %5 ÁÕþ+-þÓ²Õþ+-þÓ#þ^Rþ^¿  ¿þ^Rþ^¿  ÿÿ‰ÿãð&{' ô‹ýd 35ÿÿ‰ÿã?ð&{'t‹ýd 35ÿÿbÿãð&u' ô‹ýd 35ÿã¬Õ $†@/ †ˆ !ƒ# •Œ#%" " "!& %ÜìÔüìÔì99991äôìþÍôî9990K°TK°T[K°T[X½%ÿÀ%%@878Y@ ttttv]33267#"&546?>7>5#53ô¾7ZZ:3ƒmN´`^Àg¸àIYX0&ÄÊÊDœe‚WX5^1YnFC¼98ŸL‰VV/5<6þÿÿhk&$˼uÿÿhk&$ɼuÿÿhm&$̼u´  +@ ]1ÿÿh^&$ʼu´ #+@ @O# /#]1ÿÿhN&$ȼu´  +@ 0?  ]1hm !Ë@T   !!  ! !!!B  Á • Ž  !  VV!"ÔÄÔì2Ôî299999991/<æÖîÔî9990KSXííííííííY"² #]@  s › P#f iu {yyv v!€# ]]4&#"326!.54632#!#TY?@WX??Y˜þð!þX=>Ÿsr¡?<Òˆý_ˆÕZ?YWA?XXþóýN)sIs ¡rFv)ú‹þHÕ‡@9  B• ••••­    ÔÔ<ì2ÔÄÄ91/<ììÄôììîî0KSXííííY"²€]@gww† …– ¿ ]!!!!!!#!5ýÇý9øü=ýð Íq‹þ¶ËÕªþFªýãªþÕžüðÿÿsþu'ð&&z-ÿÿÉ‹k&(ËžuÿÿÉ‹k&(ÉžuÿÿÉ‹m&(Ìžu@@ ]1ÿÿÉ‹N&(Èžu @@ @]1ÿÿ;ºk&,Ë/uÿÿ¢k&,É/uÿÿÿþ`m&,Ì/u´ +1ÿÿXN&,È/u´ +1 ºÕ g@  © ••  2  yô<ì2ÄôìÄ91/Æ2îöîî20@( °Ÿ Ÿ Ÿ Ÿ ŸŸŸŸ¿ ¿ ¿ ¿ ¿¿¿¿]]! )#53!!3 !Ó ±–þiþPþ`ÉÉËPþ°ó5þáþËÕþ—þ€þ~þ–¼ãþýê.,ÿÿÉ3^&1Êþu´"+@ 0?""]1ÿÿsÿãÙk&2Ë'uÿÿsÿãÙk&2É'uÿÿsÿãÙm&2Ì'u´+@]1ÿÿsÿãÙ^&2Ê'u´!0 +@ 0!?0 !/0!0]1ÿÿsÿãÙN&2È'u´ +@ @O]1?œÅ …@M œ  œœœœœ œ œ B   Ô<Ì291Ô<Ì290KSXííííííííY"  ' 7 œþ7Éwþ5þ5vÈþ8vËËLþ5þ7yËþ5yÉËyþ5Ëfÿºå +ž@< +,  )&  *&•& •‘&Œ,+,* # )#3,üìüìÀ999999991äôìîÀÀ99999990@*WZWU!je!{vu! FYVjddj(|svz( ]] 324&'.#"&5!27!"&''¶ý3>¡_Ü'y=¡_Üþý''†NOy;‚ÝW¢fªNPþˆþÆ€Ý[¢gXü²@CHp¸¸@Cþ¸þåp¼Džf b¥MK¿YÆgþöžþŸþ[KK¿Xÿÿ²ÿã)k&8Ëîuÿÿ²ÿã)k&8Éîuÿÿ²ÿã)m&8Ìîu´ +@ / ]1ÿÿ²ÿã)N&8Èîu´ +@P_@O /]1ÿÿÿüçk&<ÉsuÉÕ =@• •ö  ? üì22üì91/ôüìÔì0@ ?_]332+#32654&#ÉÊþûþÿûþÊÊþš™ŽÕþøáÜÜâþ®'ýÑ’††‘ºÿã¬/š@0-'!  *†¹*¹—Œ.  !' $'$-F0üÄüÌÆîÔîî99991/äþîþÕî990@@'(Š Š     ! "&  : :!MM I!I"jj ¥¥¦ ]]4632#"&'532654&/.5467.#"#ºïÚÐÛ—¨:A9¦`áÓ@ˆIPŒAtx;e\`W§—ƒq‚ˆ»qÈÛèàs`/Q*%jŽd¬·¤_[?T>7;‡[¬gp‹ƒû“ÿÿ{ÿã-f&DCR @?&/&&]1ÿÿ{ÿã-f&DvR @?&/&&]1ÿÿ{ÿã-f&DˆR´ (,+1ÿÿ{ÿã-7&DžR´.< +@ ./<.<]1ÿÿ{ÿã-&DjR ´-( +@(o(P-_(@-O(0-?(-( ]1ÿÿ{ÿã-&DœR%@&,,& 2882 +Ä+Ä1@ ?5?/5/]0{ÿão{3>@C'-%= 4©%†ˆ©:¹.†-º*¹»1 ¸Œ%?47&%7& =&-7"E?üìÌÔü<ÔìÄ999991Ää2ô<Ääü<ôìÄî2îôîî9990@0+0,0-0.0/00@+@,@-@.@/@0P+P,P-P.P/P0…+…0€@@ @°@À@Ð@à@à@ð@??? ??0,0-0.0/@,@-@.@/P,P-P.P/ooo oo`,`-`.`/p,p-p.p/€,€-€.€/]q].#">32!3267#"&'#"&5463!54&#"5>32"326=¶¥‰™¹DJÔ„âü² Ì·hÈddÐj§øMIؽÒýû§—`¶Te¾ZŽÕï߬o™¹”—´®ž0Z^þÝúZ¿È55®*,ywxx»¨½À‹..ª''`þf{bsÙ´)ÿÿqþuç{&Fzÿÿqÿãf&HC‹ÿÿqÿãf&Hv‹ÿÿqÿãf&Hˆ‹´"+1ÿÿqÿã&Hj‹@@ ]1ÿÿÿǦf'Cÿóÿÿof'vÿóÿÿÿÞ\f&óˆÿ´ +1ÿÿÿôF&ójÿ´ +1qÿãu('@^%{&%#${##{#({'(#&'('%$%(('"#" ! B('&%"! ##¹ ¹Œ#±)&' ! (%#" QE)üìôì99999991ìÄôìî9990KSXÉÉÉÉííííY"²?*]@v%+("/#/$)%-&-'*(6%F%X X!` `!f"u u!u"%#%$&&&''(6$6%F$E%Z Z!b b!z{     {zzv v!x" *ð*']].#"32654&#"432''%'3%F2X)§¹®’‘®6 ~rþäæçþåÝ4*ŸþÁ!µäM!þÙ“ØÃ¼ÞÞ¼z¼&þà­ÿþÉ7ÿú7´kc\Ì‘oabÿÿºd7&Qž˜ÿÿqÿãuf&RCsÿÿqÿãuf&Rvsÿÿqÿãuf&Rˆs´+1ÿÿqÿãu7&Ržs´ .+@ /. .]1ÿÿqÿãu&Rjs´ +@ @O0?]1Ù–Ûo )@êêœ r ÜÔ<ü<Ä1ÔÄüÄîî03#3#!!ßööööýúúþoöþõAªHÿ¢œ¼ +ä@<+,&  )&  *&¹& ¹¸&Œ,+,* # #Q)E,üì2ôì2À9999991äôìîÀÀ99999990@p(?-YVUV jf!{    { z{ {!"#$%{&›•%¨ -ð-&YVUZ(ifej(ztvz(‰•š$¢­$]] 32654&'.#".5327#"&''‰þ)gA“¬\*g>—©}66ñ]ŸC‹_’56þîð`¡?‹`!ý°*(èÈOuš))ëÓHn.—MÅw834¨O³MÆxþíþÇ43¨Nÿÿ®ÿãXf&XC{ÿÿ®ÿãXf&Xv{ÿÿ®ÿãXf&Xˆ{´ +1ÿÿ®ÿãX&Xj{´ +@ @O0?]1ÿÿ=þVf&\v^ºþV¤>@¹¹Œ¸½— GFüì22ôì1ìääôÄìÆî0@ `€ à]%#3>32#"&4&#"326s¹¹:±{ÌÿÿÌ{±8§’’§§’’§¨ý®¾ý¢daþ¼þøþøþ¼aëËççËËççÿÿ=þV&\j^´+@ 0? /]1ÿÿh1'q¼;$´ +@@O]1ÿÿ{ÿã-ö&qJD´+@o]1ÿÿh’'šÎJ$´+1@oo]0ÿÿ{ÿã-&šO×D´"+1ÿÿþu¥Õ&$äÿÿ{þu€{&D¿ÿÿsÿã'k&&É-uÿÿqÿãçf&Fv‰ÿÿsÿã'm'ÌLu& ²<=/1ÿÿqÿãçf&Fˆ¤ÿÿsÿã'P&&ÐLuÿÿqÿãç&F›¤ÿÿsÿã'm&&Í-u@]1ÿÿqÿãçf&F‰‰ÿÿɰ&'Íì‰ÿÿqÿãÛ&GÇ @_?]1ÿÿ ºÕ’qÿãô$J@$Ó ù"¹¹ Œ¸—   GE%üìô<Äü<Ä1/ìäôÄìÄîý<î20¶`&€& &]!5!533##5#"3232654&#"¢þºF¸šš¸:±|ËÿÿË|±ýǧ’’¨¨’’§¶N}““}úü¨daDDaþËççËËççÿÿÉ‹3&(q¡=ÿÿqÿãö'q–H@p]1ÿÿÉ‹m'Ï¡u(@@]1ÿÿqÿãH'š–H@p]1ÿÿÉ‹P&(Сuÿÿqÿã&H›–ÿÿÉþuÕ&(Ìÿÿqþu{&HxÿÿÉ‹g&(ͦo@@ ]1ÿÿqÿãa&H‰”ÿû´!+@!]1ÿÿsÿã‹m'Ì\u* ²<=/1ÿÿqþVZf&ˆhJ ² <=/1ÿÿsÿã‹m&*ÏuÿÿqþVZH&Jš‹ÿÿsÿã‹P'Ð\u*@?]0ÿÿqþVZ&›hJÿÿsþ‹ð'×^ÿí*ÿÿqþVZ4'Ãà JÿÿÉ;m'Ìu+´ +@ / ]1ÿÿÿådm'ÌuK*´+1K°QX»ÿÀÿÀ88Y@ €€@@]É‹Õ:@•  •­  8 Üì22Ìüì22Ì1/<ä2üìÜ22ì2203!533##!##53!5qÊÞʨ¨Êý"ʨ¨ÊÞÕààà¤û¯Çý9Q¤¤ààxŸ>@! ‡ ‡¸— N  Ü2ì22ÌÌôì1/<ìôÄìÜ2ì290#4&#"##5353!!>32Ÿ¸||•¬¹}}¹`þ B³uÁƤý\žŸž¾¤ý‡ö¤zz¤þ¼edïÿÿÿäx^'Ê.u,´ +1ÿÿÿÓg7'žÿó´+1ÿÿY1'qÿ.;,´+1ÿÿÿòHõ'qÿÿÿó´+1ÿÿÿõgm'Ï.u,´+1ÿÿÿäVH'šÿó´+1ÿÿ°þu%Õ'ÿd,ÿÿ–þu 'ÿJLÿÿÉ•P&,Ð/u³<<1´??]0Áy`,@ ¸¿Füì91/ìä0@4D@P`p]3#Á¸¸\`û {ÿÿÉþfïÕ'-\,@ì1ÿÿÁþV±'M8L@Fì1ÿÿÿ–þf_m'Ì.u-´+1ÿÿÿÛþV\f'ˆÿù´+1ÿÿÉþjÕ'× .ÿÿºþœ'׬ Nºœ` »@(B¼ F üì2ÔÄ91/<ì290KSXííííY"² ]@_ ')+Vfgsw‚‰Ž“–—£    ('(++@ h` ‰…‰š—ª§¶ÅÖ÷ð÷ð]q]33 ##º¹%ëý®kðýǹ`þåýòý®!ýßÿÿÉjl'Énv/ÿÿÁJl'ÉZvO±<1K°QX¹@8Y@ŸO]0ÿÿÉþjÕ'×› /ÿÿˆþ­'× O@@]1ÿÿÉjÕ'ÇŸÿÃ/ÿÿÁ'Ç9O @]1ÿÿÉjÕ'y1w/ÿÿÁ„'yÖsOK° QK°SK°QZ[X¹@8Y1ÿòuÕ ?@ •  : yô<ìÄü<Ä991/äì90´0P]3%!!'7ÓË9Pþw×ü^”MáÕý˜Ûoþîýãª;jnžH ^@ — z z Ô<äü<ä991/ì90K°TX½ @ ÿÀ878Y@ @ P ` sz p à ð ]37#'7Ǹ}Lɸ{JÅý¦ZjüãšXjÿÿÉ3l'ÉÅv1@O]1ÿÿºdm&vBQ @?O]1ÿÿÉþ3Õ'× 1ÿÿºþd{'× QÿÿÉ3_&1Íõg´ +@ /  ]1ÿÿºdf&Q‰´ +@]1ÿÿ͹Õ'QU~ÉþVð;@  AüK°TX¹ÿÀ8Yì2ÔüÌ1@ •°•‘/äôìôì0!"#367632+53265Pþͳ×ÊÊNij™ãéQRµW1fO¬ÿÞü²Õñ†CCþÁþÌüoÕa`œZ ºþVd{;@ ‡‡¸¼ NF üì2ôìÄ1/äôÄìÔì90´`!Ï!]+5327654&#"#367632dRQµþéi&&||•¬¹¹BYZuÁcc¤ýHÖ``œ01™²Ÿž¾¤ý‡`®e22wxÿÿsÿãÙ1'q';2´ +@]1ÿÿqÿãuõ&qsÿR´+1ÿÿsÿãÙm'Ï'u2´+@]1ÿÿqÿãuH&šsR´#+1ÿÿsÿãÙk'Ñ'u2ÿÿqÿãuf'Ÿ Rs Õ;@•••­   üìÔÄÄÔì299991/ìì2ôì2î0!!!!! !# !3úýÇý9øû×þOþA¿±gþ¿þÀ@AÕªþFªýãª|pm|ªþáþàþßþßqÿãÃ{'3„@1†ˆ ©. ¹(¹»"%¸Œ4"1 K1 Q+E4üìôüôìÄ9991ä2ô<Ääì2Äî2îôî90@%?5_5p5Ÿ5Ï5Ð5ð5????? ooooo ]q].#"!3267#"&'#"32>32%"32654& ¤‰™¹Hü² Ì·jÈbdÐj òQGÑŒñþïñŒÓBNèâú°”¬«•“¬¬”˜³®ž5Z¾Ç44®*,nmnm98olkpþ݇çÉÉçèÈÇéÿÿÉTl'É•v5ÿÿº”m&vBUÿÿÉþTÕ'× 5ÿÿ‚þJ{'× UÿÿÉT_&5Í}g@_]0ÿÿºZf&U‰´ +@]1ÿÿ‡ÿã¢l'É•v6ÿÿoÿãÇm&vBVÿÿ‡ÿã¢m'Ì“u6 ´ ))Ic:1ÿÿoÿãÇf&ˆ%V ´ ))Ic:1ÿÿ‡þu¢ð&6z‹ÿÿoþuÇ{&Vzÿÿ‡ÿã¢m&6Í‹u ´+ ""Ic:1ÿÿoÿãÇf&V‰ ´+ ""Ic:1ÿÿÿúþuéÕ&zP7ÿÿ7þuòž&záWÿÿÿúé_&7Ísg´ +1@_]0ÿÿ7þ‚&WÇ7p@]1ÿúéÕF@ • • @ @ Ô<äÌü<äÌ1/ôì2Ô<ì20@@pŸ ]!!!!#!5!!ïýî þ÷Ëþ÷ ýîÕªýÀªý¿Aª@7òžC@©©¼ ‡ Fü<<Ä2ü<<ÄÄ2991/ìô<Äü<Ü<ì20²¯]!!3#;#"'&=#535#53w{þ…¾¾%&s½½ÕQQ‡‡‡‡žþÂéŽé‰''šPOÒéŽé>ÿÿ²ÿã)^'Êîu8´ '+@ ]1ÿÿ®ÿãX7'žƒX´&+1ÿÿ²ÿã)1'qî;8´ +@ / ]1ÿÿ®ÿãXõ'qƒÿÿX´+1ÿÿ²ÿã)m'Ïîu8´+@]1ÿÿ®ÿãXH'šƒX´+1ÿÿ²ÿã)o&8œðiÿÿ®ÿãXÊ&Xœ|Ä @@@!]1ÿÿ²ÿã)k'Ñîu8ÿÿ®ÿã^f'Ÿ°Xÿÿ²þe)Õ&8úÿðÿÿ®þuè`&X'ÿÿD¦t'Ìõ|:´+1ÿÿV5m'ˆEZ´+1ÿÿÿüçt'Ìr|<´ +1ÿÿ=þVm&ˆ^\´+1ÿÿÿüçN&<Èsu´ +1ÿÿ\l'É•v=ÿÿXÛm&vB]ÿÿ\N'Шs=ÿÿXÛ&]›ÿÿ\m&=;uÿÿXÛf&]‰´ +@ ]1/ø#@ ‡ —©¼ Lü<ÌüÌ1/ôìôì0!##53546;#"˜¹°°®½®°c'&ÑN»«™()g ÿã¤,D@%¹ ¹ Œ¸")%©,$'—".EG* ,(%#'F-ü<Ìì222Ìôìì1/ôÜ<ì2äôÄìÆî04'&#"327667632#"'&'##5353!!åST’’TSST’’TSýŽ:YX{Ì€€Ì{XY:¹šš¹Eþ»/ËtsstËËtsstRd01¢¢þøþø¢¢10d¨}““}ÿ—PÕ)C@#• •• ­ . *üì2üìÔì9991/ììôìî90²"]!2654&#!2654&#%!2#!"#546÷D££þ¼+”‘‘”þ çú€|•¥þðûýè„vœÀÉý݇‹Œ…fþ>orqp¦À±‰¢ ˘ÈÚ0_i1Fµ£ÿÿÉìÕºÿã¤&8@¹#¹ Œ¸©—(EGF'üì22ôìÄì1/ôìäôÄìÆî067632#"'&'#!%4'&#"3276s:YX{Ì€€Ì{XY:¹NýkrST’’TSST’’TS¶d01¢¢þøþø¢¢10d¨¦üÀËtsstËËtsstìÕ 3@  . /Üì2üìÄÌ1@ • ­ •/ìäôì³ 9/04'&#!!276!2#!#ON£þ¼D£NOý|NûþðûýèÉ8·‹DCýÝDC¨ýšÚÞÝÚD‘ÿã¤>@  G /Üì22ôìÄÌ1@¹¸ ¹ Œ—/äôìÄôìij9/0>32#"&'##34&#"326s:±{ÌÿÿÌ{±:¹º"Qr§’’§§’’§¶daþ¼þøþøþ¼ad¨DÑüËççËËççsÿã'ð0@†ˆ•‘ † ˆ• Œ0 Ü<ôìì1ôìôìôìôì0>3 !"&'53 !"shí†S†þzþ­„íjfç‚þðÿ‚çfbGGþaþ˜þ™þaHHÓ_^9'(9^_sÿãZd$D@"!• % ¡® •¡®•‘Œ%  0%üü2ììÄ1äôìôìîöîÜì0´&&].# !267# !2676;#"'fç‚ÿþð‚çfjí„þ­þz†S`œ SfãM?†nbÕ_^þÇþØþÙþÇ^_ÓHHŸghŸ$Ãbzª–qÿãÌ"N@$†ˆ† ˆ ¹¹¸Œ#‡—# HE#üô2Ììì1ôìäôìþôîõî0@ $$€$$ $].#"3267#"!2546;#"çNP³ÆÆ³PNM¥]ýþÖ-GF¡µE0iL~õ++ãÍÍã++ª$$>: ÖÀœaÿÿ ºÕ’ÿ—Õ.@• •  2 üìôì99991/ìôì0²`]3 !%! )"#546÷ô5þáþËþBŸ²–þhþPþa„vœÀ/ûw.,¦þ—þ€þ~þ–0_i1Fµ£ÉìÕ.@  .üìôì2ÄÄ1@ • •­• /ìôìôì0)!"!!"$54$3!!ž@Dþ¼£NýèûþðûNý|·þï#‡“ú+ÚÞÝÚÀqÿãZ?@¹¹Œ¸‡—G EüìÄôì221/üìäôÄìÄî0¶` €   ]5!#5#"3232654&#" M¸:±|ËÿÿË|±:ý§’’¨¨’’§n¦ùì¨daDDad¹üÀËççËËççqþVuc'T@ )E Q E(üìÄô²]ìÔìì99@   ¹¹¶(ôìÔüÔÌ99@%S ì910%!"'53254%&'&326&#">kþGÝxfööþøÐuŽïð›'«””¬¼~@3cnBOþðF™Fu\0%p‡9þÇþíœþü ËåèÃÂÇ *܃EÕ +@    ÜÄÄÔì2Ä1@ • •­•/ìôìôì0!5!!5!!5Eü>øý9ÇýÕú+ªªºªuÿãÙðD@&¡®•••­• ‘Œ3üÄìôì91äôìôäîîôî90!!"56$3 ! 7327uþíþî‹üpo’^‹þˆþÆþ·þ—Ü ÿÊÊÿ   2`_×FHþgþ’þŸþ[·ÌÃþääÿã{ð(@@$ † ˆ •‘)•­)† ˆ•#Œ)* &)üìÔìÔÄÄÌ1ôìôìôì9ôìôì0.54$32.#";#"3267# $546؃Ž æYÉsr¾S˜£ž•¶®¥¹Ç¾mÈTjÇ^þèþУ%!«|²Ñ ´&${spw¦•„–21Ã%%òÝÄÿ–þf#ÕA@ • °••­ üì2ÔÄÄ991ìôìîôì990² ]!!!!+53265ÉZýpPý°ÍãM?†nÕªþHªýŸþòôª–ÂÿþVøe@# ‡ ½©‡—¼ LüK° TX¹@8YK°TX¹ÿÀ8Y<Äü<ÄÄ991ä2üìî299ôì990¶@P ]#"!!+53265#535463ø°cM/þÑ®½®°cM°°®½™Phcû뻫™Ph*N»«sÿã—d&I@43! F'üìüôìüÄÄ1@•'$•$•¡®•‘Œ'äôìôìþÔî99Üì0%!5!# !246;#".# !26Ãþ¶uþæ þ¢þu‹^[£DÉãM?†npü‹þîþík¨Õ‘¦ýSU™mn™¼êª–Â×_`þÎþÑþÒþÎ%þRvÕ%•@ 'P $&ÔÔìÔìÔ]ĵ 91@ •½%$&ä222ôì³990@ #%$$ì<ì<¶$ì¶#$ì´%#@$"! #9927654'&'3#"'&547673¿6,3,,3,6ÙÞýºhC.Kd››dK.ChýºÞþý B9Iy\\yI9B z^ûÏÈ®wB‹AWWA‹Bw®È1ºG*O@, *&NF+üì2ôìÄÄÌÔì91@‡ '&‡& ‡ #¸—/<ìô<Äì9ì99ì0%27654'&'5+"&54&#"#3>323L•TWJ>yàmoþàÝF»||•¬¹¹B³uÁÆLiœbe›Þp_!”‘þìõþæÈΟž¾¤ý‡ýžedïèþò“gÉÆÕ .@ • üK°TX¹@8YìÄ991/äì9903;#"&ÉÊn†?MãÍÕü-–ªô RÕ E@ •¯  ü<ÄK°TX»@@88Yì2Ä1/ìÜ<ô20@ 0 @ P ` Ÿ ]33###53ÉÊ¿¿Ê¿¿Õýªý¿AªÉ÷ðf@ üì2ÔÄ9Ôì1@%    B  •‘¯ /<ìôì9990KSXííííY"#54&#" !#3632÷ªI&F%ýÝþöý3ÊÊlUqˆUUHy6P#ýùüãÏý1Õý‰CO\[n¹œË@   Füì2ÔÄ9Ä1@B  •— ¼/<äüä90KSX@     ííííY²]@5 ( ' ( ++@h `‰ … ‰ š —ª § ¶ÅÖ÷ ð ÷ ð ]´ q@%  ' ) +V f gs w ‚ ‰ Ž“ – —£ ]46;#"3 ##¹£µ¿¨iL%ëý®kðýǹ~ÖÀœa™ýÿãýôý¬#ýÝ * 2@F ü<ì21@ ©—/ìÔ<ì20@  @ P ` p ð ]33###53Á¸±±¸··ý8ýD¼=3''%'3%# =ëGþÔ!)KÈ4:!þÉíÃþÆþ~2¼eccÅŠhahú×<üIJÿã'Õ'J@ )PP (üìüüüÌü<91@ !•!$Œ( /ô<<ô<Äì2909%#"'&5332653327653#5#"'&¦EÀ‚¯__Ë'9u¦Ë99w{SSËË?°yzVUÕ|v{zâûïº5N¾¤ìûï¢NM_`£ìú)®gb>>ÿ–þf3ÕŒ@)B • °¯6  9üìä99üì991/ì299üì990KSXííY"² ]@068HGif€ FIWXeiy…Š•šŸ]]!3!+53265É–ÄþðýjÍãG?†nÕûáú+áû‡þòôª–ÂÿÿºþVd{DsÿãÙð 1@ 3üì2ôì21@• •­ •‘ Œäôìôìì0! ! "!&32sy:;xþˆþÅþÆþ‡µÊÿ ¬þýVûÜÜøéb¥þ[þŸþžþ[¤ÅþäÃÃýzþÿþÂ=ÿÿgÿã&2ôÌ¢4ÿÿvÿãÓë'ÌX RsÿãÏð3@•¯• ‘•Œ!  üìÔ<ìÜìÄ1/ôìôìôì0!4&#! !2!2"327&nz¼þÅþÆþ‡y;pa'ãÍüXÜþýܯ€ŠÓ–û‹Ó¤bb¥ôþòü-Lþ¸þæþåþ¸gFqþVY{!:@ ‡¼"¹¸"¹Œ½"# E"üìÔìÜìÄ91äôìôìôì04'&##"3232"327&¡&&i‰ðñþïñdRصRQý”¬«•@TþVt™10ü¼98``ÖûŒ‰çÉÉç:ð6ÿ—ñÕ:@••   ? üì2üì91/ôìÔì0@ ?_¯]32654&#%!2+#"#546÷þššþ8ÈûþÿûþÊ„vœÀ/ýÏ’‡†’¦ãÛÝâý¨0_i1Fµ£¹þV¤$O@$#¹¹¸Œ½% ©—%G  F%üì22Ì99ôì1ôì99ääôÄìÄî0@ `&€& &à&]%#46;#">32#"&4&#"326sº£µþçiL:±{ÌÿÿÌ{±8§’’§§’’§¨ý®(ÖÀœa™Èdaþ¼þøþøþ¼aëËççËËççÉþøTÕV@  ?  üì22üÄì99991@• • /ìÜôìÔì9990@ @u|]#.+#33 326&#A{>ÍÙ¿J‹xÜÊÊþüƒý‰þš™Ž´~þh–bþ‘ÕþøÖغOýÑ’ ‘rÿãð!d@ -" "ÜìäüìÄ99991@B!¡” • •”‘Œ"äôäìîöîÎ90KSX@ ííY6 327# '&546?6764'& ÌäÆÊâ{š‡¼­áøþýÖþç’‘×âz¦<;YZþ¡ä¤LäþÀ-|숋Ð_ppÙ¶Ù+23Ù@@mdÿã¼{'Ï@  !! RE(üäìÔìÄ99991@ †'‰$†‰¹$¹¸Œ(äôìþõîõî90@S !S BKSXí9í9Y"²]@/)?)_))€)) )ð)]@% '$&((*//*( ( ))$††††]@.,,,;;;; q>323267#"&546?>54&#" L´fÎà˜«@«eŒ‚aÆflÃZØ÷¥Ä?”b‰‰Z¨N?¬ž‚•$%PKQY55¾##¶œ‰™*!I@TT((ÿÿÉ‹Õ0þòþV×6@   O ü2ü2ÌÌÔÌ1@‡ ‡— ‡½ üìôìÔ<ì20;#"&5# 54!23%&'&#"3wMc°®½®¾þò/µR5¿þ†!n|wjû=hP™«»®ÒØ`@o›š,0A37þVòž?@ Fü<Äü<Ü29üÌ1@©¼‡‡½/üììô<Ìì2990!!;+53276="&5#53w{þ…Ks½¤´F0j&&Õ§‡‡žþÂý ‰N®ÖÀœ01™ŸÒ`>éÕX@ •@  ÔÔìüä991/ôì2Ä990K° TX½@ÿÀ878Y@@pŸ ]!!##"#546®;ýîË^„vœÀÕªúÕ+Zi1Fµ£7òI@  Fü<Äü<Ä2291@‡— ©¼‡ /ìô<ì299ôì990²¯]!!;#"&5#53546;#"w{þ…Ks½½Õ¢‡‡®½®°cMÃcý ‰NšŸÒ`N»«™QÿúþféÕT@• ½•@@ ÔäüäÄ1ôì2ôì0K° TX½@ÿÀ878Y@@pŸ ]!!;#"&!ïýîn†?NãÍýîÕªû=–ªôÃÿÿ­ÿ÷_&8ûÌä4ÿÿ°ÿãië'Ìî XNÿãÏÊ:@!3   üÄüÄôÄìüÄ1@ • •Œ ôìü<ì2220!! 47!5!3254'5!ÏþÀ¡ŽþþÑþÏþ‘žþÁX²Ç ØØƱXþØÂþËþwŠ>¸*‹²²aþ´ÊïþÝ"ðÊLa²ÉÿávÕ-@   üìÔì/<91@ •ŒôììÄ0%254'&'5!'&'&33ÆØcAn³¡ŽÀ¿þÏMèagÊn†"ðʦmWDþØÂþËÅÄtzðü–ÿüððd@  @ @Ôäüä9Ä9/ì1@ •‘ ¯/ì9ôìÌ990@        BKSXííííY""#3 632#54&×9%þ„ËýðÙžNZ£ˆUUªIGý¿ý9Çýšùˆ\[nƒy6P=þVØ{j@  ÔK° TK°T[X¹ @8YK°TX¹ ÿÀ8YÄÄ9Ôì991@:        B ‡½ ¹¸ ¼ Ääôìôì9990KSXíííííí2Y"@      '$$  )( % $ $ ' 755008 6 6 8 A@@@@@@@@B E G TQQUPPVUVW W U U ffh {‰Š … … ‰ ™ • • ¤ ¤ F]@%     # 5 I O N Z Z j ‡ € “ ]+5326?3 67632#54&#"“N”|“lLT3!þ;Ã^0X‡ƒ¹²Q99) hÈzšH†TNü”À43`¿†rr:T*\ÕÀ@5    B ••• B ÜK° TK° T[X¹ ÿÀ8YÄÔä99ÄÄ91/ìôìÔ<ì20KSXíí<<<323#‰ÁüL:sþ§â_%'ST†_ijxzÔXè"Jhì0ªªª@umHLIwKK!!CÌ12èÂ\RI`1]ÿÁùÕ5@ FüÔììÔÔìÌ1@ •• • ôìÔìÌÔì0 4&#!!!%$ $5& ¹¥ý÷¡ý)s¢Q;-˜ýÄþÐþÛ%†“,ªþ%ÐhþàV)$yòÝhþL?`3@  FüÄìÔÔìÌ1@‡½‡ ‡ ¼ôìÔìüÌ2ì03 4&#!!!32!"'hªÎ–¹¥þŸýŸÝiä¦;-˜þè»Ô§c%†“,ªþ&cÔþàV)$yJXÿ㥞$!"'&'5327674'&+#5333!ªpln‰þíUQQLITNP³c9:VÀ>ååÊçæ}wsªº}#¬(rA…bLr¤þ줺þV¤{@@  Füì22ÔìÌ1@ B‡ Œ¼½ìÄôôÌì0KSX¶‡íÌY#36763254'&#"s¹¹4‡QÒ¸MNüÏr98xÜ­zþÐ ªBR1pq™þWþäù…BAïÉþV“Õ&@ —½Füì1ìì0@ @P`pð]3#ÉÊÊÕøÿÿÉþV'Õ'‚”‚þVœÕ:@ ©© — ½  Ô<<Ì2ü<<Ì21ìì9/<ì2Ü<ì203!!!!#!5!5!5!sÊ_þ¡_þ¡Êþ¡_þ¡_Õý—¨ðªý,Ôªð¨ÿÿɔՔÿÿÉ Ðm'?±'ÿÿÉ °f'@Õ'ÿÿqÿã‘f'@¶GÿÿÉþf$Õ'-‘/ÿÿÉþVÞ'Me/ÿÿÁþVï'MvOÿÿÉþfòÕ'-_1ÿÿÉþV·'M>1ÿÿºþVÞ'MeQÿÿhm&$;u±<1ÿÿ{ÿã-f&D‰Z´ +'+1ÿÿÿþ`m&,Í/u ´ Ic:1ÿÿÿà^f&ó‰ÿ ´ Ic:1ÿÿsÿãÙm&2Í'uÿÿqÿãuf&R‰v± <1ÿÿ²ÿã)m&8Íöu±<1ÿÿ®ÿãXf&X‰v ´ Ic:1ÿÿ²ÿã)3&û08ÿÿ®ÿãX1'q{;¾ÿÿ²ÿã)Z&8þ6ÿÿ®ÿãX"&Xþÿ¾þÈÿÿ²ÿã)Z&80ÿÿ®ÿãX"&XÿÄþÈÿÿ²ÿã)`&8ÿ0ÿÿ®ÿãX"&Xÿÿ¾þÈÿÿqÿã{ÿÿh3&$ûÿÿ{ÿã-1&¦qR;ÿÿh3&$ýÿÿ{ÿã-ô&Dýÿ“þÁÿÿH4'q×>ˆÿÿ{ÿãoò'qèÿü¨sÿãð%T@!$"43 &üìü<ÌäüÄÄ1@"#•%•¡®•‘Œ&äôìôìþÔî99ܰ KTX±"@8Y<Ì203## !2.# !2675#535!5‹yyuþæ þ¢þu‹^’opü‹þîþík¨Cýýþ¶ þÖXÿSU™mn™HF×_`þÎþÑþÒþÎ%'µX„¦qþVú{ 4X@"2% G,E5üÄìôì22ÄÄ1@ #% ) †¹¹2/¸3¼ ¹½&)/ÄäìäôÄìþÕî99Ô<Ì20¶`6€6 6]4&#"3263#!"&'532767!5!6=#"3253¢¥•”¥¥”•¥¸³Æ:þúa¬QQžRµZý„š9²|ÎüüÎ|²9¸=ÈÜÜÈÇÜÜënXF]@Œ³,*_EG^[cb::bcªÿÿsÿã‹m&*ÍJu±!<@”!T!$!]1ÿÿqþVZc&‰JýJÿÿÉjm'Í¢u.ÿÿÿéœm&NÍu*´ +1K°QX»ÿÀÿÀ88Y@ €€@@]ÿÿsþeÙð'4ÿð2ÿÿqþeu{'€ÿðRÿÿsþeÙ1'q';¬ÿÿqþeuõ&qsÿ­ÿÿ ÿÁøm';uyÿÿXþL/f&‰TÿÿÿÛþVdf'‰ÿ%ù ´ Ic:1ÿÿÉ ÐÕ'=±'ÿÿÉ °Õ']Õ'ÿÿqÿã‘']¶Gÿÿsÿã‹l'Év*ÿÿqþVZc&JvýÉÿã-Õ5@8üì2üì2Ôì1@•­ •Œ/äìä2üìÄ03!327653! '&5!#ÉÊÞÊ>=™”B>Êd`þæþígdý"ÊÕýœdûìŸPNOK¤ŸýZ߀xxvé ý9ÉþVð@@  üì22ÔìÌ1@ B• Œ½ìÄôôÌì0KSX¶‡íÌY%#3676324'&#"“ÊÊ8‘WâÆSTü‘¡=<íºœýº¹HW5xz¤þ7þή GFþÿÿÿÉ3k'Ëu1ÿÿºdd&QCÿþÿÿhs&‡É\}ÿÿ{ÿãÜs&§Éì}ÿÿHl'É\vˆÿÿ{ÿãoc&¨veÿýÿÿfÿºål'ÉþvšÿÿHÿ¢œc&ºvýÿÿhp&$Òåzÿÿ{ÿã-d'À˜ÿþDÿÿh6&$μ>ÿÿ{ÿã-H'ÂeDÿÿÉ‹p&(Ò¥zÿÿqÿãc'ÀºÿýHÿÿÉ‹6&(Φ>ÿÿqÿãH'©Hÿÿÿ§sp&,ÒYzÿÿÿÃc'Àfÿýóÿÿw6&,Î>>ÿÿÿãUH'Â$óÿÿsÿãÙp&2ÒAzÿÿqÿãud'ÀŸÿþRÿÿsÿãÙ6&2Î>ÿÿqÿãuH'˜RÿÿÇTp&5Òyzÿÿ‚Jc'À%ÿýUÿÿÉT6&5΀>ÿÿº^H'Â-Uÿÿ²ÿã)p&8Òzÿÿ®ÿãXd'ÀÔÿþXÿÿ²ÿã)6&8Îì>ÿÿ®ÿãXH'«Xÿÿ‡þ¢ð'×v6ÿÿoþÇ{'×,VÿÿÿúþéÕ'×S7ÿÿ7þòž'×WœþRsð16767654&#"5767654'&"567632œÇÂ¥~b’[?FðøŽjOQ_ciqYxw`eÀ„†GRxŠQY–‚þÐÚµþò@XKy^Žq†j«j=vX{5?:.P¾B*8hi²ªcqMmwqÚŽ{\(GþO¼{36767654'&#"5767654'&#"0567632G²°”rXBAR9?Øß|cGIN`\hOm`bs[«yx@Il|IP‡xþñÄ£þÑ3H2#Pš™³×ÊÊQÌãéÉ×ÕÿÞü²Õñ‡†þÁþÌúÙqÿpD(4%3254'"632!"'#67&5#"'&76323 76'& ¶% 44ÊnˆôþªI5"ÄC0:XY|Ë€€Ë|YX:¸üÕST$TTTTþÜT‚¯- ¸Îþ¿H:E“<$d01¢¢¢¢10d^üæþjtsst–tssqÿã%ð ;W@$3=E (B!8;7B/E<üìôì̲ ;]ôìÌôìì91@$3•­< ;‘<,Œ<ôìôÌÔÌôì990" 7654&327654'&'52 '&54767&'&5476!˸jkkjpkkÔô‚ª_;̨_`Lm‚ä–‹ª˜¬_`œ›ýº›œ`a««CU‚tÅMMþòMMMN†‡š'|OEH-AAˆž+Mdhaº€² "ccÙttttÙcc"FYX‚SJqÿãq 4C@6E B42()+&BE5üìôìÌ2ÔìÌ2ôìì1@4)"¹.­5¹Œ5ôìôì99Ì20" 7654'& '&5467&'&5473327654'qSRRS SSSRþ:4H‚’RQ…„þ„…¤’;4?¡+IHƒ‚IJ,ÅMMþòMMMN†‡MMJ@b™@Y "ccÙttttÙÆ"#VKŽYIAèAAAAtw>\þVÕŸ@ B  ÜK° TK° T[X¹ ÿÀ8YÄÄÔìä991@ B• •• /ìôìÜì0KSX@ ííY@@ )&8HGH  /59?GJOUYfiowxŸ]]+53276=!5!5!!žHrþéi&&ûõ°üg•üPÇþßP%œ01™š‘ªšûoXþVÛ`¬@   ÜK° TK° T[X¹ ÿÀ8YK°TX¹ @8YÄÄ´@`]Ä99Ô´@`]ì1@ B© ©¼© /ìôìÜì0KSX@ ííY@2&GI + 690EIWY_fh€¯]]+53276=!5!5!!ÛžHrþéi&&ý5´ýejýL´þßP%œ01™¨%“¨üÛÿÿhP&$›Ä@ÿÿ{ÿã-&D›_ÿÿÉþu‹Õ&(z¢ÿÿqþu{&Hz{ÿÿsÿãÙ3&2ûbÿÿqÿãu1&¸qs;ÿÿsÿãÙ3&2iÿÿqÿãué&Rÿµþ¶ÿÿsÿãÙO'Ð't2ÿÿqÿãu&›sRÿÿsÿãÙ3&2ýjÿÿqÿãu1&ñqs;ÿÿÿüç1'qr;<ÿÿ=þVõ&q^ÿ\Šÿp\%3254'"632!"'#67&73Î% 44ÊnˆôþªI5"ÄC1¸‚¯- ¸Îþ¿H:E“þÂÿÛþVy` 8@ ‡½¼  OF üìä991äôì990@  @ P ` p ]3+53265Á¸£µF1iL`ûŒÖÀœa™qÿ㌠#/A@1E%G +G!E0üìô<<ü<<ôìì1@(¹ .¹Œ ¸ —/ìä2ô2ì2ì20 6& 23632#"'#5#"'&76'&  7/ST$¨¨þÜT¹õr¹rôÌÿÿÌôr¹rõË€€]STþÜTSST$Túþjtsç–çs Å^ý¢Åþ¼ýðþ¼Å¨¨Å¢¢¢¢üé–tsstþjtssqþVŒ{ %/D@1E$G+G'E0üìô<<ü<<ôìì1@ *¹.¹!¸ Œ½¼0äää2ô<ì2ì20'&  7"'##"'&763253632 6& ÍSTþÜTSST$T¹ôr¹rõË€€Ëõr¹rôÌÿÿú¢ST$¨¨þÜTd–tsstþjtssþóÅý®RÅ¢¢¢¢ÅªªÅþ¼ýðþ¼þjtsç–çsÿýÿº|3 #!#'#7'7 3!Jå†afþp|ÒˆýÖÍ2F;R/þîo½]jÕþ¡¡Yþ'üñŽFF8Äý±þO ÿºŠ",'&76!27&'!2767# '#&# rfÜuÃÃS†v=:efc.1üô ˆ‚tsfjwv„þ´Â9Øt‚ÿˆˆFX»hÏÐ$xYv»+!üf //_ÓH$$Ç\/œþØ­ ÿ¢]¼"+'7&576!27&'32767#"'&#"i`½U—–UQ.-Y_výÓc³PNONMRS]ð“7îGG³cc^NæÌ lOU ý^q+ª$VqrÍg jÕ ;@  • •: ü<Ììü<Ì1/äìÔ<ì20@ 0P€€]33#!!#53ÉÊ¿¿×ü_¿¿Õýwýÿ²ÿº1##'!5!7 !é4þ"Ëþ gZýî™8fý¦,þÔi>ýÌý ý³XÇRªBYþ boþ{=4'&/&'&54632.#"3#"'&/&'&'&'53276 23«@«LLàÎf´LN¨Z‰DE11”?ÆPS{W„Ÿ“*L'TrGYí$alfcca‚FF'K((%$JK‚ž¬®((**T@%$!,KL‰œ[@Ÿ~$š=&[ó#¾5-,Xþ3`!;#"'&/&+=!qjýN\1“*Ll“TrGYí=Z^´ýe`¨üÝ1þø~$š=&[ó?œ %PÕ6@ ÔÔìÔì9991@ •• •/ôìÔìÔì0##32654&+"56;2‘'ÊñššþE¯O˜«þôþ÷Zý¦‘‡ˆ*,¶FÜá×çP{7@   ÔÔìÔì991@ ¹ † ˆ¹¸/ôìôîÔì032654'&#"5632##/dšLU†IVVN˜«û}„ÔÂʦ‘‡AH+¶FntÛÕåþü ìÕ (\@ #  . &%)ü<Ìì22ÌüìÔì9991@(•%• ••­#/ììôìî99/<ì20²*]!!!2654&#!2654&#%!2#!#53“[þ¥D££þ¼+”‘‘”þ çú€|•¥þðûýè¿¿ÉÉʇ‹Œ…fþ>orqp¦À±‰¢ ˘ÈÚp ÿãÎÕ_@ 8AüK°TX¹ÿÀ8Y<Ìì2ü<Ìì21@ • • Œä2ôì9/<<ì2299990¶Ÿ]3!33#! 5#53!3265²ËáË¥¥þßþæþåþߦ¦¬ý®Ã®Õý–jý–¤–þÜþÖ*$–¤¤}ðÓÓðÿÿhÕ)ÉÿB‹“33#!!!!#7#!#!¸ªAX’— þ¼¹.ý˜AªA°®þ<¹Ù—þV“¾ªþFªý㪾¾ÕúÕýãǺþFqÿB&-1&'&'!3267#"'#&'&3273&#"#ÇSošþ+”JajÇbcÐk{cPªm!)ü81Gª\9/ƒý¼‡š¹ZoÏ ”—Z þò6Zþ—44®*,! œ C ¬à"2’ÅJ®žþc¬ÿ–þfRÕY@   ••° 9 üK°TX¹@8Y<ì2ä99ÄÄ1äüìÔ<ì2990@ 0@P`Ÿ]#+53265#5333R¿ÍãM?†n¿¿Ê¿wýñþòôª–¦¸ýHÿÛþVS@$¾± ‡ ½©¼  OFü<<ì22ä991äÜ<ä2ôì99ôì0@ @P`p]33#+53265#533#Á¸£££µF1iLµµ¸¸`þ¤þ(ÖÀœa™ؤ¬ésþf°ñ$C@$ • °%"•• Œ‘¯%  %üìÔì22Ì991ìäôÄìÄîäì99053;#"&5# !232#"³Än†EMãÍMì¥þòþ¬T¥ìüßêÌÍëëÍÌêíèú“–ªô„€«\\«€ýxþãþ»EEþ»qþV@{$H@"‡½%"¹¹Œ¸¼%G E%üìôì22Ì991ìäôÄìÄîôì0¶`&€& &]#"&=#"3253;32654&#"@Fµ£:±|ËÿÿË|±:¸Li1ûï§’’¨¨’’§þVÀÖ¼daDDadªûŒ™a=ËççËËçç TÕ »@  ?  !ü<Ìì2üÄì99991@!  B• • /<ôìÔ2ì29990KSXíí9Y"²@"]@Bz%%%&'&&& "66FFhuuwˆˆ˜˜]]#.+##53! 32654&#A{>ÍÙ¿J‹xÜÊ¿¿Èüƒý‰þ’••’¼~þh–bý‰w¦¸ÖغOýƒ…J{=@ Fü<ÄÄü<<1@ ‡¸© ¼/äÔ<ì2ôìÄÔÌ0´PŸ]###533>32.#"«¹¬¬¹:º….I,œ§h¤þ<Ĥø®fc½ΡÿöìÕ3!733!#!53!Ù— –Ù—œþõþöËþöþôwþјÕààà¤þvý9ÇФ¤â þVµ`+5326?!533!33!+“N”|“lLT3!ÍþÖð¾Ã¸L¸Ã¹ïþ×ÁÚmhÈzšH†TòÍþ3Íþ3þð®ÿã`{ ,¸@ .% F-üì22ÔìÌÄ991@©¹¸-&†%º"¹*Œ¼-äôìüìôìÄÔä¶%  9990@1?$?%?&?'O$O%O&O'_$_%_&_'o$o%o&o'$%&'$%&']@+?#?$?%?&?'?(?)O#O$O%O&O'O(O)_#_$_%_&_'_(_)]2654'&#"367632#!3267#"&߬A@o™\]¸¸?^^ˆ¬fe~ûþþST—`¶Te__Zóð+f{b:9ml´)Lªf01a`¢½``FE..ª'üqÿãZ{8@¹¹Œ¸¼G Eüìôì221/ìäôÄìÄî0¶`€ ]53#5#"3232654&#"¢¸¸:±|ËÿÿË|±ýǧ’’¨¨’’§¶ªû ¨daDDaþËççËËççºÿã£{ 8@¹  ¹Œ¸¼G Füì22ôì1/ìäôÄìÆî0¶`€ ]4&#"326>32#"&'#3å§’’¨¨’’§ý:±|ËÿÿË|±:¸¸/ËççËËççRdaþ¼þøþøþ¼adª`ºÿ㤠$C@¹  ¹Œ¸!©—G! F%üì22Ä99ôì1/üìäôÄìÆî0¶`&€& &]4&#"326>32#"&'#46;#"å§’’§§’’§ýŽ:±{ÌÿÿÌ{±:¹³¥þéZ[/ËççËËççRdaþ¼þøþøþ¼ad¨~ÃÓœ}}ÿãõ{ 0@ †ˆ¹¸!†ˆ¹ Œ!"EH!Ü<ôìì1ôìôìôìôì06763 #"'&'5327654'&#"LQQU–—þÖý]SRMNONP³cccc³PNON5#þîþòþÂ$ª+qrÍÍrq+qÿsç{'/O@( ,,H"E0üä2ìì991@†ˆ¹.*¹¹%¸Œ0äÌôìüÜìõî0@ 11€11 1].#"67632#"'#47&'&!23254#"çNP³Æc'>Ij£J?_S›PI™ 9/•-U¢þŠ:M’„e5¬++ãÍÍrQ,3H=Y}/)9DhQ#3¡ :#ü:9KqþV@$K@$©½%"¹¹Œ¸—%OG E%üìôì22ä1ìäôÄìÄîüì990¶`€ ]#"&=#"323;32654&#"@Fµ£:±|ËÿÿË|±:¸Li1ûï§’’¨¨’’§þVÀÖ¼daDDad^ùØ™a=ËççËËççqÿã¹$=@"¹¹ Œ¸%©—%  GE%üìôì221üääôÄìÄî0¶`€ ]546;#"#5#"3232654&#"¢£µ¿ªiL¸:±|ËÿÿË|±ýǧ’’¨¨’’§¶ÈÖÀœa™û‚¨daDDaþËççËËççqÿã{"r@ KE#üÄìôì91@†ˆ©¹ »¹¸ Œ#äôìäììôì90@)?$p$ $Ð$ð$?????,//,ooooo ]q]47632!"&'532767!7&'&#"qƒ„âü”•œþôkÐcbdcj·fg ü²¸]\šˆRS^ú’‘¡¢þíþöœœ,*®4cd¾žWWZZqÿã{A@$ †ˆ©¹»¹ ¸ŒKEüÄìôì91äôìäîîôî905!.#"5>3 #"73267qN Í·jÇbcÐk 9þ×üâþù¸¥ˆš¹Z¾Ç44®*,þÈþöþíþ½#Ä—´®ž|ÿã„{ 4w@6.('4 KE5üÄìüìÜ<üÜĶ&  91@/©.'"¹"¸5 ¹Œ5ôìôìÔÜì´†ˆôì@  &"90@ 4 &'<<ü<<<%6'6'32#"'&'&'&5>3 73;#"'&5Nf ý— Rˆ¨`•”üä‚\þLÑladbcÐk œ$˸&&i+@¯WRÖŠÏ>8þœE#Z`vþí¢¡‘gœ»'#d4®*,œ#)u”™10œ`ZÈÿÿ…ÿãÈ|Bÿÿ…ÿãÈ|Ã…ÿã*|>i@@603273;#"'&5Â|ŠþþîPUTZGUU]—UTNH‰”›tCDFEwGQPabªLÄq_ì¸&&i+@¯WRþà@\’l­¶«%88ZX83˜,-F@.. §NBj†”™10œ`ZȦFqÿãÅ|/;@ 1 &,E0üìÔìÄÄÔì1@¹¸0©0¹)Œ0ôìÔìôì²#90"327654'&+5327654'&'2# 76ñû`cchÒ—UTNH‰”›tCDFEhÄqr<žÿÛþV`K@ ‡ ½©¼  OFü<<ì22ä991äÜ<ä2ôì990@ @P`p]33#+53265#53Á¸£££µF1iLµµ`þ¤þ(ÖÀœa™ؤqþV¸ 0U@)  †¹¹&#¸-©*— ¹½*-+& G E1üÄìôì22Ä991/ÄäìôìôÄìþÕî990¶`2€2 2]4&#"326!"&'5326=#"32546;#"¢¥•”¥¥”•¥¸þþúa¬QQžRµ´9²|ÎüüÎ|²9£µ¾©iL=ÈÜÜÈÇÜÜëþâþé³,*½¿[cb::bcÈÖÀœa™qþVZ` #C@ †¹¹#¼¹½ GE$üÄìôì21/ÄäìôìþÕî990¶`%€% %]!"326!"&'5326=#"43!¢þª‡–¥”•¥¸þþúa¬QQžRµ´9²|ÎüüÎ=ˆÍ»ÇÜÜëþâþé³,*½¿[cb:ù*qÿãO{8@©¹†ˆ¹¸Œ4 EüìÜäüÄ1äôìôìþÔî990%#5!#"!2.#"326©›Ae×{ýþÖ-hÅ]_Àc³ÆÆ³O|žŒýð$$>:77ª>>ãÍÍã`þRd`#y@ %  $ÔÔìÔìÔĵ 91@ ¹ ½¼$ä222ôì³  990´ì³<´ìµ<<³<³< 3#"&54767327654'&'bBÀþ_j&;––––;&jþ_ÀBC(::(èxüܱS€c‚‚c€S±$úŽ-EIdccdIE-`ÿãd`#y@ %  $ÔÔìÔìÔĵ 91@ ¹ Œ¼$ä222ôì³  990´ì³<´ìµ<<³<³< 3#"&54767327654'&'b)ÙþrG,E––––E,GþrÙ)C'88'ÑýêbLx>‚‚>xLbü-!@2FF2@!-®þVX`9@ ‡Œ ¼ ½  NFüìôì21ää2ôÄì90´`Ï]332653##"&®¸||•­¸¸C±uÁȺ¦ýaŸŸ¾¤{ùöVfcðºdC@! ‡¸ ©— N  Füì2Ä99ôì1/<üì99ôÄì90²`]#4&#"#46;#">32d¸||•¬¹£µþçiMB³uÁƤý\žŸž¾¤ý‡~ÖÀœa™ÌedïºþVd!J@%  ‡¸"©—‡ NF"üì2Ä99ôìÄ1/Üìüì99ôÄì90²`#]+53265#"#46;#"632d£µþéiLø•¬¹£µþçiMƒçÁƤýHÖÀœa™²=¾¤ý‡~ÖÀœa™ÌÉï >@¾ ± ©¼  Fü<<<ì2221/äÜ<ì2üì0@ @P`p]33###533#¸¤¤¸´´¸¸`þ¤þ<Ĥ¬éÿÿ¦n`Ft„` '@   Üì2üì21@  ©¼ ©/ì2üì205!#3!53t¨¨ý𰼤¤ü褤Kß#<@ % V V$ÜüÜ<ü<ÜüÔ1@#!à !×/ä/ì2Ô<ì903327673#"'#&'&#"#67632=¸ &}33[¸ &}33[üí %$R‡IJý… %$R‡IJLT5@  ÜÜÔ<2ü<ÜÄ1@¹ ©—/ä9/<ì2ÔìÄ033##4'# 7632&#"3¢¸úú¸=þèõ5*7M\üþ ýŽT½öú„K9ÁþV_ (@  F üüÔÄ1@  — ‡½ ôìä990;#"&5y=x1F¿™ùÎ|tœÌÊ(ÁþL6$²@#&#" F%üü<ÔÄÔÄì̲#91@B©"©"† “¹ ½"¼ —/ääüìôìì9/ìì³ 990@$#ìì@  **8;ILT[q ]@$$%$$5$7E$FT$\ ]@    ]2!"'&'5327654'&+5!#3!CicUQþÐþè^cdjTÈm¾cd\[§®®ýj¸¸eÜ8+lhŠÝò%Ã12KK„KJ¦óü3þL¨ºÿæb&^@PP F'üìüüüü<91@  ‡#Œ'¼'ô<<ô<Äì290@0(P(p(( ( (¿(ß(ÿ( ]%#"&5332765332653#5#"'&®Cb`‚¯¾¹ruSS¹rw¦¹¹=ZXyzVUØy=<öâ¤ýb¢œ`^¤zýb¢œÀ¢zûž°e32>>ºþVb&a@PP F'üìüüüü<91@  ‡#Œ'¼½'ìô<<ô<Äì290@0(P(p(( ( (¿(ß(ÿ( ]%#"&5332765332653##"'&®Cb`‚¯¾¹ruSS¹rw¦¹¹=ZXyzVUØy=<öâ¤ýb¢œ`^¤zýb¢œÀ¢zùôZe32>>ºþV{0c@PP)%'F1üì2üüüì9Ì1@‡ %*!‡*-¸(¼&/<äô<Äì29Ôì0@02P2p22 2 2¿2ß2ÿ2 ]>32+5327654&#"#4'&#"#3>32)EÀ‚¯__RQµþéi&&ru¦¹99wSS¹¹?°yzUV‰|v{zâýHÖ``œ01™²¡œ¾¤ý‡ž¢NM_`£ý‡`®gb>>ÿÛþVk{Q@N O Füì2ä99ôì1@ ‡¸¼/äôÄì9@  ‡ ½ôì990´`Ï]#4&#"+532653>32k¸||•­£µF1iL¹B³uÁƤý\žŸž¾¤ýsÖÀœa™t®edïºþVJ{;@ N  Füì2ôìÜÄ1@ ‡¸ ¼ ©½ /üìäôÄìµ  90&54&#"#3>32;#"þR||•¬¹¹B³uÁÆ&&i1Fµþ¶`Ö²Ÿž¾¤ý‡`®edïèýH™10œ³d` y@B¼NF üìüì991/<ì2990KSXííY"² ]@068HGif€ FIWXeiy…Š•šŸ ]]!3!#³ÝÄþðþ#Ä`üy‡û lü”qÿãu{ ,@© ¹¹¸Œ Q Eüì2ôì21äôìîÜì0"!.265!2#"q“•t•’•¨ý†¨–ðþîðñþïßÁÁü è””èüþÈþìþíþÇ98q$`I@  EüìÔì2ÔÄÄij 991@ ©¼ © /ü<ôü<@© 9Ð/ì0!!!!! '&76!#";ýÔýë:üáþ»§¨¨§E*%ðxxxxð%`šþÝ›þ”œŽŽ‚lkØÙlm”ÿÜ>|$2@ &E E%üìÔüÔìì1@ ‡‡#Œ¸%äô<ì2ìÄ02765 26= "&'"&H`k&IÐþnÊI&k`þB"F:.a×¢ Öהģµ0[1þÐþð[0µ£ÄÈT\þ¤þ€l²6p£uyŸíÿÿpþVÑaÿå`/@   /Üì2991@ ‡Œ ¼ /ìôìÄÔÌ90%!"/32653#×rþù%832JI,œ§¹¹:º….´˾ü ®fcºþVJ{:@ ‡ ½‡¸¼ FüÄì2Ä1äôìÄÔÌôì90´PŸ].#";#"&53>32JI,§Liéþµ£¹:º….´˾ýž™aœÀÖt®fc„~{%@ ÜÌì2Ì1@  ‡ ¼‡/ì2üì9903!5346;#"à¤þ¤£µþéiLåý¿¤¤AÖÀœat~{%@ ÜÌÌüÌ1@  ‡ ¼‡/ì2üì9903!534&+532Ê´ýð¤Liéþµ£åý¿¤¤A™aœÀº—`°@4  B© © ¼     Füì2ÜÄì99991/<ôìÔì9990KSXíí9Y"²@]@Bz%%%&'&&& 66FFhuuwˆˆ˜˜]]#.+#!232654&#0s2®Ã¢J{Q©¹„ÚÖpýõÆwu t]þ΀:þ'`ž¥iŒþ¯VNM`º—`E@  Füì2ÜÄì99991@  ©©¼/ä2ìÔì9990332673#!32654&#º¹©Q{J¢Ã®2s0jpÖÚþ|¹Æuw`þ':€þÎ]t Œi¥žìþ¯`MNVoþVÇ{0€@C  S('  S'('B©½1 '(†‰‰!¹¹.¸Œ1' ($R$+E1üÄìÔìä9999ìÌ1äôìþõõî9üì0KSXí9í9Y"²0].#"#"/;#"&=32654&/.54632‹N¨Z‰‰b”?Ä¥÷ØdXLiéþµ£ÌÁ‚Œe«@«˜àÎf´?®((TT@I!*™‰œ¶ ™aœÀÖûjYQKP%$•‚ž¬ÿÙþV×4@ O Fü<ì22ä991@ ‡½ ‡—üì99ôì046;#"+5326¾®½®°cM£µF1iK»«™Phû)ÖÀœaÿÙþV× O@ !O F!ü<<ì22ä99Ì2Ä1@©¼! ©‡½! ‡—!üì99ôìÔ<ì2ôì0546;#"3#+53265#53#5¾®½®°cM¢££µF1iK´´¯`N»«™Phý¨¤þ(ÖÀœa™ؤi7þV5e"¶O ÔÜüì1@ ‡½‡¼ôìôì04&+532;#"&—Mc°®½®Ki1Fµ£(hP™«»üí™aœÀþòþV×2@   O ü2ü2ÌÌÔÌ1@ ‡‡— /<ôìì2ÔÌ0!3## 54!346;#"#"3276w·¿5RµþѾ®½®°cMów|n!›o@`ØÒ®»«™Phú£3A07þÀò^3@   /<Ô<ì2991@ © ¼©/<ì2ôìÄ990²¯]!5!4&+5323#²þ…{Ks½½Õ¢‡‡þÀ>`‰NšŸÒý þÂ7þVöž=@©¼‡ ½  Fü<Äü<Ä2991üìô<Äì2990²¯]!!;#"&5#53w{þ…LiÊൣ‡‡žþÂü™aœÀÖå>ÿã`C@ ‡©  Œ¼  NFüì2ôì221/ä2ôÄÜ22ì22ü0´` Ï ]3!33##5#"&=#5!326®¸:¸ºº¸C±uÁÈ®ŸýÇ||²høþøþ¤þ<¬fcðç ¤¥ŸŸºqÿâ„`S@ !  üÄüÄÔÄÌüÄ1@ ©¼ ¹Œ ôìü<ì2220@ààïïÐÐßß@@OO ]##"5467#5!32654&'7!„ìaþäááþäaíºfˆ°°ˆf´¼Hë˜ëþÜ$ë˜ëH¤ÜB׋ŸÂŸ‹×BÜÁ\b,@   FüüÄÄÄÔì91@ ¹¹¸ ¼/ìôìì0+"'&53732654'&'5\Ž‘ÞFµRQ¸&(g3°JInhX§3"î’``ÖÊý6™12Äžèec–0[«s=`h@'B¿ÔK° TX¹@8YK°TK°T[X¹ÿÀ8YÄ91/ì290KSXííííY"33# =¤ú¤Ãþ¢þ¢`û ¬üTV5` ï@   ÔK° TK°T[K°T[K°T[K° T[X¹@8YK° TK° T[K°T[X¹ÿÀ8YÌ91@  ¿/<<ì290@0U U U U   BKSXííííííííY"@ÿ - : FFI O TTZ _ aai vpvpˆ–—› ³³ÁÈÀ       %%#'!%""% ' $ ! #966 90FHF@B@@@ D D D @@VVVPQRRP S T U cdejejjjn a gouuy}x}zzx y  { v }‡ˆ——”“œ›˜˜™ @/–Ÿ¦¦¤¤««©©« ¤¯µ±½»¸ ¿ÄÃÌÊy]]!# # #335¸æåÙæå¸%ÙñòÙjü–jü–`üj–=F´ÔÔÄÄ1µ©—/<ôì0· ü<<<¶  9@   ìì3676;#"# =ìPHJ|“lL*./!ÅÃþ¡þ£ÒÇ>=š$#‡^û²lü”fk`8@   ÔÜüÔÄ91³¼/ä20@ ììì3 3#fÙ%.Ùþ]Ë`þ8ÈýþðXþV¿`ª@"B © ½©¼© OÜK° TK° T[X¹ÿÀ8YK°TX¹@8YÄ2Ä99ì1/ìôìüì0KSXííY"@B&GI + 690@@E@@CWY_``f``b€¯]]!!;#"&=!5!qjýL´Li/Fµ£ý7´ýe`¨üÛ§™aœÀÖ¨%Xÿ‘Û`!±@  "ÜKTK° T[X¹ÿÀ8YK°TX¹@8YÄ2Ä99ìÜ<ì21@ ©¼¹ ©/ü<ÜìÌôì0@ BKSXííY"@:&GI #+ #690#@@ECWY_#``fb#€#¯#]]!367632+#47!5!3254qjýL¯"TA`„:&>R~i™þ–´ýeÿ8ŠFX`¨üÛ¢G7W9W`/=3<;4¨%üÆ6]XþL/` ©@ "!ÜÔÄÔÄì̲91@B©!©† “¹ ½¼!äüìôìì9/ìì³ 990@ ìì@  **8;ILT[q ]@  %$ 5 7E FT \ ]@    ]2!"'&'5327654'&+5!5!`¾qÿãÅ|/=@1 %,%E0üìÔìÄÔìÄ1@¹¸0 © 0¹"Œ0ôìÔìôì²( 90";#"327654'&% !"$5467&'&5476EwEFDCt›”‰HNTU—Òhcc`þöa——þÖþÆîþþŠ|p<:!ÖÀœa™þå>>ãÍÍãÿÿº`ÉþòþV.9@ Fü<ü<ÜÄÜì991@  ½¼± /<Ì2ääüÄ03#33## 54!3#"3276Á¸¸¸µ½7K¼þÑÁ¸u_+éËû ‹xG`ÝÍ‹BA0 ºþLœ` ## 33œ¹ýÛëRý•ð9¹þL—þ TýÝ#ºñ`@ ¿©FüüÌÄ1/ìì03!!º¸üÉ`ü3“qþVø $C@¹  ¹¸Œ#½%‡—% "GE%üìôì221üìääôÄìÆî0@ `&€& &à&]32654&#"#"32546;#"#/§’’¨¨’’§s:±|ËÿÿË|±:£µþçiM¹/ËççËËççý®daDDadÆÖÀœa™ùÚX¥$L@ & %ÜÜ<ü<Üìij#ÌÌ1@ † $©! ¹—©/ÔäôìÜ<ì2KPXÜÔYì032765&'&#"56763 3###53T?ÀV:9c³PONNLQQUŠmlpªççÊåärLb…Ar+¬#}ºªswÔ¤þì¤X¥$M@ &"#E%üìÜ<ü<Üij "#ÌÌ1@† ©$!#¹ —#©#/ÔäôìÜ<ì2KPXÜÔYì0535&'&5476!2&'&#";3##÷çªpln‰UQQLNONP³c9:VÀ>ååʤÔwsªº}#¬+rA…bLrþš¤þìqÿãà &) 76'& %3!!!+5#"'&7632/ST$TTTTþÜT ¸iýL´ü—ž:XY|Ë€€Ë|YXòšúþjtsst–tssH^þL¨üÛ“¨d01¢¢¢¢10MüùqþL4@#5#"'&76323!2!"'&'5327654'&+5 76'& Z¸:XY|Ë€€Ë|YX:¸jþej€bVQþÐþè^cdjTÈm¾cd\]¥®®ú:ST$TTTTþÜTÍü3¨d01¢¢¢¢10d^þL¨þ$8*mhŠÝò%Ã12KK„KJ¦óÓþjtsst–tssqÿ‘ 3: 76'& %%!332!##47!#5#"'&763233254#/ST$TTTTþÜTØ™ýghýL¯<Ûãþ²)™þ–:XY|Ë€€Ë|YX:¸ý®FXúþjtsst–tss_üú“¨üÛ ö½3<;4¨d01¢¢¢¢10d^ú6[7ÿã@žF.#"#"'&'#"'&5#533!!;5327654'&/&'&54632N¨Z‰ED11”?ÆQR|{ØZa]g½ÕQQ‡‡¹{þ…%&s³fcca‚FF3,±@«LLàÎf´?®((**T@%$!,KL‰œ[[!&POÒ`>þÂý ‰''M5-,QK($)$JK‚ž¬7þV&/!05476;#"+53276=#"'&5#53!3wxWQî°c&'QRµF1i&&½ÕQQ‡‡3þˆ%&sžþÂNµ[V™((hû)Ö``œ01™POÒ`>úü¢•ý ‰''7ÿpÉž-9D!6!2&'&#"63 #"'47!"'&5#533276'&#"&57!3w{•ÿUQQLNONP³cccOÎ+eKTI™þëÕQQ‡‡;BS_ƒr(þÖ€‹þ™%&sžþÂz•#¬+qrþfr «öv)2LOAPOÒ`>úù 'KV ø›ý ‰''/þVo5+5327654&#"#!##535476;#"!;67632oRQµþéi&&||•¬¹þÓ¹°°WW½®°c'&-·BYZuÁcc¤ýHÖ``œ01™²Ÿž¾¤ý‡Ñü/ÑN»UV™((hc®e22wxÁÿãÉ#5.#"#"'&'#34632327654'&/&'&N¨Z‰DE11”?ÆPS{|ØZb]h¸¸àÎf´ý8b_ca‚FF2-±@«LL?®((**T@%$!,KL‰œ[[!&ý¬þÔý²2-,QK($)$JKÁâ Ž@   Füü<ÔÄ2991@ B ©¼©—/ììôì0KSX@  ííY@B &GI   + 09 @@@@@C EWY `````b f¯¿ß]]3!!!+Á¸iýL´ü—ž¸šþL¨üÛ“Íüù6ÿâé 333# #333# #6t‘‰‘t¹ˆ˜™ˆ¹t‘‰‘t¹ˆ˜™ˆUþéþéýýþ=þéþéýýþ6­é3@    Ü<ì2Ô<ì2Ä1@  ©©ÔüÌ2ÔüÌ20!#!#!#!#6³ýk³ýkUþXþûrþXþûþJ3@ NF Ôüìôì2Ì1@‡Œ ‡ —¼½ììôìôÌì0%#"&54&+53232653#׃çÁÆLi1Fµ£ø•¬¹¹¬ÉïèÄ™aœÀÖýBþþ¤yùìþVv!:@ #NF "Ôüìôì2ÌÌ1@‡Œ" ‡ —"‡½¼"ìüìôìôÌì0%#"&54&+53232653;#"&'׃çÁÆLi1Fµ£ø•¬¹Ph2F¶ž¬ÉïèÄ™aœÀÖýBþþ¤yûŽ”fœ¹ÝuœÄ0@ ÔÜ32ÄtNN^luu)qJy}þ…wYYk\þžgþ«88†uœÄ:K°SX@ ÔÜ<Ì/ÌÌK° QX±@8Y1²]@ Ô<ÔÌÔÌ0Y#4&#"#46;#">32ÄtNN^lugr ’B0)qJy}þ…wYYk\þž„xkW6Vr88†ÿé­î #@Ü<ÌÜ<Ì1@ÔÔÌÜÌ03+5327653#zt43r,Bttý‚x66XVr‚uœ@ ÜÌÌ1@ /ÄÔÌÔÌ0.#"#3>32.biuu$uT ¯ qkþ¶sa97Hä´ ÜÜ<Ì1µ /ÌÔÌÔÌ032653#5#"&'H.bitt$uT ü qkJýa97H­u'¶ ÜÜ<ÜÜ1@  /<ÌÔÌÔÌÜÌ032653;#"&=#"&'H.bit0B,rg$uT ü qkJý V6Xlx a97 œ!+33276?3327654'&+›CFCD‰ôtk=%%(f{n!!"þ×}K'))'K}éN;[--sþ÷?¡«5ÿ/.6œé 333# #6t‘‰‘t¹ˆ˜™ˆþéþéýýþ&­Õ+53276?33Ÿ1/.N]D0 þâ{ÝÜ{bp"#WK/iþêÿÿ tŸf ÿÿ tf&  tÿÿ®éÓÕ ¢ÿÿ²þ×Õ £ÄîéÚ@ žÜüÔÌ1Ôì0#5—R¤Ú¬þÀ@¬uï‡452654DŽu@XX@sPOOPï=>X@?X=>POæPPuï‡"'&4763"3‡sPOOPs@XX@ïPPæOP>=X?@X>uœ‰32765&'&#"567632#'y7$#?q22110335®WDDFk€[@*7K$@ ` XFh_@Cþ“uœ‰#&'&547632&'&#"3êkGDEW­53301212q>$%6y[þAmC@_hFX ` @$K7*@ 2õ°% % êþ™gþ»õ‹´´‹õ 2õ°5-5õþgþ™ê'õ‹´´‹õÁ|?f=´uÜì91µ³ôÌ2´]90K° TK°T[X½ÿÀ@878Y3# #¶”õ‹´´‹fþgþ™Á|?fL´uÜì91µ³ô<Ô²]90K° TK°T[X½ÿÀ@878Y@ 5:5:3]]33¶õ‹´´‹õ|êþ™gþÁî?f7@ ´³uÜì91ôì290K° TK°T[X½ÿÀ@878Y3#'#¶”õ‹´´‹fþˆõõÁî?f7@ ´³uÜì91ô<ì90K° TK°T[X½ÿÀ@878Y373¶õ‹´´‹õîxõõþˆÖç^@ÜÜÌ1@/Ä0#^ˆýÕ+ÿÿÕb+öqsîRf3#‹Çþº™fþˆªð‰f #o™þºfþŠvÖþÑ^ü@ÜÜÌ1@/Ä0%#^ˆüýÕ+ÿÿÕþÀ+ÿTqù^ªþ‰ÿ’#o™þºnþŠvsþRÿ”3#‹Çþº™lþˆoÔ#E@ Ô<ÄK°PX³@@88Y2991/ÄÔÄ0@ `o]@ P```dd]!!!²e³³þ›ÙJý'þ¶oÙÔ#4¶ÔÄK°PX³@@88Y91ÔÄ0@`]@ P``d]!!²eÙJÿÿuþ‡#€þÿÿuþ‡#þÔá–!533áþ>––j––,þÔÔá–5!##––––þÔ,Á‰ß 533##5#5¢”áá”áþáá”áá”já!5!áþ>Âj–Ç)9H W@ ð³VVÜìÔì1ô<Ôì0K° TX½ÿÀ@878YK°TK°T[K°T[X½@ÿÀ878Y332673#"&Çv aWV` v ž‘‘žHKKJLšDfN@ ÎÍdÔì1üì0K° TK° T[X½@ÿÀ878Y° KT°KT[X½@ÿÀ878Y3#šÌÌÌîá  @Áò ÁñV xVÔìôì1ôìôì04&#"3267#"&54632˜X@AWWA@XzŸssŸŸssŸô?XW@AWX@s  ssŸŸLþuÁ @  óô 'ÔìÄÔÌ1/üüÄ90!33267#"&546¸w-+76 >&Dzs5=X..… W]0i¶J7c@$  Ãà íVwVvôìüì99991ü<üÔ<ì99990K° TK° T[X½ÿÀ@878Y'.#"#>3232673#"&ü9! &$}f[&@%9! &$}f[&@Z7IR‡“!7IR‡“ðî®fB@´³ÔÜÔÌ991ô<ì20K° TK°T[X½ÿÀ@878Y3#3#ü²ø‡ªß‰fþˆxþˆÿÿÞ­%3;#"'&5¸&&i+@¯WRþëd¤”™10œ`ZÈ¢ïîf '#7'373\\]´µµ´]\´¶¶îaa»½``½»u«ý # 5473733254²òþõµú‰¿·‰þ¼‰ˆÜù´„„¿ò/ààþŠ®˜MM‰zœî ±/Ì1±/Ä03#zttü™uœ/2&'&#"#"'&'532654'&/&'&547632j1549W++](}24NMˆ9>=D@?>=RX o(l00GF‚@99 a /$*+MW33 k2-*)*IX01 uœ! #'#37ÿ ‰Í͉û‰»»þÏþ¾ööH+ßßuœ‰#&'&547632&'&#"3êkGDEW­53301212q>$%6y[þAmC@_hFX ` @$K7*@ÖX@ ©ÄÔì1/Ôì0!!5!•þAGЈú¨ÖX'@??//©ÄÔì21/ÔÜì]0!!5!3•þA¿ˆœˆ4ú¨ÖX@ ©ÄÔì21/ÔÜì0!!5!3•þA¿ˆhˆhú¨ÖX'@pp00©ÄÔì21/ÔÜì]0!!5!3•þA¿ˆ4ˆœú¨ÖX@ ©ÄÔì1/ìÄ0%3!5•ˆý¹ˆÐú¨ˆÿÿÁýì?ÿÖ‡ùpÿÿÕâ+v'q€q€îëf3#3#––@––fþˆxþˆÿÿîý.ÿSœøM¶þvJÿ!'&'&#"#67632327673#"&ü9 &}33[&@%9 &}33[&@þ³7 %$R‡IJ!7 %$R‡IJü¨ðþ‡f6@ ´³¸DÜì91äôì0K° TK°T[X½ÿÀ@878Y # ým™þºXfþŠvþýqîÿPf6@ ´³¸DÔì91äôì0K° TK°T[X½ÿÀ@878Y3#þ‰Çþº™fþˆsüÁîÿ?f<@´³¸uÜì991äôì290K° TK°T[X½ÿÀ@878Y3#'#ý¶”õ‹´´‹?fþˆõõsÿÿü´ÿH7žûþÿÿüÙbÿ/öqüÿÿûì|  Æüÿÿü¿)ÿ1Hšûøý˜+þd%@ ¾±¸Ôì91äüì0@4D]3#ý˜ÌÌhå°ü×Fÿ)I@Î͸ dd ÜüÔ9ì1äü<ì20@#4D`````````ppppp]3#%3#þ^ËËþyËË)ÊÊÊËý7òþ÷{"@ V@€ V /ÌìÜì1@Á@À /Üì0632#546?654&#"ý7pihX,#w3-.>GZdH3UC=A   (6%""($4ÿÿüìáÿœûþÿÿüôîÿ²fŸüüÅîÿCf<@´³¸uÜì991äô<ì90K° TK°T[X½ÿÀ@878Y373ýºõ‹´´‹õNîxõõþˆsý¼ìþD¨²/ì1±/Ì0#þDˆ¨þD¼ÿÿüðìÿ¨'¾ÿ4¾Ìü]îÿfB@´³ÔÜÔÌ991ô<ì20K° TK°T[X½ÿÀ@878Y#!#ý͇ø¾‰ßfþˆxþˆxÿÿü¿)ÿ1Ö'›üÆšûøü¿)ÿ1H µVV/ìÔì1´ ð/<Üì0#.#"#> Ïv aWV` v ž"ž)KKJLþéÿD( @ÔÌ0#3þòÓ¤é?ýÂþ‚Á#55#53þ‚òppòÃþÿ{‰þýÂþ‚Á53#7"ýñopòÃþþ‰{ÿyš‡3#@Çu™þˆÿÿü¨ýßþ‡ÿUCûþøïÿÿýqýÝÿPÿUvûþøïý ý&þDÿ53#5#5ý¼ˆˆ°þ_±þ±ˆý¼ý&þôÿ3##3þD°°ˆˆþ_ˆ±êþU†?p!5!#Iþžêˆèˆþþðk{à1@V/K° TK° T[K°T[X¹@8YÜ2Üì1@ Á/ÄüÌ0532654&'3#"&þð=X..… W]0i×w-+76 >&Dzs5ý€þþVÿ¾ @  VÔüÌ21@Á Á/üÜì0"&5463"3þVZ||Z(55(þ}ZY|x5'(5ý ý×þõÿ3!5353þD±þ±ˆþ_ˆˆ±ý ý×þõÿ#5!##ý¼±걈þˆˆˆ±ý ý&þõÿ #53533##ý¼±±ˆ±±ˆý׈±±ˆ±ý þˆþõÿ5!ý êþˆˆˆýzþVÿЀ '+53276=0RQµþéi&&€”Ö``œ01™”ýwþVÿÍ€ %3;#"'&5ýw¸&&iéþµQR€”™10œ``Öÿÿýšþ‡þfÿS›üùCÿÿüÕþ‰ÿ'ÿSjûþùCý(þþÔÿ¾ @V xVÔìôì1@Á Á/üÜì04&#"3267#"&54632þ[6'(55('6y|ZZ||ZZ|þê&65'(56&Z}}ZY||ýjþþÿT @ÔÌ03#ý¼Ó¤¬þÀÿÿý#þuþÁzüÿÿýLþuþÁüý¼ý˜þDÿT²/ì1±/Ì0#þDˆ¬þD¼üðþ ÿÿT#5!#é‰þ뉬þ¶ÂÂJücþ9ÿ˜ÿX#"4533273273"ýþ Ïv aWV` v ž"žþ6KKJLÿÿü´þ9ÿHÿSžûþùÿÿüÙþÀÿ/ÿTüÿÿûìþþ¬Büÿÿûìþÿî ¡üûŒìÿ­ #"'&'.#"5>32326SKOZq Mg3OIN’S5dJ t]F‰ ®;73 !;?®<6 7=ýxÄÿˆh!5xýðh¤¤úíÄÿÿh5!úíĤ¤ûhÿ¢ÿ¼¼'ûÈ`õ_^NÌOúÿºÿ‘'úygfFXYý¬þþ‚ÿ¾ @  VÔüÌ21@Á Á/üÜì02#52654&#ý¬Z||Z(55(B}ZY|x5'(5üñþ[ÿÿ¥3!53üñ‰‰þ[JÂÂþ¶üðý.ÿÿT!!éýÙžþë¬ýÚ&þbþêücþ9ÿ˜ÿX632#&#"#&'"#72ýþ;tÚv gfv ifvÜtúRþæ––•–ý+óþå­ '7'77þ}`}}`}}`}}`p}`}}`}}`}}`þÂÿ Ò.54675>54'&'þC!“‡RI 7!“‡RI Ç0PQn +0PQn ÿÿûì: ' Æü Æüþ¾ÿÿü¨ðþ‡fCûþÿÿýqîÿPfvûþÿÿü´ÿH7žûþÿÿýÂþ‚ÁÄÿÿüçFÿbÒüÿÿýÆþVþ¢ÿ¤üüÕÿ+I#5!#!Õ–þÖ–V––,ýþ2þáÿ¸!5!5!5!þáþ>Âþ>Âþ2x–xýþþëÿŒ3#3#ý––@––tþˆxþˆýþþáÿÖ!#!ý–þÔ*þ>,ü¶îÿJf'73327673#"'&'#7&'&#"#67632ýÿBmk  &}33[& !Bnk  &}33[& ÿg©  $%R‡JI g¨ $%R‡JI ü¶‰ÿJÌ!%'.#"#4632326=3#"&3#3#ýü9 $(}gV$=09" (}gT";è––Ò––h! 2-ev 3)dw.–C–ÿÿü¶ÅÿJ"Ê·ʧücþ(ÿÿ 7!#'73!'3þp„þ„¯¯„ç„~¯¯þ(››ÍÍ››ÍÍý3þþÍÿ¤#557'þ2d›ÍÍ›þç„~¯¯~„ýxáþˆ&'&4767þˆ@*,,*@rNPPNr‹*,@A++{OPæPNÿÿü¿ÿ1Ø'¸åÿÿý+ýUþåÿîøbýxáþˆ050567654'&ýxrNPPNr@*,,*‹{NPæPO{++A@,*ÿÿÿ.Dÿú›ý”ÿÿüpþÿ… ÇüÉÿÿüpkÕ ÇüÉPüp×k!!üp øàk”üpþÀÿT!!üp øà¬”ý* Ö'#'&'&#"#67632327673#"'&O,$e5F¢qpÈ[?9ZO,$a9G¢qpÈ[?9J7  $0G‡JI "7  $,K‡JI ÿÿüpn ÈüÉüwþ(“ÿÂ5!'3#7üws„~¯¯~„þÃd›ÍÍ› tŸf@ÔÌ1ÔÌ03 A¾ntòþ þVŸH@ÔÌ1ÔÌ0%#ŸA¾nHþò¶þV’ÿ¤ #"'&=3;’”• #þV!.åå ÿÿÿãõ{ÿÿqÿãç{'yOÿ„Fÿÿÿãõ{'yŽÿ„ÿÿžÿÃ#sîRf1@ ´³DÔì1ôì0K° TK°T[X½ÿÀ@878Y3#‹Çþº™fþˆÿÿ×FRÒ&jl@¯Ÿ_]@_q0ÿÿhf'þÚÿÿÛH®Fyÿÿÿçuf'þt#êÿÿÿóf'þ€%äÿÿÿí}f'þz'êÿÿÿòÿãf'þ-(ÿÿÿá‘f'þn2ªÿÿÿÛf'þh66ÿÿ€Ò'ÿ.FÿÿhÕ$ÿÿÉìÕ%ÉjÕ@ •üüÌÄ1/ôì03!!É¡ý)ÕªúÕhÕ=@ B••ÔÄÄ1/ìôì0KSX@ììììY !3¼þf5û¹:å9ûš¨Õú+ÿÿÉ‹Õ(ÿÿ\Õ=ÿÿÉ;Õ+sÿãÙð!2@•‘"•­" •Œ"#3"üìÄôìÄì1ôìôìôì0!!"3276'&' ! '&76ÅÂý>bÜ‚ÜÜ€€Ü:x¼¼þÆþż½½¼pª†¤¤þåþæþ¸¤¤¤¤¤þ[þžþŸÒÓÒÒbbÓÒÿÿÉ“Õ,ÿÿÉjÕ.hÕ<@ B•ÔÄÄ1/<ôì0KSX@ììììY3#3#åÕ:å9Òþ&Õú+ÿÿÉÕ0ÿÿÉ3Õ1ÉbÕ *@• •­•   ü<ÄÔ<Äì1/ìôìôì0!!!!!!2Çý9i™üg™ügqªªûªÿÿsÿãÙð2ÿÿÉ;Õ«ÿÿÉÕ3É‹Õ F@B ••  ü<Ô<Ì1/ì2ôì20KSX@   ììììY%!!5 5!!±Úü>ßþ!°ý8ߪªªpªªýóÿÿÿúéÕ7ÿÿÿüçÕ<sÙÕ'<@) !%(üìÔ<<ü<<ÔìÄ1@•' •%'/Ô<ü<ôÔ<ü<0367654'&'&'&76753#–b‚‚b–Ê–b€€b–Êôž½½õÊô¼¼ôÊŽWsÆÅsWWsÅÆsWü† ¡‡ŸŸ†¡þñþò¡†ÿÿ=;Õ;sÛÕ.@ •ÜìÔ<ü<ÔìÌ1/ä22Ü<ì20!6'"'&336763#ÂÖ¼¸Õ‚nŠÊŠn‚Õ¸¼Ö†°ÒÌh™þgþ椌ñüŒ¤™þgþ˜ÌÒHîNÏç&3@ •‘&•("3'üÄüÄôÄìüÄ1/<ì222ôì0%!567654'&#"!5!&'&576! Ïý¨±cc„„ØØþ÷cd²ý¨?žIHÀ¿1/ÁÀGG¡²²²a¦¦Êð‘‘þÝïʦ¦a²²‹••¸>ÅÅÅÄþË””ÿÿXN'È/u'´ +1ÿÿÿüçN'Èqu2´ +1ÿÿqÿçäf&>nÿÿ…ÿãÈf&PBÿÿºþVdf'ÆDÿÿ¦˜f&FÿFÿÿ•ÿâ*Ò&Rqÿçäy *È@ ,%E+üìÔÄÄ99@ ?/]q@ ¹)¸ ¹!Œ¼‡/ìäôìôì99@<ììì<ìì10@ „†ˆ‰ €]@IIIJN LNIK ]@:9:88? <>]@ + +*))]@  ]@++]'&#"3273;#"'&'#"'&763 N,-²†=MKLy†H¤c¤Í( #) Xn^T).^,ëruÆ7 çínжÜikÕç%ý¡Û1)0œT*XoW)˜&ŠšÀþVˆ!7@E Füì2ÄÔìäÔì1@¹ ¹¹ Œ½ìôìÔü9Ôì0%#! !"3 5 4# y¹ª²¬þÔYoÅ þ0kêþûEþÈþþîdZþõþ&J­:ª@ÛþÈ þV`@@ ÔÔüÔÄ1@ ¿½/<ìä20@ ìì99ìì3#&+532iõ^Ãþ;¸þÚ,_1FŰýLdû þVªD~žqÿãuð-T@(/E( Q!E.üìÔìô² ]ìÄì99@%¹%¹Œ.ôìÔüÔÌ99@Sì910&#"#"'&4767&5!232654'&'&ìfïýÐuމ‰ð‰5Kœ¹ÝxþD7VUV•“¬[a~@Fu\0%p‡þëþ÷œœÌ¥@$OFþ(IqÌËrsè¾Ç`g …ÿãÈ|2=@" † ˆ¹¸3©3'†(ˆ#¹,Œ34 '0E3üìÔìÔÄÄÄ1ôìôìÔìôìôì0&'&547632&'&#";#"32767#"'&546‹p<@ ÔìÔÔÔÄüÌK°QX¹@8Y1@¹ ¹Œ‡—ôì2ôìÜì0%#457654'&# !5!Ê„OTJP£E* :ý¢;ýìfý,KOxsPWKL,#%5,*3ìY¹¹þ”þ'þiºþVd{1@‡¸ ¼½ FN  Füì2ôìì1/ìäôÄì0@ €]#4&#"#367632d¸||•¬¹¹BYZuÁcc¤û²HŸž¾¤ý‡`®e22wxqÿéu$!O@ ¹—"•"¹Œ"#E QE"üì2ô²]ì2ì1ôìÔ@?]ìôì0@ w #€#]!3276'&#"2#"'&76±ýƒEV•–SI 6V“™Q@=ð‰‰‰‰ðñˆ‰‰ˆÆþÕœŠÉdžœ~þü´ÔÓþŠþ‹ÔÕÕÔuvÓÔ¦n` @ FüüÔÄ1@ ¼‡/ìä0;#"'&5c"$lYo´RR`ý+‘.0œ`bÔÊ¿…` I@   F üì2ÄÔÄ1@¼ /<ä20@   ìì <<ìì33 ##¿¾ãàþGþáþb‰¾`þ/ÑþZýFBþ?= F@ ÔÄÔÄ1@ ‡ —/<ôì0@ì ììììì # #'&+5zÃþÆþ~ÃëJ/k`uâeú›<üÄ2Æ~žÿÿ®þVå`wJ`B@ÔÄÔìÄ1@ ¼/ä20@ì ì9ì9!367676'&'31 þªÆ!xdLjºE.*ˆ±{`üT|¬p5dwƒY|rNįätkþR&@@ (" %'ÔìÔìÔÔÄÔÄüÌ1@¹ ¹Œ'#‡"‡—'ôü<Ôì9ôìÜì0%#457654'&# %$47#5! $Ú„OTJP£E* :ý‘MþèÜÐý‹ýÆKOxsPWKL,#%5,*µ,X$ÅR¹¹þÝ¿ ªþ¼þñÿÿqÿãu{RJÿÙ˜`/@  ÜÄìÔìÄÄÌ1@ ‡Œ‡¼/ôì22ôì0!#3267#"&5!##J117,#J%x\þc¼`¸ýPH?… ƒ°œüX¨ºþV¤{1@¹¸¹ Œ½EQ Füììô²]ìì1ìôìôì067632#"&'#44&#"326=—;¶ÌÿÿÌ{±:¹+§’’§§’’§˜fZ#þ¼þøþøþ¼adý®ÏçÝËççËËççqþRç{$6@ !& HE%üìôÌÔüÄ1@¹¹Œ% † ˆ ¹¸%ôüôìôìÜì0 !2.#"32#457654'&¨þóþÖ-U¢LNP³ÆÆ¯ƒPTJP£E* >:##¬++ãÍÍãLOxsPWKL,#%5,*qÿãÖ` 1@  QEüìô²]ìÔÄ1@ ¹Œ‡¹¼ôììôì0"32654'&'!##"'&76s˜RV«•“¬VOšcÎm‰‰ðñˆ‰‰qÎns¾ÉçèÈ·zn’¸œÝþ휜›dm`#@  ÔÄüÄÄÄ1@ ‡ ¼¹/ìôì20%;#"'&5!5!!æ$lYo´RRþ\ þWÌ0œ`bÔ¸¸ýã‘•ÿâ*`+@ E FüìÔìä@?? ?]1@ ¼¹/ìô<0327676'&'31'"'&5R27k–i;jºF-*€œþ³eb`ý+‡@EÐv»f€wƒZ{sšý»äxvÅÊpþVÑh )=@+E(#E*üìÔ<Ìü<Ôìì1@¹ ¸*'‡Œ½*ìô<ì2ô<ì20"27654'&'2##"'&7673=A__UVF6Œ‰‰Ë·Ç†ˆˆf¦B:VVMpË‘ýRh]ßÐp[„þÙþñ¡˜þn‘™œ’m£Ns¾Êsg¯.;þUda®@    Ô<Ô<9±Ä1@ © ¼©½ü<ìü<ì90°%KSX@ ìììì´ ´´ ´´9´9´  9´ 9Y#&+53;'$Ü•þÍÙ²¶1š1FA”3ÙþN¶1š1FþþúýÐ×~ž§þ0üèþ)~žpþVÑ`6@   ÜìÔ<ü<ÔìÌ1@ ¼ ‡Œ½ìô<ì2ô<<0&'&53367653#EçkƒºUJ|·ƒCUºƒvÜ·%awó‰ý~·LBÕü,BT¯ýxünc#þn‡ÿã'`8@E  EüÜììÔüÔììÜì1@ ¼ ‡/<ì2ô<Ä0 433233243! &þa›ÆÞ˪ËÞÆ›þaþð!)Rë@þÀðþOýæ±ð@þÀëý®+þÕÿÿ}&Fjÿ.ÿÿ•ÿâ*&jRÿÿqÿãuf&}Lÿÿ•ÿâ*f&"Rÿÿ‡ÿã'f&VY¦ÿéM% 'G@)E& F(üì2ÔììÔ²?]ì1@ ¹("¹Œ(¹—(ôìôìÔ¶?]ì990267656#" '&76#327>&iPËDyÌz]6îö;~þ‹ox†Ò¤Ú]ûYß:P®W­p=l“Þºlþǧé‚_ÕÕ¨,¾Õâþå¶–þꀰœ-Ñžqÿéu$ 7@ !EE üìÌÜììü<1@‡— ‡ ‡Œ ÌôìÜìôì04'&#" '&4632  ®1B•SxyJþ̃ÓЯÚõþäþ#þõ¼°/¥pö~ª‰ZªZ7ªAi6 Þþdüüþe¶Ò þóþ½BWQß I@ "!ÜÌü9ÜÄ´?@]Ì1@•• •‘/ô<ÌììÌÔì99@ oÏ]0#4''&"562%62#"F R*Ë*R Mw(oUCHk&_*SK…Hþó¥ývŠ¥ H# ª 0þr{C @[)/Bÿÿÿá¡f'þn^PÿÿWQN'ÈÅu^pþVÑ'A@)   $E(üìÔ<<ü<<ÔìÄ1@ ‡¸(‡ Œ— ½(ìäô<ü<ô<ü<02##"'&76327676'&#"üÇ…‰‰…Ƿdžˆˆ†Ç·qMTVMq·qLWULþc™œþíþ휙þo‘™œœ™úwgsÊÈugü¸HguÈÊsgAÿâm`5!#%$! 47)323764A,“Mþaþñ")þøþaM:ü’GÞϪÏÞ¨¸¸Ï¤ý­*þÕR¤ÏѧþOýã®§pþ[Íg9&'&47#"54654'&#"563277632327"'532ùö! ½`È7"7$>Ž9[@[½`È7"7þÜ>Ž9[&ÍF¢]_I¤ ðI5lþ÷|"Oš Šz:6hlà0'þÃ[Ml |"Ošþ÷Šz:6hlýàf$1 1sþXÙðD@!  üìܶ0]ü9ܶ0]ìì1@ • ‘ •½ ìÔ<ìôì0#&'&76!   76'„¸ü ½½¼;:¼¼¼Ÿ{þH‚‚¸€€þp³ÒÄÓÒÒÓþžþŸÒ³I¤¤þåþ椤¤¤¤qþVu{ <@!E E üìܲ0]ü999Üìì1@ ¹¸ ¹Œ½ ìô<ìôì0"32654'&#&'&7632s”VVUV•“¬VV9ª¾k‰‰ˆñð‰jßstþntsèÈÇtuü þn’}œœœþÈþìþíœ{‹þR«Õ$ 32#457654'&# '&76)Fþó`{[mÈzYTJP£E* :þÀŽ•¹Šxe+w˜þÍþµ˜TOxsPWKL,#%5,*ËÖeNí±ªqþRQa#"32#457654'&#"476!¬Ø>cccnƒPTJP£E* 9ÌþÖ—h4­¨HqÀÍrqLOxsPWKL,#%5,*>󼃸ÿÿÉ#Õ)ÿ@þVF'347632&'&#"!!#"'&'53276îˆ`©1213$)),x:KAþ¿‹b­933.1220W@R‚ Žd ¤>QoþÉý?’¥s ¤K_³ÿüÔÕ7"'&76'&526n ê'BƒæQ_šý¬'BƒæQ_‡þ[~ý,`*l#½FR¶Úþ‘`*l#½FR¿ˆ #!3ˆþç¸âý&¸âpüÆMý]rþV`ð!#56! #'#64?!"QªÍhЂÙíÖöáRR_@Ͷ»Ðþ¤0:ýŒIKiÆÏþÎþìýÂþXþ’LÊÑ}/M4ÐýÆ!÷wþ–x#&'#&' #'nï¹d2þF¹b.¼Ó-òà¬t¨`4þÌÀ#Mýа!„¤ØÈþPþßþ‰^ösþKÕ='6767&'&'#"'&46733276=332764''3=DÛ´þìvŒaŠyͤgDd''de’Óy{d;]ùT‡CHI}rHGÇFFt†AGC‡Tú_8dàþïËæ¦ˆd*…0Q€A^­­^^±´˜ÉŸFþ¿Ÿ·þÍkmihÆññÆhimw'·ŸAF‚œþë‡þU(`%!$! 4337643'676ˆþí#þòþa‡ÚßЪÎÞÚ‡9iÅšÒkp[Q‹Ç þò5ë@þÀðþ—Ôþ+hð@þÀëÑ„ôž{I%l +?sþVHð32763## '&6%ì´`…nbÃÄbcÙÙDhhªÿœ¢º—(J„¹þîù«˜˜™ ìø„@?ÕÝG6ÕqþVŒ{ 7653##"'&54763‹šPr^SLTT¸¸:XYÚ…‰žýýcнtstË1ùöRd01¢¨øìÈ¢ÉþKâÕ% '676767654'&#"#3676L'±¾Pe^Ò8*ˆ7D˜ ! ! 12ÀÔ°œˆ.#A“L.#^YŒq4+& "H4B;;=/?"+ŸVhP’OþV ö!! 7654'&#"#676! 3 7úÿþíþ¯þ¹º³llc¤´^#ð,V£¡¢¢þ÷þœÕ)èì¾£ñÛ„œe]’6?¡f‘ñضþòþ…µ³dþVj{ # 7654'&#"#67632327\ÚêþîþÞB§\\T‹™PÌ%I‹îèНþ/‘©ýÅþï™yYÂk}oSKu,2Rœè¤œÅþàºs¶ï5%! &'&#"567632 67632'&#" ;!53276ÚnþÍþ€þÜ"?E! rK,/ 4'Kr !D<&þÒtEGúÃGH ªÊþh=»" » C(FþK#C » "&þEýà !ªª!6Ë{5%! &'&#"56763267632'&#";!53276šÌäþ[9ï6:ƒ@%(ÅÅ(%@ƒ:6ö-:IûkI:ž8þî¤=35þú53þ¸þgžžsÿã%+$67632! '&76!2767&#"327*þÖW8QU{‚õ2þÎþùþΤ£‹|°s• K^—lŽÈº¼hiie¿¼b-sJ V"1þ¼ýðþ¼œ›PÞÒ» '±$üA‚æstÀ½xssqÿã[/&67632#"'&76!27674'&#"3276›ãI,)e[xÌ€€Ûÿ‰ˆtgO_\SG]EZ¨ST’•VXXT—”RS7xJ©F61¢¢ý𢢜›PÞÒ» '§'üøÍrsstËÇxsst,þV·Õ#5!#!#!3·ËþëËþëËàË`þòdú `duþ‹7þUÏ#5####!3­å¹å­’¹_ÿpú†zppþpÿòÍg3#"54654'&#"563277632327#"'$47(`È7"7$>Ž9[@[½`È7"7þÜ>Ž9[@[þã™þ÷|"Oš Šz:6hlà0%þÁ[Ml |"Ošþ÷Šz:6hlà0%?[MºþV¤{$4767632#"'&')! $'&  7ºZ6ž;¶Ì€€Ìx[Y: ôþþ+STþÜTSST$T%Уb^#¢¢ýð¢¢10dþXªÚ4–tsstþjtssÿÿqÿãç{FÿÿÿÛþVyMÿÿsÿãÙðaqÿãØ{!&'&#"!!32?# '&76!2Ø%%cj·f_¥ý[_f·€MJOhkþôœœ en('® c\©©\c§œœ(œœ Äÿã+{!56763 !"/532767!5!&'&#"Ä'(ne œœþôkhOJM€·f_ý[¥_f·jc%®£ œœýØœœ§c\©©\c ÿÿÉÕ ÿÿºþV¤Àÿÿsÿã'ð&ÉÕ ! !###É-}-ÅþËþÄÕþ!ßú+þçúáþV³` !!###þ¹þì™þë¹`þ{…û ²þ` ú¤UþV¤{'4767632#"'&'!!#5#5'&  7ºZ=—;¶Ì€€Ì{XY:åþ¹eSTþÜTSST$TµçŒfZ#¢¢ýð¢¢10dþȪppªô–tsstþjtssÿÿsÿã'ðHÿÿsÿã'ð&…y3ÿÿsÿã'ð'yä‰ÿÿÉ‹k&¡ËîuÿÿÉ‹N&¡Èu…±’—B°“°˜B±€B±B|°°#I°°#Ia°€bh°Fa°F`°’C°`#B°’C°`C°UX°°’C°`C8°°5Y±€B±B°°°°#Ih;°°5°°#I°@PX°°@8°°5°5Yÿúþf¬Õ4@  ÔÌü<ÌÜüÌ1@•°••/ôì2Ôìôì0%+532654&#!#!5!!!2¬ÌäL>†o||þˆËþR‹ýÞhþòôª–Â"Ÿžý9+ªªþFéîÿÿÉjk&ŸÉ®usÿã'ðN@  Üì2Ô<ÌÌ1@•­ ¡ ®• ‘¡®•Œôìôìôìôìôì±I±IPX³@8Y0! ! &! !!! 'Ôþõþ±þz†OÐÓÿþøîüâîFÓŸhgŸŽÕ½þãïªïþäÿÿ‡ÿã¢ð6ÿÿÉ“Õ,ÿÿXN‘ÿÿÿ–þf“Õ-T/Õ3@   ÔÔìÔ<ìÜì1@• ­• •/<ì2ôìüì0!565!32#!% 4&+pþÈþ‘Ù•xêûþðûþLª@£à+¸ýÊýû8ª/¦XþýšÚÝÞÚ¦‹‡ýÝÉÌÕ5@ üì2Ü<ì2Üì1@ • ­ •/<ìä2ü<ì20!!#3!332#4&+326 ý†ÊÊzÊêûþðû6£à࡟Çý9ÕýœdýšÚÞÝÚ·‹‡ý݇ÿú¬Õ,@   ÔÌü<ÌÜì1@ •  • /<ôì2Ôì02#4&#!#!5!!ºÞÉ||þˆËþR‹ýîqéîþfŠŸžý9+ªªþFÿÿɆk&¦ÉîuÿÿÉ3k&¤Ëåuÿÿ#½m'Ïru¯Éþ¿;Õ )@  •  üìÔüÔìì1· • /<ä2ìÌ0)3!3!#­þÊÞÊþªÕúÕ+ú+þ¿ÿÿhÕ$ÉìÕ.@  . üì2ôìÄÌ1@ •• ­•/ìôìôì04&#!!26!!2)£þ¼D£lýNûþùþüýè·‹‡ý݇¨¦þ@ÚÞÝÚÕÿÿÉìÕ%ÉjÕ@ •üüÌÄ1/ôì03!!É¡ý)ÕªúÕeþ¿ÛÕ4@• •ÜìÔìÔÌü<ì1@ • •/ì22Ì2ôì0%!!67!3#!#Ó”þpþ±†&axªªûÞªªÔþ þµD+?x4&úÕþAþ¿ëÿÿÉ‹Õ((vÕ˜@   Ü<ì2ÔÄ991@B  ¯ /<<ì2290KSX@    <<ííííY@ I:I:I:I:I:I:@  <<<<33 # # # 3êʪõýßDÓþþþÊþþþÓDýßõªÕýâý³üxþéþêüÿˆMý‡ÿãšð(?@ * %)ü2ÔìÌÔüÌ1@• ¡®• %¡&®"•‘ Œ)äôìôììôìÔì02#"$'532654&+532654&#"5>Iö8Žƒ‘£þîzþä,™©|¼Ð¹ÃÌÔ³ž£Æ†\ÍqìðѲ|«!ÄæéBÐY+•„•¦wps{MÅ("É3Õ y@ B ¯ 6 üìüì991/<ì2990KSXííY"² ]@068HGif€  FI WX ei y …Š •š Ÿ ]]#!33ÄýjþðÄ–Õú+áûÕûáÿÿÉ3m&¤Ïõu#´ +° KT° KT[°KT[X»@ ÿÀ88Y1É†Õ Y@   üì2ÔÄ991@ B ¯ /<ì290KSX@    <<ííííY3! # #ÉÊÒý¿_ÜýúþïÊÕýâý²üyþéþT:Õ%@   ÔÔìÔìì1@ •• /<ìôì035675!#!TÙ>WxÊþfbª/¤Xþú++¸ýÊþøýÿÿÉÕ0ÿÿÉ;Õ+ÿÿsÿãÙð2É;Õ@• üìÔìì1/<ôì0#!#;Êý"ÊÕú++úÕÕÿÿÉÕ3ÿÿsÿã'ð&ÿÿÿúéÕ7#½Õ>´ ÔÄÔÄ1¶B •/ìì20KSX@   ììììY%+53276?3 3 OûM?w.-þ!ÙsuÙµ2&]ª*jkü”lyjÕ =@!   üìÔ<<ü<<Ôìì1@ ••/Ü<ì2ôÜ<ì203>54&'$%53# ÙææÙËÙääÙËþÃþ©W=Ë=Uþ«þÃË¢ÌÅÅËËÅÅÌü+  -‹‹þÕþõþ÷þÕ²ÿÿ=;Õ;Éþ¿åÕ )@ • üìÔì<üÌ1@ •/ì2Ìô<0)3!33#;ûŽÊÞʪªÕúÕ+úÕþ¯³Õ$@  ÜìÔì2ì1·• ­ /ä2ôì0!!"&533!3èþ_ºÞÉ||xËdéîšþvŸžÇú+ÉÅÕ *@    üìÔüÔìì1@ •/ì2ô<<0%!3!3!3¬OÊùÊOʪ+ú+ÕúÕ+Éþ¿oÕ2@ •  üìÔüÔì<üÌ1@   •/ì22Ìô<<0)3!3!33#ÅùÊOÊOʪªÕúÕ+úÕ+úÕþ<Õ *@••­•  ÌÜì2ÔìÌ1/ìôìôì0!!5!!2#4'&#!!276õþGƒNûþðû6ON£þ¼D¡PO+ªýšÚÞÝÚ·‹DCýÝDCÿÿÉFÕ&¸,³ÉìÕ $@• ­ •  üì2ÔìÌ1/ìäôì04'&#!!2763!2#!ON£þ¼D£NOü²ÊNûþðûýè·‹DCýÝDC¨ýšÚÞÝÚoÿã#ðN@ Ü<ÌÔì2Ì1@•­ ¡® •‘¡®•Œôìôìôìôìôì± I±IPX³@8Y0! 7!5!&! 56! ! 'oÓîüâîþøÿÓÐO†þzþ±þõÔF½ïªï½ÕŽþaþ™þ˜þaÓÿã0ð&8@•‘'•Œ'!•&­$#(  !%$'üì2Ô<ìÔìÌ1/äôìôìôì0"3276'&76! ! '&!#3~Ü‚‚ÜÜ€€üs´´;:¼¼¼¼þÆþÅ´´þÐÊÊL¤¤þåþ椤¤¤¤¤ýóÍÌÒÓþžþŸÒÓÍÍýkÕýjˆÆÕ@@  ÔÄìÔì2ì1@ B• •­ /<ôìôì0KSX·  ììY3!!" &$54$)#!›•’:þÆ’•þí˜dÿÊþòþv'ƒ‡…ûV©×Îàú+wý‰ÿÿ{ÿã-{Dpÿã7):@+E'Q! E*üì2ôììÔÄ1@©—*$¹¹ ¸Œ*äô99ìîôì02#"'&5476$%676"32654&}ðþîðñþö:[;z631-ú~LǂӔ¬«•“¬¬{þÈþìþíþÇ0åw) v¹ ’ ,u™8wœçÉÉçèÈÇéº>` /@ " F!üì2ÔìÌÔì1@ © ©¼© /ìôìÔì0!2654&#32654&#%!2#!r~„„~þúòh„„hþV¶ÅÔljŒçÖþ9þ_ZZ^ÉþÊSJJO“…gy˜r–¤ºÐ`¶FüüÜÌ1´©¼/ôì03!!ºý£`“ü3kþå`4@ © ©ÜìÔìÔÄü<ì1@ © ¼©/ì22Ì2ôì0%!!6765!3#!#»þ}vþØ[(bõ““üt““:ŒþdÜ6(UÓ©Ôü3þRþå®ÿÿqÿã{HFï`˜@   Ü<ì2ÔÄ991@B  ¼ /<<ì2290KSX@    <<ííííY@ I:I:I:I:I:I:@  <<<<33 ##'# 3?·éÖþnÌÅþ‡»·»þ‡ÅÌþnÖé`ýòþQýO6Éþ“mÉýʱ¯ýò…ÿãÈ|(N@ &* )ÜÄÄÔìÌÔì1@ †ˆ¹#¸)©) † ˆ ¹Œ)ôü° KQXüôYìÔìôü° KQXüôYì0#"&'532654&+532654&#"5>32Â|ŠþþîP©ZGª]—©–‰”›t‡‹wG¡abªLÄãx\’l­¶«%%pZXk˜YF@\§]ºy` ?@B ¼ F F üìÔì99ì1/<ä2990KSX@  ììY##3y·ýäì·`û ƒü}`üÿÿºy&ÄššÿÌ#´ +°KT°KT[°KT[X»@ ÿÀ88Y1º‘` Y@  F üì2ÔÄ991@ B ¼ /<ì290KSX@    <<ííííY33 ##º·âþTãÎþsÅ·`ýòþOýQ5Èþ“Ls`$@ F  ÔÔìÔìì1· ©¼© /<ìôì0356765!#!L¶8Dõ¸þ{X^™~±Å·û ÍoþPÂϺO` M@B ¼  F F üìÜìì1/<Äì290KSX@   ííííY! !###º >? ¹þ˸þʹ`ýîû °ý'ÙüPº` '@ ©¼ F F üì2Üì2ì1/<ä2Üì03!3#!#º¹U¹¹ý«¹`þ7Éû ýüÿÿqÿãu{Rº`@©¼ FFüìÔìì1/<ôì0#!#¹ý«¹`û Íü3`ÿÿºþV¤{Sÿÿqÿãç{F<m`¶ ÜÔüÜÌ1µ©¼/ôì20!!#!<1þBµþB`“ü3Íÿÿ=þV`\pþVgÕ (3B@5E)! '.E4üìÔ<<ü<<Ôìì1@,¹¸—41¹$Œ ½4ìô<ì2äô<ì20327&#"#"323>32#"&'4&#"326/‘{brrb{‘à9ƒS§éé§Sƒ9¹9ƒS§éé§Sƒ9à‘{brrb{‘/ëǨ¨Çû<9^N5=L^ýü^LþÃþíþíþËN^ýÇÙëǨýì¨Çÿÿ;y`[ºþå` (@ ©F üìÔ<ìüÌ1·¼ ©/ì2Ìô<0)3!33#ü9¹U¹““`ü3Íü3þR–`;@© ¼FÜìÔì2ì1/ä2Ôì°°#I° °#IRX± ¸ÿÀ°8Y0!!"'&533!3Hþ©™f\¸45h)¸×_V¸þõu;;öû º˜` )@ F  F üìÔüÔìì1· ¼©/ì2ô<<0%!3!3!3Ú¹ú"¹Ù¹“Íû `ü3ͺþå+`2@ ©  FüìÔüÔ<ìüÌ1@  ¼ ©/ì22Ìô<<0)3!3!33#˜ú"¹Ù¹Ú¹““`ü3Íü3Íü3þR>.` ,@ E  ÄÜì2Ôìì1@ © ¼© ©/ìÔìôì02#!!5!!!2654&qÖççÖþ8þ•$þù~ƒƒ—£¨¨¤Í“þ7“þ_ZZ^ÿÿº›`'ó"غ>`%@ E  Füì2Ôìì1·© ¼©/ìäÔì04&#!!263!2#!zƒ~þú~ƒý@¹ÖççÖþ9LZ^þ_nþ7£¨¨¤qÿãç{M@ HÜ<Ìôì21@† ˆ †ˆ¹©» ¹¸Œäôìôîþôîõî± I±IPX³@8Y073267!5!.#"563 !"'qž“ÒýÈ2 ŸÇš¡¦-þÛþÿ½“ÕV«Ú“ißV¬FþÃþñþòþÂHÁÿãL{ :@ E  Füì2Ô<ìÔìì1@¹¸¹Œ©¼/äÔìôìôì0"32654&632#"'##3J”¬«•“¬¬ýqùððþîðñþù и¸ßçÉÉçèÈÇéþ¾þÈþìþíþÇ.øý÷`þAt"`<@  ÔÄìÔì21@ B©¼ © /<Ôìôì0KSX·  ììY;#" .5463!##z€wøøw€þúVtš×Ù¶¹åþ¶S^a\ü뉢¡û Ùþ'ÿÿqÿãk&ÁCZÿÿqÿã&Áj–…±’—B°“°˜B±€B±B|°°#I°#°#Ia°€bh°#Fa°F`°’C°`#B°’C°`C°UX°°’C°`C8°°5Y±€B±B°°#°°#Ih;°#°5°°##I°@PX°#°@8°#°5°5Y/þV?@N F ü<Ìì22ÌôüÌ1@©©‡ —/ìÔÄìÜ<ì2Ôì0#533!!>325654&#"#ß°°¹ýãB²v¶Øþ©×zõ||š§¹Ñ´þLþmedéþêâþY)Œ.ÒПĞþûÿÿºØm&¿v†qÿãç{N@ HEüä2ü2Ì1@†ˆ†ˆ¹ © »¹¸Œäôìôîþôîõî± I± IPX³ @8Y02&#"!!327# ¤¦¡šÇŸ 2ýÈÒ“ž“½þÿþÛ-{F¬Vßi“Ú«VªH>=ÿÿoÿãÇ{VÿÿÁyLÿÿÿôF±ÿÿÿÛþVyML¿`6@!E  ÔÔìÔ<ìÔìì1@© ©©¼©/<ìôììÔì0356765!32#!!%2654&+L¶8DØ«ÖèçÖþ›þšX^x~„„~£™~±Å·þ7£¨¨¤ÍoþPÂÏv_ZZ^þº·`8@E   Füì2Ü<ì2Ôì1@ ©  ©¼ /<ä2Ü<ì2ì032#!!#3!2654&+N«ÖèçÖþ›ýÞ¹¹"\~„„~£`þ7£¨¨¤ýü`þ7Éü3_ZZ^þ/‰:@N Fü<Ìì22ôìÌ1@©‡ —/<ìÔÄìÜ<ì20#533!!>32#4&#"#ß°°¹ýãB³u½Ê¸||˜©¹Ñ´þLþmedêíþÐ*ŸžÁ¡þûÿÿº‘m&Ævoÿÿºyk&ÄC]ÿÿ=þV&Ïš^̺þå` )@ F © F üìÔüÔìì1· ©¼ /<ä2ìÌ0)3!3!#Tþf¹U¹þf“`ü3Íû þåsÿãÕ48@$%6 )  5üÜììÔüÔììÜì1@ $•-/<ì2ô<Ì0"'&46733276=332764''3#"'&':Óy{d;]ùT‡CHI}rHGÇFFt†AGC‡Tú_8d{{Ñ’ed''de±´˜ÉŸFþ¿Ÿ·þÍkmihÆññÆhimw'·ŸAF‚œþç¾þf²±^^­­^^ÿÿ‡ÿã'`V±Õ!2#!!5!53!4'&#!!276XNûþðûýèþpÊqON£þ¼D£NOQâÚÞÝÚQ¤àà¤ýf‹DCýÝDCçÕ$4&#!!2!5!3!!!2##ƒ~þú~ýÃþ»E¹Eþ»ÖççÖò´^þ“Í“uþ‹“þÊ£þ°¤Óÿãð+#3376!2&'&# !!!2767# '&ÊÊÒÃS†vwhfst‚ÿˆg²ýF‰ÿ‚tsfjwv„þ­Ãº Çý9Õýœ§Ð$#GÕ_//œwƪþó”//_ÓH$$ÏÆOÁÿã{#2&#"!!327# '&'##33676>¦¡šæ\" Ìþ, žUxž“¼þó”{ “¸¸˜z—{F¬V´AWô^3VªH­äýü`þ3Ê€žøÕ !#!#!#3 øáþêþéÊþçþêáå±þÜþÜýåýåÕüó7ýÉ3` !#####3 Ãìã¸åìÎÑóó•þk•þk`ý¹¡þ_É Õ!#!#!#!#3!3  áþêþéÊþçþêáoýøÊÊ_<å±þÜþÜýåýåÇý9Õýœdüó7ýÉÁÐ`!#####!#3!3 ÐÃìã¸åìÃþk¸¸éÑóó•þk•þkçþ`þéý¹¡þ_sÙÕ##767!#'&'!‹Ê“d‚Õ½xªþQþP¤t¼Õ€`ø>ýƒYý§Y~¤þåbÒ†1êý2‚Òþž¡zÊ(k{`! #4'&'##767Eþ]Ñþ»kK‰ÃV:V¸S8V‰Jlþ»¶þ–ýÌ&VœþìÇtOþ\¢KtÉœU'4ÉÄÕ! !#'&'##767!#3!ïþŸþP¤t¼Õ€`™Ê“d‚Õ½UnýµÊÊ„>ýƒqdý2‚Òþž¡z ý§Y~¤þåbÒ_4ý9Õýœn(Á.`! !#4'&'##767!#3!7þçþ»kK‰ÃV:V¸S8V‰þ’¸¸þ]ÑwéýÌ&VœþìÇtOþ\¢KtÉœþ`þ?þ–sþVszS#"&#"3276&#"#"'&54763!27654'4327654!"567376767632'&#"ss˜ÆDº#`ÜA€t bTDt;ü<}J£5?u_hFA»XùV£RuþÅ¥Þ Ÿs j#B#' "2Z¦brþïRU¾Ïgˆ‚r %§',a’zQ^XRj½7¦&6îJ´- ƒþƒÞ@' WoÊWd§þýE\`[þtÈO#"&#"32632&#"#"'&53!2654'&'"#5223 54'&#"5673767632&#"Èvm¢D¨Pb!',²-cX;b1Ò2i?‰Ä,ZnN .ër‚‚rë. >þ.þ¡˜½½˜_- > ^˜¼¼˜þ¢ >‘¤òó¤‘ ‘¤óò¤‘ ¡ªÒtÓª ªÓþÆþÇÒ« qÿã¡{&P%327654'&+"&'&'#";67>2# '&5476!36767623 !#"'&'&r-¡HVV?ª- ,4, -£GVUH£- ,4 É.þÿx‰‰t. 4 .w‰‰tþû. 4 ¤`t·§ƒa  _t¸¼p_   “‰œî颈   ˆœï袉   vÿãút N#&#"56763 #5765#53#"'&'&#"0#"'&547673!27676323 4'&'39v£Z^™‚‡Nzz%éòG!…ñ¾ftê[na`zxz{Ân[êtfCGoù~[UŠ]LKf›dKJ]ŠU[~úoFCP›/L†&+CþYþõg,'Ižûò‹žD@@DD€D‹òÆâì˜kóâ¶þº63366336F¶âók˜ìâ˜ÿã¡ :#&#"56763 '5765#53!"$"# 33276762324žv£Z^™‚†Ozz$èòF"…ñvŠþrTþÕðþÔRþrŠÆ’Ð>IxdüdyI?Ð’ì›/L†&+CþYþôg,'IžþÈþãþëþPââ±þ¹úþû8[ 77 [8øGÿÿsÿã3'’cSìÿÿ‡ÿã'à&’ísþV'ð!# '&76!2&'&# 3!#ú®þ¥»ÃÃÃS†vwhfst‚ÿˆˆˆ˜ðkÉÆÐShÏÐ$#GÕ_//œþØþÓ‚”ý°qþVç{! '&576!2&'&#";#˜þû•—–UQQLNONP³cccc³õÉ–Ÿú#¬+qr͹rqýê;ÿ¥Ê§'''7'77'77ÊdØiØdØ}®}ØdØiØdØi®i®}µ}®}ØdØ}®}µ}®}µdµûÚÞÿBz/!"'&'&'&547676763!47676762þ¹þ3 8 Í 8 g    ý) M #&#"56763 v¢][“ˆ‹Jw}$)›/K†'*Cý³Âþ¥a"53#7þ¥òñ…†Â ”žý³Âþ¥a#55#53þ¥ò†…ñÍþõgž÷ÖþF` &3@MZg#.#"#> #.#"#> #.#"#> #.#"#> #.#"#> #.#"#> #.#"#> #.#"#> þÇv aWV` v ž"ž8v aWV` v ž"žù®v aWV` v ž"žfv aWV` v ž"žù®v aWV` v ž"ž·v aWV` v ž"ž÷ v aWV` v ž"ž‰v aWV` v ž"žAKKJLþQKKJLKKJLúKKJLKKJL)KKJLKKJLû˜KKJLøXýÃÂ- #)/'7'7'7%'%53-#%5#53 3#k–yo\©ûw–yo\©–zV\þãúL•yþ©[`¬@þÀøÂ¬þÀ@_Ó¤RÓÓ¤RZ–yþ©\µ–yW\þãþñ•zn[©ûw–yo\¨Ô¤‚RÔÔ¤‚R߬@þÀøÂ¬þÀ@ÿÿÉþVüm&“ÏõuÿÿÁþV8&”š ÿÌ!ìÕ!2#!#535334'&#!!276“Nûþðûý訨ʨÜON£þ¼D£NOQâÚÞÝÚQ¤àà¤ýf‹DCýÝDC&Ež 4'&#!!276!2#!#5333>C€þùB>ýøÐyttsÖþ9››¸L^*.þ—.+ßþÊURþ°RRÑ>þÂÉáÕ'+#!2'674&+327'7UŒj’~ÖþÊÈû€â šþþrG×j#u~{Sý¨ÕqrÛ’-,9†’ýÏ/´~ºþV¤{)%'7654'& 32'#"'&'#367632*ŒnŠOSTþÜTSST’F“o•Wl{XY:¹¹:YX{Ì€€ ˜§]¥sÅËtstsþjts®]³.01dý® ªd01¢¢ýð¢Éj@ •üüÜÌ1/ôìÌ03!3!É÷ªý)Õ2þ$úպК· FüüÜÌÌ1µ©¼/ôìÌ03!3!ºƒ“ý¢`:þ3ü3GïÕ )@ • • Ü<Ìü<ÌÌÄ1/ôüÜ<ì20!!5!!!!!Nþù¡ý)#ýÝ”ª—ªþªýl8U` +@  Ü<Ìü<ÌÜÌ1@  © ©¼/ôüÜ<ì20!!5!!!!!?þùý¢ þ`ôªÂþÛªþ ÉþfÌÕ3@  üü<ÜÌÄüÌ1@•°••/ôìÔìôì0#!!!2+5327654&#“Ê¡ý)¡ºqmÌäL>†87||Çý9ÕªþFwrîþÎþòôªKKÂ"ŸžºþV `3@  Füü<ÜÌÄüÌ1@©½©©¼/ôìÔìüì0#!!3 +5327654'&#r¸ý¢úFRRQµÁ¬n!&&1†çþ`ªþÁGQåþòÖ``œ07“ª )(þ¿‘Õ33 3## # # 3êʪõýß׈Å)þþþÊþþþÓDýßõªÕýâý³ý"þAþéþêüÿˆMýFþå`33 3###'# 3?·éÖþnfz¸!þ‡»·»þ‡ÅÌþnÖé`ýòþQýèþL6Éþ“mÉýʱ¯ýòÿÿ‡þušð&z9£ÿÿ…þuÈ|&zÎÃÉþ¿´Õ3! 3## #ÉÊÒý¿í ÅEýúþïÊÕýâý²ý#þAþéþºþå³`33 3###º·âþTwޏ8þsÅ·`ýòþOýêþL5Èþ“ɆÕ373! ###ÉÊ­dÁý¿_Üýúd­ÊÕý±TîËý²üyþ%u±þº‘`37533 ##5#º·`eBâþTãÎþse`·`ýòaÝvFþOýQ5þÅÔaþ“!†Õ33#! # ##53Éʨ¨Òý¿_Üýúþïʨ¨ÕàþŽâý²üyþéþe=‘3!!3 ###53º·dþœâþTãÎþsÅ·}}z}ý5þOýQ5Èþ“}2²Õ _@   ÔÜì2ÔÄ991@B   ¯ /<ì29ì0KSX@    <<ííííY!! # #!2Òý¿_ÜýúþïÊþ=Õýâý²üyþéþ+*‚` _@   ÔÜì2ÔÄ991@B   ¼ /<ì29ì0KSX@    <<ííííY!3 ##!*8âþTãÎþsÅ·þ`ýòþOýQ5Èþ“ÆÉþ¿Õ6@ •­•  • 8 üì2üì2<ì1/<Ììä2üì0²P]3!33##!#ÉÊÞÊÉÉÊý"ÊÕýœdúÕþAÇý9Áþå@`1@ ©¼© F ©  Füì2Ü<ìì2ì1/<Ììä2Üì03!33##!#Á¸W¸¸¸¸ý©¸`þ3Íü9þLýüÉÕ -@• •­  8 üì2üì2Ä1/<ä2üìì03!!!#!#ÉÊÞ¡ý)Êý"ÊÕýœdªúÕÇý9Áæ` +@© ©¼   Füì2Üì2Ä1/<ä2Üìì03!!!#!#Á¸Wý¢¸ý©¸`þ3ͪüJýüÉþftÕ8@üìÔ<ìÜÌüÌ1@• ­••/<Üìôìôì0#!#!!2+5327654&#;Êý"Êr¡ºqmÌäL>†87||Çý9+úÕÕýœwrîþÎþòôªKKÂ"ŸžÁþV!`3@!F üìÔ<ìÜÌüÌ1@  ©¼/<ÜÌôìÜÌ0#!#!3 +5327654'&ˆ¸ý©¸ÇúFRRQµÁ¬n!&&1†çþÆü:`þGQåþòÖ``œ07“¤&.sÿ-'ð1?Y@ 26>6A $0,@üìüÔÌÌìÔì91@ 2:¡•0•Œ@:•%¡$®(• ‘@ôìôüÜìôìÜìÔì90%&47632327#'#$'&76!2.# 32767654'&#"Ed`_©¨ÀfJxuŽTGIRþé¾DLþż½ÃÃSu‡^\úyÿˆˆ‚ÜÙI0a++IJ++ˆ¹Ü}}úÜÖs?]¦À ÑÒbhÏÐ$">·7^œþØþ覤.$?ÖxEGEFyþäqÿR{ ?5@$  A2H;*E@üìôÔÄÌìÔì91@7¹.¸&Œ@äôä%67654&#"&54763327#"'# '&76!2&'&#"3W!V0),3MNƒ„NM‰'8HeZ>FRë‰5<þýЉ—–UQQLCZIU³ccUT«­q‡HT*.N³›…›WXXW¥µš,4ž—œ#˜qrÍÉtsÿÿsþu'ð'z-­ÿÿqþuç{'zÍÿúþ¿éÕ ,@ @ @ ÄÜìü<ìüÄ1@ • •/Ììôì20%3##!5!!×ÉÉËýîïýîªþA+ªª<þåm` (@   ÜÜü<ìÜÌ1@ ©¼©/Ììôì20%3##!5!!¯¸¸µþB1þB™þL¶ªªÿÿÿüçÕ<=þV`o@¼  ÔK° TK°T[X¹ @8YK°TX¹ ÿÀ8YÔüI:91Ìä20@BKSXííííY"%#3 3ÅÃþ;Ã^^ÃþD¼Nü”lÿüçÕ!#!5!53 3!ßþøËþùýðÙž›Ùýðsþsªªýšfüòª=þV` !!#5!5!53 þFþúÃþïþ;Ã^^`û²XªººªXNü”l=þ¿;Õ%3## # 3 3ËpÅþ\þYÚþ/ÙsuÙþ ªþA{ý…¸ýÕ+ý3;þåy`%3## # 3 3q¸!þºþºÙ³þrÙ))Ùþk™þL¸þHJþqýßÿúþ¿GÕ%!33#!!5!!ÖÞÊÉÉûŽýîïýîª+úÕþA+ªªþåB`%!33#!!5!!xZ¸¸¸ü9þB1þB™Çü9þL¶ªª¯þ¿|Õ%3##!"'&533!3³ÉÉËþ_ºqmÉ||x˪þAÇwrî7þÙŸžd–þå¸`%3##!"'&=33!3¸¸¸þ©™f\¸45h)¸™þL _V¸êÓu;;¾¯³Õ#"'&53;333###‚;ºqmÉ||ÖËËÖÇwrî7þÙŸž9þÇdú+ÇþÏ–`5333###5#"'&=3ù ¯¸¸¯ ™f\¸4+¤Âľû  ÄÄ_V¸êÓu;0¯³Õ$@  üì2ÔìÌ1·• ­/<ôôì0!2#4&#!#z¡ºÞÉ||þˆËÕýœéîþfŠŸžý9ÕÿÿºdKÿãð"*I@#$ $3 +Ü2ÜììôìÄ91@¡® •$­ •(•‘Œ+äôììô<ì2Ìôì076! !!267# '&'&=3%!&'& ²"–¼:Cµ»ûp‹üpoƒ„’þ¢Å¼ ªvzªKB@­b‚þH€amªÒÒÛþ„þôþÎ`_×F$$ÍÂUgkßL>D9¿|¤¤|ÿâf{%.i@.&&K /Ü2ìÜ@ p0°0Ï0]ìôìÄ91@&†ˆ &©¹"»*¹¸"Œ/äôìä²o]2ìü<Ìô²/]ì90"'&=33676!2!32767'$'&&'&#"XY`œ09Jt’ÿ⃄ü² fg·jdcbchneþóœ”NRSˆš]\RZ¬F1—!&Å‚¡‘’úZ¾dc4®*—…—ZZWWžÿÿþuð'¼Gÿÿþuf{'çHÿÿÉ“Õ,ÿÿ(vm'Ï[u¢ÿÿFïH'š¨ÂÉþf5Õ32+5327654&#!#3!©‹ºqmÌäL>†87||þˆÊÊžqwrîþÎþòôªKKÂ"Ÿžý9Õý‰w¿þVˆ`3 +5327654'&#!#33^HRRQµÁ¬n!&&,‹þü¹¹%ëwGQåþòÖ``œ07“¦$)þ`þã6þVÕ!#!567!3#:Êþ„bþ‘ÔCuxÉþ’†+Ôþþªý8§.¨%5úÕªþV.þV+`%3##!56765!s¸þÞ{å¸þ{v^þ̳;bõ™™þVªdžþ’þüϙϧÔÉþf;Õ1@ 8üì2üìü<Ì1@ •­ /ä2üìÜÌ0%!#3!3+53276qý"ÊÊÞÊÎãL>†87h_ý9Õýœdú“þòôªKKÁþVˆ`/@ F Füì2Üìü<Ì1@ © ¼/ô<ÜìÜÌ0!#3!3+53276Ðý©¸¸W¸RQµÁ¬n!&ýü`þ3ÍûŒÖ``œ07ÉþVÕ!#!#3!33#;Êý"ÊÊÞÊÉþ’†Çý9ÕýœdúÕªþVÁþV@`!#!#3!33#ˆ¸ý©¸¸W¸¸þÞ{ýü`þ3Íü9™þV¯þ¿³Õ%!"'&533!3##èþ_ºqmÉ||xËËɪwrî7þÙŸždú+þ¿ë–þå`%!"'&=33!3##Hþ©™f\¸45h)¸¸¸™p_V¸êÓu;;¾û þå´ÉþVèÕ%3####! !Éþ’†+ÅþËþÄ-}-ªªþVªüúáÕüøÁþV`%3####! !H¸þÞ{å²þ˸þʲ>?™™þVª°ý'ÙüP`ýîÿÿÁyOÿÿh’'šÎJœ´+1@oo]0ÿÿ{ÿã-&šO×¼´"+1ÿÿhN&œÈ¼u´  +@ 0?  ]1ÿÿ{ÿã-&¼jR ´-( +@(o(P-_(@-O(0-?(-( ]1ÿÿHÕˆÿÿ{ÿão{¨ÿÿÉ‹m'Ï¡u¡@@]1ÿÿqÿãH'š–Á@p]1ÿÿuÿãÙðQÿÿqÿã{ÿÿuÿãÙN'È ucÿÿqÿã&jTdÿÿ(vN'ÈQu¢ÿÿFï'jžÂÿÿ‡ÿãšN'ȇu£ÿÿ…ÿãÈ&j:Ãÿÿ ÿÁøÕyÿÿXþL/`TÿÿÉ31'qõ;¤ÿÿºyõ'q’ÿÿÄÿÿÉ3N'Èõu¤ÿÿºy'j’ÄÿÿsÿãÙN&ªÈ'u´ +@ @O]1ÿÿqÿãu&Êjs´ +@ @O0?]1ÿÿsÿãÙðaÿÿqÿãu{7ÿÿsÿãÙN&sÈ'uÿÿqÿãu&tjsÿÿoÿã#N'Ègu¹ÿÿqÿãç&jâÙÿÿ#½1'qr;¯ÿÿ=þVõ&q^ÿÏÿÿ#½N'Èru¯ÿÿ=þV&j^Ïÿÿ#½k'Ñru¯ÿÿ=þVf&Ÿ^Ïÿÿ¯³N'ÈÌu³ÿÿ–&j^ÓÉþ¿jÕ #@ •  üü<ìÌÄ1¶•/ÌÌôì03!!3#É¡ý)ÉÉÕªûþAºþåÐ` #@ © F üü<ìÜÌ1¶©¼/ÌÌôì03!!3#ºý¢¸¸`ªüãþLÿÿÉFN&·È·uÿÿº›&×jGþVïÕ9@  Ü<<Ìü<ÌÌÄÜÌ1@ • •/ôüÜ<ì2ÌÜÌ0!!5!!!!!!+53265Nþù¡ý)#ýÝiGR¿þéiL”ª—ªþªýì”È`nœa­8þVU`;@  Ü<ÄÌü<ÌÜÌÜÌ1@ © ©¼/ôìÜ<ü<ÌÜÌ0!!5!!!!!!+53265?þùý¢ þ`nFRÀþéjKôªÂþÛªþŒ”È`nœa­=þf+Õ%+532767 # 3 3*SfäL>‡7( þ^þYÚþ/ÙsuÙþ ¾bzªK5sxý…¸ýÕ+ý3;þVd` +527>5 # 3 dþkkCQÄÁÄO5þ½þºÙ³þrÙ))`ýßþ&9Íasœ m˜´þHJþq=;Õ3 3!!# #!5!ÙsuÙþNtþÎÙþ\þYÚÔþŒ–ÕýÕ+ýwýD{ý…¼;y` 3 3!!# #!5·þ©Ù))Ùþª þà~ÙþºþºÙþß”Ìþqþ4ýü¸þH‘´Õ &@  ÜìÔì2ì1@ •­ • /ìäôì03!!"!"$54$3!fON£Dþ¼£NONýèûþðûN·ŠCD#CD“ú+ÚÝÞÚfqõ` %@ F EüìÔì2ì1·© ¼© /ìäÔì03!!"!"'&763!5>Bþù€C>Àþ9ÖsttyÐLZ+.i.*¶û RRPRUÅ‘ÿãCÕ 09@2&)  1ÜìÔì29Ôìì1@"••­-Œ(¼1ììä2ôìì20!"32765#"'&54$3!3327653#"'&êþ¼£NOO_´KV! 3j^nîˆûNÉ?4pi;?Ênh×Ùf1ÉCDþêP_m}ŸþÝ`61‰ÊÞÚfûì[JJO›ŸýZàxx9qÿãs` 08@2F&) E1üìÔì29Ôìì1@" ©1©-Œ(¼1ìÌä2Ôìì20!"32765#"'&54763!3327653#"'&=þù€C>A@j”\-1C]^ˆ¬fetyи>¸dhÎÓd.*^\:9m4œþöl01a`¦ªRUÅýaŸPOORAþ¸ìsxx%Éÿãð74'&+5327654'&#"567632327653#"'&'&ª\]¥®¶•OOQR˜S¾rsdeY憆GGƒ‘RQ?4pi;?Ênh×Ùf0!²„JK¦;$&´ hi²|UV!bb€[JJO›ŸýZàxx8Pa«ÿãF|54'&+5327654'&#"5>32327653#"'&NH‰”›tCDFEwGQPabªLÄqr<=ih<>¸dhÎÇpb8f83˜,-F@.. §NO]@AHOHXDEORAþ¸ìsxueÉþV<ð)!#4'&+5327654'&#"5676323#sÉ\]¥®¶•OOQR˜S¾rsdeY憆GGƒ‘RQÉɱ…JK¦;$&´ hi²|UV!ba’þùý¬«þVƒ|)%3##4'&+5327654'&#"5>32È»¸¸NH‰”›tCDFEwGQPabªLÄqr<¸dhÎÇpbþ{v^þ̳;b`ýWŠORAþ¸ìsxue{†þ’þüϙϧÔÿÿÉÿã-Õ¸Áÿã`327653#"'&=!#3!ˆzgh<>¸dhÎÇpbý©¸¸W`ýWŠORAþ¸ìsxue{Ìýü`þ3Ísÿã‰ð0@  üìÜìÄì1@••• ‘ŒäôìÔüÌì0# '&76! &! !2653‰d-‰|—þ›ÄÀ¿Åe'ááþêýÛ%×{ʺà9!ÒÌÐÍҎ׿ýŸý ”¤ðqÿãF{0@ E EüìÔìÄì1@¹¹ ¹¸ŒäôìÔüÌì076!2&#"3253# '&q’– к¾Ä½bZZb½â¸Éåþü•Ž/¡nª||rþ|r|>¾Åì禞ÿúÿãfÕ327653#"'&5!5!×?4oi;?Ênh×Öinýîï+ü–[JJO›ŸýZàxx}âqªªÿãö`327653#"'&5!5!x>=ih<>¸dhÎÇpbþB1¶ý®VFEORAþ¸ìsxue{~ªªÿÿ¤ÿã{ðRÿÿ…ÿãÈ|BTþf:Õ!56765!+532765pþfbþ‘Ý:WxÍãM?†77+¸ýÊþøý8ª0£ödþú“þòôªKLÁLþVs`!56765!+532765»þ{X^þÌ»3Dõ£µF1a.&ÍoþPÂÏ™}¦зûŒÖÀœ0)¡²ÿã Õ F@   8A!ü²p]ìüìÔì³ 991@ ••Œ /ä2ôìÜì¶  9033265332#54&+! '&²Ë®ÃÂ®Ë ,”ÀœgQ]*-þæþå‘ÕüuðÓÓð‹ü\GC¶«F1l[R.•••$²)òK@  8Aü²p]ì2ü<ìì991@ ••‘ /ÄÄ´`]ôìüÌ0¶ Ÿ ]376! #54&#"!2#54&#!²‘$Ë®ÂîX•¿œgQý¨¤$••þÙþÙ9 ðÓÓð¶«F1l[ýÁ²Äò%D@   8!&ü²p]ìô<ì2Ôì991@  • •‘ /ôìÔ<ü<Ì0²O']32#54&+#!"'&54! 4&#"3)G•¿œgQGËþ*ÉŸn(!Ë®Âî5Z¦r¶«F1l[þ=ó|îï#þÖþÜþÎðÓÓ¢¢I|²ÄòH@   8Aü²p]ìüìÔì² 91@ •¼  •‘ /Äôì9ôüÌ0²O ]32#54&+#4&#"#576! Y•¿œgQGË®ÂîˑZ`¶«F1l[üO‹ðÓÔï 9$•••\²ÿã)Õ$30!2#54'&#!3276=3! '&²ËX•_`œ07Qý¨WWÃÂWWË‘“þéþå‘1¤þ‹[[«F1l*1þ™ðjiij¬ 9ã’•••›ÿºð$2%!67#"'&543 2#54'&#!3 7654'& fè<0I|à‚ƒâƒq4ç•_`œ07Qý5˧OPPOþðOPP¯áÐ'.ˆ‡ëæƪäV]þÏþ÷[[«F1l*1LL]]¢¡]^^]¡¢]²ÿ)ÕD@8 :  üì2ô²€]ì99ôì991@ ••¼ /ääìÌüÌ0%!2#54&#!3!2#54&#!}…•¿œgQý°ËX•¿œgQý¨¯¶«F1l[Õþ‹¶«F1l[²ÿ)ò@@  8Aü²p]ìü<ìì991@ •‘ •/ìÌÄôì0¶Ÿ]376! #54&#"!2#54&#²‘(Ë®ÄìX•¿œgQ¤$••þÝþÕ9 ðÓÖíý$¶«F1l[²Žò-:#'&'&763!&'&#"#76! 32#54'&!#"327654:gi˜’m‚INK(*WÂÃWWË‘—\!%•_`œ05þл™9:E5:.± ßrs Tf½“LQR2jjiðüu¤$•••[¢[[«F1j,1‡i--Q@“²Äò+#! '&4763!332#54'&)"32765pG‘þåþí˜nŸÉÖËG•_`œ07þþT¦Z5WWÂÃWW±þþÜ•••Ž.à|³’þn[[«F1l*1}H•àijjið²)Õ:@ 8Aü²p]ì2üì1@ •¸/Ääôì0²9¶Ÿ]363 #54&#"#²Ë Ñ(Ë®ÂÁ°ËÕþ;dþÝþÕK2ðÓÑòýó²ÿVÕ 3@ : üìô²€]ì991@• /äìÌ0@0P]!2#54&#!}…•¿œgQý°ÕúÚ¶«F1l[Õ²ÿãçÕ327653#"'&!#3|%3x*%ËqXÒÆdqþÌËË`ý×þå>WWK‡°ý7Õ}bbpi“üOÕþ‹Aÿã™Í$3! '&7#'&=33!2#54'&#%" 76'&Öɼ¼þÆþż½Ãg”``œ07Q°•_`œ07Qý¨á|‚‚¸€€y&áþ¤þŸÒÓÒÒbcÚ\[«F1l*1[[«F1l*1 ž¤þåþ椤¤¤©ž²)Õ!## '&33276=3)Ë Ñþí˜ËWWÂÁYWËÅd•Ž+&ýóðjiihò˜²ÿ)Õ !2#54'&#!5 uwüÉ •_`œ07QüÝ1þkÕþ®ü,[[«F1l*1¯ÀfÿÒÉð'1?%#"'&543267#"'&543 327%&#"32 7654'& ®oU¬Ie┃r0I|à‚ƒâƒq9ƒI9~d›•þÉX/? 9.YOPPOþðOPP@$2iùšwï'.ˆ‡ëæƪäþÚöd‡kÀWÆM( ]]¢¡]^^]¡¢]²ÿÄò?@  8Aü²p]ìüìÔì1@•   •‘ /Äôì9ôÌ0²O]%32#54&#!4&#"#576! )G•¿œgQþî®Äìˑ!¯¶«F1l[‹ðÓÔï 9$••þÖþÜ\ÿâ…Õ,3276=4'&#!#5354763!!"!2#5# '&ÙWWÂÁYW07Qý ²²`_•#ý Q70X•_`Ë Ñþç’±þ˜ðijjgò¡l*1¯«[[¯1*k[[«ýFd•”%²ÿãÄÕ!! '&332765!2#54'&#)‘þæþå‘ËWWÃÂWW•_`œ07Q&ý þÜ••••$¤üuðjiijð‹[[«F1l*1ŒÿãSò" $53 6&#!5!2654& #4$ 5þ÷ýJþøÊRS覥õý¿Aæ“Äþ–¸ Ê++.’WHþ±õèçö¢NM›IšŸ‚tYa[J¸¸þñ´\ÿãnÕ@@  8ÔìÔìôìÄ1@ • Œ•ôÜìôì¶ 9²À/0326=3! #"&=33®ÄìËþßþåþæþßG’œgQÝýmðÓÔï 9þÜþÖ-!ü¶¬F2lZ²ÿã)“ 3276=3! '&576%7%5zZ[¾ÃWWË‘ŸþôþÏz=s–ý9þ¤þ™W/¯hiikï 9þψ••¯Ö&dà¡AŒ¾¾¾œ¡žU²)ò7@  8Aü²p]ìüì1@  •‘ /<ôì90¶Ÿ]376! #4&#"²‘!Ë®Âî¤$••þÖþÜü\‹ðÓÓðüu²ÿB)ò4'&#"#576! %5%$76aZ[¾Ã®Ë  1y‘=þ–Çûï\gW/ίgjÔï 92‡••²ÓþÚdà¡AŒ¾¾¾œ¡žU²¡ò##576! #4'&ˈKuË®‘9)Ÿ¯ËuBGü”lP|Ý 9´••£þêü\‹ÌP›ÿºð0%&'&43 2#54'&#!3!767654'&'& ¢EðÊâƒq4ç•_`œ07Qý5Ë«e, 7OþðOP—£ÜܪŽæƪäV]þÏþ÷[[«F1l*1LÃÂ,ƒ@B¾@^^]¡ªt~²ÄòH@   8Aü²p]ìüìÔì² 91@ •¼ •‘ /<ôì9ôüÌ0²O ]32#54&+#4&#"#76! Y•¿œgQGË®ÂîˑZ`¶«F1l[üO‹ðÓÓðüu¤$•••\ÿÿ²ÿã)Õ8²ÿÄÕ!!# '&5332765332#54'&#^ Ñþí˜ËWWÂÁYWËG•_`œ07QÅd•)±˜ðjiihò úÚ[[«F1l*1ÿÿ‡ÿã¢ð6²)ò.@  8Aü²p]ìüì1@•‘ /Äôì0¶Ÿ]376! #54&#"²‘(Ë®Âî¤$••þÝþÕ9 ðÓÓðüuŒÿãSò0@ '&53 7654'&#""#6767&'&5476! "327654'&’RQ…„ýJ„„ÊRSèSSSRõâefg#R’HJ›““›IIþP´acIJåæIJcaW"cc¯õttstö¢NMMN¢§MMM *c" Y[€`XX^‘€[YÙ01YtAAAAtY10² Õ=@ 8  Aü²p]ì2üìÌ1@ ••¸ /Ääôìì0² 9²]54&#"#363 3^®ÂÁ°ËË Ñ—uçÛ2ðÓÑòýóÕþ;d•sâ¯ÿÿsÙÕ3\ãò,<47632#"'!2#54'&#!##"'&=337654'&Ð’ëô„‘‘„ôöIô•_`œ07Qþ Ë •_`œ07Q*Š]WW]Šˆ_WW_r•sppz•zpSÚ[[«F1l*1þ=Ã[[«F1l*1ÜA>T]=BD=[V>CÿÿsÿãÙð2‡ÿã¢ð167654'&4'"!"'&'5&'&547632¹qG^CCþ¼95+<&0âkl’‘þçjxw€{vEB¾[eK[¥ç”ýø 4D~n>=„>@%‘c3A +ml¶Ùpp/ÐE# ,,W`«×arÉÿÿuï‡ÿÿ²þ×Õ~^ñ©Â#33ÕvÕñÑxñ†%"#476327653ÿ[RB˜hjµ[RB˜hjÍTDD‚jlTDD‚jlÿÿžð}fCô^å¼7#47! !"33254'&'#"üž q3U7þþû˜a\ "ÍÌ9ñÓS üA5àz\&Œ²NZñ²%03!Zªª}ñ4þb–ºÿæb&^@PP F'üìüüüü<91@  ‡#Œ¼/ô<<ô<Äì290@0(P(p(( ( (¿(ß(ÿ( ]%#"&5332765332653#5#"'&®E``‚¯¾¹ruSS¹rw¦¹¹?XXyzVUØ|:<öâ¤ýb¢œ`^¤zýb¢œÀ¢zû ®h02>>ºþVd{?@ ‡¸¼  ©  N  Füì22ô<ì1/üÌäôÄì90´`Ï]54&#"!!#3>32¬||•¬ñý¹¹B³uÁÆÂÜŸž¾¤þ6¯þV ®edïèâqþV´{ <@" GE!üìô<ì22ÜÔ1@¹ ¹¸Œ© ½¼!ääìäôÄìÆî032654&#"##"3253!!/§’’¨¨’’§+¸:±|ËÿÿË|±:¸Zþ¦/ËççËËççû\RdaDDadªüO¯ºþV¾{=@ N  Füì2ô<ìÜÄ1@ ‡¸ ¼½© /<ììäôÄìµ  90!#4&#"#3>32!d¸||•¬¹¹B³uÁÆZþVHŸž¾¤ý‡`®edïèþ ¯®ÿãX?@ ‡Œ©¼NFüì2ô<ì21/ôüÌôÄì90´`Ï]3!!3276=3#5#"'&®¸òý>>|˜TV¸¸C±uÁddºZþLªþ ŸPO_b¡¥ýv¬fcxxqþV/{<@ G Eüìôì22ÜÔ1@¹¹ ¸Œ©½¼äôìäôÄìÆî03!#"325332654&#"ZÕþs:±|ËÿÿË|±:¸üÕ§’’¨¨’’§þø¢RdaDDadªýÏËççËËçç®ÿà,@ Füü<ÜÔìÄ1@ ©¼ ©—/äìÌôì0)3!!32#54'&Sþ[¸zý†í˜M`œ01þLªüùI[«F1i&&ºþVd{>@‡¸ ¼ © ½ N  Füì2ô<ì1/üìäôÄì90´`Ï]!4&#"!!3>32¬||•¬ñüV¹B³uÁÆžŸž¾¤üŒ¯ ®edïèý\ºþV6{ )u@ +G  F*üì2ôìÜij$Üü³!Ôì²!$ISX³ $<323#'&5476#"3276#§’’§¹¹:±{Ìÿ’¢5`¬ 4xBdB˜Ÿ‹J4/ËççËü' ªdaþ¼þø¯¡Z¡+h|{Nv¯qq<qÿã/ 4@ ! GE üìô<ì2ÜÄ1@¹Œ ©¼— äô<ü<ôì0!"32765#"4763!33¢þƈbO§’šMS¸þìK•öÿ}ù<¸Õ¶za¬ËçksÔ‡þCþ¯š+Dß«§´þLªºþVd5@ ‡¸ — ½N  Füì2ôì1/ììôÄì90²`]#4&#"#3>32d¸||•¬¹¹B³uÁƤý\žŸž¾¤ûݾýžedïÁþV×` @FüüÔÄ1@¼©½ôää0!!3y^ýê¸þø¢ ºþV†I@  NFüì2Ôìôì21@‡ ©¼Œ—½ /äääô<ìÄìµ 90²@]32653#5#"&5!#3”||•­¸¸C±uÁÈþ—¹¹`ýaŸŸ¾¤{û ¬fcðçüú ¾þLqÿâ¼0\@ 2 $G,E1üìôìÜÄ´,ÜÜì1@ ©0¼1©—1¹(Œ1ôìôìô<ü<0´!""<<µ!ìì²<<#"327676''&5476;#&!!'&'&4763Ã[¥AS§’­].þÝSD81“N/VþÉ®!…‡â÷qÚ¶ZsºËç–IR\++˜(VL-%¯)$žªþ¶?‰¦¡¢뮘®þVX:@  ‡Œ— ¼ ½  NFüìôì21ìääôÄì90´`Ï]332653##"&®¸||•­¸¸C±uÁȺZû­ŸŸ¾¤{ùöVfcðÿÿºdKqÿãZ 4e@ GE5üìôü<@ (''*%%*3ÜüÔì9/ì² 91@.© ¹¸¹Œ'—/äôÄìôìÜì² 90@ `6€6 6à6]32654&#"#5#"325&+"'&5473;2/§’’¨¨’’§énD¸:±|ËÿÊWCv>!¸%“7)/ËççËËççkP¨þÛýÕ¨daDE<6pG5P0,!K7ºþV9{;@ N  Füì2ôìÜÄ1@ ‡¸ ¼ ©½ /üìäôÄìµ  90!4&#"#3>329þs||•¬¹¹B³uÁÆþø¢HŸž¾¤ý‡`®edïèüT®ÿãX-b@ (N  F.ü<ìô<ì2¶ -9·  ìì° /1@%‡Œ©¼†—/ôìô<ü<ôÄìµ! (90#5#"'&=47#5367$732%326=4'&#X¸C±uÃbdzzÚp˜­Ý>Bi°‚Oý˜Š>Ay•­cW ýà¬fcx{äIéʪ€`®&¯$„%8ÃvÆåJ¢MO¾¤;Ï)ž®ÿã +?@-% $NF,üìôì2ÜìÜÄ1@ &©—$‡&)Œ¼$/äôÄìôì90332654'&/&7676;#"#5#"&®¸||•­ M.=<‹ìì(`¸C±uÁȺ¦ýaŸŸ¾¤— ¼p0.-¯ *ó/(ü„¬fcðÿÿÿÛþVy`ù2ÿãZ#G@%  N!$ÔüÌÜìôì2Ä1@  !© — © ¼ ‡Œ /ôÄìô<ìôì90;32653#5#"&5#"'&5476;#"òƒó||•­¸¸C±uÁÈ;•^PZl}YYƒêŠýaŸŸ¾¤{û ¬fcðçü^P”zKW¯ þV•{!<@ #E F"ü<ì2Ô<ìä1@ ©½"¹¸"ôìÔüì0¶!  ìì3!!"'&547654'&#"#4632†/îýÃQ@'$ÍC#@l;q¹»¯ÔsDEÀ ¢E+G56­dZY0Y^c£Ô«e’“eÿÿºd{QFþV;`%X@##'  &ÔìÜìÌÜÄ9/ì1@#©¼&©½&üìôì990¶%  ìì¶ ! ìì3!!"'&547676/&5476;#&(3îýÃW:'$F[•L2Öse`6Žg+¶! ¢E/>A/­(32Âýê||•¬¹¹B³uÁƯ¯žŸž¾¤ý‡`®edïèþ ÿÿ®ÿãX`X®þV-=@    NFüìôì2ÜÄ1@ ‡Œ —¼ ©½üìääôÄì903326533!#"&®¸||•­¸ÕþsC±uÁȺ¦ýaŸŸ¾¤/øä¢Vfcð®ÿã{%i@  PPF&üìü<ü<üì1@‡¸ "‡Œ ¼ /<ô<ôÄìôÄìµ9µ  90@0'P'p'' ' '¿'ß'ÿ' ]3>32#4&#"#5#"&533276¸B³YÁƸ||zUV¸C±dÁȸ||‚XWå{®edïèý\žŸž_`£ý‡¬fcðç¦ýaŸŸ_\ºþVd{7@ ‡¸ ¼ ½N  Füì2ôì1/ìäôÄì90´`Ï]#4&#"#3>32d¸||•¬¹¹B³uÁƤý\žŸž¾¤ûÝ ®edïÿÿqþVZ{JÁŸ`@FüüÔÄ1@¼©/ìä0%!!3y&ý"¸¯¯`®þV%k@  PPF&üìü<ü<üì1@‡¸ "‡Œ ¼—½ /ìäôôÄìôÄìµ9µ  90@0'P'p'' ' '¿'ß'ÿ' ]3>32#4&#"##"&533276¸B³YÁƸ||vYV¸C±dÁȸ||‚XWå/ýžedïèý\žŸž_\§ûÝVfcðç¦ýaŸŸ_\þV¿{$U@&E  G/Ü<ì222ôìÄä1@"¹¹¸Œ%©½¼%äìÜ<ü<äôÄìÄî²IPX±32#"&'!!#54&#"326Õ¹:±{ÌÿÿÌ{±:éý¹+§’’§§’’§ú–Īdaþ¼þøþøþ¼adþô–°°)ËççËËççÿÿqÿãu{RzþV–*"-6u@83 .# *E7ü<ìÜ<<ü<<ÜìÄ1@†&¹—7/ ‡ Œ"½7ìô<Äì2ôìì³#99²#9¶3.  90´#,<<µ. #ìì"'&'53&'&547632##4#"27654'&,Dd†„%ßKcçfep_˜{5S#Âal’~¢¸EU@<%I]7_E8B»Q£-a`¥ta2N•þ©-blëþÚiZþn!vFDs:#÷ý+I¿³J>®ÿã¶8@    NFüìôì2ÜÄ1@  ‡Œ— ¼ ©/ìääôÄì90332653!!5#"&®¸||•­¸^ýêC±uÁȺZû­ŸŸ¾¤{üO¯¬fcððÃR@ü<ì21@ƒƒ/ì/ì073#3#ðÓÓÓÓþþRþ`¤ í27#"'&'3U ooÍ,rr€ON§cA¢UUŸWDþCÚÿÓ´ Ü<Ì21¶ÜÜÜI:03#3#D––––-–d–ÿÿ«þC‚ÿÓ'õ¨ûþä°KRX»@8<1YÿþC¢ÿÓ %¶  Ü<Ü<ÜÌ1@  Ü<Ü<ÜI:05!73#3#ÿ}––––Ö–––d–ÿþC¢ÿÓ.@ Ü<Ü<ÜÜÜÌ1@   Ü<Ü<<ÌÜI:03#3#'#5#5! ––––ú–}-–d–úÈÈ––Dþ»ÚÿQµÜÌ1ÜÌ03#D––¯–ÿÿÇþ»WÿQ&ù}ùƒ²ISX¹@8Y1ÇþCWÿÓ O@   ÔÌÜÌÜI:1²ISXA @ÿÀ88Y@    Ü<Ü<ÜI:03#3#73#D––}––ú––þÙ––––cþ»»ÿQµÜÌ1ÜÌ0!!cXý¨¯–cþu»ÿ¡´ ÜÜÜÌ1´ÜÜ<Ì0#5#5!Ú–áXõ––––––µÔÌ1ÜÌ03#––––ÿÿ––þÿÿ/þ¹ÿÝ'ùßÿ`&ùåöùþëŒ=² ISX¹ ÿÀ8Y³ I:1² ISX¹ ÿÀ8Y² ISX¹ÿÀ8Y³ I:0Nåä{µÔÌ1ÜÌ03#N––{–DþCÚÿÓµÜÌ1ÜÌ03#D––-þpdÆj!!dýåj¤c»–µÔÌ1ÜÌ0!!cXý¨––Ñÿ8‹(´ÔìÌ1²ÌÌ0´@P]3#Ѻº(ú¯–µÔÌ1ÜÌ03#––––Å[–µÔÌ1ÜÌ03#Å––––Ñ‹`#¶ Ô2ì2Ì1´¼/ÌôÌ0´@ P ]3#3#Ѻººº`Êý4ÊfÜ` ¶  ÔìÔÌÌ1¶©¼ © /ìôì06;#"!!fÍã’”†n¼ýŠ^ô–ÂþcþC»ÿÓ´ ÜÜÜÌ1´ÜÜ<Ì0#5#5!Ú–áXÃúú––²ƒ`V@ FN  FüÄìôÄìì1@ B¼ /<ô<90KSX@í <<ý <5–¸²G.Ùþi=d¸ªBôÙ]Gg`²þó¹"þ:T„Ôë®)yýú‰ÔX†`!·  ÔÄÜÌÔì1·©¼ © /ì2ôì04&#!5!23!5!&n†þZ¤ãͦûÒÎy–ôþòþ1CZ`G· ÔÄÜüÔÄ1¶B©¼ /<ôì0KSX@     í íííY4&+532##n†Œ’ÜÌ’Ç[þäÙË^Þ•ôþò<ýÞSþ­"Xh`$µÜìÌÄ1µ©¼/ôì20@]1#!5!žºýtÑü/Ѻd`4@ FN Fü<ìôìì1¶ © ¼/<ôüÌ0@ @P`p]!#3#4&#!5!2s¹¹ñ¹n†þúãÍŠývy–ôþòºt`¶FFüüì1²¼/ô0²]!#3tºº`X¦` #¶  ÜÜìÔÌ1µ©¼/ôì20@ ]5!"#7XNrXºGÑ–Âý‡y Kºd` (@ F N Füìôìì1µ© ¼/<ôì0@]!#4&#!#!2dºn†þ½¹úãÍy–ü/`ôþòºÿã¿`*@ E FüüÌÔì1@ ¹ © ¼Œäô<ìî0332654&+532! ºº•³›¯„w„„Äðþÿþþþ÷ù`ýêþýÀÈëÅÒþêþðþÏþÚ/ºÖt`¶FFüüì1³¼ôÌ0²]#3tººÖŠXþV` ,· FN Ôüìä1¶©¼½ äôì0@  ]#4&#!5!2ºn†þúãÍþV#–ôþòX` @ EN Ô<ôìì1¶ © ¼©/ìôì035!26&#!5! #Xtë’¦×þŒÀÛøòŽŸ’þøþØþ¿ïX&@ F NÔìôììÔì1¶© ¼—/ìôì04=!3!#þTý»þþ¬¿“[ÅCþLþ¬zþèlþòºd` )@ F N Füìôìì1¶ ©¼©/ìôì0@]3!2%!4&#!ºúãÍý6n†þ¾`ôþòý¢ê–Xž`^@ F EôÌ99Ôìì1@ ¹¸© ¼/<ä99äôì0°BKSX@   ííí í³ 99Y"#673632!5!4&þáWWHºFdÆÙašàÅÍýÎxŠѧ§“þðÈ €€ôþòý¢ŽÐ˨ºþVt`¶FFüüì1´½¼äì0²]#3tººþV XÎ` %·F  ÔÌÔìì1¶©¼ © /ìôì0@]4&+532!5!n†”’ãÍýмy–ôþòý¢Xÿã(` *@ E ÔÜìÔìì1@ ¹ ©¼ Œäôì2î0#5! !"264&+"ʸ þëþýøöÀéì×Ñþßþñþ´þÿ0þDµü¥þ_þÐNÿOé`U@ F ÄÄÔ99ìì1¶B¼/ä2990KSX@  ííí9í999Y%67676535673ßVGu"º,:Ép¥Îˆ‘LÞƒ4U›}*÷þÖþð¯ëp>1=!±"$ºþVd`1@ FN FüìÌüìì1@ © ©¼½äôìÔì0@]#4&#!;#"&5!2dºn†þ½\pTQ•óúãÍþV#–ðU;zÀdôþòºd`,@ E N Fü<ìÌôìì1@ ©©¼ ©/ìôìÔì0! )5!2676&+;#"&5º€*þ×ÿþ€4ã×Ýâ{\Lwuqó`þßþñþðþàŽ·ëæ»ðU;zÀCþVp`D@ F  ÔÔì9Ôìì1@ B½ ¼ä29ì0KSX@ í íY#3>=3#ºþÚÍØq_¸¥¨þV`ªþ}Õ›²ñÚC!`J@ F  ÔÄÔÄ99ìì1¶B¼© /ìô<0KSX@     õõ <=3!5!CÚcMg¸¶XCü"œ`ýú‰Ô›²þùÄþ:ŽºþVÜ`,@F Fü<ìÔììÜì1· © ¼½/ìôüÌ0!#76654&#!5!2#3äl)þW›zý®BË'â*ý˺ºu,/¬H‚‹èÂVvþâ.ý¸4X` )· FN Ôôìì1´©¼/ôì0@  ]!#4&#!5!2ºn†þúãÍy–ôþòº½`/@F Füì2ÔìÔìì1@ © ¼ ©/ìô<<Ôì033$763 76763)º¹ :¹0nþ‚LœÇaƒ¹„ÝþTþÃ`ýñ±SþÊlûþÍ+7ŸÔüýíÜþˆ`+@ F ÄÔÜìÔìì1@ ©¼ © /<ìôì20!#4&#!+53265#5!2ˆºn†þö£µdDrL¼~ãÍy–ýÅÖÀa¦;ôþòÿÿº `'–ÿÿº `'–ÿÿºÖ `'–ºä™`@ ´¼DÔì1ôì03#ÒÇþº™`þ„ºän`@´¼DDÔìÜì1ô<ì203#3#§Çþº™½Çþº™`þ„|þ„Ûì!#53®Ó¤R¬@þÀüýñ 327654'&+533676Í7N5G4¯pQf$h?FA@6b ! ¨eþ¿I(R[2*Û #53 3#®Ó¤RÓÓÓ%¬@þÀý-þ“°ð$%#5754&'./.54632.#"¼Ë'/XZH߸gÁ^a³Olƒ39ZZ8þþþ“{4<5/VV‰LŸÂ89¼CFnY1^5YV‚eš£UÞ"756767&'&54767632&767£/SD4Ž35gcbnZdF31`È9:H:ZÍçU°!LOTAKv?=¹0ps2#¹Qÿÿlþ Ã'^ÿ5ÿÿ‚þóÀµ']ÿôþ>RÁy3#Á¸¸ùìÿÿ‚þ¢ë&nÓ9þ¢ÿÿ‹ÿÆ &PÔú„ÿÿ‚ÿìë &nÔ¼Šÿÿ‚ÿìë&nÕ¼Šÿÿþ (f&;Óþ (f$3  !27# '&5767"Ã$ÄþÚ×àJKÁÔ–úþ^©ƒÔ`‰eŸ~h'?6š§®þû‡`¤v¸c–àße4-ÿÿþ (°&;Ó?}ÿÚR%67654'&'3#"'532‡¬#þ¢›—¸€Šÿÿÿ«þ b¶&?Óq ‚þ î?%#&'$473327676'&/3327653323#"'&'üT…ÁPx€mþíi¸l“_Qb_y^@@¸$;sR,%¸@nޏ\Kf% I0Š1_2F„,³kª>GHö´Îܳ&%0žlŽ}=êœJ"5Œþ^ó.327654'&'&#"&#4763&547632#bzL,5;(.;Dn2KÈxAZ¢M\MO¶bxX²'*9:X DD(ÁNOþì­f7*(”„?$S§-8’APÿÿ“þµH¶&ŠÓ? ‹ÿÆ Þ"327654'&'2#"'&5476»B!799[]KB{˜Æ¶“¥„`Q§%T*WE{R,,9.UMAx³ |”ÈKU#JïµN¹ÿ«þ @† &"34'&!5 767"'&'&547632î?,3/ÀV%.¬_]Àþ“g†¨>v-–(tYh‰YH‚Ä9!$3/,¸;þôþÌ X*V¸L_” !"b‰Wg3Zf‚þóÀJ6%#"'$47376767654'&'&'&'4762#&'&'&Vfþ©±xHþÛ?¸B¤a=~±T;~BrC:@¾_Ð`¸ B(EN><}ø9M ªI&huqƒc“- !P85J.39s–J%*=­=!'&"7*S@UYDÿÿ‚þ ÀJ&RÔôþ ܺ$š5%5%ÜHý¸Hªn‚nþŽn‚nܺ$ý&567&'&54763233"/#"'&5332767654&#" %!lE?I(7 /4KU^r8Z #08Ÿ " -d$* 9^W4'6O'&n=NVš)qaKî" %Üþ$ÿö5%%5%ÜHý¸Hþn‚nnn‚nܺ$ª5%ÜHºn‚nܹ$ý-&'&5476323"'&'#5276767654&#") lE?I(7$# +EÈ“˜‹V " -º 8_W4'6O -n=*{nmp" %Üþè$ÿØ5%ÜHþèn‚nÈË8ô(#"'&54737676533254'3'&!9EO)"a 2=`YG g -SG„L(E?4mmb}8T"ªRY$6îs9It…6Y !ìá 4&#"32>"&462–X@AWWA@XzŸæŸŸæô?XW@AWX³æ  æŸ˜¹hÁ732767#"'&'˜ gC*6:)kXZZC5"LMD¥6{S )L}@F€ÿÿO¿¦wpÕÿÿÿOþ ¦ÿÄpÕøÚóî 4373¶Ã‹‚‚‹ÃîFÃÃþº¸Â”¼3#¸Üܼú¼ !#'3¼´%1œÄyI ©Ü!nþÝþ”›Rð#'337673#"¦ ´%1œÇBR´{6´)cóoajr›þ©Ü!n›˜•UPymþúL%#'37676537653#"'« ´%1œÄ/(/H´/; ´'G›… 44.5WY¾þ9©Ü!nr|> @2¦Þ%,*ÊþÏ;l>3 ¯ÿì§ *"2767#"'&54767&'&'&76#zfˆ\MOY‡p0;JcX~VI|ee•pdkAXH,7âp ¨4C@#90¤L@rRiUZ‡hÔÀ®ÇsBBsÇ®Àþ¡ë³þËuu5³ëaUò#'#"'532N%bÄU`޲§´¥ÒþÜþŸþëþ†DK*‘2´2<!&'3673ÌbŸ~ÄšZ00ZšÄ¥xUö:þæþÊþŨ ¨;6þÓþIþÊú<3#&'#6Ì´Ux¥ÄšZ00ZšÄ~ŸbúþÊþIþÓ6;¨ ¨þÅþÊæþ:dò#"'&'&547632#54'&#"¿Î=­:i_{•š\ %ZÄ[,,G\O98<SGU37e{a}Uw¦‡–þÍþ¤œnWl42@B^!xþ©–$%-`+-!d! M fMÿÿ‚ÿìë™&n,„ý¨ÿÿ‚ÿìë&n×9„ÿÿ‚þ ë&n×9ÿÿÿ‚þë &n'Ô¼Šä©þÿÿ‚ÿìë¶&nÖ¼ ÿÿ‚þ ë&nÖ¼ÿÿÿ‚ÿìë&nؼ„ÿÿ‚þ ë&nؼÿÿÿþ (¯&;]Šÿ8ÿÿþ (ª&;×?ÿÿþ (f&;ÔŠÿÿþ (f&;ׯÿÿþ (ª&;ÕÂÿÿþ (f&;Ö––ÿÿþ (f&;Ø–¯ÿÿÿ«þ Á/',¼þ>?ÿÿÿ«þ Ér'_¼þ>?ÿÿÿ«þ &'_ù?ÿÿÿ«þ „°&?Õô ÿ¤ŸÙ:654'&32! '$&73! 76767#"'&54767632µ)B,4((7(*H³nþÃþïþáþÙ‚þ·@¸AËZAÑ×d#?z…Kb–NLZB`.+M;3*)3P&þÚ´F=)d Š\^ˆtL"9;l&NKCW4,”E$2Hf6‰âÿÿÿ¤Ÿ&„Õx~ÿÿÿ¤Ÿ&„Øxx‚ÿ§)-%2767654'&54767#"'$473¸Â$6þÊ2 #déý­Gf>5þ÷ã¡À€þ¶?¸AËhXPA7.EB|=Q#!w*6ºú(  %þ¶{{qeVUI&b Š\^ˆ~B"ÿÿ‚ÿ§)+&‡÷ÊÿÿþÈÉÄ&M_¼“þµHî#"'$47332767654'3HdnùaP„mþ¸/¸1É]]LGÁL"f¸h8­²ÅD%jdëQ¬í45Þb`¸Þœÿÿþ (°'Ö––&;Ó?ÿÿÿ«þ @r'_ÿôþ>Qÿÿ‚þóÀJRÿÿ‚þóÀr&R_ÿôþ>ÿÿ‹ÿÆ ÞPÿÿ¸Â”¼`ÿÿ¼aÿÿRðbÿÿLcRÅ%'&547632&767#"'#'3ÈX\lTX\D8/0EÃ=’ ´%1œÇBx:=œ$!"4'´Qjr›þ©Ü!n›8jÿõâ$(327654'&#"327#"'&5732#"€-2!WZWXZV%2-Z(‘ .5_³ªŸ¾_52œ‘Z¡JkVרààÕÚ<…J¡Ð•ž¸y&ÄvþŠ»þÑ‹¦žÐ 3767"'&547632&#"327ÐAoDdN.†bpŒxXWs>0B7,gŒ`áp†5ÿµoU%m—®ao´3/AbM3))´I˜·áÿÿ<gÿÿ<hÿÿdòi‡ÿãð (@  üìôìì1@    ‘Œäôìì0"32$  ‹œœþhö þ÷þ þ÷PþÍþÌþÍþÍ3343 þsþ†þ‡þsyzáZÕ (@  ÔìÌüü1@     /ì2ôìÔì0%!5!3%=ü¤JÊeþ™ªªª+H¸Hûá=Õ · Üì2Ôì1¶  /ä2Ôì03!#3uÈýlÈÈçîýhüÃÕþá=Õ ¶  Ü2Ôì21·   /ìôÔì0)5!!5!3=ü¤”ýl”Ȫ¹ªÈá=Õ ·  ÜìÔ2ì1·  /ìäÔì0%!!!3!©”ü¤”Èýlªª ÈüŽá=ÕµÜì2Ä1¶  /äÔì0!#3!=ýlÈÈ”Öý*Õý«á=Õ¶ÜìÔì1µ /äÔì0!#!3!=ÈýlÈ”crý8ÖAÕ'µÜüÌ91´/ìÜÌ0¶‡‡ìì#3Aý“þÉSuÃüNÕúÛÅÖAÕ (¶  Üü<Ü̵  /ôÜì´Ü<10%!3!#ŸþþÉÉ¢ý“þ°þ ÃüN{î 2@ EEüìܲ@]ü9Üìì1@ • •‘ /ôìÔ<ì02>4."#&'.4>329[œ¸ZZ¸œ_ÈPGr……䆇䄄rEM»p`77`p_88úæ 1§Å§bb§Å§1 ÁyÕ@ ¯üüì1/ì03#Á¸¸Õú+qÿã!“/@ E  EôìÔ¶ 0   ]ìì1@    ŒôìÔì0 6&    ¹¹¹þúþ«†þëþz>þú¹¹¹þ†þëþzþë='Ù+@  ÜÄ2üÄ291@ /ì2903#3²³Âþç¸þçÂ6£ýQüÖ*¯=q33# =¤ú¤Ãþ¢þ¢qü½ýCºq @ üìÔüì1µ£ /ìô<0)3!3ü9¹U¹qý"Þºq @ ü<Ôüì1¶ £ /ìôì0!5!!5ü9üòqü“K“qOâ!%!!5!&'.4> 2>4."¼‡ü:‡RJr……ä 惃sKRþQ[œ¸ZZ¸œ“““{ 1¨Å©bb©Å¨1 Ÿp`88`p`88º¦ %@    ìÜüÔÌÌ21¶   /ìÜÜÜÌ03"3#!5!ò„…ñpü9Çœ fšžüø“qçî2@ EüìÔ<ì2ÔüÌ1@    ‘/ôìÔ<ì2Ì0!#!##"&6 54'&"3qv¹þCœø¼°±f^‹œ]8Æmr^:ÉüÞþÄ<ÖUåf˜þɃ]8˜ÆƒÁDÕ '@   üü<ü<Ì1@    ¯/ôìÜì0#!!!y¸ƒþ5Ëßý!Õ“þ/’=ÀÕ '@   Ü<ü<üì1@    ¯/ôìÜì03!!!¸ý}Ëþ5ßý!Õ“þ/’ºÕ %@ üü<Ôìì1·   /ììÔì0!!27654'&'2#!3œþÐ,R4,,=iXXXlýιÞýµ]Oz}I_“‡_Ò­‡…ÕýœÁ$Õ;@   ôܲ_]9üÔì@    /ôì999@ ìì10#4'&'5!½ˆ4B ¸5M‰ÃcAþïê¸æþÉq™_–ÜÜ9V“= Õ4·ÔÜü9Ì1@ /ô̲]ìì´]0 53#Tþ麸ßþÇö9ú+Áë!-@ #"üìÔìÔüì1@    ! /ì2ÔìÔì03432>324&#"!4&#"!ÁôÆ}x5%–ÓÒ^ˆº¤ZþýH¤Zl”þî¦ÿK--ÒX€hý&|‚€Å•þncƒ§Òò=‹Õ &@   Üü<ü<üÌ1¶  /ôÜ<Ü<0!5!3!!#ˆþµK¸Kþµ¸€”Áþ?”ü€=Õ9@  Üü<ü<Ü<ü<üÌ1@     /<ô<Ü<<ü<<0!!5!3!3!!#!ˆþµK¹øLþ´¸þ=“Áþ?Áþ?“üüºq!@ üìÔüì1¶ £ /ìôì0!!ü9¹Uqüq“ýµK=ÃÕ! #ÆýtFCþ0æþ0Bû~ú+ÕÁnÕ 4@ üìÜ<ü<Ôìì1@       /ìôìÜ<ì20327654'&+!!2/!!m¨]%i¤þ ;°„@ED\ŒÔýTôqQE=4."þµXEr„„rJS¸RJr…CEoJ¥[œ¸ZZ¸œOó“þ{ 2¦Æ¦1 þ{† 1¦ÆSV/þÞp_88_p`88Á}Õ @ üìÔìì1µ /ôüÌ0#!#}¸þµ¹Õú+BþÅÎÁ}æ #@   üü<Üüì1µ /ÔÌüÌ0#!#3}¸þµ¹¹âüOþü›þüÁmÕ +@   ü<ìÜ<üì1@      /ìôìÔì0!%!!5!!z;þ ôþ ¬ýTÞýµK“Ñ“ú+qOÕ $2>4."%#&'.4767673 [œ¸ZZ¸œTXEr„„rJS¸RJr…CEoJR¸"p_88_p`88Î 2¦Æ¦1 þ{† 1¦ÆSV/ †qOÕ(#&'.4767675!5!!2>4."¼XEr„„rJS¸RJr…CEoJRþµNþµþQ[œ¸ZZ¸œP 2¦Æ¦1 þ{† 1¦ÆSV/ ó““ýàp_88_p`88Ùb/ö²ÜÌ1² /ì0!!ÙVýªö”Bà¾f#"&/#332?¾¢E=9Qc´‹õ”t2 %xfþ¼" %/¯õxà $â¤Dp²/ì1²/ì03#¤ÌÌÌ¿î=f3#'#´”õ‹´´‹fþˆõõ[î¥f!5'3¥ü¶{ì¶î”æäBà¾f3326?3#'#"&'B‹x% 2t”õ‹´cQ9=Efâ$ àþˆõ¯/% "[î¥f5%3%[¶ì{î”äæ”šþ‡fÿS²/ì1²/ì03#šÌÌ­ÌÕF'µ/üÜì1´/<ü<03#%3#\ËËþyËËÊÊÊÈuº·  ü<ÜÜì1´ /ìÜÌ0#527#53ºò„…ñ€þõgšžÈuº·  üÜÜ<ì1´ /ìÜÌ03"3#Èò„…ñ  gšždû¶ ÜÜüÜÌ1µ /ìÔì03#!!×ÌÌþ³üMûÌýd“dÞ '@  ÜÜ<ü<ÜÌ1@   /ìÔìÔì03#3#!!×ÌÌÌÌþ³üM„ÌìÌÞ’ÿì÷“³ÄÜÄ1² /ì0'!! üõ““Åþæ¦'(033!!3'#67654'&67654&ƒÍÍÍþu‹dÑruxt‹NM€ñddx>DD>x˜II’‰þvŠýçþ!ßý þóRTšxŽXY…¯`aþæ£þw,0dc1-ýëþ!:;z{t‚ÿëÛ{*L@$% E+ü<ô<Ô<<ìÜü@!#91@‡Œ$+<ôìÔÄ@ ‡(‡¸+üììÔÄ0%"3254"3254#"54!#"543263 #4#"h??AA??A¸ùæ'+ææ¨,L½–¸â‰Wð@@@@@@@@üÁÍÉÐpØ·QQþ¾üÇ9ªŽ‹ÿ݇Œ/@@1(. #E0üì<ÌÜÌÜü<Ì1@‡‡!Œ0‡%*‡ ¸0üìÌÜÌÜôôìÔì0"3254"54$3  !2632&#"# 54-654!"`@@@CÒv»Bþ™þîþĉըŸŠ†iUvË«þ:Ö×þknL?o@@@@þìä‰Äþ«þìN;EjfŽÕa‘ùïe:.šº88U؉ÿìôŒ8327&'"254"%47&5476! #4'&# 63 #"'632# i60IKhhþ¯*)…“7!‹o¸^R¿þýX;*:ú9«ÌÌþu`þ/'"­€6OÔfq³þ÷ý¿A¼…tqLIþ¶ $\9.ȶlþQŒ!6@   E"üÌÜÌÔüÜü1@‡¸" ‡ ½"üìÌüìÄÜÄ0463 #"&'7325#'&&7'6m¢ÝešÍÖt ½"x¯‚ØÐÖCBC¦quýôýþùðÐ Ñóßh!Ï ACBBÿÿ—š )2@  #&E*üìÔÌÔÌÜ<ì1@ ‡ ¸*%/ÔÌÔÄÌôì0"32654& 4''&5432#5476°$ % U%|{¸eÈÙÚô6þ™ãL¸j` %"%:yxþÙý~)ŽR¦·Àh–††KKª>Ü‹þ †Œ 65@$- 3 (E7ü<ìÌÔÌÔ<ü<1@ 5/‡¸7‡&Œ7ôìôìÌÔÌ"32654&4763  !27632! 54-654!"#"`$ % õçºBþ™þ­ûïy#x—”¾Jþiþ:•O×þknLI¿Òo %"%0‰yKþéþpjNdfïD×þüþ”QcwþõùØiC‘|85s½rÿîô– *;@&%   E+ü<ìÜÌÌÔÌÜì1@"‡)Œ+‡¸&¼+ìôÌÔÌÔìôì02654&'&47&7'73%$æ$!% þ­¥¦lÜÝ6ZA“‹|¸ýÌý³• $! $ý˜Vmê-·Æˆ½G4 ¡p¨¾?ýÁý´ºÿã¼{1@ FüìÌÜÌÜì1@ ‡¸<üì@ŒôÄÜÄ0%"32544!  #"54$32¥@@@þ¹þµæëäÖ)ë@@@@ëÆÆþPÄÈÈŽÌvvÌüÇÿ⃡ .<@- " 'E/üìÌÔÌÜ<Üì1@ $Œ/‡-‡)¸/ô<ì2ÜìôÌÔÌ0%"32654&672#4#"#"'&#" #"53232l$ % L ™ò¸7*>(€z*M#6Õûò—&8"$ñ %"%3|þ©ü¶0Û¯qi½áþPÄÚÒ–Wu«ÿÿþ²|+?@-$'+ ,<ÌÜÌüÌÜÌÜüÌ1@ )‡!¸,Œ&½,ìôÄÜÄü<ìÄÜÄ0%"3254"3254 #"5#&767663 #4!" @@@þ™@@@®äï!ÙÓ¢7yÁ-ž¸þæ²^õ@@@@ö@@@@ý˜ÝÃã×ÇedÕmúÛ%£‹WÿîË‘ ,9@. $  )E-ü<ìÌÔÌÔüÜüÜ1@ ‡'Œ-+"‡¸ô<ìÌÔÌôì0"32654&4323254#4#"%$7"@$ % þùæì‹µ¶'(¸ujþ‘þ’ÕÓø %"%@ÙÕ¯ˆ¨Ì°Xþûüsghý©þ¹\—_ÿã¾"9@ $ E#üÌÜÌüÌÜìÌ1@¸¼#‡ ©Œ#ô<ìììüÄÜÄ0254#"53265$54767653!"'#W@@>¸z]Uþèâî]ƒã¸þi¹TrÙs@@@@üpeœgÌÝÛýu©±/üÑþ²ssHÿîs|2@  Eüì<ÌÜÌÜü1@  ¸¼‡ŒôüìüÄÜÄ0"325447&763! 3%$5@@@þÔ¶·åíþìMg¸ýáýõî@@@@ýRÚÄ&¶Â¼—þѨË'´ýLþBHÿîs2@  Eüì<ÌÜÌÜü1@  ¸—‡ŒôüìüÄÜÄ0"325447&76! 3%$5@@@þÔ¶·åíþìMg¸ýáýõ@@@@ý<ïÅ%·Â½­þÒ¨Ë'hû˜þBYÿîÌé E"32654&!"32654&&''"&5623253765$7465&'7$ % ý$ % K”fg´¦œüÔþ饤¸£›þçÓרIJÒ %"% %"%üIK£šbáëÇþv„„4þË‹‚ŒËî˜42@7-]fÅnþ9¥™ºÿãh%A@'$ F&üìÌÜÌÜüÜüÌ1@‡Œ&ô<ü<Ì@" ¸—&ìüÄÜÄ0!"'# 432!32533253"3254hþ©”fg´þ¾ýÓþ襤¸¢›¸ü>@@@ þÃJJ=|ßëÌþ\¤¤@þ¿¦§ôýÌ@@@@hÿï} -?@, (,$ E.ü<ìÌÔÌÔüÔì1@"&¸¼. ‡Œ.ô<ü<ÄäôÌÔÌ0"32654&2533253!"'# 47&5432d$ % A¤¸¢›¸þ©”fg´þ¾™‰æìèBÒ %"%ü³§4þË©ª/üÑþÃJJ=ð¦%žøÜ‰þñM§hÿï -?@, (,$ E.ü<ìÌÔÌÔüÔì1@"&¸—. ‡Œ.ô<ü<ÄäôÌÔÌ0"32654&2533253!"'# 47&5432d$ % A¤¸¢›¸þ©”fg´þ¾™‰æìèBÒ %"%ü³§4þË©ªèûþÃJJ=ð¦%žøÜ‹þóL‘§@ÿã`$@ÜìÜì1@ ¼‡ Œô<üü<03!23! '#"543225“¸ù¸þOþ³)3¶Æœ)`ý,þíÔüÅþ¾88œ–{‹rÿîô *;@&%   E+ü<ìÜÌÌÔÌÜì1@"‡)Œ+‡¸&—+ìôÌÔÌÔìôì02654&'&47&7'73%$æ$!% þ­¥¦lÜÝ6ZA“‹|¸ýÌý³• $! $ý˜Vmê-·Æˆ½G4 ¡p¨¾Úü&ý´‹ÿïŒ ,7@  '#E-ü<ÌìÔÌÔü1@+‡Œ.%‡‡¸.ôüÔüÔÜÌôì0"32654&4! ! &# ! ! '&54323 c$ $ ½þ€ýÔ6bæuøåþŒ’ýÄþ«‡UÓàK·X $ $è8$Ü{»ŒŸþþ›nE{ËÒN%Oÿîª 0@@2, %&E1ü<ÌìÔÌÜìÔìÜ1@‡%‡/Œ1!‡*¸ôìô<ìÌÔÌÔÌì0%"32654&&'&'&5! 765! '676%&4% $  ,«D ãÏù)@ ' 1#-E5ü<ÌìÔÌÔü<1@ ‡)Œ6/‡%‡!‡¸6ôìÜìÔüÔÜÄôì0"32654& 4%$54!232#"'&#"! '&5432h$ % $—þ{ýÝ¡Ëajjh@”Mó§qKÍy)ý«þLJmá×_œ‹ %"%þù½1EæüYl0þ”xP^b8RþÂþsu_|ÞÚ]FÊþ­ð¡'"2''&'$!32'&547"32?6A€€ñS2;9ŠÂ’ þhhÍÔÓÔ¤ÚNU×~œõþ± €û †+;9j–qÎÖÐ!þþæB¦a¦©þÈÿÿnÿå·€'îýuîúÉû_ÿ¨ +@   /ÌÜÌÜü991@ /üÌÌÜÌ0! &7623$'4'74"YýÇþ¶Ãåò#!A[VЍüÞB8?<þÿ¨kÙØP$U.FŠM?>={É{+@  EüÌÜÌÜì1@   ‡¸<üìÜÌÜÌ0 ##"2#"53254#"·¸ÿn=Úåá¦;C>@{þjýËVÁÇÇ®þR777ÿÿüšÉr&ïû@ìÿji·  /ìÔÜÔì1µ  /<Ì2ÔÌ0! ! !5 74! ûþ%þ&þ?þºþ˜%»~?>þÂþÁ~»Åû@ìi$@  /ÌìÔÜÔì1¶ /<Ì22ÔÌ0! ! 3!5 76! ûþ%þ&>Ôý©þ¹þ˜%»~?>þÂwJ~~»Åû@ìÿji*@   /ìÜÄÜÄÜì1@    /<Ì2ÜüÜÌ0! ! !5 74! #5ûþ%þ&þ?þºþ˜ßî%»~?>þÂþÁ~»ÅNÜÜû@ìi.@  /ÌìÔÄÜÄÔì1@   /<Ì22ÜüÜÌ0! ! 3!5 76! #5ûþ%þ&>Ôý©þ¹þ˜ßî%»~?>þÂwJ~~»ÅTÜÜüþý3þ¶ÿ²"36654'#"5432ýî‚AA\(D³ªeN[̼þÏo[$žºN[‹uÖü§ýŽþÈÿ¯%@ /ÌÔÌÜÌÜÌ1@ /ÌÌÜÄÜÄ0"3254"547&54323253ýró>Juum‚ž„²þ@’s> [yu?{EBXFþºÃû_ÿ¨ '656%"'&76! 4"3Y¨ŠVþ¥A!. Ùå{x9üá322674&#"ŽCC›òèÌjFPˆH OQ²µ$!%!¢p'(FnäJv-Oý!3]ï $ $ÿÿzÿãÈ{&úú0ÿÓÿö1, ("32654&&3 #"4/&5432N$ % þ¤s $ËŒeÓû‹qɘzm… %"%þù´ƒµþçÏ82®º´‘y¦œþ,v\ÿãë#"6@ E#üìÜì@! ÜìÜÜÌ1@ ‡˜#Œ#ôÄÜÄüü04$54%&&5! $#"57"3254ÂiþØó xÜ«äþâïï@@@ç¸ÜXº¸4 |ì`Pþ°¤Ù³ý÷ åÜÝ?@@@@¼ÿê‘ ""32654&5&'7!$#"47#®$ % þdt¦„.;àÕûœï %"%ȈÄ_øp•½¬˜þ‰ ñÐ8>™u“þ%˜t/;4#"#"'&#"$#&532327632! '&57"32654&à"3C2z7J,"Óû¶…/IN\=0‚¶BWþ–þ¡TO½3Hý©$ % Xt\DD\t] ñÏ5ö<\UCþþüªþ÷fˆwpv Ê gH %"%ýñýÆV@/ü1/Ì03#ý´´Vþ›û~àÿ£!'@ /ÔÌÜÌÌÜÌ1@  /Ü<ÌÜÄÜÄ0'6"%)56574 65ü+*+µ×þ„þU·‘G±óm++),}åþÝ….pˆƒ·\(úû>Ç.ó4"!27676327673!#5654#"'&'&#";&543ü.ð%2~*&IHHÜ„BO™g(þâÅÐLBC]i%>’e>.`h>3È‚A?×~= h‚\$…kb8:;-F¡_Zkf2)üNàþŒ !@© /<Ìü<Ü1@© /<Ìü<Ü053533##5üNØŽØØŽ¸ŽØØŽØØÿÿû øPüšöþ>r@ /ÌÜÌ1@ /ÌÜÌ0432#"73254#"üšÔÐÔЄLTPP¾´´ÈÈHHH‡ÿã` " 7654&' ! '&476‹^L:NbX1ˆcoŒþ÷þí€qo’À„h³½`™W®£ü Ä’éý޳¥”æ•Äcÿg¨œ&24764'&#"676'&'&5476 ¶€ òÒpH¼’gc/5pØä†I´u ¦þÎö€€ýê‡uËpHECle\gâÔUÔÜš‰su—ÈýϨ‡cÿy\±$24"27#&5432# '&5?$5+€€rÐ%3Ýàí]fÜþͦÏÓ¸þ”Ù|pHF€þP¶ÆØþäðfou—ÈT¢¥àéþÇþå©aþûpHÿ스/%24'$5432327#"'&#"%$'#"5432253â¶þ¶ï¡3]L/|€t¾kZ1„AQ½þfþÜ(3¶Æœ)¸ƒDj¾R—Ææ:jÄTþûh8KOÈpÆþtþ$68œ–{‹‘ýÖþícÿWα%24"$'&5?$532&'&32!€€­þâþñþ¾rŠÏÓ¸þ”Ù|T9ïlcÙÔì ~x¢˜€ý?Lv§T¢¥àéþÇþå©aþûmY<ÅÐäØÉÇKcÿWα-224"7&5&326532&'&32$'&324!€€ýBå‘äûœ¸³}b$|T9ïlcÙÔì ~x¢þâþñþ¾rŠø=C˜€Ëh¡(ªç­­¢½ë¤ ?fXþòmY<ÅÐäØÉÇKLvÈttYÿîô4@'&''"&54323253765'$543227#"$#""32654&²¥”fg´¦œýÔþ饤¸£›Ùþ¬úu ^|€u½IþòSL\ßü>$ % ,¥™IK£šbßëÇþv„„4þË‹‚jEašØÑüÃTþûùW8Ò‹þ¾ %"%‚þÛ{ &%"324"324#"54!#"543263 #4#"h??AA??A¸ùæ'+ææ¨,L½–¸â‰Wð€€€€üÁÍÉÐpØ·QQþ¾úÛ%ªŽ–þRpt MU"32654&254"#&76767%4#"#"'&#"$#&3232763276'767‰$ % ånnþ´vp†+-•"2D2z7J,"Óû¶„0IN\=0‚¶ŽJØ%•.3?­5xvþ„'ªQœ %"%ýž933¹hk//3wá±t\DD\tþß ñÏ5º<\UCþþýÈrT­«”F-2bG;þÐ"‚b,iÿö÷’ $5354#" #"524"Åm~¸þÀþŶ¥Óý¬€€‘þ¹ý©ejüsûþX°¤þ\Ìëß|9ü€~ÿçÉš LX"327$"3273253!"''&76324%$7&76%$5#0#&76262654&'&¨€A?à€A?‘fx¸þÔ…$8ŸÙÄ$+Rb,7þHþ£ÂåîuŠ æÓ¢5r—ýô$!% @@@@@@@mýÓ”žJü¶þ¾ceÆÁã“$3ýÖþ³- Ó/™×Ô‹uŠþꟵ² îÉcdûê $! $~ÿ瓚 I"327$"3273653%"'%5254%$7&76%$5#0#&7626¨€A?à€A?‘ Tc¸þéb*@þÖÛRX6&$þHþ£ÂåîuŠ æÓ¢5r—ý@@@@@@@mýo6 Jü¶þ½,/¸Ìáƒ7'ýÖþ³- Ó/™×Ô‹uŠþꟵ² îÉcdPi.".54>7!5!!"32>54&'7i7eŽ®ÊÚȬŽd7&KlGþÜêýqÇ”VXšÐxyÓšY²lÅ«Œc66c‹«ÆlJ˜Š7ÀÀ^ŸÐszÖŸ\[ŸÖ{6yEr2b\TZ@#!#".54>7332>53!w!KNM#h¶‡N&?Q*Ðnq-Nj=8kT3È“$ Kƒ°fWxc*s÷@nQ/+Lk?øú·¸ZÿÒ 5!4.#".54>2!þ‹/A%'B/+(¥=B=iŽ ‹f:yýý'D33D( R0oC‰EOˆc88cˆOü'þMPÃ.4.#"32>7#".54>7!5!!÷"@YmEg³„LL…³fjµƒJÌk»ÿ“þ¾oXØý´qý´ØXªG†xdI(Y”Ãji¶†MK…·kù¸ii¸ùÛ«uÅÅu­ÛZ8!4.#".54>32i+Kg<>lP-7:p0M6Nˆ·if²„L>@kK*0Rp?>†?¢1ill3e²„MF}­gûÂZ,#!#4.#".54>32!|þÝÏ/@%&@0&%•BE;hRP‹g;Óüé©(C03F(#P/M‘COˆe96`†PÛþFZö(4.#"32>7".5!5!>32&/Oj=kOýO‡·Î´…MþEŠHŽHj·†Mª€oP.)NpF@pR00R°f³…LL…³fa¶ýn0/I²P- %#"3!!"$&546$3!!£J˜×‰@@‰×˜þùþóÇþØÃ`aÃ(Æ ¾º] ÔwxÔŸ\FüéwÏ¢¡ÏwýÍþ2P:G!!3!nýâÉ!.xú1Z+(4.#"32>#".53>32`+Li=?jM*,Ni=>hL+„™i³ƒJÉEŽMf²„LK{W06\xCKxT.4XwýA_bK‰¾rþŸ62N‹¾püÿZö+4.#"32>#".5##!>32*+Lk?AlM+/Pj<@jM*ÌK„µih¶ˆNëЀIžHkµƒæ?pR0,PpEBnO--On©Î²‚KK‚²gcþBvýj12K„Zþ+"32>5!#".54>3!!5!!Q@lO--Ol@>iL+ÎÿM…³gh¸ˆON‰·iý‚LÁ.QoA@nQ//Qn@/ýùþÑe²„MM„²eh²‚J{¸ýÍüP‚S$!4.#"#4>3!!"632ˆ,Mh<>e-PÉK„²hŽþr>iM,‘f²„Lè?nR0*'R}þ g³†M¶1Tr@§aK„±fþPþ»c K4.#"32>2>73#".5#".54>2™*Lj“OwÏ™YV˜Ðz|ÕšXš0/'»:Yr?DsU0 E nǬŒc67d¬ÆØÈ­e7><qU'!R–‚kL)[ŸÖz{Ö[W›×üì.>#K€]59_|D 6cŒ«ÇlkǬd77d¬Çk{ÔZ'kE"1%P#".5!5!2>53K‚°eiµ…Lþ¢*-Nj|fH'Ïèe²„MO‡µfX¸ûðAqT12Vq>P&!#"&'.5467!5!32>534J“Ep®=AB7Dþ¡ƒ4+! )#$+e;:iP/Ì—05IGH¬eLšJ¶« )RpEn),/,Nj=öùÿZ""!#".54672>53!`þÝN†´gi·‡MNL™6--Nk|jN,Íå+ìþÑe²„MK„²gV·[„@n6@nQ//Qn@ýÍý]‚S#".5332>54&'7SL„²fi³ƒJÉ,Mi>=iL+>5{-H3èe²„MK„²gûç@nQ//Qn@6|>œ/dfd‚S 4.#!!2>7##!!!2ˆ/Oj;þæ;jO/ËJGHÜ“’.’ü¥ãg´†M@qR0ý¨,Nl?d¬EHCþˆºM†³PþºbF4.#"32>5>54.#"#".4>32™Y›ÒywКYXšÐx‚Ô—RÉ6a…O"?/$/ .@KZj>mȬŽd78eŽ­ÇkoÉ­‹c5[])D0zÖž[[žÖz{Öž[Wœ×ýwAoV5³'//!6cŒ¬ÇØÇ«Œc66cŒ«Çlƒúv?GOPb 14.+32>#";+##".4>3!2™>m’TEET’mýœFU’l>>l’UFk\¢Ü€EÌF€Þ¥^^¥Þ€W€Ü¢\Yª–qAüÌ@n†@o–WV•n?—~ÙŸ[þ£][ ÙüÚŸ[]¡ÙFä!#!3!3!þßÏþËÏéü靸3ýÍþF‚S!4.#"#4>32ˆ/Oj;53`E“Ig±ƒJÉ,Mg<:iO/Ë”02Kƒ°fëþ>mQ0,Mi=øùÿnB-#".'332>=#".533267653BL…´i`ª‚RÊ0Lc8;jO/FŽIi³ƒJÉ,Ni=:c'YËèg²„KS½kFzZ40Sm>ª1/J°gíþ@mQ.+(P|ú‚S!4.#"#3632ˆ+Kh<7g4QÉÉ‘’e±ƒL|?nR0++N|ýuþaK„±fý„Pk*!5#".54>32.#"32673°YªUlǬŽd77dެÇlpÒs[.\YS$wЛYXšÐy^ªP»r2@6c‹«ÆlkÆ«‹c6JHž,ZÕôÕžZ;;xýXZ)"32>5!#".54>3!3!Q@mO--Om@>iL+ÐþþM…³gi·‰NN‰·iÎÅ.QoA@nQ//Qn@/ýìþÑe²„MM„²eh²‚J3ýÍý5‚S !4.#!!2>7#!!#!#!2ˆ )þ4Ì)Ë2VsAþ4(Àþ˜É•AsV2ª*=&þÆ$;,S~U+ºþ 9þ/XZ.?#".'332>54.'.'.54>32#6.#".Kƒ±fb­…TÊ1Ng:jN-VU^Æ]4R8èe²„MR½lHzY3/Qn@72*63•Ue³…MT¿kH|Z4/Rn@)D BFRZ‚S#"'!!332>53SL„²f•‹’ü¥É,Ni=53 K„µih¶ˆNÿО/Pj732>54.'.54>32#.#"]¡Ú~|Û¤_-H3’K7>k‘TSj==jS8mV4/Rn@8gQ6Í';#wÙ¥aL~מY[Ÿ×{:omn:vQ©NV“l=53 ,T,PŠf:l #D;%?.Ìa=4.#"32>7+!!#".4>;5!5!54>2Ÿ-  -)//?%&@.¯%?/ç:fŠP¯Wþ©2WvCDwX22XwDpþÃ=½áíêå ýô Ó@Šx½Y9¹lþY…þ|þPþWô!%! %674#"&5! % %óþ1þ,ôlÕ·‰._‹z½þðþè+þ,ÔÍS©Ž‹.+RLo þÛ¤Tnþµ8þÇþÀdþ`ã'675$!2363 ! ##&!"#"32CxþšuçM6þŽþsþcÑÈžì¼*þ›¢rËÚööÚþêE)ËÝîÎØþ ýûPþ¢ýçlaØ•y°Zþ§þ“dþX¬!&732#"&5 ¬þ]þ[*³çèÏä8F›Œ‹ þe’]N/I3þü^@Þ[7œr·rþ2dþX«'!&732=6+537#&5! «þnþN ²øÖÅggºÏЈG‰ŠåïVþ¯zkB3L.íÄ·¦ŒúÿÊ@JKW~XþqÛ\,²d­!$75&7! &324'"6­þZþ^  ,CH!¿êããèÀ €’ùþøIJ:éQþ½›U,þX\þ¤þ•$ÔÖÚdÿüý56#"! !2363#"32ýþUóÉÎþTþcŸ÷D>êƒüôìâàî0þR•^<]þ€ýø¼¼ýõ€þþtdÿì«'6#"$! «þê+†æèƒ.þ!þT‰u®eþ›þì¬uŠ«õdþY­! 473254+5365!5 ¬þWþb¤ æí퇇ÚþŒ2þÿþm“NEIJþú(ò˜ÅþbÆC+þødþ`3675$%2363363 565&#'#"#'#&#4%"fDjþP‹ÕQU»ËORÌþ‰¾ÓÛ “ ËÆ ‘ ÞÎ…¡¯ýTÇgˆ@! «®Ï®¸º²þ5þ„‡§yã<þ÷øøþïøøþ¹þO-þ‰ã6ˆd«! !234#"#!#"2«þmþL¤œCóÀ»{£½æçûÒÛþ%  }>±×eþÂý«þ‰þ~‚Ëû! )!363#"7ËþYþ`°þPlB¨¼ìëëî ýóå  rþž€ýøyþ_þ§dÿöß56#"#'#"$!2363 ÞþHúÇæ“ åÆ÷þL†éDVÛƒþz’–a!tþÝüü#þŒþër–˜…ÂÂd¬!! 4732+53274'$53¬þXþ`4§"çêêgz¯ïþÒ¶ï/ºçîþžc7Qiþìb6‡È•!6°G))þûË=HdþY¬2! 3325 '%5%«þUþcÀÚøþ|Óþ Cþ½Gþkoã Š4YîþΧŒ_ndÿöó9$5$#"#'#"$%7367 óþ>ØÞ ÝÛþBÇdž)ýoœÙQTÜ7þ-n–gDPþËîî5þ°þÀk–nÓ1¢ wþÓ5áá‚Ëñ! 3324&547ËþcþT¶øáÉœW\îýýîüþ‰wØ ?”c-'9ÆdþYÛ: %3! ! %#d’ÿýÊýÁ¼†þ·‘þ¸6‰*Qþ«&ýqý)Ýý¯QûGýåF<þY¯!! 4732#"#'4+536363$¯þWþe7¨%áù•„ … ¡nj³1HŽ:þp¨|BZˆþüšÜËËÜŽš™þmdþYà(0! 3! =4+5326#"! !2363 #"32Ìý÷þ¹.Jbhhv¯Ìþ^þ_œëLJßlɵý-ùÝÝùDþ)×o¥—ûþãWþ(ìøÐÆþwþöO*¼Ú|þ˜þ£dþY­ñ! 4732#&3$!33­þWþ`S%ãõöÏ}þÜ{—F´þpj]P0Gþü9dꬪ*,wkGdþVÞ(675$!236356#"#'#"#&%&,E[þš‡×UQÉ—þsÓÖÚ“ ßÍv±þð¯®ÇSÓ•óÂÂþKþg¥ªl+(þíüüþ›þ&Žoö˜G(ŸdþY­! 365## %3253­þWþ`¾âö3¯þ`:^Ûãö³þtþüàbå"®‚Sûþ¯ë d«%! %34#"#54#532363 #"!2«þþ:Ÿ™I‘ƒ~V`z?:¢.·ðâ Æïþ 5È»!#¹¡ÁÁþµý&•þ‰þ‚ÿìÉ'6##! '6#"3636úÎÅ·ÁÜû²_a¤¼¯?óõrþÛþräÛƒ)þÅýµªjþÇ`M¿êþø€hdþY$%#! 7! 6+532'6'&'3«Éþ)þ¥h*&£š’o{·Z‰…ËX?þçþc#¼ìL‚þcþÂþi2øˆêw/.­}.8d¬ñ ! !233#"32«þWþ` ¯2²¼ëâãëýûýsNü‚þvþ‚Ë%!236;'"#54#"363 ! 332#"‚Ë9@zpf~³e:£Ÿþ`þW¹ïãáññ#»»£ Ã#!Å þ …ýãþÇþÇjŽFþYñ%-! 4724'&' !33#3'6#&Žþ\þ^U%æéˆqYþé#M^¸½R„ý……«¢Žþm“ahi Cþü coz49 Èýí;U¢U7HUihIµ¸­‚Êñ ! 3363#72ÊþaþX¶M‡ »èãçä ýó äý¡„ýøyþ‰þZþY¢×! 4772&'&'5%53%¢þVþbZ,àþÃ=’dþœ¯´þw“¥xYUoþøË`y¥ªŒ´”Bþ…“±šszbd«+! 5332'4'#53256+53274'&53«þ_þZ¿âílµµdeµ³[ØéºØéš”žhþ˜ eeþìÔ®–zs˜„U !ÐdÒŒ/6‰~:_dþYù=! 63!2'4'#53654+53254#5325&'&'3ùþTþ76h¹T%.)ûhˆˆhfŠŠfhˆˆh›‘¦Ÿœ²²²²±²|þÕØ+ ã?þùþåáþ©þ³ ÆŽ†¤Šœ„‘ŽR!"²H61˜¥"(Ÿ»&·’0:dþó¬27&% 7&'6#"•2ð¡¤û>à–Þ•æçìáöd$! %35#$ 3#3%#" 5;54 ¬þXþ`œîîþdHrrrþÐîÞÌþ4Þîþ4¶þ±Of¡kQþ¯Ø”¡²ÎÉÓcÖØÃdþX2&!"'#!525#"3$%2363 #"321þZ†Gþ\ææçøþK¤æVOæ¥ÀéÝáåýévŸþŽïBpþþœ”õþÈÈþpþ¬¸ïdþY_!! %$54#"'! ! 4'7_ýõþGD˜µ¥ `Uþ¯þÈ6I@šbþY§srg¼Ÿ8A:þ¨ÔƒMþîþÜ){6\l‚þYÊ(3324'7%#"'#723! ‚Àíßß«îþf–Bðß»þcþVþýÌ¿0ý±?âþõþ7YãþûþpŒdþW $!6=3! 47$$5! eØÎþ¾þڞòþŒþªïþŒ¿þíþèk¹þHþë©užµŒLLþæ8çTWJÿ&)ü*dÿµ•54&#"'675&%'%"©t_‚C‚Ctâ¨ý—‘?þìèh]²|KytJfqIµ8ý¯=žþÛ£Ê&*“d2ïã 3#3#3#dÀÀѺºþ/½½ã°½®è®dõ 2"4;%"4#"32;ÐÕѹëþþFí|pux$þïþãLµRQžÂ´ºÉhØ=@ B••ÔÄÄ1/ìôì0KSX@ììììY %##™œýÇåýÆ.ûœdªú+ÕÿÿhK'œÀEgÿÿhÕ"ÿÿhO'ÐÀtgɰÕ@·üìÔì1µB¯/ì990KSX@ííííY s£û³çÂüNÚýÕý‡äÿÿɰO'Ð)tiÿÿɰN'È)uiÿÿɰÕ'‰eiw^Õ?¶ÜìÔì1µB¯/ì990KSX@ííííY 5´ü]£ªûçÂþ(þ&þðxäyÿÿw^O'Ð1tmÿÿÉØ'e5‰ÿÿ6Ø'‰¡eÿÿÉÕ&‰g5ÿÿ6Õ'‰¡gÿÿÉO'h5‰ÿÿ6O&h‰¡ÿÿÉEÕ'i•‰ÿÿÉEÕ'‰°iÿÿÉEO'j•‰ÿÿÉEO&j‰°ÿÿɰO'ÐÉ0jÿÿÉóÕ'm•‰ÿÿwEÕ&m‰°ÿÿÉóO'n•‰ÿÿwEO&n‰°ÿÿw^O'Ð?0nÉ •Õ3#ÉÌÌÕ̉œçß!38öîýYgg`ýªnCü½^Úå·^nýç7€]ý€^Úå·7ånýçn7]€]‰0­d"&533265453­ŸæŸzWA@XzCs  s!þßAWX@+ö‰!­U#454&#"#462­zX@AWzŸæŸBþßö+@XWAþß!s  ‰0µU!5!2654&#!5!2£þæ@XX@þæsŸŸ0{X@?X{Ÿæ ‰0­U 4&#"32>"&4623X@AWWA@XzŸæŸŸæC?XW@AWX³æ  æŸH>Þ %™‡þãþ'‡þãÎ1üð11üð‰œ¥ß· ]]ÜìÜìÄ1¶÷‘ô<ì203!3ýäœCü½Cü½É~•K3#ÉÌÌK͉­!5!­ýÜ${‰1­V #5#53533ØzÕÕzÕÕÕ{ÕÕ{‰1­‡##5!ØzÕ$ þ%Û{{ÿÿ:Ø'‚UeÿÿÇÕ'‚âgÿÿÉ"Õ'‚=iÿÿwqÕ'‚ŒmÿÿhÕ9ÿÿhK'œÀE“ÿÿhÕ)ÿÿhO'ÐÀt“w^Õ:µÜ<Ôì1³B¯/ì0KSX@ííííY7 5wMû³çû¶40»ý‡äýˆÿÿw^O'Ð)t•ÿÿw^N'È)u•ÿÿw^Õ'‰¶•w^Õ:µÜìÌ21³B¯/ì0KSX@ííííY%5^ûçû³¶¶xäy»ýÐÿÿw^O'Ð1t™ÿÿÉÕ'‘5‰ÿÿ6Õ&9‰¡ÿÿÉÕ'“5‰ÿÿ6Õ&“‰¡ÿÿÉO'”5‰ÿÿ6O&”‰¡ÿÿÉóÕ'••‰ÿÿwEÕ&•‰°ÿÿÉóO'–•‰ÿÿwEO&–‰°ÿÿÉóO'š•‰ÿÿwEÕ&™‰°ÿÿÉóO'š•‰ÿÿwEO&š‰°ÿÿw^N'È1u™‰› ß<´ÜÌ291µB÷‘ôä0KSX@}}}}ííííY5 üéý`sb€bsþщœß³]Üì1´÷‘ôì03‰œCü½ÿÿ²ÿã)Õ8ÿÿ²)K'œðE®²)ò*@ 8AüK°TX¹ÿÀ8Yìüì1µ •‘ /<ôì03! #4&#"²!!Ë®Âî¤$*þÖþÜü\‹ðÓÓðüuÿÿ²)O'Ðçt®w^Õ¶ 2 Ü<ôì1¶• •/ìôì07! )5! )w¾5þáþËþBŸ²–þhþPþa¦.,¦þ—þ€þ~þ–ÿÿw^O'ÐÇt°ÿÿw^N'ÈÇu°ÿÿw^Õ'‰¶°y`Õ· 2üìô<1¶ ••/ìôì0%! )! !`þaþPþh–²ŸþBþËþá5¦¦j‚€i¦þéþÔþÒþèÿÿy`O'Ð"t´ÿÿÉÿã¬Õ&‰¬ƒÿÿ²ÿã¥Õ'‰¬ÿÿɬò'®ƒ‰ÿÿ²¥ò&®‰ÿÿɬO'¯ƒ‰ÿÿ²¥O&¯‰ÿÿÉóÕ'°•‰ÿÿw½Õ'‰(°ÿÿÉóO'±•‰ÿÿw½O&±‰(ÿÿÉÕ'´½‰ÿÿy½Õ'‰(´ÿÿÉO'µ½‰ÿÿy½O&µ‰(ÿÿðÕ'´½$‰œßµ ~ÜìÌ21@ Ý÷ Ý‘ôìôì0# $54$!3#"3nþðþÿnÍ™¾¨ nË××Ên“ ¹{ÿÿ²ÿã•ß'«|¬ÿÿ²•ò'«|®ÿÿw}ß'«d°ÿÿy¯ß'«–´F>ò %@ üìÜì21@ • •‘/ôìÜ9î0"32654&"$54$32#Bz­È_€²ÌÌ€²ëþï#ÙìÊR¤—¨”©“¤—ý3IðìúáðìûêÿÿÉÁK'œÀEÌÉÁò %@  üì2Üì1@ • •‘/ôìÜ9î0"32654&#4$32#&Åf̲€_È­þTÊìÙ#þïë²R—¤“©”¨—¤ý3ý{ìðáúìðÿÿÉÁO'ÐÀtÌFÿã>Õ (@  üìÜì21@ • •Œ‘ìôîÔ9ì0%2654&#"3#"$54$3Bf̲€_È­¬ÊþðìÙþÝ벃—¤“©”¨—¤Í…ûêìðáúìðÿÿFÿã>O'ÐÓtÎÿÿFÿãüN'ÈÓuÎÉÿãÁÕ (@  üì2Üì1@ • •Œ‘ìôîÔ9ì0%2654&#"672#"$53Åz­È_€²ÌÌ€²ëþÝÙìþðʃ¤—¨”©“¤—ÍIðìúáðìÿÿÉÿãÁO'Ð5tÑÿÿÉò'ÊÚ‰ÿÿF³ò'‰ÊÿÿÉ?ò'Ì~‰ÿÿÉò'‰|ÌÿÿÉ?O&‰Í~ÿÿÉO&͉|ÿÿÉÿãÕ'ÎÚ‰ÿÿFÿã³Õ&ΉÿÿÉÿãO'ÏÚ‰ÿÿFÿã³O&ωÿÿÉÿã?Õ'Ñ~‰ÿÿÉÿãÕ&щ|ÿÿÉÿã?O'Ò~‰ÿÿÉÿãO&Ò‰|ÿÿðÿã?Õ&$Ñ~‰‹ ß $·~ ]Üì2Ôì1@ ÝÝ÷‘ôôüÔì02654&#"632#"&53ÉXP^J\TaaQ_”¬·‰•«ùVFTHUGQK})†„Œ~†„J8‹¸ß 2654&#"03#"&54632xOaT\J^P«•‰·¬”_ùKQGUHTFV}iý¶„†~Œ„†ÿÿF‘ò'«xÊÿÿÉÃò'«ªÌÿÿFÿã‘ß'«xÎÿÿÉÿãÃß'«ªÑF>ò¶ ÜìÜì1µ •‘/ôìÌ0#4$32#4&#"Ê#ÙìÊÌf€²úáðìûꤗ©ÿÿÉÁK'œÀEêÉÁò· üìÜì1µ• ‘/ôìÌ04&#"#4$32÷²€fÌÊìÙ#’©—¤ûéìðáúÿÿÉÁO'ÐÀtêFÿã>Õ¶ ÜìÜì1·• Œ‘äôîÌ032653#"$5²€fÌÊþðìÙþݾ’©—¤ûêìðáúÿÿFÿã>O'ÐÓtìÿÿFÿãüN'ÈÓuìÉÿãÁÕ· üìÜì1· •Œ‘äôîÌ03#"$53326÷ÊþÝÙìþðÊÌf€²¾úáðìû餗©ÿÿÉÿãÁO'Ð5tïÿÿÉò'èÚ‰ÿÿF³ò&è‰ÿÿÉ?ò'ê~‰ÿÿÉò&ê‰|ÿÿÉ?O'ë~‰ÿÿÉO&ë‰|ÿÿÉÿãÕ'ìÚ‰ÿÿFÿã³Õ&ì‰ÿÿÉÿãO'íÚ‰ÿÿFÿã³O&í‰ÿÿÉÿã?Õ'ï~‰ÿÿÉÿãÕ&ï‰|ÿÿÉÿã?O'ð~‰ÿÿÉÿãO&ð‰|ÿÿðÿã?Õ&$ï~‰‹ ß¶] ]ÜìÔì1· Ý÷‘ôôüÌ03#"&53326y·‰•«aO\T•Œ~†„Jý¶PML‰‹ ß32653#"&5T\Oa«•‰·•QLMPJý¶„†~ŒwÕµ:Üüì1´•/ôì0!#!5!Êý)¡+ªÿÿÉjK'œ©EÉjÕ@ •:üìì1/ôì03!!É¡ý)ÕªúÕÿÿÉjO'ЩtwÕµ:Üüì1´•/äì0!5!ü_×Õú+ª+ÿÿwO'гtÿÿwÜN'ȳuÿÿÉjÕ/ÿÿÉjO'Ð5t ÿÿÉ5Õ&‰ÿÿw¯Õ'‰ÿÿÉíÕ&‰ƒÿÿÉ¥Õ'‰ÿÿÉíO'ƒ‰ÿÿÉ¥O&‰ÿÿÉ5Õ'‰ÿÿw¯Õ&‰ÿÿÉ6O'‰ÿÿw¯O&‰ÿÿÉíÕ' ƒ‰ÿÿÉ¥Õ& ‰ÿÿÉíO' ƒ‰ÿÿÉ¥O& ‰ÿÿðíÕ&$ ƒ‰œ¥ß´]ÜìÌ1¶Ý÷‘ôôì0!!3Œýä nC‰œ ßµ ~ÜìÌ21@ Ý÷ Ý‘ôìôì0! $54$)!"3 þúþðþÿþçÍ™¾¨ nË××Ên“ ¹{‰œ¥ß3!5ýä Õü½nwÿãŠ} (@  ÜÔìÔ9ì1@ •Œ• Ôì2ôî0"32654&'2#"$547!5œ‹È__ȘúþïìëþïLýšÓ¦n¨””¨ƒ‘ªáÝìððìªjªÿÿFÿãY 'œiÿFÿãY} )@  üÄìÔ9ì1@ •Œ• Ôì2ôî0"32654&'!!#"$54$C™È`^È‹›ý™MþïëìþïÓ‘ƒ¨””¨n¦ªªjªìððìÝáÿÿ ÿã'Ðiÿ<ÅwŠš "@  ÜÔìÔ9ì1¶•• /ì2Ôì0%2654&#"!5!&54$32˜È__È‹œûêfLëìþýª‘ƒ¨””¨n¦ªªjªìððìÝáÿÿwŠ'Ъÿ<!ÿÿwŠ'Ȫÿ<!FYš #@  üÄìÔ9ì1¶•• /ì2Ôì0%2654&#""$54$32!C›‹È^`È™úþýìëMgª¦n¨””¨ƒ‘ªáÝìððìªjªÿÿFY'ÐTÿ<$ÿÿÉÿãH}'¾‰ÿÿwÿãÐ}'‰;ÿÿÉ3š'$Ú‰ÿÿFYš'‰y$ÿÿÉ3'%Ú‰ÿÿFY&%‰yÿÿFY'ÈTÿ<$‰ß\ä 2654&#""&546 !Éj>_IEcI_£¬(¬0ƒMJBSKFXCIn~|„‡‡„Q;n‰‹.ß"&5332653ܨ«abaa«‹†„Jý¶PMMPJý¶„†‰ß\ä 2654&#"0!5!&546 _IcEI_>jýmƒ0¬(¬£MICXFKSBJnn;Q„‡‡„|~wŠšµ ÜÔìÌ1µ• •/ìÔì0%2654&#!5!2#bŘûêúþï쥒¥ƒ‘ªáÝìðÿÿFY 'œ¤ÿ2FYš¶ üÄìÌ1µ ••/ìÔì0%"$54$3!!"Cìþïúûê™Æ¥¥ðìÝ᪑ƒ¥’ÿÿFY'Фÿ<2wŠšµ  ÜÔìÌ1µ• •/ìÔì052#!5!2654&ìþýúûê˜Åõ¥ðìÝ᪑ƒ¥’ÿÿwŠ'Ъÿ<4ÿÿwŠ'Ȫÿ<4FYš¶ üÄìÌ1µ•• /ìÔì0"3!!"$54$3CbÆ™ûêúþýìõ’¥ƒ‘ªáÝìðÿÿFY'Фÿ<7ÿÿÉHš'0¾‰ÿÿwК&0‰;ÿÿÉ3š'2Ú‰ÿÿFÆš&2‰1ÿÿÉ3'3Ú‰ÿÿFÆ&3‰1ÿÿÉHš'4¾‰ÿÿwК&4‰;ÿÿÉH'5¾‰ÿÿwÐ&5‰;ÿÿÉ3š'7Ú‰ÿÿFÆš&7‰1ÿÿÉ3'8Ú‰ÿÿFÆ&8‰1‰ß\ä"3!!"&5463ÜRiPY’ýnž£¬§vDZHCn~ƒ}‡w^Õ %5-5 ^ûjü–çüìü¶¶2äÔÕä2»éì¸éF Õ ·? Üìüì1µ• /ôÔì0!3#$53TCcÊþúþ ʨþ™þ½ýXonÿÿÉ2K'œ EKÉÕ @ ? üìüì1µ •/ôÔì053#3 ÃÊþ þúÊcCT¨-nþcþ‘ý¨þCÿÿÉO'Ð tKF Õ · ? Üìüì1µ •/äÔì0%#5%3# ÊôÊcþ½þ¬--noXýþ½ÿÿF O'УtMÿÿFÌN'È£uMÉÕ @  ? üìüì1µ• /äÔì0%!#3#Ãþ¬þ½cÊôÊ-gCþý¨þ‘þcnÿÿÉO'Ð3tPÿÿÉäÕ'IÚ‰ÿÿF‘Õ'‰üIÿÿÉÕ'Kƒ‰ÿÿÉ´Õ'‰KÿÿÉO'Lƒ‰ÿÿÉ´O&L‰ÿÿÉäÕ'MÚ‰ÿÿF‘Õ&M‰üÿÿÉäO'NÚ‰ÿÿF‘O&N‰üÿÿÉÕ'Pƒ‰ÿÿÉ´Õ&P‰ÿÿÉO'Qƒ‰ÿÿÉ´O&Q‰ÿÿðÕ&$Pƒ‰œèß ¶ ] ] ÜìÔì1·Ý ÷‘ ôôÜì04&+3#X¸ÏHãìÆÇœ¶þ¹å¬V‰‹ìî+.#"#"&'532654'&/&'&54632¸Cw7Bh#-8GC>©¤=‹JG‰BAm'./G?;¢=~ÇH)@@V\`RʺªÒV\`RʺªÒ¾ÓhZáþç·%XÓhZá·þÛFÿãlò632#4&#"#"&3326tҪºÊR`\VҪºÊR`\V¾X%·ÂþçáZhÓý¨þÛ·ÂáZhÿÿFÿãlO'ÐóteFÿãÜ·'32654 !"/.#"3"54!2ú!„rz|K«È£þÙ¬×42 „swU¡ùÒ¤'­Ö4°X˧|`å¥þí•þÈ~pX˧|`å¥J š3þç~ÿÿFÿãÜ'Ћÿ<gFÿãÜ·'763 #52654&#"# '4!"326(24׬'£Òù¡Uv€r„!24Ö­þ٣ȫK|zs„°p~þÈ•¦þ¼¥å`|§ËXp~þç8•­=¥å`|§ËÿÿFÿãÜ'Ћÿ<iÿÿÉÿãò&‰d•ÿÿFÿãƒò&d‰îÿÿÉÿãò'e•‰ÿÿFÿãƒò&e‰îÿÿÉÿãO'f•‰ÿÿFÿãƒO&f‰îÿÿÉÿãÙ·'gý‰ÿÿFÿãU·'‰ÀgÿÿÉÿãÙ'hý‰ÿÿFÿãU&h‰ÀÿÿÉÿãÙ·'iý‰ÿÿFÿãU·&i‰ÀÿÿÉÿãÙ'jý‰ÿÿFÿãU&j‰À‰Ï¯æ'>72#52654&#"#"&'463"326[*'so¾b„I=J>",BˆþRðÓÓð‹ü\þÜþÖ*$²j¥t_UV²ÿã)ò '2654&"#"'&54632! 33265ð,B:d:B–0<~JI§jŠ­þßþåþæþßË®Âî€B,">>",BˆVU_t¥jýNþÜþÖ*$¤üuðÓÓð²ÿã)ò '"2654&'632#"&5! #4&#"ë,B:d:B–0<~JI§jŠ­!!Ë®ÂîUB,">>",BˆVU_t¥j²$*þÖþÜü\‹ðÓÓð²ÿã)ò '"2654&74&#"#! #"&547632ð(B:d:BB®Ã®Ë!!­Šj§IJ~<UB,">>",Bˆ®ðÓÓðüu¤$*þÖþÜýNj¥t_UVÿÿ²ÿã)O'Ðçt²ÿÿ²ÿã)O'Ðçt³S^Õ$264&"&546; )5! '&óVšþhþÞÌf€²Ê#Ùì¾ fýœ¤—©’úáðìw_ò:@ ÜÜ9ìÜì1@ B •‘/Ìô9ìÌ0KSX·ííY%4$32#4&#"!7gìÙ#ʲ€fÌþÞþhš¾Xìðáú’©—¤ûédfFÿã.Õ=@ ÜìÜì1@ B •Œ‘ä9ôîÌÌ0KSX·ííY#"$533265!>þðìÙþÝʲ€fÌ"˜šü¨ìðáú’©—¤ýœfwÿã_Õ?@  ÜÜ9ìÜì1@ B •Œ‘ä9ôîÌÌ0KSX·ííY '!32653#"$5gþªš˜"Ìf€²ÊþÝÙìþðýôfdû餗©’úáðìÿÿ‰ ÍK'é Úÿÿ‰ Íò'ê Úÿÿ‰ ÍO'ë Úÿÿ‰ÿã;ä'ìýÚÿÿ‰ÿã;O'íýÚÿÿ‰ÿã Íä'ï Úÿÿ‰ÿã ÍO'ð Ú‰¨(ä (2654&""&546323326=3#"&=ÉbFntnPX”¬¬”•«/¹Q,CE«ma¬ZT:KMMKFHn…|p‡‡pX;§oBGj§§„‡ˆƒ9‰¨$ä 3>2654&"!&546323326=3#"&=!"&54632!2654&"ÉbFntnP?+¬”•«/¹Q,CE«ma¬ûÊ”¬¬”•«/ëbFntnPZT:KMMKFH;Xp‡‡pX;§oBGj§§„‡ˆƒ9…|p‡‡pX;T:KMMKFHFY‰<@   üÄìÌÌ91·B•• /ìÔìÌ0KSX@ ííY!"3"$54$3!7Yûê™ÆbìþïúXýòhòþþ‘ƒ¥’¥ðìÝáUšÿÿFY‰'Фÿ<ÜwþŠš8·  Ì9ÜÔìÌ1·B• •/ÌìÔì0KSX@ ííY!26544#!w™Æbìþýúü¨ gX‘ƒ¥’¥ðìÝáþ«šÿÿwþŠ'Ð\ÿ<ÞFþYš:@  üÌìÔÌ91·B • •/ÌìÔì0KSX@ ííY'!"$54$3"3!Yýœhü¨úþýìbÆ™XþišUáÝì𥒥ƒ‘ÿÿFþY'Ðiÿ<à‰µ\ä'%!"&5463"3!\þ±=.ýÌž£¬§RiPY’¬÷Bè~ƒ}‡nDZHCwÕ%#535!53!3##qÉÉý=ÊýÃúÉÉÊà¤Ô­Ðý-þ‚¤à!®Õ%#5#53!3!3´ÊÉÉúýÃÊ=ýÉààà¤~Óý0­Ô¤ÿÿ!®O'ÐtäwÕ533#!#5!5#5qÊÉÉý=ÊýÃúÉõàà¤þ‚ý-ЭԤÿÿwO'ÐÚtæ!®Õ5##3!35!535´ÊÉÉúýÃÊ=ýÉõàà¤þ‚ý-ЭԤÿÿ!®O'ÐVtè ›èß33#!#!5#53‰Ïþ¥‘mþ ßunfyþ~ÔnÉÿãÁÕ ,@  üì22Üì1@ • •Œ‘/ìôîÔ99ì0%2654&#"672#"'"#3Åz­È_€²ÌÌ€²ëþÝÙ·{O{ʃ¤—¨”©“¤—ÍIðìúáH+ÕÿÿɰÕ'sZÕ@  2üìôì1¶• • /ìôì0# !3! !ôþËþá5ôÊþaþPþh–²/þéþÔþÒþè/ú+j‚€i‰œNß !!!5!—©ýùý;þßVýnV‰ìN#5!5!5!53!!75$iþÎ2þÜ$iþä*þÖmÖ‚œ°°Vxn°°Vþ¢þßnÕ††ý™  u!s #'#37ÿ ‰Í͉û‰»»sþÏþ¾ööH+ßßÿÿ‰—ò'ÊY£ÿÿ‰ ±ò&Úèsÿÿ‰ Éò&Ûêÿÿ‰ ÉO&Ûëÿÿ‰ÿã 7ä&Ûìùÿÿ‰ÿã 7O&Ûíùÿÿ‰ÿã Éä&Ûïÿÿ‰ÿã ÉO&Ûðÿì÷åŸ!!ùüŸ¨ÿìÿŸ!!#!þYÊþXŸ¨ý öÿìÿÆŸ !!###!ÚþYÊ÷ÊþXŸ¨ý öý öÿìÿ‡Ÿ!!#####!›þYÊ÷Ê÷ÊþXŸ¨ý öý öý öÿìÿ HŸ!!#######! \þYÊ÷Ê÷Ê÷ÊþXŸ¨ý öý öý öý öÿìÿ Ÿ!!#########! þYÊ÷Ê÷Ê÷Ê÷ÊþXŸ¨ý öý öý öý öý öÿì÷”!3!!¨Ê§ûçŸõý ¨ÿì÷Æ” !333!!¨Ê÷ʧú&Ÿõý õý ¨ÿì÷‡”!33333!!¨Ê÷Ê÷ʧøeŸõý õý õý ¨ÿì÷ G”!3333333!!¨Ê÷Ê÷Ê÷ʦö¥Ÿõý õý õý õý ¨ÿì÷ ”!333333333!!¨Ê÷Ê÷Ê÷Ê÷ʧôãŸõý õý õý õý õý ¨ÿìÿ” !3!!#!ÂêÕêþ?êÕêþrŸõý ¨ý öÿìÿÓ”!333!!###!ÂêÕêîêÕêþ?êÕêîêÕêþrŸõý õý ¨ý öý öÿìÿ–”!33333!!#####!ÂêÕêîêÕêîêÕêþ?êÕêîêÕêîêÕêþrŸõý õý õý ¨ý öý öý öÿìÿ Y”#!3333333!!#######!ÂêÕêîêÕêîêÕêîêÕêþ?êÕêîêÕêîêÕêîêÕêþrŸõý õý õý õý ¨ý öý öý öý öÿìÿ ”+!333333333!!#########!ÂêÕêîêÕêîêÕêîêÕêîêÕêþ?êÕêîêÕêîêÕêîêÕêîêÕêþrŸõý õý õý õý õý ¨ý öý öý öý öý öÿìSC !3!!#!¨Ê§þYÊþXŸ¤þ\¨þ\¤ÿìSÆC!333!!###!¨ÊöʨþXÊöÊþXŸ¤þ\¤þ\¨þ\¤þ\¤ÿìS„C!33333!!#####!¨ÊöÊøÊ¤þ\ÊøÊöÊþXŸ¤þ\¤þ\¤þ\¨þ\¤þ\¤þ\¤ÿìS FC#!3333333!!#######!¨ÊöÊøÊöʦþZÊöÊøÊöÊþXŸ¤þ\¤þ\¤þ\¤þ\¨þ\¤þ\¤þ\¤þ\¤ÿìS C+!333333333!!#########!¨ÊöÊøÊöÊøÊ§þYÊøÊöÊøÊöÊþXŸ¤þ\¤þ\¤þ\¤þ\¤þ\¨þ\¤þ\¤þ\¤þ\¤þ\¤ÿìÿ”!33!!# #!êÕššÕêþrêÕ™™ÕêþrŸõþòý ¨ý ïþöÿìÿ”%!3!!#!!2ÐÑü^DéD^þ£þ»éþ»þ£ÏŸ÷þæ¨õý ¨ý öþÿìÿWcŸ !!!5!5!!!wûûùþÑùüsþXŸ¨þ¨¨þ ÿìþ ·Ÿ#5!! !!'!%'! !7%!77'7! Ëþºýô þúþwƒþúƒƒþúƒþwþú ýô‰ƒþúƒƒþúƒƒƒƒƒ‰þú÷¨¨þþu|ù||ù|þ‹ññþ‹|ù||þ|||||u ÿì÷ G7+/37;?CGKO!5#535#535#53533533533533#3#3#!!#3%#3%#3#3%#3%#3#3%#3%#3¨ÊÊÊÊÊÊÊ÷Ê÷Ê÷ÊÊÊÊÊÊʦö¥ë÷÷þ?÷÷þ?÷÷‚÷÷þ?÷÷þ?÷÷‚÷÷þ?÷÷þ?÷÷Ÿ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨𨨨¨¨¨¨¨¨¨¨¨¨¨¨¨¨ÿì§‹Ÿ!!!!¶øüÊŸúaO¨ø¨q:#[!' 7#þ}þCrŸþar¼Ÿ¨þCrŸžrþDÿì:ž[! !„¼rþaŸrþCþ}Ÿ¼rþbþar½=` !#!#3!ÃfþfäúþÀÀþí`ýG [`3!!!!!!!! Ùjýöóþ ý/þ¾tšè`”þÓ”þˆ“þíÍýÚ&{ÿão{4=J%#"'&=!.#"5>32>32#!3267#"'&32767%2654'&#"JÔ„â„„N Ì·hddddÐj§||MIؽii~ûþþST—`¶Te__ZŽjkü÷SR‰™\]i߬A@o™\]›Z^‘’úZ¿È5®*,=>` #% 54)3#4+327#!5#53!2xþþþúÜÜÞìòòì¹ÖŒçÖþ9||¶ÅÔ™µ´f¤_‘þڪИr–¤ø¤Äÿÿqÿãç{Fºg`32654&#%! )sÙËËÙþº7F0þÎþ¼þÉÍüÇÇÖÕÇ“þñþàþßþð$g` ! )#53!#32654&+º7F0þÎþ¼þÉ––zÁÙËËÙ`þñþàþßþðõ¤¤þŸÇÖÕÇþ̺‹` !!!!!!ºÄýöóþ ý/`”þÓ”þˆ“‚ÿãÅ|1#"&'5327654'&+5327654'&#"567632¿p<54& #.54! ì++ãþfã++ª$$>:#tNP³ÆÆ³PNM¥]ý*þÓþúU¢3MY + 3267>54&#"'>3 '# 5467'7²ý°*(èÈOuš))ëÓHn.—MÅw834¨O³MÆxþíþÇ43¨Nç)gA“¬\*g>—©}66þïñ]ŸC‹_’56ð`¡?‹`qÿãÃ{&/=5!&'&#"5>3267632#"'&'#"'&732767276'&#"qN ff·jÈbdÐj òQGhiŒñˆ‰‰ˆñŒijBNèâ„„¹RR‰™\]”VVUV•“¬VVZ¾dc44®*,nmn67œþíþ윜78lkp‘’ĘZYWWžþ´st’tsèÈÇtuq/u{ 4&#"#32²¬“”¬Âñð/ÇéçÉ8þÈþìqÿãu/ 32653#"4¬“”¬Âþïñðþî/ÇéçÉþìþÈ8—Ì`!264&#%!2#!#N[cc[þH¸¼ÁÁ¼þÿ·Åþ˜^¬^›ªþ¶ªþ>2`!.54763!##"#676#";µjpklÙ„¹©a;;?¢Ã®545ØÆw?@@?wÆ Œi¢QPû Ù%$qþá2^66É**TS++2`!&'&'3;3!"'&546#"3µ7545®Ã¢?;;a©¹þ|Ùlkp Æw?@@?wS66^2þáq$%Ùû PQ¢iŒþcQ++ST**ÿÿ<m`κÿà`$ 653 &53æs¹ÙþXÙ¹}Ž¢³ý;ÛààÛÅýM¢®L+ö%!5!2654&#!5!#TýZŸŸŸ¾¤ý…`¬fcðL¸||–¬¸¸B²tÂÈ®NÛø5353!5!2654&#!5!#®ÊÊÊŒýZŸŸŸ¾¤ý…`¬fcðxÌÌþzÊʤ¸||”®¸¸D°vÀÈ/þª{&#!5!2654&#!5!27654'&#!5!#¸|vz{âý\ž¡œ¾¤ý‡ž¢MN`_£ý‡`®gb>> EÀ‚¯__¹ru¦¹99wSS¹¹?°yzVUÿÿ=`YÿÿV5`ZÿÿXÛ`]xÿãº`73264&+5%5!2 'СӀ„{n ýÔþöF—oäþ}ɽBd»d>““þÂJŸm ¶7³ü{3!!³Iý{ªü/ÿÿ=`N—ì`#!#ì¶þ¶`û ·üI`ÿÿ—Ì`-Z^`367653#5&'&3¸U9Vˆm±¸¯mŠÄV9S`ý1MsÉ,þÔþìœ}îî}œ,þÔÇuMÿÿLs`Ç œhà !3#'!#¹­Zõg„VþXV†qþ`ü¼ÖÖœ–à!!!!!5!#!Šþ.¿þAÞý¢þ³eŠWÐ"à_ø_þÑ_ÖÖDXþI·œà &327654'&#327654'&#%!2#!þÌg1221g̼^-..-^þÅE‘OO)(N^h¬žþ¯+þÎ&&MO%%Xü@? ]65dL.- rUpzœ•à 327654'&#%! )þšÂ[ZZ[Âþ瀀þðþûƒývNO©¨NN]eeרfeœÝà !!!!!!Rþ-Àþ@ßý¢à_ø_þÑ_Sœ±à !5!!5!5!5±ý¢Þþ@Àþ-àü¼_/_ø_HŒ~ï$5#5!#"'&547632&'&#"326ÐNJYXeÝ|}}|Ý\SRFFPOW­WVVW­Cjá]þ™/rsÌÍsr'y5UVª©VUœLà 3!3#!#΀€þ2àþ©Wü¼Žþrœþà3#àü¼ÿ½¶þà 3+53265A@1(TFàüö—DE`Tlœià 33 ##¦¤þ-ô§þ<àþŸaþzþB“þmœÈà3!!Êý·àý_œÛà 33###½ðò½|ò€ó{àýÇ9ü¼ÞýÃ=ý"œGà 33##«¡|¬þ_{àýE»ü¼»ýEœGà ##3G|þ_«{¡àü¼»ýEDýE»HŒ¯ï"327654'&$  '&üŠRQQRŠ‹QQQQþ¯Œwvvwþtww“[\Ÿž\[[\žŸ\[\vvÆÅvvuvŒGŒ>ï@"327654'&327654'&'52#"&54767&'&54763ÂsCDDCstDCCBšR65<%€j<=0ER^X65`l<=ca¸·Ä==ll*6RI¢)++LK,++,KL++5##,&)$%LY+8:6iG2278PyAA‚yP87'21I.*œÞà 32764'&#%!2+#þ Y0110YþážQQQQž ƒþÆ))–))]?@ö@?þ°œ[à #'&'&+#!232654&#=)&''ˆy.,,LŠ¡PO)*þs \^^\$ )(GäÖTþŸD<32#"&'#3t45¸5445¸5þª$pMPPPPMp$uucä@AA@ä@AA86Z[þØ[Z68^gGŒ¾3#5#"'&76322764'&"Jtt%78N€PQQP€N87þš45¸5555¸5°Sü™^8Z[([Z¡ä@AA@ä@AAGŒÕ#!32767#"'&547632&'&#"Õýë@AsC?>>>BAD©bc^]ŸŽSSt44Va:: ï2j88a WW•š[ZQRmT3210YGŒÕMK° SX@ ÔÌÄÔÌ2K°SK°QZK°T[X½ÿÀÿÀÿÀ888Y1@   ÔÌ/ÌÔÌÔÌ0Y5!.#"5>32#"&73267G‚sC}>?ƒC©Å»ŸŽ¦thVau»2koa®•šµ£mTebXTŒb2&'&547632.#";#"32767#"&5476ùG&%HG{065>=f,K,,+*Ib]W-155_;65-9553–¢+,î$$4O,, ^$'U13 `fa<))RŒ`1#"'&'532654'&+5327654'&#"5>32»FLHG{065>=23-KX+*Ib]V.156_:65-9j2–RQ,+¼ H4O-+]4$'U 12  `33a<))G­¾ 14'&#"327#"'&'53276=#"'&763253J44^]4444]^4¨PP¥=7633223r99$88N‚OPPO‚N88$tmà=>>=à=>>F NO e 45k37XX"XX7_z¨î3#53ztttýô‚‚uœç 33 ##uuZ”þ‰†—þšuýþþÛþ²2þÎuœ{"4@ $ #ÔÜ32>32#4&#"²tHKYhuu'oMLl+yRowtHJZiþþžw[Wk\þžsa97EBEB‰~þ…wZXku­Ä4@ ÔÜþzx66X6V‚YYk\þžsa8BDGŒÏ 6@ ÔÌÔÌK°SK°QZK°T[X»ÿÀ ÿÀ88Y1@ ÔÌ/Ì0"32654&'2#"&546‹]ml^]ll]—­­—˜¬¬Çqq‚poƒW®›š¯¯š›®GŒu 67632#"'&'532764'&#"G0336¥^_]^ :5311213p?>>?p3121÷ XX™—YY _ ?@æ@? GÕÏ4'&"#46320T66º67z¬˜—WVÕoBAA@q›®WW›GŒÏÕ27653#"'&50Â66º67z¬˜—VWÕoBAA@q›®WW›u­ì#3>32#"&$4'&"27êuu$pMPPPPMpf45¸5445¸5úþ³b_86Z[þØ[Z6¡ä@AA@ä@AA#œÛÁ3#;#"'&5#535ìïï0Hww†33UUÁ²Pþ¬M,V-,vTP²nŒ½3327653#5#"&nt''N^67tt+78Jy~”{þˆY,-65\cý`9†nÇAÔ!5!27654'&#!5!#þU¦e22<KLÇg#"FS10gg%dAl88u{(#"&53327653327653#5#"&Q+<=Rnxu$$IZ54t$$KY45tt(78LMlE!"Šzþ‰[+,64\cþ‰[+,66Zcýb;F&œÕ33#&{ÝÜ{þøžýòýy­Ü #! !&'3254554#"ít n°þÏ…9F}µþÛæ”¥Ãþê^Ø™83•þö¸a°ž _·{¯­Õ3#5&+532ã›Ü{þãt¹<,|­þ|æýïïÔGXGŒÏï+&#" '&54767&54!232654'&'&yA– §‚JZVWþÒVWW!/b‹LþÌ+"766^]l9=P(r(B4?Kœ”WXXWšr]$,O˜'þø(@ä?A‚jp69G­  )"27654'&'2##5"'&5476734 )=;67-!XQVVQs~SVV@h)%661F¼Qþ:5}t?3XJO¥˜ZUâáUXš R=\ ,Ajq@:©%­Ä'#&+53;'&Í^Á‰sa,£(^Á‰þïra,£×þƼGX]×:þDþøFYÿÿzîg óýdÿÿu‚uýdÿÿnÿð½sjýdÿÿ&ÕsmýdÿÿyÿÜonýdÿÿÿÕsoýdyÿï‚67632#"&'#44&#"326²&_%s€  €No%tþi\[jj[\i92µþض78þ³"{q‚‚qrÿÿGÿ xqýdÿÿ%ÿÄtrýdºþV£{(!2.#">32#"&'#32654&#"ºþa¬QQžRµ´9²|ÎüüÎ|²9¸¸¥•”¥¥”•¥F³,*½¿[cbþÆþýþüþÆbcª#ÈÜÜÈÇÜÜÿÿœLàFt„`5!#3#3!53#53t¨¨¨¨ýð°°°¼¤¤þ¬¤þत ¤T‘þV/%+53276'7#3/¤´F0j&*¸¸š®ÖÀœ06”ú†GŒ¾#367632#"'&$4'&"27»tt%87N€PQQP€N78f54¸5555¸4ú_s^8Z[þØ[Z¡äA@@AäA@@GŒu&'&#"32767#"&54632u1122q>??>q22110h;Ÿ¼¾¥533÷` @?æ?@ _²—™° GKv+325&#"47&'&54632&'&#"632#"Z%0\R@5`$^¾¥4412/412q>??5{¼Ò3î *š &;/Z–™° ` ?@æ@bŠiGŒÏ.&'&#"32654'&7#"&54632''7'37 i:;n\[nO$$ZY‘’²®‹ dɱrPѺœ =67TŒb1#"'&'5327654'&+532654'&#"5>32½N+,QR–2658-56:_651.V]aIV-+K-32==l/|GHLî ))¿¿unn77wU:8PýÝ#P,i/0ÿé­\+53276=1#5333î43r,Brrtnœ x66XU P#ýÝPG­¾ ,5#"3276#"'&'53276=#"'&54763JÆ]4444]^44tPP¥=7633223r99$88N‚OPPO‚Ýë>=à=>>=¢ýÛ NO e 45k37XX‘‚XXn­½3327653##"&nt''N^67tt+87Jy~”{þˆY,-65\cüžO9†Iœ– 5333##53#Irtggttt™\þæ\ýýj‚zœ~ ;#"&5îC,rfþpUWlwIœ– 5!#3!53IMjjþ³o³\\þE\\»Iœ–5!#3#3!535#535IMjjjjþ³ooo³\\¾\¡\\¡\¾ÿV­`3#"54;33#'#"3276ztte¿ªztry "3rKNB ‚ü,|ssýW?#5˜$ z­~3;#"&5ztC,rfäüáSVXlx[­`+53276'7#3`34r,Bttòax66XS güïqœƒ3!!q“ýîýÝ_u­{467632+53265&7454&#"#4'&#"#367632Ÿ+=32#4'&#"î43r,B0t*pJz>?t'(N^66‘x66X6V~a88BDþ…wY,-56\u­U 4'&#"#367632;#"'&5P''N^66uu)89Jy?>0B,r34Y,-56\þžsa8BDþzV6X66xqœÄ 33##q«-{«þÓ{þùý~úþGŒÏ 2#"'&5476"!&'!3276‹—WVVW—˜UWWUõº6/‘/1þw &6^]6&WW›šWXXWš›WWW@9\[8ïE-AA.G®Ï&.#5!#3!535&'&5476767654'&O—¢—pFVVFp—þ^—nCWWCnt6%66%ª4#76$§\\ŒFW›šWG…\\†FWš›WE[þ*,ApoA-þ9Ô*Aâ@+F­a:.#"#"/;#"'&=32654'&/.547632;1j8W*,]({44MNˆ9> 0B“ r34@?>=RX l)k`GF‚@rýb/$+*MW33 V6X66x"j2-*TIX00ÿé­Ê476;#"+5326z73zno>43r,B0‘]Me30U:ýJx66X6#­ÛÁ3#;+5326=#"'&5#535ìïï0Hw43r,B0†33UUÁ²Pþ¬M,ax66X6V -,vTP²^Œ!533!33##5#"&=)3276^ntgtuut+87Jy~Úþš''N^61™\þæþæ\ý`9†‚Y,-6/G‹Ø&5!327654'&'5!# '&54767GE()78Z[78*,?”G$"ZYþäYZ!"J³\{':?KY7667YR8>#{\8?>LƒRRQR€R<=:uŠ’2653#"'&53„QHuDE…†EDuHâPZƒþs{>??>{þ}ZPzœ¿31+"&53?27654'&'&gËH#"YZŽ,rftA Z87)2:0Ä8?>LƒRRlwþpU67YQ8C&œÕ# #3Õ{ÜÝ{ žœýòs7œn !!!5!G'þL´ýÉ´þ\^þ=R^Ã7­ý!!#;#"&=!5!G'þL´C,rfþ>´þ\^þ=R VXlx ^Ã7^n#47#5!5!3632#'3254#|`å´þ\'þLn&ŠÒ m,7œ!!^ÃR^þ=¢ŠjR37¨£!2#"'&'5327654'&+5!5!hCQ>63``°;??C5~Ex>?::hnþ\&§ =;M|CD m**PJ*)]R^GÏ !32767&'&"2#"&76Sþo/6^]6/ +6º6,Ç—WVVW—˜¬VV*€MWXMÞmGYXFoƒvwþ^wwî¢wvü[îÿ¥f!5!73þ°ý«¨‹î”äü[îÿ¥f3!ýPõ‹¨îxä”ü[îÿ¥f#'!5þ°õ‹¨ýéfþˆä”ü[îÿ¥f!!#ýPUý騋f”äüBîÿ¾f 3#'#3þ5”õ‹´´”õ‹´fþˆõõxõüBîÿ¾f 73#'#ýË´´‹õ”´´‹õfõõþˆõõxÿÿþhÕ'Ö¼$ÿÿ{þ-{'ÖTDÿÿÉìN'Эs%ÿÿ®ÿã¤N'¸>EÿÿÉþ‡ìÕ&%Ô¥ÿÿºþ‡¤&EÔšÿÿÉþÀìÕ&%—ÿÿºþÀ¤&Eÿÿsþu'l'ÉLv‰ÿÿqþuçf&©vCÿÿɰO'Ðýt'ÿÿqÿãbN'¸þ>GÿÿÉþ‡°Õ'Ô®'ÿÿqþ‡Z'ÔzGÿÿÉþÀ°Õ&'èÿÿqþÀZ&GOÿÿÉþw°Õ&'z[ÿÿqþuZ&GzöÿÿÉþ°Õ'ˆ¦ù&'ÿÿqþZ'ˆ^ù&GÿÿÉ‹Z&(îÿÿqÿã^'ÿÙÿHÿÿÉ‹Z&(ìÿÿqÿã^&Hÿ×ÿÿÿÉþ‹Õ&(Þ±7ÿÿqþ{&HÞ°7ÿÿÉþv‹Õ&(°³ÿÿqþv{&H°›ÿÿÉþu‹m'Ï¡u&(z¾ÿÿqþuH&H'š‡zKÿÿÉ#O'Ðvt)ÿÿ/øP&I› @ÿÿsÿã‹&*‹2"ÿÿqþVZö&J‹IÿÿÉ;N'Ðs+ÿÿ®dN'¸>KÿÿÉþ‡;Õ'Ô+ÿÿºþ‡d'ÔKÿÿÉ;P&+j@ÿÿÿídN'¹>Kÿÿþt;Õ&+zþîÿÿÿÿþtd&KzþßÿÿÿÿÉþ9;Õ&+ß ÿÿºþ9d&Kß•ÿÿþv”Õ&,°ÿJÿÿÿÓþvg'°ÿLÿÿYZ&,þþtÿÿÿôF&óþþaþ¼ÿÿÉjl'Éâv.ÿÿºœl'ÉZvNÿÿÉþ‡jÕ&.ÔÜÿÿºþ‡œ&NÔ‘ÿÿÉþÀjÕ&. ÿÿºþÀœ&NvÿÿÉþ‡jÕ'Ôš/ÿÿÁþ‡'Ô'O ÿÿþˆjk'µ*u'Ô™/ÿÿÿýþ‡S1'qÿ(;âÿÿÉþÀjÕ&/ŽÿÿÿôþÀJ'ÿOÿÿÉþjÕ'ˆ—ù&/ÿÿÿÞþ\'ˆÿù&OÿÿÉl'Ésv0ÿÿºf&Pv”ÿÿÉO'Ðwt0ÿÿº'›FPÿÿÉþ‡Õ'Ôt0ÿÿºþ‡{'ÔæPÿÿÉ3N'Ðþs1ÿÿºd'›QÿÿÉþ‡3Õ'Ôþ1ÿÿºþ‡d{'ÔQÿÿÉþÀ3Õ&1÷ÿÿºþÀd{&QŒÿÿÉþ3Õ'ˆþù&1ÿÿºþd{'ˆ•ù&QÿÿsÿãÙZ&2fÿÿqÿãu &Rÿ²þ²ÿÿsÿãÙV&2lÿÿqÿãu&R'joÿÊrÿÿsÿãÙZ&2jÿÿqÿãu^&RÿµÿÿÿsÿãÙZ&2hÿÿqÿãu^'ÿ¹ÿRÿÿÉl'Éžv3ÿÿºþV¤f&Sv2ÿÿÉO'Єt3ÿÿºþV¤'›ÙSÿÿÉTN'Ѓs5ÿÿºJ&U›ÿÿÉþ‡TÕ'Ô}5ÿÿºþ‡J{'Ô UÿÿÉþ‡T1'q}; ÿÿºþ‡Jõ&qÿ ÿÿÉþÀTÕ&5åÿÿTþÀJ{&Uÿÿÿ‡ÿã¢O'иt6ÿÿoÿãÇ&›%Vÿÿ‡þ‡¢ð'ÔŠ6ÿÿoþ‡Ç{'ÔVÿÿ‡ÿã¢m'Éâv'Ф’6ÿÿoÿãâf&V&›ŒVvÿÿ‡ÿã¢W&ä6ÿÿoÿãǰ&#›" ÿÿ‡þ‡¢O'Лt ÿÿoþ‡Ç& ›*ÿÿÿúéO'Ðrt7ÿÿ7òN&W›ÿ#>ÿÿÿúþ‡éÕ'Ôq7ÿÿ7þ‡òž'ÔèWÿÿÿúþÀéÕ&7bÿÿ7þÀž&ôWÿÿÿúþéÕ'ˆrù&7ÿÿ7þ'ž'ˆÿèù&Wÿÿ²þ‰)Õ'Õð8ÿÿ®þ‰X`'Õ{Xÿÿ²þv)Õ&8°òÿÿ®þvX`&X°ÿÿ²þ)Õ&8Þà7ÿÿ®þX`&XÞu7ÿÿ²ÿã)Z&.8ÿÿ®ÿãXì&+v–†ÿÿ²ÿã)4&ü28ÿÿ®ÿãXô'üÿÈþÀXÿÿh}&9žíFÿÿ=7&Yžmÿÿþ‡hÕ&9Ô¼ÿÿ=þ‡`&YÔ^ÿÿD¦r'Ëõ|:ÿÿV5k'C ZÿÿD¦r'Éõ|:ÿÿV5m'v†ZÿÿD¦N'jõ>:ÿÿV5'jEZÿÿD¦N'Ðõs:ÿÿV5&Z›GÿÿDþ‡¦Õ&:ÔûÿÿVþ‡5`&ZÔJÿÿ=;O'ÐÊs;ÿÿ;y&[›gÿÿ=;N&;jÊ>ÿÿ;y&[jfÿÿÿüçO'Ðps<ÿÿ=þV&\›fÿÿ\m'̾u=ÿÿXÛf&]ˆÿÿ\þ‡Õ&=ÔÙÿÿXþ‡Û`&]Ô1ÿÿ\þÀÕ&=ÇÿÿXþÀÛ`&]ÿÿºþÀd&KfÿÿòN&Wjÿ->ÿÿV5&ZœBÿÿ=þV&\œÿÿ{ÿãa&D€Úÿÿ/øP&A›ÿý@ÿÿþ‡hÕ'Ô·$ÿÿ{þ‡-{'Ô!Dÿÿhð&$º¼uÿÿ{ÿã-{&DºTÿÿh:&$ÿÿ{ÿã¯Å'ÿ—þ‹Dÿÿh:&$ÿÿ{ÿã-Æ&Dÿ”þŒÿÿh[&$ÿÿ{ÿãæ'ÿ–þ‹Dÿÿhu&$ ÿÿ{ÿã-' ÿ–þ‹Dÿÿþ‡hm&„Ô¯ÿÿ{þ‡-f&¤Ô"ÿÿhZ&$ ÿÿ{ÿã-' ÿ‘þªDÿÿhZ&$ ÿÿ{ÿã-' ÿ’þªDÿÿh‹&$ ÿÿ{ÿã-5' ÿ”þªDÿÿhY&$ ÿÿ{ÿã-&D ÿ’þªÿÿþ‡h’&ÄÔÀÿÿ{þ‡-&ÅÔ3ÿÿÉþ‡‹Õ&(Ôžÿÿqþ‡{&HÔ‹ÿÿÉ‹ð&(ºžuÿÿqÿã{&HºÿÿÉ‹^'Êžu(ÿÿqÿã7'ž—HÿÿÉ:&(ÿÿqÿãèÅ'ÿÐþ‹HÿÿÉ‹:&(îÿÿqÿãÆ'ÿÐþŒHÿÿÉõ[&(îÿÿqÿã׿&HÿÐþ‹ÿÿÉ‹u&( êÿÿqÿã' ÿÑþ‹HÿÿÉþ‡‹m&ŒÔžÿÿqþ‡f'Ô‹& `ˆ‹ÿÿZð&,º#uÿÿD|&óº ÿÿÈþ‡”Õ&,Ô.ÿÿ·þ‡ƒ&LÔÿÿsþ‡Ùð&2Ô'ÿÿqþ‡u{&RÔsÿÿsÿãÙð&2º'uÿÿqÿãu{&Rº}ÿÿsÿãÙ:&2lÿÿqÿãÎÅ'ÿ¶þ‹RÿÿsÿãÙ:&2jÿÿqÿãuÆ'ÿµþŒRÿÿsÿãÙ[&2jÿÿqÿã½æ'ÿ¶þ‹RÿÿsÿãÙu&2 eÿÿqÿãu' ÿ¶þ‹Rÿÿsþ‡Ùm&–Ô'ÿÿqþ‡uf'Ôs& tˆsÿÿgÿãk'É'ubÿÿvÿãÓf&vscÿÿgÿãk'Ë'ubÿÿvÿãÓf&Cscÿÿgÿãð&bº'uÿÿvÿãÓ{&cº}ÿÿgÿã^'Ê'ubÿÿvÿãÓ7&žscÿÿgþ‡&bÔ'ÿÿvþ‡Óë&cÔsÿÿ²þ‡)Õ&8Ôõÿÿ®þ‡X`&XÔ{ÿÿ²ÿã)ð&8ºîuÿÿ®ÿãX{&Xº}ÿÿ­ÿ÷_k'Éîuqÿÿ°ÿãif&v{rÿÿ­ÿ÷_k'Ëîuqÿÿ°ÿãif&C{rÿÿ­ÿ÷_ð&qºîuÿÿ°ÿãi{&rº}ÿÿ­ÿ÷_^'Êîuqÿÿ°ÿãi7'ž€rÿÿ­þ‡_&qÔõÿÿ°þ‡ië&rÔ{ÿÿÿüçr&<Ër|ÿÿ=þVk&\C!ÿÿÿüþ‡çÕ'Ôv<ÿÿ=þV`'Ôt\ÿÿÿüç÷&<ºr|ÿÿ=þV‚&\º`ÿÿÿüç^'Êru<ÿÿ=þV7&žw\ÿÿqÿçäa&> Qpÿÿqÿçäa&> ‰Hÿÿqÿçäf&> ^”ÿÿqÿçäf&> k„ÿÿqÿçäf&> _„ÿÿqÿçäf&> l ÿÿqÿçäm&> `vÿÿqÿçäm&> mDÿÿha& Qÿ#ÿÿha& ‰þóÿÿôf'Œ ^ÿ|ÿÿôf'Œ kˆÿÿf'® _ÿSÿÿXf'ð lÿoÿÿ™m&1 `ÿQÿÿàm&x mÿNÿÿ…ÿãÈa&B QDÿÿ…ÿãÈa&B ‰9ÿÿ…ÿãÈf&B ^ ÿÿ…ÿãÈf&B k%ÿÿ…ÿããf&B _Oÿÿ…ÿãf&B lRÿÿ-a'#¢ Qþÿÿ-a'#¢ ‰þÿÿ7f'#¬ ^ÿ|ÿÿIf'#¾ kˆÿÿ­f'#" _ÿSÿÿéf'#^ lÿoÿÿºþVda&D Q¦ÿÿºþVda&D ‰¢ÿÿºþVdf&D ^€ÿÿºþVdf&D kpÿÿºþVdf&D _„ÿÿºþVdf&D l®ÿÿºþVdm&D `œÿÿºþVdm&D m‡ÿÿéa'%® Qþÿÿæa'%« ‰þÿÿçf'%¬ ^ÿ|ÿÿíf'%² kˆÿÿnf'%3 _ÿSÿÿŸf'%d lÿoÿÿ¯m'%t `ÿQÿÿÊm'% mÿNÿÿ›na&F Qÿÿÿ‘na&F ‰ÿ ÿÿÿ°¹f&F ^ÿ'ÿÿÿºÇf&F kÿ<ÿÿåf&F _ÿQÿÿÿÒîf&F lÿ=ÿÿÿËnm&F `ÿÿÿÿÆnm&F mÿÿÿAa''® Qþÿÿ5a''¢ ‰þÿÿKf''¸ ^ÿ|ÿÿKf''¸ kˆÿÿÇf''4 _ÿSÿÿf''p lÿoÿÿ"m'' `ÿQÿÿ)m''– mÿNÿÿqÿãua&L Qxÿÿqÿãua&L ‰nÿÿqÿãuf&L ^eÿÿqÿãuf&L kTÿÿqÿãuf&L _ˆÿÿqÿãuf&L l‘ÿÿÿãüa&-# QþÿÿÿãVa&-} ‰þÿÿÿãOf'-v ^ÿ|ÿÿÿãYf'-€ kˆÿÿÿãf'-6 _ÿSÿÿÿãPf'-w lÿoÿÿ•ÿâ*a&R Q=ÿÿ•ÿâ*a&R ‰ÿÿ•ÿâ*f&R ^'ÿÿ•ÿâ*f&R k!ÿÿ•ÿâ*f&R _`ÿÿ•ÿâ*f&R lWÿÿ•ÿâ*m&R `8ÿÿ•ÿâ*m&R mÿÿIa'2b ‰þÿÿÿf'2 kˆÿÿf'26 lÿoÿÿ3m'2L mÿNÿÿ‡ÿã'a&V Q^ÿÿ‡ÿã'a&V ‰Tÿÿ‡ÿã'f&V ^Yÿÿ‡ÿã'f&V k^ÿÿ‡ÿã'f&V _¤ÿÿ‡ÿã'f&V l‹ÿÿ‡ÿã'm&V `cÿÿ‡ÿã'm&V m^ÿÿa&6N Qþÿÿqa'6¢ ‰þÿÿif'6š ^ÿ|ÿÿuf'6¦ kˆÿÿCf'6t _ÿSÿÿyf'6ª lÿoÿÿm'6B `ÿQÿÿPm'6 mÿNÿÿqÿçäf&> }tÿÿqÿçäf9ÿÿ…ÿãÈf&B }Tÿÿ…ÿãÈf:ÿÿºþVdf&D }ÆÿÿºþVdf;ÿÿÿnf&F }þãÿÿ¦˜f<ÿÿqÿãuf&L }{ÿÿqÿãufYÿÿ•ÿâ*f&R }0ÿÿ•ÿâ*fZÿÿ‡ÿã'f&V }Mÿÿ‡ÿã'f[ÿÿqþVäa& ¡HÿÿqþVäa& ¢HÿÿqþVäf& £HÿÿqþVäf& ¤HÿÿqþVäf& ¥HÿÿqþVäf& ¦HÿÿqþVäm& §HÿÿqþVäm& ¨HÿÿþVha& © PºÿÿþVha& ª PºÿÿþVôf& « PFÿÿþVôf& ¬ PFÿÿþVf& ­ PhÿÿþVXf& ® PªÿÿþV™m& ¯ PëÿÿþVàm& ° P2ÿÿºþVda& ½ÿ8ÿÿºþVda& ¾ÿ8ÿÿºþVdf& ¿ÿ8ÿÿºþVdf& Àÿ8ÿÿºþVdf& Áÿ8ÿÿºþVdf& Âÿ8ÿÿºþVdm& Ãÿ8ÿÿºþVdm& Äÿ8ÿÿþVéa& Å PžÿÿþVæa& Æ P›ÿÿþVçf& Ç PœÿÿþVíf& È P¢ÿÿþVnf& É P#ÿÿþVŸf& Ê PTÿÿþV¯m& Ë PdÿÿþVÊm& Ì Pÿÿ‡þV'a& õYÿÿ‡þV'a& öYÿÿ‡þV'f& ÷Yÿÿ‡þV'f& øYÿÿ‡þV'f& ùYÿÿ‡þV'f& úYÿÿ‡þV'm& ûYÿÿ‡þV'm& üYÿÿþVa& ý P\ÿÿþVqa& þ P°ÿÿþVif& ÿ P¨ÿÿþVuf&  P´ÿÿþVCf&  P‚ÿÿþVyf&  P¸ÿÿþVm&  PPÿÿþVPm&  PÿÿqÿçäH&>šzÿÿqÿçäö&>qyÿÿqþVäf& HÿÿqþVäy&>HÿÿqþVäf&9Hÿÿqÿçä7&> RnÿÿqþVä7& HHÿÿhm&Ϻuÿÿh1&q¼;ÿÿÿüªf&B }ÿRÿÿhfÿÿþVhÕ& Pºÿÿ†Âxa Qÿÿ¶þV’ÿ¤†ÂxaH´ Ô<ܲ?]À1¶ Ô´?_]°KPXÌÀYÌ̲?]90±I±IPX³@@88Y#55#53xò†…ñÍþõgžÿÿ¶J7žÿÿ¶FJm'Ê„jÿÿºþVdf& ÿ8ÿÿºþVd{&Dÿ8ÿÿºþVdf&;ÿ8ÿÿºþVd7&D RÿÿºþVd7& Wÿ8ÿÿÿüíf'#b }ÿRÿÿÿçufÿÿÿü©f'%n }ÿRÿÿÿófÿÿÉþV;Õ&% Pÿÿÿ‰Â’f' Qÿ } ÿÿ´Â”f' Qÿ. ˆBÿÿ¶ÂJm'Ê„ QÿÿÿënH&Fšÿ$ÿÿÿãnö&FqÿÿÿÿØnÒ&F {ÿ.ÿÿ€Òÿÿÿäx7&F Rÿ.ÿÿÿæzm&F Sÿ0ÿÿÿõgm&'Ï.uÿÿY1&'qÿ.;ÿÿÿüf''q }ÿRÿÿÿí}fÿÿ~‹f' ‰þø }ÿÿ•±f' ‰ÿ ˆ_ÿÿ¶ÂJm'Ê„ ‰ÿÿ•ÿâ*H&Rš'ÿÿ•ÿâ*ö&Rq$ÿÿ•ÿâ*Ò&R {ÿÿ•ÿâ*Ò=ÿÿºþV¤a&N Q»ÿÿºþV¤a&N ‰±ÿÿ•ÿâ*7&R R'ÿÿ•ÿâ*m&R Sÿÿÿüçm&2Ïvuÿÿÿüç1&2q€;ÿÿÿüÇf'2à }ÿRÿÿÿá‘fÿÿ5a'/¨ ‰þÿÿªF)Ò&j }lÿÿ×FRÒÿÿªð‰fCÿÿ‡þV'f& Yÿÿ‡þV'`&VYÿÿ‡þV'f&[Yÿÿ‡ÿã'7&V ROÿÿ‡þV'7& Yÿÿÿüÿãf'-; }ÿRÿÿÿòÿãfÿÿÿüf'6D }ÿRÿÿÿÛfÿÿNþVÏç&6 PÿÿsîRfv†ÂxaH´ ÔÀܲ?]<1¶ Ô´?_]°KPXÌÀYÌ̲?]90±I±IPX³@@88Y53#7"†ò††òÍ”žgd߃¶œÜÌ1Ôì0!!dý僤ÿÿd߃ šdé³y¶©é/Æ1üì0!!dOû±ydéœy¶©é/Æ1üì0!!d8üÈydéœyµ©/Ì1Ôì0!!d8øÈyéyµ©/Ì1Ôì0!!øyÿÿþø&__Jÿÿÿìþÿî&BBB®éÓÕ@ žÜüÔÌ1ôì0#53Ó¤Ré­?þÁ²þ×Õ@ žÜìÔÌ1ôì03#Ó¤RÕ˜þÁ?®ÿÓþ@ žƒÔìÔÌ1üì0%3#Ó¤Rþ¬þÀ@²þ×Õ#5…R¤Õ˜þÁ?˜®émÕ '@ž   ÜüÌÔÌþÔÎ1ô<ì20#53#53Ó¤RšÓ¤Ré­?þÁ­­?þÁ®émÕ '@ ž  ÜìÔÌÜîÔÎ1ô<ì203#%3#Ó¤RšÓ¤RÕ¬þÀ@¬¬þÀ@®ÿmþ '@ žƒ   ÜìÔÌÜîÔÎ1ü<ì20%3#%3#šÓ¤RþfÓ¤Rþ¬þÀ@¬¬þÀ@®émÕ #5!#5R¤mR¤Õ­þÁ?­­þÁ?­9ÿ;ÇÕ '@¹  YW Y Ô<ìü<ì1äôÔ<ì203!!#!5!¨°oþ‘°þ‘oÕþ\™û£]™9ÿ;ÇÕ>@ ¹¹  ÂY W Y Ô<<ì2ü<<ì21äôÄ2Ä2î2î20%!#!5!!5!3!!!Çþ‘°þ‘oþ‘o°oþ‘oßþ\¤š™¤þ\™ýá3Ñ…! · Ç \ Ôì1Ôì04632#"&3­~|«¬}}¬ú|««|}¬¬3Õq3¢ðþˆìÁþ73#ìÕÕþþìkþ%3#%3#–ÕÕýVÕÕþþþþìþ #@ƒ   ÔüÔìÔì1/<<ì220%3#%3#%3#–ÔÔ©ÕÕú­ÕÕþþþþþþÜk¯i3#ÜÓÓiþqÿã Lð #'3?K®@D$%&%&'$'B@’ .’(’F’4 :&Œ$‘L%IC'1+C =  1 =I 7+ ! LüäìÔÄìäîîöîî991ä2ô<<ä2ì2îöîî20KSXííY"K°TK° T[K° T[K° T[K° T[K°T[X½L@LLÿÀ878Y"32654&'2#"&5462#"&546!3#"32654&2#"&546"32654&ôWddWUccUžº» º»ùtž¼»ŸŸ¹º% üZ VcbWWcd²žº» º»ŸWccWUcc‘”„‚••‚ƒ•Ü»»ÛÛ»¼ÛàÛ»½ÚÛ¼ºÜùóŽ•‚„””„–ýŸÜ»»ÛÛ»¼Û”„‚••‚ƒ• qÿã rð "-7;EP\"32654&'2#"&546"32654&'2#"&546  &54%3#"26542#"&546"32654& WddWUccUžº» º»ýyWddWUccUžº» º»øÖ<¼»þ¹ß üZ u¬cb®cNžº» º»ŸWccWUcc‘”„‚••‚ƒ•ÜþŠÛÛ»¼Û”„‚••‚ƒ•ÜþŠÛÛ»¼ÛàÛ»½ÚÛ¼ºÜùóŽ•‚„””„þ5ÜþŠÛÛ»¼Û”„‚••‚ƒ•(` Õ3(­Ëþß`uþ‹ÿÿ(`ÌÕ& º º,ÿÿ(`øÕ' º,& º ºX(` Õ#3 WþßË`uÿÿ(`ÌÕ& ½ ½,ÿÿ(`øÕ& ½' ½X ½, þ«ÿÃ#'#«”»¼•Rþùù¦žs#G@%èèèèBç¦onüì291ôì90KSXííííY" 5sþÓ-þ+#¿þôþô¿¢RÁ–#I@&èèèèBç¦opü<ì91ôì90KSXííííY"5 ÁÕþ+-þÓ#þ^Rþ^¿  ÿÿÃëÌ&™K'ÿèN''=NÎÿÿ“OÕ'ÿ^O“°ð$#5>323#7>54'&L¿ Za^Ág¸ßHZX/'ÅËËÅ-93A%‘­ #C¼98ŸL‰VV/5<4þòþBþR-5^1Y7ÿÿÿì| B_ÿ§þÇÿ… %³ ÄÔij991@  ÔÜÜ<Ì202$7#"$'56çÅåëþ:àßþ<ìæÂþµhh~vvuw~igÿ§Çn %³ ÄÔij991@  ÔÜÜ<Ì202&$#"56$6àÆëåþ;ççþ>æìÄnvv~hhgi~wuÿªþIÕ3 # #çbþâbíícÕþ$þ$Šþvÿÿ=ÿÅÃ' ý{' ý{ “Ý3_!!ÝVýª_Þþ‰ÿãÍð+@BŒ‘ÔÌ1ää0KSXííY"3#- ü\ ðùó°þòX 3!!#3hðþX¨ðð^ý#"ý#J°þòX 53#5!!53°ðð¨þXð^JÝøÞÝÿÿJð&"·"gÿÿ“Jð&"Jÿÿ“Jð'ÿ^"šdÿ–] 327#%dšêû³þà˜êþp]€û(®ìÿ;‡Õ 2###¬×ë׎¾Õ辸Ýü²ùášØÂ( 3+"&5463y¯¯v}¬­~ý±¬}|«ØÂ( ';2+‡¯¯v~­¬}ÂO«|}¬ÿÿ=ÿÅÃk ý{ÿB# #5#5ðÓÓR¤#þþüÛ¬þÀ@¬ÿÿÿ§þÇn& Ç Èÿÿ=ÿòÃo' ý¨ ÿÿ‘ÿBCÕ'‘'ŽÿÙHÿÿÙÓÛ1aÿÿÿ§þÇÿ~ Èø8È| #'7!5!'737!!ª ùqùþŸaúqú úqúaþŸúq)þŸ`ùrù úrûbþžúqú ùrú2¶¶ 535353úÈ,ÈÈÈÈÈþ¢ÈȼÈÈÿÿ(`$Õ' º,& º' º„ ºXfNè 53!535353fÈXÈý¨ÈÈÈÈÈÈÈþpÈÈ ÈÈföNÞ 5353535353öÈÈÈÈÈü,ÈÜȆÈÈþpÈÈ ÈÈüôÈÈ ÈÈÛ®Õ3#3#ÛÓÓÓÓÕþü'þdþåýï 3#%3#3#3#dÓÓÆÓÓýÓÓÓÓiþþþ„þùòþpDÕ %53535353#!5!3!†ÈÈÈü,ÈÜÈ| ýfš šöÈÈ ÈÈüôÈÈ ÈÈþ„ýfš ›ýe ÛP®> 3#3#3#ÛÓÓÓÓÓÓ>þýþöþÛ®w 3#3#3#3#ÛÓÓÓÓÓÓÓÓ‰þìþü–þðþWîð "27654/2#"&546Ê2332Ê233—¡ªª¡¢ªª—VV¬­VVVV­¬V¯ÞÓÔÞÞÔÓÞzœî@ Ü<Ü<Ì1@ÔÄÜÌ03#3#zttttýg‚?œôß Ô@ Ý ÷‘ ] ÜÔ<Äì291ôüÔ<ì290K°TK°T[K°T[K°T[K° T[K° T[X½@ÿÀ878YK°TK°T[X½ÿÀ@878Y@T /9IFYi‹«»       "5GK S[ e„¥µ]] !33##5!5ÝþË5¦‡‡þbfþ]ýämººyfÓß !!67632#"&'53264&#"Žþþy¡^^a`°<~B9>>Eo‚‚o4h6ß_Ì MLƒ‡KJq f®f\óð/"327654'&&'&#"67632#"&547632³X3333XW3333«1221€DD &9:D‘TTXW‘§°ll¶122m45[Z4554Z[54bg KL™1LM„ONÞÔÆuv lœÕß!#!liþ¤ˆHþ3ß0üíäYìð *:"32764'%&'&546 #"'&54767327654'&#"º55j]\655þìT./¤RQ./SZ85UVžŸUV56-/.UQ100/SS0/*,+KLV,+–+]12Hdt::dJ01:7PyAAAAyN98Æ?&%%$A?&%%$Séð.532767#"&547632#"'&2654'&#"‘1220DC #<9E¨WX‘§WXkl¶122ÌXf33XU5443®g KKš/˜„MNooÔÆuv rh\Z4554Z\44‰œ°k !!#!5!Q_þ¡iþ¡_kþÈ_þÈ8_8‰Ô°3!!‰'üÙ3_‰a°¥!!!!‰'üÙ'üÙ¥^‡_o #&'&4767TRRTe^///._üþþþ€ƒ€ú~„gˆ3#676'&ge_/../_eT)**)„~ú€ƒ€~~uœÄ0@ ÔÜ32ÄtNN^luu)qJy}þ…wYYk\þžsa88†ÿÿWÿñîT òýdÿÿ‰ÅC{ýdÿÿ^´TtýdÿÿbÿñÍTuýdÿÿ?ôC ôýdÿÿfÿñÓC õýdÿÿ\ÿñóT öýdÿÿlÕC ÷ýdÿÿYÿñìT øýdÿÿSÿñéT ùýdÿÿ‰°Ï úýdÿÿ‰8°— ûýdÿÿ‰Å°  üýdÿÿoÿif ýýdÿÿgÿiˆf þýdÿÿMÿð¡‚UýdÿÿGÿðÕ‚[ýdÿÿGÿðÏ‚dýdÿÿu!s¥ýdÿÿGÿðÕ‚\ýdV®Ö##"32.#"3267!!!!!!ÝO²cþþÛ%þe±NL­bÀÌÌÀb­LÄýÓýê:ý/¸667756ŸGFêþDêFG €þµ€þk€sÿ¦¾9'.473&'3267#"'#7&'#7&'&76%73&'hA>›/(%:@þÁwÓ]aØy›A9&›A²² œã‘X}R4>CþÁ5Aëi¥Õ<)û—^_ÓHH?W‡æÏghйKüpþØî”Ëû•(`ÿä³ð,%6767# !2.#"3>32.#".aXj]aÙyþÊþ›e6{Ù_]Ôwêù|^™˜0šn&<$€‹Š'/_ÓHGžghŸGGÕ_^þÇþØþÙœu]\Yª¶«…ËÕ!!!!3###5qZýpPý°úúÊì~WªþHªþµEþÇ9Ebð#!!53#535#535632.#"!!!5-üì¿¿ÇÇÖè=—OLˆ=”t‡þyìþ¾ªªB_ó¶))›ÔHºÿB+#&'&#"#3676323632#4&#"#̪m49wSS¹¹>YXyzU6%…ªX\x¯¾¹ru¦¹¾xGM_a¢ý‡`®f21>&>EÖ3öáý\ž¡œ¾¤ý‡u‡Õ"&)''#!333#3#!###535#53355KO¶8~ÄÓåÄÃÃÃÃþðÓåÄÃÃÃÃ8~OO¯ÎÎþò““4ýÚ&ýÚ{“{ýÚ&ýÚ&{“{{““þòÎÎÉÿã ÎÕP32654&#+#!233!!;532654&/.54632.#"#"&'5#"&5‡ï…‘‘…Ýqzìï¾­ìzWQ®eþ›Gl«`»[z„_¡<¡ÓÂ`©HJžU]‹;¹›éËU·f«É˜/ýÏ’‡†’þÌÈjqý¨ÕqRˆ>þÂý ‰N#55YQKP%$•‚ž¬®((TT@I!*™‰œ¶##ŸÒ`Éÿã3ÕE326&##.+#! 32654&/.54632.#"#"'&“þ’••’¿Ù¿J‹xÜÊÈüƒ}A{>[b`ca‚Œe«@«˜àÎf´LN¨Z‰‰b”?Ä¥÷ØZa,/ýúØ–bý‰ÕÖØº$~´3YQKP%$•‚ž¬®((TT@I!*™‰œ¶;®Õ"&)-1'#53'3!73!733#3#####5!73'!!7¶]:1Ì00ã00Í19]zžáþÚÃÜþàŸÀ¥¥þu‘û ¥¥å}ýL‘uÂÂÂÂÂÂuü€€ü€€ýg™uuþüýg™uuuu_ÿãñÕ % #4&#!#)"33!3_ÿ¸°þ±¸Žýýÿþú¸°S¸ÒþÖþÜþ‘VðÓú²*$oþªðÓNÿÿþÀ&ÓŠÿãð1@: Ó"+Ó ¡®•¡®•/‘Œ) 2+"!)#&  , & &*!/<ÔÄ2üÄÄ99999999991Ä2äôìôìîöîî2Ý<î20K° TK° T[K° T[K°T[K°T[K°T[X½2ÿÀ22@878Y@z  1Ti lnooooiko o!o"o#n$l%i'i-ŸŸŸ Ÿ Ÿ Ÿ Ÿ ŸŸŸŸŸŸ–Ÿ Ÿ!Ÿ"Ÿ#Ÿ$Ÿ%Ÿ&Ÿ'Ÿ(Ÿ)Ÿ*Ÿ+Ÿ,-2   USjg ]].#"!!!!3267#"#734&5465#7332[©fÊ A7ýæ¾8þŠ Êf©[Y¹`íþË(Ó7‹Â7œ(6ìb¹bÕiZÈ»{.# .{»ÊZiÓHH"{/ #/{"G(Õ33!!###5¿½uóý€ªþX°ùý_½—Tý‰wýsýÏý1ásÕ!5!!77#'%5'&ýîïýîåPþËæPþÊËçM4èMžªªþñ¡o؈¢oÙý¸º¡nÙˆ¢nÐþ9 ¤ð-bwƒ'67>32#"'&'"326767654'&'&67'>7632#"'.'&/#"'&54632326767654'&'&&#"32¹¢">1–aJ{%A01Q[W7>/÷W1   >$< ù¹ ´. #®dCw¯Ë-^URB$`>DL_K>.3b @N\uLM„iIä±(S395lš9,8„ýG(/&  -þ9)ЗiRm:3X’wdg7? 2øj7#=5(6$ 629T/ ·(2M !:5S}$@{mbõqŒ‚~Es/4 -& "TAB`]…’ü›|@8†nR€kcd]aCœû ".ÿãŽð)5'632327&547632#527654'#"'&#"%654'&#"¹‹“o|@X"07ãPYˆtaTkµ~jü†[Iw‰mqJ2530D#24!`ðNkB±þ¬X``S±ã«£¿Â†q¢J<“Xr~hFÙl¯V1??0W¦D¢Õ$+15#53!23#3#+##5!3276!&'&+!!64—RRÈûL\HGZMûþÊSþþMþÿMþ#ýÝ#ìVtqDjt,tkDqý¨xttzIYIíV,]ÿ[±x$-#5&'&76753&'&'6767#5!'ùŒß‰¨¨‰ßŒi`p^_kbkN?H9x"cxhuŒ…OooO¥§Ìmnͦ‰$F×_0,û?'‘¦ýS*%²§ l™þÑþÒ™lÕ5!3!!3##!##5373!/331µåµ0þûG¾“µÒªþªÕµ’½IÁC©DúÒiR{ýø{Ï{ýøýø{ÏÏÏ{Auÿã¼ð66767!5!67654&#"5>323!!!3267# $547#5\J€þ5’ ;³¥_ÌsrÚiõ¯þýgØC…üS1¼­rì{€ïjþçþݨ¡J,{ +kv€67Å&&äÆUB{\* {;^~ˆFEÐ/0àÙK?{ªþÓw!,&'&#2767#&'&576753w[TUeeUT[Y\Y[d¹sÔÔy²e]Y\þŠ[CvlC¤Õi--ûñ--iÓH$"þðuÖ9BËtèâ"#û£ùBuª¡f´lCüÿË3!üÚ~„åÿ›dü=ÿö!5!'3 üGå„~d›ü=zÿË!#'73!5ý„~¯¯~„å›ÍÍ›ü=zÿË5!'3#7ü=å„~¯¯~„d›ÍÍ›üFÿì 3#%3#%3#üËËËËþyËËfÊÊÊÊÊËûPF° 3#%3#%3#%3#ËËûkËËËËþyËË)ÊÊÊÊÊÊÊËü=zÿË #'73!'3#7üæ„~¯¯~„<„~¯¯~„›ÍÍ››ÍÍ›CÿÏØ $(B"326=7#5#"&54634&#"5>32%3#.#"3267#"&54632 pSHf¼¼™m¦ÍÆ©ogDc\”GÂÁ°ØüDØ©^o8y‰‰y8o^I‚IÊìîÑC€öBRCI”‚ Mþ >Ož–›W\ 7²$ÊúùË„²4œ”“4±"úÑÔùCÿÏþ +EI.46'&#"#&'53254&'"326=7#5#"&54634&#"5>32%3#V‡¹Nz$p´;i0™‰Êª¯‘%“‹±={û‰ pSHf¼¼™m¦ÍÆ©ogDc\”GÂÁ°ØüDØ}|ïŽ4¼9d$, !ó•5ÊLf,1„BRCI”‚ Mþ >Ož–›W\ 7²$ÊúùËsÿã'ð!.#"3267# !2'Yè—£yy£—èYjí„þ­þz†S†íý #bvAZ4û-4ZBuHHŸghŸGû[!¬þºþ»¬!ÿÿÃÿãmð&r&FÿÖÕ+™¸,/¸-/¸ܸܸ,¸(и(/¸ÜA&6FVfv†–¦¶ÆÖ ]Aåõ]¸¸ и¸ и¸и¸#и /º!"+¸!0153&'&'6767!!5&'&76wI3cc3IÊ86QLNN7887NNMR48×ü_¡k•—i³:rÍÍq;Ôzn #¬+ü¤+ª$ *ªÞ rŸn<ÿÏå(2.#"3267#"&54632%3#"326&$  &54^o8y‰‰y8o^I‚IÊìîÑC€ÝØüDØkavva`wwþß‚ÚÚþ~Ù—²4œ”“4±"üÑÔùKùËž’‘Ÿ   øÕÔøøÔÕ<ÿÏM-1332653#5#"&.#"3267#"&54632%3#\¼QPcu¼¼`ž¡ýª^o8y‰‰y8o^I‚IÊìîÑC€ÝØüDØLýõriuðüD PÁÿ²4œ”“4±"üÑÔùKùËÿÿ¤ÿã{ðRÿÿoþÖ#ã¹þóÿÿÃ&ð&r)IÿãŒûo!6767632#"'&#"32767#"'&'&547!#"'&54632327676"#"'&'&54767632¿lø(9BKc{=&%%03!((!,739%7`lG;7 þ25]­¹hB4,'5  'B[QF$%ˆ]c'G  %! }Kr~“,§ê1ý¼ÑŒ˜Ig)*!&!(D;wÖþ½¥‚},75;!_']7:y}©[´Ïž£\®@4>#,!, 'QFª§ÿj(JG4$$,*)/9„yK#%þù‹ÙP73276767654'&'&#"&'&"'632654'&'&54767767#"'&'672#"*i(X%# 1F‚S°E/ ËO.55Fu½PU[QF[00rlÓ~œ"‘KI}!;IFs;n¡íò;_þéT^ÍŒQ79}w²–^l.G‰yr\[4þúO9%#i#Þ^MX;€¥yv@c„“}e.ID\7I;>2V秉uÓ°þ¢ÓþþÉÕ3!3%!!!!!!ÕÊûŽÊþÒ’’þnýêþnqúó úó dýˆxú+Ûý%H‡#>54&#"#3>32uƒ¹ƒ j_”Ù y¸/¸wFÚx”£ ¤ý\/HT^Ȧý“ýœ^m ‘$R#a"67632#654'&#"##7373!!”UcduÁL/ƒ¸‚ .|•Ñ {¹ùšš¹@ýÀ²e22wKwGWý\ž?3L0O¾¤ý‡}““};ÿátð9#"'&5476323276765"#"'&54767632thÁ‚n<7# ;KQ>!|Za,4(XM‹•µ!}ã,üþþ†Â‚<7D9#7.M=†„¾.1?@ '(MXI(' jÿãFð!2?632327654'&54?#"'&#"632327#"&#"jŒou9„!ydG>PPÕPP5ʺò6…8^nm{z€}}È‹oÖz€Z¥'PV‰aK~p’md Œ‹yk”b¼^òÞOP681/:ª:b:DÿãnòJ327654'7#"'&'$#5"'47676766767632#"'&'&'&#"32ÛnZS_n0VBšR„äny#H‰B?„ÁX!$9B©„€Mw>7l. ;7%,;(Ó§½uy,D0ÿäÿãÒð&3273#"'#67&5477632654#0)W:K…32#"&'####53&  Á¸¸¹±ýO:±{ÌÿÿÌ{±:¹¸¸¡¡›§þܧ§$“““}þ²daþ¼ýðþ¼ad¨úü}ûã–ççþjçÆ¢Õ %# !3!# ýîêü”¨ÐdþXý0dd úóqûáú+áû6ÁÕ+/±’—B°“°˜B°/³,/°<±•í°°-ݰ.<°-°Þ°#ͱ?± °<± •í°#° °9° FhH°°)°#°#Ii;±€B±B=°°#I°€bi°F`°Fa°’C°`#B°’C°`C°UX°°’C°`C8Y°²& °<±€B±B0°0±þ°<°Ä°±ý°<°± 6ü°<° ±ý°<°9° FhH° ° °°#Ih;° <° °Ö°° ݰ,9°, FhH° °&Ö°&°°° #Ii;°/°,°#Ih:°°1#I°’C°`#B°’C°`C°PXµ& ,/°’C°`C°8K° RX° °#I°’C°`#B°’C°`C°@PX°’C°`C°@a°’C°`#B´ °’C°`C°8YYY±€B±B=°°#I°€bi°F`°Fa°’C°`#B°’C°`C°UX°°’C°`C8Y°²#)°<±€B±B1°°#I°RX ±°±í°±ý  ±°°<  ±°°< Y3525!463"!4632#"&732654&#"5!6“–º“þðýjãggggŒ92299229ŒîªkÀûÌ€•ªkû@áü4€•nŠ¿¿ŠŠ¿¿ŠNggNNggýD{{åÊ "-! ! ! ! '32654&#%!2+# ÚþJþÑþÏþR´12)þ÷þúþ†uy»ýÓ²ckkcþÁ?°´´°²ñÛþÎþÐþL´00¶eþ…þùþûþ‡y »wþ¯XQPXd‰ƒ…‡þ˜nþ;Cö0<67632#"'67327654'&#"#"'&57&547276545ˆ[«Û„ƒFIœy®eL )qz]E& J½þŸ‡EYƒq:?ß.è”0.A Æ‚¾Mke”½LP«Ú×§<+(ŒhÃ|H=þyþÞ|nŽ=B† üžî¤üïÆ{u.F/4_NÈTÕ 33!27&#%!2+!67654'&,Êd.ý@‘úþÿûþþnX<-]\,qúó ýj’dÛÓÕÚýˆZý™)VŸžV)sþøÙð!)%#'# ! % 7& 676'&B ôÝ3þÅþ‡y;:xÑý+lllþðli$ ›› Þ#››þÝï¥ab¥þ[þžþüþŽ 22×22ûjT%¶ýœµ%5û¯$¶c¶$Bÿî2 _327654'&'&'#"'&5476323276765""'&5476!6?232767#"'&B=]iS\Z—ÎV30FŸ¹n7;#FfS9!!ç< #5,h½";<¶‰„2XngšZR{,##9>;K!Q¡ýIag£Sûæ Àˆ‰D£5@7*'S:y}ž*‚7H0 5#!,I‘l» @3X§¿nh0ÞÍ{(2r:=OSÿãlñIX&54'&#"#"'&527654'&#"3"'&547632763227767654'&#"R(O*\xggŒfg-.@@?@@?\QA@@@S6—fggf—¢eÓ»p/$~AB}:1$ -*ü¤šMJJ@f·ŸŒ[¦+8ÇÎvuuv° zVWWW–›•XWWVÊVW\uvœuuu# bW1±W{|^1$h{ƒ€vC[SK\þ•GChfy— /È2Õ &.2&'&+3!.+!! !27&#676'&%3ûLDEx-MeÇþø5q>ÍþH¿J‹xþn§üu1ý•E˜A+ZY*ü²Ê—01/ÉþåO„~þh–bý‰ÕÖØÐb)¥ýj’ý™)V>U)-úó ©ƒÕ§¸ /¸!/¸ ¸и/¸ ܸи!¸ܸÜAêú]A)9IYiy‰™©¹ÉÙ ]¸ и /º9¸ /¸/¸/º+¸º +¸0132654&#+#!273 # #sþšš s­þÊÈûN®¾èþÍCéÏÔèI/ýÏ’‡†’ý_6ý¨ÕãÛ¬kÛêþ†þkþü•Éÿ%TÇ$+.3&##&'&''7#!27%7 67654#“ÑÈ?\üA>:AÍÙ¿KE6Tþ®oF^È~_ þº,¶8~ƒ|§T3JýÛ/ýîøýHD‚þh—0& ü°,¯Õ‚o®Œkغçþ]-Dƒbü‡g‹(Ø'4.#"#"&'532654&/.54632733###æUW'AG/E8piŒ4sG[d/EK7?8pcŠ|3iи¡¢¸ŠŒ‡‰·Y"*/( VAO[`*,2,* M=H\þ¬TýÈ”þØ(þlÿü0`!!#!!!!!!!3!!³þr˜þsàoþÕþ+öý…ï…àý›`€ü à`fþ÷fþ»f€üæf'“FÕ >@!  É  b b cbcÔäüäÔìÔì91ô<<ì2Ô<<Ä903#######5J®¤ªqÃ7ËrqËrÉÕÿý¾äþÑ/þB^þä^ÿ"hÄ %73# ' 3×þº,¶þ¢¦ÚÒýÇåžýo-þ£ÓM†‚o®üþFéú+žý„,õ’\™Õ %#!!!5!È8êüÈküÈOúÃ8üßd úóqdúódd XþL/ 654&#!5!5!5!!2!"'Xª£Á¹¥þð÷þ ÷ýÄ$þ'Ùþ'¬ß¦¹þþ»Ô§c£Ì†“¦e“e“¨þ°¨þ°aÔŠþÓf†JÿÿNÏç6ÿÿNÿãÏÊsþΕDU767654'&#"#"'&5733272632632!"'4'&'&#"'6763232767654'&'&#"Ž_}yj#1Q€–\$˜####‚,TGG\<‚„lG2ûe$°òþ×sa#0EKXQn#?QY>™k›DMý4þëÇgiàMq«Ìþí¢ÿã†ðE#"'&'&5476?&'&547632#"'&547654'&#"3"32767'_²ûilE_†m²l=Oc{ñˆT3-2") %+fa@aP/Z_|«{w:maZÂu> IhA¬"%@_©®Šl$=P”‹czS2VN-2!$+%$+@e—}N069n™¦a[u>_Tÿß M#"'&'!#!"'&547632327676=!7!&#"#"'&5476!27327î#X':'7á?<=ÿ**MÆþô€_4. B^l{>!þ—'Ba>ÁnG#&#w§4$Bš»00!ò£K=ýÜDc˜„€KÊ_4B( 03B{>ce‚¹DInFT=I,F´w§7K. 0É#Õ )5!!5!3#ü¦ý°Pʪ¸ªÉ9ÿÇBk32767"'&'&47'&'&'#"'&547632326765&#"6767632377632#"'&'&'&#"×,5(.'*'E`Ÿ97®y{7a;f7;>F3.^PeMD*#7@,j!HhH<=.%_yi¥pp3 T}€œ˜²B',$ *ý5Ü€/,,@!;Da97žT‘³þÄþÉþVMæ;©nwFþí»³^O?/,%!;>jytŸÖX<;}f?E'_n¥þúþôþ± Hô—˜''#  .hÿçJ) 4&#"322#"&54©WOmVPm˜ËÝ¢˜ËÝ÷t}þþÏt{ýØ£ÆþÿØ£Ægÿã²ð4 4'+5654/&4?'&547 '&5474/Ä‹c2üû›>B¬d=VEõÄ/b5Œšñc2ltc2¶c2Ðþuc1‚LS2œ?B¬dÖ,>8?]/c6¼›ñc1‚LS2ý–tc1‚LS2¶c1‚LS2ÿûÿã9ñ03#!".54?>3!4'.#!".54>323!2¾O,‡"ûô"…$„%@ý;5H *ÖY[¶ü#$‡"†x2 1[G(  Wÿ¸Að,!2#"&/#!"54?>3!!"&5462TPl 0%= -d,þmF"…$…þºmG- .7#*(/ ‚ý$þû"Sae(!‡Íq~B;Vÿ¸ºð&!"&54>323!2#"&'&5 ýmG * 5G 0%9 . ¦q~( 0 (/ ‚&ýJ“s!S'DQIF× 4632#"&3!53#5!¼pQOooOQpƒ˜ýo——ùTQooQOonûuyy5yZÿÕR; ! ! ! ! þHHþ¸þæþåé#ïþ[þŸþžþ[¥breþHþý¸Â ôÝ!#þÅþ‡y;:xÑ— L`  !!!!#!3#'!#33 # #—ƒþ¼þD—ƒµjÿ—wŠZþDZŒÖõöþÃRþëþé``€þ¶ýéþC5ü€ææ€þ³MþRþ.}þƒÞ$ÿðz`-1%5"'&'&5#2327#"'&5#!#"#463!#3#‰, †9áYl(Ht„ý*§þ²=Z2dr¥¶ý!††Z4@'!8¢ ýÖ¦zEB¯ ñÊbýãüüLs¨{dþYþ¯˜þsZ{3#"#4763 3×®UE‚El4FŒÅ©dGQõ´‰n ‚ËCFÁþ\þ§þxŸBýú¾*WŸbOZý=šþþÉþ¾ýýÈ0Õ 3%!!,Ê:ý*þnqúó ddúÕÉÕ3!3!!!!ÓÊûÊ þnýìþn8qúó úó úqúÕþwSÁ ! ! !!5 5¦ýþüúüð—üúüðùÆýþÚY*üÖü§çdüÖü§ccS£ÿã»ð!!6$3 !"$'53 !"kJýîu ^‹þuþ¢’þùopü‹þíþîk¨þþo¦SUþgþ“þ’þgHF×_`2/.2%©Õ!#!5!©Êý)¡+ª¨Õ!5!¨ü_×Õú+ª+ÿüçÕ!# #3çÙþbþeÙËfýšÇý9WJÕ " )327&#!3676654'&|²¨tKþ"þPýý"ûcõ­â“öþoûÊû¯ÍÐAfÕµ|ßcvþ~þ–ÕdúóA‹Aúó xûãPfUÂmZÿã­ #2!7#"547632!3 32767654'&#"ç* 6¯B8wxþ²!Nbb|Ëž"#>Ë|OO'vNþþ÷† ý2½¥wx87¥tžKsO= † =‚üí¨d01 ”P²²D10d^dú¬Tþdü6‚‚ŸžJthi[ÿã“{ (232767# '&5477632!7!654'&#"Ú N¡&# ¨G_yZ\klmkþô}Z5µ³üäfFý 9N©JC0<7h:‚ýJ(u*œo¦DM¢¡‘c“FPZd82vRsO 3#3#!!ɸ.¸Ô†²†$þ²ÚNéû9˜ü`ÿþV 3##676#732767!ɸ.¸fʆÐ#5H2KþÜÜ1i0/éNÞ)deéú¿ûеT0Hôd01™¬ûŒÖ``;ÿã¸ð&0 #473>32#"&'532654&7>54&#"ºþ;Hºt]þéühäƒñ2††02Þ¸S¥UWžDiƒ;2[UÔ _¦I@YËr„~•YWþí×€ác?}<¢Å$$¶/1oX3gÎQ¡X’Â?@Qˆ` $@   F ÄÄÔìì21@‡‡¼/äìÔì0!5!!5!ˆüÉþù`û “o“Ëÿÿ‰ÿãXð&{' 35u‹ýdÿÿ^ÿãXð&t' 35u‹ýdÿÿ‰ÿã^ð&{' 35 õ‹ýdÿÿ^ÿã^ð&t' 35 õ‹ýdÿÿbÿã^ð&u' 35 õ‹ýdÿÿ?ÿã^ð& ô' 35 õ‹ýdÿÿ‰ÿã~ð&{' 35 ö‹ýdÿÿfÿã~ð& õ' 35 ö‹ýdÿÿ‰ÿãwð&{' 35 ø‹ýdÿÿbÿãwð&u' 35 ø‹ýdÿÿfÿãwð& õ' 35 ø‹ýdÿÿlÿãwð& ÷' 35 ø‹ýdÿÿ‰ÿãð&{ 35ÿÿÉ“Õ,ÿÿÉ'Õ&,,”ÿÿÉ»Õ&,',”,(ÿÿÉQÕ&,9éÿÿhÕ9ÿÿ˜Õ&9,ÿÿ,Õ&9',,™ÿÿ ÀÕ&9',',™,-ÿÿÉÕ&,;Üÿÿ=;Õ;ÿÿ=®Õ&;,ÿÿ=BÕ&;',,¯ÿÿÉjÕ/ÿÿsÿã'ð&ÿÿɰÕ'ÿÿÉÕ0ÿÿÁyLÿÿÁé&LLpÿÿÁY&L'LpLàÿÿÁA&LYÂÿÿ=`Yÿÿ=½&YLDÿÿ=-&Y'LDL´ÿÿ=&Y'LD'L´L$ÿÿÁJ&L[Ñÿÿ;y`[ÿÿ;Ô&[L[ÿÿ;D&['L[LËÿÿÁyOÿÿqÿãç{FÿÿqÿãZGÿÿº{Py }Õ  ) !3 !## !5²–þhþPýŒþPþh–²Ÿô5þáþËôÊôþËþá5Õþ—þ€þ~þ–j‚€iúÑ.,ûw‰þéþÔþÒþèɰÕ#ûº +¸ º+¸º+¸¸ ¸ÐAêú]A)9IYiy‰™©¹ÉÙ ]¸ ¸ÐAêú]A)9IYiy‰™©¹ÉÙ ]¸¸%ܺ"+¸º+¸º+¸º +¸ 013 !#3 #32654&#! )“ô5þáþËôHðñÿHNŸ““Ÿþ蟲–þhþPþaY³.,²ÂÏÐÞý茊Þþ—þ€þ~þ–y }Õ(1C3 +3 !32654&+! ) #"35# !35#"&546!`HðñÿHô5þáþËôNŸ““ŸNÕýŒþPþh–²t²–þhü±NŸ““ŸNôþËþá5ôHÿñð/²ÂÏÐó.,ü°ŒŠü j‚€iþ—þ€þ~þ–ߊŒž²þéþÔþÒþè³ÃÐÏÂÿÿsÿã'ðHÿÿÿãõ{dÌ?8 ´  ÔÔÌ2@ @@ 00 ]1@  œ Ôì99ÌÌ0@  œ ü<<@ œü<<°KSX³ <<´œ ì´œìY5!!d‰xéÃû=éxUZ‰xéªéx£Ü @  œ Ôì99ÌÌ1´  ÔÔÌ2@ OO ?? ]0@  œ ü<<@ œü<<°KSX³ <<´œ ì´œìY3'#'-ZˆxèªêxÜþvxêû<ÄêxuÌP8 ´  ÔÔÌ2@ OO __ ]1@ œ Ôì99ÌÌ0@  œ ü<<@ œü<<°KSX³ <<´œ ì´œìY'7!5!'7Pþwxéû=Ãéx¯Zþwxéªéx£ÿùÕ @ œ Ôì99ÌÌ1´  ÔÔÌ2@ @@ PP ]0@  œ ü<<@ œü<<°KSX³ <<´œ ì´œìY#737‡ZþvxêªèxŠxêÄû<êxdÌP8Ú¶ ÔÌ2ÔÌ2@ PP_ _O O]1@   œ Ôì9Ì2Ì20@   œü<<@ œü<<@ œ ü<<@ œü<<°KSX· <<<<´œì´œì´ œ ì´œìY5!'7'7!d‰xé¼éx‰þwxéüDéxUZ‰xééxþwZþwxééx£ÿïÜÞ@   œ Ôì9Ì2Ì21¶ ÔÌ2ÔÌ2@__P POO@ @]0@   œü<<@ œü<<@ œ ü<<@ œü<<°KSX· <<<<´œì´œì´ œ ì´œìY3'7#7'-ZˆxèèxþxZþvxêêxÜþvxêüCêxþvŠxê½êx 4Ÿ³ 7!!# ?,þ¶^xü¢©t?©ü¢x^þ¶ 4Ÿ³ #'!5`?©ü¢x^þ¶³?ýÔJü¢x^© 4Ÿ³ %!5!73Ÿ?ýÔJü¢x^©s?©^xü¢J 4Ÿ³ %'3!_?©^xü¢J4?,þ¶^xü¢©dÌ?8'!5!!BÈ”—ýÿéxþw‰xédÈ”—š-þ¥Uéx‰Z‰xé[UþúªuÌP8!'7'7!'!5rÈ”—éx‰þwxéýœÈ”—þf×[UþúéxþwZþwxéþ¥Uª,ÓžP4327>76767632&'&'&#"#"'&/#7!í#/)85,0F"<;NJX[GR7<"#!2)85,/$#?2WG[XJN; ©?,þ¶ô!F0O<:" %7xþŸýéxUZ‰xéaxþÂþÂxaéxuÌP8 '7!' 7!'7PþwxéýþŸx>þÂxaêéx‰UþwxéþŸx>>xþŸéxþwdÌ?8 !5!3#•ûçéxþw‰x骪-éx‰Z‰xéYü¤¥Ü %'3'!!5èxˆZˆxèZü¤ªêxŠþvxêû檪uÌP8 †· œÔì2ÔÌ2@ O O _ _ ]1@  œ ÔÌÌü99ÌÌ0@  œ ü<<@ œ ü<<°KSX³  <<´œ ì´ œ ìY!#3!'7'8û窪éx‰þwx-þ§\þ§éxþwZþwx¥ÿùÕ !5!!7#7þ¨\þ¦èxþxZþxxè+ªªûæêxþvŠxê¥Ü7!!5!7'3'¯èxþx‚ü¤€þxxèèxˆZˆxèÂêxþvªªŠxêêxŠþvxêdÌ>†%52#!5! 767>54&'&'&>42/+-+-':1 üHéxþw‰xéÙÙܪ-)o=Ü  éxþwZþwxé(.46<=69)-dÌ>†>3276767654'&'&'&"5476767632+#5!5‘‚ 6 ©+/24>A1:'-+/24>‚©ý”éxþw‰xé×  ‚‚=69)-(.46=<69)-ÜÜéx‰Z‰xévÌP†>54'&'&'&"3)'7'7!#5#"'&'&'&5476767632# 6 +léx‰þwxéý”©‚>42/+-':1A>42/+ׂ  éxþwZþwxéÜÜ-)96<=64.(-)96=dÌP8X#532267676767632267676;'7'7#""'&'&'&'&'&""'&'&'&† éxþw‰xé 0$#$   "%'-0$' !  ' '- éx‰þwxé  ('Z&("  "(&Z'( -éx‰Z‰xé$ -#%"&* 'éxþwZþwxé ""&*  *&"" d¾PF%'!5!!'7'7! pþœéxþw‰x领 pdéx‰þwxéþ^¾:5éx‰Z‰xéo:þËéxþwZþwxé*ÿüØ %'7 ý!§^ßÁ bþ9YþÁ‹¾þXxýî¨û¸¾‹þÀÈbþñZ ò #!5 ªþéxþw‰xé‘ûoçéx‰Z‰xé¬[ò !'7'7!#¬—éx‰þwxéþª‘éxþwZþwxéüZÿã Õ !5!3 ýiéxþw‰xéíªDéx‰Z‰xéç¬ÿã[Õ 3!'7'7¬ªíéx‰þwxéD‘üéxþwZþwxéÝÿùÔ 7#7!5³éxþwZþwxéýÔÔü=éxþw‰xéªdÌ? !5!3?ü=éxþw‰xéª-éx‰Z‰xé,- eX&7#754767676 #4&'&'&"9èxþxZþvxê.-\ZnllnZ\-.ªBB54'&/#7!!#"'&'&'&54767D !BB54'&´x\-..0YXpl€…gtTY0../Z#þ¶,@ª#B"!BB@RN¾JV°]xþw‰x]TQ>°]x‰þwx]þ¬xL iiþ þ`iiTÁ4¬]x‰Z‰x]à4¬]xþwZþwx]JÒiiiiuÌP8!7'!7!5!7!'7'7!'7!5½giiþþÚyýìYu‚I0]x‰þwx]ýÌu‚Iþ«iiÒÒxÊK]xþwZþwx]ÊKxdÌ?8!!5!!¯ûÉ]xþw‰x]7ûQix]x‰Z‰x]xi¥Ü#'3'#'ñx\xˆZŠx^xh°ûP8^xŠþvx^ûȰhuÌP87'!5!'7'7!5$iiûQ7]x‰þwx]ûÉiix]xþwZþwx]x¥ÿùÕ737#73ñjhx^xþvZþxx\x%hh°ûÈ^xþvŠx^8dÌP8!7'!!5!'7'”iiüli…ý\]xþw‰x]¤]x‰þwxiiiá]x‰Z‰x]]xþwZþwx¤ÿðÜ7''3'7#7ñiiiá]x‰Z‰x]]xþwZþwx°ülii”iü{¤]x‰þwx]ý\]xþw‰x ÿѳ #7!##ÉPUý©?,ƒûUü°•vü°Uû„,?©ýUP¼ÿÑŸ³5#'#5!#5'ö•ü°Uûƒ,?©ýUv”ü°Uû©?ýÔ„ýU¼4Ÿ 753!5373öü°Uû©?ýÔƒýUP•qPUý„ýÔ?©ûUü° 433!'3É•PUýƒýÔ?©ûUq”PUý©?,„ûUdÌ?8!!!!5!!cÜû$ŠRü&xþw‰xÚû®¾xŠxx‰Z‰xxuÌP8!5!'!5!7'!5!Qû$ÜŠû®Úx‰þwxü&RFxŠxxþwZþwxxdÌ?8#''''#53777?(F´´´´´ncéxþw‰xé‹F´´´´´n-F´´´´´néx‰Z‰xéF´´´´´nuÌP8577773'7'7#'''un´´´´´F‹éx‰þwxécn´´´´´F-ªn´´´´´FéxþwZþwxén´´´´´F£Ü3'!!!!#!5!5!5!'-Zˆxè(þØ(þتþÚ&þÚ&êxÜþvxêþŽªªªþ¬Tªªªrêx£ÿùÕ#7!5!5!5!3!!!!7…ZþxxèþØ(þØ(ª&þÚ&þÚêxŠxêrªªªTþ¬ªªªþŽêxdÌ?8 5!!5!35!d‰xéqþéxªÿªÿUZ‰xéªéxaªªªª£Ü 3'#'3#3#-ZŠxêªêxbªªªªÜþvxêþŽrêxþVþªÿuÌP8  '7!5!'7%!#'#5Pþwxéþqéx‰ûÎÿÿªÿUþwxéªéxþw(ªªªª£ÿùÕ 737533-þvxêªêxþv‚ªªªŠxêrþŽêxþv4þþ¨ÿkË?9 !#3?ûîêxþvªªŠxêתêxŠþ~\þ€ŠxêuËI9 !'73#'7!uêxŠªªþvxêûî×êxþv€ü¤‚þvxê7†?~ 5!! !!Ïþö  ûôdþü ûôŒþöþöƒý}üüþïþ*^V 3! !!dþöþöƒý}üüþïþ*p þöûô düþûô H†P~ !! !!¸ûô  þ’ûô üŒƒþòƒ þÖþ^ÿÍVÕ #!# !!!dƒþòƒ þÖþe ûôþön ûôþ^V !! !3 3!!!Eþ*þïüüþïdƒþöþöƒrþ*rþò$èüþþ|è þöþþÔþ¤\d”^V )3! !3#!5#3 3 üšÈþïüüþïÈýÆÈžÈƒþöþöƒ\°üþýPd””x þö@t %#!5#3'!3!3! !33'ÓȞȡdþxd¡:üšÈþÑþÑÈýÙètø””Zddû®\’ýæýnªt^V%#!3!3! !3!5#3ĹØtIüš¹þþüüþþ¹þ¹tø”äØû\°üþýPìû”x^V%3 3!!! !!3 37áƒþöþöƒrþ*þïþèüüþèþïkŠþöþöŠ€dô þöý dôüþþè| þö€^V %#!5#3 3!3!! !!33 37ÓȞȃþöþöƒ:üšÈþïþèüüþèþïÈþÍŠþöþöŠ€ø””` þöü¨\˜üþþèþh þö€u†}~ 7!! !5#35! u\°üþýPd””x þöÏfÈþþÈ:Èýbȃ  ƒzMÕ!#7!!#Múc"?,þ¶^xü¢©ÕcúŽÕþŸ?©ü¢x^þ¶zMÕ35!3!5!73zpcþÞ?ýÔJü¢x^©crú+a?©^xü¢J^ÿÍV 3 3# '! !! !áƒþöþöƒƒ  þþþïüüþïe  þöüõþö dþüCüþuÌP8)5A '7!"'&'&'&'#5367676762!'7$"!&'&'!27676Pþwxéþ¡ 21@=“:C.2 ¾¾ 21@=“:C.2 _éx‰ü_R#)l$hþ˜$#R#$Uþwxé@21.2@ª@21.2@éxþw#þðÿÿwÿù;Ü' ¯, ±þÔuþtP'7!5!'7!5!'7!5!'7Pÿÿþwxéû=Ã××û=Ã××û=Ãéx‰ÿÿUÿÿZþwxéª×ת×תéxþwZÿÿdÀ?D5!3!!#!d‰xé3ªæþªýÍéxUZ‰xémþ“ªþ“méxuÀPD '7!#!5!3!'7Pþwxéýͪþæª3éx‰Uþwxéþ“mªmþ“éxþwdÀPD3!'7'7!#!5ª‰éx‰þwxéþwªþwéxþw‰xé×mþ“éxþwZþwxéþ“méx‰Z‰xédÀ?D5!333!!###!d‰x駪ªªþ⪪ªþYéxUZ‰xémþ“mþ“ªþ“mþ“méxuÀPD '7!###!5!333!'7PþwxéþYªªªþ⪪ª§éx‰Uþwxéþ“mþ“mªmþ“mþ“éxþwdÀPD333!'7'7!###!5œŒdŒ éx‰þwxéþàŒdŒþàéxþw‰xé×mþ“mþ“éxþwZþwxéþ“mþ“méx‰Z‰xé7Ä?@  !¥úJþB¾JˆôúAþƒ¾¾þƒ‚uÄ}@ 7'!5! úúPû¶J¾þBˆúúþÅ‚}þBþB7Ä}@7'! ! úúü–úý6þB¾Ê¾þBˆúúþ ôúAþƒ¾¾þƒ}þBþBhÕ %!3!3¼ýÛ ýÆÕˆ¡ˆÒýÇÇçüRÕþú+‡ÿãnðmº+¸A&6FVfv†–¦¶ÆÖ ]Aåõ]º+¸º +¸0132#&'&#"327673#"‡ ûû…B!ÊOœœOÊ!B…ûûþ÷ézÆcI7™þÍþÌþÌþÍ™7IcÅŒhÿçÁ-)b@'! '!Õ* $$*ÔÌÜÌ9991äÌÜÌÎÎ990K° TK° T[K°T[K°T[K°T[X½*@**ÿÀ878Y>54&#"#"&54632#"&54324&#"32ôIH7$$0e´ÖþßÕ˜ËÝ¢e‚ WOmVPmmW£Kƒt,>bþÊþùþ±þFØ£Æ[àt}þþÏt{É‹Õ !5!!5!!5‹ü>øýøýÕú+ªìªëªÉÿ¢‹5!#7#53!5!!5!733!‹ýKªc˜˜þÐd˜þ0ª4˜êþ☶Õú+^^ªìªëª``ªþëýkþì›ÿì]®(673#"'&'#7&'&$32 '&#" 32$767&'&Yj€iEdÈþ ¹»°80~iª?/cÆ`»¹°üÉô‘™šþÝRQQ$gý'-‘š™"SRR:;nSz²þ€þ¾_'œÒBT±€c¾_ ûé¥N“‘››‘@DüRO“‘››‘gÿú`Á8@ÔÌ91/ÄÌ90@cmpxyvn]] !3!¬þ^DýïàCúšîûÄú?ÿú`Á%! !¬¢ü¼3ý½fý½Ó<úñÁú?¯ÿìI®!!"$54$3!!!W?JŒ‰”¹ýGÀþ¾¾cÀ¹ýG”‰‹K@ õsJx‹NLŸÆ`»¹`ÈŸMOŠx] ¯þåI¯&/!!!!3!!"''&'&54$;7#"Ø–I$þ¢¹ý¯°$$¹ýGA?d–`,,±¾¾cÀêüÝFU ;¸°“Š}YI¯7ÊŸþ þŸ þî7c`»¹`ÈüÅJx‘H ¸ üNG’x]Ù›åg% $54$)!!3!+þØþÖ*(ºþFèiNvüŠ%Frߺ›öððöŽO:…Ž‘0QޝÿìI®&'&'&'!5!2#!5!676767!5¡?JŒ‰”ýG¹Àc¾¾þÀýG¹”‰‹K@ û 'Jx‹NLŸÆþ »¹þ ÈŸMOŠx] ¯þåI¯&/'7!5!!5!&#!5!2+4'&'&'3276765 –IþÜ^¹ýéQ°$$ýG¹A?d–`,,±¾¾þÀê#FT‚ ;¸°“Š}YIþå7ÊŸü ãŸ 7þøcþ »¹þ È;Jx‘H þH þNG’x]Ù›åg )5%2767!5&'&!5“(*þÖþØþFºèiNüŠv%FtÝþFgöñïöŽP:…Ž‘1RŽ,ëâ ¸/¸/01!!,¿âüœþwqÁ@×Ö¯ggÔìÔì1üìì20!#!#œÕðý ïÁø¶}ùƒœþwqÁ@ÖׯggÔìÔì1ü<üü03!3œïöðþwJùƒ}ø¶þw;Á ]@    ÔÄ91ÄÔÌÎ990@0QVPZ spvupz €€ Z pp{ t €€ ]]!! !!5 7êüA ýJïúÞÕýIÁÁý3ýÀ•!ãÙ-Û×¶œÔÄ1Ôì0!!ÙúþתÙÛ !#!5!3!!5!®¨ýÓ-¨-ýÓý+ƒªƒþ}ªתªÙÛÕ Wº+¸¸Ð¸¸ и¸ и¸ и/º +¸ º+¸¸¸и¸ Ð01!!#!5!3#®-ýÓ¨ýÓ-¨¨ þªþûªû5Ìþ‰ÿãÍð+@BŒ‘ÔÌ1ää0KSXííY"3#- ü\ ðùóÿÿŠÿ’<%?ŠP¯%73% %#'±þTU¬¨¬UþT¬UþT¨þTU‚÷“÷ïþ÷“÷÷“÷þï÷“ÿÿDG¾ÂrýÒÛH®F·ƒÔì1Ôì03#ÛÓÓFþ=ÿ×} *@    ÔÌ91ÔÌÄ903##'%\½sý®BþÁ}}`ùºs-Pbý;ÿÿ=ÿ×Ð& 8usàÿÿ=ÿ׿& 8 ôsàÝÝî %.#"326"&'#"&54632>3"31…UfvYR†½I•OF^ˆº§†_™HDža+…J1†CšXX„je†ˆþèw‹‡ߦ¯Ø~ŠŠƒ–‡¢XZÝÝÏî /ÿ@- !$'!!0 $*0ÔÄÔÄ99991ÔÄÔÄÀ9990@¾     $$$   $$ $ ***///***55500055 5 :::???:::EEE@@@EE E JJJOOOJJJV´° °!°"°&°'°(´)]]32654&#".#"326#"&54632>32#"&“1†Te€vYR…Ä1…UfvYR†F^ˆº§†_™HDža†¼§†^•/XZ‡ie†‡7XX„je†ˆ‡ߦ¯Ø~ŠŠƒá§¯ÖwË™I%!3!™û‚ªÔË~ü,¯IÕ%!3Iúf¹òüËÊÊÕúõ¯ÿ”IÕA¸/¸/º+¸¸Ðº9º9¸¸к901%&'&'3!!#4'!&'7`'JAWþÎã`òþLqR]+XþÉŒü* PÊ‹†s^þã(ýRs“§°Ê57756uíÿúÜÑ5º +¸ ¸ /¸/º 9º 9º 901 7&'7%%'6 676rý{Ž‹ÔþŠEG…þ%†yýÝ44RÀW!L!$îÒ¾³Æ ¡“¿Òñ&!L éþçþð{ž°þJP+3#°  +øfþJ›+ 7º+¸¸и¸и/¸/º9º 90137#'° ûPþµ ýMVÕüœ°oçü)g±nþJð+3#3#  @  +øáøfþJ›+{¸/¸/¸¸Ð¸/¸ܸи¸ ܸܸ ¸и¸ и¸и/¸/¸ /¸/º 9º 9º 9º 9013737##'   [P«   ]MæEü+qdý @oxûº×pü™÷AnÓ¢!3# iúhÃþÞþÝ¢û^¬üTÓ¢3 3#Ã#"Ãþ˜ú¢üT¬û^Ó¢32#4&#"#õñðõ¬”¥¦”¬P(*þÖþØý°7ôÓÓôýÉÓ¢332653#"¬”¦¥”¬õðñõRPýÉôÓÓô7ý°þØþÖ*uþM¶>2&#""&'7327㢗~”9GA¢—~”9Gâ§…}Œ‚¯ú°§…}Œ‚¯ÿÿuþMÛ& I I%ÿÿuþM& I' I% IJuþM¶-6?67632&#"#"'&'7327&'&5476767654'&'ãSOJMG7”9GcBnnVsSOJMG7”9G]InoSuŠ=,EG%,½=,HK%â§DAF7Œ‚¯þÖK|’œoUþ¾§DAF7Œ‚¯1Io›œsVý›/HgjG$þ4Ö.JhgH$uþMÛMQZc67632&#"!67632&#"#"'&'7327!#"'&'7327&'&54767!!67654'&ãSOJMG7”9G~SOJMG7”9GcBnnVsSOJMG7”9Gþ‚SOJMG7”9G]InoSu‰~þ‚¾=,HK% =,EG%â§DAF7Œ‚¯þâ7§DAF7Œ‚¯þÖK|’œoUþ¾§DAF7Œ‚¯$þçDAF7Œ‚¯0Io›œsVý’ñþ!Ö.JhgH$ºþ+/HgjG$uþMmqu~‡67632&#"!67632&#"!67632&#"#"'&'7327!#"'&'7327!#"'&'7327&'&54767!)!67654'&ãSOJMG7”9G~SOJMG7”9G~SOJMG7”9GcBnnVsSOJMG7”9Gþ‚SOJMG7”9Gþ‚SOJMG7”9G]InoSu,þ‚ýÛ~þ‚¾=,HK%2=,EG%â§DAF7Œ‚¯þâ7§DAF7Œ‚¯þâ7§DAF7Œ‚¯þÖK|’œoUþ¾§DAF7Œ‚¯$þçDAF7Œ‚¯$þçDAF7Œ‚¯0Io›œsVý’ñþñþ!Ö.JhgH$ºþ+/HgjG$uþL.3&#"7#'754'&'#"&'7327#4767>32";EY?w^H¾6¼H\O3,, HO€”;E+@/VfmVm HO€ö‚¯þ³?uœ]H½½H]sM3 üg¥‡z‚¯ˆ.Vr›mV_¥‡zuþM<%4'>7'7&#"7"&'7327&'&54767>2·=,HK%€˜=Q ‡Hl;EYLmHH§7'&#"'"&'7327&'&54767>2·=,HK%m#6,=iSH²;Ec‹¤HKÂsŸ˜€”;E]InoSuŸ˜€JÖ.JghH$6B0þ+‚@THþÖ?¤HK|Àþ¾¥‡z‚¯1Io›œsV<¤ˆzÿÿyÍžÕ' 7ÿžþ…' 7ðþ… 7GÿÿyÍžÕ' 7Gþ…' 7ð 7ÿžÿÿ¡ÍtÖ' 7ÿÆ 7ÿÆþ…ÿÿyÍžÕ' 7ð' 7ÿž' 7ðþ… 7ÿžþ…ÿÿÙ-Ûj' 7$ 0ÙŸÜj 3#3#!!ÓÓÓÓûÐXü¨þËþ•ªÿÿØŸÜj& 0' 7ÿýþW' 7ÿþ$' 7-þW 7.$ÿÿÙŸÛj& Z' 7þW 7$ÙÓÛ2#"'&'&'&#"5>32326Ûi³an’ ›^X¬bi³an“ ›^V©2³NE;=LT³NE;=KÙÓÛ23276767632.#"#"&'Ùg©V^› “na³ib¬X^› ’na³i2UK=;EN³TL=;EN¢1Ô).#"3".54>323265.#72#"&:QŸHRdhNiœ\dŽnxÂ>@°HRdhNiœ\dŽnxÄ.ttlH=Y›OšHL¢\}X[lH=Y›OšHL¢\}ÙWÛ­#"'"#322Û{ûædfftXª{ûædfftX×ý€ÖÜþ#€þ*þ$ÝÐ0!#.5476767654&'30´ND:323267#"''æcDX¬bi³a]y¬›¨eEV©gi³a`y«›S LT³NE+~FþŒ KU³NE,þ…FÙÛ¢ #"/&'&#"5>32326!!Ûi³an’›^X¬bi³an“ ›^V©ûeúþo³NE;=LT³NE;=Kˆ¨Ù`Ûô#"/&'&#"5>32326!!Ûi³an’›^X¬bi³an“›^V©ûeúþô²OE;=LS²NE; =KþkªÙbÛÓ%&32767#"'!!'7!5!7&#"5>32Ë%H\¯ i³aBPŸü﹉lþ噸‘ZX¬bi³an“3ïý}oþé -X"²OEºªþdšªó8LS²NE;IüÙ¸ÛÁ"#"/&'&#"5>32326!!!!Ûi³an’›^X¬bi³an“›^V©ûeúþúþÁ²OE;=LS²NE;?KþkªËªÙÛÁ.#"/&'&#"5>32326#5!7!5!7!!!!'Ûi³an’›^X¬bi³an“›^V©üLç•ÑýšŒoþKÐ…üɦoÁ²OE;=LS²NE;?KüLªËªŠsªËª sÙÿöÛB.32767#"'!!!!'7#5!7!5!7'&#"5>327b K`Jqi³a'+\+ýzlòü³h’>ùTmþ?u2›^X¬bi³an“c†’"%]²OE«ªËªÂNtªËªÚ=LS²NE;%úNÙÛô;?@.9*-" *œ19œ"œ œ<-<Ô<Ä21ÔìÔìÜüÔìÀ9999990#"'&'&'&#"5>32326#"'&'&'&#"5>32326Ûi³an’ ›^X¬bi³an“ ›^V©gi³an’ ›^X¬bi³an“ ›^V©o³NE;=LT³NE;=KÚ²OE;=LS²NE;=KÙÛÿ43267#"'3267#"/'&#"5>327&#"5>29+V©gi³a@LJ“ZV©gi³aƒ}¤‰9+X¬bi³a@MH’ZX¬bi³Â ¤a KU²OE»8KU³NE; þ˜@^ LT³NE»8LS²NE;f@Ù¸ÛÉ59#"/&'&#"5>32326#"/&'&#"5>32326!!Ûi³an’›^X¬bi³an“›^V©gi³aq›^X¬bi³an“Æ3V©ûeúþL²OE;=LS²NE;?KÒ²OE;=LS²NE;?KüîªÙyÛÑ5P#"/&'&#"5>32326#"/&'&#"5>32326#"/&'&#"5>32326Ûi³an’›^X¬bi³an“›^V©gi³an’›^X¬bi³an“›^V©gi³aq›^X¬bi³an“Æ3V©ײOE;=LS²NE;?KÒ²OE;=LS²NE;?KÒ²OE;=LS²NE;?KÙ¸ÛÁ"32?632.#"#"&'!5!5Ùg©V^›“na³ib¬X^›’na³iúþúþÁUK?;EN²SL=;EOþȪªþ‹ªªØ×Û+  %5 % $%5$[þáþœg&Yþ¢þà%Zþ£þÞþàþhÓ¦²•¦²—³¦²—¤²—Ù6ÛÎ9%676767!!"'&'&'!5!!5!676762!!&'&'&[C-8›þö7VYÎYW6 þöœ8.CC.8þd 6WYÎYV7 þe8-Ù,CE[¨<0[2332[39¨\DD+N+DD\¨93[2332[0<¨[EC,Ù`ÛÎ !5!676762!!&'&'&!![C.8þd 6WYÎYV7 þe8-ý;úþ++DD\¨93[2332[0<¨[EC,ýãªÿÿÙ`Ûÿ' 7¹ ÿÿÙÛÿ' 7ýº&  7¹ÿÿÙÞÿ' 7ÿþ¹&  70ý½ÿÿÙÝÿ' 7ÿþý½&  7.¹ÿÿÏ62Ï' 7ÿôþî' 7ÿô‰ WÿÿÍ63Ì& ô' 7…† 7…þîÙ`Û¢ 3654'!!5!&547!5!!æ44ò34wúþ~þ€þ‰ú0IG00GG2ðªª?8>;¨¨_8Ù`Û !!!!"264&'2#"&546Ùúþúþ…Hdd‘deH;k'**¨z{¤§¢¨ðªDbFE``‹bq+((d:s¡žvv£Ù`ÛK!!!! &!56Ùúþúþ‚áàþÿþæé¢¨ðªë—²—•²—Ù`Û!!!! 3# Ùúþúþ$´Ÿ¾¸¢¨ðª‚ýcÙþ'Ù`Û!!!!33#Ùúþúþ$¦¸¾Ÿþý´¢¨ðªþ'ÙýcÙ`ÛÊ!!!!!!'Ùúþúþú+]^*ò^ôó]¢¨ðªNþä°þä°°Ù`ÛÔ !!!!!3!Ùúþúþ‚¦Nßp!ýN¢¨ðªÀþNfýÙ`Û07GO!!!!#"3###535463!3267#"&546324&#"'53#5#"&4632264&"Ùúþúþ¤?$mmC???DíþÏNB&H#$J'`qk[Q_C<17HÆBB@,I\\I,@Íp`ctiG6B?9iÚýÐ=$#t¾u#g“SS“SÙ`Û*!!!!>32#4&#"#4&#"#3>32ÙúþúþŸ!]?U\Z79EPZ7:DPZZV:;S¢¨ðª==:xoþµHOM]QþÊHPL^PþÊ%U20=Ù`ÛÚ ,!!!!3#7#546?>54&#"5>32Ùúþúþ4eeb_--B6'Z0/`4\o$-,¢¨ðªÈN2A+,/-7#!^aO&E++ Ù'ÛÝ>@" Ïœ Ï œ  Ü<Ä291Ô<Ì2ü<ìþ<ì990!!!!!'7!5!7!Ù}®/þHÃ{üúþþ}®þÕ¶Ãý‡¢;fÕ¨ðªþÇfÓªðÙ¸ÛL !@  Ü<<Ü<<1ÔÜÜÜÜÜ0!!!!!!ÙúþúþúþתªýÀªÙÿÎÛ4!5!7!!!!!!'7!5!7!5!•ýD€™Q"þrñýµn¹üé™RþÝoþL¢ªèT”ªËªËªêT–ªËªÙÛ  )@    Ü<Ü<1ÔÜÜÜÜÜÜÜ0!!!!!!!!Ùúþúþúþúþ”ªªüKªªÙÛ¨ T@.œœœœBѧœ $# ü<ì2291/ìôì90KSXííííY" 5 !!Ûü@Àúþúþúþøþëþî²pªoüªÙÛ¨ V@/œœœœBѧœ$ # ü<<ì291/ìôì90KSXííííY"55 !5ÙúþÁAúþø°þ‘ªþ²ýǪªÚÿVÜ 3!! 5 !!Úúþü@Àúþúþúþªþëþî²pªoüªÚÿVÜ !!555 !5ÜúþúþÀBúþªªk°þ‘ªþ²ýǪªÚþ±Ü!5!7!5!7!!!!' 5'þ³Ú`ýÆȉŠ)Pþ"_=ý6ŠŠÞü@Àúþªªsª¤s1ªsª¥tFþëþî²pªoÚþ±Ü!5!7!5!7!!!!'55'þ³Ú`ýÆȉŠ)Pþ"_=ý6ŠŠþÜúþÀªªsª¤s1ªsª¥tF°þ‘ªþ²”.Ìß 5 5ÌüÆ:ûÊ6üþüÆ:ûÊ6þpþrÅŸ ÎþpþrÅŸ ”.Ìß 5555”6ûÊ:86ûÊ:ÎýöŸýøÅŽÎýöŸýøÅްþò'!67&'&54767&'676'&'{)#Y4JJ4Y#)þÀ)#Y4JJ4Y#) AAAAGF£žâßà㞢GGGG¢žãàß➣FGú2;;âÎá;<<;áþ2â;ØÜè5$?$%5%67$'W´É ßeþöþ»²Ä”‡·Èþûäd?þNÿÄ”])]²o&¶—²¯ bRô)`²q%µ ˜²¯ þŸRÙÛd%'%5% >ýz™‡“þ­ˆmþzÈþF<ß˶êþ½@6´¦ o@æh¶‹GpÙÿ Ûÿ%5'75%7-ý†™‡“ïüå­ˆmþá†ÈºþÄ%˶êC@þÊ´¦þàþ‘@æh¶‹GpÙÿ/ÛV !5!%5%%%!!'’þ‰/ôþƒ½xýËv¤H-þ‰réýÛf‹ý5L¤OlUýrªC¢ªÝ@=ÇV°lþÑŒ²žþíªÑ=Ùÿ/ÛV%'!5!75%7%5!!'‘ ÖþþøGWþbè[ý½„¤ÚýmmüÁN¤—L>üߪìw²‹õ§°·e=þ¨ˆª½þتÑ=Ùÿܨ$%#"'&'&'&#"5>32326 5Üj²bn’ š^X¬bh´`n” œ^V¨gü@Àúþð´ND:3232655Üj²bn’ š^X¬bh´`n” œ^V¨ûfúþÁ²NF<>LT²NF<>L>°þ‘ªþ²Ùÿ)ÜP14%&#"5>32%5%%%3267#"'&'&/' k X¬bh´`'+kýÆu¥E%þsäýák œ^V¨hj²bn’ "P¥vþ„1-LT´ND£ªÞ9A»T°jþÍŠ²œþãLT²NF<þñ=ß &T²N#¡w²‹ô¨°¸f=ý€J;ÐÿNÜ} 55 5ÚúþÀ8ü@Àúþ'°þ‘ªþ²»þëþî²pªoÐÿNÜ} 5 55Òü@ÀúþûúþÀ'þëþî²pªoö°þ‘ªþ²ÐþmÜ`!-%5%%%'5%%5 MMþÜ`ýºÂZ¦D„þOAòýè@ýFZ¦Dþt¸@þmþ¬*³_TW§ªÊˆ&þÔo°}þ䎲š¥ªÈþw&-r²~‘°›ÞbUÐþmÜ`!7/%5%%'%5%75%À®˜J¦’¤ýv±a¦d®ýì",ý©,ƒýV`¦bþL"ýÌ_D2,£/*/¼°Å¨&þO{ª˜”Ÿ°­Â¸²Äþ[&®}ªš”¢²­Ùÿ²ÛP %5$Ûþrýó ’þoüsaþa^þ~ï|¦|Ùÿ²ÛP 55%$Ùsüþo’ ýóaïþ|¦|þï‚^aÙÿ)ÛW!%5%5$Ûþgü—V¬þþ$Üþü}ƒ×¾‘®…ßþå]]þå×x„¦|€Ùÿ)ÛW3%55%$Ù¬Vü—þgƒü}þÜþ$¡…®‘þBW×þ€|¦„þˆ×]]ÙÿRÛW(%#"'&'&'&#"5>32326%5$Ûi³an’ ›^X¬bi³an“ ›^V©gþþ$Üþü}ƒ±³NE;=LT³NE;=K$þå]]þå×x„¦|€ÙÿRÛW(%#"'&'&'&#"5>3232655%$Ûi³an’ ›^X¬bi³an“ ›^V©ûeƒü}þÜþ$±³NE;=LT³NE;=K$×þ€|¦„þˆ×]]ÙÿƒÛ&%5$%67%'îðþÛÃEƒ¹žtºÖ‹$÷ãþ‚ë‚k—¡}u°U)¦?¥eKþtu•ï³uþÔíï"¦þ K 9'ÙþåÛ'567$'567&'Æð%þ=þ»þ⃹žtºÖ‹þÜ÷ã~ë‚þ•—¡}uRU)¦?¥ý›KŒu•ï³u,íïþÞ¦÷Küö9'Ì£è_%!"54763!!"3!èüÂÊþ슊È@üÀˆ¾^`ˆ@£ÈÆ‹–ÀˆŠ^`Ì£è_75!27654&#!5!2#Ì@ˆ`^¾ˆüÀ@ÈŠŠþìÊ£–`^ŠˆÀ–‹ÆÈþêÌÿ;èÏ #";3!!!!#"54763¢úˆ¾^`ˆ0ørªrnþdÊfýlpªpÈþꊊÈÉÀˆŠ^`&pþ–ýp–þ˜hÈÆ‹Ìÿ3èÇ 32654'&+ #!5!!5!32#úˆ¾^`ˆ0ørªrþ’œÊýš”pªpÈŠŠÈ9ÀˆŠ^`üÚþp––hþ˜þêÈÆ‹¾Ûè7!!!"'&54763!!"3!òéûéüÁÉŠ‹‹ŠÈ@üÀ‰¾_`ˆ@––,‹‹ÈÆ‹–ÀˆŠ^`Ùöè7!!5!27654&#!5!2#Ùéû@ˆ`_¾‰üÀ@ÈŠ‹‹ŠÉ––,–`^ŠˆÀ–‹ÆÈ‹‹¾ÿÛ× '!";!!!!'7!5!7&'&54763!7!!Ñþʉ¾_`ˆ'œ}ýE=øüÉa†LþéT>¹‚‹‹ŠÈsc†L0þ’RÀˆŠ^`–––í5¸––ƒ‹ÈÆ‹ï7¸–Ùÿö× '327654'&/!5!7+!!'7!5!7!5!^»ˆ`__BV™ý 5c†TpX‹‹ŠÉ÷?àüãb†LþÐm>þUéÂ`^Šˆ`C –ï7Ë XÆÈ‹‹––í5¸–––¾ÿjÛé )5!7!!'!"'&54763!!"3!Äþ.Bqx-ëý¥qxDüÁÉŠ‹‹ŠÈ@üÀ‰¾_`ˆ@––Z<––Zi‹‹ÈÆ‹–ÀˆŠ^`¾ÿjÛé )5!7!!'5!27654&#!5!2#Äþ.Bqx-ëý¥qxþ'@ˆ`_¾‰üÀ@ÈŠ‹‹ŠÉ––Z<––Zi–`^ŠˆÀ–‹ÆÈ‹‹Ó¢332653#"757!!¬”¦¥”¬õðñõîÄ54&'&'$  &'&'&547676!!#!5!ÅÖ]\LMLLML\]Ö]\LMLLML\þ®îbc1111cbîþìîbc1111cbÍdþœªþœdŽ''LM¸mj¸ML''''LM¸jm¸ML'½dbcwvŒ‰wvcbddbcvw‰Œvwcb„þ›ªþ›eªe»ÿãù$7!!"2767>54&'&'$  &'&'&547676¡rüŽ$Ö]\LMLLML\]Ö]\LMLLML\þ®îbc1111cbîþìîbc1111cbתa''LM¸mj¸ML''''LM¸jm¸ML'½dbcwvŒ‰wvcbddbcvw‰Œvwcb»ÿãù$3?"2767>54&'&'$  &'&'&547676''7'77ÅÖ]\LMLLML\]Ö]\LMLLML\þ®îbc1111cbîþìîbc1111cbíýüxüüyýüxüüŽ''LM¸mj¸ML''''LM¸jm¸ML'½dbcwvŒ‰wvcbddbcvw‰Œvwcbþ¾üüxüýyüüxüý»ÿãù$7 "2767>54&'&'$  &'&'&547676æpxýgÖ]\LMLLML\]Ö]\LMLLML\þ®îbc1111cbîþìîbc1111cb†pxý€''LM¸mj¸ML''''LM¸jm¸ML'½dbcwvŒ‰wvcbddbcvw‰Œvwcb»ÿãù$73#"2767>54&'&'$  &'&'&547676ðÔÔÕÖ]\LMLLML\]Ö]\LMLLML\þ®îbc1111cbîþìîbc1111cbþ‹''LM¸mj¸ML''''LM¸jm¸ML'½dbcwvŒ‰wvcbddbcvw‰Œvwcb»ÿãù$ 2L"264&'2#"&54>"2767>54&'&'$  &'&'&547676ZPnn noO@v+..¹†‡´¸ðÖ]\LMLLML\]Ö]\LMLLML\þ®îbc1111cbîþìîbc1111cbAoPOmmžp1.-rB„·´‡†ºÌ''LM¸mj¸ML''''LM¸jm¸ML'½dbcwvŒ‰wvcbddbcvw‰Œvwcb»ÿãù$+E %#'-73%"2767>54&'&'$  &'&'&547676ðþ½C4þÑfþÑ4Cþ½4/f/÷Ö]\LMLLML\]Ö]\LMLLML\þ®îbc1111cbîþìîbc1111cb1¯°X·þ­S·X°¯Y·Sþ­·''LM¸mj¸ML''''LM¸jm¸ML'½dbcwvŒ‰wvcbddbcvw‰Œvwcb»ÿãù$!;!!!!"2767>54&'&'$  &'&'&547676€ý€€ý€«Ö]\LMLLML\]Ö]\LMLLML\þ®îbc1111cbîþìîbc1111cbjˆÀˆô''LM¸mj¸ML''''LM¸jm¸ML'½dbcwvŒ‰wvcbddbcvw‰Œvwcb»ÿãù$37"2767>54&'&'$  &'&'&547676!!ÅÖ]\LMLLML\]Ö]\LMLLML\þ®îbc1111cbîþìîbc1111cb8€ý€Ž''LM¸mj¸ML''''LM¸jm¸ML'½dbcwvŒ‰wvcbddbcvw‰Œvwcbþª»ÿãù$!%!!!!#!5!QûX>úÂôdþœªþœd‰ûð›ú¿Yþ›ªþ›eªe»ÿãù$ !!!%!!¡rüŽPûX>úÂת\ûð›ú¿»ÿãù$   ' 7 %!%!!=þ•kyþ–þ–ykþ•yjjüûX>úÂìþ–þ–xjþ•yjjyþ•k$ûð›ú¿»ÿãù$ 3#!%!!ðÔÔþaûX>úÂþ†ûð›ú¿¯Jš@ ÜÜ<Ü1<ÜÜÜ033!!¯úäšýuýp¯Jš!#!5!3Jú䋯Iš!#!5Iýs€ýsšúå¯Iš35!3!¯€úå¯|š33!!¯Ný²šýuýp¯|š !#3!!!!.Ný²Ný²šþ$ä¯Jš !#3!!!!.úäúäšþ$ä¯Jš !3!!!#3¹üGþšýuýpš¯Jš !#33!!!#3.GVýªþšúfšýuýpš¯Jš!#3#3!!!!.c¹üG¹üGšúfšþ$ä¯ÿ¯Jë33!!!'!¯'SsþÅjý\þªs=þšýuÜ5ýYý6«ýp¯ÿ¯Jë!!!!'!#3!7!¨sêý­j½ýþþséþhx¥jýÃë5þäýÓ6÷þ$šþ$þää¯ÿ¯Jë!!'!#3!#3‚sþÅþ6þªs=þžüÿë5ýYý6«ýpšýuüñš¯ÿ¯Jë!#3!!!!!'!#37!‘ôsê:þŒjÞýçþþséþìþ²jþGšþ$-5þäýÓ6÷þ$šüÁääÙÛæ 51ü¢úþ Äþžýœ¦ÙÛæ ƒ^ü¢ª bbü:Èýï¦ÙÿžÛd 5! 5Ùªü¢úþbªªÄþžýœ¦ÙÿžÛd 5! Ùû¨^ü¢ªbªªbbü:Èýï¦yg‡¢62"'&'!"&462!6"264ŸS몧òQ3ýQ3Tò¢¦ëW4¯üdrOOsOOS¨ï¤Q3CB3R¢ò§U4CDPrOOqyg‡¢"&462!6762"'&'!$264&"aS몧òQ3¯3Tò¢¦ëW4ýQœrOOsOºS¨ï¤Q3CB3R¢ò§U4CDPrOOqbgR¢ 7!6762"'&'$&"26bÀ1[륢òS4šOsPOtO.¨D/Y§ò¢R3BŽPQqOOyÿ ;d 3#!!#3%!5!¨¨( ý󀨨þØýó dýó€ªýs €ªî<›!##5!#T~èNèÈì 35!3 3#ÈKûõÃ#"Ãþ˜úªªìüT¬û^Èì !!3# ÈKûµ@iúhÃþÞþÝìªú¾¢û^¬üTÈì !!3 3#ÈKûµ@Ã#"Ãþ˜ú쪠üT¬û^™~ )3!!&'.'&™û‚ªZV¥îF%ü,E=Î?~þë%Fî¤VZDý¼A?Î=™~ !53ïüÖ*ü,ÔªÔüÖªªÔû‚ÿúþw–Á # #·ßËý}ý}ËÝÁø¶wù‰Jÿúþw–Á 3 3Ùý!˃ƒËý#þwJù‰wø¶ŒþwÁ@ ÜÜÜÜ1@ ÜÌÌ0"# #4$HÛþÌ­½þ½­þÏãüû9àB(þØþ¾û ÇúåŒþwÁ@ ÜÜÜÜ1@ ÜÌÌ02$53 3HÛ4­þCþþC­1åãüÇû þ¾þØ(Bàû9úåÿÿþ#îu)ÿÿÛH®F 7ù1  !!'ù+]^*ò^ôó]ýþä°þä°°ÙÿÂ'B  ' Ù''üÙ›þâ>€ý°Pú€Nq^ýD^þ¢ÆÙÿÂ'B %  !'üÙüÙ''ýtþÊþ¢àNý²€ý°PÝþ^ýDÙÿÂ'B 5  5!Ù''üÙüÙŒ6âbàý²Nú€Pý°Ýäþ¢¼ÙÿÂ'B5 5ÙŒýtNüÙ>àáâÝû]ÝPÙÿÂ'B 5 'üÙüÙNýtŒ>Pý°Ý£ÝþþÙ`Ûô32?632.#"#"&'!5Ùg©V^›“na³ib¬X^›’na³iúþôUK= ;EN²SL=;EOþȪªÙ^Û¦ 3# 5¤ÓÓ7ûøúþþîþ‘þ“¶ѦÑÙ^Û¦ 3#55=ÓÓdúþþî¶þ/¦þ/¶m”. Ìß  5 5 5 ÌüÆ:ûÊ6ýüÆ:ûÊ6üþüÆ:ûÊ6þpþrÅŸ ÎþpþrÅŸ ÎþpþrÅŸ ”. Ìß 555555”6ûÊ:86ûÊ::6ûÊ:ÎýöŸýøÅŽÎýöŸýøÅŽÎýöŸýøÅŽÙþ.ÛÔ  5 !5! 5ÙÁü?úþúþü@ÀúþþÞ²þªþ‘þªNþëþî²pªoÙþ.ÛÔ 5 5!55Ûúþü?þ¿úþúþÀþÞ°oªp²þî9ªªø°þ‘ªþ²ÙÛ¨ %5 5!Ûúþü@þ¾°°oªp²þî9ªªÙÛ¨ 7 5 !5!ÙÁü?úþúþ°²þªþ‘þªÙÿ)ÛW5$%5$ÛþTüªi™þü}ƒþþ$Ü€þ_…®‘¾ú©×€|¦„x×þå]]Ùÿ)ÛW5$%$5Ù™iüªþTÜþ$þƒü}þ€×þB‘®…ý!]]×þˆ„¦|þ€Ùþ“Û!&!%'&'57&%5$%67&%7Ûþóþ*þズÕÿÚ?ðþص;öƒ¦½‚ÈâiöÚþ€@šþ]0qw^×%¤ý¡KA6#®(A‡F+¦3273267#"'' 5æcCX¬bh´`^xn›ieEV¨hj²b_zl›]ü@ÀúþLT´ND*óFé JV´ND+ðFÍþëþî²pªoÙþåܨ"%&#"5>3273267#"''55æcCX¬bh´`^xn›ieEV¨hj²b_zl›þ[úþÀLT´ND*óFé JV´ND+ðFͰþ‘ªþ²Ùþ¦ÛW&&#"5>3273267#"''%5$æcDX¬bi³a]ym›ieEV©gi³a`yl›]þþ$Üþü}ƒ. LT³NE+óFé KU³NE,ñF”þå]]þå×x„¦|€áþ¢ãW&&#"5>3273267#"''55%$îcDX¬bi³a]ym›ieEV©gi³a`yl›þ[ƒü}þÜþ$3 LT³NE+ôFê KU³NE,ðF˜×þ€|¦„þˆ×]]ÙþöÛ 7%'%5 '瞃Ùý¹ÛžÜýâL ‹Ðž‘þ  @þÁYû8ðýè@à¦\üïÄ9þ@aÎÙþöÛ ' 7%ÍžƒÙGÛžÜü´þô‹Ðž‘öþö@?YÈð@ýçà¦þ¤ý<9ü@þŸÎÙþ}Û…5!%57%!!'71ŽÏý| ýäIÁžÛý·…Îüìvž\'þ žÄ:þýqª†ß¦[×@þÅZû8ñþ»ªþß@áÐ_ÍÕþ}×…7!!'7#5!7%%%Õ¥éžé¿ý9ƒJüpvž\º]þFGjqœÈþé8@ýǹ¦þÛþÀªþß@áªäµýþ.õÐ !!#!#5!¸ õüöžþØøªþøªœÅû !'7'7!!5!5!âËéx‰þwxéþýýFb⼚éxþwZþwxéüªªªª#ÙÐ %! ! ! Ølþ”ý(þ”+þR­[®þSrvvýŠýèèýý°ýüP #53s຦ È–ýüêæ0þkþþTþ°ýüs*3#°ÃÃ*÷Ò°þP- #5s–È ¦º -êþþTþþk0æê°ýüP 53–È ¦º ýüêü¬ò•þýýÐþýêêýüP*#0PÃ*÷Ò.°þP- 3#຦ È–-êýêþýÐþý•ò¬ü°ýüP#!!sàþ#ýüðýüs*3#°ÃÃ*÷Ò°þP,!!sÝý`,ø«Ã°ýüP!5!þ# ýüUÃ÷èýüP*3#ÃÃ*÷Ò°þP,3!5!Ãý`Ý,÷èãýêX #4763!!"]ºo´þçeD9ýê×™ž°fVš¨ýü],#'&%'53 763”<(eºnKþû==Pnºe(!<˜þõýè  _A»E_  ýèþõ˜<£þX> 3!!"'&5]9Deþí´o>ùÜšVf°ž™×£ýô]>#3]ººýôJ¨ýê] 4'&#!5!2£9Deþç´oýê$šVf°ž™×ùä£ýüX,&'&3!3#76l<(eºnP==þûKnºe(!<˜ ýôþö_E»A_þöýô ˜<¨þ]> 3#!5!2765£ºo´þíeD9>ùä×™ž°fVš¯þu(3¯Æþ(÷Ø7†@Õ% !!!5 5!!3þü7Öûôdþö  þò†üüþïhûÂrƒþöþöƒvý˜ºÕÄ! !!Õùå ùå $û<ø–ÿFfü +   276764'&'&">  &vvýŠýŠrèèýþn66\]Ú]\6666\]Ú]\6¨Šîîþèî5þ•kØkþ•üæ\­þSü¤þSÈÚ]\6666\]Ú]\6666\þªîþèÿÿš!Žÿ‚ÿã¤>32#"&'#'%53%&  s:±{ÌÿÿÌ{±:¹þé!8¹#!þ¼r§þܧ§$¶daþ¼ýðþ¼ad¨æ]chÀƒaamü@–ççþjç’þ.‚Ð!3!3: ¨ü¨þØøý^¢˜ÿì”è &Ûº+¸º#+¸#º+¸A&6FVfv†–¦¶ÆÖ ]Aåõ]Aêú]A)9IYiy‰™©¹ÉÙ ]º+¸º +¸ º$%+¸$¸Ð01! 4$32! 4$#"35%33!”þ?þÂþÄþ?Îq¾ÁqÍúW¶þ¸­­þ¸¶|Ùì¡Úý—êþÁþA¿?ÆrÆÆþÈþäþp³G±±þ¹ýÿ~+˜/ü掘ÿì”è 8?º+¸º3&+¸3º+¸A&6FVfv†–¦¶ÆÖ ]Aåõ]Aêú]A)9IYiy‰™©¹ÉÙ ]¸3¸и/Aê&ú&]A&)&9&I&Y&i&y&‰&™&©&¹&É&Ù& ]º,9º+¸º +¸ º+¸º0)+¸001! 4$32! 4$#"!!56$7>54&#"5>32”þ?þÂþÄþ?Îq¾ÁqÍúW¶þ¸­­þ¸¶O´ý\R!>/_N;„sa‘=£Å0>²êþÁþA¿?ÆrÆÆþÈþäþp³G±±þ¹ýÿŽMñ"?U(?N&:«$—}:iF§˜ÿì”è Dµº+¸ºB5+¸Bº+¸A&6FVfv†–¦¶ÆÖ ]Aåõ]Aêú]A)9IYiy‰™©¹ÉÙ ]Aê5ú5]A5)595I5Y5i5y5‰5™5©5¹5É5Ù5 ]º5B9º,5B9¸,/Aê,ú,]A,),9,I,Y,i,y,‰,™,©,¹,É,Ù, ]¸ܺ&9º;9º+¸º +¸ º)"+¸)º?8+¸?º2/+¸2º/2901! 4$32! 4$#"#"&'532654&+532654&#"5>32”þ?þÂþÄþ?Îq¾ÁqÍúW¶þ¸­­þ¸¶¹ vØÃ@ˆX[}DuskcŒ‘ZX\[4yk_ˆ=¡ÁhêþÁþA¿?ÆrÆÆþÈþäþp³G±±þ¹–]œ¨0OLGNŒ<:,+¸>º2+¸201! 4$32! 4$#""32654&.#"632#"&5432”þ?þÂþÄþ?Îq¾ÁqÍúW¶þ¸­­þ¸¶¡N\\NN\\ÔTa/w €N ºÂ ¹ÀêÈ5jêþÁþA¿?ÆrÆÆþÈþäþp³G±±þ¹Ÿb[ZbbZ[bœ#”P =±”‘³ýçÚ ˜ÿì”è "ë¸#/¸$/¸Ü¸#¸и/¸ÜA&6FVfv†–¦¶ÆÖ ]Aåõ]¸¸ÜAêú]A)9IYiy‰™©¹ÉÙ ]º 9º!9º+¸º +¸ º!+¸01! 4$32! 4$#"!#!”þ?þÂþÄþ?Îq¾ÁqÍúW¶þ¸­­þ¸¶k«þ”ÁQþ1êþÁþA¿?ÆrÆÆþÈþäþp³G±±þ¹Kü£˜ÿì”è '?K º+¸º=+¸º1F+¸1º+¸A&6FVfv†–¦¶ÆÖ ]Aåõ]Aêú]A)9IYiy‰™©¹ÉÙ ]A&6FVfv†–¦¶ÆÖ ]Aåõ]AêFúF]AF)F9FIFYFiFyF‰F™F©F¹FÉFÙF ]º%F19¸%/Aê%ú%]A%)%9%I%Y%i%y%‰%™%©%¹%É%Ù% ]º+=9¸+/º4F19¸%¸7ܸ+¸@ܺ+¸º +¸ º":+¸"º.I+¸.ºC+¸Cº4C901! 4$32! 4$#""32654&%.54632#"&546732654&#"”þ?þÂþÄþ?Îq¾ÁqÍúW¶þ¸­­þ¸¶¯T__TT__þÆj·¶iv¾­­¾vWQMKRRKMQêþÁþA¿?ÆrÆÆþÈþäþp³G±±þ¹æPIIPQHIPIvStˆˆtSvƒ\Š——Š\ƒÁ=BB=>BB˜ÿì”è 4@‘º+¸º>)+¸>º+¸º/8+¸/A&6FVfv†–¦¶ÆÖ ]Aåõ]Aêú]A)9IYiy‰™©¹ÉÙ ]A>&>6>F>V>f>v>†>–>¦>¶>Æ>Ö> ]Aå>õ>]º)>9Aê8ú8]A8)898I8Y8i8y8‰8™8©8¹8É8Ù8 ]º+¸º +¸ º 2+¸ º,;+¸,º5&+¸501! 4$32! 4$#"532676#"&54632#"&2654&#"”þ?þÂþÄþ?Îq¾ÁqÍúW¶þ¸­­þ¸¶Ua.w €OŸºÂ ¹¿éÈ5kÙN[[NN\\êþÁþA¿?ÆrÆÆþÈþäþp³G±±þ¹ý‹œ$“O <¯”‘´ýèÚþö´b[[bb[[b˜ÿì”è &2>›º+¸º#+¸#º*<+¸*º60+¸6º+¸A&6FVfv†–¦¶ÆÖ ]Aåõ]Aêú]A)9IYiy‰™©¹ÉÙ ]Aê0ú0]A0)090I0Y0i0y0‰0™0©0¹0É0Ù0 ]Aê<ú<]A<)<9<I<Y<i<y<‰<™<©<¹<É<Ù< ]º+¸º +¸ º-9+¸-º$%+¸$º3'+¸3¸$¸Ð01! 4$32! 4$#"35733!"32654&'2#"&546”þ?þÂþÄþ?Îq¾ÁqÍúW¶þ¸­­þ¸¶Íž¬¼uŸþ>@EE@?FF?‚ˆˆ‚ƒˆˆêþÁþA¿?ÆrÆÆþÈþäþp³G±±þ¹þ>>'‰*ý6€Þ—£¢——¢£—{äÑÐääÐÑäÿìðåœ5!ùð¬¬ÿìšåò!ùšXþ¨ý–¸È3 ý– 2õÎÈý–È!È@ý– 2õÎÿìðåœ 5!!5!!5!±4üï)üï4𬬬¬¬¬ÿìšåò !!!!!±4üï)üï4šXþ¨Xþ¨Xþ¨ý–¸È 333     ý– üôÀ²ýNf üôÈý–È !!!È@þÀ@þÀ@ý– üôÀ²ýNf üôÿëðåœ 53353353353¼´³´²´½𬬬¬¬¬¬¬ÿìšæò 3333333¼´³´²´½šXþ¨Xþ¨Xþ¨Xþ¨ý–¸È 3333       –2ýÎø2ýÎsÙþ'ýsÙþ'Èý–È !!!!È@þÀ@þÀ@þÀ@–2ýÎø2ýÎsÙþ'ýsÙþ'ý–åœ!!ÍýÓý–¬û¦ý–åò!!ÍýÓý–\þ¨ûüÈý–åœ!!Èþ#ý–¬û¦Èý–åò!!Èþ#ý–\þ¨ûüÿìý–¸œ!5!ýÔÌý–Z¬úúÿìý–¸ò!!ýÔÌý–Xú¤ÿìý–œ!5!Èþ$ý–Z¬úúÿìý–ò!!Èþ$ý–Xú¤ðåÈ3! -ðØúÔ¬šåÈ3! -š.û*þ¨ÈðåÈ!!È@ÝðØúԬȚåÈ!!È@Ýš.û*þ¨ÿìð¸È5!3, ð¬,ú(ÿ울È!3, šXÖùÒÿìðÈ5!!Ü@ð¬,ú(ÿìšÈ!!Ü@šXÖùÒý–åÈ3!! -ýÓý– 2úÔ¬û¦ý–åÈ3!! -ýÓý– 2û*þ¨ûüÈý–åÈ #!!!P@ÝýÓý–ZØúÔ¬û¦Èý–åÈ 33!!ÈP -þ#ý–,úÔ¬û¦Èý–åÈ!!!È@Ýþ#ý– 2úÔ¬û¦Èý–åÈ #!!!P@ÝýÓý–.û*þ¨ûüÈý–åÈ 33!!ÈP -þ#ý–\Öû*þ¨ûüÈý–åÈ!!!È@Ýþ#ý– 2û*þ¨ûüÿìý–¸È!5!3ýÔ, ý–Z¬,õÎÿìý–¸È!!3ýÔ, ý–XÖõÎÿìý–È !5!!#ýÔÜ@Pý–Z¬,ú(û¦ÿìý–È !5!33Èþ$, Pý–Z¬,úÔúúÿìý–È!5!!Èþ$Ü@ý–Z¬,õÎÿìý–È !!!#ýÔÜ@Pý–XÖùÒûüÿìý–È !!33Èþ$, Pý–XÖû*ú¤ÿìý–È!!!Èþ$Ü@ý–XÖõÎÿìý–åœ!5!!ýÔùýÓý–Z¬¬û¦ÿìý–åò !!!!ýÔÌ-ýÓý–XV¬û¦ÿìý–åò !5!5!!ýÔ,ÍýÓý–Z¬Vþ¨ûüÿìý–åò!!!ýÔùýÓý–Xþ¨ûüÿìý–åœ!5!!Èþ$ùþ#ý–Z¬¬û¦ÿìý–åò !!!!Èþ$Ýþ#ý–XV¬û¦ÿìý–åò !5!5!!Èþ$Üþ#ý–Z¬Vþ¨ûüÿìý–åò!!!Èþ$ùþ#ý–Xþ¨ûüÿìðåÈ5!3!, -ð¬,úÔ¬ÿìšåÈ !3!!, -ýÓšXÖúÔ¬VÿìšåÈ 5!3!!5, -ý3ð¬,û*þ¨VÿìšåÈ!3!, -šXÖû*þ¨ÿìðåÈ5!!!Ü@Ýð¬,úÔ¬ÿìšåÈ !!!!Ü@Ýþ#šXÖúÔ¬VÿìšåÈ 5!!!!5Ü@Ýüãð¬,û*þ¨VÿìšåÈ!!!Ü@ÝšXÖû*þ¨ÿìý–åÈ #!5!3!¸ ýÔ, -ðû¦Z¬,úÔ¬ÿìý–åÈ !!3!!ýÔ, -ýÓý–XÖúÔ¬û¦ÿìý–åÈ !5!3!!ýÔ, -ýÓý–Z¬,û*þ¨ûüÿìý–åÈ !!3!!ýÔ, -ýÓý–XÖû*þ¨ûüÿìý–åÈ !5!!!!ýÔÜ@ÝýÓý–Z¬,úÔ¬û¦ÿìý–åÈ !5!3!!Èþ$, -þ#ý–Z¬,úÔ¬û¦ÿìý–åÈ !5!!!!Èþ$Ü@Ýþ#ý–Z¬,úÔ¬û¦ÿìý–åÈ !!!!!#ýÔÜ@Ýþ#Pý–XÖúÔ¬Vûüÿìý–åÈ #5!5!!!!Pþ$Ü@ÝýÓý–V¬,û*þ¨ûüÿìý–åÈ !!33!!Èþ$, PÝþ#ý–XÖû*V¬û¦ÿìý–åÈ !5!533!!Èþ$ÜP -þ#ý–Z¬VÖû*þ¨ûüÿìý–åÈ !!!!!ýÔÜ@ÝýÓý–XÖû*þ¨ûüÿìý–åÈ !!3!!Èþ$, -þ#ý–XÖû*þ¨ûüÿìý–åÈ !!!!!Èþ$Ü@Ýþ#ý–XÖúÔ¬û¦ÿìý–åÈ !5!!!!Èþ$Ü@Ýþ#ý–Z¬,û*þ¨ûüÿìý–åÈ !!!!!Èþ$Ü@Ýþ#ý–XÖû*þ¨ûüÿìðåœ5!35!, -𬬬¬ÿìšåò!!!¸-û,šXþ¨Xþ¨ý–¸È33   òÖû*ú¤ûüÈý–È!!È@þÀ@òÖû*ú¤ûüÿìDåH5!5!ùûùœ¬¬þ¨¬¬xý–XÈ333x   ý– 2õÎ 2õÎý–åH !!!!ÍýÓ-ýÓý–²¬¬¬üRxý–åœ !!##xmþs  ý–¬û¦Zû¦xý–åH !!3!!xmý3 -þsý–²¬úúZ¬üRÿìý–¸H !5!5!5!ýÔ,ýÔÌý–®¬¬¬úNÿìý–Xœ 5!###l   ð¬úúZû¦Zÿìý–XH !5!!!5!¸ý4lþ þt,ý–¬úN®¬û¦DåÈ 3!!! -ýÓ-D„û€¬¬¬xðåÈ 333!x   ðØúÔ,úÔ¬xDåÈ 3!3!¸ ü“ Íœ,û€¬þ¨„ú(¬ÿìD¸È 5!5!5!3,ýÔ, D¬¬¬€ù|ÿìðXÈ 5!333Œ   ð¬,úÔ,ú(ÿìDXÈ 5!35!3Œ ýÔÌ œ¬€úÔþ¨¬Øù|ý–åÈ 3!!!! -ýÓ-ýÓý– 2û€¬¬¬üRxý–åÈ 333!!x   þsý– 2õÎ 2úÔ¬û¦xý–åÈ 3!33!!¸ ü“  -þsœ,û€¬úú 2õÎZ¬üRÿìý–¸È !5!5!5!3ýÔ,ýÔ, ý–®¬¬¬€õÎÿìý–XÈ !5!333xþtŒ   ý–Z¬,õÎ 2õÎÿìý–XÈ 5!3!5!33Œ  þt,  œ¬€úÔúú®¬û¦ 2õÎÿìý–åH !5!!5!ýÔùýÓý4ùý–®¬¬üR¬¬ÿìý–åœ 5!!###ùþs   ð¬¬û¦Zû¦Zÿìý–åH 5!!5!3!!ùü“þt, -þsœ¬¬úú®¬û¦Z¬üRÿìDåÈ 5!5!3!ùû, -D¬¬X¬€û€¬ÿìðåÈ 5!333!Œ   ð¬,úÔ,úÔ¬ÿìDåÈ 5!5!333!ùûŒ   D¬¬X¬€úÔ,û€¬ÿìý–åÈ!5!5!5!3!!!!ýÔ,ýÔ, -ýÓ-ýÓý–®¬¬¬€û€¬¬¬üRÿìý–åÈ5!333!!###Œ   þs   ð¬,úÔ,úÔ¬û¦Zû¦Zÿìý–åÈ !!!!5!5!333!¸-þsþ þt,ýÔŒ   ý–Z¬üR®¬û¦¬€úÔ,û€¬ý–åœ 4763!!"Q[¨yþ‡Y[ý–`¡†¬~|ü ÿìý–¸œ 4'&#!5!2.-Yþˆx¨[Qý–`~=?¬†x¨ü ÿìð¸È 5!2653#xY[ Q[¨ð¬~|2ûΨx†ðåÈ !"'&533!åþ‡¨[Q [Yyð†x¨2ûÎ|~ÿ“ý–>È3mù²ûý– 2õÎÿ“ý–>È#3>²û²ý– 2ÿ“ý–>È # # 3 3>²ýÜýݲ}ýƒ²#$²ý„ý–cûûcúçÿìð|œ5!ð¬¬F¸È3 F‚ú~|ðåœ5!|ið¬¬ý–¸F3 ý–°ûPÿìš|ò!šXþ¨ÈFÈ!È@F‚ú~|šåò!|išXþ¨Èý–F!È@ý–°ûPÿìšåò5!5!!5iý—ð¬Vþ¨VÈý–È333ÈP Pý–°‚ú~ûPÿìšåò!!!iý—šXV¬VÈý–È#!#P@Pý–°‚ú~ûPÿÿÿì;( ãÿìþ;ÿ!Oþþûÿìþ;ÿö!Oþöþ ÿìþ;!Oþüñÿìþ;!Oþûìÿìþ;!Oþúçÿìþ;!Oþùâÿìþ;#!Oþ#øÝÿìþ;(!Oþ(÷Øÿìþq(!…þ(÷Øÿìþ§(!»þ(÷ØÿìþÝ(!ñþ(÷Øÿìþ(!'þ(÷ØÿìþI(!]þ(÷Øÿìþ(!“þ(÷Øÿìþµ(3Éþ(÷Øÿÿþ:( ë' ÿìþq( #'+/3!33!33!33!33!33!3§ÊüÊÊÊüÉòÊüÊÊÊüÉòÊüÊÊÊüÉþþûþûmþûþûnþûþûmþûþûnþûþûmþûþû ÿìþ4('/7?GOW_gow‡—Ÿ§¯·¿ÇÏ×ßçï÷ÿ5'#3%5'#3%5'#3%5'#35'#3%5'#3%5'#3%5'#35'#3%5'#3%5'#3%5'#35'#3%5'#3%5'#3%5'#35'#3%5'#3%5'#3%5'#35'#3%5'#3%5'#3%5'#35'#3%5'#3%5'#3%5'#35'#3%5'#3%5'#3%5'#3µ¿¿—¿¿—¿¿—¿¿ü¿¿—¿¿—¿¿—¿¿ú†¿¿—¿¿—¿¿—¿¿ü¿¿—¿¿—¿¿—¿¿ú†¿¿—¿¿—¿¿—¿¿ü¿¿—¿¿—¿¿—¿¿ú†¿¿—¿¿—¿¿—¿¿ü¿¿—¿¿—¿¿—¿¿(ûûûûûûûûÿûûûûûûûûÿûûûûûûûûÿûûûûûûûûÿûûûûûûûûÿûûûûûûûûÿûûûûûûûûÿûûûûûûûûÿìþ;(!%)-13#3#3!3!##!#3#3#3#3#3#3#ÉÉÉÉÉ^Ê^ÊÊý¢ÊÊÊ(ÊÊþlÊÊþlÊÊ(ÊÊþlÊÊþmÖÖþû÷Øþûþû¶þûýýŽþûýÿÿÿì#;( à#ÿÿqþ:( î…ÿìþ!&þûìÿÿþ9 õ'ÿÿÿì( õÿÿÿìþ9(& õ& ö ÷ÿÿÿìþ9(& ö ÷ÿÿÿìþ9(& õ& ÷ üÿÿÿìþ9(& ö& ÷ üÿÿ9( õ'ÿÿÿìþ9(& õ üÿÿÿìþ9(& õ& ö üºÿÕ$!ºü ùàºÿÕ$!!!,7úÉrŠ<úR ùàºÿÕ$ %3!254#!") ) ,äoääü‘ärVoVþªü‘þªZäätääüŒtVþªüŒþªÿÿºÿÕ$' ºÿÕ$ !%!5!5!5!5!5!5!5!5!5!ºúW7úÉ7úÉ7úÉ7úÉ7úÉü ùàr°r²r°r²r°ºÿÕ$ !%3#3#3#3#3#ºþá°°þܲ²þÞ°°þܲ²þÞ°°ü ùàt8úÈ8úÈ8úÈ8úÈ8ºÿÕ$ #'+/37;?CGKOSW[_cg35#35#35#35#35#35#35#35#35#35#35#35#35#35#35#35#35#35#35#35#35#35#35#35#35#!L²²$°°"²²üš®®®®®®®®®® ²²²²²²²²$°°°°°°°°"²²²²²²²²$­­­­­­­­­­ûˆ®®®®®Ü®þ.²þ,°þ,²þ.®Ü®þ.²þ,°þ,²¸®þ.²þ,°þ,²¸®þ.²þ,°þ,²¸®þ.²þ,°þ,²þ.®þÞ ùàºÿÕ$ !33775353535ºúWââ„ûý!ûûä¾yûB¢üߢþ|¢âü ùàTâüþ}üüß<ûB½ûäü ýüƒââºÿÕ$ !%353555#5#5#57#ºþ¬âýû„ûäû!úÉy¾yûBûüßûþ|ââü ùàrââƒüý!üûä¾ûC¡üà¡þ}¡âºÿÕ$  $(-159=AFKOSW\aejos7'#7'7'#7'7'7'#7'7'7'75#7'7'7'7'7'37'75'7'7'7'37'75'7'37'35'!,2|5y2~~2O~~5’þþ~|~þ2~~2ì~~5’þý~}}þµ}}~þµ~~~‰}1yþý|~|þ³|~|þµ}||þµ|~|þ³}}|ÿz4|2ˆ~12þ´~~~þµ~|~þ¶~~~þ³5“4~»}22þµ~}~þµ4“5~4z2ú‰31|4ýä2~~1O~~4þÿ~|~üÌ1~}1ì~~4þý}}~þµ~}}þ¶~~~ì|1þý|~|þ´}~|þ¶|||þµ|~|þ´|}}þ4|1í~2™1þ´~~~þµ~|~þµ~~~þ´44~~1™1þµ}|~þµ44~~41þÞ ùາ7!ºøüüº²%!!!,üìrøˆüvüüºšÕŽ%!!ÕùåšôºšÕŽ!!!cúÉ©ùå ýðrôºÿ­"!ºóúùäºÿ­"!!!,ýñróˆ8úVùäš!Ž%!!˜ûnŠ‘šôš!Ž !!!büSþïäûnŠ‘ ýðrôÿ!$  ü ùàÿ!$! ±Åýüó ŠÊúÄ ùàþ7 üüüüþ7! ±¢þ¯þüüˆ¦üèüüÿ!$ü üðÿ!$ xÅû;rRffúŠ üðþ7øüþþ7 x¢ý^røÁSSü¯üþ!7üþ!7 xŒûtrÁSSü¯üþÿ!$!üò$ùàÿ!$ %!±bcúüò²û6Êrùàþ!øþüþ %!±QQü³øþ ýZ¦rüÿ!$ùàÿ!$ êÅúWýšÌýšùàþøþüþ ê¢üzøþ­¦þ­þü!þü!  #ŒúWþ­¦þ­þüÿ!$  üòüðüðÿ!$ žuvýŠüó üòýˆxxýˆüðüðÿ!$ >ÕÕþ+ý‹uvýŠüó üò×þ)þ)×ýˆxxýˆüðüðpþÿ‹) )$7632#"'327$%&#"%632#"'‚þ~€~þþ~€~þeM¥¦§¥Mþ³¥§¦¥þ>‡ÃÃÄÇþyÃÄÃÃïJ’JJ’ý¶’JJ6ýÀ``ÀþÀ``ûÿ„âqqâü|âqqþ#îu"@ÔÌ91ÔÌ990 úþþôþ þ üÏüÇ9%ûÛûÓ-pþÿ‹) 327$%&#"%632#"'åM¥¦§¥Mþ³¥§¦¥þ>‡ÃÃÄÇþyÃÄÃÓýÀ``ÀþÀ``ûÿ„âqqâü|âqqrÿ‰' '/7?G%&'&'6767&'&'7%'676727"'64'7&"'62&47\+;.81F9K5Ú8.42d;E9Gœ,:.80G9J6ü&8.;+d1O9FíLšL&_À`Jný¡LšL'`À_ü¸n<1& j(0=Ju &,A=N:0('<1& j(0=Ju &1<>EB0(úÔn_I I'[Ê[Jný¡I I'[Ê[pþÿ‹) %/36%632#"'327&#"6767&'&6p‡ÃÃÄÇþyÃÄÃà AAAA‰««–,+-,,-+öA@@ãªR„âqqâü|âqqÅû¾%ö%m±ýܱ[úÐ0$û %@%¢ü|±"pþÿ‹) )73276'&#"7632#"'327$%&#"%632#"'™r99:9rr9:99í°XWXX°°XXWXþM¥¦§¥Mþ³¥§¦¥þ>‡ÃÃÄÇþyÃÄÃØþøB!!BB!!þo–e33eþje33¯ýÀ``ÀþÀ``ûÿ„âqqâü|âqqpÿ‹ @ ÜÔÌ1ÔÄ04767632#"'&'&pih¶µÒѵ¶hiih¶µÑÒµ¶hiѶµiiiiµ¶ÑѶµiiiiµ¶pÿ‹ $32#"$27$%&#pÑkÒÑkÑÑþ•ÑÒþ•<§¥Mþ³¥§A¢kÒÒþ•þ^þ•ÒÒa`ÀþÀ`pÿ‹ $32#"$"3pÑkÒÑkÑÑþ•ÑÒþ•<¦¥þ³M¥¦A¢kÒÒþ•þ^þ•ÒÒÝ`ÀýÀ`pÿ‹ $32#"$327$pÑkÒÑkÑÑþ•ÑÒþ•\M¥¦§¥MA¢kÒÒþ•þ^þ•ÒÒ>þÀ``Àpÿ‹ $32#"$%&#"pÑkÒÑkÑÑþ•ÑÒþ•Õþ³¥§¦¥þ³A¢kÒÒþ•þ^þ•ÒÒ>À``Àþpÿ‹ $  $"327$!pÑk£kÑÑþ•þ]þ•<¦¥þ³M¥¦§¥MýgA¢kÒÒþ•þ^þ•ÒÒÝ`ÀýÀ``Àpÿ‹ $  $"!pÑk£kÑÑþ•þ]þ•<¦¥þ³˜A¢kÒÒþ•þ^þ•ÒÒÝ`Àþpþÿ})6%63"'p‡ÃÃÃÃR„âqùÖqºþÿÇ)#2ÇþyÃÃÃÃÖü|âq*qºÿìš( 2654&#"!¬ú¬«|~­þ¹àú}¬¬}|««üv<ùĺþ ( $%632#"'327$%&#"!IM¥¦§¥Mþ³¥§¦¥þ>‡ÃÃÄÇþyÃÄÃÃþ_O•þÀ``ÀýÀ``ü|âqqâ„âqqùH(÷غ ( !#%&#")%632ºOþyÃÄÃÃþy¦úÏM¦¥¦¦MûìÂâqqâþ>~Á``Áºþ  3327$3!#"'$º‡ÃÃÄÇú@1þ³¦¦¥¦þ³þþ>âqqâÂûìþ‚Á``Á) %63"‡Ãæ¥þ³Ââqv`Àþ) 2#%&#ÃÇuþ³¥¦)qâþ>À`þÿ 527$3¦¥MuþyÃþÿv`Àþ>âqþÿ "'$33ÃÃþyuM¥¦þÿqâÂþÀ`p‹)%632#%&#"p‡ÃÃÄÇuþ³¥§¦¥þ³Ââqqâþ>À``Àþpþÿ‹03327$3#"'$puM¥¦§¥MuþyÃÄÃÃþyþÀ``Àþ>âqqâÿ!$ü ùàÿ!$ü ùàÿ!$!ü ÿ!$!$ùà3Ñ…! 2654&#"4632"&nŠÈЉce‹;­~|«¬ú¬údŠŠdc‰‰c|««|}¬¬ºÿÕ$!%!!ºüòœýdü ùàr<ºÿÕ$!%!!ºúW›ýeü ùàr<ºÿÕ$!%!ºúW7ü ùàr<ºÿÕ$!%!ºúW7úÉü ùàr<ºÿÕ$ !%!!!!ºý+cýý,býžü ùàr<úÄ<ÿ!$ 462"! W|VV}ýÜÅýüó ,|VV|Vþ ÊúÄ ùàÿ!$! cýüó ŠÊúÄ ùàÿ!$! ±büó ŠÊúÄ ùàpþ„(  7& $  %ñÅâÄâÅþ;âþ<âýºýûþýýüþýûìþûƒƒƒƒú¨*––þÖûXþÖ––ºÿÕ$ !!!!!!,7ýý,rúWbýžŠ<ý)ý) ùàIeºÿÕ$ !!!!%!!,ÔcúÉrúWbýžMý)<úR ùàreºÿÕ$ !!!!%!!,bÕúÉrý+cýŠ×eúR ùàreºÿÕ$ !!!!!!,7ý+ýžrý+cýŠe×úR ùàIepÿ‹  $32#"$!327$%&'pÑkÒÑkÑÑþ•ÑÒþ•‰‰þÌ]ý£4¦¥¦¦Mþ³‰ŠA¢kÒÒþ•þ^þ•ÒÒÚO²þ¬rþ¬²``ÁüÁOý+pÿ‹  $32#"$67$%&#"!pÑkÒÑkÑÑþ•ÑÒþ•uЉMþ³¦¦¥¦þÌÏý14‰‰A¢kÒÒþ•þ^þ•ÒÒ^OÁüÁ``²þ¬rþ¬²Ocpÿ‹  $32#"$!%&#"67$!pÑkÒÑkÑÑþ•ÑÒþ•\M‰‰Ðþ̦¦¥¦„Љ4ý¢A¢kÒÒþ•þ^þ•ÒÒ¼ýÁOÕT²``û%O²Tpÿ‹  $32#"$327$!%&'pÑkÒÑkÑÑþ•ÑÒþ•\M¦¥¦¦4ý0‰‰âþ̉ŠA¢kÒÒþ•þ^þ•ÒÒ¼ýÁ``²TÕOýúT²Oýÿ!$7!!xŒûtr ’úR ÿ!$ %!#ŒúW²ûn’rùàÿ!$!xŒûtrŠ’úü ùàºÿyê¯!!!,Lû´r0Rû<6úʺÿyê¯!º0‡6úʺÿÝ"K%!!!,„ü|rhOŠünû’ºÿÝ"K!ºh#nû’ÿ!$! #ŒúWŠ’úü ùà «€Õ "*2:AI3#''%#&'52#"'&5476!!'5%!!'53'5%3'5%3#'sMM7:ø=üýü=È-0ÐY ¾G.¯b#·CýKSþ­uYþ§G†r=øýµ;ö>MMÕþ¢^›?þ?îþ@Ç7µá`½d§?\µgþíOOOOþè‚y>ý*<þ?vþ¢^hÿû—á"3263#!5276;'4?'4?26õÙu'6‡"gPù39†.4!  '*ÌC0.xáèV#m14He ®'1l1 “Z+ddÈÕ?33 #&'&+"'&#"/573;2?"#'57#&'#"#5676!5‹:¸+#9,–p!—ˆj[%+ > 7ÓVCÅCc":Àï8Õ}þV .e3B=ýSe` e9*¤=9 3@=Úß}ªÿÿ‚k %C`Œ·ã:d»æ;emu}©Óý'S3273&'3327&'67&'67&'67'32654'&'2327654&#"3672 $54767&'&47'&327632#"/#"57#"54?'&5432'&27632#"/#"57#"54?'&5432'&327632#"/#"57#"54?'&5432'&27632#"/#"57#"54?'&5432'&327632#"/#"57#"54?'&5432'&27632#"/"57#"54?'&5432'4327632#"/#"57#"54?'&5432'&27632#"/#"57#"54?'&5432'&27632#"/#"57#"54?'&5432'&27632#"/#"57#"4?'&54327'4327632#"/#"57#"54?'&54327'&27632#"/"57#"54?'&5432&'67&'67&'67'&327632#"/#"57#"54?'&5432'&27632#"/"57#"54?'&5432'&27632#"/"57#"54?'&5432'&27632#"/"57#"54?'&5432'&327632#"/#"57#"54?'&5432üB€~ %<z*+'þë)+(@&'$Ê|÷¯®ø|e‹<-A}]\ŠB-7“„1SLoWŽþäþjþã\vLLý©)(0/ (( .1(%%,* # $ )*f$% +) $ #*+f%%,* $ $ )*ûË  ü»\o÷  [ ®%)#þ·&'%ü&)#`#$ *) $ #+,üU  þQ  0 üE%% +) $ $*+‰&EC&V*,)-)-*,þ%&%&žf“‘ÐБ“fU 3HhfeefhH2Ùpu^èQFs¥£ææ£¥sKQèGýh!99!  !77!  Ñ4 4 22 K44 22 22  11  š  þã     ¨      ¡    7   Ù     úà%&%&â%'%&%'%&22  //  g     û     ‚     ý44 22 ƒ© ->O`q‚“¤µÆ×èù +&'&54?632332?654/&#"2#"/54762#"/54762#"/54762#"/54762#"/54762#"/54762#"/547672#"/54762#"/54762#"/5476%2#"/5476%2#"/5476%2#"/5476D.2`{4&/<) eˆ>þúO ,4H3R 07K÷ $  ¨ $  Š # › #  # ½ $  Š #  $   $  ýU $   # " $   #  7Q=KG<s-8PZy9z €_e""#/2dt0&2j ,: . 4 . = , ý , þü  -  - ú -  -  û  . Ó . ç  , Ê  -  …ÿ÷§É !! …Wº»Vþºþþ¹9ýÇþŸýÈ`þ 8…ÿ÷§É !! 7 ! !…Wº»Vþºþþ¹DŒu€˜þ‘Žþ9ýÇþŸýÈ`þ 8þN þæÂ·þIªëÕ 7%7&5476°9 ý}îV&þ7àýÝA“ Õ6$ ý…ýôæþ8'^4?† ªÙ!2 7%7&547!&'6íI@ýÁçY%þ1çýï4åû¨HFÙS"ýÅþÕþ="l-2ßúüDC[9ª‚Ø &! 4$32 4$ #"&54>2‚þJþÉþËþJÉhº½hÈúqŠ0Š´þ¼þ¬þ¼´^mNMn2Z^Z2ìþÉþKµ7ÂiÁÁþ™Äþèþw‰±B®®þ¾±NmmN1Z00Z}ŸÑ C"32654%"32654&%#"&54767654$ #"&767&54! ±”ggJIhûœIhhIJgg[¶‚¶ZQoyþ±þ þ±y}WZ¶‚€·[zÀADÀégJIggIJggJIhhIJgU\‚··‚\Q s™´ýý´™rW\þü··\޶ø^þ¢ø±}ŸÉ A4&#"26%4&#"326! 547&'&632 $54'&'&632hIJgg”güMgJIhhIJg#þ@þ¼þ¿þ@z[·€‚¶ZW}yOàOyoQZ¶‚¶[s‘IhhIJggJJggJIggþ ÷þ£]÷´[¶¶þþ[Xr—´ûû´˜q Q\¶¶þþ\‹}ÿýâÆ "32654&7#"32†ÉÉŽÉǾ˜þÎÙ×þÎ/Ú`Tš_ÈŽÉÉŽÈ;™þPþÏ1°2ÓY}žÉ1"264&"3264#"54327&5432#"'&'3x©©ð©¨³ò¨¨yx©ü¾&þÿ·µþÿÿ·¹€Üÿ·¸þÿ·µ Ú©ð©©ð©Z©ð©©ðýäTdµþÿµ·€‰IU·þÿ·µþÿ Œ kÕ#5AMYer€š§µÅÙ3#"'%&547654'!#"'4%$53!76=332654&#"#"&54632'#"&54632#"&54632&'&67632#"&'&676'.547>'.76$6&'&54%6&'&6>#"'.54>32#"'.54÷ [$gþi< D"ûœ"D =þif%LñçùWèð‘«ì¥¨êé©§êrƒ^]ƒƒ]^ƒŸ !! !! Ñ.› .þ *)X,)ý¯,*))+þ. } Õ›¤ª™Œ+G  G+Œ™ª¤™þvK˜K9_þâþê_9K˜Kýݧêꧦìì¥]„„]_ƒƒÞ""""ýs!!""W&. - ýì. - a)," Ã"  ))  ì  !)þ” /    ‘ pÕ%-5AMYdp|ˆ”¦Êð5#!4'&'5#2#"&546"264"264"2647>'.7>'.676&'&>&'&7>'.%7>'.676&'&676&'&53!76=3%#"'676%27+%&547654'7327&'$%'#327%654'&547ì1ËÃÄÊ8þú¦å楣çæ¸¸¼. . . . þò - -Y - -ýü))G))ý«))U*)þ>- - ~- - V†áØùÉØá†þ©K;þŠ yA øøC0Bøø Ax þ‰;K'6FJ> ìþÇþ$06þ#þÇë >JF6&Â@Ž@1Añþe›ñA1@Ž@Hç£¤ææ¤£ç­¸€€¸ . .ýµ . .E - -ýÿ- ,1))Õ),(9)(©))þu- ,æ - - Gœ77Wþõþ6Ê W77œGå D&& ¥ËÇe‹‹eÇË¥ &&D "(=ˆ—pp—ˆ=("¸uÕ !!'!¾°úPnàÕú8Èhûø·vÕ "!!'!##+572367676½³úMoà‡L)þ¹>u eÍ¢—I3?b›aÕú8ÈhûøžA:þFþ½;/Iƒ¦t³»x·vÕ !!'!  ##' ½³úMoàû÷›—_þh˜[þeþi[—þiÕú8Èhûø†þi—[þeþf[—þl[š›š §@36273 ##'5ñ) UþÒ.WþÔþÒW1þÏ@þ ÞUþþS÷þ Vùõdõv#,5>~3+&=43+&=4%3+&=43+&=43+&=43+&=43+&=4%33 #&'&+"'&#"/573;2?"#'57#&'#"#5676!5Ö\:V\9\:÷\:]:&]9[\:ý«:¸+#9,–p!—ˆj[%+ > 7ÓVCÅCc":Àï8Ï #8dÏ#7ãÏ$6¹Ï$8;Î$7iÏ$7ëÏ #9špþPL  )Z. ;6ýŸZV Z3%Y63 .87€ÂÆp ˜“Ù 3DMy!674#!!6?676545&#'323276767654#3#&'&'454632767!672!&=75$/563&43!32+'!67#>54&53# û ?ô I :W0 96;E,Q 2:&l6xý0 bˆm! oþÛ¸½®±"\ü>%Eþfþ™~ýñþe2UÖÉ6g!6V#p5Cü+ C þ˜? P9 @´7H4XþÍÇm†M7RV /‚M(=H: ,qLUD)8ˆWqk±’e-Pexúó —NW =$ U  þ/0c›™)H?õ2@[nDF8T$.J?¬Ù !' !žTú4X—ýÍKóGýÏÙþwû´L†þ5ü_¡K¬Ð !'7›ŒWú4Z~Ðþwû¿D€ŸÔS&5476322632%632#"'&'#64'#"'&'&54654&'&54767632xØJX%&XA,B:\8 [ÇEMH95##Fl% !9@!#jLÏ p_’ÎMi#"?8" %lF##58HN4hok@RRr*%tþëe BB9'7*$%) "fXSþÝçò5EIþêf" )%#,7'9CB «˜>E3#"'4332327$'#"$4727%672567654&5&§oJ°7.˜b9M þúƒD† þ²ý™,B3 qY 5*Ä*]d=HN9%‡å ˜sW$,þðJ ® ]T-MMñm@eöd: ,'Z MÞ 'cM&T)$$ <þ¥ „Iµ¬ç2%!"&54676737#&'&54>;7!"&546767!7!"&54>3!6763!26ýP+=6ˆ/2´þÿD>Rý+>2,+v*>ç>+2  ,2 =,2  =,3>,¬ç2463!2!2#!!#!32#3#!¬>*v+,1>+ýR=Dþÿ³20‡6=+ýP#,>3,=  2,= 2,  2+>²Ô{"D%4&#!"!0#"3!!"3!#";#"3&'6737#&'6737!"'67!7!&'63!67!2I0!þŸ6üÙOSÿ þžSS: ÝSS®>SS]]J«]]äþÆ]]hý\\, Bv*>Kñ%39’LKIOKHLKIhghghghgE?-ü³“LÊ!D72654'6#"'4#"'54#"'54#"'675674767#%±$4:JILLHOKHLKIhghgighgD>-ü²sJ1 b6'SSý cRRþÄ ßSS®?SS\\K¬\\ä;\\þ–ý]]üÔ!AþŠ*>K›±Ó{!C%254+'3254+'!254#!'!24+!&#!"463!!2!!#!3#3ÁSS?®SSß *vA!,]]ýj\\þÅä\\¬K\\ñIKLHKOIKL’93%üëN-?Eghghghgi“ÿùLÂ!C32=732=7325732'654&#'%2&'&5&'5&'±IKLHKOHLLIJ:4$üëN->Dghgighgh³SS=®SSÝ þÆSSb ýSS'6a!0J)K>*þŠB üÔ\\üþ˜]]:ä]]«J]]}¯ØO€‹•¤³ºÁÖÜãçëïó÷ûÿ &*.26:> 3656;2#'7+"/#"'+"5&54775%"'5476;25'7&56%635&56;374765'75'76=4'&+ +"'4!#"'4543$365&5&#%#754'&5&&547'5367&547+&'&'735&2?"5%75537'7'3533553535'32767&5%2?&#%55'575775775§1’:e ,$?F?Yø>êF_LA3E¾L¬¯HÑ3,8LYLäþÐlEF'!0<… k#gþšF  ™EeYÄ!! Gà€p&þ­iq.8ZN$ÏË%`BCëf F4ùü"4._?ñeËe3&{E(1þõ-ÖÙ+$Kt8¿ - ‡ þ–$þÖÌGs ¥sM ýùrEF›"ºã2 >_ý£pÌØÁlTErf^5.>=9|5"-l)d ,&>vv]cccWpC-+ d•8 Bpp>W]oØæaþxvuPp82,ŽD¨ ^8, ^B$K»œ+ "1®R[+Òe*ñ;Ð 2 ››W Q—›Pþ‚ I&? gpëo% w ^SA$ Ÿ2 9i-5n02¢þ†Ç¿ Ai&®IY^P]D›%ˆ\??\OWC ,,1 /211/=;7777=321811{908hN%b\Dh,)h?Ì17I21!122223 21¬Ø&2%2#"'&=477654'#"'5473Bq4|ýl anN iü¡¡l‹m b üµ9 bØMOŠbý >YÑaYÆ®58Šl7P ÌP@ ª‚Ø$0<FX + &=6&# 3 6=%&#"';27!5%67%!&'&'2+"'&=476‰rþÃÂÔ þ„þúcR¹¦§þÔë~²ßUêYþ­þÔ¨Ñ082.Ôþà_þWÒ†_þV"+Ò}þIR8D).P9H'Øþ¯Öþ°ý“ƒ®²S„¬]þÙ±ÂþÄâŠZ–¦YàûHYoþ’X(I_ Û þ’‘°;.2lOþÏP%.G6R%&I8dÈØ)Nl>54'67&54&#"&'632.547#"'&'#"'3267654'7327323.#'654'567654&&5476;'&'%&+"#"8DH$$yU ?L[>!Wt…Jþ([Fho*Ám¦.2¤\=w\`|€UP7¸:/E" @7? EPþ¦¢]Eixà Òp·‚F@¶T5û†ym´,±"&e¿B@q¦€²‘(‹A _Ã% #+B7!þêN &".ËOý²S$ªXE/K(Aa]dLP*'FCaYr= CÙ44mo C‚þÿ ¶ “(FKœWYŽüF•¡vb‰ph´'UD˜'R< $ˆ“dÿüöÑ#+?Vmƒ¢­²·¼ÁÉÐÕÚßåíô#327&"#"'7'632&'$54#&73254'&#"'5&567#&''5$'67'654'6'5$'67'654$'67&'654'''5$56732#"'&#"&'$'63&47"7&'7&'7&'7&'54'6546767675477&545?&''5&#" '6%35&'.54>23#67!&#"W  OB7[l#> Ì Fþˆ_Vh "ï "®@.,=6tJþè€4V®¶p1EQJþ“qMiÎ v–h£ýþópHI£®!:¯½JJJþ Í=4m\8B*Ò®?o þv!"ºt,`—s&*_~P1>5='g=>24<+ý-s[,*&sd1PT>3J@='h<42åJ-H#*þõYT_Y)*)X^TYþõ*$D  ?>}>  *0üt"J. ™Œ ‰&b54CUE ''¤!`9 !,(MTE *!… }q~=/+)f[4f !B" <@0&9c?"V+€GoM“K~a„? }b‰9e\ P&0@k"?c*‰G€E‰JŽX ?„e}9‰ \«ÎÂ4 ¥\6 '''' 6\¥ ªNÙ(&'65&'67327&+!65+"3yyys{Åw Êʃ£µ®©³¤cþ¦c¨³©®µÞ±±ŽqXŸþñe¸óó·Xcþ÷6þ µ®®µô6 cªÿýˆÙ ,35'533#3!'#'5!5!5#53!5!5#!!-Ê·«}} ”þcˆ±††þk–v¤G þôGþõ @<<3ff¢X¯ý苜…þ›‚q°X¢üÏ»GccGª›J 326&#!2+73 ### 3(ÑttþŠvÎÔÔÎÑÚÜþg®Ûï¦þÛ·þnܧýØüŸßØÙÞþÍíþBþ(þúþéᵪ²×33#!!#'!'57!5#'5735׫¥¥$þÜ«þÞ"££×›”q˜ü‚~˜q”›ªÿý‚Õ+!#!573#'5!3!'573!#'73!#'5»¶ ;jjýÅ þJªýÄss<ªÕwýÑ¡þIžýÈjj8ž·¡/w²{Ø,32#' 3%+ &5%6323'#57'53œÝÒïþ©Ñ^Vˆœ ŠQ6þ‘¬>ѨA±©B¬ÕØ’6þÊž¹þ’ÖG2k >ÀéªîYþ3É~»||É~ªƒÙObs32732753"'#"'4323$4'5;+"'#"'53275'&'&5?5572%#&'&5%634%476=%ê@.!%,BE,#!-Q²2" $nÞàL/P¥uHED8ˆ32#"&546324&"26%! ! – Å’ÉþbþäþÞþ{±•ÆÊ=&*<<*(;ýE;R::R;åþKþÉþÊþJ¶67µøŽÁÏš˜þ{þɬ²Àž)::)*<<**<<*):<'þÊþL´67·þIÿÿª‚Õ&é'é†é ÿÿªƒÔ&é'é…ê ÿÿª‚Õ&é'ê…é ÿÿª‚Õ&é'ê…ê ÿÿª‚Õ&ê'é…é ÿÿª‚Õ&ê'é…ê ÿÿª‚Õ&ê'ê…é ÿÿª‚Õ&ê'ê…ê ‡ÿê¥á @FLRX^djp3264'&#"&47367'676756273#'#'5&'&'7&'677&'67'%%&'&'%6767%&'ô0/CB^0/AC/ýpkTcR|Â'N(¶‰OfUippqUfOж''NµŠQaQhû!$ Žb€)·dŒþ¯ËL÷Qýk þðK´ÌRþt!% cþ'¸d&„//^„000Ã'N'·|P_PfppoQ`Qy¹'N'°ƒP\ QgppmQ \Pyº,  Mª ÆNþ>&«`‚7" bþK*ŒVô&"¬g{‰ þðM¥ ÂMª‚Ø %1=! !  54 #&'&#"#46324632#"&%4632#"&ª¶67µþKþÉþÊþJ˜]î_þ¡þþ£·EASvwSAFÍ’‘ÍäD10EE01DþD10EE01Dê7·þIþÉþÊþL´6õþŸaõù]þ£ý“U@SS@U’ÌÌÉ1DD10EE01DD10EEª‚Ø %1=! !  54 3327673#"&4632#"&%4632#"&ª¶67µþKþÉþÊþJ˜]î_þ¡þþ£úF@SwvT@EÍ‘’ÍÙD10EE01DþD10EE01Dê7·þIþÉþÊþL´6õþŸaõù]þ£þñU@SS@U‘ÍÍŽ1DD10EE01DD10EEª‚Ø %1! !  5# '&'32654&#"32654&#"ª¶67µþKþÉþÊþJ;lWPiþØhQøV<=UU=È-1×\ ÄH0µe%½FýKSþ­wZþ¦G†r=øý´;÷=NN$‚E| 1† ?’'Øþ¡_œ>ÿ?îþ@Ç7µâ`½d¨@\´hþíPPPPþç‚y?ý+<þ>wþ¡_VG{?,rC‚AšÝƒ×+ +"'5$76%&'547327676=&#~‹jþt³°1/Q}›þ²ˆ]•(Éþ¯+VžéRþ¥£ž×þ‡­ÆþxþÿbO±Ê àP >ÅþþnèS˜ô]] =fªPØ+! &56;2'5$%75#"3¯þuþïiŒ³±1.Pþì~šNˆ]•(þ7P,VŸèSZ¤žy­ÆˆcO±ËþõþpáO >Åí‘èS–õ\^ þÂþüf¯ÿ0:Û1>7#'#53'&'&54767&'&=33676=3#326'&i($…„lØÜÊÊ“m……$(($…Ž[Uu&tU[Ž…$þ÷&uU[·‚·[UV$ƒ½¼|dÑdÊËbÑ e|¼½ƒ$%ƒ¼ ‚ZSSZ‚ ¼ƒ%_TYþüªªYT¯þÿ*Ú $4&#"326&5432!!##53&w衤ç礡èüá©PïìP§‰³þð¬ýýº¢¤ää¤£ØØëîïLþ´ïñš~þø|þÿ|¯þÿ*Ú $32654&#"%#"54767!5!33#b衤ç礡è©þ°ïìþ°§‰³þð¬ýýº7¤ää¤£ØØëîïþ´Lïñš~|þÿ|þø¯ÿõ¥Õ $&$76+"'&5'476!7!ôtþÆtsstEus ¨éí¦¦¨¦ÈpiþŒîþ˜d5s qtþºtrtt<ýÖ¤¤¥ëꧦg\Žþuþ¢lãèSר5264&#"#43233#!5 zª©{y«–¸»mã——ü«ò««y¹þü¹¨{Åú(|—ÀjØ#53533#632#4654&#"#*jj˜²²o˜²ïŒ—›onš˜—´´—þ}mþþ»Zåy„îH{©ªzþFø2Ø 1"32654'#"&4767!!53#5!!3!!#3!!ÍpOO87O:=0L˜mk™L/>þàþΛ›2 — 1™™þÏþàŒO79NN97Â0LØ™™ØL1ŒþK—Ó˜þJ¶þJ¶˜ý-—µ'Ø<%#5#535&'&'5'73'3#'73'676=35'73'33â—°°¥z–‘˜hNg‘’—‘‘eMj‘˜–z¥¯»»»—‹z–×TŽŒ™™ŒŽT—hOÊ‹šš‹ý7Nj–YŽŒ™™ŒŽYÓ–y‹—ì?Ø! #!!!'!27674'&#ô™.d û;6þªzüý‘F‚H¢%QØÐM_þö\ý­‚ǃþ$‚P¤<]ÎÚ$!#"#&5463 67!2#654&#"Vþâ©"T†]£’tƒ„s“¢]†U"‚¨þâXÅ"€€„¡„»üû1((1»„¡„€"Äüûý¨·uØ." 6&'67>3"#"54767&'&#52•“ÎÎ&ÏÍ•ƒLVa¡¹{§ƒ.+’þÙÓÐþØ”)0«z¹ºHUM\ÏþÚÎÎ&Ï–=B´®lþÈl)•ÒÑþÙ'ÑÒ•*l8l®´B=ÀjÚ&'5 %$ 56?63#'¨ª[øW°¦¦°þ©ýþ¥ª¨˜tuétuéü4”ZZ—/ûâ/˜[[”5  ç@Eo&<"3264,'532'&54632264&" &$#"#"&547>B¾„„_^…þ^þÈþ”™ýl;‚ƒÍ“•ÎhIöý«^……¼†…9l™ýþ–:…ƒÍ“•ÎhIõ¸„¾……¾þ (+|Tl§“ÏÏþÚgML¶„¾„„¾„x)+{Tl¦”ÏÏ”’gMM ØM>54'.#"32463227#"&5454&#"#"&'&54767632254&”K2q'$#K1o'#0ß´¬ÚþÞ†GdAoc¤„–.% 3t88<­/3u77LJMŒq'##K2o'#$L1ð¡Éݰ»þ ´|XHwyoÇᆥ›<‘>b€WDs-Kx68<32>32#&'567'45'#&+"#4'3>$4&+"¨?w(ÆK>R0ƒD32>32gYYËËYËD,.:…?—#)³•˜Žv$·E?w(ÆK>Ro}v­¯vxJvÙÈüašAjâ×ütA¾¿O°]¼Æ€wϧÛÊý„©ƒØ!5!3##'!5!~ýÒ2˜þÿ˜üÃk<ýÒÆ@˜iý80þÿý80üÃk<˜ÀjØ)127632#"'#576&#"4'5267>327&'"SkQmÃy™Ž›z,~Ѝšzi2@:$šž„“¡(.-)zW]‚¡ Ÿ˜Øüžݾg‘«… ×vxþ-ÃýðaÞþíÉX[–ü&‘Ű9{¢'Q32263227632&#""'&#"#"'&#"#'3232762327632&#"#"'&#"#"'&"#'°Es- p¬86rV+)|m^?_354.#"!&'.54>325467675#53533#63232>54.#"P#3Jœ“TRJWVJQS“OMJ4"þ?õü¹*&ElnÆhPL$ llill %LOhÆnlD')ýÖ----Ç+)Q•PQ’((’QP•Q)+ý¸þÑ/þ 6ÄklÅj$?6FWWF6?$jÅlkÄ6 þ}++--âJØHNRh|¥»&'4>32"'4>32&'4>32&54>32&54>32#!5!'!567>54.#"32767>4.#"327732>4.#"327>54.#"732>54.#"M_ 669ï4S55.½+C55C&¹.66 V\+55 c ü£$µþ•·×üÿMç##$ 6ˆ»$#$­s`%#$þšd0"%)h¦ #"#û_33@þ¾]22-"ý40446üÊä/*33UýJ"+33^þ1ýÑ/þßáK=Ó0¤¤T* ####ýø ù #&$$&#ýÑ#&$$&# ý B #### üÚ*ê"$$" UÿþÖÖ!'-2!35!#3!53573#'5#5!35!75!!5'57!«s\\sûs]]s ª—ûîJRüÚRIJý~Ö››¢–ý·•££˜E—¢›úû77êƒ__ƒývt€€t4!ýݶvØ7CQ^&54767&'&'5676767&'&54>32! 535#5##3654."!2>4.#"Ë  <$))+N-N*)N-M,**%:  ü@ ¬¬v¬¬¢<-MTM-?ÞŠþK5:664”59<5&?HPPIK* ')+K**K+)' *KIPPH>&5<:6þu”N|l||l|þ²-I+N))N+@6þ¬:55:5QÛØ)5>o654&547!&54='&'654'67.5476;+"'5#"=6&'76767%25#654&'Fzê-ü6 ŠZ8.ž ,‹N0H!h6¢%`+EH )Ø#M ;,Jga#‡iR þ‚ k'þ Mè +1^hgþ½üo8: (@sŽ.PùÀmz nxþë?.#1þp#41§`&>%!ac,,ÅÀLHJ ­xþì}647| + OJJ)!0ÿÿû× P[32>4.#"32>54.#"!5&54767&'&546767&'&4>32'&'.#"¿:e79e89f76e`[ûÙS ¥’&(*UM,N)(N-KV)&&“¦ \@Eõ€õCAÁpd88dpg66ú»9: %N&KRS—* 'TM**MT' *—SRK&NþÛ :9„}qyƒƒyq}ÉcØ $TdhyŠ67&'&"!3!67>54.#"!&'.54>325467675#53533#63232>54.#"!57!&'.54>3234'67632!P#3Jœ“TRJWVJQS“OMJ4"þ?õü¹*&ElnÆhPL$ llill %LOhÆnlD')ýÖ----¤ýsÎþè=BDw@>=)ñ)==AwDB=þéÇ+)Q•PQ’((’QP•Q)+ý¸þÑ/þ 6ÄklÅj$?6FWWF6?$jÅlkÄ6 þ}++--ü¶ÈÈÎ !yCB{C!$$!C{BCy! âJØHLP&'4>32"'4>32&'4>32&54>32&54>32#!5!5!M_ 669ï4S55.½+C55C&¹.66 V\+55 c ü£$ý)×ý)û_33@þ¾]22-"ý40446üÊä/*33UýJ"+33^þ1ýÑ/ËNNþÖOOUÿþÖÖ%)5!5!!35!#3!53573#'5#5!35!Öý€Êüìü«s\\sûs]]s ª—ûî„^^ü/oo#››¢–ý·•££˜E—¢›úû77¶vØ4@4767&'&'5676767&'&54>32!&535#5##3¬  <$))+N-N*)N-M,**%:  ü@%¬¬v¬¬<5&?HPPIK* ')+K**K+)' *KIPPH>&5<:6þn”5|l||l|LàØ3?HN654&5473#!&5454'+#"#7&'654'67654&547;2547#";65'"3%:U"-ü6 •Bu Zg0krX0c-h8ªE+`%s H>É4wM-æ'9.QY / þ¬üo8: q§’hPSå¾mh #%Bzÿ1"0@þ)5"@YþóýR0ÿÿû×.&54767&'&546767&'&4>32; ¥’&(*UM,N)(N-KV)&&“¦ 9: %N&KRS—* 'TM**MT' *—SRK&NþÛ :9CèÔ##"'##56'##"/547?˜^Øí'¢5@š_*S×U&/U»L »;YÔ§þìþê9UP(§`Ý þå–—X…ÑI.·Ë‚¥¹sÑ222732#&547636=4'&# #4'&#"*Út pëÑz&Ýþ=<ý xÇQåÐÛ>h½G:þøV He½®k%ÑþíPF5ÓNP þþçýÙBí²¶Ò|-þÛäý¼»¡—&¬pþA&NÑà±FXÒÔ &&5 –<ýãþF:þþ^Ôýý;" €V –ýgýdœäGÔ7236;2"##'65##"'&5476;235&'&=476—Öe x<JTî`Ñ(GòeRÐUdõ¿föB3 VNT<×GÔã48u <ÊB<Íh  þ©m€MþëÉD:éS GEnQÉhBêÕ"<B+"'##56#+"'&575477;2732;276=4'3&'"—~Vµ"0‘b*S×V*8Ui QÎ"_|þ®Q ”)w`·Sg‰A ËæŽ? Õáþ6‹†ÑNÚ þè›—#eu®B? ÈÔgIÇþo‚…»5 ÚF”§(°ÏûpZR¶wÔ2367632#&5476(Ët*#\l‡Ñ~ ÐýïýΨÇTÔë]1¢klàSIúãý…±ÍÚÓ|-XÒÔ &47–÷EýÄ ýÎoÔýsU ýÝŠäHÜ2`g26;2"##'65##"'&5476;2&'5476&+"326733276=4/#"567654'&#"35&5›Ëhr=)ø\Ò"IñfRÑUdöÀg÷C3&=cØGð© kv ó=ˆ=NÒr%SZž¨U ý6vk »6)¸S<ÜÎF9„¦×8:Ód  þ§m€OþèËE:ëR p±&Ëiþ³›Ì  ÔC”]&'Ax†…¾.+Þ0 ‰nM,¶WÈûÎ`cØÿþTÔ!1M3#&'&'0546%3#&'&'45463#&'&'546 $&54673 $64'&'v ‘Ì £0¥A ’Í ¢1¦Y ’Í £1¦,¨¾¹þ±þ”þ±¹½¨ƒKV§.J/¦VLƒ9?›c‚yɦt3Zrˆí`í—Å»þÎû²N‹ ±Ñ[OÄ}¡šýБBr‘­þ1±a`°bb°`a±1)EM¨˜UU˜¨ME)¬ÿö·Õ3#"/4?23hHÄ0#ÔÓ!cZÕúý£-…‚@Ô¬ÿöoÕ3#'654'&'#"54732XW­ðz=ä\à9òÔ`YÕ'6?FŠ’` 1TFG*ûÖ™-¨ƒ@Öxÿ/³Õ#"=4?2%#"=4?2ý³À6Õ±'VQýÜ–Cت+YPÕÊúí›*¤~: ©ûÛ‰8›z"CÓ¼ÿöoÕ"'4723!# 5472!5kþûéémOü¥òþþìdXX[ÕûÕ¤Š;•ýZÏ$±}@áÏœœµÿú"Ù 3363'$654'"»-ª‘ãý³8¢•wÙü?íþÿþWXÖûÔþc1 ”¬0Ù 3%#'#3%#±)N(þ¯4Ùþ/¡ûcå Šý™þ}­4Ù(,377#'#'547#5773%%‡.wwzy.þà** þà ÙþÐ<<þ‹7‡Cþ׌þ¦A<Ž<{8‹Aþú†Mý–þ‡‹t¬PÙH#0#"#"'5654'5673;54'56732733273+&+#&"#&'565¶¨³P8 cc 9Sµ§ƒ:-Œ-:ƒ£µS8!cc 7P³¤ƒ:-Œ-:ƒZ#;17F-:ƒ¯µS8!cc!8Sµ¯ƒ:-F71;#ZþRµS9 cc 9Sµ„œÙ ;G7567&'&'3#6737'#&'7#&'6735'67#3335#5;0/_í^//þ†,/,-D--C-,.-äÐ zz Ðä~jihh~äÐ zz Ðä~hihj~ntt6tt´-.,-C..C.,.-n//_üR_//÷~ijhh~þNÐ yy в~hiii~ÖÑ zz ÑsŸ.þ€€.Ÿ ¬¬#.6CMhwŒ—¦¯!2732!'5675'&=32#$'57637&/&+"+&532?4/%32#'#&&=4?#'57335'3!273+#='!"/547354;2?!&=!(‹:Mb­þ¤R».qp7°þÓž,ÊÊ_qýð >MT,›•P$’ ´6œ…6$0_äý u3d´­Ud€tœÈ_}s*$Ä"ìRþÖt0XX__ç/iþký¸=Z¬G†8þÔ*F €1 þƬ.þ­ Å þÑŠfþõþï)MC =þêg9E¢k×O 9þË!(-ƒ);& Æ þ]¬þÔt!þ±y" ³þ¥†& 2| ¬ªb³a$ U+œ Ù #8M35733!&54?'7'327!!"'&%#'7367654'77'7'&#"'676Õ«ºÊ«þýó,&T>ë=c#]–þK9.‹U¯§ÒÒÆ:1ʈó%üæ©`T?ê7>54&#"5>32&54?'7'327!!"'&%#'7367654'77'7'&#"'676]Týñ@Í1$Jë=c#]–þK9.‹U¯§ÒÒÆ:1ʈó%üæ©`T?ê32&54?'7'327!!"'&%#'7367654'77'7'&#"'676Z _­3lFHe5^\VOosHGJI)`VKm1šSüj,&T>ë=c#]–þK9.‹U¯§ÒÒÆ:1ʈó%üæ©`T?êë=c#]–þK9.‹U¯§ÒÒÆ:1ʈó%üæ©`T?êë=c#]–þK9.‹U¯§ÒÒÆ:1ʈó%üæ©`T?êë=c#]–þK9.‹U¯§ÒÒÆ:1ʈó%üæ©`T?êë=c#]–þK9.‹U¯§ÒÒÆ:1ʈó%üæ©`T?êë=c#]–þK9.‹U¯§ÒÒÆ:1ʈó%üæ©`T?ê32#"&–¼eËÅþ›þ|þ›ÅÉeþ(®<X<®³þħ¨þñ“"32#"&$2#".46–¼eËÅþ›þ|þ›ÅÉeþ(®<X<®³þħ¨þñ“"@<#"4.#"–¼eËÅþ›þ|þ›ÅÉe:<#"< !<"#;ÙÁþ˜þzþÆÆc†hÁü– =B4.#"$32>4."–¼eËÅþ›þ|þ›ÅÉe:<#"< !<"#;ü¥"< !<"#<@;ÙÁþ˜þzþÆÆc†hÁü– =B54.#"##"'5##"$'&'0!5!5&'.4>32!!676767'%''H&(G()G'%H(ã%'©þËV W3WI„muþ‰w>DE}AB|GE=mþ“d^„JW4W V›sÖ'H''H'(H''Húò`XAK|@X1(Ô3"|ˆ}DD}ˆ|" 2ý/ "1X@|þµAX1# Ö/ 673&/'67 &'"&'6?&'3 ' âK[]œ><+Gg['fBôòBe&\hœ?(K?œ]\Kýã ˆŠ!½þÐ;32T $ù #µ¤ACÅþâ«,þí¶ÅMMMþv ÕA5p_9D-ïþ–þãM**¤  û”B@0"@R//>wA·&oc/D&3.YaQ/5"1'"þ´þuŠE62/uŠ= =!m- .... yÿì²Û 7%  %  32+#".=!"&'&'#&=4;7337_þìþì‹ý%þíþìŠ Ö8)Â0/­_^«^ýM^¬¼­1/Ì 9×53üü4í<üÄ&&<üÄ&Ñ*(üÇD>?GGzB6üÊC{GG?>D9/››Cþ½›}®Û&632#"&'.#"'#!#!#Ò¸ž62ÉK#+~KF0R!9'/N£Çxåí_TþâŠþãV_‡T '¦NQ9;:#8³HL"CþßD™ý|ý¦×þ)×þ)Z)Û 532>4.#";267#&=&$32735&'.4>2æ2[02[24Z1/[»–á­þ)'5*+þXÇë A54.#"%2#".54>''#/'7''7''7/5?'77'77'7?3777'†Œû‰ŠûŒü…†üŽ'H)(H('H((I\Hs=]SH$e$HR^323#67#&"#"/&'&547&#""'6%676V ón*[‘n%'ZxL0<{2;&b;0&8a>!U*~EmL‘K}`? {aˆ7c[ O&0>j!>a)†E~CˆK‘W‹ ={d{7ˆ [+M57LL75M-Z '*''*' Y (€5[£ J5( üÖ\d (5J £[4Œ ''Ó/7O_Ÿ«³»ËÓÛãï2#".54>&'32367&%2327654'&''67&'&'&'676765467654'&#"7>326323#"'##"'&'#"&'&54767&'&54767232&'&#"6&%6767&'&'&#"676&5467&'&6732767&–$$$$áOG3%V c˜â™c V%4GL944m/122102/.303112.OF}6&V eý´"w?>v"¤pt #8ˆŠ7! vn¤":;@A<:"¢nx !6Šˆ6# sp%./13/.UVT\<>"$!!þ• !"#">k•c V &6|FO 93399 <>#"#Ö><  "$ZTU./43..V5$##$Ù59gT;&'9Z^^Z9'':Tg9§'(''&()I8:9889: Z_59eU;'( ³ÀÀ³:8.>euvc>-7:°bccb°;7-?cwud?/8K¢WZZW¢ **D@@D+8(':Te95^&)(&''(ÖDA:AD.*!žY[[Yž!&Ö !-x†”67&'67&'4&6%67.'%4'6&#"&'6767&54?67&'&#"#&'#&'5&'"'67&'&47632>4.#"%2#".4>Î'7,3 3%/0þÜ),7ß=*#þ×0*+3.22'þ÷8  Yf·©T,1'¥¤').U¨¸fY >98 "2 B2;F_ XB?2C 3" 894i¼hg¼ik¾ce½"SŸ[XŸVWŸXZŸ„£§#eªjpÍMcNTvJKrZ1VlLWMI ©p jª…k%§nA V{ww[1‡ôô‡1[ ww{V @#fÜd-#JM 7B/"‰‰"0C7 NK",dÜf#ˆÎ½hh½Î¿ggQU ®XX® UdÈÔ %3!'#!52#"62#".54>Ÿ"ù­ ýh9ý|M463%&$$Ôú5 ª›ûO ÂDý›n;ý $$$$ªôØ33'554#$/ÑýŽ[ƒüQ‡wþ“ØýâGSGýUW GJG¯þÿXÚ .5CK&5432632!!#!##53&4&#"326!&&#"327&54654'X©Pï™}}›ìP§‰³þð¬þ~¬ýýºÄè¡C;Œ„7?¡èý_Xe‚jõ;A¤çç¤>7†'sssîïLFFþ´ïñš~þø|þÿþÿ|¤äžÚבØþ¼- þø Š䤣Ø”ÔÛþl¢£rr£¢¯þq)Õ-5DL&'&6767&'"'&'&'&5'476!7!! 76'&'&'6'&ôuœtss-5 œ—Êá¨ëí¦l&‡k¦¨¦ääpiþŒî„þ˜dþ˜=pDiûê/tEust,2—œ}tsŸ5sqtþºt-Ô›—1ü ¥¤k‰&i¥ëꧦ g\Žþ}þuþ¢läç  þ¢An?\ü27/rttžs,Í“™}šqt£¢þƒÉÐ)8GO'"'!!##53&'&54326!7!&'&36'&&5'47&#"327674'¡¨ë U`þð¬ýýº‰©Pïš}z“äpiþŒîþ˜dþÒuœ>7‹;C¥æüË‚Œ;C¤çç¤>Òxts”¥K) þø|þÿ|€îïLGD g\Žþuþ¢läçûsÚà”èžýþ™ÐÕž䤣Øhk¨¤rr¥£¯þƒ²Ø .4&#"326&54762!7!!!##53&w衤ç礡èüᩨ±æÁ”hþŒîþ™n ‹¸þð¬ýý½&¤ää¤£ØØëî羚 o[ŽþŽtþ¤‚Ñê‡ý|þÿ|ÿ ÿãÐô+D#"'&'&'&47>76327'7'%'27>764'&'."(F3"D"&%#}bV`ZZ^;D"&&$ƒ[X]:3Gþ9þ†á:ÎÀ‹]†þ:ýF=~=HS]³^X‚&% «iŠiD^‹¿þ29áþ½i‹üü\=<€<92-1X?:„<91*Ïþ£î=X62'%'!!#5!5!5&'&'.546767''7'''7"2767>54&'&'&î4p6³â9ÎÀŒ]³.(EGGE@Z-<þí–þí81VDEGFF'1þ9þT]ŒÀÎ9âTþ:þŠG5>+.11./:9Š5>+.11./:9‡ ž\Š¿þ2:âþa(E«b_«E@( ¸–ðð–¸ %CE«_b©G(HiŠjËâ:οŠ\ËiŠjÓ+.wBAw./+.wABw./€ÿã4ô+F!!#"'&'.546767675!5!' 2767>54&'&'&"¥þí<-Z@EGGEDVRbfNZ@EGGEDV18þí¬kbbjøC9:/.11.+>5Š9:/.11.+>5Õþç–‘ (@E«_b«EC%##(@E«b_«EC% ‘–­kaþŸjûP/.wBAw.+/.wABw.+ ¡Ä+F####"&'&'&54767>32333'7 '%32676764'&'.#"‚Ü–U (@E«_b«EC%##(@E«b_«EC% U–Ü­kaþŸjûÉ/.wBAw.+/.wABw.+Ÿþí<-Z@EGGEDVRbfNZ@EGGEDV18þí¬kþžþžjøC9:/.11.+>5Š9:/.11.+>55Å @  ÜìÔìÄ1ÔìÔì0432#"732654&#"ÄÄþéÄÄþ陽……½½……½ëÄþêÄÅþêÂ…½½…†½½5Å @  ÜÔÌ1ÔÄ0432#"ÄÄþéÄÄþéëÄþêÄÅþêúºK +@kk k kÜìÔK°TX¹ÿÀ8YìÄ1ÔìÔì04632#"&732654&#"úÏ‘‘ÏÏ‘‘ϘuSSuuSSuì’ÍÍ’‘ÎÎSuuSSvvdPK!)7eK° RX@ *.,&"($ k3,k($kk8ÔìÔìÔìÔì991@&"6k0k 8Ô<ì2Ô<ì299990Y4632632#"'#"&7323&547&#"%6547232654&#"dÏ‘RDDR‘ÏÏ‘RDDR‘ϘuS?>Su^222Z>?SuuSì’Í Í’‘Î!!ÎSuXqpWv28ML88LM{WpqXuSSvTZ`z8Rm3#"2767>54&'&/2"'&'.5467676"2767>54&'&/2"'&'.5467676––R#)$#R#$ $LK:C.25521@=“:C.25521@=ýR#)$#R#$ $LK:C.25521@=“:C.25521@=züàZF)(JG()K¥.2IF21.2FI21–F)(JG()K¥.2IF21.2FI21 ªJ7Qk>767632"'&'.'!"'&'.546767632$"2767>54&'&'$"2767>54&'&'÷#61@=HK:C.25521@=“:C.5%þÅ'21@=“:C.25521@=HK:C.6#¹R#$$#R#$$û÷R#)$#R#$ $5[51.2IF21.4`]21.2FI21.5[F)(GG()FF)(JG()KÒæR 5%%%xüÌÜÜþŽrÊü6þŽ׊eMºM^‚xþ„x‚±V–)7654'&'575#!&54767'5!s_vR$N:È:N$Rv_£þì¤{aT,X@X,Ta{´ß4b\)1¢%==%¢1)\b4ß´»:ê`¾\KDúúDK\¾`ê¯þÿ*Ú 4&#"326&'&5432#w衤ç礡èþ$º‰©PïìP§‰³¬¢¤ää¤£ØØþ…€îïLþ´ïñš~ý{…lPj…'#"'&#"'&'&'&47>7632327>76&'&'&/&'&'&47>762!2!%327>764'&'.#"&#"327>764'&'&ÍÖs®* 0$+$$$ 1#*# ZaZ%% NT12 4 #HH  ")mR‹èOeýbÕü  , 0  +  …Í ) . $J . %'.D"&B 1 $C mR )KÏy   µ !   V!Eµdz267>54&'."#"'%"'&'.5467676;27>4.'&+"'&'.54676762%632$"267>54&'&ý.&&.&m,mQÛþ‘j–P(!N!"(! aVf&&bZ55!("!N!(P–joÛQm,úæ.&&.&q    lü?W’,>&#< A#"< " (( " <"#A <#&>,’W?~    ÀlOOj…3!#!"'.'&47676?6767>'.'&#"#"'.'&47>763276;%32676764'.'&#"676764'.'&#"32ÍÖÕžeOèþuRd2!  HH# 7   ZTN +Za21#+$0 4$$$+$0 '±sý¹  *   * OÏK) Rd#!>& 3"9*$"D. ' - D! 2 . , T% #ý: & (  Iô˜èZx–´Òþ-4H67&'&'&+"'&'&'&476767632%632 #"'%#"'&'&'&54767676;276276767654'&'&'&"276767654'&'&'&""'&'&'&547676762"'&'&'&547676762'&'&'&547654'&'&'&";276-&#"+"276767654'&5476%327%&"'&'&476762I  Q\C--%("(/*0.,+"( /ŽX]â\ˆ9þþ<…\âþ£XŽ/"$)0*3')"* %1*0CR[þë        22 2 2 2 †%•'   &J  &%C\d#½í_*]OÙþ‘”hXC%&  J&   “ÙO]*ýÝÙ    Ù   ")&`&"'$"/' <%‹ZS  ÏÏ % SZ‹%< /'* "%5"-($# þÒ;ý‚8\= !  !  " þòÂ÷/V’C "  !  !  [uV/á+    Vÿã^ðau‡ 767>54&'&'&#"&54767632 '.5467&54732#"#"676767#"'&#"'67654 ozwbda_f_zxІ|wbdaM,krnulŒœšŽ†spsnun‹þÇŠ*D$ lµ©Q$" 6*D?"5'K(2- # ž>   :72 331cdíˆèi`4331cd퇼>˜mœwn<;;8roþ졘þðwp:;;BV0/MÍï8:D@*|sa  -F(7 "*=8&0!2  1-5$& 6:B4Vÿã^ð (B\w.'%&'&"632%6767>54$2"'&'.546767" 767>54&'&'&'2 '&'&547676©?'*&$ 1ó$-+h+-$F3782ˆ**?þ’1 $&þç>>9†|wbdabc`òzwbda_f_zxŠšŽ†spsnun‹þËŽ†spsnulŒwI_ý"2[$  £""ƒ þ ýgIƒ $[2!v 55 55 31cdí…ñcaf31cdíˆèi`43d;8roþ졘þðwp:;;8roœwn<;x,Aª-57'36%33#3#!2#!3#3##$'#7”$@d’’5«{s”Vdš]˜F0 0Fþø˜]šdV”s{«5þâ’’d@×(ÚÉ j–P‚PÆ,ÆP‚P–` ÉÚ„Ü0ù ")- !676762!"'&'&'&54!ö¸ûX¸$#R#+/®þRþŸþF¨þF$#R#$1þS­ýò¬h,þÔ  ªþÞþÝkþÓ-ª"ýs!ýßûã³}ïPÐãó476?6763&'&'&547632676767654'&547632!54'&'&54'&&#"'&/&'&'&#"#"'&'&/&'&#"&'&'&?6'&'#"'&'&#"!'476='654'&545454'327654'&'&327654'&/³%4-)"$0JK&  )7   ýß %0‡'# #6 +-L _ˆ_^/s4* 1( .266 |/(1   \«   ýþ#:7  l€S&   xë71]/~[#<$  o»_%@,†: $";vRþÔ $X$+|‰‰¶!5DX&PY;¶9Do6 b'n•2  83þeF] 4T&  &  /50$?- 1@& 3l K  C"P1Ö Ÿ·:03<D:5XI.)D&þ[+-1:   q/A8   g+©jl9Lp{‰7654'"'&#"+"'&54?67676763276323273#5%6767'&#"6"/67#"27632327654'73654'676547&Øp/l0&–J!cS%YþøE]{@C"$4>™-;% ¾Ì,(6áY>ømü¼!N$Xì6"/,(«4òsS?X$U>"sJ?K(`./4‰+2K2.ž0>ü°S ¢Zp0+þ 1^ð' þÇ;cs  /è^"|Y/ 4«28Û‡ƒ´Ï•l%é%oté5oA='Y$ a…£T* ''G+- %_kj~r}jL`І|ï\gÃK@/.85–c($¶šË¯ (2LS>54/##326?%%3254'654'3>7632#"&547>32'% ;66Ií ü€™ ’™ ƒ }¸ýgý„½ ?ý¤6ýûqýn   -> 9@ îH67; ù˜’ ˜’zýƒýh¸| ýó8 >ü¶6!qýúê    ôýÂB5–Ž>%+?F4&'&/76765'7! !'!654'!4'!!$467>2"&'&!654'£ Ò33Ò û¦^^^¯¯RXý¨ü®¯I´#ýJ2ýVªülÅPú#Þ ~!88!~ KØppØphÈþ¬þ¬È,þÄp <(##(#id¶šË¯ (2LS.#"227654&'''%'654+.#"65.'&54632#"'.6#"%  íI66; üo’ ™’ ¨Æ|™¸ýƒ>?%6!ýûqý   9 ý ;76Hî ý ’˜ ’ÕÆ |¸ýhýƒì> 8ˆ6ýúqým    BýÂX{[%G'23 %%.'&"27>7%$!"#232%"'&'.4676762%#"#Š2%ký¿      Aý•ýéþÛ™±·þœ>>dþIýñ±ID`nþíS   SþínGŠYn¹ 5>5 ¹nŠ­)(%$#"#64'232%%&'&'&"27676è&ý•ýìø22økýÚ**Å!þín`à`nþí!##3ÆW 2327632#"'&'&5476Å'( ï>‡ !þ~þ¶GH ".4F+Ž@xÂH )þ0ýü$Š™'*'í²Å 23277632#"'&'&54763Å'( ïe` ý}{*˜279HF`0@x¯JýÞL ²†1 ,”±A  ' 7 çÉŽþ8þ7ŽÉþ7ŽÉÉŽêþ7Éþ7ÉÉþ8ɯ?– ‘tþÉþŒþŒþÉtþŒ7tt7êþŒþÉtþŒ7tt7þŒuþÉñÿîœÜB2632#"'&'#"'&54767'&54763267632676ø ÐÄQ   x€¬Þ L$3 Ìäz(   6XŽÌÜ3âþñÆï  šÐàþŠ6*=P*> "#  ¨˜¸ÌüêR26#"'#"'&'+"'&'#"'&547&'&54767&&5476326763276T ñë””% $$YyæX$ ‚Äzc0 + j–çù ê:  (ÛþÇÌ¢1#: _¿þû‡$ #- °Ô½ =1 '2ĺÚ pDÕ #!!!!!%!!!!!!!!#!5!36þHþVª¸ªþ¦¾þBý¨þB¾XȾþBÈþB¾ÈÆ«þUþHþVª¸Pý¨þB¾X¿ýyÈþB¾È¿pDÕ !!!!!!""ýÞþpýÞ"²þpýÞ"#pDÕ35#7!!#!5!3ãîîïrýŽðýŽrðsîðýŽrðspDÕ!!%!!!!!!“Žþr"ýÞþpýÞ"#ŽþpýÞ"#RbÕ !!#!5!3ÒþpðþpðEðü«Uð lÕ3!!'#'!!#!!3!5@––þÔþŽ–––,r–ÈÜþÔ,Ü,©–þŽý_– –r,–ú,þÔÜý_¡Ü>vÕ #!!!!!'!!!!!!!!#!5!3þ˜þèhÈþèýøþè þèÈþèÈmþèþ„ý_¡|Pýäý_¡þHÜý_¡ÜpDÕK#";54&'&'&#'!326767657'&'&'.+3!76767>5{d³IB,$2$*DE³h{LGC_RQ|66R_CIJ{h³ED*$2$,BF¶d{LGC_RQû„66R_CIJ KIB`OT|87O\FGKzd¶GB+%2%+BI´dzKGF\OTû„87O`BHL{d¶GB+%2%+BI´d»ÿåùó  #!! !!! 373#'7#Zà¿ààþAààþAàà¿:¦¦L¦¦¦lØ­mllmÙþzØlþçmllmÙóþ|þ}þ}þ|„ƒƒdþáþà »d»»þ}¼c¼¼¼„»Tÿã`ðC54'&54762327632#"'&+"'&5476=#"#"'&476323(L,68x86,L ºzFvd0000dvFzº L,68x86,L ºzFvd0000dvFzº ºzFvd0000dvFzº L,68x86,L »yFvd0110dvFy» L,68x86,LVÿç^ðƒ&'##"&'&'&4767>32367675&'&'.5467676236767>32#"&'&'&'#"'&'.546767675& Ô  R.-R  R-.R " Ô *!""! ((\(( !""!#%  Ô " R.-R  R-.R  Ô  %#!""! ((\(( !""!*“*!""! ((\(( !""!#%  Õ  R.-R  R-.R " Õ  %#!""! ((\(( !""!* Ô " R.-R  R-.R  Ô Sÿãaðƒ4&'&'&'.546767622676767>32#"&'&'&'.#"'&'.54676767>5"#"&'&'&4767>32(,$ ((*& :.r06$&**& )'De!  'd8:b&$$&b:8d'  )a@/!  ')*&$6/r/6$&*)'  ')?c'  &d8:b&!$&b:=_& (bCc"  &d8:b& $&b:=_& (a?/!  ')*&$6/r/6$&*)'  ')De!  'd8:b&$$&b:8d'  )a@)' ((*& :.r06$&**& ((Tÿå`ò0267632#"'&'&'!&'&'&54676763267632#"'&'#"'&'&'&5476767!6767632#"'&'"'&'&'&54767#"'&'&'&5476767632!#"'&'&'&54767#"'&'&'&476767632&'&5476767632!#"'.'&5476767632&'&54767676Z   ( &        þÛ    <   4 þÛ         % (   ò   (  þÛ 2     6  þÚ         %    <    %  (   Wÿã_õ2767653"4'&'&W›†spsnulŒœœŒlunsps†ì;8ro¡þðwn<;;¬¢þ²þj¢¬þ>-¬¢›œý'†þO^†__†^þOqþ¸ýï4óþÙ4H4þ"hþdÿþdÿÿœÿ„0Õ!% %!-º@– þj– þjþÀþj •þk Õþ*ëþëêëþëëþ*Öëëê˜Õ37'73 #'ãxxîxxþ™þ.xÒðÒxþ.Òxþ.ðþ.x¹ÎÐÐÎÎ ÐþòýåÐþóþòÐ ýæþóÐpDÕ #'!5!73!Ô¹GþFdþFGºýrþEGºdºGþEr¸þFG»ýŽqþFGºd»GþFqýºGþEd@ÿãLð     - FOÔþFýºcþ,OOþ,cýºFþÔðý¹dþ,POþ,dý¹GþœÔOPÔþœ Tÿã`ð '%%%%%% % -õþÝwDþÛþáš{þáwwþe#wý¼%þf{wwŸ„¤‡þy¤þ|ŸŸþ|¤þy‡¤„…{þàxxþe#wý¼%þf{wwœþÝxEþÛþàþy¤þ| Ÿþ|¤þy‡¤„Ÿ „¤»ÿãùð % %  Zp/þA¿ýÑppýÑ¿þA/ðý¼Áþ}þ}Áý»EÁƒƒÁTÿã`ð     - Zq²þïôþ þNqqþNþ ôþï²ðþ þNrqþNþ õþî²qr²þîTÿã`ð% % -Zªy‰lþ”‰þ‡ªªþ‡‰þ”l‰yðþ”‰þ‡«ªþ‡‰þ”l‰yª«y‰Tÿã`ð%% %% -ZtGcïVþªïþGþñttþñGþïþªVïcGðþ©ïþGþñstþñGþïþ©WïcGtsGcïpDÕ/3%!!%#'''%!5!%777xªoª:UþÅœ.þc¾þF™.þd;UþÇ©oªxªoªþÆU:þe.šþE¾þc.™þÈU9©o¾þE™.þf:UþÅ«o«xªo¬þÄU9þg.˜þF¼þf.›þÅU:ªoªx«o©9UþÇ™.§ÿã ð›54'&5476276767632+"#"32;2#"'&'&/"'&5476=&'&'#"'&'&547676;232?&547'&#"+"'&'&54767632676'K,68x86,L qA'C<4GW>L ƒd  f‚ L>WG4L „d  dƒ L>WG454&'&/54'&5476276767632+"#"32;2#"'&'&/"'&5476=&'&'#"'&'&547676;232?&547'&#"+"'&'&54767632676o**YK,68x86,L qA'C<4GW>L ƒd  f‚ L>WG4L „d  dƒ L>WG42#'"372"'&'&/"'&476="'&547>µŸQ!//V–Z¢ *nN™+G80j@6R¨R6@j0/P1“N± ¢TžP#00V–Z¢ ,lO@W+G80j@6R¬N6@j03L/•N² ¡Ä ]H,`,H Yc¢!77™\Ê4OO4ÐVA7§gU3š',H^ ]H,`,L&3c¢!77™\Ê4OO7ÍVA7¥fV4š&,H^¡ÿãð™±É67654'&"327632#"'&'&/#"'&5476=#"'&'&5476763232?'&#"#"'&'&5476763254'&5476276767632#"'&#"#"'&#"327676%32767654'&'&#"#"Z8%1T1%8¨5e %ZF\ +m8BS/?JV@6RTXN6@VGB1QB8n* \FZ% e53e!&ZFZ *n8BS/?JV@6R¨R6@VGB1QB8m+ \FZ&!e3þ˜DA 5<; > +F$ÜH$F+ > ;<5 AÓcJ2QD++DQ2Jþ´ (5H,'9,J&0f‰) T|\`j4OO7g`\|T '‹g/& H,9',I4( (3J,&9-H &0f‰) T|\`j4OO4j`\|T '‹g/&J,9',H5(€""'!$(:UJJU:($!'""nF­w"2767>54&'&'767632"'"'&'.'"'&'.546767"'&'.546767632.546767632¢=>343343>==>343343>x>%85670-),(-%8/[0!-(,)-02y/8%0%)-02y/8%-(.'&$W/:#-(,)-02;>/;),)-02;>/8%-( 06{IF{6006{FI{605+'g>:c.&".c;=g'+&1N%&W'+&.c:>k#"$.c:>g'+,B:>g'+&.c;=?nF­\v¦½Ö%"'&'.546767"'&'.546767632.5467676267632"'"'&'.27654&'&'&"67&'&'&'276767&5467'&'&#"32767>54&/76767>54&'&'&#"Z0%8/y20-),(-!0[/8%-(,)0-<1:3%>(-%8/|/8%-(>%85670-),(-%8/[0!-(,)-02y/8%0M=  H C# B/g H Ö/*x#$  8## H g/B õPP  $#x*/ø%N1&+'g=;c."&.c:>g'.5 ?=;c.&&.c;=? 5+'g>:c.&".c;=g'+&1N8GG$> >$ ‚ýc.,bB$#>  ºIr0C >'#> þÂLM >#$Bb,.$ >#'> C0rI Tÿã`ð)T½æ:e©Ó&'#"&'&'&4767>3267'&#"327%32676764'&'.#"7632#"#.4767676324676762>322##"&'"'&'.5#"'.'&467"&'&'&4767>&'&'.'&'>76?&'326767767>5&'&'.#"767>7.'&/32>7674&'&'67'&'.#"67'&'.'67676767"2767>54&'&'"'&'.54?&'2767>54'7654&'&'&"67'&54676762:ä    äÎ$4 4$ww4 4 åxy   ûÊ%" !()-+U$"! ((\(( !"&S+-)(! '7M"# V2% A()-.R$"! ((\(( !"(O-,*(A"#2P"# "MÁŽ   ´ ¸! *4 2ë kk  4 2 uKK   ¦ ´    Ži2 4* !ý‰== 2 4  Í `_  wR#$$#R#$$  8 < c !<>  š   8 < d!!<>   "%UV*) !!$3R  R3&!-(-%Z& "#%(.2$( &&S+,))A!$3R  R3'A))XT$""#%(`$( " þ ´     Ži3+!x== 3 Í _`   þŽ    ´ ¹!+3ë kk 3 uKJ   ßF)(GG()Fþ$ä    äÍ%3 3%ww3 3 åxy   Vÿã^ð3N^"2767>54&'&/2"'&'.4676762 '&'&547676% %-—z35++++++53z35++++++5pWDM69?=;9JHªDM69?=;9JHSšŽ†spsnun‹þËŽ†spsnulŒœþ£þ}ƒ££ƒþ}£þ(.h<;h.((.h; +F$þ$þ‚> +F$Hþ‚ ;<5 AØ~ ;<5 AÓ¹+DQ2Jþ´ (5H,'9,J&0f‰) T|\`j4OO7g`\|T '‹g/& H,9',I4( (3J,&9-H &0f‰) T|\`j4OO4j`\|T '‹g/&J,9',H5(õþG+DQ2JÁÝ$(:UþÍÝ$(:U3Ý!'""ûÝ!'""¶þÕA''7'753'75377537'7'#5''#5'7#5'7'7âðÂ<†Ä<ÄxðÂ<†x†<ÂðxÄ<Ć<ÂððÂ<†Ä<ÄxðÂ<†x†<ÂðxÄ<Ć<ÂëŠphMrhr›à‹ohMããMhoþëŠà›rhrMhpŠ‹phMrhr›à‹þêohMããMho‹à›rhrMhpœÕ!%)-153%% %%#'-75%77%7?5x# <þö$þÝ <þ÷þÝxþÝþ÷< þÝ$þö< #««#««ý÷¬««šª««ýö««x«ÕþÌ©þ°šhš¨¨™hšþ¯©þÍ3©Qšh™¨¨šhšP©'ÅcÅccÅchcbbccbbgcÆcÆÆcÆ‚0ÕA37%37' 7'#%'#5'7#5'7'7-'7'753'7xŒdð ÈX<[Œdðþö ðdŒY<\ÈþùðdŒxŒdðþ÷È[ÿB-ÏDH2#"2767>5!"&54$3!57!#"'&'.5467676#Ý_>I-74þÍÞý×3TŒÈP>CPN±DG-2.1/&D9¨¾Ð 88 '.* !-8D_èÈ2ú{ûÛj·@F'%.3r@Md7+4VýªžÿÝð/2&'&54676762"'&'.546767Z y*,&'ˆ²‚†®ˆ'&%1]~|45,-++-,54|45,-++-,5ð(+&a4|dƒÎ΃fz4a&$(üF*.j=3"&'&'&54767>32Ôîòr²JŠ6464ˆNNˆ4646ŠJ´pòêŠþ`Â684F@NLBD64:866D@NLBD668Âþ^~*Ëž i654'&#"632327632!"'&5!267&'&#"#"'&54763247632327654'&547632#"— 6+Jo.^V|;-þË™it36?þÅ̺ÆfQMeæEJS?(*$ s]vh2K)*NL13^Úv:Mc*„Zœ™£þe—C0ª3N35%&-Kt\K%9S >BWN=!$?$8(F!5{^? Z„ Q67654 547&'&+327#"'#536767&'&'&5432&5476323254'&5432ß?-BO>=þþ‹v06&%K`dC+(‘k$'eM?$#=Hb B=)+8=.mØ9eb PB÷°>$3g:84þ!þEB7WPfG+1K«HŽP<Ff#&T'0P+A'þ“š<þú›œ¸±œ›ƒ}DC‘þä/'"05276767654'&'4‘rceNS((((¦`hmº©§„Œ@DDF†Š¡©þä/CD}ƒ›œ±¸œ›>C/GF†©½»¤§…‰CGþñ˜ !&547˜†‚ƒ…þ°–•”—æþ>þ2þ;åëÆàßÄìþñ˜ 0!!6P—”•–þ°…ƒ‚ìþ<ßàþ:ëåÅιþÛ! !ÛþÜþþ$þþüÛþü ! Ûÿþ"þþþüü#þ{! !{þ<ýl”Äý`þü9þ‘ ! 9 ý`Ä”ýlþüüUþ?! !?þþþìþþüwþ` ! wÿþêþþþüüÄþHZîjþæjî•îjÍjîlþð'7'7Zîjþæjî¼îjþæú3þæjîfþ²$#"&=4&+5326=46;#"3Ôù©lŽ==k©ùÔDV[noZV¾”Ýï—ts•ðÝ“XøŽŽœøX¡þ²N$326=467.=4&+532;#"+¡DVZon[VDÔù©k==Žl©ùÔ¾XøœŽŽøX“Ýð•st—ïÝ”˜ÿì”è º+¸º +¸ 01! 4$32!5##7#”þ?þÂþÄþ?Îq¾ÁqÍûÓiÚ¡þÿìÙêþÁþA¿?ÆrÆÆþý\Ž/˜+ý‚˜ÿì”è *º%+¸%º +¸ 01! 4$32>54&#">32!5!>”þ?þÂþÄþ?Îq¾ÁqÍýæ>0Å£=‘as„;N_/>!þúR¤þL}²êþÁþA¿?ÆrÆÆþéFi:}—$«:&N?(U?"ñMŽt§˜ÿì”è 6™º+Aêú]A)9IYiy‰™©¹ÉÙ ]¸1ܺ.+¸.º +¸ º!'+¸!º+¸º9º*'!901! 4$32%4&#">32+32#"&'32654&'26”þ?þÂþÄþ?Îq¾ÁqÍþ|Á¡=ˆ_ky4[\XZ‘ŒcksuD}[Xˆ@ÃØv hêþÁþA¿?ÆrÆÆþs‰— ?<:<ŒNGLO0¨œ]r˜ÿì”è º+¸º +¸ 01! 4$32! !3535##”þ?þÂþÄþ?Îq¾ÁqÍýþó þC½¶ÎêþÁþA¿?ÆrÆÆþþ¼~þƒŽÓÓH˜ÿì”è +‹º!+Aê!ú!]A!)!9!I!Y!i!y!‰!™!©!¹!É!Ù! ]º+¸º ++¸ º+¸º($+¸(º901! 4$32>32#"&'32654&#"75!5!”þ?þÂþÄþ?Îq¾ÁqÍûïYe2hvvhDw_X…@¾Õϰ?‘ýÃêþÁþA¿?ÆrÆÆþó%aVUa/¬¬˜•° «Ž˜ÿì”è 2ñ¸3/¸4/¸3¸и/¸4¸ܸÜAêú]A)9IYiy‰™©¹ÉÙ ]¸¸ÜA&6FVfv†–¦¶ÆÖ ]Aåõ]º +¸ º +¸ º+¸º,&+¸,º/&,901! 4$32#"&54632"32654&#"7>325.”þ?þÂþÄþ?Îq¾ÁqÍý¢\NN\\NN\qÈêÀ¹ Âº N€ w/aTJjêþÁþA¿?ÆrÆÆþþZbbZ[bb*þõÚçý³‘”±= P”#œ˜ÿì”è º+¸º +¸ 01! 4$32%!35!”þ?þÂþÄþ?Îq¾ÁqÍûÂÏþ¯ÁlýUêþÁþA¿?ÆrÆÆþvüæ]K˜ÿì”è 1=‹º+¸º+¸º +¸Aêú]A)9IYiy‰™©¹ÉÙ ]A&6FVfv†–¦¶ÆÖ ]Aåõ]Aê ú ]A ) 9 I Y i y ‰ ™ © ¹ É Ù ]º/9º;9¸;/Aê;ú;]A;);9;I;Y;i;y;‰;™;©;¹;É;Ù; ]¸5ܺ+¸º )+¸ º+¸º28+¸201! 4$32#"&5463232654&'>54&#"2#"&546”þ?þÂþÄþ?Îq¾ÁqÍý¹_TT__TT_ýâ¾­­¾vi¶·jvkKRRKMQQêþÁþA¿?ÆrÆÆþþlHQPIIPPIŠ——Š\ƒvStˆˆtSvƒÄB>=BB=>B˜ÿì”è &2é¸3/¸4/¸Ü¸ÜAêú]A)9IYiy‰™©¹ÉÙ ]¸3¸'и'/¸-ÜA-&-6-F-V-f-v-†-–-¦-¶-Æ-Ö- ]Aå-õ-]º+¸º +¸ º +¸º*0+¸*º# 901! 4$32254&#"326#"&'4632#"&”þ?þÂþÄþ?Îq¾ÁqÍüÑÈ鿹 ÂºŸO€ w.aUJk<\NN[[NN\êþÁþA¿?ÆrÆÆþýK Úèý´‘”¯< O“$œ„[bb[[bb˜ÿì”è $0Óº%+¸%º+¸º++¸Aêú]A)9IYiy‰™©¹ÉÙ ]Aê+ú+]A+)+9+I+Y+i+y+‰+™+©+¹+É+Ù+ ]º+¸º .+¸ ¸¸(Ü01! 4$32!5##7##"&5463232654&#"”þ?þÂþÄþ?Îq¾ÁqÍû$Ÿu¼¬ž‰F?@EE@?Fþpˆƒ‚ˆˆ‚ƒˆêþÁþA¿?ÆrÆÆþý©€Ê*‰'ýÂ$¢——¢£——£ÐääÐÑää ÿ–«=$>  767654'&'!5%3!!  '&'&54767ôþ̆„mommom„†4†„mommom„ý¥þá¡ýP\˜–|~{{~|–˜þ¤˜–|~{{~|–Ù96ooþ÷œ—þ÷oo6996oo —œ oo6û}9“:û݈¨@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«=6P  767654'&'!!567>54&#"5>32  '&'&54767ôþ̆„mommom„†4†„mommom„þ)4ý \=)N=…kP¥`aªFºÝ7I׺\˜–|~{{~|–˜þ¤˜–|~{{~|–Ù96ooþ÷œ—þ÷oo6996oo —œ oo6û€ˆˆ_A.Xx;_x55£'(º›I„ZÞV@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«=B\  767654'&'#"&'532654&+532654&#"5>32  '&'&54767ôþ̆„mommom„†4†„mommom„ttƒóàLŸUD W˜Ÿ”„‹‘x~‚zB˜\R«GºÕrþ=\˜–|~{{~|–˜þ¤˜–|~{{~|–Ù96ooþ÷œ—þ÷oo6996oo —œ oo6ýyœt°Âœ'(xrjw…_Z\b§Ždˆ @>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«='A  767654'&'!33##!5  '&'&54767ôþ̆„mommom„†4†„mommom„Þþh˜*˪ª¡ýå+\˜–|~{{~|–˜þ¤˜–|~{{~|–Ù96ooþ÷œ—þ÷oo6996oo —œ oo6íý‚ üõ†þæœò@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«=7Q  767654'&'!!>32#"&'532654&#"  '&'&54767ôþ̆„mommom„†4†„mommom„ý£zþ#G#ÈéðÚKœSL•VŠ¢¢ŠAC\˜–|~{{~|–˜þ¤˜–|~{{~|–Ù96ooþ÷œ—þ÷oo6996oo —œ oo6cˆþÛ Û»ÁÖ£)%’}|’X@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«=%>X  767654'&'"32654&.#">32#"32  '&'&54767ôþ̆„mommom„†4†„mommom„þÖl€€lmÔ=|< © /ŽV´ÑÚµÏÛ â=|þ^\˜–|~{{~|–˜þ¤˜–|~{{~|–Ù96ooþ÷œ—þ÷oo6996oo —œ oo6ý‹•‚€––€‚•ú“ØÚEKÛ¼¸Þ>-O@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«= :  767654'&'!#!  '&'&54767ôþ̆„mommom„†4†„mommom„ý\þN¨˜ýÂÖ\˜–|~{{~|–˜þ¤˜–|~{{~|–Ù96ooþ÷œ—þ÷oo6996oo —œ oo6`Eûš#…@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«=#9E_  767654'&'"2654&%.546  &54632654&#"  '&'&54767ôþ̆„mommom„†4†„mommom„þßs„„æ…„þêhtÌdÌthuƒÔþtÔ„9tihvvhit0\˜–|~{{~|–˜þ¤˜–|~{{~|–Ù96ooþ÷œ—þ÷oo6996oo —œ oo6ý,{Ø{|kl{EŽg¦¦gŽžs­ºº­sž\hh\]hhÐ@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«=2>X  767654'&'53267#"&54632#"&2654&#"  '&'&54767ôþ̆„mommom„†4†„mommom„ý§=|< © .VµÏÚ´ÐÚþôâ=}þmml€€J\˜–|~{{~|–˜þ¤˜–|~{{~|–Ù96ooþ÷œ—þ÷oo6996oo —œ oo6û“×ÛDJÙ¼¸ÞþÃþÑþæþ¯•‚••‚••@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«=+8Ca  76767654'&'&'"32654'.  735733!  '&'&'&5476767ôþ̆„mo5885om„†4†„mo5885om„T,+VUVV++ï2QPPQþΠPþ3–•ƒþp\˜–|~-,¡üg%“&û݈¨@>}~•—±¬—•~}>@@>}~•—¬±—•~}> ÿ–«= $!5!#%  '&'&54767°þø¡þâ{\˜–|~{{~|–˜þ¤˜–|~{{~|–ˆˆ#:“9üq @>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«=6>7>54&#">32!5  '&'&54767£Ã×I7ݺFªa`©Lk…=N)þÃ\öýÕ\˜–|~{{~|–˜þ¤˜–|~{{~|– ÇÞZ„I›º('£55x_;xX.þ¿_ˆˆ@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«=(B>54&#">32+32#"&'32654&  '&'&54767ir׸G¡\\˜Bz‚~x‘‹„”Ÿ˜W DUŸLàóƒþ2\˜–|~{{~|–˜þ¤˜–|~{{~|–ˆdާb\Z_…wjrx('œ°tœ=@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«= '! !335#$  '&'&54767œþhnþ¡ªªþo\˜–|~{{~|–˜þ¤˜–|~{{~|–³ý‚ ý œþæ† ý@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«=7>32#"&'32654&#"!5  '&'&54767CAŠ¢¢ŠV™HSœKÚðéÈ#G#æþ\˜–|~{{~|–˜þ¤˜–|~{{~|–=ý¨’|}’''£ÖÁ»Û %ˆ@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«= $>2#"&546.#"32654&#">32  '&'&54767Pmml€€­C|=âþóÛϵÚÑ´VŽ/ © <|=þ\˜–|~{{~|–˜þ¤˜–|~{{~|–+•‚€––€‚•úþ¯þåþÓþÂÞ¸¼ÛKEÚØ«@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«= !35$  '&'&54767Ö>þh¨²ýÖ\˜–|~{{~|–˜þ¤˜–|~{{~|–@ˆûÝfEý@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«= +E2"&46' 654&'>54& 74632#"&  '&'&54767Yt„…æ„„/t„ԌԃuhtÌþœÌt-tihvvhit0\˜–|~{{~|–˜þ¤˜–|~{{~|–Ì{lk|{Ø{Ežs­ºº­sžŽg¦¦gŽå]hh]\hh‰@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«=$>%32#"3267#"&'"&54632  '&'&54767!C}=â ÚдÚѳV. © <|=Al€€lmþÝ\˜–|~{{~|–˜þ¤˜–|~{{~|–®Q/=Þ¸¼ÙJDÛ×g•‚••‚••@>}~þÔ±¬þÔ~}>@@>}~,¬±,~}> ÿ–«=  :2#"&546$  !5##7  '&'&54767™eddedddþ¾©©Bªû‘¡³´–\˜–|~{{~|–˜þ¤˜–|~{{~|–Õõ÷õööõ÷õ€þÃþÑþÓþÂ>-/ýˆˆ#&“%üq @>}~þÔ±¬þÔ~}>@@>}~,¬±,~}>ušPj !!5!!PþþÀpûÝ#þ@‚þpðpüpté 7%÷ý©µÔFýN‹´ªý4ýNGuÌP85 zûûDýæ<2ð2pþJþJüt” '-ZýK©ûþ˲Fýª´ÔGýNuPü!!u»¨\lý”þ¤¨ýE¾>ü~~ü>uu+"&'.546?!".4>3!'.5467>2þp4,ºü€,$$,€º,4›2.þp ,.º".2."º., þpuÌP8!5! šûÛ%¶þJZPŽþJþJuÌP8!5! šûÛ%¶þJÜHþJþJuÌP8 #3#3#3!!5 ¹ððþÀxxÈ<<éþo‘¶þJºþpþpþpîþJþJuÌP8 55!#3#3#3š¸þþ¸þoPððþÀxxÈ<<®Šþ΄„þΊXý¨Xý¨Xý¨XuÌP8!!5 šûÛ%¶þJºîþJþJäÀPD! 6ü¼–þ>lú”‚þñÂþ>þ>äÀPD ! þòDýRþ>lú”‚þñÂþ>þ>äÿøP  Bþ¢lú”‚ŠývývuÌPb3!5 5! '&'.u$##+* Z¶þJþMþM*+##$‚à0U%!ÆþJþJÆ!%Uu¢P84676763!5 5! u$##+* Z¶þJþMþM*+##$‚0U%!ÆþJþJÆ!%U0ìÿñÙ!! ^þŽr{þ…VXeýoýouÌP855!¼þDüqšžþ΄„þΞ0ušj%5!!53  !<ý9Çúèþª˜þhý9šîôîþþ>îþ¬ušj%5!!53  !<ý9Çúèþ˜þhý9šîôîþþ>îþ¬+Z° !73#57!!+ IŠdØý&–ý+ÑŠ„¬Šü·ÄÎôîýþÈþ2È&îþ¬+Z° 5!'53#'!!!+Õ–ÚØdŠü·±ü·ÄIЬý|úÈ&Èþ2Èýþî¤þ¬î˜˜2˜ !'!'!53 !ÜOýìdcnèdþ‚˜þhýžÈ”žþÈþ¶ž˜˜žþ 2˜ 3#5!7!!! Ünèdþnýd;ýc˜þh˜þÈþž”Èþèþ ž˜˜dd˜ !53#'5!'! !–]nè2þn2üÕ2òr-˜þhüÓ°Jžþdþc;dJúž˜˜ždd˜ 7!573#5!! !–2+2nè2þnü£òr-˜þhüÓLJd;cþdþžJúž˜˜ž<ÿØÄ¯6767632"'&'&'! <'CZmo~yti^Z\X^Vqo÷ti^?)XÊþ6nGCZ.//+]YÝ€zÚ_X0//+]>IžÊÊž ‚BP "&*.37#37#37#37#5!!!!3'#3'#3'#3'#á < ´ < ´ < ´ < x»»‡­Ãíþy»ýä_< <(< <(< <(< <ZððððððððPþ輂‚¼þèhððððððð¥pt> 7&#"7'7 !%¢¯*ޤBF¯þ8þ©þåþWU¨{°FC£*°þ9oþX¨:þWþæubP¢ 55!5!!'!þš¸¸šþ˜ý§XÈý¨ÈÈXȪø¼dd¼øPRÈÈȧÿÅt” '327'' !!i¯FB¤Ž*¯ÿ8þ‘¨þX:þå¨Á°*¢CF°ÿ9þ¨þåþXU¨§pt>*%&#">7'&'&">327&5467>7t¥BEH€¦¦#&NKþX$W/,0$"¨ D5Hp*G6¦¦¨$"!0,0Y"þW!F&'&#þ»GGC£uaP£'467#"!4676?'&'.5!3!.5P·›5ëê#$ý¨%"//"%X$# Ë5›‚e¼eJ(0Y! "X0(Je¼§ÿÆt”*.'.54?'#"&'2767.'32t)H5§§ þX"$ #0,0X"¨KN&#E€HEB¥”Ž£CGG¨¨&'&KþW"Y0,0$"¨E6Gs¬PX'<6%"'&'.54676$4676762"'&'&&'.54676762$/ö+ãz >ü_ $#R#afü‚þ·#R#)ž>xåþ”ÎbQuþý 88—RK…68ƒ# 88  þþvc–<å*676767632#"'&'&'&%.5467.546–A ''+/54<3¢‘þo¢8n23'9%%%%büüb%%%‚&:?$ fLLf#&#/:&'X23X'ñrrñ'X32X¢V2®c"'&'.54?654&'&'&#!"#!".4?64/&4676763!23!2767>54/&546767622ð þæZýœ ;:td Z ð‹ð  c ´  uuè ´ c  ð¢ñ2c"'&'.54?654&'&'&+"#!".4?64'&4676763!2;2767>54/&546767622þpW\xýj IJœ– ‚\W‹þ˜ Ûð  ““8 þüÛ þ˜u¦P^'#76767&'&/3#>7!5!!5!.'PˆSŒJl.. &GG& GlHŽSýÌi7*üÖånK Knü**7‚OU©nm¤'66'¤1ª¤”UýÕ=…HdŽ·)·ŽdH…=þ#îu ! !ùõþ þ óþ˜Òþ–jý.uûÛûÓ-1ýüްþò0 3%!#3!ÈþÔ€ððý€°ù¦Zddù¦d¯þò/ #3!53#5ËÈÈdý€ðð°ù¦¾øÞdZd·þò{3 #·ªþ檂üpüp¤þòh # 3hþæªþ檂üpÿÿ·þòÏ&ÀÀTÿÿ¤þò¼&ÁÁT[[Ü '#'#'##'‰Òx\xÒxjjxÒx\xÒÜý,x\ýeÓûȰhhûP8Óü›ì\xÔYÿùYÕ73373737+ý.x\xÒxjjxÒx\xý.Ôx\ìü›Ó8ûPhh°ûÈÓeý\xý,Ol„D=072767>54'&'&'&"7#7676767632#"'&ew@RN¾JV !'7$"!3!&'&'&'!#!2767676Þþwxéþ  !1cbîþìîbc1! ½½ "1cbîîbc1" `éxüâÖ]\LM& ®ª® &ML\;þRªþR &ML\]Ö]\LM&¯ZþwxéZQvcbddbcvQZª[RwcbddbcwR[éxV''LM\7=eþ›=7\ML'ýÆþ›e;6\ML''''LM\6dÌ 8 ´  ÔÔÌ2@ @@ 00 ]1@  œ Ôì99ÌÌ0@  œ ü<<@ œü<<°KSX³ <<´œ ì´œìY5!!d‰xé ‡öyéxUZ‰xéªéxuÌ 8 ´  ÔÔÌ2@ OO __ ]1@ œ Ôì99ÌÌ0@  œ ü<<@ œü<<°KSX³ <<´œ ì´œìY'7!5!'7 þwxéöy ‡éx¯ZþwxéªéxdÌ 8Ú¶ ÔÌ2ÔÌ2@ PP_ _O O]1@   œ Ôì9Ì2Ì20@   œü<<@ œü<<@ œ ü<<@ œü<<°KSX· <<<<´œì´œì´ œ ì´œìY5!'7'7!d‰xé€éx‰þwxé÷€éxUZ‰xééxþwZþwxééxdÌ 8!!5!! s÷]xþw‰x]ûöix]x‰Z‰x]xiuÌ 87'!5!'7'7!5 èiiöû]x‰þwx]÷iix]xþwZþwx]xdÌ 8!7'!!5!'7'Xii÷¨iIø˜]xþw‰x]h]x‰þwxiiiá]x‰Z‰x]]xþwZþwxdÌ 8 !5!3# Y÷#éxþw‰xéݪª-éx‰Z‰xéYü¤uÌ 8 #3!'7'7ªªÝéx‰þwxé-þ§\þ§éxþwZþwxédÌ 8 !5!53#5! Y÷¯]xþw‰x]Qªª÷7ii¡]x‰Z‰x]Íü¤EÒiiuÌ 8 !'7'7!#3!7'Q]x‰þwx]÷¯ªªÉiic]xþwZþwx]Í\þ»ÒiiuÌ 8%77777773'7'7#'''''''uF´´´´´´´´´´´´F‹éx‰þwxécn´´´´´´´´´´´´F×F´´´´´´´´´´´´FéxþwZþwxén´´´´´´´´´´´´Fÿÿ,X@,ÿÿ,ÝX ,Ýÿÿ,ÝX@',,Ýÿÿ,§XÓ,§ÿÿ,§X@',,§ÿÿ,§X ',Ý,§ÿÿ,§X@',',Ý,§ÿÿ„°@„ÿÿ,°@',„ÿÿ,ݰ@',Ý„ÿÿ,ݰ@',',Ý„ÿÿ,§°@',§„ÿÿ,§°@',',§„ÿÿ,§°@',Ý',§„ÿÿ,§°@',',Ý',§„ÿÿ„ݰ „Ýÿÿ,ݰ@',„Ýÿÿ,ݰ ',Ý„Ýÿÿ,ݰ@',',Ý„Ýÿÿ,§° ',§„Ýÿÿ,§°@',',§„Ýÿÿ,§° ',Ý',§„Ýÿÿ,§°@',',Ý',§„Ýÿÿ„ݰ@'„„Ýÿÿ,ݰ@','„„Ýÿÿ,ݰ@',Ý'„„Ýÿÿ,ݰ@',',Ý'„„Ýÿÿ,§°@',§'„„Ýÿÿ,§°@',',§'„„Ýÿÿ,§°@',Ý',§'„„Ýÿÿ,§°@',',Ý',§'„„Ýÿÿ„§°Ó„§ÿÿ,§°@',„§ÿÿ,§° ',Ý„§ÿÿ,§°@',',Ý„§ÿÿ,§°Ó',§„§ÿÿ,§°@',',§„§ÿÿ,§° ',Ý',§„§ÿÿ,§°@',',Ý',§„§ÿÿ„§°@'„„§ÿÿ,§°@','„„§ÿÿ,§°@',Ý'„„§ÿÿ,§°@',',Ý'„„§ÿÿ,§°@',§'„„§ÿÿ,§°@',',§'„„§ÿÿ,§°@',Ý',§'„„§ÿÿ,§°@',',Ý',§'„„§ÿÿ„§° '„Ý„§ÿÿ,§°@','„Ý„§ÿÿ,§° ',Ý'„Ý„§ÿÿ,§°@',',Ý'„Ý„§ÿÿ,§° ',§'„Ý„§ÿÿ,§°@',',§'„Ý„§ÿÿ,§° ',Ý',§'„Ý„§ÿÿ,§°@',',Ý',§'„Ý„§ÿÿ„§°@'„'„Ý„§ÿÿ,§°@','„'„Ý„§ÿÿ,§°@',Ý'„'„Ý„§ÿÿ,§°@',',Ý'„'„Ý„§ÿÿ,§°@',§'„'„Ý„§ÿÿ,§°@',',§'„'„Ý„§ÿÿ,§°@',Ý',§'„'„Ý„§ÿÿ,§°@',',Ý',§'„'„Ý„§ÿÿ,þpXÿœ,þpÿÿ,þpX@',,þpÿÿ,þpX ',Ý,þpÿÿ,þpX@',',Ý,þpÿÿ,þpXÓ',§,þpÿÿ,þpX@',',§,þpÿÿ,þpX ',Ý',§,þpÿÿ,þpX@',',Ý',§,þpÿÿ,þp°@'„,þpÿÿ,þp°@','„,þpÿÿ,þp°@',Ý'„,þpÿÿ,þp°@',',Ý'„,þpÿÿ,þp°@',§'„,þpÿÿ,þp°@',',§'„,þpÿÿ,þp°@',Ý',§'„,þpÿÿ,þp°@',',Ý',§'„,þpÿÿ,þp° '„Ý,þpÿÿ,þp°@','„Ý,þpÿÿ,þp° ',Ý'„Ý,þpÿÿ,þp°@',',Ý'„Ý,þpÿÿ,þp° ',§'„Ý,þpÿÿ,þp°@',',§'„Ý,þpÿÿ,þp° ',Ý',§'„Ý,þpÿÿ,þp°@',',Ý',§'„Ý,þpÿÿ,þp°@'„'„Ý,þpÿÿ,þp°@','„'„Ý,þpÿÿ,þp°@',Ý'„'„Ý,þpÿÿ,þp°@',',Ý'„'„Ý,þpÿÿ,þp°@',§'„'„Ý,þpÿÿ,þp°@',',§'„'„Ý,þpÿÿ,þp°@',Ý',§'„'„Ý,þpÿÿ,þp°@',',Ý',§'„'„Ý,þpÿÿ,þp°Ó'„§,þpÿÿ,þp°@','„§,þpÿÿ,þp° ',Ý'„§,þpÿÿ,þp°@',',Ý'„§,þpÿÿ,þp°Ó',§'„§,þpÿÿ,þp°@',',§'„§,þpÿÿ,þp° ',Ý',§'„§,þpÿÿ,þp°@',',Ý',§'„§,þpÿÿ,þp°@'„'„§,þpÿÿ,þp°@','„'„§,þpÿÿ,þp°@',Ý'„'„§,þpÿÿ,þp°@',',Ý'„'„§,þpÿÿ,þp°@',§'„'„§,þpÿÿ,þp°@',',§'„'„§,þpÿÿ,þp°@',Ý',§'„'„§,þpÿÿ,þp°@',',Ý',§'„'„§,þpÿÿ,þp° '„Ý'„§,þpÿÿ,þp°@','„Ý'„§,þpÿÿ,þp° ',Ý'„Ý'„§,þpÿÿ,þp°@',',Ý'„Ý'„§,þpÿÿ,þp° ',§'„Ý'„§,þpÿÿ,þp°@',',§'„Ý'„§,þpÿÿ,þp° ',Ý',§'„Ý'„§,þpÿÿ,þp°@',',Ý',§'„Ý'„§,þpÿÿ,þp°@'„'„Ý'„§,þpÿÿ,þp°@','„'„Ý'„§,þpÿÿ,þp°@',Ý'„'„Ý'„§,þpÿÿ,þp°@',',Ý'„'„Ý'„§,þpÿÿ,þp°@',§'„'„Ý'„§,þpÿÿ,þp°@',',§'„'„Ý'„§,þpÿÿ,þp°@',Ý',§'„'„Ý'„§,þpÿÿ,þp°@',',Ý',§'„'„Ý'„§,þpÿÿ„þp°ÿœ„þpÿÿ,þp°@',„þpÿÿ,þp° ',Ý„þpÿÿ,þp°@',',Ý„þpÿÿ,þp°Ó',§„þpÿÿ,þp°@',',§„þpÿÿ,þp° ',Ý',§„þpÿÿ,þp°@',',Ý',§„þpÿÿ„þp°@'„„þpÿÿ,þp°@','„„þpÿÿ,þp°@',Ý'„„þpÿÿ,þp°@',',Ý'„„þpÿÿ,þp°@',§'„„þpÿÿ,þp°@',',§'„„þpÿÿ,þp°@',Ý',§'„„þpÿÿ,þp°@',',Ý',§'„„þpÿÿ„þp° '„Ý„þpÿÿ,þp°@','„Ý„þpÿÿ,þp° ',Ý'„Ý„þpÿÿ,þp°@',',Ý'„Ý„þpÿÿ,þp° ',§'„Ý„þpÿÿ,þp°@',',§'„Ý„þpÿÿ,þp° ',Ý',§'„Ý„þpÿÿ,þp°@',',Ý',§'„Ý„þpÿÿ„þp°@'„'„Ý„þpÿÿ,þp°@','„'„Ý„þpÿÿ,þp°@',Ý'„'„Ý„þpÿÿ,þp°@',',Ý'„'„Ý„þpÿÿ,þp°@',§'„'„Ý„þpÿÿ,þp°@',',§'„'„Ý„þpÿÿ,þp°@',Ý',§'„'„Ý„þpÿÿ,þp°@',',Ý',§'„'„Ý„þpÿÿ„þp°Ó'„§„þpÿÿ,þp°@','„§„þpÿÿ,þp° ',Ý'„§„þpÿÿ,þp°@',',Ý'„§„þpÿÿ,þp°Ó',§'„§„þpÿÿ,þp°@',',§'„§„þpÿÿ,þp° ',Ý',§'„§„þpÿÿ,þp°@',',Ý',§'„§„þpÿÿ„þp°@'„'„§„þpÿÿ,þp°@','„'„§„þpÿÿ,þp°@',Ý'„'„§„þpÿÿ,þp°@',',Ý'„'„§„þpÿÿ,þp°@',§'„'„§„þpÿÿ,þp°@',',§'„'„§„þpÿÿ,þp°@',Ý',§'„'„§„þpÿÿ,þp°@',',Ý',§'„'„§„þpÿÿ„þp° '„Ý'„§„þpÿÿ,þp°@','„Ý'„§„þpÿÿ,þp° ',Ý'„Ý'„§„þpÿÿ,þp°@',',Ý'„Ý'„§„þpÿÿ,þp° ',§'„Ý'„§„þpÿÿ,þp°@',',§'„Ý'„§„þpÿÿ,þp° ',Ý',§'„Ý'„§„þpÿÿ,þp°@',',Ý',§'„Ý'„§„þpÿÿ„þp°@'„'„Ý'„§„þpÿÿ,þp°@','„'„Ý'„§„þpÿÿ,þp°@',Ý'„'„Ý'„§„þpÿÿ,þp°@',',Ý'„'„Ý'„§„þpÿÿ,þp°@',§'„'„Ý'„§„þpÿÿ,þp°@',',§'„'„Ý'„§„þpÿÿ,þp°@',Ý',§'„'„Ý'„§„þpÿÿ,þp°@',',Ý',§'„'„Ý'„§„þpÿÿ,þp°ÿœ',þp„þpÿÿ,þp°@',',þp„þpÿÿ,þp° ',Ý',þp„þpÿÿ,þp°@',',Ý',þp„þpÿÿ,þp°Ó',§',þp„þpÿÿ,þp°@',',§',þp„þpÿÿ,þp° ',Ý',§',þp„þpÿÿ,þp°@',',Ý',§',þp„þpÿÿ,þp°@'„',þp„þpÿÿ,þp°@','„',þp„þpÿÿ,þp°@',Ý'„',þp„þpÿÿ,þp°@',',Ý'„',þp„þpÿÿ,þp°@',§'„',þp„þpÿÿ,þp°@',',§'„',þp„þpÿÿ,þp°@',Ý',§'„',þp„þpÿÿ,þp°@',',Ý',§'„',þp„þpÿÿ,þp° '„Ý',þp„þpÿÿ,þp°@','„Ý',þp„þpÿÿ,þp° ',Ý'„Ý',þp„þpÿÿ,þp°@',',Ý'„Ý',þp„þpÿÿ,þp° ',§'„Ý',þp„þpÿÿ,þp°@',',§'„Ý',þp„þpÿÿ,þp° ',Ý',§'„Ý',þp„þpÿÿ,þp°@',',Ý',§'„Ý',þp„þpÿÿ,þp°@'„'„Ý',þp„þpÿÿ,þp°@','„'„Ý',þp„þpÿÿ,þp°@',Ý'„'„Ý',þp„þpÿÿ,þp°@',',Ý'„'„Ý',þp„þpÿÿ,þp°@',§'„'„Ý',þp„þpÿÿ,þp°@',',§'„'„Ý',þp„þpÿÿ,þp°@',Ý',§'„'„Ý',þp„þpÿÿ,þp°@',',Ý',§'„'„Ý',þp„þpÿÿ,þp°Ó'„§',þp„þpÿÿ,þp°@','„§',þp„þpÿÿ,þp° ',Ý'„§',þp„þpÿÿ,þp°@',',Ý'„§',þp„þpÿÿ,þp°Ó',§'„§',þp„þpÿÿ,þp°@',',§'„§',þp„þpÿÿ,þp° ',Ý',§'„§',þp„þpÿÿ,þp°@',',Ý',§'„§',þp„þpÿÿ,þp°@'„'„§',þp„þpÿÿ,þp°@','„'„§',þp„þpÿÿ,þp°@',Ý'„'„§',þp„þpÿÿ,þp°@',',Ý'„'„§',þp„þpÿÿ,þp°@',§'„'„§',þp„þpÿÿ,þp°@',',§'„'„§',þp„þpÿÿ,þp°@',Ý',§'„'„§',þp„þpÿÿ,þp°@',',Ý',§'„'„§',þp„þpÿÿ,þp° '„Ý'„§',þp„þpÿÿ,þp°@','„Ý'„§',þp„þpÿÿ,þp° ',Ý'„Ý'„§',þp„þpÿÿ,þp°@',',Ý'„Ý'„§',þp„þpÿÿ,þp° ',§'„Ý'„§',þp„þpÿÿ,þp°@',',§'„Ý'„§',þp„þpÿÿ,þp° ',Ý',§'„Ý'„§',þp„þpÿÿ,þp°@',',Ý',§'„Ý'„§',þp„þpÿÿ,þp°@'„'„Ý'„§',þp„þpÿÿ,þp°@','„'„Ý'„§',þp„þpÿÿ,þp°@',Ý'„'„Ý'„§',þp„þpÿÿ,þp°@',',Ý'„'„Ý'„§',þp„þpÿÿ,þp°@',§'„'„Ý'„§',þp„þpÿÿ,þp°@',',§'„'„Ý'„§',þp„þpÿÿ,þp°@',Ý',§'„'„Ý'„§',þp„þpÿÿ,þp°@',',Ý',§'„'„Ý'„§',þp„þpdÌ?8 !5!53#5!•üs]xþw‰x]ªªûûii¡]x‰Z‰x]Íü¤EÒiiuÌP8 !'7'7!#3!7']x‰þwx]üsªªiic]xþwZþwx]Í\þ»Òii´Ü 3'#'##-Z-x\xÒxÒx\®.ýÒx\ün Óû#ÝÓûö’\ÿù´Õ733737#x\xÒxÒx\xýÓZ'x\’ûöÓÝû#Ó ün\xýÒO€'µ=%"'&'&'&767670327676764'&'&'&½pk_V1..1Vbr‰xé`Xk_V1..1V_kpIêxþ¸XE?#!!';B¢]YQS@?#!!';BQ9€.-\ZnllnZ_.‰xé$-\ZnlÿlnZ\-.)èxF!F@RN¾JV>Žl§ùï˜mGСBk˜ðþî>DôdWŽû0Xd”Ýï—ts•ðÝ“þ²ß.W@#.  -&.%)/ÜK° TX¹)ÿÀ8YÌ299ÌÜ´]<Ü<Ì999991@© &$À-±/ôÌ2ÄôÌ2ì907&54&'>5!2;#"#!532654&+C­­I°02°Ký½ù§lŽ>>Žl§ùþù5ŒUUŒ5D>ð˜kB¡û0Gm˜ñ“Ýð•st—ïÝ”dXÐŽWÙþ2Ûù  5 1üªúþû¨VüªªÌdþÎýÌá¦á22üšhþ¦Ù'æ %#3 5'ªªþ ü¢úþÈü:Äþžýœ¦Ù'æ 73 ÙªL^ü¢ªÈû8bbü:Èýï¦ÙÿÂ'B 7Ù''üÙ›â>€ý°Pú€Nqþ¢ÆÙÿÂ'B 'Ù''üÙ›þ>€ý°Pú€Nq^ýDÙÿÂ'B Ù''üÙ>€ý°Pú€NÙÿÂ'B%  'üÙüÙ''ýt¢àNý²€ý°PÝþÙÿÂ'B5  5Ù''üÙüÙŒbàý²Nú€Pý°Ýäþ#îu úôþ þ uûÛûÓ-ÙÛ3!3!!#!#!5 ¨L¨3þͨþ´¨þÍ×-ýÓ-ýÓªýÓ-ýÓ-ªÙÛ333333#######5¨¨¶¨¶¨ÏϨ¶¨¶¨Ï×-ýÓ-ýÓ-ýÓªýÓ-ýÓ-ýÓ-ª:þkÆû7!!  767654'&'$  $'&'&547676h0þÐ8þÀŒ‰rtrrtr‰Œ@Œ‰rtrrtr‰þ ŽVŽŽGFFGŽŽþªþrþªŽŽGFFGŽŽèþ’È;:rsþ죟þìsr:;;:rsŸ£sr:ôެªÉŬªŽŽª¬Åɪ¬Ž:þkÆû3?  767654'&'$  $'&'&547676!!#!5! þÀŒ‰rtrrtr‰Œ@Œ‰rtrrtr‰þ ŽVŽŽGFFGŽŽþªþrþªŽŽGFFGŽ€ýs¨ýsB;:rsþ죟þìsr:;;:rsŸ£sr:ôެªÉŬªŽŽª¬Åɪ¬Ž€Kýsªýsª:þkÆû3?  767654'&'$  $'&'&547676   '  þÀŒ‰rtrrtr‰Œ@Œ‰rtrrtr‰þ ŽVŽŽGFFGŽŽþªþrþªŽŽGFFGŽ€]ÍÎxþ3Íwþ3þ2xÍþ3B;:rsþ죟þìsr:;;:rsŸ£sr:ôެªÉŬªŽŽª¬Åɪ¬Ž€èþ3Íxþ2þ3wÍþ3xÎÍÿÿuþM %' Io& I' I% IJuþM¶327!5!>2&#"!!"&' ;E þ€”2&#"!!!!"&' ;Eþø þä$Ÿ˜€”;Eþð þÜŸ˜€›‚¯Ϊð¨@¥‡z‚¯þÙ¨ðªþ¥‡zuþM¶&#"%"&'73275%>2";ECþµŸ˜€”;Eþ¾JŸ˜€ö‚¯ýç¶ ºým¥‡z‚¯¶ º“¥‡zuþM¶*3&#"&'67"&'7327&'&54767>2";E™Iq(P >6DŸ˜€”;E]InoSuŸ˜€þ=,HK%ö‚¯þÖ)ˆAH!þ+p$ þ¾¥‡z‚¯1Io›œsV<¤ˆzüEÖ.JghH$uþM-2&#"676='73'"&'7327&'&53>2";E+@/V]H½6¼H\nUm —€”;D [>wfP3,, —I6ö‚¯üx/Ur]H½½H]œlVþ¡¥‡z‚¯M>wšrN3 ™¥‡F4uþM¶!3#!!>2&#"!!"&'732²ÓÓwþ¥¢—~”9F 9þ¡ ©‘}”9G¦þýêr0®§…}Œ‚¯þõü¼þÿ­}Œ‚uþM¶+3#>2&#""&'73273264&²ÓÓc)¢—~”9GcBnnVs¢—~”9F (6o‡”¦þ~ç…|‚¯þÖK|þÒoUþ¾§…}Œ‚¯¶“ØuþMp.3#327264&#">2&#"632#"'"&'ÓÓþz;E-8pÆ–ŠqÕS¢—€”;E;D©ÎÜ›WI —€®þýµ‚¯3>6‘Ñ¡]¤ˆz‚¯þËéþÒÛ!þ£¥‡zuþM¶ 13#64&"327&'&767>2&#""&'­ÓÓË”ŠÖ¢;E]InoSuŸ˜€”;EcBnnVsŸ˜€°þzÑ‘Ïþ-‚¯1Io7sV<¤ˆz‚¯þÖK|þÒoUþ¾¥‡zuþM¶!%7!>2&#"!!"&'73273!#3˜;¢—~”9GþÄ©”9G ŬþÅ«“›þI§…}Œ‚¯þÐýþÞ­{Ž‚¯ tþêþÿ¾þMm-&#"!2#567&'!"&'7327!5!>2";Ed‹_``þë!Ÿ˜€”;D þÜ»`þÅ;`»*Ÿ˜I6ö‚¯ýïÆŒˆebýI¥‡z‚¯žº`:H:`º*¥‡F4uþM¶#&#"7'"&'7327'7'7>2";Exüüx±Ÿ˜€”;EŸzþüx²Ÿ˜€ö‚¯þXyüüx°ýô¥‡z‚¯â yüüx³Ø¥‡zuþM¶*327#467>2&#"#4'"&' ;E-A 4y›yŸ˜€”;E Z>Vy|-Ÿ˜€›‚¯¦2PœþÈIϼ+¥‡z‚¯þæEaÖþ·8ó2üJ¥‡zuþM¶'&#"63"&'7327&'&53>2";E*’yþ€”;E\?Vy~+Ÿ˜€ö‚¯ü&8þ·þŒ'þ¢¥‡z‚¯LFaÖIþÈõ1…¥‡zuþMÀù>32&#"#"&'7327!5ã¢KL~”9GA¢LK~”9Güïâ§…}Œ‚¯ú°§…}Œ‚¯g  kýb¶>32&#"#"&'73275!ã¢KL~”9GA¢LK~”9GþÉâ§…}Œ‚¯ú°§…}Œ‚¯ýР ÿÿ?œÅ™ÙÿÛ¦ 5 5ÛúþûøúþF¶Ѷeþ‘þ“¶ѦÑÙÿÛ¦ 55ÙúþúþFѶþ/ì¶þ/¦þ/¶mÿÿÙÿݦ' 7/ÿ»üÿÿÙÿÛ¦& 7þ»ýÿÿÙÿÛ'' 7áüÿÿÙÿÛ'' 7áýÿÿÙÿÝ' 7/ÌüÿÿÙÿÛ ' 7ÿþÇýÙþñÜN:A%#"'&'&'&#"5>32326#"'&'&'&#"5>32326 5Üj²bn’ š^X¬bh´`n” œ^V¨hj²bn’ š^X¬bh´`n” œ^V¨gü@ÀúþP³NE;=LT³NE;=K›²PD:32326#"'&'&'&#"5>3232655Üj²bn’ š^X¬bh´`n” œ^V¨hj²bn’ š^X¬bh´`n” œ^V¨ûeúþÀP³NE;=LT³NE;=K›²PD:327&#"56767326 5Üj²bDS4ŒWV¨hj²bm“\¤Y@/X¬bh´`ES3‹VX¬bhZmM’p[¤Y@1V¨gü@Àúþ–²PDƒ4KU³NE;é@âLT³NE‚4LR²N"*,é@ãJ^þëþî²pªoÙþ_ÜN5<#"'3267#"/'7&#"5>327&#"5>32732655Üj²bDS4ŒWV¨hj²bm“\¤Y@/X¬bh´`ES3‹VX¬bh´`n”[¤Y@1V¨ûeúþÀ–²PDƒ4KU³NE;é@âLT³NE‚4LR²ND:é@ãJ^°þ‘ªþ²Ùý„ÛT 5!5!-5 !5!Ûúþü‹uúþúþuü‹úþúþû°/ª0²Òý\ªû^ÕÒ²þЪþÑ~ªÙý„ÛT -55!55!Ùuü‹úþúþü‹þsûÕÒ²þЪþÑþ᪪ü°/ª0²ÒùªªÙÿÜN%#"/&'&#"5>32326!! 5Üj³an’š^X¬bh´`n”œ^V¨ûfúþü@Àúþ–²PD:32326!!55Üj³an’š^X¬bh´`n”œ^V¨ûfúþúþÀ–²PD:323265-5Ûi³an’›^X¬bi³an“›^V©gúþü‹þsuü‹úþ¾²OE;=LS²NE; =KJ°/ª0²Òú:ÕÒ²þЪþÑÙþÛ !(#"/&'&#"5>32326-5 5Ûi³an’›^X¬bi³an“›^V©ûeuü‹úþúþü‹¾²OE;=LS²NE; =KJÕÒ²þЪþÑû¿°/ª0²ÒÙþ,Û× -55!55!Ùuü‹úþúþü‹þs%ÕÒ²þЪþÑþ᪪(°/ª0²ÒùÙªªÙþ,Û× 5!5!-5 !5!Ûúþü‹uúþúþuü‹úþúþ%°/ª0²Òý\ª~ÕÒ²þЪþÑû^ªÙþ6Ûî 5 5 -55Ûúþü‹uúþúþuü‹úþ•°/ª0²ÒýV/°þÑý¦ÕÒ²þЪþÑþа/°Ùþ6Ûî -555 5Ùuü‹úþúþü‹uúþ•ÕÒ²þЪþÑþÛ°/°ý'°/ª0²ÒýK/°þÑÙÿ„Û& 55Ûúþúþûøpþ/¶Ñû¶ѦѶþ“Ùÿ„Û& 5 5Ùúþûøúþp¶þ/¶ý›om¶þ/¦þ/ÿÿÙÿ„Ý&' 7/þîÿÿÙÿ„Û&' 7ÿþþäÚÿ¶Ü{ 5!5 5!Úúþü@þ¾Ѫªû•°oªp²þî9ªªÚÿ¶Ü{ !5! 5 !5!ÜúþúþÀü@úþúþѪúë²þªþ‘þªÙÿÀÛÍ555Ùúþúþü@«²p²ýN²p²ü—°oªp²þîÙÿÀÛÍ 55 5ÛúþúþúþÀü@úþ«p²þþ p²þýU²þªþ‘ÙÜ(".#"#"&'5327>76325Üh¨V^œ ”n`´hb¬X^š ’nb²júþü@ÊTL>7632 5Üh¨V^œ ”n`´hb¬X^š ’nb²júýÁü?úþÊTL>ªªþÖªªÜVJ<:DN´TL<:DNüD°oªp²þîÙþ˜ÜÔ$+5!5!.#"#"&'53276767632 5Ùúþh¨V^œ ”n`´hb¬X^š ’nb²júýÀü@úþ>ªªþÖªªÜVJ<:DN´TL<:DNüD²þªþ‘ÙfÛÎ $!!!5!676762!!&'&'&!!Ùúþ‚C.8þd 6WYÎYV7 þe8-ý;úþZª{+DD\¨93[2332[0<¨[EC,üéªÙÿÏÛW7!!%5$Ùúþþþ$Üþü}ƒyª±þå]]þå×x„¦|€ÙÿÏÛW%!5505%$Ûúþƒü}þÜþ$yªª×þ€|¦„þˆ×]]Ùþ×ÛW !!'7!5!%5$¬–Zóý²…–Zþ N´þþ$Üþü}ƒqP¨ªøP¨ªþå]]þå×x„¦|€Ùþ×ÛW !!'7!5!55%$¬–Zóý²…–Zþ Ný²ƒü}þÜþ$qP¨ªøP¨ª×þ€|¦„þˆ×]]Ùÿ ÛK75!5!%5$ÙúþþÍýØ!:þ£ü[¥]3ªªþÖªªkþå-Q÷×tX¦VvÙÿ ÛK75!5!55$%$Ùúþúþ]¥ü[þ£:!ýØ3ªªþÖªªk×þŠV¦XþŒ×÷Q-ÙþqÛK!5!7!5!7!!!!'%5$&þ³Ú`ýÆȉŠ)Pþ"_=ý6ŠŠÞþÍýØ!:þ£ü[¥]êªsª¤s1ªsª¥tþå-Q÷×tX¦VvÙþqÛK!5!7!5!7!!!!'55$%$&þ³Ú`ýÆȉŠ)Pþ"_=ý6ŠŠþÜ]¥ü[þ£:!ýØêªsª¤s1ªsª¥t×þŠV¦XþŒ×÷Q-ÙþyÛ‹:E#"'&'&'&#"5>76326#"'&'&'&#"5>32>%5$Ûi³an’ ›^X¬bi²b`¡ ›^V©gi³an’ ›^X¬bi³an“ ªž°gþÍýØ!:þ£ü[¥](³NE;=LT³N9 A=Kš²OE;=LS²NE;C Eìþå-Q÷×tX¦VvÙþyÛ‹:E#"'&'&'&#"5>76326#"'&'&'&#"5>32>55$%$Ûi³an’ ›^X¬bi²b`¡ ›^V©gi³an’ ›^X¬bi³an“ ªž°ûe]¥ü[þ£:!ýØ(³NE;=LT³N9 A=Kš²OE;=LS²NE;C Eì×þŠV¦XþŒ×÷Q-ÙýíÜ‹6A#"'3267#"/'7&#"5>327&#"56767326%5$Üj²bDS4ŒWV¨hj²bm“\¤Y@/X¬bh´`ES3‹VX¬bhZmM’p[¤Y@1V¨gþÍýØ!:þ£ü[¥]$²PDƒ4KU³NE;é@âLT³NE‚4LR²N"*,é@ãJæþå-Q÷×tX¦VvÙýíÜ‹6A#"'3267#"/'7&#"5>327&#"5676732655$%$Üj²bDS4ŒWV¨hj²bm“\¤Y@/X¬bh´`ES3‹VX¬bhZmM’p[¤Y@1V¨ûe]¥ü[þ£:!ýØ$²PDƒ4KU³NE;é@âLT³NE‚4LR²N"*,é@ãJæ×þŠV¦XþŒ×÷Q-Ùþ¡Û®7 5Ùúþúþü@ÀúþÃþ²pôþ²p«þëþî²pªoÙþ¡Û®%5555ÛúþúþúþÀòþ²²²þ²i°þ‘ªþ²µÿÉŸ³ ' '!Â]ý#¿Ý\eÁý#þ´ÝÂÏN\ý#¿Ý]xý–Âý#LÝÁ ÿÉ ³ !77 ! ýþˆ\Ý¿ý#þ€ÏÂÝþ´ý#Nþˆ]ý#¿ÝýòÏÁý#þ´Ýµ4Ÿ !7 7:\ý#¿Ý]xý–Âý#LÝÁ]Ý¿ý#\eÁÝLý#Âý1 4  %''' !ý]Ý¿ý#\eÁÝLý#Âý1™\Ý¿ý#]þˆjÂÝþ´ý#Á7†P~ % ! !!5 5!3þü!üþýß…ýþö é †üüþïþþõƒƒþöþöƒƒ 7†?~% !!3þü ûô†üüþïþ*^V !!^üüþïþ* üþûô ^ÿÍVÕ!!!^ÖþÉ ûôþµÿÉŸ³' '!ŸÁý#þ´ÝÂÏäÂý#LÝÁ ÿÉ ³!  ÏÂÝþ´ý#äÏÁý#þ´Ýµ4Ÿ%7 7ÐÂý#LÝÁ4ÁÝLý#Âý1 4  ! ÁÝLý#Âý1ÂÝþ´ý#Á7†P~ % ! !3þü!üþý߆üüþïþþ^ÿÍV ! !! !EþþþïüüþïÉþüCüþuÿùR× #7!5!7ÈZþxxèü/{êxŠxêªþ:êxu-R '!5!'ÈŠxêû…Ñèxˆ þvxêþ:ªêxŠHÿù%× 7!!7Òþvxê{ü/èxþxŠxêƪþäêxþvH-% 3'!!'ÒZˆxèÑû…êx þvxêþäªÆêxºÿÕ$!%!!ºúW7úÉü ùàržºÿÕ$!!!ºúW7úÉü ùàžºÿÕ$!!,7úÉrŠ<úR ùàºÿÕ$!!cúÉrŠ<úR ùàÿ!$ výŠüó üòdxxýˆüðüðÿ!$ žuüó üòýˆðýˆüðüðÿ!$ ! žuvú} üòýˆxüðüðÿ!$! žëýŠüó üòxýˆüðüð ºÿÕ$ #)-17#535#5#5#5##5#5#5##5##53#5#5#5##5Õãqrrrrþ¥ÌêÊMrqêÌêÊþ¤rärrrrärrætr¶ÌÌ´ÊÊü$tttt¬ærtttttúÆrtæ¶ÌÌ´ÊÊÐtræ$ÿÊÐ# ! !ªzý0üªVVþºûà‹ü±O þmý“ü–ÿFfü   vvýŠýŠrèèý5þ•kØkþ•üæ\­þSü¤þS–ÿFfü7 –èèýó\­þSü¤þS"ÿ¹Ù‰ ! ÐþR®[®þRGèèýý jÕ3!!!!!!#535#53ÉÊ[þ¥[þ¥×ü_¿¿¿¿Õþþ~ª, *3###535#5333#y±±¸····¸±±¼ýÔ,8ýÈÿ×jÕ#3327673#"'!!&'&#"#>32ÉÊ &}f[×ü_ &}f[Õý, %$R†”þ/ª %$R†” Õ!2+##5332654&+!ÉÈû€€ûþÊ¿¿Êþššþ[ÕqrÛÝqqý¨ÏÑ’‡†’ÐÉþfTÕ$Ç@  $ !? %üì2üÄì9999Ì991@&  B •°• $•/ôìÔì999ôì990KSXíí9Y"²@&]@Bz%%%&'&&& &66FFhuuwˆˆ˜˜]]#.+;#"&! 32654&#A{>ÍÙ¿J‹xÜn†?MãÍÈüƒý‰þ’••’¼~þh–býñ–ªômÖØºOýƒ…Hÿ¢œ¼#(07#5#"''7&546;7&'&#"5>327354326=-¸?¼ˆ‡\g`n;ýû)ž T—`¶Te¾Zçx”_¨9»58¯þ¼>c™¹ýÅåc3Výªfa<}N…V{½À¿ E..ª''r³OËs+Aºþx.Ù´)þâ 3{ ÿèÿB“333#;#"'&'##53w1ÀªÀ ÑªKs½½ÕQ fªÖ‡‡žþÂ3ýÍþo‰NšP þÖr>ÿÿÉþ¿Õ-ºþå!#4&#"#3676323#d¸||•¬¹¹BYZuÁcc¸¸žŸž¾¤ý‡ýže22wxèýõþLÉþ¿jÕ%3###3!Á©ÅEý3ÊÊžýªþAÏý1Õý‰wýHºþåœ33 3###º¹%ëý®ÌŸ¸8ýǹüiãýôþEþL#ýÝ\þ¿èÕ !!#!5!s•üPÉû=°ügÕšûoþAš‘Xþå“` !!#!5!qjýLl¸ü}´ýe`¨üÛþR¨%sÿãwñ-@••Œ‘¯ üìÔì221/ìäôÄìÄî053#5# !232#"³ÄÄMì¥þòþ¬T¥ìüßêÌÍëëÍÌêíèú+ç„€«\\«€ýxþãþ»EEþ»ÉþfÕ! !+53265##É-}-ÍãMD†nþËþÄÕüøú“þòôª–·üúáhÕ %!#3!3¼ýÛ…åýÇÒˆ¡ˆÕÇçüRÕþ=à{6762354&#" #3$ )rQ²¹ƒÞTþìþ¢Ã¤úm*T:rr†¿áý¬û D ð # #3 3 67632#54&#"fþÉþþÅþÂþþŠÌ:9ã:54'&'&s~Ô&¢¢&Ô~~ÔþÚ¢¢þÚÔ~Õ.]Ø=@µN\ªµN\.]Ø=êÑzzÑþâžþâÑzzÑž}qäa ³!ˆSáÝûM!‰Rá}|påasÿ•§?#-n@.  '&$ /$ ‘.üìôììÄ9999991@ .'& ) )••‘Œ.äôìì/Ä9999999046$327#"''7&7&#"4'32>s~Ô&¢ãÄ ‚n€~ÔþÚ¢äÄ œnÕ‘ê§wÖœ\Š‘ý§wÖœ\êÑzvÄiÂjþÞžþâÑzwÅkÁj!žæ®˜^`¥á|å¯üg^`¥á–”Õ .@••• •¯  Ü<<<Ì2221/äìÜììÜì03#3#3#3#–þþþþþþþþÕÍßÍáÍáÍ)áÕ:@ ¯ ÜÜÄÌ1/<ä0@íí22íí # #3².èþ]þºæèºýFýâÕÉ;Õ -@    üüÜüÜüü1@ ‘• ­ /<ô<ìä03!#!#!ÊÔÊý"ÊÔÕýœüÇý9q><Õ@  ÔÄÜÄÄ9/ì1´€ ]@¯ /<ì220K°BPX@     ìììì@     @ ííY333 # #‚ÚÊ Øþ Øþ\þXÚÕþdœþsý3üø{ý…–¢Õ 1@• ¯•  Ü<Ì2Ü<Ì2´??]1/<ì2ô<ì20%3#3#3#3#¤þþþþýòþþþþÍÍÕÍûÅÍÕÍ\Õ 7@••  ôìÔÄ91/ìôì0@ BKSXííY" !!!!&æýâûT¬üœdDçúÕÕšûo–h’h $@    ÜìÜìÜìÌ1/<<Ì2203#3#3#ÆÌÌþhÌÌþhÌÌhÿÿÿÿÿÉ‹Õ0o–Õ !@ •¯• /Ì2üÌ21/ì2ôì20!!!!5!!o&þÓ.üÚ-þÒÕªûªªoÿâ1ð/,@!•‘ '•,Œ01*$ 0Ì2ÜìÜ2Ì1ä2ì2ô<ì20!"'53 !"563 676!2&# !27# '&Ð%4Âþ¬€rmyþðÿymr€OÇ4%%4ÂT€rmyÿþðymr€þ²È4*B6Ð!¯*:'(8)¯ Ï6AB6Ï ¯)þÈþØþÙþÆ*¯!Ð6oÌÕP@   Ô<<Ì2ì22Ô<<ü<<Ì21@ • • ¯/<ä2Ü<<ì22Ü<<ì2203!3!!!!#!#!5!!5!!‘Ê’ÊþëþëÊþnÊþÞ"þÞ"Ê’ÕþxˆþxªþŽªþy‡þy‡ªrªªþŽrÿÿÉ3դ̈Õ@•¯•üìÄ21/ìüì03!!!̼ýòÕªûªÿÿÉ“Õ,oÌÕ7@   /<<Ì2ü<<Ì21@•• ¯ /äÜ<ì2Ü<ì203!!!!#!5!!5!¿ÊCý½Cý½Êý°Pý°PÕþxªþŽªþy‡ªrªÉ•Õ7@ ¯üK°TX¹@8Y2ì21/ìÌÌ0@ 0 @ P ` Ÿ ]73#3#ÉÌÌÊÊÍÍÕû¦Ÿÿã¤Õ>@••‘Œ üÄìÔÄìÔì1äôììÄ0@ BKSXíY"47!5!32654'3! $Ÿ¾»ü¤¬ýx·ÅÛÉâÕË¿áþ»þ¹þÎþ¹ßwNªªþetäw–¤ˆˆ³Îà¡Îæùcÿã­ð #/9@1E- !'E0ü<ì2Ô<ì2ì1@¹ ¹‘0¹*¹$Œ0ôìÔìôìÔì02654&#""$54$322654&#""$54$32ˆ™··™™··™ùþÔ,ùù,þÔù™··™™··™ùþÔ,ùù,þÔPIIPPIIP¤¥˜—¦¦—˜¥ýPIIPPIIP¤¦—˜¥¥˜—¦sÿã§ð'(@ ) ‘(üìôìì1@ •#•‘Œ(äôìì046$32#"$&732>54.#"s~Ô&¢¢&Ô~~ÔþÚ¢¢þÚÔ~Õ\œÖwwÖœ\\œÖwwÖœ\êÑzzÑþâžþâÑzzÑž}á¥``¥á}|á¥``¥ásÿ‘§ð,P@  ! #.# ‘-üìôììÄ9991@ ! (•(•‘Œ-äôììÀ99046$32'#"$&73277654.#"s~Ô&¢¢&Ô~l¢§Äå¢þÚÔ~Õ\œÖw§þ‘ƒj\œÖwwÖœ\êžÐzzÐþâžžþàiÇkÊwzО|â¤`^¾jþI®å|â¤``¤âÉ;Õ -@   üüÜüÜüü1@ ‘ •­ /ôì2ä203!3!#ÉÊÞÊþ,Êdqý9ÇüýœdÉÇÕ (@ •¯•­•  Ü<<Ü<<Ì1/ìüìüì03#3#3#ÉþþþþþþQÍþIÍÕÍ–hÕ ?@•¯ •­ •   Ü<Ì2ÜÌÜ<Ì2¶??? ]1/<ì2üìü<ì20#53#533#3#3#hþþþþýþþþþþþþÍú+Í„ÍþIÍÕÍsÿã§ð'+>@- )(( ‘,üìôì9/Ìì²/)]1@+Î(­•#•‘Œ,äôììôì046$32#"$&732>54.#"3#s~Ô&¢¢&Ô~~ÔþÚ¢¢þÚÔ~Õ\œÖwwÖœ\\œÖwwÖœ\ÆþþêÑzzÑþâžþâÑzzÑž}á¥``¥á}|á¥``¥áÍsÿä§>,P@  %$#& !.! ‘-üìôììÄ9991@ #&$%(•(•‘Œ-äôììÄ99046$327#"$&732>54''&#"s~Ô&¢äÄ ƒžn~ÔþÚ¢¢þÚÔ~Õ\œÖwwÖœ\þ”‚p§wÖœ\êžÐzvÄiÂkþàžžþâÐzzО|â¤``¤â|å®þ?lÆ^`¤âsÿãrï%1=G@8&,20>üìô<ì2Ü<ì21@/•;• 5 )•#•‘#Œ>äôìì9/ì/ì0! #"&547 !&54632! 32654&#"4&#"326s†S“_ ™…„™þþþû™„…™ _þmþ­þz™,,,,,,,,égŸs'O;‘‘H6þ¾þâþâþ¾6H‘‘;O'sŸz<11<;22‡<11<;//dÀÕ #@• ­¯  Ô<Äü<Ä1/äô<ì203!!#!5!­ÊIý·Êý·IÕýjªýk•ªÿÿ=;Õ;sÿãrï3?Kf@•F4%+6:0Lüì2ô<ì2Ü<ì29/<ü<Ì1@•=•(I• C (7##•1•‘1ŒLäôìì9/ì/ì/<ì2ÌÄ0! #"&547"333###3&54632! 32654&#"4&#"326s†S“_ ™…„™î—»ÌééÌ»—…™A;…þïþ­þz™,,,,,,,,égŸs'O;‘‘H6¢Šßÿÿªÿÿߊ¢6H‘‘;OO4Ÿz<11<;22‡<11<;//É‹Õ;@ •••­•  üì2ÔÄÄÜ<ì21/ì2ìôìî20²]!!!33##!!!ɰýÌêêÌþïøü>ÕªþFÿÿªÿÿýãªÿÿhÕ"É;Õ@• üìÔìì1/ìô<0)3!3;ûŽÊÞÊÕúÕ+ÉyÕ=@ B• ü<Ô1/ôì20KSX@ììììY!# 5!!±èßþ!°ý8ßÀkªªýšsÛÕ#O@%$!  /<<ì22Ô<ì2ìÔ<ì2ì1@• • /<<Ô<ì2ô<<Ô<ì2032653#2#4&##"#3"3ÂÊŠòÓþ‡ÖÖyÓòŠÊŠòÓyÖÖþ‡Óò+ªþVâÈþîþºþÛþºþîÈâþVªâÈF%FÈâÉöÕ.@ ¯üK°TX¹@8YìÄÄ1/ìÄ0´í!##ÉÞþuÄÕý-úásÛÕ+f@- ,&'  #+ /<<<ì222Ô<ì2ÄìÔ<ì2ìÄ1@+¹* •  #•*'"/<<Ô<ì2ô<<Ô<ì29/<ì205!5"3332653#!!2#4&##"#35ÁÖþ‡ÓòŠÊŠòÓþ‡Ö¿þAÖyÓòŠÊŠòÓyÖ•ª>FÈâªþVâÈþîþº>ª=þºþîÈâþVªâÈF=6-éÕ@ ÜÌÜÌ1/ÌÄ20!3!3éüM•-¨þûÿÿªþ¼‚Õ'Šü6Šÿÿªþ¼‚Õ'‘ü6‘ÿÿªþ¼‚Õ'ü6ÿÿªþ¼‚Õ'ü6ÿÿªþ¼‚Õ'Šü6ÿÿªþ¼‚Õ&Šü6ÿÿªþ¼‚Õ'ü6‘ÿÿªþ¼‚Õ&‘ü6ÿÿªþ¼‚Õ'Šü6Žÿÿªþ¼ƒÕ&Š‹ü6ÿÿªþ¼‚Õ&‘Šü6ÿÿªþ¼‚Õ'‘ü6Šÿÿªþ¼‚Õ&ŠŒü6ÿÿªþ¼‚Õ'Šü6Œÿÿªþ¼‚Õ&‘ü6ÿÿªþ¼‚Õ&‘ü6ÿÿªþ¼ƒÕ'ü6‹ÿÿªþ¼‚Õ'Žü6ÿÿªþ¼ƒÕ'‹ü6‘ÿÿªþ¼‚Õ&Ž‘ü6ÿÿªþ¼‚Õ'ü6Œÿÿªþ¼‚Õ&Œü6ÿÿªþ¼‚Õ&‘ü6ÿÿªþ¼‚Õ'ü6‘ÿÿªþ¼‚Õ'ü6Šÿÿªþ¼‚Õ&Šü6ÿÿªþ¼‚Õ&ü6ÿÿªþ¼ƒÕ&‹Žü6ÿÿªþ¼‚Õ&ü6ÿÿªþ¼‚Õ&ŒŒü6ÿÿªþ¼ƒÕ&‹ü6ÿÿªþ¼‚Õ&Žü6ÿÿªþ¼‚Õ'ü6Šÿÿªþ¼‚Õ&Šü6ÿÿªþ¼‚Õ'‘ü6Œÿÿªþ¼‚Õ&‘Œü6ÿÿªþ¼‚Õ&ŽŒü6ÿÿªþ¼ƒÕ&Œ‹ü6ÿÿªþ¼‚Õ&ü6ÿÿªþ¼‚Õ'ü6ÿÿªþ¼ƒÕ&‹ü6ÿÿªþ¼‚Õ&Žü6ÿÿªþ¼ƒÕ&‹Šü6ÿÿªþ¼‚Õ&ŠŽü6ÿÿªþ¼ƒÕ&‹‘ü6ÿÿªþ¼‚Õ&‘Žü6ÿÿªþ¼ƒÕ'ü6‹ÿÿªþ¼‚Õ&Žü6ÿÿªþ¼ƒÕ&‹Œü6ÿÿªþ¼‚Õ&ŒŽü6ÿÿªþ¼‚Õ&ü6ÿÿªþ¼‚Õ&ü6ÿÿªþ¼‚Õ&Žü6ÿÿªþ¼ƒÕ&‹ü6ÿÿªþ¼‚Õ&Œü6ÿÿªþ¼‚Õ&Œü6ÿÿªþ¼‚Õ&ŽŽü6ÿÿªþ¼ƒÕ&‹‹ü6ÿÿªþ¼‚Õ&Žü6ÿÿªþ¼ƒÕ&‹ü6ÿÿªþ¼ƒÕ&Ž‹ü6ÿÿªþ¼‚Õ&ü6ÿÿªþ¼‚Õ&Œü6ÿÿªþ¼‚Õ'ü6Œ–:Ü #'+/37Ú·/$0(7,48Ü<Ü<<<<¶#+ 3'Ü<<<<Ü<¶ Ü<Ü<<<<¶ Ü<<<<Ü<°9̰XKRX°8K° bf °TX±30<32#4&#"#9þó`M1¸C±uÁȸ||•­MM 7ÑÔB³uÁƸ||•¬¹e,'"¼xMü¬fcðç¦ýaŸŸ¾¤‘?'¼»Gzýžedïèý\žŸž¾¤ûÝ®þVã5<!"'&76763!!32653#5#"&5#3!#"&5332765!"3 þÛ•^SWsvÝ™||•­¸¸C±uÁÈáÕþsC±uÁȸ||•WVþÛƒƒ¶^S‘ƒBWþLýaŸŸ¾¤{û ¬fcðçüûB¢Vfcðçfþ¡ŸŸ__¤{{Š®þV ›H!&#5#"&5332654/&763!6763232653#5#"'&=4&#"#9þó`M1¸C±uÁȸ||•­MM 7ÑÔc%ZkÁÆ>8nŒ­¸¸C±l¯bd||x¬¹e,'"¼xMü¬fcðç¦ýaŸŸ¾¤‘?'¼»Gzýž2ïèã—XO¾¤{û ¬fcx{ä䟞¾¤ûÝÿÿº[t`&ùþŠ VÈ 332673 &Vv aWV` v žþÞžKKJLÿÿ¶[`&(üÿS N~`6@  FüìÔ99Ä1·B¼© /ì2ô<0KSX·ííY%2767653)5!3$Wq…2!ºþþdþjÁ±/8ªs4÷þÖþtþV±¯ g` ##4673>=3|ëÿýuà‹¸Ë·þdþ7þ”¸ºÄþ<T £¦ëÚé"yýú¹›²þÕX½`#!5!óºüeÑü/Ѻ`!#3#4&#!5!2s¹¹ª¹n†üJ³ãÍŠývy–ôþòX»`35!26&#!5! #X-ë’¦×üÓyÛøòŽŸ’þøþØþ¿ïX»4=!3!#¨Tû\»¨þ¬¿“[ÅCþLþ¬zþèlþòº` 3!2%!4&#!º³ãÍûWïn†ý`ôþòý¢ê–X»` !#4&#!5!2»ºn†üK³ãÍy–ôþòX»`!#4&#!+5265#5!2»ºn†ýãµ rL¼±ãÍy–ýÅÖÀa¦;ôþòÙ-Û· Üüüì1µœ Ôü<Ä05!3!Ù-¨--ª-ýÓªÿÿº½–&$ÿÿº½–&$ÿÿº½–&ÿÿº½–&ÿÿ²þ»ƒ`& üìÿÿ²þuƒ`& ýìÿÿ²ƒ`& ÿ´þ\ÿÿX†`& ÿBÿÿCZ`& þœÿÿXh`&ÿÿÿºd`&ÿÿQÎ`'þúZÿÿX¦`&þÿÿºÿã¿`&&ÿÿQÖÎ`&ZþúÿÿXþV`&þäÿÿX`&ÿ:ÿÿX&þÚÿÿXž`&%ÿÿXÎ`&þ·ÿÿXÿã(`&ƒÿÿºþVd`&Iÿÿÿºd`&{ÿÿC!`&!þnÿÿÿºþVÜ`&"ÿÿX`&#ÿIÿÿº½`&$ÿÿˆ`&%<ÿÿºt–&þÆÿÿX†–& ÃÿÿX–&Šÿÿºd–&ÄXƒ3>=3##67'#3x]Gg¸²G.Ùþi=d¸ªB—·»`ýú‰Ô›²þó¹"þ:T„Ôë®)êCÿÿ‚þ ë'×9ÿnÿÿþ ï'×9ÿÙÿÿÿìþ ‡X&Ú×àÿÿÿÿìþ ~X'×àÿÛÿÿ‚þ ë'Ö¼ÿnÿÿþ ï'Ö¼ÿÙÿÿÿìþ óX&ÚÖcÿÿÿÿìþ ~X&ÛÖcÿÿÿ‚þ ë'ؼÿnÿÿþ ï'ؼÿÙÿÿÿìþ óX&ÚØcÿÿÿÿìþ ~X&ÛØcÿÿÿ‚ÿìë'×9„nÿÿÿìï'×9„Ùÿÿÿì‡â&Ú×àLÿÿÿì~â&Û×àLÿÿ‚ÿìë'ؼ„nÿÿÿìï&ÙØ¼„ÿÿÿìóâ&ÚØcLÿÿÿì~â&ÛØcLÿÿ‚ÿìë™',„ý¨nÿÿÿìï™&Ù,„ý¨ÿÿÿì0a&Ú,ÿ+þpÿÿÿì~a&Û,ÿ+þpÿÿÿ¤Ÿ'Õx~„ÿÿÿ¥\F&ÜÕx¶ÿÿÿì?&ÝÕ,~ÿÿÿì x&ÞÕ>èÿÿÿ¤Ÿ'Øxx„ÿÿÿ¥\F&ÜØx°ÿÿÿì?&ÝØ,xÿÿÿì x&ÞØ>âÿÿþ (f'ׯ;ÿÿþ >f'×£}¤ÿÿÿìþ>\/&¥× ÿ8ÿÿÿìþ>>/&¦× ÿ8ÿÿþ (f'ÔŠ;ÿÿþ >f'Ô&ÿç¤ÿÿÿìÿ8\/&¥Ôÿ8ÿÿÿìÿ8>/&¦Ôÿ8ÿÿþ (f'Ö––;ÿÿþ >f'Ö2¤ÿÿÿìþ>\/&¥Öÿ8ÿÿÿìþ>>/&¦Öÿ8ÿÿþ (f'Ø–¯;ÿÿþ >f'Ø2¤ÿÿÿìþ>\/&¥Øÿ8ÿÿÿìþ>>/&¦Øÿ8ÿÿÿ«þ „°'Õô ?ÿÿÿ«þ ~°&°Õô ÿÿÿ«þ Á/&?,¼þ>ÿÿÿ«þ ~/&°,¼þ>ÿÿ‚ÿ§)‡‚ÿ§Ù7%#"'$47332767654'&54767;#"'&/œc©Í·À€þ¶?¸AËh—¸Âž#6þÊ2 #déý­GG&+@X„A:g!axLQÿÿÿ«þ 6r'_ÿôþ>ðÿì‡X %+53276=3+HZ#c,1¸VV¸,1jÙÙ»ÿì~X%+53276=3;#"+M˜Z#c,1¸1,c7n–VV¸,1jÙÙj1,¸ÿÿ‚þóÀJRÿÿ‚þð¿òÿÿÿìþÔóX&YÔcþÔÿÿÿìþÔ~X&ZÔcþÔüpn"56$3çþ=æìÅßÔgi~wun52&$ßÅìæþ=Ôšuw~igý* '/&'&#"#67632O,$e5F¢qpÈ[?8WH7  $0G‡JI  Ö'327673#"'&'O,$a9G¢qpÈ[?8Wì7  $,K‡JI ÿÿºPšTÿ,ÿÿÿìlš&ITÿ,ÿÿºPýUÿ,i,k ;#"'&=3!1,cK‚Ž\W¸L71,¸\W+ÿÿþPÿöVÿ,ÿÿºPªWÿ,ÿÿÿìlª'Wÿ,Iÿÿ¹PýXÿ,ÿÿÿìlý'Xÿ,IÿÿþèPÿØYÿ,ÿÿÿìþèl¸'Yÿ,IÿÿÿôËdôZÿ,ÿÿÿìlô'Zÿ,Iÿÿá<[ÿ,ÿÿÿìl&I[ÿ,ÿÿ£UÞ/ÿÿÿµ…ƒ'\ÿÂ5ÿÿÿµ…ƒ&\ÿÂÿÿlÃ9']ÿÂ5ÿÿl„9&]ÿÂÿÿÿ«þ @µ']ÿÂþ>Qÿÿÿ«þ 6µ&ð]ÿÂþ>ÿÿlþ Ã'^ÿ5ÿÿlþ „&^ÿÿÿ‚þóÀµ']ÿôþ>Rÿÿ‚þð¿»&ò]XýDÿÿÿìÑç&Y]ÿ+þpÿÿÿì~ç&Z]ÿ+þpÿÿÁy5Á„ 3;#"'&Á¸1,cK‚šPWs¡ûkj1,¸\eÿÿ‚þ¢ë'Ó9þ¢nÿÿþ¢ï&ÙÓ9þ¢ÿÿÿìþÔ‡X&ÚÓàþÔÿÿÿìþÔ~X&ÛÓàþÔÿÿ‹ÿÆ 'Ôú„Pÿÿ‘^&ìÔ^„ÿÿ‚ÿìë 'Ô¼Šnÿÿÿìï &ÙÔ¼Šÿÿÿìóè&ÚÔcRÿÿÿì~è&ÛÔcRÿÿ‚ÿìë'Õ¼Šnÿÿÿìï&ÙÕ¼Šÿÿÿìóâ&ÚÕcRÿÿÿì~â&ÛÕcRÿÿþ (f'Ó;ÿÿþ >f&¤Ó¼ÿÎÿÿÿìþÔ\/&¥Ó þÔÿÿÿìþÔ>/&¦Ó þÔÿÿþ (f;þ >f0%3#"'&'&'!27# '&5767"#"5$3 "(1{R=IrbàJIÁÔ–úþ^©ƒÔ`‰ _Œ‡_Á&Èm3HZ¸¸–dœ²P·ü‰^¤v¸c–àße4)¸?6š [ _wÿì\/&'&'&5672+5327676SSgU´R¡HK¢¬ÜLX¦J‘KÝ£€dãht^¸#4bš4bBP¸H:jVÿì>/);#"'&'+53276767&'&'&5672~ÜAI2hrBVÑ~(;E)‘KÝ£€dãht^eSgU´R¡HK¢ 4bš)N"w¸¨6a.%P¸H:jV#¸ÿÿþ (°'Ó?;ÿÿþ >°&¤Ó?ÿÿÿì\L&¥Ó ¶ÿÿÿì>L&¦Ó ¶ÿÿ}ÿÚR=}ÿÚGR &'3;#"'#"'532767654"9¶ãaRQS,cK‚“a]Ï.-fgsT!"¬#?z™N‰†uIS,¸‚€!&¸* 1p*Dÿÿ}ÿÚ°'ÓE=ÿÿ}ÿÚG°&¬ÓEÿÿÿ«þ b&?ÿ«þ ~&3;#"'!5 767654x¸ I*eK‚‚2Dûþäþ¾0ËÚ# &pgM,¸>þê…—¸€ŠÐ:H~ÿÿÿ«þ b¶'Óq ?ÿÿÿ«þ ~¶&°Óq ÿÿ‚þ îA‚þ GîF%7653323;#"'#"'&''&'#&'$473327676'&/3¸N0%¸@nޏS,cK‚‘vDm% I0Š1_@8‰'T…ÁPx€mþíi¸l“_Qb_y^@@¸$:µ|_ÊÍ2©&þªaS,¸`[ F{Ÿ<³kª>GHö´Îܳ&%0žlŽ}=êœJ<~ÿìÿã î1%+53276=3327653763#"'&'#"'&€+8Lc‚Kc,P¸,+hm,%¸@nޏ\Kf%#?7‰0`DAbH<Š;!.¸,PdÀœ@dczgÂÍ2ª&þªÇq\ =„œ!1(ÿìÿã7î8#"'&'#"'&'+53276=3327653763;#"'ý%#?7‰0`DAbH<)+8Lc‚Kc,P¸,+hm,%¸@nޏS,cK‚‘vD =„œ!1(I;!.¸,PdÀœ@dczgÂÍ2ª&þªaS,¸`Zÿÿ‚þ °'Õâ Aÿÿ‚þ G°&´Õâ ÿÿÿìÿã °&µÕŠ ÿÿÿìÿã7°&¶ÕŠ ÿÿ‚þ æC‚þ áæ<I)"'&5#&'$47332767654'367676;#"/"3276'&'&uþì&4-JXîPx€mþíi¸l“_Qf[¢+!'« (s{l“HX}aº*=RK‚ƒgÌL~¨‘»í»‰%º€›MGHö´Îܳ&%DŠl“Š7(2’l^F"%GéMF ,¸\v7QlÂ?[F‡ÿì2å .327654'&#"!"'&'+53276=36767632Ш‘»íº‹%0LºþìJNA'f¬‚Kc,P¸-e_™KUskº¸ÊælÂ?[F‡ýÉ*#=Џ,PdrNP2†T‰?!'Dì©mxÿìå+8)"'&'+53276=36767632;#"/327654'&#"˜þèJNA'f¬‚Kc,P¸-e_™KUqm¾*=RK‚ƒgÌ਑»íº‹%0L*#=Џ,PdrNP2†T‰?!'DìKH ,¸\vælÂ?[F‡ÿÿ‚þ ¶'Óû Cÿÿ‚þ á¶&¼Óû ÿÿÿì2¶&½ÓÛ ÿÿÿì¶&¾ÓÛ ÿÿÜE¬))5!3%632;#"/%3276'&'&#"@ý¿þ‘o¸Ù\Dui¼*=RK‚ƒgÌýé»í»‰%0Pz±¸\û?c!'EëMF ,¸\v¸?]D‡QxÿìÔ %3276'&'&#")5!3%6329»í»‰%0Pz±uýÁþõ ¸Ù\Duiº¸Ê¸?]D‡Qxþ’¸\û?c!'Eë©mxÿì¤))5!3%632;#"/%3276'&'&#"8ý¿þõ ¸Ù\Dui¼*=RK‚ƒgÌýé»í»‰%0Pz±¸\û?c!'EëMF ,¸\v¸?]D‡QxÿÿÜ'ÓÏREÿÿ¬&ÄÓÏRÿÿÿìÔ&ÅÓÇRÿÿÿì¤&ÆÓÇRÿÿuþ ²*Guþ ²%+! '&7.54762;# '!2764"²˜þÿþ[b»=D}aî_[9^D¶U þöøš)k_ÁÔý1ˆÎþoc¼z’Œ2t*n@00@p[C+ @M¸äkl=žv–8`3$ÿìø*727&'&5763"327%+5<¡ÊK4XÌ}ûÚº>SF7J þ\þ¢²X¸];dŒ}M©‰ÿ4F!¸Å¤¸ÿìð$/%+532767&'&5476762;#""654'îÂÊv`kB;(aD hYîYh MXD=p`vʨ4/gg/¹¹¸($'UZ'-)74--47)-'bM,(¸U __ ÿÿuþ ²F'Ów°Gÿÿuþ ²L&ÌÓ©¶ÿÿÿìøF&ÍÓw°ÿÿÿìðL&ÎÓ£¶ÿÿÿ¤Ÿ'Óõ~„ÿÿÿ¥\L&ÜÓõ¶ÿÿÿì?&ÝÓ©~ÿÿÿì ~&ÞÓ»èÿÿkþG›'ÔR~oÿÿkþ À&ßÔ„kÿÿÿì?&àÔ,~ÿÿÿì ~&áÔ8èÿÿÿÈÇLÿÈÒ!D#"'5327654'&'&7676'&'$54733276763;#"'ÐJ&P DfXRNB8D-<9_¸h$$EB|=Q#!v+6ºú(  %þ¶{{qe›ÿì))5!27654'&54767;#"'&/6þ¶6”-6þÊ2 héý­GG&+@X„A:g!a_¸h$$EB|=Q#!v+6ºú(  %ý?¸+)x.›ÿÿþÈMþÈ#$%653;#"'#"'$&733276N¸1,cK‚pNyþû†UcEþÜ@¸A¦(IPm®I~ãjûkj1,¸3.(±B"[Š\ss~B"5ÿì¯ +5327653¯WPš‚Kc,1¸s²e\¸,1j•ÿìº%+5327653;#"SM˜‚Kc,1¸1,cK‚–VV¸,1j•ûkj1,¸ÿÿŒþ^óNŒþgt5%327654'&'&#"#"'&#4763&547632;#"bzL,5;(.;DÀ …Kµ2KÈxAZ¢M\HTª((&iK‚¯¨*9:X DD(©PNNOþ“m­f7*(”„?$G³C,,¸ÿìÿͦm$%#"'+5326767632%327654'&#"da“În@h t4W‡^Q°[aýð>Ÿ/4(*X.[4fb0¸G1µP8TY¸NE5EK&)Ÿ/4:''5)24fb0¸$#1µP8S§1>,¸ÄE5EX !a%ÿÿ“þµH¶'Ó? Šÿÿ“þ +¼&VÓ?&ÿÿÿì‡è'ÓàRÚÿÿÿì~è'ÓàRÛÿÿ‹ÿÆ ÞP‘^î $&'&'&'3;#"'&'#"'&5476¤ xRo´t¸$8pK‚ZI-&Šœ8:½Ìm*12e CY>)2Ñ'+¨®eO,¸3;I0š­Dÿìÿ½½å-=67654'&#"27&'&5476&'5#"'+5327654'&”$"':Aù4N--0M,Qߨ@(J¯ƒšx’‰«b 41}! @H=.%4-+#%vˆ iEN@TSZ '¹D³ÑÞ49g=ql)¸D%'“i.C!v-‹3j  °;AWE… ¸L9P)8K6(¸œS/V¸L_”+Y‡9›K1\Sÿÿ‚þóÀJR‚þð¿765&'&'&54767632;#"'&#"#"'$4733276L[/,4PT*uW€ ##rpl$-AIq€ÚYh¼uþÛ?¸A¦B³[M•!3!+ (;=A<^†Ä¸¬¥#0{bV` )g™ZZ™rNÿÿ‚þ ÀJ'Ôôþ Rÿÿ‚þ ¿'Ôôþ òÿÿÿìþÔóX&YÔcþÔÿÿÿìþÔ~X&ZÔcþÔÿÿÿ.ÿì¿í&ý\þ–,ÿÿÿ.ÿìÚí&þ\þ–,ÿÿÿåÿì¿£&ý]þ–,ÿÿÿåÿìÚ£&þ]þ–,ÿÿþ ¿&ý^þÈÿÿþ Ú&þ^þÈTÿì¿#"'5327367653¿‹7MÍžjK`Uqˆþ³ª%¸BþÔÚUG´ ¸ ˜FüA+7‰¸®TÿìÚ#"'5327367653;#"'&4;IÊ¡jK`Uqˆþ³ª%¸"@Pi¯f<[A´ ¸ ˜FüA+7‰¸®üD®Tž¸ó)ÿTL* 35'5467676?67654&#">32üüü–Ë,X\"$߸gÁ^a³Olƒ39ZZ8Lüüüý®þþ“{4<+VZ@ELŸÂ89¼CFnY1^5YV‚ešªà‚¨ !5!5!)5!ýþ­S2Sý½þ®RàÈÈÈÈÈÿÿª7‚'ÿXêFÿÿª:‚ 'bé:ÿÿª=‚ 'ÿ]éCÿÿª;‚ 'ê<bÿÿª=‚ 'ÿ]bÿÿª‚H'é€&é'é€éÿÿª‚H'é€'é€'éêÿÿª‚H'þ 'é€'é€éÿÿª‚H'é€&é'éê€ÿÿª‚H'ê€'é€'éêÿÿª‚H'ê€'é'é€þ ÿÿª‚H&é& 'é€éÿÿª‚H&ê& 'é€éÿÿª‚H'þ 'é'造ÿÿª‚H'ê'é€&éé€ÿÿª‚H'ê'é€'é€êÿÿª‚H'þ 'é€'é€êÿÿª‚H&é'ê€'êé€ÿÿª‚H&ê'é€'êê€ÿÿª‚H'þ 'ê€'êé€ÿÿª‚H& 'é€'êéÿÿª‚H& &ê'é€êÿÿª‚H& 'é€'êþ ÿÿª‚H' 'é€&éé€ÿÿª‚H&ê'é€'é€ ÿÿª‚H'þ ' 'é€é€ÿÿª‚H'ê€'é€&é ÿÿª‚H' &ê'é€ê€ÿÿª‚H' 'þ 'é€ê€ÿÿª‚H' 'é€& éÿÿª‚H' 'é€& êÿÿª‚H' & 'é€þ ÿÿª‚H'ê€'é'é€éÿÿª‚H'ê€&ê'éé€ÿÿª‚H'ê€'é'é€þ ÿÿª‚H'ê€'ê€'ééÿÿª‚H'ê€&ê'éê€ÿÿª‚H'ê€'þ 'éê€ÿÿª‚H'ê€'é& éÿÿª‚H'ê€'é& êÿÿª‚H'ê€& 'éþ ÿÿª‚H'ê€&é'é€êÿÿª‚H'ê€&ê'é€êÿÿª‚H'ê€'ê'é€þ ÿÿª‚H'ê€'ê'ê€éÿÿª‚H'ê€'ê€'êêÿÿª‚H'ê€'ê'ê€þ ÿÿª‚H'ê€&é'ê ÿÿª‚H'ê€'ê&ê ÿÿª‚H'ê€'þ 'ê ÿÿª‚H'ê€&é'é€ ÿÿª‚H'ê€' 'é€êÿÿª‚H'ê€'é€' þ ÿÿª‚H'ê€' &éê€ÿÿª‚H'ê€'ê€&ê ÿÿª‚H'ê€'ê€'þ  ÿÿª‚H'ê€&é&  ÿÿª‚H'ê€&ê&  ÿÿª‚H'ê€'þ &  ÿÿª‚H' &é'é€éÿÿª‚H' 'é€'éêÿÿª‚H' 'þ 'é€éÿÿª‚H' &é'éê€ÿÿª‚H' 'ê€'éêÿÿª‚H' 'ê€'éþ ÿÿª‚H' &é& éÿÿª‚H' &ê& éÿÿª‚H' 'þ 'é ÿÿª‚H' 'ê'é€éÿÿª‚H' 'ê'é€êÿÿª‚H' 'þ 'é€êÿÿª‚H' &é'ê€êÿÿª‚H' &ê'êê€ÿÿª‚H' 'þ 'ê€êÿÿª‚H' & 'êéÿÿª‚H' & &êêÿÿª‚H' & 'êþ ÿÿª‚H' ' 'é€éÿÿª‚H' &ê'é€ ÿÿª‚H' 'þ ' é€ÿÿª‚H' 'ê€&é ÿÿª‚H' ' &êê€ÿÿª‚H' ' 'þ ê€ÿÿª‚H' ' & éÿÿª‚H' ' & êÿÿª‚H' ' & þ ÜÕ  3%!#!! !þÍîþîY9w™ý€˜þ\{þå8qúó dú+¡þ_ü÷ÉNÕ  %*!2#!327&#363&#!3654/654'ÉfçúÀþþðûý†ê;33;êÖ$ $ýþÊ#ÐÐ>ÐÐÕÀ±å]aþáÈÚéý{wˆýÜúó ûDÜåD–6ÍÄ6è3Õ! )327&#!36'è²–þhþPýý’cõ  öþpÊÆßßÕþ—þ€þ~þ–qúóA‹Aúó ûk•{qœÉSÕ3%!!!!!!-ÊþÒxýÇý9øûvqúó ddýädýsdÉìÕ !!!!!#3É#ýoQý¯þn.ÊÊÕdýädýqúósÿã‹ð&&$#"32767!5!# !2dþü…»‡‡»‘eVþRuþæ þ¢þu‹^’oüR©©äaG@û;@&5çdýSU™mn™HFûcI¯þºþ»¯f¾Õ3%!#3!53#.ÊþnXddý¨ddqúó ddúódd ÿ–þfYÕ6765%!#!53265-V?OþÔÍãþí?†nqú±òd J^ê dú“þòôd–ÂÉ0Õ !3 #!3ÉŸü£’™üÂþpdÊÕý‰wý@üëÏý1qúó É2Õ !!!3É’×û—dÊÕúdoúó ÄoÕ !#!! !3!3Ýþ_GþbþnØ}×þÒÊúÊqû®RúÕüøú+qúó úó rÿãÚð'( ! '&76 7& 676'&&:¼¾½½þÆþż½½¼Glllþðli$ ›› Þ#››ûÌðÒÕþ þ¡ÔÓÓÒabÓÒúŽ22×22ûjT%¶ýœµ%5û¯$¶c¶$ýôˆÿÜŸö-6&/.4%&  %5 64&/.$ Pdˆ¬o©¨&œªn°žÎþÅ¢m”nÊÉþàþþûßg©zŠoÊ·Æ-[þÊš)'¹þÿNXd»''ã´pu‰éi$2ëþXîf|•’ý† /ÏŒôÿú°Õ 3%!!!!rÈüÀ¶ýîþpýìqúó ddúq±ÿãóÕ $!&%! 65! ÆX!û†!YþÓ‘€ €‘þ¬ýfþ¬qü™þæ‚ba@üÀþž`|gdü5ðÓÓðËü\þÜþÖ*$·Õ 3%! 3!šîÈþþ®ÙÚdýÇþÌqúó dûéú+D®Õ 3!3%! ! 3! !ÄDÈþ¼û5DÈþ¼þ»:9:9dþ‰þ|þÅþÂþ€qúó úó dûîûîú+úðlÕ 3%! 3 ! #(\Æü¤þ~¸v„bþL:þHþŠþ|d¶qúó dýÎ2ý„ü§2ýÎ{ÿü¬Õ 3!! #3nÚdýðþpýð”ªþ2ÈÌÊÂüòý9ÇüòªýVý{ÿãÅ{",34&'3!5#"&5463!54&#"5>3 5!">76a–=Kˆdþ°?¼ˆ¬Ëýû§—`¶Twßjþ°þþ6/^”;:þ5CˆzÓ†]ˆýYýªfaÁ¢½ÀH‹..t''üþ‹UýíNHGÜýg‹wt”ÿã-!>32#"&'!4'&'676763&#"327”N:±|ËÿÿË|±:þ²^,<<,9üR†ù¥KM_]¥ý¢daþ¼ýðþ¼ad¨tŸ‚= üz =OsKÚú¬TþdihtJžŸ‚‚qÿãç{#%#"!2&'&#"3276%çM¥]ýþÖ-U¢LEmGJXHCQRHVþ,${z$d$$>:##düWS%€êæƒ&”ÿã-!!5#"323327654'&'&#"ÅNþ²:±|ËÿÿË|±ýv9,<<,^(†ý¥]_MK¥¶^ùì¨daDDaþZžKsO= † =‚ú¬Tþdü6‚‚ŸžJthioÿã}{!327# 32!.#"}ý‹K_ÙÃmÃkþôþÇ)üåý#ÆÅiÌ©J@þb]u-)8 CþÚ÷qzþÛþóŽÓ¾ç/ã 3476%#"!!!#5354763g.9‡ñ®”:9„þ|þ±°°WX¼® -8‘û¶J_D8‹d97’ddüædd¼TVqþVð{#.=65326=#"325!!"&32767654'&#"jëülQžRµ´:±|ËÿÿË|±:NþÖþÍrÊy^,<<,9/¥KM_]¥úü=ÊŒoú—€,*½¿qdaDDad¨ü-þâþéwŸ‚= † =OsKýÂihtJžŸ‚‚ºH "34'&3'!>32!4&#"! GŒS5ü‡ëOIÆÔÛþ¬kk€•þ± h¾ý”@Á[:¶ú´Ldýžedïèý\Пž¾¤ýUæ5 33#!!J‡ ¹¹KOþ±üüh˜éËû ÿ×þV #676#532765!3#¨‡%G(=ôÝ1l$%OQRa¸¸ûеT0Hôd01™¬ûŒÖ``¾éº2 !3 #!3ºO„ŒýHÑ–ýmþ±d‡üiãýöýª#ýݰú´Læê&5#"'&5!3J=(G%‡õ¶RQOLi” H0Tµ0úZ``Ö~ûJœ^dº‡{"&1<!>32>32!4&#"!4&#"!3%34'&%34'&ºOIÆÔTÞÔÛþ¬kk€•þ¬kk€•þ±d‡[ GŒS5ã GŒS5`®ed¬J€vïèý\Пž¾«ý\Пž¾¤ýUüüh˜ h¾ý”@Á[: h¾ý”@Á[:ºH{ "34'&%3'!>32!4&#"! GŒS5ü‡ëOIÆÔÛþ¬kk€•þ± h¾ý”@Á[:üh˜d®edïèý\Пž¾¤ýUqÿãu{ #2#"27&"676'&sðþîðñþï‚3x33x3d4'pp'Ú3(pp({þÈþìþíþÇ98û× º ükp-€þ$€-Rü’-Û€-qþVð{-%!!>32#"&4'&'&'676#&#"32¿þ²N:±|ËÿÿË|±Š9,<<,^ü؆¥]_MK¥¨ý®¨daþ¼ýðþ¼a¦žKsO= üz =‚ýoHú¸Ê‚‚ŸžJthiqþVð{-%!!.#"326476767&'&3632#"¢Nþ²:±|ËÿÿË|±ýv9,<<,^(†ý¥]_MK¥¨ý®¨daþ¼ýðþ¼a¦žKsO= üz =‚ýoHú¸Ê‚‚ŸžJthiºß{3'!>32.#"!†êN:º…4I,œ§þ²üüh˜d®fc…˾ýzoÿãÇ{E67654'&/&'&5432654&/.54632.#"#"&'i'K&'q4¥=B%%U+.„39GÓS OjqL‘4vàÎf´LJ\_opPx3¡„÷ØZÃlù=vf03"3;@{R?Bsl37'*7CoTüþ78^UNO,,—ˆ¦µ z1$YXDL#/ž¤À%%7ˆž%&7#!!;!"&5#53Ð*‡\{þ…Ks½þ­Õ¢‡‡jU|ÿü7­N(þ¬dýU‰NdŸÒudT±ÿåD` "%&'&5##!5#"&5!3265!ù GŒS5C‡ëþ¬IÆÔÛTkk€•TS h¾lýÀÁ[:˜ühd®edïè¤ý0Ÿž¾¤«=±` 3%! 3!ÍYƒþ£þñT^^dþ\þÔüüh˜düT¬û Vò`3!3%!!3! !ñð‡òübð‡òþùTæåNæådþÛþÊñòþÇüüh˜üh˜dü–jü–jû –üjL` 3%! 3 ! #«—ýUþ¢|þ¥åþpþãþã|[üüh˜dþþ-ýsþÓ=þVÃ`7%! 3+53267Ò²>þ•þæ^]_lþP’|““XQ+üûÛ™Œdü—iû8Ç{dCYXb` 3%!!!5!\ývŒŠü‰ñýw‰ûö‰ýüüh˜ddühdd˜ÿÿhÕ$ÿÿÉìÕ%ÿÿsÿã'ð&ÿÿɰÕ'ÿÿÉ‹Õ(ÿÿÉ#Õ)ÿÿsÿã‹ð*ÿÿÉ;Õ+ÿÿÉ“Õ,ÿÿÿ–þf“Õ-ÿÿÉjÕ.ÿÿÉjÕ/ÿÿÉÕ0ÿÿÉ3Õ1ÿÿsÿãÙð2ÿÿÉÕ3ÿÿsþøÙð4ÿÿÉTÕ5ÿÿ‡ÿã¢ð6ÿÿÿúéÕ7ÿÿ²ÿã)Õ8ÿÿhÕ9ÿÿD¦Õ:ÿÿ=;Õ;ÿÿÿüçÕ<ÿÿ\Õ=ÿÿ{ÿã-{Dÿÿºÿã¤Eÿÿqÿãç{FÿÿqÿãZGÿÿqÿã{Hÿÿ/øIÿÿqþVZ{JÿÿºdKÿÿÁyLÿÿÿÛþVyMÿÿºœNÿÿÁyOÿÿº{Pÿÿºd{Qÿÿqÿãu{RÿÿºþV¤{SÿÿqþVZ{TÿÿºJ{UÿÿoÿãÇ{Vÿÿ7òžWÿÿ®ÿãX`Xÿÿ=`YÿÿV5`Zÿÿ;y`[ÿÿ=þV`\ÿÿXÛ`]ÿÿ‡ÿãðÿÿáZÕÿÿ–Jðÿÿœÿãsðÿÿd¤ÕÿÿžÿãdÕÿÿÿã–ðÿÿ¨hÕÿÿ‹ÿã‹ðÿÿÿã‡ðÿ¹šÇ @ÔÌ03#Çu™þˆü×ÿ)Ù¥@ ÎddÔüÜì1Ô<ì20K°TK°T[X½@ÿÀ878YK°TK° T[K°T[X½ÿÀ@878YK°TK°T[X½@ÿÀ878YK°TX½ÿÀ@878Y@````pppp]3#%3#þ^ËËþyËËÙËËËýsîþðö@BúÄÀ1ôÌ0KSXÉÉY"K° TX½ÿÀ@878YK°TX½@ÿÀ878Y@ %%6FVjg //]]3#þ7¹ä™öþøü¶ÿJéu@!  ÃÃúVV ÔìÔì99991ô<ìÔì2990K° TX½ÿÀ@878YK°TX½@ÿÀ878Y´ ]'.#"#4632326=3#"&ýü9 $(}gV$=09" (}gT";9! 2-ev 3)dwý îþ‹ö‰@BúÄÀ1ôÌ0KSXÉÉY"K° TX½ÿÀ@878YK°TX½@ÿÀ878Y@*$$5CUUŸŸ¯¯//]]#ýÇÄ™æöþøüÏîÿ1øw@ úÔÄ91ô<Ä90K° TX½ÿÀ@878YK°TX½@ÿÀ878YK°TX½ÿÀ@878Y@ //- ]3#'#ý¢¼Ó‹¦¦‹øþö²²üÏîÿ1ø†@ úÔÄ91ôÄ290K° TK° T[K° T[K° T[X½ÿÀ@878YK°TX½@ÿÀ878YK°TX½ÿÀ@878Y@ "  ]373ý¢Ó‹¦¦‹Óî ²²þöüÇÿ9ø #.#"#>32Çv cSRav  Ÿ6978w{züÇÿ9ø j@à úVVÔìÔì1ôüÌ20K° TX½ÿÀ@878YK°TX½@ÿÀ878YK°TK°T[X½ÿÀ@878Y332673#"&üÇv cSRav  Ÿø6978w{zýšþfÛG·ÎdÔì1Ôì0K°TK°T[X½@ÿÀ878YK°TX½ÿÀ@878Y3#ýšÌÌÛÍüæîÿ²ö@Ì1Ô<Ì203#3#þù¹ä™‹¹ä™öþøþøüNîÿö#!#ýÄ™äÄ™äöþøþø––53#–––––73#'3#ú––ú–––––– 3#3#'3#}––}––ú–––d–––ÿ– 3#3#'3#}––}––ú––d––––ÿ––3#3#––––d––ÿ– 3#3#3#3#ú––––ú––––d––d––ÿìï&;#"'&'#"'$&733$767654'3ïF??7Kÿÿÿì‡XYÿÿÿì~XZÿ¥\,>%!$'$&73!2%7&'&547676323!!"'654'&'&#"xhnþèþðþÏ}þ·@¸AËQ+è"R¼:4RQ‰P þôioh4"(=)1$+<'gŠ\^ˆsM6,|y$›K2S–%j¸ßAzG' <8BNÿì?Ù0654'&323276767'&54767632#!V)B,4((7(*Hý®ñT—O<?a‚Nb–NLZB`.NJ|m‘þ¿+M;3*)3P&þ· ]027EW4,”E$2Hf3ŒÐˆ,'ÿì !5;#"'+5327&'&54767632"67654'&'&f·$'“Ùˆ¹¹ˆÙ“'$¹A??8 D?$ 9úP–2*I1C2¸99¸(M.L,0W¸ 5+5DE2.4! kþ Àä.@%&'&'&547676323!!#'$'&5473!2766'&'&#"¸B.y9“(“)Wp8c2þô0-=€ž¯ÍþŽ^E>¸><Œ–Õl/"'"3 9Ld/  #+m¹=¥E2X‘:Ö¸zF‘NV¾Š}¦`k›L:‚DbZzWK# :<,; ÿÿÿì?ÙÝÿÿÿì Þÿÿÿì‡è&ÚÓàRÿÿÿì~è&ÛÓàR¶· %4'&"2>"'&4762<R8R8z?@¸?@@?¸@Ü(8)*8…¸@@@@¸@??ÿÿþï 'Ô¼Š'ä©þÙÿÿÿìþôè&Ú'ÔcRäPþôÿÿÿìþô~è&Û'ÔcRäPþôÿÿÿìï¶'Ö¼ Ùÿÿÿìóâ&ÚÖcLÿÿÿì~â&ÛÖcLÿÿþ >¯&¤]Šÿ8ÿÿÿì\K&¥]XþÔÿÿÿì>K&¦]XþÔÿÿþ >ª&¤×?ÿÿÿì\F&¥× °ÿÿÿì>F&¦× °ÿÿþ >ª&¤ÕÂÿÿÿì\F&¥Õ¶ÿÿÿì>F&¦Õ¶ÿÿþ >°&¤'Ó?Öçÿÿÿìþ>\L&¥'Öÿ8Ó ¶ÿÿÿìþ>>L&¦'Öœÿ8Ó ¶3_+ 5_ü¡_•þž–bÕþV'ÕJ@!B • ¯ 6Üìüì99Ì1/<ì299Üì0KSXííY"²]33+532765#Õ¸â¸RQµþéi&&ý¸Õûƒ}úÖ``œ01­}ûƒ` 2@ ©¼ F ü<<ì221/äÜ<ì20@  @ P ` p ]33###53ø¤¤¸´´`þ¤þ<ĤþòþV.` 54!333##"3276!þÑÁ¸µ¿5R µw{i&þVÝÍ`û ›p?`3A0ÿÿ’cè3'q½=ȼUÿÿ’aè4'q½ÿȼ[ÿÿ’^è3'мPq½=ÿÿ“cåZ'ÉðdȼUÿÿ“cåZ'ËŒdȼUÿÿ’aèZ'Éðdq½ÿÿÿ’aèZ'ËŒdq½ÿÿÿvj 3'ÊÀ\q½=ÿÿ‹cíZ'ͼbȼUÿÿvj V'ȼ}ÊÀ\ÿÿ‹cíW'ͼuм|ÿÿvj0Z'É@dÊÀ\ÿÿ‹c:'̼uÉ(Dÿÿ‹cm:'ËâD̼uÿÿvc u'̼uÊÀŒÿÿvV Y'ϼPÊÀpÿÿƒVõZ'ϼPÉðdÿÿƒVõZ'ϼPËŒdÿÿƒVõ‹'º«Ï¼Pÿÿ‹c['ºà̼uÿÿüîÿPj&²¸þøZ,,!!,þÔ,þÔÿãOÕ=32653#"&[ÂhqÊÓ÷`¾=ìQQ•Ëîüþæê,ÿÿÿ«þ &&_ùÿ«þ ö&3;#"'!5 767654x¸ I*eÃú‚2Dûþäþ¾0ËÚ# &pgM,¸>þê…—¸€ŠÐ:H~ÿÿþÈ#Ä'_¼àÿÿÿì`Ä'_ÿSáÿÿÿìºÄ'_ÿSâÿÿ‚þð¿F'_¼ýòÿÿÿìþÔ8@'_ÿ+þ 'ÔcþÔYÿÿÿìþÔ~@'_ÿ+þ 'ÔcþÔZÿÿÿ«þ ~r'_¼þ>°Á9 9µF üìÄK°SK°QZX¹ÿÀ8Y1´—‡/ìä0@  @ P ` p ð ]3;#"&5Á¸Li µ£û‚™aœÀÖÿÿ‘^îìÿÿqÿã%ðäÿÿqÿãqåÿÿ–þu `&óÿJOw`73#!!dž²†$þ²ÚNd˜ü`ÿþVw`#676#732767!5ʆÐ#5H2KþÜÜ1i0/éNÞ)deûеT0Hôd01™¬ûŒÖ``ÿÿÿÓþvg`'°ÿóÿÿ·þ‡ƒ`&óÔðØÃû3#3#ðÓÓÓÓÖþ#þ—öÕ !#3!53#—^ÉÊý¢ÉÊÕªûªª­• ?!5 ü¤?ªªÿÿÈË8ò'TXZÿÿÈË8 U'UXZÿÿȺ8è'ZôTÿÿÈË8'WXZÿÿÈË8 U'XXZÿÿȺ8 'Z,WÿÿÜ¿$ž'Wô]ÿÿÜ¿$ñ'Xô]ÁNÕ@ T¯üüüÄ1/ìì0333Á¸ÕÕú¾“ÿìNÕ@T¯ ÄüüüÄ1/ìì20%3!533yÕýžÕ¸“““BÿìyÕ@ T¯Äüüì1/ìì0)533yþsÕ¸“Bqÿ㦓8@ E EôìÔ¶0 ]99ìüÄ1@    Œ /ìôìÔì0 6& #" 3 ¹¹¹þú*NYÃþë†hí>þú¹¹¹ý †þëé“ÿìÿ㦓A@E E ÄôìÔ¶0 ]9ìüÄ1@     Œ  /<ì2ôìÔì0 6& "'!53&54 3 ¹¹¹þú*N²NþJíh†hí>þú¹¹¹ý “©Ãþëé“ÿìÿã!“8@ E EÄôìÔ¶0 ]99ìì1@    Œ /ìôìÔì0 6& &54 #"'!5 ¹¹¹þúíh†þëÃYNþJ>þú¹¹¹ýœ©Ãþëþzþë“=xÙ 4@   ÜÄ2üÄ2ÜÄ91@  ‡/ìì290)33!xýÞþç³³Âþçj*¯þ]£ýQýiÿìxÙ 6@   ÄÜÄ2üÄ2ÜÄ91@    ‡/ì2ì290%!5!33xütjþç³³Âþç“““—¯þ]£ýQýiÿì'Ù 4@     ÄÜÄ2üÄ2Ì91@   ‡ /ìì290#5!33jþç³³Âþç“—¯þ]£ýQüÖ=Ðq) #33Ðþìþ¢þ¢Ã¤úmˆ½ýCqý"ÿìÐq )5333!þìˆmúmˆþìþ¢“Þý"“½ÿìq)533#þìˆmú¤Ãþ¢“Þü½ºOq $@  üìÔüüÄ1¶£‡/ì2ô<0)3!33Oûk¹U¹Îqý"Þý"ÿìOq (@   ÄüìÔüüÄ1·£ ‡/ì22ô<0)533!33OúιU¹Î“Þý"Þý"ÿìq $@  ÄüìÔüì1¶£‡/ì2ô<0)533!3ûkιU¹“Þý"ÞºOq $@   ü<ÔüüÄ1· £ /ì2ôì035!!5!3ºüòÇΓK“ý"“ÿìOq $@   ÄüÔüüÄ1· £ /ì2ôì0#5!!5!3ÜüòÇΓK“ý"“ÿìq @ ÄüÔüì1¶ £ /ìôì0!5!!5ûkÜüòqü“K“qÔâ!&'.4> !2>4."RJr……ä 惃sKRü9[œ¸ZZ¸œ 1¨Å©bb©Å¨1 ý…“­p`88`p`88ÿìÔâ!#5!&'.4> !2>4."RJr……ä 惃sKRü9[œ¸ZZ¸œ“{ 1¨Å©bb©Å¨1 ý…“­p`88`p`88ÿìOâ#5!&'.4> 2>4."RJr……ä 惃sKRþQ[œ¸ZZ¸œ“{ 1¨Å©bb©Å¨1 üò­p`88`p`88ºO¦ &@    ÔÄüÜÌÌ21¶   /ìÜÜÜÌ03"3#!5!ò„…ñ>ûk•œ fšžüø“ÿìO¦ "·  ÄÔÄÜÌÌ21¶   /ìÜÜÜÌ03"3#!5!ò„…ñ>úcœ fšžüø“ÿì¦ $@   ÄÜüÜÌÌ21¶   /ìÜÜÜÌ03"3#!5!ò„…ñpûk•œ fšžüø“qµî7@ EüìÔ<ì2ÔüÜÄ1@    ‘ /ìôìÔ<ì2Ì0!!##"&6 !354'&"3.þCœø¼°±f^‹vÎü ]8Æmr^ÞþÄ<ÖUåf˜¶Éý"“qɃ]8˜Æƒÿìµî;@! E ÄüìÔ<ì2ÔüÜÄ1@   ‘ /ì2ôìÔ<ì2Ì0%!##"&6 !3!554'&"3.þCœø¼°±f^‹vÎù7é]8Æmr^“KþÄ<ÖUåf˜¶Éý"““ÞɃ]8˜Æƒÿìçî7@ EÄüìÔ<ì2ÔüÌ1@    ‘ /ìôìÔ<ì2Ì0%!##"&6 !!554'&"3.þCœø¼°±f^‹vúé]8Æmr^“KþÄ<ÖUåf˜¶Éü“ÞɃ]8˜ÆƒÁ•Õ ,@   üü<ü<ÜÄ1@   ¯  /ìôìÜì03!!!!!Áƒþ5Ëþ5Õ“þ/’ý´“ÿì•Õ 0@   Äüü<ü<ÜÄ1@    ¯  /ì2ôìÜì0#53!!!!!Õƒþ5Ëþ5“B“þ/’ý´“ÿìDÕ ,@    Äüü<ü<Ì1@    ¯ /ìôìÜì0)53!!!!yþsÕƒþ5Ëþ5“B“þ/’=•Õ ,@  Ü<ü<üüÄ1@   ¯  /ìôìÜì0!!5!!5!3þ5Ëþ5ƒÕß’Ñ“ú¾“ÿì•Õ 0@  ÄÜ<ü<üüÄ1@    ¯  /ì2ôìÜì0#5!!5!!5!3þ5Ëþ5ƒÕ“L’Ñ“ú¾“ÿìÀÕ ,@    ÄÜ<ü<üì1@    ¯ /ìôìÜì0)5!!5!!5!Àý,þ5Ëþ5ƒ“L’Ñ“ºŒÕ *@  üü<ÔìüÄ1@     ì2ìÔì0!!27654'&3!23œþÐ,R4,,=ýÙ¹UiXOÓÞýµ]Oz}I_ý"Õýœ‡_Ò¤‚“ÿìŒÕ.@  Äüü<ÔìüÄ1@    /ì22ìÔì0#533!23!!27654'&ιUiXOÓþþÐ,R4,,=“Býœ‡_Ò¤‚“Þýµ]Oz}I_ÿìÕ *@  Äüü<Ôìì1@     /ì2ìÔì0!!27654'&533!2#œþÐ,R4,,=ý ιUiXXXlÞýµ]Oz}I_ý"“Býœ‡_Ò­‡…ÁùÕ@@  ôܲ_]9üÔüÄ@    /ìôì999@  ìì10!4'&'5!!ª5M‰Ãcý™ˆ4B˜™_–ÜÜ9V“þïê¸æœ@9“ÿìùÕD@   Äôܲ_]9üÔüÄ@     /ì2ôì999@  ìì10#5!&'&'&'5!!» 5M‰Ãcý™ˆ4B˜“X]°–ÜÜ9V“þïê¸æœ@9“ÿì$Õ@@   Äôܲ_]9üÔì@     /ìôì999@  ìì10#5!&'&'&'5!» 5M‰Ãcý™ˆ4B “X]°–ÜÜ9V“þïê¸æþÉq=áÕ:@   ÔÜü9ÜÄ1@  /ìô̲]ìì´]0!533TþéºÕßþÇö9ú¾“ÿìáÕ >@  ÄÔÜü9ÜÄ1@  /ì2ô̲]ìì´]0#5!533hþéºÕ“LþÇö9ú¾“ÿì Õ:@  ÄÔÜü9Ì1@  /ìô̲]ìì´]0#5!53hþ麓LþÇö9ú+ÁÀ#1@%!$üìÔìÔüüÄ1@    # /ì22ÔìÔì03432>3234&#"!4&#"!ÁôÆ}x5%–ÓÒ^ˆÕþq¤ZþýH¤Zl”þî¦ÿK--ÒX€hý¹“|‚€Å•þncƒ§ÒòÿìÀ%5@'#&ÄüìÔìÔüüÄ1@    $ /ì222ÔìÔì0#53432>3234&#"!4&#"!ÕôÆ}x5%–ÓÒ^ˆÕþq¤ZþýH¤Zl”þ“[¦ÿK--ÒX€hý¹“|‚€Å•þncƒ§Òòÿìë#1@%!$ÄüìÔìÔüì1@    " /ì22ÔìÔì0#53432>324&#"!4&#"!ÕôÆ}x5%–ÓÒ^ˆº¤ZþýH¤Zl”þ“[¦ÿK--ÒX€hý&|‚€Å•þncƒ§Òò=ÜÕ -@   Üü<ü<üÜÄ1@    /ìüÜ<ü<0!!5!3!!!ˆþµK¸Kþµœ“Áþ?“ý“ÿìÜÕ1@   ÄÜü<ü<üÌÄ1@    /ì2ôÜ<ü<0#5!!5!3!!!œþµK¸Kþµœ“î“Áþ?“ý“ÿì‹Õ -@   ÄÜü<ü<üÌ1@    /ìôÜ<ü<0)5!!5!3!!@ý¬œþµK¸Kþµ“î“Áþ?“=XÕ>@ Üü<ü<Ü<ü<üÜÄ1@    /ì2ô<Ü<<ì220%!!5!3!3!!!þ=¹þµK¹øLþ´œ“îý““Áþ?Áþ?“ý“ÿìXÕB@  ÄÜü<ü<Ü<ü<üÜÄ1@    /ì22ô<Ü<<ì220#5!!5!3!3!!!%!œþµK¹øLþ´œý¬þ=“î“Áþ?Áþ?“ý““îýÿìÕ>@  ÄÜü<ü<Ü<ü<üÌ1@    /ì2ô<Ü<<ü<<0)5!!5!3!3!!!¼û0œþµK¹øLþ´¸þ=“î“Áþ?Áþ?“ýîýºOq %@   üìÔüüÄ1· £ /ì2ôì03!3!ºÇÎü$Uqý"“ÞýµKÿìOq *@    ÄüìÔüüÄ1@  £  /ì22ôì0#53!3!ÎÇÎü$U“Þý"“ÞýµKÿìq %@  ÄüìÔüì1·  £ /ì2ôì0)53!!ûkÎÇüòU“Þ“ýµK=Õ!!!ÆýtFsþ0†þhéBû~ÀÕú¾“ÿìÕ !5!!!ÆýtFýléþh†þhéBû~À“Bú¾“ÿìÃÕ!5!!ÆýtFýléþh†þ0Bû~À“Bú+ÁóÕ 8@!  üìÔ<ü<ÔìüÄ1@       /ì2ôìÜ<ì20327654'&+!!!2/!m¨]%i¤þ ;þ ô°„@ED\ŒÔ†qQE=4."RJr…CEoJRþµXEr„„rJSü9[œ¸ZZ¸œ† 1¦ÆSV/ ó“þ{ 2¦Æ¦1 ò“"p_88_p`88ÿìÔÕ*#5!5&'.4767675!5!!2>4."RJr…CEoJRþµXEr„„rJSü9[œ¸ZZ¸œ“ó 1¦ÆSV/ ó“þ{ 2¦Æ¦1 ò“"p_88_p`88ÿìOÕ(#5!5&'.4767675!5!2>4."RJr…CEoJRþµXEr„„rJSþQ[œ¸ZZ¸œ“ó 1¦ÆSV/ ó“þ{ 2¦Æ¦1 þ{"p_88_p`88ÁQÕ %@   üìÔìüÄ1·  /ìôüÌ0!!#!3Åþµ¹¼ÔBþÅÎú¾“ÿìQÕ *@  ÄüìÔìüÄ1@    /ì2ôüÌ0#5!!#!3Ùþµ¹¼Ô“¯þÅÎú¾“ÿì}Õ %@   ÄüìÔìì1·  /ìôüÌ0#5!!#!Ùþµ¹¼“¯þÅÎú+ÁQæ (@   üü<ÜüüÄ1·   /ìÔÌüÌ0!!#3!3Åþµ¹¹ÔOþü›þüü±“ÿìQæ -@   Äüü<ÜüüÄ1@     /ì2ÔÌüÌ0#5!!#3!3Ùþµ¹¹Ô“¼þü›þüü±“ÿì}æ (@    Äüü<Üüì1·   /ìÔÌüÌ0#5!!#3!Ùþµ¹¹“¼þü›þüüÁBÕ /@   ü<ìÜ<üüÄ1@      /ì2ôìÔì0!!!5!3z;þ ôþ ¬ÕÞýµKý"qÑ“ú¾“ÿìBÕ3@   Äü<ìÜ<üüÄ1@      /ì22ôìÔì0!53!!5!3z;ý7Õôþ ¬ÕÞýµKý"“ÞÑ“ú¾“ÿìmÕ /@    Äü<ìÜ<üì1@      /ì2ôìÔì0!53!!5!z;ý7Õôþ ¬ÞýµKý"“ÞÑ“ú+qÔÕ &2>4."&'.4767673! [œ¸ZZ¸œœRJr…CEoJR¸XEr„„rJS"p_88_p`88ü~† 1¦ÆSV/ †þ{ 2¦Æ¦1 ò“ÿìÔÕ (2>4."!5!5&'.4767673 [œ¸ZZ¸œlûRJr…CEoJR¸XEr„„rJS"p_88_p`88ý““ó 1¦ÆSV/ †þ{ 2¦Æ¦1 òÿìOÕ &2>4."5&'.4767673!5 [œ¸ZZ¸œœRJr…CEoJR¸XEr„„rJSý0"p_88_p`88ýó 1¦ÆSV/ †þ{ 2¦Æ¦1 þ{“qÔÕ*!&'.4767675!5!!!2>4."RJr…CEoJRþµNþµXEr„„rJSü9[œ¸ZZ¸œ† 1¦ÆSV/ ó““ò 2¦Æ¦1 ò“"p_88_p`88ÿìÔÕ ,%!5!5&'.4767675!5!!2>4."ÔûRJr…CEoJRþµNþµXEr„„rJSþQ[œ¸ZZ¸œ“““ó 1¦ÆSV/ ó““ò 2¦Æ¦1 òp_88_p`88ÿìOÕ*)5!5&'.4767675!5!!2>4."¼ý0RJr…CEoJRþµNþµXEr„„rJSþQ[œ¸ZZ¸œ“ó 1¦ÆSV/ ó““ò 2¦Æ¦1 p_88_p`88ÿÿüìûÿ œûþÿÿÿª†‚Õ'é†'éÊé ÿÿª†ƒÕ'é†'éÉê ÿÿª†‚Õ'é†'êÉé ÿÿª†‚Õ'é†'êÉê ÿÿª†‚Õ'ê†'éÉé ÿÿª†‚Õ'ê†'éÉê ÿÿª†‚Õ'ê†'êÉé ÿÿª†‚Õ'ê†'êÉê —à :@   Ô@ ? o ]Ü99ÔÜ99ÔÌ991¶ ÔÌ2Ü<Ì0#'##'##'d22¯222¯22dàÈddddÈÉþVüÕ!#!3!3#3ÄýjþðÄ–Éþ’†áûÕûáúÕªþVÁþV8`!##333#€·ýäì·í¸þÞ{ƒü}`üü9™þVÿã¬Ó$36767#"&546?>7>5#53ó¿!Ya^Ág¸ßHZX/'ÅËËÅ-93B$BüS #C¼98ŸL‰VV/5<4þú¾­,5^1Y7=pöãM_<õÃú²Ãú²÷Öý& r Umþ â÷ÖúQ r–Ífª‹55®Å´žªšq=3ۤ=´Ù‹žãd‹Û²‡á–œdž¨‹²ð²ž´Ù´Ù´Ù?“‡y}É–s)ÉÉšÉ3sÉ\É\ÿ–?ÉuÉçÉüÉLsÓÉLsɇãÿúÛ²yéD{=ãÿü{\°²Ç´Ùÿìªç{ºfqqìqÑ/qº9Á9ÿÛ¢º9Á˺ºåqºqJº+o#7®¼=‹V¼;¼=3X²´Ù‹55¬^R²\×Åsåž´ÙãdÕôÙ5^5bs®ž‹Û#5‰Å`åÁÁ‰Á‰Áb?yyyyyyË–sÉÉÉÉ\;\¢\ÿþ\3 üÉLsLsLsLsLs´LfÛ²Û²Û²Û²ãÿü×É ºç{ç{ç{ç{ç{ç{Û{fqìqìqìqìq9ÿÇ99ÿÞ9ÿôåqºåqåqåqåqåq´ÙåH®®®®¼=º¼=yç{yç{yç{–sfq–sfq–sfq–sfq)Éq3 qÉìqÉìqÉìqÉìqÉìq3sq3sq3sq3sqÉÿåTÉx\ÿä9ÿÓ\9ÿò\ÿõ9ÿä\°9–\É9Á¸ÉrÁ\ÿ–9ÿÛ?É¢º¢ºuÉXÁuÉ9ˆuÉÁuɼÁÿòFüɺüɺüɺ‚ÍüɺLsåqLsåqLsåqs/qÉJºÉJ‚ÉJº‡+o‡+o‡+o‡+oãÿú#7ãÿú#7ãÿú#7Û²®Û²®Û²®Û²®Û²®Û²®éD‹Vãÿü¼=ãÿü{\3X{\3X{\3XÑ/ áÿ—}ɺ} s–sfq3 ÿ—}ÉqåqƒLuꤚÿ–Ñÿ3s~ߺÔÉ\ ÷É¢¹9 ¼=˲üÿ–ºLsNgåv—sq7ÿ—¹Ér+dɰþò#7ã#7ãÿúÝ­°NÄÉóÿüØ={\3XT T\Ÿh3q–T]ŸhXº\Éðɬ]É `É dÉ <q¯ÉKɧÁsÉdÉaºyç{\ÿþ9ÿàLsåqÛ²®Û²®Û²®Û²®Û²®ìqyç{yç{ËÛ{3sq3sq?É¢ÿéLsåqLsåqT ŸX9ÿÛ `É dÉ <q3sqçÉuÉüɺyç{ËÛ{LfåHyç{yç{ÉìqÉìq\ÿ§9ÿÃ\9ÿãLsåqLsåqÇJ‚ÉJºÛ²®Û²®‡+oãÿú#7œ,GÉÿðâÉ´q–qâq{\3Xyç{ÉìqLsåqLsåqLsåqLsåqãÿü¼=ÌŠ¾ºÑ79ÿÛüqüqyÿý– f u ãÿ²+o3XÓPÕP} Û yÉìq\ÿ–9ÿÛ@sq Jãÿö¼ Í®qººefqq’qìqìqŽ|S…A…4…Pq9ÿÛ‘qq qÄ`Ä`®ºº9µ¦ùt*KæL:Á¦Á˺˺˺+ÿÛ#º³åqÝqÓ”GpPPOJºIº~„>tÔºÔº+o°ÿÙ°ÿÙ±7°þò#7#7ñqÉÁ¼=‹V¼=ãf3X3XŸXŸmXXXXLs£ºPqªq;ºVþòVººÑqXXqvqq˜7á7:7É/ Á<Á66JO<u1ufÿéu]H^H 6û&: ® ‹®‹²‹Äuuuuõuõu  ÁÁÁÁ3ÖÕsª3ÖÕªs²o²ouuuuMÁÇšîL¶ð†ÿÿïhuTzüuuõuòÖòÖòÖòÖòÖÁÕî¶ü¨ýqüÁü´üÙûìü¿ý˜ü×ý7üìüôüÅý¼üðü]ü¿ü¿þýýÿyü¨ýqý ý¼þUþðý€ý ý ý ý ýzýwýšüÕý(ýjý#ýLý¼üðücüÅü¿ü¿ü¿ü´üÙûìûìûŒýxúíûhúý¬üñüðücý+þûìü¨ýqü´ýüçýÆüÕýýýü¶ü¶ü¶ücý3ýxü¿ý+ýxÿ.üpüpüpüpý*üpüw: : ¶efqe²žs׊‹ÛøÿçøÿóDÿí€ÿò™ÿá›ÿÛµy}ÉuÉyÉ{\ÉLs\É?ÉyçÉüÉÉLsÉÓÉÉãÿúãÿüLs{=LsN\ãÿüFqc…<ºµ¦¡•FqÀ¼ åqS…Zkºåqµ¦·¿¼=®xJvkåqÑJº²qqÑd¡•GpŸ;Gp³‡µ¡•åq¡•³‡ê¦ôq—W½ÿá—WGp³AOpLsåq0‹²qšÉ«ÿ@G³G¿ìrwxs³‡sFqUÉë-~OÛd$s6˜såqã,J7Opºfq9ÿÛLsìqìÄ×ɺ–sçÉ5U s–s sÉÉJÿúáÉ–s‡\É\\ÿ–ÀT\ÉJÿú®ÉüÉà#Éy}É}ÉáÉ@eÉž(!‡üÉüÉ®ÉTçÉÉLsÉÓÉ–sãÿúà#ãy{=6É|¯ŽÉÀÉ©<É}É–o£Óˆç{ïp·º4ºˆkìq5FA…3º3ºÕºL º;ºåq;ººfq©<¼=×p¼;rºº–Rº‰º§>Qº·ºdq¼ÁÐtìqìq/4ºdq+o9Á9ÿô9ÿÛ8L0º7/Õº3º¼=;ºxs³‡*`ŠÓþÁ D3 GÉÁLsåk7ɘÁsS[Ú2Lsåq@R2@R2ðs<q sq pv9˜xs³‡–sfq;ûÚýý³ý³X÷ÖXøX.ÉjÁ}!·&ÓɺáÉ4ºfG¹8þÉ=ºž(5F!‡A…®ÉÕº®ÉÕº®!Õ=Ú2§*ÉIÁÉÁ¦ÉSÁ^s6q–sfqãÿú©<ãÿü¼=ãÿü¼={=¼;yÿút|¯º–|¯º–|¯º‡Ó‡Ó\Éž(5F?ÉÕ¿56].ÉIÁ6ÉrÁ|¯º–É2Á9Áyç{yç{ËÛ{ÉìqLuìqLuìqž(5F!‡A…T ŸXüÉ3ºüÉ3ºLsåqLsåqLsåq–odqà#¼=à#¼=à#¼=|¯º–áÉ4ºÉQºfG¹8{=¼;{=¼;}‘·q ‘-qÌÉô«nɵ«“6¨.çɽÁ3sGq/ÿú°ê¤S…TLï²Û²²²Û²&›t²Û²β²Û²u²—²äAÛ²¨²&²7\²ÛŒ¹\Û²Û²Û²S²&›²Û²²‡Û²ÛŒe²ãsR\Ls‡uu‹²^x"ž6^Z˺ºq"º®“qD®ºšº“qº;Á'º q®ºqº®F®9ÿÛ2  ºŸF˺  &º®‘®Ê®ºqÁÊ®/åqz®²ðw`D«ÿÿDÇÇcc/NDãdc\ÑÅ\шfc²ÞX•CÀXº.ºþXº0º.º¼XsXËXºEX.ºˆX™XNºÕº*C¦CMº¼XwºBĺ¢ºĺSº(º•Ûü‹Û?“ã9ÿµ9lÝÿ«9lC‚9Áˆ‚1‹ˆ‚ˆ‚***}}Ýÿ«Ýÿ« Ä‚ Ä‚ ¬‚ ¬‚ffÆuÆuXÿìK5k˜ÐôŒà“1‹Ýÿ«C‚C‚ÜÜÜÜÜÜÈì˜OOóL¸LLRLLL¯L‡LUL<L<LdL…™‹²\Wˆ‚5kVzˆ‚ˆ‚ˆ‚ˆ‚ˆ‚ˆ‚ˆ‚ˆ‚*******Ýÿ«Ýÿ«âÿ«Ýÿ«KKK)‚)‚Ðà“*Ýÿ«C‚C‚1‹L¸LLRLLLRLjLÐL<L<Ld‡ááááááÖÖ9Á’qd=¼=;º;ºÀq;º¡qÁ=xºåÁÍ=¬ÁÈ=D=;º=ßÁ=ÁËpÈ==Àq=Á=Á.ÁÀqÀqÙB¤¿[B[šÕ‚È‚È{d{dãÿì7Å]‚x‹‰Ülx‹€r[º")>WE_HHöY"º~h~h@€rx‹2Os N~sx‹MÊnû`P{Püšû@û@û@û@üþü§û`ûNzBz®ÿÓa\î¼d“ýûû>üNûüš‡ c c]cc Y]‚d–ji:~:~¹P†Z"ZPºZ|ZPZ}P¼P…ZPZXZ­‚³PP‚P|Z­‚­‚²P²P*FÕ‚­ZÄn­‚ÅP\Z­‚ˆZ­‚ZFdZW‚PPWF…ZdZddDPGd.d#ddadd%dvd-d/‚Cd$d/dWd/‚?d1<Nd/dBd/d-d-‚ˆd/d/‚éF.‚üZ#d{dddd‚d—dÃd.‚ndmd?dndyyyy'É'É'É'É'w'w®É­®É­®É­¼ÉɼÉÉ'ɼÉw¼Éw'w ÉX‰c^c^‰‰%‰‰ºH‰ ɉ‰‰¸E ÉïwyyyyÕwÕwÕwÕwÕwÕw®É­®É­®É­¼Éw¼Éw¼Éw¼ÉwÕw‰‰‰Û²Û²Û²Û²×w×w×w×w×y×y^ɲ^ɲ^ɲlÉ4wlÉ4w”É4y”É4y”ðy‰²²íwyFÉÉÉFŽFFÉÉáÉ*F…ɇɅɇÉáÉ*FáÉ*F…ɇɅɇɅðA‰A8F3ÉF3ÉFÉÉÉFFFÉÉáÉ*F…ɇɅɇÉáÉ*FáÉ*F…ɇɅɇɅðz‰z‰áwuÉuÉuÉáw.wáwuÉuÉþÉ&wøÉÉøÉÉþÉ&wþÉ&wøÉÉøÉÉøð‰‰‰ÐwÐFÐF• ÐwÐwÐwÐFÐFŽÉGwyÉÐFyÉÐFÐF͉Ÿ‰͉ÐwÐFÐFÐFÐwÐwÐwÐFÐFŽÉGwyÉ=FyÉ=FŽÉGwŽÉGwªÉ=FªÉ=F͉ÕwÓFÓÉÓÉÓÉÓFJFÓFÓÉÓÉ­ÉFVÉ+ÉVÉ+É­ÉF­ÉFVÉ+ÉVÉ+ÉVðY‰]‰Y‰²F²F²F"F"F"F"FGÉúFGÉúFGÉúF ÉÌF ÉÌF ÉÌF ÉÌF ‰wÉÉÉwWwwÉÉÁÉ?w‡ÉŒÉ‡ÉŒÉÁÉ?wÁÉ?w‡ÉŒÉ‡ÉŒÉ‡ðY‰‰à‰SÉSwSwSÉSÉSÉÓÉÓÉÓÉÓFÓFÓF­ÉFY‰Õwy–y–y–y–ÏMÏMÏwÏwdÉw‰Û²Û²Û²Û²Û²Û²×S×S×y×y”É4y‰¥F¥w¥w¥F¥F¥w¥w‚‰Éà‰`‰`‰`‰`‰`‰`‰`‰š‰¥F¥w¥F¥w ‰ ‰ ‰‰‰ ‰ ‰™‰•‰ÐFÐFÐwÐwÐFÐF͉%w%!%!%w%w%!%!Y É)É#s¿‰¿‰u`‰ z‰ ‰ ‰ ‰ ‰ ‰ ‰Ñÿìñÿì²ÿìsÿì 4ÿì õÿìñÿì²ÿìsÿì 3ÿì õÿìüÿì¿ÿì‚ÿì Eÿì ÿìñÿì²ÿìpÿì 2ÿì õÿìüÿìÿìOÿì £ÿì 3ÿìwÿìqÿì¼=½ Û{°>fq׺×$íºS‚9Á(¢ºª º3ºåqfyqyqy3/qåqåq2—Ð2Ð2©<˜ºå®•®/¼=‹V3X5xª³¼=ƒ—2—¹ZLr èuá//SçHÊ||ÿ½NÎYÅÅ÷H„G €ÿü°pû+"M"M>G/Mmu>GVGVGâTàR>GnzhuüuEuOGöGOGOGmu\#=nÂnüuV&7yûGSGé%nzu=nV&7yûKySGé%ºÊùt9‘>GöGöGOGâT_Ýÿé>G=nIÿzIIàÿVÿzá[qüuüuIÿéÖuEqOGOGâFKÿé\#^YGäu@zV&î7~7î7#7OGü[ü[ü[ü[üBüByç{}É®}ɺ}ɺ–sfq)Éq)Éq)Éq)Éq)ÉqÉìqÉìqÉìqÉìqÉìqšÉÑ/3sqɮɺÉÿíɺ\9ÿÓ\9ÿô?É¢º?É¢º?É¢ºuÉMÁuMÿýuÉ9ÿôuÉ9ÿÞçÉ˺çÉ˺çÉ˺üɺüɺüɺüɺLsåqLsåqLsåqLsåqÓɺÓɺÉJºÉJºÉJºÉJT‡+o‡+o‡+o‡+o‡+oãÿú#7ãÿú#7ãÿú#7ãÿú#7Û²®Û²®Û²®Û²®Û²®y¼=y¼=éD‹VéD‹VéD‹VéD‹VéD‹V{=¼;{=¼;ãÿü¼={\3X{\3X{\3Xº#‹V¼=ç{Ñ/yç{yç{yç{yç{yç{yç{yç{yç{yç{yç{yç{yç{ÉìqÉìqÉìqÉìqÉìqÉìqÉìqÉìq\Z9D\È9·LsåqLsåqLsåqLsåqLsåqLsåqLsåqNgåvNgåvNgåvNgåvNgåvÛ²®Û²®Ý­°Ý­°Ý­°Ý­°Ý­°ãÿü¼=ãÿü¼=ãÿü¼=ãÿü¼=FqFqFqFqFqFqFqFqyy'iªñS…S…S…S…S…S…°°ºÌ0lºººººººº²¯°¶7hx“µ›µ‘µÿ±µÿ»µµÿÓµÿ˵ÿÆ þÌêñåqåqåqåqåqåqoÉÂ̂á•¡•¡•¡•¡•¡•¡•¡•Eû.³‡³‡³‡³‡³‡³‡³‡³‡k¿·Ã‘Ç_žFqFqS…c…º<ºµÿµ¦åqåq¡•¡•³‡³‡FqFqFqFqFqFqFqFqyy'iªñºººººººº²¯°¶7hx“³‡³‡³‡³‡³‡³‡³‡³‡k¿·Ã‘Ç_žFqFqFqFqFqFqFqyy»ÿüŠy†¶†¶¶ºº<ºººpÿüøÿçrÿüøÿóɉ´¶µÿëµÿãµÿصµÿäµÿæ\ÿõ\ÍÿüDÿí~•¶¡•¡•¡•¡•ºº¡•¡•ãÿüãÿüÃÿü™ÿá{ª×ª³‡³‡³‡³‡³‡‡ÿü€ÿòaÿü›ÿÛNs†£V‹™Ìãdãddddÿì‹®‹²‹®‹²%®%®%®%®99¸3¸3­ìWìì‹Ü™ ¼q âqÑ(ý()(Ñ(ý()(¶ 3ž3Á´Ãâ“?“ÿìnÿ§nÿ§ÿª=ÝVþ‰°°`JݓݓúdìØØ=²nÿ§=™‘´Ùnÿ§´8°úN(´f´f‹Ûad´p‹Û‹ÛÇ5Wnz5?5f5\5l5Y5S9‰9‰9‰÷o÷g0u5W5‰5^5b5?5f5\5l5Y5S9‰9‰9‰÷o÷g"MVGOGuVGVs`…˺üu .É—Éé;F_( .Ð.D]1u!ªüü=ü=ü=üûPü=&C&C–süÃ#ÿÖ&<‰<ꤖoÃèI ÌÉH#Â;”jÃDNÿä‹ hÆR6”nœÈLsbBƒSVÈ,©yÉ(˜ÿü'yõ\ŸXNNîµD?ÉyJ\ ‚Ö}¼WØ¢JTšÉŽ9²högdÿûº(V FhZ —ž$Ò<ÈÊÉ|3£uuãÿüWªZì[ÏOÏÿ=;6QÁ‰Á^Á‰Á^ÁbÁ?Á‰ÁfÁ‰ÁbÁfÁl‹‰\ÉðÉ„ÉbÉyaõ ‰WÉ{=w= =uÉ–s)ÉçÉ9Á©ÁÁ~Á¼=}=í= ]=Á¼;”;;9Áfqq˺ öy)É öy se´d´£´u´£´d´£´ ´ ´ ´ ´d´u´,´´d´¥´u´¥´d´u´d´¥´u´¥´¥´d´v´d´v´d´d´*´Z´¬´Z´¬´Ý´d´-´O´Þ´p´Ó´´d´d´´£´u´u´´£´d´w´d´d´x´v´x´d´d´d´d´u´d´¥´u´¥´d´¤´ ´¼´¼´ ´d´u´d´u´£´£´d´£´u´£´k´u´7´^´H´^´^´^´@´^´^´^´u´z´z´^´u´w´u´d´u´d´d´u´d´7´u´7y‡#hÉÉø›ZÿúZÿúø¯ø¯¾Ùø¯ø¯¾Ù,œœd´Ù´Ù´ÙVþ‰Š´D‹Û===jݪݴ,¯,¯´í°ffÛÛÛÛ+uPuuu+uPuuu+u+u+uyy¡y´Ù´Ù´Ø´Ù´Ù´Ù´¢´ÙдٴٴٴٴٴٴٴٴٴٴٴٴشٴٴٴٶٶÙÏʹٴٴٴٴٴٴٴٴٴٴٴٴٴٴٴٴڴڴڴÚ`”`”¶°´Ø´Ù´Ù´Ù´Ù´Ù´Ú´Ù´Ù´Ð´Ð´Ð´Ð´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ì´Ì´Ì´Ì´¾´Ù´¾´Ù´¾´¾ÛÛÛ´Ù´Ù´Ù´ÙÆÙÆÙ´»´»´»´»´»´»´»´»´»´»´»´»´»ø¯ø¯ø¯ø¯*¯*¯ø¯ø¯ø¯ø¯ø¯ø¯ø¯ø¯´Ù´Ù´Ù´Ùyy´b´y*îÛÈÛÈÛÈ´´ÿúÿúŒŒô‹ÛùÙÙÙÙٴٴٴ٠a” a”´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´á´Ù´Ù´Ù´Õ–ìììXø¯¾Ùø¯ø¯¾Ùø¯ø¯Xø¯¾Ùø¯¾ÙøÙÑJÑr’´£´£´£´£è;°Ç°Çxôxdxôxd´Ù÷´ÙÀ°À†À°À†+¯+* 8œ 8œ Pœ 8œ Œx Pü–µ¦º³‡Fq 8œü#°°°°°°°°°°£¨££¨£¨+¯´7ºü–'ÿƒ’,˜,˜,˜,˜,˜,˜,˜,˜,˜,˜ÑÿìÑÿìÑÑÈÑÿìÑÿìÑÑÈÑÿëÑÿìÑÑÈÑÑÑÈÑÈÑÿìÑÿìÑÿìÑÿìÑÑÑÈÑÈÑÿìÑÿìÑÿìÑÿìÑÑÑÈÑÈÑÈÑÈÑÈÑÈÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÑÈÑÿìÑxÑÑxÑxÑÿìÑÿìÑÿìÑÑxÑxÑÿìÑÿìÑÿìÑÑxÑxÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÿìÑÑÿìÑÿìÑÑÿ“Ñÿ“Ñÿ“ÑÿìÑÑ|ÑÑÿìÑÈÑ|ÑÈÑÿìÑÈÑÿìÑÈ'ÿì'ÿì'ÿì'ÿì'ÿì'ÿì'ÿì'ÿì'ÿì'ÿì'ÿì'ÿì'ÿì'ÿì'ÿì'ÿì''ÿì'ÿì'ÿì'ÿì'q'ÿì''ÿì'ÿì'ÿì'ÿì'ÿì''ÿì'ÿ캺ººººººººlºlºººgºgº'''''''''''''''''ûpôûpûrûpûpûpûpûpûpûpûpûp7p7ºTºúúúûpûp''''¸3ººººº'''ôpººººûpûpûpûp'''¤º¤ºܺܺ',«h,d,ª,ƒ,…,…•ª+ª,ª}}_}} ÷Œ ‘,¸,·,·Bš,d,˜,¬,¬,Ÿ,«,¬,¬,²ß“,±ß“,},¬,ª,dZdøª2ªEª\ª,ª,²,ª¯ª,«,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,‡,ª,ª,ª,ª,Ý,ªé¯Ù¯Ù¯,¯,S,À,ø,,ì,],·,À,ç,,m,«,E,©,À,°,A,É,â,U,¶,Q,0,É,â,U,¶,L,0,C,¹,X,ä,B,¶,X,ä,ØƬ¬,x,¼Ƶܬß­ü¬ „,¬,œ,œ,œ,œ,œ,œ,œ,œ,¬,ª,ª,ª,1ô–ô–ô–ô–ô–ô–,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,X,X,j,Æ, T},y,},),©,,,,džª¯¯¯ %¢6¯´ ´Ï´€´ ´´´ú´d´T´ ÀÒ´±Ù¯´´V´´I´V´V´x´„´³´+´¶´5´¶´X´Š´3´í´´¯´ñ´ü´p´p´p´p´R´ ´>´p´»´T´V´S´T´W´W´/´V´0´/´0´0´0´2´„´˜´p´@´T´»´T´T´T´p´§´§´¡´¡´n´n´T´V´¡´¶´œ´‚´¡´a´a´T´T,f,z,z,z,z,ª´´°´”®”xN®Nx´>´ž´N´n´X´~´ ´‘´‘´´´¹´Û´#´9´U´w´Ä´l´f´¡,˜,˜,˜,˜,˜,˜,˜,˜,˜,˜´ ´ ´ ´ ´ ´ ´ ´ ´ ´ ´ ´ ´ ´ ´ ´ ´ ´ ´ ´ ´u´ü´u´ü´u´u´u´u´u´u´u´ä´ä´ä´u´u´ì´u´u´u´+´+´´´´´<´‚´¥´u´§´§´u´§´s´–´¢´¢´uôö°ö¯·¤s·s¤´[´Y´O´O Bu xd xu xd xd xu xd xd xu xd xu xuÜÜ,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü„Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,Ü,´d´u´´wOwOßß´ÙÙÙÙÙÙÙÙô´Ù´Ù::: šu+u+u+u+u+u+u+u+u+u+u+ÿ¾+u+u+u+u+k´´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ú´Ú´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´Ù´µ´ ´µ´ ´7´7´^´^´µ´ ´µ´ ´7´^°u°u°H°Hºººº''''ºô$ü–ü–ü"u 9 uÿ×Ó ÉçH#ÿèɺ?É¢º{\3X@sçÉyà= D±V¼h<É‹ÁGp,qssu@xC@~–yyɃv–ÿÛ{\í–{\ssg–)É?>8–{\(–ÉoŸo:oüÉ÷Ì\É:o\ÉŸcssÉÉþ–ss–s$d{=–súÉyÉÉNs‰ÉNs6,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ªЖЖåqƒ/ / /¼/¼/}/ão « ® ‘® }® <®^ºVô¶NÙ 1X?º,X¿X׺uXX´ÙeºeºeºeºN²N²N²>X¡CÜX†º°Q~Xwº°Q"XTXXäX¦X,X6ºTºC€º"XeºÎ.º>XTXTºXˆ‚Û:ÿìjÿ숂Û:ÿìjÿ숂Û:ÿìjÿ숂Û:ÿìjÿ숂Û:ÿìjÿ숂Û:ÿìjÿìKHÓÿì ÿìKHÓÿì ÿì**òÿì*ÿì**òÿì*ÿì**òÿì*ÿì**òÿì*ÿìÝÿ«jÿ«Ýÿ«jÿ«)‚)‚Ïÿìkÿì)‚)‚Ïÿìkÿìà““Ýÿ«"ÿ«:ÿìjÿìC‚«‚:ÿìjÿìüpý*XXÿìXiXXXÿìXXÿìXXÿìXÿôXÿìXXÿìã9ÿµpÿµ9lplÝÿ«"ÿ«9lplC‚«‚:ÿìjÿì9ÁpÁˆ‚Û:ÿìjÿì1‹J‘ˆ‚Û:ÿìjÿ숂Û:ÿìjÿì**òÿì*ÿì**òÿì*ÿì**òÿì*ÿì}3}}3}Ýÿ«jÿ«Ýÿ«jÿ« Ä‚ 3‚´ÿì#ÿì Ä‚ 3‚´ÿì#ÿì ¬‚ Í‚Ëÿìðÿì ¬‚ Í‚Ëÿìðÿìf˜^ÿìÿìf˜^ÿìÿìÆuBuÆÿìÜÿìÆuBu/ÿìÜÿìKHÓÿì ÿì5k¬kÓÿì ÿ옾ÏÿìkÿìÐpÿì¦ÿìôŒSŒIÿì ÿìà““:ÿìjÿì1‹J‘8ÿì°ÿìÝÿ«"ÿ«C‚«‚C‚«‚:ÿìjÿìÿ.Æÿ.ÿåÆÿåÆTÆT4,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ª,ªíÙÉèÖÉcÉ3s$f"ÿ–É=É3ÄLrˆªÿú¤±ÈÝDrl¨ÿü{ª”fqª”ìo¹/ªqåºÏæÏÿ×5ºÏæ "ºåºåqªqªqߺ+o¹7ê±Ü=HVhLÿ=¸Xy}É–s)ÉÉšÉ3sÉ\É\ÿ–?ÉuÉçÉüÉLsÓÉLsɇãÿúÛ²yéD{=ãÿü{\ç{ºfqqìqÑ/qº9Á9ÿÛ¢º9Á˺ºåqºqJº+o#7®¼=‹V¼;¼=3X‡á–œdž¨‹ÿ¹ü×ýsü¶ý üÏüÏüÇüÇýšüæüN––Û:ÿìjÿìHÓÿì ÿì¬kÓÿì ÿì:ÿìjÿì¶Û:ÿìjÿìÛ:ÿìjÿì*òÿì*ÿì*òÿì*ÿì*òÿì*ÿì*òÿì*ÿì_üÕ9îþòx’x’x’x“x“x’x’xvx‹xvx‹xvx‹x‹xvxvxƒxƒxƒx‹ü,âÿ«âÿ«pÿì¦ÿì«‚:ÿìjÿìjÿ«9ÁJ‘–qâq9–ÏOÏÿ9ÿÓ9·²ðŒ—´­ÈÈÈÈÈÈÜÜ:Á:ÿì:ÿì’q’ÿì’ÿìd=dÿìdÿì¼=¼ÿì¼ÿì;º;ÿì;ÿì;º;ÿì;ÿìÀqÀÿìÀÿì;º;ÿì;ÿì¡q¡ÿì¡ÿìÁÿìÿì=ÿìÿìxºxÿìxÿìåÁåÿìåÿìÍ=ÍÿìÍÿì¬Á¬ÿì¬ÿìÈ=ÈÿìÈÿìD=DÿìDÿì;º;ÿì;ÿì=ÿìÿìßÁßÿìßÿì=Á=ÿì=ÿìËpËÿìËÿìÈ=ÈÿìÈÿì=ÿìÿìÀqÀÿìÀÿì=Á=ÿì=ÿì=Á=ÿì=ÿì.Á.ÿì.ÿìÀqÀÿìÀÿìÀqÀÿìÀÿìüì,ª,ª,ª,ª,ª,ª,ª,ª³—.ÉjÁ?< þ0 ô$ÿÓ%ÿ·*K-r294K7ÿD9ÿˆ:ÿ­;ÿš<ÿ R&YÿÉ\ÿÜ‚ÿÓƒÿÓ„ÿÓ…ÿÓ†ÿÓ”9•9–9—9˜9Ÿÿ ´&µ&¶&·&¸&¿ÿÜÁÿÜÂÿÓÄÿÓÆÿÓàK&ÿD:ÿ $ÿÓ$ÿÜ$ÿÜ$$9$&ÿÜ$*ÿÜ$2ÿÜ$4ÿÜ$7ÿa$9ÿ}$:ÿ$<ÿa$FÿÜ$GÿÜ$HÿÜ$Iÿ·$RÿÜ$TÿÜ$WÿÜ$Yÿˆ$Zÿ­$\ÿu$mÿ·$‚9$ƒ9$„9$…9$†9$‰ÿÜ$”ÿÜ$•ÿÜ$–ÿÜ$—ÿÜ$˜ÿÜ$Ÿÿa$©ÿÜ$ªÿÜ$«ÿÜ$¬ÿÜ$­ÿÜ$´ÿÜ$µÿÜ$¶ÿÜ$·ÿÜ$¸ÿÜ$¿ÿu$Áÿu$Â9$Ä9$Æ9$ÈÿÜ$ÉÿÜ$ÊÿÜ$ËÿÜ$ÌÿÜ$ÍÿÜ$ÎÿÜ$ÏÿÜ$ÑÿÜ$ÓÿÜ$ÕÿÜ$×ÿÜ$ÙÿÜ$ÛÿÜ$ÝÿÜ$ÞÿÜ$àÿÜ$âÿÜ$ÿÜ$ÿÜ$ÿÜ$ÿÜ$ÿÜ$ÿÜ$$ÿa$%ÿÜ$&ÿa$'ÿÜ$6ÿ$7ÿ­$8ÿa$9ÿu$:ÿa$ˆÿÜ$¬ÿÜ$­ÿÜ$®ÿÜ$¯ÿÜ$Üÿa$ÝÿÜ$ðÿÜ$óÿÜ$ôÿa$õÿu$ +ÿ$ ,ÿ­$ -ÿ$ .ÿ­$ /ÿ$ 0ÿ­$ ™ÿa$ šÿu$ ¦þø$ §ÿ$ ¨/%&ÿÜ%*ÿÜ%2ÿÜ%6ÿÜ%9ÿÁ%:ÿ·%<ÿ%mÿÁ%}ÿÜ%‰ÿÜ%”ÿÜ%•ÿÜ%–ÿÜ%—ÿÜ%˜ÿÜ%Ÿÿ%ÈÿÜ%ÊÿÜ%ÌÿÜ%ÎÿÜ%ÞÿÜ%àÿÜ%âÿÜ%äÿÜ%ÿÜ%ÿÜ%ÿÜ%ÿÜ%ÿÜ% ÿÜ%"ÿÜ%6ÿ·%8ÿ%:ÿ%¬ÿÜ%®ÿÜ%ÚÿÜ%ðÿÜ% +ÿ·% -ÿ·% /ÿ·% ™ÿ% ¦ÿ% §ÿ% ¨ÿ­&<ÿÜ&mÿÜ&}ÿÜ&ŸÿÜ&8ÿÜ&:ÿÜ&ôÿÜ& ™ÿÜ& §&'$ÿÜ'9ÿÜ'<ÿ'mÿÜ'}ÿÜ'‚ÿÜ'ƒÿÜ'„ÿÜ'…ÿÜ'†ÿÜ'Ÿÿ'ÂÿÜ'ÄÿÜ'ÆÿÜ'8ÿ':ÿ'ôÿ' ™ÿ' ¦ÿÓ' §ÿÉ' ¨ÿD)þ·)ÿa)$ÿD)6ÿÜ)7ÿÜ)DÿD)Hÿ)Lÿk)Rÿ·)Uÿk)Xÿ)\ÿD)‚ÿD)ƒÿD)„ÿD)…ÿD)†ÿD)¢ÿD)£ÿD)¤ÿD)¥ÿD)¦ÿD)§ÿD)ªÿ)«ÿ)¬ÿ)­ÿ)´ÿ·)µÿ·)¶ÿ·)·ÿ·)¸ÿ·)»ÿ)¼ÿ)½ÿ)¾ÿ)¿ÿD)ÁÿD)ÂÿD)ÃÿD)ÄÿD)ÅÿD)ÆÿD)ÇÿD)Õÿ)×ÿ)Ùÿ)Ûÿ)Ýÿ)ñÿk)õÿk)ÿ·)ÿ·)ÿ·)ÿk)ÿk)ÿk)ÿÜ)ÿÜ) ÿÜ)"ÿÜ)$ÿÜ)&ÿÜ)(ÿÜ)+ÿ)-ÿ)/ÿ)1ÿ)3ÿ)5ÿ)9ÿD)­ÿ·)¯ÿ·)ÚÿÜ)ÜÿÜ)ñÿ·)õÿD) šÿD) ¦ÿÓ) ¨þˆ*7ÿ·*<ÿš*mÿÜ*}ÿÜ*Ÿÿš*&ÿ·*:ÿš* ¦ÿÓ* §ÿÓ* ¨ÿÉ+ÿÜ+ ¦ÿ·+ §ÿÁ+ ¨ÿ·-ÿ·-$ÿÜ-mÿÜ-}ÿÜ-‚ÿÜ-ƒÿÜ-„ÿÜ-…ÿÜ-†ÿÜ- ¦ÿ·- §ÿÁ- ¨ÿ.ÿ).$ÿÜ.&ÿ.2ÿ.7ÿa.8ÿÉ.:ÿ·.<ÿ·.DÿÜ.Hÿš.Rÿš.Xÿš.\ÿk.mÿ}.‚ÿÜ.ƒÿÜ.„ÿÜ.…ÿÜ.†ÿÜ.‰ÿ.”ÿ.•ÿ.–ÿ.—ÿ.˜ÿ.›ÿÉ.œÿÉ.ÿÉ.žÿÉ.Ÿÿ·.¢ÿÜ.£ÿÜ.¤ÿÜ.¥ÿÜ.¦ÿÜ.§ÿÜ.ªÿš.«ÿš.¬ÿš.­ÿš.´ÿš.µÿš.¶ÿš.·ÿš.¸ÿš.»ÿš.¼ÿš.½ÿš.¾ÿš.¿ÿk.Áÿk.Èÿ.Îÿ.Ýÿš.&ÿa.0ÿÉ.1ÿš.:ÿ·. ¦ÿÁ. §ÿÁ/ÿÜ/$//2ÿ·/7þæ/8ÿš/9ÿ/:ÿD/<þð/HÿÜ/RÿÜ/XÿÜ/\ÿD/‚//ƒ//„//…//†//”ÿ·/•ÿ·/–ÿ·/—ÿ·/˜ÿ·/›ÿš/œÿš/ÿš/žÿš/Ÿþð/ªÿÜ/«ÿÜ/¬ÿÜ/­ÿÜ/´ÿÜ/µÿÜ/¶ÿÜ/·ÿÜ/¸ÿÜ/»ÿÜ/¼ÿÜ/½ÿÜ/¾ÿÜ/¿ÿD/ÁÿD/ÝÿÜ/&þæ/0ÿš/1ÿÜ/:þð/ ¦þa/ §ýæ292ÿ­2ÿÜ2$ÿÜ29ÿÜ2;ÿ}2<ÿ2mÿÜ2‚ÿÜ2ƒÿÜ2„ÿÜ2…ÿÜ2†ÿÜ2Ÿÿ2:ÿ2 ¦ÿÓ2 §ÿÜ2 ¨ÿD3ÿÓ3þÁ3$ÿ}3<ÿÓ3Dÿ¤3Hÿ·3LÿÓ3QÿÜ3Rÿ·3UÿÜ3VÿÜ3XÿÜ3mÿÜ3‚ÿ}3ƒÿ}3„ÿ}3…ÿ}3†ÿ}3ŸÿÓ3¢ÿ¤3£ÿ¤3¤ÿ¤3¥ÿ¤3¦ÿ¤3§ÿ¤3ªÿ·3«ÿ·3¬ÿ·3­ÿ·3³ÿÜ3´ÿ·3µÿ·3¶ÿ·3·ÿ·3¸ÿ·3»ÿÜ3¼ÿÜ3½ÿÜ3¾ÿÜ3Ýÿ·3 ÿÜ3ÿÜ3ÿÜ3!ÿÜ3#ÿÜ31ÿÜ3:ÿÓ3 ¦&3 §&3 ¨þ·494 ¦ÿÓ4 §ÿÜ4 ¨ÿ}5ÿ­5ÿ·5ÿÁ5$ÿ­5&ÿš57ÿk59ÿ5:ÿ­5<ÿ}5DÿÓ5Hÿ¤5Rÿ¤5Xÿ¤5\ÿ5mÿ5}ÿÜ5‚ÿ­5ƒÿ­5„ÿ­5…ÿ­5†ÿ­5‰ÿš5Ÿÿ}5¢ÿÓ5£ÿÓ5¤ÿÓ5¥ÿÓ5¦ÿÓ5§ÿÓ5ªÿ¤5«ÿ¤5¬ÿ¤5­ÿ¤5´ÿ¤5µÿ¤5¶ÿ¤5·ÿ¤5¸ÿ¤5»ÿ¤5¼ÿ¤5½ÿ¤5¾ÿ¤5¿ÿ5Áÿ5Èÿš5Îÿš5Ýÿ¤5&ÿk51ÿ¤5:ÿ}5 ¦ÿk5 §ÿ}5 ¨ÿÜ6$&6‚&6ƒ&6„&6…&6†&7ÿD7ÿ 7ÿ7$ÿa7&ÿˆ77ÿÜ7Dþ­7Fþ¤7Hþ¤7LÿÁ7Rþ¤7UþÓ7Vþ­7XþÉ7Zþ­7\þÁ7mÿD7}ÿ7‚ÿa7ƒÿa7„ÿa7…ÿa7†ÿa7‰ÿˆ7¢ÿ7£þ­7¤ÿ7¥ÿ7¦ÿ7§ÿ7©þ¤7ªþà7«þ¤7¬þà7­þà7´þô7µþ¤7¶þô7·þô7¸þô7»þç7¼þÉ7½þç7¾þç7¿þÁ7ÁþÁ7Èÿˆ7Éþ¤7Îÿˆ7Ïþ¤7Ýþ¤7þÓ7þÓ7!þ­7#þ­7&ÿÜ71þÉ7 §ÿÓ7 ¨þø8=ÿÜ8?ÿÜ9ÿˆ9þø9ÿY9$ÿ}92ÿÜ9Dÿa9Hÿa9LÿÓ9Rÿa9Xÿu9\ÿÉ9mÿN9}ÿ9‚ÿ}9ƒÿ}9„ÿ}9…ÿ}9†ÿ}9”ÿÜ9•ÿÜ9–ÿÜ9—ÿÜ9˜ÿÜ9¢ÿa9£ÿa9¤ÿa9¥ÿa9¦ÿa9§ÿa9ªÿa9«ÿa9¬ÿa9­ÿa9´ÿa9µÿa9¶ÿa9·ÿa9¸ÿa9»ÿu9¼ÿu9½ÿu9¾ÿu9¿ÿÉ9ÁÿÉ9Ýÿa91ÿu9 ¨þæ:ÿ­:ÿ:ÿˆ:$ÿ:Dÿ}:Hÿˆ:LÿÓ:Rÿˆ:Uÿ¤:Xÿ·:\ÿÜ:mÿ:}ÿÜ:‚ÿ:ƒÿ:„ÿ:…ÿ:†ÿ:¢ÿ}:£ÿ}:¤ÿ}:¥ÿ}:¦ÿ}:§ÿ}:ªÿˆ:«ÿˆ:¬ÿˆ:­ÿˆ:´ÿˆ:µÿˆ:¶ÿˆ:·ÿˆ:¸ÿˆ:»ÿ·:¼ÿ·:½ÿ·:¾ÿ·:¿ÿÜ:ÁÿÜ:Ýÿˆ:ÿ¤:ÿ¤:1ÿ·: ¦ÿÜ: ¨þø;ÿš;&ÿk;2ÿ};7ÿÜ;Hÿ¤;mÿ;‰ÿk;”ÿ};•ÿ};–ÿ};—ÿ};˜ÿ};ªÿ¤;«ÿ¤;¬ÿ¤;­ÿ¤;Èÿk;Îÿk;Ýÿ¤;&ÿÜ; ¦ÿa; §ÿ­; ¨ÿÓ<ÿ <þa<þð<$ÿa<&ÿ<2ÿ<Dþæ<Hþð<Lÿ·<Rþð<Xÿ<mÿ<}ÿk<‚ÿa<ƒÿa<„ÿa<…ÿa<†ÿa<‰ÿ<”ÿ<•ÿ<–ÿ<—ÿ<˜ÿ<¢þæ<£þæ<¤þæ<¥þæ<¦þæ<§þæ<ªþð<«þð<¬þð<­þð<´þð<µþð<¶þð<·þð<¸þð<»ÿ<¼ÿ<½ÿ<¾ÿ<Èÿ<Îÿ<Ýþð<1ÿ< ¦ÿ< §ÿÜ< ¨þø=ÿÜ= ¦ÿÜ= §ÿÜ= ¨ÿÜH[ÿÜIÿIÿkIÿ·IWÿÜIZÿÜI\ÿÜImÿ·I}ÿÜI¿ÿÜIÁÿÜI'ÿÜI ¦AI ¨ÿNDÿÜNHÿ·NRÿ·NXÿÁN\ÿ·N¢ÿÜN£ÿÜN¤ÿÜN¥ÿÜN¦ÿÜN§ÿÜNªÿ·N«ÿ·N¬ÿ·N­ÿ·N´ÿ·Nµÿ·N¶ÿ·N·ÿ·N¸ÿ·N»ÿÁN¼ÿÁN½ÿÁN¾ÿÁN¿ÿ·NÁÿ·NÝÿ·N1ÿÁQ ¦ÿkQ §ÿQ ¨ÿ¤R&RÿÜR[ÿÁR ¦ÿkR §ÿ·R ¨ÿ}Uÿ}UÿDUÿÜUFÿÓUGÿÜUHÿÓUJÿÜUKÿÜUPÿÜUQÿÜURÿÓUTÿÜUUÿÜU[ÿÉUmÿ·U©ÿÓUªÿÓU«ÿÓU¬ÿÓU­ÿÓU³ÿÜU´ÿÓUµÿÓU¶ÿÓU·ÿÓU¸ÿÓUÉÿÓUÏÿÓUÑHUÝÿÓUáÿÜU ÿÜUÿÜUÿÜU §VU ¨þÉYÿÉYÿaYÿYmÿÜY}ÿÜY §ÿÜY ¨þðZÿDZÿZmÿÜZ}ÿÜZ ¨ÿ)[FÿÜ[HÿÁ[RÿÁ[©ÿÜ[ªÿÁ[«ÿÁ[¬ÿÁ[­ÿÁ[´ÿÁ[µÿÁ[¶ÿÁ[·ÿÁ[¸ÿÁ[ÉÿÜ[ÏÿÜ[ÝÿÁ\ÿÜ\þÜ\ÿk\mÿÜ\}ÿÜ\ ¨þÓm%ÿÜm&ÿÜm'ÿÜm*ÿÜm-ÿÜm7ÿm9ÿm:ÿÜm<ÿkmYÿÜmZÿÜm\ÿÜmˆ—m‰ÿÜmŸÿkm¿ÿÜmÁÿÜmÈÿÜmÎÿÜmÐÿÜmàÿÜm&ÿm:ÿk}$ÿ·}%ÿ·}&ÿÜ}'ÿÜ}-ÿÜ}2ÿÜ}7ÿD}9ÿN}:ÿ};ÿ}<ÿ}YÿÜ}ZÿÜ}\ÿÜ}‚ÿ·}ƒÿ·}„ÿ·}…ÿ·}†ÿ·}‰ÿÜ}”ÿÜ}•ÿÜ}–ÿÜ}—ÿÜ}˜ÿÜ}Ÿÿ}¿ÿÜ}ÁÿÜ}ÈÿÜ}ÎÿÜ}ÐÿÜ}&ÿD}:ÿ‚ÿÓ‚ÿÜ‚ÿÜ‚$9‚&ÿÜ‚*ÿÜ‚2ÿÜ‚4ÿÜ‚7ÿa‚9ÿ}‚:ÿ‚<ÿa‚FÿÜ‚GÿÜ‚HÿÜ‚Iÿ·‚RÿÜ‚TÿÜ‚WÿÜ‚Yÿˆ‚Zÿ­‚\ÿu‚mÿ·‚‚9‚ƒ9‚„9‚…9‚†9‚‰ÿÜ‚”ÿÜ‚•ÿÜ‚–ÿÜ‚—ÿÜ‚˜ÿÜ‚Ÿÿa‚©ÿÜ‚ªÿÜ‚«ÿÜ‚¬ÿÜ‚­ÿÜ‚´ÿÜ‚µÿÜ‚¶ÿÜ‚·ÿÜ‚¸ÿÜ‚¿ÿu‚Áÿu‚Â9‚Ä9‚Æ9‚ÈÿÜ‚ÉÿÜ‚ÊÿÜ‚ÌÿÜ‚ÍÿÜ‚ÎÿÜ‚ÏÿÜ‚ÑÿÜ‚ÕÿÜ‚×ÿÜ‚ÙÿÜ‚ÛÿÜ‚ÝÿÜ‚ÞÿÜ‚àÿÜ‚âÿÜ‚ÿÜ‚ÿÜ‚ÿÜ‚ÿÜ‚ÿÜ‚ÿÜ‚$ÿa‚%ÿÜ‚&ÿa‚'ÿÜ‚6ÿ‚7ÿ­‚8ÿa‚9ÿu‚:ÿa‚ðÿÜ‚ñÿÜ‚ +ÿ‚ .ÿ­‚ /ÿ‚ 0ÿ­‚ ™ÿa‚ šÿu‚ ¦þø‚ §ÿ‚ ¨/ƒÿÓƒÿ܃ÿ܃$9ƒ&ÿ܃*ÿ܃2ÿ܃4ÿ܃7ÿaƒ9ÿ}ƒ:ÿƒ<ÿaƒFÿ܃Gÿ܃Hÿ܃Iÿ·ƒRÿ܃Tÿ܃Wÿ܃YÿˆƒZÿ­ƒ\ÿuƒmÿ·ƒ‚9ƒƒ9ƒ„9ƒ…9ƒ†9ƒ‰ÿ܃”ÿ܃•ÿ܃–ÿ܃—ÿ܃˜ÿ܃Ÿÿaƒ©ÿ܃ªÿ܃«ÿ܃¬ÿ܃­ÿ܃´ÿ܃µÿ܃¶ÿ܃·ÿ܃¸ÿ܃¿ÿuƒÁÿuƒÂ9ƒÄ9ƒÆ9ƒÈÿ܃Éÿ܃Êÿ܃Ìÿ܃Íÿ܃Îÿ܃Ïÿ܃Ñÿ܃Õÿ܃×ÿ܃Ùÿ܃Ûÿ܃Ýÿ܃Þÿ܃àÿ܃âÿ܃ÿ܃ÿ܃ÿ܃ÿ܃ÿ܃ÿ܃$ÿaƒ%ÿ܃&ÿaƒ'ÿ܃6ÿƒ7ÿ­ƒ8ÿaƒ9ÿuƒ:ÿaƒðÿ܃ñÿ܃ +ÿƒ -ÿƒ .ÿ­ƒ /ÿƒ 0ÿ­ƒ ™ÿaƒ šÿuƒ ¦þøƒ §ÿƒ ¨/„ÿÓ„ÿÜ„ÿÜ„$9„&ÿÜ„*ÿÜ„2ÿÜ„4ÿÜ„7ÿa„9ÿ}„:ÿ„<ÿa„FÿÜ„GÿÜ„HÿÜ„Iÿ·„RÿÜ„TÿÜ„WÿÜ„Yÿˆ„Zÿ­„\ÿu„mÿ·„‚9„ƒ9„„9„…9„†9„‰ÿÜ„”ÿÜ„•ÿÜ„–ÿÜ„—ÿÜ„˜ÿÜ„Ÿÿa„©ÿÜ„ªÿÜ„«ÿÜ„¬ÿÜ„­ÿÜ„´ÿÜ„µÿÜ„¶ÿÜ„·ÿÜ„¸ÿÜ„¿ÿu„Áÿu„Â9„Ä9„Æ9„ÈÿÜ„ÉÿÜ„ÊÿÜ„ÌÿÜ„ÍÿÜ„ÎÿÜ„ÏÿÜ„ÑÿÜ„ÕÿÜ„×ÿÜ„ÙÿÜ„ÛÿÜ„ÝÿÜ„ÞÿÜ„àÿÜ„âÿÜ„ÿÜ„ÿÜ„ÿÜ„ÿÜ„ÿÜ„ÿÜ„$ÿa„%ÿÜ„&ÿa„'ÿÜ„6ÿ„7ÿ­„8ÿa„9ÿu„:ÿa„ðÿÜ„ñÿÜ„ +ÿ„ -ÿ„ .ÿ­„ /ÿ„ 0ÿ­„ ™ÿa„ šÿu„ ¦þø„ §ÿ„ ¨/…ÿÓ…ÿÜ…ÿÜ…$9…&ÿÜ…*ÿÜ…2ÿÜ…4ÿÜ…7ÿa…9ÿ}…:ÿ…<ÿa…FÿÜ…GÿÜ…HÿÜ…Iÿ·…RÿÜ…TÿÜ…WÿÜ…Yÿˆ…Zÿ­…\ÿu…mÿ·…‚9…ƒ9…„9……9…†9…‰ÿÜ…”ÿÜ…•ÿÜ…–ÿÜ…—ÿÜ…˜ÿÜ…Ÿÿa…©ÿÜ…ªÿÜ…«ÿÜ…¬ÿÜ…­ÿÜ…´ÿÜ…µÿÜ…¶ÿÜ…·ÿÜ…¸ÿÜ…¿ÿu…Áÿu…Â9…Ä9…Æ9…ÈÿÜ…ÉÿÜ…ÊÿÜ…ÌÿÜ…ÍÿÜ…ÎÿÜ…ÏÿÜ…ÑÿÜ…ÕÿÜ…×ÿÜ…ÙÿÜ…ÛÿÜ…ÝÿÜ…ÞÿÜ…àÿÜ…âÿÜ…ÿÜ…ÿÜ…ÿÜ…ÿÜ…ÿÜ…ÿÜ…$ÿa…%ÿÜ…&ÿa…'ÿÜ…6ÿ…7ÿ­…8ÿa…9ÿu…:ÿa…ðÿÜ… +ÿ… -ÿ… .ÿ­… /ÿ… 0ÿ­… ™ÿa… šÿu… ¦þø… §ÿ… ¨/†ÿÓ†ÿ܆ÿ܆$9†&ÿ܆*ÿ܆2ÿ܆4ÿ܆7ÿa†9ÿ}†:ÿ†<ÿa†Fÿ܆Gÿ܆Hÿ܆Iÿ·†Rÿ܆Tÿ܆Wÿ܆Yÿˆ†Zÿ­†\ÿu†mÿ·†‚9†ƒ9†„9†…9††9†‰ÿ܆”ÿ܆•ÿ܆–ÿ܆—ÿ܆˜ÿ܆Ÿÿa†©ÿ܆ªÿ܆«ÿ܆¬ÿ܆­ÿ܆´ÿ܆µÿ܆¶ÿ܆·ÿ܆¸ÿ܆¿ÿu†Áÿu†Â9†Ä9†Æ9†Èÿ܆Éÿ܆Êÿ܆Ìÿ܆Íÿ܆Îÿ܆Ïÿ܆Ñÿ܆Õÿ܆×ÿ܆Ùÿ܆Ûÿ܆Ýÿ܆Þÿ܆àÿ܆âÿ܆ÿ܆ÿ܆ÿ܆ÿ܆ÿ܆ÿ܆$ÿa†%ÿ܆&ÿa†'ÿ܆6ÿ†7ÿ­†8ÿa†9ÿu†:ÿa†ðÿ܆ñÿ܆ +ÿ† -ÿ† .ÿ­† /ÿ† 0ÿ­† ™ÿa† šÿu† ¦þø† §ÿ† ¨/ˆ ¦ÿ­ˆ §ÿ¤ˆ ¨ÿ‰<ÿ܉mÿ܉}ÿ܉Ÿÿ܉:ÿ܉ §&’$ÿÜ’9ÿÜ’<ÿ’mÿÜ’}ÿÜ’‚ÿÜ’ƒÿÜ’„ÿÜ’…ÿÜ’†ÿÜ’Ÿÿ’ÂÿÜ’ÄÿÜ’ÆÿÜ’8ÿ’:ÿ’ôÿ’ ™ÿ’ ¦ÿÓ’ §ÿÉ’ ¨ÿD”9”ÿ­”ÿÜ”$ÿÜ”9ÿÜ”;ÿ}”<ÿ”mÿÜ”‚ÿÜ”ƒÿÜ”„ÿÜ”…ÿÜ”†ÿÜ”Ÿÿ”:ÿ” ¦ÿÓ” §ÿÜ” ¨ÿD•9•ÿ­•ÿÜ•$ÿÜ•9ÿÜ•;ÿ}•<ÿ•mÿÜ•‚ÿÜ•ƒÿÜ•„ÿÜ•…ÿÜ•†ÿÜ•Ÿÿ•:ÿ• ¦ÿÓ• §ÿÜ• ¨ÿD–9–ÿ­–ÿÜ–$ÿÜ–9ÿÜ–;ÿ}–<ÿ–mÿÜ–‚ÿÜ–ƒÿÜ–„ÿÜ–…ÿÜ–†ÿÜ–Ÿÿ–:ÿ– ¦ÿÓ– §ÿÜ– ¨ÿD—9—ÿ­—ÿÜ—$ÿÜ—9ÿÜ—;ÿ}—<ÿ—mÿÜ—‚ÿÜ—ƒÿÜ—„ÿÜ—…ÿÜ—†ÿÜ—Ÿÿ—:ÿ— ¦ÿÓ— §ÿÜ— ¨ÿD˜9˜ÿ­˜ÿܘ$ÿܘ9ÿܘ;ÿ}˜<ÿ˜mÿܘ‚ÿܘƒÿܘ„ÿܘ…ÿܘ†ÿܘŸÿ˜:ÿ˜ ¦ÿÓ˜ §ÿܘ ¨ÿD›=ÿÜ›?ÿÜœ=ÿÜœ?ÿÜ=ÿÜ?ÿÜž=ÿÜž?ÿÜŸÿ ŸþaŸþðŸ$ÿaŸ&ÿŸ2ÿŸDþæŸHþðŸLÿ·ŸRþðŸXÿŸmÿŸ}ÿkŸ‚ÿaŸƒÿaŸ„ÿaŸ…ÿaŸ†ÿaŸ‰ÿŸ”ÿŸ•ÿŸ–ÿŸ—ÿŸ˜ÿŸ¢þ柣þ柤þ查þ柦þ柧þ柪þðŸ«þðŸ¬þðŸ­þðŸ´þðŸµþðŸ¶þðŸ·þðŸ¸þðŸ»ÿŸ¼ÿŸ½ÿŸ¾ÿŸÈÿŸÎÿŸÝþðŸ1ÿŸ ¦ÿŸ §ÿÜŸ ¨þø ÿk ÿ·  ¦ÿÜ  ¨ÿD¡&¡ ¦ÿ¡ §ÿ¡ ¨ÿ­ª[ÿÜ«[ÿܬ[ÿÜ­[ÿܲ ¦ÿ¤² §ÿ² ¨ÿ·³ ¦ÿk³ §ÿ³ ¨ÿ¤´&´ÿÜ´[ÿÁ´ ¦ÿk´ §ÿ·´ ¨ÿ}µ&µÿܵ[ÿÁµ ¦ÿkµ §ÿ·µ ¨ÿ}¶&¶ÿܶ[ÿÁ¶ ¦ÿk¶ §ÿ·¶ ¨ÿ}·&·ÿÜ·[ÿÁ· ¦ÿk· §ÿ·· ¨ÿ}¸&¸ÿܸ[ÿÁ¸ ¦ÿk¸ §ÿ·¸ ¨ÿ}¿ÿÜ¿þÜ¿ÿk¿mÿÜ¿}ÿÜ¿ ¨þÓÁÿÜÁþÜÁÿkÁmÿÜÁ}ÿÜÁ ¨þÓÂÿÓÂÿÜÂÿÜÂ$9Â&ÿÜÂ*ÿÜÂ2ÿÜÂ4ÿÜÂ7ÿaÂ9ÿ}Â:ÿÂ<ÿaÂFÿÜÂGÿÜÂHÿÜÂIÿ·ÂRÿÜÂTÿÜÂWÿÜÂYÿˆÂZÿ­Â\ÿuÂmÿ·Â‚9ƒ9„9Â…9†9”ÿÜ•ÿÜ–ÿÜ—ÿܘÿÜŸÿa©ÿܪÿÜ«ÿܬÿÜ­ÿÜ´ÿܵÿܶÿÜ·ÿܸÿÜ¿ÿuÂÁÿuÂÂ9ÂÄ9ÂÆ9ÂÈÿÜÂÊÿÜÂÌÿÜÂÍÿÜÂÎÿÜÂÑÿÜÂÓÿÜÂÕÿÜÂ×ÿÜÂÙÿÜÂÛÿÜÂÝÿÜÂÞÿÜÂâÿÜÂÿÜÂÿÜÂÿÜÂÿÜÂÿÜÂÿÜÂ$ÿaÂ%ÿÜÂ&ÿaÂ'ÿÜÂ6ÿÂ7ÿ­Â8ÿaÂ9ÿuÂ:ÿaÂðÿÜÂñÿÜ +ÿ -ÿ .ÿ­Â /ÿ 0ÿ­Â ™ÿa šÿu ¦þøÂ §ÿ ¨/ÄÿÓÄÿÜÄÿÜÄ$9Ä&ÿÜÄ*ÿÜÄ2ÿÜÄ4ÿÜÄ7ÿaÄ9ÿ}Ä:ÿÄ<ÿaÄFÿÜÄGÿÜÄHÿÜÄIÿ·ÄRÿÜÄTÿÜÄWÿÜÄYÿˆÄZÿ­Ä\ÿuÄmÿ·Ä‚9ă9Ä„9Ä…9Ć9Ä”ÿÜÄ•ÿÜÄ–ÿÜÄ—ÿÜĘÿÜÄŸÿaÄ©ÿÜĪÿÜÄ«ÿÜĬÿÜÄ­ÿÜÄ´ÿÜĵÿÜĶÿÜÄ·ÿÜĸÿÜÄ¿ÿuÄÁÿuÄÂ9ÄÄ9ÄÆ9ÄÈÿÜÄÊÿÜÄÌÿÜÄÍÿÜÄÎÿÜÄÑÿÜÄÓÿÜÄÕÿÜÄ×ÿÜÄÙÿÜÄÛÿÜÄÝÿÜÄÞÿÜÄâÿÜÄÿÜÄÿÜÄÿÜÄÿÜÄÿÜÄÿÜÄ$ÿaÄ%ÿÜÄ&ÿaÄ'ÿÜÄ6ÿÄ7ÿ­Ä8ÿaÄ9ÿuÄ:ÿaÄðÿÜÄñÿÜÄ +ÿÄ -ÿÄ .ÿ­Ä /ÿÄ 0ÿ­Ä ™ÿaÄ šÿuÄ ¦þøÄ §ÿÄ ¨/ÆÿÓÆÿÜÆÿÜÆ$9Æ&ÿÜÆ*ÿÜÆ2ÿÜÆ4ÿÜÆ7ÿaÆ9ÿ}Æ:ÿÆ<ÿaÆFÿÜÆGÿÜÆHÿÜÆIÿ·ÆRÿÜÆTÿÜÆWÿÜÆYÿˆÆZÿ­Æmÿ·Æ‚9ƃ9Æ„9Æ…9Ɔ9Æ”ÿÜÆ•ÿÜÆ–ÿÜÆ—ÿÜÆ˜ÿÜÆŸÿaÆ©ÿÜÆªÿÜÆ«ÿÜÆ¬ÿÜÆ­ÿÜÆ´ÿÜÆµÿÜÆ¶ÿÜÆ·ÿÜÆ¸ÿÜÆÂ9ÆÄ9ÆÆ9ÆÈÿÜÆÊÿÜÆÌÿÜÆÍÿÜÆÎÿÜÆÑÿÜÆÓÿÜÆÕÿÜÆ×ÿÜÆÙÿÜÆÛÿÜÆÝÿÜÆÞÿÜÆâÿÜÆÿÜÆÿÜÆÿÜÆÿÜÆÿÜÆ$ÿaÆ%ÿÜÆ&ÿaÆ'ÿÜÆ6ÿÆ7ÿ­Æ8ÿaÆ:ÿaÆðÿÜÆñÿÜÆ +ÿÆ -ÿÆ .ÿ­Æ /ÿÆ 0ÿ­Æ ™ÿaÆ ¦þøÆ §ÿÆ ¨/È<ÿÜÈmÿÜÈ}ÿÜÈŸÿÜÈ:ÿÜÈ §&Î<ÿÜÎmÿÜÎ}ÿÜΟÿÜÎ:ÿÜÎ §&Ð$ÿÜÐ9ÿÜÐ<ÿÐmÿÜÐ}ÿÜЂÿÜЃÿÜЄÿÜÐ…ÿÜІÿÜПÿÐÂÿÜÐÄÿÜÐÆÿÜÐ8ÿÐ:ÿÐôÿÐ ™ÿÐ ¦ÿÓÐ §ÿÉÐ ¨ÿDÒ$ÿÜÒ9ÿÜÒ<ÿÒmÿÜÒ}ÿÜÒ‚ÿÜÒƒÿÜÒ„ÿÜÒ…ÿÜÒ†ÿÜÒŸÿÒ:ÿÒ ¦ÿÓÒ §ÿÉÒ ¨ÿDÝ[ÿÜà7ÿ·à<ÿšàmÿÜà}ÿÜàŸÿšà&ÿ·à:ÿšà ¦ÿÓà §ÿÓà ¨ÿÉûÿÜû$/û2ÿ·û7þæû8ÿšû9ÿû:ÿDû<þðûHÿÜûRÿÜûXÿÜû\ÿDû‚/ûƒ/û„/û…/û†/û”ÿ·û•ÿ·û–ÿ·û—ÿ·û˜ÿ·û›ÿšûœÿšûÿšûžÿšûŸþðûªÿÜû«ÿÜû¬ÿÜû­ÿÜû´ÿÜûµÿÜû¶ÿÜû·ÿÜû¸ÿÜû»ÿÜû¼ÿÜû½ÿÜû¾ÿÜû¿ÿDûÁÿDûÝÿÜû&þæû0ÿšû1ÿÜû:þðû ¦þaû §ýæÿÿÜÿ$/ÿ2ÿ·ÿ7þæÿ8ÿšÿ9ÿÿ:ÿDÿ<þðÿHÿÜÿRÿÜÿXÿÜÿ\ÿDÿ‚/ÿƒ/ÿ„/ÿ…/ÿ†/ÿ”ÿ·ÿ•ÿ·ÿ–ÿ·ÿ—ÿ·ÿ˜ÿ·ÿ›ÿšÿœÿšÿÿšÿžÿšÿŸþðÿªÿÜÿ«ÿÜÿ¬ÿÜÿ­ÿÜÿ´ÿÜÿµÿÜÿ¶ÿÜÿ·ÿÜÿ¸ÿÜÿ»ÿÜÿ¼ÿÜÿ½ÿÜÿ¾ÿÜÿ¿ÿDÿÁÿDÿÝÿÜÿ&þæÿ0ÿšÿ1ÿÜÿ:þðÿ ¦þaÿ §ýæOÿ? ¦ÿk §ÿ ¨ÿ¤ÿ­ÿ·ÿÁ$ÿ­&ÿš7ÿk9ÿ:ÿ­<ÿ}DÿÓHÿ¤Rÿ¤Xÿ¤\ÿmÿ}ÿÜ‚ÿ­ƒÿ­„ÿ­…ÿ­†ÿ­‰ÿšŸÿ}¢ÿÓ£ÿÓ¤ÿÓ¥ÿÓ¦ÿÓ§ÿÓªÿ¤«ÿ¤¬ÿ¤­ÿ¤´ÿ¤µÿ¤¶ÿ¤·ÿ¤¸ÿ¤»ÿ¤¼ÿ¤½ÿ¤¾ÿ¤¿ÿÁÿÈÿšÎÿšÝÿ¤&ÿk1ÿ¤:ÿ} ¦ÿk §ÿ} ¨ÿÜÿ}ÿDÿÜFÿÓGÿÜHÿÓJÿÜKÿÜPÿÜQÿÜRÿÓTÿÜUÿÜ[ÿÉmÿ·©ÿÓªÿÓ«ÿÓ¬ÿÓ­ÿÓ³ÿÜ´ÿÓµÿÓ¶ÿÓ·ÿÓ¸ÿÓÉÿÓÏÿÓÑHÝÿÓáÿÜ ÿÜÿÜÿÜ §V ¨þÉÿ­ÿ·ÿÁ$ÿ­&ÿš7ÿk9ÿ:ÿ­<ÿ}DÿÓHÿ¤Rÿ¤Xÿ¤\ÿmÿ}ÿÜ‚ÿ­ƒÿ­„ÿ­…ÿ­†ÿ­‰ÿšŸÿ}¢ÿÓ£ÿÓ¤ÿÓ¥ÿÓ¦ÿÓ§ÿÓªÿ¤«ÿ¤¬ÿ¤­ÿ¤´ÿ¤µÿ¤¶ÿ¤·ÿ¤¸ÿ¤»ÿ¤¼ÿ¤½ÿ¤¾ÿ¤¿ÿÁÿÈÿšÎÿšÝÿ¤&ÿk1ÿ¤:ÿ} ¦ÿk §ÿ} ¨ÿÜÿ}ÿDÿÜFÿÓGÿÜHÿÓJÿÜKÿÜPÿÜQÿÜRÿÓTÿÜUÿÜ[ÿÉmÿ·©ÿÓªÿÓ«ÿÓ¬ÿÓ­ÿÓ³ÿÜ´ÿÓµÿÓ¶ÿÓ·ÿÓ¸ÿÓÉÿÓÏÿÓÑÿÜÝÿÓáÿÜ ÿÜÿÜÿÜ §V ¨þÉ $& ‚& ƒ& „& …& †&"$&"‚&"ƒ&"„&"…&"†&&ÿD&ÿ &ÿ&$ÿa&&ÿˆ&7ÿÜ&Dþ­&Fþ¤&Hþ¤&LÿÁ&Rþ¤&UþÓ&Vþ­&XþÉ&Zþ­&\þÁ&mÿD&}ÿ&‚ÿa&ƒÿa&„ÿa&…ÿa&†ÿa&‰ÿˆ&¢þ­&£þ­&¤þ­&¥þ­&¦þ­&§þ­&©þ¤&ªþ¤&«þ¤&¬þ¤&­þ¤&´þ¤&µþ¤&¶þ¤&·þ¤&¸þ¤&»þÉ&¼þÉ&½þÉ&¾þÉ&¿þÁ&ÁþÁ&Èÿˆ&Éþ¤&Îÿˆ&Ïþ¤&Ýþ¤&þÓ&þÓ&!þ­&#þ­&&ÿÜ&1þÉ& §ÿÓ& ¨þø0=ÿÜ0?ÿÜ:ÿ :þa:þð:$ÿa:&ÿ:2ÿ:Dþæ:Hþð:Lÿ·:Rþð:Xÿ:mÿ:}ÿk:‚ÿa:ƒÿa:„ÿa:…ÿa:†ÿa:‰ÿ:”ÿ:•ÿ:–ÿ:—ÿ:˜ÿ:¢þæ:£þæ:¤þæ:¥þæ:¦þæ:§þæ:ªþð:«þð:¬þð:­þð:´þð:µþð:¶þð:·þð:¸þð:»ÿ:¼ÿ:½ÿ:¾ÿ:Èÿ:Îÿ:Ýþð:1ÿ: ¦ÿ: §ÿÜ: ¨þø?ÿÜ? ¦ÿÜ? §ÿÜ? ¨ÿÜ š$ÿÓ š%ÿ· š*K š-r š29 š4K š7ÿD š9ÿˆ š:ÿ­ š;ÿš š<ÿ šR& šYÿÉ š\ÿÜ š‚ÿÓ šƒÿÓ š„ÿÓ š…ÿÓ š†ÿÓ š”9 š•9 š–9 š—9 š˜9 šŸÿ š´& šµ& š¶& š·& š¸& š¿ÿÜ šÁÿÜ šàK š&ÿD š:ÿ ¦$þø ¦%ÿÁ ¦&ÿ· ¦'ÿÁ ¦)ÿÁ ¦*ÿ· ¦+ÿÁ ¦-ÿÁ ¦.ÿÁ ¦/ÿÁ ¦2ÿ· ¦3ÿÁ ¦4ÿ· ¦5ÿÁ ¦;ÿˆ ¦=ÿÜ ¦Iÿ· ¦Qÿ ¦Rÿk ¦Uÿ ¦Yÿ· ¦Zÿ· ¦\ÿ· ¦‚þø ¦ƒþø ¦„þø ¦…þø ¦†þø ¦ˆþ} ¦‰ÿ· ¦”ÿ· ¦•ÿ· ¦–ÿ· ¦—ÿ· ¦˜ÿ· ¦ ÿÁ ¦¡ÿÁ ¦²ÿ· ¦³ÿ ¦´ÿk ¦µÿk ¦¶ÿk ¦·ÿk ¦¸ÿk ¦¿ÿ· ¦Áÿ· ¦Èÿ· ¦Îÿ· ¦ÐÿÁ ¦àÿ· ¦ûÿÁ ¦ÿÿÁ ¦ ÿ ¦ÿÁ ¦ÿ ¦ÿÁ ¦ÿ ¦?ÿÜ ¨$& ¨%ÿ· ¨&ÿ ¨'ÿ· ¨)ÿ· ¨*ÿ· ¨+ÿ· ¨-/ ¨.ÿ· ¨/ÿ· ¨2ÿ ¨3ÿ· ¨4ÿ ¨5ÿ· ¨7þæ ¨9þˆ ¨:ÿ ¨;ÿ· ¨<þˆ ¨IÿÜ ¨Qÿ· ¨Rÿ· ¨Uÿ· ¨Yÿ ¨Zÿ< ¨\ÿ ¨‚& ¨ƒ& ¨„& ¨…& ¨†& ¨ˆ& ¨‰ÿ ¨”ÿ ¨•ÿ ¨–ÿ ¨—ÿ ¨˜ÿ ¨Ÿþˆ ¨ ÿ· ¨¡ÿ· ¨²ÿ· ¨³ÿ· ¨´ÿ· ¨µÿ· ¨¶ÿ· ¨·ÿ· ¨¸ÿ· ¨¿ÿ ¨Áÿ ¨Èÿ ¨Îÿ ¨Ðÿ· ¨àÿ· ¨ûÿ· ¨ÿÿ· ¨ ÿ· ¨ÿ· ¨ÿ· ¨ÿ· ¨ÿ· ¨&þæ ¨:þˆDDDD¨ìDlÔ(˜ðŒà  D l ¸ < ¬ ¨ L äpDX¨ dÜ´40àxøX¬T°øhÔÐx„P d!\!Ì"P#0$ì%&T''t'À((X(„(Ô**˜+0+È,œ-4-ü.t.Ä/@000l101¨2L2ì3Œ3ü5\5Ø6\7€9¤;(<ô=À>œ>Ì?¸@<@<@ÌA˜BPChDŒDÔFFÔGøHÔI IØJKPKœLL„M$MàN0NäO@OlOÌP$P¬Q|QœQ¼QÜRÔRìSS0S`STÐU¨UÀUØUðVV8VPVhVŒV°W|W¬WÄWÜXX<XlY4ZlZ„ZœZÌ[[[¤\Ì\ð]]4]`]˜]Ø_”_¬_Ä_Ü``$`<`T`x`œbLbdb|b”b´bàc chdÜdôe e,e\etffDfpf˜fÄfäfügg,gDghg€g˜g°gÔgìhh,h<høii4iXi|i”i¬iÄiÜjj,jPjtjŒj¤jÈjàjøkk@k„l l lÄlèm m0mTmxmm¨mØn$nHnlnn´nÌnäoÔoìp$p<p`pxpœp´pèqdqôrr<rTrlrœrÈràsxtt@t`tŒt¬tÄtÜu|v¤v¼vÔvìww(wPwhw€w¨wÌwäwüx$xHx`xxx¤xÈyLyäzz4zdzˆz´zØzð{{,{D{\{t{˜{¼{à||$|<|T|l|„|œ|Ä}}è~°~Àt€€¨8ø‚´‚ăXƒÔ„|…T…°†`‡‡˜ˆP‰Š0‹‹\‹ÌŒˆœüŽDÔ䈠¸‘`’ ’¤“d” ”ô–<–L–è—€˜˜¬™8™P™hšš ›\,ž4Ÿ@Ÿü t ì¡X¡Ì¢`¢ì£`£ô¤4¤L¤Ì¤Ü¤ô¥ ¥$¥<¥T¥l¥„¥œ¥´¥Ô¥ô¦¦D¦\¦|¦œ¦À¦Ø¦ð§§ §8§P§h§€§§¨§À§Ø§ð¨¨ ¨ô©èªª,ªDªˆª ª¸ªÐªè«««@«X«p«ˆ« «¸¬P¬è­­­0­H­`­x­­¨­À­Ø­ð®® ®8®P®h®€®˜®°®È®à®ø¯¯(¯@¯X¯p¯ˆ¯ ¯¸¯Ð¯è°°°0°H°à±|±”±Ø²L²ð³ü´àµÌ¶Ä¶Ü¶ô· ·$·<·T·l·„·œ·´·Ì·ä·ü¸¸h¸ì¹T¹¸º˜»|»ì¼„½½€½Ô¾Œ¾ä¿h¿ðÀÌÁŒÁœÂ°ÃLÃèĨÅdÆŒÇÇhÇÈÉÉœÊ4ÊìˈÌhÍ(ÍØÎ¼Ï`ЀÐРѼ҈ÓÓôÔ¬ÕHÖ<×0׬ØDØðÙdÙtÙÈÚtÚüÛTÜ|ÝLÞ ß߸àLàôáˆâ4âäâôã\ãÄäPäÄåP嬿ççÀèÌé@éìêLêàëTëÔìxí0íÀîPðxññlò\ó|ôõ õ´öHöà÷|øøøèù¨ù¸úHú€ú¼ûtü0üìý|þDÿÿÈT$¸\(Œü€ €à8´ Xœ¬ÄÔä  T  ì L t œ  t Ð , \ l ˆ ¨ Ø ì  ( ” ä ø 0T€œ(ü\ˆÀô@h8˜ÌX¤Øì,@¬`ÈÜð\ÐDXlÔŒ¨ôDh„˜¬ÐôˆØü Lh˜ÈÜð\€”¨Ðô<Pdx¬ÀÔèüXt°Ð  @ l ´ è!H!d!x!Œ! !°!Ä!Ø!ü"$"L"l"è#\#t#¬#Ô#Ô$$,$@$€$”$¨$¼$Ø$ô%h%|%¤%Ô&&8&H&`&x&ˆ&Ø'' '0'L'h'„'œ'¸'Ð'è'ø((@(¬(¼(Ì(Ü)Œ)œ)¬** *0** *°*À+<+L+\,,,,À-p-”-¸-Ð-è...0/€00œ1€2P2ô3p484ˆ55Œ5œ6,6è6ø7t88´9H9¨:4:ô<<”=(=@=X=p=ˆ= >p??Ð?ì@@ÌA8AäBœCHC¼D,D<D´EE<E´FFÐGTG¼HH˜I€IøJhKKÌL`LøM4MlNN„N”N¤N´O$OO O°OÀOüP8P¸PÈPàPøQQ°R<RTSSS S0S@SØThTØTðUU UxUˆVVVPVÜVìWàX˜YDY„ZZxZˆZ˜Z¨Zð[[[ [¤\X\h\À]]x]ä^d^|^ð_œ`\`øaaÐblb¤c,c<d,dðe`e f0fggpg€gÈgØgèh,h<i$i4iŒjjdjÐkLkdkÔlxmm¬mÄndoo oÄoÔoäoôpp q4qÄqÜqôr rds<sLs°tt˜uuLuŒuèv@v¨wwŒxxðyÌz0z”z¤z´{{P{h{€|$|¨} ~Œt€0€H€`€Ä$p‚‚<‚d‚ŒƒÐ„Œ„¤„¼……„…ä†l†¬†ð‡P‡°ˆ<ˆÌ‰0‰‰¨‰ÀŠŠHŠ”ŠÜ‹(‹tŒŒ´$ôŽTŽì„œ‘Œ‘¤‘¼’’p’€““d“¨“ð”8”t”°”ø•@••Ü–<–L—(˜$˜<˜T˜d˜|˜”˜ð™T™ ™ìšdšÜ››T›œ›äœ0œ|œŒœ¸œØ@P`„¨¸Èàøžž(ž@žXžhžxžž¨žÀžØŸŸ4ŸDŸTŸlŸ„ŸœŸ´ŸÌŸäŸü  , D \ t À¡ ¡$¡<¡È¢T¢´££d£´¤,¤¨¥x¦D¦ä§€§ü¨x¨è©T©d©ÀªPªØ«,«€«« «ô¬H¬ð­˜®T®ø¯d°°”±,±Ø²`²Ü³D³ ´@´ˆ´Ðµ¶(¶¬··Œ¸(¸˜¹¹€¹Ôºl»»$»ˆ»˜¼¼Ì½P½`¾¾ ¾À¾Ð¾à¾ü¿<¿L¿ ¿ÄÀ”Á ÁÈÂPÂäÈÃøÄ|ÅtÆƈÆÈÇdÈXÈÔÈäÉäÊhËTÌÌ$ÌÐÍtÍ„ÎPÎ`ÏHÏÈÏØÐ\Ñ4ѰÑÀÑüÒØÓ Ó°ÔÌÕLÕŒÕÈÖÖ8ÖŒÖð××@×À×ìØ(ØPØ`ØÀØìÙÙ4Ù`Ù˜ÙÄÙìÚ4Ú„ÚÀÛpÛÌÜTÜ ÝÝDݘÝôÞpÞ¨ßßdßÈà(ààáápáôâœããœää¤å$å|ææ|æ”æ¬æÄæôç<ç\ç¸çèèXèÈèàèøéé(é@é\étéŒé¤é¼éÔê<êTê¨êÀëëëÜëôì¸ìÐí<íTíÔíìîîî4îðïPïÜïôð\ðÜñˆñ ñÌòlò”ò°ó<óXóÔôô`ôtôˆô°ôÌôüõTõÐöTö¼÷÷T÷¤øøPøø´øèùLùüúPúhú€ú˜ú¸úÐúèûûû0ûHû`ûxûû¨ûÀûØûðüü üÜüôý ý ý¸ýÐþ0þPþhþxþþ þ°þÀþÐþàÿ`ÿàDTdtøPœä0t´`ü,¨(l´(„h¼4´  ¬  „ Ð  œ ô € Ä ð p ¸lèpœä 0X Äì,p°ôP| ä´\¸”`ì°t4Üxä”X  !\""ü#¤$l%(%ä&Ä'L'h'ø(t(Œ(ð)`)Ø*\*¤+ +„+ü,˜-,-D-È.l.à/”/¼0D0à1,1@1Œ1ð2p2è3t3ø4œ5\5Ì6Ð7<8L949¸: :„; ;l;Øl>Ø?¨?ü@p@ÜA<A¤BdBøC4C„CÔDXD¨E E F FÀGG\G¸HHÌI I€JHJœKK|LLhLäMdMÐNN|O$O„OäPLP¸QQ”QàR<R¨S<S¤T,T„TüUhUàV,VœW0W€WìXhY Y€YìZ`ZÔ[L[À\ \\ð] ]p]Ü]ô^^^ˆ^ ^¸^Ð_@_X_p_ˆ_ _¸_Ð_è```0`H```x``¨`À`Ø`ðaa<a\a˜aÔbbTbˆbÐbècc0cTclc„cœc´cÄcÜcìddhd€d˜d°ee,eDe\eteŒe¤e¼eÔeìfff4fLfdf|f”føg(g8gPg¼gÔh<hThlh„hìiii4iLidi|i”i¬iÄiÜiôj j$j<jTjljÈjàjøkk(k¤k¼l8lPlÐlèmm€m˜m°mÈmàmønn(n@nXnpnˆn n¸nÐnèooxoÌoäoüpp,p€p˜pðqq`qxqqèrrr0rHr`rxrr¨rÀrØrðss s8sPshsÀsøt0tHt€t˜tÐtèuuu(u@uXupuˆu u¸uÐuèvvv0vHv`vxvvÌw,wLwÈwàx`xxxðyy y˜y°yÈyàyøzz(z@zXz¨zè{<{{¨{ü||h|€|˜|ì}}}4}L}d}|}”}¬}Ä}Ü}ô~ ~$~<~T~Œ~È4Œ¤ø€€(€€€˜€°€È€à€ø(@Xpˆ ¸Ðè‚‚P‚Ôƒƒ`ƒ¸ƒÐ„L„d„ä„ü……,…D…\…t…Œ…¤…¼…ԅ솆†4†L†À‡‡0‡Œ‡¤ˆˆˆ0ˆŒˆ¤ˆ¼ˆÔˆì‰‰‰4‰L‰d‰|‰”‰¬‰Ä‰Ü‰ôŠ ŠlŠ|Џ‹$‹‹ôŒ ŒpŒˆŒðpØðŽTŽlŽ„Žð,àø¬Ä‘|‘”’H’`’x’’ü“x“ô”p”ì•••˜•°–0–H–`–x–ð—T—¼—Ô˜8˜P˜´˜Ì™,™<™L™d™|™”™¬™Ä™Ü™ôš šŒ› ›œœ0œHœ`œxœœ¨œÀ4àž`žxžôŸ ŸŒŸ¤Ÿè $ ` x ´ Ì¡¡ ¡\¡à¡ð¢`¢¢ð£(£@£X£p£ˆ£ £¸£Ð£è¤¤,¤\¤˜¤Ü¥,¥T¥„¥À¦¦T¦ˆ¦Ô§8§´¨H¨x¨¼©©€ªªHª ªØ«p¬H¬p¬¤¬Ø­­`®<®¨®¸¯¯X¯ˆ°°<°x°ˆ°À°Ð°à°ð±±X±°²@²ü³4³l³°´ ´´ ´Üµ µ|µð¶¶¶ ¶p¶¶ ¶È¶Ø·4·D·x·Ä¸@¸¸À¸ð¹`¹¹¬¹Üºº0ºhº˜ºÈ»(»ä¼0¼œ¼Ä½½@½À¾H¾°¿ˆ¿ìÀTÀÈÁp”Ã(ÃLÀÄİÅ4ÅœÅØÆÆtÆ´ÇÇLÇÈÇðÈPȈÉÉŒÉäÉøÊ Ê Ê4ÊHÊ\ʸÊÌÊàË`Ëp˰ËìÌTÌ´Í8ÍÈÎXΜÎàÏhÏ´ÏìÐÐHЄÐÜÑÑPÑpÒÒ„ÒäÓHÓxÓèÔxÕ ÕpÕÈÖ0Ö¨Öì×P×xר×ôØPؼÙ(ÙHÙlÙÙ°ÙÜÚ Ú$Ú<ÚTÚlڄڜڴÚÌÚäÚüÛÛ,ÛDÛ\ÛtیۤۼÛÔÛìÜÜÜ4ÜLÜdÜ|Ü”ܬÜÌÜìÝÝÝ4ÝLÝdÝ|Ý”ݬÝÄÝÜÝôÞ Þ$Þ<ÞTÞlބޜ޴ÞÌÞäÞüßß,ßDß\ß|ß”߬ßÄßÜßôà à$à<àTàlà„àœà´àÌàäàüáá,áDá\átáŒá¬áÄáÜáôâ â$â<âTâlâ„âœâ´âÌâäâüãã,ãDã\ãtãŒã¬ãÌãäãüää,äDä\ätäŒä¤ä¼äÔäìååå4åLådå|å”å¬åÄåÜåôæ æ$æ<æTælæ„æœæ´æÌæäæüçç,çDç\çtçŒç¤ç¼çÔçìèèè4èLèdè|è”è¬èÄèÜèôé é$é<éTélé„éœé´éÌéäéüêê,êDê\êtêŒê¤ê¼êÔêìëëë4ëLëdë|ë”ë¬ëÄëÜëôì ì$ì<ìTìlì„ì¤ì¼ìÔììííí4íLídí|í”í¬íÄíÜíôî î$î<î\îtîŒî¤î¼îÔîìïïï4ïLïdï|ï”ï¬ïÄïÜïôð ð$ð<ðTðlð„ðœð´ðÌðäðüññ,ñDñ\ñtñŒñ¤ñ¼ñÔñìòòò4òLòhò€òœò¸òÐòèóóó0óHó`óxó”ó°óÌóäôôô4ôLôdô|ô”ô¬ôÄôÜôøõõ0õHõdõ€õœõ¸õÐõèööö0öHö`öxö”ö°öÌöä÷÷÷8÷T÷l÷„÷œ÷´÷Ì÷ä÷üøø0øHødø€ø˜ø°øÈøàøøùù(ù@ù\ùtùù¬ùÄùÜùôú ú$ú<úTúlú„ú ú¼úÔúðû û(ûDû\ûlû„û”û¬û¼ûÔûäûüü ü$ü4üLü\ütüŒü¤ü¼üÔüìýýý4ýLýdý|ý”ý¬ýÄýÜýôþ þ$þ<þTþlþ„þœþ´þÌþäþüÿÿ,ÿDÿ\ÿtÿŒÿ¤ÿ¼ÿÔÿì4Ld|”¬ÄÜô $<Tl„œ´ÌÜô„”¬ÄÜô $@Pl|”°Èàø(8Ph€˜´Äàø(@Xh€˜°Èàø$@Xhx¨ÀØð 8H`pÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜDpœÄÜô 0 l ¤ È $ € Ü  h ì , H ` „ Ø ð ð ð ð ð ð ð|Œ¬Ää8\Ì@pŒ|Ü,HÀð <p¨ØLd€¤´ÈPx¸(dÈø444444444444ˆÌØ<Ìô¤0d€¨à¤¸Ìàô0DXl€”¨¼Ðäø  ” D Ô!!|""„#h$8$ä%H%`''\'´)@)Ü*t++t,,¨,Ì,ì--<-t-¸-ð.°/ˆ001@1Ü2t2„2˜2°3ð4à505Œ5ü6¨7<88œ99\<<¬=`=Ð>p?ˆ@„A4BLBôC”CôD€DÔEE€EE F”FÈFØFèGàH¤I,I¨JpKPK|LÀMM¸NHNÔOLO”PPŒQ Q”QÄRR\RÌRìSS<SÀTpUUHU°VHV˜V¸VØVøWW8WXWxW˜W¸WØWøXX0X@XXXxXX X¸XØYYY(Y@Y`YpY€YY Y°YÈYèZZZ(ZHZpZˆZ˜Z°ZÐZàZð[[[”]]ð^^^À_p` `ÐaðccDctc¤cÔd dlee¼ffTf fìg4g€g¼gøh¼høiLiÌjXkkàlômTmœmÔn nDn|n´nìopoðp,p˜q@qèrXrÈrðssŒsüt tDt`t|t˜tàu(upu¸uÔuðvXvØw@w€wÀxx@xœxøy8yxy¸yøz8zxzÔ{0{ˆ{à|(|p|¼}}D}€}Ä~~H~ˆ~ì@ €€l€ì@„È‚$ƒƒƒ|ƒÀ„„d„¼……ˆ…¼…ð†@†|‡Pˆ4ˆhˆÐ‰‰ôŠ$Šˆ‹,‹€‹äŒˆŒÜHŒŽ$ŽPŽŒ h|ÈÜh€˜‘’œ’¼’à“Œ”8”T”À”蕬•Ø––@–€–Ж藗ԙ š´›Tœœä,HtŒÀðžžpžÐŸLŸ”Ÿô T À¡(¡¨¢¢°£@¤4¤Ô¥„¦l¦à§8§ô¨h¨€¨ ¨À¨à©©$©€©ìª4ªxª¸««H¬(¬¬­8­Ä®®|®è¯x°°L°Œ°ô±X± ±ä²l²Ø³,³€³ì´X´ØµP¶¶¸¶ü·D·Ð¸X¸”¸Ì¹ ¹pººŒºü»l»°»ô¼`¼Ì½ ½p½ì¾l¾Ø¿D¿¤¿¼ÀÀ\À ÀôÁLÁŒÁ̘ÃLÄÄØÅŒÆtÇXÈÈÌÉÉXɼÉôÊ0ÊTÊ|Ê ÊÄÊôË$ËTË”ËÐÌÌpÌÄÍ0Í`ÍŒÍÄÍüÎhÎÔÏ$ÏhÏŒÏÀÏøÐ,ЀаÐÜÑÑlÑÌÑÜÑìÒ ÒdÒ¤ÒäÓÓ@Ó¤ÓÜÔÔxÔØÕ0Õ„Õ¼ÕôÖHÖœ×0×ÄØ,Ø”ØàÙ,Ù¤ÚÚ¤Û,Û„ÛÜÜPÜÀÜäÝÝ(ÝLÝÀÞ(Þ€Þ˜ß ßlßààLàÀá(á„áøâ\âãã4ãlãœãÌãäää\ä|äœä¼äÜåå,åTå|åœåðæìçç4çTçtç”çìèDèèÈé4é”îLî¼ï0ï@ïPï`ïpï ïìð0ðhð„ð¼ðôññHñhñ„ñ¨ñÌñèòò<ò”òÌòèó óxó¬óÈôôHôðõõtõœ÷ ùû ý0ÿ< | è   < X Œ Ä ü 8 p ° ø D h Œ ° Ô ø@d„¨Ìð0PtœÄðDt ÈðDp˜ÈôDpœÄìDl¼è8d¸èP„¸ì XÈ4hœÐ(T€¬Ôü(Tˆ´à@l Ìø,\Ô8x¬ÜP€ÀL¨ÜDx˜´ô  , H d € œ ¸ Ô ø! !D!l!€!œ!¸!Ô!ð" "("D"`"|"˜"´"Ð"ì##$#8#ì&à'ˆ'œ'°'Ì'à'ô((((D(`(t(Œ(¨(Ä(ð)D)\)´*+P+Ø,`. .<.h.„.°.Ì.ø//L/h//¬/Ô/ð0080d0€0¬0È0ô11<1X1„1 1Ì1è2242h2¬3<33ø4ð5¤6`6Ä77t7Ð8,8”8ä9 949p9è:<::¼:è;;D;ˆ;Ì;è<<<4<|<¨<Ô<ü=$=`=¤=Ð=ø>d>¤>ä?$?d?ä@d@äAdAŒA´AÜBB$BPBlB”C„CðD¨MŒPpP¨Q Q\QÀRTS SìTPTôWxZXZ[[|[Ô]4^à_(_\`Pa$a¼bTccàd¤ehhðiljl0o˜pp qqxqürŒsÔuu v4vèww(wHwhwˆw¨wÈwèy\z,zü{¨|Ä}X}è~ ˆ€€h€ÄX‚‚l‚àƒxƒÜ„œ…|†L†À‡d‡°ˆL‰8‰¸Šà ¨ŽÄ ‘ ’´“¬”,”ô•Ü–p–ä—ˆ—Иp™8™Œ™¼šà›Ìœ œhœÌ,€Ìž`Ÿ  ¢£¤,¥h¦h§Œ¨À©°ª„«¬È® ¯$¯À°0°ä±Ø³´Œ¶L¶è·¨¸¸œ¸¸¸à¸ø¹¹(¹@¹Àºº”»Œ¼8½\¾¾ ¿DÀlÂTÅÆØÇ\ǨÈɜʔË(ÌÍÍøÎÔÏHÏÐÑ ÒXÓœÓØÔ\ÔÀÖP×´Ù@ÝÞŒààá@ãøå˜æ°çœè°é˜ê ê„êìë0ëpì<í4í´íèî$îhî˜îøïxð\ðôñ°ó0ô´øøtù$ùŒúú¨ûhûôütüðý`ý´þþ€þØÿœÿà8ˆø´`T€°  ˆÜd(ðÄHÈ(ˆÔ @ ¬!!€!è"L"h"„" "à#,#D#\$8$Ð%Œ%ü&p'(x(ä)T))Ì)ô* *H*t*œ*È*ô+ +„+è,d-.P.Ø/ð1„1ø4D5Ð7D89:4;<=(=ô? @8AhAèB˜C`CìDœEdEÜF´G|H@HpHœHÈHôI$I°IØJJTJ¨JÐKK0KPK KðLLLL”LÜM0M€MÌNNtNÌO8OÜP4P|PÔQ`QàRlS<SÌTôV V¬VèWWPWxW W¸WÐX(X|Y@ZZü[¬\\]|]¼]ü^X^”^Ð__h_ì_ì```0`D```|` `´`Ð`ìaa,aPata a´aÐaìbb,bPbtb b¼bàcc0cTc€c¬càcôdd,dPdldd´dàdüe eDepe”eÀeìf f<f`f„f°fÔgg,g`g„g°gÜhh<hph¤hàhôii,iPilii´iàiüj jDjpj”jÀjìk k<k`k„k°kÔll,l`l„l°lÜmm<mpm¤màmün nDnpn”nÀnìo oDopoœoÐoüp0pdp pÄpðqqPq|q°qär rLr€r´rðs$s`sœsàsôtt,tPtltt´tàtüu uDupu”uÀuìv v<v`v„v°vÔww,w`w„w°wÜxx<xpx¤xàxüy yDypy”yÀyìz zDzpzœzÐzü{0{d{ {Ä{ð||P|||°|ä} }L}€}´}ð~$~`~œ~à~ü Dp”Àì€ €D€p€œ€Ð€ü0d Äð‚‚P‚|‚°‚äƒ ƒLƒ€ƒ´ƒð„$„`„œ„à……0…\……¼…ð†$†`†Œ†À†ô‡0‡d‡ ‡Üˆ ˆLˆ€ˆ´ˆð‰$‰`‰œ‰àŠŠPŠŒŠÐ‹ ‹P‹”‹àŒ,ŒxŒÀÐŽ˜|\¨ä‘ ‘T‘ˆ‘°‘ä’’8’€’Ø“˜”l•P•x•à–\–È—t˜˜ˆ™™¬šPšÔ›l›èœpœôT´Äžž8žPžhž€ž˜ž°žÈŸ˜ d ´¡¡È¢„¢è£D£È¤H¤Ø¥h¥Ä¦(¦¦ô§0§l§„§œ§à¨(¨p¨À©8©´ªHªà«d«¬«ð¬T¬´­­\­Ô®H¯(°°Ø±¨±ô²<²„²Ì³³\³¸³à´´0´\´ˆ´´´àµµPµ„µ¼µô¶(¶T¶€¶¬¶Ô··4·d·”¸@¸|¸À¸è¹¹\¹œº ºh»¤¼T¼°¼À½½P½½Ä½ø¾¾à¿¿`¿ÐÀ0ÀÀ¸ÀàÁ`ÁÔœÀÄ(ÄÐňÅàÅðÆÆÆ Ç ÇÈ È€ÉÉàÊèËLË´ÌÌÜÍHÍ´ÎÎ ÎtÏ<ÏøÐÐHÐXÐØÑ4ÑÔÒ ÓHÔ,ÔŒÔäÕlÖ8ר ØtØ„ÙÀÚ@ÚPÚ”ÛÛÄÜÝÝDÝ\ÝtÝŒݤݼÝÔÝìÞÞÞ4ÞLÞdÞ|Þ”Þ¬ÞÄÞÜÞôß ß$ß<ßTßl߄ߜߴßÌßäßüàà,àDà\àtàŒà¤à¼àÔàìááá4áLádá|á”á¬áÄáÜáôâ â$â<âTâlâ„âœâ´âÌâäâüãã,ãDåðè ééÄêhêèëÈìˆíîîÌïtððÀñˆñ ñÔñìòdò¼òäó ó`ó óØôôPô”ô¬ôÄôÜôôõ õ$õ<õTõlõ„õœõ´õÌõäõüöö,öDö\ötöŒö¤ö¼öÔöì÷÷÷4÷L÷d÷|÷”÷ôø ø$ø<øTølø„øœø´øÌøäøüùù,ùDù\ùtùŒù¤ù¼ùÔùìúúú4úLúdú|ú”ú¬úÄúÜúôû û$û<ûTûlû„ûœû´ûÌûäûüüü,üDü\ütüŒü¤ü¼üÔüäýý ý°ýÈýàýøþþ þ˜þ°þÈþøÿ8ÿHÿXÿpÿˆÿˆÿˆÿˆÿˆÿˆÿˆÿˆÿˆÿˆÿˆÿˆÿˆÿˆÿˆÿˆÿˆÿ°ÿØ h|”¨Üð0H\tˆ ´ÌÜô $<Tl„œ´Ìäü @Xpˆ ¸Ðè0H`x¨ÀØð”ü€˜°ÈàðTl„”è(ü Œ , D \ t Œ œ x  ¨ À Ø ð   œŒ¤¼Ôìü„Ül„œ´Ìäü,D\tŒœhÔ\làXhx 8Phxð¬<Làð”¬ÄÜô $<Tl„Ø@@@@@@Ì <Xt¸à 4\ˆ°Ø(P|¤Ìø Hp˜Àì  < h  ¸ à!!0!\!„!¬!Ø""("P"x" "Ì"ô##H#p#˜#À#è$$<$d$Œ$¸$à%%0%X%€%¬%Ô%ü&(&P&x& &È&ð''D'l'˜'À'è((8(`(Œ(´(Ü))0)X)€)Ì*\*È++D+Ð, ,X,œ,Ì-(-Ä.`.˜/ /H/°00L0ð1Œ22 33€4H4¼4ô5P5”5à6œ77œ888Ô9 9ì:H:¼:ø;X;¬;ü<@>> >0>@>P>`>p>€>> >°>À>Ð>à>ð??? ?0?@?P?`?p?€?? ?°?À?Ð?à?ð@@@ @DAA¬B|C$CÀDlD E@E EÜFF FDFtF¤FÈGG|GŒGœH\HøI˜J`JpJ€J˜J°KK K@K`KxKK¨KÀKØKðLL L8LPLhL€L LÀLàMMMðN@N\NxN”N°NÌNèOO O<OXOtOO¬OÈOäPPP8PTPpPˆP¤PÜPôQHQ`QxQQ¨QÈQèRRdRtR„R”R¬RØS0SHS`S„S°SÌSäSüTT,TDT\TtTŒTÄUU<UÌVlVüWdWÔX<XlXœXÈYYpY¼ZZTZœ[[x[ä\@\˜\ð]ˆ^,^È_$_ˆ_ä`@`¤aa€bbˆcc¬d8dœeelff¼g`gÄh0h”i i´j@j”jðkDkxk°kälŒm<mänHn´oo°pTpìq<q”qärrPr„ssttdt¼u uduÄvvˆvüwhwèxlxìytzzˆzœzÀzä{{,{P{t{˜{¼|(|d| }–T+h @h>˜2 ãù  : ` ƒ²  (Z4;b ;¯;Å 0 Ë ï þ " F m "Ž :Ä %: h:ø ;— ;»Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. DejaVu changes are in public domain Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. DejaVu changes are in public domain DejaVu SansDejaVu SansBookBookDejaVu SansDejaVu SansDejaVu SansDejaVu SansVersion 2.24Version 2.24DejaVuSansDejaVuSansDejaVu fonts teamDejaVu fonts teamhttp://dejavu.sourceforge.nethttp://dejavu.sourceforge.netFonts are (c) Bitstream (see below). DejaVu changes are in public domain. Glyphs imported from Arev fonts are (c) Tavmjung Bah (see below) Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. Arev Fonts Copyright ------------------------------ Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the modifications to the Bitstream Vera Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Tavmjong Bah" or the word "Arev". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Tavmjong Bah Arev" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the name of Tavmjong Bah shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from Tavmjong Bah. For further information, contact: tavmjong @ free . fr.Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. Glyphs imported from Arev fonts are (c) Tavmjung Bah (see below) Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. Arev Fonts Copyright ------------------------------ Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the modifications to the Bitstream Vera Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Tavmjong Bah" or the word "Arev". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Tavmjong Bah Arev" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the name of Tavmjong Bah shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from Tavmjong Bah. For further information, contact: tavmjong @ free . fr.http://dejavu.sourceforge.net/wiki/index.php/Licensehttp://dejavu.sourceforge.net/wiki/index.php/LicenseDejaVu SansDejaVu SansBookBookÿ~Z–  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a¬£„…½–膎‹©¤ŠÚƒ“òó—ˆÃÞñžªõôö¢­ÉÇ®bcdËeÈÊÏÌÍÎéfÓÐѯgð‘ÖÔÕhëí‰jikmln oqprsutvwêxzy{}|¸¡~€ìîºýþ    ÿ øù !"#$%&'()*+ú×,-./0123456789:âã;<=>?@ABCDEFGHI°±JKLMNOPQRSûüäåTUVWXYZ[\]^_`abcdefghi»jklmæçnopqrstuvwxyz{|}~€¦‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³Øá´µ¶·¸¹º»¼½¾¿ÀÁÂÃÛÜÝàÙßÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYŸZ[\]^_`abcdefghijklmno›pqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾²³ ¿ À Á¶·Ä ´µÅ ‡ Ä Å Æ« Ç È É Ê Ë Ì ÍÆ Î Ï Ð Ñ Ò Ó Ô Õ¾¿ Ö × Ø Ù Ú Û Ü Ý Þ¼ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ                           ! " # $ % & ' (÷ ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a bŒ c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ                           ! " # $ % & ' ( ) * + , - . /˜ 0 1 2¨ 3 4 5 6 7 8 9 :š ;™ï < = > ? @ A B¥ C D E’ F G H I J K L M N O P Qœ R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m§ n o p q r s t u v w x y z { | } ~  € ‚ ƒ „ … † ‡”• ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ      !"#$%&'()*+,¹-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâÀÁãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•– sfthyphenAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019FOhornohornuni01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01C4uni01C5uni01C6uni01C7uni01C8uni01C9uni01CAuni01CBuni01CCuni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DDuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3uni01E4uni01E5Gcarongcaronuni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F0uni01F1uni01F2uni01F3uni01F4uni01F5uni01F6uni01F7uni01F8uni01F9 Aringacute aringacuteAEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccentuni021Auni021Buni021Cuni021Duni021Euni021Funi0220uni0221uni0222uni0223uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236dotlessjuni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0242uni0243uni0244uni0245uni0246uni0247uni0248uni0249uni024Auni024Buni024Cuni024Duni024Euni024Funi0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Auni025Buni025Cuni025Duni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268uni0269uni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276uni0277uni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294uni0295uni0296uni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2uni02A3uni02A4uni02A5uni02A6uni02A7uni02A8uni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AFuni02B0uni02B1uni02B2uni02B3uni02B4uni02B5uni02B6uni02B7uni02B8uni02B9uni02BAuni02BBuni02BCuni02BDuni02BEuni02BFuni02C0uni02C1uni02C2uni02C3uni02C4uni02C5uni02C8uni02C9uni02CAuni02CBuni02CCuni02CDuni02CEuni02CFuni02D0uni02D1uni02D2uni02D3uni02D4uni02D5uni02D6uni02D7uni02DEuni02DFuni02E0uni02E1uni02E2uni02E3uni02E4uni02E5uni02E6uni02E7uni02E8uni02E9uni02ECuni02EDuni02EEuni02F3uni02F7 gravecomb acutecombuni0302 tildecombuni0304uni0305uni0306uni0307uni0308 hookabovecombuni030Auni030Buni030Cuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320uni0321uni0322 dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334uni0335uni0336uni0337uni0338uni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0340uni0341uni0342uni0343uni0344uni0345uni0346uni0347uni0348uni0349uni034Auni034Buni034Cuni034Duni034Euni034Funi0351uni0352uni0353uni0357uni0358uni035Cuni035Duni035Euni035Funi0360uni0361uni0362uni0374uni0375uni037Auni037Buni037Cuni037Duni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosuni03D0theta1Upsilon1uni03D3uni03D4phi1omega1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03E2uni03E3uni03E4uni03E5uni03E6uni03E7uni03E8uni03E9uni03EAuni03EBuni03ECuni03EDuni03EEuni03EFuni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F7uni03F8uni03F9uni03FAuni03FBuni03FCuni03FDuni03FEuni03FFuni0400uni0401uni0402uni0403uni0404uni0405uni0406uni0407uni0408uni0409uni040Auni040Buni040Cuni040Duni040Euni040Funi0410uni0411uni0412uni0413uni0414uni0415uni0416uni0417uni0418uni0419uni041Auni041Buni041Cuni041Duni041Euni041Funi0420uni0421uni0422uni0423uni0424uni0425uni0426uni0427uni0428uni0429uni042Auni042Buni042Cuni042Duni042Euni042Funi0430uni0431uni0432uni0433uni0434uni0435uni0436uni0437uni0438uni0439uni043Auni043Buni043Cuni043Duni043Euni043Funi0440uni0441uni0442uni0443uni0444uni0445uni0446uni0447uni0448uni0449uni044Auni044Buni044Cuni044Duni044Euni044Funi0450uni0451uni0452uni0453uni0454uni0455uni0456uni0457uni0458uni0459uni045Auni045Buni045Cuni045Duni045Euni045Funi0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0483uni0484uni0485uni0486uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0490uni0491uni0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni0531uni0532uni0533uni0534uni0535uni0536uni0537uni0538uni0539uni053Auni053Buni053Cuni053Duni053Euni053Funi0540uni0541uni0542uni0543uni0544uni0545uni0546uni0547uni0548uni0549uni054Auni054Buni054Cuni054Duni054Euni054Funi0550uni0551uni0552uni0553uni0554uni0555uni0556uni0559uni055Auni055Buni055Cuni055Duni055Euni055Funi0561uni0562uni0563uni0564uni0565uni0566uni0567uni0568uni0569uni056Auni056Buni056Cuni056Duni056Euni056Funi0570uni0571uni0572uni0573uni0574uni0575uni0576uni0577uni0578uni0579uni057Auni057Buni057Cuni057Duni057Euni057Funi0580uni0581uni0582uni0583uni0584uni0585uni0586uni0587uni0589uni058Auni05B0uni05B1uni05B2uni05B3uni05B4uni05B5uni05B6uni05B7uni05B8uni05B9uni05BAuni05BBuni05BCuni05BDuni05BEuni05BFuni05C0uni05C1uni05C2uni05C3uni05C6uni05C7uni05D0uni05D1uni05D2uni05D3uni05D4uni05D5uni05D6uni05D7uni05D8uni05D9uni05DAuni05DBuni05DCuni05DDuni05DEuni05DFuni05E0uni05E1uni05E2uni05E3uni05E4uni05E5uni05E6uni05E7uni05E8uni05E9uni05EAuni05F0uni05F1uni05F2uni05F3uni05F4uni060Cuni0615uni061Buni061Funi0621uni0622uni0623uni0624uni0625uni0626uni0627uni0628uni0629uni062Auni062Buni062Cuni062Duni062Euni062Funi0630uni0631uni0632uni0633uni0634uni0635uni0636uni0637uni0638uni0639uni063Auni0640uni0641uni0642uni0643uni0644uni0645uni0646uni0647uni0648uni0649uni064Auni064Buni064Cuni064Duni064Euni064Funi0650uni0651uni0652uni0653uni0654uni0655uni065Auni0660uni0661uni0662uni0663uni0664uni0665uni0666uni0667uni0668uni0669uni066Auni066Buni066Cuni066Duni066Euni066Funi0674uni0679uni067Auni067Buni067Cuni067Duni067Euni067Funi0680uni0681uni0682uni0683uni0684uni0685uni0686uni0687uni0691uni0692uni0695uni0698uni06A1uni06A4uni06A6uni06A9uni06AFuni06B5uni06BAuni06BFuni06C6uni06CCuni06CEuni06D5uni06F0uni06F1uni06F2uni06F3uni06F4uni06F5uni06F6uni06F7uni06F8uni06F9uni07C0uni07C1uni07C2uni07C3uni07C4uni07C5uni07C6uni07C7uni07C8uni07C9uni07CAuni07CBuni07CCuni07CDuni07CEuni07CFuni07D0uni07D1uni07D2uni07D3uni07D4uni07D5uni07D6uni07D7uni07D8uni07D9uni07DAuni07DBuni07DCuni07DDuni07DEuni07DFuni07E0uni07E1uni07E2uni07E3uni07E4uni07E5uni07E6uni07E7uni07EBuni07ECuni07EDuni07EEuni07EFuni07F0uni07F1uni07F2uni07F3uni07F4uni07F5uni07F8uni07F9uni07FAuni0E3Funi0E81uni0E82uni0E84uni0E87uni0E88uni0E8Auni0E8Duni0E94uni0E95uni0E96uni0E97uni0E99uni0E9Auni0E9Buni0E9Cuni0E9Duni0E9Euni0E9Funi0EA1uni0EA2uni0EA3uni0EA5uni0EA7uni0EAAuni0EABuni0EADuni0EAEuni0EAFuni0EB0uni0EB1uni0EB2uni0EB3uni0EB4uni0EB5uni0EB6uni0EB7uni0EB8uni0EB9uni0EBBuni0EBCuni0EBDuni0EC0uni0EC1uni0EC2uni0EC3uni0EC4uni0EC6uni0EC8uni0EC9uni0ECAuni0ECBuni0ECCuni0ECDuni0ED0uni0ED1uni0ED2uni0ED3uni0ED4uni0ED5uni0ED6uni0ED7uni0ED8uni0ED9uni0EDCuni0EDDuni10A0uni10A1uni10A2uni10A3uni10A4uni10A5uni10A6uni10A7uni10A8uni10A9uni10AAuni10ABuni10ACuni10ADuni10AEuni10AFuni10B0uni10B1uni10B2uni10B3uni10B4uni10B5uni10B6uni10B7uni10B8uni10B9uni10BAuni10BBuni10BCuni10BDuni10BEuni10BFuni10C0uni10C1uni10C2uni10C3uni10C4uni10C5uni10D0uni10D1uni10D2uni10D3uni10D4uni10D5uni10D6uni10D7uni10D8uni10D9uni10DAuni10DBuni10DCuni10DDuni10DEuni10DFuni10E0uni10E1uni10E2uni10E3uni10E4uni10E5uni10E6uni10E7uni10E8uni10E9uni10EAuni10EBuni10ECuni10EDuni10EEuni10EFuni10F0uni10F1uni10F2uni10F3uni10F4uni10F5uni10F6uni10F7uni10F8uni10F9uni10FAuni10FBuni10FCuni1401uni1402uni1403uni1404uni1405uni1406uni1407uni1409uni140Auni140Buni140Cuni140Duni140Euni140Funi1410uni1411uni1412uni1413uni1414uni1415uni1416uni1417uni1418uni1419uni141Auni141Buni141Duni141Euni141Funi1420uni1421uni1422uni1423uni1424uni1425uni1426uni1427uni1428uni1429uni142Auni142Buni142Cuni142Duni142Euni142Funi1430uni1431uni1432uni1433uni1434uni1435uni1437uni1438uni1439uni143Auni143Buni143Cuni143Duni143Euni143Funi1440uni1441uni1442uni1443uni1444uni1445uni1446uni1447uni1448uni1449uni144Auni144Cuni144Duni144Euni144Funi1450uni1451uni1452uni1454uni1455uni1456uni1457uni1458uni1459uni145Auni145Buni145Cuni145Duni145Euni145Funi1460uni1461uni1462uni1463uni1464uni1465uni1466uni1467uni1468uni1469uni146Auni146Buni146Cuni146Duni146Euni146Funi1470uni1471uni1472uni1473uni1474uni1475uni1476uni1477uni1478uni1479uni147Auni147Buni147Cuni147Duni147Euni147Funi1480uni1481uni1482uni1483uni1484uni1485uni1486uni1487uni1488uni1489uni148Auni148Buni148Cuni148Duni148Euni148Funi1490uni1491uni1492uni1493uni1494uni1495uni1496uni1497uni1498uni1499uni149Auni149Buni149Cuni149Duni149Euni149Funi14A0uni14A1uni14A2uni14A3uni14A4uni14A5uni14A6uni14A7uni14A8uni14A9uni14AAuni14ABuni14ACuni14ADuni14AEuni14AFuni14B0uni14B1uni14B2uni14B3uni14B4uni14B5uni14B6uni14B7uni14B8uni14B9uni14BAuni14BBuni14BCuni14BDuni14C0uni14C1uni14C2uni14C3uni14C4uni14C5uni14C6uni14C7uni14C8uni14C9uni14CAuni14CBuni14CCuni14CDuni14CEuni14CFuni14D0uni14D1uni14D2uni14D3uni14D4uni14D5uni14D6uni14D7uni14D8uni14D9uni14DAuni14DBuni14DCuni14DDuni14DEuni14DFuni14E0uni14E1uni14E2uni14E3uni14E4uni14E5uni14E6uni14E7uni14E8uni14E9uni14EAuni14ECuni14EDuni14EEuni14EFuni14F0uni14F1uni14F2uni14F3uni14F4uni14F5uni14F6uni14F7uni14F8uni14F9uni14FAuni14FBuni14FCuni14FDuni14FEuni14FFuni1500uni1501uni1502uni1503uni1504uni1505uni1506uni1507uni1510uni1511uni1512uni1513uni1514uni1515uni1516uni1517uni1518uni1519uni151Auni151Buni151Cuni151Duni151Euni151Funi1520uni1521uni1522uni1523uni1524uni1525uni1526uni1527uni1528uni1529uni152Auni152Buni152Cuni152Duni152Euni152Funi1530uni1531uni1532uni1533uni1534uni1535uni1536uni1537uni1538uni1539uni153Auni153Buni153Cuni153Duni153Euni1540uni1541uni1542uni1543uni1544uni1545uni1546uni1547uni1548uni1549uni154Auni154Buni154Cuni154Duni154Euni154Funi1550uni1552uni1553uni1554uni1555uni1556uni1557uni1558uni1559uni155Auni155Buni155Cuni155Duni155Euni155Funi1560uni1561uni1562uni1563uni1564uni1565uni1566uni1567uni1568uni1569uni156Auni1574uni1575uni1576uni1577uni1578uni1579uni157Auni157Buni157Cuni157Duni157Euni157Funi1580uni1581uni1582uni1583uni1584uni1585uni158Auni158Buni158Cuni158Duni158Euni158Funi1590uni1591uni1592uni1593uni1594uni1595uni1596uni15A0uni15A1uni15A2uni15A3uni15A4uni15A5uni15A6uni15A7uni15A8uni15A9uni15AAuni15ABuni15ACuni15ADuni15AEuni15AFuni15DEuni15E1uni1646uni1647uni166Euni166Funi1670uni1671uni1672uni1673uni1674uni1675uni1676uni1680uni1681uni1682uni1683uni1684uni1685uni1686uni1687uni1688uni1689uni168Auni168Buni168Cuni168Duni168Euni168Funi1690uni1691uni1692uni1693uni1694uni1695uni1696uni1697uni1698uni1699uni169Auni169Buni169Cuni1D00uni1D01uni1D02uni1D03uni1D04uni1D05uni1D06uni1D07uni1D08uni1D09uni1D0Auni1D0Buni1D0Cuni1D0Duni1D0Euni1D0Funi1D10uni1D11uni1D12uni1D13uni1D14uni1D16uni1D17uni1D18uni1D19uni1D1Auni1D1Buni1D1Cuni1D1Duni1D1Euni1D1Funi1D20uni1D21uni1D22uni1D23uni1D26uni1D27uni1D28uni1D29uni1D2Auni1D2Buni1D2Cuni1D2Duni1D2Euni1D30uni1D31uni1D32uni1D33uni1D34uni1D35uni1D36uni1D37uni1D38uni1D39uni1D3Auni1D3Buni1D3Cuni1D3Duni1D3Euni1D3Funi1D40uni1D41uni1D42uni1D43uni1D44uni1D45uni1D46uni1D47uni1D48uni1D49uni1D4Auni1D4Buni1D4Cuni1D4Duni1D4Euni1D4Funi1D50uni1D51uni1D52uni1D53uni1D54uni1D55uni1D56uni1D57uni1D58uni1D59uni1D5Auni1D5Buni1D5Duni1D5Euni1D5Funi1D60uni1D61uni1D62uni1D63uni1D64uni1D65uni1D66uni1D67uni1D68uni1D69uni1D6Auni1D77uni1D78uni1D7Buni1D85uni1D9Buni1D9Cuni1D9Duni1D9Euni1D9Funi1DA0uni1DA1uni1DA2uni1DA3uni1DA4uni1DA5uni1DA6uni1DA7uni1DA8uni1DA9uni1DAAuni1DABuni1DACuni1DADuni1DAEuni1DAFuni1DB0uni1DB1uni1DB2uni1DB3uni1DB4uni1DB5uni1DB6uni1DB7uni1DB8uni1DB9uni1DBAuni1DBBuni1DBCuni1DBDuni1DBEuni1DBFuni1DC4uni1DC5uni1DC6uni1DC7uni1DC8uni1DC9uni1E00uni1E01uni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Euni1E2Funi1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3Funi1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E64uni1E65uni1E66uni1E67uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Auni1E9Buni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1Ygraveygraveuni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEuni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200Buni200Cuni200Duni200Euni200Funi2010uni2011 figuredashuni2015uni2016 underscoredbl quotereverseduni201Funi2023onedotenleadertwodotenleaderuni2027uni202Auni202Buni202Cuni202Duni202Euni202Funi2031minuteseconduni2034uni2035uni2036uni2037uni2038uni203B exclamdbluni203Duni203Euni203Funi2040uni2041uni2042uni2043uni2045uni2046uni2047uni2048uni2049uni204Auni204Buni204Cuni204Duni204Euni204Funi2050uni2051uni2052uni2053uni2054uni2055uni2056uni2057uni2058uni2059uni205Auni205Buni205Cuni205Duni205Euni205Funi2060uni2061uni2062uni2063uni206Auni206Buni206Cuni206Duni206Euni206Funi2070uni2071uni2074uni2075uni2076uni2077uni2078uni2079uni207Auni207Buni207Cuni207Duni207Euni207Funi2080uni2081uni2082uni2083uni2084uni2085uni2086uni2087uni2088uni2089uni208Auni208Buni208Cuni208Duni208Euni2090uni2091uni2092uni2093uni2094uni20A0 colonmonetaryuni20A2lirauni20A5uni20A6pesetauni20A8uni20A9uni20AAdongEurouni20ADuni20AEuni20AFuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5uni20D0uni20D1uni20D6uni20D7uni20DBuni20DCuni20E1uni2100uni2101uni2102uni2103uni2104uni2105uni2106uni2107uni2108uni2109uni210Buni210Cuni210Duni210Euni210Funi2110Ifrakturuni2112uni2113uni2114uni2115uni2116uni2117 weierstrassuni2119uni211Auni211BRfrakturuni211D prescriptionuni211Funi2120uni2121uni2123uni2124uni2125uni2126uni2127uni2128uni2129uni212Auni212Buni212Cuni212D estimateduni212Funi2130uni2131uni2132uni2133uni2134alephuni2136uni2137uni2138uni2139uni213Auni213Buni213Cuni213Duni213Euni213Funi2140uni2141uni2142uni2143uni2144uni2145uni2146uni2147uni2148uni2149uni214Buni214Eonethird twothirdsuni2155uni2156uni2157uni2158uni2159uni215A oneeighth threeeighths fiveeighths seveneighthsuni215Funi2160uni2161uni2162uni2163uni2164uni2165uni2166uni2167uni2168uni2169uni216Auni216Buni216Cuni216Duni216Euni216Funi2170uni2171uni2172uni2173uni2174uni2175uni2176uni2177uni2178uni2179uni217Auni217Buni217Cuni217Duni217Euni217Funi2180uni2181uni2182uni2183uni2184 arrowleftarrowup arrowright arrowdown arrowboth arrowupdnuni2196uni2197uni2198uni2199uni219Auni219Buni219Cuni219Duni219Euni219Funi21A0uni21A1uni21A2uni21A3uni21A4uni21A5uni21A6uni21A7 arrowupdnbseuni21A9uni21AAuni21ABuni21ACuni21ADuni21AEuni21AFuni21B0uni21B1uni21B2uni21B3uni21B4carriagereturnuni21B6uni21B7uni21B8uni21B9uni21BAuni21BBuni21BCuni21BDuni21BEuni21BFuni21C0uni21C1uni21C2uni21C3uni21C4uni21C5uni21C6uni21C7uni21C8uni21C9uni21CAuni21CBuni21CCuni21CDuni21CEuni21CF arrowdblleft arrowdblup arrowdblright arrowdbldown arrowdblbothuni21D5uni21D6uni21D7uni21D8uni21D9uni21DAuni21DBuni21DCuni21DDuni21DEuni21DFuni21E0uni21E1uni21E2uni21E3uni21E4uni21E5uni21E6uni21E7uni21E8uni21E9uni21EAuni21EBuni21ECuni21EDuni21EEuni21EFuni21F0uni21F1uni21F2uni21F3uni21F4uni21F5uni21F6uni21F7uni21F8uni21F9uni21FAuni21FBuni21FCuni21FDuni21FEuni21FF universaluni2201 existentialuni2204emptysetgradientelement notelementuni220Asuchthatuni220Cuni220Duni220Euni2210uni2213uni2214uni2215uni2216 asteriskmathuni2218uni2219uni221Buni221C proportional orthogonalangleuni2221uni2222uni2223uni2224uni2225uni2226 logicaland logicalor intersectionunionuni222Cuni222Duni222Euni222Funi2230uni2231uni2232uni2233 thereforeuni2235uni2236uni2237uni2238uni2239uni223Auni223Bsimilaruni223Duni223Euni223Funi2240uni2241uni2242uni2243uni2244 congruentuni2246uni2247uni2249uni224Auni224Buni224Cuni224Duni224Euni224Funi2250uni2251uni2252uni2253uni2254uni2255uni2256uni2257uni2258uni2259uni225Auni225Buni225Cuni225Duni225Euni225F equivalenceuni2262uni2263uni2266uni2267uni2268uni2269uni226Auni226Buni226Cuni226Duni226Euni226Funi2270uni2271uni2272uni2273uni2274uni2275uni2276uni2277uni2278uni2279uni227Auni227Buni227Cuni227Duni227Euni227Funi2280uni2281 propersubsetpropersuperset notsubsetuni2285 reflexsubsetreflexsupersetuni2288uni2289uni228Auni228Buni228Cuni228Duni228Euni228Funi2290uni2291uni2292uni2293uni2294 circleplusuni2296circlemultiplyuni2298uni2299uni229Auni229Buni229Cuni229Duni229Euni229Funi22A0uni22A1uni22A2uni22A3uni22A4 perpendicularuni22A6uni22A7uni22A8uni22A9uni22AAuni22ABuni22ACuni22ADuni22AEuni22AFuni22B2uni22B3uni22B4uni22B5uni22B6uni22B7uni22B8uni22B9uni22BAuni22BBuni22BCuni22BDuni22BEuni22BFuni22C0uni22C1uni22C2uni22C3uni22C4dotmathuni22C6uni22C8uni22C9uni22CAuni22CBuni22CCuni22CDuni22D6uni22D7uni22D8uni22D9uni22DAuni22DBuni22DCuni22DDuni22DEuni22DFuni22E0uni22E1uni22E2uni22E3uni22E4uni22E5uni22E6uni22E7uni22E8uni22E9uni22EAuni22EBuni22ECuni22EDuni22EEuni22EFuni22F0uni22F1uni22F2uni22F3uni22F4uni22F5uni22F6uni22F7uni22F8uni22F9uni22FAuni22FBuni22FCuni22FDuni22FEuni22FFuni2300uni2301houseuni2303uni2304uni2305uni2306uni2307uni2308uni2309uni230Auni230Buni230Cuni230Duni230Euni230F revlogicalnotuni2311uni2318uni2319uni231Cuni231Duni231Euni231F integraltp integralbtuni2324uni2325uni2326uni2327uni2328uni232Buni232Cuni2373uni2374uni2375uni237Auni237Duni2387uni2394uni239Buni239Cuni239Duni239Euni239Funi23A0uni23A1uni23A2uni23A3uni23A4uni23A5uni23A6uni23A7uni23A8uni23A9uni23AAuni23ABuni23ACuni23ADuni23AEuni23CEuni23CFuni23E3uni23E5uni2422uni2423uni2460uni2461uni2462uni2463uni2464uni2465uni2466uni2467uni2468uni2469SF100000uni2501SF110000uni2503uni2504uni2505uni2506uni2507uni2508uni2509uni250Auni250BSF010000uni250Duni250Euni250FSF030000uni2511uni2512uni2513SF020000uni2515uni2516uni2517SF040000uni2519uni251Auni251BSF080000uni251Duni251Euni251Funi2520uni2521uni2522uni2523SF090000uni2525uni2526uni2527uni2528uni2529uni252Auni252BSF060000uni252Duni252Euni252Funi2530uni2531uni2532uni2533SF070000uni2535uni2536uni2537uni2538uni2539uni253Auni253BSF050000uni253Duni253Euni253Funi2540uni2541uni2542uni2543uni2544uni2545uni2546uni2547uni2548uni2549uni254Auni254Buni254Cuni254Duni254Euni254FSF430000SF240000SF510000SF520000SF390000SF220000SF210000SF250000SF500000SF490000SF380000SF280000SF270000SF260000SF360000SF370000SF420000SF190000SF200000SF230000SF470000SF480000SF410000SF450000SF460000SF400000SF540000SF530000SF440000uni256Duni256Euni256Funi2570uni2571uni2572uni2573uni2574uni2575uni2576uni2577uni2578uni2579uni257Auni257Buni257Cuni257Duni257Euni257Fupblockuni2581uni2582uni2583dnblockuni2585uni2586uni2587blockuni2589uni258Auni258Blfblockuni258Duni258Euni258Frtblockltshadeshadedkshadeuni2594uni2595uni2596uni2597uni2598uni2599uni259Auni259Buni259Cuni259Duni259Euni259F filledboxH22073uni25A2uni25A3uni25A4uni25A5uni25A6uni25A7uni25A8uni25A9H18543H18551 filledrectuni25ADuni25AEuni25AFuni25B0uni25B1triagupuni25B3uni25B4uni25B5uni25B6uni25B7uni25B8uni25B9triagrtuni25BBtriagdnuni25BDuni25BEuni25BFuni25C0uni25C1uni25C2uni25C3triaglfuni25C5uni25C6uni25C7uni25C8uni25C9circleuni25CCuni25CDuni25CEH18533uni25D0uni25D1uni25D2uni25D3uni25D4uni25D5uni25D6uni25D7 invbullet invcircleuni25DAuni25DBuni25DCuni25DDuni25DEuni25DFuni25E0uni25E1uni25E2uni25E3uni25E4uni25E5 openbulletuni25E7uni25E8uni25E9uni25EAuni25EBuni25ECuni25EDuni25EEuni25EFuni25F0uni25F1uni25F2uni25F3uni25F4uni25F5uni25F6uni25F7uni25F8uni25F9uni25FAuni25FBuni25FCuni25FDuni25FEuni25FFuni2600uni2601uni2602uni2603uni2604uni2605uni2606uni2607uni2608uni2609uni260Auni260Buni260Cuni260Duni260Euni260Funi2610uni2611uni2612uni2613uni2614uni2615uni2616uni2617uni2618uni2619uni261Auni261Buni261Cuni261Duni261Euni261Funi2620uni2621uni2622uni2623uni2624uni2625uni2626uni2627uni2628uni2629uni262Auni262Buni262Cuni262Duni262Euni262Funi2630uni2631uni2632uni2633uni2634uni2635uni2636uni2637uni2638uni2639 smileface invsmilefacesununi263Duni263Euni263Ffemaleuni2641maleuni2643uni2644uni2645uni2646uni2647uni2648uni2649uni264Auni264Buni264Cuni264Duni264Euni264Funi2650uni2651uni2652uni2653uni2654uni2655uni2656uni2657uni2658uni2659uni265Auni265Buni265Cuni265Duni265Euni265Fspadeuni2661uni2662clubuni2664heartdiamonduni2667uni2668uni2669 musicalnotemusicalnotedbluni266Cuni266Duni266Euni266Funi2670uni2671uni2672uni2673uni2674uni2675uni2676uni2677uni2678uni2679uni267Auni267Buni267Cuni267Duni267Euni267Funi2680uni2681uni2682uni2683uni2684uni2685uni2686uni2687uni2688uni2689uni268Auni268Buni268Cuni268Duni268Euni268Funi2690uni2691uni2692uni2693uni2694uni2695uni2696uni2697uni2698uni2699uni269Auni269Buni269Cuni26A0uni26A1uni26A2uni26A3uni26A4uni26A5uni26A6uni26A7uni26A8uni26A9uni26AAuni26ABuni26ACuni26ADuni26AEuni26AFuni26B0uni26B1uni26B2uni2701uni2702uni2703uni2704uni2706uni2707uni2708uni2709uni270Cuni270Duni270Euni270Funi2710uni2711uni2712uni2713uni2714uni2715uni2716uni2717uni2718uni2719uni271Auni271Buni271Cuni271Duni271Euni271Funi2720uni2721uni2722uni2723uni2724uni2725uni2726uni2727uni2729uni272Auni272Buni272Cuni272Duni272Euni272Funi2730uni2731uni2732uni2733uni2734uni2735uni2736uni2737uni2738uni2739uni273Auni273Buni273Cuni273Duni273Euni273Funi2740uni2741uni2742uni2743uni2744uni2745uni2746uni2747uni2748uni2749uni274Auni274Buni274Duni274Funi2750uni2751uni2752uni2756uni2758uni2759uni275Auni275Buni275Cuni275Duni275Euni2761uni2762uni2763uni2764uni2765uni2766uni2767uni2768uni2769uni276Auni276Buni276Cuni276Duni276Euni276Funi2770uni2771uni2772uni2773uni2774uni2775uni2776uni2777uni2778uni2779uni277Auni277Buni277Cuni277Duni277Euni277Funi2780uni2781uni2782uni2783uni2784uni2785uni2786uni2787uni2788uni2789uni278Auni278Buni278Cuni278Duni278Euni278Funi2790uni2791uni2792uni2793uni2794uni2798uni2799uni279Auni279Buni279Cuni279Duni279Euni279Funi27A0uni27A1uni27A2uni27A3uni27A4uni27A5uni27A6uni27A7uni27A8uni27A9uni27AAuni27ABuni27ACuni27ADuni27AEuni27AFuni27B1uni27B2uni27B3uni27B4uni27B5uni27B6uni27B7uni27B8uni27B9uni27BAuni27BBuni27BCuni27BDuni27BEuni27E0uni27E6uni27E7uni27E8uni27E9uni27EAuni27EBuni27F0uni27F1uni27F2uni27F3uni27F4uni27F5uni27F6uni27F7uni27F8uni27F9uni27FAuni27FBuni27FCuni27FDuni27FEuni27FFuni2800uni2801uni2802uni2803uni2804uni2805uni2806uni2807uni2808uni2809uni280Auni280Buni280Cuni280Duni280Euni280Funi2810uni2811uni2812uni2813uni2814uni2815uni2816uni2817uni2818uni2819uni281Auni281Buni281Cuni281Duni281Euni281Funi2820uni2821uni2822uni2823uni2824uni2825uni2826uni2827uni2828uni2829uni282Auni282Buni282Cuni282Duni282Euni282Funi2830uni2831uni2832uni2833uni2834uni2835uni2836uni2837uni2838uni2839uni283Auni283Buni283Cuni283Duni283Euni283Funi2840uni2841uni2842uni2843uni2844uni2845uni2846uni2847uni2848uni2849uni284Auni284Buni284Cuni284Duni284Euni284Funi2850uni2851uni2852uni2853uni2854uni2855uni2856uni2857uni2858uni2859uni285Auni285Buni285Cuni285Duni285Euni285Funi2860uni2861uni2862uni2863uni2864uni2865uni2866uni2867uni2868uni2869uni286Auni286Buni286Cuni286Duni286Euni286Funi2870uni2871uni2872uni2873uni2874uni2875uni2876uni2877uni2878uni2879uni287Auni287Buni287Cuni287Duni287Euni287Funi2880uni2881uni2882uni2883uni2884uni2885uni2886uni2887uni2888uni2889uni288Auni288Buni288Cuni288Duni288Euni288Funi2890uni2891uni2892uni2893uni2894uni2895uni2896uni2897uni2898uni2899uni289Auni289Buni289Cuni289Duni289Euni289Funi28A0uni28A1uni28A2uni28A3uni28A4uni28A5uni28A6uni28A7uni28A8uni28A9uni28AAuni28ABuni28ACuni28ADuni28AEuni28AFuni28B0uni28B1uni28B2uni28B3uni28B4uni28B5uni28B6uni28B7uni28B8uni28B9uni28BAuni28BBuni28BCuni28BDuni28BEuni28BFuni28C0uni28C1uni28C2uni28C3uni28C4uni28C5uni28C6uni28C7uni28C8uni28C9uni28CAuni28CBuni28CCuni28CDuni28CEuni28CFuni28D0uni28D1uni28D2uni28D3uni28D4uni28D5uni28D6uni28D7uni28D8uni28D9uni28DAuni28DBuni28DCuni28DDuni28DEuni28DFuni28E0uni28E1uni28E2uni28E3uni28E4uni28E5uni28E6uni28E7uni28E8uni28E9uni28EAuni28EBuni28ECuni28EDuni28EEuni28EFuni28F0uni28F1uni28F2uni28F3uni28F4uni28F5uni28F6uni28F7uni28F8uni28F9uni28FAuni28FBuni28FCuni28FDuni28FEuni28FFuni2906uni2907uni290Auni290Buni2940uni2941uni2983uni2984uni29CEuni29CFuni29D0uni29D1uni29D2uni29D3uni29D4uni29D5uni29EBuni29FAuni29FBuni2A00uni2A01uni2A02uni2A0Cuni2A0Duni2A0Euni2A0Funi2A10uni2A11uni2A12uni2A13uni2A14uni2A15uni2A16uni2A17uni2A18uni2A19uni2A1Auni2A1Buni2A1Cuni2A2Funi2A7Duni2A7Euni2A7Funi2A80uni2A81uni2A82uni2A83uni2A84uni2A85uni2A86uni2A87uni2A88uni2A89uni2A8Auni2A8Buni2A8Cuni2A8Duni2A8Euni2A8Funi2A90uni2A91uni2A92uni2A93uni2A94uni2A95uni2A96uni2A97uni2A98uni2A99uni2A9Auni2A9Buni2A9Cuni2A9Duni2A9Euni2A9Funi2AA0uni2AAEuni2AAFuni2AB0uni2AB1uni2AB2uni2AB3uni2AB4uni2AB5uni2AB6uni2AB7uni2AB8uni2AB9uni2ABAuni2AF9uni2AFAuni2B00uni2B01uni2B02uni2B03uni2B04uni2B05uni2B06uni2B07uni2B08uni2B09uni2B0Auni2B0Buni2B0Cuni2B0Duni2B0Euni2B0Funi2B10uni2B11uni2B12uni2B13uni2B14uni2B15uni2B16uni2B17uni2B18uni2B19uni2B1Auni2B20uni2B21uni2B22uni2B23uni2C60uni2C61uni2C62uni2C63uni2C64uni2C65uni2C66uni2C67uni2C68uni2C69uni2C6Auni2C6Buni2C6Cuni2C6Duni2C6Euni2C6Funi2C71uni2C72uni2C73uni2C74uni2C75uni2C76uni2C77uni2D30uni2D31uni2D32uni2D33uni2D34uni2D35uni2D36uni2D37uni2D38uni2D39uni2D3Auni2D3Buni2D3Cuni2D3Duni2D3Euni2D3Funi2D40uni2D41uni2D42uni2D43uni2D44uni2D45uni2D46uni2D47uni2D48uni2D49uni2D4Auni2D4Buni2D4Cuni2D4Duni2D4Euni2D4Funi2D50uni2D51uni2D52uni2D53uni2D54uni2D55uni2D56uni2D57uni2D58uni2D59uni2D5Auni2D5Buni2D5Cuni2D5Duni2D5Euni2D5Funi2D60uni2D61uni2D62uni2D63uni2D64uni2D65uni2D6Funi4DC0uni4DC1uni4DC2uni4DC3uni4DC4uni4DC5uni4DC6uni4DC7uni4DC8uni4DC9uni4DCAuni4DCBuni4DCCuni4DCDuni4DCEuni4DCFuni4DD0uni4DD1uni4DD2uni4DD3uni4DD4uni4DD5uni4DD6uni4DD7uni4DD8uni4DD9uni4DDAuni4DDBuni4DDCuni4DDDuni4DDEuni4DDFuni4DE0uni4DE1uni4DE2uni4DE3uni4DE4uni4DE5uni4DE6uni4DE7uni4DE8uni4DE9uni4DEAuni4DEBuni4DECuni4DEDuni4DEEuni4DEFuni4DF0uni4DF1uni4DF2uni4DF3uni4DF4uni4DF5uni4DF6uni4DF7uni4DF8uni4DF9uni4DFAuni4DFBuni4DFCuni4DFDuni4DFEuni4DFFuniF000uniF001uniF6C5uniFB00uniFB03uniFB04uniFB05uniFB06uniFB13uniFB14uniFB15uniFB16uniFB17uniFB1DuniFB1EuniFB1FuniFB20uniFB21uniFB22uniFB23uniFB24uniFB25uniFB26uniFB27uniFB28uniFB29uniFB2AuniFB2BuniFB2CuniFB2DuniFB2EuniFB2FuniFB30uniFB31uniFB32uniFB33uniFB34uniFB35uniFB36uniFB38uniFB39uniFB3AuniFB3BuniFB3CuniFB3EuniFB40uniFB41uniFB43uniFB44uniFB46uniFB47uniFB48uniFB49uniFB4AuniFB4BuniFB4CuniFB4DuniFB4EuniFB4FuniFB52uniFB53uniFB54uniFB55uniFB56uniFB57uniFB58uniFB59uniFB5AuniFB5BuniFB5CuniFB5DuniFB5EuniFB5FuniFB60uniFB61uniFB62uniFB63uniFB64uniFB65uniFB66uniFB67uniFB68uniFB69uniFB6AuniFB6BuniFB6CuniFB6DuniFB6EuniFB6FuniFB70uniFB71uniFB72uniFB73uniFB74uniFB75uniFB76uniFB77uniFB78uniFB79uniFB7AuniFB7BuniFB7CuniFB7DuniFB7EuniFB7FuniFB80uniFB81uniFB8AuniFB8BuniFB8CuniFB8DuniFB8EuniFB8FuniFB90uniFB91uniFB92uniFB93uniFB94uniFB95uniFB9EuniFB9FuniFBD9uniFBDAuniFBE8uniFBE9uniFBFCuniFBFDuniFBFEuniFBFFuniFE00uniFE01uniFE02uniFE03uniFE04uniFE05uniFE06uniFE07uniFE08uniFE09uniFE0AuniFE0BuniFE0CuniFE0DuniFE0EuniFE0FuniFE20uniFE21uniFE22uniFE23uniFE70uniFE71uniFE72uniFE73uniFE74uniFE76uniFE77uniFE78uniFE79uniFE7AuniFE7BuniFE7CuniFE7DuniFE7EuniFE7FuniFE80uniFE81uniFE82uniFE83uniFE84uniFE85uniFE86uniFE87uniFE88uniFE89uniFE8AuniFE8BuniFE8CuniFE8DuniFE8EuniFE8FuniFE90uniFE91uniFE92uniFE93uniFE94uniFE95uniFE96uniFE97uniFE98uniFE99uniFE9AuniFE9BuniFE9CuniFE9DuniFE9EuniFE9FuniFEA0uniFEA1uniFEA2uniFEA3uniFEA4uniFEA5uniFEA6uniFEA7uniFEA8uniFEA9uniFEAAuniFEABuniFEACuniFEADuniFEAEuniFEAFuniFEB0uniFEB1uniFEB2uniFEB3uniFEB4uniFEB5uniFEB6uniFEB7uniFEB8uniFEB9uniFEBAuniFEBBuniFEBCuniFEBDuniFEBEuniFEBFuniFEC0uniFEC1uniFEC2uniFEC3uniFEC4uniFEC5uniFEC6uniFEC7uniFEC8uniFEC9uniFECAuniFECBuniFECCuniFECDuniFECEuniFECFuniFED0uniFED1uniFED2uniFED3uniFED4uniFED5uniFED6uniFED7uniFED8uniFED9uniFEDAuniFEDBuniFEDCuniFEDDuniFEDEuniFEDFuniFEE0uniFEE1uniFEE2uniFEE3uniFEE4uniFEE5uniFEE6uniFEE7uniFEE8uniFEE9uniFEEAuniFEEBuniFEECuniFEEDuniFEEEuniFEEFuniFEF0uniFEF1uniFEF2uniFEF3uniFEF4uniFEF5uniFEF6uniFEF7uniFEF8uniFEF9uniFEFAuniFEFBuniFEFCuniFEFFuniFFF9uniFFFAuniFFFBuniFFFCuniFFFDu1D300u1D301u1D302u1D303u1D304u1D305u1D306u1D307u1D308u1D309u1D30Au1D30Bu1D30Cu1D30Du1D30Eu1D30Fu1D310u1D311u1D312u1D313u1D314u1D315u1D316u1D317u1D318u1D319u1D31Au1D31Bu1D31Cu1D31Du1D31Eu1D31Fu1D320u1D321u1D322u1D323u1D324u1D325u1D326u1D327u1D328u1D329u1D32Au1D32Bu1D32Cu1D32Du1D32Eu1D32Fu1D330u1D331u1D332u1D333u1D334u1D335u1D336u1D337u1D338u1D339u1D33Au1D33Bu1D33Cu1D33Du1D33Eu1D33Fu1D340u1D341u1D342u1D343u1D344u1D345u1D346u1D347u1D348u1D349u1D34Au1D34Bu1D34Cu1D34Du1D34Eu1D34Fu1D350u1D351u1D352u1D353u1D354u1D355u1D356u1D538u1D539u1D53Bu1D53Cu1D53Du1D53Eu1D540u1D541u1D542u1D543u1D544u1D546u1D54Au1D54Bu1D54Cu1D54Du1D54Eu1D54Fu1D550u1D552u1D553u1D554u1D555u1D556u1D557u1D558u1D559u1D55Au1D55Bu1D55Cu1D55Du1D55Eu1D55Fu1D560u1D561u1D562u1D563u1D564u1D565u1D566u1D567u1D568u1D569u1D56Au1D56Bu1D5A0u1D5A1u1D5A2u1D5A3u1D5A4u1D5A5u1D5A6u1D5A7u1D5A8u1D5A9u1D5AAu1D5ABu1D5ACu1D5ADu1D5AEu1D5AFu1D5B0u1D5B1u1D5B2u1D5B3u1D5B4u1D5B5u1D5B6u1D5B7u1D5B8u1D5B9u1D5BAu1D5BBu1D5BCu1D5BDu1D5BEu1D5BFu1D5C0u1D5C1u1D5C2u1D5C3u1D5C4u1D5C5u1D5C6u1D5C7u1D5C8u1D5C9u1D5CAu1D5CBu1D5CCu1D5CDu1D5CEu1D5CFu1D5D0u1D5D1u1D5D2u1D5D3u1D7E2u1D7E3u1D7E4u1D7E5u1D7E6u1D7E7u1D7E8u1D7E9u1D7EAu1D7EB dlLtcaronDieresisAcuteTildeGrave CircumflexCaron uni0311.caseBreve Dotaccent Hungarumlaut Doubleacute arabic_dot arabic_2dots arabic_3dotsarabic_3dots_aarabic_2dots_a arabic_4dots uni066E.fina uni066E.init uni066E.medi uni06A1.fina uni06A1.init uni06A1.medi uni066F.fina uni066F.init uni066F.medi uni06BA.init uni06BA.medi arabic_ring uni067C.fina uni067C.init uni067C.medi uni067D.fina uni067D.init uni067D.medi uni0681.fina uni0681.init uni0681.medi uni0682.fina uni0682.init uni0682.medi uni0685.fina uni0685.init uni0685.medi uni06BF.fina uni06BF.init uni06BF.mediarabic_gaf_barEng.altuni0268.dotlessuni029D.dotless uni03080304 uni03040308 uni03070304 uni03080301 uni03080300 uni03040301 uni03040300 uni03030304 uni0308030C uni03030308 uni030C0307 uni03030301 uni03020301 uni03020300 uni03020303 uni03060303 uni03060301 uni03060300 uni03060309 uni03020309 uni03010307 brailledotJ.alt uni0695.finauniFEAE.fina.longstart uni06B5.fina uni06B5.init uni06B5.medi uni06CE.fina uni06CE.init uni06CE.medi uni0692.final.alt uni06D5.finauni0478.monographuni0479.monographiogonek.dotlessuni2148.dotlessuni2149.dotlessuni1E2D.dotlessuni1ECB.dotlessdcoI.alt arrow.base uni0651064B uni0651064C uni064B0651 uni0651064E uni0651064F uni064E0651 uni0654064E uni0654064F uni07CA.fina uni07CA.medi uni07CA.init uni07CB.fina uni07CB.medi uni07CB.init uni07CC.fina uni07CC.medi uni07CC.init uni07CD.fina uni07CD.medi uni07CD.init uni07CE.fina uni07CE.medi uni07CE.init uni07CF.fina uni07CF.medi uni07CF.init uni07D0.fina uni07D0.medi uni07D0.init uni07D1.fina uni07D1.medi uni07D1.init uni07D2.fina uni07D2.medi uni07D2.init uni07D3.fina uni07D3.medi uni07D3.init uni07D4.fina uni07D4.medi uni07D4.init uni07D5.fina uni07D5.medi uni07D5.init uni07D6.fina uni07D6.medi uni07D6.init uni07D7.fina uni07D7.medi uni07D7.init uni07D8.fina uni07D8.medi uni07D8.init uni07D9.fina uni07D9.medi uni07D9.init uni07DA.fina uni07DA.medi uni07DA.init uni07DB.fina uni07DB.medi uni07DB.init uni07DC.fina uni07DC.medi uni07DC.init uni07DD.fina uni07DD.medi uni07DD.init uni07DE.fina uni07DE.medi uni07DE.init uni07DF.fina uni07DF.medi uni07DF.init uni07E0.fina uni07E0.medi uni07E0.init uni07E1.fina uni07E1.medi uni07E1.init uni07E2.fina uni07E2.medi uni07E2.init uni07E3.fina uni07E3.medi uni07E3.init uni07E4.fina uni07E4.medi uni07E4.init uni07E5.fina uni07E5.medi uni07E5.init uni07E6.fina uni07E6.medi uni07E6.init uni07E7.fina uni07E7.medi uni07E7.init Ringabove uni2630.alt uni2631.alt uni2632.alt uni2633.alt uni2634.alt uni2635.alt uni2636.alt uni2637.alt uni047E.diacuni048A.brevelessuni048B.breveless uni2E18.u51¸€@ÿûþúù%ø2÷–öõþôþó%òñ–ð%ïŠAïþî–í–ìúëúêþé:èBçþæ2åäSå–äŠAäSãâ/ãúâ/áþàþß2ÞÝ–ÜþÛÚ}Ù»ØþÖŠAÖ}ÕÔGÕ}ÔGÓÒÓþÒÑþÐþÏþÎþÍ–ÌËÌþËÊ2ÉþÆ…ÆÅÄþÃþÂþÁþÀþ¿þ¾þ½þ¼þ»þº¹†%¹þ¸·»¸þ·¶]·»·€¶µ%¶]@ÿ¶@µ%´þ³–²þ±þ°þ¯þ®d­¬«%¬d«ª«%ª©ŠA©ú¨þ§þ¦þ¥¤þ£¢£2¢¡d ŠA –Ÿþž žþ œ›œd›š›š™ ˜þ—– —þ– •ŠA•–”“”(“’ú‘»‘þ]»€Ž%]@Ž%þŒ‹.Œþ‹.І%ŠA‰ˆ ‰ˆ ‡†%‡d†…†%…„þƒ‚ƒþ‚þ€þþ@ÿ~}}~þ}}|d{T{%zþyþxw v uþtúsúrúqúpþoþnþl!kþjBjSiþh}gBfþeþdþcþbþa:`ú^ ]þ[þZþYX YúX WW2VþUTUBTSSRQJQþP OþNMNþMLþKJKþJIJI IH GþF–E–DþC-CúB»AK@þ?þ>=>=<=<; <@ÿ; :þ9þ878ú76765 65 43 21 2þ1 0/ 0 / .- .- ,2+*%+d*)*%)('%(A'%&% &% $þ#þ"!! dú d BþúBBþdþþþþBþ-B}dþ  þ   þ  þ-þdþ@-þ-þ¸d…++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++fonts { font: "DejaVuSans.ttf" "ecdb/default"; } MAINTAINERCLEANFILES= Makefile.in EXTRA_DIST= burn_data.edc \ burn_image.edc \ button.edc \ check.edc \ combo.edc \ entry.edc \ erase_disc.edc \ label.edc \ welcome_page.edc \ capacity.edc \ window.edc \ config_inwin.edc \ about.edc /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/about"; min: 300 380; max: 300 380; styles { style { name: "about_style"; base: "font=ecdb/default font_size=10 align=center color=#000 style=plain wrap=word"; tag: "e" "+ font=ecdb/default:style=Bold color=#000 style=soft_shadow shadow_color=@0000001f"; tag: "hilight" "+ font=ecdb/default:style=Bold text_class=tb_light color=#f00 style=soft_shadow shadow_color=@0000001f"; tag: "br" "\n"; } } parts { part { name: "background"; mouse_events: 0; scale: 1; description { state: "default" 0.0; min: 300 380; max: 300 380; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } image { normal: "background.png"; } } } part { name: "logo"; type: IMAGE; scale: 1; description { state: "default" 0; align: 0.5 0.05; min: 83 83; max: 83 83; image { normal: "logo.png"; } } } part { name: "ecdb.about.text"; type: TEXTBLOCK; mouse_events: 0; scale: 1; description { state: "default" 0.0; rel1 { to_y: "logo"; relative: 0.0 1.0; offset: 32 17; } rel2 { to_y: "button.base"; relative: 1.0 0.0; offset: -33 -17; } text { style: "about_style"; min: 0 1; text: "text"; } } } part { name: "button.base"; type: IMAGE; mouse_events: 1; scale: 1; description { state: "default" 0.0; min: 64 32; max: 64 32; align: 0.5 1.0; fixed: 1 1; color: 255 255 255 178; rel1 { relative: 0.0 1.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -6; } image { normal: "button_base.png"; border: 7 7 7 7; } } } part { name: "button.button_clip"; type: RECT; mouse_events: 1; description { state: "default" 0.0; color: 255 255 255 255; rel1 { to: "button.base"; relative: 0.0 0.0; offset: 3 1; } rel2 { to: "button.base"; relative: 1.0 1.0; offset: -4 -2; } } } part { name: "button.focus_clip"; type: RECT; mouse_events: 1; description { state: "default" 0.0; color: 255 255 255 0; rel1 { to: "button.button"; offset: -1 0; } rel2 { to: "button.button"; offset: 0 -1; } } description { state: "visible" 0.0; inherit: "default" 0.0; color: 255 255 255 255; } } part { name: "button.focus"; type: IMAGE; mouse_events: 1; clip_to: "button.focus_clip"; scale: 1; description { state: "default" 0.0; rel1 { to: "button.focus_clip"; } rel2 { to: "button.focus_clip"; } image { normal: "focus.png"; border: 7 7 7 7; } } } part { name: "button.button"; type: IMAGE; mouse_events: 1; clip_to: "button.button_clip"; scale: 1; description { state: "default" 0.0; min: 0 24; rel1 { to: "button.button_clip"; } rel2 { to: "button.button_clip"; } image { normal: "button.png"; border: 7 7 7 7; } } description { state: "disabled" 0.0; inherit: "default" 0.0; image { normal: "button_disabled.png"; border: 11 11 10 10; } } } part { name: "button.shadow"; type: IMAGE; mouse_events: 1; clip_to: "button.button_clip"; scale: 1; description { state: "default" 0.0; min: 0 24; color: 255 255 255 0; rel1 { to: "button.button_clip"; } rel2 { to: "button.button_clip"; } image { normal: "button_shadow.png"; border: 6 6 0 6; } } description { state: "clicked" 0.0; inherit: "default" 0.0; color: 255 255 255 255; } } part { name: "button.shadow_overlay"; type: IMAGE; mouse_events: 1; clip_to: "button.button_clip"; scale: 1; description { state: "default" 0.0; min: 0 24; color: 255 255 255 0; rel1 { to: "button.button_clip"; } rel2 { to: "button.button_clip"; } image { normal: "button_shadow_overlay.png"; border: 6 6 6 13; } } description { state: "clicked" 0.0; inherit: "default" 0.0; color: 255 255 255 255; } } part { name: "ecdb.about.label"; type: TEXT; effect: NONE; mouse_events: 0; repeat_events: 1; scale: 1; description { state: "default" 0.0; align: 0.5 0.5; color: 60 60 60 255; rel1 { to: "button.base"; relative: 0.0 0.0; offset: 8 0; } rel2 { to: "button.base"; relative: 1.0 1.0; offset: -8 -1; } text { text: "Close"; font: "ecdb/default"; size: 11; align: 0.5 0.5; } } } } programs { program { name: "button_mousedown"; signal: "mouse,down,1"; action: STATE_SET "clicked" 0.0; source: "button.*"; target: "button.shadow_overlay"; target: "button.shadow"; transition: DECELERATE 0.2; } program { name: "button_focus"; signal: "mouse,down,1"; action: STATE_SET "visible" 0.0; source: "button.*"; target: "button.focus_clip"; transition: DECELERATE 0.5; } program { name: "button_mouseup"; signal: "mouse,up,1"; action: STATE_SET "default" 0.0; source: "button.*"; target: "button.shadow_overlay"; target: "button.shadow"; transition: DECELERATE 0.2; } program { name: "button_click"; signal: "mouse,clicked,1"; action: SIGNAL_EMIT "ecdb/close" "ecdb"; source: "button.*"; } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/burn_data_page"; min: 640 170; script { public message(Msg_Type:type, id, ...) { if ((type == MSG_INT_SET) && (id == 0)) { new percent[10], sectors[100]; snprintf(percent, 10, "%d%%", getarg(2)); set_text(PART:"progress_percent", percent); snprintf(sectors, 100, "%d/%d", getarg(3), getarg(4)); set_text(PART:"progress_text", sectors); /* See the erase_disc.edc for other info sent */ } else if ((type == MSG_STRING) && (id == 1)) { new text[100]; snprintf(text, 100, "%s", getarg(2)); set_text(PART:"progress_text", text); set_text(PART:"progress_percent", "100%"); } } } parts { part { name: "bg"; type: RECT; scale: 1; description { state: "default" 0.0; color: 0 0 0 0; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } } description { state: "before_show" 0.0; color: 0 0 0 0; rel1 { relative: 0.0 0.0; offset: -33 0; } rel2 { relative: 0.0 0.0; offset: -1 0; } } description { state: "hide" 0.0; color: 0 0 0 0; rel1 { relative: 1.0 1.0; offset: -1 -1; } rel2 { relative: 1.0 1.0; offset: 33 33; } } } part { name: "ecdb/burn_data/return"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to: "bg"; relative: 0.0 0.0; offset: 0 5; } rel2 { to: "bg"; relative: 1.0 1.0; offset: -5 -1; } } } part { name: "ecdb/burn_data/begin"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to_x: "bg"; to_y: "ecdb/burn_data/return"; relative: 0.0 1.0; offset: 0 5; } rel2 { to_x: "bg"; to_y: "ecdb/burn_data/return"; relative: 1.0 1.0; offset: -5 -1; } } } part { name: "ecdb/burn_data/drive"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to_x: "bg"; to_y: "ecdb/burn_data/begin"; relative: 0.0 1.0; offset: 0 5; } rel2 { to_x: "bg"; to_y: "ecdb/burn_data/begin"; relative: 1.0 1.0; offset: -5 -1; } } } part { name: "ecdb/burn_data/speed"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to_x: "bg"; to_y: "ecdb/burn_data/drive"; relative: 0.0 1.0; offset: 0 5; } rel2 { to_x: "bg"; to_y: "ecdb/burn_data/drive"; relative: 1.0 1.0; offset: -5 -1; } } } part { name: "ecdb/burn_data/settings"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to_x: "bg"; to_y: "ecdb/burn_data/speed"; relative: 0.0 1.0; offset: 0 5; } rel2 { to_x: "bg"; to_y: "ecdb/burn_data/speed"; relative: 1.0 1.0; offset: -5 -1; } } } /* Missing a description later */ part { name: "ecdb/burn_data/filelist"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; color: 255 255 255 255; align: 0.5 0.5; rel1 { to: "bg"; relative: 0.0 0.0; offset: 10 5; } rel2 { to_x: "ecdb/burn_data/return"; to_y: "ecdb/burn_data/capacity"; relative: 0.0 0.0; offset: -5 -10; } } description { state: "before_show" 0.0; align: 0.0 0.0; rel1 { relative: 0.0 0.0; offset: -33 0; } rel2 { relative: 0.0 0.0; offset: -1 0; } } description { state: "hidden" 0.0; align: 1.0 1.0; rel1 { relative: 1.0 1.0; offset: -1 -1; } rel2 { relative: 1.0 1.0; offset: 33 33; } } } part { name: "ecdb/burn_data/capacity"; type: SWALLOW; repeat_events: 0; scale: 1; description { state: "default" 0.0; max: 99999 20; min: 50 20; align: 0.0 1.0; rel1 { to_y: "ecdb/burn_data/settings"; relative: 0.0 1.0; offset: 10 5; } rel2 { to: "bg"; relative: 1.0 1.0; offset: -11 -5; } } } part { name: "progress_outline"; type: IMAGE; mouse_events: 0; clip_to: "progress_outline_clip"; description { state: "default" 0.0; color: 255 255 255 255; rel1 { to: "bg"; relative: 0.0 0.0; offset: 10 5; } rel2 { to_x: "ecdb/burn_data/return"; to_y: "ecdb/burn_data/capacity"; relative: 0.0 0.0; offset: -5 -10; } image { normal: "frame_outline.png"; border: 9 9 9 9; } } } part { name: "progress_outline_clip"; type: RECT; mouse_events: 0; description { state: "default" 0.0; color: 255 255 255 0; rel1.to: "progress_outline"; rel2.to: "progress_outline"; } description { state: "visible" 0.0; inherit: "default" 0.0; color: 255 255 255 255; } } part { name: "progress_text"; type: TEXT; effect: SOFT_SHADOW; clip_to: "progress_outline_clip"; description { state: "default" 0.0; fixed: 1 1; color: 200 200 200 255; color3: 20 20 20 255; rel1 { to: "progress_outline"; relative: 0.0 0.7; offset: 5 5; } rel2 { to: "progress_outline"; relative: 1.0 1.0; offset: -6 -6; } text { text: "Begin Burn"; font: "ecdb/default"; min: 1 1; size: 24; align: 0.5 0.5; } } description { state: "fade" 0.0; inherit: "default" 0.0; color: 255 255 255 0; } } part { name: "progress_percent"; type: TEXT; effect: SOFT_SHADOW; clip_to: "progress_outline_clip"; description { state: "default" 0.0; fixed: 1 1; color: 200 200 200 255; color3: 20 20 20 255; rel1 { to: "progress_outline"; relative: 0.0 0.0; offset: 5 5; } rel2 { to: "progress_outline"; relative: 1.0 0.7; offset: -6 -6; } text { text: "0%"; font: "ecdb/default"; min: 1 1; size: 72; align: 0.5 0.5; } } } part { name: "disabled_hack"; type: RECT; mouse_events: 1; description { state: "default" 0.0; visible: 0; rel1 { to_x: "ecdb/burn_data/return"; to_y: "bg"; relative: 0.0 0.0; offset: 0 0; } rel2 { to: "bg"; relative: 1.0 1.0; offset: -1 -1; } } description { state: "visible" 0.0; inherit: "default" 0.0; color: 255 255 255 0; visible: 1; } } } programs { program { name: "emit_clicked_return"; signal: "mouse,clicked,1"; action: SIGNAL_EMIT "ecdb,clicked" "ecdb/burn_data/return"; source: "ecdb/burn_data/return"; } program { name: "emit_clicked_begin"; signal: "mouse,clicked,1"; action: SIGNAL_EMIT "ecdb,clicked" "ecdb/burn_data/begin"; source: "ecdb/burn_data/begin"; } program { name: "emit_clicked_settings"; signal: "mouse,clicked,1"; action: SIGNAL_EMIT "ecdb,clicked" "ecdb/burn_data/settings"; source: "ecdb/burn_data/settings"; } program { name: "burn_data_page,visible1"; signal: "ecdb,burn_data_page,visible"; source: "ecdb"; action: STATE_SET "before_show" 0.0; target: "bg"; after: "burn_data_page,visible2"; } program { name: "burn_data_page,visible1,filelist"; signal: "ecdb,burn_data_page,visible"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "ecdb/burn_data/filelist"; } program { name: "burn_data_page,visible2"; action: STATE_SET "default" 0.0; target: "bg"; transition: DECELERATE 0.5; } program { name: "burn_data_page,hide"; signal: "ecdb,burn_data_page,hide"; source: "ecdb"; action: STATE_SET "hide" 0.0; target: "bg"; transition: DECELERATE 0.5; after: "hide,finished"; } program { name: "burn_data_page,controls_disable"; signal: "ecdb,burn_data,start"; source: "ecdb"; action: STATE_SET "visible" 0.0; target: "disabled_hack"; } program { name: "burn_data_page,useless,hide"; signal: "ecdb,burn_data,start"; source: "ecdb"; action: STATE_SET "hidden" 0.0; target: "ecdb/burn_data/capacity"; target: "ecdb/burn_data/filelist"; transition: DECELERATE 0.5; after: "burn_data_page,useless,reset"; } program { name: "burn_data_page,useless,reset"; action: STATE_SET "before_show" 0.0; target: "ecdb/burn_data/filelist"; } program { name: "burn_data_page,useful,show"; signal: "ecdb,burn_data,start"; source: "ecdb"; action: STATE_SET "visible" 0.0; target: "progress_outline_clip"; transition: DECELERATE 0.5; } program { name: "burn_data_page,useful,hide"; signal: "ecdb,burn_data,hide"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "progress_outline_clip"; transition: DECELERATE 0.5; } program { name: "burn_data_page,useless,show"; signal: "ecdb,burn_data,hide"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "ecdb/burn_data/filelist"; target: "ecdb/burn_data/capacity"; transition: DECELERATE 0.5; } program { name: "burn_data_page,controls_enable"; signal: "ecdb,burn_data,done"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "disabled_hack"; } program { name: "hide,finished"; action: SIGNAL_EMIT "hide,finished" "burn_data_page"; after: "hide,finished,filelist"; } program { name: "hide,finished,filelist"; action: STATE_SET "hidden" 0.0; target: "ecdb/burn_data/filelist"; } } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/burn_image_page"; min: 640 170; script { public message(Msg_Type:type, id, ...) { if ((type == MSG_INT_SET) && (id == 0)) { new percent[10], sectors[100]; snprintf(percent, 10, "%d%%", getarg(2)); set_text(PART:"progress_percent", percent); snprintf(sectors, 100, "%d/%d", getarg(3), getarg(4)); set_text(PART:"progress_text", sectors); /* See the erase_disc.edc for other info sent */ } else if ((type == MSG_STRING) && (id == 1)) { new text[100]; snprintf(text, 100, "%s", getarg(2)); set_text(PART:"progress_text", text); set_text(PART:"progress_percent", "100%"); } } } parts { part { name: "bg"; type: RECT; scale: 1; description { state: "default" 0.0; color: 0 0 0 0; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } } description { state: "before_show" 0.0; color: 0 0 0 0; rel1 { relative: 0.0 0.0; offset: -33 0; } rel2 { relative: 0.0 0.0; offset: -1 0; } } description { state: "hide" 0.0; color: 0 0 0 0; rel1 { relative: 1.0 1.0; offset: -1 -1; } rel2 { relative: 1.0 1.0; offset: 33 33; } } } part { name: "ecdb/burn_image/return"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to: "bg"; relative: 0.0 0.0; offset: 0 5; } rel2 { to: "bg"; relative: 1.0 1.0; offset: -5 -1; } } } part { name: "ecdb/burn_image/begin"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to_x: "bg"; to_y: "ecdb/burn_image/return"; relative: 0.0 1.0; offset: 0 5; } rel2 { to_x: "bg"; to_y: "ecdb/burn_image/return"; relative: 1.0 1.0; offset: -5 -1; } } } part { name: "ecdb/burn_image/file"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to_x: "bg"; to_y: "ecdb/burn_image/begin"; relative: 0.0 1.0; offset: 0 5; } rel2 { to_x: "bg"; to_y: "ecdb/burn_image/begin"; relative: 1.0 1.0; offset: -5 -1; } } } part { name: "ecdb/burn_image/drive"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to_x: "bg"; to_y: "ecdb/burn_image/file"; relative: 0.0 1.0; offset: 0 5; } rel2 { to_x: "bg"; to_y: "ecdb/burn_image/file"; relative: 1.0 1.0; offset: -5 -1; } } } part { name: "ecdb/burn_image/speed"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to_x: "bg"; to_y: "ecdb/burn_image/drive"; relative: 0.0 1.0; offset: 0 5; } rel2 { to_x: "bg"; to_y: "ecdb/burn_image/drive"; relative: 1.0 1.0; offset: -5 -1; } } } part { name: "progress_outline"; type: IMAGE; mouse_events: 0; scale: 1; description { state: "default" 0.0; rel1 { to: "bg"; relative: 0.0 0.0; offset: 10 5; } rel2 { to_y: "bg"; to_x: "ecdb/burn_image/return"; relative: 0.0 1.0; offset: -5 -10; } image { normal: "frame_outline.png"; border: 9 9 9 9; } } } part { name: "progress_text"; type: TEXT; effect: SOFT_SHADOW; scale: 1; description { state: "default" 0.0; fixed: 1 1; color: 200 200 200 255; color3: 20 20 20 255; rel1 { to: "progress_outline"; relative: 0.0 0.7; offset: 5 5; } rel2 { to: "progress_outline"; relative: 1.0 1.0; offset: -6 -6; } text { text: "Begin Burn"; font: "ecdb/default"; min: 1 1; size: 24; align: 0.5 0.5; } } description { state: "fade" 0.0; inherit: "default" 0.0; color: 255 255 255 0; } } part { name: "progress_percent"; type: TEXT; effect: SOFT_SHADOW; scale: 1; description { state: "default" 0.0; fixed: 1 1; color: 200 200 200 255; color3: 20 20 20 255; rel1 { to: "progress_outline"; relative: 0.0 0.0; offset: 5 5; } rel2 { to: "progress_outline"; relative: 1.0 0.7; offset: -6 -6; } text { text: "0%"; font: "ecdb/default"; min: 1 1; size: 72; align: 0.5 0.5; } } } part { name: "disabled_hack"; type: RECT; mouse_events: 1; description { state: "default" 0.0; visible: 0; rel1 { to_x: "ecdb/burn_image/return"; to_y: "bg"; relative: 0.0 0.0; offset: 0 0; } rel2 { to: "bg"; relative: 1.0 1.0; offset: -1 -1; } } description { state: "visible" 0.0; inherit: "default" 0.0; color: 255 255 255 0; visible: 1; } } } programs { program { name: "emit_clicked_return"; signal: "mouse,clicked,1"; action: SIGNAL_EMIT "ecdb,clicked" "ecdb/burn_image/return"; source: "ecdb/burn_image/return"; } program { name: "emit_clicked_begin"; signal: "mouse,clicked,1"; action: SIGNAL_EMIT "ecdb,clicked" "ecdb/burn_image/begin"; source: "ecdb/burn_image/begin"; } program { name: "burn_image_page,visible1"; signal: "ecdb,burn_image_page,visible"; source: "ecdb"; action: STATE_SET "before_show" 0.0; target: "bg"; after: "burn_image_page,visible2"; } program { name: "burn_image_page,visible2"; action: STATE_SET "default" 0.0; target: "bg"; transition: DECELERATE 0.5; } program { name: "burn_image_page,hide"; signal: "ecdb,burn_image_page,hide"; source: "ecdb"; action: STATE_SET "hide" 0.0; target: "bg"; transition: DECELERATE 0.5; after: "hide,finished"; } program { name: "burn_image_page,controls_disable"; signal: "ecdb,burn_image,start"; source: "ecdb"; action: STATE_SET "visible" 0.0; target: "disabled_hack"; } program { name: "burn_image_page,controls_enable"; signal: "ecdb,burn_image,done"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "disabled_hack"; } program { name: "hide,finished"; action: SIGNAL_EMIT "hide,finished" "burn_image_page"; } } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/button"; alias: "ecdb/combo_item"; data { item: "ecdb/combo_item/minw" 50; item: "ecdb/combo_item/minh" 26; } parts { part { name: "base"; type: IMAGE; mouse_events: 1; scale: 1; description { state: "default" 0.0; min: 0 26; color: 255 255 255 178; image { normal: "button_base.png"; border: 7 7 7 7; } } } part { name: "button_clip"; type: RECT; mouse_events: 1; description { state: "default" 0.0; color: 255 255 255 255; rel1 { to: "base"; relative: 0.0 0.0; offset: 3 1; } rel2 { to: "base"; relative: 1.0 1.0; offset: -4 -2; } } } part { name: "focus_clip"; type: RECT; mouse_events: 1; description { state: "default" 0.0; color: 255 255 255 0; rel1 { to: "button"; offset: -1 0; } rel2 { to: "button"; offset: 0 -1; } } description { state: "visible" 0.0; inherit: "default" 0.0; color: 255 255 255 255; } } part { name: "focus"; type: IMAGE; mouse_events: 1; clip_to: "focus_clip"; scale: 1; description { state: "default" 0.0; rel1 { to: "focus_clip"; } rel2 { to: "focus_clip"; } image { normal: "focus.png"; border: 7 7 7 7; } } } part { name: "button"; type: IMAGE; mouse_events: 1; clip_to: "button_clip"; scale: 1; description { state: "default" 0.0; min: 0 24; rel1 { to: "button_clip"; } rel2 { to: "button_clip"; } image { normal: "button.png"; border: 7 7 7 7; } } description { state: "disabled" 0.0; inherit: "default" 0.0; image { normal: "button_disabled.png"; border: 11 11 10 10; } } } part { name: "shadow"; type: IMAGE; mouse_events: 1; clip_to: "button_clip"; scale: 1; description { state: "default" 0.0; min: 0 24; color: 255 255 255 0; rel1 { to: "button_clip"; } rel2 { to: "button_clip"; } image { normal: "button_shadow.png"; border: 6 6 0 6; } } description { state: "clicked" 0.0; inherit: "default" 0.0; color: 255 255 255 255; } } part { name: "shadow_overlay"; type: IMAGE; mouse_events: 1; clip_to: "button_clip"; scale: 1; description { state: "default" 0.0; min: 0 24; color: 255 255 255 0; rel1 { to: "button_clip"; } rel2 { to: "button_clip"; } image { normal: "button_shadow_overlay.png"; border: 6 6 6 13; } } description { state: "clicked" 0.0; inherit: "default" 0.0; color: 255 255 255 255; } } part { name: "ecdb.label"; type: TEXT; effect: NONE; mouse_events: 1; scale: 1; description { state: "default" 0.0; align: 0.5 0.5; color: 60 60 60 255; rel1 { to: "base"; relative: 0.0 0.0; offset: 8 0; } rel2 { to: "base"; relative: 1.0 1.0; offset: -8 -1; } text { text: ""; font: "ecdb/default"; size: 11; align: 0.5 0.5; } } description { state: "iconized" 0.0; inherit: "default" 0.0; rel1 { to_x: "ecdb.swallow.icon"; relative: 1.0 0.0; offset: 5 0; } rel2 { relative: 1.0 1.0; offset: -8 -1; } text { align: 0.0 0.5; } } } part { name: "ecdb.swallow.icon"; type: SWALLOW; mouse_events: 1; scale: 1; description { state: "default" 0.0; max: 0 0; align: 0.0 0.5; rel1 { to_x: "button_clip"; relative: 0.0 0.0; offset: 15 0; } rel2 { to: "button_clip"; } } description { state: "filled" 0.0; inherit: "default" 0.0; max: 32 32; min: 32 32; } } } programs { program { name: "icon_swallow"; signal: "ecdb,button,icon,swallow"; source: "ecdb"; action: STATE_SET "filled" 0.0; target: "ecdb.swallow.icon"; } program { name: "swallow_text"; signal: "ecdb,button,icon,swallow"; source: "ecdb"; action: STATE_SET "iconized" 0.0; target: "ecdb.label"; } program { name: "icon_unswallow"; signal: "ecdb,button,icon,unswallow"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "ecdb.swallow.icon"; } program { name: "unswallow_text"; signal: "ecdb,button,icon,unswallow"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "ecdb.label"; } program { name: "clicked"; action: STATE_SET "clicked" 0.0; signal: "mouse,down,1"; source: "*"; target: "shadow_overlay"; target: "shadow"; transition: DECELERATE 0.2; } program { name: "focus_in,mouse"; signal: "mouse,down,1"; action: STATE_SET "visible" 0.0; source: "*"; target: "focus_clip"; transition: DECELERATE 0.5; } program { name: "focus_in,ecdb"; signal: "ecdb,focus,in"; action: STATE_SET "visible" 0.0; source: "ecdb"; target: "focus_clip"; transition: DECELERATE 0.5; } program { name: "unclick"; signal: "mouse,clicked,1"; signal: "mouse,up,1"; action: STATE_SET "default" 0.0; source: "*"; target: "shadow_overlay"; target: "shadow"; transition: DECELERATE 0.2; } program { name: "click_emit"; signal: "mouse,clicked,1"; source: "*"; action: SIGNAL_EMIT "ecdb,clicked" "ecdb"; } program { name: "focus_out"; signal: "ecdb,focus,out"; action: STATE_SET "default" 0.0; source: "ecdb"; target: "focus_clip"; transition: DECELERATE 0.5; } program { name: "disable"; signal: "ecdb,disable"; action: STATE_SET "disabled" 0.0; source: "ecdb"; target: "button"; } program { name: "enable"; signal: "ecdb,enable"; action: STATE_SET "default" 0.0; source: "ecdb"; target: "button"; } program { name: "activate"; signal: "mouse,down,1"; action: SIGNAL_EMIT "ecdb,activate" "ecdb"; source: "*"; } } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/capacity"; max: 999999 20; script { public message(Msg_Type:type, id, ...) { if ((type == MSG_FLOAT) && (id == 1)) { new Float:freq; freq = getfarg(2); set_drag(PART:"draggie", freq, 0.0); } } } parts { part { name: "confine"; mouse_events: 0; type: RECT; scale: 1; description { state: "default" 0.0; min: 0 17; fixed: 1 1; rel1 { relative: 0.0 0.0; offset: 2 1; } rel2 { relative: 1.0 1.0; offset: -2 -3; } } } part { name: "bg"; type: IMAGE; scale: 1; description { state: "default" 0.0; fixed: 1 1; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } image { normal: "progress_trough.png"; border: 3 3 3 4; } fill { smooth: 0; } } } part { name: "progress_left"; type: IMAGE; clip_to: "confine"; scale: 1; description { state: "default" 0.0; fixed: 1 1; min: 2 17; max: 2 17; rel1 { to: "confine"; relative: 0.0 0.0; offset: 0 0; } rel2 { to: "confine"; relative: 0.0 1.0; offset: -1 -1; } image { normal: "progress_bar_left.png"; } fill { smooth: 0; } } } part { name: "draggie"; type: RECT; clip_to: "confine"; scale: 1; dragable { confine: "confine"; x: 1 1 0; y: 0 0 0; } description { state: "default" 0.0; min: 1 21; fixed: 1 1; color: 0 0 0 0; rel1 { relative: 0.0 0.0; } rel2 { relative: 0.0 1.0; } } } part { name: "fill"; type: IMAGE; mouse_events: 0; clip_to: "confine"; scale: 1; description { state: "default" 0.0; fixed: 1 1; rel1 { relative: 1.0 0.0; offset: 0 0; to: "progress_left"; } rel2 { relative: 0.0 1.0; offset: -1 -1; to: "draggie"; } image { normal: "progress_bar_fill_0.png"; } fill { smooth: 1; size { relative: 0.0 0.0; offset: 28 17; } } } } part { name: "progress_right"; type: IMAGE; scale: 1; description { state: "default" 0.0; fixed: 1 1; min: 2 17; max: 2 17; rel1 { to: "fill"; relative: 1.0 0.0; } rel2 { to: "fill"; relative: 1.0 1.0; } image { normal: "progress_bar_right.png"; } fill { smooth: 0; } } } } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/check"; script { public s; public toggle_state() { if (get_int(s) == 0) { set_state(PART:"check", "active", 0.0); emit("ecdb,check,checked", "ecdb"); set_int(s, 1); } else { set_state(PART:"check", "default", 0.0); emit("ecdb,check,unchecked", "ecdb"); set_int(s, 0); } } } parts { part { name: "check_base"; type: IMAGE; scale: 1; description { state: "default" 0.0; min: 20 18; max: 20 18; fixed: 1 1; align: 0.0 0.5; rel1 { relative: 0.0 0.0; offset: 2 2; } rel2 { relative: 0.0 1.0; offset: 2 -3; } image { normal: "check_base.png"; } } description { state: "disabled" 0.0; inherit: "default" 0.0; color: 255 255 255 128; } } part { name: "check_shadow"; type: IMAGE; description { state: "default" 0.0; color: 255 255 255 128; image.normal: "check_shadow.png"; rel1 { to: "check"; relative: 0.0 0.0; offset: 0 1; } rel2 { to: "check"; relative: 1.0 1.0; offset: -1 -1; } } description { state: "focused" 0.0; inherit: "default" 0.0; color: 255 255 255 255; image.normal: "check_focus.png"; rel1 { to: "check"; relative: 0.0 0.0; offset: 0 0; } rel2 { to: "check"; relative: 1.0 1.0; offset: -1 -1; } } description { state: "disabled" 0.0; inherit: "default" 0.0; color: 255 255 255 0; } } part { name: "check"; scale: 1; description { state: "default" 0.0; min: 20 18; max: 20 18; fixed: 1 1; align: 0.0 0.5; rel1 { relative: 0.0 0.0; offset: 2 2; } rel2 { relative: 0.0 1.0; offset: 2 -3; } image { normal: "check0.png"; } } description { state: "active" 0.0; inherit: "default" 0.0; image { normal: "check2.png"; } } description { state: "disabled_unsel" 0.0; inherit: "default" 0.0; color: 255 255 255 128; } description { state: "disabled_sel" 0.0; inherit: "active" 0.0; color: 255 255 255 128; } } part { name: "ecdb.label"; type: TEXT; scale: 1; description { state: "default" 0.0; color: 60 60 60 255; fixed: 1 1; rel1 { to_x: "check_base"; to_y: "check"; relative: 1.0 0.0; offset: 5 0; } rel2 { relative: 1.0 1.0; to_y: "check"; offset: -1 0; } text { text: ""; font: "ecdb/default"; size: 11; align: 0.0 0.5; } } } } programs { program { name: "emit_clicked"; signal: "mouse,clicked,1"; action: SIGNAL_EMIT "ecdb,clicked" "ecdb"; source: "*"; } program { name: "emit_activate"; signal: "mouse,down,1"; action: SIGNAL_EMIT "ecdb,activate" "ecdb"; source: "*"; } program { name: "focus_in"; signal: "ecdb,focus,in"; signal: "mouse,down,1"; action: STATE_SET "focused" 0.0; source: "*"; target: "check_shadow"; } program { name: "focus_out"; signal: "ecdb,focus,out"; action: STATE_SET "default" 0.0; source: "ecdb"; target: "check_shadow"; } program { name: "clicked"; signal: "mouse,down,1"; source: "*"; script { toggle_state(); } } program { name: "disabled"; signal: "ecdb,disable"; source: "ecdb"; action: STATE_SET "disabled" 0.0; target: "check_base"; target: "check_shadow"; } program { name: "disabled_check"; signal: "ecdb,disable"; source: "ecdb"; script { if (get_int(s) == 1) set_state(PART:"check", "disabled_sel", 0.0); else set_state(PART:"check", "disabled_unsel", 0.0); } } program { name: "enabled"; signal: "ecdb,enable"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "check_base"; target: "check_shadow"; } program { name: "enabled_check"; signal: "ecdb,enable"; source: "ecdb"; script { if (get_int(s) == 1) set_state(PART:"check", "active", 0.0); else set_state(PART:"check", "default", 0.0); } } program { name: "check_off"; signal: "ecdb,check,off"; source: "ecdb"; script { set_int(s, 0); } action: STATE_SET "default" 0.0; target: "check"; } program { name: "check_on"; signal: "ecdb,check,on"; source: "ecdb"; script { set_int(s, 1); } action: STATE_SET "active" 0.0; target: "check"; } } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/combo/background"; parts { part { name: "base"; type: RECT; description { state: "default" 0.0; color: 0 0 0 0; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } } description { state: "active" 0.0; inherit: "default" 0.0; color: 0 0 0 90; } } } programs { program { name: "set_active"; signal: "ecdb,combo,back,show"; source: "ecdb"; action: STATE_SET "active" 0.0; target: "base"; transition: DECELERATE 0.2; } program { name: "set_default"; signal: "ecdb,combo,back,hide"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "base"; transition: DECELERATE 0.2; } } program { name: "clicked_send"; signal: "mouse,clicked,*"; source: "*"; action: SIGNAL_EMIT "ecdb,combo,back,dismiss" "ecdb"; } } group { name: "ecdb/combo"; parts { part { name: "base"; type: IMAGE; mouse_events: 0; scale: 1; description { state: "default" 0.0; color: 255 255 255 178; min: 0 24; image { normal: "button_base.png"; border: 7 7 7 7; } } } part { name: "focus_clip"; type: RECT; mouse_events: 0; scale: 1; description { state: "default" 0.0; color: 255 255 255 0; rel1 { to: "combo_button"; } rel2 { to: "combo_button"; } } description { state: "clicked" 0.0; inherit: "default" 0.0; color: 255 255 255 255; } } part { name: "focus"; type: IMAGE; mouse_events: 0; clip_to: "focus_clip"; scale: 1; description { state: "default" 0.0; rel1 { to: "combo_button"; } rel2 { to: "combo_button"; } image { normal: "combobox_focus.png"; border: 7 7 7 7; } } } part { name: "combo_button"; type: IMAGE; scale: 1; description { state: "default" 0.0; min: 0 22; rel1 { to: "base"; relative: 0.0 0.0; offset: 3 1; } rel2 { to: "base"; relative: 1.0 1.0; offset: -4 -3; } image { normal: "combobox_button.png"; border: 7 23 7 7; } } description { state: "clicked" 0.0; inherit: "default" 0.0; image { normal: "combobox_button_clicked.png"; } } description { state: "disabled" 0.0; inherit: "default" 0.0; image { normal: "combobox_button_disabled.png"; } } } part { name: "combo_arrows"; type: IMAGE; scale: 1; description { state: "default" 0.0; min: 9 13; max: 9 13; align: 1.0 0.5; rel1 { to: "combo_button"; relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -11 -1; } image { normal: "combo_arrow.png"; } fill { smooth: 0; } } } part { name: "ecdb.header"; type: TEXT; scale: 1; description { state: "default" 0.0; color: 60 60 60 255; align: 0.0 0.5; rel1 { to: "combo_button"; relative: 0.0 0.0; offset: 9 0; } rel2 { to: "combo_button"; relative: 1.0 1.0; offset: -1 -1; } text { text: ""; font: "ecdb/default"; min: 1 1; size: 11; align: 0.0 0.5; } } description { state: "deactivate" 0.0; inherit: "default" 0.0; color: 60 60 60 0; } } part { name: "ecdb.header.swallow"; type: SWALLOW; scale: 1; description { state: "default" 0.0; align: 0.0 0.5; color: 255 255 255 0; rel1 { to: "combo_button"; relative: 0.0 0.0; offset: 9 0; } rel2 { to: "combo_button"; relative: 1.0 1.0; offset: -1 -1; } } description { state: "active" 0.0; inherit: "default" 0.0; color: 255 255 255 255; } } } programs { program { name: "clicked"; action: STATE_SET "clicked" 0.0; signal: "mouse,down,1"; source: "*"; target: "combo_button"; } program { name: "focus_in,mouse"; signal: "mouse,down,1"; action: STATE_SET "clicked" 0.0; source: "*"; target: "focus_clip"; transition: DECELERATE 0.5; } program { name: "focus_in,ecdb"; signal: "ecdb,focus,in"; action: STATE_SET "clicked" 0.0; source: "*"; target: "focus_clip"; transition: DECELERATE 0.5; } program { name: "emit_clicked"; signal: "mouse,clicked,1"; source: "*"; action: SIGNAL_EMIT "ecdb,clicked" "ecdb"; } program { name: "emit_activate"; signal: "mouse,down,1"; source: "*"; action: SIGNAL_EMIT "ecdb,activate" "ecdb"; } program { name: "collapse"; signal: "ecdb,combo,default"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "combo_button"; } program { name: "expand"; signal: "ecdb,combo,active"; source: "ecdb"; action: STATE_SET "clicked" 0.0; target: "combo_button"; } program { name: "unclick2"; signal: "mouse,up,1"; source: "*"; action: STATE_SET "default" 0.0; target: "combo_button"; } program { name: "focus_out"; signal: "ecdb,focus,out"; action: STATE_SET "default" 0.0; source: "ecdb"; target: "focus_clip"; transition: DECELERATE 0.5; } program { name: "disable"; signal: "ecdb,disable"; action: STATE_SET "disabled" 0.0; source: "ecdb"; target: "combo_button"; } program { name: "enable"; signal: "ecdb,enable"; action: STATE_SET "default" 0.0; source: "ecdb"; target: "combo_button"; } program { name: "header_hide"; signal: "ecdb,combo,header,swallow"; source: "ecdb"; action: STATE_SET "deactivate" 0.0; target: "ecdb.header"; transition: DECELERATE 0.5; } program { name: "swallow_show"; signal: "ecdb,combo,header,swallow"; source: "ecdb"; action: STATE_SET "active" 0.0; target: "ecdb.header.swallow"; transition: DECELERATE 0.5; } } } group { name: "ecdb/combo/popup"; parts { part { name: "ecdb.box"; type: BOX; scale: 1; description { state: "default" 0.0; color: 255 255 255 0; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 0.0; offset: -1 -1; } box { layout: "min_size"; padding: 0 2; align: 0.0 0.5; } } description { state: "active" 0.0; inherit: "default" 0.0; color: 255 255 255 255; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } } } } programs { program { name: "expand"; signal: "ecdb,combo,popup,show,*"; source: "ecdb"; action: STATE_SET "active" 0.0; target: "ecdb.box"; transition: DECELERATE 0.2; } program { name: "collapse"; signal: "ecdb,combo,popup,hide,*"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "ecdb.box"; transition: DECELERATE 0.2; } } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/config_inwin/background"; parts { part { name: "base"; type: RECT; description { state: "default" 0.0; color: 0 0 0 0; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } } description { state: "active" 0.0; inherit: "default" 0.0; color: 0 0 0 90; } } } programs { program { name: "set_active"; signal: "ecdb,config_inwin,back,show"; source: "ecdb"; action: STATE_SET "active" 0.0; target: "base"; transition: DECELERATE 0.2; } program { name: "set_default"; signal: "ecdb,config_inwin,back,hide"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "base"; transition: DECELERATE 0.2; } } } group { name: "ecdb/config_inwin/popup"; parts { part { name: "back"; mouse_events: 0; scale: 1; description { state: "default" 0.0; color: 255 255 255 0; max: 308 128; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } image { normal: "background.png"; } } description { state: "active" 0.0; inherit: "default" 0.0; color: 255 255 255 210; } } part { name: "ecdb.table"; type: TABLE; scale: 1; description { state: "default" 0.0; color: 255 255 255 0; max: 300 120; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } table { homogeneous: TABLE; padding: 0 0; align: 0.5 0.5; } } description { state: "active" 0.0; inherit: "default" 0.0; color: 255 255 255 255; } } } programs { program { name: "expand"; signal: "ecdb,config_inwin,popup,show"; source: "ecdb"; action: STATE_SET "active" 0.0; target: "back"; target: "ecdb.table"; transition: DECELERATE 0.2; } program { name: "collapse"; signal: "ecdb,config_inwin,popup,hide"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "back"; target: "ecdb.table"; transition: DECELERATE 0.2; } } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/entry"; min: 18 18; styles { style { name: "entry_default"; base: "font=ecdb/default font_size=11 align=left color=#000 wrap=none"; tag: "br" "\n"; tag: "tab" "\t"; tag: "hilight" "+ font=Sans:style:Bold"; // Need any more tags? } } parts { part { name: "entry"; scale: 1; description { state: "default" 0.0; image { normal: "entry.png"; border: 6 6 8 7; } } description { state: "disabled" 0.0; inherit: "default" 0.0; color: 255 255 255 128; } } part { name: "entry_focus"; scale: 1; description { state: "default" 0.0; color: 255 255 255 0; image { normal: "entry_focus.png"; border: 7 7 8 7; middle: 0; } fill { smooth: 0; } } description { state: "focused" 0.0; inherit: "default" 0.0; color: 255 255 255 255; } } part { name: "ecdb.text"; type: TEXTBLOCK; mouse_events: 1; scale: 1; entry_mode: EDITABLE; multiline: 0; source2: "entry/selection/default"; //source3: "entry/cursor/default"; source3: "entry/selection/default"; description { state: "default" 0.0; align: 0.0 0.5; text { style: "entry_default"; } rel1 { to: "entry"; offset: 5 3; } rel2 { to: "entry"; offset: -2 -2; } } } } programs { program { name: "focus_set"; action: FOCUS_SET; target: "ecdb.text"; } program { name: "focus_in,mouse"; action: STATE_SET "focused" 0.0; signal: "mouse,down,*"; source: "*"; target: "entry_focus"; transition: DECELERATE 0.5; after: "focus_set"; } program { name: "signal_emit"; signal: "mouse,clicked,1"; action: SIGNAL_EMIT "ecdb,clicked" "ecdb"; source: "*"; } program { name: "ecdb,activate"; signal: "mouse,down,1"; action: SIGNAL_EMIT "ecdb,activate" "ecdb"; source: "*"; } program { name: "focus_in,ecdb"; action: STATE_SET "focused" 0.0; signal: "ecdb,focus,in"; source: "ecdb"; target: "entry_focus"; transition: DECELERATE 0.5; after: "focus_set"; } program { name: "focus_out"; signal: "ecdb,focus,out"; action: STATE_SET "default" 0.0; source: "ecdb"; target: "entry_focus"; transition: DECELERATE 0.5; } program { name: "disable"; signal: "ecdb,disable"; action: STATE_SET "disabled" 0.0; source: "ecdb"; target: "entry"; } program { name: "enable"; signal: "ecdb,enable"; action: STATE_SET "default" 0.0; source: "ecdb"; target: "entry"; } } } /* group { name: "entry/cursor/default"; parts { part { name: "cursor"; type: RECT; mouse_events: 0; description { state: "default" 0.0; color: 0 0 0 255; min: 1 1; max: 1 999999; rel1 { relative: 0.0 0.0; offset: -1 2; } rel2 { relative: 0.0 1.0; offset: -1 -1; } } } } } */ group { name: "entry/selection/default"; parts { part { name: "selection"; type: RECT; mouse_events: 0; description { state: "default" 0.0; color: 138 171 225 128; rel1 { relative: 0.0 0.0; offset: 0 2; } rel2 { relative: 1.0 1.0; offset: -1 -1; } } } } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/erase_page"; min: 640 170; script { public message(Msg_Type:type, id, ...) { if ((type == MSG_INT_SET) && (id == 0)) { new percent[10], sectors[100]; snprintf(percent, 10, "%d%%", getarg(2)); set_text(PART:"progress_percent", percent); snprintf(sectors, 100, "%d/%d", getarg(3), getarg(4)); set_text(PART:"progress_text", sectors); } } } parts { part { name: "bg"; type: RECT; description { state: "default" 0.0; color: 0 0 0 0; align: 0.5 0.5; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } } description { state: "before_show" 0.0; color: 0 0 0 0; align: 0.0 0.0; rel1 { relative: 0.0 0.0; offset: -33 0; } rel2 { relative: 0.0 0.0; offset: -1 0; } } description { state: "hide" 0.0; color: 0 0 0 0; align: 1.0 1.0; rel1 { relative: 1.0 1.0; offset: -1 -1; } rel2 { relative: 1.0 1.0; offset: 33 33; } } } part { name: "ecdb/erase/return"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to: "bg"; relative: 0.0 0.0; offset: 0 5; } rel2 { to: "bg"; relative: 1.0 1.0; offset: -5 -1; } } } part { name: "ecdb/erase/begin"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to_y: "ecdb/erase/return"; to_x: "bg"; relative: 0.0 1.0; offset: 0 5; } rel2 { to_y: "ecdb/erase/return"; to_x: "bg"; relative: 1.0 1.0; offset: -5 -1; } } } part { name: "ecdb/erase/speed"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to_x: "bg"; to_y: "ecdb/erase/begin"; relative: 0.0 1.0; offset: 0 5; } rel2 { to_x: "bg"; to_y: "ecdb/erase/begin"; relative: 1.0 1.0; offset: -5 -1; } } } part { name: "ecdb/erase/drive"; type: SWALLOW; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 100 26; max: 150 26; align: 1.0 0.0; rel1 { to_x: "bg"; to_y: "ecdb/erase/speed"; relative: 0.0 1.0; offset: 0 5; } rel2 { to_x: "bg"; to_y: "ecdb/erase/speed"; relative: 1.0 1.0; offset: -5 -1; } } } part { name: "progress_outline"; type: IMAGE; mouse_events: 0; scale: 1; description { state: "default" 0.0; rel1 { to: "bg"; relative: 0.0 0.0; offset: 10 5; } rel2 { to: "bg"; to_x: "ecdb/erase/speed"; relative: 0.0 1.0; offset: -5 -10; } image { normal: "frame_outline.png"; border: 9 9 9 9; } } } part { name: "progress_text"; type: TEXT; effect: SOFT_SHADOW; scale: 1; description { state: "default" 0.0; fixed: 1 1; color: 200 200 200 255; color3: 20 20 20 255; rel1 { to: "progress_outline"; relative: 0.0 0.7; offset: 5 5; } rel2 { to: "progress_outline"; relative: 1.0 1.0; offset: -6 -6; } text { text: "Begin Erase"; font: "ecdb/default"; min: 1 1; size: 24; align: 0.5 0.5; } } description { state: "fade" 0.0; inherit: "default" 0.0; color: 255 255 255 0; } } part { name: "progress_percent"; type: TEXT; effect: SOFT_SHADOW; scale: 1; description { state: "default" 0.0; fixed: 1 1; color: 200 200 200 255; color3: 20 20 20 255; rel1 { to: "progress_outline"; relative: 0.0 0.0; offset: 5 5; } rel2 { to: "progress_outline"; relative: 1.0 0.7; offset: -6 -6; } text { text: "0%"; font: "ecdb/default"; min: 1 1; size: 72; align: 0.5 0.5; } } } part { name: "disabled_hack"; type: RECT; mouse_events: 1; description { state: "default" 0.0; visible: 0; rel1 { to_x: "bg"; to_y: "ecdb/erase/return"; relative: 0.0 0.0; offset: 0 0; } rel2 { to: "bg"; relative: 1.0 1.0; offset: -1 -1; } } description { state: "visible" 0.0; inherit: "default" 0.0; color: 255 255 255 0; visible: 1; } } } programs { program { name: "emit_clicked_return"; signal: "mouse,clicked,1"; action: SIGNAL_EMIT "ecdb,clicked" "ecdb/erase/return"; source: "ecdb/erase/return"; } program { name: "emit_clicked_begin"; signal: "mouse,clicked,1"; action: SIGNAL_EMIT "ecdb,clicked" "ecdb/erase/begin"; source: "ecdb/erase/begin"; } program { name: "erase_page,visible1"; signal: "ecdb,erase_page,visible"; source: "ecdb"; action: STATE_SET "before_show" 0.0; target: "bg"; after: "erase_page,visible2"; } program { name: "erase_page,visible2"; action: STATE_SET "default" 0.0; target: "bg"; transition: DECELERATE 0.5; } program { name: "erase_page,hide"; signal: "ecdb,erase_page,hide"; source: "ecdb"; action: STATE_SET "hide" 0.0; target: "bg"; transition: DECELERATE 0.5; after: "hide,finished"; } program { name: "erase_page,controls_disable"; signal: "ecdb,erase,start"; source: "ecdb"; action: STATE_SET "visible" 0.0; target: "disabled_hack"; } program { name: "erase_page,controls_enable"; signal: "ecdb,erase,done"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "disabled_hack"; } program { name: "hide,finished"; action: SIGNAL_EMIT "hide,finished" "erase_page"; } } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/burn_data"; min: 32 32; parts { part { name: "image"; type: IMAGE; scale: 1; description { state: "default" 0.0; min: 32 32; max: 32 32; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } image { normal: "drive_optical.png"; } } } } } group { name: "ecdb/burn_audio"; min: 32 32; parts { part { name: "image"; type: IMAGE; scale: 1; description { state: "default" 0.0; min: 32 32; max: 32 32; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } image { normal: "audio.png"; } } } } } group { name: "ecdb/erase"; min: 32 32; parts { part { name: "image"; type: IMAGE; scale: 1; description { state: "default" 0.0; min: 32 32; max: 32 32; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } image { normal: "clear.png"; } } } } } group { name: "ecdb/burn_image"; min: 32 32; parts { part { name: "image"; type: IMAGE; scale: 1; description { state: "default" 0.0; min: 32 32; max: 32 32; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } image { normal: "iso.png"; } } } } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/label"; parts { part { name: "ecdb.label"; type: TEXT; scale: 1; description { state: "default" 0.0; color: 60 60 60 255; fixed: 1 1; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } text { text: ""; font: "ecdb/default"; size: 11; align: 0.0 0.5; } } } } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/welcome_page"; min: 640 170; parts { part { name: "bg"; type: RECT; description { state: "default" 0.0; color: 0 0 0 0; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: 0 0; } } description { state: "hidden" 0.0; color: 0 0 0 0; rel1 { relative: 1.0 1.0; offset: -1 -1; } rel2 { relative: 1.0 1.0; offset: 33 33; } } description { state: "before_show" 0.0; color: 0 0 0 0; rel1 { relative: 0.0 0.0; offset: -33 0; } rel2 { relative: 0.0 0.0; offset: -1 0; } } } part { name: "ecdb/burn_data"; type: SWALLOW; repeat_events: 1; description { state: "default" 0.0; rel1 { to: "bg"; relative: 0.0 0.0; offset: 10 10; } rel2 { to: "bg"; relative: 0.5 0.5; offset: -11 -11; } } } part { name: "ecdb/burn_audio"; type: SWALLOW; repeat_events: 1; description { state: "default" 0.0; rel1 { to: "bg"; relative: 0.5 0.0; offset: 10 10; } rel2 { to: "bg"; relative: 1.0 0.5; offset: -11 -11; } } } part { name: "ecdb/burn_image"; type: SWALLOW; repeat_events: 1; description { state: "default" 0.0; rel1 { to: "bg"; relative: 0.0 0.5; offset: 10 10; } rel2 { to: "bg"; relative: 0.5 1.0; offset: -11 -11; } } } part { name: "ecdb/erase"; type: SWALLOW; repeat_events: 1; description { state: "default" 0.0; rel1 { to: "bg"; relative: 0.5 0.5; offset: 10 10; } rel2 { to: "bg"; relative: 1.0 1.0; offset: -11 -11; } } } } programs { program { name: "welcome_page,hide"; signal: "ecdb,welcome_page,hide"; source: "ecdb"; action: STATE_SET "hidden" 0.0; transition: DECELERATE 0.5; target: "bg"; after: "hide,finished"; } program { name: "hide,finished"; action: SIGNAL_EMIT "hide,finished" "welcome_page"; } program { name: "welcome_page,show1"; signal: "ecdb,welcome_page,show"; source: "ecdb"; after: "welcome_page,show2"; action: STATE_SET "before_show" 0.0; target: "bg"; } program { name: "welcome_page,show2"; action: STATE_SET "default" 0.0; transition: DECELERATE 0.5; target: "bg"; } } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ group { name: "ecdb/window"; min: 640 390; script { public settings_time_id; public in_transition; const Float:show_timeout = 1.5; public show_settings(val) { set_int(in_transition, 1); run_program(PROGRAM:"settings,show"); run_program(PROGRAM:"icons,show"); if (get_int(settings_time_id) != 0) { cancel_timer(get_int(settings_time_id)); } return 0; } } parts { part { name: "background"; mouse_events: 0; scale: 1; description { state: "default" 0.0; min: 640 390; max: 999999 999999; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } image { normal: "background.png"; } } } part { name: "bottom_shiv"; type: RECT; scale: 1; description { state: "default" 0.0; visible: 0; align: 0.0 1.0; max: 99999 230; } } part { name: "top_shiv"; type: RECT; scale: 1; description { state: "default" 0.0; visible: 0; align: 0.0 0.0; max: 99999 145; } } part { name: "paned_container"; type: RECT; scale: 1; description { state: "default" 0.0; visible: 0; align: 0.0 0.0; max: 99999 99999; min: 99999 0; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 0.0 0.0; offset: 0 0; } } description { state: "visible" 0.0; inherit: "default" 0.0; rel1 { to_y: "top_shiv"; relative: 0.0 1.0; } rel2 { to_y: "bottom_shiv"; relative: 1.0 0.0; offset: -1 -1; } } } part { name: "paned_draggie"; type: RECT; scale: 1; dragable { confine: "paned_container"; x: 0 0 0; y: -1 1 0; } description { state: "default" 0.0; min: 99999 9; color: 0 0 0 20; rel1 { relative: 0.0 0.0; } rel2 { relative: 1.0 0.0; } } description { state: "active" 0.0; inherit: "default" 0.0; color: 0 0 0 80; } } part { name: "paned_image"; repeat_events: 1; type: IMAGE; scale: 1; description { state: "default" 0.0; min: 42 5; max: 42 5; color: 255 255 255 255; rel1 { to_y: "paned_draggie"; relative: 0.0 0.0; offset: 2 2; } rel2 { to_y: "paned_draggie"; relative: 1.0 1.0; offset: -3 -3; } image { normal: "paned_v_handle.png"; } } } part { name: "filelist_clip"; type: RECT; scale: 1; description { state: "default" 0.0; visible: 0; rel1 { offset: 5 5; } rel2 { to_y: "paned_draggie"; offset: -6 -12; } } description { state: "visible" 0.0; inherit: "default" 0.0; visible: 1; } } part { name: "filelist_container"; type: IMAGE; clip_to: "filelist_clip"; scale: 1; description { state: "default" 0.0; color: 255 255 255 0; rel1 { offset: 5 5; } rel2 { to_y: "paned_draggie"; relative: 1.0 0.0; offset: -6 -6; } image { normal: "focus.png"; border: 7 7 7 7; } } description { state: "focused" 0.0; inherit: "default" 0.0; color: 255 255 255 155; } } part { name: "filelist"; type: SWALLOW; clip_to: "filelist_clip"; scale: 1; description { state: "default" 0.0; rel1 { to: "filelist_container"; offset: 3 3; } rel2 { to: "filelist_container"; offset: -4 -4; } } } part { name: "filelist_overlay"; type: IMAGE; clip_to: "filelist_clip"; mouse_events: 1; repeat_events: 1; scale: 1; description { state: "default" 0.0; color: 255 255 255 0; rel1 { to: "filelist"; } rel2 { to: "filelist"; } image { normal: "text_overlay.png"; border: 7 7 7 7; } } description { state: "active" 0.0; inherit: "default" 0.0; color: 255 255 255 100; } } part { name: "filelist_overlay_text"; type: TEXT; effect: NONE; clip_to: "filelist_clip"; mouse_events: 1; repeat_events: 1; scale: 1; description { state: "default" 0.0; align: 0.5 0.5; color: 255 255 255 0; rel1 { to: "filelist_overlay"; relative: 0.0 0.0; } rel2 { to: "filelist_overlay"; relative: 1.0 1.0; } text { text: ""; font: "ecdb/default"; size: 24; align: 0.5 0.5; } } description { state: "active" 0.0; inherit: "default" 0.0; color: 255 255 255 255; } } part { name: "action_area"; type: SWALLOW; mouse_events: 1; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 640 170; rel1 { to: "paned_draggie"; offset: 0 12; } rel2 { relative: 1.0 1.0; offset: -1 -1; } } } part { name: "settings_tab"; type: RECT; mouse_events: 1; scale: 1; description { state: "default" 0.0; align: 1.0 0.0; min: 24 24; max: 24 24; color: 0 0 0 0; rel1 { relative: 0.0 0.0; offset: 0 0; } rel2 { relative: 1.0 1.0; offset: -1 -1; } } description { state: "visible" 0.0; inherit: "default" 0.0; min: 140 72; max: 140 72; color: 0 0 0 128; } } part { name: "about_icon"; type: IMAGE; mouse_events: 1; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 0 0; max: 0 0; color: 255 255 255 0; align: 0.0 0.0; rel1 { to: "config_icon"; relative: 1.0 0.0; offset: 4 0; } rel2 { to: "config_icon"; relative: 1.0 1.0; offset: -5 0; } image { normal: "icon_about.png"; } } description { state: "visible" 0.0; inherit: "default" 0.0; min: 64 64; max: 64 64; color: 255 255 255 255; } } part { name: "config_icon"; type: IMAGE; mouse_events: 1; repeat_events: 1; scale: 1; description { state: "default" 0.0; min: 0 0; max: 0 0; color: 255 255 255 0; align: 0.0 0.0; rel1 { to: "settings_tab"; relative: 0.0 0.0; offset: 4 4; } rel2 { to: "settings_tab"; relative: 1.0 1.0; offset: -5 -5; } image { normal: "icon_config.png"; } } description { state: "visible" 0.0; inherit: "default" 0.0; min: 64 64; max: 64 64; color: 255 255 255 255; } } } programs { program { name: "settings,activate"; signal: "mouse,in"; source: "settings_tab"; script { new i = timer(show_timeout, "show_settings", 0); set_int(settings_time_id, i); } } program { name: "settings,deactivate"; action: STATE_SET "default" 0.0; target: "settings_tab"; transition: DECELERATE 0.3; } program { name: "scripting,deactivate"; signal: "mouse,out"; source: "settings_tab"; script { if (!get_int(in_transition)) { run_program(PROGRAM:"settings,deactivate") run_program(PROGRAM:"icons,hide"); if (get_int(settings_time_id) != 0) { cancel_timer(get_int(settings_time_id)); set_int(settings_time_id, 0); } } } } program { name: "icons,hide"; action: STATE_SET "default" 0.0; target: "about_icon"; target: "config_icon"; transition: DECELERATE 0.3; } program { name: "icons,show"; action: STATE_SET "visible" 0.0; target: "about_icon"; target: "config_icon"; transition: DECELERATE 0.3; } program { name: "about_icon,clicked"; signal: "mouse,clicked,1"; source: "about_icon"; action: SIGNAL_EMIT "ecdb/about/show" "ecdb"; } program { name: "config_icon,clicked"; signal: "mouse,clicked,1"; source: "config_icon"; action: SIGNAL_EMIT "ecdb/config/show" "ecdb"; } program { name: "settings,show"; action: STATE_SET "visible" 0.0; target: "settings_tab"; transition: DECELERATE 0.3; after: "settings,after"; } program { name: "settings,after"; script { set_int(in_transition, 0); } } program { name: "filelist,visible"; signal: "ecdb,filelist,visible"; source: "ecdb"; after: "filelist,visible_swallow"; action: STATE_SET "visible" 0.0; target: "paned_container"; transition: DECELERATE 0.5; } program { name: "filelist,visible_swallow"; action: STATE_SET "visible" 0.0; target: "filelist_clip"; } program { name: "filelist,hide"; signal: "ecdb,filelist,hide"; source: "ecdb"; action: STATE_SET "default" 0.0; target: "paned_container"; transition: DECELERATE 0.5; } program { name: "filelist,hide_swallow"; after: "filelist,hide"; action: STATE_SET "default" 0.0; target: "filelist_clip"; } program { name: "filelist,focus,in"; signal: "mouse,in"; source: "filelist_overlay"; action: STATE_SET "focused" 0.0; target: "filelist_container"; transition: LINEAR 0.2; } program { name: "filelist,focus,out"; signal: "mouse,out"; source: "filelist_overlay"; action: STATE_SET "default" 0.0; target: "filelist_container"; transition: LINEAR 0.2; } program { name: "filelist_overlay,activate"; action: STATE_SET "active" 0.0; signal: "ecdb,filelist_overlay,activate"; source: "ecdb"; target: "filelist_overlay"; target: "filelist_overlay_text"; transition: DECELERATE 0.5; } program { name: "filelist_overlay,deactivate"; action: STATE_SET "default" 0.0; signal: "ecdb,filelist_overlay,deactivate"; source: "ecdb"; target: "filelist_overlay"; target: "filelist_overlay_text"; transition: DECELERATE 0.5; } program { name: "draggie_activate"; signal: "mouse,down,1"; action: STATE_SET "active" 0.0; source: "paned_draggie"; target: "paned_draggie"; transition: DECELERATE 0.5; } program { name: "draggie_deactivate"; signal: "mouse,clicked,1"; signal: "mouse,up,1"; action: STATE_SET "default" 0.0; source: "paned_draggie"; target: "paned_draggie"; transition: DECELERATE 0.5; } } } ‰PNG  IHDR szzôsBIT|dˆIDATX…í–ßkUÇ¿÷Þ;3»³¿ší&¶I±1?ÈÖÚ¢†Z4E¥U,­¶ Š‘¢‚€o¾ yk)%ú ÒHñ¢"…XˆiÁ¶šÐØæW³;›Ý;3LJlÒM³±ÙeÁ—8Ìa8çžÏ9÷ÞödKþgaÍ^ð¹Áa#ðºÀÑI„.Eá{§ ù¸×ó)}棹59C$yáömŠâuÑ}ŒQ—|gèñ|ÚÅE"ápɈª0 ©QUF ‘°Ä¹o&Ö­U7ÀñÁ4…Bž‰hv,¦‹h\Õ CCC8,!%ÄVØrÁœÕNÕPŽ=@ O%òÁØÚ]dLÀu…‚…¼i!—³JósEõé#Ñ4Á%—p\Kf f¾3g9·n•ì|® z~¹ ñ+~@cD4,¸¬¹VÃ#g‚ç;v(Ä®>ƼÀ¿ŸOx>ùtn¾Úÿø`º¹œ+ ¾8=«×Só#BH°:o°µ;ÐÀíl$¦!ÎåºÓ¿™˜¦®¬ÞïºbšÀy}g@¥ÙðoÓþŒ5ù .¢Í€mÐð òüõxå­¶ ÈàŠ8ÀÒ1 ØÌ*tì¥×S÷0ÎSLP¢LÊ$à D¨ØGcí;ÛõÏF|o_¯ØžJIM ƒ1Çqàz%pQûÖxùÍíû¹dï!àG5US‰– jÄÈqƒiš`·w.žÜ¡k»:Œž9|¬­»;#lg–½€Bé<ß‚ŒHìLîœ1ËoÚ€|»õ´&#'=دuîÞÃt]GÉ™‡ç•À¹„&“Õþ‰‡úõg3™ZÚ;ZÄÌÂoHF» •(Àv³H%î‡Â |õõç> pk¼önë‡=Ý{O<9pD·œYí¿0›»¥âuËû¸«m‹ÙpÎ$¬¢/¦ÿ™ !î€å,"$T´¥úṄ«_ ¾üeØ1—̳çFæßǯ°5å2Nzü)u>;0 ntB•qèê6H%Žd´W®Mз££Ö¥‹ÅÄ>oŽÇâÌðÇC/¦[ÛH×u¸®ƒ¬™…BÞñËt~fº<ôãhv¼RyyMË«í§Z¿ëëë{äájñhœ‡`Û%äò‹˜šš,Oüáç²æï]úó²u³ðu´»7œN¶ˆÀíŬ;uyÌšàT’Ú›6à{‹ÇûzÔwT½JZ‰Hcœå) ë¹¬?:yÕþ~b¬x·çUÔàUi¹ê»’8@ ¹sž†*ªTÙ¢JÖ/Zé@•z•V»û?'ÖF]Tð*­ö_©~¥ê€š•Ö p7ßÍÏá-¹‹ü ஈÁf6r{IEND®B`‚‰PNG  IHDRb˜žsRGB®ÎébKGDùC» pHYs  šœtIMEØ5¦’?žGIDATHÇc|ûöí4ÀÄ€°üÿ¡»JÚhgÀÔ΂EŒ&Úe€ áð„Ú±«ÁB·ð!ÚGdªÿ01Pè$¹½`”ØÀøðIEND®B`‚‰PNG  IHDRàw=øbKGDz¿)Æ‘bP pHYs  šœtIME× !¼Ø#ÿÂIDATHǽV=OA}³{ÜÀ€ÎÈA–R(#ò Ò‰„à„2ÿ‚¿ê4)SÑ¡„&D¤ .B$ãØ¾œí³½w7;)ŒOæC Vžtºb5ïÍÇÎÌjÜPWÿû¢osçA*ŸÏOº®û˜ˆ¦EDÇq7›ÍNÇé]Æ®ë:¹\î‘ëº.±ˆ4ºÝn%‚ NŸ|nn®P,· …Âë™™™¥‰‰‰03’$µö¹R cccÐZÚív3 Ãï•JåÖúCµZ­°äóùÉb±¸¹´´´»ºº:»±±A¾ïÃqœ{å'MSÔëõÜþþþ󃃃]–™ßAÐpçyó…BageeeöÕæÅqŒ_Õ†ak{›Ò4i4;Õjõc-Õ;£)ß÷×ÖÖÉt Ä D†ø¬À²E§ÝÅÚÚ:ù¾¿ —Õ@D”7>®”Ö‘^éÃC­xããJDT_@Ò45Z©¬4Ô àé‰(f6™@†mf›y-"x˜-Â0ŒˆƘ¤ÓiËC‰ûètÚbŒI2+±#0ÆdžfÖZŒ*‚Á¦¼ÖIbG#0g°QFÝð)¥¢¬ÈI’´J¥ÒÛ““¯/â¸ûtyyYOMNeÞý«±¢(ÂÙÙŸžžžŸŸŸï%IÒº¹“Ýùùùg o|ß9==ý¤oÌÌ·DˆZk(Õ+c£ÑøQ¯×K¥Ò^¹\þ Æ/ `Rkï¾iRJˆ 37Dÿ Ÿ§d”IEND®B`‚‰PNG  IHDR©JLÎsRGB®ÎébKGDùC» pHYs  šœtIMEØ ùEeÈIDATHǽ–1oAFßÌ}Þ;› V,*$„DA5i¨¤£¤A”ü¤ôü( ¥I°è¡Dr 4tˆA[9lßyhŽ@›àó“¶Üïé[Í®V¨Ø|¾]àp¸ Ç£>/'ÀõÛë%€lõ·"àðX $ÿÊràðø´vk­”þ‹¾ËÀ3à*Ðb>üÀ້j Ü­šÌKB•µRe?2i«ÕqÍ›Pe?5Q€ @Tƒè(ÛTà,õ ²MTJê§4,@41Q/@4€Wo^{–Õë7ÄDµöJ *‹‰,¬Qý"Ù ˜ÕìQ ­¸5žÔ{•âf³eIšv§EQ«(IÓ®-e™ ‡µŠ–²Ì 'Öº§Î‰ â¸YL¦S«ÃÑl4 D0U‡öÊÙlÙ}¾/‘ˆBØSÕ±6ÌFí$ݶÈ\E™ç²È¼¤Û ³‘¸»|ÛÝ=ŸçùÛŸûûçfe)ÿÛK"?e_C×Îôz_à`84àÊaž?ŽFóÜŠ޼™‘„PtÚíÏI÷÷§:¿aî¹{ÏÝ7Üýƒ»OýøL«½UÖÑ?ä7óFÆí±^+òIEND®B`‚‰PNG  IHDRàw=øbKGD•ûº pHYs  šœtIMEÖ 6(Ó»‡â¾IDATHǽVMNÛ@þf&qh q$¤4,¢,¨€EòÚ´'(Ëî{ÎÐu7]öw‰®+e°ùq°Æó×±51v’UŸdÍÄïÍ÷þ¾7Ãk!èrÝV²3¥ŠLh§ÓqwvvÞBZZk–¦i:™Lι,;Üh4jívûã8¥TcfI’<„aÐPËÀ»Ýn§ßï:88øÜjµÞ¹®»Zk(¥`ŒYŒ0Æ@éKà‹Åb>NÿŒÇãŸãñø€®@§ÓqûýþÇ£££‹ÓÓÓ¶ïûÄuÝüð&ÑZc±Xì]]]ÝÜÜ\ÐJ©aÎjH³Ùìv»Ýó“““¶ïû„sŽù|¥ÔVcp¾ï!„7›ÍÎïïï…aÓeº®çy‡ƒÁ€ÄqŒ$I ¥ÜºÃRJ$I‚8Ž1 ˆçy‡„ËΛ ëõ:%„@‘×Û³Õ“Ù !@A½^§A<æ=`Šà„Ò½Ýä2}æ$§¦Ý(¤j¿IoŒÖ:ÿe!„YjGµN¿,•y•”Òd¬±#°‹u/R”RRf%ÄŽÐBHi™®èÄ–ÚºÚnónS)+3Ø$ÛÚѲ:©˜1Ã^ËJT†A­q'e”³Še)ÎG¶_b‘v´Åô«š_Å2;¨|’_lLeTeÀZëþ[kNSºLI¥i:᜗Näº;¨xpΑ¦é„1&mÏQ]A ¤”PJA)•lÊømë2{)%‚ PQ]2Æž€-§O !þ6›Í÷Ƙöîî.¥”V^Åúk­Á9Çp8TwwwÃëëë¯OOO#ÆFpz½ÞàøøøËþþþÏóze—× ­ ¦Óé(Š¢ËÛÛÛo£Ñè7€%ÿ—1¶gŒiTW8âJ©9€…ÿ%ÿrøOÜŸïVüIEND®B`‚‰PNG  IHDRàw=øbKGDÿùb*Ž4Ø pHYs  šœtIME× Ä£éFIDATHÇc`£`Œ‚Q0 0Ì`Äg #­-à¡‚<ø,`g`` Ó`f¨Þÿ„‚DŒLK~100¼e``ø<š”IÔ*N”õôIEND®B`‚‰PNG  IHDRàw=øbKGDz¿)Æ‘bP pHYs  šœtIME×#%)eÈœ€IDATHÇc`#º€‹‹‹ƒf}e``¸¹gÏžg8-pvvÖ```¥ÐÑ—öîÝûÆaA“d``øAQ02Ê200`·€‘‘ñÃj;º~P;’Q,ø÷ïm-`dd¤­ÿÿÿ§­LLLC/FÁ(£€ û}»Ù¶IEND®B`‚‰PNG  IHDR[ÐþsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIME× &-á’gLIDAT8Ëå“ÁNÂ@†¿Ú}l1¾€F_ƒž|„¦âCÞÀg!N9@¸•{-évwƃ .¢Ä2Ùdfç›ÍÌœœ¢s4Ý?ä<˲§ƒÀ-,MS’$AUñÞcŒADhËlöLQ×»P¨~?I’„ºÞ`­ýva0¸À‹0L€Û¯±³ð²Ó9§ª*êºÆ9´~¯p³›l‚àµÞ ª{{cL05 ´Ö"Þ!¢žrÇ1 8ïþ¨@c-"ˆ,×%εà§Ú¶ úCSÏ_æ8ï÷ZY–Û~ð…ùr±(D<Ý^Õ÷G§*¬×%«å ÿíê]Å1«w‚zMVG¥ºIEND®B`‚‰PNG  IHDR[ÐþsRGB®ÎébKGDùC» pHYs  šœtIMEØNPÛIDAT8Ëí“¿oaÇ?w-$ehËñ£ I ¡.!Ö‰0TarS£FŸQG;ºÜÔEþ„º^ZfJ)‰Š.M ¼w¼¹÷ÎEM‡í®Oò]žožOž|ó<ðϕִ,ëð~ÙPv¿ß;_æóùû–ey?=c¬Ùlbš&A ”Â0 |ßÇuôû§ån·{(ß÷7€¯KÀëF£išá ¥üÍTJÑétPJQ,/¤”Ñ«¾,ln&™Íf!ð<ï—¤”ôz=ƒ…B\.—Ô4íâ: šsá°á8xJáy“É„Nç±XŒz½N<'•J¹×¥”È…ËÓ'yñüß./™Ïç¼`oïÛÛ·0 ƒýý}ÿêlX†D"„ãpow—ÇǾ!‘H „ \.S¼]d-±†¦ýy$¡Àà‡J¥Žض @ÒLR©VYßX' =©PàôËMƒìV–»• hãñˆZ­F:“F×u\× †ehŸ ÎÐuh4Jf+K¥Zåa£Aag‡ÕX ?˜N§öM6lG£ßWd²¢†A:“"e&YY]!ð}>~þÄùø }Ó×»œüåem ÝjµNù_ß9Âþs†«>IEND®B`‚‰PNG  IHDR[ÐþsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIME× Ê R¢ºIDAT8ËÍ”1‚@Eß v”â)bCï5ð\@Ž¡àrr ¡¤·Z\5ì¾d“Í/3“/XÜÛ6 àÈw& ªCšÖæQÌ¥íàÌ:®i²/ga×÷9pÃSǵ¨h¡¢8ž` ªîd¶0ô -¡à “ЯPÄ»Ðó—ý÷„øN¨2™=r`²§ÜüØ0ŸhÞC­<«E}=‡Á©¾vQT.„Ã8®*Øh» öE:$Zì•×IEND®B`‚‰PNG  IHDR[ÐþsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIME×  -«Âù°“IDAT8Ëc` 2`Dæt­~¨ÏÀÀÐÀÀÀ@„Þ§ S&–…ÊÇ0jØõÁ¡,Tþ"’D†1@õ4À8È:QtNØ ¤ @6søèâÂQ¡O)0ç6§P`à>lN„ft2 ̼ -‚6àÍ È% MMúg£+}IEND®B`‚‰PNG  IHDR[ÐþsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIME×  ;¡¸JÊIDAT8ËÍÔKK1†ágÒâý†àFDD\ûÿŠ;E\µT,{‰›30ÄÌTÁ…0™ääKNò~á¿—¦§¯AꇌM|ó`ƒã4Úe̘aŽUW´©ˆ]à7!\ .1ÅcÔiWt\á÷¸Â^Epƒ÷È>ñVL8Á5.cw©ç Ûy3¼ÆÅ„»±òþÀ…À(²9/cKÁQÔ´E°¥`\Ʀ…üKäšráô×`§-•\Ë*Uè_u\`‹S¾Å–‚ Lºô”uÄLbNî^ ‰CÄ1´»ië2@~Æ^ŠßÀÎáͧ蟓e«ØYk½yŸ—Ûÿq¸ä,@O•t?yJ¾F=ãyèùúa8S»T銶IEND®B`‚‰PNG  IHDR szzôsBIT|dˆIIDATX…µÕ}l]uÇñ÷9÷ÜÇöÞÛvm×­víº•‡µ…Ù±±=°̈‰“±dVS6jâ1èbDÑ F  Ø2Ê@cÚ1”ÛD]]ÙDÙCWº§’>ÜÇÞ¶÷áÜÇóèÞâ”tvë7ùæäwrrÎë÷9¿s~õÜ}Ø)³¼dÂ}˜hf7ªñíGºˆ0G%žß!ý¤æ†[m¹çkö\&oà˜vöøë#ºª7=ÚEf.Ò•óK·ÜÙf?rà»(ªFIE´°¾qaàÃþVÐ3±p`¦Wޱ¸Êxð©‹ÈÑ1ê×;îÙsåµsÞMÇ|¦¡¤9øX39*k›ð”Vz=-÷ÏÀR8¸·Ùü 4|¡õî[]F· н•¶°ÿòÒ7Ïš/Ì)à­>‚Ÿi2„ ÿùÕM[­©PÙ襋–3|ùƒÒ»nÖºŸ#t={¯Ñã§“¡‘sNÒJ¯CS2ÈÁTUÙœáïþÖþüß;0;;¯Ä϶„éNþ|;5‹¥¿åön33N:ÂV!b+™`AÃz%2ò‹‘N.¹cÃsøåƒ–¬ëK7ߺ°È^Ä=¯†ú­@¯´_¹p8–PU³}üaN½¯°4# Ç‹¼Þʆ•_À[±Œ¯öðÆž£Ô9‹X`ˆ d j÷Ct¶iº“=|Ë0ÄgÞt£^¹è6‚}aöï~ )aÒÞ²¯(âóùp„ÃbZ×[€?Ï0mǰQ48"ÕÆûš}äÄ8knVÖ/¡´ªŠ÷~þ8ÐGÍm¦¾êaLÃøÎº6º¯àÄ«xºÚ Õçp.ÓÁSR‚ìõò·Dë|Áܶ¯]X¼¢ŽXdás¯§³òÐA¯ƒ‡·¡\À#0´jÝÀY‡È¸ ¶üx ëw¶cæz!÷>ŠnE1 öf'}ÇÎä4îÝÔF캾Ÿµ t VœŸÞZÌögŸlvÓˆ£eǰãèº9‘°­!O«¡ÎQ }ÃÚ/ã¿f@Ѱõ<Ѹéþíó««@ £SJ&•ÄîPѳ~-A,Åæ^‡(.1†ÏüjÂÌ¥6¬mãüL÷Ÿq‡û5\*šGR3T5F %ÄnHŒ‡AÐrv‡—X MëW=V.9ËOöàök`àW3qCUAÓdtuAHbh9²©4JVÇÔ3yë™y›l¢G¨_ý¸§ÈÝðÎñ—ùâ5 Ó‰HVÓ줒 2¤AJÊ‹‰‚¸<^’1 „îŠf&†uR·r·«¤zó ½x±·ç¬ü+#‡uCð’†B":ŽER(*qõáòT#GR˜Ú(e5k‰úþÉØà~Þô€£úÖo¶ŠZñÈñ¶Î pÇZN45ENµ¡ä’(JŽdé8ࣦ »Û¸ñs…îêÆ®l2…Õ–6&}§Ôl:B`Ì|£ë(_;Â0 Z¾Õÿé)€:ã^OÁZÐҾﱹy Oê&F<Á‰Ó9üô‹ô¥TÌ©hó­O)å®ÿywòm)h‘ÿ.°B€‘ï)ÄÔQÉ÷Œk`º² ÄüXàãŸt!bjÖS‰\1³k­ÂÕ]˜@!âëß4ÁÛ9“)IEND®B`‚‰PNG  IHDR {DbKGDÔ00‡„·Ú pHYsaa¨?§itIME×-¡Bü©-tEXtCommentCreated with GIMP by http://cored.orgŸí'IDATÓнJQÅñÿÌÍw³J¶‰\L‚¬Eò‚X[¤·÷9¬}+;mÍ#¤5I#„D6„%¸×l®M‚þ`ºSœ3†/DÀ°Þæ[`·Ûí^u:‡ ÆY–=«mH€0MÓ‹F£q-"&Š¢Ó¢(—Ëå+°V hµZ'ÖÚQcLœ¦é]LJ€1ÖÚýf³y DÞûï½óÞ;©&Ir4N層@•Ÿ<ðäÊ?T¬µv»ÝWՃ͈­µs®? z&ÏóBUGõz½§ª¡ªVUµZ–åÛp8ì9çÞ °^,“ ^â8>)Ë2Ççóùü XmŸ¹Ê²lT«ÕŠ0 g³Ùåd2éîw?¢$IšÀί~û¸1cðù×÷´IEND®B`‚‰PNG  IHDR"nðÜ pHYsaa¨?§itIME×ÖøÐ-tEXtCommentCreated with GIMP by http://cored.orgŸí}IDATHÇ­V½nGþf÷xÉP"H‚!H¡Xe„<TI…YXm\æ-ü q&eÔ¨?€ƒÀ •Ö$Œ44Áå™?Gro&…tÔS!µÀà€Ý½of¾™‰ÕEÄõ÷©W†ýàjµ”R¢V«•Â0üšˆv˜Y¦išN&“yš¦víaår¹†aHDŽ™Ç‹Å¢Çñ€€ ^¯c0ˆz½Þˆ¢¨Õh4~ÜÝÝÝ/‹epÎÁïýFF!Ëå ¥Ìf³Éh4úÐï÷‘Rþ6 ú|E˜¹EÑ‹ýýý×GGG•““ªV«‚àh«ÕÂÙÙÙÆÌXk1Ëçççß_\\¼às¿Æq<Úí6EQÔl4¯+/OOÉZ‹aüι0ï>õ"¥D. ñòô”Ò4ÝǯƒÁïqO"¢¯*•ʳããš%sÌg XcÁžï€±²ÿX±Æb>[`–Ìq||B•Jå€2ÀÌBJI…BZ§k=cæ­žŽµ…BRJbf‘ÂÖZ ©1ð¼.)ùwÖ¯Ô\? ¢Õjñh4š9çàÿ"µKS6 ÍmñÎÃ9‡Ñh”à@k ­µÑZ33¯¥Ÿ 4YxµÖ¬µ68Èêƒ÷Ö9ÑZE~KCˆÖ¹;µéN¡`ïðìÝvl<°ܧë>Dô$¯fnmÑU²2ß½D1²!™ŽÛz‚|>Èçód¬„›Ky¿ÍóÍX÷Î#ŸÏSæj†aÖ!É3@¸:ûOöð[å*q…†!Ýä„"xï ­E¼eAcfxïéöÞ#ð×Í;Ç‹ùÅbi%~O‘#÷C³˜Ïá[I’€ˆl’$±RªœËån'ñ*˜ß8Y™ÖZ(¥$Ég"2 ”RB$J©·ïÞýé”R˜N¤©EšZx¥ÜŒÿ_2¼é4R ׺þB$t»]c¦Ýn÷ç÷ïÿznŒþöàà@–J%±:^~ètññcg£°xï‘$ .//]»Ýît:7Ƙé²J4›Môz½°Ùl~···÷SµZýaggç›ìgçÜÆý…ˆ ¥\:5ÿ‡o»Ýî›^¯÷7€ô¡r%”¤”efÎo:§~i~%"휛H,{Å¿J¤¥4›4ÎIEND®B`‚‰PNG  IHDR$cîí›bKGDÿÿÿ ½§“ pHYsaa¨?§itIME×1ŸX¼Ü-tEXtCommentCreated with GIMP by http://cored.orgŸípIDATHǵV±nãF}³K‘ ub NìÈ,tu|á2ûCФÌ'ärH™.þƒT9¸¾ ¸ÆNm¾ÂLÓ–¸¸äî¤([’Eë¤d‚Àîböí›·oFby1ýÿ_£<ãÉ…rˆv»Ýp]÷k" ˜Yj­õp8i­‹ªè®ë:‹{VÍ5›Í/\×u‰È0ó`<÷“$IXpJ0Q}³¿¿ÿ6 ÃïëõúWRÊ03¬µ•×B,íY5GD "E¡•RŸ®¯¯•Rþ~ssÓ`èt:ÍN§óÃÞÞÞOQÕ£(‚ëºâIVqzzŠãããóÅÌÐZ»———/}ßÿ™™1æ·$Iò<¯ÓjµÞt»]ïàÕ+Xc`Œ…åÕÌ„aˆašnH¿Ñ@»Ý†ã8u¥ÔÛ~¿ÿG’$©€˜ù…ïû»GGG$„„µ €+ƒ¶Z-!·Ð4 áððz½^DDÁLCÖZBðÎΞÑï$Úíöª‡²nâµZ RJXk©ÄY–ˆˆ/š¨f~6\«Õ‚΋-_?Ãq&œc² $IRcŒý2P£ 3# CŒFÙf {d6µš c ßßß+ì@–e¹Öš}¿¢XïÖAàîn°;Áqh­9˲|hª#k!¤|6ee bk@…1s~åÌ¥Âðç(Àš-´<œEm,²CD•ZÚÄ«â;¬±`æùMTÅÐv€Ê³Ÿ7äyåE>©7xØTÅB•“¯Œ™a…çyT^ÝyT‰É2@˜¬­“ šskžÆp]—æBˆ`­´v+Ä[0ôÐI€B²Ö>0dáñhßo,åõ¿ÔÐbÊÆ£¬1<ÇJ©$Žãf­V›xÌPõ+³‹š™Qâ8†Rꎈr”ÕQ¡â8~ÿáÃ_&Žc¤©‚Ö´.`-–¾3ýü¯Œ›¦ qczæŸB5c(Ïó´×ëýòñãßßåyöòàà@6• œœœ ‚Òe­…R çççæìììââââ]žçé¢Ë¸»»»ßv»ÝÃ0|A4+)AJ9W«æÊKƒËÛÛÛ÷½^ïÝÕÕÕ?ôS¶'4¤”Mföžë¥7ÓÞ:3Æ (³ô/±ÿÿ1@n[ÇIEND®B`‚‰PNG  IHDR"nðÜbKGDÿÿÿ ½§“ pHYsaa¨?§itIME× *§à-tEXtCommentCreated with GIMP by http://cored.orgŸí8IDATHÇ­VËnÓ@=óð¸N\UMk¥²šzÔJ•à'`Á!þ„?€.?°àS±D£.ˆH-·JHÓø1ÍXÇNBËHÖÌç™sϽ†ÕB°E ÿ¯è½Wö%F›z¶mŸBºJ)+MÓÙx<§iš¬Û1Æ¥”y“-„°÷ööö„-BH¦”ÍçóqäÀ5ß÷ŸAð¹Ûí¾´mÛâüaH)¥Ö“BYšSgòpg)¥šÍfép8üÆ»¸¾¾þ çpttÔ ‚àS¯×{ÝétÐétÀ9/ëÒï÷qvvöh¿(¥ ¥$777¶ã8ïRÊ÷qÿ¢¨ã8/<Ï{åyz½lÛE±ôQJáºîJÿ¶ßÂM8>>†çyÄ÷ý7­Vëç ´Ûm+EBH-#H–ef„J)NNNDZài¨ÉdòDz,´Ûm¤iÚ,yJ±»»‹ù|þäðBÀ²,L&“ €‚PY–Í(¥B€Rº"8Ý&„Àu]L§Ó•±m„k¶9ç`ŒAJ™–ŒÜÝÝÍÀqäy¾6:\×ÅÎÎΓ±, J)ÜßßÏ(¾©<Ïs!À+ýYA)…ã8B”ú1o»Î®†2c yžc‘o”Î#Èó¼Œ ]×1Â[« z“­],¥Ôì+3¡­$/³6’Ùg®Ñàšls½e³ðºÛÔgÚuÀ×ÕML52²)l²ébue ˆNf&jMcõpMwÕ…M6¥tiÃ=Ư¾6Zª¾mbf›ªcQŸi!zr{ª}M¢ÞFæ ÀÄtª£sÓ†&Í›ì*KƸ*aŒ%I’È$I–2õQÇH•™uv\’$H’¤`ŒÍK ”Ò8Š¢ËÁ` ³¤”(Š¢¬«¢6ÛRʶ®u" ˆ¢è'¥4.]“eÙ( Ã_”RVK?Àu¾ÞÖÖ Ò4ÅÕÕÂ0ÌúýþE–e£ê›Õõ}ÿíéééGÏóžíïﳪ‹Ö=Ì9u¶ÖÈíí­Œ¢è2 ËápøÀ´ 1v¤”r7øç'ü ©”ò7€@ùøù º„Ë(ÃGÆIEND®B`‚‰PNG  IHDR$cîí›bKGDÿÿÿ ½§“ pHYsaa¨?§itIME× B-tEXtCommentCreated with GIMP by http://cored.orgŸí‚IDATHÇíס A†Ñ·‚ …çÉuA%ÔCÔs×ú…À,%À®˜¯‚—Qÿ$8Ü^{\pòÿ fLÏûæ*抵¶-xäz™ÖØaÈ8ê§sîä:ß¶Yg(@ P€ €JGž’ëží¥9cª{¶u ¦T¿ŽŒ­¿Ž’Î÷õýIEND®B`‚‰PNG  IHDR szzôsBIT|dˆñIDATX…í—MkW†Ÿ;3’F¶ˆmÅ81¢¢¶J1BdgÇ1Iˆ’:Ò †6n’Òþ ·ô?ºq¥‹@ ‹ì’ECé¢8’El#![3ú¸s»ðÌdìŒl5]”B_tïÑÌ9Ï9Gwîü¯Y"Ì877w$þ¨”š¢ÿ0Fø¹X,~}ïÞ½öÞ/°'¢ÑèOׯ_ŸÉçóhš†R `×çÞqØ\)…ã8Ñ¥¥¥«ËËË?P(tÇqrSSS”J%,ËB¢cýì±XŒÙÙY–——¿ Ðö”Rç'&&†£Ñ¨ÜSpT';€mÛ$“IFGG_¾|ùx7 ³³³ÔjµÐìÞGõz|>pm_€ùùùx<>—Éd¨V«A¨Ðñ~¶ ,Ëb||œH$2¿°°`vh4ŸÍÍÍ™Íf)ehO÷ö½ˆv»¦iLNNlooÚ¸ròäI*•Ê¿ü½¶°yPõz'N „¸ péÒ¥GFF&°mûÀì÷kKD£Ñ •J144t¦P(Œ¼ iÚ/^¤V«á8ήŒZrÝV£ÑhpêÔ)MJùå.€ÅÅEMӴϳÙ,Õjµ«õÝ)ø~Õ±m›\.‡a ‹‹‹šðèÑ£sÓÓÓ)!Íf³ëò‡ÍÃ{÷H)‰Ç㌰ººz>Ø‚+gÏžõ³:è"ìþNÕ°m›Ó§O£ëúµ À™t:M½^ï*ûn[VV«Å±cÇPJƒ·{Á‘H$ÂÑ£Gßëm×é™N/-Û¶ú‚¢V«±²²²+“àå8Î{Û‚v€T*åù»¡·ônܸá÷“·Cº_×u&''wù÷<B‰…B)¥¨^¯S¯×ØÜÜäáÇ>€”’ééi‰J)z{{‰Ç㻪qóæMÿùPÏ866ÆÖÖÃÃÃH)}(Û¶ÙÞÞFA&“appZ­À¡C‡H§Ó»æ¦iâ8B,Ë"‘HÐ××w0@.—ãÙ³gH)9|ø0Žã i†a`Y–ï4™LbY–_F£A"‘ û”RèºÎëׯòí^b°³ #ž1‹FI§Óܹs¥†a iŽã‹Å0 ƒv»Íúú:¥R‰b±H¹\FJ‰išx˲hµZ”ËeVVVèëëC×ut]÷$5€¥ÔŸ<Éd2 "¥äöíÛ\¸pÃ0üíÙË,‘Hø™èºîgÔl6B`år™»wï’L&‰Åb~¥Ÿ?Žã8O^P¥R黥¥¥ou]O{ޤ”$“IÞ¼yC>ŸgttÔ·7›Íwkš†MÓRòøñcVWWyòä •J]׉D"!R®mll|´;Gó~v^ vŽá·=f6›ýpffæ›þþþ\6›ÕS©”ÑÓÓƒišþAU)E­V£X,òòåËö‹/äúúúï·nÝúåÕ«W€ ”;Çô° T‚§I ˆ¹——šôæØØØ‘©©©OR©Ô”iš¹ÐšÛ©”*W«Õ§kkk¿Ý¿ÿ×-7°T:Ð@(èðÇd4 ˜À;+Hw¿Wî%Ý- áTÜÀ¡êÀ“îBx-2Üàž í·\}õw‚Òx›½çÃqæ¿¡¿Q-ð˲… ºIEND®B`‚‰PNG  IHDR©¥–bKGDÿÿÿ ½§“ pHYs  šœtIME× $ t ùIDAT8Ë­”¿ŽQÅÏ7÷ßÌÀ!R‘h¬ŒKi©O`í>…¥µv6¾€[Øê+PQÚhŒ1ê²Êî° 3›™ÏB`PX…Œ§¼÷æ—sν÷#¬©ÛízZk iš¦N'Æ_ÄÌDÄëëTßÕÙ0Ñmì(Îù-¼<8¸÷f —Ðëõü( Ÿž û‡“éÄÌç1˜Ì…"*lpa‰@mÕÞŸN£C¯VP!„ ¾=Ò1·nÞ@¥RAžç;9=;1<zǃþsf>""–ËÍ8Iªí;wÑl6áyÞ¿c/R„ak«xÿácc™C@–e©RJ·Z-Xk!¥ÜµR¸® )%>}þ*—Å8Ðn·gFkø¾!ö‘R 6 ”Âo$c°¯ÇA`-”ÒÅZÑ¡Œ”V›P!U9¨ÜâT©rP©·Bu9§Û:U¥ãoéT·äEôzÇþ Ê̾1åâmÐh¨këNµkÜôû(D–å{ÁrfÌâ¾ï&:øõVñëµ`ú㔈—?úÁ&Ü,..&×Vן]¼˜Î}öûDåòFƒ\=ŠP¨§b”G eÄ.Çšµ?˜¾|âØÄµ‡66ZsÀ›=èÅÕ$ùáçµ'Þ¾pOez4ãÉ»þd¼ÞÁG†ÙöÈDJS®§ž³¿浟מ’ÏŸ7³·DÄ<À7KIuqyrøŽ&Ì=s°9ÁPÕˆ"3Dë»5Ò ŽLmðê§Æ—+“ãï<Àù•ª]ëTÜüƒ1w¨³·§V\7*3í‹Ôa¦¨ YªÕ*´[¼~vÒ_Ö­œ~r©ÑÞSK9ØŒi •ÀJTKí¨®—’ÆPÌt³ÎRê8õQaýÞ9ÀÈPŒ çúƒ%Î`t$Aú>Ø„ÞÌBÔ[º oG?–Qg±r:ÙŠtåtR´ƒGÕåßIQ5Ú…g@²„ÿ"Ù–ñ®’JÀ9P”ÛÉáœTÂvhs>•j¤Ýâ(öqÖݾT½ÑœOe[¤"B^@0‡êö)|˜€jéŸe;¼ñøZÒƒ~û\-9´'cù†‘v”,‡B!t­Ú³Íç<Y€´£,¯3{ ¦öûF¯ùkâ£Óyñîm¿oW½Ìq‘hoŠT ç¤7]ÁJàÕuã̹Œû…¢:âã^ÑÌlôû W/=ýN}trwģǪL4ï… å^ͺ!Bä °|Ãxï\ΕµÀ‹e«S{㻇‡‡—6¡I'Ï_øõJkîÌ×}ü£g¥a@'Z¡PÁ;cWͨWÊ"ÍŒ+÷Ï(ß+ù}É+±÷ψHÚŠ™ÙKªz]Uoªª©ª Òæ;UmÕßÌì”™ ìx3«#@ dÀºˆ´oñ+Iº~d·òûßôõ?¢=ʦµƒIEND®B`‚‰PNG  IHDRàw=øbKGDz¿)Æ‘bP pHYs  šœtIME×:’LÚ€IDATHÇíͱ Â@@Ñ—ã°ˆ–"`#6 ‚Ó8 i©m A ¶bHltƒ»îþÿU³ã§ÆkDizáŒ&b‡UÂ9Lq@°ÄHÚ*L°‰ËSÀ"È\ P€  EŸáÝãpÁCÂùð{Þ"tØbžxâÚžêûY~P,»IEND®B`‚‰PNG  IHDRÙìµÛbKGDz¿)Æ‘bP pHYs  šœtIME× ,Ê“ÔÚ×IDATHǽVIo[U=ß<½Æcí$•€J‰LB’v#"•MA°ë!–lÝuÁ©{þâlºdQö€Œ"(‹¡I¨•ppO±ý¦{?dHÓ¸IàHWzï]Ý{ôó 0J¥iµº/áH¥œoø«««þäw=”Ë夵ò† íÌæEf¦“.ef0313†–‘o!Å­ùÎÆÆFû$år9â¶±ÁÍF³1Ón7{žï÷ÀìYfÃ0 ÀB\$ê*¥BfÆe¬NÜ¡\6‹l6ÿI4*Þ-•J- J¥i·ûo†Aðáþ_Õ†m­¯—vg VJ€&¢5HÀÌ–†Ù†ô˯÷åýíÅt2yáùå¢qý^±XôT³)£Dö­Fóàô^ÙÜü]ëÈONçkÏóšx ;ˆˆÄÒòR1¾öC¥re¿zsyeé#÷„Öî³Ör©Õnv×Kë»ZëJ­V¿Ûëy cÀÆS,C6nÏûyþÒüç¹\îÁÃêŸ$„z›™Iyž+ûn/îöû …¼×jµ¾d N ¶ØÍ¦³ú£º•R\qéÒÜZë˜ïûmœ 6‹3v(5´Ö20‡ù `° "CD Šy`¬ CMDúx©ätr… ZG…BÀj¢`f|:›ˆÀE¤”ƒHÆ›ƒBSÆœ%`pLáŒÞÕôb?U‰c_þ+‰ä8µ¤¿ àS"¤+3'­µ·{½þ;{{Ìììîôê}"ò†ûQ‰ÉŸ»at‰È#v;ÑÅ…ÅÄÚÊŠ,Ì*¥n¸;&¸aŒyÀÚ)“kNJù>€Ïü6šñô/}#"CSÃðOª¿‰çæèòþº Ü¥ /•IEND®B`‚‰PNG  IHDR@@ªiqÞsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœµ[ml\Õ™~ι3w¾lÇÇM°cL4à&)jÃbœ¶[6ж«úôÛª T*»Ò *5HQØýQ„úg+­´E(ôc ÂjŠšPRHRBbÚ$XÁNÇã‰=3ž;3÷ã|ì¹grçzÆÎG:º3çÞ9÷<Ïû¼ïyϹwˆ”Ÿf!„ð÷={öv×ç›$?å’O£Zýè£ÏçÉ­·ÞJ …±m›@­V#`Y–€T*%»ººäÙ³ge6›•wÜq‚~$ðéqÃV€³Ù,M&“Ô4MR((jÕuú¾Oc]×a†dŒ ß÷ÑÕÕ%<Ï“•JEäóy¡¹Ñdè×ÛAøÌÌ M&“-™Lj¦iêt˲ôjµ?räÈårùfÆØ*ÆXèº^Ðu}!N²uëÖ‰„€™¦É0<™Lò™™>::*î¸ã(Ϻ^"®K„¢€ÐÇÑâñ¸žJ¥ ÏóÌr¹œ9|øð¨ã8wø|¶··×Z·nÖ®]‹µk×fff033ƒ .`vv¶vþüù<€âñøû÷Þ{ïÞt:½hš¦gÛ¶ï8‹ÇãWЏ®‰eõÑÑQ¢€wwwbžçÅÇÆÆþµZ­îH&“ŸÿÚ×¾ÖõÀ`çÎèè踢þK¥ÆÆÆðúë¯cÿþý…J¥òA"‘8¸sçÎ_š¦épççç}EÄÞ½{¯Ù-®šeõƒ’½R©º®Ç„‰üK¡PøîW¿úÕM=ö†‡‡aš&(¥.êÞ‘‰BˆFõ<‡ÂK/½„·ÞzëdWW×ïØ±ã7”Ò*cÌM&“þää$‘×¢†«"€B”Õãñ¸Î‹B¬Ã‡ß777÷ØÐÐЗ~ò“ŸÐ{aÀ0 hš!jµ<Ïç¼QÃhšÖ¨¦i²,PJÁ9‡ïûð}úÓŸðãÿXœ:uêHOOÏK÷Þ{ïÛRÊš®ëŽã8 ®†„+&@·m[`¦R©8€ÔØØØžÕ«W?ô£ýÈøÆ7¾Ó4‘H$ ¥D©TB¹\F­V»b’ÃŲ,¤Óittt€‚jµ Ïó°oß><óÌ3þ¥K—~µsçÎ=lÛ¶^*•bWCŠ(¡z±X4)¥‰¹¹¹uÇŽûÏmÛ¶ ¿üòËX½z5Òé44MC.—Ãìì,8ç׉d2 C¡P@¥RiYãñxË/..âã?ÆñãÇqòäILOO·%q``÷ÝwªÕjÛû Œ’É$ž|òItttüÓ»ï¾ûÎyÚ²¬˜išÚèèè’¥9ÐÚ-‹™œó„ã8«Ëåò·wïÞ­e2X–…ýû÷ãâÅ‹QâZ‚ÇqpäÈœ8q.\Xr>ãË_þ2†‡‡¡ëÍCÚ¶mÞ~ûíFL¥ÞjµŠýû÷ã+_ù 2™ ~øaú /|ÛqœßPJÝX,Æu·hM@0å‘R©¤sÎãBˆÌøøøoÚ²e ’É$>úè#œ9sfEÐàû>þð‡?àüã²ÉP­VïýkLLL`÷îÝMç(¥Â'Ÿ|²ì½Îœ9ƒÞÞ^¬_¿[¶lÁàààM§NúÁ¾ð…g9ç^©Tb###‚B± A@ bY–V.— ÙY_©TvŽŽB×u0Æpøðá¶`„œ}cìwš¦™BãܹsõYAêjn,•J4›Íj¯¤|ß߸aÃBP(0==ÝàõºA EÀÐÐvìØ±äš7Þx¾ï¯(uœžžn¨õ³Ÿý,N:µ‘sžÒ4-N)­”J% @BHòÙ,aŒœóøÌÌÌ—(¥=JŽüñ’d% p%BV:·fÍ<óÌ30 £©ýСC8vìØ²€£ípöìY¬_¿ýýý ”öÌÌÌ|iíÚµyMÓŒl6ëLLL\`dd„äóy-Œ±x­V»+“ÉB8瘜œl" Ø•\¡™LÏ?ÿ<ºººšÚ …ž~úé–™b;ðê855…›nº „tttjµzcl?!ÄX\\ÔFFFØÁƒ¥€”ËeR«Õ(ƒBt'“Iø¾ÇqP,áû~«QÀr–Ïd2x饗°aƦvÛ¶ñýïÙ ° ª­X,Âqxž‡D"Û¶»…qÇqŒZ­FËå2PwR©D-ËÒBˆ˜”²Ë²,xžÇq°¸¸ØX•µÚŠV×DK:Æ/~ñ |îsŸkj¯V«xä‘G0>>¾t;ÀQbJ¥Çïû°, RÊUBˆ!İ,KSq@Æ@µúbÝ”Rf,Ëç®ëÂ¶íÆŠL½,G€eYxùå—±qãÆ¦öJ¥‚Ý»wã/ùKKpQ ­‘R¶m¸® Ɖd8ç&sNÌ—SaÎ9 m)Ó4xž·`”„Víá]ßð`u]ÇÏþslÞ¼¹ €mÛøÞ÷¾×” .'ó()áv!!„P5Å9×±c º®!B脘¾ïƒ1Îy°V`Û>§>?øàƒØ¾}{Ó5®ëâ‘GYÖòíh׿ºnƒ BˆÉÓ)¥TJyù¹d¨c"¥¤¨/‡ÝJ¥Çq»9Bˆ– U [{¹€˜H$ðøã/õÃþï½÷Þ’ë—þ=~¦P­VÀE}ˆJ)kZû¾¯ÔTUrg‰%£Ö‰n¸¾øÅ/"•J5µ½ñÆxë­·ZþîJ’Ÿèg)% Ãh¸pF×ÔØð¾dx9, !R!T<σïûàœ/Ù¤høJ#![·n]ÒÏoû[PJÛÆŒ(+Å g\×m¨@E!)¥’²t? Z­JMÓ!¥,{ž×~툒%":cB°zõê%}œ?~ í$ߊŒV*QH)U"e‹ ¸®+•këêbß÷%cLPJ9€E•iš†x<Þvú‹Öhéìì\ҦܫÍmãÜ•,†Ô=ãñ8<ÏcL­#JB.„”R©Œªº® Æ“Rúš¦} „¸¿T*Ñt:úcË…RÚ¬j\]¯J4×ÎyÃ/£€Tðm5Í…}^Uu},ƒïû°mB¡iÚRJŸRÊt]º®Ë²££C†Á¸”ÒO8ç…J¥Ò­vwãñx8H6n^nåÑ¢ž G R6)CVý…IPÀ)¥0 RÊœó¥ô!„ €9Ž#.\¸Ð @š¦ ¿-\BHs>å8N·ÇÁ9_bý•¾·‹á¢ëz#Î,ÝU  šç×)ëK)á8cSš¦Õ|ßwøA¬“;BÒ0 Nñ}߯ !*BˆãRÊ-µZ­ñœ_YIm¥€(að°oß>œ8q¢‰ß÷›Њ„°µÃÀÕwµqCaw ‚뎨Bj†aøº®7¶Çär9‘J¥|)¥C©J)O2Æ]×Í(iéº!DKà+‘¡8pà@[7hG@+ÐÑxc¬‘ ú¾¿(¥<)¥¬ !!„???¯]J)I}DB×u&„p9ç¶”r±V«²,ëß÷›^si¼мVÁq¥i/ìÛQk·Ê•Tú¸À))墔ÒÖ4Í¥”ª‡$òò¦¨ ŠÅ"·,Ë`K)K®ëîsç®d2™RGå¿ËÍWB@8¶ËùÃǰBDZÇÙ§iZIa !œZ­ÖØ »€"kšæyžW%„,J)ÊåòÑT*µc%€Ë‘VË/¾ˆžžž&ù?ýôÓ˜˜˜X’ׇƒ[4à…•A)m´«Ï”R”J¥£RÊ)墢jš¦‡Èã1]I!pnÛ¶Ç«Œ±’”²àºî~Û¶·tvvv´S@ø»¦im•ëÖ­Cooo–eAÓ´%´MiÌFª(ÀaÒÊårÉuÝý„缤ëzÕ¶m? 2¸IT€ï8Ž ¬iÚ<€®ÅÅÅ©TêÓ4i4Ê·ªíˆhW(¥MA0¼òlå h¸„]”s.J¥ÒóæceƘÀGHþ@èјb$`ÈPᜅyÇqÞ™››;¡ÛNÁ«ïái4ZÂç£5JêJ÷€ÙÙÙÇyG‘çœTLMÖo" ª5‹.˜³mûõ\.÷÷VIOt`QЪM×õ¶S J†ÔçåúiE´ÓìììßmÛþ?sÁØ,K¬¿„€€RA@@Àl¡Pøßùùùb»Ì¯®FŠ„vÀ—Ë9òù|qaaáÌc.Á/l} ýÛâJ€€€8svvöUMÓ¾ÝÓÓ“Z)(†¯¬õæ›o66D”ŠÅ"t]oJzÔŽNÔ×ÉúNÁüü|evvöU3.Ö/”õ—”vïÔÕ¡Hè°À:ë ôöö>Ø××—Ur [+ \út4Pà„DF½VnWGU§¦¦ò/^|À$€)”TQå¾å;ƒËý_@¹‚¡G ³³³ÿãûþ?Æb1Mt¢Ê ZšryÕ—šîÂÊ߇1&'&&ÎÍÏÏÿ.ý÷õËÁØ—¼³"A^4DÔ9óóó¯yž·ãæ›oÞÒÕÕ¥·r…åÜ¢Ñõxþ\(ø¹sçŽ/..@]ö‚cuË7¾VÖ®àMÑ´ÀÄewèÐ à3>C)íÏf³ÿ800𙎎uØÔLžÔ/¥c BˆÆ‘sÞÈëÕ÷b±(&''gs¹Ü~!ÄùÀâQ|ó¸,û¶/\–ýËLH jVPÓ$Øu8BˆJ.—»téÒ¥ÁuëÖ ßrË-ÝÉd’Dý<º8RD)_§±ÑmøZ­&'&&¦§§ß期CÝÒsð9Ô#~xÊ[üŠ´ AM“õÀ¢f @™s¾xþüùó³³³º»»7õôôd{{{ã™Lf¹þÛž³m/^tæææò—.]:éyÞÇP÷ñ|pœP ÆÐ”í­¸Êÿ  î* ÆPw‰4ên± u×X +hKišÖ›Íf7õôôô¤R©X2™Ô’É$íêêj¸c Åb•JET«U^.—½\.—›››;É9Ÿ À•P·òBz!h+£.yu#]1ø«" BA]=F@„‘ €w5  \ ~§éºÞL&» R©Ì3Ææ,SC=…µÅ –P—z9¸ÆEÝê B¯üUЂ•+¨'JV6ŸB])Vpº‚¢i!ÇåØâન H¨µ†ËrWW¼åZ–¨A¹…R„‰:`U9ê„éÁoÂÓªŠ+*¶(µPõpÙâák\Z¡@A5±|+¢$xÁѵßàñ_/@ƒà2 ªZ¨†G—…áFMµªŠPU×âzÁ7ˆ€¦[“V °<áÏŠÕvC@7÷FÐè¸yámoM†rø \•ÿ”êÿ·#-ÉTIEND®B`‚‰PNG  IHDR@@ªiqÞsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î<)IDATxœå[iTT×–þªŠª¢ •H;4Î6Fˆy àîÚÖgì¼äEYÓ‰Ó‰®Ž+†¼!ÆçÊç”n1FÆ$Òj'’çS †´¬yòäž®®®«¢(ŠwïÞ­"€˜ O:4FÇn@Xša„g…Õʦ€—±råÊ|žç"N§³}ïÞ½ÿ ÀH²{FònX˜7H&Âç(ðìúõë·wuuY)žç¥¥¥N´ÄDf $MŒ!ßõd]E" O¥ ÉwA<@&€_~ùåADvÔ××` !¼ž‰n46ÀƒJ”UUUÕ²k ‚ ÖÖÖn' §‘õã$@ÒÈgBOd _ *¼€å›o¾yâûï¿_B¶ `ëÉDÕg¦¦¦.t»ÝvADˆÓ§Oo0@:yG‘f÷ÍRL0@é¶mÛþ\ÑívÛÓÓÓŸ0 @6ÀÆÈ®®®þuuuõB§™j`Pçf ¦Úl¶“n·ûÖœ9s² ‘dÆ0Yf(9uêÔIÊ 3}‹-*&êœD@R€Ú¾…ì~€_N:õ÷>ŸO¯{êÔ©“ ©d0Àßx8;;{®Ãá¸}çÎÏ @¢]~gIiûO™—×Ý2ªTªd­V›¶sçÎÍlÍL£F@sðàÁ:Ô‹/~Gèg€ÒS8óõL.¶¤¤d²’'´¨yÆ" þ)†íß¿]ttô0F“ˆ€ð¯Ãòc2£!|>ŸFŒñÔ®]»æ4©ƒ1ú§Ÿ~z–Ò±;a„)ä9 œätýG+yÞœ““3Z¾ž(ŠXºtélHvŸL„ÉtÒ^}õÕG'NœøÞýÎ7„9äÌD[­Övj§K–,y-++k$´qGŽYŸŸŸKdàyžî*Õ%F(èÔÿDC_à N×}üñÇs*++CRïQmFrrò¨W^ye9ˆŠwwwßRžÕªpÿ4hjjºI0I‡Z«ÓéÒ¤>÷ÜsþôÓO›KJJȧßm6›²3YÆüäÑSl6›/ܺ˗//ºtéÒÚùóçÏð€F£µoß¾…qqqqôù¶¶¶ë"Sy_dM@)™‰úúúæÇ{ ´É“'?ÒÙÙ9C¥R©ŒF£)äe²6}ž@:³~€>’¿00utt„¬ËŽI“&úôÓOÕÛÛëñù|ˆ‹‹Ó³Ï766^…”'Ф)dÈ5€Mf||»wïþ‘ã87»F£Ñl0LòÝ‘!Š"fÍš5Í`0˜ˆp¬)D!Ôîc 9Z‹V«;wnÚ@h˜Íf=žþŽçy×öíÛ¿À1„,¤f£¤ð¼MMM½/^<''ª¤–J#---ãĉ+œ#PÏLwÝDO€ä_wìØ‘;a„˜H‚G⩱±ñLcc£ €— È`8 ðAB®€ëµ×^«òù|ü@‘GydÞ‘#Gþžƒ@Di$À°Q\êæÍ›g¯X±bìPé ‚À½ùæ›ÿÀ )käFÂE‚zH~$€éžØ¹sg¥Ëåâ¼^¯nr4yž÷OŽã„üRà4RÀ“)êËð4€ 7nüŠã8AönЌăÛíö=zt+Y7®†D‚LT¦EpR’à©—^zéwW¯^m²ÛínJÐf³õ9N¾? 8ŽûúúøíÛ·ÿÀ/ Åú¹Šˆð¯¯Y³æt__Ÿ0ÁN'o³Ùü|Øívwssó•ŠŠŠµdÓFÍòG€J„T„ÏÌFeÔ>¨Ï™3gôíÛ·ÑÐÐÀÍŸ?ôž={JâããM²µ‚´Œçy¸Ýnß¶mÛ¶¾õÖ[u„F€a¥¥¥yÛ¶m›£ÑhBŽf9ÝÝÝŽU«Víýøãš9s¦.##UUUWôè&ÓÀ âD%[¡¨( ‚™¨uf‚®@Ì”)SFœ­‰$8ôôôt-Y²ä_jjjþi·© ^"¬—Lž|§Ó°‹†-ŠÊ@ ç6-°…’h€@òìÙ³Ç;vìE‹ÅÃî¼ËåBOO:;;ÑÞÞŽÖÖVx½^_yy9L&“&’àÐÛÛÛ½bŊ׫««ÿ€ Òî÷!P˜O€içû@B¤ò­Ä‚¤ÉùùùÙ}ôÑ‹&“ÉÌq\Ðη··£¥¥Ç¡´´f³9"ƒ¢(Âétö<ÿüó¯UUU}À ÀIååAΠ+Ñý–ÅÃôh>>1::Ú BŸÝn¿ºlÙ²ß×××w":û+ÃrT sÔ¹Ñc:¶´˜˜˜Ñ¯§¤¤„f0ýÍŽsçÎ,,,Ü )«s@ROíöáÇŽûÕܹs’¿ ùp:-S§N]ØÚÚÚ hê,€Õµ ž ™ÝðÊÊÊ¥á„h©º±±ñ<þ¤‚F3m²½; ,øàÂ… ? •˜L¦‘ÇÿgHÀÀô%åQ©;LËâf–ØØØaEEESï…¡¦¦¦gÏžý€N]'¤$ÇA¾w‘¿ß),,ÜyåÊ•ëJk ”îøñãÿ.===Á­ùSOþ 6¸10———OÕjµ~×<Á ­­íf^^Þ&ÇÓ‰à”–­ã¹!©j€»ÇuÌž=û---·Ã­ÛFWQQQ…» !(Aý÷ó ²+8ýÇq|aaá;===l%G~lQÏ톤 ÝlN§ónAAÁû^¯7¤C

}ÖÕ«W+Œ…”ñ¥ éŽÞ|åÞinnîu8boo¯¶´´ü€*Ä´Mz[3à|iT8R‡÷Ék×®Ý`×t8â­[·¬~ àQH1 <…t;uæåË—÷O›6-Ò=Âaâš;ËJ&ÀCrDÝõõõ7ÇŽ»3œ=Æ|YYYzbb¢™Ý-·Ûݽ|ùò54„šâY&Ó@ê Ý%%%¿u:vvÃbcc6oÞœà8Âé¨í`›8qâ¦K—.ÝFàèõ› «ò+2Ô½„i' Ág8µe€·¬¬ìA–A¯×ëÞ´iÓú¯¿þºêÍ@,}€ž~ø¡eõêÕïz<ž>öÕeË–=) ¨z”ï ”£¯tA‚ÖúØV )p,¤"È_xtéÒ¥kìv»`·ÛE»Ý.^¿~ý/óæÍ[ÉDFb—df@Í0ãOæææ¾tåÊ•k”ŽÝnÖ­[÷n™°5CzíÆLÖ‰†, ¢Û3ÙÔ1Ìe˜yåÊ•?Ûív±³³ÓúÉ'Ÿì„tûc2Bo‰Gü_†.ÍE(Ðã ]«™`Ñ¡C‡*;::lv»]lnnþ’ý'¾t²ÉVˆB„E…"ìP(%±é² €ùøñãO]»v­kíÚµ Lĉ@¢3ènެçÏI¸ž« ¼ûî»c'Mšd***:‚àV¹’·[To0GszQ‚ª—€àÖ˜¿j;Pá p—3èù;u”.(Hý#’ß,ì^ö¿¹Øp6¨×Ó‹D‡5Cö¿UèÍo 8{+D±ê‘Ö`ùcZal¹xd:ňy‚õEÔ4ÙŒÒß;üYP`Ž‘"½¡Ò‚`éRÐý9Æ`iß×½_‚G #ÿd!?ï ‘ÿ #\ƒãžLíÿ?Çøoçú3b5¬^^IEND®B`‚images { image: "background.png" COMP; image: "focus.png" COMP; image: "text_overlay.png" COMP; image: "paned_v_handle.png" COMP; image: "button_base.png" COMP; image: "button_shadow.png" COMP; image: "button_shadow_overlay.png" COMP; image: "button.png" COMP; image: "drive_optical.png" COMP; image: "audio.png" COMP; image: "clear.png" COMP; image: "iso.png" COMP; image: "frame_outline.png" COMP; image: "check0.png" COMP; image: "check2.png" COMP; image: "check_base.png" COMP; image: "check_focus.png" COMP; image: "check_shadow.png" COMP; image: "button_disabled.png" COMP; image: "entry.png" COMP; image: "entry_focus.png" COMP; image: "combobox_focus.png" COMP; image: "combobox_button.png" COMP; image: "combobox_button_clicked.png" COMP; image: "combobox_button_disabled.png" COMP; image: "combo_arrow.png" COMP; image: "progress_bar_fill_0.png" COMP; image: "progress_bar_left.png" COMP; image: "progress_bar_right.png" COMP; image: "progress_trough.png" COMP; image: "logo.png" COMP; image: "icon_config.png" COMP; image: "icon_about.png" COMP; } ‰PNG  IHDR szzôsBIT|dˆjIDATX…Å—}pTÕÀ÷¾·»Én6kÖ@0Œ !¡”š"Èçh…Xë £¥cÿ±3µ­ý §Ö‘qÆ?Ú™Rgú‡ÓNu°Ž--•ÑB‹:h•   ¤‹&›Í¾—Ý÷Þ½·„}MHéôÌœy÷ëÝóÛsÎ=wüŸE\iÁ̵wh#6Iaƒˆ"pŒÒ0â¹¾=[_ûŸÔ¯|2mÛÞoñØ‚ym‰éÓªä5•å¢`H¿q¤¯Ê¿­ðïìßý½3W nõãõiö·Ï™‘›?·©JH1=b[X,)‘R 9~FíÙ$_ Ì­ýÚv䪴umæó±Wµ7Q63<͊ؕӧ¥+ç¶ÔIKJŠžBCéTœd¢¬ü½þ¡ë³½yîSy ¾ëÉr+_ì¶,1Ç7zÍüîñó«~zWº:ñË{×/¨)(1†št‚_= ÷áðè¢O 9¾cæaNZÝ1Ù8@ߟ·ývppô÷Çû2ʶ$~ )úgÔçó­ ³ù“‡I˜±æÑ Úˆ–›+Þ~Öó‹H$²Ä²¬ Ã~œÑkndùâvój,Ê¢’g_|™/TîWJ)ß÷Ÿw]÷Þ={ö'Øã;Z[§¥þ¡µþÙ¦M›–vvv"¥ 74Æw‹Ü¶åi¤/Ðhmõ©I§vœ;wbŒ¯Ö3ÏË–-‹F"‘Í—X²d‰-„¸¯½½|>mÛcPJ*ðùÁæùDñ9Ùw†›fO#+¤•‰(7µÔðòÁwè\0ƒŽ–©(¥ÂÓ#¥Äu]:::¶lß¾]^N§×,Z´¨6‰P(RžçQ,)‹hðè7“Ësvà#æÏžF¼,­sëyíí^ê«cÜqëŒp½çyA€mÛxžG2™dΜ93>¼â"!Ä×V®\I.— Œ1á&Æ´ÖD,Ã6·süäi³9ÖÞ2“Þ÷Ï‘Ëó`g3žçáû>¾ïRJ”Rh­q]—Å‹cYÖ– ëÖ­«­®®^ÕÐЀã8(¥Èçó”——‡ÅH)…RŠÊrÉïùoîåoož çhwµ` Ch­(++CA.—CkM¡P ©©‰ÊÊÊή®®ºÀ¶íûÖ¯_o»®‹"ÔÑÑQ|ß'‘HljF£!°,‹Úê[ïšCÏÑSlýJS«D£Qâñ8Éd’d2‰çy8Žƒ”)%‘H¥ .´µÖ÷Ã…: „¸»½½×u©­­%›Í222‚‚ ¶eY$“I`ìüßœNó»Ÿ4PQn#¥DkÖz,_´ß‘R’J¥¨®®¦¿¿ŸŽŽöîÝ{ðãR!úL2™ddd„³gÏ¢µFʱô°,+4¨µó¡ÔÙàûþÏÅb1„øÏ=gŒ!ŸÏã8Zk¦L™‚Öú†Ð@Ìó„ @8œÿ·\‰ ZÆ%¾š>¥´ä/ñoXž]ÿ¤ÚxIEND®B`‚‰PNG  IHDRSSã²;sRGB®ÎébKGDhloÌÁÉ~ pHYsaa¨?§itIMEÙ* ØtEXtCommentCreated with GIMPW IDATxÚí]l[gÇçØñWœÄvš4IƉ “H” ­ã‚Œ¶šl” Ö1µªÐ€•.½˜6•¥EM;ØÖj 2W«JÇŒ‰] èh ±±ÖmóÑı“Øñçq㦶sŽ}|lÇùKVÒcûÍ{þçyßÿóuN¡É M,óÖAƒŸ·j’ÖTÅÈ8*h)Ë<¸Aæ CiK´À ØåߪÌOibþÞ+ú}Ô´‰†BGFvŽ–!wõqåá‡÷>qâä40 t) íóùÜÑhtÈÊ+ä€A`JȵÉËÞÙÙéK&“Ër!RÀ"° {Ãáðõón; K}¾Üqàà[Åì_É;À_xá×À!'Òðz½ƒÑht±Ì÷sò3[8à÷û½ÉdrRHvÉkÓää¤è’@ÆŠ“³ŠÌ ‰ƒ@ˆ øÃápÄÄ¿‘/X%ñûýC3336`Xâ@º•ÉüœÌ`YW]át:óÀ•m¤Xªçl¯ã¸[…¼«…eÜ䀘Xf7Ð ø„äÆªùZ‘Ëùó¯u¾øâËUUéééÎ^¿>™ƒ5O4cÒ8ŠÇã±¥ÓiÅï÷eœNW¾Êq¡ÐÄM5Ù9Z™¥T[ŽOŸå•4Y=ͧˆÅÆšIÍÕÇÛ?l–‰fh~$Å•ê:š% OÅb1›ø­@d™snzzºC|׆‘9üXü½V"q5æ\.WNV–½dn—¥kõ¸º»»;+>pµªT£ÜÉä‚úè£ßRµ0 +TØ’qâñ¸miiIíïï[®DQ5/STÛxEµ­Va+Æ ˆuÎQsÃËüСÃ}óέãlZáÜ|õÜ3?wñâ%÷:'²˜ÐÎÅÅEµdz€/ûý¾lqÖf# ̉˧šMæ^ år¹s´].W^ïrWÊÏjtJ¤°L{¢Cüâ8ÞL{±š?%?ç›\…ë5NpÓµªùgÑòó´/æ%2r׺g~A2+편UÕd~L|ÊÄ—$dïtTKævVò’иðVTóR1øÈÈNÅf³ ÷õõeìv{Õ%‡V‹Í+!“É(ÓÓÓö¡¡¡e£±ùÓâ¨O¶° ×cœ´,Y?2í´²i#F«$z%éÐ#>^7p øð úÜsÏ”,£”;^.c`œ{dN·qc_ÃQµ:Ïy<žðàÏÀÀ™Çch Û„7’ɤÕýRÿ¾‰–ǽU€Êä,ƒ²Ÿ¦-œä2àÞ’FK:Ÿ.]ÐBü‚¼÷8Ù¿ÿÀ ða çyE"B›^5ÿ(ÖÖ¹³b{÷>ÑéŠ%€ßìÞ½+ ì>bá|ÿ‡ÖÛt«š—²ÌC‡÷]¸ð¶3 ÖœZK=UUÉÏÎÎvìÙóÙ»ïþÌB¹ÏŒì$š(ùÞë¯ÿÉsêÔ¯GŽ<}}óæþŠs®4Ž^ŒŸòŸ;÷»\(4»EÍËÔÁ"juÕõÌÈ>ôjÁM3"EŸßV;Yã8zŽ ØÐ#@^‹–yV²QgM문sÃÀW÷í;0X&Vòx¹Ï—9îoGמé´hïIoštáò2Ö'-2Ýád‡E–Ù\2q¼K)»!2­j}ö‹n®É^_oäKq¤”ÙWî°ÈÇ\­6 ›»=š „Z—ŸõŽ…gÑ:{1A…=hU©:Çø ð¾ÕóQ+˜±ˆ u›…!³ÞPJq¤Ví¬Ø7chÝÅfa+Ö$²mF(e‘e:O›táà.LêµÔAfN/™ ‹,ÓÌ÷š0Ö½²Ä­šwvÝÆæãã§z{{{3ù|¾¦ùèÚ›bQÛü|" MÌÜFf õü"ð%I7Õ;6/8ÁïÆC¡‰{Œ¨ùÞ½ß~rlìx`õ\ë¬æý²~OÏ2ÿÖÞ³hÞvŽóS¡hµ*ð•Ó§ÏøËX g±;wÓÏ,ca‘~Ö%ˆ;€È•+W#h™ö‹¬dÚ´rA!Ó~ð_ŸÏ—‰ÅâV^x‡Xåm­B•ÊËh VfÛÕ……U¬Ô/ÛMq èû¬ÜóàÌfsVßõëcsèUsÄŒÝ4´â™Šv'Ù{rq£r¬‹ ÍùVÙ„ÆFÝ\2™ŒrãÆ û+¯œ¾|ÿý»nñ.ôô´/}ÝgØæusZÁ3¥¼‹µÊ¤¿G«Äm@C'p¾ì†¿Æ—ÿ.ñ³wƒÇ›w—\ª–L€×D ÚÝÀ¹Š®ˆŽAþˆÖìÙÕÆDvÉ }s­LK)á)µW´{Oû ·÷®Ef¹Xø»À@0t¶“š{½ÞáD"ño´Š@Å\‘¦§1À¹´´ØN–r§Ói­aŒJVi”Ìà·‘H´ðL¡õxàÝsèL–µ²?lÛ¶uQâåõŽð×ûîÛ¡» bxÉ>ñÄfDÙëœÈ9¤oI/6î7_…xþË«ÔØ b–ž;zôÙkÀÛ²'µBÚ®Sæú7àIU-M…Þý²jªô~«< .—Ëñàƒ_›Û¾ýóeog4BdUdêPùÂò©ø”¨¹‚–FóÉ :NQW†Õ®÷2_ p-½Ÿ¶ˆâ7"®Wåoo‘¬Ïðüí6º©™xK^7Ÿ9<99ÙV_’W­=¡ÅÏvÇb1;ÚÍOgw×úr­Kœ,»weùï{衯ϢóÝh]¸@ ‘H¨rÌÁÊ#Æ‹ç[p®Ý@—ø·2F¡Aà¥3g^º üB‘f¡ŸÓžgåqãYÙŸS²”´êös˜9N+P3b´•Ém—«´n-Ó un2q²õÆÿ2+‡Rg'Ò~IEND®B`‚‰PNG  IHDR*39~¸bKGDÿÿÿ ½§“ pHYs ‰ ‰7ÉË­tIME×+ ·t'IDAT(Ïcèéé9óíÛ·3 ƒ™ÍðíÛ78g0³‡z*³'Kyhî“uCIEND®B`‚‰PNG  IHDRΓÌJbKGDÿÿÿ ½§“ pHYs  šœtIME×:å}¢Ä-tEXtCommentCreated with GIMP by http://cored.orgŸí>IDAT8˽•=kA†Ÿ™D ÁÊÎV¿ò7DP S(´Q!6VB íKED´³$òÒÙ™B°±0±ò’;sÎk1³7÷#z!»ÅîÎó¾ïœ9³8Ê>ް²²¢} s |ÞÜ„¤Ý—$4;k†•¿s,õSÅ»µ/,¯Hy÷Þ€@¾kfj\»x†T¼ü=ZoV7«€ƒ¸O*¯"!7Ÿ ,]:K)F²¡í©åˆ@‚׫_Á­ áÈ[:95LKåB¹5¹z¿yå¥$HÃ’gW1)@€W¿–Íe­\ è.‰Ê«)$Ü[W0Ë#^*Ó‚ /Ö¾·» Ü…PÍHÿ ²1Qqgñ4¥ä fÊ9O-e½½ü´Õ„¼A¬]Èp Y¿lÌœ³¼xŠqvÏMÛ?¶'ÞþDfH e0Gx«#S]V9R®u4øñtù<ãÜqvÚ)ÃÖ-äRÎÆóõCoCQ 5)`$ÅØ’Fè ÷.#›w`Õ>œïÅ@$դ֒ʉ–E/ܽp˜’óüS;ÖÒ§…#ëÿuúolüãßb¿ÿ‡¿Ý$¶°š:IEND®B`‚‰PNG  IHDR÷ô}bKGDŠ«á5s# pHYs׈^R³ tIME×%(éϤVtEXtCommentCreated with GIMPWIDAT×cd``d€ÁºººÿL0Þ€0aîpjËíK IEND®B`‚‰PNG  IHDR÷ô}bKGDŠ«á5s# pHYs׈^R³ tIME×&÷W_otEXtCommentCreated with GIMPW#IDAT×cd``d````¨««ûÏÀÀ ÈÄÂ`„¹z Ëj9¶£IEND®B`‚‰PNG  IHDR"èdïrbKGDÔ00‡„·Ú pHYs׈^R³ tIME×!$/ÞÅtEXtCommentCreated with GIMPWµIDATHÇí”Aƒ0 w,$žWÖOhûú"ÿÄÿÄ^ !¡1R"ASTVac’“”•³ÒÓQUuÑÔ#24567s±$3qrt‘¤² %Bbe´ÃCDF£ð&dƒ¢ÂE„ÿÄÿÄ,!1aAqÑ"Q‘±ðÁ¡áÿÚ ?öü%Q|Ol1Ùê]K@¨{ƒC <ÇÀ²Š¼~j(›™ÀÌÄ*$Óû4³_OæàûÉÈ6’<ÏÍø²¶§W-ö;“õNlQÄCŽmy$8åÍÍÍzxûqõ™^~^½íŒ¬E>Ü,×Z¶2ðÒCâƒY wi ‰Ýù>ºÚ*;|¾YU§|/’Ꙭ3§£¿òHç\VÇFû%•²º±òÔn¡Ñ£&eã1›~q˜ÝÙ\ùùxù^ÉÇ^2É÷^‘´Tvù|²›EGo—Ë+ÉïL0`bÛ=Eªj#sˆÜcac5 `!¯qÒüœÈ?“–ì†üXé¸YNoà ¾Œ’Êm R¸ŸÇ7D7i~W‡çì.]={hŸ·Ëå•;Dý¾O,¯–ZfK‰cÃÅœ†ü=.¼Bs€Tè;ò2ësÑçÑTp¦M…_EQo“N‰â¤[£Ð{€ÝS“ˆqÏwý;÷äƒÝ6‰û|žYM¢~ß'–U=g…5ž6‰û|žYS´OÛ¤òŠ¥¬ð©ÖxQ6‰ûtžQM¢~Ý'”U=g…5ˆ.möé<¢¤O6_×IåKX§YáE\×ÍÛ¤òŠkæíÒyESÖøSYáD\×ÍÛ¤òŠ|ݺO(ªzÏ \×ÏÛ¤òŠkçíÒyESÖ)"­ëçíÒyEóvé<¢ªk<*Džõóvé<¢šù»tžQUužÖxQµóvé<¢šù»tžQUužÖxPZ×ÍÛ¤òŠkæíÒyEUÖ&±o_7n”S_7n”U]`M`N¢ÖºnÛ'”S]7m“Ê*®Ÿ…4ü(«Zé»lžQMtݶO(ªÚa4 Zé{kü¢§]/m”U]0ša±4¹Zÿ(¦º^Úÿ(ª¡þÓð¢-k¥í¯òŠk¥í¯òŠ«¦šh«Zé{kü¢šé{kü¢ªéøSO‚ֺ^Úÿ(¦º^Úÿ(ªº~Óð µ®—¶¿Ê)®—¶¿Ê*®Ÿ…4ü(-k¥í¯òŠk¥í¯òŠ«§áM? Zé{kü¢šé{kü¢ªéøSO‚ֺ^Úÿ(¦º^Úÿ(ªº~Óð µ®—¶¿Ê)®—¶¿Ê*®Ÿ…4ü(-k¥í¯òŠk¥í¯òŠ«§áM? Zé{kü¢šé{kü¢ªéøSO‚ֺ^Úÿ(¦º^Úÿ(ªº~Óð µ®—¶¿Ê)®—¶¿Ê*®Ÿ…FŸ…o]/m”T¥í¯òŠ«¬ð¦˜Ak]/m”T¦í¯òŠ­¦„UtݶO(¦ºnÛ'”Um0ša|ݹþQM|ݹþQUtü)¬W¨µ¯›·?Ê(g›/ëŸåSXšÄµóvé<¢šù»tžQUu‰¬QVµóvé<¢šù»tžQUužÖxQuóöé<¢šùûtžQU5žÖxQVõóöé<¢šùûtžQU5žÖxP[×ÏÛ¤òŠkçíÒyETÖ(Ö ·¯›·Iå×ÍÛ¤òŠ©¬ð¦³Âˆ¶j&íÒyEFÑ?n“Ê*¡‘F±shŸ·IåÚ'íÒyESÖ&±Í¢~Ý'”ShŸ·IåOXšÏ "Ù¨Ÿ·Éå”Ú'íòyeS2xSYáE[5öù<²ªÜ®3ÒR:mt§/ûŠù2xUK¸ÖÛ§hç qàPel˜„´9ÕPÆHÏEÓ;1à94¯˜nw8k›MW+Ž˜Í®cɱóä³TÖI”2*—€ó½”ïpÿ02T¦&[4…¯nQ<äö‘×7œáš¦équD̆©°ÅIf›E£3ß‘ç ¬s\nñŽQ¦•îf±‘² —½¹g›AhÌd9Âj_©tÅϧsZÐIÌ=ç0ó‘ÈåàYæ•òÔDÓO3œž\èÞÑ1¿y9så˜Èüè$Þ§­¯[&Þ?6yÿ’†O~Ó`kÛ–“ çI„Œò94är!kÄDáÍ ·ê3ÿú+—X¤ž²I :¢EÇ1¯n‘»x÷sƒ¸g×dMu¸²±TÎ^ž‹ã–œŽG#æ;ŽåùÓþ {ð¶/{œvéyÎôú߯¤“TbkavL-ÑÑÍÍÈÛ·n_¿Ã¥„ðÁÿ÷òû0ƒ×°~#¤¦Â–ªy)n…ÑÒFÒYn™Í=hÞnDxBÙ7Ñ4dȯ“—&Ïî,L£µI¬4pá+=Òã%4“ÔÔVÄÆ²8õò1šOÐsœNw7æ ªä¨»ÉÀÞS¾ì¯ ç>SQ4ºÛ´N`lþâúv'·½¥®¢»8ÁµÎÿÐòT]äào)ßvNJ‹¼œ å;îÉÈ9Öb[k3Рº·>|­SŒÿþŠ$¶ ò º ÆG+Tûÿþ‹£ä¨»ÉÀÞS¾ìœ•y8ÊwÝëf%¶Çù7VçóZ§ÿ‚†âK[¤Ûuѧç©Çÿàº>J‹¼œ å;îÉÉQw“¼§}Ù´*¨;’ïêÉýÄùUAÜ—VOî-ÿ%EÞNò÷d䨻ÉÀÞS¾ì‡Z•TÉwõdþ⟕TÉxõdþâßrT]äào)ßvNJ‹¼œ å;îÈu¡ùUAÜ—VOî'ʺä»ú²qo¹*.òp7”ï»'%EÞNò÷d:Ðü« îK¿«'÷å]r]ýY?¸·Ü•y8ÊwÝ“’¢ï'yNû²h~UÐw%ßÕ“ûŠ~UÐw%ãÕ“û‹{ÉQw“¼§}Ù9*.òp7”ï»!Ö‹å]r^=Y?¸Ÿ+(;’ñêÉýŽ䨻ÉÀÞS¾ì‚Õ ÃàRy'vGÿ–ùÁu¢ùYAÜ—VOî)ùYAÜ—VOî-ç%EÞNò÷d䨻ÉÀÞS¾ì‡Z?•”Çxõdþâ|¬·÷ãÕ“û‹yÉQw“¼§}Ù9*.òp7”ï»!Öåe¿¸ï¬ŸÜO•–þã¼z²qo9*.òp7”ï»'%EÞNò÷d:Ò|­·÷%ãÕsû‰ò¶ßÜ—UÏî-ß%EÞNò÷d䨻ÉÀÞS¾ì‡ZO•¶þã¼z®q>VÛûŽñê¹ýŻ䨻ÉÀÞS¾ìœ•y8ÊwÝëIò¶ßÜwUÏî'ÊÛqÞ=W?¸·|•y8ÊwÝ“’¢ï'yNû²i~W[ûŽóêÉýÄù]oî;Ï«'÷ë’¢ï'yNû²rT]äào)ßvC­/ÊëqÞ=W?¸Ÿ+­ýÇxõ\þâÝ T$0N9eŸ\íÙç—ÿ-à?䜕y8ÊwÝëMò¾ßÜwVOî'ÊûqÞ=Y?¸·<•y8ÊwÝ“’¢ï'yNû²i¾WÛûŽóêÉýÔù_oî;Ϫç÷Vç’¢ï'yNû²rT]äào)ßvA§ù_nî;Ϫç÷Så}»¸ï>«ŸÝ[ŽJ‹¼œ å;îÉÉQw“¼§}ÙŸå…»¸ï>«ŸÝO–îã¼ú®un9*.òp7”ï»'%EÞNò÷d:Óü°·wçÕsû©òÂÝÜwŸUÏî­Ç%EÞNò÷d䨻ÉÀÞS¾ì‡Z–îã¼ú®u>X[»Žóê¹ýո䨻ÉÀÞS¾ìœ•y8ÊwÝëOòÂÝÜwŸUÏî§Ë wqÞ}W?º·•y8ÊwÝ“’¢ï'yNû²iþX[»Žóê¹ýÔùanî;Ϫç÷Vã’¢ï'yNû²rL]äào)ßvC­?Ë wqÞ}W?ºŸ,-ÝÇyõ\þêÜrT9ò'ï ¹ÛÉ9ù²rL]ä`o)ßvC­?Ë wqÞ}W?ºŸ,-ÝÇyõ\þêÜrL]ä`o)ßvNI‹¼Œ å;îÈu§ùanî;Ϫç÷Så…»¸ï>«ŸÝ[ŽI‹¼Œ å;îÉÉ1w‘¼§}Ù´ÿ,-ÝÇyõ\þê|°·wçÕsû«qÉ1w‘¼§}Ù9&.ò07”ï»!ÖŸå…»¸ï>«ŸÝO•öîã¼ú®un9&.ò07”ï»'$ÅÞFò÷d:Óü¯·wçÕsûª>WÛ»Žóê¹ýչ䘻ÈÀÞS¾ìœ“yÊwÝëMò¾ÝÜwŸVOî§ÊëqÞ=W?º·<“yÊwÝ“’bï#yNû²i~W[ûŽñê¹ýÄù]oî;Ǫç÷ë’bï#yNû²rL]ä`o)ßvC­/ÊëqÞ=W?¸Ÿ+­ýÇxõdþâÝrL]ä`o)ßvPë\ is°Vk@Ì’çd£!Ö›åu¿¸ï>¬ŸÜO•Öþã¼z²qnÝh¤ƒð0#q»wúe“yÊwÝëIò¶ßÜwUÏî'ÊÛqÞ=W?¸·|•y8ÊwÝ“’¢ï'yNû²i>VÛûŽñê¹ýÔù[oî;Ǫç÷Vï’¢ï'yNû²rT]äào)ßvC­'ÊÛr^=W?¸Ÿ+mýÉxõ\þâÝòT]äào)ßvNJ‹¼œ å;îÈu£ùYoî;Ç«'÷åe¿¸ï¬ŸÜ[ÎJ‹¼œ å;îÉÉQw“¼§}Ù´_+(;’ñêÉýÄùYAÜ—VOî-ï%EÞNò÷d䨻ÉÀÞS¾ì‡Z/•”Éxõdþâ|¬ îKÇ«'÷÷’¢ï'yNû²rT]äào)ßvC­ʺä¼z²q>UÐw%ãÕ“û‹{ÉQw“¼§}Ù9*.òp7”ï» Ðü« îKÇ«'÷å]r]ýY?¸·Ü•y8ÊwÝ“’¡Ì‘8yuÎÞIÈ;t:Ðü« îK¿«'÷å]r]ýY?¸·Ü•y8ÊwÝ“’¢ï'yNû²h~UPw%ãÕ“û‰òªƒ¹/¬ŸÜ[îJ‹¼œ å;îÉÉQw“¼§}Ù´*¨;’ñêÉýÄùUAÜ—VOî-ÿ%EÞNò÷d䨻ÉÀÞS¾ì‡Z•TÉwõdþâ7PƒùØŽÈ6¹ýſ䨻ÉÀÞS¾ìœ•y8ÊwÝs¿(¬£šÝ|hù›GTù¯¦â{\yê­÷†“Îç[ª̵t•y8ÊwÝ“’¢ï'yNû²}øž×&Zë}áżÎmº¡¤ÕóòŠÍÙ·_àuQäZº.J‹¼œ å;îÉÉQw“¼§}Ù‡åe–{Û.l¹.|¿Ø¾>QY»ëèð6’¨ò ]%EÞNò÷d䨻ÉÀÞS¾ìƒŸn'µÇžªßxi<îuº¡ÇüËWç¯Ã‚ëÏáÑ 5‘ê«äÏh¥’ó±¦|ÝýHë\MiwÈ\&[ôî¹Þ¥NÚ@_–¿I}¨³[¡·SÌÖ9ðGbÑ:¡kwiÝýÔùÔ}úzßá´Åÿ«¬]εyÿR—háëo†Óþ®±vzß ¤]Ö¦µRÖøS[áP]Ö®?«Eþç‡úš]®öj­–ºŸSª—A¯ÑÒ™;œ;œFñÙ]·Â«Ü©¨®TRQ\i)ë)dËNãc² ŒÚwˆûB)Ç·ëÕºžßMn,§­žŠðø_QWMZaÍ.e3tdïaÒkšìÎñ’¹‡º«UÒ[m6–Ø.·ºšk] µÓÇ­’g™bk‹šƒˆ2^ææsË2½ámµ\]®ê:·BDz3<-yc^Ý´77q‘¸ªu¸g VÇu˜zÕPÊxÄP‰iá3[˜ÜÌ7 æâê­Ro†\:Æ[F#6TÚý) ¹œŸ«Õ£õ۳쪃«å~ÙKCl¢eº’ :XóІ1¹’NM†d“ý¥ÛZšÕK[áMo…ÝjkU-o…5¾u©­Tµ¾ÖøU!—®«>?÷TëU8dµYœ´ŒxrÖçþYó 5¾ÝjkU-o…5¾EÝjó®ªø’ïjÅXVÛoº×ÐRÜnÔh¨›S3Œq±Ñè°ÆóÎwä9‰Ï,³Ʒ«TÒÐÔÖSVTRSËSJ\iæ|`¾!“´]ÎÜÆã—:ƒÏ)º¨bU²Ïm¼ášªìEQ@úʈ¢kÚKDŽk@lq¿¯ A h'y­¨ê›]5v!e6"’ÅmeuCê+ S:c3cÕjÎN̜ݻ"wó.žñi³^ZÆÝíT7Ç™`©²hçÏ–9(§³Y)㨎 ML©…´õ m;@–&·A¬xË®hoZÜîdC:­^ˆX>˜båÑÿ7'ú(ÏÄþ^îno «mêP/8†ím´Þ®L‘–§Ši+ à¦dôåæF±±¸ÄÖîÓÈ?Hw/@[ËþSAº“bÑÛù¿iæþ¯þÞo¯.ÂÓDè¥Ã¶‰#v¯I®£Œƒ«ilyŒ¿éi-09 ȼsµ½U'§¢¶Ö¶ÑDú*È)¸šé…%,‰E9v–àN“–yoZëT)h.w+e-}ƶ³WÓ@ÛÏñQX×;FM_âÙ¿­'e¿®]¬˜k K-<²X-o’™=ÔŒ.‰ ’NY€ $ó•÷>ÃÓÓÍO=’Ù,3Îj%ô¬-|§žBȸö]ÎPqmêÇ=\QMiÃ;D|‡%æ}¢¿Rcdr>7°´ŽlÌÙçØ[Jn©ÒÔ^mt¼ˆ(è®QÒ¾ž®¶¡ñ‰ŒíЋF'5Înydç72?Šèùɤ]É4ƔђiÛ™€óÄwogý¼Þ† 9†à­Š¶ ®*¨šÖG3)XÖµ¡­Àfh|À‡.·Âšß ¥¬ð¦³Â‰ÅÝo…5¾KYáMg….k3ž”gÿÍCí§[áT£“úU).Ü*¡q?04“þI¬ð¡ÅÝo…5¾KYáMg….ë|+Q®”2÷]oÇYMn¨–Á¡Å²67œˆ ïq ֳ È œÁçqæç_Úî§rAPê÷\­U5W:h#Œ¾ªFS5ÍwXLš@hä3ÝàYUó Šjú»#VÚªzQCL®–)& L ,È7þüÎáØÏ¬¦Ã˜n˜Ôk ®¥ŽŽ} F7Z×sµÙ¸È*iðö¦ šßOc¶EG9ÎZvR°FóánY^9¦uNºÏYc·Sá ¢¸]j* WTILÆjZÇé´º'1ÍvâZÓ˜#%óUZ£y|aÆÇnn#8im~”šìÎOÕêÀÑÈfzíÙöWOA‡°í½ð>†Çm¥};ß$.†™Œ1¹Í s›Ü\Žp<Ë!²ØÎyÚh:ê½´ÿGoç»›úÏû¹ü(qÄŽ¬ñ²¡ÑIh‚vºŽ®¢žz:¹Ÿ ®§Ïs4ä‚0AÑ#IºYpŒêµz i`ú`M‹—GüÜŸè£?ù{¹¹¼+¬n©}SpÝœO x|‚Š='‚ Ëx ~pNjÐ²ØÆ_òš Ô›þŽßÍûO7õöóxã“«’á=³ \î ”µSˆ[#åkg`xÑk#sN‹]¿IÌÌ‚y/OlÙ€AÜw®f· áŠØàŽ³ÚªYOŠ-##`æks€ù†å¸d2Í‹Úß k|*–³ÂšÏ ]ÖøV Œ¿òúÿü'"°ë<+s˨gks.1¸9Îä8Ý×É•u@ÏÿŠïæV o…a¸L×WÔ9i•ÄsfV g….ë|)­ðªZÏ k<(q‚ã5ºÃp¸SÀj&¥¥–háò9¬$7ø‘’òª>¨7[m’ñt¼WÜd¹Û*n-¥Šž™´n0Àù˜ütnnMGHürõmg…i›†°Ëj&¨n´‰¦k›+Å$yÈpqËx AçÍ4Mê“rä¼;+ðå;+q|”P›–Q6&ÄÙ4¤”ÇÖ¸ƒhiþßš­VZØQñÆ0äצëjCI|rº3Z=’ÍÏð޵uõv{-]¶e]ª†¢†TòÀ×G!¢Ò2 Ã%†¿aºø ŠºÅkªe;t!lÔ¬x¿3s‡€"ñÄž­35•UG 3b¢§ ž©âãøÐ*£c€dz¾¼´¿.qžYîÏ!°‹ª­I¼:pëmŒØSkô¤3fr~¯VŽ@×nϲ¶T8 Òâ:Ëà·SÍ=@€G°ÆæRêXÍHÑÍ›€Ï`sd·&ËcÈŽH·äjöÓýŸœvîoë?îçð¡Ç:³ÆÊ‡E%¢ Úê:ºŠyèêæ|2ºž7=ÌÓ’ÁD&édyÂ3ªÕ襃é6.]ór¢ŒüOåîææð®±¸W 6¥õMÃvq<áò (ôœp'-à‚AùÁ9«BËcÊh7RlCú;7í<ßÕÿÛÍàCŽ>N¬"K„töÌ-s¸0RRÕN!l•­ãE¬Í:-vý'32ä½=³fqÞ¹šÜ3†+c‚:Ì?j©eÈÉÕS[quζ÷nª¥³ÃcÛ`µ>&¶pý¡‡=Äi]¥ÙËDƒ–kÔëé(kÛ®¤§©JÙáÖÆ«‘¿’öçÌá™ÈûÖ:›u®¦­Õ•6úIª¦t¯…®{¢'3$f[žýlÑ\}GU‡Ó6®’¢ÂÑu‚áKB"Ž»JΨis®ÐÜ>·qËç^q&6İËp˜×\¹@]/¦Ûwq¦§lTÌv‰iŒëZÌÉŒu€ØÒÝíÑáü;¶KdvKche:RS Vjœ~rܲ'rÇÂÐÄØ¡Ã¶ˆãn³E­£Œ¬hcòÔÐ~p;y~Ærá]’éu–ÿ_+°”W)â}âJ˜%|“µpŽFõ9´—“Ap ?”zÓÕZ¥–¢£ ÖSÕK_OEJÙ$Tó™!âY"kƒ[¢CºÍÇ.ps]c,ÖV9Žmª€9”»H¹ˆ;W7äÛÍàU Ã^ ZŠH0õ¦:z­|M£Œ2]Kt†Y³9gÍ™A¥ê#xº])qL·Z‰d–e—‘l=ÃYë:yx† ÄÕ+ñtp~.B´íÿã3±¼óÊ3$ùYNðGº[êé«è¡­£™³SÌÀøÞÞb Éþþ!éùþøýYúÇŸ§ä~YôÅȶá¬õG¼œ‹aîÏYÔ{Êâ/¥˜õÕSä[pÖzΣÞNE°÷ g¬ê=åq0ÕSä[pÖzΣÞNE°÷ g¬ê=åq0ÕSä[pÖzΣÞNE°÷ g¬ê=åq0ÕSä[pÖzΣÞNE°÷ g¬ê=åq0ÕSä[pÖzΣÞNE°÷ g¬ê=åq0ÕSä[pÖzΣÞNE°÷ g¬ê=åq0ÕSä[pÖzΣÞNE°÷ g¬ê=åqp˜‡Ü©z¥XìÑE]Om}^ÏRîM•í«séå{te - cšÌò9ïq95Ž)˜º®¿‘l=ÃYë:y9ÃÜ5ž³¨÷—!owj …ÐÐÜö&Zm ¸Ç©ŽÒ9òƒ‹ ~-­ë9Ê7ó›â9lÆÄÖ‰&me dL€Íª…Ó©#Ža i8iO]¸3×Sȶá¬õG¼œ‹aîÏYÔ{ËÁXšå¶Em¹RÇ$uw›,5"¸Ë#_ó¼0°´XÍsì4h€B0ÕSä[pÖzΣÞNE°÷ g¬ê=åq15TùÃÜ5ž³¨÷“‘l=ÃYë:y\DÌ5TùÃÜ5ž³¨÷“‘l=ÃYë:y\DÌ5TùÃÜ5ž³¨÷“‘l=ÃYë:y\DÌ5TùÃÜ5ž³¨÷“‘l=ÃYë:y\DÌ5TùÃÜ5ž³¨÷“‘l=ÃYë:yQ»]ë)±-®ËGoФÖÃ5D²ÉPcÇákˆ®Ò']¸nÞ̘æ-RvÚÊ*nD|{dñÒFóQ›M@s6ˆÇ[¿V׿ýEnŽjgÅ{]¯"Ø{†³Öuòr-‡¸k=gQï-·ÅS%ɳ[ç¶Â#ª4í}KÌ…å€FÈÚ_#7ÝÈ‚ͯѼüMnn·:k×â¡dÎÊÍVsk´r(ºçuã6ŒÜܘ.ÊçÄÕ^ä[pÖzΣÞNE°÷ g¬ê=åEøšÜÝntׯÅBÉ•š¬æ×häQuÎëÆm¹¹;04]•š Íui£†‹$¶m)íÓ˃Ho`n—\3nz@æ§&bj²ò-‡¸k=gQï'"Ø{†³Öuò¸‰˜j©ò-‡¸k=gQï'"Ø{†³Öuò¸‰˜j©ò-‡¸k=gQï'"Ø{†³Öuò¸‰˜j©ò-‡¸k=gQï'"Ø{†³Öuò¸‰˜j©ò-‡¸k=gQï'"Ø{†³Öuò¸‰˜j©ò-‡¸k=gQï'"Ø{†³Öuò¸‰˜j©ò-‡¸k=gQï'"Ø{†³Öuò«ŒkYm›Œ‘I3)h'ÑÇ;¡{Ãcsˆ7®a9npÞ9ÂãñgTZz»¸ÛŸUHÊzÆÇQO=D.|°SÉ$‘ë5-cÕ½ºQÈçÈoÉŸíw\‹aîÏYÔ{Éȶá¬õG¼¹fã̵0²Ÿ Ó¾»]²’G\tKåÓçYÑal9‚âí¸}WTºˆ¦†š1[UVÖJú¸)›4å†:‰`sc1Âàó¥ ÈÓÕ‚2Þ3Ý9âv»žE°÷ g¬ê=ää[pÖzΣÞW\ÄÕSä[pÖzΣÞNE°÷ g¬ê=åq0ÕSä[pÖzΣÞNE°÷ g¬ê=åq0ÕSä[pÖzΣÞNE°÷ g¬ê=åq0ÕSä[pÖzΣÞNE°÷ g¬ê=åq0ÕSä[pÖzΣÞNE°÷ g¬ê=åq0ÕSä[pÖzΣÞNE°÷ g¬ê=åq0ÕSä[pÖzΣÞNE°÷ g¬ê=åÊ»Í-¢ÑUo²:º®åf7M’*ŒßO§iaɤ5î$Ÿâˆ $ä5óõL›tøzk…D”Ʀ¢:ÕM Ñ4‘h3*|Ì€ÆìÛ ˆ»>Êœñ^ù;®E°÷ g¬ê=ää[pÖzΣÞ\½F/½6ÏWs¦Ã¢nSQG¡U#Ü[²ÆùžÖBç5¹ÆÜƒCÎnæfz›5`¸Ú(® 0‘SNɆBøÎ“Aë\CK›¿q f;™\øš¨ä[pÖzΣÞNE°÷ g¬ê=åq15TùÃÜ5ž³¨÷“‘l=ÃYë:y\DÌ5TùÃÜ5ž³¨÷“‘l=ÃYë:y\DÌ5TùÃÜ5ž³¨÷“‘l=ÃYë:y\DÌ5TùÃÜ5ž³¨÷“‘l=ÃYë:y\DÌ5TùÃÜ5ž³¨÷“‘l=ÃYë:y\DÌ5TùÃÜ5ž³¨÷“‘l=ÃYë:y\U®TóUÑIO}MË*ŠvÆdfDÂF¹»òË{NãØ;Ó0Õ|r-‡¸k=gQï'"Ø{†³ÖuòóÜ;tÅ%¸B®:©¯Õ—KEtñUÔGKqØŽíTYdÒç†æ×;ñ‡®Ë›wlÇÔÕö^SŽˆ0:¾ÝKOœ¼U²™áüÛ‹EAÝ¿=YÞ3Ý9â½®Ÿ‘l=ÃYë:y9ÃÜ5ž³¨÷—?xÄu¬MqŠP&£Š Hdd†ÝUY4“K,÷ÑÝÖv3%aŸ×ωê¬[-5tð>vë$¯Õ0꣤ÿݑΫG-ùoç:,ø®›‘l=ÃYë:y9ÃÜ5ž³¨÷—ιEy°[¯Æèâ®¥Š¥Œw;Zö|;×/j±% ã1my¯¸ËB*™,sÂø@cšÙ2k ‰ÌÍÒ4€í=Ãs·îaªê¹ÃÜ5ž³¨÷“‘l=ÃYë:yrÒÖÝŠ§‚ƒ™éà€’–-–ÞÝ€pÆPíZ^Fˆq!™·6º_ª/W+uÁÕR6˜ÀòË‘•12FË× ˜žÒæ44fdÍ™‡k©ä[pÖzΣÞNE°÷ g¬ê=åçöŒ[{§¡¥¹O5Eí÷+î; ¡Î¤›[DM/,ÎW¤áªqùÂÝu9º^/øLšÚÉPÙë©f®Ù]K+^ÉÜØŒpɉœåÙ蹚.:z.x®›‘l=ÃYë:y9ÃÜ5ž³¨÷— Sz¿Eƒèw:ÊŠéoÕvã4QSŠ©ãŠz–´0=¢ýšIp&»²BÛØ.µu½N…Êá{Ž‚v™[5sãŒ[ÎiÒ¬Ó n‰ éf@# \‡k¢ä[pÖzΣÞZèhmõm×ÐÙÜi_¾)*o•QºFôƒZ=ŒÊåiîX’gÑÐOˆ*môÕM¬ª¦¸TRBʉ!‰°è6Fh73$ÜÖ»AƒòNytšûŒNÜ-4m©ª6úpƺV±­Î&õĸŒÿ°sñ\ùIüKY'¡¦¦a–k²6ïp‚ýTçåàkAþÌÂüÇø~Ó[éh°Ó-L-£’æ‹7¹äëRòsq$æ\NóÙ_¦,ÞMœ²ûLØjãëtÛ+%~WZã‘ùÁ˱—ḋ_êæ ú®ý“.Uús©ì—rlåÏuC£³Ùj)îpÔËI%ul¢±Ò×È “J–lƒš÷êÆol`nÀÔuêK‡´Æy2l·øç­Ç­©†9¾OÑhJÆÈÁ-dáÚ.ŒôiÈÏ"9‰¼~W§Çäz¯®ÿÏ;Ç—³×¹Ç[$¶í1F.´Ž’ª¦š¶–àÍkšê˜´ÀÕ»Huš[Ç0Í{v¶[-’Ôɤt¤I<’'sœÞIßý«MGp)†9*YUE)h2¶j gIá…™˳˳žK¹e5;[“cx Ãþþëÿð¾2êßçgÛÇÑñ ä½jk((k4¶Ê*j(dÚØšüâ~ZlÞ?%Ú-Ìs†|Êʽ³ÃÐâSg‡¡Ä¯±¸ÑŠ¢ŠÕ^ÅIK-UT‘AO $²Ë&‹#h—8“ É+&ÏC‰MÃE힇›<=%7 UW¶xzJlðô8”Ü1TQ^Ùáèq)³ÃÐâSpÅQE{g‡¡Ä¦ÏC‰MÃE힇›<=%7 UŽh šHd–ä|ÖBç4´Ktš{EΎÈ쭖ÏC‰Mž‡›†+C-ŽË(¢Yíï 0êf˜Õîë2seÌzÊ ÜöÊ*jœâ|'[_œoÑ/fñù.Ñnc˜èŒù‚Ûìðô8•Ûj£¥t‘ ‰é“¯sZZà3Ì€^ÐOcH|á7 Ö’‚Ãc ¬5”6ku-IiišV1ä¤yI>šØ«TÛLfJi"™ïŒº94€sZææ8p ŽÁvMž‡›†*Š+Û<=%6xzJnª(¯lðô8”Ùáèq)¸b¨¢½³ÃÐâSg‡¡Ä¦áŠ¢ŠöÏC‰Mž‡›†*Š+Û<=%6xzJn­k Õ,ªt1™ãc£d¥£M­qisAç–´‘ÙÑ0Xl¶´Do¤h†gTE”-»KIíݹÇMÙ‘¼éœ­ÎÏC‰Mž‡›†+SOEENèÝOIO Ž=Te‘†è3<ôF\Ã0^a^Ùáèq)³ÃÐâSpÅQE{g‡¡Ä¦ÏC‰MÃE힇›<=%7 UW¶xzJlðô8”Ü1TQ^Ùáèq)³ÃÐâSpÅQE{g‡¡Ä¦ÏC‰MÃE힇›<=%7 UW¶xzJlðô8”Ü1Z,Ej†÷d«´TÍ<4õq˜¦0c?”ÜÈ9 Úwg‘9r# ¸oËU=T¶ T•Zé]G|šMs¤rÌæ×9§>pâ9‰[ú½Š’–Zª©"‚ž:Ie–MFÐ3.q' ’R¯b¤¥–ªªH §…Ž’Ye“E‘´ ËœIÈd”Ü3ZÆPP³U¡ELÝLÏž<¢hЕúZo·9ÚoÌó7gÎU;†÷™ÂÁj¬|ŒdôqÈ÷¸¹îÅÎ$“Ù$’º=ž‡›<=%7 ÕW¶xzJlðô8”Ü1TQ^Ùáèq)³ÃÐâSpÅQE{g‡¡Ä¦ÏC‰MÃE힇›<=%7 UW¶xzJlðô8”Ü1TQ^Ùáèq)³ÃÐâSpÅQE{g‡¡Ä¦ÏC‰MÃÌ; aw²v; ٜڌõÀÐÆD¹¹®:]nüÜÆý–´öú¬Ãj²ŽžŽ¯Z*)©C-nd@ó†´Œ›üK³ÃÐâSg‡¡Ä¦ášÐWج•ôbŠºÍnª¥: C53Á+‹‹Ÿ¢FZD¹Äžr\~r¯Å"‰‘DƲ645­hÈ4``-†ÏC‰Mž‡›†*Š+Û<=%6xzJnª(¯lðô8”Ùáèq)¸b¨¢½³ÃÐâSg‡¡Ä¦áŠ¢ŠöÏC‰Mž‡›†*Š+Û<=%6xzJnª(¯lðô8”Ùáèq)¸b¨¢½³ÃÐâSg‡¡Ä¦áŠÔSÐPÓìû=4;,& }šÝTGG67!Ö·¬fá»­oÌA‡0𨂠XmbjvG2l‘éFØÈ1µ§,ÀikH›!—2è¶xzJlðô8”Ü3\Äv„UÝ*kŸ-Í×0Øæecc{-/,ˆ44 ™Î 9ï%Z·Y¬ö×µöëMš A‚‘`#pâ8Áþã~`·»<=%6xzJn­m,RÓEKK pA qECXÆhpn*Vk’Ê${=ºÚ$ËX))™ž\Ùèšßìðô8”Ùáèq)¸b¹¨ð¾ŽæûœxvÐÊù÷>¥´Q‰\ç‚KòÌ—fsß¿3Ÿ:Ío±Y-Ì–û5º±Êf`‚™‘†ÈZZ^2¢ç ùò$vVÿg‡¡Ä¦ÏC‰MÃ5¥ µ[(***(mÔt³U;N¢H`k+¾w3qÞw•´Ú¤ñ¾ÙDæ=“Fö˜C›3´¦iopÃþ£¼æ·{<=%6xzJn®Z #… ¤žŽ 1dŠš¤´Ï (" ”·=&†äì³9gÍ™Wg³Úg´rDöºmº!»#éÚaÈ@Ð#G @#w8[Íž‡›<=%7 ×0Ì+…ã·ò{0Ý´zí~Î(cë2ËOGG-,€óîXl‘:¶††œNø ²‘Ò9€i’膈‚äœ÷ÂëvxzJåðõmúªÛìž¹òò—ôuã,a¯Ž¢Û[ .©}M5N“XeÑÓcÃIçh‚ìnðö?.þ«˜'êº?öL¿Tâ¿Î­_¿²zü­øuþ®`ŸªèÿÙ2å_©º‰þÉpÿ÷&öÏ^3‡)(ª­­š¦‚’wèӀ驘ò²@rÍæÌ“üIßžgÙzŠçÿ„V }\Ùyç¯>nÆTÑQRÒ[%Ž&1¦ARæ™ XÖi"ݘcwf´­ßÏÇÃÛo•çÓ'ÌðòóõÉã:Örm«è‹g¡Åî®ë¨Ä0Ás½²–`ƒQKœpÆÝ=)ó9–yhoð™rß"qÖ˜Ûý-ÿ w½K0ýâÆË„·qLÙ*Þ×BâàÀÖ†™žÉæç%lù¿#×}7ÇÇ˽eøžŸdöËå9ƨtv™ñŠ¢ÿ‡ç¼[£·WÆ-RV¶9%!fæ1Ú-l™8åÌw­%Ö£Y°Š \ÐDé*ÌîÑtÕñkI¥‰Á°T8åÉÝaÞÀ4‡gÕÑ|GÖxÖ*­Æ·: ¥¾nT­Š³ÌKGk–(Y3¨H&Z`é4¤942V¼Ö¹›œ¶GOì×;íN!£’6ÕRO%±²RÄçÍLèâl‚—C#XK¥.hsŽ«ÕQTb~Ï9ãdu·»ÕºÍN¯šFºghCP¾id fCXÀ\ì†óܰÁ‰,³TÛ©™[”÷6ÌêHߨé5D NEÍ.µÙ}ÛŠ©‹-Êê;•¯g’x)ê)_Õ2S‡Å6¬¸¶XÁto6äà3Þy¹Ö¦‡×<[u®3lÔ•ñ™v™&š&©†h4$?T"Ñvó¢ÝÛÈ;í¹ÓÑ^íu•qÒSUiÍ.Ó Ý[†{<¢·‘—Z÷áÏ1˜Þ¬[«©n]llšXtHÉñÈèÞ7üÎc†|Ç-Ù…ÇZ°æ&µGm¸DëMmÒ÷!Qæ’de]PŸI® qi ÜZFò3æ+_ˆpÎác¶Rˆ­5U°ÔÖUM%Sé㒢c+€…ðÈ%h.#yÀ ÎG'hôuu-ÂÓÑË­“Kމ>9Æÿ™ÌpϘå»0¬¯.ªêoV,5Ö›mŽÞÇ]]X×Ó7n„É3›ítk[•¡¹‰GYÌÕ®f½KW-µÔ4N’;E41WÕTºCFóSXý:w²5Ï®fM DŒòü§jñìHˆªˆ€ˆˆˆ€µõ×›eÞßiª«du× ¤‡"]&ƒKÝÌ7Ðwœ¾nu°\}çÔUã{n%¥½ÖFa¬dµ8BX"e<±†FLEã7HsÙdùÉÚ$(Ý^qšÏW -ƬÅ,­B÷†4»D9îh"6ærÒq>ʳ5ʯ”¶‡2C=M4Õ,p@6'D×sÏ<æn[»›v|¾(×۳ë&Ä/6˜í·¾w˜4ÈK¡!ŸŒÝ3Ç\ÌÓ»™dÇøVãˆ.¶ÚÚ:šfÅGKQÔÓ—êõ’S¸E&ˆÏVDNÌŽÎŽç &™ö:K5Ê ­•T쑬eLôÄH:PÌøœw¸¹„\ÜÊêóÌ)‚®6ÌGÆZK5#b®­ª}E$¯tÓG3å,§p,hÐn±®çç`È äúBˆˆ¨""" """ "ÐÞ0üW<]g»ÕCM==ºš¡­d£76gÉNèÞÑ–]n©ÛùÁÑ#ç•«©ÕÊŠ®ÙPë;õÍÚcÍÚ.£…Íu,mÝùL1°ïÈg$›Î奢óùð5mE5M<î¡–ªªYµEïÑ¥Š:ÆLø"Ýù=»ænˆfþ燦} ]-k¶I)›OMm’8™IKhkÙ«ŒH tsO,ù´w‡B‹Šµà·Ez´ÕÝ%Á–ÖU|¹ºYæ’ZwÇ;i9äah Ü÷ì-t¯·AHÛýMDÌ’I uPcfkK4u @ÈÆ­Çò‰ß—6ýÇG\‹ zª8bÔÅ„mnª#›ü–î‡0Ü?°,Ê‚" """ """ ÇU#ᦖhà’¡ìasaŒ´>Ba£H†æy†dœ„ª‘ðÓK4pIPö0¹°ÆZ!0ѤCs<Ã2ÎBÑuIŽiúŸßéi©ç¨¨ª·ÍM PÆ^çI# 2›7 Ï0“®+õ>Ä÷‹õÖâÚ»LF²*Êpö†Féa–šX¢lš0ktYéJðCI–VEÀÅÔò‘Õ4•5TVéf7šÚª÷œÉ¨¥™ÕŽ»®ÒBK[›wötw¾¦wªƒN)jéÝ6Ñ5;j[¥cª¥’'D÷ÓLXáãgXF¬â2NÓZDEA˜I‚1E–ßj®†Ñ=5¶Ìë\-ŽºHÝQøÚW6G“ ƒF9Íš/î9‡©\úš_ª(íeE¸ZWC³Ó>VS¿\÷‰bvÉ kÈ{As%¹örµxõÄ^uwê~éðÝU$6ûl÷ »ÅEt²>}[\ÇM;âÓÒ†Q&‹eVææ\AÌ{‹,´6: )öq-=4q?gˆG“ZÐhÜÖîÜ; ˆºˆŠ‚" """ """ ,U•0QÒMWU+!§‚7I,95hÌ’~`Ê«])Í]²ª”G†h_„íÒÙ´Œž;-ß¼|È+Xï–ëÓe4¨& ÒæÏK,ÉÙè¸6F´–œŽDn9fJêXîp[_.USÃ$ñÇ¢zæFæ5ç>a‘‘›¹÷îæ9q”ØgÅ`¾QE%¾˜VÇTÔRVK[`&ÍÓFr0èèè9­Ë<™M…z›×Z/´WJËv¸*%ÐlºÔÑ?fs]e;[§✵¡Œ'6—9NÕãÑÅÚÜo¯±Š–ò‹)[Vè9êœâÀìòÈõÍ#Ÿ1»>p–+½ºûkŽçj©mMŽ{Y+Z@qcË–`Êiörݹs˜—TÞq]Î;–Á­¢¦¥d±4ºMº§\Â7ÉÛ‘Ïsš]hÏS{À·z¬9Ic¦6Ž;uT‘InÐlµI ™­‚V‚ÆHA æw<½ÚGZê»Õ¾’õGg¨uK*«sçe”Äò÷ëCuaÚ1¼è—æÞ`±WÅu·ØªóA5%MêgC †ZÈ#`iŽG´ 4ÜçžZ€EÄ.ƒQâ:«Í–¦ÑCjž uIªqª¯’½Æ ¡,°¼¡ÚYö˲8¿%¶IzmŸBåSä|l2Û*c‰î`%Ú2º0Â2i ‡d{æ¯Ù©.ìµTTÈÊ—HȳÙä1 –ƒ ¡º qÌdÒàNcçY! ©~&žéTøŒ1ÓŠz&4’Z Ò•ÎÝÎâ2î`ùÈZ{Å¿Wbêy$§µTaúi"’(Mt‘Jd+Ú!pyiÞÆi4f'<´CkgÄ–{µk¨èª%tâ3+[-4‘ #ö´ ™ssÇÎÝV ³RÝä´O_+b¢}|‘dN„ !¥äå‘»<Ï͹s–¬3wf&šñq¥·L×µ¬0Épž¥®v¾95¬Ö°ê44 ›3ipfñ¢>ãÁµÔXáØ’‚÷Q.² Ó$5B"Ýl¦[slaæ "ïÒ€r.ö:%öÙy|ñÐË6¶ŸG[ôÒA#³Ñ%’5®Èär9dr9s,Râk3*®tŒ¨š¢¦Ö"5ÒÒË<‘ësÐëci.'"Hny™È-&Ã7 eÒzêúJ'ë¥Ìl— «e¦ÐŽV™4ÌÓq:ÀÝ Á­sÈ;È1…ðu~¾]ké/5ê-ñÅ u¦ _;džBéM9g(ÉÛÉÓ“0rfO±Ð[oöË…¶ªã •SÒ9ͨ54²Óº2Ö‡;6ÈÖ» 9å’Å„ÿ5gÕVße"ûµÐVZ°È¤¦t57Â÷—ÌâØæ©vnsœ@$5Ï$œÈÁ|aOÍYõU·ÙH¸ówàbOÎ퟾“Ù=~Vü:ÿW0OÕtì™~¨ÄÛ?}'²zü¯øuþ®`ŸªèÿÙ2áÓô÷RŸ/R<<3ü\àïñÏZ)z“äÖEK{¸Cm Ž<¡É­ 3ÐÌäœÎíåt½Dÿd¸û“{g¯ dµ”‘¾+}¦M¡IU4ä{¡ŽBâL'=1¼œÉÏŸœéø×Ý®z¿W‡¾z³ßgèï?ðš£?Ö ÿò‡Ü]F Áÿ&ÙS«©¨ª’¥Áò¾g72@sàà¼Ú–û© Ž\CކÅÒ²Fä9ƒ‰¥ÓpùúìÏÏžõêX#͈*+iªmðÒMKRç I™l†@7–0‚ nÝ—do^þÿâñßgé?£ÇÓü6¹áú·;<Ý!6yºB^îÔ–z1WZÊÇD_¡ýŽj—‘9–Ä×ÝÇ®#!»~ðªÁ‰¬SÑÉY Á’SÇSOJéÇëg@ݼ8Oñ¸io##–ÖÌE­žn‡›<Ý!cÃwËn"µ²çh–Y¨äÈÇ+éäˆ<‡Ý6‚ᓆñ»<Ç8 l“tÄQÙæèq ³ÍÐâäMÓGg›¡Ä&Ï7CˆW‘7LEžn‡›<Ý!^DÝ1vyºBlót8…ytÄQÙæèq ³ÍÐâäMÓGg›¡Ä&Ï7CˆW—7uÅQÑã+N†‰õ&¶WGQP“)]¨–V4îëœá ·nÈdO8ºb6Û<Ý!6yºBÑâÜ[=’¶®:{P¬‚ÛBÛ…ÊSS«0Àç<Æèc²ŠC‘-7Ÿ2ψñ4yëÇ0ãÛKd§2d ÑA#{–çÌ;-wùsžuëýFåÕõ$ÃÀ·KI“ö|sÖ–¢¦ur:¤áÆË¬:zȬ³9¯Ï~pˆ‡ϘçZþ'ºz<ï—”fù>«íñž2¸ƒ_îú‹Éõ×Ùšö’Ýž Ý÷ÿîÿ5°¤Á¸®š*ª\?lš XÙ \ÓÌGZº %¾Õe€Ák¶ÁIçlM äÓò¾_·×<>?ƾ¿=yU\mƒmXµÔFèùti ËcÅ$oÑÌ9’±íÏ­8á™ÈŒÊ×Ðõ9¶QAKKMw¼ÇGNúM0–-\ÒÒ[ü^–e°F 9g9Õm~/Šm~/Šù˜­úŒx~ÙOd°ÛìÔ‘ôô±ÓDé/-cCAq òò^Uv¿Å6¿Å1ME¤Uv¿Å6¿Å1ME¤Uv¿Å6¿Å1ME¤Uv¿Å6¿Å1ME¤Uv¿Å6¿Å1ME¤Uv¿Å6¿Å1ME¥ÏÝpf¹b ýMž‡”èê›T*E4zÉ\ØÝCÞZ\àÝ á¿s˜ÃÿJÛm~/Šm~/ŠbšëÒ]ÞÇÖ].zO¤eqc£o(BÒNŒÀ3!™sÿ«Ðü· Àä³bŒ%mÄ7\µpÕÛâ•”sSÈè#£&Fœ\5@Áx!À­¶×âø¦×âø¦)¨ÐÙ°U%¶ç i»]kOWS[ =CãÕG<æC$€59å+ÀäŽì÷®¡UÚü_Úü_Å5‘UÚü_Úü_Å5‘UÚü_Úü_Å5‘UÚü_Úü_Å5‘UÚü_Úü_Å5‘UÚü_Úü_Å5æ¶SË~¤¼¹ò ŠZYé˜ÐFl®‰Î$ež`ÂÜ·öO>ì¹ú>§˜z’j ¡BZŒ×Þd½òs±Û·°eCq¦o9oé6¿Å6¿Å1MFŒ`»`Ö‘U_¥QUOYTã#I¨žÙ3$w[–y°0èä42níè߮ÖzšŠÚÆPÁIp­‹QS_MS$}h,2契 hçÌd2È€EݯÅñM¯ÅñLSQ¬´a{u®¾Ž¦õ e =E5-9supÅ3â{˜Ñ–y tF{#›D 58' Éoe¶šÛ¾²™Í- m§Óde!€6ä N{ˆ­Ësµø¾)µø¾)Šj3ÀÇG qºWÊæ´4Èü´ž@ç93>ûUv¿Å6¿Å1ME¤Uv¿Å6¿Å1ME¤Uv¿Å6¿Å1ME¤Uv¿Å6¿Å1ME¤Uv¿Å6¿Å1ME¤Uv¿Å6¿Å1ME sl¬½`뽚€ÀÙî’RLòÖ1² ;p$×[È1žc™¹õ&Ãw­uÊ¢¦¼OXj´œÖÀÑQ ±HÑ&«X[”¤µ®sƒK[Èd»]¯ÅñM¯ÅñLSQ­¦ÃlÔ†éSs‹77|³ëô÷~HÚ_ÍÌœŽ|ýç©^¹Ô ©](©ÆY)ijIÔI9nSÃ#@™ùdÈ€IÉv[_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦£”‡©Õ TñÓ²ûxtPP>ßJÉE4¬§§sâq­|%®þ¥­ÍáÇ"w“¢E:Τ˜b¦ž†2é„´pvÍ%%%AtfGÉ££4/ctŽÈ1­9€·Úü_Úü_Å5;ž  ®Ã’XeLT²ÖÏY!0ÓÌ^饒W0‰¢{tt¤9dÝ ßÏžöÕE¶×InÒ:XY n‘åÏ-kCAq<ç!¼¨Úü_Úü_Å5‘UÚü_Úü_Å5‘UÚü_Úü_Å5‘UÚü_Úü_Å5‘UÚü_Úü_Å5‘UÚü_Úü_Å5‘UÚü_Úü_Å5–*¸EE,´æIc±Ì/‰å¯nc,ÚG1ùŠÅµø¾)µø¾)Šj9ó‚i%§¸6²ñv¬«®4ÅÕ’¾&ËÏ!’ÈÚÁ¢ò]½§>Îk5‡ÑÚo²^ùFå]].»Y%Sã:fVS5Ç&± n¤ 27nË 7[_‹â›_‹â˜¦£Sµ6Éa³Ègš’ÈÆÇd-:ÖŠi)²“vðY+³Ë-ùv7*uÃ%²²Û«¤¦ª¢¦£s#;EK$­9¼;H—Jí-= ḃ™Ï¢Úü_Úü_Å5,Má µÇd±ˆ£¢©¸m•µ2=Ë12 PÄÖ~26½§òttÉî¬Å–b==×Ûm-Dn†sF"Ò|Nac£üc!Ü팆D+Û_‹â›_‹â˜¦£šºà:{¨&á½O$Ô¢Ž¶Oèí5‡½áшå¦áœa‡.rNõaxñ6Â%¼\èG)˜GK©tr¿v‰‘’Æö»DŒÀËq9ó–Ûkñ|Skñ|SÔh݃i$ºG_=Òç7ã`¨©ÎŒEWQ Z#š@Ö¦4rakIczÝÁN!ÁwSÒ¶ûo¦¹UÓêÝQK ª$HÙ4\í ´^Ak@=àšÝí~/Šm~/ŠbšEF}M×”j±ây!t¯¡c…8eäc™§ "Ö½Àk4÷ùïXêp–» NÜCxŠpÖ1´ÀS¾F2Òc4âsØ—\ZàOÎ2nö¿Å6¿Å1MF£ á*cj ¬®©ÓšZ8ªÒÊ8 ƒŒqè´ÚÍî.95£<‚¥†?4gÕVße"é6¿ÅsXgó6}WmöR)eŸªË*1çvßß?Ù=~Zü:ÿW0OÕtì™~¥¿þwmýóý“×å¯Ã¯õsýWGþÉ~œêCû%ßܟÛ9yš˜WPG$•ĵ±2 Ù¡~gJ2IÍç‡?9õ¾¤‡.¤xtžQÏ»ÿæzòK~Ói·CI-²æÙÃ"Ö‚RæÃdf‘ßÞ3>sξ‡Àðñòóÿ4ï×óþ±‹æyyxø–¾ß†­yÉPÙIÏXØéšðzAÂÁðƒ˜^›Ôîës®¯¸Ò××Í[0A,o™‘‡4½Ó ØÖ‚2§xÏy^hnU?G\½ouw½H…C¥»UOGSNÙ]b3DX\Ö4ïÈŒÇ\çs­¿+×ëðõ^IßÛŒ¿ÏÙåìµ·ÇX¶L9Qo¤¦µMqª®¹Œc&p GK=TR;>¼eÖåÏ™ æßp¸ÍŒ­Ou]|TW[4õ޶ÕC ,Œu(hÜÁ wã_¤ã¼ö2[ûµª×w¦×kmÂí!T • üù8š¢Ü'…[[ pÃ6QU¯S8¡‹Y¬‹´s!­.`\Ëä}¾’ŽŽäÜKq‰øŽåu¢£°¼UELÐìžCLQ0õ¬ÑÏ2AÖ|í]BÅOO;^Úx"„=î‘ጠÒsŽnqËœ“¼žÊʨ""" """ ""á1)¹RõJ±Ù¢ŠºžÚú½ž¥Ü›+ÛVçÓÊöèÊZÇ5™äsÞârkWv±ÍI ’ÃúÈ\æ‚cv‰n“O`è¹Ã1Øq”£ˆÇ·»µÂèhn{-6†\cƒTÇ éùA‰ÅÀ?Öõ„åù‚ÍŽq¶ cbkD“6²†²&@fÕBéŒÔŽ‘Ç0Ð4œ4ˆ'®È\ée±ÙeBK=½â€FLó½Ýf@l¹‚ÏYAA[žÙEMSœO„ëbkóú%ìÞ?%Ú-ÌsŸ0SƒŠÁXšå¶Em¹RÇ$uw›,5"¸Ë#_ó¼0°´XÍsì4h€Bï–º‚Ãc ¬5”6ku-IiišV1ä¤yI>šØ¤(ˆŠ‚" """ "" -ÚïYM‰mvZ;|U&¶ª%–Jƒ†8Ÿ \@ v‘:íÃvð`Ç1lê“¶ÖQSr#ãÛ'Ž’7šŒÚj™´F:Ýú¶¿0ê,xëts]ë Õ,ªt1™ãc£d¥£M­qisAç–´‘ÙÑ0Xl¶´Do¤h†gTE”-»KIíݹÇMÙ‘¼éœ©ö9i±Ä ãOflÕ5tôrÆjÃC暥° h& ÉÒ o¹f£·«Ä;-5[%¢˜WQRŠš–åe.@4½¬©|mä»xÏ-á¹;-‰µZËbi¶ÑèÂ"†äÀׇ·-Û²sZá—1h<áX¨‚ˆŒUÇ4d‚Xö‡ ÁÌÌ@?Á-oÅÕw+…¢žßgc¢¹2¦}9êŒo†$…Ž.`aÉçZzÌÁ  :9/ÒŽ‚ÕÑ”ÒS:gS¸OO%$¢M &äÉšÒYÏ›ùš'pqoC uf®::vT<ål@?®ÑÒß–{ôŸÏ¢ß˜/¹©©æ‘²MOc\ֹ̀촀?1ÈgóäŸcê#¡c¦k!h/k¤ÐrÞÈf<9ö¾!Š8ad0ÆÈ㡬cMh€sö¨""" """ "" N1­e·^n2E$Ì¥ žwGî…ï Î HÞ¹„å¹Ãxç ÅQièjîvãn}U#)ëE<õ¹òÁO$’G¬ÔµŒ#VöéG#œ!¿.ßZ¡½Ù*í3O =\f)Œ$Ïå72AÃ6ÙäNDˆÃ.òÕOU-‚Õ%EF–ºWQÆ_&“\Çi³9µÎiÏœ8ŽbT½ûqæZ˜YO†éß ]ήÙI#®:%òÀésƬ谶œÁqv‰ÜN¾«ª]DSCM­ª«k%}\ÍšrÃD°9±˜ápyÒ…äiêÁoîïABÍV…3u3>xò‰£BWéi¼nÜçi¿3ÎtÝŸ9TîoÜbdW ª²6=ò1“ÑÇ kÞâç¸78’Od’JrŸMª"*ˆ€ˆˆˆ€ˆˆˆ€ˆˆ8—c¹¥´Z*­öGWUܬÆé²EQ›âÉôí,94’½Ä3üQ¤œ†¾~©“³cŽŸMp¨’˜ÔÔGBÚ©´&’-eO™Ý›dwgÙ]k°¦{'c°Ý™Í¨Ï\ dK›šã¥ÖïÍÌaßÙkO`/ªÌ1†«(éèêðõ¢¢š˜2ÑFæD8kHÉ¿ÁOµúhª1}é¶z»6ÃršŠ= ©âØ¥–7Ìö²9­Î6äsw03ÔÙ«ÆÑEpi„ŠšvL 2ÆtšZâ\Ýû‰1ØË }ŠÉ_F(«¬ÖêªQ3ª3S1츸¹ú$e¤KœIç%Çç*üQ²(™Lk#cCZÖŒƒ@æv#éD@DDD@DDD@U®TóUÑIO}MË*ŠvÆdfDÂF¹»òË{NãØ;Õ”AæøkÕÛðEEÆñU5Ò®Ÿ Òß\ú‡Å±ÓG)00GCZâCã<=µ›ËtÅÓYa°Öì‘ÕOJkDSµñii“ˆ4–’KzÑžíõVÃÕqSÅUaµÏ4:ˆ%$nG–ŽƒAkrÝÝ’ûä+', Ï#Û¹M¹åY³3^3'¯ËK˜‘ÏÌTû_§[НT¸ŸÚtÄšúÈèl§T2†sMN÷d:áøã&þÄrxÁ7TilØ'Ü«`:©¬Ð\.ZQ#øÚu€E Ø4œ$ü·FÞ·qçËÑ¡‚dšH¡Ž7ÎýdÎk@2;D7IDztZÑ™ì4Â×Ua¼;VÊxꬩÙM¢ÉG„Qe–ƒskrÝÝ’rŸJtxŽJ‹¼vƒoЮSGQ»1,hsfG®‡!»|„gÖ•J÷Wt¾–ŽÕ|–iŒÑ>{siã0ÃK˜Ö>Wå¦×¡“†g!¢@q[›e’+Í}ÝõU5u•¬2O¡ø¨š\[4ZÞ´»Ÿ7É98kTÝ…Þ§Úf¸‡¶AW%n˜9¹h»L,ÆC#žì‚#šÀW»µ}}­Õ×=µ—kCî2AªcE |@DÒÐ ŒszòNqüá^ߢÄ8¢ÛGs}l¬µÁWnŽ­‘A4¯ªhflcIgâ£ü¢ã¸ïÞº U²‚¢¢¢†ÝGK5S´ê$†±Ò»çq7çyT]„ð««f®8fÊj§Ök§41k$Ö$ÒvŽgH9ÀçÎ Ï84x 請Ü-WTM±ê4•Ò@&ŒÈÙKš6pcvE‘ÒA “2s¦…š¿Z›SfÅØ°×ºÞ+9DKO,0²7±²¸4Aaqh‡ëíÄè{j 5ž‚ ¡´ÐRÅO!–CNÆ67––—4¸–¹Ã1¿"GeUƒ ah(j(!ÃVh©*tuð2†!º'6é47'd@#>bœ^µgâÊÜ/qUƒ4ñÔrUutmŠvõ>X›ŽEæGÀ††fÂIËŸ«¿âZM†¢ººKÃn6GÁ-,òɱÊZÈ\aŠ ðasˆ•ƒ&ç¼æÕÝQá¬9EM%5´ÓA+$ŽHâ£{d 4€2!ÁŒvt[Ÿ0Faœ9­ö¦aûKmïv›éEb';ç,Ë"|9'´ö ­]oS¡r¸^ã ¦VÍ\øãÇ3štë4Ã[¢HºYÈ–üÍŸUÛ}”Šôö{LöŽHž×C-·D7d};L9hänç †üÊ?ªí¾ÊEÇ›¯_¿;¶þùþÉëòßá×ú¹‚~«£ÿdËõóó»oïŸìž¿.~«˜'êº?öL¸týGÔ^6IÔ—éŒòdÝŸõ£ù³EíÃô:2±²0KW8v‹€#<©ÈÏ"9‰·ýDÿd¸û“{g¯Ô”UVÖÍSAI;ôiÀtÔÌy Ù 9æófIþ$ïÏ3£ãz/¿Îø÷·ñxÏ.uët¸ó>ßTÚª) A•“[§Ñˆöt¤ ,Èv]ž]œò]£i©Àëcx ðMµ}lô8½ÕÝu†.w¶RÁ j)sŽÃ§¥>g 2Ï- þó-ÿ…ø½wÏ]çíÿõãéùsÙçÀë›Ù¡ßÉ-Cç’äÛ€¶6[m· ¹ y2Êø´â:í0çDrk’ÏímäwRTZ£®e•tÌ«~Ž„˜ ¤[“sÌæ"”œFþ‰ÊÎÏC‰^)RËôø¶–îfÅ’Å”!רt*"h§º 4bdt]3FzòÛùY´»Õ°EEÖ« RÏzl¢±Î,")‘Â7½ƒòèÃ[È’2ÉÚr6»<=%6xzJʉÚr1lðô8”Ùáèq+*'iÈųÃÐâSg‡¡Ä¬¨§#ÏC‰Mž‡²¢vœŒ[<=%6xzJʉÚr1lðô8”Ùáèq+*ó¼UkÅ3uSÃwvÑRÔÚékÌpVJ×Aé'>HÄE ’@k‹òͱ·­Ósƒ´äwû<=%cvÄÚ¨é]$B¢F:FDdëÜÖ–‡8 ó ´ØÒ8^qÕ’[¥|õqÙîU“UY¢n™´RçEZ_!/vc:wuÐçèîcì…²êq >+²×Øé*ŸvêØ«*iéõÒSÆù¨É1°É“ 9æˆk‹tK´äv´ÛLfJi"™ïŒº94€sZææ8p ŽÁvMž‡¼Ó©üØŠšý MíÔrÝ®rUCYkÔÀÊwO<‘ÌÙKKÜòη?Éy: O¨'iÈųÃÐâSg‡¡Ä¬¨§#ÏC‰Mž‡²¢vœŒ[<=%6xzJʉÚr1lðô8”Ùáèq+*'iÈųÃÐâSg‡¡Ä¬¨§#ÏC‰Mž‡´—‘z›Y©¨ë*©-{-LõŽŠ9²=’SêãsÜÓ£¤.á‘ ;-àÇZ.=R]YkÛc©Ùª+ÛC?ôFFØÒú‡dÝ̘2fç̈·"äí9™³ÃÐâSg‡¡Ä¯?¨¯ÆòÒÕG•ð9ÕT‘ÐL-í.};ëÉe•¥¤5â"zÜš3~@æ#è®’âJJ Èc²Å+E%tRë«*' Óˆ[C¥™ ý`9µÚr7Û<=%6xzJã-3búëåžúªÊë&¹2Fu‘ËL"…²9‡69Ž“'¤á§ù$dÌø‚¿Gmô¶ÚXnFY§¤”ÕÀa ̽Ï|qÈä³™Çq9æ;NGY³ÃÐâSg‡¡Ä¨¢v•ÖM&q´éÌÍ»w;›Èüã!—Ìdí9¶xzJlðô8••´äbÙáèq)³ÃÐâVTNÓ‘‹g‡¡Ä¦ÏC‰YQ;NF-ž‡›<=%eDí9¶xzJlðô8••´äV«Ø©)eªª’()ác¤–YdÑdm2çr™%*ö*JYjª¤Š xXé%–Y4Y@̹Ĝ€fIZΨõV¬ˆ.t3jjé-•3ÓÉ¢£#bqiÈ‚ð7¼ÿb>¨,ÄvÙm׆S2Ø¢ÔšÖ±ñÒÊèeŒlÀ9#fYÍ&÷†–‚@Ó‘êû<=%6xzJà¢f7¨©£‘×»•~<ó¼Ÿ×¯‘8ë?Ìmþ–ÿ†»Þ¥˜~ñceÂ[¸¦l•okƒ!qp`kC@Ì€Odós’¹~©w¨`cjp´Õ.cF²H›RÇI—9k ~bì¼=•ßXï¶ËÑ™´ÌçÀddÔòBæ‡g¢t^Ðr:.Þ7n?2Ñïöüo‡<ç'ôãÇÓëôúü»á~ÿ«¦EÌâÃ,ñÒA«®¨¬¨Ùééé´4ä~ƒä;äsZlo;Ü9•:¼c‡ij¦‚jé2å“ÌÊY_.áó5¦6ÙÒp˲°ã÷kÛ²EÏSÝ)§½TÚ£3SÒÁTçîÐs%t­nG=ç8]žîÈçß•äüfÛ4ZÄOÆm³E¬DüfÛ4ZÄOÆm³E¬DüfÛ4ZÄOÆm³E¬ZúëͲŠïo´ÕU²:ë‰RC‘.“A¥îæ€h;Î_7:c÷6èÑrלGf³ÕÃKq«1K+C€½á.Ñ{š¹œ´œ@ϲ¬Ír‚+å-¡ÌÏSM5KÐ ‰Ñ5ÀœóÏ9›–îÁæÝ›¹·@‹Ÿ³\ ºÑÉUNÉÆTÏLD€¥ ωÇq;‹˜HðeÍÌ®§ã6Ù¢Ö"~3mš-b'ã6Ù¢Ö"~3mš-b'ã6Ù¢Ö"~3mš-b'ã6Ù¢Ö"~3mš-b'ã6Ù¢Ö"~3mš-b'ã6Ù¢Ö"~3mš-b'ã6Ù¢Ö"~3mš-b'ã6¹]GI_LêjêX* sšãч´–¸9§#»0@#æ j*¤|4ÒÍT=Œ.l1–‡È@Ì4iÜÏ0Ìó•R>ifŽ *Æ6ËCä f4ˆng˜f@ùÈOÆm·E¬DüfÛ4ZÄOÆm³E¬DüfÛ4ZÄOÆm³E¬DüfÛ4ZÄOÆm³E¬DüfÛ4ZÄOÆm³E¬DüfÛ4ZÄOÆm³E¬DüfÛ4ZÄOÆm³E¬DüfÛ4ZÄOÆm³E¬DüfÛ4ZÄOÆm³E¬U®U°Û褬•/Ž<³m=<“Ès ndms?`†þ`ŸŒÛx‹Ž±âËEꦊp­‘•´ÒÕA4´’@ÇÇ„5®p:öà NNß¹o“¹¶ÍŽ:êY.sÛY.uPCòG¢zÖH絇>c™û¹÷oçÙOÆmf®Ûn«­¥­ª ¥¨ª£.4ÓK \ø ²Ðq·<†ysä­-bÔá¼ClÄ4ÛU¯m| ‘’ÏA=;$kÁ-s Œhx g›sç8L~æÝJ.IØ¢ÊÎXÖÏS#Bg®×QÍ„]^Ý& cNªL‹4ÑÝØWl÷:k­3ª)c­yaTSS?<ÜÙZ׿œ ¹þb˜ýͺ\lÇÎÚ—E^÷ f‡8ìÒþ1¥úÇÖþ4idÜÙ¤3 vUËuúÕp¶T\iê\)éK›Q­‰ñ>Ö‡8=Í r n öS¹·L‹‹Óê$ÕU5ì‘‘A;j žZ f±Ù†<‚A wÌVÁ׫kl¢ó´D@!íÅÙ—hè襥¥Öèåž{²Í1û›t‹‡µm—Êjļ™lÈÓMRU/3¤ëGñþkíø× ²‰µn­ŸDÊøLBŽc3Ƈ8:- 6è´‚IhO8_vÌcú®Ùì¥\ÙÅ—¬U¦«BØ+\dÚåËlªŠgå©<Ú®´yö2_˜¿ÕÌõ]û&_¨.ÿÛ¿|ÿdõùðëý\Á?UÑÿ²eús©ì“rlååXèY˜ùŽ˜Ã1–Ñ)÷óÿ;'œúÏRŸ/R<<3ü\àïñÏZ)z“äÖEK{¸Cm Ž<¡É­ 3ÐÌäœÎíånø^ß_Ÿ|ïòÿëÊõù{˜]gR¿Æ]oa 4_ÄŸü¤ þTgúÁ_þPû‹¨Á˜?äÛ*uu5RT¸>WÌææH`ÜÜ¿“òý^~»ãáVñ½ž>zòˆÄØz Íç×ËKE1´×º¤ºxÞ`‘ 0äwë /ìV;Bà¨-wêz›%­•÷»uEM£š[ežŽ§t­}I=±uúÑ!ÍÁÌo[ΰlót8„Ùæèq åý>‡Ûί=N᪦¯ŠRÊøðý5¶Ï5K‰’žxuú2’ZF²3¤ÜÎçnùýeÙæèq ³ÍÐâì9X‘eÙæèq ³ÍÐâ±9X‘eÙæèq ³ÍÐâ°åbE—g›¡Ä&Ï7CˆNÕ‰]žn‡›<Ý!;V$YvyºBlót8„ì9X—yÁµxÞÛ‰iou‘˜k-D–™O,a‘“xÍÒÁvY>B2v‰®Ï7CˆMžn‡‹ÊáqF¾ÝŸXè96!y´Çm¸µó¼ìÁ¦B] üféž:àÎfÜË&?·Au¶ÖÑÔÓ6*:Zˆæ¦œ¸GW¬’Â)4Fz²"vdvtw8i4öÛ<Ý!6yºB‡ÛÍ𦠸Û1i,ÔŠº¶©õ’½ÓMÏ”²À±£AºÆ»Ÿƒ 7“èk.Ï7CˆMžn‡¬²¬H²ìót8„Ùæèq Øœ¬H²ìót8„Ùæèq Ør±"˳ÍÐâg›¡Ä'aÊÄ‹.Ï7CˆMžn‡‡+,»<Ý!6yºBv®rñ‡â¹âë=ÞªiéíÔÕ k%¹³>JwFöŒ²ëuNßΉ8ä­]N®TUvʇ\iߨ®nÓnÑu.k©cnïÊa‡~C9$Þw/PÙæèq ³ÍÐâú_·O«j)ªiçu °ÕURͪ/~,QÖ2gÁïÈ,ièõß3tC7÷<=3èjéh«]²ILÚzkl‘ÄÊJ\ƒC^Í\b@[£˜ygÍ£¸·¦Ùæèq ³ÍÐâ°ûp¶¼è¯Vš» ¤¸2Úʳ¯—7K<ÒKNøçp#-0"p'< -džý…®•öè)©¨™’I!®ª lÍifޤÕ¸þQ;òæß¢YÙìót8„Ùæèq ô}ªQGª£†-LPhFÖê¢9±™Éná¸s Ãû̲ìót8„Ùæèq ö'+,»<Ý!6yºBv¬H²ìót8„Ùæèq Ør±"˳ÍÐâg›¡Ä'aÊÄ‹.Ï7CˆMžn‡‡+,»<Ý!6yºBv®gªLsOÔþÿKMO=EEU¾jhb†2÷:IXÑÙ¸fy€Ìœ€%qXŸ©ö'¼_®·ÕÚb5‘VS‡´27K ´ÒÅdуXt ¢ÏJW‚H€­ìót8„Ùæèq [*ý¼ò.§”Ž©¤©ª¢·K1¼ÖÕW¼æME,Ψ|pÝp’XzÜØã¿³£½õ3½TqKWNè)¶ˆé©ÛRØE+U,‘:'¾šbÇß:ÀÂ5`’õýžn‡›<Ý!?Ê}±"˳ÍÐâg›¡Ä+Øœ¬H²ìót8„Ùæèq Ør±"˳ÍÐâg›¡Ä'aÊÄ‹.Ï7CˆMžn‡‡+,»<Ý!6yºBv¬H²ìót8„Ùæèq Ør±"˳ÍÐâg›¡Ä'aÊò¹0F"¨²ÛíUÐÚ'¦¶Ùk…±×Iª?JæÈòaphѧ9³EàÇ0ã•+ŸSKõE±¬¨·KJèvzgÃJÊwëžñ,NÙ$ yh.c"$·>ÎCØvyºBlót8…>—íæ·~§îŸ ÕRCo¶Ïp«¼TWK#çÕµÌtÓ¾-=(eh¶P5naneÄÀ'¸±RËCc ¢ŸgÓÓGöx„qi5 Íníð2 e³ÍÐâg›¡Ä'aÊÄ‹.Ï7CˆMžn‡¯br±"˳ÍÐâg›¡Ä'aÊÄ‹.Ï7CˆMžn‡‡+,»<Ý!6yºBv¬H²ìót8„Ùæèq Ør±"˳ÍÐâg›¡Ä'aÊÄ‹.Ï7CˆMžn‡‡+‚¡À±MG…(ï´ôôÖ{è*"~ocæ"˜4‘hÔ¿yÈ‚ZrÏ›_k®’Ù²YíUIWj­¬©}DšÝe+i[+Z42v–ͤH=vDqé»<Ý!6yºBŸKöóë­º¾ý~¿Oo‚H5Mµ²õ°É sMIW4îh%¹–Ø4Ú9æ3Éb¥ÀÕ7,UQwÅTvŠªj‡T¼Ò1ï•­2EC?)Ëe“~C¤ ÿ'ѶyºBlót8„ú>Ú|+EYmÂö›uÂfÏYKE 5µÅÁò5®p$s œÈÍhp†©¶b .\c°ÓW@h­2¹ìåÍ"Gþ.0 CH4ž¼æy‚í¶yºBlót8„ì9\\–ÜG.)¼ÖÔY¬56ÚúQ¥¸ÈLˆÔ9ºl4å¹?\á™Ñž¿™|á,5Sk¹MW%žÇC³Fè¨h¥s¢£-ŽVºxÉ£Xý`a¬9ï'0{mžn‡›<Ý!;W[pÞ.µÑSŠÙWhµ:Ùkt²Ë¡3\ø³–l™›Hl-ë[¥™'xvÛ Z.ô’²ÝjG6w:¸Ö:¦I%x‹"ã©&¸°i5¹ˆ£úzýžn‡›<Ý!;·šAƒ.솺Y­ô53TOáŠ[ýY}9²(«1™³:`ndzâúÙf½Ø0%Ùe ¨¼7Y#Vç¶ l²ºG’@s´F›²ç'!™ÞJëvyºBlót8„ú>ÞyA‡1E¥ºCOh7xÅLUû„’²¤N!&gßeñòúþÌ¿/Ûåëð—Æñº¥¾ãêH#†—Ä#…¡±G4¬‘¹`âitÜ>~»3óç½z–Å3b ŠÚj›|4“RǹÃRfcÛ! åŒ ƒ·eÙךø—wÔ^H箾Ì×´–ìðnìh‡¿ÿwù­Ÿ/âz½^«å'¹g0·Dg¸9´@ÃS‚pÜ–ö[i­°[è)œÒÐÆÚxÝ6@6RÓn@´ç¸€yÚ܃ ‚FÍ &`xkÚØZìˆÏx9|zû_1ÑÃn•ò¹­ 2?-'9Î@ Ï€¾ÐAZËm†áq’ª*FRÒÉ;§–JȃZ\^æ4‡< ³-–@…«Ä8²Ók5ô•’ÐUSÒË3g©·Nê~²#!-p lÚ-ÅŒ~–MpÜAÊÎ9¶V^°uÞÍ@`l÷ I)¦ykÙƒ¸Hk‰-ä˜Ï1ÌÜú“a»ÖºåQS^'¬5ZNk`h¨†X¤h“U¬-ÊRZ×9Á¥­Èd2A¹“avT¾˜×T™<´à6‚¡ÁóFç5ñ0†d÷‚×u­Ì3ëêLs…ÙOO9¸Èb†M&ÒÌíSË ¥ÉŸ‰ÍsI“D×ÁV)°Å„µ'aºTÜâÍÍß,úý0íß’6—äGss'#Ÿ?yêW†®uªWJ*Cçq–JZZ’DµN[”ðÈÐæ~Yr rAÞ""" """ """ "" V2Ãt¶Ê+”÷Zê-º™â –èšžeÓÄrÒ%ã!¸åñQpõ4PISQYN&ŒË£-º¡ŽŠ="Ý9ZYœ,Ì h9fKYSª©ã§eöðè  }¾•’ŠiYONçÄãZøK]ýK[›ÃŽDï'DŠuI0ÅM= eÓ hà4íšJJJ‚èÌ“GFh^ÆécZ sofÇn(ç‘ÕuNl5†„êè*_P#]a¬&Gó Ï 3;ˆ'I;*ib©²µ’±¯h–'FðÌi5À9§ç9ˆ\õÏÐWaÉ,F²¦*Ykg¬˜iæ/tÒÉ+˜DÑ=º:R²noçÏ{j¢ŠÛk¤·@é ,,†7Hòç–µ¡ ¸žsÞPYDDD@DDD@DDD@Uo†ßi¬¯m4ÕFšÌ …¹É.‹IÑhì¸åð•ib«„TRËNd–1+ÂøžZöæ2ͤs˜ æ0Æ3‚çnšºà-ÔÑ2¢ pê*ãV5³âìÃwåžë¶Ûd—:¨_+ŸrªmTÁä×c‡&ä7 šw缟ìÚÜ5-N,§Ä#Ýá}3t±²›P"y‰Ò3®„¿'˜XIÒÒôKsA–ñx¯·âK5'ÓKo¹Îêm§jp–9D3MýV¯"ÝrÏLÝÍ»~¦Lliñ„–š*Wè \æÑÖí5±‘—‡ËLÆ5¯hä\â^Á£™[M†¥½Ü¨+›ˆîöÓ@ým®ÔDèš`Ì5Çv¹šOèódH [Â÷k½Æ®åMt¶PQš)}%sêX÷–é9¤º(È-œÀŽ».pWÄX3 C‹!ÅÖz{œl¦Xi£a‘ÓHâ¤_ÖŸ4’ úKec¶Ah·6Š %”i¾GË)ò=î/sœ@2縃—­Æ·;mTÔ—<;U奎 €”—I9ëKQS:¹RpãeÖ=dVYœ×ç¿H8DCçÌs­¯RÙ.þäþÙËÈìÔº‚9$¨ž È ­‰‘FÍ ó:Q’No<9ùΟêóöysÂòÿ¿ÿýÞÏ ß9×­Ò`ÜWMU.¶M¬쉅®iæ#­]’ßj²À`µÛ`¤Œó¶&†òx;ðÕ±ï2*)9ë3^H8C˜>s ÓzÝnuÕ÷Zúù«c†%ó20æ—º`á›ÐFQ´ïï+G·âû||/—쟽xúþO¯ËËž3ÿÈîö¿Å6¿Årø†õs¢½PÚ-6˜+ê*éj*sš³PÆ H$1Ä箎qóÆŠ¨‹]]U‰”ô·h)êhŸ¶‡;U%E4.2 ˜Z*˜üp KV,øµö½kñ|Skñ|WŸb wSož¼RY£©¦ ­u-Ed•26C`†]'˜â‘Ìß1nöèY%Ã<–k'ª¯¼:ÑlµFíUECê䫸¿6F+g‡J<ØâòL/v‡ZÖÖ‚IŸµÝí~/Šm~/ЬŠâ&ªÎ×âø¦×âøªÈ˜†ªÎ×âø¦×âøªÈ˜†ªÎ×âø¦×âøªÈ˜†ªÎ×âø¦×âøªÈ˜†ªÎ×âø¦×âøªË„Ä8¦åKÕ*ÇfŠ*ê{kêöz—rl¯m[ŸO+Û£(ahkÖg‘Ï{‰É¬qLÅÕz&×âø¦×âø¯;Ç·»µÂèhn{-6†\cƒTÇ éùA‰ÅÀ?Öõ„åù‚ÍŽq¶ cbkD“6²†²&@fÕBéŒÔŽ‘Ç0Ð4œ4ˆ'®È\™ñ;]ö×âø¦×âø¯8ÁXšå¶Em¹RÇ$uw›,5"¸Ë#_ó¼0°´XÍsì4h€B0ÕYÚü_Úü_Y5Vv¿Å6¿ÅVDÄ5Vv¿Å6¿ÅVDÄ5Vv¿Å6¿ÅVDÄ5Vv¿Å6¿ÅVDÄ5Vv¿Å6¿Ås—k½e6%µÙhíñTšØf¨–Y* bâ|-q5ÚDë· ÛÀ€sųªNÛYEMÈlž:HÞj3i¨fÑëwêÚüÁÿ¨±ã­ÑÍLø¯kÒ¶¿Å6¿ÅpSc‰„ÆžÌÙª(jéèåŒÕ†‡Í5K`Ñ:L“¤@Þ rÌ;GoWˆvZj¶KE0®¢¥5,1ÊÊ\€i{YRøÛÈvñž[ÃrvLø®›kñ|Skñ|WoÅÕw+…¢žßgc¢¹2¦}9êŒo†$…Ž.`aÉçZzÌÁ  :9/ÒŽ‚ÕÑ”ÒS:gS¸OO%$¢M &äÉšÒYÏ›ùš'pqk>'k²Úü_Úü_N#¡c¦k!h/k¤ÐrÞÈf<9ö®"j¬í~/Šm~/Ь‰ˆj¬í~/Šm~/Ь‰ˆj¬í~/Šm~/Ь‰ˆj¬í~/Šm~/Ь‰ˆj¬í~/Šm~/Ь‰ˆj¬í~/Šm~/ŠÐcÖ[p…æã$RLÊZ çtqÎè^ðØÜâë˜N[œ7Žp¸üYÕž†®çn6çÕR2ž±±ÔSÏQ Ÿ,òI$zÍKXÂ5on”r9À€rò™ñ^×§í~/Šm~/Šà[Œo2ÔÂÊ|7NøjîuvÊIqÑ/–L ž5gE…°<æ ˆ;´Nâuõ]Rê"šhpÅmU[Y+êà¦lÓ–ê%ÍŒÇ ƒÎ”/#OVËxÏsž'kÓ¶¿Å6¿ÅVEqUgkñ|Skñ|UdLCUgkñ|Skñ|UdLCUgkñ|Skñ|UdLCUgkñ|Skñ|UdLCUgkñ|Skñ|UdLCUgkñ|Skñ|UdLCUgkñ|Skñ|Wìw4¶‹EU¾Èê껕˜Ý6Hª3|Y>¥‡&’@׸’Š 4“×ÏÕ2vlqÓáé®Sšˆè[U6ƒDÒE Ì©ó2³l‚ ìû*sÅ{äôý¯ÅñM¯Åñ\F/½6ÏWs¦Ã¢nSQG¡U#Ü[²ÆùžÖBç5¹ÆÜƒCÎnæfz›5`¸Ú(® 0‘SNɆBøÎ“Aë\CK›¿q f;™\øš­¦×âø¦×âøªÈ˜‰ª³µø¾)µø¾*²&!ª³µø¾)µø¾*²&!ª³µø¾)µø¾*²&!ª³µø¾)µø¾*²&!ª³µø¾)µø¾*²&!ª³µø¾)µø¾*²¥}’¦%|´RÓÃTÊi •(˜ðÓ¢^{ ,ü¦aªÛm~/Šm~/Šó‹.%¯¶SÖÇyšº®®ž¢ß±VlíÕ¶¦q±²@4K7—¸ ™ÉÀ‹“ãùñ=U‚Ëe¦®žÎÝd•ú¦Tt9‘²9Õhå¿"ÍüçFgÅ{]Þ×âø¦×âø¯=ĸ¦âì;„ï¶6=±ÜêQ5;˜ùiö9ªùœCDe¼Æj½£TK]ˆî“ÔÄëß=Þ _ˆ-â¾K‹# ¦¬£‚«W"’Y*Œ>8ôƒ?Á›ƒÜ2'zÁ{«º _KGj¾K4ÆhŸ=¹´ñ˜a¥Ìk+òÓkˆÐÉÃ3Ñ 8¦aªíö¿År–_ÑñýYlöR­VºÞ$µ×WÕTÒÉC%LN©Ž˜6mÑ%9€œ¢ÉÜÒ>¹¿3–ÒÍú:/«-žÊUÏ”“ôuãm|\ôºß?ÙH¿1þ«˜'êº?öL¿MÜ?; ý󽔋ó'á×ú¹‚~«£ÿdË•~›êHrêG‡Håû¿þg¯$·í6›t4’Û.mœ2-hØ%!®l1FFa™ñã0sç<ëØú‹ÆÉ:’áý1žL›³ãž´c6h£¸~‡FV6F jçÑpg•9äG1#ÂVŸòg£Ë\ë?¿Ñùg;Çž›•OÑ×/@›Ý]ïR!PénÕSÑÔÓ¶WDØŒÑ5;ò#1×9ÜësK0SàõMª¢´Y5º}gJ@Â̇eÙåÙÏ%Ú6šœ¶0€¯Ïü¾9Ï^Ÿ‡øüµÞµÎ‚T²©ÐÆgŽ’–6µÅ¥ÍœZÒGgD|ÁU’Íh––:I-T/§ŠœÓG©ØXÈNŽq–A‡Ao7Zߘ-æÏC‰XáØ¦’há’)Äs5’fcqhv‹€;Ž‹šr=‡ÙX÷±\á˜X¶œ5f"ë!†/Å»&·I½nã“3†´v±=†Ç<ÔóOf·K-4Ξ¾•…ÑJçé¹í$u®.%Ääïç[ýž‡›<=%7 ÕW¶xzJlðô8”Ü1TQ^Ùáèq)³ÃÐâSpÅQE{g‡¡Ä¦ÏC‰MÃE힇›<=%7 UW¶xzJlðô8”Ü1TV9 ‚i!’Xc‘ð?Y œÐLnÑ-Òiì8f;#²¶[<=%6xzJn­ ¶;,¢ˆIg·¼P(é˜v`W»¬ÈÍ—0Yë((+sÛ(©ªs‰ðlM~q¿D½›Çä»E¹Žc¢3æ o³ÃÐâV7lMªŽ•ÒD*$c¤dFN½Íihs€Ï2{A=!ó„Ü3ZJ Ž‚°ÖPÙ­Ôµ%¥¦hiXÇ\\FäI$øNkb­SlU1™)¤Šf¾2èäÒÌqk›˜<áÀ‚;ØY6xzJnª(¯lðô8”Ùáèq)¸b¨¢½³ÃÐâSg‡¡Ä¦áŠ¢ŠöÏC‰Mž‡›†*Š+Û<=%6xzJnª(¯lðô8”Ùáèq)¸bµ®‚T²©ÐÆgŽ’–6µÅ¥ÍœZÒGgD|Á`m²ÚÑm¾‘¢QP´hJí-'·vç7fFó¤~r·;<=%6xzJn­)µZËbi¶ÑèÂ"†äÀׇ·-Û²sZá—1h<áX¨‚ˆŒUÇ4d‚Xö‡ ÁÌÌ@?Ál¶xzJlðô8”Ü1Z˜èh£«5qÑÓ² éç+bývŽ–ü³ß Ìþ}üÁ}ÍMO4’jx¤{æµÎ`$e¤ùŽC?Ÿ%³Ùáèq)³ÃÐâSpÅk¡Š8ad0ÆÈ㡬cMh€sö¯lðô8”Ùáèq)¸b¨¢½³ÃÐâSg‡¡Ä¦áŠ¢ŠöÏC‰Mž‡›†*Š+Û<=%6xzJnª(¯lðô8”Ùáèq)¸b¨¢½³ÃÐâSg‡¡Ä¦áŠÑb+T7»%]¢¦iá§«ŒÅ1„€ãü¦æAÈ8fÓ»<‰Èƒ‘eÃxvZ©ê¥°Z¤¨¨Ò×Jê8ËäÒk˜í#–g6¹Í9ó‡ÌJßÕìT”²ÕUIð±ÒK,²h²6™s‰9Ì’•{%,µURE<,t’Ë,š, f\âN@3$¦ášÖ2‚…š­ *fêf|ñåF„¯ÒÓxݹÎÓ~gœé»>r©Ü0Þ¸ÄÈ® Udl{äc'£Ž@×½ÅÏpn.q$žÉ$•Ñìðô8”Ùáèq)¸f¨¢½³ÃÐâSg‡¡Ä¦áŠ¢ŠöÏC‰Mž‡›†*Š+Û<=%6xzJnª(¯lðô8”Ùáèq)¸b¨¢½³ÃÐâSg‡¡Ä¦áŠ¢ŠöÏC‰Mž‡›†*Š+Û<=%6xzJn®aØS ½“±ØnÌæÔg®†2%ÍÍqÒëwææ0ïìµ§°ÕfÃU”tôuxzÑQML‚h£s"œ5¤dßàº]ž‡›<=%7 Ö‚¾Åd¯£UÖkuU(™Õ©˜ö \\\ý2Ò%Î$ó’ãó•~(ÙLŠ&5‘±¡­kFA s;l6xzJlðô8”Ü1TQ^Ùáèq)³ÃÐâSpÅQE{g‡¡Ä¦ÏC‰MÃE힇›<=%7 UW¶xzJlðô8”Ü1TQ^Ùáèq)³ÃÐâSpÅQE{g‡¡Ä¦ÏC‰MÃE|ÈÆHÇ1ík˜àCšá˜#æ+a³ÃÐâSg‡¡Ä¦áŠç©pý†–ß=¾–Él‚Ž£úúxéXØåþó@ÉßÅd·Y¬ö×µöëMš A‚‘`#pâ8Áþã~`·»<=%6xzJn­Dt1ÅI tTÌŽ‹-•‰ A“ €Ë­ë\[»-ÄŽb¾k-¶êÁP*è)jLmŠq,-vµ$µ®ÌuÍÎ ÃHüës³ÃÐâSg‡¡Ä¦áŠä«0•žh©i`…´Ô2¢ZHbŽ™¡«2 #£«fZ%¹€ÌÊñiµ^i›Kw¶Q\`kÄŠªÊÀàÀŒò$gá+w³ÃÐâSg‡¡Ä¦ášæ†Ã"*H†´è‰4­Ø£Ê]¤K]nd“»-ûÖkÕ†Ç{ÕrÍšÝrÔéj¶ºVM«Ï,ôtË<†y|ÁoöxzJlðô8”Ü3Zˆè(cŠ’è©™[+@ƒ& —[Ö¸·v[‰ÅU›Øf4fk%²CBÐÊBúV£˜G˜ëÈd\Ë¡Ùáèq)³ÃÐâSpÍrîÂxUÕ³W3e5Së5Óšµ’ki;G3¤àsççβTá¬9Svzœ?išâÙ\”qº`æå¢í24³ Ž{² ¤Ùáèq)³ÃÐâSpÍhí¶kE²yç¶Ú¨h¥¨:S>ž‘ºCó¸´ ÿŠÕYÿGEõe³ÙJ»ž‡¸ÛGèØ¾­¶{)W>^Rºñœ|WþwAûç{)æOïõsýWGþÉ—éªïÎè?|ïe"üËøuþ®`ŸªèÿÙ2å_©º‰þÉpÿ÷&öÏ^3‡)(ª­­š¦‚’wèӀ驘ò²@rÍæÌ“üIßžgÙzŠçÿ„V }\Ùyç¯>nÆTÑQRÒ[%Ž&1¦ARæ™ XÖi"ݘcwf´­ßÏÇÃÛo•çÓ'ÌðòóõÉã:Örm«è‹g¡Åî®ë¨Ä0Ás½²–`ƒQKœpÆÝ=)ó9–yhoð™rß"qÖ˜Ûý-ÿ w½K0ýâÆË„·qLÙ*Þ×BâàÀÖ†™žÉæç%lù¿#×}7ÇÇ˽eøžŸdöËå9Æ>¨×{­"´ÐPÜn´tõ³ËɶæÕÌçÆúaZXòãC.»~[œ5ñŽèÅñ×›U)•¢¢k]¨UKö*GØÝT™Æf}VoÑvE¹ŽÇ¦:žUGTè"5±Ñ²RÁ¦Ö¸´¹ ó€KHìè˜,«â>³Ë0Ä×¼7ªjŒK]†¬Ü¢’Ò6i_XèäˆÅLšot~v‰Ý“›U»-~<¸Ky®Î¾KGQ-º†¦‰‘2¦cU\Ș÷id#e)É®{NN:^ˆ ˆ€ˆˆˆ€ˆˆˆ€ˆˆ9ûȽMŒ¬ÔÔu•T–½–¦zÇEÙÉ)õq¹îiÑÒ—pÈ–ðã­©.¬µí±ÔìÕí¡Ÿú#£li}C²næL3sæÄ[‘rõAçUøÞZZ¨ã’¾:ª’: …½¥Ï§}c,²´´†¼DO[“@foÈÄ}Ò\IIAY q¶X ¥h¤®Š]ueDà4úq chs´³!࿬6ôhƒ‡´Í‹ë¯–xkê«(#¬šäÈiaÖG-0ŠÈæØæ:Lœ“†Ÿä‘“3â üm¶ÒÛia¹dž’SW„32÷=ñÄ[ ?’ÎgÄäK™Ø¢ 4NÒ¢ÚɤÎ69™ ÷nçsrœd2ù‚̈€ˆˆˆ€ˆˆˆ€ˆˆ4}P+ê­X\èfÔÕÒ[*g§“D;FFÄâÓ‘àn!yþ*Ä}PYˆ.í²Û¯ ¦d5±E©5­c㥕ÐËÙ€:rF̳šMï -€=VºŽ’¾™ÔÕÔ°U@ç5Æ)£i-psNGv`€GÌ@+:=‰˜Þ¢¦ŽG^îTñÖÞ«¨åˆ[áÊ’‘Ž©0Ê 0HŠ&‡?6‘#wA:íïªe!¦ŽÔˆáÚ#. •ƦHꦆXᥙÚ.‰‘?­Õ¦K]–Y{ """ """ """ ""+“ã*‹%ºÛx ¯‚ÈþS¨’É+Úk„´ëbv‘!Õ5ÌÉÚY80Bé~ê“ ªJXnQFêG=ÒÕR>Y$œM Ñ•Q9Ú½hÐÙ;yÒÏ/cDmyªÆtøb®àÛµÎ:¹o5CNË^›™Mõ Œ3WO+›¦ÁÖ=i¹d]¤{¼?%DÖ|µŒ©eKéctÍ©ÐÖµå£H?@égžz óȯ"" """ """ ""¡ˆ Ú°ýÆ”š±®¤–<é ØGâÉÿ¯æð䯢+ÂáÌFë>© Ž(M5%¢K|³=ÅÍ{µ5úN0ó Úyå¢íŽ¿ K‹ë1¦éˆªq40:¦†Ån6V¼ÑJÍi4ŒsXó¬kŸ¡n¤ ÚC‹½‘yæ& ÄñõE¯½aÚWK7#ÑÑ·Ztap|µzNÌäèÝ©yú$½ÁiÚ1&À¶ûŠŽôÉùVäRÈÉ!‚®mX“:iÎR5áÁÚ 7=0ÿ[D}e½bª‰l¶Ê÷> ë½5-ÅÄÓµ®£¬aª…Í#w_¢Ð\4¿z ·V[q¯|M£µIUpÔe µKQ!:[› C]¡I ßœisz9/@‚Ûn§¸ÔÜ` ¥Š¶¨4TT2¶YƒFMÓp» 2ó+H8L4û]UKóh,µÔL¯¥¦cçe–x`žª9jÝ3Ý.¬0’^NOÌd\©bhí•}R© ~åOUM<òÞÛk©—LØ"•Œ-ká!.k.\[éƒË°@¶I‰jn6ë-ï ÆÊvÅÈÕ,’¬ãÎYß$EŽ' Üó/lo‘ÅÍÞYóGn¹Å‹=ºÏ31.7jkê`‘”õ®dÛ,n˜Ð]L4[™n­Ç!Ùõ4Aç]Km·Û5]æ–ùÔïžöÙ„°Jú–ÖHêõ…Ît Ñ‹IºAÃDi^àУÔÑ» ã;Eƒ](éênPUÒS6ÅQK  ѲBèÚ4™)tC'HÐð?+5ì(ƒ€êuH(0Uõµ–ʨèÝS<Œ‚š‚ZMl:–¨¦?Œˆ3$»7Ê =§u¶/«mžÊUÜ.Õú6!ÿ–[=”¨>+Nut?¾w²‘~fü:ÿW0OÕtì™~˜¬üî‡÷ÎöR/Ìÿ‡_êæ ú®ý“*ÓHd¸sû“ûg.,g}–ŸÊwÈø£’Mš•Žcö5ú#*We¹Ãq$äFóžk¼êEû$ßܟÛ9yF:f>A£¦ pÌeŸôJ}üÃÿÎÉç;>/§ÇÝåŸ+ü¿—õeù>ÛêñÔu”ýRïPÀÆÔáiª\Æd‘6¥Ž“.rÖ2üÅÙx{+¾±ßm—£3h%™Ï€4ÈÉ©ä…ÍÏDè½ ät]¼nÜ~eåèúauJÿu¼L9„4Ñfò-~ÿ‰áêõß)oþÿfoOÉóöyÏ#¾T¯7(-TqÕT2G1õ0SéM3"iÞFàç‚|óó.cªv¬ÄÂݱÒÛ¦’˜ÈC«¤‰šZ;Ì.†FËÍó±Ã°á¤r¡Wªêª.QÏo²Í¶\bªuÍÒ¼UÉ­ŠsNá«ÜÐÆ·'åÖ3pÌ‘óºÜô4^osêP*gmºÇUjsIKjª{㧉²ANÝ` cƒ^Ù"™À–R’ O7Õ>»Ãy¡¨–¢‚²HDóušGŠÈ›q¶H˜Ý %,q=pþµÙƒ¹;N;› Ê ÍŽ‚ïJÉu4u16@Ã^ÐàŒò;ò%]ZœmžÍ„,ÖŠ§Æùèh ¦•Ñ’X\ÈÚÒA ³³m•D@DDD@DD¯®¼Û(®öûMU[#®¸™$9é4^îa¸ƒ¼åós­‚ãï86¢¯Ûq--î²3 c%¨ÂÁ)åŒ22b/ºC˜.Ë'ÈFNÑ!FêóˆìÖz¸in5f)ehp¼1¥Ú!ÏsA·3–“ˆöU™®PE|¥´9’êi¦©c€±:&¸žyç3rÝØ<Û³åñF¾ÝŸXè96!y´Çm¸µó¼ìÁ¦B] üféž:àÎfÜË&?·Au¶ÖÑÔÓ6*:Zˆæ¦œ¸GW¬’Â)4Fz²"vdvtw8i4ϱÒY®P]h䪧dc*g¦"@Ò†gÄã¸ÅÌ$x2ææWWžaLq¶b8.2ÒY©umSê)%{¦š9Ÿ)e;cFƒuw?;@o'ÐÒDEA†ñ‡â¹âë=ÞªiéíÔÕ k%¹³>JwFöŒ²ëuNßΉ8ä­]N®TUvʇ\iߨ®nÓnÑu.k©cnïÊa‡~C9$Þw(=-ŸÏ«j)ªiçu °ÕURͪ/~,QÖ2gÁïÈ,ièõß3tC7÷<=3èjéh«]²ILÚzkl‘ÄÊJ\ƒC^Í\b@[£˜ygÍ£¸´:\U¯º+Õ¦®è). ¶²¬ëåÍÒÏ4’Ó¾9ÜËLœ Ï C @ç¿ak¥}º Fßêj&d’Hkªƒ3ZY£©F5n?”Nü¹·è–::äXh£ÕQæ(4#kuQØÌ‡ä·pÜ9†áýfT:© 4³G•c › e¡ò3 D73Ì3 |ä%T†šY£‚J‡±…͆2Ðù†"™æ>r‹ªLsOÔþÿKMO=EEU¾jhb†2÷:IXÑÙ¸fy€Ìœ€%qXŸ©ö'¼_®·ÕÚb5‘VS‡´27K ´ÒÅdуXt ¢ÏJW‚H€´z²..§”Ž©¤©ª¢·K1¼ÖÕW¼æME,Ψ|pÝp’XzÜØã¿³£½õ3½TqKWNè)¶ˆé©ÛRØE+U,‘:'¾šbÇß:ÀÂ5`’vœzÒ"*ˆ€ˆˆˆ€ˆˆˆ€ˆˆ¼ÂLˆª,¶ûUt6‰é­¶gZáluÒFêÆÒ¹²<˜\4iÎlÑx'qÌ8åJçÔÒýQGlk*-ÀÒÒºž™ðÒ²úç¼K¶I^CÚ ˜È‰-ϳ«Ç®"ó«¿S÷O†ê©!·Ûg¸UÞ*+¥‘óêÚæ:ißž”2‰4[(·0·2â`ÜX©e¡±ÐQO³‰i飉ûíüã;+ƒºÛ«ï×ëôöø$ƒTÛ[)ß[ Ç4Ô•sNæ‚[™a̓M ƒžc<–*\ SrÅU|UGhª¦¨uKÍ#ùZÓ$T1³ò˜Ðì¶Y7ä9Ú@Ïò]8ô©Ãx†Ùˆi¶«^Úø4#%ž‚zvHׂZæÐð@Ï6çÎ>p¾ð­e· Úm× ›=e-0ÔJ×ÈÖ¹À Ì‚s#5¡Âj¦Ùˆ$¹r=ŽÃLi]¢´Êç²w—4‰ø¸À- dÒzó™æ6¯Åv6Ü% 5&¢=` mκ0KÙƒ2‘à5Ýc vã¹_²](¯6æ\-ï‘ð9òGøÈ_Ø÷1í,xikeÍŠ _.,šç]Geª¤¦2ò[ÆXõ9±Á®{59ž‰v‘ k¢ý,Ø>Ù|¢§l›]”è\ªkb–×ÌèµòO!-…¹9ºÑâ3kžs’çE–c\4æÎã_#1‰K35Œ.k£ÒhÖ‚ç5 ³K2æŽÈ[*+Õ¶²×5Ê)ÜÚhŒÆhŸâѸ=Í oÈ€r öW!‡\9F|Kh°ÜjgWN#»Ïq0JÇ3*}(ÿ$<È œ^Æs ´v8G ×XઙÑPIpªtóëäžY;ÜØXȃÞ4äa´½ÅÍ$°n9õ¯µZv7í`Îzýi™°Šql©5ÎcÞÜ¢Õé[„;G.°ïܶõ7J:k@ºÔ:h©‹þ¾‰:ìƒF¯-="H9ifrË5ÄRàÛŒp\]a°Ýö©a,£¹Üæ«lZ 5óBù ëÀ ÑÉ£Hƒ×÷Öûf!³`>MµËošñ~£i|‚š=) ƒ3ȼ±v‹wfCFyg¹öŒÆ¸m”M«ulú&WÂbs˜æ49ÁÑhi·E¤K@‚yÂÃkýÕ–ße"ÔÐaÌQFinÓÚ Þ1SF¾á$¬©ˆI™Ï´‡ƒ @`fŽˆÈ»-µ¯ó:hóëäµP9ì¸2'ieóå¦ßó\y»ñc¬9UÐxgw²‘~iü:ÿW0OÕtì™~•ªë«è˜?)²:BÞΈã?ópÅ~jü:ÿW0OÕtì™péú{©O—© þ.pwøç­½Iòk"¥½Ü!‚6†GPäÖèfr Îgvòº^¢²\?ýɽ³×P2ZÊHß¾Ó&ŒP‰$ª‰šr=ÐÇ!q&ž˜ÞNdçÏÎtükî×=_«Ãß=Yï³ôwŸøMQŸëùCî.£`ÿ“l©ÕÔÔUIRàù_3›™ 9€pp^mK}ÇÔG .!ˆG CbŽiY#rÁÄÒé¸|ývgçÏzõ,ŠfÄ´Õ6øi&¥Ž)s†¤ÌǶC ËA7n˲7¯ñxï³ôŸÑãéþ\ðý[žn‡›<Ý!kn¸¦šÝŒ­¸nZ:é_K,ížI¤c d‰i,ahXKœ\4[¥–›JÅòï ˆ*'7'¶($/u$ÀJÒöÆ,üpÓs[œz[ÜÑÙ ëf#o³ÍÐâg›¡Ä-%—ÛîØ²k =Þ2ÊXgdÓÚê¡/3¬‰¢0C'8€òâѽ¥u)ºb(ìót8„Ùæèq ò&鈣³ÍÐâg›¡Ä+È›¦"ŽÏ7CˆMžn‡¯"n˜Š;<Ý!6yºB¼‰ºb(ìót8„Ùæèq ò&鈣³ÍÐâg›¡Ä+Ë›ºâ¨èñ•§ ÃDú“[+£¨¨É”®ÔK+wuÎp…Û·d2'œÝ1mžn‡›<Ý!hñn-žÉ[W=¨VAm¡mÂå)©Õ˜`sžctN±ÙE!È–Œ›Ï™gÄxš $·Ñ×Ë 45Uyc!’9i˜Æ072âí{ºÐ $7/ tÄmvyºBlót8…¡ÁøÖ‚ó;è*d0ÜMuma-ÎItZN‹GeÇ,‡„¦éˆÇ³ÍÐâg›¡Ä-Æp\íÓW\ºš&TANE\jƶgˆÙÀc\Çé9€‚ܺàsÈ­Ý1ž¶Ü%·ÔÖNjâsÚø`¢šgæÆC#² aÏ&ÔDI‡Ú웦#g³ÍÐâg›¡Ä*µWúvI‡ÝHÖUÓÞêuQL×äÍšiÃÆî¸nü¬ûõxÏ ÒÛ­÷îZ·VÕÒ¿Q!ÖDçÂÆ» ÜÆn¨„d@=6ã“tÄl¶yºBlót8…ób¼Ûït²T[¥•íŠS ­–Ã$o×2@Ó“Þ9ˆ=•S–+ãÆÑØj-ôͤ©¢šªš©•NtŽÕ:½¯ŒÆwϸ‡»0Ýùg¹ºb.ìót8„Ùæèq •âñ_oÄ–jO¦–ßsÔÛNÔá,rˆf›ú­^Eº0垘9»›vý /TÉ‹*,’[c †ZˆÞØ*]5lm…®vµôÍHFý Cœ]¦Ì‡\›¦#«Ùæèq ³ÍÐâ.X¯Ga¨·Ó6’¦Šjªj¦U9Ò;Tèö¾3 ß>âìÃwåžêVìMp7ói¼YßNÚˆÍ-I«sé[DÍk¬’üóÍÉ:Y4”Ý1­žn‡›<Ý!iN,’|e>¶ÛEC£ ¨•rÏ«…õ:&˜3 qÝ®f“À:<Ùð½Úïq«¹S]-”fŠFDI\ú–=åºNi.Š2 Ag0#®ËœÝ1öyºBåéé)êìV¶TD1iÏ"Ó©fðFñü·ÜíµSR\ðìqT#––8.R]$ñÁsu€Dç>A‘c&¿"tV{_è[gÕôÞÅŠ_.¬œU³RSÃh£¨c 𢆠%‘Î.sÜèšâI;ùÉ?Å~cü:ÿW0OÕtì™~£¶þ€¶}[MìX¿.~«˜'êº?öL ýKÔTÔŽÀNà1>yëÇ0ãÛKd§2d ÑA#{–çÌ;-wùsžuëýFåÕõ$ÃÀ·KI“ö|sÕ)ðZˆð5 2ÈÓ3[HÀ÷5å®9=áß”× Èß’Õñ=ÓÑç|¼£7Éõ_oŒñ•çf¾%Ýõ’9믳5í%»<»!ïÿÝþk7É3žøvÏ*‹â.‡ Ek·C=5¶ÊÛa†M `ƒ"Z×È%§0öDz´üŸ›ãîõãÂ<~?Åòõyëʯ]lŒ®½[®ì¯¬£©¡l‘F¬¶h¤tn|ocºÒbföè¸e¸…ÌZº–X-,­ŽÕ<”qÕ@iôYo¡q…íyn§/xë2ÊBñ‘Ïœ;-¯ÅñM¯Åñ_7»Q¡ÃX*‹VRÔ[.wÛ 0¦–ª1Ô0>Y¤5}n‹§`!–@Ô*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Òçî¸3 \± þ¦ÏCÊtuMª"š=d®ln¡ï-.pnpß¹Ìaÿ¥m¶¿Å6¿Å1MFŽõƒi.ïcë.—='Ò2ޏ±Ñ·”!i'F`̹ÿÕè~[†àrY±F¶â Æ®Z¸jíñJÊ9©ä t‘Ñ“#NG® `‡<àVÛkñ|Skñ|SÔhlØ*’Ûs†´Ý®µ§«©­†ž¡ñ꣞s!’@Æœò•à òÇv{×Pªí~/Šm~/Šbš‹Hªí~/Šm~/Šbš‹Hªí~/Šm~/Šbš‹Hªí~/Šm~/Šbš‹Hªí~/Šm~/Šbš‹Hªí~/Šm~/ŠbšŒs[)å¿R^\ùE-,ôÌh#@¶WDç2Ï0an[û'Ÿv\ýSÌ=I5Њ¡- Æk„o2 Þù9ØíÛØ2!¸S7œ·ô›_‹â›_‹â˜¦£F0]°kHª¯Ò¨ª§¬ªq‘¤ÔO ì™’;­Ë<Øtr7v‹to×aË=MEmc(`¤¸VŨ©¯¦‰Œ©’>´rÒȆ´sæ2d@"î×âø¦×âø¦)¨ÖZ0½º×_GSFú†2†ž¢š–œ¹º¸b™ñ=ÌhË<…º#=À‘Í¢œ†ä·²ÛMm‚ß@ÙLæ–†6ÓÆé²²À›r§=ÄÎÖå¹Úü_Úü_Å5àc£†8Ý+åsZd~ZO sœ€Ÿ}ª»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢Ò*»_‹â›_‹â˜¦¢†9¶V^°uÞÍ@`l÷ I)¦ykÙƒ¸Hk‰-ä˜Ï1ÌÜú“a»ÖºåQS^'¬5ZNk`h¨†X¤h“U¬-ÊRZ×9Á¥­Èd2]®×âø¦×âø¦)¨ÖÓaŠ6 jNÃt©¹Å››¾YõúaÛ¿$m/È ŽææNG>~óÔ¯ \êT®”T‡Îã,”´µ$‰j$œ·)á‘ Ìü²ä@$ä»-¯ÅñM¯ÅñLSQi]¯ÅñM¯ÅñLSQi]¯ÅñM¯ÅñLSQi]¯ÅñM¯ÅñLSQi]¯ÅñM¯ÅñLSQi]¯ÅñM¯ÅñLSQi]¯ÅñM¯ÅñLSQi]¯ÅñM¯ÅñLSQÊCÔê†*xéÙ}¼:((o¥d¢šVSÓ¹ñ8ÆÖ¾×RÖæðã‘;ÉÑ"gRL1SOCtÂZ8 ;f’’’ º3#äÑÑš±€:GdÖ€ÀÛí~/Šm~/ŠbšÏÐWaÉ,F²¦*Ykg¬˜iæ/tÒÉ+˜DÑ=º:R²noçÏ{j¢ŠÛk¤·@é ,,†7Hòç–µ¡ ¸žsÞTm~/Šm~/Šbš‹Hªí~/Šm~/Šbš‹Hªí~/Šm~/Šbš‹Hªí~/Šm~/Šbš‹Hªí~/Šm~/Šbš‹Hªí~/Šm~/Šbš‹Hªí~/Šm~/Šbš‹K\"¢–Zs$±‰XæÄò×·1–m#˜üÅbÚü_Úü_Å5ùÁ4’ÓÜYx»VU×bêÉ_e‹gÉ€dm`Ñy.ÞÓŸg5šÃƒèí7Ù/|£r®®—]¬’©ñ3+)šã“XÐ7RG›·e­¯ÅñM¯ÅñLSQÏA†ª©*°¥ºŒÂ,¸v6º9e˜º¦W¶šZv´´04 4‹³ÞFZ oU©zœÛ!}¿Nïyžk#ŠŠe‹B£¨§±Œ£€êhÛ™%Ú9Œùˆê¶¿Å6¿Å1MF;m² ËT/•ϹU6ª`òk„1Ór†ŒM;óÞOö mn–§Sâˆî𾙎Š:XÙM¨<Äé×B_“Ì,$éi ú%¹­¶×âø¦×âø¦)¨Ôâl5-îåA\ÜGw¶šëiã¤e1`”²HËζ’K%srÏG˜åžõV<IÕ×.÷Xˆž¢ªž˜K)§¯JÌã.'ñŽ!¯.h'òwº ¯ÅñM¯ÅñLSQ©­ÃRÔâÊ|B1ÞÓ1ÑGK)µ'˜#:èKòy…„-!¿D·5W àרf©’ S|ª52‰§Ú›JçHý6¸—<@skK7Íq Ñ!¤t_‹â›_‹â˜¦£S ÃPâÈqE5ž†žç'i–hØdtÅ…Ò8†éõ¤gÍ$ƒ~’ÙXíZ-Í¢‚Ieo‘òÊA|{‹Üç̹Äî,›_‹â›_‹â˜¦£–·u>¦¥¤¯¥Ÿßkã®xšSPiõ‚v½¯dÂFB×—°±º9’ÐYdXÙ[GnÖʚùõlÃOV÷€u,çÑ/âºÝ¯Åñ\ÅöÆYéíwZº*yà¦e5DR¶=ìhaÜò3,ó:rÅïQAq·Ýhß5¶vM Eœp:&7&îkAkFàáÍ»›0¿.~Ÿ«˜'êº?öL¿PCSa·[[En¬¡‘¬nŒpST2G¸øI$Ÿ~eü>i¥£´a 9ÆŒ°[écxùœÖΠý-ôq`« ‹nˆ:‚ bºUFÝ'09Ä5²†ŒÉ'pëu²SöÛ§®k~2Ö`÷¶< e‘ÜͶS“æšµS_ñ4®Ðáç¾ã8Ý&›K›Ø;˜FGûW§«Óçí¼ñÿ¾ÛŸ?? öê68;mÓ×5¿6H;mÓ×5¿rf÷Œ»mžT¿ G-ã>ö™åËð׿ð>ÿôŸó?»Ïø_þ—û:Ý’ÛtõÍoÆM’ÛtõÍoÆ\Ÿ-ã.ö™åËðÔrÞ2ïiž\¿ ?÷ÿ¤ÿ™ýÏâ=ú_ìë $¶éëšßŒ›,¶éëšßŒ¹>[Æ]í3Ë—á§-ã.ö™åËðÓøúOùŸÜþ'×ÿ¥þγeƒ¶Ý=s[ñ“eƒ¶Ý=s[ñ—'ËX˽¶yrü5µŒûÛg—/ÃSøíÿ3ûŸÄzÿô¿ÙÖì°vÛ§®+~2l°vÛ§®k~2åzÆÀ; ¿ít„ŸþÚèìÕÆ¾ˆLød‚@Kdã'1ÀäAþ+ËÛñýž©ß/û—þø{||ÿF}–ÛtõÍoÆM–ÛsõÍoÆYQxõÛËm¹úæ·ã&Ím¹úâ·ã,¥Fh¬{,=¶çëšßŒ£f‡¶Üýq[ñ–TA‹fƒ¶Üýs[ñ“f‡¶Üýq[ñ–TA‹f‡¶Üýs[ñ“f‡¶Üýq[ñ–U=šÛsõÅoÆMšÛsõÅoÆY?µbÙáí·?\ÖüdÙáí·?\Vüe—û dzÃÛn~¸­øÉ³ÃÛn~¸­øË" dzÃÛ.~¹­øÉ³ÃÛn~¸­øË Qš#ãg‡¶Üýq[ñ“g‡¶Üýq[ñ—ÞhŠøÙáí—?\ÖüdÙáí·?\Vüeöˆ1ê!í·?\VüdÔCÛn~¸­øË" Ǩ‡¶\ýs[ñ“Ql¹úæ·ã,Š£ãQl¹úâ·ã&¢ÙsõÅoÆ_hƒãQl¹úâ·ã&¢.ÙsõÅoÆ_išD=¶çëŠßŒšˆ»eÏ׿} Añ¨‹¶\ýq[ñ“Qm¹úâ·ã/´Íñ¨‡¶Üýq[ñ“Ql¹úâ·ã/µ¯D]²çëŠßŒšˆ»eÏ׿}f¥Æ¢.ÙsõÅoÆMD]²çëŠßŒ¾óOD=¶çëŠßŒšˆ{mÏ׿}f™¢>uvËŸ®+~2j!í·?\Vüe÷šfŠøÔCÛn~¸­øÉ¨‡¶Üýq[ñ—ÚxP|j"í—?\VüdÔCÛ.~¸­øËï4Añ¨‡¶\ýq[ñ“Qm¹úâ·ã/´Añ¨‡¶Üýq[ñ“Qm¹úâ·ã/´Añ¨‡¶\ýs[ñ“Ql¹úæ·ã/´N£ãQl¹úâ·ã&¢ÛsõÅoÆYQm¹úâ·ã)Ùáí—?\Öüeöˆ>6x{mÏ׿6x{mÏ׿}æˆ>6x{mÏ׿6x{mÏ׿}ïRƒÏm¹úæ·ã&Ïm¹úâ·ã,ˆƒÏm¹úâ·ã&Ïm¹úâ·ã,ˆƒÏm¹úæ·ã)Ùáí·?\Vüe‘£Ím¹úâ·ã&Ím¹úâ·ã,ž(1lÐöÛŸ®+~2lÐvÛŸ®k~2È¥-šÛsõÅoÆS³CÛn~¹­øË"fƒËm¹úæ·ã&Ëm¹úæ·ã,Šw ŲÁÛnž¹­øÉ²ÁÛnž¹­øË*"1l°vÛ§®k~2–ÛtõÍoÆY¢±l°vÛ§®k~2l°vÛ§®k~2Ê™ Å²AÛnž¹­øÉ²AÛnž¹­øË2"1lvÛ§®k~2lvÛ§®k~2ʤ¢°ìvÛ§®k~2lvÛ§®k~2̈0ìvÛ§®k~2lpvÛ§®k~2Ì¥ŽÛtõÍoÆ_˜Ú:h0Ψ‰³™¤¬‘’j¹§qkY›Fr=Ä\íÃ.r¿Rs/Ì_ñýQÂßãæöa¾á¿Ùå§ê¨=“VöÔò,öìÏ=üãjÑa¿Ùí§ê¨=“VÞÞì­Váÿìiý“UEíg…5ž_M4ü*£’Æøÿäæ%£±6оª˜Ô «…~ÉO¹Ú:°ýfþÎ[·e¿zņº¥CtÄw{%mžºŠklP½ÎŠªÜèŒoYC"aÌëwg˜ö0ÂñâXå‚¢ór¥¥ž DôИŒr73¿'±Å®ë²Òipù• §ö»uueE¾år¥ŠºŠ:*ºxäf„±Ç ‰‡H´½®kNâ× àgŸ2ŠØÔuGÂTÔõ³ÕÜç¤Q2jˆêhgŠF±ï k„o`s›¤@ÌΨQõM¶Uã¸0Üô ªe[èªtŒº¦7Df[õ¤èbW<΢xa°VAÊW]º6Ѽ·PÂÙY(=lCIÚQ·7;2Fyöëë°µ4øºAq­£¸SÒŠ7juf9¡ÓÐs^Ó'²ÒÌB¯YáMg…WÓ𦚨±¬?:Ô[wV]ì\'ýÇ+új…»óÛ·Ö5ÑÊU‹Ê "Š"((%B&h¡J„DE%"(ŠJ(DDÍB LÔ" %3Pˆ š""„ˆˆ¡3A(¡J(ÍJ(DŠ4Š3LÐJ(ì A(¡;(%5*æŠ;‚QFhƒé&h%Bfˆ%)A(¡7 ”P™ •*3Dš(DŠ ”QØDB”š(D§5‚T¨LÐJ(R€§5‚Q /ÌñýQÂßãæöa~œ_˜ÿâú£…¿ÇÍì‚7`;8™á‘ò\n',†©¹œÊ·ÂÑÂÛ¥&Œ12&çPÌÈkCF{ùò Ygý˜Q}I°  ©mïh“K½§LæH@ûæUùJÓô¥'¤3íNR´ý)Gé ûUŒ¯]òËè§â¦W®ù&ôSñQU¹JÓôŸgÚœ¥iúNϳíVr½wÉ7¢ŸŠ™^{ä›ÑOÅA[”­?IÑùö}©ÊVŸ¤èüû~Õc+Ï|“z)ø©ÿ9ï’oE?~Sµ}'IçÛö¨å;WÒtž}¿j³ÿ9ï’oE??ç=òMè§â ­Êv¯¤é<û~Õñ }¢)j$mΚ‰Ÿ3ó›œç·ófUÏùÏ|“z)ø©ÿ9ï’oE?Aƒ•­IQùöý©Ê¶¼¿IQùöý«?üç¾I½üTÎóß$ÞŠ~* ü«kÏô•ŸoÚœ«kúJÏ·íYó¼÷É7¢ŸŠ™Þ{ä›ÑOÅAƒ•mIQùöý©Ê¶¿¤¨üû~ÕŸ;Ï|“z)ø©ç¾I½üT9V×ô•ŸoÚœ«kúJÏ·íYó¼÷É7¢ŸŠšWžù&ôSñPWåkgÒT~}¿jr­¯é*?>ßµXÒ¼÷É7¢ŸŠšWŽù&ôSñPWå[_ÒT~}¿jŽUµý%GçÛö«:WŽù&ôSñTi^;äŸÑÅA_•mIQùöý©Ê¶¿¤hüû~ÕcJñß$ÞŠ~*i^;äŸÑOÅDWå[^¤hüû~Ôå[_ÒT~}¿j±¥xï’D?4¯òOè‡â¢«ò­³é?>ßµ9VÙ—é*?>ßµXÒ¼wÉ?¢ŠšWŽù'ôCñPVå[gÒ4~}¿jr­³é?>ßµYÓ¼wÉ7¢ŸŠšwŽù'ôCñPVåKgÒ4~}¿jr¥³é?>ßµYÓ¼wÉ?¢ŠšwŽù'ôCñPVå[gÒ4~}¿jr­³é?<ßµYÓ¼wÉ?¢ŠšwŽù'ôSñPVå[gÒ4~y¿jŽU¶}#GçÛö«ZwŽù'ôCñSNñß$þˆ~* ¼©lúFÏ·íAu¶}#GçÛö«ZwŽù'ôCñSNñß$þˆ~* ¼©lúFÏ·íNT¶}#GçÛö«ZwŽù'ôCñTk/òOè‡â ­Ê–Ϥhüû~ÔåKgÒ4~}¿j³¬¼wÉ?¢ŠšËÇ|sú!ø©ÄVåKgÒ4~y¿jr¥³é?>ßµYÖ^;äŸÑÅMeã¾IýüTUnT¶}#GçÛö§*[>‘£óíûUeã¾IýüTÖ^;äŸÑÅA[•-ŸHÑùöý©Ê–Ϥhüó~ÕgYxï’D?5—Žù'ôCñPVåKgÒ4~}¿jr¥³é?>ßµYÖ^;äŸÑÅMeã¾IýüT¹RÙôŸoÚœ©lúFÏ·íVu—ŽøçôCñSYxï’D?[•-ŸHÑùöý©Ê–Ϥhüó~ÕgYxï’D?5—Žù'ôCñQU¹RÙôŸoÚœ©lúFÏ·íVu—Žù'ôCñSYxï’D?nT¶}#GçÛö§*[>‘£óÍûUeã¾9ýüTÖ^;äŸÑÅAX]-ŸHÑùöý©Ê–Ϥhüû~ÕkYxï’D?4ïòOè‡â «Ê–Ϥhüû~ÔåKgÒ4~}¿jµ§xï’D?4ïòOè‡â «Ê–Ì¿HÑùöýªyVÙôŸoÚ¬éÞ;äŸÑÅM;Ç|“ú!ø¨+r­³é?<ßµ9RÙô•ŸoÚ¬éÞ;äŸÑÅM;Ç|“ú!ø¨+r¥³é?>ßµ9VÙôŸoÚ¬éÞ;äŸÑÅM;Ç|“ú!ø¨+r­³é?>ßµ9VÙôŸoÚ¬éÞ;äŸÑÅM;Ç|“ú!ø¨+r¥³é?>ßµHºÛ>‘£óíûU;Ç|“z)ø©§xï’E?~U¶}#GçÛö§*Úþ’£óíûU+Ç|“ú!ø©¥xï’D?~Uµý%GçÛö§*Úþ’£óíûU+Ç|“ú)ø©¥xï’oE?~Uµý%GçÛö©å[gÒT~}¿j±¥xï’oE?4¯òMè§â¢+›­³é*?>ßµ9V×ô•ŸoÚ¬i^;ä›ÑOÅM+Ï|“ú!ø¨ªü­kúFÏ·íSʶ¿¤¨üû~ÕŸJóß$ÞŠ~*gyï’oE?Uµý%GçÛö§*Úþ’£óíûV|ï=òMè§â¦wžù&ôSñP`åk_ÒT~}¿j‘uµåúJÏ·íY³¼÷É7¢ŸŠ™Þ{ä›ÑOÅA„]­IQùöý©ÊÖ¿¤¨üû~Õ›;Ï|“z)ø©ç¾I½üTyZ×ô•ŸoÚœ­kúJÏ·íYÿç=òMè§â§üç¾I½üTyZ×ô•ŸoÚœ­júJ‹Ï·íY¿ç=òMè§â§üç¾I½üTyZ×ô•ŸoÚ§•­_IÑyöý«/üç¾I½üTÊóß$ÞŠ~*} \¯kúN‹Ï·íNVµv.T^}¿jË•ç¾I½üU9^»ä›ÑOÅA‹•­IÑyöý©ÊÖ¯¤è¼û~Õ—+×|“z)ø©•ë¾I½üT¹ZÕôŸoÚœ­júN‹Ï·íYr½wÉ7¢ŸŠš7®ùfôSñPD|òˆ ®¥–Gs1’µÄÿWæßøþ¨áoñó{0¿CVíâëkwWÖ´ÌýºÍª~üôÝü—çŸø~¨aoñó{0ƒÝí³/©#öu‡ú\ß¼wó\µ£öcEõ$~À.¢³ó¹¿xïæƒ"¡ "(A*? „¡Fh¡J"„A²…E‚­UD¢:ZX„µ@qÉ­hçq=€Åxí–¿;/Ã_0œ±þ®wµQ¼âË ¢¼ÐVÖ¿jkŸ4òNèÙÒx®Ðo…Ù Mã¶Z¼ô¿ F¦óÛ->z_†®ƒ˜ÌÍ-Mç¶Z|ô¿ 57žÙióÒü5ñ_z¶ÐÞ-ÖŠª­]mË[²E ã¬Õ´9ûÀÈdç#>Ækaš Z›Ïl´ùé~jo=²Óç¥øk[|Æ8~Ëuä»d̫ٶ·GÒèä[¦âÆÖæÌ‘—ñ[Š Êjú(khª#¨¦™ñK³kšyˆ(Œ:›Ïl´ùé~jo=²Óç¥øKâ†õm­¼ÜlôÕZuÖÝV׃†¯XÝ&o##˜ß¸œ»+aš Z‹Ïl´ùé~j.ý²ÕçeøkM<ÂϸKCÊnd‘V:…ï’–VDÙÚr1ë\ÐÍ,û[÷-­–ì.†¸ ú3GVúcµÃ«×håøÈ÷(Î{ÙȠɨ»öÛW—á©Ô]ûm¯ÎËðÕ¼ü*St7€Ò[%¬Ÿ›['ÃK}Sê ÑO¦x#3ÌoÞ=UÌÕ*m÷KìèAÿ¸Šû­¨|:¸áˆÍ4®Ðƒþ¢¾L7~Ùk² Ònÿ(×Ì¿§-Û?±z×b g‡l7#n¹ÖÍKiv·²:I¥ ‡H·XâÆÖæ$‘—ñA³ÔÝûe«ÎËðÓSxí–Ÿ=/ÃYèê`¬¤†®–VÍìl‘HÓ˜{HÌà ¬¹¢)ê/²Óç¥øI©¼vËOž—á+™­~½[qž ½¢§i¢ŸKW.ƒ™ž‹‹NçFðy š›Çl´ùé~j/²Óç¥øJæhIÍ=Eã¶Z|ô¿ 5~̶¯=/ÃW3LóAOQwí¶¯;/ÃS¨»vÛ_—á«y¡Í)"¼5ºMu²B?éÈ ÿ6d¾­õ[]>°ÆèÞ×HÇsµÃœùó«@ªVßþxüõ®öq ú©žaz_„š‹Çl´ùé~¹™\Ôxó >ã-)¹’EXê¾JYY¨iÈÇ­sC4³ìioÝó Üêo¶Óç¥øI¨¼vËOž—á,v[¨¹º¹¢‚¾ŒÑÕ¾˜ípêõÚ9~2=çJ3žçvr+aš bÇfKOž—ᦦïÛm^z_†®f™ ¨!»vÛ_—ᯙ"¼5ºMu²B?éÈ ÿ6d®æ™ ©oªTúã{\Y#Î×qÿçΕ3Ì'Ž––!,ò@'&µ£Äö_6Ü¿§žµÞÎ4‡õ…ù}ïm+ëSwìIkó²ü4ÔÞ;e«ÏKðÖ¾ó‹,VŠóC[Zý©±kŸ4òNèÙÒx®Ðo…Ù»wÎTÔÞ;e«ÏKðÔjo=²Óç¥øjî{–¾º÷m¡¼[­UZºÛ–·dAÇY«hs÷ÈÎF}ŒÑ57žÙióÒü4ÔÞ{e§ÏKð•ÜÖ«â+>µ›òµ´tâ=2Ç?7`A'˜ö;,jo=²Óç¥øi©¼öËOž—á«•0ÕÒÅUM+e†f #{yœÒ3‚÷q†Ñe®ºÔ¶GÁEO%DŒâÖ4¸™<‡ÎF¦óÛ->z_†š›Ïl´ùé~×á\OOˆ´ÝKn¹ÓB)©êcšªÈçlÑé!Źäï˜îÞ¶·*Úkuº¦á[.ªš–Í3ôIÑcAsŽCyÈ̃¦ñÙ’Óç¥øi©¼vËW—á­-9ÃÕôó>Ý=U\ì£udT­¤’9êb Ì™#[¬Ï±–ã˜[Û]`¯¶ÓW6 ŠqQeÔÎÍ #Òè½½‡b>pŠøÔÝûm«ÎËðÔêný¶ÕçeøjÞiš  Ì,2[çydr¿Hÿf“â¬PÔ2®’:˜óÑfç‚?Ì,ùîTl»­qeÓ›Û=Õ*3@PJ(DØLÑJ( s@ì"" ¡J E š æR¡J( s@R)A®¹þ–µ~úOdåù×þ _ª[ü|ÞÌ/Ñ7?ÒÖ¯ßI윿;Ä õC ›Ù„G»Ú?fT_RGìê+?;›÷Žþk—´~Ìh¾¤ØÔVþy7ïüÑX³Pˆ€ ¢ Ô" "(ͨþ)š„¡z Qšfˆ¨D@DP‚QFjTˆÿú‘ßV¿ÛD¹ŠŒ?¶c{¾#°‹m[nðBÉ¢­•ñ˜_tZæ–µÚM#½nþÊé¢ýcÕ¯öÑ+È<Â.¦µœŒØÚŠJ;µò¾y©k¡Ì½”ï{^"s²ZC€ÌoÏz×Óõ8ÄPaÚÛtL´éÏUâ*©"¨ƒ¬kšó6V²G] ü÷çáõõڃĭJ/´³Ûkn˜~êê*ê© L§W%<¬hdzB†ƒôÜ ‰fVëÿ/±õB“íÛLN¹ŠÈå‚¢1œ$ç° Æ†±­#æ;ש案¬ŒõFùSeŽ×¾¤˜ZÙšÀÝd.…í‘ÃDîÌe»"s9o(0UÊ“SÝêèì·zvÓRC%&.Nt@:šh nG!˜ç^ˆˆ'59¯”A9©Í|§a[oäÖÿ³D?¬NúµþÚ$¶þEgø×û8ÔCúÄÿ«_í¢AÌTX/ÖÌowÄvÛjÛw‚Ml¯ˆÂø›¢×4µ®ÒiíÝ¿²´±u6¬ä|fÆÔRQݯ•óÍK]eì§{Úñ ÒcezrfƒÈiºœb0ímºZt檂qT‘TAÖ5Íy›+Y È#®Ð~{óðÑ´u(¾ÒÔ[kn– «¨«ª¤4u2\”ò±¡‘érÓph`h.$™^؈<³ÿo±õB“ší¦'\ÅdrŠÁ ÑG˜Î sØcGXÖ‘—1Þº „î8¯Z•‘SÙh›,’µ­cå|Î-ë$c™ï9çw1]š òËnÅöذ¬ÑTÚ«*°Ôµ‘ÀÙg‘š ˜ÌÜK\Ð2ÑÈŒ²Þ´mêK‰†­öùŸf¯}=²ª‹SQ<‚*ye™ò6¢3 spxÜëÛÑãt½KqTµqº¶ÜD¢ÒM>µúº‘I®H¥ëw1Çx9;p€º«»Qu:Ä8zjŠQ=ÌUì°±î0Q‰˜Cbi#=“ÑìË»ÍyDÝO±%ÎÓm¤¸Ék¥užÅQm¤NùÒK¨¾G4µ»ÑÙ|å|Úz_(ñ’¼Çi‘´pQES%D›IüDmiԱЃ Ìnsdì–{‡¬©EpÝMpvª¨Kedï2“yl®5uÏ ‘¥›†_3ÈÍ£wewY¨Dš§f?ò¨?½7¶z¶©Ù¿DÁýé½³Ð\Eƒë4ÍB ”PÒ(D¢ŒÔ )Pˆ'ø"ÏæA(¡}"„A¯¹~•µ~úOdåùÛþ ?ª[ü|ÞÌ/Ñ7/ÒÖ¯ßI윿;ÄõC ›Ù„G»Z?fT_RGìê+<›÷Žþk—´~̨¾¤ØÔVþy7ïüÑXP”QØ@DMè¡ @(ŠE„T¦jBLü(™ 'aBf€ˆT ©ìFÿ«_í¢_Wj·RQ™Ð^N‹sæÍ|EúÄÿ«_í¢X¯ã:6?‘Dsvëæ(¼MQÈvª›„ò¤œ>cÓíi‘ÍÒ˳–jî—T.ö§ôê/ˆ¶]L%l8HkSW™ù²©—5s Ù,µÖ:[…ÎËn««¬fÕ$µ4±É'ã xiqõ¡Á£ævZ .¨]ìÔúuÅM>¨]ìTúuÅ]‡É¬-ÞÕ—Ð"÷UKTTö»åÆÙIPS½‘UÅQ†FÀàcsZá¾-#ßó’ˆæ´ú¡w±UéÔ?4ú¡w±UéÔ?ml=P°¥òjXm×W9ÕšB”ÍK4-œ·0àÇHƇ‘Ü ;–þçq§¶Ûj®5²ê©ia|ó?D»EÎ9IȸoWƒ‹Óê…ÞÅW§PüTÒêƒÞÅW§PüUÚZîT×;m-ʆmu-T-š4HÒc€-9˜ÜyŽõ_ß­¸vÍ=âñU³PÓèëeÕ¹ú:N š ;Üàœž—Tö*}6‡â¦}P{Ù¨ôêж³cÛ1¶\*íñ\®u†{}5‚°¸D2»-ùçÍ+¦lºM0Fa88Lú¡wµ?§Q|EN[ö!·]iè/véíòT‚ĭ)#”å¡Ñ’3Ë~Y‚½#X~uÇõO!ñX{7våæ&*pmè§Ú)c˜ !¼|Dz±Rþ‘¸ÿvŸÿuE§u¾!ý¿Ì¥/é ‡÷iÿ÷QQ/éË_öÔ{¬Õ³ìô²M£¤Z7 Ã/éË_öÔ{©» íòìþaÊÃ~Ä7 ­E’ÝQq’˜Pc0ÇDï .ž[òÏ5w>¨]íOéÔ_\ê`C"¿´v.îÏÌBVÞå_<î¨d2Í5;Äo’Ýd²d cK kA»"s9 ˆ$89Í.¨=ìÔúuÅQ¥Õ½Š¯N¡ø«}m§§œë—:G¿òdmÂy |:2¹ÑŸ'%± ¬¨mdöÚ×±õ05²6V ›Áj–® úëmeT•.ÑÉ ’èkÛÿVƒZ?-²r‡ñ!ÊiuAïb«Ó¨~*iuAïb«Ó¨~*è(1•‚¾íÉtUϨŸZèt£¦”ìh%ÌÖèêô€v–{–÷X~ux8=.¨=ìÔúuÅLú¡wµ?§Q|EÞkΚÃó§œSb+Í=èÚîô3PÖõ­†m[›+3ȹ¯a ïݸî]ŒODÉÌæ‚?ŠçqþRbì>8¦¬<`ûVúu$#Å·ù(1["³ükýœj!ý`Õ¯öÑ)·~Egø×û8ÔCúÀÿ«_í¢E}]jÝKHdcAy:-Ï›5Ê[¯xžñ5G!ÚªnSÈb|áðC˜çkLŽn–]œ³]%ôgFÑãò+ã©c´p];[Ì*ªÇú™Q¼ú¡wµ?§Q|DÒêƒÞÍG§PüUÕâK´Ö«K«!§eD¦hadoÆÒé%lc7r¿>c̹'uEsI— 9ó¾?îë7»åú}Yöyr«ëKª{>CñSKª{^CñWE„¯òÞéªä– hO8‹:zƒ4rEÍqkO4ƒ±ØO•Ö•Ÿ%9N>YÕkvmg£–•–ŽyoË<òß’÷õùøûö§ôê/ˆ«Ùq5Á÷9è.tÓRÕS9¢¢šv´9 ó8îp=‚ z°üëÏñ0ê‹9ì‹E?§ûàëóTìߢ`þôÞÙêÞj¥›ôLÞ›Û=mNhˆ‚Q@R€¨DŠ; B•ƒé&h%û¢„ÍÒ(Í /Ò¶¯ßI윿:ÿÄõ? ›Ù…ú&åúVÕûé=“—çoø€~§áo¬&öaîöŸÙÔ±ûºzßÏ'ý㿚æ-?³:/©bötõ¿žOûÇ4VEïLÓ4@ì(D@LÔ"" ¢„…B "ŒÑ¨D@ÍB ‘îÄ/úµþÚ%ówTÍ÷äTÇúÀÿ«_í¢_uÍ/„ ¹š#›Ãò>¤—7±Å¯cnniù޾|–ú{æ!5pÚ°½M„Jê·³1 ×£‘Ý“¹óìªê}]@Ë-E®¢xc¨¦¬©lðÊ@ >g½¹ƒØ-p?Åm¨…–gTáÙ-4OÏMl3´ÚA¹á™ÉÛ3˜)ÅkŒqV°G6 †šBí²z©#.ùËAo\î¸f7Œ‰Vm7z›¥ößQUC Τ­ŽHØòìõsBÖ#Î7»#áQ3¥¬ªÖ;‘ízNÍóSVk¤?ÝÍŒ >¥—Ížõz×O‡­m"ݶ”¹¡®tZ s€æÒ#yþ) ó¬/Ô÷SØð¾»Kk†ŠÃq7 },ï’IÞ÷5¹9Ðo^sÞs˰´ç©.(¨¹<Õ?ÁI-5\6’EšÆ8BíS!oä?AÝsÞAnm …í[uvSùÑö¦ÝEÝ”þt}ª*ªêu}~²ÙéíVžjæÕÆHjs‰×5òS»V÷uÃ@’zíÛ¶`\GSÔØªºŽ[¾Mh™ó<Å¢Ú Z:[˜¹Æ\Û×¢íÔ]ÙOçGÚ›uvSùÑö òÜmÔ÷bÆ^«ê¥µÑ\*íÐ[éiãeC&sŸ&€%ÇD“wnþÕb§÷Öã+Ö-ôp×mNNx©¬ŽIc,kãy‰Žˆ óÐpçå½zVÝEÝ”þt}©·Qwe?jg©v©Ã6§QVZm4õ8Ù%m®{ëKsëåcH;óçwå;›³—ª6n~éqÿ¦t;uvSùÑö®W×ÒV^lÚY™=C+MKÛÒаÈÒã—0ÍíÅúݺŽ1ý¿Ì¯šOÒîÓÿ£B´Œ–:OÏîݧÿÝQ/éËgöÔ{¯»ŽfŽAýŸÌ/‰MÛ?ÿ£Ø=e©n”hA¨ês›_ˆÁú\ÿé [›e,•6#aÒžšáT&oeºS¾Vçý±½‡û\þ ®¤£¼_íÕS² ‡ÖŠ–2GhéÆèbhpÏœfÇà·Ï-5y¯´Ý(#•í š)ˆ1ÎÑ̙̑ÉÃxϲ7'—ާ^56kh£¬ŠI¡ZÀñ[S®Ö’Ç€Ìdâ×oGG!žkpÉ6%¡10·UA>°|ÁòE¡ŸöêäËû ×Ô×\j^ð(¬p<œÄιÏ Ò³H+–6Sжi*n´õ55ÓšRö‚ã–@x€‚fw°–óíçqu*®¨ÆQÜ.íµVÚ¹våq–Îs‹â¨db1¢[‘s\ÌÈÏ.l‰X0ïR›Õ¢˜_f´´÷.’)V&alÍ-Æ71›]–îl×®íÔ]ÙOçGÚ›uvSùÑöªÝEÝ”þt}«’¼ÖRܱݾ9[9£¢¨5‡1‘Ñh‚~s ïòBºJc•ÿ«_í¢_7lÝNÑÿxþEL_§Ÿõsý´Kîµ…ñ—1Í¡ÂÕ/£ê[]RÏˀܤoö‰ç!u­¾YlqRZ$ÍSµ±4ßXÜØ`îÞùr8.{mF¯°WNÈåmE\50¹ú ’YñkÁ¶o¢ú:Ì[Tý.m gŸfœ“üQ]Dwê  .þcªðÙw„-<ÕpÍ‹©¦Ò¸ÔPJ$/hÕ¾2Ü€'±3–¼QÆ2ËTŒ¾fQüY­ôÔ4µÂ²kÛ«%lNŠ=sáhc\Z]­ô¼çù#$θšÁ‡*0,öþLp©l&ÊñUd,:½´š÷¥§Ì9—Šzb[V½éà‘϶ÒQš;f²cY+*csª^ÝtôAÏ wg½{¾ÝEÝ”þt}©·Qwe?j£Èê°UÊÑ„q%[èáŠY¦¥¬´Û­-|즩„×·­inü£fs^‡ÔâÈ놚|¶Ê‡ºª±Ã~”ò'Ÿóð·;uvSùÑö¦ÝEÝ”þt}¨9Œg›±¥ƒüoû©Öþ˜åOÿ°%Í^k)nXîß ­œÑÑT‚ØŒÈè´A?9Ðwù.– [ZwdR£ ·úºÏñ¯öq¨‡õÿV¿ÛD¦Ýý]_ø×û8”CúyÿW?ÛDŠ‹¶f™£þñüŠ©ÔÄå„#»kõR«Õ¬/„n;šÓu<¸QEfžÛ5LpÕÒÖÔ‰b{ƒ\Ý)žöý‚ב8ÎÿA]he ª¨•õ´¯cÙG)‰Á•1¹ÄI£ @ '<û ë _mVìdŠs;åmºœ¼SÑË9fqŒ´µmvŽyvVºá„á”6š“OOo!L*ZÆöÓ¹À»,ù·/º¬2À2·b‡PµÍk$dR3AÍkCGZyˆhw`™|ÏÇò÷åíçy$ŸjÉÔY’C‡k"™ŽŽFTB×µÃ"Ò(éó.qÝN1C®.Äü«D1½mív Ãù:½f«[ùnYèø;+¾Ã4–ë%¤Žã ºO/{ß(.{;œIÌžaüÚíÔ]ÙOçGÚ¶ü_Uôú||<¿Y–Tõ>Å<¯¡–§[N.ŒÊꇶ`3ßfn`g¿K~îe¦=H±.Ño²¾H©îQKq3ɯ­51HÈÝ(ÕîÑ/ï;³Ë>eí›uvSùÑö¦ÝEÝ”þt}«ÜyORÌK$Tìô•´6úxªÝ3ÄÖÇSµ¡æŸ­Þ‘;Ëy÷®×Xoµø· â;-óºËµkaª¨t:Ã,m`ÉÍc¹²$îù¿‡U·Qwe?jmÔ]ÙOçGڃʣêa|e‚àêÊ9e57Zªèãðe%d!±85Å­oqŽp2ÅCÔÇE†*mbK50Û)¦dÕqD¥L̆@âìó,$eÙ̯ZÛ¨»²ŸÎµ6ê.ì§ó£íA_ Òò}’ NJ¡µjô¿¢Ñ?Nóq=iÐg>yž´o'Ÿœów€]Õ¬ÿåÞÚ¡u›uvSùÑö®B*˜.˜îåQDñ40ÑAL齺Àù\[ŸÎÛþh•ÖæªÙÿEAý齳՞ʭgý÷¦öÏQV•‚QFjA@Sš„A?Á7¨Sš @¡J•‚B" ”  ”P¥HPˆ(\¿JÚÿ}'²rüíÿÕ -õ„ÞÌ/Ñ/Ò–¿ßI윿;þÿ©ø[ë ½˜D{µ§ögEõ,^À.ž·óÉÿxïæ¹‹OìÒ‹êX½€]=oç“þñßÍ…""„š„DAAE (DD@ÍA)š"Š3EJŒÓ5'5¢ «ÿžHò÷þôjÓ:MižÈ_C#œÙéuFv´Æøäv€’2AÈ;˜@;÷,e·ÿG«ƒßAZç‡ðµÎa5ÆÇT e§!cŽ_ÚXª|ŒÀÝëÒÿ“=ųѸýþ®} n?G«ƒßDjþEà^õé<–{‰ò+÷­Iä³Ü[<®_Fÿ«ƒßL®_Fÿ«ƒßA¬ù{Ö¤òYî'Ȭ Þµ'’Ïqlò¹}þ®}2¹}þ®}³äVïZ“Ég¸Ÿ"°/zÔžK=ųѸýþ®}FËèïõp{è5¿"ð/zÔžK=Äù»Ö¤ÿ&{‹e£qú;ý\úhܾŽÿW¾ƒ[ò7w¯KþL÷ë]›ڃͮÏ!åh€ïíÉ ŸóY4n?G«ƒßMÑÿêà÷Ðgs³9îþ ½/çµçÁþêÑÿêà÷ÖX uF`^õi<–{‹g£qú;ý\úÑÃÒà÷Ðk~F`^õ©<–{‰ò3÷«Iä³Ü[-—Ñßêà÷ÓFãôwú¸=ôß‘˜½jO%žâ|ŒÀ½ëR“=ŲѸýþ®}4n?G«ƒßA®ù»×¥ÿ&{‹cm¶Ù-p˜­V¸éNe­- 'ç 4fš7£ÿÕÁï©Ð¸ý=.}Rs9“¼¬ÕÕŒ³}h\Ná@Ð|5påþõ’(E=8ˆ½’JçºY\ÏÉÒ ‡ÎhßóæŠÃé©þ^áÿÞZá§{CšFE§²á{Ëf¦ÕÚÓÙ#´D‘’AÜÀ‚ß¹b,¸ÿ×ÿ«ƒßA‚é`Â×9„×UR–œ…Ž9|Ù–*#p1ÿéj_òg¸¶z£ÿÕÁï¨Ñ¸ýþ®}­ù{Ö¤òYî'ÈÌ Þµ'’Ïql´n?G«ƒßM—Ñßêà÷Ðk~F`^õ©<–{‰ò3÷«Iä³Ü[-—Ñßêà÷ÓFåôwú¸=ôß‘˜ôµ'ù3ÜSò7þ—¥ÿ&{‹e£qú;ý\úhÜ~ŽÿW¾m¶ØíP˜­v¸éNe­- 'ç 4f³“™Ìå`иý=.}4.Gp øjáËýè"ßý]WøÇû8ÔD?çrü½Ãÿ½Ï"žœD^Ù%sÝ,®gäéCç4oùóGB÷–ÏMª3µ¦7²Ghk#$ƒ¹¿r ‘¸4õÍižÈZûžÂ×9„×UR|…Ž9ib°Yqôú¸=ôѸýþ®}³änï^—ü™î'ÈÌ Þ½'ù3Ü[-Ñßêà÷ÓFãôwú¸=ôß‘x½jO%žâ|‹À½ëRy,÷ËFãôwú¸=ôѹ}þ®}·äVïZ“Ég¸Ÿ"°/zÔžK=ųѸýþ®}2¸ýþ®}³äVïZ“Ég¸Ÿ"°/zÔžK=ųѹ}þ®}2¹}þ®}³ä^ïZ“Ég¸§ä^ï^“ü™î-–W/£ÕÁï¦W/£ÕÁï ÖüŒÀÝëÒÿ“=Ŷ¡£µÛ©ÛOl¡m,mü–‚4[ý€¾n?G«ƒßMÑßêà÷ÐgÌ,Ö¸éK힆;›ºÑBÈÉÿ©õQh·Ârq?ä ÏL§§Šž7éˆÚCŸ–AÎ..q Ürð"¾‚(R‚QB”*¢…(%3P¥¢„A(QB‘ÌŠ”P¥¢„Í —éKWï¤öN_ÿÿÔü-õ„ÞÌ/Ð÷/Ò–¿ß?Ù9~xü?ÿSð·Ö{0ˆ÷[OìÒ‡êX½€]=oç³þñßÍsŸÙ¥Ô±{ºzßÏgý㿚+ ÍBa WÉEJ„DÍ P€ˆˆ¡ jxPJèS°€Qf€‰šƒÎ‚J "‚¡æŠç¹B"&j3A)šŒÑ)š„E3DDD@DP‚QB ”Pˆ%fˆ‰LÔ)E5 P4D Ôæ¡J(DŠ3R‚QB ”ÍBœÐ¨DŠ3LÐNjGΡJ(R€ˆˆ$"…*  )T‚¡J•óÙR‚P" ”P¥4A!J„þ(%Že('²Š ”P —éK_ïŸìœ¿<~ÿ©ø[ë ½›Wè{éK_ïŸìœ¿;þß©ø[ë ½˜D{µ§öiCõ,^À.ž»óÙÿxïæ¹‹OìÖ‡êX½€]5wç³þñßÍ…s"f€¡ABˆŠJŽÊ(@(ˆ€™¨LÐ3ÞŠ¨BTf‚T" "(ͨP¥B"" f…B fS2ˆ€Š=ȉE¨Í)šf¡æ¡*3DE3DDD@DDÍNiš„A9”ÍB œÓ5ƒè_*PJ(Ïz ”DP¨LÕŠ4ˆŠ R¡Š?Š”܈J(R€¥B ¥B ”QÙR‚FjWÊ úE P¨@‚Q P¤ ”D@ T)5÷/Ò–¿ß?Ù9~xü?¿Sð·Ö{6¯Ð÷/Ò–¿ß?Ù9~xü?¿Sð·Ö{6 ÷[WìÖ‡êX½€]=wç³þñßÍs¯Ù­Ô±{ºzïÏgý㿚 * ¡øT" ((ˆ¡E§aFh'5 •ˆT"" vQAP‚Sø(DLÑ•ˆ¡3A*4Í4ÍB(‚"fªˆ ”Í¢„PJ„ì"¨&j(¡J(DЦj¦jæ… QÍB”T¢„Í¢ŒÑ)ØDENiØPˆ%J„Í¢ŒÔ s"" E„ì •*A(¡Nj‰E P¨DŠ ”þˆ%( T"¤P )ÍBˆ„Þ¡J T" Òv¿ß?Ù¹~xü?S°·Ö{6¯Ð÷Òv¿ß?Ù¹~xü?Sð¿Ö{6¢=ÖÕû6¡ú–/`M\¦Ô~ñßÍs6¯ÙµÔ±{ºZïÏgý㿚+„D¡9‘B•¢„@DPJDD£4C΀¡Ge¨Pˆ$¨E •Q!A* LÔ •ˆ‚(LÑRT""¡RЍD…‚J•‚QFjP¢„(>‘FjP…‚QFjPæ¡J EJ" Š ”Pˆ¤P¥R „P}"⊉R¡J" ”DABãúNÙûçû7/χïêvúÂofÕúãúNÙûçû7/χïêvúÂofÔG»Z¿fÔ?RÅìék¿>Ÿ÷Žþk™µ~ͨ~¥‹ØÓWþ{?ïüÑXP•€ˆ¡¨Í "((B ˆ ¢¥ADPAB"!* ""£4(¦iΡf‚T" fŠJ„@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD¤òˆ>‘B€§°¡¥BD¢…* EJ ˆŠ T"¢Sr" EJ @¡J E P¨SØA(¡JeJ„Í LÑé;gïŸìÜ¿<~¿©Ø[ë ½›Wèké;gïŸìÜ¿<þŸ©Ø[ë ½›Qëjý›PýK° ¦¯üúÞ»ù®fÕû7¡ú–/`M_ùôÿ½wóE`DDòT¡Aˆ€y”P€ˆ…Bˆ • f¡ Eˆ€¡vPFjT (RT """ """ """ """ """ """ """ """ """ """ ""•‚sR£™;(%¨R‚QB”DæŠ*T" ÍJ€S4ˆˆ‰R£™R€¢ ”D@Sš‚§ø ”P¥„Pé+gïŸìÜ¿<þŸ©Ø_ë ½›Wèké+gïŸìÜ¿<þŸ©Ø_ë ½›Qëjý›ÐýK° ¥¯üú£÷®þk™µ~Íè~¥‹ØÓWþ}Qû×4£ „DT!RT ((S²€¡J„£J„Eî@*¢‚€ˆˆ f¡@DEAABˆB"" """ """ """ """ """ """ """ """ """ """ """ )Pˆ%… ¢%E*…¢…(J…( T" R )TŽu‚Qö…ò§™ DPN{Ñ*%?Š AFãúNÙûçû7/Ï?‡ßêvúÂofÕúáúJÙûçû7/Ï?‡ßêvúÂofÔGºZ¿fô?RÅìé«ÿ>¨ý뿚æm_³Š©bötÕÿŸT~õßÍB"‚€J" ‚ ©B‚¨D(! Pˆ€¡ „D@PS²ˆ J•‚S°ˆ ¥|©TJ"(%)T… |(‰DDT„P¤ ”Pˆ$)QÌ¥J„¤DEP¸~’¶~ùþÍËó×á÷ú…þ°›Ùµ~…¸þ’¶~ùþÍËóÏá÷ú›…¾°›ÙµÙÅÔ±{ºjÿϪ?zïæ¹›Wìâ‡êX½€]5çÕ½wó(ªèˆ€¡""‚ " (R£ø*!Š‚"‚P" !EB"ˆ""ª""" """ """ """ """ """ """ """ """ """ """ """ """ """B•R¥B” Ô¯•ô€¤(E¢TJ•ò¤ ”DA(HR¾Tÿb DD¨R‚…Çô•³÷Ïön_ž¿ÔÜ-õ„ÞÍ«ô5Çô•³÷Ïön_ž¯ÔÜ/õ„Þͨuµ~Î(~¥‹ØÒ×þ}Qû×5ÍZ¿g?RÅìékÿ>¨ýë¿™E`DDˆ! "DDT)Pˆ"!EB""!B’™¢ŠAP¤¢ˆ„DUDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD<ꨉDªJ…(%" ¡J E {(%)@R )@R)A(• PQ¸þ’¶~ùþÍËóÏáõú›…þ°›Ùµ~†¸~‘¶~ùþÍËóÏá÷ú›…þ°›Ùµî¶¿ÙÅÔ±{ºZÿϪ?zïæ¹«_ìâ‡êX½€]-çÕ½wó)UB•B• …*   ©P€T)*)* "‚ˆ T( < T*¢…?Ø¡²€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆˆ•*¢ŠT"‚T¨ U*© Ò( P¨DD©Pˆ© T(HAFáúFÙûçû7/Ï?‡ßênúÂofÕúáúJÛûçû7/Ï?‡×ênúÂofÔGºÚÿg?RÅìé+ÿ>Ÿ÷Žþk›µ~Î(~¥‹ØÑÖk& ‚ ŽÈÿ”)Q¹¥BQØRT (R  uŠ" !DQB•Qˆ€T*?Š„DD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD…DEU*Ts)D @Š”DA!J€¥"T¨DT…*©ê ¥pý#mýóý›—çŸÃëõ7 ýa7³jý pý#mùõÏön_ž¯ÔÜ/õ„Þͨ~¬l˜6ÍÛ¤Ç[)ÚáóƒVvÒÜ#ccо=ŒšdƒIÙv!Ã?ò\^¿Y öÈgÆuÊÊ8Zø™-(Æõ£8 Ýͼ“»yW¾Qáþþ+üýÝÕ6¢éô…?¢Ÿ}A‚éÝôþŠ}õÍ|£Ãýû×ùú?»§Ê,?ß½Ÿ£ûºƒ¥ÔÝ;¾ŸÑO¾š›§wÓú)÷×5ò‹÷ï_çèþîŸ(°ÿ~õþ~îè:MMÏ»éýû驹÷}?¢Ÿ}s_(°÷~õþ~“îéò‹wï_çé>ÔÜû¾ŸÑO¾ ÃsîúE>úæþQaîýëüý'ÝÓåïÞ¿ÏÒ}ÝG©¹÷u?£}57>î§ôcï®såïÞ¿ÏÒ}Ý>Páîýëüý'ÝÐtz›—wÓú1÷Ôjn]ÝOèÇß\ïÊ=ß½Ÿ¤ûº|¡ÃÝû×ùúO»ª:-MË»©ýûê 7.î§ôcï®{åïÞ¿ÏÒ}ÝGÊ ;ßµŸ¤ûºƒ¢Õ\»ºŸÑ¾š«—wSú1÷×;òƒ÷í_éŸwO”w¿jÿH¤ûº„ÃrîÚF>új®]ÛOèÇß\÷Ê ;ßµ¤R}Ý9wí_éŸwAÐê®]ÛOèÇßMUË»iýûëžåü9ßµ¤R}Ý9wí_éŸwAÐj®]ÛOèÇßMUË»iýûëŸåü9ßµ¤R}Ý9wí_éŸwTt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓSrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt«—vÓú1÷ÓUrîÚF>úçùwí_éŸwN_ÃûWúE'ÝÐt:«—vÓú1÷ÐErîÚF>úç¹wí_éŸwN_ýûWúE'ÝÔªåÝÔþŒ}ôÕ\»ºŸÑ¾¹ß”w¿jÿH¤ûº| Ã½ûWúE'ÝÐtB+ŸwSú1÷Ôên]ÝOèÇß\çÊ ;ßµŸ¤ûºŸ”w¿zÿ?I÷t¦çÝÔþŒ}ôÜûºŸÑ¾¹ß”w¿zÿ?I÷tùC‡»÷¯óôŸwAÑên}ßOèÇßMMÏ»éýûëœùC‡»÷¯óôŸwO”8{¿zÿ?I÷t&¦åÝôþŒ}ôÔÜû¾ŸÑ¾¹¿”8{¿zÿ?I÷tùE‡»÷¯óôŸwAÒên}ßOèÇßMMÏ»éýûëšùE‡»÷¯óôŸwSò‹wï_çé>Üû¾ŸÑO¾š›ŸwÓú)÷×5ò‹÷ï_çèþîŸ(°ÿ~õþ~îꎗStîúE>ú‘Óé E>úæ~QáþýëüýÝÓåïÞ¿ÏÑýÝM¨º}!Oè§ßMEÓé E>úæ~QáþýëüýÝÓåïâ¿ÏÑýÝAÔSQJ*ÅMUN¾F‚4ZÜû9fw¯Ï‡×ênúÂofÕìáþþ+üýÝׂ~w †°ü×PÊ÷é _ ŒYÇâãg>]œÿ'weå„DE褟³ÛgÿËí^º¤EÜqDD@DDD@DDD@DDDAÿÙ## Process this file with automake to produce Makefile.in bin_PROGRAMS = ecdb ecdb_SOURCES = \ ecdb.c ecdb.h \ ecdb_projects.c ecdb_projects.h \ ecdb_drives.c ecdb_drives.h \ ecdb_image.c ecdb_image.h \ ecdb_burn.c ecdb_burn.h \ ecdb_misc.c ecdb_misc.h \ ecdb_audio.c ecdb_audio.h \ ecdb_gui.c ecdb_gui.h \ ecdb_erase.c ecdb_erase.h \ ecdb_widgets.c ecdb_widgets.h \ ecdb_erase_gui.c ecdb_erase_gui.h \ ecdb_burn_image_gui.c ecdb_burn_image_gui.h \ ecdb_burn_data_gui.c ecdb_burn_data_gui.h \ ecdb_filelist_custom.c ecdb_filelist_custom.h \ ecdb_about.c ecdb_about.h \ ecdb_config_dialog.c ecdb_config_dialog.h \ ecdb_hal.c ecdb_hal.h \ ecdb_common.h ecdb_CFLAGS = @ECDB_CFLAGS@ ecdb_LDADD = @ECDB_LIBS@ /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" /* Global Variables */ int ECDB_DRIVE_ACTION_FINISHED = 0; int ECDB_DRIVE_ACTION_BEGUN = 0; int ECDB_DRIVE_ACTION_UPDATE = 0; unsigned int ECDB_FILELIST_SIZE_CHANGED = 0; Ecdb_Main *em; int ecdb_setup(); int ecdb_initialize_config(); int main(int argc, char **argv) { int ret = 0; if (!eina_init()) { fprintf(stderr, "Cannot initialize Eina!\n"); return 1; } if (!eina_error_init()) { fprintf(stderr, "Cannot initialize Eina Error!\n"); ret = 1; goto SHUTDOWN; } eina_error_log_level_set(EINA_ERROR_LEVEL_DBG); eina_error_print_cb_set(eina_error_print_cb_file, stderr); if (!ecore_init()) { EINA_ERROR_PERR("Cannot initialize Ecore!\n"); ret = 1; goto SHUTDOWN; } if (!ecore_file_init()) { EINA_ERROR_PERR("Cannot initialize Ecore_File!\n"); ret = 1; goto SHUTDOWN; } if (ecore_config_init("ecdb") != ECORE_CONFIG_ERR_SUCC) { EINA_ERROR_PERR("Cannot intialize Ecore_Config!\n"); ret = 1; goto SHUTDOWN; } if (!ecore_evas_init()) { EINA_ERROR_PERR("Cannot initialize Ecore_Evas!\n"); ret = 1; goto SHUTDOWN; } if (!edje_init()) { EINA_ERROR_PERR("Cannot initialize Edje!\n"); ret = 1; goto SHUTDOWN; } if (!efreet_init()) { EINA_ERROR_PERR("Cannot initialize Efreet!\n"); ret = 1; goto SHUTDOWN; } if (!ewl_init(&argc, argv)) { EINA_ERROR_PERR("Connot initialize Ewl!\n"); ret = 1; goto SHUTDOWN; } if (!e_dbus_init()) { EINA_ERROR_PERR("Cannot initialize E_DBus!\n"); ret = 1; goto SHUTDOWN; } if (!ecdb_image_init()) { EINA_ERROR_PERR("Cannot initialize libisofs!\n"); ret = 1; goto SHUTDOWN; } if (!ecdb_burn_init()) { EINA_ERROR_PERR("Cannot initialize libburn!\n"); ret = 1; goto SHUTDOWN; } if (!ecdb_setup()) { EINA_ERROR_PERR("Setup failed\n"); ret = 1; goto SHUTDOWN; } if (!ecdb_hal_init()) { EINA_ERROR_PERR("Ecdb_Hal initialization failed!\n"); ret = 1; goto SHUTDOWN; } ecdb_print_drive_info(); if (!ecdb_create_main_gui()) { EINA_ERROR_PERR("Cannot create main window\n"); ret = 1; ecdb_drive_info_list_free(em->drives); goto SHUTDOWN; } /* Ecdb_Audio_Project *proj; Ecdb_Source *src; proj = ecdb_audio_project_new(); i = 1; while ((i < argc) && (argv)) { if (ecore_file_exists(argv[i])) { if (!ecore_file_is_dir(argv[i])) { src = ecdb_source_new(); ecdb_source_data_set(src, argv[i], 0); ecdb_source_child_append(proj->tracks, src); } } i++; } ecdb_audio_project_start(proj); proj->publisher_id = proj->data_preparer_id = proj->system_id = proj->application_id = proj->copywrite_id = proj->abstract_id = proj->biblio_id = strdup("ecdb"); if (!ecdb_aquire_drive(ECDB_PROJECT(proj), 0)) { printf("Couldn't grab drive!\n"); ret = 1; goto SHUTDOWN; } if (!ecdb_burn_project(proj)) { printf("Burn was unsuccessful\n"); ret = 1; goto SHUTDOWN; } */ ecore_main_loop_begin(); /* End testing */ SHUTDOWN: burn_finish(); iso_finish(); e_dbus_shutdown(); ewl_shutdown(); ecore_file_shutdown(); ecore_config_shutdown(); ecore_evas_shutdown(); edje_shutdown(); ecore_shutdown(); efreet_shutdown(); ewl_shutdown(); eina_error_shutdown(); eina_shutdown(); printf("Program Done\n"); return ret; }; int ecdb_setup(void) { em = NULL; em = calloc(1, sizeof(Ecdb_Main)); if (!ecore_file_mkdir("/tmp/ecdb")) { printf("Creation of temporary directory failed!\n"); } ECDB_DRIVE_ACTION_FINISHED = ecore_event_type_new(); ECDB_DRIVE_ACTION_BEGUN = ecore_event_type_new(); ECDB_DRIVE_ACTION_UPDATE = ecore_event_type_new(); ECDB_FILELIST_SIZE_CHANGED = ewl_callback_type_add(); ecore_event_handler_add(ECORE_EVENT_SIGNAL_EXIT, ecdb_shutdown, NULL); ecore_event_handler_add(ECORE_X_EVENT_XDND_POSITION, ecdb_dnd_position, em); ecore_event_handler_add(ECORE_X_EVENT_XDND_DROP, ecdb_dnd_drop, em); ecore_event_handler_add(ECORE_X_EVENT_SELECTION_NOTIFY, ecdb_dnd_selection, em); if (!ecdb_initialize_config()) { EINA_ERROR_PERR("Initializing the configuration failed!\n"); return FALSE; } return TRUE; } int ecdb_initialize_config(void) { char *gui, *engine, *tmp_eng; Eina_List *l; // Theme ecore_config_theme_default("theme", "default"); ecore_config_describe("theme", "The name of the theme to use."); ecore_config_long_opt_set("theme", "The name of the theme to use."); ecore_config_short_opt_set("theme", 't'); // Frame rate ecore_config_int_default("frame_rate", 60); ecore_config_describe("frame_rate", "The frame rate of the GUI."); ecore_config_long_opt_set("frame_rate", "The frame rate of the GUI."); ecore_config_short_opt_set("frame_rate", 'r'); // Scale ecore_config_float_default("scale", 1.0); ecore_config_describe("scale", "The scaling factor."); ecore_config_long_opt_set("scale", "The scaling factor."); ecore_config_short_opt_set("scale", 's'); // Use scale? ecore_config_boolean_default("use_scale", 0); ecore_config_describe("use_scale", "Use scaling?"); ecore_config_long_opt_set("use_scale", "Use scaling?"); ecore_config_short_opt_set("use_scale", 'u'); // Engine ecore_config_string_default("engine", "software_x11"); ecore_config_describe("engine", "The canvas engine."); ecore_config_long_opt_set("engine", "The canvas engine."); ecore_config_short_opt_set("engine", 'e'); // Load the config ecore_config_load(); // Set up the theme path ecore_config_theme_search_path_append(PACKAGE_DATA_DIR"/themes"); gui = ecore_config_theme_with_path_get("theme"); if ((!gui) || (!ecore_file_exists(gui)) || (!edje_file_group_exists(gui, "ecdb/window"))) { EINA_ERROR_PWARN("Invalid file specified, attempting to fall back " " to default!\n"); gui = ecore_config_theme_with_path_from_name_get("default"); ecore_config_theme_set("theme", "default"); if (!gui) { EINA_ERROR_PERR("Default theme is missing! Please check" " your installation.\n"); return 0; } } em->theme_path = gui; // Set up the engines engine = ecore_config_string_get("engine"); l = ecore_evas_engines_get(); if (!eina_list_count(l)) { EINA_ERROR_PERR("There are no built engines!\n"); return 0; } EINA_LIST_FOREACH(l, l, tmp_eng) { if (!strcmp(tmp_eng, engine)) { em->engine = engine; break; } } // Default to last in the list if (!em->engine) em->engine = strdup((char *)eina_list_last(l)); ecore_evas_engines_free(l); // Initialize the other variables em->scalef = ecore_config_float_get("scale"); em->fps = ecore_config_int_get("frame_rate"); return 1; } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_H #define ECDB_H #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define PATH_MAX 4095 #include typedef struct _Ecdb_Page Ecdb_Page; struct _Ecdb_Page { Evas_Object *gui; Evas_Object *welcome; Evas_Object *filelist; Evas_Object *data; Evas_Object *audio; Evas_Object *erase; Evas_Object *image; }; /* ECDB Global Variables */ typedef struct _Ecdb_Main Ecdb_Main; struct _Ecdb_Main { char *theme_path; double scalef; char *engine; int fps; Ecore_Evas *main_win_ee; Ecore_X_Window xwin; Eina_List *drives; /* Drag and drop stuff here */ Eina_List *evas_dnd_candidates; Eina_List *ewl_dnd_candidates; Evas_Object *evas_drop_object; Ewl_Widget *ewl_drop_object; /* Change this to a list at some point, but for now save some time * and leave it singular */ Ecdb_Page *page; /* System dbus connection */ E_DBus_Connection *conn; /* E_DBus signal handlers */ E_DBus_Signal_Handler *dev_added; E_DBus_Signal_Handler *dev_removed; /* Used to help with initialization */ unsigned int init_count; }; extern Ecdb_Main *em; /* Callbacks */ extern int ECDB_DRIVE_ACTION_FINISHED; extern int ECDB_DRIVE_ACTION_BEGUN; extern int ECDB_DRIVE_ACTION_UPDATE; extern unsigned int ECDB_FILELIST_SIZE_CHANGED; #include "ecdb_common.h" #include "ecdb_projects.h" #include "ecdb_drives.h" #include "ecdb_image.h" #include "ecdb_burn.h" #include "ecdb_erase.h" #include "ecdb_misc.h" #include "ecdb_audio.h" #include "ecdb_gui.h" #include "ecdb_widgets.h" #include "ecdb_erase_gui.h" #include "ecdb_burn_image_gui.h" #include "ecdb_burn_data_gui.h" #include "ecdb_filelist_custom.h" #include "ecdb_about.h" #include "ecdb_config_dialog.h" #include "ecdb_hal.h" #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" static Ecore_Evas *win = NULL; static void _ecdb_about_destroy(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { ecore_evas_free(data); win = NULL; } void ecdb_about_show(void) { Evas_Coord mw, mh, w, x, y; Evas_Object *gui; // Only show 1 about window if (win) return; if (!strcmp(em->engine, "opengl_x11")) win = ecore_evas_gl_x11_new(0, 0, 0, 0, 255, 255); else if (!strcmp(em->engine, "xrender_x11")) win = ecore_evas_xrender_x11_new(0, 0, 0, 0, 255, 255); else if (!strcmp(em->engine, "software_x11")) win = ecore_evas_software_x11_new(0, 0, 0, 0, 255, 255); if (!win) { EINA_ERROR_PWARN("Cannot create the about window!\n"); return; } ecore_evas_title_set(win, "ECDB About"); ecore_evas_name_class_set(win, "ECDB", "ECDB"); ecore_evas_avoid_damage_set(win, 1); gui = edje_object_add(ecore_evas_get(win)); edje_object_file_set(gui, em->theme_path, "ecdb/about"); edje_object_signal_callback_add(gui, "ecdb/close", "ecdb", _ecdb_about_destroy, win); ecore_evas_object_associate(win, gui, ECORE_EVAS_OBJECT_ASSOCIATE_DEL); edje_object_size_min_get(gui, &mw, &mh); if (mw <= 0) mw = 350; if (mh <= 0) mh = 350; if (ecore_config_boolean_get("use_scale")) { mw = (int)((float)mw * em->scalef); mh = (int)((float)mh * em->scalef); } ecore_evas_resize(win, mw, mh); evas_object_resize(gui, mw, mh); ecore_evas_size_min_set(win, mw, mh); edje_object_size_max_get(gui, &mw, &mh); if (ecore_config_boolean_get("use_scale")) { mw = (int)((float)mw * em->scalef); mh = (int)((float)mh * em->scalef); } ecore_evas_size_max_set(win, mw, mh); evas_object_move(gui, 0, 0); evas_object_show(gui); ecore_evas_show(win); ecore_evas_geometry_get(em->main_win_ee, &x, &y, &w, NULL); ecore_evas_move(win, x + ((w - mw) / 2), y); /* Set the proper text */ edje_object_part_text_set(gui, "ecdb.about.label", "Close"); edje_object_part_text_set(gui, "ecdb.about.text", "Copywrite ©2006-2008, by the ECDB Development Team.
" "This software is provided as-is with no explicit or implied " "warranty. This software is governed by licensing conditions, " "so please see the COPYING and COPYRIGHT license files installed " "on your system.
" "ECDB is under HEAVY DEVELOPMENT and is " "not stable. Many features are incomplete or non-existant, and " "will likely have many bugs. You have been WARNED!" "

" "Authors:
" "Jaime Thomas <avi.thomas@gmail.com>
" "
" "Contributors:
" "Mario Danic <mario@libburnia-project.org>
"); } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_ABOUT_H #define ECDB_ABOUT_H void ecdb_about_show(void); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" int transcode_data_cb(void *data, int type, void *event); void ecdb_audio_project_start(Ecdb_Audio_Project *proj) { char cmd[PATH_MAX]; //<-- + 20ish? int i; /* Fork off the gstreamer program for every file to be added * Depending on the number of files, this can be pretty system intensive, so * is there a way to reduce/control this ? */ for (i = 0; ECDB_BURN(proj)->files->children[i]; i++) { snprintf(cmd, PATH_MAX, "ecdb_transcode_helper %s", ECDB_BURN(proj)->files->children[i]->dst); ecore_exe_pipe_run(cmd, ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_READ_LINE_BUFFERED, NULL); } proj->num_tracks = i + 1; ecore_event_handler_add(ECORE_EXE_EVENT_DATA, transcode_data_cb, proj); } int transcode_data_cb(void *data, int type, void *event) { const char *rec; Ecore_Exe_Event_Data *ev = event; Ecdb_Audio_Project *proj = data; rec = ev->data; proj->num_transcode_complete++; EINA_ERROR_PDBG("Message: %s\n", rec); if (!strcmp(rec, "EOS")) { EINA_ERROR_PDBG("Transcode complete\n"); proj->num_transcode_complete++; } else { EINA_ERROR_PWARN("Error!\n"); // How to handle these? } if (proj->num_tracks == proj->num_transcode_complete) { EINA_ERROR_PINFO("Hurrah, transcoding is done!\n"); /* Change to another event later */ ecore_event_add(ECORE_EVENT_SIGNAL_EXIT, NULL, NULL, NULL); } return 1; } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_AUDIO_H #define ECDB_AUDIO_H void ecdb_audio_project_start(Ecdb_Audio_Project *proj); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" typedef struct Burn_Data Burn_Data; struct Burn_Data { BurnDisc *disc; BurnSession *session; Eina_List *sources; Eina_List *tracks; Ecdb_Project *proj; Ecdb_Page *page; }; int ecdb_burn_finished(void *data, int type, void *event); int ecdb_erase_project_init(Ecdb_Erase_Project *proj); static void ecdb_burn_progress_handler(void *data, void *buffer, unsigned int nbyte); void ecdb_sources_list_free(Eina_List *list); void ecdb_tracks_list_free(Eina_List *list); int ecdb_burn_project(Ecdb_Burn_Project *proj, Ecdb_Page *page) { char reasons[BURN_REASONS_LEN]; int padding, i; Burn_Data *data; BurnTrack *track; BurnSource *source; BurnWriteOpts *opts; pthread_t progress_update; i = 0; data = calloc(1, sizeof(Burn_Data)); if (!data) { EINA_ERROR_PWARN("Error: Cannot create burn data structure!\n"); return FALSE; } data->proj = ECDB_PROJECT(proj); data->page = page; if (proj->burn_mode != BURN_AUDIO) padding = 300*1024; data->disc = burn_disc_create(); data->session = burn_session_create(); burn_disc_add_session(data->disc, data->session, BURN_POS_END); track = burn_track_create(); burn_track_define_data(track, 0, padding, 1, proj->burn_mode); source = ecdb_image_project(proj); if (!source) { EINA_ERROR_PWARN("Failed to add any files to burn disc!\n"); burn_session_free(data->session); burn_disc_free(data->disc); ecdb_sources_list_free(data->sources); ecdb_tracks_list_free(data->tracks); FREE(data); return ECDB_ERROR_IMAGE_CREATE; } if (burn_track_set_source(track, source) != BURN_SOURCE_OK) { EINA_ERROR_PWARN("Error: Cannot attach source object to track object!\n"); burn_session_free(data->session); burn_disc_free(data->disc); ecdb_sources_list_free(data->sources); ecdb_tracks_list_free(data->tracks); FREE(data); return ECDB_ERROR_SOURCE_ATTACH; } burn_session_add_track(data->session, track, BURN_POS_END); data->sources = eina_list_append(data->sources, source); data->tracks = eina_list_append(data->tracks, track); opts = burn_write_opts_new(ECDB_PROJECT(proj)->drive->tangible[0].drive); burn_write_opts_set_perform_opc(opts, proj->opc); burn_write_opts_set_multi(opts, proj->multi); if (proj->simulate) { EINA_ERROR_PINFO("Simulating Burn!\n"); } burn_write_opts_set_simulate(opts, proj->simulate); burn_drive_set_speed(ECDB_PROJECT(proj)->drive->tangible->drive, 0, proj->speed); burn_write_opts_set_underrun_proof(opts, proj->underrun_proof); EINA_ERROR_PDBG("Searching for burn mode\n"); if (burn_write_opts_auto_write_type(opts, data->disc, reasons, 0) == BURN_WRITE_NONE) { EINA_ERROR_PWARN("Error: Failed to find a suitable write mode" " for disc!\n"); burn_session_free(data->session); burn_disc_free(data->disc); burn_write_opts_free(opts); ecdb_sources_list_free(data->sources); ecdb_tracks_list_free(data->tracks); FREE(data); return ECDB_ERROR_WRITE_MODE; } burn_disc_write(opts, data->disc); burn_write_opts_free(opts); EINA_ERROR_PINFO("Disc now burning\n"); ECDB_PROJECT(proj)->pipe = ecore_pipe_add(ecdb_burn_progress_handler, data); pthread_create(&progress_update, NULL, ecdb_drive_progress_update, proj); pthread_detach(progress_update); ECDB_PROJECT(proj)->ev_handler = ecore_event_handler_add (ECDB_DRIVE_ACTION_FINISHED, ecdb_burn_finished, data); return ECDB_ERROR_NONE; } /* This function is pretty naive... Should probably update it at some time */ void * ecdb_drive_progress_update(void *data) { const Ecdb_Project *proj; BurnProgress p; struct burn_drive *drive; proj = data; if (!proj->drive->tangible) { EINA_ERROR_PWARN("No tangible drive!\n"); ecore_pipe_del(proj->pipe); pthread_exit(NULL); } drive = proj->drive->tangible[0].drive; EINA_ERROR_PDBG("Progress update active\n"); while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) { usleep(100000); } while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) { if (p.sectors > 0) { ecore_pipe_write(proj->pipe, &p, sizeof(p)); } usleep(100000); } EINA_ERROR_PDBG("Closing pipe\n"); ecore_pipe_write(proj->pipe, proj->pipe, sizeof(Ecore_Pipe)); pthread_exit(NULL); } static void ecdb_burn_progress_handler(void *data, void *buffer, unsigned int nbyte) { BurnProgress *p; Evas_Object *swallow; Burn_Data *bdata; static int last_sector = 0; int percent; Edje_Message_Int_Set *progress_msg; Edje_Message_String finalize; if (nbyte != sizeof(BurnProgress)) { ecore_event_add(ECDB_DRIVE_ACTION_FINISHED, NULL, NULL, NULL); EINA_ERROR_PDBG("Adding event to queue.\n"); last_sector = 0; return; } else { p = buffer; } /* Libburn reports p->sector as being 0 right at the end of the job, * so store the last sector and use that to determine the proper * percentage/sector to set */ if (last_sector <= p->sector) { last_sector = p->sector; } else { last_sector = p->sectors; } bdata = data; if (!bdata) { EINA_ERROR_PWARN("ecdb_burn_progress_handler: NULL bdata!\n"); return; } if ((!bdata->page) || (!bdata->proj)) { EINA_ERROR_PWARN("ecdb_burn_progress_handler: NULL page or proj!\n"); return; } switch (bdata->proj->type) { case ECDB_AUDIO_PROJECT: swallow = bdata->page->audio; break; case ECDB_DATA_PROJECT: swallow = bdata->page->data; break; case ECDB_IMAGE_PROJECT: swallow = bdata->page->image; break; default: EINA_ERROR_PWARN("ecdb_burn_progress_handler: Unrecognized " "project type!\n"); return; } percent = (int)((double)(last_sector + 1) / (double)p->sectors * 100.0); if (percent >= 100) { finalize.str = "Finalizing disc..."; edje_object_message_send(swallow, EDJE_MESSAGE_STRING, 1, &finalize); } else { progress_msg = alloca(sizeof(Edje_Message_Int_Set) + (4 * sizeof(int))); progress_msg->count = 5; progress_msg->val[0] = percent; progress_msg->val[1] = last_sector; progress_msg->val[2] = p->sectors; progress_msg->val[3] = (int)p->buffer_available; progress_msg->val[4] = (int)p->buffer_capacity; edje_object_message_send(swallow, EDJE_MESSAGE_INT_SET, 0, progress_msg); } } int ecdb_burn_finished(void *data, int type, void *event) { Burn_Data *proj; proj = data; EINA_ERROR_PDBG("Freeing source and tracks\n"); ecdb_sources_list_free(proj->sources); ecdb_tracks_list_free(proj->tracks); EINA_ERROR_PDBG("Freeing session and disc\n"); burn_session_free(proj->session); burn_disc_free(proj->disc); EINA_ERROR_PDBG("Releasing drive\n"); burn_drive_release(proj->proj->drive->tangible[0].drive, 1); burn_drive_info_free(proj->proj->drive->tangible); EINA_ERROR_PINFO("Burn Complete\n"); ecore_event_handler_del(proj->proj->ev_handler); proj->proj->ev_handler = NULL; ecore_pipe_del(proj->proj->pipe); proj->proj->pipe = NULL; switch (proj->proj->type) { case ECDB_AUDIO_PROJECT: EINA_ERROR_PWARN("How in the world did you get here?\n"); //ecdb_burn_audio_cleanup(proj->page); break; case ECDB_DATA_PROJECT: ecdb_burn_data_cleanup(proj->page); break; case ECDB_IMAGE_PROJECT: ecdb_burn_image_cleanup(proj->page); break; default: EINA_ERROR_PWARN("ecdb_burn_finished: unknown project type!\n"); } FREE(proj); return TRUE; } void ecdb_sources_list_free(Eina_List *list) { void *data; EINA_LIST_FREE(list, data) burn_source_free(data); } void ecdb_tracks_list_free(Eina_List *list) { void *data; EINA_LIST_FREE(list, data) burn_track_free(data); } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_BURN_H #define ECDB_BURN_H int ecdb_burn_project(Ecdb_Burn_Project *proj, Ecdb_Page *page); void *ecdb_drive_progress_update(void *data); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" static void _button_cb_return(void *data, Evas_Object *obj, const char *emission, const char *source); static void _button_cb_begin(void *data, Evas_Object *obj, const char *emission, const char *source); static void _page_del(void *data, Evas *e, Evas_Object *obj, void *ev); static void _combo_cb_clicked(void *data, Evas_Object *obj, const char *emission, const char *source); static void _update_capacity(Ewl_Widget *w, void *ev_info, void *data); // Config inwin updates static void _joliet_clicked(void *data, Evas_Object *obj, const char *emission, const char *source); static void _rockridge_clicked(void *data, Evas_Object *obj, const char *emission, const char *source); static void _symlinks_clicked(void *data, Evas_Object *obj, const char *emission, const char *source); static void _hidden_clicked(void *data, Evas_Object *obj, const char *emission, const char *source); static void _multi_clicked(void *data, Evas_Object *obj, const char *emission, const char *source); static void _sim_clicked(void *data, Evas_Object *obj, const char *emission, const char *source); static void _underrun_clicked(void *data, Evas_Object *obj, const char *emission, const char *source); static void _apply_clicked(void *data, Evas_Object *obj, const char *emission, const char *source); static void _proj_name(void *data, Evas *e, Evas_Object *obj, void *event_info); static void _settings_clicked(void *data, Evas_Object *obj, const char *emission, const char *source); // Update on showing to proper values static void _joliet_config(void *data, Evas_Object *obj, void *event_info); static void _rockridge_config(void *data, Evas_Object *obj, void *event_info); static void _symlinks_config(void *data, Evas_Object *obj, void *event_info); static void _hidden_config(void *data, Evas_Object *obj, void *event_info); static void _multi_config(void *data, Evas_Object *obj, void *event_info); static void _sim_config(void *data, Evas_Object *obj, void *event_info); static void _underrun_config(void *data, Evas_Object *obj, void *event_info); static void _proj_config(void *data, Evas_Object *obj, void *event_info); static void _page_hide_finished(void *data, Evas_Object *o, const char *emission, const char *source); // Burn callbacks and functions static void _ecdb_burn_data_do_burn(Ecdb_Page *page, Ecdb_Data_Project *proj); void _ecdb_burn_data_unmount_cb(void *data, void *reply_data, DBusError *err); static void _page_del(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *ev __UNUSED__) { Ecdb_Data_Project *proj; Ecdb_Source *src; src = data; proj = evas_object_data_get(obj, "proj"); if (proj) ecdb_data_project_destroy(proj); } static void _page_hide_finished(void *data, Evas_Object *o, const char *emission __UNUSED__, const char *source __UNUSED__) { Ecdb_Page *page = data; Ecdb_Data_Project *proj; proj = evas_object_data_get(page->data, "proj"); edje_object_part_unswallow(page->gui, page->data); evas_object_hide(page->data); ewl_widget_hide(proj->filelist); } static void _update_capacity(Ewl_Widget *w, void *ev_info, void *data) { Ecdb_Page *page; Ecdb_Source *src; Ecdb_Data_Project *proj; Ecdb_Project *base_proj; float val; page = data; proj = evas_object_data_get(page->data, "proj"); base_proj = ECDB_PROJECT(proj); src = ECDB_BURN(proj)->files; /* Find our topmost source */ while ((src) && (src->parent)) src = src->parent; if ((base_proj->drive) && (base_proj->drive->capacity > 0)) { EINA_ERROR_PDBG("drive capacity: %lld, src->size: %lld\n", base_proj->drive->capacity, src->size); val = (float)src->size / (float)base_proj->drive->capacity; } else { // Default to a nice, small size val = (float)src->size / 1048576.0 / 700.0; } if (val > 1.0) { EINA_ERROR_PDBG("OVER SIZE LIMIT!\n"); edje_object_signal_emit(proj->capacity, "ecdb,capacity,exceeded", "ecdb"); edje_object_signal_emit(page->data, "ecdb,capacity, exceeded", "ecdb"); } ecdb_capacity_float_set(proj->capacity, val); EINA_ERROR_PDBG("%lld\n", src->size); } static void _button_cb_return(void *data, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__) { Ecdb_Page *page; page = data; edje_object_signal_emit(page->data, "ecdb,burn_data_page,hide", "ecdb"); ecdb_welcome_page_show(page); /* Hide the filelist to be nice */ edje_object_signal_emit(page->gui, "ecdb,filelist,hide", "ecdb"); } static void _button_cb_begin(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { Ecdb_Data_Project *proj; Ecdb_Page *page; int drive, speed; page = data; proj = evas_object_data_get(page->data, "proj"); drive = ecdb_combo_selected_get(ECDB_PROJECT(proj)->drive_combo); if (drive < 0) { EINA_ERROR_PINFO("Choose a drive!\n"); edje_object_part_text_set(page->data, "progress_text", "Choose a Drive!"); return; } speed = ecdb_speed_convert(eina_list_nth(em->drives, drive), ecdb_combo_selected_get(ECDB_BURN(proj)->speed_combo)); if (speed < 0) ECDB_BURN(proj)->speed = 0; else ECDB_BURN(proj)->speed = speed; while (ECDB_BURN(proj)->files->parent) ECDB_BURN(proj)->files = ECDB_BURN(proj)->files->parent; if (!ecdb_set_project_drive(ECDB_PROJECT(proj), drive)) { EINA_ERROR_PWARN("Drive index doesn't exist!\n"); edje_object_part_text_set(page->data, "progress_text", "Drive doesn't exist!"); return; } if (!ECDB_PROJECT(proj)->drive->fresh_info) { EINA_ERROR_PDBG("Insert a disc into the drive!\n"); edje_object_part_text_set(page->data, "progress_text", "Insert a disc into the drive!"); return; } if (!((ECDB_PROJECT(proj)->drive->status & ECDB_DISC_BLANK) || (ECDB_PROJECT(proj)->drive->status & ECDB_DISC_APPENDABLE))) { EINA_ERROR_PDBG("Disc is not blank or appendable!\n"); edje_object_part_text_set(page->data, "progress_text", "Disc is not blank or appendable!"); return; } if (!(ECDB_PROJECT(proj)->drive->status & ECDB_DISC_BLANK)) { ecdb_hal_request_unmount(ECDB_PROJECT(proj)->drive, _ecdb_burn_data_unmount_cb, page); } else { _ecdb_burn_data_do_burn(page, proj); } } static void _combo_cb_clicked(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { Evas_Object *b; Ecdb_Data_Project *proj; Ecdb_Project *base_proj; int sel, i, num_speeds; char buf[1024]; proj = data; if (!proj) return; base_proj = ECDB_PROJECT(proj); sel = ecdb_combo_selected_get(base_proj->drive_combo); if (sel < 0) return; base_proj->drive = eina_list_nth(em->drives, sel); /* Don't keep adding items to the combo if the drive hasn't changed */ if (base_proj->drive == ecdb_combo_data_get(ECDB_BURN(proj)->speed_combo)) return; // Update the capacity here, as our capacity could change with the drive ewl_callback_call_with_event_data(proj->filelist, ECDB_FILELIST_SIZE_CHANGED, ECDB_BURN(proj)->files); num_speeds = base_proj->drive->write_speeds[0]; ecdb_combo_data_set(ECDB_BURN(proj)->speed_combo, base_proj->drive); for (i = 1; i < num_speeds; i++) { if (base_proj->drive->write_speeds[i] <= 0) continue; b = ecdb_combo_item_add(ECDB_BURN(proj)->speed_combo, NULL); snprintf(buf, sizeof(buf), "%dkb/s", base_proj->drive->write_speeds[i]); ecdb_combo_item_label_set(b, buf); ecdb_combo_append(ECDB_BURN(proj)->speed_combo, b); evas_object_show(b); } } static void _joliet_clicked(void *data, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__) { int state; Ecdb_Data_Project *proj; proj = data; proj->use_joliet = !proj->use_joliet; state = ecdb_check_checked_get(obj); if (state != proj->use_joliet) ecdb_check_checked_set(obj, proj->use_joliet); } static void _joliet_config(void *data, Evas_Object *obj, void *event_info __UNUSED__) { Ecdb_Data_Project *proj; proj = data; ecdb_check_checked_set(obj, proj->use_joliet); } static void _rockridge_clicked(void *data, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__) { int state; Ecdb_Data_Project *proj; proj = data; proj->use_rockridge = !proj->use_rockridge; state = ecdb_check_checked_get(obj); if (state != proj->use_rockridge) ecdb_check_checked_set(obj, proj->use_rockridge); } static void _rockridge_config(void *data, Evas_Object *obj, void *event_info __UNUSED__) { Ecdb_Data_Project *proj; proj = data; ecdb_check_checked_set(obj, proj->use_rockridge); } static void _symlinks_clicked(void *data, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__) { int state; Ecdb_Data_Project *proj; proj = data; proj->follow_symlinks = !proj->follow_symlinks; state = ecdb_check_checked_get(obj); if (state != proj->follow_symlinks) ecdb_check_checked_set(obj, proj->follow_symlinks); } static void _symlinks_config(void *data, Evas_Object *obj, void *event_info __UNUSED__) { Ecdb_Data_Project *proj; proj = data; ecdb_check_checked_set(obj, proj->follow_symlinks); } static void _hidden_clicked(void *data, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__) { int state; Ecdb_Data_Project *proj; proj = data; proj->ignore_hidden = !proj->ignore_hidden; state = ecdb_check_checked_get(obj); if (state != proj->ignore_hidden) ecdb_check_checked_set(obj, proj->ignore_hidden); } static void _hidden_config(void *data, Evas_Object *obj, void *event_info __UNUSED__) { Ecdb_Data_Project *proj; proj = data; ecdb_check_checked_set(obj, proj->ignore_hidden); } static void _multi_clicked(void *data, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__) { int state; Ecdb_Burn_Project *proj; proj = data; proj->multi = !proj->multi; state = ecdb_check_checked_get(obj); if (state != proj->multi) ecdb_check_checked_set(obj, proj->multi); } static void _multi_config(void *data, Evas_Object *obj, void *event_info __UNUSED__) { Ecdb_Burn_Project *proj; proj = data; ecdb_check_checked_set(obj, proj->multi); } static void _sim_clicked(void *data, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__) { int state; Ecdb_Burn_Project *proj; proj = data; proj->simulate = !proj->simulate; state = ecdb_check_checked_get(obj); if (state != proj->simulate) ecdb_check_checked_set(obj, proj->simulate); } static void _sim_config(void *data, Evas_Object *obj, void *event_info __UNUSED__) { Ecdb_Burn_Project *proj; proj = data; ecdb_check_checked_set(obj, proj->simulate); } static void _underrun_clicked(void *data, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__) { int state; Ecdb_Burn_Project *proj; proj = data; proj->underrun_proof = !proj->underrun_proof; state = ecdb_check_checked_get(obj); if (state != proj->underrun_proof) ecdb_check_checked_set(obj, proj->underrun_proof); } static void _underrun_config(void *data, Evas_Object *obj, void *event_info __UNUSED__) { Ecdb_Burn_Project *proj; proj = data; ecdb_check_checked_set(obj, proj->underrun_proof); } static void _apply_clicked(void *data, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__) { ecdb_config_inwin_hide(data); } static void _proj_name(void *data, Evas *e, Evas_Object *obj, void *event_info __UNUSED__) { char *name; Ecdb_Data_Project *proj; proj = data; name = ecdb_entry_text_get(obj); if (proj->volume_id) free(proj->volume_id); proj->volume_id = name; } static void _proj_config(void *data, Evas_Object *obj, void *event_info __UNUSED__) { Ecdb_Data_Project *proj; proj = data; if (proj->volume_id) ecdb_entry_text_set(obj, proj->volume_id); else ecdb_entry_text_set(obj, "Project Name"); } static void _settings_clicked(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { Eina_Iterator *it; const Evas_Object *table; Evas_Object *o; table = edje_object_part_object_get(data, "ecdb.table"); it = evas_object_table_iterator_new(table); if (!it) { EINA_ERROR_PWARN("_settings_clicked: NULL iterator!\n"); return; } while (eina_iterator_next(it, (void **)&o)) evas_object_smart_callback_call(o, "proj_config", NULL); eina_iterator_free(it); ecdb_config_inwin_show(data); } void ecdb_burn_data_page_show(Ecdb_Page *page) { if (!page->data) { Evas_Object *b, *t; Eina_List *l; Ecdb_Drive_Info *drive; Evas_Coord x, y, w, h; Ewl_Widget *embed; Ecdb_Data_Project *proj; Ecdb_Burn_Project *bup; Ecdb_Project *bp; page->data = edje_object_add(ecore_evas_get(em->main_win_ee)); edje_object_file_set(page->data, em->theme_path, "ecdb/burn_data_page"); edje_object_part_geometry_get(page->gui, "active_area", &x, &y, &w, &h); evas_object_move(page->data, x, y); evas_object_resize(page->data, w, h); edje_object_part_swallow(page->gui, "action_area", page->data); edje_object_signal_callback_add(page->data, "hide,finished", "burn_data_page", _page_hide_finished, page); evas_object_show(page->data); proj = ecdb_data_project_new(); bp = ECDB_PROJECT(proj); bup = ECDB_BURN(proj); evas_object_data_set(page->data, "proj", proj); bp->ret = ecdb_button_add(page->data, "ecdb/burn_data/return"); ecdb_button_label_set(bp->ret, "Return to Main Page"); edje_object_signal_callback_add(page->data, "ecdb,clicked", "ecdb/burn_data/return", _button_cb_return, page); evas_object_show(bp->ret); ecdb_button_icon_set(bp->ret, "ecdb/burn_data/return"); bp->begin = ecdb_button_add(page->data, "ecdb/burn_data/begin"); ecdb_button_label_set(bp->begin, "Start Burn"); edje_object_signal_callback_add(page->data, "ecdb,clicked", "ecdb/burn_data/begin", _button_cb_begin, page); evas_object_show(bp->begin); ecdb_button_icon_set(bp->begin, "ecdb/burn_data/begin"); bp->drive_combo = ecdb_combo_add(page->data, "ecdb/burn_data/drive"); ecdb_combo_header_set(bp->drive_combo, "Drive"); ecdb_combo_header_create_set(bp->drive_combo, ecdb_gui_combo_header_from_drive); evas_object_show(bp->drive_combo); bup->speed_combo = ecdb_combo_add(page->data, "ecdb/burn_data/speed"); ecdb_combo_header_create_set(bup->speed_combo, ecdb_gui_combo_header_from_speeds); ecdb_combo_header_set(bup->speed_combo, "Max Speed"); evas_object_show(bup->speed_combo); EINA_LIST_FOREACH(em->drives, l, drive) { b = ecdb_combo_item_add(bp->drive_combo, NULL); ecdb_combo_item_label_set(b, drive->product); edje_object_signal_callback_add(b, "ecdb,clicked", "ecdb", _combo_cb_clicked, proj); ecdb_combo_append(bp->drive_combo, b); evas_object_show(b); } embed = ewl_embed_new(); ewl_embed_dnd_aware_remove(EWL_EMBED(embed)); ewl_object_fill_policy_set(EWL_OBJECT(embed), EWL_FLAG_FILL_ALL); proj->filelist_swallow = ewl_embed_canvas_set(EWL_EMBED(embed), evas_object_evas_get(bp->drive_combo), (void *)(long)ecore_evas_software_x11_window_get(em->main_win_ee)); ewl_embed_focus_set(EWL_EMBED(embed), TRUE); ewl_widget_show(embed); proj->filelist = ecdb_custom_filelist_new(); ewl_container_child_append(EWL_CONTAINER(embed), proj->filelist); ewl_callback_append(proj->filelist, ECDB_FILELIST_SIZE_CHANGED, _update_capacity, page); ewl_widget_data_set(proj->filelist, "src", bup->files); ewl_widget_show(proj->filelist); /* Add a delete callback so we can correctly free data that won't get * handled automatically */ evas_object_event_callback_add(page->data, EVAS_CALLBACK_DEL, _page_del, bup->files); edje_object_part_geometry_get(page->data, "ecdb/burn_data/filelist", &x, &y, &w, &h); evas_object_move(proj->filelist_swallow, x, y); evas_object_resize(proj->filelist_swallow, w, h); edje_object_part_swallow(page->data, "ecdb/burn_data/filelist", proj->filelist_swallow); ecdb_widget_focus_callback_add(proj->filelist_swallow, "ecdb/burn_data/filelist"); evas_object_show(proj->filelist_swallow); proj->capacity = ecdb_capacity_add(page->data, "ecdb/burn_data/capacity"); evas_object_data_set(proj->capacity, "src", bup->files); evas_object_show(proj->capacity); t = ecdb_config_inwin_add(page->data, NULL); b = ecdb_check_add(t, NULL); ecdb_check_label_set(b, "Use Joliet"); edje_object_signal_callback_add(b, "ecdb,check,*checked", "ecdb", _joliet_clicked, proj); evas_object_smart_callback_add(b, "proj_config", _joliet_config, proj); ecdb_config_inwin_child_add(t, b, 0, 0, 1, 1); evas_object_show(b); b = ecdb_check_add(t, NULL); ecdb_check_label_set(b, "Use Rockridge"); edje_object_signal_callback_add(b, "ecdb,check,*checked", "ecdb", _rockridge_clicked, proj); evas_object_smart_callback_add(b, "proj_config", _rockridge_config, proj); ecdb_config_inwin_child_add(t, b, 0, 1, 1, 1); evas_object_show(b); b = ecdb_check_add(t, NULL); ecdb_check_label_set(b, "Follow Symlinks"); edje_object_signal_callback_add(b, "ecdb,check,*checked", "ecdb", _symlinks_clicked, proj); evas_object_smart_callback_add(b, "proj_config", _symlinks_config, proj); ecdb_config_inwin_child_add(t, b, 0, 2, 1, 1); evas_object_show(b); b = ecdb_check_add(t, NULL); ecdb_check_label_set(b, "Ignore Hidden Files"); edje_object_signal_callback_add(b, "ecdb,check,*checked", "ecdb", _hidden_clicked, proj); evas_object_smart_callback_add(b, "proj_config", _hidden_config, proj); ecdb_config_inwin_child_add(t, b, 0, 3, 1, 1); evas_object_show(b); b = ecdb_check_add(t, NULL); ecdb_check_label_set(b, "Multi-session"); edje_object_signal_callback_add(b, "ecdb,check,*checked", "ecdb", _multi_clicked, proj); evas_object_smart_callback_add(b, "proj_config", _multi_config, proj); ecdb_config_inwin_child_add(t, b, 1, 0, 1, 1); evas_object_show(b); b = ecdb_check_add(t, NULL); ecdb_check_label_set(b, "Simulate"); edje_object_signal_callback_add(b, "ecdb,check,*checked", "ecdb", _sim_clicked, proj); evas_object_smart_callback_add(b, "proj_config", _sim_config, proj); ecdb_config_inwin_child_add(t, b, 1, 1, 1, 1); evas_object_show(b); b = ecdb_check_add(t, NULL); ecdb_check_label_set(b, "Use Underrun Proof"); edje_object_signal_callback_add(b, "ecdb,check,*checked", "ecdb", _underrun_clicked, proj); evas_object_smart_callback_add(b, "proj_config", _underrun_config, proj); ecdb_config_inwin_child_add(t, b, 1, 2, 1, 1); evas_object_show(b); b = ecdb_entry_add(t, NULL); ecdb_entry_text_set(b, "Volume Name"); evas_object_event_callback_add(b, EVAS_CALLBACK_FOCUS_OUT, _proj_name, proj); evas_object_smart_callback_add(b, "proj_config", _proj_config, proj); ecdb_config_inwin_child_add(t, b, 1, 3, 1, 1); evas_object_show(b); b = ecdb_button_add(t, NULL); ecdb_button_label_set(b, "Apply"); edje_object_signal_callback_add(b, "ecdb,clicked", "*", _apply_clicked, t); ecdb_config_inwin_child_add(t, b, 0, 4, 2, 1); evas_object_show(b); // At the end so t is a valid pointer b = ecdb_button_add(page->data, "ecdb/burn_data/settings"); ecdb_button_label_set(b, "Settings"); edje_object_signal_callback_add(page->data, "ecdb,clicked", "ecdb/burn_data/settings", _settings_clicked, t); evas_object_show(b); } else if (edje_object_part_swallow_get(page->gui, "action_area") != page->data) { edje_object_part_swallow(page->gui, "action_area", page->data); evas_object_show(page->data); ewl_widget_show(ECDB_DATA(evas_object_data_get(page->data, "proj")) ->filelist); } edje_object_signal_emit(page->gui, "ecdb,burn_data_page,visible", "ecdb"); edje_object_signal_emit(page->data, "ecdb,burn_data_page,visible", "ecdb"); } void ecdb_burn_data_cleanup(Ecdb_Page *page) { Ewl_Widget *filelist; Ecdb_Source *src, *t; Ecdb_Data_Project *proj; int i; if (!page) { EINA_ERROR_PWARN("ecdb_burn_data_cleanup: NULL page!\n"); return; } proj = evas_object_data_get(page->data, "proj"); if (!proj) { EINA_ERROR_PWARN("ecdb_burn_data_cleanup: NULL proj!\n"); return; } Evas_Object *objs[] = {proj->capacity, proj->settings, proj->filelist_swallow, ECDB_BURN(proj)->speed_combo, ECDB_PROJECT(proj)->ret, ECDB_PROJECT(proj)->begin, NULL}; for (i = 0; objs[i] != NULL; i++) edje_object_signal_emit(objs[i], "ecdb,enable", "ecdb"); edje_object_signal_emit(page->data, "ecdb,burn_data,start", "ecdb"); edje_object_part_text_set(page->data, "progress_text", "Burn Complete!"); edje_object_signal_emit(page->data, "ecdb,burn_data,done", "ecdb"); filelist = proj->filelist; src = ewl_widget_data_get(filelist, "src"); while (src->parent) src = src->parent; for (i = 0; i < src->num_children; i++) { t = src->children[i]; ecdb_source_child_remove(src, t); ecdb_source_destroy(t); } /* Make sure, of course, to refresh the capacity and the filelist * after finishing the burn */ ewl_mvc_dirty_set(EWL_MVC(EWL_FILELIST(proj->filelist)->controller), TRUE); ewl_callback_call_with_event_data(proj->filelist, ECDB_FILELIST_SIZE_CHANGED, src); } void _ecdb_burn_data_unmount_cb(void *data, void *reply_data __UNUSED__, DBusError *err) { Ecdb_Data_Project *proj; Ecdb_Page *page; page = data; if (!page) { EINA_ERROR_PWARN("NULL page!\n"); return; } proj = evas_object_data_get(page->data, "proj"); if (!proj) { EINA_ERROR_PWARN("NULL project!\n"); return; } if (dbus_error_is_set(err)) { if (!strcmp(err->name, "org.freedesktop.Hal.Device.Volume.NotMounted")) { _ecdb_burn_data_do_burn(page, proj); } else { edje_object_part_text_set(page->data, "progress_text", "Can not unmount drive!"); EINA_ERROR_PWARN("Unmount error:\n%s\n%s\n", err->name, err->message); } dbus_error_free(err); } else { _ecdb_burn_data_do_burn(page, proj); } } static void _ecdb_burn_data_do_burn(Ecdb_Page *page, Ecdb_Data_Project *proj) { char *buf; int idx; Ecdb_Burn_Result burn_result; if (!proj) { EINA_ERROR_PWARN("NULL project!\n"); return; } if (!page) { EINA_ERROR_PWARN("NULL page!\n"); return; } if (!ecdb_aquire_drive(ECDB_PROJECT(proj))) { EINA_ERROR_PWARN("Couldn't grab drive!\n"); edje_object_part_text_set(page->data, "progress_text", "Couldn't grab the drive!"); return; } edje_object_part_text_set(page->data, "progress_text", "Commencing..."); burn_result = ecdb_burn_project(ECDB_BURN(proj), page); switch (burn_result) { case ECDB_ERROR_NONE: edje_object_signal_emit(page->gui, "ecdb,filelist,hide", "ecdb"); edje_object_signal_emit(page->data, "ecdb,burn_data,start", "ecdb"); Evas_Object *objs[] = {proj->capacity, proj->settings, proj->filelist_swallow, ECDB_BURN(proj)->speed_combo, ECDB_PROJECT(proj)->ret, ECDB_PROJECT(proj)->begin, NULL}; for (idx = 0; objs[idx] != NULL; idx++) { edje_object_signal_emit(objs[idx], "ecdb,disable", "ecdb"); printf("disabling controls...\n"); } return; case ECDB_ERROR_IMAGE_CREATE: buf = "Invalid file!"; break; case ECDB_ERROR_SOURCE_ATTACH: buf = "Couldn't attach source data!"; break; case ECDB_ERROR_WRITE_MODE: buf = "No suitable burn mode!"; break; default: buf = "Unknown error :-("; } edje_object_part_text_set(page->data, "progress_text", buf); burn_drive_release(ECDB_PROJECT(proj)->drive->tangible[0].drive, 0); burn_drive_info_free(ECDB_PROJECT(proj)->drive->tangible); } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_BURN_DATA_GUI_H #define ECDB_BURN_DATA_GUI_H void ecdb_burn_data_page_show(Ecdb_Page *page); void ecdb_burn_data_cleanup(Ecdb_Page *page); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" static void _button_cb_return(void *data, Evas_Object *obj, const char *emission, const char *source); static void _button_cb_begin(void *data, Evas_Object *obj, const char *emission, const char *source); static void _combo_cb_clicked(void *data, Evas_Object *obj, const char *emission, const char *source); static void _page_hide_finished(void *data, Evas_Object *o, const char *emission, const char *source); static void _page_del(void *data, Evas *e, Evas_Object *obj, void *ev); static void _page_del(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *ev __UNUSED__) { Ecdb_Image_Project *proj; proj = evas_object_data_get(obj, "proj"); if (proj) ecdb_image_project_destroy(proj); } static void _page_hide_finished(void *data, Evas_Object *o, const char *emission __UNUSED__, const char *source __UNUSED__) { Evas_Object *gui; gui = data; edje_object_part_unswallow(gui, o); evas_object_hide(o); } static void _button_cb_return(void *data, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__) { Ecdb_Page *page; page = data; edje_object_part_text_set(page->image, "progress_text", "Begin Burn"); edje_object_part_text_set(page->image, "progress_percent", "0%"); edje_object_signal_emit(page->image, "ecdb,burn_image_page,hide", "ecdb"); ecdb_welcome_page_show(page); /* Hide the filelist to be nice */ edje_object_signal_emit(page->gui, "ecdb,filelist,hide", "ecdb"); } static void _button_cb_begin(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { Ecdb_Image_Project *proj; Ecdb_Page *page; Ecdb_Source *iso_file; char *file, *buf; Ecdb_Burn_Result burn_result; int drive, speed, idx; page = data; proj = evas_object_data_get(page->image, "proj"); file = ecdb_entry_text_get(proj->entry); // Make sure that file actually exists if (!ecore_file_exists(file)) { edje_object_part_text_set(page->image, "progress_text", "File doesn't " "exists!"); FREE(file); return; } iso_file = ecdb_source_new(); ecdb_source_data_set(iso_file, file); FREE(file); ecdb_source_child_append(ECDB_BURN(proj)->files, iso_file); drive = ecdb_combo_selected_get(ECDB_PROJECT(proj)->drive_combo); if (drive < 0) { EINA_ERROR_PDBG("Choose a drive!\n"); edje_object_part_text_set(page->image, "progress_text", "Choose a Drive!"); return; } speed = ecdb_speed_convert(eina_list_nth(em->drives, drive), ecdb_combo_selected_get(ECDB_BURN(proj)->speed_combo)); if (speed < 0) ECDB_BURN(proj)->speed = 0; else ECDB_BURN(proj)->speed = speed; if (!ecdb_set_project_drive(ECDB_PROJECT(proj), drive)) { EINA_ERROR_PWARN("Drive index doesn't exist!\n"); edje_object_part_text_set(page->image, "progress_text", "Drive index doesn't exist!"); return; } if (!ECDB_PROJECT(proj)->drive->fresh_info) { EINA_ERROR_PDBG("Insert a disc into the drive!\n"); edje_object_part_text_set(page->image, "progress_text", "Insert a disc into the drive!"); return; } if (!(ECDB_PROJECT(proj)->drive->status & ECDB_DISC_BLANK)) { EINA_ERROR_PDBG("Insert a blank disc!\n"); edje_object_part_text_set(page->image, "progress_text", "Insert a blank disc!"); return; } if (!ecdb_aquire_drive(ECDB_PROJECT(proj))) { EINA_ERROR_PWARN("Couldn't grab drive!\n"); edje_object_part_text_set(page->image, "progress_text", "Couldn't grab the drive!"); return; } edje_object_part_text_set(page->image, "progress_text", "Commencing..."); burn_result = ecdb_burn_project(ECDB_BURN(proj), page); switch (burn_result) { case ECDB_ERROR_NONE: edje_object_signal_emit(page->gui, "ecdb,filelist,hide", "ecdb"); edje_object_signal_emit(page->image, "ecdb,burn_image,start", "ecdb"); Evas_Object *objs[] = {proj->entry, ECDB_PROJECT(proj)->ret, ECDB_PROJECT(proj)->begin, ECDB_PROJECT(proj)->drive_combo, ECDB_BURN(proj)->speed_combo, NULL}; for (idx = 0; objs[idx]; idx++) edje_object_signal_emit(objs[idx], "ecdb,disable", "ecdb"); return; case ECDB_ERROR_IMAGE_CREATE: buf = "Invalid file!"; break; case ECDB_ERROR_SOURCE_ATTACH: buf = "Couldn't attach source data!"; break; case ECDB_ERROR_WRITE_MODE: buf = "No suitable burn mode!"; break; default: buf = "Unknown error :-("; } edje_object_part_text_set(page->image, "progress_text", buf); burn_drive_release(ECDB_PROJECT(proj)->drive->tangible[0].drive, 0); burn_drive_info_free(ECDB_PROJECT(proj)->drive->tangible); } static void _combo_cb_clicked(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { Evas_Object *b; Ecdb_Burn_Project *bp; Ecdb_Drive_Info *drive; int sel, i, num_speeds; char buf[1024]; bp = data; sel = ecdb_combo_selected_get(ECDB_PROJECT(bp)->drive_combo); if (sel < 0) return; ECDB_PROJECT(bp)->drive = drive = eina_list_nth(em->drives, sel); /* Don't keep adding items to the combo if the drive hasn't changed */ if (drive == ecdb_combo_data_get(bp->speed_combo)) return; num_speeds = drive->write_speeds[0]; ecdb_combo_data_set(bp->speed_combo, drive); for (i = 1; i < num_speeds; i++) { if ((drive->write_speeds[i]) <= 0) continue; b = ecdb_combo_item_add(bp->speed_combo, NULL); snprintf(buf, sizeof(buf), "%dkb/s", drive->write_speeds[i]); ecdb_combo_item_label_set(b, buf); ecdb_combo_append(bp->speed_combo, b); evas_object_show(b); } } void ecdb_burn_image_page_show(Ecdb_Page *page) { if (!page->image) { Evas_Object *b; Eina_List *l; Ecdb_Drive_Info *drive; Evas_Coord x, y, w, h; Ecdb_Image_Project *proj; Ecdb_Project *bp; Ecdb_Burn_Project *bup; /* Create the swallow */ page->image = edje_object_add(ecore_evas_get(em->main_win_ee)); edje_object_file_set(page->image, em->theme_path, "ecdb/burn_image_page"); edje_object_part_geometry_get(page->gui, "active_area", &x, &y, &w, &h); evas_object_move(page->gui, x, y); evas_object_resize(page->image, w, h); edje_object_part_swallow(page->gui, "action_area", page->image); evas_object_event_callback_add(page->image, EVAS_CALLBACK_DEL, _page_del, page); edje_object_signal_callback_add(page->image, "hide,finished", "burn_image_page", _page_hide_finished, page->gui); evas_object_show(page->image); /* Make the project */ proj = ecdb_image_project_new(); bp = ECDB_PROJECT(proj); bup = ECDB_BURN(proj); evas_object_data_set(page->image, "proj", proj); /* Make the interface all nice and proper */ bp->ret = ecdb_button_add(page->image, "ecdb/burn_image/return"); ecdb_button_label_set(bp->ret, "Return to Main Page"); edje_object_signal_callback_add(page->image, "ecdb,clicked", "ecdb/burn_image/return", _button_cb_return, page); evas_object_show(bp->ret); ecdb_button_icon_set(bp->ret, "ecdb/burn_image/return"); bp->begin = ecdb_button_add(page->image, "ecdb/burn_image/begin"); ecdb_button_label_set(bp->begin, "Start Burn"); edje_object_signal_callback_add(page->image, "ecdb,clicked", "ecdb/burn_image/begin", _button_cb_begin, page); evas_object_show(bp->begin); ecdb_button_icon_set(bp->begin, "ecdb/burn_image/begin"); proj->entry = ecdb_entry_add(page->image, "ecdb/burn_image/file"); ecdb_entry_text_set(proj->entry, "Burn File"); evas_object_show(proj->entry); bp->drive_combo = ecdb_combo_add(page->image, "ecdb/burn_image/drive"); ecdb_combo_header_set(bp->drive_combo, "Drive"); ecdb_combo_header_create_set(bp->drive_combo, ecdb_gui_combo_header_from_drive); evas_object_show(bp->drive_combo); bup->speed_combo = ecdb_combo_add(page->image, "ecdb/burn_image/speed"); ecdb_combo_header_create_set(bup->speed_combo, ecdb_gui_combo_header_from_speeds); ecdb_combo_header_set(bup->speed_combo, "Max Speed"); evas_object_show(bup->speed_combo); EINA_LIST_FOREACH(em->drives, l, drive) { b = ecdb_combo_item_add(bp->drive_combo, NULL); ecdb_combo_item_label_set(b, drive->product); edje_object_signal_callback_add(b, "ecdb,clicked", "ecdb", _combo_cb_clicked, proj); ecdb_combo_append(bp->drive_combo, b); evas_object_show(b); } } else if (edje_object_part_swallow_get(page->gui, "action_area") != page->image) { edje_object_part_swallow(page->gui, "action_area", page->image); evas_object_show(page->image); } edje_object_signal_emit(page->gui, "ecdb,burn_image_page,visible", "ecdb"); edje_object_signal_emit(page->image, "ecdb,burn_image_page,visible", "ecdb"); } void ecdb_burn_image_cleanup(Ecdb_Page *page) { Ecdb_Image_Project *proj; int idx; if (!page) { EINA_ERROR_PWARN("ecdb_burn_image_cleanup: NULL page!\n"); return; } proj = evas_object_data_get(page->image, "proj"); if (!proj) { EINA_ERROR_PWARN("ecdb_burn_image_cleanup: NULL proj!\n"); return; } Evas_Object *objs[] = {proj->entry, ECDB_PROJECT(proj)->ret, ECDB_PROJECT(proj)->begin, ECDB_PROJECT(proj)->drive_combo, ECDB_BURN(proj)->speed_combo, NULL}; for (idx = 0; objs[idx]; idx++) edje_object_signal_emit(objs[idx], "ecdb,enable", "ecdb"); edje_object_part_text_set(page->image, "progress_text", "Burn Complete!"); edje_object_signal_emit(page->image, "ecdb,burn_image,done", "ecdb"); // Set project attributes back to normal ecdb_entry_text_set(proj->entry, NULL); } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_BURN_IMAGE_GUI_H #define ECDB_BURN_IMAGE_GUI_H void ecdb_burn_image_page_show(Ecdb_Page *page); void ecdb_burn_image_cleanup(Ecdb_Page *page); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_COMMON_H #define ECDB_COMMON_H #undef FREE #define FREE(dat) \ { \ if (dat) { free(dat); dat = NULL; } \ } #undef __UNUSED__ #define __UNUSED__ __attribute__ ((unused)) #define IN 1 #define OUT 2 /* Typdefs */ typedef struct burn_source BurnSource; typedef struct burn_disc BurnDisc; typedef struct burn_session BurnSession; typedef struct burn_write_opts BurnWriteOpts; typedef struct burn_track BurnTrack; typedef struct burn_progress BurnProgress; typedef struct burn_drive_info BurnDriveInfo; typedef enum burn_drive_status BurnDriveStatus; typedef enum _Ecdb_Disc_Status Ecdb_Disc_Status; enum _Ecdb_Disc_Status { ECDB_DISC_BLANK = 0x1, ECDB_DISC_REWRITABLE = 0x2, ECDB_DISC_APPENDABLE = 0x4 }; typedef enum _Ecdb_Project_Type Ecdb_Project_Type; enum _Ecdb_Project_Type { ECDB_BURN_PROJECT, ECDB_AUDIO_PROJECT, ECDB_ERASE_PROJECT, ECDB_COPY_PROJECT, ECDB_IMAGE_PROJECT, ECDB_DATA_PROJECT }; typedef enum _Ecdb_Burn_Result Ecdb_Burn_Result; enum _Ecdb_Burn_Result { ECDB_ERROR_IMAGE_CREATE, ECDB_ERROR_SOURCE_ATTACH, ECDB_ERROR_WRITE_MODE, ECDB_ERROR_NONE }; typedef struct _Ecdb_Drive_Info Ecdb_Drive_Info; struct _Ecdb_Drive_Info { /* Speeds */ int *write_speeds; /* Drive info */ char *vendor; char *product; char *revision; char *location; char *udi; unsigned char write_cdr:1; unsigned char write_cdrw:1; unsigned char write_dvdr:1; unsigned char write_dvdrw:1; unsigned char write_dvdram:1; unsigned char write_dvdplusr:1; unsigned char write_dvdplusrw:1; unsigned char write_dvdplusrwdl:1; unsigned char write_dvdplusrdl:1; unsigned char write_bdr:1; unsigned char write_bdre:1; unsigned char write_hddvdr:1; unsigned char write_hddvdrw:1; unsigned char support_multisession:1; unsigned char fresh_info:1; /* Disc info */ char *type; char *disc_udi; unsigned int status; unsigned long long capacity; BurnDriveInfo *tangible; }; typedef struct _Ecdb_Project_Info Ecdb_Project; struct _Ecdb_Project_Info { /* The drive reference */ Ecdb_Drive_Info *drive; Ecore_Event_Handler *ev_handler; Ecore_Pipe *pipe; Ecdb_Project_Type type; /* Every project has these */ Evas_Object *ret; Evas_Object *begin; Evas_Object *drive_combo; }; /* Typecast a pointer to an Ecdb_Project */ #define ECDB_PROJECT(proj) ((Ecdb_Project *) proj) /* Typecast a pointer to an Ecdb_Source */ #define ECDB_SOURCE(src) ((Ecdb_Source *) src) typedef struct _Ecdb_Source Ecdb_Source; struct _Ecdb_Source { const char *dst; unsigned char dir:1; unsigned int num_children; long long size; //Sizeof itself and children Ecdb_Source **children; Ecdb_Source *parent; IsoNode *node; }; typedef struct _Ecdb_Burn_Project Ecdb_Burn_Project; struct _Ecdb_Burn_Project { /* Inherit from normal project */ Ecdb_Project proj; /* The file source */ Ecdb_Source *files; Evas_Object *speed_combo; /* burn options */ unsigned char opc:1; unsigned char multi:1; unsigned char simulate:1; unsigned char underrun_proof:1; int speed; /* burn stuff */ int fifo_chunksize; int fifo_chunks; int burn_mode; }; /* Typecast a pointer to an Ecdb_Burn_Project */ #define ECDB_BURN(proj) ((Ecdb_Burn_Project *) proj) typedef struct _Ecdb_Image_Project Ecdb_Image_Project; struct _Ecdb_Image_Project { /* Inherit from a burn project */ Ecdb_Burn_Project proj; Evas_Object *entry; }; /* Typecast a pointer to an Ecdb_Image_Project */ #define ECDB_IMAGE(proj) ((Ecdb_Image_Project *) proj) typedef struct _Ecdb_Data_Project Ecdb_Data_Project; struct _Ecdb_Data_Project { /* Inherit from a burn project */ Ecdb_Burn_Project proj; /* Files are important here */ Evas_Object *capacity; Evas_Object *settings; Evas_Object *filelist_swallow; Ewl_Widget *filelist; /* iso options */ int iso_level; unsigned char use_joliet:1; unsigned char use_rockridge:1; unsigned char follow_symlinks:1; unsigned char ignore_hidden:1; unsigned char ignore_special:1; unsigned char iso1990:1; /* Ids */ char *volume_id; char *publisher_id; char *data_preparer_id; char *system_id; char *application_id; char *copywrite_id; char *abstract_id; char *biblio_id; }; /* Typecast a pointer to an Ecdb_Image_Project */ #define ECDB_DATA(proj) ((Ecdb_Data_Project *) proj) typedef struct _Ecdb_Audio_Project Ecdb_Audio_Project; struct _Ecdb_Audio_Project { /* Inherit from normal project */ Ecdb_Burn_Project proj; int num_tracks; int num_transcode_complete; }; /* Typecast a pointer to an Ecdb_Audio_Project */ #define ECDB_AUDIO(proj) ((Ecdb_Audio_Project *) proj) typedef struct _Ecdb_Erase_Project Ecdb_Erase_Project; struct _Ecdb_Erase_Project { /* Inherit from normal project */ Ecdb_Project proj; /* Add a check widget */ Evas_Object *speed; /* Speed */ unsigned char quick:1; unsigned char format:1; }; /* Typecast a pointer to an Ecdb_Erase_Project */ #define ECDB_ERASE(proj) ((Ecdb_Erase_Project *) proj) #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" #include #include static void destroy_cb(Ewl_Widget *w, void *event, void *data); static void conf_clicked_cb(Ewl_Widget *w, void *event, void *data); static void scale_cb(Ewl_Widget *w, void *event, void *data); static void fps_cb(Ewl_Widget *w, void *event, void *data); static void engine_cb(Ewl_Widget *w, void *event, void *data); static void theme_cb(Ewl_Widget *w, void *event, void *data); static void combo_cb(Ewl_Widget *w, void *event, void *data); static void del_cb(Ewl_Widget *w, void *event, void *data); static void use_scale_cb(Ewl_Widget *w, void *event, void *data); static Eina_List *theme_data_init(void); static Eina_List *engine_data_init(void); static int theme_data_return_active(Eina_List *data); static int engine_data_return_active(Eina_List *data); static unsigned int model_data_count(void *data); static void *model_data_fetch(void *data, unsigned int row, unsigned int col); static char *theme_cut_suffix(const char *file); static Ewl_Widget *conf_win = NULL; void ecdb_config_dialog_show(void) { Ewl_Widget *o, *l; Ewl_Widget *border_box, *hbox, *main_box; Ewl_Widget *combo; Ewl_Model *model; Ewl_View *view; Eina_List *data; int use_scale; /* Only show one config window */ if (conf_win) return; conf_win = ewl_dialog_new(); ewl_dialog_action_position_set(EWL_DIALOG(conf_win), EWL_POSITION_BOTTOM); ewl_window_title_set(EWL_WINDOW(conf_win), "ECDB Configuration"); ewl_window_name_set(EWL_WINDOW(conf_win), "ECDB"); ewl_window_class_set(EWL_WINDOW(conf_win), "ECDB"); ewl_window_leader_foreign_set(EWL_WINDOW(conf_win), EWL_EMBED_WINDOW(em->xwin)); ewl_callback_append(conf_win, EWL_CALLBACK_DELETE_WINDOW, destroy_cb, NULL); ewl_dialog_has_separator_set(EWL_DIALOG(conf_win), 1); ewl_object_fill_policy_set(EWL_OBJECT(conf_win), EWL_FLAG_FILL_NONE); ewl_widget_show(conf_win); /* the main_box contain the border_boxes */ ewl_dialog_active_area_set(EWL_DIALOG(conf_win), EWL_POSITION_TOP); main_box = ewl_vbox_new(); ewl_container_child_append(EWL_CONTAINER(conf_win), main_box); ewl_object_fill_policy_set(EWL_OBJECT(main_box), EWL_FLAG_FILL_FILL); ewl_widget_show(main_box); /* Setup and show the stock icons */ ewl_dialog_active_area_set(EWL_DIALOG(conf_win), EWL_POSITION_BOTTOM); o = ewl_button_new(); ewl_stock_type_set(EWL_STOCK(o), EWL_STOCK_CANCEL); ewl_container_child_append(EWL_CONTAINER(conf_win), o); ewl_callback_append(o, EWL_CALLBACK_CLICKED, conf_clicked_cb, conf_win); ewl_widget_show(o); o = ewl_button_new(); ewl_stock_type_set(EWL_STOCK(o), EWL_STOCK_APPLY); ewl_container_child_append(EWL_CONTAINER(conf_win), o); ewl_callback_append(o, EWL_CALLBACK_CLICKED, conf_clicked_cb, conf_win); ewl_widget_show(o); o = ewl_button_new(); ewl_stock_type_set(EWL_STOCK(o), EWL_STOCK_OK); ewl_container_child_append(EWL_CONTAINER(conf_win), o); ewl_callback_append(o, EWL_CALLBACK_CLICKED, conf_clicked_cb, conf_win); ewl_widget_show(o); /* Graphics */ border_box = ewl_border_new(); ewl_border_label_set(EWL_BORDER(border_box), "Graphics"); ewl_container_child_append(EWL_CONTAINER(main_box), border_box); ewl_widget_show(border_box); /* Scale */ hbox = ewl_grid_new(); ewl_container_child_append(EWL_CONTAINER(border_box), hbox); ewl_object_fill_policy_set(EWL_OBJECT(hbox), EWL_FLAG_FILL_FILL); ewl_grid_column_preferred_w_use(EWL_GRID(hbox), 1); ewl_widget_show(hbox); o = ewl_label_new(); ewl_label_text_set(EWL_LABEL(o), "Scale Factor:"); ewl_container_child_append(EWL_CONTAINER(hbox), o); ewl_object_fill_policy_set(EWL_OBJECT(o), EWL_FLAG_FILL_NONE); ewl_object_alignment_set(EWL_OBJECT(o), EWL_FLAG_ALIGN_LEFT); ewl_widget_show(o); l = ewl_label_new(); ewl_container_child_append(EWL_CONTAINER(hbox), l); ewl_object_fill_policy_set(EWL_OBJECT(l), EWL_FLAG_FILL_NONE); ewl_widget_show(l); use_scale = ecore_config_boolean_get("use_scale"); o = ewl_hseeker_new(); ewl_range_minimum_value_set(EWL_RANGE(o), 0.0); ewl_range_maximum_value_set(EWL_RANGE(o), 2.0); ewl_range_step_set(EWL_RANGE(o), 0.25); ewl_range_value_set(EWL_RANGE(o), em->scalef); ewl_container_child_append(EWL_CONTAINER(border_box), o); ewl_callback_append(o, EWL_CALLBACK_VALUE_CHANGED, scale_cb, l); if (!use_scale) ewl_widget_disable(o); scale_cb(o, NULL, l); ewl_widget_show(o); // Store this widget as we use it as a parameter later l = o; o = ewl_checkbutton_new(); ewl_button_label_set(EWL_BUTTON(o), "Use scaling?"); ewl_container_child_append(EWL_CONTAINER(border_box), o); ewl_object_alignment_set(EWL_OBJECT(o), EWL_FLAG_ALIGN_LEFT); ewl_togglebutton_checked_set(EWL_TOGGLEBUTTON(o), use_scale); ewl_widget_show(o); ewl_callback_append(o, EWL_CALLBACK_CLICKED, use_scale_cb, l); /* FPS */ hbox = ewl_grid_new(); ewl_container_child_append(EWL_CONTAINER(border_box), hbox); ewl_object_fill_policy_set(EWL_OBJECT(hbox), EWL_FLAG_FILL_ALL); ewl_grid_column_preferred_w_use(EWL_GRID(hbox), 1); ewl_widget_show(hbox); o = ewl_label_new(); ewl_label_text_set(EWL_LABEL(o), "Frame Rate:"); ewl_container_child_append(EWL_CONTAINER(hbox), o); ewl_object_fill_policy_set(EWL_OBJECT(o), EWL_FLAG_FILL_NONE); ewl_object_alignment_set(EWL_OBJECT(o), EWL_FLAG_ALIGN_LEFT); ewl_widget_show(o); l = ewl_label_new(); ewl_container_child_append(EWL_CONTAINER(hbox), l); ewl_object_fill_policy_set(EWL_OBJECT(l), EWL_FLAG_FILL_NONE); ewl_widget_show(l); o = ewl_hseeker_new(); ewl_range_minimum_value_set(EWL_RANGE(o), 10.0); ewl_range_maximum_value_set(EWL_RANGE(o), 100.0); ewl_range_step_set(EWL_RANGE(o), 15.0); ewl_range_value_set(EWL_RANGE(o), em->fps); ewl_container_child_append(EWL_CONTAINER(border_box), o); ewl_callback_append(o, EWL_CALLBACK_VALUE_CHANGED, fps_cb, l); fps_cb(o, NULL, l); ewl_widget_show(o); /* Setup and show the border box */ border_box = ewl_border_new(); ewl_border_label_set(EWL_BORDER(border_box), "Rendering Engine"); ewl_container_child_append(EWL_CONTAINER(main_box), border_box); ewl_object_fill_policy_set(EWL_OBJECT(border_box), EWL_FLAG_FILL_NORMAL); ewl_object_alignment_set(EWL_OBJECT(border_box), EWL_FLAG_ALIGN_CENTER); ewl_object_alignment_set(EWL_OBJECT(main_box), EWL_FLAG_ALIGN_TOP); ewl_widget_show(border_box); model = ewl_model_new(); ewl_model_data_fetch_set(model, model_data_fetch); ewl_model_data_count_set(model, model_data_count); view = ewl_label_view_get(); data = engine_data_init(); combo = ewl_combo_new(); ewl_container_child_append(EWL_CONTAINER(border_box), combo); ewl_callback_append(combo, EWL_CALLBACK_VALUE_CHANGED, engine_cb, NULL); ewl_callback_append(combo, EWL_CALLBACK_DESTROY, combo_cb, data); ewl_callback_append(combo, EWL_CALLBACK_DESTROY, del_cb, view); ewl_callback_append(combo, EWL_CALLBACK_DESTROY, del_cb, model); ewl_mvc_model_set(EWL_MVC(combo), model); ewl_mvc_view_set(EWL_MVC(combo), view); ewl_mvc_data_set(EWL_MVC(combo), data); ewl_mvc_selected_set(EWL_MVC(combo), model, data, engine_data_return_active(data), 0); ewl_widget_show(combo); /* Setup and show the border box */ border_box = ewl_border_new(); ewl_border_label_set(EWL_BORDER(border_box), "Theme"); ewl_container_child_append(EWL_CONTAINER(main_box), border_box); ewl_object_fill_policy_set(EWL_OBJECT(border_box), EWL_FLAG_FILL_FILL); ewl_object_alignment_set(EWL_OBJECT(border_box), EWL_FLAG_ALIGN_CENTER); ewl_object_alignment_set(EWL_OBJECT(main_box), EWL_FLAG_ALIGN_TOP); ewl_widget_show(border_box); data = theme_data_init(); combo = ewl_combo_new(); ewl_container_child_append(EWL_CONTAINER(border_box), combo); ewl_callback_append(combo, EWL_CALLBACK_VALUE_CHANGED, theme_cb, NULL); ewl_callback_append(combo, EWL_CALLBACK_DESTROY, combo_cb, data); ewl_mvc_model_set(EWL_MVC(combo), model); ewl_mvc_view_set(EWL_MVC(combo), view); ewl_mvc_data_set(EWL_MVC(combo), data); ewl_mvc_selected_set(EWL_MVC(combo), model, data, theme_data_return_active(data), 0); ewl_widget_show(combo); } static void destroy_cb(Ewl_Widget *w, void *event __UNUSED__, void *data __UNUSED__) { ewl_widget_destroy(w); conf_win = NULL; } static void conf_clicked_cb(Ewl_Widget *w, void *event __UNUSED__, void *data) { Ewl_Stock_Type response; response = ewl_stock_type_get(EWL_STOCK(w)); if ((response == EWL_STOCK_OK) || (response == EWL_STOCK_APPLY)) { if (ecore_config_save() != ECORE_CONFIG_ERR_SUCC) EINA_ERROR_PWARN("Failure to save configuration!\n"); } if ((response == EWL_STOCK_OK) || (response == EWL_STOCK_CANCEL)) { ewl_widget_destroy(EWL_WIDGET(data)); conf_win = NULL; } } static void scale_cb(Ewl_Widget *w, void *event __UNUSED__, void *data) { double value; char buffer[10]; value = ewl_range_value_get(EWL_RANGE(w)); ecore_config_float_set("scale", value); snprintf(buffer, 10, "%.1f", value); ewl_label_text_set(EWL_LABEL(data), buffer); } static void fps_cb(Ewl_Widget *w, void *event __UNUSED__, void *data) { int value; char buffer[10]; value = ewl_range_value_get(EWL_RANGE(w)); ecore_config_int_set("frame_rate", value); snprintf(buffer, 10, "%d", value); ewl_label_text_set(EWL_LABEL(data), buffer); } static void engine_cb(Ewl_Widget *w, void *event __UNUSED__, void *data) { Eina_List *l; Ewl_Selection_Idx *idx; char *eng; l = ewl_mvc_data_get(EWL_MVC(w)); idx = ewl_mvc_selected_get(EWL_MVC(w)); if (!idx) return; free(em->engine); eng = eina_list_nth(l, idx->row); ecore_config_string_set("engine", eng); em->engine = strdup(eng); free(idx); } static void theme_cb(Ewl_Widget *w, void *event __UNUSED__, void *data) { Eina_List *l; Ewl_Selection_Idx *idx; char *theme; l = ewl_mvc_data_get(EWL_MVC(w)); idx = ewl_mvc_selected_get(EWL_MVC(w)); if (!idx) return; theme = eina_list_nth(l, idx->row); if (strcmp(theme, ecore_config_theme_get("theme"))) { free(em->theme_path); ecore_config_theme_set("theme", theme); em->theme_path = ecore_config_theme_with_path_get("theme"); } free(idx); } static Eina_List * theme_data_init(void) { Eina_List *ret = NULL; char *dirs; char **dirs_array; struct dirent *file; DIR *rep; int i = 0; dirs = ecore_config_theme_search_path_get(); dirs_array = ecore_str_split(dirs, "|", -1); free(dirs); if (!dirs_array) { EINA_ERROR_PWARN("No possible theme paths!\n"); return NULL; } while ((dirs = dirs_array[i])) { rep = opendir(dirs); if (rep) { while ((file = readdir(rep))) { if ((strlen(file->d_name) > 4) && (ecore_str_has_suffix(file->d_name, ".edj"))) ret = eina_list_append(ret, theme_cut_suffix(file->d_name)); } closedir(rep); } i++; } free(*dirs_array); free(dirs_array); if (!ret) { EINA_ERROR_PWARN("No themes available!\n"); return NULL; } return ret; } static int theme_data_return_active(Eina_List *l) { int i; char *theme, *tmp_theme; theme = ecore_config_theme_get("theme"); EINA_LIST_FOREACH(l, l, tmp_theme) { if (!strcmp(tmp_theme, theme)) break; i++; } free(theme); return i; } static Eina_List * engine_data_init(void) { Eina_List *l, *ret = NULL; char *eng; int i; const char *accepted_engines[] = {"software_x11", "xrender_x11", "opengl_x11", NULL}; l = ecore_evas_engines_get(); EINA_LIST_FOREACH(l, l, eng) { for (i = 0; accepted_engines[i]; i++) { if (!strcmp(accepted_engines[i], eng)) { ret = eina_list_append(ret, strdup(eng)); break; } } } ecore_evas_engines_free(l); return ret; } static int engine_data_return_active(Eina_List *l) { int i = 0; char *eng, *tmp_eng; eng = ecore_config_string_get("engine"); EINA_LIST_FOREACH(l, l, tmp_eng) { if (!strcmp(tmp_eng, eng)) break; i++; } free(eng); return i; } static unsigned int model_data_count(void *data) { Eina_List *l = data; return eina_list_count(l); } static void * model_data_fetch(void *data, unsigned int row, unsigned int col __UNUSED__) { Eina_List *l = data; return eina_list_nth(l, row); } static char * theme_cut_suffix(const char *file) { char *new_file, *pt, *end; end = strrchr(file, '.'); new_file = malloc(end - file + 1); pt = new_file; while (file != end) *pt++ = *file++; *pt = '\0'; return new_file; } static void combo_cb(Ewl_Widget *w, void *event __UNUSED__, void *data) { char *str; EINA_LIST_FREE(data, str) free(str); } static void use_scale_cb(Ewl_Widget *w, void *event __UNUSED__, void *data) { int use_scale; use_scale = ewl_togglebutton_checked_get(EWL_TOGGLEBUTTON(w)); ecore_config_boolean_set("use_scale", use_scale); if (use_scale) ewl_widget_enable(EWL_WIDGET(data)); else ewl_widget_disable(EWL_WIDGET(data)); } static void del_cb(Ewl_Widget *w __UNUSED__, void *event __UNUSED__, void *data) { FREE(data); } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_CONFIG_DIALOG_H #define ECDB_CONFIG_DIALOG_H void ecdb_config_dialog_show(void); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" void _ecdb_drive_set_capabilities(Ecdb_Drive_Info *drive, E_Hal_Device_Get_All_Properties_Return *ret) { Eina_List *write_speeds, *l; char *val; int i; /* Write speeds */ write_speeds = e_hal_property_strlist_get(ret, "storage.cdrom.write_speeds", NULL); drive->write_speeds = calloc(eina_list_count(write_speeds) + 1, sizeof(int)); drive->write_speeds[0] = eina_list_count(write_speeds) + 1; i = 1; EINA_LIST_FOREACH(write_speeds, l, val) { drive->write_speeds[i] = atoi(val); i++; } } int ecdb_update_drive_info(Ecdb_Drive_Info *drive, E_Hal_Device_Get_All_Properties_Return *ret) { /* Assume that the physical hardware hasn't changed */ FREE(drive->write_speeds); _ecdb_drive_set_capabilities(drive, ret); return TRUE; } int ecdb_aquire_drive_info(E_Hal_Device_Get_All_Properties_Return *ret, char *mnt, char *udi) { Ecdb_Drive_Info *drive; drive = calloc(1, sizeof(Ecdb_Drive_Info)); if (!drive) { return FALSE; } /* General Info */ drive->vendor = e_hal_property_string_get(ret, "storage.vendor", NULL); drive->product = e_hal_property_string_get(ret, "storage.model", NULL); drive->revision = e_hal_property_string_get(ret, "storage.firmware_revision", NULL); drive->location = mnt; drive->udi = udi; /* Drive Capabilities */ drive->write_cdr = e_hal_property_bool_get(ret, "storage.cdrom.cdr", NULL); drive->write_cdrw = e_hal_property_bool_get(ret, "storage.cdrom.cdrw", NULL); drive->write_dvdr = e_hal_property_bool_get(ret, "storage.cdrom.dvdr", NULL); drive->write_dvdrw = e_hal_property_bool_get(ret, "storage.cdrom.dvdrw", NULL); drive->write_dvdram = e_hal_property_bool_get(ret, "storage.cdrom.dvdram", NULL); drive->write_dvdplusrw = e_hal_property_bool_get(ret, "storage.cdrom.dvdplusrw", NULL); drive->write_dvdplusrwdl = e_hal_property_bool_get(ret, "storage.cdrom.dvdplusrwdl", NULL); drive->write_dvdplusrdl = e_hal_property_bool_get(ret, "storage.cdrom.dvdplusrdl", NULL); drive->write_bdr = e_hal_property_bool_get(ret, "storage.cdrom.bdr", NULL); drive->write_bdre = e_hal_property_bool_get(ret, "storage.cdrom.bdre", NULL); drive->write_hddvdr = e_hal_property_bool_get(ret, "storage.cdrom.hddvdr", NULL); drive->write_hddvdrw = e_hal_property_bool_get(ret, "storage.cdrom.hddvdrw", NULL); drive->support_multisession = e_hal_property_bool_get(ret, "storage.cdrom.support_multisession", NULL); /* Update generic write speeds */ _ecdb_drive_set_capabilities(drive, ret); em->drives = eina_list_append(em->drives, drive); return TRUE; } void ecdb_drive_info_list_free(Eina_List *list) { Ecdb_Drive_Info *info; EINA_LIST_FREE(list, info) { if (!info) continue; FREE(info->write_speeds); FREE(info->vendor); FREE(info->product); FREE(info->revision); FREE(info->location); FREE(info->udi); FREE(info->type); free(info); } } void ecdb_print_drive_info(void) { Eina_List *l; Ecdb_Drive_Info *drive; int j, i; em->drives = eina_list_nth_list(em->drives, 0); EINA_LIST_FOREACH(em->drives, l, drive) { /* Leave these as printfs, they shouldn't go the stderr */ printf("Vendor: %s, Product: %s, Revision: %s, " "Location: %s\n", drive->vendor, drive->product, drive->revision, drive->location); printf("Write CD-R: %d, Write CD-RW: %d\n", drive->write_cdr, drive->write_cdrw); printf("Write DVD-R: %d, Write DVD-RW: %d, Write DVD-RAM: %d\n", drive->write_dvdr, drive->write_dvdrw, drive->write_dvdram); printf("Write DVD+R: %d, Write DVD+RW: %d, Write DVD+RWDL: %d\n", drive->write_dvdplusr, drive->write_dvdplusrw, drive->write_dvdplusrwdl); printf("Write DVD+DL: %d\n", drive->write_dvdplusrdl); printf("Write BR-R: %d, Write BR-RW: %d\n", drive->write_bdr, drive->write_bdre); printf("Write HDDVD-R: %d, Write HDDVD-RW: %d\n", drive->write_hddvdr, drive->write_hddvdrw); printf("Status: %d, Type: %s, Capacity: %llu\n", drive->status, drive->type, drive->capacity); j = drive->write_speeds[0]; for (i = 1; i < j; i ++) { printf("Write: %d\n", drive->write_speeds[i]); } } } int ecdb_aquire_drive(Ecdb_Project *proj) { char adr[BURN_DRIVE_ADR_LEN]; if (burn_drive_convert_fs_adr(proj->drive->location, adr) <= 0) { EINA_ERROR_PWARN("Error: Address doesn't provide cd burner!\n"); return FALSE; } if (burn_drive_scan_and_grab(&proj->drive->tangible, adr, 1) > 0) { return TRUE; } else { proj->drive->tangible = NULL; return FALSE; } } int ecdb_set_project_drive(Ecdb_Project *proj, unsigned int idx) { Ecdb_Drive_Info *info; info = eina_list_nth(em->drives, idx); if (!info) { EINA_ERROR_PERR("Drive index does not exist!\n"); return FALSE; } proj->drive = info; return TRUE; } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_DRIVES_H #define ECDB_DRIVES_H int ecdb_aquire_drive_info(E_Hal_Device_Get_All_Properties_Return *ret, char *mnt, char *udi); int ecdb_update_drive_info(Ecdb_Drive_Info *drive, E_Hal_Device_Get_All_Properties_Return *ret); void ecdb_print_drive_info(void); int ecdb_aquire_drive(Ecdb_Project *proj); int ecdb_set_project_drive(Ecdb_Project *proj, unsigned int idx); void ecdb_drive_info_list_free(Eina_List *list); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" typedef struct Erase_Data Erase_Data; struct Erase_Data { Ecdb_Project *proj; Ecdb_Page *page; }; static void ecdb_erase_progress_handler(void *data, void *buffer, unsigned int nbyte); int ecdb_erase_finished(void *data, int type, void *event); // FIXME Make this return an error code int ecdb_erase_disc(Ecdb_Erase_Project *proj, Ecdb_Page *page) { BurnDriveStatus disc_state; pthread_t progress_update; Erase_Data *data; data = calloc(1, sizeof(Erase_Data)); if (!data) { EINA_ERROR_PWARN("ecdb_erase_disc: NULL data!\n"); return FALSE; } data->proj = ECDB_PROJECT(proj); data->page = page; disc_state = burn_disc_get_status(ECDB_PROJECT(proj)->drive-> tangible[0].drive); if (((disc_state == BURN_DISC_FULL) || (disc_state == BURN_DISC_APPENDABLE)) && (burn_disc_erasable(ECDB_PROJECT(proj)->drive->tangible[0].drive))) { EINA_ERROR_PINFO("Beginning to erase disc!\n"); ECDB_PROJECT(proj)->pipe = ecore_pipe_add(ecdb_erase_progress_handler, data); burn_disc_erase(ECDB_PROJECT(proj)->drive->tangible[0].drive, proj->quick); pthread_create(&progress_update, NULL, ecdb_drive_progress_update, proj); pthread_detach(progress_update); ECDB_PROJECT(proj)->ev_handler = ecore_event_handler_add (ECDB_DRIVE_ACTION_FINISHED, ecdb_erase_finished, data); return TRUE; } else { EINA_ERROR_PINFO("Disc not suitable for erasing\n"); return FALSE; } } static void ecdb_erase_progress_handler(void *data, void *buffer, unsigned int nbyte) { BurnProgress *p; Evas_Object *swallow; Erase_Data *edata; static int last_sector = 0; Edje_Message_Int_Set *progress_msg; EINA_ERROR_PDBG("In progress handler, %d, %d\n", sizeof(BurnProgress), nbyte); if (nbyte != sizeof(BurnProgress)) { ecore_event_add(ECDB_DRIVE_ACTION_FINISHED, NULL, NULL, NULL); EINA_ERROR_PDBG("Adding event to queue.\n"); last_sector = 0; return; } else { p = buffer; } /* Libburn reports p->sector as being 0 right at the end of the job, * so store the last sector and use that to determine the proper * percentage/sector to set */ if (last_sector <= p->sector) { last_sector = p->sector; } else { last_sector = p->sectors; } edata = data; if (!edata) { EINA_ERROR_PWARN("ecdb_erase_progress_handler: NULL edata!\n"); return; } if ((!edata->page) || (!edata->proj)) { EINA_ERROR_PWARN("ecdb_erase_progress_handler: NULL page or proj!\n"); return; } switch (edata->proj->type) { case ECDB_ERASE_PROJECT: swallow = edata->page->erase; break; default: EINA_ERROR_PWARN("ecdb_erase_progress_handler: " "Unrecognized project type!\n"); return; } progress_msg = alloca(sizeof(Edje_Message_Int_Set) + (2 * sizeof(int))); progress_msg->count = 3; progress_msg->val[0] = (int)((double)(last_sector + 1) / (double)p->sectors * 100.0); progress_msg->val[1] = last_sector; progress_msg->val[2] = p->sectors; edje_object_message_send(swallow, EDJE_MESSAGE_INT_SET, 0, progress_msg); } int ecdb_erase_finished(void *data, int type, void *event) { Erase_Data *ed; Ecdb_Erase_Project *proj; ed = data; proj = ECDB_ERASE(ed->proj); burn_drive_release(ECDB_PROJECT(proj)->drive->tangible[0].drive, 0); burn_drive_info_free(ECDB_PROJECT(proj)->drive->tangible); ecore_event_handler_del(ECDB_PROJECT(proj)->ev_handler); ecore_pipe_del(ECDB_PROJECT(proj)->pipe); ecdb_erase_cleanup(ed->page); FREE(ed); return TRUE; } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_ERASE_H #define ECDB_ERASE_H void ecdb_erase_project_start(Ecdb_Erase_Project *proj); int ecdb_erase_disc(Ecdb_Erase_Project *proj, Ecdb_Page *page); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" static void _button_cb_return(void *data, Evas_Object *o, const char *emission, const char *source); static void _button_cb_begin(void *data, Evas_Object *o, const char *emission, const char *source); static void _page_hide_finished(void *data, Evas_Object *o, const char *emission, const char *source); static void _page_del(void *data, Evas *e, Evas_Object *obj, void *ev); static void _ecdb_erase_do_erase(Ecdb_Page *page, Ecdb_Erase_Project *proj); void _ecdb_erase_unmount_cb(void *data, void *reply_data, DBusError *err); static void _page_del(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *ev __UNUSED__) { Ecdb_Erase_Project *proj; proj = evas_object_data_get(obj, "proj"); if (proj) ecdb_erase_project_destroy(proj); } static void _page_hide_finished(void *data, Evas_Object *o, const char *emission __UNUSED__, const char *source __UNUSED__) { Evas_Object *gui; gui = data; edje_object_part_unswallow(gui, o); evas_object_hide(o); } static void _button_cb_return(void *data, Evas_Object *o __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { Ecdb_Page *page; page = data; edje_object_part_text_set(page->erase, "progress_text", "Begin Erase"); edje_object_part_text_set(page->erase, "progress_percent", "0%"); edje_object_signal_emit(page->erase, "ecdb,erase_page,hide", "ecdb"); ecdb_welcome_page_show(page); } static void _button_cb_begin(void *data, Evas_Object *o __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { Ecdb_Erase_Project *proj; Ecdb_Page *page; int idx; page = data; proj = evas_object_data_get(page->erase, "proj"); idx = ecdb_combo_selected_get(ECDB_PROJECT(proj)->drive_combo); if (idx < 0) { EINA_ERROR_PDBG("Choose a drive!\n"); edje_object_part_text_set(page->erase, "progress_text", "Choose a Drive!"); return; } if (!ecdb_set_project_drive(ECDB_PROJECT(proj), idx)) { EINA_ERROR_PWARN("Drive index doesn't exists!\n"); edje_object_part_text_set(page->erase, "progress_text", "Drive doesn't exist!"); return; } if (!ECDB_PROJECT(proj)->drive->fresh_info) { EINA_ERROR_PDBG("Insert a disc into the drive!\n"); edje_object_part_text_set(page->erase, "progress_text", "Insert a disc into the drive!"); return; } if (ECDB_PROJECT(proj)->drive->status & ECDB_DISC_BLANK) { EINA_ERROR_PDBG("Disc is already blank!\n"); edje_object_part_text_set(page->erase, "progress_text", "Disc is already blank!"); return; } /* Apparently DBus doesn't tell you when a disc has been mounted * or unmounted, so do this every time regardless... */ if (!(ECDB_PROJECT(proj)->drive->status & ECDB_DISC_BLANK)) { ecdb_hal_request_unmount(ECDB_PROJECT(proj)->drive, _ecdb_erase_unmount_cb, page); } else { _ecdb_erase_do_erase(page, proj); } } void ecdb_erase_page_show(Ecdb_Page *page) { /* Hide the filelist as it isn't needed to erase a disc */ edje_object_signal_emit(page->gui, "ecdb,filelist,hide", "ecdb"); if (!page->erase) { Evas_Object *d; Evas_Coord x, y, w, h; Eina_List *l; Ecdb_Drive_Info *drive; Ecdb_Erase_Project *proj; Ecdb_Project *bp; page->erase = edje_object_add(ecore_evas_get(em->main_win_ee)); edje_object_file_set(page->erase, em->theme_path, "ecdb/erase_page"); edje_object_part_geometry_get(page->gui, "active_area", &x, &y, &w, &h); evas_object_move(page->erase, x, y); evas_object_resize(page->erase, w, h); edje_object_part_swallow(page->gui, "action_area", page->erase); evas_object_event_callback_add(page->erase, EVAS_CALLBACK_DEL, _page_del, page); evas_object_show(page->erase); proj = ecdb_erase_project_new(); bp = ECDB_PROJECT(proj); evas_object_data_set(page->erase, "proj", proj); edje_object_signal_callback_add(page->erase, "hide,finished", "erase_page", _page_hide_finished, page->gui); bp->ret = ecdb_button_add(page->erase, "ecdb/erase/return"); ecdb_button_label_set(bp->ret, "Return to Main Page"); edje_object_signal_callback_add(page->erase, "ecdb,clicked", "ecdb/erase/return", _button_cb_return, page); evas_object_show(bp->ret); ecdb_button_icon_set(bp->ret, "ecdb/erase/return"); bp->begin = ecdb_button_add(page->erase, "ecdb/erase/begin"); ecdb_button_label_set(bp->begin, "Start Erase"); edje_object_signal_callback_add(page->erase, "ecdb,clicked", "ecdb/erase/begin", _button_cb_begin, page); evas_object_show(bp->begin); ecdb_button_icon_set(bp->begin, "ecdb/erase/begin"); proj->speed = ecdb_check_add(page->erase, "ecdb/erase/speed"); ecdb_check_label_set(proj->speed, "Quick Erase"); evas_object_show(proj->speed); bp->drive_combo = ecdb_combo_add(page->erase, "ecdb/erase/drive"); ecdb_combo_header_set(bp->drive_combo, "Drive"); ecdb_combo_header_create_set(bp->drive_combo, ecdb_gui_combo_header_from_drive); EINA_LIST_FOREACH(em->drives, l, drive) { d = ecdb_combo_item_add(bp->drive_combo, NULL); ecdb_combo_item_label_set(d, drive->product); ecdb_combo_append(bp->drive_combo, d); evas_object_show(d); } } else if (edje_object_part_swallow_get(page->gui, "action_area") != page->erase) { edje_object_part_swallow(page->gui, "action_area", page->erase); evas_object_show(page->erase); } edje_object_signal_emit(page->gui, "ecdb,erase_page,visible", "ecdb"); edje_object_signal_emit(page->erase, "ecdb,erase_page,visible", "ecdb"); } void ecdb_erase_cleanup(Ecdb_Page *page) { Ecdb_Erase_Project *proj; int idx; if (!page) { EINA_ERROR_PWARN("ecdb_erase_cleanup: NULL page!\n"); return; } proj = evas_object_data_get(page->erase, "proj"); if (!proj) { EINA_ERROR_PWARN("ecdb_erase_cleanup: NULL proj!\n"); return; } Evas_Object *objs[] = {proj->speed, ECDB_PROJECT(proj)->ret, ECDB_PROJECT(proj)->begin, ECDB_PROJECT(proj)->drive_combo, NULL}; for (idx = 0; objs[idx] != NULL; idx++) edje_object_signal_emit(objs[idx], "ecdb,disable", "ecdb"); edje_object_part_text_set(page->erase, "progress_text", "Erase Complete!"); edje_object_signal_emit(page->erase, "ecdb,erase,done", "ecdb"); // Set attributes back to normal ecdb_check_checked_set(proj->speed, 0); proj->speed = 0; } static void _ecdb_erase_do_erase(Ecdb_Page *page, Ecdb_Erase_Project *proj) { int idx; if (!proj) { EINA_ERROR_PWARN("NULL project!\n"); return; } if (!page) { EINA_ERROR_PWARN("NULL page!\n"); return; } edje_object_part_text_set(page->erase, "progress_text", "Commencing..."); if (!ecdb_aquire_drive(ECDB_PROJECT(proj))) { EINA_ERROR_PWARN("Couldn't grab the drive!\n"); edje_object_part_text_set(page->erase, "progress_text", "Couldn't grab the drive!"); } proj->quick = ecdb_check_checked_get(proj->speed); if (!ecdb_erase_disc(proj, page)) { EINA_ERROR_PINFO("Disc not erasable!\n"); edje_object_part_text_set(page->erase, "progress_text", "Disc not erasable!"); burn_drive_release(ECDB_PROJECT(proj)->drive->tangible[0].drive, 0); burn_drive_info_free(ECDB_PROJECT(proj)->drive->tangible); return; } edje_object_signal_emit(page->erase, "ecdb,erase,start", "ecdb"); Evas_Object *objs[] = {proj->speed, ECDB_PROJECT(proj)->ret, ECDB_PROJECT(proj)->begin, ECDB_PROJECT(proj)->drive_combo, NULL}; for (idx = 0; objs[idx] != NULL; idx++) edje_object_signal_emit(objs[idx], "ecdb,disable", "ecdb"); } void _ecdb_erase_unmount_cb(void *data, void *reply_data __UNUSED__, DBusError *err) { Ecdb_Erase_Project *proj; Ecdb_Page *page; page = data; if (!page) { EINA_ERROR_PWARN("NULL page!\n"); return; } proj = evas_object_data_get(page->erase, "proj"); if (!proj) { EINA_ERROR_PWARN("NULL project!\n"); return; } if (dbus_error_is_set(err)) { if (!strcmp(err->name, "org.freedesktop.Hal.Device.Volume.NotMounted")) { // This case is totally fine for our uses _ecdb_erase_do_erase(page, proj); } else { edje_object_part_text_set(page->erase, "progress_text", "Can not unmount drive!"); EINA_ERROR_PWARN("Unmount error:\n%s\n%s\n", err->name, err->message); } dbus_error_free(err); } else { _ecdb_erase_do_erase(page, proj); } } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_ERASE_GUI_H #define ECDB_ERASE_GUI_H void ecdb_erase_page_show(Ecdb_Page *page); void ecdb_erase_cleanup(Ecdb_Page *page); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" static void free_file(void *data); static void ecdb_custom_filelist_cb_clicked(Ewl_Widget *w, void *ev, void *data); static void ecdb_custom_filelist_cb_key_down(Ewl_Widget *w, void *ev, void *data); static Ewl_Widget *ecdb_custom_filelist_cb_widget_fetch(unsigned int col, void *pr_data); static void ecdb_custom_filelist_cb_widget_assign(Ewl_Widget *w, void *data, unsigned int row, unsigned int col, void *pr_data); static void *ecdb_custom_filelist_model_data_fetch(void *data, unsigned int row, unsigned int column); static void ecdb_custom_filelist_model_filter(Ewl_Filelist_Directory *dir); static unsigned int ecdb_custom_filelist_model_data_unref(void *data); static Ewl_Filelist_Directory *ecdb_custom_filelist_directory_new (Ecdb_Source *src); static void ecdb_custom_filelist_dnd_dropped_cb(Ewl_Widget *w, void *ev, void *data); Ewl_Widget * ecdb_custom_filelist_new(void) { Ewl_Widget *ret; ret = ewl_filelist_new(); if (!ret) { EINA_ERROR_PWARN("ecdb_custom_filelist_new: NULL return!\n"); return NULL; } /* Ouch, this took forever to find */ ewl_model_data_unref_set(EWL_FILELIST(ret)->model, ecdb_custom_filelist_model_data_unref); ewl_model_data_fetch_set(EWL_FILELIST(ret)->model, ecdb_custom_filelist_model_data_fetch); ewl_view_widget_constructor_set(EWL_FILELIST(ret)->view, ecdb_custom_filelist_cb_widget_fetch); ewl_view_widget_assign_set(EWL_FILELIST(ret)->view, ecdb_custom_filelist_cb_widget_assign); ewl_callback_append(ret, EWL_CALLBACK_DND_DATA_RECEIVED, ecdb_custom_filelist_dnd_dropped_cb, NULL); em->ewl_dnd_candidates = eina_list_append(em->ewl_dnd_candidates, ret); ewl_callback_del_type(EWL_FILELIST(ret)->controller, EWL_CALLBACK_CLICKED); ewl_callback_prepend(EWL_FILELIST(ret)->controller, EWL_CALLBACK_CLICKED, ecdb_custom_filelist_cb_clicked, ret); ewl_callback_append(EWL_FILELIST(ret)->controller, EWL_CALLBACK_KEY_DOWN, ecdb_custom_filelist_cb_key_down, ret); return ret; } static void ecdb_custom_filelist_dnd_dropped_cb(Ewl_Widget *w, void *ev, void *data__UNUSED__) { int i; Ewl_Event_Dnd_Data_Received *dnd = ev; char **files = dnd->data; Ecdb_Source *parent, *child; Efreet_Uri *uri; /* Get the parent, find the path of the file(s) dropped * and add them as children to parent */ parent = ewl_widget_data_get(w, "src"); for (i = 0; i < dnd->len; i++) { uri = efreet_uri_decode(files[i]); if (!ecore_file_exists(uri->path)) { efreet_uri_free(uri); continue; } child = ecdb_source_new(); ecdb_source_data_set(child, uri->path); ecdb_source_child_append(parent, child); efreet_uri_free(uri); } ecdb_custom_filelist_directory_set(EWL_FILELIST(w), parent); /* This entire thing here is a hack... */ ewl_filelist_model_data_sort(ewl_mvc_data_get (EWL_MVC(EWL_FILELIST(w)->controller)), 0, EWL_SORT_DIRECTION_ASCENDING); ewl_mvc_dirty_set(EWL_MVC(EWL_FILELIST(w)->controller), TRUE); ewl_widget_configure(EWL_FILELIST(w)->controller); /* End hack stuff */ // Send out this event so that we can get notified on size changes // and update the capacity measure ewl_callback_call_with_event_data(w, ECDB_FILELIST_SIZE_CHANGED, parent); } void ecdb_custom_filelist_directory_set(Ewl_Filelist *fl, Ecdb_Source *src) { if (src) { Ewl_Filelist_Directory *data; Ewl_Event_Action_Response ev_data; data = ewl_mvc_data_get(EWL_MVC(fl->controller)); if (data) { ecdb_custom_filelist_model_data_unref(data); } data = ecdb_custom_filelist_directory_new(src); ewl_mvc_data_set(EWL_MVC(fl->controller), data); ev_data.response = EWL_FILELIST_EVENT_DIR_CHANGE; ewl_callback_call_with_event_data(EWL_WIDGET(fl), EWL_CALLBACK_VALUE_CHANGED, &ev_data); /* Set the source as needed for file operations */ ewl_widget_data_set(EWL_WIDGET(fl), "src", src); } } static Ewl_Filelist_Directory * ecdb_custom_filelist_directory_new(Ecdb_Source *parent) { Ecdb_Source *src; Ewl_Filelist_Directory *dir; Ewl_Filelist_File *file; struct stat st; int nf = 0, nd = 0, i = 0; Ecore_List *files, *dirs; files = ecore_list_new(); dirs = ecore_list_new(); ecore_list_free_cb_set(files, free_file); ecore_list_free_cb_set(dirs, free_file); if (!parent) { return NULL; } while ((src = parent->children[i])) { file = calloc(1, sizeof(Ewl_Filelist_File)); file->name = eina_stringshare_add(src->dst); stat(src->dst, &st); file->size = st.st_size; file->modtime = st.st_mtime; file->mode = st.st_mode; file->groupname = st.st_gid; file->username = st.st_uid; file->is_dir = src->dir; file->readable = ecore_file_can_read(src->dst); file->writeable = ecore_file_can_write(src->dst); if (src->dir) { ecore_list_append(dirs, file); nd++; } else { ecore_list_append(files, file); nf++; } i++; } dir = calloc(1, sizeof(Ewl_Filelist_Directory)); dir->rfiles = files; dir->rdirs = dirs; dir->files = ecore_list_new(); dir->dirs = ecore_list_new(); dir->num_dirs = nd; dir->num_files = nf; ecdb_custom_filelist_model_filter(dir); return dir; } static void free_file(void *data) { Ewl_Filelist_File *file; file = data; eina_stringshare_del(file->name); FREE(file); } static void ecdb_custom_filelist_model_filter(Ewl_Filelist_Directory *dir) { Ewl_Filelist_File *file; int nd, nf; if (!dir->files || !dir->dirs) return; ecore_list_clear(dir->files); ecore_list_clear(dir->dirs); nd = nf = 0; if (!dir->show_dot) { ecore_list_first_goto(dir->rfiles); while ((file = ecore_list_next(dir->rfiles))) { if ((file->name) && (ecore_file_file_get(file->name)[0] != '.')) { ecore_list_append(dir->files, file); nf++; } } ecore_list_first_goto(dir->rdirs); while ((file = ecore_list_next(dir->rdirs))) { if ((file->name) && (ecore_file_file_get(file->name)[0] != '.')) { ecore_list_append(dir->dirs, file); nd++; } } } else { ecore_list_first_goto(dir->rfiles); while ((file = ecore_list_next(dir->rfiles))) { ecore_list_append(dir->files, file); nf++; } ecore_list_first_goto(dir->rdirs); while ((file = ecore_list_next(dir->rdirs))) { ecore_list_append(dir->dirs, file); nd++; } } dir->num_dirs = nd; dir->num_files = nf; } static unsigned int ecdb_custom_filelist_model_data_unref(void *data) { Ewl_Filelist_Directory *dir; dir = data; ecore_list_destroy(dir->files); ecore_list_destroy(dir->dirs); ecore_list_destroy(dir->rfiles); ecore_list_destroy(dir->rdirs); FREE(dir); return TRUE; } static void * ecdb_custom_filelist_model_data_fetch(void *data, unsigned int row, unsigned int column) { Ewl_Filelist_Directory *fld; Ewl_Filelist_File *file; int i; void *ret; fld = data; /* Check if in dirs or files list */ if (row < fld->num_dirs) { file = ecore_list_index_goto(fld->dirs, row); } else { i = row - fld->num_dirs; file = ecore_list_index_goto(fld->files, i); } if (column == 1) ret = ewl_filelist_size_get(file->size); else if (column == 2) ret = ewl_filelist_perms_get(file->mode); else if (column == 3) ret = ewl_filelist_username_get(file->username); else if (column == 4) ret = ewl_filelist_groupname_get(file->groupname); else if (column == 5) ret = ewl_filelist_modtime_get(file->modtime); else ret = strdup(file->name); /* ret needs to be freed by the view or with model_data_free_set */ return ret; } static void ecdb_custom_filelist_cb_key_down(Ewl_Widget *w, void *ev, void *data) { Ewl_Event_Key_Down *kd; Ewl_Filelist *fl; char *file; int i; Ecdb_Source *parent, *child; kd = ev; fl = data; if (!ewl_mvc_selected_count_get(EWL_MVC(w))) { return; } if (!strcmp(kd->base.keyname, "Delete")) { file = ecdb_custom_filelist_selected_file_get(fl); parent = ewl_widget_data_get(EWL_WIDGET(fl), "src"); for (i = 0; (child = parent->children[i]); i++) { if (!strcmp(child->dst, file)) { ecdb_source_child_remove(parent, child); ecdb_source_destroy(child); ecdb_custom_filelist_directory_set(fl, parent); ewl_callback_call_with_event_data(EWL_WIDGET(fl), ECDB_FILELIST_SIZE_CHANGED, parent); break; } } } } static void ecdb_custom_filelist_cb_clicked(Ewl_Widget *w, void *ev, void *data) { Ewl_Event_Mouse_Down *md; char *file; int i = 0; Ewl_Filelist *fl; Ecdb_Source *parent, *child; md = ev; fl = data; if ((!ewl_mvc_selected_count_get(EWL_MVC(w))) || (md->clicks != 2)) { ewl_filelist_selected_files_change_notify(fl); return; } file = ecdb_custom_filelist_selected_file_get(fl); parent = ewl_widget_data_get(EWL_WIDGET(fl), "src"); i = 0; while ((child = parent->children[i])) { if ((child->dir) && (!strcmp(child->dst, file))) { ecdb_custom_filelist_directory_set(fl, child); FREE(file); return; } i++; } ewl_filelist_selected_files_change_notify(fl); FREE(file); } char * ecdb_custom_filelist_selected_file_get(Ewl_Filelist *fl) { Ewl_Filelist_Directory *data; Ewl_Filelist_File *file; Ewl_Selection_Idx *idx; int i; idx = ewl_mvc_selected_get(EWL_MVC(fl->controller)); data = EWL_SELECTION(idx)->data; if (idx->row < data->num_dirs) { file = ecore_list_index_goto(data->dirs, idx->row); } else { i = (idx->row - data->num_dirs); file = ecore_list_index_goto(data->files, i); } FREE(idx); return strdup(file->name); } static Ewl_Widget *ecdb_custom_filelist_cb_widget_fetch( unsigned int col __UNUSED__, void *pr_data __UNUSED__) { Ewl_Widget *ret; ret = ewl_icon_simple_new(); ewl_icon_constrain_set(EWL_ICON(ret), EWL_ICON_SIZE_MEDIUM); ewl_box_orientation_set(EWL_BOX(ret), EWL_ORIENTATION_HORIZONTAL); ewl_object_alignment_set(EWL_OBJECT(ret), EWL_FLAG_ALIGN_LEFT); return ret; } static void ecdb_custom_filelist_cb_widget_assign(Ewl_Widget *w, void *data, unsigned int row __UNUSED__, unsigned int col, void *pr_data) { const char *img = NULL, *stock, *filename; if (col == 0) { stock = ewl_filelist_stock_icon_get(data); img = ewl_icon_theme_icon_path_get(stock, EWL_ICON_SIZE_MEDIUM); if (img) { ewl_icon_image_set(EWL_ICON(w), img, NULL); } filename = ecore_file_file_get(data); ewl_icon_label_set(EWL_ICON(w), filename); } else { ewl_icon_label_set(EWL_ICON(w), data); } FREE(data); } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_FILELIST_CUSTOM_H #define ECDB_FILELIST_CUSTOM_H void ecdb_custom_filelist_directory_set(Ewl_Filelist *fl, Ecdb_Source *src); Ewl_Widget *ecdb_custom_filelist_new(void); char * ecdb_custom_filelist_selected_file_get(Ewl_Filelist *fl); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" typedef void (*edje_cb_type)(void *, Evas_Object *, const char *, const char *); static void ecdb_cb_burn_data_clicked(void *data, Evas_Object *o, const char *emission, const char *source); static void ecdb_cb_burn_audio_clicked(void *data, Evas_Object *o, const char *emission, const char *source); static void ecdb_cb_burn_image_clicked(void *data, Evas_Object *o, const char *emission, const char *source); static void ecdb_cb_erase_disc_clicked(void *data, Evas_Object *o, const char *emission, const char *source); static void ecdb_handle_typebuf(Evas_Object *gui, Ewl_Widget *fl); static void ecdb_filelist_show(Ecdb_Page *page); static void ecdb_welcome_page_hide_finished(void *data, Evas_Object *o, const char *emission, const char *source); static void _page_del(void *data, Evas *e, Evas_Object *obj, void *ev); static void _filelist_del(Ewl_Widget *w, void *ev_data, void *data); static void ecdb_welcome_page_show_about(void *data, Evas_Object *o, const char *emission, const char *source); static void ecdb_welcome_page_show_config(void *data, Evas_Object *o, const char *emission, const char *source); static void ecdb_welcome_page_show_about(void *data __UNUSED__, Evas_Object *o __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { ecdb_about_show(); } static void ecdb_welcome_page_show_config(void *data __UNUSED__, Evas_Object *o __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { ecdb_config_dialog_show(); } static void _filelist_del(Ewl_Widget *w, void *ev_data __UNUSED__, void *data __UNUSED__) { Ewl_Filelist_Filter *f; f = ewl_filelist_filter_get(EWL_FILELIST(w)); if (f) { if (f->extension) free(f->extension); free(f); } } static void _page_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *ev __UNUSED__) { Ecdb_Page *page; page = data; if (!page) return; free(page); } /* XXX These need to be fixed when Ecdb_Page become the swallow, * whereas it is the actual gui as well */ static void ecdb_cb_enter(Ecore_Evas *ee) { edje_object_signal_emit(em->page->gui, "ecdb,window,enter", "ecdb"); } static void ecdb_cb_leave(Ecore_Evas *ee) { edje_object_signal_emit(em->page->gui, "ecdb,window,exit", "ecdb"); } static void ecdb_cb_resize(Ecore_Evas *ee) { int w, h; ecore_evas_geometry_get(ee, NULL, NULL, &w, &h); evas_object_resize(em->page->gui, w, h); } static void _cb_filelist_mouse_down(void *data, Evas *e __UNUSED__, Evas_Object *eo, void *ev_data __UNUSED__) { Ecdb_Page *page = data; evas_object_focus_set(eo, 1); edje_object_signal_emit(page->gui, "ecdb,filelist_overlay,deactivate", "ecdb"); edje_object_part_text_set(page->gui, "filelist_overlay_text", NULL); } static void _cb_filelist_key_down(void *data, Evas *e __UNUSED__, Evas_Object *eo __UNUSED__, void *ev_data) { Evas_Event_Key_Down *ek = ev_data; Ecdb_Page *page = data; if (!strcmp(ek->key, "Escape")) { edje_object_signal_emit(page->gui, "ecdb,filelist_overlay,deactivate", "ecdb"); edje_object_part_text_set(page->gui, "filelist_overlay_text", NULL); } else if (!strcmp(ek->key, "Return")) { ecdb_handle_typebuf(page->gui, evas_object_data_get(page->filelist, "filelist")); } else if (!strcmp(ek->key, "BackSpace")) { int len; const char *t; char *text; t = edje_object_part_text_get(page->gui, "filelist_overlay_text"); text = (t) ? (strdup(t)) : NULL; if ((!text) || (!*text)) return; len = strlen(text); text = realloc(text, len); text[len - 1] = '\0'; if (len == 1) { edje_object_signal_emit(page->gui, "ecdb,filelist_overlay,deactivate", "ecdb"); } edje_object_part_text_set(page->gui, "filelist_overlay_text", text); FREE(text); } else { int len; const char *t, *append; char *text; t = edje_object_part_text_get(page->gui, "filelist_overlay_text"); text = (t) ? (strdup(t)) : NULL; if ((text) && (strlen(text) >= PATH_MAX)) return; if ((ek->string) && (*ek->string) && (!ek->string[1])) { append = ek->string; } else if ((ek->keyname) && (*ek->keyname) && (!ek->key[1])) { append = ek->keyname; } else { if (text) free(text); return; } if (!text) { text = strdup(append); } else { len = strlen(text) + 2; text = realloc(text, len); strncat(text, append, len); } /* Avoid sending the same signal twice... seems to skip the * transition in that case */ if (strlen(text) == 1) { edje_object_signal_emit(page->gui, "ecdb,filelist_overlay,activate", "ecdb"); } edje_object_part_text_set(page->gui, "filelist_overlay_text", text); FREE(text); } } int ecdb_create_main_gui(void) { Evas_Coord mw, mh; if (!strcmp(em->engine, "opengl_x11")) { em->main_win_ee = ecore_evas_gl_x11_new(0, 0, 0, 0, 255, 255); em->xwin = ecore_evas_gl_x11_window_get(em->main_win_ee); } else if (!strcmp(em->engine, "xrender_x11")) { em->main_win_ee = ecore_evas_xrender_x11_new(0, 0, 0, 0, 255, 255); em->xwin = ecore_evas_xrender_x11_window_get(em->main_win_ee); } else if (!strcmp(em->engine, "software_x11")) { em->main_win_ee = ecore_evas_software_x11_new(0, 0, 0, 0, 255, 255); em->xwin = ecore_evas_software_x11_window_get(em->main_win_ee); } if (!em->main_win_ee) { EINA_ERROR_PWARN("Cannot create main window!\n"); return FALSE; } ecore_evas_title_set(em->main_win_ee, "ECDB"); ecore_evas_name_class_set(em->main_win_ee, "ECDB", "ECDB"); ecore_evas_avoid_damage_set(em->main_win_ee, 1); ecore_x_dnd_aware_set(em->xwin, 1); ecore_x_dnd_type_set(em->xwin, "*", 1); ecore_evas_callback_delete_request_set(em->main_win_ee, ecdb_shutdown); ecore_evas_callback_destroy_set(em->main_win_ee, ecdb_shutdown); ecore_evas_callback_mouse_in_set(em->main_win_ee, ecdb_cb_enter); ecore_evas_callback_mouse_out_set(em->main_win_ee, ecdb_cb_leave); ecore_evas_callback_resize_set(em->main_win_ee, ecdb_cb_resize); edje_frametime_set(1.0 / (double)em->fps); ecore_evas_show(em->main_win_ee); if (ecore_config_boolean_get("use_scale")) edje_scale_set(em->scalef); em->page = calloc(1, sizeof(Ecdb_Page)); em->page->gui = edje_object_add(ecore_evas_get(em->main_win_ee)); edje_object_file_set(em->page->gui, em->theme_path, "ecdb/window"); edje_object_signal_callback_add(em->page->gui, "ecdb/about/show", "ecdb", ecdb_welcome_page_show_about, NULL); edje_object_signal_callback_add(em->page->gui, "ecdb/config/show", "ecdb", ecdb_welcome_page_show_config, NULL); edje_object_size_min_get(em->page->gui, &mw, &mh); if (mw <= 0) mw = 400; if (mh <= 0) mh = 300; if (ecore_config_boolean_get("use_scale")) { mw = (int)((float)mw * em->scalef); mh = (int)((float)mh * em->scalef); } evas_object_resize(em->page->gui, mw, mh); ecore_evas_resize(em->main_win_ee, mw, mh); ecore_evas_size_min_set(em->main_win_ee, mw, mh); evas_object_event_callback_add(em->page->gui, EVAS_CALLBACK_DEL, _page_del, em->page); evas_object_move(em->page->gui, 0, 0); evas_object_show(em->page->gui); ecdb_welcome_page_show(em->page); return TRUE; } void ecdb_handle_typebuf(Evas_Object *gui, Ewl_Widget *fl) { Ewl_Filelist_Filter *filter; const char *ext; if (!(ext = edje_object_part_text_get(gui, "filelist_overlay_text"))) { return; } /* Check for a 'cd' first */ if (!ecdb_match_keyword(ext, "cd", 2)) { char *dir; /* What to do about multi-word directories? */ dir = ecdb_strip_string(ext); if (dir) { if (dir[0] == '/') { if (ecore_file_exists(dir)) { ewl_filelist_directory_set(EWL_FILELIST(fl), dir); } } else if (dir[0] == '~') { char path[PATH_MAX]; snprintf(path, PATH_MAX, "%s%s", getenv("HOME"), &dir[1]); if (ecore_file_exists(path)) { ewl_filelist_directory_set(EWL_FILELIST(fl), path); } } else { /* Try to concate it to our current directory */ char path[PATH_MAX]; snprintf(path, PATH_MAX, "%s/%s", ewl_filelist_directory_get (EWL_FILELIST(fl)), dir); if (ecore_file_exists(path)) { ewl_filelist_directory_set(EWL_FILELIST(fl), path); } } FREE(dir); } edje_object_signal_emit(gui, "ecdb,filelist_overlay,deactivate", "ecdb"); edje_object_part_text_set(gui, "filelist_overlay_text", NULL); return; } else if (!ecdb_match_keyword(ext, "exit", 4)) { /* This creates an ewl error... */ ecore_event_add(ECORE_EVENT_SIGNAL_EXIT, NULL, NULL, NULL); return; } filter = ewl_filelist_filter_get(EWL_FILELIST(fl)); if (!filter) { filter = calloc(sizeof(Ewl_Filelist_Filter), 1); } else { if (filter->extension) { if (strcmp(filter->extension, ext)) { FREE(filter->extension); } else { return; } } } filter->extension = strdup(ext); ewl_filelist_filter_set(EWL_FILELIST(fl), filter); ewl_filelist_refresh(EWL_FILELIST(fl)); edje_object_signal_emit(gui, "ecdb,filelist_overlay,deactivate", "ecdb"); edje_object_part_text_set(gui, "filelist_overlay_text", NULL); } void ecdb_welcome_page_show(Ecdb_Page *page) { if (!page->welcome) { Evas_Object *b; Evas_Coord x, y, w, h; page->welcome = edje_object_add(ecore_evas_get(em->main_win_ee)); edje_object_file_set(page->welcome, em->theme_path, "ecdb/welcome_page"); edje_object_part_geometry_get(page->gui, "active_area", &x, &y, &w, &h); evas_object_move(page->welcome, x, y); evas_object_resize(page->welcome, w, h); edje_object_part_swallow(page->gui, "action_area", page->welcome); edje_object_signal_callback_add(page->welcome, "hide,finished", "welcome_page", ecdb_welcome_page_hide_finished, page->gui); evas_object_show(page->welcome); int i; edje_cb_type cbs[] = { ecdb_cb_burn_data_clicked, ecdb_cb_burn_audio_clicked, ecdb_cb_burn_image_clicked, ecdb_cb_erase_disc_clicked }; char *labels[] = { "Burn Data CD", "Burn Audio CD", "Burn Image", "Erase Re-writable Disc" }; char *ids[] = { "ecdb/burn_data", "ecdb/burn_audio", "ecdb/burn_image", "ecdb/erase" }; for (i = 0; i < 4; i++) { b = ecdb_button_add(page->welcome, ids[i]); ecdb_button_label_set(b, labels[i]); evas_object_show(b); edje_object_signal_callback_add(b, "ecdb,clicked", "ecdb", cbs[i], page); ecdb_button_icon_set(b, ids[i]); } } else if (edje_object_part_swallow_get(page->gui, "action_area") != page->welcome) { /* All that is needed here? */ edje_object_part_swallow(page->gui, "action_area", page->welcome); evas_object_show(page->welcome); } edje_object_signal_emit(page->gui, "ecdb,welcome_page,show", "ecdb"); edje_object_signal_emit(page->welcome, "ecdb,welcome_page,show", "ecdb"); } static void ecdb_filelist_show(Ecdb_Page *page) { if (!page->filelist) { Evas_Coord x, y, w, h; Ewl_Widget *embed, *filelist; embed = ewl_embed_new(); ewl_object_fill_policy_set(EWL_OBJECT(embed), EWL_FLAG_FILL_ALL); page->filelist = ewl_embed_canvas_set(EWL_EMBED(embed), ecore_evas_get(em->main_win_ee), (void *)(long)ecore_evas_software_x11_window_get(em->main_win_ee)); ewl_embed_focus_set(EWL_EMBED(embed), TRUE); ewl_widget_show(embed); filelist = ewl_filelist_new(); ewl_container_child_append(EWL_CONTAINER(embed), filelist); ewl_callback_prepend(EWL_WIDGET(filelist), EWL_CALLBACK_DESTROY, _filelist_del, NULL); ewl_filelist_directory_set(EWL_FILELIST(filelist), getenv("HOME")); ewl_widget_show(filelist); edje_object_part_geometry_get(page->gui, "filelist", &x, &y, &w, &h); evas_object_move(page->filelist, x, y); evas_object_resize(page->filelist, w, h); edje_object_part_swallow(page->gui, "filelist", page->filelist); evas_object_event_callback_add(page->filelist, EVAS_CALLBACK_MOUSE_DOWN, _cb_filelist_mouse_down, page); evas_object_event_callback_add(page->filelist, EVAS_CALLBACK_KEY_DOWN, _cb_filelist_key_down, page); evas_object_data_set(page->filelist, "filelist", filelist); evas_object_show(page->filelist); } /* Assume here that it is the active projects job to hide itself and * call this show function */ else if (edje_object_part_swallow_get(page->gui, "filelist") != page->filelist) { /* Is this all that is neccessary? */ edje_object_part_swallow(page->gui, "filelist", page->filelist); evas_object_show(page->filelist); } edje_object_signal_emit(page->gui, "ecdb,filelist,visible", "ecdb"); edje_object_signal_emit(page->filelist, "ecdb,filelist,visible", "ecdb"); } /* Hurrah! Fancyness */ static void ecdb_welcome_page_hide_finished(void *data, Evas_Object *o, const char *emission __UNUSED__, const char *source __UNUSED__) { Evas_Object *gui; gui = data; edje_object_part_unswallow(gui, o); evas_object_hide(o); } void ecdb_gui_combo_header_from_drive(Evas_Object *c, const char *name, void *data, Evas_Object *obj, int sel) { Evas_Object *ret; Ecdb_Drive_Info *drive; ret = ecdb_label_add(c, name); drive = eina_list_nth(em->drives, sel); ecdb_label_text_set(ret, drive->product); evas_object_show(ret); } void ecdb_gui_combo_header_from_speeds(Evas_Object *c, const char *name, void *data, Evas_Object *obj, int sel) { Evas_Object *ret; Ecdb_Drive_Info *drive; char buf[1024]; drive = data; if (!drive) return; ret = ecdb_label_add(c, name); snprintf(buf, sizeof(buf), "%d", ecdb_speed_convert(drive, sel)); ecdb_label_text_set(ret, buf); evas_object_show(ret); } static void ecdb_cb_burn_data_clicked(void *data, Evas_Object *o, const char *emission __UNUSED__, const char *source __UNUSED__) { Ecdb_Page *page = data; edje_object_signal_emit(page->welcome, "ecdb,welcome_page,hide", "ecdb"); ecdb_burn_data_page_show(page); ecdb_filelist_show(page); } static void ecdb_cb_burn_audio_clicked(void *data, Evas_Object *o, const char *emission __UNUSED__, const char *source __UNUSED__) { Ecdb_Page *page = data; edje_object_signal_emit(page->welcome, "ecdb,filelist,hide", "ecdb"); EINA_ERROR_PDBG("Burn Audio Disc\n"); } static void ecdb_cb_burn_image_clicked(void *data, Evas_Object *o, const char *emission __UNUSED__, const char *source __UNUSED__) { Ecdb_Page *page = data; ecdb_filelist_show(page); edje_object_signal_emit(page->welcome, "ecdb,welcome_page,hide", "ecdb"); ecdb_burn_image_page_show(page); } static void ecdb_cb_erase_disc_clicked(void *data, Evas_Object *o, const char *emission __UNUSED__, const char *source __UNUSED__) { Ecdb_Page *page = data; edje_object_signal_emit(page->welcome, "ecdb,welcome_page,hide", "ecdb"); ecdb_erase_page_show(page); } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_GUI_H #define ECDB_GUI_H int ecdb_create_main_gui(void); void ecdb_set_main_theme(const char *theme_name); void ecdb_welcome_page_show(Ecdb_Page *page); void ecdb_gui_combo_header_from_drive(Evas_Object *c, const char *name, void *data, Evas_Object *obj, int sel); void ecdb_gui_combo_header_from_speeds(Evas_Object *c, const char *name, void *data, Evas_Object *obj, int sel); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" void _ecdb_hal_device_all_cb(void *data, void *reply_data, DBusError *error); void _ecdb_hal_volume_all_cb(void *data, void *reply_data, DBusError *error); void _ecdb_hal_device_handle(void *data, void *reply_data, DBusError *error); void _ecdb_hal_volume_removed_cb(void *data, DBusMessage *msg); void _ecdb_hal_volume_added_cb(void *data, DBusMessage *msg); void _ecdb_hal_volume_prop_modified(void *data, DBusMessage *msg); void _ecdb_hal_volume_cb(void *data, void *reply_data, DBusError *error); void _ecdb_hal_volume_handle(void *data, void *reply_data, DBusError *error); void _ecdb_hal_unmount_cb(void *data, void *reply_data, DBusError *error); static void _ecdb_hal_volumes_init(void); void _ecdb_hal_device_handle(void *data, void *reply_data, DBusError *error) { E_Hal_Device_Get_All_Properties_Return *ret; Eina_List *l; Ecdb_Drive_Info *drive; char *udi, *str; int err = 0; ret = reply_data; udi = data; if ((!ret) || (!udi)) { EINA_ERROR_PWARN("NULL udi or reply!\n"); return; } if (dbus_error_is_set(error)) { EINA_ERROR_PWARN("DBus error: %s %s\n", error->name, error->message); dbus_error_free(error); return; } str = e_hal_property_string_get(ret, "block.device", &err); if (!str) return; if (!err) { EINA_LIST_FOREACH(em->drives, l, drive) { if (!strcmp(drive->location, str)) { if (!ecdb_update_drive_info(drive, ret)) { EINA_ERROR_PWARN("Couldn't update old drive info!\n"); } ecdb_print_drive_info(); return; } } /* * Create the drive information here, as we will have already * returned if it previously exists */ if (!ecdb_aquire_drive_info(ret, str, udi)) { EINA_ERROR_PWARN("Couldn't create new drive info!\n"); return; } ecdb_print_drive_info(); /* Check to see if we have acquired all of the drives */ if (eina_list_count(em->drives) >= em->init_count) { em->init_count = 0; _ecdb_hal_volumes_init(); } } } void _ecdb_hal_volume_removed_cb(void *data __UNUSED__, DBusMessage *msg) { DBusError err; Eina_List *l; Ecdb_Drive_Info *drive; char *udi; dbus_error_init(&err); dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &udi, DBUS_TYPE_INVALID); EINA_LIST_FOREACH(em->drives, l, drive) { if (!strcmp(drive->disc_udi, udi)) { drive->fresh_info = FALSE; drive->status = 0; FREE(drive->type); } } } void _ecdb_hal_volume_added_cb(void *data __UNUSED__, DBusMessage *msg) { DBusError err; char *udi; dbus_error_init(&err); dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &udi, DBUS_TYPE_INVALID); /* XXX Do something with the error above? */ e_hal_device_query_capability(em->conn, udi, "volume", _ecdb_hal_volume_cb, (void *)strdup(udi)); } void _ecdb_hal_volume_cb(void *data, void *reply_data, DBusError *error) { char *udi; E_Hal_Device_Query_Capability_Return *ret; udi = data; ret = reply_data; if (!ret) { EINA_ERROR_PWARN("No DBus data!\n"); return; } if (dbus_error_is_set(error)) { EINA_ERROR_PWARN("DBus error: %s %s\n", error->name, error->message); dbus_error_free(error); return; } if (ret->boolean) { e_hal_device_get_all_properties(em->conn, udi, _ecdb_hal_volume_handle, udi); } } void _ecdb_hal_device_all_cb(void *data __UNUSED__, void *reply_data, DBusError *error) { char *udi; Eina_List *l; E_Hal_Manager_Find_Device_By_Capability_Return *ret; ret = reply_data; if ((!ret) || (!ret->strings)) { EINA_ERROR_PWARN("No DBus data!\n"); return; } if (dbus_error_is_set(error)) { EINA_ERROR_PWARN("DBus error: %s %s\n", error->name, error->message); dbus_error_free(error); return; } em->init_count = eina_list_count(ret->strings); EINA_LIST_FOREACH(ret->strings, l, udi) { e_hal_device_get_all_properties(em->conn, udi, _ecdb_hal_device_handle, (void *)strdup(udi)); } } int ecdb_hal_init(void) { em->conn = e_dbus_bus_get(DBUS_BUS_SYSTEM); if (!em->conn) { EINA_ERROR_PWARN("Error connecting to system bus!\n"); return 0; } e_hal_manager_find_device_by_capability(em->conn, "storage.cdrom", _ecdb_hal_device_all_cb, NULL); return 1; } void ecdb_hal_shutdown(void) { e_dbus_signal_handler_del(em->conn, em->dev_added); e_dbus_signal_handler_del(em->conn, em->dev_removed); e_dbus_connection_close(em->conn); } void ecdb_hal_request_unmount(Ecdb_Drive_Info *drive, E_DBus_Callback_Func cb, void *data) { e_hal_device_volume_unmount(em->conn, drive->disc_udi, NULL, cb, data); } static void _ecdb_hal_volumes_init() { em->dev_added = e_dbus_signal_handler_add(em->conn, "org.freedesktop.Hal", "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager", "DeviceAdded", _ecdb_hal_volume_added_cb, NULL); em->dev_removed = e_dbus_signal_handler_add(em->conn, "org.freedesktop.Hal", "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager", "DeviceRemoved", _ecdb_hal_volume_removed_cb, NULL); e_hal_manager_find_device_by_capability(em->conn, "volume.disc", _ecdb_hal_volume_all_cb, NULL); } void _ecdb_hal_volume_all_cb(void *data __UNUSED__, void *reply_data, DBusError *error) { char *udi; Eina_List *l; E_Hal_Manager_Find_Device_By_Capability_Return *ret; ret = reply_data; if ((!ret) || (!ret->strings)) { EINA_ERROR_PWARN("No DBus data!\n"); return; } if (dbus_error_is_set(error)) { EINA_ERROR_PWARN("DBus error: %s %s\n", error->name, error->message); dbus_error_free(error); return; } EINA_LIST_FOREACH(ret->strings, l, udi) { e_hal_device_get_all_properties(em->conn, udi, _ecdb_hal_volume_handle, (void *)strdup(udi)); } } void _ecdb_hal_volume_handle(void *data, void *reply_data, DBusError *error) { E_Hal_Device_Get_All_Properties_Return *ret; Ecdb_Drive_Info *drive; Eina_List *l; char *udi, *disc_udi; int err; ret = reply_data; disc_udi = data; if ((!ret) || (!disc_udi)) { EINA_ERROR_PWARN("No DBus data or volume data!\n"); return; } if (dbus_error_is_set(error)) { EINA_ERROR_PWARN("DBus error: %s %s\n", error->name, error->message); dbus_error_free(error); return; } /* Ignore volumes that are not discs */ if ((!e_hal_property_bool_get(ret, "volume.is_disc", &err)) || (err)) return; /* Get the parent udi */ udi = e_hal_property_string_get(ret, "info.parent", NULL); /* Find the drive this volume belongs to */ EINA_LIST_FOREACH(em->drives, l, drive) { if (!strcmp(udi, drive->udi)) { drive->fresh_info = TRUE; drive->disc_udi = disc_udi; drive->capacity = e_hal_property_uint64_get(ret, "volume.disc.capacity", NULL); if (e_hal_property_bool_get(ret, "volume.disc.is_blank", NULL)) drive->status |= ECDB_DISC_BLANK; if (e_hal_property_bool_get(ret, "volume.disc.is_appendable", NULL)) drive->status |= ECDB_DISC_APPENDABLE; if (e_hal_property_bool_get(ret, "volume.disc.is_rewritable", NULL)) drive->status |= ECDB_DISC_REWRITABLE; drive->type = e_hal_property_string_get(ret, "volume.disc.type", NULL); /* Also refresh the drive, for write speeds */ e_hal_device_get_all_properties(em->conn, udi, _ecdb_hal_device_handle, udi); break; } } FREE(udi); } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_HAL_H #define ECDB_HAL_H int ecdb_hal_init(void); void ecdb_hal_shutdown(void); void ecdb_hal_request_unmount(Ecdb_Drive_Info *drive, E_DBus_Callback_Func cb, void *data); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" int ecdb_source_init(Ecdb_Source *src); void ecdb_source_add_directory_recursive(Ecdb_Source *parent); Ecdb_Source * ecdb_source_new(void) { Ecdb_Source *src; src = calloc(1, sizeof(Ecdb_Source)); if (!src) { return NULL; } if (!ecdb_source_init(src)) { FREE(src); return NULL; } return src; } int ecdb_source_init(Ecdb_Source *src) { src->dir = FALSE; src->num_children = 0; src->size = 0; src->children = calloc(1, sizeof(Ecdb_Source)); if (!src->children) { return FALSE; } src->children[src->num_children] = NULL; return TRUE; } void ecdb_source_destroy(Ecdb_Source *src) { int i; Ecdb_Source *child; if (!src) { EINA_ERROR_PWARN("srs NULL!\n"); return; } /* free the non-recursive stuff */ if (src->dst) { eina_stringshare_del(src->dst); src->dst = NULL; } /* Not sure what to do here. The nodes should already be dealt with * in normal operation. Maybe have another function that specifically * deals with this in error cases. Anyways, commented out for now */ //if (src->node) iso_node_unref(src->node); if (src->children) { for (i = 0; src->children[i]; i++) { child = src->children[i]; ecdb_source_destroy(child); } FREE(src->children); } FREE(src); } void ecdb_source_data_set(Ecdb_Source *src, const char *dst) { if ((!src) || (!ecore_file_exists(dst))) { return; } src->dst = eina_stringshare_add(dst); src->size = ecore_file_size(dst); /* Add the files recursively here */ if (ecore_file_is_dir(src->dst)) { src->dir = !!TRUE; ecdb_source_add_directory_recursive(src); } } void ecdb_source_add_directory_recursive(Ecdb_Source *parent) { Eina_List *files; Ecdb_Source *child; char path[PATH_MAX]; char *src; if (!parent) { EINA_ERROR_PWARN("parent NULL!\n"); return; } files = ecore_file_ls(parent->dst); EINA_LIST_FREE(files, src) { child = ecdb_source_new(); snprintf(path, PATH_MAX, "%s/%s", parent->dst, src); ecdb_source_data_set(child, path); ecdb_source_child_append(parent, child); FREE(src); } } void ecdb_source_child_append(Ecdb_Source *src, Ecdb_Source *child) { long long orig, diff; Ecdb_Source *p; if (!src) { EINA_ERROR_PWARN("src NULL!\n"); return; } if (!child) { EINA_ERROR_PWARN("child NULL!\n"); return; } if (src == child) { EINA_ERROR_PWARN("Trying to make a parent of itself!\n"); return; } orig = src->size; src->num_children++; /* This is really inefficient when adding directories with lots and lots * of child files. Its good enough not to be a hugely critical thing, * but before release I need to test against using a list, or before * adding a directory count the list and malloc the entire size. Also * need to worry about multi-file drag and drop. */ src->children = realloc(src->children, sizeof(Ecdb_Source) * (src->num_children + 1)); src->children[src->num_children - 1] = child; src->children[src->num_children] = NULL; src->size += child->size; child->parent = src; diff = src->size - orig; p = src; while (p->parent) { p = p->parent; p->size += diff; } } /* Basically here we can remove all occurences (who knows why we'd get * multiple, but whatever), or just the first. For now remove the first, and * see how that goes */ void ecdb_source_child_remove(Ecdb_Source *src, Ecdb_Source *child) { Ecdb_Source **temp; Ecdb_Source *s; int i, cidx, f; if (!src) { EINA_ERROR_PWARN("src NULL!\n"); return; } if (!child) { EINA_ERROR_PWARN("child NULL!\n"); return; } if (src == child) { EINA_ERROR_PWARN("Trying to remove oneself\n"); return; } temp = calloc(src->num_children, sizeof(Ecdb_Source)); temp[src->num_children - 1] = NULL; cidx = f = 0; for (i = 0; src->children[i]; i++) { if ((src->children[i] == child) && (!f)) { f++; continue; } temp[cidx] = src->children[i]; cidx++; } FREE(src->children); src->children = temp; src->num_children--; // Propogate the size changes back up to the root s = src; do { s->size -= child->size; if (s->parent) s = s->parent; else break; } while (1); child->parent = NULL; } void ecdb_source_add_children_rec(Ecdb_Source *parent, IsoImage *image) { IsoDir *cd = NULL; Ecdb_Source *cs; int i = 0; if (!parent) { EINA_ERROR_PWARN("parent NULL!\n"); return; } if (!image) { EINA_ERROR_PWARN("image NULL!\n"); return; } while ((cs = parent->children[i])) { if (cs->dir) { iso_tree_add_new_dir(ISO_DIR(parent->node), ecore_file_file_get(cs->dst), &cd); cs->node = ISO_NODE(cd); /* If the source has children, find the node from above * (if applicable), and recursively add to it */ if (cs->num_children > 0) { ecdb_source_add_children_rec(cs, image); } } /* file */ else { iso_tree_add_node(image, ISO_DIR(parent->node), cs->dst, NULL); } i++; } } /* proj->files should only have children */ BurnSource * ecdb_image_project(Ecdb_Burn_Project *bp) { IsoImage *image; Ecdb_Source *c; IsoWriteOpts *opts; BurnSource *data_src, *fifo_src; Ecdb_Data_Project *proj; if (!bp) { EINA_ERROR_PWARN("bp NULL!\n"); return NULL; } if ((!bp->files) || (!bp->files->num_children)) { return NULL; } /* To handle already-suplied image files */ if ((bp->files->num_children == 1) && (ECDB_PROJECT(bp)->type == ECDB_IMAGE_PROJECT)) { efreet_mime_init(); c = bp->files->children[0]; if ((!ecore_file_is_dir(c->dst)) && (!strcmp(efreet_mime_type_get(c->dst), "application/x-cd-image"))) { data_src = burn_file_source_new(c->dst, NULL); goto FIFO_CREATE; } else { EINA_ERROR_PWARN("Supplied file is not an image!\n"); efreet_mime_shutdown(); return NULL; } efreet_mime_shutdown(); } if (ECDB_PROJECT(bp)->type == ECDB_DATA_PROJECT) { proj = ECDB_DATA(bp); } else { EINA_ERROR_PWARN("Incorrect project type!\n"); return NULL; } /* Otherwise we have a bunch of files */ if (!iso_image_new(proj->volume_id, &image)) { EINA_ERROR_PWARN("Failed to create image!\n"); return NULL; } /* Set all ids */ if (proj->publisher_id) { iso_image_set_publisher_id(image, proj->publisher_id); } if (proj->data_preparer_id) { iso_image_set_data_preparer_id(image, proj->data_preparer_id); } if (proj->system_id) { iso_image_set_system_id(image, proj->system_id); } if (proj->application_id) { iso_image_set_application_id(image, proj->application_id); } if (proj->copywrite_id) { iso_image_set_copyright_file_id(image, proj->copywrite_id); } if (proj->abstract_id) { iso_image_set_abstract_file_id(image, proj->abstract_id); } if (proj->biblio_id) { iso_image_set_biblio_file_id(image, proj->biblio_id); } if (!iso_write_opts_new(&opts, 2)) { EINA_ERROR_PWARN("Failed to create writing options!\n"); iso_image_unref(image); return NULL; } /* And some write opts stuff */ iso_write_opts_set_iso_level(opts, proj->iso_level); iso_write_opts_set_joliet(opts, proj->use_joliet); iso_write_opts_set_rockridge(opts, proj->use_rockridge); iso_write_opts_set_iso1999(opts, proj->iso1990); iso_write_opts_set_appendable(opts, bp->multi); iso_tree_set_follow_symlinks(image, proj->follow_symlinks); iso_tree_set_ignore_hidden(image, proj->ignore_hidden); iso_tree_set_ignore_special(image, proj->ignore_special); /* actually fill image with some files now */ bp->files->node = ISO_NODE(iso_image_get_root(image)); ecdb_source_add_children_rec(bp->files, image); /* Make the burn source here */ iso_image_create_burn_source(image, opts, &data_src); iso_write_opts_free(opts); iso_image_unref(image); /* And, convert to fifo */ FIFO_CREATE: fifo_src = burn_fifo_source_new(data_src, bp->fifo_chunksize, bp->fifo_chunks, 0); burn_source_free(data_src); return fifo_src; } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_IMAGE_H #define ECDB_IMAGE_H Ecdb_Source *ecdb_source_new(void); void ecdb_source_destroy(Ecdb_Source *src); void ecdb_source_data_set(Ecdb_Source *src, const char *dst); void ecdb_source_child_append(Ecdb_Source *src, Ecdb_Source *child); void ecdb_source_child_remove(Ecdb_Source *src, Ecdb_Source *child); BurnSource *ecdb_image_project(Ecdb_Burn_Project *proj); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" int ecdb_shutdown(void *data, int type, void *event) { FREE(em->theme_path); FREE(em->engine); if (em->drives) { ecdb_drive_info_list_free(em->drives); } if (em->evas_dnd_candidates) { eina_list_free(em->evas_dnd_candidates); } if (em->ewl_dnd_candidates) { eina_list_free(em->ewl_dnd_candidates); } ecdb_hal_shutdown(); FREE(em); if (!ecore_file_recursive_rm("/tmp/ecdb")) { EINA_ERROR_PWARN("Removal of temporary directory failed!\n"); } ecore_main_loop_quit(); return FALSE; } int ecdb_burn_init(void) { if (!burn_initialize()) { return 0; } burn_msgs_set_severities("NEVER", "SORRY", "ecdb: "); burn_set_signal_handling("ecdb: ", NULL, 0); return 1; } int ecdb_image_init(void) { if (!iso_init()) { return 0; } iso_set_msgs_severities("NEVER", "SORRY", "ecdb: "); return 1; } int ecdb_match_keyword(const char *chk, const char *key, int len) { int i; for (i = 0; i < len; i++) { if (chk[i] != key[i]) { return TRUE; } } return FALSE; } char * ecdb_strip_string(const char *strip) { char *t1 = (char *)strip; do { if (*t1 == ' ') { t1++; return strdup(t1); } } while ((t1) && (t1++)); return NULL; } char * ecdb_strip_next_argument(const char *strip) { char *t1 = (char *)strip, *t2; char *ret = NULL; int len = 0, space = FALSE; do { if (*t1 == ' ') { space = TRUE; } if ((*t1 != ' ') && (space == TRUE)) { t2 = t1; /* Find length of string to copy */ while ((*t2) && (*t2 != ' ')) { len++; t2++; } /* Given no more args */ if (!len) { return NULL; } else { len++; } /* Make a new string and copy everything over */ ret = malloc(sizeof(char) * len); memcpy(ret, t1, len); return ret; } } while (*(t1++)); return NULL; } int ecdb_dnd_position(void *data, int type, void *event) { Eina_List *l; Ecore_X_Event_Xdnd_Position *ev; Evas_Object *o; Ewl_Widget *ew; Ecore_X_Rectangle rect; Evas_Coord window_x, window_y, window_w, window_h, x, y, w, h; ev = event; if (ev->win != ecore_evas_software_x11_window_get(em->main_win_ee)) { return 1; } ecore_evas_geometry_get(em->main_win_ee, &window_x, &window_y, &window_w, &window_h); em->evas_dnd_candidates = eina_list_nth_list(em->evas_dnd_candidates, 0); EINA_LIST_FOREACH(em->evas_dnd_candidates, l, o) { if (evas_object_visible_get(o)) { evas_object_geometry_get(o, &x, &y, &w, &h); x += window_x; y += window_y; if ((ev->position.x >= x) && (ev->position.x <= (x + w)) && (ev->position.y >= y) && (ev->position.y <= (y + h))) { rect.x = x; rect.y = y; rect.width = w; rect.height = h; em->evas_drop_object = o; em->ewl_drop_object = NULL; ecore_x_dnd_send_status(1, 1, rect, ECORE_X_DND_ACTION_PRIVATE); return 1; } } } em->ewl_dnd_candidates = eina_list_nth_list(em->ewl_dnd_candidates, 0); EINA_LIST_FOREACH(em->ewl_dnd_candidates, l, ew) { ewl_object_current_geometry_get(EWL_OBJECT(ew), &x, &y, &w, &h); x += window_x; y += window_y; if ((ev->position.x >= x) && (ev->position.x <= (x + w)) && (ev->position.y >= y) && (ev->position.y <= (y + h))) { rect.x = x; rect.y = y; rect.width = w; rect.height = h; em->ewl_drop_object = ew; em->evas_drop_object = NULL; ecore_x_dnd_send_status(1, 1, rect, ECORE_X_DND_ACTION_PRIVATE); return 1; } } rect.x = window_x; rect.y = window_y; rect.width = window_w; rect.height = window_h; ecore_x_dnd_send_status(0, 1, rect, ECORE_X_DND_ACTION_PRIVATE); return 1; } int ecdb_dnd_drop(void *data, int type, void *event) { if ((em->evas_drop_object) || (em->ewl_drop_object)) { ecore_x_selection_xdnd_request (ecore_evas_software_x11_window_get(em->main_win_ee), "text/uri-list"); } return 1; } int ecdb_dnd_selection(void *data, int type, void *event) { Ecore_X_Event_Selection_Notify *ev; Ecore_X_Selection_Data *d; Ecore_X_Selection_Data_Files *files; void (*call_func)(Evas_Object *, Ecore_X_Selection_Data_Files *) = NULL; ev = event; if (((!em->evas_drop_object) && (!em->ewl_drop_object)) || (ev->selection != ECORE_X_SELECTION_XDND) || (!(files = ev->data)) || (!(d = ev->data)) || (files->num_files <= 0)) { ecore_x_dnd_send_finished(); return 1; } if (em->evas_drop_object) { call_func = evas_object_data_get(em->evas_drop_object, "dnd_call_func"); if (call_func != NULL) { call_func(em->evas_drop_object, files); } } else if (em->ewl_drop_object) { Ewl_Event_Dnd_Data_Received ewl_ev; ewl_ev.type = ev->target; ewl_ev.data = files->files; ewl_ev.len = files->num_files; ewl_ev.format = d->format; ewl_callback_call_with_event_data(em->ewl_drop_object, EWL_CALLBACK_DND_DATA_RECEIVED, &ewl_ev); } em->evas_drop_object = NULL; em->ewl_drop_object = NULL; ecore_x_dnd_send_finished(); return 1; } // For use with the entry group, where only one file is valid. We take the // first one and ignore the rest (if any) void ecdb_dnd_entry_dnd_set(Evas_Object *o, Ecore_X_Selection_Data_Files *files) { Efreet_Uri *uri; if ((files) && (files->files)) { uri = efreet_uri_decode(files->files[0]); ecdb_entry_text_set(o, uri->path); efreet_uri_free(uri); } } int ecdb_speed_convert(Ecdb_Drive_Info *drive, int sel) { int count, num_speeds, i; count = 1; sel++; num_speeds = drive->write_speeds[0]; for (i = 0; i < num_speeds; i++) { if (drive->write_speeds[i] <= 0) continue; if (count == sel) return drive->write_speeds[count]; count++; } return -1; } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_MISC_H #define ECDB_MISC_H int ecdb_shutdown(void *data, int type, void *event); int ecdb_burn_init(void); int ecdb_image_init(void); /* This is misc after all */ int ecdb_match_keyword(const char *chk, const char *key, int len); char *ecdb_strip_next_argument(const char *strip); char *ecdb_strip_string(const char *strip); int ecdb_speed_convert(Ecdb_Drive_Info *drive, int sel); // Also consider moving dnd stuff into its own file int ecdb_dnd_position(void *data, int type, void *event); int ecdb_dnd_drop(void *data, int type, void *event); int ecdb_dnd_selection(void *data, int type, void *event); void ecdb_dnd_entry_dnd_set(Evas_Object *o, Ecore_X_Selection_Data_Files *files); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" int ecdb_project_init(Ecdb_Project *proj); int ecdb_burn_project_init(Ecdb_Burn_Project *proj); int ecdb_erase_project_init(Ecdb_Erase_Project *proj); int ecdb_audio_project_init(Ecdb_Audio_Project *proj); int ecdb_data_project_init(Ecdb_Data_Project *proj); int ecdb_image_project_init(Ecdb_Image_Project *proj); Ecdb_Project * ecdb_project_new(void) { Ecdb_Project *proj; proj = calloc(1, sizeof(Ecdb_Project)); if (!proj) { return NULL; } if (!ecdb_project_init(proj)) { FREE(proj); return NULL; } return proj; } int ecdb_project_init(Ecdb_Project *proj) { return TRUE; } void ecdb_project_destroy(Ecdb_Project *proj) { if (proj->ev_handler) ecore_event_handler_del(proj->ev_handler); if (proj->pipe) ecore_pipe_del(proj->pipe); EINA_ERROR_PINFO("base\n"); } void ecdb_project_type_set(Ecdb_Project *proj, unsigned int t) { proj->type = t; } Ecdb_Burn_Project * ecdb_burn_project_new(void) { Ecdb_Burn_Project *proj; proj = calloc(1, sizeof(Ecdb_Burn_Project)); if (!proj) return NULL; if (!ecdb_burn_project_init(proj)) { FREE(proj); return NULL; } return proj; } int ecdb_burn_project_init(Ecdb_Burn_Project *proj) { if (!ecdb_project_init(ECDB_PROJECT(proj))) return FALSE; /* Create some sane defaults */ ecdb_project_type_set(ECDB_PROJECT(proj), ECDB_BURN_PROJECT); proj->burn_mode = BURN_MODE1; proj->fifo_chunksize = 2048; proj->fifo_chunks = 2048; proj->underrun_proof = TRUE; proj->files = ecdb_source_new(); proj->opc = TRUE; proj->multi = TRUE; return TRUE; } void ecdb_burn_project_destroy(Ecdb_Burn_Project *proj) { EINA_ERROR_PINFO("burn\n"); if (proj->files) { ecdb_source_destroy(proj->files); proj->files = NULL; } ecdb_project_destroy(ECDB_PROJECT(proj)); } Ecdb_Erase_Project * ecdb_erase_project_new(void) { Ecdb_Erase_Project *proj; proj = calloc(1, sizeof(Ecdb_Erase_Project)); if (!proj) { return NULL; } if (!ecdb_erase_project_init(proj)) { FREE(proj); return NULL; } return proj; } int ecdb_erase_project_init(Ecdb_Erase_Project *proj) { if (!ecdb_project_init(ECDB_PROJECT(proj))) { return FALSE; } /* Proper defaults */ ecdb_project_type_set(ECDB_PROJECT(proj), ECDB_ERASE_PROJECT); proj->quick = TRUE; proj->format = FALSE; return TRUE; } void ecdb_erase_project_destroy(Ecdb_Erase_Project *proj) { EINA_ERROR_PINFO("Destroying: erase\n"); ecdb_project_destroy(ECDB_PROJECT(proj)); FREE(proj); } Ecdb_Audio_Project * ecdb_audio_project_new(void) { Ecdb_Audio_Project *proj; proj = calloc(1, sizeof(Ecdb_Audio_Project)); if (!proj) { return NULL; } if (!ecdb_audio_project_init(proj)) { FREE(proj); return NULL; } return proj; } int ecdb_audio_project_init(Ecdb_Audio_Project *proj) { if (!ecdb_burn_project_init(ECDB_BURN(proj))) { return FALSE; } ecdb_project_type_set(ECDB_PROJECT(proj), ECDB_AUDIO_PROJECT); return TRUE; } void ecdb_audio_project_destroy(Ecdb_Audio_Project *proj) { EINA_ERROR_PINFO("Destroying: audio\n"); ecdb_burn_project_destroy(ECDB_BURN(proj)); FREE(proj); } Ecdb_Data_Project * ecdb_data_project_new(void) { Ecdb_Data_Project *proj; proj = calloc(1, sizeof(Ecdb_Data_Project)); if (!proj) { return NULL; } if (!ecdb_data_project_init(proj)) { FREE(proj); return NULL; } return proj; } int ecdb_data_project_init(Ecdb_Data_Project *proj) { if (!ecdb_burn_project_init(ECDB_BURN(proj))) { return FALSE; } ecdb_project_type_set(ECDB_PROJECT(proj), ECDB_DATA_PROJECT); proj->use_joliet = TRUE; proj->use_rockridge = TRUE; proj->iso_level = 3; return TRUE; } void ecdb_data_project_destroy(Ecdb_Data_Project *proj) { EINA_ERROR_PINFO("Destroying: data\n"); FREE(proj->volume_id); FREE(proj->publisher_id); FREE(proj->data_preparer_id); FREE(proj->system_id); FREE(proj->application_id); FREE(proj->copywrite_id); FREE(proj->abstract_id); FREE(proj->biblio_id); ecdb_burn_project_destroy(ECDB_BURN(proj)); FREE(proj); } Ecdb_Image_Project * ecdb_image_project_new(void) { Ecdb_Image_Project *proj; proj = calloc(1, sizeof(Ecdb_Image_Project)); if (!proj) { return NULL; } if (!ecdb_image_project_init(proj)) { FREE(proj); return NULL; } return proj; } int ecdb_image_project_init(Ecdb_Image_Project *proj) { if (!ecdb_burn_project_init(ECDB_BURN(proj))) return FALSE; ecdb_project_type_set(ECDB_PROJECT(proj), ECDB_IMAGE_PROJECT); return TRUE; } void ecdb_image_project_destroy(Ecdb_Image_Project *proj) { EINA_ERROR_PINFO("Destroying: image\n"); ecdb_burn_project_destroy(ECDB_BURN(proj)); FREE(proj); } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_PROJECTS_H #define ECDB_PROJECTS_H Ecdb_Project *ecdb_project_new(void); void ecdb_project_destroy(Ecdb_Project *proj); void ecdb_project_type_set(Ecdb_Project *proj, unsigned int t); Ecdb_Burn_Project *ecdb_burn_project_new(void); void ecdb_burn_project_destroy(Ecdb_Burn_Project *proj); Ecdb_Erase_Project *ecdb_erase_project_new(void); void ecdb_erase_project_destroy(Ecdb_Erase_Project *proj); Ecdb_Audio_Project *ecdb_audio_project_new(void); void ecdb_audio_project_destroy(Ecdb_Audio_Project *proj); Ecdb_Data_Project *ecdb_data_project_new(void); void ecdb_data_project_destroy(Ecdb_Data_Project *proj); Ecdb_Image_Project *ecdb_image_project_new(void); void ecdb_image_project_destroy(Ecdb_Image_Project *proj); #endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include "ecdb.h" static void _widget_focus_handle(Evas_Object *o) { static Evas_Object *old_object; if (old_object) { if (old_object != o) edje_object_signal_emit(old_object, "ecdb,focus,out", "ecdb"); } old_object = o; // Send focus to the freshly clicked widget evas_object_focus_set(o, 1); } static void _mouse_down_edje(void *data, Evas_Object *o, const char *emission, const char *source) { _widget_focus_handle(o); } static void _mouse_down_evas(void *data, Evas *e __UNUSED__, Evas_Object *eo, void *ev_data __UNUSED__) { _widget_focus_handle(eo); } void ecdb_widget_focus_callback_add(Evas_Object *o, const char *name) { evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_evas, (void *)name); } Evas_Object * ecdb_widget_add(Evas_Object *parent, const char *name) { Evas_Object *o; int x, y, w, h; o = edje_object_add(evas_object_evas_get(parent)); if (!o) { EINA_ERROR_PWARN("NULL return!\n"); return NULL; } edje_object_signal_callback_add(o, "ecdb,activate", "*", _mouse_down_edje, NULL); if (name) { edje_object_part_geometry_get(parent, name, &x, &y, &w, &h); evas_object_move(o, x, y); evas_object_resize(o, w, h); edje_object_part_swallow(parent, name, o); } return o; } /************************** BUTTON ******************************************/ Evas_Object * ecdb_button_add(Evas_Object *parent, const char *name) { Evas_Object *b; b = ecdb_widget_add(parent, name); if (!b) { EINA_ERROR_PWARN("NULL return!\n"); return NULL; } edje_object_file_set(b, em->theme_path, "ecdb/button"); return b; } void ecdb_button_label_set(Evas_Object *b, const char *label) { if (!b) { EINA_ERROR_PWARN("NULL object!\n"); return; } edje_object_part_text_set(b, "ecdb.label", label); } void ecdb_button_icon_set(Evas_Object *b, const char *group) { Evas_Object *icon; if (!b) { EINA_ERROR_PWARN("NULL object!\n"); return; } icon = ecdb_widget_add(b, "ecdb.swallow.icon"); if (!icon) { EINA_ERROR_PWARN("NULL icon!\n"); return; } if (edje_object_file_set(icon, em->theme_path, group)) edje_object_signal_emit(b, "ecdb,button,icon,swallow", "ecdb"); evas_object_show(icon); } const char * ecdb_button_label_get(Evas_Object *b) { if (!b) { EINA_ERROR_PWARN("NULL object!\n"); return NULL; } return edje_object_part_text_get(b, "ecdb.label"); } /************************* COMBO ITEM ****************************************/ Evas_Object * ecdb_combo_item_add(Evas_Object *parent, const char *name) { Evas_Object *ci; ci = ecdb_widget_add(parent, name); if (!ci) { EINA_ERROR_PWARN("NULL return!\n"); return NULL; } edje_object_file_set(ci, em->theme_path, "ecdb/combo_item"); return ci; } void ecdb_combo_item_label_set(Evas_Object *ci, const char *label) { if (!ci) { EINA_ERROR_PWARN("NULL ci!\n"); return; } edje_object_part_text_set(ci, "ecdb.label", label); } void ecdb_combo_item_icon_set(Evas_Object *ci, const char *group) { Evas_Object *icon; if (!ci) { EINA_ERROR_PWARN("NULL object!\n"); return; } icon = ecdb_widget_add(ci, "ecdb.swallow.icon"); if (!icon) { EINA_ERROR_PWARN("NULL icon!\n"); return; } edje_object_file_set(icon, em->theme_path, group); evas_object_show(icon); } const char * ecdb_combo_item_label_get(Evas_Object *ci) { return edje_object_part_text_get(ci, "ecdb.label"); } /************************* CHECK *********************************************/ typedef struct _Check_Data Check_Data; struct _Check_Data { unsigned int checked; }; static void _check_del(void *data, Evas *e, Evas_Object *obj, void *event_info) { Check_Data *cd; cd = evas_object_data_get(data, "cd"); FREE(cd); } static void _check_toggle_cb_call(void *data, Evas_Object *obj, const char *emission, const char *source) { Check_Data *cd; cd = evas_object_data_get(obj, "cd"); if (!strcmp(emission, "ecdb,check,checked")) { evas_object_smart_callback_call(obj, "checked", NULL); cd->checked = 1; } else { evas_object_smart_callback_call(obj, "unchecked", NULL); cd->checked = 0; } } Evas_Object * ecdb_check_add(Evas_Object *parent, const char *name) { Evas_Object *c; Check_Data *cd; c = ecdb_widget_add(parent, name); if (!c) { EINA_ERROR_PWARN("NULL return!\n"); return NULL; } edje_object_file_set(c, em->theme_path, "ecdb/check"); edje_object_signal_callback_add(c, "ecdb,check,*", "ecdb", _check_toggle_cb_call, c); evas_object_event_callback_add(c, EVAS_CALLBACK_DEL, _check_del, c); cd = calloc(1, sizeof(Check_Data)); if (!cd) { EINA_ERROR_PWARN("NULL data!\n"); return NULL; } evas_object_data_set(c, "cd", cd); return c; } void ecdb_check_label_set(Evas_Object *c, const char *label) { if (!c) { EINA_ERROR_PWARN("NULL object!\n"); return; } edje_object_part_text_set(c, "ecdb.label", label); } void ecdb_check_states_set(Evas_Object *c, const char *ystate, const char *nstate) { if (!c) { EINA_ERROR_PWARN("NULL object!\n"); return; } edje_object_part_text_set(c, "ecdb.ystate", ystate); edje_object_part_text_set(c, "ecdb.nstate", nstate); } void ecdb_check_checked_set(Evas_Object *c, int state) { Check_Data *cd; if (!c) { EINA_ERROR_PWARN("NULL object!\n"); return; } cd = evas_object_data_get(c, "cd"); if (!cd) { EINA_ERROR_PWARN("NULL data!\n"); return; } if (cd->checked == state) return; if (state) { cd->checked = 1; edje_object_signal_emit(c, "ecdb,check,on", "ecdb"); } else { cd->checked = 0; edje_object_signal_emit(c, "ecdb,check,off", "ecdb"); } } int ecdb_check_checked_get(Evas_Object *c) { Check_Data *cd; if (!c) { EINA_ERROR_PWARN("NULL object!\n"); return 0; } cd = evas_object_data_get(c, "cd"); if (!cd) { EINA_ERROR_PWARN("NULL data!\n"); return 0; } return cd->checked; } /************************** ENTRY *******************************************/ /* Shamelessly stolen from Elementary... */ static char * _entry_str_append(char *str, const char *txt, int *len, int *alloc) { int txt_len = strlen(txt); if (txt_len <= 0) return str; if ((*len + txt_len) >= *alloc) { char *str2; int alloc2; alloc2 = *alloc + txt_len + 128; str2 = realloc(str, alloc2); if (!str2) return str; *alloc = alloc2; str = str2; } strcpy(str + *len, txt); *len += txt_len; return str; } static char * _entry_markup_to_text(const char *mkup) { char *str = NULL; int str_len = 0, str_alloc = 0; // FIXME: markup -> text char *s, *p; char *tag_start, *tag_end, *esc_start, *esc_end, *ts; tag_start = tag_end = esc_start = esc_end = NULL; if (!mkup) return NULL; p = (char *)mkup; s = p; for (;;) { if ((*p == 0) || (tag_end) || (esc_end) || (tag_start) || (esc_start)) { if (tag_end) { char *ttag; ttag = malloc(tag_end - tag_start); if (ttag) { strncpy(ttag, tag_start + 1, tag_end - tag_start - 1); ttag[tag_end - tag_start - 1] = 0; /* if (!strcmp(ttag, "br")) str = _str_append(str, "\n", &str_len, &str_alloc); */ if (!strcmp(ttag, "\n")) str = _entry_str_append(str, "\n", &str_len, &str_alloc); else if (!strcmp(ttag, "\\n")) str = _entry_str_append(str, "\n", &str_len, &str_alloc); else if (!strcmp(ttag, "\t")) str = _entry_str_append(str, "\t", &str_len, &str_alloc); else if (!strcmp(ttag, "\\t")) str = _entry_str_append(str, "\t", &str_len, &str_alloc); free(ttag); } tag_start = tag_end = NULL; } else if (esc_end) { ts = malloc(esc_end - esc_start + 1); if (ts) { const char *esc; strncpy(ts, esc_start, esc_end - esc_start); ts[esc_end - esc_start] = 0; esc = evas_textblock_escape_string_get(ts); if (esc) str = _entry_str_append(str, esc, &str_len, &str_alloc); free(ts); } esc_start = esc_end = NULL; } else if (*p == 0) { ts = malloc(p - s + 1); if (ts) { strncpy(ts, s, p - s); ts[p - s] = 0; str = _entry_str_append(str, ts, &str_len, &str_alloc); free(ts); } s = NULL; } if (*p == 0) break; } if (*p == '<') { if (!esc_start) { tag_start = p; tag_end = NULL; ts = malloc(p - s + 1); if (ts) { strncpy(ts, s, p - s); ts[p - s] = 0; str = _entry_str_append(str, ts, &str_len, &str_alloc); free(ts); } s = NULL; } } else if (*p == '>') { if (tag_start) { tag_end = p; s = p + 1; } } else if (*p == '&') { if (!tag_start) { esc_start = p; esc_end = NULL; ts = malloc(p - s + 1); if (ts) { strncpy(ts, s, p - s); ts[p - s] = 0; str = _entry_str_append(str, ts, &str_len, &str_alloc); free(ts); } s = NULL; } } else if (*p == ';') { if (esc_start) { esc_end = p; s = p + 1; } } p++; } return str; } static void _entry_click_cb_call(void *data, Evas_Object *obj, const char *emission, const char *source) { evas_object_focus_set(obj, 1); } Evas_Object * ecdb_entry_add(Evas_Object *parent, const char *name) { Evas_Object *e; e = ecdb_widget_add(parent, name); if (!e) { EINA_ERROR_PWARN("NULL return!\n"); return NULL; } edje_object_file_set(e, em->theme_path, "ecdb/entry"); em->evas_dnd_candidates = eina_list_append(em->evas_dnd_candidates, e); evas_object_data_set(e, "dnd_call_func", ecdb_dnd_entry_dnd_set); edje_object_signal_callback_add(e, "clicked", "ecdb", _entry_click_cb_call, e); return e; } void ecdb_entry_text_set(Evas_Object *e, const char *text) { edje_object_part_text_set(e, "ecdb.text", text); } char * ecdb_entry_text_get(Evas_Object *e) { return _entry_markup_to_text(edje_object_part_text_get(e, "ecdb.text")); } /****************************** COMBO ****************************************/ typedef struct _Combo_Data Combo_Data; struct _Combo_Data { Evas_Object *parent; Evas_Object *back; Evas_Object *popup; unsigned int expanded; unsigned int count; int selected; const char *name; const char *header; void *data; void (*create_header)(Evas_Object *, const char *name, void *data, Evas_Object *, int); }; static void _combo_min_size(Evas_Object *o, Evas_Object_Box_Data *p, void *data) { Eina_List *l; Evas_Object_Box_Option *opt; int x, y, w, h; int xc, yc, wc, hc; if (eina_list_count(p->children) <= 0) return; evas_object_geometry_get(o, &x, &y, &w, &h); p->children = eina_list_nth_list(p->children, 0); h /= eina_list_count(p->children); if (h < 1) h = 1; EINA_LIST_FOREACH(p->children, l, opt) { evas_object_geometry_get(opt->obj, &xc, &yc, &wc, &hc); if ((wc != w) || (hc != h)) evas_object_resize(opt->obj, w, h); if ((xc != x) || (yc != y)) evas_object_move(opt->obj, x, y); y += h; } } // Axis preferential -- for now vertical const char * _combo_best_location(Evas_Object *c) { int x, y, w, h; int ww, wh; ecore_evas_geometry_get(em->main_win_ee, NULL, NULL, &ww, &wh); evas_object_geometry_get(c, &x, &y, &w, &h); if ((y + h) > (wh - y - h)) { return "top"; } else { return "bottom"; } } static void _combo_del(void *data, Evas *e, Evas_Object *obj, void *event_info) { Combo_Data *cd; cd = evas_object_data_get(data, "cd"); eina_stringshare_del(cd->header); FREE(cd); } static void _combo_resize(void *data, Evas *e, Evas_Object *obj, void *event_info) { Combo_Data *cd; cd = evas_object_data_get(data, "cd"); if (cd->expanded) ecdb_combo_collapse(data); } static void _combo_hide(void *data, Evas *e, Evas_Object *obj, void *event_info) { Combo_Data *cd; cd = evas_object_data_get(data, "cd"); if (cd->expanded) ecdb_combo_collapse(data); } static void _combo_clicked(void *data, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__) { Combo_Data *cd; Evas_Object *swallow, *header; Eina_Iterator *it; int i = 0; cd = evas_object_data_get(data, "cd"); swallow = edje_object_part_swallow_get(data, "ecdb.header.swallow"); if (swallow) evas_object_del(swallow); it = evas_object_box_iterator_new(edje_object_part_object_get(cd->popup, "ecdb.box")); if (!it) { EINA_ERROR_PWARN("NULL iterator!\n"); return; } while (eina_iterator_next(it, (void **)&swallow)) { if (swallow == obj) break; i++; } eina_iterator_free(it); cd->selected = i; header = edje_object_part_swallow_get(data, "ecdb.header.swallow"); if (header) { edje_object_part_unswallow(data, header); evas_object_del(header); } if (cd->create_header) cd->create_header(data, "ecdb.header.swallow", cd->data, swallow, i); edje_object_signal_emit(data, "ecdb,combo,header,swallow", "ecdb"); ecdb_combo_collapse(data); } static void _combo_moveable_click_cb(void *data, Evas_Object *obj, const char *emission, const char *source) { Combo_Data *cd; cd = evas_object_data_get(data, "cd"); if (cd->expanded) ecdb_combo_collapse(data); } static void _combo_click_cb(void *data, Evas_Object *obj, const char *emission, const char *source) { Evas_Object *c; Combo_Data *cd; c = data; cd = evas_object_data_get(c, "cd"); if ((cd) && (!cd->expanded)) ecdb_combo_expand(c); else ecdb_combo_collapse(c); } Evas_Object * ecdb_combo_add(Evas_Object *parent, const char *name) { Evas_Object *c; Combo_Data *cd; c = ecdb_widget_add(parent, name); if (!c) { EINA_ERROR_PWARN("NULL return!\n"); return NULL; } edje_object_file_set(c, em->theme_path, "ecdb/combo"); evas_object_event_callback_add(parent, EVAS_CALLBACK_RESIZE, _combo_resize, c); evas_object_event_callback_add(parent, EVAS_CALLBACK_HIDE, _combo_hide, c); evas_object_event_callback_add(c, EVAS_CALLBACK_DEL, _combo_del, c); edje_object_signal_callback_add(c, "ecdb,clicked", "ecdb", _combo_click_cb, c); cd = calloc(1, sizeof(Combo_Data)); if (!cd) { EINA_ERROR_PWARN("NULL data!\n"); return NULL; } cd->back = edje_object_add(evas_object_evas_get(parent)); edje_object_file_set(cd->back, em->theme_path, "ecdb/combo/background"); edje_object_signal_callback_add(cd->back, "ecdb,combo,back,dismiss", "ecdb", _combo_moveable_click_cb, c); evas_object_pass_events_set(cd->back, 1); evas_object_show(cd->back); cd->popup = edje_object_add(evas_object_evas_get(parent)); edje_object_file_set(cd->popup, em->theme_path, "ecdb/combo/popup"); edje_object_signal_callback_add(cd->back, "ecdb,combo,back,dismiss", "ecdb", _combo_moveable_click_cb, c); edje_box_layout_register("min_size", _combo_min_size, NULL, NULL, NULL, NULL); evas_object_pass_events_set(cd->popup, 1); evas_object_show(cd->popup); cd->parent = parent; cd->selected = -1; evas_object_data_set(c, "cd", cd); return c; } void ecdb_combo_header_set(Evas_Object *c, const char *text) { Combo_Data *cd; if (!c) { EINA_ERROR_PWARN("NULL object!\n"); return; } cd = evas_object_data_get(c, "cd"); if (!cd) { EINA_ERROR_PWARN("NULL data!\n"); return; } edje_object_part_text_set(c, "ecdb.header", text); eina_stringshare_del(cd->header); cd->header = eina_stringshare_add(text); } void ecdb_combo_append(Evas_Object *c, Evas_Object *o) { Combo_Data *cd; if (!c) { EINA_ERROR_PWARN("NULL object!\n"); return; } cd = evas_object_data_get(c, "cd"); if (!cd) { EINA_ERROR_PWARN("NULL data!\n"); return; } cd->count++; edje_object_part_box_append(cd->popup, "ecdb.box", o); edje_object_signal_callback_add(o, "ecdb,clicked", "ecdb", _combo_clicked, c); } void ecdb_combo_expand(Evas_Object *c) { Combo_Data *cd; Eina_Iterator *it; Evas_Object *o; const Evas_Object *box; const char *location, *min_size; char buf[1024]; int x, y, w, h, h2, padding; if (!c) { EINA_ERROR_PWARN("NULL object!\n"); return; } cd = evas_object_data_get(c, "cd"); if (!cd) { EINA_ERROR_PWARN("NULL data!\n"); return; } if ((cd->count == 0) || (cd->expanded)) return; edje_object_signal_emit(c, "ecdb,combo,active", "ecdb"); evas_object_move(cd->back, 0, 0); ecore_evas_geometry_get(em->main_win_ee, NULL, NULL, &w, &h); evas_object_resize(cd->back, w, h); evas_object_pass_events_set(cd->back, 0); evas_object_raise(cd->back); edje_object_signal_emit(cd->back, "ecdb,combo,back,show", "ecdb"); location = _combo_best_location(c); snprintf(buf, sizeof(buf), "ecdb,combo,popup,show,%s", location); evas_object_geometry_get(c, &x, &y, &w, &h); box = edje_object_part_object_get(cd->popup, "ecdb.box"); evas_object_box_padding_get(box, NULL, &padding); h2 = -padding; it = evas_object_box_iterator_new(edje_object_part_object_get(cd->popup, "ecdb.box")); if (!it) { EINA_ERROR_PWARN("NULL iterator!\n"); return; } while (eina_iterator_next(it, (void **)&o)) { /* I should probably this into a minimum_size_calc... */ min_size = edje_object_data_get(o, "ecdb/combo_item/minh"); if (min_size) { if (ecore_config_boolean_get("use_scale")) h2 += (int)((float)atoi(min_size) * em->scalef); else h2 += (int)atoi(min_size); } else { EINA_ERROR_PWARN("THEME ERROR! MISSING REQUIRED MINIMUM DATA VALUE\n"); } h2 += padding; } if (!strcmp(location, "top")) evas_object_move(cd->popup, x, y - h2); else evas_object_move(cd->popup, x, y + h); eina_iterator_free(it); evas_object_resize(cd->popup, w, h2); evas_object_pass_events_set(cd->popup, 0); evas_object_raise(cd->popup); edje_object_signal_emit(cd->popup, buf, "ecdb"); cd->expanded = 1; } void ecdb_combo_collapse(Evas_Object *c) { Combo_Data *cd; const char *location; char buf[1024]; Eina_Iterator *it; Evas_Object *o; // If we close the window if ((!em) || (!em->main_win_ee)) return; if (!c) { EINA_ERROR_PWARN("NULL object!\n"); return; } cd = evas_object_data_get(c, "cd"); if (!cd) { EINA_ERROR_PWARN("NULL data!\n"); return; } edje_object_signal_emit(cd->back, "ecdb,combo,back,hide", "ecdb"); evas_object_pass_events_set(cd->back, 1); edje_object_signal_emit(c, "ecdb,combo,default", "ecdb"); location = _combo_best_location(c); snprintf(buf, sizeof(buf), "ecdb,combo,popup,hide,%s", location); evas_object_pass_events_set(cd->popup, 1); edje_object_signal_emit(cd->popup, buf, "ecdb"); it = evas_object_box_iterator_new(edje_object_part_object_get(cd->popup, "ecdb.box")); if (!it) { EINA_ERROR_PWARN("NULL iterator!\n"); return; } while ((eina_iterator_next(it, (void **)&o))) edje_object_signal_emit(o, "ecdb,focus,out", "ecdb"); eina_iterator_free(it); cd->expanded = 0; } void ecdb_combo_header_create_set(Evas_Object *c, void (*func) (Evas_Object *c, const char *name, void *data, Evas_Object *clicked, int idx)) { Combo_Data *cd; if (!c) { EINA_ERROR_PWARN("NULL object!\n"); return; } cd = evas_object_data_get(c, "cd"); if (!cd) { EINA_ERROR_PWARN("NULL data!\n"); return; } cd->create_header = func; } int ecdb_combo_selected_get(Evas_Object *c) { Combo_Data *cd; if (!c) { EINA_ERROR_PWARN("NULL object!\n"); return -1; } cd = evas_object_data_get(c, "cd"); if (!cd) { EINA_ERROR_PWARN("NULL data!\n"); return -1; } return cd->selected; } void ecdb_combo_data_set(Evas_Object *c, void *data) { Combo_Data *cd; if (!c) { EINA_ERROR_PWARN("NULL object!\n"); return; } cd = evas_object_data_get(c, "cd"); if (!cd) { EINA_ERROR_PWARN("NULL data!\n"); return; } cd->data = data; } void * ecdb_combo_data_get(Evas_Object *c) { Combo_Data *cd; if (!c) { EINA_ERROR_PWARN("NULL object!\n"); return NULL; } cd = evas_object_data_get(c, "cd"); if (!cd) { EINA_ERROR_PWARN("NULL data!\n"); return NULL; } return cd->data; } void ecdb_combo_clear(Evas_Object *c) { Combo_Data *cd; if (!c) { EINA_ERROR_PWARN("NULL object!"); return; } cd = evas_object_data_get(c, "cd"); edje_object_part_box_remove_all(cd->popup, "ecdb.box", 1); cd->selected = 1; cd->count = 0; } /******************************* Label ***************************************/ Evas_Object * ecdb_label_add(Evas_Object *parent, const char *name) { Evas_Object *l; l = ecdb_widget_add(parent, name); if (!l) { EINA_ERROR_PWARN("NULL return!\n"); return NULL; } edje_object_file_set(l, em->theme_path, "ecdb/label"); return l; } void ecdb_label_text_set(Evas_Object *l, const char *text) { if (!l) { EINA_ERROR_PWARN("NULL object!\n"); return; } edje_object_part_text_set(l, "ecdb.label", text); } /************************* Config Inwin **************************************/ typedef struct _Config_Inwin_Data Config_Inwin_Data; struct _Config_Inwin_Data { Evas_Object *parent; // Used to calculate the popup layers Evas_Object *back; Evas_Object *popup; unsigned int visible; }; static void _config_inwin_resize(void *data, Evas *e, Evas_Object *obj, void *event_info) { Config_Inwin_Data *iwd; int w, h; iwd = data; // We get a resize event even though the evas has been destroyed if ((!em) || (!em->main_win_ee)) return; ecore_evas_geometry_get(em->main_win_ee, NULL, NULL, &w, &h); evas_object_resize(iwd->popup, w, h); evas_object_move(iwd->popup, 0, 0); evas_object_resize(iwd->back, w, h); evas_object_move(iwd->back, 0, 0); } static void _config_inwin_hide(void *data, Evas *e, Evas_Object *obj, void *event_info) { Config_Inwin_Data *iwd; iwd = data; if (iwd) { if (iwd->popup) evas_object_hide(iwd->popup); if (iwd->back) evas_object_hide(iwd->back); } } static void _config_inwin_del(void *data, Evas *e, Evas_Object *obj, void *event_info) { Config_Inwin_Data *iwd; iwd = data; if (!evas_object_event_callback_del(iwd->parent, EVAS_CALLBACK_HIDE, _config_inwin_hide)) { EINA_ERROR_PWARN("EVAS_CALLBACK_HIDE callback removal failure!\n"); } if (!evas_object_event_callback_del(iwd->parent, EVAS_CALLBACK_RESIZE, _config_inwin_resize)) { EINA_ERROR_PWARN("EVAS_CALLBACK_RESIZE callback removal failure!\n"); } FREE(iwd); } Evas_Object * ecdb_config_inwin_add(Evas_Object *parent, const char *name) { Config_Inwin_Data *iwd; iwd = calloc(1, sizeof(Config_Inwin_Data)); if (!iwd) { EINA_ERROR_PWARN("NULL data!\n"); return NULL; } iwd->popup = edje_object_add(evas_object_evas_get(parent)); if (!iwd->popup) { EINA_ERROR_PWARN("NULL return!\n"); return NULL; } edje_object_file_set(iwd->popup, em->theme_path, "ecdb/config_inwin/popup"); evas_object_pass_events_set(iwd->popup, 1); evas_object_show(iwd->popup); evas_object_event_callback_add(iwd->popup, EVAS_CALLBACK_DEL, _config_inwin_del, iwd); iwd->back = edje_object_add(evas_object_evas_get(parent)); edje_object_file_set(iwd->back, em->theme_path, "ecdb/config_inwin/background"); iwd->parent = parent; evas_object_pass_events_set(iwd->back, 1); evas_object_show(iwd->back); evas_object_data_set(iwd->popup, "iwd", iwd); iwd->visible = 0; /* Add the callbacks used to contain this to the parent */ evas_object_event_callback_add(parent, EVAS_CALLBACK_RESIZE, _config_inwin_resize, iwd); evas_object_event_callback_add(parent, EVAS_CALLBACK_HIDE, _config_inwin_hide, iwd); return iwd->popup; } void ecdb_config_inwin_show(Evas_Object *inwin) { Config_Inwin_Data *iwd; if (!inwin) { EINA_ERROR_PWARN("NULL object!\n"); return; } iwd = evas_object_data_get(inwin, "iwd"); if (!iwd) { EINA_ERROR_PWARN("NULL data!\n"); return; } evas_object_pass_events_set(iwd->back, 0); evas_object_raise(iwd->back); edje_object_signal_emit(iwd->back, "ecdb,config_inwin,back,show", "ecdb"); evas_object_show(iwd->back); evas_object_pass_events_set(iwd->popup, 0); evas_object_raise(iwd->popup); edje_object_signal_emit(iwd->popup, "ecdb,config_inwin,popup,show", "ecdb"); evas_object_show(iwd->popup); iwd->visible = 1; } void ecdb_config_inwin_hide(Evas_Object *inwin) { Config_Inwin_Data *iwd; if (!inwin) { EINA_ERROR_PWARN("NULL object!\n"); return; } iwd = evas_object_data_get(inwin, "iwd"); if (!iwd) { EINA_ERROR_PWARN("NULL data!\n"); return; } evas_object_pass_events_set(iwd->back, 1); edje_object_signal_emit(iwd->back, "ecdb,config_inwin,back,hide", "ecdb"); evas_object_pass_events_set(iwd->popup, 1); edje_object_signal_emit(iwd->popup, "ecdb,config_inwin,popup,hide", "ecdb"); iwd->visible = 0; } void ecdb_config_inwin_child_add(Evas_Object *inwin, Evas_Object *child, int c, int r, int cs, int rs) { if (!inwin) { EINA_ERROR_PWARN("NULL object!\n"); return; } if (!child) return; if (!edje_object_part_table_pack(inwin, "ecdb.table", child, c, r, cs, rs)) { EINA_ERROR_PWARN("Couldn't append to table!\n"); } } /***************************** Capacity **************************************/ Evas_Object * ecdb_capacity_add(Evas_Object *parent, const char *name) { Evas_Object *ret; ret = ecdb_widget_add(parent, "ecdb/burn_data/capacity"); if (!ret) { EINA_ERROR_PWARN("NULL return!\n"); return NULL; } edje_object_file_set(ret, em->theme_path, "ecdb/capacity"); return ret; } void ecdb_capacity_float_set(Evas_Object *cap, float val) { Edje_Message_Float msg; if (!cap) { EINA_ERROR_PWARN("NULL object!\n"); return; } msg.val = val; edje_object_message_send(cap, EDJE_MESSAGE_FLOAT, 1, &msg); } /* vim: set sw=3 ts=3 sts=3 expandtab: */ #ifndef ECDB_WIDGETS_H #define ECDB_WIDGETS_H void ecdb_widget_focus_callback_add(Evas_Object *o, const char *name); Evas_Object *ecdb_widget_add(Evas_Object *parent, const char *name); Evas_Object *ecdb_button_add(Evas_Object *parent, const char *name); void ecdb_button_label_set(Evas_Object *b, const char *label); void ecdb_button_icon_set(Evas_Object *b, const char *group); const char *ecdb_button_label_get(Evas_Object *b); Evas_Object *ecdb_combo_item_add(Evas_Object *parent, const char *name); void ecdb_combo_item_label_set(Evas_Object *ci, const char *label); void ecdb_combo_item_icon_set(Evas_Object *ci, const char *group); const char *ecdb_combo_item_label_get(Evas_Object *ci); Evas_Object *ecdb_check_add(Evas_Object *parent, const char *name); void ecdb_check_label_set(Evas_Object *c, const char *label); void ecdb_check_states_set(Evas_Object *c, const char *ystate, const char *nstate); void ecdb_check_checked_set(Evas_Object *c, int state); int ecdb_check_checked_get(Evas_Object *c); Evas_Object *ecdb_entry_add(Evas_Object *parent, const char *name); void ecdb_entry_text_set(Evas_Object *e, const char *text); char *ecdb_entry_text_get(Evas_Object *e); /* Combo todo list: * separate hover from combo * able to set whether combo collapses on child click * better theme */ Evas_Object *ecdb_combo_add(Evas_Object *parent, const char *name); void ecdb_combo_header_set(Evas_Object *c, const char *text); void ecdb_combo_append(Evas_Object *c, Evas_Object *o); void ecdb_combo_expand(Evas_Object *c); void ecdb_combo_collapse(Evas_Object *c); void ecdb_combo_header_create_set(Evas_Object *c, void (*func)(Evas_Object *c, const char *name, void *data, Evas_Object *clicked, int idx)); int ecdb_combo_selected_get(Evas_Object *c); void ecdb_combo_data_set(Evas_Object *c, void *data); void *ecdb_combo_data_get(Evas_Object *c); void ecdb_combo_clear(Evas_Object *c); Evas_Object *ecdb_label_add(Evas_Object *parent, const char *name); void ecdb_label_text_set(Evas_Object *l, const char *text); Evas_Object *ecdb_config_inwin_add(Evas_Object *parent, const char *name); void ecdb_config_inwin_show(Evas_Object *inwin); void ecdb_config_inwin_hide(Evas_Object *inwin); void ecdb_config_inwin_child_add(Evas_Object *inwin, Evas_Object *child, int c, int r, int cs, int rs); Evas_Object *ecdb_capacity_add(Evas_Object *parent, const char *name); void ecdb_capacity_float_set(Evas_Object *cap, float val); #endif MAINTAINERCLEANFILES = Makefile.in SUBDIRS = ecdb_transcode_helper if GSTREAMER_BUILD_SUPPORT MAINTAINERCLEANFILES = Makefile.im bin_PROGRAMS = ecdb_transcode_helper ecdb_transcode_helper_SOURCES = ecdb_transcode_helper.c ecdb_transcode_helper_CFLAGS = @GSTREAMER_CFLAGS@ ecdb_transcode_helper_LDADD = @GSTREAMER_LIBS@ endif /* vim: set sw=3 ts=3 sts=3 expandtab: */ #include GstElement *pipeline, *audio; static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data) { GMainLoop *loop = (GMainLoop *) data; switch (GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_EOS: { g_print("EOS\n"); g_main_loop_quit(loop); break; } case GST_MESSAGE_ERROR: { gchar *debug; GError *err; gst_message_parse_error(msg, &err, &debug); g_free(debug); g_print("%s\n", err->message); g_error_free(err); g_main_loop_quit(loop); break; } default: break; } return TRUE; } static void cb_newpad (GstElement *decodebin, GstPad *pad, gboolean last, gpointer data) { GstCaps *caps; GstStructure *str; GstPad *audiopad; audiopad = gst_element_get_static_pad (audio, "sink"); if (GST_PAD_IS_LINKED (audiopad)) { g_object_unref (audiopad); return; } caps = gst_pad_get_caps (pad); str = gst_caps_get_structure (caps, 0); if (!g_strrstr (gst_structure_get_name (str), "audio")) { gst_caps_unref (caps); gst_object_unref (audiopad); return; } gst_caps_unref (caps); gst_pad_link (pad, audiopad); } int main (int argc, char ** argv) { GMainLoop *loop; GstElement *src, *dec, *conv, *resample, *filter, *sink; GstCaps *filtercaps; GstPad *audiopad; GstBus *bus; gchar *path, *filename; gst_init (&argc, &argv); loop = g_main_loop_new (NULL, FALSE); if (argc != 2) { g_print ("Error: no supplied file!\n"); return 1; } pipeline = gst_pipeline_new ("pipeline"); bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); gst_bus_add_watch (bus, bus_call, loop); gst_object_unref (bus); src = gst_element_factory_make ("filesrc", NULL); g_object_set (G_OBJECT (src), "location", argv[1], NULL); dec = gst_element_factory_make ("decodebin", NULL); g_signal_connect (dec, "new-decoded-pad", G_CALLBACK (cb_newpad), NULL); gst_bin_add_many (GST_BIN (pipeline), src, dec, NULL); gst_element_link (src, dec); audio = gst_bin_new ("audiobin"); conv = gst_element_factory_make ("audioconvert", NULL); /* Here's the bit of magic */ resample = gst_element_factory_make("audioresample", NULL); filter = gst_element_factory_make("capsfilter", NULL); filtercaps = gst_caps_new_full(gst_structure_new("audio/x-raw-int", "channels", G_TYPE_INT, 2, "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, "endianness", G_TYPE_INT, 1234, "rate", G_TYPE_INT, 44100, "signed", G_TYPE_BOOLEAN, TRUE, NULL), NULL); g_object_set(GST_OBJECT(filter), "caps", filtercaps, NULL); gst_caps_unref(filtercaps); audiopad = gst_element_get_static_pad(conv, "sink"); sink = gst_element_factory_make ("filesink", NULL); /* Generate filename */ filename = g_path_get_basename(argv[1]); path = g_strconcat("/tmp/ecdb/", filename, ".wav", NULL); g_object_set(G_OBJECT(sink), "location", path, NULL); free(filename); free(path); gst_bin_add_many(GST_BIN (audio), conv, resample, filter, sink, NULL); gst_element_link_many(conv, resample, filter, sink, NULL); gst_element_add_pad (audio, gst_ghost_pad_new ("sink", audiopad)); gst_object_unref (audiopad); gst_bin_add (GST_BIN (pipeline), audio); gst_element_set_state (pipeline, GST_STATE_PLAYING); g_main_loop_run (loop); gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (GST_OBJECT (pipeline)); return 0; } /* This document will contain general hacking rules for genisofs */ C Code guidelines: ------------------- - Never more than 80 chars horizontally - Only use C-Style commenting - function names: lower case, separated by underscore - Using splint is strongly recommended - Use 4 spaces, not tabs Code should be formatted according to following example: # include "stdio.h" char *buffer; /* Comments to the right of declarations */ char *start, *end, *last; char *name; /* This separates blocks of declarations */ int baz; struct square { int x; int y; }; #ifdef ENABLE_NLS bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR); #else /* Comments to the right of preproc directives */ textdomain(PACKAGE); #endif int foo(int number, int len, char *name) { if (number > 0) { for (int i = 0; i < 7; i++) len++; number--; } else { while (len) { len--}; number++; } puts("Hi"); } /* The procedure bar is even less interesting. it does nothing particular :) */ char* bar(int nb) { long c; c = (long) foo(2, 5, "end"); /* Write "Hello" to Console */ puts("Hello"); switch (nb) { case 0: break; case 1: nb++; break; default: break; } } int bool_test(char *mask) { if (mask && ((mask[0] == '\0') || (mask[1] == '\0' && ((mask[0] == '0') || (mask[0] == '*'))))) return 0; } int function_with_lots_of_arguments(char* arg1, char* arg2, int arg3, int arg4, char* arg5, void* arg6, int arg7, float arg8, float arg9, float arg10, int arg11, char* arg12) { return 0; } Vreixo Formoso 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 Installation Instructions ************************* TODO write better install instructions TODO you need Apache Ant to compile the code TODO ant install is not implemented yet, you should copy .jar and .so files manually The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `ant' to compile the package. 3. Optionally, type `ant check' to run any self-tests that come with the package. 4. Type `ant install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `ant clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `ant distclean'. There is also a `ant maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. These are Java bindings for the libburnia project This version is a full binding for libisofs 0.2.4 and libburn 0.3.4. NOTE: This is realeased under GPL. However, it is implemented using 1.5 version of the Java language. At this time there isn't, at my knowledge, any free JVM implementation with fully support for Java 1.5. Given that this project is still in a very early stage, I really prefer to live with this problem for a while. I hope that free JVM with 1.5 support will be available soon. Anyway as these bindings reach release 1.0 they will be fully compilable and executable on a free environment, even if this implies porting them back to 1.4. Vreixo, 17-Apr-2007 Documentation: - Review of javadocs, update methods to java ones. - Write a little tutorial. - Usage examples. Design: - Improve error notification. Decide what functions should return error as int/boolean and what should throw an exception. Design exception hierarchy. - Walk to a improved Java-like API... - Java based BurnSource. java IO/NIO burn sources Testing: - Test program like test/libburner and test/iso.c (this last already done) - Write a lot of tests, either manual tests and automatic test (Junit based) to cover all methods. package=@PACKAGE_NAME@ version=@PACKAGE_VERSION@ cflags.isofs=@ISOFS_INCLUDE@ cflags.burn=@BURN_INCLUDE@ ---------------------------------------------------- Building ${package}... ---------------------------------------------------- NOT IMPLEMENTED YET NOT IMPLEMENTED YET This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by java-libburn configure 0.1, which was generated by GNU Autoconf 2.60. Invocation command line was $ ./configure ## --------- ## ## Platform. ## ## --------- ## hostname = mplaptop uname -m = i686 uname -r = 2.6.17-10-generic uname -s = Linux uname -v = #2 SMP Fri Oct 13 18:45:35 UTC 2006 /usr/bin/uname -p = unknown /bin/uname -X = unknown /bin/arch = i686 /usr/bin/arch -k = unknown /usr/convex/getsysinfo = unknown /usr/bin/hostinfo = unknown /bin/machine = unknown /usr/bin/oslevel = unknown /bin/universe = unknown PATH: /usr/local/sbin PATH: /usr/local/bin PATH: /usr/sbin PATH: /usr/bin PATH: /sbin PATH: /bin PATH: /usr/bin/X11 PATH: /usr/games ## ----------- ## ## Core tests. ## ## ----------- ## configure:1633: checking for javac configure:1651: found /usr/local/bin/javac configure:1663: result: /usr/local/bin/javac configure:1673: checking for javah configure:1691: found /usr/bin/javah configure:1703: result: /usr/bin/javah configure:1713: checking for jar configure:1731: found /usr/bin/jar configure:1743: result: /usr/bin/jar configure:1800: checking for gcc configure:1816: found /usr/bin/gcc configure:1827: result: gcc configure:2065: checking for C compiler version configure:2072: gcc --version >&5 gcc (GCC) 4.1.2 20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5) Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. configure:2075: $? = 0 configure:2082: gcc -v >&5 Using built-in specs. Target: i486-linux-gnu Configured with: ../src/configure -v --enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --program-suffix=-4.1 --enable-__cxa_atexit --enable-clocale=gnu --enable-libstdcxx-debug --enable-mpfr --enable-checking=release i486-linux-gnu Thread model: posix gcc version 4.1.2 20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5) configure:2085: $? = 0 configure:2092: gcc -V >&5 gcc: '-V' option must have argument configure:2095: $? = 1 configure:2118: checking for C compiler default output file name configure:2145: gcc conftest.c >&5 configure:2148: $? = 0 configure:2194: result: a.out configure:2199: checking whether the C compiler works configure:2209: ./a.out configure:2212: $? = 0 configure:2229: result: yes configure:2236: checking whether we are cross compiling configure:2238: result: no configure:2241: checking for suffix of executables configure:2248: gcc -o conftest conftest.c >&5 configure:2251: $? = 0 configure:2275: result: configure:2281: checking for suffix of object files configure:2307: gcc -c conftest.c >&5 configure:2310: $? = 0 configure:2333: result: o configure:2337: checking whether we are using the GNU C compiler configure:2366: gcc -c conftest.c >&5 configure:2372: $? = 0 configure:2379: test -z "$ac_c_werror_flag" || test ! -s conftest.err configure:2382: $? = 0 configure:2389: test -s conftest.o configure:2392: $? = 0 configure:2406: result: yes configure:2411: checking whether gcc accepts -g configure:2441: gcc -c -g conftest.c >&5 configure:2447: $? = 0 configure:2454: test -z "$ac_c_werror_flag" || test ! -s conftest.err configure:2457: $? = 0 configure:2464: test -s conftest.o configure:2467: $? = 0 configure:2597: result: yes configure:2614: checking for gcc option to accept ISO C89 configure:2688: gcc -c -g -O2 conftest.c >&5 configure:2694: $? = 0 configure:2701: test -z "$ac_c_werror_flag" || test ! -s conftest.err configure:2704: $? = 0 configure:2711: test -s conftest.o configure:2714: $? = 0 configure:2734: result: none needed configure:2893: creating ./config.status ## ---------------------- ## ## Running config.status. ## ## ---------------------- ## This file was extended by java-libburn config.status 0.1, which was generated by GNU Autoconf 2.60. Invocation command line was CONFIG_FILES = CONFIG_HEADERS = CONFIG_LINKS = CONFIG_COMMANDS = $ ./config.status on mplaptop config.status:563: creating build.properties ## ---------------- ## ## Cache variables. ## ## ---------------- ## ac_cv_c_compiler_gnu=yes ac_cv_env_CC_set= ac_cv_env_CC_value= ac_cv_env_CFLAGS_set= ac_cv_env_CFLAGS_value= ac_cv_env_CPPFLAGS_set= ac_cv_env_CPPFLAGS_value= ac_cv_env_LDFLAGS_set= ac_cv_env_LDFLAGS_value= ac_cv_env_build_alias_set= ac_cv_env_build_alias_value= ac_cv_env_host_alias_set= ac_cv_env_host_alias_value= ac_cv_env_target_alias_set= ac_cv_env_target_alias_value= ac_cv_objext=o ac_cv_path_JAR=/usr/bin/jar ac_cv_path_JAVAC=/usr/local/bin/javac ac_cv_path_JAVAH=/usr/bin/javah ac_cv_prog_ac_ct_CC=gcc ac_cv_prog_cc_c89= ac_cv_prog_cc_g=yes ## ----------------- ## ## Output variables. ## ## ----------------- ## BURN_INCLUDE='/usr/local/include/libburn' CC='gcc' CFLAGS='-g -O2' CPPFLAGS='' DEFS='-DPACKAGE_NAME=\"java-libburn\" -DPACKAGE_TARNAME=\"java-libburn\" -DPACKAGE_VERSION=\"0.1\" -DPACKAGE_STRING=\"java-libburn\ 0.1\" -DPACKAGE_BUGREPORT=\"\"' ECHO_C='' ECHO_N='-n' ECHO_T='' EXEEXT='' ISOFS_INCLUDE='/usr/local/include/libisofs' JAR='/usr/bin/jar' JAVAC='/usr/local/bin/javac' JAVAH='/usr/bin/javah' LDFLAGS='' LIBOBJS='' LIBS='' LTLIBOBJS='' OBJEXT='o' PACKAGE_BUGREPORT='' PACKAGE_NAME='java-libburn' PACKAGE_STRING='java-libburn 0.1' PACKAGE_TARNAME='java-libburn' PACKAGE_VERSION='0.1' PATH_SEPARATOR=':' SHELL='/bin/bash' ac_ct_CC='gcc' bindir='${exec_prefix}/bin' build_alias='' datadir='${datarootdir}' datarootdir='${prefix}/share' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' dvidir='${docdir}' exec_prefix='${prefix}' host_alias='' htmldir='${docdir}' includedir='${prefix}/include' infodir='${datarootdir}/info' libdir='${exec_prefix}/lib' libexecdir='${exec_prefix}/libexec' localedir='${datarootdir}/locale' localstatedir='${prefix}/var' mandir='${datarootdir}/man' oldincludedir='/usr/include' pdfdir='${docdir}' prefix='/usr/local' program_transform_name='s,x,x,' psdir='${docdir}' sbindir='${exec_prefix}/sbin' sharedstatedir='${prefix}/com' sysconfdir='${prefix}/etc' target_alias='' ## ----------- ## ## confdefs.h. ## ## ----------- ## #define PACKAGE_NAME "java-libburn" #define PACKAGE_TARNAME "java-libburn" #define PACKAGE_VERSION "0.1" #define PACKAGE_STRING "java-libburn 0.1" #define PACKAGE_BUGREPORT "" configure: exit 0 #! /bin/bash # Generated by configure. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=${CONFIG_SHELL-/bin/bash} ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # PATH needs CR # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) as_nl=' ' IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 { (exit 1); exit 1; } fi # Work around bugs in pre-3.0 UWIN ksh. for as_var in ENV MAIL MAILPATH do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # CDPATH. $as_unset CDPATH as_lineno_1=$LINENO as_lineno_2=$LINENO test "x$as_lineno_1" != "x$as_lineno_2" && test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line after each line using $LINENO; the second 'sed' # does the real work. The second script uses 'N' to pair each # line-number line with the line containing $LINENO, and appends # trailing '-' during substitution so that $LINENO is not a special # case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # scripts with optimization help from Paolo Bonzini. Blame Lee # E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in -n*) case `echo 'x\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. *) ECHO_C='\c';; esac;; *) ECHO_N='-n';; esac if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir fi echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # Find out whether ``test -x'' works. Don't use a zero-byte file, as # systems may use methods other than mode bits to determine executability. cat >conf$$.file <<_ASEOF #! /bin/sh exit 0 _ASEOF chmod +x conf$$.file if test -x conf$$.file >/dev/null 2>&1; then as_executable_p="test -x" else as_executable_p=: fi rm -f conf$$.file # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 # Save the log message, to keep $[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by java-libburn $as_me 0.1, which was generated by GNU Autoconf 2.60. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " # Files that config.status was made for. config_files=" build.properties" ac_cs_usage="\ \`$as_me' instantiates files from templates according to the current configuration. Usage: $0 [OPTIONS] [FILE]... -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Report bugs to ." ac_cs_version="\ java-libburn config.status 0.1 configured by ./configure, generated by GNU Autoconf 2.60, with options \"\" Copyright (C) 2006 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='/home/metalpain/Dados/workspace/java-libburn' srcdir='.' # If no file are specified by the user, then we need to provide default # value. By we need to know if files were specified by the user. ac_need_defaults=: while test $# != 0 do case $1 in --*=*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) echo "$ac_cs_version"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift CONFIG_FILES="$CONFIG_FILES $ac_optarg" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) { echo "$as_me: error: unrecognized option: $1 Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *) ac_config_targets="$ac_config_targets $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi if $ac_cs_recheck; then echo "running CONFIG_SHELL=/bin/bash /bin/bash ./configure " $ac_configure_extra_args " --no-create --no-recursion" >&6 CONFIG_SHELL=/bin/bash export CONFIG_SHELL exec /bin/bash "./configure" $ac_configure_extra_args --no-create --no-recursion fi exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX echo "$ac_log" } >&5 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "build.properties") CONFIG_FILES="$CONFIG_FILES build.properties" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} { (exit 1); exit 1; }; };; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= trap 'exit_status=$? { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status ' 0 trap '{ (exit 1); exit 1; }' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || { echo "$me: cannot create a temporary directory in ." >&2 { (exit 1); exit 1; } } # # Set up the sed scripts for CONFIG_FILES section. # # No need to generate the scripts if there are no CONFIG_FILES. # This happens for instance when ./config.status config.h if test -n "$CONFIG_FILES"; then cat >"$tmp/subs-1.sed" <<\CEOF /@[a-zA-Z_][a-zA-Z_0-9]*@/!b end s,@SHELL@,|#_!!_#|/bin/bash,g s,@PATH_SEPARATOR@,|#_!!_#|:,g s,@PACKAGE_NAME@,|#_!!_#|java-libburn,g s,@PACKAGE_TARNAME@,|#_!!_#|java-libburn,g s,@PACKAGE_VERSION@,|#_!!_#|0.1,g s,@PACKAGE_STRING@,|#_!!_#|java-libburn 0.1,g s,@PACKAGE_BUGREPORT@,|#_!!_#|,g s,@exec_prefix@,|#_!!_#|${prefix},g s,@prefix@,|#_!!_#|/usr/local,g s,@program_transform_name@,|#_!!_#|s\,x\,x\,,g s,@bindir@,|#_!!_#|${exec_prefix}/bin,g s,@sbindir@,|#_!!_#|${exec_prefix}/sbin,g s,@libexecdir@,|#_!!_#|${exec_prefix}/libexec,g s,@datarootdir@,|#_!!_#|${prefix}/share,g s,@datadir@,|#_!!_#|${datarootdir},g s,@sysconfdir@,|#_!!_#|${prefix}/etc,g s,@sharedstatedir@,|#_!!_#|${prefix}/com,g s,@localstatedir@,|#_!!_#|${prefix}/var,g s,@includedir@,|#_!!_#|${prefix}/include,g s,@oldincludedir@,|#_!!_#|/usr/include,g s,@docdir@,|#_!!_#|${datarootdir}/doc/${PACKAGE_TARNAME},g s,@infodir@,|#_!!_#|${datarootdir}/info,g s,@htmldir@,|#_!!_#|${docdir},g s,@dvidir@,|#_!!_#|${docdir},g s,@pdfdir@,|#_!!_#|${docdir},g s,@psdir@,|#_!!_#|${docdir},g s,@libdir@,|#_!!_#|${exec_prefix}/lib,g s,@localedir@,|#_!!_#|${datarootdir}/locale,g s,@mandir@,|#_!!_#|${datarootdir}/man,g s,@DEFS@,|#_!!_#|-DPACKAGE_NAME=\\"java-libburn\\" -DPACKAGE_TARNAME=\\"java-libburn\\" -DPACKAGE_VERSION=\\"0.1\\" -DPACKAGE_STRING=\\"java-libburn\\ 0.1\\" -DPACKAGE_BUGREPORT=\\"\\",g s,@ECHO_C@,|#_!!_#|,g s,@ECHO_N@,|#_!!_#|-n,g s,@ECHO_T@,|#_!!_#|,g s,@LIBS@,|#_!!_#|,g s,@build_alias@,|#_!!_#|,g s,@host_alias@,|#_!!_#|,g s,@target_alias@,|#_!!_#|,g s,@JAVAC@,|#_!!_#|/usr/local/bin/javac,g s,@JAVAH@,|#_!!_#|/usr/bin/javah,g s,@JAR@,|#_!!_#|/usr/bin/jar,g s,@CC@,|#_!!_#|gcc,g s,@CFLAGS@,|#_!!_#|-g -O2,g s,@LDFLAGS@,|#_!!_#|,g s,@CPPFLAGS@,|#_!!_#|,g s,@ac_ct_CC@,|#_!!_#|gcc,g s,@EXEEXT@,|#_!!_#|,g s,@OBJEXT@,|#_!!_#|o,g s,@ISOFS_INCLUDE@,|#_!!_#|/usr/local/include/libisofs,g s,@BURN_INCLUDE@,|#_!!_#|/usr/local/include/libburn,g s,@LIBOBJS@,|#_!!_#|,g s,@LTLIBOBJS@,|#_!!_#|,g :end s/|#_!!_#|//g CEOF fi # test -n "$CONFIG_FILES" for ac_tag in :F $CONFIG_FILES do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 echo "$as_me: error: Invalid tag $ac_tag." >&2;} { (exit 1); exit 1; }; };; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 echo "$as_me: error: cannot find input file: $ac_f" >&2;} { (exit 1); exit 1; }; };; esac ac_file_inputs="$ac_file_inputs $ac_f" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input="Generated from "`IFS=: echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} fi case $ac_tag in *:-:* | *:-) cat >"$tmp/stdin";; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` { as_dir="$ac_dir" case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 echo "$as_me: error: cannot create directory $as_dir" >&2;} { (exit 1); exit 1; }; }; } ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= case `sed -n '/datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p ' $ac_file_inputs` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} ac_datarootdir_hack=' s&@datadir@&${datarootdir}&g s&@docdir@&${datarootdir}/doc/${PACKAGE_TARNAME}&g s&@infodir@&${datarootdir}/info&g s&@localedir@&${datarootdir}/locale&g s&@mandir@&${datarootdir}/man&g s&\${datarootdir}&${prefix}/share&g' ;; esac sed "/^[ ]*VPATH[ ]*=/{ s/:*\$(srcdir):*/:/ s/:*\${srcdir}:*/:/ s/:*@srcdir@:*/:/ s/^\([^=]*=[ ]*\):*/\1/ s/:*$// s/^[^=]*=[ ]*$// } :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s&@configure_input@&$configure_input&;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined." >&5 echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined." >&2;} rm -f "$tmp/stdin" case $ac_file in -) cat "$tmp/out"; rm -f "$tmp/out";; *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; esac ;; esac done # for ac_tag { (exit 0); exit 0; } #! /bin/sh # From configure.ac Revision: 0.1 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.60 for java-libburn 0.1. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # PATH needs CR # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) as_nl=' ' IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 { (exit 1); exit 1; } fi # Work around bugs in pre-3.0 UWIN ksh. for as_var in ENV MAIL MAILPATH do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # CDPATH. $as_unset CDPATH if test "x$CONFIG_SHELL" = x; then if (eval ":") 2>/dev/null; then as_have_required=yes else as_have_required=no fi if test $as_have_required = yes && (eval ": (as_func_return () { (exit \$1) } as_func_success () { as_func_return 0 } as_func_failure () { as_func_return 1 } as_func_ret_success () { return 0 } as_func_ret_failure () { return 1 } exitcode=0 if as_func_success; then : else exitcode=1 echo as_func_success failed. fi if as_func_failure; then exitcode=1 echo as_func_failure succeeded. fi if as_func_ret_success; then : else exitcode=1 echo as_func_ret_success failed. fi if as_func_ret_failure; then exitcode=1 echo as_func_ret_failure succeeded. fi if ( set x; as_func_ret_success y && test x = \"\$1\" ); then : else exitcode=1 echo positional parameters were not saved. fi test \$exitcode = 0) || { (exit 1); exit 1; } ( as_lineno_1=\$LINENO as_lineno_2=\$LINENO test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } ") 2> /dev/null; then : else as_candidate_shells= as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /usr/bin/posix$PATH_SEPARATOR/bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. case $as_dir in /*) for as_base in sh bash ksh sh5; do as_candidate_shells="$as_candidate_shells $as_dir/$as_base" done;; esac done IFS=$as_save_IFS for as_shell in $as_candidate_shells $SHELL; do # Try only shells that exist, to save several forks. if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { ("$as_shell") 2> /dev/null <<\_ASEOF # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh : _ASEOF }; then CONFIG_SHELL=$as_shell as_have_required=yes if { "$as_shell" 2> /dev/null <<\_ASEOF # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh : (as_func_return () { (exit $1) } as_func_success () { as_func_return 0 } as_func_failure () { as_func_return 1 } as_func_ret_success () { return 0 } as_func_ret_failure () { return 1 } exitcode=0 if as_func_success; then : else exitcode=1 echo as_func_success failed. fi if as_func_failure; then exitcode=1 echo as_func_failure succeeded. fi if as_func_ret_success; then : else exitcode=1 echo as_func_ret_success failed. fi if as_func_ret_failure; then exitcode=1 echo as_func_ret_failure succeeded. fi if ( set x; as_func_ret_success y && test x = "$1" ); then : else exitcode=1 echo positional parameters were not saved. fi test $exitcode = 0) || { (exit 1); exit 1; } ( as_lineno_1=$LINENO as_lineno_2=$LINENO test "x$as_lineno_1" != "x$as_lineno_2" && test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } _ASEOF }; then break fi fi done if test "x$CONFIG_SHELL" != x; then for as_var in BASH_ENV ENV do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var done export CONFIG_SHELL exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} fi if test $as_have_required = no; then echo This script requires a shell more modern than all the echo shells that I found on your system. Please install a echo modern shell, or manually run the script under such a echo shell if you do have one. { (exit 1); exit 1; } fi fi fi (eval "as_func_return () { (exit \$1) } as_func_success () { as_func_return 0 } as_func_failure () { as_func_return 1 } as_func_ret_success () { return 0 } as_func_ret_failure () { return 1 } exitcode=0 if as_func_success; then : else exitcode=1 echo as_func_success failed. fi if as_func_failure; then exitcode=1 echo as_func_failure succeeded. fi if as_func_ret_success; then : else exitcode=1 echo as_func_ret_success failed. fi if as_func_ret_failure; then exitcode=1 echo as_func_ret_failure succeeded. fi if ( set x; as_func_ret_success y && test x = \"\$1\" ); then : else exitcode=1 echo positional parameters were not saved. fi test \$exitcode = 0") || { echo No shell found that supports shell functions. echo Please tell autoconf@gnu.org about your system, echo including any error possibly output before this echo message } as_lineno_1=$LINENO as_lineno_2=$LINENO test "x$as_lineno_1" != "x$as_lineno_2" && test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line after each line using $LINENO; the second 'sed' # does the real work. The second script uses 'N' to pair each # line-number line with the line containing $LINENO, and appends # trailing '-' during substitution so that $LINENO is not a special # case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # scripts with optimization help from Paolo Bonzini. Blame Lee # E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in -n*) case `echo 'x\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. *) ECHO_C='\c';; esac;; *) ECHO_N='-n';; esac if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir fi echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # Find out whether ``test -x'' works. Don't use a zero-byte file, as # systems may use methods other than mode bits to determine executability. cat >conf$$.file <<_ASEOF #! /bin/sh exit 0 _ASEOF chmod +x conf$$.file if test -x conf$$.file >/dev/null 2>&1; then as_executable_p="test -x" else as_executable_p=: fi rm -f conf$$.file # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='java-libburn' PACKAGE_TARNAME='java-libburn' PACKAGE_VERSION='0.1' PACKAGE_STRING='java-libburn 0.1' PACKAGE_BUGREPORT='' ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datarootdir datadir sysconfdir sharedstatedir localstatedir includedir oldincludedir docdir infodir htmldir dvidir pdfdir psdir libdir localedir mandir DEFS ECHO_C ECHO_N ECHO_T LIBS build_alias host_alias target_alias JAVAC JAVAH JAR CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT ISOFS_INCLUDE BURN_INCLUDE LIBOBJS LTLIBOBJS' ac_subst_files='' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS CPPFLAGS' # Initialize some variables set by options. ac_init_help= ac_init_version=false # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` eval enable_$ac_feature=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` eval enable_$ac_feature=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package| sed 's/-/_/g'` eval with_$ac_package=\$ac_optarg ;; -without-* | --without-*) ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package | sed 's/-/_/g'` eval with_$ac_package=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) { echo "$as_me: error: unrecognized option: $ac_option Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 { (exit 1); exit 1; }; } eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` { echo "$as_me: error: missing argument to $ac_option" >&2 { (exit 1); exit 1; }; } fi # Be sure to have absolute directory names. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; } done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || { echo "$as_me: error: Working directory cannot be determined" >&2 { (exit 1); exit 1; }; } test "X$ac_ls_di" = "X$ac_pwd_ls_di" || { echo "$as_me: error: pwd does not report name of working directory" >&2 { (exit 1); exit 1; }; } # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$0" || $as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$0" : 'X\(//\)[^/]' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || echo X"$0" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 { (exit 1); exit 1; }; } fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2 { (exit 1); exit 1; }; } pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures java-libburn 0.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/java-libburn] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of java-libburn 0.1:";; esac cat <<\_ACEOF Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if you have headers in a nonstandard directory Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF java-libburn configure 0.1 generated by GNU Autoconf 2.60 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by java-libburn $as_me 0.1, which was generated by GNU Autoconf 2.60. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; 2) ac_configure_args1="$ac_configure_args1 '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac done done $as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } $as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo cat <<\_ASBOX ## ---------------- ## ## Cache variables. ## ## ---------------- ## _ASBOX echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( *) $as_unset $ac_var ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo cat <<\_ASBOX ## ----------------- ## ## Output variables. ## ## ----------------- ## _ASBOX echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then cat <<\_ASBOX ## ------------------- ## ## File substitutions. ## ## ------------------- ## _ASBOX echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then cat <<\_ASBOX ## ----------- ## ## confdefs.h. ## ## ----------- ## _ASBOX echo cat confdefs.h echo fi test "$ac_signal" != 0 && echo "$as_me: caught signal $ac_signal" echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then set x "$CONFIG_SITE" elif test "x$prefix" != xNONE; then set x "$prefix/share/config.site" "$prefix/etc/config.site" else set x "$ac_default_prefix/share/config.site" \ "$ac_default_prefix/etc/config.site" fi shift for ac_site_file do if test -r "$ac_site_file"; then { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special # files actually), so we avoid doing that. if test -f "$cache_file"; then { echo "$as_me:$LINENO: loading cache $cache_file" >&5 echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { echo "$as_me:$LINENO: creating cache $cache_file" >&5 echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 echo "$as_me: former value: $ac_old_val" >&2;} { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 echo "$as_me: current value: $ac_new_val" >&2;} ac_cache_corrupted=: fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 echo "$as_me: error: changes in the environment can compromise the build" >&2;} { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Extract the first word of "javac", so it can be a program name with args. set dummy javac; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_JAVAC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $JAVAC in [\\/]* | ?:[\\/]*) ac_cv_path_JAVAC="$JAVAC" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_JAVAC="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi JAVAC=$ac_cv_path_JAVAC if test -n "$JAVAC"; then { echo "$as_me:$LINENO: result: $JAVAC" >&5 echo "${ECHO_T}$JAVAC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi # Extract the first word of "javah", so it can be a program name with args. set dummy javah; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_JAVAH+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $JAVAH in [\\/]* | ?:[\\/]*) ac_cv_path_JAVAH="$JAVAH" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_JAVAH="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi JAVAH=$ac_cv_path_JAVAH if test -n "$JAVAH"; then { echo "$as_me:$LINENO: result: $JAVAH" >&5 echo "${ECHO_T}$JAVAH" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi # Extract the first word of "jar", so it can be a program name with args. set dummy jar; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_path_JAR+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $JAR in [\\/]* | ?:[\\/]*) ac_cv_path_JAR="$JAR" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_JAR="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi JAR=$ac_cv_path_JAR if test -n "$JAR"; then { echo "$as_me:$LINENO: result: $JAR" >&5 echo "${ECHO_T}$JAR" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&5 echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6; } else { echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&5 echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools whose name does not start with the host triplet. If you think this configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&5 echo "$as_me: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } # Provide some information about the compiler. echo "$as_me:$LINENO: checking for C compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (ac_try="$ac_compiler --version >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compiler --version >&5") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (ac_try="$ac_compiler -v >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compiler -v >&5") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (ac_try="$ac_compiler -V >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compiler -V >&5") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; } ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # # List of possible output files, starting from the most likely. # The algorithm is not robust to junk in `.', hence go to wildcards (a.*) # only as a last resort. b.out is created by i960 compilers. ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out' # # The IRIX 6 linker writes into existing files which may not be # executable, retaining their permissions. Remove them first so a # subsequent execution test works. ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { (ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link_default") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: C compiler cannot create executables See \`config.log' for more details." >&5 echo "$as_me: error: C compiler cannot create executables See \`config.log' for more details." >&2;} { (exit 77); exit 77; }; } fi ac_exeext=$ac_cv_exeext { echo "$as_me:$LINENO: result: $ac_file" >&5 echo "${ECHO_T}$ac_file" >&6; } # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { echo "$as_me:$LINENO: checking whether the C compiler works" >&5 echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; } # FIXME: These cross compiler hacks should be removed for Autoconf 3.0 # If not cross compiling, check that we can run a simple program. if test "$cross_compiling" != yes; then if { ac_try='./$ac_file' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { echo "$as_me:$LINENO: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&5 echo "$as_me: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi fi fi { echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6; } rm -f a.out a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; } { echo "$as_me:$LINENO: result: $cross_compiling" >&5 echo "${ECHO_T}$cross_compiling" >&6; } { echo "$as_me:$LINENO: checking for suffix of executables" >&5 echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; } if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_link") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest$ac_cv_exeext { echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 echo "${ECHO_T}$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT { echo "$as_me:$LINENO: checking for suffix of object files" >&5 echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; } if test "${ac_cv_objext+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 echo "${ECHO_T}$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } if test "${ac_cv_c_compiler_gnu+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_compiler_gnu=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } GCC=`test $ac_compiler_gnu = yes && echo yes` ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } if test "${ac_cv_prog_cc_g+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 CFLAGS="" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } if test "${ac_cv_prog_cc_c89+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 (eval "$ac_try") 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_c89=$ac_arg else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { echo "$as_me:$LINENO: result: none needed" >&5 echo "${ECHO_T}none needed" >&6; } ;; xno) { echo "$as_me:$LINENO: result: unsupported" >&5 echo "${ECHO_T}unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; esac ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ISOFS_INCLUDE=`pkg-config --cflags libisofs-1 | sed -e's/-I//g' -e's/ *$//g'` BURN_INCLUDE=`pkg-config --cflags libburn-1 | sed -e's/-I//g' -e's/ *$//g'` ac_config_files="$ac_config_files build.properties" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( *) $as_unset $ac_var ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote # substitution turns \\\\ into \\, and sed turns \\ into \). sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then test "x$cache_file" != "x/dev/null" && { echo "$as_me:$LINENO: updating cache $cache_file" >&5 echo "$as_me: updating cache $cache_file" >&6;} cat confcache >$cache_file else { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. ac_script=' t clear :clear s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g t quote s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g t quote b any :quote s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g s/\[/\\&/g s/\]/\\&/g s/\$/$$/g H :any ${ g s/^\n// s/\n/ /g p } ' DEFS=`sed -n "$ac_script" confdefs.h` ac_libobjs= ac_ltlibobjs= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : ${CONFIG_STATUS=./config.status} ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 echo "$as_me: creating $CONFIG_STATUS" >&6;} cat >$CONFIG_STATUS <<_ACEOF #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # PATH needs CR # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) as_nl=' ' IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 { (exit 1); exit 1; } fi # Work around bugs in pre-3.0 UWIN ksh. for as_var in ENV MAIL MAILPATH do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # CDPATH. $as_unset CDPATH as_lineno_1=$LINENO as_lineno_2=$LINENO test "x$as_lineno_1" != "x$as_lineno_2" && test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line after each line using $LINENO; the second 'sed' # does the real work. The second script uses 'N' to pair each # line-number line with the line containing $LINENO, and appends # trailing '-' during substitution so that $LINENO is not a special # case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # scripts with optimization help from Paolo Bonzini. Blame Lee # E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in -n*) case `echo 'x\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. *) ECHO_C='\c';; esac;; *) ECHO_N='-n';; esac if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir fi echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # Find out whether ``test -x'' works. Don't use a zero-byte file, as # systems may use methods other than mode bits to determine executability. cat >conf$$.file <<_ASEOF #! /bin/sh exit 0 _ASEOF chmod +x conf$$.file if test -x conf$$.file >/dev/null 2>&1; then as_executable_p="test -x" else as_executable_p=: fi rm -f conf$$.file # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 # Save the log message, to keep $[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by java-libburn $as_me 0.1, which was generated by GNU Autoconf 2.60. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF cat >>$CONFIG_STATUS <<_ACEOF # Files that config.status was made for. config_files="$ac_config_files" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF ac_cs_usage="\ \`$as_me' instantiates files from templates according to the current configuration. Usage: $0 [OPTIONS] [FILE]... -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ java-libburn config.status 0.1 configured by $0, generated by GNU Autoconf 2.60, with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2006 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # If no file are specified by the user, then we need to provide default # value. By we need to know if files were specified by the user. ac_need_defaults=: while test $# != 0 do case $1 in --*=*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) echo "$ac_cs_version"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift CONFIG_FILES="$CONFIG_FILES $ac_optarg" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) { echo "$as_me: error: unrecognized option: $1 Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *) ac_config_targets="$ac_config_targets $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF if \$ac_cs_recheck; then echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 CONFIG_SHELL=$SHELL export CONFIG_SHELL exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "build.properties") CONFIG_FILES="$CONFIG_FILES build.properties" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} { (exit 1); exit 1; }; };; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= trap 'exit_status=$? { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status ' 0 trap '{ (exit 1); exit 1; }' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || { echo "$me: cannot create a temporary directory in ." >&2 { (exit 1); exit 1; } } # # Set up the sed scripts for CONFIG_FILES section. # # No need to generate the scripts if there are no CONFIG_FILES. # This happens for instance when ./config.status config.h if test -n "$CONFIG_FILES"; then _ACEOF ac_delim='%!_!# ' for ac_last_try in false false false false false :; do cat >conf$$subs.sed <<_ACEOF SHELL!$SHELL$ac_delim PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim PACKAGE_NAME!$PACKAGE_NAME$ac_delim PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim PACKAGE_STRING!$PACKAGE_STRING$ac_delim PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim exec_prefix!$exec_prefix$ac_delim prefix!$prefix$ac_delim program_transform_name!$program_transform_name$ac_delim bindir!$bindir$ac_delim sbindir!$sbindir$ac_delim libexecdir!$libexecdir$ac_delim datarootdir!$datarootdir$ac_delim datadir!$datadir$ac_delim sysconfdir!$sysconfdir$ac_delim sharedstatedir!$sharedstatedir$ac_delim localstatedir!$localstatedir$ac_delim includedir!$includedir$ac_delim oldincludedir!$oldincludedir$ac_delim docdir!$docdir$ac_delim infodir!$infodir$ac_delim htmldir!$htmldir$ac_delim dvidir!$dvidir$ac_delim pdfdir!$pdfdir$ac_delim psdir!$psdir$ac_delim libdir!$libdir$ac_delim localedir!$localedir$ac_delim mandir!$mandir$ac_delim DEFS!$DEFS$ac_delim ECHO_C!$ECHO_C$ac_delim ECHO_N!$ECHO_N$ac_delim ECHO_T!$ECHO_T$ac_delim LIBS!$LIBS$ac_delim build_alias!$build_alias$ac_delim host_alias!$host_alias$ac_delim target_alias!$target_alias$ac_delim JAVAC!$JAVAC$ac_delim JAVAH!$JAVAH$ac_delim JAR!$JAR$ac_delim CC!$CC$ac_delim CFLAGS!$CFLAGS$ac_delim LDFLAGS!$LDFLAGS$ac_delim CPPFLAGS!$CPPFLAGS$ac_delim ac_ct_CC!$ac_ct_CC$ac_delim EXEEXT!$EXEEXT$ac_delim OBJEXT!$OBJEXT$ac_delim ISOFS_INCLUDE!$ISOFS_INCLUDE$ac_delim BURN_INCLUDE!$BURN_INCLUDE$ac_delim LIBOBJS!$LIBOBJS$ac_delim LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 51; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} { (exit 1); exit 1; }; } else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` if test -n "$ac_eof"; then ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` ac_eof=`expr $ac_eof + 1` fi cat >>$CONFIG_STATUS <<_ACEOF cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof /@[a-zA-Z_][a-zA-Z_0-9]*@/!b end _ACEOF sed ' s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g s/^/s,@/; s/!/@,|#_!!_#|/ :n t n s/'"$ac_delim"'$/,g/; t s/$/\\/; p N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n ' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF :end s/|#_!!_#|//g CEOF$ac_eof _ACEOF # VPATH may cause trouble with some makes, so we remove $(srcdir), # ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=/{ s/:*\$(srcdir):*/:/ s/:*\${srcdir}:*/:/ s/:*@srcdir@:*/:/ s/^\([^=]*=[ ]*\):*/\1/ s/:*$// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF fi # test -n "$CONFIG_FILES" for ac_tag in :F $CONFIG_FILES do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 echo "$as_me: error: Invalid tag $ac_tag." >&2;} { (exit 1); exit 1; }; };; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 echo "$as_me: error: cannot find input file: $ac_f" >&2;} { (exit 1); exit 1; }; };; esac ac_file_inputs="$ac_file_inputs $ac_f" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input="Generated from "`IFS=: echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} fi case $ac_tag in *:-:* | *:-) cat >"$tmp/stdin";; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` { as_dir="$ac_dir" case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 echo "$as_me: error: cannot create directory $as_dir" >&2;} { (exit 1); exit 1; }; }; } ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= case `sed -n '/datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p ' $ac_file_inputs` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF sed "$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s&@configure_input@&$configure_input&;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined." >&5 echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined." >&2;} rm -f "$tmp/stdin" case $ac_file in -) cat "$tmp/out"; rm -f "$tmp/out";; *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; esac ;; esac done # for ac_tag { (exit 0); exit 0; } _ACEOF chmod +x $CONFIG_STATUS ac_clean_files=$ac_clean_files_save # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || { (exit 1); exit 1; } fi dnl configure.ac dnl dnl Copyright (c) 2007 Vreixo Formoso dnl dnl This library is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl See COPYING file for details. dnl AC_INIT(java-libburn, 0.1) AC_REVISION([$Revision: 0.1 $]) dnl look for java programs AC_PATH_PROG([JAVAC], [javac]) AC_PATH_PROG([JAVAH], [javah]) AC_PATH_PROG([JAR], [jar]) AC_PROG_CC dnl Package dependances dnl ------------------------------------------------------------------------- dnl PKG_PROG_PKG_CONFIG() dnl PKG_CHECK_MODULES([ISOFS], isofs) AC_SUBST(ISOFS_INCLUDE,`pkg-config --cflags libisofs-1 | sed -e's/-I//g' -e's/ *$//g'`) AC_SUBST(BURN_INCLUDE,`pkg-config --cflags libburn-1 | sed -e's/-I//g' -e's/ *$//g'`) dnl Output Files dnl ------------------------------------------------------------------------- AC_CONFIG_FILES([ build.properties ]) AC_OUTPUT The lib/ hierarchy contains third party libraries. TODO comentar cada libraria usada #Sat Mar 24 13:41:25 CET 2007 eclipse.preferences.version=1 org.eclipse.jdt.ui.text.custom_code_templates=